From 5d61a4a79a1e96dc5d9af1e5e712c84507307544 Mon Sep 17 00:00:00 2001 From: J M Rossy Date: Wed, 29 Jul 2015 15:40:09 -0700 Subject: [PATCH] Samples update for public Windows 10 release Fix #2 Enabling WPP Recorder in Sensors Samples causes errors Fix #4 Add back fixed KMDOD sample Add new BarcodeScanner sample in pos folder Add new MagneticStripeReader sample in pos folder Add new SynpaticsTouch sample in input folder Add new Power Engine Plugin sample in pofx folder Add new DeviceMft sample in avstream folder Add new AvsCamera sample in root Add new SimBatt sample in root Add other pre-existing samples not yet released for Win10 --- audio/sysvad/ContosoKeywordDetector.h | 34 + .../EndpointsCommon/EndpointsCommon.vcxproj | 182 + .../EndpointsCommon.vcxproj.Filters | 59 + .../MiniportAudioEngineNode.cpp | 1045 ++ .../MiniportStreamAudioEngineNode.cpp | 971 ++ .../sysvad/EndpointsCommon/bthhfpmictopo.cpp | 281 + audio/sysvad/EndpointsCommon/bthhfpmictopo.h | 41 + .../EndpointsCommon/bthhfpmictoptable.h | 279 + .../EndpointsCommon/bthhfpmicwavtable.h | 292 + .../EndpointsCommon/bthhfpmicwbwavtable.h | 361 + .../sysvad/EndpointsCommon/bthhfpminipairs.h | 224 + .../EndpointsCommon/bthhfpminwavert.cpp | 206 + .../EndpointsCommon/bthhfpspeakertopo.cpp | 281 + .../EndpointsCommon/bthhfpspeakertopo.h | 41 + .../EndpointsCommon/bthhfpspeakertoptable.h | 275 + .../EndpointsCommon/bthhfpspeakerwavtable.h | 609 + .../EndpointsCommon/bthhfpspeakerwbwavtable.h | 862 ++ audio/sysvad/EndpointsCommon/bthhfptopo.cpp | 623 + audio/sysvad/EndpointsCommon/bthhfptopo.h | 61 + .../EndpointsCommon/micarray1toptable.h | 228 + audio/sysvad/EndpointsCommon/micarraytopo.cpp | 787 + audio/sysvad/EndpointsCommon/micarraytopo.h | 112 + .../sysvad/EndpointsCommon/micarraywavtable.h | 826 ++ audio/sysvad/EndpointsCommon/mintopo.cpp | 731 + audio/sysvad/EndpointsCommon/mintopo.h | 145 + audio/sysvad/EndpointsCommon/minwavert.cpp | 2287 +++ audio/sysvad/EndpointsCommon/minwavert.h | 665 + .../EndpointsCommon/minwavertstream.cpp | 1553 ++ .../sysvad/EndpointsCommon/minwavertstream.h | 261 + audio/sysvad/EndpointsCommon/simple.h | 170 + .../sysvad/EndpointsCommon/speakerhptopo.cpp | 274 + audio/sysvad/EndpointsCommon/speakerhptopo.h | 21 + .../EndpointsCommon/speakerhptoptable.h | 157 + .../EndpointsCommon/speakerhpwavtable.h | 854 ++ audio/sysvad/EndpointsCommon/speakertopo.cpp | 85 + audio/sysvad/EndpointsCommon/speakertopo.h | 21 + .../sysvad/EndpointsCommon/speakertoptable.h | 155 + .../sysvad/EndpointsCommon/speakerwavtable.h | 863 ++ .../KeywordDetectorContosoAdapter.cpp | 171 + .../KeywordDetectorContosoAdapter.def | 6 + .../KeywordDetectorContosoAdapter.idl | 16 + .../KeywordDetectorContosoAdapter.rc | 13 + .../KeywordDetectorContosoAdapter.vcxproj | 232 + ...wordDetectorContosoAdapter.vcxproj.Filters | 39 + .../sysvad/KeywordDetectorAdapter/dllmain.cpp | 33 + .../sysvad/KeywordDetectorAdapter/stdafx.cpp | 8 + audio/sysvad/KeywordDetectorAdapter/stdafx.h | 16 + .../sysvad/KeywordDetectorAdapter/targetver.h | 8 + audio/sysvad/Package/package.VcxProj | 96 + audio/sysvad/Package/package.VcxProj.Filters | 21 + .../sysvad/PhoneAudioSample/CellularWave.cpp | 685 + audio/sysvad/PhoneAudioSample/CellularWave.h | 126 + .../sysvad/PhoneAudioSample/Cellulartopo.cpp | 973 ++ audio/sysvad/PhoneAudioSample/Cellulartopo.h | 159 + .../PhoneAudioSample/Cellulartoptable.h | 255 + .../PhoneAudioSample/Cellularwavtable.h | 286 + audio/sysvad/PhoneAudioSample/FMTopTable.h | 199 + audio/sysvad/PhoneAudioSample/FMTopo.cpp | 985 ++ audio/sysvad/PhoneAudioSample/FMTopo.h | 152 + audio/sysvad/PhoneAudioSample/FMWavTable.h | 249 + audio/sysvad/PhoneAudioSample/FMWave.cpp | 389 + audio/sysvad/PhoneAudioSample/FMWave.h | 84 + .../PhoneAudioSample/PhoneAudioSample.vcxproj | 300 + .../PhoneAudioSample.vcxproj.Filters | 73 + .../PhoneAudioSample/handsetmictopo.cpp | 274 + .../sysvad/PhoneAudioSample/handsetmictopo.h | 21 + .../PhoneAudioSample/handsetmictoptable.h | 245 + .../PhoneAudioSample/handsetmicwavtable.h | 441 + .../PhoneAudioSample/handsetspeakertopo.cpp | 561 + .../PhoneAudioSample/handsetspeakertopo.h | 86 + .../PhoneAudioSample/handsetspeakertoptable.h | 156 + .../PhoneAudioSample/handsetspeakerwavtable.h | 649 + audio/sysvad/PhoneAudioSample/michstopo.cpp | 274 + audio/sysvad/PhoneAudioSample/michstopo.h | 21 + audio/sysvad/PhoneAudioSample/michstoptable.h | 246 + audio/sysvad/PhoneAudioSample/michswavtable.h | 441 + audio/sysvad/PhoneAudioSample/minipairs.h | 504 + .../sysvad/PhoneAudioSample/speakerhstopo.cpp | 274 + audio/sysvad/PhoneAudioSample/speakerhstopo.h | 21 + .../PhoneAudioSample/speakerhstoptable.h | 157 + .../PhoneAudioSample/speakerhswavtable.h | 854 ++ audio/sysvad/ReadMe.md | 189 + audio/sysvad/SwapAPO/APO/Resource.h | 20 + audio/sysvad/SwapAPO/APO/SwapAPO.h | 319 + audio/sysvad/SwapAPO/APO/SwapAPO.vcxproj | 242 + .../SwapAPO/APO/SwapAPO.vcxproj.Filters | 45 + audio/sysvad/SwapAPO/APO/SwapAPODll.cpp | 148 + audio/sysvad/SwapAPO/APO/SwapAPODll.def | 6 + audio/sysvad/SwapAPO/APO/SwapAPODll.idl | 54 + audio/sysvad/SwapAPO/APO/SwapAPODll.rc | 24 + audio/sysvad/SwapAPO/APO/SwapAPODll.rgs | 11 + audio/sysvad/SwapAPO/APO/SwapAPOInterface.idl | 31 + audio/sysvad/SwapAPO/APO/SwapAPOMFX.rgs | 13 + audio/sysvad/SwapAPO/APO/SwapAPOSFX.rgs | 13 + audio/sysvad/SwapAPO/APO/swap.cpp | 183 + audio/sysvad/SwapAPO/APO/swapapo.png | Bin 0 -> 1251 bytes audio/sysvad/SwapAPO/APO/swapapomfx.cpp | 1047 ++ audio/sysvad/SwapAPO/APO/swapaposfx.cpp | 677 + audio/sysvad/SwapAPO/Inc/CommonMacros.h | 337 + audio/sysvad/SwapAPO/Inc/CustomPropKeys.h | 39 + audio/sysvad/SwapAPO/Inc/tlist.h | 476 + .../AdvEndpointPropPage.cpp | 1087 ++ .../PropPageExtensions/AdvEndpointPropPage.h | 74 + .../AdvEndpointPropPage.rgs | 27 + .../SwapAPO/PropPageExtensions/CplExt.cpp | 69 + .../SwapAPO/PropPageExtensions/CplExt.idl | 73 + .../SwapAPO/PropPageExtensions/CplExt.rc | 208 + .../SwapAPO/PropPageExtensions/CplExt.rgs | 11 + .../SwapAPO/PropPageExtensions/Parts.cpp | 298 + .../sysvad/SwapAPO/PropPageExtensions/Parts.h | 49 + .../PropPageExtensions/PropPageExt.def | 9 + .../PropPageExtensions/PropPageExt.vcxproj | 275 + .../PropPageExt.vcxproj.Filters | 51 + .../PropPageExtensions/SwapPropPage.cpp | 1154 ++ .../SwapAPO/PropPageExtensions/SwapPropPage.h | 92 + .../PropPageExtensions/SwapPropPage.rgs | 27 + .../PropPageExtensions/TopologyExaminers.cpp | 162 + .../PropPageExtensions/TopologyExaminers.h | 50 + .../SwapAPO/PropPageExtensions/UIWidgets.cpp | 321 + .../SwapAPO/PropPageExtensions/UIWidgets.h | 405 + .../SwapAPO/PropPageExtensions/music.ico | Bin 0 -> 26694 bytes .../SwapAPO/PropPageExtensions/resource.h | 34 + .../SwapAPO/PropPageExtensions/stdafx.cpp | 18 + .../SwapAPO/PropPageExtensions/stdafx.h | 67 + .../SwapAPO/PropPageExtensions/stdafxsrc.cpp | 1 + audio/sysvad/SysVadShared.h | 30 + .../TabletAudioSample.vcxproj | 295 + .../TabletAudioSample.vcxproj.Filters | 58 + audio/sysvad/TabletAudioSample/hdmitopo.cpp | 670 + audio/sysvad/TabletAudioSample/hdmitopo.h | 86 + audio/sysvad/TabletAudioSample/hdmitoptable.h | 172 + audio/sysvad/TabletAudioSample/hdmiwavtable.h | 626 + .../TabletAudioSample/micarray2toptable.h | 229 + .../TabletAudioSample/micarray3toptable.h | 228 + .../TabletAudioSample/micarray3wavtable.h | 455 + audio/sysvad/TabletAudioSample/micintopo.cpp | 274 + audio/sysvad/TabletAudioSample/micintopo.h | 21 + .../sysvad/TabletAudioSample/micintoptable.h | 250 + .../sysvad/TabletAudioSample/micinwavtable.h | 440 + audio/sysvad/TabletAudioSample/minipairs.h | 512 + audio/sysvad/TabletAudioSample/spdiftopo.cpp | 85 + audio/sysvad/TabletAudioSample/spdiftopo.h | 21 + .../sysvad/TabletAudioSample/spdiftoptable.h | 166 + .../sysvad/TabletAudioSample/spdifwavtable.h | 854 ++ audio/sysvad/ToneGenerator.cpp | 261 + audio/sysvad/ToneGenerator.h | 72 + audio/sysvad/UnittestData.h | 17 + audio/sysvad/adapter.cpp | 1079 ++ audio/sysvad/basetopo.cpp | 691 + audio/sysvad/basetopo.h | 98 + audio/sysvad/common.cpp | 6376 ++++++++ audio/sysvad/common.h | 622 + audio/sysvad/hw.cpp | 448 + audio/sysvad/hw.h | 107 + audio/sysvad/ihvprivatepropertyset.h | 14 + audio/sysvad/kshelper.cpp | 960 ++ audio/sysvad/kshelper.h | 159 + audio/sysvad/phoneaudiosample.inf | Bin 0 -> 44094 bytes audio/sysvad/readme.htm | 396 + audio/sysvad/savedata.cpp | 1015 ++ audio/sysvad/savedata.h | 193 + audio/sysvad/sources.inc | 34 + audio/sysvad/sysvad.h | 265 + audio/sysvad/sysvad.rc | 30 + audio/sysvad/sysvad.sln | 121 + audio/sysvad/tabletaudiosample.inf | Bin 0 -> 55952 bytes avscamera/Package/package.VcxProj | 87 + avscamera/Package/package.VcxProj.Filters | 21 + avscamera/README.md | 13 + avscamera/avscamera.sln | 59 + avscamera/common/CustomProperties.h | 48 + avscamera/common/Macros.h | 48 + avscamera/common/MetadataInternal.h | 267 + avscamera/mft0/AvsCameraMFT0.rc | 13 + avscamera/mft0/AvsCameraMft0.vcxproj | 216 + avscamera/mft0/AvsCameraMft0.vcxproj.Filters | 42 + avscamera/mft0/MFT0.def | 13 + avscamera/mft0/MFT0.idl | 46 + avscamera/mft0/MFT0Impl.cpp | 2106 +++ avscamera/mft0/MFT0Impl.h | 328 + avscamera/mft0/MFT0clsid.h | 26 + avscamera/mft0/SampleHelpers.h | 106 + avscamera/mft0/dllmain.cpp | 191 + avscamera/mft0/stdafx.h | 32 + avscamera/mft0/stdafxsrc.cpp | 1 + avscamera/mft0/targetver.h | 15 + avscamera/sys/AvsCamera.cpp | 108 + avscamera/sys/AvsCamera.rc | 21 + avscamera/sys/AvsCamera.vcxproj | 246 + avscamera/sys/AvsCamera.vcxproj.Filters | 106 + avscamera/sys/AvsCameraDevice.cpp | 99 + avscamera/sys/AvsCameraDevice.h | 73 + avscamera/sys/AvsCameraFilter.cpp | 587 + avscamera/sys/AvsCameraFilter.h | 57 + avscamera/sys/CameraProfile.h | 54 + avscamera/sys/Capture.cpp | 2075 +++ avscamera/sys/Capture.h | 305 + avscamera/sys/Common.h | 704 + avscamera/sys/Dbg.h | 81 + avscamera/sys/Device.cpp | 962 ++ avscamera/sys/Device.h | 449 + avscamera/sys/ExtendedCameraAngleOffset.h | 67 + avscamera/sys/ExtendedEVComp.h | 105 + avscamera/sys/ExtendedFieldOfView.h | 67 + .../sys/ExtendedMaxVideoFpsForPhotoRes.h | 91 + avscamera/sys/ExtendedMetadata.h | 68 + avscamera/sys/ExtendedPhotoMode.h | 73 + avscamera/sys/ExtendedProperty.h | 142 + avscamera/sys/ExtendedVidProcSetting.h | 164 + avscamera/sys/Mutex.cpp | 70 + avscamera/sys/Mutex.h | 81 + avscamera/sys/NV12Synthesizer.cpp | 183 + avscamera/sys/NV12Synthesizer.h | 73 + avscamera/sys/NonCopyable.h | 48 + avscamera/sys/PreviewHwSim.cpp | 263 + avscamera/sys/PreviewHwSim.h | 57 + avscamera/sys/RGB24Synthesizer.cpp | 133 + avscamera/sys/RGB24Synthesizer.h | 69 + avscamera/sys/Ref.h | 185 + avscamera/sys/Roi.cpp | 629 + avscamera/sys/Roi.h | 279 + avscamera/sys/Sensor.cpp | 1260 ++ avscamera/sys/Sensor.h | 554 + avscamera/sys/SensorSimulation.cpp | 3496 +++++ avscamera/sys/SensorSimulation.h | 280 + avscamera/sys/Synthesizer.cpp | 1282 ++ avscamera/sys/Synthesizer.h | 507 + avscamera/sys/Timer.cpp | 172 + avscamera/sys/Timer.h | 199 + avscamera/sys/Util.cpp | 476 + avscamera/sys/Util.h | 289 + avscamera/sys/VideoCapture.cpp | 2559 ++++ avscamera/sys/VideoCapture.h | 34 + avscamera/sys/VideoHwSim.cpp | 66 + avscamera/sys/VideoHwSim.h | 42 + avscamera/sys/Waitable.cpp | 69 + avscamera/sys/Waitable.h | 58 + avscamera/sys/WorkItem.cpp | 167 + avscamera/sys/WorkItem.h | 63 + avscamera/sys/XRGBSynthesizer.cpp | 246 + avscamera/sys/XRGBSynthesizer.h | 97 + avscamera/sys/YUVSynthesizer.cpp | 149 + avscamera/sys/YUVSynthesizer.h | 69 + avscamera/sys/YUY2Synthesizer.cpp | 137 + avscamera/sys/YUY2Synthesizer.h | 68 + avscamera/sys/avscamera.inf | Bin 0 -> 26788 bytes avscamera/sys/filter.cpp | 4039 +++++ avscamera/sys/filter.h | 482 + avscamera/sys/hwsim.cpp | 1395 ++ avscamera/sys/hwsim.h | 316 + avscamera/sys/imagecapture.cpp | 321 + avscamera/sys/imagecapture.h | 44 + avscamera/sys/imagehwsim.cpp | 1430 ++ avscamera/sys/imagehwsim.h | 202 + avstream/avshws/ReadMe.md | 107 + avstream/avshws/avshws.h | 323 + avstream/avshws/avshws.htm | 458 + avstream/avshws/avshws.inf | 173 + avstream/avshws/avshws.rc | 22 + avstream/avshws/avshws.sln | 28 + avstream/avshws/avshws.vcxproj | 200 + 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/avssamp/Filter.cpp | 836 ++ avstream/avssamp/ReadMe.md | 56 + avstream/avssamp/audio.cpp | 655 + avstream/avssamp/audio.h | 150 + avstream/avssamp/avssamp.cpp | 116 + avstream/avssamp/avssamp.h | 248 + avstream/avssamp/avssamp.htm | 297 + avstream/avssamp/avssamp.inf | 93 + avstream/avssamp/avssamp.rc | 22 + avstream/avssamp/avssamp.sln | 28 + avstream/avssamp/avssamp.vcxproj | 202 + avstream/avssamp/avssamp.vcxproj.Filters | 52 + avstream/avssamp/capture.cpp | 249 + avstream/avssamp/capture.h | 284 + avstream/avssamp/filter.h | 253 + avstream/avssamp/image.cpp | 647 + avstream/avssamp/image.h | 476 + avstream/avssamp/purecall.c | 50 + avstream/avssamp/video.cpp | 1433 ++ avstream/avssamp/video.h | 221 + avstream/avssamp/wave.cpp | 598 + avstream/avssamp/wave.h | 192 + avstream/sampledevicemft/README.md | 31 + avstream/sampledevicemft/SampleDeviceMft.sln | 28 + avstream/sampledevicemft/Source.def | 5 + avstream/sampledevicemft/basepin.cpp | 861 ++ avstream/sampledevicemft/basepin.h | 596 + avstream/sampledevicemft/common.h | 697 + avstream/sampledevicemft/custompin.cpp | 98 + avstream/sampledevicemft/custompin.h | 30 + avstream/sampledevicemft/dllmain.cpp | 337 + .../sampledevicemft/mftpeventgenerator.cpp | 250 + avstream/sampledevicemft/mftpeventgenerator.h | 94 + avstream/sampledevicemft/multipinmft.cpp | 2376 +++ avstream/sampledevicemft/multipinmft.h | 468 + avstream/sampledevicemft/multipinmft.vcxproj | 263 + .../multipinmft.vcxproj.Filters | 46 + .../sampledevicemft/multipinmfthelpers.cpp | 661 + avstream/sampledevicemft/multipinmfthelpers.h | 206 + avstream/sampledevicemft/multipinmftutils.cpp | 874 ++ avstream/sampledevicemft/stdafx.h | 38 + avstream/sampledevicemft/stdafxsrc.cpp | 1 + avstream/samplemft0/SampleMft0.sln | 18 +- avstream/samplemft0/SampleMft0.vcxproj | 5 +- .../samplemft0/SampleMft0.vcxproj.Filters | 6 +- biometrics/Package/package.VcxProj | 93 + biometrics/Package/package.VcxProj.Filters | 21 + biometrics/ReadMe.md | 78 + biometrics/WBDIsample.sln | 90 + .../adapters/engine_adapter/EngineAdapter.cpp | 665 + .../adapters/engine_adapter/EngineAdapter.def | 4 + .../adapters/engine_adapter/EngineAdapter.h | 75 + .../adapters/engine_adapter/EngineAdapter.rc | 29 + .../engine_adapter/EngineAdapter.vcxproj | 228 + .../EngineAdapter.vcxproj.Filters | 33 + biometrics/adapters/engine_adapter/precomp.h | 62 + .../adapters/engine_adapter/precompsrc.cpp | 1 + biometrics/adapters/engine_adapter/resource.h | 15 + .../adapters/sensor_adapter/SensorAdapter.cpp | 485 + .../adapters/sensor_adapter/SensorAdapter.def | 5 + .../adapters/sensor_adapter/SensorAdapter.h | 68 + .../adapters/sensor_adapter/SensorAdapter.rc | 29 + .../sensor_adapter/SensorAdapter.vcxproj | 228 + .../SensorAdapter.vcxproj.Filters | 33 + biometrics/adapters/sensor_adapter/precomp.h | 62 + .../adapters/sensor_adapter/precompsrc.cpp | 1 + biometrics/adapters/sensor_adapter/resource.h | 15 + .../storage_adapter/StorageAdapter.cpp | 594 + .../storage_adapter/StorageAdapter.def | 4 + .../adapters/storage_adapter/StorageAdapter.h | 79 + .../storage_adapter/StorageAdapter.rc | 29 + .../storage_adapter/StorageAdapter.vcxproj | 228 + .../StorageAdapter.vcxproj.Filters | 33 + biometrics/adapters/storage_adapter/precomp.h | 62 + .../adapters/storage_adapter/precompsrc.cpp | 1 + .../adapters/storage_adapter/resource.h | 15 + biometrics/driver/BioUsbSample.ctl | 1 + biometrics/driver/BioUsbSample.rc | 26 + biometrics/driver/Device.cpp | 1834 +++ biometrics/driver/Device.h | 360 + biometrics/driver/Driver.cpp | 77 + biometrics/driver/Driver.h | 95 + biometrics/driver/Internalsrc.cpp | 1 + biometrics/driver/IoQueue.cpp | 297 + biometrics/driver/IoQueue.h | 123 + biometrics/driver/RequestHelper.h | 89 + biometrics/driver/WudfBioUsbSample.inx | 145 + biometrics/driver/WudfBioUsbSample.vcxproj | 315 + .../driver/WudfBioUsbSample.vcxproj.Filters | 51 + biometrics/driver/dllsup.cpp | 87 + biometrics/driver/exports.def | 10 + biometrics/driver/inc/public.h | 42 + biometrics/driver/inc/usb_hw.h | 238 + biometrics/driver/internal.h | 164 + biometrics/driver/readme.htm | 166 + biometrics/driver/resource.h | 4 + bluetooth/bthecho/ReadMe.md | 35 +- bluetooth/bthecho/bthcli/app/BthEcho.vcxproj | 5 +- .../bthcli/app/BthEcho.vcxproj.Filters | 6 +- .../bthecho/bthcli/sys/BthEchoSampleCli.inx | 9 +- .../bthcli/sys/BthEchoSampleCli.vcxproj | 5 +- .../sys/BthEchoSampleCli.vcxproj.Filters | 11 +- bluetooth/bthecho/bthecho.sln | 130 +- .../bthecho/bthsrv/inst/bthsrvinst.vcxproj | 5 +- .../bthsrv/inst/bthsrvinst.vcxproj.Filters | 6 +- .../bthecho/bthsrv/sys/BthEchoSampleSrv.inx | 9 +- .../bthsrv/sys/BthEchoSampleSrv.vcxproj | 5 +- .../sys/BthEchoSampleSrv.vcxproj.Filters | 11 +- bluetooth/bthecho/common/lib/bthecho.vcxproj | 5 +- .../common/lib/bthecho.vcxproj.Filters | 8 +- bluetooth/serialhcibus/Fdo.c | 1045 +- bluetooth/serialhcibus/WDK/SerialBusWdk.inx | 4 +- .../serialhcibus/WDK/SerialBusWdk.vcxproj | 5 +- .../WDK/SerialBusWdk.vcxproj.Filters | 11 +- bluetooth/serialhcibus/driver.h | 117 +- bluetooth/serialhcibus/io.c | 957 +- bluetooth/serialhcibus/serialhcibus.sln | 18 +- filesys/cdfs/cdfs.sln | 18 +- filesys/cdfs/cdfs.vcxproj | 5 +- filesys/cdfs/cdfs.vcxproj.Filters | 8 +- filesys/fastfat/ReadMe.md | 49 + filesys/fastfat/acchksup.c | 421 + filesys/fastfat/allocsup.c | 5222 +++++++ filesys/fastfat/cachesup.c | 2007 +++ filesys/fastfat/cleanup.c | 1176 ++ filesys/fastfat/close.c | 1280 ++ filesys/fastfat/create.c | 6727 +++++++++ filesys/fastfat/devctrl.c | 412 + filesys/fastfat/deviosup.c | 3697 +++++ filesys/fastfat/dirctrl.c | 1607 ++ filesys/fastfat/dirsup.c | 3879 +++++ filesys/fastfat/dumpsup.c | 381 + filesys/fastfat/ea.c | 2041 +++ filesys/fastfat/easup.c | 3852 +++++ filesys/fastfat/fastfat.rc | 14 + filesys/fastfat/fastfat.sln | 28 + filesys/fastfat/fastfat.vcxproj | 359 + filesys/fastfat/fastfat.vcxproj.Filters | 130 + filesys/fastfat/fat.h | 755 + filesys/fastfat/fatdata.c | 1527 ++ filesys/fastfat/fatdata.h | 333 + filesys/fastfat/fatinit.c | 739 + filesys/fastfat/fatprocs.h | 3012 ++++ filesys/fastfat/fatprocssrc.c | 1 + filesys/fastfat/fatstruc.h | 1707 +++ filesys/fastfat/fileinfo.c | 5010 +++++++ filesys/fastfat/filobsup.c | 579 + filesys/fastfat/flush.c | 1353 ++ filesys/fastfat/fsctrl.c | 8039 ++++++++++ filesys/fastfat/fspdisp.c | 484 + filesys/fastfat/lfn.h | 56 + filesys/fastfat/lockctrl.c | 763 + filesys/fastfat/namesup.c | 1035 ++ filesys/fastfat/nodetype.h | 194 + filesys/fastfat/pnp.c | 889 ++ filesys/fastfat/read.c | 1735 +++ filesys/fastfat/resrcsup.c | 953 ++ filesys/fastfat/shutdown.c | 313 + filesys/fastfat/splaysup.c | 486 + filesys/fastfat/strucsup.c | 3876 +++++ filesys/fastfat/timesup.c | 371 + filesys/fastfat/verfysup.c | 2072 +++ filesys/fastfat/volinfo.c | 1362 ++ filesys/fastfat/workque.c | 368 + filesys/fastfat/write.c | 3050 ++++ .../MetadataManager/MetadataManager.sln | 18 +- filesys/miniFilter/MetadataManager/fmm.inf | 4 +- .../miniFilter/MetadataManager/fmm.vcxproj | 5 +- .../MetadataManager/fmm.vcxproj.Filters | 8 +- .../miniFilter/NameChanger/NameChanger.inf | 4 +- .../miniFilter/NameChanger/NameChanger.sln | 18 +- .../NameChanger/NameChanger.vcxproj | 5 +- .../NameChanger/NameChanger.vcxproj.Filters | 8 +- filesys/miniFilter/avscan/avscan.inf | 4 +- filesys/miniFilter/avscan/avscan.sln | 44 +- .../miniFilter/avscan/filter/avscan.vcxproj | 5 +- .../avscan/filter/avscan.vcxproj.Filters | 8 +- .../miniFilter/avscan/filter/communication.c | 2 +- filesys/miniFilter/avscan/user/avscan.vcxproj | 5 +- .../avscan/user/avscan.vcxproj.Filters | 6 +- filesys/miniFilter/cancelSafe/cancelSafe.inf | 4 +- filesys/miniFilter/cancelSafe/cancelSafe.sln | 18 +- .../miniFilter/cancelSafe/cancelSafe.vcxproj | 5 +- .../cancelSafe/cancelSafe.vcxproj.Filters | 8 +- filesys/miniFilter/cdo/cdo.inf | 4 +- filesys/miniFilter/cdo/cdo.sln | 18 +- filesys/miniFilter/cdo/cdo.vcxproj | 5 +- filesys/miniFilter/cdo/cdo.vcxproj.Filters | 8 +- filesys/miniFilter/change/change.inf | 4 +- filesys/miniFilter/change/change.sln | 18 +- filesys/miniFilter/change/change.vcxproj | 5 +- .../miniFilter/change/change.vcxproj.Filters | 8 +- filesys/miniFilter/ctx/ctx.inf | 4 +- filesys/miniFilter/ctx/ctx.sln | 18 +- filesys/miniFilter/ctx/ctx.vcxproj | 5 +- filesys/miniFilter/ctx/ctx.vcxproj.Filters | 8 +- filesys/miniFilter/delete/delete.inf | 4 +- filesys/miniFilter/delete/delete.sln | 18 +- filesys/miniFilter/delete/delete.vcxproj | 5 +- .../miniFilter/delete/delete.vcxproj.Filters | 8 +- .../miniFilter/minispy/filter/minispy.vcxproj | 5 +- .../minispy/filter/minispy.vcxproj.Filters | 8 +- filesys/miniFilter/minispy/minispy.inf | 4 +- filesys/miniFilter/minispy/minispy.sln | 44 +- .../miniFilter/minispy/user/minispy.vcxproj | 5 +- .../minispy/user/minispy.vcxproj.Filters | 6 +- filesys/miniFilter/nullFilter/nullFilter.inf | 4 +- filesys/miniFilter/nullFilter/nullFilter.sln | 18 +- .../miniFilter/nullFilter/nullFilter.vcxproj | 5 +- .../nullFilter/nullFilter.vcxproj.Filters | 8 +- .../miniFilter/passThrough/passThrough.inf | 4 +- .../miniFilter/passThrough/passThrough.sln | 18 +- .../passThrough/passThrough.vcxproj | 5 +- .../passThrough/passThrough.vcxproj.Filters | 8 +- .../miniFilter/scanner/filter/scanner.vcxproj | 5 +- .../scanner/filter/scanner.vcxproj.Filters | 8 +- filesys/miniFilter/scanner/scanner.inf | 4 +- filesys/miniFilter/scanner/scanner.sln | 44 +- .../miniFilter/scanner/user/scanuser.vcxproj | 5 +- .../scanner/user/scanuser.vcxproj.Filters | 6 +- filesys/miniFilter/simrep/simrep.inf | 4 +- filesys/miniFilter/simrep/simrep.sln | 18 +- filesys/miniFilter/simrep/simrep.vcxproj | 5 +- .../miniFilter/simrep/simrep.vcxproj.Filters | 8 +- .../miniFilter/swapBuffers/swapBuffers.inf | 4 +- .../miniFilter/swapBuffers/swapBuffers.sln | 18 +- .../swapBuffers/swapBuffers.vcxproj | 5 +- .../swapBuffers/swapBuffers.vcxproj.Filters | 8 +- general/PLX9x5x/PLX9x5x.sln | 44 +- general/PLX9x5x/sys/Init.c | 6 + general/PLX9x5x/sys/Pci9x5x.vcxproj | 5 +- general/PLX9x5x/sys/Pci9x5x.vcxproj.Filters | 11 +- general/PLX9x5x/sys/pci9x5x.inx | 7 +- general/PLX9x5x/test/plx.vcxproj | 5 +- general/PLX9x5x/test/plx.vcxproj.Filters | 6 +- general/SystemDma/wdm/SystemDma.sln | 44 +- .../SystemDma/wdm/exe/SystemDmaApp.vcxproj | 5 +- .../wdm/exe/SystemDmaApp.vcxproj.Filters | 6 +- general/SystemDma/wdm/sys/SDma.vcxproj | 5 +- .../SystemDma/wdm/sys/SDma.vcxproj.Filters | 8 +- general/cancel/cancel.sln | 66 +- general/cancel/exe/canclapp.vcxproj | 5 +- general/cancel/exe/canclapp.vcxproj.Filters | 6 +- general/cancel/startio/cancel.vcxproj | 5 +- general/cancel/startio/cancel.vcxproj.Filters | 8 +- general/cancel/sys/cancel.vcxproj | 5 +- general/cancel/sys/cancel.vcxproj.Filters | 8 +- general/echo/kmdf/ReadMe.md | 3 - general/echo/kmdf/driver/AutoSync/echo.inx | 4 +- .../echo/kmdf/driver/AutoSync/echo.vcxproj | 5 +- .../kmdf/driver/AutoSync/echo.vcxproj.Filters | 11 +- .../echo/kmdf/driver/DriverSync/echo_2.inx | 4 +- .../kmdf/driver/DriverSync/echo_2.vcxproj | 5 +- .../driver/DriverSync/echo_2.vcxproj.Filters | 11 +- general/echo/kmdf/exe/echoapp.vcxproj | 5 +- general/echo/kmdf/exe/echoapp.vcxproj.Filters | 6 +- general/echo/kmdf/kmdfecho.sln | 72 +- general/echo/umdf/WUDFEchoDriver.inx | 11 +- general/echo/umdf/WUDFEchoDriver.vcxproj | 5 +- .../echo/umdf/WUDFEchoDriver.vcxproj.Filters | 11 +- general/echo/umdf/echo.sln | 18 +- general/echo/umdf2/ReadMe.md | 4 - .../echo/umdf2/driver/AutoSync/echo.vcxproj | 5 +- .../driver/AutoSync/echo.vcxproj.Filters | 11 +- general/echo/umdf2/driver/AutoSync/echoum.inx | 4 +- general/echo/umdf2/exe/echoapp.vcxproj | 5 +- .../echo/umdf2/exe/echoapp.vcxproj.Filters | 6 +- general/echo/umdf2/umdf2echo.sln | 48 +- .../echo/umdfSocketEcho/Driver/SocketEcho.inx | 8 +- .../umdfSocketEcho/Driver/SocketEcho.vcxproj | 5 +- .../Driver/SocketEcho.vcxproj.Filters | 11 +- .../Exe/socketechoserver.vcxproj | 5 +- .../Exe/socketechoserver.vcxproj.Filters | 6 +- .../echo/umdfSocketEcho/umdfsocketecho.sln | 44 +- general/event/eventsample.sln | 44 +- general/event/exe/event.vcxproj | 5 +- general/event/exe/event.vcxproj.Filters | 6 +- general/event/wdm/event.vcxproj | 5 +- general/event/wdm/event.vcxproj.Filters | 8 +- general/filehistory/exe/fhsetup.vcxproj | 5 +- .../filehistory/exe/fhsetup.vcxproj.Filters | 6 +- general/filehistory/filehistory.sln | 18 +- general/installwdf/InstallWdf.vcxproj | 33 +- general/installwdf/InstallWdf.vcxproj.Filters | 6 +- general/installwdf/installwdf.sln | 18 +- general/ioctl/kmdf/ReadMe.md | 67 + general/ioctl/kmdf/exe/install.c | 812 + general/ioctl/kmdf/exe/nonpnp.inf | 8 + general/ioctl/kmdf/exe/nonpnpapp.vcxproj | 197 + .../ioctl/kmdf/exe/nonpnpapp.vcxproj.Filters | 25 + general/ioctl/kmdf/exe/testapp.c | 642 + general/ioctl/kmdf/ioctl.sln | 46 + general/ioctl/kmdf/sys/localwpp.ini | 17 + general/ioctl/kmdf/sys/nonpnp.c | 1309 ++ general/ioctl/kmdf/sys/nonpnp.h | 90 + general/ioctl/kmdf/sys/nonpnp.rc | 10 + general/ioctl/kmdf/sys/nonpnp.vcxproj | 196 + general/ioctl/kmdf/sys/nonpnp.vcxproj.Filters | 31 + general/ioctl/kmdf/sys/public.h | 53 + general/ioctl/kmdf/sys/trace.h | 68 + general/ioctl/wdm/exe/ioctlapp.vcxproj | 5 +- .../ioctl/wdm/exe/ioctlapp.vcxproj.Filters | 6 +- general/ioctl/wdm/ioctl.sln | 44 +- general/ioctl/wdm/sys/sioctl.vcxproj | 5 +- general/ioctl/wdm/sys/sioctl.vcxproj.Filters | 8 +- .../control/ObCallbackTestCtrl.vcxproj | 5 +- .../ObCallbackTestCtrl.vcxproj.Filters | 6 +- .../obcallback/driver/ObCallbackTest.vcxproj | 5 +- .../driver/ObCallbackTest.vcxproj.Filters | 8 +- general/obcallback/obcallback.sln | 44 +- general/pcidrv/kmdf/HW/PCIDRV.vcxproj | 5 +- general/pcidrv/kmdf/HW/PCIDRV.vcxproj.Filters | 11 +- general/pcidrv/kmdf/HW/nic_init.c | 6 + general/pcidrv/kmdf/genpci.inx | 15 +- general/pcidrv/pcidrv.sln | 48 +- general/pcidrv/test/myping.vcxproj | 5 +- general/pcidrv/test/myping.vcxproj.Filters | 6 +- general/perfcounters/kcs/kcs.sln | 18 +- general/perfcounters/kcs/kcs.vcxproj | 5 +- general/perfcounters/kcs/kcs.vcxproj.Filters | 8 +- general/registry/regfltr/exe/common.h | 336 +- general/registry/regfltr/exe/regctrl.vcxproj | 5 +- .../regfltr/exe/regctrl.vcxproj.Filters | 6 +- general/registry/regfltr/regfltr.sln | 44 +- general/registry/regfltr/sys/regfltr.c | 1742 +-- general/registry/regfltr/sys/regfltr.vcxproj | 5 +- .../regfltr/sys/regfltr.vcxproj.Filters | 8 +- .../toaster/toastDrv/Package/package.VcxProj | 111 + .../toastDrv/Package/package.VcxProj.Filters | 21 + general/toaster/toastDrv/ReadMe.md | 134 + .../toastDrv/classinstaller/classInst.c | 513 + .../toastDrv/classinstaller/classinst.rc | 65 + .../toastDrv/classinstaller/resource.h | 38 + .../toastDrv/classinstaller/toaster.ico | Bin 0 -> 4534 bytes .../toastDrv/classinstaller/tostrcls.def | 5 + .../toastDrv/classinstaller/tostrcls.vcxproj | 237 + .../classinstaller/tostrcls.vcxproj.Filters | 30 + general/toaster/toastDrv/coinstaller/coinst.c | 343 + .../toaster/toastDrv/coinstaller/tostrco1.def | 6 + .../toastDrv/coinstaller/tostrco1.vcxproj | 210 + .../coinstaller/tostrco1.vcxproj.Filters | 25 + .../toaster/toastDrv/exe/enum/Enum.vcxproj | 182 + .../toastDrv/exe/enum/Enum.vcxproj.Filters | 22 + general/toaster/toastDrv/exe/enum/enum.c | 307 + general/toaster/toastDrv/exe/notify/notify.c | 1244 ++ general/toaster/toastDrv/exe/notify/notify.h | 182 + general/toaster/toastDrv/exe/notify/notify.rc | 77 + .../toastDrv/exe/notify/notify.vcxproj | 171 + .../exe/notify/notify.vcxproj.Filters | 27 + .../toaster/toastDrv/exe/notify/toaster.ico | Bin 0 -> 4534 bytes general/toaster/toastDrv/exe/toast/toast.c | 343 + .../toaster/toastDrv/exe/toast/toast.vcxproj | 170 + .../toastDrv/exe/toast/toast.vcxproj.Filters | 22 + .../toaster/toastDrv/exe/wmi/WmiToast.vcxproj | 176 + .../toastDrv/exe/wmi/WmiToast.vcxproj.Filters | 28 + .../toaster/toastDrv/exe/wmi/wmiexecute.cpp | 532 + .../toaster/toastDrv/exe/wmi/wmigetset.cpp | 168 + general/toaster/toastDrv/exe/wmi/wmitoast.cpp | 535 + general/toaster/toastDrv/exe/wmi/wmitoast.h | 32 + .../toastDrv/kmdf/bus/dynamic/busenum.c | 774 + .../toastDrv/kmdf/bus/dynamic/busenum.h | 206 + .../toastDrv/kmdf/bus/dynamic/busenum.mof | 26 + .../toastDrv/kmdf/bus/dynamic/busenum.rc | 13 + .../toastDrv/kmdf/bus/dynamic/buspdo.c | 522 + .../toastDrv/kmdf/bus/dynamic/dynambus.inx | 102 + .../kmdf/bus/dynamic/dynambus.vcxproj | 192 + .../kmdf/bus/dynamic/dynambus.vcxproj.Filters | 45 + .../toaster/toastDrv/kmdf/bus/dynamic/wmi.c | 254 + .../toastDrv/kmdf/bus/static/StatBus.vcxproj | 192 + .../kmdf/bus/static/StatBus.vcxproj.Filters | 45 + .../toastDrv/kmdf/bus/static/busenum.c | 685 + .../toastDrv/kmdf/bus/static/busenum.h | 168 + .../toastDrv/kmdf/bus/static/busenum.mof | 26 + .../toastDrv/kmdf/bus/static/busenum.rc | 13 + .../toaster/toastDrv/kmdf/bus/static/buspdo.c | 360 + .../toastDrv/kmdf/bus/static/statbus.inx | 102 + .../toaster/toastDrv/kmdf/bus/static/wmi.c | 251 + .../toaster/toastDrv/kmdf/filter/filter.inx | 140 + .../toastDrv/kmdf/filter/generic/filter.c | 384 + .../toastDrv/kmdf/filter/generic/filter.h | 83 + .../toastDrv/kmdf/filter/generic/filter.rc | 12 + .../kmdf/filter/generic/filter.vcxproj | 188 + .../filter/generic/filter.vcxproj.Filters | 36 + .../toastDrv/kmdf/filter/sideband/filter.c | 611 + .../toastDrv/kmdf/filter/sideband/filter.h | 78 + .../toastDrv/kmdf/filter/sideband/filter.rc | 12 + .../kmdf/filter/sideband/filter.vcxproj | 200 + .../filter/sideband/filter.vcxproj.Filters | 36 + .../toastDrv/kmdf/func/featured/power.c | 411 + .../toastDrv/kmdf/func/featured/toaster.c | 1038 ++ .../toastDrv/kmdf/func/featured/toaster.mof | 109 + .../toastDrv/kmdf/func/featured/toaster.rc | 15 + .../toastDrv/kmdf/func/featured/trace.h | 66 + .../kmdf/func/featured/wdffeatured.inx | 120 + .../kmdf/func/featured/wdffeatured.vcxproj | 206 + .../func/featured/wdffeatured.vcxproj.Filters | 45 + .../toaster/toastDrv/kmdf/func/featured/wmi.c | 922 ++ .../toastDrv/kmdf/func/shared/toaster.h | 134 + .../toastDrv/kmdf/func/simple/toaster.c | 418 + .../toastDrv/kmdf/func/simple/wdfsimple.inx | 114 + .../kmdf/func/simple/wdfsimple.vcxproj | 167 + .../func/simple/wdfsimple.vcxproj.Filters | 31 + general/toaster/toastDrv/kmdf/inc/driver.h | 80 + general/toaster/toastDrv/kmdf/inc/public.h | 167 + .../toaster/toastDrv/kmdf/toastmon/toastmon.H | 128 + .../toastDrv/kmdf/toastmon/toastmon.RC | 12 + .../toaster/toastDrv/kmdf/toastmon/toastmon.c | 1109 ++ .../toastDrv/kmdf/toastmon/wdftoastmon.inx | 94 + .../kmdf/toastmon/wdftoastmon.vcxproj | 181 + .../kmdf/toastmon/wdftoastmon.vcxproj.Filters | 39 + general/toaster/toastDrv/kmdf/toastmon/wmi.c | 302 + general/toaster/toastDrv/toaster.sln | 256 + .../toaster/toastDrv/umdf/Toastmon/Device.cpp | 318 + .../toaster/toastDrv/umdf/Toastmon/Device.h | 162 + .../toaster/toastDrv/umdf/Toastmon/Driver.cpp | 207 + .../toaster/toastDrv/umdf/Toastmon/Driver.h | 146 + .../toastDrv/umdf/Toastmon/RemoteTarget.cpp | 538 + .../toastDrv/umdf/Toastmon/RemoteTarget.h | 228 + .../toastDrv/umdf/Toastmon/ToastMon.rc | 21 + .../toastDrv/umdf/Toastmon/WUDFToastMon.inx | 84 + .../umdf/Toastmon/WUDFToastMon.vcxproj | 273 + .../Toastmon/WUDFToastMon.vcxproj.Filters | 51 + .../toaster/toastDrv/umdf/Toastmon/comsup.cpp | 344 + .../toaster/toastDrv/umdf/Toastmon/comsup.h | 215 + .../toaster/toastDrv/umdf/Toastmon/dllsup.cpp | 174 + .../toastDrv/umdf/Toastmon/exports.def | 4 + .../toaster/toastDrv/umdf/Toastmon/internal.h | 109 + general/toaster/toastDrv/umdf/Toastmon/list.h | 77 + general/toaster/toastDrv/umdf/func/Device.cpp | 228 + general/toaster/toastDrv/umdf/func/Device.h | 78 + general/toaster/toastDrv/umdf/func/Driver.cpp | 205 + general/toaster/toastDrv/umdf/func/Driver.h | 59 + general/toaster/toastDrv/umdf/func/Queue.cpp | 283 + general/toaster/toastDrv/umdf/func/Queue.h | 96 + .../toastDrv/umdf/func/WUDFToaster.cpp | 88 + .../toastDrv/umdf/func/WUDFToaster.ctl | 2 + .../toastDrv/umdf/func/WUDFToaster.def | 6 + .../toastDrv/umdf/func/WUDFToaster.idl | 40 + .../toastDrv/umdf/func/WUDFToaster.inx | 98 + .../toaster/toastDrv/umdf/func/WUDFToaster.rc | 22 + .../toastDrv/umdf/func/WUDFToaster.vcxproj | 326 + .../umdf/func/WUDFToaster.vcxproj.Filters | 60 + general/toaster/toastDrv/umdf/func/internal.h | 114 + general/toaster/toastDrv/umdf/func/resource.h | 13 + .../toaster/toastDrv/umdf/func/stdAfxsrc.cpp | 1 + general/toaster/toastDrv/umdf/func/stdafx.cpp | 27 + general/toaster/toastDrv/umdf/func/stdafx.h | 47 + general/toaster/toastpkg/inf/toastpkg.inf | 10 +- .../toastpkg/toastapp/toastapp.vcxproj | 5 +- .../toastapp/toastapp.vcxproj.Filters | 6 +- .../toastpkg/toastcd/ToastApp/setup.exe | Bin 0 -> 44032 bytes .../toastpkg/toastcd/amd64/toastva.exe | Bin 0 -> 98304 bytes .../toastpkg/toastcd/amd64/tostrcls.dll | Bin 0 -> 16384 bytes .../toastpkg/toastcd/amd64/tostrco2.dll | Bin 0 -> 17920 bytes .../toaster/toastpkg/toastcd/i386/toastva.exe | Bin 0 -> 95744 bytes .../toastpkg/toastcd/i386/tostrcls.dll | Bin 0 -> 15872 bytes .../toastpkg/toastcd/i386/tostrco2.dll | Bin 0 -> 14336 bytes general/toaster/toastpkg/toastcd/toastpkg.inf | 10 +- general/toaster/toastpkg/toastcd/tostx86.cat | Bin 7892 -> 7987 bytes general/toaster/toastpkg/toastcd/tstamd64.cat | Bin 7794 -> 7889 bytes .../toaster/toastpkg/toastco/tostrco2.vcxproj | 5 +- .../toastpkg/toastco/tostrco2.vcxproj.Filters | 6 +- general/toaster/toastpkg/toastpkg.sln | 68 +- .../toaster/toastpkg/toastva/toastva.vcxproj | 5 +- .../toastpkg/toastva/toastva.vcxproj.Filters | 6 +- general/toaster/umdf2/Package/package.VcxProj | 87 + .../umdf2/Package/package.VcxProj.Filters | 21 + general/toaster/umdf2/ReadMe.md | 51 + general/toaster/umdf2/exe/enum/Enum.vcxproj | 182 + .../umdf2/exe/enum/Enum.vcxproj.Filters | 22 + general/toaster/umdf2/exe/enum/enum.c | 307 + general/toaster/umdf2/exe/notify/notify.c | 1244 ++ general/toaster/umdf2/exe/notify/notify.h | 182 + general/toaster/umdf2/exe/notify/notify.rc | 77 + .../toaster/umdf2/exe/notify/notify.vcxproj | 171 + .../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 | 170 + .../umdf2/exe/toast/toast.vcxproj.Filters | 22 + general/toaster/umdf2/filter/generic/filter.c | 387 + general/toaster/umdf2/filter/generic/filter.h | 85 + .../toaster/umdf2/filter/generic/filter.rc | 12 + .../toaster/umdf2/filter/generic/filterum.inx | 98 + .../umdf2/filter/generic/filterum.vcxproj | 199 + .../filter/generic/filterum.vcxproj.Filters | 31 + general/toaster/umdf2/func/featured/power.c | 385 + general/toaster/umdf2/func/featured/toaster.c | 862 ++ .../toaster/umdf2/func/featured/toaster.rc | 12 + .../umdf2/func/featured/wdffeaturedum.inx | 98 + .../umdf2/func/featured/wdffeaturedum.vcxproj | 179 + .../featured/wdffeaturedum.vcxproj.Filters | 39 + general/toaster/umdf2/func/shared/toaster.h | 118 + general/toaster/umdf2/func/simple/toaster.c | 418 + .../toaster/umdf2/func/simple/wdfsimpleum.inx | 97 + .../umdf2/func/simple/wdfsimpleum.vcxproj | 191 + .../func/simple/wdfsimpleum.vcxproj.Filters | 31 + general/toaster/umdf2/inc/driver.h | 69 + general/toaster/umdf2/inc/public.h | 167 + general/toaster/umdf2/umdf2toaster.sln | 123 + .../SystemTraceControl/SystemTraceControl.sln | 18 +- .../SystemTraceControl.vcxproj | 5 +- .../SystemTraceControl.vcxproj.Filters | 6 +- .../tracing/evntdrv/Eventdrv/Eventdrv.vcxproj | 5 +- .../evntdrv/Eventdrv/Eventdrv.vcxproj.Filters | 8 +- general/tracing/evntdrv/eventdrv.sln | 44 +- .../tracing/evntdrv/evntctrl/evntctrl.vcxproj | 5 +- .../evntdrv/evntctrl/evntctrl.vcxproj.Filters | 6 +- .../tracedriver/tracectl/tracectl.vcxproj | 5 +- .../tracectl/tracectl.vcxproj.Filters | 6 +- general/tracing/tracedriver/tracedrv.sln | 44 +- .../tracedriver/tracedrv/tracedrv.vcxproj | 5 +- .../tracedrv/tracedrv.vcxproj.Filters | 8 +- general/umdfSkeleton/UMDFSkeleton.vcxproj | 5 +- .../umdfSkeleton/UMDFSkeleton.vcxproj.Filters | 14 +- general/umdfSkeleton/UMDFSkeleton_OSR.inx | 7 +- general/umdfSkeleton/UMDFSkeleton_Root.inx | 7 +- general/umdfSkeleton/umdfSkeleton.sln | 18 +- gpio/samples/sim.sln | 79 +- gpio/samples/simdevice/simdevice.inx | 10 +- gpio/samples/simdevice/simdevice.vcxproj | 5 +- .../simdevice/simdevice.vcxproj.Filters | 11 +- gpio/samples/simgpio/simgpio.inx | Bin 3616 -> 3722 bytes gpio/samples/simgpio/simgpio.vcxproj | 5 +- gpio/samples/simgpio/simgpio.vcxproj.Filters | 11 +- gpio/samples/simgpio_i2c/simgpio_i2c.inx | Bin 3688 -> 3794 bytes gpio/samples/simgpio_i2c/simgpio_i2c.vcxproj | 5 +- .../simgpio_i2c/simgpio_i2c.vcxproj.Filters | 11 +- hid/firefly/ReadMe.md | 85 + hid/firefly/app/firefly.cpp | 135 + hid/firefly/app/flicker.vcxproj | 194 + hid/firefly/app/flicker.vcxproj.Filters | 22 + hid/firefly/driver/device.c | 128 + hid/firefly/driver/device.h | 43 + hid/firefly/driver/driver.c | 66 + hid/firefly/driver/firefly.h | 52 + hid/firefly/driver/firefly.inx | 128 + hid/firefly/driver/firefly.mof | 20 + hid/firefly/driver/firefly.rc | 13 + hid/firefly/driver/firefly.vcxproj | 188 + hid/firefly/driver/firefly.vcxproj.Filters | 48 + hid/firefly/driver/magic.h | 28 + hid/firefly/driver/vfeature.c | 250 + hid/firefly/driver/vfeature.h | 37 + hid/firefly/driver/wmi.c | 192 + hid/firefly/driver/wmi.h | 43 + hid/firefly/firefly.htm | 101 + hid/firefly/firefly.sln | 78 + hid/firefly/lib/Luminous.vcxproj | 190 + hid/firefly/lib/Luminous.vcxproj.Filters | 22 + hid/firefly/lib/luminous.cpp | 509 + hid/firefly/sauron/SAURON.vcxproj | 302 + hid/firefly/sauron/SAURON.vcxproj.Filters | 42 + hid/firefly/sauron/Sauron.cpp | 538 + hid/firefly/sauron/Sauron.h | 76 + hid/firefly/sauron/Sauron.rgs | 41 + hid/firefly/sauron/Saurondll.cpp | 71 + hid/firefly/sauron/Saurondll.def | 9 + hid/firefly/sauron/Saurondll.rc | 131 + hid/firefly/sauron/StdAfx.cpp | 12 + hid/firefly/sauron/StdAfx.h | 28 + hid/firefly/sauron/effects.h | 462 + hid/firefly/sauron/effects.idl | 128 + hid/firefly/sauron/iSauron.idl | 40 + hid/firefly/sauron/resource.h | 20 + hid/firefly/sauron/stdafxsrc.cpp | 1 + hid/firefly/shared/luminous.h | 76 + hid/firefly/shared/project.mk | 7 + hid/hclient/hclient.sln | 18 +- hid/hclient/hclient.vcxproj | 5 +- hid/hclient/hclient.vcxproj.Filters | 6 +- hid/hidusbfx2/Package/package.VcxProj | 87 + hid/hidusbfx2/Package/package.VcxProj.Filters | 21 + hid/hidusbfx2/ReadMe.md | 412 + hid/hidusbfx2/hidkmdf/hidkmdf.c | 277 + hid/hidusbfx2/hidkmdf/hidkmdf.rc | 10 + hid/hidusbfx2/hidkmdf/hidkmdf.vcxproj | 145 + hid/hidusbfx2/hidkmdf/hidkmdf.vcxproj.Filters | 31 + hid/hidusbfx2/hidusbfx2.sln | 59 + hid/hidusbfx2/readme.htm | 1797 +++ hid/hidusbfx2/sys/driver.c | 371 + hid/hidusbfx2/sys/hid.c | 1020 ++ hid/hidusbfx2/sys/hidusbfx2.h | 476 + hid/hidusbfx2/sys/hidusbfx2.inx | 186 + hid/hidusbfx2/sys/hidusbfx2.rc | 11 + hid/hidusbfx2/sys/hidusbfx2.vcxproj | 227 + hid/hidusbfx2/sys/hidusbfx2.vcxproj.Filters | 42 + hid/hidusbfx2/sys/trace.h | 102 + hid/hidusbfx2/sys/usb.c | 864 ++ hid/vhidmini2/ReadMe.md | 4 - hid/vhidmini2/app/testvhid.vcxproj | 5 +- hid/vhidmini2/app/testvhid.vcxproj.Filters | 6 +- hid/vhidmini2/driver/kmdf/vhidmini.inx | 16 +- hid/vhidmini2/driver/kmdf/vhidmini.vcxproj | 5 +- .../driver/kmdf/vhidmini.vcxproj.Filters | 11 +- hid/vhidmini2/driver/umdf2/VhidminiUm.inx | 16 +- hid/vhidmini2/driver/umdf2/VhidminiUm.vcxproj | 5 +- .../driver/umdf2/VhidminiUm.vcxproj.Filters | 11 +- hid/vhidmini2/vhidmini2.sln | 72 +- .../hiddigi/SynapticsTouch/SynapticsTouch.inx | 199 + .../hiddigi/SynapticsTouch/SynapticsTouch.sln | 28 + .../SynapticsTouch/SynapticsTouch.vcxproj | 200 + .../SynapticsTouch.vcxproj.Filters | 61 + input/hiddigi/SynapticsTouch/controller.h | 203 + input/hiddigi/SynapticsTouch/device.c | 480 + input/hiddigi/SynapticsTouch/device.h | 32 + input/hiddigi/SynapticsTouch/driver.c | 319 + input/hiddigi/SynapticsTouch/driver.h | 29 + input/hiddigi/SynapticsTouch/hid.c | 769 + input/hiddigi/SynapticsTouch/hid.h | 85 + input/hiddigi/SynapticsTouch/idle.c | 328 + input/hiddigi/SynapticsTouch/idle.h | 54 + input/hiddigi/SynapticsTouch/init.c | 1167 ++ input/hiddigi/SynapticsTouch/internal.h | 73 + input/hiddigi/SynapticsTouch/power.c | 272 + input/hiddigi/SynapticsTouch/queue.c | 180 + input/hiddigi/SynapticsTouch/queue.h | 24 + input/hiddigi/SynapticsTouch/readme.md | 72 + input/hiddigi/SynapticsTouch/registry.c | 722 + input/hiddigi/SynapticsTouch/report.c | 864 ++ input/hiddigi/SynapticsTouch/resolutions.c | 526 + input/hiddigi/SynapticsTouch/resolutions.h | 66 + input/hiddigi/SynapticsTouch/rmiinternal.h | 579 + input/hiddigi/SynapticsTouch/spb.c | 512 + input/hiddigi/SynapticsTouch/spb.h | 67 + input/hiddigi/SynapticsTouch/trace.h | 49 + input/kbfiltr/ReadMe.md | 119 + input/kbfiltr/exe/kbftest.c | 239 + input/kbfiltr/exe/kbftest.vcxproj | 142 + input/kbfiltr/exe/kbftest.vcxproj.Filters | 22 + input/kbfiltr/kbfiltr.htm | 465 + input/kbfiltr/kbfiltr.sln | 46 + input/kbfiltr/sys/kbfiltr.c | 871 ++ input/kbfiltr/sys/kbfiltr.h | 205 + input/kbfiltr/sys/kbfiltr.inx | 139 + input/kbfiltr/sys/kbfiltr.rc | 12 + input/kbfiltr/sys/kbfiltr.vcxproj | 159 + input/kbfiltr/sys/kbfiltr.vcxproj.Filters | 39 + input/kbfiltr/sys/public.h | 11 + input/kbfiltr/sys/rawpdo.c | 369 + input/moufiltr/Moufiltr.htm | 699 + input/moufiltr/ReadMe.md | 20 + input/moufiltr/moufiltr.c | 504 + input/moufiltr/moufiltr.h | 126 + input/moufiltr/moufiltr.inx | 139 + input/moufiltr/moufiltr.rc | 11 + input/moufiltr/moufiltr.sln | 28 + input/moufiltr/moufiltr.vcxproj | 146 + input/moufiltr/moufiltr.vcxproj.Filters | 36 + network/config/bindview/bindview.sln | 18 +- network/config/bindview/bindview.vcxproj | 5 +- .../config/bindview/bindview.vcxproj.Filters | 6 +- network/modem/fakemodem/fakemodem.sln | 18 +- network/modem/fakemodem/fakemodem.vcxproj | 5 +- .../modem/fakemodem/fakemodem.vcxproj.Filters | 11 +- network/modem/fakemodem/mdmfake.inx | 13 +- network/ndis/extension/base/SxBase.c | 34 +- network/ndis/extension/base/SxBase.h | 6 +- network/ndis/extension/base/sxbase.vcxproj | 5 +- .../extension/base/sxbase.vcxproj.Filters | 8 +- network/ndis/extension/extensions.sln | 52 +- .../extension/samples/forward/MsForwardExt.c | 4 +- .../samples/forward/msforwardext.inf | 16 +- .../samples/forward/msforwardext.vcxproj | 5 +- .../forward/msforwardext.vcxproj.Filters | 8 +- .../samples/passthrough/mspassthroughext.inf | 20 +- .../passthrough/mspassthroughext.vcxproj | 5 +- .../mspassthroughext.vcxproj.Filters | 8 +- network/ndis/filter/device.c | 4 +- network/ndis/filter/filter.c | 2 +- network/ndis/filter/filter.h | 2 +- network/ndis/filter/filter.sln | 18 +- network/ndis/filter/ndislwf.vcxproj | 5 +- network/ndis/filter/ndislwf.vcxproj.Filters | 8 +- network/ndis/filter/netlwf.inf | 13 +- network/ndis/mux/driver/60/mux_mp.inf | 13 +- network/ndis/mux/driver/60/muxp.inf | 15 +- network/ndis/mux/driver/60/novlan/mux.vcxproj | 5 +- .../mux/driver/60/novlan/mux.vcxproj.Filters | 8 +- .../ndis/mux/driver/60/vlan/muxvlan.vcxproj | 5 +- .../driver/60/vlan/muxvlan.vcxproj.Filters | 8 +- network/ndis/mux/mux.sln | 76 +- network/ndis/mux/notifyob/mux.vcxproj | 5 +- network/ndis/mux/notifyob/mux.vcxproj.Filters | 6 +- network/ndis/ndisprot/6x/ndisprot60.sln | 72 +- .../ndis/ndisprot/6x/sys/60/ndisprot60.inf | 20 +- .../ndisprot/6x/sys/60/ndisprot60.vcxproj | 5 +- .../6x/sys/60/ndisprot60.vcxproj.Filters | 8 +- .../ndis/ndisprot/6x/sys/630/ndisprot630.inf | 24 +- .../ndisprot/6x/sys/630/ndisprot630.vcxproj | 5 +- .../6x/sys/630/ndisprot630.vcxproj.Filters | 8 +- .../ndis/ndisprot/6x/test/prottest.vcxproj | 5 +- .../ndisprot/6x/test/prottest.vcxproj.Filters | 6 +- network/ndis/ndisprot_kmdf/60/debug.c | 423 + network/ndis/ndisprot_kmdf/60/debug.h | 218 + network/ndis/ndisprot_kmdf/60/excallbk.c | 190 + network/ndis/ndisprot_kmdf/60/macros.h | 245 + network/ndis/ndisprot_kmdf/60/ndisbind.c | 2175 +++ network/ndis/ndisprot_kmdf/60/ndisprot.h | 539 + network/ndis/ndisprot_kmdf/60/ndisprot.inx | 118 + network/ndis/ndisprot_kmdf/60/ndisprot.rc | 40 + .../ndis/ndisprot_kmdf/60/nprt6wdf.vcxproj | 237 + .../ndisprot_kmdf/60/nprt6wdf.vcxproj.Filters | 54 + network/ndis/ndisprot_kmdf/60/ntdisp.c | 1086 ++ network/ndis/ndisprot_kmdf/60/precomp.h | 23 + network/ndis/ndisprot_kmdf/60/precompsrc.c | 1 + network/ndis/ndisprot_kmdf/60/protuser.h | 107 + network/ndis/ndisprot_kmdf/60/recv.c | 1101 ++ network/ndis/ndisprot_kmdf/60/send.c | 337 + .../ndisprot_kmdf/Package/package.VcxProj | 87 + .../Package/package.VcxProj.Filters | 21 + network/ndis/ndisprot_kmdf/ReadMe.md | 158 + network/ndis/ndisprot_kmdf/ndisprot.htm | 197 + network/ndis/ndisprot_kmdf/ndisprot_kmdf.sln | 59 + .../ndis/ndisprot_kmdf/notifyob/Common.hpp | 36 + .../ndisprot_kmdf/notifyob/ProtNotify.cpp | 422 + .../ndisprot_kmdf/notifyob/ProtNotify.def | 6 + .../ndisprot_kmdf/notifyob/ProtNotify.idl | 19 + .../ndis/ndisprot_kmdf/notifyob/ProtNotify.rc | 14 + .../ndisprot_kmdf/notifyob/ProtNotify.rgs | 15 + .../ndisprot_kmdf/notifyob/ProtNotify.vcxproj | 321 + .../notifyob/ProtNotify.vcxproj.Filters | 39 + .../ndis/ndisprot_kmdf/notifyob/commonsrc.cpp | 1 + .../ndis/ndisprot_kmdf/notifyob/dllmain.cpp | 253 + .../ndis/ndisprot_kmdf/notifyob/resource.h | 17 + network/ndis/netvmini/6x/60/netvmini60.inf | Bin 8200 -> 8306 bytes .../ndis/netvmini/6x/60/netvmini60.vcxproj | 5 +- .../netvmini/6x/60/netvmini60.vcxproj.Filters | 8 +- network/ndis/netvmini/6x/620/netvmini620.inf | Bin 12946 -> 13052 bytes .../ndis/netvmini/6x/620/netvmini620.vcxproj | 5 +- .../6x/620/netvmini620.vcxproj.Filters | 8 +- network/ndis/netvmini/6x/630/netvmini630.inf | Bin 15616 -> 15762 bytes .../ndis/netvmini/6x/630/netvmini630.vcxproj | 5 +- .../6x/630/netvmini630.vcxproj.Filters | 8 +- network/ndis/netvmini/6x/netvmini.sln | 66 +- .../HidSwitchDriverSample.sln | 18 +- .../RadioSwitchHidUsbFx2.inx | 16 +- .../RadioSwitchHidUsbFx2.vcxproj | 5 +- .../RadioSwitchHidUsbFx2.vcxproj.Filters | 11 +- .../RadioManagerSample/RadioManagerSample.sln | 18 +- .../RadioManagerSample/cpp/SampleRM.vcxproj | 5 +- .../cpp/SampleRM.vcxproj.Filters | 6 +- network/trans/ReadMe.md | 277 + .../WFPSampler/HCK/WFPLogo_WFPSampler.Answer | 260 + .../WFPSampler/HCK/WFPLogo_WFPSampler.Info | 301 + network/trans/WFPSampler/WFPSampler.sln | 94 + .../docs/ADVANCED_PACKET_INJECTION.mht | 3126 ++++ .../trans/WFPSampler/docs/BASIC_ACTION.mht | 2482 ++++ .../docs/BASIC_PACKET_EXAMINATION.mht | 2577 ++++ .../docs/BASIC_PACKET_INJECTION.mht | 2927 ++++ .../docs/BASIC_PACKET_MODIFICATION.mht | 3944 +++++ .../docs/BASIC_STREAM_INJECTION.mht | 2597 ++++ .../docs/ConditionsForCommandLine.mht | 12205 ++++++++++++++++ .../WFPSampler/docs/FAST_PACKET_INJECTION.mht | 2169 +++ .../WFPSampler/docs/FAST_STREAM_INJECTION.mht | 1960 +++ .../WFPSampler/docs/FLOW_ASSOCIATION.mht | 2199 +++ .../docs/MatchTypesForCommandLine.mht | 1458 ++ .../WFPSampler/docs/PEND_AUTHORIZATION.mht | 2699 ++++ .../WFPSampler/docs/PEND_ENDPOINT_CLOSURE.mht | 2638 ++++ network/trans/WFPSampler/docs/PROXY.mht | 5441 +++++++ .../WFPSampler/docs/_WFPSampler_Overview.mht | 5894 ++++++++ .../exe/Framework_RPCClientInterface.cpp | 435 + .../exe/Framework_RPCClientInterface.h | 28 + .../WFPSampler/exe/Framework_WFPSampler.cpp | 941 ++ .../WFPSampler/exe/Framework_WFPSampler.h | 44 + .../WFPSampler/exe/Framework_WFPSampler.rc | 35 + .../exe/HelperFunctions_CommandLine.cpp | 6790 +++++++++ .../exe/HelperFunctions_CommandLine.h | 63 + .../exe/Scenarios_AdvancedPacketInjection.cpp | 260 + .../exe/Scenarios_AdvancedPacketInjection.h | 31 + .../exe/Scenarios_AppContainers.cpp | 231 + .../WFPSampler/exe/Scenarios_AppContainers.h | 35 + .../WFPSampler/exe/Scenarios_BasicAction.cpp | 487 + .../WFPSampler/exe/Scenarios_BasicAction.h | 42 + .../exe/Scenarios_BasicPacketExamination.cpp | 292 + .../exe/Scenarios_BasicPacketExamination.h | 31 + .../exe/Scenarios_BasicPacketInjection.cpp | 238 + .../exe/Scenarios_BasicPacketInjection.h | 31 + .../exe/Scenarios_BasicPacketModification.cpp | 581 + .../exe/Scenarios_BasicPacketModification.h | 31 + .../exe/Scenarios_BasicStreamInjection.cpp | 236 + .../exe/Scenarios_BasicStreamInjection.h | 31 + .../exe/Scenarios_FastPacketInjection.cpp | 149 + .../exe/Scenarios_FastPacketInjection.h | 31 + .../exe/Scenarios_FastStreamInjection.cpp | 157 + .../exe/Scenarios_FastStreamInjection.h | 31 + .../exe/Scenarios_FlowAssociation.cpp | 319 + .../exe/Scenarios_FlowAssociation.h | 31 + .../trans/WFPSampler/exe/Scenarios_Include.h | 46 + .../exe/Scenarios_PendAuthorization.cpp | 280 + .../exe/Scenarios_PendAuthorization.h | 31 + .../exe/Scenarios_PendEndpointClosure.cpp | 265 + .../exe/Scenarios_PendEndpointClosure.h | 35 + .../trans/WFPSampler/exe/Scenarios_Proxy.cpp | 663 + .../trans/WFPSampler/exe/Scenarios_Proxy.h | 30 + .../trans/WFPSampler/exe/WFPSampler.vcxproj | 206 + .../WFPSampler/exe/WFPSampler.vcxproj.Filters | 67 + .../trans/WFPSampler/idl/WFPSamplerRPC.ACF | 55 + .../trans/WFPSampler/idl/WFPSamplerRPC.IDL | 475 + network/trans/WFPSampler/inc/Identifiers.h | 4082 ++++++ .../trans/WFPSampler/inc/ProviderContexts.h | 229 + network/trans/WFPSampler/inc/ScenarioData.h | 76 + network/trans/WFPSampler/inc/WFPArrays.h | 925 ++ .../lib/HelperFunctions_FwpmCallout.cpp | 387 + .../lib/HelperFunctions_FwpmCallout.h | 59 + .../lib/HelperFunctions_FwpmEngine.cpp | 126 + .../lib/HelperFunctions_FwpmEngine.h | 38 + .../lib/HelperFunctions_FwpmFilter.cpp | 1651 +++ .../lib/HelperFunctions_FwpmFilter.h | 92 + .../lib/HelperFunctions_FwpmLayer.cpp | 1720 +++ .../lib/HelperFunctions_FwpmLayer.h | 54 + .../lib/HelperFunctions_FwpmProvider.cpp | 365 + .../lib/HelperFunctions_FwpmProvider.h | 63 + .../HelperFunctions_FwpmProviderContext.cpp | 340 + .../lib/HelperFunctions_FwpmProviderContext.h | 59 + .../lib/HelperFunctions_FwpmSubLayer.cpp | 367 + .../lib/HelperFunctions_FwpmSubLayer.h | 63 + .../lib/HelperFunctions_FwpmTransaction.cpp | 162 + .../lib/HelperFunctions_FwpmTransaction.h | 35 + .../WFPSampler/lib/HelperFunctions_GUID.cpp | 318 + .../WFPSampler/lib/HelperFunctions_GUID.h | 51 + .../lib/HelperFunctions_IPAddress.cpp | 286 + .../lib/HelperFunctions_IPAddress.h | 43 + .../WFPSampler/lib/HelperFunctions_Include.h | 46 + .../WFPSampler/lib/HelperFunctions_Log.cpp | 213 + .../WFPSampler/lib/HelperFunctions_Log.h | 30 + .../lib/HelperFunctions_MACAddress.cpp | 237 + .../lib/HelperFunctions_MACAddress.h | 30 + .../WFPSampler/lib/HelperFunctions_Macros.h | 408 + .../lib/HelperFunctions_Process.cpp | 196 + .../WFPSampler/lib/HelperFunctions_Process.h | 30 + .../lib/HelperFunctions_Registry.cpp | 232 + .../WFPSampler/lib/HelperFunctions_Registry.h | 59 + .../WFPSampler/lib/HelperFunctions_SID.cpp | 473 + .../WFPSampler/lib/HelperFunctions_SID.h | 54 + .../lib/HelperFunctions_Service.cpp | 508 + .../WFPSampler/lib/HelperFunctions_Service.h | 53 + .../lib/HelperFunctions_Strings.cpp | 196 + .../WFPSampler/lib/HelperFunctions_Strings.h | 36 + .../lib/HelperFunctions_ThreadPools.cpp | 179 + .../lib/HelperFunctions_ThreadPools.h | 49 + .../lib/HelperFunctions_ThreadsAndEvents.cpp | 389 + .../lib/HelperFunctions_ThreadsAndEvents.h | 63 + .../lib/HelperFunctions_WinSock.cpp | 999 ++ .../WFPSampler/lib/HelperFunctions_WinSock.h | 129 + .../trans/WFPSampler/lib/WFPSampler.vcxproj | 213 + .../WFPSampler/lib/WFPSampler.vcxproj.Filters | 88 + .../WFPSampler/scripts/WFPSamplerInstall.cmd | 227 + .../trans/WFPSampler/svc/Framework_Include.h | 34 + .../svc/Framework_RPCServerInterface.cpp | 420 + .../svc/Framework_RPCServerInterface.h | 30 + .../svc/Framework_WFPSamplerService.cpp | 841 ++ .../svc/Framework_WFPSamplerService.h | 48 + .../svc/Framework_WFPSamplerService.rc | 38 + .../svc/Framework_WFPSamplerService_Msg.mc | 72 + .../svc/Framework_WindowsFirewall.cpp | 373 + .../svc/Framework_WindowsFirewall.h | 30 + .../svc/Scenarios_AdvancedPacketInjection.cpp | 401 + .../svc/Scenarios_AppContainers.cpp | 764 + .../WFPSampler/svc/Scenarios_BasicAction.cpp | 609 + .../svc/Scenarios_BasicPacketExamination.cpp | 398 + .../svc/Scenarios_BasicPacketInjection.cpp | 403 + .../svc/Scenarios_BasicPacketModification.cpp | 400 + .../svc/Scenarios_BasicStreamInjection.cpp | 361 + .../svc/Scenarios_FastPacketInjection.cpp | 353 + .../svc/Scenarios_FastStreamInjection.cpp | 312 + .../svc/Scenarios_FlowAssociation.cpp | 347 + .../svc/Scenarios_PendAuthorization.cpp | 362 + .../svc/Scenarios_PendEndpointClosure.cpp | 379 + .../trans/WFPSampler/svc/Scenarios_Proxy.cpp | 702 + .../WFPSampler/svc/WFPSamplerService.vcxproj | 208 + .../svc/WFPSamplerService.vcxproj.Filters | 72 + ...ctions_AdvancedPacketInjectionCallouts.cpp | 4250 ++++++ ...unctions_AdvancedPacketInjectionCallouts.h | 74 + .../ClassifyFunctions_BasicActionCallouts.cpp | 655 + .../ClassifyFunctions_BasicActionCallouts.h | 116 + ...nctions_BasicPacketExaminationCallouts.cpp | 5640 +++++++ ...Functions_BasicPacketExaminationCallouts.h | 53 + ...Functions_BasicPacketInjectionCallouts.cpp | 4231 ++++++ ...fyFunctions_BasicPacketInjectionCallouts.h | 75 + ...ctions_BasicPacketModificationCallouts.cpp | 6604 +++++++++ ...unctions_BasicPacketModificationCallouts.h | 62 + ...Functions_BasicStreamInjectionCallouts.cpp | 947 ++ ...fyFunctions_BasicStreamInjectionCallouts.h | 99 + ...yFunctions_FastPacketInjectionCallouts.cpp | 1875 +++ ...ifyFunctions_FastPacketInjectionCallouts.h | 56 + ...yFunctions_FastStreamInjectionCallouts.cpp | 295 + ...ifyFunctions_FastStreamInjectionCallouts.h | 55 + ...ssifyFunctions_FlowAssociationCallouts.cpp | 238 + ...lassifyFunctions_FlowAssociationCallouts.h | 51 + .../sys/ClassifyFunctions_Include.h | 41 + ...ifyFunctions_PendAuthorizationCallouts.cpp | 1015 ++ ...ssifyFunctions_PendAuthorizationCallouts.h | 61 + ...yFunctions_PendEndpointClosureCallouts.cpp | 410 + ...ifyFunctions_PendEndpointClosureCallouts.h | 48 + .../sys/ClassifyFunctions_ProxyCallouts.cpp | 2424 +++ .../sys/ClassifyFunctions_ProxyCallouts.h | 91 + ...ctions_AdvancedPacketInjectionCallouts.cpp | 312 + ...unctions_AdvancedPacketInjectionCallouts.h | 62 + ...Functions_BasicPacketInjectionCallouts.cpp | 308 + ...onFunctions_BasicPacketInjectionCallouts.h | 61 + ...ctions_BasicPacketModificationCallouts.cpp | 200 + ...unctions_BasicPacketModificationCallouts.h | 52 + ...Functions_BasicStreamInjectionCallouts.cpp | 164 + ...onFunctions_BasicStreamInjectionCallouts.h | 50 + ...nFunctions_FastPacketInjectionCallouts.cpp | 79 + ...ionFunctions_FastPacketInjectionCallouts.h | 33 + ...nFunctions_FastStreamInjectionCallouts.cpp | 69 + ...ionFunctions_FastStreamInjectionCallouts.h | 33 + .../sys/CompletionFunctions_Include.h | 35 + ...ionFunctions_PendAuthorizationCallouts.cpp | 196 + ...etionFunctions_PendAuthorizationCallouts.h | 52 + .../sys/CompletionFunctions_ProxyCallouts.cpp | 183 + .../sys/CompletionFunctions_ProxyCallouts.h | 54 + .../trans/WFPSampler/sys/Framework_Events.cpp | 300 + .../trans/WFPSampler/sys/Framework_Events.h | 66 + .../trans/WFPSampler/sys/Framework_Include.h | 28 + .../WFPSampler/sys/Framework_PowerStates.cpp | 210 + .../WFPSampler/sys/Framework_PowerStates.h | 31 + .../sys/Framework_WFPSamplerCalloutDriver.cpp | 533 + .../sys/Framework_WFPSamplerCalloutDriver.h | 95 + .../sys/Framework_WFPSamplerCalloutDriver.rc | 28 + .../sys/HelperFunctions_ExposedCallouts.cpp | 1265 ++ .../sys/HelperFunctions_ExposedCallouts.h | 650 + .../sys/NotifyFunctions_AdvancedCallouts.cpp | 223 + .../sys/NotifyFunctions_AdvancedCallouts.h | 45 + .../sys/NotifyFunctions_BasicCallouts.cpp | 225 + .../sys/NotifyFunctions_BasicCallouts.h | 46 + .../sys/NotifyFunctions_FastCallouts.cpp | 227 + .../sys/NotifyFunctions_FastCallouts.h | 46 + .../sys/NotifyFunctions_FlowDelete.cpp | 95 + .../sys/NotifyFunctions_FlowDelete.h | 44 + .../WFPSampler/sys/NotifyFunctions_Include.h | 36 + .../sys/NotifyFunctions_PendCallouts.cpp | 257 + .../sys/NotifyFunctions_PendCallouts.h | 47 + .../sys/NotifyFunctions_ProxyCallouts.cpp | 248 + .../sys/NotifyFunctions_ProxyCallouts.h | 52 + .../sys/SubscriptionFunctions_BFEState.cpp | 99 + .../sys/SubscriptionFunctions_BFEState.h | 30 + .../sys/SubscriptionFunctions_Include.h | 27 + .../sys/WFPSamplerCalloutDriver.InX | 93 + .../sys/WFPSamplerCalloutDriver.vcxproj | 239 + .../WFPSamplerCalloutDriver.vcxproj.Filters | 126 + .../syslib/HelperFunctions_ClassifyData.cpp | 493 + .../syslib/HelperFunctions_ClassifyData.h | 87 + ...HelperFunctions_DeferredProcedureCalls.cpp | 1630 +++ .../HelperFunctions_DeferredProcedureCalls.h | 275 + .../syslib/HelperFunctions_FlowContext.cpp | 385 + .../syslib/HelperFunctions_FlowContext.h | 145 + .../syslib/HelperFunctions_FwpObjects.cpp | 9068 ++++++++++++ .../syslib/HelperFunctions_FwpObjects.h | 737 + .../syslib/HelperFunctions_Headers.cpp | 3899 +++++ .../syslib/HelperFunctions_Headers.h | 678 + .../syslib/HelperFunctions_ICMPMessages.h | 152 + .../syslib/HelperFunctions_Include.h | 41 + .../syslib/HelperFunctions_InjectionData.cpp | 988 ++ .../syslib/HelperFunctions_InjectionData.h | 105 + .../syslib/HelperFunctions_Macros.h | 454 + .../syslib/HelperFunctions_NDIS.cpp | 326 + .../WFPSampler/syslib/HelperFunctions_NDIS.h | 65 + .../syslib/HelperFunctions_NetBuffer.cpp | 437 + .../syslib/HelperFunctions_NetBuffer.h | 70 + .../syslib/HelperFunctions_NotifyData.h | 33 + .../syslib/HelperFunctions_PendData.cpp | 411 + .../syslib/HelperFunctions_PendData.h | 85 + .../syslib/HelperFunctions_RedirectData.cpp | 340 + .../syslib/HelperFunctions_RedirectData.h | 90 + .../syslib/HelperFunctions_WorkItems.cpp | 1242 ++ .../syslib/HelperFunctions_WorkItems.h | 223 + .../WFPSampler/syslib/WFPSampler.vcxproj | 201 + .../syslib/WFPSampler.vcxproj.Filters | 56 + network/trans/ddproxy/ddproxy.sln | 18 +- network/trans/ddproxy/sys/ddproxy.inf | 4 +- network/trans/ddproxy/sys/ddproxy.vcxproj | 5 +- .../trans/ddproxy/sys/ddproxy.vcxproj.Filters | 8 +- network/trans/inspect/inspect.sln | 18 +- network/trans/inspect/sys/inspect.inf | 6 +- network/trans/inspect/sys/inspect.vcxproj | 5 +- .../trans/inspect/sys/inspect.vcxproj.Filters | 8 +- network/trans/msnmntr/exe/monitor.vcxproj | 5 +- .../trans/msnmntr/exe/monitor.vcxproj.Filters | 6 +- network/trans/msnmntr/msnmntr.sln | 44 +- network/trans/msnmntr/sys/msnmntr.inf | 4 +- network/trans/msnmntr/sys/msnmntr.vcxproj | 5 +- .../trans/msnmntr/sys/msnmntr.vcxproj.Filters | 8 +- network/trans/stmedit/stmedit.sln | 18 +- network/trans/stmedit/sys/stmedit.inf | 4 +- network/trans/stmedit/sys/stmedit.vcxproj | 5 +- .../trans/stmedit/sys/stmedit.vcxproj.Filters | 8 +- network/wlan/ihvfrm/arm/rc4utils.dll | Bin 0 -> 36688 bytes network/wlan/ihvfrm/arm/rc4utils.lib | Bin 0 -> 2688 bytes network/wlan/ihvfrm/arm64/rc4utils.dll | Bin 0 -> 17744 bytes network/wlan/ihvfrm/arm64/rc4utils.lib | Bin 0 -> 2696 bytes network/wlan/ihvsample/ihvsample.vcxproj | 9 +- .../wlan/ihvsample/ihvsample.vcxproj.Filters | 6 +- network/wlan/ihvsampleui/IHVSampleUI.vcxproj | 5 +- .../ihvsampleui/IHVSampleUI.vcxproj.Filters | 6 +- network/wlan/wlan.sln | 44 +- network/wsk/echosrv/echosrv.sln | 18 +- network/wsk/echosrv/echosrv.vcxproj | 5 +- network/wsk/echosrv/echosrv.vcxproj.Filters | 8 +- nfc/NfcSimulator.sln | 28 + nfc/Simulator/Inc/NfcSimulatorDDI.h | 34 + nfc/Simulator/README.md | 14 + nfc/Simulator/Src/Connection.cpp | 271 + nfc/Simulator/Src/Connection.h | 121 + nfc/Simulator/Src/Constants.h | 102 + nfc/Simulator/Src/Device.cpp | 245 + nfc/Simulator/Src/Device.h | 55 + nfc/Simulator/Src/Driver.cpp | 70 + nfc/Simulator/Src/Driver.h | 27 + nfc/Simulator/Src/FileContext.cpp | 640 + nfc/Simulator/Src/FileContext.h | 268 + nfc/Simulator/Src/Internal.h | 84 + nfc/Simulator/Src/Internalsrc.cpp | 1 + nfc/Simulator/Src/LinkList.h | 121 + nfc/Simulator/Src/NfcDriverSim.vcxproj | 371 + .../Src/NfcDriverSim.vcxproj.Filters | 69 + nfc/Simulator/Src/NfcSimulator.def | 6 + nfc/Simulator/Src/NfcSimulator.inx | Bin 0 -> 4110 bytes nfc/Simulator/Src/NfcSimulator.rc | 15 + nfc/Simulator/Src/Queue.cpp | 2420 +++ nfc/Simulator/Src/Queue.h | 217 + nfc/Simulator/Src/RoutingTable.cpp | 388 + nfc/Simulator/Src/RoutingTable.h | 55 + nfc/Simulator/Src/SecureElement.cpp | 52 + nfc/Simulator/Src/SecureElement.h | 56 + nfc/Simulator/Src/SmartCard.cpp | 312 + nfc/Simulator/Src/SmartCard.h | 100 + nfc/Simulator/Src/SmartCardReader.cpp | 564 + nfc/Simulator/Src/SmartCardReader.h | 87 + nfc/Simulator/Src/SocketListener.cpp | 185 + nfc/Simulator/Src/SocketListener.h | 85 + nfc/Simulator/Src/WppDefs.h | 367 + nfp/net/driver/NetNfpProvider.vcxproj | 5 +- nfp/net/driver/NetNfpProvider.vcxproj.Filters | 11 +- nfp/net/driver/netnfpprovider.inx | 15 +- nfp/net/exe/NetNfpControl.vcxproj | 5 +- nfp/net/exe/NetNfpControl.vcxproj.Filters | 6 +- nfp/net/netnfp.sln | 44 +- pofx/PEP/ReadMe.md | 9 + pofx/PEP/acpi/acpispecific.c | 392 + pofx/PEP/acpi/acpispecific.h | 298 + pofx/PEP/acpi/pchsrc.c | 1 + pofx/PEP/acpi/sampleacpipep.inx | 85 + pofx/PEP/acpi/sampleacpipep.rc | 12 + pofx/PEP/acpi/sampleacpipep.vcxproj | 266 + pofx/PEP/acpi/sampleacpipep.vcxproj.Filters | 42 + pofx/PEP/acpi/testdevice.c | 2093 +++ pofx/PEP/common/acpinotify.c | 716 + pofx/PEP/common/driver.c | 341 + pofx/PEP/common/pchsrc.c | 1 + pofx/PEP/common/pep.c | 489 + pofx/PEP/common/pepcommon.vcxproj | 284 + pofx/PEP/common/pepcommon.vcxproj.Filters | 41 + pofx/PEP/common/util.c | 1077 ++ pofx/PEP/common/work.c | 737 + pofx/PEP/inc/common.h | 88 + pofx/PEP/inc/pch.h | 60 + pofx/PEP/inc/pep.h | 674 + pofx/PEP/inc/trace.h | 99 + pofx/PEP/pepsamples.sln | 49 + pofx/UMDF2/App/PowerFxApp.vcxproj | 17 +- pofx/UMDF2/App/PowerFxApp.vcxproj.Filters | 6 +- .../SingleComponentSingleStateUm.inx | 4 +- .../SingleComponentSingleStateUm.vcxproj | 17 +- ...ngleComponentSingleStateUm.vcxproj.Filters | 11 +- pofx/UMDF2/ReadMe.md | 3 - pofx/UMDF2/pofx.sln | 48 +- pofx/WDF/App/PowerFxApp.vcxproj | 17 +- pofx/WDF/App/PowerFxApp.vcxproj.Filters | 6 +- .../Driver/MultiComp/driver/WdfMultiComp.inx | 7 +- .../MultiComp/driver/WdfMultiComp.vcxproj | 17 +- .../driver/WdfMultiComp.vcxproj.Filters | 11 +- pofx/WDF/Driver/MultiComp/lib/WdfPoFx.vcxproj | 5 +- .../MultiComp/lib/WdfPoFx.vcxproj.Filters | 8 +- .../SingleComponentFStateDriver.vcxproj | 17 +- ...ingleComponentFStateDriver.vcxproj.Filters | 11 +- .../SingleComponentFStateSample.inx | 4 +- pofx/WDF/ReadMe.md | 3 - pofx/WDF/pofx.sln | 102 +- pos/drivers/MagneticStripeReader/Device.cpp | 101 + pos/drivers/MagneticStripeReader/Device.h | 3 + pos/drivers/MagneticStripeReader/Driver.cpp | 54 + pos/drivers/MagneticStripeReader/File.cpp | 37 + pos/drivers/MagneticStripeReader/File.h | 4 + pos/drivers/MagneticStripeReader/IoRead.cpp | 41 + pos/drivers/MagneticStripeReader/IoRead.h | 3 + pos/drivers/MagneticStripeReader/Ioctl.cpp | 878 ++ pos/drivers/MagneticStripeReader/Ioctl.h | 3 + .../MagneticStripeReader.sln | 28 + .../MagneticStripeReader/PosEvents.cpp | 50 + pos/drivers/MagneticStripeReader/PosEvents.h | 3 + pos/drivers/MagneticStripeReader/README.md | 7 + .../SampleMagneticStripeReaderDrv.inf | 78 + .../SampleMagneticStripeReaderDrv.vcxproj | 252 + ...pleMagneticStripeReaderDrv.vcxproj.Filters | 47 + pos/drivers/MagneticStripeReader/exports.def | 2 + pos/drivers/MagneticStripeReader/pch.h | 23 + pos/drivers/MagneticStripeReader/pchsrc.cpp | 1 + pos/drivers/barcodescanner/BarcodeScanner.sln | 28 + pos/drivers/barcodescanner/Device.cpp | 101 + pos/drivers/barcodescanner/Device.h | 3 + pos/drivers/barcodescanner/Driver.cpp | 54 + pos/drivers/barcodescanner/File.cpp | 37 + pos/drivers/barcodescanner/File.h | 4 + pos/drivers/barcodescanner/IoRead.cpp | 41 + pos/drivers/barcodescanner/IoRead.h | 3 + pos/drivers/barcodescanner/Ioctl.cpp | 720 + pos/drivers/barcodescanner/Ioctl.h | 3 + pos/drivers/barcodescanner/PosEvents.cpp | 97 + pos/drivers/barcodescanner/PosEvents.h | 3 + pos/drivers/barcodescanner/README.md | 7 + .../SampleBarcodeScannerDrv.inf | 78 + .../SampleBarcodeScannerDrv.vcxproj | 252 + .../SampleBarcodeScannerDrv.vcxproj.Filters | 47 + pos/drivers/barcodescanner/exports.def | 2 + pos/drivers/barcodescanner/pch.h | 23 + pos/drivers/barcodescanner/pchsrc.cpp | 1 + .../BlankProjSrc/BlankProject.vcxproj | 5 +- .../BlankProjSrc/BlankProject.vcxproj.Filters | 6 +- print/SampleOpenXPS/SampleOpenXPS.sln | 18 +- .../BlankProjSrc/BlankProject.vcxproj | 5 +- .../BlankProjSrc/BlankProject.vcxproj.Filters | 6 +- print/SampleXPS/SampleXPS.sln | 18 +- .../SimplePipelineFilter.sln | 18 +- .../WdkPipelineFilter.vcxproj | 5 +- .../WdkPipelineFilter.vcxproj.Filters | 6 +- print/XPSDrvSmpl/Package/package.VcxProj | 72 +- .../Package/package.VcxProj.Filters | 8 +- print/XPSDrvSmpl/XPSDrvSmpl.sln | 314 +- print/XPSDrvSmpl/install/xdsmpl.inf | 20 +- print/XPSDrvSmpl/src/common/precomp.h | 3 + print/XPSDrvSmpl/src/common/xdsmplcmn.vcxproj | 5 +- .../src/common/xdsmplcmn.vcxproj.Filters | 6 +- .../src/filters/booklet/xdbook.vcxproj | 5 +- .../filters/booklet/xdbook.vcxproj.Filters | 6 +- .../src/filters/color/XDColMan.vcxproj | 5 +- .../filters/color/XDColMan.vcxproj.Filters | 6 +- .../src/filters/common/xdfltcmn.vcxproj | 5 +- .../filters/common/xdfltcmn.vcxproj.Filters | 6 +- .../XPSDrvSmpl/src/filters/nup/xdnup.vcxproj | 5 +- .../src/filters/nup/xdnup.vcxproj.Filters | 6 +- print/XPSDrvSmpl/src/filters/precomp.h | 3 + .../src/filters/scaling/xdscale.vcxproj | 5 +- .../filters/scaling/xdscale.vcxproj.Filters | 6 +- .../src/filters/watermark/xdwmark.vcxproj | 5 +- .../filters/watermark/xdwmark.vcxproj.Filters | 6 +- .../src/filters/xdcont/xdcont.vcxproj | 5 +- .../src/filters/xdcont/xdcont.vcxproj.Filters | 6 +- print/XPSDrvSmpl/src/ui/precomp.h | 3 + print/XPSDrvSmpl/src/ui/xdsmplui.vcxproj | 5 +- .../src/ui/xdsmplui.vcxproj.Filters | 6 +- print/XpsRasFilter/XpsRasFilter.sln | 18 +- print/XpsRasFilter/install/xpsrassmpl.inf | 14 +- print/XpsRasFilter/src/xpsrasfilter.vcxproj | 5 +- .../src/xpsrasfilter.vcxproj.Filters | 6 +- print/autoconfig/AutoCnfg.inf | 13 +- print/autoconfig/AutoConfig.sln | 46 +- print/autoconfig/AutoConfig.vcxproj | 5 +- print/autoconfig/AutoConfig.vcxproj.Filters | 8 +- print/autoconfig/Package/package.VcxProj | 18 +- .../Package/package.VcxProj.Filters | 8 +- print/cpsuisam/cpsuisam.sln | 18 +- print/cpsuisam/cpsuisam.vcxproj | 5 +- print/cpsuisam/cpsuisam.vcxproj.Filters | 6 +- .../PrinterExtensionSample.sln | 8 +- .../ConstraintScript.sln | 18 +- .../ConstraintScript.vcxproj | 5 +- .../ConstraintScript.vcxproj.Filters | 8 +- .../HostBasedSampleDriver.sln | 40 +- .../HostBasedSampleDriver.vcxproj | 5 +- .../HostBasedSampleDriver.vcxproj.Filters | 8 +- .../Package/package.VcxProj | 18 +- .../Package/package.VcxProj.Filters | 8 +- .../usb_host_based_sample.inf | 23 +- .../USBMon-Bidi-Extension.sln | 18 +- .../USBMon-Bidi-Extension.vcxproj | 5 +- .../USBMon-Bidi-Extension.vcxproj.Filters | 8 +- .../WSDMon-Bidi-Extension.sln | 18 +- .../WSDMon-Bidi-Extension.vcxproj | 5 +- .../WSDMon-Bidi-Extension.vcxproj.Filters | 8 +- sd/miniport/sdhc/README.md | 4 +- sd/miniport/sdhc/inbox/sdhc.vcxproj | 5 +- sd/miniport/sdhc/inbox/sdhc.vcxproj.Filters | 11 +- sd/miniport/sdhc/sdhc.c | 114 + sd/miniport/sdhc/sdhc.h | 20 + sd/miniport/sdhc/sdhc.inx | Bin 11250 -> 7864 bytes sd/miniport/sdhc/sdhc.sln | 18 +- sd/sdiomars/mars.inx | Bin 4408 -> 4248 bytes sd/sdiomars/mars.vcxproj | 5 +- sd/sdiomars/mars.vcxproj.Filters | 11 +- sd/sdiomars/sdiomars.sln | 18 +- security/elam/elam.sln | 18 +- security/elam/elamsample.vcxproj | 5 +- security/elam/elamsample.vcxproj.Filters | 8 +- sensors/ADXL345Acc/ADXL345Acc.inx | 9 +- sensors/ADXL345Acc/ADXL345Acc.sln | 18 +- sensors/ADXL345Acc/ADXL345Acc.vcxproj | 5 +- sensors/ADXL345Acc/ADXL345Acc.vcxproj.Filters | 11 +- sensors/ADXL345Acc/Device.h | 2 + sensors/ADXL345Acc/client.cpp | 15 +- sensors/Activity/Activity.inx | 9 +- sensors/Activity/Activity.sln | 18 +- sensors/Activity/Activity.vcxproj | 5 +- sensors/Activity/Activity.vcxproj.Filters | 11 +- sensors/Activity/client.cpp | 6 +- sensors/Activity/hardwaresimulator.cpp | 4 +- sensors/CustomSensors/CustomSensors.inx | 9 +- sensors/CustomSensors/CustomSensors.sln | 18 +- sensors/CustomSensors/CustomSensors.vcxproj | 5 +- .../CustomSensors.vcxproj.Filters | 11 +- sensors/CustomSensors/client.cpp | 2 +- sensors/CustomSensors/device.cpp | 4 +- sensors/Pedometer/Pedometer.inx | 9 +- sensors/Pedometer/Pedometer.sln | 18 +- sensors/Pedometer/Pedometer.vcxproj | 5 +- sensors/Pedometer/Pedometer.vcxproj.Filters | 11 +- sensors/Pedometer/device.cpp | 2 +- sensors/Pedometer/hardwaresimulator.cpp | 2 +- .../SimpleDeviceOrientationSensor.inx | 10 +- .../SimpleDeviceOrientationSensor.sln | 18 +- .../SimpleDeviceOrientationSensor.vcxproj | 5 +- ...pleDeviceOrientationSensor.vcxproj.Filters | 11 +- .../SimpleDeviceOrientationSensor/client.cpp | 4 +- .../SimpleDeviceOrientationSensor/device.cpp | 4 +- serial/VirtualSerial2/ReadMe.md | 33 +- setup/DIFxAPI/AppDrv/AppDrv.vcxproj | 5 +- setup/DIFxAPI/AppDrv/AppDrv.vcxproj.Filters | 6 +- setup/DIFxAPI/DIFxAPI.sln | 44 +- setup/DIFxAPI/DIFxCmd/DIFxCmd.vcxproj | 5 +- setup/DIFxAPI/DIFxCmd/DIFxCmd.vcxproj.Filters | 6 +- setup/devcon/devcon.sln | 18 +- setup/devcon/devcon.vcxproj | 5 +- setup/devcon/devcon.vcxproj.Filters | 6 +- simbatt/ReadMe.md | 4 + simbatt/SimBatt.sln | 28 + simbatt/func/batclass_prepublish.h | 51 + simbatt/func/miniclass.c | 1634 +++ simbatt/func/simbatt-wdfcoinstall.inx | 108 + simbatt/func/simbatt.h | 136 + simbatt/func/simbatt.inx | 80 + simbatt/func/simbatt.rc | 11 + simbatt/func/simbatt.vcxproj | 193 + simbatt/func/simbatt.vcxproj.Filters | 34 + simbatt/func/simbattdriverif.h | 128 + simbatt/func/wdf.c | 935 ++ smartcrd/ReadMe.md | 12 +- smartcrd/pscr/Pscr.vcxproj | 5 +- smartcrd/pscr/Pscr.vcxproj.Filters | 11 +- smartcrd/pscr/pscr.inx | Bin 6292 -> 6338 bytes smartcrd/smartcrd.sln | 18 +- spb/SkeletonI2C/SkeletonI2C.sln | 18 +- spb/SkeletonI2C/skeletoni2c.inx | Bin 3856 -> 3874 bytes spb/SkeletonI2C/skeletoni2c.vcxproj | 13 +- spb/SkeletonI2C/skeletoni2c.vcxproj.Filters | 11 +- spb/SpbTestTool/SpbTestTool.sln | 44 +- spb/SpbTestTool/exe/SpbTestTool.vcxproj | 5 +- .../exe/SpbTestTool.vcxproj.Filters | 6 +- spb/SpbTestTool/sys/SpbTestTool.vcxproj | 13 +- .../sys/SpbTestTool.vcxproj.Filters | 11 +- spb/SpbTestTool/sys/spbtesttool.inx | Bin 4682 -> 4688 bytes storage/class/cdrom/ReadMe.md | 1 + storage/class/cdrom/cdrom.sln | 18 +- storage/class/cdrom/src/cdrom.vcxproj | 5 +- storage/class/cdrom/src/cdrom.vcxproj.Filters | 11 +- storage/class/classpnp/classpnp.sln | 18 +- storage/class/classpnp/src/class.c | 16 +- storage/class/classpnp/src/classp.h | 25 +- storage/class/classpnp/src/classpnp.vcxproj | 5 +- .../classpnp/src/classpnp.vcxproj.Filters | 8 +- storage/class/classpnp/src/retry.c | 69 +- storage/class/classpnp/src/utils.c | 123 +- storage/class/classpnp/src/xferpkt.c | 10 +- storage/class/disk/disk.sln | 18 +- storage/class/disk/src/disk.vcxproj | 5 +- storage/class/disk/src/disk.vcxproj.Filters | 8 +- storage/class/disk/src/diskdev.inf | 34 +- storage/filters/addfilter/addfilter.sln | 18 +- .../filters/addfilter/src/addfilter.vcxproj | 5 +- .../addfilter/src/addfilter.vcxproj.Filters | 6 +- storage/iscsi/iscsi.sln | 18 +- storage/iscsi/src/EmptyProject.vcxproj | 5 +- .../iscsi/src/EmptyProject.vcxproj.Filters | 6 +- storage/iscsi/src/iscsihba.mof | 45 + storage/miniports/lsi_u3/lsi_u3.sln | 18 +- storage/miniports/lsi_u3/src/lsi_u3.vcxproj | 5 +- .../lsi_u3/src/lsi_u3.vcxproj.Filters | 8 +- .../storahci/src/inbox/storahci.vcxproj | 5 +- .../src/inbox/storahci.vcxproj.Filters | 8 +- storage/miniports/storahci/storahci.sln | 18 +- storage/msdsm/ReadMe.md | 197 + storage/msdsm/msdsm.sln | 28 + storage/msdsm/src/SampleDSM.inf | Bin 0 -> 5354 bytes storage/msdsm/src/SampleDSM.vcxproj | 301 + storage/msdsm/src/SampleDSM.vcxproj.Filters | 51 + storage/msdsm/src/dsmmain.c | 9752 ++++++++++++ storage/msdsm/src/dsmtrace.mof | 111 + storage/msdsm/src/intrface.c | 5198 +++++++ storage/msdsm/src/msdsm.h | 1403 ++ storage/msdsm/src/msdsm.mof | 82 + storage/msdsm/src/msdsm.rc | 24 + storage/msdsm/src/msdsmdsm.mof | 141 + storage/msdsm/src/precomp.h | 32 + storage/msdsm/src/precompsrc.c | 1 + storage/msdsm/src/prototypes.h | 1436 ++ storage/msdsm/src/trace.h | 35 + storage/msdsm/src/utils.c | 7932 ++++++++++ storage/msdsm/src/wmi.c | 3821 +++++ storage/ramdisk/ramdisk.sln | 18 +- storage/ramdisk/src/WdfRamdisk.vcxproj | 5 +- .../ramdisk/src/WdfRamdisk.vcxproj.Filters | 11 +- storage/ramdisk/src/ramdisk.inx | 15 +- storage/sfloppy/sfloppy.sln | 18 +- storage/sfloppy/src/sfloppy.inf | 8 +- storage/sfloppy/src/sfloppy.vcxproj | 5 +- storage/sfloppy/src/sfloppy.vcxproj.Filters | 8 +- storage/tools/spti/spti.sln | 18 +- storage/tools/spti/src/spti.vcxproj | 5 +- storage/tools/spti/src/spti.vcxproj.Filters | 6 +- thermal/simsensor/simsensor.inf | 4 +- thermal/simsensor/simsensor.sln | 18 +- thermal/simsensor/simsensor.vcxproj | 5 +- thermal/simsensor/simsensor.vcxproj.Filters | 8 +- thermal/thermalclient/simtc.inf | 4 +- thermal/thermalclient/simtc.vcxproj | 5 +- thermal/thermalclient/simtc.vcxproj.Filters | 8 +- thermal/thermalclient/thermalclient.sln | 18 +- .../SDV-FailDriver-KMDF.sln | 46 +- .../driver/fail_driver1.vcxproj | 5 +- .../driver/fail_driver1.vcxproj.Filters | 8 +- .../library/fail_library1.vcxproj | 5 +- .../library/fail_library1.vcxproj.Filters | 8 +- .../SDV-FailDriver-NDIS.sln | 18 +- .../SDV-FailDriver-NDIS/driver/sdvmp.vcxproj | 5 +- .../driver/sdvmp.vcxproj.Filters | 8 +- .../SDV-FailDriver-STORPORT.sln | 18 +- .../driver/lsi_u3.vcxproj | 5 +- .../driver/lsi_u3.vcxproj.Filters | 8 +- .../SDV-FailDriver-WDM/SDV-FailDriver-WDM.sln | 18 +- .../driver/fail_driver1.vcxproj | 5 +- .../driver/fail_driver1.vcxproj.Filters | 8 +- usb/kmdf_enumswitches/kmdf_enumswitches.sln | 18 +- .../sys/kmdf_enumswitches.inx | 8 +- .../sys/kmdf_enumswitches.vcxproj | 13 +- .../sys/kmdf_enumswitches.vcxproj.Filters | 11 +- usb/kmdf_fx2/ReadMe.md | 393 + ...6-4807-ACCD-B28C09D37FF0.devicemetadata-ms | Bin 0 -> 105893 bytes usb/kmdf_fx2/driver/Device.c | 1051 ++ usb/kmdf_fx2/driver/bulkrwr.c | 436 + usb/kmdf_fx2/driver/driver.c | 299 + usb/kmdf_fx2/driver/interrupt.c | 184 + usb/kmdf_fx2/driver/ioctl.c | 1057 ++ usb/kmdf_fx2/driver/osrusbfx2.h | 335 + usb/kmdf_fx2/driver/osrusbfx2.inx | 112 + usb/kmdf_fx2/driver/osrusbfx2.man | 309 + usb/kmdf_fx2/driver/osrusbfx2.rc | 18 + usb/kmdf_fx2/driver/osrusbfx2.vcxproj | 269 + usb/kmdf_fx2/driver/osrusbfx2.vcxproj.Filters | 51 + usb/kmdf_fx2/driver/trace.h | 115 + usb/kmdf_fx2/exe/dump.c | 444 + usb/kmdf_fx2/exe/osrusbfx2.vcxproj | 192 + usb/kmdf_fx2/exe/osrusbfx2.vcxproj.Filters | 30 + usb/kmdf_fx2/exe/test.cmd | 6 + usb/kmdf_fx2/exe/testapp.c | 1210 ++ usb/kmdf_fx2/exe/testapp.rc | 12 + usb/kmdf_fx2/inc/prototypes.h | 14 + usb/kmdf_fx2/inc/public.h | 173 + usb/kmdf_fx2/kmdf_fx2.sln | 46 + usb/ufxclientsample/UfxClientSample.inx | Bin 0 -> 4136 bytes usb/ufxclientsample/UfxClientSample.man | 298 + usb/ufxclientsample/defaultqueue.c | 200 + usb/ufxclientsample/defaultqueue.h | 38 + usb/ufxclientsample/device.c | 566 + usb/ufxclientsample/device.h | 141 + usb/ufxclientsample/driver.c | 184 + usb/ufxclientsample/driver.h | 33 + usb/ufxclientsample/event.c | 340 + usb/ufxclientsample/event.h | 65 + usb/ufxclientsample/interrupt.c | 315 + usb/ufxclientsample/interrupt.h | 47 + usb/ufxclientsample/readme.md | 28 + usb/ufxclientsample/registers.c | 90 + usb/ufxclientsample/registers.h | 58 + usb/ufxclientsample/trace.h | 113 + usb/ufxclientsample/transfer.c | 2230 +++ usb/ufxclientsample/transfer.h | 180 + usb/ufxclientsample/ufxclientsample.rc | 14 + usb/ufxclientsample/ufxclientsample.sln | 28 + usb/ufxclientsample/ufxclientsample.vcxproj | 219 + .../ufxclientsample.vcxproj.Filters | 63 + usb/ufxclientsample/ufxdevice.c | 902 ++ usb/ufxclientsample/ufxdevice.h | 67 + usb/ufxclientsample/ufxendpoint.c | 481 + usb/ufxclientsample/ufxendpoint.h | 73 + usb/umdf2_fx2/ReadMe.md | 336 +- usb/umdf2_fx2/driver/osrusbfx2um.inx | 11 +- usb/umdf2_fx2/driver/osrusbfx2um.vcxproj | 5 +- .../driver/osrusbfx2um.vcxproj.Filters | 11 +- usb/umdf2_fx2/exe/osrusbfx2.vcxproj | 5 +- usb/umdf2_fx2/exe/osrusbfx2.vcxproj.Filters | 6 +- usb/umdf2_fx2/exe/testapp.c | 2 +- usb/umdf2_fx2/umdf2_fx2.sln | 44 +- usb/umdf_filter_kmdf/Package/package.VcxProj | 87 + .../Package/package.VcxProj.Filters | 21 + usb/umdf_filter_kmdf/ReadMe.md | 78 + 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 + .../kmdf_driver/osrusbfx2.man | 309 + usb/umdf_filter_kmdf/kmdf_driver/osrusbfx2.rc | 18 + .../kmdf_driver/osrusbfx2.vcxproj | 244 + .../kmdf_driver/osrusbfx2.vcxproj.Filters | 46 + usb/umdf_filter_kmdf/kmdf_driver/trace.h | 115 + .../umdf_filter/OsrUsbFilter.rc | 17 + .../umdf_filter/WUDFOsrUsbFilter.vcxproj | 286 + .../WUDFOsrUsbFilter.vcxproj.Filters | 51 + .../WUDFOsrUsbFilterOnKmDriver.inx | 122 + 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 | 24 +- .../Package/package.VcxProj.Filters | 8 +- .../umdf_driver/WUDFOsrUsbFx2.vcxproj | 5 +- .../umdf_driver/WUDFOsrUsbFx2.vcxproj.Filters | 8 +- .../umdf_filter/WUDFOsrUsbFilter.vcxproj | 5 +- .../WUDFOsrUsbFilter.vcxproj.Filters | 8 +- .../WUDFOsrUsbFilterOnUmFx2Driver.inx | 13 +- usb/umdf_filter_umdf/umdf_filter_umdf.sln | 66 +- usb/umdf_fx2/ReadMe.md | 7 - usb/umdf_fx2/driver/WUDFOsrUsbFx2.inx | 11 +- usb/umdf_fx2/driver/WUDFOsrUsbFx2.vcxproj | 5 +- .../driver/WUDFOsrUsbFx2.vcxproj.Filters | 11 +- usb/umdf_fx2/exe/WudfOsrUsbFx2Test.vcxproj | 5 +- .../exe/WudfOsrUsbFx2Test.vcxproj.Filters | 6 +- usb/umdf_fx2/umdf_fx2.sln | 44 +- usb/usbsamp/ReadMe.md | 187 + usb/usbsamp/exe/testapp.c | 1307 ++ usb/usbsamp/exe/testapp.rc | 12 + usb/usbsamp/exe/usbsamp.vcxproj | 179 + usb/usbsamp/exe/usbsamp.vcxproj.Filters | 27 + usb/usbsamp/sys/bulkrwr.c | 907 ++ usb/usbsamp/sys/device.c | 1674 +++ usb/usbsamp/sys/driver.c | 108 + usb/usbsamp/sys/driver/usbsamp.inx | 115 + usb/usbsamp/sys/driver/usbsamp.vcxproj | 203 + .../sys/driver/usbsamp.vcxproj.Filters | 51 + usb/usbsamp/sys/isorwr.c | 565 + usb/usbsamp/sys/private.h | 391 + usb/usbsamp/sys/public.h | 49 + usb/usbsamp/sys/queue.c | 595 + usb/usbsamp/sys/stream.c | 433 + usb/usbsamp/sys/usbsamp.rc | 12 + usb/usbsamp/usbsamp.htm | 92 + usb/usbsamp/usbsamp.sln | 49 + usb/usbview/display.c | 176 +- usb/usbview/usbdesc.h | 6 + usb/usbview/usbview.sln | 18 +- usb/usbview/usbview.vcxproj | 5 +- usb/usbview/usbview.vcxproj.Filters | 6 +- usb/usbview/xmlhelper.cpp | 176 +- video/KMDOD/KMDOD.sln | 28 + video/KMDOD/ReadMe.md | 69 + video/KMDOD/Sample/SampleDisplay.vcxproj | 197 + .../Sample/SampleDisplay.vcxproj.Filters | 49 + video/KMDOD/Sample/sampledisplay.inf | Bin 0 -> 5936 bytes video/KMDOD/bdd.cxx | 856 ++ video/KMDOD/bdd.hxx | 650 + 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 | 90 + video/KMDOD/readme.htm | 46 + video/KMDOD/sampledisplay.rc | 30 + video/pixlib/PixLib.vcxproj | 5 +- video/pixlib/PixLib.vcxproj.Filters | 6 +- video/pixlib/pixlib.sln | 18 +- wmi/wmiacpi/{wmiacpi => }/ReadMe.md | 0 wmi/wmiacpi/{wmiacpi => }/acpimof.def | 0 wmi/wmiacpi/{wmiacpi => }/acpimof.mof | 0 wmi/wmiacpi/{wmiacpi => }/acpimof.rc | 0 wmi/wmiacpi/{wmiacpi => }/acpimof.vcxproj | 5 +- .../{wmiacpi => }/acpimof.vcxproj.Filters | 6 +- wmi/wmiacpi/{wmiacpi => }/device.asl | 0 wmi/wmiacpi/{wmiacpi => }/readme.htm | 0 wmi/wmiacpi/{wmiacpi => }/wmi-acpi.htm | 0 wmi/wmiacpi/wmiacpi.sln | 18 +- wmi/wmisamp/WmiSamp.vcxproj | 5 +- wmi/wmisamp/WmiSamp.vcxproj.Filters | 11 +- wmi/wmisamp/wmisamp.inx | 4 +- wmi/wmisamp/wmisamp.sln | 18 +- 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 + .../RS232Connection.cpp | 316 + wpd/WpdBasicHardwareDriver/RS232Connection.h | 153 + wpd/WpdBasicHardwareDriver/RS232Target.cpp | 433 + wpd/WpdBasicHardwareDriver/RS232Target.h | 53 + wpd/WpdBasicHardwareDriver/ReadMe.md | 45 + 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 | 340 + .../WpdBasicHardwareDriver.vcxproj.Filters | 72 + .../WpdCapabilities.cpp | 505 + wpd/WpdBasicHardwareDriver/WpdCapabilities.h | 41 + wpd/WpdBasicHardwareDriver/WpdObjectEnum.cpp | 431 + wpd/WpdBasicHardwareDriver/WpdObjectEnum.h | 107 + .../WpdObjectProperties.cpp | 1314 ++ .../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 + .../WpdHelloWorldDriver.inx | 8 +- .../WpdHelloWorldDriver.sln | 18 +- .../WpdHelloWorldDriver.vcxproj | 5 +- .../WpdHelloWorldDriver.vcxproj.Filters | 11 +- .../WpdMultiTransportDriver.inx | 8 +- .../WpdMultiTransportDriver.sln | 18 +- .../WpdMultiTransportDriver.vcxproj | 5 +- .../WpdMultiTransportDriver.vcxproj.Filters | 11 +- .../WpdServiceSampleDriver.inx | 8 +- .../WpdServiceSampleDriver.sln | 18 +- .../WpdServiceSampleDriver.vcxproj | 5 +- .../WpdServiceSampleDriver.vcxproj.Filters | 11 +- .../WpdWudfSampleDriver.inx | 8 +- .../WpdWudfSampleDriver.sln | 18 +- .../WpdWudfSampleDriver.vcxproj | 5 +- .../WpdWudfSampleDriver.vcxproj.Filters | 11 +- 1839 files changed, 507977 insertions(+), 6161 deletions(-) create mode 100644 audio/sysvad/ContosoKeywordDetector.h create mode 100644 audio/sysvad/EndpointsCommon/EndpointsCommon.vcxproj create mode 100644 audio/sysvad/EndpointsCommon/EndpointsCommon.vcxproj.Filters create mode 100644 audio/sysvad/EndpointsCommon/MiniportAudioEngineNode.cpp create mode 100644 audio/sysvad/EndpointsCommon/MiniportStreamAudioEngineNode.cpp create mode 100644 audio/sysvad/EndpointsCommon/bthhfpmictopo.cpp create mode 100644 audio/sysvad/EndpointsCommon/bthhfpmictopo.h create mode 100644 audio/sysvad/EndpointsCommon/bthhfpmictoptable.h create mode 100644 audio/sysvad/EndpointsCommon/bthhfpmicwavtable.h create mode 100644 audio/sysvad/EndpointsCommon/bthhfpmicwbwavtable.h create mode 100644 audio/sysvad/EndpointsCommon/bthhfpminipairs.h create mode 100644 audio/sysvad/EndpointsCommon/bthhfpminwavert.cpp create mode 100644 audio/sysvad/EndpointsCommon/bthhfpspeakertopo.cpp create mode 100644 audio/sysvad/EndpointsCommon/bthhfpspeakertopo.h create mode 100644 audio/sysvad/EndpointsCommon/bthhfpspeakertoptable.h create mode 100644 audio/sysvad/EndpointsCommon/bthhfpspeakerwavtable.h create mode 100644 audio/sysvad/EndpointsCommon/bthhfpspeakerwbwavtable.h create mode 100644 audio/sysvad/EndpointsCommon/bthhfptopo.cpp create mode 100644 audio/sysvad/EndpointsCommon/bthhfptopo.h create mode 100644 audio/sysvad/EndpointsCommon/micarray1toptable.h create mode 100644 audio/sysvad/EndpointsCommon/micarraytopo.cpp create mode 100644 audio/sysvad/EndpointsCommon/micarraytopo.h create mode 100644 audio/sysvad/EndpointsCommon/micarraywavtable.h create mode 100644 audio/sysvad/EndpointsCommon/mintopo.cpp create mode 100644 audio/sysvad/EndpointsCommon/mintopo.h create mode 100644 audio/sysvad/EndpointsCommon/minwavert.cpp create mode 100644 audio/sysvad/EndpointsCommon/minwavert.h create mode 100644 audio/sysvad/EndpointsCommon/minwavertstream.cpp create mode 100644 audio/sysvad/EndpointsCommon/minwavertstream.h create mode 100644 audio/sysvad/EndpointsCommon/simple.h create mode 100644 audio/sysvad/EndpointsCommon/speakerhptopo.cpp create mode 100644 audio/sysvad/EndpointsCommon/speakerhptopo.h create mode 100644 audio/sysvad/EndpointsCommon/speakerhptoptable.h create mode 100644 audio/sysvad/EndpointsCommon/speakerhpwavtable.h create mode 100644 audio/sysvad/EndpointsCommon/speakertopo.cpp create mode 100644 audio/sysvad/EndpointsCommon/speakertopo.h create mode 100644 audio/sysvad/EndpointsCommon/speakertoptable.h create mode 100644 audio/sysvad/EndpointsCommon/speakerwavtable.h create mode 100644 audio/sysvad/KeywordDetectorAdapter/KeywordDetectorContosoAdapter.cpp create mode 100644 audio/sysvad/KeywordDetectorAdapter/KeywordDetectorContosoAdapter.def create mode 100644 audio/sysvad/KeywordDetectorAdapter/KeywordDetectorContosoAdapter.idl create mode 100644 audio/sysvad/KeywordDetectorAdapter/KeywordDetectorContosoAdapter.rc create mode 100644 audio/sysvad/KeywordDetectorAdapter/KeywordDetectorContosoAdapter.vcxproj create mode 100644 audio/sysvad/KeywordDetectorAdapter/KeywordDetectorContosoAdapter.vcxproj.Filters create mode 100644 audio/sysvad/KeywordDetectorAdapter/dllmain.cpp create mode 100644 audio/sysvad/KeywordDetectorAdapter/stdafx.cpp create mode 100644 audio/sysvad/KeywordDetectorAdapter/stdafx.h create mode 100644 audio/sysvad/KeywordDetectorAdapter/targetver.h create mode 100644 audio/sysvad/Package/package.VcxProj create mode 100644 audio/sysvad/Package/package.VcxProj.Filters create mode 100644 audio/sysvad/PhoneAudioSample/CellularWave.cpp create mode 100644 audio/sysvad/PhoneAudioSample/CellularWave.h create mode 100644 audio/sysvad/PhoneAudioSample/Cellulartopo.cpp create mode 100644 audio/sysvad/PhoneAudioSample/Cellulartopo.h create mode 100644 audio/sysvad/PhoneAudioSample/Cellulartoptable.h create mode 100644 audio/sysvad/PhoneAudioSample/Cellularwavtable.h create mode 100644 audio/sysvad/PhoneAudioSample/FMTopTable.h create mode 100644 audio/sysvad/PhoneAudioSample/FMTopo.cpp create mode 100644 audio/sysvad/PhoneAudioSample/FMTopo.h create mode 100644 audio/sysvad/PhoneAudioSample/FMWavTable.h create mode 100644 audio/sysvad/PhoneAudioSample/FMWave.cpp create mode 100644 audio/sysvad/PhoneAudioSample/FMWave.h create mode 100644 audio/sysvad/PhoneAudioSample/PhoneAudioSample.vcxproj create mode 100644 audio/sysvad/PhoneAudioSample/PhoneAudioSample.vcxproj.Filters create mode 100644 audio/sysvad/PhoneAudioSample/handsetmictopo.cpp create mode 100644 audio/sysvad/PhoneAudioSample/handsetmictopo.h create mode 100644 audio/sysvad/PhoneAudioSample/handsetmictoptable.h create mode 100644 audio/sysvad/PhoneAudioSample/handsetmicwavtable.h create mode 100644 audio/sysvad/PhoneAudioSample/handsetspeakertopo.cpp create mode 100644 audio/sysvad/PhoneAudioSample/handsetspeakertopo.h create mode 100644 audio/sysvad/PhoneAudioSample/handsetspeakertoptable.h create mode 100644 audio/sysvad/PhoneAudioSample/handsetspeakerwavtable.h create mode 100644 audio/sysvad/PhoneAudioSample/michstopo.cpp create mode 100644 audio/sysvad/PhoneAudioSample/michstopo.h create mode 100644 audio/sysvad/PhoneAudioSample/michstoptable.h create mode 100644 audio/sysvad/PhoneAudioSample/michswavtable.h create mode 100644 audio/sysvad/PhoneAudioSample/minipairs.h create mode 100644 audio/sysvad/PhoneAudioSample/speakerhstopo.cpp create mode 100644 audio/sysvad/PhoneAudioSample/speakerhstopo.h create mode 100644 audio/sysvad/PhoneAudioSample/speakerhstoptable.h create mode 100644 audio/sysvad/PhoneAudioSample/speakerhswavtable.h create mode 100644 audio/sysvad/ReadMe.md create mode 100644 audio/sysvad/SwapAPO/APO/Resource.h create mode 100644 audio/sysvad/SwapAPO/APO/SwapAPO.h create mode 100644 audio/sysvad/SwapAPO/APO/SwapAPO.vcxproj create mode 100644 audio/sysvad/SwapAPO/APO/SwapAPO.vcxproj.Filters create mode 100644 audio/sysvad/SwapAPO/APO/SwapAPODll.cpp create mode 100644 audio/sysvad/SwapAPO/APO/SwapAPODll.def create mode 100644 audio/sysvad/SwapAPO/APO/SwapAPODll.idl create mode 100644 audio/sysvad/SwapAPO/APO/SwapAPODll.rc create mode 100644 audio/sysvad/SwapAPO/APO/SwapAPODll.rgs create mode 100644 audio/sysvad/SwapAPO/APO/SwapAPOInterface.idl create mode 100644 audio/sysvad/SwapAPO/APO/SwapAPOMFX.rgs create mode 100644 audio/sysvad/SwapAPO/APO/SwapAPOSFX.rgs create mode 100644 audio/sysvad/SwapAPO/APO/swap.cpp create mode 100644 audio/sysvad/SwapAPO/APO/swapapo.png create mode 100644 audio/sysvad/SwapAPO/APO/swapapomfx.cpp create mode 100644 audio/sysvad/SwapAPO/APO/swapaposfx.cpp create mode 100644 audio/sysvad/SwapAPO/Inc/CommonMacros.h create mode 100644 audio/sysvad/SwapAPO/Inc/CustomPropKeys.h create mode 100644 audio/sysvad/SwapAPO/Inc/tlist.h create mode 100644 audio/sysvad/SwapAPO/PropPageExtensions/AdvEndpointPropPage.cpp create mode 100644 audio/sysvad/SwapAPO/PropPageExtensions/AdvEndpointPropPage.h create mode 100644 audio/sysvad/SwapAPO/PropPageExtensions/AdvEndpointPropPage.rgs create mode 100644 audio/sysvad/SwapAPO/PropPageExtensions/CplExt.cpp create mode 100644 audio/sysvad/SwapAPO/PropPageExtensions/CplExt.idl create mode 100644 audio/sysvad/SwapAPO/PropPageExtensions/CplExt.rc create mode 100644 audio/sysvad/SwapAPO/PropPageExtensions/CplExt.rgs create mode 100644 audio/sysvad/SwapAPO/PropPageExtensions/Parts.cpp create mode 100644 audio/sysvad/SwapAPO/PropPageExtensions/Parts.h create mode 100644 audio/sysvad/SwapAPO/PropPageExtensions/PropPageExt.def create mode 100644 audio/sysvad/SwapAPO/PropPageExtensions/PropPageExt.vcxproj create mode 100644 audio/sysvad/SwapAPO/PropPageExtensions/PropPageExt.vcxproj.Filters create mode 100644 audio/sysvad/SwapAPO/PropPageExtensions/SwapPropPage.cpp create mode 100644 audio/sysvad/SwapAPO/PropPageExtensions/SwapPropPage.h create mode 100644 audio/sysvad/SwapAPO/PropPageExtensions/SwapPropPage.rgs create mode 100644 audio/sysvad/SwapAPO/PropPageExtensions/TopologyExaminers.cpp create mode 100644 audio/sysvad/SwapAPO/PropPageExtensions/TopologyExaminers.h create mode 100644 audio/sysvad/SwapAPO/PropPageExtensions/UIWidgets.cpp create mode 100644 audio/sysvad/SwapAPO/PropPageExtensions/UIWidgets.h create mode 100644 audio/sysvad/SwapAPO/PropPageExtensions/music.ico create mode 100644 audio/sysvad/SwapAPO/PropPageExtensions/resource.h create mode 100644 audio/sysvad/SwapAPO/PropPageExtensions/stdafx.cpp create mode 100644 audio/sysvad/SwapAPO/PropPageExtensions/stdafx.h create mode 100644 audio/sysvad/SwapAPO/PropPageExtensions/stdafxsrc.cpp create mode 100644 audio/sysvad/SysVadShared.h create mode 100644 audio/sysvad/TabletAudioSample/TabletAudioSample.vcxproj create mode 100644 audio/sysvad/TabletAudioSample/TabletAudioSample.vcxproj.Filters create mode 100644 audio/sysvad/TabletAudioSample/hdmitopo.cpp create mode 100644 audio/sysvad/TabletAudioSample/hdmitopo.h create mode 100644 audio/sysvad/TabletAudioSample/hdmitoptable.h create mode 100644 audio/sysvad/TabletAudioSample/hdmiwavtable.h create mode 100644 audio/sysvad/TabletAudioSample/micarray2toptable.h create mode 100644 audio/sysvad/TabletAudioSample/micarray3toptable.h create mode 100644 audio/sysvad/TabletAudioSample/micarray3wavtable.h create mode 100644 audio/sysvad/TabletAudioSample/micintopo.cpp create mode 100644 audio/sysvad/TabletAudioSample/micintopo.h create mode 100644 audio/sysvad/TabletAudioSample/micintoptable.h create mode 100644 audio/sysvad/TabletAudioSample/micinwavtable.h create mode 100644 audio/sysvad/TabletAudioSample/minipairs.h create mode 100644 audio/sysvad/TabletAudioSample/spdiftopo.cpp create mode 100644 audio/sysvad/TabletAudioSample/spdiftopo.h create mode 100644 audio/sysvad/TabletAudioSample/spdiftoptable.h create mode 100644 audio/sysvad/TabletAudioSample/spdifwavtable.h create mode 100644 audio/sysvad/ToneGenerator.cpp create mode 100644 audio/sysvad/ToneGenerator.h create mode 100644 audio/sysvad/UnittestData.h create mode 100644 audio/sysvad/adapter.cpp create mode 100644 audio/sysvad/basetopo.cpp create mode 100644 audio/sysvad/basetopo.h create mode 100644 audio/sysvad/common.cpp create mode 100644 audio/sysvad/common.h create mode 100644 audio/sysvad/hw.cpp create mode 100644 audio/sysvad/hw.h create mode 100644 audio/sysvad/ihvprivatepropertyset.h create mode 100644 audio/sysvad/kshelper.cpp create mode 100644 audio/sysvad/kshelper.h create mode 100644 audio/sysvad/phoneaudiosample.inf create mode 100644 audio/sysvad/readme.htm create mode 100644 audio/sysvad/savedata.cpp create mode 100644 audio/sysvad/savedata.h create mode 100644 audio/sysvad/sources.inc create mode 100644 audio/sysvad/sysvad.h create mode 100644 audio/sysvad/sysvad.rc create mode 100644 audio/sysvad/sysvad.sln create mode 100644 audio/sysvad/tabletaudiosample.inf create mode 100644 avscamera/Package/package.VcxProj create mode 100644 avscamera/Package/package.VcxProj.Filters create mode 100644 avscamera/README.md create mode 100644 avscamera/avscamera.sln create mode 100644 avscamera/common/CustomProperties.h create mode 100644 avscamera/common/Macros.h create mode 100644 avscamera/common/MetadataInternal.h create mode 100644 avscamera/mft0/AvsCameraMFT0.rc create mode 100644 avscamera/mft0/AvsCameraMft0.vcxproj create mode 100644 avscamera/mft0/AvsCameraMft0.vcxproj.Filters create mode 100644 avscamera/mft0/MFT0.def create mode 100644 avscamera/mft0/MFT0.idl create mode 100644 avscamera/mft0/MFT0Impl.cpp create mode 100644 avscamera/mft0/MFT0Impl.h create mode 100644 avscamera/mft0/MFT0clsid.h create mode 100644 avscamera/mft0/SampleHelpers.h create mode 100644 avscamera/mft0/dllmain.cpp create mode 100644 avscamera/mft0/stdafx.h create mode 100644 avscamera/mft0/stdafxsrc.cpp create mode 100644 avscamera/mft0/targetver.h create mode 100644 avscamera/sys/AvsCamera.cpp create mode 100644 avscamera/sys/AvsCamera.rc create mode 100644 avscamera/sys/AvsCamera.vcxproj create mode 100644 avscamera/sys/AvsCamera.vcxproj.Filters create mode 100644 avscamera/sys/AvsCameraDevice.cpp create mode 100644 avscamera/sys/AvsCameraDevice.h create mode 100644 avscamera/sys/AvsCameraFilter.cpp create mode 100644 avscamera/sys/AvsCameraFilter.h create mode 100644 avscamera/sys/CameraProfile.h create mode 100644 avscamera/sys/Capture.cpp create mode 100644 avscamera/sys/Capture.h create mode 100644 avscamera/sys/Common.h create mode 100644 avscamera/sys/Dbg.h create mode 100644 avscamera/sys/Device.cpp create mode 100644 avscamera/sys/Device.h create mode 100644 avscamera/sys/ExtendedCameraAngleOffset.h create mode 100644 avscamera/sys/ExtendedEVComp.h create mode 100644 avscamera/sys/ExtendedFieldOfView.h create mode 100644 avscamera/sys/ExtendedMaxVideoFpsForPhotoRes.h create mode 100644 avscamera/sys/ExtendedMetadata.h create mode 100644 avscamera/sys/ExtendedPhotoMode.h create mode 100644 avscamera/sys/ExtendedProperty.h create mode 100644 avscamera/sys/ExtendedVidProcSetting.h create mode 100644 avscamera/sys/Mutex.cpp create mode 100644 avscamera/sys/Mutex.h create mode 100644 avscamera/sys/NV12Synthesizer.cpp create mode 100644 avscamera/sys/NV12Synthesizer.h create mode 100644 avscamera/sys/NonCopyable.h create mode 100644 avscamera/sys/PreviewHwSim.cpp create mode 100644 avscamera/sys/PreviewHwSim.h create mode 100644 avscamera/sys/RGB24Synthesizer.cpp create mode 100644 avscamera/sys/RGB24Synthesizer.h create mode 100644 avscamera/sys/Ref.h create mode 100644 avscamera/sys/Roi.cpp create mode 100644 avscamera/sys/Roi.h create mode 100644 avscamera/sys/Sensor.cpp create mode 100644 avscamera/sys/Sensor.h create mode 100644 avscamera/sys/SensorSimulation.cpp create mode 100644 avscamera/sys/SensorSimulation.h create mode 100644 avscamera/sys/Synthesizer.cpp create mode 100644 avscamera/sys/Synthesizer.h create mode 100644 avscamera/sys/Timer.cpp create mode 100644 avscamera/sys/Timer.h create mode 100644 avscamera/sys/Util.cpp create mode 100644 avscamera/sys/Util.h create mode 100644 avscamera/sys/VideoCapture.cpp create mode 100644 avscamera/sys/VideoCapture.h create mode 100644 avscamera/sys/VideoHwSim.cpp create mode 100644 avscamera/sys/VideoHwSim.h create mode 100644 avscamera/sys/Waitable.cpp create mode 100644 avscamera/sys/Waitable.h create mode 100644 avscamera/sys/WorkItem.cpp create mode 100644 avscamera/sys/WorkItem.h create mode 100644 avscamera/sys/XRGBSynthesizer.cpp create mode 100644 avscamera/sys/XRGBSynthesizer.h create mode 100644 avscamera/sys/YUVSynthesizer.cpp create mode 100644 avscamera/sys/YUVSynthesizer.h create mode 100644 avscamera/sys/YUY2Synthesizer.cpp create mode 100644 avscamera/sys/YUY2Synthesizer.h create mode 100644 avscamera/sys/avscamera.inf create mode 100644 avscamera/sys/filter.cpp create mode 100644 avscamera/sys/filter.h create mode 100644 avscamera/sys/hwsim.cpp create mode 100644 avscamera/sys/hwsim.h create mode 100644 avscamera/sys/imagecapture.cpp create mode 100644 avscamera/sys/imagecapture.h create mode 100644 avscamera/sys/imagehwsim.cpp create mode 100644 avscamera/sys/imagehwsim.h 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/avssamp/Filter.cpp create mode 100644 avstream/avssamp/ReadMe.md create mode 100644 avstream/avssamp/audio.cpp create mode 100644 avstream/avssamp/audio.h create mode 100644 avstream/avssamp/avssamp.cpp create mode 100644 avstream/avssamp/avssamp.h create mode 100644 avstream/avssamp/avssamp.htm create mode 100644 avstream/avssamp/avssamp.inf create mode 100644 avstream/avssamp/avssamp.rc create mode 100644 avstream/avssamp/avssamp.sln create mode 100644 avstream/avssamp/avssamp.vcxproj create mode 100644 avstream/avssamp/avssamp.vcxproj.Filters create mode 100644 avstream/avssamp/capture.cpp create mode 100644 avstream/avssamp/capture.h create mode 100644 avstream/avssamp/filter.h create mode 100644 avstream/avssamp/image.cpp create mode 100644 avstream/avssamp/image.h create mode 100644 avstream/avssamp/purecall.c create mode 100644 avstream/avssamp/video.cpp create mode 100644 avstream/avssamp/video.h create mode 100644 avstream/avssamp/wave.cpp create mode 100644 avstream/avssamp/wave.h create mode 100644 avstream/sampledevicemft/README.md create mode 100644 avstream/sampledevicemft/SampleDeviceMft.sln create mode 100644 avstream/sampledevicemft/Source.def create mode 100644 avstream/sampledevicemft/basepin.cpp create mode 100644 avstream/sampledevicemft/basepin.h create mode 100644 avstream/sampledevicemft/common.h create mode 100644 avstream/sampledevicemft/custompin.cpp create mode 100644 avstream/sampledevicemft/custompin.h create mode 100644 avstream/sampledevicemft/dllmain.cpp create mode 100644 avstream/sampledevicemft/mftpeventgenerator.cpp create mode 100644 avstream/sampledevicemft/mftpeventgenerator.h create mode 100644 avstream/sampledevicemft/multipinmft.cpp create mode 100644 avstream/sampledevicemft/multipinmft.h create mode 100644 avstream/sampledevicemft/multipinmft.vcxproj create mode 100644 avstream/sampledevicemft/multipinmft.vcxproj.Filters create mode 100644 avstream/sampledevicemft/multipinmfthelpers.cpp create mode 100644 avstream/sampledevicemft/multipinmfthelpers.h create mode 100644 avstream/sampledevicemft/multipinmftutils.cpp create mode 100644 avstream/sampledevicemft/stdafx.h create mode 100644 avstream/sampledevicemft/stdafxsrc.cpp create mode 100644 biometrics/Package/package.VcxProj create mode 100644 biometrics/Package/package.VcxProj.Filters create mode 100644 biometrics/ReadMe.md create mode 100644 biometrics/WBDIsample.sln create mode 100644 biometrics/adapters/engine_adapter/EngineAdapter.cpp create mode 100644 biometrics/adapters/engine_adapter/EngineAdapter.def create mode 100644 biometrics/adapters/engine_adapter/EngineAdapter.h create mode 100644 biometrics/adapters/engine_adapter/EngineAdapter.rc create mode 100644 biometrics/adapters/engine_adapter/EngineAdapter.vcxproj create mode 100644 biometrics/adapters/engine_adapter/EngineAdapter.vcxproj.Filters create mode 100644 biometrics/adapters/engine_adapter/precomp.h create mode 100644 biometrics/adapters/engine_adapter/precompsrc.cpp create mode 100644 biometrics/adapters/engine_adapter/resource.h create mode 100644 biometrics/adapters/sensor_adapter/SensorAdapter.cpp create mode 100644 biometrics/adapters/sensor_adapter/SensorAdapter.def create mode 100644 biometrics/adapters/sensor_adapter/SensorAdapter.h create mode 100644 biometrics/adapters/sensor_adapter/SensorAdapter.rc create mode 100644 biometrics/adapters/sensor_adapter/SensorAdapter.vcxproj create mode 100644 biometrics/adapters/sensor_adapter/SensorAdapter.vcxproj.Filters create mode 100644 biometrics/adapters/sensor_adapter/precomp.h create mode 100644 biometrics/adapters/sensor_adapter/precompsrc.cpp create mode 100644 biometrics/adapters/sensor_adapter/resource.h create mode 100644 biometrics/adapters/storage_adapter/StorageAdapter.cpp create mode 100644 biometrics/adapters/storage_adapter/StorageAdapter.def create mode 100644 biometrics/adapters/storage_adapter/StorageAdapter.h create mode 100644 biometrics/adapters/storage_adapter/StorageAdapter.rc create mode 100644 biometrics/adapters/storage_adapter/StorageAdapter.vcxproj create mode 100644 biometrics/adapters/storage_adapter/StorageAdapter.vcxproj.Filters create mode 100644 biometrics/adapters/storage_adapter/precomp.h create mode 100644 biometrics/adapters/storage_adapter/precompsrc.cpp create mode 100644 biometrics/adapters/storage_adapter/resource.h create mode 100644 biometrics/driver/BioUsbSample.ctl create mode 100644 biometrics/driver/BioUsbSample.rc create mode 100644 biometrics/driver/Device.cpp create mode 100644 biometrics/driver/Device.h create mode 100644 biometrics/driver/Driver.cpp create mode 100644 biometrics/driver/Driver.h create mode 100644 biometrics/driver/Internalsrc.cpp create mode 100644 biometrics/driver/IoQueue.cpp create mode 100644 biometrics/driver/IoQueue.h create mode 100644 biometrics/driver/RequestHelper.h create mode 100644 biometrics/driver/WudfBioUsbSample.inx create mode 100644 biometrics/driver/WudfBioUsbSample.vcxproj create mode 100644 biometrics/driver/WudfBioUsbSample.vcxproj.Filters create mode 100644 biometrics/driver/dllsup.cpp create mode 100644 biometrics/driver/exports.def create mode 100644 biometrics/driver/inc/public.h create mode 100644 biometrics/driver/inc/usb_hw.h create mode 100644 biometrics/driver/internal.h create mode 100644 biometrics/driver/readme.htm create mode 100644 biometrics/driver/resource.h create mode 100644 filesys/fastfat/ReadMe.md create mode 100644 filesys/fastfat/acchksup.c create mode 100644 filesys/fastfat/allocsup.c create mode 100644 filesys/fastfat/cachesup.c create mode 100644 filesys/fastfat/cleanup.c create mode 100644 filesys/fastfat/close.c create mode 100644 filesys/fastfat/create.c create mode 100644 filesys/fastfat/devctrl.c create mode 100644 filesys/fastfat/deviosup.c create mode 100644 filesys/fastfat/dirctrl.c create mode 100644 filesys/fastfat/dirsup.c create mode 100644 filesys/fastfat/dumpsup.c create mode 100644 filesys/fastfat/ea.c create mode 100644 filesys/fastfat/easup.c create mode 100644 filesys/fastfat/fastfat.rc create mode 100644 filesys/fastfat/fastfat.sln create mode 100644 filesys/fastfat/fastfat.vcxproj create mode 100644 filesys/fastfat/fastfat.vcxproj.Filters create mode 100644 filesys/fastfat/fat.h create mode 100644 filesys/fastfat/fatdata.c create mode 100644 filesys/fastfat/fatdata.h create mode 100644 filesys/fastfat/fatinit.c create mode 100644 filesys/fastfat/fatprocs.h create mode 100644 filesys/fastfat/fatprocssrc.c create mode 100644 filesys/fastfat/fatstruc.h create mode 100644 filesys/fastfat/fileinfo.c create mode 100644 filesys/fastfat/filobsup.c create mode 100644 filesys/fastfat/flush.c create mode 100644 filesys/fastfat/fsctrl.c create mode 100644 filesys/fastfat/fspdisp.c create mode 100644 filesys/fastfat/lfn.h create mode 100644 filesys/fastfat/lockctrl.c create mode 100644 filesys/fastfat/namesup.c create mode 100644 filesys/fastfat/nodetype.h create mode 100644 filesys/fastfat/pnp.c create mode 100644 filesys/fastfat/read.c create mode 100644 filesys/fastfat/resrcsup.c create mode 100644 filesys/fastfat/shutdown.c create mode 100644 filesys/fastfat/splaysup.c create mode 100644 filesys/fastfat/strucsup.c create mode 100644 filesys/fastfat/timesup.c create mode 100644 filesys/fastfat/verfysup.c create mode 100644 filesys/fastfat/volinfo.c create mode 100644 filesys/fastfat/workque.c create mode 100644 filesys/fastfat/write.c create mode 100644 general/ioctl/kmdf/ReadMe.md create mode 100644 general/ioctl/kmdf/exe/install.c create mode 100644 general/ioctl/kmdf/exe/nonpnp.inf create mode 100644 general/ioctl/kmdf/exe/nonpnpapp.vcxproj create mode 100644 general/ioctl/kmdf/exe/nonpnpapp.vcxproj.Filters create mode 100644 general/ioctl/kmdf/exe/testapp.c create mode 100644 general/ioctl/kmdf/ioctl.sln create mode 100644 general/ioctl/kmdf/sys/localwpp.ini create mode 100644 general/ioctl/kmdf/sys/nonpnp.c create mode 100644 general/ioctl/kmdf/sys/nonpnp.h create mode 100644 general/ioctl/kmdf/sys/nonpnp.rc create mode 100644 general/ioctl/kmdf/sys/nonpnp.vcxproj create mode 100644 general/ioctl/kmdf/sys/nonpnp.vcxproj.Filters create mode 100644 general/ioctl/kmdf/sys/public.h create mode 100644 general/ioctl/kmdf/sys/trace.h create mode 100644 general/toaster/toastDrv/Package/package.VcxProj create mode 100644 general/toaster/toastDrv/Package/package.VcxProj.Filters create mode 100644 general/toaster/toastDrv/ReadMe.md create mode 100644 general/toaster/toastDrv/classinstaller/classInst.c create mode 100644 general/toaster/toastDrv/classinstaller/classinst.rc create mode 100644 general/toaster/toastDrv/classinstaller/resource.h create mode 100644 general/toaster/toastDrv/classinstaller/toaster.ico create mode 100644 general/toaster/toastDrv/classinstaller/tostrcls.def create mode 100644 general/toaster/toastDrv/classinstaller/tostrcls.vcxproj create mode 100644 general/toaster/toastDrv/classinstaller/tostrcls.vcxproj.Filters create mode 100644 general/toaster/toastDrv/coinstaller/coinst.c create mode 100644 general/toaster/toastDrv/coinstaller/tostrco1.def create mode 100644 general/toaster/toastDrv/coinstaller/tostrco1.vcxproj create mode 100644 general/toaster/toastDrv/coinstaller/tostrco1.vcxproj.Filters create mode 100644 general/toaster/toastDrv/exe/enum/Enum.vcxproj create mode 100644 general/toaster/toastDrv/exe/enum/Enum.vcxproj.Filters create mode 100644 general/toaster/toastDrv/exe/enum/enum.c create mode 100644 general/toaster/toastDrv/exe/notify/notify.c create mode 100644 general/toaster/toastDrv/exe/notify/notify.h create mode 100644 general/toaster/toastDrv/exe/notify/notify.rc create mode 100644 general/toaster/toastDrv/exe/notify/notify.vcxproj create mode 100644 general/toaster/toastDrv/exe/notify/notify.vcxproj.Filters create mode 100644 general/toaster/toastDrv/exe/notify/toaster.ico create mode 100644 general/toaster/toastDrv/exe/toast/toast.c create mode 100644 general/toaster/toastDrv/exe/toast/toast.vcxproj create mode 100644 general/toaster/toastDrv/exe/toast/toast.vcxproj.Filters create mode 100644 general/toaster/toastDrv/exe/wmi/WmiToast.vcxproj create mode 100644 general/toaster/toastDrv/exe/wmi/WmiToast.vcxproj.Filters create mode 100644 general/toaster/toastDrv/exe/wmi/wmiexecute.cpp create mode 100644 general/toaster/toastDrv/exe/wmi/wmigetset.cpp create mode 100644 general/toaster/toastDrv/exe/wmi/wmitoast.cpp create mode 100644 general/toaster/toastDrv/exe/wmi/wmitoast.h create mode 100644 general/toaster/toastDrv/kmdf/bus/dynamic/busenum.c create mode 100644 general/toaster/toastDrv/kmdf/bus/dynamic/busenum.h create mode 100644 general/toaster/toastDrv/kmdf/bus/dynamic/busenum.mof create mode 100644 general/toaster/toastDrv/kmdf/bus/dynamic/busenum.rc create mode 100644 general/toaster/toastDrv/kmdf/bus/dynamic/buspdo.c create mode 100644 general/toaster/toastDrv/kmdf/bus/dynamic/dynambus.inx create mode 100644 general/toaster/toastDrv/kmdf/bus/dynamic/dynambus.vcxproj create mode 100644 general/toaster/toastDrv/kmdf/bus/dynamic/dynambus.vcxproj.Filters create mode 100644 general/toaster/toastDrv/kmdf/bus/dynamic/wmi.c create mode 100644 general/toaster/toastDrv/kmdf/bus/static/StatBus.vcxproj create mode 100644 general/toaster/toastDrv/kmdf/bus/static/StatBus.vcxproj.Filters create mode 100644 general/toaster/toastDrv/kmdf/bus/static/busenum.c create mode 100644 general/toaster/toastDrv/kmdf/bus/static/busenum.h create mode 100644 general/toaster/toastDrv/kmdf/bus/static/busenum.mof create mode 100644 general/toaster/toastDrv/kmdf/bus/static/busenum.rc create mode 100644 general/toaster/toastDrv/kmdf/bus/static/buspdo.c create mode 100644 general/toaster/toastDrv/kmdf/bus/static/statbus.inx create mode 100644 general/toaster/toastDrv/kmdf/bus/static/wmi.c create mode 100644 general/toaster/toastDrv/kmdf/filter/filter.inx create mode 100644 general/toaster/toastDrv/kmdf/filter/generic/filter.c create mode 100644 general/toaster/toastDrv/kmdf/filter/generic/filter.h create mode 100644 general/toaster/toastDrv/kmdf/filter/generic/filter.rc create mode 100644 general/toaster/toastDrv/kmdf/filter/generic/filter.vcxproj create mode 100644 general/toaster/toastDrv/kmdf/filter/generic/filter.vcxproj.Filters create mode 100644 general/toaster/toastDrv/kmdf/filter/sideband/filter.c create mode 100644 general/toaster/toastDrv/kmdf/filter/sideband/filter.h create mode 100644 general/toaster/toastDrv/kmdf/filter/sideband/filter.rc create mode 100644 general/toaster/toastDrv/kmdf/filter/sideband/filter.vcxproj create mode 100644 general/toaster/toastDrv/kmdf/filter/sideband/filter.vcxproj.Filters create mode 100644 general/toaster/toastDrv/kmdf/func/featured/power.c create mode 100644 general/toaster/toastDrv/kmdf/func/featured/toaster.c create mode 100644 general/toaster/toastDrv/kmdf/func/featured/toaster.mof create mode 100644 general/toaster/toastDrv/kmdf/func/featured/toaster.rc create mode 100644 general/toaster/toastDrv/kmdf/func/featured/trace.h create mode 100644 general/toaster/toastDrv/kmdf/func/featured/wdffeatured.inx create mode 100644 general/toaster/toastDrv/kmdf/func/featured/wdffeatured.vcxproj create mode 100644 general/toaster/toastDrv/kmdf/func/featured/wdffeatured.vcxproj.Filters create mode 100644 general/toaster/toastDrv/kmdf/func/featured/wmi.c create mode 100644 general/toaster/toastDrv/kmdf/func/shared/toaster.h create mode 100644 general/toaster/toastDrv/kmdf/func/simple/toaster.c create mode 100644 general/toaster/toastDrv/kmdf/func/simple/wdfsimple.inx create mode 100644 general/toaster/toastDrv/kmdf/func/simple/wdfsimple.vcxproj create mode 100644 general/toaster/toastDrv/kmdf/func/simple/wdfsimple.vcxproj.Filters create mode 100644 general/toaster/toastDrv/kmdf/inc/driver.h create mode 100644 general/toaster/toastDrv/kmdf/inc/public.h create mode 100644 general/toaster/toastDrv/kmdf/toastmon/toastmon.H create mode 100644 general/toaster/toastDrv/kmdf/toastmon/toastmon.RC create mode 100644 general/toaster/toastDrv/kmdf/toastmon/toastmon.c create mode 100644 general/toaster/toastDrv/kmdf/toastmon/wdftoastmon.inx create mode 100644 general/toaster/toastDrv/kmdf/toastmon/wdftoastmon.vcxproj create mode 100644 general/toaster/toastDrv/kmdf/toastmon/wdftoastmon.vcxproj.Filters create mode 100644 general/toaster/toastDrv/kmdf/toastmon/wmi.c create mode 100644 general/toaster/toastDrv/toaster.sln create mode 100644 general/toaster/toastDrv/umdf/Toastmon/Device.cpp create mode 100644 general/toaster/toastDrv/umdf/Toastmon/Device.h create mode 100644 general/toaster/toastDrv/umdf/Toastmon/Driver.cpp create mode 100644 general/toaster/toastDrv/umdf/Toastmon/Driver.h create mode 100644 general/toaster/toastDrv/umdf/Toastmon/RemoteTarget.cpp create mode 100644 general/toaster/toastDrv/umdf/Toastmon/RemoteTarget.h create mode 100644 general/toaster/toastDrv/umdf/Toastmon/ToastMon.rc create mode 100644 general/toaster/toastDrv/umdf/Toastmon/WUDFToastMon.inx create mode 100644 general/toaster/toastDrv/umdf/Toastmon/WUDFToastMon.vcxproj create mode 100644 general/toaster/toastDrv/umdf/Toastmon/WUDFToastMon.vcxproj.Filters create mode 100644 general/toaster/toastDrv/umdf/Toastmon/comsup.cpp create mode 100644 general/toaster/toastDrv/umdf/Toastmon/comsup.h create mode 100644 general/toaster/toastDrv/umdf/Toastmon/dllsup.cpp create mode 100644 general/toaster/toastDrv/umdf/Toastmon/exports.def create mode 100644 general/toaster/toastDrv/umdf/Toastmon/internal.h create mode 100644 general/toaster/toastDrv/umdf/Toastmon/list.h create mode 100644 general/toaster/toastDrv/umdf/func/Device.cpp create mode 100644 general/toaster/toastDrv/umdf/func/Device.h create mode 100644 general/toaster/toastDrv/umdf/func/Driver.cpp create mode 100644 general/toaster/toastDrv/umdf/func/Driver.h create mode 100644 general/toaster/toastDrv/umdf/func/Queue.cpp create mode 100644 general/toaster/toastDrv/umdf/func/Queue.h create mode 100644 general/toaster/toastDrv/umdf/func/WUDFToaster.cpp create mode 100644 general/toaster/toastDrv/umdf/func/WUDFToaster.ctl create mode 100644 general/toaster/toastDrv/umdf/func/WUDFToaster.def create mode 100644 general/toaster/toastDrv/umdf/func/WUDFToaster.idl create mode 100644 general/toaster/toastDrv/umdf/func/WUDFToaster.inx create mode 100644 general/toaster/toastDrv/umdf/func/WUDFToaster.rc create mode 100644 general/toaster/toastDrv/umdf/func/WUDFToaster.vcxproj create mode 100644 general/toaster/toastDrv/umdf/func/WUDFToaster.vcxproj.Filters create mode 100644 general/toaster/toastDrv/umdf/func/internal.h create mode 100644 general/toaster/toastDrv/umdf/func/resource.h create mode 100644 general/toaster/toastDrv/umdf/func/stdAfxsrc.cpp create mode 100644 general/toaster/toastDrv/umdf/func/stdafx.cpp create mode 100644 general/toaster/toastDrv/umdf/func/stdafx.h create mode 100644 general/toaster/toastpkg/toastcd/ToastApp/setup.exe create mode 100644 general/toaster/toastpkg/toastcd/amd64/toastva.exe create mode 100644 general/toaster/toastpkg/toastcd/amd64/tostrcls.dll create mode 100644 general/toaster/toastpkg/toastcd/amd64/tostrco2.dll create mode 100644 general/toaster/toastpkg/toastcd/i386/toastva.exe create mode 100644 general/toaster/toastpkg/toastcd/i386/tostrcls.dll create mode 100644 general/toaster/toastpkg/toastcd/i386/tostrco2.dll 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 hid/firefly/ReadMe.md create mode 100644 hid/firefly/app/firefly.cpp create mode 100644 hid/firefly/app/flicker.vcxproj create mode 100644 hid/firefly/app/flicker.vcxproj.Filters create mode 100644 hid/firefly/driver/device.c create mode 100644 hid/firefly/driver/device.h create mode 100644 hid/firefly/driver/driver.c create mode 100644 hid/firefly/driver/firefly.h create mode 100644 hid/firefly/driver/firefly.inx create mode 100644 hid/firefly/driver/firefly.mof create mode 100644 hid/firefly/driver/firefly.rc create mode 100644 hid/firefly/driver/firefly.vcxproj create mode 100644 hid/firefly/driver/firefly.vcxproj.Filters create mode 100644 hid/firefly/driver/magic.h create mode 100644 hid/firefly/driver/vfeature.c create mode 100644 hid/firefly/driver/vfeature.h create mode 100644 hid/firefly/driver/wmi.c create mode 100644 hid/firefly/driver/wmi.h create mode 100644 hid/firefly/firefly.htm create mode 100644 hid/firefly/firefly.sln create mode 100644 hid/firefly/lib/Luminous.vcxproj create mode 100644 hid/firefly/lib/Luminous.vcxproj.Filters create mode 100644 hid/firefly/lib/luminous.cpp create mode 100644 hid/firefly/sauron/SAURON.vcxproj create mode 100644 hid/firefly/sauron/SAURON.vcxproj.Filters create mode 100644 hid/firefly/sauron/Sauron.cpp create mode 100644 hid/firefly/sauron/Sauron.h create mode 100644 hid/firefly/sauron/Sauron.rgs create mode 100644 hid/firefly/sauron/Saurondll.cpp create mode 100644 hid/firefly/sauron/Saurondll.def create mode 100644 hid/firefly/sauron/Saurondll.rc create mode 100644 hid/firefly/sauron/StdAfx.cpp create mode 100644 hid/firefly/sauron/StdAfx.h create mode 100644 hid/firefly/sauron/effects.h create mode 100644 hid/firefly/sauron/effects.idl create mode 100644 hid/firefly/sauron/iSauron.idl create mode 100644 hid/firefly/sauron/resource.h create mode 100644 hid/firefly/sauron/stdafxsrc.cpp create mode 100644 hid/firefly/shared/luminous.h create mode 100644 hid/firefly/shared/project.mk create mode 100644 hid/hidusbfx2/Package/package.VcxProj create mode 100644 hid/hidusbfx2/Package/package.VcxProj.Filters create mode 100644 hid/hidusbfx2/ReadMe.md create mode 100644 hid/hidusbfx2/hidkmdf/hidkmdf.c create mode 100644 hid/hidusbfx2/hidkmdf/hidkmdf.rc create mode 100644 hid/hidusbfx2/hidkmdf/hidkmdf.vcxproj create mode 100644 hid/hidusbfx2/hidkmdf/hidkmdf.vcxproj.Filters create mode 100644 hid/hidusbfx2/hidusbfx2.sln create mode 100644 hid/hidusbfx2/readme.htm create mode 100644 hid/hidusbfx2/sys/driver.c create mode 100644 hid/hidusbfx2/sys/hid.c create mode 100644 hid/hidusbfx2/sys/hidusbfx2.h create mode 100644 hid/hidusbfx2/sys/hidusbfx2.inx create mode 100644 hid/hidusbfx2/sys/hidusbfx2.rc create mode 100644 hid/hidusbfx2/sys/hidusbfx2.vcxproj create mode 100644 hid/hidusbfx2/sys/hidusbfx2.vcxproj.Filters create mode 100644 hid/hidusbfx2/sys/trace.h create mode 100644 hid/hidusbfx2/sys/usb.c create mode 100644 input/hiddigi/SynapticsTouch/SynapticsTouch.inx create mode 100644 input/hiddigi/SynapticsTouch/SynapticsTouch.sln create mode 100644 input/hiddigi/SynapticsTouch/SynapticsTouch.vcxproj create mode 100644 input/hiddigi/SynapticsTouch/SynapticsTouch.vcxproj.Filters create mode 100644 input/hiddigi/SynapticsTouch/controller.h create mode 100644 input/hiddigi/SynapticsTouch/device.c create mode 100644 input/hiddigi/SynapticsTouch/device.h create mode 100644 input/hiddigi/SynapticsTouch/driver.c create mode 100644 input/hiddigi/SynapticsTouch/driver.h create mode 100644 input/hiddigi/SynapticsTouch/hid.c create mode 100644 input/hiddigi/SynapticsTouch/hid.h create mode 100644 input/hiddigi/SynapticsTouch/idle.c create mode 100644 input/hiddigi/SynapticsTouch/idle.h create mode 100644 input/hiddigi/SynapticsTouch/init.c create mode 100644 input/hiddigi/SynapticsTouch/internal.h create mode 100644 input/hiddigi/SynapticsTouch/power.c create mode 100644 input/hiddigi/SynapticsTouch/queue.c create mode 100644 input/hiddigi/SynapticsTouch/queue.h create mode 100644 input/hiddigi/SynapticsTouch/readme.md create mode 100644 input/hiddigi/SynapticsTouch/registry.c create mode 100644 input/hiddigi/SynapticsTouch/report.c create mode 100644 input/hiddigi/SynapticsTouch/resolutions.c create mode 100644 input/hiddigi/SynapticsTouch/resolutions.h create mode 100644 input/hiddigi/SynapticsTouch/rmiinternal.h create mode 100644 input/hiddigi/SynapticsTouch/spb.c create mode 100644 input/hiddigi/SynapticsTouch/spb.h create mode 100644 input/hiddigi/SynapticsTouch/trace.h create mode 100644 input/kbfiltr/ReadMe.md create mode 100644 input/kbfiltr/exe/kbftest.c create mode 100644 input/kbfiltr/exe/kbftest.vcxproj create mode 100644 input/kbfiltr/exe/kbftest.vcxproj.Filters create mode 100644 input/kbfiltr/kbfiltr.htm create mode 100644 input/kbfiltr/kbfiltr.sln create mode 100644 input/kbfiltr/sys/kbfiltr.c create mode 100644 input/kbfiltr/sys/kbfiltr.h create mode 100644 input/kbfiltr/sys/kbfiltr.inx create mode 100644 input/kbfiltr/sys/kbfiltr.rc create mode 100644 input/kbfiltr/sys/kbfiltr.vcxproj create mode 100644 input/kbfiltr/sys/kbfiltr.vcxproj.Filters create mode 100644 input/kbfiltr/sys/public.h create mode 100644 input/kbfiltr/sys/rawpdo.c create mode 100644 input/moufiltr/Moufiltr.htm create mode 100644 input/moufiltr/ReadMe.md create mode 100644 input/moufiltr/moufiltr.c create mode 100644 input/moufiltr/moufiltr.h create mode 100644 input/moufiltr/moufiltr.inx create mode 100644 input/moufiltr/moufiltr.rc create mode 100644 input/moufiltr/moufiltr.sln create mode 100644 input/moufiltr/moufiltr.vcxproj create mode 100644 input/moufiltr/moufiltr.vcxproj.Filters create mode 100644 network/ndis/ndisprot_kmdf/60/debug.c create mode 100644 network/ndis/ndisprot_kmdf/60/debug.h create mode 100644 network/ndis/ndisprot_kmdf/60/excallbk.c create mode 100644 network/ndis/ndisprot_kmdf/60/macros.h create mode 100644 network/ndis/ndisprot_kmdf/60/ndisbind.c create mode 100644 network/ndis/ndisprot_kmdf/60/ndisprot.h create mode 100644 network/ndis/ndisprot_kmdf/60/ndisprot.inx create mode 100644 network/ndis/ndisprot_kmdf/60/ndisprot.rc create mode 100644 network/ndis/ndisprot_kmdf/60/nprt6wdf.vcxproj create mode 100644 network/ndis/ndisprot_kmdf/60/nprt6wdf.vcxproj.Filters create mode 100644 network/ndis/ndisprot_kmdf/60/ntdisp.c create mode 100644 network/ndis/ndisprot_kmdf/60/precomp.h create mode 100644 network/ndis/ndisprot_kmdf/60/precompsrc.c create mode 100644 network/ndis/ndisprot_kmdf/60/protuser.h create mode 100644 network/ndis/ndisprot_kmdf/60/recv.c create mode 100644 network/ndis/ndisprot_kmdf/60/send.c create mode 100644 network/ndis/ndisprot_kmdf/Package/package.VcxProj create mode 100644 network/ndis/ndisprot_kmdf/Package/package.VcxProj.Filters create mode 100644 network/ndis/ndisprot_kmdf/ReadMe.md create mode 100644 network/ndis/ndisprot_kmdf/ndisprot.htm create mode 100644 network/ndis/ndisprot_kmdf/ndisprot_kmdf.sln create mode 100644 network/ndis/ndisprot_kmdf/notifyob/Common.hpp create mode 100644 network/ndis/ndisprot_kmdf/notifyob/ProtNotify.cpp create mode 100644 network/ndis/ndisprot_kmdf/notifyob/ProtNotify.def create mode 100644 network/ndis/ndisprot_kmdf/notifyob/ProtNotify.idl create mode 100644 network/ndis/ndisprot_kmdf/notifyob/ProtNotify.rc create mode 100644 network/ndis/ndisprot_kmdf/notifyob/ProtNotify.rgs create mode 100644 network/ndis/ndisprot_kmdf/notifyob/ProtNotify.vcxproj create mode 100644 network/ndis/ndisprot_kmdf/notifyob/ProtNotify.vcxproj.Filters create mode 100644 network/ndis/ndisprot_kmdf/notifyob/commonsrc.cpp create mode 100644 network/ndis/ndisprot_kmdf/notifyob/dllmain.cpp create mode 100644 network/ndis/ndisprot_kmdf/notifyob/resource.h create mode 100644 network/trans/ReadMe.md create mode 100644 network/trans/WFPSampler/HCK/WFPLogo_WFPSampler.Answer create mode 100644 network/trans/WFPSampler/HCK/WFPLogo_WFPSampler.Info create mode 100644 network/trans/WFPSampler/WFPSampler.sln create mode 100644 network/trans/WFPSampler/docs/ADVANCED_PACKET_INJECTION.mht create mode 100644 network/trans/WFPSampler/docs/BASIC_ACTION.mht create mode 100644 network/trans/WFPSampler/docs/BASIC_PACKET_EXAMINATION.mht create mode 100644 network/trans/WFPSampler/docs/BASIC_PACKET_INJECTION.mht create mode 100644 network/trans/WFPSampler/docs/BASIC_PACKET_MODIFICATION.mht create mode 100644 network/trans/WFPSampler/docs/BASIC_STREAM_INJECTION.mht create mode 100644 network/trans/WFPSampler/docs/ConditionsForCommandLine.mht create mode 100644 network/trans/WFPSampler/docs/FAST_PACKET_INJECTION.mht create mode 100644 network/trans/WFPSampler/docs/FAST_STREAM_INJECTION.mht create mode 100644 network/trans/WFPSampler/docs/FLOW_ASSOCIATION.mht create mode 100644 network/trans/WFPSampler/docs/MatchTypesForCommandLine.mht create mode 100644 network/trans/WFPSampler/docs/PEND_AUTHORIZATION.mht create mode 100644 network/trans/WFPSampler/docs/PEND_ENDPOINT_CLOSURE.mht create mode 100644 network/trans/WFPSampler/docs/PROXY.mht create mode 100644 network/trans/WFPSampler/docs/_WFPSampler_Overview.mht create mode 100644 network/trans/WFPSampler/exe/Framework_RPCClientInterface.cpp create mode 100644 network/trans/WFPSampler/exe/Framework_RPCClientInterface.h create mode 100644 network/trans/WFPSampler/exe/Framework_WFPSampler.cpp create mode 100644 network/trans/WFPSampler/exe/Framework_WFPSampler.h create mode 100644 network/trans/WFPSampler/exe/Framework_WFPSampler.rc create mode 100644 network/trans/WFPSampler/exe/HelperFunctions_CommandLine.cpp create mode 100644 network/trans/WFPSampler/exe/HelperFunctions_CommandLine.h create mode 100644 network/trans/WFPSampler/exe/Scenarios_AdvancedPacketInjection.cpp create mode 100644 network/trans/WFPSampler/exe/Scenarios_AdvancedPacketInjection.h create mode 100644 network/trans/WFPSampler/exe/Scenarios_AppContainers.cpp create mode 100644 network/trans/WFPSampler/exe/Scenarios_AppContainers.h create mode 100644 network/trans/WFPSampler/exe/Scenarios_BasicAction.cpp create mode 100644 network/trans/WFPSampler/exe/Scenarios_BasicAction.h create mode 100644 network/trans/WFPSampler/exe/Scenarios_BasicPacketExamination.cpp create mode 100644 network/trans/WFPSampler/exe/Scenarios_BasicPacketExamination.h create mode 100644 network/trans/WFPSampler/exe/Scenarios_BasicPacketInjection.cpp create mode 100644 network/trans/WFPSampler/exe/Scenarios_BasicPacketInjection.h create mode 100644 network/trans/WFPSampler/exe/Scenarios_BasicPacketModification.cpp create mode 100644 network/trans/WFPSampler/exe/Scenarios_BasicPacketModification.h create mode 100644 network/trans/WFPSampler/exe/Scenarios_BasicStreamInjection.cpp create mode 100644 network/trans/WFPSampler/exe/Scenarios_BasicStreamInjection.h create mode 100644 network/trans/WFPSampler/exe/Scenarios_FastPacketInjection.cpp create mode 100644 network/trans/WFPSampler/exe/Scenarios_FastPacketInjection.h create mode 100644 network/trans/WFPSampler/exe/Scenarios_FastStreamInjection.cpp create mode 100644 network/trans/WFPSampler/exe/Scenarios_FastStreamInjection.h create mode 100644 network/trans/WFPSampler/exe/Scenarios_FlowAssociation.cpp create mode 100644 network/trans/WFPSampler/exe/Scenarios_FlowAssociation.h create mode 100644 network/trans/WFPSampler/exe/Scenarios_Include.h create mode 100644 network/trans/WFPSampler/exe/Scenarios_PendAuthorization.cpp create mode 100644 network/trans/WFPSampler/exe/Scenarios_PendAuthorization.h create mode 100644 network/trans/WFPSampler/exe/Scenarios_PendEndpointClosure.cpp create mode 100644 network/trans/WFPSampler/exe/Scenarios_PendEndpointClosure.h create mode 100644 network/trans/WFPSampler/exe/Scenarios_Proxy.cpp create mode 100644 network/trans/WFPSampler/exe/Scenarios_Proxy.h create mode 100644 network/trans/WFPSampler/exe/WFPSampler.vcxproj create mode 100644 network/trans/WFPSampler/exe/WFPSampler.vcxproj.Filters create mode 100644 network/trans/WFPSampler/idl/WFPSamplerRPC.ACF create mode 100644 network/trans/WFPSampler/idl/WFPSamplerRPC.IDL create mode 100644 network/trans/WFPSampler/inc/Identifiers.h create mode 100644 network/trans/WFPSampler/inc/ProviderContexts.h create mode 100644 network/trans/WFPSampler/inc/ScenarioData.h create mode 100644 network/trans/WFPSampler/inc/WFPArrays.h create mode 100644 network/trans/WFPSampler/lib/HelperFunctions_FwpmCallout.cpp create mode 100644 network/trans/WFPSampler/lib/HelperFunctions_FwpmCallout.h create mode 100644 network/trans/WFPSampler/lib/HelperFunctions_FwpmEngine.cpp create mode 100644 network/trans/WFPSampler/lib/HelperFunctions_FwpmEngine.h create mode 100644 network/trans/WFPSampler/lib/HelperFunctions_FwpmFilter.cpp create mode 100644 network/trans/WFPSampler/lib/HelperFunctions_FwpmFilter.h create mode 100644 network/trans/WFPSampler/lib/HelperFunctions_FwpmLayer.cpp create mode 100644 network/trans/WFPSampler/lib/HelperFunctions_FwpmLayer.h create mode 100644 network/trans/WFPSampler/lib/HelperFunctions_FwpmProvider.cpp create mode 100644 network/trans/WFPSampler/lib/HelperFunctions_FwpmProvider.h create mode 100644 network/trans/WFPSampler/lib/HelperFunctions_FwpmProviderContext.cpp create mode 100644 network/trans/WFPSampler/lib/HelperFunctions_FwpmProviderContext.h create mode 100644 network/trans/WFPSampler/lib/HelperFunctions_FwpmSubLayer.cpp create mode 100644 network/trans/WFPSampler/lib/HelperFunctions_FwpmSubLayer.h create mode 100644 network/trans/WFPSampler/lib/HelperFunctions_FwpmTransaction.cpp create mode 100644 network/trans/WFPSampler/lib/HelperFunctions_FwpmTransaction.h create mode 100644 network/trans/WFPSampler/lib/HelperFunctions_GUID.cpp create mode 100644 network/trans/WFPSampler/lib/HelperFunctions_GUID.h create mode 100644 network/trans/WFPSampler/lib/HelperFunctions_IPAddress.cpp create mode 100644 network/trans/WFPSampler/lib/HelperFunctions_IPAddress.h create mode 100644 network/trans/WFPSampler/lib/HelperFunctions_Include.h create mode 100644 network/trans/WFPSampler/lib/HelperFunctions_Log.cpp create mode 100644 network/trans/WFPSampler/lib/HelperFunctions_Log.h create mode 100644 network/trans/WFPSampler/lib/HelperFunctions_MACAddress.cpp create mode 100644 network/trans/WFPSampler/lib/HelperFunctions_MACAddress.h create mode 100644 network/trans/WFPSampler/lib/HelperFunctions_Macros.h create mode 100644 network/trans/WFPSampler/lib/HelperFunctions_Process.cpp create mode 100644 network/trans/WFPSampler/lib/HelperFunctions_Process.h create mode 100644 network/trans/WFPSampler/lib/HelperFunctions_Registry.cpp create mode 100644 network/trans/WFPSampler/lib/HelperFunctions_Registry.h create mode 100644 network/trans/WFPSampler/lib/HelperFunctions_SID.cpp create mode 100644 network/trans/WFPSampler/lib/HelperFunctions_SID.h create mode 100644 network/trans/WFPSampler/lib/HelperFunctions_Service.cpp create mode 100644 network/trans/WFPSampler/lib/HelperFunctions_Service.h create mode 100644 network/trans/WFPSampler/lib/HelperFunctions_Strings.cpp create mode 100644 network/trans/WFPSampler/lib/HelperFunctions_Strings.h create mode 100644 network/trans/WFPSampler/lib/HelperFunctions_ThreadPools.cpp create mode 100644 network/trans/WFPSampler/lib/HelperFunctions_ThreadPools.h create mode 100644 network/trans/WFPSampler/lib/HelperFunctions_ThreadsAndEvents.cpp create mode 100644 network/trans/WFPSampler/lib/HelperFunctions_ThreadsAndEvents.h create mode 100644 network/trans/WFPSampler/lib/HelperFunctions_WinSock.cpp create mode 100644 network/trans/WFPSampler/lib/HelperFunctions_WinSock.h create mode 100644 network/trans/WFPSampler/lib/WFPSampler.vcxproj create mode 100644 network/trans/WFPSampler/lib/WFPSampler.vcxproj.Filters create mode 100644 network/trans/WFPSampler/scripts/WFPSamplerInstall.cmd create mode 100644 network/trans/WFPSampler/svc/Framework_Include.h create mode 100644 network/trans/WFPSampler/svc/Framework_RPCServerInterface.cpp create mode 100644 network/trans/WFPSampler/svc/Framework_RPCServerInterface.h create mode 100644 network/trans/WFPSampler/svc/Framework_WFPSamplerService.cpp create mode 100644 network/trans/WFPSampler/svc/Framework_WFPSamplerService.h create mode 100644 network/trans/WFPSampler/svc/Framework_WFPSamplerService.rc create mode 100644 network/trans/WFPSampler/svc/Framework_WFPSamplerService_Msg.mc create mode 100644 network/trans/WFPSampler/svc/Framework_WindowsFirewall.cpp create mode 100644 network/trans/WFPSampler/svc/Framework_WindowsFirewall.h create mode 100644 network/trans/WFPSampler/svc/Scenarios_AdvancedPacketInjection.cpp create mode 100644 network/trans/WFPSampler/svc/Scenarios_AppContainers.cpp create mode 100644 network/trans/WFPSampler/svc/Scenarios_BasicAction.cpp create mode 100644 network/trans/WFPSampler/svc/Scenarios_BasicPacketExamination.cpp create mode 100644 network/trans/WFPSampler/svc/Scenarios_BasicPacketInjection.cpp create mode 100644 network/trans/WFPSampler/svc/Scenarios_BasicPacketModification.cpp create mode 100644 network/trans/WFPSampler/svc/Scenarios_BasicStreamInjection.cpp create mode 100644 network/trans/WFPSampler/svc/Scenarios_FastPacketInjection.cpp create mode 100644 network/trans/WFPSampler/svc/Scenarios_FastStreamInjection.cpp create mode 100644 network/trans/WFPSampler/svc/Scenarios_FlowAssociation.cpp create mode 100644 network/trans/WFPSampler/svc/Scenarios_PendAuthorization.cpp create mode 100644 network/trans/WFPSampler/svc/Scenarios_PendEndpointClosure.cpp create mode 100644 network/trans/WFPSampler/svc/Scenarios_Proxy.cpp create mode 100644 network/trans/WFPSampler/svc/WFPSamplerService.vcxproj create mode 100644 network/trans/WFPSampler/svc/WFPSamplerService.vcxproj.Filters create mode 100644 network/trans/WFPSampler/sys/ClassifyFunctions_AdvancedPacketInjectionCallouts.cpp create mode 100644 network/trans/WFPSampler/sys/ClassifyFunctions_AdvancedPacketInjectionCallouts.h create mode 100644 network/trans/WFPSampler/sys/ClassifyFunctions_BasicActionCallouts.cpp create mode 100644 network/trans/WFPSampler/sys/ClassifyFunctions_BasicActionCallouts.h create mode 100644 network/trans/WFPSampler/sys/ClassifyFunctions_BasicPacketExaminationCallouts.cpp create mode 100644 network/trans/WFPSampler/sys/ClassifyFunctions_BasicPacketExaminationCallouts.h create mode 100644 network/trans/WFPSampler/sys/ClassifyFunctions_BasicPacketInjectionCallouts.cpp create mode 100644 network/trans/WFPSampler/sys/ClassifyFunctions_BasicPacketInjectionCallouts.h create mode 100644 network/trans/WFPSampler/sys/ClassifyFunctions_BasicPacketModificationCallouts.cpp create mode 100644 network/trans/WFPSampler/sys/ClassifyFunctions_BasicPacketModificationCallouts.h create mode 100644 network/trans/WFPSampler/sys/ClassifyFunctions_BasicStreamInjectionCallouts.cpp create mode 100644 network/trans/WFPSampler/sys/ClassifyFunctions_BasicStreamInjectionCallouts.h create mode 100644 network/trans/WFPSampler/sys/ClassifyFunctions_FastPacketInjectionCallouts.cpp create mode 100644 network/trans/WFPSampler/sys/ClassifyFunctions_FastPacketInjectionCallouts.h create mode 100644 network/trans/WFPSampler/sys/ClassifyFunctions_FastStreamInjectionCallouts.cpp create mode 100644 network/trans/WFPSampler/sys/ClassifyFunctions_FastStreamInjectionCallouts.h create mode 100644 network/trans/WFPSampler/sys/ClassifyFunctions_FlowAssociationCallouts.cpp create mode 100644 network/trans/WFPSampler/sys/ClassifyFunctions_FlowAssociationCallouts.h create mode 100644 network/trans/WFPSampler/sys/ClassifyFunctions_Include.h create mode 100644 network/trans/WFPSampler/sys/ClassifyFunctions_PendAuthorizationCallouts.cpp create mode 100644 network/trans/WFPSampler/sys/ClassifyFunctions_PendAuthorizationCallouts.h create mode 100644 network/trans/WFPSampler/sys/ClassifyFunctions_PendEndpointClosureCallouts.cpp create mode 100644 network/trans/WFPSampler/sys/ClassifyFunctions_PendEndpointClosureCallouts.h create mode 100644 network/trans/WFPSampler/sys/ClassifyFunctions_ProxyCallouts.cpp create mode 100644 network/trans/WFPSampler/sys/ClassifyFunctions_ProxyCallouts.h create mode 100644 network/trans/WFPSampler/sys/CompletionFunctions_AdvancedPacketInjectionCallouts.cpp create mode 100644 network/trans/WFPSampler/sys/CompletionFunctions_AdvancedPacketInjectionCallouts.h create mode 100644 network/trans/WFPSampler/sys/CompletionFunctions_BasicPacketInjectionCallouts.cpp create mode 100644 network/trans/WFPSampler/sys/CompletionFunctions_BasicPacketInjectionCallouts.h create mode 100644 network/trans/WFPSampler/sys/CompletionFunctions_BasicPacketModificationCallouts.cpp create mode 100644 network/trans/WFPSampler/sys/CompletionFunctions_BasicPacketModificationCallouts.h create mode 100644 network/trans/WFPSampler/sys/CompletionFunctions_BasicStreamInjectionCallouts.cpp create mode 100644 network/trans/WFPSampler/sys/CompletionFunctions_BasicStreamInjectionCallouts.h create mode 100644 network/trans/WFPSampler/sys/CompletionFunctions_FastPacketInjectionCallouts.cpp create mode 100644 network/trans/WFPSampler/sys/CompletionFunctions_FastPacketInjectionCallouts.h create mode 100644 network/trans/WFPSampler/sys/CompletionFunctions_FastStreamInjectionCallouts.cpp create mode 100644 network/trans/WFPSampler/sys/CompletionFunctions_FastStreamInjectionCallouts.h create mode 100644 network/trans/WFPSampler/sys/CompletionFunctions_Include.h create mode 100644 network/trans/WFPSampler/sys/CompletionFunctions_PendAuthorizationCallouts.cpp create mode 100644 network/trans/WFPSampler/sys/CompletionFunctions_PendAuthorizationCallouts.h create mode 100644 network/trans/WFPSampler/sys/CompletionFunctions_ProxyCallouts.cpp create mode 100644 network/trans/WFPSampler/sys/CompletionFunctions_ProxyCallouts.h create mode 100644 network/trans/WFPSampler/sys/Framework_Events.cpp create mode 100644 network/trans/WFPSampler/sys/Framework_Events.h create mode 100644 network/trans/WFPSampler/sys/Framework_Include.h create mode 100644 network/trans/WFPSampler/sys/Framework_PowerStates.cpp create mode 100644 network/trans/WFPSampler/sys/Framework_PowerStates.h create mode 100644 network/trans/WFPSampler/sys/Framework_WFPSamplerCalloutDriver.cpp create mode 100644 network/trans/WFPSampler/sys/Framework_WFPSamplerCalloutDriver.h create mode 100644 network/trans/WFPSampler/sys/Framework_WFPSamplerCalloutDriver.rc create mode 100644 network/trans/WFPSampler/sys/HelperFunctions_ExposedCallouts.cpp create mode 100644 network/trans/WFPSampler/sys/HelperFunctions_ExposedCallouts.h create mode 100644 network/trans/WFPSampler/sys/NotifyFunctions_AdvancedCallouts.cpp create mode 100644 network/trans/WFPSampler/sys/NotifyFunctions_AdvancedCallouts.h create mode 100644 network/trans/WFPSampler/sys/NotifyFunctions_BasicCallouts.cpp create mode 100644 network/trans/WFPSampler/sys/NotifyFunctions_BasicCallouts.h create mode 100644 network/trans/WFPSampler/sys/NotifyFunctions_FastCallouts.cpp create mode 100644 network/trans/WFPSampler/sys/NotifyFunctions_FastCallouts.h create mode 100644 network/trans/WFPSampler/sys/NotifyFunctions_FlowDelete.cpp create mode 100644 network/trans/WFPSampler/sys/NotifyFunctions_FlowDelete.h create mode 100644 network/trans/WFPSampler/sys/NotifyFunctions_Include.h create mode 100644 network/trans/WFPSampler/sys/NotifyFunctions_PendCallouts.cpp create mode 100644 network/trans/WFPSampler/sys/NotifyFunctions_PendCallouts.h create mode 100644 network/trans/WFPSampler/sys/NotifyFunctions_ProxyCallouts.cpp create mode 100644 network/trans/WFPSampler/sys/NotifyFunctions_ProxyCallouts.h create mode 100644 network/trans/WFPSampler/sys/SubscriptionFunctions_BFEState.cpp create mode 100644 network/trans/WFPSampler/sys/SubscriptionFunctions_BFEState.h create mode 100644 network/trans/WFPSampler/sys/SubscriptionFunctions_Include.h create mode 100644 network/trans/WFPSampler/sys/WFPSamplerCalloutDriver.InX create mode 100644 network/trans/WFPSampler/sys/WFPSamplerCalloutDriver.vcxproj create mode 100644 network/trans/WFPSampler/sys/WFPSamplerCalloutDriver.vcxproj.Filters create mode 100644 network/trans/WFPSampler/syslib/HelperFunctions_ClassifyData.cpp create mode 100644 network/trans/WFPSampler/syslib/HelperFunctions_ClassifyData.h create mode 100644 network/trans/WFPSampler/syslib/HelperFunctions_DeferredProcedureCalls.cpp create mode 100644 network/trans/WFPSampler/syslib/HelperFunctions_DeferredProcedureCalls.h create mode 100644 network/trans/WFPSampler/syslib/HelperFunctions_FlowContext.cpp create mode 100644 network/trans/WFPSampler/syslib/HelperFunctions_FlowContext.h create mode 100644 network/trans/WFPSampler/syslib/HelperFunctions_FwpObjects.cpp create mode 100644 network/trans/WFPSampler/syslib/HelperFunctions_FwpObjects.h create mode 100644 network/trans/WFPSampler/syslib/HelperFunctions_Headers.cpp create mode 100644 network/trans/WFPSampler/syslib/HelperFunctions_Headers.h create mode 100644 network/trans/WFPSampler/syslib/HelperFunctions_ICMPMessages.h create mode 100644 network/trans/WFPSampler/syslib/HelperFunctions_Include.h create mode 100644 network/trans/WFPSampler/syslib/HelperFunctions_InjectionData.cpp create mode 100644 network/trans/WFPSampler/syslib/HelperFunctions_InjectionData.h create mode 100644 network/trans/WFPSampler/syslib/HelperFunctions_Macros.h create mode 100644 network/trans/WFPSampler/syslib/HelperFunctions_NDIS.cpp create mode 100644 network/trans/WFPSampler/syslib/HelperFunctions_NDIS.h create mode 100644 network/trans/WFPSampler/syslib/HelperFunctions_NetBuffer.cpp create mode 100644 network/trans/WFPSampler/syslib/HelperFunctions_NetBuffer.h create mode 100644 network/trans/WFPSampler/syslib/HelperFunctions_NotifyData.h create mode 100644 network/trans/WFPSampler/syslib/HelperFunctions_PendData.cpp create mode 100644 network/trans/WFPSampler/syslib/HelperFunctions_PendData.h create mode 100644 network/trans/WFPSampler/syslib/HelperFunctions_RedirectData.cpp create mode 100644 network/trans/WFPSampler/syslib/HelperFunctions_RedirectData.h create mode 100644 network/trans/WFPSampler/syslib/HelperFunctions_WorkItems.cpp create mode 100644 network/trans/WFPSampler/syslib/HelperFunctions_WorkItems.h create mode 100644 network/trans/WFPSampler/syslib/WFPSampler.vcxproj create mode 100644 network/trans/WFPSampler/syslib/WFPSampler.vcxproj.Filters create mode 100644 network/wlan/ihvfrm/arm/rc4utils.dll create mode 100644 network/wlan/ihvfrm/arm/rc4utils.lib create mode 100644 network/wlan/ihvfrm/arm64/rc4utils.dll create mode 100644 network/wlan/ihvfrm/arm64/rc4utils.lib create mode 100644 nfc/NfcSimulator.sln create mode 100644 nfc/Simulator/Inc/NfcSimulatorDDI.h create mode 100644 nfc/Simulator/README.md create mode 100644 nfc/Simulator/Src/Connection.cpp create mode 100644 nfc/Simulator/Src/Connection.h create mode 100644 nfc/Simulator/Src/Constants.h create mode 100644 nfc/Simulator/Src/Device.cpp create mode 100644 nfc/Simulator/Src/Device.h create mode 100644 nfc/Simulator/Src/Driver.cpp create mode 100644 nfc/Simulator/Src/Driver.h create mode 100644 nfc/Simulator/Src/FileContext.cpp create mode 100644 nfc/Simulator/Src/FileContext.h create mode 100644 nfc/Simulator/Src/Internal.h create mode 100644 nfc/Simulator/Src/Internalsrc.cpp create mode 100644 nfc/Simulator/Src/LinkList.h create mode 100644 nfc/Simulator/Src/NfcDriverSim.vcxproj create mode 100644 nfc/Simulator/Src/NfcDriverSim.vcxproj.Filters create mode 100644 nfc/Simulator/Src/NfcSimulator.def create mode 100644 nfc/Simulator/Src/NfcSimulator.inx create mode 100644 nfc/Simulator/Src/NfcSimulator.rc create mode 100644 nfc/Simulator/Src/Queue.cpp create mode 100644 nfc/Simulator/Src/Queue.h create mode 100644 nfc/Simulator/Src/RoutingTable.cpp create mode 100644 nfc/Simulator/Src/RoutingTable.h create mode 100644 nfc/Simulator/Src/SecureElement.cpp create mode 100644 nfc/Simulator/Src/SecureElement.h create mode 100644 nfc/Simulator/Src/SmartCard.cpp create mode 100644 nfc/Simulator/Src/SmartCard.h create mode 100644 nfc/Simulator/Src/SmartCardReader.cpp create mode 100644 nfc/Simulator/Src/SmartCardReader.h create mode 100644 nfc/Simulator/Src/SocketListener.cpp create mode 100644 nfc/Simulator/Src/SocketListener.h create mode 100644 nfc/Simulator/Src/WppDefs.h create mode 100644 pofx/PEP/ReadMe.md create mode 100644 pofx/PEP/acpi/acpispecific.c create mode 100644 pofx/PEP/acpi/acpispecific.h create mode 100644 pofx/PEP/acpi/pchsrc.c create mode 100644 pofx/PEP/acpi/sampleacpipep.inx create mode 100644 pofx/PEP/acpi/sampleacpipep.rc create mode 100644 pofx/PEP/acpi/sampleacpipep.vcxproj create mode 100644 pofx/PEP/acpi/sampleacpipep.vcxproj.Filters create mode 100644 pofx/PEP/acpi/testdevice.c create mode 100644 pofx/PEP/common/acpinotify.c create mode 100644 pofx/PEP/common/driver.c create mode 100644 pofx/PEP/common/pchsrc.c create mode 100644 pofx/PEP/common/pep.c create mode 100644 pofx/PEP/common/pepcommon.vcxproj create mode 100644 pofx/PEP/common/pepcommon.vcxproj.Filters create mode 100644 pofx/PEP/common/util.c create mode 100644 pofx/PEP/common/work.c create mode 100644 pofx/PEP/inc/common.h create mode 100644 pofx/PEP/inc/pch.h create mode 100644 pofx/PEP/inc/pep.h create mode 100644 pofx/PEP/inc/trace.h create mode 100644 pofx/PEP/pepsamples.sln create mode 100644 pos/drivers/MagneticStripeReader/Device.cpp create mode 100644 pos/drivers/MagneticStripeReader/Device.h create mode 100644 pos/drivers/MagneticStripeReader/Driver.cpp create mode 100644 pos/drivers/MagneticStripeReader/File.cpp create mode 100644 pos/drivers/MagneticStripeReader/File.h create mode 100644 pos/drivers/MagneticStripeReader/IoRead.cpp create mode 100644 pos/drivers/MagneticStripeReader/IoRead.h create mode 100644 pos/drivers/MagneticStripeReader/Ioctl.cpp create mode 100644 pos/drivers/MagneticStripeReader/Ioctl.h create mode 100644 pos/drivers/MagneticStripeReader/MagneticStripeReader.sln create mode 100644 pos/drivers/MagneticStripeReader/PosEvents.cpp create mode 100644 pos/drivers/MagneticStripeReader/PosEvents.h create mode 100644 pos/drivers/MagneticStripeReader/README.md create mode 100644 pos/drivers/MagneticStripeReader/SampleMagneticStripeReaderDrv.inf create mode 100644 pos/drivers/MagneticStripeReader/SampleMagneticStripeReaderDrv.vcxproj create mode 100644 pos/drivers/MagneticStripeReader/SampleMagneticStripeReaderDrv.vcxproj.Filters create mode 100644 pos/drivers/MagneticStripeReader/exports.def create mode 100644 pos/drivers/MagneticStripeReader/pch.h create mode 100644 pos/drivers/MagneticStripeReader/pchsrc.cpp create mode 100644 pos/drivers/barcodescanner/BarcodeScanner.sln create mode 100644 pos/drivers/barcodescanner/Device.cpp create mode 100644 pos/drivers/barcodescanner/Device.h create mode 100644 pos/drivers/barcodescanner/Driver.cpp create mode 100644 pos/drivers/barcodescanner/File.cpp create mode 100644 pos/drivers/barcodescanner/File.h create mode 100644 pos/drivers/barcodescanner/IoRead.cpp create mode 100644 pos/drivers/barcodescanner/IoRead.h create mode 100644 pos/drivers/barcodescanner/Ioctl.cpp create mode 100644 pos/drivers/barcodescanner/Ioctl.h create mode 100644 pos/drivers/barcodescanner/PosEvents.cpp create mode 100644 pos/drivers/barcodescanner/PosEvents.h create mode 100644 pos/drivers/barcodescanner/README.md create mode 100644 pos/drivers/barcodescanner/SampleBarcodeScannerDrv.inf create mode 100644 pos/drivers/barcodescanner/SampleBarcodeScannerDrv.vcxproj create mode 100644 pos/drivers/barcodescanner/SampleBarcodeScannerDrv.vcxproj.Filters create mode 100644 pos/drivers/barcodescanner/exports.def create mode 100644 pos/drivers/barcodescanner/pch.h create mode 100644 pos/drivers/barcodescanner/pchsrc.cpp create mode 100644 simbatt/ReadMe.md create mode 100644 simbatt/SimBatt.sln create mode 100644 simbatt/func/batclass_prepublish.h create mode 100644 simbatt/func/miniclass.c create mode 100644 simbatt/func/simbatt-wdfcoinstall.inx create mode 100644 simbatt/func/simbatt.h create mode 100644 simbatt/func/simbatt.inx create mode 100644 simbatt/func/simbatt.rc create mode 100644 simbatt/func/simbatt.vcxproj create mode 100644 simbatt/func/simbatt.vcxproj.Filters create mode 100644 simbatt/func/simbattdriverif.h create mode 100644 simbatt/func/wdf.c create mode 100644 storage/msdsm/ReadMe.md create mode 100644 storage/msdsm/msdsm.sln create mode 100644 storage/msdsm/src/SampleDSM.inf create mode 100644 storage/msdsm/src/SampleDSM.vcxproj create mode 100644 storage/msdsm/src/SampleDSM.vcxproj.Filters create mode 100644 storage/msdsm/src/dsmmain.c create mode 100644 storage/msdsm/src/dsmtrace.mof create mode 100644 storage/msdsm/src/intrface.c create mode 100644 storage/msdsm/src/msdsm.h create mode 100644 storage/msdsm/src/msdsm.mof create mode 100644 storage/msdsm/src/msdsm.rc create mode 100644 storage/msdsm/src/msdsmdsm.mof create mode 100644 storage/msdsm/src/precomp.h create mode 100644 storage/msdsm/src/precompsrc.c create mode 100644 storage/msdsm/src/prototypes.h create mode 100644 storage/msdsm/src/trace.h create mode 100644 storage/msdsm/src/utils.c create mode 100644 storage/msdsm/src/wmi.c create mode 100644 usb/kmdf_fx2/ReadMe.md create mode 100644 usb/kmdf_fx2/deviceMetadata/B4D697F5-1C56-4807-ACCD-B28C09D37FF0.devicemetadata-ms create mode 100644 usb/kmdf_fx2/driver/Device.c create mode 100644 usb/kmdf_fx2/driver/bulkrwr.c create mode 100644 usb/kmdf_fx2/driver/driver.c create mode 100644 usb/kmdf_fx2/driver/interrupt.c create mode 100644 usb/kmdf_fx2/driver/ioctl.c create mode 100644 usb/kmdf_fx2/driver/osrusbfx2.h create mode 100644 usb/kmdf_fx2/driver/osrusbfx2.inx create mode 100644 usb/kmdf_fx2/driver/osrusbfx2.man create mode 100644 usb/kmdf_fx2/driver/osrusbfx2.rc create mode 100644 usb/kmdf_fx2/driver/osrusbfx2.vcxproj create mode 100644 usb/kmdf_fx2/driver/osrusbfx2.vcxproj.Filters create mode 100644 usb/kmdf_fx2/driver/trace.h create mode 100644 usb/kmdf_fx2/exe/dump.c create mode 100644 usb/kmdf_fx2/exe/osrusbfx2.vcxproj create mode 100644 usb/kmdf_fx2/exe/osrusbfx2.vcxproj.Filters create mode 100644 usb/kmdf_fx2/exe/test.cmd create mode 100644 usb/kmdf_fx2/exe/testapp.c create mode 100644 usb/kmdf_fx2/exe/testapp.rc create mode 100644 usb/kmdf_fx2/inc/prototypes.h create mode 100644 usb/kmdf_fx2/inc/public.h create mode 100644 usb/kmdf_fx2/kmdf_fx2.sln create mode 100644 usb/ufxclientsample/UfxClientSample.inx create mode 100644 usb/ufxclientsample/UfxClientSample.man create mode 100644 usb/ufxclientsample/defaultqueue.c create mode 100644 usb/ufxclientsample/defaultqueue.h create mode 100644 usb/ufxclientsample/device.c create mode 100644 usb/ufxclientsample/device.h create mode 100644 usb/ufxclientsample/driver.c create mode 100644 usb/ufxclientsample/driver.h create mode 100644 usb/ufxclientsample/event.c create mode 100644 usb/ufxclientsample/event.h create mode 100644 usb/ufxclientsample/interrupt.c create mode 100644 usb/ufxclientsample/interrupt.h create mode 100644 usb/ufxclientsample/readme.md create mode 100644 usb/ufxclientsample/registers.c create mode 100644 usb/ufxclientsample/registers.h create mode 100644 usb/ufxclientsample/trace.h create mode 100644 usb/ufxclientsample/transfer.c create mode 100644 usb/ufxclientsample/transfer.h create mode 100644 usb/ufxclientsample/ufxclientsample.rc create mode 100644 usb/ufxclientsample/ufxclientsample.sln create mode 100644 usb/ufxclientsample/ufxclientsample.vcxproj create mode 100644 usb/ufxclientsample/ufxclientsample.vcxproj.Filters create mode 100644 usb/ufxclientsample/ufxdevice.c create mode 100644 usb/ufxclientsample/ufxdevice.h create mode 100644 usb/ufxclientsample/ufxendpoint.c create mode 100644 usb/ufxclientsample/ufxendpoint.h 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/usbsamp/ReadMe.md create mode 100644 usb/usbsamp/exe/testapp.c create mode 100644 usb/usbsamp/exe/testapp.rc create mode 100644 usb/usbsamp/exe/usbsamp.vcxproj create mode 100644 usb/usbsamp/exe/usbsamp.vcxproj.Filters create mode 100644 usb/usbsamp/sys/bulkrwr.c create mode 100644 usb/usbsamp/sys/device.c create mode 100644 usb/usbsamp/sys/driver.c create mode 100644 usb/usbsamp/sys/driver/usbsamp.inx create mode 100644 usb/usbsamp/sys/driver/usbsamp.vcxproj create mode 100644 usb/usbsamp/sys/driver/usbsamp.vcxproj.Filters create mode 100644 usb/usbsamp/sys/isorwr.c create mode 100644 usb/usbsamp/sys/private.h create mode 100644 usb/usbsamp/sys/public.h create mode 100644 usb/usbsamp/sys/queue.c create mode 100644 usb/usbsamp/sys/stream.c create mode 100644 usb/usbsamp/sys/usbsamp.rc create mode 100644 usb/usbsamp/usbsamp.htm create mode 100644 usb/usbsamp/usbsamp.sln 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/readme.htm create mode 100644 video/KMDOD/sampledisplay.rc rename wmi/wmiacpi/{wmiacpi => }/ReadMe.md (100%) rename wmi/wmiacpi/{wmiacpi => }/acpimof.def (100%) rename wmi/wmiacpi/{wmiacpi => }/acpimof.mof (100%) rename wmi/wmiacpi/{wmiacpi => }/acpimof.rc (100%) rename wmi/wmiacpi/{wmiacpi => }/acpimof.vcxproj (97%) rename wmi/wmiacpi/{wmiacpi => }/acpimof.vcxproj.Filters (82%) rename wmi/wmiacpi/{wmiacpi => }/device.asl (100%) rename wmi/wmiacpi/{wmiacpi => }/readme.htm (100%) rename wmi/wmiacpi/{wmiacpi => }/wmi-acpi.htm (100%) 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 diff --git a/audio/sysvad/ContosoKeywordDetector.h b/audio/sysvad/ContosoKeywordDetector.h new file mode 100644 index 000000000..fce0f3df1 --- /dev/null +++ b/audio/sysvad/ContosoKeywordDetector.h @@ -0,0 +1,34 @@ +/* + Copyright (c) Microsoft Corporation All Rights Reserved + + Contoso voice activation driver definitions + + Hardware manufacturers define identifiers and data structures specific to + their detection technology to pass voice models, keyword data, speaker + data, or any other data relevant to their technology. + +*/ + +// +// Identifier for Contoso keyword configuration data. +// +// {6F7DBCC1-202E-498D-99C5-61C36C4EB2DC} +DEFINE_GUID(CONTOSO_KEYWORDCONFIGURATION_IDENTIFIER, 0x6f7dbcc1, 0x202e, 0x498d, 0x99, 0xc5, 0x61, 0xc3, 0x6c, 0x4e, 0xb2, 0xdc); + +// +// The format of the Contoso keyword pattern matching data. +// +typedef struct +{ + SOUNDDETECTOR_PATTERNHEADER Header; + LONGLONG ContosoDetectorConfigurationData; +} CONTOSO_KEYWORDCONFIGURATION; + +// +// The format of the Contoso match result data. +// +typedef struct +{ + SOUNDDETECTOR_PATTERNHEADER Header; + LONGLONG ContosoDetectorResultData; +} CONTOSO_KEYWORDDETECTIONRESULT; diff --git a/audio/sysvad/EndpointsCommon/EndpointsCommon.vcxproj b/audio/sysvad/EndpointsCommon/EndpointsCommon.vcxproj new file mode 100644 index 000000000..aef0e9736 --- /dev/null +++ b/audio/sysvad/EndpointsCommon/EndpointsCommon.vcxproj @@ -0,0 +1,182 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {E3BA10BE-08FF-4244-86C6-C788072CEA25} + $(MSBuildProjectName) + 1 + Debug + Win32 + {4DA68694-1C5F-491B-9272-A8223477E4B7} + + + + Windows10 + False + Universal + KMDF + WindowsKernelModeDriver10.0 + StaticLibrary + + + Windows10 + True + Universal + KMDF + WindowsKernelModeDriver10.0 + StaticLibrary + + + Windows10 + False + Universal + KMDF + WindowsKernelModeDriver10.0 + StaticLibrary + + + Windows10 + True + Universal + KMDF + WindowsKernelModeDriver10.0 + StaticLibrary + + + + $(IntDir) + + + + + + + + + + + + + + + + EndpointsCommon + + + EndpointsCommon + + + EndpointsCommon + + + EndpointsCommon + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);.. + %(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);.. + %(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);.. + %(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);.. + %(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);.. + %(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);.. + %(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);.. + %(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);.. + %(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);.. + %(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);.. + %(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);.. + %(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);.. + %(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/audio/sysvad/EndpointsCommon/EndpointsCommon.vcxproj.Filters b/audio/sysvad/EndpointsCommon/EndpointsCommon.vcxproj.Filters new file mode 100644 index 000000000..167f04687 --- /dev/null +++ b/audio/sysvad/EndpointsCommon/EndpointsCommon.vcxproj.Filters @@ -0,0 +1,59 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {44370F68-49C1-4647-BD26-31F7EBE942A3} + + + h;hpp;hxx;hm;inl;inc;xsd + {AEA2BA25-EBF9-454D-BC73-91D1CC36FB6F} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {95FAF38F-0673-42EF-A3C6-35F98AE2E188} + + + inf;inv;inx;mof;mc; + {4D0AE1D8-6C62-4B48-B101-690D5E955972} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + 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/audio/sysvad/EndpointsCommon/MiniportAudioEngineNode.cpp b/audio/sysvad/EndpointsCommon/MiniportAudioEngineNode.cpp new file mode 100644 index 000000000..db3fda398 --- /dev/null +++ b/audio/sysvad/EndpointsCommon/MiniportAudioEngineNode.cpp @@ -0,0 +1,1045 @@ +/*++ + +Module Name: + + MiniportAudioEngineNode.cpp + +Abstract: + + Implementation of IMiniportAudioEngineNode interface for wavert miniport. + +--*/ +#pragma warning (disable : 4127) + +#include +#include +#include "simple.h" +#include "minwavert.h" +#include "minwavertstream.h" +#include "IHVPrivatePropertySet.h" +#include "UnittestData.h" + +#define MINWAVERT_POOLTAG 'RWNM' +//============================================================================= +// IMiniportAudioEngineNode +//============================================================================= + +#pragma code_seg("PAGE") +/*----------------------------------------------------------------------------- +IMiniportAudioEngineNode::GetAudioEngineDescriptor + +Description: + + Portcls calls this method to get the pin id for each pin (host process, + offload, and loopback) on a specific audio engine node. + +Parameters: + + _In_ _ulNodeId: node id for the target audio engine node + _Out_ pAudioEngineDescriptor: a pointer to a KSAUDIOENGINE_DESCRIPTOR to receive the pin id informationived as input to its DriverEntry routine. + +Return Value: + + Appropriate NTSTATUS code + +Called at PASSIVE_LEVEL + +Remarks: + + The property value is of type KSAUDIOENGINE_DESCRIPTOR and indicates the static properties of the audio engine node. + The KSAUDIOENGINE_DESCRIPTOR structure is defined as follows: + + typedef struct _tagKSAUDIOENGINE_DESCRIPTOR + { + UINT nHostPinId; + UINT nOffloadPinId; + UINT nLoopbackPinId; + } KSAUDIOENGINE_DESCRIPTOR, *PKSAUDIOENGINE_DESCRIPTOR; + + The fields are defined as: + nHostPinId – The ID of the pin factory connected to the audio engine node that is intended for host processed audio data. This is the pin factory on which a software audio engine will run. + nOffloadPinId – The ID of the pin factory connected to the audio engine node that is intended for offloaded streams. + nLoopbackPinId – The ID of the pin factory connected to the audio engine that is intended for supplying a post-mix loopback or reference stream. + + All pin ids need to be unique for each audio engine node. + + A wave miniport might have more than one audio engine node and each engine node has their own set of engine pin ids. + A driver needs to make sure that the correct set of audio engine descriptor information is returned. +-------------------------------------------------------------------------------------------------------------------------*/ +STDMETHODIMP_(NTSTATUS) CMiniportWaveRT::GetAudioEngineDescriptor(_In_ ULONG _ulNodeId, _Out_ KSAUDIOENGINE_DESCRIPTOR *_pAudioEngineDescriptor) +{ + PAGED_CODE (); + NTSTATUS ntStatus = STATUS_UNSUCCESSFUL; + + ASSERT (_pAudioEngineDescriptor); + DPF_ENTER(("[CMiniportWaveRT::GetAudioEngineDescriptor]")); + // In this sample driver, only one single engine node is exposed in its wave filter + if (_ulNodeId == KSNODE_WAVE_AUDIO_ENGINE) + { + _pAudioEngineDescriptor->nHostPinId = GetSystemPinId(); + _pAudioEngineDescriptor->nOffloadPinId = GetOffloadPinId(); + _pAudioEngineDescriptor->nLoopbackPinId = GetLoopbackPinId(); + ntStatus = STATUS_SUCCESS; + } + else + { + ntStatus = STATUS_INVALID_DEVICE_REQUEST; + } + return ntStatus; +} +#pragma code_seg("PAGE") +/*----------------------------------------------------------------------------- +IMiniportAudioEngineNode::GetGfxState + +Description: + + Portcls calls this method to get the audio engine GFX's state + +Parameters + + _In_ _ulNodeId: node id for the target audio engine node + _Out_ pbEnable: a pointer to a BOOL value for receieving the returned GFX state + +Return Value: + + Appropriate NTSTATUS code + +Called at PASSIVE_LEVEL + +Remarks + The global operations (on the device stream) inside HW Audio Engine (such as src, dsp, and other special effects) are hidden from the software audio stack. +So, the driver should return TRUE if any one of the effects is on and returns FALSE when all the opertations are off. +-------------------------------------------------------------------------------------------------------------------------*/ +STDMETHODIMP_(NTSTATUS) CMiniportWaveRT::GetGfxState(_In_ ULONG _ulNodeId, _Out_ BOOL *_pbEnable) +{ + PAGED_CODE (); + + DPF_ENTER(("[CMiniportWaveRT::GetGfxState]")); + + UNREFERENCED_PARAMETER(_ulNodeId); + + *_pbEnable = m_bGfxEnabled; + return STATUS_SUCCESS; +} +#pragma code_seg("PAGE") +/*----------------------------------------------------------------------------- +IMiniportAudioEngineNode::SetGfxState + +Decscription: + + Portcls calls this method to set the audio engine GFX's state + +Parameters: + + _In_ _ulNodeId: node id for the target audio engine node + _In_ bEnable: a BOOL value. TRU: to enable GFX, FALSE: to disable GFX + +Return Value: + + Appropriate NTSTATUS code + +Called at PASSIVE_LEVEL + +Remarks + The global operations (on the device stream) inside HW Audio Engine (such as src, dsp, and other special effects) are hidden from the software audio stack. +So, when handling disabling GFX, the driver should ALL the effects. When enabling GFX, it's up the driver+HW Audio Engine to decide what opertations to turn on +when they see appropriate. +-------------------------------------------------------------------------------------------------------------------------*/ +STDMETHODIMP_(NTSTATUS) CMiniportWaveRT::SetGfxState(_In_ ULONG _ulNodeId, _In_ BOOL _bEnable) +{ + PAGED_CODE (); + + DPF_ENTER(("[CMiniportWaveRT::SetGfxState]")); + + UNREFERENCED_PARAMETER(_ulNodeId); + + // see above comments for appropriate enabling/disabling opertations on the HW Audio Engine + m_bGfxEnabled = _bEnable; + return STATUS_SUCCESS; +} +#pragma code_seg("PAGE") +/*----------------------------------------------------------------------------- +IMiniportAudioEngineNode::GetEngineFormatSize + +Decscription: + + When handling GetMixFormat, DeviceFormat, or SupportDeviceFormatsList, Portcls calls + this method to know the correct data size to allocate for the receiving the corresponding + format information. + +Parameters: + + _In_ _ulNodeId: node id for the target audio engine node + _In_ eEngineFormatType: format target to indicate which format size is being asked + _Out_ pulFormatSize: a pointer to a ULONG variable for receiving returned szize information + +Return Value: + + Appropriate NTSTATUS code + +Called at PASSIVE_LEVEL + +Remarks + +-------------------------------------------------------------------------------------------------------------------------*/ +NTSTATUS CMiniportWaveRT::GetEngineFormatSize +( + _In_ ULONG _ulNodeId, + _In_ eEngineFormatType _formatType, + _Out_ ULONG *_pulFormatSize +) +{ + PAGED_CODE (); + NTSTATUS ntStatus = STATUS_SUCCESS; + + DPF_ENTER(("[CMiniportWaveRT::GetEngineFormatSize]")); + ASSERT (_pulFormatSize); + + IF_TRUE_ACTION_JUMP(_ulNodeId != KSNODE_WAVE_AUDIO_ENGINE, ntStatus = STATUS_INVALID_DEVICE_REQUEST, Exit); + + switch (_formatType) + { + case eMixFormat: + DPF_ENTER(("[eMixFormat]")); + *_pulFormatSize = sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE); + break; + case eDeviceFormat: + DPF_ENTER(("[eDeviceFormat]")); + *_pulFormatSize = sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE); + break; + case eSupportedDeviceFormats: + DPF_ENTER(("[eSupportedDeviceFormats]")); + *_pulFormatSize = sizeof(KSMULTIPLE_ITEM) + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE) * GetAudioEngineSupportedDeviceFormats(NULL); + break; + default: + ntStatus = STATUS_INVALID_PARAMETER; + break; + } +Exit: + return ntStatus; +} +#pragma code_seg("PAGE") +/*----------------------------------------------------------------------------- +IMiniportAudioEngineNode::GetMixFormat + +Decscription: + + GetMixFormat returns the current mix format used by the HW Audio Engine + +Parameters: + + _In_ _ulNodeId: node id for the target audio engine node + _Out_ pFormat: a buffer pointer for receiving the mix format information being asked + _In_ ulBufferSize: a pointer to a ULONG variable that has the size of the buffer pointed by pFormat + +Return Value: + + Appropriate NTSTATUS code + +Called at PASSIVE_LEVEL + +Remarks + This is a read only operation; the HW Audio Engine mix format is determined by the HW Audio Engine alone. + +-------------------------------------------------------------------------------------------------------------------------*/ +STDMETHODIMP_(NTSTATUS) CMiniportWaveRT::GetMixFormat(_In_ ULONG _ulNodeId, _Out_ KSDATAFORMAT_WAVEFORMATEX *_pFormat, _In_ ULONG _ulBufferSize) +{ + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + PAGED_CODE (); + + ASSERT (_pFormat); + + DPF_ENTER(("[CMiniportWaveRT::GetMixFormat]")); + + IF_TRUE_ACTION_JUMP(_ulNodeId != KSNODE_WAVE_AUDIO_ENGINE, ntStatus = STATUS_INVALID_DEVICE_REQUEST, Exit); + IF_TRUE_ACTION_JUMP(_ulBufferSize < sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), ntStatus = STATUS_BUFFER_TOO_SMALL, Exit); +#pragma warning(push) + // IMiniportAudioEngineNode::GetMixFormat's annotation on _pFormat requires it to be KSDATAFORMAT_WAVEFORMATEX. However, + // this implementation here will always be called by our own code with _pFormat to be KSDATAFORMAT_WAVEFORMATEXTENSIBLE, + // so there should be no buffer overrun; also the IF_TRUE_ACTION_JUMP above also help to avoid buffer overrun. +#pragma warning(disable:6386) + RtlCopyMemory((PVOID)_pFormat, (PVOID)m_pMixFormat, sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE)); +#pragma warning (pop) + ntStatus = STATUS_SUCCESS; + +Exit: + return ntStatus; +} +/*----------------------------------------------------------------------------- +IMiniportAudioEngineNode::GetDeviceFormat + +Decscription: + + GetDeviceFormat returns the current device format used by the HW Audio Engine + +Parameters: + + _In_ _ulNodeId: node id for the target audio engine node + _Out_ pFormat: a buffer pointer for receiving the device format information being asked + _In_ ulBufferSize: a pointer to a ULONG variable that has the size of the buffer pointed by pFormat + +Return Value: + + Appropriate NTSTATUS code + +Called at PASSIVE_LEVEL + +Remarks + Setting the device format of a HW Audio Engine could potential impact the mix format inside the HD Audio Engine. +The driver might need to add appropriate src/format converter according or change mix format. + +-------------------------------------------------------------------------------------------------------------------------*/ +#pragma code_seg("PAGE") +STDMETHODIMP_(NTSTATUS) CMiniportWaveRT::GetDeviceFormat(_In_ ULONG _ulNodeId, _Out_ KSDATAFORMAT_WAVEFORMATEX *_pFormat, _In_ ULONG _ulBufferSize) +{ + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + PAGED_CODE (); + + ASSERT (_pFormat); + + DPF_ENTER(("[CMiniportWaveRT::GetDeviceFormat]")); + IF_TRUE_ACTION_JUMP(_ulNodeId != KSNODE_WAVE_AUDIO_ENGINE, ntStatus = STATUS_INVALID_DEVICE_REQUEST, Exit); + IF_TRUE_ACTION_JUMP(_ulBufferSize < sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), ntStatus = STATUS_BUFFER_TOO_SMALL, Exit); +#pragma warning(push) + // IMiniportAudioEngineNode::GetDeviceFormat's annotation on _pFormat requires it to be KSDATAFORMAT_WAVEFORMATEX. However, + // this implementation here will always be called by our own code with _pFormat to be KSDATAFORMAT_WAVEFORMATEXTENSIBLE, + // so there should be no buffer overrun; also the IF_TRUE_ACTION_JUMP above also help to avoid buffer overrun. +#pragma warning(disable:6386) + RtlCopyMemory((PVOID)_pFormat, (PVOID)m_pDeviceFormat, sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE)); +#pragma warning (pop) + ntStatus = STATUS_SUCCESS; + +Exit: + return ntStatus; +} +#pragma code_seg("PAGE") +/*----------------------------------------------------------------------------- +IMiniportAudioEngineNode::SetDeviceFormat + +Decscription: + + GetDeviceFormat set the current device format to be used by the HW Audio Engine + +Parameters: + + _In_ _ulNodeId: node id for the target audio engine node + _Out_ pFormat: a buffer pointer with the device format to be set to the hw Audio Engine + _In_ ulBufferSize: a pointer to a ULONG variable that has the size of the buffer pointed by pFormat + +Return Value: + + Appropriate NTSTATUS code + +Called at PASSIVE_LEVEL + +Remarks + Setting the device format of a HW Audio Engine could potential impact the mix format inside the HD Audio Engine. +The driver might need to add appropriate src/format converter according or change mix format. + +-------------------------------------------------------------------------------------------------------------------------*/ +STDMETHODIMP_(NTSTATUS) CMiniportWaveRT::SetDeviceFormat(_In_ ULONG _ulNodeId, _In_ KSDATAFORMAT_WAVEFORMATEX *_pFormat, _In_ ULONG _ulBufferSize) +{ + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + PAGED_CODE (); + + ASSERT (_pFormat); + + DPF_ENTER(("[CMiniportWaveRT::SetDeviceFormat]")); + IF_TRUE_ACTION_JUMP(_ulNodeId != KSNODE_WAVE_AUDIO_ENGINE, ntStatus = STATUS_INVALID_DEVICE_REQUEST, Exit); + IF_TRUE_ACTION_JUMP(_ulBufferSize < sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), ntStatus = STATUS_BUFFER_TOO_SMALL, Exit); + + RtlCopyMemory((PVOID)m_pDeviceFormat, (PVOID)_pFormat, sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE)); + ntStatus = STATUS_SUCCESS; + +Exit: + return ntStatus; +} +#pragma code_seg("PAGE") +/*----------------------------------------------------------------------------- +IMiniportAudioEngineNode::GetSupportedDeviceFormats + +Decscription: + + GetSupportedDeviceFormats get the complete format list supported by the hw Audio Engine + +Parameters: + + _In_ _ulNodeId: node id for the target audio engine node + _Out_ pFormat: a buffer pointer for receiving the supported device formats + _In_ ulBufferSize: a pointer to a ULONG variable that has the size of the buffer pointed by pFormat + +Return Value: + + Appropriate NTSTATUS code + +Called at PASSIVE_LEVEL + +Remarks + +-------------------------------------------------------------------------------------------------------------------------*/ +STDMETHODIMP_(NTSTATUS) CMiniportWaveRT::GetSupportedDeviceFormats(_In_ ULONG _ulNodeId, _Out_ KSMULTIPLE_ITEM* _pFormat, _In_ ULONG _ulBufferSize) +{ + PKSDATAFORMAT_WAVEFORMATEXTENSIBLE pDeviceFormats; + ULONG cDeviceFormats; + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + PAGED_CODE (); + + ASSERT(_pFormat); + + DPF_ENTER(("[CMiniportWaveRT::GetSupportedDeviceFormats]")); + IF_TRUE_ACTION_JUMP(_ulNodeId != KSNODE_WAVE_AUDIO_ENGINE, ntStatus = STATUS_INVALID_DEVICE_REQUEST, Exit); + + cDeviceFormats = GetAudioEngineSupportedDeviceFormats(&pDeviceFormats); + + KSMULTIPLE_ITEM *pKsMulti = static_cast(_pFormat); + pKsMulti->Size = sizeof(KSMULTIPLE_ITEM) + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE) * cDeviceFormats; + pKsMulti->Count = cDeviceFormats; + + IF_TRUE_ACTION_JUMP(_ulBufferSize < pKsMulti->Size, ntStatus = STATUS_BUFFER_TOO_SMALL, Exit); + + RtlCopyMemory((PVOID)(pKsMulti + 1), pDeviceFormats, + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE) * cDeviceFormats); + + ntStatus = STATUS_SUCCESS; + +Exit: + return ntStatus; +} +/*----------------------------------------------------------------------------- +IMiniportAudioEngineNode::GetDeviceChannelCount + +Decscription: + + When handling volume, mute, and meter related KS properties, Portcls calls + this method, inside its property handlers, to know the number of channels for the + corresponding KS property. + +Parameters: + + _In_ _ulNodeId: node id for the target audio engine node + _In_ _targetType: the query target (volume. mute, or peak meter) + _Out_ _pulChannelCount: a pointer to a UINT32 variable for receiving returned channel count information + +Return Value: + + Appropriate NTSTATUS code + +Called at PASSIVE_LEVEL + +Remarks + +-------------------------------------------------------------------------------------------------------------------------*/ +#pragma code_seg("PAGE") +NTSTATUS CMiniportWaveRT::GetDeviceChannelCount +( + _In_ ULONG _ulNodeId, + _In_ eChannelTargetType _targetType, + _Out_ UINT32 *_pulChannelCount +) +{ + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + PAGED_CODE (); + ASSERT(_pulChannelCount); + + DPF_ENTER(("[CMiniportWaveRT::GetChannelCount]")); + + IF_TRUE_ACTION_JUMP(_ulNodeId != KSNODE_WAVE_AUDIO_ENGINE, ntStatus = STATUS_INVALID_DEVICE_REQUEST, Exit); + + switch (_targetType) + { + case eVolumeAttribute: + ntStatus = GetVolumeChannelCount(_pulChannelCount); + break; + case eMuteAttribute: + ntStatus = GetMuteChannelCount(_pulChannelCount); + break; + case ePeakMeterAttribute: + ntStatus = GetPeakMeterChannelCount(_pulChannelCount); + break; + default: + ntStatus = STATUS_INVALID_DEVICE_REQUEST; + break; + } +Exit: + return ntStatus; +} +#pragma code_seg("PAGE") +/*----------------------------------------------------------------------------- +IMiniportAudioEngineNode::GetDeviceAttributeSteppings + +Decscription: + + When handling volume, mute, and meter related KS properties, Portcls calls + this method, inside its property handlers, to know the property stepping information for the + corresponding KS property. + +Parameters: + + _In_ _ulNodeId: node id for the target audio engine node + _In_ _targetType: the query target (volume. mute, or peak meter) + _Out_ _pKsPropMembHead: a pointer to a PKSPROPERTY_STEPPING_LONG variable for receiving returned channel count information + _In_ ulBufferSize: a pointer to a ULONG variable that has the size of the buffer pointed by _pKsPropMembHead + +Return Value: + + Appropriate NTSTATUS code + +Called at PASSIVE_LEVEL + +Remarks + +-------------------------------------------------------------------------------------------------------------------------*/ +NTSTATUS CMiniportWaveRT::GetDeviceAttributeSteppings(_In_ ULONG _ulNodeId, _In_ eChannelTargetType _targetType, _Out_ PKSPROPERTY_STEPPING_LONG _pKsPropMembHead, _In_ UINT32 _ui32DataSize) +{ + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + PAGED_CODE (); + + DPF_ENTER(("[CMiniportWaveRT::GetDeviceAttributeSteppings]")); + IF_TRUE_ACTION_JUMP(_ulNodeId != KSNODE_WAVE_AUDIO_ENGINE, ntStatus = STATUS_INVALID_DEVICE_REQUEST, Exit); + + switch (_targetType) + { + case eVolumeAttribute: + ntStatus = GetVolumeSteppings(_pKsPropMembHead, _ui32DataSize);; + break; + case eMuteAttribute: + ntStatus = GetMuteSteppings(_pKsPropMembHead, _ui32DataSize);; + break; + case ePeakMeterAttribute: + ntStatus = GetPeakMeterSteppings(_pKsPropMembHead, _ui32DataSize);; + break; + default: + ntStatus = STATUS_INVALID_DEVICE_REQUEST; + break; + } +Exit: + return ntStatus; +} +#pragma code_seg("PAGE") +/*----------------------------------------------------------------------------- +IMiniportAudioEngineNode::GetDeviceChannelVolume + +Decscription: + + When handling GET volume KS property for the device, Portcls calls + this method, inside its property handlers, to get the current setting on the specific channel. + +Parameters: + + _In_ _ulNodeId: node id for the target audio engine node + _In_ _uiChannel: the target channel for this GET volume operation + _Out_ _pVolume: a pointer to a LONG variable for receiving returned information + +Return Value: + + Appropriate NTSTATUS code + +Called at PASSIVE_LEVEL + +Remarks + +-------------------------------------------------------------------------------------------------------------------------*/ +STDMETHODIMP_(NTSTATUS) CMiniportWaveRT::GetDeviceChannelVolume(_In_ ULONG _ulNodeId, _In_ UINT32 _uiChannel, _Out_ LONG *_pVolume) +{ + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + PAGED_CODE (); + + DPF_ENTER(("[CMiniportWaveRT::GetDeviceChannelVolume]")); + IF_TRUE_ACTION_JUMP(_ulNodeId != KSNODE_WAVE_AUDIO_ENGINE, ntStatus = STATUS_INVALID_DEVICE_REQUEST, Exit); + + ntStatus = GetChannelVolume(_uiChannel, _pVolume); + +Exit: + return ntStatus; +} + +#pragma code_seg("PAGE") +/*----------------------------------------------------------------------------- +IMiniportAudioEngineNode::SetDeviceChannelVolume + +Decscription: + + When handling SET volume KS property for the device, Portcls calls + this method, inside its property handlers, to set the current setting on the specific channel. + +Parameters: + + _In_ _ulNodeId: node id for the target audio engine node + _In_ _uiChannel: the target channel for this GET volume operation + _In_ _Volume: volume value to set + +Return Value: + + Appropriate NTSTATUS code + +Called at PASSIVE_LEVEL + +Remarks + +-------------------------------------------------------------------------------------------------------------------------*/ +STDMETHODIMP_(NTSTATUS) CMiniportWaveRT::SetDeviceChannelVolume(_In_ ULONG _ulNodeId, _In_ UINT32 _uiChannel, _In_ LONG _Volume) +{ + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + PAGED_CODE (); + + DPF_ENTER(("[CMiniportWaveRT::SetEndpointChannelVolume]")); + IF_TRUE_ACTION_JUMP(_ulNodeId != KSNODE_WAVE_AUDIO_ENGINE, ntStatus = STATUS_INVALID_DEVICE_REQUEST, Exit); + + // Snap the volume level to our range of steppings. + LONG lVolume = VOLUME_NORMALIZE_IN_RANGE(_Volume); + + ntStatus = SetChannelVolume(_uiChannel, lVolume); + +Exit: + return ntStatus; +} + +#pragma code_seg("PAGE") +/*----------------------------------------------------------------------------- +IMiniportAudioEngineNode::GetDeviceChannelPeakMeter + +Decscription: + + When handling GET peak meter KS property for the device, Portcls calls + this method, inside its property handlers, to get the current setting on the specific channel. + +Parameters: + + _In_ _ulNodeId: node id for the target audio engine node + _In_ _uiChannel: the target channel for this GET volume operation + _Out_ _pPeakMeterValue: a pointer to a LONG variable for receiving returned information + +Return Value: + + Appropriate NTSTATUS code + +Called at PASSIVE_LEVEL + +Remarks + +-------------------------------------------------------------------------------------------------------------------------*/ +STDMETHODIMP_(NTSTATUS) CMiniportWaveRT::GetDeviceChannelPeakMeter(_In_ ULONG _ulNodeId, _In_ UINT32 _uiChannel, _Out_ LONG *_pPeakMeterValue) +{ + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + PAGED_CODE (); + + DPF_ENTER(("[CMiniportWaveRT::GetDeviceChannelPeakMeter]")); + IF_TRUE_ACTION_JUMP(_ulNodeId != KSNODE_WAVE_AUDIO_ENGINE, ntStatus = STATUS_INVALID_DEVICE_REQUEST, Exit); + ntStatus = GetChannelPeakMeter(_uiChannel, _pPeakMeterValue); + +Exit: + return ntStatus; +} +/*----------------------------------------------------------------------------- +IMiniportAudioEngineNode::GetDeviceChannelMute + +Decscription: + + When handling GET mute KS property for the device, Portcls calls + this method, inside its property handlers, to get the current setting on the specific channel. + +Parameters: + + _In_ _ulNodeId: node id for the target audio engine node + _In_ _uiChannel: the target channel for this GET volume operation + _Out_ _pbMute: a pointer to a BOOL variable for receiving returned information + +Return Value: + + Appropriate NTSTATUS code + +Called at PASSIVE_LEVEL + +Remarks + +-------------------------------------------------------------------------------------------------------------------------*/ +#pragma code_seg("PAGE") +STDMETHODIMP_(NTSTATUS) CMiniportWaveRT::GetDeviceChannelMute(_In_ ULONG _ulNodeId, _In_ UINT32 _uiChannel, _Out_ BOOL *_pbMute) +{ + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + PAGED_CODE (); + + DPF_ENTER(("[CMiniportWaveRT::GetEndpointChannelMute]")); + IF_TRUE_ACTION_JUMP(_ulNodeId != KSNODE_WAVE_AUDIO_ENGINE, ntStatus = STATUS_INVALID_DEVICE_REQUEST, Exit); + + ntStatus = GetChannelMute(_uiChannel, _pbMute); +Exit: + return ntStatus; +} + +#pragma code_seg("PAGE") +/*----------------------------------------------------------------------------- +IMiniportAudioEngineNode::SetDeviceChannelMute + +Decscription: + + When handling SET mute KS property for the device, Portcls calls + this method, inside its property handlers, to set the current setting on the specific channel. + +Parameters: + + _In_ _ulNodeId: node id for the target audio engine node + _In_ _uiChannel: the target channel for this GET volume operation + _In_ _bMute: volume value to set + +Return Value: + + Appropriate NTSTATUS code + +Called at PASSIVE_LEVEL + +Remarks + +-------------------------------------------------------------------------------------------------------------------------*/ +NTSTATUS CMiniportWaveRT::SetDeviceChannelMute(_In_ ULONG _ulNodeId, _In_ UINT32 _uiChannel, _In_ BOOL _bMute) +{ + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + PAGED_CODE (); + + DPF_ENTER(("[CMiniportWaveRT::SetEndpointChannelMute]")); + IF_TRUE_ACTION_JUMP(_ulNodeId != KSNODE_WAVE_AUDIO_ENGINE, ntStatus = STATUS_INVALID_DEVICE_REQUEST, Exit); + + ntStatus = SetChannelMute(_uiChannel, _bMute); + +Exit: + return ntStatus; +} + + +#pragma code_seg("PAGE") +/*----------------------------------------------------------------------------- +IMiniportAudioEngineNode::GetBufferSizeRange + +Decscription: + + Portcls calls this method, inside its property handlers, to get a buffer size capabilities for + a specifc format requested at the moment this call was made. + +Parameters: + + _In_ ULONG _ulNodeId: the node is for the target audio engine + _In_ KSDATAFORMAT_WAVEFORMATEX *_pKsDataFormatWfx: the requested format + _Out_ KSAUDIOENGINE_BUFFER_SIZE_RANGE *_pBufferSizeRange: + +Return Value: + + Appropriate NTSTATUS code + +Called at PASSIVE_LEVEL + +Remarks + + The purpose of this call is to give an audio client (such as MF SAR or direct WASAPI clients) an idea on the supported buffer size range - + almost equivalent to the hardware's buffer capability. An application pick any size within that range that it determines it would best serve + its purpose (communication apps would like small buffer size for low latency while other media app might choose large buffer for power saving advantage). + +-------------------------------------------------------------------------------------------------------------------------*/ +NTSTATUS CMiniportWaveRT::GetBufferSizeRange(_In_ ULONG _ulNodeId, _In_ KSDATAFORMAT_WAVEFORMATEX *_pKsDataFormatWfx, _Out_ KSAUDIOENGINE_BUFFER_SIZE_RANGE *_pBufferSizeRange) +{ + NTSTATUS ntStatus = STATUS_SUCCESS; + + PAGED_CODE (); + UNREFERENCED_PARAMETER(_ulNodeId); + ASSERT(_pBufferSizeRange); + ASSERT(_pKsDataFormatWfx); + DPF_ENTER(("[CMiniportWaveRTStream::GetStreamBufferSizeRange]")); + + _pBufferSizeRange->MinBufferBytes = (_pKsDataFormatWfx->WaveFormatEx.nAvgBytesPerSec * MIN_BUFFER_DURATION_MS) / MS_PER_SEC; + _pBufferSizeRange->MaxBufferBytes = (_pKsDataFormatWfx->WaveFormatEx.nAvgBytesPerSec * MAX_BUFFER_DURATION_MS) / MS_PER_SEC; + + return ntStatus; +} + +//------------------------------------------------------------------------------------------------------------------------ +//CMiniportWaveRT private supporting functions +//------------------------------------------------------------------------------------------------------------------------ +#pragma code_seg("PAGE") +NTSTATUS CMiniportWaveRT::GetVolumeChannelCount(_Out_ UINT32 *_pulChannelCount) +{ + NTSTATUS ntStatus = STATUS_SUCCESS; + PAGED_CODE (); + ASSERT (_pulChannelCount); + + DPF_ENTER(("[CMiniportWaveRT::GetVolumeChannelCount]")); + *_pulChannelCount = m_DeviceMaxChannels; + return ntStatus; +} +#pragma code_seg("PAGE") +NTSTATUS CMiniportWaveRT::GetVolumeSteppings(_Out_writes_bytes_(_ui32DataSize) PKSPROPERTY_STEPPING_LONG _pKsPropStepLong, _In_ UINT32 _ui32DataSize) +{ + PAGED_CODE (); + UINT32 ulChannelCount = _ui32DataSize / sizeof(KSPROPERTY_STEPPING_LONG); + ASSERT (_pKsPropStepLong); + DPF_ENTER(("[CMiniportWaveRT::GetVolumeSteppings]")); + + ASSERT(ulChannelCount <= m_DeviceMaxChannels); + if (ulChannelCount > m_DeviceMaxChannels) + { + return STATUS_INVALID_PARAMETER; + } + + for(UINT i = 0; i < ulChannelCount; i++) + { + _pKsPropStepLong[i].SteppingDelta = VOLUME_STEPPING_DELTA; + _pKsPropStepLong[i].Bounds.SignedMaximum = VOLUME_SIGNED_MAXIMUM; + _pKsPropStepLong[i].Bounds.SignedMinimum = VOLUME_SIGNED_MINIMUM; + } + return STATUS_SUCCESS; +} +#pragma code_seg("PAGE") +NTSTATUS CMiniportWaveRT::GetChannelVolume(_In_ UINT32 _uiChannel, _Out_ LONG *_pVolume) +{ + PAGED_CODE (); + ASSERT (_pVolume); + DPF_ENTER(("[CMiniportWaveRT::GetChannelVolume]")); + + if (_uiChannel == ALL_CHANNELS_ID) + { + *_pVolume = m_plVolumeLevel[0]; + } + else + { + ASSERT(_uiChannel <= m_DeviceMaxChannels); + *_pVolume = m_plVolumeLevel[_uiChannel]; + } + return STATUS_SUCCESS; +} +#pragma code_seg("PAGE") +NTSTATUS CMiniportWaveRT::SetChannelVolume(_In_ UINT32 _uiChannel, _In_ LONG _Volume) +{ + PAGED_CODE (); + DPF_ENTER(("[CMiniportWaveRT::SetChannelVolume]")); + + if (_uiChannel == ALL_CHANNELS_ID) + { + for (int i=0; i m_DeviceMaxChannels) + { + return STATUS_INVALID_PARAMETER; + } + + for(UINT i = 0; i < ulChannelCount; i++) + { + _pKsPropStepLong[i].SteppingDelta = PEAKMETER_STEPPING_DELTA; + _pKsPropStepLong[i].Bounds.SignedMaximum = PEAKMETER_SIGNED_MAXIMUM; + _pKsPropStepLong[i].Bounds.SignedMinimum = PEAKMETER_SIGNED_MINIMUM; + } + return STATUS_SUCCESS; +} +#pragma code_seg("PAGE") +NTSTATUS CMiniportWaveRT::GetChannelPeakMeter(_In_ UINT32 _uiChannel, _Out_ LONG *_plPeakMeter) +{ + PAGED_CODE (); + ASSERT (_plPeakMeter); + DPF_ENTER(("[CMiniportWaveRT::GetChannelPeakMeter]")); + + ASSERT(_uiChannel < m_DeviceMaxChannels); + + if (_uiChannel >= m_DeviceMaxChannels) + { + return STATUS_INVALID_PARAMETER; + } + + if (m_ulSystemAllocated + m_ulOffloadAllocated > 0) + { + *_plPeakMeter = PEAKMETER_NORMALIZE_IN_RANGE(PEAKMETER_SIGNED_MAXIMUM / 2); + } + else + { + *_plPeakMeter = 0; + } + //*_plPeakMeter = m_plPeakMeter[lChannel]; + + return STATUS_SUCCESS; +} +// mute +#pragma code_seg("PAGE") +NTSTATUS CMiniportWaveRT::GetMuteChannelCount(_Out_ UINT32 *_pulChannelCount) +{ + NTSTATUS ntStatus = STATUS_SUCCESS; + PAGED_CODE (); + ASSERT (_pulChannelCount); + + DPF_ENTER(("[CMiniportWaveRT::GetMuteChannelCount]")); + *_pulChannelCount = m_DeviceMaxChannels; + return ntStatus; +} +#pragma code_seg("PAGE") +NTSTATUS CMiniportWaveRT::GetMuteSteppings(_Out_writes_bytes_(_ui32DataSize) PKSPROPERTY_STEPPING_LONG _pKsPropStepLong, _In_ UINT32 _ui32DataSize) +{ + PAGED_CODE (); + UINT32 ulChannelCount = _ui32DataSize / sizeof(KSPROPERTY_STEPPING_LONG); + + ASSERT (_pKsPropStepLong); + DPF_ENTER(("[CMiniportWaveRT::GetMuteSteppings]")); + + ASSERT(ulChannelCount <= m_DeviceMaxChannels); + + if (ulChannelCount > m_DeviceMaxChannels) + { + return STATUS_INVALID_PARAMETER; + } + + for(UINT i = 0; i < ulChannelCount; i++) + { + _pKsPropStepLong[i].SteppingDelta = 1; + _pKsPropStepLong[i].Bounds.SignedMaximum = TRUE; + _pKsPropStepLong[i].Bounds.SignedMinimum = FALSE; + } + return STATUS_SUCCESS; +} +#pragma code_seg("PAGE") +NTSTATUS CMiniportWaveRT::GetChannelMute(_In_ UINT32 _uiChannel, _Out_ BOOL *_pbMute) +{ + PAGED_CODE (); + ASSERT (_pbMute); + DPF_ENTER(("[CMiniportWaveRT::GetChannelMute]")); + + + if (_uiChannel == ALL_CHANNELS_ID) + { + *_pbMute = m_pbMuted[0]; + } + else + { + ASSERT(_uiChannel <= m_DeviceMaxChannels); + *_pbMute = m_pbMuted[_uiChannel]; + } + + return STATUS_SUCCESS; +} +#pragma code_seg("PAGE") +NTSTATUS CMiniportWaveRT::SetChannelMute(_In_ UINT32 _uiChannel, _In_ BOOL _bMute) +{ + PAGED_CODE (); + DPF_ENTER(("[CMiniportWaveRT::SetChannelVolume]")); + + if (_uiChannel == ALL_CHANNELS_ID) + { + for (int i=0; iSetLoopbackProtection(ulProtectionOption); + if (!NT_SUCCESS(ntStatus)) + { + break; + } + } + } + + if (NT_SUCCESS(ntStatus)) + { + m_LoopbackProtection = ulProtectionOption; + } + else + { + // + // Something went wrong, restore old protection setting. + // + for (ULONG i = 0; i < MAX_OUTPUT_LOOPBACK_STREAMS; i++) + { + if (m_LoopbackStreams[i]) + { + (void)m_LoopbackStreams[i]->SetLoopbackProtection(m_LoopbackProtection); + } + } + } + +Done: + return ntStatus; +} + + diff --git a/audio/sysvad/EndpointsCommon/MiniportStreamAudioEngineNode.cpp b/audio/sysvad/EndpointsCommon/MiniportStreamAudioEngineNode.cpp new file mode 100644 index 000000000..3c775d01e --- /dev/null +++ b/audio/sysvad/EndpointsCommon/MiniportStreamAudioEngineNode.cpp @@ -0,0 +1,971 @@ +/*++ + +Module Name: + + MiniportStreamAudioEngineNode.cpp + +Abstract: + + Implementation of IMiniportstreamAudioEngineNode interface for wavert steam. + +--*/ + +#include +#include +#include +#include "simple.h" +#include "minwavert.h" +#include "minwavertstream.h" +#include "UnittestData.h" +#define MINWAVERTSTREAM_POOLTAG 'SRWM' +#define HNSTIME_PER_MILLISECOND 10000 + +#pragma warning (disable : 4127) + +#pragma code_seg("PAGE") +/*----------------------------------------------------------------------------- +IMiniportStreamAudioEngineNode::GetLfxState + +Description: + + Portcls calls this method to get a offload stream's Lfx state + +Parameters + + _Out_ pbEnable: a pointer to a BOOL value for receieving the returned LFX state + +Return Value: + + Appropriate NTSTATUS code + +Called at PASSIVE_LEVEL + +Remarks + The Lfx operations (on the offload stream) inside HW Audio Engine (such as src, dsp, and other special effects) are hidden from the software audio stack. +So, the driver should return TRUE if any one of the effects is on and returns FALSE when all the opertations are off. +-------------------------------------------------------------------------------------------------------------------------*/ +NTSTATUS CMiniportWaveRTStream::GetLfxState(_Out_ BOOL *_pbEnable) +{ + PAGED_CODE (); + + DPF_ENTER(("[CMiniportWaveRTStream::GetLfxState]")); + + *_pbEnable = m_bLfxEnabled; + return STATUS_SUCCESS; +} +#pragma code_seg("PAGE") +/*----------------------------------------------------------------------------- +IMiniportStreamAudioEngineNode::SetLfxState + +Decscription: + + Portcls calls this method to set a offload stream's Lfx state + +Parameters: + + _In_ bEnable: a BOOL value. TRU: to enable Lfx, FALSE: to disable Lfx + +Return Value: + + Appropriate NTSTATUS code + +Called at PASSIVE_LEVEL + +Remarks + The local operations (on the offload stream) inside HW Audio Engine (such as src, dsp, and other special effects) are hidden from the software audio stack. +So, when handling disabling Lfx, the driver should ALL the effects. When enabling Lfx, it's up the driver+HW Audio Engine to decide what opertations to turn on +when they see appropriate. +-------------------------------------------------------------------------------------------------------------------------*/ +NTSTATUS CMiniportWaveRTStream::SetLfxState(_In_ BOOL _bEnable) +{ + PAGED_CODE (); + + DPF_ENTER(("[CMiniportWaveRTStream::SetGfxState]")); + + UNREFERENCED_PARAMETER(_bEnable); + m_bLfxEnabled = _bEnable; + return STATUS_SUCCESS; +} +#pragma code_seg("PAGE") +/*----------------------------------------------------------------------------- +IMiniportStreamAudioEngineNode::GetStreamChannelVolume + +Decscription: + + When handling GET volume KS property for the device, Portcls calls + this method, inside its property handlers, to get the current setting on the specific channel. + +Parameters: + + _In_ _uiChannel: the target channel for this GET volume operation + _Out_ _pVolume: a pointer to a LONG variable for receiving returned information + +Return Value: + + Appropriate NTSTATUS code + +Called at PASSIVE_LEVEL + +Remarks + +-------------------------------------------------------------------------------------------------------------------------*/ +NTSTATUS CMiniportWaveRTStream::GetStreamChannelVolume(_In_ UINT32 _uiChannel, _Out_ LONG *_pVolume) +{ + PAGED_CODE (); + + DPF_ENTER(("[CMiniportWaveRTStream::GetStreamChannelVolume]")); + + *_pVolume = m_plVolumeLevel[_uiChannel]; + + return STATUS_SUCCESS; +} +#pragma code_seg("PAGE") +/*----------------------------------------------------------------------------- +IMiniportStreamAudioEngineNode::GetStreamChannelMute + +Decscription: + + When handling GET mute KS property for the device, Portcls calls + this method, inside its property handlers, to get the current setting on the specific channel. + +Parameters: + + _In_ _uiChannel: the target channel for this GET volume operation + _Out_ _pbMute: a pointer to a BOOL variable for receiving returned information + +Return Value: + + Appropriate NTSTATUS code + +Called at PASSIVE_LEVEL + +Remarks + +-------------------------------------------------------------------------------------------------------------------------*/ +STDMETHODIMP_(NTSTATUS) CMiniportWaveRTStream::GetStreamChannelMute(_In_ UINT32 _uiChannel, _Out_ BOOL *_pbMute) +{ + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + PAGED_CODE (); + ASSERT (_pbMute); + + DPF_ENTER(("[CMiniportWaveRTStream::GetStreamChannelMute]")); + ntStatus = GetChannelMute(_uiChannel, _pbMute); + + return ntStatus; +} +#pragma code_seg("PAGE") +/*----------------------------------------------------------------------------- +IMiniportStreamAudioEngineNode::GetStreamAttributeSteppings + +Decscription: + + When handling volume, mute, and meter related KS properties, Portcls calls + this method, inside its property handlers, to know the property stepping information for the + corresponding KS property. + +Parameters: + + _In_ _targetType: the query target (volume. mute, or peak meter) + _Out_ _pKsPropMembHead: a pointer to a PKSPROPERTY_STEPPING_LONG variable for receiving returned channel count information + _In_ ulBufferSize: a pointer to a ULONG variable that has the size of the buffer pointed by _pKsPropMembHead + +Return Value: + + Appropriate NTSTATUS code + +Called at PASSIVE_LEVEL + +Remarks + +-------------------------------------------------------------------------------------------------------------------------*/ +NTSTATUS CMiniportWaveRTStream::GetStreamAttributeSteppings(_In_ eChannelTargetType _targetType, _Out_ PKSPROPERTY_STEPPING_LONG _pKsPropMembHead, _In_ UINT32 _ui32DataSize) +{ + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + PAGED_CODE (); + + DPF_ENTER(("[CMiniportWaveRTStream::GetDeviceAttributeSteppings]")); + + switch (_targetType) + { + case eVolumeAttribute: + ntStatus = GetVolumeSteppings(_pKsPropMembHead, _ui32DataSize);; + break; + case eMuteAttribute: + ntStatus = GetMuteSteppings(_pKsPropMembHead, _ui32DataSize);; + break; + case ePeakMeterAttribute: + ntStatus = GetPeakMeterSteppings(_pKsPropMembHead, _ui32DataSize);; + break; + default: + ntStatus = STATUS_INVALID_DEVICE_REQUEST; + break; + } + return ntStatus; + +} +#pragma code_seg("PAGE") +/*----------------------------------------------------------------------------- +IMiniportStreamAudioEngineNode::SetStreamChannelVolume + +Decscription: + + When handling SET volume KS property for the device, Portcls calls + this method, inside its property handlers, to set the current setting on the specific channel. + +Parameters: + + _In_ Channel: the target channel for this GET volume operation + _In_ TargetVolume: volume value to set + _In_ CurveType: type of curve to apply to the ramp + _In_ CurveDuration: amount of time in hns over which to ramp the volume + +Return Value: + + Appropriate NTSTATUS code + +Called at PASSIVE_LEVEL + +Remarks + +-------------------------------------------------------------------------------------------------------------------------*/ +STDMETHODIMP_(NTSTATUS) CMiniportWaveRTStream::SetStreamChannelVolume +( + _In_ UINT32 Channel, + _In_ LONG TargetVolume, + _In_ AUDIO_CURVE_TYPE CurveType, + _In_ ULONGLONG CurveDuration +) +{ + UNREFERENCED_PARAMETER(CurveType); + UNREFERENCED_PARAMETER(CurveDuration); + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + PAGED_CODE (); + + DPF_ENTER(("[CMiniportWaveRTStream::SetStreamChannelVolume]")); + + // Snap the volume level to our range of steppings. + LONG lVolume = VOLUME_NORMALIZE_IN_RANGE(TargetVolume); + + // If Channel is ALL_CHANNELS_ID, then set the level on all channels + if ( ALL_CHANNELS_ID == Channel ) + { + for (UINT32 i = 0; i < m_pWfExt->Format.nChannels; i++) + { + ntStatus = SetChannelVolume(i, lVolume); + } + } + else + { + ntStatus = SetChannelVolume(Channel, lVolume); + } + + return ntStatus; +} + +#pragma code_seg("PAGE") +/*----------------------------------------------------------------------------- +IMiniportStreamAudioEngineNode::GetStreamChannelPeakMeter + +Decscription: + + When handling GET peak meter KS property for the device, Portcls calls + this method, inside its property handlers, to get the current setting on the specific channel. + +Parameters: + + _In_ _uiChannel: the target channel for this GET peak meter operation + _Out_ _pPeakMeterValue: a pointer to a LONG variable for receiving returned information + +Return Value: + + Appropriate NTSTATUS code + +Called at PASSIVE_LEVEL + +Remarks + +-------------------------------------------------------------------------------------------------------------------------*/ +STDMETHODIMP_(NTSTATUS) CMiniportWaveRTStream::GetStreamChannelPeakMeter(_In_ UINT32 _uiChannel, _Out_ LONG *_pPeakMeterValue) +{ + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + PAGED_CODE (); + + DPF_ENTER(("[CMiniportWaveRTStream::GetStreamChannelPeakMeter]")); + + ntStatus = GetChannelPeakMeter(_uiChannel, _pPeakMeterValue); + + return ntStatus; +} + +#pragma code_seg("PAGE") +/*----------------------------------------------------------------------------- +IMiniportStreamAudioEngineNode::SetStreamChannelMute + +Decscription: + + When handling SET mute KS property for the device, Portcls calls + this method, inside its property handlers, to set the current setting on the specific channel. + +Parameters: + + _In_ _uiChannel: the target channel for this Set mute operation + _In_ _bMute: mute value to set + +Return Value: + + Appropriate NTSTATUS code + +Called at PASSIVE_LEVEL + +Remarks + +-------------------------------------------------------------------------------------------------------------------------*/ +NTSTATUS CMiniportWaveRTStream::SetStreamChannelMute(_In_ UINT32 _uiChannel, _In_ BOOL _bMute) +{ + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + PAGED_CODE (); + + DPF_ENTER(("[CMiniportWaveRTStream::SetStreamChannelMute]")); + + // If Channel is ALL_CHANNELS_ID, then set the mute info on all channels + if ( ALL_CHANNELS_ID == _uiChannel ) + { + for (UINT32 i = 0; i < m_pWfExt->Format.nChannels; i++) + { + ntStatus = SetChannelMute(i, _bMute); + } + } + else + { + ntStatus = SetChannelMute(_uiChannel, _bMute); + } + + return ntStatus; +} +//presentation +#pragma code_seg("PAGE") +/*----------------------------------------------------------------------------- +IMiniportStreamAudioEngineNode::GetStreamPresentationPosition + +Decscription: + + Portcls calls this method, inside its property handlers, to get a stream's presentation + postion. + +Parameters: + + _Out_ pPresentationPosition: a pointer to a KSAUDIO_PRESENTATION_POSITION variable for receiving returned information + +Return Value: + + Appropriate NTSTATUS code + +Called at PASSIVE_LEVEL + +Remarks + +-------------------------------------------------------------------------------------------------------------------------*/ +NTSTATUS CMiniportWaveRTStream::GetStreamPresentationPosition(_Out_ KSAUDIO_PRESENTATION_POSITION *_pPresentationPosition) +{ + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + PAGED_CODE (); + ASSERT(_pPresentationPosition); + DPF_ENTER(("[CMiniportWaveRTStream::GetStreamPresentationPosition]")); + + ntStatus = GetPresentationPosition(_pPresentationPosition); + + return ntStatus; +} +//StreamCurrentWritePosition +#pragma code_seg("PAGE") +/*----------------------------------------------------------------------------- +IMiniportStreamAudioEngineNode::SetStreamCurrentWritePosition + +Decscription: + + Portcls calls this method, inside its property handlers, to set a stream's write position + postion. + +Parameters: + + _In_ ulCurrentWritePosition: a position value indicating the last valid byte in the buffer + +Return Value: + + Appropriate NTSTATUS code + +Called at PASSIVE_LEVEL + +Remarks + +_ulCurrentWritePosition specifies the current write position in bytes, which an offload driver can use to know how much valid data in the WaveRT buffer. +Let say, BufferByteSize is the WaveRT buffer size, the valid value range for _ulCurrentWritePosition be 0... BufferByteSize (inclusive) + +0: no valid data in the buffer +n: the last valid data byte is at the offset n from the beginning of the buffer. If this is a value smaller than a previous write position, +it means a wrap-around has happened, in the case, the driver would need to take that in to consider when calculating how many bytes are valid for fetching. +BufferByteSize: the last valid data byte is at the end of the buffer + +After a pin is instantiated and before any KSPROPERTY_AUDIO_WAVERT_CURRENT_WRITE_POSITION is received, a driver should assume its current write position is zero. + +-------------------------------------------------------------------------------------------------------------------------*/ +NTSTATUS CMiniportWaveRTStream::SetStreamCurrentWritePosition(_In_ ULONG _ulCurrentWritePosition) +{ + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + PAGED_CODE (); + DPF_ENTER(("[CMiniportWaveRTStream::SetStreamCurrentWritePosition]")); + + ntStatus = SetCurrentWritePosition(_ulCurrentWritePosition); + + return ntStatus; +} +#pragma code_seg("PAGE") +/*----------------------------------------------------------------------------- +IMiniportStreamAudioEngineNode::GetStreamLinearBufferPosition + +Decscription: + + Portcls calls this method, inside its property handlers, to set a stream's write position + postion. + +Parameters: + + _Out_ pullLinearBufferPosition: a pointer to a ULONGLONG variable for receiving returned information + + +Return Value: + + Appropriate NTSTATUS code + +Called at PASSIVE_LEVEL + +Remarks +The returned value is the number of bytes that the DMA has fetched from the audio buffer since the beginning of the stream +-------------------------------------------------------------------------------------------------------------------------*/ +NTSTATUS CMiniportWaveRTStream::GetStreamLinearBufferPosition(_Out_ ULONGLONG *_pullLinearBufferPosition) +{ + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + PAGED_CODE (); + ASSERT(_pullLinearBufferPosition); + DPF_ENTER(("[CMiniportWaveRTStream::GetStreamLinearBufferPosition]")); + + ntStatus = GetLinearBufferPosition(_pullLinearBufferPosition, NULL); + + return ntStatus; +} +//SetStreamLoopbackProtection +#pragma code_seg("PAGE") +/*----------------------------------------------------------------------------- +IMiniportStreamAudioEngineNode::SetStreamLoopbackProtection + +Decscription: + + Portcls calls this method, inside its property handlers, to set the loopback + protection option + +Parameters: + _In_ protectionOption: protection option + CONSTRICTOR_OPTION_DISABLE: Turn protection off. + CONSTRICTOR_OPTION_MUTE: Mute the loopback stream +Return Value: + + Appropriate NTSTATUS code + +Called at PASSIVE_LEVEL + +Remarks + This content protection settings requestion could come in from the host process + pin or offload pin. The miniport needs to mute its loopback stream contents for + for this request from either host process pin or offload pin. +-------------------------------------------------------------------------------------------------------------------------*/ +NTSTATUS CMiniportWaveRTStream::SetStreamLoopbackProtection(_In_ CONSTRICTOR_OPTION protectionOption) +{ + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + PAGED_CODE (); + DPF_ENTER(("[CMiniportWaveRTStream::SetStreamLoopbackProtection]")); + + // + // Miniport driver mutes/unmutes the loopback here. + // + ntStatus = m_pMiniport->SetLoopbackProtection(protectionOption); + + return ntStatus; +} +#pragma code_seg("PAGE") +/*----------------------------------------------------------------------------- +IMiniportStreamAudioEngineNode::GetStreamChannelCount + +Decscription: + + When handling volume, mute, and meter related KS properties, Portcls calls + this method, inside its property handlers, to know the number of channels for the + corresponding KS property. + +Parameters: + + _In_ _targetType: the query target (volume, mute, or peak meter) + _Out_ _pulChannelCount: a pointer to a UINT32 variable for receiving returned channel count information + +Return Value: + + Appropriate NTSTATUS code + +Called at PASSIVE_LEVEL + +Remarks + +-------------------------------------------------------------------------------------------------------------------------*/ +NTSTATUS CMiniportWaveRTStream::GetStreamChannelCount(_In_ eChannelTargetType _targetType,_Out_ UINT32 *_pulChannelCount) +{ + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + PAGED_CODE (); + + DPF_ENTER(("[CMiniportWaveRTStream::GetStreamChannelCount]")); + + switch (_targetType) + { + case eVolumeAttribute: + ntStatus = GetVolumeChannelCount(_pulChannelCount); + break; + case eMuteAttribute: + ntStatus = GetMuteChannelCount(_pulChannelCount); + break; + case ePeakMeterAttribute: + ntStatus = GetPeakMeterChannelCount(_pulChannelCount); + break; + default: + ntStatus = STATUS_INVALID_DEVICE_REQUEST; + break; + } + + return ntStatus; +} + + +//------------------------------------------------------------------------------------------------------------------------ +//CMiniportWaveRTStream private supporting functions +//------------------------------------------------------------------------------------------------------------------------ +#pragma code_seg("PAGE") +NTSTATUS CMiniportWaveRTStream::GetVolumeChannelCount(_Out_ UINT32 *_puiChannelCount) +{ + PAGED_CODE(); + + DPF_ENTER(("[CMiniportWaveRTStream::GetVolumeChannelCount]")); + + ASSERT(_puiChannelCount); + ASSERT(m_pWfExt); + + NTSTATUS ntStatus = STATUS_SUCCESS; + *_puiChannelCount = m_pWfExt->Format.nChannels; + return ntStatus; +} + +#pragma code_seg("PAGE") +NTSTATUS CMiniportWaveRTStream::GetVolumeSteppings(_Out_writes_bytes_(_ui32DataSize) PKSPROPERTY_STEPPING_LONG _pKsPropStepLong, _In_ UINT32 _ui32DataSize) +{ + PAGED_CODE (); + UINT32 ulChannelCount = _ui32DataSize / sizeof(KSPROPERTY_STEPPING_LONG); + ASSERT (_pKsPropStepLong); + DPF_ENTER(("[CMiniportWaveRTStream::GetVolumeSteppings]")); + + if (ulChannelCount != m_pWfExt->Format.nChannels) + { + return STATUS_INVALID_PARAMETER; + } + + for(UINT i = 0; i < ulChannelCount; i++) + { + _pKsPropStepLong[i].SteppingDelta = VOLUME_STEPPING_DELTA; + _pKsPropStepLong[i].Bounds.SignedMaximum = VOLUME_SIGNED_MAXIMUM; + _pKsPropStepLong[i].Bounds.SignedMinimum = VOLUME_SIGNED_MINIMUM; + } + + return STATUS_SUCCESS; +} +#pragma code_seg("PAGE") +NTSTATUS CMiniportWaveRTStream::GetChannelVolume(_In_ UINT32 _uiChannel, _Out_ LONG *_pVolume) +{ + PAGED_CODE (); + ASSERT (_pVolume); + DPF_ENTER(("[CMiniportWaveRTStream::GetChannelVolume]")); + + *_pVolume = m_plVolumeLevel[_uiChannel]; + + return STATUS_SUCCESS; +} +#pragma code_seg("PAGE") +NTSTATUS CMiniportWaveRTStream::SetChannelVolume(_In_ UINT32 _uiChannel, _In_ LONG _Volume) +{ + PAGED_CODE (); + DPF_ENTER(("[CMiniportWaveRTStream::SetChannelVolume]")); + + m_plVolumeLevel[_uiChannel] = _Volume; + + return STATUS_SUCCESS; +} +///- metering +#pragma code_seg("PAGE") +NTSTATUS CMiniportWaveRTStream::GetPeakMeterChannelCount(_Out_ UINT32 *puiChannelCount) +{ + PAGED_CODE(); + + DPF_ENTER(("[CMiniportWaveRTStream::GetPeakMeterChannelCount]")); + + ASSERT(puiChannelCount); + ASSERT(m_pWfExt); + + NTSTATUS ntStatus = STATUS_SUCCESS; + *puiChannelCount = m_pWfExt->Format.nChannels; + return ntStatus; +} + +#pragma code_seg("PAGE") +NTSTATUS CMiniportWaveRTStream::GetPeakMeterSteppings(_Out_writes_bytes_(_ui32DataSize) PKSPROPERTY_STEPPING_LONG _pKsPropStepLong, _In_ UINT32 _ui32DataSize) +{ + PAGED_CODE (); + UINT32 ulChannelCount = _ui32DataSize / sizeof(KSPROPERTY_STEPPING_LONG); + + ASSERT (_pKsPropStepLong); + DPF_ENTER(("[CMiniportWaveRTStream::GetPeakMeterSteppings]")); + + if (ulChannelCount != m_pWfExt->Format.nChannels) + { + return STATUS_INVALID_PARAMETER; + } + + for(UINT i = 0; i < ulChannelCount; i++) + { + _pKsPropStepLong[i].SteppingDelta = PEAKMETER_STEPPING_DELTA; + _pKsPropStepLong[i].Bounds.SignedMaximum = PEAKMETER_SIGNED_MAXIMUM; + _pKsPropStepLong[i].Bounds.SignedMinimum = PEAKMETER_SIGNED_MINIMUM; + } + + return STATUS_SUCCESS; +} +#pragma code_seg("PAGE") +NTSTATUS CMiniportWaveRTStream::GetChannelPeakMeter(_In_ UINT32 _uiChannel, _Out_ LONG *_plPeakMeter) +{ + PAGED_CODE (); + ASSERT (_plPeakMeter); + UNREFERENCED_PARAMETER(_uiChannel); + DPF_ENTER(("[CMiniportWaveRTStream::GetChannelPeakMeter]")); + + *_plPeakMeter = PEAKMETER_NORMALIZE_IN_RANGE(PEAKMETER_SIGNED_MAXIMUM / 2); + + return STATUS_SUCCESS; +} + + //============================================================================= +// stream Mute +#pragma code_seg("PAGE") +NTSTATUS CMiniportWaveRTStream::GetMuteChannelCount(_Out_ UINT32 *puiChannelCount) +{ + PAGED_CODE(); + + DPF_ENTER(("[CMiniportWaveRTStream::GetMuteChannelCount]")); + + ASSERT(puiChannelCount); + ASSERT(m_pWfExt); + + NTSTATUS ntStatus = STATUS_SUCCESS; + *puiChannelCount = m_pWfExt->Format.nChannels; + return ntStatus; +} + +#pragma code_seg("PAGE") +NTSTATUS CMiniportWaveRTStream::GetMuteSteppings(_Out_writes_bytes_(_ui32DataSize) PKSPROPERTY_STEPPING_LONG _pKsPropStepLong, _In_ UINT32 _ui32DataSize) +{ + PAGED_CODE (); + UINT32 ulChannelCount = _ui32DataSize / sizeof(KSPROPERTY_STEPPING_LONG); + + ASSERT (_pKsPropStepLong); + DPF_ENTER(("[CMiniportWaveRTStream::GetMuteSteppings]")); + + if (ulChannelCount != m_pWfExt->Format.nChannels) + { + return STATUS_INVALID_PARAMETER; + } + + for(UINT i = 0; i < ulChannelCount; i++) + { + _pKsPropStepLong[i].SteppingDelta = 1; + _pKsPropStepLong[i].Bounds.SignedMaximum = TRUE; + _pKsPropStepLong[i].Bounds.SignedMinimum = FALSE; + } + + return STATUS_SUCCESS; +} +#pragma code_seg("PAGE") +NTSTATUS CMiniportWaveRTStream::GetChannelMute(_In_ UINT32 _uiChannel, _Out_ BOOL *_pbMute) +{ + PAGED_CODE (); + ASSERT (_pbMute); + DPF_ENTER(("[CMiniportWaveRTStream::GetChannelMute]")); + + *_pbMute = m_pbMuted[_uiChannel]; + return STATUS_SUCCESS; +} +#pragma code_seg("PAGE") +NTSTATUS CMiniportWaveRTStream::SetChannelMute(_In_ UINT32 _uiChannel, _In_ BOOL _bMute) +{ + PAGED_CODE (); + DPF_ENTER(("[CMiniportWaveRTStream::SetChannelMute]")); + + m_plVolumeLevel[_uiChannel] = _bMute; + + return STATUS_SUCCESS; +} +//presentation +#pragma code_seg("PAGE") +// +// UINT64 u64PositionInBlocks; // The block offset from the start of the stream to the current post-decoded uncompressed +// // position in the stream, where a block is the group of channels in the same sample; for a PCM stream, +// // a block is same as a frame. For compressed formats, a block is a single sample within a frame +// // (eg. each MP3 frame has 1152 samples or 1152 blocks) +// UINT64 u64QPCPosition; // The value of the performance counter at the time that the audio endpoint device read the device +// // position (*pu64Position) in response to the KSAUDIO_PRESENTATION_POSITION call. + +NTSTATUS CMiniportWaveRTStream::GetPresentationPosition(_Out_ KSAUDIO_PRESENTATION_POSITION *_pPresentationPosition) +{ + PAGED_CODE (); + ASSERT (_pPresentationPosition); + LARGE_INTEGER timeStamp; + PADAPTERCOMMON pAdapterComm = m_pMiniport->GetAdapterCommObj(); + + DPF_ENTER(("[CMiniportWaveRTStream::GetPresentationPosition]")); + + ULONGLONG ullLinearPosition = {0}; + NTSTATUS status = STATUS_SUCCESS; + + status = GetLinearBufferPosition(&ullLinearPosition, &timeStamp); + if (!NT_SUCCESS(status)) + { + return status; + } + + _pPresentationPosition->u64PositionInBlocks = ullLinearPosition * m_pWfExt->Format.nSamplesPerSec / m_pWfExt->Format.nAvgBytesPerSec; + + _pPresentationPosition->u64QPCPosition = (UINT64)timeStamp.QuadPart; + + + //Event type: eMINIPORT_GET_PRESENTATION_POSITION + //Parameter 1: Current linear buffer position + //Parameter 2: the previous WaveRtBufferWritePosition that the drive received + //Parameter 3: Presentation position + //Parameter 4:0 + pAdapterComm->WriteEtwEvent(eMINIPORT_GET_PRESENTATION_POSITION, + ullLinearPosition, // replace with the correct "Current linear buffer position" + m_ulCurrentWritePosition, + _pPresentationPosition->u64PositionInBlocks, + 0); // always zero + + return STATUS_SUCCESS; +} +NTSTATUS CMiniportWaveRTStream::SetCurrentWritePosition(_In_ ULONG _ulCurrentWritePosition) +{ + PAGED_CODE (); + DPF_ENTER(("[CMiniportWaveRTStream::SetCurrentWritePosition]")); + + NTSTATUS ntStatus; + +#ifdef SYSVAD_BTH_BYPASS + if (m_ScoOpen) + { + ntStatus = GetScoStreamNtStatus(); + IF_FAILED_JUMP(ntStatus, Done); + } +#endif // SYSVAD_BTH_BYPASS + + // + // Basic validation. WritePosition indicates the position (1-based) of the last valid byte. + // + if (_ulCurrentWritePosition == 0) + { + ntStatus = STATUS_INVALID_DEVICE_REQUEST; + goto Done; + } + + ntStatus = SetCurrentWritePositionInternal(_ulCurrentWritePosition); + +Done: + return ntStatus; +} +NTSTATUS CMiniportWaveRTStream::SetCurrentWritePositionInternal(_In_ ULONG _ulCurrentWritePosition) +{ + PAGED_CODE (); + DPF_ENTER(("[CMiniportWaveRTStream::SetCurrentWritePositionInternal]")); + + if (_ulCurrentWritePosition > m_ulDmaBufferSize) + { + return STATUS_INVALID_DEVICE_REQUEST; + } + + PADAPTERCOMMON pAdapterComm = m_pMiniport->GetAdapterCommObj(); + + //Event type: eMINIPORT_SET_WAVERT_BUFFER_WRITE_POSITION + //Parameter 1: Current linear buffer position + //Parameter 2: the previous WaveRtBufferWritePosition that the drive received + //Parameter 3: Target WaveRtBufferWritePosition received from portcls + //Parameter 4:0 + pAdapterComm->WriteEtwEvent(eMINIPORT_SET_WAVERT_BUFFER_WRITE_POSITION, + 100, // replace with the correct "Current linear buffer position" + m_ulCurrentWritePosition, + _ulCurrentWritePosition, // this is the passed in parameter + 0); // always zero + + + // + // Check for eMINIPORT_GLITCH_REPORT - 'same writert buffer' only when in event mode. + // + if (m_ulNotificationIntervalMs > 0) + { + if (m_ulCurrentWritePosition == _ulCurrentWritePosition) + { + //Event type: eMINIPORT_GLITCH_REPORT + //Parameter 1: Current linear buffer position + //Parameter 2: the previous WaveRtBufferWritePosition that the drive received + //Parameter 3: major glitch code: 3:receive the same wavert buffer two in a row in event driven mode + //Parameter 4: minor code for the glitch cause + pAdapterComm->WriteEtwEvent(eMINIPORT_GLITCH_REPORT, + 100, // replace with the correct "Current linear buffer position" + m_ulCurrentWritePosition, + 3, // receive the same wavert buffer two in a row in event driven mode + _ulCurrentWritePosition); + } + } + + m_ulCurrentWritePosition = _ulCurrentWritePosition; + InterlockedExchange(&m_IsCurrentWritePositionUpdated, 1); + + return STATUS_SUCCESS; +} +//linear position +#pragma code_seg("PAGE") +NTSTATUS CMiniportWaveRTStream::GetLinearBufferPosition(_Out_ ULONGLONG *_pullLinearBufferPosition, LARGE_INTEGER *_pliQPCTime) +{ + PAGED_CODE (); + ASSERT (_pullLinearBufferPosition); + DPF_ENTER(("[CMiniportWaveRTStream::GetLinearBufferPosition]")); + + NTSTATUS ntStatus; + LARGE_INTEGER ilQPC; + +#ifdef SYSVAD_BTH_BYPASS + if (m_ScoOpen) + { + ntStatus = GetScoStreamNtStatus(); + IF_FAILED_JUMP(ntStatus, Done); + } +#endif // SYSVAD_BTH_BYPASS + + // Update *_pullLinearBufferPosition with the the number of bytes fetched from waveRT ever since a stream got set into RUN + // state. + // Once the stream is set to STOP state, any further read on this call would return zero. + + // + // Get the current time and update position. + // + ilQPC = KeQueryPerformanceCounter(NULL); + if (m_KsState == KSSTATE_RUN) + { + UpdatePosition(ilQPC); + } + if (_pliQPCTime) + { + *_pliQPCTime = ilQPC; + } + *_pullLinearBufferPosition = m_ullLinearPosition; + + ntStatus = STATUS_SUCCESS; + +#ifdef SYSVAD_BTH_BYPASS +Done: +#endif // SYSVAD_BTH_BYPASS + return ntStatus; +} + +#pragma code_seg("PAGE") +/*----------------------------------------------------------------------------- +CMiniportWaveRTStream::SetLoopbackProtection + +Decscription: + + Portcls calls this method, inside its property handlers, to set the loopback + protection option + +Parameters: + _In_ protectionOption: protection option + CONSTRICTOR_OPTION_DISABLE: Turn protection off. + CONSTRICTOR_OPTION_MUTE: Mute the loopback stream +Return Value: + + Appropriate NTSTATUS code + +Called at PASSIVE_LEVEL + +Remarks + This content protection settings requestion could come in from the host process + pin or offload pin. The miniport needs to mute its loopback stream contents for + for this request from either host process pin or offload pin. +-------------------------------------------------------------------------------------------------------------------------*/ +NTSTATUS CMiniportWaveRTStream::SetLoopbackProtection(_In_ CONSTRICTOR_OPTION protectionOption) +{ + PAGED_CODE (); + DPF_ENTER(("[CMiniportWaveRTStream::SetLoopbackProtection]")); + + // + // Miniport driver mutes/unmutes the loopback here. + // + m_ToneGenerator.SetMute(protectionOption == CONSTRICTOR_OPTION_MUTE); + + return STATUS_SUCCESS; +} + +#pragma code_seg("PAGE") +//---------------------------------------------------------------------------------- +// Description: +// Set current write position for the very last buffer in a stream +// +// Parameters: +// ULONG _ulWritePosition: point to the last valid data byte +// +// Remark: +// +// In Win 8, for offload streams, double buffering mechanism is used and the buffer completion contact +// between the audio stack and driver is: a driver signals buffer completion only when it reaches the +// end of either buffer. Thus, there is no concept of partial buffer (not all the data in a buffer are +// valid, only the first N frames are valid and the rest of the buffer might contain invalid or silence +// data. So, when the very last buffer is partially filled, its completion will not get singled until +// all data is consumed, which might become undesirable when the size of a buffer is large and valid frame +// count is low. +// In WinBlue, a driver implements IMiniportStreamAudioEngineNode2 to claim being able to handle the very +// last buffer that's partial filled. +// For a driver claiming supporting IMiniportStreamAudioEngineNode2, for the very last buffer position write, +// it will receive the very last buffer write via SetStreamCurrentWritePositionForLastBuffer() call instead of +// SetStreamCurrentWritePosition(). A driver should check _ulWritePosition parameter to see how much valid data +// in the buffer (instead of blindly assuming all the data are valid) before signaling buffer completion. +// No more SetStreamCurrentWritePositionForLastBuffer or SetStreamCurrentWritePosition will be called after this +// SetStreamCurrentWritePositionForLastBuffer() call. +//----------------------------------------------------------------------------------------------------------------------- + +NTSTATUS CMiniportWaveRTStream::SetStreamCurrentWritePositionForLastBuffer(_In_ ULONG _ulWritePosition) +{ + PAGED_CODE (); + DPF_ENTER(("[CMiniportWaveRT::SetStreamCurrentWritePositionForLastBuffer]")); + + return SetCurrentWritePositionInternal(_ulWritePosition); + // Miniport driver needs to prepare to signal buffer completion event + // when it's done with reading the last valid byte - an _ulWritePosition offset from the beginning WaveRT buffer + // Note: _ulWritePosition will be smaller than buffer size in most of the cases +} + diff --git a/audio/sysvad/EndpointsCommon/bthhfpmictopo.cpp b/audio/sysvad/EndpointsCommon/bthhfpmictopo.cpp new file mode 100644 index 000000000..ec3f43671 --- /dev/null +++ b/audio/sysvad/EndpointsCommon/bthhfpmictopo.cpp @@ -0,0 +1,281 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + bthhfpmictopo.cpp + +Abstract: + + Implementation of topology miniport for the mic (external: headphone). + +--*/ +#ifdef SYSVAD_BTH_BYPASS + +#pragma warning (disable : 4127) + +#include +#include "simple.h" +#include "mintopo.h" +#include "bthhfptopo.h" +#include "bthhfpmictopo.h" +#include "bthhfpmictoptable.h" + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +PropertyHandler_BthHfpMicTopoFilter +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Redirects property request to miniport object + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_BthHfpMicTopoFilter]")); + + // PropertryRequest structure is filled by portcls. + // MajorTarget is a pointer to miniport object for miniports. + // + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + // + // This line shows how to get a pointer to the miniport topology object. + // + PCMiniportTopology pMiniport = (PCMiniportTopology)PropertyRequest->MajorTarget; + UNREFERENCED_VAR(pMiniport); + + if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_Jack)) + { + switch(PropertyRequest->PropertyItem->Id) + { + case KSPROPERTY_JACK_DESCRIPTION: + ntStatus = PropertyHandler_BthHfpJackDescription( + PropertyRequest, + ARRAYSIZE(BthHfpMicJackDescriptions), + BthHfpMicJackDescriptions); + break; + + case KSPROPERTY_JACK_DESCRIPTION2: + ntStatus = PropertyHandler_BthHfpJackDescription2( + PropertyRequest, + ARRAYSIZE(BthHfpMicJackDescriptions), + BthHfpMicJackDescriptions); + break; + + case KSPROPERTY_JACK_CONTAINERID: + ntStatus = PropertyHandler_BthHfpJackContainerId( + PropertyRequest, + ARRAYSIZE(BthHfpMicJackDescriptions), + BthHfpMicJackDescriptions); + break; + } + } + else if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_BtAudio)) + { + switch(PropertyRequest->PropertyItem->Id) + { + case KSPROPERTY_ONESHOT_RECONNECT: + ntStatus = PropertyHandler_BthHfpOneShotReconnect(PropertyRequest); + break; + + case KSPROPERTY_ONESHOT_DISCONNECT: + ntStatus = PropertyHandler_BthHfpOneDisconnect(PropertyRequest); + break; + } + } + + return ntStatus; +} // PropertyHandler_BthHfpMicTopoFilter + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +PropertyHandler_BthHfpMicVolumeLevel +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_Audio, KSPROPERTY_AUDIO_VOLUMELEVEL ) + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_BthHfpMicVolumeLevel]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + PCMiniportTopology miniport = (PCMiniportTopology)PropertyRequest->MajorTarget; + PBTHHFPDEVICECOMMON bthHfpDevice = NULL; + ULONG channel = (ULONG)-1; + + bthHfpDevice = miniport->GetBthHfpDevice(); // weak ref. + ASSERT(bthHfpDevice != NULL); + + if (bthHfpDevice->IsVolumeSupported() == FALSE) + { + ntStatus = miniport->PropertyHandlerGeneric(PropertyRequest); + } + else if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = PropertyHandler_BthHfpVolumeLevel_BasicSupport(PropertyRequest); + } + else if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + // Instance is the channel #, Bluetooth HFP supports mono streams. + channel = *(PULONG(PropertyRequest->Instance)); + + if (channel == 0) + { + ULONG cbNeeded = sizeof(LONG); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbNeeded; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbNeeded) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + LONG* volume = (LONG*)PropertyRequest->Value; + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + *volume = bthHfpDevice->GetMicVolume(); + + ntStatus = STATUS_SUCCESS; + + } + else if (PropertyRequest->Verb & KSPROPERTY_TYPE_SET) + { + ntStatus = bthHfpDevice->SetMicVolume(*volume); + } + } + } + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +PropertyHandler_BthHfpMicTopoNode +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Redirects property request to miniport object + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_BthHfpMicTopoNode]")); + + // PropertryRequest structure is filled by portcls. + // MajorTarget is a pointer to miniport object for miniports. + // + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + // + // This line shows how to get a pointer to the miniport topology object. + // + PCMiniportTopology pMiniport = (PCMiniportTopology)PropertyRequest->MajorTarget; + UNREFERENCED_VAR(pMiniport); + + if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_Audio)) + { + if (PropertyRequest->PropertyItem->Id == KSPROPERTY_AUDIO_VOLUMELEVEL) + { + ntStatus = PropertyHandler_BthHfpMicVolumeLevel(PropertyRequest); + } + } + + return ntStatus; +} // PropertyHandler_BthHfpMicTopoFilter + +//============================================================================= +#pragma code_seg() +NTSTATUS +PropertyHandler_BthHfpMicTopoNodeEvent +( + _In_ PPCEVENT_REQUEST EventRequest +) +{ + ASSERT(EventRequest); + + DPF_ENTER(("[PropertyHandler_BthHfpMicTopoNodeEvent]")); + + // Validate the node. + if (EventRequest->Node != KSNODE_TOPO_VOLUME) + return STATUS_INVALID_PARAMETER; + + return PropertyHandler_BthHfpTopoNodeEvent(EventRequest); +} + +//============================================================================= +#pragma code_seg() +NTSTATUS +PropertyHandler_BthHfpMicTopoFilterEvent +( + _In_ PPCEVENT_REQUEST EventRequest +) +{ + ASSERT(EventRequest); + + DPF_ENTER(("[PropertyHandler_BthHfpMicTopoFilterEvent]")); + + return PropertyHandler_BthHfpTopoNodeEvent(EventRequest); +} + +#pragma code_seg() +#endif // SYSVAD_BTH_BYPASS + + diff --git a/audio/sysvad/EndpointsCommon/bthhfpmictopo.h b/audio/sysvad/EndpointsCommon/bthhfpmictopo.h new file mode 100644 index 000000000..48f93a69f --- /dev/null +++ b/audio/sysvad/EndpointsCommon/bthhfpmictopo.h @@ -0,0 +1,41 @@ + +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + bthhfpmictopo.h + +Abstract: + + Declaration of topology miniport for the mic (external: headphone). + +--*/ + +#ifndef _SYSVAD_BTHHFPMICTOPO_H_ +#define _SYSVAD_BTHHFPMICTOPO_H_ + +// Function declarations. +NTSTATUS +PropertyHandler_BthHfpMicTopoFilter( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + +NTSTATUS +PropertyHandler_BthHfpMicTopoNode( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + +NTSTATUS +PropertyHandler_BthHfpMicTopoNodeEvent( + _In_ PPCEVENT_REQUEST EventRequest + ); + +NTSTATUS +PropertyHandler_BthHfpMicTopoFilterEvent( + _In_ PPCEVENT_REQUEST EventRequest + ); + +#endif // _SYSVAD_BTHHFPMICTOPO_H_ + diff --git a/audio/sysvad/EndpointsCommon/bthhfpmictoptable.h b/audio/sysvad/EndpointsCommon/bthhfpmictoptable.h new file mode 100644 index 000000000..0ea699569 --- /dev/null +++ b/audio/sysvad/EndpointsCommon/bthhfpmictoptable.h @@ -0,0 +1,279 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + bthhfpmictopotable.h + +Abstract: + + Declaration of topology table for the Bluetooth hands-free profile mic (external) + +--*/ + +#ifndef _SYSVAD_BTHHFPMICTOPTABLE_H_ +#define _SYSVAD_BTHHFPMICTOPTABLE_H_ + +#include "bthhfpmictopo.h" + +//============================================================================= +static +KSDATARANGE BthHfpMicTopoPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +//============================================================================= +static +PKSDATARANGE BthHfpMicTopoPinDataRangePointersBridge[] = +{ + &BthHfpMicTopoPinDataRangesBridge[0] +}; + +//============================================================================= +static +PCPIN_DESCRIPTOR BthHfpMicTopoMiniportPins[] = +{ + // KSPIN_TOPO_MIC_ELEMENTS + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(BthHfpMicTopoPinDataRangePointersBridge),// DataRangesCount + BthHfpMicTopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_IN, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSNODETYPE_HEADSET_MICROPHONE, // Category + NULL, // Name + 0 // Reserved + } + }, + + // KSPIN_TOPO_BRIDGE + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(BthHfpMicTopoPinDataRangePointersBridge),// DataRangesCount + BthHfpMicTopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_OUT, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSCATEGORY_AUDIO, // Category + NULL, // Name + 0 // Reserved + } + } +}; + +//============================================================================= +static +KSJACK_DESCRIPTION BthHfpMicJackDesc = +{ + KSAUDIO_SPEAKER_MONO, + 0, // color + eConnTypeOtherDigital, + eGeoLocNotApplicable, + eGenLocOther, + ePortConnUnknown, + FALSE // run-time code sets this value. +}; + +// Only return a KSJACK_DESCRIPTION for the physical bridge pin. +static +PKSJACK_DESCRIPTION BthHfpMicJackDescriptions[] = +{ + &BthHfpMicJackDesc, + NULL +}; + +//============================================================================= +static +PCPROPERTY_ITEM BthHfpMicPropertiesVolume[] = +{ + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_VOLUMELEVEL, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_BthHfpMicTopoNode + } +}; + +static +PCEVENT_ITEM BthHfpMicPropertiesVolumeEvent[] = +{ + // This is a generic event for nearly every node property. + { + &KSEVENTSETID_AudioControlChange, // Something changed! + KSEVENT_CONTROL_CHANGE, // The only event-property defined. + KSEVENT_TYPE_ENABLE | KSEVENT_TYPE_BASICSUPPORT, + PropertyHandler_BthHfpMicTopoNodeEvent + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP_EVENT(AutomationBthHfpMicVolumeWithEvent, + BthHfpMicPropertiesVolume, + BthHfpMicPropertiesVolumeEvent); + + +//============================================================================= +static +PCPROPERTY_ITEM BthHfpMicPropertiesMute[] = +{ + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_MUTE, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_Topology + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationBthHfpMicMute, BthHfpMicPropertiesMute); + +//============================================================================= +// The volume node should be implemented as follows: +// +// #1 If the Bluetooth headset supports volume control, then the audio driver should +// include a volume node in its KS topology. The audio driver's volume property +// handlers send the above IOCLTs to the Bluetooth HFP driver to handle the volume. +// +// #2 If the Bluetooth headset does not implement a hardware volume, and the codec +// (or DSP) has a hardware volume, the audio driver should handle the volume control +// on the codec (or DSP). +// +// #3 If the Bluetooth headset and the audio device do not have hardware volume controls, +// no volume node should be presented and Windows will insert a software volume +// control node. +// +// This sample assumes #1 or #2, and it adds a valume node in its topology. +// +// The mute node is opitional. The driver shall implement the mute node if and only if +// the DSP or audio codec provides the capability to mute the bypass PCM signal before +// passing it to the Bluetooth controller. The mute nodes support +// KSPROPSETID_Audio/KSPROPERTY_AUDIO_MUTE +// +static +PCNODE_DESCRIPTOR BthHfpMicTopologyNodes[] = +{ + // KSNODE_TOPO_VOLUME + { + 0, // Flags + &AutomationBthHfpMicVolumeWithEvent, // AutomationTable + &KSNODETYPE_VOLUME, // Type + &KSAUDFNAME_MIC_VOLUME // Name + }, + // KSNODE_TOPO_MUTE + { + 0, // Flags + &AutomationBthHfpMicMute, // AutomationTable + &KSNODETYPE_MUTE, // Type + &KSAUDFNAME_MIC_MUTE // Name + }, +}; + +C_ASSERT( KSNODE_TOPO_VOLUME == 0 ); +C_ASSERT( KSNODE_TOPO_MUTE == 1 ); + +//============================================================================= +static +PCCONNECTION_DESCRIPTOR BthHfpMicMiniportConnections[] = +{ + // FromNode, FromPin, ToNode, ToPin + { PCFILTER_NODE, KSPIN_TOPO_MIC_ELEMENTS, KSNODE_TOPO_VOLUME, 1 }, + { KSNODE_TOPO_VOLUME, 0, KSNODE_TOPO_MUTE, 1 }, + { KSNODE_TOPO_MUTE, 0, PCFILTER_NODE, KSPIN_TOPO_BRIDGE } +}; + + +//============================================================================= +static +PCPROPERTY_ITEM BthHfpMicPropertiesTopoFilter[] = +{ + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_BthHfpMicTopoFilter + }, + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION2, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_BthHfpMicTopoFilter + }, + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_CONTAINERID, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_BthHfpMicTopoFilter + }, + { + &KSPROPSETID_BtAudio, + KSPROPERTY_ONESHOT_RECONNECT, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_BthHfpMicTopoFilter + }, + { + &KSPROPSETID_BtAudio, + KSPROPERTY_ONESHOT_DISCONNECT, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_BthHfpMicTopoFilter + } +}; + +static +PCEVENT_ITEM BthHfpMicTopoPropertiesFilterEvent[] = +{ + { + &KSEVENTSETID_PinCapsChange, // Something changed! + KSEVENT_PINCAPS_JACKINFOCHANGE, // The only event-property defined. + KSEVENT_TYPE_ENABLE | KSEVENT_TYPE_BASICSUPPORT, + PropertyHandler_BthHfpMicTopoFilterEvent + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP_EVENT(AutomationBthHfpMicTopoFilterWithEvent, + BthHfpMicPropertiesTopoFilter, + BthHfpMicTopoPropertiesFilterEvent); + + +//============================================================================= +static +PCFILTER_DESCRIPTOR BthHfpMicTopoMiniportFilterDescriptor = +{ + 0, // Version + &AutomationBthHfpMicTopoFilterWithEvent, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(BthHfpMicTopoMiniportPins), // PinCount + BthHfpMicTopoMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + SIZEOF_ARRAY(BthHfpMicTopologyNodes), // NodeCount + BthHfpMicTopologyNodes, // Nodes + SIZEOF_ARRAY(BthHfpMicMiniportConnections),// ConnectionCount + BthHfpMicMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories +}; + +#endif // _SYSVAD_BTHHFPMICTOPTABLE_H_ + diff --git a/audio/sysvad/EndpointsCommon/bthhfpmicwavtable.h b/audio/sysvad/EndpointsCommon/bthhfpmicwavtable.h new file mode 100644 index 000000000..fe28f3f3a --- /dev/null +++ b/audio/sysvad/EndpointsCommon/bthhfpmicwavtable.h @@ -0,0 +1,292 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + bthhfpmicwavtable.h + +Abstract: + + Declaration of wave miniport tables for the Bluetooth handls-free profile (external). + +--*/ + +#ifndef _SYSVAD_BTHHFPMICWAVTABLE_H_ +#define _SYSVAD_BTHHFPMICWAVTABLE_H_ + +// +// Function prototypes. +// +NTSTATUS PropertyHandler_BthHfpWaveFilter(_In_ PPCPROPERTY_REQUEST PropertyRequest); + +// +// Bluetooth Headset Mic (external) range. +// +#define BTHHFPMIC_DEVICE_MAX_CHANNELS 1 // Max Channels. +#define BTHHFPMIC_MIN_BITS_PER_SAMPLE_PCM 8 // Min Bits Per Sample +#define BTHHFPMIC_MAX_BITS_PER_SAMPLE_PCM 16 // Max Bits Per Sample +#define BTHHFPMIC_MIN_SAMPLE_RATE 8000 // Min Sample Rate +#define BTHHFPMIC_MAX_SAMPLE_RATE 8000 // Max Sample Rate + + +//============================================================================= +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE BthHfpMicPinSupportedDeviceFormats[] = +{ + { + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 8000, + 8000, + 1, + 8, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 8, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 8000, + 16000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +// +// Supported modes (only on streaming pins) - NREC not supported. +// +static +MODE_AND_DEFAULT_FORMAT BthHfpMicPinSupportedDeviceModesNoNrec[] = +{ + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_RAW, + &BthHfpMicPinSupportedDeviceFormats[SIZEOF_ARRAY(BthHfpMicPinSupportedDeviceFormats)-1].DataFormat + } +}; + +// +// Supported modes (only on streaming pins) - NREC supported. +// +static +MODE_AND_DEFAULT_FORMAT BthHfpMicPinSupportedDeviceModesNrec[] = +{ + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_DEFAULT, + &BthHfpMicPinSupportedDeviceFormats[SIZEOF_ARRAY(BthHfpMicPinSupportedDeviceFormats)-1].DataFormat + } +}; + +// +// The entries here must follow the same order as the filter's pin +// descriptor array. +// +static +PIN_DEVICE_FORMATS_AND_MODES BthHfpMicPinDeviceFormatsAndModes[] = +{ + { + BridgePin, + NULL, + 0, + NULL, + 0 + }, + { + SystemCapturePin, + BthHfpMicPinSupportedDeviceFormats, + SIZEOF_ARRAY(BthHfpMicPinSupportedDeviceFormats), + NULL, // Init dynamically, see GetCapturePinSupportedDeviceModes[Count] + 0 // Init dynamically, see GetCapturePinSupportedDeviceModes[Count] + } +}; + +//============================================================================= +static +KSDATARANGE_AUDIO BthHfpMicPinDataRangesStream[] = +{ + { + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + BTHHFPMIC_DEVICE_MAX_CHANNELS, + BTHHFPMIC_MIN_BITS_PER_SAMPLE_PCM, + BTHHFPMIC_MAX_BITS_PER_SAMPLE_PCM, + BTHHFPMIC_MIN_SAMPLE_RATE, + BTHHFPMIC_MAX_SAMPLE_RATE + }, +}; + +static +PKSDATARANGE BthHfpMicPinDataRangePointersStream[] = +{ + PKSDATARANGE(&BthHfpMicPinDataRangesStream[0]), + PKSDATARANGE(&PinDataRangeAttributeList) +}; + +//============================================================================= +static +KSDATARANGE BthHfpMicPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +static +PKSDATARANGE BthHfpMicPinDataRangePointersBridge[] = +{ + &BthHfpMicPinDataRangesBridge[0] +}; + +//============================================================================= +static +PCPIN_DESCRIPTOR BthHfpMicWaveMiniportPins[] = +{ + // Wave In Bridge Pin (Capture - From Topology) KSPIN_WAVE_BRIDGE + { + 0, + 0, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(BthHfpMicPinDataRangePointersBridge), + BthHfpMicPinDataRangePointersBridge, + KSPIN_DATAFLOW_IN, + KSPIN_COMMUNICATION_NONE, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, + + // Wave In Streaming Pin (Capture) KSPIN_WAVEIN_HOST + { + MAX_INPUT_STREAMS, + MAX_INPUT_STREAMS, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(BthHfpMicPinDataRangePointersStream), + BthHfpMicPinDataRangePointersStream, + KSPIN_DATAFLOW_OUT, + KSPIN_COMMUNICATION_SINK, + &KSCATEGORY_AUDIO, + &KSAUDFNAME_RECORDING_CONTROL, + 0 + } + } +}; + +//============================================================================= +static +PCNODE_DESCRIPTOR BthHfpMicWaveMiniportNodes[] = +{ + // KSNODE_WAVE_ADC + { + 0, // Flags + NULL, // AutomationTable + &KSNODETYPE_ADC, // Type + NULL // Name + } +}; + +//============================================================================= +static +PCCONNECTION_DESCRIPTOR BthHfpMicWaveMiniportConnections[] = +{ + { PCFILTER_NODE, KSPIN_WAVE_BRIDGE, KSNODE_WAVE_ADC, 1 }, + { KSNODE_WAVE_ADC, 0, PCFILTER_NODE, KSPIN_WAVEIN_HOST } +}; + +//============================================================================= +static +PCPROPERTY_ITEM PropertiesBthHfpMicWaveFilter[] = +{ + { + &KSPROPSETID_Pin, + KSPROPERTY_PIN_PROPOSEDATAFORMAT, + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_BthHfpWaveFilter + }, + { + &KSPROPSETID_AudioEffectsDiscovery, + KSPROPERTY_AUDIOEFFECTSDISCOVERY_EFFECTSLIST, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_BthHfpWaveFilter + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationBthHfpMicWaveFilter, PropertiesBthHfpMicWaveFilter); + +//============================================================================= +static +PCFILTER_DESCRIPTOR BthHfpMicWaveMiniportFilterDescriptor = +{ + 0, // Version + &AutomationBthHfpMicWaveFilter, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(BthHfpMicWaveMiniportPins), // PinCount + BthHfpMicWaveMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + SIZEOF_ARRAY(BthHfpMicWaveMiniportNodes), // NodeCount + BthHfpMicWaveMiniportNodes, // Nodes + SIZEOF_ARRAY(BthHfpMicWaveMiniportConnections), // ConnectionCount + BthHfpMicWaveMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories - use defaults (audio, render, capture) +}; + +#endif // _SYSVAD_BTHHFPMICWAVTABLE_H_ diff --git a/audio/sysvad/EndpointsCommon/bthhfpmicwbwavtable.h b/audio/sysvad/EndpointsCommon/bthhfpmicwbwavtable.h new file mode 100644 index 000000000..9a41f4602 --- /dev/null +++ b/audio/sysvad/EndpointsCommon/bthhfpmicwbwavtable.h @@ -0,0 +1,361 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + bthhfpmicwbwavtable.h + +Abstract: + + Declaration of wave miniport tables for the Bluetooth handls-free profile (external), + for Bluetooth connections that support Wideband Speech. + +--*/ + +#ifndef _SYSVAD_BTHHFPMICWIDEBANDWAVTABLE_H_ +#define _SYSVAD_BTHHFPMICWBWAVTABLE_H_ + +// +// Function prototypes. +// +NTSTATUS PropertyHandler_BthHfpWaveFilter(_In_ PPCPROPERTY_REQUEST PropertyRequest); + +// +// Bluetooth Headset Mic (external) range. +// +#define BTHHFPMICWB_DEVICE_MAX_CHANNELS 1 // Max Channels. +#define BTHHFPMICWB_MIN_BITS_PER_SAMPLE_PCM 8 // Min Bits Per Sample +#define BTHHFPMICWB_MAX_BITS_PER_SAMPLE_PCM 16 // Max Bits Per Sample +#define BTHHFPMICWB_MIN_SAMPLE_RATE 8000 // Min Sample Rate +#define BTHHFPMICWB_MAX_SAMPLE_RATE 16000 // Max Sample Rate + + +//============================================================================= +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE BthHfpMicWBPinSupportedDeviceFormats[] = +{ + { + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 8000, + 8000, + 1, + 8, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 8, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 8000, + 16000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 16000, + 16000, + 1, + 8, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 8, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 16000, + 32000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +// +// Supported modes (only on streaming pins) - NREC not supported. +// +static +MODE_AND_DEFAULT_FORMAT BthHfpMicWBPinSupportedDeviceModesNoNrec[] = +{ + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_RAW, + &BthHfpMicWBPinSupportedDeviceFormats[SIZEOF_ARRAY(BthHfpMicWBPinSupportedDeviceFormats)-1].DataFormat + } +}; + +// +// Supported modes (only on streaming pins) - NREC supported. +// +static +MODE_AND_DEFAULT_FORMAT BthHfpMicWBPinSupportedDeviceModesNrec[] = +{ + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_DEFAULT, + &BthHfpMicWBPinSupportedDeviceFormats[SIZEOF_ARRAY(BthHfpMicWBPinSupportedDeviceFormats)-1].DataFormat + } +}; + +// +// The entries here must follow the same order as the filter's pin +// descriptor array. +// +static +PIN_DEVICE_FORMATS_AND_MODES BthHfpMicWBPinDeviceFormatsAndModes[] = +{ + { + BridgePin, + NULL, + 0, + NULL, + 0 + }, + { + SystemCapturePin, + BthHfpMicWBPinSupportedDeviceFormats, + SIZEOF_ARRAY(BthHfpMicWBPinSupportedDeviceFormats), + NULL, // Init dynamically, see GetCapturePinSupportedDeviceModes[Count] + 0 // Init dynamically, see GetCapturePinSupportedDeviceModes[Count] + } +}; + +//============================================================================= +static +KSDATARANGE_AUDIO BthHfpMicWBPinDataRangesStream[] = +{ + { + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + BTHHFPMICWB_DEVICE_MAX_CHANNELS, + BTHHFPMICWB_MIN_BITS_PER_SAMPLE_PCM, + BTHHFPMICWB_MAX_BITS_PER_SAMPLE_PCM, + BTHHFPMICWB_MIN_SAMPLE_RATE, + BTHHFPMICWB_MIN_SAMPLE_RATE + }, + { + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + BTHHFPMICWB_DEVICE_MAX_CHANNELS, + BTHHFPMICWB_MIN_BITS_PER_SAMPLE_PCM, + BTHHFPMICWB_MAX_BITS_PER_SAMPLE_PCM, + BTHHFPMICWB_MAX_SAMPLE_RATE, + BTHHFPMICWB_MAX_SAMPLE_RATE + }, +}; + +static +PKSDATARANGE BthHfpMicWBPinDataRangePointersStream[] = +{ + PKSDATARANGE(&BthHfpMicWBPinDataRangesStream[0]), + PKSDATARANGE(&PinDataRangeAttributeList), + PKSDATARANGE(&BthHfpMicWBPinDataRangesStream[1]), + PKSDATARANGE(&PinDataRangeAttributeList) +}; + +//============================================================================= +static +KSDATARANGE BthHfpMicWBPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +static +PKSDATARANGE BthHfpMicWBPinDataRangePointersBridge[] = +{ + &BthHfpMicWBPinDataRangesBridge[0] +}; + +//============================================================================= +static +PCPIN_DESCRIPTOR BthHfpMicWBWaveMiniportPins[] = +{ + // Wave In Bridge Pin (Capture - From Topology) KSPIN_WAVE_BRIDGE + { + 0, + 0, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(BthHfpMicWBPinDataRangePointersBridge), + BthHfpMicWBPinDataRangePointersBridge, + KSPIN_DATAFLOW_IN, + KSPIN_COMMUNICATION_NONE, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, + + // Wave In Streaming Pin (Capture) KSPIN_WAVEIN_HOST + { + MAX_INPUT_STREAMS, + MAX_INPUT_STREAMS, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(BthHfpMicWBPinDataRangePointersStream), + BthHfpMicWBPinDataRangePointersStream, + KSPIN_DATAFLOW_OUT, + KSPIN_COMMUNICATION_SINK, + &KSCATEGORY_AUDIO, + &KSAUDFNAME_RECORDING_CONTROL, + 0 + } + } +}; + +//============================================================================= +static +PCNODE_DESCRIPTOR BthHfpMicWBWaveMiniportNodes[] = +{ + // KSNODE_WAVE_ADC + { + 0, // Flags + NULL, // AutomationTable + &KSNODETYPE_ADC, // Type + NULL // Name + } +}; + +//============================================================================= +static +PCCONNECTION_DESCRIPTOR BthHfpMicWBWaveMiniportConnections[] = +{ + { PCFILTER_NODE, KSPIN_WAVE_BRIDGE, KSNODE_WAVE_ADC, 1 }, + { KSNODE_WAVE_ADC, 0, PCFILTER_NODE, KSPIN_WAVEIN_HOST } +}; + +//============================================================================= +static +PCPROPERTY_ITEM PropertiesBthHfpMicWBWaveFilter[] = +{ + { + &KSPROPSETID_Pin, + KSPROPERTY_PIN_PROPOSEDATAFORMAT, + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_BthHfpWaveFilter + }, + { + &KSPROPSETID_AudioEffectsDiscovery, + KSPROPERTY_AUDIOEFFECTSDISCOVERY_EFFECTSLIST, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_BthHfpWaveFilter + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationBthHfpMicWBWaveFilter, PropertiesBthHfpMicWBWaveFilter); + +//============================================================================= +static +PCFILTER_DESCRIPTOR BthHfpMicWBWaveMiniportFilterDescriptor = +{ + 0, // Version + &AutomationBthHfpMicWBWaveFilter, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(BthHfpMicWBWaveMiniportPins), // PinCount + BthHfpMicWBWaveMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + SIZEOF_ARRAY(BthHfpMicWBWaveMiniportNodes), // NodeCount + BthHfpMicWBWaveMiniportNodes, // Nodes + SIZEOF_ARRAY(BthHfpMicWBWaveMiniportConnections), // ConnectionCount + BthHfpMicWBWaveMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories - use defaults (audio, render, capture) +}; + +#endif // _SYSVAD_BTHHFPMICWBWAVTABLE_H_ diff --git a/audio/sysvad/EndpointsCommon/bthhfpminipairs.h b/audio/sysvad/EndpointsCommon/bthhfpminipairs.h new file mode 100644 index 000000000..7d2b82f75 --- /dev/null +++ b/audio/sysvad/EndpointsCommon/bthhfpminipairs.h @@ -0,0 +1,224 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + bthhfpminipairs.h + +Abstract: + + Bluetooth HFP audio endpoint filter definitions. + +--*/ + +#ifndef _SYSVAD_BTHHFPMINIPAIRS_H_ +#define _SYSVAD_BTHHFPMINIPAIRS_H_ + +#include "bthhfpspeakertopo.h" +#include "bthhfpspeakertoptable.h" +#include "bthhfpspeakerwavtable.h" +#include "bthhfpspeakerwbwavtable.h" + +#include "bthhfpmictopo.h" +#include "bthhfpmictoptable.h" +#include "bthhfpmicwavtable.h" +#include "bthhfpmicwbwavtable.h" + +NTSTATUS +CreateMiniportWaveRTSYSVAD +( + _Out_ PUNKNOWN *, + _In_ REFCLSID, + _In_opt_ PUNKNOWN, + _In_ POOL_TYPE, + _In_ PUNKNOWN, + _In_opt_ PVOID, + _In_ PENDPOINT_MINIPAIR +); + +NTSTATUS +CreateMiniportTopologySYSVAD +( + _Out_ PUNKNOWN *, + _In_ REFCLSID, + _In_opt_ PUNKNOWN, + _In_ POOL_TYPE, + _In_ PUNKNOWN, + _In_opt_ PVOID, + _In_ PENDPOINT_MINIPAIR +); + +// +// Render miniports. +// + +/********************************************************************* +* Topology/Wave bridge connection for BTH-HFP speaker * +* * +* +------+ +------+ * +* | Wave | | Topo | * +* | | | | * +* System --->|0 2|---> Loopback | | * +* | | | | * +* Offload --->|1 3|--------------->|0 1|---> Line Out * +* | | | | * +* +------+ +------+ * +*********************************************************************/ +static +PHYSICALCONNECTIONTABLE BthHfpSpeakerTopologyPhysicalConnections[] = +{ + { + KSPIN_TOPO_WAVEOUT_SOURCE, // TopologyIn + KSPIN_WAVE_RENDER_SOURCE, // WaveOut + CONNECTIONTYPE_WAVE_OUTPUT + } +}; + +static +ENDPOINT_MINIPAIR BthHfpSpeakerMiniports = +{ + eBthHfpSpeakerDevice, + L"TopologyBthHfpSpeaker", // make sure this name matches with SYSVAD.TopologyBthHfpSpeaker.szPname in the inf's [Strings] section + CreateMiniportTopologySYSVAD, + &BthHfpSpeakerTopoMiniportFilterDescriptor, + 0, NULL, // Interface properties + L"WaveBthHfpSpeaker", // make sure this name matches with SYSVAD.WaveBthHfpSpeaker.szPname in the inf's [Strings] section + CreateMiniportWaveRTSYSVAD, + &BthHfpSpeakerWaveMiniportFilterDescriptor, + 0, NULL, // Interface properties + 1, + BthHfpSpeakerPinDeviceFormatsAndModes, + SIZEOF_ARRAY(BthHfpSpeakerPinDeviceFormatsAndModes), + BthHfpSpeakerTopologyPhysicalConnections, + SIZEOF_ARRAY(BthHfpSpeakerTopologyPhysicalConnections), + ENDPOINT_OFFLOAD_SUPPORTED +}; + +static +ENDPOINT_MINIPAIR BthHfpSpeakerWBMiniports = +{ + eBthHfpSpeakerDevice, + L"TopologyBthHfpSpeaker", // make sure this name matches with SYSVAD.TopologyBthHfpSpeaker.szPname in the inf's [Strings] section + CreateMiniportTopologySYSVAD, + &BthHfpSpeakerTopoMiniportFilterDescriptor, + 0, NULL, // Interface properties + L"WaveBthHfpSpeaker", // make sure this name matches with SYSVAD.WaveBthHfpSpeaker.szPname in the inf's [Strings] section + CreateMiniportWaveRTSYSVAD, + &BthHfpSpeakerWBWaveMiniportFilterDescriptor, + 0, NULL, // Interface properties + 1, + BthHfpSpeakerWBPinDeviceFormatsAndModes, + SIZEOF_ARRAY(BthHfpSpeakerWBPinDeviceFormatsAndModes), + BthHfpSpeakerTopologyPhysicalConnections, + SIZEOF_ARRAY(BthHfpSpeakerTopologyPhysicalConnections), + ENDPOINT_OFFLOAD_SUPPORTED +}; + +// +// Capture miniports. +// + +/********************************************************************* +* Topology/Wave bridge connection for BTH-HFP mic * +* * +* +------+ +------+ * +* | Topo | | Wave | * +* | | | | * +* Mic in --->|0 1|===>|0 1|---> Capture Host Pin * +* | | | | * +* +------+ +------+ * +*********************************************************************/ +static +PHYSICALCONNECTIONTABLE BthHfpMicTopologyPhysicalConnections[] = +{ + { + KSPIN_TOPO_BRIDGE, // TopologyOut + KSPIN_WAVE_BRIDGE, // WaveIn + CONNECTIONTYPE_TOPOLOGY_OUTPUT + } +}; + +static +ENDPOINT_MINIPAIR BthHfpMicMiniports = +{ + eBthHfpMicDevice, + L"TopologyBthHfpMic", // make sure this name matches with SYSVAD.TopologyBthHfpMic.szPname in the inf's [Strings] section + CreateMiniportTopologySYSVAD, + &BthHfpMicTopoMiniportFilterDescriptor, + 0, NULL, // Interface properties + L"WaveBthHfpMic", // make sure this name matches with SYSVAD.WaveBthHfpMic.szPname in the inf's [Strings] section + CreateMiniportWaveRTSYSVAD, + &BthHfpMicWaveMiniportFilterDescriptor, + 0, NULL, // Interface properties + 1, + BthHfpMicPinDeviceFormatsAndModes, + SIZEOF_ARRAY(BthHfpMicPinDeviceFormatsAndModes), + BthHfpMicTopologyPhysicalConnections, + SIZEOF_ARRAY(BthHfpMicTopologyPhysicalConnections), + ENDPOINT_NO_FLAGS +}; + +static +ENDPOINT_MINIPAIR BthHfpMicWBMiniports = +{ + eBthHfpMicDevice, + L"TopologyBthHfpMic", // make sure this name matches with SYSVAD.TopologyBthHfpMic.szPname in the inf's [Strings] section + CreateMiniportTopologySYSVAD, + &BthHfpMicTopoMiniportFilterDescriptor, + 0, NULL, // Interface properties + L"WaveBthHfpMic", // make sure this name matches with SYSVAD.WaveBthHfpMic.szPname in the inf's [Strings] section + CreateMiniportWaveRTSYSVAD, + &BthHfpMicWBWaveMiniportFilterDescriptor, + 0, NULL, // Interface properties + 1, + BthHfpMicWBPinDeviceFormatsAndModes, + SIZEOF_ARRAY(BthHfpMicWBPinDeviceFormatsAndModes), + BthHfpMicTopologyPhysicalConnections, + SIZEOF_ARRAY(BthHfpMicTopologyPhysicalConnections), + ENDPOINT_NO_FLAGS +}; + +//============================================================================= +// +// Render miniport pairs. +// +static +PENDPOINT_MINIPAIR g_BthHfpRenderEndpoints[] = +{ + &BthHfpSpeakerMiniports, + &BthHfpSpeakerWBMiniports +}; + +#define g_cBthHfpRenderEndpoints (SIZEOF_ARRAY(g_BthHfpRenderEndpoints)) + +// If the count of Render endpoints changes, update formula below +C_ASSERT(g_cBthHfpRenderEndpoints == 2); + +//============================================================================= +// +// Capture miniport pairs. +// +static +PENDPOINT_MINIPAIR g_BthHfpCaptureEndpoints[] = +{ + &BthHfpMicMiniports, + &BthHfpMicWBMiniports +}; + +#define g_cBthHfpCaptureEndpoints (SIZEOF_ARRAY(g_BthHfpCaptureEndpoints)) + +// If the count of Capture endpoints changes, update formula below +C_ASSERT(g_cBthHfpCaptureEndpoints == 2); + +//============================================================================= +// +// Total miniports = # endpoints * 2 (topology + wave) / 2 (since we have one each for narrowband-only and wideband) +// +#define g_MaxBthHfpMiniports ((g_cBthHfpRenderEndpoints + g_cBthHfpCaptureEndpoints)) + +// g_MaxBthHfpMiniports is used when calculating the MaxObjects inside AddDevice. +C_ASSERT(g_MaxBthHfpMiniports == 4); + +#endif // _SYSVAD_BTHHFPMINIPAIRS_H_ + diff --git a/audio/sysvad/EndpointsCommon/bthhfpminwavert.cpp b/audio/sysvad/EndpointsCommon/bthhfpminwavert.cpp new file mode 100644 index 000000000..77f12e410 --- /dev/null +++ b/audio/sysvad/EndpointsCommon/bthhfpminwavert.cpp @@ -0,0 +1,206 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + bthhfpminwavert.cpp + +Abstract: + + Implementation of wavert miniport. + +--*/ +#ifdef SYSVAD_BTH_BYPASS + +#pragma warning (disable : 4127) + +#include +#include +#include "simple.h" +#include "minwavert.h" +#include "minwavertstream.h" +#include "IHVPrivatePropertySet.h" + + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CMiniportWaveRT::PropertyHandler_BthHfpAudioEffectsDiscoveryEffectsList +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_AudioEffectsDiscovery, KSPROPERTY_AUDIOEFFECTSDISCOVERY_EFFECTSLIST ) + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + DPF_ENTER(("[PropertyHandler_BthHfpAudioEffectsDiscoveryEffectsList]")); + + NTSTATUS ntStatus = STATUS_INVALID_PARAMETER; + ULONG nPinId = (ULONG)-1; + + // + // Validate Pin ID. + // + if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + nPinId = *(PULONG(PropertyRequest->Instance)); + + // This prop is valid only on streaming pins. + if (IsSystemRenderPin(nPinId) || IsSystemCapturePin(nPinId)) + { + ntStatus = STATUS_SUCCESS; + } + else if (IsBridgePin(nPinId) || + IsLoopbackPin(nPinId) || + IsOffloadPin(nPinId)) + { + ntStatus = STATUS_NOT_SUPPORTED; + } + } + + IF_FAILED_JUMP(ntStatus, Done); + + // + // Valid actions: get and basicsupport. + // + ntStatus = STATUS_INVALID_PARAMETER; + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + ASSERT(m_BthHfpDevice != NULL); + + // + // bHfpNrecPresent is set to TRUE when the HF (remote device) handles NR + EC. + // Note that GetNRECDisableStatus() returns TRUE if the HF notified the AG to disable + // the NR + EC locally. + // + BOOL bHfpNrecPresent = m_BthHfpDevice->IsNRECSupported() && m_BthHfpDevice->GetNRECDisableStatus(); + + // If it is HFP render pin (data flow in) or if the NR-EC is not supported, + // return size 0 effect list + if (IsSystemRenderPin(nPinId) || !bHfpNrecPresent) + { + PropertyRequest->ValueSize = 0; + ntStatus = STATUS_SUCCESS; + } + else + { + // Compute total size, two effects: NS and EC (see below). + ULONG cbMinSize = sizeof(GUID) * 2; + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbMinSize; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbMinSize) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + PGUID effectList = PGUID(PropertyRequest->Value); + + *effectList = AUDIO_EFFECT_TYPE_ACOUSTIC_ECHO_CANCELLATION; + *(effectList + 1) = AUDIO_EFFECT_TYPE_NOISE_SUPPRESSION; + + PropertyRequest->ValueSize = cbMinSize; + ntStatus = STATUS_SUCCESS; + } + } + } + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +PropertyHandler_BthHfpWaveFilter +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Redirects general property request to miniport object + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + CMiniportWaveRT* waveRt = reinterpret_cast(PropertyRequest->MajorTarget); + + if (waveRt == NULL) + { + return STATUS_INVALID_PARAMETER; + } + + waveRt->AddRef(); + + if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_Pin)) + { + switch (PropertyRequest->PropertyItem->Id) + { + case KSPROPERTY_PIN_PROPOSEDATAFORMAT: + ntStatus = waveRt->PropertyHandlerProposedFormat(PropertyRequest); + break; + } + } + else if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_AudioEffectsDiscovery)) + { + switch(PropertyRequest->PropertyItem->Id) + { + case KSPROPERTY_AUDIOEFFECTSDISCOVERY_EFFECTSLIST: + ntStatus = waveRt->PropertyHandler_BthHfpAudioEffectsDiscoveryEffectsList(PropertyRequest); + break; + } + } + + waveRt->Release(); + + return ntStatus; +} // PropertyHandler_BthHfpWaveFilter + +#pragma code_seg("PAGE") +#endif // SYSVAD_BTH_BYPASS + + + diff --git a/audio/sysvad/EndpointsCommon/bthhfpspeakertopo.cpp b/audio/sysvad/EndpointsCommon/bthhfpspeakertopo.cpp new file mode 100644 index 000000000..8697d973d --- /dev/null +++ b/audio/sysvad/EndpointsCommon/bthhfpspeakertopo.cpp @@ -0,0 +1,281 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + bthhfpspeakertopo.cpp + +Abstract: + + Implementation of topology miniport for the Bluetooth Hands-Free Profile speaker (external). + +--*/ +#ifdef SYSVAD_BTH_BYPASS + +#pragma warning (disable : 4127) + +#include +#include "simple.h" +#include "mintopo.h" +#include "bthhfptopo.h" +#include "bthhfpspeakertopo.h" +#include "bthhfpspeakertoptable.h" + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +PropertyHandler_BthHfpSpeakerTopoFilter +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Redirects property request to miniport object + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_BthHfpSpeakerTopoFilter]")); + + // PropertryRequest structure is filled by portcls. + // MajorTarget is a pointer to miniport object for miniports. + // + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + // + // This line shows how to get a pointer to the miniport topology object. + // + PCMiniportTopology pMiniport = (PCMiniportTopology)PropertyRequest->MajorTarget; + UNREFERENCED_VAR(pMiniport); + + if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_Jack)) + { + switch(PropertyRequest->PropertyItem->Id) + { + case KSPROPERTY_JACK_DESCRIPTION: + ntStatus = PropertyHandler_BthHfpJackDescription( + PropertyRequest, + ARRAYSIZE(BthHfpSpeakerJackDescriptions), + BthHfpSpeakerJackDescriptions); + break; + + case KSPROPERTY_JACK_DESCRIPTION2: + ntStatus = PropertyHandler_BthHfpJackDescription2( + PropertyRequest, + ARRAYSIZE(BthHfpSpeakerJackDescriptions), + BthHfpSpeakerJackDescriptions); + break; + + case KSPROPERTY_JACK_CONTAINERID: + ntStatus = PropertyHandler_BthHfpJackContainerId( + PropertyRequest, + ARRAYSIZE(BthHfpSpeakerJackDescriptions), + BthHfpSpeakerJackDescriptions); + break; + } + } + else if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_BtAudio)) + { + switch(PropertyRequest->PropertyItem->Id) + { + case KSPROPERTY_ONESHOT_RECONNECT: + ntStatus = PropertyHandler_BthHfpOneShotReconnect(PropertyRequest); + break; + + case KSPROPERTY_ONESHOT_DISCONNECT: + ntStatus = PropertyHandler_BthHfpOneDisconnect(PropertyRequest); + break; + } + } + + return ntStatus; +} // PropertyHandler_BthHfpSpeakerTopoFilter + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +PropertyHandler_BthHfpSpeakerVolumeLevel +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_Audio, KSPROPERTY_AUDIO_VOLUMELEVEL ) + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_BthHfpSpeakerVolumeLevel]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + PCMiniportTopology miniport = (PCMiniportTopology)PropertyRequest->MajorTarget; + PBTHHFPDEVICECOMMON bthHfpDevice = NULL; + ULONG channel = (ULONG)-1; + + bthHfpDevice = miniport->GetBthHfpDevice(); // weak ref. + ASSERT(bthHfpDevice != NULL); + + if (bthHfpDevice->IsVolumeSupported() == FALSE) + { + ntStatus = miniport->PropertyHandlerGeneric(PropertyRequest); + } + else if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = PropertyHandler_BthHfpVolumeLevel_BasicSupport(PropertyRequest); + } + else if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + // Instance is the channel #, Bluetooth HFP supports mono streams. + channel = *(PULONG(PropertyRequest->Instance)); + + if (channel == 0) + { + ULONG cbNeeded = sizeof(LONG); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbNeeded; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbNeeded) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + LONG* volume = (LONG*)PropertyRequest->Value; + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + *volume = bthHfpDevice->GetSpeakerVolume(); + + ntStatus = STATUS_SUCCESS; + + } + else if (PropertyRequest->Verb & KSPROPERTY_TYPE_SET) + { + ntStatus = bthHfpDevice->SetSpeakerVolume(*volume); + } + } + } + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +PropertyHandler_BthHfpSpeakerTopoNode +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Redirects property request to miniport object + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_BthHfpSpeakerTopoNode]")); + + // PropertryRequest structure is filled by portcls. + // MajorTarget is a pointer to miniport object for miniports. + // + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + // + // This line shows how to get a pointer to the miniport topology object. + // + PCMiniportTopology pMiniport = (PCMiniportTopology)PropertyRequest->MajorTarget; + UNREFERENCED_VAR(pMiniport); + + if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_Audio)) + { + if (PropertyRequest->PropertyItem->Id == KSPROPERTY_AUDIO_VOLUMELEVEL) + { + ntStatus = PropertyHandler_BthHfpSpeakerVolumeLevel(PropertyRequest); + } + } + + return ntStatus; +} // PropertyHandler_BthHfpSpeakerTopoFilter + +//============================================================================= +#pragma code_seg() +NTSTATUS +PropertyHandler_BthHfpSpeakerTopoNodeEvent +( + _In_ PPCEVENT_REQUEST EventRequest +) +{ + ASSERT(EventRequest); + + DPF_ENTER(("[PropertyHandler_BthHfpSpeakerTopoNodeEvent]")); + + // Validate the node. + if (EventRequest->Node != KSNODE_TOPO_VOLUME) + return STATUS_INVALID_PARAMETER; + + return PropertyHandler_BthHfpTopoNodeEvent(EventRequest); +} + +//============================================================================= +#pragma code_seg() +NTSTATUS +PropertyHandler_BthHfpSpeakerTopoFilterEvent +( + _In_ PPCEVENT_REQUEST EventRequest +) +{ + ASSERT(EventRequest); + + DPF_ENTER(("[PropertyHandler_BthHfpSpeakerTopoFilterEvent]")); + + return PropertyHandler_BthHfpTopoNodeEvent(EventRequest); +} + +#pragma code_seg() +#endif // SYSVAD_BTH_BYPASS + + diff --git a/audio/sysvad/EndpointsCommon/bthhfpspeakertopo.h b/audio/sysvad/EndpointsCommon/bthhfpspeakertopo.h new file mode 100644 index 000000000..3ef8482d3 --- /dev/null +++ b/audio/sysvad/EndpointsCommon/bthhfpspeakertopo.h @@ -0,0 +1,41 @@ + +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + bthhfpspeakertopo.h + +Abstract: + + Declaration of topology miniport for the Bluetooth Hands-Free Profile speaker (external). + +--*/ + +#ifndef _SYSVAD_BTHHFPSPEAKERTOPO_H_ +#define _SYSVAD_BTHHFPSPEAKERTOPO_H_ + +// Function declarations. +NTSTATUS +PropertyHandler_BthHfpSpeakerTopoFilter( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + +NTSTATUS +PropertyHandler_BthHfpSpeakerTopoNode( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + +NTSTATUS +PropertyHandler_BthHfpSpeakerTopoNodeEvent( + _In_ PPCEVENT_REQUEST EventRequest + ); + +NTSTATUS +PropertyHandler_BthHfpSpeakerTopoFilterEvent( + _In_ PPCEVENT_REQUEST EventRequest + ); + +#endif // _SYSVAD_BTHHFPSPEAKERTOPO_H_ + diff --git a/audio/sysvad/EndpointsCommon/bthhfpspeakertoptable.h b/audio/sysvad/EndpointsCommon/bthhfpspeakertoptable.h new file mode 100644 index 000000000..48b66f030 --- /dev/null +++ b/audio/sysvad/EndpointsCommon/bthhfpspeakertoptable.h @@ -0,0 +1,275 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + bthhfpspeakertoptable.h + +Abstract: + + Declaration of topology tables for the Bluetooth Hands-Free Profile speaker (external). + +--*/ + +#ifndef _SYSVAD_BTHHFPSPEAKERTOPTABLE_H_ +#define _SYSVAD_BTHHFPSPEAKERTOPTABLE_H_ + +#include "bthhfpspeakertopo.h" + +//============================================================================= +static +KSDATARANGE BthHfpSpeakerTopoPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +//============================================================================= +static +PKSDATARANGE BthHfpSpeakerTopoPinDataRangePointersBridge[] = +{ + &BthHfpSpeakerTopoPinDataRangesBridge[0] +}; + +//============================================================================= +static +PCPIN_DESCRIPTOR BthHfpSpeakerTopoMiniportPins[] = +{ + // KSPIN_TOPO_WAVEOUT_SOURCE + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(BthHfpSpeakerTopoPinDataRangePointersBridge),// DataRangesCount + BthHfpSpeakerTopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_IN, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSCATEGORY_AUDIO, // Category + NULL, // Name + 0 // Reserved + } + }, + // KSPIN_TOPO_LINEOUT_DEST + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(BthHfpSpeakerTopoPinDataRangePointersBridge),// DataRangesCount + BthHfpSpeakerTopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_OUT, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSNODETYPE_HEADSET_SPEAKERS, // Category + NULL, // Name + 0 // Reserved + } + } +}; + +//============================================================================= +static +KSJACK_DESCRIPTION BthHfpSpeakerJackDesc = +{ + KSAUDIO_SPEAKER_MONO, + 0, // color + eConnTypeOtherDigital, + eGeoLocNotApplicable, + eGenLocOther, + ePortConnUnknown, + FALSE // run-time code sets this value. +}; + +// Only return a KSJACK_DESCRIPTION for the physical bridge pin. +static +PKSJACK_DESCRIPTION BthHfpSpeakerJackDescriptions[] = +{ + NULL, + &BthHfpSpeakerJackDesc +}; + +//============================================================================= +static +PCPROPERTY_ITEM BthHfpSpeakerPropertiesVolume[] = +{ + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_VOLUMELEVEL, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_BthHfpSpeakerTopoNode + } +}; + +static +PCEVENT_ITEM BthHfpSpeakerPropertiesVolumeEvent[] = +{ + { + &KSEVENTSETID_AudioControlChange, // Something changed! + KSEVENT_CONTROL_CHANGE, // The only event-property defined. + KSEVENT_TYPE_ENABLE | KSEVENT_TYPE_BASICSUPPORT, + PropertyHandler_BthHfpSpeakerTopoNodeEvent + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP_EVENT(AutomationBthHfpSpeakerVolumeWithEvent, + BthHfpSpeakerPropertiesVolume, + BthHfpSpeakerPropertiesVolumeEvent); + +//============================================================================= +static +PCPROPERTY_ITEM BthHfpSpeakerPropertiesMute[] = +{ + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_MUTE, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_Topology + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationBthHfpSpeakerMute, BthHfpSpeakerPropertiesMute); + +//============================================================================= +// The volume node should be implemented as follows: +// +// #1 If the Bluetooth headset supports volume control, then the audio driver should +// include a volume node in its KS topology. The audio driver's volume property +// handlers send the above IOCLTs to the Bluetooth HFP driver to handle the volume. +// +// #2 If the Bluetooth headset does not implement a hardware volume, and the codec +// (or DSP) has a hardware volume, the audio driver should handle the volume control +// on the codec (or DSP). +// +// #3 If the Bluetooth headset and the audio device do not have hardware volume controls, +// no volume node should be presented and Windows will insert a software volume +// control node. +// +// This sample assumes #1 or #2, and it adds a valume node in its topology. +// +// The mute node is opitional. The driver shall implement the mute node if and only if +// the DSP or audio codec provides the capability to mute the bypass PCM signal before +// passing it to the Bluetooth controller. The mute nodes support +// KSPROPSETID_Audio/KSPROPERTY_AUDIO_MUTE +// +static +PCNODE_DESCRIPTOR BthHfpSpeakerTopologyNodes[] = +{ + // KSNODE_TOPO_VOLUME + { + 0, // Flags + &AutomationBthHfpSpeakerVolumeWithEvent, // AutomationTable + &KSNODETYPE_VOLUME, // Type + &KSAUDFNAME_LINE_VOLUME // Name + }, + // KSNODE_TOPO_MUTE + { + 0, // Flags + &AutomationBthHfpSpeakerMute, // AutomationTable + &KSNODETYPE_MUTE, // Type + &KSAUDFNAME_LINE_MUTE // Name + }, +}; + +C_ASSERT( KSNODE_TOPO_VOLUME == 0 ); +C_ASSERT( KSNODE_TOPO_MUTE == 1 ); + +//============================================================================= +static +PCCONNECTION_DESCRIPTOR BthHfpSpeakerTopoMiniportConnections[] = +{ + // FromNode, FromPin, ToNode, ToPin + { PCFILTER_NODE, KSPIN_TOPO_WAVEOUT_SOURCE, KSNODE_TOPO_VOLUME, 1 }, + { KSNODE_TOPO_VOLUME, 0, KSNODE_TOPO_MUTE, 1 }, + { KSNODE_TOPO_MUTE, 0, PCFILTER_NODE, KSPIN_TOPO_LINEOUT_DEST } +}; + +//============================================================================= +static +PCPROPERTY_ITEM BthHfpSpeakerPropertiesTopoFilter[] = +{ + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_BthHfpSpeakerTopoFilter + }, + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION2, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_BthHfpSpeakerTopoFilter + }, + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_CONTAINERID, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_BthHfpSpeakerTopoFilter + }, + { + &KSPROPSETID_BtAudio, + KSPROPERTY_ONESHOT_RECONNECT, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_BthHfpSpeakerTopoFilter + }, + { + &KSPROPSETID_BtAudio, + KSPROPERTY_ONESHOT_DISCONNECT, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_BthHfpSpeakerTopoFilter + } +}; + +static +PCEVENT_ITEM BthHfpSpeakerPropertiesTopoFilterEvent[] = +{ + { + &KSEVENTSETID_PinCapsChange, // Something changed! + KSEVENT_PINCAPS_JACKINFOCHANGE, // The only event-property defined. + KSEVENT_TYPE_ENABLE | KSEVENT_TYPE_BASICSUPPORT, + PropertyHandler_BthHfpSpeakerTopoFilterEvent + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP_EVENT(AutomationBthHfpSpeakerTopoFilterWithEvent, + BthHfpSpeakerPropertiesTopoFilter, + BthHfpSpeakerPropertiesTopoFilterEvent); + +//============================================================================= +static +PCFILTER_DESCRIPTOR BthHfpSpeakerTopoMiniportFilterDescriptor = +{ + 0, // Version + &AutomationBthHfpSpeakerTopoFilterWithEvent, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(BthHfpSpeakerTopoMiniportPins), // PinCount + BthHfpSpeakerTopoMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + SIZEOF_ARRAY(BthHfpSpeakerTopologyNodes), // NodeCount + BthHfpSpeakerTopologyNodes, // Nodes + SIZEOF_ARRAY(BthHfpSpeakerTopoMiniportConnections), // ConnectionCount + BthHfpSpeakerTopoMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories +}; + +#endif // _SYSVAD_BTHHFPSPEAKERTOPTABLE_H_ + + diff --git a/audio/sysvad/EndpointsCommon/bthhfpspeakerwavtable.h b/audio/sysvad/EndpointsCommon/bthhfpspeakerwavtable.h new file mode 100644 index 000000000..bab3d38ab --- /dev/null +++ b/audio/sysvad/EndpointsCommon/bthhfpspeakerwavtable.h @@ -0,0 +1,609 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + bthhfpspeakerwavtable.h + +Abstract: + + Declaration of wave miniport tables for the Bluetooth Hands-Free Profile speaker (external). + +--*/ + +#ifndef _SYSVAD_BTHHFPSPEAKERWAVTABLE_H_ +#define _SYSVAD_BTHHFPSPEAKERWAVTABLE_H_ + +// +// Function prototypes. +// +NTSTATUS PropertyHandler_BthHfpWaveFilter(_In_ PPCPROPERTY_REQUEST PropertyRequest); + + + +#define BTHHFPSPEAKER_DEVICE_MAX_CHANNELS 1 // Max Channels. + +#define BTHHFPSPEAKER_HOST_MAX_CHANNELS 1 // Max Channels. +#define BTHHFPSPEAKER_HOST_MIN_BITS_PER_SAMPLE 8 // Min Bits Per Sample +#define BTHHFPSPEAKER_HOST_MAX_BITS_PER_SAMPLE 16 // Max Bits Per Sample +#define BTHHFPSPEAKER_HOST_MIN_SAMPLE_RATE 8000 // Min Sample Rate +#define BTHHFPSPEAKER_HOST_MAX_SAMPLE_RATE 8000 // Max Sample Rate + +#define BTHHFPSPEAKER_OFFLOAD_MAX_CHANNELS 1 // Max Channels. +#define BTHHFPSPEAKER_OFFLOAD_MIN_BITS_PER_SAMPLE 8 // Min Bits Per Sample +#define BTHHFPSPEAKER_OFFLOAD_MAX_BITS_PER_SAMPLE 16 // Max Bits Per Sample +#define BTHHFPSPEAKER_OFFLOAD_MIN_SAMPLE_RATE 8000 // Min Sample Rate +#define BTHHFPSPEAKER_OFFLOAD_MAX_SAMPLE_RATE 8000 // Max Sample Rate + +#define BTHHFPSPEAKER_LOOPBACK_MAX_CHANNELS 1 // Max Channels. +#define BTHHFPSPEAKER_LOOPBACK_MIN_BITS_PER_SAMPLE 8 // Min Bits Per Sample +#define BTHHFPSPEAKER_LOOPBACK_MAX_BITS_PER_SAMPLE 16 // Max Bits Per Sample +#define BTHHFPSPEAKER_LOOPBACK_MIN_SAMPLE_RATE 8000 // Min Sample Rate +#define BTHHFPSPEAKER_LOOPBACK_MAX_SAMPLE_RATE 8000 // Max Sample Rate + +#define BTHHFPSPEAKER_DOLBY_DIGITAL_MAX_CHANNELS 1 // Max Channels. +#define BTHHFPSPEAKER_DOLBY_DIGITAL_MIN_BITS_PER_SAMPLE 8 // Min Bits Per Sample +#define BTHHFPSPEAKER_DOLBY_DIGITAL_MAX_BITS_PER_SAMPLE 16 // Max Bits Per Sample +#define BTHHFPSPEAKER_DOLBY_DIGITAL_MIN_SAMPLE_RATE 8000 // Min Sample Rate +#define BTHHFPSPEAKER_DOLBY_DIGITAL_MAX_SAMPLE_RATE 8000 // Max Sample Rate + + +//============================================================================= +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE BthHfpSpeakerAudioEngineSupportedDeviceFormats[] = +{ + { // 0 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 8000, + 8000, + 1, + 8, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 8, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 1 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 8000, + 16000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE BthHfpSpeakerHostPinSupportedDeviceFormats[] = +{ + { // 0 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 8000, + 8000, + 1, + 8, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 8, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 1 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 8000, + 16000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE BthHfpSpeakerOffloadPinSupportedDeviceFormats[] = +{ + { // 0 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 8000, + 8000, + 1, + 8, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 8, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 1 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 8000, + 16000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE BthHfpSpeakerLoopbackPinSupportedDeviceFormats[] = +{ + { // 0 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 8000, + 8000, + 1, + 8, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 8, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 1 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 8000, + 16000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +// +// Supported modes (only on streaming pins). +// +static +MODE_AND_DEFAULT_FORMAT BthHfpSpeakerHostPinSupportedDeviceModes[] = +{ + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_RAW, + &BthHfpSpeakerHostPinSupportedDeviceFormats[SIZEOF_ARRAY(BthHfpSpeakerHostPinSupportedDeviceFormats)-1].DataFormat + } +}; + +static +MODE_AND_DEFAULT_FORMAT BthHfpSpeakerOffloadPinSupportedDeviceModes[] = +{ + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_RAW, + &BthHfpSpeakerHostPinSupportedDeviceFormats[SIZEOF_ARRAY(BthHfpSpeakerHostPinSupportedDeviceFormats)-1].DataFormat + } +}; + +// +// The entries here must follow the same order as the filter's pin +// descriptor array. +// +static +PIN_DEVICE_FORMATS_AND_MODES BthHfpSpeakerPinDeviceFormatsAndModes[] = +{ + { + SystemRenderPin, + BthHfpSpeakerHostPinSupportedDeviceFormats, + SIZEOF_ARRAY(BthHfpSpeakerHostPinSupportedDeviceFormats), + BthHfpSpeakerHostPinSupportedDeviceModes, + SIZEOF_ARRAY(BthHfpSpeakerHostPinSupportedDeviceModes) + }, + { + OffloadRenderPin, + BthHfpSpeakerOffloadPinSupportedDeviceFormats, + SIZEOF_ARRAY(BthHfpSpeakerOffloadPinSupportedDeviceFormats), + BthHfpSpeakerOffloadPinSupportedDeviceModes, + SIZEOF_ARRAY(BthHfpSpeakerOffloadPinSupportedDeviceModes), + }, + { + RenderLoopbackPin, + BthHfpSpeakerLoopbackPinSupportedDeviceFormats, + SIZEOF_ARRAY(BthHfpSpeakerLoopbackPinSupportedDeviceFormats), + NULL, // loopback doesn't support modes. + 0 + }, + { + BridgePin, + NULL, + 0, + NULL, + 0 + }, + { + NoPin, // For convenience, offload engine device formats appended here + BthHfpSpeakerAudioEngineSupportedDeviceFormats, + SIZEOF_ARRAY(BthHfpSpeakerAudioEngineSupportedDeviceFormats), + NULL, // no modes for this entry. + 0 + } +}; + +//============================================================================= +static +KSDATARANGE_AUDIO BthHfpSpeakerPinDataRangesStream[] = +{ + { // 0 + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + BTHHFPSPEAKER_HOST_MAX_CHANNELS, + BTHHFPSPEAKER_HOST_MIN_BITS_PER_SAMPLE, + BTHHFPSPEAKER_HOST_MAX_BITS_PER_SAMPLE, + BTHHFPSPEAKER_HOST_MIN_SAMPLE_RATE, + BTHHFPSPEAKER_HOST_MAX_SAMPLE_RATE + }, + { // 1 + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + BTHHFPSPEAKER_OFFLOAD_MAX_CHANNELS, + BTHHFPSPEAKER_OFFLOAD_MIN_BITS_PER_SAMPLE, + BTHHFPSPEAKER_OFFLOAD_MAX_BITS_PER_SAMPLE, + BTHHFPSPEAKER_OFFLOAD_MIN_SAMPLE_RATE, + BTHHFPSPEAKER_OFFLOAD_MAX_SAMPLE_RATE + }, + { // 2 + { + sizeof(KSDATARANGE_AUDIO), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + BTHHFPSPEAKER_LOOPBACK_MAX_CHANNELS, + BTHHFPSPEAKER_LOOPBACK_MIN_BITS_PER_SAMPLE, + BTHHFPSPEAKER_LOOPBACK_MAX_BITS_PER_SAMPLE, + BTHHFPSPEAKER_LOOPBACK_MIN_SAMPLE_RATE, + BTHHFPSPEAKER_LOOPBACK_MAX_SAMPLE_RATE + } +}; + +static +PKSDATARANGE BthHfpSpeakerPinDataRangePointersStream[] = +{ + PKSDATARANGE(&BthHfpSpeakerPinDataRangesStream[0]), + PKSDATARANGE(&PinDataRangeAttributeList) +}; + +static +PKSDATARANGE BthHfpSpeakerPinDataRangePointersOffloadStream[] = +{ + PKSDATARANGE(&BthHfpSpeakerPinDataRangesStream[1]), + PKSDATARANGE(&PinDataRangeAttributeList) +}; + +static +PKSDATARANGE BthHfpSpeakerPinDataRangePointersLoopbackStream[] = +{ + PKSDATARANGE(&BthHfpSpeakerPinDataRangesStream[2]) +}; + +//============================================================================= +static +KSDATARANGE BthHfpSpeakerPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +static +PKSDATARANGE BthHfpSpeakerPinDataRangePointersBridge[] = +{ + &BthHfpSpeakerPinDataRangesBridge[0] +}; + +//============================================================================= + +static +PCPROPERTY_ITEM PropertiesBthHfpSpeakerOffloadPin[] = +{ + { + &KSPROPSETID_OffloadPin, // define new property set + KSPROPERTY_OFFLOAD_PIN_GET_STREAM_OBJECT_POINTER, // define properties + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_OffloadPin + }, + { + &KSPROPSETID_OffloadPin, // define new property set + KSPROPERTY_OFFLOAD_PIN_VERIFY_STREAM_OBJECT_POINTER, // define properties + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_OffloadPin + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationBthHfpSpeakerOffloadPin, PropertiesBthHfpSpeakerOffloadPin); + +//============================================================================= +static +PCPIN_DESCRIPTOR BthHfpSpeakerWaveMiniportPins[] = +{ + // Wave Out Streaming Pin (Renderer) KSPIN_WAVE_RENDER_SINK_SYSTEM + { + MAX_INPUT_SYSTEM_STREAMS, + MAX_INPUT_SYSTEM_STREAMS, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(BthHfpSpeakerPinDataRangePointersStream), + BthHfpSpeakerPinDataRangePointersStream, + KSPIN_DATAFLOW_IN, + KSPIN_COMMUNICATION_SINK, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, + // Wave Out Streaming Pin (Renderer) KSPIN_WAVE_RENDER_SINK_OFFLOAD + { + MAX_INPUT_OFFLOAD_STREAMS, + MAX_INPUT_OFFLOAD_STREAMS, + 0, + &AutomationBthHfpSpeakerOffloadPin, // AutomationTable + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(BthHfpSpeakerPinDataRangePointersOffloadStream), + BthHfpSpeakerPinDataRangePointersOffloadStream, + KSPIN_DATAFLOW_IN, + KSPIN_COMMUNICATION_SINK, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, + // Wave Out Streaming Pin (Renderer) KSPIN_WAVE_RENDER_SINK_LOOPBACK + { + MAX_OUTPUT_LOOPBACK_STREAMS, + MAX_OUTPUT_LOOPBACK_STREAMS, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(BthHfpSpeakerPinDataRangePointersLoopbackStream), + BthHfpSpeakerPinDataRangePointersLoopbackStream, + KSPIN_DATAFLOW_OUT, + KSPIN_COMMUNICATION_SINK, + &KSNODETYPE_AUDIO_LOOPBACK, + NULL, + 0 + } + }, + // Wave Out Bridge Pin (Renderer) KSPIN_WAVE_RENDER_SOURCE + { + 0, + 0, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(BthHfpSpeakerPinDataRangePointersBridge), + BthHfpSpeakerPinDataRangePointersBridge, + KSPIN_DATAFLOW_OUT, + KSPIN_COMMUNICATION_NONE, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, +}; + +//============================================================================= +static +PCNODE_DESCRIPTOR BthHfpSpeakerWaveMiniportNodes[] = +{ + // KSNODE_WAVE_AUDIO_ENGINE + { + 0, // Flags + NULL, // AutomationTable + &KSNODETYPE_AUDIO_ENGINE, // Type KSNODETYPE_AUDIO_ENGINE + NULL // Name + } +}; +//============================================================================= +// +// ---------------------------- +// | | +// System Pin 0-->| |--> 2 Loopback Pin +// | HW Audio Engine node | +// Offload Pin 1-->| |--> 3 KSPIN_WAVE_RENDER_SOURCE +// | | +// ---------------------------- +static +PCCONNECTION_DESCRIPTOR BthHfpSpeakerWaveMiniportConnections[] = +{ + { PCFILTER_NODE, KSPIN_WAVE_RENDER_SINK_SYSTEM, KSNODE_WAVE_AUDIO_ENGINE, 1 }, + { PCFILTER_NODE, KSPIN_WAVE_RENDER_SINK_OFFLOAD, KSNODE_WAVE_AUDIO_ENGINE, 2 }, + { KSNODE_WAVE_AUDIO_ENGINE, 3, PCFILTER_NODE, KSPIN_WAVE_RENDER_SINK_LOOPBACK }, + { KSNODE_WAVE_AUDIO_ENGINE, 0, PCFILTER_NODE, KSPIN_WAVE_RENDER_SOURCE }, +}; + +//============================================================================= +static +PCPROPERTY_ITEM PropertiesBthHfpSpeakerWaveFilter[] = +{ + { + &KSPROPSETID_Pin, + KSPROPERTY_PIN_PROPOSEDATAFORMAT, + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_BthHfpWaveFilter + }, + { + &KSPROPSETID_AudioEffectsDiscovery, + KSPROPERTY_AUDIOEFFECTSDISCOVERY_EFFECTSLIST, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_BthHfpWaveFilter + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationBthHfpSpeakerWaveFilter, PropertiesBthHfpSpeakerWaveFilter); + +//============================================================================= +static +PCFILTER_DESCRIPTOR BthHfpSpeakerWaveMiniportFilterDescriptor = +{ + 0, // Version + &AutomationBthHfpSpeakerWaveFilter, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(BthHfpSpeakerWaveMiniportPins), // PinCount + BthHfpSpeakerWaveMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + SIZEOF_ARRAY(BthHfpSpeakerWaveMiniportNodes), // NodeCount + BthHfpSpeakerWaveMiniportNodes, // Nodes + SIZEOF_ARRAY(BthHfpSpeakerWaveMiniportConnections),// ConnectionCount + BthHfpSpeakerWaveMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories - use defaults (audio, render, capture) +}; + +#endif // _SYSVAD_BTHHFPSPEAKERWAVTABLE_H_ + diff --git a/audio/sysvad/EndpointsCommon/bthhfpspeakerwbwavtable.h b/audio/sysvad/EndpointsCommon/bthhfpspeakerwbwavtable.h new file mode 100644 index 000000000..1c4910229 --- /dev/null +++ b/audio/sysvad/EndpointsCommon/bthhfpspeakerwbwavtable.h @@ -0,0 +1,862 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + bthhfpspeakerwbwavtable.h + +Abstract: + + Declaration of wave miniport tables for the Bluetooth Hands-Free Profile speaker (external), + for Bluetooth connections that support Wideband Speech. + +--*/ + +#ifndef _SYSVAD_BTHHFPSPEAKERWBWAVTABLE_H_ +#define _SYSVAD_BTHHFPSPEAKERWBWAVTABLE_H_ + +// +// Function prototypes. +// +NTSTATUS PropertyHandler_BthHfpWaveFilter(_In_ PPCPROPERTY_REQUEST PropertyRequest); + + + +#define BTHHFPSPEAKERWB_DEVICE_MAX_CHANNELS 1 // Max Channels. + +#define BTHHFPSPEAKERWB_HOST_MAX_CHANNELS 1 // Max Channels. +#define BTHHFPSPEAKERWB_HOST_MIN_BITS_PER_SAMPLE 8 // Min Bits Per Sample +#define BTHHFPSPEAKERWB_HOST_MAX_BITS_PER_SAMPLE 16 // Max Bits Per Sample +#define BTHHFPSPEAKERWB_HOST_MIN_SAMPLE_RATE 8000 // Min Sample Rate +#define BTHHFPSPEAKERWB_HOST_MAX_SAMPLE_RATE 16000 // Max Sample Rate + +#define BTHHFPSPEAKERWB_OFFLOAD_MAX_CHANNELS 1 // Max Channels. +#define BTHHFPSPEAKERWB_OFFLOAD_MIN_BITS_PER_SAMPLE 8 // Min Bits Per Sample +#define BTHHFPSPEAKERWB_OFFLOAD_MAX_BITS_PER_SAMPLE 16 // Max Bits Per Sample +#define BTHHFPSPEAKERWB_OFFLOAD_MIN_SAMPLE_RATE 8000 // Min Sample Rate +#define BTHHFPSPEAKERWB_OFFLOAD_MAX_SAMPLE_RATE 16000 // Max Sample Rate + +#define BTHHFPSPEAKERWB_LOOPBACK_MAX_CHANNELS 1 // Max Channels. +#define BTHHFPSPEAKERWB_LOOPBACK_MIN_BITS_PER_SAMPLE 8 // Min Bits Per Sample +#define BTHHFPSPEAKERWB_LOOPBACK_MAX_BITS_PER_SAMPLE 16 // Max Bits Per Sample +#define BTHHFPSPEAKERWB_LOOPBACK_MIN_SAMPLE_RATE 8000 // Min Sample Rate +#define BTHHFPSPEAKERWB_LOOPBACK_MAX_SAMPLE_RATE 16000 // Max Sample Rate + +#define BTHHFPSPEAKERWB_DOLBY_DIGITAL_MAX_CHANNELS 1 // Max Channels. +#define BTHHFPSPEAKERWB_DOLBY_DIGITAL_MIN_BITS_PER_SAMPLE 8 // Min Bits Per Sample +#define BTHHFPSPEAKERWB_DOLBY_DIGITAL_MAX_BITS_PER_SAMPLE 16 // Max Bits Per Sample +#define BTHHFPSPEAKERWB_DOLBY_DIGITAL_MIN_SAMPLE_RATE 8000 // Min Sample Rate +#define BTHHFPSPEAKERWB_DOLBY_DIGITAL_MAX_SAMPLE_RATE 16000 // Max Sample Rate + + +//============================================================================= +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE BthHfpSpeakerWBAudioEngineSupportedDeviceFormats[] = +{ + { // 0 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 8000, + 8000, + 1, + 8, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 8, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 1 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 8000, + 16000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 2 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 16000, + 16000, + 1, + 8, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 8, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 3 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 16000, + 32000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE BthHfpSpeakerWBHostPinSupportedDeviceFormats[] = +{ + { // 0 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 8000, + 8000, + 1, + 8, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 8, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 1 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 8000, + 16000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 2 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 16000, + 16000, + 1, + 8, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 8, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 3 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 16000, + 32000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE BthHfpSpeakerWBOffloadPinSupportedDeviceFormats[] = +{ + { // 0 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 8000, + 8000, + 1, + 8, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 8, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 1 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 8000, + 16000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 2 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 16000, + 16000, + 1, + 8, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 8, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 3 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 16000, + 32000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE BthHfpSpeakerWBLoopbackPinSupportedDeviceFormats[] = +{ + { // 0 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 8000, + 8000, + 1, + 8, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 8, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 1 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 8000, + 16000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 2 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 16000, + 16000, + 1, + 8, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 8, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 3 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 16000, + 32000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +// +// Supported modes (only on streaming pins). +// +static +MODE_AND_DEFAULT_FORMAT BthHfpSpeakerWBHostPinSupportedDeviceModes[] = +{ + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_RAW, + &BthHfpSpeakerWBHostPinSupportedDeviceFormats[SIZEOF_ARRAY(BthHfpSpeakerWBHostPinSupportedDeviceFormats)-1].DataFormat + } +}; + +static +MODE_AND_DEFAULT_FORMAT BthHfpSpeakerWBOffloadPinSupportedDeviceModes[] = +{ + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_RAW, + &BthHfpSpeakerWBHostPinSupportedDeviceFormats[SIZEOF_ARRAY(BthHfpSpeakerWBHostPinSupportedDeviceFormats)-1].DataFormat + } +}; + +// +// The entries here must follow the same order as the filter's pin +// descriptor array. +// +static +PIN_DEVICE_FORMATS_AND_MODES BthHfpSpeakerWBPinDeviceFormatsAndModes[] = +{ + { + SystemRenderPin, + BthHfpSpeakerWBHostPinSupportedDeviceFormats, + SIZEOF_ARRAY(BthHfpSpeakerWBHostPinSupportedDeviceFormats), + BthHfpSpeakerWBHostPinSupportedDeviceModes, + SIZEOF_ARRAY(BthHfpSpeakerWBHostPinSupportedDeviceModes) + }, + { + OffloadRenderPin, + BthHfpSpeakerWBOffloadPinSupportedDeviceFormats, + SIZEOF_ARRAY(BthHfpSpeakerWBOffloadPinSupportedDeviceFormats), + BthHfpSpeakerWBOffloadPinSupportedDeviceModes, + SIZEOF_ARRAY(BthHfpSpeakerWBOffloadPinSupportedDeviceModes), + }, + { + RenderLoopbackPin, + BthHfpSpeakerWBLoopbackPinSupportedDeviceFormats, + SIZEOF_ARRAY(BthHfpSpeakerWBLoopbackPinSupportedDeviceFormats), + NULL, // loopback doesn't support modes. + 0 + }, + { + BridgePin, + NULL, + 0, + NULL, + 0 + }, + { + NoPin, // For convenience, offload engine device formats appended here + BthHfpSpeakerWBAudioEngineSupportedDeviceFormats, + SIZEOF_ARRAY(BthHfpSpeakerWBAudioEngineSupportedDeviceFormats), + NULL, // no modes for this entry. + 0 + } +}; + +//============================================================================= +static +KSDATARANGE_AUDIO BthHfpSpeakerWBPinDataRangesStream[] = +{ + { // 0 + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + BTHHFPSPEAKERWB_HOST_MAX_CHANNELS, + BTHHFPSPEAKERWB_HOST_MIN_BITS_PER_SAMPLE, + BTHHFPSPEAKERWB_HOST_MAX_BITS_PER_SAMPLE, + BTHHFPSPEAKERWB_HOST_MIN_SAMPLE_RATE, + BTHHFPSPEAKERWB_HOST_MIN_SAMPLE_RATE + }, + { // 1 + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + BTHHFPSPEAKERWB_HOST_MAX_CHANNELS, + BTHHFPSPEAKERWB_HOST_MIN_BITS_PER_SAMPLE, + BTHHFPSPEAKERWB_HOST_MAX_BITS_PER_SAMPLE, + BTHHFPSPEAKERWB_HOST_MAX_SAMPLE_RATE, + BTHHFPSPEAKERWB_HOST_MAX_SAMPLE_RATE + }, + { // 2 + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + BTHHFPSPEAKERWB_OFFLOAD_MAX_CHANNELS, + BTHHFPSPEAKERWB_OFFLOAD_MIN_BITS_PER_SAMPLE, + BTHHFPSPEAKERWB_OFFLOAD_MAX_BITS_PER_SAMPLE, + BTHHFPSPEAKERWB_OFFLOAD_MIN_SAMPLE_RATE, + BTHHFPSPEAKERWB_OFFLOAD_MIN_SAMPLE_RATE + }, + { // 3 + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + BTHHFPSPEAKERWB_OFFLOAD_MAX_CHANNELS, + BTHHFPSPEAKERWB_OFFLOAD_MIN_BITS_PER_SAMPLE, + BTHHFPSPEAKERWB_OFFLOAD_MAX_BITS_PER_SAMPLE, + BTHHFPSPEAKERWB_OFFLOAD_MAX_SAMPLE_RATE, + BTHHFPSPEAKERWB_OFFLOAD_MAX_SAMPLE_RATE + }, + { // 4 + { + sizeof(KSDATARANGE_AUDIO), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + BTHHFPSPEAKERWB_LOOPBACK_MAX_CHANNELS, + BTHHFPSPEAKERWB_LOOPBACK_MIN_BITS_PER_SAMPLE, + BTHHFPSPEAKERWB_LOOPBACK_MAX_BITS_PER_SAMPLE, + BTHHFPSPEAKERWB_LOOPBACK_MIN_SAMPLE_RATE, + BTHHFPSPEAKERWB_LOOPBACK_MIN_SAMPLE_RATE + }, + { // 5 + { + sizeof(KSDATARANGE_AUDIO), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + BTHHFPSPEAKERWB_LOOPBACK_MAX_CHANNELS, + BTHHFPSPEAKERWB_LOOPBACK_MIN_BITS_PER_SAMPLE, + BTHHFPSPEAKERWB_LOOPBACK_MAX_BITS_PER_SAMPLE, + BTHHFPSPEAKERWB_LOOPBACK_MAX_SAMPLE_RATE, + BTHHFPSPEAKERWB_LOOPBACK_MAX_SAMPLE_RATE + } +}; + +static +PKSDATARANGE BthHfpSpeakerWBPinDataRangePointersStream[] = +{ + PKSDATARANGE(&BthHfpSpeakerWBPinDataRangesStream[0]), + PKSDATARANGE(&PinDataRangeAttributeList), + PKSDATARANGE(&BthHfpSpeakerWBPinDataRangesStream[1]), + PKSDATARANGE(&PinDataRangeAttributeList) +}; + +static +PKSDATARANGE BthHfpSpeakerWBPinDataRangePointersOffloadStream[] = +{ + PKSDATARANGE(&BthHfpSpeakerWBPinDataRangesStream[2]), + PKSDATARANGE(&PinDataRangeAttributeList), + PKSDATARANGE(&BthHfpSpeakerWBPinDataRangesStream[3]), + PKSDATARANGE(&PinDataRangeAttributeList) +}; + +static +PKSDATARANGE BthHfpSpeakerWBPinDataRangePointersLoopbackStream[] = +{ + PKSDATARANGE(&BthHfpSpeakerWBPinDataRangesStream[4]), + PKSDATARANGE(&BthHfpSpeakerWBPinDataRangesStream[5]) +}; + +//============================================================================= +static +KSDATARANGE BthHfpSpeakerWBPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +static +PKSDATARANGE BthHfpSpeakerWBPinDataRangePointersBridge[] = +{ + &BthHfpSpeakerWBPinDataRangesBridge[0] +}; + +//============================================================================= + +static +PCPROPERTY_ITEM PropertiesBthHfpSpeakerWBOffloadPin[] = +{ + { + &KSPROPSETID_OffloadPin, // define new property set + KSPROPERTY_OFFLOAD_PIN_GET_STREAM_OBJECT_POINTER, // define properties + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_OffloadPin + }, + { + &KSPROPSETID_OffloadPin, // define new property set + KSPROPERTY_OFFLOAD_PIN_VERIFY_STREAM_OBJECT_POINTER, // define properties + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_OffloadPin + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationBthHfpSpeakerWBOffloadPin, PropertiesBthHfpSpeakerWBOffloadPin); + +//============================================================================= +static +PCPIN_DESCRIPTOR BthHfpSpeakerWBWaveMiniportPins[] = +{ + // Wave Out Streaming Pin (Renderer) KSPIN_WAVE_RENDER_SINK_SYSTEM + { + MAX_INPUT_SYSTEM_STREAMS, + MAX_INPUT_SYSTEM_STREAMS, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(BthHfpSpeakerWBPinDataRangePointersStream), + BthHfpSpeakerWBPinDataRangePointersStream, + KSPIN_DATAFLOW_IN, + KSPIN_COMMUNICATION_SINK, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, + // Wave Out Streaming Pin (Renderer) KSPIN_WAVE_RENDER_SINK_OFFLOAD + { + MAX_INPUT_OFFLOAD_STREAMS, + MAX_INPUT_OFFLOAD_STREAMS, + 0, + &AutomationBthHfpSpeakerWBOffloadPin, // AutomationTable + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(BthHfpSpeakerWBPinDataRangePointersOffloadStream), + BthHfpSpeakerWBPinDataRangePointersOffloadStream, + KSPIN_DATAFLOW_IN, + KSPIN_COMMUNICATION_SINK, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, + // Wave Out Streaming Pin (Renderer) KSPIN_WAVE_RENDER_SINK_LOOPBACK + { + MAX_OUTPUT_LOOPBACK_STREAMS, + MAX_OUTPUT_LOOPBACK_STREAMS, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(BthHfpSpeakerWBPinDataRangePointersLoopbackStream), + BthHfpSpeakerWBPinDataRangePointersLoopbackStream, + KSPIN_DATAFLOW_OUT, + KSPIN_COMMUNICATION_SINK, + &KSNODETYPE_AUDIO_LOOPBACK, + NULL, + 0 + } + }, + // Wave Out Bridge Pin (Renderer) KSPIN_WAVE_RENDER_SOURCE + { + 0, + 0, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(BthHfpSpeakerWBPinDataRangePointersBridge), + BthHfpSpeakerWBPinDataRangePointersBridge, + KSPIN_DATAFLOW_OUT, + KSPIN_COMMUNICATION_NONE, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, +}; +//============================================================================= +static +PCNODE_DESCRIPTOR BthHfpSpeakerWBWaveMiniportNodes[] = +{ + // KSNODE_WAVE_AUDIO_ENGINE + { + 0, // Flags + NULL, // AutomationTable + &KSNODETYPE_AUDIO_ENGINE, // Type KSNODETYPE_AUDIO_ENGINE + NULL // Name + } +}; +//============================================================================= +// +// ---------------------------- +// | | +// System Pin 0-->| |--> 2 Loopback Pin +// | HW Audio Engine node | +// Offload Pin 1-->| |--> 3 KSPIN_WAVE_RENDER_SOURCE +// | | +// ---------------------------- +static +PCCONNECTION_DESCRIPTOR BthHfpSpeakerWBWaveMiniportConnections[] = +{ + { PCFILTER_NODE, KSPIN_WAVE_RENDER_SINK_SYSTEM, KSNODE_WAVE_AUDIO_ENGINE, 1 }, + { PCFILTER_NODE, KSPIN_WAVE_RENDER_SINK_OFFLOAD, KSNODE_WAVE_AUDIO_ENGINE, 2 }, + { KSNODE_WAVE_AUDIO_ENGINE, 3, PCFILTER_NODE, KSPIN_WAVE_RENDER_SINK_LOOPBACK }, + { KSNODE_WAVE_AUDIO_ENGINE, 0, PCFILTER_NODE, KSPIN_WAVE_RENDER_SOURCE }, +}; + +//============================================================================= +static +PCPROPERTY_ITEM PropertiesBthHfpSpeakerWBWaveFilter[] = +{ + { + &KSPROPSETID_Pin, + KSPROPERTY_PIN_PROPOSEDATAFORMAT, + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_BthHfpWaveFilter + }, + { + &KSPROPSETID_AudioEffectsDiscovery, + KSPROPERTY_AUDIOEFFECTSDISCOVERY_EFFECTSLIST, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_BthHfpWaveFilter + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationBthHfpSpeakerWBWaveFilter, PropertiesBthHfpSpeakerWBWaveFilter); + +//============================================================================= +static +PCFILTER_DESCRIPTOR BthHfpSpeakerWBWaveMiniportFilterDescriptor = +{ + 0, // Version + &AutomationBthHfpSpeakerWBWaveFilter, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(BthHfpSpeakerWBWaveMiniportPins), // PinCount + BthHfpSpeakerWBWaveMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + SIZEOF_ARRAY(BthHfpSpeakerWBWaveMiniportNodes), // NodeCount + BthHfpSpeakerWBWaveMiniportNodes, // Nodes + SIZEOF_ARRAY(BthHfpSpeakerWBWaveMiniportConnections),// ConnectionCount + BthHfpSpeakerWBWaveMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories - use defaults (audio, render, capture) +}; + +#endif // _SYSVAD_BTHHFPSPEAKERWBWAVTABLE_H_ + diff --git a/audio/sysvad/EndpointsCommon/bthhfptopo.cpp b/audio/sysvad/EndpointsCommon/bthhfptopo.cpp new file mode 100644 index 000000000..6847dc051 --- /dev/null +++ b/audio/sysvad/EndpointsCommon/bthhfptopo.cpp @@ -0,0 +1,623 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + bthhfptopo.cpp + +Abstract: + + Implementation of topology miniport for the Bluetooth Hands-Free Profile (external). + +--*/ +#ifdef SYSVAD_BTH_BYPASS + +#pragma warning (disable : 4127) + +#include +#include "simple.h" +#include "mintopo.h" +#include "bthhfptopo.h" + +typedef struct _BTHHFP_VOLUME_VALUES_BLOCK +{ + KSPROPERTY_DESCRIPTION Description; + KSPROPERTY_MEMBERSHEADER SteppingHeader; + KSPROPERTY_STEPPING_LONG Stepping[1]; + KSPROPERTY_MEMBERSHEADER DefaultHeader; + ULONG Default[1]; + +} BTHHFP_VOLUME_VALUES_BLOCK, *PBTHHFP_VOLUME_VALUES_BLOCK; + + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +PropertyHandler_BthHfpVolumeLevel_BasicSupport +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Handles basic support for Bluetooth HFP ( KSPROPSETID_Audio, KSPROPERTY_AUDIO_VOLUMELEVEL ) + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[PropertyHandler_BthHfpVolumeLevel_BasicSupport]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + ASSERT(PropertyRequest); + + if (PropertyRequest->ValueSize >= sizeof(KSPROPERTY_DESCRIPTION)) + { + PCMiniportTopology miniport = (PCMiniportTopology)PropertyRequest->MajorTarget; + ULONG volumeSettingsSize = 0; + PKSPROPERTY_VALUES volumeSettings; + PKSPROPERTY_DESCRIPTION propDesc; + PBTHHFPDEVICECOMMON bthHfpDevice; + ULONG cbFullProperty = sizeof(BTHHFP_VOLUME_VALUES_BLOCK); + + propDesc = PKSPROPERTY_DESCRIPTION(PropertyRequest->Value); + bthHfpDevice = miniport->GetBthHfpDevice(); // weak ref. + ASSERT(bthHfpDevice != NULL); + + // + // Convert the KSPROPERTY_VALUES into a BTHHFP_VOLUME_VALUES_BLOCK. + // + volumeSettings = bthHfpDevice->GetVolumeSettings(&volumeSettingsSize); + ASSERT(volumeSettings != NULL); + ASSERT(volumeSettingsSize != 0); + + if (volumeSettings->MembersListCount != 2 || + volumeSettings->MembersList[0].MembersHeader.MembersFlags != KSPROPERTY_MEMBER_STEPPEDRANGES || + volumeSettings->MembersList[1].MembersHeader.MembersFlags != KSPROPERTY_MEMBER_VALUES) + { + ntStatus = STATUS_NOT_SUPPORTED; + DPF(D_ERROR, ("PropertyHandler_BthHfpVolumeLevel_BasicSupport: invalid KSPROPERTY_VALUES settings")); + goto Done; + } + + // + // Init description prop header. + // + propDesc->AccessFlags = KSPROPERTY_TYPE_ALL; + propDesc->DescriptionSize = cbFullProperty; + propDesc->PropTypeSet = volumeSettings->PropTypeSet; + propDesc->MembersListCount = 2; + propDesc->Reserved = 0; + + // if return buffer can also hold a range description, return it too + if(PropertyRequest->ValueSize >= cbFullProperty) + { + PBTHHFP_VOLUME_VALUES_BLOCK settings = (PBTHHFP_VOLUME_VALUES_BLOCK)propDesc; + + // + // First entry is the range. + // + settings->SteppingHeader.MembersFlags = KSPROPERTY_MEMBER_STEPPEDRANGES; + settings->SteppingHeader.MembersSize = sizeof(KSPROPERTY_STEPPING_LONG); + settings->SteppingHeader.MembersCount = 1; + settings->SteppingHeader.Flags = KSPROPERTY_MEMBER_FLAG_BASICSUPPORT_MULTICHANNEL; + + // fill in the stepped range. + PKSPROPERTY_STEPPING_LONG ranges = (PKSPROPERTY_STEPPING_LONG)volumeSettings->MembersList[0].Members; + settings->Stepping[0] = *ranges; + //settings->Stepping[0].SteppingDelta = 2 * 0x10000; // 2 dB + //settings->Stepping[0].Bounds.SignedMinimum = -15 * 0x10000; // -15 dB + + // + // Second entry is the default value. + // + settings->DefaultHeader.MembersFlags = KSPROPERTY_MEMBER_VALUES; + settings->DefaultHeader.MembersSize = sizeof(LONG); + settings->DefaultHeader.MembersCount = 1; + settings->DefaultHeader.Flags = KSPROPERTY_MEMBER_FLAG_DEFAULT; + + // fill in the default value. + PLONG defValue = (PLONG) volumeSettings->MembersList[1].Members; + settings->Default[0] = *defValue; + + // set the return value size + PropertyRequest->ValueSize = cbFullProperty; + } + else + { + PropertyRequest->ValueSize = sizeof(KSPROPERTY_DESCRIPTION); + } + } + else if(PropertyRequest->ValueSize >= sizeof(ULONG)) + { + // if return buffer can hold a ULONG, return the access flags + PULONG AccessFlags = PULONG(PropertyRequest->Value); + + PropertyRequest->ValueSize = sizeof(ULONG); + *AccessFlags = KSPROPERTY_TYPE_ALL; + } + else + { + PropertyRequest->ValueSize = 0; + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + +Done: + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +PropertyHandler_BthHfpJackDescription +( + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG cJackDescriptions, + _In_reads_(cJackDescriptions) PKSJACK_DESCRIPTION * JackDescriptions +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_Jack, KSPROPERTY_JACK_DESCRIPTION ) + +Arguments: + + PropertyRequest - + + cJackDescriptions - + + JackDescriptions - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_BthHfpJackDescription]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + ULONG nPinId = (ULONG)-1; + PCMiniportTopology miniport = (PCMiniportTopology)PropertyRequest->MajorTarget; + + if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + nPinId = *(PULONG(PropertyRequest->Instance)); + + if ((nPinId < cJackDescriptions) && (JackDescriptions[nPinId] != NULL)) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else + { + ULONG cbNeeded = sizeof(KSMULTIPLE_ITEM) + sizeof(KSJACK_DESCRIPTION); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbNeeded; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbNeeded) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PBTHHFPDEVICECOMMON bthHfpDevice = NULL; + PKSMULTIPLE_ITEM pMI = (PKSMULTIPLE_ITEM)PropertyRequest->Value; + PKSJACK_DESCRIPTION pDesc = (PKSJACK_DESCRIPTION)(pMI+1); + + bthHfpDevice = miniport->GetBthHfpDevice(); // weak ref. + ASSERT(bthHfpDevice != NULL); + + pMI->Size = cbNeeded; + pMI->Count = 1; + + RtlCopyMemory(pDesc, JackDescriptions[nPinId], sizeof(KSJACK_DESCRIPTION)); + + pDesc->IsConnected = bthHfpDevice->GetConnectionStatus() ? TRUE : FALSE; + + ntStatus = STATUS_SUCCESS; + } + } + } + } + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +PropertyHandler_BthHfpJackDescription2 +( + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG cJackDescriptions, + _In_reads_(cJackDescriptions) PKSJACK_DESCRIPTION * JackDescriptions +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_Jack, KSPROPERTY_JACK_DESCRIPTION2 ) + +Arguments: + + PropertyRequest - + + cJackDescriptions - + + JackDescriptions - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_BthHfpJackDescription2]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + ULONG nPinId = (ULONG)-1; + + if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + nPinId = *(PULONG(PropertyRequest->Instance)); + + if ((nPinId < cJackDescriptions) && (JackDescriptions[nPinId] != NULL)) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else + { + ULONG cbNeeded = sizeof(KSMULTIPLE_ITEM) + sizeof(KSJACK_DESCRIPTION2); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbNeeded; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbNeeded) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PKSMULTIPLE_ITEM pMI = (PKSMULTIPLE_ITEM)PropertyRequest->Value; + PKSJACK_DESCRIPTION2 pDesc = (PKSJACK_DESCRIPTION2)(pMI+1); + + pMI->Size = cbNeeded; + pMI->Count = 1; + + RtlZeroMemory(pDesc, sizeof(KSJACK_DESCRIPTION2)); + + // + // Specifies the lower 16 bits of the DWORD parameter. This parameter indicates whether + // the jack is currently active, streaming, idle, or hardware not ready. + // + pDesc->DeviceStateInfo = 0; + + // + // From MSDN: + // "If an audio device lacks jack presence detection, the IsConnected member of + // the KSJACK_DESCRIPTION structure must always be set to TRUE. To remove the + // ambiguity that results from this dual meaning of the TRUE value for IsConnected, + // a client application can call IKsJackDescription2::GetJackDescription2 to read + // the JackCapabilities flag of the KSJACK_DESCRIPTION2 structure. If this flag has + // the JACKDESC2_PRESENCE_DETECT_CAPABILITY bit set, it indicates that the endpoint + // does in fact support jack presence detection. In that case, the return value of + // the IsConnected member can be interpreted to accurately reflect the insertion status + // of the jack." + // + // Bit definitions: + // 0x00000001 - JACKDESC2_PRESENCE_DETECT_CAPABILITY + // 0x00000002 - JACKDESC2_DYNAMIC_FORMAT_CHANGE_CAPABILITY + // + pDesc->JackCapabilities = JACKDESC2_PRESENCE_DETECT_CAPABILITY; + + ntStatus = STATUS_SUCCESS; + } + } + } + } + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +PropertyHandler_BthHfpJackContainerId +( + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG cJackDescriptions, + _In_reads_(cJackDescriptions) PKSJACK_DESCRIPTION * JackDescriptions +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_Jack, KSPROPERTY_JACK_CONTAINERID ) + +Arguments: + + PropertyRequest - + + cJackDescriptions - + + JackDescriptions - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_BthHfpJackContainerId]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + PCMiniportTopology miniport = (PCMiniportTopology)PropertyRequest->MajorTarget; + ULONG nPinId = (ULONG)-1; + + if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + nPinId = *(PULONG(PropertyRequest->Instance)); + + // This property is only valid on bridge pins. + if ((nPinId < cJackDescriptions) && (JackDescriptions[nPinId] != NULL)) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else + { + ULONG cbNeeded = sizeof(GUID); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbNeeded; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbNeeded) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PBTHHFPDEVICECOMMON bthHfpDevice = NULL; + + GUID* guid = (GUID *)PropertyRequest->Value; + + bthHfpDevice = miniport->GetBthHfpDevice(); // weak ref. + ASSERT(bthHfpDevice != NULL); + + *guid = bthHfpDevice->GetContainerId(); + + ntStatus = STATUS_SUCCESS; + } + } + } + } + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +PropertyHandler_BthHfpOneShotReconnect +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_BtAudio, KSPROPERTY_ONESHOT_RECONNECT ) + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_BthHfpOneShotReconnect]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + PCMiniportTopology miniport = (PCMiniportTopology)PropertyRequest->MajorTarget; + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PBTHHFPDEVICECOMMON bthHfpDevice = NULL; + + bthHfpDevice = miniport->GetBthHfpDevice(); // weak ref. + ASSERT(bthHfpDevice != NULL); + + ntStatus = bthHfpDevice->Connect(); + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +PropertyHandler_BthHfpOneDisconnect +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_BtAudio, KSPROPERTY_ONESHOT_DISCONNECT ) + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_BthHfpOneDisconnect]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + PCMiniportTopology miniport = (PCMiniportTopology)PropertyRequest->MajorTarget; + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PBTHHFPDEVICECOMMON bthHfpDevice = NULL; + + bthHfpDevice = miniport->GetBthHfpDevice(); // weak ref. + ASSERT(bthHfpDevice != NULL); + + ntStatus = bthHfpDevice->Disconnect(); + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg() +NTSTATUS +PropertyHandler_BthHfpTopoNodeEvent +( + _In_ PPCEVENT_REQUEST EventRequest +) +{ + ASSERT(EventRequest); + + DPF_ENTER(("[PropertyHandler_BthHfpTopoNodeEvent]")); + + // The major target is the object pointer to the topology miniport. + PCMiniportTopology pMiniport = (PCMiniportTopology)EventRequest->MajorTarget; + ASSERT (pMiniport); + + switch (EventRequest->Verb) + { + // Do we support event handling?!? + case PCEVENT_VERB_SUPPORT: + DPF(D_VERBOSE, ("BasicSupport Query for event.")); + break; + + // We should add the event now! + case PCEVENT_VERB_ADD: + DPF(D_VERBOSE, ("Adding event.")); + + // If we have the interface and EventEntry is defined ... + if (EventRequest->EventEntry) + { + pMiniport->AddEventToEventList(EventRequest->EventEntry); + } + else + { + return STATUS_UNSUCCESSFUL; + } + break; + + case PCEVENT_VERB_REMOVE: + // We cannot remove the event but we can stop generating the + // events. However, it also doesn't hurt to always generate them ... + DPF(D_VERBOSE, ("Removing event.")); + break; + + default: + return STATUS_INVALID_PARAMETER; + } + + return STATUS_SUCCESS; +} + +#pragma code_seg() +#endif // SYSVAD_BTH_BYPASS + + diff --git a/audio/sysvad/EndpointsCommon/bthhfptopo.h b/audio/sysvad/EndpointsCommon/bthhfptopo.h new file mode 100644 index 000000000..24c0e1c4e --- /dev/null +++ b/audio/sysvad/EndpointsCommon/bthhfptopo.h @@ -0,0 +1,61 @@ + +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + bthhfptopo.h + +Abstract: + + Declaration of topology miniport for the mic (external: headphone). + +--*/ + +#ifndef _SYSVAD_BTHHFPTOPO_H_ +#define _SYSVAD_BTHHFPTOPO_H_ + +NTSTATUS +PropertyHandler_BthHfpVolumeLevel_BasicSupport( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + +NTSTATUS +PropertyHandler_BthHfpJackDescription( + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG cJackDescriptions, + _In_reads_(cJackDescriptions) PKSJACK_DESCRIPTION * JackDescriptions + ); + +NTSTATUS +PropertyHandler_BthHfpJackDescription2( + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG cJackDescriptions, + _In_reads_(cJackDescriptions) PKSJACK_DESCRIPTION * JackDescriptions + ); + +NTSTATUS +PropertyHandler_BthHfpJackContainerId( + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG cJackDescriptions, + _In_reads_(cJackDescriptions) PKSJACK_DESCRIPTION * JackDescriptions + ); + +NTSTATUS +PropertyHandler_BthHfpOneShotReconnect( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + +NTSTATUS +PropertyHandler_BthHfpOneDisconnect( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + +NTSTATUS +PropertyHandler_BthHfpTopoNodeEvent( + _In_ PPCEVENT_REQUEST EventRequest + ); + +#endif // _SYSVAD_BTHHFPTOPO_H_ + diff --git a/audio/sysvad/EndpointsCommon/micarray1toptable.h b/audio/sysvad/EndpointsCommon/micarray1toptable.h new file mode 100644 index 000000000..26148eff2 --- /dev/null +++ b/audio/sysvad/EndpointsCommon/micarray1toptable.h @@ -0,0 +1,228 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + micarray1toptable.h + +Abstract: + + Declaration of topology tables for the mic array (front). + +--*/ + +#ifndef _SYSVAD_MICARRAY1TOPTABLE_H_ +#define _SYSVAD_MICARRAY1TOPTABLE_H_ + +// +// {6ae81ff4-203e-4fe1-88aa-f2d57775cd4a} +DEFINE_GUID(MICARRAY1_CUSTOM_NAME, +0x6ae81ff4, 0x203e, 0x4fe1, 0x88, 0xaa, 0xf2, 0xd5, 0x77, 0x75, 0xcd, 0x4a); + +//============================================================================= +static +KSDATARANGE MicArray1TopoPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +//============================================================================= +static +PKSDATARANGE MicArray1TopoPinDataRangePointersBridge[] = +{ + &MicArray1TopoPinDataRangesBridge[0] +}; + +//============================================================================= +static +PCPIN_DESCRIPTOR MicArray1TopoMiniportPins[] = +{ + // KSPIN_TOPO_MIC_ELEMENTS + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(MicArray1TopoPinDataRangePointersBridge), // DataRangesCount + MicArray1TopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_IN, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSNODETYPE_MICROPHONE_ARRAY, // Category + &MICARRAY1_CUSTOM_NAME, // Name + 0 // Reserved + } + }, + + // KSPIN_TOPO_BRIDGE + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(MicArray1TopoPinDataRangePointersBridge), // DataRangesCount + MicArray1TopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_OUT, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSCATEGORY_AUDIO, // Category + NULL, // Name + 0 // Reserved + } + } +}; + +//============================================================================= +static +PCPROPERTY_ITEM MicArray1PropertiesVolume[] = +{ + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_VOLUMELEVEL, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_MicArrayTopology + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationMicArray1Volume, MicArray1PropertiesVolume); + +//============================================================================= +static +PCPROPERTY_ITEM MicArray1PropertiesMute[] = +{ + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_MUTE, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_MicArrayTopology + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationMicArray1Mute, MicArray1PropertiesMute); + +//============================================================================= +static +PCPROPERTY_ITEM MicArray1PropertiesPeakMeter[] = +{ + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_PEAKMETER2, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_MicArrayTopology + }, + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_CPU_RESOURCES, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_MicArrayTopology + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationMicArray1PeakMeter, MicArray1PropertiesPeakMeter); + +//============================================================================= +static +PCNODE_DESCRIPTOR MicArray1TopologyNodes[] = +{ + // KSNODE_TOPO_VOLUME + { + 0, // Flags + &AutomationMicArray1Volume, // AutomationTable + &KSNODETYPE_VOLUME, // Type + &KSAUDFNAME_MIC_VOLUME // Name + }, + // KSNODE_TOPO_MUTE + { + 0, // Flags + &AutomationMicArray1Mute, // AutomationTable + &KSNODETYPE_MUTE, // Type + &KSAUDFNAME_MIC_MUTE // Name + }, + // KSNODE_TOPO_PEAKMETER + { + 0, // Flags + &AutomationMicArray1PeakMeter, // AutomationTable + &KSNODETYPE_PEAKMETER, // Type + &KSAUDFNAME_PEAKMETER // Name + } +}; + +C_ASSERT( KSNODE_TOPO_VOLUME == 0 ); +C_ASSERT( KSNODE_TOPO_MUTE == 1 ); +C_ASSERT( KSNODE_TOPO_PEAKMETER == 2 ); + +//============================================================================= +static +PCCONNECTION_DESCRIPTOR MicArray1TopoMiniportConnections[] = +{ + // FromNode, FromPin, ToNode, ToPin + { PCFILTER_NODE, KSPIN_TOPO_MIC_ELEMENTS, KSNODE_TOPO_VOLUME, 1 }, + { KSNODE_TOPO_VOLUME, 0, KSNODE_TOPO_MUTE, 1 }, + { KSNODE_TOPO_MUTE, 0, KSNODE_TOPO_PEAKMETER, 1 }, + { KSNODE_TOPO_PEAKMETER, 0, PCFILTER_NODE, KSPIN_TOPO_BRIDGE } +}; + + +//============================================================================= +static +PCPROPERTY_ITEM MicArray1PropertiesTopoFilter[] = +{ + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_MicArrayTopoFilter + }, + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION2, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_MicArrayTopoFilter + }, + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_MIC_ARRAY_GEOMETRY, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_MicArrayTopoFilter + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationMicArray1TopoFilter, MicArray1PropertiesTopoFilter); + +//============================================================================= +static +PCFILTER_DESCRIPTOR MicArray1TopoMiniportFilterDescriptor = +{ + 0, // Version + &AutomationMicArray1TopoFilter, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(MicArray1TopoMiniportPins), // PinCount + MicArray1TopoMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + SIZEOF_ARRAY(MicArray1TopologyNodes), // NodeCount + MicArray1TopologyNodes, // Nodes + SIZEOF_ARRAY(MicArray1TopoMiniportConnections),// ConnectionCount + MicArray1TopoMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories +}; + +#endif // _SYSVAD_MICARRAY1TOPTABLE_H_ + diff --git a/audio/sysvad/EndpointsCommon/micarraytopo.cpp b/audio/sysvad/EndpointsCommon/micarraytopo.cpp new file mode 100644 index 000000000..236d03ea9 --- /dev/null +++ b/audio/sysvad/EndpointsCommon/micarraytopo.cpp @@ -0,0 +1,787 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + micarraytopo.cpp + +Abstract: + + Implementation of topology miniport for the mic array. + +--*/ + +#pragma warning (disable : 4127) + +#include +#include "simple.h" +#include "micarraytopo.h" +#include "micarray1toptable.h" + +#pragma code_seg("PAGE") + +//============================================================================= +NTSTATUS +CreateMicArrayMiniportTopology +( + _Out_ PUNKNOWN * Unknown, + _In_ REFCLSID, + _In_opt_ PUNKNOWN UnknownOuter, + _When_((PoolType & NonPagedPoolMustSucceed) != 0, + __drv_reportError("Must succeed pool allocations are forbidden. " + "Allocation failures cause a system crash")) + _In_ POOL_TYPE PoolType, + _In_ PUNKNOWN UnknownAdapter, + _In_opt_ PVOID DeviceContext, + _In_ PENDPOINT_MINIPAIR MiniportPair +) +/*++ + +Routine Description: + + Creates a new topology miniport. + +Arguments: + + Unknown - + + RefclsId - + + UnknownOuter - + + PoolType - + + UnknownAdapter - + + DeviceContext - + + MiniportPair - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(Unknown); + ASSERT(MiniportPair); + + UNREFERENCED_PARAMETER(UnknownAdapter); + UNREFERENCED_PARAMETER(DeviceContext); + + CMicArrayMiniportTopology *obj = + new (PoolType, MINTOPORT_POOLTAG) + CMicArrayMiniportTopology( UnknownOuter, + MiniportPair->TopoDescriptor, + MiniportPair->DeviceMaxChannels, + MiniportPair->DeviceType ); + if (NULL == obj) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + obj->AddRef(); + + *Unknown = reinterpret_cast(obj); + + return STATUS_SUCCESS; +} // CreateMicArrayMiniportTopology + +//============================================================================= +CMicArrayMiniportTopology::~CMicArrayMiniportTopology +( + void +) +/*++ + +Routine Description: + + Topology miniport destructor + +Arguments: + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + DPF_ENTER(("[CMicArrayMiniportTopology::~CMicArrayMiniportTopology]")); +} // ~CMicArrayMiniportTopology + +//============================================================================= +NTSTATUS +CMicArrayMiniportTopology::DataRangeIntersection +( + _In_ ULONG PinId, + _In_ PKSDATARANGE ClientDataRange, + _In_ PKSDATARANGE MyDataRange, + _In_ ULONG OutputBufferLength, + _Out_writes_bytes_to_opt_(OutputBufferLength, *ResultantFormatLength) + PVOID ResultantFormat OPTIONAL, + _Out_ PULONG ResultantFormatLength +) +/*++ + +Routine Description: + + The DataRangeIntersection function determines the highest quality + intersection of two data ranges. + +Arguments: + + PinId - Pin for which data intersection is being determined. + + ClientDataRange - Pointer to KSDATARANGE structure which contains the data range + submitted by client in the data range intersection property + request. + + MyDataRange - Pin's data range to be compared with client's data range. + + OutputBufferLength - Size of the buffer pointed to by the resultant format + parameter. + + ResultantFormat - Pointer to value where the resultant format should be + returned. + + ResultantFormatLength - Actual length of the resultant format that is placed + at ResultantFormat. This should be less than or equal + to OutputBufferLength. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + return + CMiniportTopologySYSVAD::DataRangeIntersection + ( + PinId, + ClientDataRange, + MyDataRange, + OutputBufferLength, + ResultantFormat, + ResultantFormatLength + ); +} // DataRangeIntersection + +//============================================================================= +STDMETHODIMP +CMicArrayMiniportTopology::GetDescription +( + _Out_ PPCFILTER_DESCRIPTOR * OutFilterDescriptor +) +/*++ + +Routine Description: + + The GetDescription function gets a pointer to a filter description. + It provides a location to deposit a pointer in miniport's description + structure. This is the placeholder for the FromNode or ToNode fields in + connections which describe connections to the filter's pins. + +Arguments: + + OutFilterDescriptor - Pointer to the filter description. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(OutFilterDescriptor); + + return CMiniportTopologySYSVAD::GetDescription(OutFilterDescriptor); +} // GetDescription + +//============================================================================= +STDMETHODIMP +CMicArrayMiniportTopology::Init +( + _In_ PUNKNOWN UnknownAdapter, + _In_ PRESOURCELIST ResourceList, + _In_ PPORTTOPOLOGY Port_ +) +/*++ + +Routine Description: + + The Init function initializes the miniport. Callers of this function + should run at IRQL PASSIVE_LEVEL + +Arguments: + + UnknownAdapter - A pointer to the IUnknown interface of the adapter object. + + ResourceList - Pointer to the resource list to be supplied to the miniport + during initialization. The port driver is free to examine the + contents of the ResourceList. The port driver will not be + modify the ResourceList contents. + + Port - Pointer to the topology port object that is linked with this miniport. + +Return Value: + + NT status code. + +--*/ +{ + UNREFERENCED_PARAMETER(ResourceList); + + PAGED_CODE(); + + ASSERT(UnknownAdapter); + ASSERT(Port_); + + DPF_ENTER(("[CMicArrayMiniportTopology::Init]")); + + NTSTATUS ntStatus; + + ntStatus = + CMiniportTopologySYSVAD::Init + ( + UnknownAdapter, + Port_ + ); + + return ntStatus; +} // Init + +//============================================================================= +STDMETHODIMP +CMicArrayMiniportTopology::NonDelegatingQueryInterface +( + _In_ REFIID Interface, + _COM_Outptr_ PVOID * Object +) +/*++ + +Routine Description: + + QueryInterface for MiniportTopology + +Arguments: + + Interface - GUID of the interface + + Object - interface object to be returned. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(Object); + + if (IsEqualGUIDAligned(Interface, IID_IUnknown)) + { + *Object = PVOID(PUNKNOWN(this)); + } + else if (IsEqualGUIDAligned(Interface, IID_IMiniport)) + { + *Object = PVOID(PMINIPORT(this)); + } + else if (IsEqualGUIDAligned(Interface, IID_IMiniportTopology)) + { + *Object = PVOID(PMINIPORTTOPOLOGY(this)); + } + else + { + *Object = NULL; + } + + if (*Object) + { + // We reference the interface for the caller. + PUNKNOWN(*Object)->AddRef(); + return(STATUS_SUCCESS); + } + + return(STATUS_INVALID_PARAMETER); +} // NonDelegatingQueryInterface + +//============================================================================= +NTSTATUS +CMicArrayMiniportTopology::PropertyHandlerMicArrayGeometry +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_Audio, KSPROPERTY_AUDIO_MIC_ARRAY_GEOMETRY ) + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandlerMicArrayGeometry]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + ULONG nPinId = (ULONG)-1; + + if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + nPinId = *(PULONG(PropertyRequest->Instance)); + + if (nPinId == KSPIN_TOPO_MIC_ELEMENTS) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else + { + ULONG cElements = IsCombined()? 4 : 2; + ULONG cbNeeded = FIELD_OFFSET(KSAUDIO_MIC_ARRAY_GEOMETRY, KsMicCoord) + + cElements * sizeof(KSAUDIO_MICROPHONE_COORDINATES); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbNeeded; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbNeeded) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PKSAUDIO_MIC_ARRAY_GEOMETRY pMAG = (PKSAUDIO_MIC_ARRAY_GEOMETRY)PropertyRequest->Value; + const SHORT MicArray_180_Degrees = 31416; // 10000 * pi + const SHORT MicArray_45_Degrees = 7854; // 10000 * pi / 4 + + if (IsFront()) + { + // fill in mic array geometry fields + pMAG->usVersion = 0x0100; // Version of Mic array specification (0x0100) + pMAG->usMicArrayType = (USHORT)KSMICARRAY_MICARRAYTYPE_LINEAR; // Type of Mic Array + pMAG->wVerticalAngleBegin = -MicArray_45_Degrees; // Work Volume Vertical Angle Begin + pMAG->wVerticalAngleEnd = MicArray_45_Degrees; // Work Volume Vertical Angle End + pMAG->wHorizontalAngleBegin = 0; // Work Volume HorizontalAngle Begin + pMAG->wHorizontalAngleEnd = 0; // Work Volume HorizontalAngle End + pMAG->usFrequencyBandLo = 100; // Low end of Freq Range + pMAG->usFrequencyBandHi = 8000; // High end of Freq Range + + pMAG->usNumberOfMicrophones = 2; // Count of microphone coordinate structures to follow. + + pMAG->KsMicCoord[0].usType = (USHORT)KSMICARRAY_MICTYPE_CARDIOID; + pMAG->KsMicCoord[0].wXCoord = 0; + pMAG->KsMicCoord[0].wYCoord = -100; // mic elements are 200 mm apart + pMAG->KsMicCoord[0].wZCoord = 0; + pMAG->KsMicCoord[0].wVerticalAngle = 0; + pMAG->KsMicCoord[0].wHorizontalAngle = 0; + + pMAG->KsMicCoord[1].usType = (USHORT)KSMICARRAY_MICTYPE_CARDIOID; + pMAG->KsMicCoord[1].wXCoord = 0; + pMAG->KsMicCoord[1].wYCoord = 100; // mic elements are 200 mm apart + pMAG->KsMicCoord[1].wZCoord = 0; + pMAG->KsMicCoord[1].wVerticalAngle = 0; + pMAG->KsMicCoord[1].wHorizontalAngle = 0; + } + else if (IsBack()) // in this sample the geometries for front and back are the same. + { + // fill in mic array geometry fields + pMAG->usVersion = 0x0100; // Version of Mic array specification (0x0100) + pMAG->usMicArrayType = (USHORT)KSMICARRAY_MICARRAYTYPE_LINEAR; // Type of Mic Array + pMAG->wVerticalAngleBegin = -MicArray_45_Degrees; // Work Volume Vertical Angle Begin + pMAG->wVerticalAngleEnd = MicArray_45_Degrees; // Work Volume Vertical Angle End + pMAG->wHorizontalAngleBegin = 0; // Work Volume HorizontalAngle Begin + pMAG->wHorizontalAngleEnd = 0; // Work Volume HorizontalAngle End + pMAG->usFrequencyBandLo = 100; // Low end of Freq Range + pMAG->usFrequencyBandHi = 8000; // High end of Freq Range + + pMAG->usNumberOfMicrophones = 2; // Count of microphone coordinate structures to follow. + + pMAG->KsMicCoord[0].usType = (USHORT)KSMICARRAY_MICTYPE_CARDIOID; + pMAG->KsMicCoord[0].wXCoord = 0; + pMAG->KsMicCoord[0].wYCoord = -100; // mic elements are 200 mm apart + pMAG->KsMicCoord[0].wZCoord = 0; + pMAG->KsMicCoord[0].wVerticalAngle = 0; + pMAG->KsMicCoord[0].wHorizontalAngle = 0; + + pMAG->KsMicCoord[1].usType = (USHORT)KSMICARRAY_MICTYPE_CARDIOID; + pMAG->KsMicCoord[1].wXCoord = 0; + pMAG->KsMicCoord[1].wYCoord = 100; // mic elements are 200 mm apart + pMAG->KsMicCoord[1].wZCoord = 0; + pMAG->KsMicCoord[1].wVerticalAngle = 0; + pMAG->KsMicCoord[1].wHorizontalAngle = 0; + } + else + { + // fill in mic array geometry fields + pMAG->usVersion = 0x0100; // Version of Mic array specification (0x0100) + pMAG->usMicArrayType = (USHORT)KSMICARRAY_MICARRAYTYPE_LINEAR; // Type of Mic Array + pMAG->wVerticalAngleBegin = -MicArray_45_Degrees; // Work Volume Vertical Angle Begin + pMAG->wVerticalAngleEnd = MicArray_45_Degrees; // Work Volume Vertical Angle End + pMAG->wHorizontalAngleBegin = -MicArray_180_Degrees; // Work Volume HorizontalAngle Begin + pMAG->wHorizontalAngleEnd = MicArray_180_Degrees; // Work Volume HorizontalAngle End + pMAG->usFrequencyBandLo = 100; // Low end of Freq Range + pMAG->usFrequencyBandHi = 8000; // High end of Freq Range + + pMAG->usNumberOfMicrophones = 4; // Count of microphone coordinate structures to follow. + + // front elements + pMAG->KsMicCoord[0].usType = (USHORT)KSMICARRAY_MICTYPE_CARDIOID; + pMAG->KsMicCoord[0].wXCoord = 0; + pMAG->KsMicCoord[0].wYCoord = -100; // mic elements are 200 mm apart + pMAG->KsMicCoord[0].wZCoord = 0; + pMAG->KsMicCoord[0].wVerticalAngle = 0; + pMAG->KsMicCoord[0].wHorizontalAngle = 0; + + pMAG->KsMicCoord[1].usType = (USHORT)KSMICARRAY_MICTYPE_CARDIOID; + pMAG->KsMicCoord[1].wXCoord = 0; + pMAG->KsMicCoord[1].wYCoord = 100; // mic elements are 200 mm apart + pMAG->KsMicCoord[1].wZCoord = 0; + pMAG->KsMicCoord[1].wVerticalAngle = 0; + pMAG->KsMicCoord[1].wHorizontalAngle = 0; + + // back elements + pMAG->KsMicCoord[2].usType = (USHORT)KSMICARRAY_MICTYPE_CARDIOID; + pMAG->KsMicCoord[2].wXCoord = 0; + pMAG->KsMicCoord[2].wYCoord = -100; // mic elements are 200 mm apart + pMAG->KsMicCoord[2].wZCoord = 0; + pMAG->KsMicCoord[2].wVerticalAngle = 0; + pMAG->KsMicCoord[2].wHorizontalAngle = -MicArray_180_Degrees; + + pMAG->KsMicCoord[3].usType = (USHORT)KSMICARRAY_MICTYPE_CARDIOID; + pMAG->KsMicCoord[3].wXCoord = 0; + pMAG->KsMicCoord[3].wYCoord = 100; // mic elements are 200 mm apart + pMAG->KsMicCoord[3].wZCoord = 0; + pMAG->KsMicCoord[3].wVerticalAngle = 0; + pMAG->KsMicCoord[3].wHorizontalAngle = -MicArray_180_Degrees; + + } + + ntStatus = STATUS_SUCCESS; + } + } + } + } + } + + return ntStatus; +} + +//============================================================================= +NTSTATUS +CMicArrayMiniportTopology::PropertyHandlerJackDescription +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_Jack, KSPROPERTY_JACK_DESCRIPTION ) + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandlerJackDescription]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + ULONG nPinId = (ULONG)-1; + + if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + nPinId = *(PULONG(PropertyRequest->Instance)); + + if (nPinId == KSPIN_TOPO_MIC_ELEMENTS) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else + { + ULONG cbNeeded = sizeof(KSMULTIPLE_ITEM) + sizeof(KSJACK_DESCRIPTION); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbNeeded; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbNeeded) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PKSMULTIPLE_ITEM pMI = (PKSMULTIPLE_ITEM)PropertyRequest->Value; + PKSJACK_DESCRIPTION pDesc = (PKSJACK_DESCRIPTION)(pMI+1); + + pMI->Size = cbNeeded; + pMI->Count = 1; + + pDesc->ChannelMapping = 0; // Don't specify channel mask for array mic + pDesc->Color = 0x00000000; // Black. This is an integrated device + pDesc->ConnectionType = eConnTypeUnknown; // Integrated. + pDesc->GenLocation = eGenLocPrimaryBox; + pDesc->GeoLocation = IsFront() ? eGeoLocFront : + (IsBack() ? eGeoLocRear : eGeoLocNotApplicable); + pDesc->PortConnection = ePortConnIntegratedDevice; + pDesc->IsConnected = TRUE; // This is an integrated device, so it's always "connected" + + ntStatus = STATUS_SUCCESS; + } + } + } + } + } + + return ntStatus; +} + +//============================================================================= +NTSTATUS +CMicArrayMiniportTopology::PropertyHandlerJackDescription2 +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_Jack, KSPROPERTY_JACK_DESCRIPTION2 ) + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandlerJackDescription2]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + ULONG nPinId = (ULONG)-1; + + if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + nPinId = *(PULONG(PropertyRequest->Instance)); + + if (nPinId == KSPIN_TOPO_MIC_ELEMENTS) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else + { + ULONG cbNeeded = sizeof(KSMULTIPLE_ITEM) + sizeof(KSJACK_DESCRIPTION2); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbNeeded; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbNeeded) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PKSMULTIPLE_ITEM pMI = (PKSMULTIPLE_ITEM)PropertyRequest->Value; + PKSJACK_DESCRIPTION2 pDesc = (PKSJACK_DESCRIPTION2)(pMI+1); + + pMI->Size = cbNeeded; + pMI->Count = 1; + + RtlZeroMemory(pDesc, sizeof(KSJACK_DESCRIPTION2)); + + // + // Specifies the lower 16 bits of the DWORD parameter. This parameter indicates whether + // the jack is currently active, streaming, idle, or hardware not ready. + // + pDesc->DeviceStateInfo = 0; + + // + // From MSDN: + // "If an audio device lacks jack presence detection, the IsConnected member of + // the KSJACK_DESCRIPTION structure must always be set to TRUE. To remove the + // ambiguity that results from this dual meaning of the TRUE value for IsConnected, + // a client application can call IKsJackDescription2::GetJackDescription2 to read + // the JackCapabilities flag of the KSJACK_DESCRIPTION2 structure. If this flag has + // the JACKDESC2_PRESENCE_DETECT_CAPABILITY bit set, it indicates that the endpoint + // does in fact support jack presence detection. In that case, the return value of + // the IsConnected member can be interpreted to accurately reflect the insertion status + // of the jack." + // + // Bit definitions: + // 0x00000001 - JACKDESC2_PRESENCE_DETECT_CAPABILITY + // 0x00000002 - JACKDESC2_DYNAMIC_FORMAT_CHANGE_CAPABILITY + // + pDesc->JackCapabilities = 0; + + ntStatus = STATUS_SUCCESS; + } + } + } + } + } + + return ntStatus; +} + +//============================================================================= +NTSTATUS +PropertyHandler_MicArrayTopoFilter +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Redirects property request to miniport object + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_MicArrayTopoFilter]")); + + // PropertryRequest structure is filled by portcls. + // MajorTarget is a pointer to miniport object for miniports. + // + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + PCMicArrayMiniportTopology pMiniport = (PCMicArrayMiniportTopology)PropertyRequest->MajorTarget; + + if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_Audio)) + { + if (PropertyRequest->PropertyItem->Id == KSPROPERTY_AUDIO_MIC_ARRAY_GEOMETRY) + { + ntStatus = pMiniport->PropertyHandlerMicArrayGeometry(PropertyRequest); + } + } + else if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_Jack)) + { + if (PropertyRequest->PropertyItem->Id == KSPROPERTY_JACK_DESCRIPTION) + { + ntStatus = pMiniport->PropertyHandlerJackDescription(PropertyRequest); + } + else if (PropertyRequest->PropertyItem->Id == KSPROPERTY_JACK_DESCRIPTION2) + { + ntStatus = pMiniport->PropertyHandlerJackDescription2(PropertyRequest); + } + } + + return ntStatus; +} // PropertyHandler_TopoFilter + +//============================================================================= +NTSTATUS +PropertyHandler_MicArrayTopology +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Redirects property request to miniport object + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_MicArrayTopology]")); + + // PropertryRequest structure is filled by portcls. + // MajorTarget is a pointer to miniport object for miniports. + // + PCMicArrayMiniportTopology pMiniport = (PCMicArrayMiniportTopology)PropertyRequest->MajorTarget; + + return pMiniport->PropertyHandlerGeneric(PropertyRequest); +} // PropertyHandler_MicArrayTopology + +#pragma code_seg() + diff --git a/audio/sysvad/EndpointsCommon/micarraytopo.h b/audio/sysvad/EndpointsCommon/micarraytopo.h new file mode 100644 index 000000000..8076a359d --- /dev/null +++ b/audio/sysvad/EndpointsCommon/micarraytopo.h @@ -0,0 +1,112 @@ + +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + micarraytopo.h + +Abstract: + + Declaration of mic array topology miniport. + +--*/ + +#ifndef _SYSVAD_MICARRAYTOPO_H_ +#define _SYSVAD_MICARRAYTOPO_H_ + +#include "basetopo.h" + +//============================================================================= +// Classes +//============================================================================= + +/////////////////////////////////////////////////////////////////////////////// +// CMicArrayMiniportTopology +// + +class CMicArrayMiniportTopology : + public CMiniportTopologySYSVAD, + public IMiniportTopology, + public CUnknown +{ + public: + DECLARE_STD_UNKNOWN(); + CMicArrayMiniportTopology + ( + _In_opt_ PUNKNOWN UnknownOuter, + _In_ PCFILTER_DESCRIPTOR *FilterDesc, + _In_ USHORT DeviceMaxChannels, + _In_ eDeviceType DeviceType + ) + : CUnknown(UnknownOuter), + CMiniportTopologySYSVAD(FilterDesc, DeviceMaxChannels), + m_DeviceType(DeviceType) + { + ASSERT(m_DeviceType == eMicArrayDevice1 || + m_DeviceType == eMicArrayDevice2 || + m_DeviceType == eMicArrayDevice3); + } + + ~CMicArrayMiniportTopology(); + + IMP_IMiniportTopology; + + NTSTATUS PropertyHandlerMicArrayGeometry + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + + NTSTATUS PropertyHandlerJackDescription + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + + NTSTATUS PropertyHandlerJackDescription2 + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + + protected: + eDeviceType m_DeviceType; + + protected: + bool IsFront() + { + return m_DeviceType == eMicArrayDevice1; + } + + bool IsBack() + { + return m_DeviceType == eMicArrayDevice2; + } + + bool IsCombined() + { + return m_DeviceType == eMicArrayDevice3; + } +}; + +typedef CMicArrayMiniportTopology *PCMicArrayMiniportTopology; + + +NTSTATUS +CreateMicArrayMiniportTopology( + _Out_ PUNKNOWN * Unknown, + _In_ REFCLSID, + _In_opt_ PUNKNOWN UnknownOuter, + _When_((PoolType & NonPagedPoolMustSucceed) != 0, + __drv_reportError("Must succeed pool allocations are forbidden. " + "Allocation failures cause a system crash")) + _In_ POOL_TYPE PoolType, + _In_ PUNKNOWN UnknownAdapter, + _In_opt_ PVOID DeviceContext, + _In_ PENDPOINT_MINIPAIR MiniportPair + ); + +NTSTATUS PropertyHandler_MicArrayTopoFilter(_In_ PPCPROPERTY_REQUEST PropertyRequest); +NTSTATUS PropertyHandler_MicArrayTopology(_In_ PPCPROPERTY_REQUEST PropertyRequest); + +#endif // _SYSVAD_MICARRAYTOPO_H_ + diff --git a/audio/sysvad/EndpointsCommon/micarraywavtable.h b/audio/sysvad/EndpointsCommon/micarraywavtable.h new file mode 100644 index 000000000..fb553d673 --- /dev/null +++ b/audio/sysvad/EndpointsCommon/micarraywavtable.h @@ -0,0 +1,826 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + micarraywavtable.h + +Abstract: + + Declaration of wave miniport tables for the mic array. + +--*/ + +#ifndef _SYSVAD_MICARRAYWAVTABLE_H_ +#define _SYSVAD_MICARRAYWAVTABLE_H_ + +// +// Mic array range. +// +#define MICARRAY_RAW_CHANNELS 2 // Channels for raw mode +#define MICARRAY_PROCESSED_CHANNELS 1 // Channels for default mode +#define MICARRAY_DEVICE_MAX_CHANNELS 2 // Max channels overall +#define MICARRAY_MIN_BITS_PER_SAMPLE_PCM 16 // Min Bits Per Sample +#define MICARRAY_MAX_BITS_PER_SAMPLE_PCM 16 // Max Bits Per Sample +#define MICARRAY_RAW_SAMPLE_RATE 48000 // Raw sample rate +#define MICARRAY_PROCESSED_MIN_SAMPLE_RATE 8000 // Min Sample Rate +#define MICARRAY_PROCESSED_MAX_SAMPLE_RATE 48000 // Max Sample Rate + +// +// Max # of pin instances. +// +#define MICARRAY_MAX_INPUT_STREAMS 2 // Raw + Default streams + +//============================================================================= +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE MicArrayPinSupportedDeviceFormats[] = +{ + // 0 - Note the ENDPOINT_MINIPAIR structures for the mic arrays use this first element as the proposed DEFAULT format + // 48 KHz 16-bit mono + { + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 48000, + 96000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + // 1 + // 8 KHz 16-bit mono + { + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 8000, + 16000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + // 2 + // 11.025 KHz 16-bit mono + { + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 11025, + 22050, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + // 3 - Note the ENDPOINT_MINIPAIR structures for the mic arrays use this element as the proposed SPEECH format + // 16 KHz 16-bit mono + { + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, // One channel + 16000, // 16KHz + 32000, // average bytes per second + 2, // 2 bytes per frame + 16, // 16 bits per sample container + sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) + }, + 16, // valid bits per sample + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + // 4 + // 22.05 KHz 16-bit mono + { + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 22050, + 44100, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + // 5 - Note the ENDPOINT_MINIPAIR structures for the mic arrays use this element as the proposed COMMUNICATIONS format + // 24 KHz 16-bit mono + { + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 24000, + 48000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + // 6 + // 32 KHz 16-bit mono + { + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 32000, + 64000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + // 7 + // 44.1 KHz 16-bit mono + { + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 44100, + 88200, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + // 8 - Note the ENDPOINT_MINIPAIR structures for the mic arrays use this last element as the proposed RAW format + // 48 KHz 16-bit 2 channels + { + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 48000, + 192000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + 0, // No channel configuration for unprocessed mic array + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, +}; + +// +// Supported modes (only on streaming pins). +// +static +MODE_AND_DEFAULT_FORMAT MicArrayPinSupportedDeviceModes[] = +{ + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_RAW, + &MicArrayPinSupportedDeviceFormats[SIZEOF_ARRAY(MicArrayPinSupportedDeviceFormats)-1].DataFormat + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_DEFAULT, + &MicArrayPinSupportedDeviceFormats[0].DataFormat + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_SPEECH, + &MicArrayPinSupportedDeviceFormats[3].DataFormat + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_COMMUNICATIONS, + &MicArrayPinSupportedDeviceFormats[5].DataFormat + } +}; + +//============================================================================= +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE KeywordPinSupportedDeviceFormats[] = +{ + { + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, // One channel + 16000, // 16KHz + 32000, // average bytes per second + 2, // 2 bytes per frame + 16, // 16 bits per sample container + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, // valid bits per sample + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, +}; + +static +MODE_AND_DEFAULT_FORMAT KeywordPinSupportedDeviceModes[] = +{ + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_RAW, + &KeywordPinSupportedDeviceFormats[SIZEOF_ARRAY(KeywordPinSupportedDeviceFormats) - 1].DataFormat + } +}; + +// +// The entries here must follow the same order as the filter's pin +// descriptor array. +// +static +PIN_DEVICE_FORMATS_AND_MODES MicArrayPinDeviceFormatsAndModes[] = +{ + { + BridgePin, + NULL, + 0, + NULL, + 0 + }, + { + SystemCapturePin, + MicArrayPinSupportedDeviceFormats, + SIZEOF_ARRAY(MicArrayPinSupportedDeviceFormats), + MicArrayPinSupportedDeviceModes, + SIZEOF_ARRAY(MicArrayPinSupportedDeviceModes) + }, + { + KeywordCapturePin, + KeywordPinSupportedDeviceFormats, + SIZEOF_ARRAY(KeywordPinSupportedDeviceFormats), + KeywordPinSupportedDeviceModes, + SIZEOF_ARRAY(KeywordPinSupportedDeviceModes) + } +}; + +//============================================================================= +// Data ranges +// +// See CMiniportWaveRT::DataRangeIntersection. +// +// Both mono and two-channel formats are supported for the mic arrays. The +// design of this sample driver's data range intersection handler requires a +// separate data for each supported channel count. +// +static +KSDATARANGE_AUDIO MicArrayPinDataRangesRawStream[] = +{ + { + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + MICARRAY_RAW_CHANNELS, + MICARRAY_MIN_BITS_PER_SAMPLE_PCM, + MICARRAY_MAX_BITS_PER_SAMPLE_PCM, + MICARRAY_RAW_SAMPLE_RATE, + MICARRAY_RAW_SAMPLE_RATE + }, +}; + +static +KSDATARANGE_AUDIO MicArrayPinDataRangesProcessedStream[] = +{ + { + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + MICARRAY_PROCESSED_CHANNELS, + MICARRAY_MIN_BITS_PER_SAMPLE_PCM, + MICARRAY_MAX_BITS_PER_SAMPLE_PCM, + MICARRAY_PROCESSED_MIN_SAMPLE_RATE, + MICARRAY_PROCESSED_MIN_SAMPLE_RATE + }, + { + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + MICARRAY_PROCESSED_CHANNELS, + MICARRAY_MIN_BITS_PER_SAMPLE_PCM, + MICARRAY_MAX_BITS_PER_SAMPLE_PCM, + 11025, + 11025 + }, + { + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + MICARRAY_PROCESSED_CHANNELS, + MICARRAY_MIN_BITS_PER_SAMPLE_PCM, + MICARRAY_MAX_BITS_PER_SAMPLE_PCM, + 16000, + 16000 + }, + { + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + MICARRAY_PROCESSED_CHANNELS, + MICARRAY_MIN_BITS_PER_SAMPLE_PCM, + MICARRAY_MAX_BITS_PER_SAMPLE_PCM, + 22050, + 22050 + }, + { + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + MICARRAY_PROCESSED_CHANNELS, + MICARRAY_MIN_BITS_PER_SAMPLE_PCM, + MICARRAY_MAX_BITS_PER_SAMPLE_PCM, + 24000, + 24000 + }, + { + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + MICARRAY_PROCESSED_CHANNELS, + MICARRAY_MIN_BITS_PER_SAMPLE_PCM, + MICARRAY_MAX_BITS_PER_SAMPLE_PCM, + 32000, + 32000 + }, + { + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + MICARRAY_PROCESSED_CHANNELS, + MICARRAY_MIN_BITS_PER_SAMPLE_PCM, + MICARRAY_MAX_BITS_PER_SAMPLE_PCM, + 44100, + 44100 + }, + { + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + MICARRAY_PROCESSED_CHANNELS, + MICARRAY_MIN_BITS_PER_SAMPLE_PCM, + MICARRAY_MAX_BITS_PER_SAMPLE_PCM, + MICARRAY_PROCESSED_MAX_SAMPLE_RATE, + MICARRAY_PROCESSED_MAX_SAMPLE_RATE + }, +}; + +static +PKSDATARANGE MicArrayPinDataRangePointersStream[] = +{ + PKSDATARANGE(&MicArrayPinDataRangesProcessedStream[0]), + PKSDATARANGE(&PinDataRangeAttributeList), + PKSDATARANGE(&MicArrayPinDataRangesRawStream[0]), + PKSDATARANGE(&PinDataRangeAttributeList), +}; + +//============================================================================= +static +KSDATARANGE MicArrayPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +static +PKSDATARANGE MicArrayPinDataRangePointersBridge[] = +{ + &MicArrayPinDataRangesBridge[0] +}; + +static +KSDATARANGE_AUDIO KeywordPinDataRangesStream[] = +{ + { + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + 1, // max channels + 16, // min bits per sample + 16, // max bits per sample + 16000, // min sample rate + 16000 // max sample rate + } +}; + +static +PKSDATARANGE KeywordPinDataRangePointersStream[] = +{ + PKSDATARANGE(&KeywordPinDataRangesStream[0]), + PKSDATARANGE(&PinDataRangeAttributeList), +}; + +//============================================================================= +static +PCPIN_DESCRIPTOR MicArrayWaveMiniportPins[] = +{ + // Wave In Bridge Pin (Capture - From Topology) KSPIN_WAVE_BRIDGE + { + 0, + 0, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(MicArrayPinDataRangePointersBridge), + MicArrayPinDataRangePointersBridge, + KSPIN_DATAFLOW_IN, + KSPIN_COMMUNICATION_NONE, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, + // Wave In Streaming Pin (Capture) KSPIN_WAVE_HOST + { + MICARRAY_MAX_INPUT_STREAMS, + MICARRAY_MAX_INPUT_STREAMS, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(MicArrayPinDataRangePointersStream), + MicArrayPinDataRangePointersStream, + KSPIN_DATAFLOW_OUT, + KSPIN_COMMUNICATION_SINK, + &KSCATEGORY_AUDIO, + &KSAUDFNAME_RECORDING_CONTROL, + 0 + } + }, + // Keyword Detector Streaming Pin (Capture) KSPIN_WAVEIN_KEYWORD + { + 1, + 1, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(KeywordPinDataRangePointersStream), + KeywordPinDataRangePointersStream, + KSPIN_DATAFLOW_OUT, + KSPIN_COMMUNICATION_SINK, + &KSNODETYPE_AUDIO_KEYWORDDETECTOR, + NULL, + 0 + } + } +}; + +//============================================================================= +static +PCNODE_DESCRIPTOR MicArrayWaveMiniportNodes[] = +{ + // KSNODE_WAVE_ADC + { + 0, // Flags + NULL, // AutomationTable + &KSNODETYPE_ADC, // Type + NULL // Name + } +}; + +//============================================================================= +static +PCCONNECTION_DESCRIPTOR MicArrayWaveMiniportConnections[] = +{ + { PCFILTER_NODE, KSPIN_WAVE_BRIDGE, KSNODE_WAVE_ADC, 1 }, + { KSNODE_WAVE_ADC, 0, PCFILTER_NODE, KSPIN_WAVEIN_HOST }, + { KSNODE_WAVE_ADC, 0, PCFILTER_NODE, KSPIN_WAVEIN_KEYWORD }, +}; + +//============================================================================= +DECLARE_CLASSPROPERTYHANDLER(CMiniportWaveRT, Get_SoundDetectorSupportedPatterns); +DECLARE_CLASSPROPERTYHANDLER(CMiniportWaveRT, Set_SoundDetectorPatterns); +DECLARE_CLASSPROPERTYHANDLER(CMiniportWaveRT, Get_SoundDetectorArmed); +DECLARE_CLASSPROPERTYHANDLER(CMiniportWaveRT, Set_SoundDetectorArmed); +DECLARE_CLASSPROPERTYHANDLER(CMiniportWaveRT, Get_SoundDetectorMatchResult); + +static +SYSVADPROPERTY_ITEM PropertiesMicArrayWaveFilter[] = +{ + { + { + &KSPROPSETID_General, + KSPROPERTY_GENERAL_COMPONENTID, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_WaveFilter, + }, + 0, + 0, + NULL, + NULL, + NULL + }, + { + { + &KSPROPSETID_Pin, + KSPROPERTY_PIN_PROPOSEDATAFORMAT, + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_WaveFilter, + }, + 0, + 0, + NULL, + NULL, + NULL + }, + { + { + &KSPROPSETID_Pin, + KSPROPERTY_PIN_PROPOSEDATAFORMAT2, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_WaveFilter, + }, + 0, + 0, + NULL, + NULL, + NULL + }, + { + { + &KSPROPSETID_SoundDetector, + KSPROPERTY_SOUNDDETECTOR_SUPPORTEDPATTERNS, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + SysvadPropertyDispatch, + }, + 0, + sizeof(CONTOSO_SUPPORTEDPATTERNSVALUE), + CMiniportWaveRT_Get_SoundDetectorSupportedPatterns, + NULL, + NULL + }, + { + { + &KSPROPSETID_SoundDetector, + KSPROPERTY_SOUNDDETECTOR_PATTERNS, + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + SysvadPropertyDispatch, + }, + 0, + (sizeof(KSMULTIPLE_ITEM) + sizeof(CONTOSO_KEYWORDCONFIGURATION)), + NULL, + CMiniportWaveRT_Set_SoundDetectorPatterns, + NULL + }, + { + { + &KSPROPSETID_SoundDetector, + KSPROPERTY_SOUNDDETECTOR_ARMED, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + SysvadPropertyDispatch, + }, + 0, + sizeof(BOOL), + CMiniportWaveRT_Get_SoundDetectorArmed, + CMiniportWaveRT_Set_SoundDetectorArmed, + NULL + }, + { + { + &KSPROPSETID_SoundDetector, + KSPROPERTY_SOUNDDETECTOR_MATCHRESULT, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + SysvadPropertyDispatch, + }, + 0, + sizeof(CONTOSO_KEYWORDDETECTIONRESULT), + CMiniportWaveRT_Get_SoundDetectorMatchResult, + NULL, + NULL + }, +}; + +NTSTATUS CMiniportWaveRT_EventHandler_SoundDetectorMatchDetected( + _In_ PPCEVENT_REQUEST EventRequest + ); + +static +PCEVENT_ITEM EventsMicArrayWaveFilter[] = +{ + { + &KSEVENTSETID_SoundDetector, + KSEVENT_SOUNDDETECTOR_MATCHDETECTED, + KSEVENT_TYPE_ENABLE | KSEVENT_TYPE_BASICSUPPORT, + CMiniportWaveRT_EventHandler_SoundDetectorMatchDetected, + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP_EVENT(AutomationMicArrayWaveFilter, PropertiesMicArrayWaveFilter, EventsMicArrayWaveFilter); + +//============================================================================= +static +PCFILTER_DESCRIPTOR MicArrayWaveMiniportFilterDescriptor = +{ + 0, // Version + &AutomationMicArrayWaveFilter, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(MicArrayWaveMiniportPins), // PinCount + MicArrayWaveMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + SIZEOF_ARRAY(MicArrayWaveMiniportNodes), // NodeCount + MicArrayWaveMiniportNodes, // Nodes + SIZEOF_ARRAY(MicArrayWaveMiniportConnections), // ConnectionCount + MicArrayWaveMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories - use defaults (audio, render, capture) +}; + +#endif // _SYSVAD_MICARRAYWAVTABLE_H_ + diff --git a/audio/sysvad/EndpointsCommon/mintopo.cpp b/audio/sysvad/EndpointsCommon/mintopo.cpp new file mode 100644 index 000000000..47a89cac6 --- /dev/null +++ b/audio/sysvad/EndpointsCommon/mintopo.cpp @@ -0,0 +1,731 @@ +/*++ + +Copyright (c) 1997-2011 Microsoft Corporation All Rights Reserved + +Module Name: + + mintopo.cpp + +Abstract: + + Implementation of topology miniport. + +--*/ + +#pragma warning (disable : 4127) + +#include +#include "simple.h" +#include "minwavert.h" +#include "mintopo.h" + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CreateMiniportTopologySYSVAD +( + _Out_ PUNKNOWN * Unknown, + _In_ REFCLSID, + _In_opt_ PUNKNOWN UnknownOuter, + _When_((PoolType & NonPagedPoolMustSucceed) != 0, + __drv_reportError("Must succeed pool allocations are forbidden. " + "Allocation failures cause a system crash")) + _In_ POOL_TYPE PoolType, + _In_ PUNKNOWN UnknownAdapter, + _In_opt_ PVOID DeviceContext, + _In_ PENDPOINT_MINIPAIR MiniportPair +) +/*++ + +Routine Description: + + Creates a new topology miniport. + +Arguments: + + Unknown - + + RefclsId - + + PoolType - + + UnknownOuter - + + DeviceContext - + + MiniportPair - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + UNREFERENCED_PARAMETER(UnknownAdapter); + + ASSERT(Unknown); + ASSERT(MiniportPair); + + CMiniportTopology *obj = + new (PoolType, MINWAVERT_POOLTAG) + CMiniportTopology( UnknownOuter, + MiniportPair->TopoDescriptor, + MiniportPair->DeviceMaxChannels, + MiniportPair->DeviceType, + DeviceContext ); + if (NULL == obj) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + obj->AddRef(); + *Unknown = reinterpret_cast(obj); + + return STATUS_SUCCESS; +} // CreateMiniportTopologySYSVAD + +//============================================================================= +#pragma code_seg("PAGE") +CMiniportTopology::~CMiniportTopology +( + void +) +/*++ + +Routine Description: + + Topology miniport destructor + +Arguments: + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + DPF_ENTER(("[CMiniportTopology::~CMiniportTopology]")); + +#ifdef SYSVAD_BTH_BYPASS + if (IsBthHfpDevice()) + { + ASSERT(m_BthHfpDevice != NULL); + + // + // Register with BthHfpDevice to get notification events. + // + if (m_DeviceType == eBthHfpMicDevice) + { + m_BthHfpDevice->SetMicVolumeHandler(NULL, NULL); + m_BthHfpDevice->SetMicConnectionStatusHandler(NULL, NULL); + } + else + { + ASSERT(m_DeviceType == eBthHfpSpeakerDevice); + + m_BthHfpDevice->SetSpeakerVolumeHandler(NULL, NULL); + m_BthHfpDevice->SetSpeakerConnectionStatusHandler(NULL, NULL); + } + + SAFE_RELEASE(m_BthHfpDevice); // IBthHfpDeviceCommon + } +#endif // SYSVAD_BTH_BYPASS +} // ~CMiniportTopology + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CMiniportTopology::DataRangeIntersection +( + _In_ ULONG PinId, + _In_ PKSDATARANGE ClientDataRange, + _In_ PKSDATARANGE MyDataRange, + _In_ ULONG OutputBufferLength, + _Out_writes_bytes_to_opt_(OutputBufferLength, *ResultantFormatLength) + PVOID ResultantFormat, + _Out_ PULONG ResultantFormatLength +) +/*++ + +Routine Description: + + The DataRangeIntersection function determines the highest quality + intersection of two data ranges. + +Arguments: + + PinId - Pin for which data intersection is being determined. + + ClientDataRange - Pointer to KSDATARANGE structure which contains the data range + submitted by client in the data range intersection property + request. + + MyDataRange - Pin's data range to be compared with client's data range. + + OutputBufferLength - Size of the buffer pointed to by the resultant format + parameter. + + ResultantFormat - Pointer to value where the resultant format should be + returned. + + ResultantFormatLength - Actual length of the resultant format that is placed + at ResultantFormat. This should be less than or equal + to OutputBufferLength. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + return + CMiniportTopologySYSVAD::DataRangeIntersection + ( + PinId, + ClientDataRange, + MyDataRange, + OutputBufferLength, + ResultantFormat, + ResultantFormatLength + ); +} // DataRangeIntersection + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP +CMiniportTopology::GetDescription +( + _Out_ PPCFILTER_DESCRIPTOR * OutFilterDescriptor +) +/*++ + +Routine Description: + + The GetDescription function gets a pointer to a filter description. + It provides a location to deposit a pointer in miniport's description + structure. This is the placeholder for the FromNode or ToNode fields in + connections which describe connections to the filter's pins. + +Arguments: + + OutFilterDescriptor - Pointer to the filter description. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(OutFilterDescriptor); + + return CMiniportTopologySYSVAD::GetDescription(OutFilterDescriptor); +} // GetDescription + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP +CMiniportTopology::Init +( + _In_ PUNKNOWN UnknownAdapter, + _In_ PRESOURCELIST ResourceList, + _In_ PPORTTOPOLOGY Port_ +) +/*++ + +Routine Description: + + The Init function initializes the miniport. Callers of this function + should run at IRQL PASSIVE_LEVEL + +Arguments: + + UnknownAdapter - A pointer to the Iuknown interface of the adapter object. + + ResourceList - Pointer to the resource list to be supplied to the miniport + during initialization. The port driver is free to examine the + contents of the ResourceList. The port driver will not be + modify the ResourceList contents. + + Port - Pointer to the topology port object that is linked with this miniport. + +Return Value: + + NT status code. + +--*/ +{ + UNREFERENCED_PARAMETER(ResourceList); + + PAGED_CODE(); + + ASSERT(UnknownAdapter); + ASSERT(Port_); + + DPF_ENTER(("[CMiniportTopology::Init]")); + + NTSTATUS ntStatus; + + ntStatus = + CMiniportTopologySYSVAD::Init + ( + UnknownAdapter, + Port_ + ); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Init: CMiniportTopologySYSVAD::Init failed, 0x%x", ntStatus)), + Done); + +#ifdef SYSVAD_BTH_BYPASS + if (IsBthHfpDevice()) + { + PBTHHFPDEVICECOMMON bthHfpDevice = NULL; + + bthHfpDevice = GetBthHfpDevice(); // weak ref. + ASSERT(bthHfpDevice != NULL); + + // + // Register with BthHfpDevice to get notification events. + // + if (m_DeviceType == eBthHfpMicDevice) + { + bthHfpDevice->SetMicVolumeHandler( + EvtMicVolumeHandler, // handler + PCMiniportTopology(this)); // context. + + bthHfpDevice->SetMicConnectionStatusHandler( + EvtMicConnectionStatusHandler, // handler + PCMiniportTopology(this)); // context. + } + else + { + ASSERT(m_DeviceType == eBthHfpSpeakerDevice); + + bthHfpDevice->SetSpeakerVolumeHandler( + EvtSpeakerVolumeHandler, // handler + PCMiniportTopology(this)); // context. + + bthHfpDevice->SetSpeakerConnectionStatusHandler( + EvtSpeakerConnectionStatusHandler, // handler + PCMiniportTopology(this)); // context. + } + } +#endif // SYSVAD_BTH_BYPASS + +Done: + return ntStatus; +} // Init + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP +CMiniportTopology::NonDelegatingQueryInterface +( + _In_ REFIID Interface, + _COM_Outptr_ PVOID * Object +) +/*++ + +Routine Description: + + QueryInterface for MiniportTopology + +Arguments: + + Interface - GUID of the interface + + Object - interface object to be returned. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(Object); + + if (IsEqualGUIDAligned(Interface, IID_IUnknown)) + { + *Object = PVOID(PUNKNOWN(this)); + } + else if (IsEqualGUIDAligned(Interface, IID_IMiniport)) + { + *Object = PVOID(PMINIPORT(this)); + } + else if (IsEqualGUIDAligned(Interface, IID_IMiniportTopology)) + { + *Object = PVOID(PMINIPORTTOPOLOGY(this)); + } + else + { + *Object = NULL; + } + + if (*Object) + { + // We reference the interface for the caller. + PUNKNOWN(*Object)->AddRef(); + return(STATUS_SUCCESS); + } + + return(STATUS_INVALID_PARAMETER); +} // NonDelegatingQueryInterface + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CMiniportTopology::PropertyHandlerJackDescription +( + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG cJackDescriptions, + _In_reads_(cJackDescriptions) PKSJACK_DESCRIPTION * JackDescriptions +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_Jack, KSPROPERTY_JACK_DESCRIPTION ) + +Arguments: + + PropertyRequest - + cJackDescriptions - # of elements in the jack descriptions array. + JackDescriptions - Array of jack descriptions pointers. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandlerJackDescription]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + ULONG nPinId = (ULONG)-1; + + if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + nPinId = *(PULONG(PropertyRequest->Instance)); + + if ((nPinId < cJackDescriptions) && (JackDescriptions[nPinId] != NULL)) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else + { + ULONG cbNeeded = sizeof(KSMULTIPLE_ITEM) + sizeof(KSJACK_DESCRIPTION); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbNeeded; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbNeeded) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PKSMULTIPLE_ITEM pMI = (PKSMULTIPLE_ITEM)PropertyRequest->Value; + PKSJACK_DESCRIPTION pDesc = (PKSJACK_DESCRIPTION)(pMI+1); + + pMI->Size = cbNeeded; + pMI->Count = 1; + + RtlCopyMemory(pDesc, JackDescriptions[nPinId], sizeof(KSJACK_DESCRIPTION)); + ntStatus = STATUS_SUCCESS; + } + } + } + } + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CMiniportTopology::PropertyHandlerJackDescription2 +( + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG cJackDescriptions, + _In_reads_(cJackDescriptions) PKSJACK_DESCRIPTION * JackDescriptions, + _In_ DWORD JackCapabilities +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_Jack, KSPROPERTY_JACK_DESCRIPTION2 ) + +Arguments: + + PropertyRequest - + cJackDescriptions - # of elements in the jack descriptions array. + JackDescriptions - Array of jack descriptions pointers. + JackCapabilities - Jack capabilities flags. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandlerJackDescription2]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + ULONG nPinId = (ULONG)-1; + + if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + nPinId = *(PULONG(PropertyRequest->Instance)); + + if ((nPinId < cJackDescriptions) && (JackDescriptions[nPinId] != NULL)) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else + { + ULONG cbNeeded = sizeof(KSMULTIPLE_ITEM) + sizeof(KSJACK_DESCRIPTION2); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbNeeded; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbNeeded) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PKSMULTIPLE_ITEM pMI = (PKSMULTIPLE_ITEM)PropertyRequest->Value; + PKSJACK_DESCRIPTION2 pDesc = (PKSJACK_DESCRIPTION2)(pMI+1); + + pMI->Size = cbNeeded; + pMI->Count = 1; + + RtlZeroMemory(pDesc, sizeof(KSJACK_DESCRIPTION2)); + + // + // Specifies the lower 16 bits of the DWORD parameter. This parameter indicates whether + // the jack is currently active, streaming, idle, or hardware not ready. + // + pDesc->DeviceStateInfo = 0; + + // + // From MSDN: + // "If an audio device lacks jack presence detection, the IsConnected member of + // the KSJACK_DESCRIPTION structure must always be set to TRUE. To remove the + // ambiguity that results from this dual meaning of the TRUE value for IsConnected, + // a client application can call IKsJackDescription2::GetJackDescription2 to read + // the JackCapabilities flag of the KSJACK_DESCRIPTION2 structure. If this flag has + // the JACKDESC2_PRESENCE_DETECT_CAPABILITY bit set, it indicates that the endpoint + // does in fact support jack presence detection. In that case, the return value of + // the IsConnected member can be interpreted to accurately reflect the insertion status + // of the jack." + // + // Bit definitions: + // 0x00000001 - JACKDESC2_PRESENCE_DETECT_CAPABILITY + // 0x00000002 - JACKDESC2_DYNAMIC_FORMAT_CHANGE_CAPABILITY + // + pDesc->JackCapabilities = JackCapabilities; + + ntStatus = STATUS_SUCCESS; + } + } + } + } + } + + return ntStatus; +} + +#ifdef SYSVAD_BTH_BYPASS +//============================================================================= +#pragma code_seg() +VOID +CMiniportTopology::EvtSpeakerVolumeHandler +( + _In_opt_ PVOID Context +) +{ + DPF_ENTER(("[CMiniportTopologySYSVAD::EvtSpeakerVolumeHandler]")); + + PCMiniportTopology This = PCMiniportTopology(Context); + if (This == NULL) + { + DPF(D_ERROR, ("EvtSpeakerVolumeHandler: context is null")); + return; + } + + This->GenerateEventList( + (GUID*)&KSEVENTSETID_AudioControlChange, // event set. NULL is a wild card for all events. + KSEVENT_CONTROL_CHANGE, // event ID. + FALSE, // do not use pid ID. + ULONG(-1), // pin ID, not used. + TRUE, // use node ID + KSNODE_TOPO_VOLUME); // node ID. +} + +//============================================================================= +#pragma code_seg() +VOID +CMiniportTopology::EvtSpeakerConnectionStatusHandler +( + _In_opt_ PVOID Context +) +{ + DPF_ENTER(("[CMiniportTopologySYSVAD::EvtSpeakerConnectionStatusHandler]")); + + PCMiniportTopology This = PCMiniportTopology(Context); + if (This == NULL) + { + DPF(D_ERROR, ("EvtSpeakerConnectionStatusHandler: context is null")); + return; + } + + This->GenerateEventList( + (GUID*)&KSEVENTSETID_PinCapsChange, // event set. NULL is a wild card for all events. + KSEVENT_PINCAPS_JACKINFOCHANGE, // event ID. + TRUE, // use pid ID. + KSPIN_TOPO_LINEOUT_DEST, // pin ID. + FALSE, // do not use node ID. + ULONG(-1)); // node ID, not used. +} + +//============================================================================= +#pragma code_seg() +VOID +CMiniportTopology::EvtMicVolumeHandler +( + _In_opt_ PVOID Context +) +{ + DPF_ENTER(("[CMiniportTopologySYSVAD::EvtMicVolumeHandler]")); + + PCMiniportTopology This = PCMiniportTopology(Context); + if (This == NULL) + { + DPF(D_ERROR, ("EvtMicVolumeHandler: context is null")); + return; + } + + This->GenerateEventList( + (GUID*)&KSEVENTSETID_AudioControlChange, // event set. NULL is a wild card for all events. + KSEVENT_CONTROL_CHANGE, // event ID. + FALSE, // do not use pid ID. + ULONG(-1), // pin ID, not used. + TRUE, // use node ID. + KSNODE_TOPO_VOLUME); // node ID. +} + +//============================================================================= +#pragma code_seg() +VOID +CMiniportTopology::EvtMicConnectionStatusHandler +( + _In_opt_ PVOID Context +) +{ + DPF_ENTER(("[CMiniportTopologySYSVAD::EvtMicConnectionStatusHandler]")); + + PCMiniportTopology This = PCMiniportTopology(Context); + if (This == NULL) + { + DPF(D_ERROR, ("EvtMicConnectionStatusHandler: context is null")); + return; + } + + This->GenerateEventList( + (GUID*)&KSEVENTSETID_PinCapsChange, // event set. NULL is a wild card for all events. + KSEVENT_PINCAPS_JACKINFOCHANGE, // event ID. + TRUE, // use pid ID. + KSPIN_TOPO_MIC_ELEMENTS, // pin ID. + FALSE, // do not use node ID. + ULONG(-1)); // node ID, not used. +} +#endif // SYSVAD_BTH_BYPASS + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +PropertyHandler_Topology +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Redirects property request to miniport object + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_Topology]")); + + // PropertryRequest structure is filled by portcls. + // MajorTarget is a pointer to miniport object for miniports. + // + return + ((PCMiniportTopology) + (PropertyRequest->MajorTarget))->PropertyHandlerGeneric + ( + PropertyRequest + ); +} // PropertyHandler_Topology + +#pragma code_seg() + diff --git a/audio/sysvad/EndpointsCommon/mintopo.h b/audio/sysvad/EndpointsCommon/mintopo.h new file mode 100644 index 000000000..e232e1d77 --- /dev/null +++ b/audio/sysvad/EndpointsCommon/mintopo.h @@ -0,0 +1,145 @@ + +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + minitopo.h + +Abstract: + + Declaration of topology miniport. + +--*/ + +#ifndef _SYSVAD_MINTOPO_H_ +#define _SYSVAD_MINTOPO_H_ + +#include "basetopo.h" + +//============================================================================= +// Classes +//============================================================================= + +/////////////////////////////////////////////////////////////////////////////// +// CMiniportTopology +// + +class CMiniportTopology : + public CMiniportTopologySYSVAD, + public IMiniportTopology, + public CUnknown +{ + private: + eDeviceType m_DeviceType; + union { + PVOID m_DeviceContext; +#ifdef SYSVAD_BTH_BYPASS + PBTHHFPDEVICECOMMON m_BthHfpDevice; +#endif // SYSVAD_BTH_BYPASS + }; + +public: + DECLARE_STD_UNKNOWN(); + CMiniportTopology + ( + _In_opt_ PUNKNOWN UnknownOuter, + _In_ PCFILTER_DESCRIPTOR *FilterDesc, + _In_ USHORT DeviceMaxChannels, + _In_ eDeviceType DeviceType, + _In_opt_ PVOID DeviceContext + ) + : CUnknown(UnknownOuter), + CMiniportTopologySYSVAD(FilterDesc, DeviceMaxChannels), + m_DeviceType(DeviceType), + m_DeviceContext(DeviceContext) + { +#ifdef SYSVAD_BTH_BYPASS + if (IsBthHfpDevice()) + { + if (m_BthHfpDevice != NULL) + { + // This ref is released on dtor. + m_BthHfpDevice->AddRef(); // strong ref. + } + } +#endif // SYSVAD_BTH_BYPASS + } + + ~CMiniportTopology(); + + IMP_IMiniportTopology; + + NTSTATUS PropertyHandlerJackDescription + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG cJackDescriptions, + _In_reads_(cJackDescriptions) PKSJACK_DESCRIPTION * JackDescriptions + ); + + NTSTATUS PropertyHandlerJackDescription2 + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG cJackDescriptions, + _In_reads_(cJackDescriptions) PKSJACK_DESCRIPTION * JackDescriptions, + _In_ DWORD JackCapabilities + ); + +#ifdef SYSVAD_BTH_BYPASS + BOOL IsBthHfpDevice() + { + return (m_DeviceType == eBthHfpMicDevice || + m_DeviceType == eBthHfpSpeakerDevice) ? TRUE : FALSE; + } + + // Returns a weak ref to the Bluetooth HFP device. + PBTHHFPDEVICECOMMON GetBthHfpDevice() + { + PBTHHFPDEVICECOMMON bthHfpDevice = NULL; + + if (IsBthHfpDevice()) + { + if (m_BthHfpDevice != NULL) + { + bthHfpDevice = m_BthHfpDevice; + } + } + + return bthHfpDevice; + } + + static + VOID + EvtSpeakerVolumeHandler + ( + _In_opt_ PVOID Context + ); + + static + VOID + EvtSpeakerConnectionStatusHandler + ( + _In_opt_ PVOID Context + ); + + static + VOID + EvtMicVolumeHandler + ( + _In_opt_ PVOID Context + ); + + static + VOID + EvtMicConnectionStatusHandler + ( + _In_opt_ PVOID Context + ); +#endif // SYSVAD_BTH_BYPASS +}; + +typedef CMiniportTopology *PCMiniportTopology; + +#endif // _SYSVAD_MINTOPO_H_ + diff --git a/audio/sysvad/EndpointsCommon/minwavert.cpp b/audio/sysvad/EndpointsCommon/minwavert.cpp new file mode 100644 index 000000000..843706b1f --- /dev/null +++ b/audio/sysvad/EndpointsCommon/minwavert.cpp @@ -0,0 +1,2287 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + minwavert.cpp + +Abstract: + + Implementation of wavert miniport. + +--*/ + +#pragma warning (disable : 4127) + +#include +#include +#include "ContosoKeywordDetector.h" +#include "SysVadShared.h" +#include "simple.h" +#include "minwavert.h" +#include "minwavertstream.h" +#include "IHVPrivatePropertySet.h" + +//============================================================================= +// CMiniportWaveRT +//============================================================================= + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CreateMiniportWaveRTSYSVAD +( + _Out_ PUNKNOWN * Unknown, + _In_ REFCLSID, + _In_opt_ PUNKNOWN UnknownOuter, + _When_((PoolType & NonPagedPoolMustSucceed) != 0, + __drv_reportError("Must succeed pool allocations are forbidden. " + "Allocation failures cause a system crash")) + _In_ POOL_TYPE PoolType, + _In_ PUNKNOWN UnknownAdapter, + _In_opt_ PVOID DeviceContext, + _In_ PENDPOINT_MINIPAIR MiniportPair +) +/*++ + +Routine Description: + + Create the wavert miniport. + +Arguments: + + Unknown - + + RefClsId - + + UnknownOuter - + + PoolType - + + UnkownAdapter - + + DeviceContext - + + MiniportPair - + +Return Value: + + NT status code. + +--*/ +{ + UNREFERENCED_PARAMETER(UnknownOuter); + + PAGED_CODE(); + + ASSERT(Unknown); + ASSERT(MiniportPair); + + CMiniportWaveRT *obj = new (PoolType, MINWAVERT_POOLTAG) CMiniportWaveRT + ( + UnknownAdapter, + MiniportPair, + DeviceContext + ); + if (NULL == obj) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + obj->AddRef(); + *Unknown = reinterpret_cast(obj); + + return STATUS_SUCCESS; +} + +//============================================================================= +#pragma code_seg("PAGE") +CMiniportWaveRT::~CMiniportWaveRT +( + void +) +/*++ + +Routine Description: + + Destructor for wavert miniport + +Arguments: + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + DPF_ENTER(("[CMiniportWaveRT::~CMiniportWaveRT]")); + + if (m_pDeviceFormat) + { + ExFreePoolWithTag( m_pDeviceFormat, MINWAVERT_POOLTAG ); + m_pDeviceFormat = NULL; + } + + if (m_pMixFormat) + { + ExFreePoolWithTag( m_pMixFormat, MINWAVERT_POOLTAG ); + m_pMixFormat = NULL; + } + + if (m_pbMuted) + { + ExFreePoolWithTag( m_pbMuted, MINWAVERT_POOLTAG ); + m_pbMuted = NULL; + } + + if (m_plVolumeLevel) + { + ExFreePoolWithTag( m_plVolumeLevel, MINWAVERT_POOLTAG ); + m_plVolumeLevel = NULL; + } + + if (m_plPeakMeter) + { + ExFreePoolWithTag( m_plPeakMeter, MINWAVERT_POOLTAG ); + m_plPeakMeter = NULL; + } + + if (m_pDrmPort) + { + m_pDrmPort->Release(); + m_pDrmPort = NULL; + } + + if (m_pPortEvents) + { + m_pPortEvents->Release(); + m_pPortEvents = NULL; + } + + if (m_SystemStreams) + { + ExFreePoolWithTag( m_SystemStreams, MINWAVERT_POOLTAG ); + m_SystemStreams = NULL; + } + + if (m_OffloadStreams) + { + ExFreePoolWithTag( m_OffloadStreams, MINWAVERT_POOLTAG ); + m_OffloadStreams = NULL; + } + + if (m_LoopbackStreams) + { + ExFreePoolWithTag( m_LoopbackStreams, MINWAVERT_POOLTAG ); + m_LoopbackStreams = NULL; + } + +#ifdef SYSVAD_BTH_BYPASS + if (IsBthHfpDevice()) + { + SAFE_RELEASE(m_BthHfpDevice); + } +#endif // SYSVAD_BTH_BYPASS +} // ~CMiniportWaveRT + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP_(NTSTATUS) +CMiniportWaveRT::DataRangeIntersection +( + _In_ ULONG PinId, + _In_ PKSDATARANGE ClientDataRange, + _In_ PKSDATARANGE MyDataRange, + _In_ ULONG OutputBufferLength, + _Out_writes_bytes_to_opt_(OutputBufferLength, *ResultantFormatLength) + PVOID ResultantFormat, + _Out_ PULONG ResultantFormatLength +) +/*++ + +Routine Description: + + The DataRangeIntersection function determines the highest quality + intersection of two data ranges. + + This sample just validates the # of channels and lets the class handler + do the rest. + +Arguments: + + PinId - Pin for which data intersection is being determined. + + ClientDataRange - Pointer to KSDATARANGE structure which contains the data + range submitted by client in the data range intersection + property request. + + MyDataRange - Pin's data range to be compared with client's data + range. In this case we actually ignore our own data + range, because we know that we only support one range. + + OutputBufferLength - Size of the buffer pointed to by the resultant format + parameter. + + ResultantFormat - Pointer to value where the resultant format should be + returned. + + ResultantFormatLength - Actual length of the resultant format placed in + ResultantFormat. This should be less than or equal + to OutputBufferLength. + + Return Value: + + NT status code. + + Remarks: + + This sample driver's custom data intersection handler handles all the + audio endpoints defined in this driver. Some endpoints support mono formats + while others do not. The handler is written such that it requires an exact + match in MaximumChannels. This simplifies the handler but requires the pin + data ranges to include a separate data range for mono formats if the pin + supports mono formats. + +--*/ +{ + ULONG requiredSize; + + UNREFERENCED_PARAMETER(PinId); + UNREFERENCED_PARAMETER(ResultantFormat); + + PAGED_CODE(); + + if (!IsEqualGUIDAligned(ClientDataRange->Specifier, KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)) + { + return STATUS_NOT_IMPLEMENTED; + } + + requiredSize = sizeof (KSDATAFORMAT_WAVEFORMATEX); + + // + // Validate return buffer size, if the request is only for the + // size of the resultant structure, return it now before + // returning other types of errors. + // + if (!OutputBufferLength) + { + *ResultantFormatLength = requiredSize; + return STATUS_BUFFER_OVERFLOW; + } + else if (OutputBufferLength < requiredSize) + { + return STATUS_BUFFER_TOO_SMALL; + } + + // Verify channel count is supported. This routine assumes a separate data + // range for each supported channel count. + if (((PKSDATARANGE_AUDIO)MyDataRange)->MaximumChannels != ((PKSDATARANGE_AUDIO)ClientDataRange)->MaximumChannels) + { + return STATUS_NO_MATCH; + } + + // + // Ok, let the class handler do the rest. + // + return STATUS_NOT_IMPLEMENTED; +} // DataRangeIntersection + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP_(NTSTATUS) +CMiniportWaveRT::GetDescription +( + _Out_ PPCFILTER_DESCRIPTOR * OutFilterDescriptor +) +/*++ + +Routine Description: + + The GetDescription function gets a pointer to a filter description. + It provides a location to deposit a pointer in miniport's description + structure. + +Arguments: + + OutFilterDescriptor - Pointer to the filter description. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(OutFilterDescriptor); + + *OutFilterDescriptor = &m_FilterDesc; + + return STATUS_SUCCESS; +} // GetDescription + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP_(NTSTATUS) +CMiniportWaveRT::Init +( + _In_ PUNKNOWN UnknownAdapter_, + _In_ PRESOURCELIST ResourceList_, + _In_ PPORTWAVERT Port_ +) +/*++ + +Routine Description: + + The Init function initializes the miniport. Callers of this function + should run at IRQL PASSIVE_LEVEL + +Arguments: + + UnknownAdapter - A pointer to the Iuknown interface of the adapter object. + + ResourceList - Pointer to the resource list to be supplied to the miniport + during initialization. The port driver is free to examine the + contents of the ResourceList. The port driver will not be + modify the ResourceList contents. + + Port - Pointer to the topology port object that is linked with this miniport. + +Return Value: + + NT status code. + +--*/ +{ + UNREFERENCED_PARAMETER(UnknownAdapter_); + UNREFERENCED_PARAMETER(ResourceList_); + UNREFERENCED_PARAMETER(Port_); + PAGED_CODE(); + + ASSERT(UnknownAdapter_); + ASSERT(Port_); + + DPF_ENTER(("[CMiniportWaveRT::Init]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + size_t size; + + // + // Init class data members + // + m_ulLoopbackAllocated = 0; + m_ulSystemAllocated = 0; + m_ulOffloadAllocated = 0; + m_dwCaptureAllocatedModes = 0; + m_dwBiDiCaptureAllocatedModes = 0; + m_dwSystemAllocatedModes = 0; + m_SystemStreams = NULL; + m_OffloadStreams = NULL; + m_LoopbackStreams = NULL; + m_bGfxEnabled = FALSE; + m_pbMuted = NULL; + m_plVolumeLevel = NULL; + m_plPeakMeter = NULL; + m_pMixFormat = NULL; + m_pDeviceFormat = NULL; + m_ulMixDrmContentId = 0; + m_LoopbackProtection = CONSTRICTOR_OPTION_DISABLE; + RtlZeroMemory(&m_MixDrmRights, sizeof(m_MixDrmRights)); + + // + // Init the audio-engine used by the render devices. + // + if (IsRenderDevice()) + { + // Basic validation + if (m_ulMaxSystemStreams == 0 || + m_ulMaxLoopbackStreams == 0) + { + return STATUS_INVALID_DEVICE_STATE; + } + + // System streams. + size = sizeof(PCMiniportWaveRTStream) * m_ulMaxSystemStreams; + m_SystemStreams = (PCMiniportWaveRTStream *)ExAllocatePoolWithTag(NonPagedPoolNx, size, MINWAVERT_POOLTAG); + if (m_SystemStreams == NULL) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + RtlZeroMemory(m_SystemStreams, size); + + // Loopback streams. + size = sizeof(PCMiniportWaveRTStream) * m_ulMaxLoopbackStreams; + m_LoopbackStreams = (PCMiniportWaveRTStream *)ExAllocatePoolWithTag(NonPagedPoolNx, size, MINWAVERT_POOLTAG); + if (m_LoopbackStreams == NULL) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + RtlZeroMemory(m_LoopbackStreams, size); + + // Basic validation + if (IsOffloadSupported()) + { + PKSDATAFORMAT_WAVEFORMATEXTENSIBLE pDeviceFormats; + ULONG numDeviceFormats; + + if (m_ulMaxOffloadStreams == 0) + { + return STATUS_INVALID_DEVICE_STATE; + } + + // Offload streams. + size = sizeof(PCMiniportWaveRTStream) * m_ulMaxOffloadStreams; + m_OffloadStreams = (PCMiniportWaveRTStream *)ExAllocatePoolWithTag(NonPagedPoolNx, size, MINWAVERT_POOLTAG); + if (m_OffloadStreams == NULL) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + RtlZeroMemory(m_OffloadStreams, size); + + // Formats. + m_pDeviceFormat = (PKSDATAFORMAT_WAVEFORMATEXTENSIBLE)ExAllocatePoolWithTag(NonPagedPoolNx, sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), MINWAVERT_POOLTAG); + if (m_pDeviceFormat == NULL) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + numDeviceFormats = GetAudioEngineSupportedDeviceFormats(&pDeviceFormats); + + if (numDeviceFormats < 1) + { + return STATUS_UNSUCCESSFUL; + } + RtlCopyMemory((PVOID)(m_pDeviceFormat), (PVOID)(pDeviceFormats), sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE)); + + m_pMixFormat = (PKSDATAFORMAT_WAVEFORMATEXTENSIBLE)ExAllocatePoolWithTag(NonPagedPoolNx, sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), MINWAVERT_POOLTAG); + if (m_pMixFormat == NULL) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + m_pMixFormat->DataFormat.FormatSize = sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE); + m_pMixFormat->DataFormat.Flags = 0; + m_pMixFormat->DataFormat.Reserved = 0; + m_pMixFormat->DataFormat.SampleSize = 0; + m_pMixFormat->DataFormat.MajorFormat = KSDATAFORMAT_TYPE_AUDIO; + m_pMixFormat->DataFormat.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; + m_pMixFormat->DataFormat.Specifier = KSDATAFORMAT_SPECIFIER_WAVEFORMATEX; + m_pMixFormat->WaveFormatExt.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; + m_pMixFormat->WaveFormatExt.Format.nChannels = 2; + m_pMixFormat->WaveFormatExt.Format.nSamplesPerSec = 48000; + m_pMixFormat->WaveFormatExt.Format.nBlockAlign = 4; + m_pMixFormat->WaveFormatExt.Format.nAvgBytesPerSec = 192000; + m_pMixFormat->WaveFormatExt.Format.wBitsPerSample = 16; + m_pMixFormat->WaveFormatExt.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX); + m_pMixFormat->WaveFormatExt.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; + m_pMixFormat->WaveFormatExt.Samples.wValidBitsPerSample = 16; + m_pMixFormat->WaveFormatExt.dwChannelMask = KSAUDIO_SPEAKER_STEREO; + + m_bGfxEnabled = FALSE; + + m_pbMuted = (PBOOL)ExAllocatePoolWithTag(NonPagedPoolNx, m_DeviceMaxChannels * sizeof(BOOL), MINWAVERT_POOLTAG); + if (m_pbMuted == NULL) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + RtlZeroMemory(m_pbMuted, m_DeviceMaxChannels * sizeof(BOOL)); + + m_plVolumeLevel = (PLONG)ExAllocatePoolWithTag(NonPagedPoolNx, m_DeviceMaxChannels * sizeof(LONG), MINWAVERT_POOLTAG); + if (m_plVolumeLevel == NULL) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + RtlZeroMemory(m_plVolumeLevel, m_DeviceMaxChannels * sizeof(LONG)); + + m_plPeakMeter = (PLONG)ExAllocatePoolWithTag(NonPagedPoolNx, m_DeviceMaxChannels * sizeof(LONG), MINWAVERT_POOLTAG); + if (m_plPeakMeter == NULL) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + RtlZeroMemory(m_plPeakMeter, m_DeviceMaxChannels * sizeof(LONG)); + } + + // + // For DRM support. + // + if (!NT_SUCCESS(Port_->QueryInterface(IID_IDrmPort2, (PVOID *)&m_pDrmPort))) + { + m_pDrmPort = NULL; + } + } + + // + // For KS event support. + // + if (!NT_SUCCESS(Port_->QueryInterface(IID_IPortEvents, (PVOID *)&m_pPortEvents))) + { + m_pPortEvents = NULL; + } + + return ntStatus; +} // Init + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP_(NTSTATUS) +CMiniportWaveRT::NewStream +( + _Out_ PMINIPORTWAVERTSTREAM * OutStream, + _In_ PPORTWAVERTSTREAM OuterUnknown, + _In_ ULONG Pin, + _In_ BOOLEAN Capture, + _In_ PKSDATAFORMAT DataFormat +) +/*++ + +Routine Description: + + The NewStream function creates a new instance of a logical stream + associated with a specified physical channel. Callers of NewStream should + run at IRQL PASSIVE_LEVEL. + +Arguments: + + OutStream - + + OuterUnknown - + + Pin - + + Capture - + + DataFormat - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(OutStream); + ASSERT(DataFormat); + + DPF_ENTER(("[CMiniportWaveRT::NewStream]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + PCMiniportWaveRTStream stream = NULL; + GUID signalProcessingMode = AUDIO_SIGNALPROCESSINGMODE_DEFAULT; + + *OutStream = NULL; + + // + // If the data format attributes were specified, extract them. + // + if ( DataFormat->Flags & KSDATAFORMAT_ATTRIBUTES ) + { + // The attributes are aligned (QWORD alignment) after the data format + PKSMULTIPLE_ITEM attributes = (PKSMULTIPLE_ITEM) (((PBYTE)DataFormat) + ((DataFormat->FormatSize + FILE_QUAD_ALIGNMENT) & ~FILE_QUAD_ALIGNMENT)); + ntStatus = GetAttributesFromAttributeList(attributes, attributes->Size, &signalProcessingMode); + } + + // Check if we have enough streams. + // + if (NT_SUCCESS(ntStatus)) + { + ntStatus = ValidateStreamCreate(Pin, Capture, signalProcessingMode); + } + + // Determine if the format is valid. + // + if (NT_SUCCESS(ntStatus)) + { + ntStatus = IsFormatSupported(Pin, Capture, DataFormat); + } + + // Instantiate a stream. Stream must be in + // NonPagedPool(Nx) because of file saving. + // + if (NT_SUCCESS(ntStatus)) + { + stream = new (NonPagedPoolNx, MINWAVERT_POOLTAG) + CMiniportWaveRTStream(NULL); + + if (stream) + { + stream->AddRef(); + + ntStatus = + stream->Init + ( + this, + OuterUnknown, + Pin, + Capture, + DataFormat, + signalProcessingMode + ); + } + else + { + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + } + } + + if (NT_SUCCESS(ntStatus)) + { + *OutStream = PMINIPORTWAVERTSTREAM(stream); + (*OutStream)->AddRef(); + + // The stream has references now for the caller. The caller expects these + // references to be there. + } + + // This is our private reference to the stream. The caller has + // its own, so we can release in any case. + // + if (stream) + { + stream->Release(); + } + + return ntStatus; +} // NewStream + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP_(NTSTATUS) +CMiniportWaveRT::NonDelegatingQueryInterface +( + _In_ REFIID Interface, + _COM_Outptr_ PVOID * Object +) +/*++ + +Routine Description: + + QueryInterface + +Arguments: + + Interface - GUID + + Object - interface pointer to be returned. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(Object); + + if (IsEqualGUIDAligned(Interface, IID_IUnknown)) + { + *Object = PVOID(PUNKNOWN(PMINIPORTWAVERT(this))); + } + else if (IsEqualGUIDAligned(Interface, IID_IMiniport)) + { + *Object = PVOID(PMINIPORT(this)); + } + else if (IsEqualGUIDAligned(Interface, IID_IMiniportWaveRT)) + { + *Object = PVOID(PMINIPORTWAVERT(this)); + } + else if (IsEqualGUIDAligned(Interface, IID_IMiniportAudioSignalProcessing)) + { + *Object = PVOID(PMINIPORTAudioSignalProcessing(this)); + } + // In this sample, IMiniportAudioEngineNode is supported only for offloading endpoints. + // at thso moment, offload could only be enabled for render endpoints not capture. + // Incorrectly support IMiniportAudioEngineNode interface by the miniport without underlying + // HWAudioEngine node will cause miniport::Init to fail. + else if (IsEqualGUIDAligned(Interface, IID_IMiniportAudioEngineNode) && IsOffloadSupported()) + { + *Object = (PVOID)(IMiniportAudioEngineNode*)this; + } + else + { + *Object = NULL; + } + + if (*Object) + { + // We reference the interface for the caller. + + PUNKNOWN(*Object)->AddRef(); + return STATUS_SUCCESS; + } + + return STATUS_INVALID_PARAMETER; +} // NonDelegatingQueryInterface + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP_(NTSTATUS) CMiniportWaveRT::GetDeviceDescription(_Out_ PDEVICE_DESCRIPTION DmaDeviceDescription) +{ + PAGED_CODE (); + + ASSERT (DmaDeviceDescription); + + DPF_ENTER(("[CMiniportWaveRT::GetDeviceDescription]")); + + RtlZeroMemory (DmaDeviceDescription, sizeof (DEVICE_DESCRIPTION)); + + // + // Init device description. This sample is using the same info for all m_DeviceType(s). + // + + DmaDeviceDescription->Master = TRUE; + DmaDeviceDescription->ScatterGather = TRUE; + DmaDeviceDescription->Dma32BitAddresses = TRUE; + DmaDeviceDescription->InterfaceType = PCIBus; + DmaDeviceDescription->MaximumLength = 0xFFFFFFFF; + + return STATUS_SUCCESS; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CMiniportWaveRT::GetModes +( + _In_ ULONG Pin, + _Out_writes_opt_(*NumSignalProcessingModes) GUID* SignalProcessingModes, + _Inout_ ULONG* NumSignalProcessingModes +) +/* + +1. If Pin is not a valid pin number, + return STATUS_INVALID_PARAMETER. + +2. If Pin is a valid pin number and it supports n modes (n>0), + init out-parameters and return STATUS_SUCCESS. + +3. Else this pin doesn't support any mode, + return STATUS_NOT_SUPPORTED. + example: bridge pins or another mode-not-aware pins. + +*/ +{ + PAGED_CODE(); + + DPF_ENTER(("[CMiniportWaveRT::GetModes]")); + + NTSTATUS ntStatus = STATUS_INVALID_PARAMETER; + ULONG numModes = 0; + MODE_AND_DEFAULT_FORMAT *modeInfo = NULL; + + if (Pin >= m_pMiniportPair->WaveDescriptor->PinCount) + { + return STATUS_INVALID_PARAMETER; + } + + // + // This method is valid only on the following pins: + // render is offload capable: + // sink (#0) and offload (#1) pins. + // render is NOT offload capable: + // sink (#0) pin. + // capture device: + // source (#1) pin. + // + numModes = GetPinSupportedDeviceModes(Pin, &modeInfo); + if (numModes == 0) + { + return STATUS_NOT_SUPPORTED; + } + + // If caller requests the modes, verify sufficient buffer size then return the modes + if (SignalProcessingModes != NULL) + { + if (*NumSignalProcessingModes < numModes) + { + *NumSignalProcessingModes = numModes; + ntStatus = STATUS_BUFFER_TOO_SMALL; + goto Done; + } + + for (ULONG i=0; i 0); + *NumSignalProcessingModes = numModes; + ntStatus = STATUS_SUCCESS; + +Done: + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CMiniportWaveRT::ValidateStreamCreate +( + _In_ ULONG _Pin, + _In_ BOOLEAN _Capture, + _In_ GUID _SignalProcessingMode +) +{ + PAGED_CODE(); + + DPF_ENTER(("[CMiniportWaveRT::ValidateStreamCreate]")); + + NTSTATUS ntStatus = STATUS_NOT_SUPPORTED; + + if (_Capture) + { + if (IsLoopbackPin(_Pin)) + { + if (m_ulLoopbackAllocated < m_ulMaxLoopbackStreams) + { + ntStatus = STATUS_SUCCESS; + } + else + { + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + } + } + else if (IsSystemCapturePin(_Pin)) + { + VERIFY_MODE_RESOURCES_AVAILABLE(m_dwCaptureAllocatedModes, _SignalProcessingMode, ntStatus) + } + else if (IsCellularBiDiCapturePin(_Pin)) + { + VERIFY_MODE_RESOURCES_AVAILABLE(m_dwBiDiCaptureAllocatedModes, _SignalProcessingMode, ntStatus) + } + else if (m_DeviceFormatsAndModes[_Pin].PinType == PINTYPE::KeywordCapturePin) + { + ntStatus = STATUS_SUCCESS; + } + } + else + { + if (IsSystemRenderPin(_Pin)) + { + VERIFY_MODE_RESOURCES_AVAILABLE(m_dwSystemAllocatedModes, _SignalProcessingMode, ntStatus) + } + else if (IsOffloadPin(_Pin)) + { + if (m_ulOffloadAllocated < m_ulMaxOffloadStreams) + { + ntStatus = STATUS_SUCCESS; + } + else + { + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + } + } + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CMiniportWaveRT::StreamCreated +( + _In_ ULONG _Pin, + _In_ PCMiniportWaveRTStream _Stream +) +{ + PAGED_CODE(); + + PCMiniportWaveRTStream * streams = NULL; + ULONG count = 0; + + DPF_ENTER(("[CMiniportWaveRT::StreamOpened]")); + + if (IsSystemCapturePin(_Pin)) + { + ALLOCATE_MODE_RESOURCES(m_dwCaptureAllocatedModes, _Stream->GetSignalProcessingMode()) + return STATUS_SUCCESS; + } + if (IsCellularBiDiCapturePin(_Pin)) + { + ALLOCATE_MODE_RESOURCES(m_dwBiDiCaptureAllocatedModes, _Stream->GetSignalProcessingMode()) + return STATUS_SUCCESS; + } + else if (IsLoopbackPin(_Pin)) + { + m_ulLoopbackAllocated++; + streams = m_LoopbackStreams; + count = m_ulMaxLoopbackStreams; + _Stream->m_SaveData.Disable(m_MixDrmRights.CopyProtect); + } + else if (IsSystemRenderPin(_Pin)) + { + ALLOCATE_MODE_RESOURCES(m_dwSystemAllocatedModes, _Stream->GetSignalProcessingMode()) + m_ulSystemAllocated++; + streams = m_SystemStreams; + count = m_ulMaxSystemStreams; + } + else if (IsOffloadPin(_Pin)) + { + m_ulOffloadAllocated++; + streams = m_OffloadStreams; + count = m_ulMaxOffloadStreams; + } + + // + // Cache this stream's ptr. + // + if (streams != NULL) + { + ULONG i = 0; + for (; iGetSignalProcessingMode()) + return STATUS_SUCCESS; + } + if (IsCellularBiDiCapturePin(_Pin)) + { + FREE_MODE_RESOURCES(m_dwBiDiCaptureAllocatedModes, _Stream->GetSignalProcessingMode()) + return STATUS_SUCCESS; + } + else if (IsLoopbackPin(_Pin)) + { + m_ulLoopbackAllocated--; + streams = m_LoopbackStreams; + count = m_ulMaxLoopbackStreams; + } + else if (IsSystemRenderPin(_Pin)) + { + FREE_MODE_RESOURCES(m_dwSystemAllocatedModes, _Stream->GetSignalProcessingMode()) + m_ulSystemAllocated--; + streams = m_SystemStreams; + count = m_ulMaxSystemStreams; + updateDrmRights = true; + } + else if (IsOffloadPin(_Pin)) + { + m_ulOffloadAllocated--; + streams = m_OffloadStreams; + count = m_ulMaxOffloadStreams; + updateDrmRights = true; + } + + // + // Cleanup. + // + if (streams != NULL) + { + ULONG i = 0; + for (; iCount; i++) + { + if (cbRemaining < sizeof(KSATTRIBUTE)) + { + return STATUS_INVALID_PARAMETER; + } + + if (attributeHeader->Attribute == KSATTRIBUTEID_AUDIOSIGNALPROCESSING_MODE) + { + KSATTRIBUTE_AUDIOSIGNALPROCESSING_MODE* signalProcessingModeAttribute; + + if (cbRemaining < sizeof(KSATTRIBUTE_AUDIOSIGNALPROCESSING_MODE)) + { + return STATUS_INVALID_PARAMETER; + } + + if (attributeHeader->Size != sizeof(KSATTRIBUTE_AUDIOSIGNALPROCESSING_MODE)) + { + return STATUS_INVALID_PARAMETER; + } + + signalProcessingModeAttribute = (KSATTRIBUTE_AUDIOSIGNALPROCESSING_MODE*)attributeHeader; + + // Return mode to caller. + *_pSignalProcessingMode = signalProcessingModeAttribute->SignalProcessingMode; + } + else + { + return STATUS_NOT_SUPPORTED; + } + + // Adjust pointer and buffer size to next attribute (QWORD aligned) + ULONG cbAttribute = ((attributeHeader->Size + FILE_QUAD_ALIGNMENT) & ~FILE_QUAD_ALIGNMENT); + + attributeHeader = (PKSATTRIBUTE) (((PBYTE)attributeHeader) + cbAttribute); + cbRemaining -= cbAttribute; + } + + return STATUS_SUCCESS; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CMiniportWaveRT::IsFormatSupported +( + _In_ ULONG _ulPin, + _In_ BOOLEAN _bCapture, + _In_ PKSDATAFORMAT _pDataFormat +) +{ + PAGED_CODE(); + + DPF_ENTER(("[CMiniportWaveRT::IsFormatSupported]")); + + NTSTATUS ntStatus = STATUS_NO_MATCH; + PKSDATAFORMAT_WAVEFORMATEXTENSIBLE pPinFormats = NULL; + ULONG cPinFormats = 0; + + UNREFERENCED_PARAMETER(_bCapture); + + if (_ulPin >= m_pMiniportPair->WaveDescriptor->PinCount) + { + return STATUS_INVALID_PARAMETER; + } + + cPinFormats = GetPinSupportedDeviceFormats(_ulPin, &pPinFormats); + + for (UINT iFormat = 0; iFormat < cPinFormats; iFormat++) + { + PKSDATAFORMAT_WAVEFORMATEXTENSIBLE pFormat = &pPinFormats[iFormat]; + // KSDATAFORMAT VALIDATION + if (!IsEqualGUIDAligned(pFormat->DataFormat.MajorFormat, _pDataFormat->MajorFormat)) { continue; } + if (!IsEqualGUIDAligned(pFormat->DataFormat.SubFormat, _pDataFormat->SubFormat)) { continue; } + if (!IsEqualGUIDAligned(pFormat->DataFormat.Specifier, _pDataFormat->Specifier)) { continue; } + if (pFormat->DataFormat.FormatSize < sizeof(KSDATAFORMAT_WAVEFORMATEX)) { continue; } + + // WAVEFORMATEX VALIDATION + PWAVEFORMATEX pWaveFormat = reinterpret_cast(_pDataFormat + 1); + + if (pWaveFormat->wFormatTag != WAVE_FORMAT_EXTENSIBLE) + { + if (pWaveFormat->wFormatTag != EXTRACT_WAVEFORMATEX_ID(&(pFormat->WaveFormatExt.SubFormat))) { continue; } + } + if (pWaveFormat->nChannels != pFormat->WaveFormatExt.Format.nChannels) { continue; } + if (pWaveFormat->nSamplesPerSec != pFormat->WaveFormatExt.Format.nSamplesPerSec) { continue; } + if (pWaveFormat->nBlockAlign != pFormat->WaveFormatExt.Format.nBlockAlign) { continue; } + if (pWaveFormat->wBitsPerSample != pFormat->WaveFormatExt.Format.wBitsPerSample) { continue; } + + if (pWaveFormat->wFormatTag != WAVE_FORMAT_EXTENSIBLE) + { + ntStatus = STATUS_SUCCESS; + break; + } + + // WAVEFORMATEXTENSIBLE VALIDATION + if (pWaveFormat->cbSize < sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)) { continue; } + + PWAVEFORMATEXTENSIBLE pWaveFormatExt = reinterpret_cast(pWaveFormat); + if (pWaveFormatExt->Samples.wValidBitsPerSample != pFormat->WaveFormatExt.Samples.wValidBitsPerSample) { continue; } + if (pWaveFormatExt->dwChannelMask != pFormat->WaveFormatExt.dwChannelMask) { continue; } + if (!IsEqualGUIDAligned(pWaveFormatExt->SubFormat, pFormat->WaveFormatExt.SubFormat)) { continue; } + + ntStatus = STATUS_SUCCESS; + break; + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CMiniportWaveRT::PropertyHandlerProposedFormat +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +{ + PKSP_PIN kspPin = NULL; + PKSDATAFORMAT pKsFormat = NULL; + ULONG cbMinSize = 0; + NTSTATUS ntStatus = STATUS_INVALID_PARAMETER; + + PAGED_CODE(); + + DPF_ENTER(("[CMiniportWaveRT::PropertyHandlerProposedFormat]")); + + // All properties handled by this handler require at least a KSP_PIN descriptor. + + // Verify instance data stores at least KSP_PIN fields beyond KSPPROPERTY. + if (PropertyRequest->InstanceSize < (sizeof(KSP_PIN) - RTL_SIZEOF_THROUGH_FIELD(KSP_PIN, Property))) + { + return STATUS_INVALID_PARAMETER; + } + + // Extract property descriptor from property request instance data + kspPin = CONTAINING_RECORD(PropertyRequest->Instance, KSP_PIN, PinId); + + // + // This method is valid only on streaming pins. + // + if (IsSystemRenderPin(kspPin->PinId) || + IsLoopbackPin(kspPin->PinId) || + IsOffloadPin(kspPin->PinId) || + IsSystemCapturePin(kspPin->PinId)|| + IsCellularBiDiCapturePin(kspPin->PinId)) + { + ntStatus = STATUS_SUCCESS; + } + else if (IsBridgePin(kspPin->PinId)) + { + ntStatus = STATUS_NOT_SUPPORTED; + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + + if (!NT_SUCCESS(ntStatus)) + { + return ntStatus; + } + + cbMinSize = sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE); + + // Handle KSPROPERTY_TYPE_BASICSUPPORT query + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ULONG flags = PropertyRequest->PropertyItem->Flags; + + return PropertyHandler_BasicSupport(PropertyRequest, flags, VT_ILLEGAL); + } + + // Verify value size + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbMinSize; + return STATUS_BUFFER_OVERFLOW; + } + if (PropertyRequest->ValueSize < cbMinSize) + { + return STATUS_BUFFER_TOO_SMALL; + } + + // Only SET is supported for this property + if ((PropertyRequest->Verb & KSPROPERTY_TYPE_SET) == 0) + { + return STATUS_INVALID_DEVICE_REQUEST; + } + + pKsFormat = (PKSDATAFORMAT)PropertyRequest->Value; + ntStatus = IsFormatSupported(kspPin->PinId, + IsSystemCapturePin(kspPin->PinId) || IsCellularBiDiCapturePin(kspPin->PinId) || + IsLoopbackPin(kspPin->PinId), + pKsFormat); + if (!NT_SUCCESS(ntStatus)) + { + return ntStatus; + } + + // + // Make sure there are enough resources to handle a new pin creation with + // this format. + // + if (IsOffloadPin(kspPin->PinId)) + { + ntStatus = ValidateStreamCreate(kspPin->PinId, FALSE, AUDIO_SIGNALPROCESSINGMODE_DEFAULT); + } + + return ntStatus; +} // PropertyHandlerProposedFormat + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CMiniportWaveRT::PropertyHandlerProposedFormat2 +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +{ + PKSP_PIN kspPin = NULL; + ULONG cbMinSize = 0; + NTSTATUS ntStatus = STATUS_INVALID_PARAMETER; + ULONG numModes = 0; + MODE_AND_DEFAULT_FORMAT *modeInfo = NULL; + MODE_AND_DEFAULT_FORMAT *modeTemp = NULL; + PKSMULTIPLE_ITEM pKsItemsHeader = NULL; + PKSMULTIPLE_ITEM pKsItemsHeaderOut = NULL; + size_t cbItemsList = 0; + GUID signalProcessingMode = {0}; + BOOLEAN bFound = FALSE; + ULONG i; + + PAGED_CODE(); + + DPF_ENTER(("[CMiniportWaveRT::PropertyHandlerProposedFormat2]")); + + // All properties handled by this handler require at least a KSP_PIN descriptor. + + // Verify instance data stores at least KSP_PIN fields beyond KSPPROPERTY. + if (PropertyRequest->InstanceSize < (sizeof(KSP_PIN) - RTL_SIZEOF_THROUGH_FIELD(KSP_PIN, Property))) + { + return STATUS_INVALID_PARAMETER; + } + + // Extract property descriptor from property request instance data + kspPin = CONTAINING_RECORD(PropertyRequest->Instance, KSP_PIN, PinId); + + if (kspPin->PinId >= m_pMiniportPair->WaveDescriptor->PinCount) + { + return STATUS_INVALID_PARAMETER; + } + + // + // This property is supported only on some streaming pins. + // + numModes = GetPinSupportedDeviceModes(kspPin->PinId, &modeInfo); + + ASSERT((modeInfo != NULL && numModes > 0) || (modeInfo == NULL && numModes == 0)); + + if (modeInfo == NULL) + { + return STATUS_NOT_SUPPORTED; + } + + // + // Even for pins that support modes, the pin might not support proposed formats + // + bFound = FALSE; + for (i=0, modeTemp=modeInfo; iDefaultFormat != NULL) + { + bFound = TRUE; + break; + } + } + + if (!bFound) + { + return STATUS_NOT_SUPPORTED; + } + + // + // The property is generally supported on this pin. Handle basic support request. + // + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + return PropertyHandler_BasicSupport(PropertyRequest, PropertyRequest->PropertyItem->Flags, VT_ILLEGAL); + } + + // + // Get the mode if specified. + // + pKsItemsHeader = (PKSMULTIPLE_ITEM)(kspPin + 1); + cbItemsList = (((PBYTE)PropertyRequest->Instance) + PropertyRequest->InstanceSize) - (PBYTE)pKsItemsHeader; + + ntStatus = GetAttributesFromAttributeList(pKsItemsHeader, cbItemsList, &signalProcessingMode); + if (!NT_SUCCESS(ntStatus)) + { + return ntStatus; + } + + // + // Get the info associated with this mode. + // + bFound = FALSE; + for (i=0; iMode == signalProcessingMode) + { + bFound = TRUE; + break; + } + } + + // Either the mode isn't supported, or the driver doesn't support a + // proprosed format for this specific mode. + if (!bFound || modeInfo->DefaultFormat == NULL) + { + return STATUS_NOT_SUPPORTED; + } + + // + // Compute output data buffer. + // + cbMinSize = modeInfo->DefaultFormat->FormatSize; + cbMinSize = (cbMinSize + 7) & ~7; + + pKsItemsHeaderOut = (PKSMULTIPLE_ITEM)((PBYTE)PropertyRequest->Value + cbMinSize); + + if (cbItemsList > MAXULONG) + { + return STATUS_INVALID_PARAMETER; + } + + // Total # of bytes. + ntStatus = RtlULongAdd(cbMinSize, (ULONG)cbItemsList, &cbMinSize); + if (!NT_SUCCESS(ntStatus)) + { + return STATUS_INVALID_PARAMETER; + } + + // Property not supported. + if (cbMinSize == 0) + { + return STATUS_NOT_SUPPORTED; + } + + // Verify value size + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbMinSize; + return STATUS_BUFFER_OVERFLOW; + } + if (PropertyRequest->ValueSize < cbMinSize) + { + return STATUS_BUFFER_TOO_SMALL; + } + + // Only GET is supported for this property + if ((PropertyRequest->Verb & KSPROPERTY_TYPE_GET) == 0) + { + return STATUS_INVALID_DEVICE_REQUEST; + } + + // Copy the proposed default format. + RtlCopyMemory(PropertyRequest->Value, modeInfo->DefaultFormat, modeInfo->DefaultFormat->FormatSize); + + // Copy back the attribute list. + ASSERT(cbItemsList > 0); + ((KSDATAFORMAT*)PropertyRequest->Value)->Flags = KSDATAFORMAT_ATTRIBUTES; + RtlCopyMemory(pKsItemsHeaderOut, pKsItemsHeader, cbItemsList); + + PropertyRequest->ValueSize = cbMinSize; + + return STATUS_SUCCESS; +} // PropertyHandlerProposedFormat + + + + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CMiniportWaveRT::PropertyHandlerEffectListRequest +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +{ + GUID StreamEffectList[] = + { + AUDIO_EFFECT_TYPE_LOUDNESS_EQUALIZER, + AUDIO_EFFECT_TYPE_VIRTUAL_SURROUND + }; + + PAGED_CODE(); + + DPF_ENTER(("[CMiniportWaveRT::PropertyHandlerEffectListRequest]")); + + // This specific APO->driver communication example is mainly added to show to this communication is done. + // It skips the pin id validation and returns pin specific answers to the caller, which a real miniport + // audio driver probably needs to tate care of. + + // Handle KSPROPERTY_TYPE_BASICSUPPORT query + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + return PropertyHandler_BasicSupport(PropertyRequest, PropertyRequest->PropertyItem->Flags, VT_ILLEGAL); + } + + // Verify instance data stores at least KSP_PIN fields beyond KSPPROPERTY. + if (PropertyRequest->InstanceSize < (sizeof(KSP_PIN) - RTL_SIZEOF_THROUGH_FIELD(KSP_PIN, Property))) + { + return STATUS_INVALID_PARAMETER; + } + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PKSMULTIPLE_ITEM ksMultipleItem; + ULONG ulEffectsCount = ARRAYSIZE(StreamEffectList); + ULONG cbMinSize; + LPGUID pEffectGuids = NULL; + + // Compute min value size requirements + cbMinSize = sizeof(KSMULTIPLE_ITEM) + ulEffectsCount * sizeof(GUID); + + // Verify value size + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbMinSize; + return STATUS_BUFFER_OVERFLOW; + } + if (PropertyRequest->ValueSize < cbMinSize) + { + return STATUS_BUFFER_TOO_SMALL; + } + // Value is a KSMULTIPLE_ITEM followed by list of GUIDs. + ksMultipleItem = (PKSMULTIPLE_ITEM)PropertyRequest->Value; + pEffectGuids = (LPGUID)(ksMultipleItem + 1); + + // Copy effect guid + RtlCopyMemory(pEffectGuids, StreamEffectList, ulEffectsCount * sizeof(GUID)); + + // Miniport filled in the list of GUIDs. Fill in the KSMULTIPLE_ITEM header. + ksMultipleItem->Size = sizeof(KSMULTIPLE_ITEM) + ulEffectsCount * sizeof(GUID); + ksMultipleItem->Count = ulEffectsCount; + + PropertyRequest->ValueSize = ksMultipleItem->Size; + return STATUS_SUCCESS; + } + + return STATUS_INVALID_DEVICE_REQUEST; + +} // PropertyHandlerEffectListRequest +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CMiniportWaveRT::UpdateDrmRights +( + void +) +/*++ + +Routine Description: + + Updates the mixed DrmRights. This is done by creating an array of existing + content ids and asking DrmPort to create a new contend id with a mixed + DrmRights structure. + The new DrmRights structure should be enforced, if everything goes well. + +Arguments: + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + DPF_ENTER(("[CMiniportWaveRT::UpdateDrmRights]")); + + NTSTATUS ntStatus = STATUS_UNSUCCESSFUL; + ULONG ulMixDrmContentId = 0; + BOOL fCreatedContentId = FALSE; + DRMRIGHTS MixDrmRights = {FALSE, 0, FALSE}; + ULONG ulContentIndex = 0; + ULONG* ulContentIds = NULL; + + // + // This function only runs if IID_DrmPort is implemented in Wave port. + // + if (!m_pDrmPort) + { + return STATUS_UNSUCCESSFUL; + } + + ulContentIds = new (NonPagedPoolNx, MINWAVERT_POOLTAG) ULONG[m_ulMaxSystemStreams + m_ulMaxOffloadStreams]; + if (!ulContentIds) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // Create an array of all StreamIds. + // + for (ULONG i = 0; i < m_ulMaxSystemStreams; i++) + { + if (m_SystemStreams[i]) + { + ulContentIds[ulContentIndex] = m_SystemStreams[i]->m_ulContentId; + ulContentIndex++; + } + } + + for (ULONG i = 0; i < m_ulMaxOffloadStreams; i++) + { + ASSERT(IsOffloadSupported()); + + if (m_OffloadStreams[i]) + { + ulContentIds[ulContentIndex] = m_OffloadStreams[i]->m_ulContentId; + ulContentIndex++; + } + } + + // + // Create the new contentId. + // + if (ulContentIndex) + { + ntStatus = + m_pDrmPort->CreateContentMixed + ( + ulContentIds, + ulContentIndex, + &ulMixDrmContentId + ); + + if (NT_SUCCESS(ntStatus)) + { + fCreatedContentId = TRUE; + ntStatus = + m_pDrmPort->GetContentRights + ( + ulMixDrmContentId, + &MixDrmRights + ); + } + } + + // + // If successful, destroy the old ContentId and update global rights. + // + if (NT_SUCCESS(ntStatus)) + { + m_pDrmPort->DestroyContent(m_ulMixDrmContentId); + m_ulMixDrmContentId = ulMixDrmContentId; + RtlCopyMemory(&m_MixDrmRights, &MixDrmRights, sizeof(m_MixDrmRights)); + + // + // At this point the driver should enforce the new DrmRights. + // The sample driver handles DrmRights per stream basis, and + // stops writing the stream to disk, if CopyProtect = TRUE. + // + + // + // If DigitalOutputDisable or CopyProtect is true, enable HDCP + // + if (m_DeviceType == eHdmiRenderDevice && + (m_MixDrmRights.DigitalOutputDisable || m_MixDrmRights.CopyProtect)) + { + // Enable HDCP here. + } + } + + // + // Cleanup if failed + // + if (!NT_SUCCESS(ntStatus) && fCreatedContentId) + { + m_pDrmPort->DestroyContent(ulMixDrmContentId); + } + + // + // Free allocated memory. + // + ASSERT(ulContentIds); + delete [] ulContentIds; + ulContentIds = NULL; + + return ntStatus; +} // UpdateDrmRights + +//============================================================================= +#pragma code_seg("PAGE") + +DEFINE_CLASSPROPERTYHANDLER(CMiniportWaveRT, Get_SoundDetectorSupportedPatterns) +{ + CONTOSO_SUPPORTEDPATTERNSVALUE *value; + + PAGED_CODE(); + + NT_ASSERT(PropertyRequest->ValueSize >= sizeof(*value)); + + // Does this filter support a sound detector? + if ((m_DeviceFlags & ENDPOINT_SOUNDDETECTOR_SUPPORTED) == 0) + { + return STATUS_NOT_SUPPORTED; + } + + value = (CONTOSO_SUPPORTEDPATTERNSVALUE*)PropertyRequest->Value; + + RtlZeroMemory(value, sizeof(*value)); + + value->MultipleItem.Size = sizeof(*value); + value->MultipleItem.Count = 1; + value->PatternType[0] = CONTOSO_KEYWORDCONFIGURATION_IDENTIFIER; + + PropertyRequest->ValueSize = sizeof(*value); + + return STATUS_SUCCESS; +} + +DEFINE_CLASSPROPERTYHANDLER(CMiniportWaveRT, Set_SoundDetectorPatterns) +{ + KSMULTIPLE_ITEM *itemsHeader; + SOUNDDETECTOR_PATTERNHEADER *patternHeader; + CONTOSO_KEYWORDCONFIGURATION *pattern; + ULONG cbRemaining; // Tracks bytes remaining in property value + + PAGED_CODE(); + + cbRemaining = PropertyRequest->ValueSize; + + // The SYSVADPROPERTY_ITEM for this property ensures the value size is at + // least sizeof KSMULTIPLE_ITEM. + if (cbRemaining < sizeof(KSMULTIPLE_ITEM)) + { + return STATUS_INVALID_PARAMETER; + } + + itemsHeader = (KSMULTIPLE_ITEM*)PropertyRequest->Value; + + // Verify property value is large enough to include the items + if (itemsHeader->Size > cbRemaining) + { + PropertyRequest->ValueSize = 0; + return STATUS_INVALID_PARAMETER; + } + + // No items so clear the configuration. + if (itemsHeader->Count == 0) + { + m_KeywordDetector.ResetDetector(); + return STATUS_SUCCESS; + } + + // This sample supports only 1 pattern type. + if (itemsHeader->Count > 1) + { + PropertyRequest->ValueSize = 0; + return STATUS_NOT_SUPPORTED; + } + + // Bytes remaining after the items header + cbRemaining = itemsHeader->Size - sizeof(*itemsHeader); + + // Verify the property value is large enough to include the pattern header. + if (cbRemaining < sizeof(SOUNDDETECTOR_PATTERNHEADER)) + { + PropertyRequest->ValueSize = 0; + return STATUS_INVALID_PARAMETER; + } + + patternHeader = (SOUNDDETECTOR_PATTERNHEADER*)(itemsHeader + 1); + + // Verify the pattern type is supported. + if (patternHeader->PatternType != CONTOSO_KEYWORDCONFIGURATION_IDENTIFIER) + { + PropertyRequest->ValueSize = 0; + return STATUS_NOT_SUPPORTED; + } + + // Verify the property value is large enough for the pattern. + if (cbRemaining < patternHeader->Size) + { + PropertyRequest->ValueSize = 0; + return STATUS_INVALID_PARAMETER; + } + + // Verify the pattern is large enough. + if (patternHeader->Size != sizeof(CONTOSO_KEYWORDCONFIGURATION)) + { + PropertyRequest->ValueSize = 0; + return STATUS_INVALID_PARAMETER; + } + + pattern = (CONTOSO_KEYWORDCONFIGURATION*)(patternHeader); + + // Program the hardware. + m_KeywordDetector.DownloadDetectorData(pattern->ContosoDetectorConfigurationData); + + return STATUS_SUCCESS; +} + +DEFINE_CLASSPROPERTYHANDLER(CMiniportWaveRT, Get_SoundDetectorArmed) +{ + PAGED_CODE(); + + // The SYSVADPROPERTY_ITEM for this property ensures the value size is at + // least sizeof BOOL. + NT_ASSERT(PropertyRequest->ValueSize >= sizeof(BOOL)); + + RtlZeroMemory(PropertyRequest->Value, PropertyRequest->ValueSize); + *(BOOL*)PropertyRequest->Value = m_KeywordDetector.GetArmed(); + + return STATUS_SUCCESS; +} + +DEFINE_CLASSPROPERTYHANDLER(CMiniportWaveRT, Set_SoundDetectorArmed) +{ + NTSTATUS ntStatus; + BOOL armed; + + PAGED_CODE(); + + // The SYSVADPROPERTY_ITEM for this property ensures the value size is at + // least sizeof BOOL. + NT_ASSERT(PropertyRequest->ValueSize >= sizeof(BOOL)); + + armed = ((*(BOOL*)PropertyRequest->Value) != 0); + + ntStatus = m_KeywordDetector.SetArmed(armed); + + if (NT_SUCCESS(ntStatus) && armed) + { + // FUTURE-2014/10/20 For now immediately signal a detection as soon as + // it is armed, but later, find a better way to demonstrate this from + // within CKeywordDetector. + m_pPortEvents->GenerateEventList(const_cast(&KSEVENTSETID_SoundDetector), KSEVENT_SOUNDDETECTOR_MATCHDETECTED, FALSE, 0, FALSE, 0); + m_KeywordDetector.SetArmed(FALSE); + } + + return ntStatus; +} + +DEFINE_CLASSPROPERTYHANDLER(CMiniportWaveRT, Get_SoundDetectorMatchResult) +{ + CONTOSO_KEYWORDDETECTIONRESULT *value; + + PAGED_CODE(); + + if (PropertyRequest->ValueSize < sizeof(*value)) + { + return STATUS_INVALID_PARAMETER; + } + + value = (CONTOSO_KEYWORDDETECTIONRESULT *)PropertyRequest->Value; + + RtlZeroMemory(value, sizeof(*value)); + + value->Header.Size = sizeof(CONTOSO_KEYWORDDETECTIONRESULT); + value->Header.PatternType = CONTOSO_KEYWORDCONFIGURATION_IDENTIFIER; + value->ContosoDetectorResultData = m_KeywordDetector.GetDetectorData(); + + PropertyRequest->ValueSize = sizeof(*value); + + return STATUS_SUCCESS; +} + +#pragma code_seg() +NTSTATUS CMiniportWaveRT_EventHandler_SoundDetectorMatchDetected +( + _In_ PPCEVENT_REQUEST EventRequest +) +{ + CMiniportWaveRT* miniport = reinterpret_cast(EventRequest->MajorTarget); + return miniport->EventHandler_SoundDetectorMatchDetected(EventRequest); +} + +#pragma code_seg() +NTSTATUS CMiniportWaveRT::EventHandler_SoundDetectorMatchDetected +( + _In_ PPCEVENT_REQUEST EventRequest +) +{ + if (EventRequest->Verb == PCEVENT_VERB_ADD) + { + _IRQL_limited_to_(PASSIVE_LEVEL); + m_pPortEvents->AddEventToEventList(EventRequest->EventEntry); + } + return STATUS_SUCCESS; +} + +#pragma code_seg("PAGE") +NTSTATUS +PropertyHandler_WaveFilter +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Redirects general property request to miniport object + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + CMiniportWaveRT* pWaveHelper = reinterpret_cast(PropertyRequest->MajorTarget); + + if (pWaveHelper == NULL) + { + return STATUS_INVALID_PARAMETER; + } + + pWaveHelper->AddRef(); + + if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_SysVAD)) + { + switch (PropertyRequest->PropertyItem->Id) + { + case KSPROPERTY_SYSVAD_DEFAULTSTREAMEFFECTS: + ntStatus = pWaveHelper->PropertyHandlerEffectListRequest(PropertyRequest); + break; + default: + DPF(D_TERSE, ("[PropertyHandler_WaveFilter: Invalid Device Request]")); + } + } + else if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_Pin)) + { + switch (PropertyRequest->PropertyItem->Id) + { + case KSPROPERTY_PIN_PROPOSEDATAFORMAT: + ntStatus = pWaveHelper->PropertyHandlerProposedFormat(PropertyRequest); + break; + + case KSPROPERTY_PIN_PROPOSEDATAFORMAT2: + ntStatus = pWaveHelper->PropertyHandlerProposedFormat2(PropertyRequest); + break; + + default: + DPF(D_TERSE, ("[PropertyHandler_WaveFilter: Invalid Device Request]")); + } + } + else if ((pWaveHelper->m_DeviceType == eHdmiRenderDevice || + pWaveHelper->m_DeviceType == eCellularDevice || + pWaveHelper->m_DeviceType == eHandsetSpeakerDevice) && + IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_Audio)) + { + switch (PropertyRequest->PropertyItem->Id) + { + case KSPROPERTY_AUDIO_VOLUMELEVEL: + ntStatus = PropertyHandler_Volume( + pWaveHelper->m_pAdapterCommon, + PropertyRequest, + pWaveHelper->m_DeviceMaxChannels); + break; + + case KSPROPERTY_AUDIO_MUTE: + ntStatus = PropertyHandler_Mute( + pWaveHelper->m_pAdapterCommon, + PropertyRequest, + pWaveHelper->m_DeviceMaxChannels); + break; + + case KSPROPERTY_AUDIO_PEAKMETER2: + ntStatus = PropertyHandler_PeakMeter2( + pWaveHelper->m_pAdapterCommon, + PropertyRequest, + pWaveHelper->m_DeviceMaxChannels); + break; + + case KSPROPERTY_AUDIO_CPU_RESOURCES: + ntStatus = PropertyHandler_CpuResources(PropertyRequest); + break; + + default: + DPF(D_TERSE, ("[PropertyHandler_WaveFilter: Invalid Device Request]")); + + } + } + + pWaveHelper->Release(); + + return ntStatus; +} // PropertyHandler_WaveFilter +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +PropertyHandler_OffloadPin +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +{ + PAGED_CODE(); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_OffloadPin)) + { + switch (PropertyRequest->PropertyItem->Id) + { + //KSPROPERTY_OFFLOAD_PIN_VERIFY_STREAM_OBJECT_POINTER + + case KSPROPERTY_OFFLOAD_PIN_GET_STREAM_OBJECT_POINTER: + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + ULONG cbMinSize = sizeof(ULONG_PTR); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbMinSize; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbMinSize) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + ULONG_PTR *streamObjectPtr = static_cast(PropertyRequest->Value); + *streamObjectPtr = (ULONG_PTR)(PropertyRequest->MinorTarget); + ntStatus = STATUS_SUCCESS; + } + } + + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + } + break; + case KSPROPERTY_OFFLOAD_PIN_VERIFY_STREAM_OBJECT_POINTER: + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_SET) + { + ULONG cbMinSize = sizeof(ULONG_PTR); + if (PropertyRequest->InstanceSize < cbMinSize) + { + return STATUS_INVALID_PARAMETER; + } + else + { + ULONG_PTR *streamObjectPtr = static_cast(PropertyRequest->Instance); + if (*streamObjectPtr == (ULONG_PTR)(PropertyRequest->MinorTarget)) + { + ntStatus = STATUS_SUCCESS; + } + else + { + ntStatus = STATUS_UNSUCCESSFUL; + } + } + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + } + break; + default: + DPF(D_TERSE, ("[PropertyHandler_OffloadPin: Invalid Request]")); + } + } + return ntStatus; +} + + +// ISSUE-2014/10/20 Add synchronization mechanism throughout this class +// ISSUE-2014/10/20 Add comment headers and commenting throughout +#pragma code_seg("PAGE") +CKeywordDetector::CKeywordDetector() + : + m_qpcStartCapture(0), + m_nLastQueuedPacket(-1), + m_SoundDetectorArmed(FALSE), + m_SoundDetectorData(0) +{ + PAGED_CODE(); + + // Initialize our pool of packets and the list structures + KeInitializeSpinLock(&PacketPoolSpinLock); + KeInitializeSpinLock(&PacketFifoSpinLock); + ResetFifo(); +} + +#pragma code_seg("PAGE") +VOID CKeywordDetector::ResetDetector() +{ + PAGED_CODE(); + + m_SoundDetectorData = 0; +} + +#pragma code_seg("PAGE") +VOID CKeywordDetector::DownloadDetectorData(LONGLONG Data) +{ + PAGED_CODE(); + + m_SoundDetectorData = Data; +} + +#pragma code_seg("PAGE") +LONGLONG CKeywordDetector::GetDetectorData() +{ + PAGED_CODE(); + + return m_SoundDetectorData; +} + +#pragma code_seg("PAGE") +VOID CKeywordDetector::ResetFifo() +{ + PAGED_CODE(); + + m_qpcStartCapture = 0; + m_nLastQueuedPacket = (-1); + InitializeListHead(&PacketPoolHead); + InitializeListHead(&PacketFifoHead); + + for (int i = 0; i < ARRAYSIZE(PacketPool); i++) + { + InsertTailList(&PacketPoolHead, &PacketPool[i].ListEntry); + } + return; +} + +#pragma code_seg("PAGE") +NTSTATUS CKeywordDetector::SetArmed(BOOL Arm) +{ + PAGED_CODE(); + + BOOL previousArming = m_SoundDetectorArmed; + + m_SoundDetectorArmed = Arm; + + if (Arm && !previousArming && m_qpcStartCapture == 0) + { + StartBufferingStream(); + } + return STATUS_SUCCESS; +} + +#pragma code_seg("PAGE") +BOOL CKeywordDetector::GetArmed() +{ + PAGED_CODE(); + + return m_SoundDetectorArmed; +} + +#pragma code_seg("PAGE") +VOID CKeywordDetector::Run() +{ + PAGED_CODE(); + + if (m_qpcStartCapture == 0) + { + StartBufferingStream(); + } +} + +#pragma code_seg("PAGE") +VOID CKeywordDetector::Stop() +{ + PAGED_CODE(); + + ResetFifo(); +} + +#pragma code_seg("PAGE") +VOID CKeywordDetector::StartBufferingStream() +{ + LARGE_INTEGER qpc; + LARGE_INTEGER qpcFrequency; + + PAGED_CODE(); + + NT_ASSERT(m_qpcStartCapture == 0); + NT_ASSERT(IsListEmpty(&PacketFifoHead)); + + qpc = KeQueryPerformanceCounter(&qpcFrequency); + m_qpcStartCapture = qpc.QuadPart; + + return; +} + +#pragma code_seg() +VOID CKeywordDetector::DpcRoutine(LONGLONG PerformanceCounter, LONGLONG PerformanceFrequency) +{ + LONGLONG currentPacket; + LONGLONG packetsToQueue; + + if (m_qpcStartCapture <= 0) + { + return; + } + + currentPacket = (PerformanceCounter - m_qpcStartCapture) * (SamplesPerSecond / SamplesPerPacket) / PerformanceFrequency; + packetsToQueue = currentPacket - m_nLastQueuedPacket; + + while (packetsToQueue > 0) + { + LIST_ENTRY* packetListEntry; + PACKET_ENTRY* packetEntry; + LONGLONG* signature; + + do + { + packetListEntry = ExInterlockedRemoveHeadList(&PacketPoolHead, &PacketPoolSpinLock); + if (packetListEntry != NULL) break; + + // Pool is empty, no room to buffer more, an overrun is occurring. Drop and reuse the + // oldest packet from head of fifo. + + // Since the pool is empty, the fifo should be full. However, although unlikely, the + // driver might empty the fifo before this routine removes a packet. In that case, the + // pool should have packets available again. Therefore this is a retry loop. + packetListEntry = ExInterlockedRemoveHeadList(&PacketFifoHead, &PacketFifoSpinLock); + if (packetListEntry != NULL) break; + } while (TRUE); + + packetEntry = CONTAINING_RECORD(packetListEntry, PACKET_ENTRY, ListEntry); + + packetEntry->PacketNumber = ++m_nLastQueuedPacket; + packetEntry->QpcWhenSampled = m_qpcStartCapture + (packetEntry->PacketNumber * PerformanceFrequency * SamplesPerPacket / SamplesPerSecond); + + RtlZeroMemory(&packetEntry->Samples[0], sizeof(packetEntry->Samples)); + + // For test purposes, embed the packet number and sample time into the audio data + signature = (LONGLONG*)(&packetEntry->Samples[0]); + + signature[0] = packetEntry->PacketNumber; + signature[1] = packetEntry->QpcWhenSampled; + + ExInterlockedInsertTailList(&PacketFifoHead, packetListEntry, &PacketFifoSpinLock); + + packetsToQueue -= 1; + } +} + +#pragma code_seg() +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS CKeywordDetector::GetReadPacket +( + _In_ ULONG PacketsPerWaveRtBuffer, + _In_ ULONG WaveRtBufferSize, + _Out_writes_(WaveRtBufferSize) BYTE *WaveRtBuffer, + _Out_ ULONG *PacketNumber, + _Out_ ULONG64 *PerformanceCounterValue, + _Out_ BOOL *MoreData +) +{ + NTSTATUS ntStatus; + BYTE *packetData; + PACKET_ENTRY *packetEntry; + LIST_ENTRY *packetListEntry = NULL; + ULONG packetSize = WaveRtBufferSize / PacketsPerWaveRtBuffer; + + NT_ASSERT(SamplesPerPacket * 2 == packetSize); + NT_ASSERT(sizeof(packetEntry->Samples) == packetSize); + + packetListEntry = ExInterlockedRemoveHeadList(&PacketFifoHead, &PacketFifoSpinLock); + if (packetListEntry == NULL) + { + ntStatus = STATUS_DEVICE_NOT_READY; + goto Exit; + } + packetEntry = CONTAINING_RECORD(packetListEntry, PACKET_ENTRY, ListEntry); + + packetData = WaveRtBuffer + ((packetEntry->PacketNumber * packetSize) % WaveRtBufferSize); + + ntStatus = RtlLongLongToULong(packetEntry->PacketNumber, PacketNumber); + if (!NT_SUCCESS(ntStatus)) + { + goto Exit; + } + + *PerformanceCounterValue = packetEntry->QpcWhenSampled; + *MoreData = !IsListEmpty(&PacketFifoHead); + RtlCopyMemory(packetData, packetEntry->Samples, sizeof(packetEntry->Samples)); + +Exit: + if (packetListEntry != NULL) + { + ExInterlockedInsertTailList(&PacketPoolHead, packetListEntry, &PacketPoolSpinLock); + } + + return ntStatus; +} + diff --git a/audio/sysvad/EndpointsCommon/minwavert.h b/audio/sysvad/EndpointsCommon/minwavert.h new file mode 100644 index 000000000..c30faa2d9 --- /dev/null +++ b/audio/sysvad/EndpointsCommon/minwavert.h @@ -0,0 +1,665 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + minwavert.h + +Abstract: + + Definition of wavert miniport class. + +--*/ + +#ifndef _SYSVAD_MINWAVERT_H_ +#define _SYSVAD_MINWAVERT_H_ + +#ifdef SYSVAD_BTH_BYPASS +#include "bthhfpmicwavtable.h" +#endif // SYSVAD_BTH_BYPASS + +//============================================================================= +// Referenced Forward +//============================================================================= +class CMiniportWaveRTStream; +typedef CMiniportWaveRTStream *PCMiniportWaveRTStream; + +//============================================================================= +// Classes +//============================================================================= +/////////////////////////////////////////////////////////////////////////////// +// CKeywordDetector +// +class CKeywordDetector +{ +public: + CKeywordDetector(); + + _IRQL_requires_max_(PASSIVE_LEVEL) + VOID ResetDetector(); + + _IRQL_requires_max_(PASSIVE_LEVEL) + VOID DownloadDetectorData(_In_ LONGLONG Data); + + _IRQL_requires_max_(PASSIVE_LEVEL) + LONGLONG GetDetectorData(); + + _IRQL_requires_max_(PASSIVE_LEVEL) + NTSTATUS SetArmed(_In_ BOOL Arm); + + _IRQL_requires_max_(PASSIVE_LEVEL) + BOOL GetArmed(); + + _IRQL_requires_max_(PASSIVE_LEVEL) + VOID Run(); + + _IRQL_requires_max_(PASSIVE_LEVEL) + VOID Stop(); + + _IRQL_requires_min_(DISPATCH_LEVEL) + VOID DpcRoutine(_In_ LONGLONG PeformanceCounter, _In_ LONGLONG PerformanceFrequency); + + _IRQL_requires_max_(PASSIVE_LEVEL) + NTSTATUS GetReadPacket(_In_ ULONG PacketsPerWaveRtBuffer, _In_ ULONG WaveRtBufferSize, _Out_writes_(WaveRtBufferSize) BYTE *WaveRtBuffer, _Out_ ULONG *PacketNumber, _Out_ ULONGLONG *PerformanceCount, _Out_ BOOL *MoreData); + +private: + _IRQL_requires_max_(PASSIVE_LEVEL) + VOID ResetFifo(); + + _IRQL_requires_max_(PASSIVE_LEVEL) + VOID StartBufferingStream(); + + // The Contoso keyword detector processes 10ms packets of 16KHz 16-bit PCM + // audio samples + static const int SamplesPerSecond = 16000; + static const int SamplesPerPacket = (10 * SamplesPerSecond / 1000); + + typedef struct + { + LIST_ENTRY ListEntry; + LONGLONG PacketNumber; + LONGLONG QpcWhenSampled; + UINT16 Samples[SamplesPerPacket]; + } PACKET_ENTRY; + + BOOL m_SoundDetectorArmed; + LONGLONG m_SoundDetectorData; + + LONGLONG m_qpcStartCapture; + LONGLONG m_nLastQueuedPacket; + + KSPIN_LOCK PacketPoolSpinLock; + LIST_ENTRY PacketPoolHead; + PACKET_ENTRY PacketPool[1 * SamplesPerSecond / SamplesPerPacket]; // Enough storage for 1 second of audio data + + KSPIN_LOCK PacketFifoSpinLock; + LIST_ENTRY PacketFifoHead; + +}; + +/////////////////////////////////////////////////////////////////////////////// +// CMiniportWaveRT +// +class CMiniportWaveRT : + public IMiniportWaveRT, + public IMiniportAudioEngineNode, + public IMiniportAudioSignalProcessing, + public CUnknown +{ +private: + ULONG m_ulLoopbackAllocated; + ULONG m_ulSystemAllocated; + ULONG m_ulOffloadAllocated; + DWORD m_dwCaptureAllocatedModes; + DWORD m_dwBiDiCaptureAllocatedModes; + DWORD m_dwSystemAllocatedModes; + + ULONG m_ulMaxSystemStreams; + ULONG m_ulMaxOffloadStreams; + ULONG m_ulMaxLoopbackStreams; + + // weak ref of running streams. + PCMiniportWaveRTStream * m_SystemStreams; + PCMiniportWaveRTStream * m_OffloadStreams; + PCMiniportWaveRTStream * m_LoopbackStreams; + + BOOL m_bGfxEnabled; + PBOOL m_pbMuted; + PLONG m_plVolumeLevel; + PLONG m_plPeakMeter; + PKSDATAFORMAT_WAVEFORMATEXTENSIBLE m_pMixFormat; + PKSDATAFORMAT_WAVEFORMATEXTENSIBLE m_pDeviceFormat; + PCFILTER_DESCRIPTOR m_FilterDesc; + PIN_DEVICE_FORMATS_AND_MODES * m_DeviceFormatsAndModes; + ULONG m_DeviceFormatsAndModesCount; + USHORT m_DeviceMaxChannels; + PDRMPORT m_pDrmPort; + DRMRIGHTS m_MixDrmRights; + ULONG m_ulMixDrmContentId; + CONSTRICTOR_OPTION m_LoopbackProtection; + + CKeywordDetector m_KeywordDetector; + + union { + PVOID m_DeviceContext; +#ifdef SYSVAD_BTH_BYPASS + PBTHHFPDEVICECOMMON m_BthHfpDevice; +#endif // SYSVAD_BTH_BYPASS + }; + +protected: + PADAPTERCOMMON m_pAdapterCommon; + ULONG m_DeviceFlags; + eDeviceType m_DeviceType; + PPORTEVENTS m_pPortEvents; + PENDPOINT_MINIPAIR m_pMiniportPair; + +public: + DECLARE_PROPERTYHANDLER(Get_SoundDetectorSupportedPatterns); + DECLARE_PROPERTYHANDLER(Set_SoundDetectorPatterns); + DECLARE_PROPERTYHANDLER(Get_SoundDetectorArmed); + DECLARE_PROPERTYHANDLER(Set_SoundDetectorArmed); + DECLARE_PROPERTYHANDLER(Get_SoundDetectorMatchResult); + + NTSTATUS EventHandler_SoundDetectorMatchDetected + ( + _In_ PPCEVENT_REQUEST EventRequest + ); + + NTSTATUS ValidateStreamCreate + ( + _In_ ULONG _Pin, + _In_ BOOLEAN _Capture, + _In_ GUID _SignalProcessingMode + ); + + NTSTATUS StreamCreated + ( + _In_ ULONG _Pin, + _In_ PCMiniportWaveRTStream _Stream + ); + + NTSTATUS StreamClosed + ( + _In_ ULONG _Pin, + _In_ PCMiniportWaveRTStream _Stream + ); + + NTSTATUS IsFormatSupported + ( + _In_ ULONG _ulPin, + _In_ BOOLEAN _bCapture, + _In_ PKSDATAFORMAT _pDataFormat + ); + + static NTSTATUS GetAttributesFromAttributeList + ( + _In_ const KSMULTIPLE_ITEM *_pAttributes, + _In_ size_t _Size, + _Out_ GUID* _pSignalProcessingMode + ); + +protected: + NTSTATUS UpdateDrmRights + ( + void + ); + + NTSTATUS SetLoopbackProtection + ( + _In_ CONSTRICTOR_OPTION ulProtectionOption + ); + +public: + DECLARE_STD_UNKNOWN(); + +#pragma code_seg("PAGE") + CMiniportWaveRT( + _In_ PUNKNOWN UnknownAdapter, + _In_ PENDPOINT_MINIPAIR MiniportPair, + _In_opt_ PVOID DeviceContext + ) + :CUnknown(0), + m_ulMaxSystemStreams(0), + m_ulMaxOffloadStreams(0), + m_ulMaxLoopbackStreams(0), + m_DeviceType(MiniportPair->DeviceType), + m_DeviceContext(DeviceContext), + m_DeviceMaxChannels(MiniportPair->DeviceMaxChannels), + m_DeviceFormatsAndModes(MiniportPair->PinDeviceFormatsAndModes), + m_DeviceFormatsAndModesCount(MiniportPair->PinDeviceFormatsAndModesCount), + m_DeviceFlags(MiniportPair->DeviceFlags), + m_pMiniportPair(MiniportPair) + { + PAGED_CODE(); + + m_pAdapterCommon = (PADAPTERCOMMON)UnknownAdapter; // weak ref. + + if (MiniportPair->WaveDescriptor) + { + RtlCopyMemory(&m_FilterDesc, MiniportPair->WaveDescriptor, sizeof(m_FilterDesc)); + + // + // Get the max # of pin instances. + // + if (IsRenderDevice()) + { + if (IsOffloadSupported()) + { + if (m_FilterDesc.PinCount > KSPIN_WAVE_RENDER_SOURCE) + { + m_ulMaxSystemStreams = m_FilterDesc.Pins[KSPIN_WAVE_RENDER_SINK_SYSTEM].MaxFilterInstanceCount; + m_ulMaxOffloadStreams = m_FilterDesc.Pins[KSPIN_WAVE_RENDER_SINK_OFFLOAD].MaxFilterInstanceCount; + m_ulMaxLoopbackStreams = m_FilterDesc.Pins[KSPIN_WAVE_RENDER_SINK_LOOPBACK].MaxFilterInstanceCount; + } + } + else + { + if (m_FilterDesc.PinCount > KSPIN_WAVE_RENDER2_SOURCE) + { + m_ulMaxSystemStreams = m_FilterDesc.Pins[KSPIN_WAVE_RENDER2_SINK_SYSTEM].MaxFilterInstanceCount; + m_ulMaxLoopbackStreams = m_FilterDesc.Pins[KSPIN_WAVE_RENDER2_SINK_LOOPBACK].MaxFilterInstanceCount; + } + } + } + } + +#ifdef SYSVAD_BTH_BYPASS + if (IsBthHfpDevice()) + { + if (m_BthHfpDevice != NULL) + { + // This ref is released on dtor. + m_BthHfpDevice->AddRef(); // strong ref. + } + } +#endif // SYSVAD_BTH_BYPASS + } + +#pragma code_seg() + + ~CMiniportWaveRT(); + + IMP_IMiniportWaveRT; + IMP_IMiniportAudioEngineNode; + IMP_IMiniportAudioSignalProcessing; + + // Friends + friend class CMiniportWaveRTStream; + friend class CMiniportTopologySimple; + + friend NTSTATUS PropertyHandler_WaveFilter + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + +public: + VOID DpcRoutine(LONGLONG PeformanceCounter, LONGLONG PerformanceFrequency) + { + m_KeywordDetector.DpcRoutine(PeformanceCounter, PerformanceFrequency); + } + + NTSTATUS PropertyHandlerEffectListRequest + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + + NTSTATUS PropertyHandlerProposedFormat + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + + NTSTATUS PropertyHandlerProposedFormat2 + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + + PADAPTERCOMMON GetAdapterCommObj() + { + return m_pAdapterCommon; + }; +#pragma code_seg() + +#ifdef SYSVAD_BTH_BYPASS + NTSTATUS PropertyHandler_BthHfpAudioEffectsDiscoveryEffectsList + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); +#endif // SYSVAD_BTH_BYPASS + + //--------------------------------------------------------------------------------------------------------- + // volume + //--------------------------------------------------------------------------------------------------------- + NTSTATUS GetVolumeChannelCount + ( + _Out_ UINT32 * pulChannelCount + ); + + NTSTATUS GetVolumeSteppings + ( + _Out_writes_bytes_(_ui32DataSize) PKSPROPERTY_STEPPING_LONG _pKsPropStepLong, + _In_ UINT32 _ui32DataSize + ); + + NTSTATUS GetChannelVolume + ( + _In_ UINT32 _uiChannel, + _Out_ LONG * _pVolume + ); + + NTSTATUS SetChannelVolume + ( + _In_ UINT32 _uiChannel, + _In_ LONG _Volume + ); + + //----------------------------------------------------------------------------- + // metering + //----------------------------------------------------------------------------- + NTSTATUS GetPeakMeterChannelCount + ( + _Out_ UINT32 * pulChannelCount + ); + + NTSTATUS GetPeakMeterSteppings + ( + _Out_writes_bytes_(_ui32DataSize) PKSPROPERTY_STEPPING_LONG _pKsPropStepLong, + _In_ UINT32 _ui32DataSize + ); + + NTSTATUS GetChannelPeakMeter + ( + _In_ UINT32 _uiChannel, + _Out_ LONG * _plPeakMeter + ); + + //----------------------------------------------------------------------------- + // mute + //----------------------------------------------------------------------------- + NTSTATUS GetMuteChannelCount + ( + _Out_ UINT32 * pulChannelCount + ); + + NTSTATUS GetMuteSteppings + ( + _Out_writes_bytes_(_ui32DataSize) PKSPROPERTY_STEPPING_LONG _pKsPropStepLong, + _In_ UINT32 _ui32DataSize + ); + + NTSTATUS GetChannelMute + ( + _In_ UINT32 _uiChannel, + _Out_ BOOL * _pbMute + ); + + NTSTATUS SetChannelMute + ( + _In_ UINT32 _uiChannel, + _In_ BOOL _bMute + ); + +private: +#pragma code_seg("PAGE") + //--------------------------------------------------------------------------- + // GetPinSupportedDeviceFormats + // + // Return supported formats for a given pin. + // + // Return value + // The number of KSDATAFORMAT_WAVEFORMATEXTENSIBLE items. + // + // Remarks + // Supported formats index array follows same order as filter's pin + // descriptor list. + // + _Post_satisfies_(return > 0) + ULONG GetPinSupportedDeviceFormats(_In_ ULONG PinId, _Outptr_opt_result_buffer_(return) KSDATAFORMAT_WAVEFORMATEXTENSIBLE **ppFormats) + { + PAGED_CODE(); + + ASSERT(m_DeviceFormatsAndModesCount > PinId); + ASSERT(m_DeviceFormatsAndModes[PinId].WaveFormats != NULL); + ASSERT(m_DeviceFormatsAndModes[PinId].WaveFormatsCount > 0); + + if (ppFormats != NULL) + { + *ppFormats = m_DeviceFormatsAndModes[PinId].WaveFormats; + } + + return m_DeviceFormatsAndModes[PinId].WaveFormatsCount; + } + + //--------------------------------------------------------------------------- + // GetAudioEngineSupportedDeviceFormats + // + // Return supported device formats for the audio engine node. + // + // Return value + // The number of KSDATAFORMAT_WAVEFORMATEXTENSIBLE items. + // + // Remarks + // Supported formats index array follows same order as filter's pin + // descriptor list. This routine assumes the engine formats are the + // last item in the filter's array of PIN_DEVICE_FORMATS_AND_MODES. + // + _Post_satisfies_(return > 0) + ULONG GetAudioEngineSupportedDeviceFormats(_Outptr_opt_result_buffer_(return) KSDATAFORMAT_WAVEFORMATEXTENSIBLE **ppFormats) + { + ULONG i; + + PAGED_CODE(); + + // By convention, the audio engine node's device formats are the last + // entry in the PIN_DEVICE_FORMATS_AND_MODES list. + + // Since this endpoint apparently supports offload, there must be at least a system, + // offload, and loopback pin, plus the entry for the device formats. + ASSERT(m_DeviceFormatsAndModesCount > 3); + + i = m_DeviceFormatsAndModesCount - 1; // Index of last list entry + + ASSERT(m_DeviceFormatsAndModes[i].PinType == NoPin); + ASSERT(m_DeviceFormatsAndModes[i].WaveFormats != NULL); + ASSERT(m_DeviceFormatsAndModes[i].WaveFormatsCount > 0); + + if (ppFormats != NULL) + { + *ppFormats = m_DeviceFormatsAndModes[i].WaveFormats; + } + + return m_DeviceFormatsAndModes[i].WaveFormatsCount; + } + + //--------------------------------------------------------------------------- + // GetPinSupportedDeviceModes + // + // Return mode information for a given pin. + // + // Return value + // The number of MODE_AND_DEFAULT_FORMAT items or 0 if none. + // + // Remarks + // Supported formats index array follows same order as filter's pin + // descriptor list. + // + _Success_(return != 0) + ULONG GetPinSupportedDeviceModes(_In_ ULONG PinId, _Outptr_opt_result_buffer_(return) _On_failure_(_Deref_post_null_) MODE_AND_DEFAULT_FORMAT **ppModes) + { + PMODE_AND_DEFAULT_FORMAT modes; + ULONG numModes; + + PAGED_CODE(); + + ASSERT(m_DeviceFormatsAndModesCount > PinId); + ASSERT((m_DeviceFormatsAndModes[PinId].ModeAndDefaultFormatCount == 0) == (m_DeviceFormatsAndModes[PinId].ModeAndDefaultFormat == NULL)); + + modes = m_DeviceFormatsAndModes[PinId].ModeAndDefaultFormat; + numModes = m_DeviceFormatsAndModes[PinId].ModeAndDefaultFormatCount; + +#ifdef SYSVAD_BTH_BYPASS + // Special handling for the SCO bypass endpoint, whose modes are determined at runtime + if (m_DeviceType == eBthHfpMicDevice) + { + ASSERT(m_BthHfpDevice != NULL); + if (m_BthHfpDevice->IsNRECSupported()) + { + modes = BthHfpMicPinSupportedDeviceModesNrec; + numModes = ARRAYSIZE(BthHfpMicPinSupportedDeviceModesNrec); + } + else + { + modes = BthHfpMicPinSupportedDeviceModesNoNrec; + numModes = ARRAYSIZE(BthHfpMicPinSupportedDeviceModesNoNrec); + } + } +#endif // SYSVAD_BTH_BYPASS + + if (ppModes != NULL) + { + if (numModes > 0) + { + *ppModes = modes; + } + else + { + // ensure that the returned pointer is NULL + // in the event of failure (SAL annotation above + // indicates that it must be NULL, and OACR sees a possibility + // that it might not be). + *ppModes = NULL; + } + } + + return numModes; + } +#pragma code_seg() + +protected: +#pragma code_seg("PAGE") + BOOL IsRenderDevice() + { + PAGED_CODE(); + return (m_DeviceType == eSpeakerDevice || + m_DeviceType == eSpeakerHpDevice || + m_DeviceType == eSpeakerHsDevice || + m_DeviceType == eHdmiRenderDevice || + m_DeviceType == eSpdifRenderDevice || + m_DeviceType == eBthHfpSpeakerDevice || + m_DeviceType == eHandsetSpeakerDevice) ? TRUE : FALSE; + } + + BOOL IsCellularDevice() + { + PAGED_CODE(); + return (m_DeviceType == eCellularDevice) ? TRUE : FALSE; + } + + BOOL IsOffloadSupported() + { + PAGED_CODE(); + return (m_DeviceFlags & ENDPOINT_OFFLOAD_SUPPORTED) ? TRUE : FALSE; + } + + BOOL IsSystemCapturePin(ULONG nPinId) + { + PINTYPE pinType = m_DeviceFormatsAndModes[nPinId].PinType; + PAGED_CODE(); + return (pinType == SystemCapturePin); + } + + BOOL IsCellularBiDiCapturePin(ULONG nPinId) + { + PAGED_CODE(); + return (m_DeviceFormatsAndModes[nPinId].PinType == TelephonyBidiPin); + } + + BOOL IsSystemRenderPin(ULONG nPinId) + { + PINTYPE pinType = m_DeviceFormatsAndModes[nPinId].PinType; + PAGED_CODE(); + return (pinType == SystemRenderPin); + } + + BOOL IsLoopbackPin(ULONG nPinId) + { + PAGED_CODE(); + return (m_DeviceFormatsAndModes[nPinId].PinType == RenderLoopbackPin); + } + + BOOL IsOffloadPin(ULONG nPinId) + { + PAGED_CODE(); + return (m_DeviceFormatsAndModes[nPinId].PinType == OffloadRenderPin); + } + + BOOL IsBridgePin(ULONG nPinId) + { + PAGED_CODE(); + return (m_DeviceFormatsAndModes[nPinId].PinType == BridgePin); + } + + // These three pins are the pins used by the audio engine for host, loopback, and offload. + ULONG GetSystemPinId() + { + PAGED_CODE(); + ASSERT(IsRenderDevice()); + ASSERT(!IsCellularDevice()); + return IsOffloadSupported() ? KSPIN_WAVE_RENDER_SINK_SYSTEM : KSPIN_WAVE_RENDER2_SINK_SYSTEM; + } + + + ULONG GetLoopbackPinId() + { + PAGED_CODE(); + ASSERT(IsRenderDevice()); + ASSERT(!IsCellularDevice()); + return IsOffloadSupported() ? KSPIN_WAVE_RENDER_SINK_LOOPBACK : KSPIN_WAVE_RENDER2_SINK_LOOPBACK; + } + + + ULONG GetOffloadPinId() + { + PAGED_CODE(); + ASSERT(IsRenderDevice()); + ASSERT(IsOffloadSupported()); + ASSERT(!IsCellularDevice()); + return KSPIN_WAVE_RENDER_SINK_OFFLOAD; + } +#pragma code_seg() + +#ifdef SYSVAD_BTH_BYPASS +public: +#pragma code_seg("PAGE") + BOOL IsBthHfpDevice() + { + PAGED_CODE(); + return (m_DeviceType == eBthHfpMicDevice || + m_DeviceType == eBthHfpSpeakerDevice) ? TRUE : FALSE; + } + + // Returns a weak ref to the Bluetooth HFP device. + PBTHHFPDEVICECOMMON GetBthHfpDevice() + { + PBTHHFPDEVICECOMMON bthHfpDevice = NULL; + + PAGED_CODE(); + + if (IsBthHfpDevice()) + { + if (m_BthHfpDevice != NULL) + { + bthHfpDevice = m_BthHfpDevice; + } + } + + return bthHfpDevice; + } +#pragma code_seg() +#endif // SYSVAD_BTH_BYPASS +}; +typedef CMiniportWaveRT *PCMiniportWaveRT; + +#endif // _SYSVAD_MINWAVERT_H_ + diff --git a/audio/sysvad/EndpointsCommon/minwavertstream.cpp b/audio/sysvad/EndpointsCommon/minwavertstream.cpp new file mode 100644 index 000000000..a343efa73 --- /dev/null +++ b/audio/sysvad/EndpointsCommon/minwavertstream.cpp @@ -0,0 +1,1553 @@ +#include +#include +#include +#include "simple.h" +#include "minwavert.h" +#include "minwavertstream.h" +#include "UnittestData.h" +#define MINWAVERTSTREAM_POOLTAG 'SRWM' + +#pragma warning (disable : 4127) + +//============================================================================= +// CMiniportWaveRTStream +//============================================================================= + +//============================================================================= +#pragma code_seg("PAGE") +CMiniportWaveRTStream::~CMiniportWaveRTStream +( + void +) +/*++ + +Routine Description: + + Destructor for wavertstream + +Arguments: + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + if (NULL != m_pMiniport) + { + if (m_bUnregisterStream) + { + m_pMiniport->StreamClosed(m_ulPin, this); + m_bUnregisterStream = FALSE; + } + + m_pMiniport->Release(); + m_pMiniport = NULL; + } + + if (m_pDpc) + { + ExFreePoolWithTag( m_pDpc, MINWAVERTSTREAM_POOLTAG ); + m_pDpc = NULL; + } + + if (m_pTimer) + { + ExFreePoolWithTag( m_pTimer, MINWAVERTSTREAM_POOLTAG ); + m_pTimer = NULL; + } + + if (m_pbMuted) + { + ExFreePoolWithTag( m_pbMuted, MINWAVERTSTREAM_POOLTAG ); + m_pbMuted = NULL; + } + + if (m_plVolumeLevel) + { + ExFreePoolWithTag( m_plVolumeLevel, MINWAVERTSTREAM_POOLTAG ); + m_plVolumeLevel = NULL; + } + + if (m_plPeakMeter) + { + ExFreePoolWithTag( m_plPeakMeter, MINWAVERTSTREAM_POOLTAG ); + m_plPeakMeter = NULL; + } + + if (m_pWfExt) + { + ExFreePoolWithTag( m_pWfExt, MINWAVERTSTREAM_POOLTAG ); + m_pWfExt = NULL; + } + if (m_pNotificationTimer) + { + KeCancelTimer(m_pNotificationTimer); + ExFreePoolWithTag(m_pNotificationTimer, MINWAVERTSTREAM_POOLTAG); + } + + // Since we just cancelled the notification timer, wait for all queued + // DPCs to complete before we free the notification DPC. + // + KeFlushQueuedDpcs(); + + if (m_pNotificationDpc) + { + ExFreePoolWithTag( m_pNotificationDpc, MINWAVERTSTREAM_POOLTAG ); + } + +#ifdef SYSVAD_BTH_BYPASS + ASSERT(m_ScoOpen == FALSE); +#endif // SYSVAD_BTH_BYPASS + + DPF_ENTER(("[CMiniportWaveRTStream::~CMiniportWaveRTStream]")); +} // ~CMiniportWaveRTStream + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CMiniportWaveRTStream::Init +( + _In_ PCMiniportWaveRT Miniport_, + _In_ PPORTWAVERTSTREAM PortStream_, + _In_ ULONG Pin_, + _In_ BOOLEAN Capture_, + _In_ PKSDATAFORMAT DataFormat_, + _In_ GUID SignalProcessingMode +) +/*++ + +Routine Description: + + Initializes the stream object. + +Arguments: + + Miniport_ - + + Pin_ - + + Capture_ - + + DataFormat - + + SignalProcessingMode - The driver uses the signalProcessingMode to configure + driver and/or hardware specific signal processing to be applied to this new + stream. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + PWAVEFORMATEX pWfEx = NULL; + NTSTATUS ntStatus = STATUS_SUCCESS; + + m_pMiniport = NULL; + m_ulPin = 0; + m_bUnregisterStream = FALSE; + m_bCapture = FALSE; + m_ulDmaBufferSize = 0; + m_pDmaBuffer = NULL; + m_ulNotificationsPerBuffer = 0; + m_KsState = KSSTATE_STOP; + m_pTimer = NULL; + m_pDpc = NULL; + m_llPacketCounter = 0; + m_ullPlayPosition = 0; + m_ullWritePosition = 0; + m_ullDmaTimeStamp = 0; + m_hnsElapsedTimeCarryForward = 0; + m_ulDmaMovementRate = 0; + m_bLfxEnabled = FALSE; + m_pbMuted = NULL; + m_plVolumeLevel = NULL; + m_plPeakMeter = NULL; + m_pWfExt = NULL; + m_ullLinearPosition = 0; + m_ulContentId = 0; + m_ulCurrentWritePosition = 0; + m_ulLastOsReadPacket = ULONG_MAX; + m_ulLastOsWritePacket = ULONG_MAX; + m_llEoSPosition = (-1); + m_IsCurrentWritePositionUpdated = 0; + m_SignalProcessingMode = SignalProcessingMode; + +#ifdef SYSVAD_BTH_BYPASS + m_ScoOpen = FALSE; +#endif // SYSVAD_BTH_BYPASS + + m_pPortStream = PortStream_; + InitializeListHead(&m_NotificationList); + m_ulNotificationIntervalMs = 0; + + m_pNotificationDpc = (PRKDPC)ExAllocatePoolWithTag( + NonPagedPoolNx, + sizeof(KDPC), + MINWAVERTSTREAM_POOLTAG); + if (!m_pNotificationDpc) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + m_pNotificationTimer = (PKTIMER)ExAllocatePoolWithTag( + NonPagedPoolNx, + sizeof(KTIMER), + MINWAVERTSTREAM_POOLTAG); + if (!m_pNotificationTimer) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + KeInitializeDpc(m_pNotificationDpc, TimerNotifyRT, this); + KeInitializeTimerEx(m_pNotificationTimer, NotificationTimer); + + pWfEx = GetWaveFormatEx(DataFormat_); + if (NULL == pWfEx) + { + return STATUS_UNSUCCESSFUL; + } + + m_pMiniport = reinterpret_cast(Miniport_); + if (m_pMiniport == NULL) + { + return STATUS_INVALID_PARAMETER; + } + m_pMiniport->AddRef(); + if (!NT_SUCCESS(ntStatus)) + { + return ntStatus; + } + m_ulPin = Pin_; + m_bCapture = Capture_; + m_ulDmaMovementRate = pWfEx->nAvgBytesPerSec; + + m_pDpc = (PRKDPC)ExAllocatePoolWithTag(NonPagedPoolNx, sizeof(KDPC), MINWAVERTSTREAM_POOLTAG); + if (!m_pDpc) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + m_pWfExt = (PWAVEFORMATEXTENSIBLE)ExAllocatePoolWithTag(NonPagedPoolNx, sizeof(WAVEFORMATEX) + pWfEx->cbSize, MINWAVERTSTREAM_POOLTAG); + if (m_pWfExt == NULL) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + RtlCopyMemory(m_pWfExt, pWfEx, sizeof(WAVEFORMATEX) + pWfEx->cbSize); + + m_pbMuted = (PBOOL)ExAllocatePoolWithTag(NonPagedPoolNx, m_pWfExt->Format.nChannels * sizeof(BOOL), MINWAVERTSTREAM_POOLTAG); + if (m_pbMuted == NULL) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + RtlZeroMemory(m_pbMuted, m_pWfExt->Format.nChannels * sizeof(BOOL)); + + m_plVolumeLevel = (PLONG)ExAllocatePoolWithTag(NonPagedPoolNx, m_pWfExt->Format.nChannels * sizeof(LONG), MINWAVERTSTREAM_POOLTAG); + if (m_plVolumeLevel == NULL) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + RtlZeroMemory(m_plVolumeLevel, m_pWfExt->Format.nChannels * sizeof(LONG)); + + m_plPeakMeter = (PLONG)ExAllocatePoolWithTag(NonPagedPoolNx, m_pWfExt->Format.nChannels * sizeof(LONG), MINWAVERTSTREAM_POOLTAG); + if (m_plPeakMeter == NULL) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + RtlZeroMemory(m_plPeakMeter, m_pWfExt->Format.nChannels * sizeof(LONG)); + + if (m_bCapture) + { + DWORD toneFrequency = 0; + + if (m_pMiniport->IsLoopbackPin(Pin_)) + { + // + // Loopbacks pins use a different frequency for test validation. + // + toneFrequency = 3000; // 3 kHz + } + else + { + // + // Init sine wave generator. To exercise the SignalProcessingMode parameter + // this sample driver selects the frequency based on the parameter. + // + toneFrequency = IsEqualGUID(SignalProcessingMode, AUDIO_SIGNALPROCESSINGMODE_RAW) ? 1000 : 2000; + } + + ntStatus = m_ToneGenerator.Init(toneFrequency, m_pWfExt); + if (!NT_SUCCESS(ntStatus)) + { + return ntStatus; + } + } + else if (!g_DoNotCreateDataFiles) + { + // + // Create an output file for the render data. + // + DPF(D_TERSE, ("SaveData %p", &m_SaveData)); + ntStatus = m_SaveData.SetDataFormat(DataFormat_); + if (NT_SUCCESS(ntStatus)) + { + ntStatus = m_SaveData.Initialize(m_pMiniport->IsOffloadPin(Pin_)); + } + + if (!NT_SUCCESS(ntStatus)) + { + return ntStatus; + } + } + + // + // Register this stream. + // + ntStatus = m_pMiniport->StreamCreated(m_ulPin, this); + if (NT_SUCCESS(ntStatus)) + { + m_bUnregisterStream = TRUE; + } + + return ntStatus; +} // Init + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP_(NTSTATUS) +CMiniportWaveRTStream::NonDelegatingQueryInterface +( + _In_ REFIID Interface, + _COM_Outptr_ PVOID * Object +) +/*++ + +Routine Description: + + QueryInterface + +Arguments: + + Interface - GUID + + Object - interface pointer to be returned + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(Object); + + if (IsEqualGUIDAligned(Interface, IID_IUnknown)) + { + *Object = PVOID(PUNKNOWN(PMINIPORTWAVERTSTREAM(this))); + } + else if (IsEqualGUIDAligned(Interface, IID_IMiniportWaveRTStream)) + { + *Object = PVOID(PMINIPORTWAVERTSTREAM(this)); + } + else if (IsEqualGUIDAligned(Interface, IID_IMiniportWaveRTStreamNotification)) + { + *Object = PVOID(PMINIPORTWAVERTSTREAMNOTIFICATION(this)); + } + else if (IsEqualGUIDAligned(Interface, IID_IMiniportWaveRTInputStream) && (this->m_bCapture)) + { + // This interface is supported only on capture streams + *Object = PVOID(PMINIPORTWAVERTINPUTSTREAM(this)); + } + else if (IsEqualGUIDAligned(Interface, IID_IMiniportWaveRTOutputStream) && (!this->m_bCapture)) + { + // This interface is supported only on render streams + *Object = PVOID(PMINIPORTWAVERTOUTPUTSTREAM(this)); + } + else if (IsEqualGUIDAligned(Interface, IID_IMiniportStreamAudioEngineNode)) + { + *Object = (PVOID)(IMiniportStreamAudioEngineNode*)this; + } + else if (IsEqualGUIDAligned(Interface, IID_IMiniportStreamAudioEngineNode2)) + { + *Object = (PVOID)(IMiniportStreamAudioEngineNode2*)this; + } + else if (IsEqualGUIDAligned(Interface, IID_IDrmAudioStream)) + { + *Object = (PVOID)(IDrmAudioStream*)this; + } + else + { + *Object = NULL; + } + + if (*Object) + { + PUNKNOWN(*Object)->AddRef(); + return STATUS_SUCCESS; + } + + return STATUS_INVALID_PARAMETER; +} // NonDelegatingQueryInterface + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS CMiniportWaveRTStream::AllocateBufferWithNotification +( + _In_ ULONG NotificationCount_, + _In_ ULONG RequestedSize_, + _Out_ PMDL *AudioBufferMdl_, + _Out_ ULONG *ActualSize_, + _Out_ ULONG *OffsetFromFirstPage_, + _Out_ MEMORY_CACHING_TYPE *CacheType_ +) +{ + PAGED_CODE(); + + ULONG ulBufferDurationMs = 0; + + if ( (0 == RequestedSize_) || (RequestedSize_ < m_pWfExt->Format.nBlockAlign) ) + { + return STATUS_UNSUCCESSFUL; + } + + if ((NotificationCount_ == 0) || (RequestedSize_ % NotificationCount_ != 0)) + { + return STATUS_INVALID_PARAMETER; + } + + RequestedSize_ -= RequestedSize_ % (m_pWfExt->Format.nBlockAlign); + + if (!m_bCapture && !g_DoNotCreateDataFiles) + { + NTSTATUS ntStatus; + + ntStatus = m_SaveData.SetMaxWriteSize(RequestedSize_); + if (!NT_SUCCESS(ntStatus)) + { + return ntStatus; + } + } + + PHYSICAL_ADDRESS highAddress; + highAddress.HighPart = 0; + highAddress.LowPart = MAXULONG; + + PMDL pBufferMdl = m_pPortStream->AllocatePagesForMdl (highAddress, RequestedSize_); + + if (NULL == pBufferMdl) + { + return STATUS_UNSUCCESSFUL; + } + + // From MSDN: + // "Since the Windows audio stack does not support a mechanism to express memory access + // alignment requirements for buffers, audio drivers must select a caching type for mapped + // memory buffers that does not impose platform-specific alignment requirements. In other + // words, the caching type used by the audio driver for mapped memory buffers, must not make + // assumptions about the memory alignment requirements for any specific platform. + // + // This method maps the physical memory pages in the MDL into kernel-mode virtual memory. + // Typically, the miniport driver calls this method if it requires software access to the + // scatter-gather list for an audio buffer. In this case, the storage for the scatter-gather + // list must have been allocated by the IPortWaveRTStream::AllocatePagesForMdl or + // IPortWaveRTStream::AllocateContiguousPagesForMdl method. + // + // A WaveRT miniport driver should not require software access to the audio buffer itself." + // + m_pDmaBuffer = (BYTE*)m_pPortStream->MapAllocatedPages(pBufferMdl, MmCached); + m_ulNotificationsPerBuffer = NotificationCount_; + m_ulDmaBufferSize = RequestedSize_; + ulBufferDurationMs = (RequestedSize_ * 1000) / m_ulDmaMovementRate; + m_ulNotificationIntervalMs = ulBufferDurationMs / NotificationCount_; + + *AudioBufferMdl_ = pBufferMdl; + *ActualSize_ = RequestedSize_; + *OffsetFromFirstPage_ = 0; + *CacheType_ = MmCached; + + return STATUS_SUCCESS; +} + +//============================================================================= +#pragma code_seg("PAGE") +VOID CMiniportWaveRTStream::FreeBufferWithNotification +( + _In_ PMDL Mdl_, + _In_ ULONG Size_ +) +{ + UNREFERENCED_PARAMETER(Size_); + + PAGED_CODE(); + + if (Mdl_ != NULL) + { + if (m_pDmaBuffer != NULL) + { + m_pPortStream->UnmapAllocatedPages(m_pDmaBuffer, Mdl_); + m_pDmaBuffer = NULL; + } + + m_pPortStream->FreePagesFromMdl(Mdl_); + } + + m_ulDmaBufferSize = 0; + m_ulNotificationsPerBuffer = 0; + + return; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS CMiniportWaveRTStream::RegisterNotificationEvent +( + _In_ PKEVENT NotificationEvent_ +) +{ + UNREFERENCED_PARAMETER(NotificationEvent_); + + PAGED_CODE(); + + NotificationListEntry *nleNew = (NotificationListEntry*)ExAllocatePoolWithTag( + NonPagedPoolNx, + sizeof(NotificationListEntry), + MINWAVERTSTREAM_POOLTAG); + if (NULL == nleNew) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + nleNew->NotificationEvent = NotificationEvent_; + + if (!IsListEmpty(&m_NotificationList)) + { + PLIST_ENTRY leCurrent = m_NotificationList.Flink; + while (leCurrent != &m_NotificationList) + { + NotificationListEntry* nleCurrent = CONTAINING_RECORD( leCurrent, NotificationListEntry, ListEntry); + if (nleCurrent->NotificationEvent == NotificationEvent_) + { + RemoveEntryList( leCurrent ); + ExFreePoolWithTag( nleNew, MINWAVERTSTREAM_POOLTAG ); + return STATUS_UNSUCCESSFUL; + } + + leCurrent = leCurrent->Flink; + } + } + + InsertTailList(&m_NotificationList, &(nleNew->ListEntry)); + + return STATUS_SUCCESS; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS CMiniportWaveRTStream::UnregisterNotificationEvent +( + _In_ PKEVENT NotificationEvent_ +) +{ + UNREFERENCED_PARAMETER(NotificationEvent_); + + PAGED_CODE(); + + if (!IsListEmpty(&m_NotificationList)) + { + PLIST_ENTRY leCurrent = m_NotificationList.Flink; + while (leCurrent != &m_NotificationList) + { + NotificationListEntry* nleCurrent = CONTAINING_RECORD( leCurrent, NotificationListEntry, ListEntry); + if (nleCurrent->NotificationEvent == NotificationEvent_) + { + RemoveEntryList( leCurrent ); + ExFreePoolWithTag( nleCurrent, MINWAVERTSTREAM_POOLTAG ); + return STATUS_SUCCESS; + } + + leCurrent = leCurrent->Flink; + } + } + + return STATUS_NOT_FOUND; +} + + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS CMiniportWaveRTStream::GetClockRegister +( + _Out_ PKSRTAUDIO_HWREGISTER Register_ +) +{ + UNREFERENCED_PARAMETER(Register_); + + PAGED_CODE(); + + return STATUS_NOT_IMPLEMENTED; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS CMiniportWaveRTStream::GetPositionRegister +( + _Out_ PKSRTAUDIO_HWREGISTER Register_ +) +{ + UNREFERENCED_PARAMETER(Register_); + + PAGED_CODE(); + + return STATUS_NOT_IMPLEMENTED; +} + +//============================================================================= +#pragma code_seg("PAGE") +VOID CMiniportWaveRTStream::GetHWLatency +( + _Out_ PKSRTAUDIO_HWLATENCY Latency_ +) +{ + PAGED_CODE(); + + ASSERT(Latency_); + + Latency_->ChipsetDelay = 0; + Latency_->CodecDelay = 0; + Latency_->FifoSize = 0; +} + +//============================================================================= +#pragma code_seg("PAGE") +VOID CMiniportWaveRTStream::FreeAudioBuffer +( +_In_opt_ PMDL Mdl_, +_In_ ULONG Size_ +) +{ + UNREFERENCED_PARAMETER(Size_); + + PAGED_CODE(); + + if (Mdl_ != NULL) + { + if (m_pDmaBuffer != NULL) + { + m_pPortStream->UnmapAllocatedPages(m_pDmaBuffer, Mdl_); + m_pDmaBuffer = NULL; + } + + m_pPortStream->FreePagesFromMdl(Mdl_); + } + + m_ulDmaBufferSize = 0; + m_ulNotificationsPerBuffer = 0; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS CMiniportWaveRTStream::AllocateAudioBuffer +( +_In_ ULONG RequestedSize_, +_Out_ PMDL *AudioBufferMdl_, +_Out_ ULONG *ActualSize_, +_Out_ ULONG *OffsetFromFirstPage_, +_Out_ MEMORY_CACHING_TYPE *CacheType_ +) +{ + PAGED_CODE(); + + if ((0 == RequestedSize_) || (RequestedSize_ < m_pWfExt->Format.nBlockAlign)) + { + return STATUS_UNSUCCESSFUL; + } + + RequestedSize_ -= RequestedSize_ % (m_pWfExt->Format.nBlockAlign); + + PHYSICAL_ADDRESS highAddress; + highAddress.HighPart = 0; + highAddress.LowPart = MAXULONG; + + PMDL pBufferMdl = m_pPortStream->AllocatePagesForMdl(highAddress, RequestedSize_); + + if (NULL == pBufferMdl) + { + return STATUS_UNSUCCESSFUL; + } + + // From MSDN: + // "Since the Windows audio stack does not support a mechanism to express memory access + // alignment requirements for buffers, audio drivers must select a caching type for mapped + // memory buffers that does not impose platform-specific alignment requirements. In other + // words, the caching type used by the audio driver for mapped memory buffers, must not make + // assumptions about the memory alignment requirements for any specific platform. + // + // This method maps the physical memory pages in the MDL into kernel-mode virtual memory. + // Typically, the miniport driver calls this method if it requires software access to the + // scatter-gather list for an audio buffer. In this case, the storage for the scatter-gather + // list must have been allocated by the IPortWaveRTStream::AllocatePagesForMdl or + // IPortWaveRTStream::AllocateContiguousPagesForMdl method. + // + // A WaveRT miniport driver should not require software access to the audio buffer itself." + // + m_pDmaBuffer = (BYTE*)m_pPortStream->MapAllocatedPages(pBufferMdl, MmCached); + + m_ulDmaBufferSize = RequestedSize_; + m_ulNotificationsPerBuffer = 0; + + *AudioBufferMdl_ = pBufferMdl; + *ActualSize_ = RequestedSize_; + *OffsetFromFirstPage_ = 0; + *CacheType_ = MmCached; + + return STATUS_SUCCESS; +} + +//============================================================================= +#pragma code_seg() +NTSTATUS CMiniportWaveRTStream::GetPosition +( + _Out_ KSAUDIO_POSITION *Position_ +) +{ + NTSTATUS ntStatus; + +#ifdef SYSVAD_BTH_BYPASS + if (m_ScoOpen) + { + ntStatus = GetScoStreamNtStatus(); + IF_FAILED_JUMP(ntStatus, Done); + } +#endif // SYSVAD_BTH_BYPASS + + // Return failure if this is the keyword detector pin + if (m_pMiniport->m_DeviceFormatsAndModes[m_ulPin].PinType == PINTYPE::KeywordCapturePin) + { + return STATUS_NOT_SUPPORTED; + } + + if (m_KsState == KSSTATE_RUN) + { + // + // Get the current time and update position. + // + LARGE_INTEGER ilQPC = KeQueryPerformanceCounter(NULL); + UpdatePosition(ilQPC); + } + + Position_->PlayOffset = m_ullPlayPosition; + Position_->WriteOffset = m_ullWritePosition; + + ntStatus = STATUS_SUCCESS; + +#ifdef SYSVAD_BTH_BYPASS +Done: +#endif // SYSVAD_BTH_BYPASS + return ntStatus; +} + +//============================================================================= +// CMiniportWaveRTStream::GetReadPacket +// +// Returns information about the next packet for the OS to read. +// +// Return value +// +// Returns STATUS_OPERATION_IN_PROGRESS if no new packets are available and +// the next packet is in progress. +// +// IRQL - PASSIVE_LEVEL +// +// Remarks +// Although called at passive level, this routine is non-paged code because +// it is called in the streaming path where page faults should be avoided. +// +// ISSUE-2014/10/4 Will this work correctly across pause/play? +#pragma code_seg() +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS CMiniportWaveRTStream::GetReadPacket +( + _Out_ ULONG *PacketNumber, + _Out_ DWORD *Flags, + _Out_ ULONG64 *PerformanceCounterValue, + _Out_ BOOL *MoreData +) +{ + NTSTATUS ntStatus; + ULONG availablePacketNumber; + ULONG droppedPackets; + + // The call must be from event driven mode + if(m_ulNotificationsPerBuffer == 0) + { + return STATUS_NOT_SUPPORTED; + } + + *Flags = 0; + + if (m_KsState < KSSTATE_PAUSE) + { + return STATUS_INVALID_DEVICE_STATE; + } + + // If this is the keyword detector pin, then stream from the keyword FIFO + if (m_pMiniport->m_DeviceFormatsAndModes[m_ulPin].PinType == PINTYPE::KeywordCapturePin) + { + // FUTURE-2014/11/18 Drive this with packet counter + ntStatus = m_pMiniport->m_KeywordDetector.GetReadPacket(m_ulNotificationsPerBuffer, m_ulDmaBufferSize, m_pDmaBuffer, PacketNumber, PerformanceCounterValue, MoreData); + if (NT_SUCCESS(ntStatus)) + { + m_ulLastOsReadPacket = *PacketNumber; + } + return ntStatus; + } + + if (m_KsState == KSSTATE_RUN) + { + // + // Get the current time and update simulated position. + // + LARGE_INTEGER ilQPC = KeQueryPerformanceCounter(NULL); + + UpdatePosition(ilQPC); + } + + // The 0-based number of the last completed packet + // FUTURE-2014/10/27 Update to allow different numbers of packets per WaveRT buffer + availablePacketNumber = LODWORD(m_llPacketCounter - 1); // Note this might be ULONG_MAX if called during the first packet + + // If no new packets are available... + if (availablePacketNumber == m_ulLastOsReadPacket) + { + return STATUS_DEVICE_NOT_READY; + } + + // If more than one packet has transferred since the last packet read by + // the OS, then those were dropped. That is, a glitch occurred. + droppedPackets = availablePacketNumber - m_ulLastOsReadPacket - 1; + if (droppedPackets > 0) + { + // Trace a glitch + } + + // Return next packet number to be read + *PacketNumber = availablePacketNumber; + + // Compute and return timestamp corresponding to start of the available packet. In a real hardware + // driver, the timestamp would be computed in a driver and hardware specific manner. In this sample + // driver, it is extrapolated from the sample driver's internal simulated position corrleation + // [m_ullLinearPosition @ m_ullDmaTimeStamp] and the sample's internal 64-bit packet counter, subtracting + // 1 from the packet counter to compute the time at the start of that last completed packet. + ULONGLONG linearPositionOfAvailablePacket = (m_llPacketCounter - 1) * (m_ulDmaBufferSize / m_ulNotificationsPerBuffer); + ULONGLONG deltaLinearPosition = m_ullLinearPosition - linearPositionOfAvailablePacket; + ULONGLONG deltaTimeInHns = deltaLinearPosition * 10000000 / m_ulDmaMovementRate; + ULONGLONG timeOfAvailablePacketInHns = m_ullDmaTimeStamp - deltaTimeInHns; + ULONGLONG timeOfAvailablePacketInQpc = timeOfAvailablePacketInHns * m_ullPerformanceCounterFrequency.QuadPart / 10000000; + + *PerformanceCounterValue = timeOfAvailablePacketInQpc; + + // No flags are defined yet + *Flags = 0; + + // This sample does not internally buffer data so there is never more data + // than revealed by the results from this routine. + *MoreData = FALSE; + + // Update the last packet read by the OS + m_ulLastOsReadPacket = availablePacketNumber; + +#if 0 + // For test, embed packet number and timestamp into first two LONGLONGs of the packet + LONG packetIndex = availablePacketNumber % m_ulNotificationsPerBuffer; + SIZE_T packetSize = m_ulDmaBufferSize / m_ulNotificationsPerBuffer; + BYTE *packetDataAsBytes = m_pDmaBuffer + (packetIndex * packetSize); + LONGLONG *packetDataAsLonglongs = (LONGLONG*)packetDataAsBytes; + for (int i = 0; i < packetSize / sizeof(LONGLONG); i++) + { + packetDataAsLonglongs[i] = i; + } + packetDataAsLonglongs[0] = availablePacketNumber; + packetDataAsLonglongs[1] = timeOfAvailablePacketInQpc; +#endif + + return STATUS_SUCCESS; +} + +#pragma code_seg() +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS CMiniportWaveRTStream::SetWritePacket +( +_In_ ULONG PacketNumber, +_In_ DWORD Flags, +_In_ ULONG EosPacketLength +) +{ + NTSTATUS ntStatus; + + // The call must be from event driven mode + if (m_ulNotificationsPerBuffer == 0) + { + return STATUS_NOT_SUPPORTED; + } + + ULONG oldLastOsWritePacket = m_ulLastOsWritePacket; + + // This function should not be called once EoS has been set. + if (m_llEoSPosition >= 0) + { + return STATUS_INVALID_DEVICE_STATE; + } + + // Update linear positions + if (m_KsState == KSSTATE_RUN) + { + // Get the current time and update simulated position. + LARGE_INTEGER ilQPC = KeQueryPerformanceCounter(NULL); + UpdatePosition(ilQPC); + } + + // 1-based count of completed packets, 0-based packet number of current packet + LONGLONG currentPacket = m_llPacketCounter; + + // If not running, the current packet hasn't actually started transfering so OS should be writing + // to the current packet. If running, then the current packing is already transfering to hardware + // so the OS should write the packet after the current packet. + ULONG expectedPacket = LODWORD(currentPacket); + if (m_KsState == KSSTATE_RUN) + { + expectedPacket++; + } + + // Check if OS PacketNumber is behind or too far ahead of current packet + LONG deltaFromExpectedPacket = PacketNumber - expectedPacket; // Modulo arithemetic + if (deltaFromExpectedPacket < 0) + { + return STATUS_DATA_LATE_ERROR; + } + else if (deltaFromExpectedPacket > 0) + { + return STATUS_DATA_OVERRUN; + } + + ULONG packetSize = (m_ulDmaBufferSize / m_ulNotificationsPerBuffer); + ULONG packetIndex = PacketNumber % m_ulNotificationsPerBuffer; + ULONG ulCurrentWritePosition = packetIndex * packetSize; + + // Check if EOS flag was passed + if (Flags & KSSTREAM_HEADER_OPTIONSF_ENDOFSTREAM) + { + if (EosPacketLength > packetSize) + { + return STATUS_INVALID_PARAMETER; + } + else { + // EOS position will be after the total completed packets, plus the packet in progress, + // plus this EOS packet length + m_ulLastOsWritePacket = PacketNumber; + m_llEoSPosition = ((m_llPacketCounter + 1) * packetSize) + EosPacketLength; + ntStatus = SetStreamCurrentWritePositionForLastBuffer(ulCurrentWritePosition); + } + } + else + { + m_ulLastOsWritePacket = PacketNumber; + + // This function sets the current write position to the specified byte in the DMA buffer. + // Will check if the write position is smaller than the DMA buffer size. + // Will not return an error when the passed in parameter is 0. + // Will also check if this function was called with the same write position(in event mode only) + // Underruning will also be checked via timer mechanism + ntStatus = SetCurrentWritePositionInternal(ulCurrentWritePosition); + } + + if (!NT_SUCCESS(ntStatus)) + { + m_llEoSPosition = (-1); + m_ulLastOsWritePacket = oldLastOsWritePacket; + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg() +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS CMiniportWaveRTStream::GetOutputStreamPresentationPosition +( + _Out_ KSAUDIO_PRESENTATION_POSITION *pPresentationPosition +) +{ + ASSERT (pPresentationPosition); + + // The call must be from event driven mode + if(m_ulNotificationsPerBuffer == 0) + { + return STATUS_NOT_SUPPORTED; + } + + return GetPresentationPosition(pPresentationPosition); +} + +//============================================================================= +#pragma code_seg() +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS CMiniportWaveRTStream::GetPacketCount +( + _Out_ ULONG *pPacketCount +) +{ + ASSERT(pPacketCount); + + // The call must be from event driven mode + if(m_ulNotificationsPerBuffer == 0) + { + return STATUS_NOT_SUPPORTED; + } + + if (m_KsState == KSSTATE_RUN) + { + // Get the current time and update simulated position. + LARGE_INTEGER ilQPC = KeQueryPerformanceCounter(NULL); + UpdatePosition(ilQPC); + } + + *pPacketCount = LODWORD(m_llPacketCounter); + + return STATUS_SUCCESS; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS CMiniportWaveRTStream::SetState +( + _In_ KSSTATE State_ +) +{ + PAGED_CODE(); + + NTSTATUS ntStatus = STATUS_SUCCESS; + PADAPTERCOMMON pAdapterComm = m_pMiniport->GetAdapterCommObj(); + + // Spew an event for a pin state change request from portcls + //Event type: eMINIPORT_PIN_STATE + //Parameter 1: Current linear buffer position + //Parameter 2: Current WaveRtBufferWritePosition + //Parameter 3: Pin State 0->KS_STOP, 1->KS_ACQUIRE, 2->KS_PAUSE, 3->KS_RUN + //Parameter 4:0 + pAdapterComm->WriteEtwEvent(eMINIPORT_PIN_STATE, + 100, // replace with the correct "Current linear buffer position" + m_ulCurrentWritePosition, // replace with the previous WaveRtBufferWritePosition that the drive received + State_, // repalce with the correct "Data length completed" + 0); // always zero + + switch (State_) + { + case KSSTATE_STOP: + // Reset DMA + m_llPacketCounter = 0; + m_ullPlayPosition = 0; + m_ullWritePosition = 0; + m_ullLinearPosition = 0; + + // Reset OS read/write positions + m_ulLastOsReadPacket = ULONG_MAX; + m_ulCurrentWritePosition = 0; + m_ulLastOsWritePacket = ULONG_MAX; + m_llEoSPosition = (-1); + + // Wait until all work items are completed. + if (!m_bCapture && !g_DoNotCreateDataFiles) + { + m_SaveData.WaitAllWorkItems(); + } + +#ifdef SYSVAD_BTH_BYPASS + if (m_ScoOpen) + { + PBTHHFPDEVICECOMMON bthHfpDevice; + + ASSERT(m_pMiniport->IsBthHfpDevice()); + bthHfpDevice = m_pMiniport->GetBthHfpDevice(); // weak ref. + ASSERT(bthHfpDevice != NULL); + + // + // Close the SCO connection. + // + ntStatus = bthHfpDevice->StreamClose(); + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_ERROR, ("SetState: KSSTATE_STOP, StreamClose failed, 0x%x", ntStatus)); + } + + m_ScoOpen = FALSE; + } +#endif // SYSVAD_BTH_BYPASS + break; + + case KSSTATE_ACQUIRE: +#ifdef SYSVAD_BTH_BYPASS + if (m_pMiniport->IsBthHfpDevice()) + { + if (m_ScoOpen == FALSE) + { + PBTHHFPDEVICECOMMON bthHfpDevice; + + bthHfpDevice = m_pMiniport->GetBthHfpDevice(); // weak ref. + ASSERT(bthHfpDevice != NULL); + + // + // Open the SCO connection. + // + ntStatus = bthHfpDevice->StreamOpen(); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("SetState: KSSTATE_ACQUIRE, StreamOpen failed, 0x%x", ntStatus)), + Done); + + m_ScoOpen = TRUE; + } + } +#endif // SYSVAD_BTH_BYPASS + break; + + case KSSTATE_PAUSE: + ULONGLONG ullPositionTemp; + + if (m_KsState > KSSTATE_PAUSE) + { + // + // Run -> Pause + // + if (m_pMiniport->m_DeviceFormatsAndModes[m_ulPin].PinType == PINTYPE::KeywordCapturePin) + { + m_pMiniport->m_KeywordDetector.Stop(); + } + + // Pause DMA + if (m_ulNotificationIntervalMs > 0) + { + KeCancelTimer(m_pNotificationTimer); + ExSetTimerResolution(0, FALSE); + KeFlushQueuedDpcs(); + } + } + + // This call updates the linear buffer position. + GetLinearBufferPosition(&ullPositionTemp, NULL); + break; + + case KSSTATE_RUN: + // Start DMA + LARGE_INTEGER ullPerfCounterTemp; + if (m_pMiniport->m_DeviceFormatsAndModes[m_ulPin].PinType == PINTYPE::KeywordCapturePin) + { + m_pMiniport->m_KeywordDetector.Run(); + } + ullPerfCounterTemp = KeQueryPerformanceCounter(&m_ullPerformanceCounterFrequency); + m_ullDmaTimeStamp = KSCONVERT_PERFORMANCE_TIME(m_ullPerformanceCounterFrequency.QuadPart, ullPerfCounterTemp); + m_hnsElapsedTimeCarryForward = 0; + + if (m_ulNotificationIntervalMs > 0) + { + LARGE_INTEGER delay; + + delay.QuadPart = (-2) * (((LONGLONG)m_ulNotificationIntervalMs) * HNSTIME_PER_MILLISECOND); + + // ISSUE-2014/10/20 Set resolution based on packet size + ExSetTimerResolution(HNSTIME_PER_MILLISECOND, TRUE); + + KeSetTimerEx + ( + m_pNotificationTimer, + delay, + m_ulNotificationIntervalMs, + m_pNotificationDpc + ); + } + + break; + } + + m_KsState = State_; + +#ifdef SYSVAD_BTH_BYPASS +Done: +#endif // SYSVAD_BTH_BYPASS + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS CMiniportWaveRTStream::SetFormat +( + _In_ KSDATAFORMAT *DataFormat_ +) +{ + UNREFERENCED_PARAMETER(DataFormat_); + + PAGED_CODE(); + + //if (!m_fCapture && !g_DoNotCreateDataFiles) + //{ + // ntStatus = m_SaveData.SetDataFormat(Format); + //} + + return STATUS_NOT_SUPPORTED; +} + +#pragma code_seg() + +//============================================================================= +#pragma code_seg() +VOID CMiniportWaveRTStream::UpdatePosition +( + _In_ LARGE_INTEGER ilQPC +) +{ + // Convert ticks to 100ns units. + LONGLONG hnsCurrentTime = KSCONVERT_PERFORMANCE_TIME(m_ullPerformanceCounterFrequency.QuadPart, ilQPC); + + // Calculate the time elapsed since the last call to GetPosition() or since the + // DMA engine started. Note that the division by 10000 to convert to milliseconds + // may cause us to lose some of the time, so we will carry the remainder forward + // to the next GetPosition() call. + // + ULONG TimeElapsedInMS = (ULONG)(hnsCurrentTime - m_ullDmaTimeStamp + m_hnsElapsedTimeCarryForward)/10000; + + // Carry forward the remainder of this division so we don't fall behind with our position too much. + // + m_hnsElapsedTimeCarryForward = (hnsCurrentTime - m_ullDmaTimeStamp + m_hnsElapsedTimeCarryForward) % 10000; + + // Calculate how many bytes in the DMA buffer would have been processed in the elapsed + // time. Note that the division by 1000 to convert to milliseconds may cause us to + // lose some bytes, so we will carry the remainder forward to the next GetPosition() call. + // + // need to divide by 1000 because m_ulDmaMovementRate is average bytes per sec. + ULONG ByteDisplacement = (m_ulDmaMovementRate * TimeElapsedInMS) / 1000; + + if (m_bCapture) + { + // Write sine wave to buffer. + WriteBytes(ByteDisplacement); + } + else if (!g_DoNotCreateDataFiles) + { + // Read from buffer and write to a file. + ReadBytes(ByteDisplacement); + + // If the last packet was rendered(read in the sample driver's case), send out an etw event. + if ( (m_llEoSPosition >= 0) + && ((m_llEoSPosition - m_ullLinearPosition) < ByteDisplacement)) + { + PADAPTERCOMMON pAdapterComm = m_pMiniport->GetAdapterCommObj(); + pAdapterComm->WriteEtwEvent(eMINIPORT_LAST_BUFFER_RENDERED, + m_ullLinearPosition + ByteDisplacement, // Current linear buffer position + m_ulCurrentWritePosition, // The very last WaveRtBufferWritePosition that the driver received + 0, + 0); + } + } + + // Increment the DMA position by the number of bytes displaced since the last + // call to UpdatePosition() and ensure we properly wrap at buffer length. + // + m_ullPlayPosition = m_ullWritePosition = + (m_ullWritePosition + ByteDisplacement) % m_ulDmaBufferSize; + + // m_ullDmaTimeStamp is updated in both GetPostion and GetLinearPosition calls + // so m_ullLinearPosition needs to be updated accordingly here + // + m_ullLinearPosition += ByteDisplacement; + + // Update the DMA time stamp for the next call to GetPosition() + // + m_ullDmaTimeStamp = hnsCurrentTime; +} + +//============================================================================= +#pragma code_seg() +VOID CMiniportWaveRTStream::WriteBytes +( +_In_ ULONG ByteDisplacement +) +/*++ + +Routine Description: + +This function writes the audio buffer using a sine wave generator. + +Arguments: + +ByteDisplacement - # of bytes to process. + +--*/ +{ + ULONG bufferOffset = m_ullLinearPosition % m_ulDmaBufferSize; + + // Normally this will loop no more than once for a single wrap, but if + // many bytes have been displaced then this may loops many times. + while (ByteDisplacement > 0) + { + ULONG runWrite = min(ByteDisplacement, m_ulDmaBufferSize - bufferOffset); + m_ToneGenerator.GenerateSine(m_pDmaBuffer + bufferOffset, runWrite); + bufferOffset = (bufferOffset + runWrite) % m_ulDmaBufferSize; + ByteDisplacement -= runWrite; + } +} + +//============================================================================= +#pragma code_seg() +VOID CMiniportWaveRTStream::ReadBytes +( +_In_ ULONG ByteDisplacement +) +/*++ + +Routine Description: + +This function reads the audio buffer and saves the data in a file. + +Arguments: + +ByteDisplacement - # of bytes to process. + +--*/ +{ + ULONG bufferOffset = m_ullLinearPosition % m_ulDmaBufferSize; + + // Normally this will loop no more than once for a single wrap, but if + // many bytes have been displaced then this may loops many times. + while (ByteDisplacement > 0) + { + ULONG runWrite = min(ByteDisplacement, m_ulDmaBufferSize - bufferOffset); + m_SaveData.WriteData(m_pDmaBuffer + bufferOffset, runWrite); + bufferOffset = (bufferOffset + runWrite) % m_ulDmaBufferSize; + ByteDisplacement -= runWrite; + } +} + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP_(NTSTATUS) +CMiniportWaveRTStream::SetContentId +( + _In_ ULONG contentId, + _In_ PCDRMRIGHTS drmRights +) +/*++ + +Routine Description: + + Sets DRM content Id for this stream. Also updates the Mixed content Id. + +Arguments: + + contentId - new content id + + drmRights - rights for this stream. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + DPF_ENTER(("[CMiniportWaveRT::SetContentId]")); + + NTSTATUS ntStatus; + ULONG ulOldContentId = contentId; + + m_ulContentId = contentId; + + // + // Miniport should create a mixed DrmRights. + // + ntStatus = m_pMiniport->UpdateDrmRights(); + + // + // Restore the passed-in content Id. + // + if (!NT_SUCCESS(ntStatus)) + { + m_ulContentId = ulOldContentId; + } + + // + // SYSVAD writes each stream seperately to disk. If the rights for this + // stream indicates that the stream is CopyProtected, stop writing to disk. + // + m_SaveData.Disable(drmRights->CopyProtect); + + // + // From MSDN: + // + // This sample doesn't forward protected content, but if your driver uses + // lower layer drivers or a different stack to properly work, please see the + // following info from MSDN: + // + // "Before allowing protected content to flow through a data path, the system + // verifies that the data path is secure. To do so, the system authenticates + // each module in the data path beginning at the upstream end of the data path + // and moving downstream. As each module is authenticated, that module gives + // the system information about the next module in the data path so that it + // can also be authenticated. To be successfully authenticated, a module's + // binary file must be signed as DRM-compliant. + // + // Two adjacent modules in the data path can communicate with each other in + // one of several ways. If the upstream module calls the downstream module + // through IoCallDriver, the downstream module is part of a WDM driver. In + // this case, the upstream module calls the DrmForwardContentToDeviceObject + // function to provide the system with the device object representing the + // downstream module. (If the two modules communicate through the downstream + // module's COM interface or content handlers, the upstream module calls + // DrmForwardContentToInterface or DrmAddContentHandlers instead.) + // + // DrmForwardContentToDeviceObject performs the same function as + // PcForwardContentToDeviceObject and IDrmPort2::ForwardContentToDeviceObject." + // + // Other supported DRM DDIs for down-level module validation are: + // DrmForwardContentToInterfaces and DrmAddContentHandlers. + // + // For more information, see MSDN's DRM Functions and Interfaces. + // + + return ntStatus; +} // SetContentId + +#ifdef SYSVAD_BTH_BYPASS + +//============================================================================= +#pragma code_seg() +NTSTATUS +CMiniportWaveRTStream::GetScoStreamNtStatus() +/*++ + +Routine Description: + + Checks if the Bluetooth SCO HFP connection is up, if not, an error is returned. + +Return Value: + + NT status code. + +--*/ +{ + DPF_ENTER(("[CMiniportWaveRTStream::GetScoStreamNtStatus]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_STATE; + + if (m_ScoOpen) + { + PBTHHFPDEVICECOMMON bthHfpDevice; + + ASSERT(m_pMiniport->IsBthHfpDevice()); + bthHfpDevice = m_pMiniport->GetBthHfpDevice(); // weak ref. + ASSERT(bthHfpDevice != NULL); + + if (bthHfpDevice->GetStreamStatus()) + { + ntStatus = STATUS_SUCCESS; + } + } + + return ntStatus; +} +#endif // SYSVAD_BTH_BYPASS + +//============================================================================= +#pragma code_seg() +void +TimerNotifyRT +( + _In_ PKDPC Dpc, + _In_opt_ PVOID DeferredContext, + _In_opt_ PVOID SA1, + _In_opt_ PVOID SA2 +) +{ + LARGE_INTEGER qpc; + LARGE_INTEGER qpcFrequency; + + UNREFERENCED_PARAMETER(Dpc); + UNREFERENCED_PARAMETER(SA1); + UNREFERENCED_PARAMETER(SA2); + + _IRQL_limited_to_(DISPATCH_LEVEL); + + qpc = KeQueryPerformanceCounter(&qpcFrequency); + + CMiniportWaveRTStream* _this = (CMiniportWaveRTStream*)DeferredContext; + + if (NULL == _this) + { + return; + } + + InterlockedIncrement64(&_this->m_llPacketCounter); + +#ifdef SYSVAD_BTH_BYPASS + if (_this->m_ScoOpen) + { + if (!NT_SUCCESS(_this->GetScoStreamNtStatus())) + { + return; + } + } +#endif // SYSVAD_BTH_BYPASS + + _this->m_pMiniport->DpcRoutine(qpc.QuadPart, qpcFrequency.QuadPart); + + if (_this->m_KsState != KSSTATE_RUN) + { + return; + } + + PADAPTERCOMMON pAdapterComm = _this->m_pMiniport->GetAdapterCommObj(); + + // Simple buffer underrun detection. + if (!_this->IsCurrentWaveRTWritePositionUpdated()) + { + //Event type: eMINIPORT_GLITCH_REPORT + //Parameter 1: Current linear buffer position + //Parameter 2: the previous WaveRtBufferWritePosition that the drive received + //Parameter 3: major glitch code: 1:WaveRT buffer is underrun + //Parameter 4: minor code for the glitch cause + pAdapterComm->WriteEtwEvent(eMINIPORT_GLITCH_REPORT, + 100, // replace with the correct "Current linear buffer position" + _this->GetCurrentWaveRTWritePosition(), + 1, // WaveRT buffer is underrun + 0); + } + + if (!IsListEmpty(&_this->m_NotificationList)) + { + PLIST_ENTRY leCurrent = _this->m_NotificationList.Flink; + while (leCurrent != &_this->m_NotificationList) + { + NotificationListEntry* nleCurrent = CONTAINING_RECORD( leCurrent, NotificationListEntry, ListEntry); + //Event type: eMINIPORT_BUFFER_COMPLETE + //Parameter 1: Current linear buffer position + //Parameter 2: the previous WaveRtBufferWritePosition that the drive received + //Parameter 3: Data length completed + //Parameter 4:0 + pAdapterComm->WriteEtwEvent(eMINIPORT_BUFFER_COMPLETE, + 100, // replace with the correct "Current linear buffer position" + _this->GetCurrentWaveRTWritePosition(), + 300, // repalce with the correct "Data length completed" + 0); // always zero + + KeSetEvent(nleCurrent->NotificationEvent, 0, 0); + + leCurrent = leCurrent->Flink; + } + } +} +//============================================================================= + diff --git a/audio/sysvad/EndpointsCommon/minwavertstream.h b/audio/sysvad/EndpointsCommon/minwavertstream.h new file mode 100644 index 000000000..78be0e153 --- /dev/null +++ b/audio/sysvad/EndpointsCommon/minwavertstream.h @@ -0,0 +1,261 @@ +/*++ + +Copyright (c) 1997-2010 Microsoft Corporation All Rights Reserved + +Module Name: + + minwavert.h + +Abstract: + + Definition of wavert miniport class. + +--*/ + +#ifndef _SYSVAD_MINWAVERTSTREAM_H_ +#define _SYSVAD_MINWAVERTSTREAM_H_ + +#include "savedata.h" +#include "tonegenerator.h" + +// +// Structure to store notifications events in a protected list +// +typedef struct _NotificationListEntry +{ + LIST_ENTRY ListEntry; + PKEVENT NotificationEvent; +} NotificationListEntry; + +KDEFERRED_ROUTINE TimerNotifyRT; + +//============================================================================= +// Referenced Forward +//============================================================================= +class CMiniportWaveRT; +typedef CMiniportWaveRT *PCMiniportWaveRT; + +//============================================================================= +// Classes +//============================================================================= +/////////////////////////////////////////////////////////////////////////////// +// CMiniportWaveRTStream +// +class CMiniportWaveRTStream : + public IDrmAudioStream, + public IMiniportWaveRTStreamNotification, + public IMiniportWaveRTInputStream, + public IMiniportWaveRTOutputStream, + public IMiniportStreamAudioEngineNode, + // IMiniportStreamAudioEngineNode2 is only available for WinBlue + // We might need #ifdef this for WinBlue to avoid any compilation error when building downlvel OS support. + public IMiniportStreamAudioEngineNode2, + public CUnknown +{ +protected: + PPORTWAVERTSTREAM m_pPortStream; + LIST_ENTRY m_NotificationList; + PKTIMER m_pNotificationTimer; + PRKDPC m_pNotificationDpc; + ULONG m_ulNotificationIntervalMs; + ULONG m_ulCurrentWritePosition; + LONG m_IsCurrentWritePositionUpdated; + +public: + DECLARE_STD_UNKNOWN(); + DEFINE_STD_CONSTRUCTOR(CMiniportWaveRTStream); + ~CMiniportWaveRTStream(); + + IMP_IMiniportWaveRTStream; + IMP_IMiniportWaveRTStreamNotification; + IMP_IMiniportWaveRTInputStream; + IMP_IMiniportWaveRTOutputStream; + IMP_IMiniportStreamAudioEngineNode; + IMP_IMiniportStreamAudioEngineNode2; + IMP_IMiniportWaveRT; + IMP_IDrmAudioStream; + + NTSTATUS Init + ( + _In_ PCMiniportWaveRT Miniport, + _In_ PPORTWAVERTSTREAM Stream, + _In_ ULONG Channel, + _In_ BOOLEAN Capture, + _In_ PKSDATAFORMAT DataFormat, + _In_ GUID SignalProcessingMode + ); + + // Friends + friend class CMiniportWaveRT; + friend KDEFERRED_ROUTINE TimerNotifyRT; +protected: + CMiniportWaveRT* m_pMiniport; + ULONG m_ulPin; + BOOLEAN m_bCapture; + BOOLEAN m_bUnregisterStream; + ULONG m_ulDmaBufferSize; + BYTE* m_pDmaBuffer; + ULONG m_ulNotificationsPerBuffer; + KSSTATE m_KsState; + PKTIMER m_pTimer; + PRKDPC m_pDpc; + ULONGLONG m_ullPlayPosition; + ULONGLONG m_ullWritePosition; + ULONGLONG m_ullLinearPosition; + ULONG m_ulLastOsReadPacket; + ULONG m_ulLastOsWritePacket; + LONGLONG m_llEoSPosition; + LONGLONG m_llPacketCounter; + ULONGLONG m_ullDmaTimeStamp; + LARGE_INTEGER m_ullPerformanceCounterFrequency; + ULONGLONG m_hnsElapsedTimeCarryForward; + ULONG m_ulDmaMovementRate; + BOOL m_bLfxEnabled; + PBOOL m_pbMuted; + PLONG m_plVolumeLevel; + PLONG m_plPeakMeter; + PWAVEFORMATEXTENSIBLE m_pWfExt; + ULONG m_ulContentId; + CSaveData m_SaveData; + ToneGenerator m_ToneGenerator; + GUID m_SignalProcessingMode; + +#ifdef SYSVAD_BTH_BYPASS + BOOLEAN m_ScoOpen; +#endif // SYSVAD_BTH_BYPASS + +public: + + NTSTATUS GetVolumeChannelCount + ( + _Out_ UINT32 *puiChannelCount + ); + + NTSTATUS GetVolumeSteppings + ( + _Out_writes_bytes_(_ui32DataSize) PKSPROPERTY_STEPPING_LONG _pKsPropStepLong, + _In_ UINT32 _ui32DataSize + ); + + NTSTATUS GetChannelVolume + ( + _In_ UINT32 _uiChannel, + _Out_ LONG *_pVolume + ); + + NTSTATUS SetChannelVolume + ( + _In_ UINT32 _uiChannel, + _In_ LONG _Volume + ); + + NTSTATUS GetPeakMeterChannelCount + ( + _Out_ UINT32 *puiChannelCount + ); + + NTSTATUS GetPeakMeterSteppings + ( + _Out_writes_bytes_(_ui32DataSize) PKSPROPERTY_STEPPING_LONG _pKsPropStepLong, + _In_ UINT32 _ui32DataSize + ); + + NTSTATUS GetChannelPeakMeter + ( + _In_ UINT32 _uiChannel, + _Out_ LONG *_plPeakMeter + ); + + NTSTATUS GetMuteChannelCount + ( + _Out_ UINT32 *puiChannelCount + ); + + NTSTATUS GetMuteSteppings + ( + _Out_writes_bytes_(_ui32DataSize) PKSPROPERTY_STEPPING_LONG _pKsPropStepLong, + _In_ UINT32 _ui32DataSize + ); + + NTSTATUS GetChannelMute + ( + _In_ UINT32 _uiChannel, + _Out_ BOOL *_pbMute + ); + + NTSTATUS SetChannelMute + ( + _In_ UINT32 _uiChannel, + _In_ BOOL _bMute + ); + + //presentation + NTSTATUS GetPresentationPosition + ( + _Out_ KSAUDIO_PRESENTATION_POSITION *_pPresentationPosition + ); + + NTSTATUS SetCurrentWritePosition + ( + _In_ ULONG ulCurrentWritePosition + ); + + public: + NTSTATUS SetLoopbackProtection + ( + _In_ CONSTRICTOR_OPTION ulProtectionOption + ); + + ULONG GetCurrentWaveRTWritePosition() + { + return m_ulCurrentWritePosition; + }; + + // To support simple underrun validation. + BOOL IsCurrentWaveRTWritePositionUpdated() + { + return InterlockedExchange(&m_IsCurrentWritePositionUpdated, 0) ? TRUE : FALSE; + }; + + GUID GetSignalProcessingMode() + { + return m_SignalProcessingMode; + } + +private: + + // Helper functions. + VOID WriteBytes + ( + _In_ ULONG ByteDisplacement + ); + + VOID ReadBytes + ( + _In_ ULONG ByteDisplacement + ); + + VOID UpdatePosition + ( + _In_ LARGE_INTEGER ilQPC + ); + + NTSTATUS SetCurrentWritePositionInternal + ( + _In_ ULONG ulCurrentWritePosition + ); + + NTSTATUS GetLinearBufferPosition + ( + _Out_ ULONGLONG *pullLinearBufferPosition, + _Out_ LARGE_INTEGER *_pliQPCTime + ); + +#ifdef SYSVAD_BTH_BYPASS + NTSTATUS GetScoStreamNtStatus(); +#endif // SYSVAD_BTH_BYPASS + +}; +typedef CMiniportWaveRTStream *PCMiniportWaveRTStream; +#endif // _SYSVAD_MINWAVERTSTREAM_H_ + diff --git a/audio/sysvad/EndpointsCommon/simple.h b/audio/sysvad/EndpointsCommon/simple.h new file mode 100644 index 000000000..55913cdc4 --- /dev/null +++ b/audio/sysvad/EndpointsCommon/simple.h @@ -0,0 +1,170 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + simple.h + +Abstract: + + Node and Pin numbers and other common definitions for simple sample. + +--*/ + +#ifndef _SYSVAD_SIMPLE_H_ +#define _SYSVAD_SIMPLE_H_ + +// Name Guid +// {946A7B1A-EBBC-422a-A81F-F07C8D40D3B4} +#define STATIC_NAME_SYSVAD_SIMPLE\ + 0x946a7b1a, 0xebbc, 0x422a, 0xa8, 0x1f, 0xf0, 0x7c, 0x8d, 0x40, 0xd3, 0xb4 +DEFINE_GUIDSTRUCT("946A7B1A-EBBC-422a-A81F-F07C8D40D3B4", NAME_SYSVAD_SIMPLE); +#define NAME_SYSVAD_SIMPLE DEFINE_GUIDNAMED(NAME_SYSVAD_SIMPLE) + +//---------------------------------------------------- +// New defines for the render endpoints. +//---------------------------------------------------- + +// Default pin instances. +#define MAX_INPUT_SYSTEM_STREAMS 1 +#define MAX_INPUT_OFFLOAD_STREAMS 3 +#define MAX_OUTPUT_LOOPBACK_STREAMS 1 + +// Wave pins - offloading supported. +enum +{ + KSPIN_WAVE_RENDER_SINK_SYSTEM = 0, + KSPIN_WAVE_RENDER_SINK_OFFLOAD, + KSPIN_WAVE_RENDER_SINK_LOOPBACK, + KSPIN_WAVE_RENDER_SOURCE +}; + +// Wave Topology nodes - offloading supported. +enum +{ + KSNODE_WAVE_AUDIO_ENGINE = 0 +}; + +// Wave pins - offloading is NOT supported. +enum +{ + KSPIN_WAVE_RENDER2_SINK_SYSTEM = 0, + KSPIN_WAVE_RENDER2_SINK_LOOPBACK, + KSPIN_WAVE_RENDER2_SOURCE +}; + +// Wave pins - cellular +enum +{ + KSPIN_WAVE_BIDI = 0, + KSPIN_WAVE_BIDI_BRIDGE +}; + +// Wave Topology nodes - offloading is NOT supported. +enum +{ + KSNODE_WAVE_SUM = 0, + KSNODE_WAVE_VOLUME, + KSNODE_WAVE_MUTE, + KSNODE_WAVE_PEAKMETER +}; + +// Topology pins. +enum +{ + KSPIN_TOPO_WAVEOUT_SOURCE = 0, + KSPIN_TOPO_LINEOUT_DEST, +}; + +// Topology pins - cellular +enum +{ + KSPIN_TOPO_BIDI1 = 0, + KSPIN_TOPO_BIDI1_BRIDGE, + KSPIN_TOPO_BIDI2, + KSPIN_TOPO_BIDI2_BRIDGE +}; + +// Topology nodes. +enum +{ + KSNODE_TOPO_WAVEOUT_VOLUME = 0, + KSNODE_TOPO_WAVEOUT_MUTE, + KSNODE_TOPO_WAVEOUT_PEAKMETER +}; + +//---------------------------------------------------- +// New defines for the capture endpoints. +//---------------------------------------------------- + +// Default pin instances. +#define MAX_INPUT_STREAMS 1 // Number of capture streams. + +// Wave pins +enum +{ + KSPIN_WAVE_BRIDGE = 0, + KSPIN_WAVEIN_HOST, + KSPIN_WAVEIN_KEYWORD, +}; + +// Wave pins - FM +enum +{ + KSPIN_WAVE_FMRX = 0, + KSPIN_WAVE_FMRX_BRIDGE +}; + +// Wave Topology nodes. +enum +{ + KSNODE_WAVE_ADC = 0 +}; + +// topology pins. +enum +{ + KSPIN_TOPO_MIC_ELEMENTS, + KSPIN_TOPO_BRIDGE +}; + +// topology nodes. +enum +{ + KSNODE_TOPO_VOLUME, + KSNODE_TOPO_MUTE, + KSNODE_TOPO_PEAKMETER +}; + +// Topology pins - FM +enum +{ + KSPIN_TOPO_FMRX, + KSPIN_TOPO_FMRX_BRIDGE +}; + +// data format attribute range definitions. +static +KSATTRIBUTE PinDataRangeSignalProcessingModeAttribute = +{ + sizeof(KSATTRIBUTE), + 0, + STATICGUIDOF(KSATTRIBUTEID_AUDIOSIGNALPROCESSING_MODE), +}; + +static +PKSATTRIBUTE PinDataRangeAttributes[] = +{ + &PinDataRangeSignalProcessingModeAttribute, +}; + +static +KSATTRIBUTE_LIST PinDataRangeAttributeList = +{ + ARRAYSIZE(PinDataRangeAttributes), + PinDataRangeAttributes, +}; + +#endif // _SYSVAD_SIMPLE_H_ + diff --git a/audio/sysvad/EndpointsCommon/speakerhptopo.cpp b/audio/sysvad/EndpointsCommon/speakerhptopo.cpp new file mode 100644 index 000000000..96e899a09 --- /dev/null +++ b/audio/sysvad/EndpointsCommon/speakerhptopo.cpp @@ -0,0 +1,274 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + speakerhptopo.cpp + +Abstract: + + Implementation of topology miniport for the speaker (external: headphone). + +--*/ + +#pragma warning (disable : 4127) + +#include +#include "simple.h" +#include "mintopo.h" +#include "speakerhptopo.h" +#include "speakerhptoptable.h" + + +#pragma code_seg("PAGE") + +//============================================================================= +NTSTATUS +PropertyHandler_SpeakerHpJackDescription +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_Jack, KSPROPERTY_JACK_DESCRIPTION ) + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_SpeakerHpJackDescription]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + ULONG nPinId = (ULONG)-1; + + ULONG cJackDescriptions = ARRAYSIZE(SpeakerHpJackDescriptions); + PKSJACK_DESCRIPTION * JackDescriptions = SpeakerHpJackDescriptions; + + if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + nPinId = *(PULONG(PropertyRequest->Instance)); + + if ((nPinId < cJackDescriptions) && (JackDescriptions[nPinId] != NULL)) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else + { + ULONG cbNeeded = sizeof(KSMULTIPLE_ITEM) + sizeof(KSJACK_DESCRIPTION); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbNeeded; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbNeeded) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PKSMULTIPLE_ITEM pMI = (PKSMULTIPLE_ITEM)PropertyRequest->Value; + PKSJACK_DESCRIPTION pDesc = (PKSJACK_DESCRIPTION)(pMI+1); + + pMI->Size = cbNeeded; + pMI->Count = 1; + + RtlCopyMemory(pDesc, JackDescriptions[nPinId], sizeof(KSJACK_DESCRIPTION)); + ntStatus = STATUS_SUCCESS; + } + } + } + } + } + + return ntStatus; +} + +//============================================================================= +NTSTATUS +PropertyHandler_SpeakerHpJackDescription2 +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_Jack, KSPROPERTY_JACK_DESCRIPTION2 ) + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_SpeakerHpJackDescription2]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + ULONG nPinId = (ULONG)-1; + + ULONG cJackDescriptions = ARRAYSIZE(SpeakerHpJackDescriptions); + PKSJACK_DESCRIPTION * JackDescriptions = SpeakerHpJackDescriptions; + + if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + nPinId = *(PULONG(PropertyRequest->Instance)); + + if ((nPinId < cJackDescriptions) && (JackDescriptions[nPinId] != NULL)) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else + { + ULONG cbNeeded = sizeof(KSMULTIPLE_ITEM) + sizeof(KSJACK_DESCRIPTION2); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbNeeded; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbNeeded) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PKSMULTIPLE_ITEM pMI = (PKSMULTIPLE_ITEM)PropertyRequest->Value; + PKSJACK_DESCRIPTION2 pDesc = (PKSJACK_DESCRIPTION2)(pMI+1); + + pMI->Size = cbNeeded; + pMI->Count = 1; + + RtlZeroMemory(pDesc, sizeof(KSJACK_DESCRIPTION2)); + + // + // Specifies the lower 16 bits of the DWORD parameter. This parameter indicates whether + // the jack is currently active, streaming, idle, or hardware not ready. + // + pDesc->DeviceStateInfo = 0; + + // + // From MSDN: + // "If an audio device lacks jack presence detection, the IsConnected member of + // the KSJACK_DESCRIPTION structure must always be set to TRUE. To remove the + // ambiguity that results from this dual meaning of the TRUE value for IsConnected, + // a client application can call IKsJackDescription2::GetJackDescription2 to read + // the JackCapabilities flag of the KSJACK_DESCRIPTION2 structure. If this flag has + // the JACKDESC2_PRESENCE_DETECT_CAPABILITY bit set, it indicates that the endpoint + // does in fact support jack presence detection. In that case, the return value of + // the IsConnected member can be interpreted to accurately reflect the insertion status + // of the jack." + // + // Bit definitions: + // 0x00000001 - JACKDESC2_PRESENCE_DETECT_CAPABILITY + // 0x00000002 - JACKDESC2_DYNAMIC_FORMAT_CHANGE_CAPABILITY + // + pDesc->JackCapabilities = JACKDESC2_PRESENCE_DETECT_CAPABILITY; + + ntStatus = STATUS_SUCCESS; + } + } + } + } + } + + return ntStatus; +} + +//============================================================================= +NTSTATUS +PropertyHandler_SpeakerHpTopoFilter +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Redirects property request to miniport object + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_SpeakerHpTopoFilter]")); + + // PropertryRequest structure is filled by portcls. + // MajorTarget is a pointer to miniport object for miniports. + // + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + // + // This line shows how to get a pointer to the miniport topology object. + // + PCMiniportTopology pMiniport = (PCMiniportTopology)PropertyRequest->MajorTarget; + UNREFERENCED_VAR(pMiniport); + + if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_Jack)) + { + if (PropertyRequest->PropertyItem->Id == KSPROPERTY_JACK_DESCRIPTION) + { + ntStatus = PropertyHandler_SpeakerHpJackDescription(PropertyRequest); + } + else if (PropertyRequest->PropertyItem->Id == KSPROPERTY_JACK_DESCRIPTION2) + { + ntStatus = PropertyHandler_SpeakerHpJackDescription2(PropertyRequest); + } + } + + return ntStatus; +} // PropertyHandler_SpeakerHpTopoFilter + +#pragma code_seg() + diff --git a/audio/sysvad/EndpointsCommon/speakerhptopo.h b/audio/sysvad/EndpointsCommon/speakerhptopo.h new file mode 100644 index 000000000..9974859fe --- /dev/null +++ b/audio/sysvad/EndpointsCommon/speakerhptopo.h @@ -0,0 +1,21 @@ + +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + speakerhptopo.h + +Abstract: + + Declaration of topology miniport for the speaker (external: headphone). + +--*/ + +#ifndef _SYSVAD_SPEAKERHPTOPO_H_ +#define _SYSVAD_SPEAKERHPTOPO_H_ + +NTSTATUS PropertyHandler_SpeakerHpTopoFilter(_In_ PPCPROPERTY_REQUEST PropertyRequest); + +#endif // _SYSVAD_SPEAKERHPTOPO_H_ diff --git a/audio/sysvad/EndpointsCommon/speakerhptoptable.h b/audio/sysvad/EndpointsCommon/speakerhptoptable.h new file mode 100644 index 000000000..870fd02d9 --- /dev/null +++ b/audio/sysvad/EndpointsCommon/speakerhptoptable.h @@ -0,0 +1,157 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + speakerhptoptable.h + +Abstract: + + Declaration of topology tables for the speaker (external: headphone). + +--*/ + +#ifndef _SYSVAD_SPEAKERHPTOPTABLE_H_ +#define _SYSVAD_SPEAKERHPTOPTABLE_H_ + +//============================================================================= +static +KSJACK_DESCRIPTION SpeakerHpJackDesc = +{ + KSAUDIO_SPEAKER_MONO, + JACKDESC_RGB(179, 201, 140), + eConnTypeCombination, + eGeoLocRear, + eGenLocPrimaryBox, + ePortConnJack, + TRUE +}; + +//============================================================================= +static +KSDATARANGE SpeakerHpTopoPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +//============================================================================= +static +PKSDATARANGE SpeakerHpTopoPinDataRangePointersBridge[] = +{ + &SpeakerHpTopoPinDataRangesBridge[0] +}; + +//============================================================================= +static +PCPIN_DESCRIPTOR SpeakerHpTopoMiniportPins[] = +{ + // KSPIN - topology filter in-pin + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(SpeakerHpTopoPinDataRangePointersBridge),// DataRangesCount + SpeakerHpTopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_IN, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSCATEGORY_AUDIO, // Category + NULL, // Name + 0 // Reserved + } + }, + // KSPIN - topology filter out-pin + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(SpeakerHpTopoPinDataRangePointersBridge),// DataRangesCount + SpeakerHpTopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_OUT, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSNODETYPE_HEADPHONES, // Category + NULL, // Name + 0 // Reserved + } + } +}; + +//============================================================================= +// Only return a KSJACK_DESCRIPTION for the physical bridge pin. +static +PKSJACK_DESCRIPTION SpeakerHpJackDescriptions[] = +{ + NULL, + &SpeakerHpJackDesc +}; + +//============================================================================= +static +PCCONNECTION_DESCRIPTOR SpeakerHpTopoMiniportConnections[] = +{ + // FromNode, FromPin, ToNode, ToPin + { PCFILTER_NODE, KSPIN_TOPO_WAVEOUT_SOURCE, PCFILTER_NODE, KSPIN_TOPO_LINEOUT_DEST} +}; + + +//============================================================================= +static +PCPROPERTY_ITEM PropertiesSpeakerHpTopoFilter[] = +{ + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_SpeakerHpTopoFilter + }, + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION2, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_SpeakerHpTopoFilter + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationSpeakerHpTopoFilter, PropertiesSpeakerHpTopoFilter); + +//============================================================================= +static +PCFILTER_DESCRIPTOR SpeakerHpTopoMiniportFilterDescriptor = +{ + 0, // Version + &AutomationSpeakerHpTopoFilter, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(SpeakerHpTopoMiniportPins), // PinCount + SpeakerHpTopoMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + 0, // NodeCount + NULL, // Nodes + SIZEOF_ARRAY(SpeakerHpTopoMiniportConnections), // ConnectionCount + SpeakerHpTopoMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories +}; + +#endif // _SYSVAD_SPEAKERHPTOPTABLE_H_ + + diff --git a/audio/sysvad/EndpointsCommon/speakerhpwavtable.h b/audio/sysvad/EndpointsCommon/speakerhpwavtable.h new file mode 100644 index 000000000..2b5b61ed8 --- /dev/null +++ b/audio/sysvad/EndpointsCommon/speakerhpwavtable.h @@ -0,0 +1,854 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + speakerhpwavtable.h + +Abstract: + + Declaration of wave miniport tables for the speaker (external: headphone). + +--*/ + +#ifndef _SYSVAD_SPEAKERHPWAVTABLE_H_ +#define _SYSVAD_SPEAKERHPWAVTABLE_H_ + + +// To keep the code simple assume device supports only 48KHz, 16-bit, stereo (PCM and NON-PCM) + +#define SPEAKERHP_DEVICE_MAX_CHANNELS 2 // Max Channels. + +#define SPEAKERHP_HOST_MAX_CHANNELS 2 // Max Channels. +#define SPEAKERHP_HOST_MIN_BITS_PER_SAMPLE 16 // Min Bits Per Sample +#define SPEAKERHP_HOST_MAX_BITS_PER_SAMPLE 16 // Max Bits Per Sample +#define SPEAKERHP_HOST_MIN_SAMPLE_RATE 24000 // Min Sample Rate +#define SPEAKERHP_HOST_MAX_SAMPLE_RATE 96000 // Max Sample Rate + +#define SPEAKERHP_OFFLOAD_MAX_CHANNELS 2 // Max Channels. +#define SPEAKERHP_OFFLOAD_MIN_BITS_PER_SAMPLE 16 // Min Bits Per Sample +#define SPEAKERHP_OFFLOAD_MAX_BITS_PER_SAMPLE 16 // Max Bits Per Sample +#define SPEAKERHP_OFFLOAD_MIN_SAMPLE_RATE 44100 // Min Sample Rate +#define SPEAKERHP_OFFLOAD_MAX_SAMPLE_RATE 48000 // Max Sample Rate + +#define SPEAKERHP_LOOPBACK_MAX_CHANNELS 2 // Max Channels. +#define SPEAKERHP_LOOPBACK_MIN_BITS_PER_SAMPLE 16 // Min Bits Per Sample +#define SPEAKERHP_LOOPBACK_MAX_BITS_PER_SAMPLE 16 // Max Bits Per Sample +#define SPEAKERHP_LOOPBACK_MIN_SAMPLE_RATE 24000 // Min Sample Rate +#define SPEAKERHP_LOOPBACK_MAX_SAMPLE_RATE 48000 // Max Sample Rate + +#define SPEAKERHP_DOLBY_DIGITAL_MAX_CHANNELS 2 // Max Channels. +#define SPEAKERHP_DOLBY_DIGITAL_MIN_BITS_PER_SAMPLE 16 // Min Bits Per Sample +#define SPEAKERHP_DOLBY_DIGITAL_MAX_BITS_PER_SAMPLE 16 // Max Bits Per Sample +#define SPEAKERHP_DOLBY_DIGITAL_MIN_SAMPLE_RATE 44100 // Min Sample Rate +#define SPEAKERHP_DOLBY_DIGITAL_MAX_SAMPLE_RATE 44100 // Max Sample Rate + +// +// Max # of pin instances. +// +#define SPEAKERHP_MAX_INPUT_SYSTEM_STREAMS 2 // Raw + Default streams +#define SPEAKERHP_MAX_INPUT_OFFLOAD_STREAMS MAX_INPUT_OFFLOAD_STREAMS +#define SPEAKERHP_MAX_OUTPUT_LOOPBACK_STREAMS MAX_OUTPUT_LOOPBACK_STREAMS + +//============================================================================= +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE SpeakerHpAudioEngineSupportedDeviceFormats[] = +{ + { // 0 : First entry in this table is the default format for the audio engine + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 44100, + 176400, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 1 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 24000, + 96000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 2 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 48000, + 192000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 3 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 88200, + 352800, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 4 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 96000, + 384000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE SpeakerHpHostPinSupportedDeviceFormats[] = +{ + { // 0 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 24000, + 96000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 1 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 32000, + 128000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 2 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 44100, + 176400, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 3 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 48000, + 192000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 4 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 88200, + 352800, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 5 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 96000, + 384000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE SpeakerHpOffloadPinSupportedDeviceFormats[] = +{ + { // 0 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 44100, + 176400, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 1 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 48000, + 192000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE SpeakerHpLoopbackPinSupportedDeviceFormats[] = +{ + { // 0 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 24000, + 96000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 1 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 32000, + 128000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 2 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 44100, + 176400, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 3 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 48000, + 192000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +// +// Supported modes (only on streaming pins). +// +static +MODE_AND_DEFAULT_FORMAT SpeakerHpHostPinSupportedDeviceModes[] = +{ + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_RAW, + &SpeakerHpHostPinSupportedDeviceFormats[3].DataFormat // 48KHz + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_DEFAULT, + &SpeakerHpHostPinSupportedDeviceFormats[3].DataFormat // 48KHz + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_MEDIA, + &SpeakerHpHostPinSupportedDeviceFormats[3].DataFormat // 48KHz + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_MOVIE, + &SpeakerHpHostPinSupportedDeviceFormats[3].DataFormat // 48KHz + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_COMMUNICATIONS, + &SpeakerHpHostPinSupportedDeviceFormats[0].DataFormat // 24KHz + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_NOTIFICATION, + &SpeakerHpHostPinSupportedDeviceFormats[3].DataFormat // 48KHz + } +}; + +static +MODE_AND_DEFAULT_FORMAT SpeakerHpOffloadPinSupportedDeviceModes[] = +{ + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_DEFAULT, + &SpeakerHpOffloadPinSupportedDeviceFormats[1].DataFormat // 48KHz + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_MEDIA, + &SpeakerHpOffloadPinSupportedDeviceFormats[1].DataFormat // 48KHz + } +}; + +// +// The entries here must follow the same order as the filter's pin +// descriptor array. +// +static +PIN_DEVICE_FORMATS_AND_MODES SpeakerHpPinDeviceFormatsAndModes[] = +{ + { + SystemRenderPin, + SpeakerHpHostPinSupportedDeviceFormats, + SIZEOF_ARRAY(SpeakerHpHostPinSupportedDeviceFormats), + SpeakerHpHostPinSupportedDeviceModes, + SIZEOF_ARRAY(SpeakerHpHostPinSupportedDeviceModes) + }, + { + OffloadRenderPin, + SpeakerHpOffloadPinSupportedDeviceFormats, + SIZEOF_ARRAY(SpeakerHpOffloadPinSupportedDeviceFormats), + SpeakerHpOffloadPinSupportedDeviceModes, + SIZEOF_ARRAY(SpeakerHpOffloadPinSupportedDeviceModes), + }, + { + RenderLoopbackPin, + SpeakerHpLoopbackPinSupportedDeviceFormats, + SIZEOF_ARRAY(SpeakerHpLoopbackPinSupportedDeviceFormats), + NULL, // loopback doesn't support modes. + 0 + }, + { + BridgePin, + NULL, + 0, + NULL, + 0 + }, + { + NoPin, // For convenience, offload engine device formats appended here + SpeakerHpAudioEngineSupportedDeviceFormats, + SIZEOF_ARRAY(SpeakerHpAudioEngineSupportedDeviceFormats), + NULL, // no modes for this entry. + 0 + } +}; + +//============================================================================= +static +KSDATARANGE_AUDIO SpeakerHpPinDataRangesStream[] = +{ + { // 0 + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + SPEAKERHP_HOST_MAX_CHANNELS, + SPEAKERHP_HOST_MIN_BITS_PER_SAMPLE, + SPEAKERHP_HOST_MAX_BITS_PER_SAMPLE, + SPEAKERHP_HOST_MIN_SAMPLE_RATE, + SPEAKERHP_HOST_MAX_SAMPLE_RATE + }, + { // 1 + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + SPEAKERHP_OFFLOAD_MAX_CHANNELS, + SPEAKERHP_OFFLOAD_MIN_BITS_PER_SAMPLE, + SPEAKERHP_OFFLOAD_MAX_BITS_PER_SAMPLE, + SPEAKERHP_OFFLOAD_MIN_SAMPLE_RATE, + SPEAKERHP_OFFLOAD_MAX_SAMPLE_RATE + }, + { // 2 + { + sizeof(KSDATARANGE_AUDIO), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + SPEAKERHP_LOOPBACK_MAX_CHANNELS, + SPEAKERHP_LOOPBACK_MIN_BITS_PER_SAMPLE, + SPEAKERHP_LOOPBACK_MAX_BITS_PER_SAMPLE, + SPEAKERHP_LOOPBACK_MIN_SAMPLE_RATE, + SPEAKERHP_LOOPBACK_MAX_SAMPLE_RATE + } +}; + +static +PKSDATARANGE SpeakerHpPinDataRangePointersStream[] = +{ + PKSDATARANGE(&SpeakerHpPinDataRangesStream[0]), + PKSDATARANGE(&PinDataRangeAttributeList) +}; + +static +PKSDATARANGE SpeakerHpPinDataRangePointersOffloadStream[] = +{ + PKSDATARANGE(&SpeakerHpPinDataRangesStream[1]), + PKSDATARANGE(&PinDataRangeAttributeList) +}; + +static +PKSDATARANGE SpeakerHpPinDataRangePointersLoopbackStream[] = +{ + PKSDATARANGE(&SpeakerHpPinDataRangesStream[2]) +}; + +//============================================================================= +static +KSDATARANGE SpeakerHpPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +static +PKSDATARANGE SpeakerHpPinDataRangePointersBridge[] = +{ + &SpeakerHpPinDataRangesBridge[0] +}; + +//============================================================================= + +static +PCPROPERTY_ITEM PropertiesSpeakerHpOffloadPin[] = +{ + { + &KSPROPSETID_OffloadPin, // define new property set + KSPROPERTY_OFFLOAD_PIN_GET_STREAM_OBJECT_POINTER, // define properties + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_OffloadPin + }, + { + &KSPROPSETID_OffloadPin, // define new property set + KSPROPERTY_OFFLOAD_PIN_VERIFY_STREAM_OBJECT_POINTER, // define properties + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_OffloadPin + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationSpeakerHpOffloadPin, PropertiesSpeakerHpOffloadPin); + +//============================================================================= +static +PCPIN_DESCRIPTOR SpeakerHpWaveMiniportPins[] = +{ + // Wave Out Streaming Pin (renderer) KSPIN_WAVE_RENDER_SINK_SYSTEM + { + SPEAKERHP_MAX_INPUT_SYSTEM_STREAMS, + SPEAKERHP_MAX_INPUT_SYSTEM_STREAMS, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(SpeakerHpPinDataRangePointersStream), + SpeakerHpPinDataRangePointersStream, + KSPIN_DATAFLOW_IN, + KSPIN_COMMUNICATION_SINK, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, + // Wave Out Streaming Pin (Renderer) KSPIN_WAVE_RENDER_SINK_OFFLOAD + { + SPEAKERHP_MAX_INPUT_OFFLOAD_STREAMS, + SPEAKERHP_MAX_INPUT_OFFLOAD_STREAMS, + 0, + &AutomationSpeakerHpOffloadPin, // AutomationTable + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(SpeakerHpPinDataRangePointersOffloadStream), + SpeakerHpPinDataRangePointersOffloadStream, + KSPIN_DATAFLOW_IN, + KSPIN_COMMUNICATION_SINK, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, + // Wave Out Streaming Pin (Renderer) KSPIN_WAVE_RENDER_SINK_LOOPBACK + { + SPEAKERHP_MAX_OUTPUT_LOOPBACK_STREAMS, + SPEAKERHP_MAX_OUTPUT_LOOPBACK_STREAMS, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(SpeakerHpPinDataRangePointersLoopbackStream), + SpeakerHpPinDataRangePointersLoopbackStream, + KSPIN_DATAFLOW_OUT, + KSPIN_COMMUNICATION_SINK, + &KSNODETYPE_AUDIO_LOOPBACK, + NULL, + 0 + } + }, + // Wave Out Bridge Pin (Renderer) KSPIN_WAVE_RENDER_SOURCE + { + 0, + 0, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(SpeakerHpPinDataRangePointersBridge), + SpeakerHpPinDataRangePointersBridge, + KSPIN_DATAFLOW_OUT, + KSPIN_COMMUNICATION_NONE, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, +}; + +//============================================================================= +static +PCNODE_DESCRIPTOR SpeakerHpWaveMiniportNodes[] = +{ + // KSNODE_WAVE_AUDIO_ENGINE + { + 0, // Flags + NULL, // AutomationTable + &KSNODETYPE_AUDIO_ENGINE, // Type KSNODETYPE_AUDIO_ENGINE + NULL // Name + } +}; +//============================================================================= +// +// ---------------------------- +// | | +// System Pin 0-->| |--> 2 Loopback Pin +// | HW Audio Engine node | +// Offload Pin 1-->| |--> 3 KSPIN_WAVE_RENDER_SOURCE +// | | +// ---------------------------- +static +PCCONNECTION_DESCRIPTOR SpeakerHpWaveMiniportConnections[] = +{ + { PCFILTER_NODE, KSPIN_WAVE_RENDER_SINK_SYSTEM, KSNODE_WAVE_AUDIO_ENGINE, 1 }, + { PCFILTER_NODE, KSPIN_WAVE_RENDER_SINK_OFFLOAD, KSNODE_WAVE_AUDIO_ENGINE, 2 }, + { KSNODE_WAVE_AUDIO_ENGINE, 3, PCFILTER_NODE, KSPIN_WAVE_RENDER_SINK_LOOPBACK }, + { KSNODE_WAVE_AUDIO_ENGINE, 0, PCFILTER_NODE, KSPIN_WAVE_RENDER_SOURCE }, +}; + +//============================================================================= +static +PCPROPERTY_ITEM PropertiesSpeakerHpWaveFilter[] = +{ + { + &KSPROPSETID_Pin, + KSPROPERTY_PIN_PROPOSEDATAFORMAT, + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_WaveFilter + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationSpeakerHpWaveFilter, PropertiesSpeakerHpWaveFilter); + +//============================================================================= +static +PCFILTER_DESCRIPTOR SpeakerHpWaveMiniportFilterDescriptor = +{ + 0, // Version + &AutomationSpeakerHpWaveFilter, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(SpeakerHpWaveMiniportPins), // PinCount + SpeakerHpWaveMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + SIZEOF_ARRAY(SpeakerHpWaveMiniportNodes), // NodeCount + SpeakerHpWaveMiniportNodes, // Nodes + SIZEOF_ARRAY(SpeakerHpWaveMiniportConnections), // ConnectionCount + SpeakerHpWaveMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories - use defaults (audio, render, capture) +}; + +#endif // _SYSVAD_SPEAKERHPWAVTABLE_H_ + diff --git a/audio/sysvad/EndpointsCommon/speakertopo.cpp b/audio/sysvad/EndpointsCommon/speakertopo.cpp new file mode 100644 index 000000000..cca4dac94 --- /dev/null +++ b/audio/sysvad/EndpointsCommon/speakertopo.cpp @@ -0,0 +1,85 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + speakertopo.cpp + +Abstract: + + Implementation of topology miniport for the speaker (internal). + +--*/ + +#pragma warning (disable : 4127) + +#include +#include "simple.h" +#include "mintopo.h" +#include "speakertopo.h" +#include "speakertoptable.h" + + +#pragma code_seg("PAGE") + +//============================================================================= +NTSTATUS +PropertyHandler_SpeakerTopoFilter +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Redirects property request to miniport object + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_SpeakerTopoFilter]")); + + // PropertryRequest structure is filled by portcls. + // MajorTarget is a pointer to miniport object for miniports. + // + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + PCMiniportTopology pMiniport = (PCMiniportTopology)PropertyRequest->MajorTarget; + + if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_Jack)) + { + if (PropertyRequest->PropertyItem->Id == KSPROPERTY_JACK_DESCRIPTION) + { + ntStatus = pMiniport->PropertyHandlerJackDescription( + PropertyRequest, + ARRAYSIZE(SpeakerJackDescriptions), + SpeakerJackDescriptions + ); + } + else if (PropertyRequest->PropertyItem->Id == KSPROPERTY_JACK_DESCRIPTION2) + { + ntStatus = pMiniport->PropertyHandlerJackDescription2( + PropertyRequest, + ARRAYSIZE(SpeakerJackDescriptions), + SpeakerJackDescriptions, + 0 // jack capabilities + ); + } + } + + return ntStatus; +} // PropertyHandler_SpeakerTopoFilter + +#pragma code_seg() + diff --git a/audio/sysvad/EndpointsCommon/speakertopo.h b/audio/sysvad/EndpointsCommon/speakertopo.h new file mode 100644 index 000000000..1ea213740 --- /dev/null +++ b/audio/sysvad/EndpointsCommon/speakertopo.h @@ -0,0 +1,21 @@ + +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + speakertopo.h + +Abstract: + + Declaration of topology miniport for the speaker (internal). + +--*/ + +#ifndef _SYSVAD_SPEAKERTOPO_H_ +#define _SYSVAD_SPEAKERTOPO_H_ + +NTSTATUS PropertyHandler_SpeakerTopoFilter(_In_ PPCPROPERTY_REQUEST PropertyRequest); + +#endif // _SYSVAD_SPEAKERTOPO_H_ diff --git a/audio/sysvad/EndpointsCommon/speakertoptable.h b/audio/sysvad/EndpointsCommon/speakertoptable.h new file mode 100644 index 000000000..50f58c7b8 --- /dev/null +++ b/audio/sysvad/EndpointsCommon/speakertoptable.h @@ -0,0 +1,155 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + speakertoptable.h + +Abstract: + + Declaration of topology tables. + +--*/ + +#ifndef _SYSVAD_SPEAKERTOPTABLE_H_ +#define _SYSVAD_SPEAKERTOPTABLE_H_ + +//============================================================================= +static +KSDATARANGE SpeakerTopoPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +//============================================================================= +static +PKSDATARANGE SpeakerTopoPinDataRangePointersBridge[] = +{ + &SpeakerTopoPinDataRangesBridge[0] +}; + +//============================================================================= +static +PCPIN_DESCRIPTOR SpeakerTopoMiniportPins[] = +{ + // KSPIN_TOPO_WAVEOUT_SOURCE + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(SpeakerTopoPinDataRangePointersBridge),// DataRangesCount + SpeakerTopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_IN, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSCATEGORY_AUDIO, // Category + NULL, // Name + 0 // Reserved + } + }, + // KSPIN_TOPO_LINEOUT_DEST + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(SpeakerTopoPinDataRangePointersBridge),// DataRangesCount + SpeakerTopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_OUT, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSNODETYPE_SPEAKER, // Category + NULL, // Name + 0 // Reserved + } + } +}; + +//============================================================================= +static +KSJACK_DESCRIPTION SpeakerJackDescSpeakers = +{ + KSAUDIO_SPEAKER_STEREO, + 0xB3C98C, // Color spec for green + eConnTypeUnknown, + eGeoLocFront, + eGenLocPrimaryBox, + ePortConnIntegratedDevice, + TRUE +}; + +// Only return a KSJACK_DESCRIPTION for the physical bridge pin. +static +PKSJACK_DESCRIPTION SpeakerJackDescriptions[] = +{ + NULL, + &SpeakerJackDescSpeakers +}; + +//============================================================================= +static +PCCONNECTION_DESCRIPTOR SpeakerTopoMiniportConnections[] = +{ + // FromNode, FromPin, ToNode, ToPin + { PCFILTER_NODE, KSPIN_TOPO_WAVEOUT_SOURCE, PCFILTER_NODE, KSPIN_TOPO_LINEOUT_DEST} +}; + + +//============================================================================= +static +PCPROPERTY_ITEM PropertiesSpeakerTopoFilter[] = +{ + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_SpeakerTopoFilter + }, + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION2, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_SpeakerTopoFilter + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationSpeakerTopoFilter, PropertiesSpeakerTopoFilter); + +//============================================================================= +static +PCFILTER_DESCRIPTOR SpeakerTopoMiniportFilterDescriptor = +{ + 0, // Version + &AutomationSpeakerTopoFilter, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(SpeakerTopoMiniportPins), // PinCount + SpeakerTopoMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + 0, // NodeCount + NULL, // Nodes + SIZEOF_ARRAY(SpeakerTopoMiniportConnections), // ConnectionCount + SpeakerTopoMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories +}; + +#endif // _SYSVAD_SPEAKERTOPTABLE_H_ + diff --git a/audio/sysvad/EndpointsCommon/speakerwavtable.h b/audio/sysvad/EndpointsCommon/speakerwavtable.h new file mode 100644 index 000000000..3322b0a70 --- /dev/null +++ b/audio/sysvad/EndpointsCommon/speakerwavtable.h @@ -0,0 +1,863 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + speakerwavtable.h + +Abstract: + + Declaration of wave miniport tables for the render endpoints. + +--*/ + +#ifndef _SYSVAD_SPEAKERWAVTABLE_H_ +#define _SYSVAD_SPEAKERWAVTABLE_H_ + +#include "SysVadShared.h" + +// To keep the code simple assume device supports only 48KHz, 16-bit, stereo (PCM and NON-PCM) + +#define SPEAKER_DEVICE_MAX_CHANNELS 2 // Max Channels. + +#define SPEAKER_HOST_MAX_CHANNELS 2 // Max Channels. +#define SPEAKER_HOST_MIN_BITS_PER_SAMPLE 16 // Min Bits Per Sample +#define SPEAKER_HOST_MAX_BITS_PER_SAMPLE 16 // Max Bits Per Sample +#define SPEAKER_HOST_MIN_SAMPLE_RATE 24000 // Min Sample Rate +#define SPEAKER_HOST_MAX_SAMPLE_RATE 96000 // Max Sample Rate + +#define SPEAKER_OFFLOAD_MAX_CHANNELS 2 // Max Channels. +#define SPEAKER_OFFLOAD_MIN_BITS_PER_SAMPLE 16 // Min Bits Per Sample +#define SPEAKER_OFFLOAD_MAX_BITS_PER_SAMPLE 16 // Max Bits Per Sample +#define SPEAKER_OFFLOAD_MIN_SAMPLE_RATE 44100 // Min Sample Rate +#define SPEAKER_OFFLOAD_MAX_SAMPLE_RATE 48000 // Max Sample Rate + +#define SPEAKER_LOOPBACK_MAX_CHANNELS 2 // Max Channels. +#define SPEAKER_LOOPBACK_MIN_BITS_PER_SAMPLE 16 // Min Bits Per Sample +#define SPEAKER_LOOPBACK_MAX_BITS_PER_SAMPLE 16 // Max Bits Per Sample +#define SPEAKER_LOOPBACK_MIN_SAMPLE_RATE 24000 // Min Sample Rate +#define SPEAKER_LOOPBACK_MAX_SAMPLE_RATE 48000 // Max Sample Rate + +#define SPEAKER_DOLBY_DIGITAL_MAX_CHANNELS 2 // Max Channels. +#define SPEAKER_DOLBY_DIGITAL_MIN_BITS_PER_SAMPLE 16 // Min Bits Per Sample +#define SPEAKER_DOLBY_DIGITAL_MAX_BITS_PER_SAMPLE 16 // Max Bits Per Sample +#define SPEAKER_DOLBY_DIGITAL_MIN_SAMPLE_RATE 44100 // Min Sample Rate +#define SPEAKER_DOLBY_DIGITAL_MAX_SAMPLE_RATE 44100 // Max Sample Rate + +// +// Max # of pin instances. +// +#define SPEAKER_MAX_INPUT_SYSTEM_STREAMS 2 // Raw + Default streams +#define SPEAKER_MAX_INPUT_OFFLOAD_STREAMS MAX_INPUT_OFFLOAD_STREAMS +#define SPEAKER_MAX_OUTPUT_LOOPBACK_STREAMS MAX_OUTPUT_LOOPBACK_STREAMS + +//============================================================================= +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE SpeakerAudioEngineSupportedDeviceFormats[] = +{ + { // 0 : First entry in this table is the default format for the audio engine + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 44100, + 176400, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 1 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 24000, + 96000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 2 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 48000, + 192000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 3 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 88200, + 352800, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 4 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 96000, + 384000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE SpeakerHostPinSupportedDeviceFormats[] = +{ + { // 0 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 24000, + 96000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 1 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 32000, + 128000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 2 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 44100, + 176400, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 3 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 48000, + 192000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 4 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 88200, + 352800, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 5 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 96000, + 384000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE SpeakerOffloadPinSupportedDeviceFormats[] = +{ + { // 0 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 44100, + 176400, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 1 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 48000, + 192000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE SpeakerLoopbackPinSupportedDeviceFormats[] = +{ + { // 0 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 24000, + 96000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 1 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 32000, + 128000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 2 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 44100, + 176400, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 3 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 48000, + 192000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +// +// Supported modes (only on streaming pins). +// +static +MODE_AND_DEFAULT_FORMAT SpeakerHostPinSupportedDeviceModes[] = +{ + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_RAW, + &SpeakerHostPinSupportedDeviceFormats[3].DataFormat // 48KHz + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_DEFAULT, + &SpeakerHostPinSupportedDeviceFormats[3].DataFormat // 48KHz + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_MEDIA, + &SpeakerHostPinSupportedDeviceFormats[3].DataFormat // 48KHz + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_MOVIE, + &SpeakerHostPinSupportedDeviceFormats[3].DataFormat // 48KHz + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_COMMUNICATIONS, + &SpeakerHostPinSupportedDeviceFormats[0].DataFormat // 24KHz + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_NOTIFICATION, + &SpeakerHostPinSupportedDeviceFormats[3].DataFormat // 48KHz + } +}; + +static +MODE_AND_DEFAULT_FORMAT SpeakerOffloadPinSupportedDeviceModes[] = +{ + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_DEFAULT, + &SpeakerOffloadPinSupportedDeviceFormats[1].DataFormat // 48KHz + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_MEDIA, + &SpeakerOffloadPinSupportedDeviceFormats[1].DataFormat // 48KHz + } +}; + +// +// The entries here must follow the same order as the filter's pin +// descriptor array. +// +static +PIN_DEVICE_FORMATS_AND_MODES SpeakerPinDeviceFormatsAndModes[] = +{ + { + SystemRenderPin, + SpeakerHostPinSupportedDeviceFormats, + SIZEOF_ARRAY(SpeakerHostPinSupportedDeviceFormats), + SpeakerHostPinSupportedDeviceModes, + SIZEOF_ARRAY(SpeakerHostPinSupportedDeviceModes) + }, + { + OffloadRenderPin, + SpeakerOffloadPinSupportedDeviceFormats, + SIZEOF_ARRAY(SpeakerOffloadPinSupportedDeviceFormats), + SpeakerOffloadPinSupportedDeviceModes, + SIZEOF_ARRAY(SpeakerOffloadPinSupportedDeviceModes), + }, + { + RenderLoopbackPin, + SpeakerLoopbackPinSupportedDeviceFormats, + SIZEOF_ARRAY(SpeakerLoopbackPinSupportedDeviceFormats), + NULL, // loopback doesn't support modes. + 0 + }, + { + BridgePin, + NULL, + 0, + NULL, + 0 + }, + { + NoPin, // For convenience, offload engine device formats appended here + SpeakerAudioEngineSupportedDeviceFormats, + SIZEOF_ARRAY(SpeakerAudioEngineSupportedDeviceFormats), + NULL, // no modes for this entry. + 0 + } +}; + +//============================================================================= +static +KSDATARANGE_AUDIO SpeakerPinDataRangesStream[] = +{ + { // 0 + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + SPEAKER_HOST_MAX_CHANNELS, + SPEAKER_HOST_MIN_BITS_PER_SAMPLE, + SPEAKER_HOST_MAX_BITS_PER_SAMPLE, + SPEAKER_HOST_MIN_SAMPLE_RATE, + SPEAKER_HOST_MAX_SAMPLE_RATE + }, + { // 1 + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + SPEAKER_OFFLOAD_MAX_CHANNELS, + SPEAKER_OFFLOAD_MIN_BITS_PER_SAMPLE, + SPEAKER_OFFLOAD_MAX_BITS_PER_SAMPLE, + SPEAKER_OFFLOAD_MIN_SAMPLE_RATE, + SPEAKER_OFFLOAD_MAX_SAMPLE_RATE + }, + { // 2 + { + sizeof(KSDATARANGE_AUDIO), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + SPEAKER_LOOPBACK_MAX_CHANNELS, + SPEAKER_LOOPBACK_MIN_BITS_PER_SAMPLE, + SPEAKER_LOOPBACK_MAX_BITS_PER_SAMPLE, + SPEAKER_LOOPBACK_MIN_SAMPLE_RATE, + SPEAKER_LOOPBACK_MAX_SAMPLE_RATE + } +}; + +static +PKSDATARANGE SpeakerPinDataRangePointersStream[] = +{ + PKSDATARANGE(&SpeakerPinDataRangesStream[0]), + PKSDATARANGE(&PinDataRangeAttributeList), +}; + +static +PKSDATARANGE SpeakerPinDataRangePointersOffloadStream[] = +{ + PKSDATARANGE(&SpeakerPinDataRangesStream[1]), + PKSDATARANGE(&PinDataRangeAttributeList), + +}; + +static +PKSDATARANGE SpeakerPinDataRangePointersLoopbackStream[] = +{ + PKSDATARANGE(&SpeakerPinDataRangesStream[2]) +}; + +//============================================================================= +static +KSDATARANGE SpeakerPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +static +PKSDATARANGE SpeakerPinDataRangePointersBridge[] = +{ + &SpeakerPinDataRangesBridge[0] +}; + +//============================================================================= + +static +PCPROPERTY_ITEM PropertiesSpeakerOffloadPin[] = +{ + { + &KSPROPSETID_OffloadPin, // define new property set + KSPROPERTY_OFFLOAD_PIN_GET_STREAM_OBJECT_POINTER, // define properties + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_OffloadPin + }, + { + &KSPROPSETID_OffloadPin, // define new property set + KSPROPERTY_OFFLOAD_PIN_VERIFY_STREAM_OBJECT_POINTER, // define properties + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_OffloadPin + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationSpeakerOffloadPin, PropertiesSpeakerOffloadPin); + +//============================================================================= +static +PCPIN_DESCRIPTOR SpeakerWaveMiniportPins[] = +{ + // Wave Out Streaming Pin (Renderer) KSPIN_WAVE_RENDER_SINK_SYSTEM + { + SPEAKER_MAX_INPUT_SYSTEM_STREAMS, + SPEAKER_MAX_INPUT_SYSTEM_STREAMS, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(SpeakerPinDataRangePointersStream), + SpeakerPinDataRangePointersStream, + KSPIN_DATAFLOW_IN, + KSPIN_COMMUNICATION_SINK, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, + // Wave Out Streaming Pin (Renderer) KSPIN_WAVE_RENDER_SINK_OFFLOAD + { + SPEAKER_MAX_INPUT_OFFLOAD_STREAMS, + SPEAKER_MAX_INPUT_OFFLOAD_STREAMS, + 0, + &AutomationSpeakerOffloadPin, // AutomationTable + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(SpeakerPinDataRangePointersOffloadStream), + SpeakerPinDataRangePointersOffloadStream, + KSPIN_DATAFLOW_IN, + KSPIN_COMMUNICATION_SINK, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, + // Wave Out Streaming Pin (Renderer) KSPIN_WAVE_RENDER_SINK_LOOPBACK + { + SPEAKER_MAX_OUTPUT_LOOPBACK_STREAMS, + SPEAKER_MAX_OUTPUT_LOOPBACK_STREAMS, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(SpeakerPinDataRangePointersLoopbackStream), + SpeakerPinDataRangePointersLoopbackStream, + KSPIN_DATAFLOW_OUT, + KSPIN_COMMUNICATION_SINK, + &KSNODETYPE_AUDIO_LOOPBACK, + NULL, + 0 + } + }, + // Wave Out Bridge Pin (Renderer) KSPIN_WAVE_RENDER_SOURCE + { + 0, + 0, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(SpeakerPinDataRangePointersBridge), + SpeakerPinDataRangePointersBridge, + KSPIN_DATAFLOW_OUT, + KSPIN_COMMUNICATION_NONE, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, +}; + +//============================================================================= +static +PCNODE_DESCRIPTOR SpeakerWaveMiniportNodes[] = +{ + // KSNODE_WAVE_AUDIO_ENGINE + { + 0, // Flags + NULL, // AutomationTable + &KSNODETYPE_AUDIO_ENGINE, // Type KSNODETYPE_AUDIO_ENGINE + NULL // Name + } +}; +//============================================================================= +// +// ---------------------------- +// | | +// System Pin 0-->| |--> 2 Loopback Pin +// | HW Audio Engine node | +// Offload Pin 1-->| |--> 3 KSPIN_WAVE_RENDER_SOURCE +// | | +// ---------------------------- +static +PCCONNECTION_DESCRIPTOR SpeakerWaveMiniportConnections[] = +{ + { PCFILTER_NODE, KSPIN_WAVE_RENDER_SINK_SYSTEM, KSNODE_WAVE_AUDIO_ENGINE, 1 }, + { PCFILTER_NODE, KSPIN_WAVE_RENDER_SINK_OFFLOAD, KSNODE_WAVE_AUDIO_ENGINE, 2 }, + { KSNODE_WAVE_AUDIO_ENGINE, 3, PCFILTER_NODE, KSPIN_WAVE_RENDER_SINK_LOOPBACK }, + { KSNODE_WAVE_AUDIO_ENGINE, 0, PCFILTER_NODE, KSPIN_WAVE_RENDER_SOURCE }, +}; + +//============================================================================= +static +PCPROPERTY_ITEM PropertiesSpeakerWaveFilter[] = +{ + { + &KSPROPSETID_Pin, + KSPROPERTY_PIN_PROPOSEDATAFORMAT, + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_WaveFilter + }, + { + &KSPROPSETID_SysVAD, + KSPROPERTY_SYSVAD_DEFAULTSTREAMEFFECTS, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_WaveFilter + } + +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationSpeakerWaveFilter, PropertiesSpeakerWaveFilter); + +//============================================================================= +static +PCFILTER_DESCRIPTOR SpeakerWaveMiniportFilterDescriptor = +{ + 0, // Version + &AutomationSpeakerWaveFilter, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(SpeakerWaveMiniportPins), // PinCount + SpeakerWaveMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + SIZEOF_ARRAY(SpeakerWaveMiniportNodes), // NodeCount + SpeakerWaveMiniportNodes, // Nodes + SIZEOF_ARRAY(SpeakerWaveMiniportConnections), // ConnectionCount + SpeakerWaveMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories - use defaults (audio, render, capture) +}; + +#endif // _SYSVAD_SPEAKERWAVTABLE_H_ + diff --git a/audio/sysvad/KeywordDetectorAdapter/KeywordDetectorContosoAdapter.cpp b/audio/sysvad/KeywordDetectorAdapter/KeywordDetectorContosoAdapter.cpp new file mode 100644 index 000000000..9bcdedee4 --- /dev/null +++ b/audio/sysvad/KeywordDetectorAdapter/KeywordDetectorContosoAdapter.cpp @@ -0,0 +1,171 @@ +// KeywordDetectorContosoAdapter.cpp : Defines the exported functions for the DLL application. +// + +// ISSUE-2015/1/14-frankye review all error codes + +#include "stdafx.h" + +#include +#include + +#include +#include "KeywordDetectorContosoAdapter.h" +#include "ContosoKeywordDetector.h" +#include + +using namespace Microsoft::WRL; + +class KeywordDetectorContosoAdapter : public RuntimeClass, IKeywordDetectorOemAdapter> +{ +public: + KeywordDetectorContosoAdapter() + { + } + + STDMETHODIMP KeywordDetectorContosoAdapter::GetCapabilities( + BOOL* SupportsUserModels, + KEYWORDID** KeywordIds, + ULONG* NumKeywords, + LANGID** LangIds, + ULONG* NumLanguages, + IMFMediaType* *ppMediaType) + { + HRESULT hr; + ComPtr spMFType; + const WAVEFORMATEX waveFormat = { WAVE_FORMAT_PCM, 1, 16000, 32000, 2, 16, 0 }; + + *SupportsUserModels = FALSE; + + *KeywordIds = (KEYWORDID *)CoTaskMemAlloc(sizeof(KEYWORDID)); + if (*KeywordIds == nullptr) + { + return E_OUTOFMEMORY; + } + **KeywordIds = KwVoiceAssistant; + *NumKeywords = 1; + *LangIds = (LANGID *)CoTaskMemAlloc(sizeof(LANGID)); + if (*LangIds == nullptr) + { + CoTaskMemFree(*KeywordIds); + return E_OUTOFMEMORY; + } + **LangIds = 0x409; + *NumLanguages = 1; + + hr = MFCreateMediaType(spMFType.GetAddressOf()); + if (SUCCEEDED(hr)) + { + hr = MFInitMediaTypeFromWaveFormatEx(spMFType.Get(), &waveFormat, sizeof(waveFormat)); + if (SUCCEEDED(hr)) + { + spMFType.CopyTo(ppMediaType); + hr = S_OK; + } + } + + return hr; + } + + STDMETHODIMP VerifyUserKeyword( + IStream* ModelData, + KEYWORDID KeywordId, + LANGID LangId, + LONG KeywordEndBytePos, + IMFMediaBuffer* UserRecording) + { + UNREFERENCED_PARAMETER(ModelData); + UNREFERENCED_PARAMETER(KeywordId); + UNREFERENCED_PARAMETER(LangId); + UNREFERENCED_PARAMETER(KeywordEndBytePos); + UNREFERENCED_PARAMETER(UserRecording); + + return E_NOTIMPL; + } + + STDMETHODIMP ComputeAndAddUserModelData( + IStream* ModelData, + KEYWORDSELECTOR KeywordSelector, + LONG *KeywordEndBytePos, + IMFMediaBuffer **UserRecordings, + ULONG NumUserRecordings) + { + UNREFERENCED_PARAMETER(ModelData); + UNREFERENCED_PARAMETER(KeywordSelector); + UNREFERENCED_PARAMETER(KeywordEndBytePos); + UNREFERENCED_PARAMETER(UserRecordings); + UNREFERENCED_PARAMETER(NumUserRecordings); + + return E_NOTIMPL; + } + + STDMETHODIMP BuildArmingPatternData( + IStream *ModelData, + KEYWORDSELECTOR *KeywordSelectors, + ULONG NumKeywordSelectors, + SOUNDDETECTOR_PATTERNHEADER **ppPatternData) + { + CONTOSO_KEYWORDCONFIGURATION *pPatternData = nullptr; + + UNREFERENCED_PARAMETER(ModelData); + + if (NumKeywordSelectors > 1) + { + return E_INVALIDARG; + } + if ((KeywordSelectors[0].KeywordId != KwVoiceAssistant) || + (KeywordSelectors[0].LangId != 0x0409)) + { + return E_INVALIDARG; + } + + pPatternData = (CONTOSO_KEYWORDCONFIGURATION*)CoTaskMemAlloc(sizeof(CONTOSO_KEYWORDCONFIGURATION)); + if (pPatternData == nullptr) + { + return E_OUTOFMEMORY; + } + + pPatternData->Header.Size = sizeof(*pPatternData); + pPatternData->Header.PatternType = CONTOSO_KEYWORDCONFIGURATION_IDENTIFIER; + pPatternData->ContosoDetectorConfigurationData = 0x12345678; + + *ppPatternData = &pPatternData->Header; + pPatternData = nullptr; + + return S_OK; + } + + STDMETHODIMP ParseDetectionResultData( + IStream *ModelData, + SOUNDDETECTOR_PATTERNHEADER *Result, // ISSUE-2015/1/14-frankye should be const + KEYWORDID* KeywordId, + LANGID* LangId, + BOOL *pIsUserMatch, + ULONG64 *KeywordStartPerformanceCounterValue, + ULONG64 *KeywordEndPerformanceCounterValue) + { + const CONTOSO_KEYWORDDETECTIONRESULT *contosoResult; + + UNREFERENCED_PARAMETER(ModelData); + + if (Result->PatternType != CONTOSO_KEYWORDCONFIGURATION_IDENTIFIER || Result->Size < sizeof(CONTOSO_KEYWORDDETECTIONRESULT)) + { + return E_INVALIDARG; + } + + contosoResult = (CONTOSO_KEYWORDDETECTIONRESULT*)Result; + + // Do something with the result data to determine return parameters + // PostProcessResult(contosoResult->ContosoDetectorResultData); + + *KeywordId = KwVoiceAssistant; + *LangId = 0x0409; + *pIsUserMatch = FALSE; + + *KeywordStartPerformanceCounterValue = 0; + *KeywordEndPerformanceCounterValue = 0; + + return S_OK; + } +}; + +CoCreatableClass(KeywordDetectorContosoAdapter); diff --git a/audio/sysvad/KeywordDetectorAdapter/KeywordDetectorContosoAdapter.def b/audio/sysvad/KeywordDetectorAdapter/KeywordDetectorContosoAdapter.def new file mode 100644 index 000000000..ddeb3eac1 --- /dev/null +++ b/audio/sysvad/KeywordDetectorAdapter/KeywordDetectorContosoAdapter.def @@ -0,0 +1,6 @@ +LIBRARY + +EXPORTS + DllGetActivationFactory PRIVATE + DllGetClassObject PRIVATE + DllCanUnloadNow PRIVATE diff --git a/audio/sysvad/KeywordDetectorAdapter/KeywordDetectorContosoAdapter.idl b/audio/sysvad/KeywordDetectorAdapter/KeywordDetectorContosoAdapter.idl new file mode 100644 index 000000000..47431ce71 --- /dev/null +++ b/audio/sysvad/KeywordDetectorAdapter/KeywordDetectorContosoAdapter.idl @@ -0,0 +1,16 @@ +import "oaidl.idl"; +import "ocidl.idl"; + +import "KeywordDetectorOemAdapter.idl"; + +[uuid(9D3E6826-CB8E-4D86-8B14-89F0D7EFCD01), version(1.0)] +library KeywordDetectorContosoAdapterLib +{ + // The class's uuid (i.e. the COM CLSID) must match that of the pattern + // type GUID returned by the audio driver + [uuid(6F7DBCC1-202E-498D-99C5-61C36C4EB2DC), version(1.0)] + coclass KeywordDetectorContosoAdapter + { + [default] interface IKeywordDetectorOemAdapter; + } +}; diff --git a/audio/sysvad/KeywordDetectorAdapter/KeywordDetectorContosoAdapter.rc b/audio/sysvad/KeywordDetectorAdapter/KeywordDetectorContosoAdapter.rc new file mode 100644 index 000000000..6ce780220 --- /dev/null +++ b/audio/sysvad/KeywordDetectorAdapter/KeywordDetectorContosoAdapter.rc @@ -0,0 +1,13 @@ +#if 0 +Copyright (c) Microsoft Corporation. All Rights Reserved +#endif + +#include "winres.h" +#include +#define VER_FILETYPE VFT_DLL +#define VER_FILESUBTYPE VFT_UNKNOWN +#define VER_FILEDESCRIPTION_STR "Contoso keyword detector adapter" +#define VER_INTERNALNAME_STR "KeywordDetectorContosoAdapter" +#define VER_ORIGINALFILENAME_STR "KeywordDetectorContosoAdapter.dll" +#include + \ No newline at end of file diff --git a/audio/sysvad/KeywordDetectorAdapter/KeywordDetectorContosoAdapter.vcxproj b/audio/sysvad/KeywordDetectorAdapter/KeywordDetectorContosoAdapter.vcxproj new file mode 100644 index 000000000..29fa6d041 --- /dev/null +++ b/audio/sysvad/KeywordDetectorAdapter/KeywordDetectorContosoAdapter.vcxproj @@ -0,0 +1,232 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {92E28772-ADE3-4367-B679-6DCF6FFA75F1} + $(MSBuildProjectName) + false + true + Debug + Win32 + {B1EE584B-A24D-4186-9759-A08E50D9F301} + + + + 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) + + + + + + + + + + + + + + + + KeywordDetectorContosoAdapter + + + KeywordDetectorContosoAdapter + + + KeywordDetectorContosoAdapter + + + KeywordDetectorContosoAdapter + + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + MultiThreaded + MultiThreadedDebug + true + Level4 + %(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\ + + + + + %(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\ + + + %(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\ + + + %(AdditionalDependencies);Kernel32.lib;ole32.lib;oleaut32.lib;advapi32.lib;user32.lib;uuid.lib;mfplat.lib;runtimeobject.lib + KeywordDetectorContosoAdapter.def + + + + + MultiThreaded + MultiThreadedDebug + true + Level4 + %(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\ + + + + + %(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\ + + + %(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\ + + + %(AdditionalDependencies);Kernel32.lib;ole32.lib;oleaut32.lib;advapi32.lib;user32.lib;uuid.lib;mfplat.lib;runtimeobject.lib + KeywordDetectorContosoAdapter.def + + + + + MultiThreaded + MultiThreadedDebug + true + Level4 + %(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\ + + + + + %(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\ + + + %(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\ + + + %(AdditionalDependencies);Kernel32.lib;ole32.lib;oleaut32.lib;advapi32.lib;user32.lib;uuid.lib;mfplat.lib;runtimeobject.lib + KeywordDetectorContosoAdapter.def + + + + + MultiThreaded + MultiThreadedDebug + true + Level4 + %(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\ + + + + + %(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\ + + + %(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\ + + + %(AdditionalDependencies);Kernel32.lib;ole32.lib;oleaut32.lib;advapi32.lib;user32.lib;uuid.lib;mfplat.lib;runtimeobject.lib + KeywordDetectorContosoAdapter.def + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/audio/sysvad/KeywordDetectorAdapter/KeywordDetectorContosoAdapter.vcxproj.Filters b/audio/sysvad/KeywordDetectorAdapter/KeywordDetectorContosoAdapter.vcxproj.Filters new file mode 100644 index 000000000..470f34024 --- /dev/null +++ b/audio/sysvad/KeywordDetectorAdapter/KeywordDetectorContosoAdapter.vcxproj.Filters @@ -0,0 +1,39 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {B50B7E60-1411-4963-BE4B-B214F230A656} + + + h;hpp;hxx;hm;inl;inc;xsd + {0BDF6F65-E222-4DE0-B90F-7DE3C57089E8} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {F589D76A-014F-48A8-A294-56C98193C5A1} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/audio/sysvad/KeywordDetectorAdapter/dllmain.cpp b/audio/sysvad/KeywordDetectorAdapter/dllmain.cpp new file mode 100644 index 000000000..c29f44648 --- /dev/null +++ b/audio/sysvad/KeywordDetectorAdapter/dllmain.cpp @@ -0,0 +1,33 @@ +// dllmain.cpp : Defines the entry point for the DLL application. +#include "stdafx.h" +#include + +using namespace Microsoft::WRL; + +#if !defined(__WRL_CLASSIC_COM__) +STDAPI DllGetActivationFactory(_In_ HSTRING activatibleClassId, _COM_Outptr_ IActivationFactory** factory) +{ + return Module::GetModule().GetActivationFactory(activatibleClassId, factory); +} +#endif + +#if !defined(__WRL_WINRT_STRICT__) +STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, _COM_Outptr_ void** ppv) +{ + return Module::GetModule().GetClassObject(rclsid, riid, ppv); +} +#endif + +STDAPI DllCanUnloadNow() +{ + return Module::GetModule().Terminate() ? S_OK : S_FALSE; +} + +STDAPI_(BOOL) DllMain(_In_opt_ HINSTANCE hinst, DWORD reason, _In_opt_ void*) +{ + if (reason == DLL_PROCESS_ATTACH) + { + DisableThreadLibraryCalls(hinst); + } + return TRUE; +} diff --git a/audio/sysvad/KeywordDetectorAdapter/stdafx.cpp b/audio/sysvad/KeywordDetectorAdapter/stdafx.cpp new file mode 100644 index 000000000..3c8b5956d --- /dev/null +++ b/audio/sysvad/KeywordDetectorAdapter/stdafx.cpp @@ -0,0 +1,8 @@ +// stdafx.cpp : source file that includes just the standard includes +// KeywordDetectorContosoAdapter.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +// TODO: reference any additional headers you need in STDAFX.H +// and not in this file diff --git a/audio/sysvad/KeywordDetectorAdapter/stdafx.h b/audio/sysvad/KeywordDetectorAdapter/stdafx.h new file mode 100644 index 000000000..b8ae2aec1 --- /dev/null +++ b/audio/sysvad/KeywordDetectorAdapter/stdafx.h @@ -0,0 +1,16 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#pragma once + +#include "targetver.h" + +//#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +// Windows Header Files: +#include + + + +// TODO: reference additional headers your program requires here diff --git a/audio/sysvad/KeywordDetectorAdapter/targetver.h b/audio/sysvad/KeywordDetectorAdapter/targetver.h new file mode 100644 index 000000000..87c0086de --- /dev/null +++ b/audio/sysvad/KeywordDetectorAdapter/targetver.h @@ -0,0 +1,8 @@ +#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/audio/sysvad/Package/package.VcxProj b/audio/sysvad/Package/package.VcxProj new file mode 100644 index 000000000..7bd46cd43 --- /dev/null +++ b/audio/sysvad/Package/package.VcxProj @@ -0,0 +1,96 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + + {92E28772-ADE3-4367-B679-6DCF6FFA75F1} + + + {43FF11E5-B4C4-409A-AE4B-13342918B6F8} + + + {2A1B7375-D9CB-4067-AEDE-180D75B5934D} + + + {76DBA957-38F6-47ED-A5C0-AD587D08FF12} + + + {29A5DD25-AB56-4DEE-96AB-21EC2926A300} + + + + WindowsKernelModeDriver10.0 + Utility + Package + true + Debug + + + + {5F7004D7-03BD-44AA-9FDD-6677B2C93469} + {24733FBE-11F1-4558-97A6-983B6BB36797} + $(MSBuildProjectName) + + + Windows10 + true + + + Windows10 + false + + + Windows10 + true + + + Windows10 + false + + + + + + + + + + + DbgengKernelDebugger + False + None + + + + + + %PathToInf% + False + False + True + + 133563 + + + + + + + \ No newline at end of file diff --git a/audio/sysvad/Package/package.VcxProj.Filters b/audio/sysvad/Package/package.VcxProj.Filters new file mode 100644 index 000000000..6f0874bdf --- /dev/null +++ b/audio/sysvad/Package/package.VcxProj.Filters @@ -0,0 +1,21 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {64F96D78-EC33-4578-89CD-4D57E4661BB1} + + + h;hpp;hxx;hm;inl;inc;xsd + {D77D2DFB-9D29-470D-8D22-893952C6C47D} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {3C34DD80-8AEE-4672-BEF4-BF6971E63C85} + + + inf;inv;inx;mof;mc; + {1028BDCB-C3D9-4F31-845E-AA1B26F6E4BF} + + + \ No newline at end of file diff --git a/audio/sysvad/PhoneAudioSample/CellularWave.cpp b/audio/sysvad/PhoneAudioSample/CellularWave.cpp new file mode 100644 index 000000000..1a999c1e3 --- /dev/null +++ b/audio/sysvad/PhoneAudioSample/CellularWave.cpp @@ -0,0 +1,685 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + cellularwave.cpp + +Abstract: + + Implementation of wavert miniport for cellular + +--*/ + +#pragma warning (disable : 4127) + +#include +#include "simple.h" +#include "CellularWave.h" +#include "CellularTopo.h" +#include "CellularToptable.h" + +//============================================================================= +// CCellularMiniportWaveRT +//============================================================================= + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CreateCellularMiniportWaveRT +( + _Out_ PUNKNOWN * Unknown, + _In_ REFCLSID, + _In_opt_ PUNKNOWN UnknownOuter, + _When_((PoolType & NonPagedPoolMustSucceed) != 0, + __drv_reportError("Must succeed pool allocations are forbidden. " + "Allocation failures cause a system crash")) + _In_ POOL_TYPE PoolType, + _In_ PUNKNOWN UnknownAdapter, + _In_opt_ PVOID DeviceContext, + _In_ PENDPOINT_MINIPAIR MiniportPair +) +/*++ + +Routine Description: + + Create the wavert miniport. + +Arguments: + + Unknown - + + RefClsId - + + UnknownOuter - + + PoolType - + + UnkownAdapter - + + DeviceContext - + + MiniportPair - + +Return Value: + + NT status code. + +--*/ +{ + UNREFERENCED_PARAMETER(UnknownOuter); + + PAGED_CODE(); + + ASSERT(Unknown); + ASSERT(MiniportPair); + + CMiniportWaveRT *obj = new (PoolType, MINWAVERT_POOLTAG) CCellularMiniportWaveRT + ( + UnknownAdapter, + MiniportPair, + DeviceContext + ); + if (NULL == obj) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + obj->AddRef(); + *Unknown = reinterpret_cast(obj); + + return STATUS_SUCCESS; +} + +//============================================================================= +#pragma code_seg("PAGE") +CCellularMiniportWaveRT::~CCellularMiniportWaveRT +( + void +) +/*++ + +Routine Description: + + Destructor for wavert miniport + +Arguments: + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + DPF_ENTER(("[CCellularMiniportWaveRT::~CCellularMiniportWaveRT]")); + +} // ~CCellularMiniportWaveRT + + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP +CCellularMiniportWaveRT::NonDelegatingQueryInterface +( + _In_ REFIID Interface, + _COM_Outptr_ PVOID * Object +) +/*++ + +Routine Description: + + QueryInterface for CCellularMiniportWaveRT + +Arguments: + + Interface - GUID of the interface + + Object - interface object to be returned. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(Object); + + if (IsEqualGUIDAligned(Interface, IID_IAdapterPowerManagement)) + { + *Object = PVOID(PADAPTERPOWERMANAGEMENT(this)); + } + else + { + *Object = NULL; + } + + if (*Object) + { + // We reference the interface for the caller. + + PUNKNOWN(*Object)->AddRef(); + return STATUS_SUCCESS; + } + + return CMiniportWaveRT::NonDelegatingQueryInterface(Interface, Object); +} + +#pragma code_seg("PAGE") +NTSTATUS +PropertyHandler_CellularWaveFilter +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Redirects general property request to miniport object + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + CCellularMiniportWaveRT* pWaveHelper = reinterpret_cast(PropertyRequest->MajorTarget); + + if (pWaveHelper == NULL) + { + return STATUS_INVALID_PARAMETER; + } + + pWaveHelper->AddRef(); + + if (pWaveHelper->m_DeviceType == eCellularDevice && + IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_TelephonyControl)) + { + switch (PropertyRequest->PropertyItem->Id) + { + case KSPROPERTY_TELEPHONY_PROVIDERID: + ntStatus = pWaveHelper->PropertyHandler_TelephonyProviderId(PropertyRequest); + break; + + case KSPROPERTY_TELEPHONY_CALLINFO: + ntStatus = pWaveHelper->PropertyHandler_TelephonyCallInfo(PropertyRequest); + break; + + case KSPROPERTY_TELEPHONY_CALLCONTROL: + case KSPROPERTY_TELEPHONY_PROVIDERCHANGE: + ntStatus = pWaveHelper->PropertyHandler_TelephonyCallState(PropertyRequest); + break; + + case KSPROPERTY_TELEPHONY_CALLHOLD: + ntStatus = pWaveHelper->PropertyHandler_TelephonyCallHold(PropertyRequest); + break; + + case KSPROPERTY_TELEPHONY_MUTE_TX: + ntStatus = pWaveHelper->PropertyHandler_TelephonyMuteTx(PropertyRequest); + break; + + default: + DPF(D_TERSE, ("[PropertyHandler_CellularWaveFilter: Invalid Device Request]")); + } + } + else + { + ntStatus = PropertyHandler_WaveFilter(PropertyRequest); + } + + pWaveHelper->Release(); + + return ntStatus; +} // PropertyHandler_CellularWaveFilter + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CCellularMiniportWaveRT::PropertyHandler_TelephonyProviderId +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +{ + NTSTATUS ntStatus = STATUS_INVALID_PARAMETER; + + PAGED_CODE(); + + DPF_ENTER(("[CCellularMiniportWaveRT::PropertyHandler_TelephonyProviderId]")); + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ULONG flags = PropertyRequest->PropertyItem->Flags; + ntStatus = PropertyHandler_BasicSupport(PropertyRequest, flags, VT_ILLEGAL); + } + else + { + ULONG cbMinSize = sizeof(ULONG); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbMinSize; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbMinSize) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + ULONG* providerId = (ULONG*)PropertyRequest->Value; + // Return supported TelephonyProviderId (executor id). + *providerId = m_TelephonyProviderId; + ntStatus = STATUS_SUCCESS; + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + } + } + + return ntStatus; +} // PropertyHandler_TelephonyProviderId + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CCellularMiniportWaveRT::PropertyHandler_TelephonyCallInfo +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +{ + NTSTATUS ntStatus = STATUS_INVALID_PARAMETER; + + PAGED_CODE(); + + DPF_ENTER(("[CCellularMiniportWaveRT::PropertyHandler_TelephonyCallInfo]")); + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ULONG flags = PropertyRequest->PropertyItem->Flags; + ntStatus = PropertyHandler_BasicSupport(PropertyRequest, flags, VT_ILLEGAL); + } + else + { + ULONG cbMinSize = sizeof(KSTELEPHONY_CALLINFO); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbMinSize; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbMinSize) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PKSTELEPHONY_CALLINFO TelephonyCallInfo = static_cast(PropertyRequest->Value); + // Return current CallType and CallState information. CallType information is not valid when the CallState is disabled. + TelephonyCallInfo->CallType = m_TelephonyCallType; + TelephonyCallInfo->CallState = m_TelephonyCallState; + ntStatus = STATUS_SUCCESS; + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + } + } + + return ntStatus; +} // PropertyHandler_TelephonyCallInfo + +#pragma code_seg("PAGE") +NTSTATUS +CCellularMiniportWaveRT::UpdateTopologyJackState +( + _In_ ULONG TelephonyId, + _In_ BOOL NewState +) +{ + PAGED_CODE (); + NTSTATUS ntStatus = STATUS_SUCCESS; + IUnknown *pUnknown = NULL; + ICellularTopology *pCellularTopology = NULL; + + // GetFilters returns the topology port, then topology miniport, then wave port, then wave miniport. + // we only need the topology miniport. + ntStatus = m_pAdapterCommon->GetFilters(m_pMiniportPair, NULL, &pUnknown, NULL, NULL); + if (NT_SUCCESS(ntStatus)) + { + ntStatus = pUnknown->QueryInterface(IID_ICellularTopology, (PVOID *)&pCellularTopology); + if (NT_SUCCESS(ntStatus)) + { + ntStatus = pCellularTopology->UpdateTopologyJackState(TelephonyId, NewState); + } + } + + SAFE_RELEASE(pCellularTopology); + SAFE_RELEASE(pUnknown); + + return STATUS_SUCCESS; +} +#pragma code_seg() + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CCellularMiniportWaveRT::PropertyHandler_TelephonyCallState +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +{ + NTSTATUS ntStatus = STATUS_INVALID_PARAMETER; + + PAGED_CODE(); + + DPF_ENTER(("[CCellularMiniportWaveRT::PropertyHandler_TelephonyCallState]")); + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ULONG flags = PropertyRequest->PropertyItem->Flags; + ntStatus = PropertyHandler_BasicSupport(PropertyRequest, flags, VT_ILLEGAL); + } + else + { + ULONG cbMinSize = 0; + + if (PropertyRequest->PropertyItem->Id == KSPROPERTY_TELEPHONY_CALLCONTROL) + { + cbMinSize = sizeof(KSTELEPHONY_CALLCONTROL); + } + else if (PropertyRequest->PropertyItem->Id == KSPROPERTY_TELEPHONY_PROVIDERCHANGE) + { + cbMinSize = sizeof(KSTELEPHONY_PROVIDERCHANGE); + } + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbMinSize; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbMinSize) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_SET) + { + if (PropertyRequest->PropertyItem->Id == KSPROPERTY_TELEPHONY_CALLCONTROL) + { + PKSTELEPHONY_CALLCONTROL TelephonyCallControl = static_cast(PropertyRequest->Value); + + // KSPROPERTY_TELEPHONY_CALLCONTROL has info about CallType and CallControlOp. TELEPHONY_CALLCONTROLOP_ENABLE will start + // cellular call from audio driver perspective, update jack state for associated cellular bidi endpoint to active, + // save call type and update the call state to Enabled. TELEPHONY_CALLCONTROLOP_DISABLE will terminate cellular call + // from audio driver perspective, update jack state for associated cellular bidi endpoint to unplugged and update + // call state to Disabled. Call type is ignored in this case. + switch (TelephonyCallControl->CallControlOp) + { + case TELEPHONY_CALLCONTROLOP_DISABLE: + ASSERT(m_TelephonyCallState != TELEPHONY_CALLSTATE_DISABLED); + m_TelephonyCallState = TELEPHONY_CALLSTATE_DISABLED; + UpdateTopologyJackState(m_TelephonyProviderId, FALSE); + // Release our idle power management requirement + m_pAdapterCommon->SetIdlePowerManagement(m_pMiniportPair, TRUE); + ntStatus = STATUS_SUCCESS; + break; + + case TELEPHONY_CALLCONTROLOP_ENABLE: + ASSERT(m_TelephonyCallState != TELEPHONY_CALLSTATE_ENABLED); + m_TelephonyCallType = TelephonyCallControl->CallType; + m_TelephonyCallState = TELEPHONY_CALLSTATE_ENABLED; + UpdateTopologyJackState(m_TelephonyProviderId, TRUE); + // When we're in a call, we need idle power management disabled. + m_pAdapterCommon->SetIdlePowerManagement(m_pMiniportPair, FALSE); + ntStatus = STATUS_SUCCESS; + break; + + default: + ntStatus = STATUS_INVALID_PARAMETER; + break; + } + } + else if (PropertyRequest->PropertyItem->Id == KSPROPERTY_TELEPHONY_PROVIDERCHANGE) + { + PKSTELEPHONY_PROVIDERCHANGE TelephonyProviderChange = static_cast(PropertyRequest->Value); + + // KSPROPERTY_TELEPHONY_PROVIDERCHANGE has info about CallType and ProviderChangeOp. TELEPHONY_PROVIDERCHANGEOP_BEGIN will indicate + // start of SRVCC to audio driver. Audio driver will then start the SRVCC and update the call state to ProviderTransition. + // TELEPHONY_PROVIDERCHANGEOP_END will indicate end of SRVCC to audio driver and driver will end SRVCC process and update + // call state to Enabled. TELEPHONY_PROVIDERCHANGEOP_CANCEL will indicate SRVCC was aborted and audio driver will revert back to + // pre SRVCC call. + switch (TelephonyProviderChange->ProviderChangeOp) + { + case TELEPHONY_PROVIDERCHANGEOP_END: + m_TelephonyCallState = TELEPHONY_CALLSTATE_ENABLED; + ntStatus = STATUS_SUCCESS; + break; + + case TELEPHONY_PROVIDERCHANGEOP_BEGIN: + m_PreviousTelephonyCallType = m_TelephonyCallType; + m_TelephonyCallType = TelephonyProviderChange->CallType; + m_TelephonyCallState = TELEPHONY_CALLSTATE_PROVIDERTRANSITION; + ntStatus = STATUS_SUCCESS; + break; + + case TELEPHONY_PROVIDERCHANGEOP_CANCEL: + m_TelephonyCallType = m_PreviousTelephonyCallType; + m_TelephonyCallState = TELEPHONY_CALLSTATE_ENABLED; + ntStatus = STATUS_SUCCESS; + break; + + default: + ntStatus = STATUS_INVALID_PARAMETER; + break; + } + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + } + } + + return ntStatus; +} // PropertyHandler_TelephonyCallState + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CCellularMiniportWaveRT::PropertyHandler_TelephonyCallHold +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +{ + NTSTATUS ntStatus = STATUS_INVALID_PARAMETER; + + PAGED_CODE(); + + DPF_ENTER(("[CCellularMiniportWaveRT::PropertyHandler_TelephonyCallHold]")); + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ULONG flags = PropertyRequest->PropertyItem->Flags; + ntStatus = PropertyHandler_BasicSupport(PropertyRequest, flags, VT_ILLEGAL); + } + else + { + ULONG cbMinSize = sizeof(BOOL); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbMinSize; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbMinSize) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + BOOL* bTelephonyHold = (BOOL*) PropertyRequest->Value; + // Return if the call is on hold or not + *bTelephonyHold = m_bTelephonyHold; + ntStatus = STATUS_SUCCESS; + } + else if (PropertyRequest->Verb & KSPROPERTY_TYPE_SET) + { + BOOL* bTelephonyHold = (BOOL*) PropertyRequest->Value; + m_bTelephonyHold = *bTelephonyHold; + + // If this property is TRUE, put the call on hold and update the call state to hold. + // If this property is FALSE, take the call out of hold and update the call state to enabled. + m_TelephonyCallState = (*bTelephonyHold) ? TELEPHONY_CALLSTATE_HOLD : TELEPHONY_CALLSTATE_ENABLED; + ntStatus = STATUS_SUCCESS; + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + } + } + + return ntStatus; +} // PropertyHandler_TelephonyCallHold + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CCellularMiniportWaveRT::PropertyHandler_TelephonyMuteTx +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +{ + NTSTATUS ntStatus = STATUS_INVALID_PARAMETER; + + PAGED_CODE(); + + DPF_ENTER(("[CCellularMiniportWaveRT::PropertyHandler_TelephonyMuteTx]")); + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ULONG flags = PropertyRequest->PropertyItem->Flags; + ntStatus = PropertyHandler_BasicSupport(PropertyRequest, flags, VT_ILLEGAL); + } + else + { + ULONG cbMinSize = sizeof(BOOL); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbMinSize; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbMinSize) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + BOOL* bTxMute = (BOOL*) PropertyRequest->Value; + // Return if the cellular Tx is on muted or not + *bTxMute = m_bTelephonyMuteTx; + ntStatus = STATUS_SUCCESS; + } + else if (PropertyRequest->Verb & KSPROPERTY_TYPE_SET) + { + BOOL* bTxMute = (BOOL*) PropertyRequest->Value; + // If this property is TRUE, mute cellular Tx data. However, it should not mute the data that was injected to KSNODETYPE_TELEPHONY_TX. + // If this property is FALSE, unmute cellular Tx data. + m_bTelephonyMuteTx = *bTxMute; + ntStatus = STATUS_SUCCESS; + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + } + } + + return ntStatus; +} // PropertyHandler_TelephonyMuteTx + +//============================================================================= +#pragma code_seg() +STDMETHODIMP_(void) +CCellularMiniportWaveRT::PowerChangeState +( + _In_ POWER_STATE NewState +) +{ + DPF_ENTER(("[CCellularMiniportWaveRT::PowerChangeState]")); + UNREFERENCED_PARAMETER(NewState); + + /* + Turn on and off cellular specific hardware here, if applicable. + */ + +} // PowerStateChange + +//============================================================================= +#pragma code_seg() +STDMETHODIMP_(NTSTATUS) +CCellularMiniportWaveRT::QueryDeviceCapabilities +( + _Inout_updates_bytes_(sizeof(DEVICE_CAPABILITIES)) PDEVICE_CAPABILITIES PowerDeviceCaps +) +{ + UNREFERENCED_PARAMETER(PowerDeviceCaps); + + /* + Unused at the miniport layer. Any device capabilities should be reported in CAdapterCommon + */ + + return (STATUS_SUCCESS); +} // QueryDeviceCapabilities + +//============================================================================= +#pragma code_seg() +STDMETHODIMP_(NTSTATUS) +CCellularMiniportWaveRT::QueryPowerChangeState +( + _In_ POWER_STATE NewStateQuery +) +{ + UNREFERENCED_PARAMETER(NewStateQuery); + + DPF_ENTER(("[CCellularMiniportWaveRT::QueryPowerChangeState]")); + + // if we're in a call state other than disabled, then we need the hardware turned on. + if (TELEPHONY_CALLSTATE_DISABLED != m_TelephonyCallState) + { + return STATUS_RESOURCE_IN_USE; + } + + return STATUS_SUCCESS; +} // QueryPowerChangeState + + diff --git a/audio/sysvad/PhoneAudioSample/CellularWave.h b/audio/sysvad/PhoneAudioSample/CellularWave.h new file mode 100644 index 000000000..1704ff0ce --- /dev/null +++ b/audio/sysvad/PhoneAudioSample/CellularWave.h @@ -0,0 +1,126 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + cellularwave.h + +Abstract: + + Definition of wavert miniport class for cellular + +--*/ + +#include "MinWaveRT.h" + +#ifndef _SYSVAD_CELLULARMINWAVERT_H_ +#define _SYSVAD_CELLULARMINWAVERT_H_ + +NTSTATUS +CreateCellularMiniportWaveRT +( + _Out_ PUNKNOWN * Unknown, + _In_ REFCLSID, + _In_opt_ PUNKNOWN UnknownOuter, + _When_((PoolType & NonPagedPoolMustSucceed) != 0, + __drv_reportError("Must succeed pool allocations are forbidden. " + "Allocation failures cause a system crash")) + _In_ POOL_TYPE PoolType, + _In_ PUNKNOWN UnknownAdapter, + _In_opt_ PVOID DeviceContext, + _In_ PENDPOINT_MINIPAIR MiniportPair +); + +//============================================================================= +// Classes +//============================================================================= +/////////////////////////////////////////////////////////////////////////////// +// CCellularMiniportWaveRT +// +class CCellularMiniportWaveRT : + public CMiniportWaveRT, + public IAdapterPowerManagement +{ +private: + ULONG m_TelephonyProviderId; + TELEPHONY_CALLTYPE m_TelephonyCallType; + TELEPHONY_CALLTYPE m_PreviousTelephonyCallType; + TELEPHONY_CALLSTATE m_TelephonyCallState; + BOOL m_bTelephonyHold; + BOOL m_bTelephonyMuteTx; + +public: + DECLARE_STD_UNKNOWN(); + IMP_IAdapterPowerManagement; + + CCellularMiniportWaveRT( + _In_ PUNKNOWN UnknownAdapter, + _In_ PENDPOINT_MINIPAIR MiniportPair, + _In_opt_ PVOID DeviceContext + ) + :CMiniportWaveRT(UnknownAdapter, MiniportPair, DeviceContext), + m_TelephonyProviderId(0), + m_TelephonyCallType(TELEPHONY_CALLTYPE_CIRCUITSWITCHED), + m_PreviousTelephonyCallType(TELEPHONY_CALLTYPE_CIRCUITSWITCHED), + m_TelephonyCallState(TELEPHONY_CALLSTATE_DISABLED), + m_bTelephonyHold(FALSE), + m_bTelephonyMuteTx(FALSE) + { + if (IsCellularDevice()) + { + if (m_DeviceFlags & ENDPOINT_CELLULAR_PROVIDER1) + { + m_TelephonyProviderId = 0; + } + else if (m_DeviceFlags & ENDPOINT_CELLULAR_PROVIDER2) + { + m_TelephonyProviderId = 1; + } + } + } + + ~CCellularMiniportWaveRT(); + + friend NTSTATUS PropertyHandler_CellularWaveFilter + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + +public: + NTSTATUS PropertyHandler_TelephonyProviderId + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + + NTSTATUS PropertyHandler_TelephonyCallInfo + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + + NTSTATUS PropertyHandler_TelephonyCallState + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + + NTSTATUS PropertyHandler_TelephonyCallHold + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + + NTSTATUS PropertyHandler_TelephonyMuteTx + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + +private: + NTSTATUS UpdateTopologyJackState + ( + _In_ ULONG TelephonyId, + _In_ BOOL NewState + ); +}; +typedef CCellularMiniportWaveRT *PCCellularMiniportWaveRT; + +#endif // _SYSVAD_CELLULARMINWAVERT_H_ + diff --git a/audio/sysvad/PhoneAudioSample/Cellulartopo.cpp b/audio/sysvad/PhoneAudioSample/Cellulartopo.cpp new file mode 100644 index 000000000..250a03baa --- /dev/null +++ b/audio/sysvad/PhoneAudioSample/Cellulartopo.cpp @@ -0,0 +1,973 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + cellulartopo.cpp + +Abstract: + + Implementation of topology miniport for the cellular endpoint. + +--*/ + +#pragma warning (disable : 4127) + +#include +#include "simple.h" +#include "mintopo.h" +#include "cellulartopo.h" +#include "cellulartoptable.h" +#include + + +#pragma code_seg("PAGE") + + +// table of valid render/capture combinations for cellular routing. +// speaker and mic (speakerphone) +// headset speaker and headset mic (wired headset) +// headset speaker and mic (wired headset without microphone) +// handset speaker and handset mic (handset) +// HFP speaker and HFP mic (bluetooth hands free) +// special host routing pair +KSTOPOLOGY_ENDPOINTIDPAIR g_EndpointList[] = +{ + { HANDSET_SPEAKER_TOPONAME, KSPIN_TOPO_LINEOUT_DEST, HANDSET_MIC_TOPONAME, KSPIN_TOPO_MIC_ELEMENTS }, + { SPEAKER_TOPONAME, KSPIN_TOPO_LINEOUT_DEST, MIC_ARRAY1_TOPONAME, KSPIN_TOPO_MIC_ELEMENTS }, + { SPEAKER_HEADSET_TOPONAME, KSPIN_TOPO_LINEOUT_DEST, MIC_HEADSET_TOPONAME, KSPIN_TOPO_MIC_ELEMENTS }, + { SPEAKER_HEADSET_TOPONAME, KSPIN_TOPO_LINEOUT_DEST, MIC_ARRAY1_TOPONAME, KSPIN_TOPO_MIC_ELEMENTS }, + { HFP_SPEAKER_TOPONAME, KSPIN_TOPO_LINEOUT_DEST, HFP_MIC_TOPONAME, KSPIN_TOPO_MIC_ELEMENTS }, + { HOSTRENDER_TOPONAME, HOSTRENDER_PIN, HOSTCAPTURE_TOPONAME, HOSTCAPTURE_PIN } +}; + +//============================================================================= +NTSTATUS +CreateCellularMiniportTopology +( + _Out_ PUNKNOWN * Unknown, + _In_ REFCLSID, + _In_opt_ PUNKNOWN UnknownOuter, + _When_((PoolType & NonPagedPoolMustSucceed) != 0, + __drv_reportError("Must succeed pool allocations are forbidden. " + "Allocation failures cause a system crash")) + _In_ POOL_TYPE PoolType, + _In_ PUNKNOWN UnknownAdapter, + _In_opt_ PVOID DeviceContext, + _In_ PENDPOINT_MINIPAIR MiniportPair +) +/*++ + +Routine Description: + + Creates a new topology miniport. + +Arguments: + + Unknown - + + RefclsId - + + UnknownOuter - + + PoolType - + + UnknownAdapter - + + DeviceContext - + + MiniportPair - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(Unknown); + ASSERT(MiniportPair); + + UNREFERENCED_PARAMETER(UnknownAdapter); + UNREFERENCED_PARAMETER(DeviceContext); + + CCellularMiniportTopology *obj = + new (PoolType, MINWAVERT_POOLTAG) + CCellularMiniportTopology( UnknownOuter, + MiniportPair->TopoDescriptor, + MiniportPair->DeviceMaxChannels); + if (NULL == obj) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + obj->AddRef(); + *Unknown = reinterpret_cast(obj); + + return STATUS_SUCCESS; +} // CreateCellularMiniportTopology + +CCellularMiniportTopology::CCellularMiniportTopology +( + _In_opt_ PUNKNOWN UnknownOuter, + _In_ PCFILTER_DESCRIPTOR *FilterDesc, + _In_ USHORT DeviceMaxChannels +) +: CUnknown(UnknownOuter), + CMiniportTopologySYSVAD(FilterDesc, DeviceMaxChannels), + m_CellularVolume(0) +{ + PAGED_CODE(); + + memcpy(&m_CellularRenderEndpoint, &(g_EndpointList[0].RenderEndpoint), sizeof(m_CellularRenderEndpoint)); + memcpy(&m_CellularCaptureEndpoint, &(g_EndpointList[0].CaptureEndpoint), sizeof(m_CellularCaptureEndpoint)); +} + + +//============================================================================= +CCellularMiniportTopology::~CCellularMiniportTopology +( + void +) +/*++ + +Routine Description: + + Topology miniport destructor + +Arguments: + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + DPF_ENTER(("[CCellularMiniportTopology::~CCellularMiniportTopology]")); +} // ~CCellularMiniportTopology + +//============================================================================= +NTSTATUS +CCellularMiniportTopology::DataRangeIntersection +( + _In_ ULONG PinId, + _In_ PKSDATARANGE ClientDataRange, + _In_ PKSDATARANGE MyDataRange, + _In_ ULONG OutputBufferLength, + _Out_writes_bytes_to_opt_(OutputBufferLength, *ResultantFormatLength) + PVOID ResultantFormat OPTIONAL, + _Out_ PULONG ResultantFormatLength +) +/*++ + +Routine Description: + + The DataRangeIntersection function determines the highest quality + intersection of two data ranges. + +Arguments: + + PinId - Pin for which data intersection is being determined. + + ClientDataRange - Pointer to KSDATARANGE structure which contains the data range + submitted by client in the data range intersection property + request. + + MyDataRange - Pin's data range to be compared with client's data range. + + OutputBufferLength - Size of the buffer pointed to by the resultant format + parameter. + + ResultantFormat - Pointer to value where the resultant format should be + returned. + + ResultantFormatLength - Actual length of the resultant format that is placed + at ResultantFormat. This should be less than or equal + to OutputBufferLength. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + return + CMiniportTopologySYSVAD::DataRangeIntersection + ( + PinId, + ClientDataRange, + MyDataRange, + OutputBufferLength, + ResultantFormat, + ResultantFormatLength + ); +} // DataRangeIntersection + +//============================================================================= +STDMETHODIMP +CCellularMiniportTopology::GetDescription +( + _Out_ PPCFILTER_DESCRIPTOR * OutFilterDescriptor +) +/*++ + +Routine Description: + + The GetDescription function gets a pointer to a filter description. + It provides a location to deposit a pointer in miniport's description + structure. This is the placeholder for the FromNode or ToNode fields in + connections which describe connections to the filter's pins. + +Arguments: + + OutFilterDescriptor - Pointer to the filter description. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(OutFilterDescriptor); + + return CMiniportTopologySYSVAD::GetDescription(OutFilterDescriptor); +} // GetDescription + +//============================================================================= +STDMETHODIMP +CCellularMiniportTopology::Init +( + _In_ PUNKNOWN UnknownAdapter, + _In_ PRESOURCELIST ResourceList, + _In_ PPORTTOPOLOGY Port_ +) +/*++ + +Routine Description: + + The Init function initializes the miniport. Callers of this function + should run at IRQL PASSIVE_LEVEL + +Arguments: + + UnknownAdapter - A pointer to the Iuknown interface of the adapter object. + + ResourceList - Pointer to the resource list to be supplied to the miniport + during initialization. The port driver is free to examine the + contents of the ResourceList. The port driver will not be + modify the ResourceList contents. + + Port - Pointer to the topology port object that is linked with this miniport. + +Return Value: + + NT status code. + +--*/ +{ + UNREFERENCED_PARAMETER(ResourceList); + + PAGED_CODE(); + + ASSERT(UnknownAdapter); + ASSERT(Port_); + + DPF_ENTER(("[CCellularMiniportTopology::Init]")); + + NTSTATUS ntStatus; + + ntStatus = + CMiniportTopologySYSVAD::Init + ( + UnknownAdapter, + Port_ + ); + + return ntStatus; +} // Init + +//============================================================================= +STDMETHODIMP +CCellularMiniportTopology::NonDelegatingQueryInterface +( + _In_ REFIID Interface, + _COM_Outptr_ PVOID * Object +) +/*++ + +Routine Description: + + QueryInterface for MiniportTopology + +Arguments: + + Interface - GUID of the interface + + Object - interface object to be returned. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(Object); + + if (IsEqualGUIDAligned(Interface, IID_IUnknown)) + { + *Object = PVOID(PUNKNOWN(this)); + } + else if (IsEqualGUIDAligned(Interface, IID_IMiniport)) + { + *Object = PVOID(PMINIPORT(this)); + } + else if (IsEqualGUIDAligned(Interface, IID_IMiniportTopology)) + { + *Object = PVOID(PMINIPORTTOPOLOGY(this)); + } + else if (IsEqualGUIDAligned(Interface, IID_ICellularTopology)) + { + *Object = PVOID(PCELLULARTOPOLOGY(this)); + } + else + { + *Object = NULL; + } + + if (*Object) + { + // We reference the interface for the caller. + PUNKNOWN(*Object)->AddRef(); + return(STATUS_SUCCESS); + } + + return(STATUS_INVALID_PARAMETER); +} // NonDelegatingQueryInterface + +//============================================================================= +NTSTATUS +CCellularMiniportTopology::PropertyHandlerJackDescription +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_Jack, KSPROPERTY_JACK_DESCRIPTION ) + +Arguments: + + PropertyRequest + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandlerJackDescription]")); + + ULONG cJackDescriptions = ARRAYSIZE(CellularJackDescriptions); + PKSJACK_DESCRIPTION * JackDescriptions = CellularJackDescriptions; + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + ULONG nPinId = (ULONG)-1; + + if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + nPinId = *(PULONG(PropertyRequest->Instance)); + + if ((nPinId < cJackDescriptions) && (JackDescriptions[nPinId] != NULL)) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else + { + ULONG cbNeeded = sizeof(KSMULTIPLE_ITEM) + sizeof(KSJACK_DESCRIPTION); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbNeeded; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbNeeded) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PKSMULTIPLE_ITEM pMI = (PKSMULTIPLE_ITEM)PropertyRequest->Value; + PKSJACK_DESCRIPTION pDesc = (PKSJACK_DESCRIPTION)(pMI+1); + + pMI->Size = cbNeeded; + pMI->Count = 1; + + RtlCopyMemory(pDesc, JackDescriptions[nPinId], sizeof(KSJACK_DESCRIPTION)); + ntStatus = STATUS_SUCCESS; + } + } + } + } + } + + return ntStatus; +} + +//============================================================================= +NTSTATUS +CCellularMiniportTopology::PropertyHandlerJackDescription2 +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_Jack, KSPROPERTY_JACK_DESCRIPTION2 ) + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandlerJackDescription2]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + ULONG nPinId = (ULONG)-1; + + ULONG cJackDescriptions = ARRAYSIZE(CellularJackDescriptions); + PKSJACK_DESCRIPTION * JackDescriptions = CellularJackDescriptions; + + if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + nPinId = *(PULONG(PropertyRequest->Instance)); + + if ((nPinId < cJackDescriptions) && (JackDescriptions[nPinId] != NULL)) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else + { + ULONG cbNeeded = sizeof(KSMULTIPLE_ITEM) + sizeof(KSJACK_DESCRIPTION2); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbNeeded; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbNeeded) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PKSMULTIPLE_ITEM pMI = (PKSMULTIPLE_ITEM)PropertyRequest->Value; + PKSJACK_DESCRIPTION2 pDesc = (PKSJACK_DESCRIPTION2)(pMI+1); + + pMI->Size = cbNeeded; + pMI->Count = 1; + + RtlZeroMemory(pDesc, sizeof(KSJACK_DESCRIPTION2)); + + // + // Specifies the lower 16 bits of the DWORD parameter. This parameter indicates whether + // the jack is currently active, streaming, idle, or hardware not ready. + // + pDesc->DeviceStateInfo = 0; + + // + // From MSDN: + // "If an audio device lacks jack presence detection, the IsConnected member of + // the KSJACK_DESCRIPTION structure must always be set to TRUE. To remove the + // ambiguity that results from this dual meaning of the TRUE value for IsConnected, + // a client application can call IKsJackDescription2::GetJackDescription2 to read + // the JackCapabilities flag of the KSJACK_DESCRIPTION2 structure. If this flag has + // the JACKDESC2_PRESENCE_DETECT_CAPABILITY bit set, it indicates that the endpoint + // does in fact support jack presence detection. In that case, the return value of + // the IsConnected member can be interpreted to accurately reflect the insertion status + // of the jack." + // + // Bit definitions: + // 0x00000001 - JACKDESC2_PRESENCE_DETECT_CAPABILITY + // 0x00000002 - JACKDESC2_DYNAMIC_FORMAT_CHANGE_CAPABILITY + // + pDesc->JackCapabilities = JACKDESC2_PRESENCE_DETECT_CAPABILITY; + + ntStatus = STATUS_SUCCESS; + } + } + } + } + } + + return ntStatus; +} + +//============================================================================= +NTSTATUS +CCellularMiniportTopology::PropertyHandlerTelephonyEndpointIdPair +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_TelephonyTopology, KSPROPERTY_TELEPHONY_ENDPOINTIDPAIR ) + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandlerTelephonyEndpointIdPair]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = STATUS_INVALID_PARAMETER; + ULONG ulExpectedSize = (sizeof(KSPROPERTY_DESCRIPTION) + sizeof(KSPROPERTY_MEMBERSHEADER) + sizeof(g_EndpointList)); + + if (PropertyRequest->ValueSize >= sizeof(KSPROPERTY_DESCRIPTION)) + { + // if return buffer can hold a KSPROPERTY_DESCRIPTION, return it + // + PKSPROPERTY_DESCRIPTION PropDesc = + PKSPROPERTY_DESCRIPTION(PropertyRequest->Value); + + PropDesc->AccessFlags = KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_SET |KSPROPERTY_TYPE_GET; + PropDesc->DescriptionSize = ulExpectedSize; + PropDesc->PropTypeSet.Set = KSPROPSETID_TelephonyTopology; + PropDesc->PropTypeSet.Id = KSPROPERTY_TELEPHONY_ENDPOINTIDPAIR; + PropDesc->PropTypeSet.Flags = 0; + PropDesc->MembersListCount = 1; + PropDesc->Reserved = 0; + + // buffer is big enough to hold the full data, add that + if (PropertyRequest->ValueSize >= ulExpectedSize) + { + PKSPROPERTY_MEMBERSHEADER MembersHeader = + PKSPROPERTY_MEMBERSHEADER(PropDesc + 1); + + MembersHeader->MembersFlags = KSPROPERTY_MEMBER_VALUES; + MembersHeader->MembersSize = sizeof(KSTOPOLOGY_ENDPOINTIDPAIR); + MembersHeader->MembersCount = SIZEOF_ARRAY(g_EndpointList); + MembersHeader->Flags = 0; + + memcpy(MembersHeader + 1, g_EndpointList, sizeof(g_EndpointList)); + + // tell them how much space we really used, which controls how much data is copied into the user buffer. + PropertyRequest->ValueSize = ulExpectedSize; + } + else + { + // tell them how much space we really used, which controls how much data is copied into the user buffer. + PropertyRequest->ValueSize = sizeof(KSPROPERTY_DESCRIPTION); + } + + ntStatus = STATUS_SUCCESS; + } + else if (PropertyRequest->ValueSize >= sizeof(ULONG)) + { + // if return buffer can hold a ULONG, return the access flags. + *(PULONG(PropertyRequest->Value)) = KSPROPERTY_TYPE_SET |KSPROPERTY_TYPE_GET; + PropertyRequest->ValueSize = sizeof(ULONG); + ntStatus = STATUS_SUCCESS; + } + else if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = ulExpectedSize; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + } + else + { + ULONG cbNeeded = sizeof(KSTOPOLOGY_ENDPOINTIDPAIR); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbNeeded; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbNeeded) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PKSTOPOLOGY_ENDPOINTIDPAIR EndpointIdPair = static_cast(PropertyRequest->Value); + + // Return endpoints (render and capture) info where cellular audio is being routed to. + memcpy(&(EndpointIdPair->RenderEndpoint), &m_CellularRenderEndpoint, sizeof(m_CellularRenderEndpoint)); + memcpy(&(EndpointIdPair->CaptureEndpoint), &m_CellularCaptureEndpoint, sizeof(m_CellularCaptureEndpoint)); + + ntStatus = STATUS_SUCCESS; + } + else if (PropertyRequest->Verb & KSPROPERTY_TYPE_SET) + { + PKSTOPOLOGY_ENDPOINTIDPAIR EndpointIdPair = static_cast(PropertyRequest->Value); + + // verify the requested combination is in the list of valid endpoint combinations, fail with + // invalid parameter if not found. + ntStatus = STATUS_INVALID_PARAMETER; + for (int i = 0; i < SIZEOF_ARRAY(g_EndpointList); i++) + { + if (g_EndpointList[i].RenderEndpoint.PinId == EndpointIdPair->RenderEndpoint.PinId && + 0 == _wcsnicmp(g_EndpointList[i].RenderEndpoint.TopologyName, EndpointIdPair->RenderEndpoint.TopologyName, MAX_PATH) && + g_EndpointList[i].CaptureEndpoint.PinId == EndpointIdPair->CaptureEndpoint.PinId && + 0 == _wcsnicmp(g_EndpointList[i].CaptureEndpoint.TopologyName, EndpointIdPair->CaptureEndpoint.TopologyName, MAX_PATH)) + { + if (HOSTRENDER_PIN == EndpointIdPair->RenderEndpoint.PinId && + 0 == _wcsnicmp(HOSTRENDER_TOPONAME, EndpointIdPair->RenderEndpoint.TopologyName, MAX_PATH) && + HOSTCAPTURE_PIN == EndpointIdPair->CaptureEndpoint.PinId && + 0 == _wcsnicmp(HOSTCAPTURE_TOPONAME, EndpointIdPair->CaptureEndpoint.TopologyName, MAX_PATH)) + { + // This is an indication that cellular audio is being routed through OS (For example, user plugs in USB headset + // for cellular audio) and driver must stop rendering cellular audio to any physical audio endpoint. Driver must + // also stop capturing data from any physical audio endpoint for cellular. This will update audio routing for + // all the active cellular calls. + } + else + { + // Route cellular audio to these new endpoints (render and capture). This will update audio routing for + // all the active cellular calls. + } + + memcpy(&m_CellularRenderEndpoint, &(EndpointIdPair->RenderEndpoint), sizeof(m_CellularRenderEndpoint)); + memcpy(&m_CellularCaptureEndpoint, &(EndpointIdPair->CaptureEndpoint), sizeof(m_CellularCaptureEndpoint)); + + // we found the entry, so return success + ntStatus = STATUS_SUCCESS; + break; + } + } + + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + } + } + + return ntStatus; +} + +//============================================================================= +NTSTATUS +CCellularMiniportTopology::PropertyHandlerTelephonyVolume +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_TelephonyTopology, KSPROPERTY_TELEPHONY_VOLUME ) + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandlerTelephonyVolume]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupportVolume + ( + PropertyRequest, + 1 // Telephony audio is only ever 1 channel + ); + + ULONG cbFullProperty = + sizeof(KSPROPERTY_DESCRIPTION) + + sizeof(KSPROPERTY_MEMBERSHEADER) + + sizeof(KSPROPERTY_STEPPING_LONG); + + if (NT_SUCCESS(ntStatus) && PropertyRequest->ValueSize >= cbFullProperty) + { + // Access proper values + PKSPROPERTY_DESCRIPTION PropDesc = + PKSPROPERTY_DESCRIPTION(PropertyRequest->Value); + PKSPROPERTY_MEMBERSHEADER Members = + PKSPROPERTY_MEMBERSHEADER(PropDesc + 1); + PKSPROPERTY_STEPPING_LONG Range = + PKSPROPERTY_STEPPING_LONG(Members + 1); + + // Telephony volume is only ever single channel, so the + // MULTICHANNEL and UNIFORM flags are meaningless + Members->Flags = 0; + + // Telephony Values that may be different from standard volume values + Range->Bounds.SignedMaximum = TELEPHONY_VOLUME_MAXIMUM; + Range->Bounds.SignedMinimum = TELEPHONY_VOLUME_MINIMUM; + Range->SteppingDelta = TELEPHONY_VOLUME_STEPPING; + } + } + else + { + ntStatus = + ValidatePropertyParams + ( + PropertyRequest, + sizeof(LONG), // volume value is a LONG + 0 // there is no channel associated with telephony volume + ); + + + if (NT_SUCCESS(ntStatus)) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + LONG* volume = (LONG*)PropertyRequest->Value; + // Return current cellular volume (Incall volume). + *volume = m_CellularVolume; + PropertyRequest->ValueSize = sizeof(LONG); + ntStatus = STATUS_SUCCESS; + } + else if (PropertyRequest->Verb & KSPROPERTY_TYPE_SET) + { + LONG* volume = (LONG*)PropertyRequest->Value; + + LONG normalizedVolume = + VALUE_NORMALIZE_IN_RANGE_EX + ( + *volume, + TELEPHONY_VOLUME_MINIMUM, + TELEPHONY_VOLUME_MAXIMUM, + TELEPHONY_VOLUME_STEPPING + ); + if (normalizedVolume == *volume) + { + // Update the cellular volume (Incall volume). This will update cellular volume for all the active cellular calls. + m_CellularVolume = *volume; + ntStatus = STATUS_SUCCESS; + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + } + } + + return ntStatus; +} + +//============================================================================= +NTSTATUS +PropertyHandler_CellularTopoFilter +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Redirects property request to miniport object + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_CellularTopoFilter]")); + + // PropertryRequest structure is filled by portcls. + // MajorTarget is a pointer to miniport object for miniports. + // + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + PCCellularMiniportTopology pMiniport = (PCCellularMiniportTopology)PropertyRequest->MajorTarget; + + if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_Jack)) + { + if (PropertyRequest->PropertyItem->Id == KSPROPERTY_JACK_DESCRIPTION) + { + ntStatus = pMiniport->PropertyHandlerJackDescription(PropertyRequest); + } + else if (PropertyRequest->PropertyItem->Id == KSPROPERTY_JACK_DESCRIPTION2) + { + ntStatus = pMiniport->PropertyHandlerJackDescription2(PropertyRequest); + } + } + else if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_TelephonyTopology)) + { + if (PropertyRequest->PropertyItem->Id == KSPROPERTY_TELEPHONY_ENDPOINTIDPAIR) + { + ntStatus = pMiniport->PropertyHandlerTelephonyEndpointIdPair(PropertyRequest); + } + else if (PropertyRequest->PropertyItem->Id == KSPROPERTY_TELEPHONY_VOLUME) + { + ntStatus = pMiniport->PropertyHandlerTelephonyVolume(PropertyRequest); + } + } + + + return ntStatus; +} // PropertyHandler_CellularTopoFilter + +#pragma code_seg("PAGE") +STDMETHODIMP_(NTSTATUS) +CCellularMiniportTopology::UpdateTopologyJackState +( + _In_ ULONG TelephonyId, + _In_ BOOL NewState +) +{ + PAGED_CODE (); + + struct JackState + { + ULONG TelephonyId; + ULONG PinId; + PKSJACK_DESCRIPTION JackControl; + }; + + JackState Jacks[] = + { + { + 0, + KSPIN_TOPO_BIDI1, + &CellularJackDesc + }, + { + 1, + KSPIN_TOPO_BIDI2, + &CellularJackDesc2 + } + }; + + // Query the registry and update the jack state for all of the pins. + for (int i = 0; i < SIZEOF_ARRAY(Jacks); i++) + { + if (Jacks[i].TelephonyId == TelephonyId) + { + Jacks[i].JackControl->IsConnected = NewState; + + // we have multiple pins which share the same jack state, + // notify for every pin using that jack state + GenerateEventList( + (GUID*)&KSEVENTSETID_PinCapsChange, // event set. NULL is a wild card for all events. + KSEVENT_PINCAPS_JACKINFOCHANGE, // event ID. + TRUE, // use pid ID. + Jacks[i].PinId, // pin ID. + FALSE, // do not use node ID. + ULONG(-1)); // node ID, not used. + } + } + + return STATUS_SUCCESS; +} + +#pragma code_seg() +NTSTATUS CCellularMiniportWaveRT_EventHandler_JackState +( + _In_ PPCEVENT_REQUEST EventRequest +) +{ + CCellularMiniportTopology* miniport = reinterpret_cast(EventRequest->MajorTarget); + return miniport->EventHandler_JackState(EventRequest); +} + +NTSTATUS CCellularMiniportTopology::EventHandler_JackState +( + _In_ PPCEVENT_REQUEST EventRequest +) +{ + if (EventRequest->Verb == PCEVENT_VERB_ADD) + { + m_PortEvents->AddEventToEventList(EventRequest->EventEntry); + } + + return STATUS_SUCCESS; +} + + diff --git a/audio/sysvad/PhoneAudioSample/Cellulartopo.h b/audio/sysvad/PhoneAudioSample/Cellulartopo.h new file mode 100644 index 000000000..bcd4aba73 --- /dev/null +++ b/audio/sysvad/PhoneAudioSample/Cellulartopo.h @@ -0,0 +1,159 @@ + +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + cellulartopo.h + +Abstract: + + Declaration of topology miniport for the cellular endpoint. + +--*/ + +#ifndef _SYSVAD_CELLULARTOPO_H_ +#define _SYSVAD_CELLULARTOPO_H_ + +#include "basetopo.h" + +// make sure all of these names matches with KSNAME_* in the inf's [Strings] section +#define SPEAKER_TOPONAME L"TopologySpeaker" +#define SPEAKER_HEADSET_TOPONAME L"TopologySpeakerHeadset" +#define MIC_HEADSET_TOPONAME L"TopologyMicHeadset" +#define MIC_ARRAY1_TOPONAME L"TopologyMicArray1" +#define HANDSET_SPEAKER_TOPONAME L"TopologyHandsetSpeaker" +#define HANDSET_MIC_TOPONAME L"TopologyHandsetMic" + +// from bthhfpminipairs.h, static name of bluetooth speaker and mic topologies +#define HFP_SPEAKER_TOPONAME L"TopologyBthHfpSpeaker" +#define HFP_MIC_TOPONAME L"TopologyBthHfpMic" + +// Special routing endpoints +#define HOSTRENDER_TOPONAME L"HostRender" +#define HOSTRENDER_PIN 0 +#define HOSTCAPTURE_TOPONAME L"HostCapture" +#define HOSTCAPTURE_PIN 0 + +// Telephony Volume Stepping information +#define TELEPHONY_VOLUME_MAXIMUM 0 // 0 dB +#define TELEPHONY_VOLUME_MINIMUM (-30 * 0x10000) // -30 dB +#define TELEPHONY_VOLUME_STEPPING (3 * 0x10000) // 10 steps + + +//============================================================================= +// Defines +//============================================================================= +// {3829F9C3-F341-4B82-976F-A635EB3CEA0D} +DEFINE_GUID(IID_ICellularTopology, +0x3829f9c3, 0xf341, 0x4b82, 0x97, 0x6f, 0xa6, 0x35, 0xeb, 0x3c, 0xea, 0xd); + + +//============================================================================= +// Interfaces +//============================================================================= + +/////////////////////////////////////////////////////////////////////////////// +// ICellularTopology +// +DECLARE_INTERFACE_(ICellularTopology, IMiniportTopology) +{ + STDMETHOD_(NTSTATUS, UpdateTopologyJackState) + ( + THIS_ + _In_ ULONG TelephonyId, + _In_ BOOL NewState + ) PURE; +}; + +typedef ICellularTopology *PCELLULARTOPOLOGY; + +//============================================================================= +// Classes +//============================================================================= + +/////////////////////////////////////////////////////////////////////////////// +// CCellularMiniportTopology +// + +class CCellularMiniportTopology : + public CMiniportTopologySYSVAD, + public ICellularTopology, + public CUnknown +{ + private: + KSTOPOLOGY_ENDPOINTID m_CellularRenderEndpoint; + KSTOPOLOGY_ENDPOINTID m_CellularCaptureEndpoint; + ULONG m_CellularVolume; + + public: + DECLARE_STD_UNKNOWN(); + CCellularMiniportTopology + ( + _In_opt_ PUNKNOWN UnknownOuter, + _In_ PCFILTER_DESCRIPTOR *FilterDesc, + _In_ USHORT DeviceMaxChannels + ); + + ~CCellularMiniportTopology(); + + IMP_IMiniportTopology; + + NTSTATUS PropertyHandlerJackSinkInfo + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + + NTSTATUS PropertyHandlerJackDescription + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + + NTSTATUS PropertyHandlerJackDescription2 + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + + NTSTATUS PropertyHandlerTelephonyEndpointIdPair + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + + NTSTATUS PropertyHandlerTelephonyVolume + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + + NTSTATUS EventHandler_JackState + ( + _In_ PPCEVENT_REQUEST _pEventRequest + ); + + STDMETHODIMP_(NTSTATUS) UpdateTopologyJackState + ( + _In_ ULONG TelephonyId, + _In_ BOOL NewState + ); +}; + +typedef CCellularMiniportTopology *PCCellularMiniportTopology; + +NTSTATUS +CreateCellularMiniportTopology( + _Out_ PUNKNOWN * Unknown, + _In_ REFCLSID, + _In_opt_ PUNKNOWN UnknownOuter, + _When_((PoolType & NonPagedPoolMustSucceed) != 0, + __drv_reportError("Must succeed pool allocations are forbidden. " + "Allocation failures cause a system crash")) + _In_ POOL_TYPE PoolType, + _In_ PUNKNOWN UnknownAdapter, + _In_opt_ PVOID DeviceContext, + _In_ PENDPOINT_MINIPAIR MiniportPair + ); + +NTSTATUS PropertyHandler_CellularTopoFilter(_In_ PPCPROPERTY_REQUEST PropertyRequest); + +#endif // _SYSVAD_CELLULARTOPO_H_ + diff --git a/audio/sysvad/PhoneAudioSample/Cellulartoptable.h b/audio/sysvad/PhoneAudioSample/Cellulartoptable.h new file mode 100644 index 000000000..9b6766ab7 --- /dev/null +++ b/audio/sysvad/PhoneAudioSample/Cellulartoptable.h @@ -0,0 +1,255 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + cellulartoptable.h + +Abstract: + + Declaration of topology tables for the cellular endpoint. + +--*/ + +#ifndef _SYSVAD_CELLULARTOPTABLE_H_ +#define _SYSVAD_CELLULARTOPTABLE_H_ + +// {896BC350-BF57-4039-A6B6-64DCEC3AEEB8} +DEFINE_GUID(CELLULARBIDI1_CUSTOM_NAME, +0x896bc350, 0xbf57, 0x4039, 0xa6, 0xb6, 0x64, 0xdc, 0xec, 0x3a, 0xee, 0xb8); + +// {80CC64DA-166A-4A2C-ADF7-1464C49D3B0E} +DEFINE_GUID(CELLULARBIDI2_CUSTOM_NAME, +0x80cc64da, 0x166a, 0x4a2c, 0xad, 0xf7, 0x14, 0x64, 0xc4, 0x9d, 0x3b, 0xe); + +//============================================================================= +static +KSDATARANGE CellularTopoPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +//============================================================================= +static +PKSDATARANGE CellularTopoPinDataRangePointersBridge[] = +{ + &CellularTopoPinDataRangesBridge[0] +}; + +//============================================================================= +static +PCPIN_DESCRIPTOR CellularTopoMiniportPins[] = +{ + // KSPIN_TOPO_BIDI1 + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(CellularTopoPinDataRangePointersBridge), // DataRangesCount + CellularTopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_IN, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSNODETYPE_TELEPHONY_BIDI, // Category + &CELLULARBIDI1_CUSTOM_NAME, // Name + 0 // Reserved + } + }, + // KSPIN_TOPO_BIDI1_BRIDGE + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(CellularTopoPinDataRangePointersBridge), // DataRangesCount + CellularTopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_OUT, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSCATEGORY_AUDIO, // Category + NULL, // Name + 0 // Reserved + } + }, + // KSPIN_TOPO_BIDI2 + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(CellularTopoPinDataRangePointersBridge), // DataRangesCount + CellularTopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_IN, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSNODETYPE_TELEPHONY_BIDI, // Category + &CELLULARBIDI2_CUSTOM_NAME, // Name + 0 // Reserved + } + }, + // KSPIN_TOPO_BIDI2_BRIDGE + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(CellularTopoPinDataRangePointersBridge), // DataRangesCount + CellularTopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_OUT, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSCATEGORY_AUDIO, // Category + NULL, // Name + 0 // Reserved + } + } +}; + +//============================================================================= +static +KSJACK_DESCRIPTION CellularJackDesc = +{ + KSAUDIO_SPEAKER_MONO, + 0x0000, // no color + eConnTypeUnknown, + eGeoLocNotApplicable, + eGenLocInternal, + ePortConnUnknown, + TRUE // NOTE: cellular jacks should be "unplugged" at boot, and only changed to active during a call. + // However, for convienience during development, the jack state is set to active at boot for this milestone. +}; + +static +KSJACK_DESCRIPTION CellularJackDesc2 = +{ + KSAUDIO_SPEAKER_MONO, + 0x0000, // no color + eConnTypeUnknown, + eGeoLocNotApplicable, + eGenLocInternal, + ePortConnUnknown, + TRUE // NOTE: cellular jacks should be "unplugged" at boot, and only changed to active during a call. + // However, for convienience during development, the jack state is set to active at boot for this milestone. +}; + + +// Only return a KSJACK_DESCRIPTION for the physical bridge pin. +static +PKSJACK_DESCRIPTION CellularJackDescriptions[] = +{ + &CellularJackDesc, + NULL, + &CellularJackDesc2, + NULL +}; + +//============================================================================= +static +PCCONNECTION_DESCRIPTOR CellularTopoMiniportConnections[] = +{ + // FromNode, FromPin, ToNode, ToPin + { PCFILTER_NODE, KSPIN_TOPO_BIDI1, PCFILTER_NODE, KSPIN_TOPO_BIDI1_BRIDGE}, + { PCFILTER_NODE, KSPIN_TOPO_BIDI2, PCFILTER_NODE, KSPIN_TOPO_BIDI2_BRIDGE} +}; + + +//============================================================================= +static +PCPROPERTY_ITEM PropertiesCellularTopoFilter[] = +{ + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_CellularTopoFilter + }, + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION2, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_CellularTopoFilter + }, + { + &KSPROPSETID_TelephonyTopology, + KSPROPERTY_TELEPHONY_ENDPOINTIDPAIR, + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_CellularTopoFilter + }, + { + &KSPROPSETID_TelephonyTopology, + KSPROPERTY_TELEPHONY_VOLUME, + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_CellularTopoFilter + } +}; + +NTSTATUS CCellularMiniportWaveRT_EventHandler_JackState +( + _In_ PPCEVENT_REQUEST EventRequest +); + +static PCEVENT_ITEM JackInfoChangeEvent[] = +{ + { + &KSEVENTSETID_PinCapsChange, // Something changed + KSEVENT_PINCAPS_JACKINFOCHANGE, // Jack Info Changes + KSEVENT_TYPE_ENABLE | KSEVENT_TYPE_BASICSUPPORT, + CCellularMiniportWaveRT_EventHandler_JackState + } +}; + +/***************************************************************************** + * PropertiesTopologyFilter + ***************************************************************************** + * Automation table for jack descripton/detection. + */ +DEFINE_PCAUTOMATION_TABLE_PROP_EVENT(AutomationCellularTopoFilter, PropertiesCellularTopoFilter, JackInfoChangeEvent); + + +//============================================================================= +static +PCFILTER_DESCRIPTOR CellularTopoMiniportFilterDescriptor = +{ + 0, // Version + &AutomationCellularTopoFilter, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(CellularTopoMiniportPins), // PinCount + CellularTopoMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + 0, // NodeCount + NULL, // Nodes + SIZEOF_ARRAY(CellularTopoMiniportConnections), // ConnectionCount + CellularTopoMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories +}; + +#endif // _SYSVAD_CELLULARTOPTABLE_H_ + + diff --git a/audio/sysvad/PhoneAudioSample/Cellularwavtable.h b/audio/sysvad/PhoneAudioSample/Cellularwavtable.h new file mode 100644 index 000000000..aeb91e74f --- /dev/null +++ b/audio/sysvad/PhoneAudioSample/Cellularwavtable.h @@ -0,0 +1,286 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + cellularwavtable.h + +Abstract: + + Declaration of wave miniport tables for the cellular endpoint. + +--*/ + +#ifndef _SYSVAD_CELLULARWAVTABLE_H_ +#define _SYSVAD_CELLULARWAVTABLE_H_ + + +#define CELLULAR_DEVICE_MAX_CHANNELS 1 // Max Channels. + +#define CELLULAR_CAPTURE_MAX_CHANNELS 1 // Max Channels. +#define CELLULAR_CAPTURE_MIN_BITS_PER_SAMPLE 16 // Min Bits Per Sample +#define CELLULAR_CAPTURE_MAX_BITS_PER_SAMPLE 16 // Max Bits Per Sample +#define CELLULAR_CAPTURE_MIN_SAMPLE_RATE 16000 // Min Sample Rate +#define CELLULAR_CAPTURE_MAX_SAMPLE_RATE 16000 // Max Sample Rate + +// +// Max # of pin instances. +// +#define CELLULAR_MAX_INPUT_SYSTEM_STREAMS 1 // Raw stream + + +//============================================================================= +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE CellularCapturePinSupportedDeviceFormats[] = +{ + { // 0 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 16000, + 32000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, +}; + +// +// Supported modes (only on streaming pins). +// +static +MODE_AND_DEFAULT_FORMAT CellularCapturePinSupportedDeviceModes[] = +{ + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_RAW, + &CellularCapturePinSupportedDeviceFormats[SIZEOF_ARRAY(CellularCapturePinSupportedDeviceFormats) - 1].DataFormat + } +}; + +//============================================================================= +// The entries here must follow the same order as the filter's pin +// descriptor array. +// +static +PIN_DEVICE_FORMATS_AND_MODES CellularPinDeviceFormatsAndModes[] = +{ + { // BIDI + TelephonyBidiPin, + CellularCapturePinSupportedDeviceFormats, + SIZEOF_ARRAY(CellularCapturePinSupportedDeviceFormats), + CellularCapturePinSupportedDeviceModes, + SIZEOF_ARRAY(CellularCapturePinSupportedDeviceModes) + }, + { + BridgePin, + NULL, + 0, + NULL, + 0, + } +}; + +//============================================================================= +static +KSDATARANGE_AUDIO CellularPinDataRangesStream[] = +{ + { // 0 + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + CELLULAR_CAPTURE_MAX_CHANNELS, + CELLULAR_CAPTURE_MIN_BITS_PER_SAMPLE, + CELLULAR_CAPTURE_MAX_BITS_PER_SAMPLE, + CELLULAR_CAPTURE_MIN_SAMPLE_RATE, + CELLULAR_CAPTURE_MAX_SAMPLE_RATE + } +}; + +static +PKSDATARANGE CellularPinDataRangePointersBiDiStream[] = +{ + PKSDATARANGE(&CellularPinDataRangesStream[0]), + PKSDATARANGE(&PinDataRangeAttributeList) +}; + +//============================================================================= +static +KSDATARANGE CellularPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +static +PKSDATARANGE CellularPinDataRangePointersBridge[] = +{ + &CellularPinDataRangesBridge[0] +}; + + +//============================================================================= +static +PCPIN_DESCRIPTOR CellularWaveMiniportPins[] = +{ + // Wave Out Streaming Pin (Renderer) KSPIN_WAVE_BIDI + { + CELLULAR_MAX_INPUT_SYSTEM_STREAMS, + CELLULAR_MAX_INPUT_SYSTEM_STREAMS, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(CellularPinDataRangePointersBiDiStream), + CellularPinDataRangePointersBiDiStream, + KSPIN_DATAFLOW_OUT, + KSPIN_COMMUNICATION_SINK, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, + // Wave Out Bridge Pin (Renderer) KSPIN_WAVE_BIDI_BRIDGE + { + 0, + 0, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(CellularPinDataRangePointersBridge), + CellularPinDataRangePointersBridge, + KSPIN_DATAFLOW_IN, + KSPIN_COMMUNICATION_NONE, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + } +}; + +//============================================================================= +// +// Note: +// +// ----- +// | | +// BiDi pin 1 <--| |<-- 0 BiDi bridge pin +// ----- + +static +PCCONNECTION_DESCRIPTOR CellularWaveMiniportConnections[] = +{ + { PCFILTER_NODE, KSPIN_WAVE_BIDI_BRIDGE, PCFILTER_NODE, KSPIN_WAVE_BIDI } +}; + +//============================================================================= +static +PCPROPERTY_ITEM PropertiesCellularWaveFilter[] = +{ + { + &KSPROPSETID_Pin, + KSPROPERTY_PIN_PROPOSEDATAFORMAT, + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_WaveFilter + }, + + { + &KSPROPSETID_TelephonyControl, + KSPROPERTY_TELEPHONY_PROVIDERID, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_CellularWaveFilter + }, + + { + &KSPROPSETID_TelephonyControl, + KSPROPERTY_TELEPHONY_CALLINFO, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_CellularWaveFilter + }, + + { + &KSPROPSETID_TelephonyControl, + KSPROPERTY_TELEPHONY_CALLCONTROL, + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_CellularWaveFilter + }, + + { + &KSPROPSETID_TelephonyControl, + KSPROPERTY_TELEPHONY_PROVIDERCHANGE, + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_CellularWaveFilter + }, + + { + &KSPROPSETID_TelephonyControl, + KSPROPERTY_TELEPHONY_CALLHOLD, + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_CellularWaveFilter + }, + + { + &KSPROPSETID_TelephonyControl, + KSPROPERTY_TELEPHONY_MUTE_TX, + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_CellularWaveFilter + }, +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationCellularWaveFilter, PropertiesCellularWaveFilter); + +//============================================================================= +static +PCFILTER_DESCRIPTOR CellularWaveMiniportFilterDescriptor = +{ + 0, // Version + &AutomationCellularWaveFilter, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(CellularWaveMiniportPins), // PinCount + CellularWaveMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + 0, // NodeCount + NULL, // Nodes + SIZEOF_ARRAY(CellularWaveMiniportConnections), // ConnectionCount + CellularWaveMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories - use defaults (audio, render, capture) +}; + +#endif // _SYSVAD_CELLULARWAVTABLE_H_ + diff --git a/audio/sysvad/PhoneAudioSample/FMTopTable.h b/audio/sysvad/PhoneAudioSample/FMTopTable.h new file mode 100644 index 000000000..ae8ddf9ed --- /dev/null +++ b/audio/sysvad/PhoneAudioSample/FMTopTable.h @@ -0,0 +1,199 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + fmtoptable.h + +Abstract: + + Declaration of topology tables for the fm endpoint. + +--*/ + +#ifndef _SYSVAD_FMTOPTABLE_H_ +#define _SYSVAD_FMTOPTABLE_H_ + +// {344EC7D7-F209-4FC4-B6C1-B324E8309E8D} +DEFINE_GUID(FMRX_CUSTOM_NAME, + 0x344ec7d7, 0xf209, 0x4fc4, 0xb6, 0xc1, 0xb3, 0x24, 0xe8, 0x30, 0x9e, 0x8d); + +//============================================================================= +static +KSDATARANGE FmTopoPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +//============================================================================= +static +PKSDATARANGE FmTopoPinDataRangePointersBridge[] = +{ + &FmTopoPinDataRangesBridge[0] +}; + +//============================================================================= +static +PCPIN_DESCRIPTOR FmTopoMiniportPins[] = +{ + // KSPIN_TOPO_FMRX + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(FmTopoPinDataRangePointersBridge), // DataRangesCount + FmTopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_IN, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSNODETYPE_FM_RX, // Category + &FMRX_CUSTOM_NAME, // Name + 0 // Reserved + } + }, + // KSPIN_TOPO_FMRX_BRIDGE + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(FmTopoPinDataRangePointersBridge), // DataRangesCount + FmTopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_OUT, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSCATEGORY_AUDIO, // Category + NULL, // Name + 0 // Reserved + } + }, +}; + +//============================================================================= +static +KSJACK_DESCRIPTION FmRxJackDesc = +{ + KSAUDIO_SPEAKER_STEREO, + 0x0000, // no color + eConnTypeOtherAnalog, + eGeoLocNotApplicable, + eGenLocInternal, + ePortConnUnknown, + FALSE +}; + +// Only return a KSJACK_DESCRIPTION for the physical bridge pin. +static +PKSJACK_DESCRIPTION FmJackDescriptions[] = +{ + &FmRxJackDesc, + NULL, +}; + +//============================================================================= +static +PCCONNECTION_DESCRIPTOR FmTopoMiniportConnections[] = +{ + // FromNode, FromPin, ToNode, ToPin + { PCFILTER_NODE, KSPIN_TOPO_FMRX, PCFILTER_NODE, KSPIN_TOPO_FMRX_BRIDGE}, +}; + + +//============================================================================= +static +PCPROPERTY_ITEM PropertiesFmTopoFilter[] = +{ + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_FmRxTopoFilter + }, + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION2, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_FmRxTopoFilter + }, + { + &KSPROPSETID_FMRXTopology, + KSPROPERTY_FMRX_ENDPOINTID, + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_FmRxTopoFilter + }, + { + &KSPROPSETID_FMRXTopology, + KSPROPERTY_FMRX_VOLUME, + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_FmRxTopoFilter + }, + { + &KSPROPSETID_FMRXTopology, + KSPROPERTY_FMRX_ANTENNAENDPOINTID, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_FmRxTopoFilter + }, +}; + +NTSTATUS CFmMiniportWaveRT_EventHandler_JackState +( + _In_ PPCEVENT_REQUEST EventRequest +); + +static PCEVENT_ITEM FmRxJackInfoChangeEvent[] = +{ + { + &KSEVENTSETID_PinCapsChange, // Something changed + KSEVENT_PINCAPS_JACKINFOCHANGE, // Jack Info Changes + KSEVENT_TYPE_ENABLE | KSEVENT_TYPE_BASICSUPPORT, + CFmMiniportWaveRT_EventHandler_JackState + } +}; + +/***************************************************************************** + * PropertiesTopologyFilter + ***************************************************************************** + * Automation table for jack descripton/detection. + */ +DEFINE_PCAUTOMATION_TABLE_PROP_EVENT(AutomationFmTopoFilter, PropertiesFmTopoFilter, FmRxJackInfoChangeEvent); + + +//============================================================================= +static +PCFILTER_DESCRIPTOR FmTopoMiniportFilterDescriptor = +{ + 0, // Version + &AutomationFmTopoFilter, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(FmTopoMiniportPins), // PinCount + FmTopoMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + 0, // NodeCount + NULL, // Nodes + SIZEOF_ARRAY(FmTopoMiniportConnections), // ConnectionCount + FmTopoMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories +}; + +#endif // _SYSVAD_FMTOPTABLE_H_ + + diff --git a/audio/sysvad/PhoneAudioSample/FMTopo.cpp b/audio/sysvad/PhoneAudioSample/FMTopo.cpp new file mode 100644 index 000000000..9c43168fa --- /dev/null +++ b/audio/sysvad/PhoneAudioSample/FMTopo.cpp @@ -0,0 +1,985 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + fmtopo.cpp + +Abstract: + + Implementation of topology miniport for the fm endpoint. + +--*/ + +#pragma warning (disable : 4127) + +#include +#include "simple.h" +#include "mintopo.h" +#include "fmtopo.h" +#include "fmtoptable.h" +#include + + +#pragma code_seg("PAGE") + +// Table of valid render endpoints for FM +KSTOPOLOGY_ENDPOINTID g_EndpointList[] = +{ + { SPEAKER_TOPONAME, KSPIN_TOPO_LINEOUT_DEST }, + { SPEAKER_HEADSET_TOPONAME, KSPIN_TOPO_LINEOUT_DEST }, + { HOSTRENDER_TOPONAME, HOSTRENDER_PIN } +}; + +KSTOPOLOGY_ENDPOINTID g_AntennaEndpoint = +{ + SPEAKER_HEADSET_TOPONAME, KSPIN_TOPO_LINEOUT_DEST +}; + +//============================================================================= +NTSTATUS +CreateFmMiniportTopology +( + _Out_ PUNKNOWN * Unknown, + _In_ REFCLSID, + _In_opt_ PUNKNOWN UnknownOuter, + _When_((PoolType & NonPagedPoolMustSucceed) != 0, + __drv_reportError("Must succeed pool allocations are forbidden. " + "Allocation failures cause a system crash")) + _In_ POOL_TYPE PoolType, + _In_ PUNKNOWN UnknownAdapter, + _In_opt_ PVOID DeviceContext, + _In_ PENDPOINT_MINIPAIR MiniportPair +) +/*++ + +Routine Description: + + Creates a new topology miniport. + +Arguments: + + Unknown - + + RefclsId - + + UnknownOuter - + + PoolType - + + UnknownAdapter - + + DeviceContext - + + MiniportPair - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(Unknown); + ASSERT(MiniportPair); + + UNREFERENCED_PARAMETER(UnknownAdapter); + UNREFERENCED_PARAMETER(DeviceContext); + + CFmMiniportTopology *obj = + new (PoolType, MINWAVERT_POOLTAG) + CFmMiniportTopology( UnknownOuter, + MiniportPair->TopoDescriptor, + MiniportPair->DeviceMaxChannels); + if (NULL == obj) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + obj->AddRef(); + *Unknown = reinterpret_cast(obj); + + return STATUS_SUCCESS; +} // CreateFmMiniportTopology + +CFmMiniportTopology::CFmMiniportTopology +( + _In_opt_ PUNKNOWN UnknownOuter, + _In_ PCFILTER_DESCRIPTOR *FilterDesc, + _In_ USHORT DeviceMaxChannels +) +: CUnknown(UnknownOuter), + CMiniportTopologySYSVAD(FilterDesc, DeviceMaxChannels), + m_FmRxVolume(FMRX_VOLUME_MAXIMUM) +{ + PAGED_CODE(); + + memcpy(&m_FmRxTargetEndpoint, &g_EndpointList[0], sizeof(m_FmRxTargetEndpoint)); +} + + +//============================================================================= +CFmMiniportTopology::~CFmMiniportTopology +( + void +) +/*++ + +Routine Description: + + Topology miniport destructor + +Arguments: + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + DPF_ENTER(("[CFmMiniportTopology::~CFmMiniportTopology]")); +} // ~CFmMiniportTopology + +//============================================================================= +NTSTATUS +CFmMiniportTopology::DataRangeIntersection +( + _In_ ULONG PinId, + _In_ PKSDATARANGE ClientDataRange, + _In_ PKSDATARANGE MyDataRange, + _In_ ULONG OutputBufferLength, + _Out_writes_bytes_to_opt_(OutputBufferLength, *ResultantFormatLength) + PVOID ResultantFormat OPTIONAL, + _Out_ PULONG ResultantFormatLength +) +/*++ + +Routine Description: + + The DataRangeIntersection function determines the highest quality + intersection of two data ranges. + +Arguments: + + PinId - Pin for which data intersection is being determined. + + ClientDataRange - Pointer to KSDATARANGE structure which contains the data range + submitted by client in the data range intersection property + request. + + MyDataRange - Pin's data range to be compared with client's data range. + + OutputBufferLength - Size of the buffer pointed to by the resultant format + parameter. + + ResultantFormat - Pointer to value where the resultant format should be + returned. + + ResultantFormatLength - Actual length of the resultant format that is placed + at ResultantFormat. This should be less than or equal + to OutputBufferLength. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + return + CMiniportTopologySYSVAD::DataRangeIntersection + ( + PinId, + ClientDataRange, + MyDataRange, + OutputBufferLength, + ResultantFormat, + ResultantFormatLength + ); +} // DataRangeIntersection + +//============================================================================= +STDMETHODIMP +CFmMiniportTopology::GetDescription +( + _Out_ PPCFILTER_DESCRIPTOR * OutFilterDescriptor +) +/*++ + +Routine Description: + + The GetDescription function gets a pointer to a filter description. + It provides a location to deposit a pointer in miniport's description + structure. This is the placeholder for the FromNode or ToNode fields in + connections which describe connections to the filter's pins. + +Arguments: + + OutFilterDescriptor - Pointer to the filter description. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(OutFilterDescriptor); + + return CMiniportTopologySYSVAD::GetDescription(OutFilterDescriptor); +} // GetDescription + +//============================================================================= +STDMETHODIMP +CFmMiniportTopology::Init +( + _In_ PUNKNOWN UnknownAdapter, + _In_ PRESOURCELIST ResourceList, + _In_ PPORTTOPOLOGY Port_ +) +/*++ + +Routine Description: + + The Init function initializes the miniport. Callers of this function + should run at IRQL PASSIVE_LEVEL + +Arguments: + + UnknownAdapter - A pointer to the Iuknown interface of the adapter object. + + ResourceList - Pointer to the resource list to be supplied to the miniport + during initialization. The port driver is free to examine the + contents of the ResourceList. The port driver will not be + modify the ResourceList contents. + + Port - Pointer to the topology port object that is linked with this miniport. + +Return Value: + + NT status code. + +--*/ +{ + UNREFERENCED_PARAMETER(ResourceList); + + PAGED_CODE(); + + ASSERT(UnknownAdapter); + ASSERT(Port_); + + DPF_ENTER(("[CFmMiniportTopology::Init]")); + + NTSTATUS ntStatus; + + ntStatus = + CMiniportTopologySYSVAD::Init + ( + UnknownAdapter, + Port_ + ); + + return ntStatus; +} // Init + +//============================================================================= +STDMETHODIMP +CFmMiniportTopology::NonDelegatingQueryInterface +( + _In_ REFIID Interface, + _COM_Outptr_ PVOID * Object +) +/*++ + +Routine Description: + + QueryInterface for MiniportTopology + +Arguments: + + Interface - GUID of the interface + + Object - interface object to be returned. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(Object); + + if (IsEqualGUIDAligned(Interface, IID_IUnknown)) + { + *Object = PVOID(PUNKNOWN(this)); + } + else if (IsEqualGUIDAligned(Interface, IID_IMiniport)) + { + *Object = PVOID(PMINIPORT(this)); + } + else if (IsEqualGUIDAligned(Interface, IID_IMiniportTopology)) + { + *Object = PVOID(PMINIPORTTOPOLOGY(this)); + } + else if (IsEqualGUIDAligned(Interface, IID_IFmTopology)) + { + *Object = PVOID(PFMTOPOLOGY(this)); + } + else + { + *Object = NULL; + } + + if (*Object) + { + // We reference the interface for the caller. + PUNKNOWN(*Object)->AddRef(); + return(STATUS_SUCCESS); + } + + return(STATUS_INVALID_PARAMETER); +} // NonDelegatingQueryInterface + +//============================================================================= +NTSTATUS +CFmMiniportTopology::PropertyHandlerJackDescription +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_Jack, KSPROPERTY_JACK_DESCRIPTION ) + +Arguments: + + PropertyRequest + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandlerJackDescription]")); + + ULONG cJackDescriptions = ARRAYSIZE(FmJackDescriptions); + PKSJACK_DESCRIPTION * JackDescriptions = FmJackDescriptions; + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + ULONG nPinId = (ULONG)-1; + + if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + nPinId = *(PULONG(PropertyRequest->Instance)); + + if ((nPinId < cJackDescriptions) && (JackDescriptions[nPinId] != NULL)) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else + { + ULONG cbNeeded = sizeof(KSMULTIPLE_ITEM) + sizeof(KSJACK_DESCRIPTION); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbNeeded; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbNeeded) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PKSMULTIPLE_ITEM pMI = (PKSMULTIPLE_ITEM)PropertyRequest->Value; + PKSJACK_DESCRIPTION pDesc = (PKSJACK_DESCRIPTION)(pMI+1); + + pMI->Size = cbNeeded; + pMI->Count = 1; + + RtlCopyMemory(pDesc, JackDescriptions[nPinId], sizeof(KSJACK_DESCRIPTION)); + ntStatus = STATUS_SUCCESS; + } + } + } + } + } + + return ntStatus; +} + +//============================================================================= +NTSTATUS +CFmMiniportTopology::PropertyHandlerJackDescription2 +( +_In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + +Handles ( KSPROPSETID_Jack, KSPROPERTY_JACK_DESCRIPTION2 ) + +Arguments: + +PropertyRequest - + +Return Value: + +NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandlerJackDescription2]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + ULONG nPinId = (ULONG)-1; + + ULONG cJackDescriptions = ARRAYSIZE(FmJackDescriptions); + PKSJACK_DESCRIPTION * JackDescriptions = FmJackDescriptions; + + if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + nPinId = *(PULONG(PropertyRequest->Instance)); + + if ((nPinId < cJackDescriptions) && (JackDescriptions[nPinId] != NULL)) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else + { + ULONG cbNeeded = sizeof(KSMULTIPLE_ITEM)+sizeof(KSJACK_DESCRIPTION2); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbNeeded; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbNeeded) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PKSMULTIPLE_ITEM pMI = (PKSMULTIPLE_ITEM)PropertyRequest->Value; + PKSJACK_DESCRIPTION2 pDesc = (PKSJACK_DESCRIPTION2)(pMI + 1); + + pMI->Size = cbNeeded; + pMI->Count = 1; + + RtlZeroMemory(pDesc, sizeof(KSJACK_DESCRIPTION2)); + + // + // Specifies the lower 16 bits of the DWORD parameter. This parameter indicates whether + // the jack is currently active, streaming, idle, or hardware not ready. + // + pDesc->DeviceStateInfo = 0; + + // + // From MSDN: + // "If an audio device lacks jack presence detection, the IsConnected member of + // the KSJACK_DESCRIPTION structure must always be set to TRUE. To remove the + // ambiguity that results from this dual meaning of the TRUE value for IsConnected, + // a client application can call IKsJackDescription2::GetJackDescription2 to read + // the JackCapabilities flag of the KSJACK_DESCRIPTION2 structure. If this flag has + // the JACKDESC2_PRESENCE_DETECT_CAPABILITY bit set, it indicates that the endpoint + // does in fact support jack presence detection. In that case, the return value of + // the IsConnected member can be interpreted to accurately reflect the insertion status + // of the jack." + // + // Bit definitions: + // 0x00000001 - JACKDESC2_PRESENCE_DETECT_CAPABILITY + // 0x00000002 - JACKDESC2_DYNAMIC_FORMAT_CHANGE_CAPABILITY + // + pDesc->JackCapabilities = JACKDESC2_PRESENCE_DETECT_CAPABILITY; + + ntStatus = STATUS_SUCCESS; + } + } + } + } + } + + return ntStatus; +} + +//============================================================================= +NTSTATUS +CFmMiniportTopology::PropertyHandlerFmRxEndpointId +( +_In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + +Handles ( KSPROPSETID_FMRXTopology, KSPROPERTY_FMRX_ENDPOINTID ) + +Arguments: + +PropertyRequest + +Return Value: + +NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandlerFmRxEndpointId]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = STATUS_INVALID_PARAMETER; + ULONG ulExpectedSize = (sizeof(KSPROPERTY_DESCRIPTION)+sizeof(KSPROPERTY_MEMBERSHEADER)+sizeof(g_EndpointList)); + + if (PropertyRequest->ValueSize >= sizeof(KSPROPERTY_DESCRIPTION)) + { + // if return buffer can hold a KSPROPERTY_DESCRIPTION, return it + // + PKSPROPERTY_DESCRIPTION PropDesc = + PKSPROPERTY_DESCRIPTION(PropertyRequest->Value); + + PropDesc->AccessFlags = KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_GET; + PropDesc->DescriptionSize = ulExpectedSize; + PropDesc->PropTypeSet.Set = KSPROPSETID_FMRXTopology; + PropDesc->PropTypeSet.Id = KSPROPERTY_FMRX_ENDPOINTID; + PropDesc->PropTypeSet.Flags = 0; + PropDesc->MembersListCount = 1; + PropDesc->Reserved = 0; + + // buffer is big enough to hold the full data, add that + if (PropertyRequest->ValueSize >= ulExpectedSize) + { + PKSPROPERTY_MEMBERSHEADER MembersHeader = + PKSPROPERTY_MEMBERSHEADER(PropDesc + 1); + + MembersHeader->MembersFlags = KSPROPERTY_MEMBER_VALUES; + MembersHeader->MembersSize = sizeof(KSTOPOLOGY_ENDPOINTID); + MembersHeader->MembersCount = SIZEOF_ARRAY(g_EndpointList); + MembersHeader->Flags = 0; + + memcpy(MembersHeader + 1, g_EndpointList, sizeof(g_EndpointList)); + + // tell them how much space we really used, which controls how much data is copied into the user buffer. + PropertyRequest->ValueSize = ulExpectedSize; + } + else + { + // tell them how much space we really used, which controls how much data is copied into the user buffer. + PropertyRequest->ValueSize = sizeof(KSPROPERTY_DESCRIPTION); + } + + ntStatus = STATUS_SUCCESS; + } + else if (PropertyRequest->ValueSize >= sizeof(ULONG)) + { + // if return buffer can hold a ULONG, return the access flags. + *(PULONG(PropertyRequest->Value)) = KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_GET; + PropertyRequest->ValueSize = sizeof(ULONG); + ntStatus = STATUS_SUCCESS; + } + else if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = ulExpectedSize; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + } + else + { + ntStatus = + ValidatePropertyParams + ( + PropertyRequest, + sizeof(KSTOPOLOGY_ENDPOINTID), + 0 + ); + + if (NT_SUCCESS(ntStatus)) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PKSTOPOLOGY_ENDPOINTID EndpointId = static_cast(PropertyRequest->Value); + + memcpy(EndpointId, &m_FmRxTargetEndpoint, sizeof(m_FmRxTargetEndpoint)); + + ntStatus = STATUS_SUCCESS; + } + else if (PropertyRequest->Verb & KSPROPERTY_TYPE_SET) + { + PKSTOPOLOGY_ENDPOINTID EndpointId = static_cast(PropertyRequest->Value); + + ntStatus = STATUS_INVALID_PARAMETER; + for (int i = 0; i < SIZEOF_ARRAY(g_EndpointList); i++) + { + if (g_EndpointList[i].PinId == EndpointId->PinId && + 0 == _wcsnicmp(g_EndpointList[i].TopologyName, EndpointId->TopologyName, MAX_PATH)) + { + memcpy(&m_FmRxTargetEndpoint, EndpointId, sizeof(m_FmRxTargetEndpoint)); + + if (HOSTRENDER_PIN == EndpointId->PinId && + 0 == _wcsnicmp(HOSTRENDER_TOPONAME, EndpointId->TopologyName, MAX_PATH)) + { + // This is an indication that FM audio is being routed through OS (For example, user plugs in + // USB headset for FM audio) and driver must stop rendering FM audio to any physical audio endpoint. + } + else + { + // Route FM audio to this new endpoint. + } + ntStatus = STATUS_SUCCESS; + break; + } + } + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + } + } + + return ntStatus; +} + +//============================================================================= +NTSTATUS +CFmMiniportTopology::PropertyHandlerFmRxVolume +( +_In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + +Handles ( KSPROPSETID_FMRXTopology, KSPROPERTY_FMRX_VOLUME ) + +Arguments: + +PropertyRequest + +Return Value: + +NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandlerFmRxVolume]")); + + UNREFERENCED_PARAMETER(PropertyRequest); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupportVolume + ( + PropertyRequest, + 1 // FM audio technically supports Stereo, but we only support a single volume + ); + + ULONG cbFullProperty = + sizeof(KSPROPERTY_DESCRIPTION)+ + sizeof(KSPROPERTY_MEMBERSHEADER)+ + sizeof(KSPROPERTY_STEPPING_LONG); + + if (NT_SUCCESS(ntStatus) && PropertyRequest->ValueSize >= cbFullProperty) + { + // Access proper values + PKSPROPERTY_DESCRIPTION PropDesc = + PKSPROPERTY_DESCRIPTION(PropertyRequest->Value); + PKSPROPERTY_MEMBERSHEADER Members = + PKSPROPERTY_MEMBERSHEADER(PropDesc + 1); + PKSPROPERTY_STEPPING_LONG Range = + PKSPROPERTY_STEPPING_LONG(Members + 1); + + Members->Flags = 0; + + Range->Bounds.SignedMaximum = FMRX_VOLUME_MAXIMUM; + Range->Bounds.SignedMinimum = FMRX_VOLUME_MINIMUM; + Range->SteppingDelta = FMRX_VOLUME_STEPPING; + } + } + else + { + ntStatus = + ValidatePropertyParams + ( + PropertyRequest, + sizeof(LONG), // volume value is a LONG + 0 // No support for channel since this is a property + // on the topo filter, not a volume node. + ); + + + if (NT_SUCCESS(ntStatus)) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + LONG* volume = (LONG*)PropertyRequest->Value; + // Return current FM volume + *volume = m_FmRxVolume; + ntStatus = STATUS_SUCCESS; + } + else if (PropertyRequest->Verb & KSPROPERTY_TYPE_SET) + { + LONG* volume = (LONG*)PropertyRequest->Value; + + LONG normalizedVolume = + VALUE_NORMALIZE_IN_RANGE_EX + ( + *volume, + FMRX_VOLUME_MINIMUM, + FMRX_VOLUME_MAXIMUM, + FMRX_VOLUME_STEPPING + ); + if (normalizedVolume == *volume) + { + m_FmRxVolume = *volume; + ntStatus = STATUS_SUCCESS; + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + } + } + + return ntStatus; +} + +//============================================================================= +NTSTATUS +CFmMiniportTopology::PropertyHandlerFmRxAntennaEndpointId +( +_In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + +Handles ( KSPROPSETID_FMRXTopology, KSPROPERTY_FMRX_ANTENNAENDPOINTID ) + +Arguments: + +PropertyRequest + +Return Value: + +NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandlerFmRxAntennaEndpoint]")); + + UNREFERENCED_PARAMETER(PropertyRequest); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ULONG flags = PropertyRequest->PropertyItem->Flags; + ntStatus = PropertyHandler_BasicSupport(PropertyRequest, flags, VT_ILLEGAL); + } + else + { + ntStatus = + ValidatePropertyParams + ( + PropertyRequest, + sizeof(KSTOPOLOGY_ENDPOINTID), + 0 + ); + + + if (NT_SUCCESS(ntStatus)) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PKSTOPOLOGY_ENDPOINTID pEndpointId = (PKSTOPOLOGY_ENDPOINTID)PropertyRequest->Value; + memcpy(pEndpointId, &g_AntennaEndpoint, sizeof(*pEndpointId)); + ntStatus = STATUS_SUCCESS; + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + } + } + + return ntStatus; +} + +//============================================================================= +NTSTATUS +PropertyHandler_FmRxTopoFilter +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Redirects property request to miniport object + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_FmRxTopoFilter]")); + + // PropertryRequest structure is filled by portcls. + // MajorTarget is a pointer to miniport object for miniports. + // + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + PCFmMiniportTopology pMiniport = (PCFmMiniportTopology)PropertyRequest->MajorTarget; + + if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_Jack)) + { + if (PropertyRequest->PropertyItem->Id == KSPROPERTY_JACK_DESCRIPTION) + { + ntStatus = pMiniport->PropertyHandlerJackDescription(PropertyRequest); + } + else if (PropertyRequest->PropertyItem->Id == KSPROPERTY_JACK_DESCRIPTION2) + { + ntStatus = pMiniport->PropertyHandlerJackDescription2(PropertyRequest); + } + } + else if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_FMRXTopology)) + { + if (PropertyRequest->PropertyItem->Id == KSPROPERTY_FMRX_ENDPOINTID) + { + ntStatus = pMiniport->PropertyHandlerFmRxEndpointId(PropertyRequest); + } + else if (PropertyRequest->PropertyItem->Id == KSPROPERTY_FMRX_VOLUME) + { + ntStatus = pMiniport->PropertyHandlerFmRxVolume(PropertyRequest); + } + else if (PropertyRequest->PropertyItem->Id == KSPROPERTY_FMRX_ANTENNAENDPOINTID) + { + ntStatus = pMiniport->PropertyHandlerFmRxAntennaEndpointId(PropertyRequest); + } + } + + return ntStatus; +} // PropertyHandler_FmRxTopoFilter + +#pragma code_seg("PAGE") +STDMETHODIMP_(NTSTATUS) +CFmMiniportTopology::UpdateTopologyJackState +( + _In_ BOOL NewState +) +{ + PAGED_CODE (); + + FmRxJackDesc.IsConnected = NewState; + + // we have multiple pins which share the same jack state, + // notify for every pin using that jack state + GenerateEventList( + (GUID*)&KSEVENTSETID_PinCapsChange, // event set. NULL is a wild card for all events. + KSEVENT_PINCAPS_JACKINFOCHANGE, // event ID. + TRUE, // use pid ID. + KSPIN_TOPO_FMRX, // pin ID. + FALSE, // do not use node ID. + ULONG(-1)); // node ID, not used. + + return STATUS_SUCCESS; +} + +#pragma code_seg() +NTSTATUS CFmMiniportWaveRT_EventHandler_JackState +( + _In_ PPCEVENT_REQUEST EventRequest +) +{ + CFmMiniportTopology* miniport = reinterpret_cast(EventRequest->MajorTarget); + return miniport->EventHandler_JackState(EventRequest); +} + +NTSTATUS CFmMiniportTopology::EventHandler_JackState +( + _In_ PPCEVENT_REQUEST EventRequest +) +{ + if (EventRequest->Verb == PCEVENT_VERB_ADD) + { + m_PortEvents->AddEventToEventList(EventRequest->EventEntry); + } + + return STATUS_SUCCESS; +} + + diff --git a/audio/sysvad/PhoneAudioSample/FMTopo.h b/audio/sysvad/PhoneAudioSample/FMTopo.h new file mode 100644 index 000000000..ae279bf14 --- /dev/null +++ b/audio/sysvad/PhoneAudioSample/FMTopo.h @@ -0,0 +1,152 @@ + +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + FMtopo.h + +Abstract: + + Declaration of topology miniport for the fm endpoint. + +--*/ + +#ifndef _SYSVAD_FMTOPO_H_ +#define _SYSVAD_FMTOPO_H_ + +#include "basetopo.h" + +// make sure all of these names matches with KSNAME_* in the inf's [Strings] section +#define SPEAKER_TOPONAME L"TopologySpeaker" +#define SPEAKER_HEADSET_TOPONAME L"TopologySpeakerHeadset" + +// Special routing endpoint +#define HOSTRENDER_TOPONAME L"HostRender" +#define HOSTRENDER_PIN 0 + +// FM Volume Stepping information +#define FMRX_VOLUME_MAXIMUM 0 // 0 dB +#define FMRX_VOLUME_MINIMUM (-96 * 0x10000) // -96 dB +#define FMRX_VOLUME_STEPPING (0x8000) // 0.5 dB steps + + +//============================================================================= +// Defines +//============================================================================= +// {949F42F1-A5F5-4FBD-B4BB-C543ACC24AE0} +DEFINE_GUID(IID_IFmTopology, + 0x949f42f1, 0xa5f5, 0x4fbd, 0xb4, 0xbb, 0xc5, 0x43, 0xac, 0xc2, 0x4a, 0xe0); + + +//============================================================================= +// Interfaces +//============================================================================= + +/////////////////////////////////////////////////////////////////////////////// +// IFmTopology +// +DECLARE_INTERFACE_(IFmTopology, IMiniportTopology) +{ + STDMETHOD_(NTSTATUS, UpdateTopologyJackState) + ( + THIS_ + _In_ BOOL NewState + ) PURE; +}; + +typedef IFmTopology *PFMTOPOLOGY; + +//============================================================================= +// Classes +//============================================================================= + +/////////////////////////////////////////////////////////////////////////////// +// CFmMiniportTopology +// + +class CFmMiniportTopology : + public CMiniportTopologySYSVAD, + public IFmTopology, + public CUnknown +{ + private: + KSTOPOLOGY_ENDPOINTID m_FmRxTargetEndpoint; + KSTOPOLOGY_ENDPOINTID m_FmRxAntennaEndpoint; + ULONG m_FmRxVolume; + + public: + DECLARE_STD_UNKNOWN(); + CFmMiniportTopology + ( + _In_opt_ PUNKNOWN UnknownOuter, + _In_ PCFILTER_DESCRIPTOR *FilterDesc, + _In_ USHORT DeviceMaxChannels + ); + + ~CFmMiniportTopology(); + + IMP_IMiniportTopology; + + NTSTATUS PropertyHandlerJackSinkInfo + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + + NTSTATUS PropertyHandlerJackDescription + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + + NTSTATUS PropertyHandlerJackDescription2 + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + + NTSTATUS PropertyHandlerFmRxEndpointId + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + + NTSTATUS PropertyHandlerFmRxVolume + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + + NTSTATUS PropertyHandlerFmRxAntennaEndpointId + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + + NTSTATUS EventHandler_JackState + ( + _In_ PPCEVENT_REQUEST _pEventRequest + ); + + STDMETHODIMP_(NTSTATUS) UpdateTopologyJackState + ( + _In_ BOOL NewState + ); +}; + +typedef CFmMiniportTopology *PCFmMiniportTopology; + +NTSTATUS +CreateFmMiniportTopology( + _Out_ PUNKNOWN * Unknown, + _In_ REFCLSID, + _In_opt_ PUNKNOWN UnknownOuter, + _When_((PoolType & NonPagedPoolMustSucceed) != 0, + __drv_reportError("Must succeed pool allocations are forbidden. " + "Allocation failures cause a system crash")) + _In_ POOL_TYPE PoolType, + _In_ PUNKNOWN UnknownAdapter, + _In_opt_ PVOID DeviceContext, + _In_ PENDPOINT_MINIPAIR MiniportPair + ); + +NTSTATUS PropertyHandler_FmRxTopoFilter(_In_ PPCPROPERTY_REQUEST PropertyRequest); + +#endif // _SYSVAD_FMTOPO_H_ + diff --git a/audio/sysvad/PhoneAudioSample/FMWavTable.h b/audio/sysvad/PhoneAudioSample/FMWavTable.h new file mode 100644 index 000000000..a549e06d8 --- /dev/null +++ b/audio/sysvad/PhoneAudioSample/FMWavTable.h @@ -0,0 +1,249 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + fmwavtable.h + +Abstract: + + Declaration of wave miniport tables for the fm endpoint. + + Note: the FM in this sample shows how to do h/w loopback + for non-offloaded devices. + +--*/ + +#ifndef _SYSVAD_FMWAVTABLE_H_ +#define _SYSVAD_FMWAVTABLE_H_ + + +#define FMRX_DEVICE_MAX_CHANNELS 2 // Max Channels. + +#define FMRX_MAX_CHANNELS 2 // Max Channels. +#define FMRX_MIN_BITS_PER_SAMPLE 16 // Min Bits Per Sample +#define FMRX_MAX_BITS_PER_SAMPLE 16 // Max Bits Per Sample +#define FMRX_MIN_SAMPLE_RATE 48000 // Min Sample Rate +#define FMRX_MAX_SAMPLE_RATE 48000 // Max Sample Rate +#define FMRX_MAX_INPUT_SYSTEM_STREAMS 1 // Raw stream + + +//============================================================================= +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE FmRxPinSupportedDeviceFormats[] = +{ + { // 0 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 48000, + 192000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +// +// Supported modes (only on streaming pins). +// +static +MODE_AND_DEFAULT_FORMAT FmRxPinSupportedDeviceModes[] = +{ + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_RAW, + &FmRxPinSupportedDeviceFormats[SIZEOF_ARRAY(FmRxPinSupportedDeviceFormats) - 1].DataFormat + } +}; + +//============================================================================= +// The entries here must follow the same order as the filter's pin +// descriptor array. +// +static +PIN_DEVICE_FORMATS_AND_MODES FmRxPinDeviceFormatsAndModes[] = +{ + { // RX + SystemCapturePin, + FmRxPinSupportedDeviceFormats, + SIZEOF_ARRAY(FmRxPinSupportedDeviceFormats), + FmRxPinSupportedDeviceModes, + SIZEOF_ARRAY(FmRxPinSupportedDeviceModes) + }, + { + BridgePin, + NULL, + 0, + NULL, + 0, + }, +}; + +//============================================================================= +static +KSDATARANGE_AUDIO FmPinDataRangesStream[] = +{ + { // 0 + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + FMRX_MAX_CHANNELS, + FMRX_MIN_BITS_PER_SAMPLE, + FMRX_MAX_BITS_PER_SAMPLE, + FMRX_MIN_SAMPLE_RATE, + FMRX_MAX_SAMPLE_RATE + }, +}; + +static +PKSDATARANGE FmPinDataRangePointersStream[] = +{ + PKSDATARANGE(&FmPinDataRangesStream[0]), + PKSDATARANGE(&PinDataRangeAttributeList) +}; + +//============================================================================= +static +KSDATARANGE FmPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +static +PKSDATARANGE FmPinDataRangePointersBridge[] = +{ + &FmPinDataRangesBridge[0] +}; + + +//============================================================================= +static +PCPIN_DESCRIPTOR FmWaveMiniportPins[] = +{ + // Wave In Streaming Pin KSPIN_WAVE_FMRX + { + FMRX_MAX_INPUT_SYSTEM_STREAMS, + FMRX_MAX_INPUT_SYSTEM_STREAMS, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(FmPinDataRangePointersStream), + FmPinDataRangePointersStream, + KSPIN_DATAFLOW_OUT, + KSPIN_COMMUNICATION_SINK, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, + // Wave In Bridge Pin KSPIN_WAVE_FMRX_BRIDGE + { + 0, + 0, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(FmPinDataRangePointersBridge), + FmPinDataRangePointersBridge, + KSPIN_DATAFLOW_IN, + KSPIN_COMMUNICATION_NONE, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, +}; + +//============================================================================= +// +// ----- +// | | +// FM RX bridge pin 1 -->| |--> 0 FM RX pin +// | | +// ----- + +static +PCCONNECTION_DESCRIPTOR FmWaveMiniportConnections[] = +{ + { PCFILTER_NODE, KSPIN_WAVE_FMRX_BRIDGE, PCFILTER_NODE, KSPIN_WAVE_FMRX }, +}; + +//============================================================================= +static +PCPROPERTY_ITEM PropertiesFmWaveFilter[] = +{ + { + &KSPROPSETID_Pin, + KSPROPERTY_PIN_PROPOSEDATAFORMAT, + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_WaveFilter + }, + + { + &KSPROPSETID_FMRXControl, + KSPROPERTY_FMRX_STATE, + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_FmRxWaveFilter + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationFmWaveFilter, PropertiesFmWaveFilter); + +//============================================================================= +static +PCFILTER_DESCRIPTOR FmWaveMiniportFilterDescriptor = +{ + 0, // Version + &AutomationFmWaveFilter, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(FmWaveMiniportPins), // PinCount + FmWaveMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + 0, // NodeCount + NULL, // Nodes + SIZEOF_ARRAY(FmWaveMiniportConnections), // ConnectionCount + FmWaveMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories - use defaults (audio, render, capture) +}; + +#endif // _SYSVAD_FMWAVTABLE_H_ + diff --git a/audio/sysvad/PhoneAudioSample/FMWave.cpp b/audio/sysvad/PhoneAudioSample/FMWave.cpp new file mode 100644 index 000000000..4d571f578 --- /dev/null +++ b/audio/sysvad/PhoneAudioSample/FMWave.cpp @@ -0,0 +1,389 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + fmwave.cpp + +Abstract: + + Implementation of wavert miniport for fm + +--*/ + +#pragma warning (disable : 4127) + +#include +#include "simple.h" +#include "FmWave.h" +#include "FmTopo.h" +#include "FmToptable.h" + +//============================================================================= +// CFmMiniportWaveRT +//============================================================================= + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CreateFmMiniportWaveRT +( + _Out_ PUNKNOWN * Unknown, + _In_ REFCLSID, + _In_opt_ PUNKNOWN UnknownOuter, + _When_((PoolType & NonPagedPoolMustSucceed) != 0, + __drv_reportError("Must succeed pool allocations are forbidden. " + "Allocation failures cause a system crash")) + _In_ POOL_TYPE PoolType, + _In_ PUNKNOWN UnknownAdapter, + _In_opt_ PVOID DeviceContext, + _In_ PENDPOINT_MINIPAIR MiniportPair +) +/*++ + +Routine Description: + + Create the wavert miniport. + +Arguments: + + Unknown - + + RefClsId - + + UnknownOuter - + + PoolType - + + UnkownAdapter - + + DeviceContext - + + MiniportPair - + +Return Value: + + NT status code. + +--*/ +{ + UNREFERENCED_PARAMETER(UnknownOuter); + + PAGED_CODE(); + + ASSERT(Unknown); + ASSERT(MiniportPair); + + CMiniportWaveRT *obj = new (PoolType, MINWAVERT_POOLTAG) CFmMiniportWaveRT + ( + UnknownAdapter, + MiniportPair, + DeviceContext + ); + if (NULL == obj) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + obj->AddRef(); + *Unknown = reinterpret_cast(obj); + + return STATUS_SUCCESS; +} + +//============================================================================= +#pragma code_seg("PAGE") +CFmMiniportWaveRT::~CFmMiniportWaveRT +( + void +) +/*++ + +Routine Description: + + Destructor for wavert miniport + +Arguments: + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + DPF_ENTER(("[CFmMiniportWaveRT::~CFmMiniportWaveRT]")); + +} // ~CFmMiniportWaveRT + + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP +CFmMiniportWaveRT::NonDelegatingQueryInterface +( + _In_ REFIID Interface, + _COM_Outptr_ PVOID * Object +) +/*++ + +Routine Description: + + QueryInterface for CFmMiniportWaveRT + +Arguments: + + Interface - GUID of the interface + + Object - interface object to be returned. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(Object); + + if (IsEqualGUIDAligned(Interface, IID_IAdapterPowerManagement)) + { + *Object = PVOID(PADAPTERPOWERMANAGEMENT(this)); + } + else + { + *Object = NULL; + } + + if (*Object) + { + // We reference the interface for the caller. + + PUNKNOWN(*Object)->AddRef(); + return STATUS_SUCCESS; + } + + return CMiniportWaveRT::NonDelegatingQueryInterface(Interface, Object); +} + +#pragma code_seg("PAGE") +NTSTATUS +PropertyHandler_FmRxWaveFilter +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Redirects general property request to miniport object + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + CFmMiniportWaveRT* pWaveHelper = reinterpret_cast(PropertyRequest->MajorTarget); + + if (pWaveHelper == NULL) + { + return STATUS_INVALID_PARAMETER; + } + + pWaveHelper->AddRef(); + + if (pWaveHelper->m_DeviceType == eFmRxDevice && + IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_FMRXControl)) + { + switch (PropertyRequest->PropertyItem->Id) + { + case KSPROPERTY_FMRX_STATE: + ntStatus = pWaveHelper->PropertyHandler_FmRxState(PropertyRequest); + break; + + default: + DPF(D_TERSE, ("[PropertyHandler_FmRxWaveFilter: Invalid Device Request")); + } + } + else + { + ntStatus = PropertyHandler_WaveFilter(PropertyRequest); + } + + pWaveHelper->Release(); + + return ntStatus; +} // PropertyHandler_FmRxWaveFilter + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CFmMiniportWaveRT::PropertyHandler_FmRxState +( +_In_ PPCPROPERTY_REQUEST PropertyRequest +) +{ + NTSTATUS ntStatus = STATUS_INVALID_PARAMETER; + + PAGED_CODE(); + + DPF_ENTER(("[CFmMiniportWaveRT::PropertyHandler_FmRxState]")); + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ULONG flags = PropertyRequest->PropertyItem->Flags; + ntStatus = PropertyHandler_BasicSupport(PropertyRequest, flags, VT_ILLEGAL); + } + else + { + ntStatus = + ValidatePropertyParams + ( + PropertyRequest, + sizeof(BOOL), + 0 + ); + + if (NT_SUCCESS(ntStatus)) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + BOOL* fmrxState = (BOOL*)PropertyRequest->Value; + *fmrxState = m_bFmRxState; + ntStatus = STATUS_SUCCESS; + } + else if (PropertyRequest->Verb & KSPROPERTY_TYPE_SET) + { + BOOL fmrxNewState = *(BOOL*)PropertyRequest->Value; + if (fmrxNewState == m_bFmRxState) + { + ntStatus = STATUS_SUCCESS; + } + else + { + // Turn on FM hardware + m_bFmRxState = fmrxNewState; + + // NOTE: Set the Jack state to Active ONLY if the driver supports + // recording FM data. If the driver doesn't support recording FM + // data, the jack state should never be changed to Active + ntStatus = UpdateTopologyJackState(m_bFmRxState); + + // When we're using FM, we need idle power management disabled. + // passing true here enables power management, false disables. + // when FM is enabled (true), we want to pass false do disable idle + // power management + m_pAdapterCommon->SetIdlePowerManagement(m_pMiniportPair, m_bFmRxState?FALSE:TRUE); + } + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + } + } + + return ntStatus; +} // PropertyHandler_FmRxState + + + +#pragma code_seg("PAGE") +NTSTATUS +CFmMiniportWaveRT::UpdateTopologyJackState +( + _In_ BOOL NewState +) +{ + PAGED_CODE (); + NTSTATUS ntStatus = STATUS_SUCCESS; + IUnknown *pUnknown = NULL; + IFmTopology *pFmTopology = NULL; + + // GetFilters returns the topology port, then topology miniport, then wave port, then wave miniport. + // we only need the topology miniport. + ntStatus = m_pAdapterCommon->GetFilters(m_pMiniportPair, NULL, &pUnknown, NULL, NULL); + if (NT_SUCCESS(ntStatus)) + { + ntStatus = pUnknown->QueryInterface(IID_IFmTopology, (PVOID *)&pFmTopology); + if (NT_SUCCESS(ntStatus)) + { + ntStatus = pFmTopology->UpdateTopologyJackState(NewState); + } + } + + SAFE_RELEASE(pFmTopology); + SAFE_RELEASE(pUnknown); + + return STATUS_SUCCESS; +} +#pragma code_seg() + + +//============================================================================= +#pragma code_seg() +STDMETHODIMP_(void) +CFmMiniportWaveRT::PowerChangeState +( + _In_ POWER_STATE NewState +) +{ + DPF_ENTER(("[CFmMiniportWaveRT::PowerChangeState]")); + UNREFERENCED_PARAMETER(NewState); + + /* + Turn on and off fm specific hardware here, if applicable. + */ + +} // PowerStateChange + +//============================================================================= +#pragma code_seg() +STDMETHODIMP_(NTSTATUS) +CFmMiniportWaveRT::QueryDeviceCapabilities +( + _Inout_updates_bytes_(sizeof(DEVICE_CAPABILITIES)) PDEVICE_CAPABILITIES PowerDeviceCaps +) +{ + UNREFERENCED_PARAMETER(PowerDeviceCaps); + + /* + Unused at the miniport layer. Any device capabilities should be reported in CAdapterCommon + */ + + return (STATUS_SUCCESS); +} // QueryDeviceCapabilities + +//============================================================================= +#pragma code_seg() +STDMETHODIMP_(NTSTATUS) +CFmMiniportWaveRT::QueryPowerChangeState +( + _In_ POWER_STATE NewStateQuery +) +{ + UNREFERENCED_PARAMETER(NewStateQuery); + + DPF_ENTER(("[CFmMiniportWaveRT::QueryPowerChangeState]")); + + // if we're in a call state other than disabled, then we need the hardware turned on + if (m_bFmRxState) + { + return STATUS_RESOURCE_IN_USE; + } + + return STATUS_SUCCESS; +} // QueryPowerChangeState + + diff --git a/audio/sysvad/PhoneAudioSample/FMWave.h b/audio/sysvad/PhoneAudioSample/FMWave.h new file mode 100644 index 000000000..1134b6805 --- /dev/null +++ b/audio/sysvad/PhoneAudioSample/FMWave.h @@ -0,0 +1,84 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + fmwave.h + +Abstract: + + Definition of wavert miniport class for fm + +--*/ + +#include "MinWaveRT.h" + +#ifndef _SYSVAD_FMMINWAVERT_H_ +#define _SYSVAD_FMMINWAVERT_H_ + +NTSTATUS +CreateFmMiniportWaveRT +( + _Out_ PUNKNOWN * Unknown, + _In_ REFCLSID, + _In_opt_ PUNKNOWN UnknownOuter, + _When_((PoolType & NonPagedPoolMustSucceed) != 0, + __drv_reportError("Must succeed pool allocations are forbidden. " + "Allocation failures cause a system crash")) + _In_ POOL_TYPE PoolType, + _In_ PUNKNOWN UnknownAdapter, + _In_opt_ PVOID DeviceContext, + _In_ PENDPOINT_MINIPAIR MiniportPair +); + +//============================================================================= +// Classes +//============================================================================= +/////////////////////////////////////////////////////////////////////////////// +// CFmMiniportWaveRT +// +class CFmMiniportWaveRT : + public CMiniportWaveRT, + public IAdapterPowerManagement +{ +private: + BOOL m_bFmRxState; + +public: + DECLARE_STD_UNKNOWN(); + IMP_IAdapterPowerManagement; + + CFmMiniportWaveRT( + _In_ PUNKNOWN UnknownAdapter, + _In_ PENDPOINT_MINIPAIR MiniportPair, + _In_opt_ PVOID DeviceContext + ) + :CMiniportWaveRT(UnknownAdapter, MiniportPair, DeviceContext), + m_bFmRxState(FALSE) + { + } + + ~CFmMiniportWaveRT(); + + friend NTSTATUS PropertyHandler_FmRxWaveFilter + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + +public: + NTSTATUS PropertyHandler_FmRxState + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + +private: + NTSTATUS UpdateTopologyJackState + ( + _In_ BOOL NewState + ); +}; +typedef CFmMiniportWaveRT *PCFmMiniportWaveRT; + +#endif // _SYSVAD_FMMINWAVERT_H_ + diff --git a/audio/sysvad/PhoneAudioSample/PhoneAudioSample.vcxproj b/audio/sysvad/PhoneAudioSample/PhoneAudioSample.vcxproj new file mode 100644 index 000000000..02fe961a1 --- /dev/null +++ b/audio/sysvad/PhoneAudioSample/PhoneAudioSample.vcxproj @@ -0,0 +1,300 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {43FF11E5-B4C4-409A-AE4B-13342918B6F8} + $(MSBuildProjectName) + 1 + false + true + Debug + Win32 + {05A9D8CD-F200-4641-A6EE-EF0F678D00EA} + + + + 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) + + + + + + + + + + + + + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\portcls.lib;$(DDK_LIB_PATH)\stdunk.lib;$(DDK_LIB_PATH)\libcntpr.lib + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);.. + %(PreprocessorDefinitions);_WIN32;UNICODE;_UNICODE;PC_IMPLEMENTATION + %(PreprocessorDefinitions);DEBUG_LEVEL=DEBUGLVL_TERSE + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);.. + true + Level4 + %(PreprocessorDefinitions);_WIN32;UNICODE;_UNICODE;PC_IMPLEMENTATION + %(PreprocessorDefinitions);DEBUG_LEVEL=DEBUGLVL_TERSE + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);.. + %(PreprocessorDefinitions);_WIN32;UNICODE;_UNICODE;PC_IMPLEMENTATION + %(PreprocessorDefinitions);DEBUG_LEVEL=DEBUGLVL_TERSE + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\portcls.lib;$(DDK_LIB_PATH)\stdunk.lib;$(DDK_LIB_PATH)\libcntpr.lib + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);.. + %(PreprocessorDefinitions);_WIN32;UNICODE;_UNICODE;PC_IMPLEMENTATION + %(PreprocessorDefinitions);DEBUG_LEVEL=DEBUGLVL_TERSE + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);.. + true + Level4 + %(PreprocessorDefinitions);_WIN32;UNICODE;_UNICODE;PC_IMPLEMENTATION + %(PreprocessorDefinitions);DEBUG_LEVEL=DEBUGLVL_TERSE + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);.. + %(PreprocessorDefinitions);_WIN32;UNICODE;_UNICODE;PC_IMPLEMENTATION + %(PreprocessorDefinitions);DEBUG_LEVEL=DEBUGLVL_TERSE + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\portcls.lib;$(DDK_LIB_PATH)\stdunk.lib;$(DDK_LIB_PATH)\libcntpr.lib + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);.. + %(PreprocessorDefinitions);_WIN32;UNICODE;_UNICODE;PC_IMPLEMENTATION + %(PreprocessorDefinitions);DEBUG_LEVEL=DEBUGLVL_TERSE + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);.. + true + Level4 + %(PreprocessorDefinitions);_WIN32;UNICODE;_UNICODE;PC_IMPLEMENTATION + %(PreprocessorDefinitions);DEBUG_LEVEL=DEBUGLVL_TERSE + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);.. + %(PreprocessorDefinitions);_WIN32;UNICODE;_UNICODE;PC_IMPLEMENTATION + %(PreprocessorDefinitions);DEBUG_LEVEL=DEBUGLVL_TERSE + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\portcls.lib;$(DDK_LIB_PATH)\stdunk.lib;$(DDK_LIB_PATH)\libcntpr.lib + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);.. + %(PreprocessorDefinitions);_WIN32;UNICODE;_UNICODE;PC_IMPLEMENTATION + %(PreprocessorDefinitions);DEBUG_LEVEL=DEBUGLVL_TERSE + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);.. + true + Level4 + %(PreprocessorDefinitions);_WIN32;UNICODE;_UNICODE;PC_IMPLEMENTATION + %(PreprocessorDefinitions);DEBUG_LEVEL=DEBUGLVL_TERSE + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);.. + %(PreprocessorDefinitions);_WIN32;UNICODE;_UNICODE;PC_IMPLEMENTATION + %(PreprocessorDefinitions);DEBUG_LEVEL=DEBUGLVL_TERSE + + + + PhoneAudioSample + + + PhoneAudioSample + + + PhoneAudioSample + + + PhoneAudioSample + + + + %(AdditionalDependencies);.\..\EndpointsCommon\$(IntDir)\EndpointsCommon.lib + + + %(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS + %(PreprocessorDefinitions);_USE_IPortClsRuntimePower + %(AdditionalIncludeDirectories);..\EndpointsCommon + + + + + %(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS + %(PreprocessorDefinitions);_USE_IPortClsRuntimePower + %(AdditionalIncludeDirectories);..\EndpointsCommon + + + %(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS + %(PreprocessorDefinitions);_USE_IPortClsRuntimePower + %(AdditionalIncludeDirectories);..\EndpointsCommon + + + + + %(AdditionalDependencies);.\..\EndpointsCommon\$(IntDir)\EndpointsCommon.lib + + + %(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS + %(PreprocessorDefinitions);_USE_IPortClsRuntimePower + %(AdditionalIncludeDirectories);..\EndpointsCommon + + + + + %(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS + %(PreprocessorDefinitions);_USE_IPortClsRuntimePower + %(AdditionalIncludeDirectories);..\EndpointsCommon + + + %(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS + %(PreprocessorDefinitions);_USE_IPortClsRuntimePower + %(AdditionalIncludeDirectories);..\EndpointsCommon + + + + + %(AdditionalDependencies);.\..\EndpointsCommon\$(IntDir)\EndpointsCommon.lib + + + %(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS + %(PreprocessorDefinitions);_USE_IPortClsRuntimePower + %(AdditionalIncludeDirectories);..\EndpointsCommon + + + + + %(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS + %(PreprocessorDefinitions);_USE_IPortClsRuntimePower + %(AdditionalIncludeDirectories);..\EndpointsCommon + + + %(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS + %(PreprocessorDefinitions);_USE_IPortClsRuntimePower + %(AdditionalIncludeDirectories);..\EndpointsCommon + + + + + %(AdditionalDependencies);.\..\EndpointsCommon\$(IntDir)\EndpointsCommon.lib + + + %(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS + %(PreprocessorDefinitions);_USE_IPortClsRuntimePower + %(AdditionalIncludeDirectories);..\EndpointsCommon + + + + + %(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS + %(PreprocessorDefinitions);_USE_IPortClsRuntimePower + %(AdditionalIncludeDirectories);..\EndpointsCommon + + + %(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS + %(PreprocessorDefinitions);_USE_IPortClsRuntimePower + %(AdditionalIncludeDirectories);..\EndpointsCommon + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/audio/sysvad/PhoneAudioSample/PhoneAudioSample.vcxproj.Filters b/audio/sysvad/PhoneAudioSample/PhoneAudioSample.vcxproj.Filters new file mode 100644 index 000000000..369dd396e --- /dev/null +++ b/audio/sysvad/PhoneAudioSample/PhoneAudioSample.vcxproj.Filters @@ -0,0 +1,73 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {25674444-F6A7-49F9-A19E-92D6B2B8C1CE} + + + h;hpp;hxx;hm;inl;inc;xsd + {9F8DB742-298E-4030-84A4-9B1F2C92C863} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {38FD0692-2121-4F6C-BA1E-76CCDF3C0FF9} + + + inf;inv;inx;mof;mc; + {E6A2DFAA-E516-4CB2-86BA-6A2985FEEEAB} + + + + + 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/audio/sysvad/PhoneAudioSample/handsetmictopo.cpp b/audio/sysvad/PhoneAudioSample/handsetmictopo.cpp new file mode 100644 index 000000000..d4854a90b --- /dev/null +++ b/audio/sysvad/PhoneAudioSample/handsetmictopo.cpp @@ -0,0 +1,274 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + handsetmictopo.cpp + +Abstract: + + Implementation of topology miniport for the handset mic (internal). + +--*/ + +#pragma warning (disable : 4127) + +#include +#include "simple.h" +#include "mintopo.h" +#include "handsetmictopo.h" +#include "handsetmictoptable.h" + + +#pragma code_seg("PAGE") + +//============================================================================= +NTSTATUS +PropertyHandler_HandsetMicJackDescription +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_Jack, KSPROPERTY_JACK_DESCRIPTION ) + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_HandsetMicJackDescription]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + ULONG nPinId = (ULONG)-1; + + ULONG cJackDescriptions = ARRAYSIZE(HandsetMicJackDescriptions); + PKSJACK_DESCRIPTION * JackDescriptions = HandsetMicJackDescriptions; + + if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + nPinId = *(PULONG(PropertyRequest->Instance)); + + if ((nPinId < cJackDescriptions) && (JackDescriptions[nPinId] != NULL)) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else + { + ULONG cbNeeded = sizeof(KSMULTIPLE_ITEM) + sizeof(KSJACK_DESCRIPTION); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbNeeded; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbNeeded) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PKSMULTIPLE_ITEM pMI = (PKSMULTIPLE_ITEM)PropertyRequest->Value; + PKSJACK_DESCRIPTION pDesc = (PKSJACK_DESCRIPTION)(pMI+1); + + pMI->Size = cbNeeded; + pMI->Count = 1; + + RtlCopyMemory(pDesc, JackDescriptions[nPinId], sizeof(KSJACK_DESCRIPTION)); + ntStatus = STATUS_SUCCESS; + } + } + } + } + } + + return ntStatus; +} + +//============================================================================= +NTSTATUS +PropertyHandler_HandsetMicJackDescription2 +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_Jack, KSPROPERTY_JACK_DESCRIPTION2 ) + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_HandsetMicJackDescription2]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + ULONG nPinId = (ULONG)-1; + + ULONG cJackDescriptions = ARRAYSIZE(HandsetMicJackDescriptions); + PKSJACK_DESCRIPTION * JackDescriptions = HandsetMicJackDescriptions; + + if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + nPinId = *(PULONG(PropertyRequest->Instance)); + + if ((nPinId < cJackDescriptions) && (JackDescriptions[nPinId] != NULL)) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else + { + ULONG cbNeeded = sizeof(KSMULTIPLE_ITEM) + sizeof(KSJACK_DESCRIPTION2); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbNeeded; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbNeeded) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PKSMULTIPLE_ITEM pMI = (PKSMULTIPLE_ITEM)PropertyRequest->Value; + PKSJACK_DESCRIPTION2 pDesc = (PKSJACK_DESCRIPTION2)(pMI+1); + + pMI->Size = cbNeeded; + pMI->Count = 1; + + RtlZeroMemory(pDesc, sizeof(KSJACK_DESCRIPTION2)); + + // + // Specifies the lower 16 bits of the DWORD parameter. This parameter indicates whether + // the jack is currently active, streaming, idle, or hardware not ready. + // + pDesc->DeviceStateInfo = 0; + + // + // From MSDN: + // "If an audio device lacks jack presence detection, the IsConnected member of + // the KSJACK_DESCRIPTION structure must always be set to TRUE. To remove the + // ambiguity that results from this dual meaning of the TRUE value for IsConnected, + // a client application can call IKsJackDescription2::GetJackDescription2 to read + // the JackCapabilities flag of the KSJACK_DESCRIPTION2 structure. If this flag has + // the JACKDESC2_PRESENCE_DETECT_CAPABILITY bit set, it indicates that the endpoint + // does in fact support jack presence detection. In that case, the return value of + // the IsConnected member can be interpreted to accurately reflect the insertion status + // of the jack." + // + // Bit definitions: + // 0x00000001 - JACKDESC2_PRESENCE_DETECT_CAPABILITY + // 0x00000002 - JACKDESC2_DYNAMIC_FORMAT_CHANGE_CAPABILITY + // + pDesc->JackCapabilities = JACKDESC2_PRESENCE_DETECT_CAPABILITY; + + ntStatus = STATUS_SUCCESS; + } + } + } + } + } + + return ntStatus; +} + +//============================================================================= +NTSTATUS +PropertyHandler_HandsetMicTopoFilter +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Redirects property request to miniport object + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_HandsetMicTopoFilter]")); + + // PropertryRequest structure is filled by portcls. + // MajorTarget is a pointer to miniport object for miniports. + // + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + // + // This line shows how to get a pointer to the miniport topology object. + // + PCMiniportTopology pMiniport = (PCMiniportTopology)PropertyRequest->MajorTarget; + UNREFERENCED_VAR(pMiniport); + + if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_Jack)) + { + if (PropertyRequest->PropertyItem->Id == KSPROPERTY_JACK_DESCRIPTION) + { + ntStatus = PropertyHandler_HandsetMicJackDescription(PropertyRequest); + } + else if (PropertyRequest->PropertyItem->Id == KSPROPERTY_JACK_DESCRIPTION2) + { + ntStatus = PropertyHandler_HandsetMicJackDescription2(PropertyRequest); + } + } + + return ntStatus; +} // PropertyHandler_HandsetMicTopoFilter + +#pragma code_seg() + diff --git a/audio/sysvad/PhoneAudioSample/handsetmictopo.h b/audio/sysvad/PhoneAudioSample/handsetmictopo.h new file mode 100644 index 000000000..07c8ba272 --- /dev/null +++ b/audio/sysvad/PhoneAudioSample/handsetmictopo.h @@ -0,0 +1,21 @@ + +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + handsetmictopo.h + +Abstract: + + Declaration of topology miniport for the handset mic (internal). + +--*/ + +#ifndef _SYSVAD_HANDSETMICTOPO_H_ +#define _SYSVAD_HANDSETMICTOPO_H_ + +NTSTATUS PropertyHandler_HandsetMicTopoFilter(_In_ PPCPROPERTY_REQUEST PropertyRequest); + +#endif // _SYSVAD_HANDSETMICTOPO_H_ diff --git a/audio/sysvad/PhoneAudioSample/handsetmictoptable.h b/audio/sysvad/PhoneAudioSample/handsetmictoptable.h new file mode 100644 index 000000000..5fd700102 --- /dev/null +++ b/audio/sysvad/PhoneAudioSample/handsetmictoptable.h @@ -0,0 +1,245 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + handsetmictopotable.h + +Abstract: + + Declaration of topology table for the handset mic (internal) + +--*/ + +#ifndef _SYSVAD_HANDSETMICTOPTABLE_H_ +#define _SYSVAD_HANDSETMICTOPTABLE_H_ + +// Function declarations. +NTSTATUS +PropertyHandler_HandsetMicTopoFilter( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + +//============================================================================= +static +KSDATARANGE HandsetMicTopoPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +//============================================================================= +static +PKSDATARANGE HandsetMicTopoPinDataRangePointersBridge[] = +{ + &HandsetMicTopoPinDataRangesBridge[0] +}; + +//============================================================================= +static +PCPIN_DESCRIPTOR HandsetMicTopoMiniportPins[] = +{ + // KSPIN - topology filter in-pin + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(HandsetMicTopoPinDataRangePointersBridge),// DataRangesCount + HandsetMicTopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_IN, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSNODETYPE_HANDSET, // Category + NULL, // Name + 0 // Reserved + } + }, + + // KSPIN - topology filter out-in + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(HandsetMicTopoPinDataRangePointersBridge),// DataRangesCount + HandsetMicTopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_OUT, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSCATEGORY_AUDIO, // Category + NULL, // Name + 0 // Reserved + } + } +}; + +//============================================================================= +static +KSJACK_DESCRIPTION HandsetMicJackDesc = +{ + KSAUDIO_SPEAKER_MONO, + 0x0000, // no color + eConnTypeUnknown, + eGeoLocTop, + eGenLocInternal, + ePortConnIntegratedDevice, + TRUE +}; + +//============================================================================= +// Only return a KSJACK_DESCRIPTION for the physical bridge pin. +static +PKSJACK_DESCRIPTION HandsetMicJackDescriptions[] = +{ + &HandsetMicJackDesc, + NULL +}; + +//============================================================================= +static +PCPROPERTY_ITEM HandsetMicPropertiesVolume[] = +{ + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_VOLUMELEVEL, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_Topology + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationHandsetMicVolume, HandsetMicPropertiesVolume); + +//============================================================================= +static +PCPROPERTY_ITEM HandsetMicPropertiesMute[] = +{ + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_MUTE, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_Topology + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationHandsetMicMute, HandsetMicPropertiesMute); + +//============================================================================= +static +PCPROPERTY_ITEM HandsetMicPropertiesPeakMeter[] = +{ + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_PEAKMETER2, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_Topology + }, + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_CPU_RESOURCES, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_Topology + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationHandsetMicPeakMeter, HandsetMicPropertiesPeakMeter); + +//============================================================================= +static +PCNODE_DESCRIPTOR HandsetMicTopologyNodes[] = +{ + // KSNODE_TOPO_VOLUME + { + 0, // Flags + &AutomationHandsetMicVolume, // AutomationTable + &KSNODETYPE_VOLUME, // Type + &KSAUDFNAME_MIC_VOLUME // Name + }, + // KSNODE_TOPO_MUTE + { + 0, // Flags + &AutomationHandsetMicMute, // AutomationTable + &KSNODETYPE_MUTE, // Type + &KSAUDFNAME_MIC_MUTE // Name + }, + // KSNODE_TOPO_PEAKMETER + { + 0, // Flags + &AutomationHandsetMicPeakMeter, // AutomationTable + &KSNODETYPE_PEAKMETER, // Type + &KSAUDFNAME_PEAKMETER // Name + } +}; + +C_ASSERT( KSNODE_TOPO_VOLUME == 0 ); +C_ASSERT( KSNODE_TOPO_MUTE == 1 ); +C_ASSERT( KSNODE_TOPO_PEAKMETER == 2 ); + +//============================================================================= +static +PCCONNECTION_DESCRIPTOR HandsetMicMiniportConnections[] = +{ + // FromNode, FromPin, ToNode, ToPin + { PCFILTER_NODE, KSPIN_TOPO_MIC_ELEMENTS, KSNODE_TOPO_VOLUME, 1 }, + { KSNODE_TOPO_VOLUME, 0, KSNODE_TOPO_MUTE, 1 }, + { KSNODE_TOPO_MUTE, 0, KSNODE_TOPO_PEAKMETER, 1 }, + { KSNODE_TOPO_PEAKMETER, 0, PCFILTER_NODE, KSPIN_TOPO_BRIDGE } +}; + + +//============================================================================= +static +PCPROPERTY_ITEM HandsetMicPropertiesTopoFilter[] = +{ + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_HandsetMicTopoFilter + }, + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION2, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_HandsetMicTopoFilter + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationHandsetMicTopoFilter, HandsetMicPropertiesTopoFilter); + +//============================================================================= +static +PCFILTER_DESCRIPTOR HandsetMicTopoMiniportFilterDescriptor = +{ + 0, // Version + &AutomationHandsetMicTopoFilter, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(HandsetMicTopoMiniportPins), // PinCount + HandsetMicTopoMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + SIZEOF_ARRAY(HandsetMicTopologyNodes), // NodeCount + HandsetMicTopologyNodes, // Nodes + SIZEOF_ARRAY(HandsetMicMiniportConnections), // ConnectionCount + HandsetMicMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories +}; + +#endif // _SYSVAD_HANDSETMICTOPTABLE_H_ + diff --git a/audio/sysvad/PhoneAudioSample/handsetmicwavtable.h b/audio/sysvad/PhoneAudioSample/handsetmicwavtable.h new file mode 100644 index 000000000..5f9929f63 --- /dev/null +++ b/audio/sysvad/PhoneAudioSample/handsetmicwavtable.h @@ -0,0 +1,441 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + handsetmicwavtable.h + +Abstract: + + Declaration of wave miniport tables for the handset microphone. + +--*/ + +#ifndef _SYSVAD_HANDSETMICWAVTABLE_H_ +#define _SYSVAD_HANDSETMICWAVTABLE_H_ + +// +// Handset microphone (internal) range. +// +#define HANDSETMIC_DEVICE_MAX_CHANNELS 1 // Max Channels. +#define HANDSETMIC_MIN_BITS_PER_SAMPLE_PCM 16 // Min Bits Per Sample +#define HANDSETMIC_MAX_BITS_PER_SAMPLE_PCM 16 // Max Bits Per Sample +#define HANDSETMIC_MIN_SAMPLE_RATE 8000 // Min Sample Rate +#define HANDSETMIC_MAX_SAMPLE_RATE 48000 // Max Sample Rate + +// +// Max # of pin instances. +// +#define HANDSETMIC_MAX_INPUT_STREAMS 2 // Raw + Default streams + +//============================================================================= +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE HandsetMicPinSupportedDeviceFormats[] = +{ + { // 0 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 8000, + 16000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 1 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 11025, + 22050, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 2 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 16000, + 32000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 3 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 22050, + 44100, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 4 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 24000, + 48000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 5 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 32000, + 64000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 6 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 44100, + 88200, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 7 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 48000, + 96000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +// +// Supported modes (only on streaming pins). +// +static +MODE_AND_DEFAULT_FORMAT HandsetMicPinSupportedDeviceModes[] = +{ + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_RAW, + &HandsetMicPinSupportedDeviceFormats[SIZEOF_ARRAY(HandsetMicPinSupportedDeviceFormats) - 1].DataFormat, // 48KHz + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_DEFAULT, + &HandsetMicPinSupportedDeviceFormats[SIZEOF_ARRAY(HandsetMicPinSupportedDeviceFormats) - 1].DataFormat, // 48KHz + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_SPEECH, + &HandsetMicPinSupportedDeviceFormats[2].DataFormat, // 16KHz + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_COMMUNICATIONS, + &HandsetMicPinSupportedDeviceFormats[4].DataFormat, // 24KHz + } +}; + +// +// The entries here must follow the same order as the filter's pin +// descriptor array. +// +static +PIN_DEVICE_FORMATS_AND_MODES HandsetMicPinDeviceFormatsAndModes[] = +{ + { + BridgePin, + NULL, + 0, + NULL, + 0 + }, + { + SystemCapturePin, + HandsetMicPinSupportedDeviceFormats, + SIZEOF_ARRAY(HandsetMicPinSupportedDeviceFormats), + HandsetMicPinSupportedDeviceModes, + SIZEOF_ARRAY(HandsetMicPinSupportedDeviceModes) + } +}; + +//============================================================================= +static +KSDATARANGE_AUDIO HandsetMicPinDataRangesStream[] = +{ + { + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + HANDSETMIC_DEVICE_MAX_CHANNELS, + HANDSETMIC_MIN_BITS_PER_SAMPLE_PCM, + HANDSETMIC_MAX_BITS_PER_SAMPLE_PCM, + HANDSETMIC_MIN_SAMPLE_RATE, + HANDSETMIC_MAX_SAMPLE_RATE + }, +}; + +static +PKSDATARANGE HandsetMicPinDataRangePointersStream[] = +{ + PKSDATARANGE(&HandsetMicPinDataRangesStream[0]), + PKSDATARANGE(&PinDataRangeAttributeList), +}; + +//============================================================================= +static +KSDATARANGE HandsetMicPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +static +PKSDATARANGE HandsetMicPinDataRangePointersBridge[] = +{ + &HandsetMicPinDataRangesBridge[0] +}; + +//============================================================================= +static +PCPIN_DESCRIPTOR HandsetMicWaveMiniportPins[] = +{ + // Wave In Bridge Pin (Capture - From Topology) KSPIN_WAVE_BRIDGE + { + 0, + 0, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(HandsetMicPinDataRangePointersBridge), + HandsetMicPinDataRangePointersBridge, + KSPIN_DATAFLOW_IN, + KSPIN_COMMUNICATION_NONE, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, + + // Wave In Streaming Pin (Capture) KSPIN_WAVEIN_HOST + { + HANDSETMIC_MAX_INPUT_STREAMS, + HANDSETMIC_MAX_INPUT_STREAMS, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(HandsetMicPinDataRangePointersStream), + HandsetMicPinDataRangePointersStream, + KSPIN_DATAFLOW_OUT, + KSPIN_COMMUNICATION_SINK, + &KSCATEGORY_AUDIO, + &KSAUDFNAME_RECORDING_CONTROL, + 0 + } + } +}; + +//============================================================================= +static +PCNODE_DESCRIPTOR HandsetMicWaveMiniportNodes[] = +{ + // KSNODE_WAVE_ADC + { + 0, // Flags + NULL, // AutomationTable + &KSNODETYPE_ADC, // Type + NULL // Name + } +}; + +//============================================================================= +static +PCCONNECTION_DESCRIPTOR HandsetMicWaveMiniportConnections[] = +{ + { PCFILTER_NODE, KSPIN_WAVE_BRIDGE, KSNODE_WAVE_ADC, 1 }, + { KSNODE_WAVE_ADC, 0, PCFILTER_NODE, KSPIN_WAVEIN_HOST } +}; + +//============================================================================= +static +PCPROPERTY_ITEM PropertiesHandsetMicWaveFilter[] = +{ + { + &KSPROPSETID_General, + KSPROPERTY_GENERAL_COMPONENTID, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_WaveFilter + }, + { + &KSPROPSETID_Pin, + KSPROPERTY_PIN_PROPOSEDATAFORMAT, + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_WaveFilter + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationHandsetMicWaveFilter, PropertiesHandsetMicWaveFilter); + +//============================================================================= +static +PCFILTER_DESCRIPTOR HandsetMicWaveMiniportFilterDescriptor = +{ + 0, // Version + &AutomationHandsetMicWaveFilter, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(HandsetMicWaveMiniportPins), // PinCount + HandsetMicWaveMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + SIZEOF_ARRAY(HandsetMicWaveMiniportNodes), // NodeCount + HandsetMicWaveMiniportNodes, // Nodes + SIZEOF_ARRAY(HandsetMicWaveMiniportConnections), // ConnectionCount + HandsetMicWaveMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories - use defaults (audio, render, capture) +}; + +#endif // _SYSVAD_HANDSETMICWAVTABLE_H_ diff --git a/audio/sysvad/PhoneAudioSample/handsetspeakertopo.cpp b/audio/sysvad/PhoneAudioSample/handsetspeakertopo.cpp new file mode 100644 index 000000000..9e72ce85a --- /dev/null +++ b/audio/sysvad/PhoneAudioSample/handsetspeakertopo.cpp @@ -0,0 +1,561 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + handsetspeakertopo.cpp + +Abstract: + + Implementation of topology miniport for the handsetspeaker endpoint. + +--*/ + +#pragma warning (disable : 4127) + +#include +#include "simple.h" +#include "mintopo.h" +#include "handsetspeakertopo.h" +#include "handsetspeakertoptable.h" +#include + + +#pragma code_seg("PAGE") + +//============================================================================= +NTSTATUS +CreateHandsetSpeakerMiniportTopology +( + _Out_ PUNKNOWN * Unknown, + _In_ REFCLSID, + _In_opt_ PUNKNOWN UnknownOuter, + _When_((PoolType & NonPagedPoolMustSucceed) != 0, + __drv_reportError("Must succeed pool allocations are forbidden. " + "Allocation failures cause a system crash")) + _In_ POOL_TYPE PoolType, + _In_ PUNKNOWN UnknownAdapter, + _In_opt_ PVOID DeviceContext, + _In_ PENDPOINT_MINIPAIR MiniportPair +) +/*++ + +Routine Description: + + Creates a new topology miniport. + +Arguments: + + Unknown - + + RefclsId - + + UnknownOuter - + + PoolType - + + UnknownAdapter - + + DeviceContext - + + MiniportPair - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(Unknown); + ASSERT(MiniportPair); + + UNREFERENCED_PARAMETER(UnknownAdapter); + UNREFERENCED_PARAMETER(DeviceContext); + + CHandsetSpeakerMiniportTopology *obj = + new (PoolType, MINWAVERT_POOLTAG) + CHandsetSpeakerMiniportTopology( UnknownOuter, + MiniportPair->TopoDescriptor, + MiniportPair->DeviceMaxChannels); + if (NULL == obj) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + obj->AddRef(); + *Unknown = reinterpret_cast(obj); + + return STATUS_SUCCESS; +} // CreateHandsetSpeakerMiniportTopology + +//============================================================================= +CHandsetSpeakerMiniportTopology::~CHandsetSpeakerMiniportTopology +( + void +) +/*++ + +Routine Description: + + Topology miniport destructor + +Arguments: + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + DPF_ENTER(("[CHandsetSpeakerMiniportTopology::~CHandsetSpeakerMiniportTopology]")); +} // ~CHandsetSpeakerMiniportTopology + +//============================================================================= +NTSTATUS +CHandsetSpeakerMiniportTopology::DataRangeIntersection +( + _In_ ULONG PinId, + _In_ PKSDATARANGE ClientDataRange, + _In_ PKSDATARANGE MyDataRange, + _In_ ULONG OutputBufferLength, + _Out_writes_bytes_to_opt_(OutputBufferLength, *ResultantFormatLength) + PVOID ResultantFormat OPTIONAL, + _Out_ PULONG ResultantFormatLength +) +/*++ + +Routine Description: + + The DataRangeIntersection function determines the highest quality + intersection of two data ranges. + +Arguments: + + PinId - Pin for which data intersection is being determined. + + ClientDataRange - Pointer to KSDATARANGE structure which contains the data range + submitted by client in the data range intersection property + request. + + MyDataRange - Pin's data range to be compared with client's data range. + + OutputBufferLength - Size of the buffer pointed to by the resultant format + parameter. + + ResultantFormat - Pointer to value where the resultant format should be + returned. + + ResultantFormatLength - Actual length of the resultant format that is placed + at ResultantFormat. This should be less than or equal + to OutputBufferLength. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + return + CMiniportTopologySYSVAD::DataRangeIntersection + ( + PinId, + ClientDataRange, + MyDataRange, + OutputBufferLength, + ResultantFormat, + ResultantFormatLength + ); +} // DataRangeIntersection + +//============================================================================= +STDMETHODIMP +CHandsetSpeakerMiniportTopology::GetDescription +( + _Out_ PPCFILTER_DESCRIPTOR * OutFilterDescriptor +) +/*++ + +Routine Description: + + The GetDescription function gets a pointer to a filter description. + It provides a location to deposit a pointer in miniport's description + structure. This is the placeholder for the FromNode or ToNode fields in + connections which describe connections to the filter's pins. + +Arguments: + + OutFilterDescriptor - Pointer to the filter description. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(OutFilterDescriptor); + + return CMiniportTopologySYSVAD::GetDescription(OutFilterDescriptor); +} // GetDescription + +//============================================================================= +STDMETHODIMP +CHandsetSpeakerMiniportTopology::Init +( + _In_ PUNKNOWN UnknownAdapter, + _In_ PRESOURCELIST ResourceList, + _In_ PPORTTOPOLOGY Port_ +) +/*++ + +Routine Description: + + The Init function initializes the miniport. Callers of this function + should run at IRQL PASSIVE_LEVEL + +Arguments: + + UnknownAdapter - A pointer to the Iuknown interface of the adapter object. + + ResourceList - Pointer to the resource list to be supplied to the miniport + during initialization. The port driver is free to examine the + contents of the ResourceList. The port driver will not be + modify the ResourceList contents. + + Port - Pointer to the topology port object that is linked with this miniport. + +Return Value: + + NT status code. + +--*/ +{ + UNREFERENCED_PARAMETER(ResourceList); + + PAGED_CODE(); + + ASSERT(UnknownAdapter); + ASSERT(Port_); + + DPF_ENTER(("[CHandsetSpeakerMiniportTopology::Init]")); + + NTSTATUS ntStatus; + + ntStatus = + CMiniportTopologySYSVAD::Init + ( + UnknownAdapter, + Port_ + ); + + return ntStatus; +} // Init + +//============================================================================= +STDMETHODIMP +CHandsetSpeakerMiniportTopology::NonDelegatingQueryInterface +( + _In_ REFIID Interface, + _COM_Outptr_ PVOID * Object +) +/*++ + +Routine Description: + + QueryInterface for MiniportTopology + +Arguments: + + Interface - GUID of the interface + + Object - interface object to be returned. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(Object); + + if (IsEqualGUIDAligned(Interface, IID_IUnknown)) + { + *Object = PVOID(PUNKNOWN(this)); + } + else if (IsEqualGUIDAligned(Interface, IID_IMiniport)) + { + *Object = PVOID(PMINIPORT(this)); + } + else if (IsEqualGUIDAligned(Interface, IID_IMiniportTopology)) + { + *Object = PVOID(PMINIPORTTOPOLOGY(this)); + } + else + { + *Object = NULL; + } + + if (*Object) + { + // We reference the interface for the caller. + PUNKNOWN(*Object)->AddRef(); + return(STATUS_SUCCESS); + } + + return(STATUS_INVALID_PARAMETER); +} // NonDelegatingQueryInterface + +//============================================================================= +NTSTATUS +CHandsetSpeakerMiniportTopology::PropertyHandlerJackDescription +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_Jack, KSPROPERTY_JACK_DESCRIPTION ) + +Arguments: + + PropertyRequest + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandlerJackDescription]")); + + ULONG cJackDescriptions = ARRAYSIZE(HandsetSpeakerJackDescriptions); + PKSJACK_DESCRIPTION * JackDescriptions = HandsetSpeakerJackDescriptions; + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + ULONG nPinId = (ULONG)-1; + + if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + nPinId = *(PULONG(PropertyRequest->Instance)); + + if ((nPinId < cJackDescriptions) && (JackDescriptions[nPinId] != NULL)) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else + { + ULONG cbNeeded = sizeof(KSMULTIPLE_ITEM) + sizeof(KSJACK_DESCRIPTION); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbNeeded; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbNeeded) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PKSMULTIPLE_ITEM pMI = (PKSMULTIPLE_ITEM)PropertyRequest->Value; + PKSJACK_DESCRIPTION pDesc = (PKSJACK_DESCRIPTION)(pMI+1); + + pMI->Size = cbNeeded; + pMI->Count = 1; + + RtlCopyMemory(pDesc, JackDescriptions[nPinId], sizeof(KSJACK_DESCRIPTION)); + ntStatus = STATUS_SUCCESS; + } + } + } + } + } + + return ntStatus; +} + +//============================================================================= +NTSTATUS +CHandsetSpeakerMiniportTopology::PropertyHandlerJackDescription2 +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_Jack, KSPROPERTY_JACK_DESCRIPTION2 ) + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandlerJackDescription2]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + ULONG nPinId = (ULONG)-1; + + ULONG cJackDescriptions = ARRAYSIZE(HandsetSpeakerJackDescriptions); + PKSJACK_DESCRIPTION * JackDescriptions = HandsetSpeakerJackDescriptions; + + if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + nPinId = *(PULONG(PropertyRequest->Instance)); + + if ((nPinId < cJackDescriptions) && (JackDescriptions[nPinId] != NULL)) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else + { + ULONG cbNeeded = sizeof(KSMULTIPLE_ITEM) + sizeof(KSJACK_DESCRIPTION2); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbNeeded; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbNeeded) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PKSMULTIPLE_ITEM pMI = (PKSMULTIPLE_ITEM)PropertyRequest->Value; + PKSJACK_DESCRIPTION2 pDesc = (PKSJACK_DESCRIPTION2)(pMI+1); + + pMI->Size = cbNeeded; + pMI->Count = 1; + + RtlZeroMemory(pDesc, sizeof(KSJACK_DESCRIPTION2)); + + // + // Specifies the lower 16 bits of the DWORD parameter. This parameter indicates whether + // the jack is currently active, streaming, idle, or hardware not ready. + // + pDesc->DeviceStateInfo = 0; + + // + // From MSDN: + // "If an audio device lacks jack presence detection, the IsConnected member of + // the KSJACK_DESCRIPTION structure must always be set to TRUE. To remove the + // ambiguity that results from this dual meaning of the TRUE value for IsConnected, + // a client application can call IKsJackDescription2::GetJackDescription2 to read + // the JackCapabilities flag of the KSJACK_DESCRIPTION2 structure. If this flag has + // the JACKDESC2_PRESENCE_DETECT_CAPABILITY bit set, it indicates that the endpoint + // does in fact support jack presence detection. In that case, the return value of + // the IsConnected member can be interpreted to accurately reflect the insertion status + // of the jack." + // + // Bit definitions: + // 0x00000001 - JACKDESC2_PRESENCE_DETECT_CAPABILITY + // 0x00000002 - JACKDESC2_DYNAMIC_FORMAT_CHANGE_CAPABILITY + // + pDesc->JackCapabilities = JACKDESC2_PRESENCE_DETECT_CAPABILITY; + + ntStatus = STATUS_SUCCESS; + } + } + } + } + } + + return ntStatus; +} + +//============================================================================= +NTSTATUS +PropertyHandler_HandsetSpeakerTopoFilter +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Redirects property request to miniport object + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_HandsetSpeakerTopoFilter]")); + + // PropertryRequest structure is filled by portcls. + // MajorTarget is a pointer to miniport object for miniports. + // + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + PCHandsetSpeakerMiniportTopology pMiniport = (PCHandsetSpeakerMiniportTopology)PropertyRequest->MajorTarget; + + if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_Jack)) + { + if (PropertyRequest->PropertyItem->Id == KSPROPERTY_JACK_DESCRIPTION) + { + ntStatus = pMiniport->PropertyHandlerJackDescription(PropertyRequest); + } + else if (PropertyRequest->PropertyItem->Id == KSPROPERTY_JACK_DESCRIPTION2) + { + ntStatus = pMiniport->PropertyHandlerJackDescription2(PropertyRequest); + } + } + + return ntStatus; +} // PropertyHandler_HandsetSpeakerTopoFilter + +#pragma code_seg() + diff --git a/audio/sysvad/PhoneAudioSample/handsetspeakertopo.h b/audio/sysvad/PhoneAudioSample/handsetspeakertopo.h new file mode 100644 index 000000000..7b220f707 --- /dev/null +++ b/audio/sysvad/PhoneAudioSample/handsetspeakertopo.h @@ -0,0 +1,86 @@ + +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + handsetspeakertopo.h + +Abstract: + + Declaration of topology miniport for the handsetspeaker endpoint. + +--*/ + +#ifndef _SYSVAD_HANDSETSPEAKERTOPO_H_ +#define _SYSVAD_HANDSETSPEAKERTOPO_H_ + +#include "basetopo.h" + +//============================================================================= +// Classes +//============================================================================= + +/////////////////////////////////////////////////////////////////////////////// +// CHandsetSpeakerMiniportTopology +// + +class CHandsetSpeakerMiniportTopology : + public CMiniportTopologySYSVAD, + public IMiniportTopology, + public CUnknown +{ + public: + DECLARE_STD_UNKNOWN(); + CHandsetSpeakerMiniportTopology + ( + _In_opt_ PUNKNOWN UnknownOuter, + _In_ PCFILTER_DESCRIPTOR *FilterDesc, + _In_ USHORT DeviceMaxChannels + ) + : CUnknown(UnknownOuter), + CMiniportTopologySYSVAD(FilterDesc, DeviceMaxChannels) + {} + + ~CHandsetSpeakerMiniportTopology(); + + IMP_IMiniportTopology; + + NTSTATUS PropertyHandlerJackSinkInfo + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + + NTSTATUS PropertyHandlerJackDescription + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + + NTSTATUS PropertyHandlerJackDescription2 + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); +}; + +typedef CHandsetSpeakerMiniportTopology *PCHandsetSpeakerMiniportTopology; + + +NTSTATUS +CreateHandsetSpeakerMiniportTopology( + _Out_ PUNKNOWN * Unknown, + _In_ REFCLSID, + _In_opt_ PUNKNOWN UnknownOuter, + _When_((PoolType & NonPagedPoolMustSucceed) != 0, + __drv_reportError("Must succeed pool allocations are forbidden. " + "Allocation failures cause a system crash")) + _In_ POOL_TYPE PoolType, + _In_ PUNKNOWN UnknownAdapter, + _In_opt_ PVOID DeviceContext, + _In_ PENDPOINT_MINIPAIR MiniportPair + ); + +NTSTATUS PropertyHandler_HandsetSpeakerTopoFilter(_In_ PPCPROPERTY_REQUEST PropertyRequest); + +#endif // _SYSVAD_HANDSETSPEAKERTOPO_H_ + diff --git a/audio/sysvad/PhoneAudioSample/handsetspeakertoptable.h b/audio/sysvad/PhoneAudioSample/handsetspeakertoptable.h new file mode 100644 index 000000000..68873e618 --- /dev/null +++ b/audio/sysvad/PhoneAudioSample/handsetspeakertoptable.h @@ -0,0 +1,156 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + handsetspeakertoptable.h + +Abstract: + + Declaration of topology tables for the handsetspeaker endpoint. + +--*/ + +#ifndef _SYSVAD_HANDSETSPEAKERTOPTABLE_H_ +#define _SYSVAD_HANDSETSPEAKERTOPTABLE_H_ + +//============================================================================= +static +KSDATARANGE HandsetSpeakerTopoPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +//============================================================================= +static +PKSDATARANGE HandsetSpeakerTopoPinDataRangePointersBridge[] = +{ + &HandsetSpeakerTopoPinDataRangesBridge[0] +}; + +//============================================================================= +static +PCPIN_DESCRIPTOR HandsetSpeakerTopoMiniportPins[] = +{ + // KSPIN_TOPO_WAVEOUT_SOURCE + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(HandsetSpeakerTopoPinDataRangePointersBridge), // DataRangesCount + HandsetSpeakerTopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_IN, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSCATEGORY_AUDIO, // Category + NULL, // Name + 0 // Reserved + } + }, + // KSPIN_TOPO_LINEOUT_DEST + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(HandsetSpeakerTopoPinDataRangePointersBridge), // DataRangesCount + HandsetSpeakerTopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_OUT, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSNODETYPE_HANDSET, // Category + NULL, // Name + 0 // Reserved + } + } +}; + +//============================================================================= +static +KSJACK_DESCRIPTION HandsetSpeakerJackDesc = +{ + KSAUDIO_SPEAKER_MONO, + 0x0000, // no color + eConnTypeUnknown, + eGeoLocBottom, + eGenLocInternal, + ePortConnIntegratedDevice, + TRUE +}; + +// Only return a KSJACK_DESCRIPTION for the physical bridge pin. +static +PKSJACK_DESCRIPTION HandsetSpeakerJackDescriptions[] = +{ + NULL, + &HandsetSpeakerJackDesc +}; + +//============================================================================= +static +PCCONNECTION_DESCRIPTOR HandsetSpeakerTopoMiniportConnections[] = +{ + // FromNode, FromPin, ToNode, ToPin + { PCFILTER_NODE, KSPIN_TOPO_WAVEOUT_SOURCE, PCFILTER_NODE, KSPIN_TOPO_LINEOUT_DEST} +}; + + +//============================================================================= +static +PCPROPERTY_ITEM PropertiesHandsetSpeakerTopoFilter[] = +{ + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_HandsetSpeakerTopoFilter + }, + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION2, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_HandsetSpeakerTopoFilter + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationHandsetSpeakerTopoFilter, PropertiesHandsetSpeakerTopoFilter); + +//============================================================================= +static +PCFILTER_DESCRIPTOR HandsetSpeakerTopoMiniportFilterDescriptor = +{ + 0, // Version + &AutomationHandsetSpeakerTopoFilter, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(HandsetSpeakerTopoMiniportPins), // PinCount + HandsetSpeakerTopoMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + 0, // NodeCount + NULL, // Nodes + SIZEOF_ARRAY(HandsetSpeakerTopoMiniportConnections), // ConnectionCount + HandsetSpeakerTopoMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories +}; + +#endif // _SYSVAD_HANDSETSPEAKERTOPTABLE_H_ + + diff --git a/audio/sysvad/PhoneAudioSample/handsetspeakerwavtable.h b/audio/sysvad/PhoneAudioSample/handsetspeakerwavtable.h new file mode 100644 index 000000000..4a1cfbb3b --- /dev/null +++ b/audio/sysvad/PhoneAudioSample/handsetspeakerwavtable.h @@ -0,0 +1,649 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + handsetspeakerwavtable.h + +Abstract: + + Declaration of wave miniport tables for the handsetspeaker endpoint. + + Note: the HANDSETSPEAKER in this sample shows how to do h/w loopback + for non-offloaded devices. + +--*/ + +#ifndef _SYSVAD_HANDSETSPEAKERWAVTABLE_H_ +#define _SYSVAD_HANDSETSPEAKERWAVTABLE_H_ + + +// To keep the code simple assume device supports only 48KHz, 16-bit, stereo (PCM and NON-PCM) + + +#define HANDSETSPEAKER_DEVICE_MAX_CHANNELS 2 // Max Channels. + +#define HANDSETSPEAKER_HOST_MAX_CHANNELS 2 // Max Channels. +#define HANDSETSPEAKER_HOST_MIN_BITS_PER_SAMPLE 16 // Min Bits Per Sample +#define HANDSETSPEAKER_HOST_MAX_BITS_PER_SAMPLE 16 // Max Bits Per Sample +#define HANDSETSPEAKER_HOST_MIN_SAMPLE_RATE 24000 // Min Sample Rate +#define HANDSETSPEAKER_HOST_MAX_SAMPLE_RATE 96000 // Max Sample Rate + +#define HANDSETSPEAKER_LOOPBACK_MAX_CHANNELS 2 // Max Channels. +#define HANDSETSPEAKER_LOOPBACK_MIN_BITS_PER_SAMPLE 16 // Min Bits Per Sample +#define HANDSETSPEAKER_LOOPBACK_MAX_BITS_PER_SAMPLE 16 // Max Bits Per Sample +#define HANDSETSPEAKER_LOOPBACK_MIN_SAMPLE_RATE 24000 // Min Sample Rate +#define HANDSETSPEAKER_LOOPBACK_MAX_SAMPLE_RATE 48000 // Max Sample Rate + +#define HANDSETSPEAKER_DOLBY_DIGITAL_MAX_CHANNELS 2 // Max Channels. +#define HANDSETSPEAKER_DOLBY_DIGITAL_MIN_BITS_PER_SAMPLE 16 // Min Bits Per Sample +#define HANDSETSPEAKER_DOLBY_DIGITAL_MAX_BITS_PER_SAMPLE 16 // Max Bits Per Sample +#define HANDSETSPEAKER_DOLBY_DIGITAL_MIN_SAMPLE_RATE 44100 // Min Sample Rate +#define HANDSETSPEAKER_DOLBY_DIGITAL_MAX_SAMPLE_RATE 44100 // Max Sample Rate + +// +// Max # of pin instances. +// +#define HANDSETSPEAKER_MAX_INPUT_SYSTEM_STREAMS 2 // Raw + Default streams +#define HANDSETSPEAKER_MAX_OUTPUT_LOOPBACK_STREAMS MAX_OUTPUT_LOOPBACK_STREAMS + +//============================================================================= +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE HandsetSpeakerHostPinSupportedDeviceFormats[] = +{ + { // 0 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 24000, + 96000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 1 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 32000, + 128000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 2 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 44100, + 176400, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 3 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 48000, + 192000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 4 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 88200, + 352800, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 5 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 96000, + 384000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE HandsetSpeakerLoopbackPinSupportedDeviceFormats[] = +{ + { // 0 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 24000, + 96000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 1 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 32000, + 128000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 2 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 44100, + 176400, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 3 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 48000, + 192000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +// +// Supported modes (only on streaming pins). +// +static +MODE_AND_DEFAULT_FORMAT HandsetSpeakerHostPinSupportedDeviceModes[] = +{ + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_RAW, + &HandsetSpeakerHostPinSupportedDeviceFormats[3].DataFormat, // 48KHz + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_DEFAULT, + &HandsetSpeakerHostPinSupportedDeviceFormats[3].DataFormat, // 48KHz + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_COMMUNICATIONS, + &HandsetSpeakerHostPinSupportedDeviceFormats[0].DataFormat, // 24KHz + } +}; + +// +// The entries here must follow the same order as the filter's pin +// descriptor array. +// +static +PIN_DEVICE_FORMATS_AND_MODES HandsetSpeakerPinDeviceFormatsAndModes[] = +{ + { + SystemRenderPin, + HandsetSpeakerHostPinSupportedDeviceFormats, + SIZEOF_ARRAY(HandsetSpeakerHostPinSupportedDeviceFormats), + HandsetSpeakerHostPinSupportedDeviceModes, + SIZEOF_ARRAY(HandsetSpeakerHostPinSupportedDeviceModes) + }, + { + RenderLoopbackPin, + HandsetSpeakerLoopbackPinSupportedDeviceFormats, + SIZEOF_ARRAY(HandsetSpeakerLoopbackPinSupportedDeviceFormats), + NULL, // loopback doesn't support modes. + 0 + }, + { + BridgePin, + NULL, + 0, + NULL, + 0 + } +}; + +//============================================================================= +static +KSDATARANGE_AUDIO HandsetSpeakerPinDataRangesStream[] = +{ + { // 0 + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + HANDSETSPEAKER_HOST_MAX_CHANNELS, + HANDSETSPEAKER_HOST_MIN_BITS_PER_SAMPLE, + HANDSETSPEAKER_HOST_MAX_BITS_PER_SAMPLE, + HANDSETSPEAKER_HOST_MIN_SAMPLE_RATE, + HANDSETSPEAKER_HOST_MAX_SAMPLE_RATE + }, + { // 1 + { + sizeof(KSDATARANGE_AUDIO), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + HANDSETSPEAKER_LOOPBACK_MAX_CHANNELS, + HANDSETSPEAKER_LOOPBACK_MIN_BITS_PER_SAMPLE, + HANDSETSPEAKER_LOOPBACK_MAX_BITS_PER_SAMPLE, + HANDSETSPEAKER_LOOPBACK_MIN_SAMPLE_RATE, + HANDSETSPEAKER_LOOPBACK_MAX_SAMPLE_RATE + } +}; + +static +PKSDATARANGE HandsetSpeakerPinDataRangePointersStream[] = +{ + PKSDATARANGE(&HandsetSpeakerPinDataRangesStream[0]), + PKSDATARANGE(&PinDataRangeAttributeList) +}; + +static +PKSDATARANGE HandsetSpeakerPinDataRangePointersLoopbackStream[] = +{ + PKSDATARANGE(&HandsetSpeakerPinDataRangesStream[1]) +}; + +//============================================================================= +static +KSDATARANGE HandsetSpeakerPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +static +PKSDATARANGE HandsetSpeakerPinDataRangePointersBridge[] = +{ + &HandsetSpeakerPinDataRangesBridge[0] +}; + + +//============================================================================= +static +PCPIN_DESCRIPTOR HandsetSpeakerWaveMiniportPins[] = +{ + // Wave Out Streaming Pin (Renderer) KSPIN_WAVE_RENDER2_SINK_SYSTEM + { + HANDSETSPEAKER_MAX_INPUT_SYSTEM_STREAMS, + HANDSETSPEAKER_MAX_INPUT_SYSTEM_STREAMS, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(HandsetSpeakerPinDataRangePointersStream), + HandsetSpeakerPinDataRangePointersStream, + KSPIN_DATAFLOW_IN, + KSPIN_COMMUNICATION_SINK, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, + // Wave Out Streaming Pin (Renderer) KSPIN_WAVE_RENDER2_SINK_LOOPBACK + { + HANDSETSPEAKER_MAX_OUTPUT_LOOPBACK_STREAMS, + HANDSETSPEAKER_MAX_OUTPUT_LOOPBACK_STREAMS, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(HandsetSpeakerPinDataRangePointersLoopbackStream), + HandsetSpeakerPinDataRangePointersLoopbackStream, + KSPIN_DATAFLOW_OUT, + KSPIN_COMMUNICATION_SINK, + &KSNODETYPE_AUDIO_LOOPBACK, + NULL, + 0 + } + }, + // Wave Out Bridge Pin (Renderer) KSPIN_WAVE_RENDER2_SOURCE + { + 0, + 0, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(HandsetSpeakerPinDataRangePointersBridge), + HandsetSpeakerPinDataRangePointersBridge, + KSPIN_DATAFLOW_OUT, + KSPIN_COMMUNICATION_NONE, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, +}; + +//============================================================================= +static +PCPROPERTY_ITEM HandsetSpeakerPropertiesVolume[] = +{ + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_VOLUMELEVEL, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_WaveFilter + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationHandsetSpeakerVolume, HandsetSpeakerPropertiesVolume); + +//============================================================================= +static +PCPROPERTY_ITEM HandsetSpeakerPropertiesMute[] = +{ + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_MUTE, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_WaveFilter + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationHandsetSpeakerMute, HandsetSpeakerPropertiesMute); + +//============================================================================= +static +PCPROPERTY_ITEM HandsetSpeakerPropertiesPeakMeter[] = +{ + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_PEAKMETER2, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_WaveFilter + }, + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_CPU_RESOURCES, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_WaveFilter + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationHandsetSpeakerPeakMeter, HandsetSpeakerPropertiesPeakMeter); + +//============================================================================= +static +PCNODE_DESCRIPTOR HandsetSpeakerWaveMiniportNodes[] = +{ + // KSNODE_WAVE_SUM + { + 0, // Flags + NULL, // AutomationTable + &KSNODETYPE_SUM, // Type + NULL // Name + }, + // KSNODE_WAVE_VOLUME + { + 0, // Flags + &AutomationHandsetSpeakerVolume, // AutomationTable + &KSNODETYPE_VOLUME, // Type + &KSAUDFNAME_WAVE_VOLUME // Name + }, + // KSNODE_WAVE_MUTE + { + 0, // Flags + &AutomationHandsetSpeakerMute, // AutomationTable + &KSNODETYPE_MUTE, // Type + &KSAUDFNAME_WAVE_MUTE // Name + }, + // KSNODE_WAVE_PEAKMETER + { + 0, // Flags + &AutomationHandsetSpeakerPeakMeter, // AutomationTable + &KSNODETYPE_PEAKMETER, // Type + &KSAUDFNAME_PEAKMETER // Name + } +}; + +C_ASSERT( KSNODE_WAVE_SUM == 0 ); +C_ASSERT( KSNODE_WAVE_VOLUME == 1 ); +C_ASSERT( KSNODE_WAVE_MUTE == 2 ); +C_ASSERT( KSNODE_WAVE_PEAKMETER == 3 ); + + +//============================================================================= +// +// Note: +// 1) A sum node can be used to 'sum' multiple streams associated to different +// instances of the same pin. +// 2) Any node output has an implicit 'tee' node associated with it. Thus in the +// scenario below it is ok for the peak meter node to have two identical outputs. +// +// ---------------------------------------------------------- +// | | +// System Pin 0 | |-----| |--------| |------| |-----------| |--> 1 Loopback Pin +// [instances:0...n]-->| -->| Sum |--->| Volume |-->| Mute |-->| PeakMeter |--> | +// | |-----| |--------| |------| |-----------| |--> 2 KSPIN_WAVE_RENDER2_SOURCE +// | | +// ---------------------------------------------------------- + +static +PCCONNECTION_DESCRIPTOR HandsetSpeakerWaveMiniportConnections[] = +{ + { PCFILTER_NODE, KSPIN_WAVE_RENDER2_SINK_SYSTEM, KSNODE_WAVE_SUM, 1 }, + { KSNODE_WAVE_SUM, 0, KSNODE_WAVE_VOLUME, 1 }, + { KSNODE_WAVE_VOLUME, 0, KSNODE_WAVE_MUTE, 1 }, + { KSNODE_WAVE_MUTE, 0, KSNODE_WAVE_PEAKMETER, 1 }, + { KSNODE_WAVE_PEAKMETER, 2, PCFILTER_NODE, KSPIN_WAVE_RENDER2_SINK_LOOPBACK }, + { KSNODE_WAVE_PEAKMETER, 0, PCFILTER_NODE, KSPIN_WAVE_RENDER2_SOURCE }, +}; + +//============================================================================= +static +PCPROPERTY_ITEM PropertiesHandsetSpeakerWaveFilter[] = +{ + { + &KSPROPSETID_Pin, + KSPROPERTY_PIN_PROPOSEDATAFORMAT, + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_WaveFilter + }, +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationHandsetSpeakerWaveFilter, PropertiesHandsetSpeakerWaveFilter); + +//============================================================================= +static +PCFILTER_DESCRIPTOR HandsetSpeakerWaveMiniportFilterDescriptor = +{ + 0, // Version + &AutomationHandsetSpeakerWaveFilter, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(HandsetSpeakerWaveMiniportPins), // PinCount + HandsetSpeakerWaveMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + SIZEOF_ARRAY(HandsetSpeakerWaveMiniportNodes), // NodeCount + HandsetSpeakerWaveMiniportNodes, // Nodes + SIZEOF_ARRAY(HandsetSpeakerWaveMiniportConnections), // ConnectionCount + HandsetSpeakerWaveMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories - use defaults (audio, render, capture) +}; + +#endif // _SYSVAD_HANDSETSPEAKERWAVTABLE_H_ + diff --git a/audio/sysvad/PhoneAudioSample/michstopo.cpp b/audio/sysvad/PhoneAudioSample/michstopo.cpp new file mode 100644 index 000000000..0662cd970 --- /dev/null +++ b/audio/sysvad/PhoneAudioSample/michstopo.cpp @@ -0,0 +1,274 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + michstopo.cpp + +Abstract: + + Implementation of topology miniport for the mic (external: headset). + +--*/ + +#pragma warning (disable : 4127) + +#include +#include "simple.h" +#include "mintopo.h" +#include "michstopo.h" +#include "michstoptable.h" + + +#pragma code_seg("PAGE") + +//============================================================================= +NTSTATUS +PropertyHandler_MicHsJackDescription +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_Jack, KSPROPERTY_JACK_DESCRIPTION ) + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_MicHsJackDescription]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + ULONG nPinId = (ULONG)-1; + + ULONG cJackDescriptions = ARRAYSIZE(MicHsJackDescriptions); + PKSJACK_DESCRIPTION * JackDescriptions = MicHsJackDescriptions; + + if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + nPinId = *(PULONG(PropertyRequest->Instance)); + + if ((nPinId < cJackDescriptions) && (JackDescriptions[nPinId] != NULL)) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else + { + ULONG cbNeeded = sizeof(KSMULTIPLE_ITEM) + sizeof(KSJACK_DESCRIPTION); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbNeeded; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbNeeded) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PKSMULTIPLE_ITEM pMI = (PKSMULTIPLE_ITEM)PropertyRequest->Value; + PKSJACK_DESCRIPTION pDesc = (PKSJACK_DESCRIPTION)(pMI+1); + + pMI->Size = cbNeeded; + pMI->Count = 1; + + RtlCopyMemory(pDesc, JackDescriptions[nPinId], sizeof(KSJACK_DESCRIPTION)); + ntStatus = STATUS_SUCCESS; + } + } + } + } + } + + return ntStatus; +} + +//============================================================================= +NTSTATUS +PropertyHandler_MicHsJackDescription2 +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_Jack, KSPROPERTY_JACK_DESCRIPTION2 ) + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_MicHsJackDescription2]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + ULONG nPinId = (ULONG)-1; + + ULONG cJackDescriptions = ARRAYSIZE(MicHsJackDescriptions); + PKSJACK_DESCRIPTION * JackDescriptions = MicHsJackDescriptions; + + if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + nPinId = *(PULONG(PropertyRequest->Instance)); + + if ((nPinId < cJackDescriptions) && (JackDescriptions[nPinId] != NULL)) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else + { + ULONG cbNeeded = sizeof(KSMULTIPLE_ITEM) + sizeof(KSJACK_DESCRIPTION2); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbNeeded; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbNeeded) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PKSMULTIPLE_ITEM pMI = (PKSMULTIPLE_ITEM)PropertyRequest->Value; + PKSJACK_DESCRIPTION2 pDesc = (PKSJACK_DESCRIPTION2)(pMI+1); + + pMI->Size = cbNeeded; + pMI->Count = 1; + + RtlZeroMemory(pDesc, sizeof(KSJACK_DESCRIPTION2)); + + // + // Specifies the lower 16 bits of the DWORD parameter. This parameter indicates whether + // the jack is currently active, streaming, idle, or hardware not ready. + // + pDesc->DeviceStateInfo = 0; + + // + // From MSDN: + // "If an audio device lacks jack presence detection, the IsConnected member of + // the KSJACK_DESCRIPTION structure must always be set to TRUE. To remove the + // ambiguity that results from this dual meaning of the TRUE value for IsConnected, + // a client application can call IKsJackDescription2::GetJackDescription2 to read + // the JackCapabilities flag of the KSJACK_DESCRIPTION2 structure. If this flag has + // the JACKDESC2_PRESENCE_DETECT_CAPABILITY bit set, it indicates that the endpoint + // does in fact support jack presence detection. In that case, the return value of + // the IsConnected member can be interpreted to accurately reflect the insertion status + // of the jack." + // + // Bit definitions: + // 0x00000001 - JACKDESC2_PRESENCE_DETECT_CAPABILITY + // 0x00000002 - JACKDESC2_DYNAMIC_FORMAT_CHANGE_CAPABILITY + // + pDesc->JackCapabilities = JACKDESC2_PRESENCE_DETECT_CAPABILITY; + + ntStatus = STATUS_SUCCESS; + } + } + } + } + } + + return ntStatus; +} + +//============================================================================= +NTSTATUS +PropertyHandler_MicHsTopoFilter +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Redirects property request to miniport object + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_MicHsTopoFilter]")); + + // PropertryRequest structure is filled by portcls. + // MajorTarget is a pointer to miniport object for miniports. + // + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + // + // This line shows how to get a pointer to the miniport topology object. + // + PCMiniportTopology pMiniport = (PCMiniportTopology)PropertyRequest->MajorTarget; + UNREFERENCED_VAR(pMiniport); + + if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_Jack)) + { + if (PropertyRequest->PropertyItem->Id == KSPROPERTY_JACK_DESCRIPTION) + { + ntStatus = PropertyHandler_MicHsJackDescription(PropertyRequest); + } + else if (PropertyRequest->PropertyItem->Id == KSPROPERTY_JACK_DESCRIPTION2) + { + ntStatus = PropertyHandler_MicHsJackDescription2(PropertyRequest); + } + } + + return ntStatus; +} // PropertyHandler_MicHsTopoFilter + +#pragma code_seg() + diff --git a/audio/sysvad/PhoneAudioSample/michstopo.h b/audio/sysvad/PhoneAudioSample/michstopo.h new file mode 100644 index 000000000..b8f9b106f --- /dev/null +++ b/audio/sysvad/PhoneAudioSample/michstopo.h @@ -0,0 +1,21 @@ + +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + michstopo.h + +Abstract: + + Declaration of topology miniport for the mic (external: headset). + +--*/ + +#ifndef _SYSVAD_MICHSTOPO_H_ +#define _SYSVAD_MICHSTOPO_H_ + +NTSTATUS PropertyHandler_MicHsTopoFilter(_In_ PPCPROPERTY_REQUEST PropertyRequest); + +#endif // _SYSVAD_MICHSTOPO_H_ diff --git a/audio/sysvad/PhoneAudioSample/michstoptable.h b/audio/sysvad/PhoneAudioSample/michstoptable.h new file mode 100644 index 000000000..d3d57dd21 --- /dev/null +++ b/audio/sysvad/PhoneAudioSample/michstoptable.h @@ -0,0 +1,246 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + michstopotable.h + +Abstract: + + Declaration of topology table for the mic (external: headset) + +--*/ + +#ifndef _SYSVAD_MICHSTOPTABLE_H_ +#define _SYSVAD_MICHSTOPTABLE_H_ + +// Function declarations. +NTSTATUS +PropertyHandler_MicHsTopoFilter( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + +//============================================================================= +static +KSDATARANGE MicHsTopoPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +//============================================================================= +static +PKSDATARANGE MicHsTopoPinDataRangePointersBridge[] = +{ + &MicHsTopoPinDataRangesBridge[0] +}; + +//============================================================================= +static +PCPIN_DESCRIPTOR MicHsTopoMiniportPins[] = +{ + // KSPIN - topology filter in-pin + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(MicHsTopoPinDataRangePointersBridge),// DataRangesCount + MicHsTopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_IN, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSNODETYPE_HEADSET_MICROPHONE, // Category + NULL, // Name + 0 // Reserved + } + }, + + // KSPIN - topology filter out-in + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(MicHsTopoPinDataRangePointersBridge),// DataRangesCount + MicHsTopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_OUT, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSCATEGORY_AUDIO, // Category + NULL, // Name + 0 // Reserved + } + } +}; + +//============================================================================= +static +KSJACK_DESCRIPTION MicHsJackDesc = +{ + KSAUDIO_SPEAKER_MONO, + JACKDESC_RGB(179, 201, 140), + eConnTypeCombination, + eGeoLocRear, + eGenLocPrimaryBox, + ePortConnJack, + FALSE // NOTE: For convenience, wired headset jacks will be "unplugged" at boot. + // However, we need to introduce a test hook to toggle jack state of this and other endpoints. +}; + +//============================================================================= +// Only return a KSJACK_DESCRIPTION for the physical bridge pin. +static +PKSJACK_DESCRIPTION MicHsJackDescriptions[] = +{ + &MicHsJackDesc, + NULL +}; + +//============================================================================= +static +PCPROPERTY_ITEM MicHsPropertiesVolume[] = +{ + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_VOLUMELEVEL, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_Topology + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationMicHsVolume, MicHsPropertiesVolume); + +//============================================================================= +static +PCPROPERTY_ITEM MicHsPropertiesMute[] = +{ + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_MUTE, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_Topology + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationMicHsMute, MicHsPropertiesMute); + +//============================================================================= +static +PCPROPERTY_ITEM MicHsPropertiesPeakMeter[] = +{ + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_PEAKMETER2, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_Topology + }, + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_CPU_RESOURCES, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_Topology + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationMicHsPeakMeter, MicHsPropertiesPeakMeter); + +//============================================================================= +static +PCNODE_DESCRIPTOR MicHsTopologyNodes[] = +{ + // KSNODE_TOPO_VOLUME + { + 0, // Flags + &AutomationMicHsVolume, // AutomationTable + &KSNODETYPE_VOLUME, // Type + &KSAUDFNAME_MIC_VOLUME // Name + }, + // KSNODE_TOPO_MUTE + { + 0, // Flags + &AutomationMicHsMute, // AutomationTable + &KSNODETYPE_MUTE, // Type + &KSAUDFNAME_MIC_MUTE // Name + }, + // KSNODE_TOPO_PEAKMETER + { + 0, // Flags + &AutomationMicHsPeakMeter, // AutomationTable + &KSNODETYPE_PEAKMETER, // Type + &KSAUDFNAME_PEAKMETER // Name + } +}; + +C_ASSERT( KSNODE_TOPO_VOLUME == 0 ); +C_ASSERT( KSNODE_TOPO_MUTE == 1 ); +C_ASSERT( KSNODE_TOPO_PEAKMETER == 2 ); + +//============================================================================= +static +PCCONNECTION_DESCRIPTOR MicHsMiniportConnections[] = +{ + // FromNode, FromPin, ToNode, ToPin + { PCFILTER_NODE, KSPIN_TOPO_MIC_ELEMENTS, KSNODE_TOPO_VOLUME, 1 }, + { KSNODE_TOPO_VOLUME, 0, KSNODE_TOPO_MUTE, 1 }, + { KSNODE_TOPO_MUTE, 0, KSNODE_TOPO_PEAKMETER, 1 }, + { KSNODE_TOPO_PEAKMETER, 0, PCFILTER_NODE, KSPIN_TOPO_BRIDGE } +}; + + +//============================================================================= +static +PCPROPERTY_ITEM MicHsPropertiesTopoFilter[] = +{ + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_MicHsTopoFilter + }, + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION2, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_MicHsTopoFilter + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationMicHsTopoFilter, MicHsPropertiesTopoFilter); + +//============================================================================= +static +PCFILTER_DESCRIPTOR MicHsTopoMiniportFilterDescriptor = +{ + 0, // Version + &AutomationMicHsTopoFilter, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(MicHsTopoMiniportPins), // PinCount + MicHsTopoMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + SIZEOF_ARRAY(MicHsTopologyNodes), // NodeCount + MicHsTopologyNodes, // Nodes + SIZEOF_ARRAY(MicHsMiniportConnections), // ConnectionCount + MicHsMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories +}; + +#endif // _SYSVAD_MICHSTOPTABLE_H_ + diff --git a/audio/sysvad/PhoneAudioSample/michswavtable.h b/audio/sysvad/PhoneAudioSample/michswavtable.h new file mode 100644 index 000000000..5d0ab11fa --- /dev/null +++ b/audio/sysvad/PhoneAudioSample/michswavtable.h @@ -0,0 +1,441 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + michswavtable.h + +Abstract: + + Declaration of wave miniport tables for the mic (external: headset). + +--*/ + +#ifndef _SYSVAD_MICHSWAVTABLE_H_ +#define _SYSVAD_MICHSWAVTABLE_H_ + +// +// Mic in (external: headset) range. +// +#define MICHS_DEVICE_MAX_CHANNELS 1 // Max Channels. +#define MICHS_MIN_BITS_PER_SAMPLE_PCM 16 // Min Bits Per Sample +#define MICHS_MAX_BITS_PER_SAMPLE_PCM 16 // Max Bits Per Sample +#define MICHS_MIN_SAMPLE_RATE 8000 // Min Sample Rate +#define MICHS_MAX_SAMPLE_RATE 48000 // Max Sample Rate + +// +// Max # of pin instances. +// +#define MICHS_MAX_INPUT_STREAMS 2 // Raw + Default streams + +//============================================================================= +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE MicHsPinSupportedDeviceFormats[] = +{ + { // 0 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 8000, + 16000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 1 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 11025, + 22050, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 2 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 16000, + 32000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 3 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 22050, + 44100, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 4 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 24000, + 48000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 5 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 32000, + 64000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 6 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 44100, + 88200, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 7 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 48000, + 96000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +// +// Supported modes (only on streaming pins). +// +static +MODE_AND_DEFAULT_FORMAT MicHsPinSupportedDeviceModes[] = +{ + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_RAW, + &MicHsPinSupportedDeviceFormats[SIZEOF_ARRAY(MicHsPinSupportedDeviceFormats) - 1].DataFormat, // 48KHz + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_DEFAULT, + &MicHsPinSupportedDeviceFormats[SIZEOF_ARRAY(MicHsPinSupportedDeviceFormats) - 1].DataFormat, // 48KHz + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_SPEECH, + &MicHsPinSupportedDeviceFormats[2].DataFormat, // 16KHz + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_COMMUNICATIONS, + &MicHsPinSupportedDeviceFormats[4].DataFormat, // 24KHz + } +}; + +// +// The entries here must follow the same order as the filter's pin +// descriptor array. +// +static +PIN_DEVICE_FORMATS_AND_MODES MicHsPinDeviceFormatsAndModes[] = +{ + { + BridgePin, + NULL, + 0, + NULL, + 0 + }, + { + SystemCapturePin, + MicHsPinSupportedDeviceFormats, + SIZEOF_ARRAY(MicHsPinSupportedDeviceFormats), + MicHsPinSupportedDeviceModes, + SIZEOF_ARRAY(MicHsPinSupportedDeviceModes) + } +}; + +//============================================================================= +static +KSDATARANGE_AUDIO MicHsPinDataRangesStream[] = +{ + { + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + MICHS_DEVICE_MAX_CHANNELS, + MICHS_MIN_BITS_PER_SAMPLE_PCM, + MICHS_MAX_BITS_PER_SAMPLE_PCM, + MICHS_MIN_SAMPLE_RATE, + MICHS_MAX_SAMPLE_RATE + }, +}; + +static +PKSDATARANGE MicHsPinDataRangePointersStream[] = +{ + PKSDATARANGE(&MicHsPinDataRangesStream[0]), + PKSDATARANGE(&PinDataRangeAttributeList), +}; + +//============================================================================= +static +KSDATARANGE MicHsPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +static +PKSDATARANGE MicHsPinDataRangePointersBridge[] = +{ + &MicHsPinDataRangesBridge[0] +}; + +//============================================================================= +static +PCPIN_DESCRIPTOR MicHsWaveMiniportPins[] = +{ + // Wave In Bridge Pin (Capture - From Topology) KSPIN_WAVE_BRIDGE + { + 0, + 0, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(MicHsPinDataRangePointersBridge), + MicHsPinDataRangePointersBridge, + KSPIN_DATAFLOW_IN, + KSPIN_COMMUNICATION_NONE, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, + + // Wave In Streaming Pin (Capture) KSPIN_WAVEIN_HOST + { + MICHS_MAX_INPUT_STREAMS, + MICHS_MAX_INPUT_STREAMS, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(MicHsPinDataRangePointersStream), + MicHsPinDataRangePointersStream, + KSPIN_DATAFLOW_OUT, + KSPIN_COMMUNICATION_SINK, + &KSCATEGORY_AUDIO, + &KSAUDFNAME_RECORDING_CONTROL, + 0 + } + } +}; + +//============================================================================= +static +PCNODE_DESCRIPTOR MicHsWaveMiniportNodes[] = +{ + // KSNODE_WAVE_ADC + { + 0, // Flags + NULL, // AutomationTable + &KSNODETYPE_ADC, // Type + NULL // Name + } +}; + +//============================================================================= +static +PCCONNECTION_DESCRIPTOR MicHsWaveMiniportConnections[] = +{ + { PCFILTER_NODE, KSPIN_WAVE_BRIDGE, KSNODE_WAVE_ADC, 1 }, + { KSNODE_WAVE_ADC, 0, PCFILTER_NODE, KSPIN_WAVEIN_HOST } +}; + +//============================================================================= +static +PCPROPERTY_ITEM PropertiesMicHsWaveFilter[] = +{ + { + &KSPROPSETID_General, + KSPROPERTY_GENERAL_COMPONENTID, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_WaveFilter + }, + { + &KSPROPSETID_Pin, + KSPROPERTY_PIN_PROPOSEDATAFORMAT, + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_WaveFilter + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationMicHsWaveFilter, PropertiesMicHsWaveFilter); + +//============================================================================= +static +PCFILTER_DESCRIPTOR MicHsWaveMiniportFilterDescriptor = +{ + 0, // Version + &AutomationMicHsWaveFilter, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(MicHsWaveMiniportPins), // PinCount + MicHsWaveMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + SIZEOF_ARRAY(MicHsWaveMiniportNodes), // NodeCount + MicHsWaveMiniportNodes, // Nodes + SIZEOF_ARRAY(MicHsWaveMiniportConnections), // ConnectionCount + MicHsWaveMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories - use defaults (audio, render, capture) +}; + +#endif // _SYSVAD_MICHSWAVTABLE_H_ diff --git a/audio/sysvad/PhoneAudioSample/minipairs.h b/audio/sysvad/PhoneAudioSample/minipairs.h new file mode 100644 index 000000000..3f7e9f24f --- /dev/null +++ b/audio/sysvad/PhoneAudioSample/minipairs.h @@ -0,0 +1,504 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + minipairs.h + +Abstract: + + Local audio endpoint filter definitions. + +--*/ + +#ifndef _SYSVAD_MINIPAIRS_H_ +#define _SYSVAD_MINIPAIRS_H_ + +#include "speakertopo.h" +#include "speakertoptable.h" +#include "speakerwavtable.h" + +#include "speakerhstopo.h" +#include "speakerhstoptable.h" +#include "speakerhswavtable.h" + +#include "michstopo.h" +#include "michstoptable.h" +#include "michswavtable.h" + +#include "micarraytopo.h" +#include "micarray1toptable.h" +#include "micarraywavtable.h" + +#include "cellulartopo.h" +#include "cellularwave.h" +#include "cellulartoptable.h" +#include "cellularwavtable.h" + +#include "fmtopo.h" +#include "fmwave.h" +#include "fmtoptable.h" +#include "fmwavtable.h" + +#include "handsetspeakertopo.h" +#include "handsetspeakertoptable.h" +#include "handsetspeakerwavtable.h" + +#include "handsetmictopo.h" +#include "handsetmictoptable.h" +#include "handsetmicwavtable.h" + + +NTSTATUS +CreateMiniportWaveRTSYSVAD +( + _Out_ PUNKNOWN *, + _In_ REFCLSID, + _In_opt_ PUNKNOWN, + _In_ POOL_TYPE, + _In_ PUNKNOWN, + _In_opt_ PVOID, + _In_ PENDPOINT_MINIPAIR +); + +NTSTATUS +CreateMiniportTopologySYSVAD +( + _Out_ PUNKNOWN *, + _In_ REFCLSID, + _In_opt_ PUNKNOWN, + _In_ POOL_TYPE, + _In_ PUNKNOWN, + _In_opt_ PVOID, + _In_ PENDPOINT_MINIPAIR +); + +// +// Render miniports. +// + +/********************************************************************* +* Topology/Wave bridge connection for speaker (internal) * +* * +* +------+ +------+ * +* | Wave | | Topo | * +* | | | | * +* System --->|0 2|---> Loopback | | * +* | | | | * +* Offload --->|1 3|--------------->|0 1|---> Line Out * +* | | | | * +* +------+ +------+ * +*********************************************************************/ +static +PHYSICALCONNECTIONTABLE SpeakerTopologyPhysicalConnections[] = +{ + { + KSPIN_TOPO_WAVEOUT_SOURCE, // TopologyIn + KSPIN_WAVE_RENDER_SOURCE, // WaveOut + CONNECTIONTYPE_WAVE_OUTPUT + } +}; + +static +ENDPOINT_MINIPAIR SpeakerMiniports = +{ + eSpeakerDevice, + SPEAKER_TOPONAME, // defined in cellulartopo.h for use with cellular routing control + CreateMiniportTopologySYSVAD, + &SpeakerTopoMiniportFilterDescriptor, + 0, NULL, // Interface properties + L"WaveSpeaker", // make sure this name matches with KSNAME_WaveSpeaker in the inf's [Strings] section + CreateMiniportWaveRTSYSVAD, + &SpeakerWaveMiniportFilterDescriptor, + 0, NULL, // Interface properties + SPEAKER_DEVICE_MAX_CHANNELS, + SpeakerPinDeviceFormatsAndModes, + SIZEOF_ARRAY(SpeakerPinDeviceFormatsAndModes), + SpeakerTopologyPhysicalConnections, + SIZEOF_ARRAY(SpeakerTopologyPhysicalConnections), + ENDPOINT_OFFLOAD_SUPPORTED +}; + +/********************************************************************* +* Topology/Wave bridge connection for speaker (external:headset) * +* * +* +------+ +------+ * +* | Wave | | Topo | * +* | | | | * +* System --->|0 2|---> Loopback | | * +* | | | | * +* Offload --->|1 3|--------------->|0 1|---> Line Out * +* | | | | * +* +------+ +------+ * +*********************************************************************/ +static +PHYSICALCONNECTIONTABLE SpeakerHsTopologyPhysicalConnections[] = +{ + { + KSPIN_TOPO_WAVEOUT_SOURCE, // TopologyIn + KSPIN_WAVE_RENDER_SOURCE, // WaveOut + CONNECTIONTYPE_WAVE_OUTPUT + } +}; + +static +ENDPOINT_MINIPAIR SpeakerHsMiniports = +{ + eSpeakerHsDevice, + SPEAKER_HEADSET_TOPONAME, // defined in cellulartopo.h for use with cellular routing control + CreateMiniportTopologySYSVAD, + &SpeakerHsTopoMiniportFilterDescriptor, // use the speaker headset topo filter descriptor, not headset. + 0, NULL, // Interface properties + L"WaveSpeakerHeadset", // make sure this name matches with KSNAME_WaveSpeakerHeadset in the inf's [Strings] section + CreateMiniportWaveRTSYSVAD, + &SpeakerHsWaveMiniportFilterDescriptor, + 0, NULL, // Interface properties + SPEAKERHS_DEVICE_MAX_CHANNELS, + SpeakerHsPinDeviceFormatsAndModes, + SIZEOF_ARRAY(SpeakerHsPinDeviceFormatsAndModes), + SpeakerHsTopologyPhysicalConnections, + SIZEOF_ARRAY(SpeakerHsTopologyPhysicalConnections), + ENDPOINT_OFFLOAD_SUPPORTED +}; + +// +// headset microphone miniports. +// + +/********************************************************************* +* Topology/Wave bridge connection for mic in * +* * +* +------+ +------+ * +* | Topo | | Wave | * +* | | | | * +* Mic in --->|0 1|===>|0 1|---> Capture Host Pin * +* | | | | * +* +------+ +------+ * +*********************************************************************/ +static +PHYSICALCONNECTIONTABLE MicHsTopologyPhysicalConnections[] = +{ + { + KSPIN_TOPO_BRIDGE, // TopologyOut + KSPIN_WAVE_BRIDGE, // WaveIn + CONNECTIONTYPE_TOPOLOGY_OUTPUT + } +}; + +static +ENDPOINT_MINIPAIR MicHsMiniports = +{ + eMicHsDevice, + MIC_HEADSET_TOPONAME, // defined in cellulartopo.h for use with cellular routing control + CreateMiniportTopologySYSVAD, + &MicHsTopoMiniportFilterDescriptor, + 0, NULL, // Interface properties + L"WaveMicHeadset", // make sure this name matches with KSNAME_WaveMicHeadset in the inf's [Strings] section + CreateMiniportWaveRTSYSVAD, + &MicHsWaveMiniportFilterDescriptor, + 0, NULL, // Interface properties + MICHS_DEVICE_MAX_CHANNELS, + MicHsPinDeviceFormatsAndModes, + SIZEOF_ARRAY(MicHsPinDeviceFormatsAndModes), + MicHsTopologyPhysicalConnections, + SIZEOF_ARRAY(MicHsTopologyPhysicalConnections), + ENDPOINT_NO_FLAGS +}; + + +// +// Capture miniports. +// + +/********************************************************************* +* Topology/Wave bridge connection for mic array 1 (front) * +* * +* +------+ +------+ * +* | Topo | | Wave | * +* | | | | * +* Mic in --->|0 1|===>|0 1|---> Capture Host Pin * +* | | | | * +* +------+ +------+ * +*********************************************************************/ +static +PHYSICALCONNECTIONTABLE MicArray1TopologyPhysicalConnections[] = +{ + { + KSPIN_TOPO_BRIDGE, // TopologyOut + KSPIN_WAVE_BRIDGE, // WaveIn + CONNECTIONTYPE_TOPOLOGY_OUTPUT + } +}; + +static +ENDPOINT_MINIPAIR MicArray1Miniports = +{ + eMicArrayDevice1, + L"TopologyMicArray1", // make sure this name matches with KSNAME_TopologyMicArray1 in the inf's [Strings] section + CreateMicArrayMiniportTopology, + &MicArray1TopoMiniportFilterDescriptor, + 0, NULL, // Interface properties + L"WaveMicArray1", // make sure this name matches with KSNAME_WaveMicArray1 in the inf's [Strings] section + CreateMiniportWaveRTSYSVAD, + &MicArrayWaveMiniportFilterDescriptor, + 0, NULL, // Interface properties + MICARRAY_DEVICE_MAX_CHANNELS, + MicArrayPinDeviceFormatsAndModes, + SIZEOF_ARRAY(MicArrayPinDeviceFormatsAndModes), + MicArray1TopologyPhysicalConnections, + SIZEOF_ARRAY(MicArray1TopologyPhysicalConnections), + ENDPOINT_SOUNDDETECTOR_SUPPORTED +}; + +/********************************************************************* +* Topology/Wave bridge connection for cellular endpoint * +* * +* +------+ +------+ * +* | Wave1| | Topo | * +* | | | | * +* | | | | * +* | | | | * +* System BiDi <--|0 1|<---------------|1 0|<-- BIDI * +* | | | | * +* +------+ | | * +* | | * +* +------+ | | * +* | Wave2| | | * +* | | | | * +* | | | | * +* | | | | * +* System BiDi <--|0 1|<---------------|3 2|<-- BIDI * +* | | | | * +* +------+ +------+ * +* * +*********************************************************************/ + +static +PHYSICALCONNECTIONTABLE CellularTopologyPhysicalConnections[] = +{ + { + KSPIN_TOPO_BIDI1_BRIDGE, // TopologyOut + KSPIN_WAVE_BIDI_BRIDGE, // WaveIn + CONNECTIONTYPE_TOPOLOGY_OUTPUT + } +}; + +static +ENDPOINT_MINIPAIR CellularMiniports = +{ + eCellularDevice, + L"TopologyCellular", // make sure this name matches with KSNAME_TopologyCellular in the inf's [Strings] section + CreateCellularMiniportTopology, + &CellularTopoMiniportFilterDescriptor, + 0, NULL, // Interface properties + L"WaveCellular", // make sure this name matches with KSNAME_WaveCellular in the inf's [Strings] section + CreateCellularMiniportWaveRT, + &CellularWaveMiniportFilterDescriptor, + 0, NULL, // Interface properties + CELLULAR_DEVICE_MAX_CHANNELS, + CellularPinDeviceFormatsAndModes, + SIZEOF_ARRAY(CellularPinDeviceFormatsAndModes), + CellularTopologyPhysicalConnections, + SIZEOF_ARRAY(CellularTopologyPhysicalConnections), + ENDPOINT_CELLULAR_PROVIDER1 +}; + + +static +PHYSICALCONNECTIONTABLE CellularTopologyPhysicalConnections2[] = +{ + { + KSPIN_TOPO_BIDI2_BRIDGE, // TopologyOut + KSPIN_WAVE_BIDI_BRIDGE, // WaveIn + CONNECTIONTYPE_TOPOLOGY_OUTPUT + } +}; + +static +ENDPOINT_MINIPAIR CellularMiniports2 = +{ + eCellularDevice, + L"TopologyCellular", // make sure this name matches with KSNAME_TopologyCellular in the inf's [Strings] section + CreateCellularMiniportTopology, + &CellularTopoMiniportFilterDescriptor, + 0, NULL, // Interface properties + L"WaveCellular2", // make sure this name matches with KSNAME_WaveCellular in the inf's [Strings] section + CreateCellularMiniportWaveRT, + &CellularWaveMiniportFilterDescriptor, + 0, NULL, // Interface properties + CELLULAR_DEVICE_MAX_CHANNELS, + CellularPinDeviceFormatsAndModes, + SIZEOF_ARRAY(CellularPinDeviceFormatsAndModes), + CellularTopologyPhysicalConnections2, + SIZEOF_ARRAY(CellularTopologyPhysicalConnections2), + ENDPOINT_CELLULAR_PROVIDER2 +}; + +/********************************************************************* +* Topology/Wave bridge connection for handset speaker endpoint * +* * +* +------+ +------+ * +* | Wave | | Topo | * +* | | | | * +* System --->|0 1|---> Loopback | | * +* | | | | * +* | 2|--------------->|0 1|---> Line Out * +* | | | | * +* +------+ +------+ * +*********************************************************************/ +static +PHYSICALCONNECTIONTABLE HandsetSpeakerTopologyPhysicalConnections[] = +{ + { + KSPIN_TOPO_WAVEOUT_SOURCE, // TopologyIn + KSPIN_WAVE_RENDER2_SOURCE, // WaveOut (no offloading) + CONNECTIONTYPE_WAVE_OUTPUT + } +}; + +static +ENDPOINT_MINIPAIR HandsetSpeakerMiniports = +{ + eHandsetSpeakerDevice, + HANDSET_SPEAKER_TOPONAME, // defined in cellulartopo.h for use with cellular routing control + CreateHandsetSpeakerMiniportTopology, + &HandsetSpeakerTopoMiniportFilterDescriptor, + 0, NULL, // Interface properties + L"WaveHandsetSpeaker", // make sure this name matches with KSNAME_WaveHandsetSpeaker in the inf's [Strings] section + CreateMiniportWaveRTSYSVAD, + &HandsetSpeakerWaveMiniportFilterDescriptor, + 0, NULL, // Interface properties + HANDSETSPEAKER_DEVICE_MAX_CHANNELS, + HandsetSpeakerPinDeviceFormatsAndModes, + SIZEOF_ARRAY(HandsetSpeakerPinDeviceFormatsAndModes), + HandsetSpeakerTopologyPhysicalConnections, + SIZEOF_ARRAY(HandsetSpeakerTopologyPhysicalConnections), + ENDPOINT_NO_FLAGS +}; + +// +// handset microphone miniports. +// + +/********************************************************************* +* Topology/Wave bridge connection for mic in * +* * +* +------+ +------+ * +* | Topo | | Wave | * +* | | | | * +* Mic in --->|0 1|===>|0 1|---> Capture Host Pin * +* | | | | * +* +------+ +------+ * +*********************************************************************/ +static +PHYSICALCONNECTIONTABLE HandsetMicTopologyPhysicalConnections[] = +{ + { + KSPIN_TOPO_BRIDGE, // TopologyOut + KSPIN_WAVE_BRIDGE, // WaveIn + CONNECTIONTYPE_TOPOLOGY_OUTPUT + } +}; + +static +ENDPOINT_MINIPAIR HandsetMicMiniports = +{ + eHandsetMicDevice, + HANDSET_MIC_TOPONAME, // defined in cellulartopo.h for use with cellular routing control + CreateMiniportTopologySYSVAD, + &HandsetMicTopoMiniportFilterDescriptor, + 0, NULL, // Interface properties + L"WaveHandsetMic", // make sure this name matches with KSNAME_WaveHandsetMic in the inf's [Strings] section + CreateMiniportWaveRTSYSVAD, + &HandsetMicWaveMiniportFilterDescriptor, + 0, NULL, // Interface properties + HANDSETMIC_DEVICE_MAX_CHANNELS, + HandsetMicPinDeviceFormatsAndModes, + SIZEOF_ARRAY(HandsetMicPinDeviceFormatsAndModes), + HandsetMicTopologyPhysicalConnections, + SIZEOF_ARRAY(HandsetMicTopologyPhysicalConnections), + ENDPOINT_NO_FLAGS +}; + +/********************************************************************* +* Topology/Wave bridge connection for FM RX * +* * +* +------+ +------+ * +* | Topo | | Wave | * +* | | | | * +* FM RX --->|0 1|===>|1 0|---> Capture Host Pin * +* | | | | * +* +------+ +------+ * +*********************************************************************/ +static +PHYSICALCONNECTIONTABLE FmRxTopologyPhysicalConnections[] = +{ + { + KSPIN_TOPO_FMRX_BRIDGE, // TopologyOut + KSPIN_WAVE_FMRX_BRIDGE, // WaveIn + CONNECTIONTYPE_TOPOLOGY_OUTPUT + } +}; + +static +ENDPOINT_MINIPAIR FmRxMiniports = +{ + eFmRxDevice, + L"TopologyFmRx", // make sure this name matches with KSNAME_TopologyFmRx + CreateFmMiniportTopology, + &FmTopoMiniportFilterDescriptor, + 0, NULL, // Interface properties + L"WaveFmRx", // make sure this name matches with KSNAME_WaveFmRx in the inf's [Strings] section + CreateFmMiniportWaveRT, + &FmWaveMiniportFilterDescriptor, + 0, NULL, // Interface properties + FMRX_DEVICE_MAX_CHANNELS, + FmRxPinDeviceFormatsAndModes, + SIZEOF_ARRAY(FmRxPinDeviceFormatsAndModes), + FmRxTopologyPhysicalConnections, + SIZEOF_ARRAY(FmRxTopologyPhysicalConnections), + ENDPOINT_NO_FLAGS +}; + +//============================================================================= +// +// Render miniport pairs. NOTE: the split of render and capture is arbitrary and +// unnessary, this array could contain capture endpoints. +// +static +PENDPOINT_MINIPAIR g_RenderEndpoints[] = +{ + &SpeakerMiniports, + &SpeakerHsMiniports, + &HandsetSpeakerMiniports, + &CellularMiniports, // SIM 1 + &CellularMiniports2 // SIM 2 +}; + +#define g_cRenderEndpoints (SIZEOF_ARRAY(g_RenderEndpoints)) + +//============================================================================= +// +// Capture miniport pairs. NOTE: the split of render and capture is arbitrary and +// unnessary, this array could contain render endpoints. +// +static +PENDPOINT_MINIPAIR g_CaptureEndpoints[] = +{ + &MicHsMiniports, + &MicArray1Miniports, + &HandsetMicMiniports, + &FmRxMiniports +}; + +#define g_cCaptureEndpoints (SIZEOF_ARRAY(g_CaptureEndpoints)) + +//============================================================================= +// +// Total miniports = # endpoints * 2 (topology + wave). +// +#define g_MaxMiniports ((g_cRenderEndpoints + g_cCaptureEndpoints) * 2) + +#endif // _SYSVAD_MINIPAIRS_H_ + diff --git a/audio/sysvad/PhoneAudioSample/speakerhstopo.cpp b/audio/sysvad/PhoneAudioSample/speakerhstopo.cpp new file mode 100644 index 000000000..4d69ac040 --- /dev/null +++ b/audio/sysvad/PhoneAudioSample/speakerhstopo.cpp @@ -0,0 +1,274 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + speakerhstopo.cpp + +Abstract: + + Implementation of topology miniport for the speaker (external: headset). + +--*/ + +#pragma warning (disable : 4127) + +#include +#include "simple.h" +#include "mintopo.h" +#include "speakerhstopo.h" +#include "speakerhstoptable.h" + + +#pragma code_seg("PAGE") + +//============================================================================= +NTSTATUS +PropertyHandler_SpeakerHsJackDescription +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_Jack, KSPROPERTY_JACK_DESCRIPTION ) + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_SpeakerHsJackDescription]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + ULONG nPinId = (ULONG)-1; + + ULONG cJackDescriptions = ARRAYSIZE(SpeakerHsJackDescriptions); + PKSJACK_DESCRIPTION * JackDescriptions = SpeakerHsJackDescriptions; + + if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + nPinId = *(PULONG(PropertyRequest->Instance)); + + if ((nPinId < cJackDescriptions) && (JackDescriptions[nPinId] != NULL)) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else + { + ULONG cbNeeded = sizeof(KSMULTIPLE_ITEM) + sizeof(KSJACK_DESCRIPTION); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbNeeded; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbNeeded) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PKSMULTIPLE_ITEM pMI = (PKSMULTIPLE_ITEM)PropertyRequest->Value; + PKSJACK_DESCRIPTION pDesc = (PKSJACK_DESCRIPTION)(pMI+1); + + pMI->Size = cbNeeded; + pMI->Count = 1; + + RtlCopyMemory(pDesc, JackDescriptions[nPinId], sizeof(KSJACK_DESCRIPTION)); + ntStatus = STATUS_SUCCESS; + } + } + } + } + } + + return ntStatus; +} + +//============================================================================= +NTSTATUS +PropertyHandler_SpeakerHsJackDescription2 +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_Jack, KSPROPERTY_JACK_DESCRIPTION2 ) + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_SpeakerHsJackDescription2]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + ULONG nPinId = (ULONG)-1; + + ULONG cJackDescriptions = ARRAYSIZE(SpeakerHsJackDescriptions); + PKSJACK_DESCRIPTION * JackDescriptions = SpeakerHsJackDescriptions; + + if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + nPinId = *(PULONG(PropertyRequest->Instance)); + + if ((nPinId < cJackDescriptions) && (JackDescriptions[nPinId] != NULL)) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else + { + ULONG cbNeeded = sizeof(KSMULTIPLE_ITEM) + sizeof(KSJACK_DESCRIPTION2); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbNeeded; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbNeeded) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PKSMULTIPLE_ITEM pMI = (PKSMULTIPLE_ITEM)PropertyRequest->Value; + PKSJACK_DESCRIPTION2 pDesc = (PKSJACK_DESCRIPTION2)(pMI+1); + + pMI->Size = cbNeeded; + pMI->Count = 1; + + RtlZeroMemory(pDesc, sizeof(KSJACK_DESCRIPTION2)); + + // + // Specifies the lower 16 bits of the DWORD parameter. This parameter indicates whether + // the jack is currently active, streaming, idle, or hardware not ready. + // + pDesc->DeviceStateInfo = 0; + + // + // From MSDN: + // "If an audio device lacks jack presence detection, the IsConnected member of + // the KSJACK_DESCRIPTION structure must always be set to TRUE. To remove the + // ambiguity that results from this dual meaning of the TRUE value for IsConnected, + // a client application can call IKsJackDescription2::GetJackDescription2 to read + // the JackCapabilities flag of the KSJACK_DESCRIPTION2 structure. If this flag has + // the JACKDESC2_PRESENCE_DETECT_CAPABILITY bit set, it indicates that the endpoint + // does in fact support jack presence detection. In that case, the return value of + // the IsConnected member can be interpreted to accurately reflect the insertion status + // of the jack." + // + // Bit definitions: + // 0x00000001 - JACKDESC2_PRESENCE_DETECT_CAPABILITY + // 0x00000002 - JACKDESC2_DYNAMIC_FORMAT_CHANGE_CAPABILITY + // + pDesc->JackCapabilities = JACKDESC2_PRESENCE_DETECT_CAPABILITY; + + ntStatus = STATUS_SUCCESS; + } + } + } + } + } + + return ntStatus; +} + +//============================================================================= +NTSTATUS +PropertyHandler_SpeakerHsTopoFilter +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Redirects property request to miniport object + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_SpeakerHsTopoFilter]")); + + // PropertryRequest structure is filled by portcls. + // MajorTarget is a pointer to miniport object for miniports. + // + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + // + // This line shows how to get a pointer to the miniport topology object. + // + PCMiniportTopology pMiniport = (PCMiniportTopology)PropertyRequest->MajorTarget; + UNREFERENCED_VAR(pMiniport); + + if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_Jack)) + { + if (PropertyRequest->PropertyItem->Id == KSPROPERTY_JACK_DESCRIPTION) + { + ntStatus = PropertyHandler_SpeakerHsJackDescription(PropertyRequest); + } + else if (PropertyRequest->PropertyItem->Id == KSPROPERTY_JACK_DESCRIPTION2) + { + ntStatus = PropertyHandler_SpeakerHsJackDescription2(PropertyRequest); + } + } + + return ntStatus; +} // PropertyHandler_SpeakerHsTopoFilter + +#pragma code_seg() + diff --git a/audio/sysvad/PhoneAudioSample/speakerhstopo.h b/audio/sysvad/PhoneAudioSample/speakerhstopo.h new file mode 100644 index 000000000..ea7c760f5 --- /dev/null +++ b/audio/sysvad/PhoneAudioSample/speakerhstopo.h @@ -0,0 +1,21 @@ + +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + speakerhstopo.h + +Abstract: + + Declaration of topology miniport for the speaker (external: headset). + +--*/ + +#ifndef _SYSVAD_SPEAKERHSTOPO_H_ +#define _SYSVAD_SPEAKERHSTOPO_H_ + +NTSTATUS PropertyHandler_SpeakerHsTopoFilter(_In_ PPCPROPERTY_REQUEST PropertyRequest); + +#endif // _SYSVAD_SPEAKERHSTOPO_H_ diff --git a/audio/sysvad/PhoneAudioSample/speakerhstoptable.h b/audio/sysvad/PhoneAudioSample/speakerhstoptable.h new file mode 100644 index 000000000..15d2e86c7 --- /dev/null +++ b/audio/sysvad/PhoneAudioSample/speakerhstoptable.h @@ -0,0 +1,157 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + speakerhstoptable.h + +Abstract: + + Declaration of topology tables for the speaker (external: headset). + +--*/ + +#ifndef _SYSVAD_SPEAKERHSTOPTABLE_H_ +#define _SYSVAD_SPEAKERHSTOPTABLE_H_ + +//============================================================================= +static +KSDATARANGE SpeakerHsTopoPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +//============================================================================= +static +PKSDATARANGE SpeakerHsTopoPinDataRangePointersBridge[] = +{ + &SpeakerHsTopoPinDataRangesBridge[0] +}; + +//============================================================================= +static +PCPIN_DESCRIPTOR SpeakerHsTopoMiniportPins[] = +{ + // KSPIN - topology filter in-pin + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(SpeakerHsTopoPinDataRangePointersBridge),// DataRangesCount + SpeakerHsTopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_IN, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSCATEGORY_AUDIO, // Category + NULL, // Name + 0 // Reserved + } + }, + // KSPIN - topology filter out-pin + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(SpeakerHsTopoPinDataRangePointersBridge),// DataRangesCount + SpeakerHsTopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_OUT, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSNODETYPE_HEADSET_SPEAKERS, // Category + NULL, // Name + 0 // Reserved + } + } +}; + +static +KSJACK_DESCRIPTION SpeakerHsJackDesc = +{ + KSAUDIO_SPEAKER_MONO, + JACKDESC_RGB(179, 201, 140), + eConnTypeCombination, + eGeoLocRear, + eGenLocPrimaryBox, + ePortConnJack, + FALSE // NOTE: For convenience, wired headset jacks will be "unplugged" at boot. + // However, we need to introduce a test hook to toggle jack state of this and other endpoints. +}; + +//============================================================================= +// Only return a KSJACK_DESCRIPTION for the physical bridge pin. +static +PKSJACK_DESCRIPTION SpeakerHsJackDescriptions[] = +{ + NULL, + &SpeakerHsJackDesc +}; + +//============================================================================= +static +PCCONNECTION_DESCRIPTOR SpeakerHsTopoMiniportConnections[] = +{ + // FromNode, FromPin, ToNode, ToPin + { PCFILTER_NODE, KSPIN_TOPO_WAVEOUT_SOURCE, PCFILTER_NODE, KSPIN_TOPO_LINEOUT_DEST} +}; + + +//============================================================================= +static +PCPROPERTY_ITEM PropertiesSpeakerHsTopoFilter[] = +{ + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_SpeakerHsTopoFilter + }, + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION2, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_SpeakerHsTopoFilter + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationSpeakerHsTopoFilter, PropertiesSpeakerHsTopoFilter); + +//============================================================================= +static +PCFILTER_DESCRIPTOR SpeakerHsTopoMiniportFilterDescriptor = +{ + 0, // Version + &AutomationSpeakerHsTopoFilter, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(SpeakerHsTopoMiniportPins), // PinCount + SpeakerHsTopoMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + 0, // NodeCount + NULL, // Nodes + SIZEOF_ARRAY(SpeakerHsTopoMiniportConnections), // ConnectionCount + SpeakerHsTopoMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories +}; + +#endif // _SYSVAD_SPEAKERHSTOPTABLE_H_ + + diff --git a/audio/sysvad/PhoneAudioSample/speakerhswavtable.h b/audio/sysvad/PhoneAudioSample/speakerhswavtable.h new file mode 100644 index 000000000..d1103e636 --- /dev/null +++ b/audio/sysvad/PhoneAudioSample/speakerhswavtable.h @@ -0,0 +1,854 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + speakerhswavtable.h + +Abstract: + + Declaration of wave miniport tables for the speaker (external: headset). + +--*/ + +#ifndef _SYSVAD_SPEAKERHSWAVTABLE_H_ +#define _SYSVAD_SPEAKERHSWAVTABLE_H_ + + +// To keep the code simple assume device supports only 48KHz, 16-bit, stereo (PCM and NON-PCM) + +#define SPEAKERHS_DEVICE_MAX_CHANNELS 2 // Max Channels. + +#define SPEAKERHS_HOST_MAX_CHANNELS 2 // Max Channels. +#define SPEAKERHS_HOST_MIN_BITS_PER_SAMPLE 16 // Min Bits Per Sample +#define SPEAKERHS_HOST_MAX_BITS_PER_SAMPLE 16 // Max Bits Per Sample +#define SPEAKERHS_HOST_MIN_SAMPLE_RATE 24000 // Min Sample Rate +#define SPEAKERHS_HOST_MAX_SAMPLE_RATE 96000 // Max Sample Rate + +#define SPEAKERHS_OFFLOAD_MAX_CHANNELS 2 // Max Channels. +#define SPEAKERHS_OFFLOAD_MIN_BITS_PER_SAMPLE 16 // Min Bits Per Sample +#define SPEAKERHS_OFFLOAD_MAX_BITS_PER_SAMPLE 16 // Max Bits Per Sample +#define SPEAKERHS_OFFLOAD_MIN_SAMPLE_RATE 44100 // Min Sample Rate +#define SPEAKERHS_OFFLOAD_MAX_SAMPLE_RATE 48000 // Max Sample Rate + +#define SPEAKERHS_LOOPBACK_MAX_CHANNELS 2 // Max Channels. +#define SPEAKERHS_LOOPBACK_MIN_BITS_PER_SAMPLE 16 // Min Bits Per Sample +#define SPEAKERHS_LOOPBACK_MAX_BITS_PER_SAMPLE 16 // Max Bits Per Sample +#define SPEAKERHS_LOOPBACK_MIN_SAMPLE_RATE 24000 // Min Sample Rate +#define SPEAKERHS_LOOPBACK_MAX_SAMPLE_RATE 48000 // Max Sample Rate + +#define SPEAKERHS_DOLBY_DIGITAL_MAX_CHANNELS 2 // Max Channels. +#define SPEAKERHS_DOLBY_DIGITAL_MIN_BITS_PER_SAMPLE 16 // Min Bits Per Sample +#define SPEAKERHS_DOLBY_DIGITAL_MAX_BITS_PER_SAMPLE 16 // Max Bits Per Sample +#define SPEAKERHS_DOLBY_DIGITAL_MIN_SAMPLE_RATE 44100 // Min Sample Rate +#define SPEAKERHS_DOLBY_DIGITAL_MAX_SAMPLE_RATE 44100 // Max Sample Rate + +// +// Max # of pin instances. +// +#define SPEAKERHS_MAX_INPUT_SYSTEM_STREAMS 2 // Raw + Default streams +#define SPEAKERHS_MAX_INPUT_OFFLOAD_STREAMS MAX_INPUT_OFFLOAD_STREAMS +#define SPEAKERHS_MAX_OUTPUT_LOOPBACK_STREAMS MAX_OUTPUT_LOOPBACK_STREAMS + +//============================================================================= +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE SpeakerHsAudioEngineSupportedDeviceFormats[] = +{ + { // 0 : First entry in htis table is the default format for the audio engine + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 44100, + 176400, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 1 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 24000, + 96000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 2 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 48000, + 192000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 3 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 88200, + 352800, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 4 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 96000, + 384000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE SpeakerHsHostPinSupportedDeviceFormats[] = +{ + { // 0 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 24000, + 96000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 1 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 32000, + 128000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 2 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 44100, + 176400, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 3 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 48000, + 192000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 4 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 88200, + 352800, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 5 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 96000, + 384000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE SpeakerHsOffloadPinSupportedDeviceFormats[] = +{ + { // 0 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 44100, + 176400, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 1 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 48000, + 192000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE SpeakerHsLoopbackPinSupportedDeviceFormats[] = +{ + { // 0 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 24000, + 96000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 1 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 32000, + 128000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 2 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 44100, + 176400, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 3 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 48000, + 192000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +// +// Supported modes (only on streaming pins). +// +static +MODE_AND_DEFAULT_FORMAT SpeakerHsHostPinSupportedDeviceModes[] = +{ + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_RAW, + &SpeakerHsHostPinSupportedDeviceFormats[3].DataFormat // 48KHz + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_DEFAULT, + &SpeakerHsHostPinSupportedDeviceFormats[3].DataFormat // 48KHz + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_MEDIA, + &SpeakerHsHostPinSupportedDeviceFormats[3].DataFormat // 48KHz + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_MOVIE, + &SpeakerHsHostPinSupportedDeviceFormats[3].DataFormat // 48KHz + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_COMMUNICATIONS, + &SpeakerHsHostPinSupportedDeviceFormats[0].DataFormat // 24KHz + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_NOTIFICATION, + &SpeakerHsHostPinSupportedDeviceFormats[3].DataFormat // 48KHz + } +}; + +static +MODE_AND_DEFAULT_FORMAT SpeakerHsOffloadPinSupportedDeviceModes[] = +{ + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_DEFAULT, + &SpeakerHsOffloadPinSupportedDeviceFormats[1].DataFormat // 48KHz + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_MEDIA, + &SpeakerHsOffloadPinSupportedDeviceFormats[1].DataFormat // 48KHz + } +}; + +// +// The entries here must follow the same order as the filter's pin +// descriptor array. +// +static +PIN_DEVICE_FORMATS_AND_MODES SpeakerHsPinDeviceFormatsAndModes[] = +{ + { + SystemRenderPin, + SpeakerHsHostPinSupportedDeviceFormats, + SIZEOF_ARRAY(SpeakerHsHostPinSupportedDeviceFormats), + SpeakerHsHostPinSupportedDeviceModes, + SIZEOF_ARRAY(SpeakerHsHostPinSupportedDeviceModes) + }, + { + OffloadRenderPin, + SpeakerHsOffloadPinSupportedDeviceFormats, + SIZEOF_ARRAY(SpeakerHsOffloadPinSupportedDeviceFormats), + SpeakerHsOffloadPinSupportedDeviceModes, + SIZEOF_ARRAY(SpeakerHsOffloadPinSupportedDeviceModes), + }, + { + RenderLoopbackPin, + SpeakerHsLoopbackPinSupportedDeviceFormats, + SIZEOF_ARRAY(SpeakerHsLoopbackPinSupportedDeviceFormats), + NULL, // loopback doesn't support modes. + 0 + }, + { + BridgePin, + NULL, + 0, + NULL, + 0 + }, + { + NoPin, // For convenience, offload engine device formats appended here + SpeakerHsAudioEngineSupportedDeviceFormats, + SIZEOF_ARRAY(SpeakerHsAudioEngineSupportedDeviceFormats), + NULL, // no modes for this entry. + 0 + } +}; + +//============================================================================= +static +KSDATARANGE_AUDIO SpeakerHsPinDataRangesStream[] = +{ + { // 0 + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + SPEAKERHS_HOST_MAX_CHANNELS, + SPEAKERHS_HOST_MIN_BITS_PER_SAMPLE, + SPEAKERHS_HOST_MAX_BITS_PER_SAMPLE, + SPEAKERHS_HOST_MIN_SAMPLE_RATE, + SPEAKERHS_HOST_MAX_SAMPLE_RATE + }, + { // 1 + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + SPEAKERHS_OFFLOAD_MAX_CHANNELS, + SPEAKERHS_OFFLOAD_MIN_BITS_PER_SAMPLE, + SPEAKERHS_OFFLOAD_MAX_BITS_PER_SAMPLE, + SPEAKERHS_OFFLOAD_MIN_SAMPLE_RATE, + SPEAKERHS_OFFLOAD_MAX_SAMPLE_RATE + }, + { // 2 + { + sizeof(KSDATARANGE_AUDIO), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + SPEAKERHS_LOOPBACK_MAX_CHANNELS, + SPEAKERHS_LOOPBACK_MIN_BITS_PER_SAMPLE, + SPEAKERHS_LOOPBACK_MAX_BITS_PER_SAMPLE, + SPEAKERHS_LOOPBACK_MIN_SAMPLE_RATE, + SPEAKERHS_LOOPBACK_MAX_SAMPLE_RATE + } +}; + +static +PKSDATARANGE SpeakerHsPinDataRangePointersStream[] = +{ + PKSDATARANGE(&SpeakerHsPinDataRangesStream[0]), + PKSDATARANGE(&PinDataRangeAttributeList) +}; + +static +PKSDATARANGE SpeakerHsPinDataRangePointersOffloadStream[] = +{ + PKSDATARANGE(&SpeakerHsPinDataRangesStream[1]), + PKSDATARANGE(&PinDataRangeAttributeList) +}; + +static +PKSDATARANGE SpeakerHsPinDataRangePointersLoopbackStream[] = +{ + PKSDATARANGE(&SpeakerHsPinDataRangesStream[2]) +}; + +//============================================================================= +static +KSDATARANGE SpeakerHsPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +static +PKSDATARANGE SpeakerHsPinDataRangePointersBridge[] = +{ + &SpeakerHsPinDataRangesBridge[0] +}; + +//============================================================================= + +static +PCPROPERTY_ITEM PropertiesSpeakerHsOffloadPin[] = +{ + { + &KSPROPSETID_OffloadPin, // define new property set + KSPROPERTY_OFFLOAD_PIN_GET_STREAM_OBJECT_POINTER, // define properties + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_OffloadPin + }, + { + &KSPROPSETID_OffloadPin, // define new property set + KSPROPERTY_OFFLOAD_PIN_VERIFY_STREAM_OBJECT_POINTER, // define properties + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_OffloadPin + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationSpeakerHsOffloadPin, PropertiesSpeakerHsOffloadPin); + +//============================================================================= +static +PCPIN_DESCRIPTOR SpeakerHsWaveMiniportPins[] = +{ + // Wave Out Streaming Pin (renderer) KSPIN_WAVE_RENDER_SINK_SYSTEM + { + SPEAKERHS_MAX_INPUT_SYSTEM_STREAMS, + SPEAKERHS_MAX_INPUT_SYSTEM_STREAMS, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(SpeakerHsPinDataRangePointersStream), + SpeakerHsPinDataRangePointersStream, + KSPIN_DATAFLOW_IN, + KSPIN_COMMUNICATION_SINK, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, + // Wave Out Streaming Pin (Renderer) KSPIN_WAVE_RENDER_SINK_OFFLOAD + { + SPEAKERHS_MAX_INPUT_OFFLOAD_STREAMS, + SPEAKERHS_MAX_INPUT_OFFLOAD_STREAMS, + 0, + &AutomationSpeakerHsOffloadPin, // AutomationTable + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(SpeakerHsPinDataRangePointersOffloadStream), + SpeakerHsPinDataRangePointersOffloadStream, + KSPIN_DATAFLOW_IN, + KSPIN_COMMUNICATION_SINK, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, + // Wave Out Streaming Pin (Renderer) KSPIN_WAVE_RENDER_SINK_LOOPBACK + { + SPEAKERHS_MAX_OUTPUT_LOOPBACK_STREAMS, + SPEAKERHS_MAX_OUTPUT_LOOPBACK_STREAMS, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(SpeakerHsPinDataRangePointersLoopbackStream), + SpeakerHsPinDataRangePointersLoopbackStream, + KSPIN_DATAFLOW_OUT, + KSPIN_COMMUNICATION_SINK, + &KSNODETYPE_AUDIO_LOOPBACK, + NULL, + 0 + } + }, + // Wave Out Bridge Pin (Renderer) KSPIN_WAVE_RENDER_SOURCE + { + 0, + 0, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(SpeakerHsPinDataRangePointersBridge), + SpeakerHsPinDataRangePointersBridge, + KSPIN_DATAFLOW_OUT, + KSPIN_COMMUNICATION_NONE, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, +}; + +//============================================================================= +static +PCNODE_DESCRIPTOR SpeakerHsWaveMiniportNodes[] = +{ + // KSNODE_WAVE_AUDIO_ENGINE + { + 0, // Flags + NULL, // AutomationTable + &KSNODETYPE_AUDIO_ENGINE, // Type KSNODETYPE_AUDIO_ENGINE + NULL // Name + } +}; +//============================================================================= +// +// ---------------------------- +// | | +// System Pin 0-->| |--> 2 Loopback Pin +// | HW Audio Engine node | +// Offload Pin 1-->| |--> 3 KSPIN_WAVE_RENDER_SOURCE +// | | +// ---------------------------- +static +PCCONNECTION_DESCRIPTOR SpeakerHsWaveMiniportConnections[] = +{ + { PCFILTER_NODE, KSPIN_WAVE_RENDER_SINK_SYSTEM, KSNODE_WAVE_AUDIO_ENGINE, 1 }, + { PCFILTER_NODE, KSPIN_WAVE_RENDER_SINK_OFFLOAD, KSNODE_WAVE_AUDIO_ENGINE, 2 }, + { KSNODE_WAVE_AUDIO_ENGINE, 3, PCFILTER_NODE, KSPIN_WAVE_RENDER_SINK_LOOPBACK }, + { KSNODE_WAVE_AUDIO_ENGINE, 0, PCFILTER_NODE, KSPIN_WAVE_RENDER_SOURCE }, +}; + +//============================================================================= +static +PCPROPERTY_ITEM PropertiesSpeakerHsWaveFilter[] = +{ + { + &KSPROPSETID_Pin, + KSPROPERTY_PIN_PROPOSEDATAFORMAT, + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_WaveFilter + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationSpeakerHsWaveFilter, PropertiesSpeakerHsWaveFilter); + +//============================================================================= +static +PCFILTER_DESCRIPTOR SpeakerHsWaveMiniportFilterDescriptor = +{ + 0, // Version + &AutomationSpeakerHsWaveFilter, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(SpeakerHsWaveMiniportPins), // PinCount + SpeakerHsWaveMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + SIZEOF_ARRAY(SpeakerHsWaveMiniportNodes), // NodeCount + SpeakerHsWaveMiniportNodes, // Nodes + SIZEOF_ARRAY(SpeakerHsWaveMiniportConnections), // ConnectionCount + SpeakerHsWaveMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories - use defaults (audio, render, capture) +}; + +#endif // _SYSVAD_SPEAKERHSWAVTABLE_H_ + diff --git a/audio/sysvad/ReadMe.md b/audio/sysvad/ReadMe.md new file mode 100644 index 000000000..ceaa2259d --- /dev/null +++ b/audio/sysvad/ReadMe.md @@ -0,0 +1,189 @@ +Slate Virtual Audio Device Driver Sample +======================================== + +The Microsoft Slate Virtual Audio Device Driver (SYSVAD) shows how to develop a WDM audio driver that exposes support for multiple audio devices. + +Some of these audio devices are embedded in the system (for example, speakers, microphone arrays) while others are pluggable (like headphones, speakers, microphones, Bluetooth headsets etc.). The driver uses WaveRT and audio offloading for rendering devices. The driver uses a "virtual audio device" instead of an actual hardware-based adapter, and highlights the different aspects of the audio offloading WDM audio driver architecture. + +Driver developers can use the framework in this sample to provide support for various audio devices without concern for hardware dependencies. The framework includes implementations of the following interfaces: + +- The CAdapterCommon interface gives the miniports access to virtual mixer hardware. It also implements the **IAdapterPowerManagement** interface. + +- The CMiniportTopologyMSVAD interface is the base class for all sample topologies. It has very basic common functions. In addition, this class contains common topology property handlers. + +The following table shows the features that are implemented in the various subdirectories of this sample. + + +For more information about the Windows audio engine, see [Exposing Hardware-Offloaded Audio Processing in Windows](http://msdn.microsoft.com/en-us/windows/hardware/br259116), and note that audio hardware that is offload-capable replicates the architecture that is presented in the diagram shown in the topic. + + +Build the sample +---------------- + +If you simply want to Build this sample driver and don't intend to run or test it, then you do not need a target computer (also called a test computer). If, however, you would like to deploy, run and test this sample driver, then you need a second computer that will server as your target computer. Instructions are provided in the **Run the sample** section to show you how to set up the target computer - also referred to as *provisioning* a target computer. + +Perform the following steps to build this sample driver. + +**1. Donwload and extract the sample** + +Click the download button on this page. Click **Save**, and then click **Open Folder**. Right click *Microsoft slate system virtual audio device driver sample.zip*, and choose **Extract All**. Specify a new folder, or browse to an existing one that will store the extracted files. For example, you could specify *c:\\SlateAudioSample* as the new folder into which the files will be extracted. + +**2. Open the driver solution in Visual Studio** + +In Microsoft Visual Studio, Click **File** \> **Open** \> **Project/Solution...** and navigate to the folder that contains the extracted files (for example, *c:\\SlateAudioSample*). Double-click the *sysvad* solution file. + +In Visual Studio locate the Solution Explorer. (If this is not already open, choose **Solution Explorer** from the **View** menu.) In Solution Explorer, you can see one solution that has four (4) projects. Note that the project titled SwapAPO is actually a folder that contains two projects - APO and PropPageExtensions. + +**3. Set the sample's configuration and platform** + +In Solution Explorer, right-click **Solution 'sysvad' (4 projects)**, and choose **Configuration Manager**. Make sure that the configuration and platform settings are the same for the four projects. By default, the configuration is set to "Win8.1 Debug", and the platform is set to "Win32" for all the projects. If you make any configuration and/or platform changes for one project, you must make the same changes for the remaining three projects. + +Here are the explanations for some of the configuration and platform options. + + +++++ + + + + + + + + + + + +
Configuration +Platform +Description
Win8.1 Debug +x64 +The driver will run on an x64 hardware platform that is running Windows 8.1. The driver will not run on any earlier versions of Windows.Win7 Debug +x64 +The driver will run on an x64 hardware platform that is running Windows 7 or a later version of Windows. The driver will not run on any earlier versions of Windows.
+ +**4. Build the sample using Visual Studio** + +In Visual Studio, click **Build** \> **Build Solution**. + +**5. Locate the built driver package** + +In File Explorer, navigate to the folder that contains the extracted files for the sample. For example, you would navigate to *c:\\SlateAudioSample*, if that's the folder you specified in the preceding Step 1. + +In the folder, the location of the driver package varies depending on the configuration and platform settings that you selected in the **Configuration Manager**. For example, if you left the default settings unchanged, then the built driver package will be saved to a folder named *Win8.1Debug* inside the same folder as the extracted files. Double-click the folder for the built driver package, and then double-click the folder named *package*. + +The package should contain these files: + + ++++ + + + + + + + + + + + +
File +Description
PropPageExt.dll +A sample driver extension for a property page.SlateAudioSample.sys +The driver file.
+ +Run the sample +-------------- + +The computer where you install the driver is called the *target computer* or the *test computer*. Typically this is a separate computer from the computer on which you develop and build the driver package. The computer where you develop and build the driver is called the *host computer*. + +The process of moving the driver package to the target computer and installing the driver is called *deploying* the driver. You can deploy the sample driver, SlateAudioSample, automatically or manually. + +**Automatic deployment** + +Before you automatically deploy a driver, you must provision the target computer. Verify that the target computer has an ethernet cable connecting it to your local network, and that your host and target computers can ping each other. Then perform the following steps to prepare your host and target computers. + +**1. Provision the target computer** + +On the target computer install the latest [Windows Driver Kit](http://msdn.microsoft.com/en-us/windows/hardware/gg454513.aspx) (WDK), and then when the installation is completed, navigate to the following folder: + +\\Program Files (x86)\\Windows Kits\\8.1\\Remote\\\\\ + +For example, if your target computer is an x64 machine, you would navigate to: + +\\Program Files (x86)\\Windows Kits\\8.1\\Remote\\x64\\ + +Double-click the "*WDK Test Target Setup x64-x64\_en-us.msi*" file to run it. This program prepares the target computer for provisioning. + +On the host computer, in Visual Studio click **Driver** \> **Test** \> **Configure Computers...**, and then click **Add a new computer**. + +Type the name of the target computer, select **Provision computer and choose debugger settings**, and click **Next**. In the next window, verify that the **Connection Type** is set to Network. Leave the other (default) settings as they are, and click **Next**. For more information about the settings in this window, see [Getting Set Up for Debugging](http://msdn.microsoft.com/en-us/library/windows/hardware/hh450944(v=vs.85).aspx). + +**2. Prepare the host computer** + +If you haven't already done so, then preform the steps in the **Build the sample** section, to build the sample driver. + +In Visual Studio, in Solution Explorer, right click **package** (lower case), and choose **Properties**. Navigate to **Configuration Properties** \> **Driver Install** \> **Deployment**. + +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 *\*SYSVAD\_SLATEAUDIO* for the hardware ID. Click **OK**. + +On the **Build** menu, choose **Deploy Package** or **Build Solution**. This will deploy the sample driver to your target computer. + +On the target computer, perform the steps in the **Test the sample** section to test the sample driver. + +**Manual deployment** + +Before you manually deploy a driver, you must prepare the target computer by turning on test signing and by installing a certificate. You also need to locate the DevCon tool in your WDK installation. After that you’re ready to run the built driver sample. + +**1. Prepare the target computer** + +Open a Command Prompt window as Administrator. Then enter the following command: + +**bcdedit /set TESTSIGNING ON** + +Reboot the target computer. Then navigate to the Tools folder in your WDK installation and locate the DevCon tool. For example, look in the following folder: + +*C:\\Program Files (x86)\\Windows Kits\\8.1\\Tools\\x64\\devcon.exe* + +Copy *devcon.exe* to a folder on the target computer where it is easier to find. For example, create a *C:\\Tools* folder and copy *devcon.exe* to that folder. + +Create a folder on the target for the built driver package (for example, *C:\\SysvadDriver*). Copy all the files from the built driver package on the host computer and save them to the folder that you created on the target computer. + +Create a folder on the target computer for the certificate created by the build process. For example, you could create a folder named *C:\\Certificates* on the target computer, and then copy *package.cer* to it from the host computer. You can find this certificate in the same folder on the host computer, as the *package* folder that contains the built driver files. On the target computer, right-click the certificate file, and click **Install**, then follow the prompts to install the test certificate. + +If you need more detailed instructions for setting up the target computer, see [Preparing a Computer for Manual Driver Deployment](http://msdn.microsoft.com/en-us/library/windows/hardware/dn265571(v=vs.85).aspx). + +**2. Install the driver** + +The SlateAudioSample driver package contains a sample driver and 2 driver extension samples. The following instructions show you how to install and test the sample driver. Here's the general syntax for the devcon tool that you will use to install the driver: + +*devcon install \ \* + +The INF file required for installing this driver is *sysvad.inf*. Here's how to find the hardware ID for installing the *SlateAudioSample.sys* sample: On the target computer, navigate to the folder that contains the files for your driver (for example, *C:\\SysvadDriver*). Then right-click the INF file (*sysvad.inf*) and open it with Notepad. Use Ctrl+F to find the [MicrosoftDS] section. Note that there is a comma-separated element at the end of the row. The element after the comma shows the hardware ID. So for this sample, the hardware ID is \*SYSVAD\_SLATEAUDIO. + +On the target computer, open a Command Prompt window as Administrator. Navigate to your driver package folder, and enter the following command: + +**devcon install sysvad.inf \*SYSVAD\_SLATEAUDIO** + +If you get an error message about *devcon* not being recognized, try adding the path to the *devcon* tool. For example, if you copied it to a folder called *C:\\Tools*, then try using the following command: + +**c:\\tools\\devcon install sysvad.inf \*SYSVAD\_SLATEAUDIO** + +For more detailed instructions, see [Configuring a Computer for Driver Deployment, Testing, and Debugging](http://msdn.microsoft.com/en-us/library/windows/hardware/hh698272(v=vs.85).aspx). + +After successfully installing the sample driver, you're now ready to test it. + +**Test the driver** + +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 *Microsoft Virtual Audio Device (WDM) - Slate Sample*. This is typically under the **Sound, video and game controllers** node. + +On the target computer, open Control Panel and navigate to **Hardware and Sound** \> **Manage audio devices**. In the Sound dialog box, select the speaker icon labeled as *Microsoft Virtual Audio Device (WDM) - Slate Sample*, then click **Set Default**, but do not click **OK**. This will keep the Sound dialog box open. + +Locate an MP3 or other audio file on the target computer and double-click to play it. Then in the Sound dialog box, verify that there is activity in the volume level indicator associated with the *Microsoft Virtual Audio Device (WDM) - Slate Sample* driver. + diff --git a/audio/sysvad/SwapAPO/APO/Resource.h b/audio/sysvad/SwapAPO/APO/Resource.h new file mode 100644 index 000000000..0e7dff6d2 --- /dev/null +++ b/audio/sysvad/SwapAPO/APO/Resource.h @@ -0,0 +1,20 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by SwapAPODll.rc +// +#define IDS_PROJNAME 100 +#define IDR_SWAPAPODLL 101 +#define IDR_SWAPAPOMFX 110 +#define IDR_SWAPAPOSFX 111 +#define IDI_EFFECT_ICON 200 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 201 +#define _APS_NEXT_COMMAND_VALUE 32768 +#define _APS_NEXT_CONTROL_VALUE 201 +#define _APS_NEXT_SYMED_VALUE 132 +#endif +#endif diff --git a/audio/sysvad/SwapAPO/APO/SwapAPO.h b/audio/sysvad/SwapAPO/APO/SwapAPO.h new file mode 100644 index 000000000..2f687c04c --- /dev/null +++ b/audio/sysvad/SwapAPO/APO/SwapAPO.h @@ -0,0 +1,319 @@ +// +// SwapAPO.h -- Copyright (c) Microsoft Corporation. All rights reserved. +// +// Description: +// +// Declaration of the CSwapAPO class. +// + +#pragma once + +#include +#include +#include +#include + +#include +#include + +_Analysis_mode_(_Analysis_code_type_user_driver_) + +#define PK_EQUAL(x, y) ((x.fmtid == y.fmtid) && (x.pid == y.pid)) + +// +// Define a GUID identifying the type of this APO's custom effect. +// +// APOs generally should not define new GUIDs for types of effects and instead +// should use predefined effect types. Only define a new GUID if the effect is +// truly very different from all predefined types of effects. +// +// {B8EC75BA-00ED-434C-A732-064A0F00788E} +DEFINE_GUID(SwapEffectId, 0xb8ec75ba, 0x00ed, 0x434c, 0xa7, 0x32, 0x06, 0x4a, 0x0f, 0x00, 0x78, 0x8e); +// {29FBFBB5-9002-4ABC-DCBC-DD45462478C8} +DEFINE_GUID(DelayEffectId, 0x29FBFBB5, 0x9002, 0x4ABC, 0xDC, 0xBC, 0xDD, 0x45, 0x46, 0x24, 0x78, 0xC8); + +// 1000 ms of delay +#define HNS_DELAY HNS_PER_SECOND + +#define FRAMES_FROM_HNS(hns) (ULONG)(1.0 * hns / HNS_PER_SECOND * GetFramesPerSecond() + 0.5) + +LONG GetCurrentEffectsSetting(IPropertyStore* properties, PROPERTYKEY pkeyEnable, GUID processingMode); + +#pragma AVRT_VTABLES_BEGIN +// Swap APO class - MFX +class CSwapAPOMFX : + public CComObjectRootEx, + public CComCoClass, + public CBaseAudioProcessingObject, + public IMMNotificationClient, + public IAudioSystemEffects2, + // IAudioSystemEffectsCustomFormats may be optionally supported + // by APOs that attach directly to the connector in the DEFAULT mode streaming graph + public IAudioSystemEffectsCustomFormats, + public ISwapAPOMFX +{ +public: + // constructor + CSwapAPOMFX() + : CBaseAudioProcessingObject(sm_RegProperties) + , m_hEffectsChangedEvent(NULL) + , m_AudioProcessingMode(AUDIO_SIGNALPROCESSINGMODE_DEFAULT) + , m_fEnableSwapMFX(FALSE) + , m_fEnableDelayMFX(FALSE) + , m_nDelayFrames(0) + , m_iDelayIndex(0) + { + m_pf32Coefficients = NULL; + } + + virtual ~CSwapAPOMFX(); // destructor + +DECLARE_REGISTRY_RESOURCEID(IDR_SWAPAPOMFX) + +BEGIN_COM_MAP(CSwapAPOMFX) + COM_INTERFACE_ENTRY(ISwapAPOMFX) + COM_INTERFACE_ENTRY(IAudioSystemEffects) + COM_INTERFACE_ENTRY(IAudioSystemEffects2) + // IAudioSystemEffectsCustomFormats may be optionally supported + // by APOs that attach directly to the connector in the DEFAULT mode streaming graph + COM_INTERFACE_ENTRY(IAudioSystemEffectsCustomFormats) + COM_INTERFACE_ENTRY(IMMNotificationClient) + COM_INTERFACE_ENTRY(IAudioProcessingObjectRT) + COM_INTERFACE_ENTRY(IAudioProcessingObject) + COM_INTERFACE_ENTRY(IAudioProcessingObjectConfiguration) +END_COM_MAP() + +DECLARE_PROTECT_FINAL_CONSTRUCT() + +public: + STDMETHOD_(void, APOProcess)(UINT32 u32NumInputConnections, + APO_CONNECTION_PROPERTY** ppInputConnections, UINT32 u32NumOutputConnections, + APO_CONNECTION_PROPERTY** ppOutputConnections); + + STDMETHOD(GetLatency)(HNSTIME* pTime); + + STDMETHOD(LockForProcess)(UINT32 u32NumInputConnections, + APO_CONNECTION_DESCRIPTOR** ppInputConnections, + UINT32 u32NumOutputConnections, APO_CONNECTION_DESCRIPTOR** ppOutputConnections); + + STDMETHOD(Initialize)(UINT32 cbDataSize, BYTE* pbyData); + + // IAudioSystemEffects2 + STDMETHOD(GetEffectsList)(_Outptr_result_buffer_maybenull_(*pcEffects) LPGUID *ppEffectsIds, _Out_ UINT *pcEffects, _In_ HANDLE Event); + + virtual HRESULT ValidateAndCacheConnectionInfo( + UINT32 u32NumInputConnections, + APO_CONNECTION_DESCRIPTOR** ppInputConnections, + UINT32 u32NumOutputConnections, + APO_CONNECTION_DESCRIPTOR** ppOutputConnections); + + // IMMNotificationClient + STDMETHODIMP OnDeviceStateChanged(LPCWSTR pwstrDeviceId, DWORD dwNewState) + { + UNREFERENCED_PARAMETER(pwstrDeviceId); + UNREFERENCED_PARAMETER(dwNewState); + return S_OK; + } + STDMETHODIMP OnDeviceAdded(LPCWSTR pwstrDeviceId) + { + UNREFERENCED_PARAMETER(pwstrDeviceId); + return S_OK; + } + STDMETHODIMP OnDeviceRemoved(LPCWSTR pwstrDeviceId) + { + UNREFERENCED_PARAMETER(pwstrDeviceId); + return S_OK; + } + STDMETHODIMP OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR pwstrDefaultDeviceId) + { + UNREFERENCED_PARAMETER(flow); + UNREFERENCED_PARAMETER(role); + UNREFERENCED_PARAMETER(pwstrDefaultDeviceId); + return S_OK; + } + STDMETHODIMP OnPropertyValueChanged(LPCWSTR pwstrDeviceId, const PROPERTYKEY key); + + // IAudioSystemEffectsCustomFormats + // This interface may be optionally supported by APOs that attach directly to the connector in the DEFAULT mode streaming graph + STDMETHODIMP GetFormatCount(UINT* pcFormats); + STDMETHODIMP GetFormat(UINT nFormat, IAudioMediaType** ppFormat); + STDMETHODIMP GetFormatRepresentation(UINT nFormat, _Outptr_ LPWSTR* ppwstrFormatRep); + +public: + LONG m_fEnableSwapMFX; + LONG m_fEnableDelayMFX; + GUID m_AudioProcessingMode; + CComPtr m_spAPOSystemEffectsProperties; + CComPtr m_spEnumerator; + static const CRegAPOProperties<1> sm_RegProperties; // registration properties + + // Locked memory + FLOAT32 *m_pf32Coefficients; + + CComHeapPtr m_pf32DelayBuffer; + UINT32 m_nDelayFrames; + UINT32 m_iDelayIndex; + +private: + CCriticalSection m_EffectsLock; + HANDLE m_hEffectsChangedEvent; + + HRESULT ProprietaryCommunicationWithDriver(APOInitSystemEffects2 *_pAPOSysFxInit2); + +}; +#pragma AVRT_VTABLES_END + + +#pragma AVRT_VTABLES_BEGIN +// Swap APO class - SFX +class CSwapAPOSFX : + public CComObjectRootEx, + public CComCoClass, + public CBaseAudioProcessingObject, + public IMMNotificationClient, + public IAudioSystemEffects2, + public ISwapAPOSFX +{ +public: + // constructor + CSwapAPOSFX() + : CBaseAudioProcessingObject(sm_RegProperties) + , m_hEffectsChangedEvent(NULL) + , m_AudioProcessingMode(AUDIO_SIGNALPROCESSINGMODE_DEFAULT) + , m_fEnableSwapSFX(FALSE) + , m_fEnableDelaySFX(FALSE) + , m_nDelayFrames(0) + , m_iDelayIndex(0) + { + } + + virtual ~CSwapAPOSFX(); // destructor + +DECLARE_REGISTRY_RESOURCEID(IDR_SWAPAPOSFX) + +BEGIN_COM_MAP(CSwapAPOSFX) + COM_INTERFACE_ENTRY(ISwapAPOSFX) + COM_INTERFACE_ENTRY(IAudioSystemEffects) + COM_INTERFACE_ENTRY(IAudioSystemEffects2) + COM_INTERFACE_ENTRY(IMMNotificationClient) + COM_INTERFACE_ENTRY(IAudioProcessingObjectRT) + COM_INTERFACE_ENTRY(IAudioProcessingObject) + COM_INTERFACE_ENTRY(IAudioProcessingObjectConfiguration) +END_COM_MAP() + +DECLARE_PROTECT_FINAL_CONSTRUCT() + +public: + STDMETHOD_(void, APOProcess)(UINT32 u32NumInputConnections, + APO_CONNECTION_PROPERTY** ppInputConnections, UINT32 u32NumOutputConnections, + APO_CONNECTION_PROPERTY** ppOutputConnections); + + STDMETHOD(GetLatency)(HNSTIME* pTime); + + STDMETHOD(LockForProcess)(UINT32 u32NumInputConnections, + APO_CONNECTION_DESCRIPTOR** ppInputConnections, + UINT32 u32NumOutputConnections, APO_CONNECTION_DESCRIPTOR** ppOutputConnections); + + STDMETHOD(Initialize)(UINT32 cbDataSize, BYTE* pbyData); + + // IAudioSystemEffects2 + STDMETHOD(GetEffectsList)(_Outptr_result_buffer_maybenull_(*pcEffects) LPGUID *ppEffectsIds, _Out_ UINT *pcEffects, _In_ HANDLE Event); + + // IMMNotificationClient + STDMETHODIMP OnDeviceStateChanged(LPCWSTR pwstrDeviceId, DWORD dwNewState) + { + UNREFERENCED_PARAMETER(pwstrDeviceId); + UNREFERENCED_PARAMETER(dwNewState); + return S_OK; + } + STDMETHODIMP OnDeviceAdded(LPCWSTR pwstrDeviceId) + { + UNREFERENCED_PARAMETER(pwstrDeviceId); + return S_OK; + } + STDMETHODIMP OnDeviceRemoved(LPCWSTR pwstrDeviceId) + { + UNREFERENCED_PARAMETER(pwstrDeviceId); + return S_OK; + } + STDMETHODIMP OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR pwstrDefaultDeviceId) + { + UNREFERENCED_PARAMETER(flow); + UNREFERENCED_PARAMETER(role); + UNREFERENCED_PARAMETER(pwstrDefaultDeviceId); + return S_OK; + } + STDMETHODIMP OnPropertyValueChanged(LPCWSTR pwstrDeviceId, const PROPERTYKEY key); + +public: + LONG m_fEnableSwapSFX; + LONG m_fEnableDelaySFX; + GUID m_AudioProcessingMode; + CComPtr m_spAPOSystemEffectsProperties; + CComPtr m_spEnumerator; + static const CRegAPOProperties<1> sm_RegProperties; // registration properties + + CCriticalSection m_EffectsLock; + HANDLE m_hEffectsChangedEvent; + + CComHeapPtr m_pf32DelayBuffer; + UINT32 m_nDelayFrames; + UINT32 m_iDelayIndex; +}; +#pragma AVRT_VTABLES_END + +OBJECT_ENTRY_AUTO(__uuidof(SwapAPOMFX), CSwapAPOMFX) +OBJECT_ENTRY_AUTO(__uuidof(SwapAPOSFX), CSwapAPOSFX) + +// +// Declaration of the ProcessSwap routine. +// +void ProcessSwap( + FLOAT32 *pf32OutputFrames, + const FLOAT32 *pf32InputFrames, + UINT32 u32ValidFrameCount, + UINT32 u32SamplesPerFrame); + +// +// Declaration of the ProcessSwapScale routine. +// +void ProcessSwapScale( + FLOAT32 *pf32OutputFrames, + const FLOAT32 *pf32InputFrames, + UINT32 u32ValidFrameCount, + UINT32 u32SamplesPerFrame, + FLOAT32 *pf32Coefficients ); + +// +// Declaration of the ProcessDelay routine. +// +void ProcessDelay( + _Out_writes_(u32ValidFrameCount * u32SamplesPerFrame) + FLOAT32 *pf32OutputFrames, + _In_reads_(u32ValidFrameCount * u32SamplesPerFrame) + const FLOAT32 *pf32InputFrames, + UINT32 u32ValidFrameCount, + UINT32 u32SamplesPerFrame, + _Inout_updates_(u32DelayFrames * u32SamplesPerFrame) + FLOAT32 *pf32DelayBuffer, + UINT32 u32DelayFrames, + _Inout_ + UINT32 *pu32DelayIndex ); + +// +// Convenience methods +// + +void WriteSilence( + _Out_writes_(u32FrameCount * u32SamplesPerFrame) + FLOAT32 *pf32Frames, + UINT32 u32FrameCount, + UINT32 u32SamplesPerFrame ); + +void CopyFrames( + _Out_writes_(u32FrameCount * u32SamplesPerFrame) + FLOAT32 *pf32OutFrames, + _In_reads_(u32FrameCount * u32SamplesPerFrame) + const FLOAT32 *pf32InFrames, + UINT32 u32FrameCount, + UINT32 u32SamplesPerFrame ); diff --git a/audio/sysvad/SwapAPO/APO/SwapAPO.vcxproj b/audio/sysvad/SwapAPO/APO/SwapAPO.vcxproj new file mode 100644 index 000000000..9f79895e4 --- /dev/null +++ b/audio/sysvad/SwapAPO/APO/SwapAPO.vcxproj @@ -0,0 +1,242 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {2A1B7375-D9CB-4067-AEDE-180D75B5934D} + $(MSBuildProjectName) + false + true + Debug + Win32 + {0ADE9CDB-E126-4F11-8496-335EA4A1B368} + + + + Windows10 + False + Desktop + false + + WindowsApplicationForDrivers10.0 + DynamicLibrary + + + Windows10 + True + Desktop + false + + WindowsApplicationForDrivers10.0 + DynamicLibrary + + + Windows10 + False + Desktop + false + + WindowsApplicationForDrivers10.0 + DynamicLibrary + + + Windows10 + True + Desktop + false + + WindowsApplicationForDrivers10.0 + DynamicLibrary + + + + $(IntDir) + + + + + + + + + + + + + + + + SwapAPO + + + SwapAPO + + + SwapAPO + + + SwapAPO + + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + Dynamic + + + Dynamic + + + Dynamic + + + Dynamic + + + + true + Level4 + %(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\ + + + + + %(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\ + + + %(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\ + + + %(AdditionalDependencies);Kernel32.lib;ole32.lib;oleaut32.lib;advapi32.lib;user32.lib;uuid.lib;AudioBaseProcessingObjectV140.lib;audiomediatypecrt.lib;AudioEng.lib + SwapAPODll.def + + + + + true + Level4 + %(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\ + + + + + %(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\ + + + %(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\ + + + %(AdditionalDependencies);Kernel32.lib;ole32.lib;oleaut32.lib;advapi32.lib;user32.lib;uuid.lib;AudioBaseProcessingObjectV140.lib;audiomediatypecrt.lib;AudioEng.lib + SwapAPODll.def + + + + + true + Level4 + %(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\ + + + + + %(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\ + + + %(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\ + + + %(AdditionalDependencies);Kernel32.lib;ole32.lib;oleaut32.lib;advapi32.lib;user32.lib;uuid.lib;AudioBaseProcessingObjectV140.lib;audiomediatypecrt.lib;AudioEng.lib + SwapAPODll.def + + + + + true + Level4 + %(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\ + + + + + %(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\ + + + %(PreprocessorDefinitions);_WINDLL;_USRDLL;UNICODE;_UNICODE + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\ + + + %(AdditionalDependencies);Kernel32.lib;ole32.lib;oleaut32.lib;advapi32.lib;user32.lib;uuid.lib;AudioBaseProcessingObjectV140.lib;audiomediatypecrt.lib;AudioEng.lib + SwapAPODll.def + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/audio/sysvad/SwapAPO/APO/SwapAPO.vcxproj.Filters b/audio/sysvad/SwapAPO/APO/SwapAPO.vcxproj.Filters new file mode 100644 index 000000000..10ad06cd9 --- /dev/null +++ b/audio/sysvad/SwapAPO/APO/SwapAPO.vcxproj.Filters @@ -0,0 +1,45 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {3D2AE2AB-4A59-4C45-8B37-DF9973DE29F4} + + + h;hpp;hxx;hm;inl;inc;xsd + {D2776F29-9EAD-4F9A-B1DB-827FEBAE9166} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {AC7F517D-37C5-4622-9D43-DFFD0B196012} + + + + + 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/audio/sysvad/SwapAPO/APO/SwapAPODll.cpp b/audio/sysvad/SwapAPO/APO/SwapAPODll.cpp new file mode 100644 index 000000000..4792b3446 --- /dev/null +++ b/audio/sysvad/SwapAPO/APO/SwapAPODll.cpp @@ -0,0 +1,148 @@ +// +// SwapAPODll.cpp -- Copyright (c) Microsoft Corporation. All rights reserved. +// +// Author: +// +// Description: +// +// SwapAPODll.cpp : Implementation of DLL Exports. + +#include +#include +#include +#include +#include + +#include "resource.h" +#include "SwapAPODll.h" +#include + +#include + +//------------------------------------------------------------------------- +// Array of APO_REG_PROPERTIES structures implemented in this module. +// Each new APO implementation will be added to this array. +// +APO_REG_PROPERTIES const *gCoreAPOs[] = +{ + &CSwapAPOMFX::sm_RegProperties.m_Properties, + &CSwapAPOSFX::sm_RegProperties.m_Properties +}; + +// {secret} +class CSwapAPODllModule : public CAtlDllModuleT< CSwapAPODllModule > +{ +public : + DECLARE_LIBID(LIBID_SwapAPODlllib) + DECLARE_REGISTRY_APPID_RESOURCEID(IDR_SWAPAPODLL, "{0A21D954-674A-4C09-806E-DB4FBE8F199C}") +public: + HRESULT DllRegisterServer(BOOL bRegTypeLib = TRUE) throw(); + HRESULT DllUnregisterServer(BOOL bUnRegTypeLib = TRUE) throw(); +}; + +// {secret} +CSwapAPODllModule _AtlModule; + + +HRESULT CSwapAPODllModule::DllRegisterServer(BOOL bRegTypeLib) throw() +{ + HRESULT hResult; + UINT32 u32APORegIndex = 0; + UINT32 u32APOUnregIndex = 0; + + // Register all APOs implemented in this module. + for (u32APORegIndex = 0; u32APORegIndex < SIZEOF_ARRAY(gCoreAPOs); u32APORegIndex++) + { + hResult = RegisterAPO(gCoreAPOs[u32APORegIndex]); + if (FAILED(hResult)) + { + goto ExitFailed; + } + } + + // Register the module. + hResult = CAtlDllModuleT::DllRegisterServer(bRegTypeLib); + if (FAILED(hResult)) + { + goto ExitFailed; + } + + return hResult; + +ExitFailed: + // Unregister all registered APOs if something failed. + for (u32APOUnregIndex = 0; u32APOUnregIndex < u32APORegIndex; u32APOUnregIndex++) + { + ATLVERIFY(SUCCEEDED(UnregisterAPO(gCoreAPOs[u32APOUnregIndex]->clsid))); + } + + return hResult; +} // DllRegisterServer + +HRESULT CSwapAPODllModule::DllUnregisterServer(BOOL bUnRegTypeLib) throw() +{ + HRESULT hResult; + UINT32 u32APOUnregIndex = 0; + + // Unregister the module. + hResult = CAtlDllModuleT::UnregisterServer(bUnRegTypeLib); + if (FAILED(hResult)) + { + goto Exit; + } + + // Unregister all the APOs that are implemented in this module. + for (u32APOUnregIndex = 0; u32APOUnregIndex < SIZEOF_ARRAY(gCoreAPOs); u32APOUnregIndex++) + { + hResult = UnregisterAPO(gCoreAPOs[u32APOUnregIndex]->clsid); + ATLASSERT(SUCCEEDED(hResult)); + } + +Exit: + return hResult; +} // DllUnregisterServer + +// {secret} +extern "C" BOOL WINAPI DllMain(HINSTANCE /* hInstance */, DWORD dwReason, LPVOID lpReserved) +{ + if (DLL_PROCESS_ATTACH == dwReason) + { + } + // do necessary cleanup only if the DLL is being unloaded dynamically + else if ((DLL_PROCESS_DETACH == dwReason) && (NULL == lpReserved)) + { + } + + return _AtlModule.DllMain(dwReason, lpReserved); +} + + +// {secret} +STDAPI DllCanUnloadNow(void) +{ + return _AtlModule.DllCanUnloadNow(); +} + + +// {secret} +STDAPI DllGetClassObject(_In_ REFCLSID rclsid,_In_ REFIID riid, _Outptr_ LPVOID* ppv) +{ + return _AtlModule.DllGetClassObject(rclsid, riid, ppv); +} + + +// {secret} +STDAPI DllRegisterServer(void) +{ + // registers object, typelib and all interfaces in typelib + HRESULT hr = _AtlModule.DllRegisterServer(); + return hr; +} + + +// {secret} +STDAPI DllUnregisterServer(void) +{ + HRESULT hr = _AtlModule.DllUnregisterServer(); + return hr; +} diff --git a/audio/sysvad/SwapAPO/APO/SwapAPODll.def b/audio/sysvad/SwapAPO/APO/SwapAPODll.def new file mode 100644 index 000000000..722ab4c4c --- /dev/null +++ b/audio/sysvad/SwapAPO/APO/SwapAPODll.def @@ -0,0 +1,6 @@ + +EXPORTS + DllCanUnloadNow PRIVATE + DllGetClassObject PRIVATE + DllRegisterServer PRIVATE + DllUnregisterServer PRIVATE diff --git a/audio/sysvad/SwapAPO/APO/SwapAPODll.idl b/audio/sysvad/SwapAPO/APO/SwapAPODll.idl new file mode 100644 index 000000000..e9000c7ca --- /dev/null +++ b/audio/sysvad/SwapAPO/APO/SwapAPODll.idl @@ -0,0 +1,54 @@ +// +// SwapAPODll.idl -- Copyright (c) Microsoft Corporation. All rights reserved. +// +// Author: +// +// Description: +// +// SwapAPODll.idl : Definition of COM interfaces and coclasses for the DLL. + +import "oaidl.idl"; +import "ocidl.idl"; +import "SwapAPOInterface.idl"; + +//------------------------------------------------------------------------- +// SwapAPODlllib +// +[ + uuid(7092F0B2-D28D-4095-95A7-6C37A97432A2), + version(1.0) +] +library SwapAPODlllib +{ + importlib("stdole2.tlb"); + + // for Swap APO - MFX + [ + uuid(06687E71-F043-403A-BF49-CB591BA6E103) + ] + coclass SwapAPOMFX + { + interface IAudioProcessingObject; + interface IAudioProcessingObjectRT; + interface IAudioProcessingObjectConfiguration; + interface IMMNotificationClient; + interface IAudioSystemEffects; + [default] interface ISwapAPOMFX; + }; + + // for Swap APO - SFX + [ + uuid(B48DEA3F-D962-425a-8D9A-9A5BB37A9904) + ] + coclass SwapAPOSFX + { + interface IAudioProcessingObject; + interface IAudioProcessingObjectRT; + interface IAudioProcessingObjectConfiguration; + interface IMMNotificationClient; + interface IAudioSystemEffects; + [default] interface ISwapAPOSFX; + }; + + +} diff --git a/audio/sysvad/SwapAPO/APO/SwapAPODll.rc b/audio/sysvad/SwapAPO/APO/SwapAPODll.rc new file mode 100644 index 000000000..119a3ebc6 --- /dev/null +++ b/audio/sysvad/SwapAPO/APO/SwapAPODll.rc @@ -0,0 +1,24 @@ +#if 0 +Copyright (c) Microsoft Corporation. All Rights Reserved +#endif + +#include "resource.h" +#include "winres.h" +#include +#define VER_FILETYPE VFT_DLL +#define VER_FILESUBTYPE VFT_UNKNOWN +#define VER_FILEDESCRIPTION_STR "Swap APO" +#define VER_INTERNALNAME_STR "SWAPAPODll" +#define VER_ORIGINALFILENAME_STR "SwapAPODll.Dll" +#include + +IDR_SWAPAPODLL REGISTRY "SwapAPODll.rgs" +IDR_SWAPAPOMFX REGISTRY "SwapAPOMFX.rgs" +IDR_SWAPAPOSFX REGISTRY "SwapAPOSFX.rgs" + +// ICON +IDI_EFFECT_ICON RCDATA "SwapApo.png" + +#if 0 +1 TYPELIB "SwapAPODll.tlb" +#endif diff --git a/audio/sysvad/SwapAPO/APO/SwapAPODll.rgs b/audio/sysvad/SwapAPO/APO/SwapAPODll.rgs new file mode 100644 index 000000000..ab3277b3e --- /dev/null +++ b/audio/sysvad/SwapAPO/APO/SwapAPODll.rgs @@ -0,0 +1,11 @@ +HKCR +{ + NoRemove AppID + { + '%APPID%' = s 'SwapAPODll' + 'SwapAPODll.DLL' + { + val AppID = s '%APPID%' + } + } +} diff --git a/audio/sysvad/SwapAPO/APO/SwapAPOInterface.idl b/audio/sysvad/SwapAPO/APO/SwapAPOInterface.idl new file mode 100644 index 000000000..e83f16fbc --- /dev/null +++ b/audio/sysvad/SwapAPO/APO/SwapAPOInterface.idl @@ -0,0 +1,31 @@ +// +// SwapAPO.idl -- Copyright (c) Microsoft Corporation. All rights reserved. +// +// Description: +// +// The interface and type definitions for Swap APO functionality. +// +import "oaidl.idl"; +import "ocidl.idl"; +import "audioenginebaseapo.idl"; + +// This is the SwapAPO interface. +[ + object, + uuid(3865B91A-096E-4ACA-BF56-B17D49C77406), + pointer_default(unique) +] +interface ISwapAPOMFX : IUnknown +{ +}; + +// This is the SwapAPO interface. +[ + object, + uuid(658A4077-B277-4d14-97E1-0356044D8110), + pointer_default(unique) +] +interface ISwapAPOSFX : IUnknown +{ +}; + diff --git a/audio/sysvad/SwapAPO/APO/SwapAPOMFX.rgs b/audio/sysvad/SwapAPO/APO/SwapAPOMFX.rgs new file mode 100644 index 000000000..3bebda2e5 --- /dev/null +++ b/audio/sysvad/SwapAPO/APO/SwapAPOMFX.rgs @@ -0,0 +1,13 @@ +HKCR +{ + NoRemove CLSID + { + ForceRemove {06687E71-F043-403A-BF49-CB591BA6E103} = s 'SwapAPOMFX Class' + { + InprocServer32 = s '%MODULE%' + { + val ThreadingModel = s 'Both' + } + } + } +} diff --git a/audio/sysvad/SwapAPO/APO/SwapAPOSFX.rgs b/audio/sysvad/SwapAPO/APO/SwapAPOSFX.rgs new file mode 100644 index 000000000..c553edd53 --- /dev/null +++ b/audio/sysvad/SwapAPO/APO/SwapAPOSFX.rgs @@ -0,0 +1,13 @@ +HKCR +{ + NoRemove CLSID + { + ForceRemove {B48DEA3F-D962-425a-8D9A-9A5BB37A9904} = s 'SwapAPOSFX Class' + { + InprocServer32 = s '%MODULE%' + { + val ThreadingModel = s 'Both' + } + } + } +} diff --git a/audio/sysvad/SwapAPO/APO/swap.cpp b/audio/sysvad/SwapAPO/APO/swap.cpp new file mode 100644 index 000000000..0efe23f76 --- /dev/null +++ b/audio/sysvad/SwapAPO/APO/swap.cpp @@ -0,0 +1,183 @@ +// +// Swap.cpp -- Copyright (c) Microsoft Corporation. All rights reserved. +// +// Description: +// +// Implementation of SwapSamples +// +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "SwapAPO.h" + +#pragma AVRT_CODE_BEGIN +void WriteSilence( + _Out_writes_(u32FrameCount * u32SamplesPerFrame) + FLOAT32 *pf32Frames, + UINT32 u32FrameCount, + UINT32 u32SamplesPerFrame ) +{ + ZeroMemory(pf32Frames, sizeof(FLOAT32) * u32FrameCount * u32SamplesPerFrame); +} +#pragma AVRT_CODE_END + +#pragma AVRT_CODE_BEGIN +void CopyFrames( + _Out_writes_(u32FrameCount * u32SamplesPerFrame) + FLOAT32 *pf32OutFrames, + _In_reads_(u32FrameCount * u32SamplesPerFrame) + const FLOAT32 *pf32InFrames, + UINT32 u32FrameCount, + UINT32 u32SamplesPerFrame ) +{ + CopyMemory(pf32OutFrames, pf32InFrames, sizeof(FLOAT32) * u32FrameCount * u32SamplesPerFrame); +} +#pragma AVRT_CODE_END + +#pragma AVRT_CODE_BEGIN +void ProcessSwap( + FLOAT32 *pf32OutputFrames, + const FLOAT32 *pf32InputFrames, + UINT32 u32ValidFrameCount, + UINT32 u32SamplesPerFrame ) +{ + UINT32 u32SampleIndex; + FLOAT32 fSwap32; + + ASSERT_REALTIME(); + ATLASSERT( IS_VALID_TYPED_READ_POINTER(pf32InputFrames) ); + ATLASSERT( IS_VALID_TYPED_WRITE_POINTER(pf32OutputFrames) ); + + // loop through samples + while (u32ValidFrameCount--) + { + for (u32SampleIndex=0; u32SampleIndex+1 0) + { + // copy either the rest of the input/output buffer, + // or the rest of the delay buffer, + // whichever is smaller + UINT32 framesToCopy = min(u32ValidFrameCount, u32DelayFrames - (*pu32DelayIndex)); + + // delay => output +#pragma prefast(suppress: __WARNING_POTENTIAL_BUFFER_OVERFLOW_HIGH_PRIORITY, "this copy to pf32OutputFrames is in-range") + CopyFrames( pf32OutputFrames, + &pf32DelayBuffer[(*pu32DelayIndex) * u32SamplesPerFrame], + framesToCopy, + u32SamplesPerFrame ); + + // input => delay + CopyFrames( &pf32DelayBuffer[(*pu32DelayIndex) * u32SamplesPerFrame], + pf32InputFrames, + framesToCopy, + u32SamplesPerFrame ); + + pf32OutputFrames += framesToCopy * u32SamplesPerFrame; + pf32InputFrames += framesToCopy * u32SamplesPerFrame; + u32ValidFrameCount -= framesToCopy; + + *pu32DelayIndex += framesToCopy; + if (*pu32DelayIndex == u32DelayFrames) + { + *pu32DelayIndex = 0; + } + } + + } +} +#pragma AVRT_CODE_END + diff --git a/audio/sysvad/SwapAPO/APO/swapapo.png b/audio/sysvad/SwapAPO/APO/swapapo.png new file mode 100644 index 0000000000000000000000000000000000000000..861d5bcb65c0f2de3e76908ef4b05e47f8d7893e GIT binary patch literal 1251 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD1|%QND7OGojKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$3p^r=85sBufiR<}hF1enO@*h6V@O5Z+c~=n9~+3c&fg|>vhlcRfM~{C ziy+RBS4U=A-)P~wu4++~w30b=L1Ds1aovojCyP7Q+ZUVfNN3x;h0~GY++*eglJDQ- z2rxaK{ tN#ShBl@H0Sppc49<)R8Vo$F4GXC&_=)vObmX}-qv9!N?_GO&^YpUa z`|o(=*1t~OFL`UWnVIX?)SbNB zJ$Eht!hFs1zxuj^LsxA3^s0Yf*`Y1(AO7kJF4ep8yh(NE#l|P<3Hu{Y{5|+D;GIB@ZDz<^FPgA@@?IZ>YC*eWpANN5p+yFcUmU7e}@ zpKeH6@9E6f`TF}}oM_SB4b%0i0$W4)-LAYkr?O_+f(Jg&w!gL$eR?KvR>RTK`#&E3 z^FPM7C{?s~SJbYbI%jhDzSh2Oi_-7fdgt72Z}Gi1_Qgdxq%7X#B4#G)y5o-Plczf~ z_PzU@b3IRIKmYgO>z#pnV{dM|JLSf#Xcyl7pfy;4c(_Qg7m-TCce$q(iq{kNP8?byp01u9OJPS(&yeZbyKn z-Gs@dAAbB=!&>pWrZHbA;ljj6(pyeVkG-St!?efaDG=+wY1sG??n4ZZJN6O^HmqNWx|5D{>WATDY5g7 zl{tMWPW|c2Ex*=n5mnn1_uzWN4<_qdliJjlAKbc{Lwfd=@BS{2&OKwg``T^MA#etoA5fTD;bA`D(STM~<0R9b&pa zbr#!lmN?%(8yNoGznD;$Chjtc%9$2X5V$^(e;yU{_{f BGo1hc literal 0 HcmV?d00001 diff --git a/audio/sysvad/SwapAPO/APO/swapapomfx.cpp b/audio/sysvad/SwapAPO/APO/swapapomfx.cpp new file mode 100644 index 000000000..113e0aa42 --- /dev/null +++ b/audio/sysvad/SwapAPO/APO/swapapomfx.cpp @@ -0,0 +1,1047 @@ +// +// SwapAPOMFX.cpp -- Copyright (c) Microsoft Corporation. All rights reserved. +// +// Description: +// +// Implementation of CSwapAPOMFX +// + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include "SwapAPO.h" +#include "SysVadShared.h" +#include + + + +// Static declaration of the APO_REG_PROPERTIES structure +// associated with this APO. The number in <> brackets is the +// number of IIDs supported by this APO. If more than one, then additional +// IIDs are added at the end +#pragma warning (disable : 4815) +const AVRT_DATA CRegAPOProperties<1> CSwapAPOMFX::sm_RegProperties( + __uuidof(SwapAPOMFX), // clsid of this APO + L"CSwapAPOMFX", // friendly name of this APO + L"Copyright (c) Microsoft Corporation", // copyright info + 1, // major version # + 0, // minor version # + __uuidof(ISwapAPOMFX) // iid of primary interface +// +// If you need to change any of these attributes, uncomment everything up to +// the point that you need to change something. If you need to add IIDs, uncomment +// everything and add additional IIDs at the end. +// +// Enable inplace processing for this APO. +// , DEFAULT_APOREG_FLAGS +// , DEFAULT_APOREG_MININPUTCONNECTIONS +// , DEFAULT_APOREG_MAXINPUTCONNECTIONS +// , DEFAULT_APOREG_MINOUTPUTCONNECTIONS +// , DEFAULT_APOREG_MAXOUTPUTCONNECTIONS +// , DEFAULT_APOREG_MAXINSTANCES +// + ); + +//------------------------------------------------------------------------- +// Description: +// +// GetCurrentEffectsSetting +// Gets the current aggregate effects-enable setting +// +// Parameters: +// +// properties - Property store holding configurable effects settings +// +// pkeyEnable - VT_UI4 property holding an enable/disable setting +// +// processingMode - Audio processing mode +// +// Return values: +// LONG - true if the effect is enabled +// +// Remarks: +// The routine considers the value of the specified property, the well known +// master PKEY_AudioEndpoint_Disable_SysFx property, and the specified +// processing mode.If the processing mode is RAW then the effect is off. If +// PKEY_AudioEndpoint_Disable_SysFx is non-zero then the effect is off. +// +LONG GetCurrentEffectsSetting(IPropertyStore* properties, PROPERTYKEY pkeyEnable, GUID processingMode) +{ + HRESULT hr; + BOOL enabled; + PROPVARIANT var; + + PropVariantInit(&var); + + // Get the state of whether channel swap MFX is enabled or not. + + // Check the master disable property defined by Windows + hr = properties->GetValue(PKEY_AudioEndpoint_Disable_SysFx, &var); + enabled = (SUCCEEDED(hr)) && !((var.vt == VT_UI4) && (var.ulVal != 0)); + + PropVariantClear(&var); + + // Check the APO's enable property, defined by this APO. + hr = properties->GetValue(pkeyEnable, &var); + enabled = enabled && ((SUCCEEDED(hr)) && ((var.vt == VT_UI4) && (var.ulVal != 0))); + + PropVariantClear(&var); + + enabled = enabled && !IsEqualGUID(processingMode, AUDIO_SIGNALPROCESSINGMODE_RAW); + + return (LONG)enabled; +} + +#pragma AVRT_CODE_BEGIN +//------------------------------------------------------------------------- +// Description: +// +// Do the actual processing of data. +// +// Parameters: +// +// u32NumInputConnections - [in] number of input connections +// ppInputConnections - [in] pointer to list of input APO_CONNECTION_PROPERTY pointers +// u32NumOutputConnections - [in] number of output connections +// ppOutputConnections - [in] pointer to list of output APO_CONNECTION_PROPERTY pointers +// +// Return values: +// +// void +// +// Remarks: +// +// This function processes data in a manner dependent on the implementing +// object. This routine can not fail and can not block, or call any other +// routine that blocks, or touch pagable memory. +// +STDMETHODIMP_(void) CSwapAPOMFX::APOProcess( + UINT32 u32NumInputConnections, + APO_CONNECTION_PROPERTY** ppInputConnections, + UINT32 u32NumOutputConnections, + APO_CONNECTION_PROPERTY** ppOutputConnections) +{ + UNREFERENCED_PARAMETER(u32NumInputConnections); + UNREFERENCED_PARAMETER(u32NumOutputConnections); + + FLOAT32 *pf32InputFrames, *pf32OutputFrames; + + ATLASSERT(m_bIsLocked); + + // assert that the number of input and output connectins fits our registration properties + ATLASSERT(m_pRegProperties->u32MinInputConnections <= u32NumInputConnections); + ATLASSERT(m_pRegProperties->u32MaxInputConnections >= u32NumInputConnections); + ATLASSERT(m_pRegProperties->u32MinOutputConnections <= u32NumOutputConnections); + ATLASSERT(m_pRegProperties->u32MaxOutputConnections >= u32NumOutputConnections); + + // check APO_BUFFER_FLAGS. + switch( ppInputConnections[0]->u32BufferFlags ) + { + case BUFFER_INVALID: + { + ATLASSERT(false); // invalid flag - should never occur. don't do anything. + break; + } + case BUFFER_VALID: + case BUFFER_SILENT: + { + // get input pointer to connection buffer + pf32InputFrames = reinterpret_cast(ppInputConnections[0]->pBuffer); + ATLASSERT( IS_VALID_TYPED_READ_POINTER(pf32InputFrames) ); + + // get output pointer to connection buffer + pf32OutputFrames = reinterpret_cast(ppOutputConnections[0]->pBuffer); + ATLASSERT( IS_VALID_TYPED_READ_POINTER(pf32OutputFrames) ); + + if (BUFFER_SILENT == ppInputConnections[0]->u32BufferFlags) + { + WriteSilence( pf32InputFrames, + ppInputConnections[0]->u32ValidFrameCount, + GetSamplesPerFrame() ); + } + + // swap and apply coefficients to the input buffer in-place + if ( + !IsEqualGUID(m_AudioProcessingMode, AUDIO_SIGNALPROCESSINGMODE_RAW) && + m_fEnableSwapMFX && + (1 < m_u32SamplesPerFrame) + ) + { + ProcessSwapScale(pf32InputFrames, pf32InputFrames, + ppInputConnections[0]->u32ValidFrameCount, + m_u32SamplesPerFrame, m_pf32Coefficients ); + } + + // copy to the delay buffer + if ( + !IsEqualGUID(m_AudioProcessingMode, AUDIO_SIGNALPROCESSINGMODE_RAW) && + m_fEnableDelayMFX + ) + { + ProcessDelay(pf32OutputFrames, pf32InputFrames, + ppInputConnections[0]->u32ValidFrameCount, + GetSamplesPerFrame(), + m_pf32DelayBuffer, + m_nDelayFrames, + &m_iDelayIndex); + + // we don't try to remember silence + ppOutputConnections[0]->u32BufferFlags = BUFFER_VALID; + } + else + { + // copy the memory only if there is an output connection, and input/output pointers are unequal + if ( (0 != u32NumOutputConnections) && + (ppOutputConnections[0]->pBuffer != ppInputConnections[0]->pBuffer) ) + { + CopyFrames( pf32OutputFrames, pf32InputFrames, + ppInputConnections[0]->u32ValidFrameCount, + GetSamplesPerFrame() ); + } + + // pass along buffer flags + ppOutputConnections[0]->u32BufferFlags = ppInputConnections[0]->u32BufferFlags; + } + + // Set the valid frame count. + ppOutputConnections[0]->u32ValidFrameCount = ppInputConnections[0]->u32ValidFrameCount; + + break; + } + default: + { + ATLASSERT(false); // invalid flag - should never occur + break; + } + } // switch + +} // APOProcess +#pragma AVRT_CODE_END + +//------------------------------------------------------------------------- +// Description: +// +// Report delay added by the APO between samples given on input +// and samples given on output. +// +// Parameters: +// +// pTime - [out] hundreds-of-nanoseconds of delay added +// +// Return values: +// +// S_OK on success, a failure code on failure +STDMETHODIMP CSwapAPOMFX::GetLatency(HNSTIME* pTime) +{ + ASSERT_NONREALTIME(); + HRESULT hr = S_OK; + + IF_TRUE_ACTION_JUMP(NULL == pTime, hr = E_POINTER, Exit); + + if (IsEqualGUID(m_AudioProcessingMode, AUDIO_SIGNALPROCESSINGMODE_RAW)) + { + *pTime = 0; + } + else + { + *pTime = (m_fEnableDelayMFX ? HNS_DELAY : 0); + } + +Exit: + return hr; +} + +//------------------------------------------------------------------------- +// Description: +// +// Verifies that the APO is ready to process and locks its state if so. +// +// Parameters: +// +// u32NumInputConnections - [in] number of input connections attached to this APO +// ppInputConnections - [in] connection descriptor of each input connection attached to this APO +// u32NumOutputConnections - [in] number of output connections attached to this APO +// ppOutputConnections - [in] connection descriptor of each output connection attached to this APO +// +// Return values: +// +// S_OK Object is locked and ready to process. +// E_POINTER Invalid pointer passed to function. +// APOERR_INVALID_CONNECTION_FORMAT Invalid connection format. +// APOERR_NUM_CONNECTIONS_INVALID Number of input or output connections is not valid on +// this APO. +STDMETHODIMP CSwapAPOMFX::LockForProcess(UINT32 u32NumInputConnections, + APO_CONNECTION_DESCRIPTOR** ppInputConnections, + UINT32 u32NumOutputConnections, APO_CONNECTION_DESCRIPTOR** ppOutputConnections) +{ + ASSERT_NONREALTIME(); + HRESULT hr = S_OK; + + hr = CBaseAudioProcessingObject::LockForProcess(u32NumInputConnections, + ppInputConnections, u32NumOutputConnections, ppOutputConnections); + IF_FAILED_JUMP(hr, Exit); + + if (!IsEqualGUID(m_AudioProcessingMode, AUDIO_SIGNALPROCESSINGMODE_RAW) && m_fEnableDelayMFX) + { + m_nDelayFrames = FRAMES_FROM_HNS(HNS_DELAY); + m_iDelayIndex = 0; + + m_pf32DelayBuffer.Free(); + + // Allocate one second's worth of audio + // + // This allocation is being done using CoTaskMemAlloc because the delay is very large + // This introduces a risk of glitches if the delay buffer gets paged out + // + // A more typical approach would be to allocate the memory using AERT_Allocate, which locks the memory + // But for the purposes of this APO, CoTaskMemAlloc suffices, and the risk of glitches is not important + m_pf32DelayBuffer.Allocate(GetSamplesPerFrame() * m_nDelayFrames); + WriteSilence(m_pf32DelayBuffer, m_nDelayFrames, GetSamplesPerFrame()); + if (nullptr == m_pf32DelayBuffer) + { + hr = E_OUTOFMEMORY; + goto Exit; + } + } + +Exit: + return hr; +} + +// The method that this long comment refers to is "Initialize()" +//------------------------------------------------------------------------- +// Description: +// +// Generic initialization routine for APOs. +// +// Parameters: +// +// cbDataSize - [in] the size in bytes of the initialization data. +// pbyData - [in] initialization data specific to this APO +// +// Return values: +// +// S_OK Successful completion. +// E_POINTER Invalid pointer passed to this function. +// E_INVALIDARG Invalid argument +// AEERR_ALREADY_INITIALIZED APO is already initialized +// +// Remarks: +// +// This method initializes the APO. The data is variable length and +// should have the form of: +// +// struct MyAPOInitializationData +// { +// APOInitBaseStruct APOInit; +// ... // add additional fields here +// }; +// +// If the APO needs no initialization or needs no data to initialize +// itself, it is valid to pass NULL as the pbyData parameter and 0 as +// the cbDataSize parameter. +// +// As part of designing an APO, decide which parameters should be +// immutable (set once during initialization) and which mutable (changeable +// during the lifetime of the APO instance). Immutable parameters must +// only be specifiable in the Initialize call; mutable parameters must be +// settable via methods on whichever parameter control interface(s) your +// APO provides. Mutable values should either be set in the initialize +// method (if they are required for proper operation of the APO prior to +// LockForProcess) or default to reasonable values upon initialize and not +// be required to be set before LockForProcess. +// +// Within the mutable parameters, you must also decide which can be changed +// while the APO is locked for processing and which cannot. +// +// All parameters should be considered immutable as a first choice, unless +// there is a specific scenario which requires them to be mutable; similarly, +// no mutable parameters should be changeable while the APO is locked, unless +// a specific scenario requires them to be. Following this guideline will +// simplify the APO's state diagram and implementation and prevent certain +// types of bug. +// +// If a parameter changes the APOs latency or MaxXXXFrames values, it must be +// immutable. +// +// The default version of this function uses no initialization data, but does verify +// the passed parameters and set the m_bIsInitialized member to true. +// +// Note: This method may not be called from a real-time processing thread. +// + +HRESULT CSwapAPOMFX::Initialize(UINT32 cbDataSize, BYTE* pbyData) +{ + HRESULT hr = S_OK; + GUID processingMode; + + IF_TRUE_ACTION_JUMP( ((NULL == pbyData) && (0 != cbDataSize)), hr = E_INVALIDARG, Exit); + IF_TRUE_ACTION_JUMP( ((NULL != pbyData) && (0 == cbDataSize)), hr = E_INVALIDARG, Exit); + + if (cbDataSize == sizeof(APOInitSystemEffects2)) + { + // + // Initialize for mode-specific signal processing + // + APOInitSystemEffects2* papoSysFxInit2 = (APOInitSystemEffects2*)pbyData; + + // Save reference to the effects property store. This saves effects settings + // and is the communication medium between this APO and any associated UI. + m_spAPOSystemEffectsProperties = papoSysFxInit2->pAPOSystemEffectsProperties; + + // Windows should pass a valid collection. + ATLASSERT(papoSysFxInit2->pDeviceCollection != nullptr); + IF_TRUE_ACTION_JUMP(papoSysFxInit2->pDeviceCollection == nullptr, hr = E_INVALIDARG, Exit); + + // Save the processing mode being initialized. + processingMode = papoSysFxInit2->AudioProcessingMode; + + // There is information in the APOInitSystemEffects2 structure that could help facilitate + // proprietary communication between an APO instance and the KS pin that the APO is initialized on + // Eg, in the case that an APO is implemented as an effect proxy for the effect processing hosted inside + // an driver (either host CPU based or offload DSP based), the example below uses a combination of + // IDeviceTopology, IConnector, and IKsControl interfaces to communicate with the underlying audio driver. + // the following following routine demonstrates how to implement how to communicate to an audio driver from a APO. + ProprietaryCommunicationWithDriver(papoSysFxInit2); + } + else if (cbDataSize == sizeof(APOInitSystemEffects)) + { + // + // Initialize for default signal processing + // + APOInitSystemEffects* papoSysFxInit = (APOInitSystemEffects*)pbyData; + + // Save reference to the effects property store. This saves effects settings + // and is the communication medium between this APO and any associated UI. + m_spAPOSystemEffectsProperties = papoSysFxInit->pAPOSystemEffectsProperties; + + // Assume default processing mode + processingMode = AUDIO_SIGNALPROCESSINGMODE_DEFAULT; + } + else + { + // Invalid initialization size + hr = E_INVALIDARG; + goto Exit; + } + + // Validate then save the processing mode. Note an endpoint effects APO + // does not depend on the mode. Windows sets the APOInitSystemEffects2 + // AudioProcessingMode member to GUID_NULL for an endpoint effects APO. + IF_TRUE_ACTION_JUMP((processingMode != AUDIO_SIGNALPROCESSINGMODE_DEFAULT && + processingMode != AUDIO_SIGNALPROCESSINGMODE_RAW && + processingMode != AUDIO_SIGNALPROCESSINGMODE_COMMUNICATIONS && + processingMode != AUDIO_SIGNALPROCESSINGMODE_SPEECH && + processingMode != AUDIO_SIGNALPROCESSINGMODE_MEDIA && + processingMode != AUDIO_SIGNALPROCESSINGMODE_MOVIE), hr = E_INVALIDARG, Exit); + m_AudioProcessingMode = processingMode; + + // + // An APO that implements signal processing more complex than this sample + // would configure its processing for the processingMode determined above. + // If necessary, the APO would also use the IDeviceTopology and IConnector + // interfaces retrieved above to communicate with its counterpart audio + // driver to configure any additional signal processing in the driver and + // associated hardware. + // + + // + // Get current effects settings + // + if (m_spAPOSystemEffectsProperties != NULL) + { + m_fEnableSwapMFX = GetCurrentEffectsSetting(m_spAPOSystemEffectsProperties, PKEY_Endpoint_Enable_Channel_Swap_MFX, m_AudioProcessingMode); + m_fEnableDelayMFX = GetCurrentEffectsSetting(m_spAPOSystemEffectsProperties, PKEY_Endpoint_Enable_Delay_MFX, m_AudioProcessingMode); + } + + // + // Register for notification of registry updates + // + hr = m_spEnumerator.CoCreateInstance(__uuidof(MMDeviceEnumerator)); + IF_FAILED_JUMP(hr, Exit); + + hr = m_spEnumerator->RegisterEndpointNotificationCallback(this); + IF_FAILED_JUMP(hr, Exit); + + m_bIsInitialized = true; +Exit: + return hr; +} + +//------------------------------------------------------------------------- +// +// GetEffectsList +// +// Retrieves the list of signal processing effects currently active and +// stores an event to be signaled if the list changes. +// +// Parameters +// +// ppEffectsIds - returns a pointer to a list of GUIDs each identifying a +// class of effect. The caller is responsible for freeing this memory by +// calling CoTaskMemFree. +// +// pcEffects - returns a count of GUIDs in the list. +// +// Event - passes an event handle. The APO signals this event when the list +// of effects changes from the list returned from this function. The APO +// uses this event until either this function is called again or the APO +// is destroyed. The passed handle may be NULL. In this case, the APO +// stops using any previous handle and does not signal an event. +// +// Remarks +// +// An APO imlements this method to allow Windows to discover the current +// effects applied by the APO. The list of effects may depend on what signal +// processing mode the APO initialized (see AudioProcessingMode in the +// APOInitSystemEffects2 structure) as well as any end user configuration. +// +// If there are no effects then the function still succeeds, ppEffectsIds +// returns a NULL pointer, and pcEffects returns a count of 0. +// +STDMETHODIMP CSwapAPOMFX::GetEffectsList(_Outptr_result_buffer_maybenull_(*pcEffects) LPGUID *ppEffectsIds, _Out_ UINT *pcEffects, _In_ HANDLE Event) +{ + HRESULT hr; + BOOL effectsLocked = FALSE; + UINT cEffects = 0; + + IF_TRUE_ACTION_JUMP(ppEffectsIds == NULL, hr = E_POINTER, Exit); + IF_TRUE_ACTION_JUMP(pcEffects == NULL, hr = E_POINTER, Exit); + + // Synchronize access to the effects list and effects changed event + m_EffectsLock.Enter(); + effectsLocked = TRUE; + + // Always close existing effects change event handle + if (m_hEffectsChangedEvent != NULL) + { + CloseHandle(m_hEffectsChangedEvent); + m_hEffectsChangedEvent = NULL; + } + + // If an event handle was specified, save it here (duplicated to control lifetime) + if (Event != NULL) + { + if (!DuplicateHandle(GetCurrentProcess(), Event, GetCurrentProcess(), &m_hEffectsChangedEvent, EVENT_MODIFY_STATE, FALSE, 0)) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + goto Exit; + } + } + + // naked scope to force the initialization of list[] to be after we enter the critical section + { + struct EffectControl + { + GUID effect; + BOOL control; + }; + + EffectControl list[] = + { + { SwapEffectId, m_fEnableSwapMFX }, + { DelayEffectId, m_fEnableDelayMFX }, + }; + + if (!IsEqualGUID(m_AudioProcessingMode, AUDIO_SIGNALPROCESSINGMODE_RAW)) + { + // count the active effects + for (UINT i = 0; i < ARRAYSIZE(list); i++) + { + if (list[i].control) + { + cEffects++; + } + } + } + + if (0 == cEffects) + { + *ppEffectsIds = NULL; + *pcEffects = 0; + } + else + { + GUID *pEffectsIds = (LPGUID)CoTaskMemAlloc(sizeof(GUID) * cEffects); + if (pEffectsIds == nullptr) + { + hr = E_OUTOFMEMORY; + goto Exit; + } + + // pick up the active effects + UINT j = 0; + for (UINT i = 0; i < ARRAYSIZE(list); i++) + { + if (list[i].control) + { + pEffectsIds[j++] = list[i].effect; + } + } + + *ppEffectsIds = pEffectsIds; + *pcEffects = cEffects; + } + + hr = S_OK; + } + +Exit: + if (effectsLocked) + { + m_EffectsLock.Leave(); + } + return hr; +} + +HRESULT CSwapAPOMFX::ProprietaryCommunicationWithDriver(APOInitSystemEffects2 *_pAPOSysFxInit2) +{ + HRESULT hr = S_OK; + CComPtr spMyDevice; + CComPtr spMyDeviceTopology; + CComPtr spMyConnector; + CComPtr spMyConnectorPart; + CComPtr spKsControl; + UINT uKsPinId = 0; + UINT myPartId = 0; + + ULONG ulBytesReturned = 0; + CComHeapPtr spKsMultipleItem; + KSP_PIN ksPin = {0}; + + UINT nSoftwareIoDeviceInCollection = 0 ; + UINT nSoftwareIoConnectorIndex = 0 ; + + nSoftwareIoDeviceInCollection = _pAPOSysFxInit2->nSoftwareIoDeviceInCollection; + nSoftwareIoConnectorIndex = _pAPOSysFxInit2->nSoftwareIoConnectorIndex; + + // Get the target IMMDevice + hr = _pAPOSysFxInit2->pDeviceCollection->Item(nSoftwareIoDeviceInCollection, &spMyDevice); + IF_FAILED_JUMP(hr, Exit); + + // Instantiate a device topology instance + hr = spMyDevice->Activate(__uuidof(IDeviceTopology), CLSCTX_ALL, NULL, (void**)&spMyDeviceTopology); + IF_FAILED_JUMP(hr, Exit); + + // retrieve connect instance + hr = spMyDeviceTopology->GetConnector(nSoftwareIoConnectorIndex, &spMyConnector); + IF_FAILED_JUMP(hr, Exit); + + // activate IKsControl on the IMMDevice + hr = spMyDevice->Activate(__uuidof(IKsControl), CLSCTX_INPROC_SERVER, NULL, (void**)&spKsControl); + IF_FAILED_JUMP(hr, Exit); + + // get KS pin id + hr = spMyConnector->QueryInterface(__uuidof(IPart), (void**)&spMyConnectorPart); + IF_FAILED_JUMP(hr, Exit); + hr = spMyConnectorPart->GetLocalId(&myPartId); + IF_FAILED_JUMP(hr, Exit); + + uKsPinId = myPartId & 0x0000ffff; + + ksPin.Property.Set = KSPROPSETID_SysVAD; + ksPin.Property.Id = KSPROPERTY_SYSVAD_DEFAULTSTREAMEFFECTS; + ksPin.Property.Flags = KSPROPERTY_TYPE_GET; + ksPin.PinId = uKsPinId; + + // First, get size of array returned by driver + hr = spKsControl->KsProperty( &ksPin.Property, + sizeof(KSP_PIN), + NULL, + 0, + &ulBytesReturned ); + IF_FAILED_JUMP(hr, Exit); + + if( !spKsMultipleItem.AllocateBytes(ulBytesReturned) ) + { + hr = E_OUTOFMEMORY; + IF_FAILED_JUMP(hr, Exit); + } + + // Second, now get the active effects from the driver + hr = spKsControl->KsProperty( &ksPin.Property, + sizeof(KSP_PIN), + spKsMultipleItem, + ulBytesReturned, + &ulBytesReturned ); + IF_FAILED_JUMP(hr, Exit); + + // Upon successful return, effect guids could be found in the memory following (spKsMultipleItem.m_pData + 1) + // and effectcount could be found in spKsMultipleItem->Count; + +Exit: + return hr; +} + +//------------------------------------------------------------------------- +// Description: +// +// Implementation of IMMNotificationClient::OnPropertyValueChanged +// +// Parameters: +// +// pwstrDeviceId - [in] the id of the device whose property has changed +// key - [in] the property that changed +// +// Return values: +// +// Ignored by caller +// +// Remarks: +// +// This method is called asynchronously. No UI work should be done here. +// +HRESULT CSwapAPOMFX::OnPropertyValueChanged(LPCWSTR pwstrDeviceId, const PROPERTYKEY key) +{ + HRESULT hr = S_OK; + + UNREFERENCED_PARAMETER(pwstrDeviceId); + + if (!m_spAPOSystemEffectsProperties) + { + return hr; + } + + // If either the master disable or our APO's enable properties changed... + if (PK_EQUAL(key, PKEY_Endpoint_Enable_Channel_Swap_MFX) || + PK_EQUAL(key, PKEY_Endpoint_Enable_Delay_MFX) || + PK_EQUAL(key, PKEY_AudioEndpoint_Disable_SysFx)) + { + LONG nChanges = 0; + + // Synchronize access to the effects list and effects changed event + m_EffectsLock.Enter(); + + struct KeyControl + { + PROPERTYKEY key; + LONG *value; + }; + + KeyControl controls[] = + { + { PKEY_Endpoint_Enable_Channel_Swap_MFX, &m_fEnableSwapMFX }, + { PKEY_Endpoint_Enable_Delay_MFX, &m_fEnableDelayMFX }, + }; + + for (int i = 0; i < ARRAYSIZE(controls); i++) + { + LONG fOldValue; + LONG fNewValue = true; + + // Get the state of whether channel swap MFX is enabled or not + fNewValue = GetCurrentEffectsSetting(m_spAPOSystemEffectsProperties, controls[i].key, m_AudioProcessingMode); + + // Swap in the new setting + fOldValue = InterlockedExchange(controls[i].value, fNewValue); + + if (fNewValue != fOldValue) + { + nChanges++; + } + } + + // If anything changed and a change event handle exists + if ((nChanges > 0) && (m_hEffectsChangedEvent != NULL)) + { + SetEvent(m_hEffectsChangedEvent); + } + + m_EffectsLock.Leave(); + } + + return hr; +} + +//------------------------------------------------------------------------- +// Description: +// +// Destructor. +// +// Parameters: +// +// void +// +// Return values: +// +// void +// +// Remarks: +// +// This method deletes whatever was allocated. +// +// This method may not be called from a real-time processing thread. +// +CSwapAPOMFX::~CSwapAPOMFX(void) +{ + if (m_bIsInitialized) + { + // + // unregister for callbacks + // + if (m_spEnumerator != NULL) + { + m_spEnumerator->UnregisterEndpointNotificationCallback(this); + } + } + + if (m_hEffectsChangedEvent != NULL) + { + CloseHandle(m_hEffectsChangedEvent); + } + + // Free locked memory allocations + if (NULL != m_pf32Coefficients) + { + AERT_Free(m_pf32Coefficients); + m_pf32Coefficients = NULL; + } +} // ~CSwapAPOMFX + + +//------------------------------------------------------------------------- +// Description: +// +// Validates input/output format pair during LockForProcess. +// +// Parameters: +// +// u32NumInputConnections - [in] number of input connections attached to this APO +// ppInputConnections - [in] format of each input connection attached to this APO +// u32NumOutputConnections - [in] number of output connections attached to this APO +// ppOutputConnections - [in] format of each output connection attached to this APO +// +// Return values: +// +// S_OK Connections are valid. +// +// See Also: +// +// CBaseAudioProcessingObject::LockForProcess +// +// Remarks: +// +// This method is an internal call that is called by the default implementation of +// CBaseAudioProcessingObject::LockForProcess(). This is called after the connections +// are validated for simple conformance to the APO's registration properties. It may be +// used to verify that the APO is initialized properly and that the connections that are passed +// agree with the data used for initialization. Any failure code passed back from this +// function will get returned by LockForProcess, and cause it to fail. +// +// By default, this routine just ASSERTS and returns S_OK. +// +HRESULT CSwapAPOMFX::ValidateAndCacheConnectionInfo(UINT32 u32NumInputConnections, + APO_CONNECTION_DESCRIPTOR** ppInputConnections, + UINT32 u32NumOutputConnections, + APO_CONNECTION_DESCRIPTOR** ppOutputConnections) +{ + ASSERT_NONREALTIME(); + HRESULT hResult; + CComPtr pFormat; + UNCOMPRESSEDAUDIOFORMAT UncompInputFormat, UncompOutputFormat; + FLOAT32 f32InverseChannelCount; + + UNREFERENCED_PARAMETER(u32NumInputConnections); + UNREFERENCED_PARAMETER(u32NumOutputConnections); + + _ASSERTE(!m_bIsLocked); + _ASSERTE(((0 == u32NumInputConnections) || (NULL != ppInputConnections)) && + ((0 == u32NumOutputConnections) || (NULL != ppOutputConnections))); + + EnterCriticalSection(&m_CritSec); + + // get the uncompressed formats and channel masks + hResult = ppInputConnections[0]->pFormat->GetUncompressedAudioFormat(&UncompInputFormat); + IF_FAILED_JUMP(hResult, Exit); + + hResult = ppOutputConnections[0]->pFormat->GetUncompressedAudioFormat(&UncompOutputFormat); + IF_FAILED_JUMP(hResult, Exit); + + // Since we haven't overridden the IsIn{Out}putFormatSupported APIs in this example, this APO should + // always have input channel count == output channel count. The sampling rates should also be eqaul, + // and formats 32-bit float. + _ASSERTE(UncompOutputFormat.fFramesPerSecond == UncompInputFormat.fFramesPerSecond); + _ASSERTE(UncompOutputFormat. dwSamplesPerFrame == UncompInputFormat.dwSamplesPerFrame); + + // Allocate some locked memory. We will use these as scaling coefficients during APOProcess->ProcessSwapScale + hResult = AERT_Allocate(sizeof(FLOAT32)*m_u32SamplesPerFrame, (void**)&m_pf32Coefficients); + IF_FAILED_JUMP(hResult, Exit); + + // Set scalars to decrease volume from 1.0 to 1.0/N where N is the number of channels + // starting with the first channel. + f32InverseChannelCount = 1.0f/m_u32SamplesPerFrame; + for (UINT16 u16Index=0; u16Index= _cCustomFormats), hr = E_INVALIDARG, Exit); + IF_TRUE_ACTION_JUMP((ppFormat == NULL), hr = E_POINTER, Exit); + + *ppFormat = NULL; + + hr = CreateAudioMediaType( (const WAVEFORMATEX*)&_rgCustomFormats[nFormat].wfxFmt, + sizeof(_rgCustomFormats[nFormat].wfxFmt), + ppFormat); + +Exit: + return hr; +} + +//------------------------------------------------------------------------- +// Description: +// +// Implementation of IAudioSystemEffectsCustomFormats::GetFormatRepresentation +// +// Parameters: +// +// nFormat - [in] which format is being requested +// ppwstrFormatRep - [in] address of a variable that will receive a ptr +// to a new string description of the requested format +// +// Return values: +// +// S_OK Success +// E_INVALIDARG nFormat is out of range +// E_POINTER Null pointer passed +// +// Remarks: +// +STDMETHODIMP CSwapAPOMFX::GetFormatRepresentation +( + UINT nFormat, + _Outptr_ LPWSTR* ppwstrFormatRep +) +{ + HRESULT hr; + size_t cbRep; + LPWSTR pwstrLocal; + + IF_TRUE_ACTION_JUMP((nFormat >= _cCustomFormats), hr = E_INVALIDARG, Exit); + IF_TRUE_ACTION_JUMP((ppwstrFormatRep == NULL), hr = E_POINTER, Exit); + + cbRep = (wcslen(_rgCustomFormats[nFormat].pwszRep) + 1) * sizeof(WCHAR); + + pwstrLocal = (LPWSTR)CoTaskMemAlloc(cbRep); + IF_TRUE_ACTION_JUMP((pwstrLocal == NULL), hr = E_OUTOFMEMORY, Exit); + + hr = StringCbCopyW(pwstrLocal, cbRep, _rgCustomFormats[nFormat].pwszRep); + if (FAILED(hr)) + { + CoTaskMemFree(pwstrLocal); + } + else + { + *ppwstrFormatRep = pwstrLocal; + } + +Exit: + return hr; +} diff --git a/audio/sysvad/SwapAPO/APO/swapaposfx.cpp b/audio/sysvad/SwapAPO/APO/swapaposfx.cpp new file mode 100644 index 000000000..32ddb7fc5 --- /dev/null +++ b/audio/sysvad/SwapAPO/APO/swapaposfx.cpp @@ -0,0 +1,677 @@ +// +// SwapAPOSFX.cpp -- Copyright (c) Microsoft Corporation. All rights reserved. +// +// Description: +// +// Implementation of CSwapAPOSFX +// + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "SwapAPO.h" +#include +#include + + +// Static declaration of the APO_REG_PROPERTIES structure +// associated with this APO. The number in <> brackets is the +// number of IIDs supported by this APO. If more than one, then additional +// IIDs are added at the end +#pragma warning (disable : 4815) +const AVRT_DATA CRegAPOProperties<1> CSwapAPOSFX::sm_RegProperties( + __uuidof(SwapAPOSFX), // clsid of this APO + L"CSwapAPOSFX", // friendly name of this APO + L"Copyright (c) Microsoft Corporation", // copyright info + 1, // major version # + 0, // minor version # + __uuidof(ISwapAPOSFX) // iid of primary interface + +// +// If you need to change any of these attributes, uncomment everything up to +// the point that you need to change something. If you need to add IIDs, uncomment +// everything and add additional IIDs at the end. +// +// , DEFAULT_APOREG_FLAGS +// , DEFAULT_APOREG_MININPUTCONNECTIONS +// , DEFAULT_APOREG_MAXINPUTCONNECTIONS +// , DEFAULT_APOREG_MINOUTPUTCONNECTIONS +// , DEFAULT_APOREG_MAXOUTPUTCONNECTIONS +// , DEFAULT_APOREG_MAXINSTANCES +// + ); + +#pragma AVRT_CODE_BEGIN +//------------------------------------------------------------------------- +// Description: +// +// Do the actual processing of data. +// +// Parameters: +// +// u32NumInputConnections - [in] number of input connections +// ppInputConnections - [in] pointer to list of input APO_CONNECTION_PROPERTY pointers +// u32NumOutputConnections - [in] number of output connections +// ppOutputConnections - [in] pointer to list of output APO_CONNECTION_PROPERTY pointers +// +// Return values: +// +// void +// +// Remarks: +// +// This function processes data in a manner dependent on the implementing +// object. This routine can not fail and can not block, or call any other +// routine that blocks, or touch pagable memory. +// +STDMETHODIMP_(void) CSwapAPOSFX::APOProcess( + UINT32 u32NumInputConnections, + APO_CONNECTION_PROPERTY** ppInputConnections, + UINT32 u32NumOutputConnections, + APO_CONNECTION_PROPERTY** ppOutputConnections) +{ + UNREFERENCED_PARAMETER(u32NumInputConnections); + UNREFERENCED_PARAMETER(u32NumOutputConnections); + + FLOAT32 *pf32InputFrames, *pf32OutputFrames; + + ATLASSERT(m_bIsLocked); + + // assert that the number of input and output connectins fits our registration properties + ATLASSERT(m_pRegProperties->u32MinInputConnections <= u32NumInputConnections); + ATLASSERT(m_pRegProperties->u32MaxInputConnections >= u32NumInputConnections); + ATLASSERT(m_pRegProperties->u32MinOutputConnections <= u32NumOutputConnections); + ATLASSERT(m_pRegProperties->u32MaxOutputConnections >= u32NumOutputConnections); + + // check APO_BUFFER_FLAGS. + switch( ppInputConnections[0]->u32BufferFlags ) + { + case BUFFER_INVALID: + { + ATLASSERT(false); // invalid flag - should never occur. don't do anything. + break; + } + case BUFFER_VALID: + case BUFFER_SILENT: + { + // get input pointer to connection buffer + pf32InputFrames = reinterpret_cast(ppInputConnections[0]->pBuffer); + ATLASSERT( IS_VALID_TYPED_READ_POINTER(pf32InputFrames) ); + + // get output pointer to connection buffer + pf32OutputFrames = reinterpret_cast(ppOutputConnections[0]->pBuffer); + ATLASSERT( IS_VALID_TYPED_WRITE_POINTER(pf32OutputFrames) ); + + if (BUFFER_SILENT == ppInputConnections[0]->u32BufferFlags) + { + WriteSilence( pf32InputFrames, + ppInputConnections[0]->u32ValidFrameCount, + GetSamplesPerFrame() ); + } + + // swap the input buffer in-place + if ( + !IsEqualGUID(m_AudioProcessingMode, AUDIO_SIGNALPROCESSINGMODE_RAW) && + m_fEnableSwapSFX + ) + { + ProcessSwap(pf32InputFrames, pf32InputFrames, + ppInputConnections[0]->u32ValidFrameCount, + m_u32SamplesPerFrame); + } + + // copy to the delay buffer + if ( + !IsEqualGUID(m_AudioProcessingMode, AUDIO_SIGNALPROCESSINGMODE_RAW) && + m_fEnableDelaySFX + ) + { + ProcessDelay(pf32OutputFrames, pf32InputFrames, + ppInputConnections[0]->u32ValidFrameCount, + GetSamplesPerFrame(), + m_pf32DelayBuffer, + m_nDelayFrames, + &m_iDelayIndex); + + // we don't try to remember silence + ppOutputConnections[0]->u32BufferFlags = BUFFER_VALID; + } + else + { + // copy the memory only if there is an output connection, and input/output pointers are unequal + if ( (0 != u32NumOutputConnections) && + (ppOutputConnections[0]->pBuffer != ppInputConnections[0]->pBuffer) ) + { + CopyFrames( pf32OutputFrames, pf32InputFrames, + ppInputConnections[0]->u32ValidFrameCount, + GetSamplesPerFrame() ); + } + + // pass along buffer flags + ppOutputConnections[0]->u32BufferFlags = ppInputConnections[0]->u32BufferFlags; + } + + // Set the valid frame count. + ppOutputConnections[0]->u32ValidFrameCount = ppInputConnections[0]->u32ValidFrameCount; + + break; + } + default: + { + ATLASSERT(false); // invalid flag - should never occur + break; + } + } // switch + +} // APOProcess +#pragma AVRT_CODE_END + +//------------------------------------------------------------------------- +// Description: +// +// Report delay added by the APO between samples given on input +// and samples given on output. +// +// Parameters: +// +// pTime - [out] hundreds-of-nanoseconds of delay added +// +// Return values: +// +// S_OK on success, a failure code on failure +STDMETHODIMP CSwapAPOSFX::GetLatency(HNSTIME* pTime) +{ + ASSERT_NONREALTIME(); + HRESULT hr = S_OK; + + IF_TRUE_ACTION_JUMP(NULL == pTime, hr = E_POINTER, Exit); + + if (IsEqualGUID(m_AudioProcessingMode, AUDIO_SIGNALPROCESSINGMODE_RAW)) + { + *pTime = 0; + } + else + { + *pTime = (m_fEnableDelaySFX ? HNS_DELAY : 0); + } + +Exit: + return hr; +} + +//------------------------------------------------------------------------- +// Description: +// +// Verifies that the APO is ready to process and locks its state if so. +// +// Parameters: +// +// u32NumInputConnections - [in] number of input connections attached to this APO +// ppInputConnections - [in] connection descriptor of each input connection attached to this APO +// u32NumOutputConnections - [in] number of output connections attached to this APO +// ppOutputConnections - [in] connection descriptor of each output connection attached to this APO +// +// Return values: +// +// S_OK Object is locked and ready to process. +// E_POINTER Invalid pointer passed to function. +// APOERR_INVALID_CONNECTION_FORMAT Invalid connection format. +// APOERR_NUM_CONNECTIONS_INVALID Number of input or output connections is not valid on +// this APO. +STDMETHODIMP CSwapAPOSFX::LockForProcess(UINT32 u32NumInputConnections, + APO_CONNECTION_DESCRIPTOR** ppInputConnections, + UINT32 u32NumOutputConnections, APO_CONNECTION_DESCRIPTOR** ppOutputConnections) +{ + ASSERT_NONREALTIME(); + HRESULT hr = S_OK; + + hr = CBaseAudioProcessingObject::LockForProcess(u32NumInputConnections, + ppInputConnections, u32NumOutputConnections, ppOutputConnections); + IF_FAILED_JUMP(hr, Exit); + + if (!IsEqualGUID(m_AudioProcessingMode, AUDIO_SIGNALPROCESSINGMODE_RAW) && m_fEnableDelaySFX) + { + m_nDelayFrames = FRAMES_FROM_HNS(HNS_DELAY); + m_iDelayIndex = 0; + + m_pf32DelayBuffer.Free(); + + // Allocate one second's worth of audio + // + // This allocation is being done using CoTaskMemAlloc because the delay is very large + // This introduces a risk of glitches if the delay buffer gets paged out + // + // A more typical approach would be to allocate the memory using AERT_Allocate, which locks the memory + // But for the purposes of this APO, CoTaskMemAlloc suffices, and the risk of glitches is not important + m_pf32DelayBuffer.Allocate(GetSamplesPerFrame() * m_nDelayFrames); + + WriteSilence(m_pf32DelayBuffer, m_nDelayFrames, GetSamplesPerFrame()); + if (nullptr == m_pf32DelayBuffer) + { + hr = E_OUTOFMEMORY; + goto Exit; + } + } + +Exit: + return hr; +} + +// The method that this long comment refers to is "Initialize()" +//------------------------------------------------------------------------- +// Description: +// +// Generic initialization routine for APOs. +// +// Parameters: +// +// cbDataSize - [in] the size in bytes of the initialization data. +// pbyData - [in] initialization data specific to this APO +// +// Return values: +// +// S_OK Successful completion. +// E_POINTER Invalid pointer passed to this function. +// E_INVALIDARG Invalid argument +// AEERR_ALREADY_INITIALIZED APO is already initialized +// +// Remarks: +// +// This method initializes the APO. The data is variable length and +// should have the form of: +// +// struct MyAPOInitializationData +// { +// APOInitBaseStruct APOInit; +// ... // add additional fields here +// }; +// +// If the APO needs no initialization or needs no data to initialize +// itself, it is valid to pass NULL as the pbyData parameter and 0 as +// the cbDataSize parameter. +// +// As part of designing an APO, decide which parameters should be +// immutable (set once during initialization) and which mutable (changeable +// during the lifetime of the APO instance). Immutable parameters must +// only be specifiable in the Initialize call; mutable parameters must be +// settable via methods on whichever parameter control interface(s) your +// APO provides. Mutable values should either be set in the initialize +// method (if they are required for proper operation of the APO prior to +// LockForProcess) or default to reasonable values upon initialize and not +// be required to be set before LockForProcess. +// +// Within the mutable parameters, you must also decide which can be changed +// while the APO is locked for processing and which cannot. +// +// All parameters should be considered immutable as a first choice, unless +// there is a specific scenario which requires them to be mutable; similarly, +// no mutable parameters should be changeable while the APO is locked, unless +// a specific scenario requires them to be. Following this guideline will +// simplify the APO's state diagram and implementation and prevent certain +// types of bug. +// +// If a parameter changes the APOs latency or MaxXXXFrames values, it must be +// immutable. +// +// The default version of this function uses no initialization data, but does verify +// the passed parameters and set the m_bIsInitialized member to true. +// +// Note: This method may not be called from a real-time processing thread. +// + +HRESULT CSwapAPOSFX::Initialize(UINT32 cbDataSize, BYTE* pbyData) +{ + HRESULT hr = S_OK; + CComPtr spMyDevice; + CComPtr spMyDeviceTopology; + CComPtr spMyConnector; + GUID processingMode; + + IF_TRUE_ACTION_JUMP( ((NULL == pbyData) && (0 != cbDataSize)), hr = E_INVALIDARG, Exit); + IF_TRUE_ACTION_JUMP( ((NULL != pbyData) && (0 == cbDataSize)), hr = E_INVALIDARG, Exit); + + if (cbDataSize == sizeof(APOInitSystemEffects2)) + { + // + // Initialize for mode-specific signal processing + // + APOInitSystemEffects2* papoSysFxInit2 = (APOInitSystemEffects2*)pbyData; + + // Save reference to the effects property store. This saves effects settings + // and is the communication medium between this APO and any associated UI. + m_spAPOSystemEffectsProperties = papoSysFxInit2->pAPOSystemEffectsProperties; + + // Windows should pass a valid collection. + ATLASSERT(papoSysFxInit2->pDeviceCollection != nullptr); + IF_TRUE_ACTION_JUMP(papoSysFxInit2->pDeviceCollection == nullptr, hr = E_INVALIDARG, Exit); + + // Get the IDeviceTopology and IConnector interfaces to communicate with this + // APO's counterpart audio driver. This can be used for any proprietary + // communication. + hr = papoSysFxInit2->pDeviceCollection->Item(papoSysFxInit2->nSoftwareIoDeviceInCollection, &spMyDevice); + IF_FAILED_JUMP(hr, Exit); + + hr = spMyDevice->Activate(__uuidof(IDeviceTopology), CLSCTX_ALL, NULL, (void**)&spMyDeviceTopology); + IF_FAILED_JUMP(hr, Exit); + + hr = spMyDeviceTopology->GetConnector(papoSysFxInit2->nSoftwareIoConnectorIndex, &spMyConnector); + IF_FAILED_JUMP(hr, Exit); + + // Save the processing mode being initialized. + processingMode = papoSysFxInit2->AudioProcessingMode; + + } + else if (cbDataSize == sizeof(APOInitSystemEffects)) + { + // + // Initialize for default signal processing + // + APOInitSystemEffects* papoSysFxInit = (APOInitSystemEffects*)pbyData; + + // Save reference to the effects property store. This saves effects settings + // and is the communication medium between this APO and any associated UI. + m_spAPOSystemEffectsProperties = papoSysFxInit->pAPOSystemEffectsProperties; + + // Assume default processing mode + processingMode = AUDIO_SIGNALPROCESSINGMODE_DEFAULT; + } + else + { + // Invalid initialization size + hr = E_INVALIDARG; + goto Exit; + } + + // Validate then save the processing mode. Note an endpoint effects APO + // does not depend on the mode. Windows sets the APOInitSystemEffects2 + // AudioProcessingMode member to GUID_NULL for an endpoint effects APO. + IF_TRUE_ACTION_JUMP((processingMode != AUDIO_SIGNALPROCESSINGMODE_DEFAULT && + processingMode != AUDIO_SIGNALPROCESSINGMODE_RAW && + processingMode != AUDIO_SIGNALPROCESSINGMODE_COMMUNICATIONS && + processingMode != AUDIO_SIGNALPROCESSINGMODE_SPEECH && + processingMode != AUDIO_SIGNALPROCESSINGMODE_MEDIA && + processingMode != AUDIO_SIGNALPROCESSINGMODE_MOVIE), hr = E_INVALIDARG, Exit); + m_AudioProcessingMode = processingMode; + + // + // An APO that implements signal processing more complex than this sample + // would configure its processing for the processingMode determined above. + // If necessary, the APO would also use the IDeviceTopology and IConnector + // interfaces retrieved above to communicate with its counterpart audio + // driver to configure any additional signal processing in the driver and + // associated hardware. + // + + // + // Get the current values + // + if (m_spAPOSystemEffectsProperties != NULL) + { + m_fEnableSwapSFX = GetCurrentEffectsSetting(m_spAPOSystemEffectsProperties, PKEY_Endpoint_Enable_Channel_Swap_SFX, m_AudioProcessingMode); + m_fEnableDelaySFX = GetCurrentEffectsSetting(m_spAPOSystemEffectsProperties, PKEY_Endpoint_Enable_Delay_SFX, m_AudioProcessingMode); + } + + // + // Register for notification of registry updates + // + hr = m_spEnumerator.CoCreateInstance(__uuidof(MMDeviceEnumerator)); + IF_FAILED_JUMP(hr, Exit); + + hr = m_spEnumerator->RegisterEndpointNotificationCallback(this); + IF_FAILED_JUMP(hr, Exit); + + m_bIsInitialized = true; + + +Exit: + return hr; +} + +//------------------------------------------------------------------------- +// +// GetEffectsList +// +// Retrieves the list of signal processing effects currently active and +// stores an event to be signaled if the list changes. +// +// Parameters +// +// ppEffectsIds - returns a pointer to a list of GUIDs each identifying a +// class of effect. The caller is responsible for freeing this memory by +// calling CoTaskMemFree. +// +// pcEffects - returns a count of GUIDs in the list. +// +// Event - passes an event handle. The APO signals this event when the list +// of effects changes from the list returned from this function. The APO +// uses this event until either this function is called again or the APO +// is destroyed. The passed handle may be NULL. In this case, the APO +// stops using any previous handle and does not signal an event. +// +// Remarks +// +// An APO imlements this method to allow Windows to discover the current +// effects applied by the APO. The list of effects may depend on what signal +// processing mode the APO initialized (see AudioProcessingMode in the +// APOInitSystemEffects2 structure) as well as any end user configuration. +// +// If there are no effects then the function still succeeds, ppEffectsIds +// returns a NULL pointer, and pcEffects returns a count of 0. +// +STDMETHODIMP CSwapAPOSFX::GetEffectsList(_Outptr_result_buffer_maybenull_(*pcEffects) LPGUID *ppEffectsIds, _Out_ UINT *pcEffects, _In_ HANDLE Event) +{ + HRESULT hr; + BOOL effectsLocked = FALSE; + UINT cEffects = 0; + + IF_TRUE_ACTION_JUMP(ppEffectsIds == NULL, hr = E_POINTER, Exit); + IF_TRUE_ACTION_JUMP(pcEffects == NULL, hr = E_POINTER, Exit); + + // Synchronize access to the effects list and effects changed event + m_EffectsLock.Enter(); + effectsLocked = TRUE; + + // Always close existing effects change event handle + if (m_hEffectsChangedEvent != NULL) + { + CloseHandle(m_hEffectsChangedEvent); + m_hEffectsChangedEvent = NULL; + } + + // If an event handle was specified, save it here (duplicated to control lifetime) + if (Event != NULL) + { + if (!DuplicateHandle(GetCurrentProcess(), Event, GetCurrentProcess(), &m_hEffectsChangedEvent, EVENT_MODIFY_STATE, FALSE, 0)) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + goto Exit; + } + } + + // naked scope to force the initialization of list[] to be after we enter the critical section + { + struct EffectControl + { + GUID effect; + BOOL control; + }; + + EffectControl list[] = + { + { SwapEffectId, m_fEnableSwapSFX }, + { DelayEffectId, m_fEnableDelaySFX }, + }; + + if (!IsEqualGUID(m_AudioProcessingMode, AUDIO_SIGNALPROCESSINGMODE_RAW)) + { + // count the active effects + for (UINT i = 0; i < ARRAYSIZE(list); i++) + { + if (list[i].control) + { + cEffects++; + } + } + } + + if (0 == cEffects) + { + *ppEffectsIds = NULL; + *pcEffects = 0; + } + else + { + GUID *pEffectsIds = (LPGUID)CoTaskMemAlloc(sizeof(GUID) * cEffects); + if (pEffectsIds == nullptr) + { + hr = E_OUTOFMEMORY; + goto Exit; + } + + // pick up the active effects + UINT j = 0; + for (UINT i = 0; i < ARRAYSIZE(list); i++) + { + if (list[i].control) + { + pEffectsIds[j++] = list[i].effect; + } + } + + *ppEffectsIds = pEffectsIds; + *pcEffects = cEffects; + } + + hr = S_OK; + } + +Exit: + if (effectsLocked) + { + m_EffectsLock.Leave(); + } + return hr; +} + +//------------------------------------------------------------------------- +// Description: +// +// +// +// Parameters: +// +// +// +// Return values: +// +// +// +// Remarks: +// +// +HRESULT CSwapAPOSFX::OnPropertyValueChanged(LPCWSTR pwstrDeviceId, const PROPERTYKEY key) +{ + HRESULT hr = S_OK; + + UNREFERENCED_PARAMETER(pwstrDeviceId); + + if (!m_spAPOSystemEffectsProperties) + { + return hr; + } + + // If either the master disable or our APO's enable properties changed... + if (PK_EQUAL(key, PKEY_Endpoint_Enable_Channel_Swap_SFX) || + PK_EQUAL(key, PKEY_Endpoint_Enable_Delay_SFX) || + PK_EQUAL(key, PKEY_AudioEndpoint_Disable_SysFx)) + { + LONG nChanges = 0; + + // Synchronize access to the effects list and effects changed event + m_EffectsLock.Enter(); + + struct KeyControl + { + PROPERTYKEY key; + LONG *value; + }; + + KeyControl controls[] = + { + { PKEY_Endpoint_Enable_Channel_Swap_SFX, &m_fEnableSwapSFX }, + { PKEY_Endpoint_Enable_Delay_SFX, &m_fEnableDelaySFX }, + }; + + for (int i = 0; i < ARRAYSIZE(controls); i++) + { + LONG fOldValue; + LONG fNewValue = true; + + // Get the state of whether channel swap MFX is enabled or not + fNewValue = GetCurrentEffectsSetting(m_spAPOSystemEffectsProperties, controls[i].key, m_AudioProcessingMode); + + // Swap in the new setting + fOldValue = InterlockedExchange(controls[i].value, fNewValue); + + if (fNewValue != fOldValue) + { + nChanges++; + } + } + + // If anything changed and a change event handle exists + if ((nChanges > 0) && (m_hEffectsChangedEvent != NULL)) + { + SetEvent(m_hEffectsChangedEvent); + } + + m_EffectsLock.Leave(); + } + + return hr; +} + + +//------------------------------------------------------------------------- +// Description: +// +// Destructor. +// +// Parameters: +// +// void +// +// Return values: +// +// void +// +// Remarks: +// +// This method deletes whatever was allocated. +// +// This method may not be called from a real-time processing thread. +// +CSwapAPOSFX::~CSwapAPOSFX(void) +{ + // + // unregister for callbacks + // + if (m_bIsInitialized) + { + m_spEnumerator->UnregisterEndpointNotificationCallback(this); + } + + if (m_hEffectsChangedEvent != NULL) + { + CloseHandle(m_hEffectsChangedEvent); + } +} // ~CSwapAPOSFX diff --git a/audio/sysvad/SwapAPO/Inc/CommonMacros.h b/audio/sysvad/SwapAPO/Inc/CommonMacros.h new file mode 100644 index 000000000..097c6d473 --- /dev/null +++ b/audio/sysvad/SwapAPO/Inc/CommonMacros.h @@ -0,0 +1,337 @@ +//**@@@*@@@**************************************************** +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation. All rights reserved. +// +//**@@@*@@@**************************************************** + +// +// FileName: CommonMacros.h +// +// Abstract: Useful macros +// +// ---------------------------------------------------------------------------- + + +#pragma once +#include +#include + + +//------------------------------------------------------------------------- +// Description: +// +// If the condition evaluates to TRUE, jump to the given label. +// +// Parameters: +// +// condition - [in] code that fits in if statement +// label - [in] label to jump if condition is met +// +#define IF_TRUE_JUMP(condition, label) \ + if (condition) \ + { \ + goto label; \ + } + +//------------------------------------------------------------------------- +// Description: +// +// If the condition evaluates to FALSE, jump to the given label. +// +// Parameters: +// +// condition - [in] code that fits in if statement +// label - [in] label to jump if condition is met +// +#define IF_FALSE_JUMP(condition, label) \ + if (!condition) \ + { \ + goto label; \ + } + +//------------------------------------------------------------------------- +// Description: +// +// If the hresult passed FAILED, jump to the given label. +// +// Parameters: +// +// _hresult - [in] Value to check +// label - [in] label to jump if condition is met +// +#define IF_FAILED_JUMP(_hresult, label) \ + if (FAILED(_hresult)) \ + { \ + goto label; \ + } + +//------------------------------------------------------------------------- +// Description: +// +// If the hresult passed SUCCEEDED, jump to the given label. +// +// Parameters: +// +// _hresult - [in] Value to check +// label - [in] label to jump if condition is met +// +#define IF_SUCCEEDED_JUMP(_hresult, label) \ + if (SUCCEEDED(_hresult)) \ + { \ + goto label; \ + } + +//------------------------------------------------------------------------- +// Description: +// +// If the condition evaluates to TRUE, perform the given statement +// then jump to the given label. +// +// Parameters: +// +// condition - [in] Code that fits in if statement +// action - [in] action to perform in body of if statement +// label - [in] label to jump if condition is met +// +#define IF_TRUE_ACTION_JUMP(condition, action, label) \ + if (condition) \ + { \ + action; \ + goto label; \ + } + +//------------------------------------------------------------------------- +// Description: +// +// If the hresult FAILED, perform the given statement then jump to +// the given label. +// +// Parameters: +// +// _hresult - [in] Value to check +// action - [in] action to perform in body of if statement +// label - [in] label to jump if condition is met +// +#define IF_FAILED_ACTION_JUMP(_hresult, action, label) \ + if (FAILED(_hresult)) \ + { \ + action; \ + goto label; \ + } + +//------------------------------------------------------------------------- +// Description: +// +// Closes a handle and assigns NULL. +// +// Parameters: +// +// h - [in] handle to close +// +#define SAFE_CLOSE_HANDLE(h) \ + if (NULL != h) \ + { \ + CloseHandle(h); \ + h = NULL; \ + } + +//------------------------------------------------------------------------- +// Description: +// +// Addref an interface pointer +// +// Parameters: +// +// p - [in] object to addref +// +#define SAFE_ADDREF(p) \ + if (NULL != p) \ + { \ + (p)->AddRef();; \ + } + +//------------------------------------------------------------------------- +// Description: +// +// Releases an interface pointer and assigns NULL. +// +// Parameters: +// +// p - [in] object to release +// +#define SAFE_RELEASE(p) \ + if (NULL != p) \ + { \ + (p)->Release(); \ + (p) = NULL; \ + } + +//------------------------------------------------------------------------- +// Description: +// +// Deletes a pointer and assigns NULL. Do not check for NULL because +// the default delete operator checks for it. +// +// Parameters: +// +// p - [in] object to delete +// +#define SAFE_DELETE(p) \ + delete p; \ + p = NULL; + +//------------------------------------------------------------------------- +// Description: +// +// Deletes an array pointer and assigns NULL. Do not check for NULL because +// the default delete operator checks for it. +// +// Parameters: +// +// p - [in] Array to delete +// +#define SAFE_DELETE_ARRAY(p) \ + delete [] p; \ + p = NULL; + +//------------------------------------------------------------------------- +// Description: +// +// Frees a block of memory allocated by CoTaskMemAlloc and assigns NULL to +// the pointer +// +// Parameters: +// +// p - [in] Pointer to memory to free +// +#define SAFE_COTASKMEMFREE(p) \ + if (NULL != p) \ + { \ + CoTaskMemFree(p); \ + (p) = NULL; \ + } + +//------------------------------------------------------------------------- +// Description: +// +// Frees a DLL loaded with LoadLibrary and assigns NULL to the handle +// +// Parameters: +// +// h - [in] Handle to DLL to free +// +#define SAFE_FREELIBRARY(h) \ + if (NULL != h) \ + { \ + FreeLibrary(h); \ + (h) = NULL; \ + } + +//------------------------------------------------------------------------- +// Description: +// +// Used to validate a read pointer +// +// Parameters: +// +// p - [in] read pointer. +// s - [in] size of memory in bytes pointed to by p. +// +#define IS_VALID_READ_POINTER(p, s) ((NULL != p) || (0 == s)) + +//------------------------------------------------------------------------- +// Description: +// +// Used to validate a write pointer +// +// Parameters: +// +// p - [in] write pointer. +// s - [in] size of memory in bytes pointed to by p. +// +#define IS_VALID_WRITE_POINTER(p, s) ((NULL != p) || (0 == s)) + +//------------------------------------------------------------------------- +// Description: +// +// Used to validate a read pointer of a particular type +// +// Parameters: +// +// p - [in] typed read pointer +// +#define IS_VALID_TYPED_READ_POINTER(p) IS_VALID_READ_POINTER((p), sizeof *(p)) + +//------------------------------------------------------------------------- +// Description: +// +// Used to validate a write pointer of a particular type +// +// Parameters: +// +// p - [in] typed write pointer +// +#define IS_VALID_TYPED_WRITE_POINTER(p) IS_VALID_WRITE_POINTER((p), sizeof *(p)) + +// --------------------------------------------------------------------------- +// Macros that wrap windows messages. Similar to those in windowsX.h and +// commctrl.h +// +#if !defined Static_SetIcon +#define Static_SetIcon(hwnd, hi) \ + (BOOL)SNDMSG((hwnd), STM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)(hi)) +#endif + +#define TrackBar_SetTickFrequency(hwnd, f) \ + (BOOL)SNDMSG((hwnd), TBM_SETTICFREQ, (WPARAM)(f), 0) + +#define TrackBar_SetBuddy(hwnd, f, hbud) \ + (HWND)SNDMSG((hwnd), TBM_SETBUDDY, (WPARAM)(f), (LPARAM)hbud) + +#define TrackBar_GetPos(hwnd) \ + (int)SNDMSG((hwnd), TBM_GETPOS, 0, 0) + +#define TrackBar_SetPos(hwnd, pos) \ + SNDMSG((hwnd), TBM_SETPOS, (WPARAM)TRUE, (LPARAM)pos) + +#define TrackBar_SetRange(hwnd, min, max) \ + SNDMSG((hwnd), TBM_SETRANGE , (WPARAM)TRUE, (LPARAM) MAKELONG(min, max)) + +#define TrackBar_SetThumbLength(hwnd, l) \ + SNDMSG((hwnd), TBM_SETTHUMBLENGTH, (WPARAM)l, 0); + +#define TrackBar_SetPageSize(hwnd, n) \ + SNDMSG((hwnd), TBM_SETPAGESIZE, 0, (LPARAM)n) + +#define Window_GetFont(hwnd) \ + (HFONT)SNDMSG((hwnd), WM_GETFONT, 0, 0) + +#define Window_SetFont(hwnd, font) \ + SNDMSG((hwnd), WM_SETFONT, (WPARAM)font, FALSE) + + +// ---------------------------------------------------------------------- +// A struct for holding a rect in easier terms than a RECT struct +// +struct SRECT +{ + int x, y, w, h; + SRECT() + { + x = y = w = h = 0; + } + SRECT(int X, int Y, int W, int H) + { + x = X; y = Y; w = W; h = H; + } + SRECT(RECT* prc) + { + x = prc->left; + y = prc->top; + w = prc->right - prc->left; + h = prc->bottom - prc->top; + } +}; + +#define HNS_PER_SECOND (10ull * 1000ull * 1000ull) diff --git a/audio/sysvad/SwapAPO/Inc/CustomPropKeys.h b/audio/sysvad/SwapAPO/Inc/CustomPropKeys.h new file mode 100644 index 000000000..dab286a53 --- /dev/null +++ b/audio/sysvad/SwapAPO/Inc/CustomPropKeys.h @@ -0,0 +1,39 @@ +// Microsoft Windows +// Copyright (C) Microsoft Corporation. All rights reserved. +// +#pragma once + +// header files for imported files +#include "propidl.h" + +#ifdef DEFINE_PROPERTYKEY +#undef DEFINE_PROPERTYKEY +#endif + +#ifdef INITGUID +#define DEFINE_PROPERTYKEY(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8, pid) EXTERN_C const PROPERTYKEY name = { { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } }, pid } +#else +#define DEFINE_PROPERTYKEY(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8, pid) EXTERN_C const PROPERTYKEY name +#endif // INITGUID + +// ---------------------------------------------------------------------- +// +// PKEY_Endpoint_Enable_Channel_Swap_SFX: When value is 0x00000001, Channel Swap local effect is enabled +// {A44531EF-5377-4944-AE15-53789A9629C7},2 +// vartype = VT_UI4 +DEFINE_PROPERTYKEY(PKEY_Endpoint_Enable_Channel_Swap_SFX, 0xa44531ef, 0x5377, 0x4944, 0xae, 0x15, 0x53, 0x78, 0x9a, 0x96, 0x29, 0xc7, 2); + +// PKEY_Endpoint_Enable_Channel_Swap_MFX: When value is 0x00000001, Channel Swap global effect is enabled +// {A44531EF-5377-4944-AE15-53789A9629C7},3 +// vartype = VT_UI4 +DEFINE_PROPERTYKEY(PKEY_Endpoint_Enable_Channel_Swap_MFX, 0xa44531ef, 0x5377, 0x4944, 0xae, 0x15, 0x53, 0x78, 0x9a, 0x96, 0x29, 0xc7, 3); + +// PKEY_Endpoint_Enable_Delay_SFX: When value is 0x00000001, Delay local effect is enabled +// {A44531EF-5377-4944-AE15-53789A9629C7},4 +// vartype = VT_UI4 +DEFINE_PROPERTYKEY(PKEY_Endpoint_Enable_Delay_SFX, 0xa44531ef, 0x5377, 0x4944, 0xae, 0x15, 0x53, 0x78, 0x9a, 0x96, 0x29, 0xc7, 4); + +// PKEY_Endpoint_Enable_Delay_MFX: When value is 0x00000001, Delay global effect is enabled +// {A44531EF-5377-4944-AE15-53789A9629C7},5 +// vartype = VT_UI4 +DEFINE_PROPERTYKEY(PKEY_Endpoint_Enable_Delay_MFX, 0xa44531ef, 0x5377, 0x4944, 0xae, 0x15, 0x53, 0x78, 0x9a, 0x96, 0x29, 0xc7, 5); diff --git a/audio/sysvad/SwapAPO/Inc/tlist.h b/audio/sysvad/SwapAPO/Inc/tlist.h new file mode 100644 index 000000000..6c0479aaa --- /dev/null +++ b/audio/sysvad/SwapAPO/Inc/tlist.h @@ -0,0 +1,476 @@ +//**@@@*@@@**************************************************** +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation. All rights reserved. +// +//**@@@*@@@**************************************************** + +// +// FileName: tlist.h +// +// Abstract: This is the header and implementation file for a template +// list class +// +// ---------------------------------------------------------------------------- + +#pragma once + + +typedef void* LISTPOS; + +template +class TItem +{ +public: + TItem() throw(); + ~TItem() throw(); + TItem * pNext; + TItem * pPrev; + T* pData; +}; + +template +TItem::TItem() : + pNext(NULL), + pPrev(NULL), + pData(NULL) +{ + return; +} + +template +TItem::~TItem() +{ + return; +} + + +template +class TList +{ + public: + TList() throw(); + ~TList() throw(); + + BOOL GetHead( T** ppHead ) const throw(); + LISTPOS GetHeadPosition() const throw(); + LISTPOS GetTailPosition() const throw(); + LISTPOS AddHead(__drv_aliasesMem T* pNewHead) throw(); + LISTPOS AddTail(__drv_aliasesMem T* pNewTail) throw(); + BOOL RemoveTail(T** ppOldTail) throw(); + BOOL RemoveHead(T** ppOldHead) throw(); + void RemoveAll() throw(); + LISTPOS Find( T* pSearchValue ) const throw(); + void RemoveAt(LISTPOS pos) throw(); + BOOL GetNext(LISTPOS& rPos, T**ppThisData) const throw(); + BOOL IsEmpty() const throw(); + DWORD GetCount() const throw(); + HRESULT Initialize(DWORD dwValue) throw(); + BOOL GetAt( LONG index, T** ppElement) const throw(); + BOOL GetAt( LISTPOS pos, T** ppElement) const throw(); + LISTPOS InsertBefore(LISTPOS pos, T* pNewElement) throw(); + LISTPOS InsertAfter(LISTPOS pos, T* pNewElement) throw(); + void MoveHeadList(TList *pFromList) throw(); +private: + TItem* m_pHead; + TItem* m_pTail; + LONG m_lCount; +}; + +template +TList::TList() : + m_pHead(NULL), + m_pTail(NULL), + m_lCount(0) +{ + return; +} + +template +TList::~TList() +{ + ATLASSERT(NULL == m_pHead); + ATLASSERT(NULL == m_pTail); + ATLASSERT(0 == m_lCount); + return; +} + +template +BOOL TList::GetHead(T** ppHead) const +{ + if (!m_pHead) + { + *ppHead = NULL; + return FALSE; + } + + *ppHead = m_pHead->pData; + return TRUE; +} + +template +LISTPOS TList::GetHeadPosition() const +{ + return reinterpret_cast(m_pHead); +} + +template +LISTPOS TList::GetTailPosition() const +{ + return reinterpret_cast(m_pTail); +} + +template +LISTPOS TList::AddTail(__drv_aliasesMem T* pNewTail) +{ + TItem* pNewItem; + + pNewItem = new TItem; + if (NULL == pNewItem) + { + return( reinterpret_cast( NULL )); + } + + pNewItem->pData = pNewTail; + pNewItem->pPrev = m_pTail; + pNewItem->pNext = NULL; + + if (NULL == m_pTail) + { + m_pHead = pNewItem; + } + else + { + m_pTail->pNext = pNewItem; + } + + m_pTail = pNewItem; + m_lCount++; + + return( reinterpret_cast( pNewItem ) ); +} + +template +LISTPOS TList::AddHead(__drv_aliasesMem T* pNewHead) +{ + TItem *pNewItem; + pNewItem = new TItem; + if ( !pNewItem ) + { + return( reinterpret_cast( NULL ) ); + } + + pNewItem->pData = pNewHead; + pNewItem->pNext = m_pHead; + pNewItem->pPrev = NULL; + + if ( NULL != m_pHead ) + { + m_pHead->pPrev = pNewItem; + } + else + { + m_pTail = pNewItem; + } + + m_pHead = pNewItem; + m_lCount++; + + return( reinterpret_cast( pNewItem ) ); +} + +template +void TList::MoveHeadList(TList *pFromList) +{ + // Link head item of this list to tail item of other list + if (NULL != m_pHead) + m_pHead->pPrev = pFromList->m_pTail; + if (NULL != pFromList->m_pTail) + pFromList->m_pTail->pNext = m_pHead; + + // Adjust this list's head and tail pointers + if (pFromList->m_pHead) + m_pHead = pFromList->m_pHead; + if (NULL == m_pTail) + m_pTail = pFromList->m_pTail; + + // Adjust this list's count + m_lCount += pFromList->m_lCount; + + // Reset other list's head and tail pointers and count + pFromList->m_pHead = NULL; + pFromList->m_pTail = NULL; + pFromList->m_lCount = 0; +} + +template +BOOL TList::RemoveTail( T** ppOldTail) +{ + + if (!ppOldTail || !m_pTail) + { + return FALSE; + } + + TItem *pOldItem = m_pTail; + *ppOldTail = pOldItem->pData; + + m_pTail = pOldItem->pPrev; + + if ( NULL != m_pTail ) + { + m_pTail->pNext = NULL; + } + else + { + m_pHead = NULL; + } + + delete pOldItem; + m_lCount--; + ATLASSERT(m_lCount >= 0); + + return( TRUE ); +} + +template +BOOL TList::RemoveHead(T** ppOldHead) +{ + if ( !ppOldHead || !m_pHead ) + { + return( FALSE ); + } + + TItem *pOldItem = m_pHead; + *ppOldHead = pOldItem->pData; + + m_pHead = pOldItem->pNext; + + if ( NULL != m_pHead ) + { + m_pHead->pPrev = NULL; + } + else + { + m_pTail = NULL; + } + + delete pOldItem; + m_lCount--; + ATLASSERT(m_lCount >= 0); + + return( TRUE ); +} + +template +void TList::RemoveAll() +{ + TItem *pNext; + + while( NULL != m_pHead ) + { + pNext = m_pHead->pNext; + delete m_pHead; + m_pHead = pNext; + } + + m_lCount = 0; + m_pTail = NULL; + + return; +} + +template +LISTPOS TList::Find( T* pSearchValue ) const +{ + TItem *pItem = m_pHead; + + for ( ; NULL != pItem; pItem = pItem->pNext ) + { + if ( pItem->pData == pSearchValue ) + { + return( reinterpret_cast(pItem) ); + } + } + + return( reinterpret_cast(NULL) ); +} + +template +void TList::RemoveAt(LISTPOS pos) +{ + // ATLASSERT( NULL != pos ) + TItem *pOldItem = reinterpret_cast< TItem* >(pos); + + // remove pOldItem from list + if ( pOldItem == m_pHead ) + { + m_pHead = pOldItem->pNext; + } + else + { + ATLASSERT( pOldItem->pPrev ); + pOldItem->pPrev->pNext = pOldItem->pNext; + } + + if (pOldItem == m_pTail) + { + m_pTail = pOldItem->pPrev; + } + else + { + ATLASSERT( pOldItem->pNext ); + pOldItem->pNext->pPrev = pOldItem->pPrev; + } + + delete pOldItem; + m_lCount--; + ATLASSERT(m_lCount >= 0); + return; +} + +template +BOOL TList::GetNext(LISTPOS& rPos, T**ppThisData) const +{ + TItem *pItem = reinterpret_cast< TItem* >(rPos); + + if ( !pItem ) + { + return( FALSE ); + } + + rPos = reinterpret_cast(pItem->pNext); + if (ppThisData) + { + *ppThisData = pItem->pData; + } + + return( TRUE ); +} + +template +BOOL TList::IsEmpty() const +{ + return (0 == m_lCount); +} + +template +HRESULT TList::Initialize(DWORD dwValue) +{ + HRESULT hr = S_OK; + return hr; +} + +template +DWORD TList::GetCount() const +{ + return static_cast(m_lCount); +} + + +template +LISTPOS TList::InsertBefore(LISTPOS pos, T* pNewElement) +{ + TItem* pOldItem = reinterpret_cast*>(pos); + TItem* pNewItem; + + if ((pOldItem == NULL) || (pOldItem->pPrev == NULL)) + { + return (AddHead(pNewElement)); // insert before nothing -> head of the list + } + + // Insert it before pos + pNewItem = new TItem; + if (!pNewItem) + { + return (reinterpret_cast(NULL)); + } + + pNewItem->pData = pNewElement; + pNewItem->pPrev = pOldItem->pPrev; + pNewItem->pNext = pOldItem; + + pOldItem->pPrev->pNext = pNewItem; + pOldItem->pPrev = pNewItem; + + m_lCount++; + + return (reinterpret_cast(pNewItem)); +} + +template +LISTPOS TList::InsertAfter(LISTPOS pos, T* pNewElement) +{ + TItem* pOldItem = reinterpret_cast*>(pos); + TItem* pNewItem; + + if ((pOldItem == NULL) || (pOldItem->pNext == NULL)) + { + return (AddTail(pNewElement)); // insert after nothing -> tail of the list + } + + // Insert it after pos + pNewItem = new TItem; + if (!pNewItem) + { + return (reinterpret_cast(NULL)); + } + + pNewItem->pData = pNewElement; + pNewItem->pPrev = pOldItem; + pNewItem->pNext = pOldItem->pNext; + + pOldItem->pNext->pPrev = pNewItem; + pOldItem->pNext = pNewItem; + + m_lCount++; + + return (reinterpret_cast(pNewItem)); +} + +template +BOOL TList::GetAt( LONG index, T** ppElement) const +{ + if (ppElement == NULL) + { + return false; + } + + *ppElement = NULL; + + if ((index < 0) || (index > (m_lCount - 1))) + { + return false; + } + + T* pElement = NULL; + BOOL bRes = TRUE; + LISTPOS pos = GetHeadPosition(); + + for (int i = 0; bRes && (i <= index); i++) + { + bRes = GetNext(pos, &pElement); + } + + if (bRes) + { + *ppElement = pElement; + } + + return bRes; +} + +template +BOOL TList::GetAt(LISTPOS pos, T**ppThisData) const +{ + TItem *pItem = reinterpret_cast< TItem* >(pos); + + if ( !pItem ) + { + return( FALSE ); + } + + *ppThisData = pItem->pData; + + return( TRUE ); +} diff --git a/audio/sysvad/SwapAPO/PropPageExtensions/AdvEndpointPropPage.cpp b/audio/sysvad/SwapAPO/PropPageExtensions/AdvEndpointPropPage.cpp new file mode 100644 index 000000000..73c243b44 --- /dev/null +++ b/audio/sysvad/SwapAPO/PropPageExtensions/AdvEndpointPropPage.cpp @@ -0,0 +1,1087 @@ +//**@@@*@@@**************************************************** +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation. All rights reserved. +// +//**@@@*@@@**************************************************** + +// +// FileName: AdvEndpointPropPage.cpp +// +// Abstract: Implementation of CAdvEndpointPropPage +// +// ---------------------------------------------------------------------------- + + +#include "stdafx.h" +#include +#include +#include "UIWidgets.h" +#include "Parts.h" +#include "TopologyExaminers.h" +#include "AdvEndpointPropPage.h" +#include + +_Analysis_mode_(_Analysis_code_type_user_driver_) + +#define MAX_SEARCH_DEPTH 5 // when searching for a host pin or interface + +static IID s_rgAdvCtrlInterfaces[] = +{ + __uuidof(IAudioLoudness), + __uuidof(IAudioAutoGainControl), + __uuidof(IDeviceSpecificProperty) +}; + + +// ---------------------------------------------------------------------------- +// Function: +// CAdvEndpointPropPage::CAdvEndpointPropPage +// +// Description: +// CAdvEndpointPropPage constructor +// ---------------------------------------------------------------------------- +CAdvEndpointPropPage::CAdvEndpointPropPage() +: m_pAudioExtParams(NULL) +{ +} + + +// ---------------------------------------------------------------------------- +// Function: +// CAdvEndpointPropPage::~CAdvEndpointPropPage +// +// Description: +// CAdvEndpointPropPage destructor +// ---------------------------------------------------------------------------- +CAdvEndpointPropPage::~CAdvEndpointPropPage() +{ + CUIWidget* pWidget; + + while (m_lstWidgets.RemoveHead(&pWidget)) + { + SAFE_DELETE(pWidget); + } + + SAFE_RELEASE(m_pAudioExtParams->pEndpoint); + SAFE_RELEASE(m_pAudioExtParams->pPnpInterface); + SAFE_RELEASE(m_pAudioExtParams->pPnpDevnode); + + SAFE_DELETE(m_pAudioExtParams); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CAdvEndpointPropPage::MakeNewWidget +// +// Description: +// Adds a widget to the list +// +// Parameters: +// pPart - [in] Part interface +// iid - [in] Advanced control IID which controls the kind of widget +// that gets created +// nCtrlId - [in] ID of the control +// +// Return: +// S_OK if successful +// ---------------------------------------------------------------------------- +HRESULT CAdvEndpointPropPage::MakeNewWidget +( + IPart* pPart, + REFIID iid, + UINT nCtrlId +) +{ + UNREFERENCED_PARAMETER(nCtrlId); + + HRESULT hr = S_OK; + LISTPOS posChk; + CUIWidget* pNewWidget = NULL; + + if ((iid == __uuidof(IAudioLoudness)) || + (iid == __uuidof(IAudioAutoGainControl))) + { + pNewWidget = new CBooleanWidget(pPart, iid); + IF_TRUE_ACTION_JUMP((pNewWidget == NULL), hr = E_OUTOFMEMORY, Exit); + } + else if (iid == __uuidof(IDeviceSpecificProperty)) + { + CComPtr spDevSpec; + VARTYPE vt; + + hr = pPart->Activate(CLSCTX_INPROC_SERVER, __uuidof(IDeviceSpecificProperty), (void**)&spDevSpec); + IF_FAILED_JUMP(hr, Exit); + + hr = spDevSpec->GetType(&vt); + IF_FAILED_JUMP(hr, Exit); + + if (vt == VT_BOOL) + { + pNewWidget = new CBooleanWidget(pPart, iid); + IF_TRUE_ACTION_JUMP((pNewWidget == NULL), hr = E_OUTOFMEMORY, Exit); + } + else if (vt == VT_I4) + { + pNewWidget = new CLongWidget(pPart); + IF_TRUE_ACTION_JUMP((pNewWidget == NULL), hr = E_OUTOFMEMORY, Exit); + } + else if (vt == VT_UI4) + { + pNewWidget = new CLongWidget(pPart); + IF_TRUE_ACTION_JUMP((pNewWidget == NULL), hr = E_OUTOFMEMORY, Exit); + } + else + { + hr = HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + goto Exit; + } + } + + ATLASSERT(pNewWidget); + + posChk = m_lstWidgets.AddTail(pNewWidget); + if (posChk == NULL) + { + hr = E_OUTOFMEMORY; + SAFE_DELETE(pNewWidget); + } + +Exit: + return hr; +} + + +// ---------------------------------------------------------------------------- +// Function: +// CAdvEndpointPropPage::GetDeviceFriendlyName +// +// Description: +// Retrieves the endpoint's friendly name +// +// Parameters: +// ppNameOut - [out] The friendly name of the endpoint +// +// Return values: +// S_OK if successful +// ---------------------------------------------------------------------------- +HRESULT CAdvEndpointPropPage::GetDeviceFriendlyName +( + _Outptr_result_maybenull_ LPWSTR* ppNameOut +) +{ + HRESULT hr = S_OK; + CComPtr spProperties; + PROPVARIANT var; + + IF_TRUE_ACTION_JUMP((m_pAudioExtParams == NULL), hr = E_POINTER, Exit); + IF_TRUE_ACTION_JUMP((ppNameOut == NULL), hr = E_POINTER, Exit); + + *ppNameOut = NULL; + + PropVariantInit(&var); + + if (m_pAudioExtParams->pEndpoint != NULL) + { + // Open the PropertyStore for read access + hr = m_pAudioExtParams->pEndpoint->OpenPropertyStore(STGM_READ, &spProperties); + IF_FAILED_JUMP(hr, Exit); + + // Retrieve the friendly name of the endpoint + hr = spProperties->GetValue(PKEY_Device_FriendlyName, &var); + if (SUCCEEDED(hr) && (var.vt == VT_LPWSTR)) + { + *ppNameOut = var.pwszVal; + } + else + { + PropVariantClear(&var); + } + } + +Exit: + return(hr); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CAdvEndpointPropPage::OnInitDialog +// +// Description: +// Dialog initialization routine +// +// Parameters: +// hwndDlg - [in] Handle to dialog box +// wParam - [in] Handle to control to receive the default keyboard focus +// lParam - [in] Specifies additional message-specific information +// +// Return values: +// TRUE to direct the system to set the keyboard focus to the control +// specified by wParam. Otherwise, it should return FALSE to prevent the +// system from setting the default keyboard focus. +// ---------------------------------------------------------------------------- +BOOL CAdvEndpointPropPage::OnInitDialog +( + HWND hwndDlg, + WPARAM wParam, + LPARAM lParam +) +{ + UNREFERENCED_PARAMETER(wParam); + UNREFERENCED_PARAMETER(lParam); + + LPWSTR pwstrEndpointName = NULL; + + LISTPOS pos; + CUIWidget* pWidget; + SRECT rcWidget(34, 144, 0, 0); + RECT rcDlg; + HRESULT hr = S_OK; + UINT id = 1800; + + // Retrieve the endpoint's friendly name + hr = GetDeviceFriendlyName(&pwstrEndpointName); + IF_FAILED_JUMP(hr, Exit); + + // Update the property page with retrieved information + SetWindowText(GetDlgItem(hwndDlg, IDC_EPP_ENDPOINT_NAME), pwstrEndpointName); + + // Initialize and create widgets + hr = InitializeWidgets(); + if (hr == E_NOTFOUND) + { + // If no widgets were found, show a message to depict that + SetWindowText(GetDlgItem(hwndDlg, IDC_NO_ADV_CONTROLS_FOUND), + L"No advanced controls found."); + } + else + { + // Create widget windows + ::GetClientRect(hwndDlg, &rcDlg); + rcWidget.w = rcDlg.right - rcDlg.left - 28; + rcWidget.h = 14; + + pos = m_lstWidgets.GetHeadPosition(); + while (pos) + { + pWidget = NULL; + m_lstWidgets.GetNext(pos, &pWidget); + + hr = pWidget->Create(hwndDlg, rcWidget, id); + if (SUCCEEDED(hr)) + { + id++; + + // offset to next widget + rcWidget.y = rcWidget.y + rcWidget.h + 12; + } + } + } + +Exit: + SAFE_COTASKMEMFREE(pwstrEndpointName); + return(FALSE); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CAdvEndpointPropPage::OnApply +// +// Description: +// Handle the pressing of the apply button +// +// Parameters: +// hwndDlg - [in] Handle to the dialog box +// wParam - [in] Handle to the control to receive the default keyboard focus +// lParam - [in] Specifies additional message-specific information +// +// Return values: +// TRUE to set keyboard focus on control +// ---------------------------------------------------------------------------- +BOOL CAdvEndpointPropPage::OnApply +( + HWND hwndDlg, + WPARAM wParam, + LPARAM lParam +) +{ + UNREFERENCED_PARAMETER(wParam); + UNREFERENCED_PARAMETER(lParam); + + CUIWidget* pWidget; + LISTPOS pos; + + pos = m_lstWidgets.GetHeadPosition(); + + while (pos) + { + pWidget = NULL; + m_lstWidgets.GetNext(pos, &pWidget); + pWidget->CommitValue(); + } + + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_NOERROR); + + return(TRUE); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CAdvEndpointPropPage::OnCheckBoxClicked +// +// Description: +// Handle the clicking of the check boxes +// +// Parameters: +// hwndDlg - [in] Handle to the dialog box +// wParam - [in] Handle to the control to receive the default keyboard focus +// lParam - [in] Specifies additional message-specific information +// +// Return values: +// FALSE to not set default keyboard focus +// ---------------------------------------------------------------------------- +BOOL CAdvEndpointPropPage::OnCheckBoxClicked +( + HWND hwndDlg, + WPARAM wParam, + LPARAM lParam +) +{ + UNREFERENCED_PARAMETER(lParam); + + CUIWidget* pWidget; + LISTPOS pos; + + pos = m_lstWidgets.GetHeadPosition(); + + while (pos) + { + pWidget = NULL; + m_lstWidgets.GetNext(pos, &pWidget); + + if (pWidget->OwnsCtrlId(LOWORD(wParam))) + { + pWidget->OnClick(); + } + } + + // Enable the Apply button upon user changing the control + SendMessage(GetParent(hwndDlg), PSM_CHANGED, (WPARAM)hwndDlg, 0); + + return(FALSE); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CAdvEndpointPropPage::DialogProcPage1 +// +// Description: +// Callback for property page +// +// Parameters: +// hwndDlg - [in] Handle to the dialog box +// uMsg - [in] Specifies the message +// wParam - [in] Specifies additional message-specific information +// lParam - [in] Specifies additional message-specific information +// +// Return values: +// TRUE if it processed the message, FALSE if not +// ---------------------------------------------------------------------------- +INT_PTR CALLBACK CAdvEndpointPropPage::DialogProcPage1 +( + HWND hwndDlg, + UINT uMsg, + WPARAM wParam, + LPARAM lParam +) +{ + CAdvEndpointPropPage* pthis = (CAdvEndpointPropPage*)(LONG_PTR)GetWindowLongPtr( + hwndDlg, GWLP_USERDATA); + BOOL fRet = FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + // Extract the context data from PROPSHEETPAGE::lParam + PROPSHEETPAGE* pSheetDesc = (PROPSHEETPAGE*)lParam; + + // Create the property page factory class +#pragma warning(push) +#pragma warning(disable: 28197) + pthis = new CComObject(); +#pragma warning(pop) + if (pthis == NULL) + { + return(FALSE); + } + + // Save this object in lParam + SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)pthis); + + // Keep audio extension parameters passed by the control panel + pthis->m_pAudioExtParams = (AudioExtensionParams*)pSheetDesc->lParam; + + fRet = pthis->OnInitDialog(hwndDlg, wParam, lParam); + break; + } + + case WM_NOTIFY: + { + switch (((NMHDR FAR*)lParam)->code) + { + case PSN_APPLY: + if (pthis) + { + // Apply button pressed + fRet = pthis->OnApply(hwndDlg, wParam, lParam); + } + break; + } + break; + } + + case WM_COMMAND: + { + if (pthis) + { + // Check box clicked + fRet = pthis->OnCheckBoxClicked(hwndDlg, wParam, lParam); + } + break; + } + + case WM_DESTROY: + { + SAFE_DELETE(pthis); + SetWindowLongPtr(hwndDlg, GWLP_USERDATA, NULL); + fRet = TRUE; + break; + } + } + + return(fRet); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CAdvEndpointPropPage::PropSheetPageProc +// +// Description: +// Callback that gets invoked right after page creation or right before +// before page destruction +// +// Parameters: +// hwnd - Reserved; must be NULL +// uMsg - [in] Action flag. PSPCB_ADDREF, PSPCB_CREATE, or PSPCB_RELEASE +// ppsp - [in, out] Pointer to a PROPSHEETPAGE structure that defines +// the page being created or destroyed. +// +// Return values: +// Depends on the value of the uMsg parameter +// ---------------------------------------------------------------------------- +UINT CALLBACK CAdvEndpointPropPage::PropSheetPageProc +( + HWND hwnd, + UINT uMsg, + LPPROPSHEETPAGE ppsp +) +{ + UNREFERENCED_PARAMETER(hwnd); + UNREFERENCED_PARAMETER(uMsg); + UNREFERENCED_PARAMETER(ppsp); + + // if (uMsg == PSPCB_CREATE) ... + return(1); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CAdvEndpointPropPage::Initialize +// +// Description: +// Implementation of IShellExtInit::Initialize. Initializes a property +// sheet extension, shortcut menu extension, or drag-and-drop handler. +// +// Parameters: +// pidlFolder - [in] Address of an ITEMIDLIST structure that uniquely +// identifies a folder. For property sheet extensions, +// this parameter is NULL. +// pdtobj - [out] Address of an IDataObject interface object that can be +// used to retrieve the objects being acted upon. +// hkeyProgID - [in] Registry key for the file object or folder type. +// +// Return values: +// Returns NOERROR if successful, or an OLE-defined error value otherwise +// ---------------------------------------------------------------------------- +_Use_decl_annotations_ +HRESULT CAdvEndpointPropPage::Initialize +( + LPCITEMIDLIST pidlFolder, + IDataObject* pdtobj, + HKEY hkeyProgID +) +{ + UNREFERENCED_PARAMETER(pidlFolder); + UNREFERENCED_PARAMETER(pdtobj); + UNREFERENCED_PARAMETER(hkeyProgID); + + return(S_OK); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CAdvEndpointPropPage::FindHostConnector +// +// Description: +// This method initiates a search for a software connector that supports +// the supplied format. If one is found, it returns a path to that +// connector. +// +// Parameters: +// pKsFormat - [in] Requested format +// cbFormat - [in] Size of format buffer in BYTEs +// bRejectMixedPaths - [in] If TRUE, stop looking if you find a mix unit +// ppPath - [out] The path to the Host pin +// +// Remarks: +// To get the host connector, just take the zeroth element in the +// resulting path +// +// Return: +// S_OK on success, E_NOTFOUND if not. Other error code indicates +// something unexpected. +// ---------------------------------------------------------------------------- +HRESULT CAdvEndpointPropPage::FindHostConnector +( + PKSDATAFORMAT pKsFormat, + ULONG cbFormat, + BOOL bRejectMixedPaths, + IPartsList** ppPath +) +{ + HRESULT hr; + CPartsList* pPathOut = NULL; + CComPtr spTopology; + UINT cConnectors; + CComPtr spEndpointConnector; + CComPtr spConStart; + CComQIPtr spPartStart; + DataFlow flow; + UINT cDepth = 0; + CFormatExaminer* pExaminer = NULL; + + IF_TRUE_ACTION_JUMP((ppPath == NULL), hr = E_POINTER, Exit); + *ppPath = NULL; + + ATLASSERT(m_pAudioExtParams->pEndpoint != NULL); + + // Get IDeviceTopology interface for the Endpoint + hr = m_pAudioExtParams->pEndpoint->Activate(__uuidof(IDeviceTopology), CLSCTX_ALL, NULL, (void**)&spTopology); + IF_FAILED_JUMP(hr, Exit); + + // Get the connector to start searching from. By definition, an endpoint device can only have 1 connector. + hr = spTopology->GetConnectorCount(&cConnectors); + IF_FAILED_JUMP(hr, Exit); + + ATLASSERT(cConnectors == 1); + IF_TRUE_ACTION_JUMP((cConnectors != 1), hr = E_FAIL, Exit); + + // Since an endpoint device can only have 1 connector, get the connector at index 0 + hr = spTopology->GetConnector(0, &spEndpointConnector); + IF_FAILED_JUMP(hr, Exit); + + hr = spEndpointConnector->GetConnectedTo(&spConStart); + IF_FAILED_JUMP(hr, Exit); + + // get the dataflow (required by Search method) + hr = spConStart->GetDataFlow(&flow); + IF_FAILED_JUMP(hr, Exit); + + // QI for IPart (required by Search method) + spPartStart = spConStart; + + // Create an examiner that looks for a pin with the specified format + pExaminer = new CFormatExaminer(pKsFormat, cbFormat); + IF_TRUE_ACTION_JUMP((pExaminer == NULL), hr = E_OUTOFMEMORY, Exit); + + // Create a new parts list +#pragma warning(push) +#pragma warning(disable: 28197) + pPathOut = new CPartsList(); +#pragma warning(pop) + IF_TRUE_ACTION_JUMP((pPathOut == NULL), hr = E_OUTOFMEMORY, Exit); + + // Start looking + hr = Search(spPartStart, flow, pExaminer, pPathOut, cDepth, bRejectMixedPaths); + if (bRejectMixedPaths && (hr == E_NOTFOUND)) + { + // Don't actually reject mixed paths, just avoid them + hr = Search(spPartStart, flow, pExaminer, pPathOut, cDepth, FALSE); + } + IF_FAILED_JUMP(hr, Exit); + + hr = pPathOut->QueryInterface(__uuidof(IPartsList), (void**)ppPath); + +Exit: + SAFE_RELEASE(pPathOut); + + SAFE_DELETE(pExaminer); + + return hr; +} + + +// ---------------------------------------------------------------------------- +// Function: +// CAdvEndpointPropPage::Search +// +// Description: +// Searches the device topology for parts that satisfy the supplied +// examiner. +// +// Parameters: +// pPartStart - [in] The IPart to start looking at +// flowStart - [in] The flow of the starting part. This never changes +// so it is passed as an argument. +// pExaminer - [in] The examiner that tests each IPart against some criteria +// pPathAggregate - [out] The cumulative path that has been searched +// cDepth - [in] Stop looking if we reach this recursion depth +// bRejectMixedPaths - [in] If TRUE, then stop looking if we encounter a +// MIX unit +// +// Remarks: +// The search may span several FunctionInstances connected by DeviceModel +// IConnections. The algorithm works as follows: +// +// 1) Each connector in the model is tested against the following +// (in order): +// a) Is the flow opposite that of the starting part? +// If no then goto next connector. +// b) Does there exist a path from the starting part to this +// connector? If no then goto next connector. +// c) Is the supplied examiner satisfied by the path? +// If yes, then return. +// d) Is this connector connected to another connector? +// If yes, then add the connector to a list of "deferred +// connectors" to be checked if no other connectors in this model +// satisfy (c) +// +// 2) If the function still hasn't returned, then for each connector in +// the list generated in step 1d: +// a) Get the connector that it is connected to +// b) Recurse into this function with "that" connector as the starting +// part +// +// The point of the list of "deferred connectors" is to minimize the +// length of the paths that we find. e.g. if two viable paths exist, one +// (A) which spans 3 models, and another (B) which spans only 2, then B +// will be found first, regardless of the ordering of connections in the +// models involved. +// +// Note that when a path is found, all of the parts involved in that path +// are aggregated into pPathAggregate IN REVERSE ORDER. In general, all +// the parts in pPathAggregate will not belong to a single model, so when +// using pPathAggregate, you must use IPart::GetTopologyObject to get the +// model and/or FunctionInstance of the part (i.e. don't cache the value +// of either). +// +// Return: +// S_OK if successful +// ---------------------------------------------------------------------------- +HRESULT CAdvEndpointPropPage::Search +( + IPart* pPartStart, + DataFlow flowStart, + IExaminer* pExaminer, + CPartsList* pPathAggregate, + UINT cDepth, + BOOL bRejectMixedPaths +) +{ + HRESULT hr = S_OK; + TList lstDeferredConnectors; + BOOL bHaveViableConnector = FALSE; + + { + CComPtr spTopology; + UINT cConnectors; + + // Bail if we are recursing too deep + IF_TRUE_ACTION_JUMP((cDepth == MAX_SEARCH_DEPTH), hr = E_NOTFOUND, Exit); + + // Get DeviceModel and DeviceModelUtil interfaces associated with pPartStart. + // We can't just pass these as params, because each time this function is called + // recursively, we are talking about a part (pPartStart) on a different device. + hr = pPartStart->GetTopologyObject(&spTopology); + IF_FAILED_JUMP(hr, Exit); + + // + // [Step 1] Examine each connector. + // + hr = spTopology->GetConnectorCount(&cConnectors); + IF_FAILED_JUMP(hr, Exit); + + for (UINT c = 0; (c < cConnectors) && !bHaveViableConnector; c++) + { + CComPtr spConTest; + DataFlow flowTest; + CComPtr spPath; + CComPtr spPartTest; + BOOL bConnectorIsConnected; + + hr = spTopology->GetConnector(c, &spConTest); + IF_FAILED_JUMP(hr, Exit); + + hr = spConTest->GetDataFlow(&flowTest); + IF_FAILED_JUMP(hr, Exit); + + // [1a] We only care about connectors whose dataflow is opposite that of pConStart + // (i.e. on the other side of the topology) + if (flowTest == flowStart) + continue; + + // QI for IPart + spPartTest = spConTest; + + ATLASSERT(spPartTest); + IF_TRUE_ACTION_JUMP((spPartTest == NULL), hr = E_NOINTERFACE, Exit); + + // [1b] See if there exists a path from one to the other + hr = spTopology->GetSignalPath(pPartStart, spPartTest, bRejectMixedPaths, &spPath); + + // If no path exists from here to there, then try the next connector + if (hr != S_OK) + continue; + + // [1c] Let pExaminer take a look at this path, but don't worry if it fails + pExaminer->Examine(spPath); + + // See if pExaminer has found what it was looking for yet + if (pExaminer->IsSatisfied()) + { + bHaveViableConnector = TRUE; + + // add all of the parts in spPath to pPathAggregate + hr = pPathAggregate->AddParts(spPath); + break; + } + + // [1d] See if the connector is connected. If so, add to the list of deferred + // connectors + hr = spConTest->IsConnected(&bConnectorIsConnected); + + if (SUCCEEDED(hr) && bConnectorIsConnected) + { + LISTPOS posChk; + IConnector* pConDeferred; + + // Get another interface pointer that is NOT a CComPtr, so it doesn't + // get released until we want (note that .Detach means that when spConTest + // goes out of scope it won't release the interface) + pConDeferred = spConTest.Detach(); + + // Add to list of connectors to look more closely at + posChk = lstDeferredConnectors.AddTail(pConDeferred); + + if (posChk == NULL) + { + hr = E_OUTOFMEMORY; + pConDeferred->Release(); + goto Exit; + } + } + } + + if (!bHaveViableConnector) + { + // + // [Step 2] Jump to connected devices and test them. + // + + LISTPOS pos = lstDeferredConnectors.GetHeadPosition(); + + while (pos != NULL) + { + CComPtr spConConnectedTo; + IConnector* pConDeferred = NULL; // this will be cleaned up at the end of the function + CComPtr spPartConnectedTo; + CComPtr spPath; + CComQIPtr spPartDeferred; + + // Get the deferred connector. RefCount should be 1 + lstDeferredConnectors.GetNext(pos, &pConDeferred); + + // Get the part (a connector) that this connector is connected to + hr = pConDeferred->GetConnectedTo(&spConConnectedTo); + IF_FAILED_JUMP(hr, Exit); + + // Search that device model as well. Note that we can just reuse the dataflow here + spPartConnectedTo = spConConnectedTo; + ATLASSERT(spPartConnectedTo != NULL); + IF_TRUE_ACTION_JUMP((spPartConnectedTo == NULL), hr = E_NOINTERFACE, Exit); + + // Recursion + hr = Search(spPartConnectedTo, flowStart, pExaminer, pPathAggregate, cDepth + 1, bRejectMixedPaths); + if (FAILED(hr)) + continue; + + // We found what we were looking for! + bHaveViableConnector = TRUE; + + // We need to figure out how we got here again and add that path to pPath + spPartDeferred = pConDeferred; + ATLASSERT(spPartDeferred != NULL); + + // Note: If we fail past here, pPathAggregate will contain some parts. These will + // be cleaned up by CPartsList destructor + IF_TRUE_ACTION_JUMP((spPartDeferred == NULL), hr = E_NOINTERFACE, Exit); + + hr = spTopology->GetSignalPath(pPartStart, spPartDeferred, bRejectMixedPaths, &spPath); + IF_FAILED_JUMP(hr, Exit); + + // Add all of the parts in spPath to pPathAggregate + hr = pPathAggregate->AddParts(spPath); + IF_FAILED_JUMP(hr, Exit); + + break; + } + } + } + +Exit: + // Cleanup + IConnector* pConDelete; + while (lstDeferredConnectors.RemoveHead(&pConDelete)) + { + SAFE_RELEASE(pConDelete); + } + + // Adjust result. If there were no errors, but we didn't find what + // we were searching for, then return "not found" + if (SUCCEEDED(hr) && (!bHaveViableConnector)) + { + hr = E_NOTFOUND; + } + + return hr; +} + + +// ---------------------------------------------------------------------------- +// Function: +// CAdvEndpointPropPage::InitializeWidgets +// +// Description: +// Looks for parts in the path that support any of these control +// interfaces: +// -- IAudioLoudness +// -- IAudioAutoGainControl +// -- IDeviceSpecificProperty +// +// Return: +// S_OK if at least one control found +// E_NOTFOUND if no controls found +// other if unexpected error +// +// --------------------------------------------------------------------------- +HRESULT CAdvEndpointPropPage::InitializeWidgets() +{ + HRESULT hr = S_OK; + HRESULT hrWarn = S_OK; + CComPtr spPath; + UINT cParts; + BOOL bFoundSomething = FALSE; + UINT nCtrlId = 900; + + // Note: Can use the endpoint format instead of this ... + KSDATAFORMAT format; + ZeroMemory(&format, sizeof(format)); + + format.FormatSize = sizeof(KSDATAFORMAT); + format.MajorFormat = KSDATAFORMAT_TYPE_AUDIO; + format.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; + format.Specifier = KSDATAFORMAT_SPECIFIER_WAVEFORMATEX; + + // See if we can find a path to any old PCM host pin by only passing sizeof(KSDATAFORMAT) + hr = FindHostConnector(&format, sizeof(KSDATAFORMAT), FALSE, &spPath); + IF_FAILED_JUMP(hr, Exit); + + // + hr = spPath->GetCount(&cParts); + IF_FAILED_JUMP(hr, Exit); + + // Look for parts that support any of the interfaces in s_rgAdvCtrlInterfaces + for (UINT i = 0; i < cParts; i++) + { + UINT cInterfaces; + CComPtr spPart; + + hr = spPath->GetPart(i, &spPart); + IF_FAILED_JUMP(hr, Exit); + + hr = spPart->GetControlInterfaceCount(&cInterfaces); + IF_FAILED_JUMP(hr, Exit); + + for (UINT j = 0; j < cInterfaces; j++) + { + CComPtr spInterfaceDesc; + GUID iid; + + hr = spPart->GetControlInterface(j, &spInterfaceDesc); + IF_FAILED_JUMP(hr, Exit); + + hr = spInterfaceDesc->GetIID(&iid); + IF_FAILED_JUMP(hr, Exit); + + for (int k = 0; k < ARRAYSIZE(s_rgAdvCtrlInterfaces); k++) + { + if (iid == s_rgAdvCtrlInterfaces[k]) + { + bFoundSomething = TRUE; + + hrWarn = MakeNewWidget(spPart, iid, nCtrlId++); + } + } + } + } + + if (!bFoundSomething) + { + hr = E_NOTFOUND; + } + +Exit: + return(hr); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CAdvEndpointPropPage::AddPages +// +// Description: +// Implementation of IShellPropSheetExt::AddPages. Adds one or more pages +// to a property sheet that the Shell displays for a file object. +// +// Parameters: +// lpfnAddPage - [in] Address of a function that the property sheet +// handler calls to add a page to the property sheet. The +// function takes a property sheet handle returned by the +// CreatePropertySheetPage function and the lParam parameter +// passed to the AddPages method. +// lParam - [in] Parameter to pass to the function specified by the +// lpfnAddPage method. +// +// Return values: +// Returns S_OK if successful. If the method fails, an OLE-defined error +// code is returned +// ---------------------------------------------------------------------------- +_Use_decl_annotations_ +HRESULT STDMETHODCALLTYPE CAdvEndpointPropPage::AddPages +( + LPFNADDPROPSHEETPAGE lpfnAddPage, // See PrSht.h + LPARAM lParam // Used by caller, don't modify +) +{ + HRESULT hr = S_OK; + PROPSHEETPAGE psp; + HPROPSHEETPAGE hPage1 = NULL; + AudioExtensionParams* pAudioParams = (AudioExtensionParams*)lParam; +#pragma warning(push) +#pragma warning(disable: 28197) + AudioExtensionParams* pAudioParamsCopy = new AudioExtensionParams; +#pragma warning(pop) + + if (pAudioParamsCopy == NULL) + { + return E_OUTOFMEMORY; + } + + // Make a copy of the params + CopyMemory(pAudioParamsCopy, pAudioParams, sizeof(AudioExtensionParams)); + SAFE_ADDREF(pAudioParams->pEndpoint); + SAFE_ADDREF(pAudioParams->pPnpInterface); + SAFE_ADDREF(pAudioParams->pPnpDevnode); + + // Initialize property page params and create page + psp.dwSize = sizeof(psp); + psp.dwFlags = PSP_USEREFPARENT | PSP_USECALLBACK; + psp.hInstance = _AtlBaseModule.GetModuleInstance(); + psp.hIcon = 0; + psp.pcRefParent = (UINT*)&m_dwRef; + psp.lParam = (LPARAM)pAudioParamsCopy; + psp.pszTemplate = MAKEINTRESOURCE(IDD_ADV_ENDPOINT_PROP_PAGE); + psp.pfnDlgProc = (DLGPROC)DialogProcPage1; + psp.pfnCallback = PropSheetPageProc; + + // Create the property sheet page and add the page + hPage1 = CreatePropertySheetPage(&psp); + if (hPage1) + { + if (!lpfnAddPage(hPage1, pAudioParams->AddPageParam)) + { + hr = E_FAIL; + delete pAudioParamsCopy; + DestroyPropertySheetPage(hPage1); + } + else + { + // Add ref for page + this->AddRef(); + } + } + else + { + delete pAudioParamsCopy; + hr = E_OUTOFMEMORY; + } + + return(hr); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CAdvEndpointPropPage::ReplacePage +// +// Description: +// Implementation of IShellPropSheetExt::ReplacePage. Replaces a page in +// a property sheet for a Control Panel object. +// +// Parameters: +// uPageID - [in] Identifier of the page to replace +// lpfnReplacePage - [in] Address of a function that the property sheet +// handler calls to replace a page to the property +// sheet. The function takes a property sheet handle +// returned by the CreatePropertySheetPage function and +// the lParam parameter passed to the ReplacePage +// method. +// lParam - [in] Parameter to pass to the function specified by the +// lpfnReplacePage parameter. +// +// Return values: +// Returns NOERROR if successful, or an OLE-defined error value otherwise +// ---------------------------------------------------------------------------- +_Use_decl_annotations_ +HRESULT STDMETHODCALLTYPE CAdvEndpointPropPage::ReplacePage +( + UINT uPageID, + LPFNSVADDPROPSHEETPAGE lpfnReplaceWith, + LPARAM lParam +) +{ + UNREFERENCED_PARAMETER(uPageID); + UNREFERENCED_PARAMETER(lpfnReplaceWith); + UNREFERENCED_PARAMETER(lParam); + + return(S_FALSE); +} diff --git a/audio/sysvad/SwapAPO/PropPageExtensions/AdvEndpointPropPage.h b/audio/sysvad/SwapAPO/PropPageExtensions/AdvEndpointPropPage.h new file mode 100644 index 000000000..0d02ef975 --- /dev/null +++ b/audio/sysvad/SwapAPO/PropPageExtensions/AdvEndpointPropPage.h @@ -0,0 +1,74 @@ +//**@@@*@@@**************************************************** +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation. All rights reserved. +// +//**@@@*@@@**************************************************** + +// +// FileName: AdvEndpointPropPage.h +// +// Abstract: Declaration of the CAdvEndpointPropPage class +// +// ---------------------------------------------------------------------------- + + +#pragma once + + +class ATL_NO_VTABLE CAdvEndpointPropPage : + public CComObjectRootEx, + public CComCoClass, + public IDispatchImpl, + public IShellExtInit, + public IShellPropSheetExt +{ +public: + CAdvEndpointPropPage(); + ~CAdvEndpointPropPage(); + + DECLARE_REGISTRY_RESOURCEID(IDR_ADV_ENDPOINT_PROP_PAGE) + + BEGIN_COM_MAP(CAdvEndpointPropPage) + COM_INTERFACE_ENTRY(IAdvEndpointPropPage) + COM_INTERFACE_ENTRY(IDispatch) + COM_INTERFACE_ENTRY(IShellExtInit) + COM_INTERFACE_ENTRY(IShellPropSheetExt) + END_COM_MAP() + + DECLARE_PROTECT_FINAL_CONSTRUCT() + + HRESULT FinalConstruct() + { + return S_OK; + } + + void FinalRelease() + { + } + + static INT_PTR CALLBACK DialogProcPage1(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); + static UINT CALLBACK PropSheetPageProc(HWND hwnd, UINT uMsg, LPPROPSHEETPAGE ppsp); + + // IShellExtInit + STDMETHOD(Initialize)(_In_opt_ LPCITEMIDLIST pidlFolder, _In_opt_ IDataObject* pdtobj, _In_opt_ HKEY hkeyProgID); + + // IShellPropSheetExt + STDMETHOD(AddPages)(_In_ LPFNADDPROPSHEETPAGE lpfnAddPage, _In_ LPARAM lParam); + STDMETHOD(ReplacePage)(_In_ UINT uPageID, _In_ LPFNSVADDPROPSHEETPAGE lpfnReplaceWith, _In_ LPARAM lParam); + +private: + AudioExtensionParams* m_pAudioExtParams; + TList m_lstWidgets; + + HRESULT FindHostConnector(PKSDATAFORMAT pKsFormat, ULONG cbFormat, BOOL bRejectMixedPaths, IPartsList** ppPath); + HRESULT Search(IPart* pStart, DataFlow flowStart, IExaminer* pExaminer, CPartsList* pPath, UINT cDepth, BOOL bRejectMixedPaths); + HRESULT InitializeWidgets(); + HRESULT GetDeviceFriendlyName(_Outptr_result_maybenull_ LPWSTR* ppNameOut); + HRESULT MakeNewWidget(IPart* pPart, REFIID iid, UINT nCtrlId); + BOOL OnInitDialog (HWND hwndDlg, WPARAM wParam, LPARAM lParam); + BOOL OnApply(HWND hwndDlg, WPARAM wParam, LPARAM lParam); + BOOL OnCheckBoxClicked(HWND hwndDlg, WPARAM wParam, LPARAM lParam); +}; + +OBJECT_ENTRY_AUTO(__uuidof(AdvEndpointPropPage), CAdvEndpointPropPage) diff --git a/audio/sysvad/SwapAPO/PropPageExtensions/AdvEndpointPropPage.rgs b/audio/sysvad/SwapAPO/PropPageExtensions/AdvEndpointPropPage.rgs new file mode 100644 index 000000000..eecbc126e --- /dev/null +++ b/audio/sysvad/SwapAPO/PropPageExtensions/AdvEndpointPropPage.rgs @@ -0,0 +1,27 @@ +HKCR +{ + CplExt.AdvEndpointPropPage.1 = s 'AdvEndpointPropPage Class' + { + CLSID = s '{6C57B2A2-91F5-4b90-93D5-FAB82485ECA6}' + } + CplExt.AdvEndpointPropPage = s 'AdvEndpointPropPage Class' + { + CLSID = s '{6C57B2A2-91F5-4b90-93D5-FAB82485ECA6}' + CurVer = s 'CplExt.AdvEndpointPropPage.1' + } + NoRemove CLSID + { + ForceRemove {6C57B2A2-91F5-4b90-93D5-FAB82485ECA6} = s 'AdvEndpointPropPage Class' + { + ProgID = s 'CplExt.AdvEndpointPropPage.1' + VersionIndependentProgID = s 'CplExt.AdvEndpointPropPage' + ForceRemove 'Programmable' + InprocServer32 = s '%MODULE%' + { + val ThreadingModel = s 'Apartment' + } + val AppID = s '%APPID%' + 'TypeLib' = s '{59390583-ED40-46D9-9B16-7800B9CC5CC2}' + } + } +} diff --git a/audio/sysvad/SwapAPO/PropPageExtensions/CplExt.cpp b/audio/sysvad/SwapAPO/PropPageExtensions/CplExt.cpp new file mode 100644 index 000000000..dd915cd5e --- /dev/null +++ b/audio/sysvad/SwapAPO/PropPageExtensions/CplExt.cpp @@ -0,0 +1,69 @@ +//**@@@*@@@**************************************************** +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation. All rights reserved. +// +//**@@@*@@@**************************************************** + +// +// FileName: CplExt.cpp +// +// Abstract: Implementation of DLL Exports +// +// ---------------------------------------------------------------------------- + + +#include "stdafx.h" + +_Analysis_mode_(_Analysis_code_type_user_driver_) + +HINSTANCE g_hInstance = NULL; + + +class CCplExtModule : public CAtlDllModuleT< CCplExtModule > +{ +public : + DECLARE_LIBID(LIBID_CplExtLib) + DECLARE_REGISTRY_APPID_RESOURCEID(IDR_CPL_EXT, "{A7D2EC8B-B70F-434C-A0CE-0DF324805F7D}") +}; + +CCplExtModule _AtlModule; + + +// DLL Entry Point +extern "C" BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) +{ + g_hInstance = hInstance; + return _AtlModule.DllMain(dwReason, lpReserved); +} + + +// Used to determine whether the DLL can be unloaded by OLE +STDAPI DllCanUnloadNow(void) +{ + 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) +{ + 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(); + return hr; +} + + +// DllUnregisterServer - Removes entries from the system registry +STDAPI DllUnregisterServer(void) +{ + HRESULT hr = _AtlModule.DllUnregisterServer(); + return hr; +} diff --git a/audio/sysvad/SwapAPO/PropPageExtensions/CplExt.idl b/audio/sysvad/SwapAPO/PropPageExtensions/CplExt.idl new file mode 100644 index 000000000..3254cbd82 --- /dev/null +++ b/audio/sysvad/SwapAPO/PropPageExtensions/CplExt.idl @@ -0,0 +1,73 @@ +//**@@@*@@@**************************************************** +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation. All rights reserved. +// +//**@@@*@@@**************************************************** + +// +// FileName: CplExt.idl +// +// Abstract: IDL source for CplExt. This file will be processed by the +// MIDL tool to produce the type library (CplExt.tlb) and +// marshalling code. +// +// ---------------------------------------------------------------------------- + + +import "oaidl.idl"; +import "ocidl.idl"; + +[ + object, + uuid(E6DB299B-B925-415A-879B-4A76D072F39A), + dual, + nonextensible, + helpstring("ISwapPropPage Interface"), + pointer_default(unique) +] +interface ISwapPropPage : IDispatch{ +}; + + +[ + object, + uuid(D379F7FB-66E0-427f-890D-1F479AD73B4B), + dual, + nonextensible, + helpstring("IAdvEndpointPropPage Interface"), + pointer_default(unique) +] +interface IAdvEndpointPropPage : IDispatch{ +}; + + +[ + uuid(59390583-ED40-46D9-9B16-7800B9CC5CC2), + version(1.0), + helpstring("CplExt 1.0 Type Library") +] +library CplExtLib +{ + importlib("stdole2.tlb"); + + // For Swap Property Page + [ + uuid(19166F23-5F08-47F9-BB57-9F57A977D88E), + helpstring("SwapPropPage Class") + ] + coclass SwapPropPage + { + [default] interface ISwapPropPage; + }; + + // For Advanced Endpoint Property Page + [ + uuid(6C57B2A2-91F5-4b90-93D5-FAB82485ECA6), + helpstring("AdvEndpointPropPage Class") + ] + coclass AdvEndpointPropPage + { + [default] interface IAdvEndpointPropPage; + }; +}; diff --git a/audio/sysvad/SwapAPO/PropPageExtensions/CplExt.rc b/audio/sysvad/SwapAPO/PropPageExtensions/CplExt.rc new file mode 100644 index 000000000..415ed22eb --- /dev/null +++ b/audio/sysvad/SwapAPO/PropPageExtensions/CplExt.rc @@ -0,0 +1,208 @@ +// 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 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "1 TYPELIB ""cplext.tlb""\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// 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 "InternalName", "CplExt.dll" + VALUE "LegalCopyright", "TODO: (c) . All rights reserved." + VALUE "OriginalFilename", "CplExt.dll" + VALUE "ProductName", "TODO: " + VALUE "ProductVersion", "1.0.0.1" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// REGISTRY +// + +IDR_CPL_EXT REGISTRY "CplExt.rgs" +IDR_SWAP_PROP_PAGE REGISTRY "SwapPropPage.rgs" +IDR_ADV_ENDPOINT_PROP_PAGE REGISTRY "AdvEndpointPropPage.rgs" + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_SWAP_PROP_PAGE DIALOGEX 0, 0, 284, 246 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_CHILD | WS_VISIBLE | + WS_CAPTION | WS_SYSMENU +CAPTION "System Effects - Sample" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "",IDC_SPP_ENDPOINT_NAME,19,34,211,8 + CONTROL "&Disable System Effects",IDC_DISABLE_SYSFX,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,22,92,89,8 + CONTROL "Enable Channel Swap &SFX",IDC_ENABLE_SWAP_SFX,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,22,112,98,8 + CONTROL "Enable Channel Swap &MFX",IDC_ENABLE_SWAP_MFX,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,22,132,99,8 + CONTROL "Enable D&elay SFX",IDC_ENABLE_DELAY_SFX,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,22,152,98,8 + CONTROL "Enable Del&ay MFX",IDC_ENABLE_DELAY_MFX,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,22,172,99,8 + GROUPBOX "Endpoint Name",IDC_STATIC,7,18,270,37 + GROUPBOX "System Effects Configuration",IDC_STATIC,7,71,270,132 +END + +IDD_ADV_ENDPOINT_PROP_PAGE DIALOGEX 0, 0, 284, 244 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_CHILD | WS_CAPTION | + WS_SYSMENU +CAPTION "Custom - Sample" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "",IDC_EPP_ENDPOINT_NAME,19,34,211,8 + LTEXT "",IDC_NO_ADV_CONTROLS_FOUND,20,90,232,9 + GROUPBOX "Endpoint Name",IDC_STATIC,7,18,270,37 + GROUPBOX "Advanced Controls",IDC_STATIC,7,71,270,154 +END + +IDD_WIDGET_GEN_LONG DIALOGEX 0, 0, 200, 12 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_VISIBLE +EXSTYLE WS_EX_STATICEDGE +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + RTEXT "L",IDC_GEN_LABEL,0,2,94,8 + CONTROL "",IDC_GEN_SLIDER,"msctls_trackbar32",TBS_BOTH | + TBS_NOTICKS | WS_TABSTOP | 0x400,96,0,76,13 + RTEXT "-6 dB",IDC_GEN_VALUE,174,1,24,10,0,WS_EX_STATICEDGE +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_SWAP_PROP_PAGE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 277 + TOPMARGIN, 7 + BOTTOMMARGIN, 239 + END + + IDD_ADV_ENDPOINT_PROP_PAGE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 277 + TOPMARGIN, 7 + BOTTOMMARGIN, 237 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_AUDIO_ICON ICON "music.ico" + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + IDS_PROJ_NAME "CplExt" +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// +1 TYPELIB "cplext.tlb" + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/audio/sysvad/SwapAPO/PropPageExtensions/CplExt.rgs b/audio/sysvad/SwapAPO/PropPageExtensions/CplExt.rgs new file mode 100644 index 000000000..1c97b062b --- /dev/null +++ b/audio/sysvad/SwapAPO/PropPageExtensions/CplExt.rgs @@ -0,0 +1,11 @@ +HKCR +{ + NoRemove AppID + { + '%APPID%' = s 'CplExt' + 'CplExt.DLL' + { + val AppID = s '%APPID%' + } + } +} diff --git a/audio/sysvad/SwapAPO/PropPageExtensions/Parts.cpp b/audio/sysvad/SwapAPO/PropPageExtensions/Parts.cpp new file mode 100644 index 000000000..41130ead0 --- /dev/null +++ b/audio/sysvad/SwapAPO/PropPageExtensions/Parts.cpp @@ -0,0 +1,298 @@ +//**@@@*@@@**************************************************** +// +// Microsoft Windows DevTopo.dll +// Copyright (C) Microsoft Corporation. All rights reserved. +// +//**@@@*@@@**************************************************** + +// +// FileName: Parts.cpp +// +// Abstract: Implementation of CPart and derived classes. +// +// -------------------------------------------------------------------------------- + +#include "stdafx.h" +#include +#include "Parts.h" + +_Analysis_mode_(_Analysis_code_type_user_driver_) + +// ---------------------------------------------------------------------- +// Function: +// CPartsList::CPartsList +// +// Description: +// CPartsList constructor +// ---------------------------------------------------------------------- +CPartsList::CPartsList() : + m_refCount(1) +{ +} + + +// ---------------------------------------------------------------------- +// Function: +// CPartsList::~CPartsList +// +// Description: +// CPartsList destructor +// ---------------------------------------------------------------------- +CPartsList::~CPartsList() +{ + ATLASSERT(m_refCount == 0); + + IPart* pIPart; + while (m_lstParts.RemoveHead(&pIPart)) + { + SAFE_RELEASE(pIPart); + } +} + + +// ---------------------------------------------------------------------- +// Function: +// CPartsList::QueryInterface +// +// Description: +// Implementation of IUnknown::QueryInterface +// +// Parameters: +// riid - [in] Identifier of the interface being queried +// ppvObj - [in] Pointer to a buffer that receives a pointer to the +// object whose interface is queried +// +// Return: +// S_OK if successful +// ---------------------------------------------------------------------- +STDMETHODIMP CPartsList::QueryInterface +( + const IID& iid, + void** ppUnk +) +{ + if (!ppUnk) + return E_POINTER; + + HRESULT hr = E_NOINTERFACE; + *ppUnk = NULL; + + if (iid == __uuidof(IPartsList)) + { + *ppUnk = (IPartsList*)this; + hr = S_OK; + AddRef(); + } + else + if (iid == __uuidof(IUnknown)) + { + *ppUnk = (IUnknown*)this; + hr = S_OK; + AddRef(); + } + + return hr; +} + + +// ---------------------------------------------------------------------- +// Function: +// CPartsList::AddRef +// +// Description: +// Implementation of IUnknown::AddRef +// +// Return: +// New refcount +// ---------------------------------------------------------------------- +ULONG STDMETHODCALLTYPE CPartsList::AddRef(void) +{ + return InterlockedIncrement(&m_refCount); +} + + +// ---------------------------------------------------------------------- +// Function: +// CPartsList::Release +// +// Description: +// Implementation of IUnknown::Release +// +// Return: +// New refcount +// ---------------------------------------------------------------------- +ULONG STDMETHODCALLTYPE CPartsList::Release(void) +{ + ATLASSERT(m_refCount > 0); + LONG lRef = InterlockedDecrement(&m_refCount); + ATLASSERT(lRef >= 0); + + if (lRef == 0) + { + delete this; + } + return lRef; +} + + +// ---------------------------------------------------------------------- +// Function: +// CPartsList::AddPart +// +// Description: +// Adds an item to the list of IPart interfaces +// +// Parameters: +// pIPart - [in] Part to add to the list +// +// Return: +// S_OK if successful +// ---------------------------------------------------------------------- +STDMETHODIMP CPartsList::AddPart +( + IPart* pIPart +) +{ + if (pIPart == NULL) + return E_POINTER; + + // Protect m_lstParts access + m_CritSection.Enter(); + + pIPart->AddRef(); + m_lstParts.AddTail(pIPart); + + m_CritSection.Leave(); + + return S_OK; +} + + +// ---------------------------------------------------------------------- +// Function: +// CPartsList::AddPart +// +// Description: +// Adds the PartsList Parts to the list of IPart interfaces +// +// Parameters: +// pIParts - [in] List of Parts to be added to the list +// +// Return: +// S_OK if successful +// ---------------------------------------------------------------------- +STDMETHODIMP CPartsList::AddParts +( + IPartsList* pIParts +) +{ + ATLASSERT(pIParts != NULL); + + HRESULT hr = S_OK; + UINT i, cParts = 0; + + // Protect m_lstParts + m_CritSection.Enter(); + + // Loop through specified parts list + hr = pIParts->GetCount(&cParts); + IF_FAILED_JUMP(hr, Exit); + + for (i = 0; (i < cParts) && (hr == S_OK); i++) + { + IPart* pIPart; + + // ... and copy reference to each part + hr = pIParts->GetPart(i, &pIPart); + IF_FAILED_JUMP(hr, Exit); + + // ... to this list + m_lstParts.AddTail(pIPart); + } + +Exit: + m_CritSection.Leave(); + + return hr; +} + + +// ---------------------------------------------------------------------- +// Function: +// CPartsList::GetCount +// +// Description: +// Returns the count of items in the list of IParts +// +// Parameters: +// pCount - [out] the count of parts in this list +// +// Return: +// S_OK if successful +// +// ---------------------------------------------------------------------- +_Use_decl_annotations_ +STDMETHODIMP CPartsList::GetCount +( + UINT* pCount +) +{ + if (pCount == NULL) + return E_POINTER; + + // Protect m_lstParts access + m_CritSection.Enter(); + + *pCount = m_lstParts.GetCount(); + + m_CritSection.Leave(); + + return S_OK; +} + + +// ---------------------------------------------------------------------- +// Function: +// CPartsList::GetPart +// +// Description: +// Returns the nIndex-th item in the list of IParts +// +// Parameters: +// nIndex - [in] index of the part to access +// uMsg - [out] the part at the specified index +// +// Return: +// S_OK if successful +// +// ---------------------------------------------------------------------- +_Use_decl_annotations_ +STDMETHODIMP CPartsList::GetPart +( + UINT nIndex, + IPart** ppPart +) +{ + HRESULT hr; + IPart* pIPart = NULL; + + // Protect m_lstParts access + m_CritSection.Enter(); + + IF_TRUE_ACTION_JUMP((ppPart == NULL), hr = E_POINTER, Exit); + + *ppPart = NULL; + + IF_TRUE_ACTION_JUMP((nIndex >= m_lstParts.GetCount()), hr = E_INVALIDARG, Exit); + + IF_TRUE_ACTION_JUMP((!m_lstParts.GetAt(nIndex, &pIPart)), hr = E_NOTFOUND, Exit); + + // Take care of AddRef and ensure correct interface in one shot + hr = pIPart->QueryInterface(__uuidof(IPart), reinterpret_cast(ppPart)); + IF_FAILED_JUMP(hr, Exit); + +Exit: + m_CritSection.Leave(); + + return hr; +} diff --git a/audio/sysvad/SwapAPO/PropPageExtensions/Parts.h b/audio/sysvad/SwapAPO/PropPageExtensions/Parts.h new file mode 100644 index 000000000..1cad63f52 --- /dev/null +++ b/audio/sysvad/SwapAPO/PropPageExtensions/Parts.h @@ -0,0 +1,49 @@ +//**@@@*@@@**************************************************** +// +// Microsoft Windows DevTopo.dll +// Copyright (C) Microsoft Corporation. All rights reserved. +// +//**@@@*@@@**************************************************** + +// +// FileName: Parts.h +// +// Abstract: CPartsList is a list container of parts +// +// -------------------------------------------------------------------------------- + +#pragma once + + +class CPartsList : public IPartsList +{ +private: + + // This is a REAL refcount. When it goes to zero, the object is destroyed. + LONG m_refCount; + TList m_lstParts; + + CCriticalSection m_CritSection; + +protected: + ~CPartsList(); // Refcounted objects should have a private destructor. + +public: + CPartsList(); + + STDMETHOD(AddPart)(IPart* pIPart); + STDMETHOD(AddParts)(IPartsList* pIParts); + +public: + + // IPartsList + STDMETHOD(GetCount)(_Out_ UINT* pCount); + STDMETHOD(GetPart)(_In_ UINT nIndex, _Out_ IPart** ppPart); + + // IUnknown + HRESULT STDMETHODCALLTYPE QueryInterface(const IID& iid, void** ppUnk); + ULONG STDMETHODCALLTYPE AddRef(void); + ULONG STDMETHODCALLTYPE Release(void); + + friend class CDeviceTopology; +}; diff --git a/audio/sysvad/SwapAPO/PropPageExtensions/PropPageExt.def b/audio/sysvad/SwapAPO/PropPageExtensions/PropPageExt.def new file mode 100644 index 000000000..aa2797ce3 --- /dev/null +++ b/audio/sysvad/SwapAPO/PropPageExtensions/PropPageExt.def @@ -0,0 +1,9 @@ +; PropPageExt.def : Declares the module parameters. + +LIBRARY "PropPageExt.DLL" + +EXPORTS + DllCanUnloadNow PRIVATE + DllGetClassObject PRIVATE + DllRegisterServer PRIVATE + DllUnregisterServer PRIVATE diff --git a/audio/sysvad/SwapAPO/PropPageExtensions/PropPageExt.vcxproj b/audio/sysvad/SwapAPO/PropPageExtensions/PropPageExt.vcxproj new file mode 100644 index 000000000..61fd0d385 --- /dev/null +++ b/audio/sysvad/SwapAPO/PropPageExtensions/PropPageExt.vcxproj @@ -0,0 +1,275 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {76DBA957-38F6-47ED-A5C0-AD587D08FF12} + $(MSBuildProjectName) + false + true + Debug + Win32 + {3D348571-DCCC-4D1B-B986-C7B51C270E20} + + + + 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) + + + + + + + + + + + + + + + + PropPageExt + Dynamic + + + PropPageExt + Dynamic + + + PropPageExt + Dynamic + + + PropPageExt + Dynamic + + + + Sync + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + Sync + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + Sync + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + Sync + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + true + Level4 + %(AdditionalOptions) /wd4127 + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);..\Inc;$(DDK_INC_PATH) + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);..\Inc;$(DDK_INC_PATH) + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);..\Inc;$(DDK_INC_PATH) + + + %(AdditionalDependencies);advapi32.lib;kernel32.lib;user32.lib;ole32.lib;oleaut32.lib;uuid.lib;shlwapi.lib;comctl32.lib;rpcrt4.lib + PropPageExt.def + + + + + true + Level4 + %(AdditionalOptions) /wd4127 + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);..\Inc;$(DDK_INC_PATH) + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);..\Inc;$(DDK_INC_PATH) + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);..\Inc;$(DDK_INC_PATH) + + + %(AdditionalDependencies);advapi32.lib;kernel32.lib;user32.lib;ole32.lib;oleaut32.lib;uuid.lib;shlwapi.lib;comctl32.lib;rpcrt4.lib + PropPageExt.def + + + + + true + Level4 + %(AdditionalOptions) /wd4127 + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);..\Inc;$(DDK_INC_PATH) + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);..\Inc;$(DDK_INC_PATH) + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);..\Inc;$(DDK_INC_PATH) + + + %(AdditionalDependencies);advapi32.lib;kernel32.lib;user32.lib;ole32.lib;oleaut32.lib;uuid.lib;shlwapi.lib;comctl32.lib;rpcrt4.lib + PropPageExt.def + + + + + true + Level4 + %(AdditionalOptions) /wd4127 + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);..\Inc;$(DDK_INC_PATH) + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);..\Inc;$(DDK_INC_PATH) + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);..\Inc;$(DDK_INC_PATH) + + + %(AdditionalDependencies);advapi32.lib;kernel32.lib;user32.lib;ole32.lib;oleaut32.lib;uuid.lib;shlwapi.lib;comctl32.lib;rpcrt4.lib + PropPageExt.def + + + + + ;%(AdditionalIncludeDirectories) + stdafx.h + Use + $(IntDir)\stdafx.pch + + + ;%(AdditionalIncludeDirectories) + stdafx.h + Use + $(IntDir)\stdafx.pch + + + ;%(AdditionalIncludeDirectories) + stdafx.h + Use + $(IntDir)\stdafx.pch + + + ;%(AdditionalIncludeDirectories) + stdafx.h + Create + $(IntDir)\stdafx.pch + + + ;%(AdditionalIncludeDirectories) + stdafx.h + Use + $(IntDir)\stdafx.pch + + + ;%(AdditionalIncludeDirectories) + stdafx.h + Use + $(IntDir)\stdafx.pch + + + ;%(AdditionalIncludeDirectories) + stdafx.h + Use + $(IntDir)\stdafx.pch + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/audio/sysvad/SwapAPO/PropPageExtensions/PropPageExt.vcxproj.Filters b/audio/sysvad/SwapAPO/PropPageExtensions/PropPageExt.vcxproj.Filters new file mode 100644 index 000000000..bf37ff4d1 --- /dev/null +++ b/audio/sysvad/SwapAPO/PropPageExtensions/PropPageExt.vcxproj.Filters @@ -0,0 +1,51 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {531ADE68-0355-4EB7-BA48-144A9A3C8B98} + + + h;hpp;hxx;hm;inl;inc;xsd + {DFFDE202-63D4-4633-A6F6-661E34DAE36E} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {9592BEFC-255E-4BD1-B21A-3D100EE35A6A} + + + + + 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/audio/sysvad/SwapAPO/PropPageExtensions/SwapPropPage.cpp b/audio/sysvad/SwapAPO/PropPageExtensions/SwapPropPage.cpp new file mode 100644 index 000000000..cdc2b0e26 --- /dev/null +++ b/audio/sysvad/SwapAPO/PropPageExtensions/SwapPropPage.cpp @@ -0,0 +1,1154 @@ +//**@@@*@@@**************************************************** +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation. All rights reserved. +// +//**@@@*@@@**************************************************** + +// +// FileName: SwapPropPage.cpp +// +// Abstract: Implementation of the CSwapPropPage class +// +// ---------------------------------------------------------------------------- + + +#include "stdafx.h" +#include // DEFINE_GUID +#include +#include +#include +#include "SwapPropPage.h" +#include +#include +#include "cplext_i.c" + +_Analysis_mode_(_Analysis_code_type_user_driver_) + +// ---------------------------------------------------------------------------- +// Function: +// CSwapPropPage::CSwapPropPage +// +// Description: +// CSwapPropPage constructor +// ---------------------------------------------------------------------------- +CSwapPropPage::CSwapPropPage() +: m_pAudioFXExtParams(NULL) +, m_fDisableSysFX(FALSE) +, m_fEnableSwapSFX(FALSE) +, m_fEnableSwapMFX(FALSE) +, m_fEnableDelaySFX(FALSE) +, m_fEnableDelayMFX(FALSE) +, m_fReset(FALSE) +{ +} + + +// ---------------------------------------------------------------------------- +// Function: +// CSwapPropPage::~CSwapPropPage +// +// Description: +// CSwapPropPage destructor +// ---------------------------------------------------------------------------- +CSwapPropPage::~CSwapPropPage() +{ + SAFE_RELEASE(m_pAudioFXExtParams->pFxProperties); + SAFE_DELETE(m_pAudioFXExtParams); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CSwapPropPage::GetDeviceFriendlyName +// +// Description: +// Retrieves the endpoint's friendly name +// +// Parameters: +// ppNameOut - [out] The friendly name of the endpoint +// +// Return values: +// S_OK if successful +// ---------------------------------------------------------------------------- +HRESULT CSwapPropPage::GetDeviceFriendlyName +( + _Outptr_result_maybenull_ LPWSTR* ppNameOut +) +{ + CComPtr spEnumerator; + CComPtr spProperties; + CComPtr spMMDevice; + HRESULT hr = S_OK; + PROPVARIANT var; + + IF_TRUE_ACTION_JUMP((ppNameOut == NULL), hr = E_POINTER, Exit); + + *ppNameOut = NULL; + + // Create device enumerator and get IMMDevice from the device ID + hr = spEnumerator.CoCreateInstance(__uuidof(MMDeviceEnumerator)); + IF_FAILED_JUMP(hr, Exit); + + hr = spEnumerator->GetDevice(m_pAudioFXExtParams->pwstrEndpointID, &spMMDevice); + IF_FAILED_JUMP(hr, Exit); + + // Open the PropertyStore for read access + hr = spMMDevice->OpenPropertyStore(STGM_READ, &spProperties); + IF_FAILED_JUMP(hr, Exit); + + PropVariantInit(&var); + + // Retrieve the friendly name of the endpoint + hr = spProperties->GetValue(PKEY_Device_FriendlyName, &var); + if (SUCCEEDED(hr) && (var.vt == VT_LPWSTR)) + { + *ppNameOut = var.pwszVal; + } + else + { + PropVariantClear(&var); + } + +Exit: + return(hr); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CSwapPropPage::RetrieveSysFXState +// +// Description: +// Get the current state (enabled or disabled) of system effects +// +// Return values: +// S_OK if successful +// ---------------------------------------------------------------------------- +HRESULT CSwapPropPage::RetrieveSysFXState(BOOL *pfDisabled) +{ + HRESULT hr = E_POINTER; + + if ((m_pAudioFXExtParams != NULL) && (m_pAudioFXExtParams->pFxProperties != NULL)) + { + PROPVARIANT var; + PropVariantInit(&var); + + // Get the state of whether system effects are enabled or not + hr = m_pAudioFXExtParams->pFxProperties->GetValue(PKEY_AudioEndpoint_Disable_SysFx, &var); + if (SUCCEEDED(hr) && (var.vt == VT_UI4)) + { + *pfDisabled = (var.ulVal != 0L); + } + + PropVariantClear(&var); + } + + return(hr); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CSwapPropPage::SetSysFXState +// +// Description: +// Enable or disable system effects +// +// Return values: +// S_OK if successful +// ---------------------------------------------------------------------------- +HRESULT CSwapPropPage::SetSysFXState() +{ + BOOL fCurrentState = 0; + HRESULT hr = RetrieveSysFXState(&fCurrentState); + IF_FAILED_JUMP(hr, Exit); + + if (fCurrentState != m_fDisableSysFX) + { + PROPVARIANT var; + var.vt = VT_UI4; + var.ulVal = (m_fDisableSysFX ? 1L : 0L); + + // Enable or disable SysFX + hr = m_pAudioFXExtParams->pFxProperties->SetValue(PKEY_AudioEndpoint_Disable_SysFx, var); + } + +Exit: + return(hr); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CSwapPropPage::RetrieveSwapSFXState +// +// Description: +// Get the current state (enabled or disabled) of channel swap SFX +// +// Return values: +// S_OK if successful +// ---------------------------------------------------------------------------- +HRESULT CSwapPropPage::RetrieveSwapSFXState(BOOL *pfEnabled) +{ + HRESULT hr = E_POINTER; + + if ((m_pAudioFXExtParams != NULL) && (m_pAudioFXExtParams->pFxProperties != NULL)) + { + PROPVARIANT var; + PropVariantInit(&var); + + // Get the state of whether channel swap SFX is enabled or not + hr = m_pAudioFXExtParams->pFxProperties->GetValue(PKEY_Endpoint_Enable_Channel_Swap_SFX, &var); + if (SUCCEEDED(hr) && (var.vt == VT_UI4)) + { + *pfEnabled = (var.ulVal != 0L); + } + + PropVariantClear(&var); + } + + return(hr); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CSwapPropPage::SetSwapSFXState +// +// Description: +// Enable or disable channel swap SFX +// +// Return values: +// S_OK if successful +// ---------------------------------------------------------------------------- +HRESULT CSwapPropPage::SetSwapSFXState() +{ + BOOL fCurrentState = 0; + HRESULT hr = RetrieveSwapSFXState(&fCurrentState); + IF_FAILED_JUMP(hr, Exit); + + if (fCurrentState != m_fEnableSwapSFX) + { + PROPVARIANT var; + var.vt = VT_UI4; + var.ulVal = (m_fEnableSwapSFX ? 1L : 0L); + + // Enable or disable channel swap SFX + hr = m_pAudioFXExtParams->pFxProperties->SetValue(PKEY_Endpoint_Enable_Channel_Swap_SFX, var); + + // Enabling or disabling the swap effect can be done with the engine running + // It does not invalidate anything which should be locked by IAudioProcessingObjectConfiguration::LockForProcess + // In particular, it does not impact IAudioProcessingObject::GetLatency + // So we do not need to call IAudioEndpointFormatControl::ResetToDefault here + } + +Exit: + return(hr); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CSwapPropPage::RetrieveSwapMFXState +// +// Description: +// Get the current state (enabled or disabled) of channel swap MFX +// +// Return values: +// S_OK if successful +// ---------------------------------------------------------------------------- +HRESULT CSwapPropPage::RetrieveSwapMFXState(BOOL *pfEnabled) +{ + HRESULT hr = E_POINTER; + + if ((m_pAudioFXExtParams != NULL) && (m_pAudioFXExtParams->pFxProperties != NULL)) + { + PROPVARIANT var; + PropVariantInit(&var); + + // Get the state of whether channel swap MFX is enabled or not + hr = m_pAudioFXExtParams->pFxProperties->GetValue(PKEY_Endpoint_Enable_Channel_Swap_MFX, &var); + if (SUCCEEDED(hr) && (var.vt == VT_UI4)) + { + *pfEnabled = (var.ulVal != 0L); + } + + PropVariantClear(&var); + } + + return(hr); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CSwapPropPage::SetSwapMFXState +// +// Description: +// Enable or disable channel swap MFX +// +// Return values: +// S_OK if successful +// ---------------------------------------------------------------------------- +HRESULT CSwapPropPage::SetSwapMFXState() +{ + BOOL fCurrentState = 0; + HRESULT hr = RetrieveSwapMFXState(&fCurrentState); + IF_FAILED_JUMP(hr, Exit); + + if (fCurrentState != m_fEnableSwapMFX) + { + PROPVARIANT var; + var.vt = VT_UI4; + var.ulVal = (m_fEnableSwapMFX ? 1L : 0L); + + // Enable or disable channel swap MFX + hr = m_pAudioFXExtParams->pFxProperties->SetValue(PKEY_Endpoint_Enable_Channel_Swap_MFX, var); + + // Enabling or disabling the swap effect can be done with the engine running + // It does not invalidate anything which should be locked by IAudioProcessingObjectConfiguration::LockForProcess + // In particular, it does not impact IAudioProcessingObject::GetLatency + // So we do not need to call IAudioEndpointFormatControl::ResetToDefault here + } + +Exit: + return(hr); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CSwapPropPage::RetrieveDelaySFXState +// +// Description: +// Get the current state (enabled or disabled) of delay SFX +// +// Return values: +// S_OK if successful +// ---------------------------------------------------------------------------- +HRESULT CSwapPropPage::RetrieveDelaySFXState(BOOL *pfEnabled) +{ + HRESULT hr = E_POINTER; + + if ((m_pAudioFXExtParams != NULL) && (m_pAudioFXExtParams->pFxProperties != NULL)) + { + PROPVARIANT var; + PropVariantInit(&var); + + // Get the state of whether channel swap SFX is enabled or not + hr = m_pAudioFXExtParams->pFxProperties->GetValue(PKEY_Endpoint_Enable_Delay_SFX, &var); + if (SUCCEEDED(hr) && (var.vt == VT_UI4)) + { + *pfEnabled = (var.ulVal != 0L); + } + + PropVariantClear(&var); + } + + return(hr); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CSwapPropPage::SetSwapSFXState +// +// Description: +// Enable or disable channel swap SFX +// +// Return values: +// S_OK if successful +// ---------------------------------------------------------------------------- +HRESULT CSwapPropPage::SetDelaySFXState() +{ + BOOL fCurrentState = 0; + HRESULT hr = RetrieveDelaySFXState(&fCurrentState); + IF_FAILED_JUMP(hr, Exit); + + if (fCurrentState != m_fEnableDelaySFX) + { + PROPVARIANT var; + var.vt = VT_UI4; + var.ulVal = (m_fEnableDelaySFX ? 1L : 0L); + + // Enable or disable delay SFX + hr = m_pAudioFXExtParams->pFxProperties->SetValue(PKEY_Endpoint_Enable_Delay_SFX, var); + IF_FAILED_JUMP(hr, Exit); + + // Enabling or disabling delay changes the value that should be returned by IAudioProcessingObject::GetLatency + // This is one of the things that is locked by IAudioProcessingObjectConfiguration::LockForProcess + // So we need to call IAudioEndpointFormatControl::ResetToDefault + // This will prompt AudioDG to tear down the graph and build it up again + m_fReset = TRUE; + } + +Exit: + return(hr); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CSwapPropPage::RetrieveDelayMFXState +// +// Description: +// Get the current state (enabled or disabled) of delay MFX +// +// Return values: +// S_OK if successful +// ---------------------------------------------------------------------------- +HRESULT CSwapPropPage::RetrieveDelayMFXState(BOOL *pfEnabled) +{ + HRESULT hr = E_POINTER; + + if ((m_pAudioFXExtParams != NULL) && (m_pAudioFXExtParams->pFxProperties != NULL)) + { + PROPVARIANT var; + PropVariantInit(&var); + + // Get the state of whether channel swap MFX is enabled or not + hr = m_pAudioFXExtParams->pFxProperties->GetValue(PKEY_Endpoint_Enable_Delay_MFX, &var); + if (SUCCEEDED(hr) && (var.vt == VT_UI4)) + { + *pfEnabled = (var.ulVal != 0L); + } + + PropVariantClear(&var); + } + + return(hr); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CSwapPropPage::SetDelayMFXState +// +// Description: +// Enable or disable delay MFX +// +// Return values: +// S_OK if successful +// ---------------------------------------------------------------------------- +HRESULT CSwapPropPage::SetDelayMFXState() +{ + BOOL fCurrentState = 0; + HRESULT hr = RetrieveDelayMFXState(&fCurrentState); + IF_FAILED_JUMP(hr, Exit); + + if (fCurrentState != m_fEnableDelayMFX) + { + PROPVARIANT var; + var.vt = VT_UI4; + var.ulVal = (m_fEnableDelayMFX ? 1L : 0L); + + // Enable or disable delay MFX + hr = m_pAudioFXExtParams->pFxProperties->SetValue(PKEY_Endpoint_Enable_Delay_MFX, var); + IF_FAILED_JUMP(hr, Exit); + + // Enabling or disabling delay changes the value that should be returned by IAudioProcessingObject::GetLatency + // This is one of the things that is locked by IAudioProcessingObjectConfiguration::LockForProcess + // So we need to call IAudioEndpointFormatControl::ResetToDefault + // This will prompt AudioDG to tear down the graph and build it up again + m_fReset = TRUE; + } + +Exit: + return(hr); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CSwapPropPage::OnInitDialog +// +// Description: +// Dialog initialization routine +// +// Parameters: +// hwndDlg - [in] Handle to dialog box +// wParam - [in] Handle to control to receive the default keyboard focus +// lParam - [in] Specifies additional message-specific information +// +// Return values: +// TRUE to direct the system to set the keyboard focus to the control +// specified by wParam. Otherwise, it should return FALSE to prevent the +// system from setting the default keyboard focus. +// ---------------------------------------------------------------------------- +BOOL CSwapPropPage::OnInitDialog +( + HWND hwndDlg, + WPARAM wParam, + LPARAM lParam +) +{ + UNREFERENCED_PARAMETER(wParam); + UNREFERENCED_PARAMETER(lParam); + + HRESULT hr = S_OK; + LPWSTR pwstrEndpointName = NULL; + + // Retrieve the endpoint's friendly name, system effects, and swap SFX and MFX states + hr = GetDeviceFriendlyName(&pwstrEndpointName); + IF_FAILED_JUMP(hr, Exit); + + hr = RetrieveSysFXState(&m_fDisableSysFX); + IF_FAILED_JUMP(hr, Exit); + + hr = RetrieveSwapSFXState(&m_fEnableSwapSFX); + IF_FAILED_JUMP(hr, Exit); + + hr = RetrieveSwapMFXState(&m_fEnableSwapMFX); + IF_FAILED_JUMP(hr, Exit); + + hr = RetrieveDelaySFXState(&m_fEnableDelaySFX); + IF_FAILED_JUMP(hr, Exit); + + hr = RetrieveDelayMFXState(&m_fEnableDelayMFX); + IF_FAILED_JUMP(hr, Exit); + + // Update the property page with retrieved information + SetWindowText(GetDlgItem(hwndDlg, IDC_SPP_ENDPOINT_NAME), pwstrEndpointName); + + // Based on the retrieved states, toggle the checkboxes to reflect them + if (m_fDisableSysFX) + { + CheckDlgButton(hwndDlg, IDC_DISABLE_SYSFX, BST_CHECKED); + + // Disable APO toggling controls on the page + EnableWindow(GetDlgItem(hwndDlg, IDC_ENABLE_SWAP_SFX), FALSE); + EnableWindow(GetDlgItem(hwndDlg, IDC_ENABLE_SWAP_MFX), FALSE); + EnableWindow(GetDlgItem(hwndDlg, IDC_ENABLE_DELAY_SFX), FALSE); + EnableWindow(GetDlgItem(hwndDlg, IDC_ENABLE_DELAY_MFX), FALSE); + } + else + { + CheckDlgButton(hwndDlg, IDC_DISABLE_SYSFX, BST_UNCHECKED); + + // Enable APO toggling controls on the page + EnableWindow(GetDlgItem(hwndDlg, IDC_ENABLE_SWAP_SFX), TRUE); + EnableWindow(GetDlgItem(hwndDlg, IDC_ENABLE_SWAP_MFX), TRUE); + EnableWindow(GetDlgItem(hwndDlg, IDC_ENABLE_DELAY_SFX), TRUE); + EnableWindow(GetDlgItem(hwndDlg, IDC_ENABLE_DELAY_MFX), TRUE); + } + + if (m_fEnableSwapSFX) + { + CheckDlgButton(hwndDlg, IDC_ENABLE_SWAP_SFX, BST_CHECKED); + } + else + { + CheckDlgButton(hwndDlg, IDC_ENABLE_SWAP_SFX, BST_UNCHECKED); + } + + if (m_fEnableSwapMFX) + { + CheckDlgButton(hwndDlg, IDC_ENABLE_SWAP_MFX, BST_CHECKED); + } + else + { + CheckDlgButton(hwndDlg, IDC_ENABLE_SWAP_MFX, BST_UNCHECKED); + } + + if (m_fEnableDelaySFX) + { + CheckDlgButton(hwndDlg, IDC_ENABLE_DELAY_SFX, BST_CHECKED); + } + else + { + CheckDlgButton(hwndDlg, IDC_ENABLE_DELAY_SFX, BST_UNCHECKED); + } + + if (m_fEnableDelayMFX) + { + CheckDlgButton(hwndDlg, IDC_ENABLE_DELAY_MFX, BST_CHECKED); + } + else + { + CheckDlgButton(hwndDlg, IDC_ENABLE_DELAY_MFX, BST_UNCHECKED); + } + +Exit: + SAFE_COTASKMEMFREE(pwstrEndpointName); + return(FALSE); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CSwapPropPage::OnApply +// +// Description: +// Handle the pressing of the apply button +// +// Parameters: +// hwndDlg - [in] Handle to the dialog box +// +// Return values: +// TRUE to set keyboard focus on control +// ---------------------------------------------------------------------------- +BOOL CSwapPropPage::OnApply +( + HWND hwndDlg +) +{ + HRESULT hr = S_OK; + + // Commit the settings + hr = SetSysFXState(); + IF_FAILED_JUMP(hr, Exit); + + hr = SetSwapSFXState(); + IF_FAILED_JUMP(hr, Exit); + + hr = SetSwapMFXState(); + IF_FAILED_JUMP(hr, Exit); + + hr = SetDelaySFXState(); + IF_FAILED_JUMP(hr, Exit); + + hr = SetDelayMFXState(); + IF_FAILED_JUMP(hr, Exit); + + if (NULL != m_pAudioFXExtParams && NULL != m_pAudioFXExtParams->pFxProperties) + { + hr = m_pAudioFXExtParams->pFxProperties->Commit(); + IF_FAILED_JUMP(hr, Exit); + + if (m_fReset) + { + // something changed that forces us to reset the format support + CComPtr spEnumerator; + CComPtr spMMDevice; + + // Create device enumerator and get IMMDevice from the device ID + hr = spEnumerator.CoCreateInstance(__uuidof(MMDeviceEnumerator)); + IF_FAILED_JUMP(hr, Exit); + + hr = spEnumerator->GetDevice(m_pAudioFXExtParams->pwstrEndpointID, &spMMDevice); + IF_FAILED_JUMP(hr, Exit); + + CComPtr spEndpointFormat; + hr = spMMDevice->Activate(__uuidof(IAudioEndpointFormatControl), CLSCTX_ALL, NULL, (void **)&spEndpointFormat); + IF_FAILED_JUMP(hr, Exit); + + spEndpointFormat->ResetToDefault(ENDPOINT_FORMAT_RESET_MIX_ONLY); + m_fReset = FALSE; + } + } + +Exit: + if (SUCCEEDED(hr)) + { + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_NOERROR); + } + else + { + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_INVALID); + } + + return(TRUE); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CSwapPropPage::OnCheckBoxClickedDisableSysFX +// +// Description: +// Handle the clicking of the Disable System Effects check box +// +// Parameters: +// hwndDlg - [in] Handle to the dialog box +// +// Return values: +// FALSE to not set default keyboard focus +// ---------------------------------------------------------------------------- +BOOL CSwapPropPage::OnCheckBoxClickedDisableSysFX +( + HWND hwndDlg +) +{ + // Check the state of the check box and update associated data member + if (BST_CHECKED == IsDlgButtonChecked(hwndDlg, IDC_DISABLE_SYSFX)) + { + m_fDisableSysFX = TRUE; + + // Disable APO toggling controls on the page + EnableWindow(GetDlgItem(hwndDlg, IDC_ENABLE_SWAP_SFX), FALSE); + EnableWindow(GetDlgItem(hwndDlg, IDC_ENABLE_SWAP_MFX), FALSE); + EnableWindow(GetDlgItem(hwndDlg, IDC_ENABLE_DELAY_SFX), FALSE); + EnableWindow(GetDlgItem(hwndDlg, IDC_ENABLE_DELAY_MFX), FALSE); + } + else + { + m_fDisableSysFX = FALSE; + + // Enable APO toggling controls on the page + EnableWindow(GetDlgItem(hwndDlg, IDC_ENABLE_SWAP_SFX), TRUE); + EnableWindow(GetDlgItem(hwndDlg, IDC_ENABLE_SWAP_MFX), TRUE); + EnableWindow(GetDlgItem(hwndDlg, IDC_ENABLE_DELAY_SFX), TRUE); + EnableWindow(GetDlgItem(hwndDlg, IDC_ENABLE_DELAY_MFX), TRUE); + } + + // If the user changes the check box, enable the Apply button + SendMessage(GetParent(hwndDlg), PSM_CHANGED, (WPARAM)hwndDlg, 0); + + return(FALSE); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CSwapPropPage::OnCheckBoxClickedEnableSwapSFX +// +// Description: +// Handle the clicking of the Enable Channel Swap SFX check box +// +// Parameters: +// hwndDlg - [in] Handle to the dialog box +// +// Return values: +// FALSE to not set default keyboard focus +// ---------------------------------------------------------------------------- +BOOL CSwapPropPage::OnCheckBoxClickedEnableSwapSFX +( + HWND hwndDlg +) +{ + // Check the state of the check box and update associated data member + if (BST_CHECKED == IsDlgButtonChecked(hwndDlg, IDC_ENABLE_SWAP_SFX)) + { + m_fEnableSwapSFX = TRUE; + } + else + { + m_fEnableSwapSFX = FALSE; + } + + // If the user changes the check box, enable the Apply button + SendMessage(GetParent(hwndDlg), PSM_CHANGED, (WPARAM)hwndDlg, 0); + + return(FALSE); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CSwapPropPage::OnCheckBoxClickedEnableSwapMFX +// +// Description: +// Handle the clicking of the Enable Channel Swap MFX check box +// +// Parameters: +// hwndDlg - [in] Handle to the dialog box +// +// Return values: +// FALSE to not set default keyboard focus +// ---------------------------------------------------------------------------- +BOOL CSwapPropPage::OnCheckBoxClickedEnableSwapMFX +( + HWND hwndDlg +) +{ + // Check the state of the check box and update associated data member + if (BST_CHECKED == IsDlgButtonChecked(hwndDlg, IDC_ENABLE_SWAP_MFX)) + { + m_fEnableSwapMFX = TRUE; + } + else + { + m_fEnableSwapMFX = FALSE; + } + + // If the user changes the check box, enable the Apply button + SendMessage(GetParent(hwndDlg), PSM_CHANGED, (WPARAM)hwndDlg, 0); + + return(FALSE); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CSwapPropPage::OnCheckBoxClickedEnableDelaySFX +// +// Description: +// Handle the clicking of the Enable Delay SFX check box +// +// Parameters: +// hwndDlg - [in] Handle to the dialog box +// +// Return values: +// FALSE to not set default keyboard focus +// ---------------------------------------------------------------------------- +BOOL CSwapPropPage::OnCheckBoxClickedEnableDelaySFX +( + HWND hwndDlg +) +{ + // Check the state of the check box and update associated data member + if (BST_CHECKED == IsDlgButtonChecked(hwndDlg, IDC_ENABLE_DELAY_SFX)) + { + m_fEnableDelaySFX = TRUE; + } + else + { + m_fEnableDelaySFX = FALSE; + } + + // If the user changes the check box, enable the Apply button + SendMessage(GetParent(hwndDlg), PSM_CHANGED, (WPARAM)hwndDlg, 0); + + return(FALSE); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CSwapPropPage::OnCheckBoxClickedEnableDelayMFX +// +// Description: +// Handle the clicking of the Enable Delay MFX check box +// +// Parameters: +// hwndDlg - [in] Handle to the dialog box +// +// Return values: +// FALSE to not set default keyboard focus +// ---------------------------------------------------------------------------- +BOOL CSwapPropPage::OnCheckBoxClickedEnableDelayMFX +( + HWND hwndDlg +) +{ + // Check the state of the check box and update associated data member + if (BST_CHECKED == IsDlgButtonChecked(hwndDlg, IDC_ENABLE_DELAY_MFX)) + { + m_fEnableDelayMFX = TRUE; + } + else + { + m_fEnableDelayMFX = FALSE; + } + + // If the user changes the check box, enable the Apply button + SendMessage(GetParent(hwndDlg), PSM_CHANGED, (WPARAM)hwndDlg, 0); + + return(FALSE); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CSwapPropPage::DialogProcPage1 +// +// Description: +// Callback for property page +// +// Parameters: +// hwndDlg - [in] Handle to the dialog box +// uMsg - [in] Specifies the message +// wParam - [in] Specifies additional message-specific information +// lParam - [in] Specifies additional message-specific information +// +// Return values: +// TRUE if it processed the message, FALSE if not +// ---------------------------------------------------------------------------- +INT_PTR CALLBACK CSwapPropPage::DialogProcPage1 +( + HWND hwndDlg, + UINT uMsg, + WPARAM wParam, + LPARAM lParam +) +{ + CSwapPropPage* pthis = (CSwapPropPage*)(LONG_PTR)GetWindowLongPtr( + hwndDlg, GWLP_USERDATA); + BOOL fRet = FALSE; + + switch (uMsg) + { + case WM_INITDIALOG: + { + // Extract the context data from PROPSHEETPAGE::lParam + PROPSHEETPAGE* pSheetDesc = (PROPSHEETPAGE*)lParam; + + // Create the property page factory class +#pragma warning(push) +#pragma warning(disable: 28197) + pthis = new CComObject(); +#pragma warning(pop) + if (pthis == NULL) + { + return(FALSE); + } + + // Save this object in lParam + SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)pthis); + + // Keep audio FX extension parameters passed by the control panel + pthis->m_pAudioFXExtParams = (AudioFXExtensionParams*)pSheetDesc->lParam; + + fRet = pthis->OnInitDialog(hwndDlg, wParam, lParam); + break; + } + + case WM_NOTIFY: + { + switch (((NMHDR FAR*)lParam)->code) + { + case PSN_APPLY: + if (pthis) + { + // Apply button pressed + fRet = pthis->OnApply(hwndDlg); + } + break; + } + break; + } + + case WM_COMMAND: + { + switch (LOWORD(wParam)) + { + // Handle the clicking of the check boxes + case IDC_DISABLE_SYSFX: + if (pthis) + { + fRet = pthis->OnCheckBoxClickedDisableSysFX(hwndDlg); + } + break; + + case IDC_ENABLE_SWAP_SFX: + if (pthis) + { + fRet = pthis->OnCheckBoxClickedEnableSwapSFX(hwndDlg); + } + break; + + case IDC_ENABLE_SWAP_MFX: + if (pthis) + { + fRet = pthis->OnCheckBoxClickedEnableSwapMFX(hwndDlg); + } + break; + + case IDC_ENABLE_DELAY_SFX: + if (pthis) + { + fRet = pthis->OnCheckBoxClickedEnableDelaySFX(hwndDlg); + } + break; + + case IDC_ENABLE_DELAY_MFX: + if (pthis) + { + fRet = pthis->OnCheckBoxClickedEnableDelayMFX(hwndDlg); + } + break; + } + break; + } + + case WM_DESTROY: + { + SAFE_DELETE(pthis); + SetWindowLongPtr(hwndDlg, GWLP_USERDATA, NULL); + fRet = TRUE; + break; + } + } + + return(fRet); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CSwapPropPage::PropSheetPageProc +// +// Description: +// Callback that gets invoked right after page creation or right before +// before page destruction +// +// Parameters: +// hwnd - Reserved; must be NULL +// uMsg - [in] Action flag. PSPCB_ADDREF, PSPCB_CREATE, or PSPCB_RELEASE +// ppsp - [in, out] Pointer to a PROPSHEETPAGE structure that defines +// the page being created or destroyed. +// +// Return values: +// Depends on the value of the uMsg parameter +// ---------------------------------------------------------------------------- +UINT CALLBACK CSwapPropPage::PropSheetPageProc +( + HWND hwnd, + UINT uMsg, + LPPROPSHEETPAGE ppsp +) +{ + UNREFERENCED_PARAMETER(hwnd); + UNREFERENCED_PARAMETER(uMsg); + UNREFERENCED_PARAMETER(ppsp); + + // if (uMsg == PSPCB_CREATE) ... + return(1); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CSwapPropPage::Initialize +// +// Description: +// Implementation of IShellExtInit::Initialize. Initializes a property +// sheet extension, shortcut menu extension, or drag-and-drop handler. +// +// Parameters: +// pidlFolder - [in] Address of an ITEMIDLIST structure that uniquely +// identifies a folder. For property sheet extensions, +// this parameter is NULL. +// pdtobj - [out] Address of an IDataObject interface object that can be +// used to retrieve the objects being acted upon. +// hkeyProgID - [in] Registry key for the file object or folder type. +// +// Return values: +// Returns NOERROR if successful, or an OLE-defined error value otherwise +// ---------------------------------------------------------------------------- +_Use_decl_annotations_ +HRESULT CSwapPropPage::Initialize +( + LPCITEMIDLIST pidlFolder, + IDataObject* pdtobj, + HKEY hkeyProgID +) +{ + UNREFERENCED_PARAMETER(pidlFolder); + UNREFERENCED_PARAMETER(pdtobj); + UNREFERENCED_PARAMETER(hkeyProgID); + + return(S_OK); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CSwapPropPage::AddPages +// +// Description: +// Implementation of IShellPropSheetExt::AddPages. Adds one or more pages +// to a property sheet that the Shell displays for a file object. +// +// Parameters: +// lpfnAddPage - [in] Address of a function that the property sheet +// handler calls to add a page to the property sheet. The +// function takes a property sheet handle returned by the +// CreatePropertySheetPage function and the lParam parameter +// passed to the AddPages method. +// lParam - [in] Parameter to pass to the function specified by the +// lpfnAddPage method. +// +// Return values: +// Returns S_OK if successful. If the method fails, an OLE-defined error +// code is returned +// ---------------------------------------------------------------------------- +_Use_decl_annotations_ +HRESULT STDMETHODCALLTYPE CSwapPropPage::AddPages +( + LPFNADDPROPSHEETPAGE lpfnAddPage, // See PrSht.h + LPARAM lParam // Used by caller, don't modify +) +{ + HRESULT hr = S_OK; + PROPSHEETPAGE psp; + HPROPSHEETPAGE hPage1 = NULL; + AudioFXExtensionParams* pAudioFXParams = (AudioFXExtensionParams*)lParam; +#pragma warning(push) +#pragma warning(disable: 28197) + AudioFXExtensionParams* pAudioFXParamsCopy = new AudioFXExtensionParams; +#pragma warning(pop) + + if (pAudioFXParamsCopy == NULL) + { + return E_OUTOFMEMORY; + } + + // Make a copy of the params + CopyMemory(pAudioFXParamsCopy, pAudioFXParams, sizeof(AudioFXExtensionParams)); + SAFE_ADDREF(pAudioFXParamsCopy->pFxProperties); + + // Initialize property page params and create page + psp.dwSize = sizeof(psp); + psp.dwFlags = PSP_USEREFPARENT | PSP_USECALLBACK; + psp.hInstance = _AtlBaseModule.GetModuleInstance(); + psp.hIcon = 0; + psp.pcRefParent = (UINT*)&m_dwRef; + psp.lParam = (LPARAM)pAudioFXParamsCopy; + psp.pszTemplate = MAKEINTRESOURCE(IDD_SWAP_PROP_PAGE); + psp.pfnDlgProc = (DLGPROC)DialogProcPage1; + psp.pfnCallback = PropSheetPageProc; + + // Create the property sheet page and add the page + hPage1 = CreatePropertySheetPage(&psp); + if (hPage1) + { + if (!lpfnAddPage(hPage1, pAudioFXParams->AddPageParam)) + { + hr = E_FAIL; + delete pAudioFXParamsCopy; + DestroyPropertySheetPage(hPage1); + } + else + { + // Add ref for page + this->AddRef(); + } + } + else + { + delete pAudioFXParamsCopy; + hr = E_OUTOFMEMORY; + } + + return(hr); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CSwapPropPage::ReplacePage +// +// Description: +// Implementation of IShellPropSheetExt::ReplacePage. Replaces a page in +// a property sheet for a Control Panel object. +// +// Parameters: +// uPageID - [in] Identifier of the page to replace +// lpfnReplacePage - [in] Address of a function that the property sheet +// handler calls to replace a page to the property +// sheet. The function takes a property sheet handle +// returned by the CreatePropertySheetPage function and +// the lParam parameter passed to the ReplacePage +// method. +// lParam - [in] Parameter to pass to the function specified by the +// lpfnReplacePage parameter. +// +// Return values: +// Returns NOERROR if successful, or an OLE-defined error value otherwise +// ---------------------------------------------------------------------------- +_Use_decl_annotations_ +HRESULT STDMETHODCALLTYPE CSwapPropPage::ReplacePage +( + UINT uPageID, + LPFNSVADDPROPSHEETPAGE lpfnReplaceWith, + LPARAM lParam +) +{ + UNREFERENCED_PARAMETER(uPageID); + UNREFERENCED_PARAMETER(lpfnReplaceWith); + UNREFERENCED_PARAMETER(lParam); + + return(S_FALSE); +} diff --git a/audio/sysvad/SwapAPO/PropPageExtensions/SwapPropPage.h b/audio/sysvad/SwapAPO/PropPageExtensions/SwapPropPage.h new file mode 100644 index 000000000..295e29bce --- /dev/null +++ b/audio/sysvad/SwapAPO/PropPageExtensions/SwapPropPage.h @@ -0,0 +1,92 @@ +//**@@@*@@@**************************************************** +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation. All rights reserved. +// +//**@@@*@@@**************************************************** + +// +// FileName: SwapPropPage.h +// +// Abstract: Declaration of the CSwapPropPage class +// +// ---------------------------------------------------------------------------- + + +#pragma once + +// ---------------------------------------------------------------------- +// CSwapPropPage class +// +class ATL_NO_VTABLE CSwapPropPage : + public CComObjectRootEx, + public CComCoClass, + public IDispatchImpl, + public IShellExtInit, + public IShellPropSheetExt +{ +public: + CSwapPropPage(); + ~CSwapPropPage(); + + DECLARE_REGISTRY_RESOURCEID(IDR_SWAP_PROP_PAGE) + + BEGIN_COM_MAP(CSwapPropPage) + COM_INTERFACE_ENTRY(ISwapPropPage) + COM_INTERFACE_ENTRY(IDispatch) + COM_INTERFACE_ENTRY(IShellExtInit) + COM_INTERFACE_ENTRY(IShellPropSheetExt) + END_COM_MAP() + + DECLARE_PROTECT_FINAL_CONSTRUCT() + + HRESULT FinalConstruct() + { + return S_OK; + } + + void FinalRelease() + { + } + + static INT_PTR CALLBACK DialogProcPage1(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); + static UINT CALLBACK PropSheetPageProc(HWND hwnd, UINT uMsg, LPPROPSHEETPAGE ppsp); + + // IShellExtInit + STDMETHOD(Initialize)(_In_opt_ LPCITEMIDLIST pidlFolder, _In_opt_ IDataObject* pdtobj, _In_opt_ HKEY hkeyProgID); + + // IShellPropSheetExt + STDMETHOD(AddPages)(_In_ LPFNADDPROPSHEETPAGE lpfnAddPage, _In_ LPARAM lParam); + STDMETHOD(ReplacePage)(_In_ UINT uPageID, _In_ LPFNSVADDPROPSHEETPAGE lpfnReplaceWith, _In_ LPARAM lParam); + +private: + AudioFXExtensionParams* m_pAudioFXExtParams; + BOOL m_fDisableSysFX; + BOOL m_fEnableSwapSFX; + BOOL m_fEnableSwapMFX; + BOOL m_fEnableDelaySFX; + BOOL m_fEnableDelayMFX; + BOOL m_fReset; + + HRESULT GetDeviceFriendlyName(_Outptr_result_maybenull_ LPWSTR* ppNameOut); + HRESULT RetrieveSysFXState(BOOL *pfDisabled); + HRESULT SetSysFXState(); + HRESULT RetrieveSwapSFXState(BOOL *pfEnabled); + HRESULT SetSwapSFXState(); + HRESULT RetrieveSwapMFXState(BOOL *pfEnabled); + HRESULT SetSwapMFXState(); + HRESULT RetrieveDelaySFXState(BOOL *pfEnabled); + HRESULT SetDelaySFXState(); + HRESULT RetrieveDelayMFXState(BOOL *pfEnabled); + HRESULT SetDelayMFXState(); + + BOOL OnInitDialog(HWND hwndDlg, WPARAM wParam, LPARAM lParam); + BOOL OnApply(HWND hwndDlg); + BOOL OnCheckBoxClickedDisableSysFX(HWND hwndDlg); + BOOL OnCheckBoxClickedEnableSwapSFX(HWND hwndDlg); + BOOL OnCheckBoxClickedEnableSwapMFX(HWND hwndDlg); + BOOL OnCheckBoxClickedEnableDelaySFX(HWND hwndDlg); + BOOL OnCheckBoxClickedEnableDelayMFX(HWND hwndDlg); +}; + +OBJECT_ENTRY_AUTO(__uuidof(SwapPropPage), CSwapPropPage) diff --git a/audio/sysvad/SwapAPO/PropPageExtensions/SwapPropPage.rgs b/audio/sysvad/SwapAPO/PropPageExtensions/SwapPropPage.rgs new file mode 100644 index 000000000..6ede57a10 --- /dev/null +++ b/audio/sysvad/SwapAPO/PropPageExtensions/SwapPropPage.rgs @@ -0,0 +1,27 @@ +HKCR +{ + CplExt.SwapPropPage.1 = s 'SwapPropPage Class' + { + CLSID = s '{19166F23-5F08-47F9-BB57-9F57A977D88E}' + } + CplExt.SwapPropPage = s 'SwapPropPage Class' + { + CLSID = s '{19166F23-5F08-47F9-BB57-9F57A977D88E}' + CurVer = s 'CplExt.SwapPropPage.1' + } + NoRemove CLSID + { + ForceRemove {19166F23-5F08-47F9-BB57-9F57A977D88E} = s 'SwapPropPage Class' + { + ProgID = s 'CplExt.SwapPropPage.1' + VersionIndependentProgID = s 'CplExt.SwapPropPage' + ForceRemove 'Programmable' + InprocServer32 = s '%MODULE%' + { + val ThreadingModel = s 'Apartment' + } + val AppID = s '%APPID%' + 'TypeLib' = s '{59390583-ED40-46D9-9B16-7800B9CC5CC2}' + } + } +} diff --git a/audio/sysvad/SwapAPO/PropPageExtensions/TopologyExaminers.cpp b/audio/sysvad/SwapAPO/PropPageExtensions/TopologyExaminers.cpp new file mode 100644 index 000000000..b224f4c19 --- /dev/null +++ b/audio/sysvad/SwapAPO/PropPageExtensions/TopologyExaminers.cpp @@ -0,0 +1,162 @@ +//**@@@*@@@**************************************************** +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation. All rights reserved. +// +//**@@@*@@@**************************************************** + +// +// FileName: TopologyExaminers.cpp +// +// Description: +// +// -------------------------------------------------------------------------------- + + +#include "stdafx.h" +#include "DeviceTopology.h" +#include "TopologyExaminers.h" + +_Analysis_mode_(_Analysis_code_type_user_driver_) + +// ---------------------------------------------------------------------------- +// Function: +// CFormatExaminer::CFormatExaminer +// +// Description: +// CFormatExaminer constructor +// ---------------------------------------------------------------------------- +CFormatExaminer::CFormatExaminer +( + PKSDATAFORMAT pKsFormat, + ULONG cbFormat +) +: m_bSatisfied(FALSE), + m_pFormatReq(NULL) +{ + ATLASSERT(pKsFormat); + ATLASSERT(cbFormat >= sizeof(KSDATAFORMAT)); + + // If any of this fails, then Examine will fail + if (cbFormat >= sizeof(KSDATAFORMAT)) + { + m_pFormatReq = (PKSDATAFORMAT)(new BYTE[cbFormat]); + CopyMemory(m_pFormatReq, pKsFormat, cbFormat); + if (cbFormat < m_pFormatReq->FormatSize) + m_pFormatReq->FormatSize = cbFormat; + } +} + + +// ---------------------------------------------------------------------------- +// Function: +// CFormatExaminer::~CFormatExaminer +// +// Description: +// CFormatExaminer destructor +// ---------------------------------------------------------------------------- +CFormatExaminer::~CFormatExaminer() +{ + SAFE_DELETE_ARRAY(m_pFormatReq); +} + + +// ---------------------------------------------------------------------- +// Function: +// CFormatExaminer::Examine +// +// Description: +// Implementation of IExaminer::Examine. This method checks the supplied +// path an IConnector that support the KS streaming format (KSDATAFORMAT) +// that this examiner was set up to look for. +// +// Parameters: +// pPath - [in] List of parts to examine +// +// Remarks: +// This examiner only cares about the first part in the list, which is +// always a connector or an endpoint. Therefore it only needs to make +// sure that there is a zeroth element in the list and examine that. +// +// Return values: +// S_OK if successful +// ---------------------------------------------------------------------- +HRESULT CFormatExaminer::Examine +( + IPartsList* pPath +) +{ + ATLASSERT(pPath != NULL); + + HRESULT hr = S_OK; + UINT cParts; + CComPtr spPart; + + // Make sure there is at least 1 part + hr = pPath->GetCount(&cParts); + IF_FAILED_JUMP(hr, Exit); + + IF_TRUE_ACTION_JUMP((cParts == 0), hr = E_NOTFOUND, Exit); + + // This examiner only cares about Connectors so don't bother walking the + // path, just look at the last in the list, which should be a connector. + hr = pPath->GetPart(0, &spPart); + IF_FAILED_JUMP(hr, Exit); + + hr = ExaminePart(spPart); + +Exit: + return hr; +} + + +// ---------------------------------------------------------------------- +// Function: +// CFormatExaminer::ExaminePart +// +// Description: +// Tests a single IPart against the examination criteria, namely be a +// connector that supports whatever KS Streaming format (KSDATAFORMAT) +// this examiner was set up to look for +// +// Parameters: +// pIPart - [in] Part to examine +// +// Remarks: +// 1) Make sure the connector is of type Software_IO +// 2) Activate IKsFormatSupport on the connector +// 3) Call IKsFormatSupport::IsFormatSupported(m_pFormatReq). +// +// ---------------------------------------------------------------------- +HRESULT CFormatExaminer::ExaminePart +( + IPart* pIPart +) +{ + ATLASSERT(pIPart); + + HRESULT hr = S_OK; + + // 1) Check category and make sure it is a software_IO pin + ConnectorType conType; + CComPtr spConnector; + CComPtr spFormatSupport; + + hr = pIPart->QueryInterface(__uuidof(IConnector), (void**)&spConnector); + IF_FAILED_JUMP(hr, Exit); + + hr = spConnector->GetType(&conType); + IF_FAILED_JUMP(hr, Exit); + + IF_TRUE_ACTION_JUMP((conType != Software_IO), hr = E_NOTFOUND, Exit); + + // 2) Activate IKsFormatSupport interface + hr = pIPart->Activate(CLSCTX_INPROC_SERVER, __uuidof(IKsFormatSupport), (void**)&spFormatSupport); + IF_FAILED_JUMP(hr, Exit); + + // 3) Ask the interface if this format is supported + hr = spFormatSupport->IsFormatSupported(m_pFormatReq, m_pFormatReq->FormatSize, &m_bSatisfied); + +Exit: + return hr; +} diff --git a/audio/sysvad/SwapAPO/PropPageExtensions/TopologyExaminers.h b/audio/sysvad/SwapAPO/PropPageExtensions/TopologyExaminers.h new file mode 100644 index 000000000..eafd52f09 --- /dev/null +++ b/audio/sysvad/SwapAPO/PropPageExtensions/TopologyExaminers.h @@ -0,0 +1,50 @@ +//**@@@*@@@**************************************************** +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation. All rights reserved. +// +//**@@@*@@@**************************************************** + +// +// FileName: TopologyExaminers.h +// +// Abstract: An interface and supporting class(es) for examining device +// model IParts +// +// ---------------------------------------------------------------------------- + +#pragma once + + +// ---------------------------------------------------------------------- +// interface IExaminer +// An interface for examining devicemodel IParts +// ---------------------------------------------------------------------- +interface IExaminer +{ +public: + virtual HRESULT Examine(IPartsList* pPath) = 0; + virtual BOOL IsSatisfied() = 0; +}; + + +// -------------------------------------------------------------------------------- +// class CFormatExaminer +// A class that examines IConnectors looking for a soft connector that supports +// a given KS streaming format (KSDATAFORMAT). +// ---------------------------------------------------------------------- +class CFormatExaminer : public IExaminer +{ +private: + PKSDATAFORMAT m_pFormatReq; + BOOL m_bSatisfied; + + HRESULT ExaminePart(IPart* pIPart); + +public: + CFormatExaminer(PKSDATAFORMAT pKsFormat, ULONG cbFormat); + ~CFormatExaminer(); + + HRESULT Examine(IPartsList* pPath); + BOOL IsSatisfied() { return m_bSatisfied; } +}; diff --git a/audio/sysvad/SwapAPO/PropPageExtensions/UIWidgets.cpp b/audio/sysvad/SwapAPO/PropPageExtensions/UIWidgets.cpp new file mode 100644 index 000000000..9dc272af6 --- /dev/null +++ b/audio/sysvad/SwapAPO/PropPageExtensions/UIWidgets.cpp @@ -0,0 +1,321 @@ +//**@@@*@@@**************************************************** +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation. All rights reserved. +// +//**@@@*@@@**************************************************** + +// +// FileName: UIWidgets.cpp +// +// Abstract: Implementation of CUIWidget and derived classes +// +// ---------------------------------------------------------------------------- + + +#include "stdafx.h" +#include +#include "UIWidgets.h" + +_Analysis_mode_(_Analysis_code_type_user_driver_) + +// ---------------------------------------------------------------------- +// CUIWidget base class + + +// ---------------------------------------------------------------------------- +// Function: +// CUIWidget::CUIWidget +// +// Description: +// CUIWidget constructor +// ---------------------------------------------------------------------------- +CUIWidget::CUIWidget +( + IPart* pPart, + REFIID iid +) +: m_hwndParentDlg(NULL), + m_iid(iid), + m_wstrLabel(NULL), + m_bRegisteredForCallbacks(NULL) +{ + // This is done here rather than in the initializer list to avoid a prefix error + if (pPart != NULL) + { + m_spPart = pPart; + } + + m_dwProcessId = GetCurrentProcessId(); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CUIWidget::~CUIWidget +// +// Description: +// CUIWidget destructor +// ---------------------------------------------------------------------------- +CUIWidget::~CUIWidget() +{ + SAFE_COTASKMEMFREE(m_wstrLabel); + + if (m_bRegisteredForCallbacks) + { + m_spPart->UnregisterControlChangeCallback(this); + } +} + + +// ---------------------------------------------------------------------------- +// Function: +// CUIWidget::Create +// +// Description: +// -- Activates the control interface on the specified part +// -- Registers for control change callbacks +// +// Parameters: +// hwndDlg - [in] Parent dialog of the widget +// rc - [in] Not used +// ctrlIdBase - [in] Not used +// +// Return values: +// S_OK if successful +// ---------------------------------------------------------------------------- +HRESULT CUIWidget::Create +( + HWND hwndDlg, + SRECT& /*rc*/, + UINT& /*ctrlIdBase*/ +) +{ + HRESULT hr; + + m_hwndParentDlg = hwndDlg; + + RPC_STATUS rpcs = UuidCreate(&m_guidEventContext); + if (rpcs != RPC_S_OK) + { + hr = HRESULT_FROM_WIN32(rpcs); + goto Exit; + } + + hr = m_spPart->Activate(CLSCTX_INPROC_SERVER, m_iid, (void**)&m_spControl); + IF_FAILED_JUMP(hr, Exit); + + hr = m_spPart->RegisterControlChangeCallback(m_iid, this); + IF_FAILED_JUMP(hr, Exit); + + m_bRegisteredForCallbacks = TRUE; + + hr = m_spPart->GetName(&m_wstrLabel); + +Exit: + return hr; +}; + + +// ---------------------------------------------------------------------- +// Function: +// CUIWidget::OnNotify +// +// Description: +// Implementation of IControlChangeNotify::OnNotify +// Called when someone calls a "Set" method on a control interface. +// +// Parameters: +// dwProcessId - [in] Process ID +// pguidEventContext - Event context guid +// +// Return: +// S_OK always +// ---------------------------------------------------------------------- +_Use_decl_annotations_ +HRESULT CUIWidget::OnNotify +( + DWORD dwProcessId, + LPCGUID pguidEventContext +) +{ + UNREFERENCED_PARAMETER(dwProcessId); + + if ((pguidEventContext == NULL) || + (*pguidEventContext != m_guidEventContext)) + { + // Delegate to derived class + HandleControlChangeNotification(); + } + + return S_OK; +} + + +// ---------------------------------------------------------------------- +// ---------------------------------------------------------------------- +// CBooleanWidget + + +// ---------------------------------------------------------------------------- +// Function: +// CBooleanWidget::CBooleanWidget +// +// Description: +// CBooleanWidget constructor +// ---------------------------------------------------------------------------- +CBooleanWidget::CBooleanWidget +( + IPart* pPart, + REFIID iid +) +: CUIWidget(pPart, iid), + m_hwndCheckbox(NULL), + m_bCurrentValue(FALSE) +{ +} + + +// ---------------------------------------------------------------------------- +// Function: +// CBooleanWidget::Create +// +// Description: +// Creates the widget which is a dialog +// +// Parameters: +// hwndDlg - [in] Parent dialog of the widget +// rc - [in] Position of the widget +// nCtrlId - [in] Child-window identifier +// +// Return values: +// S_OK if successful +// ---------------------------------------------------------------------------- +HRESULT CBooleanWidget::Create +( + HWND hwndDlg, + SRECT& rc, + UINT& nCtrlId +) +{ + HRESULT hr; + HFONT hFont; + DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_AUTOCHECKBOX; + + // Activate the control interface and register for callbacks + hr = CUIWidget::Create(hwndDlg, rc, nCtrlId); + IF_FAILED_JUMP(hr, Exit); + + // Create a check box + rc.w = 300; + rc.h = 20; + m_hwndCheckbox = CreateWindowEx( + 0, //WS_EX_STATICEDGE, + WC_BUTTON, + m_wstrLabel, + dwStyle, + rc.x, rc.y, // put at (x,y) + rc.w, rc.h, + m_hwndParentDlg, + (HMENU)(UINT_PTR)nCtrlId, + NULL, + 0); + IF_TRUE_ACTION_JUMP((m_hwndCheckbox == NULL), hr = HRESULT_FROM_WIN32(GetLastError()), Exit); + + // Make the new control use the same font as the dialog + hFont = Window_GetFont(hwndDlg); + Window_SetFont(m_hwndCheckbox, hFont); + + // Get the current value + hr = HandleControlChangeNotification(); + IF_FAILED_JUMP(hr, Exit); + +Exit: + return hr; +} + + +// ---------------------------------------------------------------------- +// Function: +// CBooleanWidget::HandleControlChangeNotification() +// +// Description: +// Handles widget change notifications +// +// Return: +// S_OK if successful +// ---------------------------------------------------------------------- +HRESULT CBooleanWidget::HandleControlChangeNotification() +{ + HRESULT hr = S_OK; + + ATLASSERT(m_spControl); + ATLASSERT(m_hwndCheckbox); + + // Set value on DeviceTopology control interface + if (m_iid == __uuidof(IAudioLoudness)) + { + CComQIPtr spLoudness = m_spControl; + hr = spLoudness->GetEnabled(&m_bCurrentValue); + } + else if (m_iid == __uuidof(IAudioAutoGainControl)) + { + CComQIPtr spAGC = m_spControl; + hr = spAGC->GetEnabled(&m_bCurrentValue); + } + else if (m_iid == __uuidof(IDeviceSpecificProperty)) + { + CComQIPtr spBoolCtrl = m_spControl; + DWORD cbValue = sizeof(m_bCurrentValue); + + hr = spBoolCtrl->GetValue(&m_bCurrentValue, &cbValue); + } + + // Set the check box to reflect the current value + SendMessage(m_hwndCheckbox, BM_SETCHECK, (WPARAM)(int)(m_bCurrentValue), 0L); + + return hr; +} + + +// ---------------------------------------------------------------------------- +// Function: +// CBooleanWidget::CommitValue +// +// Description: +// Commit the value set on the control +// +// Return values: +// S_OK if successful +// ---------------------------------------------------------------------------- +HRESULT CBooleanWidget::CommitValue() +{ + HRESULT hr = S_OK; + + ATLASSERT(m_spControl); + ATLASSERT(m_hwndCheckbox); + + // Get current value from check box + m_bCurrentValue = ((int)(DWORD)SendMessage(m_hwndCheckbox, BM_GETCHECK, 0L, 0L)); + + // Set value on DeviceTopology control interface + if (m_iid == __uuidof(IAudioLoudness)) + { + CComQIPtr spLoudness = m_spControl; + hr = spLoudness->SetEnabled(m_bCurrentValue, GetEventContext()); + } + else if (m_iid == __uuidof(IAudioAutoGainControl)) + { + CComQIPtr spAGC = m_spControl; + hr = spAGC->SetEnabled(m_bCurrentValue, GetEventContext()); + } + else if (m_iid == __uuidof(IDeviceSpecificProperty)) + { + CComQIPtr spBoolCtrl = m_spControl; + + hr = spBoolCtrl->SetValue(&m_bCurrentValue, sizeof(m_bCurrentValue), + GetEventContext()); + } + + return hr; +}; diff --git a/audio/sysvad/SwapAPO/PropPageExtensions/UIWidgets.h b/audio/sysvad/SwapAPO/PropPageExtensions/UIWidgets.h new file mode 100644 index 000000000..1f807bd43 --- /dev/null +++ b/audio/sysvad/SwapAPO/PropPageExtensions/UIWidgets.h @@ -0,0 +1,405 @@ +//**@@@*@@@**************************************************** +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation. All rights reserved. +// +//**@@@*@@@**************************************************** + +// +// FileName: UIWidgets.h +// +// Abstract: Declaration of CUIWidget and derived classes +// +// ---------------------------------------------------------------------------- + + +#pragma once + + +// ---------------------------------------------------------------------------- +// Class: +// CUIWidget +// +// Description: +// Base class for different types of widgets +// +// ---------------------------------------------------------------------------- +class CUIWidget : public IControlChangeNotify +{ +protected: + HWND m_hwndParentDlg; + IID m_iid; + CComPtr m_spPart; + CComPtr m_spControl; + DWORD m_dwProcessId; + LPWSTR m_wstrLabel; + BOOL m_bRegisteredForCallbacks; + GUID m_guidEventContext; + +public: + CUIWidget(IPart* pPart, REFIID iid); + virtual ~CUIWidget(); + + virtual HRESULT Create(HWND hwndDlg, SRECT& rc, UINT& nCtrlIdBase); + virtual HRESULT HandleControlChangeNotification() = 0; + virtual BOOL OwnsCtrlId(int ctrlId) + { + UNREFERENCED_PARAMETER(ctrlId); + return FALSE; + } + virtual HRESULT OnClick() { return S_OK; } + virtual HRESULT OnHScroll() { return S_OK; } + virtual LPCGUID GetEventContext() { return &m_guidEventContext; } + virtual HRESULT CommitValue() { return S_OK; } + + // IControlChangeNotify + STDMETHOD(OnNotify)(_In_ DWORD dwProcessId, _In_opt_ LPCGUID pguidEventContext); + + // IUnknown (since we implement IControlChangeNotify) + HRESULT STDMETHODCALLTYPE QueryInterface(const IID& iid, void** ppUnk) + { + UNREFERENCED_PARAMETER(iid); + + *ppUnk = NULL; + return E_NOTIMPL; + } + ULONG STDMETHODCALLTYPE AddRef(void) + { return 0; } + ULONG STDMETHODCALLTYPE Release(void) + { return 0; } +}; + + +// ---------------------------------------------------------------------------- +// Class: +// CBooleanWidget +// +// Description: +// Handles IAudioLoudness, IAudioAutoGainControl and +// IGenericBooleanProperty +// +// ---------------------------------------------------------------------------- +class CBooleanWidget : public CUIWidget +{ +protected: + BOOL m_bCurrentValue; + HWND m_hwndCheckbox; + +public: + CBooleanWidget(IPart* pPart, REFIID iid); + + HRESULT Create(HWND hwndDlg, SRECT& rc, UINT& nCtrlIdBase); // virtual + + virtual HRESULT HandleControlChangeNotification(); + virtual BOOL OwnsCtrlId(int ctrlId) + { return (ctrlId == GetDlgCtrlID(m_hwndCheckbox)); } + HRESULT CommitValue(); +}; + + +// ---------------------------------------------------------------------------- +// TODO: Underlying plumbing for the LONG and ULOG types aren't implemented +// yet in audiocore. Once that's implemented and verified working, need to +// remove #if TEST stuff +// ---------------------------------------------------------------------------- + +// ---------------------------------------------------------------------------- +// Class: +// CLongWidget +// +// Description: +// Handles IDeviceSpecificProperty +// ---------------------------------------------------------------------------- +template +class CLongWidget : public CUIWidget +{ +public: + CLongWidget(IPart* pPart); + ~CLongWidget(); + virtual HRESULT Create(HWND hwndDlg, SRECT& rc, UINT& ctrlIdBase); + virtual HRESULT HandleControlChangeNotification(); + virtual HRESULT CommitValue(); + +private: + HWND m_hwnd; + HWND m_hwndLabel; + HWND m_hwndTrackbar; + HWND m_hwndValue; + _TYPE_ m_Value; + _TYPE_ m_Min; + _TYPE_ m_Max; + LONG m_Stepping; + CComQIPtr m_spDevSpecControl; + + static INT_PTR CALLBACK DlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam); + void UpdateValueText(); +}; + + +// ---------------------------------------------------------------------------- +// ---------------------------------------------------------------------------- +// Implementation of CLongWidget + + +// ---------------------------------------------------------------------------- +// Function: +// CLongWidget<_TYPE_>::CLongWidget +// +// Description: +// CLongWidget<_TYPE_> constructor +// ---------------------------------------------------------------------------- +template +CLongWidget<_TYPE_>::CLongWidget(IPart* pPart) +: CUIWidget(pPart, __uuidof(IDeviceSpecificProperty)), + m_hwndTrackbar(NULL), + m_hwndLabel(NULL), + m_Value(0), + m_Min(0), + m_Max(0), + m_Stepping(1) +{ +} + + +// ---------------------------------------------------------------------------- +// Function: +// CLongWidget<_TYPE_>::CLongWidget +// +// Description: +// CLongWidget<_TYPE_> destructor +// ---------------------------------------------------------------------------- +template +CLongWidget<_TYPE_>::~CLongWidget() +{ + DestroyWindow(m_hwnd); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CLongWidget<_TYPE_>::Create +// +// Description: +// Creates the widget which is a dialog +// +// Parameters: +// hwndDlg - [in] Parent dialog of the widget +// rc - [in] Position of the widget +// ctrlIdBase - [in] Not used +// +// Return values: +// S_OK if successful +// ---------------------------------------------------------------------------- +template +HRESULT CLongWidget<_TYPE_>::Create +( + HWND hwndDlg, + SRECT& rc, + UINT& ctrlIdBase +) +{ + HRESULT hr = S_OK; + HFONT hFont; + RECT rcClient; + LONG lMin, lMax; + + // Activate the control interface and register for callbacks + hr = CUIWidget::Create(hwndDlg, rc, ctrlIdBase); + IF_FAILED_JUMP(hr, Exit); + + // QI for specific interface + m_spDevSpecControl = m_spControl; + + // Create dialog + m_hwnd = CreateDialogParam( g_hInstance, + MAKEINTRESOURCE(IDD_WIDGET_GEN_LONG), + hwndDlg, + CLongWidget<_TYPE_>::DlgProc, + (LPARAM)this); + + IF_TRUE_ACTION_JUMP((m_hwnd == NULL), hr = HRESULT_FROM_WIN32(GetLastError()), Exit); + + SetWindowPos(m_hwnd, NULL, rc.x, rc.y, 0, 0, SWP_NOZORDER | SWP_NOSIZE); + GetClientRect(m_hwnd, &rcClient); + rc.w = rcClient.right - rcClient.left; + rc.h = rcClient.bottom - rcClient.top; + + // Make the new controls use the same font as the dialog + hFont = Window_GetFont(hwndDlg); + + m_hwndLabel = GetDlgItem(m_hwnd, IDC_GEN_LABEL); + m_hwndTrackbar = GetDlgItem(m_hwnd, IDC_GEN_SLIDER); + m_hwndValue = GetDlgItem(m_hwnd, IDC_GEN_VALUE); + + Window_SetFont(m_hwndValue, hFont); + Window_SetFont(m_hwndLabel, hFont); + SetWindowText(m_hwndLabel, m_wstrLabel); + + // Set value text + hr = m_spDevSpecControl->Get4BRange(&lMin, &lMax, &m_Stepping); + IF_FAILED_JUMP(hr, Exit); + + m_Min = (_TYPE_)lMin; + m_Max = (_TYPE_)lMax; + + // Set trackbar range and ticks + TrackBar_SetRange(m_hwndTrackbar, 0, 100); + TrackBar_SetTickFrequency(m_hwndTrackbar, 20); // 5 ticks + + // Set trackbar position + HandleControlChangeNotification(); + + // Update the numeric representation of the trackbar + UpdateValueText(); + +Exit: + return(hr); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CLongWidget<_TYPE_>::HandleControlChangeNotification +// +// Description: +// Handles widget change notifications +// +// Return values: +// S_OK if successful +// ---------------------------------------------------------------------------- +template +HRESULT CLongWidget<_TYPE_>::HandleControlChangeNotification() +{ + ATLASSERT(m_spDevSpecControl); + ATLASSERT(m_hwndTrackbar); + + HRESULT hr = S_OK; + DWORD cbValue = sizeof(m_Value); + + // Get current value + hr = m_spDevSpecControl->GetValue(&m_Value, &cbValue); + + if (SUCCEEDED(hr)) + { + float fPos = (float)(m_Value - m_Min) / (float)(m_Max - m_Min); + + // Update trackbar position + TrackBar_SetPos(m_hwndTrackbar, (int)(fPos * 100.f)); + } + + return(hr); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CLongWidget<_TYPE_>::CommitValue +// +// Description: +// Commit the value set on the control +// +// Return values: +// S_OK if successful +// ---------------------------------------------------------------------------- +template +HRESULT CLongWidget<_TYPE_>::CommitValue() +{ + ATLASSERT(m_spDevSpecControl); + ATLASSERT(m_hwndTrackbar); + + HRESULT hr = S_OK; + float fPos; + DWORD cbValue = sizeof(m_Value); + + // Calculate new value from trackbar position + fPos = (float)TrackBar_GetPos(m_hwndTrackbar) / 100.f; + m_Value = (_TYPE_)((float)(m_Max - m_Min) * fPos) + m_Min; + + // Commit current setting of trackbar + hr = m_spDevSpecControl->SetValue(&m_Value, cbValue, GetEventContext()); + + return(hr); +}; + + +// ---------------------------------------------------------------------------- +// Function: +// CLongWidget<_TYPE_>::DlgProc +// +// Description: +// Callback for widget dialog +// +// Parameters: +// hwndDlg - [in] Handle to the widget dialog +// uMsg - [in] Specifies the message +// wParam - [in] Specifies additional message-specific information +// lParam - [in] Specifies additional message-specific information +// +// Return values: +// TRUE if it processed the message, FALSE if not +// ---------------------------------------------------------------------------- +template +INT_PTR CALLBACK CLongWidget<_TYPE_>::DlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + UNREFERENCED_PARAMETER(wParam); + + CLongWidget<_TYPE_>* pthis = (CLongWidget<_TYPE_>*) + (LONG_PTR)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); + + BOOL fRet = FALSE; + + switch (msg) + { + case WM_INITDIALOG: + { + SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam); + fRet = FALSE; // don't set focus + break; + } + + case WM_DESTROY: + { + // Don't delete. The Widget destructor is what posts the WM_DESTROY + SetWindowLongPtr(hwndDlg, GWLP_USERDATA, NULL); + fRet = TRUE; + break; + } + + case WM_HSCROLL: + + if (pthis) + { + pthis->m_Value = TrackBar_GetPos(pthis->m_hwndTrackbar); + pthis->UpdateValueText(); + + // Enable the Apply button upon user changing the control + SendMessage(GetParent(GetParent(hwndDlg)), PSM_CHANGED, (WPARAM)(GetParent(hwndDlg)), 0); + } + fRet = TRUE; + break; + } + + return(fRet); +} + + +// ---------------------------------------------------------------------------- +// Function: +// CLongWidget<_TYPE_>::UpdateValueText +// +// Description: +// Updates the numeric representation of the trackbar +// +// Return values: +// S_OK if successful +// ---------------------------------------------------------------------------- +template +void CLongWidget<_TYPE_>::UpdateValueText() +{ + WCHAR wsz[12]; + + StringCbPrintfW(wsz, sizeof(wsz), L"0x%08x", m_Value); + + // Set new text + SetWindowText(m_hwndValue, wsz); +} diff --git a/audio/sysvad/SwapAPO/PropPageExtensions/music.ico b/audio/sysvad/SwapAPO/PropPageExtensions/music.ico new file mode 100644 index 0000000000000000000000000000000000000000..3207a9dc99c4f60f0a9ee557ac9becc48cd9a269 GIT binary patch literal 26694 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 literal 0 HcmV?d00001 diff --git a/audio/sysvad/SwapAPO/PropPageExtensions/resource.h b/audio/sysvad/SwapAPO/PropPageExtensions/resource.h new file mode 100644 index 000000000..a8d36a5a2 --- /dev/null +++ b/audio/sysvad/SwapAPO/PropPageExtensions/resource.h @@ -0,0 +1,34 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by CplExt.rc +// +#define IDC_DISABLE_SYSFX 5000 // The "Disable System Effects" UI control must have ID 5000 +#define IDS_PROJ_NAME 100 +#define IDR_CPL_EXT 101 +#define IDR_SWAP_PROP_PAGE 102 +#define IDR_ADV_ENDPOINT_PROP_PAGE 103 +#define IDI_AUDIO_ICON 200 +#define IDD_SWAP_PROP_PAGE 201 +#define IDC_ENABLE_SWAP_SFX 203 +#define IDC_ENABLE_SWAP_MFX 204 +#define IDC_SPP_ENDPOINT_NAME 205 +#define IDD_ADV_ENDPOINT_PROP_PAGE 210 +#define IDC_NO_ADV_CONTROLS_FOUND 211 +#define IDC_EPP_ENDPOINT_NAME 212 +#define IDD_WIDGET_GEN_LONG 213 +#define IDC_GEN_LABEL 214 +#define IDC_GEN_SLIDER 215 +#define IDC_GEN_VALUE 216 +#define IDC_ENABLE_DELAY_SFX 217 +#define IDC_ENABLE_DELAY_MFX 218 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 230 +#define _APS_NEXT_COMMAND_VALUE 32768 +#define _APS_NEXT_CONTROL_VALUE 225 +#define _APS_NEXT_SYMED_VALUE 104 +#endif +#endif diff --git a/audio/sysvad/SwapAPO/PropPageExtensions/stdafx.cpp b/audio/sysvad/SwapAPO/PropPageExtensions/stdafx.cpp new file mode 100644 index 000000000..26e28d90f --- /dev/null +++ b/audio/sysvad/SwapAPO/PropPageExtensions/stdafx.cpp @@ -0,0 +1,18 @@ +//**@@@*@@@**************************************************** +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation. All rights reserved. +// +//**@@@*@@@**************************************************** + +// +// FileName: stdafx.cpp +// +// Abstract: Source file that includes just the standard includes +// +// ---------------------------------------------------------------------------- + + +#include "stdafx.h" + +_Analysis_mode_(_Analysis_code_type_user_driver_) diff --git a/audio/sysvad/SwapAPO/PropPageExtensions/stdafx.h b/audio/sysvad/SwapAPO/PropPageExtensions/stdafx.h new file mode 100644 index 000000000..a116cf07b --- /dev/null +++ b/audio/sysvad/SwapAPO/PropPageExtensions/stdafx.h @@ -0,0 +1,67 @@ +//**@@@*@@@**************************************************** +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation. All rights reserved. +// +//**@@@*@@@**************************************************** + +// +// FileName: stdafx.h +// +// Abstract: 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 + +//// Modify the following defines if you have to target a platform prior to the ones specified below. +//// Refer to MSDN for the latest info on corresponding values for different platforms. +//#ifndef WINVER // Allow use of features specific to Windows 95 and Windows NT 4 or later. +//#define WINVER 0x0400 // Change this to the appropriate value to target Windows 98 and Windows 2000 or later. +//#endif +// +//#ifndef _WIN32_WINNT // Allow use of features specific to Windows NT 4 or later. +//#define _WIN32_WINNT 0x0400 // Change this to the appropriate value to target Windows 2000 or later. +//#endif +// +//#ifndef _WIN32_WINDOWS // Allow use of features specific to Windows 98 or later. +//#define _WIN32_WINDOWS 0x0410 // Change this to the appropriate value to target Windows Me or later. +//#endif +// +//#ifndef _WIN32_IE // Allow use of features specific to IE 4.0 or later. +//#define _WIN32_IE 0x0400 // Change this to the appropriate value to target IE 5.0 or later. +//#endif + +#define _ATL_APARTMENT_THREADED +#define _ATL_NO_AUTOMATIC_NAMESPACE + +#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS // some CString constructors will be explicit + +// Turns off ATL's hiding of some common and often safely ignored warning messages +#define _ATL_ALL_WARNINGS + +#define _ATL_NO_HOSTING + +#include "resource.h" +#include +#include +#include +#include // atl windows +using namespace ATL; + +#include +#include +#include +#include +#include "CplExt.h" + +extern HINSTANCE g_hInstance; + +_Analysis_mode_(_Analysis_code_type_user_driver_) diff --git a/audio/sysvad/SwapAPO/PropPageExtensions/stdafxsrc.cpp b/audio/sysvad/SwapAPO/PropPageExtensions/stdafxsrc.cpp new file mode 100644 index 000000000..1577c4e3b --- /dev/null +++ b/audio/sysvad/SwapAPO/PropPageExtensions/stdafxsrc.cpp @@ -0,0 +1 @@ +#include "stdafx.h" \ No newline at end of file diff --git a/audio/sysvad/SysVadShared.h b/audio/sysvad/SysVadShared.h new file mode 100644 index 000000000..26cff9e28 --- /dev/null +++ b/audio/sysvad/SysVadShared.h @@ -0,0 +1,30 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + SysvadShared.h + +Abstract: + + Header file for common stuffs between sample SWAP APO and the SysVad sample. +*/ +#ifndef _SYSVADSHARED_H_ +#define _SYSVADSHARED_H_ + +// {D849C827-B24B-4C3D-A26E-338F4FF07ED5} +//DEFINE_GUID(KSPROPSETID_SysVAD, 0xd849c827, 0xb24b, 0x4c3d, 0xa2, 0x6e, 0x33, 0x8f, 0x4f, 0xf0, 0x7e, 0xd5); + + +#define STATIC_KSPROPSETID_SysVAD\ + 0xd849c827, 0xb24b, 0x4c3d, 0xa2, 0x6e, 0x33, 0x8f, 0x4f, 0xf0, 0x7e, 0xd5 +DEFINE_GUIDSTRUCT("D849C827-B24B-4C3D-A26E-338F4FF07ED5", KSPROPSETID_SysVAD); +#define KSPROPSETID_SysVAD DEFINE_GUIDNAMED(KSPROPSETID_SysVAD) + + +typedef enum{ + KSPROPERTY_SYSVAD_DEFAULTSTREAMEFFECTS +} KSPROPERTY_SYSVAD; + +#endif diff --git a/audio/sysvad/TabletAudioSample/TabletAudioSample.vcxproj b/audio/sysvad/TabletAudioSample/TabletAudioSample.vcxproj new file mode 100644 index 000000000..b2723d08d --- /dev/null +++ b/audio/sysvad/TabletAudioSample/TabletAudioSample.vcxproj @@ -0,0 +1,295 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {29A5DD25-AB56-4DEE-96AB-21EC2926A300} + $(MSBuildProjectName) + 1 + false + true + Debug + Win32 + {D75C2887-9174-43D6-993C-B20E6F396D03} + + + + 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) + + + + + + + + + + + + + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\portcls.lib;$(DDK_LIB_PATH)\stdunk.lib;$(DDK_LIB_PATH)\libcntpr.lib + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);.. + %(PreprocessorDefinitions);_WIN32;UNICODE;_UNICODE;PC_IMPLEMENTATION + %(PreprocessorDefinitions);DEBUG_LEVEL=DEBUGLVL_TERSE + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);.. + true + Level4 + %(PreprocessorDefinitions);_WIN32;UNICODE;_UNICODE;PC_IMPLEMENTATION + %(PreprocessorDefinitions);DEBUG_LEVEL=DEBUGLVL_TERSE + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);.. + %(PreprocessorDefinitions);_WIN32;UNICODE;_UNICODE;PC_IMPLEMENTATION + %(PreprocessorDefinitions);DEBUG_LEVEL=DEBUGLVL_TERSE + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\portcls.lib;$(DDK_LIB_PATH)\stdunk.lib;$(DDK_LIB_PATH)\libcntpr.lib + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);.. + %(PreprocessorDefinitions);_WIN32;UNICODE;_UNICODE;PC_IMPLEMENTATION + %(PreprocessorDefinitions);DEBUG_LEVEL=DEBUGLVL_TERSE + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);.. + true + Level4 + %(PreprocessorDefinitions);_WIN32;UNICODE;_UNICODE;PC_IMPLEMENTATION + %(PreprocessorDefinitions);DEBUG_LEVEL=DEBUGLVL_TERSE + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);.. + %(PreprocessorDefinitions);_WIN32;UNICODE;_UNICODE;PC_IMPLEMENTATION + %(PreprocessorDefinitions);DEBUG_LEVEL=DEBUGLVL_TERSE + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\portcls.lib;$(DDK_LIB_PATH)\stdunk.lib;$(DDK_LIB_PATH)\libcntpr.lib + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);.. + %(PreprocessorDefinitions);_WIN32;UNICODE;_UNICODE;PC_IMPLEMENTATION + %(PreprocessorDefinitions);DEBUG_LEVEL=DEBUGLVL_TERSE + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);.. + true + Level4 + %(PreprocessorDefinitions);_WIN32;UNICODE;_UNICODE;PC_IMPLEMENTATION + %(PreprocessorDefinitions);DEBUG_LEVEL=DEBUGLVL_TERSE + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);.. + %(PreprocessorDefinitions);_WIN32;UNICODE;_UNICODE;PC_IMPLEMENTATION + %(PreprocessorDefinitions);DEBUG_LEVEL=DEBUGLVL_TERSE + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\portcls.lib;$(DDK_LIB_PATH)\stdunk.lib;$(DDK_LIB_PATH)\libcntpr.lib + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);.. + %(PreprocessorDefinitions);_WIN32;UNICODE;_UNICODE;PC_IMPLEMENTATION + %(PreprocessorDefinitions);DEBUG_LEVEL=DEBUGLVL_TERSE + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);.. + true + Level4 + %(PreprocessorDefinitions);_WIN32;UNICODE;_UNICODE;PC_IMPLEMENTATION + %(PreprocessorDefinitions);DEBUG_LEVEL=DEBUGLVL_TERSE + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);.. + %(PreprocessorDefinitions);_WIN32;UNICODE;_UNICODE;PC_IMPLEMENTATION + %(PreprocessorDefinitions);DEBUG_LEVEL=DEBUGLVL_TERSE + + + + TabletAudioSample + + + TabletAudioSample + + + TabletAudioSample + + + TabletAudioSample + + + + %(AdditionalDependencies);.\..\EndpointsCommon\$(IntDir)\EndpointsCommon.lib + + + %(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS + %(PreprocessorDefinitions);_USE_SingleComponentMultiFxStates + %(AdditionalIncludeDirectories);..\EndpointsCommon + + + + + %(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS + %(PreprocessorDefinitions);_USE_SingleComponentMultiFxStates + %(AdditionalIncludeDirectories);..\EndpointsCommon + + + %(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS + %(PreprocessorDefinitions);_USE_SingleComponentMultiFxStates + %(AdditionalIncludeDirectories);..\EndpointsCommon + + + + + %(AdditionalDependencies);.\..\EndpointsCommon\$(IntDir)\EndpointsCommon.lib + + + %(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS + %(PreprocessorDefinitions);_USE_SingleComponentMultiFxStates + %(AdditionalIncludeDirectories);..\EndpointsCommon + + + + + %(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS + %(PreprocessorDefinitions);_USE_SingleComponentMultiFxStates + %(AdditionalIncludeDirectories);..\EndpointsCommon + + + %(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS + %(PreprocessorDefinitions);_USE_SingleComponentMultiFxStates + %(AdditionalIncludeDirectories);..\EndpointsCommon + + + + + %(AdditionalDependencies);.\..\EndpointsCommon\$(IntDir)\EndpointsCommon.lib + + + %(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS + %(PreprocessorDefinitions);_USE_SingleComponentMultiFxStates + %(AdditionalIncludeDirectories);..\EndpointsCommon + + + + + %(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS + %(PreprocessorDefinitions);_USE_SingleComponentMultiFxStates + %(AdditionalIncludeDirectories);..\EndpointsCommon + + + %(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS + %(PreprocessorDefinitions);_USE_SingleComponentMultiFxStates + %(AdditionalIncludeDirectories);..\EndpointsCommon + + + + + %(AdditionalDependencies);.\..\EndpointsCommon\$(IntDir)\EndpointsCommon.lib + + + %(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS + %(PreprocessorDefinitions);_USE_SingleComponentMultiFxStates + %(AdditionalIncludeDirectories);..\EndpointsCommon + + + + + %(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS + %(PreprocessorDefinitions);_USE_SingleComponentMultiFxStates + %(AdditionalIncludeDirectories);..\EndpointsCommon + + + %(PreprocessorDefinitions);_USE_WAVERT_;SYSVAD_BTH_BYPASS + %(PreprocessorDefinitions);_USE_SingleComponentMultiFxStates + %(AdditionalIncludeDirectories);..\EndpointsCommon + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/audio/sysvad/TabletAudioSample/TabletAudioSample.vcxproj.Filters b/audio/sysvad/TabletAudioSample/TabletAudioSample.vcxproj.Filters new file mode 100644 index 000000000..14b93639e --- /dev/null +++ b/audio/sysvad/TabletAudioSample/TabletAudioSample.vcxproj.Filters @@ -0,0 +1,58 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {D13BC5E0-7771-4687-83A0-245941715858} + + + h;hpp;hxx;hm;inl;inc;xsd + {E07941ED-CD6E-4BAE-A9DE-5B7A970BA57B} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {D9A3BF33-CD62-4610-BA9D-5137A76BD2C9} + + + inf;inv;inx;mof;mc; + {D7BA1DD3-90E0-45B3-9C70-34C9A6F43FEC} + + + + + 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/audio/sysvad/TabletAudioSample/hdmitopo.cpp b/audio/sysvad/TabletAudioSample/hdmitopo.cpp new file mode 100644 index 000000000..9deb16cd2 --- /dev/null +++ b/audio/sysvad/TabletAudioSample/hdmitopo.cpp @@ -0,0 +1,670 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + hdmitopo.cpp + +Abstract: + + Implementation of topology miniport for the hdmi endpoint. + +--*/ + +#pragma warning (disable : 4127) + +#include +#include "simple.h" +#include "mintopo.h" +#include "hdmitopo.h" +#include "hdmitoptable.h" +#include + + +#pragma code_seg("PAGE") + +//============================================================================= +NTSTATUS +CreateHdmiMiniportTopology +( + _Out_ PUNKNOWN * Unknown, + _In_ REFCLSID, + _In_opt_ PUNKNOWN UnknownOuter, + _When_((PoolType & NonPagedPoolMustSucceed) != 0, + __drv_reportError("Must succeed pool allocations are forbidden. " + "Allocation failures cause a system crash")) + _In_ POOL_TYPE PoolType, + _In_ PUNKNOWN UnknownAdapter, + _In_opt_ PVOID DeviceContext, + _In_ PENDPOINT_MINIPAIR MiniportPair +) +/*++ + +Routine Description: + + Creates a new topology miniport. + +Arguments: + + Unknown - + + RefclsId - + + UnknownOuter - + + PoolType - + + UnknownAdapter - + + DeviceContext - + + MiniportPair - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(Unknown); + ASSERT(MiniportPair); + + UNREFERENCED_PARAMETER(UnknownAdapter); + UNREFERENCED_PARAMETER(DeviceContext); + + CHdmiMiniportTopology *obj = + new (PoolType, MINWAVERT_POOLTAG) + CHdmiMiniportTopology( UnknownOuter, + MiniportPair->TopoDescriptor, + MiniportPair->DeviceMaxChannels); + if (NULL == obj) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + obj->AddRef(); + *Unknown = reinterpret_cast(obj); + + return STATUS_SUCCESS; +} // CreateHdmiMiniportTopology + +//============================================================================= +CHdmiMiniportTopology::~CHdmiMiniportTopology +( + void +) +/*++ + +Routine Description: + + Topology miniport destructor + +Arguments: + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + DPF_ENTER(("[CHdmiMiniportTopology::~CHdmiMiniportTopology]")); +} // ~CHdmiMiniportTopology + +//============================================================================= +NTSTATUS +CHdmiMiniportTopology::DataRangeIntersection +( + _In_ ULONG PinId, + _In_ PKSDATARANGE ClientDataRange, + _In_ PKSDATARANGE MyDataRange, + _In_ ULONG OutputBufferLength, + _Out_writes_bytes_to_opt_(OutputBufferLength, *ResultantFormatLength) + PVOID ResultantFormat OPTIONAL, + _Out_ PULONG ResultantFormatLength +) +/*++ + +Routine Description: + + The DataRangeIntersection function determines the highest quality + intersection of two data ranges. + +Arguments: + + PinId - Pin for which data intersection is being determined. + + ClientDataRange - Pointer to KSDATARANGE structure which contains the data range + submitted by client in the data range intersection property + request. + + MyDataRange - Pin's data range to be compared with client's data range. + + OutputBufferLength - Size of the buffer pointed to by the resultant format + parameter. + + ResultantFormat - Pointer to value where the resultant format should be + returned. + + ResultantFormatLength - Actual length of the resultant format that is placed + at ResultantFormat. This should be less than or equal + to OutputBufferLength. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + return + CMiniportTopologySYSVAD::DataRangeIntersection + ( + PinId, + ClientDataRange, + MyDataRange, + OutputBufferLength, + ResultantFormat, + ResultantFormatLength + ); +} // DataRangeIntersection + +//============================================================================= +STDMETHODIMP +CHdmiMiniportTopology::GetDescription +( + _Out_ PPCFILTER_DESCRIPTOR * OutFilterDescriptor +) +/*++ + +Routine Description: + + The GetDescription function gets a pointer to a filter description. + It provides a location to deposit a pointer in miniport's description + structure. This is the placeholder for the FromNode or ToNode fields in + connections which describe connections to the filter's pins. + +Arguments: + + OutFilterDescriptor - Pointer to the filter description. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(OutFilterDescriptor); + + return CMiniportTopologySYSVAD::GetDescription(OutFilterDescriptor); +} // GetDescription + +//============================================================================= +STDMETHODIMP +CHdmiMiniportTopology::Init +( + _In_ PUNKNOWN UnknownAdapter, + _In_ PRESOURCELIST ResourceList, + _In_ PPORTTOPOLOGY Port_ +) +/*++ + +Routine Description: + + The Init function initializes the miniport. Callers of this function + should run at IRQL PASSIVE_LEVEL + +Arguments: + + UnknownAdapter - A pointer to the Iuknown interface of the adapter object. + + ResourceList - Pointer to the resource list to be supplied to the miniport + during initialization. The port driver is free to examine the + contents of the ResourceList. The port driver will not be + modify the ResourceList contents. + + Port - Pointer to the topology port object that is linked with this miniport. + +Return Value: + + NT status code. + +--*/ +{ + UNREFERENCED_PARAMETER(ResourceList); + + PAGED_CODE(); + + ASSERT(UnknownAdapter); + ASSERT(Port_); + + DPF_ENTER(("[CHdmiMiniportTopology::Init]")); + + NTSTATUS ntStatus; + + ntStatus = + CMiniportTopologySYSVAD::Init + ( + UnknownAdapter, + Port_ + ); + + return ntStatus; +} // Init + +//============================================================================= +STDMETHODIMP +CHdmiMiniportTopology::NonDelegatingQueryInterface +( + _In_ REFIID Interface, + _COM_Outptr_ PVOID * Object +) +/*++ + +Routine Description: + + QueryInterface for MiniportTopology + +Arguments: + + Interface - GUID of the interface + + Object - interface object to be returned. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(Object); + + if (IsEqualGUIDAligned(Interface, IID_IUnknown)) + { + *Object = PVOID(PUNKNOWN(this)); + } + else if (IsEqualGUIDAligned(Interface, IID_IMiniport)) + { + *Object = PVOID(PMINIPORT(this)); + } + else if (IsEqualGUIDAligned(Interface, IID_IMiniportTopology)) + { + *Object = PVOID(PMINIPORTTOPOLOGY(this)); + } + else + { + *Object = NULL; + } + + if (*Object) + { + // We reference the interface for the caller. + PUNKNOWN(*Object)->AddRef(); + return(STATUS_SUCCESS); + } + + return(STATUS_INVALID_PARAMETER); +} // NonDelegatingQueryInterface + +//============================================================================= + +#define SINK_DESC_STR L"SinkDescription Sample" + +NTSTATUS +CHdmiMiniportTopology::PropertyHandlerJackSinkInfo +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_Jack, KSPROPERTY_JACK_SINK_INFO ) + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandlerJackSinkInfo]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + ULONG nPinId = (ULONG)-1; + + if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + nPinId = *(PULONG(PropertyRequest->Instance)); + + if (nPinId == KSPIN_TOPO_LINEOUT_DEST) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else + { + ULONG cbNeeded = sizeof(KSJACK_SINK_INFORMATION); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbNeeded; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbNeeded) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + size_t cchLength; + + PKSJACK_SINK_INFORMATION sinkInfo = (PKSJACK_SINK_INFORMATION)PropertyRequest->Value; + + RtlZeroMemory(sinkInfo, sizeof(KSJACK_SINK_INFORMATION)); + + // + // Init sink info data (example follows). + // + sinkInfo->ConnType = KSJACK_SINK_CONNECTIONTYPE_HDMI; + sinkInfo->ManufacturerId = 0; + sinkInfo->ProductId = 0; + sinkInfo->AudioLatency = 0; + sinkInfo->HDCPCapable = TRUE; + sinkInfo->AICapable = TRUE; + + cchLength = sizeof(SINK_DESC_STR)/sizeof(WCHAR) - 1; // -1 to remove the null wchar. + if (cchLength < MAX_SINK_DESCRIPTION_NAME_LENGTH) + { + sinkInfo->SinkDescriptionLength = (UCHAR)cchLength; + (void)RtlStringCchCopyW + ( + sinkInfo->SinkDescription, + MAX_SINK_DESCRIPTION_NAME_LENGTH, + SINK_DESC_STR + ); + } + + ntStatus = STATUS_SUCCESS; + } + } + } + } + } + + return ntStatus; +} + +//============================================================================= +NTSTATUS +CHdmiMiniportTopology::PropertyHandlerJackDescription +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_Jack, KSPROPERTY_JACK_DESCRIPTION ) + +Arguments: + + PropertyRequest + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandlerJackDescription]")); + + ULONG cJackDescriptions = ARRAYSIZE(HdmiJackDescriptions); + PKSJACK_DESCRIPTION * JackDescriptions = HdmiJackDescriptions; + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + ULONG nPinId = (ULONG)-1; + + if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + nPinId = *(PULONG(PropertyRequest->Instance)); + + if ((nPinId < cJackDescriptions) && (JackDescriptions[nPinId] != NULL)) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else + { + ULONG cbNeeded = sizeof(KSMULTIPLE_ITEM) + sizeof(KSJACK_DESCRIPTION); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbNeeded; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbNeeded) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PKSMULTIPLE_ITEM pMI = (PKSMULTIPLE_ITEM)PropertyRequest->Value; + PKSJACK_DESCRIPTION pDesc = (PKSJACK_DESCRIPTION)(pMI+1); + + pMI->Size = cbNeeded; + pMI->Count = 1; + + RtlCopyMemory(pDesc, JackDescriptions[nPinId], sizeof(KSJACK_DESCRIPTION)); + ntStatus = STATUS_SUCCESS; + } + } + } + } + } + + return ntStatus; +} + +//============================================================================= +NTSTATUS +CHdmiMiniportTopology::PropertyHandlerJackDescription2 +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_Jack, KSPROPERTY_JACK_DESCRIPTION2 ) + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandlerJackDescription2]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + ULONG nPinId = (ULONG)-1; + + ULONG cJackDescriptions = ARRAYSIZE(HdmiJackDescriptions); + PKSJACK_DESCRIPTION * JackDescriptions = HdmiJackDescriptions; + + if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + nPinId = *(PULONG(PropertyRequest->Instance)); + + if ((nPinId < cJackDescriptions) && (JackDescriptions[nPinId] != NULL)) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else + { + ULONG cbNeeded = sizeof(KSMULTIPLE_ITEM) + sizeof(KSJACK_DESCRIPTION2); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbNeeded; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbNeeded) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PKSMULTIPLE_ITEM pMI = (PKSMULTIPLE_ITEM)PropertyRequest->Value; + PKSJACK_DESCRIPTION2 pDesc = (PKSJACK_DESCRIPTION2)(pMI+1); + + pMI->Size = cbNeeded; + pMI->Count = 1; + + RtlZeroMemory(pDesc, sizeof(KSJACK_DESCRIPTION2)); + + // + // Specifies the lower 16 bits of the DWORD parameter. This parameter indicates whether + // the jack is currently active, streaming, idle, or hardware not ready. + // + pDesc->DeviceStateInfo = 0; + + // + // From MSDN: + // "If an audio device lacks jack presence detection, the IsConnected member of + // the KSJACK_DESCRIPTION structure must always be set to TRUE. To remove the + // ambiguity that results from this dual meaning of the TRUE value for IsConnected, + // a client application can call IKsJackDescription2::GetJackDescription2 to read + // the JackCapabilities flag of the KSJACK_DESCRIPTION2 structure. If this flag has + // the JACKDESC2_PRESENCE_DETECT_CAPABILITY bit set, it indicates that the endpoint + // does in fact support jack presence detection. In that case, the return value of + // the IsConnected member can be interpreted to accurately reflect the insertion status + // of the jack." + // + // Bit definitions: + // 0x00000001 - JACKDESC2_PRESENCE_DETECT_CAPABILITY + // 0x00000002 - JACKDESC2_DYNAMIC_FORMAT_CHANGE_CAPABILITY + // + pDesc->JackCapabilities = JACKDESC2_PRESENCE_DETECT_CAPABILITY; + + ntStatus = STATUS_SUCCESS; + } + } + } + } + } + + return ntStatus; +} + +//============================================================================= +NTSTATUS +PropertyHandler_HdmiTopoFilter +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Redirects property request to miniport object + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_HdmiTopoFilter]")); + + // PropertryRequest structure is filled by portcls. + // MajorTarget is a pointer to miniport object for miniports. + // + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + PCHdmiMiniportTopology pMiniport = (PCHdmiMiniportTopology)PropertyRequest->MajorTarget; + + if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_Jack)) + { + if (PropertyRequest->PropertyItem->Id == KSPROPERTY_JACK_DESCRIPTION) + { + ntStatus = pMiniport->PropertyHandlerJackDescription(PropertyRequest); + } + else if (PropertyRequest->PropertyItem->Id == KSPROPERTY_JACK_DESCRIPTION2) + { + ntStatus = pMiniport->PropertyHandlerJackDescription2(PropertyRequest); + } + else if (PropertyRequest->PropertyItem->Id == KSPROPERTY_JACK_SINK_INFO) + { + ntStatus = pMiniport->PropertyHandlerJackSinkInfo(PropertyRequest); + } + } + + return ntStatus; +} // PropertyHandler_HdmiTopoFilter + +#pragma code_seg() + diff --git a/audio/sysvad/TabletAudioSample/hdmitopo.h b/audio/sysvad/TabletAudioSample/hdmitopo.h new file mode 100644 index 000000000..cf873b700 --- /dev/null +++ b/audio/sysvad/TabletAudioSample/hdmitopo.h @@ -0,0 +1,86 @@ + +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + hdmitopo.h + +Abstract: + + Declaration of topology miniport for the hdmi endpoint. + +--*/ + +#ifndef _SYSVAD_HDMITOPO_H_ +#define _SYSVAD_HDMITOPO_H_ + +#include "basetopo.h" + +//============================================================================= +// Classes +//============================================================================= + +/////////////////////////////////////////////////////////////////////////////// +// CHdmiMiniportTopology +// + +class CHdmiMiniportTopology : + public CMiniportTopologySYSVAD, + public IMiniportTopology, + public CUnknown +{ + public: + DECLARE_STD_UNKNOWN(); + CHdmiMiniportTopology + ( + _In_opt_ PUNKNOWN UnknownOuter, + _In_ PCFILTER_DESCRIPTOR *FilterDesc, + _In_ USHORT DeviceMaxChannels + ) + : CUnknown(UnknownOuter), + CMiniportTopologySYSVAD(FilterDesc, DeviceMaxChannels) + {} + + ~CHdmiMiniportTopology(); + + IMP_IMiniportTopology; + + NTSTATUS PropertyHandlerJackSinkInfo + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + + NTSTATUS PropertyHandlerJackDescription + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + + NTSTATUS PropertyHandlerJackDescription2 + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); +}; + +typedef CHdmiMiniportTopology *PCHdmiMiniportTopology; + + +NTSTATUS +CreateHdmiMiniportTopology( + _Out_ PUNKNOWN * Unknown, + _In_ REFCLSID, + _In_opt_ PUNKNOWN UnknownOuter, + _When_((PoolType & NonPagedPoolMustSucceed) != 0, + __drv_reportError("Must succeed pool allocations are forbidden. " + "Allocation failures cause a system crash")) + _In_ POOL_TYPE PoolType, + _In_ PUNKNOWN UnknownAdapter, + _In_opt_ PVOID DeviceContext, + _In_ PENDPOINT_MINIPAIR MiniportPair + ); + +NTSTATUS PropertyHandler_HdmiTopoFilter(_In_ PPCPROPERTY_REQUEST PropertyRequest); + +#endif // _SYSVAD_HDMITOPO_H_ + diff --git a/audio/sysvad/TabletAudioSample/hdmitoptable.h b/audio/sysvad/TabletAudioSample/hdmitoptable.h new file mode 100644 index 000000000..5fab7cf4d --- /dev/null +++ b/audio/sysvad/TabletAudioSample/hdmitoptable.h @@ -0,0 +1,172 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + hdmitoptable.h + +Abstract: + + Declaration of topology tables for the hdmi endpoint. + +--*/ + +#ifndef _SYSVAD_HDMITOPTABLE_H_ +#define _SYSVAD_HDMITOPTABLE_H_ + +//============================================================================= +static +KSDATARANGE HdmiTopoPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + }, + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +//============================================================================= +static +PKSDATARANGE HdmiTopoPinDataRangePointersBridge[] = +{ + &HdmiTopoPinDataRangesBridge[0], + &HdmiTopoPinDataRangesBridge[1] +}; + +//============================================================================= +static +PCPIN_DESCRIPTOR HdmiTopoMiniportPins[] = +{ + // KSPIN_TOPO_WAVEOUT_SOURCE + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(HdmiTopoPinDataRangePointersBridge), // DataRangesCount + HdmiTopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_IN, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSCATEGORY_AUDIO, // Category + NULL, // Name + 0 // Reserved + } + }, + // KSPIN_TOPO_LINEOUT_DEST + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(HdmiTopoPinDataRangePointersBridge), // DataRangesCount + HdmiTopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_OUT, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSNODETYPE_HDMI_INTERFACE, // Category + NULL, // Name + 0 // Reserved + } + } +}; + +//============================================================================= +static +KSJACK_DESCRIPTION HdmiJackDesc = +{ + KSAUDIO_SPEAKER_STEREO, + 0x0000, // no color + eConnTypeOtherDigital, + eGeoLocHDMI, + eGenLocPrimaryBox, + ePortConnJack, + TRUE +}; + +// Only return a KSJACK_DESCRIPTION for the physical bridge pin. +static +PKSJACK_DESCRIPTION HdmiJackDescriptions[] = +{ + NULL, + &HdmiJackDesc +}; + +//============================================================================= +static +PCCONNECTION_DESCRIPTOR HdmiTopoMiniportConnections[] = +{ + // FromNode, FromPin, ToNode, ToPin + { PCFILTER_NODE, KSPIN_TOPO_WAVEOUT_SOURCE, PCFILTER_NODE, KSPIN_TOPO_LINEOUT_DEST} +}; + + +//============================================================================= +static +PCPROPERTY_ITEM PropertiesHdmiTopoFilter[] = +{ + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_HdmiTopoFilter + }, + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION2, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_HdmiTopoFilter + }, + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_SINK_INFO, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_HdmiTopoFilter + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationHdmiTopoFilter, PropertiesHdmiTopoFilter); + +//============================================================================= +static +PCFILTER_DESCRIPTOR HdmiTopoMiniportFilterDescriptor = +{ + 0, // Version + &AutomationHdmiTopoFilter, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(HdmiTopoMiniportPins), // PinCount + HdmiTopoMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + 0, // NodeCount + NULL, // Nodes + SIZEOF_ARRAY(HdmiTopoMiniportConnections), // ConnectionCount + HdmiTopoMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories +}; + +#endif // _SYSVAD_HDMITOPTABLE_H_ + + diff --git a/audio/sysvad/TabletAudioSample/hdmiwavtable.h b/audio/sysvad/TabletAudioSample/hdmiwavtable.h new file mode 100644 index 000000000..a5f63c62a --- /dev/null +++ b/audio/sysvad/TabletAudioSample/hdmiwavtable.h @@ -0,0 +1,626 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + hdmiwavtable.h + +Abstract: + + Declaration of wave miniport tables for the hdmi endpoint. + + Note: the HDMI in this sample shows how to do h/w loopback + for non-offloaded devices. + +--*/ + +#ifndef _SYSVAD_HDMIWAVTABLE_H_ +#define _SYSVAD_HDMIWAVTABLE_H_ + + +//============================================================================= +// Defines +//============================================================================= + + +#define HDMI_DEVICE_MAX_CHANNELS 2 // Max Channels. + +#define HDMI_HOST_MAX_CHANNELS 2 // Max Channels. +#define HDMI_HOST_MIN_BITS_PER_SAMPLE 16 // Min Bits Per Sample +#define HDMI_HOST_MAX_BITS_PER_SAMPLE 16 // Max Bits Per Sample +#define HDMI_HOST_MIN_SAMPLE_RATE 44100 // Min Sample Rate +#define HDMI_HOST_MAX_SAMPLE_RATE 96000 // Max Sample Rate + +#define HDMI_LOOPBACK_MAX_CHANNELS 2 // Max Channels. +#define HDMI_LOOPBACK_MIN_BITS_PER_SAMPLE 16 // Min Bits Per Sample +#define HDMI_LOOPBACK_MAX_BITS_PER_SAMPLE 16 // Max Bits Per Sample +#define HDMI_LOOPBACK_MIN_SAMPLE_RATE 44100 // Min Sample Rate +#define HDMI_LOOPBACK_MAX_SAMPLE_RATE 48000 // Max Sample Rate + +#define HDMI_DOLBY_DIGITAL_MAX_CHANNELS 2 // Max Channels. +#define HDMI_DOLBY_DIGITAL_MIN_BITS_PER_SAMPLE 16 // Min Bits Per Sample +#define HDMI_DOLBY_DIGITAL_MAX_BITS_PER_SAMPLE 16 // Max Bits Per Sample +#define HDMI_DOLBY_DIGITAL_MIN_SAMPLE_RATE 44100 // Min Sample Rate +#define HDMI_DOLBY_DIGITAL_MAX_SAMPLE_RATE 48000 // Max Sample Rate + +// +// Max # of pin instances. +// +#define HDMI_MAX_INPUT_SYSTEM_STREAMS 2 // Raw + Default streams +#define HDMI_MAX_OUTPUT_LOOPBACK_STREAMS MAX_OUTPUT_LOOPBACK_STREAMS + + +//============================================================================= +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE HdmiHostPinSupportedDeviceFormats[] = +{ + { // 0 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 44100, + 176400, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 1 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 48000, + 192000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 2 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 88200, + 352800, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 3 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 96000, + 384000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 4 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 44100, + 176400, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_5POINT1_SURROUND, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL) + } + }, + { // 5 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 48000, + 192000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_5POINT1_SURROUND, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL) + } + } +}; + +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE HdmiLoopbackPinSupportedDeviceFormats[] = +{ + { // 0 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 44100, + 176400, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 1 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 48000, + 192000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +// +// Supported modes (only on streaming pins). +// +static +MODE_AND_DEFAULT_FORMAT HdmiHostPinSupportedDeviceModes[] = +{ + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_RAW, + NULL, // just an example of no default format for this endpoint/mode + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_DEFAULT, + NULL, // just an example of no default format for this endpoint/mode + } +}; + +// +// The entries here must follow the same order as the filter's pin +// descriptor array. +// +static +PIN_DEVICE_FORMATS_AND_MODES HdmiPinDeviceFormatsAndModes[] = +{ + { + SystemRenderPin, + HdmiHostPinSupportedDeviceFormats, + SIZEOF_ARRAY(HdmiHostPinSupportedDeviceFormats), + HdmiHostPinSupportedDeviceModes, + SIZEOF_ARRAY(HdmiHostPinSupportedDeviceModes) + }, + { + RenderLoopbackPin, + HdmiLoopbackPinSupportedDeviceFormats, + SIZEOF_ARRAY(HdmiLoopbackPinSupportedDeviceFormats), + NULL, // loopback doesn't support modes. + 0 + }, + { + BridgePin, + NULL, + 0, + NULL, + 0 + } +}; + +//============================================================================= +static +KSDATARANGE_AUDIO HdmiPinDataRangesStream[] = +{ + { // 0 - PCM host + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + HDMI_HOST_MAX_CHANNELS, + HDMI_HOST_MIN_BITS_PER_SAMPLE, + HDMI_HOST_MAX_BITS_PER_SAMPLE, + HDMI_HOST_MIN_SAMPLE_RATE, + HDMI_HOST_MAX_SAMPLE_RATE + }, + { // 1 - PCM loopback + { + sizeof(KSDATARANGE_AUDIO), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + HDMI_LOOPBACK_MAX_CHANNELS, + HDMI_LOOPBACK_MIN_BITS_PER_SAMPLE, + HDMI_LOOPBACK_MAX_BITS_PER_SAMPLE, + HDMI_LOOPBACK_MIN_SAMPLE_RATE, + HDMI_LOOPBACK_MAX_SAMPLE_RATE + }, + { // 2 - DOLBY-DIGITAL host + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + HDMI_DOLBY_DIGITAL_MAX_CHANNELS, + HDMI_DOLBY_DIGITAL_MIN_BITS_PER_SAMPLE, + HDMI_DOLBY_DIGITAL_MAX_BITS_PER_SAMPLE, + HDMI_DOLBY_DIGITAL_MIN_SAMPLE_RATE, + HDMI_DOLBY_DIGITAL_MAX_SAMPLE_RATE + } +}; + +static +PKSDATARANGE HdmiPinDataRangePointersStream[] = +{ + PKSDATARANGE(&HdmiPinDataRangesStream[0]), + PKSDATARANGE(&PinDataRangeAttributeList), + PKSDATARANGE(&HdmiPinDataRangesStream[2]), + PKSDATARANGE(&PinDataRangeAttributeList) +}; + +static +PKSDATARANGE HdmiPinDataRangePointersLoopbackStream[] = +{ + PKSDATARANGE(&HdmiPinDataRangesStream[1]) +}; + +//============================================================================= +static +KSDATARANGE HdmiPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + }, + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +static +PKSDATARANGE HdmiPinDataRangePointersBridge[] = +{ + &HdmiPinDataRangesBridge[0], + &HdmiPinDataRangesBridge[1] +}; + + +//============================================================================= +static +PCPIN_DESCRIPTOR HdmiWaveMiniportPins[] = +{ + // Wave Out Streaming Pin (Renderer) KSPIN_WAVE_RENDER2_SINK_SYSTEM + { + HDMI_MAX_INPUT_SYSTEM_STREAMS, + HDMI_MAX_INPUT_SYSTEM_STREAMS, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(HdmiPinDataRangePointersStream), + HdmiPinDataRangePointersStream, + KSPIN_DATAFLOW_IN, + KSPIN_COMMUNICATION_SINK, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, + // Wave Out Streaming Pin (Renderer) KSPIN_WAVE_RENDER2_SINK_LOOPBACK + { + HDMI_MAX_OUTPUT_LOOPBACK_STREAMS, + HDMI_MAX_OUTPUT_LOOPBACK_STREAMS, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(HdmiPinDataRangePointersLoopbackStream), + HdmiPinDataRangePointersLoopbackStream, + KSPIN_DATAFLOW_OUT, + KSPIN_COMMUNICATION_SINK, + &KSNODETYPE_AUDIO_LOOPBACK, + NULL, + 0 + } + }, + // Wave Out Bridge Pin (Renderer) KSPIN_WAVE_RENDER2_SOURCE + { + 0, + 0, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(HdmiPinDataRangePointersBridge), + HdmiPinDataRangePointersBridge, + KSPIN_DATAFLOW_OUT, + KSPIN_COMMUNICATION_NONE, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, +}; + +//============================================================================= +static +PCPROPERTY_ITEM HdmiPropertiesVolume[] = +{ + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_VOLUMELEVEL, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_WaveFilter + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationHdmiVolume, HdmiPropertiesVolume); + +//============================================================================= +static +PCPROPERTY_ITEM HdmiPropertiesMute[] = +{ + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_MUTE, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_WaveFilter + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationHdmiMute, HdmiPropertiesMute); + +//============================================================================= +static +PCPROPERTY_ITEM HdmiPropertiesPeakMeter[] = +{ + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_PEAKMETER2, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_WaveFilter + }, + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_CPU_RESOURCES, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_WaveFilter + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationHdmiPeakMeter, HdmiPropertiesPeakMeter); + +//============================================================================= +static +PCNODE_DESCRIPTOR HdmiWaveMiniportNodes[] = +{ + // KSNODE_WAVE_SUM + { + 0, // Flags + NULL, // AutomationTable + &KSNODETYPE_SUM, // Type + NULL // Name + }, + // KSNODE_WAVE_VOLUME + { + 0, // Flags + &AutomationHdmiVolume, // AutomationTable + &KSNODETYPE_VOLUME, // Type + &KSAUDFNAME_WAVE_VOLUME // Name + }, + // KSNODE_WAVE_MUTE + { + 0, // Flags + &AutomationHdmiMute, // AutomationTable + &KSNODETYPE_MUTE, // Type + &KSAUDFNAME_WAVE_MUTE // Name + }, + // KSNODE_WAVE_PEAKMETER + { + 0, // Flags + &AutomationHdmiPeakMeter, // AutomationTable + &KSNODETYPE_PEAKMETER, // Type + &KSAUDFNAME_PEAKMETER // Name + } +}; + +C_ASSERT( KSNODE_WAVE_SUM == 0 ); +C_ASSERT( KSNODE_WAVE_VOLUME == 1 ); +C_ASSERT( KSNODE_WAVE_MUTE == 2 ); +C_ASSERT( KSNODE_WAVE_PEAKMETER == 3 ); + + +//============================================================================= +// +// Note: +// 1) A sum node can be used to 'sum' multiple streams associated to different +// instances of the same pin. +// 2) Any node output has an implicit 'tee' node associated with it. Thus in the +// scenario below it is ok for the peak meter node to have two identical outputs. +// +// ---------------------------------------------------------- +// | | +// System Pin 0 | |-----| |--------| |------| |-----------| |--> 1 Loopback Pin +// [instances:0...n]-->| -->| Sum |--->| Volume |-->| Mute |-->| PeakMeter |--> | +// | |-----| |--------| |------| |-----------| |--> 2 KSPIN_WAVE_RENDER2_SOURCE +// | | +// ---------------------------------------------------------- + +static +PCCONNECTION_DESCRIPTOR HdmiWaveMiniportConnections[] = +{ + { PCFILTER_NODE, KSPIN_WAVE_RENDER2_SINK_SYSTEM, KSNODE_WAVE_SUM, 1 }, + { KSNODE_WAVE_SUM, 0, KSNODE_WAVE_VOLUME, 1 }, + { KSNODE_WAVE_VOLUME, 0, KSNODE_WAVE_MUTE, 1 }, + { KSNODE_WAVE_MUTE, 0, KSNODE_WAVE_PEAKMETER, 1 }, + { KSNODE_WAVE_PEAKMETER, 2, PCFILTER_NODE, KSPIN_WAVE_RENDER2_SINK_LOOPBACK }, + { KSNODE_WAVE_PEAKMETER, 0, PCFILTER_NODE, KSPIN_WAVE_RENDER2_SOURCE }, +}; + +//============================================================================= +static +PCPROPERTY_ITEM PropertiesHdmiWaveFilter[] = +{ + { + &KSPROPSETID_Pin, + KSPROPERTY_PIN_PROPOSEDATAFORMAT, + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_WaveFilter + }, +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationHdmiWaveFilter, PropertiesHdmiWaveFilter); + +//============================================================================= +static +PCFILTER_DESCRIPTOR HdmiWaveMiniportFilterDescriptor = +{ + 0, // Version + &AutomationHdmiWaveFilter, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(HdmiWaveMiniportPins), // PinCount + HdmiWaveMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + SIZEOF_ARRAY(HdmiWaveMiniportNodes), // NodeCount + HdmiWaveMiniportNodes, // Nodes + SIZEOF_ARRAY(HdmiWaveMiniportConnections), // ConnectionCount + HdmiWaveMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories - use defaults (audio, render, capture) +}; + +#endif // _SYSVAD_HDMIWAVTABLE_H_ + diff --git a/audio/sysvad/TabletAudioSample/micarray2toptable.h b/audio/sysvad/TabletAudioSample/micarray2toptable.h new file mode 100644 index 000000000..0c03a8395 --- /dev/null +++ b/audio/sysvad/TabletAudioSample/micarray2toptable.h @@ -0,0 +1,229 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + micarray2toptable.h + +Abstract: + + Declaration of topology tables for the mic array (back). + +--*/ + +#ifndef _SYSVAD_MICARRAY2TOPTABLE_H_ +#define _SYSVAD_MICARRAY2TOPTABLE_H_ + +// +// {3fe0e3e1-ad16-4772-8382-4129169018ce} +DEFINE_GUID(MICARRAY2_CUSTOM_NAME, +0x3fe0e3e1, 0xad16, 0x4772, 0x83, 0x82, 0x41, 0x29, 0x16, 0x90, 0x18, 0xce); + +//============================================================================= +static +KSDATARANGE MicArray2TopoPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +//============================================================================= +static +PKSDATARANGE MicArray2TopoPinDataRangePointersBridge[] = +{ + &MicArray2TopoPinDataRangesBridge[0] +}; + +//============================================================================= +static +PCPIN_DESCRIPTOR MicArray2TopoMiniportPins[] = +{ + // KSPIN_TOPO_MIC_ELEMENTS + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(MicArray2TopoPinDataRangePointersBridge), // DataRangesCount + MicArray2TopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_IN, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSNODETYPE_MICROPHONE_ARRAY, // Category + &MICARRAY2_CUSTOM_NAME, // Name + 0 // Reserved + } + }, + + // KSPIN_TOPO_BRIDGE + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(MicArray2TopoPinDataRangePointersBridge), // DataRangesCount + MicArray2TopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_OUT, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSCATEGORY_AUDIO, // Category + NULL, // Name + 0 // Reserved + } + } +}; + +//============================================================================= +static +PCPROPERTY_ITEM MicArray2PropertiesVolume[] = +{ + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_VOLUMELEVEL, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_MicArrayTopology + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationMicArray2Volume, MicArray2PropertiesVolume); + +//============================================================================= +static +PCPROPERTY_ITEM MicArray2PropertiesMute[] = +{ + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_MUTE, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_MicArrayTopology + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationMicArray2Mute, MicArray2PropertiesMute); + +//============================================================================= +static +PCPROPERTY_ITEM MicArray2PropertiesPeakMeter[] = +{ + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_PEAKMETER2, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_MicArrayTopology + }, + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_CPU_RESOURCES, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_MicArrayTopology + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationMicArray2PeakMeter, MicArray2PropertiesPeakMeter); + +//============================================================================= +static +PCNODE_DESCRIPTOR MicArray2TopologyNodes[] = +{ + // KSNODE_TOPO_VOLUME + { + 0, // Flags + &AutomationMicArray2Volume, // AutomationTable + &KSNODETYPE_VOLUME, // Type + &KSAUDFNAME_MIC_VOLUME // Name + }, + // KSNODE_TOPO_MUTE + { + 0, // Flags + &AutomationMicArray2Mute, // AutomationTable + &KSNODETYPE_MUTE, // Type + &KSAUDFNAME_MIC_MUTE // Name + }, + // KSNODE_TOPO_PEAKMETER + { + 0, // Flags + &AutomationMicArray2PeakMeter, // AutomationTable + &KSNODETYPE_PEAKMETER, // Type + &KSAUDFNAME_PEAKMETER // Name + } +}; + +C_ASSERT( KSNODE_TOPO_VOLUME == 0 ); +C_ASSERT( KSNODE_TOPO_MUTE == 1 ); +C_ASSERT( KSNODE_TOPO_PEAKMETER == 2 ); + + +//============================================================================= +static +PCCONNECTION_DESCRIPTOR MicArray2TopoMiniportConnections[] = +{ + // FromNode, FromPin, ToNode, ToPin + { PCFILTER_NODE, KSPIN_TOPO_MIC_ELEMENTS, KSNODE_TOPO_VOLUME, 1 }, + { KSNODE_TOPO_VOLUME, 0, KSNODE_TOPO_MUTE, 1 }, + { KSNODE_TOPO_MUTE, 0, KSNODE_TOPO_PEAKMETER, 1 }, + { KSNODE_TOPO_PEAKMETER, 0, PCFILTER_NODE, KSPIN_TOPO_BRIDGE } +}; + + +//============================================================================= +static +PCPROPERTY_ITEM MicArray2PropertiesTopoFilter[] = +{ + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_MicArrayTopoFilter + }, + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION2, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_MicArrayTopoFilter + }, + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_MIC_ARRAY_GEOMETRY, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_MicArrayTopoFilter + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationMicArray2TopoFilter, MicArray2PropertiesTopoFilter); + +//============================================================================= +static +PCFILTER_DESCRIPTOR MicArray2TopoMiniportFilterDescriptor = +{ + 0, // Version + &AutomationMicArray2TopoFilter, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(MicArray2TopoMiniportPins), // PinCount + MicArray2TopoMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + SIZEOF_ARRAY(MicArray2TopologyNodes), // NodeCount + MicArray2TopologyNodes, // Nodes + SIZEOF_ARRAY(MicArray2TopoMiniportConnections),// ConnectionCount + MicArray2TopoMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories +}; + +#endif // _SYSVAD_MICARRAY2TOPTABLE_H_ + diff --git a/audio/sysvad/TabletAudioSample/micarray3toptable.h b/audio/sysvad/TabletAudioSample/micarray3toptable.h new file mode 100644 index 000000000..02a3d76e7 --- /dev/null +++ b/audio/sysvad/TabletAudioSample/micarray3toptable.h @@ -0,0 +1,228 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + micarray3toptable.h + +Abstract: + + Declaration of topology tables for the mic array (front/back). + +--*/ + +#ifndef _SYSVAD_MICARRAY3TOPTABLE_H_ +#define _SYSVAD_MICARRAY3TOPTABLE_H_ + +// +// {c04bdb7c-2138-48da-9dd4-2af9ff2e58c2} +DEFINE_GUID(MICARRAY3_CUSTOM_NAME, +0xc04bdb7c, 0x2138, 0x48da, 0x9d, 0xd4, 0x2a, 0xf9, 0xff, 0x2e, 0x58, 0xc2); + +//============================================================================= +static +KSDATARANGE MicArray3TopoPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +//============================================================================= +static +PKSDATARANGE MicArray3TopoPinDataRangePointersBridge[] = +{ + &MicArray3TopoPinDataRangesBridge[0] +}; + +//============================================================================= +static +PCPIN_DESCRIPTOR MicArray3TopoMiniportPins[] = +{ + // KSPIN_TOPO_MIC_ELEMENTS + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(MicArray3TopoPinDataRangePointersBridge), // DataRangesCount + MicArray3TopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_IN, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSNODETYPE_MICROPHONE_ARRAY, // Category + &MICARRAY3_CUSTOM_NAME, // Name + 0 // Reserved + } + }, + + // KSPIN_TOPO_BRIDGE + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(MicArray3TopoPinDataRangePointersBridge), // DataRangesCount + MicArray3TopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_OUT, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSCATEGORY_AUDIO, // Category + NULL, // Name + 0 // Reserved + } + } +}; + +//============================================================================= +static +PCPROPERTY_ITEM MicArray3PropertiesVolume[] = +{ + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_VOLUMELEVEL, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_MicArrayTopology + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationMicArray3Volume, MicArray3PropertiesVolume); + +//============================================================================= +static +PCPROPERTY_ITEM MicArray3PropertiesMute[] = +{ + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_MUTE, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_MicArrayTopology + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationMicArray3Mute, MicArray3PropertiesMute); + +//============================================================================= +static +PCPROPERTY_ITEM MicArray3PropertiesPeakMeter[] = +{ + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_PEAKMETER2, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_MicArrayTopology + }, + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_CPU_RESOURCES, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_MicArrayTopology + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationMicArray3PeakMeter, MicArray3PropertiesPeakMeter); + +//============================================================================= +static +PCNODE_DESCRIPTOR MicArray3TopologyNodes[] = +{ + // KSNODE_TOPO_VOLUME + { + 0, // Flags + &AutomationMicArray3Volume, // AutomationTable + &KSNODETYPE_VOLUME, // Type + &KSAUDFNAME_MIC_VOLUME // Name + }, + // KSNODE_TOPO_MUTE + { + 0, // Flags + &AutomationMicArray3Mute, // AutomationTable + &KSNODETYPE_MUTE, // Type + &KSAUDFNAME_MIC_MUTE // Name + }, + // KSNODE_TOPO_PEAKMETER + { + 0, // Flags + &AutomationMicArray3PeakMeter, // AutomationTable + &KSNODETYPE_PEAKMETER, // Type + &KSAUDFNAME_PEAKMETER // Name + } +}; + +C_ASSERT( KSNODE_TOPO_VOLUME == 0 ); +C_ASSERT( KSNODE_TOPO_MUTE == 1 ); +C_ASSERT( KSNODE_TOPO_PEAKMETER == 2 ); + +//============================================================================= +static +PCCONNECTION_DESCRIPTOR MicArray3TopoMiniportConnections[] = +{ + // FromNode, FromPin, ToNode, ToPin + { PCFILTER_NODE, KSPIN_TOPO_MIC_ELEMENTS, KSNODE_TOPO_VOLUME, 1 }, + { KSNODE_TOPO_VOLUME, 0, KSNODE_TOPO_MUTE, 1 }, + { KSNODE_TOPO_MUTE, 0, KSNODE_TOPO_PEAKMETER, 1 }, + { KSNODE_TOPO_PEAKMETER, 0, PCFILTER_NODE, KSPIN_TOPO_BRIDGE } +}; + + +//============================================================================= +static +PCPROPERTY_ITEM MicArray3PropertiesTopoFilter[] = +{ + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_MicArrayTopoFilter + }, + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION2, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_MicArrayTopoFilter + }, + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_MIC_ARRAY_GEOMETRY, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_MicArrayTopoFilter + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationMicArray3TopoFilter, MicArray3PropertiesTopoFilter); + +//============================================================================= +static +PCFILTER_DESCRIPTOR MicArray3TopoMiniportFilterDescriptor = +{ + 0, // Version + &AutomationMicArray3TopoFilter, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(MicArray3TopoMiniportPins), // PinCount + MicArray3TopoMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + SIZEOF_ARRAY(MicArray3TopologyNodes), // NodeCount + MicArray3TopologyNodes, // Nodes + SIZEOF_ARRAY(MicArray3TopoMiniportConnections),// ConnectionCount + MicArray3TopoMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories +}; + +#endif // _SYSVAD_MICARRAY3TOPTABLE_H_ + diff --git a/audio/sysvad/TabletAudioSample/micarray3wavtable.h b/audio/sysvad/TabletAudioSample/micarray3wavtable.h new file mode 100644 index 000000000..34e3df184 --- /dev/null +++ b/audio/sysvad/TabletAudioSample/micarray3wavtable.h @@ -0,0 +1,455 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + micarray3wavtable.h + +Abstract: + + Declaration of wave miniport tables for the combined mic array (front/back). + +--*/ + +#ifndef _SYSVAD_MICARRAY3WAVTABLE_H_ +#define _SYSVAD_MICARRAY3WAVTABLE_H_ + +// +// Mic array range. +// +#define MICARRAY3_RAW_CHANNELS 4 // Channels for raw mode +#define MICARRAY3_PROCESSED_CHANNELS 1 // Channels for default mode +#define MICARRAY3_DEVICE_MAX_CHANNELS 4 // Max channels overall +#define MICARRAY3_MIN_BITS_PER_SAMPLE_PCM 16 // Min Bits Per Sample +#define MICARRAY3_MAX_BITS_PER_SAMPLE_PCM 16 // Max Bits Per Sample +#define MICARRAY3_RAW_SAMPLE_RATE 48000 // Raw sample rate + +// +// Max # of pin instances. +// +#define MICARRAY3_MAX_INPUT_STREAMS 2 // Raw + Default streams + +//============================================================================= +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE MicArray3PinSupportedDeviceFormats[] = +{ + // 0 - Note the ENDPOINT_MINIPAIR structures for the mic arrays use this first element as the proposed DEFAULT format + // 48 KHz 16-bit mono + { + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 48000, + 96000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + // 1 + // 16 KHz 16-bit mono + { + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 16000, + 32000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + // 2 + // 11.025 KHz 16-bit mono + { + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 11025, + 22050, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + // 3 + // 8 KHz 16-bit mono + { + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 8000, + 16000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + // 4 - Note the ENDPOINT_MINIPAIR structures for the mic arrays use this last element as the proposed RAW format + // 48 KHz 16-bit 4 channels + { + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 4, + 48000, + 384000, + 8, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + 0, // No channel configuration for unprocessed mic array + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, +}; + +// +// Supported modes (only on streaming pins). +// +static +MODE_AND_DEFAULT_FORMAT MicArray3PinSupportedDeviceModes[] = +{ + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_RAW, + &MicArray3PinSupportedDeviceFormats[SIZEOF_ARRAY(MicArray3PinSupportedDeviceFormats)-1].DataFormat + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_DEFAULT, + &MicArray3PinSupportedDeviceFormats[0].DataFormat + } +}; + +// +// The entries here must follow the same order as the filter's pin +// descriptor array. +// +static +PIN_DEVICE_FORMATS_AND_MODES MicArray3PinDeviceFormatsAndModes[] = +{ + { + BridgePin, + NULL, + 0, + NULL, + 0 + }, + { + SystemCapturePin, + MicArray3PinSupportedDeviceFormats, + SIZEOF_ARRAY(MicArray3PinSupportedDeviceFormats), + MicArray3PinSupportedDeviceModes, + SIZEOF_ARRAY(MicArray3PinSupportedDeviceModes) + } +}; + +//============================================================================= +// Data ranges +// +// See CMiniportWaveRT::DataRangeIntersection. +// +// Both mono and two-channel formats are supported for the mic arrays. The +// design of this sample driver's data range intersection handler requires a +// separate data for each supported channel count. +// +static +KSDATARANGE_AUDIO MicArray3PinDataRangesRawStream[] = +{ + { + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + MICARRAY3_RAW_CHANNELS, + MICARRAY3_MIN_BITS_PER_SAMPLE_PCM, + MICARRAY3_MAX_BITS_PER_SAMPLE_PCM, + MICARRAY3_RAW_SAMPLE_RATE, + MICARRAY3_RAW_SAMPLE_RATE + }, +}; + +static +KSDATARANGE_AUDIO MicArray3PinDataRangesProcessedStream[] = +{ + { + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + MICARRAY3_PROCESSED_CHANNELS, + MICARRAY3_MIN_BITS_PER_SAMPLE_PCM, + MICARRAY3_MAX_BITS_PER_SAMPLE_PCM, + 48000, + 48000 + }, + { + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + MICARRAY3_PROCESSED_CHANNELS, + MICARRAY3_MIN_BITS_PER_SAMPLE_PCM, + MICARRAY3_MAX_BITS_PER_SAMPLE_PCM, + 16000, + 16000 + }, + { + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + MICARRAY3_PROCESSED_CHANNELS, + MICARRAY3_MIN_BITS_PER_SAMPLE_PCM, + MICARRAY3_MAX_BITS_PER_SAMPLE_PCM, + 11025, + 11025 + }, + { + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + MICARRAY3_PROCESSED_CHANNELS, + MICARRAY3_MIN_BITS_PER_SAMPLE_PCM, + MICARRAY3_MAX_BITS_PER_SAMPLE_PCM, + 8000, + 8000 + }, +}; + +static +PKSDATARANGE MicArray3PinDataRangePointersStream[] = +{ + PKSDATARANGE(&MicArray3PinDataRangesProcessedStream[0]), + PKSDATARANGE(&PinDataRangeAttributeList), + PKSDATARANGE(&MicArray3PinDataRangesRawStream[0]), + PKSDATARANGE(&PinDataRangeAttributeList), +}; + +//============================================================================= +static +KSDATARANGE MicArray3PinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +static +PKSDATARANGE MicArray3PinDataRangePointersBridge[] = +{ + &MicArray3PinDataRangesBridge[0] +}; + +//============================================================================= +static +PCPIN_DESCRIPTOR MicArray3WaveMiniportPins[] = +{ + // Wave In Bridge Pin (Capture - From Topology) KSPIN_WAVE_BRIDGE + { + 0, + 0, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(MicArray3PinDataRangePointersBridge), + MicArray3PinDataRangePointersBridge, + KSPIN_DATAFLOW_IN, + KSPIN_COMMUNICATION_NONE, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, + + // Wave In Streaming Pin (Capture) KSPIN_WAVE_HOST + { + MICARRAY3_MAX_INPUT_STREAMS, + MICARRAY3_MAX_INPUT_STREAMS, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(MicArray3PinDataRangePointersStream), + MicArray3PinDataRangePointersStream, + KSPIN_DATAFLOW_OUT, + KSPIN_COMMUNICATION_SINK, + &KSCATEGORY_AUDIO, + &KSAUDFNAME_RECORDING_CONTROL, + 0 + } + } +}; + +//============================================================================= +static +PCNODE_DESCRIPTOR MicArray3WaveMiniportNodes[] = +{ + // KSNODE_WAVE_ADC + { + 0, // Flags + NULL, // AutomationTable + &KSNODETYPE_ADC, // Type + NULL // Name + } +}; + +//============================================================================= +static +PCCONNECTION_DESCRIPTOR MicArray3WaveMiniportConnections[] = +{ + { PCFILTER_NODE, KSPIN_WAVE_BRIDGE, KSNODE_WAVE_ADC, 1 }, + { KSNODE_WAVE_ADC, 0, PCFILTER_NODE, KSPIN_WAVEIN_HOST } +}; + +//============================================================================= +static +PCPROPERTY_ITEM PropertiesMicArray3WaveFilter[] = +{ + { + &KSPROPSETID_General, + KSPROPERTY_GENERAL_COMPONENTID, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_WaveFilter + }, + { + &KSPROPSETID_Pin, + KSPROPERTY_PIN_PROPOSEDATAFORMAT, + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_WaveFilter + }, + { + &KSPROPSETID_Pin, + KSPROPERTY_PIN_PROPOSEDATAFORMAT2, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_WaveFilter + }, +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationMicArray3WaveFilter, PropertiesMicArray3WaveFilter); + +//============================================================================= +static +PCFILTER_DESCRIPTOR MicArray3WaveMiniportFilterDescriptor = +{ + 0, // Version + &AutomationMicArray3WaveFilter, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(MicArray3WaveMiniportPins), // PinCount + MicArray3WaveMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + SIZEOF_ARRAY(MicArray3WaveMiniportNodes), // NodeCount + MicArray3WaveMiniportNodes, // Nodes + SIZEOF_ARRAY(MicArray3WaveMiniportConnections), // ConnectionCount + MicArray3WaveMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories - use defaults (audio, render, capture) +}; + +#endif // _SYSVAD_MICARRAY3WAVTABLE_H_ + diff --git a/audio/sysvad/TabletAudioSample/micintopo.cpp b/audio/sysvad/TabletAudioSample/micintopo.cpp new file mode 100644 index 000000000..6c1de10f1 --- /dev/null +++ b/audio/sysvad/TabletAudioSample/micintopo.cpp @@ -0,0 +1,274 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + micintopo.cpp + +Abstract: + + Implementation of topology miniport for the mic (external: headphone). + +--*/ + +#pragma warning (disable : 4127) + +#include +#include "simple.h" +#include "mintopo.h" +#include "micintopo.h" +#include "micintoptable.h" + + +#pragma code_seg("PAGE") + +//============================================================================= +NTSTATUS +PropertyHandler_MicInJackDescription +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_Jack, KSPROPERTY_JACK_DESCRIPTION ) + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_MicInJackDescription]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + ULONG nPinId = (ULONG)-1; + + ULONG cJackDescriptions = ARRAYSIZE(MicInJackDescriptions); + PKSJACK_DESCRIPTION * JackDescriptions = MicInJackDescriptions; + + if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + nPinId = *(PULONG(PropertyRequest->Instance)); + + if ((nPinId < cJackDescriptions) && (JackDescriptions[nPinId] != NULL)) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else + { + ULONG cbNeeded = sizeof(KSMULTIPLE_ITEM) + sizeof(KSJACK_DESCRIPTION); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbNeeded; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbNeeded) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PKSMULTIPLE_ITEM pMI = (PKSMULTIPLE_ITEM)PropertyRequest->Value; + PKSJACK_DESCRIPTION pDesc = (PKSJACK_DESCRIPTION)(pMI+1); + + pMI->Size = cbNeeded; + pMI->Count = 1; + + RtlCopyMemory(pDesc, JackDescriptions[nPinId], sizeof(KSJACK_DESCRIPTION)); + ntStatus = STATUS_SUCCESS; + } + } + } + } + } + + return ntStatus; +} + +//============================================================================= +NTSTATUS +PropertyHandler_MicInJackDescription2 +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Handles ( KSPROPSETID_Jack, KSPROPERTY_JACK_DESCRIPTION2 ) + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_MicInJackDescription2]")); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + ULONG nPinId = (ULONG)-1; + + ULONG cJackDescriptions = ARRAYSIZE(MicInJackDescriptions); + PKSJACK_DESCRIPTION * JackDescriptions = MicInJackDescriptions; + + if (PropertyRequest->InstanceSize >= sizeof(ULONG)) + { + nPinId = *(PULONG(PropertyRequest->Instance)); + + if ((nPinId < cJackDescriptions) && (JackDescriptions[nPinId] != NULL)) + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET, + VT_ILLEGAL + ); + } + else + { + ULONG cbNeeded = sizeof(KSMULTIPLE_ITEM) + sizeof(KSJACK_DESCRIPTION2); + + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbNeeded; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize < cbNeeded) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else + { + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + PKSMULTIPLE_ITEM pMI = (PKSMULTIPLE_ITEM)PropertyRequest->Value; + PKSJACK_DESCRIPTION2 pDesc = (PKSJACK_DESCRIPTION2)(pMI+1); + + pMI->Size = cbNeeded; + pMI->Count = 1; + + RtlZeroMemory(pDesc, sizeof(KSJACK_DESCRIPTION2)); + + // + // Specifies the lower 16 bits of the DWORD parameter. This parameter indicates whether + // the jack is currently active, streaming, idle, or hardware not ready. + // + pDesc->DeviceStateInfo = 0; + + // + // From MSDN: + // "If an audio device lacks jack presence detection, the IsConnected member of + // the KSJACK_DESCRIPTION structure must always be set to TRUE. To remove the + // ambiguity that results from this dual meaning of the TRUE value for IsConnected, + // a client application can call IKsJackDescription2::GetJackDescription2 to read + // the JackCapabilities flag of the KSJACK_DESCRIPTION2 structure. If this flag has + // the JACKDESC2_PRESENCE_DETECT_CAPABILITY bit set, it indicates that the endpoint + // does in fact support jack presence detection. In that case, the return value of + // the IsConnected member can be interpreted to accurately reflect the insertion status + // of the jack." + // + // Bit definitions: + // 0x00000001 - JACKDESC2_PRESENCE_DETECT_CAPABILITY + // 0x00000002 - JACKDESC2_DYNAMIC_FORMAT_CHANGE_CAPABILITY + // + pDesc->JackCapabilities = JACKDESC2_PRESENCE_DETECT_CAPABILITY; + + ntStatus = STATUS_SUCCESS; + } + } + } + } + } + + return ntStatus; +} + +//============================================================================= +NTSTATUS +PropertyHandler_MicInTopoFilter +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Redirects property request to miniport object + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_MicInTopoFilter]")); + + // PropertryRequest structure is filled by portcls. + // MajorTarget is a pointer to miniport object for miniports. + // + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + // + // This line shows how to get a pointer to the miniport topology object. + // + PCMiniportTopology pMiniport = (PCMiniportTopology)PropertyRequest->MajorTarget; + UNREFERENCED_VAR(pMiniport); + + if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_Jack)) + { + if (PropertyRequest->PropertyItem->Id == KSPROPERTY_JACK_DESCRIPTION) + { + ntStatus = PropertyHandler_MicInJackDescription(PropertyRequest); + } + else if (PropertyRequest->PropertyItem->Id == KSPROPERTY_JACK_DESCRIPTION2) + { + ntStatus = PropertyHandler_MicInJackDescription2(PropertyRequest); + } + } + + return ntStatus; +} // PropertyHandler_MicInTopoFilter + +#pragma code_seg() + diff --git a/audio/sysvad/TabletAudioSample/micintopo.h b/audio/sysvad/TabletAudioSample/micintopo.h new file mode 100644 index 000000000..af4b8be2d --- /dev/null +++ b/audio/sysvad/TabletAudioSample/micintopo.h @@ -0,0 +1,21 @@ + +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + micintopo.h + +Abstract: + + Declaration of topology miniport for the mic (external: headphone). + +--*/ + +#ifndef _SYSVAD_MICINTOPO_H_ +#define _SYSVAD_MICINTOPO_H_ + +NTSTATUS PropertyHandler_MicInTopoFilter(_In_ PPCPROPERTY_REQUEST PropertyRequest); + +#endif // _SYSVAD_MICINTOPO_H_ diff --git a/audio/sysvad/TabletAudioSample/micintoptable.h b/audio/sysvad/TabletAudioSample/micintoptable.h new file mode 100644 index 000000000..329c2f2bb --- /dev/null +++ b/audio/sysvad/TabletAudioSample/micintoptable.h @@ -0,0 +1,250 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + micintopotable.h + +Abstract: + + Declaration of topology table for the mic (external: headphone) + +--*/ + +#ifndef _SYSVAD_MICINTOPTABLE_H_ +#define _SYSVAD_MICINTOPTABLE_H_ + +// Function declarations. +NTSTATUS +PropertyHandler_MicInTopoFilter( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + +// +// {d48deb08-fd1c-4d1e-b821-9064d49ae96e} +DEFINE_GUID(MICIN_CUSTOM_NAME, +0xd48deb08, 0xfd1c, 0x4d1e, 0xb8, 0x21, 0x90, 0x64, 0xd4, 0x9a, 0xe9, 0x6e); + +//============================================================================= +static +KSJACK_DESCRIPTION MicInJackDesc = +{ + KSAUDIO_SPEAKER_MONO, + JACKDESC_RGB(179, 201, 140), + eConnTypeCombination, + eGeoLocRear, + eGenLocPrimaryBox, + ePortConnJack, + TRUE +}; + +//============================================================================= +static +KSDATARANGE MicInTopoPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +//============================================================================= +static +PKSDATARANGE MicInTopoPinDataRangePointersBridge[] = +{ + &MicInTopoPinDataRangesBridge[0] +}; + +//============================================================================= +static +PCPIN_DESCRIPTOR MicInTopoMiniportPins[] = +{ + // KSPIN - topology filter in-pin + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(MicInTopoPinDataRangePointersBridge),// DataRangesCount + MicInTopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_IN, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSNODETYPE_MICROPHONE, // Category + &MICIN_CUSTOM_NAME, // Name + 0 // Reserved + } + }, + + // KSPIN - topology filter out-in + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(MicInTopoPinDataRangePointersBridge),// DataRangesCount + MicInTopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_OUT, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSCATEGORY_AUDIO, // Category + NULL, // Name + 0 // Reserved + } + } +}; + +//============================================================================= +// Only return a KSJACK_DESCRIPTION for the physical bridge pin. +static +PKSJACK_DESCRIPTION MicInJackDescriptions[] = +{ + &MicInJackDesc, + NULL +}; + +//============================================================================= +static +PCPROPERTY_ITEM MicInPropertiesVolume[] = +{ + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_VOLUMELEVEL, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_Topology + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationMicInVolume, MicInPropertiesVolume); + +//============================================================================= +static +PCPROPERTY_ITEM MicInPropertiesMute[] = +{ + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_MUTE, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_Topology + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationMicInMute, MicInPropertiesMute); + +//============================================================================= +static +PCPROPERTY_ITEM MicInPropertiesPeakMeter[] = +{ + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_PEAKMETER2, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_Topology + }, + { + &KSPROPSETID_Audio, + KSPROPERTY_AUDIO_CPU_RESOURCES, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_Topology + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationMicInPeakMeter, MicInPropertiesPeakMeter); + +//============================================================================= +static +PCNODE_DESCRIPTOR MicInTopologyNodes[] = +{ + // KSNODE_TOPO_VOLUME + { + 0, // Flags + &AutomationMicInVolume, // AutomationTable + &KSNODETYPE_VOLUME, // Type + &KSAUDFNAME_MIC_VOLUME // Name + }, + // KSNODE_TOPO_MUTE + { + 0, // Flags + &AutomationMicInMute, // AutomationTable + &KSNODETYPE_MUTE, // Type + &KSAUDFNAME_MIC_MUTE // Name + }, + // KSNODE_TOPO_PEAKMETER + { + 0, // Flags + &AutomationMicInPeakMeter, // AutomationTable + &KSNODETYPE_PEAKMETER, // Type + &KSAUDFNAME_PEAKMETER // Name + } +}; + +C_ASSERT( KSNODE_TOPO_VOLUME == 0 ); +C_ASSERT( KSNODE_TOPO_MUTE == 1 ); +C_ASSERT( KSNODE_TOPO_PEAKMETER == 2 ); + +//============================================================================= +static +PCCONNECTION_DESCRIPTOR MicInMiniportConnections[] = +{ + // FromNode, FromPin, ToNode, ToPin + { PCFILTER_NODE, KSPIN_TOPO_MIC_ELEMENTS, KSNODE_TOPO_VOLUME, 1 }, + { KSNODE_TOPO_VOLUME, 0, KSNODE_TOPO_MUTE, 1 }, + { KSNODE_TOPO_MUTE, 0, KSNODE_TOPO_PEAKMETER, 1 }, + { KSNODE_TOPO_PEAKMETER, 0, PCFILTER_NODE, KSPIN_TOPO_BRIDGE } +}; + + +//============================================================================= +static +PCPROPERTY_ITEM MicInPropertiesTopoFilter[] = +{ + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_MicInTopoFilter + }, + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION2, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_MicInTopoFilter + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationMicInTopoFilter, MicInPropertiesTopoFilter); + +//============================================================================= +static +PCFILTER_DESCRIPTOR MicInTopoMiniportFilterDescriptor = +{ + 0, // Version + &AutomationMicInTopoFilter, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(MicInTopoMiniportPins), // PinCount + MicInTopoMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + SIZEOF_ARRAY(MicInTopologyNodes), // NodeCount + MicInTopologyNodes, // Nodes + SIZEOF_ARRAY(MicInMiniportConnections), // ConnectionCount + MicInMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories +}; + +#endif // _SYSVAD_MICINTOPTABLE_H_ + diff --git a/audio/sysvad/TabletAudioSample/micinwavtable.h b/audio/sysvad/TabletAudioSample/micinwavtable.h new file mode 100644 index 000000000..42598c4e6 --- /dev/null +++ b/audio/sysvad/TabletAudioSample/micinwavtable.h @@ -0,0 +1,440 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + micinwavtable.h + +Abstract: + + Declaration of wave miniport tables for the mic (external: headphone). + +--*/ + +#ifndef _SYSVAD_MICINWAVTABLE_H_ +#define _SYSVAD_MICINWAVTABLE_H_ + +// +// Mic in (external: headphone) range. +// +#define MICIN_DEVICE_MAX_CHANNELS 1 // Max Channels. +#define MICIN_MIN_BITS_PER_SAMPLE_PCM 16 // Min Bits Per Sample +#define MICIN_MAX_BITS_PER_SAMPLE_PCM 16 // Max Bits Per Sample +#define MICIN_MIN_SAMPLE_RATE 8000 // Min Sample Rate +#define MICIN_MAX_SAMPLE_RATE 48000 // Max Sample Rate + +// +// Max # of pin instances. +// +#define MICIN_MAX_INPUT_STREAMS 2 // Raw + Default streams + +//============================================================================= +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE MicInPinSupportedDeviceFormats[] = +{ + { // 0 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 8000, + 16000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 1 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 11025, + 22050, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 2 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 16000, + 32000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 3 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 22050, + 44100, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 4 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 24000, + 48000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 5 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 32000, + 64000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 6 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 44100, + 88200, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 7 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 1, + 48000, + 96000, + 2, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_MONO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +// +// Supported modes (only on streaming pins). +// +static +MODE_AND_DEFAULT_FORMAT MicInPinSupportedDeviceModes[] = +{ + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_RAW, + &MicInPinSupportedDeviceFormats[SIZEOF_ARRAY(MicInPinSupportedDeviceFormats) - 1].DataFormat, + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_DEFAULT, + &MicInPinSupportedDeviceFormats[SIZEOF_ARRAY(MicInPinSupportedDeviceFormats) - 1].DataFormat, + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_SPEECH, + &MicInPinSupportedDeviceFormats[2].DataFormat, // 16KHz + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_COMMUNICATIONS, + &MicInPinSupportedDeviceFormats[4].DataFormat, // 24KHz + } +}; + +// +// The entries here must follow the same order as the filter's pin +// descriptor array. +static +PIN_DEVICE_FORMATS_AND_MODES MicInPinDeviceFormatsAndModes[] = +{ + { + BridgePin, + NULL, + 0, + NULL, + 0 + }, + { + SystemCapturePin, + MicInPinSupportedDeviceFormats, + SIZEOF_ARRAY(MicInPinSupportedDeviceFormats), + MicInPinSupportedDeviceModes, + SIZEOF_ARRAY(MicInPinSupportedDeviceModes) + } +}; + +//============================================================================= +static +KSDATARANGE MicInPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +static +PKSDATARANGE MicInPinDataRangePointersBridge[] = +{ + &MicInPinDataRangesBridge[0] +}; + +//============================================================================= +static +KSDATARANGE_AUDIO MicInPinDataRangesStream[] = +{ + { + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + MICIN_DEVICE_MAX_CHANNELS, + MICIN_MIN_BITS_PER_SAMPLE_PCM, + MICIN_MAX_BITS_PER_SAMPLE_PCM, + MICIN_MIN_SAMPLE_RATE, + MICIN_MAX_SAMPLE_RATE + }, +}; + +static +PKSDATARANGE MicInPinDataRangePointersStream[] = +{ + PKSDATARANGE(&MicInPinDataRangesStream[0]), + PKSDATARANGE(&PinDataRangeAttributeList), +}; + +//============================================================================= +static +PCPIN_DESCRIPTOR MicInWaveMiniportPins[] = +{ + // Wave In Bridge Pin (Capture - From Topology) KSPIN_WAVE_BRIDGE + { + 0, + 0, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(MicInPinDataRangePointersBridge), + MicInPinDataRangePointersBridge, + KSPIN_DATAFLOW_IN, + KSPIN_COMMUNICATION_NONE, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, + + // Wave In Streaming Pin (Capture) KSPIN_WAVEIN_HOST + { + MICIN_MAX_INPUT_STREAMS, + MICIN_MAX_INPUT_STREAMS, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(MicInPinDataRangePointersStream), + MicInPinDataRangePointersStream, + KSPIN_DATAFLOW_OUT, + KSPIN_COMMUNICATION_SINK, + &KSCATEGORY_AUDIO, + &KSAUDFNAME_RECORDING_CONTROL, + 0 + } + } +}; + +//============================================================================= +static +PCNODE_DESCRIPTOR MicInWaveMiniportNodes[] = +{ + // KSNODE_WAVE_ADC + { + 0, // Flags + NULL, // AutomationTable + &KSNODETYPE_ADC, // Type + NULL // Name + } +}; + +//============================================================================= +static +PCCONNECTION_DESCRIPTOR MicInWaveMiniportConnections[] = +{ + { PCFILTER_NODE, KSPIN_WAVE_BRIDGE, KSNODE_WAVE_ADC, 1 }, + { KSNODE_WAVE_ADC, 0, PCFILTER_NODE, KSPIN_WAVEIN_HOST }, +}; + +//============================================================================= +static +PCPROPERTY_ITEM PropertiesMicInWaveFilter[] = +{ + { + &KSPROPSETID_General, + KSPROPERTY_GENERAL_COMPONENTID, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_WaveFilter + }, + { + &KSPROPSETID_Pin, + KSPROPERTY_PIN_PROPOSEDATAFORMAT, + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_WaveFilter + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationMicInWaveFilter, PropertiesMicInWaveFilter); + +//============================================================================= +static +PCFILTER_DESCRIPTOR MicInWaveMiniportFilterDescriptor = +{ + 0, // Version + &AutomationMicInWaveFilter, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(MicInWaveMiniportPins), // PinCount + MicInWaveMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + SIZEOF_ARRAY(MicInWaveMiniportNodes), // NodeCount + MicInWaveMiniportNodes, // Nodes + SIZEOF_ARRAY(MicInWaveMiniportConnections), // ConnectionCount + MicInWaveMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories - use defaults (audio, render, capture) +}; + +#endif // _SYSVAD_MICINWAVTABLE_H_ diff --git a/audio/sysvad/TabletAudioSample/minipairs.h b/audio/sysvad/TabletAudioSample/minipairs.h new file mode 100644 index 000000000..f35398d4b --- /dev/null +++ b/audio/sysvad/TabletAudioSample/minipairs.h @@ -0,0 +1,512 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + minipairs.h + +Abstract: + + Local audio endpoint filter definitions. + +--*/ + +#ifndef _SYSVAD_MINIPAIRS_H_ +#define _SYSVAD_MINIPAIRS_H_ + +#include "speakertopo.h" +#include "speakertoptable.h" +#include "speakerwavtable.h" + +#include "speakerhptopo.h" +#include "speakerhptoptable.h" +#include "speakerhpwavtable.h" + +#include "hdmitopo.h" +#include "hdmitoptable.h" +#include "hdmiwavtable.h" + +#include "spdiftopo.h" +#include "spdiftoptable.h" +#include "spdifwavtable.h" + +#include "micintopo.h" +#include "micintoptable.h" +#include "micinwavtable.h" + +#include "micarraytopo.h" +#include "micarray1toptable.h" +#include "micarray2toptable.h" +#include "micarray3toptable.h" +#include "micarraywavtable.h" +#include "micarray3wavtable.h" + +NTSTATUS +CreateMiniportWaveRTSYSVAD +( + _Out_ PUNKNOWN *, + _In_ REFCLSID, + _In_opt_ PUNKNOWN, + _In_ POOL_TYPE, + _In_ PUNKNOWN, + _In_opt_ PVOID, + _In_ PENDPOINT_MINIPAIR +); + +NTSTATUS +CreateMiniportTopologySYSVAD +( + _Out_ PUNKNOWN *, + _In_ REFCLSID, + _In_opt_ PUNKNOWN, + _In_ POOL_TYPE, + _In_ PUNKNOWN, + _In_opt_ PVOID, + _In_ PENDPOINT_MINIPAIR +); + +// +// Describe buffer size constraints for WaveRT buffers +// +static struct +{ + KSAUDIO_PACKETSIZE_CONSTRAINTS TransportPacketConstraints; + KSAUDIO_PACKETSIZE_PROCESSINGMODE_CONSTRAINT AdditionalProcessingConstraints[1]; +} SysvadWaveRtPacketSizeConstraintsRender = +{ + { + 1 * HNSTIME_PER_MILLISECOND, // 1 ms minimum processing interval + FILE_256_BYTE_ALIGNMENT, // 256 byte packet size alignment + 0, // reserved + 2, // 2 processing constraints follow + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_DEFAULT, // constraint for default processing mode + 128, // 128 samples per processing frame + 0, // NA hns per processing frame + }, + }, + { + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_MOVIE, // constraint for movie processing mode + 1024, // 1024 samples per processing frame + 0, // NA hns per processing frame + }, + } +}; + +const SYSVAD_DEVPROPERTY SysvadWaveFilterInterfacePropertiesRender[] = +{ + { + &DEVPKEY_KsAudio_PacketSize_Constraints, // Key + DEVPROP_TYPE_BINARY, // Type + sizeof(SysvadWaveRtPacketSizeConstraintsRender), // BufferSize + &SysvadWaveRtPacketSizeConstraintsRender, // Buffer + }, +}; + +static struct +{ + KSAUDIO_PACKETSIZE_CONSTRAINTS TransportPacketConstraints; +} SysvadWaveRtPacketSizeConstraintsCapture = +{ + { + 0, // no minimum processing interval + FILE_128_BYTE_ALIGNMENT, // 128 byte packet size alignment + 0, // reserved + 1, // 1 processing constraint follows + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_COMMUNICATIONS, // constraint for communications processing mode + 0, // NA samples per processing frame + 20 * HNSTIME_PER_MILLISECOND, // 200000 hns (20ms) per processing frame + }, + }, +}; + +const SYSVAD_DEVPROPERTY SysvadWaveFilterInterfacePropertiesCapture[] = +{ + { + &DEVPKEY_KsAudio_PacketSize_Constraints, // Key + DEVPROP_TYPE_BINARY, // Type + sizeof(SysvadWaveRtPacketSizeConstraintsCapture), // BufferSize + &SysvadWaveRtPacketSizeConstraintsCapture, // Buffer + }, +}; + +// +// Render miniports. +// + +/********************************************************************* +* Topology/Wave bridge connection for speaker (internal) * +* * +* +------+ +------+ * +* | Wave | | Topo | * +* | | | | * +* System --->|0 2|---> Loopback | | * +* | | | | * +* Offload --->|1 3|--------------->|0 1|---> Line Out * +* | | | | * +* +------+ +------+ * +*********************************************************************/ +static +PHYSICALCONNECTIONTABLE SpeakerTopologyPhysicalConnections[] = +{ + { + KSPIN_TOPO_WAVEOUT_SOURCE, // TopologyIn + KSPIN_WAVE_RENDER_SOURCE, // WaveOut + CONNECTIONTYPE_WAVE_OUTPUT + } +}; + +static +ENDPOINT_MINIPAIR SpeakerMiniports = +{ + eSpeakerDevice, + L"TopologySpeaker", // make sure this name matches with KSNAME_TopologySpeaker in the inf's [Strings] section + CreateMiniportTopologySYSVAD, + &SpeakerTopoMiniportFilterDescriptor, + 0, NULL, // Interface properties + L"WaveSpeaker", // make sure this name matches with KSNAME_WaveSpeaker in the inf's [Strings] section + CreateMiniportWaveRTSYSVAD, + &SpeakerWaveMiniportFilterDescriptor, + ARRAYSIZE(SysvadWaveFilterInterfacePropertiesRender), // Interface properties + SysvadWaveFilterInterfacePropertiesRender, + SPEAKER_DEVICE_MAX_CHANNELS, + SpeakerPinDeviceFormatsAndModes, + SIZEOF_ARRAY(SpeakerPinDeviceFormatsAndModes), + SpeakerTopologyPhysicalConnections, + SIZEOF_ARRAY(SpeakerTopologyPhysicalConnections), + ENDPOINT_OFFLOAD_SUPPORTED +}; + +/********************************************************************* +* Topology/Wave bridge connection for speaker (external:headphone) * +* * +* +------+ +------+ * +* | Wave | | Topo | * +* | | | | * +* System --->|0 2|---> Loopback | | * +* | | | | * +* Offload --->|1 3|--------------->|0 1|---> Line Out * +* | | | | * +* +------+ +------+ * +*********************************************************************/ +static +PHYSICALCONNECTIONTABLE SpeakerHpTopologyPhysicalConnections[] = +{ + { + KSPIN_TOPO_WAVEOUT_SOURCE, // TopologyIn + KSPIN_WAVE_RENDER_SOURCE, // WaveOut + CONNECTIONTYPE_WAVE_OUTPUT + } +}; + +static +ENDPOINT_MINIPAIR SpeakerHpMiniports = +{ + eSpeakerHpDevice, + L"TopologySpeakerHeadphone", // make sure this name matches with KSNAME_TopologySpeakerHeadphone in the inf's [Strings] section + CreateMiniportTopologySYSVAD, + &SpeakerHpTopoMiniportFilterDescriptor, + 0, NULL, // Interface properties + L"WaveSpeakerHeadphone", // make sure this name matches with KSNAME_WaveSpeakerHeadphone in the inf's [Strings] section + CreateMiniportWaveRTSYSVAD, + &SpeakerHpWaveMiniportFilterDescriptor, + 0, NULL, // Interface properties + SPEAKERHP_DEVICE_MAX_CHANNELS, + SpeakerHpPinDeviceFormatsAndModes, + SIZEOF_ARRAY(SpeakerHpPinDeviceFormatsAndModes), + SpeakerHpTopologyPhysicalConnections, + SIZEOF_ARRAY(SpeakerHpTopologyPhysicalConnections), + ENDPOINT_OFFLOAD_SUPPORTED +}; + +/********************************************************************* +* Topology/Wave bridge connection for hdmi endpoint * +* * +* +------+ +------+ * +* | Wave | | Topo | * +* | | | | * +* System --->|0 1|---> Loopback | | * +* | | | | * +* | 2|--------------->|0 1|---> Line Out * +* | | | | * +* +------+ +------+ * +*********************************************************************/ +static +PHYSICALCONNECTIONTABLE HdmiTopologyPhysicalConnections[] = +{ + { + KSPIN_TOPO_WAVEOUT_SOURCE, // TopologyIn + KSPIN_WAVE_RENDER2_SOURCE, // WaveOut (no offloading) + CONNECTIONTYPE_WAVE_OUTPUT + } +}; + +static +ENDPOINT_MINIPAIR HdmiMiniports = +{ + eHdmiRenderDevice, + L"TopologyHdmi", // make sure this name matches with KSNAME_TopologyHdmi in the inf's [Strings] section + CreateHdmiMiniportTopology, + &HdmiTopoMiniportFilterDescriptor, + 0, NULL, // Interface properties + L"WaveHdmi", // make sure this name matches with KSNAME_WaveHdmi in the inf's [Strings] section + CreateMiniportWaveRTSYSVAD, + &HdmiWaveMiniportFilterDescriptor, + 0, NULL, // Interface properties + HDMI_DEVICE_MAX_CHANNELS, + HdmiPinDeviceFormatsAndModes, + SIZEOF_ARRAY(HdmiPinDeviceFormatsAndModes), + HdmiTopologyPhysicalConnections, + SIZEOF_ARRAY(HdmiTopologyPhysicalConnections), + ENDPOINT_NO_FLAGS +}; + +/********************************************************************* +* Topology/Wave bridge connection for spdif (internal) * +* * +* +------+ +------+ * +* | Wave | | Topo | * +* | | | | * +* System --->|0 2|---> Loopback | | * +* | | | | * +* Offload --->|1 3|--------------->|0 1|---> Line Out * +* | | | | * +* +------+ +------+ * +*********************************************************************/ +static +PHYSICALCONNECTIONTABLE SpdifTopologyPhysicalConnections[] = +{ + { + KSPIN_TOPO_WAVEOUT_SOURCE, // TopologyIn + KSPIN_WAVE_RENDER_SOURCE, // WaveOut + CONNECTIONTYPE_WAVE_OUTPUT + } +}; + +static +ENDPOINT_MINIPAIR SpdifMiniports = +{ + eSpdifRenderDevice, + L"TopologySpdif", // make sure this name matches with KSNAME_TopologySpeaker in the inf's [Strings] section + CreateMiniportTopologySYSVAD, + &SpdifTopoMiniportFilterDescriptor, + 0, NULL, // Interface properties + L"WaveSpdif", // make sure this name matches with KSNAME_WaveSpeaker in the inf's [Strings] section + CreateMiniportWaveRTSYSVAD, + &SpdifWaveMiniportFilterDescriptor, + ARRAYSIZE(SysvadWaveFilterInterfacePropertiesRender), // Interface properties + SysvadWaveFilterInterfacePropertiesRender, + SPDIF_DEVICE_MAX_CHANNELS, + SpdifPinDeviceFormatsAndModes, + SIZEOF_ARRAY(SpdifPinDeviceFormatsAndModes), + SpdifTopologyPhysicalConnections, + SIZEOF_ARRAY(SpdifTopologyPhysicalConnections), + ENDPOINT_OFFLOAD_SUPPORTED +}; + +// +// Capture miniports. +// + +/********************************************************************* +* Topology/Wave bridge connection for mic in * +* * +* +------+ +------+ * +* | Topo | | Wave | * +* | | | | * +* Mic in --->|0 1|===>|0 1|---> Capture Host Pin * +* | | | | * +* +------+ +------+ * +*********************************************************************/ +static +PHYSICALCONNECTIONTABLE MicInTopologyPhysicalConnections[] = +{ + { + KSPIN_TOPO_BRIDGE, // TopologyOut + KSPIN_WAVE_BRIDGE, // WaveIn + CONNECTIONTYPE_TOPOLOGY_OUTPUT + } +}; + +static +ENDPOINT_MINIPAIR MicInMiniports = +{ + eMicInDevice, + L"TopologyMicIn", // make sure this name matches with KSNAME_TopologyMicIn in the inf's [Strings] section + CreateMiniportTopologySYSVAD, + &MicInTopoMiniportFilterDescriptor, + 0, NULL, // Interface properties + L"WaveMicIn", // make sure this name matches with KSNAME_WaveMicIn in the inf's [Strings] section + CreateMiniportWaveRTSYSVAD, + &MicInWaveMiniportFilterDescriptor, + 0, NULL, // Interface properties + MICIN_DEVICE_MAX_CHANNELS, + MicInPinDeviceFormatsAndModes, + SIZEOF_ARRAY(MicInPinDeviceFormatsAndModes), + MicInTopologyPhysicalConnections, + SIZEOF_ARRAY(MicInTopologyPhysicalConnections), + ENDPOINT_NO_FLAGS +}; + +/********************************************************************* +* Topology/Wave bridge connection for mic array 1 (front) * +* * +* +------+ +------+ * +* | Topo | | Wave | * +* | | | | * +* Mic in --->|0 1|===>|0 1|---> Capture Host Pin * +* | | | | * +* +------+ +------+ * +*********************************************************************/ +static +PHYSICALCONNECTIONTABLE MicArray1TopologyPhysicalConnections[] = +{ + { + KSPIN_TOPO_BRIDGE, // TopologyOut + KSPIN_WAVE_BRIDGE, // WaveIn + CONNECTIONTYPE_TOPOLOGY_OUTPUT + } +}; + +static +ENDPOINT_MINIPAIR MicArray1Miniports = +{ + eMicArrayDevice1, + L"TopologyMicArray1", // make sure this name matches with KSNAME_TopologyMicArray1 in the inf's [Strings] section + CreateMicArrayMiniportTopology, + &MicArray1TopoMiniportFilterDescriptor, + 0, NULL, // Interface properties + L"WaveMicArray1", // make sure this name matches with KSNAME_WaveMicArray1 in the inf's [Strings] section + CreateMiniportWaveRTSYSVAD, + &MicArrayWaveMiniportFilterDescriptor, + ARRAYSIZE(SysvadWaveFilterInterfacePropertiesCapture), // Interface properties + SysvadWaveFilterInterfacePropertiesCapture, + MICARRAY_DEVICE_MAX_CHANNELS, + MicArrayPinDeviceFormatsAndModes, + SIZEOF_ARRAY(MicArrayPinDeviceFormatsAndModes), + MicArray1TopologyPhysicalConnections, + SIZEOF_ARRAY(MicArray1TopologyPhysicalConnections), + ENDPOINT_SOUNDDETECTOR_SUPPORTED +}; + +/********************************************************************* +* Topology/Wave bridge connection for mic array 2 (back) * +* * +* +------+ +------+ * +* | Topo | | Wave | * +* | | | | * +* Mic in --->|0 1|===>|0 1|---> Capture Host Pin * +* | | | | * +* +------+ +------+ * +*********************************************************************/ +static +PHYSICALCONNECTIONTABLE MicArray2TopologyPhysicalConnections[] = +{ + { + KSPIN_TOPO_BRIDGE, // TopologyOut + KSPIN_WAVE_BRIDGE, // WaveIn + CONNECTIONTYPE_TOPOLOGY_OUTPUT + } +}; + +static +ENDPOINT_MINIPAIR MicArray2Miniports = +{ + eMicArrayDevice2, + L"TopologyMicArray2", // make sure this name matches with KSNAME_TopologyMicArray2 in the inf's [Strings] section + CreateMicArrayMiniportTopology, + &MicArray2TopoMiniportFilterDescriptor, + 0, NULL, // Interface properties + L"WaveMicArray2", // make sure this name matches with KSNAME_WaveMicArray2 in the inf's [Strings] section + CreateMiniportWaveRTSYSVAD, + &MicArrayWaveMiniportFilterDescriptor, + 0, NULL, // Interface properties + MICARRAY_DEVICE_MAX_CHANNELS, + MicArrayPinDeviceFormatsAndModes, + SIZEOF_ARRAY(MicArrayPinDeviceFormatsAndModes), + MicArray2TopologyPhysicalConnections, + SIZEOF_ARRAY(MicArray2TopologyPhysicalConnections), + ENDPOINT_SOUNDDETECTOR_SUPPORTED +}; + +/********************************************************************* +* Topology/Wave bridge connection for mic array 3 (combined) * +* * +* +------+ +------+ * +* | Topo | | Wave | * +* | | | | * +* Mic in --->|0 1|===>|0 1|---> Capture Host Pin * +* | | | | * +* +------+ +------+ * +*********************************************************************/ +static +PHYSICALCONNECTIONTABLE MicArray3TopologyPhysicalConnections[] = +{ + { + KSPIN_TOPO_BRIDGE, // TopologyOut + KSPIN_WAVE_BRIDGE, // WaveIn + CONNECTIONTYPE_TOPOLOGY_OUTPUT + } +}; + +static +ENDPOINT_MINIPAIR MicArray3Miniports = +{ + eMicArrayDevice3, + L"TopologyMicArray3", // make sure this name matches with KSNAME_TopologyMicArray3 in the inf's [Strings] section + CreateMicArrayMiniportTopology, + &MicArray3TopoMiniportFilterDescriptor, + 0, NULL, // Interface properties + L"WaveMicArray3", // make sure this name matches with KSNAME_WaveMicArray3 in the inf's [Strings] section + CreateMiniportWaveRTSYSVAD, + &MicArray3WaveMiniportFilterDescriptor, + 0, NULL, // Interface properties + MICARRAY3_DEVICE_MAX_CHANNELS, + MicArray3PinDeviceFormatsAndModes, + SIZEOF_ARRAY(MicArray3PinDeviceFormatsAndModes), + MicArray3TopologyPhysicalConnections, + SIZEOF_ARRAY(MicArray3TopologyPhysicalConnections), + ENDPOINT_NO_FLAGS +}; + +//============================================================================= +// +// Render miniport pairs. +// +static +PENDPOINT_MINIPAIR g_RenderEndpoints[] = +{ + &SpeakerMiniports, + &SpeakerHpMiniports, + &HdmiMiniports, + &SpdifMiniports, +}; + +#define g_cRenderEndpoints (SIZEOF_ARRAY(g_RenderEndpoints)) + +//============================================================================= +// +// Capture miniport pairs. +// +static +PENDPOINT_MINIPAIR g_CaptureEndpoints[] = +{ + &MicInMiniports, + &MicArray1Miniports, + &MicArray2Miniports, + &MicArray3Miniports, +}; + +#define g_cCaptureEndpoints (SIZEOF_ARRAY(g_CaptureEndpoints)) + +//============================================================================= +// +// Total miniports = # endpoints * 2 (topology + wave). +// +#define g_MaxMiniports ((g_cRenderEndpoints + g_cCaptureEndpoints) * 2) + +#endif // _SYSVAD_MINIPAIRS_H_ + diff --git a/audio/sysvad/TabletAudioSample/spdiftopo.cpp b/audio/sysvad/TabletAudioSample/spdiftopo.cpp new file mode 100644 index 000000000..f715e41d7 --- /dev/null +++ b/audio/sysvad/TabletAudioSample/spdiftopo.cpp @@ -0,0 +1,85 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + spdiftopo.cpp + +Abstract: + + Implementation of topology miniport for the spdif (internal). + +--*/ + +#pragma warning (disable : 4127) + +#include +#include "simple.h" +#include "mintopo.h" +#include "spdiftopo.h" +#include "spdiftoptable.h" + + +#pragma code_seg("PAGE") + +//============================================================================= +NTSTATUS +PropertyHandler_SpdifTopoFilter +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Redirects property request to miniport object + +Arguments: + + PropertyRequest - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(PropertyRequest); + + DPF_ENTER(("[PropertyHandler_SpdifTopoFilter]")); + + // PropertryRequest structure is filled by portcls. + // MajorTarget is a pointer to miniport object for miniports. + // + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + PCMiniportTopology pMiniport = (PCMiniportTopology)PropertyRequest->MajorTarget; + + if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_Jack)) + { + if (PropertyRequest->PropertyItem->Id == KSPROPERTY_JACK_DESCRIPTION) + { + ntStatus = pMiniport->PropertyHandlerJackDescription( + PropertyRequest, + ARRAYSIZE(SpdifJackDescriptions), + SpdifJackDescriptions + ); + } + else if (PropertyRequest->PropertyItem->Id == KSPROPERTY_JACK_DESCRIPTION2) + { + ntStatus = pMiniport->PropertyHandlerJackDescription2( + PropertyRequest, + ARRAYSIZE(SpdifJackDescriptions), + SpdifJackDescriptions, + 0 // jack capabilities + ); + } + } + + return ntStatus; +} // PropertyHandler_SpdifTopoFilter + +#pragma code_seg() + diff --git a/audio/sysvad/TabletAudioSample/spdiftopo.h b/audio/sysvad/TabletAudioSample/spdiftopo.h new file mode 100644 index 000000000..ffaefb5cc --- /dev/null +++ b/audio/sysvad/TabletAudioSample/spdiftopo.h @@ -0,0 +1,21 @@ + +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + speakertopo.h + +Abstract: + + Declaration of topology miniport for the speaker (internal). + +--*/ + +#ifndef _SYSVAD_SPDIFTOPO_H_ +#define _SYSVAD_SPDIFTOPO_H_ + +NTSTATUS PropertyHandler_SpdifTopoFilter(_In_ PPCPROPERTY_REQUEST PropertyRequest); + +#endif // _SYSVAD_SPDIFTOPO_H_ diff --git a/audio/sysvad/TabletAudioSample/spdiftoptable.h b/audio/sysvad/TabletAudioSample/spdiftoptable.h new file mode 100644 index 000000000..8e1376a82 --- /dev/null +++ b/audio/sysvad/TabletAudioSample/spdiftoptable.h @@ -0,0 +1,166 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + spdiftoptable.h + +Abstract: + + Declaration of topology tables for the SPDIF endpoint. + +--*/ + +#ifndef _SYSVAD_SPDIFTOPTABLE_H_ +#define _SYSVAD_SPDIFTOPTABLE_H_ + +//============================================================================= +static +KSDATARANGE SpdifTopoPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + }, + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +//============================================================================= +static +PKSDATARANGE SpdifTopoPinDataRangePointersBridge[] = +{ + &SpdifTopoPinDataRangesBridge[0], + &SpdifTopoPinDataRangesBridge[1] +}; + +//============================================================================= +static +PCPIN_DESCRIPTOR SpdifTopoMiniportPins[] = +{ + // KSPIN_TOPO_WAVEOUT_SOURCE + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(SpdifTopoPinDataRangePointersBridge), // DataRangesCount + SpdifTopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_IN, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSCATEGORY_AUDIO, // Category + NULL, // Name + 0 // Reserved + } + }, + // KSPIN_TOPO_LINEOUT_DEST + { + 0, + 0, + 0, // InstanceCount + NULL, // AutomationTable + { // KsPinDescriptor + 0, // InterfacesCount + NULL, // Interfaces + 0, // MediumsCount + NULL, // Mediums + SIZEOF_ARRAY(SpdifTopoPinDataRangePointersBridge), // DataRangesCount + SpdifTopoPinDataRangePointersBridge, // DataRanges + KSPIN_DATAFLOW_OUT, // DataFlow + KSPIN_COMMUNICATION_NONE, // Communication + &KSNODETYPE_SPDIF_INTERFACE, // Category + NULL, // Name + 0 // Reserved + } + } +}; + +//============================================================================= +static +KSJACK_DESCRIPTION SpdifJackDesc = +{ + KSAUDIO_SPEAKER_STEREO, + 0x0000, // no color + eConnTypeOtherDigital, + eGeoLocRear, + eGenLocPrimaryBox, + ePortConnJack, + TRUE +}; + +// Only return a KSJACK_DESCRIPTION for the physical bridge pin. +static +PKSJACK_DESCRIPTION SpdifJackDescriptions[] = +{ + NULL, + &SpdifJackDesc +}; + +//============================================================================= +static +PCCONNECTION_DESCRIPTOR SpdifTopoMiniportConnections[] = +{ + // FromNode, FromPin, ToNode, ToPin + { PCFILTER_NODE, KSPIN_TOPO_WAVEOUT_SOURCE, PCFILTER_NODE, KSPIN_TOPO_LINEOUT_DEST} +}; + + +//============================================================================= +static +PCPROPERTY_ITEM PropertiesSpdifTopoFilter[] = +{ + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_SpdifTopoFilter + }, + { + &KSPROPSETID_Jack, + KSPROPERTY_JACK_DESCRIPTION2, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_SpdifTopoFilter + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationSpdifTopoFilter, PropertiesSpdifTopoFilter); + +//============================================================================= +static +PCFILTER_DESCRIPTOR SpdifTopoMiniportFilterDescriptor = +{ + 0, // Version + &AutomationSpdifTopoFilter, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(SpdifTopoMiniportPins), // PinCount + SpdifTopoMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + 0, // NodeCount + NULL, // Nodes + SIZEOF_ARRAY(SpdifTopoMiniportConnections), // ConnectionCount + SpdifTopoMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories +}; + +#endif // _SYSVAD_SPDIFTOPTABLE_H_ + + diff --git a/audio/sysvad/TabletAudioSample/spdifwavtable.h b/audio/sysvad/TabletAudioSample/spdifwavtable.h new file mode 100644 index 000000000..963822a8f --- /dev/null +++ b/audio/sysvad/TabletAudioSample/spdifwavtable.h @@ -0,0 +1,854 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + spdifwavtable.h + +Abstract: + + Declaration of wave miniport tables for the SPDIF endpoint. + +--*/ + +#ifndef _SYSVAD_SPDIFWAVTABLE_H_ +#define _SYSVAD_SPDIFWAVTABLE_H_ + + +//============================================================================= +// Defines +//============================================================================= + + +#define SPDIF_DEVICE_MAX_CHANNELS 2 // Max Channels. + +#define SPDIF_HOST_MAX_CHANNELS 2 // Max Channels. +#define SPDIF_HOST_MIN_BITS_PER_SAMPLE 16 // Min Bits Per Sample +#define SPDIF_HOST_MAX_BITS_PER_SAMPLE 16 // Max Bits Per Sample +#define SPDIF_HOST_MIN_SAMPLE_RATE 44100 // Min Sample Rate +#define SPDIF_HOST_MAX_SAMPLE_RATE 96000 // Max Sample Rate + +#define SPDIF_OFFLOAD_MAX_CHANNELS 2 // Max Channels. +#define SPDIF_OFFLOAD_MIN_BITS_PER_SAMPLE 16 // Min Bits Per Sample +#define SPDIF_OFFLOAD_MAX_BITS_PER_SAMPLE 16 // Max Bits Per Sample +#define SPDIF_OFFLOAD_MIN_SAMPLE_RATE 44100 // Min Sample Rate +#define SPDIF_OFFLOAD_MAX_SAMPLE_RATE 96000 // Max Sample Rate + +#define SPDIF_LOOPBACK_MAX_CHANNELS 2 // Max Channels. +#define SPDIF_LOOPBACK_MIN_BITS_PER_SAMPLE 16 // Min Bits Per Sample +#define SPDIF_LOOPBACK_MAX_BITS_PER_SAMPLE 16 // Max Bits Per Sample +#define SPDIF_LOOPBACK_MIN_SAMPLE_RATE 44100 // Min Sample Rate +#define SPDIF_LOOPBACK_MAX_SAMPLE_RATE 48000 // Max Sample Rate + +#define SPDIF_DOLBY_DIGITAL_MAX_CHANNELS 2 // Max Channels. +#define SPDIF_DOLBY_DIGITAL_MIN_BITS_PER_SAMPLE 16 // Min Bits Per Sample +#define SPDIF_DOLBY_DIGITAL_MAX_BITS_PER_SAMPLE 16 // Max Bits Per Sample +#define SPDIF_DOLBY_DIGITAL_MIN_SAMPLE_RATE 44100 // Min Sample Rate +#define SPDIF_DOLBY_DIGITAL_MAX_SAMPLE_RATE 48000 // Max Sample Rate + +// +// Max # of pin instances. +// +#define SPDIF_MAX_INPUT_SYSTEM_STREAMS 2 // Raw + Default streams +#define SPDIF_MAX_INPUT_OFFLOAD_STREAMS MAX_INPUT_OFFLOAD_STREAMS +#define SPDIF_MAX_OUTPUT_LOOPBACK_STREAMS MAX_OUTPUT_LOOPBACK_STREAMS + + +//============================================================================= +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE SpdifAudioEngineSupportedDeviceFormats[] = +{ + { // 0 : First entry in this table is the default format for the audio engine + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 44100, + 176400, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 1 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 48000, + 192000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 2 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 88200, + 352800, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 3 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 96000, + 384000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE SpdifHostPinSupportedDeviceFormats[] = +{ + { // 0 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 44100, + 176400, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 1 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 48000, + 192000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 2 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 88200, + 352800, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 3 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 96000, + 384000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 4 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 44100, + 176400, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_5POINT1_SURROUND, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL) + } + }, + { // 5 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 48000, + 192000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_5POINT1_SURROUND, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL) + } + } +}; + +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE SpdifOffloadPinSupportedDeviceFormats[] = +{ + { // 0 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 44100, + 176400, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 1 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 48000, + 192000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 2 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 88200, + 352800, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 3 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 96000, + 384000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +static +KSDATAFORMAT_WAVEFORMATEXTENSIBLE SpdifLoopbackPinSupportedDeviceFormats[] = +{ + { // 0 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 44100, + 176400, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + }, + { // 1 + { + sizeof(KSDATAFORMAT_WAVEFORMATEXTENSIBLE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + { + { + WAVE_FORMAT_EXTENSIBLE, + 2, + 48000, + 192000, + 4, + 16, + sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) + }, + 16, + KSAUDIO_SPEAKER_STEREO, + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM) + } + } +}; + +// +// Supported modes (only on streaming pins). +// +static +MODE_AND_DEFAULT_FORMAT SpdifHostPinSupportedDeviceModes[] = +{ + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_RAW, + NULL, // just an example of no default format for this endpoint/mode + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_DEFAULT, + NULL, // just an example of no default format for this endpoint/mode + } +}; + +static +MODE_AND_DEFAULT_FORMAT SpdifOffloadPinSupportedDeviceModes[] = +{ + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_RAW, + NULL, // just an example of no default format for this endpoint/mode + }, + { + STATIC_AUDIO_SIGNALPROCESSINGMODE_DEFAULT, + NULL, // just an example of no default format for this endpoint/mode + } +}; + +// +// The entries here must follow the same order as the filter's pin +// descriptor array. +// +static +PIN_DEVICE_FORMATS_AND_MODES SpdifPinDeviceFormatsAndModes[] = +{ + { + SystemRenderPin, + SpdifHostPinSupportedDeviceFormats, + SIZEOF_ARRAY(SpdifHostPinSupportedDeviceFormats), + SpdifHostPinSupportedDeviceModes, + SIZEOF_ARRAY(SpdifHostPinSupportedDeviceModes) + }, + { + OffloadRenderPin, + SpdifOffloadPinSupportedDeviceFormats, + SIZEOF_ARRAY(SpdifOffloadPinSupportedDeviceFormats), + SpdifOffloadPinSupportedDeviceModes, + SIZEOF_ARRAY(SpdifOffloadPinSupportedDeviceModes), + }, + { + RenderLoopbackPin, + SpdifLoopbackPinSupportedDeviceFormats, + SIZEOF_ARRAY(SpdifLoopbackPinSupportedDeviceFormats), + NULL, // loopback doesn't support modes. + 0 + }, + { + BridgePin, + NULL, + 0, + NULL, + 0 + }, + { + NoPin, // For convenience, offload engine device formats appended here + SpdifAudioEngineSupportedDeviceFormats, + SIZEOF_ARRAY(SpdifAudioEngineSupportedDeviceFormats), + NULL, // no modes for this entry. + 0 + } +}; + +//============================================================================= +static +KSDATARANGE_AUDIO SpdifPinDataRangesStream[] = +{ + { // 0 - PCM host + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + SPDIF_HOST_MAX_CHANNELS, + SPDIF_HOST_MIN_BITS_PER_SAMPLE, + SPDIF_HOST_MAX_BITS_PER_SAMPLE, + SPDIF_HOST_MIN_SAMPLE_RATE, + SPDIF_HOST_MAX_SAMPLE_RATE + }, + { // 1 - PCM offload + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + SPDIF_OFFLOAD_MAX_CHANNELS, + SPDIF_OFFLOAD_MIN_BITS_PER_SAMPLE, + SPDIF_OFFLOAD_MAX_BITS_PER_SAMPLE, + SPDIF_OFFLOAD_MIN_SAMPLE_RATE, + SPDIF_OFFLOAD_MAX_SAMPLE_RATE + }, + { // 2 - PCM loopback + { + sizeof(KSDATARANGE_AUDIO), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_PCM), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + SPDIF_LOOPBACK_MAX_CHANNELS, + SPDIF_LOOPBACK_MIN_BITS_PER_SAMPLE, + SPDIF_LOOPBACK_MAX_BITS_PER_SAMPLE, + SPDIF_LOOPBACK_MIN_SAMPLE_RATE, + SPDIF_LOOPBACK_MAX_SAMPLE_RATE + }, + { // 3 - DOLBY-DIGITAL host + { + sizeof(KSDATARANGE_AUDIO), + KSDATARANGE_ATTRIBUTES, // An attributes list follows this data range + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) + }, + SPDIF_DOLBY_DIGITAL_MAX_CHANNELS, + SPDIF_DOLBY_DIGITAL_MIN_BITS_PER_SAMPLE, + SPDIF_DOLBY_DIGITAL_MAX_BITS_PER_SAMPLE, + SPDIF_DOLBY_DIGITAL_MIN_SAMPLE_RATE, + SPDIF_DOLBY_DIGITAL_MAX_SAMPLE_RATE + } +}; + +static +PKSDATARANGE SpdifPinDataRangePointersStream[] = +{ + PKSDATARANGE(&SpdifPinDataRangesStream[0]), + PKSDATARANGE(&PinDataRangeAttributeList), + PKSDATARANGE(&SpdifPinDataRangesStream[3]), + PKSDATARANGE(&PinDataRangeAttributeList) +}; + +static +PKSDATARANGE SpdifPinDataRangePointersOffloadStream[] = +{ + PKSDATARANGE(&SpdifPinDataRangesStream[1]), + PKSDATARANGE(&PinDataRangeAttributeList), + +}; + +static +PKSDATARANGE SpdifPinDataRangePointersLoopbackStream[] = +{ + PKSDATARANGE(&SpdifPinDataRangesStream[2]) +}; + +//============================================================================= +static +KSDATARANGE SpdifPinDataRangesBridge[] = +{ + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_ANALOG), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + }, + { + sizeof(KSDATARANGE), + 0, + 0, + 0, + STATICGUIDOF(KSDATAFORMAT_TYPE_AUDIO), + STATICGUIDOF(KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL), + STATICGUIDOF(KSDATAFORMAT_SPECIFIER_NONE) + } +}; + +static +PKSDATARANGE SpdifPinDataRangePointersBridge[] = +{ + &SpdifPinDataRangesBridge[0], + &SpdifPinDataRangesBridge[1] +}; + +//============================================================================= + +static +PCPROPERTY_ITEM PropertiesSpdifOffloadPin[] = +{ + { + &KSPROPSETID_OffloadPin, // define new property set + KSPROPERTY_OFFLOAD_PIN_GET_STREAM_OBJECT_POINTER, // define properties + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_OffloadPin + }, + { + &KSPROPSETID_OffloadPin, // define new property set + KSPROPERTY_OFFLOAD_PIN_VERIFY_STREAM_OBJECT_POINTER, // define properties + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_OffloadPin + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationSpdifOffloadPin, PropertiesSpdifOffloadPin); + + +//============================================================================= +static +PCPIN_DESCRIPTOR SpdifWaveMiniportPins[] = +{ + // Wave Out Streaming Pin (Renderer) KSPIN_WAVE_RENDER2_SINK_SYSTEM + { + SPDIF_MAX_INPUT_SYSTEM_STREAMS, + SPDIF_MAX_INPUT_SYSTEM_STREAMS, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(SpdifPinDataRangePointersStream), + SpdifPinDataRangePointersStream, + KSPIN_DATAFLOW_IN, + KSPIN_COMMUNICATION_SINK, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, + // Wave Out Streaming Pin (Renderer) KSPIN_WAVE_RENDER_SINK_OFFLOAD + { + SPDIF_MAX_INPUT_OFFLOAD_STREAMS, + SPDIF_MAX_INPUT_OFFLOAD_STREAMS, + 0, + &AutomationSpdifOffloadPin, // AutomationTable + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(SpdifPinDataRangePointersOffloadStream), + SpdifPinDataRangePointersOffloadStream, + KSPIN_DATAFLOW_IN, + KSPIN_COMMUNICATION_SINK, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, + // Wave Out Streaming Pin (Renderer) KSPIN_WAVE_RENDER2_SINK_LOOPBACK + { + SPDIF_MAX_OUTPUT_LOOPBACK_STREAMS, + SPDIF_MAX_OUTPUT_LOOPBACK_STREAMS, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(SpdifPinDataRangePointersLoopbackStream), + SpdifPinDataRangePointersLoopbackStream, + KSPIN_DATAFLOW_OUT, + KSPIN_COMMUNICATION_SINK, + &KSNODETYPE_AUDIO_LOOPBACK, + NULL, + 0 + } + }, + // Wave Out Bridge Pin (Renderer) KSPIN_WAVE_RENDER2_SOURCE + { + 0, + 0, + 0, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(SpdifPinDataRangePointersBridge), + SpdifPinDataRangePointersBridge, + KSPIN_DATAFLOW_OUT, + KSPIN_COMMUNICATION_NONE, + &KSCATEGORY_AUDIO, + NULL, + 0 + } + }, +}; + +//============================================================================= +static +PCNODE_DESCRIPTOR SpdifWaveMiniportNodes[] = +{ + // KSNODE_WAVE_AUDIO_ENGINE + { + 0, // Flags + NULL, // AutomationTable + &KSNODETYPE_AUDIO_ENGINE, // Type KSNODETYPE_AUDIO_ENGINE + NULL // Name + } +}; + +//============================================================================= +// +// ---------------------------- +// | | +// System Pin 0-->| |--> 2 Loopback Pin +// | HW Audio Engine node | +// Offload Pin 1-->| |--> 3 KSPIN_WAVE_RENDER_SOURCE +// | | +// ---------------------------- +static +PCCONNECTION_DESCRIPTOR SpdifWaveMiniportConnections[] = +{ + { PCFILTER_NODE, KSPIN_WAVE_RENDER_SINK_SYSTEM, KSNODE_WAVE_AUDIO_ENGINE, 1 }, + { PCFILTER_NODE, KSPIN_WAVE_RENDER_SINK_OFFLOAD, KSNODE_WAVE_AUDIO_ENGINE, 2 }, + { KSNODE_WAVE_AUDIO_ENGINE, 3, PCFILTER_NODE, KSPIN_WAVE_RENDER_SINK_LOOPBACK }, + { KSNODE_WAVE_AUDIO_ENGINE, 0, PCFILTER_NODE, KSPIN_WAVE_RENDER_SOURCE }, +}; + +//============================================================================= +static +PCPROPERTY_ITEM PropertiesSpdifWaveFilter[] = +{ + { + &KSPROPSETID_Pin, + KSPROPERTY_PIN_PROPOSEDATAFORMAT, + KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_WaveFilter + }, + { + &KSPROPSETID_SysVAD, + KSPROPERTY_SYSVAD_DEFAULTSTREAMEFFECTS, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + PropertyHandler_WaveFilter + } +}; + +DEFINE_PCAUTOMATION_TABLE_PROP(AutomationSpdifWaveFilter, PropertiesSpdifWaveFilter); + +//============================================================================= +static +PCFILTER_DESCRIPTOR SpdifWaveMiniportFilterDescriptor = +{ + 0, // Version + &AutomationSpdifWaveFilter, // AutomationTable + sizeof(PCPIN_DESCRIPTOR), // PinSize + SIZEOF_ARRAY(SpdifWaveMiniportPins), // PinCount + SpdifWaveMiniportPins, // Pins + sizeof(PCNODE_DESCRIPTOR), // NodeSize + SIZEOF_ARRAY(SpdifWaveMiniportNodes), // NodeCount + SpdifWaveMiniportNodes, // Nodes + SIZEOF_ARRAY(SpdifWaveMiniportConnections), // ConnectionCount + SpdifWaveMiniportConnections, // Connections + 0, // CategoryCount + NULL // Categories - use defaults (audio, render, capture) +}; + +#endif // _SYSVAD_SPDIFWAVTABLE_H_ + diff --git a/audio/sysvad/ToneGenerator.cpp b/audio/sysvad/ToneGenerator.cpp new file mode 100644 index 000000000..24a85ebab --- /dev/null +++ b/audio/sysvad/ToneGenerator.cpp @@ -0,0 +1,261 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + ToneGenerator + +Abstract: + + Implementation of SYSVAD sine wave generator + + +--*/ +#include +#include "ToneGenerator.h" + +const double TONE_AMPLITUDE = 0.5; // Scalar value, should be between 0.0 - 1.0 +const double TWO_PI = M_PI * 2; + +// +// Double to short conversion. +// +short ConvertToShort(double Value) +{ + return (short)(Value * _I16_MAX); +}; + +// +// Double to char conversion. +// +unsigned char ConvertToUChar(double Value) +{ + const double F_127_5 = 127.5; + return (unsigned char)(Value * F_127_5 + F_127_5); +}; + +// +// Ctor: basic init. +// +ToneGenerator::ToneGenerator() +: m_Frequency(0), + m_ChannelCount(0), + m_BitsPerSample(0), + m_SamplesPerSecond(0), + m_Mute(false), + m_PartialFrame(NULL), + m_PartialFrameBytes(0), + m_FrameSize(0) +{ + // Theta (double) and SampleIncrement (double) are init in the Init() method + // after saving the floating point state. +} + +// +// Dtor: free resources. +// +ToneGenerator::~ToneGenerator() +{ + if (m_PartialFrame) + { + ExFreePoolWithTag(m_PartialFrame, SYSVAD_POOLTAG); + m_PartialFrame = NULL; + m_PartialFrameBytes = 0; + } +} + +// +// Init a new frame. +// Note: caller will save and restore the floatingpoint state. +// +#pragma warning(push) +// Caller wraps this routine between KeSaveFloatingPointState/KeRestoreFloatingPointState calls. +#pragma warning(disable: 28110) + +VOID ToneGenerator::InitNewFrame +( + _Out_writes_bytes_(FrameSize) BYTE* Frame, + _In_ DWORD FrameSize +) +{ + double sinValue = TONE_AMPLITUDE * sin( m_Theta ); + + if (FrameSize != (DWORD)m_ChannelCount * m_BitsPerSample/8) + { + ASSERT(FALSE); + RtlZeroMemory(Frame, FrameSize); + return; + } + + for(ULONG i = 0; i < m_ChannelCount; ++i) + { + if (m_BitsPerSample == 8) + { + unsigned char *dataBuffer = reinterpret_cast(Frame); + dataBuffer[i] = ConvertToUChar(sinValue); + } + else // 16 bits per sample + { + short *dataBuffer = reinterpret_cast(Frame); + dataBuffer[i] = ConvertToShort(sinValue); + } + } + + m_Theta += m_SampleIncrement; + if (m_Theta >= TWO_PI) + { + m_Theta -= TWO_PI; + } +} +#pragma warning(pop) + +// +// GenerateSamples() +// +// Generate a sine wave that fits into the specified buffer. +// +// Buffer - Buffer to hold the samples +// BufferLength - Length of the buffer. +// +// Note: this function supports 16bit and 8bit samples only. +// +void ToneGenerator::GenerateSine +( + _Out_writes_bytes_(BufferLength) BYTE *Buffer, + _In_ size_t BufferLength +) +{ + NTSTATUS status; + KFLOATING_SAVE saveData; + BYTE * buffer; + size_t length; + size_t copyBytes; + + if (m_Mute) + { + goto ZeroBuffer; + } + + status = KeSaveFloatingPointState(&saveData); + if (!NT_SUCCESS(status)) + { + goto ZeroBuffer; + } + + buffer = Buffer; + length = BufferLength; + + // + // Check if we have any residual frame bytes from the last time. + // + if (m_PartialFrameBytes) + { + ASSERT(m_FrameSize > m_PartialFrameBytes); + DWORD offset = m_FrameSize - m_PartialFrameBytes; + copyBytes = MIN(m_PartialFrameBytes, length); + RtlCopyMemory(buffer, m_PartialFrame + offset, copyBytes); + RtlZeroMemory(m_PartialFrame + offset, copyBytes); + length -= copyBytes; + buffer += copyBytes; + m_PartialFrameBytes = 0; + } + + IF_TRUE_JUMP(length == 0, Done); + + // + // Copy all the aligned frames. + // + + size_t frames = length/m_FrameSize; + + for (size_t i = 0; i < frames; ++i) + { + InitNewFrame(buffer, m_FrameSize); + buffer += m_FrameSize; + length -= m_FrameSize; + } + + IF_TRUE_JUMP(length == 0, Done); + + // + // Copy any partial frame at the end. + // + ASSERT(m_FrameSize > length); + InitNewFrame(m_PartialFrame, m_FrameSize); + RtlCopyMemory(buffer, m_PartialFrame, length); + RtlZeroMemory(m_PartialFrame, length); + m_PartialFrameBytes = m_FrameSize - (DWORD)length; + +Done: + KeRestoreFloatingPointState(&saveData); + return; + +ZeroBuffer: + RtlZeroMemory(Buffer, BufferLength); + return; +} + +NTSTATUS ToneGenerator::Init +( + _In_ DWORD ToneFrequency, + _In_ PWAVEFORMATEXTENSIBLE WfExt +) +{ + NTSTATUS status = STATUS_SUCCESS; + KFLOATING_SAVE saveData; + + // + // This sample supports PCM 16bit formats only. + // + if ((WfExt->Format.wFormatTag != WAVE_FORMAT_PCM && + !(WfExt->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE && + IsEqualGUIDAligned(WfExt->SubFormat, KSDATAFORMAT_SUBTYPE_PCM))) || + (WfExt->Format.wBitsPerSample != 16 && + WfExt->Format.wBitsPerSample != 8)) + { + status = STATUS_NOT_SUPPORTED; + } + IF_FAILED_JUMP(status, Done); + + // + // Save floating state (just in case). + // + status = KeSaveFloatingPointState(&saveData); + IF_FAILED_JUMP(status, Done); + + // + // Basic init. + // + RtlZeroMemory(&m_Theta, sizeof(m_Theta)); + m_Frequency = ToneFrequency; + m_ChannelCount = WfExt->Format.nChannels; // # channels. + m_BitsPerSample = WfExt->Format.wBitsPerSample; // bits per sample. + m_SamplesPerSecond = WfExt->Format.nSamplesPerSec; // samples per sec. + m_Mute = false; + m_SampleIncrement = (m_Frequency * TWO_PI) / (double)m_SamplesPerSecond; + m_FrameSize = (DWORD)m_ChannelCount * m_BitsPerSample/8; + ASSERT(m_FrameSize == WfExt->Format.nBlockAlign); + + // + // Restore floating state. + // + KeRestoreFloatingPointState(&saveData); + + // + // Allocate a buffer to hold a partial frame. + // + m_PartialFrame = (BYTE*)ExAllocatePoolWithTag( + NonPagedPoolNx, + m_FrameSize, + SYSVAD_POOLTAG); + + IF_TRUE_ACTION_JUMP(m_PartialFrame == NULL, status = STATUS_INSUFFICIENT_RESOURCES, Done); + + status = STATUS_SUCCESS; + +Done: + return status; +} + + diff --git a/audio/sysvad/ToneGenerator.h b/audio/sysvad/ToneGenerator.h new file mode 100644 index 000000000..af4cb310e --- /dev/null +++ b/audio/sysvad/ToneGenerator.h @@ -0,0 +1,72 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + ToneGenerator.h + +Abstract: + + Declaration of SYSVAD sine wave generator. + + +--*/ +#ifndef _SYSVAD_TONEGENERATOR_H +#define _SYSVAD_TONEGENERATOR_H + +#define _USE_MATH_DEFINES +#include +#include + +class ToneGenerator +{ +public: + DWORD m_Frequency; + WORD m_ChannelCount; + WORD m_BitsPerSample; + DWORD m_SamplesPerSecond; + double m_Theta; + double m_SampleIncrement; + bool m_Mute; + BYTE* m_PartialFrame; + DWORD m_PartialFrameBytes; + DWORD m_FrameSize; + +public: + ToneGenerator(); + ~ToneGenerator(); + + NTSTATUS + Init + ( + _In_ DWORD ToneFrequency, + _In_ PWAVEFORMATEXTENSIBLE WfExt + ); + + VOID + GenerateSine + ( + _Out_writes_bytes_(BufferLength) BYTE *Buffer, + _In_ size_t BufferLength + ); + + VOID + SetMute + ( + _In_ bool Value + ) + { + m_Mute = Value; + } + +private: + VOID InitNewFrame + ( + _Out_writes_bytes_(FrameSize) BYTE* Frame, + _In_ DWORD FrameSize + ); +}; + +#endif // _SYSVAD_TONEGENERATOR_H + diff --git a/audio/sysvad/UnittestData.h b/audio/sysvad/UnittestData.h new file mode 100644 index 000000000..e8a342372 --- /dev/null +++ b/audio/sysvad/UnittestData.h @@ -0,0 +1,17 @@ + +#ifndef _SYSVAD_UNITTESTDATA_H_ +#define _SYSVAD_UNITTESTDATA_H_ + +#define WAVERT_CURRENT_WRITE_POSITION_DATA 100 +#define STREAM_PRESENTATION_POSITION_DATA 1000 +#define LINEAR_BUFFER_POSITION_DATA 10000 + + +#define BUFFER_SIZE_RANGE_MIN 1200390 +#define BUFFER_SIZE_RANGE_MAX 1200393 + +#define MS_PER_SEC 1000 +#define MIN_BUFFER_DURATION_MS 10 +#define MAX_BUFFER_DURATION_MS 2000 + +#endif // _SYSVAD_UNITTESTDATA_H_ \ No newline at end of file diff --git a/audio/sysvad/adapter.cpp b/audio/sysvad/adapter.cpp new file mode 100644 index 000000000..08e83a45d --- /dev/null +++ b/audio/sysvad/adapter.cpp @@ -0,0 +1,1079 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + adapter.cpp + +Abstract: + + Setup and miniport installation. No resources are used by sysvad. + This sample is to demonstrate how to develop a full featured audio miniport driver. + +--*/ + +#pragma warning (disable : 4127) + +// +// All the GUIDS for all the miniports end up in this object. +// +#define PUT_GUIDS_HERE + +#include +#include +#include "IHVPrivatePropertySet.h" + +#include "simple.h" +#include "minipairs.h" +#ifdef SYSVAD_BTH_BYPASS +#include "bthhfpminipairs.h" +#endif // SYSVAD_BTH_BYPASS + + + +typedef void (*fnPcDriverUnload) (PDRIVER_OBJECT); +fnPcDriverUnload gPCDriverUnloadRoutine = NULL; +extern "C" DRIVER_UNLOAD DriverUnload; + +#ifdef _USE_SingleComponentMultiFxStates +C_ASSERT(sizeof(POHANDLE) == sizeof(PVOID)); + +//============================================================================= +// +// The number of F-states, 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. +// +#define SYSVAD_FSTATE_COUNT 4 + +#define SYSVAD_F0_LATENCY_IN_MS 0 +#define SYSVAD_F0_RESIDENCY_IN_SEC 0 + +#define SYSVAD_F1_LATENCY_IN_MS 200 +#define SYSVAD_F1_RESIDENCY_IN_SEC 3 + +#define SYSVAD_F2_LATENCY_IN_MS 400 +#define SYSVAD_F2_RESIDENCY_IN_SEC 6 + +#define SYSVAD_F3_LATENCY_IN_MS 800 +#define SYSVAD_F3_RESIDENCY_IN_SEC 12 + +#define SYSVAD_DEEPEST_FSTATE_LATENCY_IN_MS SYSVAD_F3_LATENCY_IN_MS +#define SYSVAD_DEEPEST_FSTATE_RESIDENCY_IN_SEC SYSVAD_F3_RESIDENCY_IN_SEC + + +//----------------------------------------------------------------------------- +// PoFx - Single Component - Multi Fx States support. +//----------------------------------------------------------------------------- + +PO_FX_COMPONENT_IDLE_STATE_CALLBACK PcPowerFxComponentIdleStateCallback; +PO_FX_COMPONENT_ACTIVE_CONDITION_CALLBACK PcPowerFxComponentActiveConditionCallback; +PO_FX_COMPONENT_IDLE_CONDITION_CALLBACK PcPowerFxComponentIdleConditionCallback; +PO_FX_POWER_CONTROL_CALLBACK PcPowerFxPowerControlCallback; +EVT_PC_POST_PO_FX_REGISTER_DEVICE PcPowerFxRegisterDevice; +EVT_PC_PRE_PO_FX_UNREGISTER_DEVICE PcPowerFxUnregisterDevice; + +#pragma code_seg("PAGE") +_Function_class_(EVT_PC_POST_PO_FX_REGISTER_DEVICE) +_IRQL_requires_same_ +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS +PcPowerFxRegisterDevice( + _In_ PVOID PoFxDeviceContext, + _In_ POHANDLE PoHandle + ) +{ + PDEVICE_OBJECT DeviceObject = (PDEVICE_OBJECT)PoFxDeviceContext; + PortClassDeviceContext* pExtension = static_cast(DeviceObject->DeviceExtension); + + PAGED_CODE(); + + DPF(D_VERBOSE, ("PcPowerFxRegisterDevice Context %p, PoHandle %p", PoFxDeviceContext, PoHandle)); + + pExtension->m_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(SYSVAD_DEEPEST_FSTATE_LATENCY_IN_MS) + 1) + ); + PoFxSetComponentResidency( + PoHandle, + 0, // Component + (WDF_ABS_TIMEOUT_IN_SEC(SYSVAD_DEEPEST_FSTATE_RESIDENCY_IN_SEC) + 1) + ); + + return STATUS_SUCCESS; +} + +#pragma code_seg("PAGE") +_Function_class_(EVT_PC_PRE_PO_FX_UNREGISTER_DEVICE) +_IRQL_requires_same_ +_IRQL_requires_max_(PASSIVE_LEVEL) +VOID +PcPowerFxUnregisterDevice( + _In_ PVOID PoFxDeviceContext, + _In_ POHANDLE PoHandle + ) +{ + PDEVICE_OBJECT DeviceObject = (PDEVICE_OBJECT)PoFxDeviceContext; + PortClassDeviceContext* pExtension = static_cast(DeviceObject->DeviceExtension); + + UNREFERENCED_PARAMETER(PoHandle); + + PAGED_CODE(); + + DPF(D_VERBOSE, ("PcPowerFxUnregisterDevice Context %p, PoHandle %p", PoFxDeviceContext, PoHandle)); + + // + // Driver must not use the PoHandle after this call. + // + ASSERT(pExtension->m_poHandle == PoHandle); + pExtension->m_poHandle = NULL; +} + +#pragma code_seg() +__drv_functionClass(PO_FX_COMPONENT_IDLE_STATE_CALLBACK) +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +PcPowerFxComponentIdleStateCallback( + _In_ PVOID Context, + _In_ ULONG Component, + _In_ ULONG State + ) +{ + PDEVICE_OBJECT DeviceObject = (PDEVICE_OBJECT)Context; + PortClassDeviceContext* pExtension = static_cast(DeviceObject->DeviceExtension); + + UNREFERENCED_PARAMETER(State); + + DPF(D_VERBOSE, ("PcPowerFxComponentIdleStateCallback Context %p, Component %d, State %d", + Context, Component, State)); + + PoFxCompleteIdleState(pExtension->m_poHandle, Component); +} + +#pragma code_seg() +__drv_functionClass(PO_FX_COMPONENT_ACTIVE_CONDITION_CALLBACK) +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +PcPowerFxComponentActiveConditionCallback( + _In_ PVOID Context, + _In_ ULONG Component + ) +{ + UNREFERENCED_PARAMETER(Context); + UNREFERENCED_PARAMETER(Component); + + DPF(D_VERBOSE, ("PcPowerFxComponentActiveConditionCallback Context %p, Component %d", + Context, Component)); +} + +#pragma code_seg() +__drv_functionClass(PO_FX_COMPONENT_IDLE_CONDITION_CALLBACK) +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +PcPowerFxComponentIdleConditionCallback( + _In_ PVOID Context, + _In_ ULONG Component + ) +{ + PDEVICE_OBJECT DeviceObject = (PDEVICE_OBJECT)Context; + PortClassDeviceContext* pExtension = static_cast(DeviceObject->DeviceExtension); + + DPF(D_VERBOSE, ("PcPowerFxComponentIdleConditionCallback Context %p, Component %d", + Context, Component)); + + PoFxCompleteIdleCondition(pExtension->m_poHandle, Component); +} + +#pragma code_seg() +__drv_functionClass(PO_FX_POWER_CONTROL_CALLBACK) +_IRQL_requires_max_(DISPATCH_LEVEL) +NTSTATUS +PcPowerFxPowerControlCallback( + _In_ PVOID DeviceContext, + _In_ LPCGUID PowerControlCode, + _In_reads_bytes_opt_(InBufferSize) PVOID InBuffer, + _In_ SIZE_T InBufferSize, + _Out_writes_bytes_opt_(OutBufferSize) PVOID OutBuffer, + _In_ SIZE_T OutBufferSize, + _Out_opt_ PSIZE_T BytesReturned +) +{ + UNREFERENCED_PARAMETER(DeviceContext); + UNREFERENCED_PARAMETER(PowerControlCode); + UNREFERENCED_PARAMETER(InBuffer); + UNREFERENCED_PARAMETER(InBufferSize); + UNREFERENCED_PARAMETER(OutBuffer); + UNREFERENCED_PARAMETER(OutBufferSize); + UNREFERENCED_PARAMETER(BytesReturned); + + DPF(D_VERBOSE, ("PcPowerFxPowerControlCallback Context %p", DeviceContext)); + + return STATUS_SUCCESS; +} +#endif // _USE_SingleComponentMultiFxStates + +//----------------------------------------------------------------------------- +// Referenced forward. +//----------------------------------------------------------------------------- + +DRIVER_ADD_DEVICE AddDevice; + +NTSTATUS +StartDevice +( + _In_ PDEVICE_OBJECT, + _In_ PIRP, + _In_ PRESOURCELIST +); + +_Dispatch_type_(IRP_MJ_PNP) +DRIVER_DISPATCH PnpHandler; + +// +// Rendering streams are saved to a file by default. Use the registry value +// DoNotCreateDataFiles (DWORD) > 0 to override this default. +// +DWORD g_DoNotCreateDataFiles = 0; + + +#ifdef SYSVAD_BTH_BYPASS +// +// This driver listens for arrival/removal of the bth sco bypass interfaces by +// default. Use the registry value DisableBthScoBypass (DWORD) > 0 to override +// this default. +// +DWORD g_DisableBthScoBypass = 0; +#endif // SYSVAD_BTH_BYPASS + +//----------------------------------------------------------------------------- +// Functions +//----------------------------------------------------------------------------- + +//============================================================================= +#pragma code_seg("PAGE") +extern "C" +void DriverUnload +( + _In_ PDRIVER_OBJECT DriverObject +) +/*++ + +Routine Description: + + Our driver unload routine. This just frees the WDF driver object. + +Arguments: + + DriverObject - pointer to the driver object + +Environment: + + PASSIVE_LEVEL + +--*/ +{ + PAGED_CODE(); + + DPF(D_TERSE, ("[DriverUnload]")); + + if (DriverObject == NULL) + { + goto Done; + } + + // + // Invoke first the port unload. + // + if (gPCDriverUnloadRoutine != NULL) + { + gPCDriverUnloadRoutine(DriverObject); + } + + // + // Unload WDF driver object. + // + if (WdfGetDriver() != NULL) + { + WdfDriverMiniportUnload(WdfGetDriver()); + } + +Done: + return; +} + +//============================================================================= +#pragma code_seg("INIT") +__drv_requiresIRQL(PASSIVE_LEVEL) +NTSTATUS +GetRegistrySettings( + _In_ PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Description: + + Initialize Driver Framework settings from the driver + specific registry settings under + + \REGISTRY\MACHINE\SYSTEM\ControlSetxxx\Services\\Parameters + +Arguments: + + RegistryPath - Registry path passed to DriverEntry + +Returns: + + NTSTATUS - SUCCESS if able to configure the framework + +--*/ + +{ + NTSTATUS ntStatus; + UNICODE_STRING parametersPath; + RTL_QUERY_REGISTRY_TABLE paramTable[4]; + + DPF(D_TERSE, ("[GetRegistrySettings]")); + + PAGED_CODE(); + + RtlInitUnicodeString(¶metersPath, NULL); + + parametersPath.MaximumLength = + RegistryPath->Length + sizeof(L"\\Parameters") + sizeof(WCHAR); + + parametersPath.Buffer = (PWCH) ExAllocatePoolWithTag(PagedPool, parametersPath.MaximumLength, MINADAPTER_POOLTAG); + if (parametersPath.Buffer == NULL) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlZeroMemory(parametersPath.Buffer, parametersPath.MaximumLength); + + RtlAppendUnicodeToString(¶metersPath, RegistryPath->Buffer); + RtlAppendUnicodeToString(¶metersPath, L"\\Parameters"); + + RtlZeroMemory(¶mTable[0], sizeof(paramTable)); + + g_DoNotCreateDataFiles = 0; // default is off. + + paramTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT; + paramTable[0].Name = L"DoNotCreateDataFiles"; + paramTable[0].EntryContext = &g_DoNotCreateDataFiles; + paramTable[0].DefaultType = REG_DWORD; + paramTable[0].DefaultData = &g_DoNotCreateDataFiles; + paramTable[0].DefaultLength = sizeof(ULONG); + +#ifdef SYSVAD_BTH_BYPASS + g_DisableBthScoBypass = 0; // default is off. + + paramTable[1].Flags = RTL_QUERY_REGISTRY_DIRECT; + paramTable[1].Name = L"DisableBthScoBypass"; + paramTable[1].EntryContext = &g_DisableBthScoBypass; + paramTable[1].DefaultType = REG_DWORD; + paramTable[1].DefaultData = &g_DisableBthScoBypass; + paramTable[1].DefaultLength = sizeof(ULONG); +#endif // SYSVAD_BTH_BYPASS + + + ntStatus = RtlQueryRegistryValues( + RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL, + parametersPath.Buffer, + ¶mTable[0], + NULL, + NULL + ); + + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_VERBOSE, ("RtlQueryRegistryValues failed, using default values, 0x%x", ntStatus)); + // + // Don't return error because we will operate with default values. + // + } + + // + // Dump settings. + // + DPF(D_VERBOSE, ("DoNotCreateDataFiles: %u", g_DoNotCreateDataFiles)); +#ifdef SYSVAD_BTH_BYPASS + DPF(D_VERBOSE, ("DisableBthScoBypass: %u", g_DisableBthScoBypass)); +#endif // SYSVAD_BTH_BYPASS + + // + // Cleanup. + // + ExFreePool(parametersPath.Buffer); + + return STATUS_SUCCESS; +} + +#pragma code_seg("INIT") +extern "C" DRIVER_INITIALIZE DriverEntry; +extern "C" NTSTATUS +DriverEntry +( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPathName +) +{ +/*++ + +Routine Description: + + Installable driver initialization entry point. + This entry point is called directly by the I/O system. + + All audio adapter drivers can use this code without change. + +Arguments: + + DriverObject - pointer to the driver object + + RegistryPath - pointer to a unicode string representing the path, + to driver-specific key in the registry. + +Return Value: + + STATUS_SUCCESS if successful, + STATUS_UNSUCCESSFUL otherwise. + +--*/ + NTSTATUS ntStatus; + WDF_DRIVER_CONFIG config; + + DPF(D_TERSE, ("[DriverEntry]")); + + // + // Get registry configuration. + // + ntStatus = GetRegistrySettings(RegistryPathName); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Registry Configuration error 0x%x", ntStatus)), + Done); + + WDF_DRIVER_CONFIG_INIT(&config, WDF_NO_EVENT_CALLBACK); + // + // Set WdfDriverInitNoDispatchOverride flag to tell the framework + // not to provide dispatch routines for the driver. In other words, + // the framework must not intercept IRPs that the I/O manager has + // directed to the driver. In this case, they will be handled by Audio + // port driver. + // + config.DriverInitFlags |= WdfDriverInitNoDispatchOverride; + config.DriverPoolTag = MINADAPTER_POOLTAG; + + ntStatus = WdfDriverCreate(DriverObject, + RegistryPathName, + WDF_NO_OBJECT_ATTRIBUTES, + &config, + WDF_NO_HANDLE); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("WdfDriverCreate failed, 0x%x", ntStatus)), + Done); + + // + // Tell the class driver to initialize the driver. + // + ntStatus = PcInitializeAdapterDriver(DriverObject, + RegistryPathName, + (PDRIVER_ADD_DEVICE)AddDevice); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("PcInitializeAdapterDriver failed, 0x%x", ntStatus)), + Done); + + // + // To intercept stop/remove/surprise-remove. + // + DriverObject->MajorFunction[IRP_MJ_PNP] = PnpHandler; + + // + // Hook the port class unload function + // + gPCDriverUnloadRoutine = DriverObject->DriverUnload; + DriverObject->DriverUnload = DriverUnload; + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + if (!NT_SUCCESS(ntStatus)) + { + if (WdfGetDriver() != NULL) + { + WdfDriverMiniportUnload(WdfGetDriver()); + } + } + + return ntStatus; +} // DriverEntry + +#pragma code_seg() +// disable prefast warning 28152 because +// DO_DEVICE_INITIALIZING is cleared in PcAddAdapterDevice +#pragma warning(disable:28152) +#pragma code_seg("PAGE") +//============================================================================= +NTSTATUS AddDevice +( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PDEVICE_OBJECT PhysicalDeviceObject +) +/*++ + +Routine Description: + + The Plug & Play subsystem is handing us a brand new PDO, for which we + (by means of INF registration) have been asked to provide a driver. + + We need to determine if we need to be in the driver stack for the device. + Create a function device object to attach to the stack + Initialize that device object + Return status success. + + All audio adapter drivers can use this code without change. + +Arguments: + + DriverObject - pointer to a driver object + + PhysicalDeviceObject - pointer to a device object created by the + underlying bus driver. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + NTSTATUS ntStatus; + ULONG maxObjects; + + DPF(D_TERSE, ("[AddDevice]")); + + maxObjects = g_MaxMiniports; + +#ifdef SYSVAD_BTH_BYPASS + // + // Allow three (3) Bluetooth hands-free profile devices. + // + maxObjects += g_MaxBthHfpMiniports * 3; +#endif // SYSVAD_BTH_BYPASS + + // Tell the class driver to add the device. + // + ntStatus = + PcAddAdapterDevice + ( + DriverObject, + PhysicalDeviceObject, + PCPFNSTARTDEVICE(StartDevice), + maxObjects, + 0 + ); + + + return ntStatus; +} // AddDevice + +#pragma code_seg() +NTSTATUS +_IRQL_requires_max_(DISPATCH_LEVEL) +PowerControlCallback +( + _In_ LPCGUID PowerControlCode, + _In_opt_ PVOID InBuffer, + _In_ SIZE_T InBufferSize, + _Out_writes_bytes_to_(OutBufferSize, *BytesReturned) PVOID OutBuffer, + _In_ SIZE_T OutBufferSize, + _Out_opt_ PSIZE_T BytesReturned, + _In_opt_ PVOID Context +) +{ + UNREFERENCED_PARAMETER(PowerControlCode); + UNREFERENCED_PARAMETER(BytesReturned); + UNREFERENCED_PARAMETER(InBuffer); + UNREFERENCED_PARAMETER(OutBuffer); + UNREFERENCED_PARAMETER(OutBufferSize); + UNREFERENCED_PARAMETER(InBufferSize); + UNREFERENCED_PARAMETER(Context); + + return STATUS_NOT_IMPLEMENTED; +} + +#pragma code_seg("PAGE") +NTSTATUS +InstallEndpointRenderFilters( + _In_ PDEVICE_OBJECT _pDeviceObject, + _In_ PIRP _pIrp, + _In_ PADAPTERCOMMON _pAdapterCommon, + _In_ PENDPOINT_MINIPAIR _pAeMiniports + ) +{ + NTSTATUS ntStatus = STATUS_SUCCESS; + PUNKNOWN unknownTopology = NULL; + PUNKNOWN unknownWave = NULL; + PPORTCLSETWHELPER pPortClsEtwHelper = NULL; +#ifdef _USE_IPortClsRuntimePower + PPORTCLSRUNTIMEPOWER pPortClsRuntimePower = NULL; +#endif // _USE_IPortClsRuntimePower + PPORTCLSStreamResourceManager pPortClsResMgr = NULL; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER(_pDeviceObject); + + ntStatus = _pAdapterCommon->InstallEndpointFilters( + _pIrp, + _pAeMiniports, + NULL, + &unknownTopology, + &unknownWave); + + if (unknownWave) // IID_IPortClsEtwHelper and IID_IPortClsRuntimePower interfaces are only exposed on the WaveRT port. + { + ntStatus = unknownWave->QueryInterface (IID_IPortClsEtwHelper, (PVOID *)&pPortClsEtwHelper); + if (NT_SUCCESS(ntStatus)) + { + _pAdapterCommon->SetEtwHelper(pPortClsEtwHelper); + ASSERT(pPortClsEtwHelper != NULL); + pPortClsEtwHelper->Release(); + } + +#ifdef _USE_IPortClsRuntimePower + // Let's get the runtime power interface on PortCls. + ntStatus = unknownWave->QueryInterface(IID_IPortClsRuntimePower, (PVOID *)&pPortClsRuntimePower); + if (NT_SUCCESS(ntStatus)) + { + // This interface would typically be stashed away for later use. Instead, + // let's just send an empty control with GUID_NULL. + NTSTATUS ntStatusTest = + pPortClsRuntimePower->SendPowerControl + ( + _pDeviceObject, + &GUID_NULL, + NULL, + 0, + NULL, + 0, + NULL + ); + + if (NT_SUCCESS(ntStatusTest) || STATUS_NOT_IMPLEMENTED == ntStatusTest || STATUS_NOT_SUPPORTED == ntStatusTest) + { + ntStatus = pPortClsRuntimePower->RegisterPowerControlCallback(_pDeviceObject, &PowerControlCallback, NULL); + if (NT_SUCCESS(ntStatus)) + { + ntStatus = pPortClsRuntimePower->UnregisterPowerControlCallback(_pDeviceObject); + } + } + else + { + ntStatus = ntStatusTest; + } + + pPortClsRuntimePower->Release(); + } +#endif // _USE_IPortClsRuntimePower + + // + // Test: add and remove current thread as streaming audio resource. + // In a real driver you should only add interrupts and driver-owned threads + // (i.e., do not add the current thread as streaming resource). + // + ntStatus = unknownWave->QueryInterface(IID_IPortClsStreamResourceManager, (PVOID *)&pPortClsResMgr); + if (NT_SUCCESS(ntStatus)) + { + PCSTREAMRESOURCE_DESCRIPTOR res; + PCSTREAMRESOURCE hRes = NULL; + PDEVICE_OBJECT pdo = NULL; + + PcGetPhysicalDeviceObject(_pDeviceObject, &pdo); + PCSTREAMRESOURCE_DESCRIPTOR_INIT(&res); + res.Pdo = pdo; + res.Type = ePcStreamResourceThread; + res.Resource.Thread = PsGetCurrentThread(); + + NTSTATUS ntStatusTest = pPortClsResMgr->AddStreamResource(NULL, &res, &hRes); + if (NT_SUCCESS(ntStatusTest)) + { + pPortClsResMgr->RemoveStreamResource(hRes); + hRes = NULL; + } + + pPortClsResMgr->Release(); + pPortClsResMgr = NULL; + } + } + + SAFE_RELEASE(unknownTopology); + SAFE_RELEASE(unknownWave); + + return ntStatus; +} + +#pragma code_seg("PAGE") +NTSTATUS +InstallAllRenderFilters( + _In_ PDEVICE_OBJECT _pDeviceObject, + _In_ PIRP _pIrp, + _In_ PADAPTERCOMMON _pAdapterCommon + ) +{ + NTSTATUS ntStatus; + PENDPOINT_MINIPAIR* ppAeMiniports = g_RenderEndpoints; + + PAGED_CODE(); + + for(ULONG i = 0; i < g_cRenderEndpoints; ++i, ++ppAeMiniports) + { + ntStatus = InstallEndpointRenderFilters(_pDeviceObject, _pIrp, _pAdapterCommon, *ppAeMiniports); + IF_FAILED_JUMP(ntStatus, Exit); + } + + ntStatus = STATUS_SUCCESS; + +Exit: + return ntStatus; +} + +#pragma code_seg("PAGE") +NTSTATUS +InstallEndpointCaptureFilters( + _In_ PDEVICE_OBJECT _pDeviceObject, + _In_ PIRP _pIrp, + _In_ PADAPTERCOMMON _pAdapterCommon, + _In_ PENDPOINT_MINIPAIR _pAeMiniports + ) +{ + NTSTATUS ntStatus = STATUS_SUCCESS; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER(_pDeviceObject); + + ntStatus = _pAdapterCommon->InstallEndpointFilters( + _pIrp, + _pAeMiniports, + NULL, + NULL, + NULL); + + return ntStatus; +} + +#pragma code_seg("PAGE") +NTSTATUS +InstallAllCaptureFilters( + _In_ PDEVICE_OBJECT _pDeviceObject, + _In_ PIRP _pIrp, + _In_ PADAPTERCOMMON _pAdapterCommon + ) +{ + NTSTATUS ntStatus; + PENDPOINT_MINIPAIR* ppAeMiniports = g_CaptureEndpoints; + + PAGED_CODE(); + + for(ULONG i = 0; i < g_cCaptureEndpoints; ++i, ++ppAeMiniports) + { + ntStatus = InstallEndpointCaptureFilters(_pDeviceObject, _pIrp, _pAdapterCommon, *ppAeMiniports); + IF_FAILED_JUMP(ntStatus, Exit); + } + + ntStatus = STATUS_SUCCESS; + +Exit: + return ntStatus; +} + +#ifdef _USE_SingleComponentMultiFxStates +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +UseSingleComponentMultiFxStates( + _In_ PDEVICE_OBJECT DeviceObject + +) +{ + NTSTATUS status; + PC_POWER_FRAMEWORK_SETTINGS poFxSettings; + PO_FX_COMPONENT component; + PO_FX_COMPONENT_IDLE_STATE idleStates[SYSVAD_FSTATE_COUNT]; + + // + // Note that we initialize the 'idleStates' array below based on the + // assumption that SYSVAD_FSTATE_COUNT is 4. + // If we increase the value of SYSVAD_FSTATE_COUNT, we need to initialize those + // additional F-states below. If we decrease the value of SYSVAD_FSTATE_COUNT, + // we need to remove the corresponding initializations below. + // + C_ASSERT(SYSVAD_FSTATE_COUNT == 4); + + PAGED_CODE(); + + // + // Initialization + // + RtlZeroMemory(&component, sizeof(component)); + RtlZeroMemory(idleStates, sizeof(idleStates)); + + // + // F0 + // + idleStates[0].TransitionLatency = WDF_ABS_TIMEOUT_IN_MS(SYSVAD_F0_LATENCY_IN_MS); + idleStates[0].ResidencyRequirement = WDF_ABS_TIMEOUT_IN_SEC(SYSVAD_F0_RESIDENCY_IN_SEC); + idleStates[0].NominalPower = 0; + + // + // F1 + // + idleStates[1].TransitionLatency = WDF_ABS_TIMEOUT_IN_MS(SYSVAD_F1_LATENCY_IN_MS); + idleStates[1].ResidencyRequirement = WDF_ABS_TIMEOUT_IN_SEC(SYSVAD_F1_RESIDENCY_IN_SEC); + idleStates[1].NominalPower = 0; + + // + // F2 + // + idleStates[2].TransitionLatency = WDF_ABS_TIMEOUT_IN_MS(SYSVAD_F2_LATENCY_IN_MS); + idleStates[2].ResidencyRequirement = WDF_ABS_TIMEOUT_IN_SEC(SYSVAD_F2_RESIDENCY_IN_SEC); + idleStates[2].NominalPower = 0; + + // + // F3 + // + idleStates[3].TransitionLatency = WDF_ABS_TIMEOUT_IN_MS(SYSVAD_F3_LATENCY_IN_MS); + idleStates[3].ResidencyRequirement = WDF_ABS_TIMEOUT_IN_SEC(SYSVAD_F3_RESIDENCY_IN_SEC); + idleStates[3].NominalPower = 0; + + // + // Component 0 (the only component) + // + component.IdleStateCount = SYSVAD_FSTATE_COUNT; + component.IdleStates = idleStates; + + PC_POWER_FRAMEWORK_SETTINGS_INIT(&poFxSettings); + + poFxSettings.EvtPcPostPoFxRegisterDevice = PcPowerFxRegisterDevice; + poFxSettings.EvtPcPrePoFxUnregisterDevice = PcPowerFxUnregisterDevice; + poFxSettings.ComponentIdleStateCallback = PcPowerFxComponentIdleStateCallback; + poFxSettings.ComponentActiveConditionCallback = PcPowerFxComponentActiveConditionCallback; + poFxSettings.ComponentIdleConditionCallback = PcPowerFxComponentIdleConditionCallback; + poFxSettings.PowerControlCallback = PcPowerFxPowerControlCallback; + + poFxSettings.Component = &component; + poFxSettings.PoFxDeviceContext = (PVOID) DeviceObject; + + status = PcAssignPowerFrameworkSettings(DeviceObject, &poFxSettings); + if (!NT_SUCCESS(status)) { + DPF(D_ERROR, ("PcAssignPowerFrameworkSettings failed with status 0x%x", status)); + } + + return status; +} +#endif // _USE_SingleComponentMultiFxStates + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +StartDevice +( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PIRP Irp, + _In_ PRESOURCELIST ResourceList +) +{ +/*++ + +Routine Description: + + This function is called by the operating system when the device is + started. + It is responsible for starting the miniports. This code is specific to + the adapter because it calls out miniports for functions that are specific + to the adapter. + +Arguments: + + DeviceObject - pointer to the driver object + + Irp - pointer to the irp + + ResourceList - pointer to the resource list assigned by PnP manager + +Return Value: + + NT status code. + +--*/ + UNREFERENCED_PARAMETER(ResourceList); + + PAGED_CODE(); + + ASSERT(DeviceObject); + ASSERT(Irp); + ASSERT(ResourceList); + + NTSTATUS ntStatus = STATUS_SUCCESS; + + PADAPTERCOMMON pAdapterCommon = NULL; + PUNKNOWN pUnknownCommon = NULL; + PortClassDeviceContext* pExtension = static_cast(DeviceObject->DeviceExtension); + + DPF_ENTER(("[StartDevice]")); + + // + // create a new adapter common object + // + ntStatus = NewAdapterCommon( + &pUnknownCommon, + IID_IAdapterCommon, + NULL, + NonPagedPoolNx + ); + IF_FAILED_JUMP(ntStatus, Exit); + + ntStatus = pUnknownCommon->QueryInterface( IID_IAdapterCommon,(PVOID *) &pAdapterCommon); + IF_FAILED_JUMP(ntStatus, Exit); + + ntStatus = pAdapterCommon->Init(DeviceObject); + IF_FAILED_JUMP(ntStatus, Exit); + + // + // register with PortCls for power-management services + ntStatus = PcRegisterAdapterPowerManagement( PUNKNOWN(pAdapterCommon), DeviceObject); + IF_FAILED_JUMP(ntStatus, Exit); + + // + // Install wave+topology filters for render devices + // + ntStatus = InstallAllRenderFilters(DeviceObject, Irp, pAdapterCommon); + IF_FAILED_JUMP(ntStatus, Exit); + + // + // Install wave+topology filters for capture devices + // + ntStatus = InstallAllCaptureFilters(DeviceObject, Irp, pAdapterCommon); + IF_FAILED_JUMP(ntStatus, Exit); + +#ifdef SYSVAD_BTH_BYPASS + if (!g_DisableBthScoBypass) + { + // + // Init infrastructure for Bluetooth HFP - SCO Bypass devices. + // + ntStatus = pAdapterCommon->InitBthScoBypass(); + IF_FAILED_JUMP(ntStatus, Exit); + } +#endif // SYSVAD_BTH_BYPASS + +#ifdef _USE_SingleComponentMultiFxStates + // + // Init single component - multi Fx states + // + ntStatus = UseSingleComponentMultiFxStates(DeviceObject); + IF_FAILED_JUMP(ntStatus, Exit); +#endif // _USE_SingleComponentMultiFxStates + +Exit: + + // + // Stash the adapter common object in the device extension so + // we can access it for cleanup on stop/removal. + // + if (pAdapterCommon) + { + ASSERT(pExtension != NULL); + pExtension->m_pCommon = pAdapterCommon; + } + + // + // Release the adapter IUnknown interface. + // + SAFE_RELEASE(pUnknownCommon); + + return ntStatus; +} // StartDevice + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +PnpHandler +( + _In_ DEVICE_OBJECT *_DeviceObject, + _Inout_ IRP *_Irp +) +/*++ + +Routine Description: + + Handles PnP IRPs + +Arguments: + + _DeviceObject - Functional Device object pointer. + + _Irp - The Irp being passed + +Return Value: + + NT status code. + +--*/ +{ + NTSTATUS ntStatus = STATUS_UNSUCCESSFUL; + IO_STACK_LOCATION *stack; + PortClassDeviceContext *ext; + + PAGED_CODE(); + + ASSERT(_DeviceObject); + ASSERT(_Irp); + + // + // Check for the REMOVE_DEVICE irp. If we're being unloaded, + // uninstantiate our devices and release the adapter common + // object. + // + stack = IoGetCurrentIrpStackLocation(_Irp); + + + if ((IRP_MN_REMOVE_DEVICE == stack->MinorFunction) || + (IRP_MN_SURPRISE_REMOVAL == stack->MinorFunction) || + (IRP_MN_STOP_DEVICE == stack->MinorFunction)) + { + ext = static_cast(_DeviceObject->DeviceExtension); + + if (ext->m_pCommon != NULL) + { + ext->m_pCommon->Cleanup(); + + ext->m_pCommon->Release(); + ext->m_pCommon = NULL; + } + } + + ntStatus = PcDispatchIrp(_DeviceObject, _Irp); + + return ntStatus; +} + +#pragma code_seg() + diff --git a/audio/sysvad/basetopo.cpp b/audio/sysvad/basetopo.cpp new file mode 100644 index 000000000..1ceb414ac --- /dev/null +++ b/audio/sysvad/basetopo.cpp @@ -0,0 +1,691 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + basetopo.cpp + +Abstract: + + Implementation of topology miniport. This the base class for + all SYSVAD samples + +--*/ + +//4127: conditional expression is constant +#pragma warning (disable : 4127) + +#include +#include "basetopo.h" + +//============================================================================= +#pragma code_seg("PAGE") +CMiniportTopologySYSVAD::CMiniportTopologySYSVAD +( + _In_ PCFILTER_DESCRIPTOR *FilterDesc, + _In_ USHORT DeviceMaxChannels +) +/*++ + +Routine Description: + + Topology miniport constructor + +Arguments: + + FilterDesc - + + DeviceMaxChannels - + +Return Value: + + void + +--*/ +{ + PAGED_CODE(); + + DPF_ENTER(("[%s]",__FUNCTION__)); + + m_AdapterCommon = NULL; + + ASSERT(FilterDesc != NULL); + m_FilterDescriptor = FilterDesc; + m_PortEvents = NULL; + + ASSERT(DeviceMaxChannels > 0); + m_DeviceMaxChannels = DeviceMaxChannels; +} // CMiniportTopologySYSVAD + +CMiniportTopologySYSVAD::~CMiniportTopologySYSVAD +( + void +) +/*++ + +Routine Description: + + Topology miniport destructor + +Arguments: + +Return Value: + + void + +--*/ +{ + PAGED_CODE(); + + DPF_ENTER(("[%s]",__FUNCTION__)); + + SAFE_RELEASE(m_AdapterCommon); + SAFE_RELEASE(m_PortEvents); +} // ~CMiniportTopologySYSVAD + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CMiniportTopologySYSVAD::DataRangeIntersection +( + _In_ ULONG PinId, + _In_ PKSDATARANGE ClientDataRange, + _In_ PKSDATARANGE MyDataRange, + _In_ ULONG OutputBufferLength, + _Out_writes_bytes_to_opt_(OutputBufferLength, *ResultantFormatLength) + PVOID ResultantFormat OPTIONAL, + _Out_ PULONG ResultantFormatLength +) +/*++ + +Routine Description: + + The DataRangeIntersection function determines the highest + quality intersection of two data ranges. Topology miniport does nothing. + +Arguments: + + PinId - Pin for which data intersection is being determined. + + ClientDataRange - Pointer to KSDATARANGE structure which contains the data range + submitted by client in the data range intersection property + request + + MyDataRange - Pin's data range to be compared with client's data range + + OutputBufferLength - Size of the buffer pointed to by the resultant format + parameter + + ResultantFormat - Pointer to value where the resultant format should be + returned + + ResultantFormatLength - Actual length of the resultant format that is placed + at ResultantFormat. This should be less than or equal + to OutputBufferLength + +Return Value: + + NT status code. + +--*/ +{ + UNREFERENCED_PARAMETER(PinId); + UNREFERENCED_PARAMETER(ClientDataRange); + UNREFERENCED_PARAMETER(MyDataRange); + UNREFERENCED_PARAMETER(OutputBufferLength); + UNREFERENCED_PARAMETER(ResultantFormat); + UNREFERENCED_PARAMETER(ResultantFormatLength); + + PAGED_CODE(); + + DPF_ENTER(("[%s]",__FUNCTION__)); + + return (STATUS_NOT_IMPLEMENTED); +} // DataRangeIntersection + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CMiniportTopologySYSVAD::GetDescription +( + _Out_ PPCFILTER_DESCRIPTOR * OutFilterDescriptor +) +/*++ + +Routine Description: + + The GetDescription function gets a pointer to a filter description. + It provides a location to deposit a pointer in miniport's description + structure. This is the placeholder for the FromNode or ToNode fields in + connections which describe connections to the filter's pins + +Arguments: + + OutFilterDescriptor - Pointer to the filter description. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(OutFilterDescriptor); + + DPF_ENTER(("[%s]",__FUNCTION__)); + + *OutFilterDescriptor = m_FilterDescriptor; + + return (STATUS_SUCCESS); +} // GetDescription + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CMiniportTopologySYSVAD::Init +( + _In_ PUNKNOWN UnknownAdapter_, + _In_ PPORTTOPOLOGY Port_ +) +/*++ + +Routine Description: + + Initializes the topology miniport. + +Arguments: + + UnknownAdapter - + + Port_ - Pointer to topology port + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(UnknownAdapter_); + ASSERT(Port_); + + DPF_ENTER(("[CMiniportTopologySYSVAD::Init]")); + + NTSTATUS ntStatus; + + ntStatus = + UnknownAdapter_->QueryInterface( + IID_IAdapterCommon, + (PVOID *) &m_AdapterCommon); + + if (NT_SUCCESS(ntStatus)) + { + // + // Get the port event interface. + // + ntStatus = Port_->QueryInterface( + IID_IPortEvents, + (PVOID *)&m_PortEvents); + } + + if (NT_SUCCESS(ntStatus)) + { + m_AdapterCommon->MixerReset(); + } + + if (!NT_SUCCESS(ntStatus)) + { + // clean up AdapterCommon + SAFE_RELEASE(m_AdapterCommon); + SAFE_RELEASE(m_PortEvents); + } + + return ntStatus; +} // Init + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CMiniportTopologySYSVAD::PropertyHandlerGeneric +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Handles all properties for this miniport. + +Arguments: + + PropertyRequest - property request structure + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + switch (PropertyRequest->PropertyItem->Id) + { + case KSPROPERTY_AUDIO_VOLUMELEVEL: + ntStatus = PropertyHandler_Volume( + m_AdapterCommon, + PropertyRequest, + m_DeviceMaxChannels); + break; + + case KSPROPERTY_AUDIO_MUTE: + ntStatus = PropertyHandler_Mute( + m_AdapterCommon, + PropertyRequest, + m_DeviceMaxChannels); + break; + + case KSPROPERTY_AUDIO_PEAKMETER2: + ntStatus = PropertyHandler_PeakMeter2( + m_AdapterCommon, + PropertyRequest, + m_DeviceMaxChannels); + break; + + case KSPROPERTY_AUDIO_CPU_RESOURCES: + ntStatus = PropertyHandler_CpuResources(PropertyRequest); + break; + + case KSPROPERTY_AUDIO_MUX_SOURCE: + ntStatus = PropertyHandlerMuxSource(PropertyRequest); + break; + + case KSPROPERTY_AUDIO_DEV_SPECIFIC: + ntStatus = PropertyHandlerDevSpecific(PropertyRequest); + break; + + default: + DPF(D_TERSE, ("[PropertyHandlerGeneric: Invalid Device Request]")); + } + + return ntStatus; +} // PropertyHandlerGeneric + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CMiniportTopologySYSVAD::PropertyHandlerMuxSource +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + PropertyHandler for KSPROPERTY_AUDIO_MUX_SOURCE. + +Arguments: + + PropertyRequest - property request structure + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + DPF_ENTER(("[%s]",__FUNCTION__)); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + // + // Validate node + // This property is only valid for WAVEIN_MUX node. + // + // TODO if (WAVEIN_MUX == PropertyRequest->Node) + { + if (PropertyRequest->ValueSize >= sizeof(ULONG)) + { + PULONG pulMuxValue = PULONG(PropertyRequest->Value); + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + *pulMuxValue = m_AdapterCommon->MixerMuxRead(); + PropertyRequest->ValueSize = sizeof(ULONG); + ntStatus = STATUS_SUCCESS; + } + else if (PropertyRequest->Verb & KSPROPERTY_TYPE_SET) + { + m_AdapterCommon->MixerMuxWrite(*pulMuxValue); + ntStatus = STATUS_SUCCESS; + } + else if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_ALL, + VT_I4 + ); + } + } + else + { + DPF(D_TERSE, ("[PropertyHandlerMuxSource - Invalid parameter]")); + ntStatus = STATUS_INVALID_PARAMETER; + } + } + + return ntStatus; +} // PropertyHandlerMuxSource + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CMiniportTopologySYSVAD::PropertyHandlerDevSpecific +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Property handler for KSPROPERTY_AUDIO_DEV_SPECIFIC + +Arguments: + + PropertyRequest - property request structure + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + DPF_ENTER(("[%s]",__FUNCTION__)); + + NTSTATUS ntStatus=STATUS_SUCCESS; + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + if( DEV_SPECIFIC_VT_BOOL == PropertyRequest->Node ) + { + ntStatus = PropertyHandler_BasicSupport(PropertyRequest,KSPROPERTY_TYPE_ALL,VT_BOOL); + } + else + { + ULONG ExpectedSize = sizeof( KSPROPERTY_DESCRIPTION ) + + sizeof( KSPROPERTY_MEMBERSHEADER ) + + sizeof( KSPROPERTY_BOUNDS_LONG ); + DWORD ulPropTypeSetId; + + if( DEV_SPECIFIC_VT_I4 == PropertyRequest->Node ) + { + ulPropTypeSetId = VT_I4; + } + else if ( DEV_SPECIFIC_VT_UI4 == PropertyRequest->Node ) + { + ulPropTypeSetId = VT_UI4; + } + else + { + ulPropTypeSetId = VT_ILLEGAL; + ntStatus = STATUS_INVALID_PARAMETER; + } + + if( NT_SUCCESS(ntStatus)) + { + if ( !PropertyRequest->ValueSize ) + { + PropertyRequest->ValueSize = ExpectedSize; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (PropertyRequest->ValueSize >= sizeof(KSPROPERTY_DESCRIPTION)) + { + // if return buffer can hold a KSPROPERTY_DESCRIPTION, return it + // + PKSPROPERTY_DESCRIPTION PropDesc = PKSPROPERTY_DESCRIPTION(PropertyRequest->Value); + + PropDesc->AccessFlags = KSPROPERTY_TYPE_ALL; + PropDesc->DescriptionSize = ExpectedSize; + PropDesc->PropTypeSet.Set = KSPROPTYPESETID_General; + PropDesc->PropTypeSet.Id = ulPropTypeSetId; + PropDesc->PropTypeSet.Flags = 0; + PropDesc->MembersListCount = 0; + PropDesc->Reserved = 0; + + if ( PropertyRequest->ValueSize >= ExpectedSize ) + { + // Extra information to return + PropDesc->MembersListCount = 1; + + PKSPROPERTY_MEMBERSHEADER MembersHeader = ( PKSPROPERTY_MEMBERSHEADER )( PropDesc + 1); + MembersHeader->MembersFlags = KSPROPERTY_MEMBER_RANGES; + MembersHeader->MembersCount = 1; + MembersHeader->MembersSize = sizeof( KSPROPERTY_BOUNDS_LONG ); + MembersHeader->Flags = 0; + + PKSPROPERTY_BOUNDS_LONG PeakMeterBounds = (PKSPROPERTY_BOUNDS_LONG)( MembersHeader + 1); + if(VT_I4 == ulPropTypeSetId ) + { + PeakMeterBounds->SignedMinimum = 0; + PeakMeterBounds->SignedMaximum = 0x7fffffff; + } + else + { + PeakMeterBounds->UnsignedMinimum = 0; + PeakMeterBounds->UnsignedMaximum = 0xffffffff; + } + + // set the return value size + PropertyRequest->ValueSize = ExpectedSize; + } + else + { + // No extra information to return. + PropertyRequest->ValueSize = sizeof(KSPROPERTY_DESCRIPTION); + } + + ntStatus = STATUS_SUCCESS; + } + else if (PropertyRequest->ValueSize >= sizeof(ULONG)) + { + // if return buffer can hold a ULONG, return the access flags + // + *(PULONG(PropertyRequest->Value)) = KSPROPERTY_TYPE_ALL; + + PropertyRequest->ValueSize = sizeof(ULONG); + ntStatus = STATUS_SUCCESS; + } + else + { + PropertyRequest->ValueSize = 0; + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + } + } + } + else + { + // switch on node id + switch( PropertyRequest->Node ) + { + case DEV_SPECIFIC_VT_BOOL: + { + PBOOL pbDevSpecific; + + ntStatus = ValidatePropertyParams(PropertyRequest, sizeof(BOOL), 0); + + if (NT_SUCCESS(ntStatus)) + { + pbDevSpecific = PBOOL (PropertyRequest->Value); + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + *pbDevSpecific = m_AdapterCommon->bDevSpecificRead(); + PropertyRequest->ValueSize = sizeof(BOOL); + } + else if (PropertyRequest->Verb & KSPROPERTY_TYPE_SET) + { + m_AdapterCommon->bDevSpecificWrite(*pbDevSpecific); + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + } + } + break; + case DEV_SPECIFIC_VT_I4: + { + INT* piDevSpecific; + + ntStatus = ValidatePropertyParams(PropertyRequest, sizeof(int), 0); + + if (NT_SUCCESS(ntStatus)) + { + piDevSpecific = PINT (PropertyRequest->Value); + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + *piDevSpecific = m_AdapterCommon->iDevSpecificRead(); + PropertyRequest->ValueSize = sizeof(int); + } + else if (PropertyRequest->Verb & KSPROPERTY_TYPE_SET) + { + m_AdapterCommon->iDevSpecificWrite(*piDevSpecific); + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + } + } + break; + case DEV_SPECIFIC_VT_UI4: + { + UINT* puiDevSpecific; + + ntStatus = ValidatePropertyParams(PropertyRequest, sizeof(UINT), 0); + + if (NT_SUCCESS(ntStatus)) + { + puiDevSpecific = PUINT (PropertyRequest->Value); + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + *puiDevSpecific = m_AdapterCommon->uiDevSpecificRead(); + PropertyRequest->ValueSize = sizeof(UINT); + } + else if (PropertyRequest->Verb & KSPROPERTY_TYPE_SET) + { + m_AdapterCommon->uiDevSpecificWrite(*puiDevSpecific); + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + } + } + break; + default: + ntStatus = STATUS_INVALID_PARAMETER; + break; + } + + + if( !NT_SUCCESS(ntStatus)) + { + DPF(D_TERSE, ("[%s - ntStatus=0x%08x]",__FUNCTION__,ntStatus)); + } + } + + return ntStatus; +} // PropertyHandlerDevSpecific + +//============================================================================= +#pragma code_seg("PAGE") +VOID +CMiniportTopologySYSVAD::AddEventToEventList +( + _In_ PKSEVENT_ENTRY EventEntry +) +/*++ + +Routine Description: + + The AddEventToEventList method adds an event to the port driver's event list + +Arguments: + + EventEntry - + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[CMiniportTopology::AddEventToEventList]")); + + ASSERT(m_PortEvents != NULL); + + m_PortEvents->AddEventToEventList(EventEntry); +} + +//============================================================================= +#pragma code_seg() +VOID +CMiniportTopologySYSVAD::GenerateEventList +( + _In_opt_ GUID *Set, + _In_ ULONG EventId, + _In_ BOOL PinEvent, + _In_ ULONG PinId, + _In_ BOOL NodeEvent, + _In_ ULONG NodeId +) +/*++ + +Routine Description: + + The GenerateEventList method notifies clients through the port driver's list + of event entries that a particular event has occurred. + +Arguments: + + Set - + + EventId - + + PinEvent - + + PinId - + + NodeEvent - + + NodeId - + +--*/ +{ + DPF_ENTER(("[CMiniportTopologySYSVAD::GenerateEventList]")); + + ASSERT(m_PortEvents != NULL); + + m_PortEvents->GenerateEventList( + Set, + EventId, + PinEvent, + PinId, + NodeEvent, + NodeId); +} + +#pragma code_seg() + diff --git a/audio/sysvad/basetopo.h b/audio/sysvad/basetopo.h new file mode 100644 index 000000000..429c05eb3 --- /dev/null +++ b/audio/sysvad/basetopo.h @@ -0,0 +1,98 @@ + +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + basetopo.h + +Abstract: + + Declaration of topology miniport. + +--*/ + +#ifndef _SYSVAD_BASETOPO_H_ +#define _SYSVAD_BASETOPO_H_ + +//============================================================================= +// Classes +//============================================================================= + +/////////////////////////////////////////////////////////////////////////////// +// CMiniportTopologySYSVAD +// + +class CMiniportTopologySYSVAD +{ + protected: + PADAPTERCOMMON m_AdapterCommon; // Adapter common object. + PPCFILTER_DESCRIPTOR m_FilterDescriptor; // Filter descriptor. + PPORTEVENTS m_PortEvents; // Event interface. + USHORT m_DeviceMaxChannels; // Max device channels. + + public: + CMiniportTopologySYSVAD( + _In_ PCFILTER_DESCRIPTOR *FilterDesc, + _In_ USHORT DeviceMaxChannels + ); + + ~CMiniportTopologySYSVAD(); + + NTSTATUS GetDescription + ( + _Out_ PPCFILTER_DESCRIPTOR * Description + ); + + NTSTATUS DataRangeIntersection + ( + _In_ ULONG PinId, + _In_ PKSDATARANGE ClientDataRange, + _In_ PKSDATARANGE MyDataRange, + _In_ ULONG OutputBufferLength, + _Out_writes_bytes_to_opt_(OutputBufferLength, *ResultantFormatLength) + PVOID ResultantFormat OPTIONAL, + _Out_ PULONG ResultantFormatLength + ); + + NTSTATUS Init + ( + _In_ PUNKNOWN UnknownAdapter, + _In_ PPORTTOPOLOGY Port_ + ); + + // PropertyHandlers. + NTSTATUS PropertyHandlerGeneric + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + + NTSTATUS PropertyHandlerMuxSource + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + + NTSTATUS PropertyHandlerDevSpecific + ( + _In_ PPCPROPERTY_REQUEST PropertyRequest + ); + + VOID AddEventToEventList + ( + _In_ PKSEVENT_ENTRY EventEntry + ); + + VOID GenerateEventList + ( + _In_opt_ GUID *Set, + _In_ ULONG EventId, + _In_ BOOL PinEvent, + _In_ ULONG PinId, + _In_ BOOL NodeEvent, + _In_ ULONG NodeId + ); +}; + +#endif + diff --git a/audio/sysvad/common.cpp b/audio/sysvad/common.cpp new file mode 100644 index 000000000..dae94f3d6 --- /dev/null +++ b/audio/sysvad/common.cpp @@ -0,0 +1,6376 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + common.cpp + +Abstract: + + Implementation of the AdapterCommon class. + +--*/ + +#pragma warning (disable : 4127) + +#include +#include +#include "hw.h" +#include "savedata.h" +#include "IHVPrivatePropertySet.h" +#include "simple.h" + +#ifdef SYSVAD_BTH_BYPASS +#include +#include +#include // guild-arrival/removal +#include +#include "bthhfpminipairs.h" + +// # of sec before sync request is cancelled. +#define BTH_HFP_SYNC_REQ_TIMEOUT_IN_SEC 60 +#define BTH_HFP_NOTIFICATION_MAX_ERROR_COUNT 5 + +#endif // SYSVAD_BTH_BYPASS + +//----------------------------------------------------------------------------- +// CSaveData statics +//----------------------------------------------------------------------------- + +PSAVEWORKER_PARAM CSaveData::m_pWorkItems = NULL; +PDEVICE_OBJECT CSaveData::m_pDeviceObject = NULL; + +//============================================================================= +// Classes +//============================================================================= +#ifdef SYSVAD_BTH_BYPASS +class BthHfpDevice; // Forward declaration. +#endif // SYSVAD_BTH_BYPASS + +/////////////////////////////////////////////////////////////////////////////// +// CAdapterCommon +// +class CAdapterCommon : + public IAdapterCommon, + public IAdapterPowerManagement, + public CUnknown +{ + private: + PSERVICEGROUP m_pServiceGroupWave; + PDEVICE_OBJECT m_pDeviceObject; + PDEVICE_OBJECT m_pPhysicalDeviceObject; + WDFDEVICE m_WdfDevice; // Wdf device. + DEVICE_POWER_STATE m_PowerState; + + PCSYSVADHW m_pHW; // Virtual SYSVAD HW object + PPORTCLSETWHELPER m_pPortClsEtwHelper; + + static LONG m_AdapterInstances; // # of adapter objects. + + DWORD m_dwIdleRequests; + + public: + //===================================================================== + // Default CUnknown + DECLARE_STD_UNKNOWN(); + DEFINE_STD_CONSTRUCTOR(CAdapterCommon); + ~CAdapterCommon(); + + //===================================================================== + // Default IAdapterPowerManagement + IMP_IAdapterPowerManagement; + + //===================================================================== + // IAdapterCommon methods + + STDMETHODIMP_(NTSTATUS) Init + ( + _In_ PDEVICE_OBJECT DeviceObject + ); + + STDMETHODIMP_(PDEVICE_OBJECT) GetDeviceObject(void); + + STDMETHODIMP_(PDEVICE_OBJECT) GetPhysicalDeviceObject(void); + + STDMETHODIMP_(WDFDEVICE) GetWdfDevice(void); + + STDMETHODIMP_(void) SetWaveServiceGroup + ( + _In_ PSERVICEGROUP ServiceGroup + ); + + STDMETHODIMP_(BOOL) bDevSpecificRead(); + + STDMETHODIMP_(void) bDevSpecificWrite + ( + _In_ BOOL bDevSpecific + ); + STDMETHODIMP_(INT) iDevSpecificRead(); + + STDMETHODIMP_(void) iDevSpecificWrite + ( + _In_ INT iDevSpecific + ); + STDMETHODIMP_(UINT) uiDevSpecificRead(); + + STDMETHODIMP_(void) uiDevSpecificWrite + ( + _In_ UINT uiDevSpecific + ); + + STDMETHODIMP_(BOOL) MixerMuteRead + ( + _In_ ULONG Index, + _In_ ULONG Channel + ); + + STDMETHODIMP_(void) MixerMuteWrite + ( + _In_ ULONG Index, + _In_ ULONG Channel, + _In_ BOOL Value + ); + + STDMETHODIMP_(ULONG) MixerMuxRead(void); + + STDMETHODIMP_(void) MixerMuxWrite + ( + _In_ ULONG Index + ); + + STDMETHODIMP_(void) MixerReset(void); + + STDMETHODIMP_(LONG) MixerVolumeRead + ( + _In_ ULONG Index, + _In_ ULONG Channel + ); + + STDMETHODIMP_(void) MixerVolumeWrite + ( + _In_ ULONG Index, + _In_ ULONG Channel, + _In_ LONG Value + ); + + STDMETHODIMP_(LONG) MixerPeakMeterRead + ( + _In_ ULONG Index, + _In_ ULONG Channel + ); + + STDMETHODIMP_(NTSTATUS) WriteEtwEvent + ( + _In_ EPcMiniportEngineEvent miniportEventType, + _In_ ULONGLONG ullData1, + _In_ ULONGLONG ullData2, + _In_ ULONGLONG ullData3, + _In_ ULONGLONG ullData4 + ); + + STDMETHODIMP_(VOID) SetEtwHelper + ( + PPORTCLSETWHELPER _pPortClsEtwHelper + ); + + STDMETHODIMP_(NTSTATUS) InstallSubdevice + ( + _In_opt_ PIRP Irp, + _In_ PWSTR Name, + _In_ REFGUID PortClassId, + _In_ REFGUID MiniportClassId, + _In_opt_ PFNCREATEMINIPORT MiniportCreate, + _In_ ULONG cPropertyCount, + _In_reads_opt_(cPropertyCount) const SYSVAD_DEVPROPERTY * pProperties, + _In_opt_ PVOID DeviceContext, + _In_ PENDPOINT_MINIPAIR MiniportPair, + _In_opt_ PRESOURCELIST ResourceList, + _In_ REFGUID PortInterfaceId, + _Out_opt_ PUNKNOWN * OutPortInterface, + _Out_opt_ PUNKNOWN * OutPortUnknown, + _Out_opt_ PUNKNOWN * OutMiniportUnknown + ); + + STDMETHODIMP_(NTSTATUS) UnregisterSubdevice + ( + _In_opt_ PUNKNOWN UnknownPort + ); + + STDMETHODIMP_(NTSTATUS) ConnectTopologies + ( + _In_ PUNKNOWN UnknownTopology, + _In_ PUNKNOWN UnknownWave, + _In_ PHYSICALCONNECTIONTABLE* PhysicalConnections, + _In_ ULONG PhysicalConnectionCount + ); + + STDMETHODIMP_(NTSTATUS) DisconnectTopologies + ( + _In_ PUNKNOWN UnknownTopology, + _In_ PUNKNOWN UnknownWave, + _In_ PHYSICALCONNECTIONTABLE* PhysicalConnections, + _In_ ULONG PhysicalConnectionCount + ); + + STDMETHODIMP_(NTSTATUS) InstallEndpointFilters + ( + _In_opt_ PIRP Irp, + _In_ PENDPOINT_MINIPAIR MiniportPair, + _In_opt_ PVOID DeviceContext, + _Out_opt_ PUNKNOWN * UnknownTopology, + _Out_opt_ PUNKNOWN * UnknownWave + ); + + STDMETHODIMP_(NTSTATUS) RemoveEndpointFilters + ( + _In_ PENDPOINT_MINIPAIR MiniportPair, + _In_opt_ PUNKNOWN UnknownTopology, + _In_opt_ PUNKNOWN UnknownWave + ); + + STDMETHODIMP_(NTSTATUS) GetFilters + ( + _In_ PENDPOINT_MINIPAIR MiniportPair, + _Out_opt_ PUNKNOWN *UnknownTopologyPort, + _Out_opt_ PUNKNOWN *UnknownTopologyMiniport, + _Out_opt_ PUNKNOWN *UnknownWavePort, + _Out_opt_ PUNKNOWN *UnknownWaveMiniport + ); + + STDMETHODIMP_(NTSTATUS) SetIdlePowerManagement + ( + _In_ PENDPOINT_MINIPAIR MiniportPair, + _In_ BOOL bEnabled + ); + +#ifdef SYSVAD_BTH_BYPASS + STDMETHODIMP_(NTSTATUS) InitBthScoBypass(); + + STDMETHODIMP_(VOID) CleanupBthScoBypass(); +#endif // SYSVAD_BTH_BYPASS + + STDMETHODIMP_(VOID) Cleanup(); + + //===================================================================== + // friends + friend NTSTATUS NewAdapterCommon + ( + _Out_ PUNKNOWN * Unknown, + _In_ REFCLSID, + _In_opt_ PUNKNOWN UnknownOuter, + _When_((PoolType & NonPagedPoolMustSucceed) != 0, + __drv_reportError("Must succeed pool allocations are forbidden. " + "Allocation failures cause a system crash")) + _In_ POOL_TYPE PoolType + ); + +#ifdef SYSVAD_BTH_BYPASS + //===================================================================== + // Bluetooth Hands-free Profile SCO Bypass support. + + private: + PVOID m_BthHfpScoNotificationHandle; + FAST_MUTEX m_BthHfpFastMutex; // To serialize access. + WDFWORKITEM m_BthHfpWorkItem; // Async work-item. + LIST_ENTRY m_BthHfpWorkTasks; // Work-item's tasks. + LIST_ENTRY m_BthHfpDevices; // Bth HFP devices. + NPAGED_LOOKASIDE_LIST m_BhtHfpWorkTaskPool; // LookasideList + size_t m_BhtHfpWorkTaskPoolElementSize; + BOOL m_BthHfpEnableCleanup; // Do cleanup if true. + + private: + static + DRIVER_NOTIFICATION_CALLBACK_ROUTINE EvtBthHfpScoBypassInterfaceChange; + + static + EVT_WDF_WORKITEM EvtBthHfpScoBypassInterfaceWorkItem; + + protected: + BthHfpDevice * BthHfpDeviceFind + ( + _In_ PUNICODE_STRING SymbolicLinkName + ); + + NTSTATUS BthHfpScoInterfaceArrival + ( + _In_ PUNICODE_STRING SymbolicLinkName + ); + + NTSTATUS BthHfpScoInterfaceRemoval + ( + _In_ PUNICODE_STRING SymbolicLinkName + ); +#endif // SYSVAD_BTH_BYPASS + + private: + + LIST_ENTRY m_SubdeviceCache; + + NTSTATUS GetCachedSubdevice + ( + _In_ PWSTR Name, + _Out_opt_ PUNKNOWN *OutUnknownPort, + _Out_opt_ PUNKNOWN *OutUnknownMiniport + ); + + NTSTATUS CacheSubdevice + ( + _In_ PWSTR Name, + _In_ PUNKNOWN UnknownPort, + _In_ PUNKNOWN UnknownMiniport + ); + + NTSTATUS RemoveCachedSubdevice + ( + _In_ PWSTR Name + ); + + VOID EmptySubdeviceCache(); + + NTSTATUS CreateAudioInterfaceWithProperties + ( + _In_ PCWSTR ReferenceString, + _In_ ULONG cPropertyCount, + _In_reads_(cPropertyCount) const SYSVAD_DEVPROPERTY *pProperties, + _Out_ _At_(AudioSymbolicLinkName->Buffer, __drv_allocatesMem(Mem)) PUNICODE_STRING AudioSymbolicLinkName + ); +}; + +typedef struct _MINIPAIR_UNKNOWN +{ + LIST_ENTRY ListEntry; + WCHAR Name[MAX_PATH]; + PUNKNOWN PortInterface; + PUNKNOWN MiniportInterface; + PADAPTERPOWERMANAGEMENT PowerInterface; +} MINIPAIR_UNKNOWN; + +// +// Used to implement the singleton pattern. +// +LONG CAdapterCommon::m_AdapterInstances = 0; + + +#ifdef SYSVAD_BTH_BYPASS + +//===================================================================== +// +// CAdapterCommon: Bluetooth Hands-Free Profile SCO Bypass definitions. +// + +struct BthHfpWorkItemContext +{ + CAdapterCommon * Adapter; +}; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME +( + BthHfpWorkItemContext, + GetBthHfpWorkItemContext +) + +// start/stop the device +enum eBthHfpTaskAction +{ + eBthHfpTaskStart = 1, + eBthHfpTaskStop = 2, +}; + +struct BthHfpWorkTask +{ + LIST_ENTRY ListEntry; + BthHfpDevice * Device; + eBthHfpTaskAction Action; +}; + + +//===================================================================== +// +// Device: Bluetooth Hands-Free Profile SCO Bypass definitions. +// + +// BTH HFP device's notification work-item context. +struct BthHfpDeviceNotificationWorkItemContext +{ + BthHfpDevice * BthHfpDevice; +}; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME +( + BthHfpDeviceNotificationWorkItemContext, + GetBthHfpDeviceNotificationWorkItemContext +) + +// BTH HFP device's notification request context. +union BthHfpDeviceNotificationBuffer +{ + BOOL bImmediate; + LONG Volume; + BOOL BoolStatus; + NTSTATUS NtStatus; +}; + +struct BthHfpDeviceNotificationReqContext +{ + BthHfpDevice * BthHfpDevice; + LONG Errors; + BthHfpDeviceNotificationBuffer Buffer; + WDFMEMORY MemIn; + WDFMEMORY MemOut; +}; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME +( + BthHfpDeviceNotificationReqContext, + GetBthHfpDeviceNotificationReqContext +) + +enum eBthHfpState +{ + eBthHfpStateInvalid = 0, + eBthHfpStateInitializing = 1, + eBthHfpStateRunning = 2, + eBthHfpStateStopping = 3, + eBthHfpStateStopped = 4, + eBthHfpStateFailed = 5, +}; + +// To support event notification. +struct BthHfpEventCallback +{ + PFNEVENTNOTIFICATION Handler; + PVOID Context; +}; + +// +// This class represents a the Bluetooth Hands-Free Profile SCO Bypass device. +// There is one class for each interface (id = symbolic-link-name). +// +class BthHfpDevice : + IBthHfpDeviceCommon, + public CUnknown +{ + private: + eBthHfpState m_State; + + CAdapterCommon * m_Adapter; + WDFIOTARGET m_WdfIoTarget; + + LIST_ENTRY m_ListEntry; + UNICODE_STRING m_SymbolicLinkName; + + // + // The Topo Filter Desc and Topo Pins structures referenced from + // the Miniports structure are deep copies: they start as copies of the + // static structures and are modified to allow per-SCO-Device pin + // categories based on the info obtained from + // IOCTL_BTHHFP_DEVICE_GET_DESCRIPTOR2 + // + PENDPOINT_MINIPAIR m_SpeakerMiniports; + PENDPOINT_MINIPAIR m_MicMiniports; + + PUNKNOWN m_UnknownSpeakerTopology; + PUNKNOWN m_UnknownSpeakerWave; + PUNKNOWN m_UnknownMicTopology; + PUNKNOWN m_UnknownMicWave; + + PBTHHFP_DESCRIPTOR2 m_Descriptor; + PKSPROPERTY_VALUES m_VolumePropValues; + LONG m_SpeakerVolumeLevel; + LONG m_MicVolumeLevel; + union{ + BOOL m_ConnectionStatus; + LONG m_ConnectionStatusLong; + }; // unnamed. + + union{ + NTSTATUS m_StreamStatus; + LONG m_StreamStatusLong; + }; // unnamed. + + KEVENT m_StreamStatusEvent; + + // + // Set to TRUE when the HF (remote device) wants to disable the + // NR + EC of the AG (local system). + // + LONG m_NRECDisableStatusLong; + + WDFREQUEST m_StreamReq; + WDFREQUEST m_SpeakerVolumeReq; + WDFREQUEST m_MicVolumeReq; + WDFREQUEST m_ConnectionReq; + WDFREQUEST m_NRECDisableStatusReq; + WDFWORKITEM m_WorkItem; + WDFCOLLECTION m_ReqCollection; + KSPIN_LOCK m_Lock; + + LONG m_nStreams; // # of open streams. + + BthHfpEventCallback m_SpeakerVolumeCallback; + BthHfpEventCallback m_SpeakerConnectionStatusCallback; + BthHfpEventCallback m_MicVolumeCallback; + BthHfpEventCallback m_MicConnectionStatusCallback; + + public: + //===================================================================== + // Default CUnknown + DECLARE_STD_UNKNOWN(); + DEFINE_STD_CONSTRUCTOR(BthHfpDevice); + ~BthHfpDevice(); + + NTSTATUS Init + ( + _In_ CAdapterCommon * Adapter, + _In_ PUNICODE_STRING SymbolicLinkName + ); + + public: + //===================================================================== + // + // Public functions used by CAdapterCommon object. + // + VOID Start(); + VOID Stop(); + + PLIST_ENTRY GetListEntry() + { + return &m_ListEntry; + } + + PUNICODE_STRING GetSymbolicLinkName() + { + return &m_SymbolicLinkName; + } + + static + BthHfpDevice * + GetBthHfpDevice + ( + _In_ PLIST_ENTRY le + ) + { + return CONTAINING_RECORD(le, BthHfpDevice, m_ListEntry); + } + + public: + //===================================================================== + // + // IBthHfpDeviceCommon functions. + // + STDMETHODIMP_(BOOL) IsVolumeSupported(); + + STDMETHODIMP_(PKSPROPERTY_VALUES) GetVolumeSettings + ( + _Out_ PULONG Size + ); + + STDMETHODIMP_(LONG) GetSpeakerVolume(); + + STDMETHODIMP_(NTSTATUS) SetSpeakerVolume + ( + _In_ ULONG Volume + ); + + STDMETHODIMP_(LONG) GetMicVolume(); + + STDMETHODIMP_(NTSTATUS) SetMicVolume + ( + _In_ ULONG Volume + ); + + STDMETHODIMP_(BOOL) GetConnectionStatus(); + + STDMETHODIMP_(NTSTATUS) Connect(); + + STDMETHODIMP_(NTSTATUS) Disconnect(); + + STDMETHODIMP_(BOOL) GetStreamStatus(); + + STDMETHODIMP_(NTSTATUS) StreamOpen(); + + STDMETHODIMP_(NTSTATUS) StreamClose(); + + STDMETHODIMP_(GUID) GetContainerId(); + + STDMETHODIMP_(VOID) SetSpeakerVolumeHandler + ( + _In_opt_ PFNEVENTNOTIFICATION EventHandler, + _In_opt_ PVOID EventHandlerContext + ); + + STDMETHODIMP_(VOID) SetSpeakerConnectionStatusHandler + ( + _In_opt_ PFNEVENTNOTIFICATION EventHandler, + _In_opt_ PVOID EventHandlerContext + ); + + STDMETHODIMP_(VOID) SetMicVolumeHandler + ( + _In_opt_ PFNEVENTNOTIFICATION EventHandler, + _In_opt_ PVOID EventHandlerContext + ); + + STDMETHODIMP_(VOID) SetMicConnectionStatusHandler + ( + _In_opt_ PFNEVENTNOTIFICATION EventHandler, + _In_opt_ PVOID EventHandlerContext + ); + + STDMETHODIMP_(BOOL) IsNRECSupported(); + + STDMETHODIMP_(BOOL) GetNRECDisableStatus(); + + private: + //===================================================================== + // + // Helper functions. + // + NTSTATUS SendIoCtrlSynchronously + ( + _In_opt_ WDFREQUEST Request, + _In_ ULONG IoControlCode, + _In_ ULONG InLength, + _In_ ULONG OutLength, + _When_(InLength > 0 || OutLength > 0, _In_) + _When_(InLength == 0 && OutLength == 0, _In_opt_) + PVOID Buffer + ); + + NTSTATUS SendIoCtrlAsynchronously + ( + _In_ WDFREQUEST Request, + _In_ ULONG IoControlCode, + _In_opt_ WDFMEMORY MemIn, + _In_opt_ WDFMEMORY MemOut, + _In_ PFN_WDF_REQUEST_COMPLETION_ROUTINE CompletionRoutine, + _In_ WDFCONTEXT Context + ); + + NTSTATUS GetBthHfpDescriptor + ( + _Out_ PBTHHFP_DESCRIPTOR2 * Descriptor + ); + + NTSTATUS EnableBthHfpNrecDisableStatusNotification(); + + NTSTATUS GetBthHfpVolumePropertyValues + ( + _In_ ULONG Length, + _Out_ PKSPROPERTY_VALUES * PropValues + ); + + NTSTATUS SetBthHfpSpeakerVolume + ( + _In_ LONG Volume + ); + + NTSTATUS GetBthHfpSpeakerVolume + ( + _Out_ LONG * Volume + ); + + NTSTATUS EnableBthHfpSpeakerVolumeStatusNotification(); + + NTSTATUS SetBthHfpMicVolume + ( + _In_ LONG Volume + ); + + NTSTATUS GetBthHfpMicVolume + ( + _Out_ LONG * Volume + ); + + NTSTATUS EnableBthHfpMicVolumeStatusNotification(); + + NTSTATUS GetBthHfpConnectionStatus + ( + _Out_ BOOL * ConnectionStatus + ); + + NTSTATUS EnableBthHfpConnectionStatusNotification(); + + static + NTSTATUS CreateCustomEndpointMinipair + ( + _In_ PENDPOINT_MINIPAIR pBaseMinipair, + _In_ PUNICODE_STRING FriendlyName, + _In_ PGUID pCategory, + _Outptr_ PENDPOINT_MINIPAIR *ppCustomMinipair + ); + + static + NTSTATUS UpdateCustomEndpointCategory + ( + _In_ PPCFILTER_DESCRIPTOR pCustomMinipairTopoFilter, + _In_ PPCPIN_DESCRIPTOR pCustomMinipairTopoPins, + _In_ PGUID pCategory + ); + + static + VOID DeleteCustomEndpointMinipair + ( + _In_ PENDPOINT_MINIPAIR CustomMinipair + ); + + NTSTATUS GetBthHfpCodecId + ( + _Out_ UCHAR * CodecId + ); + + NTSTATUS SetBthHfpConnect(); + + NTSTATUS SetBthHfpDisconnect(); + + NTSTATUS SetBthHfpStreamOpen(); + + NTSTATUS SetBthHfpStreamClose(); + + NTSTATUS EnableBthHfpStreamStatusNotification(); + + NTSTATUS StopBthHfpStreamStatusNotification(); + + // + // WDF I/O Target callback. + // + static + EVT_WDF_IO_TARGET_QUERY_REMOVE EvtBthHfpTargetQueryRemove; + + static + EVT_WDF_IO_TARGET_REMOVE_CANCELED EvtBthHfpTargetRemoveCanceled; + + static + EVT_WDF_IO_TARGET_REMOVE_COMPLETE EvtBthHfpTargetRemoveComplete; + + // + // Status notifications callbacks. + // + static + EVT_WDF_REQUEST_COMPLETION_ROUTINE EvtBthHfpDeviceStreamStatusCompletion; + + static + EVT_WDF_REQUEST_COMPLETION_ROUTINE EvtBthHfpDeviceNotificationStatusCompletion; + + static + EVT_WDF_WORKITEM EvtBthHfpDeviceNotificationStatusWorkItem; +}; +#endif // SYSVAD_BTH_BYPASS + +//----------------------------------------------------------------------------- +// Functions +//----------------------------------------------------------------------------- + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS SysvadIoSetDeviceInterfacePropertyDataMultiple +( + _In_ PUNICODE_STRING SymbolicLinkName, + _In_ ULONG cPropertyCount, + _In_reads_(cPropertyCount) const SYSVAD_DEVPROPERTY *pProperties +) +{ + NTSTATUS ntStatus; + + PAGED_CODE(); + + for (ULONG i = 0; i < cPropertyCount; i++) + { + ntStatus = IoSetDeviceInterfacePropertyData( + SymbolicLinkName, + pProperties[i].PropertyKey, + LOCALE_NEUTRAL, + PLUGPLAY_PROPERTY_PERSISTENT, + pProperties[i].Type, + pProperties[i].BufferSize, + pProperties[i].Buffer); + + if (!NT_SUCCESS(ntStatus)) + { + return ntStatus; + } + } + + return STATUS_SUCCESS; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +NewAdapterCommon +( + _Out_ PUNKNOWN * Unknown, + _In_ REFCLSID, + _In_opt_ PUNKNOWN UnknownOuter, + _When_((PoolType & NonPagedPoolMustSucceed) != 0, + __drv_reportError("Must succeed pool allocations are forbidden. " + "Allocation failures cause a system crash")) + _In_ POOL_TYPE PoolType +) +/*++ + +Routine Description: + + Creates a new CAdapterCommon + +Arguments: + + Unknown - + + UnknownOuter - + + PoolType + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(Unknown); + + NTSTATUS ntStatus; + + // + // This sample supports only one instance of this object. + // (b/c of CSaveData's static members and Bluetooth HFP logic). + // + if (CAdapterCommon::m_AdapterInstances != 0) + { + ntStatus = STATUS_DEVICE_BUSY; + DPF(D_ERROR, ("NewAdapterCommon failed, only one instance is allowed")); + goto Done; + } + + CAdapterCommon::m_AdapterInstances++; + + // + // Allocate an adapter object. + // + CAdapterCommon *p = new(PoolType, MINADAPTER_POOLTAG) CAdapterCommon(UnknownOuter); + if (p == NULL) + { + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + DPF(D_ERROR, ("NewAdapterCommon failed, 0x%x", ntStatus)); + goto Done; + } + + // + // Success. + // + *Unknown = PUNKNOWN((PADAPTERCOMMON)(p)); + (*Unknown)->AddRef(); + ntStatus = STATUS_SUCCESS; + +Done: + return ntStatus; +} // NewAdapterCommon + +//============================================================================= +#pragma code_seg("PAGE") +CAdapterCommon::~CAdapterCommon +( + void +) +/*++ + +Routine Description: + + Destructor for CAdapterCommon. + +Arguments: + +Return Value: + + void + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::~CAdapterCommon]")); + + if (m_pHW) + { + delete m_pHW; + m_pHW = NULL; + } + + CSaveData::DestroyWorkItems(); + + SAFE_RELEASE(m_pPortClsEtwHelper); + SAFE_RELEASE(m_pServiceGroupWave); + + if (m_WdfDevice) + { + WdfObjectDelete(m_WdfDevice); + m_WdfDevice = NULL; + } + + CAdapterCommon::m_AdapterInstances--; + ASSERT(CAdapterCommon::m_AdapterInstances == 0); +} // ~CAdapterCommon + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP_(PDEVICE_OBJECT) +CAdapterCommon::GetDeviceObject +( + void +) +/*++ + +Routine Description: + + Returns the deviceobject + +Arguments: + +Return Value: + + PDEVICE_OBJECT + +--*/ +{ + PAGED_CODE(); + + return m_pDeviceObject; +} // GetDeviceObject + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP_(PDEVICE_OBJECT) +CAdapterCommon::GetPhysicalDeviceObject +( + void +) +/*++ + +Routine Description: + + Returns the PDO. + +Arguments: + +Return Value: + + PDEVICE_OBJECT + +--*/ +{ + PAGED_CODE(); + + return m_pPhysicalDeviceObject; +} // GetPhysicalDeviceObject + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP_(WDFDEVICE) +CAdapterCommon::GetWdfDevice +( + void +) +/*++ + +Routine Description: + + Returns the associated WDF miniport device. Note that this is NOT an audio + miniport. The WDF miniport device is the WDF device associated with the + adapter. + +Arguments: + +Return Value: + + WDFDEVICE + +--*/ +{ + PAGED_CODE(); + + return m_WdfDevice; +} // GetWdfDevice + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CAdapterCommon::Init +( + _In_ PDEVICE_OBJECT DeviceObject +) +/*++ + +Routine Description: + + Initialize adapter common object. + +Arguments: + + DeviceObject - pointer to the device object + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::Init]")); + + ASSERT(DeviceObject); + + NTSTATUS ntStatus = STATUS_SUCCESS; + +#ifdef SYSVAD_BTH_BYPASS + m_BthHfpEnableCleanup = FALSE; +#endif // SYSVAD_BTH_BYPASS + + m_pServiceGroupWave = NULL; + m_pDeviceObject = DeviceObject; + m_pPhysicalDeviceObject = NULL; + m_WdfDevice = NULL; + m_PowerState = PowerDeviceD0; + m_pHW = NULL; + m_pPortClsEtwHelper = NULL; + + InitializeListHead(&m_SubdeviceCache); + + // + // Get the PDO. + // + ntStatus = PcGetPhysicalDeviceObject(DeviceObject, &m_pPhysicalDeviceObject); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("PcGetPhysicalDeviceObject failed, 0x%x", ntStatus)), + Done); + + // + // Create a WDF miniport to represent the adapter. Note that WDF miniports + // are NOT audio miniports. An audio adapter is associated with a single WDF + // miniport. This driver uses WDF to simplify the handling of the Bluetooth + // SCO HFP Bypass interface. + // + ntStatus = WdfDeviceMiniportCreate( WdfGetDriver(), + WDF_NO_OBJECT_ATTRIBUTES, + DeviceObject, // FDO + NULL, // Next device. + NULL, // PDO + &m_WdfDevice); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("WdfDeviceMiniportCreate failed, 0x%x", ntStatus)), + Done); + + // Initialize HW. + // + m_pHW = new (NonPagedPoolNx, SYSVAD_POOLTAG) CSYSVADHW; + if (!m_pHW) + { + DPF(D_TERSE, ("Insufficient memory for SYSVAD HW")); + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + } + IF_FAILED_JUMP(ntStatus, Done); + + m_pHW->MixerReset(); + + // + // Initialize SaveData class. + // + CSaveData::SetDeviceObject(DeviceObject); //device object is needed by CSaveData + ntStatus = CSaveData::InitializeWorkItems(DeviceObject); + IF_FAILED_JUMP(ntStatus, Done); + +Done: + + return ntStatus; +} // Init + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP_(void) +CAdapterCommon::MixerReset +( + void +) +/*++ + +Routine Description: + + Reset mixer registers from registry. + +Arguments: + +Return Value: + + void + +--*/ +{ + PAGED_CODE(); + + if (m_pHW) + { + m_pHW->MixerReset(); + } +} // MixerReset + +//============================================================================= +/* Here are the definitions of the standard miniport events. + +Event type : eMINIPORT_IHV_DEFINED +Parameter 1 : Defined and used by IHVs +Parameter 2 : Defined and used by IHVs +Parameter 3 :Defined and used by IHVs +Parameter 4 :Defined and used by IHVs + +Event type: eMINIPORT_BUFFER_COMPLETE +Parameter 1: Current linear buffer position +Parameter 2: the previous WaveRtBufferWritePosition that the drive received +Parameter 3: Data length completed +Parameter 4:0 + +Event type: eMINIPORT_PIN_STATE +Parameter 1: Current linear buffer position +Parameter 2: the previous WaveRtBufferWritePosition that the drive received +Parameter 3: Pin State 0->KS_STOP, 1->KS_ACQUIRE, 2->KS_PAUSE, 3->KS_RUN +Parameter 4:0 + +Event type: eMINIPORT_GET_STREAM_POS +Parameter 1: Current linear buffer position +Parameter 2: the previous WaveRtBufferWritePosition that the drive received +Parameter 3: 0 +Parameter 4:0 + + +Event type: eMINIPORT_SET_WAVERT_BUFFER_WRITE_POS +Parameter 1: Current linear buffer position +Parameter 2: the previous WaveRtBufferWritePosition that the drive received +Parameter 3: the arget WaveRtBufferWritePosition received from portcls +Parameter 4:0 + +Event type: eMINIPORT_GET_PRESENTATION_POS +Parameter 1: Current linear buffer position +Parameter 2: the previous WaveRtBufferWritePosition that the drive received +Parameter 3: Presentation position +Parameter 4:0 + +Event type: eMINIPORT_PROGRAM_DMA +Parameter 1: Current linear buffer position +Parameter 2: the previous WaveRtBufferWritePosition that the drive received +Parameter 3: Starting WaveRt buffer offset +Parameter 4: Data length + +Event type: eMINIPORT_GLITCH_REPORT +Parameter 1: Current linear buffer position +Parameter 2: the previous WaveRtBufferWritePosition that the drive received +Parameter 3: major glitch code: 1:WaveRT buffer is underrun, + 2:decoder errors, + 3:receive the same wavert buffer two in a row in event driven mode +Parameter 4: minor code for the glitch cause + +Event type: eMINIPORT_LAST_BUFFER_RENDERED +Parameter 1: Current linear buffer position +Parameter 2: the very last WaveRtBufferWritePosition that the driver received +Parameter 3: 0 +Parameter 4: 0 + +*/ +#pragma code_seg() +STDMETHODIMP +CAdapterCommon::WriteEtwEvent +( + _In_ EPcMiniportEngineEvent miniportEventType, + _In_ ULONGLONG ullData1, + _In_ ULONGLONG ullData2, + _In_ ULONGLONG ullData3, + _In_ ULONGLONG ullData4 +) +{ + NTSTATUS ntStatus = STATUS_SUCCESS; + + if (m_pPortClsEtwHelper) + { + ntStatus = m_pPortClsEtwHelper->MiniportWriteEtwEvent( miniportEventType, ullData1, ullData2, ullData3, ullData4) ; + } + return ntStatus; +} // WriteEtwEvent + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP_(void) +CAdapterCommon::SetEtwHelper +( + PPORTCLSETWHELPER _pPortClsEtwHelper +) +{ + PAGED_CODE(); + + SAFE_RELEASE(m_pPortClsEtwHelper); + + m_pPortClsEtwHelper = _pPortClsEtwHelper; + + if (m_pPortClsEtwHelper) + { + m_pPortClsEtwHelper->AddRef(); + } +} // SetEtwHelper + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP +CAdapterCommon::NonDelegatingQueryInterface +( + _In_ REFIID Interface, + _COM_Outptr_ PVOID * Object +) +/*++ + +Routine Description: + + QueryInterface routine for AdapterCommon + +Arguments: + + Interface - + + Object - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(Object); + + if (IsEqualGUIDAligned(Interface, IID_IUnknown)) + { + *Object = PVOID(PUNKNOWN(PADAPTERCOMMON(this))); + } + else if (IsEqualGUIDAligned(Interface, IID_IAdapterCommon)) + { + *Object = PVOID(PADAPTERCOMMON(this)); + } + else if (IsEqualGUIDAligned(Interface, IID_IAdapterPowerManagement)) + { + *Object = PVOID(PADAPTERPOWERMANAGEMENT(this)); + } + else + { + *Object = NULL; + } + + if (*Object) + { + PUNKNOWN(*Object)->AddRef(); + return STATUS_SUCCESS; + } + + return STATUS_INVALID_PARAMETER; +} // NonDelegatingQueryInterface + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP_(void) +CAdapterCommon::SetWaveServiceGroup +( + _In_ PSERVICEGROUP ServiceGroup +) +/*++ + +Routine Description: + + +Arguments: + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::SetWaveServiceGroup]")); + + SAFE_RELEASE(m_pServiceGroupWave); + + m_pServiceGroupWave = ServiceGroup; + + if (m_pServiceGroupWave) + { + m_pServiceGroupWave->AddRef(); + } +} // SetWaveServiceGroup + +//============================================================================= +#pragma code_seg() +STDMETHODIMP_(BOOL) +CAdapterCommon::bDevSpecificRead() +/*++ + +Routine Description: + + Fetch Device Specific information. + +Arguments: + + N/A + +Return Value: + + BOOL - Device Specific info + +--*/ +{ + if (m_pHW) + { + return m_pHW->bGetDevSpecific(); + } + + return FALSE; +} // bDevSpecificRead + +//============================================================================= +#pragma code_seg() +STDMETHODIMP_(void) +CAdapterCommon::bDevSpecificWrite +( + _In_ BOOL bDevSpecific +) +/*++ + +Routine Description: + + Store the new value in the Device Specific location. + +Arguments: + + bDevSpecific - Value to store + +Return Value: + + N/A. + +--*/ +{ + if (m_pHW) + { + m_pHW->bSetDevSpecific(bDevSpecific); + } +} // DevSpecificWrite + +//============================================================================= +#pragma code_seg() +STDMETHODIMP_(INT) +CAdapterCommon::iDevSpecificRead() +/*++ + +Routine Description: + + Fetch Device Specific information. + +Arguments: + + N/A + +Return Value: + + INT - Device Specific info + +--*/ +{ + if (m_pHW) + { + return m_pHW->iGetDevSpecific(); + } + + return 0; +} // iDevSpecificRead + +//============================================================================= +#pragma code_seg() +STDMETHODIMP_(void) +CAdapterCommon::iDevSpecificWrite +( + _In_ INT iDevSpecific +) +/*++ + +Routine Description: + + Store the new value in the Device Specific location. + +Arguments: + + iDevSpecific - Value to store + +Return Value: + + N/A. + +--*/ +{ + if (m_pHW) + { + m_pHW->iSetDevSpecific(iDevSpecific); + } +} // iDevSpecificWrite + +//============================================================================= +#pragma code_seg() +STDMETHODIMP_(UINT) +CAdapterCommon::uiDevSpecificRead() +/*++ + +Routine Description: + + Fetch Device Specific information. + +Arguments: + + N/A + +Return Value: + + UINT - Device Specific info + +--*/ +{ + if (m_pHW) + { + return m_pHW->uiGetDevSpecific(); + } + + return 0; +} // uiDevSpecificRead + +//============================================================================= +#pragma code_seg() +STDMETHODIMP_(void) +CAdapterCommon::uiDevSpecificWrite +( + _In_ UINT uiDevSpecific +) +/*++ + +Routine Description: + + Store the new value in the Device Specific location. + +Arguments: + + uiDevSpecific - Value to store + +Return Value: + + N/A. + +--*/ +{ + if (m_pHW) + { + m_pHW->uiSetDevSpecific(uiDevSpecific); + } +} // uiDevSpecificWrite + +//============================================================================= +#pragma code_seg() +STDMETHODIMP_(BOOL) +CAdapterCommon::MixerMuteRead +( + _In_ ULONG Index, + _In_ ULONG Channel +) +/*++ + +Routine Description: + + Store the new value in mixer register array. + +Arguments: + + Index - node id + +Return Value: + + BOOL - mixer mute setting for this node + +--*/ +{ + if (m_pHW) + { + return m_pHW->GetMixerMute(Index, Channel); + } + + return 0; +} // MixerMuteRead + +//============================================================================= +#pragma code_seg() +STDMETHODIMP_(void) +CAdapterCommon::MixerMuteWrite +( + _In_ ULONG Index, + _In_ ULONG Channel, + _In_ BOOL Value +) +/*++ + +Routine Description: + + Store the new value in mixer register array. + +Arguments: + + Index - node id + + Value - new mute settings + +Return Value: + + NT status code. + +--*/ +{ + if (m_pHW) + { + m_pHW->SetMixerMute(Index, Channel, Value); + } +} // MixerMuteWrite + +//============================================================================= +#pragma code_seg() +STDMETHODIMP_(ULONG) +CAdapterCommon::MixerMuxRead() +/*++ + +Routine Description: + + Return the mux selection + +Arguments: + + Index - node id + + Value - new mute settings + +Return Value: + + NT status code. + +--*/ +{ + if (m_pHW) + { + return m_pHW->GetMixerMux(); + } + + return 0; +} // MixerMuxRead + +//============================================================================= +#pragma code_seg() +STDMETHODIMP_(void) +CAdapterCommon::MixerMuxWrite +( + _In_ ULONG Index +) +/*++ + +Routine Description: + + Store the new mux selection + +Arguments: + + Index - node id + + Value - new mute settings + +Return Value: + + NT status code. + +--*/ +{ + if (m_pHW) + { + m_pHW->SetMixerMux(Index); + } +} // MixerMuxWrite + +//============================================================================= +#pragma code_seg() +STDMETHODIMP_(LONG) +CAdapterCommon::MixerVolumeRead +( + _In_ ULONG Index, + _In_ ULONG Channel +) +/*++ + +Routine Description: + + Return the value in mixer register array. + +Arguments: + + Index - node id + + Channel = which channel + +Return Value: + + Byte - mixer volume settings for this line + +--*/ +{ + if (m_pHW) + { + return m_pHW->GetMixerVolume(Index, Channel); + } + + return 0; +} // MixerVolumeRead + +//============================================================================= +#pragma code_seg() +STDMETHODIMP_(void) +CAdapterCommon::MixerVolumeWrite +( + _In_ ULONG Index, + _In_ ULONG Channel, + _In_ LONG Value +) +/*++ + +Routine Description: + + Store the new value in mixer register array. + +Arguments: + + Index - node id + + Channel - which channel + + Value - new volume level + +Return Value: + + void + +--*/ +{ + if (m_pHW) + { + m_pHW->SetMixerVolume(Index, Channel, Value); + } +} // MixerVolumeWrite + +//============================================================================= +#pragma code_seg() +STDMETHODIMP_(LONG) +CAdapterCommon::MixerPeakMeterRead +( + _In_ ULONG Index, + _In_ ULONG Channel +) +/*++ + +Routine Description: + + Return the value in mixer register array. + +Arguments: + + Index - node id + + Channel = which channel + +Return Value: + + Byte - mixer sample peak meter settings for this line + +--*/ +{ + if (m_pHW) + { + return m_pHW->GetMixerPeakMeter(Index, Channel); + } + + return 0; +} // MixerVolumeRead + +//============================================================================= +#pragma code_seg() +STDMETHODIMP_(void) +CAdapterCommon::PowerChangeState +( + _In_ POWER_STATE NewState +) +/*++ + +Routine Description: + + +Arguments: + + NewState - The requested, new power state for the device. + +Return Value: + + void + +Note: + From MSDN: + + To assist the driver, PortCls will pause any active audio streams prior to calling + this method to place the device in a sleep state. After calling this method, PortCls + will unpause active audio streams, to wake the device up. Miniports can opt for + additional notification by utilizing the IPowerNotify interface. + + The miniport driver must perform the requested change to the device's power state + before it returns from the PowerChangeState call. If the miniport driver needs to + save or restore any device state before a power-state change, the miniport driver + should support the IPowerNotify interface, which allows it to receive advance warning + of any such change. Before returning from a successful PowerChangeState call, the + miniport driver should cache the new power state. + + While the miniport driver is in one of the sleep states (any state other than + PowerDeviceD0), it must avoid writing to the hardware. The miniport driver must cache + any hardware accesses that need to be deferred until the device powers up again. If + the power state is changing from one of the sleep states to PowerDeviceD0, the + miniport driver should perform any deferred hardware accesses after it has powered up + the device. If the power state is changing from PowerDeviceD0 to a sleep state, the + miniport driver can perform any necessary hardware accesses during the PowerChangeState + call before it powers down the device. + + While powered down, a miniport driver is never asked to create a miniport driver object + or stream object. PortCls always places the device in the PowerDeviceD0 state before + calling the miniport driver's NewStream method. + +--*/ +{ + DPF_ENTER(("[CAdapterCommon::PowerChangeState]")); + + // Notify all registered miniports of a power state change + PLIST_ENTRY le = NULL; + for (le = m_SubdeviceCache.Flink; le != &m_SubdeviceCache; le = le->Flink) + { + MINIPAIR_UNKNOWN *pRecord = CONTAINING_RECORD(le, MINIPAIR_UNKNOWN, ListEntry); + + if (pRecord->PowerInterface) + { + pRecord->PowerInterface->PowerChangeState(NewState); + } + } + + // is this actually a state change?? + // + if (NewState.DeviceState != m_PowerState) + { + // switch on new state + // + switch (NewState.DeviceState) + { + case PowerDeviceD0: + case PowerDeviceD1: + case PowerDeviceD2: + case PowerDeviceD3: + m_PowerState = NewState.DeviceState; + + DPF + ( + D_VERBOSE, + ("Entering D%u", ULONG(m_PowerState) - ULONG(PowerDeviceD0)) + ); + + break; + + default: + + DPF(D_VERBOSE, ("Unknown Device Power State")); + break; + } + } +} // PowerStateChange + +//============================================================================= +#pragma code_seg() +STDMETHODIMP_(NTSTATUS) +CAdapterCommon::QueryDeviceCapabilities +( + _Inout_updates_bytes_(sizeof(DEVICE_CAPABILITIES)) PDEVICE_CAPABILITIES PowerDeviceCaps +) +/*++ + +Routine Description: + + Called at startup to get the caps for the device. This structure provides + the system with the mappings between system power state and device power + state. This typically will not need modification by the driver. + +Arguments: + + PowerDeviceCaps - The device's capabilities. + +Return Value: + + NT status code. + +--*/ +{ + UNREFERENCED_PARAMETER(PowerDeviceCaps); + + DPF_ENTER(("[CAdapterCommon::QueryDeviceCapabilities]")); + + return (STATUS_SUCCESS); +} // QueryDeviceCapabilities + +//============================================================================= +#pragma code_seg() +STDMETHODIMP_(NTSTATUS) +CAdapterCommon::QueryPowerChangeState +( + _In_ POWER_STATE NewStateQuery +) +/*++ + +Routine Description: + + Query to see if the device can change to this power state + +Arguments: + + NewStateQuery - The requested, new power state for the device + +Return Value: + + NT status code. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + + DPF_ENTER(("[CAdapterCommon::QueryPowerChangeState]")); + + // query each miniport for it's power state, we're finished if even one indicates + // it cannot go to this power state. + PLIST_ENTRY le = NULL; + for (le = m_SubdeviceCache.Flink; le != &m_SubdeviceCache && NT_SUCCESS(status); le = le->Flink) + { + MINIPAIR_UNKNOWN *pRecord = CONTAINING_RECORD(le, MINIPAIR_UNKNOWN, ListEntry); + + if (pRecord->PowerInterface) + { + status = pRecord->PowerInterface->QueryPowerChangeState(NewStateQuery); + } + } + + return status; +} // QueryPowerChangeState + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CAdapterCommon::CreateAudioInterfaceWithProperties +( + _In_ PCWSTR ReferenceString, + _In_ ULONG cPropertyCount, + _In_reads_(cPropertyCount) const SYSVAD_DEVPROPERTY *pProperties, + _Out_ _At_(AudioSymbolicLinkName->Buffer, __drv_allocatesMem(Mem)) PUNICODE_STRING AudioSymbolicLinkName +) +/*++ + +Routine Description: + +Create the audio interface (in disabled mode). + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::CreateAudioInterfaceWithProperties]")); + + NTSTATUS ntStatus; + UNICODE_STRING referenceString; + + RtlInitUnicodeString(&referenceString, ReferenceString); + + // + // Reset output value. + // + RtlZeroMemory(AudioSymbolicLinkName, sizeof(UNICODE_STRING)); + + // + // Register an audio interface if not already present. + // + ntStatus = IoRegisterDeviceInterface( + GetPhysicalDeviceObject(), + &KSCATEGORY_AUDIO, + &referenceString, + AudioSymbolicLinkName); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("CreateAudioInterfaceWithProperties: IoRegisterDeviceInterface(KSCATEGORY_AUDIO): failed, 0x%x", ntStatus)), + Done); + + // + // Set properties on the interface + // + ntStatus = SysvadIoSetDeviceInterfacePropertyDataMultiple(AudioSymbolicLinkName, cPropertyCount, pProperties); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("CreateAudioInterfaceWithProperties: SysvadIoSetDeviceInterfacePropertyDataMultiple(...): failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + if (!NT_SUCCESS(ntStatus)) + { + RtlFreeUnicodeString(AudioSymbolicLinkName); + RtlZeroMemory(AudioSymbolicLinkName, sizeof(UNICODE_STRING)); + } + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP_(NTSTATUS) +CAdapterCommon::InstallSubdevice +( + _In_opt_ PIRP Irp, + _In_ PWSTR Name, + _In_ REFGUID PortClassId, + _In_ REFGUID MiniportClassId, + _In_opt_ PFNCREATEMINIPORT MiniportCreate, + _In_ ULONG cPropertyCount, + _In_reads_opt_(cPropertyCount) const SYSVAD_DEVPROPERTY * pProperties, + _In_opt_ PVOID DeviceContext, + _In_ PENDPOINT_MINIPAIR MiniportPair, + _In_opt_ PRESOURCELIST ResourceList, + _In_ REFGUID PortInterfaceId, + _Out_opt_ PUNKNOWN * OutPortInterface, + _Out_opt_ PUNKNOWN * OutPortUnknown, + _Out_opt_ PUNKNOWN * OutMiniportUnknown +) +{ +/*++ + +Routine Description: + + This function creates and registers a subdevice consisting of a port + driver, a minport driver and a set of resources bound together. It will + also optionally place a pointer to an interface on the port driver in a + specified location before initializing the port driver. This is done so + that a common ISR can have access to the port driver during + initialization, when the ISR might fire. + +Arguments: + + Irp - pointer to the irp object. + + Name - name of the miniport. Passes to PcRegisterSubDevice + + PortClassId - port class id. Passed to PcNewPort. + + MiniportClassId - miniport class id. Passed to PcNewMiniport. + + MiniportCreate - pointer to a miniport creation function. If NULL, + PcNewMiniport is used. + + DeviceContext - deviceType specific. + + MiniportPair - endpoint configuration info. + + ResourceList - pointer to the resource list. + + PortInterfaceId - GUID that represents the port interface. + + OutPortInterface - pointer to store the port interface + + OutPortUnknown - pointer to store the unknown port interface. + + OutMiniportUnknown - pointer to store the unknown miniport interface + +Return Value: + + NT status code. + +--*/ + PAGED_CODE(); + DPF_ENTER(("[InstallSubDevice %S]", Name)); + + ASSERT(Name != NULL); + ASSERT(m_pDeviceObject != NULL); + + NTSTATUS ntStatus; + PPORT port = NULL; + PUNKNOWN miniport = NULL; + PADAPTERCOMMON adapterCommon = NULL; + UNICODE_STRING symbolicLink = { 0 }; + + adapterCommon = PADAPTERCOMMON(this); + + ntStatus = CreateAudioInterfaceWithProperties(Name, cPropertyCount, pProperties, &symbolicLink); + if (NT_SUCCESS(ntStatus)) + { + // Currently have no use for the symbolic link + RtlFreeUnicodeString(&symbolicLink); + + // Create the port driver object + // + ntStatus = PcNewPort(&port, PortClassId); + } + + // Create the miniport object + // + if (NT_SUCCESS(ntStatus)) + { + if (MiniportCreate) + { + ntStatus = + MiniportCreate + ( + &miniport, + MiniportClassId, + NULL, + NonPagedPoolNx, + adapterCommon, + DeviceContext, + MiniportPair + ); + } + else + { + ntStatus = + PcNewMiniport + ( + (PMINIPORT *) &miniport, + MiniportClassId + ); + } + } + + // Init the port driver and miniport in one go. + // + if (NT_SUCCESS(ntStatus)) + { +#pragma warning(push) + // IPort::Init's annotation on ResourceList requires it to be non-NULL. However, + // for dynamic devices, we may no longer have the resource list and this should + // still succeed. + // +#pragma warning(disable:6387) + ntStatus = + port->Init + ( + m_pDeviceObject, + Irp, + miniport, + adapterCommon, + ResourceList + ); +#pragma warning (pop) + + if (NT_SUCCESS(ntStatus)) + { + // Register the subdevice (port/miniport combination). + // + ntStatus = + PcRegisterSubdevice + ( + m_pDeviceObject, + Name, + port + ); + } + } + + // Deposit the port interfaces if it's needed. + // + if (NT_SUCCESS(ntStatus)) + { + if (OutPortUnknown) + { + ntStatus = + port->QueryInterface + ( + IID_IUnknown, + (PVOID *)OutPortUnknown + ); + } + + if (OutPortInterface) + { + ntStatus = + port->QueryInterface + ( + PortInterfaceId, + (PVOID *) OutPortInterface + ); + } + + if (OutMiniportUnknown) + { + ntStatus = + miniport->QueryInterface + ( + IID_IUnknown, + (PVOID *)OutMiniportUnknown + ); + } + + } + + if (port) + { + port->Release(); + } + + if (miniport) + { + miniport->Release(); + } + + return ntStatus; +} // InstallSubDevice + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP_(NTSTATUS) +CAdapterCommon::UnregisterSubdevice +( + _In_opt_ PUNKNOWN UnknownPort +) +/*++ + +Routine Description: + + Unregisters and releases the specified subdevice. + +Arguments: + + UnknownPort - Wave or topology port interface. + +Return Value: + + NTSTATUS + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::UnregisterSubdevice]")); + + ASSERT(m_pDeviceObject != NULL); + + NTSTATUS ntStatus = STATUS_SUCCESS; + PUNREGISTERSUBDEVICE unregisterSubdevice = NULL; + + if (NULL == UnknownPort) + { + return ntStatus; + } + + // + // Get the IUnregisterSubdevice interface. + // + ntStatus = UnknownPort->QueryInterface( + IID_IUnregisterSubdevice, + (PVOID *)&unregisterSubdevice); + + // + // Unregister the port object. + // + if (NT_SUCCESS(ntStatus)) + { + ntStatus = unregisterSubdevice->UnregisterSubdevice( + m_pDeviceObject, + UnknownPort); + + // + // Release the IUnregisterSubdevice interface. + // + unregisterSubdevice->Release(); + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP_(NTSTATUS) +CAdapterCommon::ConnectTopologies +( + _In_ PUNKNOWN UnknownTopology, + _In_ PUNKNOWN UnknownWave, + _In_ PHYSICALCONNECTIONTABLE* PhysicalConnections, + _In_ ULONG PhysicalConnectionCount +) +/*++ + +Routine Description: + + Connects the bridge pins between the wave and mixer topologies. + +Arguments: + +Return Value: + + NTSTATUS + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::ConnectTopologies]")); + + ASSERT(m_pDeviceObject != NULL); + + NTSTATUS ntStatus = STATUS_SUCCESS; + + // + // register wave <=> topology connections + // This will connect bridge pins of wave and topology + // miniports. + // + for (ULONG i = 0; i < PhysicalConnectionCount && NT_SUCCESS(ntStatus); i++) + { + + switch(PhysicalConnections[i].eType) + { + case CONNECTIONTYPE_TOPOLOGY_OUTPUT: + ntStatus = + PcRegisterPhysicalConnection + ( + m_pDeviceObject, + UnknownTopology, + PhysicalConnections[i].ulTopology, + UnknownWave, + PhysicalConnections[i].ulWave + ); + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_TERSE, ("ConnectTopologies: PcRegisterPhysicalConnection(render) failed, 0x%x", ntStatus)); + } + break; + case CONNECTIONTYPE_WAVE_OUTPUT: + ntStatus = + PcRegisterPhysicalConnection + ( + m_pDeviceObject, + UnknownWave, + PhysicalConnections[i].ulWave, + UnknownTopology, + PhysicalConnections[i].ulTopology + ); + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_TERSE, ("ConnectTopologies: PcRegisterPhysicalConnection(capture) failed, 0x%x", ntStatus)); + } + break; + } + } + + // + // Cleanup in case of error. + // + if (!NT_SUCCESS(ntStatus)) + { + // disconnect all connections on error, ignore error code because not all + // connections may have been made + DisconnectTopologies(UnknownTopology, UnknownWave, PhysicalConnections, PhysicalConnectionCount); + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP_(NTSTATUS) +CAdapterCommon::DisconnectTopologies +( + _In_ PUNKNOWN UnknownTopology, + _In_ PUNKNOWN UnknownWave, + _In_ PHYSICALCONNECTIONTABLE* PhysicalConnections, + _In_ ULONG PhysicalConnectionCount +) +/*++ + +Routine Description: + + Disconnects the bridge pins between the wave and mixer topologies. + +Arguments: + +Return Value: + + NTSTATUS + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::DisconnectTopologies]")); + + ASSERT(m_pDeviceObject != NULL); + + NTSTATUS ntStatus = STATUS_SUCCESS; + NTSTATUS ntStatus2 = STATUS_SUCCESS; + PUNREGISTERPHYSICALCONNECTION unregisterPhysicalConnection = NULL; + + // + // Get the IUnregisterPhysicalConnection interface + // + ntStatus = UnknownTopology->QueryInterface( + IID_IUnregisterPhysicalConnection, + (PVOID *)&unregisterPhysicalConnection); + + if (NT_SUCCESS(ntStatus)) + { + for (ULONG i = 0; i < PhysicalConnectionCount; i++) + { + switch(PhysicalConnections[i].eType) + { + case CONNECTIONTYPE_TOPOLOGY_OUTPUT: + ntStatus = + unregisterPhysicalConnection->UnregisterPhysicalConnection( + m_pDeviceObject, + UnknownTopology, + PhysicalConnections[i].ulTopology, + UnknownWave, + PhysicalConnections[i].ulWave + ); + + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_TERSE, ("DisconnectTopologies: UnregisterPhysicalConnection(render) failed, 0x%x", ntStatus)); + } + break; + case CONNECTIONTYPE_WAVE_OUTPUT: + ntStatus = + unregisterPhysicalConnection->UnregisterPhysicalConnection( + m_pDeviceObject, + UnknownWave, + PhysicalConnections[i].ulWave, + UnknownTopology, + PhysicalConnections[i].ulTopology + ); + if (!NT_SUCCESS(ntStatus2)) + { + DPF(D_TERSE, ("DisconnectTopologies: UnregisterPhysicalConnection(capture) failed, 0x%x", ntStatus2)); + } + break; + } + + // cache and return the first error encountered, as it's likely the most relevent + if (NT_SUCCESS(ntStatus)) + { + ntStatus = ntStatus2; + } + } + } + + // + // Release the IUnregisterPhysicalConnection interface. + // + SAFE_RELEASE(unregisterPhysicalConnection); + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CAdapterCommon::GetCachedSubdevice +( + _In_ PWSTR Name, + _Out_opt_ PUNKNOWN *OutUnknownPort, + _Out_opt_ PUNKNOWN *OutUnknownMiniport +) +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::GetCachedSubdevice]")); + + // search list, return interface to device if found, fail if not found + PLIST_ENTRY le = NULL; + BOOL bFound = FALSE; + + for (le = m_SubdeviceCache.Flink; le != &m_SubdeviceCache && !bFound; le = le->Flink) + { + MINIPAIR_UNKNOWN *pRecord = CONTAINING_RECORD(le, MINIPAIR_UNKNOWN, ListEntry); + + if (0 == wcscmp(Name, pRecord->Name)) + { + if (OutUnknownPort) + { + *OutUnknownPort = pRecord->PortInterface; + (*OutUnknownPort)->AddRef(); + } + + if (OutUnknownMiniport) + { + *OutUnknownMiniport = pRecord->MiniportInterface; + (*OutUnknownMiniport)->AddRef(); + } + + bFound = TRUE; + } + } + + return bFound?STATUS_SUCCESS:STATUS_OBJECT_NAME_NOT_FOUND; +} + + + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CAdapterCommon::CacheSubdevice +( + _In_ PWSTR Name, + _In_ PUNKNOWN UnknownPort, + _In_ PUNKNOWN UnknownMiniport +) +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::CacheSubdevice]")); + + // add the item with this name/interface to the list + NTSTATUS ntStatus = STATUS_SUCCESS; + MINIPAIR_UNKNOWN *pNewSubdevice = NULL; + + pNewSubdevice = new(NonPagedPoolNx, MINADAPTER_POOLTAG) MINIPAIR_UNKNOWN; + + if (!pNewSubdevice) + { + DPF(D_TERSE, ("Insufficient memory to cache subdevice")); + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + } + + if (NT_SUCCESS(ntStatus)) + { + memset(pNewSubdevice, 0, sizeof(MINIPAIR_UNKNOWN)); + + ntStatus = RtlStringCchCopyW(pNewSubdevice->Name, SIZEOF_ARRAY(pNewSubdevice->Name), Name); + } + + if (NT_SUCCESS(ntStatus)) + { + pNewSubdevice->PortInterface = UnknownPort; + pNewSubdevice->PortInterface->AddRef(); + + pNewSubdevice->MiniportInterface = UnknownMiniport; + pNewSubdevice->MiniportInterface->AddRef(); + + // cache the IAdapterPowerManagement interface (if available) from the filter. Some endpoints, + // like FM and cellular, have their own power requirements that we must track. If this fails, + // it just means this filter doesn't do power management. + UnknownMiniport->QueryInterface(IID_IAdapterPowerManagement, (PVOID *)&(pNewSubdevice->PowerInterface)); + + InsertTailList(&m_SubdeviceCache, &pNewSubdevice->ListEntry); + } + + if (!NT_SUCCESS(ntStatus)) + { + if (pNewSubdevice) + { + delete pNewSubdevice; + } + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CAdapterCommon::RemoveCachedSubdevice +( + _In_ PWSTR Name +) +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::RemoveCachedSubdevice]")); + + // search list, remove the entry from the list + + PLIST_ENTRY le = NULL; + BOOL bRemoved = FALSE; + + for (le = m_SubdeviceCache.Flink; le != &m_SubdeviceCache && !bRemoved; le = le->Flink) + { + MINIPAIR_UNKNOWN *pRecord = CONTAINING_RECORD(le, MINIPAIR_UNKNOWN, ListEntry); + + if (0 == wcscmp(Name, pRecord->Name)) + { + SAFE_RELEASE(pRecord->PortInterface); + SAFE_RELEASE(pRecord->MiniportInterface); + SAFE_RELEASE(pRecord->PowerInterface); + memset(pRecord->Name, 0, sizeof(pRecord->Name)); + RemoveEntryList(le); + bRemoved = TRUE; + delete pRecord; + break; + } + } + + return bRemoved?STATUS_SUCCESS:STATUS_OBJECT_NAME_NOT_FOUND; +} + +#pragma code_seg("PAGE") +VOID +CAdapterCommon::EmptySubdeviceCache() +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::EmptySubdeviceCache]")); + + while (!IsListEmpty(&m_SubdeviceCache)) + { + PLIST_ENTRY le = RemoveHeadList(&m_SubdeviceCache); + MINIPAIR_UNKNOWN *pRecord = CONTAINING_RECORD(le, MINIPAIR_UNKNOWN, ListEntry); + + SAFE_RELEASE(pRecord->PortInterface); + SAFE_RELEASE(pRecord->MiniportInterface); + SAFE_RELEASE(pRecord->PowerInterface); + memset(pRecord->Name, 0, sizeof(pRecord->Name)); + + delete pRecord; + } +} + +#pragma code_seg("PAGE") +VOID +CAdapterCommon::Cleanup() +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::Cleanup]")); + +#ifdef SYSVAD_BTH_BYPASS + // + // This ensures Bluetooth HFP notifications are turned off when port class + // cleanups and unregisters the static subdevices. + // + CleanupBthScoBypass(); +#endif // SYSVAD_BTH_BYPASS + + EmptySubdeviceCache(); +} + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP_(NTSTATUS) +CAdapterCommon::InstallEndpointFilters +( + _In_opt_ PIRP Irp, + _In_ PENDPOINT_MINIPAIR MiniportPair, + _In_opt_ PVOID DeviceContext, + _Out_opt_ PUNKNOWN * UnknownTopology, + _Out_opt_ PUNKNOWN * UnknownWave +) +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::InstallEndpointFilters]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + PUNKNOWN unknownTopology = NULL; + PUNKNOWN unknownWave = NULL; + BOOL bTopologyCreated = FALSE; + BOOL bWaveCreated = FALSE; + + if (UnknownTopology) + { + *UnknownTopology = NULL; + } + + if (UnknownWave) + { + *UnknownWave = NULL; + } + + ntStatus = GetCachedSubdevice(MiniportPair->TopoName, &unknownTopology, NULL); + if (!NT_SUCCESS(ntStatus) || NULL == unknownTopology) + { + PUNKNOWN unknownMiniportTopology = NULL; + + bTopologyCreated = TRUE; + + // Install SYSVAD topology miniport for the render endpoint. + // + ntStatus = InstallSubdevice(Irp, + MiniportPair->TopoName, // make sure this name matches with SYSVAD..szPname in the inf's [Strings] section + CLSID_PortTopology, + CLSID_PortTopology, + MiniportPair->TopoCreateCallback, + MiniportPair->TopoInterfacePropertyCount, + MiniportPair->TopoInterfaceProperties, + DeviceContext, + MiniportPair, + NULL, + IID_IPortTopology, + NULL, + &unknownTopology, + &unknownMiniportTopology + ); + if (NT_SUCCESS(ntStatus)) + { + ntStatus = CacheSubdevice(MiniportPair->TopoName, unknownTopology, unknownMiniportTopology); + } + + SAFE_RELEASE(unknownMiniportTopology); + } + + ntStatus = GetCachedSubdevice(MiniportPair->WaveName, &unknownWave, NULL); + if (!NT_SUCCESS(ntStatus) || NULL == unknownWave) + { + PUNKNOWN unknownMiniportWave = NULL; + + bWaveCreated = TRUE; + + // Install SYSVAD wave miniport for the render endpoint. + // + ntStatus = InstallSubdevice(Irp, + MiniportPair->WaveName, // make sure this name matches with SYSVAD..szPname in the inf's [Strings] section + CLSID_PortWaveRT, + CLSID_PortWaveRT, + MiniportPair->WaveCreateCallback, + MiniportPair->WaveInterfacePropertyCount, + MiniportPair->WaveInterfaceProperties, + DeviceContext, + MiniportPair, + NULL, + IID_IPortWaveRT, + NULL, + &unknownWave, + &unknownMiniportWave + ); + + if (NT_SUCCESS(ntStatus)) + { + ntStatus = CacheSubdevice(MiniportPair->WaveName, unknownWave, unknownMiniportWave); + } + + SAFE_RELEASE(unknownMiniportWave); + } + + if (unknownTopology && unknownWave) + { + // + // register wave <=> topology connections + // This will connect bridge pins of wave and topology + // miniports. + // + ntStatus = ConnectTopologies( + unknownTopology, + unknownWave, + MiniportPair->PhysicalConnections, + MiniportPair->PhysicalConnectionCount); + } + + if (NT_SUCCESS(ntStatus)) + { + // + // Set output parameters. + // + if (UnknownTopology != NULL && unknownTopology != NULL) + { + unknownTopology->AddRef(); + *UnknownTopology = unknownTopology; + } + + if (UnknownWave != NULL && unknownWave != NULL) + { + unknownWave->AddRef(); + *UnknownWave = unknownWave; + } + } + else + { + if (bTopologyCreated && unknownTopology != NULL) + { + UnregisterSubdevice(unknownTopology); + RemoveCachedSubdevice(MiniportPair->TopoName); + } + + if (bWaveCreated && unknownWave != NULL) + { + UnregisterSubdevice(unknownWave); + RemoveCachedSubdevice(MiniportPair->WaveName); + } + } + + SAFE_RELEASE(unknownTopology); + SAFE_RELEASE(unknownWave); + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP_(NTSTATUS) +CAdapterCommon::RemoveEndpointFilters +( + _In_ PENDPOINT_MINIPAIR MiniportPair, + _In_opt_ PUNKNOWN UnknownTopology, + _In_opt_ PUNKNOWN UnknownWave +) +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::RemoveEndpointFilters]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + + if (UnknownTopology != NULL && UnknownWave != NULL) + { + ntStatus = DisconnectTopologies( + UnknownTopology, + UnknownWave, + MiniportPair->PhysicalConnections, + MiniportPair->PhysicalConnectionCount); + + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_VERBOSE, ("RemoveEndpointFilters: DisconnectTopologies failed: 0x%x", ntStatus)); + } + } + + + RemoveCachedSubdevice(MiniportPair->WaveName); + + ntStatus = UnregisterSubdevice(UnknownWave); + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_VERBOSE, ("RemoveEndpointFilters: UnregisterSubdevice(wave) failed: 0x%x", ntStatus)); + } + + RemoveCachedSubdevice(MiniportPair->TopoName); + + ntStatus = UnregisterSubdevice(UnknownTopology); + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_VERBOSE, ("RemoveEndpointFilters: UnregisterSubdevice(topology) failed: 0x%x", ntStatus)); + } + + // + // All Done. + // + ntStatus = STATUS_SUCCESS; + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP_(NTSTATUS) +CAdapterCommon::GetFilters +( + _In_ PENDPOINT_MINIPAIR MiniportPair, + _Out_opt_ PUNKNOWN * UnknownTopologyPort, + _Out_opt_ PUNKNOWN * UnknownTopologyMiniport, + _Out_opt_ PUNKNOWN * UnknownWavePort, + _Out_opt_ PUNKNOWN * UnknownWaveMiniport +) +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::GetFilters]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + PUNKNOWN unknownTopologyPort = NULL; + PUNKNOWN unknownTopologyMiniport = NULL; + PUNKNOWN unknownWavePort = NULL; + PUNKNOWN unknownWaveMiniport = NULL; + + // if the client requested the topology filter, find it and return it + if (UnknownTopologyPort != NULL || UnknownTopologyMiniport != NULL) + { + ntStatus = GetCachedSubdevice(MiniportPair->TopoName, &unknownTopologyPort, &unknownTopologyMiniport); + if (NT_SUCCESS(ntStatus)) + { + if (UnknownTopologyPort) + { + *UnknownTopologyPort = unknownTopologyPort; + } + + if (UnknownTopologyMiniport) + { + *UnknownTopologyMiniport = unknownTopologyMiniport; + } + } + } + + // if the client requested the wave filter, find it and return it + if (NT_SUCCESS(ntStatus) && (UnknownWavePort != NULL || UnknownWaveMiniport != NULL)) + { + ntStatus = GetCachedSubdevice(MiniportPair->WaveName, &unknownWavePort, &unknownWaveMiniport); + if (NT_SUCCESS(ntStatus)) + { + if (UnknownWavePort) + { + *UnknownWavePort = unknownWavePort; + } + + if (UnknownWaveMiniport) + { + *UnknownWaveMiniport = unknownWaveMiniport; + } + } + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP_(NTSTATUS) +CAdapterCommon::SetIdlePowerManagement +( + _In_ PENDPOINT_MINIPAIR MiniportPair, + _In_ BOOL bEnabled +) +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::SetIdlePowerManagement]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + IUnknown *pUnknown = NULL; + PPORTCLSPOWER pPortClsPower = NULL; + // refcounting disable requests. Each miniport is responsible for calling this in pairs, + // disable on the first request to disable, enable on the last request to enable. + + // make sure that we always call SetIdlePowerManagment using the IPortClsPower + // from the requesting port, so we don't cache a reference to a port + // indefinitely, preventing it from ever unloading. + ntStatus = GetFilters(MiniportPair, NULL, NULL, &pUnknown, NULL); + if (NT_SUCCESS(ntStatus)) + { + ntStatus = + pUnknown->QueryInterface + ( + IID_IPortClsPower, + (PVOID*) &pPortClsPower + ); + } + + if (NT_SUCCESS(ntStatus)) + { + if (bEnabled) + { + m_dwIdleRequests--; + + if (0 == m_dwIdleRequests) + { + pPortClsPower->SetIdlePowerManagement(m_pDeviceObject, TRUE); + } + } + else + { + if (0 == m_dwIdleRequests) + { + pPortClsPower->SetIdlePowerManagement(m_pDeviceObject, FALSE); + } + + m_dwIdleRequests++; + } + } + + SAFE_RELEASE(pUnknown); + SAFE_RELEASE(pPortClsPower); + + return ntStatus; +} + +#ifdef SYSVAD_BTH_BYPASS +// +// CAdapterCommon Bluetooth Hands-Free Profile function implementation. +// + +//============================================================================= +#pragma code_seg("PAGE") +VOID +CAdapterCommon::EvtBthHfpScoBypassInterfaceWorkItem +( + _In_ WDFWORKITEM WorkItem +) +/*++ + +Routine Description: + + The function handles the arrival or removal of a HFP SCO Bypass interface. + +Arguments: + + WorkItem - WDF work-item object. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[EvtBthHfpScoBypassInterfaceWorkItem]")); + + CAdapterCommon * This; + + if (WorkItem == NULL) + { + return; + } + + This = GetBthHfpWorkItemContext(WorkItem)->Adapter; + ASSERT(This != NULL); + + for (;;) + { + PLIST_ENTRY le = NULL; + BthHfpWorkTask * task = NULL; + + // + // Retrieve a taask. + // + ExAcquireFastMutex(&This->m_BthHfpFastMutex); + if (!IsListEmpty(&This->m_BthHfpWorkTasks)) + { + le = RemoveHeadList(&This->m_BthHfpWorkTasks); + task = CONTAINING_RECORD(le, BthHfpWorkTask, ListEntry); + InitializeListHead(le); + } + ExReleaseFastMutex(&This->m_BthHfpFastMutex); + + if (task == NULL) + { + break; + } + + ASSERT(task->Device != NULL); + _Analysis_assume_(task->Device != NULL); + + // + // Process the task. + // + switch(task->Action) + { + case eBthHfpTaskStart: + task->Device->Start(); + break; + + case eBthHfpTaskStop: + task->Device->Stop(); + break; + + default: + DPF(D_ERROR, ("EvtBthHfpScoBypassInterfaceWorkItem: invalid action %d", task->Action)); + break; + } + + // + // Release the ref we took on the device when we inserted the task in the queue. + // For a stop operation this may be the last reference. + // + SAFE_RELEASE(task->Device); + + // + // Free the task. + // + ExFreeToNPagedLookasideList(&This->m_BhtHfpWorkTaskPool, task); + } +} + +//============================================================================= +#pragma code_seg("PAGE") +BthHfpDevice * +CAdapterCommon::BthHfpDeviceFind +( + _In_ PUNICODE_STRING SymbolicLinkName +) +/*++ + +Routine Description: + + The function looks for the specified device in the adapter's list. + +Arguments: + + SymbolicLinkName - interface's symbolic link. + +Return Value: + + BthHfpDevice pointer or NULL. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::BthHfpDeviceFind]")); + + PLIST_ENTRY le = NULL; + BthHfpDevice * bthDevice = NULL; + + ExAcquireFastMutex(&m_BthHfpFastMutex); + + for (le = m_BthHfpDevices.Flink; le != &m_BthHfpDevices; le = le->Flink) + { + BthHfpDevice * tmpBthDevice = BthHfpDevice::GetBthHfpDevice(le); + ASSERT(tmpBthDevice != NULL); + + PUNICODE_STRING unicodeStr = tmpBthDevice->GetSymbolicLinkName(); + ASSERT(unicodeStr != NULL); + + if (unicodeStr->Length == SymbolicLinkName->Length && + 0 == wcsncmp(unicodeStr->Buffer, SymbolicLinkName->Buffer, unicodeStr->Length/sizeof(WCHAR))) + { + // Found it! + bthDevice = tmpBthDevice; + bthDevice->AddRef(); + break; + } + } + + ExReleaseFastMutex(&m_BthHfpFastMutex); + + return bthDevice; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CAdapterCommon::BthHfpScoInterfaceArrival +( + _In_ PUNICODE_STRING SymbolicLinkName +) +/*++ + +Routine Description: + + The function handles the arrival of a new HFP SCO Bypass interface. + +Arguments: + + SymbolicLinkName - new interface's symbolic link. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::BthHfpScoInterfaceArrival]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + BthHfpDevice * bthDevice = NULL; + BthHfpWorkTask * bthWorkTask = NULL; + + DPF(D_VERBOSE, ("BthHfpScoInterfaceArrival: SymbolicLinkName %wZ", SymbolicLinkName)); + + // + // Check if the Bluetooth device is already present. + // According to the docs it is possible to receive two notifications for the same + // interface. + // + bthDevice = BthHfpDeviceFind(SymbolicLinkName); + if (bthDevice != NULL) + { + DPF(D_VERBOSE, ("BthHfpScoInterfaceArrival: Bluetooth HFP device already present")); + SAFE_RELEASE(bthDevice); + ntStatus = STATUS_SUCCESS; + goto Done; + } + + // + // Alloc a new structure for this Bluetooth hands-free device. + // + bthDevice = new (NonPagedPoolNx, MINADAPTER_POOLTAG) BthHfpDevice(NULL); // NULL -> OuterUnknown + if (NULL == bthDevice) + { + DPF(D_ERROR, ("BthHfpScoInterfaceArrival: unable to allocate BthHfpDevice, out of memory")); + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + goto Done; + } + + DPF(D_VERBOSE, ("BthHfpScoInterfaceArrival: created BthHfpDevice 0x%p ", bthDevice)); + + // + // Basic initialization of the Bluetooth Hands-Free Profile interface. + // The audio miniport creation is done later by the BthHfpDevice.Start() + // which is invoked asynchronously by a worker thread. + // BthHfpDevice->Init() must be invoked just after the creation of the object. + // + ntStatus = bthDevice->Init(this, SymbolicLinkName); + IF_FAILED_JUMP(ntStatus, Done); + + // + // Get and init a work task. + // + bthWorkTask = (BthHfpWorkTask*)ExAllocateFromNPagedLookasideList(&m_BhtHfpWorkTaskPool); + if (NULL == bthWorkTask) + { + DPF(D_ERROR, ("BthHfpScoInterfaceArrival: unable to allocate BthHfpWorkTask, out of memory")); + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + goto Done; + } + + RtlZeroMemory(bthWorkTask, sizeof(*bthWorkTask)); + bthWorkTask->Action = eBthHfpTaskStart; + InitializeListHead(&bthWorkTask->ListEntry); + // Note that bthDevice has one reference at this point. + bthWorkTask->Device = bthDevice; + + ExAcquireFastMutex(&m_BthHfpFastMutex); + + // + // Insert this new Bluetooth HFP device in our list. + // + InsertTailList(&m_BthHfpDevices, bthDevice->GetListEntry()); + + // + // Add a new task for the worker thread. + // + InsertTailList(&m_BthHfpWorkTasks, &bthWorkTask->ListEntry); + bthDevice->AddRef(); // released when task runs. + + // + // Schedule a work-item if not already running. + // + WdfWorkItemEnqueue(m_BthHfpWorkItem); + + ExReleaseFastMutex(&m_BthHfpFastMutex); + +Done: + if (!NT_SUCCESS(ntStatus)) + { + // Release the last ref, this will delete the BthHfpDevice + SAFE_RELEASE(bthDevice); + + if (bthWorkTask != NULL) + { + ExFreeToNPagedLookasideList(&m_BhtHfpWorkTaskPool, bthWorkTask); + bthWorkTask = NULL; + } + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CAdapterCommon::BthHfpScoInterfaceRemoval +( + _In_ PUNICODE_STRING SymbolicLinkName +) +/*++ + +Routine Description: + + The function handles the removal of a HFP SCO Bypass interface. + +Arguments: + + SymbolicLinkName - interface's symbolic link to remove. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::BthHfpScoInterfaceRemoval]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + BthHfpDevice * bthDevice = NULL; + BthHfpWorkTask * bthWorkTask = NULL; + + DPF(D_VERBOSE, ("BthHfpScoInterfaceRemoval: SymbolicLinkName %wZ", SymbolicLinkName)); + + // + // Check if the Bluetooth device is present. + // + bthDevice = BthHfpDeviceFind(SymbolicLinkName); + if (bthDevice == NULL) + { + // This can happen if the init/start of the BthHfpDevice failed. + DPF(D_VERBOSE, ("BthHfpScoInterfaceRemoval: Bluetooth HFP device not found")); + ntStatus = STATUS_SUCCESS; + goto Done; + } + + // + // Init a work task. + // + bthWorkTask = (BthHfpWorkTask*)ExAllocateFromNPagedLookasideList(&m_BhtHfpWorkTaskPool); + if (NULL == bthWorkTask) + { + DPF(D_ERROR, ("BthHfpScoInterfaceRemoval: unable to allocate BthHfpWorkTask, out of memory")); + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + goto Done; + } + + RtlZeroMemory(bthWorkTask, sizeof(*bthWorkTask)); + bthWorkTask->Action = eBthHfpTaskStop; + InitializeListHead(&bthWorkTask->ListEntry); + // Work-item callback will release the reference we got above from BthHfpDeviceFind. + bthWorkTask->Device = bthDevice; + + ExAcquireFastMutex(&m_BthHfpFastMutex); + + // + // Remove this Bluetooth device from our list and release the associated reference. + // + RemoveEntryList(bthDevice->GetListEntry()); + InitializeListHead(bthDevice->GetListEntry()); + bthDevice->Release(); // This is not the last ref. + + // + // Add a new task for the worker thread. + // + InsertTailList(&m_BthHfpWorkTasks, &bthWorkTask->ListEntry); + + // + // Schedule a work-item if not already running. + // + WdfWorkItemEnqueue(m_BthHfpWorkItem); + + ExReleaseFastMutex(&m_BthHfpFastMutex); + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + if (!NT_SUCCESS(ntStatus)) + { + // Release the ref we got in find. + SAFE_RELEASE(bthDevice); + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CAdapterCommon::EvtBthHfpScoBypassInterfaceChange( + _In_ PVOID NotificationPointer, + _Inout_opt_ PVOID Context + ) +/*++ + +Routine Description: + + This callback is invoked when a new HFP SCO Bypass interface is added or removed. + +Arguments: + NotificationPointer - Interface change notification + Context - CAdapterCommon ptr. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[EvtBthHfpScoBypassInterfaceChange]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + CAdapterCommon * This = NULL; + PDEVICE_INTERFACE_CHANGE_NOTIFICATION Notification = (PDEVICE_INTERFACE_CHANGE_NOTIFICATION) NotificationPointer; + + // + // Make sure this is the interface class we extect. Any other class guid + // is an error, but let it go since it is not fatal to the machine. + // + if (!IsEqualGUID(Notification->InterfaceClassGuid, GUID_DEVINTERFACE_BLUETOOTH_HFP_SCO_HCIBYPASS)) + { + DPF(D_VERBOSE, ("EvtBthHfpScoBypassInterfaceChange: bad interface ClassGuid")); + ASSERTMSG("EvtBthHfpScoBypassInterfaceChange: bad interface ClassGuid ", FALSE); + + goto Done; + } + + This = (CAdapterCommon *)Context; + ASSERT(This != NULL); + _Analysis_assume_(This != NULL); + + // + // Take action based on the event. Any other event type is an error, + // but let it go since it is not fatal to the machine. + // + if (IsEqualGUID(Notification->Event, GUID_DEVICE_INTERFACE_ARRIVAL)) + { + ntStatus = This->BthHfpScoInterfaceArrival(Notification->SymbolicLinkName); + } + else if (IsEqualGUID(Notification->Event, GUID_DEVICE_INTERFACE_REMOVAL)) + { + ntStatus = This->BthHfpScoInterfaceRemoval(Notification->SymbolicLinkName); + } + else + { + DPF(D_VERBOSE, ("EvtBthHfpScoBypassInterfaceChange: bad " + "GUID_DEVINTERFACE_BLUETOOTH_HFP_SCO_HCIBYPASS event")); + ASSERTMSG("EvtBthHfpScoBypassInterfaceChange: bad " + "GUID_DEVINTERFACE_BLUETOOTH_HFP_SCO_HCIBYPASS event ", FALSE); + + goto Done; + } + +Done: + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +CAdapterCommon::InitBthScoBypass() +/*++ + +Routine Description: + + Initialize the bluetooth bypass environment. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::InitBluetoothBypass]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + WDF_WORKITEM_CONFIG wiConfig; + WDF_OBJECT_ATTRIBUTES attributes; + BthHfpWorkItemContext * wiContext; + + // + // Init spin-lock, linked lists, work-item, event, etc. + // Init all members to default values. This basic init should not fail. + // + m_BthHfpWorkItem = NULL; + m_BthHfpScoNotificationHandle = NULL; + ExInitializeFastMutex(&m_BthHfpFastMutex); + InitializeListHead(&m_BthHfpWorkTasks); + InitializeListHead(&m_BthHfpDevices); + m_BhtHfpWorkTaskPoolElementSize = sizeof(BthHfpWorkTask); + ExInitializeNPagedLookasideList(&m_BhtHfpWorkTaskPool, + NULL, + NULL, + POOL_NX_ALLOCATION, + m_BhtHfpWorkTaskPoolElementSize, + MINADAPTER_POOLTAG, + 0); + // + // Enable Bluetooth HFP SCO-Bypass Cleanup. + // Do any allocation/initialization that can fail after this point. + // + m_BthHfpEnableCleanup = TRUE; + + // + // Allocate a WDF work-item. + // + WDF_WORKITEM_CONFIG_INIT(&wiConfig, EvtBthHfpScoBypassInterfaceWorkItem); + wiConfig.AutomaticSerialization = FALSE; + + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, BthHfpWorkItemContext); + attributes.ParentObject = GetWdfDevice(); + ntStatus = WdfWorkItemCreate( &wiConfig, + &attributes, + &m_BthHfpWorkItem); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("InitBthScoBypass: WdfWorkItemCreate failed: 0x%x", ntStatus)), + Done); + + wiContext = GetBthHfpWorkItemContext(m_BthHfpWorkItem); + wiContext->Adapter = this; // weak ref. + + // + // Register for bluetooth heandsfree profile interface changes. + // + ntStatus = IoRegisterPlugPlayNotification ( + EventCategoryDeviceInterfaceChange, + PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES, + (PVOID)&GUID_DEVINTERFACE_BLUETOOTH_HFP_SCO_HCIBYPASS, + m_pDeviceObject->DriverObject, + EvtBthHfpScoBypassInterfaceChange, + (PVOID)this, + &m_BthHfpScoNotificationHandle); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("InitBthScoBypass: IoRegisterPlugPlayNotification(GUID_DEVINTERFACE_BLUETOOTH_HFP_SCO_HCIBYPASS) failed: 0x%x", ntStatus)), + Done); + + // + // Initialization completed. + // + ntStatus = STATUS_SUCCESS; + +Done: + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +VOID +CAdapterCommon::CleanupBthScoBypass() +/*++ + +Routine Description: + + Cleanup the bluetooth bypass environment. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[CAdapterCommon::CleanupBthScoBypass]")); + + // + // Do nothing if Bluetooth HFP environment was not correctly initialized. + // + if (m_BthHfpEnableCleanup == FALSE) + { + return; + } + + // + // Unregister for bluetooth heandsfree profile interface changes. + // + if (m_BthHfpScoNotificationHandle != NULL) + { + (void)IoUnregisterPlugPlayNotificationEx(m_BthHfpScoNotificationHandle); + m_BthHfpScoNotificationHandle = NULL; + } + + // + // Wait for the Bluetooth hands-free profile worker thread to be done. + // + if (m_BthHfpWorkItem != NULL) + { + WdfWorkItemFlush(m_BthHfpWorkItem); + WdfObjectDelete(m_BthHfpWorkItem); + m_BthHfpWorkItem = NULL; + } + + ASSERT(IsListEmpty(&m_BthHfpWorkTasks)); + + // + // Stop and delete all BthHfpDevices. We are the only thread accessing this list, + // so there is no need to acquire the mutex. + // + while (!IsListEmpty(&m_BthHfpDevices)) + { + BthHfpDevice * bthDevice = NULL; + PLIST_ENTRY le = NULL; + + le = RemoveHeadList(&m_BthHfpDevices); + + bthDevice = BthHfpDevice::GetBthHfpDevice(le); + InitializeListHead(le); + + // bthDevice is invalid after this call. + bthDevice->Stop(); + + // This should be the last reference. + bthDevice->Release(); + } + + ASSERT(IsListEmpty(&m_BthHfpDevices)); + + // + // General cleanup. + // + ExDeleteNPagedLookasideList(&m_BhtHfpWorkTaskPool); +} +#endif // SYSVAD_BTH_BYPASS + +#ifdef SYSVAD_BTH_BYPASS +// +// BthHfpDevice implementation. +// + +//============================================================================= +#pragma code_seg("PAGE") +STDMETHODIMP +BthHfpDevice::NonDelegatingQueryInterface +( + _In_ REFIID Interface, + _COM_Outptr_ PVOID * Object +) +/*++ + +Routine Description: + + QueryInterface routine for BthHfpDevice + +Arguments: + + Interface - + + Object - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(Object); + + if (IsEqualGUIDAligned(Interface, IID_IUnknown)) + { + *Object = PVOID(PUNKNOWN(PBTHHFPDEVICECOMMON(this))); + } + else if (IsEqualGUIDAligned(Interface, IID_IBthHfpDeviceCommon)) + { + *Object = PVOID(PBTHHFPDEVICECOMMON(this)); + } + else + { + *Object = NULL; + } + + if (*Object) + { + PUNKNOWN(*Object)->AddRef(); + return STATUS_SUCCESS; + } + + return STATUS_INVALID_PARAMETER; +} // NonDelegatingQueryInterface + +//============================================================================= +// Dummy stubs to override the default WDF behavior of closing the target +// on query remove. This driver closes and deletes the supporting objects +// when the target removes the BTH HFP SCO Bypass interface. +// + +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::EvtBthHfpTargetQueryRemove +( + _In_ WDFIOTARGET IoTarget +) +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::EvtBthHfpTargetQueryRemove]")); + + UNREFERENCED_PARAMETER(IoTarget); + return STATUS_SUCCESS; +} + +#pragma code_seg("PAGE") +VOID +BthHfpDevice::EvtBthHfpTargetRemoveCanceled +( + _In_ WDFIOTARGET IoTarget +) +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::EvtBthHfpTargetRemoveCanceled]")); + + UNREFERENCED_PARAMETER(IoTarget); +} +#pragma code_seg("PAGE") +VOID +BthHfpDevice::EvtBthHfpTargetRemoveComplete +( + _In_ WDFIOTARGET IoTarget +) +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::EvtBthHfpTargetRemoveComplete]")); + + UNREFERENCED_PARAMETER(IoTarget); +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::Init +( + _In_ CAdapterCommon * Adapter, + _In_ PUNICODE_STRING SymbolicLinkName +) +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::Init]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + BthHfpDeviceNotificationWorkItemContext *wiCtx = NULL; + BthHfpDeviceNotificationReqContext *reqCtx = NULL; + WDF_OBJECT_ATTRIBUTES attributes; + WDF_IO_TARGET_OPEN_PARAMS openParams; + WDF_WORKITEM_CONFIG wiConfig; + + AddRef(); // first ref. + + // + // Basic init of all the class' members. + // + m_State = eBthHfpStateInitializing; + m_Adapter = Adapter; + + // Static config. + m_WdfIoTarget = NULL; + m_SpeakerMiniports = NULL; + m_MicMiniports = NULL; + m_UnknownSpeakerTopology = NULL; + m_UnknownSpeakerWave = NULL; + m_UnknownMicTopology = NULL; + m_UnknownMicWave = NULL; + m_Descriptor = NULL; + m_VolumePropValues = NULL; + + // Notification updates. + m_SpeakerVolumeLevel = 0; + m_MicVolumeLevel = 0; + m_ConnectionStatusLong = FALSE; + m_StreamStatusLong = STATUS_INVALID_DEVICE_STATE; // Sco stream is not open. + m_NRECDisableStatusLong = FALSE; + + m_StreamReq = NULL; + m_SpeakerVolumeReq = NULL; + m_MicVolumeReq = NULL; + m_ConnectionReq = NULL; + m_NRECDisableStatusReq = NULL; + + m_WorkItem = NULL; + m_ReqCollection = NULL; + + m_nStreams = 0; + + KeInitializeEvent(&m_StreamStatusEvent, NotificationEvent, TRUE); + + InitializeListHead(&m_ListEntry); + KeInitializeSpinLock(&m_Lock); + + RtlZeroMemory(&m_SymbolicLinkName, sizeof(m_SymbolicLinkName)); + + RtlZeroMemory(&m_SpeakerVolumeCallback, sizeof(m_SpeakerVolumeCallback)); + RtlZeroMemory(&m_SpeakerConnectionStatusCallback, sizeof(m_SpeakerConnectionStatusCallback)); + RtlZeroMemory(&m_MicVolumeCallback, sizeof(m_MicVolumeCallback)); + RtlZeroMemory(&m_MicConnectionStatusCallback, sizeof(m_MicConnectionStatusCallback)); + + // + // Allocate a notification WDF work-item. + // + WDF_WORKITEM_CONFIG_INIT(&wiConfig, EvtBthHfpDeviceNotificationStatusWorkItem); + wiConfig.AutomaticSerialization = FALSE; + + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, BthHfpDeviceNotificationWorkItemContext); + attributes.ParentObject = Adapter->GetWdfDevice(); + ntStatus = WdfWorkItemCreate( &wiConfig, + &attributes, + &m_WorkItem); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Init: WdfWorkItemCreate failed: 0x%x", ntStatus)), + Done); + + wiCtx = GetBthHfpDeviceNotificationWorkItemContext(m_WorkItem); + wiCtx->BthHfpDevice = this; // weak ref. + + // + // Allocate a collection to hold notification requests for the notification work-item. + // + WDF_OBJECT_ATTRIBUTES_INIT(&attributes); + attributes.ParentObject = m_Adapter->GetWdfDevice(); + ntStatus = WdfCollectionCreate( + &attributes, + &m_ReqCollection); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Init: WdfCollectionCreate failed: 0x%x", ntStatus)), + Done); + + // + // Open the target interface. + // + WDF_OBJECT_ATTRIBUTES_INIT(&attributes); + attributes.ParentObject = m_Adapter->GetWdfDevice(); + ntStatus = WdfIoTargetCreate(m_Adapter->GetWdfDevice(), + &attributes, + &m_WdfIoTarget); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Init: WdfIoTargetCreate failed: 0x%x", ntStatus)), + Done); + + WDF_IO_TARGET_OPEN_PARAMS_INIT_OPEN_BY_NAME( + &openParams, + SymbolicLinkName, + STANDARD_RIGHTS_ALL); + + openParams.EvtIoTargetQueryRemove = EvtBthHfpTargetQueryRemove; + openParams.EvtIoTargetRemoveCanceled = EvtBthHfpTargetRemoveCanceled; + openParams.EvtIoTargetRemoveComplete = EvtBthHfpTargetRemoveComplete; + + ntStatus = WdfIoTargetOpen(m_WdfIoTarget, &openParams); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Init: WdfIoTargetOpen(%wZ) failed: 0x%x", SymbolicLinkName, ntStatus)), + Done); + + // + // Make a copy of the symbolic link list. + // + m_SymbolicLinkName.MaximumLength = SymbolicLinkName->MaximumLength; + m_SymbolicLinkName.Length = SymbolicLinkName->Length; + m_SymbolicLinkName.Buffer = (PWSTR) ExAllocatePoolWithTag(NonPagedPoolNx, + SymbolicLinkName->MaximumLength, + MINADAPTER_POOLTAG); + if (m_SymbolicLinkName.Buffer == NULL) + { + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + } + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Init: ExAllocatePoolWithTag failed, out of memory")), + Done); + + RtlCopyUnicodeString(&m_SymbolicLinkName, SymbolicLinkName); + + // + // Allocate the WDF requests for status notifications. + // + + // + // IOCTL_BTHHFP_DEVICE_GET_NRECDISABLE_STATUS_UPDATE + // + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, BthHfpDeviceNotificationReqContext); + attributes.ParentObject = m_Adapter->GetWdfDevice(); + ntStatus = WdfRequestCreate( + &attributes, + m_WdfIoTarget, + &m_NRECDisableStatusReq); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Init: WdfRequestCreate(Nrec-disable status) failed, 0x%x", ntStatus)), + Done); + + // Init context. + reqCtx = GetBthHfpDeviceNotificationReqContext(m_NRECDisableStatusReq); + reqCtx->BthHfpDevice = this; // weak ref. + + ntStatus = WdfMemoryCreatePreallocated( + WDF_NO_OBJECT_ATTRIBUTES, + &reqCtx->Buffer, + sizeof(reqCtx->Buffer.bImmediate), + &reqCtx->MemIn); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Init: WdfMemoryCreatePreallocated failed, 0x%x", ntStatus)), + Done); + + ntStatus = WdfMemoryCreatePreallocated( + WDF_NO_OBJECT_ATTRIBUTES, + &reqCtx->Buffer, + sizeof(reqCtx->Buffer.BoolStatus), + &reqCtx->MemOut); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Init: WdfMemoryCreatePreallocated failed, 0x%x", ntStatus)), + Done); + + // + // IOCTL_BTHHFP_SPEAKER_GET_VOLUME_STATUS_UPDATE + // + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, BthHfpDeviceNotificationReqContext); + attributes.ParentObject = m_Adapter->GetWdfDevice(); + ntStatus = WdfRequestCreate( + &attributes, + m_WdfIoTarget, + &m_SpeakerVolumeReq); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Init: WdfRequestCreate(Speaker-Volume) failed, 0x%x", ntStatus)), + Done); + + // Init context. + reqCtx = GetBthHfpDeviceNotificationReqContext(m_SpeakerVolumeReq); + reqCtx->BthHfpDevice = this; // weak ref. + + ntStatus = WdfMemoryCreatePreallocated( + WDF_NO_OBJECT_ATTRIBUTES, + &reqCtx->Buffer, + sizeof(reqCtx->Buffer.bImmediate), + &reqCtx->MemIn); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Init: WdfMemoryCreatePreallocated failed, 0x%x", ntStatus)), + Done); + + ntStatus = WdfMemoryCreatePreallocated( + WDF_NO_OBJECT_ATTRIBUTES, + &reqCtx->Buffer, + sizeof(reqCtx->Buffer.Volume), + &reqCtx->MemOut); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Init: WdfMemoryCreatePreallocated failed, 0x%x", ntStatus)), + Done); + + // + // IOCTL_BTHHFP_MIC_GET_VOLUME_STATUS_UPDATE + // + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, BthHfpDeviceNotificationReqContext); + attributes.ParentObject = m_Adapter->GetWdfDevice(); + ntStatus = WdfRequestCreate( + &attributes, + m_WdfIoTarget, + &m_MicVolumeReq); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Init: WdfRequestCreate(Mic-Volume) failed, 0x%x", ntStatus)), + Done); + + // Init context. + reqCtx = GetBthHfpDeviceNotificationReqContext(m_MicVolumeReq); + reqCtx->BthHfpDevice = this; // weak ref. + + ntStatus = WdfMemoryCreatePreallocated( + WDF_NO_OBJECT_ATTRIBUTES, + &reqCtx->Buffer, + sizeof(reqCtx->Buffer.bImmediate), + &reqCtx->MemIn); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Init: WdfMemoryCreatePreallocated failed, 0x%x", ntStatus)), + Done); + + ntStatus = WdfMemoryCreatePreallocated( + WDF_NO_OBJECT_ATTRIBUTES, + &reqCtx->Buffer, + sizeof(reqCtx->Buffer.Volume), + &reqCtx->MemOut); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Init: WdfMemoryCreatePreallocated failed, 0x%x", ntStatus)), + Done); + + // + // IOCTL_BTHHFP_DEVICE_GET_CONNECTION_STATUS_UPDATE + // + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, BthHfpDeviceNotificationReqContext); + attributes.ParentObject = m_Adapter->GetWdfDevice(); + ntStatus = WdfRequestCreate( + &attributes, + m_WdfIoTarget, + &m_ConnectionReq); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Init: WdfRequestCreate(Connection-Status) failed, 0x%x", ntStatus)), + Done); + + // Init context. + reqCtx = GetBthHfpDeviceNotificationReqContext(m_ConnectionReq); + reqCtx->BthHfpDevice = this; // weak ref. + + ntStatus = WdfMemoryCreatePreallocated( + WDF_NO_OBJECT_ATTRIBUTES, + &reqCtx->Buffer, + sizeof(reqCtx->Buffer.bImmediate), + &reqCtx->MemIn); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Init: WdfMemoryCreatePreallocated failed, 0x%x", ntStatus)), + Done); + + ntStatus = WdfMemoryCreatePreallocated( + WDF_NO_OBJECT_ATTRIBUTES, + &reqCtx->Buffer, + sizeof(reqCtx->Buffer.BoolStatus), + &reqCtx->MemOut); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Init: WdfMemoryCreatePreallocated failed, 0x%x", ntStatus)), + Done); + + // + // IOCTL_BTHHFP_STREAM_GET_STATUS_UPDATE + // + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, BthHfpDeviceNotificationReqContext); + attributes.ParentObject = m_Adapter->GetWdfDevice(); + ntStatus = WdfRequestCreate( + &attributes, + m_WdfIoTarget, + &m_StreamReq); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Init: WdfRequestCreate(Stream-Status) failed, 0x%x", ntStatus)), + Done); + + // Init context. + reqCtx = GetBthHfpDeviceNotificationReqContext(m_StreamReq); + reqCtx->BthHfpDevice = this; // weak ref. + + ntStatus = WdfMemoryCreatePreallocated( + WDF_NO_OBJECT_ATTRIBUTES, + &reqCtx->Buffer, + sizeof(reqCtx->Buffer.bImmediate), + &reqCtx->MemIn); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Init: WdfMemoryCreatePreallocated failed, 0x%x", ntStatus)), + Done); + + ntStatus = WdfMemoryCreatePreallocated( + WDF_NO_OBJECT_ATTRIBUTES, + &reqCtx->Buffer, + sizeof(reqCtx->Buffer.NtStatus), + &reqCtx->MemOut); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Init: WdfMemoryCreatePreallocated failed, 0x%x", ntStatus)), + Done); + + // + // This remote device is now in running state. No need to use interlock operations + // b/c at this time this is the only thread accessing this info. + // + m_State = eBthHfpStateRunning; + + // + // Init successful. + // + ntStatus = STATUS_SUCCESS; + +Done: + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +BthHfpDevice::~BthHfpDevice +( + void +) +/*++ + +Routine Description: + + Destructor for BthHfpDevice. + +Arguments: + +Return Value: + + void + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::~BthHfpDevice]")); + + ASSERT(m_State != eBthHfpStateRunning); + ASSERT(IsListEmpty(&m_ListEntry)); + + // + // Release ref to remote stack. + // + if (m_WdfIoTarget != NULL) + { + WdfObjectDelete(m_WdfIoTarget); + m_WdfIoTarget = NULL; + } + + // + // Free symbolic links. + // + if (m_SymbolicLinkName.Buffer != NULL) + { + ExFreePoolWithTag(m_SymbolicLinkName.Buffer, MINADAPTER_POOLTAG); + RtlZeroMemory(&m_SymbolicLinkName, sizeof(m_SymbolicLinkName)); + } + + DeleteCustomEndpointMinipair(m_SpeakerMiniports); + m_SpeakerMiniports = NULL; + + DeleteCustomEndpointMinipair(m_MicMiniports); + m_MicMiniports = NULL; + + if (m_Descriptor != NULL) + { + ExFreePoolWithTag(m_Descriptor, MINADAPTER_POOLTAG); + m_Descriptor = NULL; + } + + if (m_VolumePropValues != NULL) + { + ExFreePoolWithTag(m_VolumePropValues, MINADAPTER_POOLTAG); + m_VolumePropValues = NULL; + } + + // + // Free Irps. + // + if (m_SpeakerVolumeReq != NULL) + { + BthHfpDeviceNotificationReqContext * ctx; + + // Delete the associated memory objects. + ctx = GetBthHfpDeviceNotificationReqContext(m_SpeakerVolumeReq); + if (ctx->MemIn != NULL) + { + WdfObjectDelete(ctx->MemIn); + ctx->MemIn = NULL; + } + + if (ctx->MemOut != NULL) + { + WdfObjectDelete(ctx->MemOut); + ctx->MemOut = NULL; + } + + // Delete the request. + WdfObjectDelete(m_SpeakerVolumeReq); + m_SpeakerVolumeReq = NULL; + } + + if (m_MicVolumeReq != NULL) + { + BthHfpDeviceNotificationReqContext * ctx; + + // Delete the associated memory objects. + ctx = GetBthHfpDeviceNotificationReqContext(m_MicVolumeReq); + if (ctx->MemIn != NULL) + { + WdfObjectDelete(ctx->MemIn); + ctx->MemIn = NULL; + } + + if (ctx->MemOut != NULL) + { + WdfObjectDelete(ctx->MemOut); + ctx->MemOut = NULL; + } + + // Delete the request. + WdfObjectDelete(m_MicVolumeReq); + m_MicVolumeReq = NULL; + } + + if (m_ConnectionReq != NULL) + { + BthHfpDeviceNotificationReqContext * ctx; + + // Delete the associated memory objects. + ctx = GetBthHfpDeviceNotificationReqContext(m_ConnectionReq); + if (ctx->MemIn != NULL) + { + WdfObjectDelete(ctx->MemIn); + ctx->MemIn = NULL; + } + + if (ctx->MemOut != NULL) + { + WdfObjectDelete(ctx->MemOut); + ctx->MemOut = NULL; + } + + // Delete the request. + WdfObjectDelete(m_ConnectionReq); + m_ConnectionReq = NULL; + } + + if (m_StreamReq != NULL) + { + BthHfpDeviceNotificationReqContext * ctx; + + // Delete the associated memory objects. + ctx = GetBthHfpDeviceNotificationReqContext(m_StreamReq); + if (ctx->MemIn != NULL) + { + WdfObjectDelete(ctx->MemIn); + ctx->MemIn = NULL; + } + + if (ctx->MemOut != NULL) + { + WdfObjectDelete(ctx->MemOut); + ctx->MemOut = NULL; + } + + // Delete the request. + WdfObjectDelete(m_StreamReq); + m_StreamReq = NULL; + } + + // + // Notification work-item. + // + if (m_WorkItem != NULL) + { + WdfObjectDelete(m_WorkItem); + m_WorkItem = NULL; + } + + // + // Notification req. collection. + // + if (m_ReqCollection != NULL) + { + WdfObjectDelete(m_ReqCollection); + m_ReqCollection = NULL; + } + + ASSERT(m_UnknownSpeakerTopology == NULL); + SAFE_RELEASE(m_UnknownSpeakerTopology); + + ASSERT(m_UnknownSpeakerWave == NULL); + SAFE_RELEASE(m_UnknownSpeakerWave); + + ASSERT(m_UnknownMicTopology == NULL); + SAFE_RELEASE(m_UnknownMicTopology); + + ASSERT(m_UnknownMicWave == NULL); + SAFE_RELEASE(m_UnknownMicWave); + + ASSERT(m_nStreams == 0); + + ASSERT(m_SpeakerVolumeCallback.Handler == NULL); + ASSERT(m_SpeakerConnectionStatusCallback.Handler == NULL); + ASSERT(m_MicVolumeCallback.Handler == NULL); + ASSERT(m_MicConnectionStatusCallback.Handler == NULL); +} // ~CAdapterCommon + +// +// IBthHfpDeviceCommon implementation. +// + +//============================================================================= +#pragma code_seg("PAGE") +BOOL +BthHfpDevice::IsVolumeSupported() +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::IsVolumeSupported]")); + + return m_Descriptor->SupportsVolume; +} + +//============================================================================= +#pragma code_seg("PAGE") +PKSPROPERTY_VALUES +BthHfpDevice::GetVolumeSettings +( + _Out_ PULONG Size +) +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::GetVolumeSettings]")); + + ASSERT(Size != NULL); + + *Size = m_Descriptor->VolumePropertyValuesSize; + + return m_VolumePropValues; +} + +//============================================================================= +#pragma code_seg("PAGE") +LONG +BthHfpDevice::GetSpeakerVolume() +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::GetSpeakerVolume]")); + + return InterlockedCompareExchange(&m_SpeakerVolumeLevel, 0, 0); +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::SetSpeakerVolume +( + _In_ ULONG Volume +) +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::SetSpeakerVolume]")); + + return SetBthHfpSpeakerVolume(Volume); +} + +//============================================================================= +#pragma code_seg("PAGE") +LONG +BthHfpDevice::GetMicVolume() +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::GetMicVolume]")); + + return InterlockedCompareExchange(&m_MicVolumeLevel, 0, 0); +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::SetMicVolume +( + _In_ ULONG Volume +) +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::SetMicVolume]")); + + return SetBthHfpMicVolume(Volume); +} + +//============================================================================= +#pragma code_seg("PAGE") +BOOL +BthHfpDevice::GetConnectionStatus() +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::GetConnectionStatus]")); + + return (BOOL)InterlockedCompareExchange(&m_ConnectionStatusLong, 0, 0); +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::GetBthHfpCodecId(_Out_ UCHAR * CodecId) +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::GetBthHfpCodecId]")); + + ASSERT(CodecId != NULL); + + typedef enum _HFP_BYPASS_CODEC_ID_VERSION { + REQ_HFP_BYPASS_CODEC_ID_V1 = 1, + } HFP_BYPASS_CODEC_ID_VERSION, *PHFP_BYPASS_CODEC_ID_VERSION; + + typedef struct _HFP_BYPASS_CODEC_ID_V1 { + UCHAR CodecId; + } HFP_BYPASS_CODEC_ID_V1, *PHFP_BYPASS_CODEC_ID_V1; + + NTSTATUS ntStatus = STATUS_SUCCESS; + + union { + HFP_BYPASS_CODEC_ID_V1 CodecIdV1; + HFP_BYPASS_CODEC_ID_VERSION Version; + } value; + + *CodecId = 0; + + value.Version = REQ_HFP_BYPASS_CODEC_ID_V1; + + // + // Get the Bth HFP SCO Codec ID. + // + ntStatus = SendIoCtrlSynchronously( + NULL, + IOCTL_BTHHFP_DEVICE_GET_CODEC_ID, + sizeof(value.Version), + sizeof(value.CodecIdV1), + &value); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("GetBthHfpCodecId: SendIoCtrlSynchronously(IOCTL_BTHHFP_DEVICE_GET_CODEC_ID) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + *CodecId = value.CodecIdV1.CodecId; + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::Connect() +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::Connect]")); + + return SetBthHfpConnect(); +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::Disconnect() +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::Disconnect]")); + + return SetBthHfpDisconnect(); +} + +//============================================================================= +#pragma code_seg() +BOOL +BthHfpDevice::GetStreamStatus() +{ + DPF_ENTER(("[BthHfpDevice::GetStreamStatus]")); + + NTSTATUS ntStatus; + + ntStatus = (NTSTATUS)InterlockedCompareExchange(&m_StreamStatusLong, 0, 0); + + return NT_SUCCESS(ntStatus) ? TRUE : FALSE; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::StreamOpen() +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::StreamOpen]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + LONG nStreams = 0; + + ASSERT(m_nStreams >= 0); + + nStreams = InterlockedIncrement(&m_nStreams); + if (nStreams == 1) + { + BOOLEAN streamOpen = FALSE; + + ntStatus = SetBthHfpStreamOpen(); + if (NT_SUCCESS(ntStatus)) + { + streamOpen = TRUE; + m_StreamStatus = STATUS_SUCCESS; + ntStatus = EnableBthHfpStreamStatusNotification(); + } + + // + // Cleanup if any error. + // + if (!NT_SUCCESS(ntStatus)) + { + nStreams = InterlockedDecrement(&m_nStreams); + ASSERT(nStreams == 0); + UNREFERENCED_VAR(nStreams); + + if (streamOpen) + { + SetBthHfpStreamClose(); + } + + m_StreamStatus = STATUS_INVALID_DEVICE_STATE; + } + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::StreamClose() +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::StreamClose]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + LONG nStreams = 0; + + ASSERT(m_nStreams > 0); + + nStreams = InterlockedDecrement(&m_nStreams); + if (nStreams == 0) + { + ntStatus = SetBthHfpStreamClose(); + + StopBthHfpStreamStatusNotification(); + + m_StreamStatus = STATUS_INVALID_DEVICE_STATE; + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +GUID +BthHfpDevice::GetContainerId() +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::GetContainerId]")); + + return m_Descriptor->ContainerId; +} + +//============================================================================= +#pragma code_seg("PAGE") +VOID +BthHfpDevice::SetSpeakerVolumeHandler +( + _In_opt_ PFNEVENTNOTIFICATION EventHandler, + _In_opt_ PVOID EventHandlerContext +) + { + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::SetSpeakerVolumeHandler]")); + + ASSERT(EventHandler == NULL || m_SpeakerVolumeCallback.Handler == NULL); + + m_SpeakerVolumeCallback.Handler = EventHandler; // weak ref. + m_SpeakerVolumeCallback.Context = EventHandlerContext; + } + +//============================================================================= +#pragma code_seg("PAGE") +VOID +BthHfpDevice::SetSpeakerConnectionStatusHandler +( + _In_opt_ PFNEVENTNOTIFICATION EventHandler, + _In_opt_ PVOID EventHandlerContext +) +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::SetSpeakerConnectionStatusHandler]")); + + ASSERT(EventHandler == NULL || m_SpeakerConnectionStatusCallback.Handler == NULL); + + m_SpeakerConnectionStatusCallback.Handler = EventHandler; // weak ref. + m_SpeakerConnectionStatusCallback.Context = EventHandlerContext; +} + +//============================================================================= +#pragma code_seg("PAGE") +VOID +BthHfpDevice::SetMicVolumeHandler +( + _In_opt_ PFNEVENTNOTIFICATION EventHandler, + _In_opt_ PVOID EventHandlerContext +) +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::SetMicVolumeHandler]")); + + ASSERT(EventHandler == NULL || m_MicVolumeCallback.Handler == NULL); + + m_MicVolumeCallback.Handler = EventHandler; // weak ref. + m_MicVolumeCallback.Context = EventHandlerContext; +} + +//============================================================================= +#pragma code_seg("PAGE") +VOID +BthHfpDevice::SetMicConnectionStatusHandler +( + _In_opt_ PFNEVENTNOTIFICATION EventHandler, + _In_opt_ PVOID EventHandlerContext +) +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::SetMicConnectionStatusHandler]")); + + ASSERT(EventHandler == NULL || m_MicConnectionStatusCallback.Handler == NULL); + + m_MicConnectionStatusCallback.Handler = EventHandler; // weak ref. + m_MicConnectionStatusCallback.Context = EventHandlerContext; +} + +//============================================================================= +#pragma code_seg("PAGE") +BOOL +BthHfpDevice::IsNRECSupported() +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::IsNRECSupported]")); + + return m_Descriptor->SupportsNREC; +} + +//============================================================================= +#pragma code_seg("PAGE") +BOOL +BthHfpDevice::GetNRECDisableStatus() +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::GetNRECDisableStatus]")); + + // Return TRUE if HF wants to disable the NREC on the AG. + return (BOOL)InterlockedCompareExchange(&m_NRECDisableStatusLong, 0, 0); +} + +// +// Helper functions. +// + +//============================================================================= +#pragma code_seg() +NTSTATUS +BthHfpDevice::SendIoCtrlAsynchronously +( + _In_ WDFREQUEST Request, + _In_ ULONG IoControlCode, + _In_opt_ WDFMEMORY MemIn, + _In_opt_ WDFMEMORY MemOut, + _In_ PFN_WDF_REQUEST_COMPLETION_ROUTINE CompletionRoutine, + _In_ WDFCONTEXT Context +) +/*++ + +Routine Description: + + This function aynchronously sends an I/O Ctrl request to the BTH HFP + SCO Bypass device. + +--*/ +{ + DPF_ENTER(("[BthHfpDevice::SendIoCtrlAsynchronously]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + BOOLEAN fSent = FALSE; + + // + // Format and send the request. + // + ntStatus = WdfIoTargetFormatRequestForIoctl( + m_WdfIoTarget, + Request, + IoControlCode, + MemIn, + NULL, + MemOut, + NULL); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("SendIoCtrlAsynchronously: WdfIoTargetFormatRequestForIoctl(0x%x) failed, 0x%x", IoControlCode, ntStatus)), + Done); + + WdfRequestSetCompletionRoutine( + Request, + CompletionRoutine, + Context); + + fSent = WdfRequestSend(Request, m_WdfIoTarget, NULL); // no options. + if (fSent == FALSE) + { + ntStatus = WdfRequestGetStatus(Request); + if (NT_SUCCESS(ntStatus)) + { + ntStatus = STATUS_INVALID_DEVICE_STATE; + } + + DPF(D_ERROR, ("SendIoCtrlAsynchronously: WdfRequestSend(0x%x) failed, 0x%x", IoControlCode, ntStatus)); + goto Done; + } + + // + // All Done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::SendIoCtrlSynchronously +( + _In_opt_ WDFREQUEST Request, + _In_ ULONG IoControlCode, + _In_ ULONG InLength, + _In_ ULONG OutLength, + _When_(InLength > 0 || OutLength > 0, _In_) + _When_(InLength == 0 && OutLength == 0, _In_opt_) + PVOID Buffer +) +/*++ + +Routine Description: + + This function inits and synchronously sends an I/O Ctrl request to the BTH HFP + SCO Bypass device. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::SendIoCtrlSynchronously]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + PWDF_MEMORY_DESCRIPTOR memInPtr = NULL; + PWDF_MEMORY_DESCRIPTOR memOutPtr = NULL; + WDF_MEMORY_DESCRIPTOR memIn; + WDF_MEMORY_DESCRIPTOR memOut; + WDF_REQUEST_SEND_OPTIONS reqOpts; + + // + // Format and send the request. + // + if (InLength) + { + WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&memIn, Buffer, InLength); + memInPtr = &memIn; + } + + if (OutLength) + { + WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&memOut, Buffer, OutLength); + memOutPtr = &memOut; + } + + WDF_REQUEST_SEND_OPTIONS_INIT( + &reqOpts, + WDF_REQUEST_SEND_OPTION_TIMEOUT | + WDF_REQUEST_SEND_OPTION_SYNCHRONOUS); + + reqOpts.Timeout = WDF_REL_TIMEOUT_IN_SEC(BTH_HFP_SYNC_REQ_TIMEOUT_IN_SEC); + + ntStatus = WdfIoTargetSendIoctlSynchronously( + m_WdfIoTarget, + Request, + IoControlCode, + memInPtr, + memOutPtr, + &reqOpts, + NULL); // bytes returned. + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_VERBOSE, ("SendIoCtrlSynchronously: WdfIoTargetSendIoctlSynchronously(0x%x) failed, 0x%x", IoControlCode, ntStatus)), + Done); + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg() +VOID +BthHfpDevice::EvtBthHfpDeviceNotificationStatusWorkItem +( + _In_ WDFWORKITEM WorkItem +) +/*++ + +Routine Description: + + The function processes status notification updates. + +Arguments: + + WorkItem - WDF work-item object. + +--*/ +{ + DPF_ENTER(("[BthHfpDevice::EvtBthHfpDeviceNotificationStatusWorkItem]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + BthHfpDevice * This; + KIRQL oldIrql; + + if (WorkItem == NULL) + { + return; + } + + This = GetBthHfpDeviceNotificationWorkItemContext(WorkItem)->BthHfpDevice; + ASSERT(This != NULL); + + for (;;) + { + BOOL resend = TRUE; + WDFREQUEST req = NULL; + BthHfpDeviceNotificationReqContext * reqCtx; + WDF_REQUEST_COMPLETION_PARAMS params; + + // + // Retrieve a task. + // + KeAcquireSpinLock(&This->m_Lock, &oldIrql); + req = (WDFREQUEST) WdfCollectionGetFirstItem(This->m_ReqCollection); + if (req != NULL) + { + WdfCollectionRemove(This->m_ReqCollection, req); + } + KeReleaseSpinLock(&This->m_Lock, oldIrql); + + if (req == NULL) + { + break; + } + + // + // Get request parameters and context. + // + WDF_REQUEST_COMPLETION_PARAMS_INIT(¶ms); + WdfRequestGetCompletionParams(req, ¶ms); + + reqCtx = GetBthHfpDeviceNotificationReqContext(req); + ASSERT(reqCtx != NULL); + + // + // Handle this notification. + // + if (NT_SUCCESS(params.IoStatus.Status)) + { + switch(params.Parameters.Ioctl.IoControlCode) + { + case IOCTL_BTHHFP_DEVICE_GET_NRECDISABLE_STATUS_UPDATE: + { + InterlockedExchange(&This->m_NRECDisableStatusLong, (LONG)reqCtx->Buffer.BoolStatus); + } + break; + + case IOCTL_BTHHFP_SPEAKER_GET_VOLUME_STATUS_UPDATE: + { + LONG oldVolume; + + oldVolume = InterlockedExchange(&This->m_SpeakerVolumeLevel, reqCtx->Buffer.Volume); + if (reqCtx->Buffer.Volume != oldVolume) + { + // Notify audio miniport about this change. + if (This->m_SpeakerVolumeCallback.Handler != NULL) + { + This->m_SpeakerVolumeCallback.Handler( + This->m_SpeakerVolumeCallback.Context); + } + } + } + break; + + case IOCTL_BTHHFP_MIC_GET_VOLUME_STATUS_UPDATE: + { + LONG oldVolume; + + oldVolume = InterlockedExchange(&This->m_MicVolumeLevel, reqCtx->Buffer.Volume); + if (reqCtx->Buffer.Volume != oldVolume) + { + // Notify audio miniport about this change. + if (This->m_MicVolumeCallback.Handler != NULL) + { + This->m_MicVolumeCallback.Handler( + This->m_MicVolumeCallback.Context); + } + } + } + break; + + case IOCTL_BTHHFP_DEVICE_GET_CONNECTION_STATUS_UPDATE: + { + BOOL oldStatus; + + oldStatus = (BOOL)InterlockedExchange(&This->m_ConnectionStatusLong, (LONG)reqCtx->Buffer.BoolStatus); + if (reqCtx->Buffer.BoolStatus != oldStatus) + { + // Notify audio miniport about this change. + if (This->m_SpeakerConnectionStatusCallback.Handler != NULL) + { + This->m_SpeakerConnectionStatusCallback.Handler( + This->m_SpeakerConnectionStatusCallback.Context); + } + + if (This->m_MicConnectionStatusCallback.Handler != NULL) + { + This->m_MicConnectionStatusCallback.Handler( + This->m_MicConnectionStatusCallback.Context); + } + } + } + break; + + default: + // This should never happen. + resend = FALSE; + DPF(D_ERROR, ("EvtBthHfpDeviceNotificationStatusWorkItem: invalid request ctrl 0x%x", + params.Parameters.Ioctl.IoControlCode)); + break; + } + } + + if (resend) + { + WDF_REQUEST_REUSE_PARAMS reuseParams; + + WDF_REQUEST_REUSE_PARAMS_INIT( + &reuseParams, + WDF_REQUEST_REUSE_NO_FLAGS, + STATUS_SUCCESS); + + ntStatus = WdfRequestReuse(req, &reuseParams); + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_ERROR, ("EvtBthHfpDeviceNotificationStatusWorkItem: WdfRequestReuse failed, 0x%x", ntStatus)); + break; + } + + // Resend status notification request. + reqCtx->Buffer.bImmediate = FALSE; + + ntStatus = This->SendIoCtrlAsynchronously( + req, + params.Parameters.Ioctl.IoControlCode, + reqCtx->MemIn, + reqCtx->MemOut, + EvtBthHfpDeviceNotificationStatusCompletion, + This); + + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_ERROR, ("EvtBthHfpDeviceNotificationStatusWorkItem: SendIoCtrlAsynchronously" + "(0x%x) failed, 0x%x", + params.Parameters.Ioctl.IoControlCode, ntStatus)); + break; + } + } + } +} + +//============================================================================= +#pragma code_seg() +void +BthHfpDevice::EvtBthHfpDeviceNotificationStatusCompletion +( + _In_ WDFREQUEST Request, + _In_ WDFIOTARGET Target, + _In_ PWDF_REQUEST_COMPLETION_PARAMS Params, + _In_ WDFCONTEXT Context +) +{ + DPF_ENTER(("[BthHfpDevice::EvtBthHfpDeviceNotificationStatusCompletion]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + BthHfpDeviceNotificationReqContext * ctx = NULL; + BthHfpDevice * This = NULL; + KIRQL oldIrql; + + UNREFERENCED_PARAMETER(Target); + UNREFERENCED_PARAMETER(Context); + + ctx = GetBthHfpDeviceNotificationReqContext(Request); + This = ctx->BthHfpDevice; + ASSERT(This != NULL); + + ntStatus = Params->IoStatus.Status; + if (ntStatus == STATUS_CANCELLED) + { + // BTH HFP device is shutting down. Do not re-send this request. + goto Done; + } + + // + // If something is wrong with the HFP interface, do not loop forever. + // + if (!NT_SUCCESS(ntStatus)) + { + if (++ctx->Errors > BTH_HFP_NOTIFICATION_MAX_ERROR_COUNT) + { + // Too many errors. Do not re-send this request. + goto Done; + } + } + else + { + // reset the # of errors. + ctx->Errors = 0; + } + + // + // Let the work-item thread process this request. + // + KeAcquireSpinLock(&This->m_Lock, &oldIrql); + + ntStatus = WdfCollectionAdd(This->m_ReqCollection, Request); + if (NT_SUCCESS(ntStatus)) + { + WdfWorkItemEnqueue(This->m_WorkItem); + } + + KeReleaseSpinLock(&This->m_Lock, oldIrql); + +Done:; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::GetBthHfpDescriptor +( + _Out_ PBTHHFP_DESCRIPTOR2 * Descriptor +) +/*++ + +Routine Description: + + This function synchronously gets the remote Bluetooth Hands-Free Profile SCO + Bypass descriptor. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::GetBthHfpDescriptor]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + WDFREQUEST req = NULL; + PBTHHFP_DESCRIPTOR2 descriptor = NULL; + ULONG length = 0; + ULONG_PTR information = 0; + WDF_REQUEST_REUSE_PARAMS reuseParams; + WDF_OBJECT_ATTRIBUTES attributes; + + *Descriptor = NULL; + + // + // Allocate and format a WDF request. + // + WDF_OBJECT_ATTRIBUTES_INIT(&attributes); + attributes.ParentObject = m_Adapter->GetWdfDevice(); + ntStatus = WdfRequestCreate( + &attributes, + m_WdfIoTarget, + &req); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("GetBthHfpDescriptor: WdfRequestCreate failed, 0x%x", ntStatus)), + Done); + + // + // Get the size of the buffer. + // + ntStatus = SendIoCtrlSynchronously( + req, + IOCTL_BTHHFP_DEVICE_GET_DESCRIPTOR2, + NULL, + NULL, + NULL); + + if (ntStatus != STATUS_BUFFER_TOO_SMALL) + { + if (NT_SUCCESS(ntStatus)) + { + ntStatus = STATUS_INVALID_DEVICE_STATE; + } + + DPF(D_ERROR, ("GetBthHfpDescriptor: SendIoCtrlSynchronously(IOCTL_BTHHFP_DEVICE_GET_DESCRIPTOR2): failed, 0x%x", ntStatus)); + goto Done; + } + + ntStatus = STATUS_SUCCESS; + + information = WdfRequestGetInformation(req); + if (information == 0 || information > ULONG_MAX) + { + ntStatus = STATUS_INVALID_DEVICE_STATE; + DPF(D_ERROR, ("GetBthHfpDescriptor: IOCTL_BTHHFP_DEVICE_GET_DESCRIPTOR2 buffer too big (%Id): 0x%x", information, ntStatus)); + goto Done; + } + + length = (ULONG)information; + + // + // Allocate memory needed to hold the info. + // + descriptor = (PBTHHFP_DESCRIPTOR2) ExAllocatePoolWithTag(NonPagedPoolNx, length, MINADAPTER_POOLTAG); + if (descriptor == NULL) + { + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + } + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("GetBthHfpDescriptor: ExAllocatePoolWithTag failed, out of memory")), + Done); + + // + // Get the Bth HFP SCO Bypass descriptor. + // + WDF_REQUEST_REUSE_PARAMS_INIT( + &reuseParams, + WDF_REQUEST_REUSE_NO_FLAGS, + STATUS_SUCCESS); + + ntStatus = WdfRequestReuse(req, &reuseParams); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("GetBthHfpDescriptor: WdfRequestReuse failed, 0x%x", ntStatus)), + Done); + + ntStatus = SendIoCtrlSynchronously( + req, + IOCTL_BTHHFP_DEVICE_GET_DESCRIPTOR2, + NULL, + length, + descriptor); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("GetBthHfpDescriptor: SendIoCtrlSynchronously(IOCTL_BTHHFP_DEVICE_GET_DESCRIPTOR2) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + *Descriptor = descriptor; + ntStatus = STATUS_SUCCESS; + +Done: + if (!NT_SUCCESS(ntStatus)) + { + if (descriptor != NULL) + { + ExFreePoolWithTag(descriptor, MINADAPTER_POOLTAG); + } + } + + if (req != NULL) + { + WdfObjectDelete(req); + req = NULL; + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg() +NTSTATUS +BthHfpDevice::EnableBthHfpNrecDisableStatusNotification() +/*++ + +Routine Description: + + This function registers for Bluetooth Hands-Free Profile SCO Bypass NREC-Disable + status change notification. + +--*/ +{ + DPF_ENTER(("[BthHfpDevice::EnableBthHfpNrecDisableStatusNotification]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + BthHfpDeviceNotificationReqContext * ctx = NULL; + + ctx = GetBthHfpDeviceNotificationReqContext(m_NRECDisableStatusReq); + + // + // This is a notification request. + // + ctx->Buffer.bImmediate = FALSE; + + // + // Get the Bth HFP SCO Bypass NREC-Disable status (async). + // + ntStatus = SendIoCtrlAsynchronously( + m_NRECDisableStatusReq, + IOCTL_BTHHFP_DEVICE_GET_NRECDISABLE_STATUS_UPDATE, + ctx->MemIn, + ctx->MemOut, + EvtBthHfpDeviceNotificationStatusCompletion, + this); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("EnableBthHfpNrecDisableStatusNotification: SendIoCtrlAsynchronously(IOCTL_BTHHFP_DEVICE_GET_NRECDISABLE_STATUS_UPDATE) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::GetBthHfpVolumePropertyValues +( + _In_ ULONG Length, + _Out_ PKSPROPERTY_VALUES * PropValues +) +/*++ + +Routine Description: + + This function synchronously gets the remote Bluetooth Hands-Free Profile SCO + Bypass volume values. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::GetBthHfpVolumePropertyValues]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + PKSPROPERTY_VALUES propValues = NULL; + + *PropValues = NULL; + + // + // Allocate memory. + // + propValues = (PKSPROPERTY_VALUES) ExAllocatePoolWithTag(NonPagedPoolNx, Length, MINADAPTER_POOLTAG); + if (propValues == NULL) + { + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + } + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("GetBthHfpVolumePropertyValues: ExAllocatePoolWithTag failed, out of memory")), + Done); + + // + // Get the Bth HFP SCO Bypass descriptor. + // + ntStatus = SendIoCtrlSynchronously( + NULL, + IOCTL_BTHHFP_DEVICE_GET_VOLUMEPROPERTYVALUES, + 0, + Length, + propValues); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("GetBthHfpVolumePropertyValues: SendIoCtrlSynchronously(IOCTL_BTHHFP_DEVICE_GET_VOLUMEPROPERTYVALUES) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + *PropValues = propValues; + ntStatus = STATUS_SUCCESS; + +Done: + if (!NT_SUCCESS(ntStatus)) + { + if (propValues != NULL) + { + ExFreePoolWithTag(propValues, MINADAPTER_POOLTAG); + } + } + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::SetBthHfpSpeakerVolume +( + _In_ LONG Volume +) +/*++ + +Routine Description: + + This function synchronously sets the remote Bluetooth Hands-Free Profile SCO + Bypass speaker volume. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::SetBthHfpSpeakerVolume]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + + // + // Get the Bth HFP SCO Bypass speaker volume. + // + ntStatus = SendIoCtrlSynchronously( + NULL, + IOCTL_BTHHFP_SPEAKER_SET_VOLUME, + sizeof(Volume), + 0, + &Volume); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("SetBthHfpSpeakerVolume: SendIoCtrlSynchronously(IOCTL_BTHHFP_SPEAKER_SET_VOLUME) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::GetBthHfpSpeakerVolume +( + _Out_ LONG * Volume +) +/*++ + +Routine Description: + + This function synchronously gets the remote Bluetooth Hands-Free Profile SCO + Bypass speaker volume. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::GetBthHfpSpeakerVolume]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + BthHfpDeviceNotificationBuffer buffer = {0}; + + *Volume = 0; + + buffer.bImmediate = TRUE; + + // + // Get the Bth HFP SCO Bypass speaker volume. + // + ntStatus = SendIoCtrlSynchronously( + NULL, + IOCTL_BTHHFP_SPEAKER_GET_VOLUME_STATUS_UPDATE, + sizeof(buffer.bImmediate), + sizeof(buffer.Volume), + &buffer); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("GetBthHfpSpeakerVolume: SendIoCtrlSynchronously(IOCTL_BTHHFP_SPEAKER_GET_VOLUME_STATUS_UPDATE) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + *Volume = buffer.Volume; + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg() +NTSTATUS +BthHfpDevice::EnableBthHfpSpeakerVolumeStatusNotification() +/*++ + +Routine Description: + + This function registers for Bluetooth Hands-Free Profile SCO Bypass speaker + volume change notification. + +--*/ +{ + DPF_ENTER(("[BthHfpDevice::EnableBthHfpSpeakerVolumeStatusNotification]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + BthHfpDeviceNotificationReqContext * ctx = NULL; + + ctx = GetBthHfpDeviceNotificationReqContext(m_SpeakerVolumeReq); + + // + // This is a notification request. + // + ctx->Buffer.bImmediate = FALSE; + + // + // Register for speaker volume updates. + // + ntStatus = SendIoCtrlAsynchronously( + m_SpeakerVolumeReq, + IOCTL_BTHHFP_SPEAKER_GET_VOLUME_STATUS_UPDATE, + ctx->MemIn, + ctx->MemOut, + EvtBthHfpDeviceNotificationStatusCompletion, + this); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("EnableBthHfpSpeakerVolumeStatusNotification: SendIoCtrlAsynchronously(IOCTL_BTHHFP_SPEAKER_GET_VOLUME_STATUS_UPDATE) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::SetBthHfpMicVolume +( + _In_ LONG Volume +) +/*++ + +Routine Description: + + This function synchronously sets the remote Bluetooth Hands-Free Profile SCO + Bypass mic volume. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::SetBthHfpMicVolume]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + + // + // Get the Bth HFP SCO Bypass mic volume. + // + ntStatus = SendIoCtrlSynchronously( + NULL, + IOCTL_BTHHFP_MIC_SET_VOLUME, + sizeof(Volume), + 0, + &Volume); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("SetBthHfpMicVolume: SendIoCtrlSynchronously(IOCTL_BTHHFP_MIC_SET_VOLUME) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::GetBthHfpMicVolume +( + _Out_ LONG * Volume +) +/*++ + +Routine Description: + + This function synchronously gets the remote Bluetooth Hands-Free Profile SCO + Bypass mic volume. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::GetBthHfpMicVolume]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + BthHfpDeviceNotificationBuffer buffer = {0}; + + *Volume = 0; + + buffer.bImmediate = TRUE; + + // + // Get the Bth HFP SCO Bypass mic volume. + // + ntStatus = SendIoCtrlSynchronously( + NULL, + IOCTL_BTHHFP_MIC_GET_VOLUME_STATUS_UPDATE, + sizeof(buffer.bImmediate), + sizeof(buffer.Volume), + &buffer); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("GetBthHfpMicVolume: SendIoCtrlSynchronously(IOCTL_BTHHFP_MIC_GET_VOLUME_STATUS_UPDATE) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + *Volume = buffer.Volume; + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg() +NTSTATUS +BthHfpDevice::EnableBthHfpMicVolumeStatusNotification() +/*++ + +Routine Description: + + This function registers for Bluetooth Hands-Free Profile SCO Bypass mic + volume change notification. + +--*/ +{ + DPF_ENTER(("[BthHfpDevice::EnableBthHfpMicVolumeStatusNotification]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + BthHfpDeviceNotificationReqContext * ctx = NULL; + + ctx = GetBthHfpDeviceNotificationReqContext(m_MicVolumeReq); + + // + // This is a notification request. + // + ctx->Buffer.bImmediate = FALSE; + + // + // Register for mic volume updates. + // + ntStatus = SendIoCtrlAsynchronously( + m_MicVolumeReq, + IOCTL_BTHHFP_MIC_GET_VOLUME_STATUS_UPDATE, + ctx->MemIn, + ctx->MemOut, + EvtBthHfpDeviceNotificationStatusCompletion, + this); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("EnableBthHfpMicVolumeStatusNotification: SendIoCtrlAsynchronously(IOCTL_BTHHFP_MIC_GET_VOLUME_STATUS_UPDATE) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::GetBthHfpConnectionStatus +( + _Out_ BOOL * ConnectionStatus +) +/*++ + +Routine Description: + + This function synchronously gets the remote Bluetooth Hands-Free Profile SCO + Bypass connection status. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::GetBthHfpConnectionStatus]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + BOOL bValue = TRUE; // In: bImmediate, Out: value. + + *ConnectionStatus = 0; + + // + // Get the Bth HFP SCO Bypass connection status. + // + ntStatus = SendIoCtrlSynchronously( + NULL, + IOCTL_BTHHFP_DEVICE_GET_CONNECTION_STATUS_UPDATE, + sizeof(bValue), + sizeof(bValue), + &bValue); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("GetBthHfpConnectionStatus: SendIoCtrlSynchronously(IOCTL_BTHHFP_DEVICE_GET_CONNECTION_STATUS_UPDATE) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + *ConnectionStatus = bValue; + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg() +NTSTATUS +BthHfpDevice::EnableBthHfpConnectionStatusNotification() +/*++ + +Routine Description: + + This function registers for Bluetooth Hands-Free Profile SCO Bypass + connection status notification. + +--*/ +{ + DPF_ENTER(("[BthHfpDevice::EnableBthHfpConnectionStatusNotification]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + BthHfpDeviceNotificationReqContext * ctx = NULL; + + ctx = GetBthHfpDeviceNotificationReqContext(m_ConnectionReq); + + // + // Make sure this obj is alive while the IRP is active. + // + ctx->Buffer.bImmediate = FALSE; + + // + // Get the Bth HFP SCO Bypass connection status. + // + ntStatus = SendIoCtrlAsynchronously( + m_ConnectionReq, + IOCTL_BTHHFP_DEVICE_GET_CONNECTION_STATUS_UPDATE, + ctx->MemIn, + ctx->MemOut, + EvtBthHfpDeviceNotificationStatusCompletion, + this); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("EnableBthHfpConnectionStatusNotification: SendIoCtrlAsynchronously(IOCTL_BTHHFP_DEVICE_GET_CONNECTION_STATUS_UPDATE) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::SetBthHfpConnect() +/*++ + +Routine Description: + + This function synchronously requests a Bluetooth Hands-Free Profile level + connection to the paired Bluetooth device. + + This request initiates the Service Level Connection establishment procedure + and completes without waiting for the connection procedure to complete. + Connection status can be determined using IOCTL_BTHHFP_GET_CONNECTION_STATUS_UPDATE. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::SetBthHfpConnect]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + + ntStatus = SendIoCtrlSynchronously( + NULL, + IOCTL_BTHHFP_DEVICE_REQUEST_CONNECT, + 0, + 0, + NULL); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("SetBthHfpConnect: SendIoCtrlSynchronously(IOCTL_BTHHFP_DEVICE_REQUEST_CONNECT) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::SetBthHfpDisconnect() +/*++ + +Routine Description: + + This function synchronously requests a Bluetooth Hands-Free Profile level + connection to the paired Bluetooth device. + + This request initiates disconnection of the Service Level Connection and + completes without waiting for the disconnection to complete. Connection + status can be determined using IOCTL_BTHHFP_GET_CONNECTION_STATUS_UPDATE. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::SetBthHfpDisconnect]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + + ntStatus = SendIoCtrlSynchronously( + NULL, + IOCTL_BTHHFP_DEVICE_REQUEST_DISCONNECT, + 0, + 0, + NULL); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("SetBthHfpDisconnect: SendIoCtrlSynchronously(IOCTL_BTHHFP_DEVICE_REQUEST_DISCONNECT) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg() +void +BthHfpDevice::EvtBthHfpDeviceStreamStatusCompletion +( + _In_ WDFREQUEST Request, + _In_ WDFIOTARGET Target, + _In_ PWDF_REQUEST_COMPLETION_PARAMS Params, + _In_ WDFCONTEXT Context +) +/*++ + +Routine Description: + + Completion callback for the Bluetooth Hands-Free Profile SCO Bypass + stream status notification. + +--*/ +{ + DPF_ENTER(("[BthHfpDevice::EvtBthHfpDeviceStreamStatusCompletion]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + BthHfpDeviceNotificationReqContext * reqCtx = NULL; + BthHfpDevice * This = NULL; + NTSTATUS ntResult = STATUS_SUCCESS; + + UNREFERENCED_PARAMETER(Target); + UNREFERENCED_PARAMETER(Context); + + // + // Get the SCO stream status. + // + reqCtx = GetBthHfpDeviceNotificationReqContext(Request); + This = reqCtx->BthHfpDevice; + ASSERT(This != NULL); + + ntStatus = Params->IoStatus.Status; + if (!NT_SUCCESS(ntStatus)) + { + ntResult = STATUS_INVALID_DEVICE_STATE; + } + else + { + ntResult = reqCtx->Buffer.NtStatus; + } + + InterlockedExchange(&This->m_StreamStatusLong, (LONG)ntResult); + + // + // Let the stop routine know we are done. Stop routine will + // re-init the request. + // + KeSetEvent(&This->m_StreamStatusEvent, IO_NO_INCREMENT, FALSE); +} + +//============================================================================= +#pragma code_seg() +NTSTATUS +BthHfpDevice::EnableBthHfpStreamStatusNotification() +/*++ + +Routine Description: + + This function registers for Bluetooth Hands-Free Profile SCO Bypass + stream status notification. + +--*/ +{ + DPF_ENTER(("[BthHfpDevice::EnableBthHfpStreamStatusNotification]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + BthHfpDeviceNotificationReqContext * ctx = NULL; + + ASSERT(m_nStreams > 0); + + ctx = GetBthHfpDeviceNotificationReqContext(m_StreamReq); + ctx->Buffer.bImmediate = FALSE; + + KeClearEvent(&m_StreamStatusEvent); + + // + // Get the Bth HFP SCO Bypass connection status. + // + ntStatus = SendIoCtrlAsynchronously( + m_StreamReq, + IOCTL_BTHHFP_STREAM_GET_STATUS_UPDATE, + ctx->MemIn, + ctx->MemOut, + EvtBthHfpDeviceStreamStatusCompletion, + this); + + if (!NT_SUCCESS(ntStatus)) + { + KeSetEvent(&m_StreamStatusEvent, IO_NO_INCREMENT, FALSE); + DPF(D_ERROR, ("EnableBthHfpStreamStatusNotification: SendIoCtrlAsynchronously(IOCTL_BTHHFP_STREAM_GET_STATUS_UPDATE) failed, 0x%x", ntStatus)); + goto Done; + } + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::StopBthHfpStreamStatusNotification() +/*++ + +Routine Description: + + This function stops the Bluetooth Hands-Free Profile SCO Bypass + connection status notification. + The function waits for the request to be done before returning. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::StopBthHfpStreamStatusNotification]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + BthHfpDeviceNotificationReqContext * reqCtx = NULL; + WDF_REQUEST_REUSE_PARAMS reuseParams; + + WdfRequestCancelSentRequest(m_StreamReq); + KeWaitForSingleObject(&m_StreamStatusEvent, Executive, KernelMode, FALSE, NULL); + + reqCtx = GetBthHfpDeviceNotificationReqContext(m_StreamReq); + ASSERT(reqCtx != NULL); + UNREFERENCED_VAR(reqCtx); + + // + // Re-init the request for later. + // + WDF_REQUEST_REUSE_PARAMS_INIT( + &reuseParams, + WDF_REQUEST_REUSE_NO_FLAGS, + STATUS_SUCCESS); + + ntStatus = WdfRequestReuse(m_StreamReq, &reuseParams); + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_ERROR, ("StopBthHfpStreamStatusNotification: WdfRequestReuse failed, 0x%x", ntStatus)); + } + + return STATUS_SUCCESS; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::SetBthHfpStreamOpen() +/*++ + +Routine Description: + + This function synchronously requests an open SCO channel to transmit audio + data over the air. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::SetBthHfpStreamOpen]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + + ntStatus = SendIoCtrlSynchronously( + NULL, + IOCTL_BTHHFP_STREAM_OPEN, + 0, + 0, + NULL); + + if (ntStatus == STATUS_DEVICE_BUSY) + { + // The stream channel is already open. + DPF(D_VERBOSE, ("SetBthHfpStreamOpen: the stream channel is already open")); + ntStatus = STATUS_SUCCESS; + } + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("SetBthHfpStreamOpen: SendIoCtrlSynchronously(IOCTL_BTHHFP_STREAM_OPEN) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::SetBthHfpStreamClose() +/*++ + +Routine Description: + + This function synchronously requests to close the SCO channel. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::SetBthHfpStreamClose]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + + ntStatus = SendIoCtrlSynchronously( + NULL, + IOCTL_BTHHFP_STREAM_CLOSE, + 0, + 0, + NULL); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("SetBthHfpStreamClose: SendIoCtrlSynchronously(IOCTL_BTHHFP_STREAM_CLOSE) failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done: + + return ntStatus; +} + +//============================================================================= +#pragma code_seg("PAGE") +VOID +BthHfpDevice::Start() +/*++ + +Routine Description: + + Asynchronously called to start the audio device. + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::Start]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + BOOL connStatus = FALSE; + UCHAR codecId = 0; + UINT bthMiniportsIndex = 0; + + // CVSD is the narrow band codec for SCO. Wideband codec IDs are any number higher than 1. + // mSBC is the only required wideband codec, though the controller+headset combination may + // support other wideband codecs. + const UCHAR CODEC_CVSD = 1; + + // + // Get bth hfp descriptor + // + ntStatus = GetBthHfpDescriptor(&m_Descriptor); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Start: GetBthHfpDescriptor: failed to retrieve BTHHFP_DESCRIPTOR2, 0x%x", ntStatus)), + Done); + + // + // Get valume settings. + // + if (m_Descriptor->SupportsVolume) + { + PKSPROPERTY_VALUES volumePropValues = NULL; + LONG volume = 0; + + // Volume settings. + ntStatus = GetBthHfpVolumePropertyValues( + m_Descriptor->VolumePropertyValuesSize, + &volumePropValues); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Start: GetBthHfpVolumePropertyValues: failed to retrieve KSPROPERTY_VALUES, 0x%x", ntStatus)), + Done); + + m_VolumePropValues = volumePropValues; + + // Speaker volume. + ntStatus = GetBthHfpSpeakerVolume(&volume); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Start: GetBthHfpSpeakerVolume: failed, 0x%x", ntStatus)), + Done); + + m_SpeakerVolumeLevel = volume; + + // Mic volume. + ntStatus = GetBthHfpMicVolume(&volume); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Start: GetBthHfpMicVolume: failed, 0x%x", ntStatus)), + Done); + + m_MicVolumeLevel = volume; + } + + // + // Get connection status. + // + ntStatus = GetBthHfpConnectionStatus(&connStatus); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Start: GetBthHfpConnectionStatus: failed, 0x%x", ntStatus)), + Done); + + m_ConnectionStatus = connStatus; + + // + // Get codec id (if non-zero, connection supports Wideband Speech) + // + ntStatus = GetBthHfpCodecId(&codecId); + if (ntStatus == STATUS_INVALID_DEVICE_REQUEST) + { + // GetBthHfpCodecId fails with STATUS_INVALID_DEVICE_REQUEST if the system doesn't + // support Wideband Speech (currently only Mobile supports this call) + ntStatus = STATUS_SUCCESS; + } + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Start: GetBthHfpCodecId: failed, 0x%x", ntStatus)), + Done); + + if (codecId > CODEC_CVSD) + { + // Use the miniport tables that support 16kHz + bthMiniportsIndex = 1; + } + + // + // Customize the topology/wave descriptors for this instance + // + ntStatus = CreateCustomEndpointMinipair( + g_BthHfpRenderEndpoints[bthMiniportsIndex], + &m_Descriptor->FriendlyName, + &m_Descriptor->OutputPinCategory, + &m_SpeakerMiniports); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Start: CreateCustomEndpointMinipair for Render: failed, 0x%x", ntStatus)), + Done); + + ntStatus = CreateCustomEndpointMinipair( + g_BthHfpCaptureEndpoints[bthMiniportsIndex], + &m_Descriptor->FriendlyName, + &m_Descriptor->InputPinCategory, + &m_MicMiniports); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Start: CreateCustomEndpointMinipair for Capture: failed, 0x%x", ntStatus)), + Done); + + ASSERT(m_SpeakerMiniports != NULL); + ASSERT(m_MicMiniports != NULL); + _Analysis_assume_(m_SpeakerMiniports != NULL); + _Analysis_assume_(m_MicMiniports != NULL); + + // + // Register topology and wave filters. + // + ntStatus = m_Adapter->InstallEndpointFilters( + NULL, + m_SpeakerMiniports, + PBTHHFPDEVICECOMMON(this), + &m_UnknownSpeakerTopology, + &m_UnknownSpeakerWave + ); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Start: InstallEndpointRenderFilters (Bth HFP SCO-Bypass): failed, 0x%x", ntStatus)), + Done); + + ntStatus = m_Adapter->InstallEndpointFilters( + NULL, + m_MicMiniports, + PBTHHFPDEVICECOMMON(this), + &m_UnknownMicTopology, + &m_UnknownMicWave + ); + + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Start: InstallEndpointCaptureFilters (Bth HFP SCO-Bypass): failed, 0x%x", ntStatus)), + Done); + + // + // Pend status notifications. + // + + // NREC disable AudioGateway (AG) status. + ntStatus = EnableBthHfpNrecDisableStatusNotification(); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Start: EnableBthHfpNrecDisableStatusNotification: failed, 0x%x", ntStatus)), + Done); + + // Volume speaker status. + ntStatus = EnableBthHfpSpeakerVolumeStatusNotification(); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Start: EnableBthHfpSpeakerVolumeStatusNotification: failed, 0x%x", ntStatus)), + Done); + + // Volume mic status. + ntStatus = EnableBthHfpMicVolumeStatusNotification(); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Start: EnableBthHfpMicVolumeStatusNotification: failed, 0x%x", ntStatus)), + Done); + + // Connection status. + ntStatus = EnableBthHfpConnectionStatusNotification(); + IF_FAILED_ACTION_JUMP( + ntStatus, + DPF(D_ERROR, ("Start: EnableBthHfpConnectionStatusNotification: failed, 0x%x", ntStatus)), + Done); + + // + // All done. + // + ntStatus = STATUS_SUCCESS; + +Done:; + if (!NT_SUCCESS(ntStatus)) + { + InterlockedExchange((PLONG)&m_State, eBthHfpStateFailed); + } +} + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::CreateCustomEndpointMinipair +( + _In_ PENDPOINT_MINIPAIR pBaseMinipair, + _In_ PUNICODE_STRING FriendlyName, + _In_ PGUID pCategory, + _Outptr_ PENDPOINT_MINIPAIR *ppCustomMinipair +) +{ + NTSTATUS ntStatus; + PENDPOINT_MINIPAIR pNewMinipair = NULL; + SYSVAD_DEVPROPERTY* pProperties = NULL; + PPCFILTER_DESCRIPTOR pNewTopoFilterDesc = NULL; + PPCPIN_DESCRIPTOR pNewTopoPins = NULL; + ULONG cProperties; + ULONG cTopoPins; + + PAGED_CODE(); + + // + // This routine will add one more property to whatever the base minipair describes for the topo filter interface properties + // It will also allocate and set up custom filter and pin descriptors to allow changing the KSNODETYPE for the hfp device + // + cTopoPins = pBaseMinipair->TopoDescriptor->PinCount; + cProperties = pBaseMinipair->TopoInterfacePropertyCount + 1; + pProperties = (SYSVAD_DEVPROPERTY*)ExAllocatePoolWithTag(NonPagedPoolNx, cProperties * sizeof(SYSVAD_DEVPROPERTY), SYSVAD_POOLTAG); + pNewMinipair = (ENDPOINT_MINIPAIR*)ExAllocatePoolWithTag(NonPagedPoolNx, sizeof(ENDPOINT_MINIPAIR), SYSVAD_POOLTAG); + pNewTopoFilterDesc = (PCFILTER_DESCRIPTOR*)ExAllocatePoolWithTag(NonPagedPoolNx, sizeof(PCFILTER_DESCRIPTOR), SYSVAD_POOLTAG); + pNewTopoPins = (PCPIN_DESCRIPTOR*)ExAllocatePoolWithTag(NonPagedPoolNx, cTopoPins * sizeof(PCPIN_DESCRIPTOR), SYSVAD_POOLTAG); + + if ((pProperties != NULL) && (pNewMinipair != NULL) && (pNewTopoFilterDesc != NULL) && (pNewTopoPins != NULL)) + { + SYSVAD_DEVPROPERTY *pLastProperty; + + // Copy base minipair properties to new property list + if (pBaseMinipair->TopoInterfacePropertyCount > 0) + { + RtlCopyMemory(pProperties, pBaseMinipair->TopoInterfaceProperties, pBaseMinipair->TopoInterfacePropertyCount * sizeof(SYSVAD_DEVPROPERTY)); + } + + // Add friendly name property to the list + NT_ASSERT(FriendlyName->Length + sizeof(UNICODE_NULL) <= FriendlyName->MaximumLength); // Assuming NULL terminated string + pLastProperty = &pProperties[cProperties - 1]; + pLastProperty->PropertyKey = &DEVPKEY_DeviceInterface_FriendlyName; + pLastProperty->Type = DEVPROP_TYPE_STRING_INDIRECT; + pLastProperty->BufferSize = FriendlyName->Length + sizeof(UNICODE_NULL); + pLastProperty->Buffer = FriendlyName->Buffer; + + // Copy base minipair structure + RtlCopyMemory(pNewMinipair, pBaseMinipair, sizeof(ENDPOINT_MINIPAIR)); + + RtlCopyMemory(pNewTopoFilterDesc, pBaseMinipair->TopoDescriptor, sizeof(PCFILTER_DESCRIPTOR)); + RtlCopyMemory(pNewTopoPins, pBaseMinipair->TopoDescriptor->Pins, cTopoPins * sizeof(PCPIN_DESCRIPTOR)); + + pNewTopoFilterDesc->Pins = pNewTopoPins; + pNewMinipair->TopoDescriptor = pNewTopoFilterDesc; + + // Update it to point to new property list + pNewMinipair->TopoInterfacePropertyCount = cProperties; + pNewMinipair->TopoInterfaceProperties = pProperties; + + ntStatus = UpdateCustomEndpointCategory(pNewTopoFilterDesc, pNewTopoPins, pCategory); + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_ERROR, ("UpdateCustomEndpointCategory: failed, 0x%x", ntStatus)); + } + else + { + *ppCustomMinipair = pNewMinipair; + + pProperties = NULL; + pNewMinipair = NULL; + pNewTopoFilterDesc = NULL; + pNewTopoPins = NULL; + + ntStatus = STATUS_SUCCESS; + } + } + else + { + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + } + + if (pProperties != NULL) + { + ExFreePoolWithTag(pProperties, SYSVAD_POOLTAG); + } + if (pNewMinipair != NULL) + { + ExFreePoolWithTag(pNewMinipair, SYSVAD_POOLTAG); + } + if (pNewTopoFilterDesc != NULL) + { + ExFreePoolWithTag(pNewTopoFilterDesc, SYSVAD_POOLTAG); + } + if (pNewTopoPins != NULL) + { + ExFreePoolWithTag(pNewTopoPins, SYSVAD_POOLTAG); + } + + return ntStatus; +} +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +BthHfpDevice::UpdateCustomEndpointCategory +( + _In_ PPCFILTER_DESCRIPTOR pCustomMinipairTopoFilter, + _In_ PPCPIN_DESCRIPTOR pCustomMinipairTopoPins, + _In_ PGUID pCategory +) +{ + NTSTATUS ntStatus = STATUS_NOT_FOUND; + ULONG cPinCount = 0; + BOOL FoundCategoryAudio = FALSE; + BOOL FoundNodeType = FALSE; + + PAGED_CODE(); + + cPinCount = pCustomMinipairTopoFilter->PinCount; + + // Find the right pin: There should be two pins, one with Category KSCATEGORY_AUDIO, + // and one with a KSNODETYPE_* Category. We need to modify the KSNODETYPE category. + for (ULONG i = 0; i < cPinCount; ++i) + { + if (IsEqualGUID(*pCustomMinipairTopoPins[i].KsPinDescriptor.Category, KSCATEGORY_AUDIO)) + { + ASSERT(FoundCategoryAudio == FALSE); + if (FoundCategoryAudio) + { + ntStatus = STATUS_INVALID_DEVICE_STATE; + DPF(D_ERROR, ("UpdateCustomEndpointCategory: KSCATEGORY_AUDIO found more than once, 0x%x", ntStatus)); + break; + } + + FoundCategoryAudio = TRUE; + continue; + } + + ASSERT(FoundNodeType == FALSE); + if (FoundNodeType) + { + ntStatus = STATUS_INVALID_DEVICE_STATE; + DPF(D_ERROR, ("UpdateCustomEndpointCategory: Found more than one applicable Pin, 0x%x", ntStatus)); + break; + } + + pCustomMinipairTopoPins[i].KsPinDescriptor.Category = pCategory; + FoundNodeType = TRUE; + ntStatus = STATUS_SUCCESS; + } + + return ntStatus; +} +//============================================================================= +#pragma code_seg("PAGE") +VOID +BthHfpDevice::DeleteCustomEndpointMinipair +( + _In_ PENDPOINT_MINIPAIR pCustomMinipair +) +{ + PAGED_CODE(); + + if (pCustomMinipair != NULL) + { + if (pCustomMinipair->TopoInterfaceProperties != NULL) + { + ExFreePoolWithTag(const_cast(pCustomMinipair->TopoInterfaceProperties), SYSVAD_POOLTAG); + pCustomMinipair->TopoInterfaceProperties = NULL; + } + if (pCustomMinipair->TopoDescriptor != NULL) + { + if (pCustomMinipair->TopoDescriptor->Pins != NULL) + { + ExFreePoolWithTag((PVOID)pCustomMinipair->TopoDescriptor->Pins, SYSVAD_POOLTAG); + pCustomMinipair->TopoDescriptor->Pins = NULL; + } + ExFreePoolWithTag(pCustomMinipair->TopoDescriptor, SYSVAD_POOLTAG); + pCustomMinipair->TopoDescriptor = NULL; + } + ExFreePoolWithTag(pCustomMinipair, SYSVAD_POOLTAG); + } +} + +//============================================================================= +#pragma code_seg("PAGE") +VOID +BthHfpDevice::Stop() +/*++ + +Routine Description: + + Asynchronously called to stop the audio device. + After returning from this function, there are no more async notifications + pending (volume, connection, etc.). + +--*/ +{ + PAGED_CODE(); + DPF_ENTER(("[BthHfpDevice::Stop]")); + + NTSTATUS ntStatus = STATUS_SUCCESS; + eBthHfpState state = eBthHfpStateInvalid; + + state = (eBthHfpState) InterlockedExchange((PLONG)&m_State, eBthHfpStateStopping); + ASSERT(state == eBthHfpStateRunning || state == eBthHfpStateFailed); + UNREFERENCED_VAR(state); + + // + // Stop async notifications. + // + WdfIoTargetPurge(m_WdfIoTarget, WdfIoTargetPurgeIoAndWait); + + // + // Wait for work-item. + // + WdfWorkItemFlush(m_WorkItem); + + // + // Remove the topology and wave render filters. + // + if (m_UnknownSpeakerTopology || m_UnknownSpeakerWave) + { + ntStatus = m_Adapter->RemoveEndpointFilters( + m_SpeakerMiniports, + m_UnknownSpeakerTopology, + m_UnknownSpeakerWave); + + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_ERROR, ("RemoveEndpointFilters (Bth HFP SCO-Bypass Speaker): failed, 0x%x", ntStatus)); + } + } + + // + // Remove the topology and wave capture filters. + // + if (m_UnknownMicTopology || m_UnknownMicWave) + { + ntStatus = m_Adapter->RemoveEndpointFilters( + m_MicMiniports, + m_UnknownMicTopology, + m_UnknownMicWave); + + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_ERROR, ("RemoveEndpointFilters (Bth HFP SCO-Bypass Capture): failed, 0x%x", ntStatus)); + } + } + + // + // Release port/miniport pointers. + // + SAFE_RELEASE(m_UnknownSpeakerTopology); + SAFE_RELEASE(m_UnknownSpeakerWave); + SAFE_RELEASE(m_UnknownMicTopology); + SAFE_RELEASE(m_UnknownMicWave); + + // + // The device is in the stopped state. + // + InterlockedExchange((PLONG)&m_State, eBthHfpStateStopped); + + DeleteCustomEndpointMinipair(m_SpeakerMiniports); + m_SpeakerMiniports = NULL; + + DeleteCustomEndpointMinipair(m_MicMiniports); + m_MicMiniports = NULL; + +} + +#endif // SYSVAD_BTH_BYPASS + + diff --git a/audio/sysvad/common.h b/audio/sysvad/common.h new file mode 100644 index 000000000..4e4e8a018 --- /dev/null +++ b/audio/sysvad/common.h @@ -0,0 +1,622 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + Common.h + +Abstract: + + CAdapterCommon class declaration. + +--*/ + +#ifndef _SYSVAD_COMMON_H_ +#define _SYSVAD_COMMON_H_ + +#define HNSTIME_PER_MILLISECOND 10000 + +//============================================================================= +// Macros +//============================================================================= + +#define UNREFERENCED_VAR(status) \ + status = status + +//------------------------------------------------------------------------- +// Description: +// +// If the condition evaluates to TRUE, jump to the given label. +// +// Parameters: +// +// condition - [in] Code that fits in if statement +// label - [in] label to jump if condition is met +// +#define IF_TRUE_JUMP(condition, label) \ + if (condition) \ + { \ + goto label; \ + } + +//------------------------------------------------------------------------- +// Description: +// +// If the condition evaluates to TRUE, perform the given statement +// then jump to the given label. +// +// Parameters: +// +// condition - [in] Code that fits in if statement +// action - [in] action to perform in body of if statement +// label - [in] label to jump if condition is met +// +#define IF_TRUE_ACTION_JUMP(condition, action, label) \ + if (condition) \ + { \ + action; \ + goto label; \ + } + +//------------------------------------------------------------------------- +// Description: +// +// If the ntStatus is not NT_SUCCESS, perform the given statement then jump to +// the given label. +// +// Parameters: +// +// ntStatus - [in] Value to check +// action - [in] action to perform in body of if statement +// label - [in] label to jump if condition is met +// +#define IF_FAILED_ACTION_JUMP(ntStatus, action, label) \ + if (!NT_SUCCESS(ntStatus)) \ + { \ + action; \ + goto label; \ + } + +//------------------------------------------------------------------------- +// Description: +// +// If the ntStatus passed is not NT_SUCCESS, jump to the given label. +// +// Parameters: +// +// ntStatus - [in] Value to check +// label - [in] label to jump if condition is met +// +#define IF_FAILED_JUMP(ntStatus, label) \ + if (!NT_SUCCESS(ntStatus)) \ + { \ + goto label; \ + } + +#define SAFE_RELEASE(p) {if (p) { (p)->Release(); (p) = nullptr; } } + +// JACKDESC_RGB(r, g, b) +#define JACKDESC_RGB(r, g, b) \ + ((COLORREF)((r << 16) | (g << 8) | (b))) + +// Min/Max defines. +#define MIN(x, y) ((x) < (y) ? (x) : (y)) +#define MAX(x, y) ((x) > (y) ? (x) : (y)) + +#define MINWAVERT_POOLTAG 'RWNM' +#define MINTOPORT_POOLTAG 'RTNM' +#define MINADAPTER_POOLTAG 'uAyS' + +typedef enum +{ + eSpeakerDevice = 0, + eSpeakerHpDevice, + eHdmiRenderDevice, + eMicInDevice, + eMicArrayDevice1, + eMicArrayDevice2, + eMicArrayDevice3, + eBthHfpSpeakerDevice, + eBthHfpMicDevice, + eCellularDevice, + eHandsetSpeakerDevice, + eHandsetMicDevice, + eSpeakerHsDevice, + eMicHsDevice, + eFmRxDevice, + eSpdifRenderDevice, + eMaxDeviceType + +} eDeviceType; + +// +// Signal processing modes and default formats structs. +// +typedef struct _MODE_AND_DEFAULT_FORMAT { + GUID Mode; + KSDATAFORMAT* DefaultFormat; +} MODE_AND_DEFAULT_FORMAT, *PMODE_AND_DEFAULT_FORMAT; + +// +// Enumeration of the various types of pins implemented in this driver. +// +typedef enum +{ + NoPin, + BridgePin, + SystemRenderPin, + OffloadRenderPin, + RenderLoopbackPin, + SystemCapturePin, + KeywordCapturePin, + TelephonyBidiPin, +} PINTYPE; + +// +// PIN_DEVICE_FORMATS_AND_MODES +// +// Used to specify a pin's type (e.g. system, offload, etc.), formats, and +// modes. Conceptually serves similar purpose as the PCPIN_DESCRIPTOR to +// define a pin, but is more specific to driver implementation. +// +// Arrays of these structures follow the same order as the filter's +// pin descriptor array so that KS pin IDs can serve as an index. +// +typedef struct _PIN_DEVICE_FORMATS_AND_MODES +{ + PINTYPE PinType; + + KSDATAFORMAT_WAVEFORMATEXTENSIBLE * WaveFormats; + ULONG WaveFormatsCount; + + MODE_AND_DEFAULT_FORMAT * ModeAndDefaultFormat; + ULONG ModeAndDefaultFormatCount; + +} PIN_DEVICE_FORMATS_AND_MODES, *PPIN_DEVICE_FORMATS_AND_MODES; + +// forward declaration. +typedef struct _ENDPOINT_MINIPAIR *PENDPOINT_MINIPAIR; + +// both wave & topology miniport create function prototypes have this form: +typedef HRESULT (*PFNCREATEMINIPORT)( + _Out_ PUNKNOWN * Unknown, + _In_ REFCLSID, + _In_opt_ PUNKNOWN UnknownOuter, + _When_((PoolType & NonPagedPoolMustSucceed) != 0, + __drv_reportError("Must succeed pool allocations are forbidden. " + "Allocation failures cause a system crash")) + _In_ POOL_TYPE PoolType, + _In_ PUNKNOWN UnknownAdapter, + _In_opt_ PVOID DeviceContext, + _In_ PENDPOINT_MINIPAIR MiniportPair +); + +//============================================================================= +// +//============================================================================= +typedef struct _SYSVAD_DEVPROPERTY { + const DEVPROPKEY *PropertyKey; + DEVPROPTYPE Type; + ULONG BufferSize; + __field_bcount_opt(BufferSize) PVOID Buffer; +} SYSVAD_DEVPROPERTY, PSYSVAD_DEVPROPERTY; + +#define ENDPOINT_NO_FLAGS 0x00000000 +#define ENDPOINT_OFFLOAD_SUPPORTED 0x00000001 +#define ENDPOINT_SOUNDDETECTOR_SUPPORTED 0x00000002 +#define ENDPOINT_CELLULAR_PROVIDER1 0x00000010 +#define ENDPOINT_CELLULAR_PROVIDER2 0x00000020 + +// +// Endpoint miniport pair (wave/topology) descriptor. +// +typedef struct _ENDPOINT_MINIPAIR +{ + eDeviceType DeviceType; + + // Topology miniport. + PWSTR TopoName; // make sure this name matches with SYSVAD..szPname in the inf's [Strings] section + PFNCREATEMINIPORT TopoCreateCallback; + PCFILTER_DESCRIPTOR* TopoDescriptor; + ULONG TopoInterfacePropertyCount; + const SYSVAD_DEVPROPERTY* TopoInterfaceProperties; + + // Wave RT miniport. + PWSTR WaveName; // make sure this name matches with SYSVAD..szPname in the inf's [Strings] section + PFNCREATEMINIPORT WaveCreateCallback; + PCFILTER_DESCRIPTOR* WaveDescriptor; + ULONG WaveInterfacePropertyCount; + const SYSVAD_DEVPROPERTY* WaveInterfaceProperties; + + USHORT DeviceMaxChannels; + PIN_DEVICE_FORMATS_AND_MODES* PinDeviceFormatsAndModes; + ULONG PinDeviceFormatsAndModesCount; + + // Miniport physical connections. + PHYSICALCONNECTIONTABLE* PhysicalConnections; + ULONG PhysicalConnectionCount; + + // General endpoint flags (one of more ENDPOINT_, see above) + ULONG DeviceFlags; +} ENDPOINT_MINIPAIR; + +//============================================================================= +// Defines +//============================================================================= + +DEFINE_GUID(IID_IAdapterCommon, +0x7eda2950, 0xbf9f, 0x11d0, 0x87, 0x1f, 0x0, 0xa0, 0xc9, 0x11, 0xb5, 0x44); + +//============================================================================= +// Interfaces +//============================================================================= + +/////////////////////////////////////////////////////////////////////////////// +// IAdapterCommon +// +DECLARE_INTERFACE_(IAdapterCommon, IUnknown) +{ + STDMETHOD_(NTSTATUS, Init) + ( + THIS_ + _In_ PDEVICE_OBJECT DeviceObject + ) PURE; + + STDMETHOD_(PDEVICE_OBJECT, GetDeviceObject) + ( + THIS + ) PURE; + + STDMETHOD_(PDEVICE_OBJECT, GetPhysicalDeviceObject) + ( + THIS + ) PURE; + + STDMETHOD_(WDFDEVICE, GetWdfDevice) + ( + THIS + ) PURE; + + STDMETHOD_(VOID, SetWaveServiceGroup) + ( + THIS_ + _In_ PSERVICEGROUP ServiceGroup + ) PURE; + + STDMETHOD_(BOOL, bDevSpecificRead) + ( + THIS_ + ) PURE; + + STDMETHOD_(VOID, bDevSpecificWrite) + ( + THIS_ + _In_ BOOL bDevSpecific + ); + + STDMETHOD_(INT, iDevSpecificRead) + ( + THIS_ + ) PURE; + + STDMETHOD_(VOID, iDevSpecificWrite) + ( + THIS_ + _In_ INT iDevSpecific + ); + + STDMETHOD_(UINT, uiDevSpecificRead) + ( + THIS_ + ) PURE; + + STDMETHOD_(VOID, uiDevSpecificWrite) + ( + THIS_ + _In_ UINT uiDevSpecific + ); + + STDMETHOD_(BOOL, MixerMuteRead) + ( + THIS_ + _In_ ULONG Index, + _In_ ULONG Channel + ) PURE; + + STDMETHOD_(VOID, MixerMuteWrite) + ( + THIS_ + _In_ ULONG Index, + _In_ ULONG Channel, + _In_ BOOL Value + ); + + STDMETHOD_(ULONG, MixerMuxRead) + ( + THIS + ); + + STDMETHOD_(VOID, MixerMuxWrite) + ( + THIS_ + _In_ ULONG Index + ); + + STDMETHOD_(LONG, MixerVolumeRead) + ( + THIS_ + _In_ ULONG Index, + _In_ ULONG Channel + ) PURE; + + STDMETHOD_(VOID, MixerVolumeWrite) + ( + THIS_ + _In_ ULONG Index, + _In_ ULONG Channel, + _In_ LONG Value + ) PURE; + + STDMETHOD_(LONG, MixerPeakMeterRead) + ( + THIS_ + _In_ ULONG Index, + _In_ ULONG Channel + ) PURE; + + STDMETHOD_(VOID, MixerReset) + ( + THIS + ) PURE; + + STDMETHOD_(NTSTATUS, WriteEtwEvent) + ( + THIS_ + _In_ EPcMiniportEngineEvent miniportEventType, + _In_ ULONGLONG ullData1, + _In_ ULONGLONG ullData2, + _In_ ULONGLONG ullData3, + _In_ ULONGLONG ullData4 + ) PURE; + + STDMETHOD_(VOID, SetEtwHelper) + ( + THIS_ + PPORTCLSETWHELPER _pPortClsEtwHelper + ) PURE; + + STDMETHOD_(NTSTATUS, InstallSubdevice) + ( + _In_opt_ PIRP Irp, + _In_ PWSTR Name, + _In_ REFGUID PortClassId, + _In_ REFGUID MiniportClassId, + _In_opt_ PFNCREATEMINIPORT MiniportCreate, + _In_ ULONG cPropertyCount, + _In_reads_opt_(cPropertyCount) const SYSVAD_DEVPROPERTY * pProperties, + _In_opt_ PVOID DeviceContext, + _In_ PENDPOINT_MINIPAIR MiniportPair, + _In_opt_ PRESOURCELIST ResourceList, + _In_ REFGUID PortInterfaceId, + _Out_opt_ PUNKNOWN * OutPortInterface, + _Out_opt_ PUNKNOWN * OutPortUnknown, + _Out_opt_ PUNKNOWN * OutMiniportUnknown + ); + + STDMETHOD_(NTSTATUS, UnregisterSubdevice) + ( + THIS_ + _In_opt_ PUNKNOWN UnknownPort + ); + + STDMETHOD_(NTSTATUS, ConnectTopologies) + ( + THIS_ + _In_ PUNKNOWN UnknownTopology, + _In_ PUNKNOWN UnknownWave, + _In_ PHYSICALCONNECTIONTABLE* PhysicalConnections, + _In_ ULONG PhysicalConnectionCount + ); + + STDMETHOD_(NTSTATUS, DisconnectTopologies) + ( + THIS_ + _In_ PUNKNOWN UnknownTopology, + _In_ PUNKNOWN UnknownWave, + _In_ PHYSICALCONNECTIONTABLE* PhysicalConnections, + _In_ ULONG PhysicalConnectionCount + ); + + STDMETHOD_(NTSTATUS, InstallEndpointFilters) + ( + THIS_ + _In_opt_ PIRP Irp, + _In_ PENDPOINT_MINIPAIR MiniportPair, + _In_opt_ PVOID DeviceContext, + _Out_opt_ PUNKNOWN * UnknownTopology, + _Out_opt_ PUNKNOWN * UnknownWave + ); + + STDMETHOD_(NTSTATUS, RemoveEndpointFilters) + ( + THIS_ + _In_ PENDPOINT_MINIPAIR MiniportPair, + _In_opt_ PUNKNOWN UnknownTopology, + _In_opt_ PUNKNOWN UnknownWave + ); + + STDMETHOD_(NTSTATUS, GetFilters) + ( + THIS_ + _In_ PENDPOINT_MINIPAIR MiniportPair, + _Out_opt_ PUNKNOWN *UnknownTopologyPort, + _Out_opt_ PUNKNOWN *UnknownTopologyMiniport, + _Out_opt_ PUNKNOWN *UnknownWavePort, + _Out_opt_ PUNKNOWN *UnknownWaveMiniport + ); + + STDMETHOD_(NTSTATUS, SetIdlePowerManagement) + ( + THIS_ + _In_ PENDPOINT_MINIPAIR MiniportPair, + _In_ BOOL bEnable + ); + +#ifdef SYSVAD_BTH_BYPASS + STDMETHOD_(NTSTATUS, InitBthScoBypass)(); + +#endif // SYSVAD_BTH_BYPASS + + STDMETHOD_(VOID, Cleanup)(); +}; + +typedef IAdapterCommon *PADAPTERCOMMON; + +//============================================================================= +// Function Prototypes +//============================================================================= +NTSTATUS +NewAdapterCommon +( + _Out_ PUNKNOWN * Unknown, + _In_ REFCLSID, + _In_opt_ PUNKNOWN UnknownOuter, + _When_((PoolType & NonPagedPoolMustSucceed) != 0, + __drv_reportError("Must succeed pool allocations are forbidden. " + "Allocation failures cause a system crash")) + _In_ POOL_TYPE PoolType +); + + +#ifdef SYSVAD_BTH_BYPASS + +// Event callback definition. +typedef VOID (*PFNEVENTNOTIFICATION)( + _In_opt_ PVOID Context +); + +DEFINE_GUID(IID_IBthHfpDeviceCommon, +0x576b824a, 0x5248, 0x47b1, 0x82, 0xc5, 0xe4, 0x7b, 0xa7, 0xe2, 0xaf, 0x2b); + +//============================================================================= +// Interfaces +//============================================================================= + +/////////////////////////////////////////////////////////////////////////////// +// IAdapterCommon +// +DECLARE_INTERFACE_(IBthHfpDeviceCommon, IUnknown) +{ + STDMETHOD_(BOOL, IsVolumeSupported) + ( + THIS_ + ) PURE; + + STDMETHOD_(PKSPROPERTY_VALUES, GetVolumeSettings) + ( + THIS_ + _Out_ PULONG Size + ) PURE; + + STDMETHOD_(LONG, GetSpeakerVolume) + ( + THIS_ + ) PURE; + + STDMETHOD_(NTSTATUS, SetSpeakerVolume) + ( + THIS_ + _In_ ULONG Volume + ) PURE; + + STDMETHOD_(LONG, GetMicVolume) + ( + THIS_ + ) PURE; + + STDMETHOD_(NTSTATUS, SetMicVolume) + ( + THIS_ + _In_ ULONG Volume + ) PURE; + + STDMETHOD_(BOOL, GetConnectionStatus) + ( + THIS_ + ) PURE; + + STDMETHOD_(NTSTATUS, Connect) + ( + THIS_ + ) PURE; + + STDMETHOD_(NTSTATUS, Disconnect) + ( + THIS_ + ) PURE; + + STDMETHOD_(BOOL, GetStreamStatus) + ( + THIS_ + ) PURE; + + STDMETHOD_(NTSTATUS, StreamOpen) + ( + THIS_ + ) PURE; + + STDMETHOD_(NTSTATUS, StreamClose) + ( + THIS_ + ) PURE; + + STDMETHOD_(GUID, GetContainerId) + ( + THIS_ + ) PURE; + + STDMETHOD_(VOID, SetSpeakerVolumeHandler) + ( + THIS_ + _In_opt_ PFNEVENTNOTIFICATION EventHandler, + _In_opt_ PVOID EventHandlerContext + ) PURE; + + STDMETHOD_(VOID, SetSpeakerConnectionStatusHandler) + ( + THIS_ + _In_opt_ PFNEVENTNOTIFICATION EventHandler, + _In_opt_ PVOID EventHandlerContext + ) PURE; + + STDMETHOD_(VOID, SetMicVolumeHandler) + ( + THIS_ + _In_opt_ PFNEVENTNOTIFICATION EventHandler, + _In_opt_ PVOID EventHandlerContext + ) PURE; + + STDMETHOD_(VOID, SetMicConnectionStatusHandler) + ( + THIS_ + _In_opt_ PFNEVENTNOTIFICATION EventHandler, + _In_opt_ PVOID EventHandlerContext + ) PURE; + + STDMETHOD_(BOOL, IsNRECSupported) + ( + THIS_ + ) PURE; + + STDMETHOD_(BOOL, GetNRECDisableStatus) + ( + THIS_ + ) PURE; +}; +typedef IBthHfpDeviceCommon *PBTHHFPDEVICECOMMON; + +#endif // SYSVAD_BTH_BYPASS + +#endif //_SYSVAD_COMMON_H_ + diff --git a/audio/sysvad/hw.cpp b/audio/sysvad/hw.cpp new file mode 100644 index 000000000..2ba0d5141 --- /dev/null +++ b/audio/sysvad/hw.cpp @@ -0,0 +1,448 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + hw.cpp + +Abstract: + + Implementation of SYSVAD HW class. + SYSVAD HW has an array for storing mixer and volume settings + for the topology. + + +--*/ +#include +#include "hw.h" + +//============================================================================= +// CSYSVADHW +//============================================================================= + +//============================================================================= +#pragma code_seg("PAGE") +CSYSVADHW::CSYSVADHW() +: m_ulMux(0), + m_bDevSpecific(FALSE), + m_iDevSpecific(0), + m_uiDevSpecific(0) +/*++ + +Routine Description: + + Constructor for SYSVADHW. + +Arguments: + +Return Value: + + void + +--*/ +{ + PAGED_CODE(); + + MixerReset(); +} // CSYSVADHW +#pragma code_seg() + + +//============================================================================= +BOOL +CSYSVADHW::bGetDevSpecific() +/*++ + +Routine Description: + + Gets the HW (!) Device Specific info + +Arguments: + + N/A + +Return Value: + + True or False (in this example). + +--*/ +{ + return m_bDevSpecific; +} // bGetDevSpecific + +//============================================================================= +void +CSYSVADHW::bSetDevSpecific +( + _In_ BOOL bDevSpecific +) +/*++ + +Routine Description: + + Sets the HW (!) Device Specific info + +Arguments: + + fDevSpecific - true or false for this example. + +Return Value: + + void + +--*/ +{ + m_bDevSpecific = bDevSpecific; +} // bSetDevSpecific + +//============================================================================= +INT +CSYSVADHW::iGetDevSpecific() +/*++ + +Routine Description: + + Gets the HW (!) Device Specific info + +Arguments: + + N/A + +Return Value: + + int (in this example). + +--*/ +{ + return m_iDevSpecific; +} // iGetDevSpecific + +//============================================================================= +void +CSYSVADHW::iSetDevSpecific +( + _In_ INT iDevSpecific +) +/*++ + +Routine Description: + + Sets the HW (!) Device Specific info + +Arguments: + + fDevSpecific - true or false for this example. + +Return Value: + + void + +--*/ +{ + m_iDevSpecific = iDevSpecific; +} // iSetDevSpecific + +//============================================================================= +UINT +CSYSVADHW::uiGetDevSpecific() +/*++ + +Routine Description: + + Gets the HW (!) Device Specific info + +Arguments: + + N/A + +Return Value: + + UINT (in this example). + +--*/ +{ + return m_uiDevSpecific; +} // uiGetDevSpecific + +//============================================================================= +void +CSYSVADHW::uiSetDevSpecific +( + _In_ UINT uiDevSpecific +) +/*++ + +Routine Description: + + Sets the HW (!) Device Specific info + +Arguments: + + uiDevSpecific - int for this example. + +Return Value: + + void + +--*/ +{ + m_uiDevSpecific = uiDevSpecific; +} // uiSetDevSpecific + + +//============================================================================= +BOOL +CSYSVADHW::GetMixerMute +( + _In_ ULONG ulNode, + _In_ ULONG ulChannel +) +/*++ + +Routine Description: + + Gets the HW (!) mute levels for SYSVAD + +Arguments: + + ulNode - topology node id + + ulChannel - which channel are we reading? + +Return Value: + + mute setting + +--*/ +{ + UNREFERENCED_PARAMETER(ulChannel); + + if (ulNode < MAX_TOPOLOGY_NODES) + { + return m_MuteControls[ulNode]; + } + + return 0; +} // GetMixerMute + +//============================================================================= +ULONG +CSYSVADHW::GetMixerMux() +/*++ + +Routine Description: + + Return the current mux selection + +Arguments: + +Return Value: + + ULONG + +--*/ +{ + return m_ulMux; +} // GetMixerMux + +//============================================================================= +LONG +CSYSVADHW::GetMixerVolume +( + _In_ ULONG ulNode, + _In_ ULONG ulChannel +) +/*++ + +Routine Description: + + Gets the HW (!) volume for SYSVAD. + +Arguments: + + ulNode - topology node id + + ulChannel - which channel are we reading? + +Return Value: + + LONG - volume level + +--*/ +{ + UNREFERENCED_PARAMETER(ulChannel); + + if (ulNode < MAX_TOPOLOGY_NODES) + { + return m_VolumeControls[ulNode]; + } + + return 0; +} // GetMixerVolume + +//============================================================================= +LONG +CSYSVADHW::GetMixerPeakMeter +( + _In_ ULONG ulNode, + _In_ ULONG ulChannel +) +/*++ + +Routine Description: + + Gets the HW (!) peak meter for SYSVAD. + +Arguments: + + ulNode - topology node id + + ulChannel - which channel are we reading? + +Return Value: + + LONG - sample peak meter level + +--*/ +{ + UNREFERENCED_PARAMETER(ulChannel); + + if (ulNode < MAX_TOPOLOGY_NODES) + { + return m_PeakMeterControls[ulNode]; + } + + return 0; +} // GetMixerVolume + +//============================================================================= +#pragma code_seg("PAGE") +void +CSYSVADHW::MixerReset() +/*++ + +Routine Description: + + Resets the mixer registers. + +Arguments: + +Return Value: + + void + +--*/ +{ + PAGED_CODE(); + + RtlFillMemory(m_VolumeControls, sizeof(LONG) * MAX_TOPOLOGY_NODES, 0xFF); + RtlFillMemory(m_MuteControls, sizeof(BOOL) * MAX_TOPOLOGY_NODES, TRUE); + + for (ULONG i=0; iMajorFormat, + KSDATAFORMAT_TYPE_AUDIO) && + ( IsEqualGUIDAligned(pDataFormat->Specifier, + KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) || + IsEqualGUIDAligned(pDataFormat->Specifier, + KSDATAFORMAT_SPECIFIER_DSOUND) ) ) + ) + { + pWfx = PWAVEFORMATEX(pDataFormat + 1); + + if (IsEqualGUIDAligned(pDataFormat->Specifier, + KSDATAFORMAT_SPECIFIER_DSOUND)) + { + PKSDSOUND_BUFFERDESC pwfxds; + + pwfxds = PKSDSOUND_BUFFERDESC(pDataFormat + 1); + pWfx = &pwfxds->WaveFormatEx; + } + } + + return pWfx; +} // GetWaveFormatEx + +//----------------------------------------------------------------------------- +#pragma code_seg("PAGE") +NTSTATUS +ValidatePropertyParams +( + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG cbValueSize, + _In_ ULONG cbInstanceSize /* = 0 */ +) +/*++ + +Routine Description: + + Validates property parameters. + +Arguments: + + PropertyRequest - + cbValueSize - + cbInstanceSize - + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + NTSTATUS ntStatus = STATUS_UNSUCCESSFUL; + + if (PropertyRequest && cbValueSize) + { + // If the caller is asking for ValueSize. + // + if (0 == PropertyRequest->ValueSize) + { + PropertyRequest->ValueSize = cbValueSize; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + // If the caller passed an invalid ValueSize. + // + else if (PropertyRequest->ValueSize < cbValueSize) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else if (PropertyRequest->InstanceSize < cbInstanceSize) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + // If all parameters are OK. + // + else if (PropertyRequest->ValueSize >= cbValueSize) + { + if (PropertyRequest->Value) + { + ntStatus = STATUS_SUCCESS; + // + // Caller should set ValueSize, if the property + // call is successful. + // + } + } + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + + // Clear the ValueSize if unsuccessful. + // + if (PropertyRequest && + STATUS_SUCCESS != ntStatus && + STATUS_BUFFER_OVERFLOW != ntStatus) + { + PropertyRequest->ValueSize = 0; + } + + return ntStatus; +} // ValidatePropertyParams + +//----------------------------------------------------------------------------- +#pragma code_seg("PAGE") +NTSTATUS +SysvadPropertyDispatch +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + Handles and dispatches a SYSVADPROPERTY_ITEM. + + Use this as the property handler only if the property item is a + SYSVADPROPERTY_ITEM. +--*/ +{ + PAGED_CODE(); + + SYSVADPROPERTY_ITEM* item = (SYSVADPROPERTY_ITEM*)PropertyRequest->PropertyItem; + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + if (item->SupportHandler != nullptr) + { + return item->SupportHandler(PropertyRequest); + } + else + { + return PropertyHandler_BasicSupport(PropertyRequest, PropertyRequest->PropertyItem->Flags, VT_ILLEGAL); + } + } + + // Verify instance data size + if (PropertyRequest->InstanceSize < item->MinProperty) + { + return STATUS_INVALID_PARAMETER; + } + + ULONG cbMinSize = item->MinData; + + // Verify value size + if (PropertyRequest->ValueSize == 0) + { + PropertyRequest->ValueSize = cbMinSize; + return STATUS_BUFFER_OVERFLOW; + } + if (PropertyRequest->ValueSize < cbMinSize) + { + PropertyRequest->ValueSize = 0; + return STATUS_BUFFER_TOO_SMALL; + } + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + if (item->GetHandler != nullptr) + { + return item->GetHandler(PropertyRequest); + } + else + { + return STATUS_NOT_SUPPORTED; + } + } + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_SET) + { + if (item->SetHandler != nullptr) + { + return item->SetHandler(PropertyRequest); + } + else + { + return STATUS_NOT_SUPPORTED; + } + } + + return STATUS_INVALID_DEVICE_REQUEST; +} + +//----------------------------------------------------------------------------- +#pragma code_seg("PAGE") +NTSTATUS +PropertyHandler_BasicSupport +( + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG Flags, + _In_ DWORD PropTypeSetId +) +/*++ + +Routine Description: + + Default basic support handler. Basic processing depends on the size of data. + For ULONG it only returns Flags. For KSPROPERTY_DESCRIPTION, the structure + is filled. + +Arguments: + + PropertyRequest - + + Flags - Support flags. + + PropTypeSetId - PropTypeSetId + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + ASSERT(Flags & KSPROPERTY_TYPE_BASICSUPPORT); + + NTSTATUS ntStatus = STATUS_INVALID_PARAMETER; + + if (PropertyRequest->ValueSize >= sizeof(KSPROPERTY_DESCRIPTION)) + { + // if return buffer can hold a KSPROPERTY_DESCRIPTION, return it + // + PKSPROPERTY_DESCRIPTION PropDesc = + PKSPROPERTY_DESCRIPTION(PropertyRequest->Value); + + PropDesc->AccessFlags = Flags; + PropDesc->DescriptionSize = sizeof(KSPROPERTY_DESCRIPTION); + if (VT_ILLEGAL != PropTypeSetId) + { + PropDesc->PropTypeSet.Set = KSPROPTYPESETID_General; + PropDesc->PropTypeSet.Id = PropTypeSetId; + } + else + { + PropDesc->PropTypeSet.Set = GUID_NULL; + PropDesc->PropTypeSet.Id = 0; + } + PropDesc->PropTypeSet.Flags = 0; + PropDesc->MembersListCount = 0; + PropDesc->Reserved = 0; + + PropertyRequest->ValueSize = sizeof(KSPROPERTY_DESCRIPTION); + ntStatus = STATUS_SUCCESS; + } + else if (PropertyRequest->ValueSize >= sizeof(ULONG)) + { + // if return buffer can hold a ULONG, return the access flags + // + *(PULONG(PropertyRequest->Value)) = Flags; + + PropertyRequest->ValueSize = sizeof(ULONG); + ntStatus = STATUS_SUCCESS; + } + else + { + PropertyRequest->ValueSize = 0; + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + + return ntStatus; +} // PropertyHandler_BasicSupport + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +PropertyHandler_BasicSupportVolume +( + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG MaxChannels +) +/*++ + +Routine Description: + + Handles BasicSupport for Volume nodes. + +Arguments: + + PropertyRequest - property request structure. + + MaxChannels - # of supported channels. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + NTSTATUS ntStatus = STATUS_SUCCESS; + ULONG cbFullProperty = + sizeof(KSPROPERTY_DESCRIPTION) + + sizeof(KSPROPERTY_MEMBERSHEADER) + + sizeof(KSPROPERTY_STEPPING_LONG) * MaxChannels; + + ASSERT(MaxChannels > 0); + + if (PropertyRequest->ValueSize >= (sizeof(KSPROPERTY_DESCRIPTION))) + { + PKSPROPERTY_DESCRIPTION PropDesc = + PKSPROPERTY_DESCRIPTION(PropertyRequest->Value); + + PropDesc->AccessFlags = KSPROPERTY_TYPE_ALL; + PropDesc->DescriptionSize = cbFullProperty; + PropDesc->PropTypeSet.Set = KSPROPTYPESETID_General; + PropDesc->PropTypeSet.Id = VT_I4; + PropDesc->PropTypeSet.Flags = 0; + PropDesc->MembersListCount = 1; + PropDesc->Reserved = 0; + + // if return buffer can also hold a range description, return it too + if(PropertyRequest->ValueSize >= cbFullProperty) + { + // fill in the members header + PKSPROPERTY_MEMBERSHEADER Members = + PKSPROPERTY_MEMBERSHEADER(PropDesc + 1); + + Members->MembersFlags = KSPROPERTY_MEMBER_STEPPEDRANGES; + Members->MembersSize = sizeof(KSPROPERTY_STEPPING_LONG); + Members->MembersCount = MaxChannels; + Members->Flags = KSPROPERTY_MEMBER_FLAG_BASICSUPPORT_MULTICHANNEL; + + // fill in the stepped range + PKSPROPERTY_STEPPING_LONG Range = + PKSPROPERTY_STEPPING_LONG(Members + 1); + + for (ULONG i=0; iValueSize = cbFullProperty; + } + else + { + PropertyRequest->ValueSize = sizeof(KSPROPERTY_DESCRIPTION); + } + } + else if(PropertyRequest->ValueSize >= sizeof(ULONG)) + { + // if return buffer can hold a ULONG, return the access flags + PULONG AccessFlags = PULONG(PropertyRequest->Value); + + PropertyRequest->ValueSize = sizeof(ULONG); + *AccessFlags = KSPROPERTY_TYPE_ALL; + } + else + { + PropertyRequest->ValueSize = 0; + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + + return ntStatus; +} // PropertyHandlerBasicSupportVolume + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +PropertyHandler_BasicSupportMute +( + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG MaxChannels +) +/*++ + +Routine Description: + + Handles BasicSupport for Mute nodes. + +Arguments: + + PropertyRequest - property request structure. + + MaxChannels - # of supported channels. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + NTSTATUS ntStatus = STATUS_SUCCESS; + ULONG cbFullProperty = + sizeof(KSPROPERTY_DESCRIPTION) + + sizeof(KSPROPERTY_MEMBERSHEADER) + + sizeof(KSPROPERTY_STEPPING_LONG) * MaxChannels; + + ASSERT(MaxChannels > 0); + + if (PropertyRequest->ValueSize >= (sizeof(KSPROPERTY_DESCRIPTION))) + { + PKSPROPERTY_DESCRIPTION PropDesc = + PKSPROPERTY_DESCRIPTION(PropertyRequest->Value); + + PropDesc->AccessFlags = KSPROPERTY_TYPE_ALL; + PropDesc->DescriptionSize = cbFullProperty; + PropDesc->PropTypeSet.Set = KSPROPTYPESETID_General; + PropDesc->PropTypeSet.Id = VT_BOOL; + PropDesc->PropTypeSet.Flags = 0; + PropDesc->MembersListCount = 1; + PropDesc->Reserved = 0; + + // if return buffer can also hold a range description, return it too + if(PropertyRequest->ValueSize >= cbFullProperty) + { + // fill in the members header + PKSPROPERTY_MEMBERSHEADER Members = + PKSPROPERTY_MEMBERSHEADER(PropDesc + 1); + + Members->MembersFlags = KSPROPERTY_MEMBER_STEPPEDRANGES; + Members->MembersSize = sizeof(KSPROPERTY_STEPPING_LONG); + Members->MembersCount = MaxChannels; + Members->Flags = KSPROPERTY_MEMBER_FLAG_BASICSUPPORT_MULTICHANNEL; + + // fill in the stepped range + PKSPROPERTY_STEPPING_LONG Range = + PKSPROPERTY_STEPPING_LONG(Members + 1); + + for (ULONG i=0; i true + Range[i].Reserved = 0; + } + + // set the return value size + PropertyRequest->ValueSize = cbFullProperty; + } + else + { + PropertyRequest->ValueSize = sizeof(KSPROPERTY_DESCRIPTION); + } + } + else if(PropertyRequest->ValueSize >= sizeof(ULONG)) + { + // if return buffer can hold a ULONG, return the access flags + PULONG AccessFlags = PULONG(PropertyRequest->Value); + + PropertyRequest->ValueSize = sizeof(ULONG); + *AccessFlags = KSPROPERTY_TYPE_ALL; + } + else + { + PropertyRequest->ValueSize = 0; + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + + return ntStatus; +} // PropertyHandlerBasicSupportVolume + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +PropertyHandler_BasicSupportPeakMeter2 +( + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG MaxChannels +) +/*++ + +Routine Description: + + Handles BasicSupport for peak meter nodes. + +Arguments: + + PropertyRequest - property request structure. + + MaxChannels - # of supported channels. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + NTSTATUS ntStatus = STATUS_SUCCESS; + ULONG cbFullProperty = + sizeof(KSPROPERTY_DESCRIPTION) + + sizeof(KSPROPERTY_MEMBERSHEADER) + + sizeof(KSPROPERTY_STEPPING_LONG) * MaxChannels; + + ASSERT(MaxChannels > 0); + + if (PropertyRequest->ValueSize >= (sizeof(KSPROPERTY_DESCRIPTION))) + { + PKSPROPERTY_DESCRIPTION PropDesc = + PKSPROPERTY_DESCRIPTION(PropertyRequest->Value); + + PropDesc->AccessFlags = KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT; + PropDesc->DescriptionSize = cbFullProperty; + PropDesc->PropTypeSet.Set = KSPROPTYPESETID_General; + PropDesc->PropTypeSet.Id = VT_I4; + PropDesc->PropTypeSet.Flags = 0; + PropDesc->MembersListCount = 1; + PropDesc->Reserved = 0; + + // if return buffer can also hold a range description, return it too + if(PropertyRequest->ValueSize >= cbFullProperty) + { + // fill in the members header + PKSPROPERTY_MEMBERSHEADER Members = + PKSPROPERTY_MEMBERSHEADER(PropDesc + 1); + + Members->MembersFlags = KSPROPERTY_MEMBER_STEPPEDRANGES; + Members->MembersSize = sizeof(KSPROPERTY_STEPPING_LONG); + Members->MembersCount = MaxChannels; + Members->Flags = KSPROPERTY_MEMBER_FLAG_BASICSUPPORT_MULTICHANNEL; + + // fill in the stepped range + PKSPROPERTY_STEPPING_LONG Range = + PKSPROPERTY_STEPPING_LONG(Members + 1); + + for (ULONG i=0; iValueSize = cbFullProperty; + } + else + { + PropertyRequest->ValueSize = sizeof(KSPROPERTY_DESCRIPTION); + } + } + else if(PropertyRequest->ValueSize >= sizeof(ULONG)) + { + // if return buffer can hold a ULONG, return the access flags + PULONG AccessFlags = PULONG(PropertyRequest->Value); + + PropertyRequest->ValueSize = sizeof(ULONG); + *AccessFlags = KSPROPERTY_TYPE_ALL; + } + else + { + PropertyRequest->ValueSize = 0; + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + + return ntStatus; +} // PropertyHandlerBasicSupportVolume + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +PropertyHandler_CpuResources +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +) +/*++ + +Routine Description: + + Processes KSPROPERTY_AUDIO_CPURESOURCES + +Arguments: + + PropertyRequest - property request structure. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + DPF_ENTER(("[%s]",__FUNCTION__)); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + ntStatus = ValidatePropertyParams(PropertyRequest, sizeof(ULONG)); + if (NT_SUCCESS(ntStatus)) + { + *(PULONG(PropertyRequest->Value)) = KSAUDIO_CPU_RESOURCES_NOT_HOST_CPU; + PropertyRequest->ValueSize = sizeof(ULONG); + } + } + else if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = + PropertyHandler_BasicSupport + ( + PropertyRequest, + KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT, + VT_UI4 + ); + } + + return ntStatus; +} // PropertyHandlerCpuResources + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +PropertyHandler_Volume +( + _In_ PADAPTERCOMMON AdapterCommon, + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG MaxChannels +) +/*++ + +Routine Description: + + Property handler for KSPROPERTY_AUDIO_VOLUMELEVEL + +Arguments: + + AdapterCommon - interface to the common adapter object. + + PropertyRequest - property request structure. + + MaxChannels - # of supported channels. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + DPF_ENTER(("[%s]",__FUNCTION__)); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + ULONG ulChannel; + PLONG plVolume; + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = PropertyHandler_BasicSupportVolume( + PropertyRequest, + MaxChannels); + } + else + { + ntStatus = + ValidatePropertyParams + ( + PropertyRequest, + sizeof(LONG), // volume value is a LONG + sizeof(ULONG) // instance is the channel number + ); + if (NT_SUCCESS(ntStatus)) + { + ulChannel = * (PULONG (PropertyRequest->Instance)); + plVolume = PLONG (PropertyRequest->Value); + + if (ulChannel >= MaxChannels && + ulChannel != ALL_CHANNELS_ID) + { + ntStatus = STATUS_INVALID_PARAMETER; + } + else if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + *plVolume = + AdapterCommon->MixerVolumeRead + ( + PropertyRequest->Node, + ulChannel == ALL_CHANNELS_ID ? 0 : ulChannel + ); + PropertyRequest->ValueSize = sizeof(ULONG); + } + else if (PropertyRequest->Verb & KSPROPERTY_TYPE_SET) + { + if (ALL_CHANNELS_ID == ulChannel) + { + for (ULONG i=0; iMixerVolumeWrite + ( + PropertyRequest->Node, + i, + VOLUME_NORMALIZE_IN_RANGE(*plVolume) + ); + } + } + else + { + AdapterCommon->MixerVolumeWrite + ( + PropertyRequest->Node, + ulChannel, + VOLUME_NORMALIZE_IN_RANGE(*plVolume) + ); + } + } + } + + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_TERSE, ("[%s - ntStatus=0x%08x]",__FUNCTION__,ntStatus)); + } + } + + return ntStatus; +} // PropertyHandlerVolume + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +PropertyHandler_Mute +( + _In_ PADAPTERCOMMON AdapterCommon, + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG MaxChannels +) +/*++ + +Routine Description: + + Property handler for KSPROPERTY_AUDIO_MUTE + +Arguments: + + AdapterCommon - interface to the common adapter object. + + PropertyRequest - property request structure. + + MaxChannels - # of supported channels. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + DPF_ENTER(("[%s]",__FUNCTION__)); + + NTSTATUS ntStatus; + ULONG ulChannel; + PBOOL pfMute; + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = PropertyHandler_BasicSupportMute( + PropertyRequest, + MaxChannels); + } + else + { + ntStatus = + ValidatePropertyParams + ( + PropertyRequest, + sizeof(BOOL), + sizeof(ULONG) + ); + if (NT_SUCCESS(ntStatus)) + { + ulChannel = * (PULONG (PropertyRequest->Instance)); + pfMute = PBOOL (PropertyRequest->Value); + + if (ulChannel >= MaxChannels && + ulChannel != ALL_CHANNELS_ID) + { + ntStatus = STATUS_INVALID_PARAMETER; + } + else if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + *pfMute = + AdapterCommon->MixerMuteRead + ( + PropertyRequest->Node, + ulChannel == ALL_CHANNELS_ID ? 0 : ulChannel + ); + PropertyRequest->ValueSize = sizeof(BOOL); + } + else if (PropertyRequest->Verb & KSPROPERTY_TYPE_SET) + { + if (ALL_CHANNELS_ID == ulChannel) + { + for (ULONG i=0; iMixerMuteWrite + ( + PropertyRequest->Node, + i, + (*pfMute) ? TRUE : FALSE + ); + } + } + else + { + AdapterCommon->MixerMuteWrite + ( + PropertyRequest->Node, + ulChannel, + (*pfMute) ? TRUE : FALSE + ); + } + } + } + + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_TERSE, ("[%s - ntStatus=0x%08x]",__FUNCTION__,ntStatus)); + } + } + + return ntStatus; +} // PropertyHandlerMute + +//============================================================================= +#pragma code_seg("PAGE") +NTSTATUS +PropertyHandler_PeakMeter2 +( + _In_ PADAPTERCOMMON AdapterCommon, + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG MaxChannels +) +/*++ + +Routine Description: + + Property handler for KSPROPERTY_AUDIO_PEAKMETER2 + +Arguments: + + AdapterCommon - interface to the common adapter object. + + PropertyRequest - property request structure. + + MaxChannels - # of supported channels. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + DPF_ENTER(("[%s]",__FUNCTION__)); + + NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST; + ULONG ulChannel; + PLONG plSample; + + if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) + { + ntStatus = PropertyHandler_BasicSupportPeakMeter2( + PropertyRequest, + MaxChannels); + } + else + { + ntStatus = + ValidatePropertyParams + ( + PropertyRequest, + sizeof(LONG), // sample value is a LONG + sizeof(ULONG) // instance is the channel number + ); + if (NT_SUCCESS(ntStatus)) + { + ulChannel = * (PULONG (PropertyRequest->Instance)); + plSample = PLONG (PropertyRequest->Value); + + if (ulChannel >= MaxChannels && + ulChannel != ALL_CHANNELS_ID) + { + ntStatus = STATUS_INVALID_PARAMETER; + } + else if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) + { + *plSample = + PEAKMETER_NORMALIZE_IN_RANGE( + AdapterCommon->MixerPeakMeterRead + ( + PropertyRequest->Node, + ulChannel == ALL_CHANNELS_ID ? 0 : ulChannel + )); + + PropertyRequest->ValueSize = sizeof(ULONG); + } + } + + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_TERSE, ("[%s - ntStatus=0x%08x]",__FUNCTION__,ntStatus)); + } + } + + return ntStatus; +} // PropertyHandlerVolume + + diff --git a/audio/sysvad/kshelper.h b/audio/sysvad/kshelper.h new file mode 100644 index 000000000..10c1cadcf --- /dev/null +++ b/audio/sysvad/kshelper.h @@ -0,0 +1,159 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + kshelper.h + +Abstract: + + Helper functions for sysvad + +--*/ +#ifndef _SYSVAD_KSHELPER_H_ +#define _SYSVAD_KSHELPER_H_ + +#include +#include + +PWAVEFORMATEX +GetWaveFormatEx +( + _In_ PKSDATAFORMAT pDataFormat +); + +NTSTATUS +ValidatePropertyParams +( + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG cbValueSize, + _In_ ULONG cbInstanceSize = 0 +); + +NTSTATUS +PropertyHandler_BasicSupport +( + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG Flags, + _In_ DWORD PropTypeSetId +); + +NTSTATUS +PropertyHandler_BasicSupportVolume +( + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG MaxChannels +); + +NTSTATUS +PropertyHandler_BasicSupportMute +( + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG MaxChannels +); + +NTSTATUS +PropertyHandler_BasicSupportPeakMeter2 +( + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG MaxChannels +); + +NTSTATUS +PropertyHandler_CpuResources +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +); + +NTSTATUS +PropertyHandler_Volume +( + _In_ PADAPTERCOMMON AdapterCommon, + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG MaxChannels +); + +NTSTATUS +PropertyHandler_Mute +( + _In_ PADAPTERCOMMON AdapterCommon, + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG MaxChannels +); + +NTSTATUS +PropertyHandler_PeakMeter2 +( + _In_ PADAPTERCOMMON AdapterCommon, + _In_ PPCPROPERTY_REQUEST PropertyRequest, + _In_ ULONG MaxChannels +); + +//============================================================================= +// Property helpers +//============================================================================= + +NTSTATUS +SysvadPropertyDispatch +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +); + +// Use this structure to define property items with extra data allowing easier +// definition of separate get, set, and support handlers dispatched through +// SysvadPropertyDispatch. +typedef struct +{ + PCPROPERTY_ITEM PropertyItem; // Standard PCPROPERTY_ITEM + ULONG MinProperty; // Minimum size of the property instance data + ULONG MinData; // Minimum size of the property value + PCPFNPROPERTY_HANDLER GetHandler; // Property get handler (NULL if GET not supported) + PCPFNPROPERTY_HANDLER SetHandler; // Property set handler (NULL if SET not supported) + PCPFNPROPERTY_HANDLER SupportHandler; // Property support handler (NULL for common handler) +} SYSVADPROPERTY_ITEM; + +// The following macros facilitate adding property handlers to a class, allowing +// easier declaration and definition of a "thunk" routine that directly handles +// the property request and calls into a class instance method. Note that as +// written, the thunk routine assumes PAGED_CODE. +#define DECLARE_CLASSPROPERTYHANDLER(theClass, theMethod) \ +NTSTATUS theClass##_##theMethod \ +( \ + _In_ PPCPROPERTY_REQUEST PropertyRequest \ +); + +#define DECLARE_PROPERTYHANDLER(theMethod) \ +NTSTATUS theMethod \ +( \ + _In_ PPCPROPERTY_REQUEST PropertyRequest \ +); + +#define DEFINE_CLASSPROPERTYHANDLER(theClass, theMethod) \ +NTSTATUS theClass##_##theMethod \ +( \ + _In_ PPCPROPERTY_REQUEST PropertyRequest \ +) \ +{ \ + NTSTATUS status; \ + \ + PAGED_CODE(); \ + \ + theClass* p = reinterpret_cast(PropertyRequest->MajorTarget); \ + \ + p->AddRef(); \ + status = p->theMethod(PropertyRequest); \ + p->Release(); \ + \ + return status; \ +} \ +NTSTATUS theClass::theMethod \ +( \ + _In_ PPCPROPERTY_REQUEST PropertyRequest \ +) + + + + +#endif // _SYSVAD_KSHELPER_H_ + diff --git a/audio/sysvad/phoneaudiosample.inf b/audio/sysvad/phoneaudiosample.inf new file mode 100644 index 0000000000000000000000000000000000000000..a949197af92f5a91f72089a2ead651353339a218 GIT binary patch literal 44094 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 fkCpk*Sbn(eKG^d0xR{nEd literal 0 HcmV?d00001 diff --git a/audio/sysvad/readme.htm b/audio/sysvad/readme.htm new file mode 100644 index 000000000..a5e3563d0 --- /dev/null +++ b/audio/sysvad/readme.htm @@ -0,0 +1,396 @@ + + + + + + + + +AEVAD Driver + + + + + + + +
+ +

Microsoft Audio Engine Virtual +Audio Device (AEVAD) Driver

+ +

SUMMARY

+ +

This is a sample driver that demonstrates + how to develop a WDM audio driver that exposes support for audio offloading. The + driver uses a "virtual audio device" instead of an +actual hardware-based adapter and highlights the different +aspects of the audio offloading WDM audio driver architecture.

+ +

Driver +developers can use the framework in this sample to implement offload support without concern +for hardware dependencies. The framework includes implementations of the +following interfaces:

+ +

·         +The +CAdapterCommon interface gives the miniports access +to virtual mixer hardware. It also implements the IAdapterPowerManagement +interface.

+ +

·         +The +CMiniportTopologySYSVAD interface is the base class for all sample topologies. It has +very basic common functions. In addition, this class contains common topology +property handlers.

+ +

The following table shows +the features that are implemented in the various subdirectories of this sample.

+ + + + + + + + + + +
+

Subdirectory

+
+

Description

+
+

aesamplewavert

+
+ Basic audio offloading driver with independent microphone support.
+ +

 

+ +

BUILDING THE SAMPLE

+ +

To build this sample, open the +VC++ project file (AESample.VcxProj) from the WDK sample content in Visual Studio Dev11.

WDK + +

The sample +must be manually installed.  It can be +installed via the devcon.exe tool, +which is available as a free download from http://support.microsoft.com/kb/311272, +or it can be installed programmatically via a third-party setup application.

+ +

Devcon source code is also +included elsewhere in the WDK as a sample.

+ +

DRIVER ISSUES

+ +

No known issues

+ +

CODE TOUR

+ +

File Manifest

+ +
File           Description
 
adapter.cpp    Connects the driver to the system
+
aevad.inf      Installation INF file for this sample
aevad.rc       Common resource file
+
aevadtest.h    Common header file for test cases
+
basetopo.cpp   Base topology class implementation
basetopo.h     Base topology class definition
common.cpp     Common object used by all miniport drivers
common.h       Header file for the common object
hw.cpp         Hardware abstraction of SYSVAD
hw.h           Hardware class definition
+
ihvprivatepropertyset.h Header for private KS property set example
kshelper.cpp   Kernel streaming utility function implementations
kshelper.h     Kernel streaming utility function definitions
sysvad.h        Common definitions
+
readme.htm     The documentation for this driver (this file)
sources.inc    Common sources file included by all sample drivers
+
unittestdata.h Definitions that can be used for unit testing
 
+ +

Top of page

+ +
 
+ + + + + +
+

 

+
+ +

© 2004 Microsoft Corporation

+ +
+ + + + diff --git a/audio/sysvad/savedata.cpp b/audio/sysvad/savedata.cpp new file mode 100644 index 000000000..d621a21a5 --- /dev/null +++ b/audio/sysvad/savedata.cpp @@ -0,0 +1,1015 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + savedata.cpp + +Abstract: + + Implementation of SYSVAD data saving class. + + To save the playback data to disk, this class maintains a circular data + buffer, associated frame structures and worker items to save frames to + disk. + Each frame structure represents a portion of buffer. When that portion + of frame is full, a workitem is scheduled to save it to disk. + + + +--*/ +#pragma warning (disable : 4127) +#pragma warning (disable : 26165) + +#include +#include "savedata.h" +#include // This is for using RtlStringcbPrintf + +#define SAVEDATA_POOLTAG 'TDVS' +#define SAVEDATA_POOLTAG1 '1DVS' +#define SAVEDATA_POOLTAG2 '2DVS' +#define SAVEDATA_POOLTAG3 '3DVS' +#define SAVEDATA_POOLTAG4 '4DVS' +#define SAVEDATA_POOLTAG5 '5DVS' +#define SAVEDATA_POOLTAG6 '6DVS' +#define SAVEDATA_POOLTAG7 '7DVS' + +//============================================================================= +// Defines +//============================================================================= +#define RIFF_TAG 0x46464952; +#define WAVE_TAG 0x45564157; +#define FMT__TAG 0x20746D66; +#define DATA_TAG 0x61746164; + +#define DEFAULT_FRAME_COUNT 2 +#define DEFAULT_FRAME_SIZE PAGE_SIZE * 4 +#define DEFAULT_BUFFER_SIZE DEFAULT_FRAME_SIZE * DEFAULT_FRAME_COUNT + +#define DEFAULT_FILE_NAME L"\\DosDevices\\C:\\STREAM" +#define OFFLOAD_FILE_NAME L"OFFLOAD" +#define HOST_FILE_NAME L"HOST" + +#define MAX_WORKER_ITEM_COUNT 15 + +//============================================================================= +// Statics +//============================================================================= +ULONG CSaveData::m_ulStreamId = 0; +ULONG CSaveData::m_ulOffloadStreamId = 0; + +#pragma code_seg("PAGE") +//============================================================================= +// CSaveData +//============================================================================= + +//============================================================================= +CSaveData::CSaveData() +: m_pDataBuffer(NULL), + m_FileHandle(NULL), + m_ulFrameCount(DEFAULT_FRAME_COUNT), + m_ulBufferSize(DEFAULT_BUFFER_SIZE), + m_ulFrameSize(DEFAULT_FRAME_SIZE), + m_ulBufferOffset(0), + m_ulFrameIndex(0), + m_fFrameUsed(NULL), + m_waveFormat(NULL), + m_pFilePtr(NULL), + m_fWriteDisabled(FALSE), + m_bInitialized(FALSE) +{ + PAGED_CODE(); + + m_FileHeader.dwRiff = RIFF_TAG; + m_FileHeader.dwFileSize = 0; + m_FileHeader.dwWave = WAVE_TAG; + m_FileHeader.dwFormat = FMT__TAG; + m_FileHeader.dwFormatLength = sizeof(WAVEFORMATEX); + + m_DataHeader.dwData = DATA_TAG; + m_DataHeader.dwDataLength = 0; + + RtlZeroMemory(&m_objectAttributes, sizeof(m_objectAttributes)); +} // CSaveData + +//============================================================================= +CSaveData::~CSaveData() +{ + PAGED_CODE(); + + DPF_ENTER(("[CSaveData::~CSaveData]")); + + // Update the wave header in data file with real file size. + // + if(m_pFilePtr) + { + m_FileHeader.dwFileSize = + (DWORD) m_pFilePtr->QuadPart - 2 * sizeof(DWORD); + m_DataHeader.dwDataLength = (DWORD) m_pFilePtr->QuadPart - + sizeof(m_FileHeader) - + m_FileHeader.dwFormatLength - + sizeof(m_DataHeader); + + if (STATUS_SUCCESS == KeWaitForSingleObject + ( + &m_FileSync, + Executive, + KernelMode, + FALSE, + NULL + )) + { + if (NT_SUCCESS(FileOpen(FALSE))) + { + FileWriteHeader(); + + FileClose(); + } + + KeReleaseMutex(&m_FileSync, FALSE); + } + } + + if (m_waveFormat) + { + ExFreePoolWithTag(m_waveFormat, SAVEDATA_POOLTAG1); + m_waveFormat = NULL; + } + + if (m_fFrameUsed) + { + ExFreePoolWithTag(m_fFrameUsed, SAVEDATA_POOLTAG2); + m_fFrameUsed = NULL; + // NOTE : Do not release m_pFilePtr. + } + + if (m_FileName.Buffer) + { + ExFreePoolWithTag(m_FileName.Buffer, SAVEDATA_POOLTAG3); + m_FileName.Buffer = NULL; + } + + if (m_pDataBuffer) + { + ExFreePoolWithTag(m_pDataBuffer, SAVEDATA_POOLTAG4); + m_pDataBuffer = NULL; + } +} // CSaveData + +//============================================================================= +void +CSaveData::DestroyWorkItems +( + void +) +{ + PAGED_CODE(); + + if (m_pWorkItems) + { + for (int i = 0; i < MAX_WORKER_ITEM_COUNT; i++) + { + if (m_pWorkItems[i].WorkItem!=NULL) + { + IoFreeWorkItem(m_pWorkItems[i].WorkItem); + m_pWorkItems[i].WorkItem = NULL; + } + } + ExFreePoolWithTag(m_pWorkItems, SAVEDATA_POOLTAG); + m_pWorkItems = NULL; + } + +} // DestroyWorkItems + +//============================================================================= +void +CSaveData::Disable +( + _In_ BOOL fDisable +) +{ + PAGED_CODE(); + + m_fWriteDisabled = fDisable; +} // Disable + +//============================================================================= +NTSTATUS +CSaveData::FileClose(void) +{ + PAGED_CODE(); + + NTSTATUS ntStatus = STATUS_SUCCESS; + + if (m_FileHandle) + { + ntStatus = ZwClose(m_FileHandle); + m_FileHandle = NULL; + } + + return ntStatus; +} // FileClose + +//============================================================================= +NTSTATUS +CSaveData::FileOpen +( + _In_ BOOL fOverWrite +) +{ + PAGED_CODE(); + + NTSTATUS ntStatus = STATUS_SUCCESS; + IO_STATUS_BLOCK ioStatusBlock; + + if( FALSE == m_bInitialized ) + { + return STATUS_UNSUCCESSFUL; + } + + if(!m_FileHandle) + { + ntStatus = + ZwCreateFile + ( + &m_FileHandle, + GENERIC_WRITE | SYNCHRONIZE, + &m_objectAttributes, + &ioStatusBlock, + NULL, + FILE_ATTRIBUTE_NORMAL, + 0, + fOverWrite ? FILE_OVERWRITE_IF : FILE_OPEN_IF, + FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, + NULL, + 0 + ); + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_TERSE, ("[CSaveData::FileOpen : Error opening data file]")); + } + } + + return ntStatus; +} // FileOpen + +//============================================================================= +NTSTATUS +CSaveData::FileWrite +( + _In_reads_bytes_(ulDataSize) PBYTE pData, + _In_ ULONG ulDataSize +) +{ + PAGED_CODE(); + + ASSERT(pData); + ASSERT(m_pFilePtr); + + NTSTATUS ntStatus; + + if (m_FileHandle) + { + IO_STATUS_BLOCK ioStatusBlock; + + ntStatus = ZwWriteFile( m_FileHandle, + NULL, + NULL, + NULL, + &ioStatusBlock, + pData, + ulDataSize, + m_pFilePtr, + NULL); + + if (NT_SUCCESS(ntStatus)) + { + ASSERT(ioStatusBlock.Information == ulDataSize); + + m_pFilePtr->QuadPart += ulDataSize; + } + else + { + DPF(D_TERSE, ("[CSaveData::FileWrite : WriteFileError]")); + } + } + else + { + DPF(D_TERSE, ("[CSaveData::FileWrite : File not open]")); + ntStatus = STATUS_INVALID_HANDLE; + } + + return ntStatus; +} // FileWrite + +//============================================================================= +NTSTATUS +CSaveData::FileWriteHeader(void) +{ + PAGED_CODE(); + + NTSTATUS ntStatus; + + if (m_FileHandle && m_waveFormat) + { + IO_STATUS_BLOCK ioStatusBlock; + + m_pFilePtr->QuadPart = 0; + + m_FileHeader.dwFormatLength = (m_waveFormat->wFormatTag == WAVE_FORMAT_PCM) ? + sizeof( PCMWAVEFORMAT ) : + sizeof( WAVEFORMATEX ) + m_waveFormat->cbSize; + + ntStatus = ZwWriteFile( m_FileHandle, + NULL, + NULL, + NULL, + &ioStatusBlock, + &m_FileHeader, + sizeof(m_FileHeader), + m_pFilePtr, + NULL); + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_TERSE, ("[CSaveData::FileWriteHeader : Write File Header Error]")); + } + + m_pFilePtr->QuadPart += sizeof(m_FileHeader); + + ntStatus = ZwWriteFile( m_FileHandle, + NULL, + NULL, + NULL, + &ioStatusBlock, + m_waveFormat, + m_FileHeader.dwFormatLength, + m_pFilePtr, + NULL); + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_TERSE, ("[CSaveData::FileWriteHeader : Write Format Error]")); + } + + m_pFilePtr->QuadPart += m_FileHeader.dwFormatLength; + + ntStatus = ZwWriteFile( m_FileHandle, + NULL, + NULL, + NULL, + &ioStatusBlock, + &m_DataHeader, + sizeof(m_DataHeader), + m_pFilePtr, + NULL); + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_TERSE, ("[CSaveData::FileWriteHeader : Write Data Header Error]")); + } + + m_pFilePtr->QuadPart += sizeof(m_DataHeader); + } + else + { + DPF(D_TERSE, ("[CSaveData::FileWriteHeader : File not open]")); + ntStatus = STATUS_INVALID_HANDLE; + } + + return ntStatus; +} // FileWriteHeader +NTSTATUS +CSaveData::SetDeviceObject +( + _In_ PDEVICE_OBJECT DeviceObject +) +{ + PAGED_CODE(); + + ASSERT(DeviceObject); + + NTSTATUS ntStatus = STATUS_SUCCESS; + + m_pDeviceObject = DeviceObject; + return ntStatus; +} + +PDEVICE_OBJECT +CSaveData::GetDeviceObject +( + void +) +{ + PAGED_CODE(); + + return m_pDeviceObject; +} + +#pragma code_seg() +//============================================================================= +PSAVEWORKER_PARAM +CSaveData::GetNewWorkItem +( + void +) +{ + LARGE_INTEGER timeOut = { 0 }; + NTSTATUS ntStatus; + + for (int i = 0; i < MAX_WORKER_ITEM_COUNT; i++) + { + ntStatus = + KeWaitForSingleObject + ( + &m_pWorkItems[i].EventDone, + Executive, + KernelMode, + FALSE, + &timeOut + ); + if (STATUS_SUCCESS == ntStatus) + { + if (m_pWorkItems[i].WorkItem) + return &(m_pWorkItems[i]); + else + return NULL; + } + } + + return NULL; +} // GetNewWorkItem +#pragma code_seg("PAGE") + +//============================================================================= +NTSTATUS +CSaveData::Initialize +( + _In_ BOOL _bOffloaded +) +{ + PAGED_CODE(); + + NTSTATUS ntStatus = STATUS_SUCCESS; + WCHAR szTemp[MAX_PATH]; + size_t cLen; + + DPF_ENTER(("[CSaveData::Initialize]")); + + if (_bOffloaded) + { + m_ulOffloadStreamId++; + } + else + { + m_ulStreamId++; + } + + // Allocate data file name. + // + RtlStringCchPrintfW(szTemp, MAX_PATH, L"%s_%s_%d.wav", DEFAULT_FILE_NAME, _bOffloaded ? OFFLOAD_FILE_NAME : HOST_FILE_NAME, _bOffloaded ? m_ulOffloadStreamId : m_ulStreamId); + m_FileName.Length = 0; + ntStatus = RtlStringCchLengthW (szTemp, sizeof(szTemp)/sizeof(szTemp[0]), &cLen); + if (NT_SUCCESS(ntStatus)) + { + m_FileName.MaximumLength = (USHORT)((cLen * sizeof(WCHAR)) + sizeof(WCHAR));//convert to wchar and add room for NULL + m_FileName.Buffer = (PWSTR) + ExAllocatePoolWithTag + ( + PagedPool, + m_FileName.MaximumLength, + SAVEDATA_POOLTAG3 + ); + if (!m_FileName.Buffer) + { + DPF(D_TERSE, ("[Could not allocate memory for FileName]")); + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + } + } + + // Allocate memory for data buffer. + // + if (NT_SUCCESS(ntStatus)) + { + RtlStringCbCopyW(m_FileName.Buffer, m_FileName.MaximumLength, szTemp); + m_FileName.Length = (USHORT)wcslen(m_FileName.Buffer) * sizeof(WCHAR); + DPF(D_BLAB, ("[New DataFile -- %S", m_FileName.Buffer)); + + m_pDataBuffer = (PBYTE) + ExAllocatePoolWithTag + ( + NonPagedPoolNx, + m_ulBufferSize, + SAVEDATA_POOLTAG4 + ); + if (!m_pDataBuffer) + { + DPF(D_TERSE, ("[Could not allocate memory for Saving Data]")); + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + } + else + { + RtlZeroMemory(m_pDataBuffer, m_ulBufferSize); + } + } + + // Allocate memory for frame usage flags and m_pFilePtr. + // + if (NT_SUCCESS(ntStatus)) + { + m_fFrameUsed = (PBOOL) + ExAllocatePoolWithTag + ( + NonPagedPoolNx, + m_ulFrameCount * sizeof(BOOL) + + sizeof(LARGE_INTEGER), + SAVEDATA_POOLTAG2 + ); + if (!m_fFrameUsed) + { + DPF(D_TERSE, ("[Could not allocate memory for frame flags]")); + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + } + } + + // Initialize the spinlock to synchronize access to the frames + // + KeInitializeSpinLock ( &m_FrameInUseSpinLock ) ; + + // Initialize the file mutex + // + KeInitializeMutex( &m_FileSync, 1 ) ; + + // Open the data file. + // + if (NT_SUCCESS(ntStatus)) + { + // m_fFrameUsed has additional memory to hold m_pFilePtr + // + m_pFilePtr = (PLARGE_INTEGER) + (((PBYTE) m_fFrameUsed) + m_ulFrameCount * sizeof(BOOL)); + RtlZeroMemory(m_fFrameUsed, m_ulFrameCount * sizeof(BOOL) + sizeof(LARGE_INTEGER)); + + // Create data file. + InitializeObjectAttributes + ( + &m_objectAttributes, + &m_FileName, + OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE, + NULL, + NULL + ); + + m_bInitialized = TRUE; + + // Write wave header information to data file. + ntStatus = KeWaitForSingleObject + ( + &m_FileSync, + Executive, + KernelMode, + FALSE, + NULL + ); + + if (STATUS_SUCCESS == ntStatus) + { + ntStatus = FileOpen(TRUE); + if (NT_SUCCESS(ntStatus)) + { + ntStatus = FileWriteHeader(); + + FileClose(); + } + + KeReleaseMutex( &m_FileSync, FALSE ); + } + } + + return ntStatus; +} // Initialize + +//============================================================================= +NTSTATUS +CSaveData::InitializeWorkItems +( + _In_ PDEVICE_OBJECT DeviceObject +) +{ + PAGED_CODE(); + + ASSERT(DeviceObject); + + NTSTATUS ntStatus = STATUS_SUCCESS; + + DPF_ENTER(("[CSaveData::InitializeWorkItems]")); + + if (m_pWorkItems != NULL) + { + return ntStatus; + } + + m_pWorkItems = (PSAVEWORKER_PARAM) + ExAllocatePoolWithTag + ( + NonPagedPoolNx, + sizeof(SAVEWORKER_PARAM) * MAX_WORKER_ITEM_COUNT, + SAVEDATA_POOLTAG + ); + if (m_pWorkItems) + { + for (int i = 0; i < MAX_WORKER_ITEM_COUNT; i++) + { + + m_pWorkItems[i].WorkItem = IoAllocateWorkItem(DeviceObject); + if(m_pWorkItems[i].WorkItem == NULL) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + KeInitializeEvent + ( + &m_pWorkItems[i].EventDone, + NotificationEvent, + TRUE + ); + } + } + else + { + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + } + + return ntStatus; +} // InitializeWorkItems + +//============================================================================= + +IO_WORKITEM_ROUTINE SaveFrameWorkerCallback; + +VOID +SaveFrameWorkerCallback +( + _In_ PDEVICE_OBJECT pDeviceObject, + _In_opt_ PVOID Context +) +{ + UNREFERENCED_PARAMETER(pDeviceObject); + + PAGED_CODE(); + + ASSERT(Context); + + PSAVEWORKER_PARAM pParam = (PSAVEWORKER_PARAM) Context; + PCSaveData pSaveData; + + if (NULL == pParam) + { + // This is completely unexpected, assert here. + // + ASSERT(pParam); + return; + } + + DPF(D_VERBOSE, ("[SaveFrameWorkerCallback], %d", pParam->ulFrameNo)); + + ASSERT(pParam->pSaveData); + ASSERT(pParam->pSaveData->m_fFrameUsed); + + if (pParam->WorkItem) + { + pSaveData = pParam->pSaveData; + + if (STATUS_SUCCESS == KeWaitForSingleObject + ( + &pSaveData->m_FileSync, + Executive, + KernelMode, + FALSE, + NULL + )) + { + if (NT_SUCCESS(pSaveData->FileOpen(FALSE))) + { + pSaveData->FileWrite(pParam->pData, pParam->ulDataSize); + pSaveData->FileClose(); + } + InterlockedExchange( (LONG *)&(pSaveData->m_fFrameUsed[pParam->ulFrameNo]), FALSE ); + + KeReleaseMutex( &pSaveData->m_FileSync, FALSE ); + } + } + + KeSetEvent(&pParam->EventDone, 0, FALSE); +} // SaveFrameWorkerCallback + +//============================================================================= +NTSTATUS +CSaveData::SetDataFormat +( + _In_ PKSDATAFORMAT pDataFormat +) +{ + PAGED_CODE(); + NTSTATUS ntStatus = STATUS_SUCCESS; + + DPF_ENTER(("[CSaveData::SetDataFormat]")); + + ASSERT(pDataFormat); + + PWAVEFORMATEX pwfx = NULL; + + if (IsEqualGUIDAligned(pDataFormat->Specifier, + KSDATAFORMAT_SPECIFIER_DSOUND)) + { + pwfx = + &(((PKSDATAFORMAT_DSOUND) pDataFormat)->BufferDesc.WaveFormatEx); + } + else if (IsEqualGUIDAligned(pDataFormat->Specifier, + KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)) + { + pwfx = &((PKSDATAFORMAT_WAVEFORMATEX) pDataFormat)->WaveFormatEx; + } + + if (pwfx) + { + // Free the previously allocated waveformat + if (m_waveFormat) + { + ExFreePoolWithTag(m_waveFormat, SAVEDATA_POOLTAG1); + } + + m_waveFormat = (PWAVEFORMATEX) + ExAllocatePoolWithTag + ( + NonPagedPoolNx, + (pwfx->wFormatTag == WAVE_FORMAT_PCM) ? + sizeof( PCMWAVEFORMAT ) : + sizeof( WAVEFORMATEX ) + pwfx->cbSize, + SAVEDATA_POOLTAG1 + ); + + if(m_waveFormat) + { + RtlCopyMemory( m_waveFormat, + pwfx, + (pwfx->wFormatTag == WAVE_FORMAT_PCM) ? + sizeof( PCMWAVEFORMAT ) : + sizeof( WAVEFORMATEX ) + pwfx->cbSize); + } + else + { + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + } + } + return ntStatus; +} // SetDataFormat + +//============================================================================= +NTSTATUS +CSaveData::SetMaxWriteSize +( + _In_ ULONG ulMaxWriteSize +) +{ + PAGED_CODE(); + + NTSTATUS ntStatus = STATUS_SUCCESS; + ULONG bufferSize = 0; + PBYTE buffer = NULL; + + DPF_ENTER(("[CSaveData::SetMaxWriteSize]")); + + // + // Compute new buffer size. + // + ntStatus = RtlULongMult(ulMaxWriteSize, DEFAULT_FRAME_COUNT, &bufferSize); + if (!NT_SUCCESS(ntStatus)) + { + DPF(D_TERSE, ("[Could not allocate memory for Saving Data, MaxWriteSize %u is too big]", ulMaxWriteSize)); + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + goto Done; + } + + // + // Alloc memory for buffer. + // + buffer = (PBYTE) + ExAllocatePoolWithTag + ( + NonPagedPoolNx, + bufferSize, + SAVEDATA_POOLTAG4 + ); + if (!buffer) + { + DPF(D_TERSE, ("[Could not allocate memory for Saving Data]")); + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + goto Done; + } + + RtlZeroMemory(buffer, bufferSize); + + // + // Free old one. + // + if (m_pDataBuffer) + { + ExFreePoolWithTag(m_pDataBuffer, SAVEDATA_POOLTAG4); + m_pDataBuffer = NULL; + } + + // + // Init new buffer settings. + // + m_pDataBuffer = buffer; + m_ulBufferSize = bufferSize; + m_ulFrameSize = ulMaxWriteSize; + + ntStatus = STATUS_SUCCESS; + +Done: + return ntStatus; +} // SetDataFormat + +//============================================================================= +void +CSaveData::ReadData +( + _Inout_updates_bytes_all_(ulByteCount) PBYTE pBuffer, + _In_ ULONG ulByteCount +) +{ + UNREFERENCED_PARAMETER(pBuffer); + UNREFERENCED_PARAMETER(ulByteCount); + + PAGED_CODE(); + + // Not implemented yet. +} // ReadData + +//============================================================================= +#pragma code_seg() +void +CSaveData::SaveFrame +( + _In_ ULONG ulFrameNo, + _In_ ULONG ulDataSize +) +{ + PSAVEWORKER_PARAM pParam = NULL; + + DPF_ENTER(("[CSaveData::SaveFrame]")); + + pParam = GetNewWorkItem(); + if (pParam) + { + pParam->pSaveData = this; + pParam->ulFrameNo = ulFrameNo; + pParam->ulDataSize = ulDataSize; + pParam->pData = m_pDataBuffer + ulFrameNo * m_ulFrameSize; + KeResetEvent(&pParam->EventDone); + IoQueueWorkItem(pParam->WorkItem, SaveFrameWorkerCallback, + CriticalWorkQueue, (PVOID)pParam); + } +} // SaveFrame +#pragma code_seg("PAGE") +//============================================================================= +void +CSaveData::WaitAllWorkItems +( + void +) +{ + PAGED_CODE(); + + DPF_ENTER(("[CSaveData::WaitAllWorkItems]")); + + // Save the last partially-filled frame + if (m_ulBufferOffset > m_ulFrameIndex * m_ulFrameSize) + { + ULONG size; + + size = m_ulBufferOffset - m_ulFrameIndex * m_ulFrameSize; + SaveFrame(m_ulFrameIndex, size); + } + + for (int i = 0; i < MAX_WORKER_ITEM_COUNT; i++) + { + DPF(D_VERBOSE, ("[Waiting for WorkItem] %d", i)); + KeWaitForSingleObject + ( + &(m_pWorkItems[i].EventDone), + Executive, + KernelMode, + FALSE, + NULL + ); + } +} // WaitAllWorkItems + +#pragma code_seg() +//============================================================================= +void +CSaveData::WriteData +( + _In_reads_bytes_(ulByteCount) PBYTE pBuffer, + _In_ ULONG ulByteCount +) +{ + ASSERT(pBuffer); + + BOOL fSaveFrame = FALSE; + ULONG ulSaveFrameIndex = 0; + KIRQL oldIrql; + + // If stream writing is disabled, then exit. + // + if (m_fWriteDisabled) + { + return; + } + + DPF_ENTER(("[CSaveData::WriteData ulByteCount=%lu]", ulByteCount)); + + if( 0 == ulByteCount ) + { + return; + } + + // The logic below assumes that write size is <= than frame size. + if (ulByteCount > m_ulFrameSize) + { + ulByteCount = m_ulFrameSize; + } + + // Check to see if this frame is available. + KeAcquireSpinLock(&m_FrameInUseSpinLock, &oldIrql); + if (!m_fFrameUsed[m_ulFrameIndex]) + { + KeReleaseSpinLock(&m_FrameInUseSpinLock, oldIrql ); + + ULONG ulWriteBytes = ulByteCount; + + if( (m_ulBufferSize - m_ulBufferOffset) < ulWriteBytes ) + { + ulWriteBytes = m_ulBufferSize - m_ulBufferOffset; + } + + RtlCopyMemory(m_pDataBuffer + m_ulBufferOffset, pBuffer, ulWriteBytes); + m_ulBufferOffset += ulWriteBytes; + + // Check to see if we need to save this frame + if (m_ulBufferOffset >= ((m_ulFrameIndex + 1) * m_ulFrameSize)) + { + fSaveFrame = TRUE; + } + + // Loop the buffer, if we reached the end. + if (m_ulBufferOffset == m_ulBufferSize) + { + fSaveFrame = TRUE; + m_ulBufferOffset = 0; + } + + if (fSaveFrame) + { + InterlockedExchange( (LONG *)&(m_fFrameUsed[m_ulFrameIndex]), TRUE ); + ulSaveFrameIndex = m_ulFrameIndex; + m_ulFrameIndex = (m_ulFrameIndex + 1) % m_ulFrameCount; + } + + // Write the left over if the next frame is available. + if (ulWriteBytes != ulByteCount) + { + KeAcquireSpinLock(&m_FrameInUseSpinLock, &oldIrql ); + if (!m_fFrameUsed[m_ulFrameIndex]) + { + KeReleaseSpinLock(&m_FrameInUseSpinLock, oldIrql ); + RtlCopyMemory + ( + m_pDataBuffer + m_ulBufferOffset, + pBuffer + ulWriteBytes, + ulByteCount - ulWriteBytes + ); + + m_ulBufferOffset += ulByteCount - ulWriteBytes; + } + else + { + KeReleaseSpinLock(&m_FrameInUseSpinLock, oldIrql); + DPF(D_BLAB, ("[Frame overflow, next frame is in use]")); + } + } + + if (fSaveFrame) + { + SaveFrame(ulSaveFrameIndex, m_ulFrameSize); + } + } + else + { + KeReleaseSpinLock(&m_FrameInUseSpinLock, oldIrql ); + DPF(D_BLAB, ("[Frame %d is in use]", m_ulFrameIndex)); + } + +} // WriteData + + diff --git a/audio/sysvad/savedata.h b/audio/sysvad/savedata.h new file mode 100644 index 000000000..a1525bdab --- /dev/null +++ b/audio/sysvad/savedata.h @@ -0,0 +1,193 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + savedata.h + +Abstract: + + Declaration of SYSVAD data saving class. This class supplies services +to save data to disk. + + +--*/ + +#ifndef _SYSVAD_SAVEDATA_H +#define _SYSVAD_SAVEDATA_H + +//----------------------------------------------------------------------------- +// Forward declaration +//----------------------------------------------------------------------------- +class CSaveData; +typedef CSaveData *PCSaveData; + + +//----------------------------------------------------------------------------- +// Structs +//----------------------------------------------------------------------------- + +// Parameter to workitem. +#include +typedef struct _SAVEWORKER_PARAM { + PIO_WORKITEM WorkItem; + ULONG ulFrameNo; + ULONG ulDataSize; + PBYTE pData; + PCSaveData pSaveData; + KEVENT EventDone; +} SAVEWORKER_PARAM; +typedef SAVEWORKER_PARAM *PSAVEWORKER_PARAM; +#include + +// wave file header. +#include +typedef struct _OUTPUT_FILE_HEADER +{ + DWORD dwRiff; + DWORD dwFileSize; + DWORD dwWave; + DWORD dwFormat; + DWORD dwFormatLength; +} OUTPUT_FILE_HEADER; +typedef OUTPUT_FILE_HEADER *POUTPUT_FILE_HEADER; + +typedef struct _OUTPUT_DATA_HEADER +{ + DWORD dwData; + DWORD dwDataLength; +} OUTPUT_DATA_HEADER; +typedef OUTPUT_DATA_HEADER *POUTPUT_DATA_HEADER; + +#include + +//----------------------------------------------------------------------------- +// Classes +//----------------------------------------------------------------------------- + +/////////////////////////////////////////////////////////////////////////////// +// CSaveData +// Saves the wave data to disk. +// +IO_WORKITEM_ROUTINE SaveFrameWorkerCallback; + +class CSaveData +{ +protected: + UNICODE_STRING m_FileName; // DataFile name. + HANDLE m_FileHandle; // DataFile handle. + PBYTE m_pDataBuffer; // Data buffer. + ULONG m_ulBufferSize; // Total buffer size. + + ULONG m_ulFrameIndex; // Current Frame. + ULONG m_ulFrameCount; // Frame count. + ULONG m_ulFrameSize; + ULONG m_ulBufferOffset; // index in buffer. + PBOOL m_fFrameUsed; // Frame usage table. + KSPIN_LOCK m_FrameInUseSpinLock; // Spinlock for synch. + KMUTEX m_FileSync; // Synchronizes file access + + OBJECT_ATTRIBUTES m_objectAttributes; // Used for opening file. + + OUTPUT_FILE_HEADER m_FileHeader; + PWAVEFORMATEX m_waveFormat; + OUTPUT_DATA_HEADER m_DataHeader; + PLARGE_INTEGER m_pFilePtr; + + static PDEVICE_OBJECT m_pDeviceObject; + static ULONG m_ulStreamId; + static ULONG m_ulOffloadStreamId; + static PSAVEWORKER_PARAM m_pWorkItems; + + BOOL m_fWriteDisabled; + + BOOL m_bInitialized; + +public: + CSaveData(); + ~CSaveData(); + + static NTSTATUS InitializeWorkItems + ( + _In_ PDEVICE_OBJECT DeviceObject + ); + static void DestroyWorkItems + ( + void + ); + void Disable + ( + _In_ BOOL fDisable + ); + static PSAVEWORKER_PARAM GetNewWorkItem + ( + void + ); + NTSTATUS Initialize + ( + _In_ BOOL _bOffloaded + ); + static NTSTATUS SetDeviceObject + ( + _In_ PDEVICE_OBJECT DeviceObject + ); + static PDEVICE_OBJECT GetDeviceObject + ( + void + ); + void ReadData + ( + _Inout_updates_bytes_all_(ulByteCount) PBYTE pBuffer, + _In_ ULONG ulByteCount + ); + NTSTATUS SetDataFormat + ( + _In_ PKSDATAFORMAT pDataFormat + ); + NTSTATUS SetMaxWriteSize + ( + _In_ ULONG ulMaxWriteSize + ); + void WaitAllWorkItems + ( + void + ); + void WriteData + ( + _In_reads_bytes_(ulByteCount) PBYTE pBuffer, + _In_ ULONG ulByteCount + ); + +private: + NTSTATUS FileClose + ( + void + ); + NTSTATUS FileOpen + ( + _In_ BOOL fOverWrite + ); + NTSTATUS FileWrite + ( + _In_reads_bytes_(ulDataSize) PBYTE pData, + _In_ ULONG ulDataSize + ); + NTSTATUS FileWriteHeader + ( + void + ); + + void SaveFrame + ( + _In_ ULONG ulFrameNo, + _In_ ULONG ulDataSize + ); + + friend + IO_WORKITEM_ROUTINE SaveFrameWorkerCallback; +}; +typedef CSaveData *PCSaveData; + +#endif + diff --git a/audio/sysvad/sources.inc b/audio/sysvad/sources.inc new file mode 100644 index 000000000..7396cf461 --- /dev/null +++ b/audio/sysvad/sources.inc @@ -0,0 +1,34 @@ +TARGETTYPE=DRIVER + +TARGETLIBS= \ + $(DDK_LIB_PATH)\portcls.lib\ + $(DDK_LIB_PATH)\stdunk.lib \ + $(SDK_LIB_PATH)\libcntpr.lib + +INCLUDES= \ + $(DDK_INC_PATH); \ + .. + +MSC_WARNING_LEVEL=-W4 -WX + +C_DEFINES= $(C_DEFINES) -D_WIN32 -DUNICODE -D_UNICODE -DPC_IMPLEMENTATION + +# +# Different levels of debug printage. First is nothing but +# catastrophic errors, last is everything under the sun. +# +#C_DEFINES= $(C_DEFINES) -DDEBUG_LEVEL=DEBUGLVL_ERROR +C_DEFINES= $(C_DEFINES) -DDEBUG_LEVEL=DEBUGLVL_TERSE +#C_DEFINES= $(C_DEFINES) -DDEBUG_LEVEL=DEBUGLVL_VERBOSE +#C_DEFINES= $(C_DEFINES) -DDEBUG_LEVEL=DEBUGLVL_BLAB + +SOURCES=\ + ..\adapter.cpp \ + ..\basetopo.cpp \ + ..\common.cpp \ + ..\hw.cpp \ + ..\kshelper.cpp \ + ..\savedata.cpp \ + ..\tonegenerator.cpp \ + ..\sysvad.rc + diff --git a/audio/sysvad/sysvad.h b/audio/sysvad/sysvad.h new file mode 100644 index 000000000..0e35aebdb --- /dev/null +++ b/audio/sysvad/sysvad.h @@ -0,0 +1,265 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + Sysvad.h + +Abstract: + + Header file for common stuff. + +--*/ + +#ifndef _SYSVAD_H_ +#define _SYSVAD_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +//============================================================================= +// Defines +//============================================================================= + +// Version number. Revision numbers are specified for each sample. +#define SYSVAD_VERSION 1 + +// Revision number. +#define SYSVAD_REVISION 0 + +// Product Id +// {5B722BF8-F0AB-47ee-B9C8-8D61D31375A1} +#define STATIC_PID_SYSVAD\ + 0x5b722bf8, 0xf0ab, 0x47ee, 0xb9, 0xc8, 0x8d, 0x61, 0xd3, 0x13, 0x75, 0xa1 +DEFINE_GUIDSTRUCT("5B722BF8-F0AB-47ee-B9C8-8D61D31375A1", PID_SYSVAD); +#define PID_SYSVAD DEFINE_GUIDNAMED(PID_SYSVAD) + +// Pool tag used for SYSVAD allocations +#define SYSVAD_POOLTAG 'DVSM' + +// Debug module name +#define STR_MODULENAME "SYSVAD: " + +// Debug utility macros +#define D_FUNC 4 +#define D_BLAB DEBUGLVL_BLAB +#define D_VERBOSE DEBUGLVL_VERBOSE +#define D_TERSE DEBUGLVL_TERSE +#define D_ERROR DEBUGLVL_ERROR +#define DPF _DbgPrintF +#define DPF_ENTER(x) DPF(D_FUNC, x) + +// Channel orientation +#define CHAN_LEFT 0 +#define CHAN_RIGHT 1 +#define CHAN_MASTER (-1) + +// Dma Settings. +#define DMA_BUFFER_SIZE 0x16000 + +#define KSPROPERTY_TYPE_ALL KSPROPERTY_TYPE_BASICSUPPORT | \ + KSPROPERTY_TYPE_GET | \ + KSPROPERTY_TYPE_SET + +// Specific node numbers +#define DEV_SPECIFIC_VT_BOOL 9 +#define DEV_SPECIFIC_VT_I4 10 +#define DEV_SPECIFIC_VT_UI4 11 + +#define _100NS_PER_MILLISECOND 10000 // number of 100ns units per millisecond + +// Default volume settings. +#define VOLUME_STEPPING_DELTA 0x8000 +#define VOLUME_SIGNED_MAXIMUM 0x00000000 +#define VOLUME_SIGNED_MINIMUM (-96 * 0x10000) + +// Default peak meter settings +#define PEAKMETER_STEPPING_DELTA 0x1000 +#define PEAKMETER_SIGNED_MAXIMUM LONG_MAX +#define PEAKMETER_SIGNED_MINIMUM LONG_MIN + +#define VALUE_NORMALIZE_P(v, step) \ + ((((v) + (step)/2) / (step)) * (step)) + +#define VALUE_NORMALIZE(v, step) \ + ((v) > 0 ? VALUE_NORMALIZE_P((v), (step)) : -(VALUE_NORMALIZE_P(-(v), (step)))) + +#define VALUE_NORMALIZE_IN_RANGE_EX(v, min, max, step) \ + ((v) > (max) ? (max) : \ + (v) < (min) ? (min) : \ + VALUE_NORMALIZE((v), (step))) + +// to normalize volume values. +#define VOLUME_NORMALIZE_IN_RANGE(v) \ + VALUE_NORMALIZE_IN_RANGE_EX((v), VOLUME_SIGNED_MINIMUM, VOLUME_SIGNED_MAXIMUM, VOLUME_STEPPING_DELTA) + +// to normalize sample peak meter. +#define PEAKMETER_NORMALIZE_IN_RANGE(v) \ + VALUE_NORMALIZE_IN_RANGE_EX((v), PEAKMETER_SIGNED_MINIMUM, PEAKMETER_SIGNED_MAXIMUM, PEAKMETER_STEPPING_DELTA) + +#define ALL_CHANNELS_ID UINT32_MAX + +// Flags to identify stream processing mode +typedef enum { + SIGNALPROCESSINGMODE_NONE = 0x00, + SIGNALPROCESSINGMODE_DEFAULT = 0x01, + SIGNALPROCESSINGMODE_RAW = 0x02, + SIGNALPROCESSINGMODE_COMMUNICATIONS = 0x04, + SIGNALPROCESSINGMODE_SPEECH = 0x08, + SIGNALPROCESSINGMODE_NOTIFICATION = 0x10, + SIGNALPROCESSINGMODE_MEDIA = 0x20, + SIGNALPROCESSINGMODE_MOVIE = 0x40 +} SIGNALPROCESSINGMODE; + +#define MAP_GUID_TO_MODE(guid, mode) \ + if (IsEqualGUID(guid, AUDIO_SIGNALPROCESSINGMODE_DEFAULT)) \ + { \ + mode = SIGNALPROCESSINGMODE_DEFAULT; \ + } \ + else if (IsEqualGUID(guid, AUDIO_SIGNALPROCESSINGMODE_RAW)) \ + { \ + mode = SIGNALPROCESSINGMODE_RAW; \ + } \ + else if (IsEqualGUID(guid, AUDIO_SIGNALPROCESSINGMODE_COMMUNICATIONS)) \ + { \ + mode = SIGNALPROCESSINGMODE_COMMUNICATIONS; \ + } \ + else if (IsEqualGUID(guid, AUDIO_SIGNALPROCESSINGMODE_SPEECH)) \ + { \ + mode = SIGNALPROCESSINGMODE_SPEECH; \ + } \ + else if (IsEqualGUID(guid, AUDIO_SIGNALPROCESSINGMODE_NOTIFICATION)) \ + { \ + mode = SIGNALPROCESSINGMODE_NOTIFICATION; \ + } \ + else if (IsEqualGUID(guid, AUDIO_SIGNALPROCESSINGMODE_MEDIA)) \ + { \ + mode = SIGNALPROCESSINGMODE_MEDIA; \ + } \ + else if (IsEqualGUID(guid, AUDIO_SIGNALPROCESSINGMODE_MOVIE)) \ + { \ + mode = SIGNALPROCESSINGMODE_MOVIE; \ + } \ + else \ + { \ + ASSERT(FALSE && "Unknown Signal Processing Mode"); \ + mode = SIGNALPROCESSINGMODE_NONE; \ + } + +#define VERIFY_MODE_RESOURCES_AVAILABLE(modes, guid, status) \ + { \ + SIGNALPROCESSINGMODE mode = SIGNALPROCESSINGMODE_NONE; \ + MAP_GUID_TO_MODE(guid, mode); \ + if (SIGNALPROCESSINGMODE_NONE != mode) \ + { \ + status = (modes & mode) ? STATUS_INSUFFICIENT_RESOURCES : STATUS_SUCCESS; \ + } \ + else \ + { \ + status = STATUS_INVALID_PARAMETER; \ + } \ + } + +#define ALLOCATE_MODE_RESOURCES(modes, guid) \ + { \ + SIGNALPROCESSINGMODE mode = SIGNALPROCESSINGMODE_NONE; \ + MAP_GUID_TO_MODE(guid, mode); \ + modes |= mode; \ + } + +#define FREE_MODE_RESOURCES(modes, guid) \ + { \ + SIGNALPROCESSINGMODE mode = SIGNALPROCESSINGMODE_NONE; \ + MAP_GUID_TO_MODE(guid, mode); \ + modes &= (~mode); \ + } + +// Define the value data type for supported sound detector patterns. Only +// one pattern type is supported in this sample. +typedef struct { + KSMULTIPLE_ITEM MultipleItem; + GUID PatternType[1]; +} CONTOSO_SUPPORTEDPATTERNSVALUE; + +//============================================================================= +// Typedefs +//============================================================================= + +// Flags to identify stream processing mode +typedef enum { + CONNECTIONTYPE_TOPOLOGY_OUTPUT = 0, + CONNECTIONTYPE_WAVE_OUTPUT = 1 +} CONNECTIONTYPE; + +// Connection table for registering topology/wave bridge connection +typedef struct _PHYSICALCONNECTIONTABLE +{ + ULONG ulTopology; + ULONG ulWave; + CONNECTIONTYPE eType; +} PHYSICALCONNECTIONTABLE, *PPHYSICALCONNECTIONTABLE; + +// +// This is the structure of the portclass FDO device extension Nt has created +// for us. We keep the adapter common object here. +// +struct IAdapterCommon; +typedef struct _PortClassDeviceContext // 32 64 Byte offsets for 32 and 64 bit architectures +{ + ULONG_PTR m_pulReserved1[2]; // 0-7 0-15 First two pointers are reserved. + PDEVICE_OBJECT m_DoNotUsePhysicalDeviceObject; // 8-11 16-23 Reserved pointer to our Physical Device Object (PDO). + PVOID m_pvReserved2; // 12-15 24-31 Reserved pointer to our Start Device function. + PVOID m_pvReserved3; // 16-19 32-39 "Out Memory" according to DDK. + IAdapterCommon* m_pCommon; // 20-23 40-47 Pointer to our adapter common object. +#ifdef _USE_SingleComponentMultiFxStates + POHANDLE m_poHandle; // 24-27 48-55 PoFxDevice handle. +#else + PVOID m_pvUnused1; // 24-27 48-55 Unused space. +#endif // _USE_SingleComponentMultiFxStates + PVOID m_pvUnused2; // 28-31 56-63 Unused space. + + // Anything after above line should not be used. + // This actually goes on for (64*sizeof(ULONG_PTR)) but it is all opaque. +} PortClassDeviceContext; + +// +// Global settings. +// +extern DWORD g_DoNotCreateDataFiles; +extern DWORD g_DisableBthScoBypass; + +//============================================================================= +// Function prototypes +//============================================================================= + +// Generic topology handler +NTSTATUS PropertyHandler_Topology +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +); + +// Default WaveFilter automation table. +// Handles the GeneralComponentId request. +NTSTATUS PropertyHandler_WaveFilter +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +); + +NTSTATUS PropertyHandler_OffloadPin +( + _In_ PPCPROPERTY_REQUEST PropertyRequest +); + +// common.h uses some of the above definitions. +#include "common.h" +#include "kshelper.h" + +#endif + diff --git a/audio/sysvad/sysvad.rc b/audio/sysvad/sysvad.rc new file mode 100644 index 000000000..cd8f2faa0 --- /dev/null +++ b/audio/sysvad/sysvad.rc @@ -0,0 +1,30 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + sysvad.rc + +Abstract: + + +--*/ + +#include + +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SOUND +#define VER_FILEDESCRIPTION_STR "Microsoft Virtual Audio Device Slate Sample Driver" +#define VER_INTERNALNAME_STR "slateaudiosample.sys" +#define VER_ORIGINALFILENAME_STR "slateaudiosample.sys" + +#define VER_LEGALCOPYRIGHT_YEARS "2013" +#define VER_LEGALCOPYRIGHT_STR "Copyright (C) Microsoft Corp." VER_LEGALCOPYRIGHT_YEARS + +#include "common.ver" + + + diff --git a/audio/sysvad/sysvad.sln b/audio/sysvad/sysvad.sln new file mode 100644 index 000000000..a177531ae --- /dev/null +++ b/audio/sysvad/sysvad.sln @@ -0,0 +1,121 @@ + +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", "{C3727A52-F4BA-46A5-87DD-422C5A6E6487}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "KeywordDetectorAdapter", "KeywordDetectorAdapter", "{CAA240C8-9145-42EC-850F-72D0A9D611D8}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "PropPageExtensions", "PropPageExtensions", "{4894017C-6595-4FE8-8E11-D89D220C30A6}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SwapAPO", "SwapAPO", "{AC42EA6C-395E-4886-8BEF-7B348AD02B2B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "APO", "APO", "{2CED13CF-D957-488D-981D-0D68A8964814}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "EndpointsCommon", "EndpointsCommon", "{0ADDFDA3-E93A-49F4-8506-4F0F77C94BBC}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "PhoneAudioSample", "PhoneAudioSample", "{90897FF0-653E-44A3-A37D-AE5B59FDAE64}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TabletAudioSample", "TabletAudioSample", "{E93D14DB-304C-457B-AF3F-70F74A9B9D8E}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "package", "Package\package.VcxProj", "{5F7004D7-03BD-44AA-9FDD-6677B2C93469}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "KeywordDetectorContosoAdapter", "KeywordDetectorAdapter\KeywordDetectorContosoAdapter.vcxproj", "{92E28772-ADE3-4367-B679-6DCF6FFA75F1}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PropPageExt", "SwapAPO\PropPageExtensions\PropPageExt.vcxproj", "{76DBA957-38F6-47ED-A5C0-AD587D08FF12}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SwapAPO", "SwapAPO\APO\SwapAPO.vcxproj", "{2A1B7375-D9CB-4067-AEDE-180D75B5934D}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "EndpointsCommon", "EndpointsCommon\EndpointsCommon.vcxproj", "{E3BA10BE-08FF-4244-86C6-C788072CEA25}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PhoneAudioSample", "PhoneAudioSample\PhoneAudioSample.vcxproj", "{43FF11E5-B4C4-409A-AE4B-13342918B6F8}" + ProjectSection(ProjectDependencies) = postProject + {E3BA10BE-08FF-4244-86C6-C788072CEA25} = {E3BA10BE-08FF-4244-86C6-C788072CEA25} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TabletAudioSample", "TabletAudioSample\TabletAudioSample.vcxproj", "{29A5DD25-AB56-4DEE-96AB-21EC2926A300}" + ProjectSection(ProjectDependencies) = postProject + {E3BA10BE-08FF-4244-86C6-C788072CEA25} = {E3BA10BE-08FF-4244-86C6-C788072CEA25} + 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 + {5F7004D7-03BD-44AA-9FDD-6677B2C93469}.Debug|Win32.ActiveCfg = Debug|Win32 + {5F7004D7-03BD-44AA-9FDD-6677B2C93469}.Debug|Win32.Build.0 = Debug|Win32 + {5F7004D7-03BD-44AA-9FDD-6677B2C93469}.Release|Win32.ActiveCfg = Release|Win32 + {5F7004D7-03BD-44AA-9FDD-6677B2C93469}.Release|Win32.Build.0 = Release|Win32 + {5F7004D7-03BD-44AA-9FDD-6677B2C93469}.Debug|x64.ActiveCfg = Debug|x64 + {5F7004D7-03BD-44AA-9FDD-6677B2C93469}.Debug|x64.Build.0 = Debug|x64 + {5F7004D7-03BD-44AA-9FDD-6677B2C93469}.Release|x64.ActiveCfg = Release|x64 + {5F7004D7-03BD-44AA-9FDD-6677B2C93469}.Release|x64.Build.0 = Release|x64 + {92E28772-ADE3-4367-B679-6DCF6FFA75F1}.Debug|Win32.ActiveCfg = Debug|Win32 + {92E28772-ADE3-4367-B679-6DCF6FFA75F1}.Debug|Win32.Build.0 = Debug|Win32 + {92E28772-ADE3-4367-B679-6DCF6FFA75F1}.Release|Win32.ActiveCfg = Release|Win32 + {92E28772-ADE3-4367-B679-6DCF6FFA75F1}.Release|Win32.Build.0 = Release|Win32 + {92E28772-ADE3-4367-B679-6DCF6FFA75F1}.Debug|x64.ActiveCfg = Debug|x64 + {92E28772-ADE3-4367-B679-6DCF6FFA75F1}.Debug|x64.Build.0 = Debug|x64 + {92E28772-ADE3-4367-B679-6DCF6FFA75F1}.Release|x64.ActiveCfg = Release|x64 + {92E28772-ADE3-4367-B679-6DCF6FFA75F1}.Release|x64.Build.0 = Release|x64 + {76DBA957-38F6-47ED-A5C0-AD587D08FF12}.Debug|Win32.ActiveCfg = Debug|Win32 + {76DBA957-38F6-47ED-A5C0-AD587D08FF12}.Debug|Win32.Build.0 = Debug|Win32 + {76DBA957-38F6-47ED-A5C0-AD587D08FF12}.Release|Win32.ActiveCfg = Release|Win32 + {76DBA957-38F6-47ED-A5C0-AD587D08FF12}.Release|Win32.Build.0 = Release|Win32 + {76DBA957-38F6-47ED-A5C0-AD587D08FF12}.Debug|x64.ActiveCfg = Debug|x64 + {76DBA957-38F6-47ED-A5C0-AD587D08FF12}.Debug|x64.Build.0 = Debug|x64 + {76DBA957-38F6-47ED-A5C0-AD587D08FF12}.Release|x64.ActiveCfg = Release|x64 + {76DBA957-38F6-47ED-A5C0-AD587D08FF12}.Release|x64.Build.0 = Release|x64 + {2A1B7375-D9CB-4067-AEDE-180D75B5934D}.Debug|Win32.ActiveCfg = Debug|Win32 + {2A1B7375-D9CB-4067-AEDE-180D75B5934D}.Debug|Win32.Build.0 = Debug|Win32 + {2A1B7375-D9CB-4067-AEDE-180D75B5934D}.Release|Win32.ActiveCfg = Release|Win32 + {2A1B7375-D9CB-4067-AEDE-180D75B5934D}.Release|Win32.Build.0 = Release|Win32 + {2A1B7375-D9CB-4067-AEDE-180D75B5934D}.Debug|x64.ActiveCfg = Debug|x64 + {2A1B7375-D9CB-4067-AEDE-180D75B5934D}.Debug|x64.Build.0 = Debug|x64 + {2A1B7375-D9CB-4067-AEDE-180D75B5934D}.Release|x64.ActiveCfg = Release|x64 + {2A1B7375-D9CB-4067-AEDE-180D75B5934D}.Release|x64.Build.0 = Release|x64 + {E3BA10BE-08FF-4244-86C6-C788072CEA25}.Debug|Win32.ActiveCfg = Debug|Win32 + {E3BA10BE-08FF-4244-86C6-C788072CEA25}.Debug|Win32.Build.0 = Debug|Win32 + {E3BA10BE-08FF-4244-86C6-C788072CEA25}.Release|Win32.ActiveCfg = Release|Win32 + {E3BA10BE-08FF-4244-86C6-C788072CEA25}.Release|Win32.Build.0 = Release|Win32 + {E3BA10BE-08FF-4244-86C6-C788072CEA25}.Debug|x64.ActiveCfg = Debug|x64 + {E3BA10BE-08FF-4244-86C6-C788072CEA25}.Debug|x64.Build.0 = Debug|x64 + {E3BA10BE-08FF-4244-86C6-C788072CEA25}.Release|x64.ActiveCfg = Release|x64 + {E3BA10BE-08FF-4244-86C6-C788072CEA25}.Release|x64.Build.0 = Release|x64 + {43FF11E5-B4C4-409A-AE4B-13342918B6F8}.Debug|Win32.ActiveCfg = Debug|Win32 + {43FF11E5-B4C4-409A-AE4B-13342918B6F8}.Debug|Win32.Build.0 = Debug|Win32 + {43FF11E5-B4C4-409A-AE4B-13342918B6F8}.Release|Win32.ActiveCfg = Release|Win32 + {43FF11E5-B4C4-409A-AE4B-13342918B6F8}.Release|Win32.Build.0 = Release|Win32 + {43FF11E5-B4C4-409A-AE4B-13342918B6F8}.Debug|x64.ActiveCfg = Debug|x64 + {43FF11E5-B4C4-409A-AE4B-13342918B6F8}.Debug|x64.Build.0 = Debug|x64 + {43FF11E5-B4C4-409A-AE4B-13342918B6F8}.Release|x64.ActiveCfg = Release|x64 + {43FF11E5-B4C4-409A-AE4B-13342918B6F8}.Release|x64.Build.0 = Release|x64 + {29A5DD25-AB56-4DEE-96AB-21EC2926A300}.Debug|Win32.ActiveCfg = Debug|Win32 + {29A5DD25-AB56-4DEE-96AB-21EC2926A300}.Debug|Win32.Build.0 = Debug|Win32 + {29A5DD25-AB56-4DEE-96AB-21EC2926A300}.Release|Win32.ActiveCfg = Release|Win32 + {29A5DD25-AB56-4DEE-96AB-21EC2926A300}.Release|Win32.Build.0 = Release|Win32 + {29A5DD25-AB56-4DEE-96AB-21EC2926A300}.Debug|x64.ActiveCfg = Debug|x64 + {29A5DD25-AB56-4DEE-96AB-21EC2926A300}.Debug|x64.Build.0 = Debug|x64 + {29A5DD25-AB56-4DEE-96AB-21EC2926A300}.Release|x64.ActiveCfg = Release|x64 + {29A5DD25-AB56-4DEE-96AB-21EC2926A300}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {5F7004D7-03BD-44AA-9FDD-6677B2C93469} = {C3727A52-F4BA-46A5-87DD-422C5A6E6487} + {92E28772-ADE3-4367-B679-6DCF6FFA75F1} = {CAA240C8-9145-42EC-850F-72D0A9D611D8} + {76DBA957-38F6-47ED-A5C0-AD587D08FF12} = {4894017C-6595-4FE8-8E11-D89D220C30A6} + {2A1B7375-D9CB-4067-AEDE-180D75B5934D} = {2CED13CF-D957-488D-981D-0D68A8964814} + {E3BA10BE-08FF-4244-86C6-C788072CEA25} = {0ADDFDA3-E93A-49F4-8506-4F0F77C94BBC} + {43FF11E5-B4C4-409A-AE4B-13342918B6F8} = {90897FF0-653E-44A3-A37D-AE5B59FDAE64} + {29A5DD25-AB56-4DEE-96AB-21EC2926A300} = {E93D14DB-304C-457B-AF3F-70F74A9B9D8E} + {4894017C-6595-4FE8-8E11-D89D220C30A6} = {AC42EA6C-395E-4886-8BEF-7B348AD02B2B} + {2CED13CF-D957-488D-981D-0D68A8964814} = {AC42EA6C-395E-4886-8BEF-7B348AD02B2B} + EndGlobalSection +EndGlobal diff --git a/audio/sysvad/tabletaudiosample.inf b/audio/sysvad/tabletaudiosample.inf new file mode 100644 index 0000000000000000000000000000000000000000..ab5596e892a34a5850d3762e05530bb3a6118dd9 GIT binary patch literal 55952 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 bjn`wn>{{=2Q6lSgQyB*-xGj67#%2Ex2Y3RB literal 0 HcmV?d00001 diff --git a/avscamera/Package/package.VcxProj b/avscamera/Package/package.VcxProj new file mode 100644 index 000000000..5e013d6fe --- /dev/null +++ b/avscamera/Package/package.VcxProj @@ -0,0 +1,87 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + + {11E4CED5-8A3E-4BDF-9FEC-3C63BEF81478} + + + {BB6E820D-6149-4486-AD06-96E8F33DCE8A} + + + + WindowsKernelModeDriver10.0 + Utility + Package + true + Debug + + + + {E1203C9D-047C-4357-9747-0476C871A53B} + {C7A901F1-4095-4C27-A0AA-87BE488930F7} + $(MSBuildProjectName) + + + Windows10 + true + + + Windows10 + false + + + Windows10 + true + + + Windows10 + false + + + + + + + + + + + DbgengKernelDebugger + False + None + + + + + + %PathToInf% + False + False + True + + 133563 + + + + + + + \ No newline at end of file diff --git a/avscamera/Package/package.VcxProj.Filters b/avscamera/Package/package.VcxProj.Filters new file mode 100644 index 000000000..d0e1ece54 --- /dev/null +++ b/avscamera/Package/package.VcxProj.Filters @@ -0,0 +1,21 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {532008E1-2000-458F-880C-BB33071663FA} + + + h;hpp;hxx;hm;inl;inc;xsd + {6C9436B8-8F80-4015-B139-6A072D19C24C} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {21B1ED41-2D14-4605-B7CC-F42B6037C5EC} + + + inf;inv;inx;mof;mc; + {A5CFB552-03B4-4E4F-BE46-A88DEEC7B2C2} + + + \ No newline at end of file diff --git a/avscamera/README.md b/avscamera/README.md new file mode 100644 index 000000000..e6f79be66 --- /dev/null +++ b/avscamera/README.md @@ -0,0 +1,13 @@ +AvsCamera: AVStream Camera Sample Driver +======================================== +The AvsCamera sample provides a pin-centric AVStream capture driver for a simulated front and back camera. The driver performs simulated captures at 320x240 or 640x480 in RGB24, RGB32, YUY2 and NV12 formats at various frame rates. The purpose of the sample is to demonstrate how to write a fully functional AVStream camera driver. + +This sample features strong parameter validation and overflow detection. It provides validation and simulation logic for all advanced camera controls in the CCaptureFilter class. A real camera driver would replace the filter automation table and CSensor and CSynthesizer class hierarchies to produce a new camera driver. + +The sample comes with its own MFT0 called AvsCameraMft0.dll. This MFT0 is used to parse metadata supplied in the AvsCamera driver samples. The metadata communications from the driver is primarily a private channel to its MFT0. The MFT0 is responsible for reformatting that information for the capture pipeline. + +## Universal Windows Driver Compliant +This sample builds a Universal Windows Driver. It uses only APIs and DDIs that are included in OneCoreUAP. The sample works on 32-bit and 64-bit x86, amd64 and arm platforms. Once installed, the simulated camera should show in the Windows Inbox camera app. + +## Building the sample +The AvsCamera sample can be built by opening the AvsCamera.sln solution file. A successful build produces AvsCamera.sys, AvsCameraMft0.dll, AvsCamera.inf and AvsCamera.cat. diff --git a/avscamera/avscamera.sln b/avscamera/avscamera.sln new file mode 100644 index 000000000..c2e8f3549 --- /dev/null +++ b/avscamera/avscamera.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}") = "Package", "Package", "{5D110464-BE76-4F9B-B694-1B9F6318EFFA}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Mft0", "Mft0", "{ED28A2B9-91CF-4A13-90F5-9F7C53FD007A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sys", "Sys", "{68E7C36A-2246-4DC9-B1E6-240D7D24DA83}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "package", "Package\package.VcxProj", "{E1203C9D-047C-4357-9747-0476C871A53B}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AvsCameraMft0", "mft0\AvsCameraMft0.vcxproj", "{11E4CED5-8A3E-4BDF-9FEC-3C63BEF81478}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AvsCamera", "sys\AvsCamera.vcxproj", "{BB6E820D-6149-4486-AD06-96E8F33DCE8A}" +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 + {E1203C9D-047C-4357-9747-0476C871A53B}.Debug|Win32.ActiveCfg = Debug|Win32 + {E1203C9D-047C-4357-9747-0476C871A53B}.Debug|Win32.Build.0 = Debug|Win32 + {E1203C9D-047C-4357-9747-0476C871A53B}.Release|Win32.ActiveCfg = Release|Win32 + {E1203C9D-047C-4357-9747-0476C871A53B}.Release|Win32.Build.0 = Release|Win32 + {E1203C9D-047C-4357-9747-0476C871A53B}.Debug|x64.ActiveCfg = Debug|x64 + {E1203C9D-047C-4357-9747-0476C871A53B}.Debug|x64.Build.0 = Debug|x64 + {E1203C9D-047C-4357-9747-0476C871A53B}.Release|x64.ActiveCfg = Release|x64 + {E1203C9D-047C-4357-9747-0476C871A53B}.Release|x64.Build.0 = Release|x64 + {11E4CED5-8A3E-4BDF-9FEC-3C63BEF81478}.Debug|Win32.ActiveCfg = Debug|Win32 + {11E4CED5-8A3E-4BDF-9FEC-3C63BEF81478}.Debug|Win32.Build.0 = Debug|Win32 + {11E4CED5-8A3E-4BDF-9FEC-3C63BEF81478}.Release|Win32.ActiveCfg = Release|Win32 + {11E4CED5-8A3E-4BDF-9FEC-3C63BEF81478}.Release|Win32.Build.0 = Release|Win32 + {11E4CED5-8A3E-4BDF-9FEC-3C63BEF81478}.Debug|x64.ActiveCfg = Debug|x64 + {11E4CED5-8A3E-4BDF-9FEC-3C63BEF81478}.Debug|x64.Build.0 = Debug|x64 + {11E4CED5-8A3E-4BDF-9FEC-3C63BEF81478}.Release|x64.ActiveCfg = Release|x64 + {11E4CED5-8A3E-4BDF-9FEC-3C63BEF81478}.Release|x64.Build.0 = Release|x64 + {BB6E820D-6149-4486-AD06-96E8F33DCE8A}.Debug|Win32.ActiveCfg = Debug|Win32 + {BB6E820D-6149-4486-AD06-96E8F33DCE8A}.Debug|Win32.Build.0 = Debug|Win32 + {BB6E820D-6149-4486-AD06-96E8F33DCE8A}.Release|Win32.ActiveCfg = Release|Win32 + {BB6E820D-6149-4486-AD06-96E8F33DCE8A}.Release|Win32.Build.0 = Release|Win32 + {BB6E820D-6149-4486-AD06-96E8F33DCE8A}.Debug|x64.ActiveCfg = Debug|x64 + {BB6E820D-6149-4486-AD06-96E8F33DCE8A}.Debug|x64.Build.0 = Debug|x64 + {BB6E820D-6149-4486-AD06-96E8F33DCE8A}.Release|x64.ActiveCfg = Release|x64 + {BB6E820D-6149-4486-AD06-96E8F33DCE8A}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {E1203C9D-047C-4357-9747-0476C871A53B} = {5D110464-BE76-4F9B-B694-1B9F6318EFFA} + {11E4CED5-8A3E-4BDF-9FEC-3C63BEF81478} = {ED28A2B9-91CF-4A13-90F5-9F7C53FD007A} + {BB6E820D-6149-4486-AD06-96E8F33DCE8A} = {68E7C36A-2246-4DC9-B1E6-240D7D24DA83} + EndGlobalSection +EndGlobal diff --git a/avscamera/common/CustomProperties.h b/avscamera/common/CustomProperties.h new file mode 100644 index 000000000..584d8c007 --- /dev/null +++ b/avscamera/common/CustomProperties.h @@ -0,0 +1,48 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + CustomProperties.h + + Abstract: + + This file contains a definition used to demonstrate a custom property. + + History: + + created 10/10/2014 + +**************************************************************************/ +#pragma once + + +#define STATIC_PROPSETID_VIDCAP_CUSTOM_CAMERACONTROL_MFT0 \ + 0x452da6d7, 0xa42b, 0x4906, 0xa6, 0xc3, 0x81, 0x62, 0xe7, 0x2d, 0x38, 0x3d +DEFINE_GUIDSTRUCT("452da6d7-a42b-4906-a6c3-8162e72d383d", PROPSETID_VIDCAP_CUSTOM_CAMERACONTROL_MFT0); +#define PROPSETID_VIDCAP_CUSTOM_CAMERACONTROL_MFT0 DEFINE_GUIDNAMED(PROPSETID_VIDCAP_CUSTOM_CAMERACONTROL_MFT0) + +typedef enum +{ + KSPROPERTY_CUSTOM_CAMERACONTROL_MFT0_VIDEOSTABILIZATION +} KSPROPERTY_CUSTOM_CAMERACONTROL_MFT0; + +typedef struct +{ + BOOL VideoStabilizationEnabled; +} KSPROPERTY_CUSTOM_CAMERACONTROL_MFT0_VIDEOSTABILIZATION_S, *PKSPROPERTY_CUSTOM_CAMERACONTROL_MFT0_VIDEOSTABILIZATION_S; + +// {05DC8943-B9FB-4201-808F-6E91451C4760} +#define STATIC_PROPSETID_VIDCAP_CUSTOMCONTROL \ + 0x5dc8943, 0xb9fb, 0x4201, 0x80, 0x8f, 0x6e, 0x91, 0x45, 0x1c, 0x47, 0x60 +DEFINE_GUIDSTRUCT("05DC8943-B9FB-4201-808F-6E91451C4760", PROPSETID_VIDCAP_CUSTOMCONTROL ); +#define PROPSETID_VIDCAP_CUSTOMCONTROL DEFINE_GUIDNAMED(PROPSETID_VIDCAP_CUSTOMCONTROL ) + +enum +{ + KSPROPERTY_CUSTOMCONTROL_DUMMY +}; + diff --git a/avscamera/common/Macros.h b/avscamera/common/Macros.h new file mode 100644 index 000000000..b87aa4369 --- /dev/null +++ b/avscamera/common/Macros.h @@ -0,0 +1,48 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + macros.h + + Abstract: + + This file contains common error checking macros for the MFT0 + + History: + + created 10/10/2014 + +**************************************************************************/ + +#pragma once + +#define IFFAILED_BREAK(exp) \ +{ \ + if ( FAILED(hr= (exp)) ) \ + { \ + break; \ + } \ +} + +#define IFFALSE_BREAK_HR(exp, retval) \ +{ \ + if ( !(exp)) \ + { \ + hr = retval; \ + break; \ + } \ +} + + +#define IFNULL_BREAK_HR(exp, retval) \ +{ \ + if ( (exp) == NULL ) \ + { \ + hr = retval; \ + break; \ + } \ +} \ No newline at end of file diff --git a/avscamera/common/MetadataInternal.h b/avscamera/common/MetadataInternal.h new file mode 100644 index 000000000..41f494696 --- /dev/null +++ b/avscamera/common/MetadataInternal.h @@ -0,0 +1,267 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2013, Microsoft Corporation. + + File: + + MetadataInternal.h + + Abstract: + + Internal structures for private metadata communication between the + driver and its MFT0. + + History: + + created 4/29/2013 + +**************************************************************************/ + + + +enum MetadataId_Custom +{ + MetadataId_Custom_ImageAggregation = MetadataId_Custom_Start, + MetadataId_Custom_FaceDetection, + MetadataId_Custom_PreviewAggregation, + MetadataId_Custom_Histogram +}; + +typedef struct +{ + UINT32 Set; + UINT32 Value; +} METADATA_UINT32, METADATA_LONG; + +typedef struct +{ + UINT32 Set; + UINT16 Value; + UINT16 Reserved; +} METADATA_UINT16, METADATA_SHORT; + +typedef struct +{ + UINT32 Length; // Use a length of 0 for not Set + CHAR String[32]; +} METADATA_SHORTSTRING; + +typedef struct +{ + UINT32 Set; + UINT32 Reserved; + UINT32 Numerator; + UINT32 Denominator; +} METADATA_RATIONAL; + +typedef struct +{ + UINT32 Set; + UINT32 Reserved; + INT64 Value; +} METADATA_INT64; + +typedef struct +{ + UINT32 Set; + UINT32 Reserved; + UINT64 Value; +} METADATA_UINT64; + +typedef struct +{ + UINT32 Set; + UINT32 Reserved; + INT32 Numerator; + INT32 Denominator; +} METADATA_SRATIONAL; + +typedef struct +{ + UINT32 Set; + INT32 Value; + UINT64 Flags; +} METADATA_EVCOMP; + +#ifndef _WDM_INCLUDED_ +// +// Time conversion routines +// +typedef short CSHORT; + +typedef struct _TIME_FIELDS +{ + CSHORT Year; // range [1601...] + CSHORT Month; // range [1..12] + CSHORT Day; // range [1..31] + CSHORT Hour; // range [0..23] + CSHORT Minute; // range [0..59] + CSHORT Second; // range [0..59] + CSHORT Milliseconds;// range [0..999] + CSHORT Weekday; // range [0..6] == [Sunday..Saturday] +} TIME_FIELDS; +typedef TIME_FIELDS *PTIME_FIELDS; + +#endif //TIME_FIELDS + +typedef struct +{ + UINT32 Set; + UINT32 Reserved; + TIME_FIELDS Time; +} METADATA_TIMEFIELDS; + +typedef struct _METADATA_PREVIEWAGGREGATION +{ + // Mandatory fields + UINT32 FocusState; + UINT32 Reserved; + METADATA_INT64 ExposureTime; + METADATA_EVCOMP EVCompensation; + METADATA_UINT32 ISOSpeed; + METADATA_UINT32 LensPosition; + METADATA_UINT32 FlashOn; + METADATA_UINT32 WhiteBalanceMode; + METADATA_SRATIONAL IsoAnalogGain; // MF_CAPTURE_METADATA_ISO_GAINS + METADATA_SRATIONAL IsoDigitalGain; // MF_CAPTURE_METADATA_ISO_GAINS + METADATA_UINT64 SensorFrameRate; // MF_CAPTURE_METADATA_SENSORFRAMERATE + METADATA_SRATIONAL WhiteBalanceGain_R; // MF_CAPTURE_METADATA_WHITEBALANCE_GAINS + METADATA_SRATIONAL WhiteBalanceGain_G; // MF_CAPTURE_METADATA_WHITEBALANCE_GAINS + METADATA_SRATIONAL WhiteBalanceGain_B; // MF_CAPTURE_METADATA_WHITEBALANCE_GAINS + +} METADATA_PREVIEWAGGREGATION, *PMETADATA_PREVIEWAGGREGATION; + +typedef struct _METADATA_IMAGEAGGREGATION +{ + // Mandatory fields + METADATA_UINT32 FrameId; + METADATA_INT64 ExposureTime; + METADATA_UINT32 ISOSpeed; + METADATA_UINT32 LensPosition; + METADATA_UINT64 SceneMode; + METADATA_UINT32 FlashOn; + METADATA_UINT32 FlashPower; + METADATA_UINT32 WhiteBalanceMode; + METADATA_UINT32 ZoomFactor; + METADATA_UINT32 FocusLocked; + METADATA_UINT32 WhiteBalanceLocked; + METADATA_UINT32 ExposureLocked; + + // Required Exif data + METADATA_SHORT Orientation; + METADATA_TIMEFIELDS LocalTime; + METADATA_SHORTSTRING Make; + METADATA_SHORTSTRING Model; + METADATA_SHORTSTRING Software; + METADATA_SHORT ColorSpace; + METADATA_RATIONAL Gamma; + METADATA_SHORTSTRING MakerNote; + METADATA_RATIONAL FNumber; + METADATA_SHORT ExposureProgram; + METADATA_SRATIONAL ShutterSpeedValue; + METADATA_RATIONAL Aperture; + METADATA_SRATIONAL Brightness; + METADATA_SRATIONAL ExposureBias; + METADATA_RATIONAL SubjectDistance; + METADATA_SHORT MeteringMode; + METADATA_SHORT LightSource; + METADATA_SHORT Flash; + METADATA_RATIONAL FocalLength; + METADATA_RATIONAL FocalPlaneXResolution; + METADATA_RATIONAL FocalPlaneYResolution; + METADATA_RATIONAL ExposureIndex; + METADATA_SHORT ExposureMode; + METADATA_SHORT WhiteBalance; + METADATA_RATIONAL DigitalZoomRatio; + METADATA_SHORT FocalLengthIn35mmFilm; + METADATA_SHORT SceneCaptureType; + METADATA_RATIONAL GainControl; + METADATA_SHORT Contrast; + METADATA_SHORT Saturation; + METADATA_SHORT Sharpness; + METADATA_SHORT SubjectDistanceRange; + + // Additional required metadata + METADATA_EVCOMP EVCompensation; + + // Optional + METADATA_LONG FocusState; + +} METADATA_IMAGEAGGREGATION, *PMETADATA_IMAGEAGGREGATION; + +typedef enum +{ + Metadata_Orientation_TopBottomLeftRight = 1, + Metadata_Orientation_TopBottomRightLeft = 2, + Metadata_Orientation_BottomTopLeftRight = 3, + Metadata_Orientation_BottomTopRightLeft = 4, + Metadata_Orientation_LeftRightTopBottom = 5, // 90 degree rotation + Metadata_Orientation_RightLeftTopBottom = 6, + Metadata_Orientation_LeftRightBottomTop = 7, + Metadata_Orientation_RightLeftBottomTop = 8 + +} METADATA_ORIENTATION_ENUM; + +typedef struct +{ + KSCAMERA_METADATA_ITEMHEADER Header; + METADATA_PREVIEWAGGREGATION Data; +} CAMERA_METADATA_PREVIEWAGGREGATION, *PCAMERA_METADATA_PREVIEWAGGREGATION; + +typedef struct +{ + KSCAMERA_METADATA_ITEMHEADER Header; + METADATA_IMAGEAGGREGATION Data; +} CAMERA_METADATA_IMAGEAGGREGATION, *PCAMERA_METADATA_IMAGEAGGREGATION; + +typedef struct +{ + KSCAMERA_METADATA_ITEMHEADER Header; + UINT32 eFocusState; + UINT32 Reserved; +} CAMERA_METADATA_FOCUS_STATE, *PCAMERA_METADATA_FOCUS_STATE; + +typedef enum _METADATA_EXPRESSION +{ + EXPRESSION_NONE=0, + EXPRESSION_SMILE=1 +} METADATA_EXPRESSION; + +typedef struct _METADATA_FACEDATA +{ + RECT Region; // A rectangle describing the face. + ULONG confidenceLevel; // [0, 100]. + ULONG BlinkScoreLeft; // [0, 100]. 0 indicates no blink for the left eye. 100 indicates definite blink for the left eye + ULONG BlinkScoreRight; // [0, 100]. 0 indicates no blink for the right eye. 100 indicates definite blink for the right eye + ULONG Reserved; // Pad for 64-bit alignment. + BOOL FacialExpression; // Is the face smiling? + ULONG FacialExpressionScore; // [0, 100]. 0 indicates no such facial expression as identified. 100 indicates definite such facial expression as defined +} METADATA_FACEDATA, *PMETADATA_FACEDATA; + +typedef struct _CAMERA_METADATA_FACEHEADER +{ + KSCAMERA_METADATA_ITEMHEADER Header; + UINT32 Count; // Number of detected faces. + UINT64 Flags; // If KSCAMERA_EXTENDEDPROP_FACEDETECTION_BLINK or _SMILE bitflags are set, characterization metadata is valid. + UINT64 Timestamp; // Time in 100ns of actual face detection. +} CAMERA_METADATA_FACEHEADER, *PCAMERA_METADATA_FACEHEADER; + +typedef struct _METADATA_HISTOGRAM +{ + ULONG Width; + ULONG Height; + ULONG ChannelMask; + ULONG FourCC; + ULONG P0Data[256]; // first primary (R / Y ) + ULONG P1Data[256]; // second (G / Cr) + ULONG P2Data[256]; // third (B / Cb) +} METADATA_HISTOGRAM, *PMETADATA_HISTOGRAM; + +typedef struct _CAMERA_METADATA_HISTOGRAM +{ + KSCAMERA_METADATA_ITEMHEADER Header; + METADATA_HISTOGRAM Data; +} CAMERA_METADATA_HISTOGRAM, *PCAMERA_METADATA_HISTOGRAM; + diff --git a/avscamera/mft0/AvsCameraMFT0.rc b/avscamera/mft0/AvsCameraMFT0.rc new file mode 100644 index 000000000..86c06ba28 --- /dev/null +++ b/avscamera/mft0/AvsCameraMFT0.rc @@ -0,0 +1,13 @@ +#include +#include + + +#define VER_FILETYPE VFT_DLL +#define VER_FILESUBTYPE VFT2_UNKNOWN +#define VER_FILEDESCRIPTION_STR "A/V Stream Camera MFT0" +#define VER_INTERNALNAME_STR "AvsCameraMft0.dll" +#define VER_ORIGINALFILENAME_STR "AvsCameraMft0.dll" +#define VER_LANGNEUTRAL + +#include "common.ver" + diff --git a/avscamera/mft0/AvsCameraMft0.vcxproj b/avscamera/mft0/AvsCameraMft0.vcxproj new file mode 100644 index 000000000..8f60604b0 --- /dev/null +++ b/avscamera/mft0/AvsCameraMft0.vcxproj @@ -0,0 +1,216 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {11E4CED5-8A3E-4BDF-9FEC-3C63BEF81478} + $(MSBuildProjectName) + false + true + Debug + Win32 + {C6FB0F20-5242-4187-95E4-B6BD741DA56C} + + + + 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) + + + + + + + + + + + + + + + + AvsCameraMft0 + + + AvsCameraMft0 + + + AvsCameraMft0 + + + AvsCameraMft0 + + + + Sync + + + + + Sync + + + + + Sync + + + + + Sync + + + + + %(AdditionalIncludeDirectories);$(SDK_INC_PATH)\ks;..\common + + + %(AdditionalIncludeDirectories);$(SDK_INC_PATH)\ks;..\common + + + %(AdditionalIncludeDirectories);$(SDK_INC_PATH)\ks;..\common + + + + + %(AdditionalIncludeDirectories);$(SDK_INC_PATH)\ks;..\common + + + %(AdditionalIncludeDirectories);$(SDK_INC_PATH)\ks;..\common + + + %(AdditionalIncludeDirectories);$(SDK_INC_PATH)\ks;..\common + + + + + %(AdditionalIncludeDirectories);$(SDK_INC_PATH)\ks;..\common + + + %(AdditionalIncludeDirectories);$(SDK_INC_PATH)\ks;..\common + + + %(AdditionalIncludeDirectories);$(SDK_INC_PATH)\ks;..\common + + + + + %(AdditionalIncludeDirectories);$(SDK_INC_PATH)\ks;..\common + + + %(AdditionalIncludeDirectories);$(SDK_INC_PATH)\ks;..\common + + + %(AdditionalIncludeDirectories);$(SDK_INC_PATH)\ks;..\common + + + + + %(AdditionalDependencies);mfplat.lib;uuid.lib;mfuuid.lib;OneCoreUAP.lib + Mft0.def + + + + + %(AdditionalDependencies);mfplat.lib;uuid.lib;mfuuid.lib;OneCoreUAP.lib + Mft0.def + + + + + %(AdditionalDependencies);mfplat.lib;uuid.lib;mfuuid.lib;OneCoreUAP.lib + Mft0.def + + + + + %(AdditionalDependencies);mfplat.lib;uuid.lib;mfuuid.lib;OneCoreUAP.lib + Mft0.def + + + + + + ;%(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/avscamera/mft0/AvsCameraMft0.vcxproj.Filters b/avscamera/mft0/AvsCameraMft0.vcxproj.Filters new file mode 100644 index 000000000..28d495e3a --- /dev/null +++ b/avscamera/mft0/AvsCameraMft0.vcxproj.Filters @@ -0,0 +1,42 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {60DA7BE1-F98A-4082-8012-10277DA284FC} + + + h;hpp;hxx;hm;inl;inc;xsd + {0A04E637-D033-4503-9ED5-F0FE9523FC0F} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {9131C40A-BF05-41D0-8089-D1207A87E3BA} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/avscamera/mft0/MFT0.def b/avscamera/mft0/MFT0.def new file mode 100644 index 000000000..b4430ceb3 --- /dev/null +++ b/avscamera/mft0/MFT0.def @@ -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 + +; SampleMft0.def : Declares the module parameters. + +LIBRARY + +EXPORTS + DllCanUnloadNow PRIVATE + DllGetClassObject PRIVATE \ No newline at end of file diff --git a/avscamera/mft0/MFT0.idl b/avscamera/mft0/MFT0.idl new file mode 100644 index 000000000..40098f71f --- /dev/null +++ b/avscamera/mft0/MFT0.idl @@ -0,0 +1,46 @@ +//// 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(7B917902-D657-4437-9F93-93B94482F286), + oleautomation, + nonextensible, + pointer_default(unique) +] +interface ISocMft0 : IUnknown{ + [id(1)] HRESULT SetState([in] UINT32 state); + [id(2)] HRESULT GetState([out] UINT* pState); +}; +[ + uuid(8F14E328-2084-442E-A4D9-A80AA30ECBA8), + version(1.0), +] +library SampleSocMft0Lib +{ + importlib("stdole2.tlb"); + [ + uuid(424BF154-D92A-42EB-B041-1395F9E5B4A2) + ] + coclass Mft0 + { + [default] interface ISocMft0; + interface IInspectable; + interface IMFTransform; + }; +}; + diff --git a/avscamera/mft0/MFT0Impl.cpp b/avscamera/mft0/MFT0Impl.cpp new file mode 100644 index 000000000..90caedd64 --- /dev/null +++ b/avscamera/mft0/MFT0Impl.cpp @@ -0,0 +1,2106 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + Mft0Impl.cpp + + Abstract: + + This is the implementation of CSocMft0. + + CSocMft0 is the MFT0 for the AvsCamera sample driver. This MFT0 + performs the following key tasks: + 1. Filters overscan media types supplied by the driver, and + 2. Parses private metadata and attaches an IMFAttribute to the sample. + + History: + + created 5/15/2014 + +**************************************************************************/ + + +#include "stdafx.h" +#include "Mft0Impl.h" +#include "SampleHelpers.h" +#include "metadataInternal.h" +#include +#include +#include +#include + +#include "CustomProperties.h" +#include "macros.h" + +///////////////////////////////////////////////////////////////////////////////// +// +// These two functions demostrate that OEM application can communicate with MFT0 +// +STDMETHODIMP CSocMft0::SetState(UINT32 state) +{ + // OEM can use similar function to update the status of MFT + // From their own application + m_uiInternalState = state; + return S_OK; +} + +STDMETHODIMP CSocMft0::GetState(UINT32 *pState) +{ + HRESULT hr = S_OK; + + // OEM can use similar function to get the status of MFT + // From their own application + if(!pState) + { + return E_POINTER; + } + *pState = m_uiInternalState; + + return hr; +} + +////////////////////////////////////////////////////////////////////////////////// +// +// This initializes the CSocMFT0 for Com +// +// +HRESULT CSocMft0::CreateInstance( + REFIID iid, + void **ppMFT +) +{ + HRESULT hr = S_OK; + ComPtr spMFT; + UNREFERENCED_PARAMETER(iid); + + auto sObject = Microsoft::WRL::Make(); + + if(!sObject) + { + return E_OUTOFMEMORY; + } + hr = sObject->FinalConstruct(); + + if(SUCCEEDED(hr)) + { + hr = sObject.As(&spMFT); + *ppMFT = spMFT.Detach(); + } + + return hr; +} + +///////////////////////////////////////////////////////////////////////////////// +// +// IInspectable interface enable COM interface to talk with other programming environments +// +// Gets the interfaces that are implemented by the current Windows Runtime class. +// +STDMETHODIMP CSocMft0::GetIids( + _Out_ ULONG *iidCount, + _Outptr_result_buffer_maybenull_(*iidCount) IID **iids +) +{ + HRESULT hr = S_OK; + + if(!iidCount) + { + return E_POINTER; + } + + if(!iids) + { + return E_POINTER; + } + + *iids = NULL; + *iidCount = 0; + + return hr; +} + +///////////////////////////////////////////////////////////////////////////////// +// +// Gets the fully qualified name of the current Windows Runtime object. +// +STDMETHODIMP CSocMft0::GetRuntimeClassName( + _Outptr_result_maybenull_ HSTRING *pClassName +) +{ + + if(!pClassName) + { + return E_POINTER; + } + return WindowsCreateString(NULL, 0, pClassName); +} + +///////////////////////////////////////////////////////////////////////////////// +// +// Gets the trust level of the current Windows Runtime object. +// +STDMETHODIMP CSocMft0::GetTrustLevel( + _Out_ TrustLevel *trustLevel +) +{ + HRESULT hr = S_OK; + + if(trustLevel) + { + return E_POINTER; + } + *trustLevel = TrustLevel::BaseTrust; + + return hr; +} + +//////////////////////////////////////////////////////////////////////// +// +// Final Construct +// +HRESULT CSocMft0::FinalConstruct() +{ + HRESULT hr = S_OK; + hr = MFCreateAttributes(&m_spGlobalAttributes, 3); + if (FAILED(hr)) + { + goto done; + } + + hr = m_spGlobalAttributes->SetUINT32(MF_TRANSFORM_ASYNC, FALSE); + if (FAILED(hr)) + { + goto done; + } + + hr = m_spGlobalAttributes->SetString( + MFT_ENUM_HARDWARE_URL_Attribute, + L"Sample_CameraExtensionMft"); + if (FAILED(hr)) + { + goto done; + } + + hr = m_spGlobalAttributes->SetUINT32( + MFT_SUPPORT_DYNAMIC_FORMAT_CHANGE, + TRUE); + +done: + if(FAILED(hr)) + { + m_spGlobalAttributes.Reset(); + } + return hr; +} + +// IMFTransform methods. Refer to the Media Foundation SDK documentation for details. + +//////////////////////////////////////////////////////////////////////// +// +// GetStreamLimits +// Returns the minimum and maximum number of streams. +// +STDMETHODIMP CSocMft0::GetStreamLimits( + _Out_ DWORD *pdwInputMinimum, + _Out_ DWORD *pdwInputMaximum, + _Out_ DWORD *pdwOutputMinimum, + _Out_ DWORD *pdwOutputMaximum +) +{ + if( (!pdwInputMinimum) || + (!pdwInputMaximum) || + (!pdwOutputMinimum) || + (!pdwOutputMaximum)) + { + return E_POINTER; + } + + // This MFT has a fixed number of streams. + *pdwInputMinimum = 1; + *pdwInputMaximum = 1; + *pdwOutputMinimum = 1; + *pdwOutputMaximum = 1; + + return S_OK; +} + +//////////////////////////////////////////////////////////////////////// +// +// GetStreamCount +// Returns the actual number of streams. +// +STDMETHODIMP CSocMft0::GetStreamCount( + _Out_ DWORD *pcInputStreams, + _Out_ DWORD *pcOutputStreams +) +{ + if(!pcInputStreams || !pcOutputStreams) + { + return E_POINTER; + } + + // This MFT has a fixed number of streams. + *pcInputStreams = 1; + *pcOutputStreams = 1; + + return S_OK; +} + +//////////////////////////////////////////////////////////////////////// +// +// GetStreamIDs +// Returns stream IDs for the input and output streams. +// +STDMETHODIMP CSocMft0::GetStreamIDs( + DWORD dwInputIDArraySize, + _Out_writes_(dwInputIDArraySize) DWORD *pdwInputIDs, + DWORD dwOutputIDArraySize, + _Out_writes_(dwOutputIDArraySize) DWORD *pdwOutputIDs +) +{ + UNREFERENCED_PARAMETER(dwInputIDArraySize); + UNREFERENCED_PARAMETER(dwOutputIDArraySize); + UNREFERENCED_PARAMETER(pdwInputIDs); + UNREFERENCED_PARAMETER(pdwOutputIDs); + + // It is not required to implement this method if the MFT has a fixed number of + // streams AND the stream IDs are numbered sequentially from zero (that is, the + // stream IDs match the stream indexes). + + // In that case, it is OK to return E_NOTIMPL. + return (E_NOTIMPL); +} + +//////////////////////////////////////////////////////////////////////// +// +// GetInputStreamInfo +// Returns information about an input stream. +// +STDMETHODIMP CSocMft0::GetInputStreamInfo( + DWORD dwInputStreamID, + _Out_ MFT_INPUT_STREAM_INFO *pStreamInfo +) +{ + CAutoLock lock(&m_critSec); + + if(!pStreamInfo) + { + return E_POINTER; + } + + if (!IsValidInputStream(dwInputStreamID)) + { + return MF_E_INVALIDSTREAMNUMBER; + } + + if(!m_spInputType) + { + return MF_E_TRANSFORM_TYPE_NOT_SET; + } + + pStreamInfo->hnsMaxLatency = 0; + pStreamInfo->cbAlignment = 0; + pStreamInfo->cbSize = 0; + pStreamInfo->dwFlags = + MFT_INPUT_STREAM_WHOLE_SAMPLES | + MFT_INPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER | + MFT_INPUT_STREAM_PROCESSES_IN_PLACE ; + pStreamInfo->hnsMaxLatency = 0; + + return S_OK; +} + +//////////////////////////////////////////////////////////////////////// +// +// GetOutputStreamInfo +// Returns information about an output stream. +// +STDMETHODIMP CSocMft0::GetOutputStreamInfo( + DWORD dwOutputStreamID, + _Out_ MFT_OUTPUT_STREAM_INFO *pStreamInfo +) +{ + CAutoLock lock(&m_critSec); + + if(!pStreamInfo) + { + return E_POINTER; + } + + if (!IsValidInputStream(dwOutputStreamID)) + { + return MF_E_INVALIDSTREAMNUMBER; + } + + if(!m_spOutputType) + { + return MF_E_TRANSFORM_TYPE_NOT_SET; + } + + 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; + + return S_OK; +} + +///////////////////////////////////////////////////////////////////////// +// +// GetAttributes +// Returns the global attributes for the MFT. +// +STDMETHODIMP CSocMft0::GetAttributes( + _Outptr_result_maybenull_ IMFAttributes **ppAttributes +) +{ + if(!ppAttributes) + { + return E_POINTER; + } + + return m_spGlobalAttributes.CopyTo(ppAttributes); +} + +//////////////////////////////////////////////////////////////////////// +// +// GetInputStreamAttributes +// Returns stream-level attributes for an input stream. +// +STDMETHODIMP CSocMft0::GetInputStreamAttributes( + DWORD dwInputStreamID, + _Outptr_result_maybenull_ IMFAttributes **ppAttributes +) +{ + HRESULT hr = S_OK; + + if(dwInputStreamID > 0) + { + return MF_E_INVALIDSTREAMNUMBER; + } + if(!ppAttributes) + { + return E_POINTER; + } + + if(!m_spInputAttributes) + { + hr = MFCreateAttributes(&m_spInputAttributes, 2); + if (FAILED(hr)) + { + goto done; + } + hr = m_spInputAttributes->SetUINT32(MFT_SUPPORT_DYNAMIC_FORMAT_CHANGE, TRUE); + if (FAILED(hr)) + { + goto done; + } + hr = m_spInputAttributes->SetString(MFT_ENUM_HARDWARE_URL_Attribute, L"Sample_CameraExtensionMft"); + if (FAILED(hr)) + { + goto done; + } + } + hr = m_spInputAttributes.CopyTo(ppAttributes); + +done: + if(FAILED(hr)) + { + m_spInputAttributes.Reset(); + } + return hr; +} + +//////////////////////////////////////////////////////////////////////// +// +// GetOutputStreamAttributes +// Returns stream-level attributes for an output stream. +// +STDMETHODIMP CSocMft0::GetOutputStreamAttributes( + DWORD dwOutputStreamID, + _Outptr_result_maybenull_ IMFAttributes **ppAttributes +) +{ + if(dwOutputStreamID > 0) + { + return MF_E_INVALIDSTREAMNUMBER; + } + + if(!ppAttributes) + { + return E_POINTER; + } + + if(!m_spInputAttributes) + { + return MF_E_TRANSFORM_TYPE_NOT_SET; + } + + return m_spInputAttributes.CopyTo(ppAttributes); +} + +//////////////////////////////////////////////////////////////////////// +// +// DeleteInputStream +// +STDMETHODIMP CSocMft0::DeleteInputStream( + DWORD dwStreamID +) +{ + UNREFERENCED_PARAMETER(dwStreamID); + + // This MFT has a fixed number of input streams, so the method is not supported. + return (E_NOTIMPL); +} + +//////////////////////////////////////////////////////////////////////// +// +// AddInputStreams +// +STDMETHODIMP CSocMft0::AddInputStreams( + DWORD cStreams, + _In_ DWORD *adwStreamIDs +) +{ + UNREFERENCED_PARAMETER(cStreams); + UNREFERENCED_PARAMETER(adwStreamIDs); + + // This MFT has a fixed number of output streams, so the method is not supported. + return (E_NOTIMPL); +} + +//////////////////////////////////////////////////////////////////////// +// +// GetInputAvailableType +// Returns a preferred input type. +// +STDMETHODIMP CSocMft0::GetInputAvailableType( + DWORD dwInputStreamID, + DWORD dwTypeIndex, + _Outptr_result_maybenull_ IMFMediaType **ppType +) +{ + HRESULT hr = S_OK; + ComPtr spUnknown; + ComPtr spSourceAttributes; + + CAutoLock lock(&m_critSec); + if(!IsValidInputStream(dwInputStreamID)) + { + return MF_E_INVALIDSTREAMNUMBER; + } + + if(!m_spSourceTransform && m_spInputAttributes) + { + hr = m_spInputAttributes->GetUnknown(MFT_CONNECTED_STREAM_ATTRIBUTE, IID_PPV_ARGS(spSourceAttributes.ReleaseAndGetAddressOf())); + if (FAILED(hr)) + { + return hr; + } + + hr = spSourceAttributes->GetUnknown(MF_DEVICESTREAM_EXTENSION_PLUGIN_CONNECTION_POINT, IID_PPV_ARGS(spUnknown.ReleaseAndGetAddressOf())); + if (FAILED(hr)) + { + return hr; + } + + hr = spUnknown.As(&m_spSourceTransform); + if (FAILED(hr)) + { + return hr; + } + + hr = spSourceAttributes->GetGUID(MF_DEVICESTREAM_STREAM_CATEGORY, &m_stStreamType); + if (FAILED(hr)) + { + return hr; + } + + hr = spSourceAttributes->GetUINT32(MF_DEVICESTREAM_STREAM_ID, &m_uiSourceStreamId); + if (FAILED(hr)) + { + return hr; + } + + hr = GenerateMFMediaTypeListFromDevice(); + if (FAILED(hr)) + { + return hr; + } + } + + hr = GetMediaType(dwInputStreamID, dwTypeIndex, ppType); + + return hr; +} + +//////////////////////////////////////////////////////////////////////// +// +// GetOutputAvailableType +// Returns a preferred output type. +// +STDMETHODIMP CSocMft0::GetOutputAvailableType( + DWORD dwOutputStreamID, + DWORD dwTypeIndex, + _Outptr_result_maybenull_ IMFMediaType **ppType +) +{ + HRESULT hr = S_OK; + ComPtr spUnknown; + ComPtr spSourceAttributes; + + CAutoLock lock(&m_critSec); + + if (!IsValidInputStream(dwOutputStreamID)) + { + return MF_E_INVALIDSTREAMNUMBER; + } + + if (!m_spSourceTransform && m_spInputAttributes) + { + hr = m_spInputAttributes->GetUnknown(MFT_CONNECTED_STREAM_ATTRIBUTE, IID_PPV_ARGS(spSourceAttributes.ReleaseAndGetAddressOf())); + if (FAILED(hr)) + { + return hr; + } + + hr = spSourceAttributes->GetUnknown(MF_DEVICESTREAM_EXTENSION_PLUGIN_CONNECTION_POINT, IID_PPV_ARGS(spUnknown.ReleaseAndGetAddressOf())); + if (FAILED(hr)) + { + return hr; + } + + hr = spUnknown.As(&m_spSourceTransform); + if (FAILED(hr)) + { + return hr; + } + + hr = spSourceAttributes->GetGUID(MF_DEVICESTREAM_STREAM_CATEGORY, &m_stStreamType); + if (FAILED(hr)) + { + return hr; + } + + hr = spSourceAttributes->GetUINT32(MF_DEVICESTREAM_STREAM_ID, &m_uiSourceStreamId); + if (FAILED(hr)) + { + return hr; + } + + // + // This is the first function get called after the MFT0 object get instantiated, + // Here we can generate the supported media type from the connected input pin, + // Also, we can selectively plug in MFT0 only on pins requires processing + // by returning error on the unwanted pin types. Example below if we did not + // want MFT0 for Record + + //if(m_stStreamType != PINNAME_IMAGE && m_stStreamType != PINNAME_VIDEO_PREVIEW) + //{ + // return E_UNEXPECTED; + //} + + hr = GenerateMFMediaTypeListFromDevice(); + if (FAILED(hr)) + { + return hr; + } + } + + return GetMediaType(dwOutputStreamID, dwTypeIndex, ppType); +} + +//////////////////////////////////////////////////////////////////////// +// +// SetInputType +// +STDMETHODIMP CSocMft0::SetInputType( + DWORD dwInputStreamID, + _In_opt_ IMFMediaType *pType, + DWORD dwFlags +) +{ + HRESULT hr = S_OK; + + CAutoLock lock(&m_critSec); + + BOOL bReallySet = ((dwFlags & MFT_SET_TYPE_TEST_ONLY) == 0); + + // Validate flags. + // Only MFT_SET_TYPE_TEST_ONLY is supported, if any other bit flags + // get set, it should return error. + if(dwFlags & ~MFT_SET_TYPE_TEST_ONLY) + { + return E_INVALIDARG; + } + + if(!bReallySet) + { + return IsMediaTypeSupported(dwInputStreamID, pType); + } + + ComPtr spFullType; + hr = IsMediaTypeSupported(dwInputStreamID, pType, spFullType.ReleaseAndGetAddressOf()); + if(FAILED(hr)) + { + return hr; + } + + m_spInputType = spFullType; + + return S_OK; +} + +//////////////////////////////////////////////////////////////////////// +// +// SetOutputType +// This function set the output media type and at the same time, it should +// also choose a compatible input type +// +STDMETHODIMP CSocMft0::SetOutputType( + DWORD dwOutputStreamID, + _In_opt_ IMFMediaType *pType, + DWORD dwFlags +) +{ + HRESULT hr = S_OK; + + CAutoLock lock(&m_critSec); + BOOL bReallySet = ((dwFlags & MFT_SET_TYPE_TEST_ONLY) == 0); + BOOL bUseModifiedInputType = FALSE; + + // Validate flags. + if(dwFlags & ~MFT_SET_TYPE_TEST_ONLY) + { + return E_INVALIDARG; + } + + if(!bReallySet) + { + return IsMediaTypeSupported(dwOutputStreamID, pType); + } + + ComPtr spFullType; + ComPtr spFullInputType; + hr = IsMediaTypeSupported(dwOutputStreamID, pType, spFullType.ReleaseAndGetAddressOf()); + if(FAILED(hr)) + { + return hr; + } + + if (m_stStreamType == PINNAME_CAPTURE) + { + hr = GetVideoStabilizationEnabled(); + if (FAILED(hr)) + { + return hr; + } + + // Hardware Video Stabilization is enabled + if (m_bEnableVideoStabilization) + { + UINT32 uiIndex = 0; + //This shouldn't fail, since it is derived from IsMediaTypeSupported + // and that just passed. + hr = FindMediaIndex(dwOutputStreamID, pType, &uiIndex); + if (FAILED(hr)) + { + return hr; + } + //Device Driver contains a static list of mediatypes on the pin + // We have ordered them in such a way so that all odd mediatypes are the + // overscan media of the even mediatype immediatley proceeding it, ie. + // -normalVGA + // -overscanVGA + // -normalQVGA + // -overscanQVGA + // etc + // The following command determines if we are dealing with an overscan or normal mediatype + // if it is overscan, we fail (cannot overscan an overscan mediatype) + // otherwise, we set our input mediatype to the overscan type associated with it. + if (uiIndex % 2 != 0) //odd means overscan + { + return MF_E_INVALIDMEDIATYPE; + } + else //even + { + hr = GetMediaType(dwOutputStreamID, uiIndex + 1, spFullInputType.ReleaseAndGetAddressOf()); + if (FAILED(hr)) + { + return hr; + } + + bUseModifiedInputType = TRUE; + } + } + } + m_spOutputType = spFullType; + + // Here, we choose a compatible input type which is the same type + // as output. If OEM choose to encode sample here, it should choose + // an input which is supported by encoder. + if (bUseModifiedInputType) + { + m_spInputType = spFullInputType; + } + else + { + m_spInputType = spFullType; + } + + return S_OK; +} + +//////////////////////////////////////////////////////////////////////// +// +// GetInputCurrentType +// Returns the current input type. +// +STDMETHODIMP CSocMft0::GetInputCurrentType( + DWORD dwInputStreamID, + _Outptr_result_maybenull_ IMFMediaType **ppType +) +{ + HRESULT hr = S_OK; + + CAutoLock lock(&m_critSec); + + if(!ppType) + { + return E_POINTER; + } + if (!IsValidInputStream(dwInputStreamID)) + { + return MF_E_INVALIDSTREAMNUMBER; + } + + if(!m_spInputType) + { + return MF_E_TRANSFORM_TYPE_NOT_SET; + } + + ComPtr spMediaType; + hr = MFCreateMediaType(spMediaType.ReleaseAndGetAddressOf()); + if (FAILED(hr)) + { + return hr; + } + + hr = m_spInputType->CopyAllItems(spMediaType.Get()); + if (FAILED(hr)) + { + return hr; + } + + return spMediaType.CopyTo(ppType); +} + +//////////////////////////////////////////////////////////////////////// +// +// GetOutputCurrentType +// Returns the current output type. +// +STDMETHODIMP CSocMft0::GetOutputCurrentType( + DWORD dwOutputStreamID, + _Outptr_result_maybenull_ IMFMediaType **ppType +) +{ + HRESULT hr = S_OK; + + CAutoLock lock(&m_critSec); + + if(!ppType) + { + return E_POINTER; + } + + if (!IsValidOutputStream(dwOutputStreamID)) + { + return MF_E_INVALIDSTREAMNUMBER; + } + if(!m_spOutputType) + { + return MF_E_TRANSFORM_TYPE_NOT_SET; + } + + ComPtr spMediaType; + hr = MFCreateMediaType(spMediaType.ReleaseAndGetAddressOf()); + if (FAILED(hr)) + { + return hr; + } + + hr = m_spOutputType->CopyAllItems(spMediaType.Get()); + if (FAILED(hr)) + { + return hr; + } + + return spMediaType.CopyTo(ppType); +} + +//////////////////////////////////////////////////////////////////////// +// +// GetInputStatus +// Query if the MFT is accepting more input. +// +STDMETHODIMP CSocMft0::GetInputStatus( + DWORD dwInputStreamID, + _Out_ DWORD *pdwFlags +) +{ + CAutoLock lock(&m_critSec); + + if(!pdwFlags) + { + return E_POINTER; + } + + if (!IsValidInputStream(dwInputStreamID)) + { + return 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_spSample == NULL) + { + *pdwFlags = MFT_INPUT_STATUS_ACCEPT_DATA; + } + else + { + *pdwFlags = 0; + } + + return S_OK; +} + +//////////////////////////////////////////////////////////////////////// +// +// GetOutputStatus +// Query if the MFT can produce output. +// +STDMETHODIMP CSocMft0::GetOutputStatus( + _Out_ DWORD *pdwFlags +) +{ + if(!pdwFlags) + { + return E_POINTER; + } + + CAutoLock lock(&m_critSec); + + // We can produce an output sample if (and only if) + // we have an input sample. + if (m_spSample != NULL) + { + *pdwFlags = MFT_OUTPUT_STATUS_SAMPLE_READY; + } + else + { + *pdwFlags = 0; + } + + return S_OK; +} + +//////////////////////////////////////////////////////////////////////// +// +// SetOutputBounds +// Sets the range of time stamps that the MFT will output. +// +STDMETHODIMP CSocMft0::SetOutputBounds( + LONGLONG hnsLowerBound, + LONGLONG hnsUpperBound +) +{ + UNREFERENCED_PARAMETER(hnsLowerBound); + UNREFERENCED_PARAMETER(hnsUpperBound); + + // Implementation of this method is optional. + return (E_NOTIMPL); +} + +//////////////////////////////////////////////////////////////////////// +// +// ProcessEvent +// Sends an event to an input stream. +// +STDMETHODIMP CSocMft0::ProcessEvent( + DWORD dwInputStreamID, + _In_opt_ IMFMediaEvent *pEvent +) +{ + UNREFERENCED_PARAMETER(dwInputStreamID); + UNREFERENCED_PARAMETER(pEvent); + return E_NOTIMPL; +} + +//////////////////////////////////////////////////////////////////////// +// +// ProcessMessage +// +STDMETHODIMP CSocMft0::ProcessMessage( + MFT_MESSAGE_TYPE eMessage, + ULONG_PTR ulParam +) +{ + UNREFERENCED_PARAMETER(ulParam); + HRESULT hr = S_OK; + + CAutoLock lock(&m_critSec); + + switch (eMessage) + { + case MFT_MESSAGE_COMMAND_FLUSH: + // Flush the MFT. + hr = OnFlush(); + break; + } + + return hr; +} + +///////////////////////////////////////////////////////////////////// +// +// ProcessInput +// Process an input sample. The sample is cached and real work is done +// in ProcessOutput +// +STDMETHODIMP CSocMft0::ProcessInput( + DWORD dwInputStreamID, + IMFSample *pSample, + DWORD dwFlags +) +{ + HRESULT hr = S_OK; + + CAutoLock lock(&m_critSec); + + if(!pSample) + { + return E_POINTER; + } + if(dwFlags != 0) + { + return E_INVALIDARG; + } + + if (!IsValidInputStream(dwInputStreamID)) + { + return MF_E_INVALIDSTREAMNUMBER; + } + + if (!m_spInputType || !m_spOutputType) + { + return MF_E_NOTACCEPTING; // Client must set input and output types. + } + + if (m_spSample != NULL) + { + return 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. + DWORD dwBufferCount = 0; + hr = pSample->GetBufferCount(&dwBufferCount); + if(FAILED(hr)) + { + return hr; + } + + if (dwBufferCount == 0) + { + return E_FAIL; + } + + if (dwBufferCount > 1) + { + return MF_E_SAMPLE_HAS_TOO_MANY_BUFFERS; + } + + // Cache the sample. We do the actual work in ProcessOutput. + m_spSample = pSample; + + return S_OK; +} + +///////////////////////////////////////////////////////////////////// +// +// ProcessOutput +// Process an output sample. +// +STDMETHODIMP CSocMft0::ProcessOutput( + DWORD dwFlags, + DWORD cOutputBufferCount, + MFT_OUTPUT_DATA_BUFFER *pOutputSamples, + DWORD *pdwStatus +) +{ + HRESULT hr = S_OK; + + CAutoLock lock(&m_critSec); + + if(dwFlags != 0) + { + return E_INVALIDARG; + } + + if(!pOutputSamples || !pdwStatus) + { + return E_POINTER; + } + + if( pOutputSamples[0].pSample) + { + pOutputSamples[0].pSample->Release(); + pOutputSamples[0].pSample = nullptr; + } + + // Must be exactly one output buffer. + if(cOutputBufferCount != 1) + { + return E_INVALIDARG; + } + + // If we don't have an input sample, we need some input before + // we can generate any output. + if(m_spSample == nullptr) + { + return MF_E_TRANSFORM_NEED_MORE_INPUT; + } + + hr = ProcessMetadata(); + if(FAILED(hr)) + { + //Log the failure + hr = S_OK; + } + hr = CreateOutputSample(&pOutputSamples[0].pSample); + if(FAILED(hr)) + { + return hr; + } + pOutputSamples[0].dwStatus = 0; + *pdwStatus = 0; + + // if createOutputSample actually make a copy of the sample, + // (for example, JPEG encoding case), we need to copy the + // attribute from m_spSample to spOutputIMFSample + m_spSample.Reset(); + return hr; +} + +///////////////////////////////////////////////////////////////////// +// +// The actual processing is here, required operation is demux +// Other operation is optional +// +HRESULT +CSocMft0:: +CreateOutputSample( + _Outptr_result_maybenull_ + IMFSample **ppSample +) +{ + HRESULT hr = S_OK; + + //Do nothing for now + if(!ppSample) + { + return E_POINTER; + } + + m_spSample.CopyTo(ppSample); + + return hr; +} +///////////////////////////////////////////////////////////////////// +// +// Flush the MFT. +// +STDMETHODIMP CSocMft0::OnFlush() +{ + // For this MFT, flushing just means releasing the input sample. + CAutoLock lock(&m_critSec); + m_spSample.Reset(); + return S_OK; +} + +///////////////////////////////////////////////////////////////////// +// +// Get the mediaType of selected stream. +// +STDMETHODIMP CSocMft0::GetMediaType( + _In_ DWORD dwStreamId, + _In_ DWORD dwTypeIndex, + _Outptr_result_maybenull_ IMFMediaType **ppType +) +{ + HRESULT hr = S_OK; + ComPtr spMediaType; + + if(!ppType) + { + return E_POINTER; + } + + if(dwStreamId != 0) + { + return MF_E_INVALIDSTREAMNUMBER; + } + + if(dwTypeIndex >= m_listOfMediaTypes.size()) + { + return MF_E_NO_MORE_TYPES; + } + + hr = MFCreateMediaType(spMediaType.ReleaseAndGetAddressOf()); + if (FAILED(hr)) + { + return hr; + } + + hr = m_listOfMediaTypes[dwTypeIndex]->CopyAllItems(spMediaType.Get()); + if (FAILED(hr)) + { + return hr; + } + + return spMediaType.CopyTo(ppType); +} + +///////////////////////////////////////////////////////////////////// +// +// Check if the media type is supported by MFT0. +// +STDMETHODIMP CSocMft0::IsMediaTypeSupported( + _In_ UINT uiStreamId, + _In_ IMFMediaType *pIMFMediaType, + _Outptr_result_maybenull_ IMFMediaType **ppIMFMediaTypeFull +) +{ + HRESULT hr = S_OK; + + if(!pIMFMediaType) + { + return E_POINTER; + } + + if(uiStreamId != 0) + { + return MF_E_INVALIDINDEX; + } + + BOOL bFound =FALSE; + + + for(UINT i = 0; i< m_listOfMediaTypes.size(); 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) + { + m_listOfMediaTypes[i].CopyTo(ppIMFMediaTypeFull); + } + break; + } + else if(FAILED(hr)) + { + return hr; + } + } + + if(bFound == FALSE) + { + return MF_E_INVALIDMEDIATYPE; + } + + return S_OK; +} + +///////////////////////////////////////////////////////////////////// +// +// Check if the media type is supported by MFT0. +// +_Success_(return == 0) +STDMETHODIMP CSocMft0::FindMediaIndex( + _In_ UINT uiStreamId, + _In_ IMFMediaType *pIMFMediaType, + _Out_ UINT *puiMediaIndex +) +{ + HRESULT hr = S_OK; + + if(!pIMFMediaType || !puiMediaIndex) + { + return E_INVALIDARG; + } + + if(uiStreamId != 0) + { + return MF_E_INVALIDINDEX; + } + + BOOL bFound = FALSE; + + for(UINT i = 0; i< m_listOfMediaTypes.size(); 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; + *puiMediaIndex = i; + break; + } + else if(FAILED(hr)) + { + return hr; + } + } + + if(bFound == FALSE) + { + return MF_E_INVALIDMEDIATYPE; + } + + return S_OK; +} + +///////////////////////////////////////////////////////////////////// +// +// Generate the supported input media type from device source media type +// +STDMETHODIMP CSocMft0::GenerateMFMediaTypeListFromDevice() +{ + HRESULT hr = S_OK; + + if(!m_spSourceTransform) + { + return MF_E_NOT_FOUND; + } + + m_listOfMediaTypes.clear(); + UINT iMediaType = 0; + while(SUCCEEDED(hr)) + { + ComPtr spMediaType; + hr = m_spSourceTransform->GetOutputAvailableType(m_uiSourceStreamId, iMediaType, spMediaType.ReleaseAndGetAddressOf()); + if(FAILED(hr)) + { + if(hr == MF_E_NO_MORE_TYPES) + { + hr = S_OK; + } + break; + } + + if(m_stStreamType == PINNAME_IMAGE || m_stStreamType == PINNAME_VIDEO_STILL) + { + GUID guidPhotoSubType = {}; + spMediaType->GetGUID(MF_MT_SUBTYPE, &guidPhotoSubType); + if (IsEqualGUID(guidPhotoSubType, MFVideoFormat_NV12)) + { + // Need to set MF_MT_VIDEO_NOMINAL_RANGE on NV12 or the pipeline ends up with a copy due to mismatched nominal range + hr = spMediaType->SetUINT32(MF_MT_VIDEO_NOMINAL_RANGE, MFNominalRange_0_255); + if (FAILED(hr)) + { + return hr; + } + } + + + m_listOfMediaTypes.push_back(spMediaType); + } + + else if (m_stStreamType == PINNAME_CAPTURE) + { + UINT32 width = 0; + UINT32 height = 0; + hr = MFGetAttributeSize(spMediaType.Get(), MF_MT_FRAME_SIZE, &width, &height); + if (FAILED(hr)) + { + return hr; + } + + MFVideoArea validVideo; + validVideo.OffsetX.value = 0; + validVideo.OffsetX.fract = 0; + validVideo.OffsetY.value = 0; + validVideo.OffsetY.fract = 0; + validVideo.Area.cx = width; + validVideo.Area.cy = height; + hr = spMediaType->SetBlob(MF_MT_MINIMUM_DISPLAY_APERTURE, (BYTE *)&validVideo, sizeof(validVideo)); + if (FAILED(hr)) + { + return hr; + } + + m_listOfMediaTypes.push_back(spMediaType); + } + + else + { + m_listOfMediaTypes.push_back(spMediaType); + } + + iMediaType++; + } + + return hr; +} + +///////////////////////////////////////////////////////////////////// +// +// Get the current media type in order to generate photo confirmation +// +HRESULT CSocMft0::GetPreviewMediaType( + _Outptr_result_maybenull_ IMFMediaType **ppType +) +{ + CAutoLock lock(&m_critSec); + if(!m_spSourceTransform) + { + return MF_E_NOT_FOUND; + } + + DWORD dwInStreamCount = 0, dwOutStreamCount = 0; + HRESULT hr = m_spSourceTransform->GetStreamCount( + &dwInStreamCount, + &dwOutStreamCount); + if (FAILED(hr)) + { + return hr; + } + + for(UINT32 i = 0 ; i < dwOutStreamCount; i++) + { + ComPtr spAttributes; + hr = m_spSourceTransform->GetOutputStreamAttributes(i, spAttributes.ReleaseAndGetAddressOf()); + if(FAILED(hr)) + { + continue; + } + + GUID guidPinCategory = GUID_NULL; + hr = spAttributes->GetGUID( + MF_DEVICESTREAM_STREAM_CATEGORY, + &guidPinCategory); + if(FAILED(hr)) + { + continue; + } + if (IsEqualGUID(guidPinCategory, PINNAME_VIDEO_PREVIEW)) + { + ComPtr spMediaType; + hr = m_spSourceTransform->GetOutputCurrentType(i, spMediaType.ReleaseAndGetAddressOf()); + if(FAILED(hr)) + { + return MF_E_NOT_FOUND; + } + return spMediaType.CopyTo(ppType); + } + } + + return MF_E_NOT_FOUND; +} + +///////////////////////////////////////////////////////////////////// +// +// Handle the Metadata with the buffer +// +HRESULT CSocMft0::ProcessMetadata() +{ + ComPtr spMetadata; + HRESULT hr = m_spSample->GetUnknown(MFSampleExtension_CaptureMetadata, IID_PPV_ARGS(spMetadata.ReleaseAndGetAddressOf())); + if(FAILED(hr)) + { + return hr; + } + + ComPtr spBuffer; + hr = spMetadata->GetUnknown(MF_CAPTURE_METADATA_FRAME_RAWSTREAM, IID_PPV_ARGS(spBuffer.ReleaseAndGetAddressOf())); + if(FAILED(hr)) + { + return hr; + } + + MediaBufferLock bufferLock(spBuffer.Get()); + BYTE *pData = NULL; + DWORD dwLength = 0; + hr = bufferLock.LockBuffer(&pData, NULL, &dwLength); + if(FAILED(hr)) + { + return hr; + } + + // OEM put meta data passing logic here, + if(FAILED(hr)) + { + return hr; + } + + LONG lBufferLeft = static_cast(dwLength); + if(lBufferLeft < sizeof(KSCAMERA_METADATA_ITEMHEADER)) + { + return E_UNEXPECTED; + } + + PKSCAMERA_METADATA_ITEMHEADER pItem = (PKSCAMERA_METADATA_ITEMHEADER) pData; + + while(lBufferLeft > 0) + { + switch (pItem->MetadataId) + { + case MetadataId_Custom_PreviewAggregation: + hr = ParseMetadata_PreviewAggregation(pItem, spMetadata.Get()); + if(FAILED(hr)) + { + return hr; + } + break; + case MetadataId_Custom_ImageAggregation: + hr = ParseMetadata_ImageAggregation(pItem, spMetadata.Get()); + if(FAILED(hr)) + { + return hr; + } + break; + case MetadataId_Custom_Histogram: + hr = ParseMetadata_Histogram(pItem, spMetadata.Get()); + if(FAILED(hr)) + { + return hr; + } + break; + case MetadataId_Custom_FaceDetection: + hr = ParseMetadata_FaceDetection(pItem, spMetadata.Get()); + if (FAILED(hr)) + { + return hr; + } + } + + if(!pItem->Size) + { + // 0 size item will cause the loop to break and + // we will report buffer malformated + break; + } + lBufferLeft -= (LONG)pItem->Size; + if(lBufferLeft < sizeof(KSCAMERA_METADATA_ITEMHEADER)) + { + break; + } + pItem = reinterpret_cast + (reinterpret_cast(pItem) + pItem->Size); + } + + if(lBufferLeft != 0) + { + //Check and log for malformated data + return E_UNEXPECTED; + } + + return S_OK; +} + +HRESULT CSocMft0::ParseMetadata_PreviewAggregation( + _In_ PKSCAMERA_METADATA_ITEMHEADER pItem, + _In_ IMFAttributes *pMetaDataAttributes +) +{ + HRESULT hr = S_OK; + if(pItem->Size < sizeof(CAMERA_METADATA_PREVIEWAGGREGATION)) + { + return E_UNEXPECTED; + } + + PCAMERA_METADATA_PREVIEWAGGREGATION pFixedStruct = + (PCAMERA_METADATA_PREVIEWAGGREGATION)pItem; + + hr = pMetaDataAttributes->SetUINT32( + MF_CAPTURE_METADATA_FOCUSSTATE, + pFixedStruct->Data.FocusState); + if(FAILED(hr)) + { + return hr; + } + + if(pFixedStruct->Data.ExposureTime.Set) + { + hr = pMetaDataAttributes->SetUINT64( + MF_CAPTURE_METADATA_EXPOSURE_TIME, + pFixedStruct->Data.ExposureTime.Value); + if(FAILED(hr)) + { + return hr; + } + } + + if(pFixedStruct->Data.ISOSpeed.Set) + { + hr = pMetaDataAttributes->SetUINT32( + MF_CAPTURE_METADATA_ISO_SPEED, + pFixedStruct->Data.ISOSpeed.Value); + if(FAILED(hr)) + { + return hr; + } + } + + if(pFixedStruct->Data.LensPosition.Set) + { + hr = pMetaDataAttributes->SetUINT32( + MF_CAPTURE_METADATA_LENS_POSITION, + pFixedStruct->Data.LensPosition.Value); + if(FAILED(hr)) + { + return hr; + } + } + + if(pFixedStruct->Data.FlashOn.Set) + { + hr = pMetaDataAttributes->SetUINT32( + MF_CAPTURE_METADATA_FLASH, + pFixedStruct->Data.FlashOn.Value); + if(FAILED(hr)) + { + return hr; + } + } + + if(pFixedStruct->Data.WhiteBalanceMode.Set) + { + hr = pMetaDataAttributes->SetUINT32( + MF_CAPTURE_METADATA_WHITEBALANCE, + pFixedStruct->Data.WhiteBalanceMode.Value); + if(FAILED(hr)) + { + return hr; + } + } + + if(pFixedStruct->Data.EVCompensation.Set) + { + CapturedMetadataExposureCompensation EVCompensation; + + EVCompensation.Flags = pFixedStruct->Data.EVCompensation.Flags; + EVCompensation.Value = pFixedStruct->Data.EVCompensation.Value; + + hr = pMetaDataAttributes->SetBlob( + MF_CAPTURE_METADATA_EXPOSURE_COMPENSATION, + (const UINT8 *)&EVCompensation, + sizeof(EVCompensation)); + if(FAILED(hr)) + { + return hr; + } + } + + if(pFixedStruct->Data.SensorFrameRate.Set) + { + hr = pMetaDataAttributes->SetUINT64( + MF_CAPTURE_METADATA_SENSORFRAMERATE, + pFixedStruct->Data.SensorFrameRate.Value); + if(FAILED(hr)) + { + return hr; + } + } + + if( pFixedStruct->Data.IsoAnalogGain.Set || + pFixedStruct->Data.IsoDigitalGain.Set ) + { + CapturedMetadataISOGains IsoGains; + + if( pFixedStruct->Data.IsoAnalogGain.Set ) + { + IsoGains.AnalogGain = + FLOAT( pFixedStruct->Data.IsoAnalogGain.Numerator ) / + FLOAT( pFixedStruct->Data.IsoAnalogGain.Denominator ); + } + if( pFixedStruct->Data.IsoDigitalGain.Set ) + { + IsoGains.DigitalGain = + FLOAT( pFixedStruct->Data.IsoDigitalGain.Numerator ) / + FLOAT( pFixedStruct->Data.IsoDigitalGain.Denominator ); + } + + hr = pMetaDataAttributes->SetBlob( + MF_CAPTURE_METADATA_ISO_GAINS, + (const UINT8 *)&IsoGains, + sizeof(IsoGains)); + if(FAILED(hr)) + { + return hr; + } + } + + if( pFixedStruct->Data.WhiteBalanceGain_R.Set || + pFixedStruct->Data.WhiteBalanceGain_G.Set || + pFixedStruct->Data.WhiteBalanceGain_B.Set ) + { + CapturedMetadataWhiteBalanceGains WhiteBalanceGains; + + if( pFixedStruct->Data.WhiteBalanceGain_R.Set ) + { + WhiteBalanceGains.R = + FLOAT( pFixedStruct->Data.WhiteBalanceGain_R.Numerator ) / + FLOAT( pFixedStruct->Data.WhiteBalanceGain_R.Denominator ); + } + if( pFixedStruct->Data.WhiteBalanceGain_G.Set ) + { + WhiteBalanceGains.G = + FLOAT( pFixedStruct->Data.WhiteBalanceGain_G.Numerator ) / + FLOAT( pFixedStruct->Data.WhiteBalanceGain_G.Denominator ); + } + if( pFixedStruct->Data.WhiteBalanceGain_B.Set ) + { + WhiteBalanceGains.B = + FLOAT( pFixedStruct->Data.WhiteBalanceGain_B.Numerator ) / + FLOAT( pFixedStruct->Data.WhiteBalanceGain_B.Denominator ); + } + + hr = pMetaDataAttributes->SetBlob( + MF_CAPTURE_METADATA_WHITEBALANCE_GAINS, + (const UINT8 *)&WhiteBalanceGains, + sizeof(WhiteBalanceGains)); + if(FAILED(hr)) + { + return hr; + } + } + + return S_OK; +} + +HRESULT CSocMft0::ParseMetadata_ImageAggregation( + _In_ PKSCAMERA_METADATA_ITEMHEADER pItem, + _In_ IMFAttributes *pMetaDataAttributes +) +{ + HRESULT hr = S_OK; + if(pItem->Size < sizeof(CAMERA_METADATA_IMAGEAGGREGATION)) + { + return E_UNEXPECTED; + } + + PCAMERA_METADATA_IMAGEAGGREGATION pFixedStruct = + (PCAMERA_METADATA_IMAGEAGGREGATION)pItem; + + if(pFixedStruct->Data.FrameId.Set) + { + hr = pMetaDataAttributes->SetUINT32( + MF_CAPTURE_METADATA_REQUESTED_FRAME_SETTING_ID, + pFixedStruct->Data.FrameId.Value); + if(FAILED(hr)) + { + return hr; + } + } + + if(pFixedStruct->Data.ExposureTime.Set) + { + hr = pMetaDataAttributes->SetUINT64( + MF_CAPTURE_METADATA_EXPOSURE_TIME, + pFixedStruct->Data.ExposureTime.Value); + if(FAILED(hr)) + { + return hr; + } + } + + if(pFixedStruct->Data.ISOSpeed.Set) + { + hr = pMetaDataAttributes->SetUINT32( + MF_CAPTURE_METADATA_ISO_SPEED, + pFixedStruct->Data.ISOSpeed.Value); + if(FAILED(hr)) + { + return hr; + } + } + + if(pFixedStruct->Data.LensPosition.Set) + { + hr = pMetaDataAttributes->SetUINT32( + MF_CAPTURE_METADATA_LENS_POSITION, + pFixedStruct->Data.LensPosition.Value); + if(FAILED(hr)) + { + return hr; + } + } + + if(pFixedStruct->Data.SceneMode.Set) + { + hr = pMetaDataAttributes->SetUINT64( + MF_CAPTURE_METADATA_SCENE_MODE, + pFixedStruct->Data.SceneMode.Value); + if(FAILED(hr)) + { + return hr; + } + } + + if(pFixedStruct->Data.FlashOn.Set) + { + hr = pMetaDataAttributes->SetUINT32( + MF_CAPTURE_METADATA_FLASH, + pFixedStruct->Data.FlashOn.Value); + if(FAILED(hr)) + { + return hr; + } + } + + if(pFixedStruct->Data.FlashPower.Set) + { + hr = pMetaDataAttributes->SetUINT32( + MF_CAPTURE_METADATA_FLASH_POWER, + pFixedStruct->Data.FlashPower.Value); + if(FAILED(hr)) + { + return hr; + } + } + + if(pFixedStruct->Data.WhiteBalanceMode.Set) + { + hr = pMetaDataAttributes->SetUINT32( + MF_CAPTURE_METADATA_WHITEBALANCE, + pFixedStruct->Data.WhiteBalanceMode.Value); + if(FAILED(hr)) + { + return hr; + } + } + + if(pFixedStruct->Data.ZoomFactor.Set) + { + hr = pMetaDataAttributes->SetUINT32( + MF_CAPTURE_METADATA_ZOOMFACTOR, + pFixedStruct->Data.ZoomFactor.Value); + if(FAILED(hr)) + { + return hr; + } + } + + if(pFixedStruct->Data.EVCompensation.Set) + { + CapturedMetadataExposureCompensation EVCompensation; + + EVCompensation.Flags = pFixedStruct->Data.EVCompensation.Flags; + EVCompensation.Value = pFixedStruct->Data.EVCompensation.Value; + + hr = pMetaDataAttributes->SetBlob( + MF_CAPTURE_METADATA_EXPOSURE_COMPENSATION, + (const UINT8 *)&EVCompensation, + sizeof(EVCompensation)); + if(FAILED(hr)) + { + return hr; + } + } + + if( pFixedStruct->Data.FocusState.Set ) + { + hr = pMetaDataAttributes->SetUINT32( + MF_CAPTURE_METADATA_FOCUSSTATE, + pFixedStruct->Data.FocusState.Value); + if(FAILED(hr)) + { + return hr; + } + } + return S_OK; +} + +struct HistogramData +{ + HistogramDataHeader Header; + ULONG Color[256]; + + HistogramData() + { + Header.Size = sizeof(*this); + Header.ChannelMask = 0; + Header.Linear = 1; + RtlZeroMemory( Color, sizeof(Color) ); + } +}; + +// This is the blob we pass back every time. +template +struct Histogram +{ + HistogramBlobHeader Blob; + // RGB or YUV Histograms. + HistogramHeader Header; + HistogramData Data[I]; + + Histogram( + _In_ ULONG Width=0, + _In_ ULONG Height=0 + ) + { + Blob.Histograms = 1; + Blob.Size = sizeof(*this); + + Header.Size = sizeof(Header) + sizeof(Data); + Header.Bins = 256; + Header.Grid.Height = Height; + Header.Grid.Width = Width; + Header.Grid.Region.top = 0; + Header.Grid.Region.left = 0; + Header.Grid.Region.bottom = Height-1; + Header.Grid.Region.right = Width-1; + } +}; + +#define MF_HISTOGRAM_RGB (MF_HISTOGRAM_CHANNEL_R | MF_HISTOGRAM_CHANNEL_G | MF_HISTOGRAM_CHANNEL_B ) +#define MF_HISTOGRAM_YCrCb (MF_HISTOGRAM_CHANNEL_Y | MF_HISTOGRAM_CHANNEL_Cr | MF_HISTOGRAM_CHANNEL_Cb) + +HRESULT CSocMft0::ParseMetadata_Histogram( + _In_ PKSCAMERA_METADATA_ITEMHEADER pItem, + _In_ IMFAttributes *pMetaDataAttributes +) +{ + if(pItem->Size < sizeof(CAMERA_METADATA_HISTOGRAM)) + { + return E_UNEXPECTED; + } + + PCAMERA_METADATA_HISTOGRAM pHistogram = (PCAMERA_METADATA_HISTOGRAM) pItem; + + if( (pHistogram->Data.ChannelMask & MF_HISTOGRAM_RGB) == MF_HISTOGRAM_RGB ) + { + Histogram<4> Blob( pHistogram->Data.Width, pHistogram->Data.Height ); + + Blob.Header.FourCC = pHistogram->Data.FourCC; + Blob.Header.ChannelMasks = pHistogram->Data.ChannelMask; + + // For a RGB Histogram, we fake the Y channel by copying the G channel. + Blob.Data[0].Header.ChannelMask = MF_HISTOGRAM_CHANNEL_Y; + RtlCopyMemory( Blob.Data[0].Color, pHistogram->Data.P1Data, sizeof(Blob.Data[0].Color) ); + + // Now just copy the RGB channels normally. + Blob.Data[1].Header.ChannelMask = MF_HISTOGRAM_CHANNEL_R; + RtlCopyMemory( Blob.Data[1].Color, pHistogram->Data.P0Data, sizeof(Blob.Data[1].Color) ); + Blob.Data[2].Header.ChannelMask = MF_HISTOGRAM_CHANNEL_G; + RtlCopyMemory( Blob.Data[2].Color, pHistogram->Data.P1Data, sizeof(Blob.Data[2].Color) ); + Blob.Data[3].Header.ChannelMask = MF_HISTOGRAM_CHANNEL_B; + RtlCopyMemory( Blob.Data[3].Color, pHistogram->Data.P2Data, sizeof(Blob.Data[3].Color) ); + + return pMetaDataAttributes->SetBlob( + MF_CAPTURE_METADATA_HISTOGRAM, + (PBYTE) &Blob, + sizeof(Blob) + ); + } + else if( (pHistogram->Data.ChannelMask & MF_HISTOGRAM_YCrCb) == MF_HISTOGRAM_YCrCb ) + { + Histogram<3> Blob( pHistogram->Data.Width, pHistogram->Data.Height ); + + Blob.Header.FourCC = pHistogram->Data.FourCC; + Blob.Header.ChannelMasks = pHistogram->Data.ChannelMask; + + Blob.Data[0].Header.ChannelMask = MF_HISTOGRAM_CHANNEL_Y; + RtlCopyMemory( Blob.Data[0].Color, pHistogram->Data.P0Data, sizeof(Blob.Data[0].Color) ); + Blob.Data[1].Header.ChannelMask = MF_HISTOGRAM_CHANNEL_Cr; + RtlCopyMemory( Blob.Data[1].Color, pHistogram->Data.P1Data, sizeof(Blob.Data[1].Color) ); + Blob.Data[2].Header.ChannelMask = MF_HISTOGRAM_CHANNEL_Cb; + RtlCopyMemory( Blob.Data[2].Color, pHistogram->Data.P2Data, sizeof(Blob.Data[2].Color) ); + + //TODO: + return pMetaDataAttributes->SetBlob( + MF_CAPTURE_METADATA_HISTOGRAM, + (PBYTE) &Blob, + sizeof(Blob) + ); + } + return E_UNEXPECTED; +} + +HRESULT CSocMft0::ParseMetadata_FaceDetection( + _In_ PKSCAMERA_METADATA_ITEMHEADER pItem, + _In_ IMFAttributes *pMetaDataAttributes +) +{ + HRESULT hr = S_OK; + + if (pItem->Size < sizeof(CAMERA_METADATA_FACEHEADER)) + { + return E_UNEXPECTED; + } + + PCAMERA_METADATA_FACEHEADER pFaceHeader = (PCAMERA_METADATA_FACEHEADER)pItem; + + if( pItem->Size < (sizeof(CAMERA_METADATA_FACEHEADER) + (sizeof(METADATA_FACEDATA) * pFaceHeader->Count)) ) + { + return E_UNEXPECTED; + } + PMETADATA_FACEDATA pFaceData = (PMETADATA_FACEDATA) (pFaceHeader+1); + UINT32 cbRectSize = sizeof(FaceRectInfoBlobHeader) + (sizeof(FaceRectInfo) * (pFaceHeader->Count)); + BYTE *pRectBuf = new BYTE [cbRectSize]; + if (pRectBuf == NULL) + { + return E_OUTOFMEMORY; + } + + UINT32 cbCharSize = sizeof(FaceCharacterizationBlobHeader) + (sizeof(FaceCharacterization) * (pFaceHeader->Count)); + BYTE *pCharBuf = new BYTE[cbCharSize]; + if (pCharBuf == NULL) + { + delete [] pRectBuf; + return E_OUTOFMEMORY; + } + + FaceRectInfoBlobHeader *pFaceRectHeader = (FaceRectInfoBlobHeader *)pRectBuf; + pFaceRectHeader->Size = cbRectSize; + pFaceRectHeader->Count = pFaceHeader->Count; + + FaceCharacterizationBlobHeader *pFaceCharHeader = (FaceCharacterizationBlobHeader *)pCharBuf; + pFaceCharHeader->Size = cbCharSize; + pFaceCharHeader->Count = pFaceHeader->Count; + + FaceRectInfo *FaceRegions = (FaceRectInfo *)(pFaceRectHeader + 1); + FaceCharacterization *FaceChars = (FaceCharacterization *) (pFaceCharHeader + 1); + + for (UINT i = 0; i < pFaceHeader->Count; i++) + { + FaceRegions[i].Region = pFaceData[i].Region; + FaceRegions[i].confidenceLevel = pFaceData[i].confidenceLevel; + + FaceChars[i].BlinkScoreLeft = pFaceData[i].BlinkScoreLeft; + FaceChars[i].BlinkScoreRight = pFaceData[i].BlinkScoreRight; + FaceChars[i].FacialExpression = (pFaceData[i].FacialExpression==EXPRESSION_SMILE)?MF_METADATAFACIALEXPRESSION_SMILE:0; + FaceChars[i].FacialExpressionScore = pFaceData[i].FacialExpressionScore; + } + + hr = pMetaDataAttributes->SetBlob(MF_CAPTURE_METADATA_FACEROIS, pRectBuf, cbRectSize); + if( FAILED(hr) ) + { + goto done; + } + + MetadataTimeStamps timestamp; + timestamp.Flags = MF_METADATATIMESTAMPS_DEVICE; + timestamp.Device = pFaceHeader->Timestamp; + + hr = pMetaDataAttributes->SetBlob(MF_CAPTURE_METADATA_FACEROITIMESTAMPS, (const UINT8 *)×tamp, sizeof(MetadataTimeStamps)); + if( FAILED(hr) ) + { + goto done; + } + + // Include characterization data if any of the associated bits were set. + if( pFaceHeader->Flags & KSCAMERA_EXTENDEDPROP_FACEDETECTION_ADVANCED_MASK ) + { + hr = pMetaDataAttributes->SetBlob(MF_CAPTURE_METADATA_FACEROICHARACTERIZATIONS , pCharBuf, cbCharSize); + } + +done: + delete [] pRectBuf; + delete [] pCharBuf; + + return hr; +} + +HRESULT CSocMft0::FillBufferLengthFromMediaType( + _In_ IMFMediaType *pPreviewType, + _Inout_ IMFMediaBuffer *pBuffer +) +{ + if(!pPreviewType || !pBuffer) + { + return E_INVALIDARG; + } + + UINT32 uiWidth = 0, uiHeight = 0, uiImageSize = 0; + GUID guidSubType= {}; + + HRESULT hr = MFGetAttributeSize(pPreviewType, MF_MT_FRAME_SIZE, &uiWidth, &uiHeight); + if (FAILED(hr)) + { + return hr; + } + + hr = pPreviewType->GetGUID(MF_MT_SUBTYPE, &guidSubType); + if (FAILED(hr)) + { + return hr; + } + + hr = MFCalculateImageSize(guidSubType, uiWidth, uiHeight, &uiImageSize); + if (FAILED(hr)) + { + return hr; + } + + return pBuffer->SetCurrentLength(uiImageSize); +} + +HRESULT CSocMft0::GetVideoStabilizationEnabled() +{ + HRESULT hr = S_OK; + ComPtr spKsControl; + KSPROPERTY ksProperty = { 0 }; + KSPROPERTY_CUSTOM_CAMERACONTROL_MFT0_VIDEOSTABILIZATION_S ksData = { 0 }; + ULONG bytesReturned = 0; + + if (m_spSourceTransform != NULL) + { + hr = m_spSourceTransform.As(&spKsControl); + if (SUCCEEDED(hr)) + { + ksProperty.Set = PROPSETID_VIDCAP_CUSTOM_CAMERACONTROL_MFT0; + ksProperty.Id = KSPROPERTY_CUSTOM_CAMERACONTROL_MFT0_VIDEOSTABILIZATION; + ksProperty.Flags = KSPROPERTY_TYPE_GET; + + hr = spKsControl->KsProperty(&ksProperty, sizeof(ksProperty), &ksData, sizeof(ksData), &bytesReturned); + if (SUCCEEDED(hr)) + { + m_bEnableVideoStabilization = ksData.VideoStabilizationEnabled; + } + } + } + else + { + hr = E_INVALIDARG; + } + return hr; +} diff --git a/avscamera/mft0/MFT0Impl.h b/avscamera/mft0/MFT0Impl.h new file mode 100644 index 000000000..8644cf849 --- /dev/null +++ b/avscamera/mft0/MFT0Impl.h @@ -0,0 +1,328 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2013, Microsoft Corporation. + + File: + + Mft0Impl.h + + Abstract: + + The CSocMft0 class definition - an implementation of the iSocMft0 + interface found in Mft0.idl. + + History: + + created 4/26/2013 + +**************************************************************************/ + + +#pragma once +#include "Mft0.h" +#include "SampleHelpers.h" +#include "Mft0clsid.h" + +#include + +// CSocMft0 +#define FaceDetectionDelayMax 2 //frames between emitting facedetection data + +ULONG DllAddRef(); +ULONG DllRelease(); + +using namespace Microsoft::WRL; + +class CSocMft0: + public Microsoft::WRL::RuntimeClass, + ISocMft0, + IMFTransform, + IInspectable> +{ + +public: + + static HRESULT CreateInstance(REFIID iid, void **ppMFT); + + CSocMft0(): + m_bEnableEffects(TRUE), + m_bEnableVideoStabilization(FALSE), + m_uiSourceStreamId(0), + m_uiInternalState(0), + m_uThumbnailScaleFactor(4) + { + InitializeCriticalSection(&m_critSec); + m_stThumbnailFormat = MFVideoFormat_ARGB32; + DllAddRef(); + + } + + HRESULT FinalConstruct(); + +public: + + /*ISocMft0*/ + STDMETHOD(SetState)(UINT32 state); + STDMETHOD(GetState)(UINT32 *pState); + + /*IInspectable*/ + STDMETHOD(GetIids)( + _Out_ ULONG *iidCount, + _Outptr_result_buffer_maybenull_(*iidCount) + IID **iids + ); + + STDMETHOD(GetRuntimeClassName)( + _Outptr_result_maybenull_ HSTRING *pClassName + ); + + STDMETHOD(GetTrustLevel)( + _Out_ TrustLevel *trustLevel + ); + + /*IMFTransform*/ + STDMETHOD(GetStreamLimits)( + _Out_ DWORD *pdwInputMinimum, + _Out_ DWORD *pdwInputMaximum, + _Out_ DWORD *pdwOutputMinimum, + _Out_ DWORD *pdwOutputMaximum + ); + + STDMETHOD(GetStreamCount)( + _Out_ DWORD *pcInputStreams, + _Out_ DWORD *pcOutputStreams + ); + + STDMETHOD(GetStreamIDs)( + DWORD dwInputIDArraySize, + _Out_writes_(dwInputIDArraySize) DWORD *pdwInputIDs, + DWORD dwOutputIDArraySize, + _Out_writes_(dwOutputIDArraySize) DWORD *pdwOutputIDs + ); + + STDMETHOD(GetInputStreamInfo)( + DWORD dwInputStreamID, + _Out_ MFT_INPUT_STREAM_INFO *pStreamInfo + ); + + STDMETHOD(GetOutputStreamInfo)( + DWORD dwOutputStreamID, + _Out_ MFT_OUTPUT_STREAM_INFO *pStreamInfo + ); + + STDMETHOD(GetAttributes)( + _Outptr_result_maybenull_ IMFAttributes **pAttributes + ); + + STDMETHOD(GetInputStreamAttributes)( + DWORD dwInputStreamID, + _Outptr_result_maybenull_ IMFAttributes **pAttributes + ); + + STDMETHOD(GetOutputStreamAttributes)( + DWORD dwOutputStreamID, + _Outptr_result_maybenull_ + IMFAttributes **pAttributes + ); + + STDMETHOD(DeleteInputStream)( + DWORD dwStreamID + ); + + STDMETHOD(AddInputStreams)( + DWORD cStreams, + _In_ DWORD *adwStreamIDs + ); + + STDMETHOD(GetInputAvailableType)( + DWORD dwInputStreamID, + DWORD dwTypeIndex, + _Outptr_result_maybenull_ IMFMediaType **ppType + ); + + STDMETHOD(GetOutputAvailableType)( + DWORD dwOutputStreamID, + DWORD dwTypeIndex, + _Outptr_result_maybenull_ IMFMediaType **ppType + ); + + STDMETHOD(SetInputType)( + DWORD dwInputStreamID, + _In_opt_ IMFMediaType *pType, + DWORD dwFlags); + + STDMETHOD(SetOutputType)( + DWORD dwOutputStreamID, + _In_opt_ IMFMediaType *pType, + DWORD dwFlags); + + STDMETHOD(GetInputCurrentType)( + DWORD dwInputStreamID, + _Outptr_result_maybenull_ IMFMediaType **ppType + ); + + STDMETHOD(GetOutputCurrentType)( + DWORD dwOutputStreamID, + _Outptr_result_maybenull_ IMFMediaType **ppType + ); + + STDMETHOD(GetInputStatus)( + DWORD dwInputStreamID, + _Out_ DWORD *pdwFlags + ); + + STDMETHOD(GetOutputStatus)( + _Out_ DWORD *pdwFlags + ); + + STDMETHOD(SetOutputBounds)( + LONGLONG hnsLowerBound, + LONGLONG hnsUpperBound + ); + + STDMETHOD(ProcessEvent)( + DWORD dwInputStreamID, + _In_opt_ IMFMediaEvent *pEvent + ); + + STDMETHOD(ProcessMessage)( + MFT_MESSAGE_TYPE eMessage, + ULONG_PTR ulParam + ); + + STDMETHOD(ProcessInput)( + DWORD dwInputStreamID, + IMFSample *pSample, + DWORD dwFlags + ); + + STDMETHOD(ProcessOutput)( + DWORD dwFlags, + DWORD cOutputBufferCount, + MFT_OUTPUT_DATA_BUFFER *pOutputSamples, + DWORD *pdwStatus + ); + +protected: + virtual ~CSocMft0() + { + DeleteCriticalSection(&m_critSec); + DllRelease(); + } + + STDMETHOD(GetMediaType)( + _In_ DWORD dwStreamID, + _In_ DWORD dwTypeIndex, + _Outptr_result_maybenull_ IMFMediaType **ppType + ); + + STDMETHOD(IsMediaTypeSupported)( + _In_ UINT uiStreamId, + _In_ IMFMediaType *pIMFMediaType, + _Outptr_result_maybenull_ IMFMediaType **ppIMFMediaTypeFull = NULL + ); + + STDMETHOD(GenerateMFMediaTypeListFromDevice)(); + + _Success_(return == 0) + STDMETHOD(FindMediaIndex)( + _In_ UINT uiStreamId, + _In_ IMFMediaType *pIMFMediaType, + _Out_ UINT *puiMediaIndex + ); + + // HasPendingOutput: Returns TRUE if the MFT is holding an input sample. + BOOL HasPendingOutput() const + { + return m_spSample != NULL; + } + + // IsValidInputStream: Returns TRUE if dwInputStreamID is a valid input stream identifier. + BOOL IsValidInputStream(DWORD dwInputStreamID) const + { + return dwInputStreamID == 0; + } + + // IsValidOutputStream: Returns TRUE if dwOutputStreamID is a valid output stream identifier. + BOOL IsValidOutputStream(DWORD dwOutputStreamID) const + { + return dwOutputStreamID == 0; + } + + HRESULT CreateOutputSample( + _Outptr_result_maybenull_ IMFSample **ppSample + ); + + HRESULT GetPreviewMediaType( + _Outptr_result_maybenull_ IMFMediaType **ppType + ); + + HRESULT ProcessMetadata(); + + HRESULT ParseMetadata_PreviewAggregation( + _In_ PKSCAMERA_METADATA_ITEMHEADER pItem, + _In_ IMFAttributes *pMetaDataAttributes + ); + + HRESULT ParseMetadata_ImageAggregation( + _In_ PKSCAMERA_METADATA_ITEMHEADER pItem, + _In_ IMFAttributes *pMetaDataAttributes + ); + + HRESULT ParseMetadata_Histogram( + _In_ PKSCAMERA_METADATA_ITEMHEADER pItem, + _In_ IMFAttributes *pMetaDataAttributes + ); + + HRESULT ParseMetadata_FaceDetection( + _In_ PKSCAMERA_METADATA_ITEMHEADER pItem, + _In_ IMFAttributes *pMetaDataAttributes + ); + + HRESULT FillBufferLengthFromMediaType( + _In_ IMFMediaType *pPreviewType, + _Inout_ IMFMediaBuffer *pBuffer + ); + + STDMETHOD(OnFlush)(); + + HRESULT GetVideoStabilizationEnabled(); + + HRESULT GetThumbnailResolution(); + + HRESULT OnProcessImage( + _Outptr_result_maybenull_ + IMFSample **ppIMFOutputSample + ); + + CRITICAL_SECTION m_critSec; + + ComPtr m_spSample; // Input sample. + ComPtr m_spInputType; // Input media type. + ComPtr m_spOutputType; // Output media type. + + // Image transform function. (Changes based on the media type.) + ComPtr m_spInputAttributes; + ComPtr m_spGlobalAttributes; + GUID m_stStreamType; + ComPtr m_spSourceTransform; + volatile BOOL m_bEnableEffects; + BOOL m_bEnableVideoStabilization; + UINT m_uiSourceStreamId; + UINT m_uiInternalState; + BYTE m_uThumbnailScaleFactor; + GUID m_stThumbnailFormat; + + std::vector> + m_listOfMediaTypes; + +}; + +inline HRESULT MFT0CreateInstance(REFIID riid, void **ppv) +{ + return CSocMft0::CreateInstance(riid, ppv); +} + +//CoCreateableClass(CSocMft0); \ No newline at end of file diff --git a/avscamera/mft0/MFT0clsid.h b/avscamera/mft0/MFT0clsid.h new file mode 100644 index 000000000..d8aad5f5c --- /dev/null +++ b/avscamera/mft0/MFT0clsid.h @@ -0,0 +1,26 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2015, Microsoft Corporation. + + File: + + Mft0clsid.h + + Abstract: + + Define the Class GUID of the MFT. + + History: + + created 2/19/2015 + +**************************************************************************/ + +#pragma once + +// CLSID of the MFT. +// {1C2CE17A-FAAD-4E73-85E7-167068093F25} +DEFINE_GUID(CLSID_AvsCamMft0, + 0x1c2ce17a, 0xfaad, 0x4e73, 0x85, 0xe7, 0x16, 0x70, 0x68, 0x9, 0x3f, 0x25); diff --git a/avscamera/mft0/SampleHelpers.h b/avscamera/mft0/SampleHelpers.h new file mode 100644 index 000000000..11908f829 --- /dev/null +++ b/avscamera/mft0/SampleHelpers.h @@ -0,0 +1,106 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2013, Microsoft Corporation. + + File: + + SampleHelpers.h + + Abstract: + + Helper classes for the MFT0. + + History: + + created 4/26/2013 + +**************************************************************************/ + +#pragma once + +/***************************************************************************\ +***************************************************************************** +* +* class CAutoLock +* +* Locks a critical section at construction time and unlocks it automatically +* when the object goes out of scope +* +***************************************************************************** +\***************************************************************************/ + +class CAutoLock +{ +private: + // Make copy constructor and assignment operator inaccessible + CAutoLock(const CAutoLock &refAutoLock); + CAutoLock &operator=(const CAutoLock &refAutoLock); + + // CAutoLock should not be created on the heap, so declare + // (but do not implement) private operator new/delete. + static void *operator new(size_t); + static void operator delete(void *); + static void *operator new[](size_t); + static void operator delete[](void *); + +protected: + CRITICAL_SECTION *m_pLock; + +public: + _Acquires_lock_(m_pLock) + explicit CAutoLock(CRITICAL_SECTION *pLock): + m_pLock(pLock) + { + EnterCriticalSection(m_pLock); + } + + _Releases_lock_(m_pLock) + ~CAutoLock() + { + LeaveCriticalSection(m_pLock); + } +}; + +class MediaBufferLock +{ +public: + MediaBufferLock(_In_ IMFMediaBuffer *pBuffer): + m_bLocked(false) + { + m_spBuffer = pBuffer; + } + + HRESULT LockBuffer( + _Outptr_result_bytebuffer_to_(*pcbMaxLength, *pcbCurrentLength) BYTE **ppbBuffer, + _Out_opt_ DWORD *pcbMaxLength, + _Out_opt_ DWORD *pcbCurrentLength) + { + if(!m_spBuffer) + { + return E_INVALIDARG; + } + + HRESULT hr = m_spBuffer->Lock(ppbBuffer, pcbMaxLength, pcbCurrentLength); + if(FAILED(hr)) + { + return hr; + } + m_bLocked = true; + return S_OK; + } + + ~MediaBufferLock() + { + if(m_spBuffer && m_bLocked) + { + //Unlock fails only if we did not lock it first + (void) m_spBuffer->Unlock(); + } + } + +private: + ComPtr m_spBuffer; + bool m_bLocked; +}; diff --git a/avscamera/mft0/dllmain.cpp b/avscamera/mft0/dllmain.cpp new file mode 100644 index 000000000..5eda92dce --- /dev/null +++ b/avscamera/mft0/dllmain.cpp @@ -0,0 +1,191 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + dllmain.cpp + + Abstract: + + DLL and COM initialization. + + History: + + created 5/15/2014 + +**************************************************************************/ +//// 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 + + +#include "stdafx.h" +#include "Mft0.h" +#include "Mft0clsid.h" +#include "Mft0Impl.h" +#include + +using namespace Microsoft::WRL; + +// Module Ref count +volatile ULONG g_cRefModule = 0; + +// Handle to the DLL's module +HMODULE g_hModule = NULL; + +ULONG DllAddRef() +{ + return InterlockedIncrement(&g_cRefModule); +} + +ULONG DllRelease() +{ + return InterlockedDecrement(&g_cRefModule); +} + +class CClassFactory : public IClassFactory +{ +public: + static HRESULT CreateInstance( + REFCLSID clsid, + REFIID riid, + _COM_Outptr_ void **ppv + ) + { + *ppv = NULL; + + HRESULT hr = CLASS_E_CLASSNOTAVAILABLE; + + if(IsEqualGUID(clsid, CLSID_AvsCamMft0)) + { + IClassFactory *pClassFactory = new (std::nothrow) CClassFactory(); + if(!pClassFactory) + { + return E_OUTOFMEMORY; + } + hr = pClassFactory->QueryInterface(riid, ppv); + pClassFactory->Release(); + } + return hr; + } + + //IUnknown methods + IFACEMETHODIMP QueryInterface(REFIID riid, void **ppvObject) + { + HRESULT hr = S_OK; + + if(ppvObject == NULL) + { + return E_POINTER; + } + + if(riid == IID_IUnknown) + { + *ppvObject = (IUnknown*)this; + } + else if(riid == IID_IClassFactory) + { + *ppvObject = (IClassFactory*)this; + } + else + { + *ppvObject = NULL; + return E_NOINTERFACE; + } + + AddRef(); + + return hr; + } + + IFACEMETHODIMP_(ULONG) AddRef() + { + return InterlockedIncrement(&m_cRef); + } + + IFACEMETHODIMP_(ULONG) Release() + { + long cRef = InterlockedDecrement(&m_cRef); + if(cRef == 0) + { + delete this; + } + return cRef; + } + + //IClassFactory Methods + IFACEMETHODIMP CreateInstance(_In_ IUnknown *punkOuter, _In_ REFIID riid, _Outptr_ void **ppv) + { + return punkOuter ? CLASS_E_NOAGGREGATION : MFT0CreateInstance(riid, ppv); + } + + IFACEMETHODIMP LockServer(BOOL fLock) + { + if(fLock) + { + DllAddRef(); + } + else + { + DllRelease(); + } + return S_OK; + } + +private: + CClassFactory() : + m_cRef(1) + { + DllAddRef(); + } + + ~CClassFactory() + { + DllRelease(); + } + + long m_cRef; +}; + + + + +STDAPI_(BOOL) DllMain(_In_opt_ HINSTANCE hinst, DWORD reason, _In_opt_ void *) +{ + if (reason == DLL_PROCESS_ATTACH) + { + g_hModule = (HMODULE)hinst; + DisableThreadLibraryCalls(hinst); + } + return TRUE; +} + +// +// DllCanUnloadNow +// +///////////////////////////////////////////////////////////////////////// +STDAPI DllCanUnloadNow() +{ + return (g_cRefModule == 0) ? S_OK : S_FALSE; +} + +///////////////////////////////////////////////////////////////////////// +// +// DllGetClassObject +// +///////////////////////////////////////////////////////////////////////// +_Check_return_ +STDAPI DllGetClassObject( + _In_ REFCLSID rclsid, + _In_ REFIID riid, + _Outptr_ LPVOID FAR *ppv +) +{ + return CClassFactory::CreateInstance(rclsid, riid, ppv); +} diff --git a/avscamera/mft0/stdafx.h b/avscamera/mft0/stdafx.h new file mode 100644 index 000000000..ef768c0b6 --- /dev/null +++ b/avscamera/mft0/stdafx.h @@ -0,0 +1,32 @@ +//// 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" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + + +using namespace Microsoft::WRL; \ No newline at end of file diff --git a/avscamera/mft0/stdafxsrc.cpp b/avscamera/mft0/stdafxsrc.cpp new file mode 100644 index 000000000..1577c4e3b --- /dev/null +++ b/avscamera/mft0/stdafxsrc.cpp @@ -0,0 +1 @@ +#include "stdafx.h" \ No newline at end of file diff --git a/avscamera/mft0/targetver.h b/avscamera/mft0/targetver.h new file mode 100644 index 000000000..fc349fa5f --- /dev/null +++ b/avscamera/mft0/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/avscamera/sys/AvsCamera.cpp b/avscamera/sys/AvsCamera.cpp new file mode 100644 index 000000000..3742e69df --- /dev/null +++ b/avscamera/sys/AvsCamera.cpp @@ -0,0 +1,108 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + AvsCamera.cpp + + Abstract: + + Sample Camera driver initialization. + + History: + + created 5/15/2014 + +**************************************************************************/ + +#include "Common.h" + +/************************************************************************** + + DESCRIPTOR AND DISPATCH LAYOUT + +**************************************************************************/ + +// +// 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. +// +DEFINE_CAMERA_KSDEVICE_DISPATCH( AvsCameraDispatch, CAvsCameraDevice ); + +// +// 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 +AvsCameraDeviceDescriptor = +{ + &AvsCameraDispatch, + 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, + &AvsCameraDeviceDescriptor + ); + +} + diff --git a/avscamera/sys/AvsCamera.rc b/avscamera/sys/AvsCamera.rc new file mode 100644 index 000000000..25131b0ab --- /dev/null +++ b/avscamera/sys/AvsCamera.rc @@ -0,0 +1,21 @@ +//+------------------------------------------------------------------------- +// +// Microsoft Windows +// +// Copyright (C) Microsoft Corporation, 2015 +// +// File: AvsCam.rc +// +//-------------------------------------------------------------------------- + +#include + +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_UNKNOWN +#define VER_FILEDESCRIPTION_STR "A/V Stream Camera Simulator" +#define VER_INTERNALNAME_STR "AvsCam.sys" +#define VER_ORIGINALFILENAME_STR "AvsCam.sys" + +#include "common.ver" diff --git a/avscamera/sys/AvsCamera.vcxproj b/avscamera/sys/AvsCamera.vcxproj new file mode 100644 index 000000000..8773f7150 --- /dev/null +++ b/avscamera/sys/AvsCamera.vcxproj @@ -0,0 +1,246 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {BB6E820D-6149-4486-AD06-96E8F33DCE8A} + $(MSBuildProjectName) + false + true + Debug + Win32 + {032DA1CE-AC79-4BEF-ADDE-9A060C7D5E47} + + + + 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) + + + + + + + + + + + + + + + + AvsCamera + + + AvsCamera + + + AvsCamera + + + AvsCamera + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ks.lib;$(DDK_LIB_PATH)\ntoskrnl.lib;$(DDK_LIB_PATH)\cng.lib + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\common;$(SDK_INC_PATH)\ks + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\common;$(SDK_INC_PATH)\ks + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\common;$(SDK_INC_PATH)\ks + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ks.lib;$(DDK_LIB_PATH)\ntoskrnl.lib;$(DDK_LIB_PATH)\cng.lib + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\common;$(SDK_INC_PATH)\ks + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\common;$(SDK_INC_PATH)\ks + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\common;$(SDK_INC_PATH)\ks + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ks.lib;$(DDK_LIB_PATH)\ntoskrnl.lib;$(DDK_LIB_PATH)\cng.lib + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\common;$(SDK_INC_PATH)\ks + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\common;$(SDK_INC_PATH)\ks + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\common;$(SDK_INC_PATH)\ks + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ks.lib;$(DDK_LIB_PATH)\ntoskrnl.lib;$(DDK_LIB_PATH)\cng.lib + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\common;$(SDK_INC_PATH)\ks + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\common;$(SDK_INC_PATH)\ks + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\common;$(SDK_INC_PATH)\ks + + + + NTDDI_WINTHRESHOLD + + + NTDDI_WINTHRESHOLD + + + NTDDI_WINTHRESHOLD + + + NTDDI_WINTHRESHOLD + + + + true + Level4 + + + + + %(AdditionalOptions) -merge:PAGECONST=PAGE + + + + + true + Level4 + + + + + %(AdditionalOptions) -merge:PAGECONST=PAGE + + + + + true + Level4 + + + + + %(AdditionalOptions) -merge:PAGECONST=PAGE + + + + + true + Level4 + + + + + %(AdditionalOptions) -merge:PAGECONST=PAGE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/avscamera/sys/AvsCamera.vcxproj.Filters b/avscamera/sys/AvsCamera.vcxproj.Filters new file mode 100644 index 000000000..189d7e9ce --- /dev/null +++ b/avscamera/sys/AvsCamera.vcxproj.Filters @@ -0,0 +1,106 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {435FFB2D-4374-4BBF-97B6-52E43DA1C157} + + + h;hpp;hxx;hm;inl;inc;xsd + {F0B00B7F-1E80-4427-93E0-FA621775CBBF} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {BD39318B-B023-47F9-A201-FAB94C9DBE26} + + + inf;inv;inx;mof;mc; + {59B47F1B-A111-4923-BF9B-C8AF5D2F41CF} + + + + + 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/avscamera/sys/AvsCameraDevice.cpp b/avscamera/sys/AvsCameraDevice.cpp new file mode 100644 index 000000000..ebfb589da --- /dev/null +++ b/avscamera/sys/AvsCameraDevice.cpp @@ -0,0 +1,99 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2015, Microsoft Corporation. + + File: + + AvsCameraDevice.cpp + + Abstract: + + Device class implementation. Derived from CCaptureDevice. + This class overloads our base device class implementation. + + History: + + created 02/24/2015 + +**************************************************************************/ + +#include "Common.h" + +/************************************************************************** + + PAGEABLE CODE + +**************************************************************************/ + +#ifdef ALLOC_PRAGMA +#pragma code_seg("PAGE") +#endif // ALLOC_PRAGMA + +// +// DispatchCreate(): +// +// This is the Add Device dispatch for the capture device. It creates +// the CCaptureDevice and associates it with the device via the bag. +// +NTSTATUS +CAvsCameraDevice:: +DispatchCreate ( + _In_ PKSDEVICE Device +) +{ + PAGED_CODE(); + + DBG_ENTER("()"); + + CAvsCameraDevice *CapDevice = new (NonPagedPoolNx, 'eviD') CAvsCameraDevice (Device); + if( !CapDevice) + { + // + // Return failure if we couldn't create the device. + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + NTSTATUS Status = CapDevice->Prepare(); + if (!NT_SUCCESS (Status)) + { + // Init failure. + delete CapDevice; + } + + DBG_LEAVE("()"); + return Status; +} + +NTSTATUS +CAvsCameraDevice:: +Initialize() +{ + PAGED_CODE(); + + static + CSensorContext Context[] = + { + { L"RFC", &AvsCameraFilterDescriptor, AcpiPldPanelBack }, + { L"FFC", &AvsCameraFilterDescriptorFFC, AcpiPldPanelFront } + }; + + m_FilterDescriptorCount = SIZEOF_ARRAY(Context); + m_Context = Context; + + return CCaptureDevice::Initialize(); +} + +CSensor * +CAvsCameraDevice:: +CreateSensor( + _In_ const KSFILTER_DESCRIPTOR *Descriptors +) +{ + PAGED_CODE(); + + return new (NonPagedPoolNx, 'sneS') CSensorSimulation( this, Descriptors ); +} + diff --git a/avscamera/sys/AvsCameraDevice.h b/avscamera/sys/AvsCameraDevice.h new file mode 100644 index 000000000..ec98e8928 --- /dev/null +++ b/avscamera/sys/AvsCameraDevice.h @@ -0,0 +1,73 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2015, Microsoft Corporation. + + File: + + AvsCameraDevice.h + + Abstract: + + Device class implementation. Derived from CCaptureDevice. + This class overloads our base device class implementation. + + History: + + created 02/24/2015 + +**************************************************************************/ + +#pragma once + +class CAvsCameraDevice : public CCaptureDevice +{ +public: + // + // 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 + ); + +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. + // + CAvsCameraDevice( + _In_ PKSDEVICE Device + ) + : CCaptureDevice( Device ) + {} + + virtual + NTSTATUS + Initialize(); + +protected: + virtual + CSensor * + CreateSensor( + _In_ const KSFILTER_DESCRIPTOR *Descriptors + ); + +}; + +// {B27E3887-AD10-4A4E-BFB8-D6765ADD0E38} +#define STATIC_FrontCamera_Filter \ + 0xb27e3887, 0xad10, 0x4a4e, 0xbf, 0xb8, 0xd6, 0x76, 0x5a, 0xdd, 0xe, 0x38 + +// {4EE16166-F358-4F10-8889-93107806B7A7} +#define STATIC_RearCamera_Filter \ + 0x4ee16166, 0xf358, 0x4f10, 0x88, 0x89, 0x93, 0x10, 0x78, 0x6, 0xb7, 0xa7 + diff --git a/avscamera/sys/AvsCameraFilter.cpp b/avscamera/sys/AvsCameraFilter.cpp new file mode 100644 index 000000000..bee08fbe5 --- /dev/null +++ b/avscamera/sys/AvsCameraFilter.cpp @@ -0,0 +1,587 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2015, Microsoft Corporation. + + File: + + AvsCameraFilter.cpp + + Abstract: + + Filter class implementation. Derived from CCaptureFilter. + This class overrides the default implementation. + + History: + + created 02/24/2015 + +**************************************************************************/ + +#include "Common.h" + +/************************************************************************** + + PAGEABLE CODE + +**************************************************************************/ + +#ifdef ALLOC_PRAGMA +#pragma code_seg("PAGE") +#endif // ALLOC_PRAGMA + +CAvsCameraFilter:: +CAvsCameraFilter( + _In_ PKSFILTER Filter +) + : CCaptureFilter( Filter ) +{ + PAGED_CODE(); +} + +// +// ~CCaptureFilter(): +// +// The capture filter destructor. +// +CAvsCameraFilter:: +~CAvsCameraFilter() +{ + PAGED_CODE(); +} + + +NTSTATUS +CAvsCameraFilter:: +DispatchCreate ( + _In_ PKSFILTER Filter, + _In_ PIRP Irp +) + +/*++ + +Routine Description: + + This is the creation dispatch for the capture filter. It creates + the CCaptureFilter object. + +Arguments: + + Filter - + The AVStream filter being created + + Irp - + The creation Irp + +Return Value: + + Success / failure + +--*/ + +{ + PAGED_CODE(); + + DBG_ENTER("(Filter=0x%p)", Filter); + + NTSTATUS Status = STATUS_SUCCESS; + + CAvsCameraFilter *CapFilter = new (NonPagedPoolNx, 'rtlf') CAvsCameraFilter(Filter); + + if (!CapFilter) + { + // + // Return failure if we couldn't create the filter. + // + Status = STATUS_INSUFFICIENT_RESOURCES; + + } + else + { + // Allocate memory or whatever. + Status = CapFilter->Initialize(); + + if (!NT_SUCCESS (Status)) + { + delete CapFilter; + } + else + { + Filter -> Context = reinterpret_cast (CapFilter); + } + } + + DBG_LEAVE( "()=0x%08X", Status ); + return Status; +} + +// Get KSPROPERTY_CUSTOMCONTROL_DUMMY. +NTSTATUS +CAvsCameraFilter:: +GetCustomDummy( + _Inout_ ULONG *pDummy +) +{ + PAGED_CODE(); + + *pDummy = m_CustomValue; + return STATUS_SUCCESS; +} + +// Set KSPROPERTY_CUSTOMCONTROL_DUMMY. +NTSTATUS +CAvsCameraFilter:: +SetCustomDummy( + _In_ ULONG *pDummy +) +{ + PAGED_CODE(); + + m_CustomValue = *pDummy; + return STATUS_SUCCESS; +} + +/************************************************************************** + + DESCRIPTOR AND DISPATCH LAYOUT + +**************************************************************************/ + +GUID g_PINNAME_VIDEO_CAPTURE = {STATIC_PINNAME_VIDEO_CAPTURE}; +GUID g_PINNAME_VIDEO_PREVIEW = {STATIC_PINNAME_VIDEO_PREVIEW}; +GUID g_PINNAME_IMAGE_CAPTURE = {STATIC_PINNAME_IMAGE}; + +// +// CaptureFilterCategories: +// +// The list of category GUIDs for the capture filter. +// +const +GUID +FilterCategories [CAPTURE_FILTER_CATEGORIES_COUNT] = +{ + STATICGUIDOF (KSCATEGORY_VIDEO_CAMERA), + STATICGUIDOF (KSCATEGORY_VIDEO), + STATICGUIDOF (KSCATEGORY_CAPTURE) +}; + +DEFINE_KSPROPERTY_TABLE(FocusPropertyItems) +{ + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_REGION_OF_INTEREST_PROPERTY_ID, KSPROPERTY_CAMERACONTROL_REGION_OF_INTEREST_S, FocusRect ) +}; + +DEFINE_KSPROPERTY_TABLE(VideoStabPropertyItems) +{ + DEFINE_PROP_ITEM( CCaptureFilter, 0, KSPROPERTY_CAMERACONTROL_VIDEOSTABILIZATION_MODE_S, VideoStabMode ) +}; + +DEFINE_KSPROPERTY_TABLE(FlashPropertyItems) +{ + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_FLASH_PROPERTY_ID, KSPROPERTY_CAMERACONTROL_FLASH_S, Flash ) +}; + +DEFINE_KSPROPERTY_TABLE(PinDependencePropertyItems) +{ + // This control only requires a getter. + DEFINE_PROP_ITEM_NO_SET( CCaptureFilter, KSPROPERTY_CAMERACONTROL_IMAGE_PIN_CAPABILITY_PROPERTY_ID, KSPROPERTY_CAMERACONTROL_IMAGE_PIN_CAPABILITY_S, + PinDependence ) +}; + +DEFINE_KSPROPERTY_TABLE(ExtendedPropertyItems) +{ + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOMODE, CExtendedPhotoMode, PhotoMode ), + DEFINE_PROP_ITEM_NO_SET( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOFRAMERATE, CExtendedProperty, PhotoFrameRate ), + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOMAXFRAMERATE, CExtendedProperty, PhotoMaxFrameRate ), + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOTRIGGERTIME, CExtendedProperty, TriggerTime ), + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_WARMSTART, CExtendedProperty, WarmStart ), + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_MAXVIDFPS_PHOTORES, CExtendedMaxVideoFpsForPhotoRes, MaxVideoFpsForPhotoRes ), + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_SCENEMODE, CExtendedProperty, SceneMode ), + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_TORCHMODE, CExtendedProperty, TorchMode ), + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_FLASHMODE, CExtendedProperty, ExtendedFlash ), + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_OPTIMIZATIONHINT, CExtendedProperty, OptimizationHint ), + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_WHITEBALANCEMODE, CExtendedVidProcSetting, WhiteBalance ), + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_EXPOSUREMODE, CExtendedVidProcSetting, Exposure ), + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_FOCUSMODE, CExtendedVidProcSetting, Focus ), + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_ISO, CExtendedProperty, Iso ), + DEFINE_PROP_ITEM_NO_SET( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_FIELDOFVIEW, CExtendedFieldOfView, FieldOfView ), + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_EVCOMPENSATION, CExtendedEvCompensation, EvCompensation ), + DEFINE_PROP_ITEM_NO_SET( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_CAMERAANGLEOFFSET, CExtendedCameraAngleOffset, CameraAngleOffset ), + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOCONFIRMATION, CExtendedProperty, PhotoConfirmation ), + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_METADATA, CExtendedMetadata, Metadata ), + DEFINE_PROP_ITEM_NO_SET( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_FOCUSSTATE, CExtendedProperty, FocusState ), + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_FOCUSPRIORITY, CExtendedProperty, FocusPriority ), + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_FACEDETECTION, CExtendedVidProcSetting, FaceDetection ), + DEFINE_PROP_ITEM_NO_SET( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_ROI_CONFIGCAPS, CRoiConfig, RoiConfigCaps ), + DEFINE_KSPROPERTY_ITEM( + KSPROPERTY_CAMERACONTROL_EXTENDED_ROI_ISPCONTROL, + CCaptureFilter::GetRoiIspControl, + sizeof(KSPROPERTY), + 0, + CCaptureFilter::SetRoiIspControl, + NULL, 0, NULL, NULL, 0 + ), + DEFINE_PROP_ITEM(CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_ZOOM, CExtendedVidProcSetting, Zoom), + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_VIDEOHDR, CExtendedProperty, VideoHDR ), + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_VFR, CExtendedProperty, VFR ), + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_ISO_ADVANCED, CExtendedVidProcSetting, IsoAdvanced ), + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_VIDEOSTABILIZATION, CExtendedProperty, VideoStabilization), + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_HISTOGRAM, CExtendedProperty, Histogram ), + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_OIS, CExtendedProperty, OpticalImageStabilization ), + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_ADVANCEDPHOTO, CExtendedProperty, AdvancedPhoto ), + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_PROFILE, CExtendedProfile, CameraProfile ) +}; + +// Front-facing cameras often have limited capabilities. One way to express that is to use a restricted automation table such as this one. +DEFINE_KSPROPERTY_TABLE(ExtendedPropertyItemsFFC) +{ + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOMODE, CExtendedPhotoMode, PhotoMode ), + DEFINE_PROP_ITEM_NO_SET( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOFRAMERATE, CExtendedProperty, PhotoFrameRate ), + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOMAXFRAMERATE, CExtendedProperty, PhotoMaxFrameRate ), + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOTRIGGERTIME, CExtendedProperty, TriggerTime ), + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_WARMSTART, CExtendedProperty, WarmStart ), + DEFINE_PROP_ITEM_NO_SET( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_MAXVIDFPS_PHOTORES, CExtendedMaxVideoFpsForPhotoRes, MaxVideoFpsForPhotoRes ), + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_SCENEMODE, CExtendedProperty, SceneMode ), + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_TORCHMODE, CExtendedProperty, TorchMode ), + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_OPTIMIZATIONHINT, CExtendedProperty, OptimizationHint ), + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_WHITEBALANCEMODE, CExtendedVidProcSetting, WhiteBalance ), + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_EXPOSUREMODE, CExtendedVidProcSetting, Exposure ), + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_ISO, CExtendedProperty, Iso ), + DEFINE_PROP_ITEM_NO_SET( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_FIELDOFVIEW, CExtendedFieldOfView, FieldOfView ), + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_EVCOMPENSATION, CExtendedEvCompensation, EvCompensation ), + DEFINE_PROP_ITEM_NO_SET( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_CAMERAANGLEOFFSET, CExtendedCameraAngleOffset, CameraAngleOffset ), + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOCONFIRMATION, CExtendedProperty, PhotoConfirmation ), + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_METADATA, CExtendedMetadata, Metadata ), + DEFINE_PROP_ITEM(CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_ZOOM, CExtendedVidProcSetting, Zoom), + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_FACEDETECTION, CExtendedVidProcSetting, FaceDetection ), + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_ISO_ADVANCED, CExtendedVidProcSetting, IsoAdvanced ), + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_VIDEOSTABILIZATION, CExtendedProperty, VideoStabilization ), + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_ADVANCEDPHOTO, CExtendedProperty, AdvancedPhoto ), + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXTENDED_PROFILE, CExtendedProfile, CameraProfile ) +}; + +//-------------------------------------------------------------- +// Legacy Camera Contorl properties +DEFINE_KSPROPERTY_TABLE(CameraControlPropertyTable) +{ + DEFINE_PROP_ITEM_WITH_VALUES(CCaptureFilter, KSPROPERTY_CAMERACONTROL_EXPOSURE, KSPROPERTY_CAMERACONTROL_S, Exposure, &ExposureValues), + DEFINE_PROP_ITEM_WITH_VALUES(CCaptureFilter, KSPROPERTY_CAMERACONTROL_FOCUS, KSPROPERTY_CAMERACONTROL_S, Focus, &FocusValues), + DEFINE_PROP_ITEM_WITH_VALUES(CCaptureFilter, KSPROPERTY_CAMERACONTROL_ZOOM, KSPROPERTY_CAMERACONTROL_S, Zoom, &ZoomValues), + DEFINE_PROP_ITEM_WITH_VALUES(CCaptureFilter, KSPROPERTY_CAMERACONTROL_ZOOM_RELATIVE, KSPROPERTY_CAMERACONTROL_S, ZoomRelative, &ZoomRelativeValues), + DEFINE_PROP_ITEM_WITH_VALUES(CCaptureFilter, KSPROPERTY_CAMERACONTROL_PAN, KSPROPERTY_CAMERACONTROL_S, Pan, &PanValues), + DEFINE_PROP_ITEM_WITH_VALUES(CCaptureFilter, KSPROPERTY_CAMERACONTROL_ROLL, KSPROPERTY_CAMERACONTROL_S, Roll, &RollValues), + DEFINE_PROP_ITEM_WITH_VALUES(CCaptureFilter, KSPROPERTY_CAMERACONTROL_TILT, KSPROPERTY_CAMERACONTROL_S, Tilt, &TiltValues), + DEFINE_PROP_ITEM_WITH_VALUES(CCaptureFilter, KSPROPERTY_CAMERACONTROL_FOCAL_LENGTH, KSPROPERTY_CAMERACONTROL_FOCAL_LENGTH_S, FocalLength, NULL) +}; + +DEFINE_KSPROPERTY_TABLE(PFSPropertyTable) +{ + DEFINE_KSPROPERTY_ITEM( + KSPROPERTY_CAMERACONTROL_PERFRAMESETTING_CAPABILITY, + CCaptureFilter::GetPFSCaps, // GetSupported or Handler + sizeof(KSPROPERTY), // MinProperty + 0, // MinData + NULL, // SetSupported or Handler + NULL, // Values + 0, // RelationsCount + NULL, // Relations + NULL, // SupportHandler + 0 // SerializedSize + ), + DEFINE_KSPROPERTY_ITEM( + KSPROPERTY_CAMERACONTROL_PERFRAMESETTING_SET, + CCaptureFilter::GetPerFrameSettings, // GetSupported or Handler + sizeof(KSPROPERTY), // MinProperty + 0, // MinData + CCaptureFilter::SetPerFrameSettings, // SetSupported or Handler + NULL, // Values + 0, // RelationsCount + NULL, // Relations + NULL, // SupportHandler + 0 // SerializedSize + ), + DEFINE_KSPROPERTY_ITEM( + KSPROPERTY_CAMERACONTROL_PERFRAMESETTING_CLEAR, + NULL, // GetSupported or Handler + sizeof(KSPROPERTY), // MinProperty + 0, // MinData + CCaptureFilter::ClearPerFrameSettings, // SetSupported or Handler + NULL, // Values + 0, // RelationsCount + NULL, // Relations + NULL, // SupportHandler + 0 // SerializedSize + ) +}; + +DEFINE_KSPROPERTY_TABLE(FilterVidcapPropertyTable) +{ + DEFINE_PROP_ITEM( CCaptureFilter, KSPROPERTY_VIDEOCONTROL_MODE, KSPROPERTY_VIDEOCONTROL_MODE_S, VideoControlMode ) +}; + +DEFINE_KSPROPERTY_TABLE( VideoProcampPropertyTable ) +{ + DEFINE_PROP_ITEM_WITH_VALUES(CCaptureFilter, KSPROPERTY_VIDEOPROCAMP_BACKLIGHT_COMPENSATION, KSPROPERTY_VIDEOPROCAMP_S, BacklightCompensation, + &BacklightCompensationValues), + DEFINE_PROP_ITEM_WITH_VALUES(CCaptureFilter, KSPROPERTY_VIDEOPROCAMP_BRIGHTNESS, KSPROPERTY_VIDEOPROCAMP_S, Brightness, &BrightnessValues), + DEFINE_PROP_ITEM_WITH_VALUES(CCaptureFilter, KSPROPERTY_VIDEOPROCAMP_CONTRAST, KSPROPERTY_VIDEOPROCAMP_S, Contrast, &ContrastValues), + DEFINE_PROP_ITEM_WITH_VALUES(CCaptureFilter, KSPROPERTY_VIDEOPROCAMP_HUE, KSPROPERTY_VIDEOPROCAMP_S, Hue, &HueValues), + DEFINE_PROP_ITEM_WITH_VALUES(CCaptureFilter, KSPROPERTY_VIDEOPROCAMP_WHITEBALANCE, KSPROPERTY_VIDEOPROCAMP_S, WhiteBalance, &WhiteBalanceValues), + DEFINE_PROP_ITEM_WITH_VALUES(CCaptureFilter, KSPROPERTY_VIDEOPROCAMP_POWERLINE_FREQUENCY, KSPROPERTY_VIDEOPROCAMP_S, PowerlineFreq, &PLFValues) +}; + +DEFINE_KSPROPERTY_TABLE( CustomPropertyTable ) +{ + DEFINE_PROP_ITEM(CAvsCameraFilter, KSPROPERTY_CUSTOMCONTROL_DUMMY, ULONG, CustomDummy) +}; + +DEFINE_KSEVENT_TABLE(VidCapRoiEventTable) +{ + DEFINE_STD_EVENT_ITEM(KSPROPERTY_CAMERACONTROL_REGION_OF_INTEREST_PROPERTY_ID) +}; + +DEFINE_KSEVENT_TABLE(ExtendedPropertyEventTable) +{ + DEFINE_STD_EVENT_ITEM(KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOMODE), + DEFINE_STD_EVENT_ITEM(KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOMAXFRAMERATE), + DEFINE_STD_EVENT_ITEM(KSPROPERTY_CAMERACONTROL_EXTENDED_WARMSTART), + DEFINE_STD_EVENT_ITEM(KSPROPERTY_CAMERACONTROL_EXTENDED_SCENEMODE), + DEFINE_STD_EVENT_ITEM(KSPROPERTY_CAMERACONTROL_EXTENDED_WHITEBALANCEMODE), + DEFINE_STD_EVENT_ITEM(KSPROPERTY_CAMERACONTROL_EXTENDED_EXPOSUREMODE), + DEFINE_STD_EVENT_ITEM(KSPROPERTY_CAMERACONTROL_EXTENDED_FOCUSMODE), + DEFINE_STD_EVENT_ITEM(KSPROPERTY_CAMERACONTROL_EXTENDED_ISO), + DEFINE_STD_EVENT_ITEM(KSPROPERTY_CAMERACONTROL_EXTENDED_EVCOMPENSATION), + DEFINE_STD_EVENT_ITEM(KSPROPERTY_CAMERACONTROL_EXTENDED_ROI_ISPCONTROL), + DEFINE_STD_EVENT_ITEM(KSPROPERTY_CAMERACONTROL_EXTENDED_ISO_ADVANCED), + DEFINE_STD_EVENT_ITEM(KSPROPERTY_CAMERACONTROL_EXTENDED_PROFILE) +}; + +DEFINE_KSPROPERTY_SET_TABLE(PropertySets) +{ + DEFINE_STD_PROPERTY_SET( PROPSETID_VIDCAP_CAMERACONTROL_REGION_OF_INTEREST, FocusPropertyItems ), + DEFINE_STD_PROPERTY_SET( PROPSETID_VIDCAP_CAMERACONTROL_FLASH, FlashPropertyItems ), + DEFINE_STD_PROPERTY_SET( PROPSETID_VIDCAP_CAMERACONTROL_VIDEO_STABILIZATION, VideoStabPropertyItems ), + DEFINE_STD_PROPERTY_SET( PROPSETID_VIDCAP_VIDEOCONTROL, FilterVidcapPropertyTable ), + DEFINE_STD_PROPERTY_SET( PROPSETID_VIDCAP_CAMERACONTROL_IMAGE_PIN_CAPABILITY, PinDependencePropertyItems ), + DEFINE_STD_PROPERTY_SET( KSPROPERTYSETID_ExtendedCameraControl, ExtendedPropertyItems ), + DEFINE_STD_PROPERTY_SET( KSPROPERTYSETID_PerFrameSettingControl, PFSPropertyTable ), + DEFINE_STD_PROPERTY_SET( PROPSETID_VIDCAP_CAMERACONTROL, CameraControlPropertyTable ), + DEFINE_STD_PROPERTY_SET( PROPSETID_VIDCAP_VIDEOPROCAMP, VideoProcampPropertyTable ), + DEFINE_STD_PROPERTY_SET( PROPSETID_VIDCAP_CUSTOMCONTROL, CustomPropertyTable ) +}; + +DEFINE_KSEVENT_SET_TABLE(EventSets) +{ + DEFINE_KSEVENT_SET + ( + &KSEVENTSETID_ExtendedCameraControl, + SIZEOF_ARRAY(ExtendedPropertyEventTable), + ExtendedPropertyEventTable + ), + DEFINE_KSEVENT_SET + ( + &EVENTSETID_VIDCAP_CAMERACONTROL_REGION_OF_INTEREST, + SIZEOF_ARRAY(VidCapRoiEventTable), + VidCapRoiEventTable + ) +}; + +DEFINE_KSAUTOMATION_TABLE(AvsCameraFilterAutomationTable) +{ + DEFINE_KSAUTOMATION_PROPERTIES(PropertySets), + DEFINE_KSAUTOMATION_METHODS_NULL, + DEFINE_KSAUTOMATION_EVENTS(EventSets) +}; + +//FFC +DEFINE_KSPROPERTY_SET_TABLE(PropertySetsFFC) +{ + DEFINE_STD_PROPERTY_SET( PROPSETID_VIDCAP_VIDEOCONTROL, FilterVidcapPropertyTable ), + DEFINE_STD_PROPERTY_SET( PROPSETID_VIDCAP_CAMERACONTROL_IMAGE_PIN_CAPABILITY, PinDependencePropertyItems ), + DEFINE_STD_PROPERTY_SET( KSPROPERTYSETID_ExtendedCameraControl, ExtendedPropertyItemsFFC ), + DEFINE_STD_PROPERTY_SET( PROPSETID_VIDCAP_VIDEOPROCAMP, VideoProcampPropertyTable ), + DEFINE_STD_PROPERTY_SET( PROPSETID_VIDCAP_CUSTOMCONTROL, CustomPropertyTable ) +}; + +DEFINE_KSEVENT_SET_TABLE(EventSetsFFC) +{ + DEFINE_KSEVENT_SET + ( + &KSEVENTSETID_ExtendedCameraControl, + SIZEOF_ARRAY(ExtendedPropertyEventTable), + ExtendedPropertyEventTable + ) +}; + +DEFINE_KSAUTOMATION_TABLE(AvsCameraFilterAutomationTableFFC) +{ + DEFINE_KSAUTOMATION_PROPERTIES(PropertySetsFFC), + DEFINE_KSAUTOMATION_METHODS_NULL, + DEFINE_KSAUTOMATION_EVENTS(EventSetsFFC) +}; + +// +// CaptureFilterPinDescriptors: +// +// The list of pin descriptors on the capture filter. +// +const +KSPIN_DESCRIPTOR_EX +PinDescriptors[] = +{ + // + // Video Capture Pin + // + { + &VideoCapturePinDispatch, + NULL, + { + 0, // Interfaces (NULL, 0 == default) + NULL, + 0, // Mediums (NULL, 0 == default) + NULL, + SIZEOF_ARRAY(VideoCapturePinDataRanges),// Range Count + VideoCapturePinDataRanges, // Ranges + KSPIN_DATAFLOW_OUT, // Dataflow + KSPIN_COMMUNICATION_BOTH, // Communication + &PIN_CATEGORY_CAPTURE, // Category + &g_PINNAME_VIDEO_CAPTURE, // Name + 0 // Reserved + }, + KSPIN_FLAG_PROCESS_IN_RUN_STATE_ONLY, + 1, // Instances Possible + 0, // Instances Necessary + &VideoCapturePinAllocatorFraming, // Allocator Framing + reinterpret_cast + (CVideoCapturePin::IntersectHandler) + }, + // + // Video Preview Pin + // + { + &VideoCapturePinDispatch, + NULL, + { + 0, + NULL, + 0, + NULL, + SIZEOF_ARRAY(VideoPreviewPinDataRanges),// Range Count + VideoPreviewPinDataRanges, // Ranges + KSPIN_DATAFLOW_OUT, // Dataflow + KSPIN_COMMUNICATION_BOTH, // Communication + &PIN_CATEGORY_PREVIEW, // Category + &g_PINNAME_VIDEO_PREVIEW, // Name + 0 // Reserved + }, + KSPIN_FLAG_PROCESS_IN_RUN_STATE_ONLY, + 1, // Instances Possible + 0, // Instances Necessary + &VideoCapturePinAllocatorFraming, // Allocator Framing + reinterpret_cast + (CVideoCapturePin::IntersectHandler) + }, + // + // Image Capture Pin + // + { + &ImageCapturePinDispatch, + NULL, + { + 0, // Interfaces (NULL, 0 == default) + NULL, + 0, // Mediums (NULL, 0 == default) + NULL, + SIZEOF_ARRAY(ImageCapturePinDataRanges),// Range Count + ImageCapturePinDataRanges, // Ranges + KSPIN_DATAFLOW_OUT, // Dataflow + KSPIN_COMMUNICATION_BOTH, // Communication + &PINNAME_IMAGE, // Category + &g_PINNAME_IMAGE_CAPTURE, // Name + 0 // Reserved + }, +////////////////////////////////////////////////////////////////////////////////////////////////////// + KSPIN_FLAG_INITIATE_PROCESSING_ON_EVERY_ARRIVAL | +////////////////////////////////////////////////////////////////////////////////////////////////////// + KSPIN_FLAG_PROCESS_IN_RUN_STATE_ONLY, + 1, // Instances Possible + 0, // Instances Necessary + &ImageCapturePinAllocatorFraming, // Allocator Framing + reinterpret_cast + (CImageCapturePin::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 +AvsCameraFilterDispatch = +{ + CAvsCameraFilter::DispatchCreate, // Filter Create + CCaptureFilter::DispatchClose, // Filter Close + NULL, // Filter Process + NULL // Filter Reset +}; + +// {B27E3887-AD10-4A4E-BFB8-D6765ADD0E38} +const +GUID +AvsCam_FrontCamera_Filter = {STATIC_FrontCamera_Filter}; + +// {4EE16166-F358-4F10-8889-93107806B7A7} +const +GUID +AvsCam_RearCamera_Filter = {STATIC_RearCamera_Filter}; + +// +// AvsCameraFilterDescriptor: +// +// 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 +AvsCameraFilterDescriptor = +{ + &AvsCameraFilterDispatch, // Dispatch Table + &AvsCameraFilterAutomationTable, // Automation Table + KSFILTER_DESCRIPTOR_VERSION, // Version + KSFILTER_FLAG_PRIORITIZE_REFERENCEGUID, // Flags + &AvsCam_RearCamera_Filter, // Reference GUID + DEFINE_KSFILTER_PIN_DESCRIPTORS(PinDescriptors), + DEFINE_KSFILTER_CATEGORIES(FilterCategories), + 0, + sizeof (KSNODE_DESCRIPTOR), + NULL, + 0, + NULL, + NULL // Component ID +}; + +const +KSFILTER_DESCRIPTOR +AvsCameraFilterDescriptorFFC = +{ + &AvsCameraFilterDispatch, // Dispatch Table + &AvsCameraFilterAutomationTableFFC, // Automation Table + KSFILTER_DESCRIPTOR_VERSION, // Version + KSFILTER_FLAG_PRIORITIZE_REFERENCEGUID, // Flags + &AvsCam_FrontCamera_Filter, // Reference GUID + DEFINE_KSFILTER_PIN_DESCRIPTORS(PinDescriptors), + DEFINE_KSFILTER_CATEGORIES(FilterCategories), + 0, + sizeof (KSNODE_DESCRIPTOR), + NULL, + 0, + NULL, + NULL // Component ID +}; + diff --git a/avscamera/sys/AvsCameraFilter.h b/avscamera/sys/AvsCameraFilter.h new file mode 100644 index 000000000..ce81cedea --- /dev/null +++ b/avscamera/sys/AvsCameraFilter.h @@ -0,0 +1,57 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2015, Microsoft Corporation. + + File: + + AvsCameraFilter.h + + Abstract: + + Filter class implementation. Derived from CCapturefilter. + This class overloads basic filter behavior for our device. + + History: + + created 02/24/2015 + +**************************************************************************/ + +class CAvsCameraFilter : public CCaptureFilter +{ +public: + // + // Ctor + // + CAvsCameraFilter( + _In_ PKSFILTER Filter + ); + + // + // Dtor + // + virtual + ~CAvsCameraFilter (); + + // + // 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 + ); + + // Example of adding a new, custom property. + DECLARE_PROPERTY_HANDLERS( ULONG, CustomDummy ) + +protected: + DWORD m_CustomValue; +}; diff --git a/avscamera/sys/CameraProfile.h b/avscamera/sys/CameraProfile.h new file mode 100644 index 000000000..1a2aca8ac --- /dev/null +++ b/avscamera/sys/CameraProfile.h @@ -0,0 +1,54 @@ +/************************************************************************** + + AvsCam - An AVStream Simulated Camera Device. + + Copyright (c) 2014, Microsoft Corporation. + + File: + + CameraProfile.h + + Abstract: + + This file contains the helper class CExtendedProfile. + + History: + + created 3/19/2015 + +**************************************************************************/ + +// +// Helper class for CameraProfile +// +class CExtendedProfile : public CExtendedHeader +{ +public: + KSCAMERA_EXTENDEDPROP_PROFILE m_Profile; + +public: + CExtendedProfile( ULONGLONG flags=0, ULONG result=STATUS_SUCCESS ) + : CExtendedProfile( flags, result ) + { + Size = sizeof(*this); + m_Profile.ProfileId = KSCAMERAPROFILE_Legacy; + m_Profile.Index = 0; + m_Profile.Reserved = 0; + } + + CExtendedProfile( KSCAMERA_EXTENDEDPROP_HEADER &hdr ) + : CExtendedHeader(hdr) + { + Size = sizeof(*this); + m_Profile.ProfileId = KSCAMERAPROFILE_Legacy; + m_Profile.Index = 0; + m_Profile.Reserved = 0; + } + + bool isValid() + { + return CExtendedHeader::isValid() && (Size >= sizeof(*this)); + } +}; + + diff --git a/avscamera/sys/Capture.cpp b/avscamera/sys/Capture.cpp new file mode 100644 index 000000000..0f32aac33 --- /dev/null +++ b/avscamera/sys/Capture.cpp @@ -0,0 +1,2075 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2001, Microsoft Corporation. + + File: + + capture.cpp + + Abstract: + + This is the implementation of the CCapturePin class. + + CCapturePin wraps a PKSPIN object and does: + 1. Object construction and cleanup, + 2. State management, + 3. Accepts new frame buffers for filling, + 4. Completes frame buffers delivered by the simulation, + 5. Negotiates for and sets new formats / data ranges, and + 6. Manages allocator framing. + + History: + + created 3/8/2001 + +**************************************************************************/ + +#include "Common.h" +#include +#include "ntintsafe.h" + +/************************************************************************** + + PAGEABLE CODE + +**************************************************************************/ + + +#ifdef ALLOC_PRAGMA +#pragma code_seg("PAGE") +#endif // ALLOC_PRAGMA + +CCapturePin:: +CCapturePin ( + _In_ PKSPIN Pin +) + : m_Pin (Pin) + , m_PinState(PinStopped) + , m_Clock(nullptr) + , m_PendIo(FALSE) + , m_AcquiredResources(FALSE) + , m_VideoInfoHeader(nullptr) + , m_pBitmapInfoHeader(nullptr) + , m_PreviousStreamPointer(nullptr) + , m_PresentationTime(0) + , m_FrameNumber(0) + , m_DroppedFrames(0) + , m_DesiredFrames(2) +/*++ + +Routine Description: + + Construct a new capture pin. + +Arguments: + + Pin - + The AVStream pin object corresponding to the capture pin + +Return Value: + + None + +--*/ +{ + PAGED_CODE(); + + NT_ASSERT(Pin); + + PKSDEVICE Device = KsPinGetDevice (Pin); + + NT_ASSERT(Device); + + // + // Set up our device pointer. This gives us access to "hardware I/O" + // during the capture routines. + // + m_Device = CCaptureDevice::Recast(Device); + + NT_ASSERT( m_Device ); + + m_Sensor = m_Device->GetSensor( KsPinGetParentFilter(Pin ) ); + + NT_ASSERT( m_Sensor ); +} + +CCapturePin:: +~CCapturePin() +{ + PAGED_CODE(); + + SAFE_FREE( m_pBitmapInfoHeader ); + SAFE_FREE( m_VideoInfoHeader ); +} + +/*************************************************/ + +NTSTATUS +CCapturePin:: +ReleaseAllFrames() + +/*++ + +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:: +Initialize() +/*++ + +Routine Description: + + Post construction initialization: + 1. Add us to the Pin's bag, + 2. Capture information about the image/video format, and + 3. Update the allocator's framing to match. + +Arguments: + + None + +Return Value: + + Success / Failure + +--*/ +{ + PAGED_CODE(); + + DBG_ENTER("(Pin=%d)", m_Pin->Id); + + NTSTATUS Status = STATUS_SUCCESS; + + if( !m_Sensor ) + { + // Fail if we couldn't find the sensor. + Status = STATUS_INVALID_PARAMETER; + } + 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 ( + m_Pin->Bag, + this, + reinterpret_cast (Cleanup) + ); + + if( NT_SUCCESS(Status) ) + { + m_Pin->Context = this; + } + } + + // + // 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. + // + if (NT_SUCCESS (Status)) + { + Status = CaptureBitmapInfoHeader(); + } + + if (NT_SUCCESS(Status)) + { + // + // Edit the framing descriptors to match our pin's requirements given the current format. + // + Status = UpdateAllocatorFraming(); + } + + DBG_LEAVE("(Pin=%d)=0x%08X", m_Pin->Id, Status); + return Status; +} + +/*************************************************/ + + +NTSTATUS +CCapturePin:: +CaptureBitmapInfoHeader() +/*++ + +Routine Description: + + Capture information about the image/video format for the pin. + +Arguments: + + None + +Return Value: + + Success / Failure + +--*/ +{ + PAGED_CODE( ); + + const GUID ImageInfoSpecifier ={ STATICGUIDOF( KSDATAFORMAT_SPECIFIER_IMAGE ) }; + const GUID VideoInfoSpecifier ={ STATICGUIDOF( KSDATAFORMAT_SPECIFIER_VIDEOINFO ) }; + + // Free any previous copy of these header. + SAFE_FREE(m_pBitmapInfoHeader); + SAFE_FREE(m_VideoInfoHeader); + + m_pBitmapInfoHeader = reinterpret_cast ( + ExAllocatePoolWithTag ( + NonPagedPoolNx, + sizeof(KS_BITMAPINFOHEADER), + AVSHWS_POOLTAG + ) + ); + if( !m_pBitmapInfoHeader ) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + if( IsEqualGUID( m_Pin->ConnectionFormat->Specifier, ImageInfoSpecifier ) && + m_Pin->ConnectionFormat->FormatSize >= sizeof (KS_DATAFORMAT_IMAGEINFO) ) + { + + PKS_BITMAPINFOHEADER ConnectionHeader = + &((reinterpret_cast + (m_Pin->ConnectionFormat))->ImageInfoHeader); + + // + // Copy the connection format video info header into the newly + // allocated "captured" video info header. + // + *m_pBitmapInfoHeader = *ConnectionHeader; + + m_VideoInfoHeader = reinterpret_cast ( + ExAllocatePoolWithTag ( + NonPagedPoolNx, + sizeof(KS_VIDEOINFOHEADER), + AVSHWS_POOLTAG + ) + ); + if( !m_VideoInfoHeader ) + { + SAFE_FREE( m_pBitmapInfoHeader ); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // Copy the connection format video info header into the newly + // allocated "captured" video info header. + // + m_VideoInfoHeader->bmiHeader = *ConnectionHeader; + + // If we don't have a known bit-depth (compressed formats), assume the worse-case. + if( m_pBitmapInfoHeader->biBitCount==0 ) + { + m_pBitmapInfoHeader->biBitCount=32; + } + + // Estimate the image size. The derived pin can chose to override this value. + m_VideoInfoHeader->bmiHeader.biSizeImage = + (m_pBitmapInfoHeader->biWidth*m_pBitmapInfoHeader->biHeight*m_pBitmapInfoHeader->biBitCount)/8; + + // Estimate a frame rate. The derived pin can chose to override this value. + m_VideoInfoHeader->AvgTimePerFrame = ONESECOND/30; + } + else + if( IsEqualGUID( m_Pin->ConnectionFormat->Specifier, VideoInfoSpecifier ) && + m_Pin->ConnectionFormat->FormatSize >= sizeof (KS_DATAFORMAT_VIDEOINFOHEADER) ) + { + PKS_VIDEOINFOHEADER ConnectionHeader = + &((reinterpret_cast + (m_Pin->ConnectionFormat))-> + VideoInfoHeader); + + m_VideoInfoHeader = reinterpret_cast ( + ExAllocatePoolWithTag ( + NonPagedPoolNx, + KS_SIZE_VIDEOHEADER (ConnectionHeader), + AVSHWS_POOLTAG + ) + ); + if( !m_VideoInfoHeader ) + { + SAFE_FREE( m_pBitmapInfoHeader ); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // Copy the connection format video info header into the newly + // allocated "captured" video info header. + // + RtlCopyMemory ( + m_VideoInfoHeader, + ConnectionHeader, + KS_SIZE_VIDEOHEADER (ConnectionHeader) + ); + + // + // Copy the connection format video info header into the newly + // allocated "captured" video info header. + // + *m_pBitmapInfoHeader = m_VideoInfoHeader->bmiHeader; + } + + DBG_TRACE( "+++ AvgTimePerFrame = %lld +++", m_VideoInfoHeader->AvgTimePerFrame ); + + 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; + + DBG_ENTER( "( Pin=%d, ToState=%s, FromState=%s )\n", + m_Pin->Id, KSStateToStateName(ToState), KSStateToStateName(FromState) ) ; + + switch (ToState) + { + + case KSSTATE_STOP: + + // + // First, stop the hardware if we actually did anything to it. + // + if (m_PinState != PinStopped) + { + Status = m_Sensor->Stop(m_Pin); + NT_ASSERT (NT_SUCCESS (Status)); + + m_PinState = PinStopped; + } + + // + // 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 = ReleaseAllFrames (); + + // + // 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_Sensor->ReleaseHardwareResources (m_Pin); + + 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. + // + // TODO: Move this to a derived CCapturePin class - one + // specifically for testing, not as a sample. + if(GetAcquireFailureKey()) + { + Status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + if (FromState == KSSTATE_STOP) + { + Status = + m_Sensor-> + AcquireHardwareResources ( + m_Pin, + this, + m_VideoInfoHeader, + &m_HardwareSimulation + ); + + 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; + } + + } + + // + // Standard transport pins will always receive transitions in + // +/- 1 manner. This means we'll always see a PAUSE->ACQUIRE + // transition before stopping the pin. + // + + 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_Sensor->Pause (m_Pin, TRUE); + + if (NT_SUCCESS (Status)) + { + m_PinState = PinPaused; + } + } + 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 (FromState == KSSTATE_PAUSE && m_PinState == PinPaused) + { + Status = m_Sensor->Pause (m_Pin, FALSE); + } + else + { + Status = m_Sensor->Start (m_Pin); + } + + if (NT_SUCCESS (Status)) + { + m_PinState = PinRunning; + } + break; + + } + + DBG_LEAVE( "( Pin=%d, ToState=%s, FromState=%s ) = 0x%08X\n", + m_Pin->Id, KSStateToStateName(ToState), KSStateToStateName(FromState), Status ) ; + + return Status; + +} + +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( ); + + DBG_ENTER( "( Pin=%d )", m_Pin->Id ); + + NTSTATUS Status = STATUS_SUCCESS; + PKSSTREAM_POINTER Leading; + + 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); + } + + // + // 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_Sensor->ProgramScatterGatherMappings( + m_Pin, + &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. + // + if( !NT_SUCCESS( Status ) || Status == STATUS_PENDING ) + { + m_PendIo = TRUE; + } + + DBG_LEAVE( "( Pin=%d )=0x%08X", m_Pin->Id, Status ); + + return Status; +} + +// +// Emit metadata here for video or preview pin. +// +// This function gives us one last chance to tack metadata onto the sample. +// +void +CCapturePin:: +EmitMetadata( + _Inout_ PKSSTREAM_HEADER pStreamHeader +) +{ + PAGED_CODE(); + + NT_ASSERT(pStreamHeader); +} + + +NTSTATUS +CCapturePin:: +CompleteMapping( + _In_ PKSSTREAM_POINTER Clone + ) + +/*++ + +Routine Description: + + Called to notify the pin that a given number of scatter / gather + mappings have completed. Let the buffers go if possible. + +Arguments: + + Clone - + The stream pointer for the frame to complete. + If Clone is null, use the head of the queue. + +Return Value: + + Success / Failure + +--*/ + +{ + PAGED_CODE(); + + DBG_ENTER("(Clone=%p)", Clone); + + NTSTATUS Status = STATUS_SUCCESS; + + if( !Clone ) + { + Clone = KsPinGetFirstCloneStreamPointer(m_Pin); + } + + // + // 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( Clone ) + { + if( Clone->StreamHeader->DataUsed >= Clone->OffsetOut.Remaining ) + { + Clone->StreamHeader->Duration = + m_VideoInfoHeader->AvgTimePerFrame; + + Clone->StreamHeader->OptionsFlags |= + KSSTREAM_HEADER_OPTIONSF_DURATIONVALID; + + // + // Increment the frame number. This is the total count of frames which + // have attempted capture. + // + m_FrameNumber++; + DBG_TRACE( "m_FrameNumber=%lld", 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; + } + + 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. + // + Status = + KsStreamPointerAdvanceOffsets( + Clone, + 0, + Clone->StreamHeader->DataUsed, + FALSE + ); + } + } + else + { + // We had nothing to process. + Status = STATUS_UNSUCCESSFUL; + } + + // + // 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) + { + KsPinAttemptProcessing (m_Pin, TRUE); + m_PendIo = FALSE; + } + + DBG_LEAVE("(Clone=%p)=0x%08X", Clone, Status); + return Status; +} + + +bool +CCapturePin:: +GetAcquireFailureKey() +{ + NTSTATUS ntstatus; + bool acquireFailure = false; + UNICODE_STRING RegistryKeyName; + OBJECT_ATTRIBUTES ObjectAttributes; + HANDLE handleRegKey = NULL; + PKEY_VALUE_FULL_INFORMATION pKeyInfo = NULL; + ULONG ulKeyInfoSizeNeeded = 0; + UNICODE_STRING ValueName = {0}; + + + PAGED_CODE(); + + // Get the Registry key + RtlInitUnicodeString(&RegistryKeyName, L"\\Registry\\Machine\\Software\\Microsoft\\wtt\\MachineConfig"); + InitializeObjectAttributes(&ObjectAttributes, + &RegistryKeyName, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + NULL, // handle + NULL); + + ntstatus = ZwOpenKey(&handleRegKey, KEY_READ, &ObjectAttributes); + // If we can't open the key, we're done. Worst case, we cannot stream. + if(NT_SUCCESS(ntstatus)) + { + // Get the location from the registry key + RtlInitUnicodeString(&ValueName, L"AcquireFailure"); + + // Figure out how big keyInfo needs to be + ntstatus = ZwQueryValueKey(handleRegKey, + &ValueName, + KeyValueFullInformation, + pKeyInfo, + 0, + &ulKeyInfoSizeNeeded ); + + // We expect one of these errors + if( (ntstatus == STATUS_BUFFER_TOO_SMALL) || (ntstatus == STATUS_BUFFER_OVERFLOW) ) + { + // Allocate the memory needed for the key + pKeyInfo = (PKEY_VALUE_FULL_INFORMATION) new (NonPagedPoolNx) BYTE[ulKeyInfoSizeNeeded]; + RtlZeroMemory(pKeyInfo, ulKeyInfoSizeNeeded); + + // Now get the actual key data + ntstatus = ZwQueryValueKey( handleRegKey, + &ValueName, + KeyValueFullInformation, + pKeyInfo, + ulKeyInfoSizeNeeded, + &ulKeyInfoSizeNeeded ); + + if(ntstatus == STATUS_SUCCESS) + { + acquireFailure = true; + } + + delete[] pKeyInfo; + } + } + + // All done with the registry + if (NULL != handleRegKey) + { + ZwClose(handleRegKey); + } + + return acquireFailure; +} + + +NTSTATUS +CCapturePin:: +DispatchClose( + _In_ PKSPIN Pin, + _In_ PIRP Irp +) +/*++ + +Routine Description: + + Static thunking function to forward Close + +Arguments: + + Pin - + Our pin + Irp - + The IRP the request came in on. + +Return Value: + + Success / Failure + +--*/ +{ + PAGED_CODE( ); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + DBG_ENTER("()"); + + NT_ASSERT( Pin ); + NT_ASSERT( Pin->Context ); + if( Pin && Pin->Context ) + { + CCapturePin *pPin = reinterpret_cast (Pin->Context); + Status = pPin->Close( Irp ); + } + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +NTSTATUS +CCapturePin:: +DispatchSetState( + _In_ PKSPIN Pin, + _In_ KSSTATE ToState, + _In_ KSSTATE FromState +) +/*++ + +Routine Description: + + Static thunking function to forward SetState + +Arguments: + + Pin - + Our pin + Irp - + The IRP the request came in on. + +Return Value: + + Success / Failure + +--*/ +{ + PAGED_CODE( ); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + DBG_ENTER("()"); + + NT_ASSERT( Pin ); + NT_ASSERT( Pin->Context ); + if( Pin && Pin->Context ) + { + CCapturePin *pPin = reinterpret_cast (Pin->Context); + Status = pPin->SetState( ToState, FromState ); + } + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +NTSTATUS +CCapturePin:: +DispatchSetFormat( + _In_ PKSPIN Pin, + _In_opt_ PKSDATAFORMAT OldFormat, + _In_opt_ PKSMULTIPLE_ITEM OldAttributeList, + _In_ const KSDATARANGE *DataRange, + _In_opt_ const KSATTRIBUTE_LIST *AttributeRange +) + +/*++ + +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( ); + + DBG_ENTER( "(Pin->Id=%d)", Pin->Id ); + + NTSTATUS Status = STATUS_NO_MATCH; + const GUID ImageInfoSpecifier = { STATICGUIDOF( KSDATAFORMAT_SPECIFIER_IMAGE ) }; + const GUID VideoInfoSpecifier = { STATICGUIDOF( KSDATAFORMAT_SPECIFIER_VIDEOINFO ) }; + + // + // 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. + // + CCapturePin *CapPin = reinterpret_cast (Pin->Context); + + if( IsEqualGUID( Pin->ConnectionFormat->Specifier, ImageInfoSpecifier ) && + Pin->ConnectionFormat->FormatSize >= sizeof (KS_DATAFORMAT_IMAGEINFO) ) + { + PKS_DATAFORMAT_IMAGEINFO ConnectionFormat = reinterpret_cast (Pin->ConnectionFormat); + + // + // DataRange comes out of OUR data range list. I know the range + // is valid as such. + // + const KS_DATARANGE_IMAGE *VIRange = reinterpret_cast (DataRange); + + // + // Check that bmiHeader.biSize is valid since we use it later. + // + ULONG ImageHeaderSize = ConnectionFormat->ImageInfoHeader.biSize; + ULONG DataFormatSize = sizeof (KSDATAFORMAT) +sizeof(KS_BITMAPINFOHEADER); + + if( ImageHeaderSize < ConnectionFormat->ImageInfoHeader.biSize || + DataFormatSize < ImageHeaderSize || + DataFormatSize > ConnectionFormat->DataFormat.FormatSize ) + { + Status = STATUS_INVALID_PARAMETER; + } + + // + // Check that the format is a match for the selected range. + // + else if( (ConnectionFormat->ImageInfoHeader.biWidth != VIRange->ImageInfoHeader.biWidth) || + (ConnectionFormat->ImageInfoHeader.biHeight != VIRange->ImageInfoHeader.biHeight) || + (ConnectionFormat->ImageInfoHeader.biCompression != VIRange->ImageInfoHeader.biCompression) ) + { + Status = STATUS_NO_MATCH; + } + else + { + + ULONG ImageSize; + + if( !MultiplyCheckOverflow( (ULONG) ConnectionFormat->ImageInfoHeader.biWidth, (ULONG) abs( ConnectionFormat->ImageInfoHeader.biHeight ), &ImageSize ) ) + { + Status = STATUS_INVALID_PARAMETER; + } + + else if( !MultiplyCheckOverflow( ImageSize, (ULONG) (ConnectionFormat->ImageInfoHeader.biBitCount/8), &ImageSize ) ) + { + Status = STATUS_INVALID_PARAMETER; + } + + // + // Valid for the formats we use. Otherwise, this would be + // checked later. + // + else if( ConnectionFormat->ImageInfoHeader.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 && CapPin ) + { + // + // 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 ) + { + Status = CapPin->CaptureBitmapInfoHeader(); + } + else + { + // + // Because we don't accept dynamic format changes, we + // should never get here. Just being over-protective. + // + Status = STATUS_INVALID_DEVICE_STATE; + } + } + } + } + } + + else 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 && CapPin ) + { + // + // 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->CaptureBitmapInfoHeader( ) ) + { + 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; + } + } + } + } + } + + DBG_LEAVE( "(Pin->Id=%d)=0x%08X", Pin->Id, Status ); + 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_opt_ 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 ImageInfoSpecifier = { STATICGUIDOF( KSDATAFORMAT_SPECIFIER_IMAGE ) }; + const GUID VideoInfoSpecifier = { STATICGUIDOF( KSDATAFORMAT_SPECIFIER_VIDEOINFO ) }; + ULONG DataFormatSize; + + NT_ASSERT( Filter ); + NT_ASSERT( Irp ); + NT_ASSERT( PinInstance ); + NT_ASSERT( CallerDataRange ); + NT_ASSERT( DescriptorDataRange ); + NT_ASSERT( DataSize ); + + // + // Specifier FORMAT_VideoInfo for VIDEOINFOHEADER + // + if( IsEqualGUID( CallerDataRange->Specifier, ImageInfoSpecifier ) && + CallerDataRange->FormatSize >= sizeof (KS_DATARANGE_IMAGE) ) + { + + PKS_DATARANGE_IMAGE callerDataRange = + reinterpret_cast (CallerDataRange); + + PKS_DATARANGE_IMAGE descriptorDataRange = + reinterpret_cast (DescriptorDataRange); + + // + // Check that the other fields match + // + if( (RtlCompareMemory( &callerDataRange->ConfigCaps, &descriptorDataRange->ConfigCaps, sizeof (KS_VIDEO_STREAM_CONFIG_CAPS) ) + != sizeof (KS_VIDEO_STREAM_CONFIG_CAPS)) ) + { + return STATUS_NO_MATCH; + } + + { + ULONG ImageHeaderSize = ((PKS_DATARANGE_IMAGE) callerDataRange)->ImageInfoHeader.biSize; + ULONG DataRangeSize = FIELD_OFFSET( KS_DATARANGE_IMAGE, ImageInfoHeader ) + ImageHeaderSize; + + if( ImageHeaderSize < callerDataRange->ImageInfoHeader.biSize || + DataRangeSize < ImageHeaderSize || + DataRangeSize > callerDataRange->DataRange.FormatSize ) + { + return STATUS_INVALID_PARAMETER; + } + } + + DataFormatSize = sizeof (KSDATAFORMAT) +sizeof(KS_BITMAPINFOHEADER); + // + // 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; + + PKS_DATAFORMAT_IMAGEINFO pFormatImageInfoHeader = PKS_DATAFORMAT_IMAGEINFO( 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( pFormatImageInfoHeader, DescriptorDataRange, sizeof (KSDATAFORMAT) ); + pFormatImageInfoHeader->DataFormat.FormatSize = DataFormatSize; + + // + // Copy over the callers requested KS_BITMAPINFOHEADER + // + RtlCopyMemory( &pFormatImageInfoHeader->ImageInfoHeader, &callerDataRange->ImageInfoHeader, sizeof(KS_BITMAPINFOHEADER) ); + return STATUS_SUCCESS; + } + else 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; +} + +NTSTATUS +CCapturePin:: +DispatchProcess( + _In_ PKSPIN Pin +) +/*++ + +Routine Description: + + Static thunking function to forward Process + +Arguments: + + Pin - + Our pin + +Return Value: + + Success / Failure + +--*/ +{ + PAGED_CODE( ); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + DBG_ENTER("()"); + + NT_ASSERT( Pin ); + NT_ASSERT( Pin->Context ); + if( Pin && Pin->Context ) + { + CCapturePin *pPin = reinterpret_cast (Pin->Context); + Status = pPin->Process(); + } + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +void +CCapturePin:: +DispatchReset( + _In_ PKSPIN Pin +) +/*++ + +Routine Description: + + Static thunking function to forward Reset + +Arguments: + + Pin - + Our pin + +Return Value: + + Success / Failure + +--*/ +{ + PAGED_CODE( ); + + DBG_ENTER("()"); + + NT_ASSERT( Pin ); + NT_ASSERT( Pin->Context ); + if( Pin && Pin->Context ) + { + CCapturePin *pPin = reinterpret_cast (Pin->Context); + pPin->Reset( ); + } + DBG_LEAVE("()"); +} + +NTSTATUS +CCapturePin:: +DispatchConnect( + _In_ PKSPIN Pin +) +/*++ + +Routine Description: + + Static thunking function to forward Reset + +Arguments: + + Pin - + Our pin + +Return Value: + + Success / Failure + +--*/ +{ + PAGED_CODE( ); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + DBG_ENTER("()"); + + NT_ASSERT( Pin ); + NT_ASSERT( Pin->Context ); + if( Pin && Pin->Context ) + { + CCapturePin *pPin = reinterpret_cast (Pin->Context); + Status = pPin->Connect( ); + } + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +void +CCapturePin:: +DispatchDisconnect( + _In_ PKSPIN Pin +) +/*++ + +Routine Description: + + Static thunking function to forward Disconnect + +Arguments: + + Pin - + Our pin + +Return Value: + + Success / Failure + +--*/ +{ + PAGED_CODE( ); + + DBG_ENTER("()"); + + NT_ASSERT( Pin ); + NT_ASSERT( Pin->Context ); + if( Pin && Pin->Context ) + { + CCapturePin *pPin = reinterpret_cast (Pin->Context); + pPin->Disconnect( ); + } + DBG_LEAVE("()"); +} + + +NTSTATUS +CCapturePin:: +Close( + _In_ PIRP Irp +) +/*++ + +Routine Description: + + Close handler. + + Replace or overload if you need special handling. + +Arguments: + + Irp - + The IRP the request came in on. + +Return Value: + + Success / Failure + +--*/ +{ + PAGED_CODE( ); + + // Some reasonable default behavior. + return STATUS_SUCCESS; +} + +NTSTATUS +CCapturePin:: +SetFormat( + _In_opt_ PKSDATAFORMAT OldFormat, + _In_opt_ PKSMULTIPLE_ITEM OldAttributeList, + _In_ const KSDATARANGE *DataRange, + _In_opt_ const KSATTRIBUTE_LIST *AttributeRange +) +/*++ + +Routine Description: + + SetFormat handler. + + Replace or overload if you need special handling. + +Arguments: + + 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 + +--*/ +{ + PAGED_CODE( ); + + // Some reasonable default behavior here. + return STATUS_SUCCESS; +} + +void +CCapturePin:: +Reset() +/*++ + +Routine Description: + + Reset handler. + + Replace or overload if you need special handling. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + PAGED_CODE( ); + + // Some reasonable default behavior here. + m_Sensor->Reset(m_Pin); +} + +NTSTATUS +CCapturePin:: +Connect() +/*++ + +Routine Description: + + Connect handler. + + Replace or overload if you need special handling. + +Arguments: + + None. + +Return Value: + + Success / Failure + +--*/ +{ + PAGED_CODE( ); + + // Some reasonable default behavior here. + return STATUS_SUCCESS; +} + +void +CCapturePin:: +Disconnect() +/*++ + +Routine Description: + + Disconnect handler. + + Replace or overload if you need special handling. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + PAGED_CODE( ); + + // Some reasonable default behavior here. +} + +// Update the Pin's Allocator Framing +NTSTATUS +CCapturePin:: +UpdateAllocatorFraming() +{ + PAGED_CODE(); + + // Acquire the lock and update the Pin's allocator framing. + NTSTATUS Status = + KsEdit( m_Pin, &m_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( m_Pin, &m_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 ( + m_Pin->Descriptor->AllocatorFraming + ); + + Framing->FramingItem[0].Frames = m_DesiredFrames; + Framing->FramingItem[0].PhysicalRange.MinFrameSize = m_VideoInfoHeader->bmiHeader.biSize; + Framing->FramingItem[0].PhysicalRange.MaxFrameSize = m_VideoInfoHeader->bmiHeader.biSize; + Framing->FramingItem [0].FramingRange.Range.MinFrameSize = m_VideoInfoHeader->bmiHeader.biSize; + Framing->FramingItem [0].FramingRange.Range.MaxFrameSize = m_VideoInfoHeader->bmiHeader.biSize; + + Framing->FramingItem [0].PhysicalRange.Stepping = 0; + Framing->FramingItem [0].FramingRange.Range.Stepping = 0; + + DBG_TRACE( "Advertising Frame requirement: %d", m_DesiredFrames ); + DBG_TRACE("Image size estimate of: %d bytes", m_VideoInfoHeader->bmiHeader.biSize ); + } + } + + DBG_LEAVE("()=0x%08X",Status); + return Status; +} + diff --git a/avscamera/sys/Capture.h b/avscamera/sys/Capture.h new file mode 100644 index 000000000..5056c0d49 --- /dev/null +++ b/avscamera/sys/Capture.h @@ -0,0 +1,305 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2001, Microsoft Corporation. + + File: + + capture.h + + Abstract: + + This file contains header for the base capture pin class for a filter. + The camera 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 + +**************************************************************************/ + +#pragma once +#include + +#define DMAX_X 640 +#define DMAX_Y 480 +#define D_X 320 +#define D_Y 240 + +#define DMAX_X_OS 728 +#define DMAX_Y_OS 576 +#define D_X_OS 384 +#define D_Y_OS 240 + +/////////////////////////////////////////////////////////////////////////////// +// GUID for primary and FFC cameras +/////////////////////////////////////////////////////////////////////////////// +// {9D9A0F5E-CDE9-49f5-B464-BF13429DDEB5} +DEFINE_GUID(PRIMARY_CAMERA_GUID, + 0x9d9a0f5e, 0xcde9, 0x49f5, 0xb4, 0x64, 0xbf, 0x13, 0x42, 0x9d, 0xde, 0xb5); + +// {75D53C4E-963E-4415-AA88-89107F08D1EE} +DEFINE_GUID(FRONT_FACING_CAMERA_GUID, + 0x75d53c4e, 0x963e, 0x4415, 0xaa, 0x88, 0x89, 0x10, 0x7f, 0x8, 0xd1, 0xee); + +/* +#define Mpx12_X 4160 +#define Mpx12_Y 3120 +#define Mpx5_X 2592 +#define Mpx5_Y 1944 +#define Mpx8_X 3264 +#define Mpx8_Y 2448 +*/ +// +// 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 ICapturePin, + public CNonCopyable +{ +protected: + PKSPIN m_Pin; // The AVStream pin we're associated with. + CCaptureDevice *m_Device; // Pointer to the internal device object for our capture device. + CSensor *m_Sensor; // The h/w sensor simulation for the filter. + CHardwareSimulation *m_HardwareSimulation; // The h/w isp simulation for this pin, if the pin has been through KSSTATE_ACQUIRE. + PIN_STATE m_PinState; // The state we've put the hardware into. + PIKSREFERENCECLOCK m_Clock; // The clock we've been assigned. + BOOLEAN m_PendIo; // An indication of whether or not we pended I/O for some reason. + BOOLEAN m_AcquiredResources; // An indication of whether or not this pin has acquired the necessary hardware resources to operate. + PKS_VIDEOINFOHEADER m_VideoInfoHeader; + PKS_BITMAPINFOHEADER m_pBitmapInfoHeader; // Optional info for image formats. + + // + // 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; + LONGLONG m_PresentationTime; + LONGLONG m_FrameNumber; + LONGLONG m_DroppedFrames; + + ULONG m_DesiredFrames; + + // + // ReleaseAllFrames(): + // + // Clean up any references we hold on frames in the queue. This is called + // when we abruptly stop the fake hardware. + // + NTSTATUS + ReleaseAllFrames (); + + static + void + Cleanup ( + _In_ CCapturePin *Pin) + { + delete Pin; + } + + // + // Emit metadata here for video or preview pin. + // + virtual + void + EmitMetadata( + _Inout_ PKSSTREAM_HEADER pStreamHeader + ); + + // Helper function for finding our filter object. + CCaptureFilter * + GetFilter() + { + return reinterpret_cast + (KsPinGetParentFilter(m_Pin)->Context); + } + + virtual + NTSTATUS + Close( + _In_ PIRP Irp + ); + + // Intended to give the pin an opportunity to do something extra with the selected format. + virtual + NTSTATUS + SetFormat( + _In_opt_ PKSDATAFORMAT OldFormat, + _In_opt_ PKSMULTIPLE_ITEM OldAttributeList, + _In_ const KSDATARANGE *DataRange, + _In_opt_ const KSATTRIBUTE_LIST *AttributeRange + ); + + virtual + NTSTATUS + Process( ); + + virtual + NTSTATUS + SetState( + _In_ KSSTATE ToState, + _In_ KSSTATE FromState + ); + + virtual + void + Reset(); + + virtual + NTSTATUS + Connect(); // Pin Connect + + virtual + void + Disconnect(); // Pin Disconnect + + virtual + NTSTATUS + CaptureBitmapInfoHeader( ); + + virtual + NTSTATUS + Initialize(); + + void + SetDesiredFrames( ULONG n ) + { + m_DesiredFrames = n; + } + +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. + // + virtual + ~CCapturePin(); + + // + // ICapturePin::CompleteMapping() + // + // This is the capture pin notification mechanism for mapping completion. + // When the device detects that frames have been completed by the fake + // hardware, it informs the capture sink through this method. + // + virtual + NTSTATUS + CompleteMapping( + _In_ PKSSTREAM_POINTER Clone=nullptr + ); + + // Update the Pin's Allocator Framing + virtual + NTSTATUS + UpdateAllocatorFraming(); + + // TEST: Move this to a derived CCapturePin class - one + // specifically for testing, not as a sample. + static + bool + GetAcquireFailureKey(); + + PIN_STATE GetState() const + { + return m_PinState; + } + + static + NTSTATUS + DispatchClose( + _In_ PKSPIN Pin, + _In_ PIRP Irp + ); + static NTSTATUS DispatchSetState( + _In_ PKSPIN Pin, + _In_ KSSTATE ToState, + _In_ KSSTATE FromState + ); + + static NTSTATUS DispatchSetFormat( + _In_ PKSPIN Pin, + _In_opt_ PKSDATAFORMAT OldFormat, + _In_opt_ PKSMULTIPLE_ITEM OldAttributeList, + _In_ const KSDATARANGE *DataRange, + _In_opt_ const KSATTRIBUTE_LIST *AttributeRange + ); + + static NTSTATUS DispatchProcess( _In_ PKSPIN Pin ); + + static void DispatchReset( _In_ PKSPIN ); + static NTSTATUS DispatchConnect( _In_ PKSPIN Pin ); // Pin Connect + static void DispatchDisconnect( _In_ PKSPIN Pin ); // Pin Disconnect + + static + NTSTATUS + IntersectHandler( + _In_ PKSFILTER Filter, + _In_ PIRP Irp, + _In_ PKSP_PIN PinInstance, + _In_ PKSDATARANGE CallerDataRange, + _In_ PKSDATARANGE DescriptorDataRange, + _In_ ULONG BufferSize, + _Out_opt_ PVOID Data OPTIONAL, + _Out_ PULONG DataSize + ); +}; + +// +// Define a dispatch table for a capture pin. It provides notifications +// about creation, closure, processing, data formats, etc. using naming +// conventions shared by all CCapturePin derived classes. +// +#define DEFINE_CAMERA_KSPIN_DISPATCH( Table, Class ) \ +const \ +KSPIN_DISPATCH \ +Table ={ \ + Class::DispatchCreate, /* Pin Create */ \ + Class::DispatchClose, /* Pin Close */ \ + Class::DispatchProcess, /* Pin Process */ \ + Class::DispatchReset, /* Pin Reset */ \ + Class::DispatchSetFormat, /* Pin Set Data Format */ \ + Class::DispatchSetState, /* Pin Set Device State */ \ + Class::DispatchConnect, /* Pin Connect */ \ + Class::DispatchDisconnect, /* Pin Disconnect */ \ + NULL, /* Clock Dispatch */ \ + NULL /* Allocator Dispatch */ \ +}; + diff --git a/avscamera/sys/Common.h b/avscamera/sys/Common.h new file mode 100644 index 000000000..5ed63b6a8 --- /dev/null +++ b/avscamera/sys/Common.h @@ -0,0 +1,704 @@ + +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2015, Microsoft Corporation. + + File: + + Common.h + + Abstract: + + Common project header. + + History: + + created 02/18/2015 + +**************************************************************************/ + +#pragma once + +#define _NO_SYS_GUID_OPERATOR_EQ_ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#define NOBITMAP +#include +#undef NOBITMAP +#include + +#include "MetadataInternal.h" +#include "CustomProperties.h" +#include "Dbg.h" + +/************************************************* + + Add definitions that are missing for C++14. + +*************************************************/ + +/*++ + +Routine Description: + + Array new() operator for creating objects with a specified allocation tag. + +Arguments: + + iSize - + The size of the entire allocation. + + poolType - + The type of allocation. Ex: PagedPool or NonPagedPoolNx + + tag - + A 4-byte allocation identifier. + +Return Value: + + None + +--*/ +inline +PVOID +operator new[]( + size_t iSize, + _When_((poolType & NonPagedPoolMustSucceed) != 0, + __drv_reportError("Must succeed pool allocations are forbidden. " + "Allocation failures cause a system crash")) + POOL_TYPE poolType, + ULONG tag +) +{ + PVOID result = ExAllocatePoolWithTag(poolType, iSize, tag); + + if (result) + { + RtlZeroMemory(result, iSize); + } + + return result; +} + +/*++ + +Routine Description: + + Array delete() operator. + +Arguments: + + pVoid - + The memory to free. + +Return Value: + + None + +--*/ +inline +void +__cdecl +operator delete[]( + PVOID pVoid +) +{ + if (pVoid) + { + ExFreePool(pVoid); + } +} + +/*++ + +Routine Description: + + Sized delete() operator. + +Arguments: + + pVoid - + The memory to free. + + size - + The size of the memory to free. + +Return Value: + + None + +--*/ +inline void __cdecl operator delete +( + void *pVoid, + size_t /*size*/ +) +{ + if (pVoid) + { + ExFreePool(pVoid); + } +} + +/*++ + +Routine Description: + + Sized delete[]() operator. + +Arguments: + + pVoid - + The memory to free. + + size - + The size of the memory to free. + +Return Value: + + None + +--*/ +inline void __cdecl operator delete[] +( + void *pVoid, + size_t /*size*/ +) +{ + if (pVoid) + { + ExFreePool(pVoid); + } +} + +/************************************************* + + Misc Definitions + +*************************************************/ +#pragma warning (disable : 4100 4127 4131 4189 4701 4706) +#define STR_MODULENAME "AvsCamera: " + +#define IFFAILED_EXIT(status) \ + if (!NT_SUCCESS( Status = (status) )) \ + { \ + goto done; \ + } + +#define IFNULL_EXIT(exp) \ + if ( (exp) == nullptr ) \ + { \ + Status = STATUS_INSUFFICIENT_RESOURCES; \ + goto done; \ + } + +// +// Number of 100ns in one second +// +#define ONESECOND 10000000 + +#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') +#define FOURCC_NV12 mmioFOURCC('N', 'V', '1', '2') + +//Number of filters: FFC, RFC +#define CAPTURE_FILTER_COUNT 2 + +//filter index +#define CAPTURE_FILTER_RFC 0 +#define CAPTURE_FILTER_FFC 1 + +//Arbitrary Maximum faces +#define MAX_FACES 15 + +// +// CAPTURE_PIN_DATA_RANGE_COUNT:(formats) +// +// The number of ranges supported on the capture pin. +// +#define IMAGE_CAPTURE_PIN_DATA_RANGE_COUNT 2 +#define VIDEO_CAPTURE_PIN_DATA_RANGE_COUNT 30 +#define VIDEO_PREVIEW_PIN_DATA_RANGE_COUNT 15 + +// +// CAPTURE_FILTER_PIN_COUNT: +// +// The number of pins on the capture filter. +// +#define CAPTURE_FILTER_VIDEO_PIN_COUNT 2 +#define CAPTURE_FILTER_IMAGE_PIN_COUNT 1 +#define CAPTURE_FILTER_PIN_COUNT (CAPTURE_FILTER_VIDEO_PIN_COUNT+CAPTURE_FILTER_IMAGE_PIN_COUNT) + + +#define CAPTURE_FILTER_VIDEO_PIN 0 +#define CAPTURE_FILTER_PREVIEW_PIN 1 +#define CAPTURE_FILTER_STILL_PIN 2 + +// +// Define the number of frames needed in the queue for each pin. +// +#define IMAGE_CAPTURE_PIN_MINIMUM_FRAMES 3 +#define VIDEO_CAPTURE_PIN_MINIMUM_FRAMES 4 +#define PREVIEW_PIN_MINIMUM_FRAMES 6 +#define IMAGE_CAPTURE_PIN_MAXIMUM_FRAMES 20 +#define IMAGE_CAPTURE_PIN_MAXIMUM_HISTORY_FRAMES 10 + +// +// CAPTURE_FILTER_CATEGORIES_COUNT: +// +// The number of categories for the capture filter. +// +#define CAPTURE_FILTER_CATEGORIES_COUNT 3 + +#define AVSHWS_POOLTAG 'hSVA' + +// +// Focal length definitions +// + +// These are the definitions found in MSDN for the legacy zoom controls. +// +//#define FOCALLENGTH_OCULAR 50 +//#define FOCALLENGTH_OPTICAL_MIN 10 +//#define FOCALLENGTH_OPTICAL_MAX 600 + +//#define FOCALLENGTH_OCULAR 50 +//#define FOCALLENGTH_OPTICAL_MIN 10 +//#define FOCALLENGTH_OPTICAL_MAX 600 + +// Note: Despite what MSDN says, they are NOT limits as far as the +// pipeline is conerned. You should supply values here that are +// appropriate to your hardware - whatever that may be. +// +// Also note that since both optical and ocular focal length are +// nomally measured in millimeters, and since the optical length is +// always the numerator and ocular is used as the denominator, the +// units (millimeters) cancel out. This means you can stick any set +// of values you like here that can be used to represent the min and +// maximum zoom value of your hardware as a simple rational number. +// +// Again, the rational number is: (Objective length / Ocular length) +// +// We use the following definitions in this simulation in order to have the +// values produced and used in the legacy control be the same as the ones +// in the new extended zoom control. +// +#define FOCALLENGTH_OCULAR (1<<16) // Denominator of 2^16 makes this the same as Q16 format in extended. +#define FOCALLENGTH_OPTICAL_MIN (FOCALLENGTH_OCULAR/16) // Allow a wide-field view of 1/16th. +#define FOCALLENGTH_OPTICAL_MAX (FOCALLENGTH_OCULAR*32) // Allow a zoom-in view of 32x. + +// Values for Power Line Frequency +#define POWERLINEFREQ_DISABLED 0 +#define POWERLINEFREQ_50HZ 1 +#define POWERLINEFREQ_60HZ 2 +#define POWERLINEFREQ_DEFAULT POWERLINEFREQ_DISABLED + + +// +// Q-format definitions. +// +#define BASE_Q(n) (((ULONGLONG)1)<>16) +#define DIV_Q16(_x_, _y_) (LONGLONG)((((LONGLONG)(_x_)<<16)+(_y_/2))/(LONGLONG)(_y_)) + +#define MAKEWORD(a, b) ((WORD)(((BYTE)(((DWORD_PTR)(a)) & 0xff)) | ((WORD)((BYTE)(((DWORD_PTR)(b)) & 0xff))) << 8)) +#define MAKELONG(a, b) ((LONG)(((WORD)(((DWORD_PTR)(a)) & 0xffff)) | ((DWORD)((WORD)(((DWORD_PTR)(b)) & 0xffff))) << 16)) + +// +// Copy of histogram channel bitmask definitions from mfapi.h (not available in wdk). +// +#define MF_HISTOGRAM_CHANNEL_Y 0x00000001 +#define MF_HISTOGRAM_CHANNEL_R 0x00000002 +#define MF_HISTOGRAM_CHANNEL_G 0x00000004 +#define MF_HISTOGRAM_CHANNEL_B 0x00000008 +#define MF_HISTOGRAM_CHANNEL_Cb 0x00000010 +#define MF_HISTOGRAM_CHANNEL_Cr 0x00000020 + +/************************************************* + + Externed information + +*************************************************/ +// +// filter.cpp externs: +// +extern +const +KSFILTER_DISPATCH +AvsCameraFilterDispatch; + +extern +const +KSFILTER_DESCRIPTOR +AvsCameraFilterDescriptor; + +extern +const +KSFILTER_DESCRIPTOR +AvsCameraFilterDescriptorFFC; + +extern +const +KSPIN_DESCRIPTOR_EX +AvsCameraFilterPinDescriptors [CAPTURE_FILTER_PIN_COUNT]; + +extern +const +KSPIN_DESCRIPTOR_EX +AvsCameraFilterPinDescriptorsFFC [CAPTURE_FILTER_PIN_COUNT]; + +extern +const +GUID +AvsCameraFilterCategories [CAPTURE_FILTER_CATEGORIES_COUNT]; + +// +// capture.cpp externs: +// +extern +const +KSALLOCATOR_FRAMING_EX +VideoCapturePinAllocatorFraming; + +extern +const +KSALLOCATOR_FRAMING_EX +ImageCapturePinAllocatorFraming; + +extern +const +KSPIN_DISPATCH +VideoCapturePinDispatch; + +extern +const +KSPIN_DISPATCH +ImageCapturePinDispatch; + +extern +const +PKSDATARANGE +VideoCapturePinDataRanges [VIDEO_CAPTURE_PIN_DATA_RANGE_COUNT]; + +extern +const +PKSDATARANGE +VideoPreviewPinDataRanges[VIDEO_PREVIEW_PIN_DATA_RANGE_COUNT]; + +extern +const +PKSDATARANGE +ImageCapturePinDataRanges[IMAGE_CAPTURE_PIN_DATA_RANGE_COUNT]; + +/************************************************* + + Enums / Typedefs + +*************************************************/ + +typedef enum _PIN_STATE +{ + + PinStopped = 0, + PinPaused, + PinRunning + +} PIN_STATE, *PPIN_STATE; + +typedef enum _PIN_MODE +{ + + PinNormalMode = 0, + PinBurstMode + +} PIN_MODE, *PPIN_MODE; + + +/************************************************* + + Class Definitions + +*************************************************/ + +struct ISP_FRAME_SETTINGS +{ + ULONGLONG ISOMode; + ULONG ISOValue; // for Manual ISO settings. + KSCAMERA_EXTENDEDPROP_EVCOMPENSATION EVCompensation; + ULONGLONG WhiteBalanceMode; + KSCAMERA_EXTENDEDPROP_VIDEOPROCSETTING WhiteBalanceSetting; + ULONGLONG ExposureMode; + KSCAMERA_EXTENDEDPROP_VIDEOPROCSETTING ExposureSetting; + ULONGLONG FocusMode; + KSCAMERA_EXTENDEDPROP_VIDEOPROCSETTING FocusSetting; + ULONGLONG FocusPriority; + ULONGLONG FlashMode; + ULONG FlashValue; // Adjustable power setting (0-100) + BOOLEAN bPhotoConfirmation; +} ; + +struct SOC_CAP_WITH_STEPPING +{ + KSCAMERA_PERFRAMESETTING_CAP_ITEM_HEADER Hdr; + KSPROPERTY_STEPPING_LONG Stepping; +} ; + +struct SOC_CAP_WITH_STEPPING_LONGLONG +{ + KSCAMERA_PERFRAMESETTING_CAP_ITEM_HEADER Hdr; + KSPROPERTY_STEPPING_LONGLONG Stepping; +} ; + +// +// ICapturePin: +// +// This is a capture pin interface. The device level calls back the +// CompleteMapping method passing the number of completed mappings for +// the capture pin. +// +class ICapturePin +{ + +public: + + virtual + NTSTATUS + CompleteMapping( + _In_ PKSSTREAM_POINTER Clone=nullptr + ) = 0; + +}; + +class LockDevice +{ +public: + + LockDevice(PKSDEVICE device) : m_device(device) + { + NT_ASSERT(m_device); + KsAcquireDevice(m_device); + } + + ~LockDevice() + { + KsReleaseDevice(m_device); + } +private: + PKSDEVICE m_device; +}; + +class LockFilter +{ +public: + + LockFilter(PKSFILTER filter) : m_filter(filter) + { + NT_ASSERT(m_filter); + KsFilterAcquireControl(m_filter); + } + + ~LockFilter() + { + KsFilterReleaseControl(m_filter); + } +private: + PKSFILTER m_filter; +}; + +// The following class definitions and ctors are used solely +// to simplify initialization of the parent metadata structures. +// They are not here for any other purpose. + +template +class CMetaRational : public U +{ +public: + CMetaRational( T Num=1, T Denom=1 ) + { + Set=TRUE; + Numerator = Num; + Denominator = Denom; + } +}; + +typedef CMetaRational CMetadataRational; +typedef CMetaRational CMetadataSRational; + +template +class CMetaPrimative : public U +{ +public: + CMetaPrimative( T n ) + { + Set = TRUE; + Value = n; + } +}; + +typedef CMetaPrimative CMetadataShort; +typedef CMetaPrimative CMetadataLong; +typedef CMetaPrimative CMetadataLongLong; +typedef CMetaPrimative CMetadataULongLong; + +class CMetadataShortString : public METADATA_SHORTSTRING +{ +public: + CMetadataShortString( + _In_z_ const CHAR *str + ) + { + Length = (UINT32) min(strlen(str), sizeof(String)); + strncpy_s( String, str, Length ); + } +}; + +class CMetadataEVCompensation : public METADATA_EVCOMP +{ +public: + CMetadataEVCompensation( + _In_ UINT64 flags, + _In_ UINT32 value + ) + { + Set = TRUE; + Flags = flags; + Value = value; + } +}; + +typedef struct +{ + LONG Value; + ULONG Flags; + ULONG Capabilities; +} VIDCAP_PROPERTY, *PVIDCAP_PROPERTY; + +typedef struct +{ + LONG lOcularFocalLength; + LONG lObjectiveFocalLengthMin; + LONG lObjectiveFocalLengthMax; +} FOCAL_LENGTH_PROPERTY, *PFOCAL_LENGTH_PROPERTY; + +const ULONG KSCAMERA_EXTENDEDPROP_VERSION = 1; + +// +// Note: I cannot simplify this down to just the FrameIndex. +// Past frames and non-VPS photos can have a photo confirmation set +// and those images will not have an index associated with them. +// +class PHOTOCONFIRMATION_INFO +{ +public: + PHOTOCONFIRMATION_INFO( + _In_ ULONG FrameIndex, + _In_ LONGLONG Time + ) + : m_Index( FrameIndex ) + , m_Time(Time) + , m_bRequired(TRUE) + {} + + PHOTOCONFIRMATION_INFO() + : m_Index(0) + , m_Time(0) + , m_bRequired( FALSE ) + {} + + BOOL + isRequired() + { + return m_bRequired; + } + + ULONG + getIndex() + { + return m_Index; + } + + LONGLONG + getTime() + { + return m_Time; + } + +private: + BOOL m_bRequired; + ULONG m_Index; + LONGLONG m_Time; +}; + +/************************************************* + + Global Functions + +*************************************************/ + +/************************************************* + + Internal Includes + +*************************************************/ + +#include "util.h" +#include "NonCopyable.h" +#include "ref.h" +#include "Mutex.h" +#include "Waitable.h" +#include "WorkItem.h" +#include "Timer.h" +#include "ExtendedProperty.h" +#include "ExtendedVidProcSetting.h" +#include "ExtendedEvComp.h" +#include "ExtendedMetadata.h" +#include "ExtendedPhotoMode.h" +#include "ExtendedMaxVideoFpsForPhotoRes.h" +#include "ExtendedFieldOfView.h" +#include "ExtendedCameraAngleOffset.h" +#include "CameraProfile.h" +#include "Synthesizer.h" +#include "XRGBSynthesizer.h" +#include "RGB24Synthesizer.h" +#include "YUVSynthesizer.h" +#include "YUY2Synthesizer.h" +#include "NV12Synthesizer.h" +#include "hwsim.h" +#include "ImageHwSim.h" +#include "PreviewHwSim.h" +#include "VideoHwSim.h" +#include "device.h" +#include "AvsCameraDevice.h" +#include "Roi.h" +#include "Sensor.h" +#include "SensorSimulation.h" +#include "filter.h" +#include "AvsCameraFilter.h" +#include "capture.h" +#include "videocapture.h" +#include "imagecapture.h" +#include +#include diff --git a/avscamera/sys/Dbg.h b/avscamera/sys/Dbg.h new file mode 100644 index 000000000..b0bc6e800 --- /dev/null +++ b/avscamera/sys/Dbg.h @@ -0,0 +1,81 @@ +/************************************************************************** + + AVStream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + Dbg.h + + Abstract: + + Debug definitions. + + History: + + created 2/18/2015 + +**************************************************************************/ + +#pragma once + +#define DEBUGLVL_VERBOSE 2 +#define DEBUGLVL_TERSE 1 +#define DEBUGLVL_ERROR 0 + +const int 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 + +// This is for debug output; but we're going to leave it in the +// driver for now. We can always turn off the Windbg output at +// the debugger. +// Enable via debugger: ed Kd_IHVSTREAMING_Mask 0xFFFFFFFF +#define ENABLE_TRACING 1 +#if ENABLE_TRACING + +#define DBG_OUT( FMT, ... ) \ + { \ + LARGE_INTEGER SysTime; \ + LARGE_INTEGER LocalTime; \ + TIME_FIELDS TimeFields; \ + \ + KeQuerySystemTimePrecise( &SysTime ); \ + ExSystemTimeToLocalTime( &SysTime, &LocalTime ); \ + RtlTimeToTimeFields( &LocalTime, &TimeFields ); \ + DbgPrintEx( DPFLTR_IHVSTREAMING_ID, DPFLTR_MASK|DPFLTR_TRACE_LEVEL, \ + "%02d:%02d:%02d.%03d [%p] " ## FMT ## "\n", \ + TimeFields.Hour, TimeFields.Minute, TimeFields.Second, TimeFields.Milliseconds, \ + KeGetCurrentThread(), __VA_ARGS__ ); \ + } + +#define DBG_ENTER( FMT, ... ) \ + DBG_OUT( "Entering: " __FUNCTION__ ## FMT, __VA_ARGS__ ); + +#define DBG_LEAVE( FMT, ... ) \ + DBG_OUT( " Leaving: " __FUNCTION__ ## FMT, __VA_ARGS__ ); + +#define DBG_TRACE( FMT, ... ) \ + DBG_OUT( " In: " __FUNCTION__ ## " " ## FMT, __VA_ARGS__ ); + +#else // !ENABLE_TRACING +#define DBG_ENTER( ... ) +#define DBG_LEAVE( ... ) +#define DBG_TRACE( ... ) +#endif // !ENABLE_TRACING + diff --git a/avscamera/sys/Device.cpp b/avscamera/sys/Device.cpp new file mode 100644 index 000000000..89eab0ca1 --- /dev/null +++ b/avscamera/sys/Device.cpp @@ -0,0 +1,962 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2001, Microsoft Corporation. + + File: + + device.cpp + + Abstract: + + This file contains the implementation of CCaptureDevice. + + CCaptureDevice is the base device class object. It handles basic PnP + requests and constructs the filter factories. + + History: + + created 3/9/2001 + +**************************************************************************/ + +#include "Common.h" + +/************************************************************************** + + PAGEABLE CODE + +**************************************************************************/ + +///// +/// Structures... +//// Profile 0 +static KSCAMERA_PROFILE_MEDIAINFO s_Profile0_PreviewMediaInfo[] = +{ + //{ resolution }, { fps }, Flags, Data0, Data1, Data2, Data 3 + { { 1920, 1080 }, { 30, 1 }, 0, 0, 0, 0, 0 }, + { { 1280, 720 }, { 30, 1 }, 0, 0, 0, 0, 0 }, + { { 640, 360 }, { 30, 1 }, 0, 0, 0, 0, 0 }, +}; + +static KSCAMERA_PROFILE_MEDIAINFO s_Profile0_RecordMediaInfo[] = +{ + //{ resolution }, { fps }, Flags, Data0, Data1, Data2, Data 3 + { { 3840, 2160 }, { 30, 1 }, 0, 0, 0, 0, 0 }, + { { 1920, 1080 }, { 120, 1 }, 0, 0, 0, 0, 0 }, + { { 1920, 1080 }, { 90, 1 }, 0, 0, 0, 0, 0 }, + { { 1920, 1080 }, { 60, 1 }, 0, 0, 0, 0, 0 }, + { { 1920, 1080 }, { 30, 1 }, 0, 0, 0, 0, 0 }, + { { 1280, 720 }, { 30, 1 }, 0, 0, 0, 0, 0 }, + { { 640, 360 }, { 30, 1 }, 0, 0, 0, 0, 0 }, +}; + +static KSCAMERA_PROFILE_MEDIAINFO s_Profile0_PhotoMediaInfo[] = +{ + //{ resolution }, { fps }, Flags, Data0, Data1, Data2, Data 3 + { { 1920, 1080 }, { 30, 1 }, 0, 0, 0, 0, 0 }, + { { 1280, 720 }, { 30, 1 }, 0, 0, 0, 0, 0 }, + { { 640, 360 }, { 30, 1 }, 0, 0, 0, 0, 0 } +}; + + +//// Profile 1 +static KSCAMERA_PROFILE_MEDIAINFO s_Profile1_PreviewMediaInfo[] = +{ + //{ resolution }, { fps }, Flags, Data0, Data1, Data2, Data 3 + { { 1920, 1080 }, { 30, 1 }, 0, 0, 0, 0, 0 }, + { { 1280, 720 }, { 30, 1 }, 0, 0, 0, 0, 0 }, + { { 640, 360 }, { 30, 1 }, 0, 0, 0, 0, 0 } +}; + +static KSCAMERA_PROFILE_MEDIAINFO s_Profile1_PhotoMediaInfo[] = +{ + //{ resolution }, { fps }, Flags, Data0, Data1, Data2, Data 3 + { { 7680, 4320 }, { 0, 1 }, 0, 0, 0, 0, 0 }, + { { 3840, 2160 }, { 30, 1 }, 0, 0, 0, 0, 0 }, + { { 1920, 1080 }, { 30, 1 }, 0, 0, 0, 0, 0 }, + { { 1280, 720 }, { 30, 1 }, 0, 0, 0, 0, 0 }, + { { 640, 360 }, { 30, 1 }, 0, 0, 0, 0, 0 } +}; + +//// Profile 2 +static KSCAMERA_PROFILE_MEDIAINFO s_Profile2_PreviewMediaInfo[] = +{ + //{ resolution }, { fps }, Flags, Data0, Data1, Data2, Data 3 + { { 1920, 1080 }, { 30, 1 }, 0, 0, 0, 0, 0 }, + { { 1280, 720 }, { 30, 1 }, 0, 0, 0, 0, 0 }, + { { 640, 360 }, { 30, 1 }, 0, 0, 0, 0, 0 } +}; + +static KSCAMERA_PROFILE_MEDIAINFO s_Profile2_RecordMediaInfo[] = +{ + //{ resolution }, { fps }, Flags, Data0, Data1, Data2, Data 3 + { { 1920, 1080 }, { 30, 1 }, 0, 0, 0, 0, 0 }, + { { 1280, 720 }, { 30, 1 }, 0, 0, 0, 0, 0 }, + { { 640, 360 }, { 30, 1 }, 0, 0, 0, 0, 0 } +}; + +static KSCAMERA_PROFILE_PININFO s_Profile0_PinInfo[] = +{ + { {STATIC_PINNAME_VIDEO_PREVIEW}, 0, ARRAYSIZE(s_Profile0_PreviewMediaInfo), s_Profile0_PreviewMediaInfo }, + { {STATIC_PINNAME_VIDEO_CAPTURE}, 0, ARRAYSIZE(s_Profile0_RecordMediaInfo), s_Profile0_RecordMediaInfo }, + { {STATIC_PINNAME_IMAGE}, 0, ARRAYSIZE(s_Profile0_PhotoMediaInfo), s_Profile0_PhotoMediaInfo } +}; + +static KSCAMERA_PROFILE_PININFO s_Profile1_PinInfo[] = +{ + { {STATIC_PINNAME_VIDEO_PREVIEW}, 0, ARRAYSIZE(s_Profile1_PreviewMediaInfo), s_Profile1_PreviewMediaInfo }, + { {STATIC_PINNAME_IMAGE}, 0, ARRAYSIZE(s_Profile1_PhotoMediaInfo), s_Profile1_PhotoMediaInfo } +}; + +static KSCAMERA_PROFILE_PININFO s_Profile2_PinInfo[] = +{ + { {STATIC_PINNAME_VIDEO_PREVIEW}, 0, ARRAYSIZE(s_Profile2_PreviewMediaInfo), s_Profile2_PreviewMediaInfo }, + { {STATIC_PINNAME_VIDEO_CAPTURE}, 0, ARRAYSIZE(s_Profile2_RecordMediaInfo), s_Profile2_RecordMediaInfo } +}; + +static KSCAMERA_PROFILE_INFO s_Profiles[] = +{ + { {STATIC_KSCAMERAPROFILE_BalancedVideoAndPhoto}, 0, ARRAYSIZE(s_Profile0_PinInfo), s_Profile0_PinInfo }, + { {STATIC_KSCAMERAPROFILE_HighQualityPhoto}, 0, ARRAYSIZE(s_Profile1_PinInfo), s_Profile1_PinInfo }, + { {STATIC_KSCAMERAPROFILE_VideoRecording}, 0, ARRAYSIZE(s_Profile2_PinInfo), s_Profile2_PinInfo } +}; + +// The front camera is concurrent with the rear camera, so the front camera concurrency +// has the back camera's reference GUID and vice versa. +static KSCAMERA_PROFILE_CONCURRENCYINFO s_Profile2_ConcurrencyInfoFront[] = +{ + { {STATIC_RearCamera_Filter}, 0, 1, &s_Profiles[2] } +}; + +static KSCAMERA_PROFILE_CONCURRENCYINFO s_Profile2_ConcurrencyInfoBack[] = +{ + { {STATIC_FrontCamera_Filter}, 0, 1, &s_Profiles[2] } +}; + +const UINT32 s_ProfileCount = 3; + +UINT32 +InitializeDeviceProfiles( + _In_ BOOL fFrontCamera, + _Outptr_result_maybenull_ + PKSDEVICE_PROFILE_INFO *ppDeviceProfiles +) +{ + UINT32 uiProfileCount = 0; + PKSDEVICE_PROFILE_INFO pDeviceProfiles = NULL; + + pDeviceProfiles = (PKSDEVICE_PROFILE_INFO)ExAllocatePoolWithTag( PagedPool, sizeof(KSDEVICE_PROFILE_INFO) * ARRAYSIZE(s_Profiles) , 'fpSC'); + if( !pDeviceProfiles ) + { + // Can't publish, we're out of memory, silently fail here. + *ppDeviceProfiles = nullptr; + goto Exit; + } + + for (UINT32 i = 0; i < ARRAYSIZE(s_Profiles); i++) + { + pDeviceProfiles[i].Type = KSDEVICE_PROFILE_TYPE_CAMERA; + pDeviceProfiles[i].Size = sizeof(KSDEVICE_PROFILE_INFO); + pDeviceProfiles[i].Camera.Info = s_Profiles[i]; + pDeviceProfiles[i].Camera.Reserved = 0; + pDeviceProfiles[i].Camera.ConcurrencyCount = 0; + pDeviceProfiles[i].Camera.Concurrency = NULL; + } + + pDeviceProfiles[2].Camera.ConcurrencyCount = 1; + if (fFrontCamera) + { + pDeviceProfiles[2].Camera.Concurrency = (PKSCAMERA_PROFILE_CONCURRENCYINFO)&s_Profile2_ConcurrencyInfoFront; + } + else + { + pDeviceProfiles[2].Camera.Concurrency = (PKSCAMERA_PROFILE_CONCURRENCYINFO)&s_Profile2_ConcurrencyInfoBack; + } + + uiProfileCount = ARRAYSIZE(s_Profiles); + *ppDeviceProfiles = pDeviceProfiles; + pDeviceProfiles = NULL; + +Exit: + return uiProfileCount; +} + +#ifdef ALLOC_PRAGMA +#pragma code_seg("PAGE") +#endif // ALLOC_PRAGMA + +CCaptureDevice:: +CCaptureDevice ( + _In_ PKSDEVICE Device +) + : m_Device (Device) + , m_FilterDescriptorCount(0) + , m_Sensor(nullptr) + , m_Context(nullptr) +{ + PAGED_CODE(); +} + +CCaptureDevice:: +~CCaptureDevice() +{ + PAGED_CODE(); + + if( m_Sensor ) + { + for( size_t i=0; iDescriptor->ReferenceGuid && + IsEqualGUID(*(m_Context[i].Descriptor->ReferenceGuid), *Filter->Descriptor->ReferenceGuid)) + { + break; + } + } + + // This is a complete failure. We really need to bail. + NT_ASSERT(i Bag, + reinterpret_cast (this), + reinterpret_cast (CCaptureDevice::Cleanup) + ); + + if( NT_SUCCESS (Status) ) + { + Device->Context=this; + } + return Status; +} + +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(); + + DBG_ENTER("()"); + + CCaptureDevice *CapDevice = new (NonPagedPoolNx, 'eviD') CCaptureDevice (Device); + if( !CapDevice) + { + // + // Return failure if we couldn't create the device. + // + return STATUS_INSUFFICIENT_RESOURCES; + } + + NTSTATUS Status = CapDevice->Prepare(); + if (!NT_SUCCESS (Status)) + { + // Init failure. + delete CapDevice; + } + + DBG_LEAVE("()"); + return Status; +} + +// +// DispatchStart(): +// +// This is the Pnp Start dispatch for the capture device. It simply +// bridges to PnpStart() in the context of the CCaptureDevice. +// +NTSTATUS +CCaptureDevice:: +DispatchStart ( + _In_ PKSDEVICE Device, + _In_ PIRP Irp, + _In_ PCM_RESOURCE_LIST TranslatedResourceList, + _In_ PCM_RESOURCE_LIST UntranslatedResourceList +) +{ + PAGED_CODE(); + + DBG_ENTER("()"); + + NTSTATUS Status = + Recast(Device)-> + PnpStart ( + TranslatedResourceList, + UntranslatedResourceList + ); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// +// DispatchStop(): +// +// This is the Pnp stop dispatch for the capture device. It simply +// bridges to PnpStop() in the context of the CCaptureDevice. +// +void +CCaptureDevice:: +DispatchStop ( + _In_ PKSDEVICE Device, + _In_ PIRP Irp +) +{ + PAGED_CODE(); + + DBG_ENTER("()"); + Recast(Device)->PnpStop(Irp); + DBG_LEAVE("()"); +} + +NTSTATUS +CCaptureDevice:: +DispatchPostStart( + _In_ PKSDEVICE Device +) +{ + PAGED_CODE(); + + DBG_ENTER("()"); + + NTSTATUS Status = + Recast(Device)->PnpPostStart(); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +NTSTATUS +CCaptureDevice:: +DispatchQueryStop( + _In_ PKSDEVICE Device, + _In_ PIRP Irp +) +{ + PAGED_CODE(); + + DBG_ENTER("()"); + + NTSTATUS Status = + Recast(Device)->PnpQueryStop(Irp); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +void +CCaptureDevice:: +DispatchCancelStop( + _In_ PKSDEVICE Device, + _In_ PIRP Irp +) +{ + PAGED_CODE(); + + DBG_ENTER("()"); + Recast(Device)->PnpCancelStop(Irp); + DBG_LEAVE("()"); +} + +NTSTATUS +CCaptureDevice:: +DispatchQueryRemove( + _In_ PKSDEVICE Device, + _In_ PIRP Irp +) +{ + PAGED_CODE(); + + DBG_ENTER("()"); + + NTSTATUS Status = + Recast(Device)->PnpQueryRemove(Irp); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +void +CCaptureDevice:: +DispatchCancelRemove( + _In_ PKSDEVICE Device, + _In_ PIRP Irp +) +{ + PAGED_CODE(); + + DBG_ENTER("()"); + Recast(Device)->PnpCancelRemove(Irp); + DBG_LEAVE("()"); +} + +void +CCaptureDevice:: +DispatchRemove( + _In_ PKSDEVICE Device, + _In_ PIRP Irp +) +{ + PAGED_CODE(); + + DBG_ENTER("()"); + Recast(Device)->PnpRemove(Irp); + DBG_LEAVE("()"); +} + +void +CCaptureDevice:: +DispatchSurpriseRemoval( + _In_ PKSDEVICE Device, + _In_ PIRP Irp +) +{ + PAGED_CODE(); + + DBG_ENTER("()"); + Recast(Device)->PnpSurpriseRemoval(Irp); + DBG_LEAVE("()"); +} + +NTSTATUS +CCaptureDevice:: +DispatchQueryCapabilities( + _In_ PKSDEVICE Device, + _In_ PIRP Irp, + _Inout_ PDEVICE_CAPABILITIES Capabilities +) +{ + PAGED_CODE(); + + DBG_ENTER("()"); + + NTSTATUS Status = + Recast(Device)-> + PnpQueryCapabilities( Irp, Capabilities ); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + + +// Make Power callbacks non-pageable. +#ifdef ALLOC_PRAGMA +#pragma code_seg() +#endif // ALLOC_PRAGMA + +NTSTATUS +CCaptureDevice:: +DispatchQueryPower( + _In_ PKSDEVICE Device, + _In_ PIRP Irp, + _In_ DEVICE_POWER_STATE DeviceTo, + _In_ DEVICE_POWER_STATE DeviceFrom, + _In_ SYSTEM_POWER_STATE SystemTo, + _In_ SYSTEM_POWER_STATE SystemFrom, + _In_ POWER_ACTION Action +) +{ + // If this device supports the the DO_POWER_INRUSH flag, this function + // can be called at DISPATCH. + + DBG_ENTER("()"); + NTSTATUS Status = + Recast(Device)-> + PnpQueryPower( + Irp, + DeviceTo, + DeviceFrom, + SystemTo, + SystemFrom, + Action ); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +void +CCaptureDevice:: +DispatchSetPower( + _In_ PKSDEVICE Device, + _In_ PIRP Irp, + _In_ DEVICE_POWER_STATE To, + _In_ DEVICE_POWER_STATE From +) +{ + // If this device supports the the DO_POWER_INRUSH flag, this function + // can be called at DISPATCH. + + DBG_ENTER("()"); + Recast(Device)-> + PnpSetPower( Irp, To, From ); + DBG_LEAVE("()"); +} + +#ifdef ALLOC_PRAGMA +#pragma code_seg("PAGE") +#endif // ALLOC_PRAGMA + +NTSTATUS +CCaptureDevice:: +DispatchQueryInterface( + _In_ PKSDEVICE Device, + _In_ PIRP Irp +) +{ + PAGED_CODE(); + + DBG_ENTER("()"); + + NTSTATUS Status = + Recast(Device)-> + PnpQueryInterface(Irp); + + DBG_LEAVE("()=0x%08X", Status); + 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 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; + + // + // 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. + // + // Walk the list of descriptors and enumerate the h/w sims described. + // + + if (!m_Device -> Started) + { + // Create the Filter for the device + for( size_t i=0; iFunctionalDeviceObject, + m_Context[i].Descriptor, + m_Context[i].Name, + NULL, + KSCREATE_ITEM_FREEONSTOP, + NULL, + NULL, + &FilterFactory ) + ); + + // Set primary camera to its interface, this allows device to be queried by GUID + if( FilterFactory ) + { + PKSDEVICE_PROFILE_INFO pDeviceProfiles = nullptr; + UINT32 uiProfileCount = 0; + PUNICODE_STRING SymbolicLinkName = KsFilterFactoryGetSymbolicLink(FilterFactory); + ACPI_PLD_BUFFER pld = {0}; + BOOL fFrontCamera = FALSE; + pld.Panel = m_Context[i].AcpiPosition; + static + const + GUID FFC_Filter = {STATIC_FrontCamera_Filter}; + + IFFAILED_EXIT( + IoSetDeviceInterfacePropertyData(SymbolicLinkName, + &DEVPKEY_Device_PhysicalDeviceLocation, + LOCALE_NEUTRAL, + PLUGPLAY_PROPERTY_PERSISTENT, + DEVPROP_TYPE_BINARY, + sizeof(ACPI_PLD_BUFFER), + (PVOID)&pld) + ); + + if( m_Context[i].Descriptor->ReferenceGuid && + IsEqualGUID(*(m_Context[i].Descriptor->ReferenceGuid), FFC_Filter)) + { + fFrontCamera = TRUE; + } + + // Publish our profile here. + uiProfileCount = InitializeDeviceProfiles(fFrontCamera, &pDeviceProfiles); + if( uiProfileCount > 0 ) + { + if( NT_SUCCESS(KsInitializeDeviceProfile(FilterFactory)) ) + { + for( UINT32 j=0; jInitialize()); + } + } + +done: + if( !NT_SUCCESS(Status) ) + { + // Free any sensor objects we created during this failed attempt. + for( size_t i=0; iFunctionalDeviceObject; +} + +/*************************************************/ + + +void +CCaptureDevice:: +PnpStop ( + _In_ PIRP Irp +) + +/*++ + +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 + +--*/ + +{ + UNREFERENCED_PARAMETER(Irp); + PAGED_CODE(); + + if (m_DmaAdapterObject) + { + // + // Return the DMA adapter back to the system. + // + m_DmaAdapterObject -> DmaOperations -> + PutDmaAdapter (m_DmaAdapterObject); + + m_DmaAdapterObject = NULL; + } +} + +NTSTATUS +CCaptureDevice:: +PnpPostStart() +{ + PAGED_CODE(); + return STATUS_SUCCESS; +} + +NTSTATUS +CCaptureDevice:: +PnpQueryStop( + _In_ PIRP Irp +) +{ + PAGED_CODE(); + UNREFERENCED_PARAMETER(Irp); + return STATUS_SUCCESS; +} + +void +CCaptureDevice:: +PnpCancelStop( + _In_ PIRP Irp +) +{ + PAGED_CODE(); + UNREFERENCED_PARAMETER(Irp); +} + +NTSTATUS +CCaptureDevice:: +PnpQueryRemove( + _In_ PIRP Irp +) +{ + PAGED_CODE(); + UNREFERENCED_PARAMETER(Irp); + return STATUS_SUCCESS; +} + +void +CCaptureDevice:: +PnpCancelRemove( + _In_ PIRP Irp +) +{ + PAGED_CODE(); + UNREFERENCED_PARAMETER(Irp); +} + +void +CCaptureDevice:: +PnpRemove( + _In_ PIRP Irp +) +{ + PAGED_CODE(); + UNREFERENCED_PARAMETER(Irp); +} + +void +CCaptureDevice:: +PnpSurpriseRemoval( + _In_ PIRP Irp +) +{ + PAGED_CODE(); + UNREFERENCED_PARAMETER(Irp); +} + +NTSTATUS +CCaptureDevice:: +PnpQueryCapabilities( + _In_ PIRP Irp, + _Inout_ PDEVICE_CAPABILITIES Capabilities +) +{ + PAGED_CODE(); + UNREFERENCED_PARAMETER(Irp); + UNREFERENCED_PARAMETER(Capabilities); + return STATUS_SUCCESS; +} + +// Make Power callbacks non-pageable. +#ifdef ALLOC_PRAGMA +#pragma code_seg() +#endif // ALLOC_PRAGMA + +NTSTATUS +CCaptureDevice:: +PnpQueryPower( + _In_ PIRP Irp, + _In_ DEVICE_POWER_STATE DeviceTo, + _In_ DEVICE_POWER_STATE DeviceFrom, + _In_ SYSTEM_POWER_STATE SystemTo, + _In_ SYSTEM_POWER_STATE SystemFrom, + _In_ POWER_ACTION Action +) +{ + UNREFERENCED_PARAMETER(Irp); + UNREFERENCED_PARAMETER(DeviceTo); + UNREFERENCED_PARAMETER(DeviceFrom); + UNREFERENCED_PARAMETER(SystemTo); + UNREFERENCED_PARAMETER(SystemFrom); + UNREFERENCED_PARAMETER(Action); + return STATUS_SUCCESS; +} + +void +CCaptureDevice:: +PnpSetPower( + _In_ PIRP Irp, + _In_ DEVICE_POWER_STATE To, + _In_ DEVICE_POWER_STATE From +) +{ + UNREFERENCED_PARAMETER(Irp); + UNREFERENCED_PARAMETER(To); + UNREFERENCED_PARAMETER(From); +} + +#ifdef ALLOC_PRAGMA +#pragma code_seg("PAGE") +#endif // ALLOC_PRAGMA + +NTSTATUS +CCaptureDevice:: +PnpQueryInterface( + _In_ PIRP Irp +) +{ + PAGED_CODE(); + UNREFERENCED_PARAMETER(Irp); + return STATUS_SUCCESS; +} + +/*************************************************/ + diff --git a/avscamera/sys/Device.h b/avscamera/sys/Device.h new file mode 100644 index 000000000..c3135d045 --- /dev/null +++ b/avscamera/sys/Device.h @@ -0,0 +1,449 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2001, Microsoft Corporation. + + File: + + device.h + + Abstract: + + This file contains the declaration of CCaptureDevice. + + CCaptureDevice is the base device class object. It handles basic PnP + requests and constructs the filter factories. + + History: + + created 3/9/2001 + +**************************************************************************/ + +// +// Forward references... +// +class CSensor; + +struct CSensorContext +{ + PWSTR Name; // What to call this class of filter. + const KSFILTER_DESCRIPTOR *Descriptor; // The description of the filter. + AcpiPldPanel AcpiPosition; // Where the device is located. +}; + +class CCaptureDevice +{ +protected: + + // + // The AVStream device we're associated with. + // + PKSDEVICE m_Device; + + // + // Since we don't have physical hardware, this provides the hardware + // simulation. m_HardwareSimulation provides the fake ISR, fake DPC, + // etc... m_Synthesizer provides RGB24 and UYVY image synthesis and + // overlay in software. + // + + // + // Number of pins with resources acquired. This is used as a locking + // mechanism for resource acquisition on the device. + // + + // The capture pin. When we complete scatter / gather mappings, we + // notify the capture pin. + // + + // Number of Filter descriptors & filter factories. + size_t m_FilterDescriptorCount; + + // Pointer to an array of filter descriptor pointers. + // Typically it's one sensor for each filter factory. + CSensorContext *m_Context; + + // The sensor state for each camera. + CSensor **m_Sensor; + + // + // 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; + + // + // 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; + } + + // + // Prepare(): + // + // Called by CCaptureDevice::DispatchCreate to finish AddDevice processing, + // once the object is created. + // + // This is a seperate helper function to allow the derived DispatchCreate + // implementation to be as simple as is feasible. + // + NTSTATUS + Prepare(); + +public: + // + // 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 + ); + + // + // DispatchStart(): + // + // This is the Pnp Start dispatch for the capture device. It simply + // bridges to PnpStart() in the context of the CCaptureDevice. + // + static + NTSTATUS + DispatchStart ( + _In_ PKSDEVICE Device, + _In_ PIRP Irp, + _In_ PCM_RESOURCE_LIST TranslatedResourceList, + _In_ PCM_RESOURCE_LIST UntranslatedResourceList + ); + + // + // DispatchStop(): + // + // This is the Pnp stop dispatch for the capture device. It simply + // bridges to PnpStop() in the context of the CCaptureDevice. + // + static + void + DispatchStop( + _In_ PKSDEVICE Device, + _In_ PIRP Irp + ); + + static + NTSTATUS + DispatchPostStart( + _In_ PKSDEVICE Device + ); + + static + NTSTATUS + DispatchQueryStop( + _In_ PKSDEVICE Device, + _In_ PIRP Irp + ); + + static + void + DispatchCancelStop( + _In_ PKSDEVICE Device, + _In_ PIRP Irp + ); + + static + NTSTATUS + DispatchQueryRemove( + _In_ PKSDEVICE Device, + _In_ PIRP Irp + ); + + static + void + DispatchCancelRemove( + _In_ PKSDEVICE Device, + _In_ PIRP Irp + ); + + static + void + DispatchRemove( + _In_ PKSDEVICE Device, + _In_ PIRP Irp + ); + + static + void + DispatchSurpriseRemoval( + _In_ PKSDEVICE Device, + _In_ PIRP Irp + ); + + static + NTSTATUS + DispatchQueryCapabilities( + _In_ PKSDEVICE Device, + _In_ PIRP Irp, + _Inout_ PDEVICE_CAPABILITIES Capabilities + ); + + static + NTSTATUS + DispatchQueryPower( + _In_ PKSDEVICE Device, + _In_ PIRP Irp, + _In_ DEVICE_POWER_STATE DeviceTo, + _In_ DEVICE_POWER_STATE DeviceFrom, + _In_ SYSTEM_POWER_STATE SystemTo, + _In_ SYSTEM_POWER_STATE SystemFrom, + _In_ POWER_ACTION Action + ); + + static + void + DispatchSetPower( + _In_ PKSDEVICE Device, + _In_ PIRP Irp, + _In_ DEVICE_POWER_STATE To, + _In_ DEVICE_POWER_STATE From + ); + + static + NTSTATUS + DispatchQueryInterface( + _In_ PKSDEVICE Device, + _In_ PIRP Irp + ); + +protected: + // + // PnpStart(): + // + // This is the Pnp start routine for our simulated hardware. Note that + // DispatchStart bridges to here in the context of the CCaptureDevice. + // + virtual + 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. + // + virtual + void + PnpStop ( + _In_ PIRP Irp + ); + + virtual + NTSTATUS + PnpPostStart(); + + virtual + NTSTATUS + PnpQueryStop( + _In_ PIRP Irp + ); + + virtual + void + PnpCancelStop( + _In_ PIRP Irp + ); + + virtual + NTSTATUS + PnpQueryRemove( + _In_ PIRP Irp + ); + + virtual + void + PnpCancelRemove( + _In_ PIRP Irp + ); + + virtual + void + PnpRemove( + _In_ PIRP Irp + ); + + virtual + void + PnpSurpriseRemoval( + _In_ PIRP Irp + ); + + virtual + NTSTATUS + PnpQueryCapabilities( + _In_ PIRP Irp, + _Inout_ PDEVICE_CAPABILITIES Capabilities + ); + + virtual + NTSTATUS + PnpQueryPower( + _In_ PIRP Irp, + _In_ DEVICE_POWER_STATE DeviceTo, + _In_ DEVICE_POWER_STATE DeviceFrom, + _In_ SYSTEM_POWER_STATE SystemTo, + _In_ SYSTEM_POWER_STATE SystemFrom, + _In_ POWER_ACTION Action + ); + + virtual + void + PnpSetPower( + _In_ PIRP Irp, + _In_ DEVICE_POWER_STATE To, + _In_ DEVICE_POWER_STATE From + ); + + virtual + NTSTATUS + PnpQueryInterface( + _In_ PIRP Irp + ); + +protected: + virtual + CSensor * + CreateSensor( + _In_ const KSFILTER_DESCRIPTOR *Descriptors + ); + +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 + ); + + // + // ~CCaptureDevice(): + // + // The capture device destructor. + // + virtual + ~CCaptureDevice(); + + virtual + NTSTATUS + Initialize(); + + // + // Recast(): + // + // Helper function. Used to convert from a PKSDEVICE to a CCaptureDevice * + // + static + inline + CCaptureDevice * + Recast( + _In_ PKSDEVICE Device + ) + { + return reinterpret_cast (Device -> Context); + } + + // + // QueryInterruptTime(): + // + // Determine the frame number that this frame corresponds to. + // + ULONG + QueryInterruptTime (LONG ID, + _In_ PKSPIN Pin + ); + + virtual + PDEVICE_OBJECT + GetDeviceObject(); + + PKSDEVICE + GetKsDevice() + { + return m_Device; + } + + ULONG + GetFilterIndex(PKSFILTER Filter); + + ULONG + GetFilterCount() + { + return (ULONG) m_FilterDescriptorCount; + } + + PCWSTR + GetFilterName(ULONG FilterIndex) + { + return (FilterIndex < m_FilterDescriptorCount) ? m_Context[FilterIndex].Name : nullptr; + } + + CSensor * + GetSensor( PKSFILTER Filter ) + { + ULONG Index = GetFilterIndex(Filter); + + return ( Index < m_FilterDescriptorCount ) ? m_Sensor[ Index ] : nullptr; + } + +}; + +// +// Define a dispatch table for a capture pin. It provides notifications +// about creation, closure, processing, data formats, etc... +// +#define DEFINE_CAMERA_KSDEVICE_DISPATCH( Table, Class ) \ +const \ +KSDEVICE_DISPATCH \ +Table = \ +{ \ + Class::DispatchCreate, /* Pnp Add Device */ \ + Class::DispatchStart, /* Pnp Start */ \ + Class::DispatchPostStart, /* Post-Start */ \ + Class::DispatchQueryStop, /* Pnp Query Stop */ \ + Class::DispatchCancelStop, /* Pnp Cancel Stop */ \ + Class::DispatchStop, /* Pnp Stop */ \ + Class::DispatchQueryRemove, /* Pnp Query Remove */ \ + Class::DispatchCancelRemove, /* Pnp Cancel Remove */ \ + Class::DispatchRemove, /* Pnp Remove */ \ + Class::DispatchQueryCapabilities, /* Pnp Query Capabilities */ \ + Class::DispatchSurpriseRemoval, /* Pnp Surprise Removal */ \ + Class::DispatchQueryPower, /* Power Query Power */ \ + Class::DispatchSetPower, /* Power Set Power */ \ + Class::DispatchQueryInterface /* Pnp Query Interface */ \ +}; + diff --git a/avscamera/sys/ExtendedCameraAngleOffset.h b/avscamera/sys/ExtendedCameraAngleOffset.h new file mode 100644 index 000000000..dca18ee17 --- /dev/null +++ b/avscamera/sys/ExtendedCameraAngleOffset.h @@ -0,0 +1,67 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + ExtendedCameraAngleOffset.h + + Abstract: + + This file contains the helper class CExtendedCameraAngleOffset. + The class is provides initialization and accessor functions for + KSCAMERA_EXTENDEDPROP_CAMERAOFFSET. + + History: + + created 11/26/2014 + +**************************************************************************/ + +// +// Helper class for Extended Camera Angle Offset. +// +class CExtendedCameraAngleOffset + : public CExtendedHeader +{ +public: + KSCAMERA_EXTENDEDPROP_CAMERAOFFSET m_Params; + +public: + // Standardized ctors to help ensure a consistant definition. + CExtendedCameraAngleOffset( ULONGLONG flags=0, ULONG result=STATUS_SUCCESS ) + : CExtendedHeader( flags, result ) + { + Size = sizeof(*this); + RtlZeroMemory( &m_Params, sizeof(m_Params) ); + } + + CExtendedCameraAngleOffset( KSCAMERA_EXTENDEDPROP_HEADER &hdr ) + : CExtendedHeader(hdr) + { + Size = sizeof(*this); + RtlZeroMemory( &m_Params, sizeof(m_Params) ); + } + + // Access functions let you more easily fetch the embedded field. + LONG & + PitchAngle() + { + return m_Params.PitchAngle; + } + + LONG & + YawAngle() + { + return m_Params.YawAngle; + } + + bool isValid() + { + return CExtendedHeader::isValid() && + (Size >= sizeof(*this)) ; + } +}; + diff --git a/avscamera/sys/ExtendedEVComp.h b/avscamera/sys/ExtendedEVComp.h new file mode 100644 index 000000000..31f7916be --- /dev/null +++ b/avscamera/sys/ExtendedEVComp.h @@ -0,0 +1,105 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + ExtendedEvComp.h + + Abstract: + + This file contains the helper class CExtendedEvCompensation. + The class is provides initialization and accessor functions for + KSCAMERA_EXTENDEDPROP_EVCOMPENSATION. + + History: + + created 6/3/2014 + +**************************************************************************/ + +// +// Helper class for Extended Properties. +// +class CExtendedEvCompensation + : public CExtendedHeader +{ +public: + KSCAMERA_EXTENDEDPROP_EVCOMPENSATION m_EvComp; + +public: + // Standardized ctors to help ensure a consistant definition. + CExtendedEvCompensation( ULONGLONG flags=0, ULONG result=STATUS_SUCCESS ) + :CExtendedHeader( flags, result ) + { + Size = sizeof(*this); + RtlZeroMemory( &m_EvComp, sizeof(m_EvComp) ); + } + + CExtendedEvCompensation( KSCAMERA_EXTENDEDPROP_HEADER &hdr ) + : CExtendedHeader(hdr) + { + Size = sizeof(*this); + RtlZeroMemory( &m_EvComp, sizeof(m_EvComp) ); + } + + bool isValid() + { + return CExtendedHeader::isValid() && + (Size >= sizeof(*this)) ; + } + + // Access operators let you fetch the value from the value field. + operator LONG() + { + return m_EvComp.Value; + } + + // Assignment operators let you assign a value to the property. + CExtendedEvCompensation & + operator =( LONG x ) + { + Value() = x; + return *this; + } + + CExtendedEvCompensation & + operator =( const KSCAMERA_EXTENDEDPROP_EVCOMPENSATION &x ) + { + m_EvComp = x; + return *this; + } + + LONG & + Min() + { + return m_EvComp.Min; + } + + LONG & + Max() + { + return m_EvComp.Max; + } + + ULONG & + Mode() + { + return m_EvComp.Mode; + } + + LONG & + Value() + { + return m_EvComp.Value; + } + + LONG & + GetLONG() + { + return m_EvComp.Value; + } +}; + diff --git a/avscamera/sys/ExtendedFieldOfView.h b/avscamera/sys/ExtendedFieldOfView.h new file mode 100644 index 000000000..388356a6b --- /dev/null +++ b/avscamera/sys/ExtendedFieldOfView.h @@ -0,0 +1,67 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + ExtendedFieldOfView.h + + Abstract: + + This file contains the helper class CExtendedFieldOfView. + The class is provides initialization and accessor functions for + KSCAMERA_EXTENDEDPROP_FIELDOFVIEW. + + History: + + created 11/26/2014 + +**************************************************************************/ + +// +// Helper class for Extended Field of View. +// +class CExtendedFieldOfView + : public CExtendedHeader +{ +public: + KSCAMERA_EXTENDEDPROP_FIELDOFVIEW m_Params; + +public: + // Standardized ctors to help ensure a consistant definition. + CExtendedFieldOfView( ULONGLONG flags=0, ULONG result=STATUS_SUCCESS ) + : CExtendedHeader( flags, result ) + { + Size = sizeof(*this); + RtlZeroMemory( &m_Params, sizeof(m_Params) ); + } + + CExtendedFieldOfView( KSCAMERA_EXTENDEDPROP_HEADER &hdr ) + : CExtendedHeader(hdr) + { + Size = sizeof(*this); + RtlZeroMemory( &m_Params, sizeof(m_Params) ); + } + + // Access functions let you more easily fetch the embedded field. + ULONG & + NormalizedFocalLengthX() + { + return m_Params.NormalizedFocalLengthX; + } + + ULONG & + NormalizedFocalLengthY() + { + return m_Params.NormalizedFocalLengthY; + } + + bool isValid() + { + return CExtendedHeader::isValid() && + (Size >= sizeof(*this)) ; + } +}; + diff --git a/avscamera/sys/ExtendedMaxVideoFpsForPhotoRes.h b/avscamera/sys/ExtendedMaxVideoFpsForPhotoRes.h new file mode 100644 index 000000000..29013c82b --- /dev/null +++ b/avscamera/sys/ExtendedMaxVideoFpsForPhotoRes.h @@ -0,0 +1,91 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + ExtendedMaxVideoFpsForPhotoRes.h + + Abstract: + + This file contains the helper class CExtendedMaxVideoFpsForPhotoRes. + The class is provides initialization and accessor functions for + KSCAMERA_MAXVIDEOFPS_FORPHOTORES. + + History: + + created 11/26/2014 + +**************************************************************************/ + +// +// Helper class for Extended Max Video Fps for Photo Resolution. +// +class CExtendedMaxVideoFpsForPhotoRes + : public CExtendedHeader +{ +public: + KSCAMERA_MAXVIDEOFPS_FORPHOTORES m_Params; + +public: + // Standardized ctors to help ensure a consistant definition. + CExtendedMaxVideoFpsForPhotoRes( ULONGLONG flags=0, ULONG result=STATUS_SUCCESS ) + : CExtendedHeader( flags, result ) + { + Size = sizeof(*this); + RtlZeroMemory( &m_Params, sizeof(m_Params) ); + } + + CExtendedMaxVideoFpsForPhotoRes( KSCAMERA_EXTENDEDPROP_HEADER &hdr ) + : CExtendedHeader(hdr) + { + Size = sizeof(*this); + RtlZeroMemory( &m_Params, sizeof(m_Params) ); + } + + // Access functions let you more easily fetch the embedded field. + ULONG & + PhotoResWidth() + { + return m_Params.PhotoResWidth; + } + + ULONG & + PhotoResHeight() + { + return m_Params.PhotoResHeight; + } + + ULONG & + PreviewFPSNum() + { + return m_Params.PreviewFPSNum; + } + + ULONG & + PreviewFPSDenom() + { + return m_Params.PreviewFPSDenom; + } + + ULONG & + CaptureFPSNum() + { + return m_Params.CaptureFPSNum; + } + + ULONG & + CaptureFPSDenom() + { + return m_Params.CaptureFPSDenom; + } + + bool isValid() + { + return CExtendedHeader::isValid() && + (Size >= sizeof(*this)) ; + } +}; + diff --git a/avscamera/sys/ExtendedMetadata.h b/avscamera/sys/ExtendedMetadata.h new file mode 100644 index 000000000..27ef4a797 --- /dev/null +++ b/avscamera/sys/ExtendedMetadata.h @@ -0,0 +1,68 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + ExtendedMetadata.h + + Abstract: + + This file contains the helper class CExtendedMetadata. + The class is provides initialization and accessor functions for + KSCAMERA_EXTENDEDPROP_METADATAINFO. + + History: + + created 6/20/2014 + +**************************************************************************/ + +// +// Helper class for Extended Metadata. +// +class CExtendedMetadata + : public CExtendedHeader +{ +public: + KSCAMERA_EXTENDEDPROP_METADATAINFO m_Info; + + // Maximum metadata required ... except for Histogram + static const ULONG METADATA_MAX=4096; + + // Standardized ctors to help ensure a consistant definition. + CExtendedMetadata( + _In_ ULONG pinId=0, // Just so we have a default ctor. + _In_ ULONG size=METADATA_MAX, + _In_ ULONG align=KSCAMERA_EXTENDEDPROP_MetadataAlignment_32, + _In_ ULONGLONG flags=(KSCAMERA_EXTENDEDPROP_METADATA_ALIGNMENTREQUIRED | KSCAMERA_EXTENDEDPROP_METADATA_SYSTEMMEMORY), + _In_ ULONG result=STATUS_SUCCESS + ) + : CExtendedHeader( flags, result ) + { + PinId = pinId; + Size = sizeof(*this); + m_Info.BufferAlignment = align; + m_Info.MaxMetadataBufferSize= size; + } + + // Used to modify the metadata allocation size. + CExtendedMetadata & + operator =( ULONG x ) + { + m_Info.MaxMetadataBufferSize = x; + return *this; + } + + bool isValid() + { + return CExtendedHeader::isValid() && + (Size == sizeof(*this)) && + ((Flags & ~(KSCAMERA_EXTENDEDPROP_METADATA_SYSTEMMEMORY | KSCAMERA_EXTENDEDPROP_METADATA_ALIGNMENTREQUIRED)) == 0) && // Only valid flags + ((m_Info.BufferAlignment-1) & m_Info.BufferAlignment); // Not more than 1 bit set. + } + +}; + diff --git a/avscamera/sys/ExtendedPhotoMode.h b/avscamera/sys/ExtendedPhotoMode.h new file mode 100644 index 000000000..daa7c1271 --- /dev/null +++ b/avscamera/sys/ExtendedPhotoMode.h @@ -0,0 +1,73 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + ExtendedPhotoMode.h + + Abstract: + + This file contains the helper class CExtendedPhotoMode. + The class is provides initialization and accessor functions for + KSCAMERA_EXTENDEDPROP_PHOTOMODE. + + History: + + created 11/20/2014 + +**************************************************************************/ + +// +// Helper class for Extended Photo Mode. +// +class CExtendedPhotoMode + : public CExtendedHeader +{ +public: + KSCAMERA_EXTENDEDPROP_PHOTOMODE m_PhotoMode; + +public: + // Standardized ctors to help ensure a consistant definition. + CExtendedPhotoMode( ULONGLONG flags=0, ULONG result=STATUS_SUCCESS ) + : CExtendedHeader( flags, result ) + { + Size = sizeof(*this); + RtlZeroMemory( &m_PhotoMode, sizeof(m_PhotoMode) ); + } + + CExtendedPhotoMode( KSCAMERA_EXTENDEDPROP_HEADER &hdr ) + : CExtendedHeader(hdr) + { + Size = sizeof(*this); + RtlZeroMemory( &m_PhotoMode, sizeof(m_PhotoMode) ); + } + + // Access functions let you more easily fetch the embedded field. + ULONG & + RequestedHistoryFrames() + { + return m_PhotoMode.RequestedHistoryFrames; + } + + ULONG & + MaxHistoryFrames() + { + return m_PhotoMode.MaxHistoryFrames; + } + + ULONG & + SubMode() + { + return m_PhotoMode.SubMode; + } + + bool isValid() + { + return CExtendedHeader::isValid() && + (Size >= sizeof(*this)) ; + } +}; + diff --git a/avscamera/sys/ExtendedProperty.h b/avscamera/sys/ExtendedProperty.h new file mode 100644 index 000000000..32affaa61 --- /dev/null +++ b/avscamera/sys/ExtendedProperty.h @@ -0,0 +1,142 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2013, Microsoft Corporation. + + File: + + ExtendedProperty.h + + Abstract: + + This file contains the helper class CExtendedProperty and the coommon + base class CExtendedHeader. + + CExtendedHeader provides initialization and accessor functions for + KSCAMERA_EXTENDEDPROP_HEADER. + + CExtendedProperty provides initialization and accessor functions for + KSCAMERA_EXTENDEDPROP_VALUE. + + CExtendedProperty also combines KSCAMERA_EXTENDEDPROP_HEADER and + KSCAMERA_EXTENDEDPROP_VALUE into a single object for use in the + macros and templates used by our filter and sensor classes. + + History: + + created 5/21/2013 + +**************************************************************************/ + +// +// Helper class for Extended Properties. +// +class CExtendedHeader + : public KSCAMERA_EXTENDEDPROP_HEADER +{ +public: + // Standardized ctors to help ensure a consistant definition. + CExtendedHeader( ULONGLONG flags=0, ULONG result=STATUS_SUCCESS ) + { + Version = KSCAMERA_EXTENDEDPROP_VERSION; + PinId = KSCAMERA_EXTENDEDPROP_FILTERSCOPE; + Size = sizeof(*this); + Result = result; + Capability = 0; + Flags = flags; + } + + CExtendedHeader( KSCAMERA_EXTENDEDPROP_HEADER &hdr ) + : KSCAMERA_EXTENDEDPROP_HEADER(hdr) + { + Size = sizeof(*this); + } + + bool isValid() + { + return Version == KSCAMERA_EXTENDEDPROP_VERSION; + } +}; + +class CExtendedProperty + : public CExtendedHeader +{ +public: + KSCAMERA_EXTENDEDPROP_VALUE m_Value; + +public: + // Standardized ctors to help ensure a consistant definition. + CExtendedProperty( ULONGLONG flags=0, ULONG result=STATUS_SUCCESS ) + : CExtendedHeader( flags, result ) + { + Size = sizeof(*this); + m_Value.Value.ull = 0; + } + + CExtendedProperty( KSCAMERA_EXTENDEDPROP_HEADER &hdr ) + : CExtendedHeader(hdr) + { + Size = sizeof(*this); + m_Value.Value.ull = 0; + } + + // Access operators let you fetch the value from the value field. + operator LONG() + { + return m_Value.Value.l; + } + operator ULONG() + { + return m_Value.Value.ul; + } + operator LONGLONG() + { + return m_Value.Value.ll; + } + operator ULONGLONG() + { + return m_Value.Value.ull; + } + operator KSCAMERA_EXTENDEDPROP_VALUE() + { + return m_Value; + } + + // Assignment operators let you assign a value to the property. + CExtendedProperty & + operator =( LONG x ) + { + m_Value.Value.l = x; + return *this; + } + + CExtendedProperty & + operator =( LONGLONG x ) + { + m_Value.Value.ll = x; + return *this; + } + + CExtendedProperty & + operator =( ULONG x ) + { + m_Value.Value.ul = x; + return *this; + } + + CExtendedProperty & + operator =( ULONGLONG x ) + { + m_Value.Value.ull = x; + return *this; + } + + bool isValid() + { + return CExtendedHeader::isValid() && + (Size >= sizeof(*this)) ; + } + +}; + diff --git a/avscamera/sys/ExtendedVidProcSetting.h b/avscamera/sys/ExtendedVidProcSetting.h new file mode 100644 index 000000000..11c22caa3 --- /dev/null +++ b/avscamera/sys/ExtendedVidProcSetting.h @@ -0,0 +1,164 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2013, Microsoft Corporation. + + File: + + ExtendedProperty.h + + Abstract: + + This file contains the helper class CExtendedVidProcSetting. + The class is provides initialization and accessor functions for + KSCAMERA_EXTENDEDPROP_VIDEOPROCSETTING. + + History: + + created 5/21/2013 + +**************************************************************************/ + +// +// Helper class for Extended Properties KSCAMERA_EXTENDEDPROP_VIDEOPROCSETTING. +// +class CExtendedVidProcSetting + : public CExtendedHeader +{ +public: + KSCAMERA_EXTENDEDPROP_VIDEOPROCSETTING m_Setting; + +public: + // Standardized ctors to help ensure a consistant definition. + CExtendedVidProcSetting( ULONGLONG flags=0, ULONG result=STATUS_SUCCESS ) + : CExtendedHeader( flags, result ) + { + Size = sizeof(*this); + RtlZeroMemory( &m_Setting, sizeof(m_Setting) ); + } + + CExtendedVidProcSetting( KSCAMERA_EXTENDEDPROP_HEADER &hdr ) + : CExtendedHeader(hdr) + { + Size = sizeof(*this); + RtlZeroMemory( &m_Setting, sizeof(m_Setting) ); + } + + bool isValid() + { + return CExtendedHeader::isValid() && + (Size >= sizeof(*this)) ; + } + + template + NTSTATUS + BoundsCheck( T Value ) + { + return ::BoundsCheck( Value, m_Setting ); + } + + // Access operators let you fetch the value from the value field. + operator LONG() + { + return m_Setting.VideoProc.Value.l; + } + operator ULONG() + { + return m_Setting.VideoProc.Value.ul; + } + operator LONGLONG() + { + return m_Setting.VideoProc.Value.ll; + } + operator ULONGLONG() + { + return m_Setting.VideoProc.Value.ull; + } + + // Assignment operators let you assign a value to the property. + CExtendedVidProcSetting & + operator =( LONG x ) + { + m_Setting.VideoProc.Value.l = x; + return *this; + } + + CExtendedVidProcSetting & + operator =( LONGLONG x ) + { + m_Setting.VideoProc.Value.ll = x; + return *this; + } + + CExtendedVidProcSetting & + operator =( ULONG x ) + { + m_Setting.VideoProc.Value.ul = x; + return *this; + } + + CExtendedVidProcSetting & + operator =( ULONGLONG x ) + { + m_Setting.VideoProc.Value.ull = x; + return *this; + } + + CExtendedVidProcSetting & + operator =( const KSCAMERA_EXTENDEDPROP_VIDEOPROCSETTING &x ) + { + m_Setting = x; + return *this; + } + + LONG & + Min() + { + return m_Setting.Min; + } + + LONG & + Max() + { + return m_Setting.Max; + } + + LONG & + Step() + { + return m_Setting.Step; + } + + ULONG & + Mode() + { + return m_Setting.Mode; + } + + LONG & + GetLONG() + { + return m_Setting.VideoProc.Value.l; + } + + LONGLONG & + GetLONGLONG() + { + return m_Setting.VideoProc.Value.ll; + } + + ULONG & + GetULONG() + { + return m_Setting.VideoProc.Value.ul; + } + + ULONGLONG & + GetULONGLONG() + { + return m_Setting.VideoProc.Value.ull; + } + +}; + diff --git a/avscamera/sys/Mutex.cpp b/avscamera/sys/Mutex.cpp new file mode 100644 index 000000000..1dd3d88cf --- /dev/null +++ b/avscamera/sys/Mutex.cpp @@ -0,0 +1,70 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2013, Microsoft Corporation. + + File: + + Mutex.cpp + + Abstract: + + This file provides the implementation of KMutex and KScopedMutex. + + KMutex provides a wrapper around a KMUTEX object. KScopedMutex is + provided as a helper class to ensure all exits release the lock. + + History: + + created 7/16/2013 + +**************************************************************************/ + +#include "Common.h" + +// +// Dummy dtor. Required because the dtor is virtual. +// +KMutex::~KMutex() +{} + +// +// Preferred lock method. +// +_IRQL_requires_min_(PASSIVE_LEVEL) +_When_((Timeout==NULL || Timeout->QuadPart!=0), _IRQL_requires_max_(APC_LEVEL)) +_When_((Timeout!=NULL &&Timeout->QuadPart==0), _IRQL_requires_max_(DISPATCH_LEVEL)) +NTSTATUS +KMutex::Lock( + _In_opt_ PLARGE_INTEGER Timeout +) +{ + //DBG_ENTER(": Thread Owner = %p", m_Lock.OwnerThread); + + NTSTATUS status = + KeWaitForSingleObject( + &m_Lock, + Executive, + KernelMode, + FALSE, + Timeout ); + + //DBG_LEAVE(": Status = %d Thread Owner = %p", status, m_Lock.OwnerThread); + + return status; +} + +// +// Preferred unlock method. +// +_IRQL_requires_max_(DISPATCH_LEVEL) +void +KMutex::Unlock() +{ + //DBG_ENTER(": Thread Owner = %p", m_Lock.OwnerThread); + + KeReleaseMutex( &m_Lock, FALSE ); + + //DBG_LEAVE(": Thread Owner = %p", m_Lock.OwnerThread); +} diff --git a/avscamera/sys/Mutex.h b/avscamera/sys/Mutex.h new file mode 100644 index 000000000..a8157da53 --- /dev/null +++ b/avscamera/sys/Mutex.h @@ -0,0 +1,81 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2013, Microsoft Corporation. + + File: + + Mutex.h + + Abstract: + + This file provides the declaration of KMutex and KScopedMutex. + + KMutex provides a wrapper around a KMUTEX object. KScopedMutex is + provided as a helper class to ensure all exits release the lock. + + History: + + created 7/16/2013 + +**************************************************************************/ +#pragma once + +// +// Specialized class for KMUTEX +// +class KMutex : CNonCopyable +{ + KMUTEX m_Lock; + +public: + KMutex() + { + KeInitializeMutex( &m_Lock, 0 ); + } + + virtual ~KMutex(); + + // + // Preferred lock method. + // + _IRQL_requires_min_(PASSIVE_LEVEL) + _When_((Timeout==NULL || Timeout->QuadPart!=0), _IRQL_requires_max_(APC_LEVEL)) + _When_((Timeout!=NULL &&Timeout->QuadPart==0), _IRQL_requires_max_(DISPATCH_LEVEL)) + NTSTATUS + Lock( + _In_opt_ PLARGE_INTEGER Timeout=nullptr + ); + + // + // Preferred unlock method. + // + _IRQL_requires_max_(DISPATCH_LEVEL) + void + Unlock(); +}; + +// +// Use this class to hold a spinlock through a particular scope +// +class KScopedMutex : CNonCopyable +{ + KMutex &m_Lock; + +public: + _IRQL_requires_min_(PASSIVE_LEVEL) + _IRQL_requires_max_(APC_LEVEL) + KScopedMutex( KMutex &lock ) + : m_Lock(lock) + { + m_Lock.Lock(); + } + + _IRQL_requires_max_(DISPATCH_LEVEL) + ~KScopedMutex(void) + { + m_Lock.Unlock(); + } +}; + diff --git a/avscamera/sys/NV12Synthesizer.cpp b/avscamera/sys/NV12Synthesizer.cpp new file mode 100644 index 000000000..2cdbf71b6 --- /dev/null +++ b/avscamera/sys/NV12Synthesizer.cpp @@ -0,0 +1,183 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2001, Microsoft Corporation. + + File: + + CNV12Synthesizer.h + + Abstract: + + This file contains the implementation of CNV12Synthesizer. + + CNV12Synthesizer is derived from CYUVSynthesizer. It uses the YUV color + space and defines a Commit() function that reformats pixels into the + NV12 format. From MSDN: + + A format in which all Y samples are found first in memory as an + array of unsigned char with an even number of lines (possibly with + a larger stride for memory alignment). This is followed immediately + by an array of unsigned char containing interleaved Cb and Cr + samples. If these samples are addressed as a little-endian WORD + type, Cb would be in the least significant bits and Cr would be in + the most significant bits with the same total stride as the Y + samples. NV12 is the preferred 4:2:0 pixel format. + + A visual representation of the layout: + YYYY + YYYY + UVUV + + + History: + created 4/14/2013 + +**************************************************************************/ + +#include "Common.h" + +/************************************************************************** + + LOCKED CODE + +**************************************************************************/ + +#ifdef ALLOC_PRAGMA +#pragma code_seg() +#endif // ALLOC_PRAGMA + + +// suppressed due to Esp:773 +#pragma warning (push) +#pragma warning( disable:26015 ) // Suppress OACR error. Seems nonsensical. TODO: Must revisit. +#pragma warning( disable:26019 ) +_Success_(return > 0) +ULONG +CNV12Synthesizer:: +Commit( + _Out_writes_bytes_(Size) + PUCHAR Buffer, + _In_ ULONG Size, + _In_ ULONG Stride +) +/*++ + +Routine Description: + + Copy (and reformat, if necessary) pixels from the internal scratch + buffer. If the output format decimates chrominance, do it here. + +Arguments: + + Buffer - + The output buffer to fill. + + Size - + The size of the output buffer in bytes. + + Stride - + The length of a row in bytes. + +Return Value: + + Number of bytes copied into Buffer. + +--*/ +{ + // We assume the output stride is the same as the original width here. + UNREFERENCED_PARAMETER(Stride); + + // The code actually handles this gracefully. It rounds down. + NT_ASSERT( (m_Width&1) == 0 ); + NT_ASSERT( (m_Height&1) == 0 ); + + // These are impossible conditions. + if( !( Buffer && + m_Buffer && + (Size%6)==0 && // Should be evenly divisible by 1 macropixel + Size>=6 // At least 1 macropixel + ) ) + { + NT_ASSERT(FALSE); + return 0; + } + + ULONG MacroPixelsWide = m_Width/2; + + // Impossible. + if( MacroPixelsWide==0 ) + { + NT_ASSERT(FALSE); + return 0; + } + + // At most, 2/3rds of the available space is used by the Y plane. + // Notice that we limit the number of macro pixel rows to what will + // fit in the space available, no matter what is in the original. + ULONG Y_limit = (Size /3) * 2; + ULONG MacroPixelsHigh = min( m_Height, Y_limit/m_Width)/2; + + // Impossible. + if( MacroPixelsHigh==0 ) + { + NT_ASSERT(FALSE); + return 0; + } + + // Now we work back to arrive at the Y & UV plane sizes. + ULONG UV_size = MacroPixelsWide * MacroPixelsHigh *2; + ULONG Y_size = MacroPixelsWide * MacroPixelsHigh *4; + ULONG YUV_size = Y_size + UV_size; + + // Impossible. + if( Size < YUV_size ) + { + NT_ASSERT(FALSE); + return 0; + } + + PWORD pY = (PWORD) Buffer; + PWORD pUV = (PWORD) (Buffer + Y_size); + + for(ULONG row = 0; row < MacroPixelsHigh; row++) + { + PKS_RGBQUAD pSrc = (PKS_RGBQUAD) GetImageLocation(0, row*2); + for(ULONG col = 0; col < MacroPixelsWide; col++) + { + KS_RGBQUAD TL = pSrc[0]; + KS_RGBQUAD TR = pSrc[1]; + KS_RGBQUAD BL = pSrc[m_Width+0]; + KS_RGBQUAD BR = pSrc[m_Width+1]; + pSrc += 2; + + // Copy luma first + pY[0] = MAKEWORD(TL.rgbGreen, TR.rgbGreen); + pY[MacroPixelsWide] = MAKEWORD(BL.rgbGreen, BR.rgbGreen); + pY++; + + LONG + tU = TL.rgbBlue; //top left + tU += BL.rgbBlue; //bottom left + tU += TR.rgbBlue; //top right + tU += BR.rgbBlue; //bottom right + + LONG + tV = TL.rgbRed; //top left + tV += BL.rgbRed; //bottom left + tV += TR.rgbRed; //top right + tV += BR.rgbRed; //bottom right + + // Copy decimated chroma + *pUV++ = MAKEWORD(((tU+2)>>2), ((tV+2)>>2)); + } + // Skip a scan line, since we've already copied it. + pY += MacroPixelsWide; + } + + return (ULONG) (((PUCHAR) pUV) - Buffer); +} +// suppressed due to Esp:773 +#pragma warning (pop) + diff --git a/avscamera/sys/NV12Synthesizer.h b/avscamera/sys/NV12Synthesizer.h new file mode 100644 index 000000000..27bc6ccb8 --- /dev/null +++ b/avscamera/sys/NV12Synthesizer.h @@ -0,0 +1,73 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2001, Microsoft Corporation. + + File: + + CNV12Synthesizer.h + + Abstract: + + This file defines a class that implements NV12 image synthesis. + + CNV12Synthesis is derived from CYUVSynthesis. It uses the YUV color + space and defines a Commit() function that reformats pixels into the + NV12 format. From MSDN: + + A format in which all Y samples are found first in memory as an + array of unsigned char with an even number of lines (possibly with + a larger stride for memory alignment). This is followed immediately + by an array of unsigned char containing interleaved Cb and Cr + samples. If these samples are addressed as a little-endian WORD + type, Cb would be in the least significant bits and Cr would be in + the most significant bits with the same total stride as the Y + samples. NV12 is the preferred 4:2:0 pixel format. + + A visual representation of the layout: + YYYY + YYYY + UVUV + + + History: + created 4/16/2013 + +**************************************************************************/ + +#pragma once + +class CNV12Synthesizer : + public CYUVSynthesizer +{ +public: + // + // DEFAULT CONSTRUCTOR + // + CNV12Synthesizer ( + ULONG Width=0, + ULONG Height=0 + ) : + CYUVSynthesizer("NV12", Width, Height) + { + NT_ASSERT( ( Width % 2 ) == 0 ); + NT_ASSERT( ( Height % 2 ) == 0 ); + } + + // + // Commit + // + // Copy (and reformat, if necessary) pixels from the internal scratch + // buffer. If the output format decimates chrominance, do it here. + // + virtual + _Success_(return > 0) + ULONG + Commit( + _Out_writes_bytes_(Size) + PUCHAR Buffer, + _In_ ULONG Size, + _In_ ULONG Stride + ); +}; \ No newline at end of file diff --git a/avscamera/sys/NonCopyable.h b/avscamera/sys/NonCopyable.h new file mode 100644 index 000000000..98d7bad4a --- /dev/null +++ b/avscamera/sys/NonCopyable.h @@ -0,0 +1,48 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2013, Microsoft Corporation. + + File: + + NonCopyable.h + + Abstract: + + Simple base class that hides the copy ctor and assignment operators. + + Derive from this class any time its not safe (or reasonable) to + permit copies of an object. + + History: + + created 5/8/2013 + +**************************************************************************/ +#pragma once + +// +// General base class for noncopyable objects +// +class CNonCopyable +{ +protected: + CNonCopyable() {} + ~CNonCopyable() {} + +private: + CNonCopyable( + _In_ const CNonCopyable & + ) + {} + + const CNonCopyable & + operator =( + _In_ const CNonCopyable & + ) + { + return *this; + } +}; + diff --git a/avscamera/sys/PreviewHwSim.cpp b/avscamera/sys/PreviewHwSim.cpp new file mode 100644 index 000000000..dfe2dc1a2 --- /dev/null +++ b/avscamera/sys/PreviewHwSim.cpp @@ -0,0 +1,263 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + PreviewHwSim.cpp + + Abstract: + + This file contains the implementation of the CPreviewHardwareSimulation + class. + + This is a specialization of CHardwareSimulation that provides preview- + specific metadata. + + History: + + created 5/28/2014 + +**************************************************************************/ + +#include "Common.h" + +/************************************************************************** + + PAGEABLE CODE + +**************************************************************************/ + +#ifdef ALLOC_PRAGMA +#pragma code_seg("PAGE") +#endif // ALLOC_PRAGMA + + +CPreviewHardwareSimulation::CPreviewHardwareSimulation( + _Inout_ CSensor *Sensor, + _In_ LONG PinID +) + : CHardwareSimulation( Sensor, PinID ) +{ + PAGED_CODE(); +} + + +CPreviewHardwareSimulation::~CPreviewHardwareSimulation() +{ + PAGED_CODE(); +} + +void +CPreviewHardwareSimulation:: +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 + +--*/ +{ + PAGED_CODE(); + + // Do normal timer handling. + CHardwareSimulation::FakeHardware(); + + // Update the zoom. + m_Sensor->UpdateZoom(); +} + +// +// Helper function that collects the current ISP settings into our metadata +// structure. +// +METADATA_PREVIEWAGGREGATION +CPreviewHardwareSimulation:: +GetMetadata() +{ + PAGED_CODE(); + + METADATA_PREVIEWAGGREGATION Metadata; + ISP_FRAME_SETTINGS *pSettings = GetIspSettings(); + + // Wipe the metadata so all settings will default to "Not Set". + RtlZeroMemory( &Metadata, sizeof(Metadata) ); + + // FocusState; + m_Sensor->GetFocusState( (KSCAMERA_EXTENDEDPROP_FOCUSSTATE *) &Metadata.FocusState ); + + // ExposureTime; + Metadata.ExposureTime = + CMetadataLongLong( GetCurrentExposureTime() ); + + // EVCompensation; + Metadata.EVCompensation = CMetadataEVCompensation(pSettings->EVCompensation.Mode, pSettings->EVCompensation.Value); + + // ISOSpeed; + Metadata.ISOSpeed = CMetadataLong(GetCurrentISOSpeed()); + DBG_TRACE("ISO=%d, ISO Flags=0x%016llX", Metadata.ISOSpeed.Value, pSettings->ISOMode); + + // LensPosition; + Metadata.LensPosition = CMetadataLong( pSettings->FocusSetting.VideoProc.Value.ul ); + + // FlashOn; + Metadata.FlashOn = CMetadataLong((ULONG) pSettings->FlashMode); + + // WhiteBalanceMode; + Metadata.WhiteBalanceMode = CMetadataLong((ULONG) pSettings->WhiteBalanceMode); + + // IsoGains; + Metadata.IsoAnalogGain = + CMetadataSRational( GetRandom( (LONG)5000, (LONG)20000 ), (LONG)10000 ); // Some number from 0.5 to 2.0 - for testing. + Metadata.IsoDigitalGain = + CMetadataSRational( GetRandom( (LONG)5000, (LONG)20000 ), (LONG)10000 ); // Some number from 0.5 to 2.0 - for testing. + + // SensorFrameRate; + ULARGE_INTEGER FrameRate; + FrameRate.LowPart = 60 * 60 *24; + FrameRate.HighPart = (ULONG) ((ONESECOND * FrameRate.LowPart) / m_TimePerFrame); // compute number of frames per day. + Metadata.SensorFrameRate = CMetadataULongLong( FrameRate.QuadPart ); + + // WhiteBalanceGains; + Metadata.WhiteBalanceGain_R = // MF_CAPTURE_METADATA_WHITEBALANCE_GAINS + CMetadataSRational( GetRandom( (LONG)5000, (LONG)20000 ), (LONG)10000 ); // Some number from 0.5 to 2.0 - for testing. + Metadata.WhiteBalanceGain_G = // MF_CAPTURE_METADATA_WHITEBALANCE_GAINS + CMetadataSRational( GetRandom( (LONG)5000, (LONG)20000 ), (LONG)10000 ); // Some number from 0.5 to 2.0 - for testing. + Metadata.WhiteBalanceGain_B = // MF_CAPTURE_METADATA_WHITEBALANCE_GAINS + CMetadataSRational( GetRandom( (LONG)5000, (LONG)20000 ), (LONG)10000 ); // Some number from 0.5 to 2.0 - for testing. + + return Metadata; +} + +// +// Emit metadata here for preview pin. +// +void +CPreviewHardwareSimulation:: +EmitMetadata( + _Inout_ PKSSTREAM_HEADER pStreamHeader +) +/*++ + +Routine Description: + + Emit metadata for a photo. + +Arguments: + + None + +Return Value: + + Success / Failure + +--*/ +{ + PAGED_CODE(); + + NT_ASSERT(pStreamHeader); + + // Add the normal frame info to the metadata + // Note: This call ensures the KSSTREAM_METADATA_INFO is properly initialized. + CHardwareSimulation::EmitMetadata( pStreamHeader ); + + if (0 != (pStreamHeader->OptionsFlags & KSSTREAM_HEADER_OPTIONSF_METADATA)) + { + PKS_FRAME_INFO pFrameInfo = (PKS_FRAME_INFO)(pStreamHeader + 1); + PKSSTREAM_METADATA_INFO pMetadata = (PKSSTREAM_METADATA_INFO) (pFrameInfo + 1); + ULONG BytesLeft = pMetadata->BufferSize - pMetadata->UsedSize; + + if(m_PhotoConfirmationEntry.isRequired()) + { + if(BytesLeft < sizeof(KSCAMERA_METADATA_PHOTOCONFIRMATION)) + { + //Fatal driver error, should never come here. + NT_ASSERT(FALSE); + return; + } + PKSCAMERA_METADATA_PHOTOCONFIRMATION pPhotoConfirmation = + (PKSCAMERA_METADATA_PHOTOCONFIRMATION) (((PBYTE)pMetadata->SystemVa) + pMetadata->UsedSize); + pPhotoConfirmation->Header.MetadataId = MetadataId_PhotoConfirmation; + pPhotoConfirmation->Header.Size = sizeof(*pPhotoConfirmation); + pPhotoConfirmation->PhotoConfirmationIndex = m_PhotoConfirmationEntry.getIndex(); + pMetadata->UsedSize += sizeof(*pPhotoConfirmation); + BytesLeft -= sizeof(*pPhotoConfirmation); + + DBG_TRACE("PhotoConfirmation Header.MetadataId = %u", pPhotoConfirmation->Header.MetadataId); + DBG_TRACE("PhotoConfirmation Header.Size = %u", pPhotoConfirmation->Header.Size); + DBG_TRACE("PhotoConfirmation Index = %u", pPhotoConfirmation->PhotoConfirmationIndex); + } + else + { + DBG_TRACE("Normal frame; no photo confirmation metadata."); + } + + if( BytesLeft >= sizeof(CAMERA_METADATA_PREVIEWAGGREGATION) ) + { + PCAMERA_METADATA_PREVIEWAGGREGATION pPreviewAggregation = + (PCAMERA_METADATA_PREVIEWAGGREGATION) (((PBYTE)pMetadata->SystemVa) + pMetadata->UsedSize); + pPreviewAggregation->Header.MetadataId = (ULONG) MetadataId_Custom_PreviewAggregation; + pPreviewAggregation->Header.Size = sizeof(*pPreviewAggregation); + pPreviewAggregation->Data = GetMetadata(); + pMetadata->UsedSize += sizeof(*pPreviewAggregation); + BytesLeft -= sizeof(*pPreviewAggregation); + } + + // MF_CAPTURE_METADATA_HISTOGRAM + // + // Generate the entire Histogram metadata blob here. + // + if( BytesLeft >= sizeof(CAMERA_METADATA_HISTOGRAM) ) + { + PCAMERA_METADATA_HISTOGRAM pHistogram = + (PCAMERA_METADATA_HISTOGRAM) (((PBYTE)pMetadata->SystemVa) + pMetadata->UsedSize); + ULONG ChannelMask = m_Synthesizer->GetChannelMask(); + CExtendedProperty Setting; + + m_Sensor->GetHistogram( &Setting ); + if( Setting.Flags & KSCAMERA_EXTENDEDPROP_HISTOGRAM_ON && + ChannelMask != 0 ) + { + // Just checking to see if we messed something up here. + NT_ASSERT( pMetadata->BufferSize >= CExtendedMetadata::METADATA_MAX + sizeof(CAMERA_METADATA_HISTOGRAM) ); + + pHistogram->Header.MetadataId = (ULONG) MetadataId_Custom_Histogram; + pHistogram->Header.Size = sizeof(*pHistogram); + + // Acquire the data for every channel. + pHistogram->Data.ChannelMask = ChannelMask; + pHistogram->Data.Height = m_Height; + pHistogram->Data.Width = m_Width; + pHistogram->Data.FourCC = m_Sensor->GetFourCC( m_PinID ); + m_Synthesizer->Histogram( pHistogram->Data.P0Data, pHistogram->Data.P1Data, pHistogram->Data.P2Data ); + pMetadata->UsedSize += sizeof(*pHistogram); + BytesLeft -= sizeof(*pHistogram); + } + } + + CExtendedVidProcSetting FaceDetect; + m_Sensor->GetFaceDetection(&FaceDetect); + + if( FaceDetect.Flags & KSCAMERA_EXTENDEDPROP_FACEDETECTION_PREVIEW ) + { + DBG_TRACE("PREVIEW"); + EmitFaceMetadata( + pStreamHeader, + FaceDetect.GetULONG(), + FaceDetect.Flags & KSCAMERA_EXTENDEDPROP_FACEDETECTION_ADVANCED_MASK); + } + } +} + diff --git a/avscamera/sys/PreviewHwSim.h b/avscamera/sys/PreviewHwSim.h new file mode 100644 index 000000000..92d3eae70 --- /dev/null +++ b/avscamera/sys/PreviewHwSim.h @@ -0,0 +1,57 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + PreviewHwSim.h + + Abstract: + + This file contains the definition of the CPreviewHardwareSimulation class. + + This is a specialization of CHardwareSimulation that provides preview- + specific metadata. + + History: + + created 5/28/2014 + +**************************************************************************/ + +#pragma once + +// forward references +struct ISP_FRAME_SETTINGS; + +class CPreviewHardwareSimulation : + public CHardwareSimulation +{ +public: + CPreviewHardwareSimulation( + _Inout_ CSensor *Sensor, + _In_ LONG PinID + ); + + virtual ~CPreviewHardwareSimulation(); + + virtual + void + FakeHardware(); + +protected: + METADATA_PREVIEWAGGREGATION + CPreviewHardwareSimulation:: + GetMetadata(); + + // Inject preview pin-specific metadata. + virtual + void + EmitMetadata( + _Inout_ PKSSTREAM_HEADER pStreamHeader + ); + +}; + diff --git a/avscamera/sys/RGB24Synthesizer.cpp b/avscamera/sys/RGB24Synthesizer.cpp new file mode 100644 index 000000000..21c6e054b --- /dev/null +++ b/avscamera/sys/RGB24Synthesizer.cpp @@ -0,0 +1,133 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + RGB24Synthesizer.cpp + + Abstract: + + This file contains the implementation of CRGB24Synthesizer. + + CRGB24Synthesizer is derived from CRGBSynthesizer. It uses the RGB + color space and defines a Commit() function that reformats pixels into + the RGB24 format. RGB24 uses 8 bits per primary and 3 bytes per pixel + in a single plane. + + History: + + created 4/14/2014 + +**************************************************************************/ + +#include "Common.h" + +/************************************************************************** + + LOCKED CODE + +**************************************************************************/ + +#ifdef ALLOC_PRAGMA +#pragma code_seg() +#endif // ALLOC_PRAGMA + +// suppressed due to Esp:773 +#pragma warning (push) +#pragma warning( disable:26015 ) // Suppress OACR error. Seems nonsensical. TODO: Must revisit. +#pragma warning( disable:26019 ) +_Success_(return > 0) +ULONG +CRGB24Synthesizer:: +Commit( + _Out_writes_bytes_(Size) + PUCHAR Buffer, + _In_ ULONG Size, + _In_ ULONG Stride +) +/*++ + +Routine Description: + + Copy (and reformat, if necessary) pixels from the internal scratch + buffer. If the output format decimates chrominance, do it here. + +Arguments: + + Buffer - + The output buffer to fill. + + Size - + The size of the output buffer in bytes. + + Stride - + The length of a row in bytes. + +Return Value: + + Number of bytes copied into Buffer. + +--*/ +{ + // In case stride isn't initialized. + if( Stride==0 ) + { + Stride = m_OutputStride; + } + + // These are impossible conditions. + if( !( Buffer && + m_Buffer && + (Size&3)==0 && // the rows are dword aligned, so the size must be too. + Size>3 && // there must be room for at least 1 pixel + (Stride&3)==0 && // rows should be DWORD aligned + Stride>3 && // rows should contain at least 1 pixel + m_Height>0 && + m_Width>0 + ) ) + { + NT_ASSERT(FALSE); + return 0; + } + + ULONG limit = min( Size/Stride, m_Height ); + for( ULONG row=0; row=4 ) + { + DWORD P0 = pSrc[0]; + DWORD P1 = pSrc[1]; + DWORD P2 = pSrc[2]; + DWORD P3 = pSrc[3]; + ((PDWORD) pDst)[0] = ((P0 ) & 0x00FFFFFF) | (P1 << 24); + ((PDWORD) pDst)[1] = ((P1 >> 8) & 0x0000FFFF) | (P2 << 16); + ((PDWORD) pDst)[2] = ((P2 >> 16) & 0x000000FF) | (P3 << 8); + pDst += 4 * 3; + pSrc += 4; + col -= 4; + } + else + { + DWORD P0 = *pSrc++; + *pDst++ = (UCHAR) (P0); + *pDst++ = (UCHAR) (P0 >> 8); + *pDst++ = (UCHAR) (P0 >> 16); + col--; + } + } + } + + return limit * Stride; +} +// suppressed due to Esp:773 +#pragma warning (pop) diff --git a/avscamera/sys/RGB24Synthesizer.h b/avscamera/sys/RGB24Synthesizer.h new file mode 100644 index 000000000..5a5d8f709 --- /dev/null +++ b/avscamera/sys/RGB24Synthesizer.h @@ -0,0 +1,69 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + RGB24Synthesizer.h + + Abstract: + + This file contains the definition of CRGB24Synthesizer. + + CRGB24Synthesizer is derived from CXRGBSynthesizer. It uses the RGB + color space and defines a Commit() function that reformats pixels into + the RGB24 format. RGB24 uses 8 bits per primary and 3 bytes per pixel + in a single plane. + + History: + + created 4/14/2014 + +**************************************************************************/ + +/************************************************* + + CRGB24Synthesizer + + Image synthesizer for RGB24 format. + +*************************************************/ + +class CRGB24Synthesizer + : public CXRGBSynthesizer +{ +public: + + // + // Default constructor + // + CRGB24Synthesizer( + LONG Width=0, + LONG Height=0 + ) + : CXRGBSynthesizer(Width, Height) + { + // Round up to the next full dword for a row on output. + m_OutputStride = (( Width * 3 ) + 3) & ((LONG) ~3); + m_FormatName = "RGB24"; + } + + // + // Commit + // + // Copy (and reformat, if necessary) pixels from the internal scratch + // buffer. If the output format decimates chrominance, do it here. + // + virtual + _Success_(return > 0) + ULONG + Commit( + _Out_writes_bytes_(Size) + PUCHAR Buffer, + _In_ ULONG Size, + _In_ ULONG Stride + ); +}; + diff --git a/avscamera/sys/Ref.h b/avscamera/sys/Ref.h new file mode 100644 index 000000000..c2c4d76c2 --- /dev/null +++ b/avscamera/sys/Ref.h @@ -0,0 +1,185 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + ref.h + + Abstract: + + Implement a reference counting base class. Also implement something + similar to ComPtr<>, called a CRefPtr<>. The implementation may not + be as robust as ComPtr<>, but it should do the job here. + + History: + + created 12/8/2014 + +**************************************************************************/ + +#pragma once + +class CRef : public CNonCopyable +{ +private: + EX_RUNDOWN_REF m_Rundown; + +public: + // Initialize. + CRef() + { + Reset(); + } + + // Prevent deletion until the last reference is gone. + ~CRef() + { + Wait(); + } + + // Increment the reference. + BOOLEAN + Acquire() + { + return ExAcquireRundownProtection( &m_Rundown ); + } + + // Decrement the reference. + void + Release() + { + return ExReleaseRundownProtection( &m_Rundown ); + } + + // Wait for the last reference to go away. + // Note: This must be called by the child object's destructor. + void + Wait() + { + ExWaitForRundownProtectionRelease( &m_Rundown ); + } + + // Reinitialize to reuse the associated object. + void + Reset() + { + ExInitializeRundownProtection( &m_Rundown ); + } +}; + +template +class CRefPtr +{ +private: + T *m_ptr; + +public: + CRefPtr() + : m_ptr(nullptr) + {} + + // Copy constructor. + CRefPtr( const CRefPtr &ptr ) + : m_ptr(nullptr) + { + // Make an initial assignment. + if( ptr && ptr->Acquire() ) + { + m_ptr = ptr; + } + } + + // Initial construction with a T object. + CRefPtr( T *ptr ) + : m_ptr(nullptr) + { + // Make an initial assignment. + if( ptr && ptr->Acquire() ) + { + m_ptr = ptr; + } + } + + // Decrement the reference on destruction. + ~CRefPtr() + { + // Free any ptr. + if( m_ptr ) + { + m_ptr->Release(); + } + } + + // Assignment + CRefPtr & + operator = ( T *ptr ) + { + if( m_ptr ) + { + m_ptr->Release(); + } + + if( ptr ) + { + ptr->Acquire(); + } + + m_ptr = ptr; + return *this; + } + + // Assignment + CRefPtr & + operator = ( CRefPtr &ptr ) + { + return + (*this = ptr.m_ptr); + } + + // Dereference the pointer. + T * + operator -> () + { + return m_ptr; + } + + // Test for nullptr. + operator bool() + { + return m_ptr != nullptr; + } + + // Test for !nullptr. + bool + operator !() + { + return !m_ptr; + } + + // Test for equality. + bool + operator ==( T *ptr ) + { + return m_ptr == ptr; + } + + // Unsafe accessor. Use sparingly. + T * + get() + { + return m_ptr; + } +}; + +// Test for equality. +template +inline +bool +operator ==( T *ptr1, CRefPtr &ptr2 ) +{ + return ptr2 == ptr1; +} + diff --git a/avscamera/sys/Roi.cpp b/avscamera/sys/Roi.cpp new file mode 100644 index 000000000..e11189963 --- /dev/null +++ b/avscamera/sys/Roi.cpp @@ -0,0 +1,629 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2013, Microsoft Corporation. + + File: + + Roi.cpp + + Abstract: + + This file contains the implemention of CRoiConfig, CRoiProperty and + related classes used in the filter to implement ROI handling. + + History: + + created 6/27/2013 + +**************************************************************************/ + +#include "Common.h" + +/************************************************************************** + + PAGED CODE + +**************************************************************************/ + +#ifdef ALLOC_PRAGMA +#pragma code_seg("PAGE") +#endif // ALLOC_PRAGMA + +// Helper function to diff ptrs. +inline +UINT_PTR +ByteDiffPtrs( PVOID pBase, PVOID pCurrent ) +{ + PAGED_CODE(); + + return reinterpret_cast(pCurrent) - reinterpret_cast(pBase) ; +} + +// Helper function to get the size of an ROI control based just on the ID. +static +ULONG +GetSizeOfRoiInfo( ULONG ControlId ) +{ + PAGED_CODE(); + + switch( ControlId ) + { + case KSPROPERTY_CAMERACONTROL_EXTENDED_WHITEBALANCEMODE: + return sizeof(KSCAMERA_EXTENDEDPROP_ROI_WHITEBALANCE); + break; + + case KSPROPERTY_CAMERACONTROL_EXTENDED_EXPOSUREMODE: + return sizeof(KSCAMERA_EXTENDEDPROP_ROI_EXPOSURE); + break; + + case KSPROPERTY_CAMERACONTROL_EXTENDED_FOCUSMODE: + return sizeof(KSCAMERA_EXTENDEDPROP_ROI_FOCUS); + break; + + default: + NT_ASSERT(FALSE); + return 0; + } +} + +// Helper function to get the size of an ROI control. +static +ULONG +GetSize( + _In_ PKSCAMERA_EXTENDEDPROP_ROI_ISPCONTROL pCtrl +) +{ + PAGED_CODE(); + + // This actually needs to return the type based on the ROI_INFO size + ULONG RoiInfoSize = GetSizeOfRoiInfo( pCtrl->ControlId ); + + if( RoiInfoSize ) + { + return + sizeof(KSCAMERA_EXTENDEDPROP_ROI_ISPCONTROL) + + (pCtrl->ROICount * RoiInfoSize); + } + else + { + NT_ASSERTMSG("GetSizeOfRoiInfo( pCtrl->ControlId ) returned 0! Should never happen!", FALSE); + return 0; + } +} + +// Helper function to walk to the next ROI control. +static +PKSCAMERA_EXTENDEDPROP_ROI_ISPCONTROL +NextCtrl( + _In_ PKSCAMERA_EXTENDEDPROP_ROI_ISPCONTROL pCtrl +) +{ + PAGED_CODE(); + + ULONG Size = GetSize( pCtrl ); + + if( Size ) + { + return + reinterpret_cast + (((PBYTE) pCtrl) + Size); + } + else + { + NT_ASSERTMSG("GetSize( pCtrl ) returned 0! Should never happen!", FALSE); + return nullptr; + } +} + +PKSCAMERA_EXTENDEDPROP_ROI_ISPCONTROL +CRoiProperty:: +Find( + _In_ KSPROPERTY_CAMERACONTROL_EXTENDED_PROPERTY Property +) +/*++ + +Routine Description: + + Search this ROI property for a property ID. + +Arguments: + + Property - + Which control to find. + +Return Value: + + A pointer to the ROI control structure. + +--*/ +{ + PAGED_CODE(); + + // We assume the controls have been validated first. + PKSCAMERA_EXTENDEDPROP_ROI_ISPCONTROL pIspCtrl = + reinterpret_cast (this+1); + + // Loop thru the controls. + for( ULONG i=0; iControlId == (ULONG) Property ) + { + return pIspCtrl; + } + + // Advance to the next control. + pIspCtrl = NextCtrl( pIspCtrl ); + } + + return nullptr; +} + +PKSCAMERA_EXTENDEDPROP_ROI_ISPCONTROL +CRoiProperty:: +Add( + _In_ PKSCAMERA_EXTENDEDPROP_ROI_ISPCONTROL pCtrl +) +/*++ + +Routine Description: + + Add a control to the end of this header. + + Note: This function does not reallocate memory. Be careful to ensure + there is space available. + +Arguments: + + pCtrl - + An ROI control to append. + +Return Value: + + A pointer to the appended ROI control structure. + +--*/ +{ + PAGED_CODE(); + + // Only bother to add if there are ROIs. + if( pCtrl->ROICount > 0 ) + { + // We assume the controls have been validated first. + PKSCAMERA_EXTENDEDPROP_ROI_ISPCONTROL pIspCtrl = + reinterpret_cast (this+1); + + // Loop thru all the controls. + for( ULONG i=0; i (this+1); + + // Loop thru the controls. + for( ULONG i=0; iControlId ) + { + case KSPROPERTY_CAMERACONTROL_EXTENDED_WHITEBALANCEMODE: + case KSPROPERTY_CAMERACONTROL_EXTENDED_EXPOSUREMODE: + case KSPROPERTY_CAMERACONTROL_EXTENDED_FOCUSMODE: + break; + + default: + //NT_ASSERT(FALSE); + DBG_LEAVE( "()=false : Invalid ControlId = 0x%08X", pIspCtrl->ControlId ); + return false; + } + + // Make sure there is room for the # of ROIs reported + if( Size < ByteDiffPtrs( this, NextCtrl(pIspCtrl) ) || + m_Hdr.Size < ByteDiffPtrs( &m_Hdr, NextCtrl(pIspCtrl) ) ) + { + //NT_ASSERT(FALSE); + DBG_TRACE( "Failed(2): Size=%d, should be at least %Iu", Size, ByteDiffPtrs( this, NextCtrl(pIspCtrl) ) ); + DBG_TRACE( " Or: m_Hdr.Size=%d, should be at least %Iu", m_Hdr.Size, ByteDiffPtrs( &m_Hdr, NextCtrl(pIspCtrl) ) ); + DBG_LEAVE( "()=false" ); + return false; + } + + // Validate the ROIs + for( ULONG j=0; jROICount; j++ ) + { + // Index into to the control's ROI list. Get the equivilent of "pIspCtrl->RoiInfo[j]" + PKSCAMERA_EXTENDEDPROP_ROI_INFO pRoiInfo = + reinterpret_cast + (((PBYTE) (pIspCtrl+1)) + (j * GetSizeOfRoiInfo(pIspCtrl->ControlId) )); + + // Validate the cooridinates + if( pRoiInfo->Region.top < (LONG) TO_Q31(0) || + pRoiInfo->Region.bottom < (LONG) TO_Q31(0) || + pRoiInfo->Region.left < (LONG) TO_Q31(0) || + pRoiInfo->Region.right < (LONG) TO_Q31(0) ) + { + //NT_ASSERT(FALSE); + DBG_TRACE( "Failed: Region out of range." ); + DBG_TRACE( " (top=0x%08X, left=0x%08X, bottom=0x%08X, right=0x%08X)" + , pRoiInfo->Region.top + , pRoiInfo->Region.bottom + , pRoiInfo->Region.left + , pRoiInfo->Region.right ); + DBG_LEAVE( "()=false" ); + return false; + } + + // Validate the RegionOfInterestType + switch( pRoiInfo->RegionOfInterestType ) + { + case KSCAMERA_EXTENDEDPROP_ROITYPE_UNKNOWN: + case KSCAMERA_EXTENDEDPROP_ROITYPE_FACE: + break; + default: + //NT_ASSERT(FALSE); + DBG_LEAVE( "()=false Failed: RegionOfInterestType=%d", pRoiInfo->RegionOfInterestType ); + return false; + } + + // Validate the Weight + if( pRoiInfo->Weight > 100 ) + { + //NT_ASSERT(FALSE); + DBG_LEAVE( "()=false Failed: Weight=%d", pRoiInfo->Weight ); + return false; + } + + // Validate the Flags. + // Note: the driver doesn't allow mis-matched ROI Flags. + ULONGLONG RoiFlags = pRoiInfo->Flags & ~KSCAMERA_EXTENDEDPROP_FLAG_CANCELOPERATION; + + switch( pIspCtrl->ControlId ) + { + // Exposure & WhiteBalance only support Auto, Manual & Lock. + case KSPROPERTY_CAMERACONTROL_EXTENDED_EXPOSUREMODE: + if( j!=0 ) + { + if( RoiFlags != ExposureFlags ) + { + DBG_LEAVE( "()=false : Mismatched Exposure Flags" ); + return false; + } + } + else + { + switch( RoiFlags & + (KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO| + KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK) ) + { + case KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO: + case KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO + KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK: + case 0: + break; + default: + DBG_LEAVE( "()=false : Invalid Exposure Flags" ); + return false; + } + ExposureFlags = RoiFlags; + } + break; + + case KSPROPERTY_CAMERACONTROL_EXTENDED_WHITEBALANCEMODE: + if( j!=0 ) + { + if( RoiFlags != WhiteBalanceFlags ) + { + DBG_LEAVE( "()=false : Mismatched WhiteBalance Flags" ); + return false; + } + } + else + { + switch( RoiFlags & + (KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO| + KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK) ) + { + case KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO: + case KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO + KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK: + case 0: + break; + default: + DBG_LEAVE( "()=false : Invalid WhiteBalance Flags" ); + return false; + } + WhiteBalanceFlags = RoiFlags; + } + break; + + // Focus supports additional flags... + case KSPROPERTY_CAMERACONTROL_EXTENDED_FOCUSMODE: + if( j!=0 ) + { + if( RoiFlags != FocusFlags ) + { + DBG_LEAVE( "()=false : Mismatched Focus Flags" ); + return false; + } + } + else + { + switch( RoiFlags & + (KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO | + KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK | + KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUS | + KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUSLOCK) ) + { + case KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO: + case KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO + KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK: + case KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUS: + case KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUS + KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUSLOCK: + case 0: + break; + default: + DBG_LEAVE( "()=false : Invalid Focus Flags" ); + return false; + } + + switch( RoiFlags & KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_MASK ) + { + case KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_MACRO: + case KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_NORMAL: + case KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_FULLRANGE: + // Depreciated cases are not supported: + //case KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_INFINITY: + //case KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_HYPERFOCAL: + case 0: + break; + default: + DBG_LEAVE( "()=false : Invalid Focus Range" ); + return false; + } + FocusFlags = RoiFlags; + } + break; + } + } + + // Advance to the next control. + pIspCtrl = NextCtrl( pIspCtrl ) ; + } + + DBG_LEAVE( "()=true" ); + return true; +} + +// Log to the debug output. +void +CRoiProperty:: +Log() +{ + PAGED_CODE(); + + DBG_TRACE("Version=%d, PinId=%d, Capability=0x%016llX, Flags=0x%016llX, Size=%d", + Version, PinId, Capability, Flags, Size ); + + CRoiIspControl *pIspCtrl = reinterpret_cast (this+1); + + // Loop thru the controls. + for( ULONG i=0; iLog(); + + // Advance to the next control. + pIspCtrl = reinterpret_cast( NextCtrl( pIspCtrl ) ) ; + } +} + +// +// Initialize a block of memory from another ROI control +// +// Note: This function assumes enough ROI_INFO structures follow +// such that this copy will not overrun. +// +BOOL +CRoiIspControl:: +Init( + _In_ CRoiProperty *pRoiProperty, + _In_ KSPROPERTY_CAMERACONTROL_EXTENDED_PROPERTY Property +) +/*++ + +Routine Description: + + Initialize a block of memory from another ROI control + + Note: This function assumes enough ROI_INFO structures follow + such that this copy will not overrun. + +Arguments: + + pRoiProperty - + The extended ROI control payload. + + Property - + Which ISP control to copy from the payload. + +Return Value: + + TRUE - The ROI was set. + FALSE - The ROI was cleared. + +--*/ +{ + PAGED_CODE(); + + CRoiIspControl * + pCtrl = reinterpret_cast (pRoiProperty->Find( Property )); + + if( pCtrl ) + { + memcpy( this, + pCtrl, + pCtrl->GetSize() ); + return TRUE; + } + + ControlId = Property; + ROICount = 0; + return FALSE; +} + +// Log to the debug output. +void +CRoiIspControl:: +Log() +{ + PAGED_CODE(); + + switch( ControlId ) + { + case KSPROPERTY_CAMERACONTROL_EXTENDED_WHITEBALANCEMODE: + DBG_TRACE("WhiteBalance"); + break; + + case KSPROPERTY_CAMERACONTROL_EXTENDED_EXPOSUREMODE: + DBG_TRACE("Exposure"); + break; + + case KSPROPERTY_CAMERACONTROL_EXTENDED_FOCUSMODE: + DBG_TRACE("Focus"); + break; + + default: + DBG_TRACE("[Unknown]"); + NT_ASSERT(FALSE); + break; + } + + DBG_TRACE("Result=0x%08X", Result); + + for( ULONG i=0; iRoiInfo[j]" + PKSCAMERA_EXTENDEDPROP_ROI_INFO pRoiInfo = + reinterpret_cast + (((PBYTE) (this+1)) + (i * GetSizeOfRoiInfo(ControlId) )); + + DBG_TRACE(" | Flags=0x%016llX [Top=0x%08X, Left=0x%08X, Bottom=0x%08X, Right=0x%08X]", + pRoiInfo->Flags, + pRoiInfo->Region.top, + pRoiInfo->Region.left, + pRoiInfo->Region.bottom, + pRoiInfo->Region.right ); + } +} + +// +// Initialize a block of memory with an empty control list. +// +CRoiIspControl:: +CRoiIspControl( + _In_ KSPROPERTY_CAMERACONTROL_EXTENDED_PROPERTY Property +) +{ + PAGED_CODE(); + + ControlId = Property; + ROICount = 0; + Result = 0; + Reserved = 0; +} + +ULONG +CRoiIspControl:: +GetSize() +{ + PAGED_CODE(); + + return (ROICount>0) ? ::GetSize( this ) : 0; +} + diff --git a/avscamera/sys/Roi.h b/avscamera/sys/Roi.h new file mode 100644 index 000000000..5149bf24e --- /dev/null +++ b/avscamera/sys/Roi.h @@ -0,0 +1,279 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2013, Microsoft Corporation. + + File: + + Roi.h + + Abstract: + + This file contains the definition of CRoiConfig, CRoiProperty and + related classes used in the filter to implement ROI handling. + + History: + + created 6/27/2013 + +**************************************************************************/ + +const ULONG MAX_ROI = 4; + +class CRoiIspControl ; + +class CRoiConfigCaps : public KSCAMERA_EXTENDEDPROP_ROI_CONFIGCAPS +{ +public: + CRoiConfigCaps( + _In_ KSPROPERTY_CAMERACONTROL_EXTENDED_PROPERTY Id, + _In_ ULONG MaxROI, + _In_ ULONGLONG Caps=0 + ) + { + ControlId = Id; + MaxNumberOfROIs = MaxROI; + Capability = Caps; + } + + static const ULONGLONG FOCUS_CAPS = + KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO | + KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK | + KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUS | + KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_MACRO | + KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_NORMAL | + KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_FULLRANGE; + + static const ULONGLONG EXPOSURE_CAPS = + KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO | + KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK; + + static const ULONGLONG WHITEBALANCE_CAPS = + KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO | + KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK; +}; + +class CRoiConfigCapsHdr : public KSCAMERA_EXTENDEDPROP_ROI_CONFIGCAPSHEADER +{ + CRoiConfigCaps FocusCaps; + CRoiConfigCaps ExposureCaps; + CRoiConfigCaps WhiteBalanceCaps; + +public: + CRoiConfigCapsHdr() + : FocusCaps( + KSPROPERTY_CAMERACONTROL_EXTENDED_FOCUSMODE, + MAX_ROI, + CRoiConfigCaps::FOCUS_CAPS) + , ExposureCaps( + KSPROPERTY_CAMERACONTROL_EXTENDED_EXPOSUREMODE, + MAX_ROI, + CRoiConfigCaps::EXPOSURE_CAPS ) + , WhiteBalanceCaps( + KSPROPERTY_CAMERACONTROL_EXTENDED_WHITEBALANCEMODE, + MAX_ROI, + CRoiConfigCaps::WHITEBALANCE_CAPS ) + { + ConfigCapCount = 3; + Reserved = 0; + Size = sizeof(*this); + } +}; + +class CRoiConfig : public KSCAMERA_EXTENDEDPROP_HEADER +{ + CRoiConfigCapsHdr CapsHdr; +public: + CRoiConfig() + { + Version = 1; + PinId = KSCAMERA_EXTENDEDPROP_FILTERSCOPE; + Size = sizeof(*this); + Result = 0; + Capability = 0; + Flags = 0; + } + CRoiConfig( KSCAMERA_EXTENDEDPROP_HEADER &hdr ) + : KSCAMERA_EXTENDEDPROP_HEADER(hdr) + { + Size = sizeof(*this); + } + + bool isValid() + { + return (Version == KSCAMERA_EXTENDEDPROP_VERSION) && + (Size >= sizeof(*this)) ; + } +}; + +// +// A wrapper for the KSCAMERA_EXTENDEDPROP_ROI payload +// +// The payload extends beyond the end of this class definition. However the +// payload is variable in length, so this class definition contains member +// functions that can be used to access and append items to the payload. +// +class CRoiProperty : public KSCAMERA_EXTENDEDPROP_HEADER +{ + KSCAMERA_EXTENDEDPROP_ROI_ISPCONTROLHEADER m_Hdr; + +public: + // Default ctor - used to init the buffer when returning ROI info. + CRoiProperty() + { + Version = KSCAMERA_EXTENDEDPROP_VERSION; + PinId = KSCAMERA_EXTENDEDPROP_FILTERSCOPE; + Size = sizeof(*this); + Result = 0; + Capability = KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL | KSCAMERA_EXTENDEDPROP_CAPS_CANCELLABLE; + Flags = 0; + + m_Hdr.ControlCount = 0; + m_Hdr.Size = sizeof(m_Hdr); + m_Hdr.Reserved = 0; + } + + // Validate the CRoiProperty + bool + isValid(); + + // Get Size attribute. + ULONG + GetSize() + { + return Size; + } + + // Get number of controls. (1-3) + ULONG + GetControlCount() + { + return m_Hdr.ControlCount; + } + + // Find a given control after the property header. + PKSCAMERA_EXTENDEDPROP_ROI_ISPCONTROL + Find( + _In_ KSPROPERTY_CAMERACONTROL_EXTENDED_PROPERTY Property + ); + + // + // Add a control to the end of this header. + // + // Note: This function does not reallocate memory. Be careful to ensure + // there is space available. + // + PKSCAMERA_EXTENDEDPROP_ROI_ISPCONTROL + Add( + _In_ PKSCAMERA_EXTENDEDPROP_ROI_ISPCONTROL pCtrl + ); + + void + Log(); +}; + +// Note: No initializer classes are provided for the following DDI +// structure definitions since there is no practical difference in their +// definitions. However the CRoiIspControl::GetSize() method is aware +// of them and will report the correct size of the structure if there were +// any difference in size. +// +// - KSCAMERA_EXTENDEDPROP_ROI_WHITEBALANCE +// - KSCAMERA_EXTENDEDPROP_ROI_EXPOSURE +// - KSCAMERA_EXTENDEDPROP_ROI_FOCUS +// +class CRoiIspControl : public KSCAMERA_EXTENDEDPROP_ROI_ISPCONTROL +{ +public: + BOOL + Init( + _In_ CRoiProperty *pRoiProperty, + _In_ KSPROPERTY_CAMERACONTROL_EXTENDED_PROPERTY Property + ); + + CRoiIspControl( + _In_ KSPROPERTY_CAMERACONTROL_EXTENDED_PROPERTY Property= + KSPROPERTY_CAMERACONTROL_EXTENDED_WHITEBALANCEMODE + ); + + void + Log(); + + ULONG + GetSize(); +}; + +// Simple classes used by the driver to hold the maximum allowed number of ROIs. +class CWhiteBalanceRoiIspControl : public CRoiIspControl +{ +private: + KSCAMERA_EXTENDEDPROP_ROI_WHITEBALANCE ROI[MAX_ROI]; + +public: + CWhiteBalanceRoiIspControl( + _In_ CRoiProperty *pRoiProperty + ) + { + Init( pRoiProperty, KSPROPERTY_CAMERACONTROL_EXTENDED_WHITEBALANCEMODE ); + } + + CWhiteBalanceRoiIspControl() + : CRoiIspControl( KSPROPERTY_CAMERACONTROL_EXTENDED_WHITEBALANCEMODE ) + {} + + ULONGLONG + GetFlags() + { + return ( ROICount>0 ) ? ROI[0].ROIInfo.Flags : 0; + } +}; + +class CExposureRoiIspControl : public CRoiIspControl +{ +private: + KSCAMERA_EXTENDEDPROP_ROI_EXPOSURE ROI[MAX_ROI]; + +public: + CExposureRoiIspControl( + _In_ CRoiProperty *pRoiProperty + ) + { + Init( pRoiProperty, KSPROPERTY_CAMERACONTROL_EXTENDED_EXPOSUREMODE ); + } + + CExposureRoiIspControl() + : CRoiIspControl( KSPROPERTY_CAMERACONTROL_EXTENDED_EXPOSUREMODE ) + {} + + ULONGLONG + GetFlags() + { + return ( ROICount>0 ) ? ROI[0].ROIInfo.Flags : 0; + } +}; + +class CFocusRoiIspControl : public CRoiIspControl +{ +private: + KSCAMERA_EXTENDEDPROP_ROI_FOCUS ROI[MAX_ROI]; + +public: + CFocusRoiIspControl( + _In_ CRoiProperty *pRoiProperty + ) + { + Init( pRoiProperty, KSPROPERTY_CAMERACONTROL_EXTENDED_FOCUSMODE ); + } + + CFocusRoiIspControl() + : CRoiIspControl( KSPROPERTY_CAMERACONTROL_EXTENDED_EXPOSUREMODE ) + {} + + ULONGLONG + GetFlags() + { + return ( ROICount>0 ) ? ROI[0].ROIInfo.Flags : 0; + } +}; + diff --git a/avscamera/sys/Sensor.cpp b/avscamera/sys/Sensor.cpp new file mode 100644 index 000000000..e973492ca --- /dev/null +++ b/avscamera/sys/Sensor.cpp @@ -0,0 +1,1260 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + Sensor.cpp + + Abstract: + + Base Sensor class implementation. + + This class also controls access to the pin simulations. Most cameras + have a limited set of ISP resources and can only instantiate a fixed + number of pins. This class grants access to those resources. + + History: + + created 5/5/2014 + +**************************************************************************/ + +#include "Common.h" + +/************************************************************************** + + PAGEABLE CODE + +**************************************************************************/ + +#ifdef ALLOC_PRAGMA +#pragma code_seg("PAGE") +#endif // ALLOC_PRAGMA + + +CSensor::CSensor( + _In_ CCaptureDevice *Device, + _In_ ULONG PinCount +) + : m_Device(Device) + , m_PinCount(PinCount) + , m_HardwareSimulation(nullptr) + , m_Synthesizer(nullptr) + , m_CapturePin(nullptr) + , m_VideoInfoHeader(nullptr) + , m_InterruptTime(nullptr) + , m_LastMappingsCompleted(nullptr) + , m_PreviewIndex(INVALID_PIN_INDEX) + , m_StillIndex(INVALID_PIN_INDEX) + , m_VideoIndex(INVALID_PIN_INDEX) + , m_FilterInstanceCount(0) + , m_pIspSettings(nullptr) + , m_PfsFrameLimit(0) + , m_PfsLoopLimit(0) +{ + PAGED_CODE(); + NT_ASSERT(Device); + NT_ASSERT(PinCount); +} + + +CSensor::~CSensor() +{ + PAGED_CODE(); + for(ULONG i=0; i < m_PinCount; i++) + { + if( m_HardwareSimulation) + { + SAFE_DELETE( m_HardwareSimulation[i] ); + } + if( m_Synthesizer ) + { + SAFE_DELETE( m_Synthesizer[i] ); + } + if( m_CapturePin ) + { + m_CapturePin[i] = nullptr; + } + if( m_VideoInfoHeader ) + { + SAFE_DELETE( m_VideoInfoHeader[i] ); + } + } + + SAFE_DELETE_ARRAY( m_pIspSettings ); + SAFE_DELETE_ARRAY( m_HardwareSimulation ); + SAFE_DELETE_ARRAY( m_Synthesizer ); + SAFE_DELETE_ARRAY( m_CapturePin ); + SAFE_DELETE_ARRAY( m_VideoInfoHeader ); + SAFE_DELETE_ARRAY( m_InterruptTime ); + SAFE_DELETE_ARRAY( m_LastMappingsCompleted); +} + + +NTSTATUS +CSensor:: +AddFilter(PKSFILTER pFilter) +{ + PAGED_CODE(); + + // If your sensor object needs to keep track of the Filters that are + // attached to it, you would do that here. We only want to keep an + // outstanding filter count so we know when to reset our defaults. + UNREFERENCED_PARAMETER(pFilter); + + LONG Count = IncrementFilterCount(); + NTSTATUS Status= STATUS_SUCCESS; + + // If this is the first filter, reprogram the sensor. + if( Count==1 ) + { + Status = ProgramDefaults(); + if( !NT_SUCCESS(Status) ) + { + Count = DecrementFilterCount(); + } + } + DBG_LEAVE("(Count=%d)=0x%08X", Count, Status); + return Status; +} + +NTSTATUS +CSensor:: +RemoveFilter(PKSFILTER pFilter) +{ + PAGED_CODE(); + + // If your sensor object needs to keep track of the Filters that are + // attached to it, you would clean up here. We only want to keep an + // outstanding filter count so we know when to reset our defaults. + UNREFERENCED_PARAMETER(pFilter); + + LONG Count = DecrementFilterCount(); + NT_ASSERT( Count>=0 ); + + NTSTATUS Status = Count>=0 ? STATUS_SUCCESS : STATUS_INVALID_DEVICE_STATE; + + DBG_LEAVE("(Count=%d)=0x%08X", Count, Status); + return Status; +} + +NTSTATUS +CSensor:: +ProgramDefaults() +{ + PAGED_CODE(); + return STATUS_SUCCESS; +} + +NTSTATUS +CSensor:: +Initialize() +{ + PAGED_CODE(); + + m_HardwareSimulation = new (NonPagedPoolNx, 'sneS') CHardwareSimulation *[m_PinCount]; + m_Synthesizer = new (NonPagedPoolNx, 'sneS') CSynthesizer *[m_PinCount]; + m_CapturePin = new (NonPagedPoolNx, 'sneS') ICapturePin *[m_PinCount]; + m_VideoInfoHeader = new (NonPagedPoolNx, 'sneS') PKS_VIDEOINFOHEADER[m_PinCount]; + m_InterruptTime = new (NonPagedPoolNx, 'sneS') ULONG[m_PinCount]; + m_LastMappingsCompleted = new (NonPagedPoolNx, 'sneS') ULONG[m_PinCount]; + + if( !m_HardwareSimulation || + !m_Synthesizer || + !m_CapturePin || + !m_VideoInfoHeader || + !m_InterruptTime || + !m_LastMappingsCompleted ) + { + SAFE_DELETE_ARRAY( m_HardwareSimulation ); + SAFE_DELETE_ARRAY( m_Synthesizer ); + SAFE_DELETE_ARRAY( m_CapturePin ); + SAFE_DELETE_ARRAY( m_VideoInfoHeader ); + SAFE_DELETE_ARRAY( m_InterruptTime ); + SAFE_DELETE_ARRAY( m_LastMappingsCompleted); + return STATUS_INSUFFICIENT_RESOURCES; + } + + for(ULONG i=0; i=0 ) + { + DBG_ENTER("(PinIndex=%d): m_LastMappingsCompleted[%d]=%d", PinIndex, + PinIndex, m_LastMappingsCompleted[PinIndex]); + + m_InterruptTime[PinIndex]++; + + // + // 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 LastMappingCompleted = m_LastMappingsCompleted[PinIndex]; + if( PinIndex != (LONG) m_StillIndex ) + { + while( LastMappingCompleted < + m_HardwareSimulation[PinIndex]->ReadNumberOfMappingsCompleted() ) + { + // Complete a frame. + if( !NT_SUCCESS(m_CapturePin[PinIndex]->CompleteMapping()) ) + { + break; + } + LastMappingCompleted++; + } + } + else + { + CImageHardwareSimulation *pImageHw = (CImageHardwareSimulation *) m_HardwareSimulation[PinIndex]; + PKSSTREAM_POINTER pClone = pImageHw->m_pClone; + + // Just make sure the image pin actually generate a frame. + if( pClone ) + { + // Emit a photo confirmation image (we don't send off a copy of this image), + // but only if we need one for this photo and if the timestamp is valid. + if( pImageHw->m_PhotoConfirmationInfo.isRequired() && + (pClone->StreamHeader->OptionsFlags & KSSTREAM_HEADER_OPTIONSF_TIMEVALID) && + m_PreviewIndex < m_PinCount ) + { + m_HardwareSimulation[m_PreviewIndex]-> + GeneratePhotoConfirmation( + pImageHw->m_PhotoConfirmationInfo.getIndex(), + pImageHw->m_PhotoConfirmationInfo.getTime() + ); + } + + // + // Inform the capture pin that a given number of scatter / gather + // mappings have completed. + // + if( NT_SUCCESS(m_CapturePin[PinIndex]->CompleteMapping(pClone)) ) + { + LastMappingCompleted++; + } + + pImageHw->m_pClone = NULL; + } + pImageHw->m_PhotoConfirmationInfo = PHOTOCONFIRMATION_INFO(); + } + + m_LastMappingsCompleted[PinIndex] = LastMappingCompleted; + DBG_LEAVE("(PinIndex=%d): LastMappingCompleted=%d", PinIndex, LastMappingCompleted); + } +} + +NTSTATUS +CSensor:: +AcquireHardwareResources( + _In_ PKSPIN Pin, + _In_ ICapturePin *CapturePin, + _In_ PKS_VIDEOINFOHEADER VideoInfoHeader, + _Out_ CHardwareSimulation **pSim +) + +/*++ + +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: + + Pin - + The pin to acquire. + + CapturePin - + The capture pin attempting to acquire resources. When scatter / + gather mappings are completed, the capture pin 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. + + pSim - + A location to store a pointer to the simulation object for this pin. + +Return Value: + + Success / Failure + +--*/ + +{ + PAGED_CODE(); + + DBG_ENTER( "(Pin=%d, CapturePin=%p, VideoInfoHeader=%p)", Pin->Id, CapturePin, VideoInfoHeader ); + + NTSTATUS Status = STATUS_SUCCESS; + LONG lPindex = Pin->Id; + // + // If we're the first pin to go into acquire (remember we can have + // a filter in another graph going simultaneously), grab the resources for Preview + // + if( InterlockedCompareExchangePointer( + (PVOID *) &m_CapturePin[lPindex], + CapturePin, + nullptr) == nullptr) + { + m_VideoInfoHeader[lPindex] = VideoInfoHeader; + + LONG Width = VideoInfoHeader->bmiHeader.biWidth; + LONG Height= VideoInfoHeader->bmiHeader.biHeight; + + // + // If there's an old hardware simulation sitting around for some + // reason, blow it away. + // + SAFE_DELETE( m_Synthesizer[lPindex] ); + + DBG_TRACE( "biBitCount =%d", VideoInfoHeader->bmiHeader.biBitCount ); + DBG_TRACE( "biWidth =%d", VideoInfoHeader->bmiHeader.biWidth ); + DBG_TRACE( "biHeight =%d", VideoInfoHeader->bmiHeader.biHeight ); + DBG_TRACE( "biCompression=0x%08X", VideoInfoHeader->bmiHeader.biCompression ); + DBG_TRACE( "biCompression='%04s'", (PSTR) &VideoInfoHeader->bmiHeader.biCompression ); + DBG_TRACE( "AvgTimePerFrame=%lld", VideoInfoHeader->AvgTimePerFrame ); + + // + // Create the necessary type of image synthesizer. + // + if (VideoInfoHeader->bmiHeader.biBitCount == 24 && + 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_Synthesizer[lPindex] = new (NonPagedPoolNx, 'RysI') + CRGB24Synthesizer ( Width, Height ); + DBG_TRACE( "Creating CRGB24Synthesizer..." ); + } + else if (VideoInfoHeader->bmiHeader.biBitCount == 32 && + VideoInfoHeader->bmiHeader.biCompression == KS_BI_RGB) + { + // + // If we're RGB32, create a new RGB32 synth. RGB32 surfaces + // can be in either orientation. The origin is lower left if + // height < 0. Otherwise, it's upper left. + // + m_Synthesizer[lPindex] = new (NonPagedPoolNx, '23RI') + CXRGBSynthesizer ( Width, Height ); + DBG_TRACE( "Creating CXRGBSynthesizer..." ); + } + else if (VideoInfoHeader->bmiHeader.biBitCount == 16 && + (VideoInfoHeader->bmiHeader.biCompression == FOURCC_YUY2)) + { + // + // If we're YUY2, create the YUY2 synth. + // + m_Synthesizer[lPindex] = new(NonPagedPoolNx, 'YysI') CYUY2Synthesizer(Width, Height); + DBG_TRACE( "Creating CYUY2Synthesizer..." ); + + } + else if (VideoInfoHeader->bmiHeader.biBitCount == 12 && + (VideoInfoHeader->bmiHeader.biCompression == FOURCC_NV12)) + { + + m_Synthesizer[lPindex] = new(NonPagedPoolNx, 'Nv12') CNV12Synthesizer(Width, Height); + DBG_TRACE( "Creating CNV12Synthesizer..." ); + + } + else + { + Status = STATUS_INVALID_PARAMETER; + } + + if (NT_SUCCESS(Status) && !m_Synthesizer[lPindex]) + { + Status = STATUS_INSUFFICIENT_RESOURCES; + } + + if (NT_SUCCESS (Status)) + { + // + // If everything has succeeded thus far, set the capture pin. + // + m_CapturePin[lPindex] = CapturePin; + *pSim = m_HardwareSimulation[lPindex]; + + } + else + { + // + // If anything failed in here, we release the resources we've + // acquired. + // + ReleaseHardwareResources(Pin); + } + } + else + { + Status = STATUS_INSUFFICIENT_RESOURCES; + } + + DBG_LEAVE( "(Pin=%d, CapturePin=%p, VideoInfoHeader=%p) = 0x%08X", Pin->Id, CapturePin, VideoInfoHeader, Status ); + + return Status; + +} + +/*************************************************/ + + +NTSTATUS +CSensor:: +Start ( + _In_ PKSPIN Pin +) + +/*++ + +Routine Description: + + Start the capture device based on the video info header we were told + about when resources were acquired. + +Arguments: + + Pin - + The pin to start + +Return Value: + + Success / Failure + +--*/ + +{ + PAGED_CODE(); + + DBG_ENTER( "( Pin=%d )\n", Pin->Id ) ; + + LONG lPindex = Pin->Id; + + m_LastMappingsCompleted[lPindex] = 0; + m_InterruptTime[lPindex] = 0; + + NT_ASSERT( m_VideoInfoHeader[lPindex] != nullptr ); + if( !m_VideoInfoHeader[lPindex] ) + { + return STATUS_INVALID_DEVICE_STATE; + } + + // Ideally we'd do this when the filter is constructed; but the + // simulation is constructed first and these values aren't used until + // we call start. + + if(lPindex != (LONG) m_StillIndex) + { + return + m_HardwareSimulation[lPindex] -> Start ( + m_Synthesizer[lPindex], + m_VideoInfoHeader[lPindex] -> AvgTimePerFrame, + m_VideoInfoHeader[lPindex] -> bmiHeader.biWidth, + ABS (m_VideoInfoHeader[lPindex] -> bmiHeader.biHeight), + m_VideoInfoHeader[lPindex] -> bmiHeader.biSizeImage + ); + } + else + { + CImageHardwareSimulation *pHwSim = (CImageHardwareSimulation *) m_HardwareSimulation[lPindex] ; + + // The following is necessary if Per-Frame Settings are active. + if( IsVPSActive() ) + { + // Program the simulation with our settings + pHwSim->SetPFS( m_pIspSettings, m_PfsFrameLimit, m_PfsLoopLimit ); + } + else + { + // Clear out any previous settings, if they still exist. + pHwSim->SetPFS(nullptr, 0, 0); + } + + // Init to the default frame rate. + LONGLONG TimePerFrame = m_VideoInfoHeader[lPindex]->AvgTimePerFrame; + DBG_TRACE("Image Pin's AvgTimePerFrame=%lld", TimePerFrame); + + // Query our control for the max frame rate and convert it into a "PERFORMACE TIME". + CExtendedProperty MaxFrameRate; + GetPhotoMaxFrameRate( &MaxFrameRate ); + LARGE_INTEGER PerformanceTime = { MaxFrameRate.m_Value.Value.ratio.LowPart }; + LONGLONG Frequency = MaxFrameRate.m_Value.Value.ratio.HighPart; + + // Handle an user-specified frame-rate override. + if( Frequency != 0 ) + { + DBG_TRACE( "MaxFrameRate = %lld/%lld", PerformanceTime.QuadPart, Frequency ); + + SetPhotoFrameRate( + KSCONVERT_PERFORMANCE_TIME( Frequency, PerformanceTime ) + ); + } + + CExtendedPhotoMode Mode; + GetPhotoMode( &Mode ); + + NTSTATUS status = pHwSim -> Start ( + m_Synthesizer[lPindex], + m_VideoInfoHeader[lPindex] -> bmiHeader.biWidth, + ABS (m_VideoInfoHeader[lPindex] -> bmiHeader.biHeight), + m_VideoInfoHeader[lPindex] -> bmiHeader.biSizeImage, + (Mode.Flags == 0 ? PinNormalMode : PinBurstMode) + ); + + if(NT_SUCCESS(status)) + { + status = pHwSim -> SetClock(Pin); + } + + DBG_LEAVE( "( Pin=%d )=0x%08X\n", Pin->Id, status ); + + return status; + } +} + +/*************************************************/ + + +NTSTATUS +CSensor:: +Pause ( + _In_ PKSPIN Pin, + _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: + + Pin - + The pin to pause. + + 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[Pin->Id]->Pause(Pausing); +} + +/*************************************************/ + + +NTSTATUS +CSensor:: +Stop ( + _In_ PKSPIN Pin +) + +/*++ + +Routine Description: + + Stop the capture device. + +Arguments: + + None + +Return Value: + + Success / Failure + +--*/ + +{ + + PAGED_CODE(); + + LONG lPindex = Pin->Id; + CHardwareSimulation *pHwSim = m_HardwareSimulation[lPindex]; + + if( !pHwSim ) + { + return STATUS_INVALID_DEVICE_STATE; + } + + NTSTATUS Status = pHwSim->Stop(); + + if( lPindex == (LONG) m_StillIndex ) + { + // Clear out any previous Per Frame Settings, if they still exist. + ((CImageHardwareSimulation *)pHwSim)->SetPFS(nullptr, 0, 0) ; + } + + return Status; +} + +NTSTATUS +CSensor:: +Reset( + _In_ PKSPIN Pin +) +{ + PAGED_CODE(); + + if( m_StillIndex > m_PinCount || + !m_HardwareSimulation[m_StillIndex] ) + { + return STATUS_INVALID_DEVICE_STATE; + } + + if( Pin->Id == m_StillIndex ) + { + return Reset(); + } + return STATUS_SUCCESS; +} + +//Resets both filter's first PIN +NTSTATUS +CSensor:: +Reset( +) +{ + PAGED_CODE(); + + if( m_StillIndex > m_PinCount || + !m_HardwareSimulation[m_StillIndex] ) + { + return STATUS_INVALID_DEVICE_STATE; + } + + CHardwareSimulation *pHwSim = m_HardwareSimulation[m_StillIndex]; + + pHwSim->Reset(); + m_LastMappingsCompleted[m_StillIndex] = pHwSim->ReadNumberOfMappingsCompleted(); + + for( ULONG i=0; i m_PinCount || + !m_HardwareSimulation[PinId] || + m_HardwareSimulation[PinId]->GetState() != PinRunning ) + { + return STATUS_INVALID_DEVICE_STATE; + } + + return + ((CImageHardwareSimulation *)m_HardwareSimulation[PinId])->Trigger(mode); + +} + +NTSTATUS +CSensor:: +SetPhotoFrameRate( + _In_ ULONGLONG TimePerFrame +) +/*++ + +Routine Description: + + For a photo pin, set the desired frame rate. + +Arguments: + + TimePerFrame - + Time in 100ns between each frame. + +Return Value: + + Success / Failure + +--*/ +{ + PAGED_CODE(); + + if( m_StillIndex > m_PinCount || + !m_VideoInfoHeader[m_StillIndex] || + !m_HardwareSimulation[m_StillIndex] ) + { + return STATUS_INVALID_DEVICE_STATE; + } + + ULONGLONG AvgTimePerFrame = + (ULONGLONG) m_VideoInfoHeader[m_StillIndex]->AvgTimePerFrame; + + DBG_TRACE( "TimePerFrame=%lld, AvgTimePerFrame=%lld", TimePerFrame, AvgTimePerFrame ); + + // Handle a reset back to the standard frame rate. + if( TimePerFrame == 0 ) + { + TimePerFrame = AvgTimePerFrame; + } + // Handle an user-specified frame-rate override. + else + { + // It is unrealistic to allow a frame rate faster than the rate specified for this format. + TimePerFrame = max( TimePerFrame, AvgTimePerFrame ); + } + + DBG_TRACE( "Setting new TimePerFrame=%lld", TimePerFrame ); + + return + ((CImageHardwareSimulation *) m_HardwareSimulation[m_StillIndex])->SetPhotoFrameRate( TimePerFrame ); +} + +ULONGLONG +CSensor:: +GetPhotoFrameRate() +/*++ + +Routine Description: + + Get the desired frame rate for a photo pin. + +Arguments: + + None + +Return Value: + + Time in 100ns between each frame. + +--*/ +{ + PAGED_CODE(); + + if( m_StillIndex > m_PinCount || + !m_HardwareSimulation[m_StillIndex] ) + { + NT_ASSERT(FALSE); + return 0; + } + + CImageHardwareSimulation *pSim = (CImageHardwareSimulation *) (m_HardwareSimulation[m_StillIndex]); + return pSim->GetPhotoFrameRate( ); +} + +NTSTATUS +CSensor:: +SetFlashStatus( + _In_ ULONGLONG ulFlashStatus +) +{ + PAGED_CODE(); + + if( m_StillIndex > m_PinCount || + !m_HardwareSimulation[m_StillIndex] ) + { + NT_ASSERT(FALSE); + return 0; + } + + return + ((CImageHardwareSimulation *) m_HardwareSimulation[m_StillIndex])->SetFlashStatus (ulFlashStatus); +} + +/*************************************************/ + + +NTSTATUS +CSensor:: +SetPinMode( + _In_ ULONGLONG Flags, + _In_ ULONG PastBuffers +) +{ + PAGED_CODE(); + + if( m_StillIndex > m_PinCount || + !m_HardwareSimulation[m_StillIndex] ) + { + return STATUS_INVALID_DEVICE_STATE; + } + + return + ((CImageHardwareSimulation *) m_HardwareSimulation[m_StillIndex])-> + SetMode( Flags, PastBuffers ); +} + +NTSTATUS +CSensor:: +GetQPC( + _Out_ PULONGLONG TriggerTime +) +{ + PAGED_CODE(); + + if( m_StillIndex > m_PinCount || + !m_HardwareSimulation[m_StillIndex] ) + { + return STATUS_INVALID_DEVICE_STATE; + } + + *TriggerTime = + ((CImageHardwareSimulation *) m_HardwareSimulation[m_StillIndex])-> + GetTriggerTime(); + + return STATUS_SUCCESS; +} + +NTSTATUS +CSensor:: +SetQPC( + _In_ ULONGLONG TriggerTime +) +{ + PAGED_CODE(); + + if( m_StillIndex > m_PinCount || + !m_HardwareSimulation[m_StillIndex] ) + { + return STATUS_INVALID_DEVICE_STATE; + } + + ((CImageHardwareSimulation *) m_HardwareSimulation[m_StillIndex])-> + SetTriggerTime( TriggerTime ); + + return STATUS_SUCCESS; +} + +// +// Program the Per Frame Settings for simulation +// We do it before calling start so we can be ready +// to program our simulation's hardware. +// +// Note: We make a local copy. +// +NTSTATUS +CSensor:: +SetPFS( + _In_ ISP_FRAME_SETTINGS *pIspSettings, + _In_ ULONG FrameLimit, + _In_ ULONG LoopLimit +) +/*++ + +Routine Description: + + Program the Per Frame Settings for simulation + We do it before calling start so we can be ready + to program our simulation's hardware. + + Note: We make a local copy. + +Arguments: + + pIspSettings - + The Per-Frame Settings to use. + + FrameLimit - + The number of frames to capture. + + LoopLimit - + The number of times to loop over the sequence. + +Return Value: + + Success / Failure. + +--*/ +{ + PAGED_CODE(); + KScopedMutex Lock(m_SensorMutex); + + // Set the current ISP settings and free any prior. + // This has to be done at simulation start or stop or we have + // a synchronization problem. + SAFE_DELETE_ARRAY( m_pIspSettings ); + + if( pIspSettings ) + { + m_pIspSettings = new (NonPagedPoolNx) ISP_FRAME_SETTINGS[FrameLimit]; + if( !m_pIspSettings ) + { + m_PfsFrameLimit = 0; + m_PfsLoopLimit = 0; + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory( m_pIspSettings, pIspSettings, FrameLimit*sizeof(ISP_FRAME_SETTINGS) ); + m_PfsLoopLimit = LoopLimit; + m_PfsFrameLimit = FrameLimit; + } + else + { + m_PfsFrameLimit = 0; + m_PfsLoopLimit = 0; + } + + return STATUS_SUCCESS; +} + +/*************************************************/ + + +void +CSensor:: +ReleaseHardwareResources ( + _In_ PKSPIN Pin +) + +/*++ + +Routine Description: + + Release hardware resources. This should only be called by + an object which has acquired them. + +Arguments: + + Pin - + The pin to acquire. + +Return Value: + + None + +--*/ + +{ + + PAGED_CODE(); + + DBG_ENTER( "( Pin=%d )\n", Pin->Id ) ; + + LONG lPindex = Pin->Id; + // + // Blow away the image synth. + // + m_HardwareSimulation[lPindex]->Reset(); + + SAFE_DELETE( m_Synthesizer[lPindex] ); + m_VideoInfoHeader[lPindex] = NULL; + + // + // Release our "lock" on hardware resources. This will allow another + // pin (perhaps in another graph) to acquire them. + // + InterlockedExchangePointer( + (PVOID *) &(m_CapturePin[lPindex]), + nullptr + ); + + DBG_LEAVE( "( Pin=%d )\n", Pin->Id ) ; +} + +ULONG +CSensor:: +ProgramScatterGatherMappings ( + _In_ PKSPIN Pin, + _In_ PKSSTREAM_POINTER *Clone, + _In_ PUCHAR *Buffer, + _In_ PKSMAPPING Mappings, + _In_ ULONG MappingsCount +) + +/*++ + +Routine Description: + + Program the scatter / gather mappings for the "fake" hardware. + + Punts the request to the H/W simulation (CHardwareSimulation) object for + that pin. + +Arguments: + + Pin - + The pin to program. + + Clone - + A stream pointer. + + 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[Pin->Id]->ProgramScatterGatherMappings ( + Clone, + Buffer, + Mappings, + MappingsCount, + sizeof (KSMAPPING) + ); +} + +// Expose a pointer to the global ISP_FRAME_SETTINGS +ISP_FRAME_SETTINGS * +CSensor:: +GetGlobalIspSettings() +{ + PAGED_CODE(); + return &m_GlobalIspSettings; +} + +void +CSensor:: +UpdateZoom(void) +{ + PAGED_CODE(); +} + +// +// This "Null" definition fails the request as if it doesn't exist in the +// automation table. This is the default behavior for most properties in +// CSensor. +// +#define DEFINE_NULL_PROPERTY_FUNC( _sal_, type, func ) \ +NTSTATUS \ +func( \ + _sal_ type *Value \ + ) \ +{ \ + PAGED_CODE(); \ + UNREFERENCED_PARAMETER(Value); \ + return STATUS_NOT_FOUND; \ +} + +#define DEFINE_NULL_SIZEOF_FUNC( func ) \ +ULONG \ +func() \ +{ \ + PAGED_CODE(); \ + return 0; \ +} + +#define DEFINE_NULL_PROPERTY_GET( T, type, name ) \ + DEFINE_NULL_PROPERTY_FUNC( _Inout_, type, T::Get##name ) + +#define DEFINE_NULL_PROPERTY_SET( T, type, name ) \ + DEFINE_NULL_PROPERTY_FUNC( _In_, type, T::Set##name ) + +#define DEFINE_NULL_PROPERTY( T, type, name ) \ + DEFINE_NULL_PROPERTY_GET( T, type, name ) \ + DEFINE_NULL_PROPERTY_SET( T, type, name ) + +#define DEFINE_NULL_PROPERTY_ASYNC( T, type, name ) \ + DEFINE_NULL_PROPERTY_GET( T, type, name ) \ + \ +NTSTATUS \ +T:: \ +Set##name##Async( \ + _In_ type *pProperty, \ + _In_ CNotifier *Notifier \ + ) \ +{ \ + PAGED_CODE(); \ + UNREFERENCED_PARAMETER(pProperty); \ + UNREFERENCED_PARAMETER(Notifier); \ + return STATUS_NOT_FOUND; \ +} \ + \ +NTSTATUS \ +T:: \ +Cancel##name() \ +{ \ + PAGED_CODE(); \ + return STATUS_UNSUCCESSFUL; \ +} + +#define DEFINE_NULL_PROPERTY_VARSIZE_ASYNC( T, type, name ) \ + DEFINE_NULL_PROPERTY_ASYNC( T, type, name ) \ + \ +ULONG \ +T:: \ +SizeOf##name() \ +{ \ + PAGED_CODE(); \ + return 0; \ +} + +// +// Define a bunch of "not implemented" function stubs. +// +DEFINE_NULL_PROPERTY_ASYNC(CSensor, KSPROPERTY_CAMERACONTROL_REGION_OF_INTEREST_S, FocusRect) +DEFINE_NULL_PROPERTY_ASYNC(CSensor, CExtendedPhotoMode, PhotoMode) + +DEFINE_NULL_PROPERTY(CSensor, KSPROPERTY_VIDEOCONTROL_MODE_S, VideoControlMode) +DEFINE_NULL_PROPERTY(CSensor, KSPROPERTY_CAMERACONTROL_FLASH_S, Flash) +DEFINE_NULL_PROPERTY_GET(CSensor, KSPROPERTY_CAMERACONTROL_IMAGE_PIN_CAPABILITY_S, PinDependence) + +DEFINE_NULL_PROPERTY(CSensor, CExtendedProperty, TriggerTime) +DEFINE_NULL_PROPERTY(CSensor, CExtendedProperty, TorchMode) +DEFINE_NULL_PROPERTY(CSensor, CExtendedProperty, ExtendedFlash) + +DEFINE_NULL_PROPERTY_GET(CSensor, CExtendedProperty, PhotoFrameRate) +DEFINE_NULL_PROPERTY_ASYNC(CSensor, CExtendedProperty, PhotoMaxFrameRate) +DEFINE_NULL_PROPERTY_ASYNC(CSensor, CExtendedProperty, WarmStart) +DEFINE_NULL_PROPERTY(CSensor, CExtendedMaxVideoFpsForPhotoRes, MaxVideoFpsForPhotoRes) +DEFINE_NULL_PROPERTY_GET(CSensor, CExtendedFieldOfView, FieldOfView) +DEFINE_NULL_PROPERTY_GET(CSensor, CExtendedCameraAngleOffset, CameraAngleOffset) +DEFINE_NULL_PROPERTY_GET(CSensor, KSCAMERA_EXTENDEDPROP_FOCUSSTATE, FocusState) +DEFINE_NULL_PROPERTY_ASYNC(CSensor, CExtendedVidProcSetting, Focus) +DEFINE_NULL_PROPERTY_ASYNC(CSensor, CExtendedProperty, Iso) +DEFINE_NULL_PROPERTY_ASYNC(CSensor, CExtendedVidProcSetting, IsoAdvanced) +DEFINE_NULL_PROPERTY_ASYNC(CSensor, CExtendedEvCompensation, EvCompensation) +DEFINE_NULL_PROPERTY_ASYNC(CSensor, CExtendedVidProcSetting, WhiteBalance) +DEFINE_NULL_PROPERTY_ASYNC(CSensor, CExtendedVidProcSetting, Exposure) +DEFINE_NULL_PROPERTY_ASYNC(CSensor, CExtendedProperty, SceneMode) +DEFINE_NULL_PROPERTY_ASYNC(CSensor, CExtendedProperty, Thumbnail) +DEFINE_NULL_PROPERTY(CSensor, CExtendedProperty, PhotoConfirmation) +DEFINE_NULL_PROPERTY(CSensor, CExtendedProperty, FocusPriority) +DEFINE_NULL_PROPERTY(CSensor, CExtendedProperty, VideoHDR) +DEFINE_NULL_PROPERTY(CSensor, CExtendedProperty, VFR) +DEFINE_NULL_PROPERTY(CSensor, CExtendedVidProcSetting, Zoom) +DEFINE_NULL_PROPERTY(CSensor, CExtendedProperty, VideoStabilization) +DEFINE_NULL_PROPERTY(CSensor, CExtendedProperty, Histogram) +DEFINE_NULL_PROPERTY(CSensor, CExtendedMetadata, Metadata) +DEFINE_NULL_PROPERTY(CSensor, CExtendedProperty, OpticalImageStabilization) +DEFINE_NULL_PROPERTY(CSensor, CExtendedProperty, OptimizationHint) +DEFINE_NULL_PROPERTY(CSensor, CExtendedProperty, AdvancedPhoto) +DEFINE_NULL_PROPERTY(CSensor, CExtendedVidProcSetting, FaceDetection) + +DEFINE_NULL_PROPERTY(CSensor, KSPROPERTY_CAMERACONTROL_VIDEOSTABILIZATION_MODE_S, VideoStabMode) + +DEFINE_NULL_PROPERTY(CSensor, KSPROPERTY_CAMERACONTROL_S, Exposure) +DEFINE_NULL_PROPERTY(CSensor, KSPROPERTY_CAMERACONTROL_S, Focus) +DEFINE_NULL_PROPERTY(CSensor, KSPROPERTY_CAMERACONTROL_S, Zoom) +DEFINE_NULL_PROPERTY(CSensor, KSPROPERTY_CAMERACONTROL_S, ZoomRelative) +DEFINE_NULL_PROPERTY(CSensor, KSPROPERTY_CAMERACONTROL_S, Pan) +DEFINE_NULL_PROPERTY(CSensor, KSPROPERTY_CAMERACONTROL_S, Roll) +DEFINE_NULL_PROPERTY(CSensor, KSPROPERTY_CAMERACONTROL_S, Tilt) +DEFINE_NULL_PROPERTY(CSensor, KSPROPERTY_CAMERACONTROL_FOCAL_LENGTH_S, FocalLength) + +DEFINE_NULL_PROPERTY(CSensor, KSPROPERTY_VIDEOPROCAMP_S, BacklightCompensation); +DEFINE_NULL_PROPERTY(CSensor, KSPROPERTY_VIDEOPROCAMP_S, Brightness); +DEFINE_NULL_PROPERTY(CSensor, KSPROPERTY_VIDEOPROCAMP_S, Contrast); +DEFINE_NULL_PROPERTY(CSensor, KSPROPERTY_VIDEOPROCAMP_S, Hue); +DEFINE_NULL_PROPERTY(CSensor, KSPROPERTY_VIDEOPROCAMP_S, WhiteBalance); +DEFINE_NULL_PROPERTY(CSensor, KSPROPERTY_VIDEOPROCAMP_S, PowerlineFreq); + +DEFINE_NULL_PROPERTY_GET(CSensor, CRoiConfig, RoiConfigCaps); +DEFINE_NULL_PROPERTY_VARSIZE_ASYNC(CSensor, CRoiProperty, Roi); + +_Success_(return == 0) +NTSTATUS +CSensor:: +GetPfsCaps( + _Inout_opt_ KSCAMERA_PERFRAMESETTING_CAP_HEADER *Caps, + _Inout_ ULONG *Size +) +{ + PAGED_CODE(); + UNREFERENCED_PARAMETER(Caps); + *Size = 0; + return STATUS_NOT_FOUND; +} diff --git a/avscamera/sys/Sensor.h b/avscamera/sys/Sensor.h new file mode 100644 index 000000000..37adc008c --- /dev/null +++ b/avscamera/sys/Sensor.h @@ -0,0 +1,554 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + Sensor.h + + Abstract: + + Base Sensor class. Derive from this class to implement an interface + to your hardware sensor object. + + This class provides a set of "knobs" to control a model camera. + + This class also controls access to the pin simulations. Most cameras + have a limited set of ISP resources and can only instantiate a fixed + number of pins. This class grants access to those resources. Other + than that, it's just an interface class. + + History: + + created 5/5/2014 + +**************************************************************************/ + +#pragma once + +// +// The following DECLARE_PROPERTY_XXX() macros are used to create consistent +// function names for each control. +// +#define DECLARE_PROPERTY_GET( type, name ) \ + virtual \ + NTSTATUS \ + Get##name( \ + _Inout_ type *pProperty \ + ); \ + +#define DECLARE_PROPERTY_SIZEOF( name ) \ + virtual \ + ULONG \ + SizeOf##name(); + +#define DECLARE_PROPERTY_SET( type, name ) \ + virtual \ + NTSTATUS \ + Set##name( \ + _In_ type *pProperty \ + ); + +#define DECLARE_PROPERTY_SET_ASYNC( type, name ) \ + virtual \ + NTSTATUS \ + Set##name##Async( \ + _In_ type *pProperty, \ + _In_ CNotifier *Notifier \ + ); + +#define DECLARE_PROPERTY_CANCEL( type, name ) \ + virtual \ + NTSTATUS \ + Cancel##name(); + +#define DECLARE_PROPERTY( type, name ) \ + DECLARE_PROPERTY_GET( type, name ) \ + DECLARE_PROPERTY_SET( type, name ) + +#define DECLARE_PROPERTY_ASYNC_NOCANCEL( type, name ) \ + DECLARE_PROPERTY_GET( type, name ) \ + DECLARE_PROPERTY_SET_ASYNC( type, name ) + +#define DECLARE_PROPERTY_ASYNC( type, name ) \ + DECLARE_PROPERTY_ASYNC_NOCANCEL( type, name ) \ + DECLARE_PROPERTY_CANCEL( type, name ) + +#define DECLARE_PROPERTY_VARSIZE_ASYNC( type, name ) \ + DECLARE_PROPERTY_ASYNC( type, name ) \ + DECLARE_PROPERTY_SIZEOF( name ) + +#define DECLARE_PROPERTY_VARSIZE_ASYNC_NOCANCEL( type, name ) \ + DECLARE_PROPERTY_ASYNC_NOCANCEL( type, name ) \ + DECLARE_PROPERTY_SIZEOF( name ) + + +// +// CNotifer +// +// A CNotiver is just a wrapper on a KS event. It holds the parameters +// needed to invoke the KS event successfully and it is referenced counted +// to make sure the filter does not go away before your sensor is done with +// the notifier. +// +// These objects should be instantiated in the filter and a reference +// held in your sensor object. +// +class CNotifier : public CRef +{ +private: + PVOID m_Object; + GUID m_EventSet; + ULONG m_EventId; + ULONG m_DataSize; + PVOID m_Data; + PFNKSGENERATEEVENTCALLBACK m_Callback; + PVOID m_CallbackContext; + +public: + CNotifier( + _In_ PKSFILTER Filter, + _In_ const GUID *EventSet, + _In_ ULONG EventId, + _In_ ULONG DataSize=0, + _In_opt_ PVOID Data=nullptr, + _In_opt_ PFNKSGENERATEEVENTCALLBACK Callback=nullptr, + _In_opt_ PVOID CallbackContext=nullptr + ) + : m_Object(Filter) + , m_EventSet(*EventSet) + , m_EventId(EventId) + , m_DataSize(DataSize) + , m_Data(Data) + , m_Callback(Callback) + , m_CallbackContext(CallbackContext) + {} + + CNotifier( + _In_ PKSPIN Pin, + _In_ const GUID *EventSet, + _In_ ULONG EventId, + _In_ ULONG DataSize=0, + _In_opt_ PVOID Data=nullptr, + _In_opt_ PFNKSGENERATEEVENTCALLBACK Callback=nullptr, + _In_opt_ PVOID CallbackContext=nullptr + ) + : m_Object(Pin) + , m_EventSet(*EventSet) + , m_EventId(EventId) + , m_DataSize(DataSize) + , m_Data(Data) + , m_Callback(Callback) + , m_CallbackContext(CallbackContext) + {} + + void + Set() + { + KsGenerateEvents(m_Object, &m_EventSet, m_EventId, m_DataSize, m_Data, m_Callback, m_CallbackContext); + } +}; + +class CSensor +{ +protected: + + CCaptureDevice *m_Device; + + ULONG m_PinCount; + LONG m_FilterInstanceCount; + + // + // The AVStream filter object associated with this CCaptureFilter. + // + CHardwareSimulation **m_HardwareSimulation; + CSynthesizer **m_Synthesizer; + ICapturePin **m_CapturePin; + + PKS_VIDEOINFOHEADER *m_VideoInfoHeader; + + // + // The number of ISR's that have occurred since capture started. + // + ULONG *m_InterruptTime; + + // + // The last reading of mappings completed. + // + ULONG *m_LastMappingsCompleted; + + static const ULONG INVALID_PIN_INDEX = 0xffffffff; + + ULONG m_PreviewIndex; + ULONG m_StillIndex; + ULONG m_VideoIndex; + + // Access control mutex. + KMutex m_SensorMutex; + + ULONG m_PfsLoopLimit; + ULONG m_PfsFrameLimit; + ISP_FRAME_SETTINGS *m_pIspSettings; + + ISP_FRAME_SETTINGS m_GlobalIspSettings; + +public: + + CSensor( + _In_ CCaptureDevice *Device, + _In_ ULONG PinCount + ); + + // + // ~CSensor(): + // + // The h/w Sensor destructor. + // + virtual + ~CSensor( ); + + virtual + NTSTATUS + Initialize(); + + virtual + NTSTATUS + ProgramDefaults(); + + virtual + void + Interrupt( + _In_ LONG PinIndex + ); + + virtual + PDEVICE_OBJECT + GetDeviceObject() + { + return m_Device->GetDeviceObject(); + } + + // + // 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. + // + virtual + NTSTATUS + AcquireHardwareResources( + _In_ PKSPIN Pin, + _In_ ICapturePin *CaptureSink, + _In_ PKS_VIDEOINFOHEADER VideoInfoHeader, + _Out_ CHardwareSimulation **pSim + ); + + // + // ReleaseHardwareResources(): + // + // Called to release hardware resources for the device. + // + virtual + void + ReleaseHardwareResources( + _In_ PKSPIN Pin + //_In_ CHardwareSimulation *pSim + ); + + // + // Start(): + // + // Called to start the hardware simulation. This causes us to simulate + // interrupts, simulate filling buffers with synthesized data, etc... + // + NTSTATUS + Start ( + _In_ PKSPIN Pin + ); + + // + // 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_ PKSPIN Pin, + _In_ BOOLEAN Pausing + ); + + NTSTATUS + Trigger( + _In_ ULONG PinId, + _In_ LONG mode + ); + + NTSTATUS + SetQPC( + _In_ ULONGLONG QPC + ); + + NTSTATUS + GetQPC( + _Out_ PULONGLONG QPC + ); + + NTSTATUS + SetPinMode( + _In_ ULONGLONG Flags, + _In_ ULONG PastBuffers + ); + + NTSTATUS + SetPhotoFrameRate( + _In_ ULONGLONG TimePerFrame + ); + + ULONGLONG + GetPhotoFrameRate(); + + NTSTATUS + SetFlashStatus( + _In_ ULONGLONG ulFlashStatus + ); + + bool + IsPreviewIndex( + _In_ ULONG Index + ) + { + return m_PreviewIndex == Index ; + } + + bool + IsStillIndex( + _In_ ULONG Index + ) + { + return m_StillIndex == Index ; + } + + bool + IsVideoIndex( + _In_ ULONG Index + ) + { + return m_VideoIndex == Index ; + } + + bool + IsValidIndex( + _In_ ULONG Index + ) + { + return Index < GetPinCount() ; + } + + inline ULONG GetPreviewIndex() + { + return m_PreviewIndex; + } + + inline ULONG GetStillIndex() + { + return m_StillIndex; + } + + inline ULONG GetVideoIndex() + { + return m_VideoIndex; + } + + //Not thread safe, for User mode notification only. + LONG + GetFilterCount() + { + return m_FilterInstanceCount; + } + + NTSTATUS + AddFilter(PKSFILTER pFilter); + + NTSTATUS + RemoveFilter(PKSFILTER pFilter); + + ULONG + GetPinCount() + { + return m_PinCount; + } + + // + // GetFourCC + // + // Return the FourCC code for the current pin. + // + ULONG + GetFourCC( + _In_ ULONG Index + ) + { + return IndexbmiHeader.biCompression : 0; + } + + // + // 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 ( + _In_ PKSPIN + ); + + NTSTATUS + Reset( + _In_ PKSPIN + ); + + NTSTATUS + Reset( + ); + + + // + // 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_ PKSPIN Pin, + _In_ PKSSTREAM_POINTER *Clone, + _In_ PUCHAR *Buffer, + _In_ PKSMAPPING Mappings, + _In_ ULONG MappingsCount + ); + + // Lets you know if Variable Photo Sequence is active. + // Note: This doesn't tell you if the pin is running. + virtual + BOOLEAN + IsVPSActive() + { + // VPS isn't supported by default. + return FALSE; + } + + // Fetch a pointer to the global ISP_FRAME_SETTINGS + virtual + ISP_FRAME_SETTINGS * + GetGlobalIspSettings(); + + // Program the Per Frame Settings for simulation + // We do it before calling start so we can be ready + // to program our simulation's hardware. + // Note: We make a local copy. + virtual + NTSTATUS + SetPFS( + _In_ ISP_FRAME_SETTINGS *pIspSettings, + _In_ ULONG FrameLimit, + _In_ ULONG LoopLimit + ); + + virtual + void + UpdateZoom(void); + +protected: + LONG + IncrementFilterCount() + { + return InterlockedIncrement(&m_FilterInstanceCount); + } + + LONG + DecrementFilterCount() + { + return InterlockedDecrement(&m_FilterInstanceCount); + } + +public: + + DECLARE_PROPERTY( KSPROPERTY_VIDEOCONTROL_MODE_S, VideoControlMode ); + + DECLARE_PROPERTY_GET( KSCAMERA_EXTENDEDPROP_FOCUSSTATE, FocusState ); + DECLARE_PROPERTY_ASYNC( KSPROPERTY_CAMERACONTROL_REGION_OF_INTEREST_S, FocusRect ); + DECLARE_PROPERTY( KSPROPERTY_CAMERACONTROL_FLASH_S, Flash ); + DECLARE_PROPERTY_GET( KSPROPERTY_CAMERACONTROL_IMAGE_PIN_CAPABILITY_S, PinDependence ); + + DECLARE_PROPERTY_ASYNC( CExtendedPhotoMode, PhotoMode ); + DECLARE_PROPERTY_GET( CExtendedProperty, PhotoFrameRate ); + DECLARE_PROPERTY_ASYNC( CExtendedProperty, PhotoMaxFrameRate ); + DECLARE_PROPERTY_ASYNC( CExtendedProperty, WarmStart ); + DECLARE_PROPERTY( CExtendedMaxVideoFpsForPhotoRes, MaxVideoFpsForPhotoRes ); + DECLARE_PROPERTY_GET( CExtendedFieldOfView, FieldOfView ); + DECLARE_PROPERTY_GET( CExtendedCameraAngleOffset, CameraAngleOffset ); + DECLARE_PROPERTY( CExtendedProperty, ExtendedFlash ); + + DECLARE_PROPERTY( CExtendedProperty, TriggerTime ); + DECLARE_PROPERTY( CExtendedProperty, TorchMode ); + + DECLARE_PROPERTY_ASYNC( CExtendedVidProcSetting, Focus ); + DECLARE_PROPERTY_ASYNC( CExtendedProperty, Iso ); + DECLARE_PROPERTY_ASYNC( CExtendedVidProcSetting, IsoAdvanced ); + DECLARE_PROPERTY_ASYNC( CExtendedEvCompensation, EvCompensation ); + DECLARE_PROPERTY_ASYNC( CExtendedVidProcSetting, WhiteBalance ); + DECLARE_PROPERTY_ASYNC( CExtendedVidProcSetting, Exposure ); + DECLARE_PROPERTY( CExtendedProperty, PhotoConfirmation ); + DECLARE_PROPERTY_ASYNC( CExtendedProperty, SceneMode ); + DECLARE_PROPERTY( CExtendedProperty, FocusPriority ); + DECLARE_PROPERTY_ASYNC( CExtendedProperty, Thumbnail ); + DECLARE_PROPERTY( CExtendedProperty, VideoHDR ); + DECLARE_PROPERTY( CExtendedProperty, VFR ); + DECLARE_PROPERTY( CExtendedVidProcSetting, Zoom ); + DECLARE_PROPERTY( CExtendedProperty, VideoStabilization ); + DECLARE_PROPERTY( CExtendedProperty, Histogram ); + DECLARE_PROPERTY( CExtendedMetadata, Metadata ); + DECLARE_PROPERTY( CExtendedProperty, OpticalImageStabilization ); + DECLARE_PROPERTY( CExtendedProperty, OptimizationHint ); + DECLARE_PROPERTY( CExtendedProperty, AdvancedPhoto ); + DECLARE_PROPERTY( CExtendedVidProcSetting, FaceDetection ); + + DECLARE_PROPERTY_VARSIZE_ASYNC( CRoiProperty, Roi ); + + DECLARE_PROPERTY( KSPROPERTY_CAMERACONTROL_VIDEOSTABILIZATION_MODE_S, VideoStabMode ); + + DECLARE_PROPERTY(KSPROPERTY_CAMERACONTROL_S, Exposure); + DECLARE_PROPERTY(KSPROPERTY_CAMERACONTROL_S, Focus); + DECLARE_PROPERTY(KSPROPERTY_CAMERACONTROL_S, Zoom); + DECLARE_PROPERTY(KSPROPERTY_CAMERACONTROL_S, ZoomRelative); + DECLARE_PROPERTY(KSPROPERTY_CAMERACONTROL_S, Pan); + DECLARE_PROPERTY(KSPROPERTY_CAMERACONTROL_S, Roll); + DECLARE_PROPERTY(KSPROPERTY_CAMERACONTROL_S, Tilt); + DECLARE_PROPERTY(KSPROPERTY_CAMERACONTROL_FOCAL_LENGTH_S, FocalLength); + + DECLARE_PROPERTY(KSPROPERTY_VIDEOPROCAMP_S, BacklightCompensation); + DECLARE_PROPERTY(KSPROPERTY_VIDEOPROCAMP_S, Brightness); + DECLARE_PROPERTY(KSPROPERTY_VIDEOPROCAMP_S, Contrast); + DECLARE_PROPERTY(KSPROPERTY_VIDEOPROCAMP_S, Hue); + DECLARE_PROPERTY(KSPROPERTY_VIDEOPROCAMP_S, WhiteBalance); + DECLARE_PROPERTY(KSPROPERTY_VIDEOPROCAMP_S, PowerlineFreq); + + DECLARE_PROPERTY_GET( CRoiConfig, RoiConfigCaps ); + + _Success_(return == 0) + virtual + NTSTATUS + GetPfsCaps( + _Inout_opt_ KSCAMERA_PERFRAMESETTING_CAP_HEADER *Caps, + _Inout_ ULONG *Size + ); +}; + diff --git a/avscamera/sys/SensorSimulation.cpp b/avscamera/sys/SensorSimulation.cpp new file mode 100644 index 000000000..6d93c37f2 --- /dev/null +++ b/avscamera/sys/SensorSimulation.cpp @@ -0,0 +1,3496 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + SensorSimulation.cpp + + Abstract: + + Simulation class implementation. Derived from the base CSensor object. + + This class simulates a theoretical model camera. The camera supports + every modern camera control available and produces a simulation of its + behavior. In most cases this simulation is very very basic because we + have no actual hardware to work with and there producing a visible + effect in the simulation would be difficult or expensive to do. + + History: + + created 5/8/2014 + +**************************************************************************/ + +#include "Common.h" + +/************************************************************************** + + PAGEABLE CODE + +**************************************************************************/ + +#ifdef ALLOC_PRAGMA +#pragma code_seg("PAGE") +#endif // ALLOC_PRAGMA + +// +// The following macro is used to define a legacy control's range and +// default value. We use the macro simply to minimize the verbosity of +// our declarations. +// +#define DEFINE_LEGACY_CTRL_CAPS( Control, Minimum, Maximum, SteppingDelta, DefValue ) \ +/*-------------------------------------------------------------- \ + This structure defines what the control is capable of. \ +--------------------------------------------------------------*/ \ +KSPROPERTY_STEPPING_LONG Control##RangeAndStep[] = \ +{ \ + { \ + SteppingDelta, /* SteppingDelta */ \ + 0, /* Reserved */ \ + Minimum, /* Minimum */ \ + Maximum /* Maximum */ \ + } \ +}; \ + \ +/* This is the default control value. */ \ +const LONG Control##Default = DefValue; \ + \ +KSPROPERTY_MEMBERSLIST Control##MembersList[] = \ +{ \ + { \ + { \ + KSPROPERTY_MEMBER_STEPPEDRANGES, \ + sizeof (Control##RangeAndStep), \ + SIZEOF_ARRAY(Control##RangeAndStep), \ + 0 \ + }, \ + (PVOID)Control##RangeAndStep, \ + }, \ + { \ + { \ + KSPROPERTY_MEMBER_VALUES, \ + sizeof (Control##Default), \ + 1, \ + KSPROPERTY_MEMBER_FLAG_DEFAULT \ + }, \ + (PVOID)&Control##Default \ + } \ +}; \ + \ +KSPROPERTY_VALUES Control##Values = \ +{ \ + { \ + STATICGUIDOF(KSPROPTYPESETID_General), \ + VT_I4, \ + 0 \ + }, \ + SIZEOF_ARRAY(Control##MembersList), \ + Control##MembersList \ +}; + +DEFINE_LEGACY_CTRL_CAPS( ZoomRelative, -2, +2, 1, 0 ) +DEFINE_LEGACY_CTRL_CAPS( Zoom, FOCALLENGTH_OPTICAL_MIN, FOCALLENGTH_OPTICAL_MAX, 1, FOCALLENGTH_OCULAR ) +DEFINE_LEGACY_CTRL_CAPS( Pan, -180, 180, 1, 0 ) +DEFINE_LEGACY_CTRL_CAPS( Roll, -180, 180, 1, 0 ) +DEFINE_LEGACY_CTRL_CAPS( Tilt, -180, 180, 1, 0 ) +DEFINE_LEGACY_CTRL_CAPS( BacklightCompensation, 0, 1, 1, 0 ) +DEFINE_LEGACY_CTRL_CAPS( Brightness, -10000, 10000, 1, 750 ) +DEFINE_LEGACY_CTRL_CAPS( Contrast, -10000, 10000, 1, 100 ) +DEFINE_LEGACY_CTRL_CAPS( Hue, -18000, 18000, 1, 0 ) +DEFINE_LEGACY_CTRL_CAPS( PLF, POWERLINEFREQ_DISABLED, POWERLINEFREQ_60HZ, 1, POWERLINEFREQ_DEFAULT ) +DEFINE_LEGACY_CTRL_CAPS( WhiteBalance, WHITEBALANCE_MIN, WHITEBALANCE_MAX, WHITEBALANCE_STEP, WHITEBALANCE_DEF ) +DEFINE_LEGACY_CTRL_CAPS( Exposure, EXPOSURE_BILOG_MIN, EXPOSURE_BILOG_MAX, EXPOSURE_BILOG_STEP, EXPOSURE_BILOG_DEF) +DEFINE_LEGACY_CTRL_CAPS( Focus, 1, 1200, 1, 100 ); + +void +CSensorSimulation:: +FocusConvergence( + _In_opt_ PVOID Context +) +/*++ + +Routine Description: + + A timer callback that simulates focus convergence. + + Update the focus state to focused. + + Update the position to a random location that is in bounds if we are in + an auto or continuous auto mode. The new location is probably not + realistic; but it is important to simulate the change in location for + testing. + + Notice that we do no kick off the timer again on continuous auto focus. + A camera that is performing continuous auto focus in software would + probably do something there. + +Arguments: + + Context - + In our case, this is a pointer to our simulation object. + +Return Value: + + None + +--*/ +{ + PAGED_CODE(); + + if( Context ) + { + CSensorSimulation *Sensor = (CSensorSimulation *) Context; + KScopedMutex lock( Sensor->m_SensorMutex ); + + if( Sensor->m_FocusNotifier ) + { + Sensor->m_FocusNotifier->Set(); + Sensor->m_FocusNotifier = nullptr; + } + + // Let's just fake a focus change if we get here from somewhere else. + // This only applies to auto focus modes and only if we're not focused. + if( (Sensor->m_GlobalIspSettings.FocusMode & + (KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO | KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUS)) && + Sensor->m_FocusState != KSCAMERA_EXTENDEDPROP_FOCUSSTATE_FOCUSED ) + { + Sensor->m_GlobalIspSettings.FocusSetting.VideoProc.Value.ul = + GetRandom( Sensor->m_GlobalIspSettings.FocusSetting.Min, + Sensor->m_GlobalIspSettings.FocusSetting.Max ); + } + else if( Sensor->m_GlobalIspSettings.FocusMode & KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_MANUAL ) + { + switch( Sensor->m_GlobalIspSettings.FocusMode & KSCAMERA_EXTENDEDPROP_FOCUS_DISTANCE_MASK ) + { + case KSCAMERA_EXTENDEDPROP_FOCUS_DISTANCE_INFINITY: + Sensor->m_GlobalIspSettings.FocusSetting.VideoProc.Value.ul = + Sensor->m_GlobalIspSettings.FocusSetting.Max; + break; + + case KSCAMERA_EXTENDEDPROP_FOCUS_DISTANCE_HYPERFOCAL: + Sensor->m_GlobalIspSettings.FocusSetting.VideoProc.Value.ul = + Sensor->m_GlobalIspSettings.FocusSetting.Max - Sensor->m_GlobalIspSettings.FocusSetting.Step; + break; + + case KSCAMERA_EXTENDEDPROP_FOCUS_DISTANCE_NEAREST: + Sensor->m_GlobalIspSettings.FocusSetting.VideoProc.Value.ul = + Sensor->m_GlobalIspSettings.FocusSetting.Min; + break; + + } + } + + // TODO: + // If we're continuous focus, we should probably kick off another focus change here... + // ... but that tends to mess with testing. + + // We always want to transition to here. + Sensor->m_FocusState = KSCAMERA_EXTENDEDPROP_FOCUSSTATE_FOCUSED; + } +} + +void +CSensorSimulation:: +FocusRectConvergence( + _In_opt_ PVOID Context +) +/*++ + +Routine Description: + + A timer callback that simulates FocusRect convergence. + +Arguments: + + Context - + In our case, this is a pointer to our simulation object. + +Return Value: + + None + +--*/ +{ + PAGED_CODE(); + + if( Context ) + { + CSensorSimulation *Sensor = (CSensorSimulation *) Context; + KScopedMutex lock( Sensor->m_SensorMutex ); + + if( Sensor->m_FocusRectNotifier ) + { + Sensor->m_FocusRectNotifier->Set(); + Sensor->m_FocusRectNotifier = nullptr; + } + + // Not much else to do here. + } +} + +CSensorSimulation::CSensorSimulation( + _In_ CCaptureDevice *Device, + _In_ const KSFILTER_DESCRIPTOR *Descriptors +) + : CSensor( Device, Descriptors->PinDescriptorsCount ) + , m_Descriptors( Descriptors ) + , m_MetadataInfo( nullptr ) + , m_FocusTimer( nullptr ) + , m_FocusRectTimer( nullptr ) + +{ + PAGED_CODE(); + + KeQuerySystemTime (&m_StartTime); +} + + +CSensorSimulation::~CSensorSimulation() +{ + PAGED_CODE(); + + delete m_FocusTimer; + delete m_FocusRectTimer; + delete [] m_MetadataInfo; + delete [] (PBYTE) m_pPerFrameSettings; + delete [] m_WarmStartEnabled; +} + +NTSTATUS +CSensorSimulation:: +ProgramDefaults() +/*++ + +Routine Description: + + Reset our simulation back to the DDI-specified defaults. + +Arguments: + + None + +Return Value: + + Success / Failure + +--*/ +{ + PAGED_CODE(); + + DBG_ENTER("()"); + + m_FocusNotifier = nullptr; + m_pPerFrameSettings = nullptr; + + + m_FaceDetectionMax = MAX_FACES; + m_FaceDetectionFlags = KSCAMERA_EXTENDEDPROP_FACEDETECTION_OFF; + m_FaceDetectionCurrentMax = 0; + m_AutoFocusLock = false; + m_AutoExposureLock = false; + m_AutoWhitebalanceLock = false; + m_Flash = 0; + m_VideoStabMode = 0; + m_VFR = KSCAMERA_EXTENDEDPROP_VFR_ON; + m_VideoHDR = KSCAMERA_EXTENDEDPROP_VIDEOHDR_OFF; + m_VideoStabilization = KSCAMERA_EXTENDEDPROP_VIDEOSTABILIZATION_OFF; + m_Histogram = KSCAMERA_EXTENDEDPROP_HISTOGRAM_OFF; + m_OpticalImageStabilization = KSCAMERA_EXTENDEDPROP_OIS_AUTO; + m_AdvancedPhoto = KSCAMERA_EXTENDEDPROP_ADVANCEDPHOTO_OFF; + + m_FocusRect.left = 0; + m_FocusRect.top = 0; + m_FocusRect.right = 0; + m_FocusRect.bottom = 0; + + m_FocusState = KSCAMERA_EXTENDEDPROP_FOCUSSTATE_UNINITIALIZED; + + m_PhotoMode = CExtendedPhotoMode(); + m_PhotoMode.Capability = KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL | KSCAMERA_EXTENDEDPROP_PHOTOMODE_SEQUENCE; + m_PhotoMode.PinId = GetStillIndex(); + m_PhotoMode.MaxHistoryFrames() = IMAGE_CAPTURE_PIN_MAXIMUM_HISTORY_FRAMES; + m_PhotoMode.RequestedHistoryFrames() = 0; + m_PhotoMode.SubMode() = KSCAMERA_EXTENDEDPROP_PHOTOMODE_SEQUENCE_SUB_NONE; + + m_MaxFrameRate = CExtendedProperty(); // assume nothing. + SetQPC( 0 ); + m_TorchMode.Flags = KSCAMERA_EXTENDEDPROP_VIDEOTORCH_OFF; + m_TorchMode = 50UL; + + m_OptimizationHint.Flags = KSCAMERA_EXTENDEDPROP_OPTIMIZATION_PHOTO; + + m_SceneMode = KSCAMERA_EXTENDEDPROP_SCENEMODE_AUTO; + + // Wipe the global settings to zeros for now. + RtlFillBytes(&m_GlobalIspSettings, sizeof(m_GlobalIspSettings), 0); + + m_GlobalIspSettings.FlashMode = KSCAMERA_EXTENDEDPROP_FLASH_OFF; + m_GlobalIspSettings.FlashValue = 50; + + m_GlobalIspSettings.ISOMode = KSCAMERA_EXTENDEDPROP_ISO_AUTO; + m_GlobalIspSettings.ISOValue = 200; + + m_GlobalIspSettings.EVCompensation.Mode = 0; + m_GlobalIspSettings.EVCompensation.Min = -2; + m_GlobalIspSettings.EVCompensation.Max = 2; + m_GlobalIspSettings.EVCompensation.Value = 0; + m_GlobalIspSettings.EVCompensation.Mode = KSCAMERA_EXTENDEDPROP_EVCOMP_FULLSTEP; + + m_GlobalIspSettings.WhiteBalanceMode = KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO; + m_GlobalIspSettings.WhiteBalanceSetting.Max = WHITEBALANCE_MAX; + m_GlobalIspSettings.WhiteBalanceSetting.Min = WHITEBALANCE_MIN; + m_GlobalIspSettings.WhiteBalanceSetting.Step = WHITEBALANCE_STEP; + m_GlobalIspSettings.WhiteBalanceSetting.Reserved = 0; + m_GlobalIspSettings.WhiteBalanceSetting.VideoProc.Value.ul = WhiteBalanceDefault; + m_GlobalIspSettings.WhiteBalanceSetting.Mode = KSCAMERA_EXTENDEDPROP_WHITEBALANCE_TEMPERATURE; + + m_GlobalIspSettings.ExposureMode = KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO; + m_GlobalIspSettings.ExposureSetting.Max = (MAX_EXPOSURE_TIME>LONG_MAX) ? LONG_MAX : (LONG)(LONG_MAX & MAX_EXPOSURE_TIME); + m_GlobalIspSettings.ExposureSetting.Min = (LONG)MIN_EXPOSURE_TIME; + m_GlobalIspSettings.ExposureSetting.Step = 1; + m_GlobalIspSettings.ExposureSetting.Reserved = 0; + m_GlobalIspSettings.ExposureSetting.VideoProc.Value.ll = DEF_EXPOSURE_TIME; + m_GlobalIspSettings.ExposureSetting.Mode = 0; + + m_GlobalIspSettings.FocusMode = KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUS | KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_FULLRANGE; + m_GlobalIspSettings.FocusSetting.Max = 1200; + m_GlobalIspSettings.FocusSetting.Min = 1; + m_GlobalIspSettings.FocusSetting.Step = 1; + m_GlobalIspSettings.FocusSetting.Reserved = 0; + m_GlobalIspSettings.FocusSetting.VideoProc.Value.ul = 100; + m_GlobalIspSettings.FocusSetting.Mode = 0; + m_GlobalIspSettings.FocusPriority = KSCAMERA_EXTENDEDPROP_FOCUSPRIORITY_ON; + + m_GlobalIspSettings.bPhotoConfirmation = TRUE; + + // Defaults to fall back to if we get a cancel. + m_LastFocusMode = KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUS | KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_FULLRANGE; + m_LastFocusSetting = 100; + + + // Specifies the dynamic range of our fictious camera's lens system + // Note: Magnification = Lobjective / Locular + + // The following values set only the minimum and maximum for the rational number expressing the zoom. + + // This specifies the denominator of the rational value used to specify magnification. + m_FocalLength.lOcularFocalLength = FOCALLENGTH_OCULAR; + // If lObjectiveFocalLengthMin < lOcularFocalLength, then camera allows Zoom out: aka, wide-field view. + m_FocalLength.lObjectiveFocalLengthMin = FOCALLENGTH_OPTICAL_MIN; + // If lObjectiveFocalLengthMax > lOcularFocalLength, then camera allows Zoom in: aka, Magnified, narrow-field view. + m_FocalLength.lObjectiveFocalLengthMax = FOCALLENGTH_OPTICAL_MAX; + + // Cache values for the new extended zoom property. + m_ExtendedZoom.Flags = KSCAMERA_EXTENDEDPROP_ZOOM_DIRECT; + m_ExtendedZoom.Min() = (LONG)(TO_Q16(FOCALLENGTH_OPTICAL_MIN) / FOCALLENGTH_OCULAR); + m_ExtendedZoom.Max() = (LONG)(TO_Q16(FOCALLENGTH_OPTICAL_MAX) / FOCALLENGTH_OCULAR); + m_ExtendedZoom.Step() = 1; // This avoids the rounding issue. + m_ExtendedZoom = ZoomDefault; + //TO_Q16(ZoomRangeAndStep[0].SteppingDelta) / FOCALLENGTH_OCULAR; + + // Set up our current zoom. + m_Zoom.Capabilities = + KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL | + KSPROPERTY_CAMERACONTROL_FLAGS_AUTO | + KSPROPERTY_CAMERACONTROL_FLAGS_ABSOLUTE | + KSPROPERTY_CAMERACONTROL_FLAGS_RELATIVE; + m_Zoom.Value = ZoomDefault; + m_Zoom.Flags = KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL | KSPROPERTY_CAMERACONTROL_FLAGS_ABSOLUTE; + + // Set up our relative zoom. + m_ZoomRelative.Capabilities = + KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL | + KSPROPERTY_CAMERACONTROL_FLAGS_AUTO | + KSPROPERTY_CAMERACONTROL_FLAGS_ABSOLUTE; + m_ZoomRelative.Value = ZoomRelativeDefault; + m_ZoomRelative.Flags = KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL | KSPROPERTY_CAMERACONTROL_FLAGS_ABSOLUTE; + + m_Pan.Capabilities = + KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL | + KSPROPERTY_CAMERACONTROL_FLAGS_AUTO | + KSPROPERTY_CAMERACONTROL_FLAGS_ABSOLUTE; + m_Pan.Value = PanDefault; + m_Pan.Flags = KSPROPERTY_CAMERACONTROL_FLAGS_AUTO | KSPROPERTY_CAMERACONTROL_FLAGS_ABSOLUTE; + + m_Roll.Capabilities = + KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL | + KSPROPERTY_CAMERACONTROL_FLAGS_AUTO | + KSPROPERTY_CAMERACONTROL_FLAGS_ABSOLUTE; + m_Roll.Value = RollDefault; + m_Roll.Flags = KSPROPERTY_CAMERACONTROL_FLAGS_AUTO | KSPROPERTY_CAMERACONTROL_FLAGS_ABSOLUTE; + + m_Tilt.Capabilities = + KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL | + KSPROPERTY_CAMERACONTROL_FLAGS_AUTO | + KSPROPERTY_CAMERACONTROL_FLAGS_ABSOLUTE; + m_Tilt.Value = TiltDefault; + m_Tilt.Flags = KSPROPERTY_CAMERACONTROL_FLAGS_AUTO | KSPROPERTY_CAMERACONTROL_FLAGS_ABSOLUTE; + + m_BacklightCompensation.Capabilities = + KSPROPERTY_VIDEOPROCAMP_FLAGS_MANUAL | + KSPROPERTY_VIDEOPROCAMP_FLAGS_AUTO; + m_BacklightCompensation.Value = BacklightCompensationDefault; + m_BacklightCompensation.Flags = KSPROPERTY_CAMERACONTROL_FLAGS_AUTO; + + m_Brightness.Capabilities = + KSPROPERTY_VIDEOPROCAMP_FLAGS_MANUAL | + KSPROPERTY_VIDEOPROCAMP_FLAGS_AUTO; + m_Brightness.Value = BrightnessDefault; + m_Brightness.Flags = KSPROPERTY_CAMERACONTROL_FLAGS_AUTO; + + m_Contrast.Capabilities = + KSPROPERTY_VIDEOPROCAMP_FLAGS_MANUAL | + KSPROPERTY_VIDEOPROCAMP_FLAGS_AUTO; + m_Contrast.Value = ContrastDefault; + m_Contrast.Flags = KSPROPERTY_CAMERACONTROL_FLAGS_AUTO; + + m_Hue.Capabilities = + KSPROPERTY_VIDEOPROCAMP_FLAGS_MANUAL | + KSPROPERTY_VIDEOPROCAMP_FLAGS_AUTO; + m_Hue.Value = HueDefault; + m_Hue.Flags = KSPROPERTY_CAMERACONTROL_FLAGS_AUTO; + + // Set up our Power Line Frequency defaults. + m_PowerLineFreq.Capabilities = KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL; + m_PowerLineFreq.Value = POWERLINEFREQ_DEFAULT; + m_PowerLineFreq.Flags = KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL; + + m_IsoResult = STATUS_SUCCESS; + m_EvCompResult = STATUS_SUCCESS; + m_WhiteBalanceResult= STATUS_SUCCESS; + m_ExposureResult = STATUS_SUCCESS; + m_SceneModeResult = STATUS_SUCCESS; + m_FaceDetectionResult = STATUS_SUCCESS; + m_FocusResult = STATUS_SUCCESS; + + for( ULONG PinIndex=0; PinIndexGetDeviceObject() ) ); + IFNULL_EXIT( m_FocusRectTimer = new (NonPagedPoolNx, 'emiT') KPassiveTimer( m_Device->GetDeviceObject() ) ); + IFNULL_EXIT( m_MetadataInfo = new (PagedPool, 'ateM') CExtendedMetadata[GetPinCount()] ); + IFNULL_EXIT( m_WarmStartEnabled = new (PagedPool, 'mraW' ) CExtendedProperty[GetPinCount()] ); + + if( NT_SUCCESS( Status ) ) + { + for( ULONG PinIndex=0; + NT_SUCCESS(Status) && PinIndexPinDescriptorsCount; + PinIndex++ ) + { + const KSPIN_DESCRIPTOR_EX *PinDescriptors = m_Descriptors->PinDescriptors; + CHardwareSimulation *pSim=nullptr; + + NT_ASSERT( m_Descriptors->PinDescriptorSize == sizeof( *PinDescriptors ) ); + + // Video pin type + if( IsEqualGUID( *PinDescriptors[PinIndex].PinDescriptor.Category, PIN_CATEGORY_CAPTURE ) ) + { + pSim = new (NonPagedPoolNx, 'ediV') CVideoHardwareSimulation( this, PinIndex ); + m_VideoIndex = PinIndex; + } + else if( IsEqualGUID( *PinDescriptors[PinIndex].PinDescriptor.Category, PIN_CATEGORY_PREVIEW ) ) + { + pSim = new (NonPagedPoolNx, 'verP') CPreviewHardwareSimulation( this, PinIndex ); + m_PreviewIndex = PinIndex; // must set to enable photo confirmation... + } + else if( IsEqualGUID( *PinDescriptors[PinIndex].PinDescriptor.Category, PINNAME_IMAGE ) ) + { + pSim = new (NonPagedPoolNx, 'litS') CImageHardwareSimulation( this, PinIndex ); + m_StillIndex = PinIndex; + } + else + { + Status = STATUS_INTERNAL_ERROR; + NT_ASSERT(FALSE); + } + + if( pSim ) + { + if( pSim->Initialize() ) + { + m_HardwareSimulation[PinIndex] = pSim; + } + else + { + // + // If we couldn't create the hardware simulation, fail. + // + delete pSim; + Status = STATUS_INSUFFICIENT_RESOURCES; + } + } + else + { + Status = STATUS_INSUFFICIENT_RESOURCES; + } + } + } + +done: + DBG_ENTER("()=0x%08X", Status); + return Status; +} + +/************************************************************************** + + Sensor-level control simulations + + 1. Query capabilities and state of the device. + 2. Issue a command or set state on the device. + +**************************************************************************/ + +// Get for KSPROPERTY_CAMERACONTROL_REGION_OF_INTEREST_PROPERTY_ID +NTSTATUS +CSensorSimulation:: +GetFocusRect( + _Inout_ PKSPROPERTY_CAMERACONTROL_REGION_OF_INTEREST_S pRoi +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + pRoi->FocusRect = m_FocusRect; + pRoi->AutoFocusLock = m_AutoFocusLock; + pRoi->AutoExposureLock = m_AutoExposureLock; + pRoi->AutoWhitebalanceLock = m_AutoWhitebalanceLock; + pRoi->Capabilities = KSPROPERTY_CAMERACONTROL_REGION_OF_INTEREST_FLAGS_AUTO | KSPROPERTY_CAMERACONTROL_REGION_OF_INTEREST_FLAGS_MANUAL | + KSPROPERTY_CAMERACONTROL_REGION_OF_INTEREST_FLAGS_ASYNC | KSPROPERTY_CAMERACONTROL_REGION_OF_INTEREST_CONFIG_FOCUS | + KSPROPERTY_CAMERACONTROL_REGION_OF_INTEREST_CONFIG_EXPOSURE; + return STATUS_SUCCESS; +} + +// Set for KSPROPERTY_CAMERACONTROL_REGION_OF_INTEREST_PROPERTY_ID +NTSTATUS +CSensorSimulation:: +SetFocusRectAsync( + _In_ PKSPROPERTY_CAMERACONTROL_REGION_OF_INTEREST_S pRoi, + _In_ CNotifier *Notifier // Use to notify the framework that the control is done. +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + if( m_FocusRectNotifier ) + { + // The last operation is still pending. + return STATUS_INVALID_DEVICE_STATE; + } + + m_FocusRectNotifier = Notifier; + + m_FocusRect = pRoi->FocusRect; + m_AutoFocusLock = pRoi->AutoFocusLock; + m_AutoExposureLock = pRoi->AutoExposureLock; + m_AutoWhitebalanceLock = pRoi->AutoWhitebalanceLock; + + if(m_AutoFocusLock) + { + m_GlobalIspSettings.FocusMode = KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO; + m_GlobalIspSettings.FocusSetting.VideoProc.Value.ul = 100; + } + if(m_AutoExposureLock) + { + m_GlobalIspSettings.ExposureMode = KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO; + m_GlobalIspSettings.ExposureSetting.VideoProc.Value.ll = DEF_EXPOSURE_TIME; + DBG_TRACE("ExposureMode=KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO, ExposureTime=%lld00ns", + m_GlobalIspSettings.ExposureSetting.VideoProc.Value.ll); + } + if(m_AutoWhitebalanceLock) + { + m_GlobalIspSettings.WhiteBalanceMode = KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO; + m_GlobalIspSettings.WhiteBalanceSetting.VideoProc.Value.ul = WhiteBalanceDefault; + m_GlobalIspSettings.WhiteBalanceSetting.Mode = KSCAMERA_EXTENDEDPROP_WHITEBALANCE_TEMPERATURE; + } + + // Fire a one-shot timer to transition the focus state. + m_FocusTimer->Set( + -2500000LL, // 250ms + FocusRectConvergence, + this ); + + return STATUS_SUCCESS; +} + +// Cancel an outstanding KSPROPERTY_CAMERACONTROL_REGION_OF_INTEREST_PROPERTY_ID Set +NTSTATUS +CSensorSimulation:: +CancelFocusRect() +{ + PAGED_CODE(); + + // Per spec, this control cannot be cancelled. So we block until complete. + // Even if we didn't block here, the CNotifier object is reference counted, + // so the CCaptureFilter would block until the operation completes. + if( m_FocusRectNotifier ) + { + m_FocusRectTimer->Wait(); + } + return STATUS_UNSUCCESSFUL; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_FACEDETECTION. +NTSTATUS +CSensorSimulation:: +GetFaceDetection( + _Inout_ CExtendedVidProcSetting *pProperty +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + *pProperty = CExtendedVidProcSetting( m_FaceDetectionFlags ); + + pProperty->Capability = + KSCAMERA_EXTENDEDPROP_FACEDETECTION_OFF | + KSCAMERA_EXTENDEDPROP_FACEDETECTION_PREVIEW | + KSCAMERA_EXTENDEDPROP_FACEDETECTION_VIDEO | + KSCAMERA_EXTENDEDPROP_FACEDETECTION_PHOTO | + KSCAMERA_EXTENDEDPROP_FACEDETECTION_BLINK | + KSCAMERA_EXTENDEDPROP_FACEDETECTION_SMILE ; + + pProperty->Max() = m_FaceDetectionMax; + pProperty->Min() = 1; + pProperty->Step() = 1; + *pProperty = m_FaceDetectionCurrentMax; + pProperty->Result = m_FaceDetectionResult; + + return STATUS_SUCCESS; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_FACEDETECTION. +NTSTATUS +CSensorSimulation:: +SetFaceDetection( + _In_ CExtendedVidProcSetting *pProperty +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + m_FaceDetectionCurrentMax = + ( KSCAMERA_EXTENDEDPROP_FACEDETECTION_MASK & pProperty->Flags ) ? + pProperty->GetULONG() : 0; + m_FaceDetectionFlags = pProperty->Flags; + m_FaceDetectionResult = STATUS_SUCCESS; + return STATUS_SUCCESS; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_FOCUSSTATE +NTSTATUS +CSensorSimulation:: +GetFocusState( + _Out_ KSCAMERA_EXTENDEDPROP_FOCUSSTATE *FocusState +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + *FocusState = m_FocusState; + + DBG_LEAVE("(%s)", FocusStateDbgTxt(m_FocusState)); + return STATUS_SUCCESS; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_FOCUSMODE +NTSTATUS +CSensorSimulation:: +GetFocus( + _Inout_ CExtendedVidProcSetting *pSetting +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + // Mode + pSetting->Flags = m_GlobalIspSettings.FocusMode; + + // capabilities. + pSetting->Capability = KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL | + KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO | + KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK | + KSCAMERA_EXTENDEDPROP_FOCUS_UNLOCK | + KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_MANUAL | + KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUS | + KSCAMERA_EXTENDEDPROP_CAPS_CANCELLABLE | + KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_MACRO | + KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_NORMAL | + KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_FULLRANGE | + KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUSLOCK | + KSCAMERA_EXTENDEDPROP_FOCUS_REGIONBASED | + KSCAMERA_EXTENDEDPROP_FOCUS_DRIVERFALLBACK_OFF | + KSCAMERA_EXTENDEDPROP_FOCUS_DISTANCE_INFINITY | + KSCAMERA_EXTENDEDPROP_FOCUS_DISTANCE_HYPERFOCAL | + KSCAMERA_EXTENDEDPROP_FOCUS_DISTANCE_NEAREST + ; + + pSetting->Result = m_FocusResult; + + // min/max/step/etc. + pSetting->m_Setting = m_GlobalIspSettings.FocusSetting; + + return STATUS_SUCCESS; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_FOCUSMODE +NTSTATUS +CSensorSimulation:: +SetFocusAsync( + _In_ CExtendedVidProcSetting *pSetting, + _In_ CNotifier *Notifier // Use to notify the framework that the control is done. +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + // Signal immediately if the we are continuous focus. + bool bSignalImmediately = false; + ULONGLONG Flags = pSetting->Flags; + + // Cache our current focus settings in case we get a cancel request. + m_LastFocusMode = m_GlobalIspSettings.FocusMode; + m_LastFocusSetting = m_GlobalIspSettings.FocusSetting.VideoProc.Value.ul; + + // If we've been asked to unlock, remove any lock flags. + if( Flags & KSCAMERA_EXTENDEDPROP_FOCUS_UNLOCK ) + { + // Mark us as signalling immediately if Focus was not locked. + bSignalImmediately = + ( m_GlobalIspSettings.FocusMode & + ( KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK | + KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUSLOCK ) ) == 0; + + // Change our operation so we go back to the previous state. + Flags = m_GlobalIspSettings.FocusMode & + ~( KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK | + KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUSLOCK ); + } + + // Handle the stand-alone lock case by setting the appropriate flags. + if( ( Flags & + (KSCAMERA_EXTENDEDPROP_FOCUS_MODE_MASK | + KSCAMERA_EXTENDEDPROP_FOCUS_MODE_ADVANCED_MASK) ) == + KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK ) + { + if( m_GlobalIspSettings.FocusMode & KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO ) + { + Flags = + m_GlobalIspSettings.FocusMode | KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK; + } + if( m_GlobalIspSettings.FocusMode & KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUS ) + { + Flags = + m_GlobalIspSettings.FocusMode |= KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUSLOCK; + } + } + + // Record the Focus mode. + DBG_TRACE("Recording FocusMode = 0x%016llX", Flags); + m_GlobalIspSettings.FocusMode = Flags; + + if(m_GlobalIspSettings.FocusMode & KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_MANUAL) + { + m_GlobalIspSettings.FocusSetting.VideoProc.Value.l = pSetting->GetLONG(); + } + + if( bSignalImmediately ) + // Unlock, but nothing really to do. + { + Notifier->Set(); + m_FocusNotifier = nullptr; + } + else if( Flags & KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUS ) + // Continuous case: Signal immediately. + // Then prime the timer to switch the state to focused. + { + m_FocusNotifier = nullptr; + m_FocusState = KSCAMERA_EXTENDEDPROP_FOCUSSTATE_SEARCHING; + Notifier->Set(); + + // Fire a one-shot timer to transition the focus state. + m_FocusTimer->Set( + -4000000LL, // 400ms + FocusConvergence, + this ); + } + else if( Flags & KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO ) + // Normal [auto?] focus operation. + // Autofocus should be SLOW. + { + m_FocusNotifier = Notifier; + m_FocusState = KSCAMERA_EXTENDEDPROP_FOCUSSTATE_SEARCHING; + + // Fire a one-shot timer to transition the focus state. + m_FocusTimer->Set( + -4000000LL, // 400ms + FocusConvergence, + this ); + } + else + // Handle Manual, lock & unlock cases FAST ~1 frame time. + { + m_FocusNotifier = Notifier; + m_FocusState = KSCAMERA_EXTENDEDPROP_FOCUSSTATE_SEARCHING; + + // Fire a one-shot timer to transition the focus state. + m_FocusTimer->Set( + -330000LL, // 33ms + FocusConvergence, + this ); + } + + m_FocusResult = STATUS_SUCCESS; + return STATUS_SUCCESS; +} + +// Cancel oustanding set KSPROPERTY_CAMERACONTROL_EXTENDED_FOCUSMODE +NTSTATUS +CSensorSimulation:: +CancelFocus() +{ + PAGED_CODE(); + + // Cancel our timer to prevent a race. + m_FocusTimer->Cancel(); + + // Acquire the lock after the cancel to avoid a potential deadlock. + KScopedMutex lock( m_SensorMutex ); + + // Now check to see if an operation was still outstanding. + // We could just check the status from the timer's Cancel(), but I + // prefer checking to see if the handler cleared the notifier instead. + if( m_FocusNotifier ) + { + m_FocusState = KSCAMERA_EXTENDEDPROP_FOCUSSTATE_LOST; + + // Cancel behavior isn't well-defined in the spec. Do something reasonable. + // In our case, we'll put the focus mode and value back to the last setting. + m_GlobalIspSettings.FocusMode = m_LastFocusMode; + m_GlobalIspSettings.FocusSetting.VideoProc.Value.ul = m_LastFocusSetting; + + m_FocusNotifier->Set(); + m_FocusNotifier = nullptr; + m_FocusResult = (ULONG) STATUS_CANCELLED; + return STATUS_SUCCESS; + } + return STATUS_UNSUCCESSFUL; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_FLASHMODE +NTSTATUS +CSensorSimulation:: +GetExtendedFlash( + _Inout_ CExtendedProperty *pProperty +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + *pProperty = CExtendedProperty( m_GlobalIspSettings.FlashMode ); + *pProperty = m_GlobalIspSettings.FlashValue; + pProperty->Capability = KSCAMERA_EXTENDEDPROP_FLASH_ON | KSCAMERA_EXTENDEDPROP_FLASH_ON_ADJUSTABLEPOWER | + KSCAMERA_EXTENDEDPROP_FLASH_AUTO | KSCAMERA_EXTENDEDPROP_FLASH_AUTO_ADJUSTABLEPOWER | + KSCAMERA_EXTENDEDPROP_FLASH_REDEYEREDUCTION | KSCAMERA_EXTENDEDPROP_FLASH_SINGLEFLASH | + KSCAMERA_EXTENDEDPROP_FLASH_MULTIFLASHSUPPORTED | + KSCAMERA_EXTENDEDPROP_FLASH_ASSISTANT_ON | KSCAMERA_EXTENDEDPROP_FLASH_ASSISTANT_OFF | + KSCAMERA_EXTENDEDPROP_FLASH_ASSISTANT_AUTO + ; + + DBG_TRACE("FlashMode=0x%016llX, FlashValue=%d", + m_GlobalIspSettings.FlashMode, + m_GlobalIspSettings.FlashValue ); + + return STATUS_SUCCESS; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_FLASHMODE +NTSTATUS +CSensorSimulation:: +SetExtendedFlash( + _In_ CExtendedProperty *pProperty +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + if(pProperty->Flags & (KSCAMERA_EXTENDEDPROP_FLASH_ON_ADJUSTABLEPOWER | + KSCAMERA_EXTENDEDPROP_FLASH_AUTO_ADJUSTABLEPOWER) ) + { + m_GlobalIspSettings.FlashValue = pProperty->m_Value.Value.ul; + } + else + { + m_GlobalIspSettings.FlashValue = 50; + } + + m_GlobalIspSettings.FlashMode = pProperty->Flags; + NTSTATUS Status = SetFlashStatus(pProperty->Flags); + + DBG_TRACE("FlashMode=0x%016llX, FlashValue=%d, Status=0x%08X", + m_GlobalIspSettings.FlashMode, + m_GlobalIspSettings.FlashValue, + Status ); + + pProperty->Result = (ULONG) Status; + return Status; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_ISO +NTSTATUS +CSensorSimulation:: +GetIso( + _Inout_ CExtendedProperty *pProperty +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + pProperty->Flags = IsoModeValue2Preset( + m_GlobalIspSettings.ISOMode, + m_GlobalIspSettings.ISOValue ); + pProperty->Result = STATUS_SUCCESS; + pProperty->Capability = KSCAMERA_EXTENDEDPROP_ISO_AUTO | + KSCAMERA_EXTENDEDPROP_ISO_50 | KSCAMERA_EXTENDEDPROP_ISO_80 | + KSCAMERA_EXTENDEDPROP_ISO_100 | KSCAMERA_EXTENDEDPROP_ISO_200 | + KSCAMERA_EXTENDEDPROP_ISO_400 | KSCAMERA_EXTENDEDPROP_ISO_800 | + KSCAMERA_EXTENDEDPROP_ISO_1600 | KSCAMERA_EXTENDEDPROP_ISO_3200 | + KSCAMERA_EXTENDEDPROP_ISO_6400 | KSCAMERA_EXTENDEDPROP_ISO_12800 | + KSCAMERA_EXTENDEDPROP_ISO_25600 | + KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL; + pProperty->Result = m_IsoResult; + return STATUS_SUCCESS; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_ISO +NTSTATUS +CSensorSimulation:: +SetIsoAsync( + _In_ CExtendedProperty *pProperty, + _In_ CNotifier *Notifier // Use to notify the framework that the control is done. +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + m_GlobalIspSettings.ISOMode = (ULONG)pProperty->Flags; + m_GlobalIspSettings.ISOValue = 0; + + // This could be done in a separate work item after the scene mode has been programmed... + m_IsoResult = STATUS_SUCCESS; + Notifier->Set(); + + return STATUS_SUCCESS; +} + +#define KSCAMERA_EXTENDEDPROP_ISO_ADVANCED_MASK (KSCAMERA_EXTENDEDPROP_ISO_AUTO | KSCAMERA_EXTENDEDPROP_ISO_MANUAL) + +// Create a set of default settings... +static +const +KSCAMERA_EXTENDEDPROP_VIDEOPROCSETTING g_IsoAdvancedSettings = +{ + 0, // Mode - Unused + 50, // Min + 25600, // Max + 1 // Step (advertised doesn't have to be actual) +}; + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_ISO_ADVANCED. +NTSTATUS +CSensorSimulation:: +GetIsoAdvanced( + _Inout_ CExtendedVidProcSetting *pProperty +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + // Initialize the property with defaults + (AUTO | MANUAL) + pProperty->Flags = + m_GlobalIspSettings.ISOMode & + KSCAMERA_EXTENDEDPROP_ISO_ADVANCED_MASK; + pProperty->Result = STATUS_SUCCESS; + + // Advertise basic caps... + pProperty->Capability = + KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL | + KSCAMERA_EXTENDEDPROP_ISO_AUTO | + KSCAMERA_EXTENDEDPROP_ISO_MANUAL ; + + // Advertise the manual caps... + *pProperty = g_IsoAdvancedSettings; + + // If ISO is MANUAL, set the value. + if( m_GlobalIspSettings.ISOMode == KSCAMERA_EXTENDEDPROP_ISO_MANUAL ) + { + *pProperty = m_GlobalIspSettings.ISOValue; + } + else if( !(m_GlobalIspSettings.ISOMode == KSCAMERA_EXTENDEDPROP_ISO_AUTO) ) + { + // Try converting any legacy presets to a manual value. + pProperty->Flags = KSCAMERA_EXTENDEDPROP_ISO_MANUAL; + // Convert the ISO setting + *pProperty = IsoPreset2Value( m_GlobalIspSettings.ISOMode ); + // If we weren't able to report a valid number, just report something reasonable... + if( pProperty->GetLONG() > pProperty->Max() ) + { + *pProperty = pProperty->Min(); + } + } + pProperty->Result = m_IsoResult; + + return STATUS_SUCCESS; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_ISO_ADVANCED. +NTSTATUS +CSensorSimulation:: +SetIsoAdvancedAsync( + _In_ CExtendedVidProcSetting *pProperty, + _In_ CNotifier *Notifier // Use to notify the framework that the control is done. +) +{ + PAGED_CODE(); + + KScopedMutex lock( m_SensorMutex ); + + // Just save the parameters + if( pProperty->Flags & KSCAMERA_EXTENDEDPROP_ISO_MANUAL ) + { + m_GlobalIspSettings.ISOMode = pProperty->Flags; + m_GlobalIspSettings.ISOValue= pProperty->GetULONG(); + } + else if( pProperty->Flags & KSCAMERA_EXTENDEDPROP_ISO_AUTO ) + { + m_GlobalIspSettings.ISOMode = pProperty->Flags; + } + + // Set regardless - it takes effect immediately for us. + m_IsoResult = STATUS_SUCCESS; + Notifier->Set(); + + return STATUS_SUCCESS; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_EVCOMPENSATION +NTSTATUS +CSensorSimulation:: +GetEvCompensation( + _Inout_ CExtendedEvCompensation *pProperty +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + *pProperty = CExtendedEvCompensation( m_GlobalIspSettings.EVCompensation.Mode ); + + // Return our cached copy; but update the value from the global setting. + *pProperty = m_GlobalIspSettings.EVCompensation; + pProperty->Capability = KSCAMERA_EXTENDEDPROP_EVCOMP_SIXTHSTEP | KSCAMERA_EXTENDEDPROP_EVCOMP_QUARTERSTEP | + KSCAMERA_EXTENDEDPROP_EVCOMP_THIRDSTEP | KSCAMERA_EXTENDEDPROP_EVCOMP_HALFSTEP | + KSCAMERA_EXTENDEDPROP_EVCOMP_FULLSTEP | KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL; + pProperty->Result = m_EvCompResult; + return STATUS_SUCCESS; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_EVCOMPENSATION +NTSTATUS +CSensorSimulation:: +SetEvCompensationAsync( + _In_ CExtendedEvCompensation *pProperty, + _In_ CNotifier *Notifier // Use to notify the framework that the control is done. +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + // Update the Globals + m_GlobalIspSettings.EVCompensation.Mode = (ULONG) pProperty->Flags; + m_GlobalIspSettings.EVCompensation.Value = pProperty->Value(); + + // Update the current setting in case we're queried for it later. + m_GlobalIspSettings.EVCompensation.Value = pProperty->Value(); + + // This could be done in a separate work item after the device has been programmed... + m_EvCompResult = STATUS_SUCCESS; + Notifier->Set(); + + return STATUS_SUCCESS; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_WHITEBALANCEMODE +NTSTATUS +CSensorSimulation:: +GetWhiteBalance( + _Inout_ CExtendedVidProcSetting *pProperty +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + *pProperty = CExtendedVidProcSetting( m_GlobalIspSettings.WhiteBalanceMode ); + + // Return our cached copy; but update the value from the global setting. + *pProperty = m_GlobalIspSettings.WhiteBalanceSetting; + pProperty->Capability = KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL | KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO | + KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK | KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_MANUAL; + pProperty->Result = m_WhiteBalanceResult; + return STATUS_SUCCESS; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_WHITEBALANCEMODE +NTSTATUS +CSensorSimulation:: +SetWhiteBalanceAsync( + _In_ CExtendedVidProcSetting *pProperty, + _In_ CNotifier *Notifier // Use to notify the framework that the control is done. +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + m_GlobalIspSettings.WhiteBalanceMode = pProperty->Flags; + m_GlobalIspSettings.WhiteBalanceSetting.Mode = pProperty->Mode(); + m_GlobalIspSettings.WhiteBalanceSetting.VideoProc.Value.ul = pProperty->GetULONG(); + + // This could be done in a separate work item after the device has been programmed... + m_WhiteBalanceResult = STATUS_SUCCESS; + Notifier->Set(); + + return STATUS_SUCCESS; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_EXPOSURE +NTSTATUS +CSensorSimulation:: +GetExposure( + _Inout_ CExtendedVidProcSetting *pProperty +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + *pProperty = CExtendedVidProcSetting( m_GlobalIspSettings.ExposureMode ); + + // Return our cached copy; but update the value from the global setting. + *pProperty = m_GlobalIspSettings.ExposureSetting; + pProperty->Capability = KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL | KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO | + KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK | KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_MANUAL; + pProperty->Result = m_ExposureResult; + DBG_TRACE("ExposureMode=0x%016llX, ExposureTime=%lld00ns", + pProperty->Flags, pProperty->GetLONGLONG()); + return STATUS_SUCCESS; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_EXPOSURE +NTSTATUS +CSensorSimulation:: +SetExposureAsync( + _In_ CExtendedVidProcSetting *pProperty, + _In_ CNotifier *Notifier // Use to notify the framework that the control is done. +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + DBG_TRACE("ExposureMode=0x%016llX, ExposureTime=%lld00ns", + pProperty->Flags, pProperty->GetLONGLONG()); + if( pProperty->Flags & KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_MANUAL ) + { + m_GlobalIspSettings.ExposureSetting.VideoProc.Value.ull = pProperty->GetULONGLONG(); + } + m_GlobalIspSettings.ExposureMode = pProperty->Flags; + + // This could be done in a separate work item after the device has been programmed... + m_ExposureResult = STATUS_SUCCESS; + Notifier->Set(); + + return STATUS_SUCCESS; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOCONFIRMATION +NTSTATUS +CSensorSimulation:: +GetPhotoConfirmation( + _Inout_ CExtendedProperty *pProperty +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + *pProperty = CExtendedProperty( m_GlobalIspSettings.bPhotoConfirmation ); + DBG_TRACE( "Is %s", m_GlobalIspSettings.bPhotoConfirmation ? "On" : "Off" ); + return STATUS_SUCCESS; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOCONFIRMATION +NTSTATUS +CSensorSimulation:: +SetPhotoConfirmation( + _In_ CExtendedProperty *pProperty +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + // Return our cached copy; but update the value from the global setting. + m_GlobalIspSettings.bPhotoConfirmation = + BOOLEAN( (pProperty->Flags & KSCAMERA_EXTENDEDPROP_PHOTOCONFIRMATION_ON) + == KSCAMERA_EXTENDEDPROP_PHOTOCONFIRMATION_ON); + DBG_TRACE( "Is %s", m_GlobalIspSettings.bPhotoConfirmation ? "On" : "Off" ); + return STATUS_SUCCESS; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_SCENEMODE +NTSTATUS +CSensorSimulation:: +GetSceneMode( + _Inout_ CExtendedProperty *pProperty +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + *pProperty = CExtendedProperty( m_SceneMode ); + pProperty->Capability = KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL | KSCAMERA_EXTENDEDPROP_SCENEMODE_MACRO | + KSCAMERA_EXTENDEDPROP_SCENEMODE_PORTRAIT | KSCAMERA_EXTENDEDPROP_SCENEMODE_SPORT | + KSCAMERA_EXTENDEDPROP_SCENEMODE_SNOW | KSCAMERA_EXTENDEDPROP_SCENEMODE_NIGHT | + KSCAMERA_EXTENDEDPROP_SCENEMODE_BEACH | KSCAMERA_EXTENDEDPROP_SCENEMODE_SUNSET | + KSCAMERA_EXTENDEDPROP_SCENEMODE_CANDLELIGHT | KSCAMERA_EXTENDEDPROP_SCENEMODE_LANDSCAPE | + KSCAMERA_EXTENDEDPROP_SCENEMODE_NIGHTPORTRAIT | KSCAMERA_EXTENDEDPROP_SCENEMODE_BACKLIT; + pProperty->Result = m_SceneModeResult; + return STATUS_SUCCESS; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_SCENEMODE +NTSTATUS +CSensorSimulation:: +SetSceneModeAsync( + _In_ CExtendedProperty *pProperty, + _In_ CNotifier *Notifier // Use to notify the framework that the control is done. +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + m_SceneMode = (ULONG)pProperty->Flags; + + UpdateSettings( m_SceneMode ); + + // This could be done in a separate work item after the scene mode has been programmed... + m_SceneModeResult = STATUS_SUCCESS; + Notifier->Set(); + + return STATUS_SUCCESS; +} + +// Apply scene mode changes. +NTSTATUS +CSensorSimulation:: +UpdateSettings( + _In_ ULONGLONG ullSceneMode +) +{ + PAGED_CODE(); + + NTSTATUS status = STATUS_SUCCESS; + KScopedMutex lock( m_SensorMutex ); + + if( KSCAMERA_EXTENDEDPROP_SCENEMODE_AUTO==ullSceneMode ) + { + // + // If the mode was auto, let's randomly replace it with another valid value. + // + static + ULONGLONG ValidModes[] = + { + KSCAMERA_EXTENDEDPROP_SCENEMODE_MACRO, + KSCAMERA_EXTENDEDPROP_SCENEMODE_PORTRAIT, KSCAMERA_EXTENDEDPROP_SCENEMODE_SPORT, + KSCAMERA_EXTENDEDPROP_SCENEMODE_SNOW, KSCAMERA_EXTENDEDPROP_SCENEMODE_NIGHT, + KSCAMERA_EXTENDEDPROP_SCENEMODE_BEACH, KSCAMERA_EXTENDEDPROP_SCENEMODE_SUNSET, + KSCAMERA_EXTENDEDPROP_SCENEMODE_CANDLELIGHT, KSCAMERA_EXTENDEDPROP_SCENEMODE_LANDSCAPE, + KSCAMERA_EXTENDEDPROP_SCENEMODE_NIGHTPORTRAIT, KSCAMERA_EXTENDEDPROP_SCENEMODE_BACKLIT + }; + + ullSceneMode = + ValidModes[ (ULONG) (1 << GetRandom( (ULONG)0, SIZEOF_ARRAY(ValidModes)-1 )) ]; + } + + switch(ullSceneMode) + { + case KSCAMERA_EXTENDEDPROP_SCENEMODE_MACRO: + m_GlobalIspSettings.ISOMode = KSCAMERA_EXTENDEDPROP_ISO_AUTO; + m_GlobalIspSettings.EVCompensation.Value = 0; + m_GlobalIspSettings.EVCompensation.Mode = KSCAMERA_EXTENDEDPROP_EVCOMP_FULLSTEP; + m_GlobalIspSettings.WhiteBalanceSetting.VideoProc.Value.ul = WhiteBalanceDefault; + m_GlobalIspSettings.WhiteBalanceSetting.Mode = KSCAMERA_EXTENDEDPROP_WHITEBALANCE_TEMPERATURE; + m_GlobalIspSettings.WhiteBalanceMode = KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO; + m_GlobalIspSettings.ExposureMode = KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO; + m_GlobalIspSettings.ExposureSetting.VideoProc.Value.ul = DEF_EXPOSURE_TIME; + m_GlobalIspSettings.ExposureSetting.Mode = 0; + m_GlobalIspSettings.FocusMode = KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUS | KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_FULLRANGE; + m_GlobalIspSettings.FocusSetting.VideoProc.Value.ul = 100; + break; + case KSCAMERA_EXTENDEDPROP_SCENEMODE_PORTRAIT: + m_GlobalIspSettings.ISOMode = KSCAMERA_EXTENDEDPROP_ISO_AUTO; + m_GlobalIspSettings.EVCompensation.Value = 0; + m_GlobalIspSettings.EVCompensation.Mode = KSCAMERA_EXTENDEDPROP_EVCOMP_FULLSTEP; + m_GlobalIspSettings.WhiteBalanceSetting.VideoProc.Value.ul = WhiteBalanceDefault; + m_GlobalIspSettings.WhiteBalanceSetting.Mode = KSCAMERA_EXTENDEDPROP_WHITEBALANCE_TEMPERATURE; + m_GlobalIspSettings.WhiteBalanceMode = KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO; + m_GlobalIspSettings.ExposureMode = KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO; + m_GlobalIspSettings.ExposureSetting.VideoProc.Value.ul = DEF_EXPOSURE_TIME; + m_GlobalIspSettings.ExposureSetting.Mode = 0; + m_GlobalIspSettings.FocusMode = KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUS | KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_FULLRANGE; + m_GlobalIspSettings.FocusSetting.VideoProc.Value.ul = 100; + break; + case KSCAMERA_EXTENDEDPROP_SCENEMODE_SPORT: + m_GlobalIspSettings.ISOMode = KSCAMERA_EXTENDEDPROP_ISO_AUTO; + m_GlobalIspSettings.EVCompensation.Value = 0; + m_GlobalIspSettings.EVCompensation.Mode = KSCAMERA_EXTENDEDPROP_EVCOMP_FULLSTEP; + m_GlobalIspSettings.WhiteBalanceSetting.VideoProc.Value.ul = WhiteBalanceDefault; + m_GlobalIspSettings.WhiteBalanceSetting.Mode = KSCAMERA_EXTENDEDPROP_WHITEBALANCE_TEMPERATURE; + m_GlobalIspSettings.WhiteBalanceMode = KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO; + m_GlobalIspSettings.ExposureMode = KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO; + m_GlobalIspSettings.ExposureSetting.VideoProc.Value.ul = DEF_EXPOSURE_TIME; + m_GlobalIspSettings.ExposureSetting.Mode = 0; + m_GlobalIspSettings.FocusMode = KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUS | KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_FULLRANGE; + m_GlobalIspSettings.FocusSetting.VideoProc.Value.ul = 100; + break; + case KSCAMERA_EXTENDEDPROP_SCENEMODE_SNOW: + m_GlobalIspSettings.ISOMode = KSCAMERA_EXTENDEDPROP_ISO_AUTO; + m_GlobalIspSettings.EVCompensation.Value = 0; + m_GlobalIspSettings.EVCompensation.Mode = KSCAMERA_EXTENDEDPROP_EVCOMP_FULLSTEP; + m_GlobalIspSettings.WhiteBalanceSetting.VideoProc.Value.ul = WhiteBalanceDefault; + m_GlobalIspSettings.WhiteBalanceSetting.Mode = KSCAMERA_EXTENDEDPROP_WHITEBALANCE_TEMPERATURE; + m_GlobalIspSettings.WhiteBalanceMode = KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO; + m_GlobalIspSettings.ExposureMode = KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO; + m_GlobalIspSettings.ExposureSetting.VideoProc.Value.ul = DEF_EXPOSURE_TIME; + m_GlobalIspSettings.ExposureSetting.Mode = 0; + m_GlobalIspSettings.FocusMode = KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUS | KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_FULLRANGE; + m_GlobalIspSettings.FocusSetting.VideoProc.Value.ul = 100; + break; + case KSCAMERA_EXTENDEDPROP_SCENEMODE_NIGHT: + m_GlobalIspSettings.ISOMode = KSCAMERA_EXTENDEDPROP_ISO_AUTO; + m_GlobalIspSettings.EVCompensation.Value = 0; + m_GlobalIspSettings.EVCompensation.Mode = KSCAMERA_EXTENDEDPROP_EVCOMP_FULLSTEP; + m_GlobalIspSettings.WhiteBalanceSetting.VideoProc.Value.ul = WhiteBalanceDefault; + m_GlobalIspSettings.WhiteBalanceSetting.Mode = KSCAMERA_EXTENDEDPROP_WHITEBALANCE_TEMPERATURE; + m_GlobalIspSettings.WhiteBalanceMode = KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO; + m_GlobalIspSettings.ExposureMode = KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO; + m_GlobalIspSettings.ExposureSetting.VideoProc.Value.ul = DEF_EXPOSURE_TIME; + m_GlobalIspSettings.ExposureSetting.Mode = 0; + m_GlobalIspSettings.FocusMode = KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUS | KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_FULLRANGE; + m_GlobalIspSettings.FocusSetting.VideoProc.Value.ul = 100; + break; + case KSCAMERA_EXTENDEDPROP_SCENEMODE_BEACH: + m_GlobalIspSettings.ISOMode = KSCAMERA_EXTENDEDPROP_ISO_AUTO; + m_GlobalIspSettings.EVCompensation.Value = 0; + m_GlobalIspSettings.EVCompensation.Mode = KSCAMERA_EXTENDEDPROP_EVCOMP_FULLSTEP; + m_GlobalIspSettings.WhiteBalanceSetting.VideoProc.Value.ul = WhiteBalanceDefault; + m_GlobalIspSettings.WhiteBalanceSetting.Mode = KSCAMERA_EXTENDEDPROP_WHITEBALANCE_TEMPERATURE; + m_GlobalIspSettings.WhiteBalanceMode = KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO; + m_GlobalIspSettings.ExposureMode = KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO; + m_GlobalIspSettings.ExposureSetting.VideoProc.Value.ul = DEF_EXPOSURE_TIME; + m_GlobalIspSettings.ExposureSetting.Mode = 0; + m_GlobalIspSettings.FocusMode = KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUS | KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_FULLRANGE; + m_GlobalIspSettings.FocusSetting.VideoProc.Value.ul = 100; + break; + case KSCAMERA_EXTENDEDPROP_SCENEMODE_SUNSET: + m_GlobalIspSettings.ISOMode = KSCAMERA_EXTENDEDPROP_ISO_AUTO; + m_GlobalIspSettings.EVCompensation.Value = 0; + m_GlobalIspSettings.EVCompensation.Mode = KSCAMERA_EXTENDEDPROP_EVCOMP_FULLSTEP; + m_GlobalIspSettings.WhiteBalanceSetting.VideoProc.Value.ul = WhiteBalanceDefault; + m_GlobalIspSettings.WhiteBalanceSetting.Mode = KSCAMERA_EXTENDEDPROP_WHITEBALANCE_TEMPERATURE; + m_GlobalIspSettings.WhiteBalanceMode = KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO; + m_GlobalIspSettings.ExposureMode = KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO; + m_GlobalIspSettings.ExposureSetting.VideoProc.Value.ul = DEF_EXPOSURE_TIME; + m_GlobalIspSettings.ExposureSetting.Mode = 0; + m_GlobalIspSettings.FocusMode = KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUS | KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_FULLRANGE; + m_GlobalIspSettings.FocusSetting.VideoProc.Value.ul = 100; + break; + case KSCAMERA_EXTENDEDPROP_SCENEMODE_CANDLELIGHT: + m_GlobalIspSettings.ISOMode = KSCAMERA_EXTENDEDPROP_ISO_AUTO; + m_GlobalIspSettings.EVCompensation.Value = 0; + m_GlobalIspSettings.EVCompensation.Mode = KSCAMERA_EXTENDEDPROP_EVCOMP_FULLSTEP; + m_GlobalIspSettings.WhiteBalanceSetting.VideoProc.Value.ul = WhiteBalanceDefault; + m_GlobalIspSettings.WhiteBalanceSetting.Mode = KSCAMERA_EXTENDEDPROP_WHITEBALANCE_TEMPERATURE; + m_GlobalIspSettings.WhiteBalanceMode = KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO; + m_GlobalIspSettings.ExposureMode = KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO; + m_GlobalIspSettings.ExposureSetting.VideoProc.Value.ul = DEF_EXPOSURE_TIME; + m_GlobalIspSettings.ExposureSetting.Mode = 0; + m_GlobalIspSettings.FocusMode = KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUS | KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_FULLRANGE; + m_GlobalIspSettings.FocusSetting.VideoProc.Value.ul = 100; + break; + case KSCAMERA_EXTENDEDPROP_SCENEMODE_LANDSCAPE: + m_GlobalIspSettings.ISOMode = KSCAMERA_EXTENDEDPROP_ISO_AUTO; + m_GlobalIspSettings.EVCompensation.Value = 0; + m_GlobalIspSettings.EVCompensation.Mode = KSCAMERA_EXTENDEDPROP_EVCOMP_FULLSTEP; + m_GlobalIspSettings.WhiteBalanceSetting.VideoProc.Value.ul = WhiteBalanceDefault; + m_GlobalIspSettings.WhiteBalanceSetting.Mode = KSCAMERA_EXTENDEDPROP_WHITEBALANCE_TEMPERATURE; + m_GlobalIspSettings.WhiteBalanceMode = KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO; + m_GlobalIspSettings.ExposureMode = KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO; + m_GlobalIspSettings.ExposureSetting.VideoProc.Value.ul = DEF_EXPOSURE_TIME; + m_GlobalIspSettings.ExposureSetting.Mode = 0; + m_GlobalIspSettings.FocusMode = KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUS | KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_FULLRANGE; + m_GlobalIspSettings.FocusSetting.VideoProc.Value.ul = 100; + break; + case KSCAMERA_EXTENDEDPROP_SCENEMODE_NIGHTPORTRAIT: + m_GlobalIspSettings.ISOMode = KSCAMERA_EXTENDEDPROP_ISO_AUTO; + m_GlobalIspSettings.EVCompensation.Value = 0; + m_GlobalIspSettings.EVCompensation.Mode = KSCAMERA_EXTENDEDPROP_EVCOMP_FULLSTEP; + m_GlobalIspSettings.WhiteBalanceSetting.VideoProc.Value.ul = WhiteBalanceDefault; + m_GlobalIspSettings.WhiteBalanceSetting.Mode = KSCAMERA_EXTENDEDPROP_WHITEBALANCE_TEMPERATURE; + m_GlobalIspSettings.WhiteBalanceMode = KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO; + m_GlobalIspSettings.ExposureMode = KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO; + m_GlobalIspSettings.ExposureSetting.VideoProc.Value.ul = DEF_EXPOSURE_TIME; + m_GlobalIspSettings.ExposureSetting.Mode = 0; + m_GlobalIspSettings.FocusMode = KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUS | KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_FULLRANGE; + m_GlobalIspSettings.FocusSetting.VideoProc.Value.ul = 100; + break; + case KSCAMERA_EXTENDEDPROP_SCENEMODE_BACKLIT: + m_GlobalIspSettings.ISOMode = KSCAMERA_EXTENDEDPROP_ISO_AUTO; + m_GlobalIspSettings.EVCompensation.Value = 0; + m_GlobalIspSettings.EVCompensation.Mode = KSCAMERA_EXTENDEDPROP_EVCOMP_FULLSTEP; + m_GlobalIspSettings.WhiteBalanceSetting.VideoProc.Value.ul = WhiteBalanceDefault; + m_GlobalIspSettings.WhiteBalanceSetting.Mode = KSCAMERA_EXTENDEDPROP_WHITEBALANCE_TEMPERATURE; + m_GlobalIspSettings.WhiteBalanceMode = KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO; + m_GlobalIspSettings.ExposureMode = KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO; + m_GlobalIspSettings.ExposureSetting.VideoProc.Value.ul = DEF_EXPOSURE_TIME; + m_GlobalIspSettings.ExposureSetting.Mode = 0; + m_GlobalIspSettings.FocusMode = KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUS | KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_FULLRANGE; + m_GlobalIspSettings.FocusSetting.VideoProc.Value.ul = 100; + break; + default: + break; + } + DBG_TRACE("ExposureMode=0x%016llX, ExposureTime=%lld00ns", + m_GlobalIspSettings.ExposureMode, m_GlobalIspSettings.ExposureSetting.VideoProc.Value.ll); + return status; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_FOCUSPRIORITY +NTSTATUS +CSensorSimulation:: +GetFocusPriority( + _Inout_ CExtendedProperty *pProperty +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + *pProperty = CExtendedProperty(m_GlobalIspSettings.FocusPriority); + DBG_TRACE("Is %s", m_GlobalIspSettings.FocusPriority ? "On" : "Off"); + return STATUS_SUCCESS; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_FOCUSPRIORITY +NTSTATUS +CSensorSimulation:: +SetFocusPriority( + _In_ CExtendedProperty *pProperty +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + m_GlobalIspSettings.FocusPriority = pProperty->Flags; + return STATUS_SUCCESS; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_VIDEOHDR. +NTSTATUS +CSensorSimulation:: +GetVideoHDR( + _Inout_ CExtendedProperty *pProperty +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + pProperty->Flags = m_VideoHDR; + pProperty->Capability = KSCAMERA_EXTENDEDPROP_VIDEOHDR_OFF | + KSCAMERA_EXTENDEDPROP_VIDEOHDR_ON | + KSCAMERA_EXTENDEDPROP_VIDEOHDR_AUTO; + pProperty->PinId = m_VideoIndex; + return STATUS_SUCCESS; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_VIDEOHDR. +NTSTATUS +CSensorSimulation:: +SetVideoHDR( + _In_ CExtendedProperty *pProperty +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + m_VideoHDR = pProperty->Flags; + return STATUS_SUCCESS; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_VFR. +NTSTATUS +CSensorSimulation:: +GetVFR( + _Inout_ CExtendedProperty *pProperty +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + *pProperty = CExtendedProperty( m_VFR ); + pProperty->PinId = m_VideoIndex; + return STATUS_SUCCESS; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_VFR. +NTSTATUS +CSensorSimulation:: +SetVFR( + _In_ CExtendedProperty *pProperty +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + m_VFR = pProperty->Flags; + return STATUS_SUCCESS; +} + +// KSPROPERTY_CAMERACONTROL_EXTENDED_ZOOM Get handler +NTSTATUS +CSensorSimulation:: +GetZoom( + _Inout_ CExtendedVidProcSetting *pProperty +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + + *pProperty = CExtendedVidProcSetting(m_ExtendedZoom); + pProperty->Capability = KSCAMERA_EXTENDEDPROP_ZOOM_DEFAULT | + KSCAMERA_EXTENDEDPROP_ZOOM_DIRECT | + KSCAMERA_EXTENDEDPROP_ZOOM_SMOOTH ; + + // Update the zoom factor. + // + // I am using the same pFilter->m_Zoom value here just to keep the extended and legacy + // controls tied together. It is not necessary to implement both in a real driver. + // However the legacy control was implemented first in this simulation and leaving both + // in place might have some value. But supporting both adds the question of whether they + // should both present a consistent view of zoom or if they should just save and report + // the values set for their own respective properties. + // + // I have chosen to support a consistent view of the zoom factor between the extended and + // legacy controls. Given that the legacy control was implemented first, I have chosen + // to keep that storage and rescale the value set by the extended zoom to match the + // legacy settings. + // + // But to make the two controls more consistent, I have changed the reported Ocular Focal + // Length to 2^16, which matches the implied denominator in Q16 format - the format used + // by this extended property. So as long as lOcularFocalLength is 0x10000, the following + // function should not change the reported value at all. This should simplify any + // debugging or tracing. + // + DBG_TRACE("Legacy Zoom Value = %d", m_Zoom.Value); + + // To ensure we clear out the high order value in the VideoProc + // union, we'll assign the LONG to LONGLONG field. + pProperty->m_Setting.VideoProc.Value.ll = (LONG) (TO_Q16(m_Zoom.Value) / m_FocalLength.lOcularFocalLength); + + DBG_TRACE("Extended Zoom Value = %d", pProperty->GetLONG()); + + return STATUS_SUCCESS; +} + +// KSPROPERTY_CAMERACONTROL_EXTENDED_ZOOM Set handler +NTSTATUS +CSensorSimulation:: +SetZoom( + _In_ CExtendedVidProcSetting *pProperty +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + + NTSTATUS ntStatus = STATUS_SUCCESS; + + LONG Zoom = pProperty->GetLONG(); + DBG_TRACE("Extended Zoom Value = %d", Zoom); + + m_ExtendedZoom = *pProperty; + DBG_TRACE("Zoom Flags = 0x%016llX", m_ExtendedZoom.Flags); + + // Take us directly to the position if we're doing a direct zoom. + // Depends on IHVs, KSCAMERA_EXTENDEDPROP_ZOOM_DEFAULT may be either direct zoom or + // smooth zoom. Here direct zoom will be performed when DEFAULT is specified. + if( pProperty->Flags != KSCAMERA_EXTENDEDPROP_ZOOM_SMOOTH ) + { + // Convert back to legacy notation... + // + // I am using the same pFilter->m_Zoom value here just to keep the extended and legacy + // controls tied together. It is not necessary to implement both in a real driver. + // However the legacy control was implemented first in this simulation and leaving both + // in place might have some value. But supporting both adds the question of whether they + // should both present a consistent view of zoom or if they should just save and report + // the values set for their own respective properties. + // + // I have chosen to support a consistent view of the zoom factor between the extended and + // legacy controls. Given that the legacy control was implemented first, I have chosen + // to keep that storage and rescale the value set by the extended zoom to match the + // legacy settings. + // + // But to make the two controls more consistent, I have changed the reported Ocular Focal + // Length to 2^16, which matches the implied denominator in Q16 format - the format used + // by this extended property. So as long as lOcularFocalLength is 0x10000, the following + // function should not change the reported value at all. This should simplify any + // debugging or tracing. + // + m_Zoom.Value = FROM_Q16(Zoom, m_FocalLength.lOcularFocalLength); + DBG_TRACE("Legacy Zoom Value = %d", m_Zoom.Value); + } + + // It's always success; this is a sync control, so this isn't really used. + m_ExtendedZoom.Result = ntStatus; + + DBG_LEAVE("()=0x%08X", ntStatus); + return ntStatus; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_VIDEOSTABILIZATION. +NTSTATUS +CSensorSimulation:: +GetVideoStabilization( + _Inout_ CExtendedProperty *pProperty +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + + *pProperty = CExtendedProperty(m_VideoStabilization); + pProperty->Capability = KSCAMERA_EXTENDEDPROP_VIDEOSTABILIZATION_OFF | + KSCAMERA_EXTENDEDPROP_VIDEOSTABILIZATION_ON | + KSCAMERA_EXTENDEDPROP_VIDEOSTABILIZATION_AUTO; + pProperty->PinId = m_VideoIndex; + + return STATUS_SUCCESS; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_VIDEOSTABILIZATION. +NTSTATUS +CSensorSimulation:: +SetVideoStabilization( + _In_ CExtendedProperty *pProperty +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + + m_VideoStabilization = pProperty->Flags; + return STATUS_SUCCESS; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_OIS. +NTSTATUS +CSensorSimulation:: +GetOpticalImageStabilization( + _Inout_ CExtendedProperty *pProperty +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + + *pProperty = CExtendedProperty(m_OpticalImageStabilization); + pProperty->Capability = KSCAMERA_EXTENDEDPROP_OIS_OFF | + KSCAMERA_EXTENDEDPROP_OIS_ON | + KSCAMERA_EXTENDEDPROP_OIS_AUTO; + + return STATUS_SUCCESS; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_OIS. +NTSTATUS +CSensorSimulation:: +SetOpticalImageStabilization( + _In_ CExtendedProperty *pProperty +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + + m_OpticalImageStabilization = pProperty->Flags; + return STATUS_SUCCESS; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_ADVANCEDPHOTO. +NTSTATUS +CSensorSimulation:: +GetAdvancedPhoto( + _Inout_ CExtendedProperty *pProperty +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + + *pProperty = CExtendedProperty(m_AdvancedPhoto); + pProperty->PinId = GetStillIndex(); + pProperty->Capability = KSCAMERA_EXTENDEDPROP_ADVANCEDPHOTO_OFF | + KSCAMERA_EXTENDEDPROP_ADVANCEDPHOTO_AUTO | + KSCAMERA_EXTENDEDPROP_ADVANCEDPHOTO_HDR | + KSCAMERA_EXTENDEDPROP_ADVANCEDPHOTO_FNF | + KSCAMERA_EXTENDEDPROP_ADVANCEDPHOTO_ULTRALOWLIGHT; + return STATUS_SUCCESS; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_ADVANCEDPHOTO. +NTSTATUS +CSensorSimulation:: +SetAdvancedPhoto( + _In_ CExtendedProperty *pProperty +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + + m_AdvancedPhoto = pProperty->Flags; + return STATUS_SUCCESS; +} + +// Get [legacy] KSPROPERTY_CAMERACONTROL_FOCUS +NTSTATUS +CSensorSimulation:: +GetFocus( + _Inout_ PKSPROPERTY_CAMERACONTROL_S pKsCameraControl +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + + pKsCameraControl->Capabilities = KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL | + KSPROPERTY_CAMERACONTROL_FLAGS_AUTO | + KSPROPERTY_CAMERACONTROL_FLAGS_ABSOLUTE; + pKsCameraControl->Flags = KSPROPERTY_CAMERACONTROL_FLAGS_ABSOLUTE; + if (m_GlobalIspSettings.FocusMode & KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO) + { + pKsCameraControl->Flags |= KSPROPERTY_CAMERACONTROL_FLAGS_AUTO; + } + else if (m_GlobalIspSettings.FocusMode & KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_MANUAL) + { + pKsCameraControl->Flags |= KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_MANUAL; + } + pKsCameraControl->Value = m_GlobalIspSettings.FocusSetting.VideoProc.Value.ul; + + return STATUS_SUCCESS; +} + +// Set [legacy] KSPROPERTY_CAMERACONTROL_FOCUS +NTSTATUS +CSensorSimulation:: +SetFocus( + _In_ PKSPROPERTY_CAMERACONTROL_S pKsCameraControl +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + NTSTATUS ntStatus = STATUS_SUCCESS; + + if (pKsCameraControl->Flags & KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL) + { + // Convert relative values to absolute. + if (pKsCameraControl->Flags & KSPROPERTY_CAMERACONTROL_FLAGS_RELATIVE) + { + ntStatus = STATUS_INVALID_PARAMETER; + } + + if (NT_SUCCESS(ntStatus)) + { + // We report these changes in the next metadata payload. + m_GlobalIspSettings.FocusSetting.VideoProc.Value.ul = pKsCameraControl->Value; + m_GlobalIspSettings.FocusMode = + KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_MANUAL; + + m_FocusNotifier = nullptr; + m_FocusState = KSCAMERA_EXTENDEDPROP_FOCUSSTATE_SEARCHING; + + // Fire a one-shot timer to transition the focus state. + m_FocusTimer->Set( + -330000LL, // 33ms + FocusConvergence, + this); + } + + } + else if (pKsCameraControl->Flags & KSPROPERTY_CAMERACONTROL_FLAGS_AUTO) + { + m_GlobalIspSettings.FocusMode = KSPROPERTY_CAMERACONTROL_FLAGS_AUTO | KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK; + + m_FocusNotifier = nullptr; + m_FocusState = KSCAMERA_EXTENDEDPROP_FOCUSSTATE_SEARCHING; + + // Fire a one-shot timer to transition the focus state. + m_FocusTimer->Set( + -4000000LL, // 400ms + FocusConvergence, + this ); + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + + return ntStatus; +} + +// Get [legacy] KSPROPERTY_CAMERACONTROL_EXPOSURE +NTSTATUS +CSensorSimulation:: +GetExposure( + _Inout_ PKSPROPERTY_CAMERACONTROL_S pKsCameraControl +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + + pKsCameraControl->Capabilities = KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL | + KSPROPERTY_CAMERACONTROL_FLAGS_AUTO | + KSPROPERTY_CAMERACONTROL_FLAGS_ABSOLUTE; + pKsCameraControl->Flags = KSPROPERTY_CAMERACONTROL_FLAGS_ABSOLUTE; + if (m_GlobalIspSettings.ExposureMode & KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO) + { + pKsCameraControl->Flags |= KSPROPERTY_CAMERACONTROL_FLAGS_AUTO; + } + else if (m_GlobalIspSettings.ExposureMode & KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_MANUAL) + { + pKsCameraControl->Flags |= KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_MANUAL; + } + + pKsCameraControl->Value = Exposure100nsToBinaryLog(m_GlobalIspSettings.ExposureSetting.VideoProc.Value.ll); + + DBG_TRACE("ExposureMode=0x%016llX, ExposureTime=%lld00ns", + m_GlobalIspSettings.ExposureMode, m_GlobalIspSettings.ExposureSetting.VideoProc.Value.ll); + + return STATUS_SUCCESS; +} + +// Set [legacy] KSPROPERTY_CAMERACONTROL_EXPOSURE +NTSTATUS +CSensorSimulation:: +SetExposure( + _In_ PKSPROPERTY_CAMERACONTROL_S pKsCameraControl +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + NTSTATUS ntStatus = STATUS_SUCCESS; + + if (pKsCameraControl->Flags & KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL) + { + // Convert relative values to absolute. + if (pKsCameraControl->Flags & KSPROPERTY_CAMERACONTROL_FLAGS_RELATIVE || !ExposureBinaryLogIsValid(pKsCameraControl->Value)) + { + ntStatus = STATUS_INVALID_PARAMETER; + } + + if (NT_SUCCESS(ntStatus)) + { + // We report these changes in the next metadata payload + m_GlobalIspSettings.ExposureSetting.VideoProc.Value.ll = ExposureBinaryLogTo100ns(pKsCameraControl->Value); + m_GlobalIspSettings.ExposureMode = + KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_MANUAL; + } + } + else if (pKsCameraControl->Flags & KSPROPERTY_CAMERACONTROL_FLAGS_AUTO) + { + m_GlobalIspSettings.ExposureMode = KSPROPERTY_CAMERACONTROL_FLAGS_AUTO | KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK; + + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + + DBG_TRACE("ExposureMode=0x%016llX, ExposureTime=%lld00ns", + m_GlobalIspSettings.ExposureMode, m_GlobalIspSettings.ExposureSetting.VideoProc.Value.ll); + + return ntStatus; +} + +// Update the zoom factor over time. +void +CSensorSimulation:: +SmoothZoom() +{ + PAGED_CODE(); + +#define ZOOM_SPEED (LONG) (TO_Q16(1) * 0.07f) +#define ZOOM_SPEED_VIDEO_RECORDING (LONG) (TO_Q16(1) * 0.02f) +#define ZOOM_EPSILON (LONG)(TO_Q16(1) * 0.005f) + + LONG currentZoom = (LONG) + (TO_Q16(m_Zoom.Value) / + m_FocalLength.lOcularFocalLength); + LONG targetZoom = m_ExtendedZoom.GetLONG(); + + LONG ZoomDelta = abs(currentZoom - targetZoom); + + // Jump to target if we are very close + if( ZoomDelta <= ZOOM_EPSILON ) + { + currentZoom = targetZoom; + } + else + { + // Zoom direction + bool bZoomIn = (targetZoom > currentZoom); + LONG OpModeZoomSpeed = ZOOM_SPEED; + + // User lower zoom speed for video. + if( INVALID_PIN_INDEX != m_VideoIndex && + PinRunning == m_HardwareSimulation[m_VideoIndex]->GetState() ) + { + OpModeZoomSpeed = ZOOM_SPEED_VIDEO_RECORDING; + } + + // Calculate zoom step that is relative to current zoom factor. + // the bigger the current zoom factor the bigger the step. + LONG ZoomStep; + if( bZoomIn ) + { + ZoomStep = MULT_Q16(OpModeZoomSpeed, currentZoom); + } + else + { + ZoomStep = currentZoom - DIV_Q16(currentZoom, (TO_Q16(1) + OpModeZoomSpeed)); + } + + if( ZoomDelta < 2 * ZoomStep) + { + // decrease step when close to target + ZoomStep = MULT_Q16(ZoomDelta, ((LONG)(TO_Q16(1) * 0.4f))); + } + + if (bZoomIn) + { + currentZoom += ZoomStep; + } + else + { + currentZoom -= ZoomStep; + } + } + + // Convert back to the zoom factor. + m_Zoom.Value = FROM_Q16(currentZoom, m_FocalLength.lOcularFocalLength); + + if (!NT_SUCCESS(BoundsCheckSigned(m_Zoom.Value, ZoomRangeAndStep[0]))) + { + m_Zoom.Value = FROM_Q16(targetZoom, m_FocalLength.lOcularFocalLength); + } + + DBG_LEAVE(" m_Zoom.Value=%d, FocusMode=0x%016llX", m_Zoom.Value, m_GlobalIspSettings.FocusMode); +} + + +// Adjust the zoom for each preview frame if the zoom is in motion. +// This function is used by both the legacy relative zoom control +// and the extended property smooth zoom control. +void +CSensorSimulation:: +UpdateZoom(void) +{ + PAGED_CODE(); + + // Legacy zoom relative adjustment. + LONG Value = m_Zoom.Value + m_ZoomRelative.Value; + + if (NT_SUCCESS(BoundsCheckSigned(Value, ZoomRangeAndStep[0]))) + { + m_Zoom.Value = Value; + } + else + { + m_ZoomRelative.Value = 0; + } + + // Extended property zoom / smooth zoom adjustment. + SmoothZoom(); +} + +// Get [legacy] KSPROPERTY_CAMERACONTROL_ZOOM +NTSTATUS +CSensorSimulation:: +GetZoom( + _Inout_ PKSPROPERTY_CAMERACONTROL_S pKsCameraControl +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + + pKsCameraControl->Capabilities = m_Zoom.Capabilities; + pKsCameraControl->Flags = m_Zoom.Flags; + pKsCameraControl->Value = m_Zoom.Value; + + return STATUS_SUCCESS; +} + +// Set [legacy] KSPROPERTY_CAMERACONTROL_ZOOM +NTSTATUS +CSensorSimulation:: +SetZoom( + _In_ PKSPROPERTY_CAMERACONTROL_S pKsCameraControl +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + NTSTATUS ntStatus = STATUS_SUCCESS; + + if (pKsCameraControl->Flags & KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL) + { + // Convert relative values to absolute. + if (pKsCameraControl->Flags & KSPROPERTY_CAMERACONTROL_FLAGS_RELATIVE) + { + pKsCameraControl->Value += m_Zoom.Value; + } + + // Bounds check + ntStatus = BoundsCheckSigned(pKsCameraControl->Value, ZoomRangeAndStep[0]); + if (NT_SUCCESS(ntStatus)) + { + // We report these changes in the next metadata payload. + m_Zoom.Value = pKsCameraControl->Value; + m_Zoom.Flags = + KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL | + KSPROPERTY_CAMERACONTROL_FLAGS_ABSOLUTE; + } + } + else if (pKsCameraControl->Flags & KSPROPERTY_CAMERACONTROL_FLAGS_AUTO) + { + m_Zoom.Flags = KSPROPERTY_CAMERACONTROL_FLAGS_AUTO; + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + + return ntStatus; +} + +// Get [legacy] KSPROPERTY_CAMERACONTROL_ZOOM_RELATIVE +NTSTATUS +CSensorSimulation:: +GetZoomRelative( + _Inout_ PKSPROPERTY_CAMERACONTROL_S pKsCameraControl +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + + pKsCameraControl->Capabilities = m_ZoomRelative.Capabilities; + pKsCameraControl->Flags = m_ZoomRelative.Flags; + pKsCameraControl->Value = m_ZoomRelative.Value; + + return STATUS_SUCCESS; +} + +// Set [legacy] KSPROPERTY_CAMERACONTROL_ZOOM_RELATIVE +NTSTATUS +CSensorSimulation:: +SetZoomRelative( + _In_ PKSPROPERTY_CAMERACONTROL_S pKsCameraControl +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + NTSTATUS ntStatus = STATUS_SUCCESS; + + if (pKsCameraControl->Flags & KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL) + { + // Convert relative values to absolute. + if (pKsCameraControl->Flags & KSPROPERTY_CAMERACONTROL_FLAGS_RELATIVE) + { + ntStatus = STATUS_INVALID_PARAMETER; + } + else + { + // Bounds check + ntStatus = BoundsCheckSigned(pKsCameraControl->Value, ZoomRelativeRangeAndStep[0]); + } + + if (NT_SUCCESS(ntStatus)) + { + // We report these changes in the next metadata payload. + m_ZoomRelative.Value = pKsCameraControl->Value; + m_ZoomRelative.Flags = + KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL | + KSPROPERTY_CAMERACONTROL_FLAGS_ABSOLUTE; + } + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + + return ntStatus; +} + +// Get KSPROPERTY_CAMERACONTROL_PAN +NTSTATUS +CSensorSimulation:: +GetPan( + _Inout_ PKSPROPERTY_CAMERACONTROL_S pKsCameraControl +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + + pKsCameraControl->Capabilities = m_Pan.Capabilities; + pKsCameraControl->Flags = m_Pan.Flags; + pKsCameraControl->Value = m_Pan.Value; + + return STATUS_SUCCESS; +} + +// Set KSPROPERTY_CAMERACONTROL_PAN +NTSTATUS +CSensorSimulation:: +SetPan( + _In_ PKSPROPERTY_CAMERACONTROL_S pKsCameraControl +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + NTSTATUS ntStatus = STATUS_SUCCESS; + + if (pKsCameraControl->Flags & KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL) + { + // Convert relative values to absolute. + if (pKsCameraControl->Flags & KSPROPERTY_CAMERACONTROL_FLAGS_RELATIVE) + { + ntStatus = STATUS_INVALID_PARAMETER; + } + + if (NT_SUCCESS(ntStatus)) + { + // Bounds check + ntStatus = BoundsCheckSigned(pKsCameraControl->Value, PanRangeAndStep[0]); + if (NT_SUCCESS(ntStatus)) + { + // We report these changes in the next metadata payload. + m_Pan.Value = pKsCameraControl->Value; + m_Pan.Flags = + KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL | + KSPROPERTY_CAMERACONTROL_FLAGS_ABSOLUTE; + } + } + } + else if (pKsCameraControl->Flags & KSPROPERTY_CAMERACONTROL_FLAGS_AUTO) + { + m_Pan.Flags = KSPROPERTY_CAMERACONTROL_FLAGS_AUTO; + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + + return ntStatus; +} + +// Get KSPROPERTY_CAMERACONTROL_ROLL +NTSTATUS +CSensorSimulation:: +GetRoll( + _Inout_ PKSPROPERTY_CAMERACONTROL_S pKsCameraControl +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + + pKsCameraControl->Capabilities = m_Roll.Capabilities; + pKsCameraControl->Flags = m_Roll.Flags; + pKsCameraControl->Value = m_Roll.Value; + + return STATUS_SUCCESS; +} + +// Set KSPROPERTY_CAMERACONTROL_ROLL +NTSTATUS +CSensorSimulation:: +SetRoll( + _In_ PKSPROPERTY_CAMERACONTROL_S pKsCameraControl +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + NTSTATUS ntStatus = STATUS_SUCCESS; + + if (pKsCameraControl->Flags & KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL) + { + // Convert relative values to absolute. + if (pKsCameraControl->Flags & KSPROPERTY_CAMERACONTROL_FLAGS_RELATIVE) + { + ntStatus = STATUS_INVALID_PARAMETER; + } + if (NT_SUCCESS(ntStatus)) + { + // Bounds check + ntStatus = BoundsCheckSigned(pKsCameraControl->Value, RollRangeAndStep[0]); + if (NT_SUCCESS(ntStatus)) + { + // We report these changes in the next metadata payload. + m_Roll.Value = pKsCameraControl->Value; + m_Roll.Flags = + KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL | + KSPROPERTY_CAMERACONTROL_FLAGS_ABSOLUTE; + } + } + } + else if (pKsCameraControl->Flags & KSPROPERTY_CAMERACONTROL_FLAGS_AUTO) + { + m_Roll.Flags = KSPROPERTY_CAMERACONTROL_FLAGS_AUTO; + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + + return ntStatus; +} + +// Get KSPROPERTY_CAMERACONTROL_TILT +NTSTATUS +CSensorSimulation:: +GetTilt( + _Inout_ PKSPROPERTY_CAMERACONTROL_S pKsCameraControl +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + + pKsCameraControl->Capabilities = m_Tilt.Capabilities; + pKsCameraControl->Flags = m_Tilt.Flags; + pKsCameraControl->Value = m_Tilt.Value; + + return STATUS_SUCCESS; +} + +// Set KSPROPERTY_CAMERACONTROL_TILT +NTSTATUS +CSensorSimulation:: +SetTilt( + _In_ PKSPROPERTY_CAMERACONTROL_S pKsCameraControl +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + NTSTATUS ntStatus = STATUS_SUCCESS; + + if (pKsCameraControl->Flags & KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL) + { + // Convert relative values to absolute. + if (pKsCameraControl->Flags & KSPROPERTY_CAMERACONTROL_FLAGS_RELATIVE) + { + ntStatus = STATUS_INVALID_PARAMETER; + } + if (NT_SUCCESS(ntStatus)) + { + // Bounds check + ntStatus = BoundsCheckSigned(pKsCameraControl->Value, TiltRangeAndStep[0]); + if (NT_SUCCESS(ntStatus)) + { + // We report these changes in the next metadata payload. + m_Tilt.Value = pKsCameraControl->Value; + m_Tilt.Flags = + KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL | + KSPROPERTY_CAMERACONTROL_FLAGS_ABSOLUTE; + } + } + } + else if (pKsCameraControl->Flags & KSPROPERTY_CAMERACONTROL_FLAGS_AUTO) + { + m_Tilt.Flags = KSPROPERTY_CAMERACONTROL_FLAGS_AUTO; + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + + return ntStatus; +} + +// Get KSPROPERTY_CAMERACONTROL_FOCAL_LENGTH +NTSTATUS +CSensorSimulation:: +GetFocalLength( + _Inout_ PKSPROPERTY_CAMERACONTROL_FOCAL_LENGTH_S pKsCameraControl +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + + pKsCameraControl->lOcularFocalLength = m_FocalLength.lOcularFocalLength; + pKsCameraControl->lObjectiveFocalLengthMax = m_FocalLength.lObjectiveFocalLengthMax; + pKsCameraControl->lObjectiveFocalLengthMin = m_FocalLength.lObjectiveFocalLengthMin; + + return STATUS_SUCCESS; +} + +// Set KSPROPERTY_CAMERACONTROL_FOCAL_LENGTH +NTSTATUS +CSensorSimulation:: +SetFocalLength( + _In_ PKSPROPERTY_CAMERACONTROL_FOCAL_LENGTH_S pKsCameraControl +) +{ + PAGED_CODE(); + return STATUS_NOT_FOUND; +} + +// Get KSPROPERTY_VIDEOPROCAMP_BACKLIGHT_COMPENSATION +NTSTATUS +CSensorSimulation:: +GetBacklightCompensation( + _Inout_ PKSPROPERTY_VIDEOPROCAMP_S pKsCameraControl +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + + pKsCameraControl->Capabilities = m_BacklightCompensation.Capabilities; + pKsCameraControl->Flags = m_BacklightCompensation.Flags; + pKsCameraControl->Value = m_BacklightCompensation.Value; + + return STATUS_SUCCESS; +} + +// Set KSPROPERTY_VIDEOPROCAMP_BACKLIGHT_COMPENSATION +NTSTATUS +CSensorSimulation:: +SetBacklightCompensation( + _In_ PKSPROPERTY_VIDEOPROCAMP_S pKsCameraControl +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + NTSTATUS ntStatus = STATUS_SUCCESS; + + if (pKsCameraControl->Flags & KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL) + { + // Bounds check + ntStatus = BoundsCheckSigned(pKsCameraControl->Value, BacklightCompensationRangeAndStep[0]); + if (NT_SUCCESS(ntStatus)) + { + // We report these changes in the next metadata payload. + m_BacklightCompensation.Value = pKsCameraControl->Value; + m_BacklightCompensation.Flags = + KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL; + } + } + else if (pKsCameraControl->Flags & KSPROPERTY_CAMERACONTROL_FLAGS_AUTO) + { + m_BacklightCompensation.Flags = KSPROPERTY_CAMERACONTROL_FLAGS_AUTO; + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + + return ntStatus; +} + +// Get KSPROPERTY_VIDEOPROCAMP_BRIGHTNESS +NTSTATUS +CSensorSimulation:: +GetBrightness( + _Inout_ PKSPROPERTY_VIDEOPROCAMP_S pKsCameraControl +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + + pKsCameraControl->Capabilities = m_Brightness.Capabilities; + pKsCameraControl->Flags = m_Brightness.Flags; + pKsCameraControl->Value = m_Brightness.Value; + + return STATUS_SUCCESS; +} + +// Set KSPROPERTY_VIDEOPROCAMP_BRIGHTNESS +NTSTATUS +CSensorSimulation:: +SetBrightness( + _In_ PKSPROPERTY_VIDEOPROCAMP_S pKsCameraControl +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + NTSTATUS ntStatus = STATUS_SUCCESS; + + if (pKsCameraControl->Flags & KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL) + { + // Bounds check + ntStatus = BoundsCheckSigned(pKsCameraControl->Value, BrightnessRangeAndStep[0]); + if (NT_SUCCESS(ntStatus)) + { + // We report these changes in the next metadata payload. + m_Brightness.Value = pKsCameraControl->Value; + m_Brightness.Flags = + KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL; + } + } + else if (pKsCameraControl->Flags & KSPROPERTY_CAMERACONTROL_FLAGS_AUTO) + { + m_Brightness.Flags = KSPROPERTY_CAMERACONTROL_FLAGS_AUTO; + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + + return ntStatus; +} + +// Get KSPROPERTY_VIDEOPROCAMP_CONTRAST +NTSTATUS +CSensorSimulation:: +GetContrast( + _Inout_ PKSPROPERTY_VIDEOPROCAMP_S pKsCameraControl +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + + pKsCameraControl->Capabilities = m_Contrast.Capabilities; + pKsCameraControl->Flags = m_Contrast.Flags; + pKsCameraControl->Value = m_Contrast.Value; + + return STATUS_SUCCESS; +} + +// Set KSPROPERTY_VIDEOPROCAMP_CONTRAST +NTSTATUS +CSensorSimulation:: +SetContrast( + _In_ PKSPROPERTY_VIDEOPROCAMP_S pKsCameraControl +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + NTSTATUS ntStatus = STATUS_SUCCESS; + + if (pKsCameraControl->Flags & KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL) + { + // Bounds check + ntStatus = BoundsCheckSigned(pKsCameraControl->Value, ContrastRangeAndStep[0]); + if (NT_SUCCESS(ntStatus)) + { + // We report these changes in the next metadata payload. + m_Contrast.Value = pKsCameraControl->Value; + m_Contrast.Flags = + KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL; + } + } + else if (pKsCameraControl->Flags & KSPROPERTY_CAMERACONTROL_FLAGS_AUTO) + { + m_Contrast.Flags = KSPROPERTY_CAMERACONTROL_FLAGS_AUTO; + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + + return ntStatus; +} + +// Get KSPROPERTY_VIDEOPROCAMP_HUE +NTSTATUS +CSensorSimulation:: +GetHue( + _Inout_ PKSPROPERTY_VIDEOPROCAMP_S pKsCameraControl +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + + pKsCameraControl->Capabilities = m_Hue.Capabilities; + pKsCameraControl->Flags = m_Hue.Flags; + pKsCameraControl->Value = m_Hue.Value; + + return STATUS_SUCCESS; +} + +// Set KSPROPERTY_VIDEOPROCAMP_HUE +NTSTATUS +CSensorSimulation:: +SetHue( + _In_ PKSPROPERTY_VIDEOPROCAMP_S pKsCameraControl +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + NTSTATUS ntStatus = STATUS_SUCCESS; + + if (pKsCameraControl->Flags & KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL) + { + // Bounds check + ntStatus = BoundsCheckSigned(pKsCameraControl->Value, HueRangeAndStep[0]); + if (NT_SUCCESS(ntStatus)) + { + // We report these changes in the next metadata payload. + m_Hue.Value = pKsCameraControl->Value; + m_Hue.Flags = + KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL; + } + } + else if (pKsCameraControl->Flags & KSPROPERTY_CAMERACONTROL_FLAGS_AUTO) + { + m_Hue.Flags = KSPROPERTY_CAMERACONTROL_FLAGS_AUTO; + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + + return ntStatus; +} + +// Get KSPROPERTY_VIDEOPROCAMP_POWERLINE_FREQUENCY +NTSTATUS +CSensorSimulation:: +GetPowerlineFreq( + _Inout_ PKSPROPERTY_VIDEOPROCAMP_S pKsCameraControl +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + + pKsCameraControl->Capabilities = m_PowerLineFreq.Capabilities; + pKsCameraControl->Flags = m_PowerLineFreq.Flags; + pKsCameraControl->Value = m_PowerLineFreq.Value; + + return STATUS_SUCCESS; +} + +// Set KSPROPERTY_VIDEOPROCAMP_POWERLINE_FREQUENCY +NTSTATUS +CSensorSimulation:: +SetPowerlineFreq( + _In_ PKSPROPERTY_VIDEOPROCAMP_S pKsCameraControl +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + NTSTATUS ntStatus = STATUS_SUCCESS; + + if (pKsCameraControl->Flags & KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL) + { + // Bounds check + ntStatus = BoundsCheckSigned(pKsCameraControl->Value, PLFRangeAndStep[0]); + if (NT_SUCCESS(ntStatus)) + { + // We report these changes in the next metadata payload. + m_PowerLineFreq.Value = pKsCameraControl->Value; + m_PowerLineFreq.Flags = + KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL; + } + } + else if (pKsCameraControl->Flags & KSPROPERTY_CAMERACONTROL_FLAGS_AUTO) + { + m_PowerLineFreq.Flags = KSPROPERTY_CAMERACONTROL_FLAGS_AUTO; + m_PowerLineFreq.Value = 0; + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + + return ntStatus; +} + +// Get [legacy] KSPROPERTY_VIDEOPROCAMP_WHITEBALANCE +NTSTATUS +CSensorSimulation:: +GetWhiteBalance( + _Inout_ PKSPROPERTY_VIDEOPROCAMP_S pKsCameraControl +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + + pKsCameraControl->Capabilities = KSPROPERTY_CAMERACONTROL_FLAGS_AUTO | KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL; + if (m_GlobalIspSettings.WhiteBalanceMode & KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO) + { + pKsCameraControl->Flags = KSPROPERTY_CAMERACONTROL_FLAGS_AUTO; + } + else + { + pKsCameraControl->Flags = KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL; + } + + ULONG temperature = m_GlobalIspSettings.WhiteBalanceSetting.VideoProc.Value.ul; + if (m_GlobalIspSettings.WhiteBalanceSetting.Mode != KSCAMERA_EXTENDEDPROP_WHITEBALANCE_TEMPERATURE) + { + temperature = WhiteBalancePreset2Temperature(m_GlobalIspSettings.WhiteBalanceSetting.VideoProc.Value.ul); + } + pKsCameraControl->Value = (LONG)temperature; + + return STATUS_SUCCESS; +} + +// Set [legacy] KSPROPERTY_VIDEOPROCAMP_WHITEBALANCE +NTSTATUS +CSensorSimulation:: +SetWhiteBalance( + _In_ PKSPROPERTY_VIDEOPROCAMP_S pKsCameraControl +) +{ + PAGED_CODE(); + KScopedMutex lock(m_SensorMutex); + NTSTATUS ntStatus = STATUS_SUCCESS; + + if (pKsCameraControl->Flags & KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL) + { + // We report these changes in the next metadata payload. + m_GlobalIspSettings.WhiteBalanceSetting.VideoProc.Value.ul = pKsCameraControl->Value; + m_GlobalIspSettings.WhiteBalanceMode = + KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_MANUAL; + m_GlobalIspSettings.WhiteBalanceSetting.Mode = KSCAMERA_EXTENDEDPROP_WHITEBALANCE_TEMPERATURE; + } + else if (pKsCameraControl->Flags & KSPROPERTY_CAMERACONTROL_FLAGS_AUTO) + { + m_GlobalIspSettings.WhiteBalanceMode = + KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO; + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + + return ntStatus; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_HISTOGRAM. +NTSTATUS +CSensorSimulation:: +GetHistogram( + _Inout_ CExtendedProperty *pProperty +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + *pProperty = CExtendedProperty( m_Histogram ); + pProperty->PinId = m_PreviewIndex; + pProperty->Capability = 0; + return STATUS_SUCCESS; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_HISTOGRAM. +NTSTATUS +CSensorSimulation:: +SetHistogram( + _In_ CExtendedProperty *pProperty +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + m_Histogram = pProperty->Flags; + + // Adjust the reported metadata buffer size for preview. + m_MetadataInfo[m_PreviewIndex] = + CExtendedMetadata::METADATA_MAX + + (( m_Histogram == KSCAMERA_EXTENDEDPROP_HISTOGRAM_ON ) ? sizeof(CAMERA_METADATA_HISTOGRAM) : 0 ) ; + + return STATUS_SUCCESS; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_METADATA +NTSTATUS +CSensorSimulation:: +GetMetadata( + _Inout_ CExtendedMetadata *pProperty +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + *pProperty = m_MetadataInfo[pProperty->PinId]; + return STATUS_SUCCESS; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_METADATA +NTSTATUS +CSensorSimulation:: +SetMetadata( + _In_ CExtendedMetadata *pProperty +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + *pProperty = m_MetadataInfo[pProperty->PinId]; + return STATUS_SUCCESS; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_OPTIMIZATIONHINT +NTSTATUS +CSensorSimulation:: +GetOptimizationHint( + _Inout_ CExtendedProperty *pProperty +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + *pProperty = m_OptimizationHint; + pProperty->Capability = + KSCAMERA_EXTENDEDPROP_OPTIMIZATION_PHOTO | + KSCAMERA_EXTENDEDPROP_OPTIMIZATION_VIDEO | + KSCAMERA_EXTENDEDPROP_OPTIMIZATION_QUALITY | + KSCAMERA_EXTENDEDPROP_OPTIMIZATION_LATENCY | + KSCAMERA_EXTENDEDPROP_OPTIMIZATION_POWER | + KSCAMERA_EXTENDEDPROP_OPTIMIZATION_DEFAULT; + + DBG_TRACE( "Hint = 0x%016llX", m_OptimizationHint.Flags ); + + return STATUS_SUCCESS; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_OPTIMIZATIONHINT +NTSTATUS +CSensorSimulation:: +SetOptimizationHint( + _In_ CExtendedProperty *pProperty +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + m_OptimizationHint = *pProperty; + + DBG_TRACE( "Hint = 0x%016llX", m_OptimizationHint.Flags ); + + return STATUS_SUCCESS; +} + +// Get for PROPSETID_VIDCAP_CAMERACONTROL_VIDEO_STABILIZATION:0 +NTSTATUS +CSensorSimulation:: +GetVideoStabMode( + _Inout_ KSPROPERTY_CAMERACONTROL_VIDEOSTABILIZATION_MODE_S *pMode +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + pMode->Capabilities = + KSPROPERTY_CAMERACONTROL_VIDEOSTABILIZATION_MODE_FLAGS_AUTO | + KSPROPERTY_CAMERACONTROL_VIDEOSTABILIZATION_MODE_FLAGS_MANUAL ; + pMode->VideoStabilizationMode = m_VideoStabMode; + return STATUS_SUCCESS; +} + +// Set for PROPSETID_VIDCAP_CAMERACONTROL_VIDEO_STABILIZATION:0 +NTSTATUS +CSensorSimulation:: +SetVideoStabMode( + _In_ KSPROPERTY_CAMERACONTROL_VIDEOSTABILIZATION_MODE_S *pMode +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + m_VideoStabMode = pMode->VideoStabilizationMode; + return STATUS_SUCCESS; +} + +// Get for KSPROPERTY_CAMERACONTROL_FLASH_PROPERTY_ID +NTSTATUS +CSensorSimulation:: +GetFlash( + _Inout_ KSPROPERTY_CAMERACONTROL_FLASH_S *pFlash +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + pFlash->Capabilities = + KSPROPERTY_CAMERACONTROL_FLASH_FLAGS_AUTO | + KSPROPERTY_CAMERACONTROL_FLASH_FLAGS_MANUAL ; + pFlash->Flash = m_Flash; + return STATUS_SUCCESS; +} + +// Set for KSPROPERTY_CAMERACONTROL_FLASH_PROPERTY_ID +NTSTATUS +CSensorSimulation:: +SetFlash( + _In_ KSPROPERTY_CAMERACONTROL_FLASH_S *pFlash +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + m_Flash = pFlash->Flash; + return STATUS_SUCCESS; +} + +// Get for KSPROPERTY_CAMERACONTROL_IMAGE_PIN_CAPABILITY_PROPERTY_ID +NTSTATUS +CSensorSimulation:: +GetPinDependence( + _Inout_ KSPROPERTY_CAMERACONTROL_IMAGE_PIN_CAPABILITY_S *pCaps +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + // Limit capabilities to the valid set. + pCaps->Capabilities &= + ( KSPROPERTY_CAMERACONTROL_IMAGE_PIN_CAPABILITY_EXCLUSIVE_WITH_RECORD | + KSPROPERTY_CAMERACONTROL_IMAGE_PIN_CAPABILITY_SEQUENCE_EXCLUSIVE_WITH_RECORD ); + + return STATUS_SUCCESS; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOTRIGGERTIME +NTSTATUS +CSensorSimulation:: +GetTriggerTime( + _Inout_ CExtendedProperty *pQPC +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + *pQPC = CExtendedProperty(0); + pQPC->PinId = GetStillIndex(); + NTSTATUS Status = GetQPC( &pQPC->m_Value.Value.ull ); + + pQPC->Flags = ( pQPC->m_Value.Value.ull != 0 ) ? + KSPROPERTY_CAMERA_PHOTOTRIGGERTIME_SET : + KSPROPERTY_CAMERA_PHOTOTRIGGERTIME_CLEAR; + return Status; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOTRIGGERTIME +NTSTATUS +CSensorSimulation:: +SetTriggerTime( + _In_ CExtendedProperty *pQPC +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + ULONGLONG Time = ( pQPC->Flags & KSPROPERTY_CAMERA_PHOTOTRIGGERTIME_SET ) ? *pQPC : 0; + return SetQPC( Time ); +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_TORCHMODE +NTSTATUS +CSensorSimulation:: +GetTorchMode( + _Inout_ CExtendedProperty *pTorchMode +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + *pTorchMode = m_TorchMode; + pTorchMode->Capability = + KSCAMERA_EXTENDEDPROP_VIDEOTORCH_OFF | + KSCAMERA_EXTENDEDPROP_VIDEOTORCH_ON | + KSCAMERA_EXTENDEDPROP_VIDEOTORCH_ON_ADJUSTABLEPOWER; + + return STATUS_SUCCESS; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_TORCHMODE +NTSTATUS +CSensorSimulation:: +SetTorchMode( + _In_ CExtendedProperty *pTorchMode +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + m_TorchMode = *pTorchMode; + return STATUS_SUCCESS; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOMODE +NTSTATUS +CSensorSimulation:: +GetPhotoMode( + _Inout_ CExtendedPhotoMode *pPhotoMode +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + *pPhotoMode = m_PhotoMode; + return STATUS_SUCCESS; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOMODE +NTSTATUS +CSensorSimulation:: +SetPhotoModeAsync( + _In_ CExtendedPhotoMode *pPhotoMode, + _In_ CNotifier *Notifier // Use to notify the framework that the control is done. +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + DBG_ENTER("()"); + + m_PhotoMode = *pPhotoMode; + + NTSTATUS Status = + SetPinMode( pPhotoMode->Flags, pPhotoMode->RequestedHistoryFrames() ); + + // Tell the caller that we're done. + Notifier->Set(); + + DBG_LEAVE("()=0x%08X",Status); + return Status; +} + +/* + * KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOMAXFRAMERATE Get Handler + */ +NTSTATUS +CSensorSimulation:: +GetPhotoMaxFrameRate( + _Inout_ CExtendedProperty *pPhotoMaxFrameRate +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + *pPhotoMaxFrameRate = m_MaxFrameRate; + pPhotoMaxFrameRate->Capability = KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL; + pPhotoMaxFrameRate->PinId = GetStillIndex(); + return STATUS_SUCCESS; +} + +/* + * KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOMAXFRAMERATE Set Handler + */ +NTSTATUS +CSensorSimulation:: +SetPhotoMaxFrameRateAsync( + _In_ CExtendedProperty *pPhotoMaxFrameRate, + _In_ CNotifier *Notifier // Use to notify the framework that the control is done. +) +{ + PAGED_CODE(); + + ULONGLONG TimePerFrame = 0; + { + // Note: Don't lock around SetPhotoFrameRate() to avoid a priority inversion. + KScopedMutex lock( m_SensorMutex ); + + m_MaxFrameRate = *pPhotoMaxFrameRate; + + // Try to apply the setting immediately. + LARGE_INTEGER PerformanceTime = { m_MaxFrameRate.m_Value.Value.ratio.LowPart }; + LONGLONG Frequency = m_MaxFrameRate.m_Value.Value.ratio.HighPart; + + if( Frequency != 0 ) + { + TimePerFrame = KSCONVERT_PERFORMANCE_TIME( Frequency, PerformanceTime ); + } + } + + DBG_TRACE( "SetPhotoFrameRate(%lld)", TimePerFrame ); + + // Consume any error; we'll set the rate again when the pin starts. + SetPhotoFrameRate( TimePerFrame ); + + // Tell the caller that we're done. + Notifier->Set(); + + return STATUS_SUCCESS; +} + +/* + * KSPROPERTY_CAMERACONTROL_EXTENDED_WARMSTART + */ +NTSTATUS +CSensorSimulation:: +GetWarmStart( + _Inout_ CExtendedProperty *pWarmStart +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + *pWarmStart = m_WarmStartEnabled[pWarmStart->PinId]; + pWarmStart->Capability = + KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL | + KSCAMERA_EXTENDEDPROP_WARMSTART_MODE_DISABLED | + KSCAMERA_EXTENDEDPROP_WARMSTART_MODE_ENABLED ; + return STATUS_SUCCESS; +} + +/* + * KSPROPERTY_CAMERACONTROL_EXTENDED_WARMSTART + */ +NTSTATUS +CSensorSimulation:: +SetWarmStartAsync( + _In_ CExtendedProperty *pWarmStart, + _In_ CNotifier *Notifier // Use to notify the framework that the control is done. +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + m_WarmStartEnabled[pWarmStart->PinId] = *pWarmStart; + + // Tell the caller that we're done. + Notifier->Set(); + + return STATUS_SUCCESS; +} + +/* + * KSPROPERTY_CAMERACONTROL_EXTENDED_MAXVIDFPS_PHOTORES Get Handler + */ +NTSTATUS +CSensorSimulation:: +GetMaxVideoFpsForPhotoRes( + _Inout_ CExtendedMaxVideoFpsForPhotoRes *pPhotoRes +) +{ + PAGED_CODE(); + + // We're just a simulation and have no constraints - set 30FPS. + pPhotoRes->PreviewFPSNum() =30; + pPhotoRes->PreviewFPSDenom() = 1; + pPhotoRes->CaptureFPSNum() = 30; + pPhotoRes->CaptureFPSDenom() = 1; + + return STATUS_SUCCESS; +} + +/* + * KSPROPERTY_CAMERACONTROL_EXTENDED_MAXVIDFPS_PHOTORES Set Handler + */ +NTSTATUS +CSensorSimulation:: +SetMaxVideoFpsForPhotoRes( + _In_ CExtendedMaxVideoFpsForPhotoRes *pPhotoRes +) +{ + PAGED_CODE(); + + // We don't really use this call. + return STATUS_SUCCESS; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_FIELDOFVIEW +NTSTATUS +CSensorSimulation:: +GetFieldOfView( + _Inout_ CExtendedFieldOfView *pFieldOfView +) +{ + PAGED_CODE(); + + // Report dummy values. + *pFieldOfView = CExtendedFieldOfView(); + pFieldOfView->NormalizedFocalLengthX() = 35; + pFieldOfView->NormalizedFocalLengthY() = 35; + + return STATUS_SUCCESS; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_FIELDOFVIEW +NTSTATUS +CSensorSimulation:: +GetCameraAngleOffset( + _Inout_ CExtendedCameraAngleOffset *pAngle +) +{ + PAGED_CODE(); + + // Just return zeros. We're dead-on, no tilt. + *pAngle = CExtendedCameraAngleOffset(); + + return STATUS_SUCCESS; +} + +// KSPROPERTY_CAMERACONTROL_EXTENDED_ROI_CONFIGCAPS Get handler +NTSTATUS +CSensorSimulation:: +GetRoiConfigCaps( + _Inout_ CRoiConfig *pCaps +) +{ + PAGED_CODE(); + // No state accessed... + //KScopedMutex lock( m_SensorMutex ); + + // Our CRoiConfig ctor defines our capabilities. + *pCaps = CRoiConfig(); + + return STATUS_SUCCESS; +} + +// Per Frame Settings Caps +struct SOC_PFS_CAPS +{ + KSCAMERA_PERFRAMESETTING_CAP_HEADER Hdr; + SOC_CAP_WITH_STEPPING_LONGLONG ExposureTime; + KSCAMERA_PERFRAMESETTING_CAP_ITEM_HEADER Flash; + SOC_CAP_WITH_STEPPING EVCompensation; + SOC_CAP_WITH_STEPPING Iso; + SOC_CAP_WITH_STEPPING Focus; + KSCAMERA_PERFRAMESETTING_CAP_ITEM_HEADER StillConfirmation; +} ; + +const ULONG SOC_PFS_NUMCAPS = 6; + +// Our predefined Variable Photo Sequence capabilities. +SOC_PFS_CAPS g_PFSCaps = +{ + // KSCAMERA_PERFRAMESETTING_CAP_HEADER + { sizeof(SOC_PFS_CAPS), SOC_PFS_NUMCAPS, 0 }, //Hdr + // ExposureTime + { + // SOC_CAP_WITH_STEPPING_LONGLONG + { + // KSCAMERA_PERFRAMESETTING_CAP_ITEM_HEADER + sizeof(g_PFSCaps.ExposureTime), //Size + KSCAMERA_PERFRAMESETTING_ITEM_EXPOSURE_TIME, //Type + KSCAMERA_PERFRAMESETTING_AUTO //Flags + | KSCAMERA_PERFRAMESETTING_MANUAL + }, + { + // KSPROPERTY_STEPPING_LONGLONG + 1000, //SteppingDelta (in us) + { + // KSPROPERTY_BOUNDS_LONGLONG + MIN_EXPOSURE_TIME, //SignedMinimum + MAX_EXPOSURE_TIME //SignedMaximum (arbitrary - 1 hour) + } + } + }, + // Flash + { + // KSCAMERA_PERFRAMESETTING_CAP_ITEM_HEADER + sizeof(g_PFSCaps.Flash), + KSCAMERA_PERFRAMESETTING_ITEM_FLASH, + KSCAMERA_EXTENDEDPROP_FLASH_OFF + | KSCAMERA_EXTENDEDPROP_FLASH_ON + | KSCAMERA_EXTENDEDPROP_FLASH_ON_ADJUSTABLEPOWER + | KSCAMERA_EXTENDEDPROP_FLASH_AUTO + | KSCAMERA_EXTENDEDPROP_FLASH_AUTO_ADJUSTABLEPOWER + | KSCAMERA_EXTENDEDPROP_FLASH_REDEYEREDUCTION + }, + // ExposureCompensation + { + // SOC_CAP_WITH_STEPPING + { + // KSCAMERA_PERFRAMESETTING_CAP_ITEM_HEADER + sizeof(g_PFSCaps.EVCompensation), //Size + KSCAMERA_PERFRAMESETTING_ITEM_EXPOSURE_COMPENSATION, //Type + KSCAMERA_PERFRAMESETTING_AUTO //Flags + | KSCAMERA_EXTENDEDPROP_EVCOMP_SIXTHSTEP + | KSCAMERA_EXTENDEDPROP_EVCOMP_QUARTERSTEP + | KSCAMERA_EXTENDEDPROP_EVCOMP_THIRDSTEP + | KSCAMERA_EXTENDEDPROP_EVCOMP_HALFSTEP + | KSCAMERA_EXTENDEDPROP_EVCOMP_FULLSTEP + }, + { + // KSPROPERTY_STEPPING_LONG + 1, //SteppingDelta + 0, //Reserved + { + // KSPROPERTY_BOUNDS_LONG + -2, //SignedMinimum + 2 //SignedMaximum + } + } + }, + // Iso + { + // SOC_CAP_WITH_STEPPING + { + // KSCAMERA_PERFRAMESETTING_CAP_ITEM_HEADER + sizeof(g_PFSCaps.Iso), + KSCAMERA_PERFRAMESETTING_ITEM_ISO, + KSCAMERA_EXTENDEDPROP_ISO_AUTO + | KSCAMERA_EXTENDEDPROP_ISO_MANUAL + }, + { + // KSPROPERTY_STEPPING_LONG + 50, //SteppingDelta + 0, //Reserved + { + // KSPROPERTY_BOUNDS_LONG + 50, //SignedMinimum + 256000 //SignedMaximum + } + } + }, + // Focus + { + // SOC_CAP_WITH_STEPPING + { + // KSCAMERA_PERFRAMESETTING_CAP_ITEM_HEADER + sizeof(g_PFSCaps.Focus), //Size + KSCAMERA_PERFRAMESETTING_ITEM_FOCUS, //Type + KSCAMERA_PERFRAMESETTING_AUTO //Flags + | KSCAMERA_PERFRAMESETTING_MANUAL + }, + { + // KSPROPERTY_STEPPING_LONG + 10, //SteppingDelta (We'll use centimeters for kicks) + 0, //Reserved + { + // KSPROPERTY_BOUNDS_LONG + 0, //SignedMinimum + 10000 //SignedMaximum (100 meters max) + } + } + }, + // StillConfirmation + { + // KSCAMERA_PERFRAMESETTING_CAP_ITEM_HEADER + sizeof(g_PFSCaps.StillConfirmation), + KSCAMERA_PERFRAMESETTING_ITEM_PHOTOCONFIRMATION, + 0 + }, +}; + +// Get KSPROPERTY_CAMERACONTROL_PERFRAMESETTING_CAPABILITY +_Success_(return == 0) +NTSTATUS +CSensorSimulation:: +GetPfsCaps( + _Inout_opt_ KSCAMERA_PERFRAMESETTING_CAP_HEADER *Caps, + _Inout_ ULONG *Size +) +{ + PAGED_CODE(); + + if( Caps ) + { + size_t Length = sizeof(g_PFSCaps); + if( Size && *Size < Length ) + { + Length = *Size; + } + RtlCopyMemory( Caps, &g_PFSCaps, Length ); + } + + *Size = sizeof(g_PFSCaps); + + return STATUS_SUCCESS; +} + +// KSPROPERTY_CAMERACONTROL_EXTENDED_ROI_ISPCONTROL Get handler +NTSTATUS +CSensorSimulation:: +GetRoi( + _Inout_ CRoiProperty *pRoi +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + // *pRoiProperty = ... + *pRoi = CRoiProperty(); // initialize it. + + // Add control properties to it. + pRoi->Add( &m_RoiWhiteBalance ) ; + pRoi->Add( &m_RoiExposureMode ) ; + pRoi->Add( &m_RoiFocusMode ) ; + + // Log what we created. + pRoi->Log(); + + return STATUS_SUCCESS; +} + +// KSPROPERTY_CAMERACONTROL_EXTENDED_ROI_ISPCONTROL Set handler +NTSTATUS +CSensorSimulation:: +SetRoiAsync( + _In_ CRoiProperty *pRoi, + _In_ CNotifier *Notifier +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + if( pRoi->GetControlCount() == 0 ) + { + // Clear all settings. + DBG_TRACE("Clearing all settings..."); + m_RoiWhiteBalance = CWhiteBalanceRoiIspControl(); + m_RoiExposureMode = CExposureRoiIspControl(); + m_RoiFocusMode = CFocusRoiIspControl(); + } + else + { + // Parse our settings. + DBG_TRACE("Parsing new settings..."); + if( m_RoiWhiteBalance. + Init( pRoi, KSPROPERTY_CAMERACONTROL_EXTENDED_WHITEBALANCEMODE ) ) + { + m_GlobalIspSettings.WhiteBalanceMode = m_RoiWhiteBalance.GetFlags(); + m_RoiWhiteBalance.Log(); + } + if( m_RoiExposureMode. + Init( pRoi, KSPROPERTY_CAMERACONTROL_EXTENDED_EXPOSUREMODE ) ) + { + m_GlobalIspSettings.ExposureMode = m_RoiExposureMode.GetFlags(); + m_RoiExposureMode.Log(); + } + if( m_RoiFocusMode. + Init( pRoi, KSPROPERTY_CAMERACONTROL_EXTENDED_FOCUSMODE ) ) + { + ULONGLONG Flags = m_RoiFocusMode.GetFlags(); + DBG_TRACE("Requesting FocusMode = 0x%016llX", Flags); + + if( Flags ) + { + // If we've been asked to unlock, remove any lock flags. + if( Flags & KSCAMERA_EXTENDEDPROP_FOCUS_UNLOCK ) + { + // Change our operation so we go back to the previous state. + Flags = m_GlobalIspSettings.FocusMode & + ~( KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK | + KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUSLOCK ); + } + + // Handle the stand-alone lock case by setting the appropriate flags. + if( ( Flags & + (KSCAMERA_EXTENDEDPROP_FOCUS_MODE_MASK | + KSCAMERA_EXTENDEDPROP_FOCUS_MODE_ADVANCED_MASK) ) == + KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK ) + { + if( m_GlobalIspSettings.FocusMode & KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO ) + { + Flags = + m_GlobalIspSettings.FocusMode | KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK; + } + if( m_GlobalIspSettings.FocusMode & KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUS ) + { + Flags = + m_GlobalIspSettings.FocusMode |= KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUSLOCK; + } + } + // Note: We do not simulate deferred completion. + m_FocusState = KSCAMERA_EXTENDEDPROP_FOCUSSTATE_FOCUSED; + + // Record the Focus mode. + DBG_TRACE("Recording FocusMode = 0x%016llX", Flags); + m_GlobalIspSettings.FocusMode = Flags; + } + + m_RoiFocusMode.Log(); + } + } + + Notifier->Set(); + return STATUS_SUCCESS; +} + +// KSPROPERTY_CAMERACONTROL_EXTENDED_ROI_ISPCONTROL size handler +ULONG +CSensorSimulation:: +SizeOfRoi() +{ + PAGED_CODE(); + + return + sizeof( CRoiProperty ) + + m_RoiWhiteBalance.GetSize() + + m_RoiExposureMode.GetSize() + + m_RoiFocusMode.GetSize() ; +} + + +// Get KSPROPERTY_VIDEOCONTROL_MODE. +NTSTATUS +CSensorSimulation:: +GetVideoControlMode( + _Inout_ KSPROPERTY_VIDEOCONTROL_MODE_S *pMode +) +{ + PAGED_CODE(); + KScopedMutex lock( m_SensorMutex ); + + pMode->Mode = + KS_VideoControlFlag_IndependentImagePin | + KS_VideoControlFlag_Trigger | + KS_VideoControlFlag_StartPhotoSequenceCapture | + KS_VideoControlFlag_StopPhotoSequenceCapture; + + return STATUS_SUCCESS; +} + +// Set KSPROPERTY_VIDEOCONTROL_MODE. +NTSTATUS +CSensorSimulation:: +SetVideoControlMode( + _In_ KSPROPERTY_VIDEOCONTROL_MODE_S *pMode +) +{ + PAGED_CODE(); + + // Note: Trigger will acquire a lock on the hwsim. + return Trigger( pMode->StreamIndex, pMode->Mode ); +} + diff --git a/avscamera/sys/SensorSimulation.h b/avscamera/sys/SensorSimulation.h new file mode 100644 index 000000000..ecf043e38 --- /dev/null +++ b/avscamera/sys/SensorSimulation.h @@ -0,0 +1,280 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + SensorSimulation.h + + Abstract: + + Simulation class declaration. Derived from the base CSensor object. + + This class simulates a theoretical model camera. The camera supports + every modern camera control available and produces a simulation of its + behavior. In most cases this simulation is very very basic because we + have no actual hardware to work with and there producing a visible + effect in the simulation would be difficult or expensive to do. + + History: + + created 5/8/2014 + +**************************************************************************/ + +#pragma once +class CSensorSimulation : + public CSensor +{ +protected: + const + KSFILTER_DESCRIPTOR *m_Descriptors; + + LARGE_INTEGER m_StartTime; + + // State for the various controls. + RECT m_FocusRect; + BOOL m_AutoFocusLock; + BOOL m_AutoExposureLock; + BOOL m_AutoWhitebalanceLock; + ULONG m_Flash; + ULONG m_VideoStabMode; + CExtendedProperty *m_WarmStartEnabled; + CExtendedPhotoMode m_PhotoMode; + ULONG m_RequestedHistoryFrames; + CExtendedProperty m_MaxFrameRate; + ULONGLONG m_FaceDetectionFlags; + ULONG m_FaceDetectionMax; + ULONG m_FaceDetectionCurrentMax; + ULONGLONG m_SceneMode; + CExtendedProperty m_TorchMode; + CExtendedProperty m_OptimizationHint; + CExtendedMetadata *m_MetadataInfo; + PKSCAMERA_PERFRAMESETTING_HEADER m_pPerFrameSettings; + ULONG m_PFSSize; + KSCAMERA_EXTENDEDPROP_FOCUSSTATE m_FocusState; + ULONGLONG m_VFR; + ULONGLONG m_VideoHDR; + ULONGLONG m_VideoStabilization; + ULONGLONG m_Histogram; + ULONGLONG m_OpticalImageStabilization; + ULONGLONG m_AdvancedPhoto; + + CWhiteBalanceRoiIspControl m_RoiWhiteBalance; + CExposureRoiIspControl m_RoiExposureMode; + CFocusRoiIspControl m_RoiFocusMode; + + VIDCAP_PROPERTY m_Pan; + VIDCAP_PROPERTY m_Roll; + VIDCAP_PROPERTY m_Tilt; + + // Zoom parameters + FOCAL_LENGTH_PROPERTY m_FocalLength; // A description of the zoom limits. + VIDCAP_PROPERTY m_Zoom; // Absolute zoom position / mode. + VIDCAP_PROPERTY m_ZoomRelative; // Relative zoom position / mode. + + // Cached Q16-adjusted bounds. I do not use the zoom value stored below. + // + // I am using the same m_Zoom value here to keep both the extended and legacy + // controls tied together. It is not necessary to implement both in a real driver. + // However the legacy control was implemented first in this simulation and leaving both + // in place might have some value. But supporting both adds the question of whether they + // should both present a consistent view of zoom or if they should just save and report + // the values set for their own respective properties. + // + // I have chosen to support a consistent view of the zoom factor between the extended and + // legacy controls. Given that the legacy control was implemented first, I have chosen + // to keep that storage and rescale the value set by the extended zoom to match the + // legacy settings. + // + // But to make the two controls more consistent, I have changed the reported Ocular Focal + // Length to 2^16, which matches the implied denominator in Q16 format - the format used + // by this extended property. So as long as lOcularFocalLength is 0x10000, the following + // function should not change the reported value at all. This should simplify any + // debugging or tracing. + + CExtendedVidProcSetting m_ExtendedZoom; // Must update zoom value on the fly. + + VIDCAP_PROPERTY m_PowerLineFreq; // Power Line Frequency setting. + VIDCAP_PROPERTY m_BacklightCompensation; + VIDCAP_PROPERTY m_Brightness; + VIDCAP_PROPERTY m_Contrast; + VIDCAP_PROPERTY m_Hue; + + ULONG m_IsoResult; + ULONG m_EvCompResult; + ULONG m_WhiteBalanceResult; + ULONG m_ExposureResult; + ULONG m_SceneModeResult; + ULONG m_FaceDetectionResult; + ULONG m_FocusResult; + + ULONGLONG m_LastFocusMode; + ULONG m_LastFocusSetting; + + CRefPtr m_FocusNotifier; // A reference to the CNotifier for any Focus in flight. + KPassiveTimer *m_FocusTimer; // A timer object used to simulate a focus event. + CRefPtr m_FocusRectNotifier; + KPassiveTimer *m_FocusRectTimer; + + static const ULONG METADATA_MAX=4096; + +protected: + // + // Deal with scene mode changes. + // + NTSTATUS + UpdateSettings( + _In_ ULONGLONG ullSceneMode + ); + + // + // Put us back to the defaults specified in the DDI. + // + virtual + NTSTATUS + ProgramDefaults(); + +public: + // + // This simulation is based solely on the Descriptors. Yours might + // need additional information. + // + CSensorSimulation( + _In_ CCaptureDevice *Device, + _In_ const KSFILTER_DESCRIPTOR *Descriptors + ); + + virtual ~CSensorSimulation(); + + // + // Parse descriptors to determine what pin simulations are required. + // Do memory allocations, etc. + // + virtual + NTSTATUS + Initialize(); + + virtual + NTSTATUS + GetFocusState( + _Out_ KSCAMERA_EXTENDEDPROP_FOCUSSTATE *FocusState + ); + + // Lets you know if Variable Photo Sequence is active. + // Note: This doesn't tell you if the pin is running. + virtual + BOOLEAN + IsVPSActive() + { + return + KSCAMERA_EXTENDEDPROP_PHOTOMODE_SEQUENCE_SUB_VARIABLE==m_PhotoMode.SubMode() && + m_pIspSettings ; + } + + // Callback to handle focus convergence. + static + void + FocusConvergence( + _In_opt_ PVOID Context + ); + + // Callback to handle focus convergence. + static + void + FocusRectConvergence( + _In_opt_ PVOID Context + ); + + // Declare the property controls handled by this sensor. + DECLARE_PROPERTY( KSPROPERTY_VIDEOCONTROL_MODE_S, VideoControlMode ); + + DECLARE_PROPERTY_ASYNC( KSPROPERTY_CAMERACONTROL_REGION_OF_INTEREST_S, FocusRect ); + + DECLARE_PROPERTY( KSPROPERTY_CAMERACONTROL_FLASH_S, Flash ); + DECLARE_PROPERTY_GET( KSPROPERTY_CAMERACONTROL_IMAGE_PIN_CAPABILITY_S, PinDependence ); + DECLARE_PROPERTY( CExtendedProperty, TriggerTime ); + DECLARE_PROPERTY( CExtendedProperty, TorchMode ); + + DECLARE_PROPERTY( CExtendedProperty, ExtendedFlash ); + + DECLARE_PROPERTY_ASYNC( CExtendedVidProcSetting, Focus ); + DECLARE_PROPERTY_ASYNC_NOCANCEL( CExtendedProperty, Iso ); + DECLARE_PROPERTY_ASYNC_NOCANCEL( CExtendedVidProcSetting, IsoAdvanced ); + DECLARE_PROPERTY_ASYNC_NOCANCEL( CExtendedEvCompensation, EvCompensation ); + DECLARE_PROPERTY_ASYNC_NOCANCEL( CExtendedVidProcSetting, WhiteBalance ); + DECLARE_PROPERTY_ASYNC_NOCANCEL( CExtendedVidProcSetting, Exposure ); + DECLARE_PROPERTY( CExtendedProperty, PhotoConfirmation ); + DECLARE_PROPERTY_ASYNC_NOCANCEL( CExtendedProperty, SceneMode ); + DECLARE_PROPERTY( CExtendedProperty, FocusPriority ); + DECLARE_PROPERTY( CExtendedProperty, VideoHDR ); + DECLARE_PROPERTY( CExtendedProperty, VFR ); + DECLARE_PROPERTY( CExtendedVidProcSetting, Zoom ); + DECLARE_PROPERTY( CExtendedProperty, VideoStabilization ); + DECLARE_PROPERTY( CExtendedProperty, Histogram ); + DECLARE_PROPERTY( CExtendedMetadata, Metadata ); + DECLARE_PROPERTY( CExtendedProperty, OpticalImageStabilization ); + DECLARE_PROPERTY( CExtendedProperty, OptimizationHint ); + DECLARE_PROPERTY( CExtendedProperty, AdvancedPhoto ); + DECLARE_PROPERTY( CExtendedVidProcSetting, FaceDetection ); + DECLARE_PROPERTY_ASYNC_NOCANCEL( CExtendedPhotoMode, PhotoMode ); + DECLARE_PROPERTY_ASYNC_NOCANCEL( CExtendedProperty, PhotoMaxFrameRate ); + DECLARE_PROPERTY_ASYNC_NOCANCEL( CExtendedProperty, WarmStart ); + DECLARE_PROPERTY( CExtendedMaxVideoFpsForPhotoRes, MaxVideoFpsForPhotoRes ); + DECLARE_PROPERTY_GET( CExtendedFieldOfView, FieldOfView ); + DECLARE_PROPERTY_GET( CExtendedCameraAngleOffset, CameraAngleOffset ); + + DECLARE_PROPERTY_VARSIZE_ASYNC_NOCANCEL( CRoiProperty, Roi ); + + DECLARE_PROPERTY( KSPROPERTY_CAMERACONTROL_VIDEOSTABILIZATION_MODE_S, VideoStabMode ); + + // Update the zoom factor over time. + void + SmoothZoom(); + + // Apply a zoom factor. + virtual + void + UpdateZoom(void); + + // Declare legacy controls that are needed. + DECLARE_PROPERTY(KSPROPERTY_CAMERACONTROL_S, Exposure); + DECLARE_PROPERTY(KSPROPERTY_CAMERACONTROL_S, Focus); + DECLARE_PROPERTY(KSPROPERTY_CAMERACONTROL_S, Zoom); + DECLARE_PROPERTY(KSPROPERTY_CAMERACONTROL_S, ZoomRelative); + DECLARE_PROPERTY(KSPROPERTY_CAMERACONTROL_S, Pan); + DECLARE_PROPERTY(KSPROPERTY_CAMERACONTROL_S, Roll); + DECLARE_PROPERTY(KSPROPERTY_CAMERACONTROL_S, Tilt); + DECLARE_PROPERTY(KSPROPERTY_CAMERACONTROL_FOCAL_LENGTH_S, FocalLength); + + DECLARE_PROPERTY(KSPROPERTY_VIDEOPROCAMP_S, BacklightCompensation); + DECLARE_PROPERTY(KSPROPERTY_VIDEOPROCAMP_S, Brightness); + DECLARE_PROPERTY(KSPROPERTY_VIDEOPROCAMP_S, Contrast); + DECLARE_PROPERTY(KSPROPERTY_VIDEOPROCAMP_S, Hue); + DECLARE_PROPERTY(KSPROPERTY_VIDEOPROCAMP_S, WhiteBalance); + DECLARE_PROPERTY(KSPROPERTY_VIDEOPROCAMP_S, PowerlineFreq); + + DECLARE_PROPERTY_GET( CRoiConfig, RoiConfigCaps ); + + // Special-case Per-Frame settings since it requires a variable length structure. + _Success_(return == 0) + virtual + NTSTATUS + GetPfsCaps( + _Inout_opt_ KSCAMERA_PERFRAMESETTING_CAP_HEADER *Caps, + _Inout_ ULONG *Size + ); +}; + +// Constants... +const LONG WHITEBALANCE_MIN = 0; +const LONG WHITEBALANCE_MAX = 15000; +const LONG WHITEBALANCE_STEP= 1; +const LONG WHITEBALANCE_DEF = 5000; + +const LONG EXPOSURE_BILOG_MIN = -10; +const LONG EXPOSURE_BILOG_MAX = 9; +const LONG EXPOSURE_BILOG_STEP = 1; +const LONG EXPOSURE_BILOG_DEF = -5; \ No newline at end of file diff --git a/avscamera/sys/Synthesizer.cpp b/avscamera/sys/Synthesizer.cpp new file mode 100644 index 000000000..fbb0147fb --- /dev/null +++ b/avscamera/sys/Synthesizer.cpp @@ -0,0 +1,1282 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + Synthesizer.cpp + + Abstract: + + This file contains the implementation of CSynthesizer. + + The base image synthesis and overlay class. These classes provide + image synthesis (pixel, color-bar, etc...) onto buffers of various + formats. + + Internally, all CSynthesizer objects represent a pixel as a 32 bit + quantity with 8 bits per sample. The base CSynthesizer implements + all rendering functionality with this assumption in mind. It + simplifies rendering substantially and means we do not need to re- + implement rendering functions for each format. + + Most end formats can be synthesized from this uncompressed format + with overhead that is slightly worse than a copy. Color spaces are + handled by using a rendering palette that is unique for each. + + History: + + created 04/14/2014 + +**************************************************************************/ + +#include "Common.h" + +/************************************************************************** + + PAGED CODE + +**************************************************************************/ + +#ifdef ALLOC_PRAGMA +#pragma code_seg("PAGE") +#endif // ALLOC_PRAGMA + +/************************************************************************** + + Constants + +**************************************************************************/ + +// +// Standard definition of EIA-189-A color bars. The actual color definitions +// are either in CRGB24Synthesizer or CYUVSynthesizer. +// + +const COLOR CSynthesizer::m_ColorBars[8] = +{MAGENTA, BLUE, GREEN, BLACK, WHITE, YELLOW, RED, CYAN }; + + +// +// Corner registration block colors. +// +const COLOR g_TopLeft = GREY; +const COLOR g_TopRight = BLUE; +const COLOR g_BotLeft = RED; +const COLOR g_BotRight = GREEN; + +// +// Persistent stats. +// +LONGLONG CSynthesizer::m_RelPts=0; +LONGLONG CSynthesizer::m_QpcTime=0; +ULONG CSynthesizer::m_Frame=0; + +// +// The following is an 8x8 bitmapped font for use in the text overlay +// code. +// +const UCHAR +CSynthesizer:: +m_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} +}; + +BOOLEAN +CSynthesizer:: +Initialize() +/*++ + +Routine Description: + + Class initialization. + + Set the buffer the synthesizer generates images to. + Override to do any additional processing you think is needed. + +Arguments: + + none + +Return Value: + + TRUE - success. + +--*/ +{ + PAGED_CODE(); + + // Capture the working palette. + // Recast it to a point to an array of primaries. + if( !(m_Colors = GetPalette()) ) + { + return FALSE; + } + + // Clear the globals. This occurs redundantly, but hey ... + // ... test output doesn't make sense if we don't do this. + m_RelPts=0; + m_QpcTime=0; + m_Frame=0; + + // + // Allocate a scratch buffer for the synthesizer. + // + NT_ASSERT(m_Length); + if( !m_Length ) + { + return FALSE; + } + + m_Buffer = new (PagedPool) UCHAR[m_Length]; + NT_ASSERT(m_Buffer); + if( !m_Buffer ) + { + return FALSE; + } + + // + // Allocate a scratch buffer for the Gradient. + // + m_GradientBmp = new (PagedPool) CKsRgbQuad[m_Width * MAX_COLOR]; + NT_ASSERT(m_GradientBmp); + if( !m_GradientBmp ) + { + SAFE_DELETE_ARRAY( m_Buffer ); + return FALSE; + } + + return TRUE; +} + +LONGLONG +ConvertPerfTime( + _In_ LONGLONG Freq, + _In_ LONGLONG Time +) +{ + PAGED_CODE(); + + LARGE_INTEGER tL ; + tL.QuadPart = Time; + return KSCONVERT_PERFORMANCE_TIME( Freq, tL ); +} + +void +CSynthesizer:: +Destroy() +/*++ + +Routine Description: + + Clean up from initialize. + + Note: A good place to generate synthesis stats. + +Arguments: + + none + +Return Value: + + void. + +--*/ +{ + PAGED_CODE(); + + SAFE_DELETE_ARRAY( m_Buffer ); + SAFE_DELETE_ARRAY( m_GradientBmp ); + + // Report rendering times. + DBG_TRACE( "Synthesis Time - Total = %lld", ConvertPerfTime( m_Frequency.QuadPart, m_SynthesisTime ) ); + DBG_TRACE( " Count = %d", m_SynthesisCount ); + DBG_TRACE( " Avg = %lld", ConvertPerfTime( m_Frequency.QuadPart, m_SynthesisCount ? m_SynthesisTime / m_SynthesisCount : 0 ) ); + DBG_TRACE( " Commit Time - Total = %lld", ConvertPerfTime( m_Frequency.QuadPart, m_CommitTime ) ); + DBG_TRACE( " Count = %d", m_CommitCount ); + DBG_TRACE( " Avg = %lld", ConvertPerfTime( m_Frequency.QuadPart, m_CommitCount ? m_CommitTime / m_CommitCount : 0 ) ); +} + +NTSTATUS +CSynthesizer:: +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: + + success / failure + +--*/ +{ + PAGED_CODE(); + + if( !m_Buffer ) + { + return STATUS_INVALID_DEVICE_STATE; + } + + const COLOR *CurColor = m_ColorBars; + ULONG ColorCount = SIZEOF_ARRAY (m_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 (m_ColorBars [((x * ColorCount) / m_Width)]); + } + + PUCHAR ImageEnd = m_Cursor; + + // + // Copy the synthesized line to all subsequent lines. + // + for (ULONG line = 0; line < m_Height; line++) + { + GetImageLocation (0, line); + + RtlCopyMemory ( + m_Cursor, + ImageStart, + ImageEnd - ImageStart + ); + } + + // Paint the top left and top right registrations boxes. + GetImageLocation(0,0); + + ImageStart = m_Cursor; + for(ULONG x =0; x < m_Width; x++) + { + if(x < (m_Height/16)) + { + PutPixel(g_TopLeft); + } + else if(x > (m_Width - (m_Height / 16))) + { + PutPixel(g_TopRight); + } + else + { + PutPixel (m_ColorBars [((x * ColorCount) / m_Width)]); + } + } + + ImageEnd = m_Cursor; + + for (ULONG line = 0; line < m_Height/16; line++) + { + GetImageLocation (0, line); + + RtlCopyMemory ( + m_Cursor, + ImageStart, + ImageEnd - ImageStart + ); + } + + // Paint the bottom left and bottom right registrations boxes. + GetImageLocation(0, (15*m_Height) / 16); + ImageStart = m_Cursor; + for(ULONG x =0; x < m_Width; x++) + { + if(x < (m_Height/16)) + { + PutPixel(g_BotLeft); + } + else if(x > (m_Width - (m_Height / 16))) + { + PutPixel(g_BotRight); + } + else + { + PutPixel (m_ColorBars [((x * ColorCount) / m_Width)]); + } + } + + ImageEnd = m_Cursor; + + for (ULONG line = 0; line < m_Height/16; line++) + { + GetImageLocation (0, line+(15*m_Height) / 16); + + RtlCopyMemory ( + m_Cursor, + ImageStart, + ImageEnd - ImageStart + ); + } + + return STATUS_SUCCESS; +} + +void +CSynthesizer:: +EncodeNumber( + _In_ ULONG LocY, + _In_ UINT32 Number, + _In_ COLOR LowColor, + _In_ COLOR HighColor +) +/* +Routine Description: + + Overlays an encoded number over the synthesized image. The image buffer + used is the set synthesis buffer. The pattern is scaled to fit the image. + +Arguments: + + LocY - + The row on the image to begin the overlay. This must + be inside the image. + + Number - + The number to encode + + LowColor - + The color to represent 0 (a clear bit) + + HighColor - + The color to represent 1 (a set bit) + +Return Value: + + void + +*/ +{ + PAGED_CODE(); + + if( !m_Buffer ) + { + return; + } + + NT_ASSERT(LocY <= m_Height); + + GetImageLocation(0, LocY); + UINT32 mask = 0x1; + + PUCHAR ImageStart = m_Cursor; + for(ULONG i = 0; i < 32; i++) + { + for(ULONG j = 0; j < m_Width/32; j++) + { + if(Number & mask) + { + PutPixel(HighColor); + } + else + { + PutPixel(LowColor); + } + } + mask = mask << 1; + } + PUCHAR ImageEnd = m_Cursor; + + // + // Copy the synthesized line to all subsequent lines. + // + for (ULONG line = 1; line < m_Height/16; line++) + { + RtlCopyMemory ( + GetImageLocation (0, line + LocY), + ImageStart, + ImageEnd - ImageStart + ); + } +} + +void +CSynthesizer:: +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 or no text will be rendered. + 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 or no text will be rendered. + POSITION_CENTER may be used to indicate vertical centering. + + Scaling - + Normally, the overlay is done in an 8x8 font. A scaling of + 2 indicates 16x16, 3 indicates 24x24 and so forth. + + Text - + A null terminated 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 + +--*/ + +{ + PAGED_CODE(); + + // Validate internal state. + if( !m_Buffer ) + { + return; + } + + NT_ASSERT( (LocX <= m_Width || LocX == POSITION_CENTER) && + (LocY <= m_Height || LocY == POSITION_CENTER) && + Text !=nullptr && + (BgColor < MAX_COLOR || BgColor == TRANSPARENT) && + (FgColor < MAX_COLOR || FgColor == TRANSPARENT) + ); + + // Validate parameters. + if( !( (LocX <= m_Width || LocX == POSITION_CENTER) && + (LocY <= m_Height || LocY == POSITION_CENTER) && + Text !=nullptr && + (BgColor < MAX_COLOR || BgColor == TRANSPARENT) && + (FgColor < MAX_COLOR || FgColor == TRANSPARENT) ) ) + { + return; + } + + size_t StrLen = 0; + PCHAR CurChar; + + // + // Determine the character length of the string. + // + if( !NT_SUCCESS( RtlStringCchLengthA( Text, m_Width, &StrLen ) ) ) + { + // Invalid parameter. + return; + } + + // + // 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 = (ULONG) ((StrLen * (((size_t)Scaling) << 3)) + 1 + StrLen); +#else // NO_CHARACTER_SEPARATION + ULONG LenX = (ULONG) ((StrLen * (((size_t_)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) + { + //LocX = min( 0, ((LONG)m_Width - (LONG)LenX) >> 1); + if (LenX >= m_Width) + { + LocX = 0; + } + else + { + LocX = (m_Width - LenX) >> 1; + } + } + + if (LocY == POSITION_CENTER) + { + //LocY = min( 0, ((LONG)m_Width - (LONG)LenY) >> 1); + if (LenY >= m_Height) + { + LocY = 0; + } + else + { + LocY = (m_Height - 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( SpaceY ) + { + if( BgColor != TRANSPARENT ) + { + for (ULONG x = 0; x < LenX && x < SpaceX; x++) + { + PutPixel (BgColor); + } + } + SpaceY--; + } + LocY++; + + // + // 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 ) + { + UCHAR CharBase = m_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); + } + } +} + +void +CSynthesizer::ApplyGradient( + _In_ ULONG LocY, + _In_ COLOR Gradient +) +/*++ + +Routine Description: + + Synthesize a horizontal gradient bar from a given color + +Arguments: + + LocY - + row + + Gradient - + A starting color + +Return Value: + + void + +--*/ +{ + PAGED_CODE(); + + if( !m_Buffer ) + { + return; + } + + NT_ASSERT(LocY <= m_Height); + + PUCHAR Image = GetImageLocation(0, LocY); + PUCHAR RowBmp = reinterpret_cast(&m_GradientBmp[Gradient*m_Width]); + ULONG Stride = m_Width * sizeof(CKsRgbQuad); + + // + // Copy the synthesized line to all lines. + // + for (ULONG line = 0; line < m_Height/16; line++) + { + RtlCopyMemory ( + Image, + RowBmp, + Stride + ); + Image += Stride; + } +} + +// +// Synthesize +// +// Fill the image buffer with some base image. All h/w simulations will +// call this function to generate a base image - even for jpeg. +// +NTSTATUS +CSynthesizer:: +Synthesize() +/*++ + +Routine Description: + + Fill the image buffer with some base image. All h/w simulations will + call this function to generate a base image. + +Arguments: + + None + +Return Value: + + Success / failure + +--*/ +{ + PAGED_CODE(); + + if( !m_Buffer ) + { + return STATUS_INVALID_DEVICE_STATE; + } + + // + // Generate a "time stamp" just to overlay it onto the capture image. + // It makes it more exciting than bars that do nothing. + // + SynthesizeBars(); + + ApplyGradient( (m_Height)/16, RED); + ApplyGradient( (2*m_Height)/16, GREEN); + ApplyGradient( (3*m_Height)/16, BLUE); + ApplyGradient( (4*m_Height)/16, WHITE); + + EncodeNumber((5*m_Height)/16, (UINT32)m_Frame, BLACK, WHITE); + EncodeNumber((6*m_Height)/16, (UINT32)(m_QpcTime), BLACK, WHITE); + + CHAR Text [256]; + RtlStringCbPrintfA(Text, sizeof(Text), "Frame: %ld", m_Frame); + + // + // Overlay the frame # onto the scratch space image. + // + OverlayText ( + POSITION_CENTER, + (m_Height - 28), + 1, + Text, + BLACK, + WHITE + ); + + // + // Add a description of the frame type/width/height. + // + RtlStringCbPrintfA(Text, sizeof(Text), "%s (%dx%d)", m_FormatName, m_Width, m_Height); + OverlayText ( + 0, + (m_Height - 28), + 1, + Text, + BLACK, + WHITE + ); + return STATUS_SUCCESS; +} + +void +CSynthesizer::PutPixel ( + COLOR Color +) +/*++ + +Routine Description: + + Place a pixel at the default cursor location. The cursor location + must be set via GetImageLocation(x, y). + +Arguments: + + Color - + The pixel color to render + +Return Value: + + void + +--*/ +{ + PAGED_CODE(); + + PKS_RGBQUAD &pPixel = (PKS_RGBQUAD &) m_Cursor; + + if (Color != TRANSPARENT) + { + *pPixel = CKsRgbQuad( m_Colors[Color][2], m_Colors[Color][1], m_Colors[Color][0] ); + } + + // Next pixel + pPixel++; +} + +// +// PutPixel(): +// +// Place a pixel at the default cursor location. The cursor location +// must be set via GetImageLocation(x, y). +// +void +CSynthesizer::PutPixel ( + UCHAR colorR, + UCHAR colorB, + UCHAR colorG +) +/*++ + +Routine Description: + + Place a pixel at the default cursor location. The cursor location + must be set via GetImageLocation(x, y). + + TODO: Remove. This function appears to be dead code. + +Arguments: + + colorR - + The first color primary of the pixel. + + colorB - + The third color primary of the pixel. + + colorG - + The second color primary of the pixel. + + +Return Value: + + void + +--*/ +{ + PAGED_CODE(); + + PKS_RGBQUAD &pPixel = (PKS_RGBQUAD &) m_Cursor; + + *pPixel++ = CKsRgbQuad( colorR, colorG, colorB ); +} + + +// +// Synthesize +// +// Fill the image buffer with some base image. All h/w simulations will +// call this function to generate a base image - even for jpeg. +// +NTSTATUS +CSynthesizer:: +DoSynthesize() +{ + PAGED_CODE(); + + LARGE_INTEGER StartTime = {0}; + LARGE_INTEGER EndTime = {0}; + + StartTime = KeQueryPerformanceCounter(NULL); + + NTSTATUS Status = Synthesize(); + + m_SynthesisTime += KeQueryPerformanceCounter(NULL).QuadPart - StartTime.QuadPart; + m_SynthesisCount++; + + return Status; +} + +// +// Commit +// +// Copy (and reformat, if necessary) pixels from the internal scratch +// buffer. If the output format decimates chrominance, do it here. +// +_Success_(return > 0) +ULONG +CSynthesizer:: +DoCommit( + _Out_writes_bytes_(Size) + PUCHAR Buffer, + _In_ ULONG Size, + _In_ ULONG Stride +) +{ + PAGED_CODE(); + + LARGE_INTEGER StartTime = KeQueryPerformanceCounter(NULL); + + ULONG n = Commit( Buffer, Size, Stride ); + + m_CommitTime += KeQueryPerformanceCounter(NULL).QuadPart - StartTime.QuadPart; + m_CommitCount++; + + return n; +} + +// +// DoCommit +// +// Note: The stride needs to be a function of the bits per pixel of the +// OUTPUT format - not the format we store it in. The class ctor +// must initialize m_OutputStride to a default stride value. +// +_Success_(return > 0) +ULONG +CSynthesizer:: +DoCommit( + _Out_writes_bytes_(Size) + PUCHAR Buffer, + _In_ ULONG Size +) +{ + PAGED_CODE(); + + LARGE_INTEGER StartTime = KeQueryPerformanceCounter(NULL); + + ULONG n = Commit( Buffer, Size ); + + m_CommitTime += KeQueryPerformanceCounter(NULL).QuadPart - StartTime.QuadPart; + m_CommitCount++; + + return n; +} + +#define CLEAR_HISTOGRAM( H ) \ + if( H ) \ + { \ + RtlZeroMemory( H, 256*sizeof(*H) ); \ + } + +#define BUMP_HISTOGRAM_PRIMARY( H, C ) \ + if( H ) \ + { \ + H[C]++; \ + } + +// +// Histogram +// +// Fill an array with histogram data. +// +void +CSynthesizer:: +Histogram( + _Out_writes_opt_(256) + PULONG HistogramP0, // rgbRed + _Out_writes_opt_(256) + PULONG HistogramP1, // rgbGreen + _Out_writes_opt_(256) + PULONG HistogramP2 // rgbBlue +) +/*++ + +Routine Description: + + Fill array(s) with histogram data. + +Arguments: + + HistogramP0 - + Stats on the first primary. + + HistogramP1 - + Stats on the second primary. + + HistogramP2 - + Stats on the third primary. + +Return Value: + + void + +--*/ +{ + PAGED_CODE(); + + CLEAR_HISTOGRAM( HistogramP0 ); + CLEAR_HISTOGRAM( HistogramP1 ); + CLEAR_HISTOGRAM( HistogramP2 ); + + ULONG row, col; + + for( row=0; rowrgbRed ); + BUMP_HISTOGRAM_PRIMARY( HistogramP1, pPixel->rgbGreen ); + BUMP_HISTOGRAM_PRIMARY( HistogramP2, pPixel->rgbBlue ); + } + } +} diff --git a/avscamera/sys/Synthesizer.h b/avscamera/sys/Synthesizer.h new file mode 100644 index 000000000..b39dfb7e8 --- /dev/null +++ b/avscamera/sys/Synthesizer.h @@ -0,0 +1,507 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + Synthesizer.h + + Abstract: + + This file contains the definition of CSynthesizer. + + The base image synthesis and overlay class. These classes provide + image synthesis (pixel, color-bar, etc...) onto buffers of various + formats. + + Internally, all CSynthesizer objects represent a pixel as a 32 bit + quantity with 8 bits per sample. The base CSynthesizer implements + all rendering functionality with this assumption in mind. It + simplifies rendering substantially and means we do not need to re- + implement rendering functions for each format. + + Most end formats can be synthesized from this uncompressed format + with overhead that is slightly worse than a copy. Color spaces are + handled by using a rendering palette that is unique for each. + + History: + + created 04/14/2014 + +**************************************************************************/ + +#pragma once + +// +// 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; + +#define CHANNEL_YCrCb ( MF_HISTOGRAM_CHANNEL_Y | MF_HISTOGRAM_CHANNEL_Cr | MF_HISTOGRAM_CHANNEL_Cb ) +#define CHANNEL_RGB ( MF_HISTOGRAM_CHANNEL_R | MF_HISTOGRAM_CHANNEL_G | MF_HISTOGRAM_CHANNEL_B ) +#define CHANNEL_NONE ( 0 ) + +// +// 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) + +// +// Initializer class for a KS_RGBQUAD +// +class CKsRgbQuad : public KS_RGBQUAD +{ +public: + // Initializer ctor + CKsRgbQuad( + BYTE r=0, + BYTE g=0, + BYTE b=0 ) + { + rgbBlue = b; + rgbGreen = g; + rgbRed = r; + rgbReserved = 0; + } +}; + +/************************************************* + + CSynthesizer + + 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. + + The base synthesizer is essentially the XRGB Synthesizer. For YUV, we + repurpose the color primaries and provide a different definition for the + color pallette. A call to CSynthesizer::Commit(buffer) will reformat the + internal representation and down-sample the color primaries, as necessary + into the supplied buffer. + +*************************************************/ + +typedef +UCHAR UCHAR4[4]; + +class CSynthesizer : + public CNonCopyable +{ +protected: + + static + const UCHAR m_FontData [256][8]; + static + const COLOR m_ColorBars[8]; + + // These values are used by Synthesize() for display purposes. They are + // static so that setting the values on the preview pin affects all pins. + // + // TODO: Change to references to a shared object. Use of static values + // could cause confusion if two (or more) cameras are in use at + // the same time. + static + LONGLONG m_RelPts; + static + LONGLONG m_QpcTime; + static + ULONG m_Frame; + + // + // The width and height the synthesizer is set to. + // + ULONG m_Width; + ULONG m_Height; + + // + // The synthesis buffer: + // + // This buffer points to an array of KS_RGBQUAD. Internally we synthesize + // images in an uncompressed format, because it is at least as fast to + // composite an image in such a format and convert to the target compressed + // format in a final pass over the image. + // + // An uncompressed format also has less loss of fidelity. For instance, + // NV12 only contains 1 chroma sample per macro-pixel (2x2). Once this + // macro pixel is rendered, compositing additional color information + // into it results in a loss of color precision / fidelity. + // + PUCHAR m_Buffer; // pointer to pixel data + ULONG m_Length; // size of the buffer in bytes + LONG m_SynthesisStride; // size of scan line in bytes + + // + // The assumed stride of our output format. Currently used by YUY2 and + // all image captures, except NV12. This value should be initialized by + // the derived classes. + // + LONG m_OutputStride; + + // Bitmap with a gradient applied for each color in the color pallet. + CKsRgbQuad *m_GradientBmp; + + // + // The default cursor. This is a pointer into the synthesis buffer where + // a non specific PutPixel will be placed. + // + PUCHAR m_Cursor; + + // + // A printable name identifying this format. + // + PCCHAR m_FormatName; + + // + // A color palette for this colorspace. + // + // Specifying a unique color palette is a cheap method for switching + // between RGB and YUV color spaces. A derived class is free to assign + // this to any table it chooses. It can be used to either change the + // colors used for rendering, or it can be used to support a new (or + // slightly different) color space. + // + const UCHAR4 *m_Colors; + + // + // A bitset specifying the color primaries used by this format. + // (Used by Histogram.) + // + ULONG m_ChannelMask; + + // + // Debugging values + // + LARGE_INTEGER m_Frequency; + LONGLONG m_SynthesisTime; + ULONG m_SynthesisCount; + LONGLONG m_CommitTime; + ULONG m_CommitCount; + +public: + + // + // DEFAULT CONSTRUCTOR + // + CSynthesizer( + PCCHAR Name="[Unknown]", + ULONG ChannelMask=0, + ULONG Width=0, + ULONG Height=0 + ) + : m_Width(Width) + , m_Height(Height) + , m_Buffer(nullptr) + , m_Cursor(nullptr) + , m_GradientBmp(nullptr) + , m_SynthesisStride(m_Width * sizeof(KS_RGBQUAD)) + , m_OutputStride(0) + , m_FormatName(Name) + , m_ChannelMask(ChannelMask) + , m_SynthesisCount(0) + , m_SynthesisTime(0) + , m_CommitCount(0) + , m_CommitTime(0) + { + m_Length = Height * m_SynthesisStride; + KeQueryPerformanceCounter(&m_Frequency); + } + + // + // DESTRUCTOR: + // + virtual + ~CSynthesizer() + {} + + // + // PutPixel(): + // + // Place a pixel at the default cursor location. The cursor location + // must be set via GetImageLocation(x, y). + // + virtual + void + PutPixel ( + COLOR Color + ); + + // + // PutPixel(): + // + // Place a pixel at the default cursor location. The cursor location + // must be set via GetImageLocation(x, y). + // + virtual + void + PutPixel ( + UCHAR colorR, + UCHAR colorB, + UCHAR colorG + ); + + // + // Create a horizontal color bar that slowly fades from the specified + // color to black. + // + // Note: To generalize, this function could specify the height of the + // color bar instead of inferring it. + // + void + ApplyGradient( + _In_ ULONG LocY, + _In_ COLOR Gradient + ); + + + // + // SetImageSize(): + // + // Set the image size of the synthesis buffer. + // + void + SetImageSize ( + _In_ ULONG Width, + _In_ ULONG Height + ) + { + m_Width = Width; + m_Height = Height; + } + + // + // Initialize() + // + // Set the buffer the synthesizer generates images to. + // Override to do any additional processing you think is needed. + // + virtual + BOOLEAN + Initialize(); + + // + // Destroy() + // + // Clean up from initialize. + // + virtual + void + Destroy(); + + // + // SynthesizeBars(): + // + // Synthesize EIA-189-A standard color bars. + // + virtual + NTSTATUS + SynthesizeBars(); + + // + // Synthesize + // + // Fill the image buffer with some base image. All h/w simulations will + // call this function to generate a base image + // + virtual + NTSTATUS + Synthesize(); + + // + // Commit + // + // Copy (and reformat, if necessary) pixels from the internal scratch + // buffer. If the output format decimates chrominance, do it here. + // + virtual + _Success_(return > 0) + ULONG + Commit( + _Out_writes_bytes_(Size) + PUCHAR Buffer, + _In_ ULONG Size, + _In_ ULONG Stride + )=0; + + // + // Commit + // + // Note: The stride needs to be a function of the bits per pixel of the + // OUTPUT format - not the format we store it in. The class ctor + // must initialize m_OutputStride to a default stride value. + // + _Success_(return > 0) + ULONG + Commit( + _Out_writes_bytes_(Size) + PUCHAR Buffer, + _In_ ULONG Size + ) + { + return Commit( Buffer, Size, m_OutputStride ); + } + + // + // Histogram + // + // Fill an array with histogram data. Each parameter is optional. + // + void + Histogram( + _Out_writes_opt_(256) + PULONG HistogramP0, // rgbRed + _Out_writes_opt_(256) + PULONG HistogramP1, // rgbGreen + _Out_writes_opt_(256) + PULONG HistogramP2 // rgbBlue + ); + + // + // Synthesize + // + // Fill the image buffer with some base image. All h/w simulations will + // call this function to generate a base image + // + NTSTATUS + DoSynthesize(); + + // + // Commit + // + // Copy (and reformat, if necessary) pixels from the internal scratch + // buffer. If the output format decimates chrominance, do it here. + // + _Success_(return > 0) + ULONG + DoCommit( + _Out_writes_bytes_(Size) + PUCHAR Buffer, + _In_ ULONG Size, + _In_ ULONG Stride + ); + + // + // DoCommit + // + // Note: The stride needs to be a function of the bits per pixel of the + // OUTPUT format - not the format we store it in. The class ctor + // must initialize m_OutputStride to a default stride value. + // + _Success_(return > 0) + ULONG + DoCommit( + _Out_writes_bytes_(Size) + PUCHAR Buffer, + _In_ ULONG Size + ); + + // + // 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 + ); + + virtual + void + EncodeNumber( + _In_ ULONG LocY, + _In_ UINT32 Number, + _In_ COLOR LowColor, + _In_ COLOR HighColor + ); + + void + SetRelativePts( LONGLONG Pts ) + { + m_RelPts = Pts; + } + + void + SetQpcTime( LONGLONG Pts ) + { + m_QpcTime = Pts; + } + + void + SetFrameNumber( ULONG Frame ) + { + m_Frame = Frame; + } + + // + // GetChannelMask + // + // Get the color channel mask associated with this format. + // + ULONG + GetChannelMask() + { + return m_ChannelMask; + } + +protected: + // + // GetPalette + // + // Get a pointer to an array of palette colors. Used mostly to handle + // different color primaries. Location of the primary must agree with + // Commit(). + // + virtual + const UCHAR4 * + GetPalette() + { + return nullptr; + } + + // + // 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. + // + PUCHAR + GetImageLocation ( + _In_ ULONG LocX, + _In_ ULONG LocY + ) + { + return + m_Cursor = + (m_Buffer + (sizeof(CKsRgbQuad) * LocX) + (LocY * m_SynthesisStride)); + } + +}; + diff --git a/avscamera/sys/Timer.cpp b/avscamera/sys/Timer.cpp new file mode 100644 index 000000000..5345a361a --- /dev/null +++ b/avscamera/sys/Timer.cpp @@ -0,0 +1,172 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + timer.cpp + + Abstract: + + Contains the implementation of a KTimer and KPassiveTimer. + + KTimer wraps a kernel timer object. KPassiveTimer uses a passive + callback through a KWorkItem. + + History: + + created 5/30/2014 + +**************************************************************************/ + +#include "Common.h" + +KTimer:: +KTimer( TIMER_TYPE type ) + : m_Period(0) + , m_Context(NULL) + , m_Callback(NULL) +{ + KeInitializeTimerEx( &m_Timer,type ) ; + KeInitializeDpc( &m_Dpc, KTimer::DpcThunk, this ) ; +} + +KTimer:: +~KTimer() +{ + Cancel(); +} + +// +// Cancels the timer and ensures no DPC is executing. +// +BOOLEAN +KTimer:: +Cancel() +{ + // Clear the timer and dequeue the DPC. + if( !Reset() ) + { + // If the item wasn't found, flush all the DPC queues. + KeFlushQueuedDpcs() ; + + // Return false because we lost the race. + return FALSE; + } + return TRUE; +} + +// +// Support for waitable class +// +PVOID +KTimer:: +GetDispatchObject() +{ + return &m_Timer; +} + +// +// Dpc Thunking function for KeSetTimerEx +// +_Function_class_(KDEFERRED_ROUTINE) +VOID +KTimer:: +DpcThunk( + _In_ PKDPC /*Dpc*/, + _Inout_opt_ PVOID DeferredContext, + _Inout_opt_ PVOID /*Arg1*/, + _Inout_opt_ PVOID /*Arg2*/ +) +{ + KTimer *pKTimer = (KTimer *) DeferredContext ; + pKTimer->Func() ; +} + +// +// Function invoked when a timer expires +// +// IRQL <= DISPATCH_LEVEL +// +void +KTimer:: +Func() +{ + if( m_Callback ) + { + m_Callback( m_Context ) ; + } +} + +// +// Dtor +// +// Calls Cancel() here to clean up. +// +// Note: KTimer will also call Cancel(), but it will invoke KTimer::Cancel(). +// Since KPassiveTimer::Cancel() first calls KTimer::Cancel() this second +// call is redudant; but preventing it would probably require an explicit +// call to Cancel() before destruction. This way you can just delete +// timers when they are no longer needed, but it make take a bit longer. +// +KPassiveTimer:: +~KPassiveTimer() +{ + Cancel(); +} + +// +// Function invoked when a timer expires +// +// IRQL == DISPATCH_LEVEL +// +void +KPassiveTimer:: +Func() +{ + if( m_Callback ) + { + // Start the work item. If it fails, there's not much we can do since + // it can only fail if the owner of this timer is trying to cancel it. + m_WorkItem.Start(); + } +} + +VOID +KPassiveTimer:: +WorkThunk( + _In_ PDEVICE_OBJECT DeviceObject, + _In_opt_ PVOID Context +) +{ + UNREFERENCED_PARAMETER(DeviceObject); + + if( Context ) + { + ((KPassiveTimer *) Context)->KTimer::Func(); + } +} + +// +// Cancels the timer +// +BOOLEAN +KPassiveTimer:: +Cancel() +{ + // Kill the timer. + if( !KTimer::Cancel() ) + { + // Wait until the work item is idle. + m_WorkItem.Wait(); + + // Reset the WorkItem so we can use it again. + m_WorkItem.Reset(); + + return FALSE; + } + return TRUE; +} + diff --git a/avscamera/sys/Timer.h b/avscamera/sys/Timer.h new file mode 100644 index 000000000..d4e6322e2 --- /dev/null +++ b/avscamera/sys/Timer.h @@ -0,0 +1,199 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + timer.h + + Abstract: + + Contains the definition of a KTimer and KPassiveTimer. + + KTimer wraps a kernel timer object. KPassiveTimer uses a passive + callback through a KWorkItem. + + History: + + created 5/30/2014 + +**************************************************************************/ + +#pragma once + +typedef void (*PKTimerCallback)( PVOID Context ) ; + +class KTimer : public KWaitable, public Cancelable +{ +public: + KTimer( TIMER_TYPE type=NotificationTimer ); + + virtual + ~KTimer(); + +public: + // + // Override function that is invoked when a timer expires + // + virtual + void + Func() ; + + // + // Cancels the timer + // + virtual + BOOLEAN + Cancel(); + + // + // Returns TRUE is if the timer is signalled + // + BOOLEAN + State() + { + return KeReadStateTimer( &m_Timer ) ; + } + + // + // Resets (Cancels) the timer and DPC. + // + // Reset() returns TRUE if the KTIMER or the KDPC are dequeued. + // If Reset() returns FALSE the DPC may still be executing on another + // processor. + // + BOOLEAN + Reset() + { + // Try cancelling the timer. + if( !KeCancelTimer( &m_Timer ) ) + { + // If the timer couldn't be cancelled, try removing the DPC. + return KeRemoveQueueDpc( &m_Dpc ) ; + } + return TRUE; + } + + // + // Sets the timer + // + BOOLEAN + Set( + _In_ LARGE_INTEGER DueTime, + _In_opt_ PKTimerCallback Callback=NULL, + _In_opt_ PVOID Context=NULL, + _In_ LONG Period=0 + ) + { + m_Callback = Callback ; + m_Context = Context ; + m_Period = Period ; + return KeSetTimerEx( &m_Timer, DueTime, Period, &m_Dpc ) ; + } + + // + // Sets the timer + // + BOOLEAN + Set( + _In_ LONGLONG DueTime, + _In_opt_ PKTimerCallback Callback=NULL, + _In_opt_ PVOID Context=NULL, + _In_ LONG Period=0 + ) + { + m_Callback = Callback ; + m_Context = Context ; + m_Period = Period ; + LARGE_INTEGER Due; + Due.QuadPart = DueTime; + return KeSetTimerEx( &m_Timer, Due, Period, &m_Dpc ) ; + } + + // + // Support for waitable class + // + virtual + PVOID + GetDispatchObject(); + +private: + // + // Dpc Thunking function for KeSetTimerEx + // + _Function_class_(KDEFERRED_ROUTINE) + static + VOID + DpcThunk( + _In_ PKDPC Dpc, + _Inout_opt_ PVOID DeferredContext, + _Inout_opt_ PVOID Arg1, + _Inout_opt_ PVOID Arg2 + ) ; + + TIMER_TYPE GetType() + { + return m_type; + } + +private: + KTIMER m_Timer ; + TIMER_TYPE m_type; + + KDPC m_Dpc ; + LONG m_Period ; + +protected: + PKTimerCallback m_Callback ; + PVOID m_Context ; +}; + +class KPassiveTimer : public KTimer +{ +public: + KPassiveTimer( + _In_ PDEVICE_OBJECT DeviceObj, + _In_ TIMER_TYPE type=NotificationTimer + ) + : KTimer( type ) + , m_WorkItem( DeviceObj, WorkThunk, this ) + {} + + virtual + ~KPassiveTimer(); + + // + // Override function that is invoked when a timer expires + // + virtual + void + Func() ; + + // + // Cancels the timer + // + virtual + BOOLEAN + Cancel(); + + // + // Make sure it's in a valid state. + // + virtual + BOOLEAN + IsValid() + { + return m_WorkItem.IsValid(); + } +private: + KWorkItem m_WorkItem; + + // + // Dpc Thunking function for KeSetTimerEx + // + static + IO_WORKITEM_ROUTINE + WorkThunk; +}; \ No newline at end of file diff --git a/avscamera/sys/Util.cpp b/avscamera/sys/Util.cpp new file mode 100644 index 000000000..76a0d902c --- /dev/null +++ b/avscamera/sys/Util.cpp @@ -0,0 +1,476 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2013, Microsoft Corporation. + + File: + + util.cpp + + Abstract: + + Contains miscellaneous helper functions such as random number + generation and bounds checking functions. + + History: + + created 7/12/2013 + +**************************************************************************/ + +#include "Common.h" +#include + +#ifdef ALLOC_PRAGMA +#pragma code_seg() +#endif // ALLOC_PRAGMA + +// +// Generate a random number using the standard Crypto library. +// Fall back to the QPC, if crypto library reports an error. +// +ULONG +GetRandom( + _In_ ULONG Minimum, + _In_ ULONG Maximum +) +{ + ULONG Value=0; + NTSTATUS Status = STATUS_UNSUCCESSFUL; + + if( KeGetCurrentIrql()==PASSIVE_LEVEL ) + { + Status= + BCryptGenRandom( NULL, (PUCHAR) &Value, sizeof(Value), BCRYPT_USE_SYSTEM_PREFERRED_RNG ); + } + + // Fallback to QPC if BCRYPT isn't available. + if( !NT_SUCCESS(Status) ) + { + LARGE_INTEGER QPC = KeQueryPerformanceCounter(NULL); + Value = QPC.u.LowPart; + } + + // Make sure we don't divide by 0. + if( Minimum < Maximum ) + { + Value = ((Value - Minimum) % (Maximum - Minimum +1)) + Minimum; + } + else + { + Value = Minimum ; + } + + return Value; +} + +// +// Generate a random number using the standard Crypto library. +// Fall back to the QPC, if crypto library reports an error. +// +LONG +GetRandom( + _In_ LONG Minimum, + _In_ LONG Maximum +) +{ + ULONG Value=0; + NTSTATUS Status = STATUS_UNSUCCESSFUL; + + if( KeGetCurrentIrql()==PASSIVE_LEVEL ) + { + Status= + BCryptGenRandom( NULL, (PUCHAR) &Value, sizeof(Value), BCRYPT_USE_SYSTEM_PREFERRED_RNG ); + } + + // Fallback to QPC if BCRYPT isn't available. + if( !NT_SUCCESS(Status) ) + { + LARGE_INTEGER QPC = KeQueryPerformanceCounter(NULL); + Value = QPC.u.LowPart; + } + + // Make sure we don't divide by 0. + if( Minimum < Maximum ) + { + Value = ((Value - Minimum) % (Maximum - Minimum +1)) + Minimum; + } + else + { + Value = Minimum ; + } + + return Value; +} + +#ifdef ALLOC_PRAGMA +#pragma code_seg("PAGE") +#endif // ALLOC_PRAGMA + +// +// Convert an EV Compensation flag into the denominator of a fraction. +// +LONG +EVFlags2Denominator( + _In_ ULONGLONG Flags +) +{ + PAGED_CODE( ); + + static + LONG val[] = {1,2,3,4,6}; + + switch( Flags ) + { + case KSCAMERA_EXTENDEDPROP_EVCOMP_SIXTHSTEP: + return 6; + case KSCAMERA_EXTENDEDPROP_EVCOMP_QUARTERSTEP: + return 4; + case KSCAMERA_EXTENDEDPROP_EVCOMP_THIRDSTEP: + return 3; + case KSCAMERA_EXTENDEDPROP_EVCOMP_HALFSTEP: + return 2; + case KSCAMERA_EXTENDEDPROP_EVCOMP_FULLSTEP: + return 1; + case KSCAMERA_PERFRAMESETTING_AUTO: + return val[GetRandom( (ULONG) 0, (ULONG) SIZEOF_ARRAY(val)-1)]; + } + + return 0; +} + +// Convert white balance presets to a temperature. +ULONG +WhiteBalancePreset2Temperature( + _In_ ULONG WBPreset +) +{ + PAGED_CODE(); + + ULONG temp = 0; + + switch (WBPreset) + { + case KSCAMERA_EXTENDEDPROP_WBPRESET_CLOUDY: + temp = 6000; + break; + case KSCAMERA_EXTENDEDPROP_WBPRESET_DAYLIGHT: + temp = 5200; + break; + case KSCAMERA_EXTENDEDPROP_WBPRESET_FLASH: + temp = 6000; + break; + case KSCAMERA_EXTENDEDPROP_WBPRESET_FLUORESCENT: + temp = 4000; + break; + case KSCAMERA_EXTENDEDPROP_WBPRESET_TUNGSTEN: + temp = 3200; + break; + case KSCAMERA_EXTENDEDPROP_WBPRESET_CANDLELIGHT: + temp = 1000; + break; + default: + temp = 5000; + break; + } + + return temp; +} + +typedef struct +{ + LONG exposureBinaryLog; + LONGLONG exposure100ns; +} ExposurePresetStruct; + +const ExposurePresetStruct ExposurePresets[] = +{ + { -10, 10000 }, // 1/1024s -> 9765.625 (100 ns) (Min is 1 ms) + { -9, 19531 }, // 1/512s -> 19531.25 (100 ns) + { -8, 39063 }, // 1/256s -> 39062.5 (100 ns) + { -7, 78125 }, // 1/128s -> 78125 (100 ns) + { -6, 196250 }, // 1/64s -> 196250 (100 ns) + { -5, 312500 }, // 1/32s -> 312500 (100 ns) + { -4, 625000 }, // 1/16s -> 625000 (100 ns) + { -3, 1250000 }, // 1/8s -> 1250000 (100 ns) + { -2, 2500000 }, // 1/4s -> 2500000 (100 ns) + { -1, 5000000 }, // 1/2s -> 5000000 (100 ns) + { 0, 10000000 },// 1s -> 1 * 10000000 (100 ns) + { 1, 20000000 },// 2s -> 2 * 10000000 (100 ns) + { 2, 40000000 },// 4s -> 4 * 10000000 (100 ns) + { 3, 80000000 },// 8s -> 8 * 10000000 (100 ns) + { 4, 160000000 },// 16s -> 16 * 10000000 (100 ns) + { 5, 320000000 },// 32s -> 32 * 10000000 (100 ns) + { 6, 640000000 },// 64s -> 64 * 10000000 (100 ns) + { 7, 1280000000 },// 128s -> 128 * 10000000 (100 ns) + { 8, 2560000000 },// 256s -> 256 * 10000000 (100 ns) + { 9, 3600000000 },// 512s -> 512 * 10000000 (100 ns) (Max is 360 s) +}; + +const ExposurePresetStruct ExposureMidpoints[] = +{ + { -10, 13811 }, // 2^(-9.5) -> .001381067 + { -9, 27621 }, // 2^(-8.5) -> .002762136 + { -8, 55243 }, // 2^(-7.5) -> .005524271 + { -7, 110485 }, // 2^(-6.5) -> .011048543 + { -6, 220970 }, // 2^(-5.5) -> .022097086 + { -5, 441941 }, // 2^(-4.5) -> .044194174 + { -4, 883883 }, // 2^(-3.5) -> .0883883476 + { -3, 1767767 }, // 2^(-2.5) -> .1767766953 + { -2, 3535534 }, // 2^(-1.5) -> .3535533905 + { -1, 7071067 }, // 2^(-0.5) -> .7071067811 + { 0, 14142136 },// 2^(0.5) -> 1.414213562 + { 1, 28284271 },// 2^(1.5) -> 2.828427125 + { 2, 56568542 },// 2^(2.5) -> 5.656854250 + { 3, 113137085 },// 2^(3.5) -> 11.31370850 + { 4, 226274170 },// 2^(4.5) -> 22.62741700 + { 5, 452548340 },// 2^(5.5) -> 45.25483400 + { 6, 905096680 },// 2^(6.5) -> 90.50966799 + { 7, 1810193360 },// 2^(7.5) -> 181.0193360 + { 8, 3080000000 },// Halfway between 8 and Max: 30800 + { 9, 3600000000 },// Above +}; + +BOOL +ExposureBinaryLogIsValid( + _In_ LONG ExposureBL +) +{ + PAGED_CODE(); + + if( ExposurePresets[0].exposureBinaryLog > ExposureBL || + ExposurePresets[_countof(ExposurePresets) - 1].exposureBinaryLog < ExposureBL ) + { + return FALSE; + } + + return TRUE; +} + +// Changes a Legacy Preset value into 100 ns units for Exposure. If the +// preset is out of range of what we support, return 0 to indicate failure. +LONGLONG +ExposureBinaryLogTo100ns( + _In_ LONG ExposureBL +) +{ + PAGED_CODE( ); + + for (LONG i = 0; i < _countof(ExposurePresets) - 1; i++) + { + if (ExposureBL == ExposurePresets[i].exposureBinaryLog) + { + return ExposurePresets[i].exposure100ns; + } + } + + return 0; +} + +LONG +Exposure100nsToBinaryLog( + _In_ LONGLONG Exposure100ns +) +{ + PAGED_CODE( ); + + for(LONG i = 0; i < _countof(ExposureMidpoints) - 1; i++) + { + if(Exposure100ns <= ExposureMidpoints[i].exposure100ns) + { + return ExposureMidpoints[i].exposureBinaryLog; + } + } + + return ExposureMidpoints[_countof(ExposureMidpoints) - 1].exposureBinaryLog; +} + + +static +ULONGLONG PotentialIsoSetting[] = +{ + KSCAMERA_EXTENDEDPROP_ISO_50 + , KSCAMERA_EXTENDEDPROP_ISO_80 + , KSCAMERA_EXTENDEDPROP_ISO_100 + , KSCAMERA_EXTENDEDPROP_ISO_200 + , KSCAMERA_EXTENDEDPROP_ISO_400 + , KSCAMERA_EXTENDEDPROP_ISO_800 + , KSCAMERA_EXTENDEDPROP_ISO_1600 + , KSCAMERA_EXTENDEDPROP_ISO_3200 + , KSCAMERA_EXTENDEDPROP_ISO_6400 + , KSCAMERA_EXTENDEDPROP_ISO_12800 + , KSCAMERA_EXTENDEDPROP_ISO_25600 +}; + +// +// Convert ISO preset flags to a preset number +// +ULONG +IsoPreset2Value( + _In_ ULONGLONG Flags +) +{ + PAGED_CODE( ); + + static + ULONG IsoValue[] = + { + 50 + , 80 + , 100 + , 200 + , 400 + , 800 + , 1600 + , 3200 + , 6400 + , 12800 + , 25600 + }; + + // Simple scan for min + for( ULONG i=0; i<_countof(PotentialIsoSetting); i++ ) + { + if( PotentialIsoSetting[i] & Flags ) + { + return IsoValue[i]; + } + } + return MAXULONG; +} + +// +// Convert ISO Mode & Value to legacy presets. +// +// Note: This function will simply pass legacy presets through. +// +ULONGLONG +IsoModeValue2Preset( + _In_ ULONGLONG Mode, + _In_ ULONG Value +) +{ + PAGED_CODE( ); + + // These values are staggerred midpoints along a logrithmic scale. + // Using these pre-calculated values simplifies our search to a + // simple set of comparisons. + static + ULONG IsoValue[] = + { + 64 //50 //KSCAMERA_EXTENDEDPROP_ISO_50 + ,90 //, 80 //KSCAMERA_EXTENDEDPROP_ISO_80 + , 142 //, 100 //KSCAMERA_EXTENDEDPROP_ISO_100 + , 283 //, 200 //KSCAMERA_EXTENDEDPROP_ISO_200 + , 566 //, 400 //KSCAMERA_EXTENDEDPROP_ISO_400 + , 1132 //, 800 //KSCAMERA_EXTENDEDPROP_ISO_800 + , 2263 //, 1600 //KSCAMERA_EXTENDEDPROP_ISO_1600 + , 4526 //, 3200 //KSCAMERA_EXTENDEDPROP_ISO_3200 + , 9051 //, 6400 //KSCAMERA_EXTENDEDPROP_ISO_6400 + , 18102 //, 12800 //KSCAMERA_EXTENDEDPROP_ISO_12800 + }; + + if( Mode & KSCAMERA_EXTENDEDPROP_ISO_MANUAL ) + { + // Simple scan for min + for( ULONG i=0; i<_countof(PotentialIsoSetting)-1; i++ ) + { + if( Value < IsoValue[i] ) + { + return PotentialIsoSetting[i]; + } + } + return KSCAMERA_EXTENDEDPROP_ISO_25600; + } + return Mode; +} + +LPCSTR +KSStateToStateName( + _In_ KSSTATE state +) +{ + PAGED_CODE( ); + + static const CHAR *txt[] = + { + "STOP", + "ACQUIRE", + "PAUSE", + "RUN" + }; + + return ((KSSTATE_STOP <= state && KSSTATE_RUN >= state) ? txt[state] : "UNK"); +} + +// Debugging helper. +PCSTR +FocusStateDbgTxt( + _In_ KSCAMERA_EXTENDEDPROP_FOCUSSTATE State +) +{ + PAGED_CODE(); + + switch( State ) + { + case KSCAMERA_EXTENDEDPROP_FOCUSSTATE_UNINITIALIZED: + return "FOCUSSTATE_UNINITIALIZED"; + case KSCAMERA_EXTENDEDPROP_FOCUSSTATE_LOST: + return "FOCUSSTATE_LOST"; + case KSCAMERA_EXTENDEDPROP_FOCUSSTATE_SEARCHING: + return "FOCUSSTATE_SEARCHING"; + case KSCAMERA_EXTENDEDPROP_FOCUSSTATE_FOCUSED: + return "FOCUSSTATE_FOCUSED"; + case KSCAMERA_EXTENDEDPROP_FOCUSSTATE_FAILED: + return "FOCUSSTATE_FAILED"; + default: + return "FOCUSSTATE_unknown"; + } +} + +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; +} + diff --git a/avscamera/sys/Util.h b/avscamera/sys/Util.h new file mode 100644 index 000000000..34a3a5683 --- /dev/null +++ b/avscamera/sys/Util.h @@ -0,0 +1,289 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2013, Microsoft Corporation. + + File: + + util.h + + Abstract: + + Contains miscellaneous helper functions such as random number + generation and bounds checking functions. + + History: + + created 7/12/2013 + +**************************************************************************/ + +// +// Generate a random number using the standard Crypto library. +// Fall back to the QPC, if crypto library reports an error. +// +ULONG +GetRandom( + _In_ ULONG Minimum, + _In_ ULONG Maximum +); + +// +// Generate a random number using the standard Crypto library. +// Fall back to the QPC, if crypto library reports an error. +// +LONG +GetRandom( + _In_ LONG Minimum, + _In_ LONG Maximum +); + +// +// Convert an EV Compensation flag into the denominator of a fraction. +// +LONG +EVFlags2Denominator( + _In_ ULONGLONG Flags +); + +// +// Convert a WhiteBalance Extended Property Enum to temperature +// +ULONG +WhiteBalancePreset2Temperature( + _In_ ULONG WBPreset +); + +// +// Checks if a legacy Exposure setting is in range +// +BOOL +ExposureBinaryLogIsValid( + _In_ LONG ExposureBL +); + +// +// Converts a legacy Exposure setting into 100 ns +// +LONGLONG +ExposureBinaryLogTo100ns( + _In_ LONG ExposureBL +); + +// +// Converts a Exposure setting into a Binary log preset +// +LONG +Exposure100nsToBinaryLog( + _In_ LONGLONG Exposure100ns +); + +// +// Convert ISO preset flags to a preset number +// +ULONG +IsoPreset2Value( + _In_ ULONGLONG Flags +); + +// +// Convert ISO Mode & Value to legacy presets. +// +// Note: This function will simply pass legacy presets through. +// +ULONGLONG +IsoModeValue2Preset( + _In_ ULONGLONG Mode, + _In_ ULONG Value +); + +LPCSTR +KSStateToStateName( + _In_ KSSTATE state +); + +BOOL +MultiplyCheckOverflow ( + ULONG a, + ULONG b, + ULONG *pab +); + +// Note: I overload this function intentionally. Just use +// the correct type as the first parameter and the +// compiler figures out the function to use. +template +inline +NTSTATUS +BoundsCheck( + _In_ T Value, + _In_ const KSCAMERA_EXTENDEDPROP_VIDEOPROCSETTING &Bounds +) +{ + NTSTATUS ntStatus = STATUS_SUCCESS; + + if( ((Value - T(Bounds.Min) ) % T(Bounds.Step)) != 0 || + Value > T(Bounds.Max) || + Value < T(Bounds.Min) ) + { + ntStatus = STATUS_INVALID_PARAMETER; + } + + return ntStatus; +} + +inline +NTSTATUS +BoundsCheckSigned( + _In_ LONGLONG Value, + _In_ const KSCAMERA_EXTENDEDPROP_VIDEOPROCSETTING &Bounds +) +{ + return BoundsCheck( Value, Bounds ); +} + +inline +NTSTATUS +BoundsCheckSigned( + _In_ LONG Value, + _In_ const KSPROPERTY_STEPPING_LONG &Bounds +) +{ + NTSTATUS ntStatus = STATUS_SUCCESS; + + if( ((Value - Bounds.Bounds.SignedMinimum) % Bounds.SteppingDelta) != 0 || + Value > Bounds.Bounds.SignedMaximum || + Value < Bounds.Bounds.SignedMinimum ) + { + ntStatus = STATUS_INVALID_PARAMETER; + } + + return ntStatus; +} + +inline +NTSTATUS +BoundsCheckSigned( + _In_ LONGLONG Value, + _In_ const KSPROPERTY_STEPPING_LONGLONG &Bounds +) +{ + NTSTATUS ntStatus = STATUS_SUCCESS; + + if( ((Value - Bounds.Bounds.SignedMinimum) % Bounds.SteppingDelta) != 0 || + Value > Bounds.Bounds.SignedMaximum || + Value < Bounds.Bounds.SignedMinimum ) + { + ntStatus = STATUS_INVALID_PARAMETER; + } + + return ntStatus; +} + +// Note: I overload this function intentionally. Just use +// the correct type as the first parameter and the +// compiler figures out the function to use. +inline +NTSTATUS +BoundsCheckUnsigned( + _In_ ULONG Value, + _In_ const KSPROPERTY_STEPPING_LONG &Bounds +) +{ + NTSTATUS ntStatus = STATUS_SUCCESS; + + if( ((Value - Bounds.Bounds.UnsignedMinimum) % Bounds.SteppingDelta) != 0 || + Value > Bounds.Bounds.UnsignedMaximum || + Value < Bounds.Bounds.UnsignedMinimum ) + { + ntStatus = STATUS_INVALID_PARAMETER; + } + + return ntStatus; +} + +inline +NTSTATUS +BoundsCheckUnsigned( + _In_ ULONGLONG Value, + _In_ const KSPROPERTY_STEPPING_LONGLONG &Bounds +) +{ + NTSTATUS ntStatus = STATUS_SUCCESS; + + if( ((Value - Bounds.Bounds.UnsignedMinimum) % Bounds.SteppingDelta) != 0 || + Value > Bounds.Bounds.UnsignedMaximum || + Value < Bounds.Bounds.UnsignedMinimum ) + { + ntStatus = STATUS_INVALID_PARAMETER; + } + + return ntStatus; +} + +// +// This function is used internally to translate KSCAMERA_PERFRAMESETTING +// AUTO & MANUAL flags to KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG AUTO & MANUAL +// flags. There is no need to translate them back. +// +inline +ULONGLONG +TranslatePFS2VideoProcFlags( + ULONGLONG FromMode +) +{ + // The PFS settings and VideoProc flags are mixed. + // To handle that, we'll first remove any PFS flags + // and hang onto the rest. + ULONGLONG ToMode= + FromMode & ~(KSCAMERA_PERFRAMESETTING_AUTO|KSCAMERA_PERFRAMESETTING_MANUAL); + + if( FromMode & KSCAMERA_PERFRAMESETTING_AUTO ) + { + ToMode |= KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO; + } + if( FromMode & KSCAMERA_PERFRAMESETTING_MANUAL ) + { + ToMode |= KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_MANUAL; + } + + return ToMode; +} + +// +// Debugging Helper +// +PCSTR +FocusStateDbgTxt( + _In_ KSCAMERA_EXTENDEDPROP_FOCUSSTATE State +); + +#define SAFE_FREE(_x_) \ + if( _x_ ) \ + { \ + ExFreePool( _x_ ); \ + _x_ = nullptr; \ + } + +#define SAFE_DELETE(_x_) \ + if( _x_ ) \ + { \ + delete _x_; \ + _x_ = nullptr; \ + } + +#define SAFE_DELETE_ARRAY(_x_) \ + if( _x_ ) \ + { \ + delete [] _x_; \ + _x_ = nullptr; \ + } + +#define SAFE_CLOSE_HANDLE(_x_) \ + if( (_x_ != NULL) && (_x_ != INVALID_HANDLE_VALUE) ) \ + { \ + ZwClose( _x_ ); \ + _x_ = INVALID_HANDLE_VALUE; \ + } + diff --git a/avscamera/sys/VideoCapture.cpp b/avscamera/sys/VideoCapture.cpp new file mode 100644 index 000000000..63d5c7de5 --- /dev/null +++ b/avscamera/sys/VideoCapture.cpp @@ -0,0 +1,2559 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2001, Microsoft Corporation. + + File: + + videocapture.cpp + + Abstract: + + Video Capture Pin implementation. + + Handles construction and defines pin data ranges. + + History: + + created 3/8/2001 + +**************************************************************************/ + +#include "Common.h" +#include "ntintsafe.h" + +#ifdef ALLOC_PRAGMA +#pragma code_seg("PAGE") +#endif // ALLOC_PRAGMA + + +CVideoCapturePin::CVideoCapturePin(IN PKSPIN Pin) : + CCapturePin (Pin) +{ + PAGED_CODE(); + +} + + +/************************************************* + +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 + +**************************************************/ + +NTSTATUS +CVideoCapturePin:: +DispatchCreate( + IN PKSPIN Pin, + IN PIRP Irp +) +{ + PAGED_CODE(); + + DBG_ENTER("(Pin=%d)", Pin->Id); + + NTSTATUS Status = STATUS_SUCCESS; + + CCaptureFilter* pFilter = reinterpret_cast (KsPinGetParentFilter(Pin)->Context); + CVideoCapturePin *CapPin = new (NonPagedPoolNx) CVideoCapturePin (Pin); + + if( !CapPin ) + { + // Fail if we couldn't create the pin. + Status = STATUS_INSUFFICIENT_RESOURCES; + } + else + { + Status = + CapPin->Initialize(); + } + + if( NT_SUCCESS (Status) ) + { + // + // Adjust the stream header size. The video packets have extended + // header info (KS_FRAME_INFO). + // + pFilter->setPin(CapPin, Pin->Id); + } + else + { + // Clean up. + delete CapPin; + } + + DBG_LEAVE("(Pin=%d)=0x%08X", Pin->Id, Status); + return Status; +} + +/************************************************************************** + + DISPATCH AND DESCRIPTOR LAYOUT + +**************************************************************************/ + +// +// FormatRGB24Bpp_Capture: +// +// This is the data range description of the RGB24 capture format we support. +// +const +KS_DATARANGE_VIDEO +FormatRGB24Bpp_CaptureQVGA = +{ + + // + // 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; + } +}; + +const +KS_DATARANGE_VIDEO +FormatRGB24Bpp_CaptureVGA = +{ + + // + // KSDATARANGE + // + { + sizeof (KS_DATARANGE_VIDEO), // FormatSize + 0, // Flags + DMAX_X *DMAX_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 + DMAX_X,DMAX_Y, // InputSize, (the inherent size of the incoming signal + // with every digitized pixel unique) + DMAX_X,DMAX_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; + DMAX_X, DMAX_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 * 3 * 30 * DMAX_X * DMAX_Y, // MinBitsPerSecond; + 8 * 3 * 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 * 3 * 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; + 24, // WORD biBitCount; + KS_BI_RGB, // DWORD biCompression; + DMAX_X *DMAX_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_CaptureQVGA = +{ + + // + // KSDATARANGE + // + { + sizeof (KS_DATARANGE_VIDEO), // FormatSize + 0, // Flags + D_X *D_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 + 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 * 2 * 30 * D_X * D_Y, // MinBitsPerSecond; + 8 * 2 * 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 * 2 * 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; + 16, // WORD biBitCount; + FOURCC_YUY2, // DWORD biCompression; + D_X *D_Y * 2, // DWORD biSizeImage; + 0, // LONG biXPelsPerMeter; + 0, // LONG biYPelsPerMeter; + 0, // DWORD biClrUsed; + 0 // DWORD biClrImportant; + } +}; + + + +const +KS_DATARANGE_VIDEO +FormatYUY2_CaptureVGA = +{ + + // + // 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; + } +}; + + + +// +//60 fps capture +// +const +KS_DATARANGE_VIDEO +FormatRGB24Bpp_CaptureVGA_60fps = +{ + + // + // KSDATARANGE + // + { + sizeof (KS_DATARANGE_VIDEO), // FormatSize + 0, // Flags + DMAX_X *DMAX_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 + DMAX_X,DMAX_Y, // InputSize, (the inherent size of the incoming signal + // with every digitized pixel unique) + DMAX_X,DMAX_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; + DMAX_X, DMAX_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 + 166833, // MinFrameInterval, 100 nS units + 640000000, // MaxFrameInterval, 100 nS units + 8 * 3 * 60 * DMAX_X * DMAX_Y, // MinBitsPerSecond; + 8 * 3 * 60 * 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 * 3 * 8 * 60, // DWORD dwBitRate; + 0L, // DWORD dwBitErrorRate; + 166833, // REFERENCE_TIME AvgTimePerFrame; + sizeof (KS_BITMAPINFOHEADER), // DWORD biSize; + DMAX_X, // LONG biWidth; + DMAX_Y, // LONG biHeight; + 1, // WORD biPlanes; + 24, // WORD biBitCount; + KS_BI_RGB, // DWORD biCompression; + DMAX_X *DMAX_Y * 3, // DWORD biSizeImage; + 0, // LONG biXPelsPerMeter; + 0, // LONG biYPelsPerMeter; + 0, // DWORD biClrUsed; + 0 // DWORD biClrImportant; + } +}; + +const +KS_DATARANGE_VIDEO +FormatYUY2_CaptureVGA_60fps = +{ + + // + // 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 + 166833, // MinFrameInterval, 100 nS units + 640000000, // MaxFrameInterval, 100 nS units + 8 * 2 * 60 * D_X * D_Y, // MinBitsPerSecond; + 8 * 2 * 60 * 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 * 60, // DWORD dwBitRate; + 0L, // DWORD dwBitErrorRate; + 166833, // 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; + } +}; + + +// +//90 fps capture +// +const +KS_DATARANGE_VIDEO +FormatRGB24Bpp_CaptureVGA_90fps = +{ + + // + // KSDATARANGE + // + { + sizeof (KS_DATARANGE_VIDEO), // FormatSize + 0, // Flags + DMAX_X *DMAX_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 + DMAX_X,DMAX_Y, // InputSize, (the inherent size of the incoming signal + // with every digitized pixel unique) + DMAX_X,DMAX_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; + DMAX_X, DMAX_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 + 111111, // MinFrameInterval, 100 nS units + 640000000, // MaxFrameInterval, 100 nS units + 8 * 3 * 90 * DMAX_X * DMAX_Y, // MinBitsPerSecond; + 8 * 3 * 90 * 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 * 3 * 8 * 90, // DWORD dwBitRate; + 0L, // DWORD dwBitErrorRate; + 111111, // REFERENCE_TIME AvgTimePerFrame; + sizeof (KS_BITMAPINFOHEADER), // DWORD biSize; + DMAX_X, // LONG biWidth; + DMAX_Y, // LONG biHeight; + 1, // WORD biPlanes; + 24, // WORD biBitCount; + KS_BI_RGB, // DWORD biCompression; + DMAX_X *DMAX_Y * 3, // DWORD biSizeImage; + 0, // LONG biXPelsPerMeter; + 0, // LONG biYPelsPerMeter; + 0, // DWORD biClrUsed; + 0 // DWORD biClrImportant; + } +}; + +const +KS_DATARANGE_VIDEO +FormatYUY2_CaptureVGA_90fps = +{ + + // + // 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 + 111111, // MinFrameInterval, 100 nS units + 640000000, // MaxFrameInterval, 100 nS units + 8 * 2 * 90 * D_X * D_Y, // MinBitsPerSecond; + 8 * 2 * 90 * 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 * 90, // DWORD dwBitRate; + 0L, // DWORD dwBitErrorRate; + 111111, // 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; + } +}; + + +// +//120 fps capture +// +const +KS_DATARANGE_VIDEO +FormatRGB24Bpp_CaptureVGA_120fps = +{ + + // + // KSDATARANGE + // + { + sizeof (KS_DATARANGE_VIDEO), // FormatSize + 0, // Flags + DMAX_X *DMAX_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 + DMAX_X,DMAX_Y, // InputSize, (the inherent size of the incoming signal + // with every digitized pixel unique) + DMAX_X,DMAX_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; + DMAX_X, DMAX_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 + 83333, // MinFrameInterval, 100 nS units + 640000000, // MaxFrameInterval, 100 nS units + 8 * 3 * 120 * DMAX_X * DMAX_Y, // MinBitsPerSecond; + 8 * 3 * 120 * 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 * 3 * 8 * 120, // DWORD dwBitRate; + 0L, // DWORD dwBitErrorRate; + 83333, // REFERENCE_TIME AvgTimePerFrame; + sizeof (KS_BITMAPINFOHEADER), // DWORD biSize; + DMAX_X, // LONG biWidth; + DMAX_Y, // LONG biHeight; + 1, // WORD biPlanes; + 24, // WORD biBitCount; + KS_BI_RGB, // DWORD biCompression; + DMAX_X *DMAX_Y * 3, // DWORD biSizeImage; + 0, // LONG biXPelsPerMeter; + 0, // LONG biYPelsPerMeter; + 0, // DWORD biClrUsed; + 0 // DWORD biClrImportant; + } +}; + +const +KS_DATARANGE_VIDEO +FormatYUY2_CaptureVGA_120fps = +{ + + // + // 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 + 83333, // MinFrameInterval, 100 nS units + 640000000, // MaxFrameInterval, 100 nS units + 8 * 2 * 120 * D_X * D_Y, // MinBitsPerSecond; + 8 * 2 * 120 * 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 * 120, // DWORD dwBitRate; + 0L, // DWORD dwBitErrorRate; + 83333, // 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; + } +}; + +const +KS_DATARANGE_VIDEO +FormatRGB32Bpp_CaptureQVGA = +{ + + // + // KSDATARANGE + // + { + sizeof (KS_DATARANGE_VIDEO), // FormatSize + 0, // Flags + D_X *D_Y * 4, // SampleSize + 0, // Reserved + + STATICGUIDOF (KSDATAFORMAT_TYPE_VIDEO), // aka. MEDIATYPE_Video + 0xe436eb7e, 0x524f, 0x11ce, 0x9f, 0x53, 0x00, 0x20, + 0xaf, 0x0b, 0xa7, 0x70, // aka. MEDIASUBTYPE_RGB32 + 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 + ONESECOND / 30, // MinFrameInterval, 100 nS units + ONESECOND, // MaxFrameInterval, 100 nS units + 8 * 4 * 30 * D_X * D_Y, // MinBitsPerSecond; + 8 * 4 * 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 * 4 * 8 * 30, // DWORD dwBitRate; + 0L, // DWORD dwBitErrorRate; + ONESECOND / 30, // REFERENCE_TIME AvgTimePerFrame; + sizeof (KS_BITMAPINFOHEADER), // DWORD biSize; + D_X, // LONG biWidth; + D_Y, // LONG biHeight; + 1, // WORD biPlanes; + 32, // WORD biBitCount; + KS_BI_RGB, // DWORD biCompression; + D_X *D_Y * 4, // DWORD biSizeImage; + 0, // LONG biXPelsPerMeter; + 0, // LONG biYPelsPerMeter; + 0, // DWORD biClrUsed; + 0 // DWORD biClrImportant; + } +}; + + +const +KS_DATARANGE_VIDEO +FormatRGB32Bpp_CaptureVGA = +{ + + // + // KSDATARANGE + // + { + sizeof (KS_DATARANGE_VIDEO), // FormatSize + 0, // Flags + DMAX_X *DMAX_Y * 4, // SampleSize + 0, // Reserved + + STATICGUIDOF (KSDATAFORMAT_TYPE_VIDEO), // aka. MEDIATYPE_Video + 0xe436eb7e, 0x524f, 0x11ce, 0x9f, 0x53, 0x00, 0x20, + 0xaf, 0x0b, 0xa7, 0x70, // aka. MEDIASUBTYPE_RGB32 + 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) + DMAX_X,DMAX_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; + DMAX_X, DMAX_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 + ONESECOND / 30, // MinFrameInterval, 100 nS units + ONESECOND, // MaxFrameInterval, 100 nS units + 8 * 4 * 30 * DMAX_X * DMAX_Y, // MinBitsPerSecond; + 8 * 4 * 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 * 4 * 8 * 30, // DWORD dwBitRate; + 0L, // DWORD dwBitErrorRate; + ONESECOND / 30, // REFERENCE_TIME AvgTimePerFrame; + sizeof (KS_BITMAPINFOHEADER), // DWORD biSize; + DMAX_X, // LONG biWidth; + DMAX_Y, // LONG biHeight; + 1, // WORD biPlanes; + 32, // WORD biBitCount; + KS_BI_RGB, // DWORD biCompression; + DMAX_X *DMAX_Y * 4, // DWORD biSizeImage; + 0, // LONG biXPelsPerMeter; + 0, // LONG biYPelsPerMeter; + 0, // DWORD biClrUsed; + 0 // DWORD biClrImportant; + } +}; + + + +// +//60 fps capture +// +const +KS_DATARANGE_VIDEO +FormatRGB32Bpp_CaptureVGA_60fps = +{ + + // + // KSDATARANGE + // + { + sizeof (KS_DATARANGE_VIDEO), // FormatSize + 0, // Flags + DMAX_X *DMAX_Y * 4, // SampleSize + 0, // Reserved + + STATICGUIDOF (KSDATAFORMAT_TYPE_VIDEO), // aka. MEDIATYPE_Video + 0xe436eb7e, 0x524f, 0x11ce, 0x9f, 0x53, 0x00, 0x20, + 0xaf, 0x0b, 0xa7, 0x70, // aka. MEDIASUBTYPE_RGB32 + 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) + DMAX_X,DMAX_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; + DMAX_X, DMAX_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 + ONESECOND / 60, // MinFrameInterval, 100 nS units + ONESECOND, // MaxFrameInterval, 100 nS units + 8 * 4 * 60 * DMAX_X * DMAX_Y, // MinBitsPerSecond; + 8 * 4 * 60 * 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 * 4 * 8 * 60, // DWORD dwBitRate; + 0L, // DWORD dwBitErrorRate; + ONESECOND / 60, // REFERENCE_TIME AvgTimePerFrame; + sizeof (KS_BITMAPINFOHEADER), // DWORD biSize; + DMAX_X, // LONG biWidth; + DMAX_Y, // LONG biHeight; + 1, // WORD biPlanes; + 32, // WORD biBitCount; + KS_BI_RGB, // DWORD biCompression; + DMAX_X *DMAX_Y * 4, // DWORD biSizeImage; + 0, // LONG biXPelsPerMeter; + 0, // LONG biYPelsPerMeter; + 0, // DWORD biClrUsed; + 0 // DWORD biClrImportant; + } +}; + +// +//90 fps capture +// +const +KS_DATARANGE_VIDEO +FormatRGB32Bpp_CaptureVGA_90fps = +{ + + // + // KSDATARANGE + // + { + sizeof (KS_DATARANGE_VIDEO), // FormatSize + 0, // Flags + DMAX_X *DMAX_Y * 4, // SampleSize + 0, // Reserved + + STATICGUIDOF (KSDATAFORMAT_TYPE_VIDEO), // aka. MEDIATYPE_Video + 0xe436eb7e, 0x524f, 0x11ce, 0x9f, 0x53, 0x00, 0x20, + 0xaf, 0x0b, 0xa7, 0x70, // aka. MEDIASUBTYPE_RGB32 + 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) + DMAX_X,DMAX_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; + DMAX_X, DMAX_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 + ONESECOND / 90, // MinFrameInterval, 100 nS units + ONESECOND, // MaxFrameInterval, 100 nS units + 8 * 4 * 90 * DMAX_X * DMAX_Y, // MinBitsPerSecond; + 8 * 4 * 90 * 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 * 4 * 8 * 90, // DWORD dwBitRate; + 0L, // DWORD dwBitErrorRate; + ONESECOND / 90, // REFERENCE_TIME AvgTimePerFrame; + sizeof (KS_BITMAPINFOHEADER), // DWORD biSize; + DMAX_X, // LONG biWidth; + DMAX_Y, // LONG biHeight; + 1, // WORD biPlanes; + 32, // WORD biBitCount; + KS_BI_RGB, // DWORD biCompression; + DMAX_X *DMAX_Y * 4, // DWORD biSizeImage; + 0, // LONG biXPelsPerMeter; + 0, // LONG biYPelsPerMeter; + 0, // DWORD biClrUsed; + 0 // DWORD biClrImportant; + } +}; + +// +//120 fps capture +// +const +KS_DATARANGE_VIDEO +FormatRGB32Bpp_CaptureVGA_120fps = +{ + + // + // KSDATARANGE + // + { + sizeof (KS_DATARANGE_VIDEO), // FormatSize + 0, // Flags + DMAX_X *DMAX_Y * 4, // SampleSize + 0, // Reserved + + STATICGUIDOF (KSDATAFORMAT_TYPE_VIDEO), // aka. MEDIATYPE_Video + 0xe436eb7e, 0x524f, 0x11ce, 0x9f, 0x53, 0x00, 0x20, + 0xaf, 0x0b, 0xa7, 0x70, // aka. MEDIASUBTYPE_RGB32 + 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) + DMAX_X,DMAX_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; + DMAX_X, DMAX_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 + ONESECOND / 120,// MinFrameInterval, 100 nS units + ONESECOND, // MaxFrameInterval, 100 nS units + 8 * 4 * 120 * DMAX_X * DMAX_Y, // MinBitsPerSecond; + 8 * 4 * 120 * 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 * 4 * 8 * 120, // DWORD dwBitRate; + 0L, // DWORD dwBitErrorRate; + ONESECOND / 120, // REFERENCE_TIME AvgTimePerFrame; + sizeof (KS_BITMAPINFOHEADER), // DWORD biSize; + DMAX_X, // LONG biWidth; + DMAX_Y, // LONG biHeight; + 1, // WORD biPlanes; + 32, // WORD biBitCount; + KS_BI_RGB, // DWORD biCompression; + DMAX_X *DMAX_Y * 4, // DWORD biSizeImage; + 0, // LONG biXPelsPerMeter; + 0, // LONG biYPelsPerMeter; + 0, // DWORD biClrUsed; + 0 // DWORD biClrImportant; + } +}; + +// Start of Overscan Dataranges. These are related to the above mediatypes, but take into account the increased sizes required. +// For simulation purposes, we are assuming an additional 20 % on the Width and 20 % on the Height +// +// FormatRGB24Bpp_Capture: +// +// This is the data range description of the RGB24 capture format we support. +// +const +KS_DATARANGE_VIDEO +FormatRGB24Bpp_CaptureQVGAOverscan = +{ + + // + // KSDATARANGE + // + { + sizeof (KS_DATARANGE_VIDEO), // FormatSize + 0, // Flags + D_X_OS *D_Y_OS * 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_OS, D_Y_OS, // InputSize, (the inherent size of the incoming signal + // with every digitized pixel unique) + D_X_OS, D_Y_OS, // MinCroppingSize, smallest rcSrc cropping rect allowed + D_X_OS, D_Y_OS, // MaxCroppingSize, largest rcSrc cropping rect allowed + 8, // CropGranularityX, granularity of cropping size + 1, // CropGranularityY + 8, // CropAlignX, alignment of cropping rect + 1, // CropAlignY; + D_X_OS, D_Y_OS, // MinOutputSize, smallest bitmap stream can produce + D_X_OS, D_Y_OS, // 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_OS * D_Y_OS, // MinBitsPerSecond; + 8 * 3 * 30 * D_X_OS *D_Y_OS // MaxBitsPerSecond; + }, + + // + // KS_VIDEOINFOHEADER (default format) + // + { + 0, 0, 0, 0, // RECT rcSource; + 0, 0, 0, 0, // RECT rcTarget; + D_X_OS *D_Y_OS * 3 * 8 * 30, // DWORD dwBitRate; + 0L, // DWORD dwBitErrorRate; + 333667, // REFERENCE_TIME AvgTimePerFrame; + sizeof (KS_BITMAPINFOHEADER), // DWORD biSize; + D_X_OS, // LONG biWidth; + D_Y_OS, // LONG biHeight; + 1, // WORD biPlanes; + 24, // WORD biBitCount; + KS_BI_RGB, // DWORD biCompression; + D_X_OS *D_Y_OS * 3, // DWORD biSizeImage; + 0, // LONG biXPelsPerMeter; + 0, // LONG biYPelsPerMeter; + 0, // DWORD biClrUsed; + 0 // DWORD biClrImportant; + } +}; + +const +KS_DATARANGE_VIDEO +FormatRGB24Bpp_CaptureVGAOverscan = +{ + + // + // KSDATARANGE + // + { + sizeof (KS_DATARANGE_VIDEO), // FormatSize + 0, // Flags + DMAX_X_OS *DMAX_Y_OS * 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 + DMAX_X_OS, DMAX_Y_OS, // InputSize, (the inherent size of the incoming signal + // with every digitized pixel unique) + DMAX_X_OS, DMAX_Y_OS, // MinCroppingSize, smallest rcSrc cropping rect allowed + DMAX_X_OS, DMAX_Y_OS, // MaxCroppingSize, largest rcSrc cropping rect allowed + 8, // CropGranularityX, granularity of cropping size + 1, // CropGranularityY + 8, // CropAlignX, alignment of cropping rect + 1, // CropAlignY; + DMAX_X_OS, DMAX_Y_OS, // MinOutputSize, smallest bitmap stream can produce + DMAX_X_OS, DMAX_Y_OS, // 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 * DMAX_X_OS * DMAX_Y_OS, // MinBitsPerSecond; + 8 * 3 * 30 * DMAX_X_OS *DMAX_Y_OS // MaxBitsPerSecond; + }, + + // + // KS_VIDEOINFOHEADER (default format) + // + { + 0, 0, 0, 0, // RECT rcSource; + 0, 0, 0, 0, // RECT rcTarget; + DMAX_X_OS *DMAX_Y_OS * 3 * 8 * 30, // DWORD dwBitRate; + 0L, // DWORD dwBitErrorRate; + 333667, // REFERENCE_TIME AvgTimePerFrame; + sizeof (KS_BITMAPINFOHEADER), // DWORD biSize; + DMAX_X_OS, // LONG biWidth; + DMAX_Y_OS, // LONG biHeight; + 1, // WORD biPlanes; + 24, // WORD biBitCount; + KS_BI_RGB, // DWORD biCompression; + DMAX_X_OS *DMAX_Y_OS * 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_CaptureQVGAOverscan = +{ + + // + // KSDATARANGE + // + { + sizeof (KS_DATARANGE_VIDEO), // FormatSize + 0, // Flags + D_X_OS *D_Y_OS * 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 + D_X_OS, D_Y_OS, // InputSize, (the inherent size of the incoming signal + // with every digitized pixel unique) + D_X_OS, D_Y_OS, // MinCroppingSize, smallest rcSrc cropping rect allowed + D_X_OS, D_Y_OS, // MaxCroppingSize, largest rcSrc cropping rect allowed + 8, // CropGranularityX, granularity of cropping size + 1, // CropGranularityY + 8, // CropAlignX, alignment of cropping rect + 1, // CropAlignY; + D_X_OS, D_Y_OS, // MinOutputSize, smallest bitmap stream can produce + D_X_OS, D_Y_OS, // 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_OS * D_Y_OS, // MinBitsPerSecond; + 8 * 2 * 30 * D_X_OS * D_Y_OS, // MaxBitsPerSecond; + }, + + // + // KS_VIDEOINFOHEADER (default format) + // + { + 0, 0, 0, 0, // RECT rcSource; + 0, 0, 0, 0, // RECT rcTarget; + D_X_OS *D_Y_OS * 2 * 8 * 30, // DWORD dwBitRate; + 0L, // DWORD dwBitErrorRate; + 333667, // REFERENCE_TIME AvgTimePerFrame; + sizeof (KS_BITMAPINFOHEADER), // DWORD biSize; + D_X_OS, // LONG biWidth; + D_Y_OS, // LONG biHeight; + 1, // WORD biPlanes; + 16, // WORD biBitCount; + FOURCC_YUY2, // DWORD biCompression; + D_X_OS *D_Y_OS * 2, // DWORD biSizeImage; + 0, // LONG biXPelsPerMeter; + 0, // LONG biYPelsPerMeter; + 0, // DWORD biClrUsed; + 0 // DWORD biClrImportant; + } +}; + + + +const +KS_DATARANGE_VIDEO +FormatYUY2_CaptureVGAOverscan = +{ + + // + // KSDATARANGE + // + { + sizeof (KS_DATARANGE_VIDEO), // FormatSize + 0, // Flags + DMAX_X_OS *DMAX_Y_OS * 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_OS, DMAX_Y_OS, // InputSize, (the inherent size of the incoming signal + // with every digitized pixel unique) + DMAX_X_OS, DMAX_Y_OS, // MinCroppingSize, smallest rcSrc cropping rect allowed + DMAX_X_OS, DMAX_Y_OS, // MaxCroppingSize, largest rcSrc cropping rect allowed + 8, // CropGranularityX, granularity of cropping size + 1, // CropGranularityY + 8, // CropAlignX, alignment of cropping rect + 1, // CropAlignY; + DMAX_X_OS, DMAX_Y_OS, // MinOutputSize, smallest bitmap stream can produce + DMAX_X_OS, DMAX_Y_OS, // 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 * DMAX_X_OS * DMAX_Y_OS, // MinBitsPerSecond; + 8 * 2 * 30 * DMAX_X_OS * DMAX_Y_OS, // MaxBitsPerSecond; + }, + + // + // KS_VIDEOINFOHEADER (default format) + // + { + 0, 0, 0, 0, // RECT rcSource; + 0, 0, 0, 0, // RECT rcTarget; + DMAX_X_OS *DMAX_Y_OS * 2 * 8 * 30, // DWORD dwBitRate; + 0L, // DWORD dwBitErrorRate; + 333667, // REFERENCE_TIME AvgTimePerFrame; + sizeof (KS_BITMAPINFOHEADER), // DWORD biSize; + DMAX_X_OS, // LONG biWidth; + DMAX_Y_OS, // LONG biHeight; + 1, // WORD biPlanes; + 16, // WORD biBitCount; + FOURCC_YUY2, // DWORD biCompression; + DMAX_X_OS *DMAX_Y_OS * 2, // DWORD biSizeImage; + 0, // LONG biXPelsPerMeter; + 0, // LONG biYPelsPerMeter; + 0, // DWORD biClrUsed; + 0 // DWORD biClrImportant; + } +}; + + + +// +//60 fps capture +// +const +KS_DATARANGE_VIDEO +FormatRGB24Bpp_CaptureVGA_60fpsOverscan = +{ + + // + // KSDATARANGE + // + { + sizeof (KS_DATARANGE_VIDEO), // FormatSize + 0, // Flags + DMAX_X_OS *DMAX_Y_OS * 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 + DMAX_X_OS, DMAX_Y_OS, // InputSize, (the inherent size of the incoming signal + // with every digitized pixel unique) + DMAX_X_OS, DMAX_Y_OS, // MinCroppingSize, smallest rcSrc cropping rect allowed + DMAX_X_OS, DMAX_Y_OS, // MaxCroppingSize, largest rcSrc cropping rect allowed + 8, // CropGranularityX, granularity of cropping size + 1, // CropGranularityY + 8, // CropAlignX, alignment of cropping rect + 1, // CropAlignY; + DMAX_X_OS, DMAX_Y_OS, // MinOutputSize, smallest bitmap stream can produce + DMAX_X_OS, DMAX_Y_OS, // 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 + 166833, // MinFrameInterval, 100 nS units + 640000000, // MaxFrameInterval, 100 nS units + 8 * 3 * 60 * DMAX_X_OS * DMAX_Y_OS, // MinBitsPerSecond; + 8 * 3 * 60 * DMAX_X_OS *DMAX_Y_OS // MaxBitsPerSecond; + }, + + // + // KS_VIDEOINFOHEADER (default format) + // + { + 0, 0, 0, 0, // RECT rcSource; + 0, 0, 0, 0, // RECT rcTarget; + DMAX_X_OS *DMAX_Y_OS * 3 * 8 * 60, // DWORD dwBitRate; + 0L, // DWORD dwBitErrorRate; + 166833, // REFERENCE_TIME AvgTimePerFrame; + sizeof (KS_BITMAPINFOHEADER), // DWORD biSize; + DMAX_X_OS, // LONG biWidth; + DMAX_Y_OS, // LONG biHeight; + 1, // WORD biPlanes; + 24, // WORD biBitCount; + KS_BI_RGB, // DWORD biCompression; + DMAX_X_OS *DMAX_Y_OS * 3, // DWORD biSizeImage; + 0, // LONG biXPelsPerMeter; + 0, // LONG biYPelsPerMeter; + 0, // DWORD biClrUsed; + 0 // DWORD biClrImportant; + } +}; + +const +KS_DATARANGE_VIDEO +FormatYUY2_CaptureVGA_60fpsOverscan = +{ + + // + // KSDATARANGE + // + { + sizeof (KS_DATARANGE_VIDEO), // FormatSize + 0, // Flags + DMAX_X_OS *DMAX_Y_OS * 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_OS, DMAX_Y_OS, // InputSize, (the inherent size of the incoming signal + // with every digitized pixel unique) + DMAX_X_OS, DMAX_Y_OS, // MinCroppingSize, smallest rcSrc cropping rect allowed + DMAX_X_OS, DMAX_Y_OS, // MaxCroppingSize, largest rcSrc cropping rect allowed + 8, // CropGranularityX, granularity of cropping size + 1, // CropGranularityY + 8, // CropAlignX, alignment of cropping rect + 1, // CropAlignY; + DMAX_X_OS, DMAX_Y_OS, // MinOutputSize, smallest bitmap stream can produce + DMAX_X_OS, DMAX_Y_OS, // 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 + 166833, // MinFrameInterval, 100 nS units + 640000000, // MaxFrameInterval, 100 nS units + 8 * 2 * 60 * DMAX_X_OS * DMAX_Y_OS, // MinBitsPerSecond; + 8 * 2 * 60 * DMAX_X_OS * DMAX_Y_OS, // MaxBitsPerSecond; + }, + + // + // KS_VIDEOINFOHEADER (default format) + // + { + 0, 0, 0, 0, // RECT rcSource; + 0, 0, 0, 0, // RECT rcTarget; + DMAX_X_OS *DMAX_Y_OS * 2 * 8 * 60, // DWORD dwBitRate; + 0L, // DWORD dwBitErrorRate; + 166833, // REFERENCE_TIME AvgTimePerFrame; + sizeof (KS_BITMAPINFOHEADER), // DWORD biSize; + DMAX_X_OS, // LONG biWidth; + DMAX_Y_OS, // LONG biHeight; + 1, // WORD biPlanes; + 16, // WORD biBitCount; + FOURCC_YUY2, // DWORD biCompression; + DMAX_X_OS *DMAX_Y_OS * 2, // DWORD biSizeImage; + 0, // LONG biXPelsPerMeter; + 0, // LONG biYPelsPerMeter; + 0, // DWORD biClrUsed; + 0 // DWORD biClrImportant; + } +}; + + +// +//90 fps capture +// +const +KS_DATARANGE_VIDEO +FormatRGB24Bpp_CaptureVGA_90fpsOverscan = +{ + + // + // KSDATARANGE + // + { + sizeof (KS_DATARANGE_VIDEO), // FormatSize + 0, // Flags + DMAX_X_OS *DMAX_Y_OS * 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 + DMAX_X_OS, DMAX_Y_OS, // InputSize, (the inherent size of the incoming signal + // with every digitized pixel unique) + DMAX_X_OS, DMAX_Y_OS, // MinCroppingSize, smallest rcSrc cropping rect allowed + DMAX_X_OS, DMAX_Y_OS, // MaxCroppingSize, largest rcSrc cropping rect allowed + 8, // CropGranularityX, granularity of cropping size + 1, // CropGranularityY + 8, // CropAlignX, alignment of cropping rect + 1, // CropAlignY; + DMAX_X_OS, DMAX_Y_OS, // MinOutputSize, smallest bitmap stream can produce + DMAX_X_OS, DMAX_Y_OS, // 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 + 111111, // MinFrameInterval, 100 nS units + 640000000, // MaxFrameInterval, 100 nS units + 8 * 3 * 90 * DMAX_X_OS * DMAX_Y_OS, // MinBitsPerSecond; + 8 * 3 * 90 * DMAX_X_OS *DMAX_Y_OS // MaxBitsPerSecond; + }, + + // + // KS_VIDEOINFOHEADER (default format) + // + { + 0, 0, 0, 0, // RECT rcSource; + 0, 0, 0, 0, // RECT rcTarget; + DMAX_X_OS *DMAX_Y_OS * 3 * 8 * 90, // DWORD dwBitRate; + 0L, // DWORD dwBitErrorRate; + 111111, // REFERENCE_TIME AvgTimePerFrame; + sizeof (KS_BITMAPINFOHEADER), // DWORD biSize; + DMAX_X_OS, // LONG biWidth; + DMAX_Y_OS, // LONG biHeight; + 1, // WORD biPlanes; + 24, // WORD biBitCount; + KS_BI_RGB, // DWORD biCompression; + DMAX_X_OS *DMAX_Y_OS * 3, // DWORD biSizeImage; + 0, // LONG biXPelsPerMeter; + 0, // LONG biYPelsPerMeter; + 0, // DWORD biClrUsed; + 0 // DWORD biClrImportant; + } +}; + +const +KS_DATARANGE_VIDEO +FormatYUY2_CaptureVGA_90fpsOverscan = +{ + + // + // KSDATARANGE + // + { + sizeof (KS_DATARANGE_VIDEO), // FormatSize + 0, // Flags + DMAX_X_OS *DMAX_Y_OS * 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_OS, DMAX_Y_OS, // InputSize, (the inherent size of the incoming signal + // with every digitized pixel unique) + DMAX_X_OS, DMAX_Y_OS, // MinCroppingSize, smallest rcSrc cropping rect allowed + DMAX_X_OS, DMAX_Y_OS, // MaxCroppingSize, largest rcSrc cropping rect allowed + 8, // CropGranularityX, granularity of cropping size + 1, // CropGranularityY + 8, // CropAlignX, alignment of cropping rect + 1, // CropAlignY; + DMAX_X_OS, DMAX_Y_OS, // MinOutputSize, smallest bitmap stream can produce + DMAX_X_OS, DMAX_Y_OS, // 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 + 111111, // MinFrameInterval, 100 nS units + 640000000, // MaxFrameInterval, 100 nS units + 8 * 2 * 90 * DMAX_X_OS * DMAX_Y_OS, // MinBitsPerSecond; + 8 * 2 * 90 * DMAX_X_OS * DMAX_Y_OS, // MaxBitsPerSecond; + }, + + // + // KS_VIDEOINFOHEADER (default format) + // + { + 0, 0, 0, 0, // RECT rcSource; + 0, 0, 0, 0, // RECT rcTarget; + DMAX_X_OS *DMAX_Y_OS * 2 * 8 * 90, // DWORD dwBitRate; + 0L, // DWORD dwBitErrorRate; + 111111, // REFERENCE_TIME AvgTimePerFrame; + sizeof (KS_BITMAPINFOHEADER), // DWORD biSize; + DMAX_X_OS, // LONG biWidth; + DMAX_Y_OS, // LONG biHeight; + 1, // WORD biPlanes; + 16, // WORD biBitCount; + FOURCC_YUY2, // DWORD biCompression; + DMAX_X_OS *DMAX_Y_OS * 2, // DWORD biSizeImage; + 0, // LONG biXPelsPerMeter; + 0, // LONG biYPelsPerMeter; + 0, // DWORD biClrUsed; + 0 // DWORD biClrImportant; + } +}; + + +// +//120 fps capture +// +const +KS_DATARANGE_VIDEO +FormatRGB24Bpp_CaptureVGA_120fpsOverscan = +{ + + // + // KSDATARANGE + // + { + sizeof (KS_DATARANGE_VIDEO), // FormatSize + 0, // Flags + DMAX_X_OS *DMAX_Y_OS * 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 + DMAX_X_OS, DMAX_Y_OS, // InputSize, (the inherent size of the incoming signal + // with every digitized pixel unique) + DMAX_X_OS, DMAX_Y_OS, // MinCroppingSize, smallest rcSrc cropping rect allowed + DMAX_X_OS, DMAX_Y_OS, // MaxCroppingSize, largest rcSrc cropping rect allowed + 8, // CropGranularityX, granularity of cropping size + 1, // CropGranularityY + 8, // CropAlignX, alignment of cropping rect + 1, // CropAlignY; + DMAX_X_OS, DMAX_Y_OS, // MinOutputSize, smallest bitmap stream can produce + DMAX_X_OS, DMAX_Y_OS, // 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 + 83333, // MinFrameInterval, 100 nS units + 640000000, // MaxFrameInterval, 100 nS units + 8 * 3 * 120 * DMAX_X_OS * DMAX_Y_OS, // MinBitsPerSecond; + 8 * 3 * 120 * DMAX_X_OS *DMAX_Y_OS // MaxBitsPerSecond; + }, + + // + // KS_VIDEOINFOHEADER (default format) + // + { + 0, 0, 0, 0, // RECT rcSource; + 0, 0, 0, 0, // RECT rcTarget; + DMAX_X_OS *DMAX_Y_OS * 3 * 8 * 120, // DWORD dwBitRate; + 0L, // DWORD dwBitErrorRate; + 83333, // REFERENCE_TIME AvgTimePerFrame; + sizeof (KS_BITMAPINFOHEADER), // DWORD biSize; + DMAX_X_OS, // LONG biWidth; + DMAX_Y_OS, // LONG biHeight; + 1, // WORD biPlanes; + 24, // WORD biBitCount; + KS_BI_RGB, // DWORD biCompression; + DMAX_X_OS *DMAX_Y_OS * 3, // DWORD biSizeImage; + 0, // LONG biXPelsPerMeter; + 0, // LONG biYPelsPerMeter; + 0, // DWORD biClrUsed; + 0 // DWORD biClrImportant; + } +}; + +const +KS_DATARANGE_VIDEO +FormatYUY2_CaptureVGA_120fpsOverscan = +{ + + // + // KSDATARANGE + // + { + sizeof (KS_DATARANGE_VIDEO), // FormatSize + 0, // Flags + DMAX_X_OS *DMAX_Y_OS * 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_OS, DMAX_Y_OS, // InputSize, (the inherent size of the incoming signal + // with every digitized pixel unique) + DMAX_X_OS, DMAX_Y_OS, // MinCroppingSize, smallest rcSrc cropping rect allowed + DMAX_X_OS, DMAX_Y_OS, // MaxCroppingSize, largest rcSrc cropping rect allowed + 8, // CropGranularityX, granularity of cropping size + 1, // CropGranularityY + 8, // CropAlignX, alignment of cropping rect + 1, // CropAlignY; + DMAX_X_OS, DMAX_Y_OS, // MinOutputSize, smallest bitmap stream can produce + DMAX_X_OS, DMAX_Y_OS, // 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 + 83333, // MinFrameInterval, 100 nS units + 640000000, // MaxFrameInterval, 100 nS units + 8 * 2 * 120 * DMAX_X_OS * DMAX_Y_OS, // MinBitsPerSecond; + 8 * 2 * 120 * DMAX_X_OS * DMAX_Y_OS, // MaxBitsPerSecond; + }, + + // + // KS_VIDEOINFOHEADER (default format) + // + { + 0, 0, 0, 0, // RECT rcSource; + 0, 0, 0, 0, // RECT rcTarget; + DMAX_X_OS *DMAX_Y_OS * 2 * 8 * 120, // DWORD dwBitRate; + 0L, // DWORD dwBitErrorRate; + 83333, // REFERENCE_TIME AvgTimePerFrame; + sizeof (KS_BITMAPINFOHEADER), // DWORD biSize; + DMAX_X_OS, // LONG biWidth; + DMAX_Y_OS, // LONG biHeight; + 1, // WORD biPlanes; + 16, // WORD biBitCount; + FOURCC_YUY2, // DWORD biCompression; + DMAX_X_OS *DMAX_Y_OS * 2, // DWORD biSizeImage; + 0, // LONG biXPelsPerMeter; + 0, // LONG biYPelsPerMeter; + 0, // DWORD biClrUsed; + 0 // DWORD biClrImportant; + } +}; + +const +KS_DATARANGE_VIDEO +FormatRGB32Bpp_CaptureQVGAOverscan = +{ + + // + // KSDATARANGE + // + { + sizeof (KS_DATARANGE_VIDEO), // FormatSize + 0, // Flags + D_X_OS *D_Y_OS * 4, // SampleSize + 0, // Reserved + + STATICGUIDOF(KSDATAFORMAT_TYPE_VIDEO), // aka. MEDIATYPE_Video + 0xe436eb7e, 0x524f, 0x11ce, 0x9f, 0x53, 0x00, 0x20, + 0xaf, 0x0b, 0xa7, 0x70, // aka. MEDIASUBTYPE_RGB32 + 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_OS, D_Y_OS, // InputSize, (the inherent size of the incoming signal + // with every digitized pixel unique) + D_X_OS, D_Y_OS, // MinCroppingSize, smallest rcSrc cropping rect allowed + D_X_OS, D_Y_OS, // MaxCroppingSize, largest rcSrc cropping rect allowed + 8, // CropGranularityX, granularity of cropping size + 1, // CropGranularityY + 8, // CropAlignX, alignment of cropping rect + 1, // CropAlignY; + D_X_OS, D_Y_OS, // MinOutputSize, smallest bitmap stream can produce + D_X_OS, D_Y_OS, // 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 + ONESECOND / 30, // MinFrameInterval, 100 nS units + ONESECOND, // MaxFrameInterval, 100 nS units + 8 * 4 * 30 * D_X_OS * D_Y_OS, // MinBitsPerSecond; + 8 * 4 * 30 * D_X_OS *D_Y_OS // MaxBitsPerSecond; + }, + + // + // KS_VIDEOINFOHEADER (default format) + // + { + 0, 0, 0, 0, // RECT rcSource; + 0, 0, 0, 0, // RECT rcTarget; + D_X_OS *D_Y_OS * 4 * 8 * 30, // DWORD dwBitRate; + 0L, // DWORD dwBitErrorRate; + ONESECOND / 30, // REFERENCE_TIME AvgTimePerFrame; + sizeof (KS_BITMAPINFOHEADER), // DWORD biSize; + D_X_OS, // LONG biWidth; + D_Y_OS, // LONG biHeight; + 1, // WORD biPlanes; + 32, // WORD biBitCount; + KS_BI_RGB, // DWORD biCompression; + D_X_OS *D_Y_OS * 4, // DWORD biSizeImage; + 0, // LONG biXPelsPerMeter; + 0, // LONG biYPelsPerMeter; + 0, // DWORD biClrUsed; + 0 // DWORD biClrImportant; + } +}; + + +const +KS_DATARANGE_VIDEO +FormatRGB32Bpp_CaptureVGAOverscan = +{ + + // + // KSDATARANGE + // + { + sizeof (KS_DATARANGE_VIDEO), // FormatSize + 0, // Flags + DMAX_X_OS *DMAX_Y_OS * 4, // SampleSize + 0, // Reserved + + STATICGUIDOF(KSDATAFORMAT_TYPE_VIDEO), // aka. MEDIATYPE_Video + 0xe436eb7e, 0x524f, 0x11ce, 0x9f, 0x53, 0x00, 0x20, + 0xaf, 0x0b, 0xa7, 0x70, // aka. MEDIASUBTYPE_RGB32 + 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_OS, DMAX_Y_OS, // InputSize, (the inherent size of the incoming signal + // with every digitized pixel unique) + DMAX_X_OS, DMAX_Y_OS, // MinCroppingSize, smallest rcSrc cropping rect allowed + DMAX_X_OS, DMAX_Y_OS, // MaxCroppingSize, largest rcSrc cropping rect allowed + 8, // CropGranularityX, granularity of cropping size + 1, // CropGranularityY + 8, // CropAlignX, alignment of cropping rect + 1, // CropAlignY; + DMAX_X_OS, DMAX_Y_OS, // MinOutputSize, smallest bitmap stream can produce + DMAX_X_OS, DMAX_Y_OS, // 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 + ONESECOND / 30, // MinFrameInterval, 100 nS units + ONESECOND, // MaxFrameInterval, 100 nS units + 8 * 4 * 30 * DMAX_X_OS * DMAX_Y_OS, // MinBitsPerSecond; + 8 * 4 * 30 * DMAX_X_OS *DMAX_Y_OS // MaxBitsPerSecond; + }, + + // + // KS_VIDEOINFOHEADER (default format) + // + { + 0, 0, 0, 0, // RECT rcSource; + 0, 0, 0, 0, // RECT rcTarget; + DMAX_X_OS *DMAX_Y_OS * 4 * 8 * 30, // DWORD dwBitRate; + 0L, // DWORD dwBitErrorRate; + ONESECOND / 30, // REFERENCE_TIME AvgTimePerFrame; + sizeof (KS_BITMAPINFOHEADER), // DWORD biSize; + DMAX_X_OS, // LONG biWidth; + DMAX_Y_OS, // LONG biHeight; + 1, // WORD biPlanes; + 32, // WORD biBitCount; + KS_BI_RGB, // DWORD biCompression; + DMAX_X_OS *DMAX_Y_OS * 4, // DWORD biSizeImage; + 0, // LONG biXPelsPerMeter; + 0, // LONG biYPelsPerMeter; + 0, // DWORD biClrUsed; + 0 // DWORD biClrImportant; + } +}; + + + +// +//60 fps capture +// +const +KS_DATARANGE_VIDEO +FormatRGB32Bpp_CaptureVGA_60fpsOverscan = +{ + + // + // KSDATARANGE + // + { + sizeof (KS_DATARANGE_VIDEO), // FormatSize + 0, // Flags + DMAX_X_OS *DMAX_Y_OS * 4, // SampleSize + 0, // Reserved + + STATICGUIDOF(KSDATAFORMAT_TYPE_VIDEO), // aka. MEDIATYPE_Video + 0xe436eb7e, 0x524f, 0x11ce, 0x9f, 0x53, 0x00, 0x20, + 0xaf, 0x0b, 0xa7, 0x70, // aka. MEDIASUBTYPE_RGB32 + 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_OS, DMAX_Y_OS, // InputSize, (the inherent size of the incoming signal + // with every digitized pixel unique) + DMAX_X_OS, DMAX_Y_OS, // MinCroppingSize, smallest rcSrc cropping rect allowed + DMAX_X_OS, DMAX_Y_OS, // MaxCroppingSize, largest rcSrc cropping rect allowed + 8, // CropGranularityX, granularity of cropping size + 1, // CropGranularityY + 8, // CropAlignX, alignment of cropping rect + 1, // CropAlignY; + DMAX_X_OS, DMAX_Y_OS, // MinOutputSize, smallest bitmap stream can produce + DMAX_X_OS, DMAX_Y_OS, // 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 + ONESECOND / 60, // MinFrameInterval, 100 nS units + ONESECOND, // MaxFrameInterval, 100 nS units + 8 * 4 * 60 * DMAX_X_OS * DMAX_Y_OS, // MinBitsPerSecond; + 8 * 4 * 60 * DMAX_X_OS *DMAX_Y_OS // MaxBitsPerSecond; + }, + + // + // KS_VIDEOINFOHEADER (default format) + // + { + 0, 0, 0, 0, // RECT rcSource; + 0, 0, 0, 0, // RECT rcTarget; + DMAX_X_OS *DMAX_Y_OS * 4 * 8 * 60, // DWORD dwBitRate; + 0L, // DWORD dwBitErrorRate; + ONESECOND / 60, // REFERENCE_TIME AvgTimePerFrame; + sizeof (KS_BITMAPINFOHEADER), // DWORD biSize; + DMAX_X_OS, // LONG biWidth; + DMAX_Y_OS, // LONG biHeight; + 1, // WORD biPlanes; + 32, // WORD biBitCount; + KS_BI_RGB, // DWORD biCompression; + DMAX_X_OS *DMAX_Y_OS * 4, // DWORD biSizeImage; + 0, // LONG biXPelsPerMeter; + 0, // LONG biYPelsPerMeter; + 0, // DWORD biClrUsed; + 0 // DWORD biClrImportant; + } +}; + +// +//90 fps capture +// +const +KS_DATARANGE_VIDEO +FormatRGB32Bpp_CaptureVGA_90fpsOverscan = +{ + + // + // KSDATARANGE + // + { + sizeof (KS_DATARANGE_VIDEO), // FormatSize + 0, // Flags + DMAX_X_OS *DMAX_Y_OS * 4, // SampleSize + 0, // Reserved + + STATICGUIDOF(KSDATAFORMAT_TYPE_VIDEO), // aka. MEDIATYPE_Video + 0xe436eb7e, 0x524f, 0x11ce, 0x9f, 0x53, 0x00, 0x20, + 0xaf, 0x0b, 0xa7, 0x70, // aka. MEDIASUBTYPE_RGB32 + 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_OS, DMAX_Y_OS, // InputSize, (the inherent size of the incoming signal + // with every digitized pixel unique) + DMAX_X_OS, DMAX_Y_OS, // MinCroppingSize, smallest rcSrc cropping rect allowed + DMAX_X_OS, DMAX_Y_OS, // MaxCroppingSize, largest rcSrc cropping rect allowed + 8, // CropGranularityX, granularity of cropping size + 1, // CropGranularityY + 8, // CropAlignX, alignment of cropping rect + 1, // CropAlignY; + DMAX_X_OS, DMAX_Y_OS, // MinOutputSize, smallest bitmap stream can produce + DMAX_X_OS, DMAX_Y_OS, // 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 + ONESECOND / 90, // MinFrameInterval, 100 nS units + ONESECOND, // MaxFrameInterval, 100 nS units + 8 * 4 * 90 * DMAX_X_OS * DMAX_Y_OS, // MinBitsPerSecond; + 8 * 4 * 90 * DMAX_X_OS *DMAX_Y_OS // MaxBitsPerSecond; + }, + + // + // KS_VIDEOINFOHEADER (default format) + // + { + 0, 0, 0, 0, // RECT rcSource; + 0, 0, 0, 0, // RECT rcTarget; + DMAX_X_OS *DMAX_Y_OS * 4 * 8 * 90, // DWORD dwBitRate; + 0L, // DWORD dwBitErrorRate; + ONESECOND / 90, // REFERENCE_TIME AvgTimePerFrame; + sizeof (KS_BITMAPINFOHEADER), // DWORD biSize; + DMAX_X_OS, // LONG biWidth; + DMAX_Y_OS, // LONG biHeight; + 1, // WORD biPlanes; + 32, // WORD biBitCount; + KS_BI_RGB, // DWORD biCompression; + DMAX_X_OS *DMAX_Y_OS * 4, // DWORD biSizeImage; + 0, // LONG biXPelsPerMeter; + 0, // LONG biYPelsPerMeter; + 0, // DWORD biClrUsed; + 0 // DWORD biClrImportant; + } +}; + +// +//120 fps capture +// +const +KS_DATARANGE_VIDEO +FormatRGB32Bpp_CaptureVGA_120fpsOverscan = +{ + + // + // KSDATARANGE + // + { + sizeof (KS_DATARANGE_VIDEO), // FormatSize + 0, // Flags + DMAX_X_OS *DMAX_Y_OS * 4, // SampleSize + 0, // Reserved + + STATICGUIDOF(KSDATAFORMAT_TYPE_VIDEO), // aka. MEDIATYPE_Video + 0xe436eb7e, 0x524f, 0x11ce, 0x9f, 0x53, 0x00, 0x20, + 0xaf, 0x0b, 0xa7, 0x70, // aka. MEDIASUBTYPE_RGB32 + 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_OS, DMAX_Y_OS, // InputSize, (the inherent size of the incoming signal + // with every digitized pixel unique) + DMAX_X_OS, DMAX_Y_OS, // MinCroppingSize, smallest rcSrc cropping rect allowed + DMAX_X_OS, DMAX_Y_OS, // MaxCroppingSize, largest rcSrc cropping rect allowed + 8, // CropGranularityX, granularity of cropping size + 1, // CropGranularityY + 8, // CropAlignX, alignment of cropping rect + 1, // CropAlignY; + DMAX_X_OS, DMAX_Y_OS, // MinOutputSize, smallest bitmap stream can produce + DMAX_X_OS, DMAX_Y_OS, // 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 + ONESECOND / 120,// MinFrameInterval, 100 nS units + ONESECOND, // MaxFrameInterval, 100 nS units + 8 * 4 * 120 * DMAX_X_OS * DMAX_Y_OS, // MinBitsPerSecond; + 8 * 4 * 120 * DMAX_X_OS *DMAX_Y_OS // MaxBitsPerSecond; + }, + + // + // KS_VIDEOINFOHEADER (default format) + // + { + 0, 0, 0, 0, // RECT rcSource; + 0, 0, 0, 0, // RECT rcTarget; + DMAX_X_OS *DMAX_Y_OS * 4 * 8 * 120, // DWORD dwBitRate; + 0L, // DWORD dwBitErrorRate; + ONESECOND / 120, // REFERENCE_TIME AvgTimePerFrame; + sizeof (KS_BITMAPINFOHEADER), // DWORD biSize; + DMAX_X_OS, // LONG biWidth; + DMAX_Y_OS, // LONG biHeight; + 1, // WORD biPlanes; + 32, // WORD biBitCount; + KS_BI_RGB, // DWORD biCompression; + DMAX_X_OS *DMAX_Y_OS * 4, // 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... +// +DEFINE_CAMERA_KSPIN_DISPATCH( VideoCapturePinDispatch, CVideoCapturePin ); + +// +// 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 ( + VideoCapturePinAllocatorFraming, + 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 +// RGB24, YUY2, and RGB32 +// +const +PKSDATARANGE +VideoCapturePinDataRanges [VIDEO_CAPTURE_PIN_DATA_RANGE_COUNT] = +{ + (PKSDATARANGE) &FormatYUY2_CaptureQVGA, + (PKSDATARANGE) &FormatYUY2_CaptureQVGAOverscan, + (PKSDATARANGE) &FormatYUY2_CaptureVGA, + (PKSDATARANGE) &FormatYUY2_CaptureVGAOverscan, + (PKSDATARANGE) &FormatRGB24Bpp_CaptureQVGA, + (PKSDATARANGE) &FormatRGB24Bpp_CaptureQVGAOverscan, + (PKSDATARANGE) &FormatRGB24Bpp_CaptureVGA, + (PKSDATARANGE) &FormatRGB24Bpp_CaptureVGAOverscan, + (PKSDATARANGE) &FormatYUY2_CaptureVGA_60fps, + (PKSDATARANGE) &FormatYUY2_CaptureVGA_60fpsOverscan, + (PKSDATARANGE) &FormatRGB24Bpp_CaptureVGA_60fps, + (PKSDATARANGE) &FormatRGB24Bpp_CaptureVGA_60fpsOverscan, + (PKSDATARANGE) &FormatYUY2_CaptureVGA_90fps, + (PKSDATARANGE) &FormatYUY2_CaptureVGA_90fpsOverscan, + (PKSDATARANGE) &FormatRGB24Bpp_CaptureVGA_90fps, + (PKSDATARANGE) &FormatRGB24Bpp_CaptureVGA_90fpsOverscan, + (PKSDATARANGE) &FormatYUY2_CaptureVGA_120fps, + (PKSDATARANGE) &FormatYUY2_CaptureVGA_120fpsOverscan, + (PKSDATARANGE) &FormatRGB24Bpp_CaptureVGA_120fps, + (PKSDATARANGE) &FormatRGB24Bpp_CaptureVGA_120fpsOverscan, + (PKSDATARANGE) &FormatRGB32Bpp_CaptureVGA, + (PKSDATARANGE) &FormatRGB32Bpp_CaptureVGAOverscan, + (PKSDATARANGE) &FormatRGB32Bpp_CaptureVGA_60fps, + (PKSDATARANGE) &FormatRGB32Bpp_CaptureVGA_60fpsOverscan, + (PKSDATARANGE) &FormatRGB32Bpp_CaptureVGA_90fps, + (PKSDATARANGE) &FormatRGB32Bpp_CaptureVGA_90fpsOverscan, + (PKSDATARANGE) &FormatRGB32Bpp_CaptureVGA_120fps, + (PKSDATARANGE) &FormatRGB32Bpp_CaptureVGA_120fpsOverscan, + (PKSDATARANGE) &FormatRGB32Bpp_CaptureQVGA, + (PKSDATARANGE) &FormatRGB32Bpp_CaptureQVGAOverscan, +}; + +const +PKSDATARANGE +VideoPreviewPinDataRanges[VIDEO_PREVIEW_PIN_DATA_RANGE_COUNT] = +{ + (PKSDATARANGE) &FormatYUY2_CaptureQVGA, + (PKSDATARANGE) &FormatYUY2_CaptureVGA, + (PKSDATARANGE) &FormatRGB24Bpp_CaptureQVGA, + (PKSDATARANGE) &FormatRGB24Bpp_CaptureVGA, + (PKSDATARANGE) &FormatYUY2_CaptureVGA_60fps, + (PKSDATARANGE) &FormatRGB24Bpp_CaptureVGA_60fps, + (PKSDATARANGE) &FormatYUY2_CaptureVGA_90fps, + (PKSDATARANGE) &FormatRGB24Bpp_CaptureVGA_90fps, + (PKSDATARANGE) &FormatYUY2_CaptureVGA_120fps, + (PKSDATARANGE) &FormatRGB24Bpp_CaptureVGA_120fps, + (PKSDATARANGE) &FormatRGB32Bpp_CaptureVGA, + (PKSDATARANGE) &FormatRGB32Bpp_CaptureVGA_60fps, + (PKSDATARANGE) &FormatRGB32Bpp_CaptureVGA_90fps, + (PKSDATARANGE) &FormatRGB32Bpp_CaptureVGA_120fps, + (PKSDATARANGE) &FormatRGB32Bpp_CaptureQVGA, +}; \ No newline at end of file diff --git a/avscamera/sys/VideoCapture.h b/avscamera/sys/VideoCapture.h new file mode 100644 index 000000000..da08b0a8f --- /dev/null +++ b/avscamera/sys/VideoCapture.h @@ -0,0 +1,34 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2001, Microsoft Corporation. + + File: + + videocapture.h + + Abstract: + + Video Capture Pin definition. + + CVideoCapturePin derived from CCapturePin. + + History: + + created 3/8/2001 + +**************************************************************************/ + +#pragma once + +#include "capture.h" + +class CVideoCapturePin : public CCapturePin +{ +public: + static NTSTATUS DispatchCreate (IN PKSPIN Pin, IN PIRP Irp); + +private: + CVideoCapturePin(IN PKSPIN Pin); +}; diff --git a/avscamera/sys/VideoHwSim.cpp b/avscamera/sys/VideoHwSim.cpp new file mode 100644 index 000000000..33b6de90a --- /dev/null +++ b/avscamera/sys/VideoHwSim.cpp @@ -0,0 +1,66 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + VideoHwSim.cpp + + Abstract: + + A fake h/w simulation for the video pin. Provides Video pin specific + metadata. + + History: + + created 5/28/2014 + +**************************************************************************/ + +#include "Common.h" + + +CVideoHardwareSimulation::CVideoHardwareSimulation( + _Inout_ CSensor *Sensor, + _In_ LONG PinID +) + : CHardwareSimulation( Sensor, PinID ) +{} + + +CVideoHardwareSimulation::~CVideoHardwareSimulation() +{} + +// +// Emit metadata here for video pin. +// +void +CVideoHardwareSimulation:: +EmitMetadata( + _Inout_ PKSSTREAM_HEADER pStreamHeader +) +{ + NT_ASSERT(pStreamHeader); + + // Add the normal frame info to the metadata + // Note: This call ensures the KSSTREAM_METADATA_INFO is properly initialized. + CHardwareSimulation::EmitMetadata( pStreamHeader ); + + if (0 != (pStreamHeader->OptionsFlags & KSSTREAM_HEADER_OPTIONSF_METADATA)) + { + CExtendedVidProcSetting FaceDetect; + m_Sensor->GetFaceDetection(&FaceDetect); + + if( FaceDetect.Flags & KSCAMERA_EXTENDEDPROP_FACEDETECTION_VIDEO ) + { + DBG_TRACE("VIDEO"); + EmitFaceMetadata( + pStreamHeader, + FaceDetect.GetULONG(), + FaceDetect.Flags & KSCAMERA_EXTENDEDPROP_FACEDETECTION_ADVANCED_MASK); + } + } +} + diff --git a/avscamera/sys/VideoHwSim.h b/avscamera/sys/VideoHwSim.h new file mode 100644 index 000000000..40b0b24b9 --- /dev/null +++ b/avscamera/sys/VideoHwSim.h @@ -0,0 +1,42 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + VideoHwSim.h + + Abstract: + + A fake h/w simulation for the video pin. Provides Video pin specific + metadata. + + History: + + created 5/28/2014 + +**************************************************************************/ + +#pragma once +class CVideoHardwareSimulation : + public CHardwareSimulation +{ +public: + CVideoHardwareSimulation( + _Inout_ CSensor *Sensor, + _In_ LONG PinID + ); + + virtual ~CVideoHardwareSimulation(); + + // + // Emit metadata here for video pin. + // + void + EmitMetadata( + _Inout_ PKSSTREAM_HEADER pStreamHeader + ); +}; + diff --git a/avscamera/sys/Waitable.cpp b/avscamera/sys/Waitable.cpp new file mode 100644 index 000000000..1de5827a3 --- /dev/null +++ b/avscamera/sys/Waitable.cpp @@ -0,0 +1,69 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + waitable.cpp + + Abstract: + + Base interface for any object that is waitable. + In otherwords, it contains a kernel dispatch object. + + History: + + created 5/30/2014 + +**************************************************************************/ + +#include "Common.h" + +KWaitable:: +KWaitable() +{} + + +KWaitable:: +~KWaitable() +{} + +NTSTATUS +KWaitable:: +Wait( + _In_opt_ PLARGE_INTEGER Timeout +) +/*++ + +Routine Description: + + Default Wait implementation. + +Arguments: + + Timeout - + An optional timeout value in 100ns units. Negative is relative time. + If NULL, wait forever. + +Return Value: + + Success / Failure. + +--*/ +{ + PVOID pObj = GetDispatchObject() ; + + if(pObj != NULL) + { + return + KeWaitForSingleObject( + pObj, + Executive, + KernelMode, + FALSE, + Timeout); + } + return STATUS_SUCCESS; +} diff --git a/avscamera/sys/Waitable.h b/avscamera/sys/Waitable.h new file mode 100644 index 000000000..1f0926577 --- /dev/null +++ b/avscamera/sys/Waitable.h @@ -0,0 +1,58 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + waitable.h + + Abstract: + + Base interface for any object that is waitable. + In otherwords, it contains a kernel dispatch object. + + Also implements a Cancelable interface class. + + History: + + created 5/30/2014 + +**************************************************************************/ + +#pragma once + +// +// Interface definitions for KWaitable and Cancelable +// +class KWaitable : + public CNonCopyable +{ +public: + KWaitable(); + + virtual + ~KWaitable(); + + virtual + NTSTATUS + Wait( + _In_opt_ PLARGE_INTEGER Timeout = NULL + ); + + // Must implement + virtual + PVOID + GetDispatchObject() = 0; +}; + +class Cancelable +{ +public: + // Must implement + virtual + BOOLEAN + Cancel() = 0; +}; + diff --git a/avscamera/sys/WorkItem.cpp b/avscamera/sys/WorkItem.cpp new file mode 100644 index 000000000..9bb0d7956 --- /dev/null +++ b/avscamera/sys/WorkItem.cpp @@ -0,0 +1,167 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + workitem.cpp + + Abstract: + + This module contains an implementation for KWorkItem. + + A KWorkItem inherits from CRef and is reference counted. The object + cannot be destroyed until all references are released. This includes + the reference held when the workitem is in flight. This allows you + to store the workitem in an object that has a lifecycle shorter than + the parent device object. + + History: + + created 5/30/2014 + +**************************************************************************/ + +#include "Common.h" + +KWorkItem:: +KWorkItem( + _In_ PDEVICE_OBJECT DeviceObj, + _In_ PIO_WORKITEM_ROUTINE Callback, + _In_ PVOID Context, + _In_ WORK_QUEUE_TYPE QueueType +) + : m_Callback( Callback ) + , m_Context ( Context ) + , m_QueueType( QueueType ) +/*++ + +Routine Description: + + Constructor. + + Sets up callback handler for the workitem and performs initialization. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + m_WorkItem = IoAllocateWorkItem( DeviceObj ); +} + + +KWorkItem:: +~KWorkItem() +/*++ + +Routine Description: + + Destructor. + + Blocks until all references are released, then frees the workitem. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + // Make sure the work item has been run down. + Wait(); + + // Now delete it. + if( m_WorkItem ) + { + IoFreeWorkItem( m_WorkItem ); + } +} + +BOOLEAN +KWorkItem:: +Start() +/*++ + +Routine Description: + + Enqueue this workitem and acquires a reference on the object. + +Arguments: + + None. + +Return Value: + + TRUE - a workitem was scheduled. + +--*/ +{ + // Make sure construction succeeded. + if( m_WorkItem ) + { + // Make sure the work item isn't already in flight and acquire the lock. + // This is to make sure this KWorkItem doesn't get destroyed until + // the callback is complete. + if( Acquire() ) + { + // Schedule the work item. + IoQueueWorkItem( m_WorkItem, &KWorkItem::Handler, m_QueueType, this ); + + // Note: Release() is called in Handler. + return TRUE; + } + } + return FALSE; +} + +VOID +KWorkItem:: +Handler( + _In_ PDEVICE_OBJECT IoObject, + _In_opt_ PVOID Context +) +/*++ + +Routine Description: + + Callback thunking function. + + Calls the callback handler registered in the constructor, if one exists. + Releases the reference on this object. + +Arguments: + + IoObject - + The parent device object. + Context - + An optional context object. + +Return Value: + + void + +--*/ +{ + // Get our "this" pointer (Me). + KWorkItem *Me = (KWorkItem *) Context; + + // Invoke the callback if one exists. + if( Me->m_Callback ) + { + Me->m_Callback( IoObject, Me->m_Context ); + } + + // Unlock the work item; allow for destruction or re-enqueing. + Me->Release(); +} diff --git a/avscamera/sys/WorkItem.h b/avscamera/sys/WorkItem.h new file mode 100644 index 000000000..dfce3b91a --- /dev/null +++ b/avscamera/sys/WorkItem.h @@ -0,0 +1,63 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + workitem.h + + Abstract: + + This module contains a class wrapper definition for an IO_WORKITEM. + + A KWorkItem inherits from CRef and is reference counted. The object + cannot be destroyed until all references are released. This includes + the reference held when the workitem is in flight. This allows you + to store the workitem in an object that has a lifecycle shorter than + the parent device object. + + History: + + created 5/30/2014 + +**************************************************************************/ + +#pragma once +class KWorkItem : + public CRef // Reference counted... +{ +private: + PIO_WORKITEM m_WorkItem; + PIO_WORKITEM_ROUTINE m_Callback; + PVOID m_Context; + WORK_QUEUE_TYPE m_QueueType; + +public: + KWorkItem( + _In_ PDEVICE_OBJECT DeviceObj, + _In_ PIO_WORKITEM_ROUTINE Callback, + _In_ PVOID Context=nullptr, + _In_ WORK_QUEUE_TYPE QueueType=NormalWorkQueue + ); + + virtual + ~KWorkItem(); + + BOOLEAN + IsValid() + { + return m_WorkItem != nullptr; + } + + // Returns FALSE if failed to start. + BOOLEAN + Start(); + +private: + static + IO_WORKITEM_ROUTINE + Handler; +}; + diff --git a/avscamera/sys/XRGBSynthesizer.cpp b/avscamera/sys/XRGBSynthesizer.cpp new file mode 100644 index 000000000..7e7686481 --- /dev/null +++ b/avscamera/sys/XRGBSynthesizer.cpp @@ -0,0 +1,246 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + XRGBSynthesizer.cpp + + Abstract: + + This file contains the implementation of CXRGBSynthesizer. + + CXRGBSynthesizer is derived from CSynthesizer. It uses the RGB + color space and defines a Commit() function. XRGB is known also + known as RGB32. It uses 8 bits per primary plus 8 bits of padding + for 4 bytes per pixel in a single plane. + + History: + + created 4/14/2014 + +**************************************************************************/ + +#include "Common.h" + +/************************************************************************** + + PAGED CODE + +**************************************************************************/ + +#ifdef ALLOC_PRAGMA +#pragma code_seg("PAGE") +#endif // ALLOC_PRAGMA + +// suppressed due to Esp:773 +#pragma warning (push) +#pragma warning( disable:26015 ) // Suppress OACR error. Seems nonsensical. TODO: Must revisit. +#pragma warning( disable:6101 ) +_Success_(return > 0) +ULONG +CXRGBSynthesizer:: +Commit( + _Out_writes_bytes_(Size) + PUCHAR Buffer, + _In_ ULONG Size, + _In_ ULONG Stride +) +/*++ + +Routine Description: + + Copy (and reformat, if necessary) pixels from the internal scratch + buffer. If the output format decimates chrominance, do it here. + +Arguments: + + Buffer - + The output buffer to fill. + + Size - + The size of the output buffer in bytes. + + Stride - + The length of a row in bytes. + +Return Value: + + Number of bytes copied into Buffer. + +--*/ +{ + PAGED_CODE(); + + // In case stride isn't initialized. + if( Stride==0 ) + { + Stride = m_OutputStride; + } + + if( !( Buffer && + m_Buffer && + Size >= sizeof(KS_RGBQUAD) && + m_Length >= sizeof(KS_RGBQUAD) && + (Stride%4)==0 && + (Size%4)==0 + ) ) + { + NT_ASSERT(FALSE); + return 0; + } + + ULONG OutputStride = min( Stride, (ULONG) m_OutputStride ); + ULONG OutputSize = min( Size, m_Length ); + ULONG Limit = min( m_Height, OutputSize/Stride ); + + // Is the row order in the bitmap inverted? + if( m_FlipVertical ) + { + PUCHAR Start = Buffer; + for( ULONG y=Limit; y; y-- ) + { + // Inverted row copy, with possible stride, width or height difference. + RtlCopyMemory( Buffer, GetImageLocation( 0, y-1 ), OutputStride ); + Buffer += Stride; + } + return (ULONG) (Buffer - Start); + } + else + { + PUCHAR Start = Buffer; + + if( Limit * OutputStride == OutputSize ) + { + // 1:1 copy case. (Path normally taken, so fastest.) + RtlCopyMemory( Buffer, m_Buffer, OutputSize ); + return OutputSize; + } + else + { + for( ULONG y=0; y> 24 )) + +BOOLEAN +CXRGBSynthesizer:: +Initialize() +/*++ + +Routine Description: + + Class initialization. + + Used to pre-initialize a bitmap that contains a gradient bar that starts + with one of our rendering colors and fades to black. Each row will contain + the gradient for a single starting color. To paint a gradient bar, simply + replicate the row as many times as necessary. + + This front-loads all gradient calculations to initialization and caches the + result, allowing us to render a gradient bar in the time it takes to copy + the pixels even on a relatively slow machine. + +Arguments: + + none + +Return Value: + + TRUE - success. + +--*/ +{ + PAGED_CODE(); + + // First, do the base class initialization. + BOOLEAN Status = CSynthesizer::Initialize(); + + if( Status ) + { + // Now initialize the Gradient bmp... + CKsRgbQuad *Bmp = m_GradientBmp; + + for( ULONG color=BLACK; color>1; // round, don't truncate. + ULONG B = TO_Q24_8bit_to_32bit(m_Colors[color][0])+HALF; + ULONG G = TO_Q24_8bit_to_32bit(m_Colors[color][1])+HALF; + ULONG R = TO_Q24_8bit_to_32bit(m_Colors[color][2])+HALF; + ULONG BDelta = B-TO_Q24_8bit_to_32bit(m_Colors[BLACK][0]); + ULONG GDelta = G-TO_Q24_8bit_to_32bit(m_Colors[BLACK][1]); + ULONG RDelta = R-TO_Q24_8bit_to_32bit(m_Colors[BLACK][2]); + + BDelta /= (ULONG) m_Width; + GDelta /= (ULONG) m_Width; + RDelta /= (ULONG) m_Width; + + for(ULONG i = 0; i < m_Width; i++) + { + *Bmp++ = CKsRgbQuad( + FROM_Q24_32bit_to_8bit(R), + FROM_Q24_32bit_to_8bit(G), + FROM_Q24_32bit_to_8bit(B) + ); + B -= BDelta; + G -= GDelta; + R -= RDelta; + } + } + } + return Status; +} diff --git a/avscamera/sys/XRGBSynthesizer.h b/avscamera/sys/XRGBSynthesizer.h new file mode 100644 index 000000000..5e7ddd460 --- /dev/null +++ b/avscamera/sys/XRGBSynthesizer.h @@ -0,0 +1,97 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + XRGBSynthesizer.h + + Abstract: + + This file contains the definition of CXRGBSynthesizer. + + CXRGBSynthesizer is derived from CSynthesizer. It uses the RGB + color space and defines a Commit() function. XRGB is known also + known as RGB32. It uses 8 bits per primary plus 8 bits of padding + for 4 bytes per pixel in a single plane. + + History: + + created 4/14/2014 + +**************************************************************************/ + +/************************************************* + + CXRGBSynthesizer + + Image synthesizer for XRGB (aka RGB32) format. + +*************************************************/ + +class CXRGBSynthesizer + : public CSynthesizer +{ +protected: + BOOLEAN m_FlipVertical; + +public: + // + // DEFAULT CONSTRUCTOR: + // + CXRGBSynthesizer ( + LONG Width=0, + LONG Height=0 + ) : + CSynthesizer("RGB32", CHANNEL_RGB, Width, ABS(Height)) + , m_FlipVertical(Height==ABS(Height)) + { + m_OutputStride = Width * sizeof(KS_RGBQUAD); + } + + // + // DESTRUCTOR: + // + virtual + ~CXRGBSynthesizer () + {} + + // + // Commit + // + // Copy (and reformat, if necessary) pixels from the internal scratch + // buffer. If the output format decimates chrominance, do it here. + // + virtual + _Success_(return > 0) + ULONG + Commit( + _Out_writes_bytes_(Size) + PUCHAR Buffer, + _In_ ULONG Size, + _In_ ULONG Stride + ); + + // + // Initialize() + // + // Initialize the Gradient bmp for RGB color space. + // + virtual + BOOLEAN + Initialize(); + +protected: + // + // GetPalette + // + // Get a pointer to an array of palette colors. Used mostly to handle + // different color primaries. Location of the primary must agree with + // Commit(). + virtual + const UCHAR4 * + GetPalette(); +}; + diff --git a/avscamera/sys/YUVSynthesizer.cpp b/avscamera/sys/YUVSynthesizer.cpp new file mode 100644 index 000000000..41a942ee1 --- /dev/null +++ b/avscamera/sys/YUVSynthesizer.cpp @@ -0,0 +1,149 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + YUVSynthesizer.cpp + + Abstract: + + This file contains the implementation of CYUVSynthesizer. + + A Base image synthesizer for all YUV formats. It provides a YUV color + palette and sets up cached gradient bars. + + History: + + created 4/14/2014 + +**************************************************************************/ + +#include "Common.h" + +/************************************************************************** + + PAGED CODE + +**************************************************************************/ + +#ifdef ALLOC_PRAGMA +#pragma code_seg("PAGE") +#endif // ALLOC_PRAGMA + +const UCHAR4 * +CYUVSynthesizer:: +GetPalette() +/*++ + +Routine Description: + + Get a pointer to an array of palette colors. Used mostly to handle + different color primaries. Location of the primary must agree with + Commit(). + +Arguments: + + none + +Return Value: + + A pointer to an array of color primaries used for rendering. + +--*/ +{ + PAGED_CODE(); + + static + UCHAR4 Colors [MAX_COLOR] = + { + {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 + }; + + return Colors; +}; + +// Macros used to do fixed-point arithmetic in initialization. +#define TO_Q24_8bit_to_32bit_signed(_x_) ( ( ( LONG) (_x_) ) << 24 ) +#define FROM_Q24_32bit_to_8bit_signed(_x_) (( CHAR) (( ( LONG) (_x_) ) >> 24 )) +#define TO_Q24_8bit_to_32bit_unsigned(_x_) ( ( (ULONG) (_x_) ) << 24 ) +#define FROM_Q24_32bit_to_8bit_unsigned(_x_) ((UCHAR) (( (ULONG) (_x_) ) >> 24 )) + +BOOLEAN +CYUVSynthesizer:: +Initialize() +/*++ + +Routine Description: + + Class initialization. + + Used to pre-initialize a bitmap that contains a gradient bar that starts + with one of our rendering colors and fades to black. Each row will contain + the gradient for a single starting color. To paint a gradient bar, simply + replicate the row as many times as necessary. + + This front-loads all gradient calculations to initialization and caches the + result, allowing us to render a gradient bar in the time it takes to copy + the pixels even on a relatively slow machine. + +Arguments: + + none + +Return Value: + + TRUE - success. + +--*/ +{ + PAGED_CODE(); + + // First, do the base class initialization. + BOOLEAN Status = CSynthesizer::Initialize(); + + if( Status ) + { + // Now initialize the Gradient bmp... + CKsRgbQuad *Bmp = m_GradientBmp; + // {54, 145, 34}, // GREEN to {128, 16, 128}, // BLACK + for( ULONG color=BLACK; color>1; // round, don't truncate. + LONG U = TO_Q24_8bit_to_32bit_signed (m_Colors[color][0])+HALF; + ULONG Y = TO_Q24_8bit_to_32bit_unsigned(m_Colors[color][1])+HALF; + LONG V = TO_Q24_8bit_to_32bit_signed (m_Colors[color][2])+HALF; + LONG UDelta = U-TO_Q24_8bit_to_32bit_signed (m_Colors[BLACK][0]); + ULONG YDelta = Y-TO_Q24_8bit_to_32bit_unsigned(m_Colors[BLACK][1]); + LONG VDelta = V-TO_Q24_8bit_to_32bit_signed (m_Colors[BLACK][2]); + + UDelta /= (LONG) m_Width; + YDelta /= (LONG) m_Width; + VDelta /= (LONG) m_Width; + + for(ULONG i = 0; i < m_Width; i++) + { + *Bmp++ = CKsRgbQuad( + FROM_Q24_32bit_to_8bit_signed (V), + FROM_Q24_32bit_to_8bit_unsigned(Y), + FROM_Q24_32bit_to_8bit_signed (U) + ); + U -= UDelta; + Y -= YDelta; + V -= VDelta; + } + } + } + return Status; +} diff --git a/avscamera/sys/YUVSynthesizer.h b/avscamera/sys/YUVSynthesizer.h new file mode 100644 index 000000000..819139e48 --- /dev/null +++ b/avscamera/sys/YUVSynthesizer.h @@ -0,0 +1,69 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + YUVSynthesizer.h + + Abstract: + + This file contains the definition of CYUVSynthesizer. + + A Base image synthesizer for all YUV formats. It provides a YUV color + palette and sets up cached gradient bars. + + History: + + created 4/14/2014 + +**************************************************************************/ + +#pragma once + +/************************************************* + + CYUVSynthesizer + + Image synthesizer for YUV format. + +*************************************************/ + + +class CYUVSynthesizer : + public CSynthesizer +{ +protected: + // + // GetPalette + // + // Get a pointer to an array of palette colors. Used mostly to handle + // different color primaries. Location of the primary must agree with + // Commit(). + virtual + const UCHAR4 * + GetPalette(); + + // + // Initialize() + // + // Initialize the Gradient bmp for YUV color space. + // + virtual + BOOLEAN + Initialize(); + +public: + + //Default constructor + CYUVSynthesizer ( + PCCHAR Name="[Something YUV]", + ULONG Width=0, + ULONG Height=0 + ) + : CSynthesizer(Name, CHANNEL_YCrCb, Width, Height) + {} +}; + diff --git a/avscamera/sys/YUY2Synthesizer.cpp b/avscamera/sys/YUY2Synthesizer.cpp new file mode 100644 index 000000000..14a762620 --- /dev/null +++ b/avscamera/sys/YUY2Synthesizer.cpp @@ -0,0 +1,137 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + YUVSynthesizer.cpp + + Abstract: + + This file contains the implementation of CYUY2Synthesizer. + + CYUY2Synthesizer is derived from CYUVSynthesizer. An image + synthesizer for YUY2 format. It uses the YUV color space and defines + a Commit() function. + + YUY2 is a 4:2:2 format that uses a single plane that decimates chroma + samples on the x axis with an effective bit rate of 16 bits per pixel. + A plane is an array of rows. Rows consist of pairs of pixels in the + sequence: Y0|U|Y1|V. + + The synthesizer stores the YUV information in 32 bit format internally + for speed and accuracy. The Commit function does the format conversion. + + History: + + created 4/14/2014 + +**************************************************************************/ + +#include "Common.h" + +/************************************************************************** + + PAGED CODE + +**************************************************************************/ + +#ifdef ALLOC_PRAGMA +#pragma code_seg("PAGE") +#endif // ALLOC_PRAGMA + +// suppressed due to Esp:773 +#pragma warning (push) +#pragma warning( disable:26015 ) // Suppress OACR error. Seems nonsensical. TODO: Must revisit. +#pragma warning( disable:26019 ) +_Success_(return > 0) +ULONG +CYUY2Synthesizer:: +Commit( + _Out_writes_bytes_(Size) + PUCHAR Buffer, + _In_ ULONG Size, + _In_ ULONG Stride +) +/*++ + +Routine Description: + + Copy (and reformat, if necessary) pixels from the internal scratch + buffer. If the output format decimates chrominance, do it here. + +Arguments: + + Buffer - + The output buffer to fill. + + Size - + The size of the output buffer in bytes. + + Stride - + The length of a row in bytes. + +Return Value: + + Number of bytes copied into Buffer. + +--*/ +{ + PAGED_CODE(); + + NT_ASSERT( Buffer ); + NT_ASSERT( m_Buffer ); + NT_ASSERT( Size >= Stride * m_Height ); + NT_ASSERT( (Stride&3)==0 ); + NT_ASSERT( (m_Width&1) == 0 ); + NT_ASSERT( (m_Height&1) == 0 ); + NT_ASSERT( m_Width>1 && m_Height>1 ); + NT_ASSERT( (Size&1)==0 ); + + // In most cases, stride isn't initialized. + // If so, just assume its the Width. + if( Stride==0 ) + { + Stride = m_OutputStride; + } + + if( !( Buffer && + m_Buffer && + Size >= Stride * m_Height && + (Stride&3)==0 && + (m_Width&1) == 0 && + (m_Height&1) == 0 && + m_Width>1 && m_Height>1 && + (Size&1)==0 + ) ) + { + // Wipe the destination before returning failure. + return 0; + } + + PKS_RGBQUAD pSrc = (PKS_RGBQUAD) m_Buffer; + ULONG RowLimit = min( m_Height, Size / Stride ); + ULONG ColLimit = min( m_Width, Stride/sizeof(WORD) ) & ~0x1; + + for(ULONG row = 0; row < RowLimit; row++) + { + PDWORD pY = (PDWORD) &Buffer[ row * Stride ]; + + for(ULONG col = 0; col < ColLimit; col+=2) + { + KS_RGBQUAD L = pSrc[col+0]; + KS_RGBQUAD R = pSrc[col+1]; + + *pY++ = + MAKELONG( MAKEWORD( L.rgbGreen, (ULONG(L.rgbBlue) + R.rgbBlue)/2), + MAKEWORD( R.rgbGreen, (ULONG(L.rgbRed ) + R.rgbRed )/2) ); + } + pSrc = (PKS_RGBQUAD) (((PUCHAR) pSrc) + m_SynthesisStride); + } + + return RowLimit * Stride; +} +// suppressed due to Esp:773 +#pragma warning (pop) diff --git a/avscamera/sys/YUY2Synthesizer.h b/avscamera/sys/YUY2Synthesizer.h new file mode 100644 index 000000000..a304fb43a --- /dev/null +++ b/avscamera/sys/YUY2Synthesizer.h @@ -0,0 +1,68 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2014, Microsoft Corporation. + + File: + + YUY2Synthesizer.h + + Abstract: + + This file contains the definition of CYUY2Synthesizer. + + CYUY2Synthesizer is derived from CYUVSynthesizer. An image + synthesizer for YUY2 format. It uses the YUV color space and defines + a Commit() function. + + YUY2 is a 4:2:2 format that uses a single plane that decimates chroma + samples on the x axis with an effective bit rate of 16 bits per pixel. + A plane is an array of rows. Rows consist of pairs of pixels in the + sequence: Y0|U|Y1|V. + + The synthesizer stores the YUV information in 32 bit format internally + for speed, accuracy and simplicity. The Commit function does the + format conversion. + + History: + + created 4/14/2014 + +**************************************************************************/ + +class CYUY2Synthesizer : public CYUVSynthesizer +{ +public: + // + // DEFAULT CONSTRUCTOR + // + CYUY2Synthesizer ( + ULONG Width=0, + ULONG Height=0 + ) : + CYUVSynthesizer("YUY2", Width, Height) + { + NT_ASSERT( ( Width % 2 ) == 0 ); + m_OutputStride = Width * sizeof(WORD); + } + + // + // Commit + // + // Copy (and reformat, if necessary) pixels from the internal scratch + // buffer. If the output format decimates chrominance, do it here. + // + virtual + _Success_(return > 0) + ULONG + Commit( + _Out_writes_bytes_(Size) + PUCHAR Buffer, + _In_ ULONG Size, + _In_ ULONG Stride + ); + +}; + + diff --git a/avscamera/sys/avscamera.inf b/avscamera/sys/avscamera.inf new file mode 100644 index 0000000000000000000000000000000000000000..b5a29ce91143d9378497374403cb63fbf2be5759 GIT binary patch literal 26788 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 WIMIVev?HJE<3nuuSrB#{2>$~Woyuka literal 0 HcmV?d00001 diff --git a/avscamera/sys/filter.cpp b/avscamera/sys/filter.cpp new file mode 100644 index 000000000..f55c31274 --- /dev/null +++ b/avscamera/sys/filter.cpp @@ -0,0 +1,4039 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2001, Microsoft Corporation. + + File: + + filter.cpp + + Abstract: + + This file contains the filter level implementation for the base capture + filter class. It provides a wrapper on KSFILTER. + + CCaptureFilter holds state information for the filter and contains + implementations for standard camera controls. These implementations + validate arguments and capabilities of the control according to DDI + definitions. Once validated, control is passes to a CSensor object + to manipulate the state of camera. + + To add a new control, derive from CCaptureFilter and use one of the + DECLARE_PROPERTY_XXX() macros below to declare the property handlers. + You can also use the DEFINE_PROP_ITEM_XXX() and DEFINE_STD_EVENT_ITEM() + macros as aids in constructing a filter's automation table. + + History: + + created 3/12/2001 + +**************************************************************************/ + +#include "Common.h" + +/************************************************************************** + + PAGEABLE CODE + +**************************************************************************/ + +#ifdef ALLOC_PRAGMA +#pragma code_seg("PAGE") +#endif // ALLOC_PRAGMA + +CCaptureFilter::CCaptureFilter ( + _In_ PKSFILTER Filter +): +/*++ + +Routine Description: + + CCaptureFilter object construction. + +Arguments: + + Filter - + The AVStream filter being wrapped. + +Return Value: + + None + +--*/ + m_pCaptureDevice(NULL), + m_pPerFrameSettings(nullptr), + m_pinArray(nullptr), + m_PhotoModeNotifier( Filter, &KSEVENTSETID_ExtendedCameraControl, KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOMODE ), + m_PhotoMaxFrameRateNotifier( Filter, &KSEVENTSETID_ExtendedCameraControl, KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOMAXFRAMERATE) , + m_FocusNotifier( Filter, &KSEVENTSETID_ExtendedCameraControl, KSPROPERTY_CAMERACONTROL_EXTENDED_FOCUSMODE ), + m_FocusRectNotifier( Filter, &EVENTSETID_VIDCAP_CAMERACONTROL_REGION_OF_INTEREST, KSPROPERTY_CAMERACONTROL_REGION_OF_INTEREST_PROPERTY_ID ), + m_IsoNotifier( Filter, &KSEVENTSETID_ExtendedCameraControl, KSPROPERTY_CAMERACONTROL_EXTENDED_ISO ), + m_IsoAdvancedNotifier( Filter, &KSEVENTSETID_ExtendedCameraControl, KSPROPERTY_CAMERACONTROL_EXTENDED_ISO_ADVANCED ), + m_EvCompensationNotifier( Filter, &KSEVENTSETID_ExtendedCameraControl, KSPROPERTY_CAMERACONTROL_EXTENDED_EVCOMPENSATION ), + m_WhiteBalanceNotifier( Filter, &KSEVENTSETID_ExtendedCameraControl, KSPROPERTY_CAMERACONTROL_EXTENDED_WHITEBALANCEMODE ), + m_ExposureNotifier( Filter, &KSEVENTSETID_ExtendedCameraControl, KSPROPERTY_CAMERACONTROL_EXTENDED_EXPOSUREMODE ), + m_SceneModeNotifier( Filter, &KSEVENTSETID_ExtendedCameraControl, KSPROPERTY_CAMERACONTROL_EXTENDED_SCENEMODE ), + m_ThumbnailNotifier( Filter, &KSEVENTSETID_ExtendedCameraControl, KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOTHUMBNAIL ), + m_WarmStartNotifier( Filter, &KSEVENTSETID_ExtendedCameraControl, KSPROPERTY_CAMERACONTROL_EXTENDED_WARMSTART ), + m_RoiNotifier( Filter, &KSEVENTSETID_ExtendedCameraControl, KSPROPERTY_CAMERACONTROL_EXTENDED_ROI_ISPCONTROL ), + m_ProfileNotifier( Filter, &KSEVENTSETID_ExtendedCameraControl, KSPROPERTY_CAMERACONTROL_EXTENDED_PROFILE ) +{ + + PAGED_CODE(); + + DBG_ENTER("(Filter=%p)", Filter); + + m_pKSFilter = Filter; + + m_pCaptureDevice = CCaptureDevice::Recast(KsFilterGetDevice(Filter)); + + KeQuerySystemTime (&m_StartTime); + + m_Profile.ProfileId = KSCAMERAPROFILE_Legacy; + m_Profile.Index = 0; + m_Profile.Reserved = 0; + + DBG_ENTER("(Filter=%p)", Filter); +} + +NTSTATUS +CCaptureFilter:: +Initialize() +/*++ + +Routine Description: + + Post construction initialization. + +Arguments: + + [None] + +Return Value: + + Success / failure + +--*/ +{ + + PAGED_CODE(); + + NTSTATUS Status = STATUS_SUCCESS; + + DBG_ENTER("(Filter=%S)", GetFilterName()); + + // Get Sensor object from our parent device object. + + m_Sensor = m_pCaptureDevice->GetSensor(m_pKSFilter); + NT_ASSERT(m_Sensor); + + // We get a nullptr back if the Filter doesn't match one of our's. + if( !m_Sensor ) + { + Status = STATUS_INVALID_PARAMETER; + goto done; + } + + // In reality we're just adding a reference to the sensor for this filter. + // The sensor has no back pointer to the filter since more than one filter + // can be open at a time. + IFFAILED_EXIT( m_Sensor->AddFilter(m_pKSFilter) ); + + IFNULL_EXIT(m_pinArray = new (PagedPool, 'sniP') CCapturePin *[m_Sensor->GetPinCount()]); + + for( ULONG i=0; iGetPinCount(); i++ ) + { + m_pinArray[i] = nullptr; + } + +done: + DBG_LEAVE("(Filter=%S)=0x%08X", GetFilterName(), Status); + return Status; +} + +void +CCaptureFilter::Cleanup() +/*++ + +Routine Description: + + Clean up. Cancels any outstanding operations on the sensor. + +Arguments: + + [None] + +Return Value: + + [None] + +--*/ +{ + PAGED_CODE(); + + // Cancel all Async operations. Ignore any failures. + m_Sensor->CancelPhotoMode(); + m_Sensor->CancelPhotoMaxFrameRate(); + m_Sensor->CancelFocus(); + m_Sensor->CancelFocusRect(); + m_Sensor->CancelIso(); + m_Sensor->CancelIsoAdvanced(); + m_Sensor->CancelEvCompensation(); + m_Sensor->CancelWhiteBalance(); + m_Sensor->CancelExposure(); + m_Sensor->CancelSceneMode(); + m_Sensor->CancelThumbnail(); + m_Sensor->CancelWarmStart(); + m_Sensor->CancelRoi(); +} + +// +// ~CCaptureFilter(): +// +// The capture filter destructor. +// +CCaptureFilter::~CCaptureFilter() +{ + PAGED_CODE(); + + LPCWSTR Name = GetFilterName(); + DBG_ENTER("(%S)", Name); + + delete [] ((PBYTE) m_pPerFrameSettings); + + // In reality we're just removing a reference to the sensor for this filter. + m_Sensor->RemoveFilter(m_pKSFilter); + + // The sensor may need to do some cleanup. In particular, the image pin + // may need to have some things reset. + m_Sensor->Reset(); + + delete [] m_pinArray; + + DBG_LEAVE("(%S)", Name); +} + + +NTSTATUS +CCaptureFilter:: +DispatchCreate ( + _In_ PKSFILTER Filter, + _In_ PIRP Irp +) +/*++ + +Routine Description: + + Static function for creation dispatch for the capture filter. It creates + the CCaptureFilter object. + +Arguments: + + Filter - + The AVStream filter being created + + Irp - + The creation Irp + +Return Value: + + Success / failure + +--*/ + +{ + PAGED_CODE(); + + DBG_ENTER("(Filter=0x%p)", Filter); + + NTSTATUS Status = STATUS_SUCCESS; + + CCaptureFilter *CapFilter = new (NonPagedPoolNx, 'rtlf') CCaptureFilter (Filter); + + if (!CapFilter) + { + // + // Return failure if we couldn't create the filter. + // + Status = STATUS_INSUFFICIENT_RESOURCES; + + } + else + { + // Allocate memory or whatever. + Status = CapFilter->Initialize(); + + if (!NT_SUCCESS (Status)) + { + delete CapFilter; + } + else + { + Filter -> Context = reinterpret_cast (CapFilter); + } + + } + + DBG_LEAVE( "()=0x%08X", Status ); + return Status; +} + +NTSTATUS +CCaptureFilter::DispatchClose( + _In_ PKSFILTER Filter, + _In_ PIRP Irp +) +/*++ + +Routine Description: + + Static function for the close dispatch for the capture filter. It handles + filter cleanup. + +Arguments: + + Filter - + The AVStream filter being closed + + Irp - + The creation Irp + +Return Value: + + Success / failure + +--*/ +{ + PAGED_CODE(); + + CCaptureFilter *pFilter = reinterpret_cast (Filter->Context); + + if( pFilter ) + { + pFilter->Cleanup(); + delete pFilter; + } + return STATUS_SUCCESS; +} + +void +CCaptureFilter:: +setPin( + _In_ CCapturePin *pPin, + _In_ unsigned Id +) +/*++ + +Routine Description: + + Attach a CCapturePin to the filter. + +Arguments: + + pPin - + The pin. + Id - + The pin index. + +Return Value: + + Success / failure + +--*/ +{ + PAGED_CODE( ); + + if( Id >= m_Sensor->GetPinCount() ) + { + __debugbreak(); + return; + } + + // Take out a weak reference to the pin object. + m_pinArray[Id] = pPin; +} + +CCapturePin * +CCaptureFilter:: +getPin( + _In_ unsigned Id +) +/*++ + +Routine Description: + + Get a pointer to an attached CCapturePin. + +Arguments: + + Id - + The pin index. + +Return Value: + + Success / failure + +--*/ +{ + PAGED_CODE( ); + + if( Id >= m_Sensor->GetPinCount() ) + { + __debugbreak(); + return nullptr; + } + + // Convert back to a strong reference before returning. + return m_pinArray[Id] ; +} + +NTSTATUS +CCaptureFilter:: +UpdateAllocatorFraming( + _In_ ULONG PinId +) +/*++ + +Routine Description: + + Update the Pin's Allocator Framing. + +Arguments: + + PinId - + A pin's index + +Return Value: + + Success / failure + +--*/ +{ + PAGED_CODE(); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + // Acquire the lock and update the Pin's allocator framing. + LockFilter Lock(m_pKSFilter); + + if( m_pinArray[PinId] ) + { + Status = m_pinArray[PinId]->UpdateAllocatorFraming(); + } + + DBG_LEAVE("()=0x%08X",Status); + return Status; +} + +/************************************************************************** + + Control validation and forwarding. + +**************************************************************************/ + +// Get for KSPROPERTY_CAMERACONTROL_FLASH_PROPERTY_ID +NTSTATUS +CCaptureFilter:: +GetFlash( + _Inout_ KSPROPERTY_CAMERACONTROL_FLASH_S *pFlash +) +{ + PAGED_CODE(); + + return + m_Sensor->GetFlash( pFlash ); +} + +// Set for KSPROPERTY_CAMERACONTROL_FLASH_PROPERTY_ID +NTSTATUS +CCaptureFilter:: +SetFlash( + _In_ KSPROPERTY_CAMERACONTROL_FLASH_S *pFlash +) +{ + PAGED_CODE(); + + KSPROPERTY_CAMERACONTROL_FLASH_S Caps; + + RtlZeroMemory( &Caps, sizeof(Caps) ); + NTSTATUS Status = m_Sensor->GetFlash(&Caps); + + if( NT_SUCCESS(Status) ) + { + // Assume an invalid parameter. + Status = STATUS_INVALID_PARAMETER; + + // Validate the parameters against our caps. + switch( pFlash->Flash ) + { + case KSPROPERTY_CAMERACONTROL_FLASH_OFF: + case KSPROPERTY_CAMERACONTROL_FLASH_ON: + if( Caps.Capabilities & KSPROPERTY_CAMERACONTROL_FLASH_FLAGS_MANUAL ) + { + Status = STATUS_SUCCESS; + } + break; + + case KSPROPERTY_CAMERACONTROL_FLASH_AUTO: + if( Caps.Capabilities & KSPROPERTY_CAMERACONTROL_FLASH_FLAGS_AUTO ) + { + Status = STATUS_SUCCESS; + } + break; + } + } + if( NT_SUCCESS(Status) ) + { + Status = m_Sensor->SetFlash( pFlash ); + } + + return Status; +} + +// Get for PROPSETID_VIDCAP_CAMERACONTROL_VIDEO_STABILIZATION:0 +NTSTATUS +CCaptureFilter:: +GetVideoStabMode( + _Inout_ KSPROPERTY_CAMERACONTROL_VIDEOSTABILIZATION_MODE_S *pVideoStab +) +{ + PAGED_CODE(); + + return + m_Sensor->GetVideoStabMode( pVideoStab ); +} + +// Set for PROPSETID_VIDCAP_CAMERACONTROL_VIDEO_STABILIZATION:0 +NTSTATUS +CCaptureFilter:: +SetVideoStabMode( + _In_ KSPROPERTY_CAMERACONTROL_VIDEOSTABILIZATION_MODE_S *pVideoStab +) +{ + PAGED_CODE(); + + KSPROPERTY_CAMERACONTROL_VIDEOSTABILIZATION_MODE_S Caps; + + RtlZeroMemory( &Caps, sizeof(Caps) ); + NTSTATUS Status = m_Sensor->GetVideoStabMode( &Caps ); + + DBG_ENTER("(Mode=0x%08X, Caps=0x%08X)", pVideoStab->VideoStabilizationMode, Caps.Capabilities ); + + if( NT_SUCCESS(Status) ) + { + Status = STATUS_INVALID_PARAMETER; + switch( pVideoStab->VideoStabilizationMode ) + { + case KSPROPERTY_CAMERACONTROL_VIDEOSTABILIZATION_MODE_OFF: + Status = STATUS_SUCCESS; + break; + + case KSPROPERTY_CAMERACONTROL_VIDEOSTABILIZATION_MODE_AUTO: + if( Caps.Capabilities & KSPROPERTY_CAMERACONTROL_VIDEOSTABILIZATION_MODE_FLAGS_AUTO ) + { + Status = STATUS_SUCCESS; + } + break; + + case KSPROPERTY_CAMERACONTROL_VIDEOSTABILIZATION_MODE_LOW: + case KSPROPERTY_CAMERACONTROL_VIDEOSTABILIZATION_MODE_MEDIUM: + case KSPROPERTY_CAMERACONTROL_VIDEOSTABILIZATION_MODE_HIGH: + if( Caps.Capabilities & KSPROPERTY_CAMERACONTROL_VIDEOSTABILIZATION_MODE_FLAGS_MANUAL ) + { + Status = STATUS_SUCCESS; + } + break; + } + } + if( NT_SUCCESS(Status) ) + { + Status = m_Sensor->SetVideoStabMode( pVideoStab ); + } + DBG_LEAVE("(Mode=0x%08X, Caps=0x%08X)=0x%08X", pVideoStab->VideoStabilizationMode, Caps.Capabilities, Status ); + return Status; +} + +// Get for KSPROPERTY_CAMERACONTROL_IMAGE_PIN_CAPABILITY_PROPERTY_ID +NTSTATUS +CCaptureFilter:: +GetPinDependence( + _Inout_ KSPROPERTY_CAMERACONTROL_IMAGE_PIN_CAPABILITY_S *pPinDependence +) +{ + PAGED_CODE(); + + return + m_Sensor->GetPinDependence( pPinDependence ); +} + +// +// A macro used for consistent asynchronous SET behavior. +// +#define INVOKE_SET_ASYNC( SENSOR, PROPERTY, OUTPUT, NOTIFIER ) \ + ( OUTPUT->Flags & KSCAMERA_EXTENDEDPROP_FLAG_CANCELOPERATION ) ? SENSOR->Cancel ## PROPERTY () : \ + SENSOR->Set ## PROPERTY ## Async( OUTPUT, &NOTIFIER ); + +#define INVOKE_SET_ASYNC_NOT_CANCELLABLE( SENSOR, PROPERTY, OUTPUT, NOTIFIER ) \ + ( OUTPUT->Flags & KSCAMERA_EXTENDEDPROP_FLAG_CANCELOPERATION ) ? STATUS_INVALID_PARAMETER : \ + SENSOR->Set ## PROPERTY ## Async( OUTPUT, &NOTIFIER ); + +// Get for KSPROPERTY_CAMERACONTROL_REGION_OF_INTEREST_PROPERTY_ID +NTSTATUS +CCaptureFilter::GetFocusRect( + _Inout_ KSPROPERTY_CAMERACONTROL_REGION_OF_INTEREST_S *pRoi +) +{ + PAGED_CODE(); + return m_Sensor->GetFocusRect( pRoi ); +} + +// Set for KSPROPERTY_CAMERACONTROL_REGION_OF_INTEREST_PROPERTY_ID +NTSTATUS +CCaptureFilter:: +SetFocusRect( + _In_ KSPROPERTY_CAMERACONTROL_REGION_OF_INTEREST_S *pRoi +) +{ + PAGED_CODE(); + + return m_Sensor->SetFocusRectAsync( pRoi, &m_FocusRectNotifier ); +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOMODE +NTSTATUS +CCaptureFilter::GetPhotoMode( + _Inout_ CExtendedPhotoMode *pMode +) +{ + PAGED_CODE(); + + DBG_ENTER( "()" ); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( pMode->isValid() && + m_Sensor->IsStillIndex( pMode->PinId ) ) + { + Status = m_Sensor->GetPhotoMode( pMode ); + } + + DBG_TRACE( "Capability=0x%016llX", pMode->Capability ); + DBG_TRACE( "PinId=%d", pMode->PinId ); + DBG_TRACE( "MaxHistoryFrames=%d", pMode->MaxHistoryFrames() ); + + DBG_LEAVE( "()=0x%08X", Status ); + return Status; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOMODE +NTSTATUS +CCaptureFilter:: +SetPhotoMode( + _In_ CExtendedPhotoMode *pMode +) +{ + PAGED_CODE(); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + DBG_ENTER( "()" ); + + if( pMode->isValid() && + m_Sensor->IsStillIndex( pMode->PinId ) && + (pMode->Capability & KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL) ) + { + CExtendedPhotoMode Caps(*pMode); + Status = m_Sensor->GetPhotoMode( &Caps ); + + if( NT_SUCCESS(Status) ) + { + // Assume an invalid parameter again. + Status = STATUS_INVALID_PARAMETER; + + if( (pMode->Flags & Caps.Capability) == pMode->Flags ) + { + // We don't support cancel here. + switch( pMode->Flags ) + { + case KSCAMERA_EXTENDEDPROP_PHOTOMODE_SEQUENCE: + { + // We cache the Per-Frame Settings in the filter as a simplification for the getter. + // But we can also use it here to tell us basic info about the current PFS. + PKSCAMERA_PERFRAMESETTING_HEADER pPFS = m_pPerFrameSettings; + if( pMode->SubMode() == KSCAMERA_EXTENDEDPROP_PHOTOMODE_SEQUENCE_SUB_VARIABLE && + !pPFS ) + { + DBG_TRACE( "VPS requested without Per Frame Settings." ); + break; + } + + // Reject any request for too many history frames. + if( pMode->RequestedHistoryFrames() > Caps.MaxHistoryFrames() ) + { + DBG_TRACE( "Too many history frames requested." ); + break; + } + + DBG_TRACE( "Flags=0x%08X, RequestedHistoryFrames=%d, SubMode=0x%08X", + (ULONG) pMode->Flags, + pMode->RequestedHistoryFrames(), + pMode->SubMode() ); + + // Assume the normal photo sequence case. + // For normal photo sequence, we provide history frames +1 or at least a minimum of 3 frames. + ULONG TotalFrames = max( (Caps.RequestedHistoryFrames() + 1), IMAGE_CAPTURE_PIN_MINIMUM_FRAMES ); + + // Modify TotalFrames for the VPS case. + // For VPS, we need LoopCount * FrameCount; but limit by the minimum(3) and maximum (20) frames. + if( KSCAMERA_EXTENDEDPROP_PHOTOMODE_SEQUENCE_SUB_VARIABLE==pMode->SubMode() ) + { + ULONG LoopCount = max( pPFS->LoopCount, 1 ); + ULONG FrameCount= pPFS->FrameCount; + + TotalFrames = max( IMAGE_CAPTURE_PIN_MINIMUM_FRAMES, Caps.RequestedHistoryFrames() + (LoopCount * FrameCount) ); + ASSERT( TotalFrames <= IMAGE_CAPTURE_PIN_MAXIMUM_FRAMES ); + + DBG_TRACE( "TotalFrames=%d, LoopCount=%d, FrameCount=%d", TotalFrames, LoopCount, FrameCount ); + } + + TotalFrames = min( TotalFrames, IMAGE_CAPTURE_PIN_MAXIMUM_FRAMES ); + DBG_TRACE( "Advertising Framing requirement: %d", TotalFrames ); + + Status = UpdateAllocatorFraming( pMode->PinId ); + break; + } + case KSCAMERA_EXTENDEDPROP_PHOTOMODE_NORMAL: + { + // Set it back to 4 frames. + Status = UpdateAllocatorFraming( pMode->PinId ); + break; + } + } + + // Update the sensor object... + if( NT_SUCCESS(Status) ) + { + Status = m_Sensor->SetPhotoModeAsync( pMode, &m_PhotoModeNotifier ); + } + } + } + } + + DBG_TRACE( "Capability=0x%016llX", pMode->Capability ); + DBG_TRACE( "Flags=0x%016llX", pMode->Flags ); + DBG_TRACE( "PinId=%d", pMode->PinId ); + DBG_TRACE( "MaxHistoryFrames=%d", pMode->MaxHistoryFrames() ); + DBG_TRACE( "RequestedHistoryFrames=%d", pMode->RequestedHistoryFrames() ); + DBG_TRACE( "SubMode=0x%08X", pMode->SubMode() ); + DBG_LEAVE( "()=0x%08X", Status ); + return Status; +} + +/* + * KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOFRAMERATE Get Handler + */ +NTSTATUS +CCaptureFilter::GetPhotoFrameRate( + _Inout_ CExtendedProperty *pFrameRate +) +{ + PAGED_CODE(); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( pFrameRate->isValid() && + m_Sensor->IsStillIndex(pFrameRate->PinId) ) + { + ULONGLONG TimePerFrame = m_Sensor->GetPhotoFrameRate( ); + if( TimePerFrame > ULONG_MAX ) + { + // Handle very large (slow) frame rates in seconds. + pFrameRate->m_Value.Value.ratio.HighPart = 1; + pFrameRate->m_Value.Value.ratio.LowPart = (ULONG) (TimePerFrame / ONESECOND); + } + else + { + // Handle smaller frame rates as 100ns units. + pFrameRate->m_Value.Value.ratio.HighPart = ONESECOND; + pFrameRate->m_Value.Value.ratio.LowPart = (ULONG) TimePerFrame; + } + Status = STATUS_SUCCESS; + } + + DBG_LEAVE("(PinId=%d, estimated FrameRate=%d/%d)=0x%08X", + pFrameRate->PinId, + pFrameRate->m_Value.Value.ratio.HighPart, + pFrameRate->m_Value.Value.ratio.LowPart, + Status ); + return Status; +} + +/* + * KSPROPERTY_CAMERACONTROL_EXTENDED_MAXVIDFPS_PHOTORES Get Handler + */ +NTSTATUS +CCaptureFilter:: +GetMaxVideoFpsForPhotoRes( + _Inout_ CExtendedMaxVideoFpsForPhotoRes *pFps +) +{ + PAGED_CODE(); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( pFps->isValid() && + pFps->PinId == KSCAMERA_EXTENDEDPROP_FILTERSCOPE && + pFps->Capability == 0 && + pFps->Flags == 0 ) + { + Status = m_Sensor->GetMaxVideoFpsForPhotoRes( pFps ); + } + return Status; +} + +/* + * KSPROPERTY_CAMERACONTROL_EXTENDED_MAXVIDFPS_PHOTORES Set Handler + */ +NTSTATUS +CCaptureFilter::SetMaxVideoFpsForPhotoRes( + _In_ CExtendedMaxVideoFpsForPhotoRes *pFps +) +{ + PAGED_CODE(); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( pFps->isValid() && + pFps->PinId == KSCAMERA_EXTENDEDPROP_FILTERSCOPE && + pFps->Capability == 0 && + pFps->Flags == 0 ) + { + Status = m_Sensor->SetMaxVideoFpsForPhotoRes( pFps ); + } + return Status; +} + +/* + * KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOMAXFRAMERATE Get Handler + */ +NTSTATUS +CCaptureFilter::GetPhotoMaxFrameRate( + _Inout_ CExtendedProperty *pFrameRate +) +{ + PAGED_CODE(); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( pFrameRate->isValid() && + m_Sensor->IsStillIndex(pFrameRate->PinId) ) + { + Status = m_Sensor->GetPhotoMaxFrameRate( pFrameRate ); + } + return Status; +} + +/* + * KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOMAXFRAMERATE Set Handler + */ +NTSTATUS +CCaptureFilter:: +SetPhotoMaxFrameRate( + _In_ CExtendedProperty *pFrameRate +) +{ + PAGED_CODE(); + + DBG_ENTER("(MaxFrameRate = %d/%d)", + pFrameRate->m_Value.Value.ratio.HighPart, + pFrameRate->m_Value.Value.ratio.LowPart ); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( pFrameRate->isValid() && + m_Sensor->IsStillIndex( pFrameRate->PinId ) && + (pFrameRate->Capability & KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL) ) + { + // Don't allow either the numerator or denominator alone to be 0. + // We don't want a rate of 0 fps or undefined. + if( pFrameRate->m_Value.Value.ll == 0 || + ( pFrameRate->m_Value.Value.ratio.LowPart != 0 && + pFrameRate->m_Value.Value.ratio.HighPart != 0 ) ) + { + // Take what we're given here; SetPhotoFrameRate handles default setting. + DBG_TRACE("[Override] MaxFrameRate = %d/%d", + pFrameRate->m_Value.Value.ratio.HighPart, + pFrameRate->m_Value.Value.ratio.LowPart ); + + Status = m_Sensor->SetPhotoMaxFrameRateAsync( pFrameRate, &m_PhotoMaxFrameRateNotifier ); + } + } + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOTRIGGERTIME +NTSTATUS +CCaptureFilter:: +GetTriggerTime( + _Inout_ CExtendedProperty *pTriggerTime +) +{ + PAGED_CODE(); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( pTriggerTime->isValid() && + m_Sensor->IsStillIndex(pTriggerTime->PinId) ) + { + Status = m_Sensor->GetTriggerTime( pTriggerTime ); + } + return Status; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOTRIGGERTIME +NTSTATUS +CCaptureFilter:: +SetTriggerTime( + _In_ CExtendedProperty *pTriggerTime +) +{ + PAGED_CODE(); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( pTriggerTime->isValid() && + pTriggerTime->Capability == 0 && + m_Sensor->IsStillIndex(pTriggerTime->PinId) ) + { + Status = m_Sensor->SetTriggerTime( pTriggerTime ); + } + return Status; +} + +/* + * KSPROPERTY_CAMERACONTROL_EXTENDED_WARMSTART + */ +NTSTATUS +CCaptureFilter:: +GetWarmStart( + _Inout_ CExtendedProperty *pWarmStart +) +{ + PAGED_CODE(); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( pWarmStart->isValid() && + m_Sensor->IsValidIndex( pWarmStart->PinId ) ) + { + Status = m_Sensor->GetWarmStart( pWarmStart ); + } + DBG_LEAVE("(PinId=%d, Flags=0x%016llX, Cap=0x%016llX)=0x%08X", + pWarmStart->PinId, pWarmStart->Flags, pWarmStart->Capability, Status ); + return Status; +} + +/* + * KSPROPERTY_CAMERACONTROL_EXTENDED_WARMSTART Set Handler + */ +NTSTATUS +CCaptureFilter::SetWarmStart( + _In_ CExtendedProperty *pWarmStart +) +{ + PAGED_CODE(); + DBG_LEAVE("(PinId=%d, Flags=0x%016llX, Cap=0x%016llX)", + pWarmStart->PinId, pWarmStart->Flags, pWarmStart->Capability ); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( pWarmStart->isValid() && + m_Sensor->IsValidIndex( pWarmStart->PinId ) && + (pWarmStart->Capability & KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL) ) + { + CExtendedProperty Caps(*pWarmStart); + Status = m_Sensor->GetWarmStart( &Caps ); + + if( NT_SUCCESS(Status ) ) + { + if( pWarmStart->Flags == (pWarmStart->Flags & Caps.Capability) ) + { + Status = INVOKE_SET_ASYNC( m_Sensor, WarmStart, pWarmStart, m_WarmStartNotifier ); + } + } + else + { + Status = STATUS_INVALID_PARAMETER; + } + } + + DBG_LEAVE("()=0x%08X", Status ); + return Status; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOTHUMBNAIL +NTSTATUS +CCaptureFilter:: +SetThumbnail( + _In_ CExtendedProperty *pThumbnail +) +{ + PAGED_CODE(); + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + DBG_ENTER("()"); + + // Validate + if( (pThumbnail->Capability & KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL) && + pThumbnail->isValid() && + m_Sensor->IsStillIndex(pThumbnail->PinId) ) + { + CExtendedProperty Caps; + Status = m_Sensor->GetThumbnail( &Caps ); + + if( NT_SUCCESS(Status) ) + { + if( pThumbnail->Flags == (Caps.Capability & pThumbnail->Flags) ) + { + Status = INVOKE_SET_ASYNC( m_Sensor, Thumbnail, pThumbnail, m_ThumbnailNotifier ); + } + else + { + Status = STATUS_INVALID_PARAMETER; + } + } + } + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOTHUMBNAIL +NTSTATUS +CCaptureFilter:: +GetThumbnail( + _Inout_ CExtendedProperty *pThumbnail +) +{ + PAGED_CODE(); + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + DBG_ENTER("()"); + + // Validate + if( pThumbnail->isValid() && + m_Sensor->IsStillIndex(pThumbnail->PinId) ) + { + Status = m_Sensor->GetThumbnail( pThumbnail ); + } + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_SCENEMODE +NTSTATUS +CCaptureFilter:: +GetSceneMode( + _Inout_ CExtendedProperty *pSceneMode +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( pSceneMode->isValid() && + pSceneMode->PinId == KSCAMERA_EXTENDEDPROP_FILTERSCOPE ) + { + Status = m_Sensor->GetSceneMode( pSceneMode ); + } + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_SCENEMODE +NTSTATUS +CCaptureFilter:: +SetSceneMode( + _In_ CExtendedProperty *pSceneMode +) +{ + PAGED_CODE(); + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + DBG_ENTER("()"); + + if( (pSceneMode->Capability & KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL) && + pSceneMode->isValid() && + pSceneMode->PinId == KSCAMERA_EXTENDEDPROP_FILTERSCOPE ) + { + CExtendedProperty Caps; + Status = m_Sensor->GetSceneMode( &Caps ); + + if( NT_SUCCESS(Status) ) + { + if( pSceneMode->Flags == (Caps.Capability & pSceneMode->Flags) ) + { + Status = INVOKE_SET_ASYNC_NOT_CANCELLABLE( m_Sensor, SceneMode, pSceneMode, m_SceneModeNotifier ); + } + else + { + Status = STATUS_INVALID_PARAMETER; + } + } + } + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_TORCHMODE +NTSTATUS +CCaptureFilter:: +GetTorchMode( + _Inout_ CExtendedProperty *pTorchMode +) +{ + PAGED_CODE(); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( pTorchMode->isValid() && + pTorchMode->PinId == KSCAMERA_EXTENDEDPROP_FILTERSCOPE ) + { + Status = m_Sensor->GetTorchMode( pTorchMode ); + } + DBG_LEAVE("(PinId=%d, Flags=0x%016llX, Cap=0x%016llX)=0x%08X", + pTorchMode->PinId, pTorchMode->Flags, pTorchMode->Capability, Status ); + return Status; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_TORCHMODE +NTSTATUS +CCaptureFilter:: +SetTorchMode( + _In_ CExtendedProperty *pTorchMode +) +{ + PAGED_CODE(); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( !(pTorchMode->Capability & KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL) && + pTorchMode->isValid() && + pTorchMode->PinId == KSCAMERA_EXTENDEDPROP_FILTERSCOPE && + pTorchMode->m_Value.Value.ul <= 100 ) + { + CExtendedProperty Caps(*pTorchMode); + Status = m_Sensor->GetTorchMode(&Caps); + + if( NT_SUCCESS(Status) ) + { + // Assume an invalid parameter. + Status = STATUS_INVALID_PARAMETER; + + if( pTorchMode->Flags == (pTorchMode->Flags & Caps.Capability) ) + { + switch( pTorchMode->Flags ) + { + case KSCAMERA_EXTENDEDPROP_VIDEOTORCH_OFF: + case KSCAMERA_EXTENDEDPROP_VIDEOTORCH_ON: + case KSCAMERA_EXTENDEDPROP_VIDEOTORCH_ON_ADJUSTABLEPOWER: + Status = m_Sensor->SetTorchMode( pTorchMode ); + break; + } + } + } + } + DBG_LEAVE("(PinId=%d, Flags=0x%016llX, Cap=0x%016llX)=0x%08X", + pTorchMode->PinId, pTorchMode->Flags, pTorchMode->Capability, Status ); + return Status; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_FLASHMODE +NTSTATUS +CCaptureFilter:: +GetExtendedFlash( + _Inout_ CExtendedProperty *pFlash +) +{ + PAGED_CODE(); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( pFlash->isValid() && + pFlash->PinId == KSCAMERA_EXTENDEDPROP_FILTERSCOPE) + { + Status = m_Sensor->GetExtendedFlash( pFlash ); + } + DBG_LEAVE("(PinId=%d, Flags=0x%016llX, Cap=0x%016llX)=0x%08X", + pFlash->PinId, pFlash->Flags, pFlash->Capability, Status ); + return Status; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_FLASHMODE +NTSTATUS +CCaptureFilter:: +SetExtendedFlash( + _In_ CExtendedProperty *pFlash +) +{ + PAGED_CODE(); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( !(pFlash->Capability & KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL) && + pFlash->isValid() && + pFlash->PinId == KSCAMERA_EXTENDEDPROP_FILTERSCOPE) + { + CExtendedProperty Caps; + Status = m_Sensor->GetExtendedFlash( &Caps ); + + if( NT_SUCCESS(Status) ) + { + // Assume an invalid parameter. + Status = STATUS_INVALID_PARAMETER; + + if( pFlash->Flags == (pFlash->Flags & Caps.Capability) ) + { + // Validate flash settings. + // First validate the basic flash controls + switch( pFlash->Flags & KSCAMERA_EXTENDEDPROP_FLASH_MODE_MASK ) + { + // Valid combinations + case KSCAMERA_EXTENDEDPROP_FLASH_OFF: + case KSCAMERA_EXTENDEDPROP_FLASH_ON: + case KSCAMERA_EXTENDEDPROP_FLASH_ON_ADJUSTABLEPOWER: + case KSCAMERA_EXTENDEDPROP_FLASH_AUTO: + case KSCAMERA_EXTENDEDPROP_FLASH_AUTO_ADJUSTABLEPOWER: + Status = STATUS_SUCCESS; + break; + } + + // Validate red eye reduction, single flash & multiflash supported. + if( !(pFlash->Flags & + ( KSCAMERA_EXTENDEDPROP_FLASH_REDEYEREDUCTION | + KSCAMERA_EXTENDEDPROP_FLASH_SINGLEFLASH | + KSCAMERA_EXTENDEDPROP_FLASH_MULTIFLASHSUPPORTED )) || + (pFlash->Flags & KSCAMERA_EXTENDEDPROP_FLASH_MODE_MASK) != KSCAMERA_EXTENDEDPROP_FLASH_OFF ) + { + Status = STATUS_SUCCESS; + } + + // Validate Flash Assistant parameters + switch( pFlash->Flags & KSCAMERA_EXTENDEDPROP_FLASH_ASSISTANT_MASK ) + { + // Valid combinations + case KSCAMERA_EXTENDEDPROP_FLASH_ASSISTANT_OFF: + case KSCAMERA_EXTENDEDPROP_FLASH_ASSISTANT_ON: + case KSCAMERA_EXTENDEDPROP_FLASH_ASSISTANT_AUTO: + Status = STATUS_SUCCESS; + break; + } + } + } + } + + if( NT_SUCCESS(Status) ) + { + Status = m_Sensor->SetExtendedFlash( pFlash ); + } + + DBG_LEAVE("(PinId=%d, Flags=0x%016llX, Cap=0x%016llX)=0x%08X", + pFlash->PinId, pFlash->Flags, pFlash->Capability, Status ); + return Status; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_OPTIMIZATIONHINT +NTSTATUS +CCaptureFilter:: +GetOptimizationHint( + _Inout_ CExtendedProperty *pHint +) +{ + PAGED_CODE(); + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( pHint->isValid() && + !(pHint->Capability & KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL) && + pHint->PinId == KSCAMERA_EXTENDEDPROP_FILTERSCOPE ) + { + Status = m_Sensor->GetOptimizationHint(pHint); + } + DBG_LEAVE("(PinId=%d, Flags=0x%016llX, Cap=0x%016llX)=0x%08X", + pHint->PinId, pHint->Flags, pHint->Capability, Status ); + return Status; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_OPTIMIZATIONHINT +NTSTATUS +CCaptureFilter:: +SetOptimizationHint( + _In_ CExtendedProperty *pHint +) +{ + PAGED_CODE(); + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( pHint->isValid() && + !(pHint->Capability & KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL) && + pHint->PinId == KSCAMERA_EXTENDEDPROP_FILTERSCOPE ) + { + CExtendedProperty Caps(*pHint); + Status = m_Sensor->GetOptimizationHint(&Caps); + + if( NT_SUCCESS(Status) ) + { + // Assume an invalid parameter. + Status = STATUS_INVALID_PARAMETER; + + if( pHint->Flags == (pHint->Flags & Caps.Capability) ) + { + // Inherit the previous primary use flag settings, if they were not set here. + if( KSCAMERA_EXTENDEDPROP_OPTIMIZATION_DEFAULT == pHint->Flags || + 0 == (pHint->Flags & KSCAMERA_EXTENDEDPROP_OPTIMIZATION_PRIMARYUSE_MASK) ) + { + pHint->Flags |= (Caps.Flags & KSCAMERA_EXTENDEDPROP_OPTIMIZATION_PRIMARYUSE_MASK); + } + else + { + // Inherit the previous perf flag settings, if they were not set here and if DEFAULT is not set. + if( 0 == (pHint->Flags & KSCAMERA_EXTENDEDPROP_OPTIMIZATION_PERF_MASK) ) + { + pHint->Flags |= (Caps.Flags & KSCAMERA_EXTENDEDPROP_OPTIMIZATION_PERF_MASK); + } + } + + // Make sure the requested flags are a valid capability combination. + switch( pHint->Flags ) + { + case KSCAMERA_EXTENDEDPROP_OPTIMIZATION_PHOTO: + case KSCAMERA_EXTENDEDPROP_OPTIMIZATION_PHOTO | KSCAMERA_EXTENDEDPROP_OPTIMIZATION_QUALITY: + case KSCAMERA_EXTENDEDPROP_OPTIMIZATION_PHOTO | KSCAMERA_EXTENDEDPROP_OPTIMIZATION_LATENCY: + case KSCAMERA_EXTENDEDPROP_OPTIMIZATION_PHOTO | KSCAMERA_EXTENDEDPROP_OPTIMIZATION_POWER : + case KSCAMERA_EXTENDEDPROP_OPTIMIZATION_VIDEO: + case KSCAMERA_EXTENDEDPROP_OPTIMIZATION_VIDEO | KSCAMERA_EXTENDEDPROP_OPTIMIZATION_QUALITY: + case KSCAMERA_EXTENDEDPROP_OPTIMIZATION_VIDEO | KSCAMERA_EXTENDEDPROP_OPTIMIZATION_LATENCY: + case KSCAMERA_EXTENDEDPROP_OPTIMIZATION_VIDEO | KSCAMERA_EXTENDEDPROP_OPTIMIZATION_POWER : + case KSCAMERA_EXTENDEDPROP_OPTIMIZATION_PHOTO | + KSCAMERA_EXTENDEDPROP_OPTIMIZATION_QUALITY | + KSCAMERA_EXTENDEDPROP_OPTIMIZATION_LATENCY : + case KSCAMERA_EXTENDEDPROP_OPTIMIZATION_PHOTO | + KSCAMERA_EXTENDEDPROP_OPTIMIZATION_QUALITY | + KSCAMERA_EXTENDEDPROP_OPTIMIZATION_POWER : + case KSCAMERA_EXTENDEDPROP_OPTIMIZATION_PHOTO | + KSCAMERA_EXTENDEDPROP_OPTIMIZATION_LATENCY | + KSCAMERA_EXTENDEDPROP_OPTIMIZATION_POWER : + case KSCAMERA_EXTENDEDPROP_OPTIMIZATION_VIDEO | + KSCAMERA_EXTENDEDPROP_OPTIMIZATION_QUALITY | + KSCAMERA_EXTENDEDPROP_OPTIMIZATION_LATENCY : + case KSCAMERA_EXTENDEDPROP_OPTIMIZATION_VIDEO | + KSCAMERA_EXTENDEDPROP_OPTIMIZATION_QUALITY | + KSCAMERA_EXTENDEDPROP_OPTIMIZATION_POWER : + case KSCAMERA_EXTENDEDPROP_OPTIMIZATION_VIDEO | + KSCAMERA_EXTENDEDPROP_OPTIMIZATION_LATENCY | + KSCAMERA_EXTENDEDPROP_OPTIMIZATION_POWER : + Status = m_Sensor->SetOptimizationHint(pHint); + break; + } + } + } + } + DBG_LEAVE("(PinId=%d, Flags=0x%016llX, Cap=0x%016llX)=0x%08X", + pHint->PinId, pHint->Flags, pHint->Capability, Status ); + return Status; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_FIELDOFVIEW +NTSTATUS +CCaptureFilter:: +GetFieldOfView( + _Inout_ CExtendedFieldOfView *pFieldOfView +) +{ + PAGED_CODE(); + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( pFieldOfView->isValid() && + pFieldOfView->PinId == KSCAMERA_EXTENDEDPROP_FILTERSCOPE ) + { + Status = m_Sensor->GetFieldOfView(pFieldOfView); + } + return Status; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_CAMERAANGLEOFFSET +NTSTATUS +CCaptureFilter:: +GetCameraAngleOffset( + _Inout_ CExtendedCameraAngleOffset *pAngleOffset +) +{ + PAGED_CODE(); + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( pAngleOffset->isValid() && + pAngleOffset->PinId == KSCAMERA_EXTENDEDPROP_FILTERSCOPE ) + { + Status = m_Sensor->GetCameraAngleOffset(pAngleOffset); + } + return Status; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_METADATA +NTSTATUS +CCaptureFilter:: +GetMetadata( + _Inout_ CExtendedMetadata *pMetadata +) +{ + PAGED_CODE(); + + if( pMetadata->PinId >= m_Sensor->GetPinCount() ) + { + return STATUS_INVALID_PARAMETER; + } + + return m_Sensor->GetMetadata( pMetadata ); +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_METADATA +NTSTATUS +CCaptureFilter:: +SetMetadata( + _In_ CExtendedMetadata *pMetadata +) +{ + PAGED_CODE(); + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( !(pMetadata->Capability & KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL) && + pMetadata->isValid() && + m_Sensor->IsValidIndex( pMetadata->PinId ) ) + { + if( m_pinArray[pMetadata->PinId] && + m_pinArray[pMetadata->PinId]->GetState() == PinRunning ) + { + Status = STATUS_INVALID_DEVICE_REQUEST; + } + else + { + Status = m_Sensor->SetMetadata( pMetadata ); + } + } + return Status; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_ISO +NTSTATUS +CCaptureFilter:: +GetIso( + _Inout_ CExtendedProperty *pIso +) +{ + PAGED_CODE(); + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( pIso->isValid() && + m_Sensor->IsStillIndex( pIso->PinId ) ) + { + Status = m_Sensor->GetIso(pIso); + } + + DBG_LEAVE("(PinId=%d, Flags=0x%016llX, Cap=0x%016llX)=0x%08X", + pIso->PinId, pIso->Flags, pIso->Capability, Status ); + return Status; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_ISO +NTSTATUS +CCaptureFilter:: +SetIso( + _In_ CExtendedProperty *pIso +) +{ + PAGED_CODE(); + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + DBG_ENTER("(Flags=0x%016llX)", pIso->Flags); + + if( (pIso->Capability & KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL) && + pIso->isValid() && + m_Sensor->IsStillIndex( pIso->PinId ) ) + { + CExtendedProperty Caps; + Status = m_Sensor->GetIso( &Caps ); + + if( NT_SUCCESS(Status) ) + { + // Assume an invalid parameter. + Status = STATUS_INVALID_PARAMETER; + + if( pIso->Flags == (pIso->Flags & Caps.Capability) ) + { + // Make sure only one preset was set. + ULONGLONG presets = pIso->Flags & + ( KSCAMERA_EXTENDEDPROP_ISO_AUTO | + KSCAMERA_EXTENDEDPROP_ISO_50 | KSCAMERA_EXTENDEDPROP_ISO_80 | + KSCAMERA_EXTENDEDPROP_ISO_100 | KSCAMERA_EXTENDEDPROP_ISO_200 | + KSCAMERA_EXTENDEDPROP_ISO_400 | KSCAMERA_EXTENDEDPROP_ISO_800 | + KSCAMERA_EXTENDEDPROP_ISO_1600 | KSCAMERA_EXTENDEDPROP_ISO_3200 | + KSCAMERA_EXTENDEDPROP_ISO_6400 | KSCAMERA_EXTENDEDPROP_ISO_12800 | + KSCAMERA_EXTENDEDPROP_ISO_25600 ) ; + + DBG_TRACE("presets=0x%016llX", presets); + + // Check to see if there is exactly one set. + if( presets && ( (presets-1) & presets ) == 0 ) + { + Status = INVOKE_SET_ASYNC( m_Sensor, Iso, pIso, m_IsoNotifier ); + } + } + } + } + + DBG_LEAVE("(PinId=%d, Flags=0x%016llX, Cap=0x%016llX)=0x%08X", + pIso->PinId, pIso->Flags, pIso->Capability, Status ); + + return Status; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_EVCOMPENSATION +NTSTATUS +CCaptureFilter:: +GetEvCompensation( + _Inout_ CExtendedEvCompensation *pEvComp +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( pEvComp->isValid() && + pEvComp->PinId == KSCAMERA_EXTENDEDPROP_FILTERSCOPE ) + { + Status = m_Sensor->GetEvCompensation( pEvComp ); + } + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_EVCOMPENSATION +NTSTATUS +CCaptureFilter:: +SetEvCompensation( + _In_ CExtendedEvCompensation *pEvComp +) +{ + PAGED_CODE(); + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + DBG_ENTER("()"); + if( (pEvComp->Capability & KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL) && + pEvComp->isValid() && + pEvComp->PinId == KSCAMERA_EXTENDEDPROP_FILTERSCOPE ) + { + CExtendedEvCompensation Caps; + Status = m_Sensor->GetEvCompensation( &Caps ); + + if( NT_SUCCESS(Status) ) + { + // Assume an invalid parameter. + Status = STATUS_INVALID_PARAMETER; + + if( (pEvComp->Flags & Caps.Capability ) == pEvComp->Flags ) + { + Status = INVOKE_SET_ASYNC( m_Sensor, EvCompensation, pEvComp, m_EvCompensationNotifier ); + } + } + } + + DBG_LEAVE("(PinId=%d, Flags=0x%016llX, Cap=0x%016llX)=0x%08X", + pEvComp->PinId, pEvComp->Flags, pEvComp->Capability, Status ); + return Status; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_WHITEBALANCEMODE +NTSTATUS +CCaptureFilter:: +GetWhiteBalance( + _Inout_ CExtendedVidProcSetting *pWhiteBalance +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( pWhiteBalance->isValid() && + pWhiteBalance->PinId == KSCAMERA_EXTENDEDPROP_FILTERSCOPE ) + { + Status = m_Sensor->GetWhiteBalance( pWhiteBalance ); + } + + DBG_LEAVE("(PinId=%d, Flags=0x%016llX, Cap=0x%016llX, Mode=0x%08X, Val=0x%016llX)=0x%08X", + pWhiteBalance->PinId, pWhiteBalance->Flags, pWhiteBalance->Capability, + pWhiteBalance->Mode(), pWhiteBalance->GetLONGLONG(), Status ); + return Status; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_WHITEBALANCEMODE +NTSTATUS +CCaptureFilter:: +SetWhiteBalance( + _In_ CExtendedVidProcSetting *pWhiteBalance +) +{ + PAGED_CODE(); + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + DBG_ENTER("()"); + if( (pWhiteBalance->Capability & KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL) && + pWhiteBalance->isValid() && + pWhiteBalance->PinId == KSCAMERA_EXTENDEDPROP_FILTERSCOPE ) + { + CExtendedVidProcSetting Caps; + Status = m_Sensor->GetWhiteBalance( &Caps ); + + if( NT_SUCCESS(Status) ) + { + if( (pWhiteBalance->Flags & Caps.Capability ) == pWhiteBalance->Flags ) + { + switch( pWhiteBalance->Flags ) + { + case KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO: + break; + case KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO | KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK: + case KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK: + pWhiteBalance->Flags = KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO | KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK; + break; + + case KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_MANUAL: + switch( pWhiteBalance->Mode() ) + { + case KSCAMERA_EXTENDEDPROP_WHITEBALANCE_TEMPERATURE: + Status = Caps.BoundsCheck( pWhiteBalance->GetLONGLONG() ); + break; + + case KSCAMERA_EXTENDEDPROP_WHITEBALANCE_PRESET: + switch( pWhiteBalance->GetLONGLONG() ) + { + case KSCAMERA_EXTENDEDPROP_WBPRESET_CLOUDY: + case KSCAMERA_EXTENDEDPROP_WBPRESET_DAYLIGHT: + case KSCAMERA_EXTENDEDPROP_WBPRESET_FLASH: + case KSCAMERA_EXTENDEDPROP_WBPRESET_FLUORESCENT: + case KSCAMERA_EXTENDEDPROP_WBPRESET_TUNGSTEN: + case KSCAMERA_EXTENDEDPROP_WBPRESET_CANDLELIGHT: + break; + + default: + Status = STATUS_INVALID_PARAMETER; + break; + } + break; + + default: + Status = STATUS_INVALID_PARAMETER; + break; + } + break; + + default: + Status = STATUS_INVALID_PARAMETER; + break; + } + + if( NT_SUCCESS(Status) ) + { + Status = INVOKE_SET_ASYNC( m_Sensor, WhiteBalance, pWhiteBalance, m_WhiteBalanceNotifier ); + } + } + else + { + Status = STATUS_INVALID_PARAMETER; + } + } + } + + DBG_LEAVE("(PinId=%d, Flags=0x%016llX, Cap=0x%016llX, Mode=0x%08X, Val=0x%016llX)=0x%08X", + pWhiteBalance->PinId, pWhiteBalance->Flags, pWhiteBalance->Capability, + pWhiteBalance->Mode(), pWhiteBalance->GetLONGLONG(), Status ); + return Status; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_EXPOSUREMODE +NTSTATUS +CCaptureFilter:: +GetExposure( + _Inout_ CExtendedVidProcSetting *pExposure +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( pExposure->isValid() && + pExposure->PinId == KSCAMERA_EXTENDEDPROP_FILTERSCOPE ) + { + Status = m_Sensor->GetExposure( pExposure ); + } + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_EXPOSUREMODE +NTSTATUS +CCaptureFilter:: +SetExposure( + _In_ CExtendedVidProcSetting *pExposure +) +{ + PAGED_CODE(); + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + DBG_ENTER("()"); + if( (pExposure->Capability & KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL) && + pExposure->isValid() && + pExposure->PinId == KSCAMERA_EXTENDEDPROP_FILTERSCOPE ) + { + CExtendedVidProcSetting Caps; + Status = m_Sensor->GetExposure( &Caps ); + + if( NT_SUCCESS(Status) ) + { + if( (pExposure->Flags & Caps.Capability ) == pExposure->Flags ) + { + if( pExposure->Flags == KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_MANUAL ) + { + Status = Caps.BoundsCheck( pExposure->GetULONGLONG() ); + } + + if( NT_SUCCESS(Status) ) + { + Status = INVOKE_SET_ASYNC( m_Sensor, Exposure, pExposure, m_ExposureNotifier ); + } + } + else + { + Status = STATUS_INVALID_PARAMETER; + } + } + } + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_FOCUSMODE +NTSTATUS +CCaptureFilter:: +GetFocus( + _Inout_ CExtendedVidProcSetting *pFocus +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( pFocus->isValid() && + pFocus->PinId == KSCAMERA_EXTENDEDPROP_FILTERSCOPE ) + { + Status = m_Sensor->GetFocus( pFocus ); + } + + DBG_LEAVE("(FocusMode=0x%016llX)=0x%08X", pFocus->Flags, Status); + return Status; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_FOCUSMODE +NTSTATUS +CCaptureFilter:: +SetFocus( + _In_ CExtendedVidProcSetting *pFocus +) +{ + PAGED_CODE(); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + ULONGLONG Flags = pFocus->Flags; + + DBG_ENTER("(Preset Flags=0x%016llX)", Flags); + + CExtendedVidProcSetting Caps(*pFocus); + + // This part is just setting validation. + if( (pFocus->Capability & KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL) && + pFocus->isValid() && + pFocus->Mode() == 0 && + pFocus->PinId == KSCAMERA_EXTENDEDPROP_FILTERSCOPE ) + { + Status = m_Sensor->GetFocus( &Caps ); + } + + if( NT_SUCCESS(Status) ) + { + // Make sure we have a valid combinations of Flags + switch( Flags & + (KSCAMERA_EXTENDEDPROP_FOCUS_MODE_MASK | + KSCAMERA_EXTENDEDPROP_FOCUS_MODE_ADVANCED_MASK) ) + { + // Auto-focus combinations: + case KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO: + case KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO | + KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK: + case KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO | + KSCAMERA_EXTENDEDPROP_FOCUS_REGIONBASED: + case KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO | + KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK | + KSCAMERA_EXTENDEDPROP_FOCUS_REGIONBASED: + case KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO | + KSCAMERA_EXTENDEDPROP_FOCUS_DRIVERFALLBACK_OFF: + case KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO | + KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK | + KSCAMERA_EXTENDEDPROP_FOCUS_DRIVERFALLBACK_OFF: + case KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO | + KSCAMERA_EXTENDEDPROP_FOCUS_REGIONBASED | + KSCAMERA_EXTENDEDPROP_FOCUS_DRIVERFALLBACK_OFF: + case KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO | + KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK | + KSCAMERA_EXTENDEDPROP_FOCUS_REGIONBASED | + KSCAMERA_EXTENDEDPROP_FOCUS_DRIVERFALLBACK_OFF: + break; + + // Continuous-focus combinations: + case KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUS: + case KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUS | + KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUSLOCK: + case KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUS | + KSCAMERA_EXTENDEDPROP_FOCUS_REGIONBASED: + case KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUS | + KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUSLOCK | + KSCAMERA_EXTENDEDPROP_FOCUS_REGIONBASED: + case KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUS | + KSCAMERA_EXTENDEDPROP_FOCUS_DRIVERFALLBACK_OFF: + case KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUS | + KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUSLOCK | + KSCAMERA_EXTENDEDPROP_FOCUS_DRIVERFALLBACK_OFF: + case KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUS | + KSCAMERA_EXTENDEDPROP_FOCUS_REGIONBASED | + KSCAMERA_EXTENDEDPROP_FOCUS_DRIVERFALLBACK_OFF: + case KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUS | + KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUSLOCK | + KSCAMERA_EXTENDEDPROP_FOCUS_REGIONBASED | + KSCAMERA_EXTENDEDPROP_FOCUS_DRIVERFALLBACK_OFF: + break; + + // Manual stands alone. + case KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_MANUAL: + Status = BoundsCheckSigned( + pFocus->GetLONG(), + Caps.m_Setting ); + + if( !NT_SUCCESS(Status) ) + { + DBG_TRACE( "ERROR: Focus request out of bounds. Value=%d", pFocus->GetLONG() ); + } + break; + + // Unlock must stand alone. + // Also note that by definition unlock is accepted regardless of our current state. + case KSCAMERA_EXTENDEDPROP_FOCUS_UNLOCK: + break; + + // Lock can stand alone. + case KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK: + // Make sure we are actually being asked during an auto or continuous. + if( !(Caps.Flags & + (KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO | KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUS)) ) + { + // This is an ambiguous case under the WinBlue DDI. However, + // producing an error code here will cause a test failure. + // Instead we will silently consume the error and just skip this + // request. + // + // Actually the WinBlue WinRT layer interprets a "Manual Preset" as + // a LOCK-only request when there is no value involved. That is kind + // rediculous since the value is sent without considering the previous + // mode. Instead, it thinks that just setting a LOCK-only mode is + // synonymous with "Manual at the current position." + // + // Therefore, our interpretation will be, If we're in manual, we can + // just silently consume the entire request and do nothing. + if( Caps.Flags & KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_MANUAL ) + { + Status = STATUS_INVALID_LOCK_SEQUENCE; + } + else + { + Status = STATUS_INVALID_PARAMETER; + } + } + break; + + // Anything else is just wrong. + default: + Status = STATUS_INVALID_PARAMETER; + break; + } + } + + // Validate focus ranges. + if( NT_SUCCESS(Status) ) + { + // If AUTO or CONTINUOUS is requested, we may support RANGE flags. + if( (Flags & + (KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO | + KSCAMERA_EXTENDEDPROP_FOCUS_CONTINUOUS)) ) + { + switch( Flags & KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_MASK ) + { + case KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_MACRO: + case KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_NORMAL: + case KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_FULLRANGE: + // Depreciated flags are not supported: + //case KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_INFINITY: + //case KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_HYPERFOCAL: + case 0: + break; + + default: + Status = STATUS_INVALID_PARAMETER; + break; + } + } + // Otherwise no RANGE flags are permitted. + else if( Flags & KSCAMERA_EXTENDEDPROP_FOCUS_RANGE_MASK ) + { + Status = STATUS_INVALID_PARAMETER; + } + } + + // Validate focus distances. + if( NT_SUCCESS(Status) ) + { + // If MANUAL is requested, we may support DISTANCE flags. + if( Flags & KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_MANUAL ) + { + switch( Flags & KSCAMERA_EXTENDEDPROP_FOCUS_DISTANCE_MASK ) + { + case KSCAMERA_EXTENDEDPROP_FOCUS_DISTANCE_INFINITY: + case KSCAMERA_EXTENDEDPROP_FOCUS_DISTANCE_HYPERFOCAL: + case KSCAMERA_EXTENDEDPROP_FOCUS_DISTANCE_NEAREST: + case 0: + break; + + default: + Status = STATUS_INVALID_PARAMETER; + break; + } + } + // Otherwise no DISTANCE flags are permitted. + else if( Flags & KSCAMERA_EXTENDEDPROP_FOCUS_DISTANCE_MASK ) + { + Status = STATUS_INVALID_PARAMETER; + } + } + + if( NT_SUCCESS(Status) ) + { + Status = INVOKE_SET_ASYNC( m_Sensor, Focus, pFocus, m_FocusNotifier ); + } + // This is the failure case. In this case, we don't change state; but + // if we were ASYNC, we need to signal the client that we completed. + else + { + // Don't need to set the event if this was [erroneously] an sync request. + if( pFocus->Capability & KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL ) + { + m_FocusNotifier.Set(); + } + + // Silently consume Lock after Manual requests: + if( STATUS_INVALID_LOCK_SEQUENCE == Status ) + { + Status = STATUS_SUCCESS; + } + } + DBG_LEAVE("(Preset Flags=0x%016llX)=0x%08X", Flags, Status); + return Status; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOCONFIRMATION +NTSTATUS +CCaptureFilter:: +GetPhotoConfirmation( + _Inout_ CExtendedProperty *pConfirmation +) +{ + PAGED_CODE(); + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( pConfirmation->isValid() && + pConfirmation->PinId == KSCAMERA_EXTENDEDPROP_FILTERSCOPE ) + { + Status = m_Sensor->GetPhotoConfirmation( pConfirmation ); + } + return Status; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOCONFIRMATION +NTSTATUS +CCaptureFilter:: +SetPhotoConfirmation( + _In_ CExtendedProperty *pConfirmation +) +{ + PAGED_CODE(); + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( !(pConfirmation->Capability & KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL) && + pConfirmation->isValid() && + pConfirmation->PinId == KSCAMERA_EXTENDEDPROP_FILTERSCOPE ) + { + switch( pConfirmation->Flags ) + { + case KSCAMERA_EXTENDEDPROP_PHOTOCONFIRMATION_ON: + case KSCAMERA_EXTENDEDPROP_PHOTOCONFIRMATION_OFF: + Status = m_Sensor->SetPhotoConfirmation( pConfirmation ); + break; + } + } + return Status; +} + +// Get KSPROPERTY_CAMERACONTROL_PERFRAMESETTING_CAPABILITY +NTSTATUS +CCaptureFilter::GetPFSCaps( + _In_ PIRP pIrp, + _In_ PKSPROPERTY pProperty, + _Inout_ PVOID pData +) +{ + PAGED_CODE(); + + NT_ASSERT(pIrp); + NT_ASSERT(pProperty); + + CCaptureFilter *pFilter = reinterpret_cast (KsGetFilterFromIrp(pIrp)->Context); + PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation(pIrp); + ULONG ulOutputBufferLength = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength; + ULONG RequiredLength = 0; + + NTSTATUS Status = + pFilter->m_Sensor->GetPfsCaps( nullptr, &RequiredLength ); + + // Note: We may want to determine capabilities at runtime in the future. + + // We only handle GETs + if( NT_SUCCESS(Status) ) + { + if (ulOutputBufferLength == 0) + { + pIrp->IoStatus.Information = RequiredLength; + Status = STATUS_BUFFER_OVERFLOW; + } + else if (ulOutputBufferLength < RequiredLength) + { + Status = STATUS_BUFFER_TOO_SMALL; + } + else if (pData && ulOutputBufferLength >= RequiredLength) + { + Status = + pFilter->m_Sensor->GetPfsCaps( + (KSCAMERA_PERFRAMESETTING_CAP_HEADER *) pData, + &RequiredLength ); + pIrp->IoStatus.Information = RequiredLength; + } + else + { + Status = STATUS_INVALID_PARAMETER; + } + } + return Status; +} + +// Get KSPROPERTY_CAMERACONTROL_PERFRAMESETTING_SET +NTSTATUS +CCaptureFilter::GetPerFrameSettings( + _In_ PIRP pIrp, + _In_ PKSPROPERTY pProperty, + _Inout_ PVOID pData +) +{ + PAGED_CODE(); + NTSTATUS ntStatus = STATUS_SUCCESS; + + NT_ASSERT(pIrp); + NT_ASSERT(pProperty); + + CCaptureFilter *pFilter = reinterpret_cast (KsGetFilterFromIrp(pIrp)->Context); + PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation(pIrp); + ULONG ulOutputBufferLength = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength; + ULONG ulOutputSize = + pFilter->m_pPerFrameSettings ? pFilter->m_pPerFrameSettings->Size : 0; + + // Only handle GETs + if (ulOutputBufferLength == 0) + { + pIrp->IoStatus.Information = ulOutputSize; + ntStatus = STATUS_BUFFER_OVERFLOW; + } + else if (ulOutputBufferLength < ulOutputSize) + { + ntStatus = STATUS_BUFFER_TOO_SMALL; + } + else if (pData && ulOutputBufferLength >= ulOutputSize) + { + // Return the cached copy. + RtlCopyMemory( pData, pFilter->m_pPerFrameSettings, ulOutputSize ); + pIrp->IoStatus.Information = ulOutputSize; + ntStatus = STATUS_SUCCESS; + } + else + { + ntStatus = STATUS_INVALID_PARAMETER; + } + + return ntStatus; +} + +// Set KSPROPERTY_CAMERACONTROL_PERFRAMESETTING_SET +NTSTATUS +CCaptureFilter:: +SetPerFrameSettings( + _In_ PIRP pIrp, + _In_ PKSPROPERTY pProperty, + _Inout_ PVOID pData +) +{ + PAGED_CODE(); + NTSTATUS Status = STATUS_SUCCESS; + + NT_ASSERT(pIrp); + NT_ASSERT(pProperty); + + CCaptureFilter *pFilter = reinterpret_cast (KsGetFilterFromIrp(pIrp)->Context); + PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation(pIrp); + ULONG ulOutputBufferLength = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength; + + // Only handle SETs + if (ulOutputBufferLength == 0) + { + // This is pretty meaningless. + // We can't really tell the caller the size of this buffer. + pIrp->IoStatus.Information = + sizeof(KSCAMERA_PERFRAMESETTING_HEADER) + + sizeof(KSCAMERA_PERFRAMESETTING_FRAME_HEADER) + + sizeof(KSCAMERA_PERFRAMESETTING_ITEM_HEADER) ; + Status = STATUS_BUFFER_OVERFLOW; + } + else if (ulOutputBufferLength < sizeof(KSCAMERA_PERFRAMESETTING_HEADER)) + { + Status = STATUS_BUFFER_TOO_SMALL; + } + else + { + PKSCAMERA_PERFRAMESETTING_HEADER pPFS = + (PKSCAMERA_PERFRAMESETTING_HEADER) pData ; + ISP_FRAME_SETTINGS *pSettings = nullptr; + + // Parse, but don't save the PerFrameSettings + Status = pFilter->ParsePFSBuffer( pPFS, ulOutputBufferLength, &pSettings ); + + // Cache a copy of the unparsed PFS for the GET. + // Pass the parsed settings down to the sensor. + if( NT_SUCCESS(Status) ) + { + delete [] ((PBYTE) pFilter->m_pPerFrameSettings); + pFilter->m_pPerFrameSettings = (PKSCAMERA_PERFRAMESETTING_HEADER) + new (PagedPool, '-SFP') BYTE [pPFS->Size]; + + if( pFilter->m_pPerFrameSettings ) + { + // Cache a copy of the settings for the GET. + RtlCopyMemory( pFilter->m_pPerFrameSettings, pPFS, pPFS->Size ); + + // Program the sensor with a copy of the parsed settings. + // The sensor object should copy what it needs. + pFilter->m_Sensor->SetPFS( pSettings, pPFS->FrameCount, pPFS->LoopCount ); + } + else + { + Status = STATUS_INSUFFICIENT_RESOURCES; + } + } + + // Delete the settings buffer ParsePFSBuffer created. + delete [] pSettings; + } + + return Status; +} + +// Set KSPROPERTY_CAMERACONTROL_PERFRAMESETTING_CLEAR +NTSTATUS +CCaptureFilter::ClearPerFrameSettings( + _In_ PIRP pIrp, + _In_ PKSPROPERTY pProperty, + _Inout_ PVOID pData +) +{ + PAGED_CODE(); + NTSTATUS ntStatus = STATUS_SUCCESS; + + NT_ASSERT(pIrp); + NT_ASSERT(pProperty); + + CCaptureFilter *pFilter = reinterpret_cast (KsGetFilterFromIrp(pIrp)->Context); + + // Only handle SETs + delete [] ((PBYTE) pFilter->m_pPerFrameSettings); + pFilter->m_pPerFrameSettings = nullptr; + pFilter->m_Sensor->SetPFS( nullptr, 0, 0 ); + + return ntStatus; +} + +PKSCAMERA_PERFRAMESETTING_CAP_ITEM_HEADER +Find( + _In_ PKSCAMERA_PERFRAMESETTING_CAP_HEADER Caps, + _In_ ULONG Type +) +/*++ + +Routine Description: + + Helper function that finds a specific PFS capability. + +Arguments: + + Caps - + A list of PFS capabilities. + Type - + The capability type to search for. + +Return Value: + + A pointer to the capability item header. + +--*/ +{ + PAGED_CODE(); + + if( Caps ) + { + PKSCAMERA_PERFRAMESETTING_CAP_ITEM_HEADER Item = + (PKSCAMERA_PERFRAMESETTING_CAP_ITEM_HEADER) (Caps+1); + + for( ULONG i=0; iItemCount; i++ ) + { + if( Item->Type == Type ) + { + return Item; + } + Item = (PKSCAMERA_PERFRAMESETTING_CAP_ITEM_HEADER) (((PBYTE) Item) + Item->Size); + } + } + return nullptr; +} + +// This function can be used to validate PerFrameSettings, or +// it can be used to populate a buffer containing PFS settings. +_Success_(return == 0) +NTSTATUS +CCaptureFilter:: +ParsePFSBuffer( + _In_reads_bytes_(BufferLimit) + PKSCAMERA_PERFRAMESETTING_HEADER pPFS, + _In_ ULONG BufferLimit, + _Outptr_opt_result_maybenull_ + ISP_FRAME_SETTINGS **ppSettings +) +/*++ + +Routine Description: + + Helper function that parses Per-Frame Settings. + + This function validates the API's PerFrameSettings list and optionally + returns an array of ISP_FRAME_SETTINGS for each frame. + + Any returned array of ISP_FRAME_SETTINGS must be deleted by the caller. + +Arguments: + + pPFS - + A variable length list of per frame settings. + BufferLimit - + The maximum amount of space, in bytes, used by the PFS list. + ppSettings - + An optional pointer to an array of frame settings to be populated. + +Return Value: + + Success / failure + +--*/ +{ + PAGED_CODE(); + NTSTATUS Status = STATUS_SUCCESS; + + DBG_ENTER("[%S]()", GetFilterName()); + + NT_ASSERT(pPFS); + NT_ASSERT(BufferLimit!=0); + + if( !pPFS || + BufferLimit == 0 || + pPFS->Size > BufferLimit || + pPFS->Size < sizeof(KSCAMERA_PERFRAMESETTING_HEADER) || + pPFS->FrameCount > MAX_FRAME_COUNT || + pPFS->FrameCount == 0 ) + { + Status = STATUS_INVALID_PARAMETER; + } + else + { + ULONG i=0 ; + ISP_FRAME_SETTINGS *pSettings = nullptr; + LPVOID pEnd = ((LPBYTE) pPFS) + BufferLimit ; + PKSCAMERA_PERFRAMESETTING_FRAME_HEADER pFrame = + (PKSCAMERA_PERFRAMESETTING_FRAME_HEADER) &pPFS[1]; + + // Make sure we have at least one frame in the array. + ULONG FrameCount = pPFS->FrameCount ? pPFS->FrameCount : 1 ; + + // Query the PFS Caps from the Sensor + PKSCAMERA_PERFRAMESETTING_CAP_HEADER Caps = nullptr; + ULONG RequiredSize = 0; + Status = m_Sensor->GetPfsCaps( nullptr, &RequiredSize ); + + if( NT_SUCCESS(Status) ) + { + Caps = (PKSCAMERA_PERFRAMESETTING_CAP_HEADER) new (PagedPool, '-SFP') BYTE[RequiredSize]; + + if( Caps ) + { + RtlZeroMemory( Caps, RequiredSize ); + Status = m_Sensor->GetPfsCaps( Caps, &RequiredSize ); + } + else + { + Status = STATUS_INSUFFICIENT_RESOURCES; + } + } + + if( NT_SUCCESS(Status) ) + { + // Allocate an array + pSettings = new (NonPagedPoolNx, '-SFP') ISP_FRAME_SETTINGS[FrameCount]; + if( !pSettings ) + { + Status = STATUS_INSUFFICIENT_RESOURCES; + } + else + { + // Initialize the array to our globals + pSettings[0] = *m_Sensor->GetGlobalIspSettings(); + for( i=1; iSize); + PKSCAMERA_PERFRAMESETTING_ITEM_HEADER pItem = + (PKSCAMERA_PERFRAMESETTING_ITEM_HEADER) &pFrame[1]; + + // Verify that the frame is within the buffer. + if( pFrame->Id >= pPFS->FrameCount || + pNextFrame > pEnd || + pFrame->Size < sizeof(KSCAMERA_PERFRAMESETTING_FRAME_HEADER) ) + { + DBG_TRACE( "Malformed PFS Buffer. pPFS=%p pFrame=%p", + pPFS, pFrame ); + + Status=STATUS_INVALID_PARAMETER; + break; + } + + DBG_TRACE("Frame Id=%d",pFrame->Id); + + ISP_FRAME_SETTINGS &FrameSetting = pSettings[pFrame->Id] ; + + // Scan for at most n Items regardless. + for( i=0; + i < pFrame->ItemCount && + (pItem+1) <= (PVOID)pNextFrame; + i++ ) + { + PKSCAMERA_PERFRAMESETTING_ITEM_HEADER pNextItem = + (PKSCAMERA_PERFRAMESETTING_ITEM_HEADER) + (((LPBYTE)pItem) + pItem->Size); + + PKSCAMERA_PERFRAMESETTING_CAP_ITEM_HEADER ItemCap = + Find( Caps, pItem->Type ); + + // Verify that the item is within the frame ... + // ... and a supported capability. + if( pNextItem>(LPVOID)pNextFrame || + pItem->SizeType ) + { + case KSCAMERA_PERFRAMESETTING_ITEM_EXPOSURE_TIME: + DBG_TRACE("KSCAMERA_PERFRAMESETTING_ITEM_EXPOSURE_TIME"); + + FrameSetting.ExposureMode = + TranslatePFS2VideoProcFlags( pItem->Flags ); + + if( pItem->Flags & KSCAMERA_PERFRAMESETTING_MANUAL ) + { + if( pItem->Size== + sizeof(KSCAMERA_PERFRAMESETTING_ITEM_HEADER)+ + sizeof(KSCAMERA_EXTENDEDPROP_VALUE) ) + { + FrameSetting.ExposureSetting.VideoProc.Value = + ((PKSCAMERA_EXTENDEDPROP_VALUE) &(pItem[1]))->Value; + // Make sure the setting is in range. + Status = + BoundsCheckSigned( + FrameSetting.ExposureSetting.VideoProc.Value.ll, + ((SOC_CAP_WITH_STEPPING_LONGLONG *) ItemCap)->Stepping ); + DBG_TRACE("Status=0x%08X, ExposureMode=KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_MANUAL, ExposureTime=%lldns, element=%d", + Status, FrameSetting.ExposureSetting.VideoProc.Value.ll, i); + } + else + { + Status=STATUS_INVALID_PARAMETER; + DBG_TRACE("Status=STATUS_INVALID_PARAMETER"); + } + } + else if( pItem->Size!=sizeof(KSCAMERA_PERFRAMESETTING_ITEM_HEADER) ) + { + Status=STATUS_INVALID_PARAMETER; + DBG_TRACE("Status=STATUS_INVALID_PARAMETER"); + } + break; + + case KSCAMERA_PERFRAMESETTING_ITEM_FLASH: + DBG_TRACE("KSCAMERA_PERFRAMESETTING_ITEM_FLASH"); + FrameSetting.FlashMode = pItem->Flags; + if( pItem->Flags & + (KSCAMERA_EXTENDEDPROP_FLASH_ON_ADJUSTABLEPOWER | + KSCAMERA_EXTENDEDPROP_FLASH_AUTO_ADJUSTABLEPOWER) ) + { + if( pItem->Size== + sizeof(KSCAMERA_PERFRAMESETTING_ITEM_HEADER)+ + sizeof(KSCAMERA_EXTENDEDPROP_VALUE) ) + { + FrameSetting.FlashValue = + ((PKSCAMERA_EXTENDEDPROP_VALUE) &(pItem[1]))->Value.ul; + + // Make sure the setting is in range. + if( FrameSetting.FlashValue > 100 ) + { + Status=STATUS_INVALID_PARAMETER; + } + else + { + DBG_TRACE("FlashPower (%d%) accepted...", FrameSetting.FlashValue); + } + } + else + { + Status=STATUS_INVALID_PARAMETER; + } + } + else if( pItem->Size!=sizeof(KSCAMERA_PERFRAMESETTING_ITEM_HEADER) ) + { + Status=STATUS_INVALID_PARAMETER; + } + + if( NT_SUCCESS(Status) ) + { + DBG_TRACE("FlashMode=0x%016llX, FlashPower=%d", FrameSetting.FlashMode, FrameSetting.FlashValue); + } + break; + + case KSCAMERA_PERFRAMESETTING_ITEM_EXPOSURE_COMPENSATION: + DBG_TRACE("KSCAMERA_PERFRAMESETTING_ITEM_EXPOSURE_COMPENSATION"); + { + LONG Denominator = EVFlags2Denominator( pItem->Flags ); + KSPROPERTY_STEPPING_LONG + Bounds = ((SOC_CAP_WITH_STEPPING *) ItemCap)->Stepping; + + // Adjust the bounds for the step size... (A quirk of EV) + Bounds.Bounds.SignedMinimum *= Denominator; + Bounds.Bounds.SignedMaximum *= Denominator; + + FrameSetting.EVCompensation.Mode = (ULONG) + pItem->Flags & ~(KSCAMERA_PERFRAMESETTING_AUTO); + + // Validate manual setting + if( (pItem->Flags != KSCAMERA_PERFRAMESETTING_AUTO) ) + { + if( pItem->Size== + sizeof(KSCAMERA_PERFRAMESETTING_ITEM_HEADER)+ + sizeof(KSCAMERA_EXTENDEDPROP_VALUE) && + (Denominator != 0) ) + { + FrameSetting.EVCompensation.Value = + ((PKSCAMERA_EXTENDEDPROP_VALUE) &(pItem[1]))->Value.l; + + // Make sure the setting is in range. + Status = + BoundsCheckSigned( + FrameSetting.EVCompensation.Value, + Bounds ); + } + else + { + Status=STATUS_INVALID_PARAMETER; + } + } + // Auto can be the only flag, if auto is enabled. + else //if( pItem->Flags == KSCAMERA_PERFRAMESETTING_AUTO ) + { + if( pItem->Size==sizeof(KSCAMERA_PERFRAMESETTING_ITEM_HEADER) ) + { + static + ULONG step[] = + { + 0, + KSCAMERA_EXTENDEDPROP_EVCOMP_FULLSTEP, //1 + KSCAMERA_EXTENDEDPROP_EVCOMP_HALFSTEP, //2 + KSCAMERA_EXTENDEDPROP_EVCOMP_THIRDSTEP, //3 + KSCAMERA_EXTENDEDPROP_EVCOMP_QUARTERSTEP, //4 + 0, //5 + KSCAMERA_EXTENDEDPROP_EVCOMP_SIXTHSTEP //6 + }; + + // Work back from the randomly selected denominator ... + /// ... to the step flag. + FrameSetting.EVCompensation.Mode = step[Denominator]; + + // Now pick a "random" numerator that is in range. + FrameSetting.EVCompensation.Value = (LONG) + GetRandom( Bounds.Bounds.SignedMinimum, + Bounds.Bounds.SignedMaximum ); + } + else + { + Status=STATUS_INVALID_PARAMETER; + } + } + } + break; + + case KSCAMERA_PERFRAMESETTING_ITEM_ISO: + DBG_TRACE("KSCAMERA_PERFRAMESETTING_ITEM_ISO: Flags=0x%016llX", pItem->Flags); + + FrameSetting.ISOMode = pItem->Flags; + // Handle Auto. + if( pItem->Flags == KSCAMERA_EXTENDEDPROP_ISO_AUTO ) + { + if( pItem->Size==sizeof(KSCAMERA_PERFRAMESETTING_ITEM_HEADER) ) + { + FrameSetting.ISOValue = 0; + } + else + { + Status=STATUS_INVALID_PARAMETER; + } + } + // Handle manual settings. + else if( pItem->Flags == KSCAMERA_EXTENDEDPROP_ISO_MANUAL ) + { + if( pItem->Size== + sizeof(KSCAMERA_PERFRAMESETTING_ITEM_HEADER)+ + sizeof(KSCAMERA_EXTENDEDPROP_VALUE) ) + { + FrameSetting.ISOValue = + ((PKSCAMERA_EXTENDEDPROP_VALUE) &(pItem[1]))->Value.ul; + // Make sure the setting is in range. + Status = + BoundsCheckUnsigned( + FrameSetting.ISOValue, + ((SOC_CAP_WITH_STEPPING *)ItemCap)->Stepping ); + + DBG_TRACE("ISOValue=%d", FrameSetting.ISOValue); + } + else + { + Status=STATUS_INVALID_PARAMETER; + } + } + else + { + Status=STATUS_INVALID_PARAMETER; + } + break; + + case KSCAMERA_PERFRAMESETTING_ITEM_FOCUS: + DBG_TRACE("KSCAMERA_PERFRAMESETTING_ITEM_FOCUS"); + if( pItem->Flags == KSCAMERA_PERFRAMESETTING_MANUAL ) + { + if( pItem->Size== + sizeof(KSCAMERA_PERFRAMESETTING_ITEM_HEADER)+ + sizeof(KSCAMERA_EXTENDEDPROP_VALUE) ) + { + FrameSetting.FocusMode = + KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_MANUAL; + FrameSetting.FocusSetting.VideoProc = + *((PKSCAMERA_EXTENDEDPROP_VALUE) &(pItem[1])); + // Make sure the setting is in range. + Status = + BoundsCheckUnsigned( + FrameSetting.FocusSetting.VideoProc.Value.ul, + ((SOC_CAP_WITH_STEPPING *)ItemCap)->Stepping ); + } + else + { + Status=STATUS_INVALID_PARAMETER; + } + } + else + { + Status=STATUS_INVALID_PARAMETER; + } + break; + + case KSCAMERA_PERFRAMESETTING_ITEM_PHOTOCONFIRMATION: + DBG_TRACE("KSCAMERA_PERFRAMESETTING_ITEM_PHOTOCONFIRMATION"); + + if( pItem->Size!=sizeof(KSCAMERA_PERFRAMESETTING_ITEM_HEADER) ) + { + Status=STATUS_INVALID_PARAMETER; + } + else + { + switch( pItem->Flags ) + { + case KSCAMERA_EXTENDEDPROP_PHOTOCONFIRMATION_ON: + FrameSetting.bPhotoConfirmation = TRUE; + DBG_TRACE("PFS Photo Confirmation ON for frame #%d", pFrame->Id); + break; + + case KSCAMERA_EXTENDEDPROP_PHOTOCONFIRMATION_OFF: + FrameSetting.bPhotoConfirmation = FALSE; + DBG_TRACE("PFS Photo Confirmation OFF for frame #%d", pFrame->Id); + break; + + default: + Status=STATUS_INVALID_PARAMETER; + break; + } + } + break; + + case KSCAMERA_PERFRAMESETTING_ITEM_CUSTOM: + DBG_TRACE("KSCAMERA_PERFRAMESETTING_ITEM_CUSTOM"); + break; + + default: + // We don't understand this item. Do nothing. + NT_ASSERT(FALSE); + break; + } + + // Bail here (a little early) if any of the parsing failed. + if( !NT_SUCCESS(Status) ) + { + break; + } + + // Move to the next Item + pItem = pNextItem; + } + + // Bail here (a little early) if any of the parsing failed. + if( !NT_SUCCESS(Status) ) + { + DBG_TRACE( "Malformed PFS Buffer. pPFS=%p pItem=%p", + pPFS, pItem ); + break; + } + + // Move to next Frame + pFrame = pNextFrame; + } + + // We need to log where in the buffer we encountered an error. + // For now we just emit info to the debugger. + } + + if( NT_SUCCESS(Status) && ppSettings ) + { + *ppSettings = pSettings; + } + else + { + delete [] pSettings; + } + } + + delete [] ((PBYTE) Caps); + } + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_FOCUSSTATE +NTSTATUS +CCaptureFilter::GetFocusState( + _Inout_ CExtendedProperty *pState +) +{ + PAGED_CODE(); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( pState->isValid() && + pState->PinId == KSCAMERA_EXTENDEDPROP_FILTERSCOPE ) + { + KSCAMERA_EXTENDEDPROP_FOCUSSTATE FocusState = KSCAMERA_EXTENDEDPROP_FOCUSSTATE_UNINITIALIZED; + Status = m_Sensor->GetFocusState( &FocusState ); + if( NT_SUCCESS(Status) ) + { + *pState = CExtendedProperty( (ULONGLONG) FocusState ); + pState->Capability = 0; + } + } + DBG_LEAVE("(PinId=%u, Flags=0x%016llX, Version=%u)=0x%08X", + pState->PinId, pState->Flags, pState->Version, Status); + return Status; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_FOCUSPRIORITY +NTSTATUS +CCaptureFilter:: +GetFocusPriority( + _Inout_ CExtendedProperty *pPriority +) +{ + PAGED_CODE(); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( pPriority->isValid() && + pPriority->PinId == KSCAMERA_EXTENDEDPROP_FILTERSCOPE ) + { + Status = m_Sensor->GetFocusPriority( pPriority ); + } + DBG_LEAVE("(PinId=%u, Flags=0x%016llX, Version=%u)=0x%08X", + pPriority->PinId, pPriority->Flags, pPriority->Version, Status); + return Status; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_FOCUSPRIORITY +NTSTATUS +CCaptureFilter:: +SetFocusPriority( + _In_ CExtendedProperty *pPriority +) +{ + PAGED_CODE(); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( pPriority->isValid() && + pPriority->Capability == 0 && + pPriority->PinId == KSCAMERA_EXTENDEDPROP_FILTERSCOPE && + (pPriority->Flags & ~KSCAMERA_EXTENDEDPROP_FOCUSPRIORITY_ON) == 0 ) + { + Status = m_Sensor->SetFocusPriority( pPriority ); + } + DBG_LEAVE("(PinId=%u, Flags=0x%016llX, Version=%u)=0x%08X", + pPriority->PinId, pPriority->Flags, pPriority->Version, Status); + return Status; +} + +// KSPROPERTY_CAMERACONTROL_EXTENDED_ROI_CONFIGCAPS Get handler +NTSTATUS +CCaptureFilter:: +GetRoiConfigCaps( + _Inout_ CRoiConfig *pOutput +) +{ + PAGED_CODE(); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( pOutput->isValid() && + pOutput->PinId == KSCAMERA_EXTENDEDPROP_FILTERSCOPE ) + { + Status = m_Sensor->GetRoiConfigCaps( pOutput ); + } + return Status; +} + +// KSPROPERTY_CAMERACONTROL_EXTENDED_ROI_ISPCONTROL Get handler +// +// Note: ROI handlers are left as static member functions because +// it wasn't worth the code needed to generalize handling of a +// variable length parameter. +NTSTATUS +CCaptureFilter:: +GetRoiIspControl( + _In_ PIRP pIrp, + _In_ PKSPROPERTY pProperty, + _Inout_ PVOID pData +) +{ + PAGED_CODE(); + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + DBG_ENTER("()"); + + NT_ASSERT(pIrp); + NT_ASSERT(pProperty); + + CCaptureFilter *pFilter = reinterpret_cast (KsGetFilterFromIrp(pIrp)->Context); + CSensor *pSensor = pFilter->m_Sensor; + PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation(pIrp); + ULONG ulOutputBufferLength = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength; + CRoiProperty *pRoi = (reinterpret_cast(pData)); + ULONG RequiredSize = pSensor->SizeOfRoi(); + + // Only handle GETs + if( ulOutputBufferLength == 0 ) + { + pIrp->IoStatus.Information = RequiredSize;; + Status = STATUS_BUFFER_OVERFLOW; + } + else if( ulOutputBufferLength < RequiredSize ) + { + Status = STATUS_BUFFER_TOO_SMALL; + } + else if( pData ) + { + Status = pSensor->GetRoi( pRoi ); + if( NT_SUCCESS(Status) ) + { + pIrp->IoStatus.Information = pRoi->GetSize(); + } + } + + DBG_LEAVE("()=0x%08X",Status); + return Status; +} + +// KSPROPERTY_CAMERACONTROL_EXTENDED_ROI_ISPCONTROL Set handler +NTSTATUS +CCaptureFilter:: +SetRoiIspControl( + _In_ PIRP pIrp, + _In_ PKSPROPERTY pProperty, + _Inout_ PVOID pData +) +{ + PAGED_CODE(); + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + DBG_ENTER("()"); + NT_ASSERT(pIrp); + NT_ASSERT(pProperty); + + CCaptureFilter *pFilter = reinterpret_cast (KsGetFilterFromIrp(pIrp)->Context); + CSensor *pSensor = pFilter->m_Sensor; + PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation(pIrp); + ULONG ulOutputBufferLength = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength; + CRoiProperty *pRoi = (reinterpret_cast(pData)); + ULONG RequiredSize = sizeof(*pRoi); + + // Only handle SETs + if( ulOutputBufferLength == 0 ) + { + // Minimum size for this control. + pIrp->IoStatus.Information = RequiredSize; + Status = STATUS_BUFFER_OVERFLOW; + } + else if( ulOutputBufferLength < RequiredSize ) + { + DBG_TRACE("***BUFFER TOO SMALL*** ulOutputBufferLength=%d, Size=%d", + ulOutputBufferLength, pRoi->Size ); + Status = STATUS_BUFFER_TOO_SMALL; + } + else if( pData && ulOutputBufferLength >= pRoi->Size ) + { + // Validate the data blob. + if( pRoi->isValid() && + pRoi->PinId == KSCAMERA_EXTENDEDPROP_FILTERSCOPE && + pRoi->Flags == 0 ) + { + Status = INVOKE_SET_ASYNC( pSensor, Roi, pRoi, pFilter->m_RoiNotifier ); + } + } + + DBG_LEAVE("()=0x%08X",Status); + return Status; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_ISO_ADVANCED. +NTSTATUS +CCaptureFilter:: +GetIsoAdvanced( + _Inout_ CExtendedVidProcSetting *pIso +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( pIso->isValid() && + m_Sensor->IsStillIndex(pIso->PinId) ) + { + Status = m_Sensor->GetIsoAdvanced( pIso ); + } + + // Report to the debug output... + DBG_TRACE(" Flags = 0x%016llX", pIso->Flags); + DBG_TRACE(" Min = %d", pIso->m_Setting.Min); + DBG_TRACE(" Max = %d", pIso->m_Setting.Max); + DBG_TRACE(" Step = %d", pIso->m_Setting.Step); + DBG_TRACE("VideoProc.Value = %lld", pIso->m_Setting.VideoProc.Value.ll); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_ISO_ADVANCED. +NTSTATUS +CCaptureFilter:: +SetIsoAdvanced( + _In_ CExtendedVidProcSetting *pIso +) +{ + PAGED_CODE(); + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + DBG_ENTER("()"); + + // Report to the debug output... + DBG_TRACE(" Flags = 0x%016llX", pIso->Flags); + DBG_TRACE(" Min = %d", pIso->m_Setting.Min); + DBG_TRACE(" Max = %d", pIso->m_Setting.Max); + DBG_TRACE(" Step = %d", pIso->m_Setting.Step); + DBG_TRACE("VideoProc.Value = %lld", pIso->m_Setting.VideoProc.Value.ll); + + // Validate + switch( pIso->Flags ) + { + case KSCAMERA_EXTENDEDPROP_ISO_AUTO: + case KSCAMERA_EXTENDEDPROP_FLAG_CANCELOPERATION: + case KSCAMERA_EXTENDEDPROP_ISO_AUTO | KSCAMERA_EXTENDEDPROP_FLAG_CANCELOPERATION: + case KSCAMERA_EXTENDEDPROP_ISO_MANUAL: + case KSCAMERA_EXTENDEDPROP_ISO_MANUAL | KSCAMERA_EXTENDEDPROP_FLAG_CANCELOPERATION: + if( pIso->isValid() && + (pIso->Capability & KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL) && + m_Sensor->IsStillIndex(pIso->PinId) ) + { + CExtendedVidProcSetting Caps; + Status = m_Sensor->GetIsoAdvanced(&Caps); + + if( NT_SUCCESS(Status) ) + { + // Just save the parameters + if( pIso->Flags & KSCAMERA_EXTENDEDPROP_ISO_MANUAL ) + { + Status = BoundsCheckSigned( pIso->GetLONGLONG(), Caps.m_Setting ); + } + if( NT_SUCCESS(Status) ) + { + Status = INVOKE_SET_ASYNC( m_Sensor, IsoAdvanced, pIso, m_IsoAdvancedNotifier ) + } + } + else + { + Status = STATUS_INVALID_PARAMETER; + } + } + break; + } + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_VFR. +NTSTATUS +CCaptureFilter:: +GetVFR( + _Inout_ CExtendedProperty *pVFR +) +{ + PAGED_CODE(); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( m_Sensor->IsVideoIndex(pVFR->PinId) && + pVFR->isValid() ) + { + //Get the current VFR state + Status = m_Sensor->GetVFR( pVFR ); + } + DBG_LEAVE("PinId=%u, VideoIndex=%u, Flags=0x%016llX, Version=%u, Status=0x%08X", + pVFR->PinId, m_Sensor->GetVideoIndex(), pVFR->Flags, pVFR->Version, Status); + return Status; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_VFR. +NTSTATUS +CCaptureFilter:: +SetVFR( + _In_ CExtendedProperty *pVFR +) +{ + PAGED_CODE(); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( m_Sensor->IsVideoIndex(pVFR->PinId) && + pVFR->isValid() && + pVFR->Capability == 0 && + (KSCAMERA_EXTENDEDPROP_VFR_OFF == pVFR->Flags || + KSCAMERA_EXTENDEDPROP_VFR_ON == pVFR->Flags || + KSCAMERA_EXTENDEDPROP_VIDEOHDR_AUTO == pVFR->Flags ) ) + { + //Set the current VFR state + Status = m_Sensor->SetVFR( pVFR ); + } + DBG_LEAVE("PinId=%u, VideoIndex=%u, Flags=0x%016llX, Version=%u, Status=0x%08X", + pVFR->PinId, m_Sensor->GetVideoIndex(), pVFR->Flags, pVFR->Version, Status); + return Status; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_VIDEOHDR. +NTSTATUS +CCaptureFilter:: +GetVideoHDR( + _Inout_ CExtendedProperty *pVideoHDR +) +{ + PAGED_CODE(); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( m_Sensor->IsVideoIndex(pVideoHDR->PinId) && + pVideoHDR->isValid() ) + { + //Get the current Video HDR state + Status = m_Sensor->GetVideoHDR(pVideoHDR); + } + DBG_LEAVE("PinId=%u, VideoIndex=%u, Flags=0x%016llX, Version=%u, Status=0x%08X", + pVideoHDR->PinId, m_Sensor->GetVideoIndex(), pVideoHDR->Flags, pVideoHDR->Version, Status); + return Status; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_VIDEOHDR. +NTSTATUS +CCaptureFilter:: +SetVideoHDR( + _In_ CExtendedProperty *pVideoHDR +) +{ + PAGED_CODE(); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + // Call must come for Video PIN only and flags must be supported and mutually exclusive and version must be 1 + if( m_Sensor->IsVideoIndex(pVideoHDR->PinId) && + pVideoHDR->isValid() && + !(pVideoHDR->Capability & KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL) && + (KSCAMERA_EXTENDEDPROP_VIDEOHDR_OFF == pVideoHDR->Flags || + KSCAMERA_EXTENDEDPROP_VIDEOHDR_ON == pVideoHDR->Flags || + KSCAMERA_EXTENDEDPROP_VIDEOHDR_AUTO== pVideoHDR->Flags) ) + { + if( m_pinArray[pVideoHDR->PinId]->GetState() == PinStopped ) + { + CExtendedProperty Caps(*pVideoHDR); + Status = m_Sensor->GetVideoHDR( &Caps ); + + if( NT_SUCCESS(Status) ) + { + // Assume an invalid parameter. + Status = STATUS_INVALID_PARAMETER; + + // Call must come for Video PIN only and flags must be supported and mutually exclusive and version must be 1 + if( pVideoHDR->Flags == (pVideoHDR->Flags & Caps.Capability) ) + { + Status = m_Sensor->SetVideoHDR(pVideoHDR); + } + } + } + else + { + Status = STATUS_INVALID_DEVICE_STATE; + } + } + + DBG_LEAVE("PinId=%u, VideoIndex=%u, Flags=0x%016llX, Version=%u, Status=0x%08X", + pVideoHDR->PinId, m_Sensor->GetVideoIndex(), pVideoHDR->Flags, pVideoHDR->Version, Status); + return Status; +} + + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_HISTOGRAM. +NTSTATUS +CCaptureFilter:: +GetHistogram( + _Inout_ CExtendedProperty *pHistogram +) +{ + PAGED_CODE(); + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( m_Sensor->IsPreviewIndex(pHistogram->PinId) && + pHistogram->isValid() && + pHistogram->Capability == 0 ) + { + //Get the histogram state + Status = m_Sensor->GetHistogram( pHistogram ); + } + DBG_TRACE("pHistogram->PinId = %u, m_sensor->PreviewIndex = %u, pHistogram->Flags = %016llx, pHistogram->Version = %u", + pHistogram->PinId, m_Sensor->GetPreviewIndex(), pHistogram->Flags, pHistogram->Version); + return Status; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_HISTOGRAM. +NTSTATUS +CCaptureFilter:: +SetHistogram( + _In_ CExtendedProperty *pHistogram +) +{ + PAGED_CODE(); + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( m_Sensor->IsPreviewIndex(pHistogram->PinId) && + pHistogram->isValid() && + !(pHistogram->Capability & KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL) && + (KSCAMERA_EXTENDEDPROP_HISTOGRAM_OFF == pHistogram->Flags || + KSCAMERA_EXTENDEDPROP_HISTOGRAM_ON == pHistogram->Flags ) ) + { + if( m_pinArray[pHistogram->PinId] && + m_pinArray[pHistogram->PinId]->GetState() != PinStopped ) + { + Status = STATUS_INVALID_DEVICE_STATE; + } + else + { + //Set the histogram state. + Status = m_Sensor->SetHistogram( pHistogram ); + } + } + + DBG_TRACE("pHistogram->PinId = %u, m_sensor->PreviewIndex = %u, pHistogram->Flags = %016llx, pHistogram->Version = %u", + pHistogram->PinId, m_Sensor->GetPreviewIndex(), pHistogram->Flags, pHistogram->Version); + return Status; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_FACEDETECTION. +NTSTATUS +CCaptureFilter:: +GetFaceDetection( + _Inout_ CExtendedVidProcSetting *pFaceDetect +) +{ + PAGED_CODE(); + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + DBG_ENTER("()"); + + if( pFaceDetect->PinId==KSCAMERA_EXTENDEDPROP_FILTERSCOPE && + pFaceDetect->isValid() ) + { + //Get the FaceDetection state + Status = m_Sensor->GetFaceDetection( pFaceDetect ); + } + + DBG_LEAVE("(): PinId=0x%08X, Flags=0x%016llX, Version=%u, Value=%d, Status=0x%08X", + pFaceDetect->PinId, pFaceDetect->Flags, pFaceDetect->Version, pFaceDetect->GetULONG(), Status); + return Status; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_FACEDETECTION. +NTSTATUS +CCaptureFilter:: +SetFaceDetection( + _In_ CExtendedVidProcSetting *pFaceDetect +) +{ + PAGED_CODE(); + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + DBG_ENTER("(): PinId=0x%08X, Flags=0x%016llX, Version=%u, Value=%d", + pFaceDetect->PinId, pFaceDetect->Flags, pFaceDetect->Version, pFaceDetect->GetULONG()); + + if( pFaceDetect->PinId==KSCAMERA_EXTENDEDPROP_FILTERSCOPE && + pFaceDetect->isValid() && + ( KSCAMERA_EXTENDEDPROP_FACEDETECTION_OFF == pFaceDetect->Flags || + (KSCAMERA_EXTENDEDPROP_FACEDETECTION_MASK & pFaceDetect->Flags) !=0 ) ) + { + CExtendedVidProcSetting Caps(*pFaceDetect); + Status = m_Sensor->GetFaceDetection( &Caps ); + + if( NT_SUCCESS(Status) ) + { + // Assume an invalid parameter. + Status = STATUS_INVALID_PARAMETER; + + if( pFaceDetect->Flags == (pFaceDetect->Flags & Caps.Capability) && + ( KSCAMERA_EXTENDEDPROP_FACEDETECTION_OFF == pFaceDetect->Flags || + NT_SUCCESS( Status = Caps.BoundsCheck( pFaceDetect->GetULONG() ) ) ) ) + { + //Set the FaceDetection state. + Status = m_Sensor->SetFaceDetection( pFaceDetect ); + } + } + } + + DBG_LEAVE("(): Status=0x%08X", Status); + return Status; +} + +// KSPROPERTY_CAMERACONTROL_EXTENDED_ZOOM Get handler +NTSTATUS +CCaptureFilter:: +GetZoom( + _Inout_ CExtendedVidProcSetting *pZoom +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( pZoom->isValid() && + pZoom->PinId == KSCAMERA_EXTENDEDPROP_FILTERSCOPE ) + { + Status = m_Sensor->GetZoom(pZoom); + } + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// KSPROPERTY_CAMERACONTROL_EXTENDED_ZOOM Set handler +NTSTATUS +CCaptureFilter:: +SetZoom( + _In_ CExtendedVidProcSetting *pZoom +) +{ + PAGED_CODE(); + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + DBG_ENTER("()"); + + // Validate + if( pZoom->isValid() && + pZoom->PinId == KSCAMERA_EXTENDEDPROP_FILTERSCOPE && + !(pZoom->Capability & KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL) ) + { + CExtendedVidProcSetting Caps(*pZoom); + Status = m_Sensor->GetZoom( &Caps ); + + if( NT_SUCCESS(Status) ) + { + // Report to the debug output... + DBG_TRACE(" Flags = 0x%016llX", Caps.Flags); + DBG_TRACE(" Min = %d", Caps.Min()); + DBG_TRACE(" Max = %d", Caps.Max()); + DBG_TRACE(" Step = %d", Caps.Step()); + DBG_TRACE("VideoProc.Value = %d", pZoom->GetLONG()); + + if( pZoom->Flags == (pZoom->Flags & Caps.Capability) ) + { + Status = BoundsCheckSigned( pZoom->GetLONG(), Caps.m_Setting ); + + if (NT_SUCCESS(Status)) + { + Status = m_Sensor->SetZoom(pZoom); + } + } + else + { + Status = STATUS_INVALID_PARAMETER; + } + } + } + + DBG_LEAVE("(PinId = %u, Flags = 0x%016llX, Caps = 0x%016llX)=0x%08X", + pZoom->PinId, pZoom->Flags, pZoom->Capability, Status); + return Status; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_VIDEOSTABILIZATION. +NTSTATUS +CCaptureFilter:: +GetVideoStabilization( + _Inout_ CExtendedProperty *pVideoStab +) +{ + PAGED_CODE(); + + if (m_Sensor->IsVideoIndex(pVideoStab->PinId) && + pVideoStab->isValid()) + { + //Get the current state + return m_Sensor->GetVideoStabilization(pVideoStab); + } + return STATUS_INVALID_PARAMETER; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_VIDEOSTABILIZATION. +NTSTATUS +CCaptureFilter:: +SetVideoStabilization( + _In_ CExtendedProperty *pVideoStab +) +{ + PAGED_CODE(); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + // Call must come for Video PIN only and flags must be supported and mutually exclusive and version must be 1 + if( m_Sensor->IsVideoIndex(pVideoStab->PinId) && + pVideoStab->isValid() && + !(pVideoStab->Capability & KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL) && + (KSCAMERA_EXTENDEDPROP_VIDEOSTABILIZATION_OFF == pVideoStab->Flags || + KSCAMERA_EXTENDEDPROP_VIDEOSTABILIZATION_ON == pVideoStab->Flags || + KSCAMERA_EXTENDEDPROP_VIDEOSTABILIZATION_AUTO == pVideoStab->Flags) ) + { + CExtendedProperty Caps(*pVideoStab); + Status = m_Sensor->GetVideoStabilization( &Caps ); + + if( NT_SUCCESS(Status) ) + { + // Assume an invalid parameter. + Status = STATUS_INVALID_PARAMETER; + + // Call must come for Video PIN only and flags must be supported and mutually exclusive and version must be 1 + if( pVideoStab->Flags == (pVideoStab->Flags & Caps.Capability) ) + { + LockFilter Lock(m_pKSFilter); + + if( m_pinArray[pVideoStab->PinId] && + m_pinArray[pVideoStab->PinId]->GetState() != PinStopped ) + { + Status = STATUS_INVALID_DEVICE_STATE; + } + else + { + Status = m_Sensor->SetVideoStabilization(pVideoStab); + } + } + } + } + DBG_TRACE("pVideoStab = %p, PinId = %u, m_sensor->VideoIndex = %u, Flags = %llu, Version = %u", + pVideoStab, pVideoStab->PinId, m_Sensor->GetVideoIndex(), pVideoStab->Flags, pVideoStab->Version); + + return Status; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_OIS. +NTSTATUS +CCaptureFilter:: +GetOpticalImageStabilization( + _Inout_ CExtendedProperty *pOIS +) +{ + PAGED_CODE(); + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( pOIS->PinId==KSCAMERA_EXTENDEDPROP_FILTERSCOPE && + pOIS->isValid() ) + { + //Get the current state + Status = m_Sensor->GetOpticalImageStabilization(pOIS); + } + + DBG_TRACE("pOIS = %p, PinId = %u, Flags = %llu, Version = %u", + pOIS, pOIS->PinId, pOIS->Flags, pOIS->Version); + + return Status; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_OIS. +NTSTATUS +CCaptureFilter:: +SetOpticalImageStabilization( + _In_ CExtendedProperty *pOIS +) +{ + PAGED_CODE(); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + // Call must come for Video PIN only and flags must be supported and mutually exclusive and version must be 1 + if( pOIS->PinId==KSCAMERA_EXTENDEDPROP_FILTERSCOPE && + pOIS->isValid() && + !(pOIS->Capability & KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL) && + (KSCAMERA_EXTENDEDPROP_OIS_OFF == pOIS->Flags || + KSCAMERA_EXTENDEDPROP_OIS_ON == pOIS->Flags || + KSCAMERA_EXTENDEDPROP_OIS_AUTO== pOIS->Flags)) + { + CExtendedProperty Caps(*pOIS); + Status = m_Sensor->GetOpticalImageStabilization(&Caps); + + if( NT_SUCCESS(Status) ) + { + // Assume an invalid parameter. + Status = STATUS_INVALID_PARAMETER; + + if( pOIS->Flags == (pOIS->Flags & Caps.Capability) ) + { + Status = m_Sensor->SetOpticalImageStabilization(pOIS); + } + } + } + DBG_TRACE("pOIS = %p, PinId = %u, Flags = %llu, Version = %u", + pOIS, pOIS->PinId, pOIS->Flags, pOIS->Version); + + return Status; +} + +// Get KSPROPERTY_CAMERACONTROL_EXTENDED_ADVANCEDPHOTO. +NTSTATUS +CCaptureFilter:: +GetAdvancedPhoto( + _Inout_ CExtendedProperty *pAdvancedPhoto +) +{ + PAGED_CODE(); + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( m_Sensor->IsStillIndex(pAdvancedPhoto->PinId) && + pAdvancedPhoto->isValid() ) + { + //Get the current state + Status = m_Sensor->GetAdvancedPhoto(pAdvancedPhoto); + } + + DBG_LEAVE(" PinId=%u, Flags=0x%016llX, Version=%u, Status=0x%08X", + pAdvancedPhoto->PinId, pAdvancedPhoto->Flags, pAdvancedPhoto->Version, Status); + return Status; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_ADVANCEDPHOTO. +NTSTATUS +CCaptureFilter:: +SetAdvancedPhoto( + _In_ CExtendedProperty *pAdvancedPhoto +) +{ + PAGED_CODE(); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + // Valid request, still pin & no unsupported flags. + if( m_Sensor->IsStillIndex(pAdvancedPhoto->PinId) && + pAdvancedPhoto->isValid() && + !(pAdvancedPhoto->Capability & KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL) ) + { + CExtendedProperty Caps(*pAdvancedPhoto); + Status = m_Sensor->GetAdvancedPhoto(&Caps); + + if( NT_SUCCESS(Status) ) + { + // Assume an invalid parameter. + Status = STATUS_INVALID_PARAMETER; + + if( pAdvancedPhoto->Flags == (pAdvancedPhoto->Flags & Caps.Capability) ) + { + // Make sure the requested flags are a valid capability combination. + switch( pAdvancedPhoto->Flags ) + { + case KSCAMERA_EXTENDEDPROP_ADVANCEDPHOTO_OFF : + case KSCAMERA_EXTENDEDPROP_ADVANCEDPHOTO_AUTO : + case KSCAMERA_EXTENDEDPROP_ADVANCEDPHOTO_HDR : + case KSCAMERA_EXTENDEDPROP_ADVANCEDPHOTO_FNF : + case KSCAMERA_EXTENDEDPROP_ADVANCEDPHOTO_ULTRALOWLIGHT: + case KSCAMERA_EXTENDEDPROP_ADVANCEDPHOTO_AUTO | + KSCAMERA_EXTENDEDPROP_ADVANCEDPHOTO_HDR : + case KSCAMERA_EXTENDEDPROP_ADVANCEDPHOTO_AUTO | + KSCAMERA_EXTENDEDPROP_ADVANCEDPHOTO_FNF : + case KSCAMERA_EXTENDEDPROP_ADVANCEDPHOTO_AUTO | + KSCAMERA_EXTENDEDPROP_ADVANCEDPHOTO_ULTRALOWLIGHT: + Status = m_Sensor->SetAdvancedPhoto(pAdvancedPhoto); + } + } + } + } + + DBG_LEAVE(" PinId=%u, Flags=0x%016llX, Version=%u, Status=0x%08X", + pAdvancedPhoto->PinId, pAdvancedPhoto->Flags, pAdvancedPhoto->Version, Status); + return Status; +} + +// Get [legacy] KSPROPERTY_CAMERACONTROL_EXPOSURE +NTSTATUS +CCaptureFilter:: +GetCameraProfile( + _Inout_ CExtendedProfile *pProfile +) +{ + PAGED_CODE(); + NTSTATUS Status = STATUS_SUCCESS; + + pProfile->Version = KSCAMERA_EXTENDEDPROP_VERSION; + pProfile->PinId = KSCAMERA_EXTENDEDPROP_FILTERSCOPE; + pProfile->Size = sizeof(KSCAMERA_EXTENDEDPROP_HEADER) + sizeof (KSCAMERA_EXTENDEDPROP_PROFILE); + pProfile->Result = 0; + pProfile->Flags = 0; + pProfile->Capability = KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL; + pProfile->m_Profile = m_Profile; + + return Status; +} + +// Set KSPROPERTY_CAMERACONTROL_EXTENDED_PROFILE. +NTSTATUS +CCaptureFilter:: +SetCameraProfile( + _In_ CExtendedProfile *pProfile +) +{ + PAGED_CODE(); + + if (pProfile->PinId != KSCAMERA_EXTENDEDPROP_FILTERSCOPE || + !pProfile->isValid() || + 0 == (pProfile->Capability & KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL) || + pProfile->Flags != 0) + { + return STATUS_INVALID_PARAMETER; + } + m_Profile = pProfile->m_Profile; + + m_ProfileNotifier.Set(); + return STATUS_SUCCESS; +} + + +NTSTATUS +CCaptureFilter:: +GetExposure( + _Inout_ KSPROPERTY_CAMERACONTROL_S *pProperty +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = m_Sensor->GetExposure(pProperty); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Set [legacy] KSPROPERTY_CAMERACONTROL_EXPOSURE +NTSTATUS +CCaptureFilter:: +SetExposure( + _In_ KSPROPERTY_CAMERACONTROL_S *pProperty +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = m_Sensor->SetExposure(pProperty); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Get [legacy] KSPROPERTY_CAMERACONTROL_FOCUS +NTSTATUS +CCaptureFilter:: +GetFocus( + _Inout_ PKSPROPERTY_CAMERACONTROL_S pProperty +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = m_Sensor->GetFocus(pProperty); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Set [legacy] KSPROPERTY_CAMERACONTROL_FOCUS +NTSTATUS +CCaptureFilter:: +SetFocus( + _In_ PKSPROPERTY_CAMERACONTROL_S pProperty +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + m_Sensor->CancelFocus(); + + NTSTATUS Status = m_Sensor->SetFocus(pProperty); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Get [legacy] KSPROPERTY_CAMERACONTROL_ZOOM +NTSTATUS +CCaptureFilter:: +GetZoom( + _Inout_ PKSPROPERTY_CAMERACONTROL_S pProperty +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = m_Sensor->GetZoom(pProperty); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Set [legacy] KSPROPERTY_CAMERACONTROL_ZOOM +NTSTATUS +CCaptureFilter:: +SetZoom( + _In_ PKSPROPERTY_CAMERACONTROL_S pProperty +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = m_Sensor->SetZoom(pProperty); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Get [legacy] KSPROPERTY_CAMERACONTROL_ZOOM_RELATIVE +NTSTATUS +CCaptureFilter:: +GetZoomRelative( + _Inout_ PKSPROPERTY_CAMERACONTROL_S pProperty +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = m_Sensor->GetZoomRelative(pProperty); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Set [legacy] KSPROPERTY_CAMERACONTROL_ZOOM_RELATIVE +NTSTATUS +CCaptureFilter:: +SetZoomRelative( + _In_ PKSPROPERTY_CAMERACONTROL_S pProperty +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = m_Sensor->SetZoomRelative(pProperty); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Get KSPROPERTY_CAMERACONTROL_PAN +NTSTATUS +CCaptureFilter:: +GetPan( + _Inout_ PKSPROPERTY_CAMERACONTROL_S pProperty +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = m_Sensor->GetPan(pProperty); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Set KSPROPERTY_CAMERACONTROL_PAN +NTSTATUS +CCaptureFilter:: +SetPan( + _In_ PKSPROPERTY_CAMERACONTROL_S pProperty +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = m_Sensor->SetPan(pProperty); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Get KSPROPERTY_CAMERACONTROL_ROLL +NTSTATUS +CCaptureFilter:: +GetRoll( + _Inout_ PKSPROPERTY_CAMERACONTROL_S pProperty +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = m_Sensor->GetRoll(pProperty); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Set KSPROPERTY_CAMERACONTROL_ROLL +NTSTATUS +CCaptureFilter:: +SetRoll( + _In_ PKSPROPERTY_CAMERACONTROL_S pProperty +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = m_Sensor->SetRoll(pProperty); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Get KSPROPERTY_CAMERACONTROL_TILT +NTSTATUS +CCaptureFilter:: +GetTilt( + _Inout_ PKSPROPERTY_CAMERACONTROL_S pProperty +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = m_Sensor->GetTilt(pProperty); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Set KSPROPERTY_CAMERACONTROL_TILT +NTSTATUS +CCaptureFilter:: +SetTilt( + _In_ PKSPROPERTY_CAMERACONTROL_S pProperty +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = m_Sensor->SetTilt(pProperty); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Get KSPROPERTY_CAMERACONTROL_FOCAL_LENGTH +NTSTATUS +CCaptureFilter:: +GetFocalLength( + _Inout_ PKSPROPERTY_CAMERACONTROL_FOCAL_LENGTH_S pProperty +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = m_Sensor->GetFocalLength(pProperty); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Set KSPROPERTY_CAMERACONTROL_FOCAL_LENGTH +NTSTATUS +CCaptureFilter:: +SetFocalLength( + _In_ PKSPROPERTY_CAMERACONTROL_FOCAL_LENGTH_S pProperty +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = m_Sensor->SetFocalLength(pProperty); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Get KSPROPERTY_VIDEOPROCAMP_BACKLIGHT_COMPENSATION +NTSTATUS +CCaptureFilter:: +GetBacklightCompensation( + _Inout_ PKSPROPERTY_VIDEOPROCAMP_S pProperty +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = m_Sensor->GetBacklightCompensation(pProperty); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Set KSPROPERTY_VIDEOPROCAMP_BACKLIGHT_COMPENSATION +NTSTATUS +CCaptureFilter:: +SetBacklightCompensation( + _In_ PKSPROPERTY_VIDEOPROCAMP_S pProperty +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = m_Sensor->SetBacklightCompensation(pProperty); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Get KSPROPERTY_VIDEOPROCAMP_BRIGHTNESS +NTSTATUS +CCaptureFilter:: +GetBrightness( + _Inout_ PKSPROPERTY_VIDEOPROCAMP_S pProperty +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = m_Sensor->GetBrightness(pProperty); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Set KSPROPERTY_VIDEOPROCAMP_BRIGHTNESS +NTSTATUS +CCaptureFilter:: +SetBrightness( + _In_ PKSPROPERTY_VIDEOPROCAMP_S pProperty +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = m_Sensor->SetBrightness(pProperty); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Get KSPROPERTY_VIDEOPROCAMP_CONTRAST +NTSTATUS +CCaptureFilter:: +GetContrast( + _Inout_ PKSPROPERTY_VIDEOPROCAMP_S pProperty +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = m_Sensor->GetContrast(pProperty); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Set KSPROPERTY_VIDEOPROCAMP_CONTRAST +NTSTATUS +CCaptureFilter:: +SetContrast( + _In_ PKSPROPERTY_VIDEOPROCAMP_S pProperty +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = m_Sensor->SetContrast(pProperty); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Get KSPROPERTY_VIDEOPROCAMP_HUE +NTSTATUS +CCaptureFilter:: +GetHue( + _Inout_ PKSPROPERTY_VIDEOPROCAMP_S pProperty +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = m_Sensor->GetHue(pProperty); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Set KSPROPERTY_VIDEOPROCAMP_HUE +NTSTATUS +CCaptureFilter:: +SetHue( + _In_ PKSPROPERTY_VIDEOPROCAMP_S pProperty +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = m_Sensor->SetHue(pProperty); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Get [legacy] KSPROPERTY_VIDEOPROCAMP_WHITEBALANCE +NTSTATUS +CCaptureFilter:: +GetWhiteBalance( + _Inout_ PKSPROPERTY_VIDEOPROCAMP_S pProperty +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = m_Sensor->GetWhiteBalance(pProperty); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Set [legacy] KSPROPERTY_VIDEOPROCAMP_WHITEBALANCE +NTSTATUS +CCaptureFilter:: +SetWhiteBalance( + _In_ PKSPROPERTY_VIDEOPROCAMP_S pProperty +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = m_Sensor->SetWhiteBalance(pProperty); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Get KSPROPERTY_VIDEOPROCAMP_POWERLINE_FREQUENCY +NTSTATUS +CCaptureFilter:: +GetPowerlineFreq( + _Inout_ PKSPROPERTY_VIDEOPROCAMP_S pProperty +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = m_Sensor->GetPowerlineFreq(pProperty); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Set KSPROPERTY_VIDEOPROCAMP_POWERLINE_FREQUENCY +NTSTATUS +CCaptureFilter:: +SetPowerlineFreq( + _In_ PKSPROPERTY_VIDEOPROCAMP_S pProperty +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = m_Sensor->SetPowerlineFreq(pProperty); + + DBG_LEAVE("()=0x%08X", Status); + return Status; +} + +// Get KSPROPERTY_VIDEOCONTROL_MODE. +NTSTATUS +CCaptureFilter:: +GetVideoControlMode( + _Inout_ KSPROPERTY_VIDEOCONTROL_MODE_S *pMode +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( m_Sensor->IsStillIndex(pMode->StreamIndex) ) + { + Status = m_Sensor->GetVideoControlMode( pMode ); + } + + DBG_LEAVE("( Pin=%d, Mode=0x%08X )=0x%08X", + pMode->StreamIndex, pMode->Mode, Status ); + return Status; +} + +// Set KSPROPERTY_VIDEOCONTROL_MODE. +NTSTATUS +CCaptureFilter:: +SetVideoControlMode( + _In_ KSPROPERTY_VIDEOCONTROL_MODE_S *pMode +) +{ + PAGED_CODE(); + DBG_ENTER("()"); + + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + if( m_Sensor->IsStillIndex(pMode->StreamIndex) ) + { + KSPROPERTY_VIDEOCONTROL_MODE_S Caps; + RtlZeroMemory( &Caps, sizeof(Caps) ); + + Status = m_Sensor->GetVideoControlMode( &Caps ); + if( NT_SUCCESS(Status) )//&& + // pMode->Mode == (pMode->Mode & Caps.Mode) ) + { + DBG_TRACE("Mode=0x%08X, Caps.Mode=0x%08X", pMode->Mode, Caps.Mode); + Status = m_Sensor->SetVideoControlMode( pMode ); + } + else + { + Status = STATUS_INVALID_PARAMETER; + } + } + + DBG_LEAVE("( Pin=%d, Mode=0x%08X )=0x%08X", + pMode->StreamIndex, pMode->Mode, Status ); + return Status; +} + diff --git a/avscamera/sys/filter.h b/avscamera/sys/filter.h new file mode 100644 index 000000000..5b3c06f59 --- /dev/null +++ b/avscamera/sys/filter.h @@ -0,0 +1,482 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2001, Microsoft Corporation. + + File: + + filter.h + + Abstract: + + This file contains the filter level header for the base capture filter + class. It provides a wrapper on KSFILTER. + + CCaptureFilter holds state information for the filter and contains + implementations for standard camera controls. These implementations + validate arguments and capabilities of the control according to DDI + definitions. Once validated, control is passes to a CSensor object + to manipulate the state of camera. + + CCaptureFilter holds a reference to any active CCapturePins that may + have been created on this filter. The reference is used to obtain + the state of the pin and potentially modify the pin's allocator + framing. + + To add a new control, derive from CCaptureFilter and use one of the + DECLARE_PROPERTY_XXX() macros below to declare the property handlers. + You can also use the DEFINE_PROP_ITEM_XXX() and DEFINE_STD_EVENT_ITEM() + macros as aids in constructing a filter's automation table. + + History: + + created 3/12/2001 + +**************************************************************************/ + +extern KSPROPERTY_VALUES ZoomRelativeValues; +extern KSPROPERTY_VALUES ZoomValues; +extern KSPROPERTY_VALUES PanValues; +extern KSPROPERTY_VALUES RollValues; +extern KSPROPERTY_VALUES TiltValues; +extern KSPROPERTY_VALUES BacklightCompensationValues; +extern KSPROPERTY_VALUES BrightnessValues; +extern KSPROPERTY_VALUES ContrastValues; +extern KSPROPERTY_VALUES HueValues; +extern KSPROPERTY_VALUES PLFValues; +extern KSPROPERTY_VALUES WhiteBalanceValues; +extern KSPROPERTY_VALUES ExposureValues; +extern KSPROPERTY_VALUES FocusValues; + +class CCapturePin; +class CCaptureFilter +{ +private: + + // + // The AVStream filter object associated with this CCaptureFilter. + // + CCapturePin **m_pinArray; + +protected: + // + // Setup, memory allocation, etc. + // + NTSTATUS + Initialize(); + + // + // Cleanup + // + void + Cleanup(); + +public: + + // + // setPin() + // + // Attach a CCapturePin. + // + void + setPin( + _In_ CCapturePin *pPin, + _In_ unsigned Id + ) ; + + // + // getPin() + // + // Query an attached CCapturePin. + // + CCapturePin * + getPin( + _In_ unsigned Id + ) ; + + // + // CCaptureFilter() + // + // Constructor. + // + CCaptureFilter ( + _In_ PKSFILTER Filter + ); + + // + // ~CCaptureFilter(): + // + // The capture filter destructor. + // + virtual + ~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 + ); + + // + // Close(): Called in response to IRP_MJ_CLOSE. + // + static + NTSTATUS + DispatchClose( + _In_ PKSFILTER Filter, + _In_ PIRP Irp + ); + + LARGE_INTEGER m_StartTime; + CCaptureDevice *m_pCaptureDevice; + CSensor *m_Sensor; + PKSFILTER m_pKSFilter; + PKSCAMERA_PERFRAMESETTING_HEADER m_pPerFrameSettings; + ULONG m_PFSSize; + + CNotifier m_PhotoModeNotifier; + CNotifier m_PhotoMaxFrameRateNotifier; + CNotifier m_FocusNotifier; + CNotifier m_FocusRectNotifier; + CNotifier m_IsoNotifier; + CNotifier m_IsoAdvancedNotifier; + CNotifier m_EvCompensationNotifier; + CNotifier m_WhiteBalanceNotifier; + CNotifier m_ExposureNotifier; + CNotifier m_SceneModeNotifier; + CNotifier m_ThumbnailNotifier; + CNotifier m_WarmStartNotifier; + CNotifier m_RoiNotifier; + CNotifier m_ProfileNotifier; + + // + // The following static member template function is used to validate + // most KSPROPERTYs and thunk that call down to a handler that is a non- + // static member of CCaptureFilter (or a derived class). + // + // Note: A separate instance of this thunking function is instantiated for + // each control, so code space is not conserved. However, performing + // this logic in only one place ensures consistant behavior across + // controls and reduces the chances for mistakes. + // + template + + __declspec(code_seg("PAGE")) // Put this function into paged code. + static + NTSTATUS + Prop( + _In_ PIRP pIrp, + _In_ PKSPROPERTY pProperty, + _Inout_ PVOID pData) + { + PAGED_CODE(); + UNREFERENCED_PARAMETER(pProperty); + + NTSTATUS Status = STATUS_SUCCESS; + NT_ASSERT(pIrp); + + FILTER *pFilter = reinterpret_cast (KsGetFilterFromIrp(pIrp)->Context); + PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation(pIrp); + ULONG ulOutputBufferLength = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength; + OUTPUT *pOutput = reinterpret_cast(pData); + + if (ulOutputBufferLength == 0) + { + pIrp->IoStatus.Information = sizeof(OUTPUT); + Status = STATUS_BUFFER_OVERFLOW; + } + else if (ulOutputBufferLength < sizeof(OUTPUT)) + { + Status = STATUS_BUFFER_TOO_SMALL; + } + else if (pData && ulOutputBufferLength >= sizeof(OUTPUT)) + { + Status = (pFilter->*pFun)( pOutput ); + if( NT_SUCCESS(Status) ) + { + pIrp->IoStatus.Information = sizeof(OUTPUT); + } + } + else + { + Status = STATUS_INVALID_PARAMETER; + } + + return Status; + } + + // Get Per Frame Setting Caps + static + NTSTATUS + GetPFSCaps( + _In_ PIRP pIrp, + _In_ PKSPROPERTY pProperty, + _Inout_ PVOID pData + ); + + // Get current Per Frame Settings + static + NTSTATUS + GetPerFrameSettings( + _In_ PIRP pIrp, + _In_ PKSPROPERTY pProperty, + _Inout_ PVOID pData + ); + + // Set new Per Frame Settings + static + NTSTATUS + SetPerFrameSettings( + _In_ PIRP pIrp, + _In_ PKSPROPERTY pProperty, + _Inout_ PVOID pData + ); + + // Clear Per Frame Settings + static + NTSTATUS + ClearPerFrameSettings( + _In_ PIRP pIrp, + _In_ PKSPROPERTY pProperty, + _Inout_ PVOID pData + ); + +// +// DECLARE_PROPERTY_XXX() macros. +// +// The follow macros declare a property handler using a common naming +// convention. This convention is unique to this sample code. The +// convention is used again in the DEFINE_PROP_ITEM_XXX() macros during +// construction of the automation tables. +// +// Note: These definitions are non-static members of the filter. +// + +// Declare a property's GET handler. +#define DECLARE_PROPERTY_GET_HANDLER( type, name ) \ + NTSTATUS \ + Get##name( \ + _Inout_ type *pOutput \ + ); + +// Declare a property's SET handler. +#define DECLARE_PROPERTY_SET_HANDLER( type, name ) \ + NTSTATUS \ + Set##name( \ + _In_ type *pOutput \ + ); + +// Declare a property's GET and SET handlers. +#define DECLARE_PROPERTY_HANDLERS( type, name ) \ +DECLARE_PROPERTY_GET_HANDLER( type, name ) \ +DECLARE_PROPERTY_SET_HANDLER( type, name ) + + // + // Declaration of all the standard control handlers that use fixed structures. + // + DECLARE_PROPERTY_HANDLERS( CExtendedPhotoMode, PhotoMode ); + DECLARE_PROPERTY_GET_HANDLER( CExtendedProperty, PhotoFrameRate ); + DECLARE_PROPERTY_HANDLERS( CExtendedProperty, PhotoMaxFrameRate ); + DECLARE_PROPERTY_HANDLERS( CExtendedProperty, WarmStart ); + DECLARE_PROPERTY_HANDLERS( CExtendedMaxVideoFpsForPhotoRes, MaxVideoFpsForPhotoRes ); + DECLARE_PROPERTY_GET_HANDLER( CExtendedFieldOfView, FieldOfView ); + DECLARE_PROPERTY_GET_HANDLER( CExtendedCameraAngleOffset, CameraAngleOffset ); + DECLARE_PROPERTY_HANDLERS( KSPROPERTY_CAMERACONTROL_REGION_OF_INTEREST_S, FocusRect ); + DECLARE_PROPERTY_HANDLERS( CExtendedVidProcSetting, Focus ); + DECLARE_PROPERTY_GET_HANDLER( CExtendedProperty, FocusState ); + DECLARE_PROPERTY_HANDLERS( CExtendedProperty, FocusPriority ); + DECLARE_PROPERTY_HANDLERS( CExtendedProperty, ExtendedFlash ) + DECLARE_PROPERTY_HANDLERS( CExtendedProperty, Iso ) + DECLARE_PROPERTY_HANDLERS( CExtendedVidProcSetting, IsoAdvanced ) + DECLARE_PROPERTY_HANDLERS( CExtendedEvCompensation, EvCompensation ) + DECLARE_PROPERTY_HANDLERS( CExtendedVidProcSetting, WhiteBalance ) + DECLARE_PROPERTY_HANDLERS( CExtendedVidProcSetting, Exposure ) + DECLARE_PROPERTY_HANDLERS( CExtendedVidProcSetting, FaceDetection ) + DECLARE_PROPERTY_HANDLERS( CExtendedProperty, PhotoConfirmation ) + DECLARE_PROPERTY_HANDLERS( CExtendedProperty, SceneMode ) + DECLARE_PROPERTY_HANDLERS( CExtendedProperty, VFR ) + DECLARE_PROPERTY_HANDLERS( CExtendedProperty, VideoHDR ) + DECLARE_PROPERTY_HANDLERS( CExtendedVidProcSetting, Zoom ) + DECLARE_PROPERTY_HANDLERS( CExtendedProperty, VideoStabilization) + DECLARE_PROPERTY_HANDLERS( CExtendedProperty, Histogram ) + DECLARE_PROPERTY_HANDLERS( CExtendedMetadata, Metadata ) + DECLARE_PROPERTY_HANDLERS( CExtendedProperty, OpticalImageStabilization ) + DECLARE_PROPERTY_HANDLERS( CExtendedProperty, OptimizationHint ) + DECLARE_PROPERTY_HANDLERS( CExtendedProperty, AdvancedPhoto ) + DECLARE_PROPERTY_HANDLERS( CExtendedProfile, CameraProfile ) + DECLARE_PROPERTY_HANDLERS( CExtendedProperty, Thumbnail ) + DECLARE_PROPERTY_HANDLERS( CExtendedProperty, TriggerTime ) + DECLARE_PROPERTY_HANDLERS( CExtendedProperty, TorchMode ) + + DECLARE_PROPERTY_HANDLERS( KSPROPERTY_CAMERACONTROL_VIDEOSTABILIZATION_MODE_S, VideoStabMode ) + DECLARE_PROPERTY_HANDLERS( KSPROPERTY_CAMERACONTROL_FLASH_S, Flash ) + DECLARE_PROPERTY_GET_HANDLER( KSPROPERTY_CAMERACONTROL_IMAGE_PIN_CAPABILITY_S, PinDependence ) + + DECLARE_PROPERTY_HANDLERS(KSPROPERTY_CAMERACONTROL_S, Exposure); + DECLARE_PROPERTY_HANDLERS(KSPROPERTY_CAMERACONTROL_S, Focus); + DECLARE_PROPERTY_HANDLERS(KSPROPERTY_CAMERACONTROL_S, Zoom); + DECLARE_PROPERTY_HANDLERS(KSPROPERTY_CAMERACONTROL_S, ZoomRelative); + DECLARE_PROPERTY_HANDLERS(KSPROPERTY_CAMERACONTROL_S, Pan); + DECLARE_PROPERTY_HANDLERS(KSPROPERTY_CAMERACONTROL_S, Roll); + DECLARE_PROPERTY_HANDLERS(KSPROPERTY_CAMERACONTROL_S, Tilt); + DECLARE_PROPERTY_HANDLERS(KSPROPERTY_CAMERACONTROL_FOCAL_LENGTH_S, FocalLength); + + DECLARE_PROPERTY_HANDLERS(KSPROPERTY_VIDEOPROCAMP_S, BacklightCompensation); + DECLARE_PROPERTY_HANDLERS(KSPROPERTY_VIDEOPROCAMP_S, Brightness); + DECLARE_PROPERTY_HANDLERS(KSPROPERTY_VIDEOPROCAMP_S, Contrast); + DECLARE_PROPERTY_HANDLERS(KSPROPERTY_VIDEOPROCAMP_S, Hue); + DECLARE_PROPERTY_HANDLERS(KSPROPERTY_VIDEOPROCAMP_S, WhiteBalance); + DECLARE_PROPERTY_HANDLERS(KSPROPERTY_VIDEOPROCAMP_S, PowerlineFreq); + + DECLARE_PROPERTY_GET_HANDLER( CRoiConfig, RoiConfigCaps ); + + DECLARE_PROPERTY_HANDLERS(KSPROPERTY_VIDEOCONTROL_MODE_S, VideoControlMode); + + // + // The following are property handlers that use an atypical variable + // length (not known at compile-time) parameter list. + // + // Since we only have a few, declare them explicitly. + // + + // KSPROPERTY_CAMERACONTROL_EXTENDED_ROI_ISPCONTROL Get handler + static + NTSTATUS + GetRoiIspControl( + _In_ PIRP pIrp, + _In_ PKSPROPERTY pProperty, + _Inout_ PVOID pData + ); + + // KSPROPERTY_CAMERACONTROL_EXTENDED_ROI_ISPCONTROL Set handler + static + NTSTATUS + SetRoiIspControl( + _In_ PIRP pIrp, + _In_ PKSPROPERTY pProperty, + _Inout_ PVOID pData); + + // Make sure the PERFRAMESETTINGs passed to us are valid. + _Success_(return == 0) + NTSTATUS + CCaptureFilter::ParsePFSBuffer( + _In_reads_bytes_(BufferLimit) + PKSCAMERA_PERFRAMESETTING_HEADER pPFS, + _In_ ULONG BufferLimit, + _Outptr_opt_result_maybenull_ + ISP_FRAME_SETTINGS **ppSettings ); + + // Helper function for finding our filter object. + static + CCaptureFilter * + GetFilter( + _In_ PKSPIN Pin + ) + { + return reinterpret_cast + (KsPinGetParentFilter(Pin)->Context); + } + + // Accessor to convert a reference to a CCaptureFilter to a PKSFILTER. + operator PKSFILTER() + { + return m_pKSFilter; + } + + // Get the name of the filter from the context structure passed in at initialization. + LPCWSTR GetFilterName() + { + return m_pCaptureDevice->GetFilterName(m_pCaptureDevice->GetFilterIndex(m_pKSFilter)); + } + + // Update the pin's allocator to a specific frame count. + NTSTATUS + UpdateAllocatorFraming( + _In_ ULONG PinId + ); + +private: + // An arbitrary limit on Per-Frame settings. + static + const ULONG MAX_FRAME_COUNT=512; + + // Filter level state information. + KSCAMERA_EXTENDEDPROP_PROFILE m_Profile; +}; + +// +// Helper definitions for creating custom property sets +// +// Note: Property automation tables require a statically defined accessor +// functions, however our properties functions are (mostly) implemented +// as non-static member functions. The following macros provide a +// static member thunking function by invoking a member template +// function called Prop<>. The Prop<> performs limited validation on +// the buffer, provides back the size if the buffer size was 0, picks +// up a pointer to the filter through the IRP and calls the member +// function that handles the property. +// + +#define DEFINE_PROP_ITEM_NO_SET( T, ctrl, type, name ) \ + DEFINE_KSPROPERTY_ITEM( \ + ctrl, \ + (&T::Prop), \ + sizeof(KSPROPERTY), \ + 0, \ + NULL, \ + NULL, 0, NULL, NULL, 0 \ + ) + +#define DEFINE_PROP_ITEM_WITH_VALUES( T, ctrl, type, name, values ) \ + DEFINE_KSPROPERTY_ITEM( \ + ctrl, \ + (&T::Prop), \ + sizeof(KSPROPERTY), \ + 0, \ + (&T::Prop), \ + values, 0, NULL, NULL, 0 \ + ) + +#define DEFINE_PROP_ITEM( T, ctrl, type, name ) \ + DEFINE_PROP_ITEM_WITH_VALUES( T, ctrl, type, name, NULL ) + +// +// A simplification for defining a KSEVENT. We only need to specify one parameter. +// +#define DEFINE_STD_EVENT_ITEM(EVENTID) \ + DEFINE_KSEVENT_ITEM \ + ( \ + EVENTID, \ + sizeof(KSEVENTDATA), \ + 0, \ + NULL, \ + NULL, \ + NULL \ + ) + +// +// A simplification for defining a KSPROPERTY_SET. We only need to specify two parameters. +// +#define DEFINE_STD_PROPERTY_SET(SET, PROPERTIES) \ + DEFINE_KSPROPERTY_SET( \ + &SET, \ + SIZEOF_ARRAY(PROPERTIES), \ + PROPERTIES, \ + 0, \ + NULL) + +const LONGLONG MAX_EXPOSURE_TIME = 10000000ULL * 60 * 60; // 1 min +const LONG MIN_EXPOSURE_TIME=10000; // 1 ms +const LONG DEF_EXPOSURE_TIME=10000 * 30; // 30 ms. diff --git a/avscamera/sys/hwsim.cpp b/avscamera/sys/hwsim.cpp new file mode 100644 index 000000000..2c474e56a --- /dev/null +++ b/avscamera/sys/hwsim.cpp @@ -0,0 +1,1395 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2001, Microsoft Corporation. + + File: + + hwsim.cpp + + Abstract: + + This file contains the implementation of CHardwareSimulation used to + simulate a camera's capture stream. + + Our simulation cannot mimic a real camera with any precision. A real + camera would likely have dedicated hardware to provide interrupts, do + DMA transfers of frames from an internal buffer as well as do scaling + and on-the-fly colorspace and planar format conversions so as to supply + frames for preview, video and photo in varying resolutions and + colorspaces. + + This simulation does not have such hardware, so instead it synthesizes + a frame on the fly for each local pin as required. The frame is + synthesized from common state information, so the images produced + should be comparable from pin to pin. CHardwareSimulation is used as + a base class for each simulated pin / hardware. + + History: + + created 3/9/2001 + +**************************************************************************/ + +#include "Common.h" + +/************************************************************************** + + PAGEABLE CODE + +**************************************************************************/ + +#ifdef ALLOC_PRAGMA +#pragma code_seg("PAGE") +#endif // ALLOC_PRAGMA + + +inline +CHwSimTimer:: +CHwSimTimer( + _In_ CHardwareSimulation *Parent +) + : KPassiveTimer( Parent->m_Sensor->GetDeviceObject() ) + , m_Parent( Parent ) +/*++ + +Routine Description: + + CHwSimTimer object construction. + +Arguments: + + Parent - + The parent CHardwareSimulation. + +Return Value: + + None + +--*/ +{ + PAGED_CODE(); +} + +void +CHwSimTimer::Handler( + _In_opt_ PVOID Context +) +/*++ + +Routine Description: + + Punts to the HardwareSim... + +Arguments: + + Context - + Our parent CHardwareSimulation. + +Return Value: + + None + +--*/ +{ + PAGED_CODE(); + ((CHardwareSimulation *) Context)->FakeHardware(); +} + +/*************************************************/ + +CHardwareSimulation:: +CHardwareSimulation ( + _Inout_ CSensor *Sensor, + _In_ LONG PinID +) + : m_Sensor (Sensor) + , m_IsrTimer(this) + , m_ScatterGatherMappingsMax (SCATTER_GATHER_MAPPINGS_MAX) + , m_TimePerFrame(ONESECOND/30) + , m_PinID(PinID) + , m_Synthesizer(nullptr) + , m_Width(0) + , m_Height(0) + , m_ImageSize(0) + , m_PinState(PinStopped) + , m_NumMappingsCompleted(0) + , m_ScatterGatherMappingsQueued(0) + , m_ScatterGatherBytesQueued(0) + , m_NumFramesSkipped(0) + , m_InterruptTime(0) + , m_LastReportedExposureTime(DEF_EXPOSURE_TIME) // Assume the default exposure time for now. + , m_LastReportedWhiteBalance(0) + , m_FaceDetectionDelay(1) // Start out reporting immediately. + +/*++ + +Routine Description: + + CHardwareSimulation Construction. + +Arguments: + + Sensor - + The parent CSensor object that contains state for the controls. + PinID - + The pin index. + +Return Value: + + None + +--*/ +{ + + PAGED_CODE(); + + // + // Initialize the timer's, and locks necessary to simulate this hardware. + // + InitializeListHead (&m_ScatterGatherMappings); + + // + // Initialize the entry lookaside. + // + ExInitializeNPagedLookasideList ( + &m_ScatterGatherLookaside, + NULL, + NULL, + POOL_NX_ALLOCATION, + sizeof (SCATTER_GATHER_ENTRY), + 'nEGS', + 0 + ); + + m_StartTime.QuadPart = 0; +} + +CHardwareSimulation:: +~CHardwareSimulation() +{ + PAGED_CODE(); + + // Cancel the timer, just in case its running. + m_IsrTimer.Cancel(); + + // + // Delete the scatter / gather lookaside list. + // + ExDeleteNPagedLookasideList (&m_ScatterGatherLookaside); +} + +/*************************************************/ + +BOOLEAN +CHardwareSimulation:: +Initialize() + +/*++ + +Routine Description: + + Initialize the hardware simulation + +Arguments: + + [none] + +Return Value: + + TRUE - CHardwareSimulation is fully initialized. + +--*/ + +{ + PAGED_CODE(); + + return m_IsrTimer.IsValid() ; +} + +/*************************************************/ + +NTSTATUS +CHardwareSimulation:: +Start ( + IN CSynthesizer *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(); + + // Prevent state-changes during this call. + KScopedMutex Lock(m_ListLock); + + NTSTATUS Status = STATUS_SUCCESS; + + m_Synthesizer = ImageSynth; + m_TimePerFrame = TimePerFrame; + m_ImageSize = ImageSize; + m_Height = Height; + m_Width = Width; + + m_NumMappingsCompleted = 0; + m_ScatterGatherMappingsQueued = 0; + m_NumFramesSkipped = 0; + m_InterruptTime = 0; + + KeQuerySystemTime (&m_StartTime); + + if( !m_Synthesizer->Initialize() ) + { + Status = STATUS_INSUFFICIENT_RESOURCES; + } + + // + // If everything is ok, start issuing interrupts. + // + if (NT_SUCCESS (Status)) + { + LARGE_INTEGER NextTime; + NextTime.QuadPart = m_StartTime.QuadPart + m_TimePerFrame; + + m_PinState = PinRunning; + m_IsrTimer.Set( NextTime ); + } + + return Status; +} + +/*************************************************/ + +NTSTATUS +CHardwareSimulation:: +Pause ( + BOOLEAN Pausing +) + +/*++ + +Routine Description: + + Pause the hardware simulation... When the hardware simulation is told + to pause, it stops stops the timer, 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(); + + { + // Prevent state-changes during this call. + KScopedMutex Lock(m_ListLock); + + if (Pausing && m_PinState == PinRunning) + { + // + // If we were running, stop completing mappings, etc... + // + m_PinState = PinPaused; + } + else if (!Pausing && m_PinState == PinPaused) + { + // + // 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_PinState = PinRunning; + m_IsrTimer.Set( UnpauseTime ); + } + } + + // If we are pausing, it's best to stop the timer and not wait for another frame. + if( Pausing ) + { + m_IsrTimer.Cancel(); + } + + return STATUS_SUCCESS; +} + +NTSTATUS +CHardwareSimulation:: +Stop() + +/*++ + +Routine Description: + + Stop the hardware simulation... + + For us, stop the timer, free the synthesizer and flush the queue. + +Arguments: + + None + +Return Value: + + Success / Failure + +--*/ + +{ + PAGED_CODE(); + + // + // 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. + // + + DBG_ENTER("(): m_PinID=%d", m_PinID); + + BOOLEAN bCancel = FALSE; + + { + // Prevent state-changes during this call. + KScopedMutex Lock(m_ListLock); + + bCancel = (m_PinState == PinRunning); + m_PinState = PinStopped; + + // Free the synthesis buffer. + m_Synthesizer->Destroy(); + + // + // Free S/G buffer + // + FreeSGList( &m_ScatterGatherMappings, L"StreamPointer Stop SG List" ); + + m_ScatterGatherMappingsQueued=0; + m_NumMappingsCompleted = 0; + m_ScatterGatherBytesQueued = 0; + } + + // If running, stop the timer to make sure we don't try to deliver one last frame. + if( bCancel ) + { + m_IsrTimer.Cancel(); + } + + return STATUS_SUCCESS; +} + +NTSTATUS +CHardwareSimulation:: +Reset() +{ + PAGED_CODE(); + + KScopedMutex Lock( m_ListLock ); + DBG_ENTER("(): m_PinID=%d", m_PinID); + + FreeSGList( &m_ScatterGatherMappings, L"StreamPointer Reset SG List" ); + + m_ScatterGatherMappingsQueued=0; + m_NumMappingsCompleted = 0; + m_ScatterGatherBytesQueued = 0; + + DBG_LEAVE("(): m_PinID=%d", m_PinID); + return STATUS_SUCCESS; +} + +NTSTATUS +CHardwareSimulation:: +Reset (IN PKSPIN Pin) +{ + PAGED_CODE(); + + NTSTATUS status = STATUS_SUCCESS; + if(Pin->ResetState == KSRESET_END) + { + KsPinAttemptProcessing(Pin, TRUE); + } + else + { + status = Reset(); + } + + return status; +} + +ULONG +CHardwareSimulation:: +ReadNumberOfMappingsCompleted() + +/*++ + +Routine Description: + + Read the number of scatter / gather mappings which have been + completed since the last reset of the simulated hardware + +Arguments: + + None + +Return Value: + + Total number of completed mappings. + +--*/ + +{ + PAGED_CODE(); + + return m_NumMappingsCompleted; +} + +/*************************************************/ + + +ULONG +CHardwareSimulation:: +ProgramScatterGatherMappings ( + IN PKSSTREAM_POINTER *Clone, + IN PUCHAR *Buffer, + IN PKSMAPPING Mappings, + IN ULONG MappingsCount, + IN ULONG MappingStride +) + +/*++ + +Routine Description: + + Add a single entry to our queue. In practice our simulation can only + accept one frame at a time, but we still base this code off of the + avshws sample. + +Arguments: + + Buffer - + The virtual address of the buffer mapped by the mapping list + + ClonePointer + The KSStreamClone Pointer to be associated with the entry item + + 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. + +--*/ + +{ + PAGED_CODE(); + + ULONG MappingsInserted = 0; + + DBG_ENTER("(Clone=%p, Buffer=%p, MappingsCount=%d)", + Clone, Buffer, MappingsCount); + + // + // Protect our S/G list with a spinlock. + // + KScopedMutex Lock( m_ListLock ); + + // + // 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. + // + do + { + PSCATTER_GATHER_ENTRY Entry = + reinterpret_cast ( + ExAllocateFromNPagedLookasideList ( + &m_ScatterGatherLookaside + ) + ); + + if (!Entry) + { + break; + } + + Entry ->CloneEntry = nullptr; + Entry -> Virtual = nullptr; + Entry -> ByteCount = 0; + + + Entry ->CloneEntry = *Clone; + Entry -> CloneEntry -> StreamHeader -> PresentationTime.Time = 0; + Entry -> CloneEntry -> StreamHeader -> OptionsFlags &= ~KSSTREAM_HEADER_OPTIONSF_TIMEVALID; + + Entry -> Virtual = *Buffer; + Entry -> ByteCount = MappingsCount; + Entry->PhotoConfirmationInfo = PHOTOCONFIRMATION_INFO(); + + // + // 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)); + m_ScatterGatherMappingsQueued++; + MappingsInserted = MappingsCount; + DBG_TRACE("m_ScatterGatherMappingsQueued=%d", m_ScatterGatherMappingsQueued); + m_ScatterGatherBytesQueued += MappingsCount; + + } + while(FALSE); + + DBG_LEAVE(" MappingsInserted=%d", MappingsInserted ); + + return MappingsInserted; + +} + +/************************************************************************** + + The following are helper functions used in the production of metadata. + Since this is just a simulation, most "auto" modes get converted into + a random value that is in range. + + While simple, this still requires writing a helper function for each + piece of data. + +**************************************************************************/ + +// +// Translate Exposure settings into a "current value". +// +LONGLONG +CHardwareSimulation:: +GetCurrentExposureTime() +{ + PAGED_CODE(); + + ISP_FRAME_SETTINGS *pSettings = GetIspSettings(); + LONGLONG Value = 0; + LPCSTR Mode = "[UNKNOWN]"; + + if( pSettings->ExposureMode & KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO ) + { + // Get random value in global setting's bound (LONG) + // I'm abandoning the reported min/max and using something more reasonable. + Value = GetRandom( MIN_EXPOSURE_TIME*5, DEF_EXPOSURE_TIME*5 ); + Mode = "KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO"; + } + + if( pSettings->ExposureMode & KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_MANUAL ) + { + Value = pSettings->ExposureSetting.VideoProc.Value.ll; + Mode = "KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_MANUAL"; + } + + // Locked just reports the last value set... + if( pSettings->ExposureMode & KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK ) + { + Value = m_LastReportedExposureTime; + Mode = "KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK"; + } + + DBG_TRACE("ExposureMode=0x%016llX (%s), Time=%llu00ns, LastReported=%llu00ns", + pSettings->ExposureMode, Mode, Value, m_LastReportedExposureTime ); + + m_LastReportedExposureTime = Value; + return Value; +} + +// +// Translate White Balance into a "current value". +// +ULONG +CHardwareSimulation:: +GetCurrentWhiteBalance() +{ + PAGED_CODE(); + + ISP_FRAME_SETTINGS *pSettings = GetIspSettings(); + ULONG Value = 0; + + if( pSettings->WhiteBalanceMode & KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO ) + { + Value = (ULONG) GetRandom( pSettings->WhiteBalanceSetting.Min, pSettings->WhiteBalanceSetting.Max ); + } + + if( pSettings->WhiteBalanceMode & KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_MANUAL ) + { + Value = pSettings->WhiteBalanceSetting.VideoProc.Value.ul; + } + + // Locked just reports the last value set... + if( pSettings->WhiteBalanceMode & KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK ) + { + Value = m_LastReportedWhiteBalance; + } + + m_LastReportedWhiteBalance= Value; + return Value; +} + +// +// Translate ISO settings into a "current value". +// +ULONG +CHardwareSimulation:: +GetCurrentISOSpeed() +{ + PAGED_CODE(); + + ISP_FRAME_SETTINGS *pSettings = GetIspSettings(); + ULONG Value = 0; + + if( pSettings->ISOMode & KSCAMERA_EXTENDEDPROP_ISO_AUTO ) + { + Value = GetRandom( (ULONG) 50, (ULONG) 3200 ); + } + else if( pSettings->ISOMode & KSCAMERA_EXTENDEDPROP_ISO_MANUAL ) + { + Value = pSettings->ISOValue; + } + else + { + Value = // Try converting any legacy presets to a manual value. + IsoPreset2Value( pSettings->ISOMode ); + } + return Value; +} + +// +// Translate Exposure Mode to an Exif Exposure Program value +// +USHORT +CHardwareSimulation:: +GetExposureProgram() +{ + PAGED_CODE(); + + ISP_FRAME_SETTINGS *pSettings = GetIspSettings(); + USHORT Value = 0; // Undefined. + + if( pSettings->ExposureMode & KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_MANUAL ) + { + Value = 1; // Manual + } + if( pSettings->ExposureMode & KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_AUTO ) + { + Value = 2; // Normal + } + + // TODO: Extend SceneMode to ISP settings so I can use it here to capture Portrait mode, etc. + return Value; +} + +/*************************************************/ + +// +// Emit metadata here that is common to all pins. +// +// All derived classes' EmitMetadata() function must +// call this function first. +// +void +CHardwareSimulation:: +EmitMetadata( + _Inout_ PKSSTREAM_HEADER pStreamHeader +) +{ + PAGED_CODE(); + + NT_ASSERT(pStreamHeader); + + if (0 != (pStreamHeader->OptionsFlags & KSSTREAM_HEADER_OPTIONSF_METADATA)) + { + PKS_FRAME_INFO pFrameInfo = (PKS_FRAME_INFO)(pStreamHeader + 1); + PKSSTREAM_METADATA_INFO pMetadata = (PKSSTREAM_METADATA_INFO) (pFrameInfo + 1); + //PBYTE pData = (PBYTE) pMetadata->SystemVa; + //ULONG BytesLeft = pMetadata->BufferSize; + + // A real driver might write focus state or other info here. We've got nothing. + pMetadata->UsedSize = 0; + } + else + { + DBG_TRACE("Metadata not present..."); + } +} + +void +CHardwareSimulation:: +EmitFaceMetadata( + _Inout_ PKSSTREAM_HEADER pStreamHeader, + _In_ ULONG Count, + _In_ ULONGLONG Flags, + _In_ ULONG DelayLimit +) +/*++ + +Routine Description: + + Helper function that emits FaceDetection metadata. + + Note: This function assumes a metadata buffer is present. + +Arguments: + + pStreamHeader - + The frame's stream header. + + Count - + The maximum number of faces to detect. + + Flags - + A combination of the KSCAMERA_EXTENDEDPROP_FACEDETECTION_BLINK and + KSCAMERA_EXTENDEDPROP_FACEDETECTION_BLINK flags passed to the face + detection control. Used to decide what characterization data to + present in the metadata. + + DelayLimit - + The maximum number of frames to delay before generating the next + face detection. + +Return Value: + + None. + +--*/ +{ + PAGED_CODE(); + + NT_ASSERT(pStreamHeader); + NT_ASSERT(Count <= SIZEOF_ARRAY(m_LastFaceDetect.Data)); + + // Do nothing if we're turned on, but they want no faces. + if( Count==0 ) + { + return; + } + + if( --m_FaceDetectionDelay != 0 || // Count down our random delay for new face data. + Count > m_LastFaceDetect.Count ) // Make sure we never emit more faces than requested. + { + ULONG CountReported = GetRandom( 0, Count ); + + DBG_TRACE("Generating new faces (Count=%d, Flags=0x%016llX, Delay=%d)...", + Count, Flags, DelayLimit); + + // Synthesize a new set of "detected" faces... + m_LastFaceDetect.Header.MetadataId = (ULONG) MetadataId_Custom_FaceDetection; + m_LastFaceDetect.Header.Size = + sizeof(CAMERA_METADATA_FACEHEADER) + (sizeof(METADATA_FACEDATA) * CountReported); + + m_LastFaceDetect.Count = CountReported; + m_LastFaceDetect.Flags = Flags; + m_LastFaceDetect.Timestamp = pStreamHeader->PresentationTime.Time ; + + DBG_TRACE("Count=%d, Flags=0x%016llX, Time=0x%016llX", + m_LastFaceDetect.Count, + m_LastFaceDetect.Flags, + m_LastFaceDetect.Timestamp ); + + // Report face data... + for( ULONG i=0; iBufferSize - pMetadata->UsedSize; + + // Write Face Detection Info here + // Note: If we derived Video and Preview from a common base class, we + // might use that to emit this information for both types. + if( BytesLeft >= m_LastFaceDetect.Header.Size ) + { + // Always write the most recently generated face detection info. + RtlCopyMemory( + ((PBYTE)pMetadata->SystemVa) + pMetadata->UsedSize, + &m_LastFaceDetect, + m_LastFaceDetect.Header.Size ); + + DBG_TRACE("Emitting Face metadata..."); + + BytesLeft -= m_LastFaceDetect.Header.Size; + pMetadata->UsedSize += m_LastFaceDetect.Header.Size; + } + else + { + DBG_TRACE("WARNING: Out of metadata buffer..."); + } +} + +// +// PIN_STATE text for debug output. +// +static +const char * +GetPinStateTxt( PIN_STATE state ) +{ + PAGED_CODE(); + + switch( state ) + { + case PinStopped: + return "STOPPED"; + case PinPaused: + return "PAUSED"; + case PinRunning: + return "RUNNING"; + } + return "[UNKNOWN]"; +} + +NTSTATUS +CHardwareSimulation:: +FillScatterGatherBuffers() + +/*++ + +Routine Description: + + The hardware has synthesized a buffer and we're to fill a frame buffer. + +Arguments: + + None + +Return Value: + + Success / Failure + +--*/ + +{ + PAGED_CODE(); + + DBG_ENTER("() m_PinState=%s, m_PinID=0x%08X, m_ImageSize=0x%08X, m_ScatterGatherMappingsQueued=%u, " + "m_ScatterGatherBytesQueue=0x%08X", + GetPinStateTxt(m_PinState), + m_PinID, m_ImageSize, m_ScatterGatherMappingsQueued, m_ScatterGatherBytesQueued); + + NTSTATUS ntStatus = STATUS_SUCCESS; + + ULONG BufferRemaining = m_ImageSize; + + // + // If there isn't a frame buffer queued, we justskip the frame and consider + // it starvation. + // + while (BufferRemaining && + !IsListEmpty(&m_ScatterGatherMappings) && + m_ScatterGatherBytesQueued >= BufferRemaining) + { + LIST_ENTRY *listEntry = RemoveHeadList (&m_ScatterGatherMappings); + m_ScatterGatherMappingsQueued--; + + PSCATTER_GATHER_ENTRY SGEntry = + reinterpret_cast ( + CONTAINING_RECORD ( + listEntry, + SCATTER_GATHER_ENTRY, + ListEntry + ) + ); + + // Deal with cancellation. + PIRP pIrp = KsStreamPointerGetIrp(SGEntry->CloneEntry, FALSE, FALSE); + if (pIrp) + { + if (pIrp->Cancel) + { + DBG_TRACE( "Cancelling..." ); + FreeSGEntry( listEntry, L"StreamPointer Cancel SG List" ); + continue; + } + } + + // + // Since we're software, we'll be accessing this by virtual address... + // + ULONG Stride = 0; + if ( SGEntry->CloneEntry -> StreamHeader -> Size >= sizeof (KSSTREAM_HEADER) + + sizeof (KS_FRAME_INFO)) + { + PKS_FRAME_INFO FrameInfo = reinterpret_cast (SGEntry->CloneEntry->StreamHeader+1); + Stride = (ULONG) ABS(FrameInfo->lSurfacePitch); + } + + // Have the synthesizer output a frame to the buffer. + ULONG BytesCopied = m_Synthesizer->DoCommit( SGEntry->Virtual, SGEntry->ByteCount, Stride ); + NT_ASSERT( BytesCopied ); + DBG_TRACE( "BytesCopied = %d", BytesCopied ); + + //Adding time stamp + if(m_PhotoConfirmationEntry.isRequired()) + { + SGEntry -> CloneEntry -> StreamHeader -> PresentationTime.Time = + m_PhotoConfirmationEntry.getTime(); + DBG_TRACE("Using Photo Confirmation time = 0x%016llX", m_PhotoConfirmationEntry.getTime()); + } + else + { + SGEntry -> CloneEntry -> StreamHeader -> PresentationTime.Time = ConvertQPCtoTimeStamp(NULL); + DBG_TRACE("PresentationTime = 0x%016llX", SGEntry->CloneEntry->StreamHeader->PresentationTime.Time ); + } + + SGEntry -> CloneEntry -> StreamHeader -> PresentationTime.Numerator = + SGEntry -> CloneEntry -> StreamHeader -> PresentationTime.Denominator = 1; + + SGEntry -> CloneEntry -> StreamHeader -> OptionsFlags |= KSSTREAM_HEADER_OPTIONSF_TIMEVALID ; + + // Add metadata to the sample. + EmitMetadata( SGEntry -> CloneEntry -> StreamHeader ); + + BufferRemaining = 0; //-= BytesCopied; + m_NumMappingsCompleted++; + m_ScatterGatherBytesQueued -= SGEntry -> ByteCount; + + // + // Release the scatter / gather entry back to our lookaside. + // + ExFreeToNPagedLookasideList ( + &m_ScatterGatherLookaside, + reinterpret_cast (SGEntry) + ); + + } + + // Report an error if we used the last buffer. + if (BufferRemaining) + { + //DBG_TRACE("BufferRemaining=%u", BufferRemaining); + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + } + + DBG_LEAVE("() = 0x%08X", ntStatus); + return ntStatus; +} + +/************************************************************************** + + Debug helpers + +**************************************************************************/ + +PCCHAR +DVS_Text( ULONGLONG Flags ) +{ + PAGED_CODE(); + + switch( Flags ) + { + case KSCAMERA_EXTENDEDPROP_VIDEOSTABILIZATION_OFF: return "Off"; + case KSCAMERA_EXTENDEDPROP_VIDEOSTABILIZATION_ON: return "On"; + case KSCAMERA_EXTENDEDPROP_VIDEOSTABILIZATION_AUTO: return "Auto"; + default: return "Unknown"; + } +} + +PCCHAR +OIS_Text( ULONGLONG Flags ) +{ + PAGED_CODE(); + + switch( Flags ) + { + case KSCAMERA_EXTENDEDPROP_OIS_OFF: return "Off"; + case KSCAMERA_EXTENDEDPROP_OIS_ON: return "On"; + case KSCAMERA_EXTENDEDPROP_OIS_AUTO: return "Auto"; + default: return "Unknown"; + } +} + +/**************************************************************************/ + +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 + +--*/ + +{ + PAGED_CODE(); + + KScopedMutex Lock( m_ListLock ); + + 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_PinState == PinRunning) + { + // + // Generate a "time stamp" just to overlay it onto the capture image. + // It makes it more exciting than bars that do nothing. + // + ULONGLONG time = ConvertQPCtoTimeStamp(NULL); + DBG_TRACE("QPC=0x%016llX", time); + + m_Synthesizer->SetFrameNumber( m_InterruptTime ); + m_Synthesizer->SetRelativePts( (m_InterruptTime + 1) * m_TimePerFrame ); + m_Synthesizer->SetQpcTime( time ); + + m_Synthesizer->DoSynthesize(); + + CHAR Text[64]; + + CExtendedProperty Control; + m_Sensor->GetVideoStabilization( &Control ); + RtlStringCbPrintfA(Text, sizeof(Text), "DVS: %s", DVS_Text(Control.Flags)); + m_Synthesizer->OverlayText( 0, m_Height-38, 1, Text, BLACK, WHITE ); + + m_Sensor->GetOpticalImageStabilization( &Control ); + RtlStringCbPrintfA(Text, sizeof(Text), "OIS: %s", OIS_Text(Control.Flags)); + m_Synthesizer->OverlayText( 0, m_Height-48, 1, Text, BLACK, WHITE ); + + // + // 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_Sensor -> Interrupt (m_PinID); + + // + // Schedule the timer for the next interrupt time, if the pin is still running. + // + if( m_PinState == PinRunning ) + { + LARGE_INTEGER NextTime; + NextTime.QuadPart = m_StartTime.QuadPart + + (m_TimePerFrame * (m_InterruptTime + 1)); + +#ifdef ENABLE_TRACING // To keep us from a tight spin when trying to debug this code... + LARGE_INTEGER Now; + KeQuerySystemTime(&Now); + + if( Now.QuadPart >= NextTime.QuadPart ) + { + NextTime.QuadPart = 0LL - m_TimePerFrame ; + } + +#endif + m_IsrTimer.Set( NextTime ); + } +} + +ULONGLONG +CHardwareSimulation:: +ConvertQPCtoTimeStamp( + _In_opt_ PLARGE_INTEGER pQpcTime +) +/*++ + +Routine Description: + + Get the QPC measured in 100ns units. + +Arguments: + + pQpcTime - + An optional time value to convert. If NULL, use the current QPC. + +Return Value: + + ULONLONG - + The time in 100ns increments. + +--*/ +{ + PAGED_CODE(); + + LARGE_INTEGER qpcTime = {0}; + LARGE_INTEGER qpcFrequency; + + qpcTime = KeQueryPerformanceCounter(&qpcFrequency); + + if( pQpcTime ) + { + qpcTime = *pQpcTime; + } + + return KSCONVERT_PERFORMANCE_TIME( qpcFrequency.QuadPart, qpcTime ); +} + +NTSTATUS +CHardwareSimulation:: +GeneratePhotoConfirmation( + _In_ ULONG PfsFrameNumber, + _In_ LONGLONG time +) +/*++ + +Routine Description: + + So a photo sim can ask a preview sim for a confirmation frame. + +Arguments: + + PfsFrameNumber - + The VPS shot number. + time - + The precise time of the associated photo. + +Return Value: + + None + +--*/ +{ + PAGED_CODE(); + + KScopedMutex Lock( m_ListLock ); + + if( PinRunning != m_PinState ) + { + return STATUS_DEVICE_NOT_READY; + } + + DBG_ENTER("( Index=%d, Time=0x%016llX )", PfsFrameNumber, time ); + + m_PhotoConfirmationEntry = PHOTOCONFIRMATION_INFO( PfsFrameNumber, time ); + + // We basically used the previously synthesized preview image + NTSTATUS status = FillScatterGatherBuffers(); + + //Clean up the photo confiramtion flag + m_PhotoConfirmationEntry = PHOTOCONFIRMATION_INFO(); + + if(NT_SUCCESS(status)) + { + m_Sensor->Interrupt (m_PinID); + } + + DBG_LEAVE("() = 0x%08X", status); + + return status; +} + +void +CHardwareSimulation:: +FreeSGEntry( + _In_ PLIST_ENTRY Entry, + _In_ PCWSTR EventName, + _In_opt_ LPCGUID Activity, + _In_ ULONG DataUsed +) +{ + PAGED_CODE(); + + PSCATTER_GATHER_ENTRY SGEntry = + reinterpret_cast ( + CONTAINING_RECORD ( + Entry, + SCATTER_GATHER_ENTRY, + ListEntry + ) + ); + + NT_ASSERT(SGEntry->CloneEntry); + NT_ASSERT(SGEntry->CloneEntry->StreamHeader); + + SGEntry->CloneEntry->StreamHeader->DataUsed = DataUsed; + + KsStreamPointerDelete(SGEntry->CloneEntry); + + // + // Release the scatter / gather entry back to our lookaside. + // + ExFreeToNPagedLookasideList ( + &m_ScatterGatherLookaside, + SGEntry + ); +} + +void +CHardwareSimulation:: +FreeSGList( + _In_ PLIST_ENTRY List, + _In_ PCWSTR EventName, + _In_opt_ LPCGUID Activity +) +{ + PAGED_CODE(); + + while(!IsListEmpty(List)) + { + PLIST_ENTRY head = RemoveHeadList(List); + m_ScatterGatherMappingsQueued--; + FreeSGEntry( head, EventName, Activity ); + } +} + +// +// Get the current frame settings. +// +ISP_FRAME_SETTINGS * +CHardwareSimulation:: +GetIspSettings(void) +{ + PAGED_CODE(); + + return m_Sensor->GetGlobalIspSettings(); +} diff --git a/avscamera/sys/hwsim.h b/avscamera/sys/hwsim.h new file mode 100644 index 000000000..247de1f0d --- /dev/null +++ b/avscamera/sys/hwsim.h @@ -0,0 +1,316 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2001, Microsoft Corporation. + + File: + + hwsim.h + + Abstract: + + This file contains the definition of the base class, CHardwareSimulation, + used to simulate a camera's capture stream. + + Our simulation cannot mimic a real camera with any precision. A real + camera would likely have dedicated hardware to provide interrupts, do + DMA transfers of frames from an internal buffer as well as do scaling + and on-the-fly colorspace and planar format conversions so as to supply + frames for preview, video and photo in varying resolutions and + colorspaces. + + This simulation does not have such hardware, so instead it synthesizes + a frame on the fly for each local pin as required. The frame is + synthesized from common state information, so the images produced + should be comparable from pin to pin. CHardwareSimulation is used as + a base class for each simulated pin / hardware. + + A CHardwareSimulation contains state, a list of frame bufferss and a + timer that is used in place of a hardware interrupt. The timer + schedules a workitem that is used to synthesize an image, select a + buffer and copy that image into the buffer. + + Specializations of CHardwareSimulation provide differing metadata and + implement specialized functionality for photo sequence and variable + photo sequence. + + History: + + created 3/9/2001 + +**************************************************************************/ + +#define SCATTER_GATHER_MAPPINGS_MAX 128 + +// forward class references +class CSensor; +class CHardwareSimulation; +class CHwSimTimer; + +// internal structure definitions + +// Face metadata structure. Face metadata can be synthesized on any pin. +class CAMERA_METADATA_FACE_DETECTION + : public CAMERA_METADATA_FACEHEADER +{ +public: + METADATA_FACEDATA Data[MAX_FACES]; +}; + +// Information about each frame buffer in the queue. +typedef struct _SCATTER_GATHER_ENTRY +{ + LIST_ENTRY ListEntry; + PKSSTREAM_POINTER CloneEntry; + PUCHAR Virtual; + ULONG ByteCount; + PHOTOCONFIRMATION_INFO PhotoConfirmationInfo; +} SCATTER_GATHER_ENTRY, *PSCATTER_GATHER_ENTRY; + +// +// A specialization of a KPassiveTimer that holds a back pointer to the +// CHardwareSimulation that owns it. +// +class CHwSimTimer + : public KPassiveTimer +{ + CHardwareSimulation *m_Parent; + +public: + CHwSimTimer( + _In_ CHardwareSimulation *Parent + ); + + // + // Sets the timer + // + BOOLEAN + Set( + _In_ LARGE_INTEGER DueTime + ); + +private: + static + void + Handler( + _In_opt_ PVOID Context + ); +}; + +class CHardwareSimulation +{ +protected: + CSynthesizer *m_Synthesizer; // Synthesizer for various formats. + LONGLONG m_TimePerFrame; + ULONG m_Width; + ULONG m_Height; + ULONG m_ImageSize; + LONG m_PinID; + KMutex m_ListLock; + LIST_ENTRY m_ScatterGatherMappings; + + NPAGED_LOOKASIDE_LIST m_ScatterGatherLookaside; + PIN_STATE m_PinState; + ULONG m_ScatterGatherMappingsMax; + ULONG m_NumMappingsCompleted; + ULONG m_ScatterGatherMappingsQueued; + ULONG m_ScatterGatherBytesQueued; + ULONG m_NumFramesSkipped; + ULONG m_InterruptTime; + LARGE_INTEGER m_StartTime; + + CSensor *m_Sensor; + CHwSimTimer m_IsrTimer; + + PHOTOCONFIRMATION_INFO m_PhotoConfirmationEntry; + + // Cached values used for Locking simulated state. + LONGLONG m_LastReportedExposureTime; + ULONG m_LastReportedWhiteBalance; + + // Used to simulate the skipping of frames when reporting face detection data. + ULONG m_FaceDetectionDelay; + CAMERA_METADATA_FACE_DETECTION m_LastFaceDetect; + + // Copy a synthesized framebuffer. + virtual + NTSTATUS + FillScatterGatherBuffers(); + + // Overloadable function for attaching metadata to a frame. + virtual + void + EmitMetadata( + _Inout_ PKSSTREAM_HEADER pStreamHeader + ); + + // Helper function to generate random face metadata. + void + EmitFaceMetadata( + _Inout_ PKSSTREAM_HEADER pStreamHeader, + _In_ ULONG Count, + _In_ ULONGLONG Flags, + _In_ ULONG DelayLimit=30 + ); + + // Release an entry from our queue. + void + FreeSGEntry( + _In_ PLIST_ENTRY Entry, + _In_ PCWSTR EventName=L"StreamPointer freed", + _In_opt_ LPCGUID Activity=NULL, + _In_ ULONG DataUsed=0 + ); + + // Release our entire queue. + void + FreeSGList( + _In_ PLIST_ENTRY List, + _In_ PCWSTR EventName=L"StreamPointer freed", + _In_opt_ LPCGUID Activity=NULL + ); + +public: + + // Debug helper + LONG GetSkippedFrameCount() + { + return m_NumFramesSkipped; + } + + // Constructor + CHardwareSimulation ( + _Inout_ CSensor *Sensor, + _In_ LONG PinID + ); + + // Destructor + virtual + ~CHardwareSimulation (); + + // Periodic interrupt handling + virtual + void + FakeHardware ( + ); + + // Start streaming. + virtual + NTSTATUS + Start ( + CSynthesizer *ImageSynth, + IN LONGLONG TimePerFrame, + IN ULONG Width, + IN ULONG Height, + IN ULONG ImageSize + ); + + // Pause streaming + virtual + NTSTATUS + Pause ( + IN BOOLEAN Pausing + ); + + // Stop streaming. + virtual + NTSTATUS + Stop(); + + // Reset streaming. + virtual + NTSTATUS + Reset( + IN PKSPIN Pin + ); + + // Reset streaming. + virtual + NTSTATUS + Reset(); + + // Add a frame buffer to our queue. + virtual + ULONG + ProgramScatterGatherMappings ( + IN PKSSTREAM_POINTER *Clone, + IN PUCHAR *Buffer, + IN PKSMAPPING Mappings, + IN ULONG MappingsCount, + IN ULONG MappingStride + ); + + // Initialization. + virtual + BOOLEAN + Initialize(); + + // Number of frames completed during this run. + ULONG + ReadNumberOfMappingsCompleted(); + + // Get the QPC measured in 100ns units. + ULONGLONG + ConvertQPCtoTimeStamp( + _In_opt_ PLARGE_INTEGER pQpcTime + ); + + // Get the current frame settings. + virtual + ISP_FRAME_SETTINGS * + GetIspSettings(void); + + // So a photo sim can ask a preview sim for a confirmation frame. + NTSTATUS + GeneratePhotoConfirmation( + _In_ ULONG PfsFrameNumber, + _In_ LONGLONG time + ); + + // Get the exposure value for the current frame. + LONGLONG + GetCurrentExposureTime(); + + // Get the White balance for the current frame. + ULONG + GetCurrentWhiteBalance(); + + // Get the ISO Speed value for the current frame. + ULONG + GetCurrentISOSpeed(); + + // Get the Lens position for the current frame. + ULONG + GetCurrentLensPosition(); + + // Get the Scene Mode for the current frame. + ULONG + GetCurrentSceneMode(); + + // Translate Exposure Mode to an Exif Exposure Program value + USHORT + GetExposureProgram(); + + // Pin state accessor... + PIN_STATE + GetState() + { + return m_PinState; + } + + friend class CHwSimTimer; +}; + +// +// Sets the timer using our callback and our parent CHardwareSimulation. +// +inline +BOOLEAN +CHwSimTimer:: +Set( + _In_ LARGE_INTEGER DueTime +) +{ + return KPassiveTimer::Set( DueTime, Handler, m_Parent ); +} diff --git a/avscamera/sys/imagecapture.cpp b/avscamera/sys/imagecapture.cpp new file mode 100644 index 000000000..b236f1410 --- /dev/null +++ b/avscamera/sys/imagecapture.cpp @@ -0,0 +1,321 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2001, Microsoft Corporation. + + File: + + imagecapture.cpp + + Abstract: + + An implementation of CImageCapturePin + + History: + + created 3/8/2001 + +**************************************************************************/ + +#include "Common.h" + +#ifdef ALLOC_PRAGMA +#pragma code_seg("PAGE") +#endif // ALLOC_PRAGMA + +CImageCapturePin:: +CImageCapturePin( + _In_ PKSPIN Pin +) : CCapturePin (Pin) +{ + PAGED_CODE(); +} + +NTSTATUS +CImageCapturePin:: +DispatchCreate( + _In_ PKSPIN Pin, + _In_ PIRP Irp +) +/*++ + +Routine Description: + + Static member function called when a pin is created. Instantiate a + CImageCapturePin and attach it to our filter object. + +Arguments: + + Pin - + The KSPIN to wrap. + + Irp - + The IRP assocated with this request. + +Return Value: + + Success / Failure + +--*/ +{ + PAGED_CODE(); + + DBG_ENTER("(Pin=%d)", Pin->Id); + + NTSTATUS Status = STATUS_SUCCESS; + + CCaptureFilter* pFilter = reinterpret_cast (KsPinGetParentFilter(Pin)->Context); + CImageCapturePin *CapPin = new (NonPagedPoolNx) CImageCapturePin (Pin); + + if( !CapPin ) + { + // Fail if we couldn't create the pin. + Status = STATUS_INSUFFICIENT_RESOURCES; + } + else + { + // Query the filter for the PhotoMode so we can figure out how many frames should be allocated. + CExtendedPhotoMode Mode; + Mode.PinId = Pin->Id; // Use Pin ID from KS. + Status = pFilter->GetPhotoMode( &Mode ); + + // CSensor::GetPhotoMode could fail if the framework gives us a Pin ID that doesn't match our descriptors... + if( NT_SUCCESS(Status) ) + { + // Override the number of frames. + CapPin->SetDesiredFrames( + ( (Mode.Flags & KSCAMERA_EXTENDEDPROP_PHOTOMODE_SEQUENCE) ? + Mode.RequestedHistoryFrames() : 1) + + IMAGE_CAPTURE_PIN_MINIMUM_FRAMES ); + + Status = + CapPin->Initialize(); + } + } + + if (NT_SUCCESS (Status)) + { + // + // Adjust the stream header size. The video packets have extended + // header info (KS_FRAME_INFO). + // + pFilter->setPin(CapPin, Pin->Id); + } + else + { + // Clean up. + delete CapPin; + } + + DBG_LEAVE("(Pin=%d)=0x%08X", Pin->Id, Status); + return Status; +} + + +NTSTATUS +CImageCapturePin:: +Close( + _In_ PIRP Irp +) +{ + PAGED_CODE(); + + Reset(); + + return STATUS_SUCCESS; +} + +// ImageCapturePinDispatch: +// +// This is the dispatch table for the capture pin. It provides notifications +// about creation, closure, processing, data formats, etc... +// just a copy of CapturePinDispatch for now. +// +DEFINE_CAMERA_KSPIN_DISPATCH( ImageCapturePinDispatch, CImageCapturePin ); + + +// +// ImagePinAllocatorFraming: +// +// 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. +// just a copy of CapturePinAllocatorFraming for now. +// +DECLARE_SIMPLE_FRAMING_EX ( + ImageCapturePinAllocatorFraming, + STATICGUIDOF (KSMEMORY_TYPE_KERNEL_NONPAGED), + KSALLOCATOR_REQUIREMENTF_SYSTEM_MEMORY | + KSALLOCATOR_REQUIREMENTF_PREFERENCES_ONLY, + 4, + 0, + 2 * PAGE_SIZE, + 2 * PAGE_SIZE +); + +const +KS_DATARANGE_VIDEO +FormatYUY2Image_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; + } +}; + +const +KS_DATARANGE_VIDEO +FormatNV12Image_Capture = +{ + + // + // KSDATARANGE + // + { + sizeof (KS_DATARANGE_VIDEO), // FormatSize + 0, // Flags + 0, // SampleSize + 0, // Reserved + STATICGUIDOF (KSDATAFORMAT_TYPE_VIDEO), // aka. MEDIATYPE_Video + 0x3231564E, 0x0000, 0x0010, 0x80, 0x00, + 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71, //aka. MEDIASUBTYPE_NV12, + 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 + 12 * 30 * D_X * D_Y, // MinBitsPerSecond; + 12 * 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 * 12 * 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; + 12, // WORD biBitCount; + FOURCC_NV12, // DWORD biCompression; + DWORD(DMAX_X *DMAX_Y * 1.5), // DWORD biSizeImage; + 0, // LONG biXPelsPerMeter; + 0, // LONG biYPelsPerMeter; + 0, // DWORD biClrUsed; + 0 // DWORD biClrImportant; + } +}; + + +// +// CapturePinDataRanges: +// +// This is the list of data ranges supported on the capture pin. We support +// two: one RGB24, and one YUY2. +// +const +PKSDATARANGE +ImageCapturePinDataRanges [IMAGE_CAPTURE_PIN_DATA_RANGE_COUNT] = +{ + (PKSDATARANGE) &FormatYUY2Image_Capture, + (PKSDATARANGE) &FormatNV12Image_Capture +}; \ No newline at end of file diff --git a/avscamera/sys/imagecapture.h b/avscamera/sys/imagecapture.h new file mode 100644 index 000000000..1e11c31ac --- /dev/null +++ b/avscamera/sys/imagecapture.h @@ -0,0 +1,44 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2001, Microsoft Corporation. + + File: + + imagecapture.h + + Abstract: + + Image Capture Pin - A specialization of a capture pin. + + CImageCapturePin defines a unique list of data ranges for the image + pin. + + History: + + created 3/8/2001 + +**************************************************************************/ +#pragma once + +#include "capture.h" + +class CImageCapturePin : public CCapturePin +{ +public: + static NTSTATUS DispatchCreate( + _In_ PKSPIN Pin, + _In_ PIRP Irp + ); + +private: + virtual + NTSTATUS Close( + _In_ PIRP Irp + ); + + CImageCapturePin( + _In_ PKSPIN Pin + ); +}; diff --git a/avscamera/sys/imagehwsim.cpp b/avscamera/sys/imagehwsim.cpp new file mode 100644 index 000000000..64d5eb5de --- /dev/null +++ b/avscamera/sys/imagehwsim.cpp @@ -0,0 +1,1430 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2001, Microsoft Corporation. + + File: + + ImageHwSim.cpp + + Abstract: + + This file contains the implementation of the CImageHardwareSimulation + class. + + This is a specialization of CHardwareSimulation that provides photo- + specific metadata and implements specialized functionality for photo, + photo sequence and variable photo sequence. + + History: + + created 3/9/2001 + +**************************************************************************/ + +#include "Common.h" + +/************************************************************************** + + PAGEABLE CODE + +**************************************************************************/ + +#ifdef ALLOC_PRAGMA +#pragma code_seg("PAGE") +#endif // ALLOC_PRAGMA + + +CImageHardwareSimulation:: +CImageHardwareSimulation ( + _Inout_ CSensor *Sensor, + _In_ LONG PinID +) + : CHardwareSimulation( Sensor, PinID ) + , m_Clock(NULL) + , m_PfsLoopLimit(0) + , m_PfsFrameLimit(0) + , m_pIspSettings(nullptr) + , m_PfsLoopNumber(0) + , m_PfsFrameNumber(0) + , m_GlobalFrameNumber(0) + , m_bEndOfSequence(FALSE) + , m_PastBufferCount(0) // Zero only when the simulation inits. + +/*++ + +Routine Description: + + Construct a hardware simulation + +Arguments: + + Sensor - + The hardware sink interface. This is used to trigger + fake interrupt service routines from. + +Return Value: + + Success / Failure + +--*/ + +{ + PAGED_CODE(); + + InitializeListHead (&m_BurstList); +} + +CImageHardwareSimulation:: +~CImageHardwareSimulation() +{ + + PAGED_CODE(); + + SAFE_DELETE( m_pIspSettings ); + + if (m_Clock) + { + m_Clock -> Release (); + m_Clock = NULL; + } +} + +/*************************************************/ + +NTSTATUS +CImageHardwareSimulation:: +Start ( + _In_ CSynthesizer *ImageSynth, + _In_ ULONG Width, + _In_ ULONG Height, + _In_ ULONG ImageSize, + _In_ PIN_MODE pinMode +) + +/*++ + +Routine Description: + + Start capturing frames. This turns on the timer and begins frame capture, + but it we do not deliver frames until we receive a Trigger. 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. + + 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. + + PinMode - + Normal or photosequence. + + +Return Value: + + Success / Failure (typical failure will be out of memory on the + scratch buffer, etc...) + +--*/ + +{ + + PAGED_CODE(); + + DBG_ENTER("(Width=%d, Height=%d, ImageSize=%d, pinMode=%s): m_PinID=%d", + Width, Height, ImageSize, pinMode?"PinBurstMode":"PinNormalMode", m_PinID ); + + NTSTATUS Status = STATUS_SUCCESS; + + // Prevent state-changes during this call. + KScopedMutex Lock(m_ListLock); + + m_Synthesizer = ImageSynth; + + NT_ASSERT(ImageSize); + m_ImageSize = ImageSize; + m_Height = Height; + m_Width = Width; + + m_NumMappingsCompleted = 0; + m_ScatterGatherMappingsQueued = 0; + m_NumFramesSkipped = 0; + m_InterruptTime = 0; + m_bTriggered = FALSE; + m_bEndOfSequence = FALSE; + m_pClone = NULL; + m_bPastBufferTrigger = FALSE; + m_PinMode = pinMode; + m_TriggerTime = 0; + m_bFlashed = FALSE; + + DBG_TRACE("m_bTriggered=FALSE, m_bPastBufferTrigger=FALSE"); + + m_FlashStatus = 0; + + // Initialize VPS counters. + m_PfsLoopNumber = 0 ; + m_PfsFrameNumber= 0 ; + m_GlobalFrameNumber = 0; + + KeQuerySystemTime (&m_StartTime); + + if( !m_Synthesizer->Initialize() ) + { + Status = STATUS_INSUFFICIENT_RESOURCES; + } + + // + // If everything is ok, start issuing interrupts. + // + if (NT_SUCCESS (Status)) + { + LARGE_INTEGER NextTime; + NextTime.QuadPart = m_StartTime.QuadPart + m_TimePerFrame; + + m_PinState = PinRunning; + m_IsrTimer.Set( NextTime ); + + } + + DBG_LEAVE("(Width=%d, Height=%d, ImageSize=%d, pinMode=%s): m_PinID=%d, Status=0x%08X", + Width, Height, ImageSize, pinMode?"PinBurstMode":"PinNormalMode", m_PinID, Status ); + + return Status; + +} + +NTSTATUS +CImageHardwareSimulation:: +Trigger( + _In_ LONG mode +) +/*++ + +Routine Description: + + Take a picture / Begin delivering frames. + +Arguments: + + mode - + Normal trigger, start or stop photo sequence. + + +Return Value: + + Success / Failure (typical failure will be out of memory on the + scratch buffer, etc...) + +--*/ + +{ + PAGED_CODE(); + + NTSTATUS status = STATUS_SUCCESS; + + DBG_ENTER( "(mode=0x%08X), m_PinMode=%d", mode, m_PinMode ); + + KScopedMutex Lock( m_ListLock ); + + //Start Trigger for Burst Mode + if(mode & KS_VideoControlFlag_StartPhotoSequenceCapture) + { + if(m_bTriggered == FALSE && m_PinMode == PinBurstMode) + { + m_bPastBufferTrigger = TRUE; + m_bTriggered = TRUE; + DBG_TRACE("m_bTriggered=TRUE, m_bPastBufferTrigger=TRUE"); + + status = STATUS_SUCCESS; + } + else + { + status = STATUS_INVALID_PARAMETER; + } + } + //Stop Trigger for Burst Mode + else if(mode & KS_VideoControlFlag_StopPhotoSequenceCapture) + { + if(m_bTriggered == TRUE && m_PinMode == PinBurstMode) + { + m_bTriggered = FALSE; + DBG_TRACE("m_bTriggered=FALSE"); + + // reset the PFS EOS, frame number and loop count. + m_bEndOfSequence = FALSE; + m_PfsFrameNumber = 0; + m_PfsLoopNumber = 0; + + m_bFlashed = FALSE; + status = STATUS_SUCCESS; + } + else + { + status = STATUS_INVALID_PARAMETER; + } + } + //Normal Trigger + else if(mode & KS_VideoControlFlag_Trigger ) + { + if(m_PinMode == PinBurstMode) + { + status = STATUS_INVALID_PARAMETER; + } + else + { + m_bTriggered = TRUE; + m_bEndOfSequence = FALSE; + DBG_TRACE("m_bTriggered=TRUE"); + } + } + + DBG_LEAVE("()"); + + return STATUS_SUCCESS; +} + + +NTSTATUS +CImageHardwareSimulation:: +SetMode( + _In_ ULONGLONG Flags, + _In_ ULONG PastBuffers +) +/*++ + +Routine Description: + + Set the photo sequence mode. + +Arguments: + + Flags - + Normal or photo sequence. + + PastBuffers - + Number of history frames to gather. + + +Return Value: + + Success / Failure (typical failure will be out of memory on the + scratch buffer, etc...) + +--*/ + +{ + PAGED_CODE(); + + DBG_ENTER( "(Flags=0x%016llX, PastBuffers=%d)", Flags, PastBuffers ); + + if(m_bTriggered == TRUE) + { + return STATUS_INVALID_TRANSACTION; + } + + m_PastBufferCount = PastBuffers; + + if(Flags & KSCAMERA_EXTENDEDPROP_PHOTOMODE_SEQUENCE) + { + m_PinMode = PinBurstMode; + } + else + { + m_PinMode = PinNormalMode; + } + + DBG_LEAVE("()"); + + return STATUS_SUCCESS; +} + +// +// Set the interval between frames here. +// +NTSTATUS +CImageHardwareSimulation:: +SetPhotoFrameRate( + _In_ ULONGLONG TimePerFrame +) +{ + PAGED_CODE(); + + // Prevent state-changes during this call. + KScopedMutex Lock(m_ListLock); + + m_TimePerFrame = TimePerFrame; + + // + // Reschedule the timer if the hardware isn't being stopped. + // + if( m_PinState == PinRunning ) // && !m_StopHardware ) + { + // First restart our start time. We can't use the old time. + KeQuerySystemTime( &m_StartTime ); + + // + // Reschedule the timer for the next interrupt time. + // + m_StartTime.QuadPart += m_TimePerFrame; + m_InterruptTime = 0; + + m_IsrTimer.Set( m_StartTime ); + } + return STATUS_SUCCESS; +} + +NTSTATUS +CImageHardwareSimulation:: +Stop() + +/*++ + +Routine Description: + + Stop the hardware simulation... + + Wait until the timer has stopped, flush the queue, dereference the clock + and reset our state before returning. + +Arguments: + + None + +Return Value: + + Success / Failure + +--*/ + +{ + PAGED_CODE(); + + // 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. + // + + DBG_ENTER("(): m_PinID=%d", m_PinID); + + // + // Protect the S/G list + // + KScopedMutex Lock( m_ListLock ); + + CHardwareSimulation::Stop(); + + // + // Free S/G buffer + // + FreeSGList( &m_ScatterGatherMappings, L"StreamPointer Stop Burst List" ); + + if (m_Clock) + { + m_Clock -> Release (); + m_Clock = NULL; + } + + m_bTriggered = FALSE; + m_bEndOfSequence = FALSE; + m_pClone = NULL; + m_bPastBufferTrigger = FALSE; + m_PinMode = PinNormalMode; + m_TriggerTime = 0; + + DBG_TRACE("m_bTriggered=FALSE, m_bPastBufferTrigger=FALSE"); + + DBG_LEAVE("(): m_PinID=%d", m_PinID); + + return STATUS_SUCCESS; +} + + +/*************************************************/ + + +// +// Helper function that collects current settings into our metadata structure. +// +METADATA_IMAGEAGGREGATION +CImageHardwareSimulation:: +GetMetadata() +{ + PAGED_CODE(); + + METADATA_IMAGEAGGREGATION Metadata; + ISP_FRAME_SETTINGS *pSettings = GetIspSettings(); + + // Wipe the metadata so all settings will default to "Not Set". + RtlZeroMemory( &Metadata, sizeof(Metadata) ); + + // Identify the current PFS frame number. + // If PFS not active, then this item is not present. + Metadata.FrameId.Set = IsPfsActive(); + Metadata.FrameId.Value = (ULONG) m_PfsFrameNumber; + DBG_TRACE("Metadata.FrameId.Set=%s, Metadata.FrameId.Value=%d", + (Metadata.FrameId.Set?"Yes":"No"), Metadata.FrameId.Value); + + // Just reflect the exposure time from the setting. + //Metadata.ExposureTime.Set = TRUE; + //Metadata.ExposureTime.Value = GetCurrentExposureTime(); + + // Just reflect the ISO Speed from the setting. + Metadata.ISOSpeed = CMetadataLong(GetCurrentISOSpeed()); + DBG_TRACE("ISO=%d, ISO Flags=0x%016llX", Metadata.ISOSpeed.Value, pSettings->ISOMode); + + // TODO: Do we need to bracket this by whether or not a flash has been taken? + // Report the current flash mode. + Metadata.FlashOn = CMetadataLong((ULONG) pSettings->FlashMode); + + // Report the current flash power. + Metadata.FlashPower = CMetadataLong(pSettings->FlashValue); + + // Set the White Balance lock state. + Metadata.WhiteBalanceLocked = CMetadataLong( + ( (pSettings->WhiteBalanceMode & KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK) + == KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK) ); + + // Set the Exposure lock state. + Metadata.ExposureLocked = CMetadataLong( + ( (pSettings->ExposureMode & KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK) + == KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_LOCK) ); + + Metadata.ExposureTime = + //CMetadataRational(GetCurrentExposureTime(), 10000000); + CMetadataLongLong( GetCurrentExposureTime() ); + + Metadata.LensPosition = CMetadataLong( pSettings->FocusSetting.VideoProc.Value.ul ); + + Metadata.SceneMode = CMetadataULongLong(KSCAMERA_EXTENDEDPROP_SCENEMODE_AUTO); //TODO: Need to fill in real value from CCaptureFilter::m_SceneMode + + Metadata.WhiteBalanceMode = CMetadataLong((ULONG) pSettings->WhiteBalanceMode); + + CExtendedVidProcSetting Zoom; + m_Sensor->GetZoom( &Zoom ); + Metadata.ZoomFactor = CMetadataLong(Zoom.GetLONG()); //TODO: Fill in a real value from zoom simulation. + + Metadata.FocusLocked = CMetadataLong(FALSE); //TODO: Fill in a real value when we complete the focus changes. + + // Add EVCompensation metadata... + Metadata.EVCompensation = CMetadataEVCompensation(pSettings->EVCompensation.Mode, pSettings->EVCompensation.Value); + + Metadata.Orientation = CMetadataShort(Metadata_Orientation_TopBottomLeftRight); //TODO: Randomize? + + { + LARGE_INTEGER SystemTime; + LARGE_INTEGER LocalTime; + + KeQuerySystemTimePrecise( &SystemTime ); + ExSystemTimeToLocalTime( &SystemTime, &LocalTime ); + RtlTimeToTimeFields( &LocalTime, &Metadata.LocalTime.Time ); + Metadata.LocalTime.Set = TRUE; + } + + Metadata.Make = CMetadataShortString("Make: Microsoft SOC Camera"); + Metadata.Model = CMetadataShortString( "Model: AvsCam" ); + Metadata.Software = CMetadataShortString( "Software: Microsoft Camera Sim" ); + + Metadata.ColorSpace.Set = TRUE; + Metadata.ColorSpace.Value = 0xFFFF; // 0xFFFF Means "uncalibrated". Use this value for all non-RGB formats. + + Metadata.Gamma = CMetadataRational(); + + Metadata.MakerNote = CMetadataShortString( "Maker's Note..." ); + + // Just reflect the exposure time from the setting. + //Metadata.ExposureTime = + // CMetadataRational( GetCurrentExposureTime(), 1000 ); // report exposure time as milliseconds. + + Metadata.FNumber = CMetadataRational(4); // Fake an FNumber of 4. // TODO: It looks like we might be able to calculate this. + + Metadata.ExposureProgram.Set = TRUE; + Metadata.ExposureProgram.Value = GetExposureProgram(); + + Metadata.ShutterSpeedValue = CMetadataSRational(); // TODO: Calculate this from the ExposureTime. *** + Metadata.Aperture = CMetadataRational(4); // TODO: Calculate this from the F-Number. (We're currently faking the FNumber.) + Metadata.Brightness = CMetadataSRational(0); // TODO: Find a more reasonable brightness value. + Metadata.ExposureBias = CMetadataSRational(0); // TODO: More reasonable? + Metadata.SubjectDistance = CMetadataRational(0xFFFFFFFF); // Distance in meters. Infinity. (Anything better?) + + Metadata.MeteringMode.Set = TRUE; + Metadata.MeteringMode.Value = (USHORT) GetRandom( (ULONG) 1, (ULONG) 6); // Pick a number ... any number. + + Metadata.LightSource.Set = TRUE; + Metadata.LightSource.Value = 1; // TODO: Pick a random value; but override when a flash occurs. + + Metadata.Flash.Set = TRUE; + Metadata.Flash.Value = (UINT16) pSettings->FlashMode; // We assume that the flash fires when requested! + DBG_TRACE("FlashMode=0x%016llX, FlashPower=%d", pSettings->FlashMode, pSettings->FlashValue); + + Metadata.FocalLength = CMetadataRational(); // TODO: Calculate? + Metadata.FocalPlaneXResolution = CMetadataRational(); // TODO: Calculate? + Metadata.FocalPlaneYResolution = CMetadataRational(); // TODO: Calculate? + Metadata.ExposureIndex = CMetadataRational(); // TODO: Calculate? + + Metadata.ExposureMode.Set = TRUE; + Metadata.ExposureMode.Value = 0 ; // Assume Auto exposure. + if( pSettings->ExposureMode & KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_MANUAL ) + { + Metadata.ExposureMode.Value = 0 ; // Manual exposure. + } + + Metadata.WhiteBalance.Set = TRUE; + Metadata.WhiteBalance.Value = 0 ; // Assume Auto white balance. + if( pSettings->WhiteBalanceMode & KSCAMERA_EXTENDEDPROP_VIDEOPROCFLAG_MANUAL ) + { + Metadata.WhiteBalance.Value = 0 ; // Manual while balance. + } + + Metadata.DigitalZoomRatio = CMetadataRational(1); + + Metadata.FocalLengthIn35mmFilm = CMetadataShort(0); + Metadata.SceneCaptureType = CMetadataShort(0); + Metadata.GainControl = CMetadataRational(); + Metadata.Contrast = CMetadataShort(0); + Metadata.Saturation = CMetadataShort(0); + Metadata.Sharpness = CMetadataShort(0); + Metadata.SubjectDistanceRange = CMetadataShort(0); + + // Report (optional) focus state. + KSCAMERA_EXTENDEDPROP_FOCUSSTATE State = KSCAMERA_EXTENDEDPROP_FOCUSSTATE_UNINITIALIZED; + if( NT_SUCCESS(m_Sensor->GetFocusState( &State )) ) + { + Metadata.FocusState = CMetadataLong((UINT32)State); + } + + return Metadata; +} + +// +// Emit metadata here for still pin. +// +void +CImageHardwareSimulation:: +EmitMetadata( + _Inout_ PKSSTREAM_HEADER pStreamHeader +) +/*++ + +Routine Description: + + Emit metadata for a photo. + +Arguments: + + None + +Return Value: + + Success / Failure + +--*/ +{ + PAGED_CODE(); + + NT_ASSERT(pStreamHeader); + + // Add the normal frame info to the metadata + CHardwareSimulation::EmitMetadata( pStreamHeader ); + + if (0 != (pStreamHeader->OptionsFlags & KSSTREAM_HEADER_OPTIONSF_METADATA)) + { + PKS_FRAME_INFO pFrameInfo = (PKS_FRAME_INFO)(pStreamHeader + 1); + PKSSTREAM_METADATA_INFO pMetadata = (PKSSTREAM_METADATA_INFO) (pFrameInfo + 1); + PCAMERA_METADATA_IMAGEAGGREGATION pAggregation = + (PCAMERA_METADATA_IMAGEAGGREGATION) (((PBYTE) pMetadata->SystemVa) + pMetadata->UsedSize); + ULONG BytesLeft = pMetadata->BufferSize - pMetadata->UsedSize; + + if( BytesLeft >= sizeof(*pAggregation) ) + { + pAggregation->Header.MetadataId = (ULONG) MetadataId_Custom_ImageAggregation; + pAggregation->Header.Size = sizeof(*pAggregation); + + // Just copy over the current frame's ISP settings for now. + // We still need to develop a contract between the driver and the MFT0 for these settings. + pAggregation->Data = GetMetadata(); + + pMetadata->UsedSize += sizeof(*pAggregation); + BytesLeft -= sizeof(*pAggregation); + } + + CExtendedVidProcSetting FaceDetect; + m_Sensor->GetFaceDetection(&FaceDetect); + + if( FaceDetect.Flags & KSCAMERA_EXTENDEDPROP_FACEDETECTION_PHOTO ) + { + DBG_TRACE("IMAGE"); + EmitFaceMetadata( + pStreamHeader, + FaceDetect.GetULONG(), + FaceDetect.Flags & KSCAMERA_EXTENDEDPROP_FACEDETECTION_ADVANCED_MASK, + 1); + } + } +} + +NTSTATUS +CImageHardwareSimulation:: +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 + +--*/ + +{ + PAGED_CODE(); + + DBG_ENTER("() m_PinID=0x%08X, m_ImageSize=0x%08X, m_ScatterGatherMappingsQueued=%d, " + "m_ScatterGatherBytesQueue=0x%08X", + m_PinID, m_ImageSize, m_ScatterGatherMappingsQueued, m_ScatterGatherBytesQueued); + + // + // We're using this list lock to protect our scatter / gather lists instead + // of some hardware mechanism / KeSynchronizeExecution / whatever. + // + //KeAcquireSpinLockAtDpcLevel (&m_ListLock); + + ULONG BufferRemaining = m_ImageSize; + + // + // If there aren't enough scatter / gather buffers queued, consider it starvation. + // + while( BufferRemaining && + !IsListEmpty(&m_ScatterGatherMappings) && + m_ScatterGatherBytesQueued >= BufferRemaining) + { + DBG_TRACE( "BufferRemaining=0x%08X, m_ScatterGatherBytesQueued=0x%08X, m_ScatterGatherMappingsQueued=%d", + BufferRemaining, m_ScatterGatherBytesQueued, m_ScatterGatherMappingsQueued ); + + LIST_ENTRY *listEntry = RemoveHeadList (&m_ScatterGatherMappings); + m_ScatterGatherMappingsQueued--; + + PSCATTER_GATHER_ENTRY SGEntry = + reinterpret_cast ( + CONTAINING_RECORD ( + listEntry, + SCATTER_GATHER_ENTRY, + ListEntry + ) + ); + + // Deal with cancellation. + PIRP pIrp = KsStreamPointerGetIrp(SGEntry->CloneEntry, FALSE, FALSE); + if (pIrp) + { + if (pIrp->Cancel) + { + DBG_TRACE( "Cancelling..." ); + FreeSGEntry( listEntry, L"StreamPointer Cancel SG List" ); + continue; + } + } + + // + // Since we're software, we'll be accessing this by virtual address... + // + ULONG BytesToCopy = min( BufferRemaining, SGEntry->ByteCount ); + + // Have the synthesizer output a frame to the buffer. + DBG_TRACE( "DataUsed before Commit() = %d", SGEntry->CloneEntry->StreamHeader->DataUsed ); + ULONG BytesCopied = + m_Synthesizer->DoCommit( SGEntry->Virtual, BytesToCopy ); + NT_ASSERT(BytesCopied); + DBG_TRACE( "BytesCopied = %d", BytesCopied ); + + BufferRemaining = 0; //-= BytesCopied; + + // Add metadata to the sample. + EmitMetadata( SGEntry -> CloneEntry -> StreamHeader ); + + ULONGLONG time = ConvertQPCtoTimeStamp(NULL); + + if (IsPhotoConfirmationNeeded()) + { + DBG_TRACE( "PhotoConfirmation is needed. Frame=%d, Time=0x%016llX", m_PfsFrameNumber, (LONGLONG) time ); + SGEntry->PhotoConfirmationInfo = PHOTOCONFIRMATION_INFO( m_PfsFrameNumber, (LONGLONG) time ); + } + + SGEntry -> CloneEntry -> StreamHeader -> PresentationTime.Time = time; + DBG_TRACE("PresentationTime = 0x%016llX", SGEntry->CloneEntry->StreamHeader->PresentationTime.Time ); + + SGEntry -> CloneEntry -> StreamHeader -> OptionsFlags |= KSSTREAM_HEADER_OPTIONSF_TIMEVALID; + + DBG_TRACE("m_FlashStatus=0x%016llX", m_FlashStatus); + + if(m_FlashStatus & KSCAMERA_EXTENDEDPROP_FLASH_ON || m_FlashStatus & KSCAMERA_EXTENDEDPROP_FLASH_ON_ADJUSTABLEPOWER || + m_FlashStatus & KSCAMERA_EXTENDEDPROP_FLASH_AUTO || m_FlashStatus & KSCAMERA_EXTENDEDPROP_FLASH_AUTO_ADJUSTABLEPOWER) + { + if(m_FlashStatus & KSCAMERA_EXTENDEDPROP_FLASH_SINGLEFLASH && time >= m_TriggerTime && m_TriggerTime != 0 && !m_bFlashed) + { + m_bFlashed = TRUE; + DBG_TRACE("(Single) FLASHED!!!"); + } + } + + // + // Release the scatter / gather entry back to our lookaside. + // + if( m_bTriggered && !IsPfsEOS() ) + { + DBG_TRACE("m_PinMode=%d", m_PinMode); + m_pClone = SGEntry->CloneEntry; + m_PhotoConfirmationInfo = SGEntry->PhotoConfirmationInfo; + m_NumMappingsCompleted++; + m_ScatterGatherBytesQueued -= SGEntry -> ByteCount; + + DBG_TRACE( "m_NumMappingsCompleted=%d, m_PhotoConfirmationInfo.isRequired()=%s", m_NumMappingsCompleted, m_PhotoConfirmationInfo.isRequired()?"TRUE":"FALSE" ); + + if(m_PinMode != PinBurstMode) + { + m_bTriggered = FALSE; + DBG_TRACE("m_bTriggered=FALSE"); + } + + // Update the VPS frame and loop numbers here. Mark the frame as the EOS + // if we've completed the sequence. + // + // Note: It's actually up to DevProxy to stop feeding us frames! + if( AdvanceFrameCounter() ) + { + // We've reached the end of a VPS sequence! Mark the frame as EOS. + SGEntry->CloneEntry->StreamHeader->OptionsFlags |= KSSTREAM_HEADER_OPTIONSF_ENDOFPHOTOSEQUENCE; + m_bEndOfSequence = TRUE; + } + + ExFreeToNPagedLookasideList ( + &m_ScatterGatherLookaside, + reinterpret_cast (SGEntry) + ); + } + else + { + InsertTailList( &m_ScatterGatherMappings, listEntry ); + m_ScatterGatherMappingsQueued++; + m_pClone = NULL; + } + } + + DBG_LEAVE("()"); + + if (BufferRemaining) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + else + { + return STATUS_SUCCESS; + } +} + +/*************************************************/ + +void +CImageHardwareSimulation:: +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 + +--*/ + +{ + PAGED_CODE(); + + // Prevent state-changes during this call. + KScopedMutex Lock(m_ListLock); + + 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_PinState == PinRunning ) + { + if(m_PinMode == PinBurstMode && m_bTriggered && m_bPastBufferTrigger) + { + CompletePastBuffers(); + } + + m_Synthesizer->DoSynthesize(); + + // + // 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_Sensor -> Interrupt (m_PinID); + + // + // Schedule the timer for the next interrupt time, if the pin is still running. + // + if( m_PinState == PinRunning ) + { + LARGE_INTEGER NextTime; + NextTime.QuadPart = m_StartTime.QuadPart + + (m_TimePerFrame * (m_InterruptTime + 1)); + +#ifdef ENABLE_TRACING // To keep us from a tight spin when trying to debug this code... + LARGE_INTEGER Now; + KeQuerySystemTime(&Now); + + if( Now.QuadPart >= NextTime.QuadPart ) + { + NextTime.QuadPart = 0LL - m_TimePerFrame ; + } + +#endif + m_IsrTimer.Set( NextTime ); + } +} + +NTSTATUS +CImageHardwareSimulation:: +CompletePastBuffers() + +/*++ + +Routine Description: + + Find and complete any history frames. + +Arguments: + + None + +Return Value: + + Success / Failure + +--*/ + +{ + PAGED_CODE(); + + ULONG ulNumBuffers = m_PastBufferCount; + BOOLEAN bContinue = TRUE; + LIST_ENTRY *listEntry = NULL; + + DBG_ENTER("()"); + DBG_TRACE("m_PastBufferCount=%d, m_TriggerTime=0x%016llX", m_PastBufferCount, m_TriggerTime ); + + // If we're in burst mode and have ISP settings, we can't + // really support changing the ISP settings in the past... + // ... so we'll just treat them all past frames. + // Note: The upper layer will determine past frames from the + // metadata FrameId. + if( m_PinMode == PinBurstMode && + !m_pIspSettings ) + { + // Walk through the entire list of buffers, find the most + // recent frame presentation time and put all past buffers + // into another list. + while( !IsListEmpty(&m_ScatterGatherMappings) ) + { + listEntry = RemoveTailList(&m_ScatterGatherMappings); + m_ScatterGatherMappingsQueued--; + + PSCATTER_GATHER_ENTRY SGEntry = + reinterpret_cast ( + CONTAINING_RECORD ( + listEntry, + SCATTER_GATHER_ENTRY, + ListEntry + ) + ); + + NT_ASSERT(SGEntry); + NT_ASSERT(SGEntry->CloneEntry); + NT_ASSERT(SGEntry->CloneEntry->StreamHeader); + + // We've found one that's not stamped. We must be at the end. + // Push it back and exit the loop. + if(SGEntry->CloneEntry->StreamHeader->PresentationTime.Time == 0) + { + InsertTailList(&m_ScatterGatherMappings, listEntry); + m_ScatterGatherMappingsQueued++; + DBG_TRACE( "No past frames found." ); + bContinue = FALSE; + break; + } + + // Since we're walking from the most recent to least recent frame, + // This one is a valid "future" frame. + DBG_TRACE( "Adding 'future frame' to the list" ); + PushCloneList(SGEntry); + + // If the presentation time is less than the trigger time, stop here + // and use this as the first triggered frame. + if((ULONGLONG)(SGEntry->CloneEntry->StreamHeader->PresentationTime.Time) < m_TriggerTime) + { + DBG_TRACE( "First matching frame time=0x%016llX", SGEntry->CloneEntry->StreamHeader->PresentationTime.Time ); + break; + } + + // Watch out! We might actually need to pick up multiple frames since + // in theory we could have generated several since the trigger time. + } + } + + // Grab N past frames from the queue, if we have them. + while( bContinue && + !IsListEmpty(&m_ScatterGatherMappings) && + ulNumBuffers) + { + listEntry = RemoveTailList(&m_ScatterGatherMappings); + m_ScatterGatherMappingsQueued--; + + PSCATTER_GATHER_ENTRY SGEntry = + reinterpret_cast ( + CONTAINING_RECORD ( + listEntry, + SCATTER_GATHER_ENTRY, + ListEntry + ) + ); + + if(!(SGEntry -> CloneEntry -> StreamHeader -> OptionsFlags & KSSTREAM_HEADER_OPTIONSF_TIMEVALID) ) + { + InsertTailList(&m_ScatterGatherMappings, listEntry); + m_ScatterGatherMappingsQueued++; + DBG_TRACE( "No more past frames found." ); + bContinue = FALSE; + } + else + { + PushCloneList(SGEntry); + ulNumBuffers--; + DBG_TRACE( "Past frame #%d found (%p)", ulNumBuffers, SGEntry->CloneEntry->StreamHeader ); + } + } + + // If we got all of the past frames we needed and didn't consume the entire queue... + if(bContinue && (m_ScatterGatherMappingsQueued > 0)) + { + //Mark the next tail as Time = 0 so that we don't output any super old frames. + listEntry = RemoveTailList(&m_ScatterGatherMappings); + m_ScatterGatherMappingsQueued--; + + PSCATTER_GATHER_ENTRY SGEntry = + reinterpret_cast ( + CONTAINING_RECORD ( + listEntry, + SCATTER_GATHER_ENTRY, + ListEntry + ) + ); + // Mark the PTS as invalid. + SGEntry->CloneEntry->StreamHeader->PresentationTime.Time = 0; + SGEntry->CloneEntry->StreamHeader->OptionsFlags &= ~KSSTREAM_HEADER_OPTIONSF_TIMEVALID; + + InsertTailList(&m_ScatterGatherMappings, listEntry); + m_ScatterGatherMappingsQueued++; + } + + CompleteCloneList(); + + m_bPastBufferTrigger = FALSE; + + DBG_LEAVE("()"); + + return STATUS_SUCCESS; + +} + +// +// Put an item onto the history list. +// +void +CImageHardwareSimulation:: +PushCloneList( + _Inout_ PSCATTER_GATHER_ENTRY SGEntry +) +{ + PAGED_CODE(); + + DBG_TRACE( "Frame %p, PresentationTime=0x%016llX", + SGEntry->CloneEntry->StreamHeader, + SGEntry->CloneEntry->StreamHeader->PresentationTime.Time ); + + InsertHeadList( &m_BurstList, &SGEntry->ListEntry ); +} + +// +// Complete the history list. +// +NTSTATUS +CImageHardwareSimulation:: +CompleteCloneList() +{ + PAGED_CODE(); + + int i = 0; + while(!IsListEmpty(&m_BurstList)) + { + LIST_ENTRY *listEntry = RemoveHeadList(&m_BurstList); + + PSCATTER_GATHER_ENTRY SGEntry = + reinterpret_cast ( + CONTAINING_RECORD ( + listEntry, + SCATTER_GATHER_ENTRY, + ListEntry + ) + ); + + m_pClone = SGEntry->CloneEntry; + m_PhotoConfirmationInfo = SGEntry->PhotoConfirmationInfo; + + m_NumMappingsCompleted++; + m_ScatterGatherBytesQueued -= SGEntry -> ByteCount; + + DBG_TRACE( "m_NumMappingsCompleted=%d, m_PhotoConfirmationInfo.isRequired()=%s", m_NumMappingsCompleted, m_PhotoConfirmationInfo.isRequired( )?"TRUE":"FALSE" ); + DBG_TRACE( "Frame %p, PresentationTime=0x%016llX", + SGEntry->CloneEntry->StreamHeader, + SGEntry->CloneEntry->StreamHeader->PresentationTime.Time ); + + m_Sensor -> Interrupt (m_PinID); + + ExFreeToNPagedLookasideList ( + &m_ScatterGatherLookaside, + reinterpret_cast (SGEntry) + ); + i++; + } + + return STATUS_SUCCESS; +} + +NTSTATUS +CImageHardwareSimulation:: +SetClock(PKSPIN pin) +{ + PAGED_CODE(); + + if(!NT_SUCCESS(KsPinGetReferenceClockInterface(pin, &m_Clock))) + { + m_Clock = NULL; + } + + return STATUS_SUCCESS; +} + +void +CImageHardwareSimulation:: +SetTriggerTime( + _In_ ULONGLONG TriggerTime +) +/*++ + +Routine Description: + + Identify exactly when the user pressed that button. + +Arguments: + + TriggerTime - + The QPC time in 100ns when the user asked for the photo. + +Return Value: + + void + +--*/ +{ + PAGED_CODE(); + + m_TriggerTime = TriggerTime; + DBG_TRACE( "Setting Trigger Time = 0x%016llX", TriggerTime ); +} + +NTSTATUS +CImageHardwareSimulation:: +Reset() +{ + PAGED_CODE(); + + KScopedMutex Lock( m_ListLock ); + DBG_ENTER("(): m_PinID=%d", m_PinID); + + // Parent class reset first... + CHardwareSimulation::Reset(); + + FreeSGList( &m_ScatterGatherMappings, L"StreamPointer Reset Burst List" ); + + m_bTriggered = FALSE; + m_bEndOfSequence = FALSE; + m_pClone = NULL; + m_bPastBufferTrigger = FALSE; + m_TriggerTime = 0; + + DBG_LEAVE("(): m_PinID=%d", m_PinID); + + return STATUS_SUCCESS; +} + +NTSTATUS +CImageHardwareSimulation:: +SetFlashStatus( + _In_ ULONGLONG ullFlashStatus +) +{ + PAGED_CODE(); + + m_FlashStatus = ullFlashStatus; + + DBG_TRACE("FlashStatus=0x%016llX", ullFlashStatus); + + return STATUS_SUCCESS; +} + +// +// Program the Per Frame Settings for simulation +// We do it before calling start so we can be ready +// to program our simulation's hardware. +// +// Note: We make a local copy. +// +NTSTATUS +CImageHardwareSimulation:: +SetPFS( + _In_ ISP_FRAME_SETTINGS *pIspSettings, + _In_ ULONG FrameLimit, + _In_ ULONG LoopLimit +) +{ + PAGED_CODE(); + + // Set the current ISP settings and free any prior. + // This has to be done at simulation start or stop or we have + // a synchronization problem. + SAFE_DELETE_ARRAY( m_pIspSettings ); + + if( pIspSettings ) + { + m_pIspSettings = new (NonPagedPoolNx) ISP_FRAME_SETTINGS[FrameLimit]; + if( !m_pIspSettings ) + { + m_PfsFrameLimit = 0; + m_PfsLoopLimit = 0; + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory( m_pIspSettings, pIspSettings, FrameLimit*sizeof(ISP_FRAME_SETTINGS) ); + m_PfsLoopLimit = LoopLimit; + m_PfsFrameLimit = FrameLimit; + } + else + { + m_PfsFrameLimit = 0; + m_PfsLoopLimit = 0; + } + + return STATUS_SUCCESS; +} + +// Function: +// bool CImageHardwareSimulation::AdvanceFrameCounter(void) +// +// Description: +// Advance our frame and loop pointers to the next PFS settings. +// +// Parameters: +// [None] +// +// Returns: +// bool - true if we've reached the end of our Per Frame Settings. +// +bool +CImageHardwareSimulation:: +AdvanceFrameCounter(void) +{ + PAGED_CODE(); + + bool bEOS = false; + + DBG_ENTER( "()" ); + + m_GlobalFrameNumber ++; + DBG_TRACE( "m_GlobalFrameNumber=%lld", m_GlobalFrameNumber ); + + // + // Calculate the PFS frame & loop numbers, but only if we've + // gotten ISP settings. + // + if( m_bTriggered && + m_PinMode == PinBurstMode && + m_pIspSettings ) + { + m_PfsFrameNumber ++; + + if( m_PfsFrameNumber >= m_PfsFrameLimit ) + { + m_PfsFrameNumber = 0; + m_PfsLoopNumber ++; + } + + // Only mark EOS if we're not in an infinite loop. + if( m_PfsLoopLimit != 0 ) + { + NT_ASSERT( !(m_PfsLoopNumber > m_PfsLoopLimit) ); + // Check to see if we've hit our limit. + if( m_PfsLoopNumber >= m_PfsLoopLimit ) + { + DBG_TRACE( "Marking EOS" ); + bEOS = true; + } + } + } + + DBG_TRACE( "m_PfsFrameNumber=%d, m_PfsLoopNumber=%d", m_PfsFrameNumber, m_PfsLoopNumber ); + DBG_TRACE( "m_PfsFrameLimit=%d, m_PfsLoopLimit=%d", m_PfsFrameLimit, m_PfsLoopLimit ); + DBG_LEAVE( "() = %s", bEOS ? "true" : "false" ); + return bEOS; +} + + +// Function: +// bool CImageHardwareSimulation::IsPfsEOS(void) +// +// Description: +// Determine if we're at the EOS. +// +// Parameters: +// [None] +// +// Returns: +// bool - true if we've reached the end of our Per Frame Settings. +// +bool +CImageHardwareSimulation:: +IsPfsEOS(void) +{ + PAGED_CODE(); + + DBG_ENTER( "()" ); + DBG_TRACE( "m_bTriggered=%s, m_bEndOfSequence=%s, m_PinMode=%d, m_pIspSetting=0x%p", + ( m_bTriggered ? "true" : "false" ), + ( m_bEndOfSequence ? "true" : "false" ), + m_PinMode, + m_pIspSettings ); + + // + // Make sure we're in a Variable Photo Sequence. + // + bool result = + bool( m_bTriggered == TRUE && + m_bEndOfSequence && + m_PinMode == PinBurstMode && + m_pIspSettings ); // Must have ISP settings set to be PFS EOS. + + DBG_LEAVE( "() = %s", (result ? "true" : "false") ); + return result; +} + +// Function: +// BOOL CImageHardwareSimulation::IsPfsActive(void) +// +// Description: +// Determine if we're in a Variable Photo Sequence. +// +// Parameters: +// [None] +// +// Returns: +// BOOL - TRUE if we are actively processing Per Frame Settings. +// +BOOL +CImageHardwareSimulation:: +IsPfsActive(void) +{ + PAGED_CODE(); + + return + BOOL( m_bTriggered && !m_bEndOfSequence && + m_PinMode == PinBurstMode && + m_pIspSettings ) ; +} + +// Get the current frame settings. +// +// Note: +// Call this function to acquire ISP settings for the current frame's +// simulation. Initially we'll just use it to report back the ISP +// settings originally requested in the PFS by the user. +// +ISP_FRAME_SETTINGS * +CImageHardwareSimulation:: +GetIspSettings(void) +{ + PAGED_CODE(); + + return + IsPfsActive() + ? &m_pIspSettings[m_PfsFrameNumber] + : CHardwareSimulation::GetIspSettings() ; +} + +BOOLEAN +CImageHardwareSimulation:: +IsPhotoConfirmationNeeded() +/*++ + +Routine Description: + + Check flags for photo confirmation and return + whether driver should issue confirmation. + +--*/ +{ + PAGED_CODE(); + + ISP_FRAME_SETTINGS *pSettings = GetIspSettings(); + + return pSettings ? pSettings->bPhotoConfirmation : FALSE; +} diff --git a/avscamera/sys/imagehwsim.h b/avscamera/sys/imagehwsim.h new file mode 100644 index 000000000..64357ad5e --- /dev/null +++ b/avscamera/sys/imagehwsim.h @@ -0,0 +1,202 @@ +/************************************************************************** + + A/V Stream Camera Sample + + Copyright (c) 2001, Microsoft Corporation. + + File: + + ImageHwSim.h + + Abstract: + + This file contains the definition of the CImageHardwareSimulation class. + + This is a specialization of CHardwareSimulation that provides photo- + specific metadata and implements specialized functionality for photo, + photo sequence and variable photo sequence. + + History: + + created 3/9/2001 + +**************************************************************************/ + +// forward references +struct ISP_FRAME_SETTINGS; + +class CImageHardwareSimulation + : public CHardwareSimulation +{ +private: + LIST_ENTRY m_BurstList; + PWSTR m_szwFramePath; + BOOLEAN m_bTriggered; + BOOLEAN m_bEndOfSequence; + BOOLEAN m_bFlashed; + BOOLEAN m_bPastBufferTrigger; + PIN_MODE m_PinMode; + ULONG m_PastBufferCount; + ULONGLONG m_TriggerTime; + PIKSREFERENCECLOCK m_Clock; + ULONGLONG m_FlashStatus; + + // Settings for VPS simulation. + ULONG m_PfsLoopLimit; + ULONG m_PfsFrameLimit; + ISP_FRAME_SETTINGS *m_pIspSettings; // + ULONG m_PfsLoopNumber; // Number of loops we've done. + ULONG m_PfsFrameNumber; + ULONGLONG m_GlobalFrameNumber; // Informational only. + + // Copy a synthesized framebuffer. + virtual + NTSTATUS + FillScatterGatherBuffers(); + + // Find and complete any history frames. + NTSTATUS + CompletePastBuffers(); + + // Put an item onto the history list. + void + PushCloneList(_Inout_ PSCATTER_GATHER_ENTRY SGEntry); + + // Complete the history list. + NTSTATUS + CompleteCloneList(); + + // Add Photo-specific metadata. + void + EmitMetadata( + _Inout_ PKSSTREAM_HEADER pStreamHeader + ); + + // Does the current frame need a photo confirmation? + BOOLEAN + IsPhotoConfirmationNeeded(); + + // Helper function that gets a buffer full of image metadata. + METADATA_IMAGEAGGREGATION + GetMetadata(); + +public: + PKSSTREAM_POINTER m_pClone; + PHOTOCONFIRMATION_INFO m_PhotoConfirmationInfo; + + // Constructor + CImageHardwareSimulation ( + _Inout_ CSensor *Sensor, + _In_ LONG PinID + ); + + // Destructor + virtual + ~CImageHardwareSimulation(); + + // Periodic interrupt handling + virtual + void + FakeHardware(); + + // Start streaming. + virtual + NTSTATUS + Start( + _In_ CSynthesizer *ImageSynth, + _In_ ULONG Width, + _In_ ULONG Height, + _In_ ULONG ImageSize, + _In_ PIN_MODE pinMode + ); + + // Stop streaming. + virtual + NTSTATUS + Stop(); + + virtual + NTSTATUS + Reset(); + + // Take a picture or start a photo sequence. + virtual + NTSTATUS + Trigger( + _In_ LONG mode + ); + + // Limit the rate of frames produced. + virtual + NTSTATUS + SetPhotoFrameRate ( + _In_ ULONGLONG TimePerFrame + ); + + // Get the rate of frames produced. + virtual + ULONGLONG + GetPhotoFrameRate() + { + return m_TimePerFrame; + } + + // Set the master clock. + NTSTATUS + SetClock( + _In_ PKSPIN pin + ); + + // Identify exactly when the user pressed that button. + void + SetTriggerTime( + _In_ ULONGLONG TriggerTime + ); + + // Report the current trigger time. + ULONGLONG + GetTriggerTime() + { + return m_TriggerTime; + } + + // Normal vs. Photo sequence + number of history frames. + NTSTATUS + SetMode( + _In_ ULONGLONG Flags, + _In_ ULONG PastBuffers + ); + + NTSTATUS + SetFlashStatus( + _In_ ULONGLONG ulFlashStatus + ); + + // Program the Per Frame Settings for simulation + // We do it before calling start so we can be ready + // to program our simulation's hardware. + // Note: We make a local copy. + NTSTATUS + SetPFS( + _In_ ISP_FRAME_SETTINGS *pIspSettings, + _In_ ULONG FrameLimit, + _In_ ULONG LoopLimit + ); + + // Advance our frame and loop pointers to the next PFS settings. + bool + AdvanceFrameCounter(void); + + // Determine if we're at the EOS. + bool + IsPfsEOS(void); + + // Determine if we're in a Variable Photo Sequence. + BOOL + IsPfsActive(void); + + // Get the current frame settings. + virtual + ISP_FRAME_SETTINGS * + GetIspSettings(void); +}; diff --git a/avstream/avshws/ReadMe.md b/avstream/avshws/ReadMe.md new file mode 100644 index 000000000..276a71623 --- /dev/null +++ b/avstream/avshws/ReadMe.md @@ -0,0 +1,107 @@ +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. + + +Build the sample +---------------- + +Install the sample files +------------------------ + +To install the sample files in preparation for building and running the sample, follow these steps: + +1. Click the blue **C++** Download button above. +2. The files will be downloaded to a temporary folder. Move them to a convenient location on your hard drive. +3. Unzip the files by right-clicking the folder, and then select **Extract All…** and select another folder location. + +Provision a target computer +--------------------------- + +After you’ve installed the sample on your host computer, run Visual Studio 2013, and from the **File** menu, select **Open**, then **Project/Solution…**, navigate to the directory where you’ve copied the Avshws sample, then to the C++ folder, and select **avshws.vcxproj** (the VC++ Project). + +In the **Solution Explorer** pane in Visual Studio, at the top is **Solution ‘avshws’**. Right-click this and select **Configuration Manager**. Follow the instructions in [Building a Driver with the WDK](http://msdn.microsoft.com/en-us/library/windows/hardware/ff554644) to set the platform, operating system, and debug configuration you want to use, and to build the sample. This sample project will automatically sign the driver package. + +Provision your target computer using instructions in, for example, [Preparing a Computer for Provisioning (WDK 8.1)](http://msdn.microsoft.com/en-us/library/windows/hardware/dn265573). Ensure that in the **Network and Sharing Center** control panel your target computer has **Network Discovery** and **File and Printer Sharing** enabled. + +Deploy the driver to the target computer +---------------------------------------- + +Now you can deploy the Avshws driver that you’ve just built to the target computer, using guidance in [Deploying a Driver to a Test Computer](http://msdn.microsoft.com/en-us/library/windows/hardware/hh454834). Specifically, find the package file under the **Package** folder in the Avshws solution. Right-click **package** and select **Properties**. Under Configuration Properties, click **Driver install** and then **Deployment**. Here you must click the check box for **Enable deployment**, and then click the button to the right of **\**. In the next dialog you enter the **Target Computer Name** and can let the host computer automatically provision the target computer and set up debugger options. + +Finally, in Visual Studio 2013, from the **Build** menu select **Deploy Solution** to deploy the sample to the target computer. On the target computer, you can see the deployed package in the **%Systemdrive%\\drivertest\\drivers** folder. + +Install the driver +------------------ + +On the target computer, open Device Manager, and follow these steps: + +1. In the **Action** menu, click **Add Legacy Hardware**, and the **Add Hardware Wizard** appears. Click **Next** and then **Next** again. +2. In the **Add Hardware** window, select **Show All Devices**. +3. In the **Manufacturer** list in the left pane, click **Microsoft**. +4. You should see the **AVStream Simulated Hardware Sample** in the **Model** pane on the right. Click this and then click **Next**. +5. Click **Next** again to install the driver, and then click **Finish** to exit the wizard. + +The sample driver now appears in the Device Manager console tree under **Sound, video and game controllers**. The Avshws INF file will be on the system drive at, for example, **…windows\\System32\\DriverStore\\FileRepository\\**. + +Sample code hierarchy +--------------------- + +[**DriverEntry**](http://msdn.microsoft.com/en-us/library/windows/hardware/ff558717) in Device.cpp is the initial point of entry into the driver. This routine passes control to AVStream by calling the [**KsInitializeDriver**](http://msdn.microsoft.com/en-us/library/windows/hardware/ff562683) function. 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, a simulated piece of capture hardware is created (the **CHardwareSimulation** class), and a DMA adapter is acquired from the operating system and is registered with AVStream by calling the [**KsDeviceRegisterAdapterObject**](http://msdn.microsoft.com/en-us/library/windows/hardware/ff561687) function. This call is required for a sample that performs DMA access directly into the capture buffers, instead of using DMA access to write to a common buffer. The driver creates the [KS Filter](http://msdn.microsoft.com/en-us/library/windows/hardware/ff567644) for this device dynamically by calling the [**KsCreateFilterFactory**](http://msdn.microsoft.com/en-us/library/windows/hardware/ff561650) function. + +Filter.cpp is where the sample lays out the [**KSPIN\_DESCRIPTOR\_EX**](http://msdn.microsoft.com/en-us/library/windows/hardware/ff563534) structure for the single video pin. In addition, a [**KSFILTER\_DISPATCH**](http://msdn.microsoft.com/en-us/library/windows/hardware/ff562554) structure and a [**KSFILTER\_DESCRIPTOR**](http://msdn.microsoft.com/en-us/library/windows/hardware/ff562553) 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 because 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**](http://msdn.microsoft.com/en-us/library/windows/hardware/ff563535) 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 perform DMA transfers with AVStream functionality. The other is the **CCapturePin::CompleteMappings** method. These two methods show how to use the queue, obtain clone pointers, use scatter/gather lists, and perform other DMA-related tasks. + +For more information, see the comments in all .cpp files. + +Run the sample +-------------- + +Follow these steps to see how the sample driver functions: + +1. After installation has completed, access the driver through the Graphedt tool. Graphedt.exe is available in the *tools* directory of the WDK. +2. Before running GraphEdit, use the regsvr32 utility to register the proppage.dll DLL and to enable GraphEdit to display property pages for some of the built-in Microsoft DirectShow filters. Open an elevated command window with Administrator privileges, and navigate to the WDK or SDK *tools* directory that contains proppage.dll. +3. On the command line, type regsvr32 proppage.dll. If the registration succeeds, you’ll get a message, “DllRegisterServer in proppage.dll succeeded.†Click OK. +4. In the Graphedt tool, click the **Graph** menu and click **Insert Filters**. The sample appears under "WDM Streaming Capture Devices" as "avshws Source." +5. Click **Insert Filter**. The sample appears in the graph as a single filter labeled, "avshws Source." There is one output pin, which is the video capture pin. This pin emits video in YUY2 format. +6. Attach this filter to either a DirectShow Video Renderer or to the VMR default video renderer. Then click **Play**. + +The output that is produced by the sample is a 320 x 240 pixel 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 display format is MINUTES:SECONDS.HUNDREDTHS. + +In the upper-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. + +Code tour +--------- + +**File manifest** + + ++++ + + + + + + + + + + + +
File +Description
Avshws.h +

Main header file for the sample

Avshws.inf +

Sample installation file

+ + diff --git a/avstream/avshws/avshws.h b/avstream/avshws/avshws.h new file mode 100644 index 000000000..70f220f3e --- /dev/null +++ b/avstream/avshws/avshws.h @@ -0,0 +1,323 @@ +/************************************************************************** + + 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 + +*************************************************/ + +/*++ + +Routine Description: + + Array delete() operator. + +Arguments: + + pVoid - + The memory to free. + +Return Value: + + None + +--*/ +inline +void +__cdecl +operator delete[]( + PVOID pVoid +) +{ + if (pVoid) + { + ExFreePool(pVoid); + } +} + +/*++ + +Routine Description: + + Sized delete() operator. + +Arguments: + + pVoid - + The memory to free. + + size - + The size of the memory to free. + +Return Value: + + None + +--*/ +inline void __cdecl operator delete +( + void *pVoid, + size_t /*size*/ +) +{ + if (pVoid) + { + ExFreePool(pVoid); + } +} + +/*++ + +Routine Description: + + Sized delete[]() operator. + +Arguments: + + pVoid - + The memory to free. + + size - + The size of the memory to free. + +Return Value: + + None + +--*/ +inline void __cdecl operator delete[] +( + void *pVoid, + size_t /*size*/ +) +{ + if (pVoid) + { + ExFreePool(pVoid); + } +} + +/************************************************* + + 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 000000000..bd715e2cb --- /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 000000000..8360d50f0 --- /dev/null +++ b/avstream/avshws/avshws.inf @@ -0,0 +1,173 @@ +;/*++ +; +;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=%ProviderName% +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] +%ManufacturerName%=Standard,NTx86,NTamd64,ntarm,ntarm64 + +[Standard.NTx86] +%avshws.DeviceDesc%=avshws.NTx86,AVSHWS + +[Standard.NTamd64] +%avshws.DeviceDesc%=avshws.NTamd64,AVSHWS + +[Standard.NTarm] +%avshws.DeviceDesc%=avshws.NTarm,AVSHWS + +[Standard.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 +ProviderName="TODO-Set-Provider" +ManufacturerName="TODO-Set-Manufacturer" +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 000000000..3e8a3e6a8 --- /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 000000000..170265922 --- /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", "{40F5B84D-59BB-4A25-A352-166715D6D7C4}" +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 + {40F5B84D-59BB-4A25-A352-166715D6D7C4}.Debug|Win32.ActiveCfg = Debug|Win32 + {40F5B84D-59BB-4A25-A352-166715D6D7C4}.Debug|Win32.Build.0 = Debug|Win32 + {40F5B84D-59BB-4A25-A352-166715D6D7C4}.Release|Win32.ActiveCfg = Release|Win32 + {40F5B84D-59BB-4A25-A352-166715D6D7C4}.Release|Win32.Build.0 = Release|Win32 + {40F5B84D-59BB-4A25-A352-166715D6D7C4}.Debug|x64.ActiveCfg = Debug|x64 + {40F5B84D-59BB-4A25-A352-166715D6D7C4}.Debug|x64.Build.0 = Debug|x64 + {40F5B84D-59BB-4A25-A352-166715D6D7C4}.Release|x64.ActiveCfg = Release|x64 + {40F5B84D-59BB-4A25-A352-166715D6D7C4}.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 000000000..f08dc0758 --- /dev/null +++ b/avstream/avshws/avshws.vcxproj @@ -0,0 +1,200 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {40F5B84D-59BB-4A25-A352-166715D6D7C4} + $(MSBuildProjectName) + Debug + Win32 + {D6F9E8D8-B257-4DE2-B79A-F9B31E238C88} + + + + 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 000000000..64ea1e424 --- /dev/null +++ b/avstream/avshws/avshws.vcxproj.Filters @@ -0,0 +1,46 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {ABE488E5-3DF7-4962-BBAC-F378FDFB32D5} + + + h;hpp;hxx;hm;inl;inc;xsd + {B7A823E9-BED1-4D5B-BE9A-B8E81CA6BC20} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {19E87A7B-173D-4D83-B9E7-D53BA8F93A4D} + + + inf;inv;inx;mof;mc; + {314F8E71-FFC5-460E-B7EB-AA16C51B169F} + + + + + 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 000000000..f23ec0e77 --- /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 (NonPagedPoolNx, 'niPC') 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 ( + NonPagedPoolNx, + 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 000000000..502e01e31 --- /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 000000000..75b06cd5f --- /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 (NonPagedPoolNx, 'veDC') 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 (NonPagedPoolNx, 'miSH') 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 (NonPagedPoolNx, '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(NonPagedPoolNx, '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 000000000..2bd9899d5 --- /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 000000000..aea3acf7c --- /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 (NonPagedPoolNx, 'liFC') 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 000000000..e5cb98954 --- /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 000000000..9a6d5f86d --- /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 (NonPagedPoolNx, 'miSH') 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 ( + NonPagedPoolNx, + 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, + POOL_NX_ALLOCATION, + 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 000000000..21ddcb419 --- /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 000000000..8ab356241 --- /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 000000000..95574479c --- /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 000000000..ea61fbff9 --- /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/avssamp/Filter.cpp b/avstream/avssamp/Filter.cpp new file mode 100644 index 000000000..524d6ad8b --- /dev/null +++ b/avstream/avssamp/Filter.cpp @@ -0,0 +1,836 @@ +/************************************************************************** + + AVStream Filter-Centric Sample + + Copyright (c) 1999 - 2001, Microsoft Corporation + + File: + + filter.cpp + + Abstract: + + This file contails the capture filter implementation (including + frame synthesis) for the fake capture filter. + + History: + + created 5/31/01 + +**************************************************************************/ + +#include "avssamp.h" + +// +// TimerRoutine(): +// +// This is the timer routine called every 1/Nth of a second to trigger +// capture by the filter. +// +KDEFERRED_ROUTINE TimerRoutine; +void +TimerRoutine ( + IN PKDPC Dpc, + IN PVOID This, + IN PVOID SystemArg1, + IN PVOID SystemArg2 + ) +{ + CCaptureFilter *pCCaptureFilter = (CCaptureFilter*)This; + if (pCCaptureFilter) + { + pCCaptureFilter -> TimerDpc (); + } +} + + +/************************************************************************** + + PAGEABLE CODE + +**************************************************************************/ + +#ifdef ALLOC_PRAGMA +#pragma code_seg("PAGE") +#endif // ALLOC_PRAGMA + + +CCaptureFilter:: +CCaptureFilter ( + IN PKSFILTER Filter + ) : + m_Filter (Filter) + +/*++ + +Routine Description: + + This is the constructor for the capture filter. It initializes all the + structures necessary to kick off timer DPC's for capture. + +Arguments: + + Filter - + The AVStream filter being created. + +Return Value: + + None + +--*/ + +{ + PAGED_CODE(); + + // + // Initialize the DPC's, timers, and events necessary to cause a + // capture trigger to happen. + // + KeInitializeDpc ( + &m_TimerDpc, + TimerRoutine, + this + ); + + KeInitializeEvent ( + &m_StopDPCEvent, + SynchronizationEvent, + FALSE + ); + + KeInitializeTimer (&m_Timer); + +} + +/*************************************************/ + + +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); + } + + } + + // + // Create the wave reader. We need it at this point because the data + // ranges exposed on the audio pin need to change dynamically right + // now. + // + if (NT_SUCCESS (Status)) { + + CapFilter -> m_WaveObject = + new (NonPagedPool, 'evaW') CWaveObject ( + L"\\DosDevices\\c:\\avssamp.wav" + ); + + if (!CapFilter -> m_WaveObject) { + Status = STATUS_INSUFFICIENT_RESOURCES; + } else { + Status = CapFilter -> m_WaveObject -> ParseAndRead (); + + // + // If the file cannot be found, don't fail to create the filter. + // This simply means that audio cannot be synthesized. + // + if (Status == STATUS_OBJECT_NAME_NOT_FOUND || + Status == STATUS_ACCESS_DENIED) { + delete CapFilter -> m_WaveObject; + CapFilter -> m_WaveObject = NULL; + Status = STATUS_SUCCESS; + } + + } + + } + + if (NT_SUCCESS (Status) && CapFilter -> m_WaveObject) { + // + // Add the wave object to the filter's bag for auto-cleanup. + // + Status = KsAddItemToObjectBag ( + Filter -> Bag, + reinterpret_cast (CapFilter -> m_WaveObject), + reinterpret_cast (CWaveObject::Cleanup) + ); + + if (!NT_SUCCESS (Status)) { + delete CapFilter -> m_WaveObject; + CapFilter -> m_WaveObject = NULL; + } else { + Status = CapFilter -> BindAudioToWaveObject (); + } + } + + return Status; + +} + +/*************************************************/ + +NTSTATUS +CCaptureFilter:: +BindAudioToWaveObject ( + ) + +/*++ + +Routine Description: + + Create an audio pin directly bound to m_WaveObject (aka: it only exposes + the format (channels, frequency, etc...) that m_WaveObject represents. + This will actually create a pin on the filter dynamically. + +Arguments: + + None + +Return Value: + + Success / Failure + +--*/ + +{ + + PAGED_CODE(); + + NT_ASSERT (m_WaveObject); + + NTSTATUS Status = STATUS_SUCCESS; + + // + // Build a pin descriptor from the template. This descriptor is + // temporary scratch space because the call to AVStream to create the + // pin will actually duplicate the descriptor. + // + KSPIN_DESCRIPTOR_EX PinDescriptor = AudioPinDescriptorTemplate; + + // + // The data range must be dynamically created since we're basing it + // on dynamic reading of a wave file! + // + PKSDATARANGE_AUDIO DataRangeAudio = + reinterpret_cast ( + ExAllocatePoolWithTag (PagedPool, sizeof (KSDATARANGE_AUDIO), AVSSMP_POOLTAG) + ); + + PKSDATARANGE_AUDIO *DataRanges = + reinterpret_cast ( + ExAllocatePoolWithTag (PagedPool, sizeof (PKSDATARANGE_AUDIO), AVSSMP_POOLTAG) + ); + + PKSALLOCATOR_FRAMING_EX Framing = + reinterpret_cast ( + ExAllocatePoolWithTag (PagedPool, sizeof (KSALLOCATOR_FRAMING_EX), AVSSMP_POOLTAG) + ); + + if (DataRangeAudio && DataRanges && Framing) { + DataRangeAudio -> DataRange.FormatSize = sizeof (KSDATARANGE_AUDIO); + DataRangeAudio -> DataRange.Flags = 0; + DataRangeAudio -> DataRange.SampleSize = 0; + DataRangeAudio -> DataRange.Reserved = 0; + DataRangeAudio -> DataRange.MajorFormat = KSDATAFORMAT_TYPE_AUDIO; + DataRangeAudio -> DataRange.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; + DataRangeAudio -> DataRange.Specifier = + KSDATAFORMAT_SPECIFIER_WAVEFORMATEX; + + m_WaveObject -> WriteRange (DataRangeAudio); + + *DataRanges = DataRangeAudio; + + } else { + if (DataRangeAudio) { + ExFreePool (DataRangeAudio); + DataRangeAudio = NULL; + } + if (DataRanges) { + ExFreePool (DataRanges); + DataRanges = NULL; + } + if (Framing) { + ExFreePool (Framing); + Framing = NULL; + } + Status = STATUS_INSUFFICIENT_RESOURCES; + } + + if (NT_SUCCESS (Status)) { + // + // Bag the newly created range information in the filter's bag since + // this will be alive for the lifetime of the filter. + // + Status = KsAddItemToObjectBag ( + m_Filter -> Bag, + DataRangeAudio, + NULL + ); + + if (!NT_SUCCESS (Status)) { + ExFreePool (DataRangeAudio); + ExFreePool (DataRanges); + ExFreePool (Framing); + } + + } + + if (NT_SUCCESS (Status)) { + + Status = KsAddItemToObjectBag ( + m_Filter -> Bag, + DataRanges, + NULL + ); + + if (!NT_SUCCESS (Status)) { + ExFreePool (DataRanges); + ExFreePool (Framing); + } + + } + + if (NT_SUCCESS (Status)) { + + Status = KsAddItemToObjectBag ( + m_Filter -> Bag, + Framing, + NULL + ); + + if (!NT_SUCCESS (Status)) { + ExFreePool (Framing); + } + + } + + if (NT_SUCCESS (Status)) { + // + // The physical and optimal ranges must block aligned and + // the size of 1/(fps) * bytes_per_sec in size. It's true + // that we don't know the frame rate at this point due + // to the fact that the video pin doesn't exist yet; however, that + // would also be true if this were edited at audio pin creation. + // + // Thus, we instead adjust the allocator for the minimum frame rate + // we support (which is 1/30 of a second). + // + *Framing = *PinDescriptor.AllocatorFraming; + + Framing -> FramingItem [0].PhysicalRange.MinFrameSize = + Framing -> FramingItem [0].PhysicalRange.MaxFrameSize = + Framing -> FramingItem [0].FramingRange.Range.MinFrameSize = + Framing -> FramingItem [0].FramingRange.Range.MaxFrameSize = + ((DataRangeAudio -> MaximumSampleFrequency * + DataRangeAudio -> MaximumBitsPerSample * + DataRangeAudio -> MaximumChannels) + 29) / 30; + + Framing -> FramingItem [0].PhysicalRange.Stepping = + Framing -> FramingItem [0].FramingRange.Range.Stepping = + 0; + + PinDescriptor.AllocatorFraming = Framing; + + PinDescriptor.PinDescriptor.DataRangesCount = 1; + PinDescriptor.PinDescriptor.DataRanges = + reinterpret_cast (DataRanges); + + // + // Create the actual pin. We need to save the pin id returned. It + // is how we refer to the audio pin in the future. + // + Status = KsFilterCreatePinFactory ( + m_Filter, + &PinDescriptor, + &m_AudioPinId + ); + + } + + return Status; + +} + + +/*************************************************/ + + +void +CCaptureFilter:: +StartDPC ( + IN LONGLONG TimerInterval + ) + +/*++ + +Routine Description: + + This routine starts the timer DPC running at a specified interval. The + specified interval is the amount of time between triggering frame captures. + Once this routine returns, the timer DPC should be running and attempting + to trigger processing on the capture filter as a whole. + +Arguments: + + TimerInterval - + The amount of time between timer DPC's. This is the amount of delay + between one frame and the next. Since the DPC is driven off the + video capture pin, this should be an amount of time specified by + the video info header. + +Return Value: + + None + +--*/ + +{ + + PAGED_CODE(); + + // + // Initialize any variables used by the timer DPC. + // + m_Tick = 0; + m_TimerInterval = TimerInterval; + KeQuerySystemTime (&m_StartTime); + + // + // Schedule the DPC to happen one frame time from now. + // + LARGE_INTEGER NextTime; + NextTime.QuadPart = m_StartTime.QuadPart + m_TimerInterval; + + KeSetTimer (&m_Timer, NextTime, &m_TimerDpc); + +} + +/*************************************************/ + + +void +CCaptureFilter:: +StopDPC ( + ) + +/*++ + +Routine Description: + + Stop the timer DPC from firing. After this routine returns, there is + a guarantee that no more timer DPC's will fire and no more processing + attempts will occur. Note that this routine does block. + +Arguments: + + None + +Return Value: + + None + +--*/ + +{ + + PAGED_CODE(); + + m_StoppingDPC = TRUE; + + KeWaitForSingleObject ( + &m_StopDPCEvent, + Suspended, + KernelMode, + FALSE, + NULL + ); + + NT_ASSERT (m_StoppingDPC == FALSE); + +} + +/************************************************************************** + + LOCKED CODE + +**************************************************************************/ + +#ifdef ALLOC_PRAGMA +#pragma code_seg() +#endif // ALLOC_PRAGMA + + +LONGLONG +CCaptureFilter:: +GetTimerInterval ( + ) + +/*++ + +Routine Description: + + Return the timer interval being used to fire DPC's. + +Arguments: + + None + +Return Value: + + The timer interval being used to fire DPC's. + +--*/ + +{ + + return m_TimerInterval; + +} + +/*************************************************/ + + +NTSTATUS +CCaptureFilter:: +Process ( + IN PKSPROCESSPIN_INDEXENTRY ProcessPinsIndex + ) + +/*++ + +Routine Description: + + This is the processing function for the capture filter. It is responsible + for copying synthesized image data into the image buffers. The timer DPC + will attempt to trigger processing (and hence indirectly call this routine) + to trigger a capture. + +Arguments: + + ProcessPinsIndex - + Contains a pointer to an array of process pin index entries. This + array is indexed by pin ID. An index entry indicates the number + of pin instances for the corresponding filter type and points to the + first corresponding process pin structure in the ProcessPins array. + This allows the process pin structure to be quickly accessed by pin ID + when the number of instances per type is not known in advance. + +Return Value: + + Indication of whether more processing should be done if frames are + available. A value of STATUS_PENDING indicates that processing should not + continue even if frames are available on all required queues. + STATUS_SUCCESS indicates processing should continue if frames are available + on all required queues. + +--*/ + +{ + + // + // The audio and video pins do not necessarily need to exist (one could + // be capturing video w/o audio or vice-versa). Do not assume the + // existence by checking Index[ID].Pins[0]. Always check the Count + // field first. + // + PKSPROCESSPIN VideoPin = NULL; + CCapturePin *VidCapPin = NULL; + PKSPROCESSPIN AudioPin = NULL; + CCapturePin *AudCapPin = NULL; + ULONG VidCapDrop = 0; + ULONG AudCapDrop = (ULONG)-1; + + if (ProcessPinsIndex [VIDEO_PIN_ID].Count != 0) { + // + // There can be at most one instance via the possible instances field, + // so the below is safe. + // + VideoPin = ProcessPinsIndex [VIDEO_PIN_ID].Pins [0]; + VidCapPin = + reinterpret_cast (VideoPin -> Pin -> Context); + } + + // + // The audio pin only exists on the filter if the wave object does. + // They're tied together at filter create time. + // + if (m_WaveObject && ProcessPinsIndex [m_AudioPinId].Count != 0) { + // + // There can be at most one instance via the possible instances field, + // so the below is safe. + // + AudioPin = ProcessPinsIndex [m_AudioPinId].Pins [0]; + AudCapPin = + reinterpret_cast (AudioPin -> Pin -> Context); + } + + if (VidCapPin) { + VidCapDrop = VidCapPin -> QueryFrameDrop (); + } + + if (AudCapPin) { + AudCapDrop = AudCapPin -> QueryFrameDrop (); + } + + // + // If there's a video pin around, trigger capture on it. We call the + // pin object to actually synthesize the frame; however, we could just + // as easily have done that here. + // + if (VidCapPin) { + // + // This is used to notify the pin how many frames have been dropped + // on each pin to allow that to be rendered. + // + VidCapPin -> NotifyDrops (VidCapDrop, AudCapDrop); + VidCapPin -> CaptureFrame (VideoPin, m_Tick); + } + + // + // If there's an audio pin around, trigger capture on it. Since the + // audio capture pin isn't necessary for capture, there might be an + // instance which is connected and is in the stop state when we get + // called [there will never be one in acquire or pause since we specify + // KSPIN_FLAG_PROCESS_IN_RUN_STATE_ONLY]. Don't bother triggering capture + // on the pin unless it's actually running. + // + // On DX8.x platforms, the Pin -> ClientState field does not exist. + // Hence, we check the state we maintain ourselves. DeviceState is not + // the right thing to check here. + // + if (AudCapPin && AudCapPin -> GetState () == KSSTATE_RUN) { + AudCapPin -> CaptureFrame (AudioPin, m_Tick); + } + + // + // STATUS_PENDING indicates that we do not want to be called back if + // there is more data available. We only want to trigger processing + // (and hence capture) on the timer ticks. + // + return STATUS_PENDING; + +} + +/*************************************************/ + + +void +CCaptureFilter:: +TimerDpc ( + ) + +/*++ + +Routine Description: + + This is the timer function for our timer (bridged to from TimerRoutine + in the context of the appropriate CCaptureFilter). It is called every + 1/Nth of a second as specified in StartDpc() to trigger capture of a video + frame. + +Arguments: + + None + +Return Value: + + None + +--*/ + +{ + + // + // Increment the tick counter. This keeps track of the number of ticks + // that have happened since the timer DPC started running. Note that the + // timer DPC starts running before the pins go into run state and this + // variable gets incremented from the original start point. + // + m_Tick++; + + // + // Trigger processing on the filter. Since the filter is prepared to + // run at DPC, we do not request asynchronous processing. Thus, if + // possible, processing will occur in the context of this DPC. + // + KsFilterAttemptProcessing (m_Filter, FALSE); + + // + // Reschedule the timer if the hardware isn't being stopped. + // + if (!m_StoppingDPC) { + + LARGE_INTEGER NextTime; + + NextTime.QuadPart = m_StartTime.QuadPart + + (m_TimerInterval * (m_Tick + 1)); + + KeSetTimer (&m_Timer, NextTime, &m_TimerDpc); + + } else { + + // + // If another thread is waiting on the DPC to stop running, raise + // the stop event and clear the flag. + // + m_StoppingDPC = FALSE; + KeSetEvent (&m_StopDPCEvent, IO_NO_INCREMENT, FALSE); + + } + +} + +/************************************************************************** + + 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) +}; + +// +// CaptureFilterPinDescriptors: +// +// The list of pin descriptors on the capture filter. +// +const +KSPIN_DESCRIPTOR_EX +CaptureFilterPinDescriptors [CAPTURE_FILTER_PIN_COUNT] = { + // + // Video Capture Pin + // + { + &VideoCapturePinDispatch, + NULL, + { + NULL, // Interfaces (NULL, 0 == default) + 0, + NULL, // Mediums (NULL, 0 == default) + 0, + SIZEOF_ARRAY (VideoCapturePinDataRanges), // Range Count + VideoCapturePinDataRanges, // Ranges + KSPIN_DATAFLOW_OUT, // Dataflow + KSPIN_COMMUNICATION_BOTH, // Communication + &KSCATEGORY_VIDEO, // Category + &g_PINNAME_VIDEO_CAPTURE, // Name + 0 // Reserved + }, + KSPIN_FLAG_FRAMES_NOT_REQUIRED_FOR_PROCESSING | // Flags + KSPIN_FLAG_DO_NOT_INITIATE_PROCESSING | + KSPIN_FLAG_PROCESS_IN_RUN_STATE_ONLY, + 1, // Instances Possible + 1, // Instances Necessary + &VideoCapturePinAllocatorFraming, // Allocator Framing + reinterpret_cast + (CVideoCapturePin::IntersectHandler) + } +}; + +// +// CaptureFilterDispatch: +// +// This is the dispatch table for the capture filter. It provides notification +// of creation, closure, processing, and resets. +// +const +KSFILTER_DISPATCH +CaptureFilterDispatch = { + CCaptureFilter::DispatchCreate, // Filter Create + NULL, // Filter Close + CCaptureFilter::DispatchProcess, // 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 + KSFILTER_FLAG_DISPATCH_LEVEL_PROCESSING,// Flags + &KSNAME_Filter, // Reference GUID + DEFINE_KSFILTER_PIN_DESCRIPTORS (CaptureFilterPinDescriptors), + DEFINE_KSFILTER_CATEGORIES (CaptureFilterCategories), + + DEFINE_KSFILTER_NODE_DESCRIPTORS_NULL, + DEFINE_KSFILTER_DEFAULT_CONNECTIONS, + + NULL // Component ID +}; + + diff --git a/avstream/avssamp/ReadMe.md b/avstream/avssamp/ReadMe.md new file mode 100644 index 000000000..185178803 --- /dev/null +++ b/avstream/avssamp/ReadMe.md @@ -0,0 +1,56 @@ +AVStream filter-centric simulated capture sample driver (Avssamp) +================================================================= + +The AVStream filter-centric simulated capture sample driver (Avssamp) provides a filter-centric [AVStream](http://msdn.microsoft.com/en-us/library/windows/hardware/ff554240) capture driver with functional audio. This streaming media driver performs video captures at 320 x 240 pixel resolution in RGB24 or YUV422 format while playing a user-provided Pulse Code Modulation (PCM) wave audio file in a loop. The sample demonstrates how to write a filter-centric AVStream minidriver. + + +Installation instructions +------------------------- + +1. Copy AVssamp.inf to a directory, for example, C:\\Avstream\\. +2. In this directory, create a new subdirectory named objfre\_x86 if the target operating system is x86-based, or objfre\_amd64 for an x64-based target operating system, for example, C:\\AVstream\\objfre\_x86\\. +3. Copy the processor-appropriate Avssamp.sys file to the objfre\_\* directory. +4. Start a command prompt with administrator privilege and run the processor-specific WDK tool Devcon.exe to launch the installation. For example: + + `C:\WinDDK\7600.16384.0\tools\devcon\i386\devcon.exe install C:\AVstream\avssamp.inf SW\{20698827-7099-4c4e-861A-4879D639A35F}` + +Programming Tour +---------------- + +[**DriverEntry**](http://msdn.microsoft.com/en-us/library/windows/hardware/ff558717) in Avssamp.cpp is the initial point of entry into the driver. This routine passes control to AVStream by calling [**KsInitializeDriver**](http://msdn.microsoft.com/en-us/library/windows/hardware/ff562683). 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. + +Filter.cpp is where the sample lays out the [**KSPIN\_DESCRIPTOR\_EX**](http://msdn.microsoft.com/en-us/library/windows/hardware/ff563534) structure for the single capture pin. Audio.cpp contains the **KSPIN\_DESCRIPTOR\_EX** structure for the audio capture pin. This pin is dynamically created only if C:\\avssamp.wav exists and is a valid and readable PCM format wave file. + +The filter dispatch structure [**KSFILTER\_DISPATCH**](http://msdn.microsoft.com/en-us/library/windows/hardware/ff562554) in Filter.cpp provides dispatches to create and process data. The **DispatchProcess** method is defined inline in Filter.h. It calls the **Process** method in Filter.cpp in the context of the **CCaptureFilter** class. Be aware that the process dispatch is provided in **KSFILTER\_DISPATCH** because this sample is filter-centric. + +Audio.cpp lays out a [**KSPIN\_DISPATCH**](http://msdn.microsoft.com/en-us/library/windows/hardware/ff563535) pin dispatch structure, which contains the dispatch table for the audio pin. Be aware that the **Process** member of this structure is **NULL** because the sample is filter-centric. Similarly, Video.cpp contains the **KSPIN\_DISPATCH** structure for the video capture pin, again with the **Process** member set to **NULL**. + +For more information, see the comments in all .cpp files. + +Code tour +--------- + +**File manifest** + + ++++ + + + + + + + + + + + +
File +Description
Audio.cpp +

Audio capture pin implementation

Audio.h +

Header file for Audio.cpp

+ + diff --git a/avstream/avssamp/audio.cpp b/avstream/avssamp/audio.cpp new file mode 100644 index 000000000..cc35f869d --- /dev/null +++ b/avstream/avssamp/audio.cpp @@ -0,0 +1,655 @@ +/************************************************************************** + + AVStream Filter-Centric Sample + + Copyright (c) 1999 - 2001, Microsoft Corporation + + File: + + audio.cpp + + Abstract: + + This file contains the audio capture pin implementation. + + History: + + created 6/28/01 + +**************************************************************************/ + +#include "avssamp.h" + +/************************************************************************** + + PAGED CODE + +**************************************************************************/ + +#ifdef ALLOC_PRAGMA +#pragma code_seg("PAGE") +#endif // ALLOC_PRAGMA + + +NTSTATUS +CAudioCapturePin:: +DispatchCreate ( + IN PKSPIN Pin, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + Create a new audio capture pin. This is the creation dispatch for + the audio capture pin. + +Arguments: + + Pin - + The pin being created + + Irp - + The creation Irp + +Return Value: + + Success / Failure + +--*/ + +{ + + PAGED_CODE(); + + NTSTATUS Status = STATUS_SUCCESS; + + CAudioCapturePin *CapPin = new (NonPagedPool) CAudioCapturePin (Pin); + CCapturePin *BasePin = static_cast (CapPin); + + 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 (BasePin), + reinterpret_cast (CCapturePin::BagCleanup) + ); + + if (!NT_SUCCESS (Status)) { + delete CapPin; + } else { + Pin -> Context = reinterpret_cast (BasePin); + } + + } + + return Status; + +} + +/*************************************************/ + + +NTSTATUS +CAudioCapturePin:: +Acquire ( + IN KSSTATE FromState + ) + +/*++ + +Routine Description: + + Called when the pin transitions into acquire, this gets and releases + our hold on the wave object we use to synthesize audio streams. + +Arguments: + + FromState - + The state the pin is transitioning away from + +Return Value: + + Success / Failure + +--*/ + +{ + + PAGED_CODE(); + + NTSTATUS Status = STATUS_SUCCESS; + + if (FromState == KSSTATE_STOP) { + // + // On the transition into acquire from stop, get ahold of the + // wave object we're synthesizing from. + // + m_WaveObject = m_ParentFilter -> GetWaveObject (); + NT_ASSERT (m_WaveObject); + + // + // There must be a wave object or something is really wrong. + // + if (!m_WaveObject) { + Status = STATUS_INTERNAL_ERROR; + } else { + m_WaveObject -> Reset (); + } + + } else { + // + // Ensure we hold no reference on the wave object. + // + m_WaveObject = NULL; + + } + + return Status; + +} + +/*************************************************/ + + +NTSTATUS +CAudioCapturePin:: +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: + + The intersect handler for the audio capture pin. This is really quite + simple because the audio pin only exposes the number of channels, + sampling frequency, etc... that the wave file it is synthesizing from + contains. + +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(); + + // + // Verify that the inpassed range is valid size. + // + if (CallerDataRange -> FormatSize < sizeof (KSDATARANGE_AUDIO)) { + return STATUS_NO_MATCH; + } + + // + // Because the only range we expose is such that it will match + // KSDATARANGE_AUDIO, it is safe to interpret the data structures as + // KSDATARANGE_AUDIO. This is due to the fact that AVStream will have + // prematched the GUIDs for us. + // + PKSDATARANGE_AUDIO CallerAudioRange = + reinterpret_cast (CallerDataRange); + + PKSDATARANGE_AUDIO DescriptorAudioRange = + reinterpret_cast (DescriptorDataRange); + + // + // We are returning a KSDATAFORMAT_WAVEFORMATEX. Specify such if a size + // query happens. + // + if (BufferSize == 0) { + *DataSize = sizeof (KSDATAFORMAT_WAVEFORMATEX); + return STATUS_BUFFER_OVERFLOW; + } + + if (BufferSize < sizeof (KSDATAFORMAT_WAVEFORMATEX)) { + return STATUS_BUFFER_TOO_SMALL; + } + + // + // Match the blocks. We only support one format (not really a range), so + // this intersection aught to be really simple. It's more of a check + // if the format we are going to use intersects somewhere in + // CallerAudioRange. + // + if (DescriptorAudioRange -> MaximumChannels > + CallerAudioRange -> MaximumChannels || + DescriptorAudioRange -> MinimumBitsPerSample < + CallerAudioRange -> MinimumBitsPerSample || + DescriptorAudioRange -> MinimumBitsPerSample > + CallerAudioRange -> MaximumBitsPerSample || + DescriptorAudioRange -> MinimumSampleFrequency < + CallerAudioRange -> MinimumSampleFrequency || + DescriptorAudioRange -> MinimumSampleFrequency > + CallerAudioRange -> MaximumSampleFrequency) { + + // + // If the descriptor's "range" (more of a single format specified + // in a range) doesn't intersect the caller's, no match the call. + // + *DataSize = sizeof (KSDATAFORMAT_WAVEFORMATEX); + return STATUS_NO_MATCH; + + } + + // + // Build the format. + // + PKSDATAFORMAT_WAVEFORMATEX WaveFormat = + reinterpret_cast (Data); + + RtlCopyMemory ( + &WaveFormat -> DataFormat, + &DescriptorAudioRange -> DataRange, + sizeof (KSDATAFORMAT) + ); + + WaveFormat -> WaveFormatEx.wFormatTag = WAVE_FORMAT_PCM; + WaveFormat -> WaveFormatEx.nChannels = + (WORD)DescriptorAudioRange -> MaximumChannels; + WaveFormat -> WaveFormatEx.nSamplesPerSec = + DescriptorAudioRange -> MaximumSampleFrequency; + WaveFormat -> WaveFormatEx.wBitsPerSample = + (WORD)DescriptorAudioRange -> MaximumBitsPerSample; + WaveFormat -> WaveFormatEx.nBlockAlign = + (WaveFormat -> WaveFormatEx.wBitsPerSample / 8) * + WaveFormat -> WaveFormatEx.nChannels; + WaveFormat -> WaveFormatEx.nAvgBytesPerSec = + WaveFormat -> WaveFormatEx.nBlockAlign * + WaveFormat -> WaveFormatEx.nSamplesPerSec; + WaveFormat -> WaveFormatEx.cbSize = 0; + WaveFormat -> DataFormat.SampleSize = + WaveFormat -> WaveFormatEx.nBlockAlign; + + WaveFormat -> DataFormat.FormatSize = + *DataSize = sizeof (KSDATAFORMAT_WAVEFORMATEX); + + return STATUS_SUCCESS; + +} + +/*************************************************/ + + +NTSTATUS +CAudioCapturePin:: +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(); + + // + // This pin does not accept any format changes. It is fixed format based + // on what the wave file we're synthesizing from is. Thus, we don't + // need to worry about this being called in any context except pin + // creation (KSPIN_FLAG_FIXED_FORMAT ensures this). Knowing that the + // format already is a GUID match for the range and we only have one + // range, the interpretation without any guid checks is safe. + // + NT_ASSERT (!OldFormat); + + const KSDATARANGE_AUDIO *DataRangeAudio = + reinterpret_cast (DataRange); + + // + // Verify the format is the right size. + // + if (Pin -> ConnectionFormat -> FormatSize < + sizeof (KSDATAFORMAT_WAVEFORMATEX)) { + + return STATUS_NO_MATCH; + } + + PKSDATAFORMAT_WAVEFORMATEX WaveFormat = + reinterpret_cast ( + Pin -> ConnectionFormat + ); + + // + // This is not an intersection, but rather a direct comparison due to + // the fact that we're fixed to a single format and do not really have + // a range. + // + if (WaveFormat -> WaveFormatEx.wFormatTag != WAVE_FORMAT_PCM || + WaveFormat -> WaveFormatEx.nChannels != + DataRangeAudio -> MaximumChannels || + WaveFormat -> WaveFormatEx.nSamplesPerSec != + DataRangeAudio -> MaximumSampleFrequency || + WaveFormat -> WaveFormatEx.wBitsPerSample != + DataRangeAudio -> MaximumBitsPerSample) { + + return STATUS_NO_MATCH; + + } + + // + // The format passes consideration. Allow the pin creation with this + // particular format. + // + return STATUS_SUCCESS; + +} + +/************************************************************************** + + LOCKED CODE + +**************************************************************************/ + +#ifdef ALLOC_PRAGMA +#pragma code_seg() +#endif // ALLOC_PRAGMA + + +NTSTATUS +CAudioCapturePin:: +CaptureFrame ( + IN PKSPROCESSPIN ProcessPin, + IN ULONG Tick + ) + +/*++ + +Routine Description: + + Called to synthesize a frame of audio data from the wave object. + +Arguments: + + ProcessPin - + The process pin from the filter's process pins index + + Tick - + The tick counter from the filter (the number of DPC's that have + happened since the DPC timer started). Note that the DPC timer + starts at pause and capture starts at run. + +Return Value: + + Success / Failure + +--*/ + +{ + + NT_ASSERT (ProcessPin -> Pin == m_Pin); + + // + // Increment the frame number. This is the total count of frames which + // have attempted capture. + // + m_FrameNumber++; + + // + // Find out how much time worth of audio data to synthesize into + // the buffer a buffer (or how much time to skip if there are no available + // capture buffers). + // + LONGLONG TimerInterval = m_ParentFilter -> GetTimerInterval (); + + // + // Since this pin is KSPIN_FLAG_FRAMES_NOT_REQUIRED_FOR_PROCESSING, it + // means that we do not require frames available in order to process. + // This means that this routine can get called from our DPC with no + // buffers available to capture into. In this case, we increment our + // dropped frame counter and skip forward into the audio stream. + // + if (ProcessPin -> BytesAvailable) { + // + // Synthesize a fixed amount of audio data based on the timer interval. + // + ULONG BytesUsed = m_WaveObject -> SynthesizeFixed ( + TimerInterval, + ProcessPin -> Data, + ProcessPin -> BytesAvailable + ); + + ProcessPin -> BytesUsed = BytesUsed; + ProcessPin -> Terminate = TRUE; + + // + // Time stamp the packet if there is a clock assigned. + // + if (m_Clock) { + PKSSTREAM_HEADER StreamHeader = + ProcessPin -> StreamPointer -> StreamHeader; + + StreamHeader -> PresentationTime.Time = m_Clock -> GetTime (); + StreamHeader -> PresentationTime.Numerator = + StreamHeader -> PresentationTime.Denominator = 1; + StreamHeader -> OptionsFlags |= + KSSTREAM_HEADER_OPTIONSF_TIMEVALID; + } + + } else { + m_DroppedFrames++; + + // + // Since we've skipped an audio frame, inform the wave object to + // skip forward this much. + // + m_WaveObject -> SkipFixed (TimerInterval); + } + + return STATUS_SUCCESS; + +} + +/************************************************************************** + + DESCRIPTOR / DISPATCH LAYOUT + +**************************************************************************/ + +// +// AudioCapturePinDispatch: +// +// This is the dispatch table for the capture pin. It provides notifications +// about creation, closure, processing, data formats, etc... +// +const +KSPIN_DISPATCH +AudioCapturePinDispatch = { + CAudioCapturePin::DispatchCreate, // Pin Create + NULL, // Pin Close + NULL, // Pin Process + NULL, // Pin Reset + CAudioCapturePin::DispatchSetFormat, // Pin Set Data Format + CCapturePin::DispatchSetState, // Pin Set Device State + NULL, // Pin Connect + NULL, // Pin Disconnect + NULL, // Clock Dispatch + NULL // Allocator Dispatch +}; + +// +// AudioDefaultAllocatorFraming: +// +// A default framing for the audio pin. In order for this to work properly, +// the frame size must be at least 1/fps * bytes_per_sec large. Otherwise, +// the audio stream will fall behind. This is dynamically adjusted when +// the actual pin is created. +// +DECLARE_SIMPLE_FRAMING_EX ( + AudioDefaultAllocatorFraming, + STATICGUIDOF (KSMEMORY_TYPE_KERNEL_NONPAGED), + KSALLOCATOR_REQUIREMENTF_SYSTEM_MEMORY | + KSALLOCATOR_REQUIREMENTF_PREFERENCES_ONLY, + 25, + 0, + 2 * PAGE_SIZE, + 2 * PAGE_SIZE + ); + +// +// g_PINNAME_AUDIO_CAPTURE: +// +// A GUID identifying the name of the audio capture pin. I use the standard +// STATIC_PINNAME_VIDEO_CAPTURE for the video capture pin, but a custom name +// as defined in avssamp.inf for the audio capture pin. +// +GUID g_PINNAME_AUDIO_CAPTURE = + {0xba1184b9, 0x1fe6, 0x488a, 0xae, 0x78, 0x6e, 0x99, 0x7b, 0x2, 0xca, 0xea}; + +// +// AudioPinDescriptorTemplate: +// +// The template for the audio pin descriptor. The audio pin on this filter +// is created dynamically -- if and only if c:\avssamp.wav exists and is +// a valid and readable wave file. +// +const +KSPIN_DESCRIPTOR_EX +AudioPinDescriptorTemplate = { + // + // Audio Capture Pin + // + &AudioCapturePinDispatch, + NULL, + { + NULL, // Interfaces (NULL, 0 == default) + 0, + NULL, // Mediums (NULL, 0 == default) + 0, + 0, // Range count (filled in later) + NULL, // Ranges (filled in later) + KSPIN_DATAFLOW_OUT, // Dataflow + KSPIN_COMMUNICATION_BOTH, // Communication + &KSCATEGORY_AUDIO, // Category + &g_PINNAME_AUDIO_CAPTURE, // Name + 0 // Reserved + }, + KSPIN_FLAG_FRAMES_NOT_REQUIRED_FOR_PROCESSING | // Flags + KSPIN_FLAG_DO_NOT_INITIATE_PROCESSING | + KSPIN_FLAG_PROCESS_IN_RUN_STATE_ONLY | + KSPIN_FLAG_FIXED_FORMAT, + 1, // Instances Possible + 0, // Instances Necessary + &AudioDefaultAllocatorFraming, // Allocator Framing (filled later) + reinterpret_cast // Intersect Handler + (CAudioCapturePin::IntersectHandler) +}; + diff --git a/avstream/avssamp/audio.h b/avstream/avssamp/audio.h new file mode 100644 index 000000000..0240ddc85 --- /dev/null +++ b/avstream/avssamp/audio.h @@ -0,0 +1,150 @@ +/************************************************************************** + + AVStream Filter-Centric Sample + + Copyright (c) 1999 - 2001, Microsoft Corporation + + File: + + audio.h + + Abstract: + + This file contains the audio capture pin header. + + History: + + created 6/28/01 + +**************************************************************************/ + +class CAudioCapturePin : + public CCapturePin + +{ + +private: + + // + // The wave object used to synthesize audio data. + // + CWaveObject *m_WaveObject; + +public: + + // + // CAudioCapturePin(): + // + // Construct a new audio capture pin. + // + CAudioCapturePin ( + IN PKSPIN Pin + ) : CCapturePin (Pin) + { + } + + // + // ~CAudioCapturePin(): + // + // Destruct an audio capture pin. + // + ~CAudioCapturePin ( + ) + { + } + + // + // Acquire(): + // + // Called when the audio capture pin is transitioning into the acquire + // state (from either stop or pause). This routine will get ahold of + // the wave object from the filter. + // + virtual + NTSTATUS + Acquire ( + IN KSSTATE FromState + ); + + // + // CaptureFrame(): + // + // This is called when the filter processes and wants to trigger processing + // of an audio frame. The routine will compute how far into the stream + // we've progressed and ask the filter's wave object to copy enough + // "synthesized" audio data from the wave object in order to reach + // the position. + // + virtual + NTSTATUS + CaptureFrame ( + IN PKSPROCESSPIN ProcessPin, + IN ULONG Tick + ); + + /************************************************* + + Dispatch Functions + + *************************************************/ + + // + // DispatchCreate(): + // + // This is the creation dispatch for the audio capture pin on the filter. + // It creates the CAudioCapturePin, associates it with the AVStream pin + // object and bags the class object for automatic cleanup when the + // pin is closed. + // + static + NTSTATUS + DispatchCreate ( + IN PKSPIN Pin, + IN PIRP Irp + ); + + // + // 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 + ); + + // + // 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 + ); + + +}; diff --git a/avstream/avssamp/avssamp.cpp b/avstream/avssamp/avssamp.cpp new file mode 100644 index 000000000..2cf0aac54 --- /dev/null +++ b/avstream/avssamp/avssamp.cpp @@ -0,0 +1,116 @@ +/************************************************************************** + + AVStream Filter-Centric Sample + + Copyright (c) 1999 - 2001, Microsoft Corporation + + File: + + avssamp.cpp + + Abstract: + + This is the main file for the filter-centric sample. + + History: + + created 6/18/01 + +**************************************************************************/ + +#include "avssamp.h" + +/************************************************************************** + + 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 + ); + +} + +/************************************************************************** + + DESCRIPTOR AND DISPATCH LAYOUT + +**************************************************************************/ + +// +// FilterDescriptors: +// +// The table of filter descriptors that this device supports. Each one of +// these will be used as a template to create a filter-factory on the device. +// +DEFINE_KSFILTER_DESCRIPTOR_TABLE (FilterDescriptors) { + &CaptureFilterDescriptor +}; + +// +// 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 = { + // + // Since this is a software sample (filter-centric filters usually are + // software kinds of transforms), we really don't care about device level + // notifications and work. The default behavior done on behalf of us + // by AVStream will be quite sufficient. + // + NULL, + SIZEOF_ARRAY (FilterDescriptors), + FilterDescriptors, + KSDEVICE_DESCRIPTOR_VERSION +}; + diff --git a/avstream/avssamp/avssamp.h b/avstream/avssamp/avssamp.h new file mode 100644 index 000000000..78319edfb --- /dev/null +++ b/avstream/avssamp/avssamp.h @@ -0,0 +1,248 @@ +/************************************************************************** + + AVStream Filter-Centric Sample + + Copyright (c) 2001, Microsoft Corporation + + File: + + avssamp.h + + Abstract: + + AVStream Filter-Centric Sample header file. This is the main + header. + + History: + + created 6/18/01 + +**************************************************************************/ + +/************************************************* + + Standard Includes + +*************************************************/ + +extern "C" { +#include +} + +#include +#include +#include +#include +#define NOBITMAP +#include +#undef NOBITMAP +#include +#include +#include +#include +#pragma warning (disable : 4100 4101 4131 4127 4189 4701 4706) +/************************************************* + + Misc Definitions + +*************************************************/ + +#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_YUV422 mmioFOURCC('U', 'Y', 'V', 'Y') + +// +// 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 2 + +#define AVSSMP_POOLTAG 'sSVA' + +/************************************************* + + 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]; + +// +// video.cpp externs: +// +extern +const +KSALLOCATOR_FRAMING_EX +VideoCapturePinAllocatorFraming; + +extern +const +KSPIN_DISPATCH +VideoCapturePinDispatch; + +extern +const +PKSDATARANGE +VideoCapturePinDataRanges [CAPTURE_PIN_DATA_RANGE_COUNT]; + +// +// audio.cpp externs: +// +extern +const +KSPIN_DESCRIPTOR_EX +AudioPinDescriptorTemplate; + +// +// avssamp.cpp externs: +// +extern +const +KSDEVICE_DESCRIPTOR +CaptureDeviceDescriptor; + +/*++ + +Routine Description: + + Array delete() operator. + +Arguments: + + pVoid - + The memory to free. + +Return Value: + + None + +--*/ +inline +void +__cdecl +operator delete[]( + PVOID pVoid +) +{ + if (pVoid) + { + ExFreePool(pVoid); + } +} + +/*++ + +Routine Description: + + Sized delete() operator. + +Arguments: + + pVoid - + The memory to free. + + size - + The size of the memory to free. + +Return Value: + + None + +--*/ +inline void __cdecl operator delete +( + void *pVoid, + size_t /*size*/ + ) +{ + if (pVoid) + { + ExFreePool(pVoid); + } +} + +/*++ + +Routine Description: + + Sized delete[]() operator. + +Arguments: + + pVoid - + The memory to free. + + size - + The size of the memory to free. + +Return Value: + + None + +--*/ +inline void __cdecl operator delete[] +( + void *pVoid, + size_t /*size*/ +) +{ + if (pVoid) + { + ExFreePool(pVoid); + } +} + +/************************************************* + + Internal Includes + +*************************************************/ + +#include "image.h" +#include "wave.h" +#include "filter.h" +#include "capture.h" +#include "video.h" +#include "audio.h" diff --git a/avstream/avssamp/avssamp.htm b/avstream/avssamp/avssamp.htm new file mode 100644 index 000000000..c7390f8ea --- /dev/null +++ b/avstream/avssamp/avssamp.htm @@ -0,0 +1,297 @@ + + + + + + +Avssamp + + + + + + +
+ +

Avssamp: Sample Filter-Centric AVStream +Simulated Capture Driver

+ +

SUMMARY

+ +

The Avssamp +sample provides a filter-centric AVStream capture +driver with functional audio. The driver performs captures at 320x240 in either +an RGB24 or YUV422 format while playing a user-provided PCM wave audio file in +a loop. The purpose of the sample is to demonstrate how to write a +filter-centric AVStream minidriver. +This sample was significantly redesigned for the Microsoft® Windows Server +2003® DDK.

+ +

BUILDING THE SAMPLE

+ +

Build the sample by +typing build -zc in +either the standard checked or free build environment. A successful build +produces avssamp.sys. To install the +driver, right click on avssamp.inf and select +Install. When prompted for avssamp.sys, select +the built binary.

+ +

The sample works on x86 +platforms and builds correctly using Microsoft® Visual C® 6.0. The driver uses +Plug and Play.

+ +

This sample runs on +Microsoft® Windows XP®, and Windows Server 2003® or any platform Windows 98® +gold or beyond, including Windows 2000®, that has +DirectX 8.0 or beyond installed.

+ +

PROGRAMMING TOUR

+ +

DriverEntry in avssamp.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.

+ +

Filter.cpp is where the sample lays out the +KSPIN_DESCRIPTOR_EX structure for the single video pin. Audio.cpp +contains the KSPIN_DESCRIPTOR_EX structure for the audio capture pin. This pin +is dynamically created only if c:\avssamp.wav exists and is a valid and +readable PCM format wave file.

+ +

The filter dispatch +structure in filter.cpp provides dispatches to +create and process. The DispatchProcess method +is defined inline in filter.h. It calls the Process +method in filter.cpp in the context of the CCaptureFilter. Note that the process dispatch is provided +in KSFILTER_DISPATCH since this sample is filter-centric.

+ +

Audio.cpp lays out a KSPIN_DISPATCH +structure which contains the dispatch table for the audio pin. Note that the +Process member of this structure is NULL since the sample is filter-centric. +Similarly, video.cpp contains the pin dispatch +structure for the video capture pin, again with the Process member set to NULL. +

+ +

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

+ +

RUNNING THE SAMPLE

+ +

Once +installation is complete, access the driver using the Graphedt +tool. Graphedt.exe +is available under the Tools\AVStream +directory of the DDK. In the Graphedt application, +click the Graph menu and select Insert Filters. The sample appears under +"WDM Streaming Capture Devices" as "avssamp +Source." Click Insert Filter and the sample appears +in the graph as a single filter labelled as avssamp Source. Attach this filter to either a DirectShow +Video Renderer or the VMR default video renderer and click Play.

+ +

To play audio, before +inserting the avssamp filter, make sure that a valid +PCM wave file exists at c:\avssamp.wav. Then insert the avssamp filter. If a valid file exists at this location, +the avssamp filter will appear with an audio capture +pin. Connect the video pin to the DirectShow Smart Tee filter and connect the +Tee's preview pin as described for video above (note that this is only +necessary for synchronized preview -- not capture). Attach the audio pin to a +Default Waveout Device filter from the Audio Renderer list. Click play.

+ +

The output produced by +the sample is a 320x240 image of standard EIA-189-A color bars.

+ +

COMMENTS

+ +

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

+ +

CODE TOUR

+ +

File Manifest

+ +
File           Description
 
audio.cpp      Audio capture pin implementation.
audio.h        Audio capture pin header.
avssamp.cpp    The main file for the filter-centric sample.
avssamp.h      The main header file for the sample.
avssamp.htm    The Sample Tour documentation for this sample (this file).
avssamp.inf    A sample installation file.
avssamp.rc     Resource file mainly for version.
capture.cpp    The capture pin implementation for all capture pins on the sample filter.
capture.h      The capture pin header for all capture pins on the sample filter.
filter.cpp     The capture filter implementation (including frame synthesis) for the fake capture filter.
filter.h       Header for the capture filter implementation for the fake capture filter.
image.cpp      Image synthesis and overlay code. See comments in file.
image.h        Image synthesis and overlay header.
Sources        The generic file for building the code sample.
video.cpp      Video capture pin implementation.
video.h        Video capture pin header.
wave.cpp       Wave object implementation.
wave.h         Wave object header.
 
 
+ + + + + +
+

 

+
+ +
 
 
+ +

© +2004 Microsoft Corporation

+ +
 
+ + + + diff --git a/avstream/avssamp/avssamp.inf b/avstream/avssamp/avssamp.inf new file mode 100644 index 000000000..62db5009f --- /dev/null +++ b/avstream/avssamp/avssamp.inf @@ -0,0 +1,93 @@ +; Copyright (c) Microsoft Corporation. All rights reserved. +; +; avssamp.INF -- This file contains installation information for the filter-based +; AVStream sample driver avssamp.sys +; +; Note: +; +; This INF expects the following hierarchy in the installation folder: +; +; \ +; avssamp.inf +; avssamp.sys +; + +[Version] +Signature="$Windows NT$" +Class=MEDIA +ClassGUID={4d36e96c-e325-11ce-bfc1-08002be10318} +Provider=%ProviderName% +CatalogFile=avssamp.cat +DriverVer=09/30/2004,1.0.0.0 + +[SourceDisksNames] +1000 = %cdname%,,, + +[SourceDisksFiles] +avssamp.sys = 1000 + +[ControlFlags] +ExcludeFromSelect=* + +[DestinationDirs] +avssamp.CopyFiles=12 + +[Manufacturer] +%ManufacturerName%=Standard,NTamd64,NTx86 + +;--------------------------------------------------------------- +; The preferred method to install as a Root-enumerated device. +; NOTE: DO NOT INCLUDE THIS FOR A HARDWARE DRIVER! +;--------------------------------------------------------------- + +[DeviceInstall32] +AddDevice = ROOT\SW\{20698827-7099-4c4e-861A-4879D639A35F},,avssamp_RootEnumInstall + +[avssamp_RootEnumInstall] +HardwareIds = SW\{20698827-7099-4c4e-861A-4879D639A35F} +;--------------------------------------------------------------- + +[Standard.NTx86] +%avssamp.DeviceDesc%=avssamp,SW\{20698827-7099-4c4e-861A-4879D639A35F} + +[Standard.NTamd64] +%avssamp.DeviceDesc%=avssamp,SW\{20698827-7099-4c4e-861A-4879D639A35F} + +[avssamp.NT] +include=ks.inf,kscaptur.inf +needs=KS.Registration,KSCAPTUR.Registration.NT +CopyFiles=avssamp.CopyFiles + +[avssamp.CopyFiles] +avssamp.sys + +[avssamp.NT.Services] +AddService=avssamp, 0x00000002, avssamp.ServiceInstall + +[avssamp.ServiceInstall] +DisplayName=%avssamp.DeviceDesc% +ServiceType=%SERVICE_KERNEL_DRIVER% +StartType=%SERVICE_DEMAND_START% +ErrorControl=%SERVICE_ERROR_NORMAL% +ServiceBinary=%12%\avssamp.sys + +[Strings] +; non-localizable +Proxy.CLSID="{17CCA71B-ECD7-11D0-B908-00A0C9223196}" +avssamp.DeviceId="{20698827-7099-4c4e-861A-4879D639A35F}" +KSCATEGORY_CAPTURE="{65E8773D-8F56-11D0-A3B9-00A0C9223196}" +KSSTRING_Filter="{9B365890-165F-11D0-A195-0020AFD156E4}" + +SERVICE_KERNEL_DRIVER=1 +SERVICE_DEMAND_START=3 +SERVICE_ERROR_NORMAL=1 +REG_EXPAND_SZ=0x00020000 +REG_DWORD=0x00010001 + +;localizable +ProviderName="TODO-Set-Provider" +ManufacturerName="TODO-Set-Manufacturer" +avssamp.DeviceDesc="AVStream Filter-Centric Sample Driver" +avssamp.Reader.FriendlyName="avssamp Source" + +cdname="Disk 1" diff --git a/avstream/avssamp/avssamp.rc b/avstream/avssamp/avssamp.rc new file mode 100644 index 000000000..b8c7f25b5 --- /dev/null +++ b/avstream/avssamp/avssamp.rc @@ -0,0 +1,22 @@ +//+------------------------------------------------------------------------- +// +// Microsoft Windows +// +// Copyright (C) Microsoft Corporation, 1999 - 1999 +// +// File: avssamp.rc +// +//-------------------------------------------------------------------------- + +#include + +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_UNKNOWN +#define VER_FILEDESCRIPTION_STR "AVStream Filter-Centric Sample" +#define VER_INTERNALNAME_STR "avssamp.sys" +#define VER_ORIGINALFILENAME_STR "avssamp.sys" + +#include "common.ver" + diff --git a/avstream/avssamp/avssamp.sln b/avstream/avssamp/avssamp.sln new file mode 100644 index 000000000..466631807 --- /dev/null +++ b/avstream/avssamp/avssamp.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}") = "avssamp", "avssamp.vcxproj", "{5F47C95F-F225-46CA-9EB6-1B1DF6A2A10E}" +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 + {5F47C95F-F225-46CA-9EB6-1B1DF6A2A10E}.Debug|Win32.ActiveCfg = Debug|Win32 + {5F47C95F-F225-46CA-9EB6-1B1DF6A2A10E}.Debug|Win32.Build.0 = Debug|Win32 + {5F47C95F-F225-46CA-9EB6-1B1DF6A2A10E}.Release|Win32.ActiveCfg = Release|Win32 + {5F47C95F-F225-46CA-9EB6-1B1DF6A2A10E}.Release|Win32.Build.0 = Release|Win32 + {5F47C95F-F225-46CA-9EB6-1B1DF6A2A10E}.Debug|x64.ActiveCfg = Debug|x64 + {5F47C95F-F225-46CA-9EB6-1B1DF6A2A10E}.Debug|x64.Build.0 = Debug|x64 + {5F47C95F-F225-46CA-9EB6-1B1DF6A2A10E}.Release|x64.ActiveCfg = Release|x64 + {5F47C95F-F225-46CA-9EB6-1B1DF6A2A10E}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/avstream/avssamp/avssamp.vcxproj b/avstream/avssamp/avssamp.vcxproj new file mode 100644 index 000000000..ceb26506a --- /dev/null +++ b/avstream/avssamp/avssamp.vcxproj @@ -0,0 +1,202 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {5F47C95F-F225-46CA-9EB6-1B1DF6A2A10E} + $(MSBuildProjectName) + Debug + Win32 + {EAF20BE9-2DCE-40E6-A9BF-4CA1CED83133} + + + + 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) + + + + + + + + + + + + + + + + avssamp + + + avssamp + + + avssamp + + + avssamp + + + + %(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/avssamp/avssamp.vcxproj.Filters b/avstream/avssamp/avssamp.vcxproj.Filters new file mode 100644 index 000000000..74ea459c8 --- /dev/null +++ b/avstream/avssamp/avssamp.vcxproj.Filters @@ -0,0 +1,52 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {7793ECBE-4482-4C88-A024-E8D2F1F80F38} + + + h;hpp;hxx;hm;inl;inc;xsd + {50335876-3174-49EA-AFD7-6F6E510BDB0C} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {344836DC-1F8B-4D73-AC33-5DD9C8E9A465} + + + inf;inv;inx;mof;mc; + {9AA3E219-66BC-4AE9-8DC5-75267CA4DEE7} + + + + + 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/avstream/avssamp/capture.cpp b/avstream/avssamp/capture.cpp new file mode 100644 index 000000000..019b5ad75 --- /dev/null +++ b/avstream/avssamp/capture.cpp @@ -0,0 +1,249 @@ +/************************************************************************** + + AVStream Filter-Centric Sample + + Copyright (c) 1999 - 2001, Microsoft Corporation + + File: + + capture.cpp + + Abstract: + + This file contains the capture pin implementation for all capture + pins on the sample filter. + + History: + + created 5/31/01 + +**************************************************************************/ + +#include "avssamp.h" + +/************************************************************************** + + PAGED CODE + +**************************************************************************/ + +#ifdef ALLOC_PRAGMA +#pragma code_seg("PAGE") +#endif // ALLOC_PRAGMA + + +CCapturePin:: +CCapturePin ( + IN PKSPIN Pin + ) : + m_Pin (Pin), + m_State (KSSTATE_STOP) + +/*++ + +Routine Description: + + Construct a new capture pin. Find out the filter associated with this + pin and stash a pointer to our parent filter. + +Arguments: + + Pin - + The AVStream pin object being created. + +Return Value: + + None + +--*/ + +{ + + PAGED_CODE(); + + PKSFILTER ParentFilter = KsPinGetParentFilter (Pin); + + m_ParentFilter = reinterpret_cast ( + ParentFilter -> Context + ); + +} + +/*************************************************/ + + +NTSTATUS +CCapturePin:: +SetState ( + IN KSSTATE ToState, + IN KSSTATE FromState + ) + +/*++ + +Routine Description: + + Called when the pin is transitioning state. This is a bridge from + DispatchSetState in the context of the capture pin. The function itself + performs basic clock handling (things that all the derived pins would use) + and then calls the appropriate method in the derived class. + +Arguments: + + FromState - + The state the pin is transitioning away from + + ToState - + The state the pin is transitioning towards + +Return Value: + + Success / Failure of state transition. + +--*/ + +{ + + PAGED_CODE(); + + NTSTATUS Status = STATUS_SUCCESS; + + switch (ToState) { + + case KSSTATE_STOP: + + // + // Reset the dropped frame counter. + // + m_DroppedFrames = 0; + m_FrameNumber = 0; + + // + // On a transition to stop, the clock will be released. + // + if (m_Clock) { + m_Clock -> Release (); + m_Clock = NULL; + } + + Status = Stop (FromState); + break; + + case KSSTATE_ACQUIRE: + + // + // On a transition to acqiure (from stop), the pin queries for + // its assigned clock. This can be done either here or at the + // transition to pause. + // + if (FromState == KSSTATE_STOP) { + + Status = KsPinGetReferenceClockInterface ( + m_Pin, + &m_Clock + ); + + if (!NT_SUCCESS (Status)) { + m_Clock = NULL; + } + + } + + Status = Acquire (FromState); + break; + + case KSSTATE_PAUSE: + + Status = Pause (FromState); + break; + + case KSSTATE_RUN: + + Status = Run (FromState); + break; + + } + + if (NT_SUCCESS (Status)) { + m_State = ToState; + } + + return Status; + +} + +/************************************************************************** + + LOCKED CODE + +**************************************************************************/ + +#ifdef ALLOC_PRAGMA +#pragma code_seg() +#endif // ALLOC_PRAGMA + + +ULONG +CCapturePin:: +QueryFrameDrop ( + ) + +/*++ + +Routine Description: + + Return the number of frames which have been dropped on this pin. + +Arguments: + + None + +Return Value: + + The number of frames which have been dropped on this pin. + +--*/ + +{ + + return m_DroppedFrames; + +} + +/*************************************************/ + + +void +CCapturePin:: +NotifyDrops ( + IN ULONG VidDrop, + IN ULONG AudDrop + ) + +/*++ + +Routine Description: + + Stash the number of dropped frames on each pin in this pin to allow + this data to be incorporated into any synthesis. + +Arguments: + + VidDrop - + Number of video frames that have been dropped + + AudDrop - + Number of audio frames that have been dropped + +Return Value: + + None + +--*/ + +{ + + m_NotifyVidDrop = VidDrop; + m_NotifyAudDrop = AudDrop; + +} diff --git a/avstream/avssamp/capture.h b/avstream/avssamp/capture.h new file mode 100644 index 000000000..a761358f3 --- /dev/null +++ b/avstream/avssamp/capture.h @@ -0,0 +1,284 @@ +/************************************************************************** + + AVStream Filter-Centric Sample + + Copyright (c) 1999 - 2001, Microsoft Corporation + + File: + + capture.h + + Abstract: + + This file contains the capture pin level header for all capture pins + on the sample filter. + + History: + + created 5/31/01 + +**************************************************************************/ + +class CCapturePin +{ + +protected: + + // + // The clock object associated with this pin. + // + PIKSREFERENCECLOCK m_Clock; + + // + // The AVStream pin object associated with this pin. + // + PKSPIN m_Pin; + + // + // The CCaptureFilter owning this pin. + // + CCaptureFilter *m_ParentFilter; + + // + // The count of dropped frames. The base class will reset this upon + // stopping the pin. + // + ULONG m_DroppedFrames; + + // + // The frame number. + // + ULONGLONG m_FrameNumber; + + // + // Notifications as to frame drop. This is used to incorporate frame + // drop data into the synthesis. + // + ULONG m_NotifyVidDrop; + ULONG m_NotifyAudDrop; + + // + // Current state. + // + KSSTATE m_State; + +public: + + // + // CCapturePin(): + // + // Construct a new capture pin. + // + CCapturePin ( + IN PKSPIN Pin + ); + + // + // ~CCapturePin(): + // + // Destruct a capture pin. The destructor is virtual because the cleanup + // code will delete the derived class as a CCapturePin. + // + virtual + ~CCapturePin ( + ) + { + } + + // + // ClockAssigned(): + // + // Determine whether or not there is a clock assigned to the pin. + // + BOOLEAN + ClockAssigned ( + ) + { + return (m_Clock != NULL); + } + + // + // GetTime(): + // + // Get the time on the clock. There must be a clock assigned to the pin + // for this call to work. Verification should be made through + // the ClockAssigned() call. + // + LONGLONG + GetTime ( + ) + { + return m_Clock -> GetTime (); + } + + // + // SetState(): + // + // Called to set the state of the pin. The base class performs clock + // handling and calls the appropriate derived method (Run/Pause/Acquire/ + // Stop). + // + NTSTATUS + SetState ( + IN KSSTATE ToState, + IN KSSTATE FromState + ); + + // + // Run(): + // + // Called when a pin transitions to KSSTATE_ACQUIRE by SetState(). + // The derived class can override this to provide any implementation it + // needs. + // + virtual + NTSTATUS + Run ( + IN KSSTATE FromState + ) + { + return STATUS_SUCCESS; + } + + // + // Pause(): + // + // Called when a pin transitions to KSSTATE_PAUSE by SetState(). + // The derived class can override this to provide any implementation it + // needs. + // + virtual + NTSTATUS + Pause ( + IN KSSTATE FromState + ) + { + return STATUS_SUCCESS; + } + + // + // Acquire(): + // + // Called when a pin transitions to KSSTATE_ACQUIRE by SetState(). + // The derived class can override this to provide any implementation it + // needs. + // + virtual + NTSTATUS + Acquire ( + IN KSSTATE FromState + ) + { + return STATUS_SUCCESS; + } + + // + // Stop(): + // + // Called when a pin transitions to KSSTATE_STOP by SetState(). + // The derived class can override this to provide any implementation it + // needs. + // + virtual + NTSTATUS + Stop ( + IN KSSTATE FromState + ) + { + return STATUS_SUCCESS; + } + + // + // GetState(): + // + // Return the current state of the pin. + // + KSSTATE + GetState ( + ) + { + return m_State; + } + + // + // CaptureFrame(): + // + // Called in order to trigger capture of a frame on the given pin. The + // filter's "tick" count is passed as a reference to synthesize an + // appropriate frame. + // + virtual + NTSTATUS + CaptureFrame ( + IN PKSPROCESSPIN ProcessPin, + IN ULONG Tick + ) = 0; + + // + // QueryFrameDrop(): + // + // Query the number of dropped frames. + // + ULONG + QueryFrameDrop ( + ); + + // + // NotifyDrops(): + // + // Notify the pin how many frames have been dropped on all pins. + // + void + NotifyDrops ( + IN ULONG VidDrop, + IN ULONG AudDrop + ); + + /************************************************* + + Dispatch Functions + + *************************************************/ + + // + // DispatchSetState(): + // + // This is the set device state dispatch for the pin. It merely acts + // as a bridge to SetState() in the context of the CCapturePin associated + // with Pin. + // + static + NTSTATUS + DispatchSetState ( + IN PKSPIN Pin, + IN KSSTATE ToState, + IN KSSTATE FromState + ) + { + return + (reinterpret_cast (Pin -> Context)) -> + SetState (ToState, FromState); + } + + // + // BagCleanup(): + // + // This is the free callback for the CCapturePin that we bag. Normally, + // ExFreePool would be used, but we must delete instead. This function + // will just delete the CCapturePin instead of freeing it. Because our + // destructor is virtual, the appropriate derived class destructor will + // get called. + // + static + void + BagCleanup ( + IN CCapturePin *This + ) + + { + + delete This; + + } + +}; diff --git a/avstream/avssamp/filter.h b/avstream/avssamp/filter.h new file mode 100644 index 000000000..b39255ea6 --- /dev/null +++ b/avstream/avssamp/filter.h @@ -0,0 +1,253 @@ +/************************************************************************** + + AVStream Filter-Centric Sample + + Copyright (c) 1999 - 2001, Microsoft Corporation + + File: + + filter.h + + Abstract: + + This file contails the filter level header for the filter-centric + capture filter. + + History: + + created 5/31/01 + +**************************************************************************/ + +/************************************************************************** + + DEFINES + +**************************************************************************/ + +// +// VIDEO_PIN_ID: +// +// The pin factory id of the video pin (the order in the descriptor table). +// +#define VIDEO_PIN_ID 0 + +/************************************************************************** + + CLASSES + +**************************************************************************/ + +class CCaptureFilter { + +private: + + // + // The AVStream filter object associated with this CCaptureFilter. + // + PKSFILTER m_Filter; + + // + // The DPC used for the timer. + // + KDPC m_TimerDpc; + + // + // The timer used for simulation of capture timings. + // + KTIMER m_Timer; + + // + // Boolean used to detect whether the DPC routine is to shutdown or not + // + BOOLEAN m_StoppingDPC; + + // + // The event used to signal successful shutdown of the timer DPC + // + KEVENT m_StopDPCEvent; + + // + // The number of timer ticks that have occurred since the timer DPC + // started firing. + // + volatile ULONG m_Tick; + + // + // The system time at the point that the timer DPC starts. + // + LARGE_INTEGER m_StartTime; + + // + // The amount of time between timer DPC's (and hence frame capture + // triggers). + // + LONGLONG m_TimerInterval; + + // + // The wave object. This is passed to the audio pin later, but it's + // used at filter create time to determine what ranges to expose on + // the audio pin. + // + CWaveObject *m_WaveObject; + + // + // The audio pin factory id. This is dynamic since the pin is created + // dynamically at filter create time. + // + ULONG m_AudioPinId; + + // + // Process(): + // + // The process routine for the capture filter. This is responsible for + // copying synthesized data into image buffers. The DispatchProcess() + // function bridges to this routine in the context of the CCaptureFilter. + // + NTSTATUS + Process ( + IN PKSPROCESSPIN_INDEXENTRY ProcessPinsIndex + ); + + + // + // BindAudioToWaveObject(): + // + // This function call binds the audio stream exposed by the filter to + // the wave object m_WaveObject. + // + NTSTATUS + BindAudioToWaveObject ( + ); + + // + // 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 + ); + + // + // ~CCaptureFilter(): + // + // The capture filter destructor. + // + ~CCaptureFilter ( + ) + { + } + + // + // StartDPC(): + // + // This is called in order to start the timer DPC running. + // + void + StartDPC ( + IN LONGLONG TimerInterval + ); + + // + // StopDPC(): + // + // This is called in order to stop the timer DPC running. The function + // will not return until it guarantees that no more timer DPC's fire. + // + void + StopDPC ( + ); + + // + // GetWaveObject(): + // + // Returns the wave object that has been opened for the filter. + // + CWaveObject * + GetWaveObject ( + ) + { + return m_WaveObject; + } + + // + // GetTimerInterval(): + // + // Returns the timer interval we're using to generate DPC's. + // + LONGLONG + GetTimerInterval ( + ); + + /************************************************* + + Dispatch Routines + + *************************************************/ + + // + // 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 + ); + + // + // DispatchProcess(): + // + // This is the filter process dispatch for the capture filter. It merely + // bridges to Process() in the context of the CCaptureFilter. + // + static + NTSTATUS + DispatchProcess ( + IN PKSFILTER Filter, + IN PKSPROCESSPIN_INDEXENTRY ProcessPinsIndex + ) + { + return + (reinterpret_cast (Filter -> Context)) -> + Process (ProcessPinsIndex); + } + + + // + // TimerDpc(): + // + // The timer dpc routine. This is bridged to from TimerRoutine in the + // context of the appropriate CCaptureFilter. + // + void + TimerDpc ( + ); + +}; + diff --git a/avstream/avssamp/image.cpp b/avstream/avssamp/image.cpp new file mode 100644 index 000000000..f4e70655c --- /dev/null +++ b/avstream/avssamp/image.cpp @@ -0,0 +1,647 @@ +/************************************************************************** + + 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 "avssamp.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 + +--*/ + +{ + 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:: +Fill ( + IN ULONG X_TopLeft, + IN ULONG Y_TopLeft, + IN ULONG X_BottomRight, + IN ULONG Y_BottomRight, + IN COLOR Color + ) + +{ + + // + // Set the default cursor and capture the copy location. Draw a line + // of the specified color at the default location. + // + PUCHAR ImageStart = GetImageLocation (X_TopLeft, Y_TopLeft); + for (ULONG x = X_TopLeft; x <= X_BottomRight; x++) + PutPixel (Color); + + PUCHAR ImageEnd = m_Cursor; + + // + // Copy the fill line from the current location downward to the requested + // end location. + // + for (ULONG y = Y_TopLeft + 1; y <= Y_BottomRight; y++) { + + GetImageLocation (X_TopLeft, y); + + 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/avssamp/image.h b/avstream/avssamp/image.h new file mode 100644 index 000000000..6d9091e05 --- /dev/null +++ b/avstream/avssamp/image.h @@ -0,0 +1,476 @@ +/************************************************************************** + + 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); + } + + // + // Fill(): + // + // Fill an area of the image with a specific color. + // + virtual void + Fill ( + IN ULONG X_TopLeft, + IN ULONG Y_TopLeft, + IN ULONG X_BottomRight, + IN ULONG Y_BottomRight, + IN COLOR Color + ); + + // + // 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 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][1]; + } else { + *(*ImageLocation)++ = Colors [(ULONG)Color][0]; + *(*ImageLocation)++ = Colors [(ULONG)Color][1]; + *(*ImageLocation)++ = Colors [(ULONG)Color][2]; + } + } 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][1]; + } else { + *m_Cursor++ = Colors [(ULONG)Color][0]; + *m_Cursor++ = Colors [(ULONG)Color][1]; + *m_Cursor++ = Colors [(ULONG)Color][2]; + } + } else { + m_Cursor += (m_Parity ? 1 : 3); + } + + m_Parity = !m_Parity; + + } + + 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/avssamp/purecall.c b/avstream/avssamp/purecall.c new file mode 100644 index 000000000..efb3cb493 --- /dev/null +++ b/avstream/avssamp/purecall.c @@ -0,0 +1,50 @@ +/************************************************************************** + + AVStream Filter-Centric Sample + + Copyright (c) 1999 - 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/avssamp/video.cpp b/avstream/avssamp/video.cpp new file mode 100644 index 000000000..d2291a249 --- /dev/null +++ b/avstream/avssamp/video.cpp @@ -0,0 +1,1433 @@ +/************************************************************************** + + AVStream Filter-Centric Sample + + Copyright (c) 1999 - 2001, Microsoft Corporation + + File: + + video.cpp + + Abstract: + + This file contains the video capture pin implementation. + + History: + + created 6/11/01 + +**************************************************************************/ + +#include "avssamp.h" + +/************************************************************************** + + PAGEABLE CODE + +**************************************************************************/ + +#ifdef ALLOC_PRAGMA +#pragma code_seg("PAGE") +#endif // ALLOC_PRAGMA + +NTSTATUS +CVideoCapturePin:: +DispatchCreate ( + IN PKSPIN Pin, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + Create a new video 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; + + CVideoCapturePin *CapPin = new (NonPagedPool) CVideoCapturePin (Pin); + CCapturePin *BasePin = static_cast (CapPin); + + 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 (BasePin), + reinterpret_cast (CCapturePin::BagCleanup) + ); + + if (!NT_SUCCESS (Status)) { + delete CapPin; + } else { + Pin -> Context = reinterpret_cast (BasePin); + } + + } + + // + // 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, 'aChS'); + + if (NT_SUCCESS (Status)) { + Status = KsEdit ( + Pin, + &(Pin -> Descriptor -> AllocatorFraming), + 'aChS' + ); + } + + // + // If the edits proceeded without running out of memory, adjust + // the framing based on the video info header. + // + 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 +CVideoCapturePin:: +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), + AVSSMP_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 +CVideoCapturePin:: +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 +CVideoCapturePin:: +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; + CVideoCapturePin *VidCapPin = 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); + + // + // We know this pin happens to be the video capture pin. Downcast it. + // + VidCapPin = static_cast (CapPin); + } + + 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 (!VidCapPin -> 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; + +} + +/*************************************************/ + + +NTSTATUS +CVideoCapturePin:: +Pause ( + IN KSSTATE FromState + ) + +/*++ + +Routine Description: + + Called when the pin transitions into the pause state. If we're in an + upward transition, start the capture DPC. Note that we do not actually + trigger capture in the pause state, but we start up our DPC. + +Arguments: + + FromState - + The state that the pin is transitioning away from. This is either + KSSTATE_ACQUIRE, indicating an upward transition, or KSSTATE_RUN, + indicating a downward transition. + +Return Value: + + STATUS_SUCCESS + +--*/ + +{ + + PAGED_CODE(); + + // + // On the transition from acquire -> pause, start the timer DPC running. + // + if (FromState == KSSTATE_ACQUIRE) { + m_ParentFilter -> StartDPC (m_VideoInfoHeader -> AvgTimePerFrame); + } + + return STATUS_SUCCESS; + +} + +/*************************************************/ + + +NTSTATUS +CVideoCapturePin:: +Acquire ( + IN KSSTATE FromState + ) + +/*++ + +Routine Description: + + This is called from the base class when the video capture pin transitions + into the acquire state (from either Stop or Pause). The state the pin + transitioned from is passed in. + + During this phase, the video capture pin creates the image synthesizer + and initializes it. + +Arguments: + + FromState - + The state transitioning from (KSSTATE_STOP or KSSTATE_PAUSE) + +Return Value: + + Success / Failure + +--*/ + +{ + + PAGED_CODE(); + + NT_ASSERT (m_VideoInfoHeader); + + NTSTATUS Status = STATUS_SUCCESS; + + if (FromState == KSSTATE_STOP) { + + m_SynthesisBuffer = reinterpret_cast ( + ExAllocatePoolWithTag ( + NonPagedPool, + m_VideoInfoHeader -> bmiHeader.biSizeImage, + AVSSMP_POOLTAG + ) + ); + + if (!m_SynthesisBuffer) { + Status = STATUS_INSUFFICIENT_RESOURCES; + } else { + // + // Determine the necessary type of image synthesizer to create + // based on the format that has been set on this pin. + // + 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, + m_VideoInfoHeader -> bmiHeader.biWidth, + ABS (m_VideoInfoHeader -> bmiHeader.biHeight) + ); + + } else + if (m_VideoInfoHeader -> bmiHeader.biBitCount == 16 && + m_VideoInfoHeader -> bmiHeader.biCompression == FOURCC_YUV422) { + + // + // If we're UYVY, create the YUV synth. + // + m_ImageSynth = new (NonPagedPool, 'YysI') CYUVSynthesizer ( + m_VideoInfoHeader -> bmiHeader.biWidth, + m_VideoInfoHeader -> bmiHeader.biHeight + ); + + } 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; + } + + } + + // + // Bag the image synthesizer. + // + if (NT_SUCCESS (Status)) { + + Status = KsAddItemToObjectBag ( + m_Pin -> Bag, + m_ImageSynth, + reinterpret_cast (CVideoCapturePin::CleanupSynth) + ); + + } + + // + // If everything is okay at this point, inform the synthesizer of + // the scratch buffer. + // + if (NT_SUCCESS (Status)) { + m_ImageSynth -> SetBuffer (m_SynthesisBuffer); + } + + } else { + + // + // The only other state we can come from is pause. If we're in a + // downward state transition below pause, tell the filter to stop the + // capture DPC. + // + m_ParentFilter -> StopDPC (); + + } + + return Status; + +} + +/*************************************************/ + + +NTSTATUS +CVideoCapturePin:: +Stop ( + IN KSSTATE FromState + ) + +/*++ + +Routine Description: + + Called when the video capture pin transitions from acquire to stop. + This function will clean up the image synth and any data structures + that we need to clean up on stop. + +Arguments: + + FromState - + The state the pin is transitioning away from. This should + always be KSSTATE_ACQUIRE for this call. + +Return Value: + + STATUS_SUCCESS + +--*/ + +{ + PAGED_CODE(); + + NT_ASSERT (FromState == KSSTATE_ACQUIRE); + + // + // Remove the image synthesizer from the object bag and free it. + // + KsRemoveItemFromObjectBag ( + m_Pin -> Bag, + m_ImageSynth, + TRUE + ); + + m_ImageSynth = NULL; + + if (m_SynthesisBuffer) { + ExFreePool (m_SynthesisBuffer); + m_SynthesisBuffer = NULL; + } + + return STATUS_SUCCESS; + +} + +/************************************************************************** + + LOCKED CODE + +**************************************************************************/ + +#ifdef ALLOC_PRAGMA +#pragma code_seg() +#endif // ALLOC_PRAGMA + + +NTSTATUS +CVideoCapturePin:: +CaptureFrame ( + IN PKSPROCESSPIN ProcessPin, + IN ULONG Tick + ) + +/*++ + +Routine Description: + + This routine is called from the filter processing function to capture + a frame for the video capture pin. The process pin to capture to is + passed. + +Arguments: + + ProcessPin - + The process pin associated with this pin. + + Tick - + The tick count on the filter. This is the number of timer DPC's that + have fired since the timer DPC started. + +Return Value: + + STATUS_SUCCESS + +--*/ + +{ + + NT_ASSERT (ProcessPin -> Pin == m_Pin); + + // + // Increment the frame number. This is the total count of frames which + // have attempted capture. + // + m_FrameNumber++; + + // + // Since this pin is KSPIN_FLAG_FRAMES_NOT_REQUIRED_FOR_PROCESSING, it + // means that we do not require frames available in order to process. + // This means that this routine can get called from our DPC with no + // buffers available to capture into. In this case, we increment our + // dropped frame counter and do nothing. + // + if (ProcessPin -> BytesAvailable) { + + // + // Because we adjusted the allocator framing, each frame should be + // sufficient to trigger capture of the appropriate buffer size. + // + NT_ASSERT (ProcessPin -> BytesAvailable >= + m_VideoInfoHeader -> bmiHeader.biSizeImage); + + // + // If we get an invalid buffer, kick it out. + // + if (ProcessPin -> BytesAvailable < + m_VideoInfoHeader -> bmiHeader.biSizeImage) { + + ProcessPin -> BytesUsed = 0; + ProcessPin -> Terminate = TRUE; + m_DroppedFrames++; + return STATUS_SUCCESS; + } + + // + // Generate a synthesized image. + // + m_ImageSynth -> SynthesizeBars (); + + // + // Overlay some activity onto the bars. + // + ULONG DropLength = (Tick * 2) % + (ABS (m_VideoInfoHeader -> bmiHeader.biHeight)); + + // + // Create a drop flowing down DropLength lines from the top of the + // image. + // + m_ImageSynth -> Fill ( + 0, 0, + m_VideoInfoHeader -> bmiHeader.biWidth - 1, DropLength, + GREEN + ); + + // + // Overlay the dropped frame count over the image. + // + char Text [256]; + Text[0] = '\0'; + RtlStringCbPrintfA(Text, sizeof(Text), "Video Skipped: %ld", m_DroppedFrames); + + m_ImageSynth -> OverlayText ( + 10, + 10, + 1, + Text, + TRANSPARENT, + BLUE + ); + + // + // This is used to indicate that there is no audio pin. + // + if (m_NotifyAudDrop != (ULONG)-1) { + RtlStringCbPrintfA(Text, sizeof(Text), "Audio Skipped: %ld", m_NotifyAudDrop); + + m_ImageSynth -> OverlayText ( + 10, + 20, + 1, + Text, + TRANSPARENT, + BLUE + ); + } + + // + // Copy the synthesized image into the buffer. + // + RtlCopyMemory ( + ProcessPin -> Data, + m_SynthesisBuffer, + m_VideoInfoHeader -> bmiHeader.biSizeImage + ); + + ProcessPin -> BytesUsed = m_VideoInfoHeader -> bmiHeader.biSizeImage; + ProcessPin -> Terminate = TRUE; + + + PKSSTREAM_HEADER StreamHeader = + ProcessPin -> StreamPointer -> StreamHeader; + + // + // If there is a clock assigned to the pin, time stamp the sample. + // + if (m_Clock) { + + StreamHeader -> PresentationTime.Time = GetTime (); + StreamHeader -> Duration = m_VideoInfoHeader -> AvgTimePerFrame; + + StreamHeader -> OptionsFlags = + KSSTREAM_HEADER_OPTIONSF_TIMEVALID | + KSSTREAM_HEADER_OPTIONSF_DURATIONVALID; + + } + + // + // Update the extended header info. + // + NT_ASSERT (StreamHeader -> Size >= sizeof (KSSTREAM_HEADER) + + sizeof (KS_FRAME_INFO)); + + // + // 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 (StreamHeader -> Size >= sizeof (KSSTREAM_HEADER) + + sizeof (KS_FRAME_INFO)) { + + PKS_FRAME_INFO FrameInfo = reinterpret_cast ( + StreamHeader + 1 + ); + + FrameInfo -> ExtendedHeaderSize = sizeof (KS_FRAME_INFO); + FrameInfo -> PictureNumber = (LONGLONG)m_FrameNumber; + FrameInfo -> DropCount = (LONGLONG)m_DroppedFrames; + + } + + } else { + m_DroppedFrames++; + } + + return STATUS_SUCCESS; + +} + +/************************************************************************** + + DESCRIPTOR AND DISPATCH LAYOUT + +**************************************************************************/ + +#define D_X 320 +#define D_Y 240 + +// +// 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?) + TRUE, // 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_NTSC_M | + KS_AnalogVideo_PAL_B, // AnalogVideoStandard + 720,480, // InputSize, (the inherent size of the incoming signal + // with every digitized pixel unique) + 160,120, // MinCroppingSize, smallest rcSrc cropping rect allowed + 720,480, // MaxCroppingSize, largest rcSrc cropping rect allowed + 8, // CropGranularityX, granularity of cropping size + 1, // CropGranularityY + 8, // CropAlignX, alignment of cropping rect + 1, // CropAlignY; + 160, 120, // MinOutputSize, smallest bitmap stream can produce + 720, 480, // 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 * 160 * 120, // MinBitsPerSecond; + 8 * 3 * 30 * 720 * 480 // MaxBitsPerSecond; + }, + + // + // KS_VIDEOINFOHEADER (default format) + // + { + 0,0,0,0, // RECT rcSource; + 0,0,0,0, // RECT rcTarget; + D_X * D_Y * 3 * 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; + } +}; + +#undef D_X +#undef D_Y + +#define D_X 320 +#define D_Y 240 + +// +// FormatUYU2_Capture: +// +// This is the data range description of the UYVY format we support. +// +const +KS_DATARANGE_VIDEO +FormatUYU2_Capture = { + + // + // KSDATARANGE + // + { + sizeof (KS_DATARANGE_VIDEO), // FormatSize + 0, // Flags + D_X * D_Y * 2, // SampleSize + 0, // Reserved + STATICGUIDOF (KSDATAFORMAT_TYPE_VIDEO), // aka. MEDIATYPE_Video + 0x59565955, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, + 0x00, 0x38, 0x9b, 0x71, // aka. MEDIASUBTYPE_UYVY, + STATICGUIDOF (KSDATAFORMAT_SPECIFIER_VIDEOINFO) // aka. FORMAT_VideoInfo + }, + + TRUE, // BOOL, bFixedSizeSamples (all samples same size?) + TRUE, // 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_NTSC_M | + KS_AnalogVideo_PAL_B, // AnalogVideoStandard + 720,480, // InputSize, (the inherent size of the incoming signal + // with every digitized pixel unique) + 160,120, // MinCroppingSize, smallest rcSrc cropping rect allowed + 720,480, // MaxCroppingSize, largest rcSrc cropping rect allowed + 8, // CropGranularityX, granularity of cropping size + 1, // CropGranularityY + 8, // CropAlignX, alignment of cropping rect + 1, // CropAlignY; + 160, 120, // MinOutputSize, smallest bitmap stream can produce + 720, 480, // 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 * 160 * 120, // MinBitsPerSecond; + 8 * 2 * 30 * 720 * 480 // MaxBitsPerSecond; + }, + + // + // KS_VIDEOINFOHEADER (default format) + // + { + 0,0,0,0, // RECT rcSource; + 0,0,0,0, // RECT rcTarget; + D_X * D_Y * 2 * 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; + 16, // WORD biBitCount; + FOURCC_YUV422, // DWORD biCompression; + D_X * D_Y * 2, // DWORD biSizeImage; + 0, // LONG biXPelsPerMeter; + 0, // LONG biYPelsPerMeter; + 0, // DWORD biClrUsed; + 0 // DWORD biClrImportant; + } +}; + +// +// VideoCapturePinDispatch: +// +// This is the dispatch table for the capture pin. It provides notifications +// about creation, closure, processing, data formats, etc... +// +const +KSPIN_DISPATCH +VideoCapturePinDispatch = { + CVideoCapturePin::DispatchCreate, // Pin Create + NULL, // Pin Close + NULL, // Pin Process + NULL, // Pin Reset + CVideoCapturePin::DispatchSetFormat, // Pin Set Data Format + CCapturePin::DispatchSetState, // Pin Set Device State + NULL, // Pin Connect + NULL, // Pin Disconnect + NULL, // Clock Dispatch + NULL // Allocator Dispatch +}; + +// +// VideoCapturePinAllocatorFraming: +// +// 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 ( + VideoCapturePinAllocatorFraming, + STATICGUIDOF (KSMEMORY_TYPE_KERNEL_NONPAGED), + KSALLOCATOR_REQUIREMENTF_SYSTEM_MEMORY | + KSALLOCATOR_REQUIREMENTF_PREFERENCES_ONLY, + 2, + 0, + 2 * PAGE_SIZE, + 2 * PAGE_SIZE + ); + +// +// VideoCapturePinDataRanges: +// +// This is the list of data ranges supported on the capture pin. We support +// two: one RGB24, and one UYVY. +// +const +PKSDATARANGE +VideoCapturePinDataRanges [CAPTURE_PIN_DATA_RANGE_COUNT] = { + (PKSDATARANGE) &FormatRGB24Bpp_Capture, + (PKSDATARANGE) &FormatUYU2_Capture + }; + diff --git a/avstream/avssamp/video.h b/avstream/avssamp/video.h new file mode 100644 index 000000000..36b9dbe51 --- /dev/null +++ b/avstream/avssamp/video.h @@ -0,0 +1,221 @@ +/************************************************************************** + + AVStream Filter-Centric Sample + + Copyright (c) 1999 - 2001, Microsoft Corporation + + File: + + video.h + + Abstract: + + This file contains the video capture pin header. + + History: + + created 6/11/01 + +**************************************************************************/ + +class CVideoCapturePin : + public CCapturePin { + +private: + + // + // A scratch buffer to write into. Due to the fact that we're likely + // sitting upstream of the VMR and getting video memory, I don't want the + // image synthesizer writing to video memory a single byte at a time. + // This will be the buffer that the image synth uses. After a synthesis, + // the buffer will get copied into the data buffers for capture. + // + PUCHAR m_SynthesisBuffer; + + // + // The captured video info header. The settings for image synthesis will + // be based off this header. + // + PKS_VIDEOINFOHEADER m_VideoInfoHeader; + + // + // The image synthesizer. This object is used to construct synthesized + // image data in the video format specified by the connection format. + // + CImageSynthesizer *m_ImageSynth; + + // + // CaptureVideoInfoHeader(): + // + // This routine stashes the video info header set on the pin connection + // in the CVideoCapturePin object. This is used to determine necessary + // variables for image synthesis, etc... + // + PKS_VIDEOINFOHEADER + CaptureVideoInfoHeader ( + ); + +protected: + +public: + + // + // CVideoCapturePin(): + // + // Construct a new video capture pin. + // + CVideoCapturePin ( + IN PKSPIN Pin + ) : + CCapturePin (Pin) + { + } + + // + // ~CVideoCapturePin(): + // + // Destruct a video capture pin. + // + virtual + ~CVideoCapturePin ( + ) + { + } + + // + // CaptureFrame(): + // + // Called from the filter processing routine to indicate that the pin + // should attempt to trigger capture of a video frame. This routine + // will copy synthesized image data into the frame buffer and complete + // the frame buffer. + // + virtual + NTSTATUS + CaptureFrame ( + IN PKSPROCESSPIN ProcessPin, + IN ULONG Tick + ); + + // + // Pause(): + // + // Called when the video capture pin is transitioning into the pause + // state. This will instruct the capture filter to start the timer DPC's + // at the interval demanded by the video info header in the connection + // format. + // + virtual + NTSTATUS + Pause ( + IN KSSTATE FromState + ); + + // + // Acquire(): + // + // Called when the video capture pin is transitioning into the acquire + // state. This will create the necessary image synthesizer to begin + // synthesizing frame capture data when the pin transitions to the + // appropriate state. + // + virtual + NTSTATUS + Acquire ( + IN KSSTATE FromState + ); + + // + // Stop(): + // + // Called when the video capture pin is transitioning into a stop state. + // This simply destroys the image synthesizer in preparation for creating + // a new one next acquire. + // + virtual + NTSTATUS + Stop ( + IN KSSTATE FromState + ); + + /************************************************* + + Dispatch Functions + + *************************************************/ + + // + // DispatchCreate(): + // + // This is the creation dispatch for the video capture pin on the filter. + // It creates the CVideoCapturePin, associates it with the AVStream pin + // object and bags the class object for automatic cleanup when the + // pin is closed. + // + static + NTSTATUS + DispatchCreate ( + IN PKSPIN Pin, + IN PIRP Irp + ); + + // + // 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 + ); + + // + // 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 + ); + + // + // CleanupSynth(): + // + // Called when the Image Synthesizer is removed from the object bag + // to be cleaned up. We simply delete the image synth. + // + static + void + CleanupSynth ( + IN CImageSynthesizer *ImageSynth + ) + { + delete ImageSynth; + } + +}; + diff --git a/avstream/avssamp/wave.cpp b/avstream/avssamp/wave.cpp new file mode 100644 index 000000000..7af2f60ad --- /dev/null +++ b/avstream/avssamp/wave.cpp @@ -0,0 +1,598 @@ +/************************************************************************** + + AVStream Filter-Centric Sample + + Copyright (c) 1999 - 2001, Microsoft Corporation + + File: + + wave.cpp + + Abstract: + + Wave object implementation. + + History: + + Created 6/28/01 + +**************************************************************************/ + +#include "avssamp.h" + +/************************************************************************** + + PAGED CODE + +**************************************************************************/ + +#ifdef ALLOC_PRAGMA +#pragma code_seg("PAGE") +#endif // ALLOC_PRAGMA + + +CWaveObject:: +~CWaveObject ( + ) + +/*++ + +Routine Description: + + Destroy a wave object. + +Arguments: + + None + +Return Value: + + None + +--*/ + +{ + PAGED_CODE(); + + if (m_WaveData) { + ExFreePool (m_WaveData); + } + +} + +/*************************************************/ + + +NTSTATUS +CWaveObject:: +ParseForBlock ( + IN HANDLE FileHandle, + IN ULONG BlockHeader, + IN OUT PLARGE_INTEGER BlockPosition, + OUT PULONG BlockSize + ) + +/*++ + +Routine Description: + + Given that BlockPosition points to the offset of the start of a RIFF block, + continue parsing the specified file until a block with the header of + BlockHeader is found. Return the position of the block data and the size + of the block. + +Arguments: + + FileHandle - + Handle to the file to parse + + BlockHeader - + The block header to scan for + + BlockPosition - + INPUT : Points to the block header to start at + OUTPUT: If successful, points to the block data for the sought block + If unsuccessful, unchanged + + BlockSize - + On output, if successful -- the size of the sought block will be + placed here + +Return Value: + + Success / Failure of the search + +--*/ + +{ + + PAGED_CODE(); + + NTSTATUS Status; + ULONG FmtBlockSize = 0; + LARGE_INTEGER ReadPos = *BlockPosition; + IO_STATUS_BLOCK iosb; + + while (1) { + ULONG BlockHeaderData [2]; + + Status = ZwReadFile ( + FileHandle, + NULL, + NULL, + NULL, + &iosb, + BlockHeaderData, + sizeof (BlockHeaderData), + &ReadPos, + NULL + ); + + if (NT_SUCCESS (Status)) { + if (BlockHeaderData [0] == BlockHeader) { + FmtBlockSize = BlockHeaderData [1]; + ReadPos.QuadPart += 0x8; + break; + } else { + // + // This isn't a format block. Just ignore it. All we + // care about is the format block and the PCM data. + // + ReadPos.QuadPart += BlockHeaderData [1] + 0x8; + } + } else { + break; + } + + } + + if (FmtBlockSize == 0) { + Status = STATUS_NOT_FOUND; + } else { + *BlockPosition = ReadPos; + *BlockSize = FmtBlockSize; + } + + return Status; + +} + +/*************************************************/ + + +NTSTATUS +CWaveObject:: +ParseAndRead ( + ) + +/*++ + +Routine Description: + + Parse the wave file and read the data into an internally allocated + buffer. This prepares to synthesize audio data from the wave + object. + +Arguments: + + None + +Return Value: + + Success / Failure + + If the wave is unrecognized, unparsable, or insufficient memory + exists to allocate the internal buffer, an error code will + be returned and the object will be incapable of synthesizing + audio data based on the wave. + +--*/ + +{ + + PAGED_CODE(); + + IO_STATUS_BLOCK iosb; + UNICODE_STRING FileName; + OBJECT_ATTRIBUTES ObjectAttributes; + NTSTATUS Status; + HANDLE FileHandle = NULL; + FILE_OBJECT *FileObj; + + RtlInitUnicodeString (&FileName, m_FileName); + + InitializeObjectAttributes ( + &ObjectAttributes, + &FileName, + (OBJ_CASE_INSENSITIVE | + OBJ_KERNEL_HANDLE), + NULL, + NULL + ); + + Status = ZwCreateFile ( + &FileHandle, + GENERIC_READ | SYNCHRONIZE, + &ObjectAttributes, + &iosb, + 0, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ, + FILE_OPEN, + FILE_SYNCHRONOUS_IO_NONALERT, + NULL, + 0 + ); + + if (NT_SUCCESS (Status)) { + ULONG RiffWaveHeader [3]; + + // + // Read the header: RIFF size WAVE + // + Status = ZwReadFile ( + FileHandle, + NULL, + NULL, + NULL, + &iosb, + RiffWaveHeader, + sizeof (RiffWaveHeader), + NULL, + NULL + ); + + // + // Ensure that this is a RIFF file and it's a WAVE. + // + if (NT_SUCCESS (Status)) { + + if (RiffWaveHeader [0] != 'FFIR' || + RiffWaveHeader [2] != 'EVAW') { + Status = STATUS_INVALID_PARAMETER; + } + } + } + + // + // Find the wave format block and ensure it's WAVEFORMATEX and PCM + // data. Otherwise, this can't parse the wave. + // + LARGE_INTEGER ReadPos; + ReadPos.QuadPart = 0xc; + ULONG FmtBlockSize = 0; + + if (NT_SUCCESS (Status)) { + Status = ParseForBlock (FileHandle, ' tmf', &ReadPos, &FmtBlockSize); + } + + // + // If the format block was not found, the file cannot be parsed. If the + // format block is unrecognized, the file cannot be parsed. + // + if (FmtBlockSize >= sizeof (m_WaveFormat)) { + Status = STATUS_INVALID_PARAMETER; + } + + if (NT_SUCCESS (Status)) { + Status = ZwReadFile ( + FileHandle, + NULL, + NULL, + NULL, + &iosb, + &m_WaveFormat, + FmtBlockSize, + &ReadPos, + NULL + ); + } + + if (NT_SUCCESS (Status)) { + if (m_WaveFormat.wFormatTag != WAVE_FORMAT_PCM) { + Status = STATUS_INVALID_PARAMETER; + } + } + + ReadPos.QuadPart += FmtBlockSize; + + // + // Find the data block and read it in. + // + ULONG DataBlockSize; + if (NT_SUCCESS (Status)) { + Status = ParseForBlock (FileHandle, 'atad', &ReadPos, &DataBlockSize); + } + + // + // Perform a slight validation. + // + if (NT_SUCCESS (Status) && + (DataBlockSize == 0 || + (DataBlockSize & (m_WaveFormat.nBlockAlign - 1)))) { + + Status = STATUS_INVALID_PARAMETER; + } + + // + // If we're okay so far, allocate memory for the wave data. + // + if (NT_SUCCESS (Status)) { + m_WaveData = reinterpret_cast ( + ExAllocatePoolWithTag (NonPagedPool, DataBlockSize, AVSSMP_POOLTAG) + ); + + if (!m_WaveData) { + Status = STATUS_INSUFFICIENT_RESOURCES; + } + } + + // + // Read the wave data in. + // + if (NT_SUCCESS (Status)) { + Status = ZwReadFile ( + FileHandle, + NULL, + NULL, + NULL, + &iosb, + m_WaveData, + DataBlockSize, + &ReadPos, + NULL + ); + + m_WaveSize = DataBlockSize; + } + + // + // If we failed, clean up. + // + if (!NT_SUCCESS (Status)) { + if (m_WaveData) { + ExFreePool (m_WaveData); + m_WaveData = NULL; + } + } + + if (FileHandle) { + ZwClose (FileHandle); + } + + return Status; +} + +/*************************************************/ + + +void +CWaveObject:: +WriteRange ( + OUT PKSDATARANGE_AUDIO DataRange + ) + +/*++ + +Routine Description: + + Fill out the extended portion of the audio data range at DataRange. This + includes the channel, bps, and frequency fields. + +Arguments: + + DataRange - + The data range to fill out + +Return Value: + + None + +--*/ + +{ + + PAGED_CODE(); + + DataRange -> MaximumChannels = m_WaveFormat.nChannels; + DataRange -> MinimumBitsPerSample = + DataRange -> MaximumBitsPerSample = + m_WaveFormat.wBitsPerSample; + DataRange -> MinimumSampleFrequency = + DataRange -> MaximumSampleFrequency = + m_WaveFormat.nSamplesPerSec; + + +} + +/************************************************************************** + + LOCKED CODE + +**************************************************************************/ + +#ifdef ALLOC_PRAGMA +#pragma code_seg() +#endif // ALLOC_PRAGMA + + +void +CWaveObject:: +SkipFixed ( + IN LONGLONG TimeDelta + ) + +/*++ + +Routine Description: + + Skip ahead a specific time delta within the wave. + +Arguments: + + TimeDelta - + The amount of time to skip ahead. + +--*/ + +{ + if (TimeDelta > 0) { + + // + // Compute the number of bytes of audio data necessary to move the + // stream forward TimeDelta time. Remember that TimeDelta is in + // units of 100nS. + // + ULONG Samples = (ULONG)( + (m_WaveFormat.nSamplesPerSec * TimeDelta) / 10000000 + ); + + ULONG Bytes = Samples * (m_WaveFormat.wBitsPerSample / 8) * + m_WaveFormat.nChannels; + + m_WavePointer = (m_WavePointer + Bytes) % m_WaveSize; + + m_SynthesisTime += TimeDelta; + + } + +} + + +ULONG +CWaveObject:: +SynthesizeFixed ( + IN LONGLONG TimeDelta, + IN PVOID Buffer, + IN ULONG BufferSize + ) + +/*++ + +Routine Description: + + Copy wave data from our wave block in order to synthesize forward in time + TimeDelta (in 100nS units). + +Arguments: + + TimeDelta - + The amount of time to move the stream (in 100nS increments) + + Buffer - + The buffer to synthesize into + + BufferSize - + The size of the buffer + +Return Value: + + Number of bytes synthesized. + +--*/ + +{ + + // + // If there is no time delta, return 0. + // + if (TimeDelta < 0) + return 0; + + // + // Compute the number of bytes of audio data necessary to move the stream + // forward TimeDelta time. Remember that TimeDelta is in units of 100nS. + // + ULONG Samples = (ULONG)( + (m_WaveFormat.nSamplesPerSec * TimeDelta) / 10000000 + ); + + ULONG Bytes = Samples * (m_WaveFormat.wBitsPerSample / 8) * + m_WaveFormat.nChannels; + + // + // Now that we have a specified number of bytes, we determine how many + // to really copy based on the Size of the buffer. + // + if (Bytes > BufferSize) Bytes = BufferSize; + + // + // Because the buffer is looping, this may multiple distinct copies. For + // large wave files, this may be two chunks. For small wave files, this + // may be MANY distinct chunks. + // + ULONG BytesRemaining = Bytes; + PUCHAR DataCopy = reinterpret_cast (Buffer); + + while (BytesRemaining) { + ULONG ChunkCount = m_WaveSize - m_WavePointer; + if (ChunkCount > BytesRemaining) ChunkCount = BytesRemaining; + + RtlCopyMemory ( + DataCopy, + m_WaveData + m_WavePointer, + ChunkCount + ); + + m_WavePointer += ChunkCount; + if (m_WavePointer >= m_WaveSize) m_WavePointer -= m_WaveSize; + + BytesRemaining -= ChunkCount; + DataCopy += ChunkCount; + + } + + // + // Consider that we have synthesized up to the specified time. If the + // buffer was not large enough to do this, we'll end up falling behind + // the synthesis time. This does not skip samples. + // + m_SynthesisTime += TimeDelta; + + return Bytes; + +} + + +ULONG +CWaveObject:: +SynthesizeTo ( + IN LONGLONG StreamTime, + IN PVOID Buffer, + IN ULONG BufferSize + ) + +/*++ + +Routine Description: + + Copy wave data from our wave block in order to synthesize the stream + up to the specified stream time. If the buffers are not large enough, + this will fall behind on synthesis. + +Arguments: + + StreamTime - + The time to synthesize up to + + Buffer - + The buffer to copy synthesized wave data into + + BufferSize - + The size of the buffer + +Return Value: + + The number of bytes used. + +--*/ + +{ + + LONGLONG TimeDelta = StreamTime - m_SynthesisTime; + + return SynthesizeFixed (TimeDelta, Buffer, BufferSize); + + +} + diff --git a/avstream/avssamp/wave.h b/avstream/avssamp/wave.h new file mode 100644 index 000000000..d8546e3ea --- /dev/null +++ b/avstream/avssamp/wave.h @@ -0,0 +1,192 @@ +/************************************************************************** + + AVStream Filter-Centric Sample + + Copyright (c) 1999 - 2001, Microsoft Corporation + + File: + + wave.h + + Abstract: + + Wave object header. + + History: + + Created 6/28/01 + +**************************************************************************/ + +// +// The CWaveObject is a class which will parse PCM wave files, read the +// data, and expose the data in a loop. This allows the sample to "synthesize" +// audio data by using any PCM wave file the user wishes. +// +class CWaveObject { + +private: + + // + // The wave format. + // + WAVEFORMATEX m_WaveFormat; + + // + // The wave data. + // + PUCHAR m_WaveData; + + // + // The size of the wave data. + // + ULONG m_WaveSize; + + // + // The filename for the wave file. This string must be constant and + // static over the lifetime of the wave object. + // + PWCHAR m_FileName; + + // + // The time we have synthesized to. + // + LONGLONG m_SynthesisTime; + + // + // The pointer into the wave data that we have synthesized to. + // + ULONG m_WavePointer; + + // + // ParseBlock(): + // + // Parse the wave file, starting at the specified location, until the + // specified block has been found. The pointer will be updated to + // point to the block data and the amount of data in the block will + // be returned in a variable. + // + NTSTATUS + ParseForBlock ( + IN HANDLE FileHandle, + IN ULONG BlockHeader, + IN OUT PLARGE_INTEGER BlockPointer, + OUT PULONG BlockSize + ); + +public: + + // + // CWaveObject(): + // + // Construct a new wave object using the specified file name. + // + CWaveObject ( + _In_ LPWSTR FileName + ) : + m_FileName (FileName) + { + m_WaveData = NULL; + } + + // + // ~CWaveObject(): + // + // Destroy a wave object. + // + ~CWaveObject ( + ); + + // + // ParseAndRead(): + // + // Parse the wave file and read it into an internally allocated buffer + // inside the wave object. This is preparation to synthesize looped + // audio based on the wave. + // + NTSTATUS + ParseAndRead ( + ); + + // + // WriteRange(): + // + // Given the address of a KSDATARANGE_AUDIO, write out a range which + // matches exactly the specifications of the wave we're using to + // synthesize audio data. + // + // The GUIDs must be filled out already. This only fills out the + // channel, bps, and freq fields. + // + void + WriteRange ( + PKSDATARANGE_AUDIO AudioRange + ); + + // + // SynthesizeTo(): + // + // Given a specific stream time, synthesize from the current stream time + // (assume 0) to the supplied stream time. + // + ULONG + SynthesizeTo ( + IN LONGLONG StreamTime, + IN PVOID Data, + IN ULONG BufferSize + ); + + // + // SynthesizeFixed(): + // + // Given a specific amount of time, synthesize forward in time that + // particular amount. Units expressed in 100nS increments. + // + ULONG + SynthesizeFixed ( + IN LONGLONG TimeDelta, + IN PVOID Data, + IN ULONG BufferSize + ); + + // + // SkipFixed(): + // + // Given a specific amount of time, skip forward in time that + // particular amount. Units expressed in 100nS increments. + // + void + SkipFixed ( + IN LONGLONG TimeDelta + ); + + // + // Reset(): + // + // Reset the synthesis time and block pointers. This will cause the + // clock with respect to this wave object to go to zero. + // + void + Reset ( + ) + { + m_WavePointer = 0; + m_SynthesisTime = 0; + } + + // + // Cleanup(): + // + // This is a bag cleanup callback. It merely deletes the wave object + // instead of letting the default of ExFreePool free it. + // + static + void + Cleanup ( + IN CWaveObject *This + ) + { + delete This; + } + +}; diff --git a/avstream/sampledevicemft/README.md b/avstream/sampledevicemft/README.md new file mode 100644 index 000000000..3d5e3914a --- /dev/null +++ b/avstream/sampledevicemft/README.md @@ -0,0 +1,31 @@ +Driver Device Transform Sample +============================== +Illustrative example for a *Driver Device Transform* which loads in a process streaming an Avstream based camera device using Media Foundation. + +A *Driver Device Transform* is a new kind of a transform that's used with a specific camera when capturing video. The *Driver Device Transform* is also known as DeviceMFT because it is the first Device Transform applied to the video source. This *Driver Device Transform* is an alternative to the Driver MFT i.e. MFT0 in that, it caters to the source rather than the streams . An N stream source supporting DeviceMFT will have a single instance of the *Device Driver Transform* loaded, while MFT0 will have *N* instances for each pipeline process. The DeviceMFT can advertise multiple streams at the output, which can differ from the number of streams advertised by the source. This is analogous to having a user mode driver in the MF pipeline which intercepts and processes commands before they enter/ leave the Kernel mode driver loaded for the streaming source. + +In this sample, the Device Transform , when enabled, will replicate a photo sequence in the user mode from a one pin device . It acts as a passthrough for sources exposing more than one pins. + +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) + +[Universal camera driver design guide for Windows 10](https://msdn.microsoft.com/en-us/Library/Windows/Hardware/dn937080) + +**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) \ No newline at end of file diff --git a/avstream/sampledevicemft/SampleDeviceMft.sln b/avstream/sampledevicemft/SampleDeviceMft.sln new file mode 100644 index 000000000..874e9b3ce --- /dev/null +++ b/avstream/sampledevicemft/SampleDeviceMft.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}") = "multipinmft", "multipinmft.vcxproj", "{FBAACBDA-D207-4158-8497-37F54DD12701}" +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 + {FBAACBDA-D207-4158-8497-37F54DD12701}.Debug|Win32.ActiveCfg = Debug|Win32 + {FBAACBDA-D207-4158-8497-37F54DD12701}.Debug|Win32.Build.0 = Debug|Win32 + {FBAACBDA-D207-4158-8497-37F54DD12701}.Release|Win32.ActiveCfg = Release|Win32 + {FBAACBDA-D207-4158-8497-37F54DD12701}.Release|Win32.Build.0 = Release|Win32 + {FBAACBDA-D207-4158-8497-37F54DD12701}.Debug|x64.ActiveCfg = Debug|x64 + {FBAACBDA-D207-4158-8497-37F54DD12701}.Debug|x64.Build.0 = Debug|x64 + {FBAACBDA-D207-4158-8497-37F54DD12701}.Release|x64.ActiveCfg = Release|x64 + {FBAACBDA-D207-4158-8497-37F54DD12701}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/avstream/sampledevicemft/Source.def b/avstream/sampledevicemft/Source.def new file mode 100644 index 000000000..752ef2ea8 --- /dev/null +++ b/avstream/sampledevicemft/Source.def @@ -0,0 +1,5 @@ +EXPORTS + DllCanUnloadNow PRIVATE + DllRegisterServer PRIVATE + DllUnregisterServer PRIVATE + DllGetClassObject PRIVATE diff --git a/avstream/sampledevicemft/basepin.cpp b/avstream/sampledevicemft/basepin.cpp new file mode 100644 index 000000000..30bfcc20e --- /dev/null +++ b/avstream/sampledevicemft/basepin.cpp @@ -0,0 +1,861 @@ +#include "stdafx.h" +#include "common.h" +#include "multipinmft.h" +#include "multipinmfthelpers.h" +#include "basepin.h" + +#ifdef MF_WPP +#include "basepin.tmh" +#endif +/* -------> New STATE + | + |old State +DeviceStreamState_Stop DeviceStreamState_Pause DeviceStreamState_Run DeviceStreamState_Disabled +DeviceStreamState_Pause +DeviceStreamState_Run +DeviceStreamState_Disabled +*/ + +DeviceStreamState pinStateTransition[4][4] = { + { DeviceStreamState_Stop, DeviceStreamState_Stop, DeviceStreamState_Run, DeviceStreamState_Disabled }, + { DeviceStreamState_Stop, DeviceStreamState_Pause, DeviceStreamState_Run, DeviceStreamState_Disabled }, + { DeviceStreamState_Stop, DeviceStreamState_Pause, DeviceStreamState_Run, DeviceStreamState_Disabled }, + { DeviceStreamState_Disabled, DeviceStreamState_Disabled, DeviceStreamState_Disabled, DeviceStreamState_Disabled } +}; + +CBasePin::CBasePin( _In_ ULONG id, _In_ CMultipinMft *parent) : + m_StreamId(id) + , m_Parent(parent) + , m_setMediaType(nullptr) + , m_Attributes(nullptr) + , m_Ikscontrol(nullptr) +{ + +} + +CBasePin::~CBasePin() +{ + IMFMediaType *pMediaType = nullptr; + for ( ULONG ulIndex = 0, ulSize = (ULONG)m_listOfMediaTypes.size(); ulIndex < ulSize; ulIndex++ ) + { + pMediaType = m_listOfMediaTypes[ulIndex]; + SAFE_RELEASE(pMediaType); + } + m_listOfMediaTypes.clear(); + m_Attributes = nullptr; +} + +HRESULT CBasePin::AddMediaType( _Inout_ DWORD *pos, _In_ IMFMediaType *pMediaType) +{ + HRESULT hr = S_OK; + + DMFTCHECKNULL_GOTO(pMediaType, done, E_INVALIDARG); + + m_listOfMediaTypes.push_back( pMediaType ); + pMediaType->AddRef(); + + if (pos) + { + *pos = (DWORD)(m_listOfMediaTypes.size() - 1); + } + +done: + return hr; +} + +HRESULT CBasePin::GetMediaTypeAt( _In_ DWORD pos, _Outptr_result_maybenull_ IMFMediaType **ppMediaType ) +{ + HRESULT hr = S_OK; + + DMFTCHECKNULL_GOTO(ppMediaType,done,E_INVALIDARG); + + if (pos >= m_listOfMediaTypes.size()) + { + DMFTCHECKHR_GOTO(MF_E_NO_MORE_TYPES,done); + } + *ppMediaType = m_listOfMediaTypes[pos]; + if (*ppMediaType) + { + (*ppMediaType)->AddRef(); + } +done: + return hr; +} + +STDMETHODIMP_(BOOL) CBasePin::IsMediaTypeSupported +( + _In_ IMFMediaType *pMediaType, + _When_(ppIMFMediaTypeFull != nullptr, _Outptr_result_maybenull_) + IMFMediaType **ppIMFMediaTypeFull +) +{ + HRESULT hr = S_OK; + BOOL bFound = FALSE; + + DMFTCHECKNULL_GOTO(pMediaType,done,E_INVALIDARG); + if (ppIMFMediaTypeFull) + { + *ppIMFMediaTypeFull = nullptr; + } + + for (UINT uIIndex = 0, uISize = (UINT)m_listOfMediaTypes.size(); uIIndex < uISize ; uIIndex++ ) + { + DWORD dwResult = 0; + hr = m_listOfMediaTypes[ uIIndex ]->IsEqual( pMediaType, &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[uIIndex]; + (*ppIMFMediaTypeFull)->AddRef(); + } + break; + } + else if (FAILED(hr)) + { + DMFTCHECKHR_GOTO(hr,done); + } + } +done: + return SUCCEEDED(hr) ? TRUE : FALSE; +} + + +STDMETHODIMP CBasePin::GetOutputAvailableType(_In_ DWORD dwTypeIndex, _Out_opt_ IMFMediaType** ppType) +{ + return GetMediaTypeAt( dwTypeIndex, ppType ); +} + + +HRESULT CBasePin::QueryInterface( + _In_ REFIID iid, + _Outptr_result_maybenull_ void** ppv + ) +{ + HRESULT hr = S_OK; + + DMFTCHECKNULL_GOTO(ppv, out, E_POINTER); + + *ppv = nullptr; + + if ( iid == __uuidof( IUnknown ) ) + { + *ppv = static_cast(this); + AddRef(); + } + else + if ( iid == __uuidof( IMFAttributes ) ) + { + *ppv = static_cast< IMFAttributes* >( this ); + AddRef(); + } + else + if ( iid == __uuidof( IKsControl ) ) + { + *ppv = static_cast< IKsControl* >( this ); + AddRef(); + } + else + { + hr = E_NOINTERFACE; + } +out: + return hr; +} + + + + +// +//Input Pin implementation +// +CInPin::CInPin( + _In_opt_ IMFAttributes *pAttributes, + _In_ ULONG ulPinId, + _In_ CMultipinMft *pParent) + : + CBasePin(ulPinId, pParent), + m_pSourceTransform(nullptr), + m_stStreamType(GUID_NULL), + m_activeStreamCount(0), + m_state(DeviceStreamState_Stop), + m_prefferedMediaType(nullptr), + m_waitInputMediaTypeWaiter(NULL), + m_preferredStreamState(DeviceStreamState_Stop) +{ + setAttributes(pAttributes); +} + + + +CInPin::~CInPin() +{ + setAttributes( nullptr ); + m_pSourceTransform = nullptr; + + for (ULONG ulIndex = 0, ulSize = (ULONG)m_outpins.size(); + ulIndex < ulSize; + ulIndex++) + { + CBasePin *pPin = m_outpins[ulIndex]; + SAFERELEASE(pPin); + } + if (m_waitInputMediaTypeWaiter) + { + CloseHandle(m_waitInputMediaTypeWaiter); + } + +} + + + +STDMETHODIMP CInPin::Init( + _In_ IMFTransform* pTransform + ) +{ + + HRESULT hr = S_OK; + + DMFTCHECKNULL_GOTO( pTransform, done, E_INVALIDARG ); + + m_pSourceTransform = pTransform; + + DMFTCHECKHR_GOTO( GetGUID( MF_DEVICESTREAM_STREAM_CATEGORY, &m_stStreamType ), done ); + + // + //Get the DevProxy IKSControl.. used to send the KSControls or the device control IOCTLS over to devproxy and finally on to the driver!!!! + // + DMFTCHECKHR_GOTO( m_pSourceTransform.As( &m_Ikscontrol ), done ); + + m_waitInputMediaTypeWaiter = CreateEvent( NULL, + FALSE, + FALSE, + TEXT("MediaTypeWaiter") + ); + DMFTCHECKNULL_GOTO( m_waitInputMediaTypeWaiter, done, E_OUTOFMEMORY ); + + DMFTCHECKHR_GOTO( GenerateMFMediaTypeListFromDevice(streamId()),done ); + +done: + if ( FAILED(hr) ) + { + m_pSourceTransform = nullptr; + + if ( m_waitInputMediaTypeWaiter ) + { + CloseHandle( m_waitInputMediaTypeWaiter ); + m_waitInputMediaTypeWaiter = NULL; + } + + m_stStreamType = GUID_NULL; + } + + return hr; +} + + +STDMETHODIMP CInPin::GenerateMFMediaTypeListFromDevice( + _In_ UINT uiStreamId + ) +{ + HRESULT hr = S_OK; + GUID stSubType = { 0 }; + //This is only called in the begining when the input pin is constructed + DMFTCHECKNULL_GOTO( m_pSourceTransform, done, MF_E_TRANSFORM_TYPE_NOT_SET ); + for (UINT iMediaType = 0; SUCCEEDED(hr) ; iMediaType++) + { + ComPtr pMediaType = nullptr; + hr = m_pSourceTransform->MFTGetOutputAvailableType( uiStreamId, iMediaType, pMediaType.GetAddressOf() ); + if (hr != S_OK) + break; + DMFTCHECKHR_GOTO(pMediaType->GetGUID(MF_MT_SUBTYPE, &stSubType),done); + + 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)) ) + { + DWORD pos = 0; + AddMediaType(&pos, pMediaType.Get()); + } + pMediaType = nullptr; + } +done: + if (hr == MF_E_NO_MORE_TYPES) { + hr = S_OK; + } + return hr; +} + +STDMETHODIMP CInPin::SendSample( + _In_ IMFSample *pSample + ) +{ + HRESULT hr = S_OK; + BOOL sentOne = TRUE; + DMFTCHECKNULL_GOTO(pSample, done, S_OK); + + for ( ULONG ulIndex = 0, ulSize = (ULONG) m_outpins.size(); ulIndex < ulSize; ulIndex++ ) + { + COutPin *poPin = (COutPin *)m_outpins[ ulIndex ]; + + pSample->AddRef(); + + if (FAILED(hr = poPin->AddSample(pSample, this))) + { + pSample->Release(); + } + sentOne = (sentOne || SUCCEEDED(hr)); + } +done: + return sentOne ? S_OK : E_FAIL; +} + +STDMETHODIMP_(VOID) CInPin::ConnectPin( _In_ CBasePin * poPin ) +{ + if (poPin!=nullptr) + { + m_outpins.push_back(poPin); + poPin->AddRef(); + } +} + +STDMETHODIMP_(DeviceStreamState) CInPin::GetState() +{ + return m_state; +} + +STDMETHODIMP_(DeviceStreamState) CInPin::SetState( _In_ DeviceStreamState state ) +{ + return (DeviceStreamState)InterlockedExchange((LONG*)&m_state, state ); +} + +STDMETHODIMP CInPin::WaitForSetInputPinMediaChange() +{ + DWORD dwWait = 0; + HRESULT hr = S_OK; + + dwWait = WaitForSingleObject( m_waitInputMediaTypeWaiter, INFINITE ); + + if ( dwWait != WAIT_OBJECT_0 ) + { + hr = E_FAIL; + goto done; + } +done: + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! exiting %x = %!HRESULT!", hr, hr); + return hr; +} + +STDMETHODIMP CInPin::GetInputStreamPreferredState( + _Inout_ DeviceStreamState* value, + _Outptr_opt_result_maybenull_ IMFMediaType** ppMediaType + ) +{ + HRESULT hr = S_OK; + CAutoLock Lock(lock()); + + if (value!=nullptr) + { + *value = m_preferredStreamState; + } + + if (ppMediaType ) + { + *ppMediaType = nullptr; + if ( m_prefferedMediaType != nullptr ) + { + m_prefferedMediaType.CopyTo(ppMediaType); + } + } + + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! exiting %x = %!HRESULT!", hr, hr); + return hr; +} + +HRESULT CInPin::SetInputStreamState( + _In_ IMFMediaType* pMediaType, + _In_ DeviceStreamState value, + _In_ DWORD dwFlags + ) +{ + HRESULT hr = S_OK; + UNREFERENCED_PARAMETER(dwFlags); + + CAutoLock Lock(lock()); + // + //Set the media type + // + + setMediaType(pMediaType); + SetState(value); + + // + //Set the event. This event is being waited by an output media/state change operation + // + + m_prefferedMediaType = nullptr; + SetEvent(m_waitInputMediaTypeWaiter); + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! exiting %x = %!HRESULT!", hr, hr); + return hr; +} + + +// +//Output Pin Implementation +// +COutPin::COutPin( + _In_ ULONG ulPinId, + _In_opt_ CMultipinMft *pparent, + _In_ IKsControl* pIksControl + ) + : CBasePin( ulPinId, pparent ), + m_firstSample( false ) +{ + HRESULT hr = S_OK; + CPinState* pState = NULL; + IMFAttributes *pAttributes = nullptr; + + for ( ULONG ulIndex = 0; ulIndex <= DeviceStreamState_Disabled; ulIndex++ ) + { + switch ( ulIndex ) + { + + case DeviceStreamState_Run: + pState = ( CPinState* )new CPinOpenState(this); + DMFTCHECKNULL_GOTO(pState, done, E_OUTOFMEMORY); + break; + case DeviceStreamState_Pause: + case DeviceStreamState_Stop: + case DeviceStreamState_Disabled: + //Currently both the closed and the drain state behave similarly + pState = ( CPinState* )new CPinClosedState(); + DMFTCHECKNULL_GOTO(pState, done, E_OUTOFMEMORY); + break; + } + + m_states.push_back( pState ); + + m_state = m_states[ DeviceStreamState_Stop ]; + } + // + //Get the input pin IKS control.. the pin IKS control talks to sourcetransform's IKS control + // + m_Ikscontrol = pIksControl; + + MFCreateAttributes( &pAttributes, 3 ); //Create the space for the attribute store!! + setAttributes( pAttributes ); + DMFTCHECKHR_GOTO( SetUINT32( MFT_SUPPORT_DYNAMIC_FORMAT_CHANGE, TRUE ), done ); + DMFTCHECKHR_GOTO( SetString( MFT_ENUM_HARDWARE_URL_Attribute, L"Sample_CameraExtensionMft" ),done ); + DMFTCHECKHR_GOTO( SetUINT32( MF_TRANSFORM_ASYNC, TRUE ),done ); + //Set the pin attribute + + SAFE_RELEASE( pAttributes ); +done: + ; + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! exiting %x = %!HRESULT!", hr, hr); +} + +COutPin::~COutPin() +{ + CPinQueue *que = NULL; + m_Attributes = nullptr; + + for ( ULONG ulIndex = 0, ulSize = (ULONG)m_queues.size(); ulIndex < ulSize; ulIndex++ ) + { + que = m_queues[ ulIndex ]; + delete(que); + que = NULL; + } + CPinState *pinState = NULL; + for (ULONG ulIndex = 0, ulSize = (ULONG)m_states.size(); ulIndex < ulSize; ulIndex++) + { + pinState = m_states[ ulIndex ]; + delete( pinState ); + pinState = NULL; + } + m_states.clear(); + m_queues.clear(); +} + +/*++ +COutPin::AddPin +Description: +Called from AddSample if the Output Pin is in open state. This function looks for the queue +corresponding to the input pin and adds it in the queue. +--*/ +STDMETHODIMP COutPin::AddPin( + _In_ DWORD inputPinId + ) +{ + // + //Add a new queue corresponding to the input pin + // + HRESULT hr = S_OK; + + CPinQueue *que = new (std::nothrow) CPinQueue(inputPinId); + DMFTCHECKNULL_GOTO( que, done, E_OUTOFMEMORY ); + + (void)m_queues.push_back( que ); + // + //Just ramdonmize media types for odd numbered pins + // + if ( streamId() != 0 && streamId() % 2 != 0 ) + { + RandomnizeMediaTypes(m_listOfMediaTypes); + } + +done: + + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! exiting %x = %!HRESULT!", hr, hr); + return S_OK; +} + +/*++ +COutPin::AddSampleInternal +Description: +Called from AddSample if the Output Pin is in open state. This function looks for the queue +corresponding to the input pin and adds it in the queue. +--*/ + +STDMETHODIMP COutPin::AddSampleInternal( _In_ IMFSample *pSample, _In_ CBasePin *pPin ) +{ + BOOL res = true; + CAutoLock Lock( lock() ); + + for ( DWORD dwIndex = 0, dwSize = (DWORD)m_queues.size(); dwIndex < dwSize; dwIndex++ ) + { + // + //This output pin maybe connected to multiple input pins + //Only insert into the corresponding queue to the input pin on which the + //sample is received + // + if (m_queues[dwIndex]->pinStreamId() == pPin->streamId()) + { + if (! m_queues[ dwIndex ]->Insert( pSample ) ) + { + res |= false; + } + + } + } + return res ? S_OK : E_FAIL; +} + +/*++ +COutPin::AddSample +Description: +Called when ProcessInput is called on the Device Transform. The Input Pin puts the samples +in the pins connected. If the Output pins are in open state the sample lands in the queues +--*/ + +STDMETHODIMP COutPin::AddSample( _In_ IMFSample *pSample, _In_ CBasePin *pPin) +{ + HRESULT hr = S_OK; + CAutoLock lock( lock() ); + + DMFTCHECKHR_GOTO( m_state->Open(), done ); + + DMFTCHECKHR_GOTO( AddSampleInternal( pSample, pPin ),done ); + +done: + return hr; +} + +/*++ +COutPin::SetState +Description: +State setter for the output pin +--*/ +STDMETHODIMP_(VOID) COutPin::SetFirstSample( + _In_ BOOL fisrtSample ) +{ + m_firstSample = fisrtSample; +} + +/*++ +COutPin::GetState +Description: +State getter for the output pin +--*/ +DeviceStreamState COutPin::GetState() +{ + return m_state->State(); +} + +/*++ +COutPin::SetState +Description: +State setter for the output pin +--*/ + +DeviceStreamState COutPin::SetState(DeviceStreamState state) +{ + CAutoLock Lock(lock()); + DeviceStreamState oldState = m_state->State(); + m_state = m_states[state]; + return oldState; + +} + +/*++ +COutPin::FlushQueues +Description: +Called from the device Transform when the output queues have to be flushed + +--*/ +HRESULT COutPin::FlushQueues() +{ + HRESULT hr = S_OK; + CAutoLock Lock( lock() ); + + for ( DWORD dwIndex = 0, dwSize = (DWORD) m_queues.size(); dwIndex < dwSize; dwIndex++ ) + { + CPinQueue *que = m_queues[ dwIndex ]; + que->Clear(); + } + + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! exiting %x = %!HRESULT!", hr, hr); + return hr; +} +/*++ +COutPin::ChangeMediaTypeFromInpin +Description: +called from the Device Transfrom When the input media type is changed. This will result in +the xvp being possibly installed in the queue if the media types set on the input +and the output dont match +--*/ +HRESULT COutPin::ChangeMediaTypeFromInpin( + _In_ CInPin* inPin, + _In_ IMFMediaType *pInMediatype, + _In_ IMFMediaType* pOutMediaType, + _In_ DeviceStreamState state) +{ + HRESULT hr = S_OK; + CPinQueue *que = NULL; + CAutoLock Lock(lock()); + // + //Set the state to disabled and while going out we will reset the state back to the requested state + //Flush so that we drop any samples we have in store!! + // + SetState(DeviceStreamState_Disabled); + FlushQueues(); + + for (DWORD dwIndex = 0, dwSize = (DWORD) m_queues.size(); dwIndex < dwSize; dwIndex++) + { + que = m_queues[ dwIndex ]; + if (inPin->streamId() == que->pinStreamId()) + { + break; + } + que = NULL; + } + + if ( que ) + { + // + //recreate the tee, pass the D3D Manager to the Tee which will use DX if D3D manager is present + // + IUnknown *pD3DManagerUnk = NULL; + + (VOID)Parent()->GetD3DDeviceManager( &pD3DManagerUnk ); + hr = que->RecreateTee( pInMediatype, pOutMediaType, pD3DManagerUnk ); + if ( SUCCEEDED( hr ) ) + { + (VOID)setMediaType( pOutMediaType ); + (VOID)SetState( state ); + } + SAFE_RELEASE(pD3DManagerUnk); + } + return hr; +} + +/*++ +Description: + called from the IMFdeviceTransform's +--*/ + +STDMETHODIMP COutPin::GetOutputStreamInfo( + _Out_ MFT_OUTPUT_STREAM_INFO *pStreamInfo + ) +{ + HRESULT hr = S_OK; + IMFMediaType* pMediatype = nullptr; + + getMediaType( &pMediatype ); + + if (SUCCEEDED(hr) && !pMediatype) { + pMediatype->Release(); + pStreamInfo->cbAlignment = 0; + pStreamInfo->cbSize = 0; + pStreamInfo->dwFlags = MFT_OUTPUT_STREAM_WHOLE_SAMPLES | MFT_OUTPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER | MFT_OUTPUT_STREAM_FIXED_SAMPLE_SIZE; + pStreamInfo->dwFlags |= MFT_OUTPUT_STREAM_PROVIDES_SAMPLES; + //We provide our samples.. + } + else { + hr = MF_E_TRANSFORM_TYPE_NOT_SET; + } + return hr; +} + +/*++ +COutPin::ProcessOutput +Description: + called from the Device Transfrom when the transform manager demands output samples.. + If we have samples we forward it. + If we are a photo pin then we forward only if trigger is sent. We ask the devicetransform if we have recieved the transform or not. + If we have recieved the sample and we are passing out a sample we should reset the trigger set on the Device Transform +--*/ + +STDMETHODIMP COutPin::ProcessOutput(_In_ DWORD dwFlags, + _Inout_ MFT_OUTPUT_DATA_BUFFER *pOutputSample, + _Out_ DWORD *pdwStatus + ) +{ + HRESULT hr = S_OK; + IMFSample* pSample = nullptr; + GUID pinClsid = GUID_NULL; + BOOL IsImagePin = FALSE; + BOOL IsSkipSample = FALSE; + UNREFERENCED_PARAMETER(pdwStatus); + UNREFERENCED_PARAMETER(dwFlags); + CAutoLock lock(lock()); + DMFTCHECKHR_GOTO(m_state->Open(), done); + + // + //Check if we are an image photo pin. The process output in that case should only proceed if trigger has been sent + //Candidate for subclass! + // + + if (SUCCEEDED(GetGUID(MF_DEVICESTREAM_STREAM_CATEGORY, &pinClsid)) + && ((IsEqualCLSID(pinClsid, PINNAME_IMAGE)) || IsEqualCLSID(pinClsid, PINNAME_VIDEO_STILL))) + { + IsImagePin = TRUE; + } + + if (IsImagePin && !Parent()->isPhotoTriggerSent()) + { + IsSkipSample = TRUE; + } + + + + for ( DWORD dwIndex = 0, dwSize = (DWORD) m_queues.size(); dwIndex < dwSize; dwIndex++ ) + { + CPinQueue *que = m_queues[dwIndex]; + + pOutputSample->dwStatus = S_OK; + + if (!que->Remove(&pSample)) + { + break; + } + + MFTIME llTime = 0L; + + if (FAILED(pSample->GetSampleTime(&llTime))) + { + llTime = MFGetSystemTime(); + pSample->SetSampleTime(llTime); + } + + if (!IsSkipSample) + { + if (m_firstSample) + { + pSample->SetUINT32(MFSampleExtension_Discontinuity,TRUE); + SetFirstSample(FALSE); + } + + // + // Any processing before we pass the sample to further in the pipeline should be done here + // PROCESSSAMPLE(pSample); + // + + pOutputSample->pSample = pSample; + pOutputSample->dwStatus = S_OK; + } + else + { + SAFERELEASE(pSample); + } + } + if (!IsSkipSample && IsImagePin && pSample) + { + // + //A sample has been sent over so image pin should exit + // +#if defined (MF_DEVICEMFT_PHTOTOCONFIRMATION) + if (Parent()->IsPhotoConfirmationEnabled()) + { + // + // Photo confirmation is enabled i.e. the pipeline has set up photo confirmation + // Service photo confirmation. + // + ComPtr spMediaType = nullptr; + + DMFTCHECKHR_GOTO(getMediaType(spMediaType.GetAddressOf()), done); + + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! Calling PhotoConfirmation %p, is passed", pSample); + + DMFTCHECKHR_GOTO(Parent()->ProcessCapturePhotoConfirmationCallBack(spMediaType.Get(), pSample), done); + } +#endif + if (!Parent()->isPhotoModePhotoSequence()) + { + // + //A sample has been sent over so image pin should exit + // + Parent()->setPhotoTriggerSent(FALSE); + } + } +done: + return hr; +} + +/*++ + COutPin::KsProperty +Description: +The KsProperty for the Pin.. this is to reroute all pin kscontrols to the input pin +--*/ +STDMETHODIMP COutPin::KsProperty( + _In_reads_bytes_(ulPropertyLength) PKSPROPERTY pProperty, + _In_ ULONG ulPropertyLength, + _Inout_updates_bytes_(ulDataLength) LPVOID pPropertyData, + _In_ ULONG ulDataLength, + _Out_opt_ ULONG* pBytesReturned + ) +{ + // + //Route it to input pin + // + return m_Ikscontrol->KsProperty(pProperty, + ulPropertyLength, + pPropertyData, + ulDataLength, + pBytesReturned); +} + + + diff --git a/avstream/sampledevicemft/basepin.h b/avstream/sampledevicemft/basepin.h new file mode 100644 index 000000000..63b94c2be --- /dev/null +++ b/avstream/sampledevicemft/basepin.h @@ -0,0 +1,596 @@ +//*@@@+++@@@@****************************************************************** +// +// Microsoft Windows Media Foundation +// Copyright (C) Microsoft Corporation. All rights reserved. +// +//*@@@---@@@@****************************************************************** +// +#pragma once +#include "stdafx.h" +#include "common.h" + + +extern DeviceStreamState pinStateTransition[][4]; + + +class CPinQueue; +class CPinState; +class CMultipinMft; + +class CBasePin: + public IMFAttributes, + public IKsControl +{ +public: + CBasePin( _In_ ULONG _id=0, _In_ CMultipinMft *parent=NULL); + + virtual ~CBasePin() = 0; + virtual STDMETHODIMP_(DeviceStreamState) GetState() = 0; + virtual STDMETHODIMP_(DeviceStreamState) SetState( _In_ DeviceStreamState State) = 0; + + + // + //IUnknown Interface functions + // + + STDMETHODIMP_(ULONG) AddRef( + void + ) + { + return InterlockedIncrement(&m_nRefCount); + } + STDMETHODIMP_(ULONG) Release( + void + ) + { + ULONG uCount = InterlockedDecrement(&m_nRefCount); + if (uCount == 0) + { + delete this; + } + return uCount; + } + + STDMETHODIMP_(HRESULT) QueryInterface( + _In_ REFIID riid, + _Outptr_result_maybenull_ void **ppvObject + ); + // + // IKsControl Interface functions + // + + STDMETHOD(KsProperty)( + _In_reads_bytes_(ulPropertyLength) PKSPROPERTY pProperty, + _In_ ULONG ulPropertyLength, + _Inout_updates_bytes_(ulDataLength) LPVOID pPropertyData, + _In_ ULONG ulDataLength, + _Out_opt_ ULONG* pBytesReturned + ) + { + if ( m_Ikscontrol!=nullptr ) + { + return m_Ikscontrol->KsProperty(pProperty, + ulPropertyLength, + pPropertyData, + ulDataLength, + pBytesReturned); + } + else + { + return E_NOTIMPL; + } + } + // + //NOOPs for this iteration.. + // + STDMETHOD(KsMethod)( + _In_reads_bytes_(ulMethodLength) PKSMETHOD pMethod, + _In_ ULONG ulMethodLength, + _Inout_updates_bytes_(ulDataLength) LPVOID pMethodData, + _In_ ULONG ulDataLength, + _Out_opt_ ULONG* pBytesReturned + ) + { + UNREFERENCED_PARAMETER(pBytesReturned); + UNREFERENCED_PARAMETER(ulDataLength); + UNREFERENCED_PARAMETER(pMethodData); + UNREFERENCED_PARAMETER(pMethod); + UNREFERENCED_PARAMETER(ulMethodLength); + return S_OK; + } + + STDMETHOD(KsEvent)( + _In_reads_bytes_(ulEventLength) PKSEVENT pEvent, + _In_ ULONG ulEventLength, + _Inout_updates_bytes_opt_(ulDataLength) LPVOID pEventData, + _In_ ULONG ulDataLength, + _Out_opt_ ULONG* pBytesReturned + ) + { + UNREFERENCED_PARAMETER(pBytesReturned); + UNREFERENCED_PARAMETER(ulDataLength); + UNREFERENCED_PARAMETER(pEventData); + UNREFERENCED_PARAMETER(pEvent); + UNREFERENCED_PARAMETER(ulEventLength); + return S_OK; + } + + // + //IMFAttributes implementation + // + STDMETHOD(GetItem)( + _In_ REFGUID guidKey, + _Inout_opt_ PROPVARIANT* pValue + ) + { + return m_Attributes->GetItem(guidKey, pValue); + } + + STDMETHOD(GetItemType)( + _In_ REFGUID guidKey, + _Out_ MF_ATTRIBUTE_TYPE* pType + ) + { + return m_Attributes->GetItemType(guidKey, pType); + } + + STDMETHOD(CompareItem)( + _In_ REFGUID guidKey, + _In_ REFPROPVARIANT Value, + _Out_ BOOL* pbResult + ) + { + return m_Attributes->CompareItem(guidKey, Value, pbResult); + } + + STDMETHOD(Compare)( + _In_ IMFAttributes* pTheirs, + _In_ MF_ATTRIBUTES_MATCH_TYPE MatchType, + _Out_ BOOL* pbResult + ) + { + return m_Attributes->Compare(pTheirs, MatchType, pbResult); + } + + STDMETHOD(GetUINT32)( + _In_ REFGUID guidKey, + _Out_ UINT32* punValue + ) + { + return m_Attributes->GetUINT32(guidKey, punValue); + } + + STDMETHOD(GetUINT64)( + _In_ REFGUID guidKey, + _Out_ UINT64* punValue + ) + { + return m_Attributes->GetUINT64(guidKey, punValue); + } + + STDMETHOD(GetDouble)( + _In_ REFGUID guidKey, + _Out_ double* pfValue + ) + { + return m_Attributes->GetDouble(guidKey, pfValue); + } + + STDMETHOD(GetGUID)( + _In_ REFGUID guidKey, + _Out_ GUID* pguidValue + ) + { + return m_Attributes->GetGUID(guidKey, pguidValue); + } + + STDMETHOD(GetStringLength)( + _In_ REFGUID guidKey, + _Out_ UINT32* pcchLength + ) + { + return m_Attributes->GetStringLength(guidKey, pcchLength); + } + + STDMETHOD(GetString)( + _In_ REFGUID guidKey, + _Out_writes_(cchBufSize) LPWSTR pwszValue, + _In_ UINT32 cchBufSize, + _Inout_opt_ UINT32* pcchLength + ) + { + return m_Attributes->GetString(guidKey, pwszValue, cchBufSize, pcchLength); + } + + STDMETHOD(GetAllocatedString)( + _In_ REFGUID guidKey, + _Out_writes_(*pcchLength + 1) LPWSTR* ppwszValue, + _Inout_ UINT32* pcchLength + ) + { + return m_Attributes->GetAllocatedString(guidKey, ppwszValue, pcchLength); + } + + STDMETHOD(GetBlobSize)( + _In_ REFGUID guidKey, + _Out_ UINT32* pcbBlobSize + ) + { + return m_Attributes->GetBlobSize(guidKey, pcbBlobSize); + } + + STDMETHOD(GetBlob)( + _In_ REFGUID guidKey, + _Out_writes_(cbBufSize) UINT8* pBuf, + UINT32 cbBufSize, + _Inout_ UINT32* pcbBlobSize + ) + { + return m_Attributes->GetBlob(guidKey, pBuf, cbBufSize, pcbBlobSize); + } + + STDMETHOD(GetAllocatedBlob)( + __RPC__in REFGUID guidKey, + __RPC__deref_out_ecount_full_opt(*pcbSize) UINT8** ppBuf, + __RPC__out UINT32* pcbSize + ) + { + return m_Attributes->GetAllocatedBlob(guidKey, ppBuf, pcbSize); + } + + STDMETHOD(GetUnknown)( + __RPC__in REFGUID guidKey, + __RPC__in REFIID riid, + __RPC__deref_out_opt LPVOID *ppv + ) + { + return m_Attributes->GetUnknown(guidKey, riid, ppv); + } + + STDMETHOD(SetItem)( + _In_ REFGUID guidKey, + _In_ REFPROPVARIANT Value + ) + { + return m_Attributes->SetItem(guidKey, Value); + } + + STDMETHOD(DeleteItem)( + _In_ REFGUID guidKey + ) + { + return m_Attributes->DeleteItem(guidKey); + } + + STDMETHOD(DeleteAllItems)() + { + return m_Attributes->DeleteAllItems(); + } + + STDMETHOD(SetUINT32)( + _In_ REFGUID guidKey, + _In_ UINT32 unValue + ) + { + return m_Attributes->SetUINT32(guidKey, unValue); + } + + STDMETHOD(SetUINT64)( + _In_ REFGUID guidKey, + _In_ UINT64 unValue + ) + { + return m_Attributes->SetUINT64(guidKey, unValue); + } + + STDMETHOD(SetDouble)( + _In_ REFGUID guidKey, + _In_ double fValue + ) + { + return m_Attributes->SetDouble(guidKey, fValue); + } + + STDMETHOD(SetGUID)( + _In_ REFGUID guidKey, + _In_ REFGUID guidValue + ) + { + return m_Attributes->SetGUID(guidKey, guidValue); + } + + STDMETHOD(SetString)( + _In_ REFGUID guidKey, + _In_ LPCWSTR wszValue + ) + { + return m_Attributes->SetString(guidKey, wszValue); + } + + STDMETHOD(SetBlob)( + _In_ REFGUID guidKey, + _In_reads_(cbBufSize) const UINT8* pBuf, + UINT32 cbBufSize + ) + { + return m_Attributes->SetBlob(guidKey, pBuf, cbBufSize); + } + + STDMETHOD(SetUnknown)( + _In_ REFGUID guidKey, + _In_ IUnknown* pUnknown + ) + { + return m_Attributes->SetUnknown(guidKey, pUnknown); + } + + STDMETHOD(LockStore)() + { + return m_Attributes->LockStore(); + } + + STDMETHOD(UnlockStore)() + { + return m_Attributes->UnlockStore(); + } + + STDMETHOD(GetCount)( + _Out_ UINT32* pcItems + ) + { + return m_Attributes->GetCount(pcItems); + } + + STDMETHOD(GetItemByIndex)( + UINT32 unIndex, + _Out_ GUID* pguidKey, + _Inout_ PROPVARIANT* pValue + ) + { + return m_Attributes->GetItemByIndex(unIndex, pguidKey, pValue); + } + + STDMETHOD(CopyAllItems)( + _In_ IMFAttributes* pDest + ) + { + return m_Attributes->CopyAllItems(pDest); + } + + // + //Helper Functions + // + __inline DWORD streamId() + { + return m_StreamId; + } + + __inline VOID setMediaType(_In_opt_ IMFMediaType *pMediaType) + { + m_setMediaType = pMediaType; + } + + __inline HRESULT getMediaType(_Outptr_opt_result_maybenull_ IMFMediaType **ppMediaType) + { + HRESULT hr = S_OK; + if (!ppMediaType) + return E_INVALIDARG; + + if (m_setMediaType != nullptr) + { + hr = m_setMediaType.CopyTo(ppMediaType); + } + else + { + hr = MF_E_TRANSFORM_TYPE_NOT_SET; + } + return hr; + } + + __inline STDMETHOD (getPinAttributes) (_In_ IMFAttributes **ppAttributes) + { + HRESULT hr = S_OK; + DMFTCHECKNULL_GOTO( ppAttributes, done, E_INVALIDARG ); + DMFTCHECKHR_GOTO ( QueryInterface( IID_PPV_ARGS(ppAttributes) ), done ); + done: + return hr; + } + + STDMETHODIMP AddMediaType( + _Inout_ DWORD *pos, + _In_ IMFMediaType *pMediatype); /*Filling the media types data structure*/ + STDMETHODIMP GetMediaTypeAt( + _In_ DWORD pos, + _Outptr_result_maybenull_ IMFMediaType **pMediaType); /* getting the data from the data structure*/ + STDMETHODIMP_(BOOL) IsMediaTypeSupported( + _In_ IMFMediaType *pMediaType, + _When_(ppIMFMediaTypeFull != nullptr, _Outptr_result_maybenull_) + IMFMediaType **ppIMFMediaTypeFull); + STDMETHODIMP GetOutputAvailableType( + _In_ DWORD dwTypeIndex, + _Out_opt_ IMFMediaType **ppType); + +protected: + // + //Inline helper functions + // + _inline CMultipinMft* Parent() + { + return m_Parent.Get(); + } + __inline HRESULT setAttributes(_In_ IMFAttributes* _pAttributes) + { + m_Attributes = _pAttributes; + return S_OK; + } + __inline CCritSec& lock() + { + return m_lock; + } + IMFMediaTypeArray m_listOfMediaTypes; + ComPtr m_Attributes; + ComPtr m_Ikscontrol; + +private: + ULONG m_StreamId; /*Device Stream Id*/ + CCritSec m_lock; /*This is only used to change the reference count i.e. active users of this stream*/ + ComPtr m_setMediaType; + ComPtr m_Parent; + ULONG m_nRefCount; +}; + + + +class CInPin: public CBasePin{ +public: + CInPin( _In_opt_ IMFAttributes*, _In_ ULONG ulPinId = 0, _In_ CMultipinMft *pParent=NULL); + ~CInPin(); + + STDMETHOD ( Init )( + _In_ IMFTransform * + ); + STDMETHOD_( VOID, ConnectPin)( + _In_ CBasePin * + ); + STDMETHOD (SendSample)( + _In_ IMFSample * + ); + STDMETHODIMP GenerateMFMediaTypeListFromDevice( + _In_ UINT uiStreamId + ); + STDMETHODIMP WaitForSetInputPinMediaChange( + ); + STDMETHOD_ (DeviceStreamState, SetState)( + _In_ DeviceStreamState + ); /*True for Active and False for Stop*/ + STDMETHOD_(DeviceStreamState, GetState)( + VOID + ); + // + //Corresponding IMFDeviceTransform functions for the Pin + // + STDMETHODIMP_(HRESULT) GetInputStreamPreferredState( + _Inout_ DeviceStreamState *value, + _Outptr_opt_result_maybenull_ IMFMediaType** ppMediaType + ); + STDMETHODIMP_(HRESULT) SetInputStreamState( + _In_ IMFMediaType *pMediaType, + _In_ DeviceStreamState value, + _In_ DWORD dwFlags + ); + + // + //Inline functions + // + __inline IMFMediaType* getPreferredMediaType() + { + return m_prefferedMediaType.Get(); + } + __inline VOID setPreferredMediaType( _In_ IMFMediaType *pMediaType) + { + m_prefferedMediaType = pMediaType; + } + __inline DeviceStreamState setPreferredStreamState(_In_ DeviceStreamState streamState) + { + return (DeviceStreamState)InterlockedCompareExchange((LONG*)&m_preferredStreamState, (LONG)streamState, (LONG)m_preferredStreamState); + } + __inline DeviceStreamState getPreferredStreamState() + { + return m_preferredStreamState; + } + +protected: + ComPtr m_pSourceTransform; /*Source Transform*/ +private: + GUID m_stStreamType; /*GUID representing the GUID*/ + ULONG m_activeStreamCount; /*Set when this stream is active*/ + vector m_outpins; + DeviceStreamState m_state; + DeviceStreamState m_preferredStreamState; + ComPtr m_prefferedMediaType; + HANDLE m_waitInputMediaTypeWaiter; /*Set when the input media type is changed*/ +}; + + + +class COutPin: public CBasePin{ +public: + COutPin( _In_ ULONG id = 0, + _In_opt_ CMultipinMft *pparent = NULL, + _In_ IKsControl* iksControl=NULL); + ~COutPin(); + STDMETHODIMP AddPin( + _In_ DWORD pinId + ); + STDMETHODIMP AddSample( + _In_ IMFSample *pSample, + _In_ CBasePin *inPin + ); + STDMETHODIMP AddSampleInternal( + _In_ IMFSample *pSample, + _In_ CBasePin *inPin + ); + STDMETHODIMP RemoveSample( + _Out_ IMFSample ** + ); + STDMETHODIMP_(DeviceStreamState) SetState( + _In_ DeviceStreamState + ); /*True for Active and False for Stop*/ + STDMETHODIMP_(DeviceStreamState) GetState( + ); + STDMETHODIMP FlushQueues( + ); + STDMETHODIMP GetOutputStreamInfo( + _Out_ MFT_OUTPUT_STREAM_INFO *pStreamInfo + ); + STDMETHODIMP ChangeMediaTypeFromInpin( + _In_ CInPin* inPin, + _In_ IMFMediaType *pInMediatype, + _In_ IMFMediaType* pOutMediaType, + _In_ DeviceStreamState state ); + STDMETHODIMP ProcessOutput ( + _In_ DWORD dwFlags, + _Inout_ MFT_OUTPUT_DATA_BUFFER *pOutputSample, + _Out_ DWORD *pdwStatus + ); + STDMETHODIMP KsProperty( + _In_reads_bytes_(ulPropertyLength) PKSPROPERTY pProperty, + _In_ ULONG ulPropertyLength, + _Inout_updates_bytes_(ulDataLength) LPVOID pPropertyData, + _In_ ULONG ulDataLength, + _Out_opt_ ULONG* pBytesReturned + ); + STDMETHODIMP_(VOID) SetD3Dmanager( + _In_opt_ IUnknown * + ); + STDMETHODIMP_(VOID) SetFirstSample( + _In_ BOOL + ); + + +private: + vector< CPinState *> m_states; /*Array of possible states*/ + CPinState* m_state; /*Current state*/ + vector< CPinQueue *> m_queues; /*List of Queues corresponding to input pins*/ + BOOL m_firstSample; + friend class CPinState; +}; + +// +//Not Implemented!!! +// + +class CImagePin : public COutPin{ +private: + BOOL isTriggerSent; +public: + CImagePin(); + ~CImagePin(); + STDMETHODIMP ProcessOutput( + _In_ DWORD dwFlags, + _Inout_ MFT_OUTPUT_DATA_BUFFER *pOutputSample, + _Out_ DWORD *pdwStatus + ); +}; + + diff --git a/avstream/sampledevicemft/common.h b/avstream/sampledevicemft/common.h new file mode 100644 index 000000000..3959f96b7 --- /dev/null +++ b/avstream/sampledevicemft/common.h @@ -0,0 +1,697 @@ +//*@@@+++@@@@****************************************************************** +// +// Microsoft Windows Media Foundation +// Copyright (C) Microsoft Corporation. All rights reserved. +// +//*@@@---@@@@****************************************************************** +// + +#pragma once + +#define GUID_STRING_LENGTH 36 +#define GUID_BUFFER_SIZE 37 +#define SLEEP_5MILLISEC 5 +#define SLEEP_ONE_SECOND 1000 // One second in milli seconds. + +#ifndef MF_WPP +#define DMFTRACE(...) +#endif + +//TP_NORMAL +//TP_LOWEST +//TP_LOW +//TP_HIGH +//TP_HIGHEST +//TP_ERROR +//TP_MUTED = 0x00070000 + + + +#define mf_assert(a) if(!a) DebugBreak() + +#define WPP_CONTROL_GUIDS \ + WPP_DEFINE_CONTROL_GUID(CtlGUID_DMFTTrace, (CBCCA12E, 9472, 409D, A1B1, 753C98BF03C0), \ + WPP_DEFINE_BIT(DMFT_INIT) \ + WPP_DEFINE_BIT(DMFT_CONTROL) \ + WPP_DEFINE_BIT(DMFT_GENERAL) \ + ) + +#define WPP_LEVEL_FLAG_LOGGER(lvl,flags) WPP_LEVEL_LOGGER(flags) +#define WPP_LEVEL_FLAG_ENABLED(lvl, flags) (WPP_LEVEL_ENABLED(flags) && WPP_CONTROL(WPP_BIT_ ## flags).Level >= lvl) +#define WPP_FLAG_LEVEL_LOGGER(flags,lvl) WPP_LEVEL_LOGGER(flags) +#define WPP_FLAG_LEVEL_ENABLED(flags, lvl) (WPP_LEVEL_ENABLED(flags) && WPP_CONTROL(WPP_BIT_ ## flags).Level >= lvl) + +#define SAFE_ADDREF(p) if( NULL != p ) { ( p )->AddRef(); } +#define SAFE_DELETE(p) delete p; p = NULL; +#define SAFE_SHUTDELETE(p) if( NULL != p ) { ( p )->Shutdown(); delete p; p = NULL; } +#define SAFE_RELEASE(p) if( NULL != p ) { ( p )->Release(); p = NULL; } +#define SAFE_SHUTRELEASE(p) if( NULL != p ) { ( p )->Shutdown(); ( p )->Release(); p = NULL; } +#define SAFE_CLOSERELEASE(p) if( NULL != p ) { ( p )->Close( TRUE ); ( p )->Release(); p = NULL; } +#define SAFE_COTASKMEMFREE(p) CoTaskMemFree( p ); p = NULL; +#define SAFE_SYSFREESTRING(p) SysFreeString( p ); p = NULL; +#define SAFE_ARRAYDELETE(p) delete [] p; p = NULL; +#define SAFE_BYTEARRAYDELETE(p) delete [] (BYTE*) p; p = NULL; + + + +#define DMFTCHECKHR_GOTO(val,label) \ + hr = (val); \ +if (FAILED(hr)) {\ + /*DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! exiting %x = %!HRESULT!", hr, hr); */\ + goto label; \ +} + + +#define DMFTCHECKNULL_GOTO( val, label, err) \ +if( (val) == NULL ) { \ + hr = (err); \ + /*DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! exiting %x = %!HRESULT!", hr, hr);*/\ + goto label; \ +} + +#define TP_SCOPE_TRACE TP_NORMAL +#define DH_THIS_FILE DH_DEVPROXY + +#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__); \ + /*MFWMITRACE(DH_THIS_FILE, TP_NORMAL, __FUNCTION__ " : MULTIPINERROR File: %s Ln:%d",__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; \ +} + + +// +// The Below type definitions are added for this iteration. It will taken out in the +// next one when we will have the windows headers modified to include the interfaces +// externally. +// + + +#if !defined(_IKsControl_) +#define _IKsControl_ +interface DECLSPEC_UUID("28F54685-06FD-11D2-B27A-00A0C9223196") IKsControl; +#undef INTERFACE +#define INTERFACE IKsControl +DECLARE_INTERFACE_(IKsControl, IUnknown) +{ + STDMETHOD(KsProperty)( + THIS_ + IN PKSPROPERTY Property, + IN ULONG PropertyLength, + IN OUT LPVOID PropertyData, + IN ULONG DataLength, + OUT ULONG* BytesReturned + ) PURE; + STDMETHOD(KsMethod)( + THIS_ + IN PKSMETHOD Method, + IN ULONG MethodLength, + IN OUT LPVOID MethodData, + IN ULONG DataLength, + OUT ULONG* BytesReturned + ) PURE; + STDMETHOD(KsEvent)( + THIS_ + IN PKSEVENT Event OPTIONAL, + IN ULONG EventLength, + IN OUT LPVOID EventData, + IN ULONG DataLength, + OUT ULONG* BytesReturned + ) PURE; +}; +#endif //!defined(_IKsControl_) + + + +#if !defined(__IMFDeviceTransform_INTERFACE_DEFINED__) +// +//These definitions will have to be taken out when the +//internal defines are made public +// + +EXTERN_GUID(MEDeviceStreamCreated, 0x0252a1cf, 0x3540, 0x43b4, 0x91, 0x64, 0xd7, 0x2e, 0xb4, 0x05, 0xfa, 0x40); + +typedef +enum _DeviceStreamState +{ + DeviceStreamState_Stop = 0, + DeviceStreamState_Pause = (DeviceStreamState_Stop + 1), + DeviceStreamState_Run = (DeviceStreamState_Pause + 1), + DeviceStreamState_Disabled = (DeviceStreamState_Run + 1) +} DeviceStreamState; + +/* size is 4, align is 4*/ +typedef enum _DeviceStreamState *PDeviceStreamState; + +EXTERN_C const IID IID_IMFDeviceTransform; +MIDL_INTERFACE("D818FBD8-FC46-42F2-87AC-1EA2D1F9BF32") +IMFDeviceTransform : public IUnknown +{ +public: + virtual HRESULT STDMETHODCALLTYPE InitializeTransform( + /* [annotation][in] */ + _In_ IMFAttributes *pAttributes) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetInputAvailableType( + /* [annotation][in] */ + _In_ DWORD dwInputStreamID, + /* [annotation][in] */ + _In_ DWORD dwTypeIndex, + /* [annotation][out] */ + _COM_Outptr_ IMFMediaType **pMediaType) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetInputCurrentType( + /* [annotation][in] */ + _In_ DWORD dwInputStreamID, + /* [annotation][out] */ + _COM_Outptr_ IMFMediaType **pMediaType) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetInputStreamAttributes( + /* [annotation][in] */ + _In_ DWORD dwInputStreamID, + /* [annotation][out] */ + _COM_Outptr_ IMFAttributes **ppAttributes) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetOutputAvailableType( + /* [annotation][in] */ + _In_ DWORD dwOutputStreamID, + /* [annotation][in] */ + _In_ DWORD dwTypeIndex, + /* [annotation][out] */ + _COM_Outptr_ IMFMediaType **pMediaType) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetOutputCurrentType( + /* [annotation][in] */ + _In_ DWORD dwOutputStreamID, + /* [annotation][out] */ + _COM_Outptr_ IMFMediaType **pMediaType) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetOutputStreamAttributes( + /* [annotation][in] */ + _In_ DWORD dwOutputStreamID, + /* [annotation][out] */ + _COM_Outptr_ IMFAttributes **ppAttributes) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetStreamCount( + /* [annotation][out] */ + _Out_ DWORD *pcInputStreams, + /* [annotation][out] */ + _Out_ DWORD *pcOutputStreams) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetStreamIDs( + /* [annotation][in] */ + _In_ DWORD dwInputIDArraySize, + /* [annotation][out] */ + _Out_ DWORD *pdwInputStreamIds, + /* [annotation][in] */ + _In_ DWORD dwOutputIDArraySize, + /* [annotation][out] */ + _Out_ DWORD *pdwOutputStreamIds) = 0; + + virtual HRESULT STDMETHODCALLTYPE ProcessEvent( + /* [annotation][in] */ + _In_ DWORD dwInputStreamID, + /* [annotation][in] */ + _In_ IMFMediaEvent *pEvent) = 0; + + virtual HRESULT STDMETHODCALLTYPE ProcessInput( + /* [annotation][in] */ + _In_ DWORD dwInputStreamID, + /* [annotation][in] */ + _In_ IMFSample *pSample, + /* [annotation][in] */ + _In_ DWORD dwFlags) = 0; + + virtual HRESULT STDMETHODCALLTYPE ProcessMessage( + /* [annotation][in] */ + _In_ MFT_MESSAGE_TYPE eMessage, + /* [annotation][in] */ + _In_ ULONG_PTR ulParam) = 0; + + virtual HRESULT STDMETHODCALLTYPE ProcessOutput( + /* [annotation][in] */ + _In_ DWORD dwFlags, + /* [annotation][in] */ + _In_ DWORD cOutputBufferCount, + /* [size_is][annotation][out][in] */ + _Inout_ MFT_OUTPUT_DATA_BUFFER *pOutputSample, + /* [annotation][out] */ + _Out_ DWORD *pdwStatus) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetInputStreamState( + /* [annotation][in] */ + _In_ DWORD dwStreamID, + /* [annotation][in] */ + _In_ IMFMediaType *pMediaType, + /* [annotation][in] */ + _In_ DeviceStreamState value, + /* [annotation][in] */ + _In_ DWORD dwFlags) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetInputStreamState( + /* [annotation][in] */ + _In_ DWORD dwStreamID, + /* [annotation][out] */ + _Out_ DeviceStreamState *value) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetOutputStreamState( + /* [annotation][in] */ + _In_ DWORD dwStreamID, + /* [annotation][in] */ + _In_ IMFMediaType *pMediaType, + /* [annotation][in] */ + _In_ DeviceStreamState value, + /* [annotation][in] */ + _In_ DWORD dwFlags) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetOutputStreamState( + /* [annotation][in] */ + _In_ DWORD dwStreamID, + /* [annotation][out] */ + _Out_ DeviceStreamState *value) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetInputStreamPreferredState( + /* [annotation][in] */ + _In_ DWORD dwStreamID, + /* [annotation][out] */ + _Out_ DeviceStreamState *value, + /* [annotation][out] */ + _COM_Outptr_ IMFMediaType **ppMediaType) = 0; + + virtual HRESULT STDMETHODCALLTYPE FlushInputStream( + /* [annotation][in] */ + _In_ DWORD dwStreamIndex, + /* [annotation][in] */ + _In_ DWORD dwFlags) = 0; + + virtual HRESULT STDMETHODCALLTYPE FlushOutputStream( + /* [annotation][in] */ + _In_ DWORD dwStreamIndex, + /* [annotation][in] */ + _In_ DWORD dwFlags) = 0; + +}; + +#endif + + +interface IDirect3DDeviceManager9; +//Forward defintion +class CBasePin; +////////////////////////////////////////////////////////////////////////// +// CCritSec +// Description: Wraps a critical section. +////////////////////////////////////////////////////////////////////////// + +class CCritSec +{ +private: + CRITICAL_SECTION m_criticalSection; +public: + CCritSec(); + ~CCritSec(); + _Requires_lock_not_held_(m_criticalSection) _Acquires_lock_(m_criticalSection) + void Lock(); + _Requires_lock_held_(m_criticalSection) _Releases_lock_(m_criticalSection) + void Unlock(); +}; + + +////////////////////////////////////////////////////////////////////////// +// CAutoLock +// Description: Provides automatic locking and unlocking of a +// of a critical section. +////////////////////////////////////////////////////////////////////////// + +class CAutoLock +{ +protected: + CCritSec *m_pCriticalSection; +public: + _Acquires_lock_(this->m_pCriticalSection->m_criticalSection) + CAutoLock(CCritSec& crit); + _Acquires_lock_(this->m_pCriticalSection->m_criticalSection) + CAutoLock(CCritSec* crit); + _Releases_lock_(this->m_pCriticalSection->m_criticalSection) + ~CAutoLock(); +}; + +////////////////////////////////////////////////////////////////////////// +// CMediaTypePrinter +// Description: Rudimentary class for printing media type! +////////////////////////////////////////////////////////////////////////// + +#define MEDIAPRINTER_STARTLEN (512) + +class CMediaTypePrinter{ +private: + IMFMediaType *pMediaType; + PCHAR m_pBuffer; + ULONG buffLen; +public: + CMediaTypePrinter( _In_ IMFMediaType *_pMediaType ); + ~CMediaTypePrinter( ); + STDMETHODIMP_(PCHAR) ToCompleteString( ); + STDMETHODIMP_(PCHAR) ToString(); +}; + + +// +//The below guid is used to register the GUID as the Device Transform. This should be adeed to the +//HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\DeviceClasses\ and under the +//GLOBAL#\Device Parameters key, add a CameraPostProcessingPluginCLSID value, and set its value to +// {0E313280-3169-4F41-A329-9E854169634F} for the Pipeline to pick up the Transform. MFT0 and Device +// Transforms are exclusive and the presence of this key will lead the MFT0 to not load. +// +DEFINE_GUID(CLSID_MultiPinMFT, + 0xe313280, 0x3169, 0x4f41, 0xa3, 0x29, 0x9e, 0x85, 0x41, 0x69, 0x63, 0x4f); + + +typedef enum _MF_TRANSFORM_XVP_OPERATION{ + DeviceMftTransformXVPDisruptiveIn, //Break the XVP tranform and go to the new Media Type + DeviceMftTransformXVPDisruptiveOut, //Keep the old transform + DeviceMftTransformXVPCurrent, //Don't need an XVP + DeviceMftTransformXVPIllegal //Either of the media types NULL, Major types don't match +}MF_TRANSFORM_XVP_OPERATION,*PMF_TRANSFORM_XVP_OPERATION; + +typedef std::vector< IMFMediaType *> IMFMediaTypeArray; +typedef std::vector< CBasePin *> CBasePinArray; +typedef std::vector< IMFSample *> IMFSampleList; +typedef std::pair< std::multimap::iterator, std::multimap::iterator > MMFTMMAPITERATOR; + + +HRESULT CreateCodec( + _In_opt_ IMFMediaType * inMediaType, + _In_opt_ IMFMediaType *outMediaType, + _In_ BOOL operation /*True = Encode, False = Decode*/, + _Out_ IMFTransform **pTransform + ); + +STDMETHODIMP IsOptimizedPlanarVideoInputImageOutputPair( + _In_ IMFMediaType *inMediaType, + _In_ IMFMediaType *outMediaType, + _Out_ bool *optimized, + _Out_ bool *optimizedxvpneeded + ); + +STDMETHODIMP CompareMediaTypesForXVP(_In_opt_ IMFMediaType *inMediaType, + _In_ IMFMediaType *newMediaType, + _Inout_ MF_TRANSFORM_XVP_OPERATION *operation + ); + +STDMETHODIMP RandomnizeMediaTypes( + _In_ IMFMediaTypeArray &pMediaTypeArray + ); + +STDMETHODIMP IsInputDxSample( + _In_ IMFSample* pSample, + _Inout_ BOOL *isDxSample + ); + +STDMETHODIMP_(BOOL) IsPinStateInActive( + _In_ DeviceStreamState state + ); + +STDMETHODIMP_(BOOL) IsKnownUncompressedVideoType( + _In_ GUID guidSubType + ); +LPSTR DumpGUIDA( + _In_ REFGUID guid + ); + + +void printMessageEvent(MFT_MESSAGE_TYPE msg); + + +template +HRESULT ExceptionBoundary(Lambda&& lambda) +{ + try + { + lambda(); + return S_OK; + } + catch (const _com_error& e) + { + return e.Error(); + } + catch (const std::bad_alloc&) + { + return E_OUTOFMEMORY; + } + catch (const std::out_of_range&) + { + return MF_E_INVALIDINDEX; + } + catch (...) + { + return E_FAIL; + } +} + + +// +// Custom Pin GUID. The avstream driver should define a PIN_DESCRIPTOR with this GUID +// defined in cateogory. This will lead to devproxy creating this pin. This pin however +// cannot be used by the pipeline as it may share a custom media type and pipeline could +// fail. Moreover pipeline can currently (when this sample is written) only deal with +// the known pins (PREVIEW/CAPTURE/IMAGE). This code snippet creates a custom pin +// and +// +DEFINE_GUID(AVSTREAM_CUSTOM_PIN_IMAGE, + 0x888c4105, 0xb328, 0x4ed6, 0xa3, 0xca, 0x2f, 0xf4, 0xc0, 0x3a, 0x9f, 0x33); + + +// +// The Below redirections are to support MFT0. Implement this only if you need to load +// MFT0 and DeviceMft simultaneously. +// + +#define _DEFINE_DEVICEMFT_MFT0HELPER_IMPL__ \ + STDMETHOD(MFTGetStreamLimits)( \ + _Inout_ DWORD *pdwInputMinimum, \ + _Inout_ DWORD *pdwInputMaximum, \ + _Inout_ DWORD *pdwOutputMinimum, \ + _Inout_ DWORD *pdwOutputMaximum \ + ) \ +{ \ + UNREFERENCED_PARAMETER(pdwInputMinimum); \ + UNREFERENCED_PARAMETER(pdwInputMaximum); \ + UNREFERENCED_PARAMETER(pdwOutputMinimum); \ + UNREFERENCED_PARAMETER(pdwOutputMaximum); \ + return E_NOTIMPL; \ +} \ + STDMETHOD(MFTGetStreamCount)( \ + _Inout_ DWORD* pdwInputStreams, \ + _Inout_ DWORD* pdwOutputStreams \ + ) \ +{ \ + return GetStreamCount(pdwInputStreams, pdwOutputStreams); \ +} \ + STDMETHOD(MFTGetStreamIDs)( \ + _In_ DWORD dwInputIDArraySize, \ + _Inout_updates_(dwInputIDArraySize) DWORD* pdwInputIDs, \ + _In_ DWORD dwOutputIDArraySize, \ + _Inout_updates_(dwOutputIDArraySize) DWORD* pdwOutputIDs \ + ) \ +{ \ + return GetStreamIDs(dwInputIDArraySize, \ + pdwInputIDs, \ + dwOutputIDArraySize, \ + pdwOutputIDs); \ +} \ + STDMETHOD(MFTGetInputStreamInfo)( \ + _In_ DWORD dwInputStreamID, \ + _Inout_ MFT_INPUT_STREAM_INFO* pStreamInfo \ + ) \ +{ \ + UNREFERENCED_PARAMETER(dwInputStreamID); \ + UNREFERENCED_PARAMETER(pStreamInfo); \ + return E_NOTIMPL; \ +} \ + STDMETHOD(MFTGetOutputStreamInfo)( \ + _In_ DWORD dwOutputStreamID, \ + _Inout_ MFT_OUTPUT_STREAM_INFO* pStreamInfo \ + ) \ +{ \ + UNREFERENCED_PARAMETER(dwOutputStreamID); \ + UNREFERENCED_PARAMETER(pStreamInfo); \ + return E_NOTIMPL; \ +} \ + STDMETHOD(MFTGetInputStreamAttributes)( \ + _In_ DWORD dwInputStreamID, \ + _Out_ IMFAttributes** ppAttributes \ + ) \ +{ \ + return GetInputStreamAttributes( \ + dwInputStreamID, \ + ppAttributes); \ +} \ + STDMETHOD(MFTGetOutputStreamAttributes)( \ + _In_ DWORD dwOutputStreamID, \ + _Out_ IMFAttributes** ppAttributes \ + ) \ +{ \ + return GetOutputStreamAttributes( \ + dwOutputStreamID, ppAttributes); \ +} \ + STDMETHOD(MFTDeleteInputStream)( \ + _In_ DWORD dwStreamID \ + ) \ +{ \ + UNREFERENCED_PARAMETER(dwStreamID); \ + return E_NOTIMPL; \ +} \ + STDMETHOD(MFTAddInputStreams)( \ + _In_ DWORD cStreams, \ + _In_ DWORD* adwStreamIDs \ + ) \ +{ \ + UNREFERENCED_PARAMETER(cStreams); \ + UNREFERENCED_PARAMETER(adwStreamIDs); \ + return E_NOTIMPL; \ +} \ + STDMETHOD(MFTGetInputAvailableType)( \ + _In_ DWORD dwInputStreamID, \ + _In_ DWORD dwTypeIndex, \ + _Out_ IMFMediaType** ppType \ + ) \ +{ \ + return GetInputAvailableType( \ + dwInputStreamID, dwTypeIndex, ppType); \ +} \ + STDMETHOD(MFTGetOutputAvailableType)( \ + _In_ DWORD dwOutputStreamID, \ + _In_ DWORD dwTypeIndex, \ + _Out_ IMFMediaType** ppMediaType \ + ) \ +{ \ + return GetOutputAvailableType( \ + dwOutputStreamID, dwTypeIndex, ppMediaType); \ +} \ + STDMETHOD(MFTSetInputType)( \ + _In_ DWORD dwInputStreamID, \ + _In_ IMFMediaType* pMediaType, \ + _In_ DWORD dwFlags \ + ) \ +{\ + UNREFERENCED_PARAMETER(dwInputStreamID); \ + UNREFERENCED_PARAMETER(pMediaType); \ + UNREFERENCED_PARAMETER(dwFlags); \ + return E_NOTIMPL; \ +} \ + STDMETHOD(MFTSetOutputType)( \ + _In_ DWORD dwOutputStreamID, \ + _In_ IMFMediaType* pMediaType, \ + _In_ DWORD dwFlags \ + ) \ +{ \ + UNREFERENCED_PARAMETER(dwOutputStreamID); \ + UNREFERENCED_PARAMETER(pMediaType); \ + UNREFERENCED_PARAMETER(dwFlags); \ + return E_NOTIMPL; \ +} \ + STDMETHOD(MFTGetInputCurrentType)( \ + _In_ DWORD dwInputStreamID, \ + _Out_ IMFMediaType** ppMediaType \ + ) \ +{\ + UNREFERENCED_PARAMETER(dwInputStreamID); \ + UNREFERENCED_PARAMETER(ppMediaType); \ + return E_NOTIMPL; \ +} \ + STDMETHOD(MFTGetOutputCurrentType)( \ + _In_ DWORD dwOutputStreamID, \ + _Out_ IMFMediaType** ppMediaType \ + ) \ +{ \ + return GetOutputCurrentType(dwOutputStreamID, ppMediaType); \ +} \ + STDMETHOD(MFTGetInputStatus)( \ + _In_ DWORD dwInputStreamID, \ + _Inout_ DWORD* pdwFlags \ + )\ +{\ + UNREFERENCED_PARAMETER(dwInputStreamID); \ + UNREFERENCED_PARAMETER(pdwFlags); \ + return E_NOTIMPL; \ +}\ + STDMETHOD(MFTGetOutputStatus)( \ + _Inout_ DWORD *pdwFlags \ + ) \ +{ \ + UNREFERENCED_PARAMETER(pdwFlags); \ + return E_NOTIMPL; \ +} \ + STDMETHOD(MFTSetOutputBounds)( \ + _In_ LONGLONG hnsLowerBound, \ + _In_ LONGLONG hnsUpperBound \ + ) \ +{\ + UNREFERENCED_PARAMETER(hnsLowerBound); \ + UNREFERENCED_PARAMETER(hnsUpperBound); \ + return E_NOTIMPL; \ +}\ + STDMETHOD(MFTProcessMessage)( \ + _In_ MFT_MESSAGE_TYPE eMessage, \ + _In_ ULONG_PTR ulParam \ + ) \ +{ \ + UNREFERENCED_PARAMETER(eMessage); \ + UNREFERENCED_PARAMETER(ulParam); \ + return E_NOTIMPL; \ +} \ + STDMETHOD(MFTProcessInput)( \ + _In_ DWORD dwInputStreamID, \ + _In_ IMFSample* pSample, \ + _In_ DWORD dwFlags \ + ) \ +{ \ + UNREFERENCED_PARAMETER(dwInputStreamID); \ + UNREFERENCED_PARAMETER(pSample); \ + UNREFERENCED_PARAMETER(dwFlags); \ + return E_NOTIMPL; \ +} \ + STDMETHOD(MFTProcessOutput)( \ + _In_ DWORD dwFlags, \ + _In_ DWORD cOutputBufferCount, \ + _Inout_updates_(cOutputBufferCount) MFT_OUTPUT_DATA_BUFFER *pOutputSamples, \ + _Inout_ DWORD *pdwStatus \ + ) \ +{ \ + UNREFERENCED_PARAMETER(dwFlags); \ + UNREFERENCED_PARAMETER(cOutputBufferCount); \ + UNREFERENCED_PARAMETER(pOutputSamples); \ + UNREFERENCED_PARAMETER(pdwStatus); \ + return E_NOTIMPL; \ +} + + diff --git a/avstream/sampledevicemft/custompin.cpp b/avstream/sampledevicemft/custompin.cpp new file mode 100644 index 000000000..5c983b542 --- /dev/null +++ b/avstream/sampledevicemft/custompin.cpp @@ -0,0 +1,98 @@ +#include "stdafx.h" +#include "multipinmft.h" +#include "basepin.h" +#include "custompin.h" +#ifdef MF_WPP +#include "custompin.tmh" +#endif + + +// +// Implementation notes +// Implement this file only if you have custom media pins streaming +// This implementation just streams from the driver and sends it back +// to the pipeline. This method can be used to stream 3A/Statistic Pins. +// + +CCustomPin::CCustomPin( + _In_ IMFAttributes *pAttributes, + _In_ ULONG PinId, + _In_ CMultipinMft *pParent + ) + :CInPin( pAttributes, PinId, pParent ) +{ + +} +// +// Process input calls the pin here.. In this code sample we +// don't send it anywhere. It is simply shortcircuited back to the pipeline +// +STDMETHODIMP CCustomPin::SendSample( + _In_ IMFSample *pSample + ) +{ + // + // Log sample and exit.. The pipeline will just keep on churning more samples till + // We go into the stop state + // + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! Custom Pin %d recieved Sample %p", streamId(), pSample); + + return S_OK; +} + +// +// State change transitions on the custom pins are different than the other pins +// The other known pins i.e. preview, record, image etc are exposed out to the pipeline +// and the pipeline will set state on it, reset state, For the other pins we tell the +// pipeline that we have samples and the pipeline picks it up. +// +// The CUSTOM pin differs in that it's state has to be managed to by the device MFT. +// This code snippet just varies state transitions on the custom pin when the Device MFT +// changes state to STREAMING/END_STREAMING on it's exposed pins. +// +// The below function tells the pipeline to change the state on the custom pin by sending +// METransformInputStreamStateChanged event with the streamid attribute. The pipeline will +// call back in the custom pin at getprefferedmediatype and finally setinputstreamstate +// which will signal back here singnalling sucessful state transition. +// + +STDMETHODIMP_(DeviceStreamState) CCustomPin::SetState( + _In_ DeviceStreamState State + ) +{ + HRESULT hr = S_OK; + + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! Id:%d Transition into state: %d ", streamId(), State); + + DeviceStreamState oldState = setPreferredStreamState( State ); + + if ( oldState != State ) + { + ComPtr preferredMediaType = nullptr; + + if (State == DeviceStreamState_Run) + { + GetMediaTypeAt(0, preferredMediaType.ReleaseAndGetAddressOf()); + } + + setPreferredMediaType(preferredMediaType.Get()); + setPreferredStreamState( State ); + + { + // + // Notify the device transform manager that we have changed state + // + ComPtr pEvent = nullptr; + DMFTCHECKHR_GOTO( MFCreateMediaEvent(METransformInputStreamStateChanged, GUID_NULL, S_OK, NULL, pEvent.ReleaseAndGetAddressOf()), done ); + DMFTCHECKHR_GOTO( pEvent->SetUINT32(MF_EVENT_MFT_INPUT_STREAM_ID, streamId()), done ); + DMFTCHECKHR_GOTO( Parent()->QueueEvent(pEvent.Get()), done ); + } + // + // Wait to be notified back from the pipeline. + // + DMFTCHECKHR_GOTO( WaitForSetInputPinMediaChange(),done ); + } +done: + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! exiting %x = %!HRESULT!", hr, hr); + return oldState; +} diff --git a/avstream/sampledevicemft/custompin.h b/avstream/sampledevicemft/custompin.h new file mode 100644 index 000000000..c0c3c35f7 --- /dev/null +++ b/avstream/sampledevicemft/custompin.h @@ -0,0 +1,30 @@ +#pragma once +#include "stdafx.h" +#include "common.h" + +// +// Custom Pin is only an input pin in this sample..It is short circuited back +// to the pipeline. This can have uses in implementing depth pins etc. +// It is not exposed to the down stream i.e. captureengine or the +// WinRT code. +// +class CCustomPin : public CInPin{ +public: + + CCustomPin( + _In_ IMFAttributes *pAttributes, + _In_ ULONG PinId, + _In_ CMultipinMft *pParent + ); + STDMETHODIMP SendSample( + _In_ IMFSample *pSample + ); + STDMETHODIMP_( DeviceStreamState )SetState( + _In_ DeviceStreamState + ); +}; + +STDMETHODIMP CheckCustomPin( + _In_ CInPin * pin, + _Inout_ PBOOL isCustom + ); diff --git a/avstream/sampledevicemft/dllmain.cpp b/avstream/sampledevicemft/dllmain.cpp new file mode 100644 index 000000000..7445bfbda --- /dev/null +++ b/avstream/sampledevicemft/dllmain.cpp @@ -0,0 +1,337 @@ +////////////////////////////////////////////////////////////////////////// +// +// dllmain.cpp : Implements DLL exports and COM class factory +// +// 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. +// +// Note: This source file implements the class factory for the transform, +// plus the following DLL functions: +// - DllMain +// - DllCanUnloadNow +// - DllRegisterServer +// - DllUnregisterServer +// - DllGetClassObject +// +////////////////////////////////////////////////////////////////////////// +#include "stdafx.h" +#include "common.h" +#include "multipinmft.h" +#include + +#ifdef MF_WPP +#include "dllmain.tmh" +#endif + + +HRESULT RegisterObject(HMODULE hModule, REFGUID guid, PCWSTR pszDescription, PCWSTR pszThreadingModel); + +HRESULT UnregisterObject(const GUID& guid); + + +// Module Ref count +long g_cRefModule = 0; + +// Handle to the DLL's module +HMODULE g_hModule = NULL; + +void DllAddRef() +{ + InterlockedIncrement(&g_cRefModule); +} + +void DllRelease() +{ + InterlockedDecrement(&g_cRefModule); +} + +// +// IClassFactory implementation +// + +typedef HRESULT (*PFNCREATEINSTANCE)(REFIID riid, void **ppvObject); +struct CLASS_OBJECT_INIT +{ + const CLSID *pClsid; + PFNCREATEINSTANCE pfnCreate; +}; + +// Classes supported by this module: +const CLASS_OBJECT_INIT c_rgClassObjectInit[] = +{ + { &CLSID_MultiPinMFT, MFT_CreateInstance }, +}; + +class CClassFactory : public IClassFactory +{ +public: + + static HRESULT CreateInstance( + REFCLSID clsid, // The CLSID of the object to create (from DllGetClassObject) + const CLASS_OBJECT_INIT *pClassObjectInits, // Array of class factory data. + size_t cClassObjectInits, // Number of elements in the array. + REFIID riid, // The IID of the interface to retrieve (from DllGetClassObject) + void **ppv // Receives a pointer to the interface. + ) + { + *ppv = NULL; + + HRESULT hr = CLASS_E_CLASSNOTAVAILABLE; + + for (size_t i = 0; i < cClassObjectInits; i++) + { + if (clsid == *pClassObjectInits[i].pClsid) + { + IClassFactory *pClassFactory = new (std::nothrow) CClassFactory(pClassObjectInits[i].pfnCreate); + + if (pClassFactory) + { + hr = pClassFactory->QueryInterface(riid, ppv); + pClassFactory->Release(); + } + else + { + hr = E_OUTOFMEMORY; + } + break; // match found + } + } + return hr; + } + + // IUnknown methods + IFACEMETHODIMP QueryInterface(REFIID riid, void ** ppv) + { +#if 0 + static const QITAB qit[] = + { + QITABENT(CClassFactory, IClassFactory), + { 0 } + }; + return QISearch(this, qit, riid, ppv); + +#else + if (riid == __uuidof(IClassFactory)) + { + *ppv = static_cast< IClassFactory* >(this); + AddRef(); + } + return S_OK; +#endif + } + + IFACEMETHODIMP_(ULONG) AddRef() + { + return InterlockedIncrement(&m_cRef); + } + + IFACEMETHODIMP_(ULONG) Release() + { + long cRef = InterlockedDecrement(&m_cRef); + if (cRef == 0) + { + delete this; + } + return cRef; + } + + // IClassFactory methods + + IFACEMETHODIMP CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv) + { + return punkOuter ? CLASS_E_NOAGGREGATION : m_pfnCreate(riid, ppv); + } + + IFACEMETHODIMP LockServer(BOOL fLock) + { + if (fLock) + { + DllAddRef(); + } + else + { + DllRelease(); + } + return S_OK; + } + +private: + + CClassFactory(PFNCREATEINSTANCE pfnCreate) : m_cRef(1), m_pfnCreate(pfnCreate) + { + DllAddRef(); + } + + ~CClassFactory() + { + DllRelease(); + } + + long m_cRef; + PFNCREATEINSTANCE m_pfnCreate; +}; + + + +// +// Standard DLL functions +// + +STDMETHODIMP_(BOOL) WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, void *) +{ + if (dwReason == DLL_PROCESS_ATTACH) + { + g_hModule = (HMODULE)hInstance; + DisableThreadLibraryCalls(hInstance); +#ifdef MF_WPP + WPP_INIT_TRACING(L"MultiPinMft"); +#endif + } + else + if (dwReason == DLL_PROCESS_DETACH) + { +#ifdef MF_WPP + WPP_CLEANUP(); +#endif + } + return TRUE; +} + +STDMETHODIMP DllCanUnloadNow() +{ + return (g_cRefModule == 0) ? S_OK : S_FALSE; +} + +_Check_return_ +STDAPI DllGetClassObject(_In_ REFCLSID clsid, _In_ REFIID riid, _Outptr_ LPVOID FAR* ppv) +{ + return CClassFactory::CreateInstance(clsid, c_rgClassObjectInit, ARRAYSIZE(c_rgClassObjectInit), riid, ppv); +} + +STDMETHODIMP DllRegisterServer() +{ + assert(g_hModule != NULL); + + // Register the CLSID for CoCreateInstance. + HRESULT hr = RegisterObject(g_hModule, CLSID_MultiPinMFT, TEXT("Multiple MFTs"), TEXT("Both")); + + return hr; +} + +STDMETHODIMP DllUnregisterServer() +{ + // Unregister the CLSID. + UnregisterObject(CLSID_MultiPinMFT); + + return S_OK; +} + + +// Converts a CLSID into a string with the form "CLSID\{clsid}" +STDMETHODIMP CreateObjectKeyName(REFGUID guid, _Out_writes_(cchMax) PWSTR pszName, DWORD cchMax) +{ + const DWORD chars_in_guid = 39; + + // convert CLSID uuid to string + OLECHAR szCLSID[chars_in_guid]; + HRESULT hr = StringFromGUID2(guid, szCLSID, chars_in_guid); + if (SUCCEEDED(hr)) + { + // Create a string of the form "CLSID\{clsid}" + hr = StringCchPrintf((STRSAFE_LPWSTR)pszName, cchMax, TEXT("Software\\Classes\\CLSID\\%ls"), szCLSID); + } + return hr; +} + +// Creates a registry key (if needed) and sets the default value of the key +STDMETHODIMP CreateRegKeyAndValue(HKEY hKey, PCWSTR pszSubKeyName, PCWSTR pszValueName, + PCWSTR pszData, PHKEY phkResult) +{ + *phkResult = NULL; + LONG lRet = RegCreateKeyExW( + hKey, pszSubKeyName, + 0, NULL, REG_OPTION_NON_VOLATILE, + KEY_ALL_ACCESS, NULL, phkResult, NULL); + + if (lRet == ERROR_SUCCESS) + { + lRet = RegSetValueExW( + (*phkResult), + pszValueName, 0, REG_SZ, + (LPBYTE) pszData, + ((DWORD) wcslen(pszData) + 1) * sizeof(WCHAR) + ); + + if (lRet != ERROR_SUCCESS) + { + RegCloseKey(*phkResult); + } + } + + return HRESULT_FROM_WIN32(lRet); +} + +// Creates the registry entries for a COM object. + +HRESULT RegisterObject(HMODULE hModule, const GUID& guid, const TCHAR *pszDescription, const TCHAR *pszThreadingModel) +{ + HKEY hKey = NULL; + HKEY hSubkey = NULL; + TCHAR achTemp[MAX_PATH]; + + // Create the name of the key from the object's CLSID + HRESULT hr = CreateObjectKeyName(guid, achTemp, MAX_PATH); + + // Create the new key. + if (SUCCEEDED(hr)) + { + hr = CreateRegKeyAndValue(HKEY_LOCAL_MACHINE, achTemp, NULL, pszDescription,&hKey); + } + + if (SUCCEEDED(hr)) + { + (void)GetModuleFileName(hModule, achTemp, MAX_PATH); + + hr = HRESULT_FROM_WIN32(GetLastError()); + } + + // Create the "InprocServer32" subkey + if (SUCCEEDED(hr)) + { + hr = CreateRegKeyAndValue(hKey, L"InProcServer32", NULL, achTemp, &hSubkey); + RegCloseKey(hSubkey); + } + + // Add a new value to the subkey, for "ThreadingModel" = + if (SUCCEEDED(hr)) + { + hr = CreateRegKeyAndValue(hKey, L"InProcServer32", L"ThreadingModel", pszThreadingModel, &hSubkey); + RegCloseKey(hSubkey); + } + + // close hkeys + RegCloseKey(hKey); + return hr; +} + +// Deletes the registry entries for a COM object. + +HRESULT UnregisterObject(const GUID& guid) +{ + WCHAR achTemp[MAX_PATH]; + + HRESULT hr = CreateObjectKeyName(guid, achTemp, MAX_PATH); + if (SUCCEEDED(hr)) + { + // Delete the key recursively. + LONG lRes = RegDeleteTree(HKEY_LOCAL_MACHINE, achTemp); + hr = HRESULT_FROM_WIN32(lRes); + } + return hr; +} + + diff --git a/avstream/sampledevicemft/mftpeventgenerator.cpp b/avstream/sampledevicemft/mftpeventgenerator.cpp new file mode 100644 index 000000000..b38142609 --- /dev/null +++ b/avstream/sampledevicemft/mftpeventgenerator.cpp @@ -0,0 +1,250 @@ +//*@@@+++@@@@****************************************************************** +// +// Microsoft Windows Media Foundation +// Copyright (C) Microsoft Corporation. All rights reserved. +// +//*@@@---@@@@****************************************************************** +// + +#include "stdafx.h" +#include "common.h" +#include "mftpeventgenerator.h" + + +#define TP_SCOPE_TRACE TP_NORMAL +#define DH_THIS_FILE DH_DEVPROXY + +#ifdef MF_WPP +#include "mftpeventgenerator.tmh" +#endif + +CMediaEventGenerator::CMediaEventGenerator () : + m_nRefCount(0), + m_pQueue(NULL), + m_bShutdown(FALSE) +{ + //Call this explicit... + InitMediaEventGenerator(); +} + +STDMETHODIMP CMediaEventGenerator::InitMediaEventGenerator( + void + ) +{ + + return MFCreateEventQueue(&m_pQueue); + +} + +STDMETHODIMP_(ULONG) CMediaEventGenerator::AddRef( + void + ) +{ + return InterlockedIncrement(&m_nRefCount); +} + +STDMETHODIMP_(ULONG) CMediaEventGenerator::Release( + void + ) +{ + ULONG uCount = InterlockedDecrement(&m_nRefCount); + + if (uCount == 0) + { + delete this; + } + return uCount; +} + +STDMETHODIMP CMediaEventGenerator::QueryInterface( + _In_ REFIID iid, + _COM_Outptr_ void** ppv) +{ + HRESULT hr = S_OK; + + *ppv = NULL; + + if (iid == __uuidof(IUnknown) || iid == __uuidof(IMFMediaEventGenerator)) + { + *ppv = static_cast(this); + AddRef(); + } + else + { + hr = E_NOINTERFACE; + DMFTCHECKHR_GOTO(hr, done); + } + +done: + + return hr; +} + +// +// IMediaEventGenerator methods +// +STDMETHODIMP CMediaEventGenerator::BeginGetEvent( + _In_ IMFAsyncCallback* pCallback, + _In_ IUnknown* pState + ) +{ + HRESULT hr = S_OK; + //MFWMITRACE(DH_THIS_FILE, TP_NORMAL, __FUNCTION__ " : MultiPinMFT BeginGetEvent called"); + m_critSec.Lock(); + + hr = CheckShutdown(); + + if (SUCCEEDED(hr)) + { + hr = m_pQueue->BeginGetEvent(pCallback, pState); + } + + m_critSec.Unlock(); + + return hr; +} + +STDMETHODIMP CMediaEventGenerator::EndGetEvent( + _In_ IMFAsyncResult* pResult, + _Outptr_result_maybenull_ IMFMediaEvent** ppEvent + ) +{ + HRESULT hr = S_OK; + //MFWMITRACE(DH_THIS_FILE, TP_NORMAL, __FUNCTION__ " : MultiPinMFT EndGetEvent called"); + m_critSec.Lock(); + + hr = CheckShutdown(); + + if (SUCCEEDED(hr)) + { + hr = m_pQueue->EndGetEvent(pResult, ppEvent); + } + + m_critSec.Unlock(); + + return hr; +} + +STDMETHODIMP CMediaEventGenerator::GetEvent( + _In_ DWORD dwFlags, + _Outptr_result_maybenull_ IMFMediaEvent** ppEvent + ) +{ + // + // Because GetEvent can block indefinitely, it requires + // a slightly different locking strategy. + // + HRESULT hr = S_OK; + //MFWMITRACE(DH_THIS_FILE, TP_NORMAL, __FUNCTION__ " : MultiPinMFT GetEvent called"); + IMFMediaEventQueue *pQueue = NULL; + + m_critSec.Lock(); + + hr = CheckShutdown(); + // + // Store the pointer in a local variable, so that another thread + // does not release it after we leave the critical section. + // + if (SUCCEEDED(hr)) + { + pQueue = m_pQueue; + } + + m_critSec.Unlock(); + + if (SUCCEEDED(hr)) + { + hr = pQueue->GetEvent(dwFlags, ppEvent); + } + + return hr; +} + +STDMETHODIMP CMediaEventGenerator::QueueEvent( + _In_ MediaEventType met, + _In_ REFGUID extendedType, + _In_ HRESULT hrStatus, + _In_opt_ const PROPVARIANT* pvValue + ) +{ + HRESULT hr = S_OK; + //MFWMITRACE(DH_THIS_FILE, TP_NORMAL, __FUNCTION__ " : MultiPinMFT QueueEvent called"); + m_critSec.Lock(); + + hr = CheckShutdown(); + + if (SUCCEEDED(hr)) + { + + hr = m_pQueue->QueueEventParamVar( + met, + extendedType, + hrStatus, + pvValue + ); + } + + m_critSec.Unlock(); + + return hr; +} + +STDMETHODIMP CMediaEventGenerator::ShutdownEventGenerator( + void + ) +{ + HRESULT hr = S_OK; + + // MFWMITRACE( DH_THIS_FILE, TP_LOWEST, __FUNCTION__ " : entering ..."); + + m_critSec.Lock(); + + hr = CheckShutdown(); + + if (SUCCEEDED(hr)) + { + if (m_pQueue) + { + hr = m_pQueue->Shutdown(); + //MFWMITRACE( DH_THIS_FILE, TP_NORMAL, __FUNCTION__ "Event Generator Queue shutdown hr = %x", hr); + } + SAFE_RELEASE(m_pQueue); + m_bShutdown = TRUE; + } + m_critSec.Unlock(); + + // MFWMITRACE( DH_THIS_FILE, TP_LOWEST, __FUNCTION__ " : exiting..."); + + return hr; +} + +STDMETHODIMP CMediaEventGenerator::QueueEvent( + _In_ IMFMediaEvent* pEvent + ) +{ + HRESULT hr = S_OK; + //MFWMITRACE(DH_THIS_FILE, TP_LOWEST, __FUNCTION__ " : QueueEvent 1 ..."); + m_critSec.Lock(); + + hr = CheckShutdown(); + + if (SUCCEEDED(hr)) + { + if (m_pQueue) + { + hr = m_pQueue->QueueEvent(pEvent); + } + } + + m_critSec.Unlock(); + return hr; +} + +CMediaEventGenerator::~CMediaEventGenerator ( + void + ) +{ + ShutdownEventGenerator(); + //MFASSERT(m_bShutdown); + //MFASSERT(m_nRefCount == 0); +} diff --git a/avstream/sampledevicemft/mftpeventgenerator.h b/avstream/sampledevicemft/mftpeventgenerator.h new file mode 100644 index 000000000..2ddae218f --- /dev/null +++ b/avstream/sampledevicemft/mftpeventgenerator.h @@ -0,0 +1,94 @@ +//*@@@+++@@@@****************************************************************** +// +// Microsoft Windows Media Foundation +// Copyright (C) Microsoft Corporation. All rights reserved. +// +//*@@@---@@@@****************************************************************** +// +#pragma once + +class CMediaEventGenerator : + public IMFMediaEventGenerator +{ + +public: + + // + // IUnknown + // + STDMETHOD_(ULONG, AddRef)( + void + ); + + STDMETHOD_(ULONG, Release)( + void + ); + + STDMETHOD(QueryInterface)( + _In_ REFIID iid, + _COM_Outptr_ void** ppv); + + + // + // IMFMediaEventGenerator + // + STDMETHOD(BeginGetEvent)( + _In_ IMFAsyncCallback* pCallback, + _In_ IUnknown* pState + ); + + STDMETHOD(EndGetEvent)( + _In_ IMFAsyncResult* pResult, + _Outptr_result_maybenull_ IMFMediaEvent** ppEvent + ); + + STDMETHOD(GetEvent)( + _In_ DWORD dwFlags, + _Outptr_result_maybenull_ IMFMediaEvent** ppEvent + ); + + STDMETHOD(QueueEvent)( + _In_ MediaEventType met, + _In_ REFGUID extendedType, + _In_ HRESULT hrStatus, + _In_opt_ const PROPVARIANT* pvValue + ); + + STDMETHOD(QueueEvent)( + _In_ IMFMediaEvent* pEvent + ); + +protected: + + CMediaEventGenerator( + void + ); + + virtual ~CMediaEventGenerator ( + void + ); + // + // Utility Methods + // + STDMETHOD(ShutdownEventGenerator)( + void + ); + + STDMETHOD (InitMediaEventGenerator)( + void + ); + + __inline HRESULT (CheckShutdown)( + void + ) const + { + return (m_bShutdown? MF_E_SHUTDOWN : S_OK); + } + +private: + + long m_nRefCount; + CCritSec m_critSec; + IMFMediaEventQueue* m_pQueue; + BOOL m_bShutdown; +}; diff --git a/avstream/sampledevicemft/multipinmft.cpp b/avstream/sampledevicemft/multipinmft.cpp new file mode 100644 index 000000000..310018584 --- /dev/null +++ b/avstream/sampledevicemft/multipinmft.cpp @@ -0,0 +1,2376 @@ +//*@@@+++@@@@****************************************************************** +// +// Microsoft Windows Media Foundation +// Copyright (C) Microsoft Corporation. All rights reserved. +// +//*@@@---@@@@****************************************************************** +// + +#include "stdafx.h" +#include "multipinmft.h" +#ifdef MF_WPP +#include "multipinmft.tmh" +#endif +// +//Note since MFT_UNIQUE_METHOD_NAMES is defined all the functions of IMFTransform have the Mft suffix.. +// +extern const CLSID CLSID_HwMFTActivate; + +#if _NEED_MFTLOCKING_ +#define MFTLOCKED() {\ + UINT32 punValue = FALSE; \ + hr = GetUINT32(MF_TRANSFORM_ASYNC_UNLOCK, &punValue);\ +if (FAILED(hr) || punValue == FALSE){\ + return MF_E_TRANSFORM_ASYNC_LOCKED; \ +}\ +} +#else + +#define MFTLOCKED() + +#endif + +CMultipinMft::CMultipinMft() +: m_nRefCount( 0 ), + m_InputPinCount( 0 ), + m_OutputPinCount( 0 ), + m_dwWorkQueueId ( MFASYNC_CALLBACK_QUEUE_MULTITHREADED ), + m_lWorkQueuePriority ( 0 ), + m_attributes( nullptr ), + m_pSourceTransform( nullptr ), + m_PhotoTriggerSent(false), + m_filterHasIndependentPin( false ), + m_FilterInPhotoSequence( false ), + m_AsyncPropEvent( nullptr ), + m_filterInWarmStart(false) +#if defined (MF_DEVICEMFT_PHTOTOCONFIRMATION) + , m_spPhotoConfirmationCallback(nullptr) +#endif +{ + ComPtr pAttributes = nullptr; + MFCreateAttributes( &pAttributes, 0 ); + pAttributes->SetUINT32( MF_TRANSFORM_ASYNC, TRUE ); + pAttributes->SetUINT32( MFT_SUPPORT_DYNAMIC_FORMAT_CHANGE, TRUE ); + pAttributes->SetUINT32( MF_SA_D3D_AWARE, TRUE ); + pAttributes->SetString( MFT_ENUM_HARDWARE_URL_Attribute, L"SampleMultiPinMft" ); + m_attributes = pAttributes; +} + +CMultipinMft::~CMultipinMft( ) +{ + CBasePin *pioPin = NULL; + + for ( ULONG ulIndex = 0, ulSize = (ULONG) m_InPins.size(); ulIndex < ulSize; ulIndex++ ) + { + pioPin = m_InPins[ ulIndex ]; + SAFERELEASE( pioPin ); + } + m_InPins.clear(); + + for (ULONG ulIndex = 0, ulSize = (ULONG) m_OutPins.size(); ulIndex < ulSize; ulIndex++) + { + pioPin = m_OutPins[ ulIndex ]; + SAFERELEASE( pioPin ); + } + m_OutPins.clear(); + m_pSourceTransform = nullptr; + +} + +STDMETHODIMP_(ULONG) CMultipinMft::AddRef( + void + ) +{ + return InterlockedIncrement(&m_nRefCount); +} + +STDMETHODIMP_(ULONG) CMultipinMft::Release( + void + ) +{ + ULONG uCount = InterlockedDecrement(&m_nRefCount); + + if ( uCount == 0 ) + { + delete this; + } + return uCount; +} + +STDMETHODIMP CMultipinMft::QueryInterface( + _In_ REFIID iid, + _COM_Outptr_ void** ppv + ) +{ + + HRESULT hr = S_OK; + *ppv = NULL; + + if ((iid == __uuidof(IMFDeviceTransform)) || (iid == __uuidof(IUnknown))) + { + *ppv = static_cast< IMFDeviceTransform* >(this); + AddRef(); + } + else + if ( iid == __uuidof( IMFMediaEventGenerator ) ) + { + *ppv = static_cast< IMFMediaEventGenerator* >(this); + AddRef(); + } + else + if ( iid == __uuidof( IMFShutdown ) ) + { + *ppv = static_cast< IMFShutdown* >( this ); + AddRef(); + } +#if defined (MF_DEVICEMFT_ALLOW_MFT0_LOAD) && defined (MFT_UNIQUE_METHOD_NAMES) + else + if (iid == __uuidof(IMFTransform)) + { + *ppv = static_cast< IMFTransform* >(this); + AddRef(); + } +#endif + else + if ( iid == __uuidof( IKsControl ) ) + { + *ppv = static_cast< IKsControl* >( this ); + AddRef(); + } + else + if ( iid == __uuidof( IMFRealTimeClientEx ) ) + { + *ppv = static_cast< IMFRealTimeClientEx* >( this ); + AddRef(); + } +#if defined (MF_DEVICEMFT_PHTOTOCONFIRMATION) + else + if (iid == __uuidof(IMFCapturePhotoConfirmation)) + { + *ppv = static_cast< IMFCapturePhotoConfirmation* >(this); + AddRef(); + } +#endif + else + { + hr = E_NOINTERFACE; + } + return hr; +} + +/*++ + Description: + This function is the entry point of the transform + The following things may be initialized here + 1) Query for MF_DEVICEMFT_CONNECTED_FILTER_KSCONTROL on the attributes supplied + 2) From the IUnknown acquired get the IMFTransform interface. + 3) Get the stream count.. The output streams are of consequence to the tranform. + The input streams should correspond to the output streams exposed by the source transform + acquired from the Attributes supplied. + 4) Get the IKSControl which is used to send KSPROPERTIES, KSEVENTS and KSMETHODS to the driver for the filer level. Store it in your filter class + 5) Get the OutPutStreamAttributes for the output pins of the source transform. This can further be used to QI and acquire + the IKSControl related to the specific pin. This can be used to send PIN level KSPROPERTIES, EVENTS and METHODS to the pins + 6) Create the output pins + +--*/ + +STDMETHODIMP CMultipinMft::InitializeTransform ( + _In_ IMFAttributes *pAttributes + ) +{ + HRESULT hr = S_OK; + ComPtr spFilterUnk = nullptr; + DWORD *pcInputStreams = NULL, *pcOutputStreams = NULL; + DWORD inputStreams = 0; + DWORD outputStreams = 0; + GUID* outGuids = NULL; + GUID streamCategory = GUID_NULL; + + DMFTCHECKNULL_GOTO( pAttributes, done, E_INVALIDARG ); + // + //The attribute passed with MF_DEVICEMFT_CONNECTED_FILTER_KSCONTROL is the source transform. This generally represents a filter + //This needs to be stored so that we know the device properties. We cache it. We query for the IKSControl which is used to send + //controls to the driver. + // + DMFTCHECKHR_GOTO( pAttributes->GetUnknown( MF_DEVICEMFT_CONNECTED_FILTER_KSCONTROL,IID_PPV_ARGS( &spFilterUnk ) ),done ); + + DMFTCHECKHR_GOTO( spFilterUnk.As( &m_pSourceTransform ), done ); + + DMFTCHECKHR_GOTO( m_pSourceTransform.As( &m_ikscontrol ), done ); + + DMFTCHECKHR_GOTO( m_pSourceTransform->MFTGetStreamCount( &inputStreams, &outputStreams ), done ); + + spFilterUnk = nullptr; + + // + //The number of input pins created by the device transform should match the pins exposed by + //the source transform i.e. outputStreams from SourceTransform or DevProxy = Input pins of the Device MFT + // + + if ( inputStreams > 0 || outputStreams > 0 ) + { + pcInputStreams = new DWORD[ inputStreams ]; + DMFTCHECKNULL_GOTO( pcInputStreams, done, E_OUTOFMEMORY); + + pcOutputStreams = new DWORD[ outputStreams ]; + DMFTCHECKNULL_GOTO( pcOutputStreams, done, E_OUTOFMEMORY ); + + DMFTCHECKHR_GOTO( m_pSourceTransform->MFTGetStreamIDs( inputStreams, pcInputStreams, + outputStreams, + pcOutputStreams ),done ); + + // + // Output pins from DevProxy = Input pins of device MFT.. We are the first transform in the pipeline before MFT0 + // + + for ( ULONG ulIndex = 0; ulIndex < outputStreams; ulIndex++ ) + { + ComPtr pInAttributes = nullptr; + + DMFTCHECKHR_GOTO( m_pSourceTransform->GetOutputStreamAttributes(pcOutputStreams[ulIndex], pInAttributes.GetAddressOf()), done); + + DMFTCHECKHR_GOTO( pInAttributes->GetGUID(MF_DEVICESTREAM_STREAM_CATEGORY, &streamCategory), done); + + if ( IsEqualCLSID( streamCategory, PINNAME_IMAGE ) ) + { + //We have independent pins.. + m_filterHasIndependentPin = true; + } + CInPin *pInPin = nullptr; + if (IsEqualCLSID(streamCategory, AVSTREAM_CUSTOM_PIN_IMAGE)) + { + pInPin = new CCustomPin( pInAttributes.Get(), pcOutputStreams[ulIndex], this); + DMFTCHECKNULL_GOTO( pInPin, done, E_OUTOFMEMORY); + // + // Since the custom pin is an input pin too we will push it + // in the input pins list. This is however not being connected + // to the output in this sample. + m_CustomPinCount++; + } + else + { + pInPin = new CInPin( pInAttributes.Get(), pcOutputStreams[ulIndex], this); + DMFTCHECKNULL_GOTO(pInPin, done, E_OUTOFMEMORY); + } + m_InPins.push_back( pInPin ); + + DMFTCHECKHR_GOTO( pInPin->Init(m_pSourceTransform.Get() ), done); + + + pInPin->AddRef(); + + } + // + // If we have just one output stream exposed off the source transform create a one to three pin + // + if ( ( outputStreams == 1 ) && IsEqualGUID( streamCategory, PINNAME_VIDEO_CAPTURE ) ) + { + outputStreams = 3; + outGuids = new GUID [ outputStreams ]; + DMFTCHECKNULL_GOTO(outGuids, done, E_OUTOFMEMORY); + + m_OutPins.resize(outputStreams, nullptr); + + CInPin *piPin = (CInPin *)m_InPins[0]; + + *outGuids = PINNAME_VIDEO_CAPTURE; + *( outGuids + 1 ) = PINNAME_VIDEO_PREVIEW; + *(outGuids + 2) = PINNAME_IMAGE; //PINNAME_IMAGE for independent pin and PINNAME_VIDEO_STILL for dependent pin!! + + for ( ULONG ulIndex = 0; ulIndex < outputStreams; ulIndex++ ) + { + ComPtr pKsControl = NULL; + + DMFTCHECKHR_GOTO( piPin->QueryInterface(IID_PPV_ARGS( &pKsControl ) ), done); + + COutPin *poPin = new COutPin( ulIndex, this, pKsControl.Get() ); + DMFTCHECKNULL_GOTO( poPin,done, E_OUTOFMEMORY ); + + DMFTCHECKHR_GOTO( BridgeInputPinOutputPin( piPin, poPin ),done ); + + DMFTCHECKHR_GOTO( poPin->SetGUID( MF_DEVICESTREAM_STREAM_CATEGORY, outGuids[ ulIndex ] ),done ); + + DMFTCHECKHR_GOTO( poPin->SetUINT32( MF_DEVICESTREAM_STREAM_ID, ulIndex ),done ); + + m_OutPins[ ulIndex ] = poPin; + poPin->AddRef(); + + } + } + else + { + // + //Create one on one mapping + // + + for (ULONG ulIndex = 0; ulIndex < m_InPins.size(); ulIndex++) + { + ULONG ulPinIndex = 0; + GUID pinGuid = GUID_NULL; + ComPtr< IKsControl > pKsControl = nullptr; + + CInPin *piPin = ( CInPin * )m_InPins[ ulIndex ]; + + if (piPin) + { + BOOL isCustom = false; + if ( SUCCEEDED( CheckCustomPin( piPin, &isCustom )) && ( isCustom ) ) + { + // + // In this sample we are not connecting the custom pin to the output + // This is because we really have no way of testing the custom pin with the + // pipeline. + // This however can be changed if the custom media type is converted here in + // the device MFT and later exposed to the pipeline.. + // + continue; + } + ulPinIndex = piPin->streamId(); + + DMFTCHECKHR_GOTO(piPin->GetGUID(MF_DEVICESTREAM_STREAM_CATEGORY, &pinGuid), done); + + DMFTCHECKHR_GOTO(piPin->QueryInterface(IID_PPV_ARGS(&pKsControl)), done); + + COutPin *poPin = new COutPin(ulPinIndex, this, pKsControl.Get()); + + DMFTCHECKNULL_GOTO(poPin, done, E_OUTOFMEMORY); + + DMFTCHECKHR_GOTO(poPin->SetGUID(MF_DEVICESTREAM_STREAM_CATEGORY, pinGuid), done); + + DMFTCHECKHR_GOTO(poPin->SetUINT32(MF_DEVICESTREAM_STREAM_ID, ulPinIndex), done); + +#if defined (MF_DEVICEMFT_ALLOW_MFT0_LOAD) && defined (MFT_UNIQUE_METHOD_NAMES) + // + // If we wish to load MFT0 as well as Device MFT then we should be doing the following + // Copy over the GUID attribute MF_DEVICESTREAM_EXTENSION_PLUGIN_CLSID from the input + // pin to the output pin. This is because Device MFT is the new face of the filter now + // and MFT0 will now get loaded for the output pins exposed from Device MFT rather than + // DevProxy! + // + + GUID guidMFT0 = GUID_NULL; + + hr = piPin->GetGUID(MF_DEVICESTREAM_EXTENSION_PLUGIN_CLSID, &guidMFT0); + + if (SUCCEEDED(hr)) + { + // + // This stream has an MFT0 .. Attach the GUID to the Outpin pin attribute + // The downstream will query this attribute on the pins exposed from device MFT + // + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! setting Mft0 guid on pin %d", ulIndex); + + DMFTCHECKHR_GOTO(poPin->SetGUID(MF_DEVICESTREAM_EXTENSION_PLUGIN_CLSID, guidMFT0), done); + + DMFTCHECKHR_GOTO(poPin->SetUnknown(MF_DEVICESTREAM_EXTENSION_PLUGIN_CONNECTION_POINT, + static_cast< IUnknown* >(static_cast < IKsControl * >(this))), done); + + } + else + { + // Reset Error.. MFT0 absence should not be an error + hr = S_OK; + } +#endif + DMFTCHECKHR_GOTO(BridgeInputPinOutputPin(piPin, poPin), done); + + m_OutPins.push_back(poPin); + } + } + } + } + + m_InputPinCount = ULONG ( m_InPins.size() ); + m_OutputPinCount = ULONG ( m_OutPins.size() ); + + +done: + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! exiting %x = %!HRESULT!",hr,hr); + + if ( pcInputStreams ) + { + delete[ ] ( pcInputStreams ); + } + if ( pcOutputStreams ) + { + delete[ ] ( pcOutputStreams ); + } + if ( outGuids ) + { + delete [] ( outGuids ); + } + if ( FAILED( hr ) ) + { + //Release the pins and the resources acquired + while ( m_InPins.size() > 0 ) + { + CInPin *pInPin = nullptr; + pInPin = ( CInPin* )m_InPins.back(); + m_InPins.pop_back(); + SAFERELEASE( pInPin ); + } + while ( m_OutPins.size() > 0 ) + { + COutPin *pin = nullptr; + pin = ( COutPin* )m_OutPins.back(); + m_OutPins.pop_back(); + SAFERELEASE( pin ); + } + // + // Simply clear the custom pins since the input pins must have deleted the pin + // + m_pSourceTransform = nullptr; + } + return hr; +} + + +STDMETHODIMP CMultipinMft::SetWorkQueueEx( + _In_ DWORD dwWorkQueueId, + _In_ LONG lWorkItemBasePriority + ) +/*++ + Description: + + Implements IMFRealTimeClientEx::SetWorkQueueEx function + +--*/ +{ + CAutoLock lock( m_critSec ); + // + // Cache the WorkQueuId and WorkItemBasePriority + // + m_dwWorkQueueId = dwWorkQueueId; + m_lWorkQueuePriority = lWorkItemBasePriority; + return S_OK; + +} + +// +// IMFDeviceTransform functions +// +STDMETHODIMP CMultipinMft::GetStreamCount( + _Inout_ DWORD *pdwInputStreams, + _Inout_ DWORD *pdwOutputStreams + ) +/*++ + Description: Implements IMFTransform::GetStreamCount function +--*/ +{ + HRESULT hr = S_OK; + CAutoLock lock(m_critSec); + + + *pdwInputStreams = m_InputPinCount; + *pdwOutputStreams = m_OutputPinCount; + + DMFTRACE( DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! exiting %x = %!HRESULT!", hr, hr ); + return hr; +} + +// +//Doesn't striclt conform to GetStreamIDs on IMFTransform Interface! +// +STDMETHODIMP CMultipinMft::GetStreamIDs( + _In_ DWORD dwInputIDArraySize, + _When_(dwInputIDArraySize >= m_InputPinCount, _Out_writes_(dwInputIDArraySize)) DWORD* pdwInputIDs, + _In_ DWORD dwOutputIDArraySize, + _When_(dwOutputIDArraySize >= m_OutputPinCount && (pdwInputIDs && (dwInputIDArraySize > 0)), + _Out_writes_(dwOutputIDArraySize)) _On_failure_(_Valid_) DWORD* pdwOutputIDs + ) +/*++ + Description: + Implements IMFTransform::GetStreamIDs function +--*/ +{ + HRESULT hr = S_OK; + CAutoLock lock(m_critSec); + + MFTLOCKED(); + + if ( ( dwInputIDArraySize < m_InputPinCount ) && ( dwOutputIDArraySize < m_OutputPinCount ) ) + { + hr = MF_E_BUFFERTOOSMALL; + goto done; + } + + if ( dwInputIDArraySize ) + { + DMFTCHECKNULL_GOTO( pdwInputIDs, done, E_POINTER ); + for ( DWORD dwIndex = 0; dwIndex < ((dwInputIDArraySize > m_InputPinCount) ? m_InputPinCount: + dwInputIDArraySize); dwIndex++ ) + { + pdwInputIDs[ dwIndex ] = ( m_InPins[dwIndex] )->streamId(); + } + } + + if ( dwOutputIDArraySize ) + { + DMFTCHECKNULL_GOTO( pdwOutputIDs, done, E_POINTER ); + for ( DWORD dwIndex = 0; dwIndex < ((dwOutputIDArraySize > m_OutputPinCount)? m_OutputPinCount: + dwOutputIDArraySize); dwIndex++ ) + { + pdwOutputIDs[ dwIndex ] = (m_OutPins[ dwIndex ])->streamId(); + } + } +done: + return hr; +} + +/*++ +Name: CMultipinMft::GetInputAvailableType +Description: +Implements IMFTransform::GetInputAvailableType function. This function +gets the media type supported by the specified stream based on the +index dwTypeIndex. +--*/ +STDMETHODIMP CMultipinMft::GetInputAvailableType( + _In_ DWORD dwInputStreamID, + _In_ DWORD dwTypeIndex, + _Out_ IMFMediaType** ppMediaType + ) +{ + HRESULT hr = S_OK; + MFTLOCKED(); + + + CInPin *piPin = ( CInPin* )GetInPin( dwInputStreamID ); + + DMFTCHECKNULL_GOTO( piPin, done, MF_E_INVALIDSTREAMNUMBER ); + + *ppMediaType = nullptr; + + hr = piPin->GetOutputAvailableType( dwTypeIndex,ppMediaType ); + + if (FAILED(hr)) + { + DMFTRACE( DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! Pin: %d Index: %d exiting %!HRESULT!", + dwInputStreamID, + dwTypeIndex, + hr); + + } + +done: + return hr; +} + +STDMETHODIMP CMultipinMft::GetOutputAvailableType( + _In_ DWORD dwOutputStreamID, + _In_ DWORD dwTypeIndex, + _Out_ IMFMediaType** ppMediaType + ) +/*++ + Description: + + Implements IMFTransform::GetOutputAvailableType function. This function + gets the media type supported by the specified stream based on the + index dwTypeIndex. + +--*/ +{ + HRESULT hr = S_OK; + MFTLOCKED(); + + DMFTCHECKNULL_GOTO(ppMediaType, done, E_INVALIDARG); + + COutPin*poPin = ( COutPin* )GetOutPin( dwOutputStreamID ); + + DMFTCHECKNULL_GOTO( poPin, done, MF_E_INVALIDSTREAMNUMBER ); + + *ppMediaType = nullptr; + + hr = poPin->GetOutputAvailableType( dwTypeIndex, ppMediaType ); + + if ( FAILED( hr ) ) + { + DMFTRACE( DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! Pin: %d Index: %d exiting %!HRESULT!", + dwOutputStreamID, + dwTypeIndex, + hr ); + } + +done: + return hr; +} + +STDMETHODIMP CMultipinMft::GetInputCurrentType( + _In_ DWORD dwInputStreamID, + _COM_Outptr_result_maybenull_ IMFMediaType** ppMediaType + ) +/*++ + Description: + Implements IMFTransform::GetInputCurrentType function. This function + returns the current media type set on the specified stream. +--*/ +{ + // + //The input current types will not come to this transform. + //The outputs of this transform matter. The DTM manages the + //output of this transform and the inptuts of the source transform + // + UNREFERENCED_PARAMETER(dwInputStreamID); + UNREFERENCED_PARAMETER(ppMediaType); + return S_OK; +} + +STDMETHODIMP CMultipinMft::GetOutputCurrentType( + _In_ DWORD dwOutputStreamID, + _Out_ IMFMediaType** ppMediaType + ) +/*++ + Description: + + Implements IMFTransform::GetOutputCurrentType function. This function + returns the current media type set on the specified stream. + +--*/ +{ + HRESULT hr = S_OK; + MFTLOCKED(); + CAutoLock lock( m_critSec ); + + DMFTCHECKNULL_GOTO( ppMediaType, done, E_INVALIDARG ); + + *ppMediaType = nullptr; + + COutPin *poPin = ( COutPin* )GetOutPin( dwOutputStreamID ); + + DMFTCHECKNULL_GOTO( poPin, done, MF_E_INVALIDSTREAMNUMBER ); + + DMFTCHECKHR_GOTO( poPin->getMediaType( ppMediaType ),done ); + + DMFTCHECKNULL_GOTO( *ppMediaType, done, MF_E_TRANSFORM_TYPE_NOT_SET ); + +done: + DMFTRACE( DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! exiting %x = %!HRESULT!", hr, hr ); + return hr; +} + + +STDMETHODIMP CMultipinMft::ProcessEvent( + _In_ DWORD dwInputStreamID, + _In_ IMFMediaEvent* pEvent + ) + /*++ + Description: + + Implements IMFTransform::ProcessEvent function. This function + processes events that come to the MFT. + + --*/ +{ + UNREFERENCED_PARAMETER(dwInputStreamID); + UNREFERENCED_PARAMETER(pEvent); + return S_OK; +} + + + +STDMETHODIMP CMultipinMft::ProcessMessage( + _In_ MFT_MESSAGE_TYPE eMessage, + _In_ ULONG_PTR ulParam + ) +/*++ + Description: + + Implements IMFTransform::ProcessMessage function. This function + processes messages coming to the MFT. + +--*/ +{ + HRESULT hr = S_OK; + MFTLOCKED(); + + UNREFERENCED_PARAMETER(ulParam); + + CAutoLock _lock( m_critSec ); + + printMessageEvent( eMessage ); + + switch ( eMessage ) + { + case MFT_MESSAGE_COMMAND_FLUSH: + // + //This is MFT wide flush.. Flush all output pins + // + (VOID)FlushAllStreams(); + break; + case MFT_MESSAGE_COMMAND_DRAIN: + // + //There is no draining for Device MFT. Just kept here for reference + // + break; + case MFT_MESSAGE_NOTIFY_START_OF_STREAM: + // + //No op for device MFTs + // + break; + case MFT_MESSAGE_SET_D3D_MANAGER: + { + if ( ulParam ) + { + ComPtr< IDirect3DDeviceManager9 > spD3D9Manager; + ComPtr< IMFDXGIDeviceManager > spDXGIManager; + + hr = ( ( IUnknown* ) ulParam )->QueryInterface( IID_PPV_ARGS( &spD3D9Manager ) ); + if ( SUCCEEDED( hr ) ) + { + m_spDeviceManagerUnk = ( IUnknown* )ulParam; + DMFTRACE( DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! IDirect3DDeviceManager9 %p, is passed", spD3D9Manager.Get() ); + } + else + { + hr = ( ( IUnknown* ) ulParam )->QueryInterface( IID_PPV_ARGS( &spDXGIManager ) ); + if ( SUCCEEDED(hr) ) + { + m_spDeviceManagerUnk = (IUnknown*)ulParam; + DMFTRACE( DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! IMFDXGIDeviceManager %p, is passed", spDXGIManager.Get()); + } + } + } + else + { + m_spDeviceManagerUnk = nullptr; + hr = S_OK; + DMFTRACE( DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC!IDirect3DDeviceManager9 was not passed in"); + } + } + break; + case MFT_MESSAGE_NOTIFY_BEGIN_STREAMING: + { + SetStreamingState( DeviceStreamState_Run ); + // + // Start Streaming custom pins if the device transfrom has any + // + SetStreamingStateCustomPins( DeviceStreamState_Run ); + } + break; + case MFT_MESSAGE_NOTIFY_END_STREAMING: + { + SetStreamingState(DeviceStreamState_Stop); + // + // Stop streaming custom pins if the device transform has any + // + SetStreamingStateCustomPins( DeviceStreamState_Stop ); + } + break; + case MFT_MESSAGE_NOTIFY_END_OF_STREAM: + { + SetStreamingState(DeviceStreamState_Stop); + } + break; + default: + ; + } + + DMFTRACE( DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! exiting %x = %!HRESULT!", hr, hr ); + return hr; +} + +STDMETHODIMP CMultipinMft::ProcessInput( + _In_ DWORD dwInputStreamID, + _In_ IMFSample* pSample, + _In_ DWORD dwFlags + ) +/*++ + Description: + + Implements IMFTransform::ProcessInput function.This function is called + when the sourcetransform has input to feed. the pins will try to deliver the + samples to the active output pins conencted. if none are connected then just + returns the sample back to the source transform + +--*/ +{ + HRESULT hr = S_OK; + UNREFERENCED_PARAMETER( dwFlags ); + + MFTLOCKED(); + + CInPin *inPin = ( CInPin* )GetInPin( dwInputStreamID ); + DMFTCHECKNULL_GOTO( inPin, done, E_INVALIDARG ); + + if ( !IsStreaming() ) + { + goto done; + } + + DMFTCHECKHR_GOTO( inPin->SendSample( pSample ), done ); + + QueueEvent( METransformHaveOutput, GUID_NULL, S_OK, NULL ); + +done: + SAFERELEASE( pSample ); + DMFTRACE( DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! exiting %x = %!HRESULT!", hr, hr ); + return hr; + +} + +STDMETHODIMP CMultipinMft::ProcessOutput( + _In_ DWORD dwFlags, + _In_ DWORD cOutputBufferCount, + _Inout_updates_(cOutputBufferCount) MFT_OUTPUT_DATA_BUFFER *pOutputSamples, + _Out_ DWORD *pdwStatus +) +/*++ +Description: + +Implements IMFTransform::ProcessOutput function. This is called by the DTM when +the DT indicates it has samples to give. The DTM will send enough MFT_OUTPUT_DATA_BUFFER +pointers to be filled up as is the number of output pins available. The DT should traverse its +output pins and populate the corresponding MFT_OUTPUT_DATA_BUFFER with the samples available + +--*/ +{ + HRESULT hr = S_OK; + BOOL gotOne = false; + MFTLOCKED(); + UNREFERENCED_PARAMETER( dwFlags ); + + if (cOutputBufferCount > m_OutputPinCount ) + { + DMFTCHECKHR_GOTO( E_INVALIDARG, done ); + } + *pdwStatus = 0; + + for ( DWORD i = 0; i < cOutputBufferCount; i++ ) + { + DWORD dwStreamID = pOutputSamples[i].dwStreamID; + + COutPin *poPin = ( COutPin * )GetOutPin( dwStreamID ); + DMFTCHECKNULL_GOTO( poPin, done, E_INVALIDARG ); + + if ( SUCCEEDED( poPin->ProcessOutput( dwFlags, &pOutputSamples[i], + pdwStatus ) ) ) + { + gotOne = true; + } + } + if (gotOne) + { + hr = S_OK; + } + +done: + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! exiting %x = %!HRESULT!", hr, hr); + return hr; +} + +STDMETHODIMP CMultipinMft::GetInputStreamAttributes( + _In_ DWORD dwInputStreamID, + _COM_Outptr_result_maybenull_ IMFAttributes** ppAttributes + ) +/*++ + Description: + + Implements IMFTransform::GetInputStreamAttributes function. This function + gets the specified input stream's attributes. + +--*/ +{ + HRESULT hr = S_OK; + MFTLOCKED(); + + DMFTCHECKNULL_GOTO( ppAttributes, done, E_INVALIDARG ); + *ppAttributes = nullptr; + + CInPin *piPin = static_cast(GetInPin( dwInputStreamID )); + + DMFTCHECKNULL_GOTO( piPin, done, E_INVALIDARG ); + + hr = piPin->getPinAttributes(ppAttributes); + +done: + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! exiting %x = %!HRESULT!", hr, hr); + return hr; +} + +STDMETHODIMP CMultipinMft::GetOutputStreamAttributes( + _In_ DWORD dwOutputStreamID, + _Out_ IMFAttributes** ppAttributes + ) +/*++ + Description: + + Implements IMFTransform::GetOutputStreamAttributes function. This function + gets the specified output stream's attributes. + +--*/ +{ + HRESULT hr = S_OK; + MFTLOCKED(); + + DMFTCHECKNULL_GOTO(ppAttributes, done, E_INVALIDARG); + *ppAttributes = nullptr; + + COutPin *poPin = (COutPin *)GetOutPin(dwOutputStreamID); + + DMFTCHECKNULL_GOTO( poPin, done, E_INVALIDARG ); + + DMFTCHECKHR_GOTO( poPin->getPinAttributes(ppAttributes), done ); +done: + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! exiting %x = %!HRESULT!", hr, hr); + return hr; +} + +_Requires_no_locks_held_ +STDMETHODIMP CMultipinMft::SetInputStreamState( + _In_ DWORD dwStreamID, + _In_ IMFMediaType *pMediaType, + _In_ DeviceStreamState value, + _In_ DWORD dwFlags + ) + /*++ + Description: + + Implements IMFdeviceTransform::SetInputStreamState function. + Sets the input stream state. + + The control lock is not taken here. The lock is taken for operations on + output pins. This operation is a result of the DT notifying the DTM that + output pin change has resulted in the need for the input to be changed. In + this case the DTM sends a getpreferredinputstate and then this call + + --*/ +{ + HRESULT hr = S_OK; + CInPin *piPin = (CInPin*)GetInPin(dwStreamID); + DMFTCHECKNULL_GOTO(piPin, done, MF_E_INVALIDSTREAMNUMBER); + + DMFTCHECKHR_GOTO(piPin->SetInputStreamState(pMediaType, value, dwFlags),done); + +done: + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! exiting %x = %!HRESULT!", hr, hr); + return hr; +} + +STDMETHODIMP CMultipinMft::GetInputStreamState( + _In_ DWORD dwStreamID, + _Out_ DeviceStreamState *value + ) +{ + HRESULT hr = S_OK; + CInPin *piPin = (CInPin*)GetInPin(dwStreamID); + + DMFTCHECKNULL_GOTO(piPin, done, MF_E_INVALIDSTREAMNUMBER); + + *value = piPin->GetState(); + +done: + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! exiting %x = %!HRESULT!", hr, hr); + return hr; +} + + +STDMETHODIMP CMultipinMft::SetOutputStreamState( + _In_ DWORD dwStreamID, + _In_ IMFMediaType *pMediaType, + _In_ DeviceStreamState state, + _In_ DWORD dwFlags + ) + /*++ + Description: + + Implements IMFdeviceTransform::SetOutputStreamState function. + Sets the output stream state. This is called whenever the stream + is selected or deslected i.e. started or stopped. + + The control lock taken here and this operation should be atomic. + This function should check the input pins connected to the output pin + switch off the state of the input pin. Check if any other Pin connected + to the input pin is in a conflicting state with the state requested on this + output pin. Accordinly it calculates the media type to be set on the input pin + and the state to transition into. It then might recreate the other output pins + connected to it + --*/ +{ + HRESULT hr = S_OK; + UNREFERENCED_PARAMETER(dwFlags); + CAutoLock Lock(m_critSec); + + DMFTCHECKHR_GOTO(ChangeMediaTypeEx(dwStreamID, pMediaType, state),done); + +done: + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! exiting %x = %!HRESULT!", hr, hr); + return hr; +} + +STDMETHODIMP CMultipinMft::GetOutputStreamState( + _In_ DWORD dwStreamID, + _Out_ DeviceStreamState *value + ) + /*++ + Description: + + Implements IMFdeviceTransform::GetOutputStreamState function. + Gets the output stream state. + Called by the DTM to checks states. Atomic operation. needs a lock + --*/ +{ + HRESULT hr = S_OK; + CAutoLock lock(m_critSec); + + COutPin *poPin = (COutPin *)GetOutPin(dwStreamID); + DMFTCHECKNULL_GOTO(poPin, done, MF_E_INVALIDSTREAMNUMBER); + + *value = poPin->GetState(); +done: + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! exiting %x = %!HRESULT!", hr, hr); + return hr; +} + +STDMETHODIMP CMultipinMft::GetInputStreamPreferredState( + _In_ DWORD dwStreamID, + _Inout_ DeviceStreamState *value, + _Outptr_opt_result_maybenull_ IMFMediaType **ppMediaType + ) + /*++ + Description: + + Implements IMFdeviceTransform::GetInputStreamPreferredState function. + Gets the preferred state and the media type to be set on the input pin. + The lock is not held as this will always be called only when we notify + DTM to call us. We notify DTM only from the context on operations + happening on the output pin + --*/ +{ + HRESULT hr = S_OK; + CInPin *piPin = (CInPin*)GetInPin(dwStreamID); + DMFTCHECKNULL_GOTO(piPin, done, MF_E_INVALIDSTREAMNUMBER); + + piPin->GetInputStreamPreferredState(value, ppMediaType); +done: + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! exiting %x = %!HRESULT!", hr, hr); + return hr; +} + +STDMETHODIMP CMultipinMft::FlushInputStream( + _In_ DWORD dwStreamIndex, + _In_ DWORD dwFlags + ) + /*++ + Description: + + Implements IMFdeviceTransform::FlushInputStream function. + --*/ +{ + HRESULT hr = S_OK; + UNREFERENCED_PARAMETER(dwStreamIndex); + UNREFERENCED_PARAMETER(dwFlags); + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! exiting %x = %!HRESULT!", hr, hr); + return hr; +} + +STDMETHODIMP CMultipinMft::FlushOutputStream( + _In_ DWORD dwStreamIndex, + _In_ DWORD dwFlags + ) + /*++ + Description: + + Implements IMFdeviceTransform::FlushOutputStream function. + Called by the DTM to flush streams + --*/ +{ + + HRESULT hr = S_OK; + UNREFERENCED_PARAMETER(dwFlags); + CAutoLock Lock(m_critSec); + + COutPin *poPin = (COutPin*)GetOutPin(dwStreamIndex); + DMFTCHECKNULL_GOTO(poPin, done, E_INVALIDARG); + + DeviceStreamState oldState = poPin->SetState(DeviceStreamState_Disabled); + + hr = poPin->FlushQueues(); + + if (FAILED(hr)) + { + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! failed %x = %!HRESULT!", hr, hr); + } + // + //Restore state + // + poPin->SetState(oldState); +done: + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! exiting %x = %!HRESULT!", hr, hr); + return hr; +} + + +/*++ + Description: + + Called when the Device Transform gets a MFT_MESSAGE_COMMAND_FLUSH. We drain all the queues. + This is called in device source when the source gets end of streaming. + --*/ +STDMETHODIMP_(VOID) CMultipinMft::FlushAllStreams( + VOID + ) +{ + DeviceStreamState oldState; + for ( DWORD dwIndex = 0, dwSize = (DWORD)m_OutPins.size(); dwIndex < dwSize; dwIndex++ ) + { + COutPin *poPin = (COutPin *)m_OutPins[dwIndex]; + oldState = poPin->SetState(DeviceStreamState_Disabled); + poPin->FlushQueues(); + // + //Restore state + // + poPin->SetState(oldState); + } +} + + +// +// IKsControl interface functions +// +STDMETHODIMP CMultipinMft::KsProperty( + _In_reads_bytes_(ulPropertyLength) PKSPROPERTY pProperty, + _In_ ULONG ulPropertyLength, + _Inout_updates_bytes_(ulDataLength) LPVOID pvPropertyData, + _In_ ULONG ulDataLength, + _Inout_ ULONG* pulBytesReturned + ) + /*++ + Description: + + Implements IKSProperty::KsProperty function. + used to pass control commands to the driver (generally) + This can be used to intercepted the control to figure out + if it needs to be propogated to the driver or not + --*/ +{ + HRESULT hr = S_OK; + UNREFERENCED_PARAMETER(pulBytesReturned); + DMFTCHECKNULL_GOTO(pProperty, done, E_INVALIDARG); + DMFTCHECKNULL_GOTO(pulBytesReturned, done, E_INVALIDARG); + if (IsEqualCLSID(pProperty->Set, KSPROPERTYSETID_ExtendedCameraControl) + && (pProperty->Id == KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOTHUMBNAIL)) + { + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! Thumbnail sent %d",pProperty->Flags); + } + if (IsEqualCLSID(pProperty->Set, KSPROPERTYSETID_ExtendedCameraControl) + &&(!filterHasIndependentPin())) + { + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! Extended Control %d Passed ",pProperty->Id); + switch (pProperty->Id) + { + case KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOMODE: + hr = ExtendedPhotoModeHandler(pProperty, + ulPropertyLength, pvPropertyData, ulDataLength, pulBytesReturned); + goto done; + break; + case KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOMAXFRAMERATE: + hr = ExtendedPhotoMaxFrameRate(pProperty, + ulPropertyLength, pvPropertyData, ulDataLength, pulBytesReturned); + goto done; + break; + case KSPROPERTY_CAMERACONTROL_EXTENDED_MAXVIDFPS_PHOTORES: + hr = MaxVidFPS_PhotoResHandler(pProperty, + ulPropertyLength, pvPropertyData, ulDataLength, pulBytesReturned); + goto done; + break; + case KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOTRIGGERTIME: + hr = QPCTimeHandler(pProperty, + ulPropertyLength, pvPropertyData, ulDataLength, pulBytesReturned); + goto done; + break; + case KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOFRAMERATE: + hr = PhotoFrameRateHandler(pProperty, + ulPropertyLength, pvPropertyData, ulDataLength, pulBytesReturned); + goto done; + break; + case KSPROPERTY_CAMERACONTROL_EXTENDED_WARMSTART: + hr = WarmStartHandler(pProperty, + ulPropertyLength, pvPropertyData, ulDataLength, pulBytesReturned); + goto done; + break; + } + } + else + if ((IsEqualCLSID(pProperty->Set, PROPSETID_VIDCAP_VIDEOCONTROL)) && + (pProperty->Id == KSPROPERTY_VIDEOCONTROL_MODE)) + { + // + //A photo trigger was sent!!! + //We need to set the event haveoutput + // + PKSPROPERTY_VIDEOCONTROL_MODE_S VideoControl = NULL; + if (sizeof(KSPROPERTY_VIDEOCONTROL_MODE_S) == ulDataLength) + { + VideoControl = (PKSPROPERTY_VIDEOCONTROL_MODE_S)pvPropertyData; + m_PhotoModeIsPhotoSequence = false; + + if (VideoControl->Mode == KS_VideoControlFlag_StartPhotoSequenceCapture) + { + // + //Signalling start of photo sequence + // + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! Starting PhotoSequence Trigger"); + m_PhotoModeIsPhotoSequence = true; + setPhotoTriggerSent(true); + } + else + if (VideoControl->Mode == KS_VideoControlFlag_StopPhotoSequenceCapture) + { + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! Stopping PhotoSequence Trigger"); + m_PhotoModeIsPhotoSequence = false; + setPhotoTriggerSent(false); + } + else + { + // + //Normal trigger sent for single photo acquisition! + // + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! Take Single Photo Trigger"); + setPhotoTriggerSent(true); + } + + } + + if (!filterHasIndependentPin()) + { + goto done; + } + } + hr = m_ikscontrol->KsProperty(pProperty, + ulPropertyLength, + pvPropertyData, + ulDataLength, + pulBytesReturned); + + +done: + LPSTR guidStr = DumpGUIDA(pProperty->Set); + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! g:%s p:%d exiting %x = %!HRESULT!", guidStr, pProperty->Id, hr, hr); + delete(guidStr); + return hr; +} + +STDMETHODIMP CMultipinMft::KsMethod( + _In_reads_bytes_(ulPropertyLength) PKSMETHOD pMethod, + _In_ ULONG ulPropertyLength, + _Inout_updates_bytes_(ulDataLength) LPVOID pvPropertyData, + _In_ ULONG ulDataLength, + _Inout_ ULONG* pulBytesReturned + ) + /*++ + Description: + + Implements IKSProperty::KsMethod function. + --*/ +{ + return m_ikscontrol->KsMethod( + pMethod, + ulPropertyLength, + pvPropertyData, + ulDataLength, + pulBytesReturned + ); +} + +STDMETHODIMP CMultipinMft::KsEvent( + _In_reads_bytes_(ulEventLength) PKSEVENT pEvent, + _In_ ULONG ulEventLength, + _Inout_updates_bytes_opt_(ulDataLength) LPVOID pEventData, + _In_ ULONG ulDataLength, + _Inout_ ULONG* pBytesReturned + ) + /*++ + Description: + + Implements IKSProperty::KsEvent function. + --*/ +{ + if ((pEvent && (pEvent->Set == KSEVENTSETID_ExtendedCameraControl)) + && (!filterHasIndependentPin())) + { + // + //Store only for emualted controls..To do!!! + // + switch (pEvent->Id) + { + case KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOMODE: + case KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOMAXFRAMERATE: + case KSPROPERTY_CAMERACONTROL_EXTENDED_MAXVIDFPS_PHOTORES: + case KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOTRIGGERTIME: + case KSPROPERTY_CAMERACONTROL_EXTENDED_PHOTOFRAMERATE: + case KSPROPERTY_CAMERACONTROL_EXTENDED_WARMSTART: + { + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! Acquiring Event for Async Extended Control"); + KSEVENTDATA* ksEventData = (KSEVENTDATA*)pEventData; + m_AsyncPropEvent = ksEventData->EventHandle.Event; + } + return S_OK; + } + } + return m_ikscontrol->KsEvent(pEvent, + ulEventLength, + pEventData, + ulDataLength, + pBytesReturned); +} + + +#if defined (MF_DEVICEMFT_PHTOTOCONFIRMATION) +// +//IMFGetService functions +// + +STDMETHODIMP CMultipinMft::GetService( + __in REFGUID guidService, + __in REFIID riid, + __deref_out LPVOID* ppvObject + ) +{ + // + //This doesn't necessarily need to be a GetService function, but this is just so that the + //pipeline implementation and the Device transform implementation is consistent. + //In this sample the photoconfirmation interface is implementated by the same class + //we can delegate it later to any of the pins if needed + // + UNREFERENCED_PARAMETER(guidService); + if (riid == __uuidof(IMFCapturePhotoConfirmation)) + { + return QueryInterface(riid, ppvObject); + } + else + return MF_E_UNSUPPORTED_SERVICE; + + +} + +// +//IMFCapturePhotoConfirmation functions implemented +// + +STDMETHODIMP CMultipinMft::SetPhotoConfirmationCallback( + _In_ IMFAsyncCallback* pNotificationCallback + ) +{ + CAutoLock Lock(m_critSec); + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! Setting PhotoConfirmation %p, is passed", pNotificationCallback); + + m_spPhotoConfirmationCallback = pNotificationCallback; + return S_OK; +} +STDMETHODIMP CMultipinMft::SetPixelFormat( + _In_ GUID subtype + ) +{ + m_guidPhotoConfirmationSubtype = subtype; + return S_OK; +} +STDMETHODIMP CMultipinMft::GetPixelFormat( + _Out_ GUID* subtype + ) +{ + *subtype = m_guidPhotoConfirmationSubtype; + return S_OK; +} + +#endif + + + +#if defined (MF_DEVICEMFT_ALLOW_MFT0_LOAD) && defined (MFT_UNIQUE_METHOD_NAMES) + +// +// IMFTransform function(s). +// + +// +// Note: This is the only IMFTransform function which is not a redirector to the +// DeviceTransform functions. The rest of IMFTransform functions are in the file common.h +// This function returns the IMFAttribute created for Device MFT. If DMFT is +// not loaded (usually )MFT0's call to GetAttributes will get the Attribute store of DevProxy. +// A device MFT loaded will not pass through the devproxy attribute store, but it will pass +// the device MFT attributes. This should be similar to the singular DevProxy attribute +// which the MFT0 providers can use to synchronize across various MFT0's +// + +STDMETHODIMP CMultipinMft::GetAttributes( + _COM_Outptr_opt_result_maybenull_ IMFAttributes** ppAttributes + ) +{ + HRESULT hr = S_OK; + + DMFTCHECKNULL_GOTO(ppAttributes, done, E_INVALIDARG); + + *ppAttributes = nullptr; + + if (m_attributes != nullptr) + { + m_attributes.CopyTo(ppAttributes); + } + else + { + hr = E_OUTOFMEMORY; + } + +done: + return hr; +} +#endif + + + + +// +//HELPER FUNCTIONS +// + +STDMETHODIMP_(CBasePin*) CMultipinMft::GetInPin( + _In_ DWORD dwStreamId + ) +{ + CInPin *inPin = NULL; + for (DWORD dwIndex = 0, dwSize = (DWORD)m_InPins.size(); dwIndex < dwSize; dwIndex++) + { + inPin = (CInPin *)m_InPins[dwIndex]; + if (dwStreamId == inPin->streamId()) + { + break; + } + inPin = NULL; + } + return inPin; +} + +STDMETHODIMP_(CBasePin*) CMultipinMft::GetOutPin( + _In_ DWORD dwStreamId + ) +{ + COutPin *outPin = NULL; + + for ( DWORD dwIndex = 0, dwSize = (DWORD) m_OutPins.size(); dwIndex < dwSize; dwIndex++ ) + { + outPin = ( COutPin * )m_OutPins[ dwIndex ]; + + if ( dwStreamId == outPin->streamId() ) + { + break; + } + + outPin = NULL; + } + + return outPin; +} + +/*++ +Description: +This is a critical function which changes the state on an output pin +This should be called under the control lock of the DT. +Here the +--*/ +STDMETHODIMP CMultipinMft::ChangeMediaTypeEx( + _In_ ULONG pinId, + _In_opt_ IMFMediaType *pMediaType, + _In_ DeviceStreamState reqState + ) +{ + HRESULT hr = S_OK; + DeviceStreamState oldOutPinState; + DeviceStreamState newOutStreamState; + ComPtr pFullType = nullptr; + + COutPin *poPin = static_cast( GetOutPin(pinId) ); + + MMFTMMAPITERATOR inputPinPos; + + DMFTCHECKNULL_GOTO( poPin, done, E_INVALIDARG ); + // + //Check if the media type requested is a supported type + // + if ( pMediaType ) + { + if ( !poPin->IsMediaTypeSupported( pMediaType, &pFullType ) ) + { + DMFTCHECKHR_GOTO(MF_E_INVALIDMEDIATYPE, done); + } + } + // + //First step disable the output pin. store the old state + // + oldOutPinState = poPin->SetState( DeviceStreamState_Disabled ); + + (void)poPin->FlushQueues(); + + newOutStreamState = pinStateTransition[oldOutPinState][reqState]; //New state needed + + // + //Go through the output pins' maps that has the conencted input pin to the output pin + // + inputPinPos = m_outputPinMap.equal_range( pinId ); + + for (std::multimap::iterator piterator = inputPinPos.first; piterator != inputPinPos.second;piterator++) + { + // + //Get the output pins connected to the input pin. The input pin map consists of the output pins connected + // + ULONG connectedInputPin = (*piterator).second; + BOOL isAnyConnectedOutPinActive = false; + BOOL isAnyConnectedOutPinPaused = false; + BOOL isAnyConnectedOutPinRunning = false; + BOOL doWeWaitForSetInput = false; + DeviceStreamState oldInputStreamState; + MF_TRANSFORM_XVP_OPERATION operation = DeviceMftTransformXVPIllegal; + ComPtr pInputMediaType = nullptr; + CInPin *pconnectedInPin = (CInPin*)GetInPin( connectedInputPin ); + oldInputStreamState = pconnectedInPin->SetState( DeviceStreamState_Disabled ); + DeviceStreamState newRequestedInPinState = pinStateTransition[oldInputStreamState][newOutStreamState]; + + if (pFullType) + { + pconnectedInPin->getMediaType( &pInputMediaType ); + } + + // + // Check if we need an XVP to be inserted between this input and output pins. + // + CompareMediaTypesForXVP( pInputMediaType.Get(), pFullType.Get(), &operation ); + DMFTCHECKHR_GOTO( GetConnectOutPinStatus( connectedInputPin, + pinId, // pinId = stream which should be excluded from the search. This function searches if + &isAnyConnectedOutPinPaused, // there are any other output pins connected other than the ouput pin (on which the change media type is requested) + &isAnyConnectedOutPinRunning, //, which is streaming, paused. This way if the output pin is requested to go to Pause, Stop and if any of the + &isAnyConnectedOutPinActive ),done ); //other connected output pins are active, then input it not deactivated + + // + //Check if we are asked to go to an active state + //Check for the new media type. if + // + if ( !IsPinStateInActive( newRequestedInPinState ) ) + { + // + //We are being told to go active + //We will request a change in input stream state. + // + DMFTRACE( DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! %p Going active IN: %d OUT: %d ", this, connectedInputPin, pinId ); + doWeWaitForSetInput = true; + if ( !pFullType ) + { + DMFTCHECKHR_GOTO(MF_E_INVALIDMEDIATYPE, done); + } + if ( (! pInputMediaType ) || ( operation == DeviceMftTransformXVPDisruptiveIn ) ) + { + pInputMediaType = pFullType; + } + if ( newRequestedInPinState == DeviceStreamState_Pause && isAnyConnectedOutPinRunning ) + { + newRequestedInPinState = DeviceStreamState_Run; + } + } + else + { + // + //We have been requested to go inactive + // + DMFTRACE( DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! %p Going Inactive IN: %d OUT: %d ", this, connectedInputPin, pinId ); + + if ( operation == DeviceMftTransformXVPDisruptiveIn ) + { + // + //Only where we recieve a disruptive media type change and a media type along with it, which is unlikely!! + // + pInputMediaType = pFullType; + doWeWaitForSetInput = true; + } + if ( isAnyConnectedOutPinActive ) + { + newRequestedInPinState = ( isAnyConnectedOutPinRunning ) ? DeviceStreamState_Run : DeviceStreamState_Pause; + } + else + { + //Switch over to the new media type.. + pInputMediaType = pFullType; + if ( !IsPinStateInActive( oldInputStreamState ) ) + { + //It was originall active.. now going down + doWeWaitForSetInput = true; + } + } + } + + // + // This will happen if we need a change in Input media type, We will send an event + // METransformInputStreamStateChanged to the Device transform manager. This will result + // in the Device Transform manager calling us back in getprefferedinputstate where + // we will give it back the state of the input and the media type to be set. + // All this happens when we are holding a lock here in Change output media type. Hence during the + // input media type change we don't hold a lock. THE DTM takes care not to send you any more media + // type change operations that can cause a deadlock. + // + + if ( doWeWaitForSetInput ) + { + pconnectedInPin->setPreferredMediaType( pInputMediaType.Get() ); + pconnectedInPin->setPreferredStreamState( newRequestedInPinState ); + SendEventToManager( METransformInputStreamStateChanged, GUID_NULL, pconnectedInPin->streamId() ); + // + //The media type will be set on the input pin by the time we return from the wait + // + DMFTCHECKHR_GOTO( pconnectedInPin->WaitForSetInputPinMediaChange(), done ); + } + else + { + pconnectedInPin->SetState( oldInputStreamState ); + pconnectedInPin->setMediaType( pInputMediaType.Get() ); + } + + //Now the input type is all set.. + pInputMediaType = nullptr; + (VOID)pconnectedInPin->getMediaType( &pInputMediaType ); + // + //Now propogate the media type change to the output pins + // + MMFTMMAPITERATOR outputPinPos = m_inputPinMap.equal_range(pconnectedInPin->streamId()); + for ( std::multimap::iterator poutPinsIterator = outputPinPos.first; + poutPinsIterator != outputPinPos.second; + poutPinsIterator++ ) + { + ComPtr pOutMediatype = nullptr; + DeviceStreamState outPinState; + ULONG connectedoutPin = (*poutPinsIterator).second; + COutPin* pIoPin = static_cast( GetOutPin ( connectedoutPin ) ); + + (VOID)pIoPin->getMediaType( &pOutMediatype ); + outPinState = pIoPin->GetState(); + + if ( pIoPin->streamId() != pinId) + { + // + //This is the pin other than the output pin where the original change media type was recieved. + // + + if ( pInputMediaType != nullptr && pOutMediatype != nullptr ) + { + DMFTCHECKHR_GOTO( pIoPin->ChangeMediaTypeFromInpin(pconnectedInPin, pInputMediaType.Get(), + pOutMediatype.Get(), + outPinState ),done); + } + } + else + { + // + //Change the media type of the requested output pin + // + DMFTCHECKHR_GOTO( pIoPin->ChangeMediaTypeFromInpin(pconnectedInPin, pInputMediaType.Get(), pFullType.Get(), newOutStreamState), done); + //Also signal to the manager that a stream state change has happened + SendEventToManager( MEUnknown, MEDeviceStreamCreated, pIoPin->streamId()); + // + //Set the First Sample Flag. this will get reset when the first sample comes in. We will signal the discontinuity + //when we get a Processoutput from the Device Transform Manager + // + pIoPin->SetFirstSample(TRUE); + + } + pOutMediatype = nullptr; + } + + } + +done: + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! exiting %x = %!HRESULT!", hr, hr); + return hr; +} + +STDMETHODIMP CMultipinMft::SendEventToManager( + _In_ MediaEventType eventType, + _In_ REFGUID pGuid, + _In_ UINT32 context + ) + /*++ + Description: + Used to send the event to DTM. + --*/ + { + HRESULT hr = S_OK; + ComPtr pEvent = nullptr; + + DMFTCHECKHR_GOTO(MFCreateMediaEvent(eventType, pGuid, S_OK, NULL, &pEvent ),done); + DMFTCHECKHR_GOTO(pEvent->SetUINT32(MF_EVENT_MFT_INPUT_STREAM_ID, (ULONG)context),done); + DMFTCHECKHR_GOTO(QueueEvent(pEvent.Get()),done); + done: + + return hr; + } + + +STDMETHODIMP CMultipinMft::GetConnectOutPinStatus( + _In_ ULONG ulPinId, + _In_ ULONG ulOutPinId, + _Inout_ PBOOL pAnyInPauseState, + _Inout_ PBOOL pAnyInRunState, + _Inout_ PBOOL pAnyActive + ) + /*++ + Description: + + This function gets the statuses of the output pins other than the one specified + The arguments are as follows + ulPinId: The input pins which are connected to the ouput pin ulOutPinId + ulOutPinId: The pin to be excluded from the check i.e. the input pin from which the request is usually sent. + --*/ +{ + HRESULT hr = S_OK; + MMFTMMAPITERATOR outputPinPos; + CInPin *pconnectedInPin = (CInPin*)GetInPin(ulPinId); + DMFTCHECKNULL_GOTO( pconnectedInPin, done, E_INVALIDARG ); + + outputPinPos = m_inputPinMap.equal_range( pconnectedInPin->streamId() ); + DMFTCHECKNULL_GOTO( pconnectedInPin, done, E_FAIL ); + + *pAnyActive = *pAnyInPauseState = *pAnyInRunState = false; + + for ( std::multimap::iterator poutPosIterator = outputPinPos.first; + poutPosIterator != outputPinPos.second; + poutPosIterator++ ) + { + ULONG connectedoutPin = (*poutPosIterator).second; + COutPin* pconnectedOutPin = ( COutPin * )GetOutPin( connectedoutPin ); + + if (pconnectedOutPin->streamId() != ulOutPinId) + { + //This is excluding the outpin which requested pin state change + *pAnyActive |= !IsPinStateInActive( pconnectedOutPin->GetState() ); + *pAnyInPauseState |= ( pconnectedOutPin->GetState() == DeviceStreamState_Pause ); + *pAnyInRunState |= ( pconnectedOutPin->GetState() == DeviceStreamState_Run ); + } + + } + done: + return S_OK; +} + + +/*++ +Description: +This function connects the input and output pins. +Any media type filtering can happen here +--*/ +STDMETHODIMP CMultipinMft::BridgeInputPinOutputPin( + _In_ CInPin* piPin, + _In_ COutPin* poPin + ) +{ + HRESULT hr = S_OK; + ULONG ulIndex = 0; + ComPtr pMediaType = nullptr; + + DMFTCHECKNULL_GOTO( piPin, done, E_INVALIDARG ); + DMFTCHECKNULL_GOTO( poPin, done, E_INVALIDARG ); + // + //Copy over the media types from input pin to output pin. Since there is no + //decoder support, only the uncompressed media types are inserted + // + while ( SUCCEEDED( hr = piPin->GetMediaTypeAt( ulIndex++, &pMediaType ))) + { + GUID subType = GUID_NULL; + DMFTCHECKHR_GOTO( pMediaType->GetGUID(MF_MT_SUBTYPE,&subType), done ); + + if ( IsKnownUncompressedVideoType( subType ) ) + { + DMFTCHECKHR_GOTO( poPin->AddMediaType(NULL, pMediaType.Get() ), done ); + } + + pMediaType = nullptr; + } + // + //Add the Input Pin to the output Pin + // + DMFTCHECKHR_GOTO( poPin->AddPin(piPin->streamId()), done ); + // + //Add the output pin to the input pin. + // + piPin->ConnectPin( poPin ); + + // + //Create the map. This will be useful when we have to decide the state transitions of the pins + // + m_inputPinMap.insert ( std::pair< int,int >( piPin->streamId(), poPin->streamId()) ); + m_outputPinMap.insert ( std::pair< int, int >( poPin->streamId(), piPin->streamId()) ); +done: + // + //Failed adding media types + // + return hr; +} + + +// +//The below routines are used to implement the extended controls needed to implement the photo sequence +//The photo sequence is enabled for cameras with no independent image pins +//The extended controls needed to enable photo sequence are discussed in detail in the photo sequence document +// + +/*++ +Extended Photo Mode handler is the extended property handler dealing with the photosequence and single mode capabilities of the camera +--*/ +STDMETHODIMP CMultipinMft::ExtendedPhotoModeHandler( +_In_ PKSPROPERTY Property, +_In_ ULONG ulPropertyLength, +_In_ LPVOID pData, +_In_ ULONG ulOutputBufferLength, +_Inout_ PULONG pulBytesReturned +) + +{ + HRESULT hr = S_OK; + UNREFERENCED_PARAMETER( ulPropertyLength ); + + if ( Property->Flags & KSPROPERTY_TYPE_SET ) + { + if ( ulOutputBufferLength == 0 ) + { + *pulBytesReturned = sizeof( KSCAMERA_EXTENDEDPROP_HEADER )+sizeof( KSCAMERA_EXTENDEDPROP_PHOTOMODE ); + DMFTCHECKHR_GOTO( HRESULT_FROM_WIN32( ERROR_MORE_DATA ), done ); + } + else if ( ulOutputBufferLength < sizeof( KSCAMERA_EXTENDEDPROP_HEADER )+sizeof( KSCAMERA_EXTENDEDPROP_PHOTOMODE ) ) + { + DMFTCHECKHR_GOTO( HRESULT_FROM_WIN32( ERROR_MORE_DATA ), done ); + } + else if ( pData && ulOutputBufferLength >= sizeof( KSCAMERA_EXTENDEDPROP_HEADER )+sizeof( KSCAMERA_EXTENDEDPROP_PHOTOMODE ) ) + { + PBYTE pPayload = ( PBYTE )pData; + PKSCAMERA_EXTENDEDPROP_HEADER pExtendedHeader = ( PKSCAMERA_EXTENDEDPROP_HEADER )( pPayload ); + // + //Use the below structure to make changes to the Property and thus affect the configuration + //PKSCAMERA_EXTENDEDPROP_PHOTOMODE pExtendedValue = (PKSCAMERA_EXTENDEDPROP_PHOTOMODE)(pPayload + sizeof(KSCAMERA_EXTENDEDPROP_HEADER)); + // + m_FilterInPhotoSequence = pExtendedHeader->Flags & KSCAMERA_EXTENDEDPROP_PHOTOMODE_SEQUENCE; + SetEvent( m_AsyncPropEvent ); + } + else + { + DMFTCHECKHR_GOTO(E_INVALIDARG, done); + } + } + else if ( Property->Flags & KSPROPERTY_TYPE_GET ) + { + if ( ulOutputBufferLength == 0 ) + { + *pulBytesReturned = sizeof( KSCAMERA_EXTENDEDPROP_HEADER )+sizeof( KSCAMERA_EXTENDEDPROP_PHOTOMODE ); + hr = HRESULT_FROM_WIN32(ERROR_MORE_DATA); + } + else if ( pData && ulOutputBufferLength >= sizeof( KSCAMERA_EXTENDEDPROP_HEADER )+sizeof( KSCAMERA_EXTENDEDPROP_PHOTOMODE ) ) + { + PBYTE pPayload = (PBYTE)pData; + PKSCAMERA_EXTENDEDPROP_HEADER pExtendedHeader = ( PKSCAMERA_EXTENDEDPROP_HEADER )( pPayload ); + PKSCAMERA_EXTENDEDPROP_PHOTOMODE pExtendedValue = ( PKSCAMERA_EXTENDEDPROP_PHOTOMODE )( pPayload + sizeof( KSCAMERA_EXTENDEDPROP_HEADER ) ); + + pExtendedHeader->Capability = ( KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL | KSCAMERA_EXTENDEDPROP_PHOTOMODE_SEQUENCE ); + pExtendedHeader->Flags = ( m_FilterInPhotoSequence ) ? KSCAMERA_EXTENDEDPROP_PHOTOMODE_SEQUENCE : KSCAMERA_EXTENDEDPROP_PHOTOMODE_NORMAL; + pExtendedHeader->Result = 0; + pExtendedHeader->Size = sizeof( KSCAMERA_EXTENDEDPROP_HEADER )+sizeof( KSCAMERA_EXTENDEDPROP_PHOTOMODE ); + pExtendedHeader->Version = 1; + + pExtendedValue->MaxHistoryFrames = 10; + pExtendedValue->RequestedHistoryFrames = 0 ; + pExtendedValue->SubMode = 0 ; + *pulBytesReturned = sizeof( KSCAMERA_EXTENDEDPROP_HEADER )+sizeof( KSCAMERA_EXTENDEDPROP_PHOTOMODE ); + } + else + { + DMFTCHECKHR_GOTO( E_INVALIDARG, done ); + } + } + +done: + DMFTRACE( DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! exiting %x = %!HRESULT!", hr, hr); + + return hr; +} + +/*++ +The Extended Max Frame rate is self explanatory +--*/ +STDMETHODIMP CMultipinMft::ExtendedPhotoMaxFrameRate( + _In_ PKSPROPERTY Property, + _In_ ULONG ulPropertyLength, + _In_ LPVOID pData, + _In_ ULONG ulOutputBufferLength, + _Inout_ PULONG pulBytesReturned + ) + +{ + HRESULT hr = S_OK; + UNREFERENCED_PARAMETER( ulPropertyLength ); + + if ( Property->Flags & KSPROPERTY_TYPE_SET ) + { + if ( ulOutputBufferLength == 0 ) + { + *pulBytesReturned = sizeof( KSCAMERA_EXTENDEDPROP_HEADER )+sizeof( KSCAMERA_EXTENDEDPROP_VALUE ); + DMFTCHECKHR_GOTO(HRESULT_FROM_WIN32(ERROR_MORE_DATA), done); + } + else if (ulOutputBufferLength < sizeof(KSCAMERA_EXTENDEDPROP_HEADER)+sizeof(KSCAMERA_EXTENDEDPROP_VALUE)) + { + DMFTCHECKHR_GOTO(HRESULT_FROM_WIN32(ERROR_MORE_DATA), done); + } + else if (pData && ulOutputBufferLength >= sizeof(KSCAMERA_EXTENDEDPROP_HEADER)+sizeof(KSCAMERA_EXTENDEDPROP_VALUE)) + { + // + //This is for setting the Max frame rate.. + // + //PBYTE pPayload = (PBYTE)pData; + //PKSCAMERA_EXTENDEDPROP_HEADER pExtendedHeader = (PKSCAMERA_EXTENDEDPROP_HEADER)(pPayload); + //PKSCAMERA_EXTENDEDPROP_VALUE pExtendedValue = (PKSCAMERA_EXTENDEDPROP_VALUE)(pPayload + sizeof(KSCAMERA_EXTENDEDPROP_HEADER)); + // + SetEvent(m_AsyncPropEvent); + } + else + { + DMFTCHECKHR_GOTO(E_INVALIDARG, done); + } + } + else if ( Property->Flags & KSPROPERTY_TYPE_GET ) + { + if ( ulOutputBufferLength == 0 ) + { + *pulBytesReturned = sizeof( KSCAMERA_EXTENDEDPROP_HEADER )+sizeof( KSCAMERA_EXTENDEDPROP_VALUE ); + hr = HRESULT_FROM_WIN32( ERROR_MORE_DATA ); + } + else if ( pData && ulOutputBufferLength >= sizeof( KSCAMERA_EXTENDEDPROP_HEADER )+sizeof( KSCAMERA_EXTENDEDPROP_VALUE )) + { + PBYTE pPayload = ( PBYTE )pData; + PKSCAMERA_EXTENDEDPROP_HEADER pExtendedHeader = ( PKSCAMERA_EXTENDEDPROP_HEADER )( pPayload ); + PKSCAMERA_EXTENDEDPROP_VALUE pExtendedValue = ( PKSCAMERA_EXTENDEDPROP_VALUE )( pPayload + sizeof( KSCAMERA_EXTENDEDPROP_HEADER ) ); + pExtendedHeader->Capability = KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL; + pExtendedHeader->Flags = 0; + pExtendedHeader->Result = 0; + pExtendedHeader->Size = sizeof(KSCAMERA_EXTENDEDPROP_HEADER)+sizeof(KSCAMERA_EXTENDEDPROP_VALUE); + pExtendedHeader->Version = 1; + pExtendedValue->Value.ratio.HighPart = 30; + pExtendedValue->Value.ratio.LowPart = 1; + *pulBytesReturned = sizeof(KSCAMERA_EXTENDEDPROP_HEADER)+sizeof(KSCAMERA_EXTENDEDPROP_VALUE); + } + else + { + DMFTCHECKHR_GOTO(E_INVALIDARG, done); + } + } + +done: + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! exiting %x = %!HRESULT!", hr, hr); + + return hr; +} + + +STDMETHODIMP CMultipinMft::MaxVidFPS_PhotoResHandler( + _In_ PKSPROPERTY Property, + _In_ ULONG ulPropertyLength, + _In_ LPVOID pData, + _In_ ULONG ulOutputBufferLength, + _Inout_ PULONG pulBytesReturned + ) +{ + HRESULT hr = S_OK; + UNREFERENCED_PARAMETER( ulPropertyLength ); + if ( Property->Flags & KSPROPERTY_TYPE_SET ) + { + if ( ulOutputBufferLength == 0 ) + { + *pulBytesReturned = sizeof( KSCAMERA_EXTENDEDPROP_HEADER )+ sizeof( KSCAMERA_MAXVIDEOFPS_FORPHOTORES ); + DMFTCHECKHR_GOTO(HRESULT_FROM_WIN32(ERROR_MORE_DATA), done); + } + else if ( ulOutputBufferLength < sizeof( KSCAMERA_EXTENDEDPROP_HEADER )+ sizeof( KSCAMERA_MAXVIDEOFPS_FORPHOTORES ) ) + { + DMFTCHECKHR_GOTO( HRESULT_FROM_WIN32( ERROR_MORE_DATA ), done ); + } + else if (pData && ulOutputBufferLength >= sizeof( KSCAMERA_EXTENDEDPROP_HEADER )+sizeof( KSCAMERA_MAXVIDEOFPS_FORPHOTORES )) + { + PBYTE pPayload = (PBYTE)pData; + PKSCAMERA_EXTENDEDPROP_HEADER pExtendedHeader = ( PKSCAMERA_EXTENDEDPROP_HEADER )( pPayload ); + // + //Use the extended value to make changes to the property.. refer documentation + //PKSCAMERA_MAXVIDEOFPS_FORPHOTORES pExtendedValue = (PKSCAMERA_MAXVIDEOFPS_FORPHOTORES)(pPayload + sizeof(KSCAMERA_EXTENDEDPROP_HEADER)); + // + pExtendedHeader->Capability = 0; + pExtendedHeader->Flags = 0; + pExtendedHeader->Result = 0; + pExtendedHeader->Size = sizeof( KSCAMERA_EXTENDEDPROP_HEADER )+sizeof( KSCAMERA_EXTENDEDPROP_VALUE ); + pExtendedHeader->Version = 1; + + *pulBytesReturned = sizeof( KSCAMERA_EXTENDEDPROP_HEADER )+sizeof( KSCAMERA_MAXVIDEOFPS_FORPHOTORES ); + } + + else + { + hr = E_INVALIDARG; + } + } + else if (Property->Flags & KSPROPERTY_TYPE_GET) + { + if (ulOutputBufferLength == 0) + { + *pulBytesReturned = sizeof(KSCAMERA_EXTENDEDPROP_HEADER)+sizeof(KSCAMERA_MAXVIDEOFPS_FORPHOTORES); + DMFTCHECKHR_GOTO(HRESULT_FROM_WIN32(ERROR_MORE_DATA), done); + } + else if (ulOutputBufferLength < sizeof(KSCAMERA_EXTENDEDPROP_HEADER)+sizeof(KSCAMERA_MAXVIDEOFPS_FORPHOTORES)) + { + DMFTCHECKHR_GOTO(HRESULT_FROM_WIN32(ERROR_MORE_DATA), done); + } + else if (pData && ulOutputBufferLength >= sizeof(KSCAMERA_EXTENDEDPROP_HEADER)+sizeof(KSCAMERA_MAXVIDEOFPS_FORPHOTORES)) + { + PBYTE pPayload = (PBYTE)pData; + PKSCAMERA_EXTENDEDPROP_HEADER pExtendedHeader = ( PKSCAMERA_EXTENDEDPROP_HEADER )(pPayload ); + PKSCAMERA_MAXVIDEOFPS_FORPHOTORES pExtendedValue = (PKSCAMERA_MAXVIDEOFPS_FORPHOTORES)(pPayload + sizeof(KSCAMERA_EXTENDEDPROP_HEADER)); + pExtendedHeader->Capability = 0; + pExtendedHeader->Flags = 0; + pExtendedHeader->Result = 0; + pExtendedHeader->Size = sizeof(KSCAMERA_EXTENDEDPROP_HEADER) + sizeof(PKSCAMERA_MAXVIDEOFPS_FORPHOTORES); + pExtendedHeader->Version = 1; + + pExtendedValue->PreviewFPSNum = 30; + pExtendedValue->PreviewFPSDenom = 1; + pExtendedValue->CaptureFPSNum = 30; + pExtendedValue->CaptureFPSDenom = 1; + pExtendedValue->PhotoResHeight = 240; + pExtendedValue->PhotoResWidth = 320; + + *pulBytesReturned = sizeof(KSCAMERA_EXTENDEDPROP_HEADER) + sizeof(KSCAMERA_MAXVIDEOFPS_FORPHOTORES); + } + else + { + hr = E_INVALIDARG; + } + } +done: + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! exiting %x = %!HRESULT!", hr, hr); + return hr; +} + + +STDMETHODIMP CMultipinMft::QPCTimeHandler( + _In_ PKSPROPERTY Property, + _In_ ULONG ulPropertyLength, + _In_ LPVOID pData, + _In_ ULONG ulOutputBufferLength, + _Inout_ PULONG pulBytesReturned + ) +{ + HRESULT hr = S_OK; + UNREFERENCED_PARAMETER( ulPropertyLength ); + + if ( Property->Flags & KSPROPERTY_TYPE_SET ) + { + if ( ulOutputBufferLength == 0 ) + { + *pulBytesReturned = sizeof( KSCAMERA_EXTENDEDPROP_HEADER )+sizeof( KSCAMERA_EXTENDEDPROP_VALUE ); + DMFTCHECKHR_GOTO( HRESULT_FROM_WIN32( ERROR_MORE_DATA ), done ); + } + else if ( ulOutputBufferLength < sizeof( KSCAMERA_EXTENDEDPROP_HEADER )+sizeof( KSCAMERA_EXTENDEDPROP_VALUE ) ) + { + DMFTCHECKHR_GOTO(HRESULT_FROM_WIN32(ERROR_MORE_DATA), done); + } + else if ( pData && ulOutputBufferLength >= sizeof( KSCAMERA_EXTENDEDPROP_HEADER )+sizeof( KSCAMERA_EXTENDEDPROP_VALUE ) ) + { + //PBYTE pPayload = (PBYTE)pData; + // + //If the payload is to be used.. use the below structures + // + //PKSCAMERA_EXTENDEDPROP_HEADER pExtendedHeader = (PKSCAMERA_EXTENDEDPROP_HEADER)(pPayload); + //Use the extended value to make changes to the property.. refer documentation + //PKSCAMERA_EXTENDEDPROP_VALUE pExtendedValue = (PKSCAMERA_EXTENDEDPROP_VALUE)(pPayload +sizeof(KSCAMERA_EXTENDEDPROP_HEADER)); + // + *pulBytesReturned = sizeof( PKSCAMERA_EXTENDEDPROP_HEADER )+sizeof( KSCAMERA_EXTENDEDPROP_VALUE ); + } + else + { + hr = E_INVALIDARG; + } + } + else if ( Property->Flags & KSPROPERTY_TYPE_GET ) + { + if ( ulOutputBufferLength == 0 ) + { + *pulBytesReturned = sizeof( KSCAMERA_EXTENDEDPROP_HEADER )+sizeof( KSCAMERA_EXTENDEDPROP_VALUE ); + DMFTCHECKHR_GOTO(HRESULT_FROM_WIN32(ERROR_MORE_DATA), done); + } + else if ( ulOutputBufferLength < sizeof( KSCAMERA_EXTENDEDPROP_HEADER ) + sizeof( KSCAMERA_EXTENDEDPROP_VALUE ) ) + { + DMFTCHECKHR_GOTO( HRESULT_FROM_WIN32( ERROR_MORE_DATA ), done ); + } + else if ( pData && ulOutputBufferLength >= sizeof( KSCAMERA_EXTENDEDPROP_HEADER ) + sizeof( KSCAMERA_EXTENDEDPROP_VALUE ) ) + { + PBYTE pPayload = (PBYTE)pData; + PKSCAMERA_EXTENDEDPROP_HEADER pExtendedHeader = ( PKSCAMERA_EXTENDEDPROP_HEADER )( pPayload ); + PKSCAMERA_EXTENDEDPROP_VALUE pExtendedValue = ( PKSCAMERA_EXTENDEDPROP_VALUE )( pPayload + sizeof( KSCAMERA_EXTENDEDPROP_HEADER ) ); + pExtendedHeader->Capability = 0; + pExtendedHeader->Flags = KSPROPERTY_CAMERA_PHOTOTRIGGERTIME_SET; + pExtendedHeader->Result = 0; + pExtendedHeader->Size = sizeof( KSCAMERA_EXTENDEDPROP_HEADER ) + sizeof( KSCAMERA_EXTENDEDPROP_VALUE ); + pExtendedHeader->Version = 1; + pExtendedValue->Value.ull = 0; + *pulBytesReturned = sizeof( KSCAMERA_EXTENDEDPROP_HEADER )+ sizeof( KSCAMERA_EXTENDEDPROP_VALUE ); + } + else + { + hr = E_INVALIDARG; + } + } +done: + DMFTRACE( DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! exiting %x = %!HRESULT!", hr, hr ); + return hr; +} + +STDMETHODIMP CMultipinMft::PhotoFrameRateHandler( + _In_ PKSPROPERTY Property, + _In_ ULONG ulPropertyLength, + _In_ LPVOID pData, + _In_ ULONG ulOutputBufferLength, + _Inout_ PULONG pulBytesReturned + ) +{ + HRESULT hr = S_OK; + + UNREFERENCED_PARAMETER( ulPropertyLength ); + if ( Property->Flags & KSPROPERTY_TYPE_SET ) + { + // + //This is a read only property!!! + // + hr = E_INVALIDARG; + } + else if ( Property->Flags & KSPROPERTY_TYPE_GET ) + { + if ( ulOutputBufferLength < sizeof( KSCAMERA_EXTENDEDPROP_HEADER )+sizeof( KSCAMERA_EXTENDEDPROP_VALUE )) + { + *pulBytesReturned = sizeof( KSCAMERA_EXTENDEDPROP_HEADER )+sizeof( KSCAMERA_EXTENDEDPROP_VALUE ); + DMFTCHECKHR_GOTO( HRESULT_FROM_WIN32( ERROR_MORE_DATA ), done ); + } + PBYTE pPayload = (PBYTE)pData; + PKSCAMERA_EXTENDEDPROP_HEADER pExtendedHeader = ( PKSCAMERA_EXTENDEDPROP_HEADER )( pPayload ); + PKSCAMERA_EXTENDEDPROP_VALUE pExtendedValue = ( PKSCAMERA_EXTENDEDPROP_VALUE )( pPayload + sizeof( KSCAMERA_EXTENDEDPROP_HEADER ) ); + pExtendedHeader->Capability = 0; + pExtendedHeader->Flags = 0; + pExtendedHeader->Result = 0; + pExtendedHeader->Size = sizeof( KSCAMERA_EXTENDEDPROP_HEADER )+sizeof( KSCAMERA_EXTENDEDPROP_VALUE ); + pExtendedHeader->Version = 1; + pExtendedValue->Value.ratio.HighPart = 30; + pExtendedValue->Value.ratio.LowPart = 1; + *pulBytesReturned = sizeof( KSCAMERA_EXTENDEDPROP_HEADER )+sizeof( KSCAMERA_EXTENDEDPROP_VALUE ); + + } +done: + DMFTRACE( DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! exiting %x = %!HRESULT!", hr, hr ); + return hr; +} + + +STDMETHODIMP CMultipinMft::WarmStartHandler( + _In_ PKSPROPERTY Property, + _In_ ULONG ulPropertyLength, + _In_ LPVOID pData, + _In_ ULONG ulOutputBufferLength, + _Inout_ PULONG pulBytesReturned + ) +{ + + HRESULT hr = S_OK; + UNREFERENCED_PARAMETER( ulPropertyLength ); + *pulBytesReturned = 0; + + if ( Property->Flags & KSPROPERTY_TYPE_SET ) + { + if ( ulOutputBufferLength == 0 ) + { + *pulBytesReturned = sizeof( KSCAMERA_EXTENDEDPROP_HEADER )+sizeof( KSCAMERA_EXTENDEDPROP_VALUE ); + DMFTCHECKHR_GOTO( HRESULT_FROM_WIN32( ERROR_MORE_DATA ), done); + } + else if (ulOutputBufferLength < sizeof( KSCAMERA_EXTENDEDPROP_HEADER )+sizeof( KSCAMERA_EXTENDEDPROP_VALUE )) + { + DMFTCHECKHR_GOTO( HRESULT_FROM_WIN32( ERROR_MORE_DATA ), done); + } + else if ( pData && ulOutputBufferLength >= sizeof( KSCAMERA_EXTENDEDPROP_HEADER )+sizeof( KSCAMERA_EXTENDEDPROP_VALUE )) + { + PBYTE pPayload = ( PBYTE )pData; + PKSCAMERA_EXTENDEDPROP_HEADER pExtendedHeader = ( PKSCAMERA_EXTENDEDPROP_HEADER )pPayload; + // + //Use the extended value to make changes to the property.. refer documentation + //PKSCAMERA_EXTENDEDPROP_VALUE pExtendedValue = (PKSCAMERA_EXTENDEDPROP_VALUE)(pPayload + sizeof(KSCAMERA_EXTENDEDPROP_HEADER)); + // + if ( pExtendedHeader->Flags & KSCAMERA_EXTENDEDPROP_WARMSTART_MODE_ENABLED ) + { + m_filterInWarmStart = true; + } + else + { + m_filterInWarmStart = false; + } + + *pulBytesReturned = sizeof( PKSCAMERA_EXTENDEDPROP_HEADER )+sizeof( KSCAMERA_EXTENDEDPROP_VALUE ); + + SetEvent( m_AsyncPropEvent ); + } + else + { + hr = S_OK; + } + } + else if (Property->Flags & KSPROPERTY_TYPE_GET) + { + if (ulOutputBufferLength == 0) + { + *pulBytesReturned = sizeof( KSCAMERA_EXTENDEDPROP_HEADER )+sizeof( KSCAMERA_EXTENDEDPROP_VALUE ); + DMFTCHECKHR_GOTO(HRESULT_FROM_WIN32(ERROR_MORE_DATA), done); + } + else if (ulOutputBufferLength < sizeof( KSCAMERA_EXTENDEDPROP_HEADER )+sizeof( KSCAMERA_EXTENDEDPROP_VALUE )) + { + DMFTCHECKHR_GOTO( HRESULT_FROM_WIN32( ERROR_MORE_DATA ), done ); + } + else if (pData && ulOutputBufferLength >= sizeof( KSCAMERA_EXTENDEDPROP_HEADER )+sizeof( KSCAMERA_EXTENDEDPROP_VALUE )) + { + PBYTE pPayload = ( PBYTE )pData; + PKSCAMERA_EXTENDEDPROP_HEADER pExtendedHeader = ( PKSCAMERA_EXTENDEDPROP_HEADER )( pPayload ); + // + //Use the extended value to make changes to the property.. refer documentation + //PKSCAMERA_EXTENDEDPROP_VALUE pExtendedValue = (PKSCAMERA_EXTENDEDPROP_VALUE)(pPayload +sizeof(KSCAMERA_EXTENDEDPROP_HEADER)); + // + pExtendedHeader->Capability = KSCAMERA_EXTENDEDPROP_CAPS_ASYNCCONTROL | KSCAMERA_EXTENDEDPROP_WARMSTART_MODE_ENABLED; + pExtendedHeader->Flags = 0; + + if (m_filterInWarmStart) + { + pExtendedHeader->Flags |= KSCAMERA_EXTENDEDPROP_WARMSTART_MODE_ENABLED; + } + + pExtendedHeader->Result = 0; + pExtendedHeader->Size = sizeof( KSCAMERA_EXTENDEDPROP_HEADER )+sizeof( KSCAMERA_EXTENDEDPROP_VALUE ); + pExtendedHeader->Version = 1; + *pulBytesReturned = sizeof( KSCAMERA_EXTENDEDPROP_HEADER )+sizeof( KSCAMERA_EXTENDEDPROP_VALUE ); + hr = S_OK; + } + else + { + hr = S_OK; + } + } +done: + DMFTRACE( DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! exiting %x = %!HRESULT!", hr, hr ); + return hr; +} + + +// +// IMFShutdown interface functions +// + +/*++ +Description: +Implements the Shutdown from IMFShutdown +--*/ +STDMETHODIMP CMultipinMft::Shutdown( + void + ) +{ + return ShutdownEventGenerator(); +} + +// +// Static method to create an instance of the MFT. +// +HRESULT CMultipinMft::CreateInstance(REFIID iid, void **ppMFT) +{ + HRESULT hr = S_OK; + CMultipinMft *pMFT = NULL; + DMFTCHECKNULL_GOTO(ppMFT, done, E_POINTER); + pMFT = new (std::nothrow) CMultipinMft(); + DMFTCHECKNULL_GOTO(pMFT, done, E_OUTOFMEMORY); + + DMFTCHECKHR_GOTO(pMFT->QueryInterface(iid, ppMFT), done); + +done: + if (FAILED(hr)) + { + SAFERELEASE(pMFT); + } + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! exiting %x = %!HRESULT!", hr, hr); + return hr; +} + + + + + +#if defined (MF_DEVICEMFT_PHTOTOCONFIRMATION) +/* +Desciption: +This function will be called by the preview pin to execute the photo confirmation stored with the +MFT. +*/ + +STDMETHODIMP CMultipinMft::ProcessCapturePhotoConfirmationCallBack( + _In_ IMFMediaType* pMediaType, + _In_ IMFSample* pSample + ) +{ + // + //PhotoConfirmation Implementation + //Note this function doesn't scan the metadata as the pipeline does to find out which buffer is the photoconfirmation buffer + //The pipeline treats the preview buffer as the photo confirmation buffer and the driver marks the metadata on the buffer as being so. + //This example treats every buffer coming on the pin as the confirmation buffer. + // + + HRESULT hr = S_OK; + ComPtr spMediaType = nullptr; + LONGLONG timeStamp = 0; + + DMFTCHECKHR_GOTO(MFCreateMediaType(&spMediaType), done); + DMFTCHECKHR_GOTO(pMediaType->CopyAllItems(spMediaType.Get()), done); + + DMFTCHECKHR_GOTO(pSample->SetUnknown(MFSourceReader_SampleAttribute_MediaType, spMediaType.Get()), done); + + DMFTCHECKHR_GOTO(pSample->GetSampleTime(&timeStamp), done); + DMFTCHECKHR_GOTO(pSample->SetUINT64(MFSampleExtension_DeviceReferenceSystemTime, timeStamp), done); + + if (m_spPhotoConfirmationCallback) + { + + // + //We are directly sending the photo sample over to the consumers of the photoconfirmation interface. + // + ComPtr spResult; + DMFTCHECKHR_GOTO(MFCreateAsyncResult(pSample, m_spPhotoConfirmationCallback.Get(), NULL, &spResult), done); + DMFTCHECKHR_GOTO(MFInvokeCallback(spResult.Get()), done); + } +done: + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! exiting %x = %!HRESULT!", hr, hr); + return hr; +} + +#endif + +// +// Only worry about this if you have customs pins defined in the driver +// + +STDMETHODIMP CMultipinMft::SetStreamingStateCustomPins( + DeviceStreamState State + ) +{ + HRESULT hr = S_OK; + + if ( m_CustomPinCount > 0 ) + { + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! Custom Pin State changing to %d", State); + + for (ULONG ulIndex = 0; ulIndex < m_InPins.size(); ulIndex++) + { + BOOL isCustom = false; + CInPin* pInPin = static_cast(m_InPins[ulIndex]); + + if ( SUCCEEDED( CheckCustomPin(pInPin, &isCustom) ) + && ( isCustom ) ) + { + pInPin->SetState(State); + } + } + } + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! exiting %x = %!HRESULT!", hr, hr); + return hr; +} diff --git a/avstream/sampledevicemft/multipinmft.h b/avstream/sampledevicemft/multipinmft.h new file mode 100644 index 000000000..b67c4179c --- /dev/null +++ b/avstream/sampledevicemft/multipinmft.h @@ -0,0 +1,468 @@ +//*@@@+++@@@@****************************************************************** +// +// Microsoft Windows Media Foundation +// Copyright (C) Microsoft Corporation. All rights reserved. +// +//*@@@---@@@@****************************************************************** +// + +#pragma once + +#include "common.h" +#include "mftpeventgenerator.h" +#include "basepin.h" +#include "custompin.h" + + + +interface IDirect3DDeviceManager9; + +// +// Forward declarations +// +class CMFAttributes; + +// +// CMultipinMft class: +// Implements a device proxy MFT. +// +class CMultipinMft : + public IMFDeviceTransform +#if defined (MF_DEVICEMFT_ALLOW_MFT0_LOAD) && defined (MFT_UNIQUE_METHOD_NAMES) + , public IMFTransform +#endif + , public IMFShutdown + , public CMediaEventGenerator + , public IMFRealTimeClientEx + , public IKsControl +#if defined (MF_DEVICEMFT_PHTOTOCONFIRMATION) + , public IMFCapturePhotoConfirmation + , public IMFGetService +#endif +{ +public: + CMultipinMft( + void ); + + virtual ~CMultipinMft(); + + // + // IUnknown + // + STDMETHOD_(ULONG, AddRef)( + void ); + + STDMETHOD_(ULONG, Release)( + void ); + + STDMETHOD(QueryInterface)( + _In_ REFIID iid, + _COM_Outptr_ void** ppv); + + + // + // IMFDeviceTransform functions + // + STDMETHOD(GetStreamCount)( + _Inout_ DWORD *pdwInputStreams, + _Inout_ DWORD *pdwOutputStreams); + + + STDMETHOD(GetStreamIDs)( + _In_ DWORD dwInputIDArraySize, + _When_(dwInputIDArraySize >= m_InputPinCount, _Out_writes_(dwInputIDArraySize)) DWORD* pdwInputIDs, + _In_ DWORD dwOutputIDArraySize, + _When_(dwOutputIDArraySize >= m_OutputPinCount && (pdwInputIDs && (dwInputIDArraySize > 0)), + _Out_writes_(dwOutputIDArraySize)) _On_failure_(_Valid_) DWORD* pdwOutputIDs + ); + + STDMETHOD(GetInputStreamAttributes)( + _In_ DWORD dwInputStreamID, + _COM_Outptr_result_maybenull_ IMFAttributes** ppAttributes); + + STDMETHOD(GetOutputStreamAttributes)( + _In_ DWORD dwOutputStreamID, + _Out_ IMFAttributes** ppAttributes); + + STDMETHOD(GetInputAvailableType)( + _In_ DWORD dwInputStreamID, + _In_ DWORD dwTypeIndex, + _Out_ IMFMediaType** ppType); + + STDMETHOD(GetOutputAvailableType)( + _In_ DWORD dwOutputStreamID, + _In_ DWORD dwTypeIndex, + _Out_ IMFMediaType** ppMediaType); + + STDMETHOD(GetInputCurrentType)( + _In_ DWORD dwInputStreamID, + _COM_Outptr_result_maybenull_ IMFMediaType** ppMediaType); + + STDMETHOD(GetOutputCurrentType)( + _In_ DWORD dwOutputStreamID, + _Out_ IMFMediaType** ppMediaType); + + STDMETHOD(ProcessMessage)( + _In_ MFT_MESSAGE_TYPE eMessage, + _In_ ULONG_PTR ulParam ); + + STDMETHOD(ProcessEvent)( + _In_ DWORD dwInputStreamID, + _In_ IMFMediaEvent *pEvent); + + + STDMETHOD(ProcessInput)( + _In_ DWORD dwInputStreamID, + _In_ IMFSample* pSample, + _In_ DWORD dwFlags ); + + STDMETHOD(ProcessOutput)( + _In_ DWORD dwFlags, + _In_ DWORD cOutputBufferCount, + _Inout_updates_(cOutputBufferCount) MFT_OUTPUT_DATA_BUFFER *pOutputSamples, + _Out_ DWORD *pdwStatus ); + + // + // IMFRealTimeClientEx + // + STDMETHOD(RegisterThreadsEx)( + _Inout_ DWORD* pdwTaskIndex, + _In_ LPCWSTR wszClassName, + _In_ LONG lBasePriority ) + { + UNREFERENCED_PARAMETER(pdwTaskIndex); + UNREFERENCED_PARAMETER(wszClassName); + UNREFERENCED_PARAMETER(lBasePriority); + return S_OK; + } + + STDMETHOD(UnregisterThreads)() + { + return S_OK; + } + + STDMETHOD(SetWorkQueueEx)( + _In_ DWORD dwWorkQueueId, + _In_ LONG lWorkItemBasePriority ); + + // + // IMFShutdown + // + STDMETHOD(Shutdown)( + void ); + + STDMETHOD(GetShutdownStatus)( + MFSHUTDOWN_STATUS *pStatus) + { + UNREFERENCED_PARAMETER(pStatus); + return(m_eShutdownStatus); + }; + + // + // IMFDeviceTransform function declarations + // + STDMETHODIMP InitializeTransform( + _In_ IMFAttributes *pAttributes ); + + _Requires_no_locks_held_ + STDMETHODIMP SetInputStreamState( + _In_ DWORD dwStreamID, + _In_ IMFMediaType *pMediaType, + _In_ DeviceStreamState value, + _In_ DWORD dwFlags ); + + STDMETHODIMP GetInputStreamState( + _In_ DWORD dwStreamID, + _Out_ DeviceStreamState *value ); + + STDMETHODIMP SetOutputStreamState( + _In_ DWORD dwStreamID, + _In_ IMFMediaType *pMediaType, + _In_ DeviceStreamState value, + _In_ DWORD dwFlags ); + + STDMETHODIMP GetOutputStreamState( + _In_ DWORD dwStreamID, + _Out_ DeviceStreamState *value ); + + STDMETHODIMP GetInputStreamPreferredState( + _In_ DWORD dwStreamID, + _Inout_ DeviceStreamState *value, + _Outptr_opt_result_maybenull_ IMFMediaType **ppMediaType ); + + STDMETHODIMP FlushInputStream( + _In_ DWORD dwStreamIndex, + _In_ DWORD dwFlags ); + + STDMETHODIMP FlushOutputStream( + _In_ DWORD dwStreamIndex, + _In_ DWORD dwFlags ); + + STDMETHODIMP_(VOID) FlushAllStreams( + VOID + ); + + // + //IKSControl Inferface function declarations + // + STDMETHOD(KsEvent)( + _In_reads_bytes_(ulEventLength) PKSEVENT pEvent, + _In_ ULONG ulEventLength, + _Inout_updates_bytes_opt_(ulDataLength) LPVOID pEventData, + _In_ ULONG ulDataLength, + _Inout_ ULONG* pBytesReturned + ); + STDMETHOD(KsProperty)( + _In_reads_bytes_(ulPropertyLength) PKSPROPERTY pProperty, + _In_ ULONG ulPropertyLength, + _Inout_updates_bytes_(ulDataLength) LPVOID pPropertyData, + _In_ ULONG ulDataLength, + _Inout_ ULONG* pBytesReturned + ); + STDMETHOD(KsMethod)( + _In_reads_bytes_(ulPropertyLength) PKSMETHOD pProperty, + _In_ ULONG ulPropertyLength, + _Inout_updates_bytes_(ulDataLength) LPVOID pPropertyData, + _In_ ULONG ulDataLength, + _Inout_ ULONG* pBytesReturned + ); +#if defined (MF_DEVICEMFT_PHTOTOCONFIRMATION) + // + // The Below functions are needed for photoconfirmation + // + // + STDMETHOD(GetService)( + __in REFGUID guidService, + __in REFIID riid, + __deref_out LPVOID* ppvObject + ); + // + //IMFCapturePhotoConfirmation Inferface function declarations + // + STDMETHOD(SetPhotoConfirmationCallback)( + _In_ IMFAsyncCallback* pNotificationCallback + ); + STDMETHOD(SetPixelFormat)( + _In_ GUID subtype + ); + STDMETHOD(GetPixelFormat)( + _Out_ GUID* subtype + ); + + __inline BOOL IsPhotoConfirmationEnabled() + { + return (m_spPhotoConfirmationCallback != nullptr); + } + + STDMETHODIMP ProcessCapturePhotoConfirmationCallBack( + _In_ IMFMediaType* pMediaType, + _In_ IMFSample* pSample + ); + + __inline VOID SetPhotoConfirmationCallBack(_In_ IMFAsyncCallback *Callback) + { + m_spPhotoConfirmationCallback = Callback; + } + +#endif + + static STDMETHODIMP CreateInstance( + REFIID iid, void **ppMFT); + + // + //Below functions are used by the pins to enquire the state of the Filter for Photo sequence. + //A Trigger is a ksproperty sent to the filter. + // + + __inline VOID setPhotoTriggerSent(_In_ BOOL Set) + { + m_PhotoTriggerSent = Set; + } + __inline BOOL isPhotoTriggerSent() + { + return m_PhotoTriggerSent; + } + + __inline BOOL isPhotoModePhotoSequence() + { + return m_PhotoModeIsPhotoSequence; + } + __inline BOOL filterHasIndependentPin() + { + return m_filterHasIndependentPin; + } + + // + //Will be used from Pins to get the D3D manager once set!!! + // + __inline STDMETHODIMP_(VOID) GetD3DDeviceManager( + IUnknown** ppDeviceManagerUnk + ) + { + m_spDeviceManagerUnk.CopyTo( ppDeviceManagerUnk ); + } + + STDMETHODIMP SendEventToManager( + _In_ MediaEventType, + _In_ REFGUID, + _In_ UINT32 + ); + +protected: + + // + //Helper functions + // + + STDMETHODIMP SetStreamingStateCustomPins( + _In_ DeviceStreamState + ); + + STDMETHODIMP_(CBasePin*) GetInPin( + _In_ DWORD dwStreamID + ); + + STDMETHODIMP_(CBasePin*) GetOutPin( + _In_ DWORD dwStreamID + ); + + STDMETHODIMP ChangeMediaTypeEx( + _In_ ULONG pinId, + _In_opt_ IMFMediaType *pMediaType, + _In_ DeviceStreamState newState + ); + + STDMETHODIMP GetConnectOutPinStatus( + _In_ ULONG ulPinId, + _In_ ULONG ulOutPin, + _Inout_ PBOOL pAnyInPauseState, + _Inout_ PBOOL pAnyInRunState, + _Inout_ PBOOL pAnyActive + ); + STDMETHODIMP BridgeInputPinOutputPin( + _In_ CInPin* pInPin, + _In_ COutPin* pOutPin); + + // + //Implementing the below to simulate a photosequence + //This is just to show that photosequence can be achieved purely in + //the user mode with the device MFT.. + // + STDMETHODIMP ExtendedPhotoModeHandler( + _In_ PKSPROPERTY pProperty, + _In_ ULONG ulPropertyLength, + _In_ LPVOID pvPropertyData, + _In_ ULONG ulDataLength, + _Inout_ PULONG pulBytesReturned + ); + + STDMETHODIMP ExtendedPhotoMaxFrameRate( + _In_ PKSPROPERTY Property, + _In_ ULONG ulPropertyLength, + _In_ LPVOID pData, + _In_ ULONG ulOutputBufferLength, + _Inout_ PULONG pulBytesReturned + ); + STDMETHODIMP MaxVidFPS_PhotoResHandler( + _In_ PKSPROPERTY Property, + _In_ ULONG ulPropertyLength, + _In_ LPVOID pData, + _In_ ULONG ulOutputBufferLength, + _Inout_ PULONG pulBytesReturned + ); + STDMETHODIMP QPCTimeHandler( + _In_ PKSPROPERTY Property, + _In_ ULONG ulPropertyLength, + _In_ LPVOID pData, + _In_ ULONG ulOutputBufferLength, + _Inout_ PULONG pulBytesReturned + ); + STDMETHODIMP PhotoFrameRateHandler( + _In_ PKSPROPERTY Property, + _In_ ULONG ulPropertyLength, + _In_ LPVOID pData, + _In_ ULONG ulOutputBufferLength, + _Inout_ PULONG pulBytesReturned + ); + STDMETHODIMP CMultipinMft::WarmStartHandler( + _In_ PKSPROPERTY Property, + _In_ ULONG ulPropertyLength, + _In_ LPVOID pData, + _In_ ULONG ulOutputBufferLength, + _Inout_ PULONG pulBytesReturned + ); +#if defined (MF_DEVICEMFT_ALLOW_MFT0_LOAD) && defined (MFT_UNIQUE_METHOD_NAMES) + STDMETHODIMP CMultipinMft::GetAttributes( + _COM_Outptr_opt_result_maybenull_ IMFAttributes** ppAttributes + ); +#endif + +#if (defined (MF_DEVICEMFT_ALLOW_MFT0_LOAD) && defined (MFT_UNIQUE_METHOD_NAMES)) + _DEFINE_DEVICEMFT_MFT0HELPER_IMPL__ +#endif + + + // + //Inline functions + // + + __inline IMFTransform* Parent() + { + return m_pSourceTransform.Get(); + } + + __inline VOID SetStreamingState(DeviceStreamState state) + { + InterlockedExchange((LONG*)&m_StreamingState, state); + } + __inline DeviceStreamState GetStreamingState() + { + return (DeviceStreamState)InterlockedCompareExchange((LONG*)&m_StreamingState, m_StreamingState, m_StreamingState); + } + __inline BOOL IsStreaming() + { + return (InterlockedCompareExchange((LONG*)&m_StreamingState, DeviceStreamState_Run, DeviceStreamState_Run) == DeviceStreamState_Run); + } + +private: + ULONG m_InputPinCount; + ULONG m_OutputPinCount; + ULONG m_CustomPinCount; + DeviceStreamState m_StreamingState; + CBasePinArray m_OutPins; + CBasePinArray m_InPins; + BOOL m_PhotoTriggerSent; + BOOL m_PhotoModeIsPhotoSequence; // used to store if the filter is in photo sequence or not + BOOL m_FilterInPhotoSequence; // This applies for the filter, which is in a photo sequence or not. This is set by the Extended property handler + BOOL m_filterHasIndependentPin; // If any of the input pins from the source transform expose an independent photo pin then we will not simluate photoseqeunce + BOOL m_filterInWarmStart; // Used to store if the filter is in warm start or not! + long m_nRefCount; // Reference count + CCritSec m_critSec; // Control lock.. taken only durign state change operations + ComPtr m_spDeviceManagerUnk; // D3D Manager set, when MFT_MESSAGE_SET_D3D_MANAGER is called through ProcessMessage + ComPtr m_pSourceTransform; // The sources transform + MFSHUTDOWN_STATUS m_eShutdownStatus; + DWORD m_dwWorkQueueId; + LONG m_lWorkQueuePriority; + UINT32 m_punValue; + ComPtr m_ikscontrol; + ComPtr m_attributes; + + multimap m_inputPinMap; //How input pins are connected to output pins o-><0..inpins> + multimap m_outputPinMap; //How output pins are connected to input pins i-><0..outpins> + HANDLE m_AsyncPropEvent; + +#if defined (MF_DEVICEMFT_PHTOTOCONFIRMATION) + ComPtr m_spPhotoConfirmationCallback; //Photo Confirmation related definitions + GUID m_guidPhotoConfirmationSubtype; +#endif + +}; + + + +inline HRESULT MFT_CreateInstance(REFIID riid, void **ppv) +{ + return CMultipinMft::CreateInstance(riid, ppv); +} + + diff --git a/avstream/sampledevicemft/multipinmft.vcxproj b/avstream/sampledevicemft/multipinmft.vcxproj new file mode 100644 index 000000000..2493f0211 --- /dev/null +++ b/avstream/sampledevicemft/multipinmft.vcxproj @@ -0,0 +1,263 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {CD76DACF-18E8-4625-9A09-19B020579A56} + $(MSBuildProjectName) + false + Debug + Win32 + {6DBDE413-6AD0-40FA-ABE2-273BA6176E9E} + + + + Windows10 + False + Universal + + WindowsApplicationForDrivers10.0 + DynamicLibrary + + + Windows10 + True + Universal + + WindowsApplicationForDrivers10.0 + DynamicLibrary + + + Windows10 + False + Universal + + WindowsApplicationForDrivers10.0 + DynamicLibrary + + + Windows10 + True + Universal + + WindowsApplicationForDrivers10.0 + DynamicLibrary + + + + $(IntDir) + + + + + + + + + + + + + + + + true + true + DMFTRACE(FLAG,LEVEL,MSG,...) + {um-default.tpl}*.tmh + ;%(AdditionalIncludeDirectories) + stdafx.h + Use + $(IntDir)\stdafx.h.pch + + + true + true + DMFTRACE(FLAG,LEVEL,MSG,...) + {um-default.tpl}*.tmh + ;%(AdditionalIncludeDirectories) + stdafx.h + Use + $(IntDir)\stdafx.h.pch + + + true + true + DMFTRACE(FLAG,LEVEL,MSG,...) + {um-default.tpl}*.tmh + ;%(AdditionalIncludeDirectories) + stdafx.h + Use + $(IntDir)\stdafx.h.pch + + + true + true + DMFTRACE(FLAG,LEVEL,MSG,...) + {um-default.tpl}*.tmh + ;%(AdditionalIncludeDirectories) + stdafx.h + Use + $(IntDir)\stdafx.h.pch + + + true + true + DMFTRACE(FLAG,LEVEL,MSG,...) + {um-default.tpl}*.tmh + ;%(AdditionalIncludeDirectories) + stdafx.h + Use + $(IntDir)\stdafx.h.pch + + + true + true + DMFTRACE(FLAG,LEVEL,MSG,...) + {um-default.tpl}*.tmh + ;%(AdditionalIncludeDirectories) + stdafx.h + Use + $(IntDir)\stdafx.h.pch + + + true + true + DMFTRACE(FLAG,LEVEL,MSG,...) + {um-default.tpl}*.tmh + ;%(AdditionalIncludeDirectories) + stdafx.h + Use + $(IntDir)\stdafx.h.pch + + + + multipinmft + + + multipinmft + + + multipinmft + + + multipinmft + + + + Sync + + + + + Sync + + + + + Sync + + + + + Sync + + + + + %(PreprocessorDefinitions);UNICODE;MF_WPP;SECURITY_WIN32;MFT_UNIQUE_METHOD_NAMES;MF_DEVICEMFT_ALLOW_MFT0_LOAD + + + %(PreprocessorDefinitions);UNICODE;MF_WPP;SECURITY_WIN32;MFT_UNIQUE_METHOD_NAMES;MF_DEVICEMFT_ALLOW_MFT0_LOAD + + + %(PreprocessorDefinitions);UNICODE;MF_WPP;SECURITY_WIN32;MFT_UNIQUE_METHOD_NAMES;MF_DEVICEMFT_ALLOW_MFT0_LOAD + + + %(AdditionalDependencies);D2d1.lib;mf.lib;mfplat.lib;mfuuid.lib;uuid.lib + Source.def + + + + + %(PreprocessorDefinitions);UNICODE;MF_WPP;SECURITY_WIN32;MFT_UNIQUE_METHOD_NAMES;MF_DEVICEMFT_ALLOW_MFT0_LOAD + + + %(PreprocessorDefinitions);UNICODE;MF_WPP;SECURITY_WIN32;MFT_UNIQUE_METHOD_NAMES;MF_DEVICEMFT_ALLOW_MFT0_LOAD + + + %(PreprocessorDefinitions);UNICODE;MF_WPP;SECURITY_WIN32;MFT_UNIQUE_METHOD_NAMES;MF_DEVICEMFT_ALLOW_MFT0_LOAD + + + %(AdditionalDependencies);D2d1.lib;mf.lib;mfplat.lib;mfuuid.lib;uuid.lib + Source.def + + + + + %(PreprocessorDefinitions);UNICODE;MF_WPP;SECURITY_WIN32;MFT_UNIQUE_METHOD_NAMES;MF_DEVICEMFT_ALLOW_MFT0_LOAD + + + %(PreprocessorDefinitions);UNICODE;MF_WPP;SECURITY_WIN32;MFT_UNIQUE_METHOD_NAMES;MF_DEVICEMFT_ALLOW_MFT0_LOAD + + + %(PreprocessorDefinitions);UNICODE;MF_WPP;SECURITY_WIN32;MFT_UNIQUE_METHOD_NAMES;MF_DEVICEMFT_ALLOW_MFT0_LOAD + + + %(AdditionalDependencies);D2d1.lib;mf.lib;mfplat.lib;mfuuid.lib;uuid.lib + Source.def + + + + + %(PreprocessorDefinitions);UNICODE;MF_WPP;SECURITY_WIN32;MFT_UNIQUE_METHOD_NAMES;MF_DEVICEMFT_ALLOW_MFT0_LOAD + + + %(PreprocessorDefinitions);UNICODE;MF_WPP;SECURITY_WIN32;MFT_UNIQUE_METHOD_NAMES;MF_DEVICEMFT_ALLOW_MFT0_LOAD + + + %(PreprocessorDefinitions);UNICODE;MF_WPP;SECURITY_WIN32;MFT_UNIQUE_METHOD_NAMES;MF_DEVICEMFT_ALLOW_MFT0_LOAD + + + %(AdditionalDependencies);D2d1.lib;mf.lib;mfplat.lib;mfuuid.lib;uuid.lib + Source.def + + + + + ;%(AdditionalIncludeDirectories) + stdafx.h + Create + $(IntDir)\stdafx.h.pch + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/avstream/sampledevicemft/multipinmft.vcxproj.Filters b/avstream/sampledevicemft/multipinmft.vcxproj.Filters new file mode 100644 index 000000000..4cf74a604 --- /dev/null +++ b/avstream/sampledevicemft/multipinmft.vcxproj.Filters @@ -0,0 +1,46 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {94BAD99E-E9EC-451B-A064-3C99833E8EE5} + + + h;hpp;hxx;hm;inl;inc;xsd + {1308E3EE-C5B2-4417-BD87-A7116D7DD60D} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {2DCD9326-19A0-46C1-B32D-00012EB223F3} + + + + + Source Files + + + 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/avstream/sampledevicemft/multipinmfthelpers.cpp b/avstream/sampledevicemft/multipinmfthelpers.cpp new file mode 100644 index 000000000..f2f8fc8ac --- /dev/null +++ b/avstream/sampledevicemft/multipinmfthelpers.cpp @@ -0,0 +1,661 @@ +#include "stdafx.h" +#include "common.h" +#include "multipinmfthelpers.h" +#include "multipinmft.h" +#include "basepin.h" +#include + +#ifdef MF_WPP +#include "multipinmfthelpers.tmh" +#endif +class CMediaTypePrinter; +// +//Queue implementation +// + +CPinQueue::CPinQueue( _In_ DWORD _InpinId ) +: m_teer(0), + m_discotinuity(0), + m_sampleCount(0), + m_dwInPinId(_InpinId) + /* + Description + _InpinId is the input pin Id to which this queue corresponds + */ +{ +} +CPinQueue::~CPinQueue( ) +{ +} + +/*++ +Description: + Insert sample into the list once we reach the open queue +--*/ +STDMETHODIMP_(VOID) CPinQueue::InsertInternal( _In_ IMFSample *pSample ) +{ + pSample->AddRef(); + m_sampleList.push_back( pSample ); +} + +STDMETHODIMP_(BOOL) CPinQueue::Insert( _In_ IMFSample *pSample ) +// +//m_teer is the wraptee.. this could be a null tee which is a passthrough, an xvp tee, which inserts an xvp into the queue +// +{ + return SUCCEEDED( m_teer->PassThrough( pSample, this ) ); +} + + +/*++ +Description: + This extracts the first sample from the queue. called from Pin's ProcessOutput +--*/ + +STDMETHODIMP_(BOOL) CPinQueue::Remove( _Outptr_result_maybenull_ IMFSample **ppSample) +{ + HRESULT hr = S_OK; + DMFTCHECKNULL_GOTO( ppSample, done,E_INVALIDARG ); + *ppSample = nullptr; + + if ( !m_sampleList.empty() ) + { + *ppSample = m_sampleList.front(); + } + + DMFTCHECKNULL_GOTO( *ppSample, done, MF_E_TRANSFORM_NEED_MORE_INPUT ); + + (*ppSample)->Release( ); + + m_sampleList.erase( m_sampleList.begin() ); +done: + return SUCCEEDED( hr ) ? TRUE : FALSE; +} + +/*++ +Description: + Empties the Queue. used by the flush +--*/ +VOID CPinQueue::Clear( ) +{ + IMFSample* pSample = nullptr; + while ( !Empty() ) + { + Remove( &pSample ); + SAFE_RELEASE( pSample ); + } +} + +/*++ +Description: + RecreateTee creates the underlying Tees in the queue. It accepts the input media type + which is the media type set on the input pin and the output mediatype, which (duh) is the + media type on the output pin + It also takes an IUnknown which is the D3D Manager. if it is valid we might use it if we + have an xvp in the path i.e. inputMediatype =! outputMediatype +--*/ + +STDMETHODIMP CPinQueue::RecreateTee( _In_ IMFMediaType *inMediatype, + _In_ IMFMediaType *outMediatype, + _In_opt_ IUnknown* punkManager ) +{ + HRESULT hr = S_OK; + MF_TRANSFORM_XVP_OPERATION operation = DeviceMftTransformXVPIllegal; + + DMFTCHECKNULL_GOTO(inMediatype, done, E_INVALIDARG); + DMFTCHECKNULL_GOTO(outMediatype, done, E_INVALIDARG); + + SAFE_DELETE(m_teer); + + CNullTee *nulltee = new (std::nothrow) CNullTee(); + DMFTCHECKNULL_GOTO( nulltee, done, E_OUTOFMEMORY); + + DMFTCHECKHR_GOTO( CompareMediaTypesForXVP(inMediatype, outMediatype, &operation), done); + + if ( (operation != DeviceMftTransformXVPCurrent) && (operation!= DeviceMftTransformXVPIllegal) ) + { + CXvptee* pXvptee = new (std::nothrow) CXvptee(nulltee); + DMFTCHECKNULL_GOTO(pXvptee, done, E_OUTOFMEMORY); + (void)pXvptee->SetD3DManager( punkManager ); + (void)pXvptee->SetMediaTypes( inMediatype, outMediatype ); + m_teer = dynamic_cast< Ctee* >( pXvptee ); + } + else + { + m_teer = nulltee; /*A simple passthrough*/ + } + +done: + DMFTRACE( DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! exiting %x = %!HRESULT!", hr, hr ); + + if (FAILED(hr)) + { + if (m_teer){ + delete(m_teer); + m_teer = NULL; + } + + } + return hr; +} + +// +//State implementation +// +CPinOpenState::CPinOpenState(CBasePin *_pin) +:CPinState(DeviceStreamState_Run), m_pin(_pin) +{ + +} + + + +/*++ +Description: + This is the passthrough Tee in the literal sense i.e. It just dumps the sample + into the queue qupplied as an argument +--*/ +STDMETHODIMP CNullTee::PassThrough( _In_ IMFSample *pSample, _In_ CPinQueue *que ) +{ + HRESULT hr = S_OK; + + DMFTCHECKNULL_GOTO(pSample, done, S_OK); //No OP for a NULL Sample!!! + (VOID)que->InsertInternal(pSample); + + done: + return hr; +} + +/*++ +Description: + This function sets the Media types on the XVP or the decoder transform. Here is where we configure the XVP + or the Decoder or the Encoder tee if present. The first iteration only has the XVP added +--*/ +STDMETHODIMP CWrapTee::SetMediaTypes( _In_ IMFMediaType* pInMediaType, _In_ IMFMediaType* pOutMediaType ) +{ + HRESULT hr = S_OK; + IMFTransform *pTransform = nullptr; + + DMFTCHECKHR_GOTO(Configure( pInMediaType, pOutMediaType, &pTransform ),done); + m_videoProcessor = pTransform; + m_pInputMediaType = pInMediaType; + m_pOutputMediaType = pOutMediaType; + +done: + SAFE_RELEASE(pTransform); + return hr; +} + +/*++ +Description: +This is used when an XVP is present in the Pin. If the path is a passthrough i.e. +the media type on the input and the output pins are the same then we will not see +this path traversed. This function feeds the sample to the XVP or the decoding Tee +--*/ +STDMETHODIMP CWrapTee::PassThrough(_In_ IMFSample* pInSample, _In_ CPinQueue *pQue) +{ + HRESULT hr = S_OK; + IMFSample* pOutSample = nullptr; + + DMFTCHECKHR_GOTO(Do(pInSample, &pOutSample),done); + + if (m_objectWrapped) + { + hr = m_objectWrapped->PassThrough(pOutSample, pQue); + } + +done: + if (FAILED(hr)) + { + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! exiting %x = %!HRESULT!", hr, hr); + } + + return hr; +} + + +/*++ +CXvptee::Do +Description: +Called to do what an XVP is supposed to do i.e. scaling, resize etc. +This function is called when the samples are desposited from the input pin to the +output pin queue during a ProcessInput call on the device transform. Of course the +outpin should be in Open state for the sample to reach the XVP and consequetively the +Output Pin. + +If the DX manager is set on the Transform and we receieve a DX sample then we will +undertake the DX transform in the XVP else we will skip it and go the software way +--*/ +STDMETHODIMP CXvptee::Do(_In_ IMFSample *pSample, _Outptr_ IMFSample** pOutSample) +{ + // + //Since we are streaming in the software mode we will create a new sample + // + HRESULT hr = S_OK,pohr = S_OK; + MFT_OUTPUT_DATA_BUFFER outputSample; + MFT_OUTPUT_STREAM_INFO StreamInfo; + IMFSample* spXVPOutputSample = nullptr; + IMFMediaType* pOutMediaType = nullptr; + ComPtr spIMFMediaBuffer = nullptr; + GUID guidOutputSubType = GUID_NULL; + pOutMediaType = getOutMediaType(); + + DMFTCHECKNULL_GOTO(pOutSample, done, E_INVALIDARG); + + DMFTCHECKHR_GOTO(pOutMediaType->GetGUID(MF_MT_SUBTYPE, &guidOutputSubType), done); + + BOOL isDx = false; + + if (SUCCEEDED(IsInputDxSample(pSample, &isDx)) && isDx) + { + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! DX Sample sent to XVP %p", pSample); + } + + outputSample.dwStreamID = 0; + outputSample.pSample = NULL; + + if (!(isDx && m_spDeviceManagerUnk!=nullptr)) + { + // + //Create a new sample for software encoding + // + DMFTCHECKHR_GOTO(MFCreateSample(&spXVPOutputSample), done); + + DMFTCHECKHR_GOTO(pSample->CopyAllItems(spXVPOutputSample), done); + + outputSample.pSample = spXVPOutputSample; + + + DMFTCHECKHR_GOTO(Transform()->MFTGetOutputStreamInfo(0, &StreamInfo), done); + + if (!m_isOutPutImage) + { + DMFTCHECKHR_GOTO(MFGetAttributeSize(pOutMediaType, MF_MT_FRAME_SIZE, &m_uWidth, &m_uHeight), done); + hr = MFCreate2DMediaBuffer( + m_uWidth, + m_uHeight, + guidOutputSubType.Data1, + FALSE, // top-down buffer (DX compatible) + &spIMFMediaBuffer); + if (FAILED(hr)) + { + spIMFMediaBuffer = nullptr; + spXVPOutputSample->Release(); + + DMFTCHECKHR_GOTO(MFCreateAlignedMemoryBuffer( + StreamInfo.cbSize, + StreamInfo.cbAlignment, + &spIMFMediaBuffer), done); + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! XVP Configured without DX, Created a 1D buffer"); + + } + else + { + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! XVP Configured with DX, Created a 2D buffer"); + } + DMFTCHECKHR_GOTO(spXVPOutputSample->AddBuffer(spIMFMediaBuffer.Get()), done); + } + } + + + // + //Start streaming the xvp transform.. + // + DMFTCHECKHR_GOTO(Transform()->MFTProcessMessage(MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, 0), done); // ulParam set to zero + DMFTCHECKHR_GOTO(Transform()->MFTProcessMessage(MFT_MESSAGE_NOTIFY_START_OF_STREAM, 0), done); // ulParam set to zero + + + DMFTCHECKHR_GOTO(Transform()->MFTProcessInput(0, pSample, 0),done); + + DWORD dwStatus = 0; + + pohr = Transform()->MFTProcessOutput(0, 1, &outputSample, &dwStatus); + + if (FAILED(pohr)) + { + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! XVP ProcessOutput,failure %x sample =%p", pohr, &outputSample); + SAFERELEASE(spXVPOutputSample); + } + + DMFTCHECKHR_GOTO(Transform()->MFTProcessMessage(MFT_MESSAGE_COMMAND_FLUSH, 0), done); // Flush the stream + DMFTCHECKHR_GOTO(Transform()->MFTProcessMessage(MFT_MESSAGE_NOTIFY_END_OF_STREAM, 0), done); // Notify end of stream + DMFTCHECKHR_GOTO(Transform()->MFTProcessMessage(MFT_MESSAGE_NOTIFY_END_STREAMING, 0), done); // Notify end of streaming + + spXVPOutputSample = outputSample.pSample; + + if ( SUCCEEDED(pohr) ) + { + //Let us copy the timestamps + LONGLONG hnsSampleDuration = 0; + LONGLONG hnsSampleTime = 0; + + pSample->GetSampleDuration(&hnsSampleDuration); + pSample->GetSampleTime(&hnsSampleTime); + spXVPOutputSample->SetSampleDuration(hnsSampleDuration); + spXVPOutputSample->SetSampleTime(hnsSampleTime); + } + + *pOutSample = spXVPOutputSample; + // + //Release the Sample back to the pipeline + // + pSample->Release(); + +done: + hr = FAILED(pohr) ? pohr : hr; + return hr; +} + +/*++ +Description: + Set the D3D Manager on the XVP +--*/ +STDMETHODIMP_(VOID) CXvptee::SetD3DManager(IUnknown* pUnk) +{ + m_spDeviceManagerUnk = pUnk; +} +/*++ +Descrtiption: + Create the XVP Transform with the media types supplied. + This is invoked when an XVP is added to the Queue in the Output pins + and the input pins media type and the output pins media type don't match. + +--*/ +STDMETHODIMP CXvptee::Configure( + _In_opt_ IMFMediaType* inMediaType, + _In_opt_ IMFMediaType* outMediaType, + _Outptr_ IMFTransform **ppTransform + ) +{ + HRESULT hr = S_OK; + GUID inMajorType = GUID_NULL; + GUID outMajorType = GUID_NULL; + bool imageType = false; + bool optimized = false; + bool optimizedxvpneeded = false; + IMFMediaType *pXvpOutputMediaType = nullptr; + ComPtr pXvpTransformAttributes = nullptr; + UINT32 width = 0, height = 0; + + DMFTCHECKNULL_GOTO(ppTransform, done, E_INVALIDARG); + + m_isOutPutImage = false; + m_isoptimizedPlanarInputOutput = false; + + // + //Print out the Media type set on the XVP + // + { + CMediaTypePrinter inType(inMediaType); + CMediaTypePrinter outType(outMediaType); + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! Input MediaType %s", inType.ToString()); + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! Output MediaType %s", outType.ToString()); + } + + DMFTCHECKHR_GOTO( CoCreateInstance( + CLSID_VideoProcessorMFT, + NULL, + CLSCTX_INPROC_SERVER, + IID_PPV_ARGS(ppTransform)), done); + + + + DMFTCHECKHR_GOTO( inMediaType->GetMajorType( &inMajorType ),done ); + DMFTCHECKHR_GOTO( outMediaType->GetMajorType( &outMajorType ), done ); + // + //Configuring DX Manager in SW mode. We will look to make it DX complaint in the next phase + // + if ( !IsEqualGUID(MFMediaType_Video, outMajorType ) ) + { + imageType = true; + m_isOutPutImage = true; + IsOptimizedPlanarVideoInputImageOutputPair( + inMediaType, + outMediaType, + &optimized, + &optimizedxvpneeded ); + } + + // + //Disable frame rate conversion + // + DMFTCHECKHR_GOTO((*ppTransform)->GetAttributes(&pXvpTransformAttributes),done); + pXvpTransformAttributes->SetUINT32(MF_XVP_DISABLE_FRC, TRUE); + DMFTCHECKHR_GOTO( (*ppTransform)->MFTProcessMessage( MFT_MESSAGE_SET_D3D_MANAGER, NULL ), done ); + DMFTCHECKHR_GOTO((*ppTransform)->MFTSetInputType(0, inMediaType, 0), done); + + + DMFTCHECKHR_GOTO(MFCreateMediaType(&pXvpOutputMediaType), done); + DMFTCHECKHR_GOTO(outMediaType->CopyAllItems(pXvpOutputMediaType), done); + DMFTCHECKHR_GOTO(pXvpOutputMediaType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video), done); //Video propcessor doesn't understand image types + + + if ( imageType ) + { + GUID guidSub; + + if ( !optimized ) + { + DMFTCHECKHR_GOTO( inMediaType->GetGUID(MF_MT_SUBTYPE, &guidSub ), done ); + if ( IsEqualGUID( guidSub, MFVideoFormat_ARGB32 ) ) + { + DMFTCHECKHR_GOTO(pXvpOutputMediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_ARGB32), done); + } + else + { + DMFTCHECKHR_GOTO(pXvpOutputMediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32), done); + } + DMFTCHECKHR_GOTO(MFGetAttributeSize(pXvpOutputMediaType, MF_MT_FRAME_SIZE, &width, &height), done); + + m_uWidth = width; + m_uHeight = height; + + DMFTCHECKHR_GOTO(pXvpOutputMediaType->SetUINT32(MF_MT_DEFAULT_STRIDE, width * 4), done); //So we always get a top down buffer + + DMFTCHECKHR_GOTO((*ppTransform)->MFTSetOutputType(0, pXvpOutputMediaType, 0), done); + } + else + { + //*** If we come here we know that the XVP is needed + //This case should be for non planar and xvp needed cases which we will always assume + LONG cStride = 0; + //For now we will just assume that we need the xvp in all cases + DMFTCHECKHR_GOTO( inMediaType->GetGUID(MF_MT_SUBTYPE, &guidSub ), done ); + + DMFTCHECKHR_GOTO(pXvpOutputMediaType->SetGUID(MF_MT_SUBTYPE, guidSub), done); + + DMFTCHECKHR_GOTO(MFGetAttributeSize(pXvpOutputMediaType, MF_MT_FRAME_SIZE, &width, &height), done); + + DMFTCHECKHR_GOTO( MFGetStrideForBitmapInfoHeader( guidSub.Data1, width, &cStride ), done ); + + DMFTCHECKHR_GOTO(pXvpOutputMediaType->SetUINT32(MF_MT_DEFAULT_STRIDE, cStride), done); //So we always get a top down buffer for Nv12/Yv12 + + DMFTCHECKHR_GOTO(pXvpOutputMediaType->SetUINT32(MF_MT_VIDEO_NOMINAL_RANGE, MFNominalRange_0_255), done); //Always set nominal range to 0-255 to feed into a WIC encoder + + DMFTCHECKHR_GOTO((*ppTransform)->MFTSetOutputType(0, pXvpOutputMediaType, 0), done); + } + } + else + { + DMFTCHECKHR_GOTO((*ppTransform)->MFTSetOutputType(0, outMediaType, 0), done); + } + if ((m_spDeviceManagerUnk != nullptr)) + { + // + //Set the D3D Manager on the XVP if present + // + DMFTCHECKHR_GOTO((*ppTransform)->MFTProcessMessage(MFT_MESSAGE_SET_D3D_MANAGER, + reinterpret_cast(m_spDeviceManagerUnk.Get())), done); + } +done: + SAFE_RELEASE(pXvpOutputMediaType); + return hr; + +} + + +CXvptee::CXvptee( _In_ Ctee *tee) : +CWrapTee(tee), + m_isOutPutImage(false), + m_isoptimizedPlanarInputOutput(true), + m_spDeviceManagerUnk(nullptr), + m_uHeight(0), + m_uWidth(0) + +{ + +} + + +CXvptee::~CXvptee() +{ + m_spDeviceManagerUnk = nullptr; +} + +//To be implemented..Not needed for the sample!! +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ + +STDMETHODIMP CDecoderTee::Do(_In_ IMFSample* pSample, _Out_ IMFSample **pOutSample ) +{ + UNREFERENCED_PARAMETER(pSample); + UNREFERENCED_PARAMETER(pOutSample); + return S_OK; +} +STDMETHODIMP CDecoderTee::Configure( _In_opt_ IMFMediaType *inMediaType, _In_opt_ IMFMediaType *outMediaType, _Outptr_ IMFTransform** ppTransform) +{ + HRESULT hr = S_OK; + GUID guidcategory = GUID_NULL; + BOOL IsDecoder = true; + UINT32 unFlags = 0; + MFT_REGISTER_TYPE_INFO in; + MFT_REGISTER_TYPE_INFO out; + UINT32 cActivates = 0; + UINT32 unMFTFlags = 0; + ComPtr pInputMediaType = nullptr; + ComPtr pOutputMediaType = nullptr; + IMFActivate **ppActivates = nullptr; + DWORD dwInputStreams; + DWORD dwOutputStreams; + UNREFERENCED_PARAMETER(outMediaType); + UNREFERENCED_PARAMETER(inMediaType); + UNREFERENCED_PARAMETER(ppTransform); + + guidcategory = (IsDecoder)?MFT_CATEGORY_VIDEO_DECODER : MFT_CATEGORY_VIDEO_ENCODER; + + if (IsDecoder) + { + DMFTCHECKHR_GOTO(pInputMediaType->GetGUID(MF_MT_MAJOR_TYPE, &in.guidMajorType), done); + DMFTCHECKHR_GOTO(pInputMediaType->GetGUID(MF_MT_SUBTYPE, &in.guidSubtype), done); + } + else + { + DMFTCHECKHR_GOTO(pOutputMediaType->GetGUID(MF_MT_MAJOR_TYPE, &out.guidMajorType), done); + DMFTCHECKHR_GOTO(pOutputMediaType->GetGUID(MF_MT_SUBTYPE, &out.guidSubtype), done); + } + + // + //Use all possible plugins + // + + unFlags = MFT_ENUM_FLAG_ASYNCMFT | MFT_ENUM_FLAG_SYNCMFT | MFT_ENUM_FLAG_SORTANDFILTER | MFT_ENUM_FLAG_SORTANDFILTER_WEB_ONLY; + DMFTCHECKHR_GOTO(MFTEnumEx(guidcategory, + unFlags, + IsDecoder ? &in : NULL, + IsDecoder ? NULL : &out, + &ppActivates, + &cActivates + ),done); + if (cActivates!=0) + { + DMFTCHECKHR_GOTO(MF_E_TOPO_CODEC_NOT_FOUND, done); + } + for (DWORD dwIndex = 0; dwIndex < cActivates; dwIndex++) + { + // + //Check if this MFT requires an unlocking.. skip if it does + // + unMFTFlags = MFGetAttributeUINT32(ppActivates[dwIndex], MF_TRANSFORM_FLAGS_Attribute, 0); + if (unMFTFlags & MFT_ENUM_FLAG_FIELDOFUSE) + { + // + //If you have the unlocking key set MFT_FIELDOFUSE_UNLOCK_Attribute on the activate + // + continue; + } + + hr = ppActivates[dwIndex]->ActivateObject(IID_PPV_ARGS(ppTransform)); + if (FAILED(hr)) + continue; //hope the next activate is activable + ppActivates[dwIndex]->DetachObject(); + + hr = (*ppTransform)->MFTGetStreamCount(&dwInputStreams, &dwOutputStreams); + + if (FAILED(hr) || dwInputStreams != 1 || dwOutputStreams > 127)//remove the hardcoding + { + // + //Exception.. we can't use a stream that doesn't give us an idea about it's streams + // + continue; + } + if (IsDecoder) + { + hr = ConfigureDecoder(*ppTransform); + } + else + { + hr = ConfigureEncoder(*ppTransform); + } + if (FAILED(hr)) + { + SAFE_RELEASE(*ppTransform); + } + } + +done: + return hr; +} + +/*++ + +--*/ + +STDMETHODIMP CDecoderTee::ConfigureEncoder( _In_ IMFTransform *pTransform ) +{ + HRESULT hr = S_OK; + // + //Set the input media type and the output media type and that should be pretty much it + // + ComPtr pInputType = nullptr; + ComPtr pOutputType = nullptr; + + pInputType = getInMediaType(); + pOutputType = getOutMediaType(); + + if (pInputType && pOutputType) + { + //We should be good by now + DMFTCHECKHR_GOTO( pTransform->MFTSetInputType( 0,pInputType.Get(), 0), done); + //If we have D3D enabled then try again with D3D disabled and see the result + // + DMFTCHECKHR_GOTO(pTransform->MFTSetOutputType(0,pOutputType.Get(), 0), done); + // + //If the above operation fails and we are allowing partial media types then + //we have to find the full media type and then check again + } + else + { + hr = E_FAIL; + } +done: + return hr; +} + +STDMETHODIMP CDecoderTee::ConfigureDecoder( _In_ IMFTransform *pTransform) +{ + UNREFERENCED_PARAMETER( pTransform ); + return S_OK; +} + + + + diff --git a/avstream/sampledevicemft/multipinmfthelpers.h b/avstream/sampledevicemft/multipinmfthelpers.h new file mode 100644 index 000000000..a998f4833 --- /dev/null +++ b/avstream/sampledevicemft/multipinmfthelpers.h @@ -0,0 +1,206 @@ +//*@@@+++@@@@****************************************************************** +// +// Microsoft Windows Media Foundation +// Copyright (C) Microsoft Corporation. All rights reserved. +// +//*@@@---@@@@****************************************************************** +// +#pragma once +#include "stdafx.h" +#include "common.h" + + + +// +//Queue class!!! +// +class Ctee; +//typedef CMFAttributesTrace CMediaTypeTrace; /* Only used for debug. take this out*/ + + +class CPinQueue{ +public: + CPinQueue(_In_ DWORD _inPinId); + ~CPinQueue(); + + STDMETHODIMP_(BOOL) SetState ( _In_ BOOL state ); + STDMETHODIMP_(VOID) InsertInternal ( _In_ IMFSample *pSample = nullptr ); + STDMETHODIMP_(BOOL) Insert ( _In_ IMFSample *pSample ); + STDMETHODIMP_(BOOL) Remove (_Outptr_result_maybenull_ IMFSample **pSample); + STDMETHODIMP RecreateTee ( _In_ IMFMediaType *inMediatype, _In_ IMFMediaType *outMediatype, _In_opt_ IUnknown* punkManager ); + STDMETHODIMP_(VOID) Clear(); + + // + //Inline functions + // + __inline BOOL Empty() + { + return (!m_sampleList.size()); + } + + __inline DWORD pinStreamId() + { + return m_dwInPinId; + } + +private: + DWORD m_dwInPinId; /*This is the input pin */ + IMFSampleList m_sampleList; /*List storing the samples */ + ULONG m_sampleCount; /*Numebr of sampels */ + Ctee* m_teer; /*Tee that acts as a passthrough or an XVP */ + BOOL m_discotinuity; /*Set after the queue is emptied or flushed */ + +}; + +class CPinState{ +public: + virtual STDMETHODIMP Open() = 0; + CPinState( _In_ DeviceStreamState _state = DeviceStreamState_Disabled) :m_State( _state ) + { + + } + STDMETHODIMP_(DeviceStreamState) SetState( _In_ DeviceStreamState ); /*True for Active and False for Stop*/ + STDMETHODIMP_(DeviceStreamState) GetState(); + + __inline STDMETHODIMP_(DeviceStreamState) State() + { + return m_State; + } + virtual ~CPinState() = 0 + { + } +protected: + DeviceStreamState m_State; +}; + +class CPinOpenState :public CPinState{ +public: + CPinOpenState( _In_ CBasePin *p = NULL ); + // + //Inline + // + __inline STDMETHODIMP Open() + { + return S_OK; + } + +private: + CBasePin *m_pin; +}; +class CPinClosedState : public CPinState{ +public: + CPinClosedState(DeviceStreamState _state = DeviceStreamState_Stop) + :CPinState(_state) + { + + } + // + //Inline + // + __inline STDMETHODIMP Open() + { + return MF_INVALID_STATE_ERR; + } +}; + + + +// +// Define these in different components +// The below classes are used to add the +// XVP and the Decoder components +// +class Ctee{ +public: + virtual STDMETHODIMP PassThrough( _In_ IMFSample *, _In_ CPinQueue * ) = 0; +}; + + +class CNullTee:public Ctee{ +public: + STDMETHODIMP PassThrough( _In_ IMFSample*, _In_ CPinQueue * ); +}; + + +class CWrapTee : public Ctee{ +public: + CWrapTee(_In_ Ctee *tee=nullptr) +: m_objectWrapped(tee) + , m_pInputMediaType(nullptr) + , m_pOutputMediaType(nullptr) + { + + } + virtual ~CWrapTee()=0 + { + m_videoProcessor = nullptr; + if (m_objectWrapped) + { + delete(m_objectWrapped); + } + } + + STDMETHODIMP PassThrough ( _In_ IMFSample*, _In_ CPinQueue * ); + virtual STDMETHODIMP Configure ( _In_ IMFMediaType *, _In_ IMFMediaType *, _Inout_ IMFTransform** ) = 0; + virtual STDMETHODIMP Do ( _In_ IMFSample* pSample, _Out_ IMFSample ** ) = 0; + STDMETHODIMP SetMediaTypes ( _In_ IMFMediaType* pInMediaType, _In_ IMFMediaType* pOutMediaType ); + + // + //Inline functions + // +protected: + __inline IMFTransform* Transform() + { + return m_videoProcessor.Get(); + } + __inline IMFMediaType* getInMediaType() + { + IMFMediaType* pmediaType = nullptr; + m_pInputMediaType.CopyTo(&pmediaType); + return pmediaType; + } + __inline IMFMediaType* getOutMediaType() + { + IMFMediaType* pmediaType = nullptr; + m_pOutputMediaType.CopyTo(&pmediaType); + return pmediaType; + } + +private: + ComPtr< IMFTransform > m_videoProcessor; + ComPtr< IMFMediaType > m_pInputMediaType; + ComPtr< IMFMediaType > m_pOutputMediaType; + Ctee *m_objectWrapped; +}; + + +class CXvptee :public CWrapTee{ +public: + CXvptee( _In_ Ctee * ); + ~CXvptee(); + STDMETHODIMP Do ( _In_ IMFSample* pSample, _Outptr_ IMFSample ** ); + STDMETHODIMP Configure ( _In_opt_ IMFMediaType *, _In_opt_ IMFMediaType *, _Outptr_ IMFTransform** ); + STDMETHODIMP_(VOID) SetD3DManager( IUnknown* punk ); + +private: + BOOL m_isoptimizedPlanarInputOutput; + BOOL m_isOutPutImage; + UINT32 m_uWidth; + UINT32 m_uHeight; + ComPtr m_spDeviceManagerUnk; +}; + +class CDecoderTee : public CWrapTee{ +public: + CDecoderTee(Ctee* *); + ~CDecoderTee(); + + STDMETHODIMP Do( _In_ IMFSample* pSample, _Out_ IMFSample ** ); + STDMETHODIMP Configure( _In_opt_ IMFMediaType *, _In_opt_ IMFMediaType *, _Outptr_ IMFTransform** ); + STDMETHODIMP ConfigureEncoder( _In_ IMFTransform *pTransform ); + STDMETHODIMP ConfigureDecoder( _In_ IMFTransform *pTransform ); +private: + ComPtr m_spDeviceManagerUnk; +}; + + diff --git a/avstream/sampledevicemft/multipinmftutils.cpp b/avstream/sampledevicemft/multipinmftutils.cpp new file mode 100644 index 000000000..d36baa132 --- /dev/null +++ b/avstream/sampledevicemft/multipinmftutils.cpp @@ -0,0 +1,874 @@ +//*@@@+++@@@@****************************************************************** +// +// Microsoft Windows Media Foundation +// Copyright (C) Microsoft Corporation. All rights reserved. +// +//*@@@---@@@@****************************************************************** +// + +#include "stdafx.h" +#include "common.h" +#include "multipinmft.h" +#include "basepin.h" + +#pragma comment(lib, "d2d1") +#ifdef MF_WPP +#include "multipinmftutils.tmh" +#endif + +// Critical sections + +CCritSec::CCritSec() +{ + InitializeCriticalSection(&m_criticalSection); +} + +CCritSec::~CCritSec() +{ + DeleteCriticalSection(&m_criticalSection); +} + +_Requires_lock_not_held_(m_criticalSection) _Acquires_lock_(m_criticalSection) +void CCritSec::Lock() +{ + EnterCriticalSection(&m_criticalSection); +} + +_Requires_lock_held_(m_criticalSection) _Releases_lock_(m_criticalSection) +void CCritSec::Unlock() +{ + LeaveCriticalSection(&m_criticalSection); +} + + +_Acquires_lock_(this->m_pCriticalSection->m_criticalSection) +CAutoLock::CAutoLock(CCritSec& crit) +{ + m_pCriticalSection = &crit; + m_pCriticalSection->Lock(); +} +_Acquires_lock_(this->m_pCriticalSection->m_criticalSection) +CAutoLock::CAutoLock(CCritSec* crit) +{ + m_pCriticalSection = crit; + m_pCriticalSection->Lock(); +} +_Releases_lock_(this->m_pCriticalSection->m_criticalSection) +CAutoLock::~CAutoLock() +{ + m_pCriticalSection->Unlock(); +} + +// +//Some utility functions.. +// + +/*++ + Description: + Used to check if the input and the output Image types are optinmized or not +--*/ + +STDMETHODIMP IsOptimizedPlanarVideoInputImageOutputPair( + _In_ IMFMediaType *inMediaType, + _In_ IMFMediaType *outMediaType, + _Out_ bool *optimized, + _Out_ bool *optimizedxvpneeded ) +{ + HRESULT hr = S_OK; + GUID guidInputSubType = GUID_NULL; + GUID guidOutputSubType = GUID_NULL; + UINT32 uWidthIn = 0, uHeightIn = 0, uWidthOut = 0, uHeightOut = 0; + + + DMFTCHECKHR_GOTO(inMediaType->GetGUID(MF_MT_SUBTYPE, &guidInputSubType), done); + + DMFTCHECKHR_GOTO(outMediaType->GetGUID(MF_MT_SUBTYPE, &guidOutputSubType), done); + + *optimized = false; //Assume we aren't optimized . Optimized = (ip = YU12|NV12 and op = JPEG) + *optimizedxvpneeded = true; //Assume we need xvps + + if (IsEqualGUID(guidInputSubType, MFVideoFormat_YV12) || IsEqualGUID(guidInputSubType, MFVideoFormat_NV12)) + { + if (IsEqualGUID(guidOutputSubType, GUID_ContainerFormatJpeg)) + { + *optimized = true; + } + } + + if (!*optimized) + { + goto done; + } + + DMFTCHECKHR_GOTO(MFGetAttributeSize(inMediaType, MF_MT_FRAME_SIZE, &uWidthIn, &uHeightIn), done); + DMFTCHECKHR_GOTO(MFGetAttributeSize(outMediaType, MF_MT_FRAME_SIZE, &uWidthOut, &uHeightOut), done); + + if ((uWidthIn == uWidthOut) && (uHeightIn == uHeightOut)) + { + *optimizedxvpneeded = false; + } + if (!*optimizedxvpneeded) + { + UINT32 nominalRange; + hr = inMediaType->GetUINT32(MF_MT_VIDEO_NOMINAL_RANGE, &nominalRange); + + if (FAILED(hr) || nominalRange != MFNominalRange_0_255) + { + //XVP needed since nominal range is not 0-255 for YV12 or NV12 fed into WIC + *optimizedxvpneeded = true; + } + hr = S_OK; + } + +done: + return hr; +} + + +/* +Description: + + This is used whenever there is a media type change on an output pin and the + Output queue is being reconfigured. + The possible return values for the function are as follows + + DeviceMftTransformXVPIllegal -> If either of the mediatypes or both are NULL + DeviceMftTransformXVPDisruptiveIn -> If the mediatype at the output pin is greater than the input pin. This will result in change of the media type on the input + DeviceMftTransformXVPDisruptiveOut -> This is a reconfiguration or addition of the XVP in the Output pin queue + DeviceMftTransformXVPCurrent -> No XVP needed at all + Note: This iteration doesn't support decoder. The next one will and this function will accordingly change +*/ +STDMETHODIMP CompareMediaTypesForXVP( + _In_opt_ IMFMediaType *inMediaType, + _In_ IMFMediaType *newMediaType, + _Inout_ MF_TRANSFORM_XVP_OPERATION *operation + ) +{ + UINT32 unWidthin, unHeightin, unWidthNew, unHeightNew = 0; + HRESULT hr = S_OK; + GUID guidTypeA = GUID_NULL; + GUID guidTypeB = GUID_NULL; + + *operation = DeviceMftTransformXVPIllegal; + if ((!inMediaType) || (!newMediaType)) + { + goto done; + } + + DMFTCHECKHR_GOTO( MFGetAttributeSize( inMediaType, MF_MT_FRAME_SIZE, &unWidthin, &unHeightin ), done ); + DMFTCHECKHR_GOTO( MFGetAttributeSize( newMediaType, MF_MT_FRAME_SIZE, &unWidthNew, &unHeightNew ), done ); + + + if ( SUCCEEDED( inMediaType->GetGUID( MF_MT_MAJOR_TYPE, &guidTypeA ) ) && + SUCCEEDED( newMediaType->GetGUID( MF_MT_MAJOR_TYPE, &guidTypeB ) ) && + IsEqualGUID( guidTypeA, guidTypeB ) ) + { + if ( SUCCEEDED( inMediaType->GetGUID ( MF_MT_SUBTYPE, &guidTypeA ) ) && + SUCCEEDED( newMediaType->GetGUID( MF_MT_SUBTYPE, &guidTypeB ) ) && + IsEqualGUID( guidTypeA, guidTypeB ) ) + { + //Comparing the MF_MT_AM_FORMAT_TYPE for the directshow format guid +#if 0 + if (SUCCEEDED(inMediaType->GetGUID(MF_MT_AM_FORMAT_TYPE, &guidTypeA)) && + SUCCEEDED(newMediaType->GetGUID(MF_MT_AM_FORMAT_TYPE, &guidTypeB)) && + IsEqualGUID(guidTypeA, guidTypeB)) +#endif + { + + if (!(( unWidthin == unWidthNew ) && + ( unHeightin == unHeightNew ) ) ) + { + if ( ( unWidthNew > unWidthin ) || ( unHeightNew > unHeightin ) ) + { + *operation = DeviceMftTransformXVPDisruptiveIn; //Media type needs to change at input + } + else + { + *operation = DeviceMftTransformXVPDisruptiveOut; //Media type needs to change at output + } + goto done; + } + + if ( MFGetAttributeUINT32( inMediaType, MF_MT_SAMPLE_SIZE, 0 ) != + MFGetAttributeUINT32( newMediaType, MF_MT_SAMPLE_SIZE, 0 ) ) + { + hr = S_FALSE; //Sample sizes differ. + goto done; + } + else + { + //Same media type.. No XVP needed or the current XVP is fine! + *operation = DeviceMftTransformXVPCurrent; + } + } + } + else + { + //This is a disruptive operation. Actually a decoder operation! + *operation = DeviceMftTransformXVPDisruptiveIn; + } + } + done: + return hr; +} + +/*++ +Description: + A Simple function to randomnize the media types supplied in an array +--*/ +STDMETHODIMP RandomnizeMediaTypes(_In_ IMFMediaTypeArray &pMediaTypeArray) +{ + HRESULT hr = S_OK; + IMFMediaType *pTempMediaType = nullptr; + UNREFERENCED_PARAMETER(pTempMediaType); + if (pMediaTypeArray.empty()) + { + goto done; + } + + srand(static_cast(GetTickCount64())); + + for (DWORD dwIndex = (DWORD)pMediaTypeArray.size() - 1; dwIndex > 0; dwIndex--) + { + DWORD fromIndex = rand() % ( dwIndex + 1 ); + pTempMediaType = pMediaTypeArray[dwIndex]; + pMediaTypeArray [ dwIndex ] = pMediaTypeArray[fromIndex] ; + pMediaTypeArray [ fromIndex ] = pTempMediaType; + } + // + //Just reverse the array + // + for (DWORD dwIndex = (DWORD)pMediaTypeArray.size() - 1, fromIndex = 0; dwIndex > 0; dwIndex--, fromIndex++) + { + + pTempMediaType = pMediaTypeArray[dwIndex]; + pMediaTypeArray [ dwIndex ] = pMediaTypeArray[fromIndex]; + pMediaTypeArray [ fromIndex ] = pTempMediaType; + } + done: + return hr; + +} + +/*++ +Description: Used to test if the sample passed is a DX sample or not? +--*/ +STDMETHODIMP IsInputDxSample( + _In_ IMFSample* pSample, + _Inout_ BOOL *isDxSample + ) +{ + HRESULT hr = S_OK; + ComPtr spBuffer = nullptr; + DMFTCHECKNULL_GOTO( pSample, done, E_INVALIDARG ); + + *isDxSample = false; + if ( SUCCEEDED( pSample->GetBufferByIndex( 0, &spBuffer ) ) ) + { + ComPtr spDXGIBuffer; + ComPtr spResourceTexture; + if ( SUCCEEDED( spBuffer.As(&spDXGIBuffer ) ) && + SUCCEEDED( spDXGIBuffer-> GetResource( IID_PPV_ARGS( &spResourceTexture ) ) ) ) + { + *isDxSample = true; + } + } +done: + return hr; +} + + +/*++ + Description: + Helper function to return back if the Pin is in stopped stateo or not +--*/ +STDMETHODIMP_(BOOL) IsPinStateInActive( _In_ DeviceStreamState state) +{ + if ((state == DeviceStreamState_Disabled) || + (state == DeviceStreamState_Stop)) + { + return TRUE; + } + return FALSE; +} + +/* +Description: + A Helper function to return if the Mediatype is a compressed video type or not! +*/ +STDMETHODIMP_(BOOL) IsKnownUncompressedVideoType(_In_ GUID guidSubType) +{ + if (guidSubType == MFVideoFormat_ARGB32 || + guidSubType == MFVideoFormat_RGB24 || + guidSubType == MFVideoFormat_RGB555 || + guidSubType == MFVideoFormat_RGB565 || + guidSubType == MFVideoFormat_RGB32 || + guidSubType == MFVideoFormat_RGB8 || + guidSubType == MFVideoFormat_AI44 || + guidSubType == MFVideoFormat_AYUV || + guidSubType == MFVideoFormat_YUY2 || + guidSubType == MFVideoFormat_YVYU || + guidSubType == MFVideoFormat_YVU9 || + guidSubType == MFVideoFormat_UYVY || + guidSubType == MFVideoFormat_NV11 || + guidSubType == MFVideoFormat_NV12 || + guidSubType == MFVideoFormat_YV12 || + guidSubType == MFVideoFormat_I420 || + guidSubType == MFVideoFormat_IYUV || + guidSubType == MFVideoFormat_Y210 || + guidSubType == MFVideoFormat_Y216 || + guidSubType == MFVideoFormat_Y410 || + guidSubType == MFVideoFormat_Y416 || + guidSubType == MFVideoFormat_Y41P || + guidSubType == MFVideoFormat_Y41T || + guidSubType == MFVideoFormat_Y42T || + guidSubType == MFVideoFormat_P210 || + guidSubType == MFVideoFormat_P216 || + guidSubType == MFVideoFormat_P010 || + guidSubType == MFVideoFormat_P016 || + guidSubType == MFVideoFormat_v210 || + guidSubType == MFVideoFormat_v216 || + guidSubType == MFVideoFormat_v410 ) + { + return( TRUE ); + } + + return( FALSE ); +} + +/*++ +Description: + Function used to lock the MFT. +--*/ + +STDMETHODIMP UnLockAsynMFT(IMFTransform* pTransform) +{ + HRESULT hr = S_OK; + IMFAttributes *pAttributes; + UINT32 unValue; + + DMFTCHECKNULL_GOTO(pTransform,done, E_INVALIDARG); + DMFTCHECKHR_GOTO(pTransform->GetAttributes(&pAttributes),done); + DMFTCHECKHR_GOTO(pAttributes->GetUINT32(MF_TRANSFORM_ASYNC, &unValue), done); + + if (unValue) + { + DMFTCHECKHR_GOTO(pAttributes->SetUINT32(MF_TRANSFORM_ASYNC, true), done); + } + +done: + return hr; +} + + +#ifndef IF_EQUAL_RETURN +#define IF_EQUAL_RETURN(param, val) if(val == param) return #val +#endif + +#define checkAdjustBufferCap(a,len){\ + char* tStore = NULL; \ +if (a && strlen(a) > ((len * 7) / 10)){\ + tStore = a; \ + len *= 2; \ + a = new char[len]; \ +if (!a){\ +goto done;}\ + a[0] = 0; \ + strcat_s(a, len, tStore); \ + delete(tStore); }\ +} + + +/*++ +Description: + returns an ascii buffer for the GUID passed. The Caller should release the memory allocated +--*/ +LPSTR DumpGUIDA(_In_ REFGUID guid) +{ + LPOLESTR lpszGuidString = NULL; + char *ansiguidStr = NULL; + if (SUCCEEDED(StringFromCLSID(guid, &lpszGuidString))) + { + int mbGuidLen = 0; + mbGuidLen = WideCharToMultiByte( CP_ACP, WC_NO_BEST_FIT_CHARS, lpszGuidString, -1, NULL, 0, NULL, NULL); + if (mbGuidLen > 0) + { + mf_assert(mbGuidLen == (int)wcslen(lpszGuidString)); + ansiguidStr = new char[mbGuidLen]; + WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, lpszGuidString, -1, ansiguidStr, mbGuidLen, NULL, NULL); + CoTaskMemFree(lpszGuidString); + ansiguidStr[mbGuidLen - 1] = 0; + } + } + return ansiguidStr; +} + +// +//Borrrowed from MDSN sample +// +LPCSTR GetGUIDNameConst(const GUID& guid) +{ + IF_EQUAL_RETURN(guid, MF_MT_MAJOR_TYPE); + IF_EQUAL_RETURN(guid, MF_MT_MAJOR_TYPE); + IF_EQUAL_RETURN(guid, MF_MT_SUBTYPE); + IF_EQUAL_RETURN(guid, MF_MT_ALL_SAMPLES_INDEPENDENT); + IF_EQUAL_RETURN(guid, MF_MT_FIXED_SIZE_SAMPLES); + IF_EQUAL_RETURN(guid, MF_MT_COMPRESSED); + IF_EQUAL_RETURN(guid, MF_MT_SAMPLE_SIZE); + IF_EQUAL_RETURN(guid, MF_MT_WRAPPED_TYPE); + IF_EQUAL_RETURN(guid, MF_MT_AUDIO_NUM_CHANNELS); + IF_EQUAL_RETURN(guid, MF_MT_AUDIO_SAMPLES_PER_SECOND); + IF_EQUAL_RETURN(guid, MF_MT_AUDIO_FLOAT_SAMPLES_PER_SECOND); + IF_EQUAL_RETURN(guid, MF_MT_AUDIO_AVG_BYTES_PER_SECOND); + IF_EQUAL_RETURN(guid, MF_MT_AUDIO_BLOCK_ALIGNMENT); + IF_EQUAL_RETURN(guid, MF_MT_AUDIO_BITS_PER_SAMPLE); + IF_EQUAL_RETURN(guid, MF_MT_AUDIO_VALID_BITS_PER_SAMPLE); + IF_EQUAL_RETURN(guid, MF_MT_AUDIO_SAMPLES_PER_BLOCK); + IF_EQUAL_RETURN(guid, MF_MT_AUDIO_CHANNEL_MASK); + IF_EQUAL_RETURN(guid, MF_MT_AUDIO_FOLDDOWN_MATRIX); + IF_EQUAL_RETURN(guid, MF_MT_AUDIO_WMADRC_PEAKREF); + IF_EQUAL_RETURN(guid, MF_MT_AUDIO_WMADRC_PEAKTARGET); + IF_EQUAL_RETURN(guid, MF_MT_AUDIO_WMADRC_AVGREF); + IF_EQUAL_RETURN(guid, MF_MT_AUDIO_WMADRC_AVGTARGET); + IF_EQUAL_RETURN(guid, MF_MT_AUDIO_PREFER_WAVEFORMATEX); + IF_EQUAL_RETURN(guid, MF_MT_AAC_PAYLOAD_TYPE); + IF_EQUAL_RETURN(guid, MF_MT_AAC_AUDIO_PROFILE_LEVEL_INDICATION); + IF_EQUAL_RETURN(guid, MF_MT_FRAME_SIZE); + IF_EQUAL_RETURN(guid, MF_MT_FRAME_RATE); + IF_EQUAL_RETURN(guid, MF_MT_FRAME_RATE_RANGE_MAX); + IF_EQUAL_RETURN(guid, MF_MT_FRAME_RATE_RANGE_MIN); + IF_EQUAL_RETURN(guid, MF_MT_PIXEL_ASPECT_RATIO); + IF_EQUAL_RETURN(guid, MF_MT_DRM_FLAGS); + IF_EQUAL_RETURN(guid, MF_MT_PAD_CONTROL_FLAGS); + IF_EQUAL_RETURN(guid, MF_MT_SOURCE_CONTENT_HINT); + IF_EQUAL_RETURN(guid, MF_MT_VIDEO_CHROMA_SITING); + IF_EQUAL_RETURN(guid, MF_MT_INTERLACE_MODE); + IF_EQUAL_RETURN(guid, MF_MT_TRANSFER_FUNCTION); + IF_EQUAL_RETURN(guid, MF_MT_VIDEO_PRIMARIES); + IF_EQUAL_RETURN(guid, MF_MT_CUSTOM_VIDEO_PRIMARIES); + IF_EQUAL_RETURN(guid, MF_MT_YUV_MATRIX); + IF_EQUAL_RETURN(guid, MF_MT_VIDEO_LIGHTING); + IF_EQUAL_RETURN(guid, MF_MT_VIDEO_NOMINAL_RANGE); + IF_EQUAL_RETURN(guid, MF_MT_GEOMETRIC_APERTURE); + IF_EQUAL_RETURN(guid, MF_MT_MINIMUM_DISPLAY_APERTURE); + IF_EQUAL_RETURN(guid, MF_MT_PAN_SCAN_APERTURE); + IF_EQUAL_RETURN(guid, MF_MT_PAN_SCAN_ENABLED); + IF_EQUAL_RETURN(guid, MF_MT_AVG_BITRATE); + IF_EQUAL_RETURN(guid, MF_MT_AVG_BIT_ERROR_RATE); + IF_EQUAL_RETURN(guid, MF_MT_MAX_KEYFRAME_SPACING); + IF_EQUAL_RETURN(guid, MF_MT_DEFAULT_STRIDE); + IF_EQUAL_RETURN(guid, MF_MT_PALETTE); + IF_EQUAL_RETURN(guid, MF_MT_USER_DATA); + IF_EQUAL_RETURN(guid, MF_MT_AM_FORMAT_TYPE); + IF_EQUAL_RETURN(guid, MF_MT_MPEG_START_TIME_CODE); + IF_EQUAL_RETURN(guid, MF_MT_MPEG2_PROFILE); + IF_EQUAL_RETURN(guid, MF_MT_MPEG2_LEVEL); + IF_EQUAL_RETURN(guid, MF_MT_MPEG2_FLAGS); + IF_EQUAL_RETURN(guid, MF_MT_MPEG_SEQUENCE_HEADER); + IF_EQUAL_RETURN(guid, MF_MT_DV_AAUX_SRC_PACK_0); + IF_EQUAL_RETURN(guid, MF_MT_DV_AAUX_CTRL_PACK_0); + IF_EQUAL_RETURN(guid, MF_MT_DV_AAUX_SRC_PACK_1); + IF_EQUAL_RETURN(guid, MF_MT_DV_AAUX_CTRL_PACK_1); + IF_EQUAL_RETURN(guid, MF_MT_DV_VAUX_SRC_PACK); + IF_EQUAL_RETURN(guid, MF_MT_DV_VAUX_CTRL_PACK); + IF_EQUAL_RETURN(guid, MF_MT_ARBITRARY_HEADER); + IF_EQUAL_RETURN(guid, MF_MT_ARBITRARY_FORMAT); + IF_EQUAL_RETURN(guid, MF_MT_IMAGE_LOSS_TOLERANT); + IF_EQUAL_RETURN(guid, MF_MT_MPEG4_SAMPLE_DESCRIPTION); + IF_EQUAL_RETURN(guid, MF_MT_MPEG4_CURRENT_SAMPLE_ENTRY); + IF_EQUAL_RETURN(guid, MF_MT_ORIGINAL_4CC); + IF_EQUAL_RETURN(guid, MF_MT_ORIGINAL_WAVE_FORMAT_TAG); + + // Media types + + IF_EQUAL_RETURN(guid, MFMediaType_Audio); + IF_EQUAL_RETURN(guid, MFMediaType_Video); + IF_EQUAL_RETURN(guid, MFMediaType_Protected); + IF_EQUAL_RETURN(guid, MFMediaType_SAMI); + IF_EQUAL_RETURN(guid, MFMediaType_Script); + IF_EQUAL_RETURN(guid, MFMediaType_Image); + IF_EQUAL_RETURN(guid, MFMediaType_HTML); + IF_EQUAL_RETURN(guid, MFMediaType_Binary); + IF_EQUAL_RETURN(guid, MFMediaType_FileTransfer); + + IF_EQUAL_RETURN(guid, MFVideoFormat_AI44); // FCC('AI44') + IF_EQUAL_RETURN(guid, MFVideoFormat_ARGB32); // D3DFMT_A8R8G8B8 + IF_EQUAL_RETURN(guid, MFVideoFormat_AYUV); // FCC('AYUV') + IF_EQUAL_RETURN(guid, MFVideoFormat_DV25); // FCC('dv25') + IF_EQUAL_RETURN(guid, MFVideoFormat_DV50); // FCC('dv50') + IF_EQUAL_RETURN(guid, MFVideoFormat_DVH1); // FCC('dvh1') + IF_EQUAL_RETURN(guid, MFVideoFormat_DVSD); // FCC('dvsd') + IF_EQUAL_RETURN(guid, MFVideoFormat_DVSL); // FCC('dvsl') + IF_EQUAL_RETURN(guid, MFVideoFormat_H264); // FCC('H264') + IF_EQUAL_RETURN(guid, MFVideoFormat_I420); // FCC('I420') + IF_EQUAL_RETURN(guid, MFVideoFormat_IYUV); // FCC('IYUV') + IF_EQUAL_RETURN(guid, MFVideoFormat_M4S2); // FCC('M4S2') + IF_EQUAL_RETURN(guid, MFVideoFormat_MJPG); + IF_EQUAL_RETURN(guid, MFVideoFormat_MP43); // FCC('MP43') + IF_EQUAL_RETURN(guid, MFVideoFormat_MP4S); // FCC('MP4S') + IF_EQUAL_RETURN(guid, MFVideoFormat_MP4V); // FCC('MP4V') + IF_EQUAL_RETURN(guid, MFVideoFormat_MPG1); // FCC('MPG1') + IF_EQUAL_RETURN(guid, MFVideoFormat_MSS1); // FCC('MSS1') + IF_EQUAL_RETURN(guid, MFVideoFormat_MSS2); // FCC('MSS2') + IF_EQUAL_RETURN(guid, MFVideoFormat_NV11); // FCC('NV11') + IF_EQUAL_RETURN(guid, MFVideoFormat_NV12); // FCC('NV12') + IF_EQUAL_RETURN(guid, MFVideoFormat_P010); // FCC('P010') + IF_EQUAL_RETURN(guid, MFVideoFormat_P016); // FCC('P016') + IF_EQUAL_RETURN(guid, MFVideoFormat_P210); // FCC('P210') + IF_EQUAL_RETURN(guid, MFVideoFormat_P216); // FCC('P216') + IF_EQUAL_RETURN(guid, MFVideoFormat_RGB24); // D3DFMT_R8G8B8 + IF_EQUAL_RETURN(guid, MFVideoFormat_RGB32); // D3DFMT_X8R8G8B8 + IF_EQUAL_RETURN(guid, MFVideoFormat_RGB555); // D3DFMT_X1R5G5B5 + IF_EQUAL_RETURN(guid, MFVideoFormat_RGB565); // D3DFMT_R5G6B5 + IF_EQUAL_RETURN(guid, MFVideoFormat_RGB8); + IF_EQUAL_RETURN(guid, MFVideoFormat_UYVY); // FCC('UYVY') + IF_EQUAL_RETURN(guid, MFVideoFormat_v210); // FCC('v210') + IF_EQUAL_RETURN(guid, MFVideoFormat_v410); // FCC('v410') + IF_EQUAL_RETURN(guid, MFVideoFormat_WMV1); // FCC('WMV1') + IF_EQUAL_RETURN(guid, MFVideoFormat_WMV2); // FCC('WMV2') + IF_EQUAL_RETURN(guid, MFVideoFormat_WMV3); // FCC('WMV3') + IF_EQUAL_RETURN(guid, MFVideoFormat_WVC1); // FCC('WVC1') + IF_EQUAL_RETURN(guid, MFVideoFormat_Y210); // FCC('Y210') + IF_EQUAL_RETURN(guid, MFVideoFormat_Y216); // FCC('Y216') + IF_EQUAL_RETURN(guid, MFVideoFormat_Y410); // FCC('Y410') + IF_EQUAL_RETURN(guid, MFVideoFormat_Y416); // FCC('Y416') + IF_EQUAL_RETURN(guid, MFVideoFormat_Y41P); + IF_EQUAL_RETURN(guid, MFVideoFormat_Y41T); + IF_EQUAL_RETURN(guid, MFVideoFormat_YUY2); // FCC('YUY2') + IF_EQUAL_RETURN(guid, MFVideoFormat_YV12); // FCC('YV12') + IF_EQUAL_RETURN(guid, MFVideoFormat_YVYU); + + IF_EQUAL_RETURN(guid, MFAudioFormat_PCM); // WAVE_FORMAT_PCM + IF_EQUAL_RETURN(guid, MFAudioFormat_Float); // WAVE_FORMAT_IEEE_FLOAT + IF_EQUAL_RETURN(guid, MFAudioFormat_DTS); // WAVE_FORMAT_DTS + IF_EQUAL_RETURN(guid, MFAudioFormat_Dolby_AC3_SPDIF); // WAVE_FORMAT_DOLBY_AC3_SPDIF + IF_EQUAL_RETURN(guid, MFAudioFormat_DRM); // WAVE_FORMAT_DRM + IF_EQUAL_RETURN(guid, MFAudioFormat_WMAudioV8); // WAVE_FORMAT_WMAUDIO2 + IF_EQUAL_RETURN(guid, MFAudioFormat_WMAudioV9); // WAVE_FORMAT_WMAUDIO3 + IF_EQUAL_RETURN(guid, MFAudioFormat_WMAudio_Lossless); // WAVE_FORMAT_WMAUDIO_LOSSLESS + IF_EQUAL_RETURN(guid, MFAudioFormat_WMASPDIF); // WAVE_FORMAT_WMASPDIF + IF_EQUAL_RETURN(guid, MFAudioFormat_MSP1); // WAVE_FORMAT_WMAVOICE9 + IF_EQUAL_RETURN(guid, MFAudioFormat_MP3); // WAVE_FORMAT_MPEGLAYER3 + IF_EQUAL_RETURN(guid, MFAudioFormat_MPEG); // WAVE_FORMAT_MPEG + IF_EQUAL_RETURN(guid, MFAudioFormat_AAC); // WAVE_FORMAT_MPEG_HEAAC + IF_EQUAL_RETURN(guid, MFAudioFormat_ADTS); // WAVE_FORMAT_MPEG_ADTS_AAC + + return NULL; +} + + +LPSTR DumpAttribute( _In_ const MF_ATTRIBUTE_TYPE& type, + _In_ REFPROPVARIANT var) +{ + CHAR *tempStr = NULL; + tempStr = new CHAR[256]; + switch (type) + { + case MF_ATTRIBUTE_UINT32: + if (var.vt == VT_UI4) + { + sprintf_s(tempStr, 256, "%u", var.ulVal); + } + break; + case MF_ATTRIBUTE_UINT64: + if (var.vt == VT_UI8) + { + sprintf_s(tempStr, 256, "%I64d (high: %d low: %d)", var.uhVal.QuadPart, var.uhVal.HighPart, var.uhVal.LowPart); + } + break; + case MF_ATTRIBUTE_DOUBLE: + if (var.vt == VT_R8) + { + sprintf_s(tempStr, 256, "%.4f", var.dblVal); + } + break; + case MF_ATTRIBUTE_GUID: + if (var.vt == VT_CLSID) + { + return DumpGUIDA(*var.puuid); + } + break; + case MF_ATTRIBUTE_STRING: + if (var.vt == VT_LPWSTR) + { + sprintf_s(tempStr, 256, "%S", var.pwszVal); + } + break; + case MF_ATTRIBUTE_IUNKNOWN: + break; + default: + printf("(Unknown Attribute Type = %d) ", type); + break; + } + return tempStr; +} + +CMediaTypePrinter::CMediaTypePrinter( + _In_ IMFMediaType *_pMediaType ) + : pMediaType(_pMediaType), + m_pBuffer(NULL) +{ +} + +CMediaTypePrinter::~CMediaTypePrinter() +{ + if (m_pBuffer) + { + delete(m_pBuffer); + } +} + +/*++ +Description: +Rudimentary function to print the complete Media type +--*/ +PCHAR CMediaTypePrinter::ToCompleteString( ) +{ + HRESULT hr = S_OK; + UINT32 attrCount = 0; + GUID attrGuid = { 0 }; + char *tempStore = nullptr; + PROPVARIANT var; + LPSTR pTempBaseStr; + MF_ATTRIBUTE_TYPE pType; + + if ( pMediaType && !m_pBuffer ) + { + DMFTCHECKHR_GOTO(pMediaType->GetCount(&attrCount), done); + buffLen = MEDIAPRINTER_STARTLEN; + m_pBuffer = new char[buffLen]; + m_pBuffer[0] = 0; + for ( UINT32 ulIndex = 0; ulIndex < attrCount; ulIndex++ ) + { + PropVariantInit( &var ); + checkAdjustBufferCap( m_pBuffer, buffLen ); + DMFTCHECKHR_GOTO( pMediaType->GetItemByIndex( ulIndex, &attrGuid, &var ), done ); + DMFTCHECKHR_GOTO( pMediaType->GetItemType( attrGuid, &pType ), done ); + if ( ulIndex > 0 ) + strcat_s(m_pBuffer, MEDIAPRINTER_STARTLEN, " : "); + strcat_s( m_pBuffer, buffLen, GetGUIDNameConst( attrGuid ) ); + strcat_s( m_pBuffer, buffLen, "=" ); + pTempBaseStr = DumpAttribute( pType, var ); + strcat_s( m_pBuffer, buffLen, pTempBaseStr ); + delete( pTempBaseStr ); + PropVariantClear( &var ); + } + done: + if ( tempStore ) + { + delete( tempStore ); + } + } + return m_pBuffer; +} + +/*++ +Description: +Rudimentary function to print the Media type +--*/ + +PCHAR CMediaTypePrinter::ToString() +{ + // + //Following are the important ones of Mediatype attributes + // + + HRESULT hr = S_OK; + PROPVARIANT var; + LPSTR pTempBaseStr; + MF_ATTRIBUTE_TYPE pType; + GUID attrGuid; + GUID impGuids[] = { + MF_MT_SUBTYPE, + MF_MT_FRAME_SIZE, + MF_MT_SAMPLE_SIZE, + MF_MT_FRAME_RATE, + MF_MT_DEFAULT_STRIDE, + MF_XVP_DISABLE_FRC + }; + + if (pMediaType && !m_pBuffer) + { + buffLen = MEDIAPRINTER_STARTLEN; + m_pBuffer = new char[buffLen]; + m_pBuffer[0] = 0; + for (UINT32 ulIndex = 0; ulIndex < ARRAYSIZE(impGuids); ulIndex++) + { + PropVariantInit(&var); + checkAdjustBufferCap(m_pBuffer, buffLen); + attrGuid = impGuids[ulIndex]; + DMFTCHECKHR_GOTO(pMediaType->GetItemType(attrGuid, &pType), done); + DMFTCHECKHR_GOTO(pMediaType->GetItem(attrGuid, &var), done); + if (ulIndex > 0) + strcat_s(m_pBuffer, MEDIAPRINTER_STARTLEN, " : "); + strcat_s(m_pBuffer, buffLen, GetGUIDNameConst(attrGuid)); + strcat_s(m_pBuffer, buffLen, "="); + pTempBaseStr = DumpAttribute(pType, var); + strcat_s(m_pBuffer, buffLen, pTempBaseStr); + delete(pTempBaseStr); + PropVariantClear(&var); + } + } +done: + return m_pBuffer; +} +/*++ +Description: + Debug message printer to print the message passed through WPP +--*/ +void printMessageEvent(MFT_MESSAGE_TYPE msg) +{ + switch (msg) + { + case MFT_MESSAGE_COMMAND_FLUSH: + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! :PROCESSMESSAGE: MFT_MESSAGE_COMMAND_FLUSH"); + break; + case MFT_MESSAGE_COMMAND_DRAIN: + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! :PROCESSMESSAGE: MFT_MESSAGE_COMMAND_DRAIN"); + break; + case MFT_MESSAGE_COMMAND_MARKER: + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! :PROCESSMESSAGE: MFT_MESSAGE_COMMAND_MARKER"); + break; + case MFT_MESSAGE_COMMAND_TICK: + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! :PROCESSMESSAGE: MFT_MESSAGE_COMMAND_TICK"); + break; + case MFT_MESSAGE_NOTIFY_END_OF_STREAM: + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! :PROCESSMESSAGE: MFT_MESSAGE_NOTIFY_END_OF_STREAM"); + break; + case MFT_MESSAGE_NOTIFY_BEGIN_STREAMING: + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! :PROCESSMESSAGE: MFT_MESSAGE_NOTIFY_BEGIN_STREAMING"); + break; + case MFT_MESSAGE_NOTIFY_START_OF_STREAM: + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! :PROCESSMESSAGE: MFT_MESSAGE_NOTIFY_START_OF_STREAM"); + break; + case MFT_MESSAGE_DROP_SAMPLES: + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! :PROCESSMESSAGE: MFT_MESSAGE_DROP_SAMPLES"); + break; + case MFT_MESSAGE_SET_D3D_MANAGER: + DMFTRACE(DMFT_GENERAL, TRACE_LEVEL_INFORMATION, "%!FUNC! :PROCESSMESSAGE: MFT_MESSAGE_SET_D3D_MANAGER"); + break; + + } +} + +/*++ +Below functions not used in the current iteration.. +--*/ +HRESULT CreateCodec( + _In_opt_ IMFMediaType* inMediaType, + _In_opt_ IMFMediaType *outMediaType, + _In_ BOOL operation /*True = Encode, False = Decode*/, + _Out_ IMFTransform **pTransform) +{ + UNREFERENCED_PARAMETER(inMediaType); + UNREFERENCED_PARAMETER(outMediaType); + UNREFERENCED_PARAMETER(operation); + UNREFERENCED_PARAMETER(pTransform); + HRESULT hr = S_OK; + *pTransform = nullptr; + return hr; +} + + +HRESULT CheckDX9RotationSupport(_In_ ID3D11VideoDevice* pVideoDevice) +{ + HRESULT hr = S_OK; + // + // check if the DX9 device is ok + // New drivers should have an DXVA-HD rotation cap set + // + D3D11_VIDEO_PROCESSOR_CONTENT_DESC desc = {}; + D3D11_VIDEO_PROCESSOR_CAPS caps; + ComPtr spVideoProcEnum = nullptr; + + DMFTCHECKNULL_GOTO(pVideoDevice, done, E_NOTIMPL); + desc.InputWidth = 640; + desc.InputHeight = 480; + desc.OutputWidth = 640; + desc.OutputHeight = 480; + + DMFTCHECKHR_GOTO(pVideoDevice->CreateVideoProcessorEnumerator(&desc, &spVideoProcEnum), done); + DMFTCHECKHR_GOTO(spVideoProcEnum->GetVideoProcessorCaps(&caps), done); + + if (!(caps.FeatureCaps & D3D11_VIDEO_PROCESSOR_FEATURE_CAPS_ROTATION)) + { + hr = E_NOTIMPL; + } +done: + return hr; +} + + + +HRESULT IsDXOptimal(_In_ IUnknown *pDeviceManager, _Out_ BOOL *pIsOptimal) +{ + HRESULT hr = S_OK; + ComPtr spDXGIDeviceManager = nullptr; + ComPtr spD3D11 = nullptr; + ComPtr spVideoDevice = nullptr; + HANDLE hDevice = NULL; + BOOL locked = FALSE; + + *pIsOptimal = false; + DMFTCHECKNULL_GOTO( pDeviceManager, done, E_INVALIDARG ); + DMFTCHECKHR_GOTO( pDeviceManager->QueryInterface(IID_PPV_ARGS( &spDXGIDeviceManager )), done ); + DMFTCHECKHR_GOTO( spDXGIDeviceManager->OpenDeviceHandle( &hDevice ), done ); + DMFTCHECKHR_GOTO( spDXGIDeviceManager->LockDevice( hDevice, IID_PPV_ARGS( &spD3D11 ), TRUE), done ); + locked = TRUE; + DMFTCHECKHR_GOTO( spD3D11.As(&spVideoDevice), done ); + + if (spD3D11->GetFeatureLevel() <= D3D_FEATURE_LEVEL_9_3) + { + if (FAILED(CheckDX9RotationSupport(spVideoDevice.Get()))) + { + goto done; + } + } + *pIsOptimal = TRUE; +done: + if (hDevice) + { + if (locked) + { + spDXGIDeviceManager->UnlockDevice(hDevice, false); + } + spDXGIDeviceManager->CloseDeviceHandle(hDevice); + } + return hr; +} + + +// +// Used to check if the pin is a custom pin or not!! +// + +STDMETHODIMP CheckCustomPin( + _In_ CInPin * pPin, + _Inout_ PBOOL pIsCustom + ) +{ + HRESULT hr = S_OK; + DMFTCHECKNULL_GOTO( pPin, done, E_INVALIDARG); + DMFTCHECKNULL_GOTO( pIsCustom, done, E_INVALIDARG); + + *pIsCustom = false; + +#if defined _CPPRTTI + if (dynamic_cast(pPin) != nullptr) + { + *pIsCustom = true; + } + else +#endif + { + GUID categoryGUID = GUID_NULL; + if (SUCCEEDED(pPin->GetGUID(MF_DEVICESTREAM_STREAM_CATEGORY, &categoryGUID)) + && IsEqualCLSID(categoryGUID, AVSTREAM_CUSTOM_PIN_IMAGE)) + { + *pIsCustom = true; + } + } +done: + return hr; +} + diff --git a/avstream/sampledevicemft/stdafx.h b/avstream/sampledevicemft/stdafx.h new file mode 100644 index 000000000..58cd95dca --- /dev/null +++ b/avstream/sampledevicemft/stdafx.h @@ -0,0 +1,38 @@ +// +// Copyright (C) Microsoft. All rights reserved. +// +#pragma once + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +using namespace std; +#include +#include +using namespace ABI::Windows::Foundation; +using namespace Microsoft::WRL; diff --git a/avstream/sampledevicemft/stdafxsrc.cpp b/avstream/sampledevicemft/stdafxsrc.cpp new file mode 100644 index 000000000..1577c4e3b --- /dev/null +++ b/avstream/sampledevicemft/stdafxsrc.cpp @@ -0,0 +1 @@ +#include "stdafx.h" \ No newline at end of file diff --git a/avstream/samplemft0/SampleMft0.sln b/avstream/samplemft0/SampleMft0.sln index 64e3c78f5..c8951b323 100644 --- a/avstream/samplemft0/SampleMft0.sln +++ b/avstream/samplemft0/SampleMft0.sln @@ -3,7 +3,7 @@ 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", "{88F464FD-A174-4CAD-BB13-A599CA5873D6}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SampleMft0", "SampleMft0.vcxproj", "{6AE575B1-1F8F-4CCD-B889-A18D1BC8A60D}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {88F464FD-A174-4CAD-BB13-A599CA5873D6}.Debug|Win32.ActiveCfg = Debug|Win32 - {88F464FD-A174-4CAD-BB13-A599CA5873D6}.Debug|Win32.Build.0 = Debug|Win32 - {88F464FD-A174-4CAD-BB13-A599CA5873D6}.Release|Win32.ActiveCfg = Release|Win32 - {88F464FD-A174-4CAD-BB13-A599CA5873D6}.Release|Win32.Build.0 = Release|Win32 - {88F464FD-A174-4CAD-BB13-A599CA5873D6}.Debug|x64.ActiveCfg = Debug|x64 - {88F464FD-A174-4CAD-BB13-A599CA5873D6}.Debug|x64.Build.0 = Debug|x64 - {88F464FD-A174-4CAD-BB13-A599CA5873D6}.Release|x64.ActiveCfg = Release|x64 - {88F464FD-A174-4CAD-BB13-A599CA5873D6}.Release|x64.Build.0 = Release|x64 + {6AE575B1-1F8F-4CCD-B889-A18D1BC8A60D}.Debug|Win32.ActiveCfg = Debug|Win32 + {6AE575B1-1F8F-4CCD-B889-A18D1BC8A60D}.Debug|Win32.Build.0 = Debug|Win32 + {6AE575B1-1F8F-4CCD-B889-A18D1BC8A60D}.Release|Win32.ActiveCfg = Release|Win32 + {6AE575B1-1F8F-4CCD-B889-A18D1BC8A60D}.Release|Win32.Build.0 = Release|Win32 + {6AE575B1-1F8F-4CCD-B889-A18D1BC8A60D}.Debug|x64.ActiveCfg = Debug|x64 + {6AE575B1-1F8F-4CCD-B889-A18D1BC8A60D}.Debug|x64.Build.0 = Debug|x64 + {6AE575B1-1F8F-4CCD-B889-A18D1BC8A60D}.Release|x64.ActiveCfg = Release|x64 + {6AE575B1-1F8F-4CCD-B889-A18D1BC8A60D}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/avstream/samplemft0/SampleMft0.vcxproj b/avstream/samplemft0/SampleMft0.vcxproj index ef9548b88..7d9d2267e 100644 --- a/avstream/samplemft0/SampleMft0.vcxproj +++ b/avstream/samplemft0/SampleMft0.vcxproj @@ -19,11 +19,11 @@ - {88F464FD-A174-4CAD-BB13-A599CA5873D6} + {6AE575B1-1F8F-4CCD-B889-A18D1BC8A60D} $(MSBuildProjectName) Debug Win32 - {01B26604-84E9-4055-B490-900335F245E9} + {92586FC2-7791-4E03-B6A4-4CD4324F57F2} @@ -207,7 +207,6 @@ - diff --git a/avstream/samplemft0/SampleMft0.vcxproj.Filters b/avstream/samplemft0/SampleMft0.vcxproj.Filters index fe1c62a23..8b59ec03c 100644 --- a/avstream/samplemft0/SampleMft0.vcxproj.Filters +++ b/avstream/samplemft0/SampleMft0.vcxproj.Filters @@ -3,15 +3,15 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {1D447F88-CC6C-422F-8181-8A5880E77E15} + {C4EF77A5-A475-4447-8C75-D63EAD531425} h;hpp;hxx;hm;inl;inc;xsd - {E18FF6EE-9EED-46A0-A204-040342E4F4EF} + {7668EFE9-B330-4217-95EC-4AAC7F64B20C} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {E441010A-8812-4C39-8275-FCAE9832E077} + {42BF196B-979A-4457-99D6-E05389FF4B6C} diff --git a/biometrics/Package/package.VcxProj b/biometrics/Package/package.VcxProj new file mode 100644 index 000000000..985a9977f --- /dev/null +++ b/biometrics/Package/package.VcxProj @@ -0,0 +1,93 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + + {105C343E-EFA4-45C3-A954-A48D79504BE6} + + + {D6372638-968B-4FDC-A56F-C70C339E1448} + + + {B7ABEA68-C3BF-4E04-A77D-10473902A5E3} + + + {F97C7BE3-C238-4575-BBAF-5C5E42C7D6BF} + + + + WindowsKernelModeDriver10.0 + Utility + Package + true + Debug + + + + {FB7B90E0-BABC-4B40-ACCF-ED9EE8750AD6} + {970D30FD-F299-4C8C-A760-05F2F25ECD0B} + $(MSBuildProjectName) + + + Windows10 + true + + + Windows10 + false + + + Windows10 + true + + + Windows10 + false + + + + + + + + + + + DbgengRemoteDebugger + False + None + + + + + + %PathToInf% + False + False + True + + 133563 + + + + + + + \ No newline at end of file diff --git a/biometrics/Package/package.VcxProj.Filters b/biometrics/Package/package.VcxProj.Filters new file mode 100644 index 000000000..8ca22a7b6 --- /dev/null +++ b/biometrics/Package/package.VcxProj.Filters @@ -0,0 +1,21 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {ED68A319-79DA-48C5-ABAD-D38444CA0353} + + + h;hpp;hxx;hm;inl;inc;xsd + {D611AB41-169F-4F7A-A139-5B50EE2D98C2} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {FD8AE17A-BEDC-4AA2-8C00-7C34727334E1} + + + inf;inv;inx;mof;mc; + {1227B1E4-BCC6-4385-A177-87FA8995BFE4} + + + \ No newline at end of file diff --git a/biometrics/ReadMe.md b/biometrics/ReadMe.md new file mode 100644 index 000000000..d012d6bc1 --- /dev/null +++ b/biometrics/ReadMe.md @@ -0,0 +1,78 @@ +Windows Biometric Driver Samples (UMDF Version 1) +================================================= + +The Windows Biometric Driver Samples contain the Windows Biometric Driver Interface sample and the Windows Biometric Service Adapter samples. + +The following table describes the samples contained in this sample set: + +*Windows Biometric Driver Interface* +This sample implements the Windows Biometric Driver Interface (WBDI). It contains skeleton code for handling the mandatory IOCTLs necessary to interoperate with the Windows Biometric Framework. A WBDI driver can be deployed in conjunction with an engine adapter DLL to allow a sensor to be exposed from the Windows Biometric Framework. This sample has been written to make use of the UMDF framework, which allows for ease of development and system stability. + +*Windows Biometric Service Adapters* +These samples provide skeleton code that developers can use as a basis for writing Sensor, Engine, and Storage Adapters for the Windows Biometric Service. Note that the stubs in these samples are non-functional, and Adapter writers will need to follow the programming guidelines in the WinBio Service documentation in order produce a working Adapter component. + + +Build the sample +---------------- + +For information on how to build a driver solution using Microsoft Visual Studio, see [Building a Driver](http://msdn.microsoft.com/en-us/library/windows/hardware/ff554644). + +**Note**  Starting in Windows 8.1, the WDK no longer contains the co-installers by default. You can obtain the co-installers by downloading the *wdfcoinstaller.msi* package from [WDK 8 Redistributable Components](http://go.microsoft.com/fwlink/p/?LinkID=226396). + +Run the sample +-------------- + +Installation +------------ + +### Windows Biometric Driver Interface + +The sample requires the use of a suitable fingerprint sensor. It does not capture real data, but it does create a biometric unit in the Windows Biometric Framework. + +### Windows Biometric Service Adapters + +To write and test an Adapter plug-in, it will be necessary to have a biometric device and a working WBDI driver for the device. + +Adapters are generally installed along with the WBDI driver for the corresponding device. Consult the WinBio Service documentation for information on the INF file commands used for installing Adapters. Note that Adapters are trusted plug-in components, so they can only be installed using a privileged account. + +Design and Operation +-------------------- + +### Windows Biometric Driver Interface + +This sample is taken from the UMDF FX2 sample and has been modified to expose WBDI. It has the necessary hooks to make this a WBDI driver: + +- Installs WBDI driver, including correct class GUID settings and icons, and registry settings for Windows Biometric Framework configuration. +- Publishes WBDI device interface. +- Supports all the mandatory [WBDI IOCTLs](http://msdn.microsoft.com/en-us/library/windows/hardware/ff536414). +- Supports cancellation. +- Can be opened with exclusivity. + +All of these things are required for the Windows Biometric Framework service to recognize this device as a biometric device and set up a Biometric Unit. It allows the service to properly control the device. + +The sample makes use of ATL support for simplified handling of COM objects with UMDF. + +The driver makes use of a parallel queue so that multiple requests can be outstanding at once. + +It uses device level-locking to simplify internal thread synchronization. This means that only one framework callback can be active at a time. + +It supports cancellation of any IOCTL which may be I/O intensive, particularly a capture IOCTL. This sample does not have a real capture mechanism, so it is simulated by a 5 second delay returning a capture IOCTL. Cancellation is supported through the mechanism exposed by WUDF, with a callback for a request object. Cancellation support is required for all IOCTLs. + +There are hooks for all [WBDI IOCTLs](http://msdn.microsoft.com/en-us/library/windows/hardware/ff536414), including the optional IOCTLs. + +PnP is very simple for this driver. It needs to only implement OnPrepareHardware and OnReleaseHardware from IPnpCallbackHardware. + +Some device drivers may need to keep several pending reads to the WinUsb I/O target in order to properly flush all I/O that comes from the device during a capture. + +### Windows Biometric Service Adapters + +WinBio Adapters are plug-in components that provide a standard interface layer between the Windows Biometric Service and a biometric device. The WinBio Service recognizes three types of Adapters: + +- Sensor Adapters - expose the sample-capture capabilities of the biometric device. +- Engine Adapters - expose the sample manipulation, template generation, and matching capabilities of the device. +- Storage Adapters - expose the template storage and retrieval capabilities of the device. + +For many simple biometric devices, it will only be necessary to write a WBDI driver for the device plus an Engine Adapter to perform matching operations. Consult the programming guidelines in the WinBio Service documentation for more details. + +Each Adapter sample contains a well-known interface-discovery function, whose job is to return the address of a function dispatch table. When the WinBio Service loads an Adapter plug-in, it uses the interface-discovery function to locate the dispatch table, and then calls various methods in the table to communicate with the biometric device. The purpose, arguments, and return codes of each Adapter method are described in the WinBio Service programming guidelines. More information on adapter plug-ins is available at [WBDI Plug-in Reference](http://msdn.microsoft.com/en-us/library/windows/desktop/dd401553(v=vs.85).aspx). + diff --git a/biometrics/WBDIsample.sln b/biometrics/WBDIsample.sln new file mode 100644 index 000000000..a53ad2d3e --- /dev/null +++ b/biometrics/WBDIsample.sln @@ -0,0 +1,90 @@ + +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", "{6F33BAE3-DB33-414E-B171-55970896CDA4}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Storage_adapter", "Storage_adapter", "{4F587A41-3B93-4D50-8618-209293411F50}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Adapters", "Adapters", "{72335D2B-AB5E-4320-BBDD-B5A754017728}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sensor_adapter", "Sensor_adapter", "{08A96B90-4662-4593-814D-28D16DC0D9DA}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Engine_adapter", "Engine_adapter", "{49B24D3B-CFD4-43D8-BF54-6E34546EE7C5}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Driver", "Driver", "{0E4A0D1D-41F3-42EB-A79C-465B3C28744E}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "package", "Package\package.VcxProj", "{FB7B90E0-BABC-4B40-ACCF-ED9EE8750AD6}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "StorageAdapter", "adapters\storage_adapter\StorageAdapter.vcxproj", "{B7ABEA68-C3BF-4E04-A77D-10473902A5E3}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SensorAdapter", "adapters\sensor_adapter\SensorAdapter.vcxproj", "{D6372638-968B-4FDC-A56F-C70C339E1448}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "EngineAdapter", "adapters\engine_adapter\EngineAdapter.vcxproj", "{105C343E-EFA4-45C3-A954-A48D79504BE6}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WudfBioUsbSample", "driver\WudfBioUsbSample.vcxproj", "{F97C7BE3-C238-4575-BBAF-5C5E42C7D6BF}" +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 + {FB7B90E0-BABC-4B40-ACCF-ED9EE8750AD6}.Debug|Win32.ActiveCfg = Debug|Win32 + {FB7B90E0-BABC-4B40-ACCF-ED9EE8750AD6}.Debug|Win32.Build.0 = Debug|Win32 + {FB7B90E0-BABC-4B40-ACCF-ED9EE8750AD6}.Release|Win32.ActiveCfg = Release|Win32 + {FB7B90E0-BABC-4B40-ACCF-ED9EE8750AD6}.Release|Win32.Build.0 = Release|Win32 + {FB7B90E0-BABC-4B40-ACCF-ED9EE8750AD6}.Debug|x64.ActiveCfg = Debug|x64 + {FB7B90E0-BABC-4B40-ACCF-ED9EE8750AD6}.Debug|x64.Build.0 = Debug|x64 + {FB7B90E0-BABC-4B40-ACCF-ED9EE8750AD6}.Release|x64.ActiveCfg = Release|x64 + {FB7B90E0-BABC-4B40-ACCF-ED9EE8750AD6}.Release|x64.Build.0 = Release|x64 + {B7ABEA68-C3BF-4E04-A77D-10473902A5E3}.Debug|Win32.ActiveCfg = Debug|Win32 + {B7ABEA68-C3BF-4E04-A77D-10473902A5E3}.Debug|Win32.Build.0 = Debug|Win32 + {B7ABEA68-C3BF-4E04-A77D-10473902A5E3}.Release|Win32.ActiveCfg = Release|Win32 + {B7ABEA68-C3BF-4E04-A77D-10473902A5E3}.Release|Win32.Build.0 = Release|Win32 + {B7ABEA68-C3BF-4E04-A77D-10473902A5E3}.Debug|x64.ActiveCfg = Debug|x64 + {B7ABEA68-C3BF-4E04-A77D-10473902A5E3}.Debug|x64.Build.0 = Debug|x64 + {B7ABEA68-C3BF-4E04-A77D-10473902A5E3}.Release|x64.ActiveCfg = Release|x64 + {B7ABEA68-C3BF-4E04-A77D-10473902A5E3}.Release|x64.Build.0 = Release|x64 + {D6372638-968B-4FDC-A56F-C70C339E1448}.Debug|Win32.ActiveCfg = Debug|Win32 + {D6372638-968B-4FDC-A56F-C70C339E1448}.Debug|Win32.Build.0 = Debug|Win32 + {D6372638-968B-4FDC-A56F-C70C339E1448}.Release|Win32.ActiveCfg = Release|Win32 + {D6372638-968B-4FDC-A56F-C70C339E1448}.Release|Win32.Build.0 = Release|Win32 + {D6372638-968B-4FDC-A56F-C70C339E1448}.Debug|x64.ActiveCfg = Debug|x64 + {D6372638-968B-4FDC-A56F-C70C339E1448}.Debug|x64.Build.0 = Debug|x64 + {D6372638-968B-4FDC-A56F-C70C339E1448}.Release|x64.ActiveCfg = Release|x64 + {D6372638-968B-4FDC-A56F-C70C339E1448}.Release|x64.Build.0 = Release|x64 + {105C343E-EFA4-45C3-A954-A48D79504BE6}.Debug|Win32.ActiveCfg = Debug|Win32 + {105C343E-EFA4-45C3-A954-A48D79504BE6}.Debug|Win32.Build.0 = Debug|Win32 + {105C343E-EFA4-45C3-A954-A48D79504BE6}.Release|Win32.ActiveCfg = Release|Win32 + {105C343E-EFA4-45C3-A954-A48D79504BE6}.Release|Win32.Build.0 = Release|Win32 + {105C343E-EFA4-45C3-A954-A48D79504BE6}.Debug|x64.ActiveCfg = Debug|x64 + {105C343E-EFA4-45C3-A954-A48D79504BE6}.Debug|x64.Build.0 = Debug|x64 + {105C343E-EFA4-45C3-A954-A48D79504BE6}.Release|x64.ActiveCfg = Release|x64 + {105C343E-EFA4-45C3-A954-A48D79504BE6}.Release|x64.Build.0 = Release|x64 + {F97C7BE3-C238-4575-BBAF-5C5E42C7D6BF}.Debug|Win32.ActiveCfg = Debug|Win32 + {F97C7BE3-C238-4575-BBAF-5C5E42C7D6BF}.Debug|Win32.Build.0 = Debug|Win32 + {F97C7BE3-C238-4575-BBAF-5C5E42C7D6BF}.Release|Win32.ActiveCfg = Release|Win32 + {F97C7BE3-C238-4575-BBAF-5C5E42C7D6BF}.Release|Win32.Build.0 = Release|Win32 + {F97C7BE3-C238-4575-BBAF-5C5E42C7D6BF}.Debug|x64.ActiveCfg = Debug|x64 + {F97C7BE3-C238-4575-BBAF-5C5E42C7D6BF}.Debug|x64.Build.0 = Debug|x64 + {F97C7BE3-C238-4575-BBAF-5C5E42C7D6BF}.Release|x64.ActiveCfg = Release|x64 + {F97C7BE3-C238-4575-BBAF-5C5E42C7D6BF}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {FB7B90E0-BABC-4B40-ACCF-ED9EE8750AD6} = {6F33BAE3-DB33-414E-B171-55970896CDA4} + {B7ABEA68-C3BF-4E04-A77D-10473902A5E3} = {4F587A41-3B93-4D50-8618-209293411F50} + {D6372638-968B-4FDC-A56F-C70C339E1448} = {08A96B90-4662-4593-814D-28D16DC0D9DA} + {105C343E-EFA4-45C3-A954-A48D79504BE6} = {49B24D3B-CFD4-43D8-BF54-6E34546EE7C5} + {F97C7BE3-C238-4575-BBAF-5C5E42C7D6BF} = {0E4A0D1D-41F3-42EB-A79C-465B3C28744E} + {4F587A41-3B93-4D50-8618-209293411F50} = {72335D2B-AB5E-4320-BBDD-B5A754017728} + {08A96B90-4662-4593-814D-28D16DC0D9DA} = {72335D2B-AB5E-4320-BBDD-B5A754017728} + {49B24D3B-CFD4-43D8-BF54-6E34546EE7C5} = {72335D2B-AB5E-4320-BBDD-B5A754017728} + EndGlobalSection +EndGlobal diff --git a/biometrics/adapters/engine_adapter/EngineAdapter.cpp b/biometrics/adapters/engine_adapter/EngineAdapter.cpp new file mode 100644 index 000000000..229611248 --- /dev/null +++ b/biometrics/adapters/engine_adapter/EngineAdapter.cpp @@ -0,0 +1,665 @@ +/*++ + + 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 + +Module Name: + + EngineAdapter.cpp + +Abstract: + + This module contains a stub implementation of an Engine Adapter + plug-in for the Windows Biometric service. + +Author: + + - + +Environment: + + Win32, user mode only. + +Revision History: + +NOTES: + + (None) + +--*/ + +/////////////////////////////////////////////////////////////////////////////// +// +// Header files... +// +/////////////////////////////////////////////////////////////////////////////// +#include "precomp.h" +#include "winbio_adapter.h" +#include "EngineAdapter.h" + + +/////////////////////////////////////////////////////////////////////////////// +// +// Forward declarations for the Engine Adapter's interface routines... +// +/////////////////////////////////////////////////////////////////////////////// +static HRESULT +WINAPI +EngineAdapterAttach( + _Inout_ PWINBIO_PIPELINE Pipeline + ); + +static HRESULT +WINAPI +EngineAdapterDetach( + _Inout_ PWINBIO_PIPELINE Pipeline + ); + +static HRESULT +WINAPI +EngineAdapterClearContext( + _Inout_ PWINBIO_PIPELINE Pipeline + ); + +static HRESULT +WINAPI +EngineAdapterEndOperation( + _Inout_ PWINBIO_PIPELINE Pipeline + ); + +static HRESULT +WINAPI +EngineAdapterQueryPreferredFormat( + _Inout_ PWINBIO_PIPELINE Pipeline, + _Out_ PWINBIO_REGISTERED_FORMAT StandardFormat, + _Out_ PWINBIO_UUID VendorFormat + ); + +static HRESULT +WINAPI +EngineAdapterQueryIndexVectorSize( + _Inout_ PWINBIO_PIPELINE Pipeline, + _Out_ PSIZE_T IndexElementCount + ); + +static HRESULT +WINAPI +EngineAdapterQueryHashAlgorithms( + _Inout_ PWINBIO_PIPELINE Pipeline, + _Out_ PSIZE_T AlgorithmCount, + _Out_ PSIZE_T AlgorithmBufferSize, + _Out_ PUCHAR *AlgorithmBuffer + ); + +static HRESULT +WINAPI +EngineAdapterSetHashAlgorithm( + _Inout_ PWINBIO_PIPELINE Pipeline, + _In_ SIZE_T AlgorithmBufferSize, + _In_ PUCHAR AlgorithmBuffer + ); + +static HRESULT +WINAPI +EngineAdapterAcceptSampleHint( + _Inout_ PWINBIO_PIPELINE Pipeline, + _Out_ PSIZE_T SampleHint + ); + +static HRESULT +WINAPI +EngineAdapterAcceptSampleData( + _Inout_ PWINBIO_PIPELINE Pipeline, + _In_ PWINBIO_BIR SampleBuffer, + _In_ SIZE_T SampleSize, + _In_ WINBIO_BIR_PURPOSE Purpose, + _Out_ PWINBIO_REJECT_DETAIL RejectDetail + ); + +static HRESULT +WINAPI +EngineAdapterExportEngineData( + _Inout_ PWINBIO_PIPELINE Pipeline, + _In_ WINBIO_BIR_DATA_FLAGS Flags, + _Out_ PWINBIO_BIR *SampleBuffer, + _Out_ PSIZE_T SampleSize + ); + +static HRESULT +WINAPI +EngineAdapterVerifyFeatureSet( + _Inout_ PWINBIO_PIPELINE Pipeline, + _In_ PWINBIO_IDENTITY Identity, + _In_ WINBIO_BIOMETRIC_SUBTYPE SubFactor, + _Out_ PBOOLEAN Match, + _Out_ PUCHAR *PayloadBlob, + _Out_ PSIZE_T PayloadBlobSize, + _Out_ PUCHAR *HashValue, + _Out_ PSIZE_T HashSize, + _Out_ PWINBIO_REJECT_DETAIL RejectDetail + ); + +static HRESULT +WINAPI +EngineAdapterIdentifyFeatureSet( + _Inout_ PWINBIO_PIPELINE Pipeline, + _Out_ PWINBIO_IDENTITY Identity, + _Out_ PWINBIO_BIOMETRIC_SUBTYPE SubFactor, + _Out_ PUCHAR *PayloadBlob, + _Out_ PSIZE_T PayloadBlobSize, + _Out_ PUCHAR *HashValue, + _Out_ PSIZE_T HashSize, + _Out_ PWINBIO_REJECT_DETAIL RejectDetail + ); + +static HRESULT +WINAPI +EngineAdapterCreateEnrollment( + _Inout_ PWINBIO_PIPELINE Pipeline + ); + +static HRESULT +WINAPI +EngineAdapterUpdateEnrollment( + _Inout_ PWINBIO_PIPELINE Pipeline, + _Out_ PWINBIO_REJECT_DETAIL RejectDetail + ); + +static HRESULT +WINAPI +EngineAdapterGetEnrollmentStatus( + _Inout_ PWINBIO_PIPELINE Pipeline, + _Out_ PWINBIO_REJECT_DETAIL RejectDetail + ); + +static HRESULT +WINAPI +EngineAdapterGetEnrollmentHash( + _Inout_ PWINBIO_PIPELINE Pipeline, + _Out_ PUCHAR *HashValue, + _Out_ PSIZE_T HashSize + ); + +static HRESULT +WINAPI +EngineAdapterCheckForDuplicate( + _Inout_ PWINBIO_PIPELINE Pipeline, + _Out_ PWINBIO_IDENTITY Identity, + _Out_ PWINBIO_BIOMETRIC_SUBTYPE SubFactor, + _Out_ PBOOLEAN Duplicate + ); + +static HRESULT +WINAPI +EngineAdapterCommitEnrollment( + _Inout_ PWINBIO_PIPELINE Pipeline, + _In_ PWINBIO_IDENTITY Identity, + _In_ WINBIO_BIOMETRIC_SUBTYPE SubFactor, + _In_ PUCHAR PayloadBlob, + _In_ SIZE_T PayloadBlobSize + ); + +static HRESULT +WINAPI +EngineAdapterDiscardEnrollment( + _Inout_ PWINBIO_PIPELINE Pipeline + ); + +static HRESULT +WINAPI +EngineAdapterControlUnit( + _Inout_ PWINBIO_PIPELINE Pipeline, + _In_ ULONG ControlCode, + _In_ PUCHAR SendBuffer, + _In_ SIZE_T SendBufferSize, + _In_ PUCHAR ReceiveBuffer, + _In_ SIZE_T ReceiveBufferSize, + _Out_ PSIZE_T ReceiveDataSize, + _Out_ PULONG OperationStatus + ); + +static HRESULT +WINAPI +EngineAdapterControlUnitPrivileged( + _Inout_ PWINBIO_PIPELINE Pipeline, + _In_ ULONG ControlCode, + _In_ PUCHAR SendBuffer, + _In_ SIZE_T SendBufferSize, + _In_ PUCHAR ReceiveBuffer, + _In_ SIZE_T ReceiveBufferSize, + _Out_ PSIZE_T ReceiveDataSize, + _Out_ PULONG OperationStatus + ); +//----------------------------------------------------------------------------- + + +/////////////////////////////////////////////////////////////////////////////// +// +// Interface dispatch table +// +/////////////////////////////////////////////////////////////////////////////// +static WINBIO_ENGINE_INTERFACE g_EngineInterface = { + WINBIO_ENGINE_INTERFACE_VERSION_1, + WINBIO_ADAPTER_TYPE_ENGINE, + sizeof(WINBIO_ENGINE_INTERFACE), + {0xb876fdc8, 0x34e7, 0x471a, {0x82, 0xc8, 0x9c, 0xba, 0x6a, 0x35, 0x38, 0xec}}, + + EngineAdapterAttach, + EngineAdapterDetach, + EngineAdapterClearContext, + EngineAdapterQueryPreferredFormat, + EngineAdapterQueryIndexVectorSize, + EngineAdapterQueryHashAlgorithms, + EngineAdapterSetHashAlgorithm, + EngineAdapterAcceptSampleHint, + EngineAdapterAcceptSampleData, + EngineAdapterExportEngineData, + EngineAdapterVerifyFeatureSet, + EngineAdapterIdentifyFeatureSet, + EngineAdapterCreateEnrollment, + EngineAdapterUpdateEnrollment, + EngineAdapterGetEnrollmentStatus, + EngineAdapterGetEnrollmentHash, + EngineAdapterCheckForDuplicate, + EngineAdapterCommitEnrollment, + EngineAdapterDiscardEnrollment, + EngineAdapterControlUnit, + EngineAdapterControlUnitPrivileged +}; +//----------------------------------------------------------------------------- + + +/////////////////////////////////////////////////////////////////////////////// +// +// Mandatory DLL entrypoint function. +// +/////////////////////////////////////////////////////////////////////////////// +BOOL APIENTRY +DllMain( + HANDLE ModuleHandle, + DWORD ReasonForCall, + LPVOID Reserved + ) +{ + UNREFERENCED_PARAMETER(ModuleHandle); + UNREFERENCED_PARAMETER(ReasonForCall); + UNREFERENCED_PARAMETER(Reserved); + + return TRUE; +} +//----------------------------------------------------------------------------- + + +/////////////////////////////////////////////////////////////////////////////// +// +// Well-known interface-discovery function exported by the Engine Adapter +// +/////////////////////////////////////////////////////////////////////////////// +HRESULT +WINAPI +WbioQueryEngineInterface( + _Out_ PWINBIO_ENGINE_INTERFACE *EngineInterface + ) +{ + *EngineInterface = &g_EngineInterface; + return S_OK; +} +//----------------------------------------------------------------------------- + + +/////////////////////////////////////////////////////////////////////////////// +// +// Engine Adapter action routines +// +/////////////////////////////////////////////////////////////////////////////// +static HRESULT +WINAPI +EngineAdapterAttach( + _Inout_ PWINBIO_PIPELINE Pipeline + ) +{ + UNREFERENCED_PARAMETER(Pipeline); + + return E_NOTIMPL; +} +//----------------------------------------------------------------------------- + +static HRESULT +WINAPI +EngineAdapterDetach( + _Inout_ PWINBIO_PIPELINE Pipeline + ) +{ + UNREFERENCED_PARAMETER(Pipeline); + + return E_NOTIMPL; +} +//----------------------------------------------------------------------------- + +static HRESULT +WINAPI +EngineAdapterClearContext( + _Inout_ PWINBIO_PIPELINE Pipeline + ) +{ + UNREFERENCED_PARAMETER(Pipeline); + return E_NOTIMPL; +} +//----------------------------------------------------------------------------- + +static HRESULT +WINAPI +EngineAdapterQueryPreferredFormat( + _Inout_ PWINBIO_PIPELINE Pipeline, + _Out_ PWINBIO_REGISTERED_FORMAT StandardFormat, + _Out_ PWINBIO_UUID VendorFormat + ) +{ + UNREFERENCED_PARAMETER(Pipeline); + UNREFERENCED_PARAMETER(StandardFormat); + UNREFERENCED_PARAMETER(VendorFormat); + return E_NOTIMPL; +} +//----------------------------------------------------------------------------- + +static HRESULT +WINAPI +EngineAdapterQueryIndexVectorSize( + _Inout_ PWINBIO_PIPELINE Pipeline, + _Out_ PSIZE_T IndexElementCount + ) +{ + UNREFERENCED_PARAMETER(Pipeline); + UNREFERENCED_PARAMETER(IndexElementCount); + return E_NOTIMPL; +} +//----------------------------------------------------------------------------- + +static HRESULT +WINAPI +EngineAdapterQueryHashAlgorithms( + _Inout_ PWINBIO_PIPELINE Pipeline, + _Out_ PSIZE_T AlgorithmCount, + _Out_ PSIZE_T AlgorithmBufferSize, + _Out_ PUCHAR *AlgorithmBuffer + ) +{ + UNREFERENCED_PARAMETER(Pipeline); + UNREFERENCED_PARAMETER(AlgorithmCount); + UNREFERENCED_PARAMETER(AlgorithmBufferSize); + UNREFERENCED_PARAMETER(AlgorithmBuffer); + return E_NOTIMPL; +} +//----------------------------------------------------------------------------- + +static HRESULT +WINAPI +EngineAdapterSetHashAlgorithm( + _Inout_ PWINBIO_PIPELINE Pipeline, + _In_ SIZE_T AlgorithmBufferSize, + _In_ PUCHAR AlgorithmBuffer + ) +{ + UNREFERENCED_PARAMETER(Pipeline); + UNREFERENCED_PARAMETER(AlgorithmBufferSize); + UNREFERENCED_PARAMETER(AlgorithmBuffer); + return E_NOTIMPL; +} +//----------------------------------------------------------------------------- + +static HRESULT +WINAPI +EngineAdapterAcceptSampleHint( + _Inout_ PWINBIO_PIPELINE Pipeline, + _Out_ PSIZE_T SampleHint + ) +{ + UNREFERENCED_PARAMETER(Pipeline); + UNREFERENCED_PARAMETER(SampleHint); + return E_NOTIMPL; +} +//----------------------------------------------------------------------------- + +static HRESULT +WINAPI +EngineAdapterAcceptSampleData( + _Inout_ PWINBIO_PIPELINE Pipeline, + _In_ PWINBIO_BIR SampleBuffer, + _In_ SIZE_T SampleSize, + _In_ WINBIO_BIR_PURPOSE Purpose, + _Out_ PWINBIO_REJECT_DETAIL RejectDetail + ) +{ + UNREFERENCED_PARAMETER(Pipeline); + UNREFERENCED_PARAMETER(SampleBuffer); + UNREFERENCED_PARAMETER(SampleSize); + UNREFERENCED_PARAMETER(Purpose); + UNREFERENCED_PARAMETER(RejectDetail); + return E_NOTIMPL; +} +//----------------------------------------------------------------------------- + +static HRESULT +WINAPI +EngineAdapterExportEngineData( + _Inout_ PWINBIO_PIPELINE Pipeline, + _In_ WINBIO_BIR_DATA_FLAGS Flags, + _Out_ PWINBIO_BIR *SampleBuffer, + _Out_ PSIZE_T SampleSize + ) +{ + UNREFERENCED_PARAMETER(Pipeline); + UNREFERENCED_PARAMETER(Flags); + UNREFERENCED_PARAMETER(SampleBuffer); + UNREFERENCED_PARAMETER(SampleSize); + return E_NOTIMPL; +} +//----------------------------------------------------------------------------- + +static HRESULT +WINAPI +EngineAdapterVerifyFeatureSet( + _Inout_ PWINBIO_PIPELINE Pipeline, + _In_ PWINBIO_IDENTITY Identity, + _In_ WINBIO_BIOMETRIC_SUBTYPE SubFactor, + _Out_ PBOOLEAN Match, + _Out_ PUCHAR *PayloadBlob, + _Out_ PSIZE_T PayloadBlobSize, + _Out_ PUCHAR *HashValue, + _Out_ PSIZE_T HashSize, + _Out_ PWINBIO_REJECT_DETAIL RejectDetail + ) +{ + UNREFERENCED_PARAMETER(Pipeline); + UNREFERENCED_PARAMETER(Identity); + UNREFERENCED_PARAMETER(SubFactor); + UNREFERENCED_PARAMETER(Match); + UNREFERENCED_PARAMETER(PayloadBlob); + UNREFERENCED_PARAMETER(PayloadBlobSize); + UNREFERENCED_PARAMETER(HashValue); + UNREFERENCED_PARAMETER(HashSize); + UNREFERENCED_PARAMETER(RejectDetail); + return E_NOTIMPL; +} +//----------------------------------------------------------------------------- + +static HRESULT +WINAPI +EngineAdapterIdentifyFeatureSet( + _Inout_ PWINBIO_PIPELINE Pipeline, + _Out_ PWINBIO_IDENTITY Identity, + _Out_ PWINBIO_BIOMETRIC_SUBTYPE SubFactor, + _Out_ PUCHAR *PayloadBlob, + _Out_ PSIZE_T PayloadBlobSize, + _Out_ PUCHAR *HashValue, + _Out_ PSIZE_T HashSize, + _Out_ PWINBIO_REJECT_DETAIL RejectDetail + ) +{ + UNREFERENCED_PARAMETER(Pipeline); + UNREFERENCED_PARAMETER(Identity); + UNREFERENCED_PARAMETER(SubFactor); + UNREFERENCED_PARAMETER(PayloadBlob); + UNREFERENCED_PARAMETER(PayloadBlobSize); + UNREFERENCED_PARAMETER(HashValue); + UNREFERENCED_PARAMETER(HashSize); + UNREFERENCED_PARAMETER(RejectDetail); + return E_NOTIMPL; +} +//----------------------------------------------------------------------------- + +static HRESULT +WINAPI +EngineAdapterCreateEnrollment( + _Inout_ PWINBIO_PIPELINE Pipeline + ) +{ + UNREFERENCED_PARAMETER(Pipeline); + return E_NOTIMPL; +} +//----------------------------------------------------------------------------- + +static HRESULT +WINAPI +EngineAdapterUpdateEnrollment( + _Inout_ PWINBIO_PIPELINE Pipeline, + _Out_ PWINBIO_REJECT_DETAIL RejectDetail + ) +{ + UNREFERENCED_PARAMETER(Pipeline); + UNREFERENCED_PARAMETER(RejectDetail); + return E_NOTIMPL; +} +//----------------------------------------------------------------------------- + +static HRESULT +WINAPI +EngineAdapterGetEnrollmentStatus( + _Inout_ PWINBIO_PIPELINE Pipeline, + _Out_ PWINBIO_REJECT_DETAIL RejectDetail + ) +{ + UNREFERENCED_PARAMETER(Pipeline); + UNREFERENCED_PARAMETER(RejectDetail); + return E_NOTIMPL; +} +//----------------------------------------------------------------------------- + +static HRESULT +WINAPI +EngineAdapterGetEnrollmentHash( + _Inout_ PWINBIO_PIPELINE Pipeline, + _Out_ PUCHAR *HashValue, + _Out_ PSIZE_T HashSize + ) +{ + UNREFERENCED_PARAMETER(Pipeline); + UNREFERENCED_PARAMETER(HashValue); + UNREFERENCED_PARAMETER(HashSize); + return E_NOTIMPL; +} +//----------------------------------------------------------------------------- + +static HRESULT +WINAPI +EngineAdapterCheckForDuplicate( + _Inout_ PWINBIO_PIPELINE Pipeline, + _Out_ PWINBIO_IDENTITY Identity, + _Out_ PWINBIO_BIOMETRIC_SUBTYPE SubFactor, + _Out_ PBOOLEAN Duplicate + ) +{ + UNREFERENCED_PARAMETER(Pipeline); + UNREFERENCED_PARAMETER(Identity); + UNREFERENCED_PARAMETER(SubFactor); + UNREFERENCED_PARAMETER(Duplicate); + return E_NOTIMPL; +} +//----------------------------------------------------------------------------- + +static HRESULT +WINAPI +EngineAdapterCommitEnrollment( + _Inout_ PWINBIO_PIPELINE Pipeline, + _In_ PWINBIO_IDENTITY Identity, + _In_ WINBIO_BIOMETRIC_SUBTYPE SubFactor, + _In_ PUCHAR PayloadBlob, + _In_ SIZE_T PayloadBlobSize + ) +{ + UNREFERENCED_PARAMETER(Pipeline); + UNREFERENCED_PARAMETER(Identity); + UNREFERENCED_PARAMETER(SubFactor); + UNREFERENCED_PARAMETER(PayloadBlob); + UNREFERENCED_PARAMETER(PayloadBlobSize); + return E_NOTIMPL; +} +//----------------------------------------------------------------------------- + +static HRESULT +WINAPI +EngineAdapterDiscardEnrollment( + _Inout_ PWINBIO_PIPELINE Pipeline + ) +{ + UNREFERENCED_PARAMETER(Pipeline); + return E_NOTIMPL; +} +//----------------------------------------------------------------------------- + +static HRESULT +WINAPI +EngineAdapterControlUnit( + _Inout_ PWINBIO_PIPELINE Pipeline, + _In_ ULONG ControlCode, + _In_ PUCHAR SendBuffer, + _In_ SIZE_T SendBufferSize, + _In_ PUCHAR ReceiveBuffer, + _In_ SIZE_T ReceiveBufferSize, + _Out_ PSIZE_T ReceiveDataSize, + _Out_ PULONG OperationStatus + ) +{ + UNREFERENCED_PARAMETER(Pipeline); + UNREFERENCED_PARAMETER(ControlCode); + UNREFERENCED_PARAMETER(SendBuffer); + UNREFERENCED_PARAMETER(SendBufferSize); + UNREFERENCED_PARAMETER(ReceiveBuffer); + UNREFERENCED_PARAMETER(ReceiveBufferSize); + UNREFERENCED_PARAMETER(ReceiveDataSize); + UNREFERENCED_PARAMETER(OperationStatus); + + return E_NOTIMPL; +} +//----------------------------------------------------------------------------- + +static HRESULT +WINAPI +EngineAdapterControlUnitPrivileged( + _Inout_ PWINBIO_PIPELINE Pipeline, + _In_ ULONG ControlCode, + _In_ PUCHAR SendBuffer, + _In_ SIZE_T SendBufferSize, + _In_ PUCHAR ReceiveBuffer, + _In_ SIZE_T ReceiveBufferSize, + _Out_ PSIZE_T ReceiveDataSize, + _Out_ PULONG OperationStatus + ) +{ + UNREFERENCED_PARAMETER(Pipeline); + UNREFERENCED_PARAMETER(ControlCode); + UNREFERENCED_PARAMETER(SendBuffer); + UNREFERENCED_PARAMETER(SendBufferSize); + UNREFERENCED_PARAMETER(ReceiveBuffer); + UNREFERENCED_PARAMETER(ReceiveBufferSize); + UNREFERENCED_PARAMETER(ReceiveDataSize); + UNREFERENCED_PARAMETER(OperationStatus); + + return E_NOTIMPL; +} +//----------------------------------------------------------------------------- + diff --git a/biometrics/adapters/engine_adapter/EngineAdapter.def b/biometrics/adapters/engine_adapter/EngineAdapter.def new file mode 100644 index 000000000..8f8b458fe --- /dev/null +++ b/biometrics/adapters/engine_adapter/EngineAdapter.def @@ -0,0 +1,4 @@ +LIBRARY EngineAdapter + +EXPORTS + WbioQueryEngineInterface diff --git a/biometrics/adapters/engine_adapter/EngineAdapter.h b/biometrics/adapters/engine_adapter/EngineAdapter.h new file mode 100644 index 000000000..1be2cd6d2 --- /dev/null +++ b/biometrics/adapters/engine_adapter/EngineAdapter.h @@ -0,0 +1,75 @@ +/*++ + + 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 + +Module Name: + + EngineAdapter.h + +Abstract: + + This module contains a stub implementation of an Engine Adapter + plug-in for the Windows Biometric service. + +Author: + + - + +Environment: + + Win32, user mode only. + +Revision History: + +NOTES: + + (None) + +--*/ +#pragma once + +#include "winbio_adapter.h" + +/////////////////////////////////////////////////////////////////////////////// +// +// The WINIBIO_ENGINE_CONTEXT structure is privately-defined by each +// Engine Adapter. Its purpose is to maintain any information that +// should persist across Engine Adapter API calls. +// +// The Adapter allocates and initializes one of these structures in its +// 'Attach' routine and saves its address in the Pipeline->EngineContext +// field. +// +// The Engine Adapter's 'Detach' routine cleans up and deallocates the +// structure and sets the PipelineContext->EngineContext field to NULL. +// +/////////////////////////////////////////////////////////////////////////////// +typedef struct _WINIBIO_ENGINE_CONTEXT { + // + // The following fields illustrate the kind of information + // the Engine Adapter needs to keep in this structure: + // + // FeatureSet - A processed description of a biometric + // sample. + // + // Enrollment - An object that tracks the current state + // of an in-progress enrollment operation. + // + // Template - A template either created from the Feature + // Set or from the Enrollment object. + // + // Comparison - An object that tracks the result of a + // one-to-one comparison between the Template + // and the Feature Set. + // + PVOID x; + PVOID y; + PVOID z; + +} WINIBIO_ENGINE_CONTEXT, *PWINIBIO_ENGINE_CONTEXT; + diff --git a/biometrics/adapters/engine_adapter/EngineAdapter.rc b/biometrics/adapters/engine_adapter/EngineAdapter.rc new file mode 100644 index 000000000..d2a99f201 --- /dev/null +++ b/biometrics/adapters/engine_adapter/EngineAdapter.rc @@ -0,0 +1,29 @@ +//--------------------------------------------------------------------------- +// 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 +// +// BioUsbSample.rc +// +// Copyright (c) 2007 Microsoft Corporation, All Rights Reserved +//--------------------------------------------------------------------------- + + +#include +#include +#include "resource.h" + +// +// 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 "Engine Adapter Sample" +#define VER_INTERNALNAME_STR "EngineAdapter" +#define VER_ORIGINALFILENAME_STR "EngineAdapter.dll" + +#include "common.ver" diff --git a/biometrics/adapters/engine_adapter/EngineAdapter.vcxproj b/biometrics/adapters/engine_adapter/EngineAdapter.vcxproj new file mode 100644 index 000000000..387629f5d --- /dev/null +++ b/biometrics/adapters/engine_adapter/EngineAdapter.vcxproj @@ -0,0 +1,228 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {105C343E-EFA4-45C3-A954-A48D79504BE6} + $(MSBuildProjectName) + false + true + Debug + Win32 + {48043DC3-C3FC-43D3-A670-1B6FC26854FA} + + + + 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) + + + + + + + + + + + + + + + + EngineAdapter + + + EngineAdapter + + + EngineAdapter + + + EngineAdapter + + + + true + Level4 + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + true + Level4 + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + true + Level4 + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + true + Level4 + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(AdditionalDependencies);advapi32.lib;kernel32.lib;user32.lib + EngineAdapter.def + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(AdditionalDependencies);advapi32.lib;kernel32.lib;user32.lib + EngineAdapter.def + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(AdditionalDependencies);advapi32.lib;kernel32.lib;user32.lib + EngineAdapter.def + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(AdditionalDependencies);advapi32.lib;kernel32.lib;user32.lib + EngineAdapter.def + + + + + ;%(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/biometrics/adapters/engine_adapter/EngineAdapter.vcxproj.Filters b/biometrics/adapters/engine_adapter/EngineAdapter.vcxproj.Filters new file mode 100644 index 000000000..63a6f5019 --- /dev/null +++ b/biometrics/adapters/engine_adapter/EngineAdapter.vcxproj.Filters @@ -0,0 +1,33 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {C52A92E2-8FD1-4C9B-8DA7-E18B129EB115} + + + h;hpp;hxx;hm;inl;inc;xsd + {05BE1FA8-D94F-4850-AB73-84FB6C537CCA} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {C077B583-3F44-4B4F-BCA5-E76387B9F5C1} + + + + + Source Files + + + Source Files + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/biometrics/adapters/engine_adapter/precomp.h b/biometrics/adapters/engine_adapter/precomp.h new file mode 100644 index 000000000..c36eb36cd --- /dev/null +++ b/biometrics/adapters/engine_adapter/precomp.h @@ -0,0 +1,62 @@ +/*++ + + 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 + +Module Name: + + precomp.h + +Abstract: + + This module contains identifies all the headers that + shoulde be pre-compiled. + +Author: + + - + +Environment: + + Win32, user mode only. + +Revision History: + +NOTES: + + (None) + +--*/ +#pragma once + +// +// Necessary for compiling under VC. +// +#if(!defined(WINVER) || (WINVER < 0x0500)) + #undef WINVER + #define WINVER 0x0500 +#endif +#if(!defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0500)) + #undef _WIN32_WINNT + #define _WIN32_WINNT 0x0500 +#endif + +// +// Required header files that shouldn't change often. +// +#include +#include +#include + +// +// StrSafe.h needs to be included last +// to disallow unsafe string functions. +#include + +#ifndef ARGUMENT_PRESENT +#define ARGUMENT_PRESENT(x) ((x) != NULL) +#endif diff --git a/biometrics/adapters/engine_adapter/precompsrc.cpp b/biometrics/adapters/engine_adapter/precompsrc.cpp new file mode 100644 index 000000000..5944cf515 --- /dev/null +++ b/biometrics/adapters/engine_adapter/precompsrc.cpp @@ -0,0 +1 @@ +#include "precomp.h" \ No newline at end of file diff --git a/biometrics/adapters/engine_adapter/resource.h b/biometrics/adapters/engine_adapter/resource.h new file mode 100644 index 000000000..95ed37fa6 --- /dev/null +++ b/biometrics/adapters/engine_adapter/resource.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 +// +// BioUsbSample.rc +// +// Copyright (c) 2007 Microsoft Corporation, All Rights Reserved +//--------------------------------------------------------------------------- + +#pragma once + diff --git a/biometrics/adapters/sensor_adapter/SensorAdapter.cpp b/biometrics/adapters/sensor_adapter/SensorAdapter.cpp new file mode 100644 index 000000000..0e5345aaf --- /dev/null +++ b/biometrics/adapters/sensor_adapter/SensorAdapter.cpp @@ -0,0 +1,485 @@ +/*++ + + 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 + +Module Name: + + SensorAdapter.cpp + +Abstract: + + This module contains a stub implementation of a Sensor Adapter + plug-in for the Windows Biometric service. + +Author: + + - + +Environment: + + Win32, user mode only. + +Revision History: + +NOTES: + + (None) + +--*/ + +/////////////////////////////////////////////////////////////////////////////// +// +// Header files... +// +/////////////////////////////////////////////////////////////////////////////// +#include "precomp.h" +#include "winbio_adapter.h" +#include "SensorAdapter.h" + + +/////////////////////////////////////////////////////////////////////////////// +// +// Forward declarations for the Engine Adapter's interface routines... +// +/////////////////////////////////////////////////////////////////////////////// +static HRESULT +WINAPI +SensorAdapterAttach( + _Inout_ PWINBIO_PIPELINE Pipeline + ); + +static HRESULT +WINAPI +SensorAdapterDetach( + _Inout_ PWINBIO_PIPELINE Pipeline + ); + +static HRESULT +WINAPI +SensorAdapterClearContext( + _Inout_ PWINBIO_PIPELINE Pipeline + ); + +static HRESULT +WINAPI +SensorAdapterQueryStatus( + _Inout_ PWINBIO_PIPELINE Pipeline, + _Out_ PWINBIO_SENSOR_STATUS Status + ); + +static HRESULT +WINAPI +SensorAdapterReset( + _Inout_ PWINBIO_PIPELINE Pipeline + ); + +static HRESULT +WINAPI +SensorAdapterSetMode( + _Inout_ PWINBIO_PIPELINE Pipeline, + _In_ WINBIO_SENSOR_MODE Mode + ); + +static HRESULT +WINAPI +SensorAdapterSetIndicatorStatus( + _Inout_ PWINBIO_PIPELINE Pipeline, + _In_ WINBIO_INDICATOR_STATUS IndicatorStatus + ); + +static HRESULT +WINAPI +SensorAdapterGetIndicatorStatus( + _Inout_ PWINBIO_PIPELINE Pipeline, + _Out_ PWINBIO_INDICATOR_STATUS IndicatorStatus + ); + +static HRESULT +WINAPI +SensorAdapterStartCapture( + _Inout_ PWINBIO_PIPELINE Pipeline, + _In_ WINBIO_BIR_PURPOSE Purpose, + _Out_ LPOVERLAPPED *Overlapped + ); + +static HRESULT +WINAPI +SensorAdapterFinishCapture( + _Inout_ PWINBIO_PIPELINE Pipeline, + _Out_ PWINBIO_REJECT_DETAIL RejectDetail + ); + +static HRESULT +WINAPI +SensorAdapterClearCaptureBuffer( + _Inout_ PWINBIO_PIPELINE Pipeline + ); + +static HRESULT +WINAPI +SensorAdapterExportSensorData( + _Inout_ PWINBIO_PIPELINE Pipeline, + _Out_ PWINBIO_BIR *SampleBuffer, + _Out_ PSIZE_T SampleSize + ); + +static HRESULT +WINAPI +SensorAdapterCancel( + _Inout_ PWINBIO_PIPELINE Pipeline + ); + +static HRESULT +WINAPI +SensorAdapterPushDataToEngine( + _Inout_ PWINBIO_PIPELINE Pipeline, + _In_ WINBIO_BIR_PURPOSE Purpose, + _In_ WINBIO_BIR_DATA_FLAGS Flags, + _Out_ PWINBIO_REJECT_DETAIL RejectDetail + ); + +static HRESULT +WINAPI +SensorAdapterControlUnit( + _Inout_ PWINBIO_PIPELINE Pipeline, + _In_ ULONG ControlCode, + _In_ PUCHAR SendBuffer, + _In_ SIZE_T SendBufferSize, + _In_ PUCHAR ReceiveBuffer, + _In_ SIZE_T ReceiveBufferSize, + _Out_ PSIZE_T ReceiveDataSize, + _Out_ PULONG OperationStatus + ); + +static HRESULT +WINAPI +SensorAdapterControlUnitPrivileged( + _Inout_ PWINBIO_PIPELINE Pipeline, + _In_ ULONG ControlCode, + _In_ PUCHAR SendBuffer, + _In_ SIZE_T SendBufferSize, + _In_ PUCHAR ReceiveBuffer, + _In_ SIZE_T ReceiveBufferSize, + _Out_ PSIZE_T ReceiveDataSize, + _Out_ PULONG OperationStatus + ); +//----------------------------------------------------------------------------- + + +/////////////////////////////////////////////////////////////////////////////// +// +// Interface dispatch table +// +/////////////////////////////////////////////////////////////////////////////// +static WINBIO_SENSOR_INTERFACE g_SensorInterface = { + WINBIO_STORAGE_INTERFACE_VERSION_1, + WINBIO_ADAPTER_TYPE_SENSOR, + sizeof(WINBIO_SENSOR_INTERFACE), + {0xa545298c, 0xec34, 0x4306, {0x84, 0x12, 0x83, 0x12, 0x5d, 0xca, 0xfa, 0xe1}}, + + SensorAdapterAttach, + SensorAdapterDetach, + SensorAdapterClearContext, + SensorAdapterQueryStatus, + SensorAdapterReset, + SensorAdapterSetMode, + SensorAdapterSetIndicatorStatus, + SensorAdapterGetIndicatorStatus, + SensorAdapterStartCapture, + SensorAdapterFinishCapture, + SensorAdapterExportSensorData, + SensorAdapterCancel, + SensorAdapterPushDataToEngine, + SensorAdapterControlUnit, + SensorAdapterControlUnitPrivileged +}; +//----------------------------------------------------------------------------- + + +/////////////////////////////////////////////////////////////////////////////// +// +// Mandatory DLL entrypoint function. +// +/////////////////////////////////////////////////////////////////////////////// +BOOL APIENTRY +DllMain( + HANDLE ModuleHandle, + DWORD ReasonForCall, + LPVOID Reserved + ) +{ + UNREFERENCED_PARAMETER(ModuleHandle); + UNREFERENCED_PARAMETER(ReasonForCall); + UNREFERENCED_PARAMETER(Reserved); + + return TRUE; +} +//----------------------------------------------------------------------------- + + +/////////////////////////////////////////////////////////////////////////////// +// +// Well-known interface-discovery function exported by the Sensor Adapter +// +/////////////////////////////////////////////////////////////////////////////// +HRESULT +WINAPI +WbioQuerySensorInterface( + _Out_ PWINBIO_SENSOR_INTERFACE *SensorInterface + ) +{ + *SensorInterface = &g_SensorInterface; + return S_OK; +} +//----------------------------------------------------------------------------- + + +/////////////////////////////////////////////////////////////////////////////// +// +// Storage Adapter action routines +// +/////////////////////////////////////////////////////////////////////////////// +static HRESULT +WINAPI +SensorAdapterAttach( + _Inout_ PWINBIO_PIPELINE Pipeline + ) +{ + UNREFERENCED_PARAMETER(Pipeline); + + return E_NOTIMPL; +} +/////////////////////////////////////////////////////////////////////////////// + +static HRESULT +WINAPI +SensorAdapterDetach( + _Inout_ PWINBIO_PIPELINE Pipeline + ) +{ + UNREFERENCED_PARAMETER(Pipeline); + + return E_NOTIMPL; +} +/////////////////////////////////////////////////////////////////////////////// + +static HRESULT +WINAPI +SensorAdapterClearContext( + _Inout_ PWINBIO_PIPELINE Pipeline + ) +{ + UNREFERENCED_PARAMETER(Pipeline); + + return E_NOTIMPL; +} +/////////////////////////////////////////////////////////////////////////////// + +static HRESULT +WINAPI +SensorAdapterQueryStatus( + _Inout_ PWINBIO_PIPELINE Pipeline, + _Out_ PWINBIO_SENSOR_STATUS Status + ) +{ + UNREFERENCED_PARAMETER(Pipeline); + UNREFERENCED_PARAMETER(Status); + + return E_NOTIMPL; +} +/////////////////////////////////////////////////////////////////////////////// + +static HRESULT +WINAPI +SensorAdapterReset( + _Inout_ PWINBIO_PIPELINE Pipeline + ) +{ + UNREFERENCED_PARAMETER(Pipeline); + + return E_NOTIMPL; +} +/////////////////////////////////////////////////////////////////////////////// + +static HRESULT +WINAPI +SensorAdapterSetMode( + _Inout_ PWINBIO_PIPELINE Pipeline, + _In_ WINBIO_SENSOR_MODE Mode + ) +{ + UNREFERENCED_PARAMETER(Pipeline); + UNREFERENCED_PARAMETER(Mode); + + return E_NOTIMPL; +} +/////////////////////////////////////////////////////////////////////////////// + +static HRESULT +WINAPI +SensorAdapterSetIndicatorStatus( + _Inout_ PWINBIO_PIPELINE Pipeline, + _In_ WINBIO_INDICATOR_STATUS IndicatorStatus + ) +{ + UNREFERENCED_PARAMETER(Pipeline); + UNREFERENCED_PARAMETER(IndicatorStatus); + + return E_NOTIMPL; +} +/////////////////////////////////////////////////////////////////////////////// + +static HRESULT +WINAPI +SensorAdapterGetIndicatorStatus( + _Inout_ PWINBIO_PIPELINE Pipeline, + _Out_ PWINBIO_INDICATOR_STATUS IndicatorStatus + ) +{ + UNREFERENCED_PARAMETER(Pipeline); + UNREFERENCED_PARAMETER(IndicatorStatus); + + return E_NOTIMPL; +} +/////////////////////////////////////////////////////////////////////////////// + +static HRESULT +WINAPI +SensorAdapterStartCapture( + _Inout_ PWINBIO_PIPELINE Pipeline, + _In_ WINBIO_BIR_PURPOSE Purpose, + _Out_ LPOVERLAPPED *Overlapped + ) +{ + UNREFERENCED_PARAMETER(Pipeline); + UNREFERENCED_PARAMETER(Purpose); + UNREFERENCED_PARAMETER(Overlapped); + + return E_NOTIMPL; +} +/////////////////////////////////////////////////////////////////////////////// + +static HRESULT +WINAPI +SensorAdapterFinishCapture( + _Inout_ PWINBIO_PIPELINE Pipeline, + _Out_ PWINBIO_REJECT_DETAIL RejectDetail + ) +{ + UNREFERENCED_PARAMETER(Pipeline); + UNREFERENCED_PARAMETER(RejectDetail); + + return E_NOTIMPL; +} +/////////////////////////////////////////////////////////////////////////////// + +// +// Export raw capture buffer +// +static HRESULT +WINAPI +SensorAdapterExportSensorData( + _Inout_ PWINBIO_PIPELINE Pipeline, + _Out_ PWINBIO_BIR *SampleBuffer, + _Out_ PSIZE_T SampleSize + ) +{ + UNREFERENCED_PARAMETER(Pipeline); + UNREFERENCED_PARAMETER(SampleBuffer); + UNREFERENCED_PARAMETER(SampleSize); + + return E_NOTIMPL; +} +/////////////////////////////////////////////////////////////////////////////// + +static HRESULT +WINAPI +SensorAdapterCancel( + _Inout_ PWINBIO_PIPELINE Pipeline + ) +{ + UNREFERENCED_PARAMETER(Pipeline); + + return E_NOTIMPL; +} +/////////////////////////////////////////////////////////////////////////////// + +// +// Push current sample into the Engine and +// convert it into a feature set for use in +// additional processing. +// +static HRESULT +WINAPI +SensorAdapterPushDataToEngine( + _Inout_ PWINBIO_PIPELINE Pipeline, + _In_ WINBIO_BIR_PURPOSE Purpose, + _In_ WINBIO_BIR_DATA_FLAGS Flags, + _Out_ PWINBIO_REJECT_DETAIL RejectDetail + ) +{ + UNREFERENCED_PARAMETER(Pipeline); + UNREFERENCED_PARAMETER(Purpose); + UNREFERENCED_PARAMETER(Flags); + UNREFERENCED_PARAMETER(RejectDetail); + + return E_NOTIMPL; +} +/////////////////////////////////////////////////////////////////////////////// + +static HRESULT +WINAPI +SensorAdapterControlUnit( + _Inout_ PWINBIO_PIPELINE Pipeline, + _In_ ULONG ControlCode, + _In_ PUCHAR SendBuffer, + _In_ SIZE_T SendBufferSize, + _In_ PUCHAR ReceiveBuffer, + _In_ SIZE_T ReceiveBufferSize, + _Out_ PSIZE_T ReceiveDataSize, + _Out_ PULONG OperationStatus + ) +{ + UNREFERENCED_PARAMETER(Pipeline); + UNREFERENCED_PARAMETER(ControlCode); + UNREFERENCED_PARAMETER(SendBuffer); + UNREFERENCED_PARAMETER(SendBufferSize); + UNREFERENCED_PARAMETER(ReceiveBuffer); + UNREFERENCED_PARAMETER(ReceiveBufferSize); + UNREFERENCED_PARAMETER(ReceiveDataSize); + UNREFERENCED_PARAMETER(OperationStatus); + + return E_NOTIMPL; +} +/////////////////////////////////////////////////////////////////////////////// + +static HRESULT +WINAPI +SensorAdapterControlUnitPrivileged( + _Inout_ PWINBIO_PIPELINE Pipeline, + _In_ ULONG ControlCode, + _In_ PUCHAR SendBuffer, + _In_ SIZE_T SendBufferSize, + _In_ PUCHAR ReceiveBuffer, + _In_ SIZE_T ReceiveBufferSize, + _Out_ PSIZE_T ReceiveDataSize, + _Out_ PULONG OperationStatus + ) +{ + UNREFERENCED_PARAMETER(Pipeline); + UNREFERENCED_PARAMETER(ControlCode); + UNREFERENCED_PARAMETER(SendBuffer); + UNREFERENCED_PARAMETER(SendBufferSize); + UNREFERENCED_PARAMETER(ReceiveBuffer); + UNREFERENCED_PARAMETER(ReceiveBufferSize); + UNREFERENCED_PARAMETER(ReceiveDataSize); + UNREFERENCED_PARAMETER(OperationStatus); + + return E_NOTIMPL; +} +/////////////////////////////////////////////////////////////////////////////// diff --git a/biometrics/adapters/sensor_adapter/SensorAdapter.def b/biometrics/adapters/sensor_adapter/SensorAdapter.def new file mode 100644 index 000000000..e25340089 --- /dev/null +++ b/biometrics/adapters/sensor_adapter/SensorAdapter.def @@ -0,0 +1,5 @@ +LIBRARY SensorAdapter + +EXPORTS + WbioQuerySensorInterface + diff --git a/biometrics/adapters/sensor_adapter/SensorAdapter.h b/biometrics/adapters/sensor_adapter/SensorAdapter.h new file mode 100644 index 000000000..f70c3f515 --- /dev/null +++ b/biometrics/adapters/sensor_adapter/SensorAdapter.h @@ -0,0 +1,68 @@ +/*++ + + 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 + +Module Name: + + SensorAdapter.h + +Abstract: + + This module contains a stub implementation of an Sensor Adapter + plug-in for the Windows Biometric service. + +Author: + + - + +Environment: + + Win32, user mode only. + +Revision History: + +NOTES: + + (None) + +--*/ +#pragma once + +#include "winbio_adapter.h" + +/////////////////////////////////////////////////////////////////////////////// +// +// The WINIBIO_SENSOR_CONTEXT structure is privately-defined by each +// Sensor Adapter. Its purpose is to maintain any information that +// should persist across Sensor Adapter API calls. +// +// The Adapter allocates and initializes one of these structures in its +// 'Attach' routine and saves its address in the Pipeline->SensorContext +// field. +// +// The Sensor Adapter's 'Detach' routine cleans up and deallocates the +// structure and sets the PipelineContext->SensorContext field to NULL. +// +/////////////////////////////////////////////////////////////////////////////// +typedef struct _WINIBIO_SENSOR_CONTEXT { + // + // The following fields illustrate the kind of information + // the Sensor Adapter needs to keep in this structure: + // + // SampleBuffer - A pointer to the most-recently-captured + // data sample from the sensor device. + // + // SampleSize - Count of the number of bytes in the + // sample buffer. + // + PWINBIO_BIR SampleBuffer; + SIZE_T SampleSize; + +} WINIBIO_SENSOR_CONTEXT, *PWINIBIO_SENSOR_CONTEXT; + + diff --git a/biometrics/adapters/sensor_adapter/SensorAdapter.rc b/biometrics/adapters/sensor_adapter/SensorAdapter.rc new file mode 100644 index 000000000..0c08a2089 --- /dev/null +++ b/biometrics/adapters/sensor_adapter/SensorAdapter.rc @@ -0,0 +1,29 @@ +//--------------------------------------------------------------------------- +// 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 +// +// BioUsbSample.rc +// +// Copyright (c) 2007 Microsoft Corporation, All Rights Reserved +//--------------------------------------------------------------------------- + + +#include +#include +#include "resource.h" + +// +// 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 "Sensor Adapter Sample" +#define VER_INTERNALNAME_STR "SensorAdapter" +#define VER_ORIGINALFILENAME_STR "SensorAdapter.dll" + +#include "common.ver" diff --git a/biometrics/adapters/sensor_adapter/SensorAdapter.vcxproj b/biometrics/adapters/sensor_adapter/SensorAdapter.vcxproj new file mode 100644 index 000000000..9e0cc29ce --- /dev/null +++ b/biometrics/adapters/sensor_adapter/SensorAdapter.vcxproj @@ -0,0 +1,228 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {D6372638-968B-4FDC-A56F-C70C339E1448} + $(MSBuildProjectName) + false + true + Debug + Win32 + {9A3072FA-B82D-4F09-B51C-E316E1D5967E} + + + + 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) + + + + + + + + + + + + + + + + SensorAdapter + + + SensorAdapter + + + SensorAdapter + + + SensorAdapter + + + + true + Level4 + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + true + Level4 + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + true + Level4 + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + true + Level4 + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(AdditionalDependencies);advapi32.lib;kernel32.lib;user32.lib + SensorAdapter.def + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(AdditionalDependencies);advapi32.lib;kernel32.lib;user32.lib + SensorAdapter.def + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(AdditionalDependencies);advapi32.lib;kernel32.lib;user32.lib + SensorAdapter.def + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(AdditionalDependencies);advapi32.lib;kernel32.lib;user32.lib + SensorAdapter.def + + + + + ;%(AdditionalIncludeDirectories) + precomp.h + Create + $(IntDir)\precomp.h.pch + + + ;%(AdditionalIncludeDirectories) + precomp.h + Use + $(IntDir)\precomp.h.pch + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/biometrics/adapters/sensor_adapter/SensorAdapter.vcxproj.Filters b/biometrics/adapters/sensor_adapter/SensorAdapter.vcxproj.Filters new file mode 100644 index 000000000..5de956b9e --- /dev/null +++ b/biometrics/adapters/sensor_adapter/SensorAdapter.vcxproj.Filters @@ -0,0 +1,33 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {C204C4D5-940E-485F-AE8D-FFE0647C24A9} + + + h;hpp;hxx;hm;inl;inc;xsd + {F784ED9B-1FF3-4FAA-99B0-440161B7AA8E} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {AC8EF464-4442-49B1-9AFA-ED1AEA162B4E} + + + + + Source Files + + + Source Files + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/biometrics/adapters/sensor_adapter/precomp.h b/biometrics/adapters/sensor_adapter/precomp.h new file mode 100644 index 000000000..c36eb36cd --- /dev/null +++ b/biometrics/adapters/sensor_adapter/precomp.h @@ -0,0 +1,62 @@ +/*++ + + 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 + +Module Name: + + precomp.h + +Abstract: + + This module contains identifies all the headers that + shoulde be pre-compiled. + +Author: + + - + +Environment: + + Win32, user mode only. + +Revision History: + +NOTES: + + (None) + +--*/ +#pragma once + +// +// Necessary for compiling under VC. +// +#if(!defined(WINVER) || (WINVER < 0x0500)) + #undef WINVER + #define WINVER 0x0500 +#endif +#if(!defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0500)) + #undef _WIN32_WINNT + #define _WIN32_WINNT 0x0500 +#endif + +// +// Required header files that shouldn't change often. +// +#include +#include +#include + +// +// StrSafe.h needs to be included last +// to disallow unsafe string functions. +#include + +#ifndef ARGUMENT_PRESENT +#define ARGUMENT_PRESENT(x) ((x) != NULL) +#endif diff --git a/biometrics/adapters/sensor_adapter/precompsrc.cpp b/biometrics/adapters/sensor_adapter/precompsrc.cpp new file mode 100644 index 000000000..5944cf515 --- /dev/null +++ b/biometrics/adapters/sensor_adapter/precompsrc.cpp @@ -0,0 +1 @@ +#include "precomp.h" \ No newline at end of file diff --git a/biometrics/adapters/sensor_adapter/resource.h b/biometrics/adapters/sensor_adapter/resource.h new file mode 100644 index 000000000..95ed37fa6 --- /dev/null +++ b/biometrics/adapters/sensor_adapter/resource.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 +// +// BioUsbSample.rc +// +// Copyright (c) 2007 Microsoft Corporation, All Rights Reserved +//--------------------------------------------------------------------------- + +#pragma once + diff --git a/biometrics/adapters/storage_adapter/StorageAdapter.cpp b/biometrics/adapters/storage_adapter/StorageAdapter.cpp new file mode 100644 index 000000000..4e5cfb664 --- /dev/null +++ b/biometrics/adapters/storage_adapter/StorageAdapter.cpp @@ -0,0 +1,594 @@ +/*++ + + 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 + +Module Name: + + Storage.cpp + +Abstract: + + This module contains a stub implementation of a Storage Adapter + plug-in for the Windows Biometric service. + +Author: + + - + +Environment: + + Win32, user mode only. + +Revision History: + +NOTES: + + (None) + +--*/ + +/////////////////////////////////////////////////////////////////////////////// +// +// Header files... +// +/////////////////////////////////////////////////////////////////////////////// +#include "precomp.h" +#include "winbio_adapter.h" +#include "StorageAdapter.h" + + +/////////////////////////////////////////////////////////////////////////////// +// +// Forward declarations for the Storage Adapter's interface routines... +// +/////////////////////////////////////////////////////////////////////////////// +static HRESULT +WINAPI +StorageAdapterAttach( + _Inout_ PWINBIO_PIPELINE Pipeline + ); + +static HRESULT +WINAPI +StorageAdapterDetach( + _Inout_ PWINBIO_PIPELINE Pipeline + ); + +static HRESULT +WINAPI +StorageAdapterClearContext( + _Inout_ PWINBIO_PIPELINE Pipeline + ); + +static HRESULT +WINAPI +StorageAdapterCreateDatabase( + _Inout_ PWINBIO_PIPELINE Pipeline, + _In_ PWINBIO_UUID DatabaseId, + _In_ WINBIO_BIOMETRIC_TYPE Factor, + _In_ PWINBIO_UUID Format, + _In_ LPCWSTR FilePath, + _In_ LPCWSTR ConnectString, + _In_ SIZE_T IndexElementCount, + _In_ SIZE_T InitialSize + ); + +static HRESULT +WINAPI +StorageAdapterEraseDatabase( + _Inout_ PWINBIO_PIPELINE Pipeline, + _In_ PWINBIO_UUID DatabaseId, + _In_ LPCWSTR FilePath, + _In_ LPCWSTR ConnectString + ); + +static HRESULT +WINAPI +StorageAdapterOpenDatabase( + _Inout_ PWINBIO_PIPELINE Pipeline, + _In_ PWINBIO_UUID DatabaseId, + _In_ LPCWSTR FilePath, + _In_ LPCWSTR ConnectString + ); + +static HRESULT +WINAPI +StorageAdapterCloseDatabase( + _Inout_ PWINBIO_PIPELINE Pipeline + ); + +static HRESULT +WINAPI +StorageAdapterGetDataFormat( + _Inout_ PWINBIO_PIPELINE Pipeline, + _Out_ PWINBIO_UUID Format, + _Out_ PWINBIO_VERSION Version + ); + +static HRESULT +WINAPI +StorageAdapterGetDatabaseSize( + _Inout_ PWINBIO_PIPELINE Pipeline, + _Out_ PSIZE_T AvailableRecordCount, + _Out_ PSIZE_T TotalRecordCount + ); + +static HRESULT +WINAPI +StorageAdapterAddRecord( + _Inout_ PWINBIO_PIPELINE Pipeline, + _In_ PWINBIO_STORAGE_RECORD RecordContents + ); + +static HRESULT +WINAPI +StorageAdapterDeleteRecord( + _Inout_ PWINBIO_PIPELINE Pipeline, + _In_ PWINBIO_IDENTITY Identity, + _In_ WINBIO_BIOMETRIC_SUBTYPE SubFactor + ); + +static HRESULT +WINAPI +StorageAdapterQueryBySubject( + _Inout_ PWINBIO_PIPELINE Pipeline, + _In_ PWINBIO_IDENTITY Identity, + _In_ WINBIO_BIOMETRIC_SUBTYPE SubFactor + ); + +static HRESULT +WINAPI +StorageAdapterQueryByContent( + _Inout_ PWINBIO_PIPELINE Pipeline, + _In_ WINBIO_BIOMETRIC_SUBTYPE SubFactor, + _In_ ULONG IndexVector[], + _In_ SIZE_T IndexElementCount + ); + +static HRESULT +WINAPI +StorageAdapterGetRecordCount( + _Inout_ PWINBIO_PIPELINE Pipeline, + _Out_ PSIZE_T RecordCount + ); + +static HRESULT +WINAPI +StorageAdapterFirstRecord( + _Inout_ PWINBIO_PIPELINE Pipeline + ); + +static HRESULT +WINAPI +StorageAdapterNextRecord( + _Inout_ PWINBIO_PIPELINE Pipeline + ); + +static HRESULT +WINAPI +StorageAdapterGetCurrentRecord( + _Inout_ PWINBIO_PIPELINE Pipeline, + _Out_ PWINBIO_STORAGE_RECORD RecordContents + ); + +static HRESULT +WINAPI +StorageAdapterControlUnit( + _Inout_ PWINBIO_PIPELINE Pipeline, + _In_ ULONG ControlCode, + _In_ PUCHAR SendBuffer, + _In_ SIZE_T SendBufferSize, + _In_ PUCHAR ReceiveBuffer, + _In_ SIZE_T ReceiveBufferSize, + _Out_ PSIZE_T ReceiveDataSize, + _Out_ PULONG OperationStatus + ); + +static HRESULT +WINAPI +StorageAdapterControlUnitPrivileged( + _Inout_ PWINBIO_PIPELINE Pipeline, + _In_ ULONG ControlCode, + _In_ PUCHAR SendBuffer, + _In_ SIZE_T SendBufferSize, + _In_ PUCHAR ReceiveBuffer, + _In_ SIZE_T ReceiveBufferSize, + _Out_ PSIZE_T ReceiveDataSize, + _Out_ PULONG OperationStatus + ); +//----------------------------------------------------------------------------- + + +/////////////////////////////////////////////////////////////////////////////// +// +// Interface dispatch table +// +/////////////////////////////////////////////////////////////////////////////// +static WINBIO_STORAGE_INTERFACE g_StorageInterface = { + WINBIO_STORAGE_INTERFACE_VERSION_1, + WINBIO_ADAPTER_TYPE_STORAGE, + sizeof(WINBIO_STORAGE_INTERFACE), + {0x7f6c2610, 0xfdba, 0x41a3, {0xae, 0x1c, 0x8f, 0xd5, 0x84, 0x59, 0x8d, 0x13}}, + + StorageAdapterAttach, + StorageAdapterDetach, + StorageAdapterClearContext, + StorageAdapterCreateDatabase, + StorageAdapterEraseDatabase, + StorageAdapterOpenDatabase, + StorageAdapterCloseDatabase, + StorageAdapterGetDataFormat, + StorageAdapterGetDatabaseSize, + StorageAdapterAddRecord, + StorageAdapterDeleteRecord, + StorageAdapterQueryBySubject, + StorageAdapterQueryByContent, + StorageAdapterGetRecordCount, + StorageAdapterFirstRecord, + StorageAdapterNextRecord, + StorageAdapterGetCurrentRecord, + StorageAdapterControlUnit, + StorageAdapterControlUnitPrivileged +}; +//----------------------------------------------------------------------------- + + +/////////////////////////////////////////////////////////////////////////////// +// +// Mandatory DLL entrypoint function. +// +/////////////////////////////////////////////////////////////////////////////// +BOOL APIENTRY +DllMain( + HANDLE ModuleHandle, + DWORD ReasonForCall, + LPVOID Reserved + ) +{ + UNREFERENCED_PARAMETER(ModuleHandle); + UNREFERENCED_PARAMETER(ReasonForCall); + UNREFERENCED_PARAMETER(Reserved); + + return TRUE; +} +//----------------------------------------------------------------------------- + + +/////////////////////////////////////////////////////////////////////////////// +// +// Well-known interface-discovery function exported by the Storage Adapter +// +/////////////////////////////////////////////////////////////////////////////// +HRESULT +WINAPI +WbioQueryStorageInterface( + _Out_ PWINBIO_STORAGE_INTERFACE *StorageInterface + ) +{ + *StorageInterface = &g_StorageInterface; + return S_OK; +} +//----------------------------------------------------------------------------- + + +/////////////////////////////////////////////////////////////////////////////// +// +// Storage Adapter action routines +// +/////////////////////////////////////////////////////////////////////////////// +static HRESULT +WINAPI +StorageAdapterAttach( + _Inout_ PWINBIO_PIPELINE Pipeline + ) +{ + UNREFERENCED_PARAMETER(Pipeline); + + return E_NOTIMPL; +} +//----------------------------------------------------------------------------- + +static HRESULT +WINAPI +StorageAdapterDetach( + _Inout_ PWINBIO_PIPELINE Pipeline + ) +{ + UNREFERENCED_PARAMETER(Pipeline); + + return E_NOTIMPL; +} +//----------------------------------------------------------------------------- + +static HRESULT +WINAPI +StorageAdapterClearContext( + _Inout_ PWINBIO_PIPELINE Pipeline + ) +{ + UNREFERENCED_PARAMETER(Pipeline); + + return E_NOTIMPL; +} +//----------------------------------------------------------------------------- + +static HRESULT +WINAPI +StorageAdapterCreateDatabase( + _Inout_ PWINBIO_PIPELINE Pipeline, + _In_ PWINBIO_UUID DatabaseId, + _In_ WINBIO_BIOMETRIC_TYPE Factor, + _In_ PWINBIO_UUID Format, + _In_ LPCWSTR FilePath, + _In_ LPCWSTR ConnectString, + _In_ SIZE_T IndexElementCount, + _In_ SIZE_T InitialSize + ) +{ + UNREFERENCED_PARAMETER(Pipeline); + UNREFERENCED_PARAMETER(DatabaseId); + UNREFERENCED_PARAMETER(Factor); + UNREFERENCED_PARAMETER(Format); + UNREFERENCED_PARAMETER(FilePath); + UNREFERENCED_PARAMETER(ConnectString); + UNREFERENCED_PARAMETER(IndexElementCount); + UNREFERENCED_PARAMETER(InitialSize); + + return E_NOTIMPL; +} +//----------------------------------------------------------------------------- + +static HRESULT +WINAPI +StorageAdapterEraseDatabase( + _Inout_ PWINBIO_PIPELINE Pipeline, + _In_ PWINBIO_UUID DatabaseId, + _In_ LPCWSTR FilePath, + _In_ LPCWSTR ConnectString + ) +{ + UNREFERENCED_PARAMETER(Pipeline); + UNREFERENCED_PARAMETER(DatabaseId); + UNREFERENCED_PARAMETER(FilePath); + UNREFERENCED_PARAMETER(ConnectString); + + return E_NOTIMPL; +} +//----------------------------------------------------------------------------- + +static HRESULT +WINAPI +StorageAdapterOpenDatabase( + _Inout_ PWINBIO_PIPELINE Pipeline, + _In_ PWINBIO_UUID DatabaseId, + _In_ LPCWSTR FilePath, + _In_ LPCWSTR ConnectString + ) +{ + UNREFERENCED_PARAMETER(Pipeline); + UNREFERENCED_PARAMETER(DatabaseId); + UNREFERENCED_PARAMETER(FilePath); + UNREFERENCED_PARAMETER(ConnectString); + + return E_NOTIMPL; +} +//----------------------------------------------------------------------------- + +static HRESULT +WINAPI +StorageAdapterCloseDatabase( + _Inout_ PWINBIO_PIPELINE Pipeline + ) +{ + UNREFERENCED_PARAMETER(Pipeline); + + return E_NOTIMPL; +} +//----------------------------------------------------------------------------- + +static HRESULT +WINAPI +StorageAdapterGetDataFormat( + _Inout_ PWINBIO_PIPELINE Pipeline, + _Out_ PWINBIO_UUID Format, + _Out_ PWINBIO_VERSION Version + ) +{ + UNREFERENCED_PARAMETER(Pipeline); + UNREFERENCED_PARAMETER(Format); + UNREFERENCED_PARAMETER(Version); + + return E_NOTIMPL; +} +//----------------------------------------------------------------------------- + +static HRESULT +WINAPI +StorageAdapterGetDatabaseSize( + _Inout_ PWINBIO_PIPELINE Pipeline, + _Out_ PSIZE_T AvailableRecordCount, + _Out_ PSIZE_T TotalRecordCount + ) +{ + UNREFERENCED_PARAMETER(Pipeline); + UNREFERENCED_PARAMETER(AvailableRecordCount); + UNREFERENCED_PARAMETER(TotalRecordCount); + + return E_NOTIMPL; +} +//----------------------------------------------------------------------------- + +static HRESULT +WINAPI +StorageAdapterAddRecord( + _Inout_ PWINBIO_PIPELINE Pipeline, + _In_ PWINBIO_STORAGE_RECORD RecordContents + ) +{ + UNREFERENCED_PARAMETER(Pipeline); + UNREFERENCED_PARAMETER(RecordContents); + + return E_NOTIMPL; +} +//----------------------------------------------------------------------------- + +static HRESULT +WINAPI +StorageAdapterDeleteRecord( + _Inout_ PWINBIO_PIPELINE Pipeline, + _In_ PWINBIO_IDENTITY Identity, + _In_ WINBIO_BIOMETRIC_SUBTYPE SubFactor + ) +{ + UNREFERENCED_PARAMETER(Pipeline); + UNREFERENCED_PARAMETER(Identity); + UNREFERENCED_PARAMETER(SubFactor); + + return E_NOTIMPL; +} +//----------------------------------------------------------------------------- + +static HRESULT +WINAPI +StorageAdapterQueryBySubject( + _Inout_ PWINBIO_PIPELINE Pipeline, + _In_ PWINBIO_IDENTITY Identity, + _In_ WINBIO_BIOMETRIC_SUBTYPE SubFactor + ) +{ + UNREFERENCED_PARAMETER(Pipeline); + UNREFERENCED_PARAMETER(Identity); + UNREFERENCED_PARAMETER(SubFactor); + + return E_NOTIMPL; +} +//----------------------------------------------------------------------------- + +static HRESULT +WINAPI +StorageAdapterQueryByContent( + _Inout_ PWINBIO_PIPELINE Pipeline, + _In_ WINBIO_BIOMETRIC_SUBTYPE SubFactor, + _In_ ULONG IndexVector[], + _In_ SIZE_T IndexElementCount + ) +{ + UNREFERENCED_PARAMETER(Pipeline); + UNREFERENCED_PARAMETER(SubFactor); + UNREFERENCED_PARAMETER(IndexVector); + UNREFERENCED_PARAMETER(IndexElementCount); + + return E_NOTIMPL; +} +//----------------------------------------------------------------------------- + +static HRESULT +WINAPI +StorageAdapterGetRecordCount( + _Inout_ PWINBIO_PIPELINE Pipeline, + _Out_ PSIZE_T RecordCount + ) +{ + UNREFERENCED_PARAMETER(Pipeline); + UNREFERENCED_PARAMETER(RecordCount); + + return E_NOTIMPL; +} +//----------------------------------------------------------------------------- + +static HRESULT +WINAPI +StorageAdapterFirstRecord( + _Inout_ PWINBIO_PIPELINE Pipeline + ) +{ + UNREFERENCED_PARAMETER(Pipeline); + + return E_NOTIMPL; +} +//----------------------------------------------------------------------------- + +static HRESULT +WINAPI +StorageAdapterNextRecord( + _Inout_ PWINBIO_PIPELINE Pipeline + ) +{ + UNREFERENCED_PARAMETER(Pipeline); + + return E_NOTIMPL; +} +//----------------------------------------------------------------------------- + +static HRESULT +WINAPI +StorageAdapterGetCurrentRecord( + _Inout_ PWINBIO_PIPELINE Pipeline, + _Out_ PWINBIO_STORAGE_RECORD RecordContents + ) +{ + UNREFERENCED_PARAMETER(Pipeline); + UNREFERENCED_PARAMETER(RecordContents); + + return E_NOTIMPL; +} +//----------------------------------------------------------------------------- + +static HRESULT +WINAPI +StorageAdapterControlUnit( + _Inout_ PWINBIO_PIPELINE Pipeline, + _In_ ULONG ControlCode, + _In_ PUCHAR SendBuffer, + _In_ SIZE_T SendBufferSize, + _In_ PUCHAR ReceiveBuffer, + _In_ SIZE_T ReceiveBufferSize, + _Out_ PSIZE_T ReceiveDataSize, + _Out_ PULONG OperationStatus + ) +{ + UNREFERENCED_PARAMETER(Pipeline); + UNREFERENCED_PARAMETER(ControlCode); + UNREFERENCED_PARAMETER(SendBuffer); + UNREFERENCED_PARAMETER(SendBufferSize); + UNREFERENCED_PARAMETER(ReceiveBuffer); + UNREFERENCED_PARAMETER(ReceiveBufferSize); + UNREFERENCED_PARAMETER(ReceiveDataSize); + UNREFERENCED_PARAMETER(OperationStatus); + + return E_NOTIMPL; +} +//----------------------------------------------------------------------------- + +static HRESULT +WINAPI +StorageAdapterControlUnitPrivileged( + _Inout_ PWINBIO_PIPELINE Pipeline, + _In_ ULONG ControlCode, + _In_ PUCHAR SendBuffer, + _In_ SIZE_T SendBufferSize, + _In_ PUCHAR ReceiveBuffer, + _In_ SIZE_T ReceiveBufferSize, + _Out_ PSIZE_T ReceiveDataSize, + _Out_ PULONG OperationStatus + ) +{ + UNREFERENCED_PARAMETER(Pipeline); + UNREFERENCED_PARAMETER(ControlCode); + UNREFERENCED_PARAMETER(SendBuffer); + UNREFERENCED_PARAMETER(SendBufferSize); + UNREFERENCED_PARAMETER(ReceiveBuffer); + UNREFERENCED_PARAMETER(ReceiveBufferSize); + UNREFERENCED_PARAMETER(ReceiveDataSize); + UNREFERENCED_PARAMETER(OperationStatus); + + return E_NOTIMPL; +} +//----------------------------------------------------------------------------- + + diff --git a/biometrics/adapters/storage_adapter/StorageAdapter.def b/biometrics/adapters/storage_adapter/StorageAdapter.def new file mode 100644 index 000000000..685b9f3fb --- /dev/null +++ b/biometrics/adapters/storage_adapter/StorageAdapter.def @@ -0,0 +1,4 @@ +LIBRARY StorageAdapter + +EXPORTS + WbioQueryStorageInterface diff --git a/biometrics/adapters/storage_adapter/StorageAdapter.h b/biometrics/adapters/storage_adapter/StorageAdapter.h new file mode 100644 index 000000000..d0259213f --- /dev/null +++ b/biometrics/adapters/storage_adapter/StorageAdapter.h @@ -0,0 +1,79 @@ +/*++ + + 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 + +Module Name: + + StorageAdapter.h + +Abstract: + + This module contains a stub implementation of a Storage Adapter + plug-in for the Windows Biometric service. + +Author: + + - + +Environment: + + Win32, user mode only. + +Revision History: + +NOTES: + + (None) + +--*/ +#pragma once + +#include "winbio_adapter.h" + +/////////////////////////////////////////////////////////////////////////////// +// +// The WINIBIO_STORAGE_CONTEXT structure is privately-defined by each +// Storage Adapter. Its purpose is to maintain any information that +// should persist across Storage Adapter API calls. +// +// The Adapter allocates and initializes one of these structures in its +// 'Attach' routine and saves its address in the Pipeline->StorageContext +// field. +// +// The Storage Adapter's 'Detach' routine cleans up and deallocates the +// structure and sets the PipelineContext->StorageContext field to NULL. +// +/////////////////////////////////////////////////////////////////////////////// +typedef struct _WINIBIO_STORAGE_CONTEXT { + // + // The following fields illustrate the kind of information + // the Storage Adapter needs to keep in this structure: + // + // DatabaseId - Identify of the Adapter's + // currently-opened database. + // + // DatabaseHandle - A handle to the Storage Adapter's + // currently-open database. + // + // ResultSet - A collection of records generated by + // a database query operation. + // + // ResultSetCursor - Current location in the result + // set. Used to iterate through the + // result set and make individual + // records available. + // + WINBIO_UUID DatabaseId; + PVOID DatabaseHandle; + PVOID ResultSet; + PVOID ResultSetCursor; + +} WINIBIO_STORAGE_CONTEXT, *PWINIBIO_STORAGE_CONTEXT; + + + diff --git a/biometrics/adapters/storage_adapter/StorageAdapter.rc b/biometrics/adapters/storage_adapter/StorageAdapter.rc new file mode 100644 index 000000000..332ed60ce --- /dev/null +++ b/biometrics/adapters/storage_adapter/StorageAdapter.rc @@ -0,0 +1,29 @@ +//--------------------------------------------------------------------------- +// 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 +// +// BioUsbSample.rc +// +// Copyright (c) 2007 Microsoft Corporation, All Rights Reserved +//--------------------------------------------------------------------------- + + +#include +#include +#include "resource.h" + +// +// 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 "Storage Adapter Sample" +#define VER_INTERNALNAME_STR "StorageAdapter" +#define VER_ORIGINALFILENAME_STR "StorageAdapter.dll" + +#include "common.ver" diff --git a/biometrics/adapters/storage_adapter/StorageAdapter.vcxproj b/biometrics/adapters/storage_adapter/StorageAdapter.vcxproj new file mode 100644 index 000000000..4b72bca8b --- /dev/null +++ b/biometrics/adapters/storage_adapter/StorageAdapter.vcxproj @@ -0,0 +1,228 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {B7ABEA68-C3BF-4E04-A77D-10473902A5E3} + $(MSBuildProjectName) + false + true + Debug + Win32 + {A18B4D8E-1329-49B7-B20C-BC4FCD005769} + + + + 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) + + + + + + + + + + + + + + + + StorageAdapter + + + StorageAdapter + + + StorageAdapter + + + StorageAdapter + + + + true + Level4 + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + true + Level4 + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + true + Level4 + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + true + Level4 + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(AdditionalDependencies);advapi32.lib;kernel32.lib;user32.lib + StorageAdapter.def + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(AdditionalDependencies);advapi32.lib;kernel32.lib;user32.lib + StorageAdapter.def + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(AdditionalDependencies);advapi32.lib;kernel32.lib;user32.lib + StorageAdapter.def + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(AdditionalDependencies);advapi32.lib;kernel32.lib;user32.lib + StorageAdapter.def + + + + + ;%(AdditionalIncludeDirectories) + precomp.h + Create + $(IntDir)\precomp.h.pch + + + ;%(AdditionalIncludeDirectories) + precomp.h + Use + $(IntDir)\precomp.h.pch + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/biometrics/adapters/storage_adapter/StorageAdapter.vcxproj.Filters b/biometrics/adapters/storage_adapter/StorageAdapter.vcxproj.Filters new file mode 100644 index 000000000..fa9088c87 --- /dev/null +++ b/biometrics/adapters/storage_adapter/StorageAdapter.vcxproj.Filters @@ -0,0 +1,33 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {0E001F97-1F21-4426-83CC-9917217142A5} + + + h;hpp;hxx;hm;inl;inc;xsd + {8286FF05-36A6-4565-884A-45E260B3A4EF} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {75330418-4B06-44A9-8189-EB643689CB0C} + + + + + Source Files + + + Source Files + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/biometrics/adapters/storage_adapter/precomp.h b/biometrics/adapters/storage_adapter/precomp.h new file mode 100644 index 000000000..c36eb36cd --- /dev/null +++ b/biometrics/adapters/storage_adapter/precomp.h @@ -0,0 +1,62 @@ +/*++ + + 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 + +Module Name: + + precomp.h + +Abstract: + + This module contains identifies all the headers that + shoulde be pre-compiled. + +Author: + + - + +Environment: + + Win32, user mode only. + +Revision History: + +NOTES: + + (None) + +--*/ +#pragma once + +// +// Necessary for compiling under VC. +// +#if(!defined(WINVER) || (WINVER < 0x0500)) + #undef WINVER + #define WINVER 0x0500 +#endif +#if(!defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0500)) + #undef _WIN32_WINNT + #define _WIN32_WINNT 0x0500 +#endif + +// +// Required header files that shouldn't change often. +// +#include +#include +#include + +// +// StrSafe.h needs to be included last +// to disallow unsafe string functions. +#include + +#ifndef ARGUMENT_PRESENT +#define ARGUMENT_PRESENT(x) ((x) != NULL) +#endif diff --git a/biometrics/adapters/storage_adapter/precompsrc.cpp b/biometrics/adapters/storage_adapter/precompsrc.cpp new file mode 100644 index 000000000..5944cf515 --- /dev/null +++ b/biometrics/adapters/storage_adapter/precompsrc.cpp @@ -0,0 +1 @@ +#include "precomp.h" \ No newline at end of file diff --git a/biometrics/adapters/storage_adapter/resource.h b/biometrics/adapters/storage_adapter/resource.h new file mode 100644 index 000000000..95ed37fa6 --- /dev/null +++ b/biometrics/adapters/storage_adapter/resource.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 +// +// BioUsbSample.rc +// +// Copyright (c) 2007 Microsoft Corporation, All Rights Reserved +//--------------------------------------------------------------------------- + +#pragma once + diff --git a/biometrics/driver/BioUsbSample.ctl b/biometrics/driver/BioUsbSample.ctl new file mode 100644 index 000000000..bcec77c14 --- /dev/null +++ b/biometrics/driver/BioUsbSample.ctl @@ -0,0 +1 @@ +864936A6-DB79-451e-B764-E720D61A9361 WudfBioUsbSampleTraceGuid \ No newline at end of file diff --git a/biometrics/driver/BioUsbSample.rc b/biometrics/driver/BioUsbSample.rc new file mode 100644 index 000000000..36f83c5a6 --- /dev/null +++ b/biometrics/driver/BioUsbSample.rc @@ -0,0 +1,26 @@ +// 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 +// +// BioUsbSample.rc +// + + +#include +#include +#include "resource.h" + +// +// 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 "WUDF: Biometric Sample" +#define VER_INTERNALNAME_STR "WudfBioUsbSample" +#define VER_ORIGINALFILENAME_STR "WudfBioUsbSample.dll" + +#include "common.ver" diff --git a/biometrics/driver/Device.cpp b/biometrics/driver/Device.cpp new file mode 100644 index 000000000..8f8dd6b45 --- /dev/null +++ b/biometrics/driver/Device.cpp @@ -0,0 +1,1834 @@ +/*++ + + 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 + +Module Name: + + Device.cpp + +Abstract: + + This module contains the implementation of the Biometric + device driver. + +Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ +#include "internal.h" +#include "device.tmh" + +#pragma warning(disable : 4189) + +DWORD WINAPI +CaptureSleepThread( + LPVOID lpParam + ) +{ + CBiometricDevice *device = (CBiometricDevice *) lpParam; + PCAPTURE_SLEEP_PARAMS sleepParams = device->GetCaptureSleepParams(); + + // + // Make sure it is less than or equal to 1 minute. + // + if (sleepParams->SleepValue > 60) + { + sleepParams->SleepValue = 60; + } + + Sleep(sleepParams->SleepValue * 1000); + + device->CompletePendingRequest(sleepParams->Hr, sleepParams->Information); + + return 0; +} + + +HRESULT +CBiometricDevice::CreateInstanceAndInitialize( + _In_ IWDFDriver *FxDriver, + _In_ IWDFDeviceInitialize * FxDeviceInit, + _Out_ CBiometricDevice **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 + +--*/ +{ + // + // Create a new instance of the device class + // + CComObject *pMyDevice = NULL; + HRESULT hr = CComObject::CreateInstance( &pMyDevice ); + + if (SUCCEEDED(hr)) + { + + // + // Initialize the instance. This calls the WUDF framework, + // which keeps a reference to the device interface for the lifespan + // of the device. + // + if (NULL != pMyDevice) + { + hr = pMyDevice->Initialize(FxDriver, FxDeviceInit); + + if (FAILED(hr)) + { + BiometricSafeRelease(pMyDevice); + } + + } + + *Device = pMyDevice; + + } + + return hr; +} + +HRESULT +CBiometricDevice::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; + HRESULT hr = S_OK; + IUnknown *unknown = NULL; + + // + // Configure things like the locking model before we go to create our + // partner device. + // + + // + // Set the locking model. + // + + FxDeviceInit->SetLockingConstraint(WdfDeviceLevel); + + // + // 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. + // + + // + // We pass an IUnknown reference to CreateDevice, which takes its own + // reference if everything works. + // + + if (SUCCEEDED(hr)) + { + hr = this->QueryInterface(__uuidof(IUnknown), (void **)&unknown); + + } + + if (SUCCEEDED(hr)) + { + + hr = FxDriver->CreateDevice(FxDeviceInit, unknown, &fxDevice); + BiometricSafeRelease(unknown); + } + + // + // 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. + // + + BiometricSafeRelease(fxDevice); + } + + return hr; +} + +HRESULT +CBiometricDevice::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 + +--*/ +{ + + HRESULT hr = S_OK; + + // + // Create the I/O queue + // + + if (SUCCEEDED(hr)) + { + hr = CBiometricIoQueue::CreateInstanceAndInitialize(m_FxDevice, this, &m_IoQueue); + + if (SUCCEEDED(hr)) + { + hr = m_IoQueue->Configure(); + } + } + + // + // Create Device Interface + // + + if (SUCCEEDED(hr)) + { + hr = m_FxDevice->CreateDeviceInterface(&GUID_DEVINTERFACE_BIOMETRIC_READER, + NULL); + } + + if (SUCCEEDED(hr)) + { + hr = m_FxDevice->AssignDeviceInterfaceState(&GUID_DEVINTERFACE_BIOMETRIC_READER, + NULL, + TRUE); + } + + // + // TODO - this is where additional interfaces can be exposed. + // + + return hr; +} + +HRESULT +CBiometricDevice::OnPrepareHardware( + _In_ IWDFDevice * /* FxDevice */ + ) +/*++ + +Routine Description: + + This routine is invoked to ready the driver + to talk to hardware. It opens the handle to the + device and talks to it using the WINUSB interface. + It invokes WINUSB to discver the interfaces and stores + the information related to bulk endpoints. + +Arguments: + + FxDevice : Pointer to the WDF device interface + +Return Value: + + HRESULT + +--*/ +{ + PWSTR deviceName = NULL; + DWORD deviceNameCch = 0; + + HRESULT hr; + + // + // Get the device name. + // Get the length to allocate first + // + + hr = m_FxDevice->RetrieveDeviceName(NULL, &deviceNameCch); + + if (FAILED(hr)) + { + TraceEvents(TRACE_LEVEL_ERROR, + BIOMETRIC_TRACE_DEVICE, + "%!FUNC! Cannot get device name %!hresult!", + hr + ); + } + + // + // Allocate the buffer + // + + if (SUCCEEDED(hr)) + { + deviceName = (PWSTR) malloc(deviceNameCch * sizeof (WCHAR)); + + if (deviceName == NULL) + { + hr = E_OUTOFMEMORY; + } + } + + // + // Get the actual name + // + + if (SUCCEEDED(hr)) + { + hr = m_FxDevice->RetrieveDeviceName(deviceName, &deviceNameCch); + + if (FAILED(hr)) + { + TraceEvents(TRACE_LEVEL_ERROR, + BIOMETRIC_TRACE_DEVICE, + "%!FUNC! Cannot get device name %!hresult!", + hr + ); + } + } + + if (SUCCEEDED(hr)) + { + TraceEvents(TRACE_LEVEL_INFORMATION, + BIOMETRIC_TRACE_DEVICE, + "%!FUNC! Device name %S", + deviceName + ); + } + + // + // Create USB I/O Targets and configure them + // + + if (SUCCEEDED(hr)) + { + hr = CreateUsbIoTargets(); + } + + if (SUCCEEDED(hr)) + { + ULONG length = sizeof(m_Speed); + + hr = m_pIUsbTargetDevice->RetrieveDeviceInformation(DEVICE_SPEED, + &length, + &m_Speed); + if (FAILED(hr)) + { + TraceEvents(TRACE_LEVEL_ERROR, + BIOMETRIC_TRACE_DEVICE, + "%!FUNC! Cannot get usb device speed information %!HRESULT!", + hr + ); + } + } + + if (SUCCEEDED(hr)) + { + TraceEvents(TRACE_LEVEL_INFORMATION, + BIOMETRIC_TRACE_DEVICE, + "%!FUNC! Speed - %x\n", + m_Speed + ); + } + + // + // Setup power-management settings on the device. + // + + if (SUCCEEDED(hr)) + { + hr = SetPowerManagement(); + } + + // + // We have non-power managed queues so we Stop them in OnReleaseHardware + // and start them in OnPrepareHardware + // + + if (SUCCEEDED(hr)) + { + m_IoQueue->Start(); + } + + if (SUCCEEDED(hr)) + { + // + // If the device stack allows read to remain pending across power-down + // and up, it can be initiated during OnPrepareHardware + // + // If the device stack doesn't allow the read to remain pending (i.e. it + // cancels the pending read during power transition) driver will have to + // stop sending pending read during D0Exit and re-initiate it during + // D0Entry + // + // USB core actually doesn't allow read to remain pending across power + // transition but WinUSB does. Since we are layered above WinUSB we don't + // need to manage pending read across power transitions. + // + + hr = InitiatePendingRead(); + } + + if (deviceName) + { + free(deviceName); + deviceName = NULL; + } + + return hr; +} + +HRESULT +CBiometricDevice::OnReleaseHardware( + _In_ IWDFDevice * /* FxDevice */ + ) +/*++ + +Routine Description: + + This routine is invoked when the device is being removed or stopped + It releases all resources allocated for this device. + +Arguments: + + FxDevice - Pointer to the Device object. + +Return Value: + + HRESULT - Always succeeds. + +--*/ +{ + // + // Cancel the pending data collection I/O, if one exists. + // + CompletePendingRequest(HRESULT_FROM_WIN32(ERROR_CANCELLED), 0); + + // + // Since we have non-power managed queues, we need to Stop them + // explicitly + // + // We need to stop them before deleting I/O targets otherwise we + // will continue to get I/O and our I/O processing will try to access + // freed I/O targets + // + // We initialize queues in CMyDevice::Initialize so we can't get + // here with queues being NULL and don't need to guard against that + // + + m_IoQueue->StopSynchronously(); + + // + // Delete USB Target Device WDF Object, this will in turn + // delete all the children - interface and the pipe objects + // + // This makes sure that + // 1. We drain the I/O before releasing the targets + // a. We always need to do that for the pending read which does + // not come from an I/O queue + // b. We need to do this even for I/O coming from I/O queues because + // we set them to non-power managed queues (to leverage wait/wake + // from WinUsb.sys) + // 2. We remove USB target objects from object tree (and thereby free them) + // before any potential subsequent OnPrepareHardware creates new ones + // + // m_pIUsbTargetDevice could be NULL if OnPrepareHardware failed so we need + // to guard against that + // + + if (m_pIUsbTargetDevice) + { + m_pIUsbTargetDevice->DeleteWdfObject(); + } + + // + // This sample has a thread that will sleep for 5 seconds before + // completing a capture request. + // + if (m_SleepThread != INVALID_HANDLE_VALUE) + { + WaitForSingleObject(m_SleepThread, INFINITE); + CloseHandle(m_SleepThread); + m_SleepThread = INVALID_HANDLE_VALUE; + } + + return S_OK; +} + +HRESULT +CBiometricDevice::CreateUsbIoTargets( + ) +/*++ + +Routine Description: + + This routine creates Usb device, interface and pipe objects + +Arguments: + + None + +Return Value: + + HRESULT +--*/ +{ + HRESULT hr; + UCHAR NumEndPoints = 0; + IWDFUsbTargetFactory * pIUsbTargetFactory = NULL; + IWDFUsbTargetDevice * pIUsbTargetDevice = NULL; + IWDFUsbInterface * pIUsbInterface = NULL; + IWDFUsbTargetPipe * pIUsbPipe = NULL; + + hr = m_FxDevice->QueryInterface(IID_PPV_ARGS(&pIUsbTargetFactory)); + + if (FAILED(hr)) + { + TraceEvents(TRACE_LEVEL_ERROR, + BIOMETRIC_TRACE_DEVICE, + "%!FUNC! Cannot get usb target factory %!HRESULT!", + hr + ); + } + + if (SUCCEEDED(hr)) + { + hr = pIUsbTargetFactory->CreateUsbTargetDevice( + &pIUsbTargetDevice); + if (FAILED(hr)) + { + TraceEvents(TRACE_LEVEL_ERROR, + BIOMETRIC_TRACE_DEVICE, + "%!FUNC! Unable to create USB Device I/O Target %!HRESULT!", + hr + ); + } + else + { + m_pIUsbTargetDevice = pIUsbTargetDevice; + + // + // Release the creation reference as object tree will maintain a reference + // + + BiometricSafeRelease(pIUsbTargetDevice); + } + } + + if (SUCCEEDED(hr)) + { + UCHAR NumInterfaces = pIUsbTargetDevice->GetNumInterfaces(); + TraceEvents(TRACE_LEVEL_INFORMATION, + BIOMETRIC_TRACE_DEVICE, + "%!FUNC! Found %u interfaces", + NumInterfaces + ); + + hr = pIUsbTargetDevice->RetrieveUsbInterface(0, &pIUsbInterface); + if (FAILED(hr)) + { + TraceEvents(TRACE_LEVEL_ERROR, + BIOMETRIC_TRACE_DEVICE, + "%!FUNC! Unable to retrieve USB interface from USB Device I/O Target %!HRESULT!", + hr + ); + } + else + { + m_pIUsbInterface = pIUsbInterface; + + BiometricSafeRelease(pIUsbInterface); // release creation reference + } + } + + if (SUCCEEDED(hr)) + { + NumEndPoints = pIUsbInterface->GetNumEndPoints(); + + if (NumEndPoints != NUM_WBDI_ENDPOINTS) + { + hr = E_UNEXPECTED; + TraceEvents(TRACE_LEVEL_ERROR, + BIOMETRIC_TRACE_DEVICE, + "%!FUNC! Has %d endpoints, expected %d, returning %!HRESULT! ", + NumEndPoints, + NUM_WBDI_ENDPOINTS, + hr + ); + } + } + + if (SUCCEEDED(hr)) + { + for (UCHAR PipeIndex = 0; PipeIndex < NumEndPoints; PipeIndex++) + { + hr = pIUsbInterface->RetrieveUsbPipeObject(PipeIndex, + &pIUsbPipe); + + if (FAILED(hr)) + { + TraceEvents(TRACE_LEVEL_ERROR, + BIOMETRIC_TRACE_DEVICE, + "%!FUNC! Unable to retrieve USB Pipe for PipeIndex %d, %!HRESULT!", + PipeIndex, + hr + ); + } + else + { + if ( pIUsbPipe->IsInEndPoint() ) + { + if ( UsbdPipeTypeInterrupt == pIUsbPipe->GetType() ) + { + m_pIUsbInterruptPipe = pIUsbPipe; + } + else if ( UsbdPipeTypeBulk == pIUsbPipe->GetType() ) + { + m_pIUsbInputPipe = pIUsbPipe; + } + else + { + pIUsbPipe->DeleteWdfObject(); + } + } + else if ( pIUsbPipe->IsOutEndPoint() && (UsbdPipeTypeBulk == pIUsbPipe->GetType()) ) + { + m_pIUsbOutputPipe = pIUsbPipe; + } + else + { + pIUsbPipe->DeleteWdfObject(); + } + + BiometricSafeRelease(pIUsbPipe); //release creation reference + } + } + + if (NULL == m_pIUsbInputPipe || NULL == m_pIUsbOutputPipe) + { + hr = E_UNEXPECTED; + TraceEvents(TRACE_LEVEL_ERROR, + BIOMETRIC_TRACE_DEVICE, + "%!FUNC! Input or output pipe not found, returning %!HRESULT!", + hr + ); + } + } + + BiometricSafeRelease(pIUsbTargetFactory); + + return hr; +} + +HRESULT +CBiometricDevice::SetPowerManagement( + VOID + ) +/*++ + + Routine Description: + + This method enables the WinUSB driver to power the device down when it is + idle. + + Arguments: + + None + + Return Value: + + Status + +--*/ +{ + + HRESULT hr = S_OK; + ULONG value = WBDI_SUSPEND_DELAY; + + hr = m_pIUsbTargetDevice->SetPowerPolicy( SUSPEND_DELAY, + sizeof(ULONG), + (PVOID) &value ); + + if (FAILED(hr)) + { + TraceEvents(TRACE_LEVEL_ERROR, + BIOMETRIC_TRACE_DEVICE, + "%!FUNC! Unable to set power policy (SUSPEND_DELAY) for the device %!HRESULT!", + hr + ); + } + + + // + // Finally enable auto-suspend. + // + + if (SUCCEEDED(hr)) + { + BOOL AutoSuspsend = TRUE; + + hr = m_pIUsbTargetDevice->SetPowerPolicy( AUTO_SUSPEND, + sizeof(BOOL), + (PVOID) &AutoSuspsend ); + } + + if (FAILED(hr)) + { + TraceEvents(TRACE_LEVEL_ERROR, + BIOMETRIC_TRACE_DEVICE, + "%!FUNC! Unable to set power policy (AUTO_SUSPEND) for the device %!HRESULT!", + hr + ); + } + + return hr; +} + +HRESULT +CBiometricDevice::SendControlTransferSynchronously( + _In_ PWINUSB_SETUP_PACKET SetupPacket, + _Inout_updates_(BufferLength) PBYTE Buffer, + _In_ ULONG BufferLength, + _Out_ PULONG LengthTransferred + ) +/*++ + + Routine Description: + + This method synchronously sends a control transfer request to + the USB I/O target. + + Arguments: + + SetupPacket - The command parameter structure + + Buffer - The data to transfer + + BufferLength - The size of the data buffer to transfer + + LengthTransferred - Contains the actual number of bytes transferred. + + Return Value: + + HRESULT + +--*/ +{ + HRESULT hr = S_OK; + IWDFIoRequest *pWdfRequest = NULL; + IWDFDriver * FxDriver = NULL; + IWDFMemory * FxMemory = NULL; + IWDFRequestCompletionParams * FxComplParams = NULL; + IWDFUsbRequestCompletionParams * FxUsbComplParams = NULL; + + *LengthTransferred = 0; + + hr = m_FxDevice->CreateRequest( NULL, //pCallbackInterface + NULL, //pParentObject + &pWdfRequest); + + if (SUCCEEDED(hr)) + { + m_FxDevice->GetDriver(&FxDriver); + + hr = FxDriver->CreatePreallocatedWdfMemory( Buffer, + BufferLength, + NULL, //pCallbackInterface + pWdfRequest, //pParetObject + &FxMemory ); + } + + if (SUCCEEDED(hr)) + { + hr = m_pIUsbTargetDevice->FormatRequestForControlTransfer( pWdfRequest, + SetupPacket, + FxMemory, + NULL); //TransferOffset + } + + if (SUCCEEDED(hr)) + { + hr = pWdfRequest->Send( m_pIUsbTargetDevice, + WDF_REQUEST_SEND_OPTION_SYNCHRONOUS, + 0); //Timeout + } + + if (SUCCEEDED(hr)) + { + pWdfRequest->GetCompletionParams(&FxComplParams); + + hr = FxComplParams->GetCompletionStatus(); + } + + if (SUCCEEDED(hr)) + { + HRESULT hrQI = FxComplParams->QueryInterface(IID_PPV_ARGS(&FxUsbComplParams)); + if (SUCCEEDED(hrQI)) + { + FxUsbComplParams->GetDeviceControlTransferParameters( NULL, + LengthTransferred, + NULL, + NULL ); + } + } + + BiometricSafeRelease(FxUsbComplParams); + BiometricSafeRelease(FxComplParams); + BiometricSafeRelease(FxMemory); + + pWdfRequest->DeleteWdfObject(); + BiometricSafeRelease(pWdfRequest); + + BiometricSafeRelease(FxDriver); + + return hr; +} + +WDF_IO_TARGET_STATE +CBiometricDevice::GetTargetState( + IWDFIoTarget * pTarget + ) +/*++ + + Routine Description: + + This method gets the state of the I/O target + + Arguments: + + pTarget - A pointer to the I/O target + + Return Value: + + WDF_IO_TARGET_STATE + +--*/ +{ + IWDFIoTargetStateManagement * pStateMgmt = NULL; + WDF_IO_TARGET_STATE state = WdfIoTargetStateUndefined; + + HRESULT hrQI = pTarget->QueryInterface(IID_PPV_ARGS(&pStateMgmt)); + if (FAILED(hrQI)) + { + TraceEvents(TRACE_LEVEL_ERROR, + BIOMETRIC_TRACE_DEVICE, + "%!FUNC! Cannot query interface %!HRESULT!", + hrQI + ); + + return state; + } + + state = pStateMgmt->GetState(); + + BiometricSafeRelease(pStateMgmt); + + return state; +} + +HRESULT +CBiometricDevice::InitiatePendingRead( + VOID + ) +/*++ + + Routine Description: + + This routine starts up a cycling read on the interrupt pipe. As each + read completes it will start up the next one. + + Arguments: + + None + + Return Value: + + Status + +--*/ +{ + HRESULT hr = S_OK; + IWDFIoRequest * FxRequest = NULL; + IWDFMemory * FxMemory = NULL; + IWDFDriver * FxDriver = NULL; + IRequestCallbackRequestCompletion * FxComplCallback = NULL; + + hr = m_FxDevice->CreateRequest(NULL, NULL, &FxRequest); + + if (SUCCEEDED(hr)) + { + m_FxDevice->GetDriver(&FxDriver); + + hr = FxDriver->CreatePreallocatedWdfMemory( (PBYTE) &m_InterruptMessage, + sizeof(m_InterruptMessage), + NULL, //pCallbackInterface + FxRequest, //pParetObject + &FxMemory ); + } + + if (SUCCEEDED(hr)) + { + hr = m_pIUsbInterruptPipe->FormatRequestForRead(FxRequest, + NULL, //pFile - IoTarget would apply its file + FxMemory, + NULL, //Memory offset + NULL); //Device offset + } + + if (SUCCEEDED(hr)) + { + hr = this->QueryInterface(IID_PPV_ARGS(&FxComplCallback)); + if (SUCCEEDED(hr)) + { + FxRequest->SetCompletionCallback(FxComplCallback, NULL); + + hr = FxRequest->Send(m_pIUsbInterruptPipe, 0, 0); + } + } + + if (FAILED(hr)) + { + m_InterruptReadProblem = hr; + + if (FxRequest) + { + FxRequest->DeleteWdfObject(); + } + } + + BiometricSafeRelease(FxRequest); + BiometricSafeRelease(FxMemory); + BiometricSafeRelease(FxDriver); + BiometricSafeRelease(FxComplCallback); + + return hr; +} + +VOID +CBiometricDevice::OnCompletion( + _In_ IWDFIoRequest* FxRequest, + _In_ IWDFIoTarget* pIoTarget, + _In_ IWDFRequestCompletionParams* pParams, + _In_ PVOID pContext + ) +/*++ + + Routine Description: + + This method is called when the asynchronous pending + read on the interrupt pipe completes. + + Arguments: + + FxRequest - The request object + + pIoTarget - The I/O target for the request + + pParams - The completion parameters + + pContext - Optional context + + Return Value: + + None + +--*/ +{ + UNREFERENCED_PARAMETER(pIoTarget); + UNREFERENCED_PARAMETER(pContext); + + IWDFUsbRequestCompletionParams * pUsbComplParams = NULL; + IWDFMemory * FxMemory = NULL; + SIZE_T bytesRead = 0; + HRESULT hrCompletion = pParams->GetCompletionStatus(); + + TraceEvents(TRACE_LEVEL_INFORMATION, + BIOMETRIC_TRACE_DEVICE, + "%!FUNC! Pending read completed with %!hresult!", + hrCompletion + ); + + if (FAILED(hrCompletion)) + { + m_InterruptReadProblem = hrCompletion; + } + else + { + // + // Get the interrupt message + // + + HRESULT hrQI = pParams->QueryInterface(IID_PPV_ARGS(&pUsbComplParams)); + if (SUCCEEDED(hrQI)) + { + pUsbComplParams->GetPipeReadParameters(&FxMemory, &bytesRead, NULL); + if (bytesRead == sizeof(INTERRUPT_MESSAGE)) + { + + PVOID pBuff = FxMemory->GetDataBuffer(NULL); + CopyMemory(&m_InterruptMessage, pBuff, sizeof(m_InterruptMessage)); + + // + // TODO: Parse m_InterruptMessage + // + } + } + } + + // + // Don't complete the request since we created it, just delete it. + // + + FxRequest->DeleteWdfObject(); + + // + // Re-initiate pending read if I/O Target is not stopped/removed + // + + if (WdfIoTargetStarted == GetTargetState(m_pIUsbInterruptPipe)) + { + int numRetries = 0; + HRESULT hr = InitiatePendingRead(); + + // + // If we fail here, the device will become unresponsive. + // Re-issue the request until it succeeds. + // + for (numRetries = 0; FAILED(hr) && numRetries < 3; ++numRetries) + { + hr = InitiatePendingRead(); + } + } + + BiometricSafeRelease(pUsbComplParams); + BiometricSafeRelease(FxMemory); +} + + +// +// I/O handlers +// + +void +CBiometricDevice::GetIoRequestParams( + _In_ IWDFIoRequest *FxRequest, + _Out_ ULONG *MajorControlCode, + _Outptr_result_bytebuffer_(*InputBufferSizeInBytes) PUCHAR *InputBuffer, + _Out_ SIZE_T *InputBufferSizeInBytes, + _Outptr_result_bytebuffer_(*OutputBufferSizeInBytes) PUCHAR *OutputBuffer, + _Out_ SIZE_T *OutputBufferSizeInBytes + ) +/*++ + + Routine Description: + + This method retrieves the input and output buffers associated with the request. + + Arguments: + + FxRequest - The WDF request oject + + MajorControlCode - Contains the control code for the I/O request + + InputBuffer - Contains the input buffer pointer + + InputBufferSizeInBytes - Contains the size of the input buffer + + OutputBuffer - Contains the output buffer pointer + + OutputBufferSizeInBytes - Contains the size of the output buffer + + Return Value: + + None + +--*/ +{ + // + // Get main parameters + // + FxRequest->GetDeviceIoControlParameters(MajorControlCode, + InputBufferSizeInBytes, + OutputBufferSizeInBytes); + + // Get pointer to input buffer + IWDFMemory *fxMemory = NULL; + FxRequest->GetInputMemory(&fxMemory); + if (fxMemory) + { + *InputBuffer = (PUCHAR) fxMemory->GetDataBuffer(InputBufferSizeInBytes); + BiometricSafeRelease(fxMemory); + } + + // Save pointer to reply buffer + fxMemory = NULL; + FxRequest->GetOutputMemory(&fxMemory); + if (fxMemory) + { + *OutputBuffer = (PUCHAR) fxMemory->GetDataBuffer(OutputBufferSizeInBytes); + BiometricSafeRelease(fxMemory); + } +} + +void +CBiometricDevice::OnGetAttributes( + _Inout_ IWDFIoRequest *FxRequest + ) +/*++ + + Routine Description: + + This method is invoked when the IOCTL_BIOMETRIC_GET_ATTRIBUTES command is called. + + Arguments: + + FxRequest - The output for this request is a PWINBIO_SENSOR_ATTRIBUTES. + + Return Value: + + None + +--*/ +{ + CRequestHelper MyRequest(FxRequest); // RAII helper class + ULONG controlCode = 0; + PUCHAR inputBuffer= NULL; + SIZE_T inputBufferSize = 0; + PWINBIO_SENSOR_ATTRIBUTES sensorAttributes = NULL; + SIZE_T outputBufferSize; + + // + // Get the request parameters + // + GetIoRequestParams(FxRequest, + &controlCode, + &inputBuffer, + &inputBufferSize, + (PUCHAR *)&sensorAttributes, + &outputBufferSize); + + // + // Make sure we have an output buffer big enough + // + if (sensorAttributes == NULL || outputBufferSize < sizeof(DWORD)) + { + // We cannot return size information. + TraceEvents(TRACE_LEVEL_ERROR, + BIOMETRIC_TRACE_DEVICE, + "%!FUNC!Output buffer NULL or too small to return size information."); + MyRequest.SetCompletionHr(E_INVALIDARG); + return; + } + + // We only have one supported format, so sizeof (WINBIO_SENSOR_ATTRIBUTES) is sufficient. + if (outputBufferSize < sizeof(WINBIO_SENSOR_ATTRIBUTES)) + { + // Buffer too small. + TraceEvents(TRACE_LEVEL_ERROR, + BIOMETRIC_TRACE_DEVICE, + "%!FUNC!Buffer too small - return size necessary in PayloadSize - 0x%x.", sizeof(WINBIO_SENSOR_ATTRIBUTES)); + sensorAttributes->PayloadSize = (DWORD) sizeof(WINBIO_SENSOR_ATTRIBUTES); + MyRequest.SetInformation(sizeof(DWORD)); + MyRequest.SetCompletionHr(S_OK); + return; + } + + // + // Fill in the attribute payload structure + // + RtlZeroMemory(sensorAttributes, outputBufferSize); + sensorAttributes->PayloadSize = (DWORD) sizeof(WINBIO_SENSOR_ATTRIBUTES); + sensorAttributes->WinBioHresult = S_OK; + sensorAttributes->WinBioVersion.MajorVersion = WINBIO_WBDI_MAJOR_VERSION; + sensorAttributes->WinBioVersion.MinorVersion = WINBIO_WBDI_MINOR_VERSION; + sensorAttributes->SensorType = WINBIO_TYPE_FINGERPRINT; + sensorAttributes->SensorSubType = WINBIO_FP_SENSOR_SUBTYPE_SWIPE; + sensorAttributes->Capabilities = WINBIO_CAPABILITY_SENSOR; + sensorAttributes->SupportedFormatEntries = 1; + sensorAttributes->SupportedFormat[0].Owner = WINBIO_ANSI_381_FORMAT_OWNER; + sensorAttributes->SupportedFormat[0].Type= WINBIO_ANSI_381_FORMAT_TYPE; + RtlCopyMemory(sensorAttributes->ManufacturerName, SAMPLE_MANUFACTURER_NAME, (wcslen(SAMPLE_MANUFACTURER_NAME)+1)*sizeof(WCHAR)); + RtlCopyMemory(sensorAttributes->ModelName, SAMPLE_MODEL_NAME, (wcslen(SAMPLE_MODEL_NAME)+1)*sizeof(WCHAR)); + RtlCopyMemory(sensorAttributes->SerialNumber, SAMPLE_SERIAL_NUMBER, (wcslen(SAMPLE_SERIAL_NUMBER)+1)*sizeof(WCHAR)); + sensorAttributes->FirmwareVersion.MajorVersion = 1; + sensorAttributes->FirmwareVersion.MinorVersion = 0; + + MyRequest.SetInformation(sensorAttributes->PayloadSize); + MyRequest.SetCompletionHr(S_OK); +} + + +void +CBiometricDevice::OnReset( + _Inout_ IWDFIoRequest *FxRequest + ) +/*++ + + Routine Description: + + This method is invoked when the IOCTL_BIOMETRIC_RESET command is called. + + Arguments: + + FxRequest - + + Return Value: + + None + +--*/ +{ + CRequestHelper MyRequest(FxRequest); // RAII helper class + ULONG controlCode = 0; + PUCHAR inputBuffer= NULL; + SIZE_T inputBufferSize = 0; + PWINBIO_BLANK_PAYLOAD blankPayload = NULL; + SIZE_T outputBufferSize; + + // + // Get the request parameters + // + GetIoRequestParams(FxRequest, + &controlCode, + &inputBuffer, + &inputBufferSize, + (PUCHAR *)&blankPayload, + &outputBufferSize); + + // + // Make sure we have an output buffer big enough + // + if (blankPayload== NULL || outputBufferSize < sizeof(DWORD)) + { + // We cannot return size information. + TraceEvents(TRACE_LEVEL_ERROR, + BIOMETRIC_TRACE_DEVICE, + "%!FUNC!Output buffer NULL or too small to return size information."); + MyRequest.SetInformation(sizeof(DWORD)); + MyRequest.SetCompletionHr(S_OK); + MyRequest.SetCompletionHr(E_INVALIDARG); + return; + } + + if (outputBufferSize < sizeof(WINBIO_BLANK_PAYLOAD)) + { + // Buffer too small. + TraceEvents(TRACE_LEVEL_ERROR, + BIOMETRIC_TRACE_DEVICE, + "%!FUNC!Buffer too small - return size necessary in PayloadSize - 0x%x.", sizeof(WINBIO_DIAGNOSTICS)); + MyRequest.SetInformation(sizeof(DWORD)); + MyRequest.SetCompletionHr(S_OK); + return; + } + + // + // This is a simulated device. Nothing to do here except cancel the pending data + // collection I/O, if one exists. + // + CompletePendingRequest(HRESULT_FROM_WIN32(ERROR_CANCELLED), 0); + + // + // Fill in the OUT payload structure + // + RtlZeroMemory(blankPayload, outputBufferSize); + blankPayload->PayloadSize = (DWORD) sizeof(WINBIO_BLANK_PAYLOAD); + blankPayload->WinBioHresult = S_OK; + + FxRequest->SetInformation(blankPayload->PayloadSize); + MyRequest.SetCompletionHr(S_OK); + +} + +void +CBiometricDevice::OnCalibrate( + _Inout_ IWDFIoRequest *FxRequest + ) +/*++ + + Routine Description: + + This method is invoked when the IOCTL_BIOMETRIC_CALIBRATE command is called. + + Arguments: + + FxRequest - + IN - blank payload + OUT - PWINBIO_CALIBRATION_INFO + + Return Value: + + None + +--*/ +{ + CRequestHelper MyRequest(FxRequest); // RAII helper class + ULONG controlCode = 0; + PUCHAR inputBuffer= NULL; + SIZE_T inputBufferSize = 0; + PWINBIO_CALIBRATION_INFO calibrationInfo = NULL; + SIZE_T outputBufferSize; + + // + // Get the request parameters + // + GetIoRequestParams(FxRequest, + &controlCode, + &inputBuffer, + &inputBufferSize, + (PUCHAR *)&calibrationInfo, + &outputBufferSize); + + // + // Make sure we have an output buffer big enough + // + if (calibrationInfo == NULL || outputBufferSize < sizeof(DWORD)) + { + // We cannot return size information. + TraceEvents(TRACE_LEVEL_ERROR, + BIOMETRIC_TRACE_DEVICE, + "%!FUNC!Output buffer NULL or too small to return size information."); + MyRequest.SetCompletionHr(E_INVALIDARG); + return; + } + + if (outputBufferSize < sizeof(WINBIO_CALIBRATION_INFO)) + { + // Buffer too small. + TraceEvents(TRACE_LEVEL_ERROR, + BIOMETRIC_TRACE_DEVICE, + "%!FUNC!Buffer too small - return size necessary in PayloadSize - 0x%x.", sizeof(WINBIO_DIAGNOSTICS)); + calibrationInfo->PayloadSize = (DWORD) sizeof(WINBIO_CALIBRATION_INFO); + MyRequest.SetInformation(sizeof(DWORD)); + MyRequest.SetCompletionHr(S_OK); + return; + } + + // + // This is where code to calibrate the device goes. + // + + // + // Fill in the OUT payload structure + // + RtlZeroMemory(calibrationInfo, outputBufferSize); + calibrationInfo->PayloadSize = (DWORD) sizeof(WINBIO_CALIBRATION_INFO); + calibrationInfo->WinBioHresult = S_OK; + + MyRequest.SetInformation(calibrationInfo->PayloadSize); + MyRequest.SetCompletionHr(S_OK); +} + + +void +CBiometricDevice::OnGetSensorStatus( + _Inout_ IWDFIoRequest *FxRequest + ) +/*++ + + Routine Description: + + This method is invoked when the IOCTL_BIOMETRIC_GET_SENSOR_STATUS command is called. + + Arguments: + + FxRequest - + IN payload: none + OUT payload: PWINBIO_DIAGNOSTICS + + Return Value: + + None + +--*/ +{ + CRequestHelper MyRequest(FxRequest); // RAII helper class + ULONG controlCode = 0; + PUCHAR inputBuffer= NULL; + SIZE_T inputBufferSize = 0; + PWINBIO_DIAGNOSTICS diagnostics = NULL; + SIZE_T outputBufferSize; + + // + // Get the request parameters + // + GetIoRequestParams(FxRequest, + &controlCode, + &inputBuffer, + &inputBufferSize, + (PUCHAR *)&diagnostics, + &outputBufferSize); + + // + // Make sure we have an output buffer big enough + // + if (diagnostics == NULL || outputBufferSize < sizeof(DWORD)) + { + // We cannot return size information. + TraceEvents(TRACE_LEVEL_ERROR, + BIOMETRIC_TRACE_DEVICE, + "%!FUNC!Output buffer NULL or too small to return size information."); + MyRequest.SetCompletionHr(E_INVALIDARG); + return; + } + + if (outputBufferSize < sizeof(WINBIO_DIAGNOSTICS)) + { + // Buffer too small. + TraceEvents(TRACE_LEVEL_ERROR, + BIOMETRIC_TRACE_DEVICE, + "%!FUNC!Buffer too small - return size necessary in PayloadSize - 0x%x.", sizeof(WINBIO_DIAGNOSTICS)); + diagnostics->PayloadSize = (DWORD) sizeof(WINBIO_DIAGNOSTICS); + MyRequest.SetInformation(sizeof(DWORD)); + MyRequest.SetCompletionHr(S_OK); + return; + } + + // + // Fill in the OUT payload structure + // + RtlZeroMemory(diagnostics, outputBufferSize); + diagnostics->PayloadSize = (DWORD) sizeof(WINBIO_DIAGNOSTICS); + diagnostics->WinBioHresult = S_OK; + diagnostics->SensorStatus = WINBIO_SENSOR_READY; + + MyRequest.SetInformation(diagnostics->PayloadSize); + MyRequest.SetCompletionHr(S_OK); +} + + +void +CBiometricDevice::OnCaptureData( + _Inout_ IWDFIoRequest *FxRequest + ) +/*++ + + Routine Description: + + This method is invoked when the IOCTL_BIOMETRIC_CAPTURE_DATA command is called. + + Arguments: + + FxRequest - + IN payload: PWINBIO_CAPTURE_PARAMETERS + OUT payload: PWINBIO_CAPTURE_DATA + + Return Value: + + None + +--*/ +{ + ULONG controlCode = 0; + PWINBIO_CAPTURE_PARAMETERS captureParams = NULL; + SIZE_T inputBufferSize = 0; + PWINBIO_CAPTURE_DATA captureData = NULL; + SIZE_T outputBufferSize = 0; + + // + // We can only have one outstanding data capture request at a time. + // Check to see if we have a request pending. + // + bool requestPending = false; + + EnterCriticalSection(&m_RequestLock); + + if (m_PendingRequest == NULL) + { + // + // See if we have an active sleep thread. + // If so, tell it to exit. + // Wait for it to exit. + // + if (m_SleepThread != INVALID_HANDLE_VALUE) + { + LeaveCriticalSection(&m_RequestLock); + + // TODO: Add code to signal thread to exit. + + // NOTE: Sleeping for INFINITE time is dangerous. A real driver + // should be able to handle the case where the thread does + // not exit. + WaitForSingleObject(m_SleepThread, INFINITE); + CloseHandle(m_SleepThread); + m_SleepThread = INVALID_HANDLE_VALUE; + + EnterCriticalSection(&m_RequestLock); + } + + // + // We might have had to leave the CS to wait for the sleep thread. + // Double check that the pending request is still NULL. + // + if (m_PendingRequest == NULL) + { + // Save the request. + m_PendingRequest = FxRequest; + + // Mark the request as cancellable. + m_PendingRequest->MarkCancelable(this); + } + else + { + requestPending = true; + } + + } + else + { + requestPending = true; + } + + LeaveCriticalSection(&m_RequestLock); + + if (requestPending) + { + // Complete the request to tell the app that there is already + // a pending data collection request. + FxRequest->Complete(WINBIO_E_DATA_COLLECTION_IN_PROGRESS); + return; + } + + // + // Get the request parameters + // + GetIoRequestParams(FxRequest, + &controlCode, + (PUCHAR *)&captureParams, + &inputBufferSize, + (PUCHAR *)&captureData, + &outputBufferSize); + + // + // Check input parameters. + // + if (inputBufferSize < sizeof (WINBIO_CAPTURE_PARAMETERS)) + { + // Invalid arguments + TraceEvents(TRACE_LEVEL_ERROR, + BIOMETRIC_TRACE_DEVICE, + "%!FUNC!Invalid argument(s)."); + CompletePendingRequest(E_INVALIDARG, 0); + return; + } + + // + // Make sure we have an output buffer big enough + // + if (outputBufferSize < sizeof(DWORD)) + { + // We cannot return size information. + TraceEvents(TRACE_LEVEL_ERROR, + BIOMETRIC_TRACE_DEVICE, + "%!FUNC!Output buffer NULL or too small to return size information."); + CompletePendingRequest(E_INVALIDARG, 0); + return; + } + + // + // Check output buffer size. + // + if (outputBufferSize < sizeof (WINBIO_CAPTURE_DATA)) + { + // Buffer too small. + TraceEvents(TRACE_LEVEL_ERROR, + BIOMETRIC_TRACE_DEVICE, + "%!FUNC!Buffer too small - must be at least 0x%x.", sizeof (WINBIO_CAPTURE_DATA)); + // + // NOTE: The output buffer size necessary for this sample is sizeof(WINBIO_CAPTURE_DATA). + // Real devices will need additional space to handle a typical capture. + // The value that should be returned here is sizeof(WINBIO_CAPTURE_DATA) + CaptureBufferSize. + // + captureData->PayloadSize = (DWORD) sizeof(WINBIO_CAPTURE_DATA); + CompletePendingRequest(S_OK, sizeof(DWORD)); + return; + } + + // + // NOTE: This call always fails in this sample since it is not + // written for a real device. + // + + // + // Set default values in output buffer. + // + captureData->PayloadSize = (DWORD) sizeof (WINBIO_CAPTURE_DATA); + captureData->WinBioHresult = WINBIO_E_NO_CAPTURE_DATA; + captureData->SensorStatus = WINBIO_SENSOR_FAILURE; + captureData->RejectDetail= 0; + captureData->CaptureData.Size = 0; + + // + // Check purpose, format and type. + // + if (captureParams->Purpose == WINBIO_NO_PURPOSE_AVAILABLE) + { + captureData->WinBioHresult = WINBIO_E_UNSUPPORTED_PURPOSE; + } + else if ((captureParams->Format.Type != WINBIO_ANSI_381_FORMAT_TYPE) || + (captureParams->Format.Owner != WINBIO_ANSI_381_FORMAT_OWNER)) + { + captureData->WinBioHresult = WINBIO_E_UNSUPPORTED_DATA_FORMAT; + } + else if (captureParams->Flags != WINBIO_DATA_FLAG_RAW) + { + captureData->WinBioHresult = WINBIO_E_UNSUPPORTED_DATA_TYPE; + } + + // + // NOTE: This sample completes the request after + // sleeping for 5 seconds. A real driver would + // program the device for capture mode, and then + // return from this callback. The request would + // remain pending until cancelled, or until the + // driver detects a capture is complete. + // + // The construct of m_PendingRequest will allow + // a driver to have only one pending request at any + // time, which can be cancelled in a Reset IOCTL, or + // by calling CancelIoEx. + // + + // + // Create thread to sleep 5 seconds before completing the request. + // + m_SleepParams.SleepValue = 5; + m_SleepParams.Hr = S_OK; + m_SleepParams.Information = captureData->PayloadSize; + m_SleepThread = CreateThread(NULL, // default security attributes + 0, // use default stack size + CaptureSleepThread, // thread function name + this, // argument to thread function + 0, // use default creation flags + NULL); // returns the thread identifier +} + + +void +CBiometricDevice::OnUpdateFirmware( + _Inout_ IWDFIoRequest *FxRequest + ) +/*++ + + Routine Description: + + This method is invoked when the IOCTL_BIOMETRIC_UPDATE_FIRMWARE command is called. + + Arguments: + + FxRequest - + + Return Value: + + None + +--*/ +{ + FxRequest->Complete(E_NOTIMPL); +} + +void +CBiometricDevice::OnGetSupportedAlgorithms( + _Inout_ IWDFIoRequest *FxRequest + ) +/*++ + + Routine Description: + + This method is invoked when the IOCTL_BIOMETRIC_GET_SUPPORTED_ALGORITHMS command is called. + + Arguments: + + FxRequest - + + Return Value: + + None + +--*/ +{ + FxRequest->Complete(E_NOTIMPL); +} + +void +CBiometricDevice::OnGetIndicator( + _Inout_ IWDFIoRequest *FxRequest + ) +/*++ + + Routine Description: + + This method is invoked when the IOCTL_BIOMETRIC_GET_INDICATOR command is called. + + Arguments: + + FxRequest - + + Return Value: + + None + +--*/ +{ + FxRequest->Complete(E_NOTIMPL); +} + + +void +CBiometricDevice::OnSetIndicator( + _Inout_ IWDFIoRequest *FxRequest + ) +/*++ + + Routine Description: + + This method is invoked when the IOCTL_BIOMETRIC_SET_INDICATOR command is called. + + Arguments: + + FxRequest - + + Return Value: + + None + +--*/ +{ + FxRequest->Complete(E_NOTIMPL); +} + +void +CBiometricDevice::OnControlUnit( + _Inout_ IWDFIoRequest *FxRequest + ) +/*++ + + Routine Description: + + This method is invoked when the IOCTL_BIOMETRIC_CONTROL_UNIT command is called. + + Arguments: + + FxRequest - + + Return Value: + + None + +--*/ +{ + FxRequest->Complete(E_NOTIMPL); +} + + +VOID +CBiometricDevice::CompletePendingRequest( + HRESULT hr, + DWORD information + ) +{ + EnterCriticalSection(&m_RequestLock); + + if (m_PendingRequest) + { + // + // Only complete the request if we weren't cancelled. Otherwise, the + // OnCancel callback will complete the request. + // + HRESULT hrUnmark = m_PendingRequest->UnmarkCancelable(); + if (HRESULT_FROM_WIN32(ERROR_OPERATION_ABORTED) != hrUnmark) + { + m_PendingRequest->SetInformation(information); + m_PendingRequest->Complete(hr); + m_PendingRequest = NULL; + } + } + + LeaveCriticalSection(&m_RequestLock); +} + +VOID +STDMETHODCALLTYPE +CBiometricDevice::OnCancel( + _In_ IWDFIoRequest *pWdfRequest + ) +{ + EnterCriticalSection(&m_RequestLock); + + if (m_PendingRequest != pWdfRequest) + { + TraceEvents(TRACE_LEVEL_ERROR, + BIOMETRIC_TRACE_DEVICE, + "%!FUNC! Cancelled request does not match pending request."); + } + + // + // TODO: In a real driver, the device would be reset so that it is no longer in capture mode. + // Add your code to do so here. + // + + if (m_PendingRequest == NULL) + { + TraceEvents(TRACE_LEVEL_ERROR, + BIOMETRIC_TRACE_DEVICE, + "%!FUNC! Pending request is NULL."); + } + else + { + m_PendingRequest->Complete(HRESULT_FROM_WIN32(ERROR_OPERATION_ABORTED)); + m_PendingRequest = NULL; + } + + LeaveCriticalSection(&m_RequestLock); +} diff --git a/biometrics/driver/Device.h b/biometrics/driver/Device.h new file mode 100644 index 000000000..690943255 --- /dev/null +++ b/biometrics/driver/Device.h @@ -0,0 +1,360 @@ +/*++ + + 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 + +Module Name: + + Device.h + +Abstract: + + This module contains the type definitions of the Biometric + device driver. + +Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + +#pragma once + +// +// TODO: Change this to match your device +// +#define NUM_WBDI_ENDPOINTS 3 + +// +// Power policy suspend delay time. 10 seconds. +// +#define WBDI_SUSPEND_DELAY ((ULONG)(10 * 1000)) + +// +// Struct for passing parameters for capture request completion. +// +typedef struct _CAPTURE_SLEEP_PARAMS +{ + DWORD SleepValue; + HRESULT Hr; + DWORD Information; +} CAPTURE_SLEEP_PARAMS, *PCAPTURE_SLEEP_PARAMS; + + +// +// Class for the Biometric driver. +// + +class CBiometricDevice : + public CComObjectRootEx, + public IRequestCallbackRequestCompletion, + public IRequestCallbackCancel, + public IPnpCallbackHardware +{ +public: + + DECLARE_NOT_AGGREGATABLE(CBiometricDevice) + + BEGIN_COM_MAP(CBiometricDevice) + COM_INTERFACE_ENTRY(IPnpCallbackHardware) + COM_INTERFACE_ENTRY(IRequestCallbackRequestCompletion) + COM_INTERFACE_ENTRY(IRequestCallbackCancel) + END_COM_MAP() + + CBiometricDevice() : + m_FxDevice(NULL), + m_IoQueue(NULL), + m_pIUsbTargetDevice(NULL), + m_pIUsbInterface(NULL), + m_pIUsbInputPipe(NULL), + m_pIUsbOutputPipe(NULL), + m_pIUsbInterruptPipe(NULL), + m_PendingRequest(NULL), + m_Speed(0), + m_InterruptReadProblem(S_OK), + m_SleepThread(INVALID_HANDLE_VALUE) + { + InitializeCriticalSection(&m_RequestLock); + } + + ~CBiometricDevice() + { + DeleteCriticalSection(&m_RequestLock); + } + +// +// Private data members. +// +private: + + // + // Weak reference to framework device object. + // + IWDFDevice * m_FxDevice; + + // + // Weak reference to I/O queue + // + PCBiometricIoQueue m_IoQueue; + + // + // USB Device I/O Target + // + IWDFUsbTargetDevice * m_pIUsbTargetDevice; + + // + // USB Interface + // + IWDFUsbInterface * m_pIUsbInterface; + + // + // USB Input pipe for Reads + // + IWDFUsbTargetPipe * m_pIUsbInputPipe; + + // + // USB Output pipe for writes + // + IWDFUsbTargetPipe * m_pIUsbOutputPipe; + + // + // USB interrupt pipe + // + IWDFUsbTargetPipe * m_pIUsbInterruptPipe; + + // + // Device Speed (Low, Full, High) + // + UCHAR m_Speed; + + // + // If reads stopped because of a transient problem, the error status + // is stored here. + // + + HRESULT m_InterruptReadProblem; + + // + // Interrupt message buffer + // + + INTERRUPT_MESSAGE m_InterruptMessage; + + // + // Holds a reference to a pending data I/O request. + // + + IWDFIoRequest *m_PendingRequest; + + // + // Synchronization for m_PendingRequest + // + + CRITICAL_SECTION m_RequestLock; + + // + // Handle to a thread that will sleep before completing a request. + // + HANDLE m_SleepThread; + CAPTURE_SLEEP_PARAMS m_SleepParams; + +// +// Private methods. +// +private: + + HRESULT + Initialize( + _In_ IWDFDriver *FxDriver, + _In_ IWDFDeviceInitialize *FxDeviceInit + ); + + // + // Helper methods + // + + HRESULT + CreateUsbIoTargets( + VOID + ); + + HRESULT + SetPowerManagement( + VOID + ); + + // + // Helper functions + // + + HRESULT + SendControlTransferSynchronously( + _In_ PWINUSB_SETUP_PACKET SetupPacket, + _Inout_updates_(BufferLength) PBYTE Buffer, + _In_ ULONG BufferLength, + _Out_ PULONG LengthTransferred + ); + + static + WDF_IO_TARGET_STATE + GetTargetState( + IWDFIoTarget * pTarget + ); + + HRESULT + InitiatePendingRead( + ); + +// +// Public methods +// +public: + + // + // The factory method used to create an instance of this driver. + // + + static + HRESULT + CreateInstanceAndInitialize( + _In_ IWDFDriver *FxDriver, + _In_ IWDFDeviceInitialize *FxDeviceInit, + _Out_ CBiometricDevice **Device + ); + + HRESULT + Configure( + VOID + ); + +// +// COM methods +// +public: + + // + // IPnpCallbackHardware + // + + virtual + HRESULT + STDMETHODCALLTYPE + OnPrepareHardware( + _In_ IWDFDevice *FxDevice + ); + + virtual + HRESULT + STDMETHODCALLTYPE + OnReleaseHardware( + _In_ IWDFDevice *FxDevice + ); + + + // + // IRequestCallbackRequestCompletion + // + virtual + void + STDMETHODCALLTYPE + OnCompletion( + _In_ IWDFIoRequest* FxRequest, + _In_ IWDFIoTarget* pIoTarget, + _In_ IWDFRequestCompletionParams* pParams, + _In_ PVOID pContext + ); + + // + // IRequestCallbackCancel + // + virtual + VOID + STDMETHODCALLTYPE + OnCancel( + _In_ IWDFIoRequest *pWdfRequest + ); + +public: + + // + // I/O handlers. + // + void + GetIoRequestParams( + _In_ IWDFIoRequest *FxRequest, + _Out_ ULONG *MajorControlCode, + _Outptr_result_bytebuffer_(*InputBufferSizeInBytes) PUCHAR *InputBuffer, + _Out_ SIZE_T *InputBufferSizeInBytes, + _Outptr_result_bytebuffer_(*OutputBufferSizeInBytes) PUCHAR *OutputBuffer, + _Out_ SIZE_T *OutputBufferSizeInBytes + ); + + void + OnGetAttributes( + _Inout_ IWDFIoRequest *FxRequest + ); + + void + OnReset( + _Inout_ IWDFIoRequest *FxRequest + ); + + void + OnCalibrate( + _Inout_ IWDFIoRequest *FxRequest + ); + + void + OnGetSensorStatus( + _Inout_ IWDFIoRequest *FxRequest + ); + + void + OnCaptureData( + _Inout_ IWDFIoRequest *FxRequest + ); + + void + OnUpdateFirmware( + _Inout_ IWDFIoRequest *FxRequest + ); + + void + OnGetSupportedAlgorithms( + _Inout_ IWDFIoRequest *FxRequest + ); + + void + OnGetIndicator( + _Inout_ IWDFIoRequest *FxRequest + ); + + void + OnSetIndicator( + _Inout_ IWDFIoRequest *FxRequest + ); + + void + OnControlUnit( + _Inout_ IWDFIoRequest *FxRequest + ); + + VOID + CompletePendingRequest( + HRESULT hr, + DWORD information + ); + + inline PCAPTURE_SLEEP_PARAMS + GetCaptureSleepParams() + { + return &m_SleepParams; + } + +}; + + diff --git a/biometrics/driver/Driver.cpp b/biometrics/driver/Driver.cpp new file mode 100644 index 000000000..0d5c0797f --- /dev/null +++ b/biometrics/driver/Driver.cpp @@ -0,0 +1,77 @@ +/*++ + + 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 + +Module Name: + + Driver.cpp + +Abstract: + + This module contains the implementation of the Biometric + core driver callback object. + +Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + +#include "internal.h" +#include "driver.tmh" + +HRESULT +CBiometricDriver::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 = S_OK; + CBiometricDevice *device = NULL; + + // + // Create device callback object + // + + hr = CBiometricDevice::CreateInstanceAndInitialize(FxWdfDriver, + FxDeviceInit, + &device); + + // + // 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(); + } + + return hr; +} diff --git a/biometrics/driver/Driver.h b/biometrics/driver/Driver.h new file mode 100644 index 000000000..0fc2566a9 --- /dev/null +++ b/biometrics/driver/Driver.h @@ -0,0 +1,95 @@ +/*++ + + 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 + +Module Name: + + Driver.h + +Abstract: + + This module contains the type definitions for the Biometric + 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. +// + +EXTERN_C const CLSID CLSID_BiometricUsbSample; + +class CBiometricDriver : + public CComObjectRootEx, + public CComCoClass, + public IDriverEntry +{ +public: + + CBiometricDriver() + { + } + + DECLARE_NO_REGISTRY() + + DECLARE_NOT_AGGREGATABLE(CBiometricDriver) + + BEGIN_COM_MAP(CBiometricDriver) + COM_INTERFACE_ENTRY(IDriverEntry) + END_COM_MAP() + +// +// Public 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; + } + +}; + +OBJECT_ENTRY_AUTO(CLSID_BiometricUsbSample, CBiometricDriver) diff --git a/biometrics/driver/Internalsrc.cpp b/biometrics/driver/Internalsrc.cpp new file mode 100644 index 000000000..1e8fe6f23 --- /dev/null +++ b/biometrics/driver/Internalsrc.cpp @@ -0,0 +1 @@ +#include "Internal.h" \ No newline at end of file diff --git a/biometrics/driver/IoQueue.cpp b/biometrics/driver/IoQueue.cpp new file mode 100644 index 000000000..ce8837d92 --- /dev/null +++ b/biometrics/driver/IoQueue.cpp @@ -0,0 +1,297 @@ +/*++ + + 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 + +Module Name: + + IoQueue.cpp + +Abstract: + + This file implements the I/O queue interface and performs + the ioctl operations. + +Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + +#include "internal.h" +#include "ioqueue.tmh" + + +HRESULT +CBiometricIoQueue::CreateInstanceAndInitialize( + _In_ IWDFDevice *FxDevice, + _In_ CBiometricDevice *BiometricDevice, + _Out_ CBiometricIoQueue** Queue + ) +/*++ + +Routine Description: + + CreateInstanceAndInitialize creates an instance of the queue object. + +Arguments: + + +Return Value: + + HRESULT indicating success or failure + +--*/ +{ + // + // Create a new instance of the device class + // + CComObject *pMyQueue = NULL; + HRESULT hr = CComObject::CreateInstance( &pMyQueue ); + + if (SUCCEEDED(hr)) { + + // + // Initialize the instance. + // + + if (NULL != pMyQueue) + { + hr = pMyQueue->Initialize(FxDevice, BiometricDevice); + } + + *Queue = pMyQueue; + + } + + return hr; +} + +HRESULT +CBiometricIoQueue::Initialize( + _In_ IWDFDevice *FxDevice, + _In_ CBiometricDevice *BiometricDevice + ) +/*++ + +Routine Description: + + Initialize creates a framework queue and sets up I/O for the queue object. + +Arguments: + + FxDevice - Framework device associated with this queue. + + BiometricDevice - Pointer to the Biometric device class object. + +Return Value: + + HRESULT indicating success or failure + +--*/ +{ + IWDFIoQueue *fxQueue = NULL; + HRESULT hr = S_OK; + IUnknown *unknown = NULL; + + // + // Make sure we have valid parameters. + // + if (FxDevice == NULL) { + TraceEvents(TRACE_LEVEL_ERROR, + BIOMETRIC_TRACE_QUEUE, + "%!FUNC!Pointer to framework device object is NULL."); + return (E_INVALIDARG); + } + if (BiometricDevice == NULL) { + TraceEvents(TRACE_LEVEL_ERROR, + BIOMETRIC_TRACE_QUEUE, + "%!FUNC!Pointer to Biometric device is NULL."); + return (E_INVALIDARG); + } + + // + // Create the framework queue + // + + if (SUCCEEDED(hr)) + { + hr = this->QueryInterface(__uuidof(IUnknown), (void **)&unknown); + + } + + if (SUCCEEDED(hr)) + { + hr = FxDevice->CreateIoQueue(unknown, + FALSE, // Default Queue? + WdfIoQueueDispatchParallel, // Dispatch type + FALSE, // Power managed? + FALSE, // Allow zero-length requests? + &fxQueue); // I/O queue + BiometricSafeRelease(unknown); + } + + if (FAILED(hr)) + { + TraceEvents(TRACE_LEVEL_ERROR, + BIOMETRIC_TRACE_QUEUE, + "%!FUNC!Failed to create framework queue."); + return hr; + } + + // + // Configure this queue to filter all Device I/O requests. + // + hr = FxDevice->ConfigureRequestDispatching(fxQueue, + WdfRequestDeviceIoControl, + TRUE); + + if (SUCCEEDED(hr)) + { + m_FxQueue = fxQueue; + m_BiometricDevice= BiometricDevice; + } + + // + // Safe to release here. The framework keeps a reference to the Queue + // for the lifetime of the device. + // + BiometricSafeRelease(fxQueue); + + return hr; +} + +VOID +STDMETHODCALLTYPE +CBiometricIoQueue::OnDeviceIoControl( + _In_ IWDFIoQueue *FxQueue, + _In_ IWDFIoRequest *FxRequest, + _In_ ULONG ControlCode, + _In_ SIZE_T InputBufferSizeInBytes, + _In_ SIZE_T OutputBufferSizeInBytes + ) +/*++ + +Routine Description: + + + DeviceIoControl dispatch routine + +Aruments: + + FxQueue - Framework Queue instance + FxRequest - Framework Request instance + ControlCode - IO Control Code + InputBufferSizeInBytes - Lenth of input buffer + OutputBufferSizeInBytes - Lenth of output buffer + + Always succeeds DeviceIoIoctl +Return Value: + + VOID + +--*/ +{ + UNREFERENCED_PARAMETER(FxQueue); + UNREFERENCED_PARAMETER(InputBufferSizeInBytes); + UNREFERENCED_PARAMETER(OutputBufferSizeInBytes); + + if (m_BiometricDevice == NULL) { + // We don't have pointer to device object + TraceEvents(TRACE_LEVEL_ERROR, + BIOMETRIC_TRACE_QUEUE, + "%!FUNC!NULL pointer to device object."); + FxRequest->Complete(E_POINTER); + return; + } + + // + // Process the IOCTLs + // + + switch (ControlCode) { + + // + // Mandatory IOCTLs + // + case IOCTL_BIOMETRIC_GET_ATTRIBUTES: + m_BiometricDevice->OnGetAttributes(FxRequest); + break; + + case IOCTL_BIOMETRIC_RESET: + m_BiometricDevice->OnReset(FxRequest); + break; + + case IOCTL_BIOMETRIC_CALIBRATE: + m_BiometricDevice->OnCalibrate(FxRequest); + break; + + case IOCTL_BIOMETRIC_GET_SENSOR_STATUS: + m_BiometricDevice->OnGetSensorStatus(FxRequest); + break; + + case IOCTL_BIOMETRIC_CAPTURE_DATA: + m_BiometricDevice->OnCaptureData(FxRequest); + break; + + // + // Optional IOCTLs + // + case IOCTL_BIOMETRIC_UPDATE_FIRMWARE: + m_BiometricDevice->OnUpdateFirmware(FxRequest); + break; + + case IOCTL_BIOMETRIC_GET_SUPPORTED_ALGORITHMS: + m_BiometricDevice->OnGetSupportedAlgorithms(FxRequest); + break; + + case IOCTL_BIOMETRIC_GET_INDICATOR: + m_BiometricDevice->OnGetIndicator(FxRequest); + break; + + case IOCTL_BIOMETRIC_SET_INDICATOR: + m_BiometricDevice->OnSetIndicator(FxRequest); + break; + + default: + + // + // First check to see if this is for a BIOMETRIC file. + // + if ((ControlCode & CTL_CODE(0xFFFFFFFF, 0, 0, 0)) == CTL_CODE(FILE_DEVICE_BIOMETRIC, 0, 0, 0)) { + + if ((ControlCode & IOCTL_BIOMETRIC_VENDOR) == IOCTL_BIOMETRIC_VENDOR) { + // This is a vendor IOCTL. + m_BiometricDevice->OnControlUnit(FxRequest); + break; + } + + } else { + + // This is a legacy IOCTL - non-Windows Biometric Framework + TraceEvents(TRACE_LEVEL_ERROR, + BIOMETRIC_TRACE_QUEUE, + "%!FUNC!Legacy control units not supported by the driver."); + + } + + // + // Didn't match any of the above. + // + TraceEvents(TRACE_LEVEL_ERROR, + BIOMETRIC_TRACE_QUEUE, + "%!FUNC! Unsupported IOCTL - 0x%x.", + ControlCode); + FxRequest->Complete(HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION)); + break; + + } + + return; + +} + diff --git a/biometrics/driver/IoQueue.h b/biometrics/driver/IoQueue.h new file mode 100644 index 000000000..1a68416b1 --- /dev/null +++ b/biometrics/driver/IoQueue.h @@ -0,0 +1,123 @@ +/*++ + + 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 + +Module Name: + + IoQueue.h + +Abstract: + + This file defines the queue callback interface. + +Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + +#pragma once + +// +// Queue Callback Object. +// + +class CBiometricIoQueue : + public CComObjectRootEx, + public IQueueCallbackDeviceIoControl +{ + +public: + + DECLARE_NOT_AGGREGATABLE(CBiometricIoQueue) + + BEGIN_COM_MAP(CBiometricIoQueue) + COM_INTERFACE_ENTRY(IQueueCallbackDeviceIoControl) + END_COM_MAP() + + CBiometricIoQueue() : + m_FxQueue(NULL), + m_BiometricDevice(NULL) + { + } + + ~CBiometricIoQueue() + { + // empty + } + + HRESULT + Initialize( + _In_ IWDFDevice *FxDevice, + _In_ CBiometricDevice *BiometricDevice + ); + + static + HRESULT + CreateInstanceAndInitialize( + _In_ IWDFDevice *FxDevice, + _In_ CBiometricDevice *BiometricDevice, + _Out_ CBiometricIoQueue** Queue + ); + + HRESULT + Configure( + VOID + ) + { + return S_OK; + } + + VOID + Start( + ) + { + m_FxQueue->Start(); + } + + VOID + StopSynchronously( + ) + { + m_FxQueue->StopSynchronously(); + } + + // + // Wdf Callbacks + // + + // + // IQueueCallbackDeviceIoControl + // + virtual + VOID + STDMETHODCALLTYPE + OnDeviceIoControl( + _In_ IWDFIoQueue *pWdfQueue, + _In_ IWDFIoRequest *pWdfRequest, + _In_ ULONG ControlCode, + _In_ SIZE_T InputBufferSizeInBytes, + _In_ SIZE_T OutputBufferSizeInBytes + ); + +// +// Private member variables. +// +private: + + // + // Weak reference to framework queue object. + // + IWDFIoQueue * m_FxQueue; + + // + // Pointer to device class. + // + CBiometricDevice * m_BiometricDevice; + +}; diff --git a/biometrics/driver/RequestHelper.h b/biometrics/driver/RequestHelper.h new file mode 100644 index 000000000..89a8747c7 --- /dev/null +++ b/biometrics/driver/RequestHelper.h @@ -0,0 +1,89 @@ +/*++ + + 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 + +Module Name: + + RequestHelper.h + +Abstract: + + This module contains the class definition and implementation + of an RAII Request object helper class. + +Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + +#pragma once + +// +// This class handles RAII for IWdfIoRequest pointers. +// A function can declare this class at the beginning, and +// set the HRESULT for the request completion. +// +// The destructor is always called on function exit. +// It will complete the request only if the HRESULT +// is something besides HRESULT_FROM_WIN32(ERROR_IO_PENDING) +// +// If the function does not want to complete the request, +// it should not call SetCompletionHr. Then the request +// will remain pending. +// + +class CRequestHelper +{ + +// +// Public methods +// +public: + + CRequestHelper( + IWDFIoRequest *FxRequest + ) + { + m_Request = FxRequest; + m_Hr = HRESULT_FROM_WIN32(ERROR_IO_PENDING); + } + + ~CRequestHelper() + { + if (m_Hr != HRESULT_FROM_WIN32(ERROR_IO_PENDING)) + { + m_Request->Complete(m_Hr); + } + } + + void + SetCompletionHr( + HRESULT Hr + ) + { + m_Hr = Hr; + } + + void + SetInformation( + SIZE_T Information + ) + { + m_Request->SetInformation(Information); + } + +// +// Private members +// +private: + + IWDFIoRequest * m_Request; + HRESULT m_Hr; + +}; diff --git a/biometrics/driver/WudfBioUsbSample.inx b/biometrics/driver/WudfBioUsbSample.inx new file mode 100644 index 000000000..ccd35c838 --- /dev/null +++ b/biometrics/driver/WudfBioUsbSample.inx @@ -0,0 +1,145 @@ +; +; Copyright (C) Microsoft. All rights reserved. +; +; WudfBioUsbSample.inf - Install the WBDI USB user-mode driver - WBDI sample +; + +[Version] +Signature="$Windows NT$" +Class=Biometric +ClassGuid={53D29EF7-377C-4D14-864B-EB3A85769359} +Provider=%ProviderString% +CatalogFile=biometrics.cat + +[Manufacturer] +%ManufacturerName%=Standard,NT$ARCH$ + +[Standard.NT$ARCH$] +; TODO: Change the VendorID (VID) and ProductID (PID) to match your device +%WBDIUsbDeviceName%=Biometric_Install, USB\VID_0547&PID_1002 + +[ClassInstall32] +AddReg=BiometricClass_RegistryAdd + +[BiometricClass_RegistryAdd] +HKR,,,,%ClassName% +HKR,,Icon,,"-201" +HKR,,IconPath,0x00020000,"%%SYSTEMROOT%%\system32\SysClass.dll,-201" + +[SourceDisksFiles] +WudfBioUsbSample.dll=1 +EngineAdapter.dll=1 ; Vendor engine adapter +WudfUpdate_$UMDFCOINSTALLERVERSION$.dll=1 + +[SourceDisksNames] +1 = %MediaDescription% + +; =================== UMDF WBDI Usb Device - WBDI IOCTL interface ================================== + +[Biometric_Install.NT] +CopyFiles=UMDriverCopy, WinBioEngineAdapterCopy +AddProperty=Device_Properties +Include=WINUSB.INF ; Import sections from WINUSB.INF +Needs=WINUSB.NT ; Run the CopyFiles & AddReg directives for WinUsb.INF + +[Biometric_Install.NT.hw] +AddReg=Biometric_Device_AddReg +AddReg=DriverPlugInAddReg, DatabaseAddReg + +[Device_Properties] +DeviceIcon,,,,"%%SYSTEMROOT%%\system32\SysClass.dll,-201" ; This is where a device specific icon can be specified. + +[Biometric_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. + +[Biometric_Install.NT.Wdf] +KmdfService=WINUSB, WinUsb_Install +UmdfDispatcher=WinUsb +UmdfService=WudfBioUsbSample, WudfBioUsbSample_Install +UmdfServiceOrder=WudfBioUsbSample + +[Biometric_Install.NT.CoInstallers] +AddReg=CoInstallers_AddReg +CopyFiles=CoInstallers_CopyFiles + +[WinUsb_Install] +KmdfLibraryVersion = 1.9 + +[WudfBioUsbSample_Install] +UmdfLibraryVersion=$UMDFVERSION$ +DriverCLSID = "{F1CB3C15-A916-47bc-BEA1-D5D4163BC6AE}" +ServiceBinary = "%12%\UMDF\WudfBioUsbSample.dll" + +[Biometric_Device_AddReg] +HKR,,"DeviceCharacteristics",0x10001,0x0100 ; Use same security checks on relative opens +HKR,,"Security",,"D:P(A;;GA;;;BA)(A;;GA;;;SY)" ; Allow generic-all access to Built-in administrators and Local system +HKR,,"LowerFilters",0x00010008,"WinUsb" ; FLG_ADDREG_TYPE_MULTI_SZ | FLG_ADDREG_APPEND +HKR,,"Exclusive",0x10001,1 +HKR,,"SystemWakeEnabled",0x00010001,1 +HKR,,"DeviceIdleEnabled",0x00010001,1 +HKR,,"UserSetDeviceIdleEnabled",0x00010001,1 +HKR,,"DefaultIdleState",0x00010001,1 +HKR,,"DefaultIdleTimeout",0x00010001,5000 + +[DriverPlugInAddReg] +HKR,WinBio\Configurations,DefaultConfiguration,,"0" +HKR,WinBio\Configurations\0,SensorMode,0x10001,1 ; Basic - 1, Advanced - 2 +HKR,WinBio\Configurations\0,SystemSensor,0x10001,1 ; UAC/Winlogon - 1 +HKR,WinBio\Configurations\0,SensorAdapterBinary,,"WinBioSensorAdapter.DLL" ; Windows built-in WBDI sensor adapter - see SensorAdapter.dll for sample vendor adapter +HKR,WinBio\Configurations\0,EngineAdapterBinary,,"EngineAdapter.DLL" ; Vendor engine +HKR,WinBio\Configurations\0,StorageAdapterBinary,,"WinBioStorageAdapter.DLL" ; Windows built-in storage adapter - see StorageAdapter.dll for sample vendor adapter +HKR,WinBio\Configurations\0,DatabaseId,,"6E9D4C5A-55B4-4c52-90B7-DDDC75CA4D50" ; Unique database GUID + +[DatabaseAddReg] +HKLM,System\CurrentControlSet\Services\WbioSrvc\Databases\{6E9D4C5A-55B4-4c52-90B7-DDDC75CA4D50},BiometricType,0x00010001,0x00000008 +HKLM,System\CurrentControlSet\Services\WbioSrvc\Databases\{6E9D4C5A-55B4-4c52-90B7-DDDC75CA4D50},Attributes,0x00010001,0x00000001 +HKLM,System\CurrentControlSet\Services\WbioSrvc\Databases\{6E9D4C5A-55B4-4c52-90B7-DDDC75CA4D50},Format,,"00000000-0000-0000-0000-000000000000" +HKLM,System\CurrentControlSet\Services\WbioSrvc\Databases\{6E9D4C5A-55B4-4c52-90B7-DDDC75CA4D50},InitialSize,0x00010001,0x00000020 +HKLM,System\CurrentControlSet\Services\WbioSrvc\Databases\{6E9D4C5A-55B4-4c52-90B7-DDDC75CA4D50},AutoCreate,0x00010001,0x00000001 +HKLM,System\CurrentControlSet\Services\WbioSrvc\Databases\{6E9D4C5A-55B4-4c52-90B7-DDDC75CA4D50},AutoName,0x00010001,0x00000001 +HKLM,System\CurrentControlSet\Services\WbioSrvc\Databases\{6E9D4C5A-55B4-4c52-90B7-DDDC75CA4D50},FilePath,,"" +HKLM,System\CurrentControlSet\Services\WbioSrvc\Databases\{6E9D4C5A-55B4-4c52-90B7-DDDC75CA4D50},ConnectionString,,"" + +[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,"WudfUpdate_$UMDFCOINSTALLERVERSION$.dll" +HKR,,CoInstallers32,0x00010000,"WudfCoinstaller.dll" + +[CoInstallers_CopyFiles] +WudfUpdate_$UMDFCOINSTALLERVERSION$.dll + +[DestinationDirs] +UMDriverCopy=12,UMDF ; copy to \Windows\System32\drivers\UMDF +WinBioEngineAdapterCopy=11,WinBioPlugins ; copy to \Windows\System32\WinBioPlugins +CoInstallers_CopyFiles=11 + +[UMDriverCopy] +WudfBioUsbSample.dll + +[WinBioEngineAdapterCopy] +EngineAdapter.dll + +; =================== Generic ================================== + +[Strings] +ManufacturerName="TODO-Set-Manufacturer" +ProviderString="TODO-Set-Provider" +MediaDescription="Sample Driver Installation Media" +ClassName="Biometric Devices" +WudfRdDisplayName="Windows Driver Foundation - User-mode Driver Framework Reflector" +WBDIUsbDeviceName="WUDF WBDI sample" +WinUsb_SvcDesc="WinUSB Driver" diff --git a/biometrics/driver/WudfBioUsbSample.vcxproj b/biometrics/driver/WudfBioUsbSample.vcxproj new file mode 100644 index 000000000..a64f4c3e0 --- /dev/null +++ b/biometrics/driver/WudfBioUsbSample.vcxproj @@ -0,0 +1,315 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {F97C7BE3-C238-4575-BBAF-5C5E42C7D6BF} + $(MSBuildProjectName) + 1 + 1 + false + true + Debug + Win32 + {FCA7AD48-723F-4C66-B96E-EDE5724E4FA0} + + + + 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 + ;%(AdditionalIncludeDirectories) + Internal.h + Use + $(IntDir)\Internal.h.pch + + + true + true + internal.h + ;%(AdditionalIncludeDirectories) + Internal.h + Use + $(IntDir)\Internal.h.pch + + + true + true + internal.h + ;%(AdditionalIncludeDirectories) + Internal.h + Use + $(IntDir)\Internal.h.pch + + + true + true + internal.h + ;%(AdditionalIncludeDirectories) + Internal.h + Use + $(IntDir)\Internal.h.pch + + + $(InfArch) + true + .\$(IntDir)\WudfBioUsbSample.inf + + + true + true + internal.h + + + + WudfBioUsbSample + 0x0A00 + 0x0A000000 + Dynamic + + + WudfBioUsbSample + 0x0A00 + 0x0A000000 + Dynamic + + + WudfBioUsbSample + 0x0A00 + 0x0A000000 + Dynamic + + + WudfBioUsbSample + 0x0A00 + 0x0A000000 + Dynamic + + + + true + Level4 + %(DisableSpecificWarnings);4201 + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + true + Level4 + %(DisableSpecificWarnings);4201 + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + true + Level4 + %(DisableSpecificWarnings);4201 + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + true + Level4 + %(DisableSpecificWarnings);4201 + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + %(AdditionalIncludeDirectories);inc;$(DDK_INC_PATH) + + + %(AdditionalIncludeDirectories);inc;$(DDK_INC_PATH) + + + + + %(AdditionalIncludeDirectories);inc;$(DDK_INC_PATH) + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\advapi32.lib;$(SDK_LIB_PATH)\kernel32.lib;$(SDK_LIB_PATH)\ole32.lib;$(SDK_LIB_PATH)\oleaut32.lib;$(SDK_LIB_PATH)\strsafe.lib;$(SDK_LIB_PATH)\user32.lib;$(SDK_LIB_PATH)\uuid.lib + exports.def + + + + + %(AdditionalIncludeDirectories);inc;$(DDK_INC_PATH) + + + %(AdditionalIncludeDirectories);inc;$(DDK_INC_PATH) + + + + + %(AdditionalIncludeDirectories);inc;$(DDK_INC_PATH) + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\advapi32.lib;$(SDK_LIB_PATH)\kernel32.lib;$(SDK_LIB_PATH)\ole32.lib;$(SDK_LIB_PATH)\oleaut32.lib;$(SDK_LIB_PATH)\strsafe.lib;$(SDK_LIB_PATH)\user32.lib;$(SDK_LIB_PATH)\uuid.lib + exports.def + + + + + %(AdditionalIncludeDirectories);inc;$(DDK_INC_PATH) + + + %(AdditionalIncludeDirectories);inc;$(DDK_INC_PATH) + + + + + %(AdditionalIncludeDirectories);inc;$(DDK_INC_PATH) + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\advapi32.lib;$(SDK_LIB_PATH)\kernel32.lib;$(SDK_LIB_PATH)\ole32.lib;$(SDK_LIB_PATH)\oleaut32.lib;$(SDK_LIB_PATH)\strsafe.lib;$(SDK_LIB_PATH)\user32.lib;$(SDK_LIB_PATH)\uuid.lib + exports.def + + + + + %(AdditionalIncludeDirectories);inc;$(DDK_INC_PATH) + + + %(AdditionalIncludeDirectories);inc;$(DDK_INC_PATH) + + + + + %(AdditionalIncludeDirectories);inc;$(DDK_INC_PATH) + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\advapi32.lib;$(SDK_LIB_PATH)\kernel32.lib;$(SDK_LIB_PATH)\ole32.lib;$(SDK_LIB_PATH)\oleaut32.lib;$(SDK_LIB_PATH)\strsafe.lib;$(SDK_LIB_PATH)\user32.lib;$(SDK_LIB_PATH)\uuid.lib + exports.def + + + + + ;%(AdditionalIncludeDirectories) + Internal.h + Create + $(IntDir)\Internal.h.pch + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/biometrics/driver/WudfBioUsbSample.vcxproj.Filters b/biometrics/driver/WudfBioUsbSample.vcxproj.Filters new file mode 100644 index 000000000..5d3a3bf13 --- /dev/null +++ b/biometrics/driver/WudfBioUsbSample.vcxproj.Filters @@ -0,0 +1,51 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {882F87D8-8927-4528-9BB1-4C04321C6C1B} + + + h;hpp;hxx;hm;inl;inc;xsd + {C885C011-3876-45C7-806E-A49C965C0EA9} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {5038EACF-350F-4A7F-A58C-493BE2164051} + + + inf;inv;inx;mof;mc; + {830657F7-FD3F-4574-983E-BF35A8AB0072} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Driver Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/biometrics/driver/dllsup.cpp b/biometrics/driver/dllsup.cpp new file mode 100644 index 000000000..8030a1c0b --- /dev/null +++ b/biometrics/driver/dllsup.cpp @@ -0,0 +1,87 @@ +/*++ + + 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 + +Module Name: + + Dllsup.cpp + +Abstract: + + This module contains the implementation of the Driver DLL entry point. + +Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + +#include "internal.h" +#include "dllsup.tmh" + +// +// TODO - define a new GUID here +// This GUID goes in the inf file in the DriverCLSID value for the service binary +// {F1CB3C15-A916-47bc-BEA1-D5D4163BC6AE} +// +const CLSID CLSID_BiometricUsbSample = +{ 0xf1cb3c15, 0xa916, 0x47bc, { 0xbe, 0xa1, 0xd5, 0xd4, 0x16, 0x3b, 0xc6, 0xae } }; + + + +HINSTANCE g_hInstance = NULL; + +class CBiometricDriverModule : + public CAtlDllModuleT< CBiometricDriverModule > +{ +}; + +CBiometricDriverModule _AtlModule; + +// +// DLL Entry Point +// + +extern "C" +BOOL +WINAPI +DllMain( + HINSTANCE hInstance, + DWORD dwReason, + LPVOID lpReserved + ) +{ + if (dwReason == DLL_PROCESS_ATTACH) { + WPP_INIT_TRACING(MYDRIVER_TRACING_ID); + + g_hInstance = hInstance; + DisableThreadLibraryCalls(hInstance); + + } else if (dwReason == DLL_PROCESS_DETACH) { + WPP_CLEANUP(); + } + + return _AtlModule.DllMain(dwReason, lpReserved); +} + + +// +// Returns a class factory to create an object of the requested type +// + +STDAPI +DllGetClassObject( + _In_ REFCLSID rclsid, + _In_ REFIID riid, + _Outptr_ LPVOID FAR* ppv + ) +{ + return _AtlModule.DllGetClassObject(rclsid, riid, ppv); +} + + diff --git a/biometrics/driver/exports.def b/biometrics/driver/exports.def new file mode 100644 index 000000000..37b136229 --- /dev/null +++ b/biometrics/driver/exports.def @@ -0,0 +1,10 @@ +; Exports.def : Declares the module parameters. + +; +; TODO: Change the library name here to match your binary name. +; + +LIBRARY "WudfBioUsbSample.DLL" + +EXPORTS + DllGetClassObject PRIVATE diff --git a/biometrics/driver/inc/public.h b/biometrics/driver/inc/public.h new file mode 100644 index 000000000..11a7e8fa1 --- /dev/null +++ b/biometrics/driver/inc/public.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 + +Module Name: + + public.h + +Abstract: + + Public definitions for the Biometric Device. + +Environment: + + User & Kernel mode + +--*/ + +#ifndef _PUBLIC_H +#define _PUBLIC_H + +#include + +// +// INTERRUPT_MESSAGE +// + +typedef struct _INTERRUPT_MESSAGE +{ + + // + // TODO: Fill this in with your device specific fields. + // + +} INTERRUPT_MESSAGE, *PINTERRUPT_MESSAGE; + +#endif diff --git a/biometrics/driver/inc/usb_hw.h b/biometrics/driver/inc/usb_hw.h new file mode 100644 index 000000000..299e7a02f --- /dev/null +++ b/biometrics/driver/inc/usb_hw.h @@ -0,0 +1,238 @@ +/*++ + + 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 + +Module Name: + + Usb.h + +Abstract: + + Contains prototypes for interfacing with a USB connected device. These + are copied from the KMDF WDFUSB.H header file (but with the WDF specific + portions removed) + +Environment: + + kernel mode only + +--*/ + +#pragma once + +typedef enum _WINUSB_BMREQUEST_DIRECTION { + BmRequestHostToDevice = BMREQUEST_HOST_TO_DEVICE, + BmRequestDeviceToHost = BMREQUEST_DEVICE_TO_HOST, +} WINUSB_BMREQUEST_DIRECTION; + +typedef enum _WINUSB_BMREQUEST_TYPE { + BmRequestStandard = BMREQUEST_STANDARD, + BmRequestClass = BMREQUEST_CLASS, + BmRequestVendor = BMREQUEST_VENDOR, +} WINUSB_BMREQUEST_TYPE; + +typedef enum _WINUSB_BMREQUEST_RECIPIENT { + BmRequestToDevice = BMREQUEST_TO_DEVICE, + BmRequestToInterface = BMREQUEST_TO_INTERFACE, + BmRequestToEndpoint = BMREQUEST_TO_ENDPOINT, + BmRequestToOther = BMREQUEST_TO_OTHER, +} WINUSB_BMREQUEST_RECIPIENT; + +typedef enum _WINUSB_DEVICE_TRAITS { + WINUSB_DEVICE_TRAIT_SELF_POWERED = 0x00000001, + WINUSB_DEVICE_TRAIT_REMOTE_WAKE_CAPABLE = 0x00000002, + WINUSB_DEVICE_TRAIT_AT_HIGH_SPEED = 0x00000004, +} WINUSB_DEVICE_TRAITS; + +typedef enum _WdfUsbTargetDeviceSelectInterfaceType { + WdfUsbTargetDeviceSelectInterfaceTypeInterface = 0x10, + WdfUsbTargetDeviceSelectInterfaceTypeUrb = 0x11, +} WdfUsbTargetDeviceSelectInterfaceType; + + + +typedef union _WINUSB_CONTROL_SETUP_PACKET { + struct { + union { + #pragma warning(disable:4214) // bit field types other than int + struct { + // + // Valid values are BMREQUEST_TO_DEVICE, BMREQUEST_TO_INTERFACE, + // BMREQUEST_TO_ENDPOINT, BMREQUEST_TO_OTHER + // + BYTE Recipient:2; + + BYTE Reserved:3; + + // + // Valid values are BMREQUEST_STANDARD, BMREQUEST_CLASS, + // BMREQUEST_VENDOR + // + BYTE Type:2; + + // + // Valid values are BMREQUEST_HOST_TO_DEVICE, + // BMREQUEST_DEVICE_TO_HOST + // + BYTE Dir:1; + } Request; + #pragma warning(default:4214) // bit field types other than int + BYTE Byte; + } bm; + + BYTE bRequest; + + union { + struct { + BYTE LowByte; + BYTE HiByte; + } Bytes; + USHORT Value; + } wValue; + + union { + struct { + BYTE LowByte; + BYTE HiByte; + } Bytes; + USHORT Value; + } wIndex; + + USHORT wLength; + } Packet; + + struct { + BYTE Bytes[8]; + } Generic; + + WINUSB_SETUP_PACKET WinUsb; + +} WINUSB_CONTROL_SETUP_PACKET, *PWINUSB_CONTROL_SETUP_PACKET; + +VOID +FORCEINLINE +WINUSB_CONTROL_SETUP_PACKET_INIT( + PWINUSB_CONTROL_SETUP_PACKET Packet, + WINUSB_BMREQUEST_DIRECTION Direction, + WINUSB_BMREQUEST_RECIPIENT Recipient, + BYTE Request, + USHORT Value, + USHORT Index + ) +{ + RtlZeroMemory(Packet, sizeof(WINUSB_CONTROL_SETUP_PACKET)); + + Packet->Packet.bm.Request.Dir = (BYTE) Direction; + Packet->Packet.bm.Request.Type = (BYTE) BmRequestStandard; + Packet->Packet.bm.Request.Recipient = (BYTE) Recipient; + + Packet->Packet.bRequest = Request; + Packet->Packet.wValue.Value = Value; + Packet->Packet.wIndex.Value = Index; + + // Packet->Packet.wLength will be set by the formatting function +} + +VOID +FORCEINLINE +WINUSB_CONTROL_SETUP_PACKET_INIT_CLASS( + PWINUSB_CONTROL_SETUP_PACKET Packet, + WINUSB_BMREQUEST_DIRECTION Direction, + WINUSB_BMREQUEST_RECIPIENT Recipient, + BYTE Request, + USHORT Value, + USHORT Index + ) +{ + RtlZeroMemory(Packet, sizeof(WINUSB_CONTROL_SETUP_PACKET)); + + Packet->Packet.bm.Request.Dir = (BYTE) Direction; + Packet->Packet.bm.Request.Type = (BYTE) BmRequestClass; + Packet->Packet.bm.Request.Recipient = (BYTE) Recipient; + + Packet->Packet.bRequest = Request; + Packet->Packet.wValue.Value = Value; + Packet->Packet.wIndex.Value = Index; + + // Packet->Packet.wLength will be set by the formatting function +} + +VOID +FORCEINLINE +WINUSB_CONTROL_SETUP_PACKET_INIT_VENDOR( + PWINUSB_CONTROL_SETUP_PACKET Packet, + WINUSB_BMREQUEST_DIRECTION Direction, + WINUSB_BMREQUEST_RECIPIENT Recipient, + BYTE Request, + USHORT Value, + USHORT Index + ) +{ + RtlZeroMemory(Packet, sizeof(WINUSB_CONTROL_SETUP_PACKET)); + + Packet->Packet.bm.Request.Dir = (BYTE) Direction; + Packet->Packet.bm.Request.Type = (BYTE) BmRequestVendor; + Packet->Packet.bm.Request.Recipient = (BYTE) Recipient; + + Packet->Packet.bRequest = Request; + Packet->Packet.wValue.Value = Value; + Packet->Packet.wIndex.Value = Index; + + // Packet->Packet.wLength will be set by the formatting function +} + +VOID +FORCEINLINE +WINUSB_CONTROL_SETUP_PACKET_INIT_FEATURE( + PWINUSB_CONTROL_SETUP_PACKET Packet, + WINUSB_BMREQUEST_RECIPIENT BmRequestRecipient, + USHORT FeatureSelector, + USHORT Index, + BOOLEAN SetFeature + ) +{ + RtlZeroMemory(Packet, sizeof(WINUSB_CONTROL_SETUP_PACKET)); + + Packet->Packet.bm.Request.Dir = (BYTE) BmRequestHostToDevice; + Packet->Packet.bm.Request.Type = (BYTE) BmRequestStandard; + Packet->Packet.bm.Request.Recipient = (BYTE) BmRequestRecipient; + + if (SetFeature) { + Packet->Packet.bRequest = USB_REQUEST_SET_FEATURE; + } + else { + Packet->Packet.bRequest = USB_REQUEST_CLEAR_FEATURE; + } + + Packet->Packet.wValue.Value = FeatureSelector; + Packet->Packet.wIndex.Value = Index; + + // Packet->Packet.wLength will be set by the formatting function +} + +VOID +FORCEINLINE +WINUSB_CONTROL_SETUP_PACKET_INIT_GET_STATUS( + PWINUSB_CONTROL_SETUP_PACKET Packet, + WINUSB_BMREQUEST_RECIPIENT BmRequestRecipient, + USHORT Index + ) +{ + RtlZeroMemory(Packet, sizeof(WINUSB_CONTROL_SETUP_PACKET)); + + Packet->Packet.bm.Request.Dir = (BYTE) BmRequestDeviceToHost; + Packet->Packet.bm.Request.Type = (BYTE) BmRequestStandard; + Packet->Packet.bm.Request.Recipient = (BYTE) BmRequestRecipient; + + Packet->Packet.bRequest = USB_REQUEST_GET_STATUS; + Packet->Packet.wIndex.Value = Index; + Packet->Packet.wValue.Value = 0; + + // Packet->Packet.wLength will be set by the formatting function +} + diff --git a/biometrics/driver/internal.h b/biometrics/driver/internal.h new file mode 100644 index 000000000..bbccbdba0 --- /dev/null +++ b/biometrics/driver/internal.h @@ -0,0 +1,164 @@ +/*++ + + 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 + +Module Name: + + Internal.h + +Abstract: + + This module contains necessary include directives, WPP tracing macros, + and string definitions for the Biometric driver sample. + +Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + +#pragma once + +// +// ATL support +// +#include "atlbase.h" +#include "atlcom.h" + +// +// Include the WUDF Headers +// + +#include "wudfddi.h" + +// +// Use specstrings for in/out annotation of function parameters. +// + +#include "specstrings.h" + +// +// Get limits on common data types (ULONG_MAX for example) +// + +#include "limits.h" + +// +// We need usb I/O targets to talk to the USB device. +// + +#include "wudfusb.h" + +// +// WinUsb structures. +// + +#include "usb_hw.h" + +// +// Public definitions. +// +#include "public.h" + +// +// GUID include +// +#include + +// +// Windows IOCTL definitions. +// +#include "winioctl.h" + +// +// WinBio includes +// +#include "winbio_types.h" +#include "winbio_err.h" +#include "winbio_ioctl.h" + +// +// RAII helper class for requests +// +#include "RequestHelper.h" + +// +// Define the tracing flags. +// +// Tracing GUID defined in BioUsbSample.ctl - 864936A6-DB79-451e-B764-E720D61A9361 +// +// TODO: Generate a new tracing GUID for your driver, and replace all +// instances of the GUID above with your new GUID. +// + +#define WPP_CONTROL_GUIDS \ + WPP_DEFINE_CONTROL_GUID( \ + WudfBioUsbSampleTraceGuid, (864936A6,DB79,451e,B764,E720D61A9361), \ + \ + WPP_DEFINE_BIT(MYDRIVER_ALL_INFO) \ + WPP_DEFINE_BIT(BIOMETRIC_TRACE_DRIVER) \ + WPP_DEFINE_BIT(BIOMETRIC_TRACE_DEVICE) \ + WPP_DEFINE_BIT(BIOMETRIC_TRACE_QUEUE) \ + ) + +#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 Trace{FLAG=MYDRIVER_ALL_INFO}(LEVEL, MSG, ...); +// FUNC TraceEvents(LEVEL, FLAGS, MSG, ...); +// end_wpp +// + +// +// Forward definition of queue. +// +typedef class CBiometricIoQueue *PCBiometricIoQueue; + +// +// Include the type specific headers. +// +#include "Driver.h" +#include "Device.h" +#include "IoQueue.h" + +// +// Driver specific #defines +// TODO: Put strings specific to your device here. +// + +#define MYDRIVER_TRACING_ID L"Microsoft\\UMDF\\Biometric USB Sample V1.0" + +#define SAMPLE_MANUFACTURER_NAME L"Biometric Sample Manufacturer" +#define SAMPLE_MODEL_NAME L"Biometric Sample Model" +#define SAMPLE_SERIAL_NUMBER L"000-000-000" + + +template +inline void BiometricSafeRelease(T *&t) +{ + if (t) + { + t->Release(); + } + t = NULL; +} diff --git a/biometrics/driver/readme.htm b/biometrics/driver/readme.htm new file mode 100644 index 000000000..fdd2bd001 --- /dev/null +++ b/biometrics/driver/readme.htm @@ -0,0 +1,166 @@ + + + +Windows Biometric Driver Interface (WBDI) Sample + + + + + + + + + + + + +
+
 
+ + + + +
Windows Driver Kit: Biometric Devices
+

Windows Biometric Driver Interface (WBDI) Sample

[This is preliminary documentation and subject to change.]

Description

+

 This sample implements the Windows Biometric Driver Interface (WBDI). It contains shell code for handling the mandatory IOCTLs necessary to interoperate with the Windows Biometric Framework. A WBDI driver can be deployed in conjunction with an engine adapter DLL to allow a sensor to be exposed from the Windows Biometric Framework. This sample has been written to make use of the UMDF framework, which allows for ease of development and system stability.

+

Theory of Operation

+

 This sample was taken from the UMDF FX2 sample and modified to expose WBDI. It will install on top of the OSR FX2 test device. It still has basic USB operations for talking to a USB device, but it also has the necessary hooks to make this a WBDI driver:

+

Installs WBDI driver, including correct class GUID settings and icons, and registry settings for Windows Biometric Framework configuration

+

Publishes WBDI device interface

+

Supports all the mandatory WBDI IOCTLs

+

Supports cancellation

+

Can be opened with exclusivity

+

All of these things are required for the Windows Biometric Framework service to recognize this device as a biometric device and set up a Biometric Unit. It allows the service to properly control the device.

+

Implementation and Design

+

The sample makes use of ATL support for simplified handling of COM objects with UMDF. 

+

The driver makes use of a parallel queue so that multiple requests can be outstanding at once.

+

It uses device level-locking to simplify internal thread synchronization. This means that only one framework callback can be active at a time.

+

It supports cancellation of any IOCTL which may be I/O intensive, particularly a capture IOCTL. This sample does not have a real capture mechanism, so it is simulated by a 5 second delay returning a capture IOCTL. Cancellation is supported through the mechanism exposed by WUDF, with a callback for a request object. Cancellation support is required for all IOCTLs.

+

There are hooks for all WBDI IOCTLs, including the optional IOCTLs.

+

PnP is very simple for this driver. It needs to only implement OnPrepareHardware and OnReleaseHardware from IPnpCallbackHardware.

+

Some device drivers may need to keep several pending reads to the WinUsb I/O target in order to properly flush all I/O that comes from the device during a capture.

+

Software Requirements

+

The sample runs on the following Windows operating systems:

    +
  • Windows 7
  • +

    +
+

Hardware Requirements

+

The sample requires the following hardware:

    +
  • OSR FX2 test device
  • +

    +
+

+

Processor Requirements

+

The sample runs on the following processor architectures:

    +
  • x86
  • +
  • x64
  • +
  • IA64
  • +

    +
+

+

Installation

+

 The following files must be present in order to install this driver:

+

WudfBioUsbSample.dll

+

WudfBioUsbSample.inf

+

EngineAdapter.dll (built from adapter sample)

+

This sample installs on the OSR FX2 test device. It will not capture real data, but it will create a Biometric Unit in the Windows Biometric Framework.

+

TBD – Links to the test driver suite should go here.

+

Code Tour

+

File Manifest

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileDescription
BioUsbSample.ctlContains the trace guid for the driver
BioUsbSample.rcFile name and description resources
Device.cppImplements the CBiometricDevice class, which has methods to interface with the WinUsb I/O target and methods that implement the WBDI IOCTLs
Device.hContains type definitions for the device, including CBiometricDevice class
dllsup.cppImplements the driver DLL entry points
Driver.cppImplements the CBiometricDriver class
Driver.hDefines CBiometricDriver class
exports.defDefines DLL exports
internal.hPre-compiled header containing all common headers and type definitions
IoQueue.cppImplements CBiometricIoQueue, a parallel queue for processing IOCTLs
IoQueue.hDefines CBiometricIoQueue
MakefileBuild file
makefile.incBuild file
RequestHelper.hDefines and implements a helper class that ensures a request is completed correctly upon exit
resource.hResource definitions
Sources
WudfBioUsbSample.inxUnprocessed INF file, which is generated at build time
inc\list.hImplements a doubly linked list
inc\public.hDefinitions for OSR_FX2 device operations.
inc\usb_hw.hDefines prototypes for interfacing with a USB connected device
WUDFOsrUsbPublic.hDefines the public interface GUID for the OSR_FX2 device
+

+

+

+ +
Build machine: CAPEBUILD
+ + diff --git a/biometrics/driver/resource.h b/biometrics/driver/resource.h new file mode 100644 index 000000000..09e852e8e --- /dev/null +++ b/biometrics/driver/resource.h @@ -0,0 +1,4 @@ +// +// Copyright (C) Microsoft. All rights reserved. +// +#define IDR_MYDRIVER_CLASSINFO 101 diff --git a/bluetooth/bthecho/ReadMe.md b/bluetooth/bthecho/ReadMe.md index 8bc6e35f3..cf19189ac 100644 --- a/bluetooth/bthecho/ReadMe.md +++ b/bluetooth/bthecho/ReadMe.md @@ -3,39 +3,14 @@ Bluetooth Echo L2CAP Profile Driver This sample demonstrates developing [Bluetooth L2CAP profile drivers](http://msdn.microsoft.com/en-us/library/windows/hardware/ff536598) using [Bluetooth L2CAP DDIs](http://msdn.microsoft.com/en-us/library/windows/hardware/ff536585).The sample includes two drivers. One for a device that acts as an L2CAP server and another for a device that acts as an L2CAP client. The server simply echoes back any data that it receives from client on the same L2CA channel. These drivers can be used with devices that can be installed with bth.inf. Such devices get installed as ‘Generic Bluetooth Radio’. Examples of such devices are Bluetooth USB dongles such as (but not limited to): -``` {.syntax xml:space="preserve"} -Generic Bluetooth Radio=\ - BthUsb, USB\Vid_0a12&Pid_0001 -CSR Nanosira=\ - BthUsb, USB\Vid_0a12&Pid_0003 -CSR Nanosira WHQL Reference Radio=\ - BthUsb, USB\Vid_0a12&Pid_0004 -CSR Nanosira-Multimedia=\ - BthUsb, USB\Vid_0a12&Pid_0005 -CSR Nanosira-Multimedia WHQL Reference Radio=\ - BthUsb, USB\Vid_0a12&Pid_0006 -``` + Generic Bluetooth Radio = BthUsb, USB\Vid_0a12&Pid_0001 + CSR Nanosira = BthUsb, USB\Vid_0a12&Pid_0003 + CSR Nanosira WHQL Reference Radi o= BthUsb, USB\Vid_0a12&Pid_0004 + CSR Nanosira-Multimedia = BthUsb, USB\Vid_0a12&Pid_0005 + CSR Nanosira-Multimedia WHQL Reference Radio = BthUsb, USB\Vid_0a12&Pid_0006 Please refer to bth.inf for the complete list of devices. The installation steps below describe how to install echo server and client with such a device. Please note that RFCOMM based profiles must be developed and accessed using user-mode socket APIs. This sample is applicable to Windows® Vista, Windows® 7 and Windows® 8 operating systems. -**Note**   - -To build this sample, you can use Microsoft Visual Studio 2013 (Express, Professional, or Ultimate) and Windows Driver Kit (WDK) 8.1 Update. 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. - -Operating system requirements ------------------------------ - -Client - -Windows 7 - -Server - -Windows Server 2008 R2 Build the sample ---------------- diff --git a/bluetooth/bthecho/bthcli/app/BthEcho.vcxproj b/bluetooth/bthecho/bthcli/app/BthEcho.vcxproj index f982f9b73..8da1d16a6 100644 --- a/bluetooth/bthecho/bthcli/app/BthEcho.vcxproj +++ b/bluetooth/bthecho/bthcli/app/BthEcho.vcxproj @@ -19,11 +19,11 @@
- {B506A244-2ADC-4C65-85B8-8B19764B4849} + {8D6A9C42-F4FA-4D7B-A038-5209844883C3} $(MSBuildProjectName) Debug Win32 - {ED7BAD90-5755-4501-83C1-30383B8C2D76} + {A9FD1AC6-CE1D-45EE-9079-FE3C370F871F} @@ -177,7 +177,6 @@ - diff --git a/bluetooth/bthecho/bthcli/app/BthEcho.vcxproj.Filters b/bluetooth/bthecho/bthcli/app/BthEcho.vcxproj.Filters index bb3d32adf..5f00fc7a0 100644 --- a/bluetooth/bthecho/bthcli/app/BthEcho.vcxproj.Filters +++ b/bluetooth/bthecho/bthcli/app/BthEcho.vcxproj.Filters @@ -3,15 +3,15 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {0A8DC157-9CD8-47FC-96AB-AB876FCB97C8} + {D5028DD9-763A-43E5-A26D-8D3BEF9DEB88} h;hpp;hxx;hm;inl;inc;xsd - {B0233FE1-6785-4466-886B-FC81C40F8257} + {AD474526-552E-4398-818A-E95F266E7F91} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {6E8F0465-6F65-432E-9958-9869CF84EB6B} + {6B067815-190E-45D2-B008-7E8562CD0B30} diff --git a/bluetooth/bthecho/bthcli/sys/BthEchoSampleCli.inx b/bluetooth/bthecho/bthcli/sys/BthEchoSampleCli.inx index 9f55cc597..4fe47e283 100644 --- a/bluetooth/bthecho/bthcli/sys/BthEchoSampleCli.inx +++ b/bluetooth/bthecho/bthcli/sys/BthEchoSampleCli.inx @@ -21,7 +21,7 @@ Signature="$WINDOWS NT$" Class=Sample ClassGuid={e0cbf06c-cd8b-4647-bb8a-263b43f0f974} -Provider=%MSFTSAMPLE% +Provider=%ProviderString% DriverVer=06/21/2006,6.0.5841. CatalogFile=KmdfSamples.cat @@ -39,8 +39,7 @@ BthEchoSampleCli.sys = 1,, ;***************************************** [Manufacturer] -%MSFTSAMPLE%=Microsoft,NTamd64...1 -%MSFTSAMPLE%=Microsoft,NTx86...1 +%ManufacturerString%=Microsoft,NTx86...1,NTamd64...1 [Microsoft.NTamd64...1] %BthEchoSampleCli.DeviceDesc% = BthEchoSampleCli_Inst,BTHENUM\{c07508f2-b970-43ca-b5dd-cc4f2391bef4} @@ -94,8 +93,8 @@ KmdfLibraryVersion = $KMDFVERSION$ [Strings] SPSVCINST_ASSOCSERVICE= 0x00000002 -MSFTSAMPLE = "Microsoft Sample" -StdMfg = "(Standard system devices)" +ProviderString = "TODO-Set-Provider" +ManufacturerString = "TODO-Set-Manufacturer" DiskId1 = "BthEchoSampleCli installation disk" BthEchoSampleCli.DeviceDesc = "Bluetooth Echo Sample Client" BthEchoSampleCli.SVCDESC = "BthEchoSampleCli" diff --git a/bluetooth/bthecho/bthcli/sys/BthEchoSampleCli.vcxproj b/bluetooth/bthecho/bthcli/sys/BthEchoSampleCli.vcxproj index f30cb9f3d..2b1117341 100644 --- a/bluetooth/bthecho/bthcli/sys/BthEchoSampleCli.vcxproj +++ b/bluetooth/bthecho/bthcli/sys/BthEchoSampleCli.vcxproj @@ -19,12 +19,12 @@ - {AADDAE40-9C46-4C50-AC5F-FF957C3A6E94} + {D0526687-DE4D-409A-9488-F0A874D6EA3E} $(MSBuildProjectName) 1 Debug Win32 - {FA5BA6C8-691B-4FF7-9AFB-B50CE61C8ED1} + {67C06B25-EC35-414F-A8A1-03BC3787C159} @@ -187,7 +187,6 @@ - diff --git a/bluetooth/bthecho/bthcli/sys/BthEchoSampleCli.vcxproj.Filters b/bluetooth/bthecho/bthcli/sys/BthEchoSampleCli.vcxproj.Filters index 176a771fe..49ad94cf0 100644 --- a/bluetooth/bthecho/bthcli/sys/BthEchoSampleCli.vcxproj.Filters +++ b/bluetooth/bthecho/bthcli/sys/BthEchoSampleCli.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {12E8E936-7BEE-4822-9BB5-A95B2C6C6537} + {33095013-BA3C-4D73-972A-1BC536F0480F} h;hpp;hxx;hm;inl;inc;xsd - {379796ED-172B-4B2C-9E23-302F90088C9F} + {54B5DEBA-240B-44F1-AEA4-DCA8F28C63CB} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {5B69AC41-D941-4D63-82AB-FAED650522CE} + {04631531-1FEF-42BD-83FA-5382EC0D29A3} inf;inv;inx;mof;mc; - {1907D7F9-002C-43DE-BB60-1544D4D62A74} + {32A5AE44-0838-409A-BAB9-2611B33E8829} @@ -33,9 +33,6 @@ - - Driver Files - Driver Files diff --git a/bluetooth/bthecho/bthecho.sln b/bluetooth/bthecho/bthecho.sln index 3e94270a1..cf33ad908 100644 --- a/bluetooth/bthecho/bthecho.sln +++ b/bluetooth/bthecho/bthecho.sln @@ -3,36 +3,36 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0 MinimumVisualStudioVersion = 12.0 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Lib", "Lib", "{83E67B5A-A079-4795-BB30-7DF17133F3F2}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Lib", "Lib", "{ABF4399D-D992-42E1-8EBF-68C9CFB7F2C3}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Common", "Common", "{5581D7EE-1489-4811-94EE-AA7717B4367A}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Common", "Common", "{556D6471-9F94-46BB-BF1D-B4436E8577E1}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "App", "App", "{17CBFF38-C5AD-417C-8CC8-864EE0DDDD4C}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "App", "App", "{CD3B1A5A-0BBE-4F0C-B412-2A5662ED766A}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Bthcli", "Bthcli", "{8107A759-05EB-4C85-8E43-21D9C34D8437}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Bthcli", "Bthcli", "{A10654D9-385E-4D89-B10B-992F8B31D5DC}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sys", "Sys", "{7F96521C-27C0-448F-9408-BE90EAEAC221}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sys", "Sys", "{B1A01E5B-0A99-42AD-A897-8B91E9ADAB2F}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Inst", "Inst", "{2E43D7E2-4E3C-490D-951B-4271FB00B68D}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Inst", "Inst", "{82373034-DC87-4536-9BE6-06818943E8BA}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Bthsrv", "Bthsrv", "{2FC6C414-6626-45DC-9519-70E6412A131E}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Bthsrv", "Bthsrv", "{23D66828-26DC-48AC-A34B-6503C2345B78}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sys", "Sys", "{2D8F29A9-064E-4557-BBCB-1A09BCC8CC68}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sys", "Sys", "{52BF960B-FC39-4AC7-9486-C3EC71456856}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bthecho", "common\lib\bthecho.vcxproj", "{A2EE1D94-8521-48CE-B925-271ACA5DD459}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bthecho", "common\lib\bthecho.vcxproj", "{279A982A-666D-434D-A53D-3FF3CEBC175D}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BthEcho", "bthcli\app\BthEcho.vcxproj", "{B506A244-2ADC-4C65-85B8-8B19764B4849}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BthEcho", "bthcli\app\BthEcho.vcxproj", "{8D6A9C42-F4FA-4D7B-A038-5209844883C3}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BthEchoSampleCli", "bthcli\sys\BthEchoSampleCli.vcxproj", "{AADDAE40-9C46-4C50-AC5F-FF957C3A6E94}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BthEchoSampleCli", "bthcli\sys\BthEchoSampleCli.vcxproj", "{5346197B-56DB-4486-BEE2-68D0380B7D5B}" ProjectSection(ProjectDependencies) = postProject - {A2EE1D94-8521-48CE-B925-271ACA5DD459} = {A2EE1D94-8521-48CE-B925-271ACA5DD459} + {279A982A-666D-434D-A53D-3FF3CEBC175D} = {279A982A-666D-434D-A53D-3FF3CEBC175D} EndProjectSection EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bthsrvinst", "bthsrv\inst\bthsrvinst.vcxproj", "{9DF556F0-A8AB-41C6-A9A4-EEB07CA724A2}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bthsrvinst", "bthsrv\inst\bthsrvinst.vcxproj", "{AD63285D-E1DA-41F5-93F8-A076CF4A3B64}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BthEchoSampleSrv", "bthsrv\sys\BthEchoSampleSrv.vcxproj", "{B3D6AD34-E4B4-4C3E-8CD2-583FFE3501E3}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BthEchoSampleSrv", "bthsrv\sys\BthEchoSampleSrv.vcxproj", "{2A776B98-0216-4882-A0EA-1AC2251AD597}" ProjectSection(ProjectDependencies) = postProject - {A2EE1D94-8521-48CE-B925-271ACA5DD459} = {A2EE1D94-8521-48CE-B925-271ACA5DD459} + {279A982A-666D-434D-A53D-3FF3CEBC175D} = {279A982A-666D-434D-A53D-3FF3CEBC175D} EndProjectSection EndProject Global @@ -43,60 +43,60 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {A2EE1D94-8521-48CE-B925-271ACA5DD459}.Debug|Win32.ActiveCfg = Debug|Win32 - {A2EE1D94-8521-48CE-B925-271ACA5DD459}.Debug|Win32.Build.0 = Debug|Win32 - {A2EE1D94-8521-48CE-B925-271ACA5DD459}.Release|Win32.ActiveCfg = Release|Win32 - {A2EE1D94-8521-48CE-B925-271ACA5DD459}.Release|Win32.Build.0 = Release|Win32 - {A2EE1D94-8521-48CE-B925-271ACA5DD459}.Debug|x64.ActiveCfg = Debug|x64 - {A2EE1D94-8521-48CE-B925-271ACA5DD459}.Debug|x64.Build.0 = Debug|x64 - {A2EE1D94-8521-48CE-B925-271ACA5DD459}.Release|x64.ActiveCfg = Release|x64 - {A2EE1D94-8521-48CE-B925-271ACA5DD459}.Release|x64.Build.0 = Release|x64 - {B506A244-2ADC-4C65-85B8-8B19764B4849}.Debug|Win32.ActiveCfg = Debug|Win32 - {B506A244-2ADC-4C65-85B8-8B19764B4849}.Debug|Win32.Build.0 = Debug|Win32 - {B506A244-2ADC-4C65-85B8-8B19764B4849}.Release|Win32.ActiveCfg = Release|Win32 - {B506A244-2ADC-4C65-85B8-8B19764B4849}.Release|Win32.Build.0 = Release|Win32 - {B506A244-2ADC-4C65-85B8-8B19764B4849}.Debug|x64.ActiveCfg = Debug|x64 - {B506A244-2ADC-4C65-85B8-8B19764B4849}.Debug|x64.Build.0 = Debug|x64 - {B506A244-2ADC-4C65-85B8-8B19764B4849}.Release|x64.ActiveCfg = Release|x64 - {B506A244-2ADC-4C65-85B8-8B19764B4849}.Release|x64.Build.0 = Release|x64 - {AADDAE40-9C46-4C50-AC5F-FF957C3A6E94}.Debug|Win32.ActiveCfg = Debug|Win32 - {AADDAE40-9C46-4C50-AC5F-FF957C3A6E94}.Debug|Win32.Build.0 = Debug|Win32 - {AADDAE40-9C46-4C50-AC5F-FF957C3A6E94}.Release|Win32.ActiveCfg = Release|Win32 - {AADDAE40-9C46-4C50-AC5F-FF957C3A6E94}.Release|Win32.Build.0 = Release|Win32 - {AADDAE40-9C46-4C50-AC5F-FF957C3A6E94}.Debug|x64.ActiveCfg = Debug|x64 - {AADDAE40-9C46-4C50-AC5F-FF957C3A6E94}.Debug|x64.Build.0 = Debug|x64 - {AADDAE40-9C46-4C50-AC5F-FF957C3A6E94}.Release|x64.ActiveCfg = Release|x64 - {AADDAE40-9C46-4C50-AC5F-FF957C3A6E94}.Release|x64.Build.0 = Release|x64 - {9DF556F0-A8AB-41C6-A9A4-EEB07CA724A2}.Debug|Win32.ActiveCfg = Debug|Win32 - {9DF556F0-A8AB-41C6-A9A4-EEB07CA724A2}.Debug|Win32.Build.0 = Debug|Win32 - {9DF556F0-A8AB-41C6-A9A4-EEB07CA724A2}.Release|Win32.ActiveCfg = Release|Win32 - {9DF556F0-A8AB-41C6-A9A4-EEB07CA724A2}.Release|Win32.Build.0 = Release|Win32 - {9DF556F0-A8AB-41C6-A9A4-EEB07CA724A2}.Debug|x64.ActiveCfg = Debug|x64 - {9DF556F0-A8AB-41C6-A9A4-EEB07CA724A2}.Debug|x64.Build.0 = Debug|x64 - {9DF556F0-A8AB-41C6-A9A4-EEB07CA724A2}.Release|x64.ActiveCfg = Release|x64 - {9DF556F0-A8AB-41C6-A9A4-EEB07CA724A2}.Release|x64.Build.0 = Release|x64 - {B3D6AD34-E4B4-4C3E-8CD2-583FFE3501E3}.Debug|Win32.ActiveCfg = Debug|Win32 - {B3D6AD34-E4B4-4C3E-8CD2-583FFE3501E3}.Debug|Win32.Build.0 = Debug|Win32 - {B3D6AD34-E4B4-4C3E-8CD2-583FFE3501E3}.Release|Win32.ActiveCfg = Release|Win32 - {B3D6AD34-E4B4-4C3E-8CD2-583FFE3501E3}.Release|Win32.Build.0 = Release|Win32 - {B3D6AD34-E4B4-4C3E-8CD2-583FFE3501E3}.Debug|x64.ActiveCfg = Debug|x64 - {B3D6AD34-E4B4-4C3E-8CD2-583FFE3501E3}.Debug|x64.Build.0 = Debug|x64 - {B3D6AD34-E4B4-4C3E-8CD2-583FFE3501E3}.Release|x64.ActiveCfg = Release|x64 - {B3D6AD34-E4B4-4C3E-8CD2-583FFE3501E3}.Release|x64.Build.0 = Release|x64 + {279A982A-666D-434D-A53D-3FF3CEBC175D}.Debug|Win32.ActiveCfg = Debug|Win32 + {279A982A-666D-434D-A53D-3FF3CEBC175D}.Debug|Win32.Build.0 = Debug|Win32 + {279A982A-666D-434D-A53D-3FF3CEBC175D}.Release|Win32.ActiveCfg = Release|Win32 + {279A982A-666D-434D-A53D-3FF3CEBC175D}.Release|Win32.Build.0 = Release|Win32 + {279A982A-666D-434D-A53D-3FF3CEBC175D}.Debug|x64.ActiveCfg = Debug|x64 + {279A982A-666D-434D-A53D-3FF3CEBC175D}.Debug|x64.Build.0 = Debug|x64 + {279A982A-666D-434D-A53D-3FF3CEBC175D}.Release|x64.ActiveCfg = Release|x64 + {279A982A-666D-434D-A53D-3FF3CEBC175D}.Release|x64.Build.0 = Release|x64 + {8D6A9C42-F4FA-4D7B-A038-5209844883C3}.Debug|Win32.ActiveCfg = Debug|Win32 + {8D6A9C42-F4FA-4D7B-A038-5209844883C3}.Debug|Win32.Build.0 = Debug|Win32 + {8D6A9C42-F4FA-4D7B-A038-5209844883C3}.Release|Win32.ActiveCfg = Release|Win32 + {8D6A9C42-F4FA-4D7B-A038-5209844883C3}.Release|Win32.Build.0 = Release|Win32 + {8D6A9C42-F4FA-4D7B-A038-5209844883C3}.Debug|x64.ActiveCfg = Debug|x64 + {8D6A9C42-F4FA-4D7B-A038-5209844883C3}.Debug|x64.Build.0 = Debug|x64 + {8D6A9C42-F4FA-4D7B-A038-5209844883C3}.Release|x64.ActiveCfg = Release|x64 + {8D6A9C42-F4FA-4D7B-A038-5209844883C3}.Release|x64.Build.0 = Release|x64 + {5346197B-56DB-4486-BEE2-68D0380B7D5B}.Debug|Win32.ActiveCfg = Debug|Win32 + {5346197B-56DB-4486-BEE2-68D0380B7D5B}.Debug|Win32.Build.0 = Debug|Win32 + {5346197B-56DB-4486-BEE2-68D0380B7D5B}.Release|Win32.ActiveCfg = Release|Win32 + {5346197B-56DB-4486-BEE2-68D0380B7D5B}.Release|Win32.Build.0 = Release|Win32 + {5346197B-56DB-4486-BEE2-68D0380B7D5B}.Debug|x64.ActiveCfg = Debug|x64 + {5346197B-56DB-4486-BEE2-68D0380B7D5B}.Debug|x64.Build.0 = Debug|x64 + {5346197B-56DB-4486-BEE2-68D0380B7D5B}.Release|x64.ActiveCfg = Release|x64 + {5346197B-56DB-4486-BEE2-68D0380B7D5B}.Release|x64.Build.0 = Release|x64 + {AD63285D-E1DA-41F5-93F8-A076CF4A3B64}.Debug|Win32.ActiveCfg = Debug|Win32 + {AD63285D-E1DA-41F5-93F8-A076CF4A3B64}.Debug|Win32.Build.0 = Debug|Win32 + {AD63285D-E1DA-41F5-93F8-A076CF4A3B64}.Release|Win32.ActiveCfg = Release|Win32 + {AD63285D-E1DA-41F5-93F8-A076CF4A3B64}.Release|Win32.Build.0 = Release|Win32 + {AD63285D-E1DA-41F5-93F8-A076CF4A3B64}.Debug|x64.ActiveCfg = Debug|x64 + {AD63285D-E1DA-41F5-93F8-A076CF4A3B64}.Debug|x64.Build.0 = Debug|x64 + {AD63285D-E1DA-41F5-93F8-A076CF4A3B64}.Release|x64.ActiveCfg = Release|x64 + {AD63285D-E1DA-41F5-93F8-A076CF4A3B64}.Release|x64.Build.0 = Release|x64 + {2A776B98-0216-4882-A0EA-1AC2251AD597}.Debug|Win32.ActiveCfg = Debug|Win32 + {2A776B98-0216-4882-A0EA-1AC2251AD597}.Debug|Win32.Build.0 = Debug|Win32 + {2A776B98-0216-4882-A0EA-1AC2251AD597}.Release|Win32.ActiveCfg = Release|Win32 + {2A776B98-0216-4882-A0EA-1AC2251AD597}.Release|Win32.Build.0 = Release|Win32 + {2A776B98-0216-4882-A0EA-1AC2251AD597}.Debug|x64.ActiveCfg = Debug|x64 + {2A776B98-0216-4882-A0EA-1AC2251AD597}.Debug|x64.Build.0 = Debug|x64 + {2A776B98-0216-4882-A0EA-1AC2251AD597}.Release|x64.ActiveCfg = Release|x64 + {2A776B98-0216-4882-A0EA-1AC2251AD597}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {A2EE1D94-8521-48CE-B925-271ACA5DD459} = {83E67B5A-A079-4795-BB30-7DF17133F3F2} - {B506A244-2ADC-4C65-85B8-8B19764B4849} = {17CBFF38-C5AD-417C-8CC8-864EE0DDDD4C} - {AADDAE40-9C46-4C50-AC5F-FF957C3A6E94} = {7F96521C-27C0-448F-9408-BE90EAEAC221} - {9DF556F0-A8AB-41C6-A9A4-EEB07CA724A2} = {2E43D7E2-4E3C-490D-951B-4271FB00B68D} - {B3D6AD34-E4B4-4C3E-8CD2-583FFE3501E3} = {2D8F29A9-064E-4557-BBCB-1A09BCC8CC68} - {83E67B5A-A079-4795-BB30-7DF17133F3F2} = {5581D7EE-1489-4811-94EE-AA7717B4367A} - {17CBFF38-C5AD-417C-8CC8-864EE0DDDD4C} = {8107A759-05EB-4C85-8E43-21D9C34D8437} - {7F96521C-27C0-448F-9408-BE90EAEAC221} = {8107A759-05EB-4C85-8E43-21D9C34D8437} - {2E43D7E2-4E3C-490D-951B-4271FB00B68D} = {2FC6C414-6626-45DC-9519-70E6412A131E} - {2D8F29A9-064E-4557-BBCB-1A09BCC8CC68} = {2FC6C414-6626-45DC-9519-70E6412A131E} + {279A982A-666D-434D-A53D-3FF3CEBC175D} = {ABF4399D-D992-42E1-8EBF-68C9CFB7F2C3} + {8D6A9C42-F4FA-4D7B-A038-5209844883C3} = {CD3B1A5A-0BBE-4F0C-B412-2A5662ED766A} + {5346197B-56DB-4486-BEE2-68D0380B7D5B} = {B1A01E5B-0A99-42AD-A897-8B91E9ADAB2F} + {AD63285D-E1DA-41F5-93F8-A076CF4A3B64} = {82373034-DC87-4536-9BE6-06818943E8BA} + {2A776B98-0216-4882-A0EA-1AC2251AD597} = {52BF960B-FC39-4AC7-9486-C3EC71456856} + {ABF4399D-D992-42E1-8EBF-68C9CFB7F2C3} = {556D6471-9F94-46BB-BF1D-B4436E8577E1} + {CD3B1A5A-0BBE-4F0C-B412-2A5662ED766A} = {A10654D9-385E-4D89-B10B-992F8B31D5DC} + {B1A01E5B-0A99-42AD-A897-8B91E9ADAB2F} = {A10654D9-385E-4D89-B10B-992F8B31D5DC} + {82373034-DC87-4536-9BE6-06818943E8BA} = {23D66828-26DC-48AC-A34B-6503C2345B78} + {52BF960B-FC39-4AC7-9486-C3EC71456856} = {23D66828-26DC-48AC-A34B-6503C2345B78} EndGlobalSection EndGlobal diff --git a/bluetooth/bthecho/bthsrv/inst/bthsrvinst.vcxproj b/bluetooth/bthecho/bthsrv/inst/bthsrvinst.vcxproj index 612205d04..2f38026b8 100644 --- a/bluetooth/bthecho/bthsrv/inst/bthsrvinst.vcxproj +++ b/bluetooth/bthecho/bthsrv/inst/bthsrvinst.vcxproj @@ -19,11 +19,11 @@ - {9DF556F0-A8AB-41C6-A9A4-EEB07CA724A2} + {4BAF5A1E-F44D-4883-8434-758C29841444} $(MSBuildProjectName) Debug Win32 - {6D053B10-3595-40FE-AB0C-0BFF3690D74B} + {E2F8D294-B9B1-4F9E-9A7B-F0A49D7C4C8F} @@ -177,7 +177,6 @@ - diff --git a/bluetooth/bthecho/bthsrv/inst/bthsrvinst.vcxproj.Filters b/bluetooth/bthecho/bthsrv/inst/bthsrvinst.vcxproj.Filters index d6a736d10..a4d8af9ae 100644 --- a/bluetooth/bthecho/bthsrv/inst/bthsrvinst.vcxproj.Filters +++ b/bluetooth/bthecho/bthsrv/inst/bthsrvinst.vcxproj.Filters @@ -3,15 +3,15 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {269E58B5-F87C-40BF-9AF8-D7A43956F273} + {54764289-21F6-4CB7-B759-F580ABFAA48F} h;hpp;hxx;hm;inl;inc;xsd - {C83F223A-D6E4-4CBD-BE8A-F23F9C51CD21} + {2CA7739A-5BEB-4DBE-B55B-4E42BC89D6BC} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {E0EF99F8-3D01-474D-9924-A4E579A44058} + {755A8841-90F7-4E7A-92AF-47B1BF16358B} diff --git a/bluetooth/bthecho/bthsrv/sys/BthEchoSampleSrv.inx b/bluetooth/bthecho/bthsrv/sys/BthEchoSampleSrv.inx index c414b5655..a85479d05 100644 --- a/bluetooth/bthecho/bthsrv/sys/BthEchoSampleSrv.inx +++ b/bluetooth/bthecho/bthsrv/sys/BthEchoSampleSrv.inx @@ -21,7 +21,7 @@ Signature="$WINDOWS NT$" Class=Sample ClassGuid={e0cbf06c-cd8b-4647-bb8a-263b43f0f974} -Provider=%MSFTSAMPLE% +Provider=%ProviderString% DriverVer=06/21/2006,6.0.6001.16625 CatalogFile=KmdfSamples.cat @@ -39,8 +39,7 @@ BthEchoSampleSrv.sys = 1,, ;***************************************** [Manufacturer] -%MSFTSAMPLE%=Microsoft,NTamd64...1 -%MSFTSAMPLE%=Microsoft,NTx86...1 +%ManufacturerString%=Microsoft,NTx86...1,NTamd64...1 [Microsoft.NTamd64...1] %BthEchoSampleSrv.DeviceDesc% = BthEchoSampleSrv_Inst,BTHENUM\{c07508f2-b970-43ca-b5dd-cc4f2391bef4} @@ -95,8 +94,8 @@ KmdfLibraryVersion = $KMDFVERSION$ [Strings] SPSVCINST_ASSOCSERVICE= 0x00000002 -MSFTSAMPLE = "Microsoft Sample" -StdMfg = "(Standard system devices)" +ProviderString = "TODO-Set-Provider" +ManufacturerString = "TODO-Set-Manufacturer" DiskId1 = "BthEchoSampleSrv installation disk" BthEchoSampleSrv.DeviceDesc = "Bluetooth Echo Sample Server" BthEchoSampleSrv.SVCDESC = "BthEchoSampleSrv" diff --git a/bluetooth/bthecho/bthsrv/sys/BthEchoSampleSrv.vcxproj b/bluetooth/bthecho/bthsrv/sys/BthEchoSampleSrv.vcxproj index d175beeac..c46955f2d 100644 --- a/bluetooth/bthecho/bthsrv/sys/BthEchoSampleSrv.vcxproj +++ b/bluetooth/bthecho/bthsrv/sys/BthEchoSampleSrv.vcxproj @@ -19,12 +19,12 @@ - {B3D6AD34-E4B4-4C3E-8CD2-583FFE3501E3} + {E76B7342-7D14-4C40-ADA4-40D0269CC2C4} $(MSBuildProjectName) 1 Debug Win32 - {B2ECC080-547B-4E53-90B4-DD7637A509E1} + {8092859B-AF55-4BF1-A3F4-945B4198D753} @@ -187,7 +187,6 @@ - diff --git a/bluetooth/bthecho/bthsrv/sys/BthEchoSampleSrv.vcxproj.Filters b/bluetooth/bthecho/bthsrv/sys/BthEchoSampleSrv.vcxproj.Filters index bcf30fdbb..12d1cfc1e 100644 --- a/bluetooth/bthecho/bthsrv/sys/BthEchoSampleSrv.vcxproj.Filters +++ b/bluetooth/bthecho/bthsrv/sys/BthEchoSampleSrv.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {AD6BD7F9-8AA3-4C43-80E5-43FAB4855E99} + {DB9631DF-48DF-461B-AE7F-506167663FDB} h;hpp;hxx;hm;inl;inc;xsd - {C9966B02-9CF0-4709-81DB-6C07A8C61EA2} + {80DE4EC6-38A5-4477-89E7-7474A47F2AB9} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {3EB93E42-3C57-45BD-B13C-3BFBBE7327C8} + {9A504811-A75F-4216-9CF0-308AA242BA42} inf;inv;inx;mof;mc; - {1AD1BB40-C641-417E-BEEB-6D99F9B5AF3A} + {D808070A-9612-4A62-842D-141D1C7A3100} @@ -36,9 +36,6 @@ - - Driver Files - Driver Files diff --git a/bluetooth/bthecho/common/lib/bthecho.vcxproj b/bluetooth/bthecho/common/lib/bthecho.vcxproj index 1ff84251c..0966aafa3 100644 --- a/bluetooth/bthecho/common/lib/bthecho.vcxproj +++ b/bluetooth/bthecho/common/lib/bthecho.vcxproj @@ -19,12 +19,12 @@ - {A2EE1D94-8521-48CE-B925-271ACA5DD459} + {279A982A-666D-434D-A53D-3FF3CEBC175D} $(MSBuildProjectName) 1 Debug Win32 - {7D08015D-5262-4372-8E97-C25A002DA190} + {2887F895-DC60-4F81-9E78-B126FC82D6DE} @@ -188,7 +188,6 @@ - diff --git a/bluetooth/bthecho/common/lib/bthecho.vcxproj.Filters b/bluetooth/bthecho/common/lib/bthecho.vcxproj.Filters index 70735b7ec..ec089435e 100644 --- a/bluetooth/bthecho/common/lib/bthecho.vcxproj.Filters +++ b/bluetooth/bthecho/common/lib/bthecho.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {2F1B68C0-756D-4458-87A8-8E75D9D889E3} + {6217FA2C-0C23-4AB7-A668-E0C7E3CA4CF6} h;hpp;hxx;hm;inl;inc;xsd - {B9556308-CC93-4C4F-A7E2-55F119DCD6DA} + {620A4632-EEE2-4CB4-AC1B-3E0BB40B6E8F} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {A3F3F256-9D63-4D56-8493-B44D13A67F0A} + {0A73451A-D21E-42CE-810D-663390E414AF} inf;inv;inx;mof;mc; - {11D85DF8-6812-42E3-A08E-798DE942BED5} + {BD9AA505-1CD0-4D77-AECB-D3D8871886E6} diff --git a/bluetooth/serialhcibus/Fdo.c b/bluetooth/serialhcibus/Fdo.c index c7fcc4ff2..8082e4e86 100644 --- a/bluetooth/serialhcibus/Fdo.c +++ b/bluetooth/serialhcibus/Fdo.c @@ -9,7 +9,7 @@ Module Name: Abstract: This module contains routines to handle the function driver - aspect of the bus driver. + aspect of the bus driver. Environment: @@ -18,13 +18,12 @@ Module Name: --*/ #include "driver.h" -#include +#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) @@ -40,14 +39,14 @@ Module Name: #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 +// 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; + WDFDEVICE Fdo; } ENABLE_PDO_CONTEXT, *PENABLE_PDO_CONTEXT; WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(ENABLE_PDO_CONTEXT, GetEnablePdoWorkItemContext) @@ -68,31 +67,31 @@ Routine Description: _pWorkItem - work item that contains a context to help carrying out its task -Return Value: ---*/ +Return Value: +--*/ { - PENABLE_PDO_CONTEXT Context; + PENABLE_PDO_CONTEXT Context; LARGE_INTEGER RemoteWakeTimeout; - + NTSTATUS Status = STATUS_SUCCESS; PAGED_CODE(); - DoTrace(LEVEL_INFO, TFLAG_PNP, ("+DeviceEnablePDOWorker")); - Context = GetEnablePdoWorkItemContext(_WorkItem); + DoTrace(LEVEL_INFO, TFLAG_PNP, ("+DeviceEnablePDOWorker")); + Context = GetEnablePdoWorkItemContext(_WorkItem); - RemoteWakeTimeout.QuadPart = WDF_REL_TIMEOUT_IN_MS(g_WaitToEnablePDO); - KeDelayExecutionThread(KernelMode, FALSE, &RemoteWakeTimeout); + RemoteWakeTimeout.QuadPart = WDF_REL_TIMEOUT_IN_MS(g_WaitToEnablePDO); + KeDelayExecutionThread(KernelMode, FALSE, &RemoteWakeTimeout); - DoTrace(LEVEL_INFO, TFLAG_PNP, ("+Complete the wait")); + 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 ); + BLUETOOTH_FUNC_IDS ); + + DoTrace(LEVEL_INFO, TFLAG_POWER, ("-DeviceEnablePDOWorker %!STATUS!", Status)); - DoTrace(LEVEL_INFO, TFLAG_POWER, ("-DeviceEnablePDOWorker %!STATUS!", Status)); - } NTSTATUS @@ -138,7 +137,6 @@ Return Value: pDesc->SerialNo); } - NTSTATUS FdoCreateOneChildDeviceDynamic( _In_ WDFDEVICE _Device, @@ -187,7 +185,7 @@ Routine Description: // response to InvalidateDeviceRelations call made as part of adding // a new child. // - Status = WdfChildListAddOrUpdateChildDescriptionAsPresent(WdfFdoGetDefaultChildList(_Device), + Status = WdfChildListAddOrUpdateChildDescriptionAsPresent(WdfFdoGetDefaultChildList(_Device), &Description.Header, NULL); // AddressDescription @@ -204,32 +202,31 @@ Routine Description: #endif // ifdef DYNAMIC_ENUM - NTSTATUS FdoCreateOneChildDevice( _In_ WDFDEVICE _Device, - _In_ PWSTR _HardwareIds, + _In_ PWSTR _HardwareIds, _In_ ULONG _SerialNo ) /*++ Routine Description: - Create a new PDO, initialize it, add it to the list of PDOs for this + 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 + _HardwareIDs - hardware Id for a device _SerialNo - Unique ID for a child DO Returns: Status - + --*/ { NTSTATUS Status = STATUS_SUCCESS; @@ -240,7 +237,7 @@ Routine Description: PAGED_CODE(); - DoTrace(LEVEL_INFO, TFLAG_PNP, ("+ FdoCreateOneChildDevice() HWID: %S", _HardwareIds)); + DoTrace(LEVEL_INFO, TFLAG_PNP, ("+ FdoCreateOneChildDevice() HWID: %S", _HardwareIds)); // // First make sure that we don't already have another device with the @@ -271,7 +268,7 @@ Routine Description: WdfFdoLockStaticChildListForIteration(_Device); while ((ChildDevice = WdfFdoRetrieveNextStaticChild(_Device, - ChildDevice, + ChildDevice, WdfRetrieveAddedChildren)) != NULL) { // // WdfFdoRetrieveNextStaticChild returns reported and to be reported @@ -306,7 +303,7 @@ Routine Description: WdfFdoUnlockStaticChildListFromIteration(_Device); WdfWaitLockRelease(FdoExtension->ChildLock); - DoTrace(LEVEL_INFO, TFLAG_PNP, ("- FdoCreateOneChildDevice() %!STATUS!", Status)); + DoTrace(LEVEL_INFO, TFLAG_PNP, ("- FdoCreateOneChildDevice() %!STATUS!", Status)); return Status; } @@ -334,10 +331,10 @@ Routine Description: Returns: Status - + --*/ -{ +{ PPDO_EXTENSION PdoExtension; BOOLEAN Found = FALSE; BOOLEAN PlugOutAll; @@ -353,7 +350,7 @@ Routine Description: WdfFdoLockStaticChildListForIteration(_Device); while ((ChildDevice = WdfFdoRetrieveNextStaticChild(_Device, - ChildDevice, + ChildDevice, WdfRetrieveAddedChildren)) != NULL) { if (PlugOutAll) { @@ -366,7 +363,7 @@ Routine Description: Found = TRUE; } else { - PdoExtension = PdoGetExtension(ChildDevice); + PdoExtension = PdoGetExtension(ChildDevice); if (_SerialNo == PdoExtension->SerialNo) { @@ -391,7 +388,6 @@ Routine Description: return Status; } - NTSTATUS FdoCreateAllChildren( _In_ WDFDEVICE _Device @@ -400,7 +396,7 @@ FdoCreateAllChildren( Routine Description: The routine enables you to statically enumerate child device functions - during start. + during start. Arguments: @@ -408,25 +404,25 @@ Routine Description: Returns: - Status + Status --*/ { - NTSTATUS Status; + NTSTATUS Status; PFDO_EXTENSION FdoExtension; PAGED_CODE(); - DoTrace(LEVEL_INFO, TFLAG_PNP, (" + FdoCreateAllChildren")); + 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 + // This sample code only enuemrate the Bluetooth function as the only // child device. // Status = FdoCreateOneChildDevice(_Device, @@ -436,16 +432,15 @@ Routine Description: FdoExtension = FdoGetExtension(_Device); if (NT_SUCCESS(Status)) { FdoExtension->IsRadioEnabled = TRUE; - } - + } + return Status; } - NTSTATUS HlpInitializeFdoExtension( - WDFDEVICE _Device + WDFDEVICE _Device ) /*++ Routine Description: @@ -460,7 +455,7 @@ Return Value: Status ---*/ +--*/ { PFDO_EXTENSION FdoExtension; WDF_OBJECT_ATTRIBUTES Attributes; @@ -468,7 +463,7 @@ Return Value: PAGED_CODE(); - DoTrace(LEVEL_INFO, TFLAG_PNP,("+HlpInitializeFdoExtension")); + DoTrace(LEVEL_INFO, TFLAG_PNP,("+HlpInitializeFdoExtension")); FdoExtension = FdoGetExtension(_Device); FdoExtension->WdfDevice = _Device; @@ -476,58 +471,62 @@ Return Value: // // 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. + // 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.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 // - // Preallocate Request - // WDF_OBJECT_ATTRIBUTES_INIT(&Attributes); - Attributes.ParentObject = _Device; - + Attributes.ParentObject = _Device; + Status = WdfRequestCreate(&Attributes, FdoExtension->IoTargetSerial, &FdoExtension->RequestIoctlSync); - if (!NT_SUCCESS(Status)) + 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)) + if (!NT_SUCCESS(Status)) { DoTrace(LEVEL_ERROR, TFLAG_PNP, (" WdfRequestCreate failed %!STATUS!", Status)); goto Exit; - } - - Status = WdfMemoryCreatePreallocated(&Attributes, + } + + Status = WdfMemoryCreatePreallocated(&Attributes, &FdoExtension->SerErrorMask, sizeof(FdoExtension->SerErrorMask), - &FdoExtension->WaitMaskMemory); + &FdoExtension->WaitMaskMemory); - if (!NT_SUCCESS(Status)) + if (!NT_SUCCESS(Status)) { DoTrace(LEVEL_ERROR, TFLAG_PNP, (" WdfMemoryCreatePreallocated failed %!STATUS!", Status)); goto Exit; - } + } - KeInitializeSpinLock(&FdoExtension->QueueAccessLock); + Status = WdfSpinLockCreate(&Attributes, &FdoExtension->QueueAccessLock); + if (!NT_SUCCESS(Status)) + { + DoTrace(LEVEL_ERROR, TFLAG_PNP, (" WdfSpinLockCreate failed %!STATUS!", Status)); + goto Exit; + } Exit: - + return Status; } - VOID FdoEvtDeviceDisarmWake( _In_ WDFDEVICE _Device @@ -535,12 +534,11 @@ FdoEvtDeviceDisarmWake( /*++ 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 + 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 + 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: @@ -551,13 +549,12 @@ Return Value: VOID ---*/ -{ +--*/ +{ UNREFERENCED_PARAMETER(_Device); DoTrace(LEVEL_INFO, TFLAG_PNP,(" FdoEvtDeviceDisarmWake")); } - NTSTATUS FdoEvtDeviceArmWake( _In_ WDFDEVICE _Device @@ -566,8 +563,8 @@ FdoEvtDeviceArmWake( 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. + 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: @@ -577,14 +574,14 @@ Return Value: NTSTATUS ---*/ +--*/ { NTSTATUS Status = STATUS_SUCCESS; UNREFERENCED_PARAMETER(_Device); - + DoTrace(LEVEL_INFO, TFLAG_PNP,(" FdoEvtDeviceArmWake")); - return Status; + return Status; } NTSTATUS @@ -620,7 +617,7 @@ Return Value: { PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor; - PFDO_EXTENSION FdoExtension; + PFDO_EXTENSION FdoExtension; ULONG Index; ULONG ResourceCount = 0; NTSTATUS Status; @@ -630,96 +627,94 @@ Return Value: PAGED_CODE(); - DoTrace(LEVEL_INFO, TFLAG_PNP,("+FdoFindConnectResources")); + DoTrace(LEVEL_INFO, TFLAG_PNP,("+FdoFindConnectResources")); FdoExtension = FdoGetExtension(_Device); Status = STATUS_SUCCESS; // - // Walk through the resource list and find and cache expected resources. + // Walk through the resource list and find and cache expected resources. // ResourceCount = WdfCmResourceListGetCount(_ResourcesTranslated); - - for (Index = 0; Index < ResourceCount; Index++) + + for (Index = 0; Index < ResourceCount; Index++) { Descriptor = WdfCmResourceListGetDescriptor(_ResourcesTranslated, Index); - - switch(Descriptor->Type) - { + + switch(Descriptor->Type) + { case CmResourceTypeConnection: // // Cache connetion ID that this BT Peripheral device is connected to // - UART (must exist) - // - GPIO (optional) + // - GPIO (optional) // - + if ((Descriptor->u.Connection.Class == CM_RESOURCE_CONNECTION_CLASS_SERIAL) && - (Descriptor->u.Connection.Type == CM_RESOURCE_CONNECTION_TYPE_SERIAL_UART)) + (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)", + 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)) + (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)", + + 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)) + (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)", + + 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)); + 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)); - + DoTrace(LEVEL_INFO, TFLAG_PNP,("-FdoFindConnectResources ResourceCount %d, %!STATUS!", ResourceCount, Status)); + return Status; } - NTSTATUS FdoOpenDevice( _In_ WDFDEVICE _Device, @@ -741,34 +736,33 @@ Return Value: NTSTATUS ---*/ -{ - NTSTATUS Status = STATUS_SUCCESS; +--*/ +{ + 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")); + WDF_IO_TARGET_OPEN_PARAMS OpenParams; + + DoTrace(LEVEL_INFO, TFLAG_PNP,("+FdoOpenDevice")); Status = WdfIoTargetCreate(_Device, - WDF_NO_OBJECT_ATTRIBUTES, + WDF_NO_OBJECT_ATTRIBUTES, &IoTargetSerial); - if (!NT_SUCCESS(Status)) + 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. + // 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)) @@ -786,7 +780,7 @@ Return Value: goto Exit; } } - else + else { // Query the system for device with SERIAL interface Status = IoGetDeviceInterfaces(&GUID_DEVINTERFACE_COMPORT, @@ -794,7 +788,7 @@ Return Value: 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)); @@ -810,13 +804,13 @@ Return Value: // 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); + RtlInitUnicodeString(&TargetDeviceName, SymbolicLinkList); } - DoTrace(LEVEL_INFO, TFLAG_PNP, (" Symbolic Name '%S'", TargetDeviceName.Buffer)); + DoTrace(LEVEL_INFO, TFLAG_PNP, (" Symbolic Name '%S'", TargetDeviceName.Buffer)); // - // Open the "remote" IO Target (device) using its symbolic link. + // Open the "remote" IO Target (device) using its symbolic link. // WDF_IO_TARGET_OPEN_PARAMS_INIT_OPEN_BY_NAME(&OpenParams, &TargetDeviceName, @@ -826,48 +820,47 @@ Return Value: // // Open this serial device (Io Target) in order to send IOCTL_SERIAL_* control to it. // - Status = WdfIoTargetOpen(IoTargetSerial, - &OpenParams); - - if (!NT_SUCCESS(Status)) + Status = WdfIoTargetOpen(IoTargetSerial, + &OpenParams); + + if (!NT_SUCCESS(Status)) { - DoTrace(LEVEL_INFO, TFLAG_PNP, ( " WdfIoTargetOpen failed %!STATUS!", 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). - + 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 + 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 + Vendor: If its Enumerator is ACPI, then it might be possible to support idle while in S0. This is vendor specific. Arguments: @@ -880,15 +873,15 @@ Return Value: NTSTATUS ---*/ +--*/ { WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS IdleSettings; - NTSTATUS Status = STATUS_SUCCESS; + NTSTATUS Status = STATUS_SUCCESS; BOOLEAN AssignS0IdleSettings = TRUE; - DoTrace(LEVEL_INFO, TFLAG_PNP,("+FdoSetIdleSettings")); + DoTrace(LEVEL_INFO, TFLAG_PNP,("+FdoSetIdleSettings")); - switch (_IdleCapState) + switch (_IdleCapState) { case IdleCapActiveOnly: @@ -896,87 +889,87 @@ Return Value: // 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; + 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; + 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 + // 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. + // 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; + + 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; + 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 + // 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 + // 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; + + 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; @@ -985,24 +978,23 @@ Return Value: if (AssignS0IdleSettings) { Status = WdfDeviceAssignS0IdleSettings(_Device, - &IdleSettings); + &IdleSettings); } - - DoTrace(LEVEL_INFO, TFLAG_PNP,("-FdoSetIdleSettings %!STATUS!", Status)); - return Status; -} + DoTrace(LEVEL_INFO, TFLAG_PNP,("-FdoSetIdleSettings %!STATUS!", Status)); + return Status; +} NTSTATUS FdoDevPrepareHardware( _In_ WDFDEVICE _Device, _In_ WDFCMRESLIST _ResourcesRaw, - _In_ WDFCMRESLIST _ResourcesTranslated + _In_ WDFCMRESLIST _ResourcesTranslated ) /*++ Routine Description: - This PnP CB function allocate hardware related resource allocation and + This PnP CB function allocate hardware related resource allocation and perform device initialization. Arguments: @@ -1010,22 +1002,21 @@ Routine Description: _Device - WDF Device object _ResourcesRaw - (Not referenced) - + _ResourcesTranslated - (Not referenced) Return Value: NTSTATUS ---*/ +--*/ { NTSTATUS Status; - PFDO_EXTENSION FdoExtension; - - + PFDO_EXTENSION FdoExtension; + PAGED_CODE(); - DoTrace(LEVEL_INFO, TFLAG_PNP,("+FdoDevPrepareHardware")); + DoTrace(LEVEL_INFO, TFLAG_PNP,("+FdoDevPrepareHardware")); // // Acquire connection ID of connected controllers (UART and GPIO) @@ -1035,13 +1026,12 @@ Return Value: _ResourcesTranslated); if (!NT_SUCCESS(Status)) { - DoTrace(LEVEL_ERROR, TFLAG_PNP, (" Failed to find connection ID of target UART controller %!STATUS!", 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); @@ -1049,42 +1039,42 @@ Return Value: // Open Bluetooth UART device as a remote IO Target // Status = FdoOpenDevice(_Device, &FdoExtension->IoTargetSerial); - - if (!NT_SUCCESS(Status) || FdoExtension->IoTargetSerial == NULL) - { + + if (!NT_SUCCESS(Status) || FdoExtension->IoTargetSerial == NULL) + { DoTrace(LEVEL_ERROR, TFLAG_PNP, (" FdoOpenDevice failed %!STATUS!", Status)); - // Log(Error): Failed to open UART controller + // Log(Error): Failed to open UART controller goto Exit; } // // Initialize content of this device extension // - Status = HlpInitializeFdoExtension(_Device); - + Status = HlpInitializeFdoExtension(_Device); + if (!NT_SUCCESS(Status)) { - DoTrace(LEVEL_ERROR, TFLAG_PNP, (" HlpInitializeFdoExtension failed %!STATUS!", Status)); + DoTrace(LEVEL_ERROR, TFLAG_PNP, (" HlpInitializeFdoExtension failed %!STATUS!", Status)); goto Exit; - } + } // // Set device's idle configuration if it is capable - // - Status = FdoSetIdleSettings(_Device, + // + Status = FdoSetIdleSettings(_Device, IdleCapCanTurnOff); - if (!NT_SUCCESS(Status)) + if (!NT_SUCCESS(Status)) { - DoTrace(LEVEL_ERROR, TFLAG_PNP, (" FdoSetIdleSettings failed %!STATUS!", 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)); + DoTrace(LEVEL_ERROR, TFLAG_PNP,("DeviceEnable failed %!STATUS!", Status)); goto Exit; } } @@ -1093,35 +1083,35 @@ Return Value: if (ValidConnectionID(FdoExtension->I2CConnectionId)) { Status = DevicePowerOn(_Device); if (!NT_SUCCESS(Status)) { - DoTrace(LEVEL_ERROR, TFLAG_PNP,("DevicePowerOn failed %!STATUS!", Status)); + DoTrace(LEVEL_ERROR, TFLAG_PNP,("DevicePowerOn failed %!STATUS!", Status)); goto Exit; } } - + // - // Configure local UART controller + // Configure local UART controller // FdoExtension->DeviceInitialized = DeviceInitialize(FdoExtension, - FdoExtension->IoTargetSerial, + FdoExtension->IoTargetSerial, FdoExtension->RequestIoctlSync, - TRUE); + 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)); + DoTrace(LEVEL_ERROR, TFLAG_PNP, (" DeviceInitialize failed %!STATUS!", Status)); - // Log(Error): Failed to intialize/configure the device + // 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 + // 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. + // to start the enuermation after driver has started. // { WDF_OBJECT_ATTRIBUTES ObjAttributes; @@ -1129,12 +1119,12 @@ Return Value: WDFWORKITEM WorkItem; PENABLE_PDO_CONTEXT Context; - PAGED_CODE(); + PAGED_CODE(); + + DoTrace(LEVEL_INFO, TFLAG_PNP, ("+CreateWorkItem to enable PDO")); - 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; @@ -1143,32 +1133,31 @@ Return Value: 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); - } + 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)); - + DoTrace(LEVEL_INFO, TFLAG_PNP, ("-FdoDevPrepareHardware %!STATUS!", Status)); + return Status; } - NTSTATUS FdoDevReleaseHardware( _In_ WDFDEVICE _Device, @@ -1182,22 +1171,22 @@ Routine Description: Arguments: _Device - WDF Device object - + _ResourcesTranslated - (Not referenced) Return Value: NTSTATUS ---*/ +--*/ { - PFDO_EXTENSION FdoExtension; - + PFDO_EXTENSION FdoExtension; + PAGED_CODE(); - - UNREFERENCED_PARAMETER(_ResourcesTranslated); - DoTrace(LEVEL_INFO, TFLAG_PNP,("+PnpReleaseHardware")); + UNREFERENCED_PARAMETER(_ResourcesTranslated); + + DoTrace(LEVEL_INFO, TFLAG_PNP,("+PnpReleaseHardware")); FdoExtension = FdoGetExtension(_Device); @@ -1205,14 +1194,13 @@ Return Value: { WdfObjectDelete(FdoExtension->IoTargetSerial); FdoExtension->IoTargetSerial = NULL; - } - + } + return STATUS_SUCCESS; } - -NTSTATUS +NTSTATUS FdoDevSelfManagedIoInit( _In_ WDFDEVICE _Device ) @@ -1220,7 +1208,7 @@ FdoDevSelfManagedIoInit( Routine Description: This PnP CB function is invoked once and will perform IO related resource allocation - and start the read pump. + and start the read pump. Arguments: @@ -1230,56 +1218,55 @@ Return Value: NTSTATUS ---*/ +--*/ { NTSTATUS Status; PFDO_EXTENSION FdoExtension; - + PAGED_CODE(); - DoTrace(LEVEL_INFO, TFLAG_PNP,("+FdoDevSelfManagedIoInit")); + DoTrace(LEVEL_INFO, TFLAG_PNP,("+FdoDevSelfManagedIoInit")); // - // Preallocate resources needed to perform read opeations - // + // 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; + // Start the read pump + FdoExtension->ReadPumpRunning = TRUE; Status = ReadH4Packet(&FdoExtension->ReadContext, FdoExtension->ReadRequest, FdoExtension->ReadMemory, FdoExtension->ReadBuffer, - INITIAL_H4_READ_SIZE); + INITIAL_H4_READ_SIZE); if (!NT_SUCCESS(Status)) { DoTrace(LEVEL_ERROR, TFLAG_IO, (" ReadH4Packet failed %!STATUS!", Status)); goto Exit; - } - + } + Exit: - return Status; + return Status; } - -VOID +VOID FdoDevSelfManagedIoCleanup( _In_ WDFDEVICE _Device ) /*++ Routine Description: - This PnP CB function is invoked once and will be used here to free resource + This PnP CB function is invoked once and will be used here to free resource that was alocated in its corresponding SelfMagedInit fucntion. Arguments: @@ -1290,20 +1277,19 @@ Return Value: none ---*/ -{ +--*/ +{ PAGED_CODE(); - DoTrace(LEVEL_INFO, TFLAG_PNP,("+FdoDevSelfManagedIoCleanup")); - + DoTrace(LEVEL_INFO, TFLAG_PNP,("+FdoDevSelfManagedIoCleanup")); + // // Cancel and free resources - // - ReadResourcesFree(_Device); - - return; -} + // + ReadResourcesFree(_Device); + return; +} NTSTATUS FdoDevD0Entry( @@ -1320,33 +1306,33 @@ Routine Description: 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; + NTSTATUS Status = STATUS_SUCCESS; + + UNREFERENCED_PARAMETER(_PreviousState); - UNREFERENCED_PARAMETER(_PreviousState); - - DoTrace(LEVEL_INFO, TFLAG_UART, ("+FdoDevD0Entry")); + 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)) { + 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)); + DoTrace(LEVEL_ERROR, TFLAG_PNP,("DeviceEnable failed %!STATUS!", Status)); goto Done; } } @@ -1355,30 +1341,30 @@ Return Value: if (ValidConnectionID(FdoExtension->I2CConnectionId)) { Status = DevicePowerOn(_Device); if (!NT_SUCCESS(Status)) { - DoTrace(LEVEL_ERROR, TFLAG_PNP,("DevicePowerOn failed %!STATUS!", 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 + // 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->DeviceInitialized = DeviceInitialize(FdoExtension, + FdoExtension->IoTargetSerial, FdoExtension->RequestIoctlSync, - FALSE); + FALSE); if (!IsDeviceInitialized(FdoExtension)) { Status = STATUS_DEVICE_NOT_READY; - DoTrace(LEVEL_ERROR, TFLAG_PNP, ("DeviceInitialize failed!")); - goto Done; + DoTrace(LEVEL_ERROR, TFLAG_PNP, ("DeviceInitialize failed!")); + goto Done; } -#else +#else // Set to TRUE in order to restart the read pump FdoExtension->DeviceInitialized = TRUE; #endif @@ -1386,31 +1372,30 @@ Return Value: // Restart the IOTarget to receiving request Status = WdfIoTargetStart(FdoExtension->IoTargetSerial); if (!NT_SUCCESS(Status)) { - DoTrace(LEVEL_ERROR, TFLAG_PNP, ("WdfIoTargetStart failed %!STATUS!", 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); + INITIAL_H4_READ_SIZE); if (!NT_SUCCESS(Status)) { - DoTrace(LEVEL_ERROR, TFLAG_IO, ("ReadH4Packet [0] failed %!STATUS!", 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, @@ -1421,34 +1406,34 @@ 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. + 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(); + 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, + // 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 + // Delete GPIO IoTarget to disable the device and this will // require device to be re-initialized when it re-enters D0. if (FdoExtension->IoTargetGPIO) { @@ -1460,13 +1445,12 @@ Return Value: // // Note: Do not delete the UART's IoTarget. // - + DoTrace(LEVEL_INFO, TFLAG_UART, ("-FdoDevD0Exit")); return STATUS_SUCCESS; } - NTSTATUS HCIContextValidate( ULONG _Index, @@ -1475,12 +1459,12 @@ HCIContextValidate( /*++ Routine Description: - This function validate the incoming data context and print out (WPP) trace. - + 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 + _HCIContext - Context to be valdiated Return Value: @@ -1492,44 +1476,44 @@ Return Value: 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->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)); + 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, + 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", @@ -1539,13 +1523,13 @@ Return Value: if (!WithinRange(MIN_HCI_EVENT_SIZE, _HCIContext->DataLen, MAX_HCI_EVENT_SIZE)) { Status = STATUS_INVALID_PARAMETER; - DoTrace(LEVEL_ERROR, TFLAG_HCI,(" HciPacketEvent %!STATUS!", Status)); + DoTrace(LEVEL_ERROR, TFLAG_HCI,(" HciPacketEvent %!STATUS!", Status)); break; - } - } + } + } break; - - case HciPacketAclData: + + 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)", @@ -1554,39 +1538,37 @@ Return Value: 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)) + + 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)); + 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!"); + NT_ASSERT(NT_SUCCESS(Status) && L"Invlaid data is detected!"); + + DoTrace(LEVEL_INFO, TFLAG_HCI,("-HCIContextValidate %!STATUS!", Status)); - DoTrace(LEVEL_INFO, TFLAG_HCI,("-HCIContextValidate %!STATUS!", Status)); - return Status; } - - NTSTATUS FdoWriteDeviceIO( _In_ WDFREQUEST _RequestFromBthport, @@ -1598,7 +1580,7 @@ FdoWriteDeviceIO( Routine Description: - This function send an HCI packet to target device. + This function send an HCI packet to target device. Arguments: @@ -1610,18 +1592,17 @@ Routine Description: Return Value: NTSTATUS - ---*/ -{ - WDF_OBJECT_ATTRIBUTES ObjAttributes; - NTSTATUS Status; + +--*/ +{ + WDF_OBJECT_ATTRIBUTES ObjAttributes; + NTSTATUS Status; WDFREQUEST RequestToUART; PUART_WRITE_CONTEXT TransferContext = NULL; ULONG DataLength; PVOID Data = NULL; - - DoTrace(LEVEL_INFO, TFLAG_DATA,("+FdoWriteDeviceIO")); + DoTrace(LEVEL_INFO, TFLAG_DATA,("+FdoWriteDeviceIO")); if (!IsDeviceInitialized(_FdoExtension)) { @@ -1635,13 +1616,13 @@ Return Value: // WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&ObjAttributes, UART_WRITE_CONTEXT); - + Status = WdfObjectAllocateContext(_RequestFromBthport, &ObjAttributes, - &TransferContext); + &TransferContext); if (!NT_SUCCESS(Status)) { - DoTrace(LEVEL_ERROR, TFLAG_IO, (" WdfObjectAllocateContext failed %!STATUS!", Status)); - goto Done; + DoTrace(LEVEL_ERROR, TFLAG_IO, (" WdfObjectAllocateContext failed %!STATUS!", Status)); + goto Done; } Status = HLP_AllocateResourceForWrite( @@ -1650,72 +1631,71 @@ Return Value: &RequestToUART); if (!NT_SUCCESS(Status)) { - DoTrace(LEVEL_ERROR, TFLAG_IO,(" HLP_WriteDeviceIO %!STATUS!", 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 + 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, + Status = WdfMemoryCreatePreallocated(&ObjAttributes, + Data, + DataLength, &TransferContext->Memory); if (!NT_SUCCESS(Status)) { - DoTrace(LEVEL_ERROR, TFLAG_IO, (" WdfMemoryCreatePreallocated failed %!STATUS!", Status)); + DoTrace(LEVEL_ERROR, TFLAG_IO, (" WdfMemoryCreatePreallocated failed %!STATUS!", Status)); goto Done; } - + Status = WdfIoTargetFormatRequestForWrite(_FdoExtension->IoTargetSerial, RequestToUART, - TransferContext->Memory, - NULL, - NULL); + TransferContext->Memory, + NULL, + NULL); if (!NT_SUCCESS(Status)) { - DoTrace(LEVEL_ERROR, TFLAG_IO, (" WdfIoTargetFormatRequestForRead failed %!STATUS!", Status)); + DoTrace(LEVEL_ERROR, TFLAG_IO, (" WdfIoTargetFormatRequestForRead failed %!STATUS!", Status)); goto Done; } - + // Setup transfer context - TransferContext->FdoExtension = _FdoExtension; - TransferContext->HCIContext = _HCIContext; + TransferContext->FdoExtension = _FdoExtension; + TransferContext->HCIContext = _HCIContext; TransferContext->RequestFromBthport = _RequestFromBthport; TransferContext->RequestCompletePath = REQUEST_PATH_NONE; TransferContext->RequestToUART = RequestToUART; TransferContext->HCIPacket = Data; - TransferContext->HCIPacketLen = DataLength; + 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. + // 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); - + 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); - + // 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 + // 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); @@ -1723,12 +1703,12 @@ Return Value: StatusTemp = WdfRequestUnmarkCancelable(_RequestFromBthport); // Balance the reference count for both Requests due to failure. - WdfObjectDereference(RequestToUART); - WdfObjectDereference(_RequestFromBthport); - + 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 @@ -1747,13 +1727,12 @@ Return Value: return Status; } - NTSTATUS FdoWriteToDeviceSync( _In_ WDFIOTARGET _IoTargetSerial, _In_ WDFREQUEST _RequestWriteSync, _In_ ULONG _IoControlCode, - _In_opt_ ULONG _InBufferSize, + _In_opt_ ULONG _InBufferSize, _In_opt_ PVOID _InBuffer, _Out_ PULONG_PTR _BytesWritten ) @@ -1761,28 +1740,28 @@ FdoWriteToDeviceSync( Routine Description: This helper function send a synchronous write or Ioctl Request to device with - timeout (to prevent hang). - + 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. + _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 + _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; + 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; @@ -1790,14 +1769,13 @@ Return Value: DoTrace(LEVEL_INFO, TFLAG_IO,("+FdoWriteToDeviceSync")); - - WDF_REQUEST_REUSE_PARAMS_INIT(&RequestReuseParams, WDF_REQUEST_REUSE_NO_FLAGS, STATUS_SUCCESS); + 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; @@ -1805,16 +1783,16 @@ Return Value: _InBuffer, _InBufferSize); } - - WDF_REQUEST_SEND_OPTIONS_INIT(&Options, WDF_REQUEST_SEND_OPTION_SYNCHRONOUS); + + 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, + NULL, _IoControlCode, - HasInputParam ? &MemoryDescriptor : NULL, // InputBuffer + HasInputParam ? &MemoryDescriptor : NULL, // InputBuffer NULL, // OutputBuffer &Options, // RequestOptions &BytesWritten // BytesReturned @@ -1823,14 +1801,14 @@ Return Value: else { Status = WdfIoTargetSendWriteSynchronously(_IoTargetSerial, - NULL, + 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)) @@ -1839,10 +1817,9 @@ Return Value: } Done: - return Status; + return Status; } - VOID FdoIoQuDeviceControl( _In_ WDFQUEUE _Queue, @@ -1856,7 +1833,7 @@ FdoIoQuDeviceControl( Routine Description: This routine is the dispatch routine for device control requests. - + Arguments: _Queue - Handle to the framework queue object that is associated @@ -1877,21 +1854,19 @@ Return Value: --*/ { - WDFMEMORY ReqInMemory = NULL, ReqOutMemory = NULL; + WDFMEMORY ReqInMemory = NULL, ReqOutMemory = NULL; PVOID InBuffer = NULL, OutBuffer = NULL; - size_t InBufferSize = 0, OutBufferSize = 0; + 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)); + (ULONG) _InputBufferLength, (ULONG) _OutputBufferLength)); Device = WdfIoQueueGetDevice(_Queue); @@ -1899,46 +1874,46 @@ Return Value: if (_InputBufferLength) { - Status = WdfRequestRetrieveInputMemory(_Request, &ReqInMemory); - if (NT_SUCCESS(Status)) - { - InBuffer = WdfMemoryGetBuffer(ReqInMemory, &InBufferSize); + 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: + 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)) + !OutBuffer || OutBufferSize != sizeof(BTHX_HCI_PACKET_TYPE)) { Status = STATUS_INVALID_PARAMETER; DoTrace(LEVEL_ERROR, TFLAG_IOCTL,(" IOCTL_BTHX_WRITE_HCI %!STATUS!", Status)); - break; + break; } - + HCIContext = (PBTHX_HCI_READ_WRITE_CONTEXT) InBuffer; - PacketType = (BTHX_HCI_PACKET_TYPE) HCIContext->Type; - + 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; - } - + DoTrace(LEVEL_ERROR, TFLAG_IOCTL,(" Mismach Write HCI packet type and IOCTL %!STATUS!", Status)); + break; + } + if (PacketType == HciPacketCommand) { InterlockedIncrement(&FdoExtension->CntCommandReq); @@ -1947,176 +1922,176 @@ Return Value: { InterlockedIncrement(&FdoExtension->CntWriteDataReq); } - + Status = FdoWriteDeviceIO(_Request, Device, FdoExtension, - HCIContext); + HCIContext); break; - + case IOCTL_BTHX_READ_HCI: - DoTrace(LEVEL_INFO, TFLAG_IOCTL,(" IOCTL_BTHX_READ_HCI <----------")); - // Validate input and output parameters + 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; - } + break; + } + + PacketType = *((BTHX_HCI_PACKET_TYPE *) InBuffer); - 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; + break; } if (PacketType == HciPacketEvent) { - KeAcquireSpinLock(&FdoExtension->QueueAccessLock, &Irql); + WdfSpinLockAcquire(FdoExtension->QueueAccessLock); // Queue the new request to preserve sequential order - Status = WdfRequestForwardToIoQueue(_Request, FdoExtension->ReadEventQueue); + Status = WdfRequestForwardToIoQueue(_Request, FdoExtension->ReadEventQueue); if (NT_SUCCESS(Status)) { InterlockedIncrement(&FdoExtension->EventQueueCount); - InterlockedIncrement(&FdoExtension->CntEventReq); + InterlockedIncrement(&FdoExtension->CntEventReq); } - KeReleaseSpinLock(&FdoExtension->QueueAccessLock, Irql); + WdfSpinLockRelease(FdoExtension->QueueAccessLock); if (NT_SUCCESS(Status)) { - Status = ReadRequestComplete(FdoExtension, + Status = ReadRequestComplete(FdoExtension, HciPacketEvent, 0, NULL, FdoExtension->ReadEventQueue, &FdoExtension->EventQueueCount, &FdoExtension->ReadEventList, - &FdoExtension->EventListCount); - } + &FdoExtension->EventListCount); + } } else if (PacketType == HciPacketAclData) - { - KeAcquireSpinLock(&FdoExtension->QueueAccessLock, &Irql); + { + WdfSpinLockAcquire(FdoExtension->QueueAccessLock); // Queue the new request to preserve sequential order - Status = WdfRequestForwardToIoQueue(_Request, FdoExtension->ReadDataQueue); + Status = WdfRequestForwardToIoQueue(_Request, FdoExtension->ReadDataQueue); if (NT_SUCCESS(Status)) - { + { InterlockedIncrement(&FdoExtension->DataQueueCount); InterlockedIncrement(&FdoExtension->CntReadDataReq); } - KeReleaseSpinLock(&FdoExtension->QueueAccessLock, Irql); + WdfSpinLockRelease(FdoExtension->QueueAccessLock); if (NT_SUCCESS(Status)) - { - Status = ReadRequestComplete(FdoExtension, + { + Status = ReadRequestComplete(FdoExtension, HciPacketAclData, 0, NULL, FdoExtension->ReadDataQueue, &FdoExtension->DataQueueCount, &FdoExtension->ReadDataList, - &FdoExtension->DataListCount); - } + &FdoExtension->DataListCount); + } } else { Status = STATUS_INVALID_PARAMETER; DoTrace(LEVEL_ERROR, TFLAG_IOCTL,(" IOCTL_BTHX_READ_HCI %!STATUS!", Status)); - break; + break; } - break; + break; - case IOCTL_BTHX_GET_VERSION: - CompleteRequest = TRUE; + 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; + break; + + case IOCTL_BTHX_SET_VERSION: + CompleteRequest = TRUE; + DoTrace(LEVEL_INFO, TFLAG_IOCTL,("IOCTL_BTHX_SET_VERSION")); - 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; + break; case IOCTL_BTHX_QUERY_CAPABILITIES: - CompleteRequest = TRUE; + 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)); + RtlCopyMemory(pCaps, &FdoExtension->BthXCaps, sizeof(BTHX_CAPABILITIES)); WdfRequestCompleteWithInformation(_Request, Status, sizeof(BTHX_CAPABILITIES)); - return; + return; } else { Status = STATUS_INVALID_PARAMETER; - } - break; + } + break; // // This IOCTL is used to support radio on/off feature by doing the following - // 1. Power up/down the Bluetooth radio function, and + // 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")); + 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 (IsRadioEnabled) { if (!FdoExtension->IsRadioEnabled) { - + // - // 1. Power up the Bluetooth function of this device; + // 1. Power up the Bluetooth function of this device; // - Status = DevicePowerOn(Device); + Status = DevicePowerOn(Device); if (NT_SUCCESS(Status)) { - + // - // 2. Create a PDO for the Bluetooth devnode; + // 2. Create a PDO for the Bluetooth devnode; // Status = FdoCreateOneChildDevice(Device, BT_PDO_HARDWARE_IDS, - BLUETOOTH_FUNC_IDS); + BLUETOOTH_FUNC_IDS); if (NT_SUCCESS(Status)) { FdoExtension->IsRadioEnabled = TRUE; - } + } } - DoTrace(LEVEL_INFO, TFLAG_IOCTL,(" EnableRadio %!STATUS!", Status)); + DoTrace(LEVEL_INFO, TFLAG_IOCTL,(" EnableRadio %!STATUS!", Status)); } else { - Status = STATUS_SUCCESS; + Status = STATUS_SUCCESS; DoTrace(LEVEL_WARNING, TFLAG_IOCTL,(" Already enabled!")); } } @@ -2125,41 +2100,41 @@ Return Value: // // 1. Remove the PDO for the Bluetooth devnode; - // + // Status = FdoRemoveOneChildDevice(Device, BLUETOOTH_FUNC_IDS); if (NT_SUCCESS(Status)) { - FdoExtension->IsRadioEnabled = FALSE; - + FdoExtension->IsRadioEnabled = FALSE; + // - // 2. Power down the Bluetooth function (at least the antenna) of this device; - // - Status = DevicePowerOff(Device); + // 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_SUCCESS; + DoTrace(LEVEL_WARNING, TFLAG_IOCTL,(" Already disabled!")); } - } + } } else { Status = STATUS_INVALID_PARAMETER; - } - break; - + } + break; + default: DoTrace(LEVEL_INFO, TFLAG_IOCTL,(" IOCTL_(0x%x, Func %d)", _IoControlCode, ControlCode)); - Status = STATUS_NOT_SUPPORTED; - break; + Status = STATUS_NOT_SUPPORTED; + break; } - - if (!NT_SUCCESS(Status) || CompleteRequest) + + if (!NT_SUCCESS(Status) || CompleteRequest) { WdfRequestComplete(_Request, Status); - } + } return; } diff --git a/bluetooth/serialhcibus/WDK/SerialBusWdk.inx b/bluetooth/serialhcibus/WDK/SerialBusWdk.inx index b6e81096f..5ba76403d 100644 --- a/bluetooth/serialhcibus/WDK/SerialBusWdk.inx +++ b/bluetooth/serialhcibus/WDK/SerialBusWdk.inx @@ -20,7 +20,7 @@ Signature="$WINDOWS NT$" Class=System ClassGuid={4D36E97D-E325-11CE-BFC1-08002BE10318} -Provider=%MSFTSAMPLE% +Provider=%ProviderString% DriverVer=06/21/2006,6.2.7923.0 CatalogFile=SerialBusWdk.cat @@ -81,7 +81,7 @@ ServiceBinary = %12%\SerialBusWdk.sys SPSVCINST_ASSOCSERVICE= 0x00000002 ;Localizable -MSFTSAMPLE = "Microsoft Sample" +ProviderString = "TODO-Set-Provider" StdMfg = "(Standard system devices)" DiskId1 = "Serial Bus Driver over UART Installation Disk #1" Bus.DeviceDesc = "Serial Bus Driver over UART Bus Enumerator" diff --git a/bluetooth/serialhcibus/WDK/SerialBusWdk.vcxproj b/bluetooth/serialhcibus/WDK/SerialBusWdk.vcxproj index 1b1088b19..79bbcc831 100644 --- a/bluetooth/serialhcibus/WDK/SerialBusWdk.vcxproj +++ b/bluetooth/serialhcibus/WDK/SerialBusWdk.vcxproj @@ -19,12 +19,12 @@ - {186E63FF-9F2F-4FF6-82CB-FBAD7BA24089} + {6844B626-ABF4-4F07-BD90-7AA8A8A87E12} $(MSBuildProjectName) 1 Debug Win32 - {B81CFC73-D7CD-484D-B134-1D6AC0106514} + {F1C3593E-8279-482B-8D67-8319913E265E} @@ -196,7 +196,6 @@ - diff --git a/bluetooth/serialhcibus/WDK/SerialBusWdk.vcxproj.Filters b/bluetooth/serialhcibus/WDK/SerialBusWdk.vcxproj.Filters index 90e9b2a75..7d476dc7f 100644 --- a/bluetooth/serialhcibus/WDK/SerialBusWdk.vcxproj.Filters +++ b/bluetooth/serialhcibus/WDK/SerialBusWdk.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {201450EB-D675-4E41-BAAC-8BF2E93F8BA4} + {413E2A31-6D3D-48D5-B3C7-73714F810CAF} h;hpp;hxx;hm;inl;inc;xsd - {28214B85-07EC-4DAA-9EE8-7ECB4867524B} + {1494E901-78AF-4262-8B85-C70B93AA8465} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {CCE43425-7067-4FF7-A1FA-B55E892E4CF6} + {38B6E2D0-6448-48E5-B1F7-BD648B4C0112} inf;inv;inx;mof;mc; - {678E1E03-C75C-4BFD-A872-84A5F2191188} + {62F3F5F6-E755-4AFE-9C33-C1E065E5BAF1} @@ -36,9 +36,6 @@ - - Driver Files - Driver Files diff --git a/bluetooth/serialhcibus/driver.h b/bluetooth/serialhcibus/driver.h index d82f1f556..4d9deccaa 100644 --- a/bluetooth/serialhcibus/driver.h +++ b/bluetooth/serialhcibus/driver.h @@ -8,7 +8,7 @@ Module Name: Abstract: - This module contains the common private declarations for + This module contains the common private declarations for for the Serial HCI bus driver. Environment: @@ -32,7 +32,7 @@ Module Name: #include // BT Extensible Transport DDI -#include "device.h" // Device specific +#include "device.h" // Device specific #include "io.h" // Read pump #include "debugdef.h" // WPP trace #include "public.h" // Share between driver and application @@ -47,7 +47,7 @@ DEFINE_GUID(GUID_CONTAINERID_INTERNALLY_CONNECTED_DEVICE, //{00000000-0000-0000-ffff-ffffffffffff} -#endif // #ifdef DEFINE_GUID +#endif // #ifdef DEFINE_GUID // // Define HCI event code @@ -71,7 +71,6 @@ DEFINE_GUID(GUID_CONTAINERID_INTERNALLY_CONNECTED_DEVICE, #define BLUETOOTH_FUNC_IDS 0x1001 - // // Device's idle state capability // @@ -81,7 +80,6 @@ typedef enum _IDLE_CAP_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 @@ -123,15 +121,13 @@ typedef struct _PDO_IDENTIFICATION_DESCRIPTION } 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 +// Bus driver's FDO (Function Device Object) extension structure used to maintain device // properties and state. // - typedef struct _FDO_EXTENSION { WDFWAITLOCK ChildLock; @@ -149,20 +145,20 @@ typedef struct _FDO_EXTENSION // // Serial port IO Target where we send IOCTL/READ/WRITE reuquest to // - WDFIOTARGET IoTargetSerial; + WDFIOTARGET IoTargetSerial; // - // (optional) GPIO IO Target to enable serial bus device + // (optional) GPIO IO Target to enable serial bus device // - WDFIOTARGET IoTargetGPIO; + WDFIOTARGET IoTargetGPIO; // // Bluetooth child dev node (PDO) capabilities // - BTHX_CAPABILITIES BthXCaps; + BTHX_CAPABILITIES BthXCaps; // - // Indicator if UART is properly initialize; may require re-inialization + // Indicator if UART is properly initialize; may require re-inialization // when tranistion from exiting D0 to resume D0. // BOOLEAN DeviceInitialized; @@ -176,10 +172,10 @@ typedef struct _FDO_EXTENSION // Cached I2C controller connection IDs // LARGE_INTEGER I2CConnectionId; - + // // Cached GPIO controller connection IDs - // + // LARGE_INTEGER GPIOConnectionId; // @@ -190,7 +186,7 @@ typedef struct _FDO_EXTENSION // // Preallocate WDF Requests to wait on serial error event // - WDFREQUEST RequestWaitOnError; + WDFREQUEST RequestWaitOnError; // // Data return from serial event wait mask IOCTL @@ -200,42 +196,42 @@ typedef struct _FDO_EXTENSION // // WDM memory use for Wait Mask event // - WDFMEMORY WaitMaskMemory; + 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; + BOOLEAN ReadPumpRunning; // - // Track number of out-of-sync error that has been detected + // Track number of out-of-sync error that has been detected // ULONG OutOfSyncErrorCount; // // Locks for synchronization for list and queue // - KSPIN_LOCK QueueAccessLock; - + WDFSPINLOCK QueueAccessLock; + // // Track next packet read (one and only one) // - UART_READ_CONTEXT ReadContext; + UART_READ_CONTEXT ReadContext; - // - // Preallocated local WDF requested and memory object that is reused to + // + // 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]; + WDFREQUEST ReadRequest; + WDFMEMORY ReadMemory; + UCHAR ReadBuffer[MAX_H4_HCI_PACKET_SIZE]; -#if DBG +#if DBG // // Track last completed HCI packet // @@ -245,57 +241,52 @@ typedef struct _FDO_EXTENSION // // WDF Queue for HCI event Request and total number of such request recevied // - WDFQUEUE ReadEventQueue; - LONG EventQueueCount; + WDFQUEUE ReadEventQueue; + LONG EventQueueCount; // // List to store (prefetched) incoming HCI events and number of entries // - LIST_ENTRY ReadEventList; - LONG EventListCount; - + LIST_ENTRY ReadEventList; + LONG EventListCount; // // WDF Queue for HCI read data Request and total number of such request recevied // - WDFQUEUE ReadDataQueue; - LONG DataQueueCount; + WDFQUEUE ReadDataQueue; + LONG DataQueueCount; // // List to store (prefetched) incoming HCI data and number of entries - // - LIST_ENTRY ReadDataList; - LONG DataListCount; + // + 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 + // 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 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 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 + 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. @@ -317,12 +308,10 @@ typedef struct _PDO_EXTENSION WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(PDO_EXTENSION, PdoGetExtension) - // // Prototypes of functions // - // // Driver.c // @@ -338,7 +327,6 @@ EVT_WDF_DRIVER_DEVICE_ADD DriverDeviceAdd; DRIVER_INITIALIZE DriverEntry; - // // FDO.c // @@ -357,7 +345,7 @@ FdoWriteToDeviceSync(_In_ WDFIOTARGET _IoTargetSerial, _In_ WDFREQUEST _RequestWriteSync, _In_ ULONG _IoControlCode, _In_opt_ ULONG _InBufferSize, - _In_opt_ PVOID _InBuffer, + _In_opt_ PVOID _InBuffer, _Out_ PULONG_PTR _BytesWritten); NTSTATUS @@ -367,12 +355,10 @@ DeviceConfigWaitOnError(_In_ WDFIOTARGET _IoTargetSerial, _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; @@ -432,7 +418,6 @@ FdoFindConnectResources(_In_ WDFDEVICE _Device, _In_ WDFCMRESLIST _ResourcesRaw, _In_ WDFCMRESLIST _ResourcesTranslated); - // // Pdo.c // @@ -468,7 +453,7 @@ HLP_AllocateResourceForWrite(_In_ WDFDEVICE _Device, _In_ WDFIOTARGET _IoTargetSerial, _Out_ WDFREQUEST *_pRequest); -VOID +VOID HLP_FreeResourceForWrite(PUART_WRITE_CONTEXT _TransferContext); EVT_WDF_REQUEST_CANCEL CB_RequestFromBthportCancel; @@ -479,9 +464,9 @@ NTSTATUS ReadRequestComplete(_In_ PFDO_EXTENSION _FdoExtension, _In_ UCHAR _Type, _In_ ULONG _PacketLength, - _In_reads_bytes_opt_(_PacketLength) PUCHAR _Packet, + _In_reads_bytes_opt_(_PacketLength) PUCHAR _Packet, _Inout_ WDFQUEUE _Queue, - _Inout_ PLONG _QueueCount, + _Inout_ PLONG _QueueCount, _Inout_ PLIST_ENTRY _ListHead, _Inout_ PLONG _ListCount); @@ -494,7 +479,6 @@ ReadH4Packet(_In_ PUART_READ_CONTEXT _ReadContext, _Pre_notnull_ _Pre_writable_byte_size_(_BufferLen) PVOID _Buffer, _In_ ULONG _BufferLen); - // // Device.c // @@ -518,13 +502,12 @@ NTSTATUS DeviceEnable(_In_ WDFDEVICE _Device, _In_ BOOLEAN _Enabled); -NTSTATUS +NTSTATUS DevicePowerOn(_In_ WDFDEVICE _Device); -NTSTATUS +NTSTATUS DevicePowerOff(_In_ WDFDEVICE _Device); #endif - diff --git a/bluetooth/serialhcibus/io.c b/bluetooth/serialhcibus/io.c index 0b4d64ac4..c0a771424 100644 --- a/bluetooth/serialhcibus/io.c +++ b/bluetooth/serialhcibus/io.c @@ -14,7 +14,7 @@ Module Name: Kernel mode only -Revision History: +Revision History: --*/ @@ -34,23 +34,23 @@ CB_RequestFromBthportCancel( Routine Description: - Request from upper layer that driver owns is being canceled. Its associated + 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. + 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 + 2. Cancellation routine is invoked while lower Request is pending. The lower request could be completed either - a. Synchronously - completion routine is invoked before + 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 + 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 @@ -59,24 +59,22 @@ Return Value: none ---*/ -{ +--*/ +{ PUART_WRITE_CONTEXT TransferContext; WDFREQUEST RequestToUART; - WDFMEMORY Memory; + 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; + // 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. @@ -97,23 +95,22 @@ Return Value: if (REQUEST_PATH_NONE == CompletePath) { - DoTrace(LEVEL_WARNING, TFLAG_IO, (" >CancelSentRequest(%p) to IO Target", RequestToUART)); + 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, @@ -216,53 +211,53 @@ CR_WriteDeviceIO( 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 + 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 + 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. - + 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 + - Dereference(RequestUART) - will not be accessed by cancellation function - Complete(RequestFromUpper) & Delete(its Memory Object) - + - Delete(RequestUART) - - Dereference(RequestFromUpper) - + - 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) - + - 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 + + 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. + - completion function - right before it exits. Arguments: @@ -270,30 +265,30 @@ Routine Description: _Target - WDF IO Target _Params - Completion parameters _Context - Context used to process this request - + Return Value: none - + --*/ { NTSTATUS Status; - PUART_WRITE_CONTEXT TransferContext; + PUART_WRITE_CONTEXT TransferContext; PFDO_EXTENSION FdoExtension; WDFREQUEST RequestFromBthport; - ULONG BytesDataWritten = 0; - LONG CompletePath = REQUEST_PATH_NONE; + ULONG BytesDataWritten = 0; + LONG CompletePath = REQUEST_PATH_NONE; - UNREFERENCED_PARAMETER(_Target); + UNREFERENCED_PARAMETER(_Target); Status = _Params->IoStatus.Status; - TransferContext = (PUART_WRITE_CONTEXT) _Context; + 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!"); - 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. // @@ -314,12 +309,12 @@ Return Value: // 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 { // @@ -354,10 +349,10 @@ Return Value: } 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 // @@ -365,85 +360,84 @@ Return Value: { WDFMEMORY ReqOutMemory = NULL; ULONG BytesWritten; - PULONG OutBuffer = NULL; - size_t OutBufferSize = 0; + PULONG OutBuffer = NULL; + size_t OutBufferSize = 0; - BytesWritten = (ULONG) _Params->Parameters.Write.Length; + BytesWritten = (ULONG) _Params->Parameters.Write.Length; - DoTrace(LEVEL_INFO, TFLAG_DATA,(" Packet: Type %d, DataLen %d, BytesWritten %d", + DoTrace(LEVEL_INFO, TFLAG_DATA,(" Packet: Type %d, DataLen %d, BytesWritten %d", TransferContext->HCIContext->Type, TransferContext->HCIContext->DataLen, - BytesWritten)); + 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)) - { + Status = WdfRequestRetrieveOutputMemory(RequestFromBthport, &ReqOutMemory); + if (NT_SUCCESS(Status)) + { OutBuffer = (PULONG) WdfMemoryGetBuffer(ReqOutMemory, &OutBufferSize); if (OutBufferSize >= sizeof(ULONG)) - { - // Set OutputParameter value and its size + { + // Set OutputParameter value and its size *OutBuffer = TransferContext->HCIContext->DataLen; - BytesDataWritten = sizeof(ULONG); + BytesDataWritten = sizeof(ULONG); } - } + } } - else + else { // Return the status as is. - } + } } } -Done: +Done: if (REQUEST_PATH_NONE == CompletePath) - { + { // Increment the completion count based on packet type. - FdoExtension = TransferContext->FdoExtension; - + FdoExtension = TransferContext->FdoExtension; + if (TransferContext->HCIContext->Type == (UCHAR) HciPacketCommand) { InterlockedIncrement(&FdoExtension->CntCommandCompleted); } else if (TransferContext->HCIContext->Type == (UCHAR) HciPacketAclData) { - InterlockedIncrement(&FdoExtension->CntWriteDataCompleted); - } + InterlockedIncrement(&FdoExtension->CntWriteDataCompleted); + } DoTrace(LEVEL_INFO, TFLAG_IO,(" WriteDeviceIO: Request %p complete with %!STATUS! and %d BytesDataWritten", - RequestFromBthport, Status, BytesDataWritten)); + RequestFromBthport, Status, BytesDataWritten)); // Delete this memory object that is no longer needed. - WdfObjectDelete(TransferContext->Memory); + WdfObjectDelete(TransferContext->Memory); + + // Cannot access this Request and its context after it is completed. + WdfRequestCompleteWithInformation(RequestFromBthport, Status, BytesDataWritten); - // Cannot access this Request and its context after it is completed. - WdfRequestCompleteWithInformation(RequestFromBthport, Status, BytesDataWritten); - } - - // Delete this request in its completion function. - WdfObjectDelete(_Request); + + // 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")); + DoTrace(LEVEL_INFO, TFLAG_IO,("-CR_WriteDeviceIO")); } - VOID ReadSegmentStateSet( PUART_READ_CONTEXT _ReadContext, @@ -453,52 +447,52 @@ ReadSegmentStateSet( Routine Description: - This helper centralize the setting of read state. It can be used to detect - possible incorrect state transition. + 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", + OldState == GET_PKT_HEADER ? "Header" : + OldState == GET_PKT_PAYLOAD ? "Payload" : "Unknown", _NewState == GET_PKT_TYPE ? "Type" : - _NewState == GET_PKT_HEADER ? "Header" : + _NewState == GET_PKT_HEADER ? "Header" : _NewState == GET_PKT_PAYLOAD ? "Payload" : "Unknown" )); // Validate the state transition switch (_NewState) { - case GET_PKT_TYPE: + 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; + break; case GET_PKT_HEADER: case GET_PKT_PAYLOAD: // Reset segment count _ReadContext->BytesReadNextSegment = 0; - break; + break; } - + _ReadContext->ReadSegmentState = _NewState; } // Full packet: match to a Request and complete it. -NTSTATUS +NTSTATUS ReadH4PacketComplete( PFDO_EXTENSION _FdoExtension, UCHAR _Type, @@ -508,44 +502,43 @@ ReadH4PacketComplete( { NTSTATUS Status = STATUS_SUCCESS; - DoTrace(LEVEL_INFO, TFLAG_IO, ("+ReadH4PacketComplete %S Packet Length %d", - _Type == (UCHAR) HciPacketEvent ? L"Event" : L"AclData", _BufferLength )); + DoTrace(LEVEL_INFO, TFLAG_IO, ("+ReadH4PacketComplete %S Packet Length %d", + _Type == (UCHAR) HciPacketEvent ? L"Event" : L"AclData", _BufferLength )); #if DBG - // Tracking last completed packet + // Tracking last completed packet RtlCopyMemory(_FdoExtension->LastPacket, _Buffer, _BufferLength); _FdoExtension->LastPacketLength = _BufferLength; -#endif - +#endif + if (_Type == (UCHAR) HciPacketEvent) { - ReadRequestComplete(_FdoExtension, - HciPacketEvent, + ReadRequestComplete(_FdoExtension, + HciPacketEvent, _BufferLength, _Buffer, _FdoExtension->ReadEventQueue, &_FdoExtension->EventQueueCount, - &_FdoExtension->ReadEventList, - &_FdoExtension->EventListCount); + &_FdoExtension->ReadEventList, + &_FdoExtension->EventListCount); } - else + else { - ReadRequestComplete(_FdoExtension, + ReadRequestComplete(_FdoExtension, HciPacketAclData, _BufferLength, _Buffer, _FdoExtension->ReadDataQueue, &_FdoExtension->DataQueueCount, - &_FdoExtension->ReadDataList, - &_FdoExtension->DataListCount); - } + &_FdoExtension->ReadDataList, + &_FdoExtension->DataListCount); + } DoTrace(LEVEL_INFO, TFLAG_IO, ("-ReadH4PacketComplete %!STATUS!", Status)); - + return Status; } - NTSTATUS ReadH4PacketReassemble( _Inout_ PUART_READ_CONTEXT _ReadContext, @@ -556,45 +549,44 @@ ReadH4PacketReassemble( Routine Description: - A function enforce a state machine to process reading data to form a - complete HCI packet. + 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; + NTSTATUS Status = STATUS_SUCCESS; ULONG BytesRemained = _BytesRead; PUCHAR Buffer = _Buffer; - PFDO_EXTENSION FdoExtension = _ReadContext->FdoExtension; + 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)); - // + 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. + // - 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). + // * 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 // @@ -603,210 +595,210 @@ Return Value: // ByteToRead = DataLength // Second read // - Event/AclData - // Complete (5 + BytesToRead) - // + // Complete (5 + BytesToRead) + // while (NT_SUCCESS(Status) && BytesRemained > 0) { - + // Process read buffer based on its read state switch (_ReadContext->ReadSegmentState) { - case GET_PKT_TYPE: + 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] ---------- ")); + DoTrace(LEVEL_INFO, TFLAG_IO, (" [Event] ---------- ")); _ReadContext->BytesToRead4FullPacket = HCI_EVENT_HEADER_SIZE; } - else if (H4Packet->Type == (UCHAR) HciPacketAclData) { - DoTrace(LEVEL_INFO, TFLAG_IO, (" [AclData] ---------- ")); + 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. + // 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; + 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); + _ReadContext->H4Packet.Type = H4Packet->Type; // Valid packet type is cached. + ReadSegmentStateSet(_ReadContext, GET_PKT_HEADER); break; - - case GET_PKT_HEADER: + + 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); + 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)); + 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; + 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); + (PUCHAR) &_ReadContext->H4Packet.Packet.Event, + PacketLen); // Read next packet - ReadSegmentStateSet(_ReadContext, GET_PKT_TYPE); + ReadSegmentStateSet(_ReadContext, GET_PKT_TYPE); } // Read the remainder of a full (Event) packet else { - if (BytesRemained < _ReadContext->H4Packet.Packet.Event.ParamsCount) { - _ReadContext->BytesToRead4FullPacket = + 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); + 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 - } + 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 + 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); + 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 = + 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); - } - } + ReadSegmentStateSet(_ReadContext, GET_PKT_PAYLOAD); + } + } break; - + case GET_PKT_PAYLOAD: - if (_ReadContext->H4Packet.Type == (UCHAR) HciPacketEvent) { - + 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, + 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); - + 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; + PacketLen = HCI_EVENT_HEADER_LEN + _ReadContext->H4Packet.Packet.Event.ParamsCount; Status = ReadH4PacketComplete(FdoExtension, _ReadContext->H4Packet.Type, - (PUCHAR) &_ReadContext->H4Packet.Packet.Event, - PacketLen); + (PUCHAR) &_ReadContext->H4Packet.Packet.Event, + PacketLen); // Read next packet - ReadSegmentStateSet(_ReadContext, GET_PKT_TYPE); + ReadSegmentStateSet(_ReadContext, GET_PKT_TYPE); } else { // Partial packet - RtlCopyMemory(&_ReadContext->H4Packet.Packet.Event.Params[_ReadContext->BytesReadNextSegment], - Buffer, + RtlCopyMemory(&_ReadContext->H4Packet.Packet.Event.Params[_ReadContext->BytesReadNextSegment], + Buffer, BytesRemained); - DoTrace(LEVEL_INFO, TFLAG_IO, (" [Event] Payload[%d + %d] = Partial; %d to read", + 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); - + BytesToRead - BytesRemained)); + _ReadContext->BytesReadNextSegment += BytesRemained; + BUFFER_AND_SIZE_ADJUSTED(Buffer, BytesRemained, _ReadContext->BytesReadNextSegment, BytesRemained); + // Remaining event params to read - _ReadContext->BytesToRead4FullPacket = + _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", + 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; + 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, + RtlCopyMemory(&_ReadContext->H4Packet.Packet.AclData.Data[_ReadContext->BytesReadNextSegment], + Buffer, BytesToRead); - DoTrace(LEVEL_INFO, TFLAG_IO, (" [AclData] Payload[%d + %d] = FULL", + DoTrace(LEVEL_INFO, TFLAG_IO, (" [AclData] Payload[%d + %d] = FULL", _ReadContext->BytesReadNextSegment, - BytesToRead)); - BUFFER_AND_SIZE_ADJUSTED(Buffer, BytesRemained, _ReadContext->BytesReadNextSegment, BytesToRead); - + 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; + PacketLen = HCI_ACLDATA_HEADER_LEN + _ReadContext->H4Packet.Packet.AclData.DataLength; Status = ReadH4PacketComplete(FdoExtension, _ReadContext->H4Packet.Type, - (PUCHAR) &_ReadContext->H4Packet.Packet.AclData, - PacketLen); + (PUCHAR) &_ReadContext->H4Packet.Packet.AclData, + PacketLen); // Next packet - ReadSegmentStateSet(_ReadContext, GET_PKT_TYPE); + ReadSegmentStateSet(_ReadContext, GET_PKT_TYPE); } else { // Process partial packet - RtlCopyMemory(&_ReadContext->H4Packet.Packet.AclData.Data[_ReadContext->BytesReadNextSegment], - Buffer, + RtlCopyMemory(&_ReadContext->H4Packet.Packet.AclData.Data[_ReadContext->BytesReadNextSegment], + Buffer, BytesRemained); - DoTrace(LEVEL_INFO, TFLAG_IO, (" [AclData] Payload[%d + %d] = Partial; %d to read", + 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); - + 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; - + _ReadContext->BytesToRead4FullPacket = + _ReadContext->H4Packet.Packet.AclData.DataLength - _ReadContext->BytesReadNextSegment; + } + } + break; + default: DoTrace(LEVEL_ERROR, TFLAG_IO, (" Unknown ReadSegmentState")); break; @@ -814,15 +806,14 @@ Return Value: } return Status; - + OutOfSync: DoTrace(LEVEL_ERROR, TFLAG_IO, (" Out-of-sync error detected in ProcessReadBuffer() %!STATUS!", Status)); - + return Status; } - VOID ReadH4PacketCompletionRoutine( _In_ WDFREQUEST _Request, @@ -834,8 +825,8 @@ ReadH4PacketCompletionRoutine( 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 + 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: @@ -844,14 +835,14 @@ Routine Description: _Target - WDF IO Target _Params - Completion parameters _Context - Context of this request - + Return Value: none - ---*/ + +--*/ { - NTSTATUS Status; + NTSTATUS Status; PUART_READ_CONTEXT ReadContext; PFDO_EXTENSION FdoExtension; ULONG BytesRead; @@ -859,143 +850,141 @@ Return Value: PUCHAR OutBuffer; size_t OutBufferSize; READ_REQUEST_STATE PreviousState; - - UNREFERENCED_PARAMETER(_Request); - UNREFERENCED_PARAMETER(_Target); + UNREFERENCED_PARAMETER(_Request); + UNREFERENCED_PARAMETER(_Target); - // Operation result + // 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. + + // Set to REQUEST_COMPLETE if skip REQUEST_PENDING state. PreviousState = InterlockedCompareExchange((PLONG)&ReadContext->RequestState, REQUEST_COMPLETE, - REQUEST_SENT); + REQUEST_SENT); + + DoTrace(LEVEL_WARNING, TFLAG_DATA, ("+ReadH4PacketCompletionRoutine %!STATUS! %d BytesRead %S)", + Status, BytesRead, PreviousState == REQUEST_PENDING ? L"Async" : L"*Sync*")); - DoTrace(LEVEL_WARNING, TFLAG_DATA, ("+ReadH4PacketCompletionRoutine %!STATUS! %d BytesRead %S)", - Status, BytesRead, PreviousState == REQUEST_PENDING ? L"Async" : L"*Sync*")); + FdoExtension = (PFDO_EXTENSION) ReadContext->FdoExtension; - FdoExtension = (PFDO_EXTENSION) ReadContext->FdoExtension; - // - // The return status can either be - // - successful (buffer completely filled), + // The return status can either be + // - successful (buffer completely filled), // - timeout (buffer not completed filled prior to interval timeout expired - // - cancellation + // - cancellation // - failure // if (NT_SUCCESS(Status) || Status == STATUS_IO_TIMEOUT || Status == STATUS_TIMEOUT) { - // Continue to process + // 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). - // + // 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); + OutBuffer = (PUCHAR) WdfMemoryGetBuffer(ReadMemory, &OutBufferSize); NT_ASSERT(OutBufferSize >= BytesRead); - DoTrace(LEVEL_INFO, TFLAG_IO, (" ReadH4PacketCompletionRoutine %d BytesRead pBuffer %p", BytesRead, OutBuffer)); - + 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); + 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++; + 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, + // 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 + // 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); + + WdfSpinLockAcquire(FdoExtension->QueueAccessLock); // 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); + Status = WdfIoQueueRetrieveNextRequest(FdoExtension->ReadDataQueue, &Request); } - KeReleaseSpinLock(&FdoExtension->QueueAccessLock, Irql); + WdfSpinLockRelease(FdoExtension->QueueAccessLock); - if (NT_SUCCESS(Status)) { - DoTrace(LEVEL_ERROR, TFLAG_IO, (" Complete a request with STATUS_DEVICE_DATA_ERROR")); - WdfRequestComplete(Request, STATUS_DEVICE_DATA_ERROR); + 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; + goto Exit; } else { DoTrace(LEVEL_ERROR, TFLAG_IO, (" Detect out-of-sync error but read ahead...")); - + // Reset hardware error. - FdoExtension->HardwareErrorDetected = FALSE; + FdoExtension->HardwareErrorDetected = FALSE; - // try next - goto ReadNext; - } - } + // try next + goto ReadNext; + } + } } else { NT_ASSERT(Status == STATUS_TIMEOUT); } -ReadNext: +ReadNext: - if (PreviousState == REQUEST_PENDING) - { + if (PreviousState == REQUEST_PENDING) + { ULONG BytesToRead; // @@ -1006,9 +995,9 @@ Return Value: 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. + + // 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, @@ -1017,27 +1006,26 @@ Return Value: } else { - // Fall through and leave this fucntion if this request was completed synchronously; + // 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)")); + DoTrace(LEVEL_INFO, TFLAG_IO, ("-CR_ReadReadIO (fall though)")); return; Exit: - + if (!NT_SUCCESS(Status)) - { - NT_ASSERT(Status == STATUS_CANCELLED); + { + NT_ASSERT(Status == STATUS_CANCELLED); FdoExtension->ReadPumpRunning = FALSE; - DoTrace(LEVEL_WARNING, TFLAG_IO, (" Pump has stopped!")); - } + DoTrace(LEVEL_WARNING, TFLAG_IO, (" Pump has stopped!")); + } - DoTrace(LEVEL_INFO, TFLAG_IO, ("-CR_ReadReadIO (error)")); + DoTrace(LEVEL_INFO, TFLAG_IO, ("-CR_ReadReadIO (error)")); } - NTSTATUS ReadH4Packet( _In_ PUART_READ_CONTEXT _ReadContext, @@ -1059,17 +1047,16 @@ Routine Description: Return Value: NTSTATUS - ---*/ -{ - PFDO_EXTENSION FdoExtension; - WDF_REQUEST_REUSE_PARAMS RequestReuseParams; - NTSTATUS Status; +--*/ +{ + PFDO_EXTENSION FdoExtension; + WDF_REQUEST_REUSE_PARAMS RequestReuseParams; + NTSTATUS Status; DoTrace(LEVEL_INFO, TFLAG_IO, ("+ReadH4Packet")); - FdoExtension = _ReadContext->FdoExtension; + FdoExtension = _ReadContext->FdoExtension; if (0 == _BufferLen) { DoTrace(LEVEL_ERROR, TFLAG_IO, (" ReadH4Packet: _BufferLen cannot be 0")); @@ -1078,10 +1065,10 @@ Return Value: } while (TRUE) { - + DoTrace(LEVEL_INFO, TFLAG_IO, (" ReadH4Packet - ")); - NT_ASSERT(_ReadContext->RequestState != REQUEST_SENT); - + 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)); @@ -1091,50 +1078,50 @@ Return Value: // // Issue a read event request // - WDF_REQUEST_REUSE_PARAMS_INIT(&RequestReuseParams, WDF_REQUEST_REUSE_NO_FLAGS, STATUS_SUCCESS); + 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)); + 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); - + _ReadContext); + InterlockedExchange((PLONG)&_ReadContext->RequestState, REQUEST_SENT); - if (FALSE == WdfRequestSend(_WdfRequest, + 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! + // 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. @@ -1143,22 +1130,22 @@ Return Value: REQUEST_SENT); DoTrace(LEVEL_WARNING, TFLAG_IO, (" WdfRequestSend ReqState: %d -> %d", - PreviousState, _ReadContext->RequestState)); + PreviousState, _ReadContext->RequestState)); - if (PreviousState == REQUEST_SENT) + if (PreviousState == REQUEST_SENT) { - // Request is still pending, and will be completed asychronously in the + // Request is still pending, and will be completed asychronously in the // completion routine where it can issue next read. - Status = STATUS_PENDING; + Status = STATUS_PENDING; break; } - else + 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. + // Previous request has been complete synchronously in the + // completion routine; do next read in this function. } else { @@ -1173,31 +1160,31 @@ Return Value: if (!NT_SUCCESS(Status)) { - NT_ASSERT(Status == STATUS_CANCELLED); + NT_ASSERT(Status == STATUS_CANCELLED); FdoExtension->ReadPumpRunning = FALSE; - } - + } + DoTrace(LEVEL_INFO, TFLAG_IO, ("-ReadH4Packet %!STATUS!", Status)); return Status; } -__inline +__inline PHCI_PACKET_ENTRY HLP_CreatePacketEntry( - _In_ ULONG _PacketLength, + _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); + RtlCopyMemory(PacketEntry->Packet, _Packet, _PacketLength); PacketEntry->PacketLen = _PacketLength; } - + return PacketEntry; } @@ -1205,17 +1192,17 @@ NTSTATUS ReadRequestComplete( _In_ PFDO_EXTENSION _FdoExtension, _In_ UCHAR _PacketType, - _In_ ULONG _PacketLength, - _In_reads_bytes_opt_(_PacketLength) PUCHAR _Packet, + _In_ ULONG _PacketLength, + _In_reads_bytes_opt_(_PacketLength) PUCHAR _Packet, _Inout_ WDFQUEUE _Queue, - _Inout_ PLONG _QueueCount, + _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 + 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: @@ -1229,9 +1216,8 @@ Return Value: NTSTATUS - STATUS_SUCCESS Or STATUS_INSUFFICIENT_RESOURCE ---*/ +--*/ { - KIRQL Irql; WDFREQUEST Request = NULL; NTSTATUS Status = STATUS_SUCCESS; PHCI_PACKET_ENTRY PacketEntry = NULL; @@ -1240,29 +1226,28 @@ Return Value: PBTHX_HCI_READ_WRITE_CONTEXT HCIContext; BOOLEAN CompleteRequest = FALSE; - DoTrace(LEVEL_INFO, TFLAG_IO, ("+ReadRequestComplete")); + 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); + WdfSpinLockAcquire(_FdoExtension->QueueAccessLock); if (_Packet) { - - Status = WdfIoQueueRetrieveNextRequest(_Queue, &Request); + + 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)); + 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)); } @@ -1272,31 +1257,31 @@ Return Value: if (PacketEntry == NULL) { // Error condition Status = STATUS_INSUFFICIENT_RESOURCES; - DoTrace(LEVEL_ERROR, TFLAG_IO, (" (C0/Error) Could not allocate HCI_PACKET_ENTRY %!STATUS!", Status)); + 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)); + DoTrace(LEVEL_INFO, TFLAG_IO, (" (C0) Queuing packet with list count %d", *_ListCount)); } } - } - else { - if (!IsListEmpty(_ListHead)) { - Status = WdfIoQueueRetrieveNextRequest(_Queue, &Request); + } + 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); - + 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)); + _PacketLength = PacketEntry->PacketLen; + InterlockedDecrement(_ListCount); + + DoTrace(LEVEL_INFO, TFLAG_IO, (" (C2) Complete a request %p, _Packet %p, _PacketLength %d", + Request, _Packet, _PacketLength)); CompleteRequest = TRUE; } @@ -1308,18 +1293,18 @@ Return Value: // 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); - + WdfSpinLockRelease(_FdoExtension->QueueAccessLock); + if (!CompleteRequest) { goto Done; - } - - // Complete this request - Status = WdfRequestRetrieveOutputMemory(Request, &ReqOutMemory); - if (Status != STATUS_SUCCESS) { + } + + // 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; @@ -1328,22 +1313,22 @@ Return Value: 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); - + // 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; + HCIContext->DataLen = _PacketLength; if (BytesToReturn <= BufferSize) { - RtlCopyMemory(&HCIContext->Data, _Packet, _PacketLength); + RtlCopyMemory(&HCIContext->Data, _Packet, _PacketLength); } else { - Status = STATUS_BUFFER_TOO_SMALL; + Status = STATUS_BUFFER_TOO_SMALL; BytesToReturn = 0; } - + // Validate and print out (WPP) HCI packet info - HCIContextValidate(HCIContext->Type == (UCHAR) HciPacketEvent ? + HCIContextValidate(HCIContext->Type == (UCHAR) HciPacketEvent ? _FdoExtension->CntEventCompleted : _FdoExtension->CntReadDataCompleted, HCIContext); @@ -1351,36 +1336,35 @@ Return Value: // Release memory allocated for a completed packet entry; it was not removed from the packet list. // if (PacketEntry) { - ExFreePool(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)); + _FdoExtension->CntEventCompleted, Status, (ULONG) BytesToReturn)); } else if (HCIContext->Type == (UCHAR) HciPacketAclData) { - InterlockedIncrement(&_FdoExtension->CntReadDataCompleted); + InterlockedIncrement(&_FdoExtension->CntReadDataCompleted); DoTrace(LEVEL_INFO, TFLAG_DATA, (" [%d] HciPacketAclData completing %!STATUS!, %d BytesToReturn", - _FdoExtension->CntReadDataCompleted, Status, (ULONG) BytesToReturn)); - } + _FdoExtension->CntReadDataCompleted, Status, (ULONG) BytesToReturn)); + } DoTrace(LEVEL_INFO, TFLAG_IO, (" Completing Request(%p) %!STATUS!, %d BytesToReturn", - Request, Status, (ULONG) 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)); + WdfRequestCompleteWithInformation(Request, Status, BytesToReturn); + +Done: + + DoTrace(LEVEL_INFO, TFLAG_IO, ("-ReadRequestComplete: %!STATUS!", Status)); return Status; } - VOID ReadResourcesFree( _In_ WDFDEVICE _Device @@ -1399,35 +1383,34 @@ 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 + // 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 + // WdfIoQueueRetrieveNextRequest() returns STATUS_WDF_PAUSED since this // function is invoked after entered D0. // // // Free resources allocated earlier // - - while(!IsListEmpty(&FdoExtension->ReadEventList)) + + while(!IsListEmpty(&FdoExtension->ReadEventList)) { - PHCI_PACKET_ENTRY PacketEntry; + PHCI_PACKET_ENTRY PacketEntry; - KeAcquireSpinLock(&FdoExtension->QueueAccessLock, &Irql); + WdfSpinLockAcquire(FdoExtension->QueueAccessLock); PacketEntry = (PHCI_PACKET_ENTRY)RemoveHeadList(&FdoExtension->ReadEventList); - InterlockedDecrement(&FdoExtension->EventListCount); - KeReleaseSpinLock(&FdoExtension->QueueAccessLock, Irql); - + InterlockedDecrement(&FdoExtension->EventListCount); + WdfSpinLockRelease(FdoExtension->QueueAccessLock); + if (PacketEntry) { ExFreePool(PacketEntry); @@ -1436,31 +1419,30 @@ Return } NT_ASSERT(FdoExtension->EventListCount == 0); - while(!IsListEmpty(&FdoExtension->ReadDataList)) + while(!IsListEmpty(&FdoExtension->ReadDataList)) { - PHCI_PACKET_ENTRY PacketEntry; + PHCI_PACKET_ENTRY PacketEntry; - KeAcquireSpinLock(&FdoExtension->QueueAccessLock, &Irql); + WdfSpinLockAcquire(FdoExtension->QueueAccessLock); PacketEntry = (PHCI_PACKET_ENTRY)RemoveHeadList(&FdoExtension->ReadDataList); - InterlockedDecrement(&FdoExtension->DataListCount); - KeReleaseSpinLock(&FdoExtension->QueueAccessLock, Irql); - + InterlockedDecrement(&FdoExtension->DataListCount); + WdfSpinLockRelease(FdoExtension->QueueAccessLock); + 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 @@ -1479,21 +1461,20 @@ 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")); + 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 + // 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); @@ -1502,77 +1483,74 @@ Return Value: WDF_NO_OBJECT_ATTRIBUTES, &FdoExtension->ReadEventQueue); - if (!NT_SUCCESS(Status)) + if (!NT_SUCCESS(Status)) { DoTrace(LEVEL_ERROR, TFLAG_IO, (" WdfIoQueueCreate(Event) %!STATUS!", Status)); goto Done; } - + InitializeListHead(&FdoExtension->ReadEventList); - - FdoExtension->EventListCount = 0; + + 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 + // 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)) + if (!NT_SUCCESS(Status)) { DoTrace(LEVEL_ERROR, TFLAG_IO, (" WdfIoQueueCreate(Data) %!STATUS!", Status)); goto Done; } - - InitializeListHead(&FdoExtension->ReadDataList); - - FdoExtension->DataListCount = 0; - FdoExtension->DataQueueCount = 0; + 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->CntEventCompleted = 0; + FdoExtension->CntWriteDataReq = 0; - FdoExtension->CntWriteDataCompleted = 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, + ObjAttributes.ParentObject = _Device; + + Status = WdfRequestCreate(&ObjAttributes, + FdoExtension->IoTargetSerial, &FdoExtension->ReadRequest); - - if (!NT_SUCCESS(Status)) + + 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)); + RtlZeroMemory(&FdoExtension->ReadContext, sizeof(UART_READ_CONTEXT)); FdoExtension->ReadContext.FdoExtension = FdoExtension; - ReadSegmentStateSet(&FdoExtension->ReadContext, GET_PKT_TYPE); + ReadSegmentStateSet(&FdoExtension->ReadContext, GET_PKT_TYPE); - Status = WdfMemoryCreatePreallocated(&ObjAttributes, + Status = WdfMemoryCreatePreallocated(&ObjAttributes, &FdoExtension->ReadBuffer, - sizeof(FdoExtension->ReadBuffer), - &FdoExtension->ReadMemory); + sizeof(FdoExtension->ReadBuffer), + &FdoExtension->ReadMemory); - if (!NT_SUCCESS(Status)) + if (!NT_SUCCESS(Status)) { DoTrace(LEVEL_ERROR, TFLAG_IO, (" WdfMemoryCreatePreallocated(ReadMemory) failed %!STATUS!", Status)); goto Done; @@ -1580,13 +1558,12 @@ Return Value: Done: - DoTrace(LEVEL_INFO, TFLAG_IO,("-ReadResourcesAllocate %!STATUS!", Status)); + DoTrace(LEVEL_INFO, TFLAG_IO,("-ReadResourcesAllocate %!STATUS!", Status)); if (!NT_SUCCESS(Status)) { - ReadResourcesFree(_Device); + ReadResourcesFree(_Device); } return Status; } - diff --git a/bluetooth/serialhcibus/serialhcibus.sln b/bluetooth/serialhcibus/serialhcibus.sln index f299309d7..25bc57e73 100644 --- a/bluetooth/serialhcibus/serialhcibus.sln +++ b/bluetooth/serialhcibus/serialhcibus.sln @@ -3,7 +3,7 @@ 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", "{186E63FF-9F2F-4FF6-82CB-FBAD7BA24089}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SerialBusWdk", "WDK\SerialBusWdk.vcxproj", "{6844B626-ABF4-4F07-BD90-7AA8A8A87E12}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {186E63FF-9F2F-4FF6-82CB-FBAD7BA24089}.Debug|Win32.ActiveCfg = Debug|Win32 - {186E63FF-9F2F-4FF6-82CB-FBAD7BA24089}.Debug|Win32.Build.0 = Debug|Win32 - {186E63FF-9F2F-4FF6-82CB-FBAD7BA24089}.Release|Win32.ActiveCfg = Release|Win32 - {186E63FF-9F2F-4FF6-82CB-FBAD7BA24089}.Release|Win32.Build.0 = Release|Win32 - {186E63FF-9F2F-4FF6-82CB-FBAD7BA24089}.Debug|x64.ActiveCfg = Debug|x64 - {186E63FF-9F2F-4FF6-82CB-FBAD7BA24089}.Debug|x64.Build.0 = Debug|x64 - {186E63FF-9F2F-4FF6-82CB-FBAD7BA24089}.Release|x64.ActiveCfg = Release|x64 - {186E63FF-9F2F-4FF6-82CB-FBAD7BA24089}.Release|x64.Build.0 = Release|x64 + {6844B626-ABF4-4F07-BD90-7AA8A8A87E12}.Debug|Win32.ActiveCfg = Debug|Win32 + {6844B626-ABF4-4F07-BD90-7AA8A8A87E12}.Debug|Win32.Build.0 = Debug|Win32 + {6844B626-ABF4-4F07-BD90-7AA8A8A87E12}.Release|Win32.ActiveCfg = Release|Win32 + {6844B626-ABF4-4F07-BD90-7AA8A8A87E12}.Release|Win32.Build.0 = Release|Win32 + {6844B626-ABF4-4F07-BD90-7AA8A8A87E12}.Debug|x64.ActiveCfg = Debug|x64 + {6844B626-ABF4-4F07-BD90-7AA8A8A87E12}.Debug|x64.Build.0 = Debug|x64 + {6844B626-ABF4-4F07-BD90-7AA8A8A87E12}.Release|x64.ActiveCfg = Release|x64 + {6844B626-ABF4-4F07-BD90-7AA8A8A87E12}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/filesys/cdfs/cdfs.sln b/filesys/cdfs/cdfs.sln index b45ac8dff..c14b42788 100644 --- a/filesys/cdfs/cdfs.sln +++ b/filesys/cdfs/cdfs.sln @@ -3,7 +3,7 @@ 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", "{6E1BC036-32B6-4CDA-A44E-13471EF68E06}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cdfs", "cdfs.vcxproj", "{062AB514-86CC-49DA-9F51-EA88D2B4E71F}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {6E1BC036-32B6-4CDA-A44E-13471EF68E06}.Debug|Win32.ActiveCfg = Debug|Win32 - {6E1BC036-32B6-4CDA-A44E-13471EF68E06}.Debug|Win32.Build.0 = Debug|Win32 - {6E1BC036-32B6-4CDA-A44E-13471EF68E06}.Release|Win32.ActiveCfg = Release|Win32 - {6E1BC036-32B6-4CDA-A44E-13471EF68E06}.Release|Win32.Build.0 = Release|Win32 - {6E1BC036-32B6-4CDA-A44E-13471EF68E06}.Debug|x64.ActiveCfg = Debug|x64 - {6E1BC036-32B6-4CDA-A44E-13471EF68E06}.Debug|x64.Build.0 = Debug|x64 - {6E1BC036-32B6-4CDA-A44E-13471EF68E06}.Release|x64.ActiveCfg = Release|x64 - {6E1BC036-32B6-4CDA-A44E-13471EF68E06}.Release|x64.Build.0 = Release|x64 + {062AB514-86CC-49DA-9F51-EA88D2B4E71F}.Debug|Win32.ActiveCfg = Debug|Win32 + {062AB514-86CC-49DA-9F51-EA88D2B4E71F}.Debug|Win32.Build.0 = Debug|Win32 + {062AB514-86CC-49DA-9F51-EA88D2B4E71F}.Release|Win32.ActiveCfg = Release|Win32 + {062AB514-86CC-49DA-9F51-EA88D2B4E71F}.Release|Win32.Build.0 = Release|Win32 + {062AB514-86CC-49DA-9F51-EA88D2B4E71F}.Debug|x64.ActiveCfg = Debug|x64 + {062AB514-86CC-49DA-9F51-EA88D2B4E71F}.Debug|x64.Build.0 = Debug|x64 + {062AB514-86CC-49DA-9F51-EA88D2B4E71F}.Release|x64.ActiveCfg = Release|x64 + {062AB514-86CC-49DA-9F51-EA88D2B4E71F}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/filesys/cdfs/cdfs.vcxproj b/filesys/cdfs/cdfs.vcxproj index 4c0cae16f..8c51053d6 100644 --- a/filesys/cdfs/cdfs.vcxproj +++ b/filesys/cdfs/cdfs.vcxproj @@ -19,12 +19,12 @@ - {6E1BC036-32B6-4CDA-A44E-13471EF68E06} + {062AB514-86CC-49DA-9F51-EA88D2B4E71F} $(MSBuildProjectName) false Debug Win32 - {B7974984-CC80-44F6-A5FF-3450139B650B} + {0D9B1F6C-C6A5-4688-9A62-159AA562676F} @@ -316,7 +316,6 @@ - diff --git a/filesys/cdfs/cdfs.vcxproj.Filters b/filesys/cdfs/cdfs.vcxproj.Filters index f9071fe0a..6eb4daa0e 100644 --- a/filesys/cdfs/cdfs.vcxproj.Filters +++ b/filesys/cdfs/cdfs.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {DDD1C6BA-4FDB-4B31-8CA1-A98A3B87BCAD} + {A2172E28-97CB-41ED-AC7C-C2AD8DA0F52A} h;hpp;hxx;hm;inl;inc;xsd - {6330F9B6-65BA-4A38-955C-1968DCB91EDE} + {4F6688C5-2155-4638-B834-D253854A676C} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {D575D231-5B8E-4A00-BBEF-7105E4F2B0FB} + {B5EE8D2A-8B5F-4206-B3DF-3E8452D2ECE0} inf;inv;inx;mof;mc; - {B1FE81D8-D0AC-4BB6-B311-CF9DA3709742} + {C487DB37-0DF6-4916-B8C5-B6179219926C} diff --git a/filesys/fastfat/ReadMe.md b/filesys/fastfat/ReadMe.md new file mode 100644 index 000000000..d7ce95954 --- /dev/null +++ b/filesys/fastfat/ReadMe.md @@ -0,0 +1,49 @@ +fastfat File System Driver +========================== + +The *fastfat* sample is file system driver that you can use as a model to write new file systems. + +*fastfat* is a complete file system that addresses various issues such as storing data on disk, interacting with the cache manager, and handling various I/O operations such as file creation, performing read/writes on a file, setting information on a file, and performing control operations on the file system. + +## Universal Windows Driver Compliant +This sample builds a Universal Windows Driver. It uses only APIs and DDIs that are included in OneCoreUAP. + +Build the sample +---------------- + +You can build the sample in two ways: using Microsoft Visual Studio or the command line (*MSBuild*). + +Building a Driver Using Visual Studio +------------------------------------- + +You build a driver the same way you build any project or solution in Visual Studio. When you create a new driver project using a Windows driver template, the template defines a default (active) project configuration and a default (active) solution build configuration. When you create a project from existing driver sources or convert existing driver code that was built with previous versions of the WDK, the conversion process preserves the target version information (operating systems and platform). + +The default Solution build configuration is Windows 8.1 Debug and Win32. + +### To select a configuration and build a driver + +1. Open the driver project or solution in Visual Studio (find fastfat.sln or fastfat.vcxproj). +2. Right-click the solution in the **Solutions Explorer** and select **Configuration Manager**. +3. From the **Configuration Manager**, select the **Active Solution Configuration** (for example, Windows 8.1 Debug or Windows 8.1 Release) and the **Active Solution Platform** (for example, Win32) that correspond to the type of build you are interested in. +4. From the Build menu, click **Build Solution** (Ctrl+Shift+B). + +Building a Driver Using the Command Line (MSBuild) +-------------------------------------------------- + +You can build a driver from the command line using the Visual Studio Command Prompt window and the Microsoft Build Engine (MSBuild.exe) Previous versions of the WDK used the Windows Build utility (Build.exe) and provided separate build environment windows for each of the supported build configurations. You can now use the Visual Studio Command Prompt window for all build configurations. + +### To select a configuration and build a driver or an application + +1. Open a Visual Studio Command Prompt window at the **Start** screen. From this window you can use MsBuild.exe to build any Visual Studio project by specifying the project (.VcxProj) or solutions (.Sln) file. +2. Navigate to the project directory and enter the **MSbuild** command for your target. For example, to perform a clean build of a Visual Studio driver project called *filtername*.vcxproj, navigate to the project directory and enter the following MSBuild command: **msbuild /t:clean /t:build .\\fastfat.vcxproj**. + +Run the sample +-------------- + +Installation +------------ + +No INF file is provided with this sample because the *fastfat* file system driver (fastfat.sys) is already part of the Windows operating system. You can build a private version of this file system and use it as a replacement for the native driver. + +[Send comments about this topic to Microsoft](mailto:wsddocfb@microsoft.com?subject=Documentation%20feedback%20[\ifsk]:%20fastfat%20File%20System%20Driver%20%20RELEASE:%20(1/23/2015)&body=%0A%0APRIVACY%20STATEMENT%0A%0AWe%20use%20your%20feedback%20to%20improve%20the%20documentation.%20We%20don't%20use%20your%20email%20address%20for%20any%20other%20purpose,%20and%20we'll%20remove%20your%20email%20address%20from%20our%20system%20after%20the%20issue%20that%20you're%20reporting%20is%20fixed.%20While%20we're%20working%20to%20fix%20this%20issue,%20we%20might%20send%20you%20an%20email%20message%20to%20ask%20for%20more%20info.%20Later,%20we%20might%20also%20send%20you%20an%20email%20message%20to%20let%20you%20know%20that%20we've%20addressed%20your%20feedback.%0A%0AFor%20more%20info%20about%20Microsoft's%20privacy%20policy,%20see%20http://privacy.microsoft.com/en-us/default.aspx. "Send comments about this topic to Microsoft") + diff --git a/filesys/fastfat/acchksup.c b/filesys/fastfat/acchksup.c new file mode 100644 index 000000000..af6c2f142 --- /dev/null +++ b/filesys/fastfat/acchksup.c @@ -0,0 +1,421 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + AcChkSup.c + +Abstract: + + This module implements the FAT access checking routine + + +--*/ + +#include "FatProcs.h" + +// +// Our debug trace level +// + +#define Dbg (DEBUG_TRACE_ACCHKSUP) + +NTSTATUS +FatCreateRestrictEveryoneToken( + IN PACCESS_TOKEN Token, + OUT PACCESS_TOKEN *RestrictedToken + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, FatCheckFileAccess) +#pragma alloc_text(PAGE, FatCheckManageVolumeAccess) +#pragma alloc_text(PAGE, FatCreateRestrictEveryoneToken) +#pragma alloc_text(PAGE, FatExplicitDeviceAccessGranted) +#endif + + +BOOLEAN +FatCheckFileAccess ( + PIRP_CONTEXT IrpContext, + IN UCHAR DirentAttributes, + IN PACCESS_MASK DesiredAccess + ) + +/*++ + +Routine Description: + + This routine checks if a desired access is allowed to a file represented + by the specified DirentAttriubutes. + +Arguments: + + DirentAttributes - Supplies the Dirent attributes to check access for + + DesiredAccess - Supplies the desired access mask that we are checking for + +Return Value: + + BOOLEAN - TRUE if access is allowed and FALSE otherwise + +--*/ + +{ + BOOLEAN Result; + + DebugTrace(+1, Dbg, "FatCheckFileAccess\n", 0); + DebugTrace( 0, Dbg, "DirentAttributes = %8lx\n", DirentAttributes); + DebugTrace( 0, Dbg, "DesiredAccess = %8lx\n", *DesiredAccess); + + PAGED_CODE(); + + // + // This procedures is programmed like a string of filters each + // filter checks to see if some access is allowed, if it is not allowed + // the filter return FALSE to the user without further checks otherwise + // it moves on to the next filter. The filter check is to check for + // desired access flags that are not allowed for a particular dirent + // + + Result = TRUE; + + try { + + // + // Check for Volume ID or Device Dirents, these are not allowed user + // access at all + // + + if (FlagOn(DirentAttributes, FAT_DIRENT_ATTR_VOLUME_ID) || + FlagOn(DirentAttributes, FAT_DIRENT_ATTR_DEVICE)) { + + DebugTrace(0, Dbg, "Cannot access volume id or device\n", 0); + + try_return( Result = FALSE ); + } + + // + // Check the desired access for the object - we only blackball that + // we do not understand. The model of filesystems using ACLs is that + // they do not type the ACL to the object the ACL is on. Permissions + // are not checked for consistency vs. the object type - dir/file. + // + + if (FlagOn(*DesiredAccess, ~(DELETE | + READ_CONTROL | + WRITE_OWNER | + WRITE_DAC | + SYNCHRONIZE | + ACCESS_SYSTEM_SECURITY | + FILE_WRITE_DATA | + FILE_READ_EA | + FILE_WRITE_EA | + FILE_READ_ATTRIBUTES | + FILE_WRITE_ATTRIBUTES | + FILE_LIST_DIRECTORY | + FILE_TRAVERSE | + FILE_DELETE_CHILD | + FILE_APPEND_DATA | + MAXIMUM_ALLOWED))) { + + DebugTrace(0, Dbg, "Cannot open object\n", 0); + + try_return( Result = FALSE ); + } + + // + // Check for a read-only Dirent + // + + if (FlagOn(DirentAttributes, FAT_DIRENT_ATTR_READ_ONLY)) { + + // + // Check the desired access for a read-only dirent. AccessMask will contain + // the flags we're going to allow. + // + + ACCESS_MASK AccessMask = DELETE | READ_CONTROL | WRITE_OWNER | WRITE_DAC | + SYNCHRONIZE | ACCESS_SYSTEM_SECURITY | FILE_READ_DATA | + FILE_READ_EA | FILE_WRITE_EA | FILE_READ_ATTRIBUTES | + FILE_WRITE_ATTRIBUTES | FILE_EXECUTE | FILE_LIST_DIRECTORY | + FILE_TRAVERSE; + + // + // If this is a subdirectory also allow add file/directory and delete. + // + + if (FlagOn(DirentAttributes, FAT_DIRENT_ATTR_DIRECTORY)) { + + AccessMask |= FILE_ADD_SUBDIRECTORY | FILE_ADD_FILE | FILE_DELETE_CHILD; + } + + if (FlagOn(*DesiredAccess, ~AccessMask)) { + + DebugTrace(0, Dbg, "Cannot open readonly\n", 0); + + try_return( Result = FALSE ); + } + } + + try_exit: NOTHING; + } finally { + + DebugUnwind( FatCheckFileAccess ); + + DebugTrace(-1, Dbg, "FatCheckFileAccess -> %08lx\n", Result); + } + + UNREFERENCED_PARAMETER( IrpContext ); + + return Result; +} + + +BOOLEAN +FatCheckManageVolumeAccess ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PACCESS_STATE AccessState, + _In_ KPROCESSOR_MODE ProcessorMode + ) + +/*++ + +Routine Description: + + This function checks whether the SID described in the input access state has + manage volume privilege. + +Arguments: + + AccessState - the access state describing the security context to be checked + + ProcessorMode - the mode this check should occur against + +Return Value: + + BOOLEAN - TRUE if privilege is held and FALSE otherwise + +--*/ + +{ + PRIVILEGE_SET PrivilegeSet; + + PAGED_CODE(); + + PrivilegeSet.PrivilegeCount = 1; + PrivilegeSet.Control = PRIVILEGE_SET_ALL_NECESSARY; + PrivilegeSet.Privilege[0].Luid = RtlConvertLongToLuid( SE_MANAGE_VOLUME_PRIVILEGE ); + PrivilegeSet.Privilege[0].Attributes = 0; + + if (SePrivilegeCheck( &PrivilegeSet, + &AccessState->SubjectSecurityContext, + ProcessorMode )) { + + return TRUE; + } + + UNREFERENCED_PARAMETER( IrpContext ); + + return FALSE; +} + + +NTSTATUS +FatExplicitDeviceAccessGranted ( + IN PIRP_CONTEXT IrpContext, + IN PDEVICE_OBJECT DeviceObject, + IN PACCESS_STATE AccessState, + IN KPROCESSOR_MODE ProcessorMode + ) + +/*++ + +Routine Description: + + This function asks whether the SID described in the input access state has + been granted any explicit access to the given device object. It does this + by acquiring a token stripped of its ability to acquire access via the + Everyone SID and re-doing the access check. + +Arguments: + + DeviceObject - the device whose ACL will be checked + + AccessState - the access state describing the security context to be checked + + ProcessorMode - the mode this check should occur against + +Return Value: + + NTSTATUS - Indicating whether explicit access was granted. + +--*/ + +{ + NTSTATUS Status; + + PACCESS_TOKEN OriginalAccessToken; + PACCESS_TOKEN RestrictedAccessToken; + + PACCESS_TOKEN *EffectiveToken; + + ACCESS_MASK GrantedAccess; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( IrpContext ); + + // + // If the access state indicates that specific access other + // than traverse was acquired, either Everyone does have such + // access or explicit access was granted. In both cases, we're + // happy to let this proceed. + // + + if (AccessState->PreviouslyGrantedAccess & (SPECIFIC_RIGHTS_ALL ^ + FILE_TRAVERSE)) { + + return STATUS_SUCCESS; + } + + // + // If the manage volume privilege is held, this also permits access. + // + + if (FatCheckManageVolumeAccess( IrpContext, + AccessState, + ProcessorMode )) { + + return STATUS_SUCCESS; + } + + // + // Capture the subject context as a prelude to everything below. + // + + SeLockSubjectContext( &AccessState->SubjectSecurityContext ); + + // + // Convert the token in the subject context into one which does not + // acquire access through the Everyone SID. + // + // The logic for deciding which token is effective comes from + // SeQuerySubjectContextToken; since there is no natural way + // of getting a pointer to it, do it by hand. + // + + if (ARGUMENT_PRESENT( AccessState->SubjectSecurityContext.ClientToken )) { + EffectiveToken = &AccessState->SubjectSecurityContext.ClientToken; + } else { + EffectiveToken = &AccessState->SubjectSecurityContext.PrimaryToken; + } + + OriginalAccessToken = *EffectiveToken; + Status = FatCreateRestrictEveryoneToken( OriginalAccessToken, &RestrictedAccessToken ); + + if (!NT_SUCCESS(Status)) { + + SeReleaseSubjectContext( &AccessState->SubjectSecurityContext ); + return Status; + } + + // + // Now see if the resulting context has access to the device through + // its explicitly granted access. We swap in our restricted token + // for this check as the effective client token. + // + + *EffectiveToken = RestrictedAccessToken; + +#pragma prefast( suppress: 28175, "we're a file system, this is ok to touch" ) + SeAccessCheck( DeviceObject->SecurityDescriptor, + &AccessState->SubjectSecurityContext, + FALSE, + AccessState->OriginalDesiredAccess, + 0, + NULL, + IoGetFileObjectGenericMapping(), + ProcessorMode, + &GrantedAccess, + &Status ); + + *EffectiveToken = OriginalAccessToken; + + // + // Cleanup and return. + // + + SeUnlockSubjectContext( &AccessState->SubjectSecurityContext ); + ObDereferenceObject( RestrictedAccessToken ); + + return Status; +} + + +NTSTATUS +FatCreateRestrictEveryoneToken ( + IN PACCESS_TOKEN Token, + OUT PACCESS_TOKEN *RestrictedToken + ) + +/*++ + +Routine Description: + + This function takes a token as the input and returns a new restricted token + from which Everyone sid has been disabled. The resulting token may be used + to find out if access is available to a user-sid by explicit means. + +Arguments: + + Token - Input token from which Everyone sid needs to be deactivated. + + RestrictedToken - Receives the the new restricted token. + This must be released using ObDereferenceObject(*RestrictedToken); + +Return Value: + + NTSTATUS - Returned by SeFilterToken. + +--*/ + +{ + // + // Array of sids to disable. + // + + TOKEN_GROUPS SidsToDisable; + + NTSTATUS Status = STATUS_SUCCESS; + + PAGED_CODE(); + + // + // Restricted token will contain the original sids with one change: + // If Everyone sid is present in the token, it will be marked for DenyOnly. + // + + *RestrictedToken = NULL; + + // + // Put Everyone sid in the array of sids to disable. This will mark it + // for SE_GROUP_USE_FOR_DENY_ONLY and it'll only be applicable for Deny aces. + // + + SidsToDisable.GroupCount = 1; + SidsToDisable.Groups[0].Attributes = 0; + SidsToDisable.Groups[0].Sid = SeExports->SeWorldSid; + + Status = SeFilterToken( + Token, // Token that needs to be restricted. + 0, // No flags + &SidsToDisable, // Disable everyone sid + NULL, // Do not create any restricted sids + NULL, // Do not delete any privileges + RestrictedToken // Restricted token + ); + + return Status; +} + diff --git a/filesys/fastfat/allocsup.c b/filesys/fastfat/allocsup.c new file mode 100644 index 000000000..d58508270 --- /dev/null +++ b/filesys/fastfat/allocsup.c @@ -0,0 +1,5222 @@ +/*++ + +Copyright (c) 1990-2000 Microsoft Corporation + +Module Name: + + AllocSup.c + +Abstract: + + This module implements the Allocation support routines for Fat. + + +--*/ + +#include "FatProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (FAT_BUG_CHECK_ALLOCSUP) + +// +// Local debug trace level +// + +#define Dbg (DEBUG_TRACE_ALLOCSUP) + +#define FatMin(a, b) ((a) < (b) ? (a) : (b)) + +// +// Define prefetch page count for the FAT +// + +#define FAT_PREFETCH_PAGE_COUNT 0x100 + +// +// Local support routine prototypes +// + +VOID +FatLookupFatEntry( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN ULONG FatIndex, + IN OUT PULONG FatEntry, + IN OUT PFAT_ENUMERATION_CONTEXT Context + ); + +VOID +FatSetFatRun( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN ULONG StartingFatIndex, + IN ULONG ClusterCount, + IN BOOLEAN ChainTogether + ); + +UCHAR +FatLogOf( + IN ULONG Value + ); + +// +// Note that the KdPrint below will ONLY fire when the assert does. Leave it +// alone. +// + +#if DBG +#define ASSERT_CURRENT_WINDOW_GOOD(VCB) { \ + ULONG FreeClusterBitMapClear; \ + NT_ASSERT( (VCB)->FreeClusterBitMap.Buffer != NULL ); \ + FreeClusterBitMapClear = RtlNumberOfClearBits(&(VCB)->FreeClusterBitMap); \ + if ((VCB)->CurrentWindow->ClustersFree != FreeClusterBitMapClear) { \ + KdPrint(("FAT: ClustersFree %x h != FreeClusterBitMapClear %x h\n", \ + (VCB)->CurrentWindow->ClustersFree, \ + FreeClusterBitMapClear)); \ + } \ + NT_ASSERT( (VCB)->CurrentWindow->ClustersFree == FreeClusterBitMapClear ); \ +} +#else +#define ASSERT_CURRENT_WINDOW_GOOD(VCB) +#endif + +// +// The following macros provide a convenient way of hiding the details +// of bitmap allocation schemes. +// + + +// +// VOID +// FatLockFreeClusterBitMap ( +// IN PVCB Vcb +// ); +// + +#define FatLockFreeClusterBitMap(VCB) { \ + NT_ASSERT(KeAreApcsDisabled()); \ + ExAcquireFastMutexUnsafe( &(VCB)->FreeClusterBitMapMutex ); \ + ASSERT_CURRENT_WINDOW_GOOD(VCB) \ +} + +// +// VOID +// FatUnlockFreeClusterBitMap ( +// IN PVCB Vcb +// ); +// + +#define FatUnlockFreeClusterBitMap(VCB) { \ + ASSERT_CURRENT_WINDOW_GOOD(VCB) \ + NT_ASSERT(KeAreApcsDisabled()); \ + ExReleaseFastMutexUnsafe( &(VCB)->FreeClusterBitMapMutex ); \ +} + +// +// BOOLEAN +// FatIsClusterFree ( +// IN PIRP_CONTEXT IrpContext, +// IN PVCB Vcb, +// IN ULONG FatIndex +// ); +// + +#define FatIsClusterFree(IRPCONTEXT,VCB,FAT_INDEX) \ + (RtlCheckBit(&(VCB)->FreeClusterBitMap,(FAT_INDEX)-2) == 0) + +// +// VOID +// FatFreeClusters ( +// IN PIRP_CONTEXT IrpContext, +// IN PVCB Vcb, +// IN ULONG FatIndex, +// IN ULONG ClusterCount +// ); +// + +#define FatFreeClusters(IRPCONTEXT,VCB,FAT_INDEX,CLUSTER_COUNT) { \ + if ((CLUSTER_COUNT) == 1) { \ + FatSetFatEntry((IRPCONTEXT),(VCB),(FAT_INDEX),FAT_CLUSTER_AVAILABLE); \ + } else { \ + FatSetFatRun((IRPCONTEXT),(VCB),(FAT_INDEX),(CLUSTER_COUNT),FALSE); \ + } \ +} + +// +// VOID +// FatAllocateClusters ( +// IN PIRP_CONTEXT IrpContext, +// IN PVCB Vcb, +// IN ULONG FatIndex, +// IN ULONG ClusterCount +// ); +// + +#define FatAllocateClusters(IRPCONTEXT,VCB,FAT_INDEX,CLUSTER_COUNT) { \ + if ((CLUSTER_COUNT) == 1) { \ + FatSetFatEntry((IRPCONTEXT),(VCB),(FAT_INDEX),FAT_CLUSTER_LAST); \ + } else { \ + FatSetFatRun((IRPCONTEXT),(VCB),(FAT_INDEX),(CLUSTER_COUNT),TRUE); \ + } \ +} + +// +// VOID +// FatUnreserveClusters ( +// IN PIRP_CONTEXT IrpContext, +// IN PVCB Vcb, +// IN ULONG FatIndex, +// IN ULONG ClusterCount +// ); +// + +#define FatUnreserveClusters(IRPCONTEXT,VCB,FAT_INDEX,CLUSTER_COUNT) { \ + NT_ASSERT( (FAT_INDEX) + (CLUSTER_COUNT) - 2 <= (VCB)->FreeClusterBitMap.SizeOfBitMap );\ + NT_ASSERT( (FAT_INDEX) >= 2); \ + RtlClearBits(&(VCB)->FreeClusterBitMap,(FAT_INDEX)-2,(CLUSTER_COUNT)); \ + if ((FAT_INDEX) < (VCB)->ClusterHint) { \ + (VCB)->ClusterHint = (FAT_INDEX); \ + } \ +} + +// +// VOID +// FatReserveClusters ( +// IN PIRP_CONTEXT IrpContext, +// IN PVCB Vcb, +// IN ULONG FatIndex, +// IN ULONG ClusterCount +// ); +// +// Handle wrapping the hint back to the front. +// + +#define FatReserveClusters(IRPCONTEXT,VCB,FAT_INDEX,CLUSTER_COUNT) { \ + ULONG _AfterRun = (FAT_INDEX) + (CLUSTER_COUNT); \ + NT_ASSERT( (FAT_INDEX) + (CLUSTER_COUNT) - 2 <= (VCB)->FreeClusterBitMap.SizeOfBitMap );\ + NT_ASSERT( (FAT_INDEX) >= 2); \ + RtlSetBits(&(VCB)->FreeClusterBitMap,(FAT_INDEX)-2,(CLUSTER_COUNT)); \ + \ + if (_AfterRun - 2 >= (VCB)->FreeClusterBitMap.SizeOfBitMap) { \ + _AfterRun = 2; \ + } \ + if (RtlCheckBit(&(VCB)->FreeClusterBitMap, _AfterRun - 2)) { \ + (VCB)->ClusterHint = RtlFindClearBits( &(VCB)->FreeClusterBitMap, 1, _AfterRun - 2) + 2; \ + if (1 == (VCB)->ClusterHint) { \ + (VCB)->ClusterHint = 2; \ + } \ + } \ + else { \ + (VCB)->ClusterHint = _AfterRun; \ + } \ +} + +// +// ULONG +// FatFindFreeClusterRun ( +// IN PIRP_CONTEXT IrpContext, +// IN PVCB Vcb, +// IN ULONG ClusterCount, +// IN ULONG AlternateClusterHint +// ); +// +// Do a special check if only one cluster is desired. +// + +#define FatFindFreeClusterRun(IRPCONTEXT,VCB,CLUSTER_COUNT,CLUSTER_HINT) ( \ + (CLUSTER_COUNT == 1) && \ + FatIsClusterFree((IRPCONTEXT), (VCB), (CLUSTER_HINT)) ? \ + (CLUSTER_HINT) : \ + RtlFindClearBits( &(VCB)->FreeClusterBitMap, \ + (CLUSTER_COUNT), \ + (CLUSTER_HINT) - 2) + 2 \ +) + +// +// FAT32: Define the maximum size of the FreeClusterBitMap to be the +// maximum size of a FAT16 FAT. If there are more clusters on the +// volume than can be represented by this many bytes of bitmap, the +// FAT will be split into "buckets", each of which does fit. +// +// Note this count is in clusters/bits of bitmap. +// + +#define MAX_CLUSTER_BITMAP_SIZE (1 << 16) + +// +// Calculate the window a given cluster number is in. +// + +#define FatWindowOfCluster(C) (((C) - 2) / MAX_CLUSTER_BITMAP_SIZE) + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, FatAddFileAllocation) +#pragma alloc_text(PAGE, FatAllocateDiskSpace) +#pragma alloc_text(PAGE, FatDeallocateDiskSpace) +#pragma alloc_text(PAGE, FatExamineFatEntries) +#pragma alloc_text(PAGE, FatInterpretClusterType) +#pragma alloc_text(PAGE, FatLogOf) +#pragma alloc_text(PAGE, FatLookupFatEntry) +#pragma alloc_text(PAGE, FatLookupFileAllocation) +#pragma alloc_text(PAGE, FatLookupFileAllocationSize) +#pragma alloc_text(PAGE, FatMergeAllocation) +#pragma alloc_text(PAGE, FatSetFatEntry) +#pragma alloc_text(PAGE, FatSetFatRun) +#pragma alloc_text(PAGE, FatSetupAllocationSupport) +#pragma alloc_text(PAGE, FatSplitAllocation) +#pragma alloc_text(PAGE, FatTearDownAllocationSupport) +#pragma alloc_text(PAGE, FatTruncateFileAllocation) +#endif + + +INLINE +ULONG +FatSelectBestWindow( + IN PVCB Vcb + ) +/*++ + +Routine Description: + + Choose a window to allocate clusters from. Order of preference is: + + 1. First window with >50% free clusters + 2. First empty window + 3. Window with greatest number of free clusters. + +Arguments: + + Vcb - Supplies the Vcb for the volume + +Return Value: + + 'Best window' number (index into Vcb->Windows[]) + +--*/ +{ + ULONG i, Fave = 0; + ULONG MaxFree = 0; + ULONG FirstEmpty = (ULONG)-1; + ULONG ClustersPerWindow = MAX_CLUSTER_BITMAP_SIZE; + + NT_ASSERT( 1 != Vcb->NumberOfWindows); + + for (i = 0; i < Vcb->NumberOfWindows; i++) { + + if (Vcb->Windows[i].ClustersFree == ClustersPerWindow) { + + if (-1 == FirstEmpty) { + + // + // Keep note of the first empty window on the disc + // + + FirstEmpty = i; + } + } + else if (Vcb->Windows[i].ClustersFree > MaxFree) { + + // + // This window has the most free clusters, so far + // + + MaxFree = Vcb->Windows[i].ClustersFree; + Fave = i; + + // + // If this window has >50% free clusters, then we will take it, + // so don't bother considering more windows. + // + + if (MaxFree >= (ClustersPerWindow >> 1)) { + + break; + } + } + } + + // + // If there were no windows with 50% or more freespace, then select the + // first empty window on the disc, if any - otherwise we'll just go with + // the one with the most free clusters. + // + + if ((MaxFree < (ClustersPerWindow >> 1)) && (-1 != FirstEmpty)) { + + Fave = FirstEmpty; + } + + return Fave; +} + + +VOID +FatSetupAllocationSupport ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb + ) + +/*++ + +Routine Description: + + This routine fills in the Allocation Support structure in the Vcb. + Most entries are computed using fat.h macros supplied with data from + the Bios Parameter Block. The free cluster count, however, requires + going to the Fat and actually counting free sectors. At the same time + the free cluster bit map is initalized. + +Arguments: + + Vcb - Supplies the Vcb to fill in. + +--*/ + +{ + ULONG BitIndex; + ULONG ClustersDescribableByFat; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatSetupAllocationSupport\n", 0); + DebugTrace( 0, Dbg, " Vcb = %p\n", Vcb); + + // + // Compute a number of fields for Vcb.AllocationSupport + // + + Vcb->AllocationSupport.RootDirectoryLbo = FatRootDirectoryLbo( &Vcb->Bpb ); + Vcb->AllocationSupport.RootDirectorySize = FatRootDirectorySize( &Vcb->Bpb ); + + Vcb->AllocationSupport.FileAreaLbo = FatFileAreaLbo( &Vcb->Bpb ); + + Vcb->AllocationSupport.NumberOfClusters = FatNumberOfClusters( &Vcb->Bpb ); + + Vcb->AllocationSupport.FatIndexBitSize = FatIndexBitSize( &Vcb->Bpb ); + + Vcb->AllocationSupport.LogOfBytesPerSector = FatLogOf(Vcb->Bpb.BytesPerSector); + Vcb->AllocationSupport.LogOfBytesPerCluster = FatLogOf(FatBytesPerCluster( &Vcb->Bpb )); + Vcb->AllocationSupport.NumberOfFreeClusters = 0; + + + // + // Deal with a bug in DOS 5 format, if the Fat is not big enough to + // describe all the clusters on the disk, reduce this number. We expect + // that fat32 volumes will not have this problem. + // + // Turns out this was not a good assumption. We have to do this always now. + // + + ClustersDescribableByFat = ( ((FatIsFat32(Vcb)? Vcb->Bpb.LargeSectorsPerFat : + Vcb->Bpb.SectorsPerFat) * + Vcb->Bpb.BytesPerSector * 8) + / FatIndexBitSize(&Vcb->Bpb) ) - 2; + + if (Vcb->AllocationSupport.NumberOfClusters > ClustersDescribableByFat) { + + Vcb->AllocationSupport.NumberOfClusters = ClustersDescribableByFat; + } + + // + // Extend the virtual volume file to include the Fat + // + + { + CC_FILE_SIZES FileSizes; + + FileSizes.AllocationSize.QuadPart = + FileSizes.FileSize.QuadPart = (FatReservedBytes( &Vcb->Bpb ) + + FatBytesPerFat( &Vcb->Bpb )); + FileSizes.ValidDataLength = FatMaxLarge; + + if ( Vcb->VirtualVolumeFile->PrivateCacheMap == NULL ) { + + FatInitializeCacheMap( Vcb->VirtualVolumeFile, + &FileSizes, + TRUE, + &FatData.CacheManagerNoOpCallbacks, + Vcb ); + + } else { + + CcSetFileSizes( Vcb->VirtualVolumeFile, &FileSizes ); + } + } + + try { + + if (FatIsFat32(Vcb) && + Vcb->AllocationSupport.NumberOfClusters > MAX_CLUSTER_BITMAP_SIZE) { + + Vcb->NumberOfWindows = (Vcb->AllocationSupport.NumberOfClusters + + MAX_CLUSTER_BITMAP_SIZE - 1) / + MAX_CLUSTER_BITMAP_SIZE; + + } else { + + Vcb->NumberOfWindows = 1; + } + + Vcb->Windows = FsRtlAllocatePoolWithTag( PagedPool, + Vcb->NumberOfWindows * sizeof(FAT_WINDOW), + TAG_FAT_WINDOW ); + + RtlInitializeBitMap( &Vcb->FreeClusterBitMap, + NULL, + 0 ); + + // + // Chose a FAT window to begin operation in. + // + + if (Vcb->NumberOfWindows > 1) { + + // + // Read the fat and count up free clusters. We bias by the two reserved + // entries in the FAT. + // + + FatExamineFatEntries( IrpContext, Vcb, + 2, + Vcb->AllocationSupport.NumberOfClusters + 2 - 1, + TRUE, + NULL, + NULL); + + + // + // Pick a window to begin allocating from + // + + Vcb->CurrentWindow = &Vcb->Windows[ FatSelectBestWindow( Vcb)]; + + } else { + + Vcb->CurrentWindow = &Vcb->Windows[0]; + + // + // Carefully bias ourselves by the two reserved entries in the FAT. + // + + Vcb->CurrentWindow->FirstCluster = 2; + Vcb->CurrentWindow->LastCluster = Vcb->AllocationSupport.NumberOfClusters + 2 - 1; + } + + // + // Now transition to the FAT window we have chosen. + // + + FatExamineFatEntries( IrpContext, Vcb, + 0, + 0, + FALSE, + Vcb->CurrentWindow, + NULL); + + // + // Now set the ClusterHint to the first free bit in our favorite + // window (except the ClusterHint is off by two). + // + + Vcb->ClusterHint = + (BitIndex = RtlFindClearBits( &Vcb->FreeClusterBitMap, 1, 0 )) != -1 ? + BitIndex + 2 : 2; + + } finally { + + DebugUnwind( FatSetupAllocationSupport ); + + // + // If we hit an exception, back out. + // + + if (AbnormalTermination()) { + + FatTearDownAllocationSupport( IrpContext, Vcb ); + } + } + + return; +} + + +VOID +FatTearDownAllocationSupport ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb + ) + +/*++ + +Routine Description: + + This routine prepares the volume for closing. Specifically, we must + release the free fat bit map buffer, and uninitialize the dirty fat + Mcb. + +Arguments: + + Vcb - Supplies the Vcb to fill in. + +Return Value: + + VOID + +--*/ + +{ + DebugTrace(+1, Dbg, "FatTearDownAllocationSupport\n", 0); + DebugTrace( 0, Dbg, " Vcb = %p\n", Vcb); + + PAGED_CODE(); + + // + // If there are FAT buckets, free them. + // + + if ( Vcb->Windows != NULL ) { + + ExFreePool( Vcb->Windows ); + Vcb->Windows = NULL; + } + + // + // Free the memory associated with the free cluster bitmap. + // + + if ( Vcb->FreeClusterBitMap.Buffer != NULL ) { + + ExFreePool( Vcb->FreeClusterBitMap.Buffer ); + + // + // NULL this field as an flag. + // + + Vcb->FreeClusterBitMap.Buffer = NULL; + } + + // + // And remove all the runs in the dirty fat Mcb + // + + FatRemoveMcbEntry( Vcb, &Vcb->DirtyFatMcb, 0, 0xFFFFFFFF ); + + DebugTrace(-1, Dbg, "FatTearDownAllocationSupport -> (VOID)\n", 0); + + UNREFERENCED_PARAMETER( IrpContext ); + + return; +} + + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatLookupFileAllocation ( + IN PIRP_CONTEXT IrpContext, + IN PFCB FcbOrDcb, + IN VBO Vbo, + OUT PLBO Lbo, + OUT PULONG ByteCount, + OUT PBOOLEAN Allocated, + OUT PBOOLEAN EndOnMax, + OUT PULONG Index + ) + +/*++ + +Routine Description: + + This routine looks up the existing mapping of VBO to LBO for a + file/directory. The information it queries is either stored in the + mcb field of the fcb/dcb or it is stored on in the fat table and + needs to be retrieved and decoded, and updated in the mcb. + +Arguments: + + FcbOrDcb - Supplies the Fcb/Dcb of the file/directory being queried + + Vbo - Supplies the VBO whose LBO we want returned + + Lbo - Receives the LBO corresponding to the input Vbo if one exists + + ByteCount - Receives the number of bytes within the run the run + that correpond between the input vbo and output lbo. + + Allocated - Receives TRUE if the Vbo does have a corresponding Lbo + and FALSE otherwise. + + EndOnMax - Receives TRUE if the run ends in the maximal FAT cluster, + which results in a fractional bytecount. + + Index - Receives the Index of the run + +--*/ + +{ + VBO CurrentVbo; + LBO CurrentLbo; + LBO PriorLbo; + + VBO FirstVboOfCurrentRun = 0; + LBO FirstLboOfCurrentRun; + + BOOLEAN LastCluster; + ULONG Runs; + + PVCB Vcb; + FAT_ENTRY FatEntry; + ULONG BytesPerCluster; + ULARGE_INTEGER BytesOnVolume; + + FAT_ENUMERATION_CONTEXT Context; + + PAGED_CODE(); + + Vcb = FcbOrDcb->Vcb; + + + DebugTrace(+1, Dbg, "FatLookupFileAllocation\n", 0); + DebugTrace( 0, Dbg, " FcbOrDcb = %p\n", FcbOrDcb); + DebugTrace( 0, Dbg, " Vbo = %8lx\n", Vbo); + DebugTrace( 0, Dbg, " Lbo = %8lx\n", Lbo); + DebugTrace( 0, Dbg, " ByteCount = %8lx\n", ByteCount); + DebugTrace( 0, Dbg, " Allocated = %8lx\n", Allocated); + + Context.Bcb = NULL; + + *EndOnMax = FALSE; + + // + // Check the trivial case that the mapping is already in our + // Mcb. + // + + if ( FatLookupMcbEntry(Vcb, &FcbOrDcb->Mcb, Vbo, Lbo, ByteCount, Index) ) { + + *Allocated = TRUE; + + NT_ASSERT( *ByteCount != 0 ); + + // + // Detect the overflow case, trim and claim the condition. + // + + if (Vbo + *ByteCount == 0) { + + *EndOnMax = TRUE; + } + + DebugTrace( 0, Dbg, "Found run in Mcb.\n", 0); + DebugTrace(-1, Dbg, "FatLookupFileAllocation -> (VOID)\n", 0); + return; + } + + // + // Initialize the Vcb, the cluster size, LastCluster, and + // FirstLboOfCurrentRun (to be used as an indication of the first + // iteration through the following while loop). + // + + BytesPerCluster = 1 << Vcb->AllocationSupport.LogOfBytesPerCluster; + + BytesOnVolume.QuadPart = UInt32x32To64( Vcb->AllocationSupport.NumberOfClusters, BytesPerCluster ); + + LastCluster = FALSE; + FirstLboOfCurrentRun = 0; + + // + // Discard the case that the request extends beyond the end of + // allocation. Note that if the allocation size if not known + // AllocationSize is set to 0xffffffff. + // + + if ( Vbo >= FcbOrDcb->Header.AllocationSize.LowPart ) { + + *Allocated = FALSE; + + DebugTrace( 0, Dbg, "Vbo beyond end of file.\n", 0); + DebugTrace(-1, Dbg, "FatLookupFileAllocation -> (VOID)\n", 0); + return; + } + + // + // The Vbo is beyond the last Mcb entry. So we adjust Current Vbo/Lbo + // and FatEntry to describe the beginning of the last entry in the Mcb. + // This is used as initialization for the following loop. + // + // If the Mcb was empty, we start at the beginning of the file with + // CurrentVbo set to 0 to indicate a new run. + // + + if (FatLookupLastMcbEntry( Vcb, &FcbOrDcb->Mcb, &CurrentVbo, &CurrentLbo, &Runs )) { + + DebugTrace( 0, Dbg, "Current Mcb size = %8lx.\n", CurrentVbo + 1); + + CurrentVbo -= (BytesPerCluster - 1); + CurrentLbo -= (BytesPerCluster - 1); + + // + // Convert an index to a count. + // + + Runs += 1; + + } else { + + DebugTrace( 0, Dbg, "Mcb empty.\n", 0); + + // + // Check for an FcbOrDcb that has no allocation + // + + if (FcbOrDcb->FirstClusterOfFile == 0) { + + *Allocated = FALSE; + + DebugTrace( 0, Dbg, "File has no allocation.\n", 0); + DebugTrace(-1, Dbg, "FatLookupFileAllocation -> (VOID)\n", 0); + return; + + } else { + + CurrentVbo = 0; + CurrentLbo = FatGetLboFromIndex( Vcb, FcbOrDcb->FirstClusterOfFile ); + FirstVboOfCurrentRun = CurrentVbo; + FirstLboOfCurrentRun = CurrentLbo; + + Runs = 0; + + DebugTrace( 0, Dbg, "First Lbo of file = %8lx\n", CurrentLbo); + } + } + + // + // Now we know that we are looking up a valid Vbo, but it is + // not in the Mcb, which is a monotonically increasing list of + // Vbo's. Thus we have to go to the Fat, and update + // the Mcb as we go. We use a try-finally to unpin the page + // of fat hanging around. Also we mark *Allocated = FALSE, so that + // the caller wont try to use the data if we hit an exception. + // + + *Allocated = FALSE; + + try { + + FatEntry = (FAT_ENTRY)FatGetIndexFromLbo( Vcb, CurrentLbo ); + + // + // ASSERT that CurrentVbo and CurrentLbo are now cluster alligned. + // The assumption here, is that only whole clusters of Vbos and Lbos + // are mapped in the Mcb. + // + + NT_ASSERT( ((CurrentLbo - Vcb->AllocationSupport.FileAreaLbo) + % BytesPerCluster == 0) && + (CurrentVbo % BytesPerCluster == 0) ); + + // + // Starting from the first Vbo after the last Mcb entry, scan through + // the Fat looking for our Vbo. We continue through the Fat until we + // hit a noncontiguity beyond the desired Vbo, or the last cluster. + // + + while ( !LastCluster ) { + + // + // Get the next fat entry, and update our Current variables. + // + + FatLookupFatEntry( IrpContext, Vcb, FatEntry, (PULONG)&FatEntry, &Context ); + + PriorLbo = CurrentLbo; + CurrentLbo = FatGetLboFromIndex( Vcb, FatEntry ); + CurrentVbo += BytesPerCluster; + + switch ( FatInterpretClusterType( Vcb, FatEntry )) { + + // + // Check for a break in the Fat allocation chain. + // + + case FatClusterAvailable: + case FatClusterReserved: + case FatClusterBad: + + DebugTrace( 0, Dbg, "Break in allocation chain, entry = %d\n", FatEntry); + DebugTrace(-1, Dbg, "FatLookupFileAllocation -> Fat Corrupt. Raise Status.\n", 0); + + FatPopUpFileCorrupt( IrpContext, FcbOrDcb ); + FatRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); + break; + + // + // If this is the last cluster, we must update the Mcb and + // exit the loop. + // + + case FatClusterLast: + + // + // Assert we know where the current run started. If the + // Mcb was empty when we were called, thenFirstLboOfCurrentRun + // was set to the start of the file. If the Mcb contained an + // entry, then FirstLboOfCurrentRun was set on the first + // iteration through the loop. Thus if FirstLboOfCurrentRun + // is 0, then there was an Mcb entry and we are on our first + // iteration, meaing that the last cluster in the Mcb was + // really the last allocated cluster, but we checked Vbo + // against AllocationSize, and found it OK, thus AllocationSize + // must be too large. + // + // Note that, when we finally arrive here, CurrentVbo is actually + // the first Vbo beyond the file allocation and CurrentLbo is + // meaningless. + // + + DebugTrace( 0, Dbg, "Read last cluster of file.\n", 0); + + // + // Detect the case of the maximal file. Note that this really isn't + // a proper Vbo - those are zero-based, and this is a one-based number. + // The maximal file, of 2^32 - 1 bytes, has a maximum byte offset of + // 2^32 - 2. + // + // Just so we don't get confused here. + // + + if (CurrentVbo == 0) { + + *EndOnMax = TRUE; + CurrentVbo -= 1; + } + + LastCluster = TRUE; + + if (FirstLboOfCurrentRun != 0 ) { + + DebugTrace( 0, Dbg, "Adding a run to the Mcb.\n", 0); + DebugTrace( 0, Dbg, " Vbo = %08lx.\n", FirstVboOfCurrentRun); + DebugTrace( 0, Dbg, " Lbo = %08lx.\n", FirstLboOfCurrentRun); + DebugTrace( 0, Dbg, " Length = %08lx.\n", CurrentVbo - FirstVboOfCurrentRun); + + (VOID)FatAddMcbEntry( Vcb, + &FcbOrDcb->Mcb, + FirstVboOfCurrentRun, + FirstLboOfCurrentRun, + CurrentVbo - FirstVboOfCurrentRun ); + + Runs += 1; + } + + // + // Being at the end of allocation, make sure we have found + // the Vbo. If we haven't, seeing as we checked VBO + // against AllocationSize, the real disk allocation is less + // than that of AllocationSize. This comes about when the + // real allocation is not yet known, and AllocaitonSize + // contains MAXULONG. + // + // KLUDGE! - If we were called by FatLookupFileAllocationSize + // Vbo is set to MAXULONG - 1, and AllocationSize to the lookup + // hint. Thus we merrily go along looking for a match that isn't + // there, but in the meantime building an Mcb. If this is + // the case, fill in AllocationSize and return. + // + + if ( Vbo == MAXULONG - 1 ) { + + *Allocated = FALSE; + + FcbOrDcb->Header.AllocationSize.QuadPart = CurrentVbo; + + DebugTrace( 0, Dbg, "New file allocation size = %08lx.\n", CurrentVbo); + try_return ( NOTHING ); + } + + // + // We will lie ever so slightly if we really terminated on the + // maximal byte of a file. It is really allocated. + // + + if (Vbo >= CurrentVbo && !*EndOnMax) { + + *Allocated = FALSE; + try_return ( NOTHING ); + } + + break; + + // + // This is a continuation in the chain. If the run has a + // discontiguity at this point, update the Mcb, and if we are beyond + // the desired Vbo, this is the end of the run, so set LastCluster + // and exit the loop. + // + + case FatClusterNext: + + // + // This is the loop check. The Vbo must not be bigger than the size of + // the volume, and the Vbo must not have a) wrapped and b) not been at the + // very last cluster in the chain, for the case of the maximal file. + // + + if ( CurrentVbo == 0 || + (BytesOnVolume.HighPart == 0 && CurrentVbo > BytesOnVolume.LowPart)) { + + FatPopUpFileCorrupt( IrpContext, FcbOrDcb ); + FatRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); + } + + if ( PriorLbo + BytesPerCluster != CurrentLbo ) { + + // + // Note that on the first time through the loop + // (FirstLboOfCurrentRun == 0), we don't add the + // run to the Mcb since it curresponds to the last + // run already stored in the Mcb. + // + + if ( FirstLboOfCurrentRun != 0 ) { + + DebugTrace( 0, Dbg, "Adding a run to the Mcb.\n", 0); + DebugTrace( 0, Dbg, " Vbo = %08lx.\n", FirstVboOfCurrentRun); + DebugTrace( 0, Dbg, " Lbo = %08lx.\n", FirstLboOfCurrentRun); + DebugTrace( 0, Dbg, " Length = %08lx.\n", CurrentVbo - FirstVboOfCurrentRun); + + FatAddMcbEntry( Vcb, + &FcbOrDcb->Mcb, + FirstVboOfCurrentRun, + FirstLboOfCurrentRun, + CurrentVbo - FirstVboOfCurrentRun ); + + Runs += 1; + } + + // + // Since we are at a run boundry, with CurrentLbo and + // CurrentVbo being the first cluster of the next run, + // we see if the run we just added encompases the desired + // Vbo, and if so exit. Otherwise we set up two new + // First*boOfCurrentRun, and continue. + // + + if (CurrentVbo > Vbo) { + + LastCluster = TRUE; + + } else { + + FirstVboOfCurrentRun = CurrentVbo; + FirstLboOfCurrentRun = CurrentLbo; + } + } + break; + + default: + + DebugTrace(0, Dbg, "Illegal Cluster Type.\n", FatEntry); + +#pragma prefast( suppress: 28159, "we bugcheck here because our internal data structures are seriously corrupted if this happens" ) + FatBugCheck( 0, 0, 0 ); + + break; + + } // switch() + } // while() + + // + // Load up the return parameters. + // + // On exit from the loop, Vbo still contains the desired Vbo, and + // CurrentVbo is the first byte after the run that contained the + // desired Vbo. + // + + *Allocated = TRUE; + + *Lbo = FirstLboOfCurrentRun + (Vbo - FirstVboOfCurrentRun); + + *ByteCount = CurrentVbo - Vbo; + + if (ARGUMENT_PRESENT(Index)) { + + // + // Note that Runs only needs to be accurate with respect to where we + // ended. Since partial-lookup cases will occur without exclusive + // synchronization, the Mcb itself may be much bigger by now. + // + + *Index = Runs - 1; + } + + try_exit: NOTHING; + + } finally { + + DebugUnwind( FatLookupFileAllocation ); + + // + // We are done reading the Fat, so unpin the last page of fat + // that is hanging around + // + + FatUnpinBcb( IrpContext, Context.Bcb ); + + DebugTrace(-1, Dbg, "FatLookupFileAllocation -> (VOID)\n", 0); + } + + return; +} + + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatAddFileAllocation ( + IN PIRP_CONTEXT IrpContext, + IN PFCB FcbOrDcb, + IN PFILE_OBJECT FileObject OPTIONAL, + IN ULONG DesiredAllocationSize + ) + +/*++ + +Routine Description: + + This routine adds additional allocation to the specified file/directory. + Additional allocation is added by appending clusters to the file/directory. + + If the file already has a sufficient allocation then this procedure + is effectively a noop. + +Arguments: + + FcbOrDcb - Supplies the Fcb/Dcb of the file/directory being modified. + This parameter must not specify the root dcb. + + FileObject - If supplied inform the cache manager of the change. + + DesiredAllocationSize - Supplies the minimum size, in bytes, that we want + allocated to the file/directory. + +--*/ + +{ + PVCB Vcb; + LARGE_MCB NewMcb = {0}; + PLARGE_MCB McbToCleanup = NULL; + PDIRENT Dirent = NULL; + ULONG NewAllocation = 0; + PBCB Bcb = NULL; + BOOLEAN UnwindWeAllocatedDiskSpace = FALSE; + BOOLEAN UnwindAllocationSizeSet = FALSE; + BOOLEAN UnwindCacheManagerInformed = FALSE; + BOOLEAN UnwindWeInitializedMcb = FALSE; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatAddFileAllocation\n", 0); + DebugTrace( 0, Dbg, " FcbOrDcb = %p\n", FcbOrDcb); + DebugTrace( 0, Dbg, " DesiredAllocationSize = %8lx\n", DesiredAllocationSize); + + Vcb = FcbOrDcb->Vcb; + + // + // If we haven't yet set the correct AllocationSize, do so. + // + + if (FcbOrDcb->Header.AllocationSize.QuadPart == FCB_LOOKUP_ALLOCATIONSIZE_HINT) { + + FatLookupFileAllocationSize( IrpContext, FcbOrDcb ); + } + + + // + // Check for the benign case that the desired allocation is already + // within the allocation size. + // + + if (DesiredAllocationSize <= FcbOrDcb->Header.AllocationSize.LowPart) { + + DebugTrace(0, Dbg, "Desired size within current allocation.\n", 0); + + DebugTrace(-1, Dbg, "FatAddFileAllocation -> (VOID)\n", 0); + return; + } + + DebugTrace( 0, Dbg, "InitialAllocation = %08lx.\n", FcbOrDcb->Header.AllocationSize.LowPart); + + // + // Get a chunk of disk space that will fullfill our needs. If there + // was no initial allocation, start from the hint in the Vcb, otherwise + // try to allocate from the cluster after the initial allocation. + // + // If there was no initial allocation to the file, we can just use the + // Mcb in the FcbOrDcb, otherwise we have to use a new one, and merge + // it to the one in the FcbOrDcb. + // + + try { + + if (FcbOrDcb->Header.AllocationSize.LowPart == 0) { + + LBO FirstLboOfFile; + + NT_ASSERT( FcbOrDcb->FcbCondition == FcbGood ); + + FatGetDirentFromFcbOrDcb( IrpContext, + FcbOrDcb, + FALSE, + &Dirent, + &Bcb ); + // + // Set this dirty right now since this call can fail. + // + + FatSetDirtyBcb( IrpContext, Bcb, Vcb, TRUE ); + + FatAllocateDiskSpace( IrpContext, + Vcb, + 0, + &DesiredAllocationSize, + FALSE, + &FcbOrDcb->Mcb ); + + UnwindWeAllocatedDiskSpace = TRUE; + McbToCleanup = &FcbOrDcb->Mcb; + + // + // We have to update the dirent and FcbOrDcb copies of + // FirstClusterOfFile since before it was 0 + // + + FatLookupMcbEntry( FcbOrDcb->Vcb, + &FcbOrDcb->Mcb, + 0, + &FirstLboOfFile, + (PULONG)NULL, + NULL ); + + DebugTrace( 0, Dbg, "First Lbo of file will be %08lx.\n", FirstLboOfFile ); + + FcbOrDcb->FirstClusterOfFile = FatGetIndexFromLbo( Vcb, FirstLboOfFile ); + + Dirent->FirstClusterOfFile = (USHORT)FcbOrDcb->FirstClusterOfFile; + + if ( FatIsFat32(Vcb) ) { + + Dirent->FirstClusterOfFileHi = (USHORT)(FcbOrDcb->FirstClusterOfFile >> 16); + } + + // + // Note the size of the allocation we need to tell the cache manager about. + // + + NewAllocation = DesiredAllocationSize; + + } else { + + LBO LastAllocatedLbo; + VBO DontCare; + + // + // Get the first cluster following the current allocation. It is possible + // the Mcb is empty (or short, etc.) so we need to be slightly careful + // about making sure we don't lie with the hint. + // + + (void)FatLookupLastMcbEntry( FcbOrDcb->Vcb, &FcbOrDcb->Mcb, &DontCare, &LastAllocatedLbo, NULL ); + + // + // Try to get some disk space starting from there. + // + + NewAllocation = DesiredAllocationSize - FcbOrDcb->Header.AllocationSize.LowPart; + + FsRtlInitializeLargeMcb( &NewMcb, PagedPool ); + UnwindWeInitializedMcb = TRUE; + McbToCleanup = &NewMcb; + + FatAllocateDiskSpace( IrpContext, + Vcb, + (LastAllocatedLbo != ~0 ? + FatGetIndexFromLbo(Vcb,LastAllocatedLbo + 1) : + 0), + &NewAllocation, + FALSE, + &NewMcb ); + + UnwindWeAllocatedDiskSpace = TRUE; + } + + // + // Now that we increased the allocation of the file, mark it in the + // FcbOrDcb. Carefully prepare to handle an inability to grow the cache + // structures. + // + + FcbOrDcb->Header.AllocationSize.LowPart += NewAllocation; + + // + // Handle the maximal file case, where we may have just wrapped. Note + // that this must be the precise boundary case wrap, i.e. by one byte, + // so that the new allocation is actually one byte "less" as far as we're + // concerned. This is important for the extension case. + // + + if (FcbOrDcb->Header.AllocationSize.LowPart == 0) { + + NewAllocation -= 1; + FcbOrDcb->Header.AllocationSize.LowPart = 0xffffffff; + } + + UnwindAllocationSizeSet = TRUE; + + // + // Inform the cache manager to increase the section size + // + + if ( ARGUMENT_PRESENT(FileObject) && CcIsFileCached(FileObject) ) { + + CcSetFileSizes( FileObject, + (PCC_FILE_SIZES)&FcbOrDcb->Header.AllocationSize ); + UnwindCacheManagerInformed = TRUE; + } + + // + // In the extension case, we have held off actually gluing the new + // allocation onto the file. This simplifies exception cleanup since + // if it was already added and the section grow failed, we'd have to + // do extra work to unglue it. This way, we can assume that if we + // raise the only thing we need to do is deallocate the disk space. + // + // Merge the allocation now. + // + + if (FcbOrDcb->Header.AllocationSize.LowPart != NewAllocation) { + + // + // Tack the new Mcb onto the end of the FcbOrDcb one. + // + + FatMergeAllocation( IrpContext, + Vcb, + &FcbOrDcb->Mcb, + &NewMcb ); + } + + } finally { + + DebugUnwind( FatAddFileAllocation ); + + // + // Give FlushFileBuffer/Cleanup a clue here, regardless of success/fail.. + // + + SetFlag(FcbOrDcb->FcbState, FCB_STATE_FLUSH_FAT); + + // + // If we were dogged trying to complete this operation, we need to go + // back various things out. + // + + if (AbnormalTermination()) { + + // + // Pull off the allocation size we tried to add to this object if + // we failed to grow cache structures or Mcb structures. + // + + if (UnwindAllocationSizeSet) { + + FcbOrDcb->Header.AllocationSize.LowPart -= NewAllocation; + } + + if (UnwindCacheManagerInformed) { + + CcSetFileSizes( FileObject, + (PCC_FILE_SIZES)&FcbOrDcb->Header.AllocationSize ); + } + + // + // In the case of initial allocation, we used the Fcb's Mcb and have + // to clean that up as well as the FAT chain references. + // + + if (FcbOrDcb->Header.AllocationSize.LowPart == 0) { + + if (Dirent != NULL) { + + FcbOrDcb->FirstClusterOfFile = 0; + Dirent->FirstClusterOfFile = 0; + + if ( FatIsFat32(Vcb) ) { + + Dirent->FirstClusterOfFileHi = 0; + } + } + } + + // + // ... and drop the dirent Bcb if we got it. Do it now + // so we can afford to take the exception if we have to. + // + + FatUnpinBcb( IrpContext, Bcb ); + + try { + + // + // Note this can re-raise. + // + + if ( UnwindWeAllocatedDiskSpace ) { + + FatDeallocateDiskSpace( IrpContext, Vcb, McbToCleanup, FALSE ); + } + + } finally { + + // + // We always want to clean up the non-initial allocation temporary Mcb, + // otherwise we have the Fcb's Mcb and we just truncate it away. + // + + if (UnwindWeInitializedMcb == TRUE) { + + // + // Note that we already know a raise is in progress. No danger + // of encountering the normal case code below and doing this again. + // + + FsRtlUninitializeLargeMcb( McbToCleanup ); + + } else { + + if (McbToCleanup) { + + FsRtlTruncateLargeMcb( McbToCleanup, 0 ); + } + } + } + } + + DebugTrace(-1, Dbg, "FatAddFileAllocation -> (VOID)\n", 0); + } + + // + // Non-exceptional cleanup we always want to do. In handling the re-raise possibilities + // during exceptions we had to make sure these two steps always happened there beforehand. + // So now we handle the usual case. + // + + FatUnpinBcb( IrpContext, Bcb ); + + if (UnwindWeInitializedMcb == TRUE) { + + FsRtlUninitializeLargeMcb( &NewMcb ); + } +} + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatTruncateFileAllocation ( + IN PIRP_CONTEXT IrpContext, + IN PFCB FcbOrDcb, + IN ULONG DesiredAllocationSize, + IN BOOLEAN ForDeletion + ) + +/*++ + +Routine Description: + + This routine truncates the allocation to the specified file/directory. + + If the file is already smaller than the indicated size then this procedure + is effectively a noop. + + +Arguments: + + FcbOrDcb - Supplies the Fcb/Dcb of the file/directory being modified + This parameter must not specify the root dcb. + + DesiredAllocationSize - Supplies the maximum size, in bytes, that we want + allocated to the file/directory. It is rounded + up to the nearest cluster. + +Return Value: + + VOID - TRUE if the operation completed and FALSE if it had to + block but could not. + +--*/ + +{ + PVCB Vcb; + PBCB Bcb = NULL; + LARGE_MCB RemainingMcb = {0}; + ULONG BytesPerCluster; + PDIRENT Dirent = NULL; + BOOLEAN UpdatedDirent = FALSE; + + ULONG UnwindInitialAllocationSize; + ULONG UnwindInitialFirstClusterOfFile; + BOOLEAN UnwindWeAllocatedMcb = FALSE; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( ForDeletion ); + + Vcb = FcbOrDcb->Vcb; + + + DebugTrace(+1, Dbg, "FatTruncateFileAllocation\n", 0); + DebugTrace( 0, Dbg, " FcbOrDcb = %p\n", FcbOrDcb); + DebugTrace( 0, Dbg, " DesiredAllocationSize = %8lx\n", DesiredAllocationSize); + + // + // If the Fcb isn't in good condition, we have no business whacking around on + // the disk after "its" clusters. + // + // Inspired by a Prefix complaint. + // + + NT_ASSERT( FcbOrDcb->FcbCondition == FcbGood ); + + // + // If we haven't yet set the correct AllocationSize, do so. + // + + if (FcbOrDcb->Header.AllocationSize.QuadPart == FCB_LOOKUP_ALLOCATIONSIZE_HINT) { + + FatLookupFileAllocationSize( IrpContext, FcbOrDcb ); + } + + // + // Round up the Desired Allocation Size to the next cluster size + // + + BytesPerCluster = 1 << Vcb->AllocationSupport.LogOfBytesPerCluster; + + // + // Note if the desired allocation is zero, to distinguish this from + // the wrap case below. + // + + if (DesiredAllocationSize != 0) { + + DesiredAllocationSize = (DesiredAllocationSize + (BytesPerCluster - 1)) & + ~(BytesPerCluster - 1); + // + // Check for the benign case that the file is already smaller than + // the desired truncation. Note that if it wraps, then a) it was + // specifying an offset in the maximally allocatable cluster and + // b) we're not asking to extend the file, either. So stop. + // + + if (DesiredAllocationSize == 0 || + DesiredAllocationSize >= FcbOrDcb->Header.AllocationSize.LowPart) { + + DebugTrace(0, Dbg, "Desired size within current allocation.\n", 0); + + DebugTrace(-1, Dbg, "FatTruncateFileAllocation -> (VOID)\n", 0); + return; + } + + } + + // + // This is a no-op if the allocation size is already what we want. + // + + if (DesiredAllocationSize == FcbOrDcb->Header.AllocationSize.LowPart) { + + DebugTrace(0, Dbg, "Desired size equals current allocation.\n", 0); + DebugTrace(-1, Dbg, "FatTruncateFileAllocation -> (VOID)\n", 0); + return; + } + + UnwindInitialAllocationSize = FcbOrDcb->Header.AllocationSize.LowPart; + UnwindInitialFirstClusterOfFile = FcbOrDcb->FirstClusterOfFile; + + // + // Update the FcbOrDcb allocation size. If it is now zero, we have the + // additional task of modifying the FcbOrDcb and Dirent copies of + // FirstClusterInFile. + // + // Note that we must pin the dirent before actually deallocating the + // disk space since, in unwind, it would not be possible to reallocate + // deallocated disk space as someone else may have reallocated it and + // may cause an exception when you try to get some more disk space. + // Thus FatDeallocateDiskSpace must be the final dangerous operation. + // + + try { + + FcbOrDcb->Header.AllocationSize.QuadPart = DesiredAllocationSize; + + // + // Special case 0 + // + + if (DesiredAllocationSize == 0) { + + // + // We have to update the dirent and FcbOrDcb copies of + // FirstClusterOfFile since before it was 0 + // + + NT_ASSERT( FcbOrDcb->FcbCondition == FcbGood ); + + FatGetDirentFromFcbOrDcb( IrpContext, FcbOrDcb, FALSE, &Dirent, &Bcb ); + + Dirent->FirstClusterOfFile = 0; + + if (FatIsFat32(Vcb)) { + + Dirent->FirstClusterOfFileHi = 0; + } + + FcbOrDcb->FirstClusterOfFile = 0; + + FatSetDirtyBcb( IrpContext, Bcb, Vcb, TRUE ); + UpdatedDirent = TRUE; + + FatDeallocateDiskSpace( IrpContext, Vcb, &FcbOrDcb->Mcb, ((FcbOrDcb->FcbState & FCB_STATE_ZERO_ON_DEALLOCATION) != 0)); + + FatRemoveMcbEntry( FcbOrDcb->Vcb, &FcbOrDcb->Mcb, 0, 0xFFFFFFFF ); + + } else { + + // + // Split the existing allocation into two parts, one we will keep, and + // one we will deallocate. + // + + FsRtlInitializeLargeMcb( &RemainingMcb, PagedPool ); + UnwindWeAllocatedMcb = TRUE; + + FatSplitAllocation( IrpContext, + Vcb, + &FcbOrDcb->Mcb, + DesiredAllocationSize, + &RemainingMcb ); + + FatDeallocateDiskSpace( IrpContext, Vcb, &RemainingMcb, ((FcbOrDcb->FcbState & FCB_STATE_ZERO_ON_DEALLOCATION) != 0) ); + + FsRtlUninitializeLargeMcb( &RemainingMcb ); + } + + } finally { + + DebugUnwind( FatTruncateFileAllocation ); + + // + // Is this really the right backout strategy? It would be nice if we could + // pretend the truncate worked if we knew that the file had gotten into + // a consistent state. Leaving dangled clusters is probably quite preferable. + // + + if ( AbnormalTermination() ) { + + FcbOrDcb->Header.AllocationSize.LowPart = UnwindInitialAllocationSize; + + if ( (DesiredAllocationSize == 0) && (Dirent != NULL)) { + + if (UpdatedDirent) { + + // + // If the dirent has been updated ok and marked dirty, then we + // failed in deallocatediscspace, and don't know what state + // the on disc fat chain is in. So we throw away the mcb, + // and potentially loose a few clusters until the next + // chkdsk. The operation has succeeded, but the exception + // will still propogate. 5.1 + // + + FatRemoveMcbEntry( Vcb, &FcbOrDcb->Mcb, 0, 0xFFFFFFFF ); + FcbOrDcb->Header.AllocationSize.QuadPart = 0; + } + else if (FcbOrDcb->FirstClusterOfFile == 0) { + + Dirent->FirstClusterOfFile = (USHORT)UnwindInitialFirstClusterOfFile; + + if ( FatIsFat32(Vcb) ) { + + Dirent->FirstClusterOfFileHi = + (USHORT)(UnwindInitialFirstClusterOfFile >> 16); + } + + FcbOrDcb->FirstClusterOfFile = UnwindInitialFirstClusterOfFile; + } + } + + if ( UnwindWeAllocatedMcb ) { + + FsRtlUninitializeLargeMcb( &RemainingMcb ); + } + + // + // Note that in the non zero truncation case, we will also + // leak clusters. However, apart from this, the in memory and on disc + // structures will agree. + } + + FatUnpinBcb( IrpContext, Bcb ); + + // + // Give FlushFileBuffer/Cleanup a clue here, regardless of success/fail. + // + + SetFlag(FcbOrDcb->FcbState, FCB_STATE_FLUSH_FAT); + + DebugTrace(-1, Dbg, "FatTruncateFileAllocation -> (VOID)\n", 0); + } +} + + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatLookupFileAllocationSize ( + IN PIRP_CONTEXT IrpContext, + IN PFCB FcbOrDcb + ) + +/*++ + +Routine Description: + + This routine retrieves the current file allocatio size for the + specified file/directory. + +Arguments: + + FcbOrDcb - Supplies the Fcb/Dcb of the file/directory being modified + +--*/ + +{ + LBO Lbo; + ULONG ByteCount; + BOOLEAN DontCare; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatLookupAllocationSize\n", 0); + DebugTrace( 0, Dbg, " FcbOrDcb = %p\n", FcbOrDcb); + + // + // We call FatLookupFileAllocation with Vbo of 0xffffffff - 1. + // + + FatLookupFileAllocation( IrpContext, + FcbOrDcb, + MAXULONG - 1, + &Lbo, + &ByteCount, + &DontCare, + &DontCare, + NULL ); + + // + // FileSize was set at Fcb creation time from the contents of the directory entry, + // and we are only now looking up the real length of the allocation chain. If it + // cannot be contained, this is trash. Probably more where that came from. + // + + if (FcbOrDcb->Header.FileSize.LowPart > FcbOrDcb->Header.AllocationSize.LowPart) { + + FatPopUpFileCorrupt( IrpContext, FcbOrDcb ); + FatRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); + } + + DebugTrace(-1, Dbg, "FatLookupFileAllocationSize -> (VOID)\n", 0); + return; +} + + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatAllocateDiskSpace ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN ULONG AbsoluteClusterHint, + IN PULONG ByteCount, + IN BOOLEAN ExactMatchRequired, + OUT PLARGE_MCB Mcb + ) + +/*++ + +Routine Description: + + This procedure allocates additional disk space and builds an mcb + representing the newly allocated space. If the space cannot be + allocated then this procedure raises an appropriate status. + + Searching starts from the hint index in the Vcb unless an alternative + non-zero hint is given in AlternateClusterHint. If we are using the + hint field in the Vcb, it is set to the cluster following our allocation + when we are done. + + Disk space can only be allocated in cluster units so this procedure + will round up any byte count to the next cluster boundary. + + Pictorially what is done is the following (where ! denotes the end of + the fat chain (i.e., FAT_CLUSTER_LAST)): + + + Mcb (empty) + + becomes + + Mcb |--a--|--b--|--c--! + + ^ + ByteCount ----------+ + +Arguments: + + Vcb - Supplies the VCB being modified + + AbsoluteClusterHint - Supplies an alternate hint index to start the + search from. If this is zero we use, and update, + the Vcb hint field. + + ByteCount - Supplies the number of bytes that we are requesting, and + receives the number of bytes that we got. + + ExactMatchRequired - Caller should set this to TRUE if only the precise run requested + is acceptable. + + Mcb - Receives the MCB describing the newly allocated disk space. The + caller passes in an initialized Mcb that is filled in by this procedure. + + Return Value: + + TRUE - Allocated ok + FALSE - Failed to allocate exactly as requested (=> ExactMatchRequired was TRUE) + +--*/ + +{ + UCHAR LogOfBytesPerCluster; + ULONG BytesPerCluster; + ULONG StartingCluster; + ULONG ClusterCount; + ULONG WindowRelativeHint; +#if DBG + ULONG PreviousClear = 0; +#endif + + PFAT_WINDOW Window; + BOOLEAN Wait = FALSE; + BOOLEAN Result = TRUE; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatAllocateDiskSpace\n", 0); + DebugTrace( 0, Dbg, " Vcb = %p\n", Vcb); + DebugTrace( 0, Dbg, " *ByteCount = %8lx\n", *ByteCount); + DebugTrace( 0, Dbg, " Mcb = %p\n", Mcb); + DebugTrace( 0, Dbg, " Hint = %8lx\n", AbsoluteClusterHint); + + NT_ASSERT((AbsoluteClusterHint <= Vcb->AllocationSupport.NumberOfClusters + 2) && (1 != AbsoluteClusterHint)); + + // + // Make sure byte count is not zero + // + + if (*ByteCount == 0) { + + DebugTrace(0, Dbg, "Nothing to allocate.\n", 0); + + DebugTrace(-1, Dbg, "FatAllocateDiskSpace -> (VOID)\n", 0); + return; + } + + // + // Compute the cluster count based on the byte count, rounding up + // to the next cluster if there is any remainder. Note that the + // pathalogical case BytesCount == 0 has been eliminated above. + // + + LogOfBytesPerCluster = Vcb->AllocationSupport.LogOfBytesPerCluster; + BytesPerCluster = 1 << LogOfBytesPerCluster; + + *ByteCount = (*ByteCount + (BytesPerCluster - 1)) + & ~(BytesPerCluster - 1); + + // + // If ByteCount is NOW zero, then we were asked for the maximal + // filesize (or at least for bytes in the last allocatable sector). + // + + if (*ByteCount == 0) { + + *ByteCount = 0xffffffff; + ClusterCount = 1 << (32 - LogOfBytesPerCluster); + + } else { + + ClusterCount = (*ByteCount >> LogOfBytesPerCluster); + } + + // + // Analysis tools don't figure out that ClusterCount is not zero because + // of the ByteCount == 0 checks, so give them a hint. + // + _Analysis_assume_(ClusterCount > 0); + + // + // Make sure there are enough free clusters to start with, and + // take them now so that nobody else takes them from us. + // + + ExAcquireResourceSharedLite(&Vcb->ChangeBitMapResource, TRUE); + FatLockFreeClusterBitMap( Vcb ); + + if (ClusterCount <= Vcb->AllocationSupport.NumberOfFreeClusters) { + + Vcb->AllocationSupport.NumberOfFreeClusters -= ClusterCount; + + } else { + + FatUnlockFreeClusterBitMap( Vcb ); + ExReleaseResourceLite(&Vcb->ChangeBitMapResource); + + DebugTrace(0, Dbg, "Disk Full. Raise Status.\n", 0); + FatRaiseStatus( IrpContext, STATUS_DISK_FULL ); + } + + // + // Did the caller supply a hint? + // + + if ((0 != AbsoluteClusterHint) && (AbsoluteClusterHint < (Vcb->AllocationSupport.NumberOfClusters + 2))) { + + if (Vcb->NumberOfWindows > 1) { + + // + // If we're being called upon to allocate clusters outside the + // current window (which happens only via MoveFile), it's a problem. + // We address this by changing the current window to be the one which + // contains the alternate cluster hint. Note that if the user's + // request would cross a window boundary, he doesn't really get what + // he wanted. + // + + if (AbsoluteClusterHint < Vcb->CurrentWindow->FirstCluster || + AbsoluteClusterHint > Vcb->CurrentWindow->LastCluster) { + + ULONG BucketNum = FatWindowOfCluster( AbsoluteClusterHint ); + + NT_ASSERT( BucketNum < Vcb->NumberOfWindows); + + // + // Drop our shared lock on the ChangeBitMapResource, and pick it up again + // exclusive in preparation for making the window swap. + // + + FatUnlockFreeClusterBitMap(Vcb); + ExReleaseResourceLite(&Vcb->ChangeBitMapResource); + ExAcquireResourceExclusiveLite(&Vcb->ChangeBitMapResource, TRUE); + FatLockFreeClusterBitMap(Vcb); + + Window = &Vcb->Windows[BucketNum]; + + // + // Again, test the current window against the one we want - some other + // thread could have sneaked in behind our backs and kindly set it to the one + // we need, when we dropped and reacquired the ChangeBitMapResource above. + // + + if (Window != Vcb->CurrentWindow) { + + try { + + Wait = BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT); + SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT); + + // + // Change to the new window (update Vcb->CurrentWindow) and scan it + // to build up a freespace bitmap etc. + // + + FatExamineFatEntries( IrpContext, Vcb, + 0, + 0, + FALSE, + Window, + NULL); + + } finally { + + if (!Wait) { + + ClearFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT); + } + + if (AbnormalTermination()) { + + // + // We will have raised as a result of failing to pick up the + // chunk of the FAT for this window move. Release our resources + // and return the cluster count to the volume. + // + + Vcb->AllocationSupport.NumberOfFreeClusters += ClusterCount; + + FatUnlockFreeClusterBitMap( Vcb ); + ExReleaseResourceLite(&Vcb->ChangeBitMapResource); + } + } + } + } + + // + // Make the hint cluster number relative to the base of the current window... + // + // Currentwindow->Firstcluster is baised by +2 already, so we will lose the + // bias already in AbsoluteClusterHint. Put it back.... + // + + WindowRelativeHint = AbsoluteClusterHint - Vcb->CurrentWindow->FirstCluster + 2; + } + else { + + // + // Only one 'window', ie fat16/12. No modification necessary. + // + + WindowRelativeHint = AbsoluteClusterHint; + } + } + else { + + // + // Either no hint supplied, or it was out of range, so grab one from the Vcb + // + // NOTE: Clusterhint in the Vcb is not guaranteed to be set (may be -1) + // + + WindowRelativeHint = Vcb->ClusterHint; + AbsoluteClusterHint = 0; + + // + // Vcb hint may not have been initialized yet. Force to valid cluster. + // + + if (-1 == WindowRelativeHint) { + + WindowRelativeHint = 2; + } + } + + NT_ASSERT((WindowRelativeHint >= 2) && (WindowRelativeHint < Vcb->FreeClusterBitMap.SizeOfBitMap + 2)); + + // + // Keep track of the window we're allocating from, so we can clean + // up correctly if the current window changes after we unlock the + // bitmap. + // + + Window = Vcb->CurrentWindow; + + // + // Try to find a run of free clusters large enough for us. + // + + StartingCluster = FatFindFreeClusterRun( IrpContext, + Vcb, + ClusterCount, + WindowRelativeHint ); + // + // If the above call was successful, we can just update the fat + // and Mcb and exit. Otherwise we have to look for smaller free + // runs. + // + // This test is a bit funky. Note that the error return from + // RtlFindClearBits is -1, and adding two to that is 1. + // + + if ((StartingCluster != 1) && + ((0 == AbsoluteClusterHint) || (StartingCluster == WindowRelativeHint)) + ) { + +#if DBG + PreviousClear = RtlNumberOfClearBits( &Vcb->FreeClusterBitMap ); +#endif // DBG + + // + // Take the clusters we found, and unlock the bit map. + // + + FatReserveClusters(IrpContext, Vcb, StartingCluster, ClusterCount); + + Window->ClustersFree -= ClusterCount; + + StartingCluster += Window->FirstCluster; + StartingCluster -= 2; + + NT_ASSERT( PreviousClear - ClusterCount == Window->ClustersFree ); + + FatUnlockFreeClusterBitMap( Vcb ); + + // + // Note that this call will never fail since there is always + // room for one entry in an empty Mcb. + // + + FatAddMcbEntry( Vcb, Mcb, + 0, + FatGetLboFromIndex( Vcb, StartingCluster ), + *ByteCount); + try { + + // + // Update the fat. + // + + FatAllocateClusters(IrpContext, Vcb, + StartingCluster, + ClusterCount); + + } finally { + + DebugUnwind( FatAllocateDiskSpace ); + + // + // If the allocate clusters failed, remove the run from the Mcb, + // unreserve the clusters, and reset the free cluster count. + // + + if (AbnormalTermination()) { + + FatRemoveMcbEntry( Vcb, Mcb, 0, *ByteCount ); + + FatLockFreeClusterBitMap( Vcb ); + + // Only clear bits if the bitmap window is the same. + + if (Window == Vcb->CurrentWindow) { + + // Both values (startingcluster and window->firstcluster) are + // already biased by 2, so will cancel, so we need to add in the 2 again. + + FatUnreserveClusters( IrpContext, Vcb, + StartingCluster - Window->FirstCluster + 2, + ClusterCount ); + } + + Window->ClustersFree += ClusterCount; + Vcb->AllocationSupport.NumberOfFreeClusters += ClusterCount; + + FatUnlockFreeClusterBitMap( Vcb ); + } + + ExReleaseResourceLite(&Vcb->ChangeBitMapResource); + } + + } else { + + // + // Note that Index is a zero-based window-relative number. When appropriate + // it'll get converted into a true cluster number and put in Cluster, which + // will be a volume relative true cluster number. + // + + ULONG Index = 0; + ULONG Cluster = 0; + ULONG CurrentVbo = 0; + ULONG PriorLastCluster = 0; + ULONG BytesFound = 0; + + ULONG ClustersFound = 0; + ULONG ClustersRemaining = 0; + + BOOLEAN LockedBitMap = FALSE; + BOOLEAN SelectNextContigWindow = FALSE; + + // + // Drop our shared lock on the ChangeBitMapResource, and pick it up again + // exclusive in preparation for making a window swap. + // + + FatUnlockFreeClusterBitMap(Vcb); + ExReleaseResourceLite(&Vcb->ChangeBitMapResource); + ExAcquireResourceExclusiveLite(&Vcb->ChangeBitMapResource, TRUE); + FatLockFreeClusterBitMap(Vcb); + LockedBitMap = TRUE; + + try { + + if ( ExactMatchRequired && (1 == Vcb->NumberOfWindows)) { + + // + // Give up right now, there are no more windows to search! RtlFindClearBits + // searchs the whole bitmap, so we would have found any contiguous run + // large enough. + // + + try_leave( Result = FALSE); + } + + // + // While the request is still incomplete, look for the largest + // run of free clusters, mark them taken, allocate the run in + // the Mcb and Fat, and if this isn't the first time through + // the loop link it to prior run on the fat. The Mcb will + // coalesce automatically. + // + + ClustersRemaining = ClusterCount; + CurrentVbo = 0; + PriorLastCluster = 0; + + while (ClustersRemaining != 0) { + + // + // If we just entered the loop, the bit map is already locked + // + + if ( !LockedBitMap ) { + + FatLockFreeClusterBitMap( Vcb ); + LockedBitMap = TRUE; + } + + // + // Find the largest run of free clusters. If the run is + // bigger than we need, only use what we need. Note that + // this will then be the last while() iteration. + // + + // 12/3/95: need to bias bitmap by 2 bits for the defrag + // hooks and the below macro became impossible to do without in-line + // procedures. + // + // ClustersFound = FatLongestFreeClusterRun( IrpContext, Vcb, &Index ); + + ClustersFound = 0; + + if (!SelectNextContigWindow) { + + if ( 0 != WindowRelativeHint) { + + ULONG Desired = Vcb->FreeClusterBitMap.SizeOfBitMap - (WindowRelativeHint - 2); + + // + // We will try to allocate contiguously. Try from the current hint the to + // end of current window. Don't try for more than we actually need. + // + + if (Desired > ClustersRemaining) { + + Desired = ClustersRemaining; + } + + if (RtlAreBitsClear( &Vcb->FreeClusterBitMap, + WindowRelativeHint - 2, + Desired)) + { + // + // Clusters from hint->...windowend are free. Take them. + // + + Index = WindowRelativeHint - 2; + ClustersFound = Desired; + + if (FatIsFat32(Vcb)) { + + // + // We're now up against the end of the current window, so indicate that we + // want the next window in the sequence next time around. (If we're not up + // against the end of the window, then we got what we needed and won't be + // coming around again anyway). + // + + SelectNextContigWindow = TRUE; + WindowRelativeHint = 2; + } + else { + + // + // FAT 12/16 - we've run up against the end of the volume. Clear the + // hint, since we now have no idea where to look. + // + + WindowRelativeHint = 0; + } +#if DBG + PreviousClear = RtlNumberOfClearBits( &Vcb->FreeClusterBitMap ); +#endif // DBG + } + else { + + if (ExactMatchRequired) { + + // + // If our caller required an exact match, then we're hosed. Bail out now. + // + + try_leave( Result = FALSE); + } + + // + // Hint failed, drop back to pot luck + // + + WindowRelativeHint = 0; + } + } + + if ((0 == WindowRelativeHint) && (0 == ClustersFound)) { + + if (ClustersRemaining <= Vcb->CurrentWindow->ClustersFree) { + + // + // The remaining allocation could be satisfied entirely from this + // window. We will ask only for what we need, to try and avoid + // unnecessarily fragmenting large runs of space by always using + // (part of) the largest run we can find. This call will return the + // first run large enough. + // + + Index = RtlFindClearBits( &Vcb->FreeClusterBitMap, ClustersRemaining, 0); + + if (-1 != Index) { + + ClustersFound = ClustersRemaining; + } + } + + if (0 == ClustersFound) { + + // + // Still nothing, so just take the largest free run we can find. + // + + ClustersFound = RtlFindLongestRunClear( &Vcb->FreeClusterBitMap, &Index ); + + } +#if DBG + PreviousClear = RtlNumberOfClearBits( &Vcb->FreeClusterBitMap ); +#endif // DBG + if (ClustersFound >= ClustersRemaining) { + + ClustersFound = ClustersRemaining; + } + else { + + // + // If we just ran up to the end of a window, set up a hint that + // we'd like the next consecutive window after this one. (FAT32 only) + // + + if ( ((Index + ClustersFound) == Vcb->FreeClusterBitMap.SizeOfBitMap) && + FatIsFat32( Vcb) + ) { + + SelectNextContigWindow = TRUE; + WindowRelativeHint = 2; + } + } + } + } + + if (ClustersFound == 0) { + + ULONG FaveWindow = 0; + BOOLEAN SelectedWindow; + + // + // If we found no free clusters on a single-window FAT, + // there was a bad problem with the free cluster count. + // + + if (1 == Vcb->NumberOfWindows) { + +#pragma prefast( suppress: 28159, "we bugcheck here because our internal data structures are seriously corrupted if this happens" ) + FatBugCheck( 0, 5, 0 ); + } + + // + // Switch to a new bucket. Possibly the next one if we're + // currently on a roll (allocating contiguously) + // + + SelectedWindow = FALSE; + + if ( SelectNextContigWindow) { + + ULONG NextWindow; + + NextWindow = (((ULONG)((PUCHAR)Vcb->CurrentWindow - (PUCHAR)Vcb->Windows)) / sizeof( FAT_WINDOW)) + 1; + + if ((NextWindow < Vcb->NumberOfWindows) && + ( Vcb->Windows[ NextWindow].ClustersFree > 0) + ) { + + FaveWindow = NextWindow; + SelectedWindow = TRUE; + } + else { + + if (ExactMatchRequired) { + + // + // Some dope tried to allocate a run past the end of the volume... + // + + try_leave( Result = FALSE); + } + + // + // Give up on the contiguous allocation attempts + // + + WindowRelativeHint = 0; + } + + SelectNextContigWindow = FALSE; + } + + if (!SelectedWindow) { + + // + // Select a new window to begin allocating from + // + + FaveWindow = FatSelectBestWindow( Vcb); + } + + // + // By now we'd better have found a window with some free clusters + // + + if (0 == Vcb->Windows[ FaveWindow].ClustersFree) { + +#pragma prefast( suppress: 28159, "we bugcheck here because our internal data structures are seriously corrupted if this happens" ) + FatBugCheck( 0, 5, 1 ); + } + + Wait = BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT); + SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT); + + FatExamineFatEntries( IrpContext, Vcb, + 0, + 0, + FALSE, + &Vcb->Windows[FaveWindow], + NULL); + + if (!Wait) { + + ClearFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT); + } + + // + // Now we'll just go around the loop again, having switched windows, + // and allocate.... + // +#if DBG + PreviousClear = RtlNumberOfClearBits( &Vcb->FreeClusterBitMap ); +#endif //DBG + } // if (clustersfound == 0) + else { + + // + // Take the clusters we found, convert our index to a cluster number + // and unlock the bit map. + // + + Window = Vcb->CurrentWindow; + + FatReserveClusters( IrpContext, Vcb, (Index + 2), ClustersFound ); + + Cluster = Index + Window->FirstCluster; + + Window->ClustersFree -= ClustersFound; + NT_ASSERT( PreviousClear - ClustersFound == Window->ClustersFree ); + + FatUnlockFreeClusterBitMap( Vcb ); + LockedBitMap = FALSE; + + // + // Add the newly alloced run to the Mcb. + // + + BytesFound = ClustersFound << LogOfBytesPerCluster; + + FatAddMcbEntry( Vcb, Mcb, + CurrentVbo, + FatGetLboFromIndex( Vcb, Cluster ), + BytesFound ); + + // + // Connect the last allocated run with this one, and allocate + // this run on the Fat. + // + + if (PriorLastCluster != 0) { + + FatSetFatEntry( IrpContext, + Vcb, + PriorLastCluster, + (FAT_ENTRY)Cluster ); + } + + // + // Update the fat + // + + FatAllocateClusters( IrpContext, Vcb, Cluster, ClustersFound ); + + // + // Prepare for the next iteration. + // + + CurrentVbo += BytesFound; + ClustersRemaining -= ClustersFound; + PriorLastCluster = Cluster + ClustersFound - 1; + } + } // while (clustersremaining) + + } finally { + + DebugUnwind( FatAllocateDiskSpace ); + + ExReleaseResourceLite(&Vcb->ChangeBitMapResource); + + // + // Is there any unwinding to do? + // + + if ( AbnormalTermination() || (FALSE == Result)) { + + // + // Flag to the caller that they're getting nothing + // + + *ByteCount = 0; + + // + // There are three places we could have taken this exception: + // when switching the window (FatExamineFatEntries), adding + // a found run to the Mcb (FatAddMcbEntry), or when writing + // the changes to the FAT (FatSetFatEntry). In the first case + // we don't have anything to unwind before deallocation, and + // can detect this by seeing if we have the ClusterBitmap + // mutex out. + + if (!LockedBitMap) { + + FatLockFreeClusterBitMap( Vcb ); + + // + // In these cases, we have the possiblity that the FAT + // window is still in place and we need to clear the bits. + // If the Mcb entry isn't there (we raised trying to add + // it), the effect of trying to remove it is a noop. + // + + if (Window == Vcb->CurrentWindow) { + + // + // Cluster reservation works on cluster 2 based window-relative + // numbers, so we must convert. The subtraction will lose the + // cluster 2 base, so bias the result. + // + + FatUnreserveClusters( IrpContext, Vcb, + (Cluster - Window->FirstCluster) + 2, + ClustersFound ); + } + + // + // Note that FatDeallocateDiskSpace will take care of adjusting + // to account for the entries in the Mcb. All we have to account + // for is the last run that didn't make it. + // + + Window->ClustersFree += ClustersFound; + Vcb->AllocationSupport.NumberOfFreeClusters += ClustersFound; + + FatUnlockFreeClusterBitMap( Vcb ); + + FatRemoveMcbEntry( Vcb, Mcb, CurrentVbo, BytesFound ); + + } else { + + // + // Just drop the mutex now - we didn't manage to do anything + // that needs to be backed out. + // + + FatUnlockFreeClusterBitMap( Vcb ); + } + + try { + + // + // Now we have tidied up, we are ready to just send the Mcb + // off to deallocate disk space + // + + FatDeallocateDiskSpace( IrpContext, Vcb, Mcb, FALSE ); + + } finally { + + // + // Now finally (really), remove all the entries from the mcb + // + + FatRemoveMcbEntry( Vcb, Mcb, 0, 0xFFFFFFFF ); + } + } + + DebugTrace(-1, Dbg, "FatAllocateDiskSpace -> (VOID)\n", 0); + + } // finally + } + + return; +} + + + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatDeallocateDiskSpace ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PLARGE_MCB Mcb, + IN BOOLEAN ZeroOnDeallocate + ) + +/*++ + +Routine Description: + + This procedure deallocates the disk space denoted by an input + mcb. Note that the input MCB does not need to necessarily describe + a chain that ends with a FAT_CLUSTER_LAST entry. + + Pictorially what is done is the following + + Fat |--a--|--b--|--c--| + Mcb |--a--|--b--|--c--| + + becomes + + Fat |--0--|--0--|--0--| + Mcb |--a--|--b--|--c--| + +Arguments: + + Vcb - Supplies the VCB being modified + + Mcb - Supplies the MCB describing the disk space to deallocate. Note + that Mcb is unchanged by this procedure. + + +Return Value: + + None. + +--*/ + +{ + LBO Lbo; + VBO Vbo; + + ULONG RunsInMcb; + ULONG ByteCount; + ULONG ClusterCount = 0; + ULONG ClusterIndex = 0; + ULONG McbIndex = 0; + + UCHAR LogOfBytesPerCluster; + + PFAT_WINDOW Window; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatDeallocateDiskSpace\n", 0); + DebugTrace( 0, Dbg, " Vcb = %p\n", Vcb); + DebugTrace( 0, Dbg, " Mcb = %p\n", Mcb); + + LogOfBytesPerCluster = Vcb->AllocationSupport.LogOfBytesPerCluster; + + RunsInMcb = FsRtlNumberOfRunsInLargeMcb( Mcb ); + + if ( RunsInMcb == 0 ) { + + DebugTrace(-1, Dbg, "FatDeallocateDiskSpace -> (VOID)\n", 0); + return; + } + + // + // If we are supposed to zero out the allocation before freeing it, do so. + // + + if (ZeroOnDeallocate) { + + NTSTATUS Status = STATUS_SUCCESS; + PIRP IoIrp; + KEVENT IoEvent; + IO_STATUS_BLOCK Iosb; + PVOID Buffer = NULL; + PMDL Mdl; + ULONG ByteCountToZero; + ULONG MdlSizeMapped; + + // + // Issue the writes down for each run in the Mcb + // + + KeInitializeEvent( &IoEvent, + NotificationEvent, + FALSE ); + + for ( McbIndex = 0; McbIndex < RunsInMcb; McbIndex++ ) { + + FatGetNextMcbEntry( Vcb, Mcb, McbIndex, &Vbo, &Lbo, &ByteCount ); + + // + // Assert that Fat files have no holes. + // + + NT_ASSERT( Lbo != 0 ); + + // + // Setup our MDL for the this run. + // + + Mdl = FatBuildZeroMdl( IrpContext, ByteCount); + if (!Mdl) { + FatNormalizeAndRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES); + } + + try { + + // + // Map the MDL. + // + + Buffer = MmGetSystemAddressForMdlSafe(Mdl, HighPagePriority|MdlMappingNoExecute); + if (!Buffer) { + FatNormalizeAndRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES); + } + + // + // We might not have not been able to get an MDL big enough to map the whole + // run. In this case, break up the write. + // + + MdlSizeMapped = min( ByteCount, Mdl->ByteCount ); + ByteCountToZero = ByteCount; + + // + // Loop until there are no bytes left to write + // + + while (ByteCountToZero != 0) { + + // + // Write zeros to each run. + // + + KeClearEvent( &IoEvent ); + + IoIrp = IoBuildSynchronousFsdRequest( IRP_MJ_WRITE, + Vcb->TargetDeviceObject, + Buffer, + MdlSizeMapped, + (PLARGE_INTEGER)&Lbo, + &IoEvent, + &Iosb ); + + if (IoIrp == NULL) { + + FatRaiseStatus( IrpContext, + STATUS_INSUFFICIENT_RESOURCES ); + } + + // + // Set a flag indicating that we want to write through any + // cache on the controller. This eliminates the need for + // an explicit flush-device after the write. + // + + SetFlag( IoGetNextIrpStackLocation(IoIrp)->Flags, SL_WRITE_THROUGH ); + + Status = IoCallDriver( Vcb->TargetDeviceObject, IoIrp ); + + if (Status == STATUS_PENDING) { + + (VOID)KeWaitForSingleObject( &IoEvent, + Executive, + KernelMode, + FALSE, + (PLARGE_INTEGER)NULL ); + + Status = Iosb.Status; + } + + if (!NT_SUCCESS( Status )) { + + FatNormalizeAndRaiseStatus( IrpContext, + Status ); + } + + // + // Decrement ByteCount + // + + if (MdlSizeMapped <= ByteCountToZero) { + ByteCountToZero -= MdlSizeMapped; + } else { + MdlSizeMapped = ByteCountToZero; + } + } + + } finally { + + if (!FlagOn( Mdl->MdlFlags, MDL_SOURCE_IS_NONPAGED_POOL) && + FlagOn( Mdl->MdlFlags, MDL_MAPPED_TO_SYSTEM_VA )) { + + MmUnmapLockedPages( Mdl->MappedSystemVa, Mdl ); + } + IoFreeMdl( Mdl ); + } + + } + + } + + try { + + // + // Run though the Mcb, freeing all the runs in the fat. + // + // We do this in two steps (first update the fat, then the bitmap + // (which can't fail)) to prevent other people from taking clusters + // that we need to re-allocate in the event of unwind. + // + + ExAcquireResourceSharedLite(&Vcb->ChangeBitMapResource, TRUE); + + RunsInMcb = FsRtlNumberOfRunsInLargeMcb( Mcb ); + + for ( McbIndex = 0; McbIndex < RunsInMcb; McbIndex++ ) { + + FatGetNextMcbEntry( Vcb, Mcb, McbIndex, &Vbo, &Lbo, &ByteCount ); + + // + // Assert that Fat files have no holes. + // + + NT_ASSERT( Lbo != 0 ); + + // + // Write FAT_CLUSTER_AVAILABLE to each cluster in the run. + // + + if (ByteCount == 0xFFFFFFFF) { + + // + // Special case the computation of ClusterCout + // when file is of max size (4GiB - 1). + // + + ClusterCount = (1 << (32 - LogOfBytesPerCluster)); + + } else { + + ClusterCount = ByteCount >> LogOfBytesPerCluster; + } + + ClusterIndex = FatGetIndexFromLbo( Vcb, Lbo ); + + FatFreeClusters( IrpContext, Vcb, ClusterIndex, ClusterCount ); + } + + // + // From now on, nothing can go wrong .... (as in raise) + // + + FatLockFreeClusterBitMap( Vcb ); + + for ( McbIndex = 0; McbIndex < RunsInMcb; McbIndex++ ) { + + ULONG ClusterEnd; + ULONG MyStart, MyLength, count; +#if DBG + ULONG PreviousClear = 0; + ULONG i = 0; +#endif + + FatGetNextMcbEntry( Vcb, Mcb, McbIndex, &Vbo, &Lbo, &ByteCount ); + + // + // Mark the bits clear in the FreeClusterBitMap. + // + + if (ByteCount == 0xFFFFFFFF) { + + // + // Special case the computation of ClusterCout + // when file is of max size (2^32 - 1). + // + + ClusterCount = (1 << (32 - LogOfBytesPerCluster)); + + } else { + + ClusterCount = ByteCount >> LogOfBytesPerCluster; + } + + ClusterIndex = FatGetIndexFromLbo( Vcb, Lbo ); + + Window = Vcb->CurrentWindow; + + // + // If we've divided the bitmap, elide bitmap manipulation for + // runs that are outside the current bucket. + // + + ClusterEnd = ClusterIndex + ClusterCount - 1; + + if (!(ClusterIndex > Window->LastCluster || + ClusterEnd < Window->FirstCluster)) { + + // + // The run being freed overlaps the current bucket, so we'll + // have to clear some bits. + // + + if (ClusterIndex < Window->FirstCluster && + ClusterEnd > Window->LastCluster) { + + MyStart = Window->FirstCluster; + MyLength = Window->LastCluster - Window->FirstCluster + 1; + + } else if (ClusterIndex < Window->FirstCluster) { + + MyStart = Window->FirstCluster; + MyLength = ClusterEnd - Window->FirstCluster + 1; + + } else { + + // + // The range being freed starts in the bucket, and may possibly + // extend beyond the bucket. + // + + MyStart = ClusterIndex; + + if (ClusterEnd <= Window->LastCluster) { + + MyLength = ClusterCount; + + } else { + + MyLength = Window->LastCluster - ClusterIndex + 1; + } + } + + if (MyLength == 0) { + + continue; + } + +#if DBG +#pragma prefast( suppress:28931, "this is DBG build only" ) + PreviousClear = RtlNumberOfClearBits( &Vcb->FreeClusterBitMap ); + + + // + // Verify that the Bits are all really set. + // + + NT_ASSERT( MyStart + MyLength - Window->FirstCluster <= Vcb->FreeClusterBitMap.SizeOfBitMap ); + + for (i = 0; i < MyLength; i++) { + + NT_ASSERT( RtlCheckBit(&Vcb->FreeClusterBitMap, + MyStart - Window->FirstCluster + i) == 1 ); + } +#endif // DBG + + FatUnreserveClusters( IrpContext, Vcb, + MyStart - Window->FirstCluster + 2, + MyLength ); + } + + // + // Adjust the ClustersFree count for each bitmap window, even the ones + // that are not the current window. + // + + if (FatIsFat32(Vcb)) { + + Window = &Vcb->Windows[FatWindowOfCluster( ClusterIndex )]; + + } else { + + Window = &Vcb->Windows[0]; + } + + MyStart = ClusterIndex; + + for (MyLength = ClusterCount; MyLength > 0; MyLength -= count) { + + count = FatMin(Window->LastCluster - MyStart + 1, MyLength); + Window->ClustersFree += count; + + // + // If this was not the last window this allocation spanned, + // advance to the next. + // + + if (MyLength != count) { + + Window++; + MyStart = Window->FirstCluster; + } + } + + // + // Deallocation is now complete. Adjust the free cluster count. + // + + Vcb->AllocationSupport.NumberOfFreeClusters += ClusterCount; + } + +#if DBG + if (Vcb->CurrentWindow->ClustersFree != + RtlNumberOfClearBits(&Vcb->FreeClusterBitMap)) { + + DbgPrint("%x vs %x\n", Vcb->CurrentWindow->ClustersFree, + RtlNumberOfClearBits(&Vcb->FreeClusterBitMap)); + + DbgPrint("%x for %x\n", ClusterIndex, ClusterCount); + } +#endif + + FatUnlockFreeClusterBitMap( Vcb ); + + + } finally { + + DebugUnwind( FatDeallocateDiskSpace ); + + // + // Is there any unwinding to do? + // + + ExReleaseResourceLite(&Vcb->ChangeBitMapResource); + + if ( AbnormalTermination() ) { + + LBO LocalLbo; + VBO LocalVbo; + + ULONG Index; + ULONG Clusters; + ULONG FatIndex; + ULONG PriorLastIndex; + + // + // For each entry we already deallocated, reallocate it, + // chaining together as nessecary. Note that we continue + // up to and including the last "for" iteration even though + // the SetFatRun could not have been successful. This + // allows us a convienent way to re-link the final successful + // SetFatRun. + // + // It is possible that the reason we got here will prevent us + // from succeeding in this operation. + // + + PriorLastIndex = 0; + + for (Index = 0; Index <= McbIndex; Index++) { + + FatGetNextMcbEntry(Vcb, Mcb, Index, &LocalVbo, &LocalLbo, &ByteCount); + + if (ByteCount == 0xFFFFFFFF) { + + // + // Special case the computation of ClusterCout + // when file is of max size (2^32 - 1). + // + + Clusters = (1 << (32 - LogOfBytesPerCluster)); + + } else { + + Clusters = ByteCount >> LogOfBytesPerCluster; + } + + FatIndex = FatGetIndexFromLbo( Vcb, LocalLbo ); + + // + // We must always restore the prior iteration's last + // entry, pointing it to the first cluster of this run. + // + + if (PriorLastIndex != 0) { + + FatSetFatEntry( IrpContext, + Vcb, + PriorLastIndex, + (FAT_ENTRY)FatIndex ); + } + + // + // If this is not the last entry (the one that failed) + // then reallocate the disk space on the fat. + // + + if ( Index < McbIndex ) { + + FatAllocateClusters(IrpContext, Vcb, FatIndex, Clusters); + + PriorLastIndex = FatIndex + Clusters - 1; + } + } + } + + DebugTrace(-1, Dbg, "FatDeallocateDiskSpace -> (VOID)\n", 0); + } + + return; +} + + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatSplitAllocation ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN OUT PLARGE_MCB Mcb, + IN VBO SplitAtVbo, + OUT PLARGE_MCB RemainingMcb + ) + +/*++ + +Routine Description: + + This procedure takes a single mcb and splits its allocation into + two separate allocation units. The separation must only be done + on cluster boundaries, otherwise we bugcheck. + + On the disk this actually works by inserting a FAT_CLUSTER_LAST into + the last index of the first part being split out. + + Pictorially what is done is the following (where ! denotes the end of + the fat chain (i.e., FAT_CLUSTER_LAST)): + + + Mcb |--a--|--b--|--c--|--d--|--e--|--f--| + + ^ + SplitAtVbo ---------------------+ + + RemainingMcb (empty) + + becomes + + Mcb |--a--|--b--|--c--! + + + RemainingMcb |--d--|--e--|--f--| + +Arguments: + + Vcb - Supplies the VCB being modified + + Mcb - Supplies the MCB describing the allocation being split into + two parts. Upon return this Mcb now contains the first chain. + + SplitAtVbo - Supplies the VBO of the first byte for the second chain + that we creating. + + RemainingMcb - Receives the MCB describing the second chain of allocated + disk space. The caller passes in an initialized Mcb that + is filled in by this procedure STARTING AT VBO 0. + +Return Value: + + VOID - TRUE if the operation completed and FALSE if it had to + block but could not. + +--*/ + +{ + VBO SourceVbo; + VBO TargetVbo; + VBO DontCare; + + LBO Lbo; + + ULONG ByteCount; + +#if DBG + ULONG BytesPerCluster; +#endif + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatSplitAllocation\n", 0); + DebugTrace( 0, Dbg, " Vcb = %p\n", Vcb); + DebugTrace( 0, Dbg, " Mcb = %p\n", Mcb); + DebugTrace( 0, Dbg, " SplitAtVbo = %8lx\n", SplitAtVbo); + DebugTrace( 0, Dbg, " RemainingMcb = %p\n", RemainingMcb); + +#if DBG + BytesPerCluster = 1 << Vcb->AllocationSupport.LogOfBytesPerCluster; +#endif + + // + // Assert that the split point is cluster alligned + // + + NT_ASSERT( (SplitAtVbo & (BytesPerCluster - 1)) == 0 ); + + // + // We should never be handed an empty source MCB and asked to split + // at a non zero point. + // + + NT_ASSERT( !((0 != SplitAtVbo) && (0 == FsRtlNumberOfRunsInLargeMcb( Mcb)))); + + // + // Assert we were given an empty target Mcb. + // + + // + // This assert is commented out to avoid hitting in the Ea error + // path. In that case we will be using the same Mcb's to split the + // allocation that we used to merge them. The target Mcb will contain + // the runs that the split will attempt to insert. + // + // + // NT_ASSERT( FsRtlNumberOfRunsInMcb( RemainingMcb ) == 0 ); + // + + try { + + // + // Move the runs after SplitAtVbo from the souce to the target + // + + SourceVbo = SplitAtVbo; + TargetVbo = 0; + + while (FatLookupMcbEntry(Vcb, Mcb, SourceVbo, &Lbo, &ByteCount, NULL)) { + + FatAddMcbEntry( Vcb, RemainingMcb, TargetVbo, Lbo, ByteCount ); + + FatRemoveMcbEntry( Vcb, Mcb, SourceVbo, ByteCount ); + + TargetVbo += ByteCount; + SourceVbo += ByteCount; + + // + // If SourceVbo overflows, we were actually snipping off the end + // of the maximal file ... and are now done. + // + + if (SourceVbo == 0) { + + break; + } + } + + // + // Mark the last pre-split cluster as a FAT_LAST_CLUSTER + // + + if ( SplitAtVbo != 0 ) { + + FatLookupLastMcbEntry( Vcb, Mcb, &DontCare, &Lbo, NULL ); + + FatSetFatEntry( IrpContext, + Vcb, + FatGetIndexFromLbo( Vcb, Lbo ), + FAT_CLUSTER_LAST ); + } + + } finally { + + DebugUnwind( FatSplitAllocation ); + + // + // If we got an exception, we must glue back together the Mcbs + // + + if ( AbnormalTermination() ) { + + TargetVbo = SplitAtVbo; + SourceVbo = 0; + + while (FatLookupMcbEntry(Vcb, RemainingMcb, SourceVbo, &Lbo, &ByteCount, NULL)) { + + FatAddMcbEntry( Vcb, Mcb, TargetVbo, Lbo, ByteCount ); + + FatRemoveMcbEntry( Vcb, RemainingMcb, SourceVbo, ByteCount ); + + TargetVbo += ByteCount; + SourceVbo += ByteCount; + } + } + + DebugTrace(-1, Dbg, "FatSplitAllocation -> (VOID)\n", 0); + } + + return; +} + + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatMergeAllocation ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN OUT PLARGE_MCB Mcb, + IN PLARGE_MCB SecondMcb + ) + +/*++ + +Routine Description: + + This routine takes two separate allocations described by two MCBs and + joins them together into one allocation. + + Pictorially what is done is the following (where ! denotes the end of + the fat chain (i.e., FAT_CLUSTER_LAST)): + + + Mcb |--a--|--b--|--c--! + + SecondMcb |--d--|--e--|--f--| + + becomes + + Mcb |--a--|--b--|--c--|--d--|--e--|--f--| + + SecondMcb |--d--|--e--|--f--| + + +Arguments: + + Vcb - Supplies the VCB being modified + + Mcb - Supplies the MCB of the first allocation that is being modified. + Upon return this Mcb will also describe the newly enlarged + allocation + + SecondMcb - Supplies the ZERO VBO BASED MCB of the second allocation + that is being appended to the first allocation. This + procedure leaves SecondMcb unchanged. + +Return Value: + + VOID - TRUE if the operation completed and FALSE if it had to + block but could not. + +--*/ + +{ + VBO SpliceVbo = 0; + LBO SpliceLbo; + + VBO SourceVbo; + VBO TargetVbo = 0; + + LBO Lbo; + + ULONG ByteCount; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatMergeAllocation\n", 0); + DebugTrace( 0, Dbg, " Vcb = %p\n", Vcb); + DebugTrace( 0, Dbg, " Mcb = %p\n", Mcb); + DebugTrace( 0, Dbg, " SecondMcb = %p\n", SecondMcb); + + try { + + // + // Append the runs from SecondMcb to Mcb + // + + (void)FatLookupLastMcbEntry( Vcb, Mcb, &SpliceVbo, &SpliceLbo, NULL ); + + SourceVbo = 0; + TargetVbo = SpliceVbo + 1; + + while (FatLookupMcbEntry(Vcb, SecondMcb, SourceVbo, &Lbo, &ByteCount, NULL)) { + + FatAddMcbEntry( Vcb, Mcb, TargetVbo, Lbo, ByteCount ); + + SourceVbo += ByteCount; + TargetVbo += ByteCount; + } + + // + // Link the last pre-merge cluster to the first cluster of SecondMcb + // + + FatLookupMcbEntry( Vcb, SecondMcb, 0, &Lbo, (PULONG)NULL, NULL ); + + FatSetFatEntry( IrpContext, + Vcb, + FatGetIndexFromLbo( Vcb, SpliceLbo ), + (FAT_ENTRY)FatGetIndexFromLbo( Vcb, Lbo ) ); + + } finally { + + DebugUnwind( FatMergeAllocation ); + + // + // If we got an exception, we must remove the runs added to Mcb + // + + if ( AbnormalTermination() ) { + + ULONG CutLength; + + if ((CutLength = TargetVbo - (SpliceVbo + 1)) != 0) { + + FatRemoveMcbEntry( Vcb, Mcb, SpliceVbo + 1, CutLength); + } + } + + DebugTrace(-1, Dbg, "FatMergeAllocation -> (VOID)\n", 0); + } + + return; +} + + +// +// Internal support routine +// + +CLUSTER_TYPE +FatInterpretClusterType ( + IN PVCB Vcb, + IN FAT_ENTRY Entry + ) + +/*++ + +Routine Description: + + This procedure tells the caller how to interpret the input fat table + entry. It will indicate if the fat cluster is available, resereved, + bad, the last one, or the another fat index. This procedure can deal + with both 12 and 16 bit fat. + +Arguments: + + Vcb - Supplies the Vcb to examine, yields 12/16 bit info + + Entry - Supplies the fat entry to examine + +Return Value: + + CLUSTER_TYPE - Is the type of the input Fat entry + +--*/ + +{ + DebugTrace(+1, Dbg, "InterpretClusterType\n", 0); + DebugTrace( 0, Dbg, " Vcb = %p\n", Vcb); + DebugTrace( 0, Dbg, " Entry = %8lx\n", Entry); + + PAGED_CODE(); + + switch(Vcb->AllocationSupport.FatIndexBitSize ) { + case 32: + Entry &= FAT32_ENTRY_MASK; + break; + + case 12: + NT_ASSERT( Entry <= 0xfff ); + if (Entry >= 0x0ff0) { + Entry |= 0x0FFFF000; + } + break; + + default: + case 16: + NT_ASSERT( Entry <= 0xffff ); + if (Entry >= 0x0fff0) { + Entry |= 0x0FFF0000; + } + break; + } + + if (Entry == FAT_CLUSTER_AVAILABLE) { + + DebugTrace(-1, Dbg, "FatInterpretClusterType -> FatClusterAvailable\n", 0); + + return FatClusterAvailable; + + } else if (Entry < FAT_CLUSTER_RESERVED) { + + DebugTrace(-1, Dbg, "FatInterpretClusterType -> FatClusterNext\n", 0); + + return FatClusterNext; + + } else if (Entry < FAT_CLUSTER_BAD) { + + DebugTrace(-1, Dbg, "FatInterpretClusterType -> FatClusterReserved\n", 0); + + return FatClusterReserved; + + } else if (Entry == FAT_CLUSTER_BAD) { + + DebugTrace(-1, Dbg, "FatInterpretClusterType -> FatClusterBad\n", 0); + + return FatClusterBad; + + } else { + + DebugTrace(-1, Dbg, "FatInterpretClusterType -> FatClusterLast\n", 0); + + return FatClusterLast; + } +} + + +// +// Internal support routine +// + +VOID +FatLookupFatEntry ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN ULONG FatIndex, + IN OUT PULONG FatEntry, + IN OUT PFAT_ENUMERATION_CONTEXT Context + ) + +/*++ + +Routine Description: + + This routine takes an index into the fat and gives back the value + in the Fat at this index. At any given time, for a 16 bit fat, this + routine allows only one page per volume of the fat to be pinned in + memory. For a 12 bit bit fat, the entire fat (max 6k) is pinned. This + extra layer of caching makes the vast majority of requests very + fast. The context for this caching stored in a structure in the Vcb. + +Arguments: + + Vcb - Supplies the Vcb to examine, yields 12/16 bit info, + fat access context, etc. + + FatIndex - Supplies the fat index to examine. + + FatEntry - Receives the fat entry pointed to by FatIndex. Note that + it must point to non-paged pool. + + Context - This structure keeps track of a page of pinned fat between calls. + +--*/ + +{ + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatLookupFatEntry\n", 0); + DebugTrace( 0, Dbg, " Vcb = %p\n", Vcb); + DebugTrace( 0, Dbg, " FatIndex = %4x\n", FatIndex); + DebugTrace( 0, Dbg, " FatEntry = %8lx\n", FatEntry); + + // + // Make sure they gave us a valid fat index. + // + + FatVerifyIndexIsValid(IrpContext, Vcb, FatIndex); + + // + // Case on 12 or 16 bit fats. + // + // In the 12 bit case (mostly floppies) we always have the whole fat + // (max 6k bytes) pinned during allocation operations. This is possibly + // a wee bit slower, but saves headaches over fat entries with 8 bits + // on one page, and 4 bits on the next. + // + // The 16 bit case always keeps the last used page pinned until all + // operations are done and it is unpinned. + // + + // + // DEAL WITH 12 BIT CASE + // + + if (Vcb->AllocationSupport.FatIndexBitSize == 12) { + + // + // Check to see if the fat is already pinned, otherwise pin it. + // + + if (Context->Bcb == NULL) { + + FatReadVolumeFile( IrpContext, + Vcb, + FatReservedBytes( &Vcb->Bpb ), + FatBytesPerFat( &Vcb->Bpb ), + &Context->Bcb, + &Context->PinnedPage ); + } + + // + // Load the return value. + // + + + FatLookup12BitEntry( Context->PinnedPage, FatIndex, FatEntry ); + + } else if (Vcb->AllocationSupport.FatIndexBitSize == 32) { + + // + // DEAL WITH 32 BIT CASE + // + + ULONG PageEntryOffset; + ULONG OffsetIntoVolumeFile; + + // + // Initialize two local variables that help us. + // + OffsetIntoVolumeFile = FatReservedBytes(&Vcb->Bpb) + FatIndex * sizeof(FAT_ENTRY); + PageEntryOffset = (OffsetIntoVolumeFile % PAGE_SIZE) / sizeof(FAT_ENTRY); + + // + // Check to see if we need to read in a new page of fat + // + + if ((Context->Bcb == NULL) || + (OffsetIntoVolumeFile / PAGE_SIZE != Context->VboOfPinnedPage / PAGE_SIZE)) { + + // + // The entry wasn't in the pinned page, so must we unpin the current + // page (if any) and read in a new page. + // + + FatUnpinBcb( IrpContext, Context->Bcb ); + + FatReadVolumeFile( IrpContext, + Vcb, + OffsetIntoVolumeFile & ~(PAGE_SIZE - 1), + PAGE_SIZE, + &Context->Bcb, + &Context->PinnedPage ); + + Context->VboOfPinnedPage = OffsetIntoVolumeFile & ~(PAGE_SIZE - 1); + } + + // + // Grab the fat entry from the pinned page, and return + // + + *FatEntry = ((PULONG)(Context->PinnedPage))[PageEntryOffset] & FAT32_ENTRY_MASK; + + } else { + + // + // DEAL WITH 16 BIT CASE + // + + ULONG PageEntryOffset; + ULONG OffsetIntoVolumeFile; + + // + // Initialize two local variables that help us. + // + + OffsetIntoVolumeFile = FatReservedBytes(&Vcb->Bpb) + FatIndex * sizeof(USHORT); + PageEntryOffset = (OffsetIntoVolumeFile % PAGE_SIZE) / sizeof(USHORT); + + // + // Check to see if we need to read in a new page of fat + // + + if ((Context->Bcb == NULL) || + (OffsetIntoVolumeFile / PAGE_SIZE != Context->VboOfPinnedPage / PAGE_SIZE)) { + + // + // The entry wasn't in the pinned page, so must we unpin the current + // page (if any) and read in a new page. + // + + FatUnpinBcb( IrpContext, Context->Bcb ); + + FatReadVolumeFile( IrpContext, + Vcb, + OffsetIntoVolumeFile & ~(PAGE_SIZE - 1), + PAGE_SIZE, + &Context->Bcb, + &Context->PinnedPage ); + + Context->VboOfPinnedPage = OffsetIntoVolumeFile & ~(PAGE_SIZE - 1); + } + + // + // Grab the fat entry from the pinned page, and return + // + + *FatEntry = ((PUSHORT)(Context->PinnedPage))[PageEntryOffset]; + } + + DebugTrace(-1, Dbg, "FatLookupFatEntry -> (VOID)\n", 0); + return; +} + + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatSetFatEntry ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN ULONG FatIndex, + IN FAT_ENTRY FatEntry + ) + +/*++ + +Routine Description: + + This routine takes an index into the fat and puts a value in the Fat + at this index. The routine special cases 12, 16 and 32 bit fats. In + all cases we go to the cache manager for a piece of the fat. + + We have a special form of this call for setting the DOS-style dirty bit. + Unlike the dirty bit in the boot sector, we do not go to special effort + to make sure that this hits the disk synchronously - if the system goes + down in the window between the dirty bit being set in the boot sector + and the FAT index zero dirty bit being lazy written, then life is tough. + + The only possible scenario is that Win9x may see what it thinks is a clean + volume that really isn't (hopefully Memphis will pay attention to our dirty + bit as well). The dirty bit will get out quickly, and if heavy activity is + occurring, then the dirty bit should actually be there virtually all of the + time since the act of cleaning the volume is the "rare" occurance. + + There are synchronization concerns that would crop up if we tried to make + this synchronous. This thread may already own the Bcb shared for the first + sector of the FAT (so we can't get it exclusive for a writethrough). This + would require some more serious replumbing to work around than I want to + consider at this time. + + We can and do, however, synchronously set the bit clean. + + At this point the reader should understand why the NT dirty bit is where it is. + +Arguments: + + Vcb - Supplies the Vcb to examine, yields 12/16/32 bit info, etc. + + FatIndex - Supplies the destination fat index. + + FatEntry - Supplies the source fat entry. + +--*/ + +{ + LBO Lbo; + PBCB Bcb = NULL; + ULONG SectorSize; + ULONG OffsetIntoVolumeFile; + ULONG WasWait = TRUE; + BOOLEAN RegularOperation = TRUE; + BOOLEAN CleaningOperation = FALSE; + BOOLEAN ReleaseMutex = FALSE; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatSetFatEntry\n", 0); + DebugTrace( 0, Dbg, " Vcb = %p\n", Vcb); + DebugTrace( 0, Dbg, " FatIndex = %4x\n", FatIndex); + DebugTrace( 0, Dbg, " FatEntry = %4x\n", FatEntry); + + // + // Make sure they gave us a valid fat index if this isn't the special + // clean-bit modifying call. + // + + if (FatIndex == FAT_DIRTY_BIT_INDEX) { + + // + // We are setting the clean bit state. Of course, we could + // have corruption that would cause us to try to fiddle the + // reserved index - we guard against this by having the + // special entry values use the reserved high 4 bits that + // we know that we'll never try to set. + // + + // + // We don't want to repin the FAT pages involved here. Just + // let the lazy writer hit them when it can. + // + + RegularOperation = FALSE; + + switch (FatEntry) { + case FAT_CLEAN_VOLUME: + FatEntry = (FAT_ENTRY)FAT_CLEAN_ENTRY; + CleaningOperation = TRUE; + break; + + case FAT_DIRTY_VOLUME: + switch (Vcb->AllocationSupport.FatIndexBitSize) { + case 12: + FatEntry = FAT12_DIRTY_ENTRY; + break; + + case 32: + FatEntry = FAT32_DIRTY_ENTRY; + break; + + default: + FatEntry = FAT16_DIRTY_ENTRY; + break; + } + break; + + default: + FatRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); + break; + } + + // + // Disable dirtying semantics for the duration of this operation. Force this + // operation to wait for the duration. + // + + WasWait = FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ); + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT | IRP_CONTEXT_FLAG_DISABLE_DIRTY ); + + } else { + + NT_ASSERT( !(FatEntry & ~FAT32_ENTRY_MASK) ); + FatVerifyIndexIsValid(IrpContext, Vcb, FatIndex); + } + + // + // Set Sector Size + // + + SectorSize = 1 << Vcb->AllocationSupport.LogOfBytesPerSector; + + // + // Case on 12 or 16 bit fats. + // + // In the 12 bit case (mostly floppies) we always have the whole fat + // (max 6k bytes) pinned during allocation operations. This is possibly + // a wee bit slower, but saves headaches over fat entries with 8 bits + // on one page, and 4 bits on the next. + // + // In the 16 bit case we only read the page that we need to set the fat + // entry. + // + + // + // DEAL WITH 12 BIT CASE + // + + try { + + if (Vcb->AllocationSupport.FatIndexBitSize == 12) { + + PVOID PinnedFat; + + // + // Make sure we have a valid entry + // + + FatEntry &= 0xfff; + + // + // We read in the entire fat. Note that using prepare write marks + // the bcb pre-dirty, so we don't have to do it explicitly. + // + + OffsetIntoVolumeFile = FatReservedBytes( &Vcb->Bpb ) + FatIndex * 3 / 2; + + FatPrepareWriteVolumeFile( IrpContext, + Vcb, + FatReservedBytes( &Vcb->Bpb ), + FatBytesPerFat( &Vcb->Bpb ), + &Bcb, + &PinnedFat, + RegularOperation, + FALSE ); + + // + // Mark the sector(s) dirty in the DirtyFatMcb. This call is + // complicated somewhat for the 12 bit case since a single + // entry write can span two sectors (and pages). + // + // Get the Lbo for the sector where the entry starts, and add it to + // the dirty fat Mcb. + // + + Lbo = OffsetIntoVolumeFile & ~(SectorSize - 1); + + FatAddMcbEntry( Vcb, &Vcb->DirtyFatMcb, (VBO) Lbo, Lbo, SectorSize); + + // + // If the entry started on the last byte of the sector, it continues + // to the next sector, so mark the next sector dirty as well. + // + // Note that this entry will simply coalese with the last entry, + // so this operation cannot fail. Also if we get this far, we have + // made it, so no unwinding will be needed. + // + + if ( (OffsetIntoVolumeFile & (SectorSize - 1)) == (SectorSize - 1) ) { + + Lbo += SectorSize; + + FatAddMcbEntry( Vcb, &Vcb->DirtyFatMcb, (VBO) Lbo, Lbo, SectorSize ); + } + + // + // Store the entry into the fat; we need a little synchonization + // here and can't use a spinlock since the bytes might not be + // resident. + // + + FatLockFreeClusterBitMap( Vcb ); + ReleaseMutex = TRUE; + + FatSet12BitEntry( PinnedFat, FatIndex, FatEntry ); + + FatUnlockFreeClusterBitMap( Vcb ); + ReleaseMutex = FALSE; + + } else if (Vcb->AllocationSupport.FatIndexBitSize == 32) { + + // + // DEAL WITH 32 BIT CASE + // + + PULONG PinnedFatEntry32; + + // + // Read in a new page of fat + // + + OffsetIntoVolumeFile = FatReservedBytes( &Vcb->Bpb ) + + FatIndex * sizeof( FAT_ENTRY ); + + FatPrepareWriteVolumeFile( IrpContext, + Vcb, + OffsetIntoVolumeFile, + sizeof(FAT_ENTRY), + &Bcb, + (PVOID *)&PinnedFatEntry32, + RegularOperation, + FALSE ); + // + // Mark the sector dirty in the DirtyFatMcb + // + + Lbo = OffsetIntoVolumeFile & ~(SectorSize - 1); + + FatAddMcbEntry( Vcb, &Vcb->DirtyFatMcb, (VBO) Lbo, Lbo, SectorSize); + + // + // Store the FatEntry to the pinned page. + // + // Preserve the reserved bits in FAT32 entries in the file heap. + // + +#ifdef ALPHA + FatLockFreeClusterBitMap( Vcb ); + ReleaseMutex = TRUE; +#endif // ALPHA + + if (FatIndex != FAT_DIRTY_BIT_INDEX) { + + *PinnedFatEntry32 = ((*PinnedFatEntry32 & ~FAT32_ENTRY_MASK) | FatEntry); + + } else { + + *PinnedFatEntry32 = FatEntry; + } + +#ifdef ALPHA + FatUnlockFreeClusterBitMap( Vcb ); + ReleaseMutex = FALSE; +#endif // ALPHA + + } else { + + // + // DEAL WITH 16 BIT CASE + // + + PUSHORT PinnedFatEntry; + + // + // Read in a new page of fat + // + + OffsetIntoVolumeFile = FatReservedBytes( &Vcb->Bpb ) + + FatIndex * sizeof(USHORT); + + FatPrepareWriteVolumeFile( IrpContext, + Vcb, + OffsetIntoVolumeFile, + sizeof(USHORT), + &Bcb, + (PVOID *)&PinnedFatEntry, + RegularOperation, + FALSE ); + // + // Mark the sector dirty in the DirtyFatMcb + // + + Lbo = OffsetIntoVolumeFile & ~(SectorSize - 1); + + FatAddMcbEntry( Vcb, &Vcb->DirtyFatMcb, (VBO) Lbo, Lbo, SectorSize); + + // + // Store the FatEntry to the pinned page. + // + // We need extra synchronization here for broken architectures + // like the ALPHA that don't support atomic 16 bit writes. + // + +#ifdef ALPHA + FatLockFreeClusterBitMap( Vcb ); + ReleaseMutex = TRUE; +#endif // ALPHA + + *PinnedFatEntry = (USHORT)FatEntry; + +#ifdef ALPHA + FatUnlockFreeClusterBitMap( Vcb ); + ReleaseMutex = FALSE; +#endif // ALPHA + } + + } finally { + + DebugUnwind( FatSetFatEntry ); + + // + // Re-enable volume dirtying in case this was a dirty bit operation. + // + + ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_DIRTY ); + + // + // Make this operation asynchronous again if needed. + // + + if (!WasWait) { + + ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ); + } + + // + // If we still somehow have the Mutex, release it. + // + + if (ReleaseMutex) { + + NT_ASSERT( AbnormalTermination() ); + + FatUnlockFreeClusterBitMap( Vcb ); + } + + // + // Unpin the Bcb. For cleaning operations, we make this write-through. + // + + if (CleaningOperation && Bcb) { + + IO_STATUS_BLOCK IgnoreStatus; + + CcRepinBcb( Bcb ); + CcUnpinData( Bcb ); + DbgDoit( IrpContext->PinCount -= 1 ); + CcUnpinRepinnedBcb( Bcb, TRUE, &IgnoreStatus ); + + } else { + + FatUnpinBcb(IrpContext, Bcb); + } + + DebugTrace(-1, Dbg, "FatSetFatEntry -> (VOID)\n", 0); + } + + return; +} + + +// +// Internal support routine +// + +VOID +FatSetFatRun ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN ULONG StartingFatIndex, + IN ULONG ClusterCount, + IN BOOLEAN ChainTogether + ) + +/*++ + +Routine Description: + + This routine sets a continuous run of clusters in the fat. If ChainTogether + is TRUE, then the clusters are linked together as in normal Fat fasion, + with the last cluster receiving FAT_CLUSTER_LAST. If ChainTogether is + FALSE, all the entries are set to FAT_CLUSTER_AVAILABLE, effectively + freeing all the clusters in the run. + +Arguments: + + Vcb - Supplies the Vcb to examine, yields 12/16 bit info, etc. + + StartingFatIndex - Supplies the destination fat index. + + ClusterCount - Supplies the number of contiguous clusters to work on. + + ChainTogether - Tells us whether to fill the entries with links, or + FAT_CLUSTER_AVAILABLE + + +Return Value: + + VOID + +--*/ + +{ +#define MAXCOUNTCLUS 0x10000 +#define COUNTSAVEDBCBS ((MAXCOUNTCLUS * sizeof(FAT_ENTRY) / PAGE_SIZE) + 2) + PBCB SavedBcbs[COUNTSAVEDBCBS][2]; + + ULONG SectorSize; + ULONG Cluster; + + LBO StartSectorLbo; + LBO FinalSectorLbo; + LBO Lbo; + + PVOID PinnedFat; + + BOOLEAN ReleaseMutex = FALSE; + + ULONG SavedStartingFatIndex = StartingFatIndex; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatSetFatRun\n", 0); + DebugTrace( 0, Dbg, " Vcb = %p\n", Vcb); + DebugTrace( 0, Dbg, " StartingFatIndex = %8x\n", StartingFatIndex); + DebugTrace( 0, Dbg, " ClusterCount = %8lx\n", ClusterCount); + DebugTrace( 0, Dbg, " ChainTogether = %s\n", ChainTogether ? "TRUE":"FALSE"); + + // + // Make sure they gave us a valid fat run. + // + + FatVerifyIndexIsValid(IrpContext, Vcb, StartingFatIndex); + FatVerifyIndexIsValid(IrpContext, Vcb, StartingFatIndex + ClusterCount - 1); + + // + // Check special case + // + + if (ClusterCount == 0) { + + DebugTrace(-1, Dbg, "FatSetFatRun -> (VOID)\n", 0); + return; + } + + // + // Set Sector Size + // + + SectorSize = 1 << Vcb->AllocationSupport.LogOfBytesPerSector; + + // + // Case on 12 or 16 bit fats. + // + // In the 12 bit case (mostly floppies) we always have the whole fat + // (max 6k bytes) pinned during allocation operations. This is possibly + // a wee bit slower, but saves headaches over fat entries with 8 bits + // on one page, and 4 bits on the next. + // + // In the 16 bit case we only read one page at a time, as needed. + // + + // + // DEAL WITH 12 BIT CASE + // + + try { + + if (Vcb->AllocationSupport.FatIndexBitSize == 12) { + + // + // We read in the entire fat. Note that using prepare write marks + // the bcb pre-dirty, so we don't have to do it explicitly. + // + + RtlZeroMemory( &SavedBcbs[0][0], 2 * sizeof(PBCB) * 2); + + FatPrepareWriteVolumeFile( IrpContext, + Vcb, + FatReservedBytes( &Vcb->Bpb ), + FatBytesPerFat( &Vcb->Bpb ), + &SavedBcbs[0][0], + &PinnedFat, + TRUE, + FALSE ); + + // + // Mark the affected sectors dirty. Note that FinalSectorLbo is + // the Lbo of the END of the entry (Thus * 3 + 2). This makes sure + // we catch the case of a dirty fat entry straddling a sector boundry. + // + // Note that if the first AddMcbEntry succeeds, all following ones + // will simply coalese, and thus also succeed. + // + + StartSectorLbo = (FatReservedBytes( &Vcb->Bpb ) + StartingFatIndex * 3 / 2) + & ~(SectorSize - 1); + + FinalSectorLbo = (FatReservedBytes( &Vcb->Bpb ) + ((StartingFatIndex + + ClusterCount) * 3 + 2) / 2) & ~(SectorSize - 1); + + for (Lbo = StartSectorLbo; Lbo <= FinalSectorLbo; Lbo += SectorSize) { + + FatAddMcbEntry( Vcb, &Vcb->DirtyFatMcb, (VBO) Lbo, Lbo, SectorSize ); + } + + // + // Store the entries into the fat; we need a little + // synchonization here and can't use a spinlock since the bytes + // might not be resident. + // + + FatLockFreeClusterBitMap( Vcb ); + ReleaseMutex = TRUE; + + for (Cluster = StartingFatIndex; + Cluster < StartingFatIndex + ClusterCount - 1; + Cluster++) { + + FatSet12BitEntry( PinnedFat, + Cluster, + ChainTogether ? Cluster + 1 : FAT_CLUSTER_AVAILABLE ); + } + + // + // Save the last entry + // + + FatSet12BitEntry( PinnedFat, + Cluster, + ChainTogether ? + FAT_CLUSTER_LAST & 0xfff : FAT_CLUSTER_AVAILABLE ); + + FatUnlockFreeClusterBitMap( Vcb ); + ReleaseMutex = FALSE; + + } else if (Vcb->AllocationSupport.FatIndexBitSize == 32) { + + // + // DEAL WITH 32 BIT CASE + // + + for (;;) { + + VBO StartOffsetInVolume; + VBO FinalOffsetInVolume; + + ULONG Page; + ULONG FinalCluster; + PULONG FatEntry = NULL; + ULONG ClusterCountThisRun; + + StartOffsetInVolume = FatReservedBytes(&Vcb->Bpb) + + StartingFatIndex * sizeof(FAT_ENTRY); + + if (ClusterCount > MAXCOUNTCLUS) { + ClusterCountThisRun = MAXCOUNTCLUS; + } else { + ClusterCountThisRun = ClusterCount; + } + + FinalOffsetInVolume = StartOffsetInVolume + + (ClusterCountThisRun - 1) * sizeof(FAT_ENTRY); + + { + ULONG NumberOfPages; + ULONG Offset; + + NumberOfPages = (FinalOffsetInVolume / PAGE_SIZE) - + (StartOffsetInVolume / PAGE_SIZE) + 1; + + RtlZeroMemory( &SavedBcbs[0][0], (NumberOfPages + 1) * sizeof(PBCB) * 2 ); + + for ( Page = 0, Offset = StartOffsetInVolume & ~(PAGE_SIZE - 1); + Page < NumberOfPages; + Page++, Offset += PAGE_SIZE ) { + + FatPrepareWriteVolumeFile( IrpContext, + Vcb, + Offset, + PAGE_SIZE, + &SavedBcbs[Page][0], + (PVOID *)&SavedBcbs[Page][1], + TRUE, + FALSE ); + + if (Page == 0) { + + FatEntry = (PULONG)((PUCHAR)SavedBcbs[0][1] + + (StartOffsetInVolume % PAGE_SIZE)); + } + } + } + + // + // Mark the run dirty + // + + StartSectorLbo = StartOffsetInVolume & ~(SectorSize - 1); + FinalSectorLbo = FinalOffsetInVolume & ~(SectorSize - 1); + + for (Lbo = StartSectorLbo; Lbo <= FinalSectorLbo; Lbo += SectorSize) { + + FatAddMcbEntry( Vcb, &Vcb->DirtyFatMcb, (VBO)Lbo, Lbo, SectorSize ); + } + + // + // Store the entries + // + // We need extra synchronization here for broken architectures + // like the ALPHA that don't support atomic 16 bit writes. + // + +#ifdef ALPHA + FatLockFreeClusterBitMap( Vcb ); + ReleaseMutex = TRUE; +#endif // ALPHA + + FinalCluster = StartingFatIndex + ClusterCountThisRun - 1; + Page = 0; + + for (Cluster = StartingFatIndex; + Cluster <= FinalCluster; + Cluster++, FatEntry++) { + + // + // If we just crossed a page boundry (as opposed to starting + // on one), update our idea of FatEntry. + + if ( (((ULONG_PTR)FatEntry & (PAGE_SIZE-1)) == 0) && + (Cluster != StartingFatIndex) ) { + + Page += 1; + FatEntry = (PULONG)SavedBcbs[Page][1]; + } + + *FatEntry = ChainTogether ? (FAT_ENTRY)(Cluster + 1) : + FAT_CLUSTER_AVAILABLE; + } + + // + // Fix up the last entry if we were chaining together + // + + if ((ClusterCount <= MAXCOUNTCLUS) && + ChainTogether ) { + + *(FatEntry-1) = FAT_CLUSTER_LAST; + } + +#ifdef ALPHA + FatUnlockFreeClusterBitMap( Vcb ); + ReleaseMutex = FALSE; +#endif // ALPHA + + { + ULONG i; + + // + // Unpin the Bcbs + // + + for (i = 0; (i < COUNTSAVEDBCBS) && (SavedBcbs[i][0] != NULL); i++) { + + FatUnpinBcb( IrpContext, SavedBcbs[i][0] ); + SavedBcbs[i][0] = NULL; + } + } + + if (ClusterCount <= MAXCOUNTCLUS) { + + break; + + } else { + + StartingFatIndex += MAXCOUNTCLUS; + ClusterCount -= MAXCOUNTCLUS; + } + } + + } else { + + // + // DEAL WITH 16 BIT CASE + // + + VBO StartOffsetInVolume; + VBO FinalOffsetInVolume; + + ULONG Page; + ULONG FinalCluster; + PUSHORT FatEntry = NULL; + + StartOffsetInVolume = FatReservedBytes(&Vcb->Bpb) + + StartingFatIndex * sizeof(USHORT); + + FinalOffsetInVolume = StartOffsetInVolume + + (ClusterCount - 1) * sizeof(USHORT); + + // + // Read in one page of fat at a time. We cannot read in the + // all of the fat we need because of cache manager limitations. + // + // SavedBcb was initialized to be able to hold the largest + // possible number of pages in a fat plus and extra one to + // accomadate the boot sector, plus one more to make sure there + // is enough room for the RtlZeroMemory below that needs the mark + // the first Bcb after all the ones we will use as an end marker. + // + + { + ULONG NumberOfPages; + ULONG Offset; + + NumberOfPages = (FinalOffsetInVolume / PAGE_SIZE) - + (StartOffsetInVolume / PAGE_SIZE) + 1; + + RtlZeroMemory( &SavedBcbs[0][0], (NumberOfPages + 1) * sizeof(PBCB) * 2 ); + + for ( Page = 0, Offset = StartOffsetInVolume & ~(PAGE_SIZE - 1); + Page < NumberOfPages; + Page++, Offset += PAGE_SIZE ) { + + FatPrepareWriteVolumeFile( IrpContext, + Vcb, + Offset, + PAGE_SIZE, + &SavedBcbs[Page][0], + (PVOID *)&SavedBcbs[Page][1], + TRUE, + FALSE ); + + if (Page == 0) { + + FatEntry = (PUSHORT)((PUCHAR)SavedBcbs[0][1] + + (StartOffsetInVolume % PAGE_SIZE)); + } + } + } + + // + // Mark the run dirty + // + + StartSectorLbo = StartOffsetInVolume & ~(SectorSize - 1); + FinalSectorLbo = FinalOffsetInVolume & ~(SectorSize - 1); + + for (Lbo = StartSectorLbo; Lbo <= FinalSectorLbo; Lbo += SectorSize) { + + FatAddMcbEntry( Vcb, &Vcb->DirtyFatMcb, (VBO) Lbo, Lbo, SectorSize ); + } + + // + // Store the entries + // + // We need extra synchronization here for broken architectures + // like the ALPHA that don't support atomic 16 bit writes. + // + +#ifdef ALPHA + FatLockFreeClusterBitMap( Vcb ); + ReleaseMutex = TRUE; +#endif // ALPHA + + FinalCluster = StartingFatIndex + ClusterCount - 1; + Page = 0; + + for (Cluster = StartingFatIndex; + Cluster <= FinalCluster; + Cluster++, FatEntry++) { + + // + // If we just crossed a page boundry (as opposed to starting + // on one), update our idea of FatEntry. + + if ( (((ULONG_PTR)FatEntry & (PAGE_SIZE-1)) == 0) && + (Cluster != StartingFatIndex) ) { + + Page += 1; + FatEntry = (PUSHORT)SavedBcbs[Page][1]; + } + + *FatEntry = (USHORT) (ChainTogether ? (FAT_ENTRY)(Cluster + 1) : + FAT_CLUSTER_AVAILABLE); + } + + // + // Fix up the last entry if we were chaining together + // + + if ( ChainTogether ) { + +#pragma warning( suppress: 4310 ) + *(FatEntry-1) = (USHORT)FAT_CLUSTER_LAST; + + } +#ifdef ALPHA + FatUnlockFreeClusterBitMap( Vcb ); + ReleaseMutex = FALSE; +#endif // ALPHA + } + + } finally { + + ULONG i; + + DebugUnwind( FatSetFatRun ); + + // + // If we still somehow have the Mutex, release it. + // + + if (ReleaseMutex) { + + NT_ASSERT( AbnormalTermination() ); + + FatUnlockFreeClusterBitMap( Vcb ); + } + + // + // Unpin the Bcbs + // + + for (i = 0; (i < COUNTSAVEDBCBS) && (SavedBcbs[i][0] != NULL); i++) { + + FatUnpinBcb( IrpContext, SavedBcbs[i][0] ); + } + + // + // At this point nothing in this finally clause should have raised. + // So, now comes the unsafe (sigh) stuff. + // + + if ( AbnormalTermination() && + (Vcb->AllocationSupport.FatIndexBitSize == 32) ) { + + // + // Fat32 unwind + // + // This case is more complex because the FAT12 and FAT16 cases + // pin all the needed FAT pages (128K max), after which it + // can't fail, before changing any FAT entries. In the Fat32 + // case, it may not be practical to pin all the needed FAT + // pages, because that could span many megabytes. So Fat32 + // attacks in chunks, and if a failure occurs once the first + // chunk has been updated, we have to back out the updates. + // + // The unwind consists of walking back over each FAT entry we + // have changed, setting it back to the previous value. Note + // that the previous value with either be FAT_CLUSTER_AVAILABLE + // (if ChainTogether==TRUE) or a simple link to the successor + // (if ChainTogether==FALSE). + // + // We concede that any one of these calls could fail too; our + // objective is to make this case no more likely than the case + // for a file consisting of multiple disjoint runs. + // + + while ( StartingFatIndex > SavedStartingFatIndex ) { + + StartingFatIndex--; + + FatSetFatEntry( IrpContext, Vcb, StartingFatIndex, + ChainTogether ? + StartingFatIndex + 1 : FAT_CLUSTER_AVAILABLE ); + } + } + + DebugTrace(-1, Dbg, "FatSetFatRun -> (VOID)\n", 0); + } + + return; +} + + +// +// Internal support routine +// + +UCHAR +FatLogOf ( + IN ULONG Value + ) + +/*++ + +Routine Description: + + This routine just computes the base 2 log of an integer. It is only used + on objects that are know to be powers of two. + +Arguments: + + Value - The value to take the base 2 log of. + +Return Value: + + UCHAR - The base 2 log of Value. + +--*/ + +{ + UCHAR Log = 0; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "LogOf\n", 0); + DebugTrace( 0, Dbg, " Value = %8lx\n", Value); + + // + // Knock bits off until we we get a one at position 0 + // + + while ( (Value & 0xfffffffe) != 0 ) { + + Log++; + Value >>= 1; + } + + // + // If there was more than one bit set, the file system messed up, + // Bug Check. + // + + if (Value != 0x1) { + + DebugTrace( 0, Dbg, "Received non power of 2.\n", 0); + +#pragma prefast( suppress: 28159, "we bugcheck here because our internal data structures are seriously corrupted if this happens" ) + FatBugCheck( Value, Log, 0 ); + } + + DebugTrace(-1, Dbg, "LogOf -> %8lx\n", Log); + + return Log; +} + + +VOID +FatExamineFatEntries( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN ULONG StartIndex OPTIONAL, + IN ULONG EndIndex OPTIONAL, + IN BOOLEAN SetupWindows, + IN PFAT_WINDOW SwitchToWindow OPTIONAL, + IN PULONG BitMapBuffer OPTIONAL + ) +/*++ + +Routine Description: + + This routine handles scanning a segment of the FAT into in-memory structures. + + There are three fundamental cases, with variations depending on the FAT type: + + 1) During volume setup, FatSetupAllocations + + 1a) for FAT12/16, read the FAT into our free clusterbitmap + 1b) for FAT32, perform the initial scan for window free cluster counts + + 2) Switching FAT32 windows on the fly during system operation + + 3) Reading arbitrary segments of the FAT for the purposes of the GetVolumeBitmap + call (only for FAT32) + + There really is too much going on in here. At some point this should be + substantially rewritten. + +Arguments: + + Vcb - Supplies the volume involved + + StartIndex - Supplies the starting cluster, ignored if SwitchToWindow supplied + + EndIndex - Supplies the ending cluster, ignored if SwitchToWindow supplied + + SetupWindows - Indicates if we are doing the initial FAT32 scan + + SwitchToWindow - Supplies the FAT window we are examining and will switch to + + BitMapBuffer - Supplies a specific bitmap to fill in, if not supplied we fill + in the volume free cluster bitmap if !SetupWindows + +Return Value: + + None. Lots of side effects. + +--*/ +{ + ULONG FatIndexBitSize; + ULONG Page = 0; + ULONG Offset = 0; + ULONG FatIndex; + FAT_ENTRY FatEntry = FAT_CLUSTER_AVAILABLE; + FAT_ENTRY FirstFatEntry = FAT_CLUSTER_AVAILABLE; + PUSHORT FatBuffer; + PVOID pv; + PBCB Bcb = NULL; + ULONG EntriesPerWindow; + + ULONG ClustersThisRun; + ULONG StartIndexOfThisRun; + + PULONG FreeClusterCount = NULL; + + PFAT_WINDOW CurrentWindow = NULL; + + PVOID NewBitMapBuffer = NULL; + PRTL_BITMAP BitMap = NULL; + RTL_BITMAP PrivateBitMap; + + ULONG ClusterSize = 0; + ULONG PrefetchPages = 0; + ULONG FatPages = 0; + + VBO BadClusterVbo = 0; + LBO Lbo = 0; + + enum RunType { + FreeClusters, + AllocatedClusters, + UnknownClusters + } CurrentRun; + + PAGED_CODE(); + + // + // Now assert correct usage. + // + + FatIndexBitSize = Vcb->AllocationSupport.FatIndexBitSize; + + NT_ASSERT( !(SetupWindows && (SwitchToWindow || BitMapBuffer))); + NT_ASSERT( !(SetupWindows && FatIndexBitSize != 32)); + + if (Vcb->NumberOfWindows > 1) { + + // + // FAT32: Calculate the number of FAT entries covered by a window. This is + // equal to the number of bits in the freespace bitmap, the size of which + // is hardcoded. + // + + EntriesPerWindow = MAX_CLUSTER_BITMAP_SIZE; + + } else { + + EntriesPerWindow = Vcb->AllocationSupport.NumberOfClusters; + } + + // + // We will also fill in the cumulative count of free clusters for + // the entire volume. If this is not appropriate, NULL it out + // shortly. + // + + FreeClusterCount = &Vcb->AllocationSupport.NumberOfFreeClusters; + + if (SetupWindows) { + + NT_ASSERT(BitMapBuffer == NULL); + + // + // In this case we're just supposed to scan the fat and set up + // the information regarding where the buckets fall and how many + // free clusters are in each. + // + // It is fine to monkey with the real windows, we must be able + // to do this to activate the volume. + // + + BitMap = NULL; + + CurrentWindow = &Vcb->Windows[0]; + CurrentWindow->FirstCluster = StartIndex; + CurrentWindow->ClustersFree = 0; + + // + // We always wish to calculate total free clusters when + // setting up the FAT windows. + // + + } else if (BitMapBuffer == NULL) { + + // + // We will be filling in the free cluster bitmap for the volume. + // Careful, we can raise out of here and be hopelessly hosed if + // we built this up in the main bitmap/window itself. + // + // For simplicity's sake, we'll do the swap for everyone. FAT32 + // provokes the need since we can't tolerate partial results + // when switching windows. + // + + NT_ASSERT( SwitchToWindow ); + + CurrentWindow = SwitchToWindow; + StartIndex = CurrentWindow->FirstCluster; + EndIndex = CurrentWindow->LastCluster; + + BitMap = &PrivateBitMap; + NewBitMapBuffer = FsRtlAllocatePoolWithTag( PagedPool, + (EntriesPerWindow + 7) / 8, + TAG_FAT_BITMAP ); + + RtlInitializeBitMap( &PrivateBitMap, + NewBitMapBuffer, + EndIndex - StartIndex + 1); + + if ((FatIndexBitSize == 32) && + (Vcb->NumberOfWindows > 1)) { + + // + // We do not wish count total clusters here. + // + + FreeClusterCount = NULL; + + } + + } else { + + BitMap = &PrivateBitMap; + RtlInitializeBitMap(&PrivateBitMap, + BitMapBuffer, + EndIndex - StartIndex + 1); + + // + // We do not count total clusters here. + // + + FreeClusterCount = NULL; + } + + // + // Now, our start index better be in the file heap. + // + + NT_ASSERT( StartIndex >= 2 ); + + try { + + // + // Pick up the initial chunk of the FAT and first entry. + // + + if (FatIndexBitSize == 12) { + + // + // We read in the entire fat in the 12 bit case. + // + + FatReadVolumeFile( IrpContext, + Vcb, + FatReservedBytes( &Vcb->Bpb ), + FatBytesPerFat( &Vcb->Bpb ), + &Bcb, + (PVOID *)&FatBuffer ); + + FatLookup12BitEntry(FatBuffer, 0, &FirstFatEntry); + + } else { + + // + // Read in one page of fat at a time. We cannot read in the + // all of the fat we need because of cache manager limitations. + // + + ULONG BytesPerEntry = FatIndexBitSize >> 3; + + FatPages = (FatReservedBytes(&Vcb->Bpb) + FatBytesPerFat(&Vcb->Bpb) + (PAGE_SIZE - 1)) / PAGE_SIZE; + Page = (FatReservedBytes(&Vcb->Bpb) + StartIndex * BytesPerEntry) / PAGE_SIZE; + + Offset = Page * PAGE_SIZE; + + // + // Prefetch the FAT entries in memory for optimal performance. + // + + PrefetchPages = FatPages - Page; + + if (PrefetchPages > FAT_PREFETCH_PAGE_COUNT) { + + PrefetchPages = ALIGN_UP_BY(Page, FAT_PREFETCH_PAGE_COUNT) - Page; + } + +#if (NTDDI_VERSION >= NTDDI_WIN8) + FatPrefetchPages( IrpContext, + Vcb->VirtualVolumeFile, + Page, + PrefetchPages ); +#endif + + FatReadVolumeFile( IrpContext, + Vcb, + Offset, + PAGE_SIZE, + &Bcb, + &pv); + + if (FatIndexBitSize == 32) { + + FatBuffer = (PUSHORT)((PUCHAR)pv + + (FatReservedBytes(&Vcb->Bpb) + StartIndex * BytesPerEntry) % + PAGE_SIZE); + + FirstFatEntry = *((PULONG)FatBuffer); + FirstFatEntry = FirstFatEntry & FAT32_ENTRY_MASK; + + } else { + + FatBuffer = (PUSHORT)((PUCHAR)pv + + FatReservedBytes(&Vcb->Bpb) % PAGE_SIZE) + 2; + + FirstFatEntry = *FatBuffer; + } + + } + + ClusterSize = 1 << (Vcb->AllocationSupport.LogOfBytesPerCluster); + + CurrentRun = (FirstFatEntry == FAT_CLUSTER_AVAILABLE) ? + FreeClusters : AllocatedClusters; + + StartIndexOfThisRun = StartIndex; + + for (FatIndex = StartIndex; FatIndex <= EndIndex; FatIndex++) { + + if (FatIndexBitSize == 12) { + + FatLookup12BitEntry(FatBuffer, FatIndex, &FatEntry); + + } else { + + // + // If we are setting up the FAT32 windows and have stepped into a new + // bucket, finalize this one and move forward. + // + + if (SetupWindows && + FatIndex > StartIndex && + (FatIndex - 2) % EntriesPerWindow == 0) { + + CurrentWindow->LastCluster = FatIndex - 1; + + if (CurrentRun == FreeClusters) { + + // + // We must be counting clusters in order to modify the + // contents of the window. + // + + NT_ASSERT( FreeClusterCount ); + + ClustersThisRun = FatIndex - StartIndexOfThisRun; + CurrentWindow->ClustersFree += ClustersThisRun; + + if (FreeClusterCount) { + *FreeClusterCount += ClustersThisRun; + } + + } else { + + NT_ASSERT(CurrentRun == AllocatedClusters); + + } + + StartIndexOfThisRun = FatIndex; + CurrentRun = UnknownClusters; + + CurrentWindow++; + CurrentWindow->ClustersFree = 0; + CurrentWindow->FirstCluster = FatIndex; + } + + // + // If we just stepped onto a new page, grab a new pointer. + // + + if (((ULONG_PTR)FatBuffer & (PAGE_SIZE - 1)) == 0) { + + FatUnpinBcb( IrpContext, Bcb ); + + Page++; + Offset += PAGE_SIZE; + +#if (NTDDI_VERSION >= NTDDI_WIN8) + // + // If we have exhausted all the prefetch pages, prefetch the next chunk. + // + + if (--PrefetchPages == 0) { + + PrefetchPages = FatPages - Page; + + if (PrefetchPages > FAT_PREFETCH_PAGE_COUNT) { + + PrefetchPages = FAT_PREFETCH_PAGE_COUNT; + } + + FatPrefetchPages( IrpContext, + Vcb->VirtualVolumeFile, + Page, + PrefetchPages ); + } +#endif + + FatReadVolumeFile( IrpContext, + Vcb, + Offset, + PAGE_SIZE, + &Bcb, + &pv ); + + FatBuffer = (PUSHORT)pv; + } + + if (FatIndexBitSize == 32) { + +#pragma warning( suppress: 4213 ) + FatEntry = *((PULONG)FatBuffer)++; + FatEntry = FatEntry & FAT32_ENTRY_MASK; + + } else { + + FatEntry = *FatBuffer; + FatBuffer += 1; + } + } + + if (CurrentRun == UnknownClusters) { + + CurrentRun = (FatEntry == FAT_CLUSTER_AVAILABLE) ? + FreeClusters : AllocatedClusters; + } + + // + // Are we switching from a free run to an allocated run? + // + + if (CurrentRun == FreeClusters && + FatEntry != FAT_CLUSTER_AVAILABLE) { + + ClustersThisRun = FatIndex - StartIndexOfThisRun; + + if (FreeClusterCount) { + + *FreeClusterCount += ClustersThisRun; + CurrentWindow->ClustersFree += ClustersThisRun; + } + + if (BitMap) { + + RtlClearBits( BitMap, + StartIndexOfThisRun - StartIndex, + ClustersThisRun ); + } + + CurrentRun = AllocatedClusters; + StartIndexOfThisRun = FatIndex; + } + + // + // Are we switching from an allocated run to a free run? + // + + if (CurrentRun == AllocatedClusters && + FatEntry == FAT_CLUSTER_AVAILABLE) { + + ClustersThisRun = FatIndex - StartIndexOfThisRun; + + if (BitMap) { + + RtlSetBits( BitMap, + StartIndexOfThisRun - StartIndex, + ClustersThisRun ); + } + + CurrentRun = FreeClusters; + StartIndexOfThisRun = FatIndex; + } + + // + // If the entry is marked bad, add it to the bad block MCB + // + + if ((SetupWindows || (Vcb->NumberOfWindows == 1)) && + (FatInterpretClusterType( Vcb, FatEntry ) == FatClusterBad)) { + + // + // This cluster is marked bad. + // Add it to the BadBlockMcb. + // + + Lbo = FatGetLboFromIndex( Vcb, FatIndex ); + FatAddMcbEntry( Vcb, &Vcb->BadBlockMcb, BadClusterVbo, Lbo, ClusterSize ); + BadClusterVbo += ClusterSize; + } + } + + // + // If we finished the scan, then we know about all the possible bad clusters. + // + + SetFlag( Vcb->VcbState, VCB_STATE_FLAG_BAD_BLOCKS_POPULATED); + + // + // Now we have to record the final run we encountered + // + + ClustersThisRun = FatIndex - StartIndexOfThisRun; + + if (CurrentRun == FreeClusters) { + + if (FreeClusterCount) { + + *FreeClusterCount += ClustersThisRun; + CurrentWindow->ClustersFree += ClustersThisRun; + } + + if (BitMap) { + + RtlClearBits( BitMap, + StartIndexOfThisRun - StartIndex, + ClustersThisRun ); + } + + } else { + + if (BitMap) { + + RtlSetBits( BitMap, + StartIndexOfThisRun - StartIndex, + ClustersThisRun ); + } + } + + // + // And finish the last window if we are in setup. + // + + if (SetupWindows) { + + CurrentWindow->LastCluster = FatIndex - 1; + } + + // + // Now switch the active window if required. We've succesfully gotten everything + // nailed down. + // + // If we were tracking the free cluster count, this means we should update the + // window. This is the case of FAT12/16 initialization. + // + + if (SwitchToWindow) { + + if (Vcb->FreeClusterBitMap.Buffer) { + + ExFreePool( Vcb->FreeClusterBitMap.Buffer ); + } + + RtlInitializeBitMap( &Vcb->FreeClusterBitMap, + NewBitMapBuffer, + EndIndex - StartIndex + 1 ); + + NewBitMapBuffer = NULL; + + Vcb->CurrentWindow = SwitchToWindow; + Vcb->ClusterHint = (ULONG)-1; + + if (FreeClusterCount) { + + NT_ASSERT( !SetupWindows ); + + Vcb->CurrentWindow->ClustersFree = *FreeClusterCount; + } + } + + // + // Make sure plausible things occured ... + // + + if (!SetupWindows && BitMapBuffer == NULL) { + + ASSERT_CURRENT_WINDOW_GOOD( Vcb ); + } + + NT_ASSERT(Vcb->AllocationSupport.NumberOfFreeClusters <= Vcb->AllocationSupport.NumberOfClusters); + + } finally { + + // + // Unpin the last bcb and drop the temporary bitmap buffer if it exists. + // + + FatUnpinBcb( IrpContext, Bcb); + + if (NewBitMapBuffer) { + + ExFreePool( NewBitMapBuffer ); + } + } +} + diff --git a/filesys/fastfat/cachesup.c b/filesys/fastfat/cachesup.c new file mode 100644 index 000000000..5203814b7 --- /dev/null +++ b/filesys/fastfat/cachesup.c @@ -0,0 +1,2007 @@ +/*++ + +Copyright (c) 1990-2000 Microsoft Corporation + +Module Name: + + cache.c + +Abstract: + + This module implements the cache management routines for the Fat + FSD and FSP, by calling the Common Cache Manager. + + +--*/ + +#include "FatProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (FAT_BUG_CHECK_CACHESUP) + +// +// Local debug trace level +// + +#define Dbg (DEBUG_TRACE_CACHESUP) + +#if DBG + +BOOLEAN +FatIsCurrentOperationSynchedForDcbTeardown ( + IN PIRP_CONTEXT IrpContext, + IN PDCB Dcb + ); + +#endif + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, FatCloseEaFile) +#pragma alloc_text(PAGE, FatCompleteMdl) +#pragma alloc_text(PAGE, FatOpenDirectoryFile) +#pragma alloc_text(PAGE, FatOpenEaFile) +#pragma alloc_text(PAGE, FatPinMappedData) +#pragma alloc_text(PAGE, FatPrepareWriteDirectoryFile) +#pragma alloc_text(PAGE, FatPrepareWriteVolumeFile) +#pragma alloc_text(PAGE, FatReadDirectoryFile) +#pragma alloc_text(PAGE, FatReadVolumeFile) +#pragma alloc_text(PAGE, FatRepinBcb) +#pragma alloc_text(PAGE, FatSyncUninitializeCacheMap) +#pragma alloc_text(PAGE, FatUnpinRepinnedBcbs) +#pragma alloc_text(PAGE, FatZeroData) +#pragma alloc_text(PAGE, FatPrefetchPages) +#if DBG +#pragma alloc_text(PAGE, FatIsCurrentOperationSynchedForDcbTeardown) +#endif +#endif + +VOID +FatInitializeCacheMap ( + _In_ PFILE_OBJECT FileObject, + _In_ PCC_FILE_SIZES FileSizes, + _In_ BOOLEAN PinAccess, + _In_ PCACHE_MANAGER_CALLBACKS Callbacks, + _In_ PVOID LazyWriteContext + ) +/*++ + +Routine Description: + + Wrapper over CcInitializeCacheMap and CcSetAdditionalCacheAttributesEx to initialize + caching and enable IO accounting on a file. + +--*/ + +{ + // + // Initialize caching + // + + CcInitializeCacheMap( FileObject, + FileSizes, + PinAccess, + Callbacks, + LazyWriteContext ); + +#if (NTDDI_VERSION >= NTDDI_WIN8) + // + // Enable Disk IO Accounting for this file + // + + if (FatDiskAccountingEnabled) { + + CcSetAdditionalCacheAttributesEx( FileObject, CC_ENABLE_DISK_IO_ACCOUNTING ); + } +#endif +} + +VOID +FatReadVolumeFile ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN VBO StartingVbo, + IN ULONG ByteCount, + OUT PBCB *Bcb, + OUT PVOID *Buffer + ) + +/*++ + +Routine Description: + + This routine is called when the specified range of sectors is to be + read into the cache. In fat, the volume file only contains the boot + sector, reserved sectors, and the "fat(s)." Thus the volume file is + of fixed size and only extends up to (but not not including) the root + directory entry, and will never move or change size. + + The fat volume file is also peculiar in that, since it starts at the + logical beginning of the disk, Vbo == Lbo. + +Arguments: + + Vcb - Pointer to the VCB for the volume + + StartingVbo - The virtual offset of the first desired byte + + ByteCount - Number of bytes desired + + Bcb - Returns a pointer to the BCB which is valid until unpinned + + Buffer - Returns a pointer to the sectors, which is valid until unpinned + +--*/ + +{ + LARGE_INTEGER Vbo; + + PAGED_CODE(); + + // + // Check to see that all references are within the Bios Parameter Block + // or the fat(s). A special case is made when StartingVbo == 0 at + // mounting time since we do not know how big the fat is. + // + + NT_ASSERT( ((StartingVbo == 0) || ((StartingVbo + ByteCount) <= (ULONG) + (FatRootDirectoryLbo( &Vcb->Bpb ) + PAGE_SIZE)))); + + DebugTrace(+1, Dbg, "FatReadVolumeFile\n", 0); + DebugTrace( 0, Dbg, "Vcb = %p\n", Vcb); + DebugTrace( 0, Dbg, "StartingVbo = %08lx\n", StartingVbo); + DebugTrace( 0, Dbg, "ByteCount = %08lx\n", ByteCount); + + // + // Call the Cache manager to attempt the transfer. + // + + Vbo.QuadPart = StartingVbo; + + if (!CcMapData( Vcb->VirtualVolumeFile, + &Vbo, + ByteCount, + BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT), + Bcb, + Buffer )) { + + NT_ASSERT( !FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) ); + + // + // Could not read the data without waiting (cache miss). + // + + FatRaiseStatus( IrpContext, STATUS_CANT_WAIT ); + } + + DbgDoit( IrpContext->PinCount += 1 ) + + DebugTrace(-1, Dbg, "FatReadVolumeFile -> VOID, *BCB = %p\n", *Bcb); + + return; +} + + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatPrepareWriteVolumeFile ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN VBO StartingVbo, + IN ULONG ByteCount, + OUT PBCB *Bcb, + OUT PVOID *Buffer, + IN BOOLEAN Reversible, + IN BOOLEAN Zero + ) + +/*++ + +Routine Description: + + This routine first looks to see if the specified range of sectors, + is already in the cache. If so, it increments the BCB PinCount, + sets the BCB dirty, and returns with the location of the sectors. + + If the sectors are not in the cache and Wait is TRUE, it finds a + free BCB (potentially causing a flush), and clears out the entire + buffer. Once this is done, it increments the BCB PinCount, sets the + BCB dirty, and returns with the location of the sectors. + + If the sectors are not in the cache and Wait is FALSE, this routine + raises STATUS_CANT_WAIT. + +Arguments: + + Vcb - Pointer to the VCB for the volume + + StartingVbo - The virtual offset of the first byte to be written + + ByteCount - Number of bytes to be written + + Bcb - Returns a pointer to the BCB which is valid until unpinned + + Buffer - Returns a pointer to the sectors, which is valid until unpinned + + Reversible - Supplies TRUE if the specified range of modification should + be repinned so that the operation can be reversed in a controlled + fashion if errors are encountered. + + Zero - Supplies TRUE if the specified range of bytes should be zeroed + +--*/ + +{ + LARGE_INTEGER Vbo; + + PAGED_CODE(); + + // + // Check to see that all references are within the Bios Parameter Block + // or the fat(s). + // + + NT_ASSERT( ((StartingVbo + ByteCount) <= (ULONG) + (FatRootDirectoryLbo( &Vcb->Bpb )))); + + DebugTrace(+1, Dbg, "FatPrepareWriteVolumeFile\n", 0); + DebugTrace( 0, Dbg, "Vcb = %p\n", Vcb); + DebugTrace( 0, Dbg, "StartingVbo = %08lx\n", (ULONG)StartingVbo); + DebugTrace( 0, Dbg, "ByteCount = %08lx\n", ByteCount); + DebugTrace( 0, Dbg, "Zero = %08lx\n", Zero); + + // + // Call the Cache manager to attempt the transfer. + // + + Vbo.QuadPart = StartingVbo; + + if (!CcPinRead( Vcb->VirtualVolumeFile, + &Vbo, + ByteCount, + BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT), + Bcb, + Buffer )) { + + NT_ASSERT( !FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) ); + + // + // Could not read the data without waiting (cache miss). + // + + FatRaiseStatus( IrpContext, STATUS_CANT_WAIT ); + } + + // + // This keeps the data pinned until we complete the request + // and writes the dirty bit through to the disk. + // + + DbgDoit( IrpContext->PinCount += 1 ) + + try { + + if (Zero) { + + RtlZeroMemory( *Buffer, ByteCount ); + } + + FatSetDirtyBcb( IrpContext, *Bcb, Vcb, Reversible ); + + } finally { + + if (AbnormalTermination()) { + + FatUnpinBcb(IrpContext, *Bcb); + } + } + + DebugTrace(-1, Dbg, "FatPrepareWriteVolumeFile -> VOID, *Bcb = %p\n", *Bcb); + + return; +} + + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatReadDirectoryFile ( + IN PIRP_CONTEXT IrpContext, + IN PDCB Dcb, + IN VBO StartingVbo, + IN ULONG ByteCount, + IN BOOLEAN Pin, + OUT PBCB *Bcb, + OUT PVOID *Buffer, + OUT PNTSTATUS Status + ) + +/*++ + +Routine Description: + + This routine is called when the specified range of sectors is to be + read into the cache. If the desired range falls beyond the current + cache mapping, the fat will be searched, and if the desired range can + be satisfied, the cache mapping will be extended and the MCB updated + accordingly. + +Arguments: + + Dcb - Pointer to the DCB for the directory + + StartingVbo - The virtual offset of the first desired byte + + ByteCount - Number of bytes desired + + Pin - Tells us if we should pin instead of just mapping. + + Bcb - Returns a pointer to the BCB which is valid until unpinned + + Buffer - Returns a pointer to the sectors, which is valid until unpinned + + Status - Returns the status of the operation. + +--*/ + +{ + LARGE_INTEGER Vbo; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatReadDirectoryFile\n", 0); + DebugTrace( 0, Dbg, "Dcb = %p\n", Dcb); + DebugTrace( 0, Dbg, "StartingVbo = %08lx\n", StartingVbo); + DebugTrace( 0, Dbg, "ByteCount = %08lx\n", ByteCount); + + // + // Check for the zero case + // + + if (ByteCount == 0) { + + DebugTrace(0, Dbg, "Nothing to read\n", 0); + + *Bcb = NULL; + *Buffer = NULL; + *Status = STATUS_SUCCESS; + + DebugTrace(-1, Dbg, "FatReadDirectoryFile -> VOID\n", 0); + return; + } + + // + // If we need to create a directory file and initialize the + // cachemap, do so. + // + + FatOpenDirectoryFile( IrpContext, Dcb ); + + // + // Now if the transfer is beyond the allocation size return EOF. + // + + if (StartingVbo >= Dcb->Header.AllocationSize.LowPart) { + + DebugTrace(0, Dbg, "End of file read for directory\n", 0); + + *Bcb = NULL; + *Buffer = NULL; + *Status = STATUS_END_OF_FILE; + + DebugTrace(-1, Dbg, "FatReadDirectoryFile -> VOID\n", 0); + return; + } + + // + // If the caller is trying to read past the EOF, truncate the + // read. + // + + ByteCount = (Dcb->Header.AllocationSize.LowPart - StartingVbo < ByteCount) ? + Dcb->Header.AllocationSize.LowPart - StartingVbo : ByteCount; + + NT_ASSERT( ByteCount != 0 ); + + // + // Call the Cache manager to attempt the transfer. + // + + Vbo.QuadPart = StartingVbo; + + if (Pin ? + + !CcPinRead( Dcb->Specific.Dcb.DirectoryFile, + &Vbo, + ByteCount, + BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT), + Bcb, + Buffer ) + : + + !CcMapData( Dcb->Specific.Dcb.DirectoryFile, + &Vbo, + ByteCount, + BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT), + Bcb, + Buffer ) ) { + + // + // Could not read the data without waiting (cache miss). + // + + *Bcb = NULL; + *Buffer = NULL; + FatRaiseStatus( IrpContext, STATUS_CANT_WAIT ); + } + + DbgDoit( IrpContext->PinCount += 1 ) + + *Status = STATUS_SUCCESS; + + DebugTrace(-1, Dbg, "FatReadDirectoryFile -> VOID, *BCB = %p\n", *Bcb); + + return; +} + + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatPrepareWriteDirectoryFile ( + IN PIRP_CONTEXT IrpContext, + IN PDCB Dcb, + IN VBO StartingVbo, + IN ULONG ByteCount, + OUT PBCB *Bcb, + OUT PVOID *Buffer, + IN BOOLEAN Zero, + IN BOOLEAN Reversible, + OUT PNTSTATUS Status + ) + +/*++ + +Routine Description: + + This routine first looks to see if the specified range of sectors + is already in the cache. If so, it increments the BCB PinCount, + sets the BCB dirty, and returns TRUE with the location of the sectors. + + The IrpContext->Flags .. Wait == TRUE/FALSE actions of this routine are identical to + FatPrepareWriteVolumeFile() above. + +Arguments: + + Dcb - Pointer to the DCB for the directory + + StartingVbo - The virtual offset of the first byte to be written + + ByteCount - Number of bytes to be written + + Bcb - Returns a pointer to the BCB which is valid until unpinned + + Buffer - Returns a pointer to the sectors, which is valid until unpinned + + Zero - Supplies TRUE if the specified range of bytes should be zeroed + + Reversible - Supplies TRUE if the specified range of modification should + be repinned so that the operation can be reversed in a controlled + fashion if errors are encountered. + + Status - Returns the status of the operation. + +--*/ + +{ + LARGE_INTEGER Vbo; + ULONG InitialAllocation = 0; + BOOLEAN UnwindWeAllocatedDiskSpace = FALSE; + PBCB LocalBcb = NULL; + PVOID LocalBuffer = NULL; + ULONG InitialRequest = ByteCount; + ULONG MappingGranularity = PAGE_SIZE; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatPrepareWriteDirectoryFile\n", 0); + DebugTrace( 0, Dbg, "Dcb = %p\n", Dcb); + DebugTrace( 0, Dbg, "StartingVbo = %08lx\n", (ULONG)StartingVbo); + DebugTrace( 0, Dbg, "ByteCount = %08lx\n", ByteCount); + DebugTrace( 0, Dbg, "Zero = %08lx\n", Zero); + + *Bcb = NULL; + *Buffer = NULL; + + // + // If we need to create a directory file and initialize the + // cachemap, do so. + // + + FatOpenDirectoryFile( IrpContext, Dcb ); + + // + // If the transfer is beyond the allocation size we need to + // extend the directory's allocation. The call to + // AddFileAllocation will raise a condition if + // it runs out of disk space. Note that the root directory + // cannot be extended. + // + + Vbo.QuadPart = StartingVbo; + + try { + + if (StartingVbo + ByteCount > Dcb->Header.AllocationSize.LowPart) { + + if (NodeType(Dcb) == FAT_NTC_ROOT_DCB && + !FatIsFat32(Dcb->Vcb)) { + + FatRaiseStatus( IrpContext, STATUS_DISK_FULL ); + } + + DebugTrace(0, Dbg, "Try extending normal directory\n", 0); + + InitialAllocation = Dcb->Header.AllocationSize.LowPart; + + FatAddFileAllocation( IrpContext, + Dcb, + Dcb->Specific.Dcb.DirectoryFile, + StartingVbo + ByteCount ); + + UnwindWeAllocatedDiskSpace = TRUE; + + // + // Inform the cache manager of the new allocation + // + + Dcb->Header.FileSize.LowPart = + Dcb->Header.AllocationSize.LowPart; + + CcSetFileSizes( Dcb->Specific.Dcb.DirectoryFile, + (PCC_FILE_SIZES)&Dcb->Header.AllocationSize ); + + // + // Set up the Bitmap buffer if it is not big enough already + // + + FatCheckFreeDirentBitmap( IrpContext, Dcb ); + + // + // The newly allocated clusters should be zeroed starting at + // the previous allocation size + // + + Zero = TRUE; + Vbo.QuadPart = InitialAllocation; + ByteCount = Dcb->Header.AllocationSize.LowPart - InitialAllocation; + } + + while (ByteCount > 0) { + + ULONG BytesToPin; + + LocalBcb = NULL; + + // + // We must pin in terms of pages below the boundary of the initial request. + // Once we pass the end of the request, we are free to expand the pin size to + // VACB_MAPPING_GRANULARITY. This will prevent Cc from returning OBCBs + // and hence will prevent bugchecks when we then attempt to repin one, yet + // allow us to be more efficient by pinning in 256KB chunks instead of 4KB pages. + // + + if (Vbo.QuadPart > StartingVbo + InitialRequest) { + + MappingGranularity = VACB_MAPPING_GRANULARITY; + } + + // + // If the first and final byte are both described by the same page, pin + // the entire range. Note we pin in pages to prevent cache manager from + // returning OBCBs, which would result in a bugcheck on CcRepinBcb. + // + + if ((Vbo.QuadPart / MappingGranularity) == + ((Vbo.QuadPart + ByteCount - 1) / MappingGranularity)) { + + BytesToPin = ByteCount; + + } else { + + BytesToPin = MappingGranularity - + ((ULONG)Vbo.QuadPart & (MappingGranularity - 1)); + } + + if (!CcPinRead( Dcb->Specific.Dcb.DirectoryFile, + &Vbo, + BytesToPin, + BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT), + &LocalBcb, + &LocalBuffer )) { + + // + // Could not read the data without waiting (cache miss). + // + + FatRaiseStatus( IrpContext, STATUS_CANT_WAIT ); + } + + // + // Update our caller with the beginning of their request. + // + + if (*Buffer == NULL) { + + *Buffer = LocalBuffer; + *Bcb = LocalBcb; + } + + DbgDoit( IrpContext->PinCount += 1 ) + + if (Zero) { + + // + // We set this guy dirty right now so that we can raise CANT_WAIT when + // it needs to be done. It'd be beautiful if we could noop the read IO + // since we know we don't care about it. + // + + RtlZeroMemory( LocalBuffer, BytesToPin ); + CcSetDirtyPinnedData( LocalBcb, NULL ); + } + + ByteCount -= BytesToPin; + Vbo.QuadPart += BytesToPin; + + if (*Bcb != LocalBcb) { + + FatRepinBcb( IrpContext, LocalBcb ); + FatUnpinBcb( IrpContext, LocalBcb ); + } + } + + // + // This lets us get the data pinned until we complete the request + // and writes the dirty bit through to the disk. + // + + FatSetDirtyBcb( IrpContext, *Bcb, Dcb->Vcb, Reversible ); + + *Status = STATUS_SUCCESS; + + } finally { + + DebugUnwind( FatPrepareWriteDirectoryFile ); + + if (AbnormalTermination()) { + + // + // Make sure we unpin the buffers. + // + + if (*Bcb != LocalBcb) { + + FatUnpinBcb( IrpContext, LocalBcb ); + } + + FatUnpinBcb(IrpContext, *Bcb); + + // + // These steps are carefully arranged - FatTruncateFileAllocation can raise. + // Make sure we unpin the buffer. If FTFA raises, the effect should be benign. + // + + if (UnwindWeAllocatedDiskSpace == TRUE) { + + // + // Inform the cache manager of the change. + // + + FatTruncateFileAllocation( IrpContext, Dcb, InitialAllocation, FALSE ); + + Dcb->Header.FileSize.LowPart = + Dcb->Header.AllocationSize.LowPart; + + CcSetFileSizes( Dcb->Specific.Dcb.DirectoryFile, + (PCC_FILE_SIZES)&Dcb->Header.AllocationSize ); + } + } + + DebugTrace(-1, Dbg, "FatPrepareWriteDirectoryFile -> (VOID), *Bcb = %p\n", *Bcb); + } + + return; +} + + +#if DBG +BOOLEAN FatDisableParentCheck = 0; + +BOOLEAN +FatIsCurrentOperationSynchedForDcbTeardown ( + IN PIRP_CONTEXT IrpContext, + IN PDCB Dcb + ) +{ + PIRP Irp = IrpContext->OriginatingIrp; + PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation( Irp ) ; + PVCB Vcb; + PFCB Fcb; + PCCB Ccb; + + PFILE_OBJECT ToCheck[3]; + ULONG Index = 0; + + PAGED_CODE(); + + // + // While mounting, we're OK without having to own anything. + // + + if (Stack->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL && + Stack->MinorFunction == IRP_MN_MOUNT_VOLUME) { + + return TRUE; + } + + // + // With the Vcb held, the close path is blocked out. + // + + if (ExIsResourceAcquiredSharedLite( &Dcb->Vcb->Resource ) || + ExIsResourceAcquiredExclusiveLite( &Dcb->Vcb->Resource )) { + + return TRUE; + } + + // + // Accept this assertion at face value. It comes from GetDirentForFcbOrDcb, + // and is reliable. + // + + if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_PARENT_BY_CHILD )) { + + return TRUE; + } + + // + // Determine which fileobjects are around on this operation. + // + + if (Stack->MajorFunction == IRP_MJ_SET_INFORMATION && + Stack->Parameters.SetFile.FileObject) { + + ToCheck[Index++] = Stack->Parameters.SetFile.FileObject; + } + + if (Stack->FileObject) { + + ToCheck[Index++] = Stack->FileObject; + } + + ToCheck[Index] = NULL; + + // + // If the fileobjects we have are for this dcb or a child of it, we are + // also guaranteed that this dcb isn't going anywhere (even without + // the Vcb). + // + + for (Index = 0; ToCheck[Index] != NULL; Index++) { + + (VOID) FatDecodeFileObject( ToCheck[Index], &Vcb, &Fcb, &Ccb ); + + while ( Fcb ) { + + if (Fcb == Dcb) { + + return TRUE; + } + + Fcb = Fcb->ParentDcb; + } + } + + return FatDisableParentCheck; +} +#endif // DBG + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatOpenDirectoryFile ( + IN PIRP_CONTEXT IrpContext, + IN PDCB Dcb + ) + +/*++ + +Routine Description: + + This routine opens a new directory file if one is not already open. + +Arguments: + + Dcb - Pointer to the DCB for the directory + +Return Value: + + None. + +--*/ + +{ + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatOpenDirectoryFile\n", 0); + DebugTrace( 0, Dbg, "Dcb = %p\n", Dcb); + + // + // If we don't have some hold on this Dcb (there are several ways), there is nothing + // to prevent child files from closing and tearing this branch of the tree down in the + // midst of our slapping this reference onto it. + // + // I really wish we had a proper Fcb synchronization model (like CDFS/UDFS/NTFS). + // + + NT_ASSERT( FatIsCurrentOperationSynchedForDcbTeardown( IrpContext, Dcb )); + + // + // If we haven't yet set the correct AllocationSize, do so. + // + + if (Dcb->Header.AllocationSize.QuadPart == FCB_LOOKUP_ALLOCATIONSIZE_HINT) { + + FatLookupFileAllocationSize( IrpContext, Dcb ); + + Dcb->Header.FileSize.LowPart = + Dcb->Header.AllocationSize.LowPart; + } + + // + // Setup the Bitmap buffer if it is not big enough already + // + + FatCheckFreeDirentBitmap( IrpContext, Dcb ); + + // + // Check if we need to create a directory file. + // + // We first do a spot check and then synchronize and check again. + // + + if (Dcb->Specific.Dcb.DirectoryFile == NULL) { + + PFILE_OBJECT DirectoryFileObject = NULL; + + FatAcquireDirectoryFileMutex( Dcb->Vcb ); + + try { + + if (Dcb->Specific.Dcb.DirectoryFile == NULL) { + + PDEVICE_OBJECT RealDevice; + + // + // Create the special file object for the directory file, and set + // up its pointers back to the Dcb and the section object pointer. + // Note that setting the DirectoryFile pointer in the Dcb has + // to be the last thing done. + // + // Preallocate a close context since we have no Ccb for this object. + // + + RealDevice = Dcb->Vcb->CurrentDevice; + + DirectoryFileObject = IoCreateStreamFileObject( NULL, RealDevice ); + FatPreallocateCloseContext( Dcb->Vcb); + + FatSetFileObject( DirectoryFileObject, + DirectoryFile, + Dcb, + NULL ); + + // + // Remember this internal open. + // + + InterlockedIncrement( (LONG*)&(Dcb->Vcb->InternalOpenCount) ); + + // + // If this is the root directory, it is also a residual open. + // + + if (NodeType( Dcb ) == FAT_NTC_ROOT_DCB) { + + InterlockedIncrement( (LONG*)&(Dcb->Vcb->ResidualOpenCount) ); + } + + DirectoryFileObject->SectionObjectPointer = &Dcb->NonPaged->SectionObjectPointers; + + DirectoryFileObject->ReadAccess = TRUE; + DirectoryFileObject->WriteAccess = TRUE; + DirectoryFileObject->DeleteAccess = TRUE; + + InterlockedIncrement( (LONG*)&Dcb->Specific.Dcb.DirectoryFileOpenCount ); + + Dcb->Specific.Dcb.DirectoryFile = DirectoryFileObject; + + // + // Indicate we're happy with the fileobject now. + // + + DirectoryFileObject = NULL; + } + + } finally { + + FatReleaseDirectoryFileMutex( Dcb->Vcb ); + + // + // Rip the object up if we couldn't get the close context. + // + + if (DirectoryFileObject) { + + ObDereferenceObject( DirectoryFileObject ); + } + } + } + + // + // Finally check if we need to initialize the Cache Map for the + // directory file. The size of the section we are going to map + // the current allocation size for the directory. Note that the + // cache manager will provide syncronization for us. + // + + if ( Dcb->Specific.Dcb.DirectoryFile->PrivateCacheMap == NULL ) { + + Dcb->Header.ValidDataLength = FatMaxLarge; + Dcb->ValidDataToDisk = MAXULONG; + + FatInitializeCacheMap( Dcb->Specific.Dcb.DirectoryFile, + (PCC_FILE_SIZES)&Dcb->Header.AllocationSize, + TRUE, + &FatData.CacheManagerNoOpCallbacks, + Dcb ); + } + + DebugTrace(-1, Dbg, "FatOpenDirectoryFile -> VOID\n", 0); + + return; +} + + + + +PFILE_OBJECT +FatOpenEaFile ( + IN PIRP_CONTEXT IrpContext, + IN PFCB EaFcb + ) + +/*++ + +Routine Description: + + This routine opens the Ea file. + +Arguments: + + EaFcb - Pointer to the Fcb for the Ea file. + +Return Value: + + Pointer to the new file object. + +--*/ + +{ + PFILE_OBJECT EaFileObject = NULL; + PDEVICE_OBJECT RealDevice; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatOpenEaFile\n", 0); + DebugTrace( 0, Dbg, "EaFcb = %p\n", EaFcb); + + // + // Create the special file object for the ea file, and set + // up its pointers back to the Fcb and the section object pointer + // + + RealDevice = EaFcb->Vcb->CurrentDevice; + + EaFileObject = IoCreateStreamFileObject( NULL, RealDevice ); + + try { + + FatPreallocateCloseContext( IrpContext->Vcb); + + FatSetFileObject( EaFileObject, + EaFile, + EaFcb, + NULL ); + + // + // Remember this internal, residual open. + // + + InterlockedIncrement( (LONG*)&(EaFcb->Vcb->InternalOpenCount) ); + InterlockedIncrement( (LONG*)&(EaFcb->Vcb->ResidualOpenCount) ); + + EaFileObject->SectionObjectPointer = &EaFcb->NonPaged->SectionObjectPointers; + + EaFileObject->ReadAccess = TRUE; + EaFileObject->WriteAccess = TRUE; + + // + // Finally check if we need to initialize the Cache Map for the + // ea file. The size of the section we are going to map + // the current allocation size for the Fcb. + // + + EaFcb->Header.ValidDataLength = FatMaxLarge; + + FatInitializeCacheMap( EaFileObject, + (PCC_FILE_SIZES)&EaFcb->Header.AllocationSize, + TRUE, + &FatData.CacheManagerCallbacks, + EaFcb ); + + CcSetAdditionalCacheAttributes( EaFileObject, TRUE, TRUE ); + + } finally { + + // + // Drop the fileobject if we're raising. Two cases: couldn't get + // the close context, and it is still an UnopenedFileObject, or + // we lost trying to build the cache map - in which case we're + // OK for the close context if we have to. + // + + if (AbnormalTermination()) { + + ObDereferenceObject( EaFileObject ); + } + } + + DebugTrace(-1, Dbg, "FatOpenEaFile -> %p\n", EaFileObject); + + UNREFERENCED_PARAMETER( IrpContext ); + + return EaFileObject; +} + + +VOID +FatCloseEaFile ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN BOOLEAN FlushFirst + ) + +/*++ + +Routine Description: + + This routine shuts down the ea file. Usually this is required when the volume + begins to leave the system: after verify, dismount, deletion, pnp. + +Arguments: + + Vcb - the volume to close the ea file on + + FlushFirst - whether the file should be flushed + +Return Value: + + None. As a side effect, the EA fileobject in the Vcb is cleared. + + Caller must have the Vcb exclusive. + +--*/ + +{ + PFILE_OBJECT EaFileObject = Vcb->VirtualEaFile; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatCloseEaFile\n", 0); + DebugTrace( 0, Dbg, "Vcb = %p\n", Vcb); + + NT_ASSERT( FatVcbAcquiredExclusive(IrpContext, Vcb) ); + + if (EaFileObject != NULL) { + + EaFileObject = Vcb->VirtualEaFile; + + if (FlushFirst) { + + CcFlushCache( Vcb->VirtualEaFile->SectionObjectPointer, NULL, 0, NULL ); + } + + Vcb->VirtualEaFile = NULL; + + // + // Empty the Mcb for the Ea file. + // + + FatRemoveMcbEntry( Vcb, &Vcb->EaFcb->Mcb, 0, 0xFFFFFFFF ); + + // + // Uninitialize the cache for this file object and dereference it. + // + + FatSyncUninitializeCacheMap( IrpContext, EaFileObject ); + + ObDereferenceObject( EaFileObject ); + } + + DebugTrace(-1, Dbg, "FatCloseEaFile -> %p\n", EaFileObject); +} + + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatSetDirtyBcb ( + IN PIRP_CONTEXT IrpContext, + IN PBCB Bcb, + IN PVCB Vcb OPTIONAL, + IN BOOLEAN Reversible + ) + +/*++ + +Routine Description: + + This routine saves a reference to the bcb in the irp context and + sets the bcb dirty. This will have the affect of keeping the page in + memory until we complete the request + + In addition, a DPC is set to fire in 5 seconds (or if one is pending, + pushed back 5 seconds) to mark the volume clean. + +Arguments: + + Bcb - Supplies the Bcb being set dirty + + Vcb - Supplies the volume being marked dirty + + Reversible - Supplies TRUE if the specified range of bcb should be repinned + so that the changes can be reversed in a controlled fashion if errors + are encountered. + +Return Value: + + None. + +--*/ + +{ + DebugTrace(+1, Dbg, "FatSetDirtyBcb\n", 0 ); + DebugTrace( 0, Dbg, "IrpContext = %p\n", IrpContext ); + DebugTrace( 0, Dbg, "Bcb = %p\n", Bcb ); + DebugTrace( 0, Dbg, "Vcb = %p\n", Vcb ); + + // + // Repin the bcb as required + // + + if (Reversible) { + + FatRepinBcb( IrpContext, Bcb ); + } + + // + // Set the bcb dirty + // + + CcSetDirtyPinnedData( Bcb, NULL ); + + // + // If volume dirtying isn't disabled for this operation (for + // instance, when we're changing the dirty state), set the + // volume dirty if we were given a Vcb that we want to perform + // clean volume processing on, and return. + // + // As a historical note, we used to key off of the old floppy + // (now deferred flush) bit to disable dirtying behavior. Since + // hotpluggable media can still be yanked while operations are + // in flight, recognize that its really the case that FAT12 + // doesn't have the dirty bit. + // + + if ( !FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_DIRTY) && + ARGUMENT_PRESENT(Vcb) && + !FatIsFat12(Vcb)) { + + KIRQL SavedIrql; + + BOOLEAN SetTimer; + + LARGE_INTEGER TimeSincePreviousCall; + LARGE_INTEGER CurrentTime; + + // + // "Borrow" the irp context spinlock. + // + + KeQuerySystemTime( &CurrentTime ); + + KeAcquireSpinLock( &FatData.GeneralSpinLock, &SavedIrql ); + + TimeSincePreviousCall.QuadPart = + CurrentTime.QuadPart - Vcb->LastFatMarkVolumeDirtyCall.QuadPart; + + // + // If more than one second has elapsed since the prior call + // to here, bump the timer up again and see if we need to + // physically mark the volume dirty. + // + + if ( (TimeSincePreviousCall.HighPart != 0) || + (TimeSincePreviousCall.LowPart > (1000 * 1000 * 10)) ) { + + SetTimer = TRUE; + + } else { + + SetTimer = FALSE; + } + + KeReleaseSpinLock( &FatData.GeneralSpinLock, SavedIrql ); + + if ( SetTimer ) { + + LARGE_INTEGER CleanVolumeTimer; + + // + // We use a shorter volume clean timer for hot plug volumes. + // + + CleanVolumeTimer.QuadPart = FlagOn( Vcb->VcbState, VCB_STATE_FLAG_DEFERRED_FLUSH) + ? (LONG)-1500*1000*10 + : (LONG)-8*1000*1000*10; + + (VOID)KeCancelTimer( &Vcb->CleanVolumeTimer ); + (VOID)KeRemoveQueueDpc( &Vcb->CleanVolumeDpc ); + + // + // We have now synchronized with anybody clearing the dirty + // flag, so we can now see if we really have to actually write + // out the physical bit. + // + + if ( !FlagOn(Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DIRTY) ) { + + // + // We want to really mark the volume dirty now. + // + + if (!FlagOn(Vcb->VcbState, VCB_STATE_FLAG_MOUNTED_DIRTY)) { + + FatMarkVolume( IrpContext, Vcb, VolumeDirty ); + } + + SetFlag( Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DIRTY ); + + // + // Lock the volume if it is removable. + // + + if (FlagOn( Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA)) { + + FatToggleMediaEjectDisable( IrpContext, Vcb, TRUE ); + } + } + + KeAcquireSpinLock( &FatData.GeneralSpinLock, &SavedIrql ); + + KeQuerySystemTime( &Vcb->LastFatMarkVolumeDirtyCall ); + + KeReleaseSpinLock( &FatData.GeneralSpinLock, SavedIrql ); + + KeSetTimer( &Vcb->CleanVolumeTimer, + CleanVolumeTimer, + &Vcb->CleanVolumeDpc ); + } + } + + DebugTrace(-1, Dbg, "FatSetDirtyBcb -> VOID\n", 0 ); +} + + +VOID +FatRepinBcb ( + IN PIRP_CONTEXT IrpContext, + IN PBCB Bcb + ) + +/*++ + +Routine Description: + + This routine saves a reference to the bcb in the irp context. This will + have the affect of keeping the page in memory until we complete the + request + +Arguments: + + Bcb - Supplies the Bcb being referenced + +Return Value: + + None. + +--*/ + +{ + PREPINNED_BCBS Repinned; + ULONG i; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatRepinBcb\n", 0 ); + DebugTrace( 0, Dbg, "IrpContext = %p\n", IrpContext ); + DebugTrace( 0, Dbg, "Bcb = %p\n", Bcb ); + + // + // The algorithm is to search the list of repinned records until + // we either find a match for the bcb or we find a null slot. + // + + Repinned = &IrpContext->Repinned; + + while (TRUE) { + + // + // For every entry in the repinned record check if the bcb's + // match or if the entry is null. If the bcb's match then + // we've done because we've already repinned this bcb, if + // the entry is null then we know, because it's densely packed, + // that the bcb is not in the list so add it to the repinned + // record and repin it. + // + + for (i = 0; i < REPINNED_BCBS_ARRAY_SIZE; i += 1) { + + if (Repinned->Bcb[i] == Bcb) { + + DebugTrace(-1, Dbg, "FatRepinBcb -> VOID\n", 0 ); + return; + } + + if (Repinned->Bcb[i] == NULL) { + + Repinned->Bcb[i] = Bcb; + CcRepinBcb( Bcb ); + + DebugTrace(-1, Dbg, "FatRepinBcb -> VOID\n", 0 ); + return; + } + } + + // + // We finished checking one repinned record so now locate the next + // repinned record, If there isn't one then allocate and zero out + // a new one. + // + + if (Repinned->Next == NULL) { + + Repinned->Next = FsRtlAllocatePoolWithTag( PagedPool, + sizeof(REPINNED_BCBS), + TAG_REPINNED_BCB ); + + RtlZeroMemory( Repinned->Next, sizeof(REPINNED_BCBS) ); + } + + Repinned = Repinned->Next; + } +} + + +VOID +FatUnpinRepinnedBcbs ( + IN PIRP_CONTEXT IrpContext + ) + +/*++ + +Routine Description: + + This routine frees all of the repinned bcbs, stored in an IRP context. + +Arguments: + +Return Value: + + None. + +--*/ + +{ + IO_STATUS_BLOCK RaiseIosb; + PREPINNED_BCBS Repinned; + BOOLEAN WriteThroughToDisk; + PFILE_OBJECT FileObject = NULL; + BOOLEAN ForceVerify = FALSE; + ULONG i; + PFCB FcbOrDcb = NULL; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatUnpinRepinnedBcbs\n", 0 ); + DebugTrace( 0, Dbg, "IrpContext = %p\n", IrpContext ); + + // + // The algorithm for this procedure is to scan the entire list of + // repinned records unpinning any repinned bcbs. We start off + // with the first record in the irp context, and while there is a + // record to scan we do the following loop. + // + + Repinned = &IrpContext->Repinned; + RaiseIosb.Status = STATUS_SUCCESS; + + // + // WinSE bug #307418 "Occasional data corruption when + // standby/resume while copying files to removable FAT + // formatted media". + // Extract main FCB pointer from the irp context - we + // will need it later to detect new file creation operation. + // + + if (IrpContext->MajorFunction == IRP_MJ_CREATE && + IrpContext->OriginatingIrp != NULL) { + PIO_STACK_LOCATION IrpSp; + + IrpSp = IoGetCurrentIrpStackLocation( IrpContext->OriginatingIrp ); + + if (IrpSp != NULL && + IrpSp->FileObject != NULL && + IrpSp->FileObject->FsContext != NULL) { + + FcbOrDcb = IrpSp->FileObject->FsContext; + } + } + + // + // If the request is write through or the media is deferred flush, + // unpin the bcb's write through. + // + + WriteThroughToDisk = (BOOLEAN) (!FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_WRITE_THROUGH) && + IrpContext->Vcb != NULL && + (FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WRITE_THROUGH) || + FlagOn(IrpContext->Vcb->VcbState, VCB_STATE_FLAG_DEFERRED_FLUSH))); + + while (Repinned != NULL) { + + // + // For every non-null entry in the repinned record unpin the + // repinned entry. + // + // If the this is removable media (therefore all requests write- + // through) and the write fails, purge the cache so that we throw + // away the modifications as we will be returning an error to the + // user. + // + + for (i = 0; i < REPINNED_BCBS_ARRAY_SIZE; i += 1) { + + if (Repinned->Bcb[i] != NULL) { + + IO_STATUS_BLOCK Iosb; + + if (WriteThroughToDisk && + FlagOn(IrpContext->Vcb->VcbState, VCB_STATE_FLAG_DEFERRED_FLUSH)) { + + FileObject = CcGetFileObjectFromBcb( Repinned->Bcb[i] ); + } + + CcUnpinRepinnedBcb( Repinned->Bcb[i], + WriteThroughToDisk, + &Iosb ); + + if (!NT_SUCCESS(Iosb.Status)) { + + if (RaiseIosb.Status == STATUS_SUCCESS) { + + RaiseIosb = Iosb; + } + + // + // If this was a writethrough device, purge the cache, + // except for Irp major codes that either don't handle + // the error paths correctly or are simple victims like + // cleanup.c. + // + + if (FileObject && + (IrpContext->MajorFunction != IRP_MJ_CLEANUP) && + (IrpContext->MajorFunction != IRP_MJ_FLUSH_BUFFERS) && + (IrpContext->MajorFunction != IRP_MJ_SET_INFORMATION) + + && + + // + // WinSE bug #307418 "Occasional data corruption when + // standby/resume while copying files to removable FAT + // formatted media". + // Buffer unpinning for new file creation operation can + // be interrupted by system syspend. As a result some BCBs + // will be successfully written to the disk while others will + // be kicked back with STATUS_VERIFY_REQUIRED. Since there is + // is still a chance for the failed BCBs to reach the disk + // after the volume verification we'll not purge them. + // Instead FatCommonCreate() will unroll the file creation + // changes for these pages. + // + + !(IrpContext->MajorFunction == IRP_MJ_CREATE && + Iosb.Status == STATUS_VERIFY_REQUIRED && + FcbOrDcb != NULL && + NodeType( FcbOrDcb ) == FAT_NTC_FCB)) { + + // + // The call to CcPurgeCacheSection() below will + // purge the entire file from memory. It will also + // block until all the file's BCB's are pinned. + // + // We end up in a deadlock situation of there + // are any other pinned BCB's in this IRP context + // so the first thing we do is search the list + // for BCB's pinned in the same file and unpin + // them. + // + // We are probably not going to lose data because + // it's safe to assume that all flushes will + // fail after the first one fails. + // + + ULONG j; + ULONG k = i + 1; + PREPINNED_BCBS RepinnedToPurge = Repinned; + + while( RepinnedToPurge != NULL ) { + + for (j = k; j < REPINNED_BCBS_ARRAY_SIZE; j++) { + + if (RepinnedToPurge->Bcb[j] != NULL) { + + if (CcGetFileObjectFromBcb( RepinnedToPurge->Bcb[j] ) == FileObject) { + + CcUnpinRepinnedBcb( RepinnedToPurge->Bcb[j], + FALSE, + &Iosb ); + + RepinnedToPurge->Bcb[j] = NULL; + } + } + } + + RepinnedToPurge = RepinnedToPurge->Next; + k = 0; + } + + CcPurgeCacheSection( FileObject->SectionObjectPointer, + NULL, + 0, + FALSE ); + + // + // Force a verify operation here since who knows + // what state things are in. + // + + ForceVerify = TRUE; + } + } + + Repinned->Bcb[i] = NULL; + + } + } + + // + // Now find the next repinned record in the list, and possibly + // delete the one we've just processed. + // + + if (Repinned != &IrpContext->Repinned) { + + PREPINNED_BCBS Saved; + + Saved = Repinned->Next; + ExFreePool( Repinned ); + Repinned = Saved; + + } else { + + Repinned = Repinned->Next; + IrpContext->Repinned.Next = NULL; + } + } + + // + // Now if we weren't completely successful in the our unpin + // then raise the iosb we got + // + + if (!NT_SUCCESS(RaiseIosb.Status)) { + + if (ForceVerify && FileObject) { + + SetFlag(FileObject->DeviceObject->Flags, DO_VERIFY_VOLUME); + + IoSetHardErrorOrVerifyDevice( IrpContext->OriginatingIrp, + FileObject->DeviceObject ); + } + + if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_RAISE )) { + + IrpContext->OriginatingIrp->IoStatus = RaiseIosb; + FatNormalizeAndRaiseStatus( IrpContext, RaiseIosb.Status ); + } + } + + DebugTrace(-1, Dbg, "FatUnpinRepinnedBcbs -> VOID\n", 0 ); + + return; +} + + +FINISHED +FatZeroData ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PFILE_OBJECT FileObject, + IN ULONG StartingZero, + IN ULONG ByteCount + ) + +/*++ + + **** Temporary function - Remove when CcZeroData is capable of handling + non sector aligned requests. + +--*/ +{ + LARGE_INTEGER ZeroStart = {0,0}; + LARGE_INTEGER BeyondZeroEnd = {0,0}; + + ULONG SectorSize; + + BOOLEAN Finished; + + PAGED_CODE(); + + SectorSize = (ULONG)Vcb->Bpb.BytesPerSector; + + ZeroStart.LowPart = (StartingZero + (SectorSize - 1)) & ~(SectorSize - 1); + + // + // Detect overflow if we were asked to zero in the last sector of the file, + // which must be "zeroed" already (or we're in trouble). + // + + if (StartingZero != 0 && ZeroStart.LowPart == 0) { + + return TRUE; + } + + // + // Note that BeyondZeroEnd can take the value 4gb. + // + + BeyondZeroEnd.QuadPart = ((ULONGLONG) StartingZero + ByteCount + (SectorSize - 1)) + & (~((LONGLONG) SectorSize - 1)); + + // + // If we were called to just zero part of a sector we are in trouble. + // + + if ( ZeroStart.QuadPart == BeyondZeroEnd.QuadPart ) { + + return TRUE; + } + + Finished = CcZeroData( FileObject, + &ZeroStart, + &BeyondZeroEnd, + BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) ); + + return Finished; +} + + +NTSTATUS +FatCompleteMdl ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the function of completing Mdl read and write + requests. It should be called only from FatFsdRead and FatFsdWrite. + +Arguments: + + Irp - Supplies the originating Irp. + +Return Value: + + NTSTATUS - Will always be STATUS_PENDING or STATUS_SUCCESS. + +--*/ + +{ + PFILE_OBJECT FileObject; + PIO_STACK_LOCATION IrpSp; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatCompleteMdl\n", 0 ); + DebugTrace( 0, Dbg, "IrpContext = %p\n", IrpContext ); + DebugTrace( 0, Dbg, "Irp = %p\n", Irp ); + + // + // Do completion processing. + // + + FileObject = IoGetCurrentIrpStackLocation( Irp )->FileObject; + + switch( IrpContext->MajorFunction ) { + + case IRP_MJ_READ: + + CcMdlReadComplete( FileObject, Irp->MdlAddress ); + break; + + case IRP_MJ_WRITE: + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + NT_ASSERT( FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT )); + + CcMdlWriteComplete( FileObject, &IrpSp->Parameters.Write.ByteOffset, Irp->MdlAddress ); + + Irp->IoStatus.Status = STATUS_SUCCESS; + + break; + + default: + + DebugTrace( DEBUG_TRACE_ERROR, 0, "Illegal Mdl Complete.\n", 0); +#pragma prefast( suppress: 28159, "we're very broken if we get here" ) + FatBugCheck( IrpContext->MajorFunction, 0, 0 ); + } + + // + // Mdl is now deallocated. + // + + Irp->MdlAddress = NULL; + + // + // Complete the request and exit right away. + // + + FatCompleteRequest( IrpContext, Irp, STATUS_SUCCESS ); + + DebugTrace(-1, Dbg, "FatCompleteMdl -> STATUS_SUCCESS\n", 0 ); + + return STATUS_SUCCESS; +} + +VOID +FatSyncUninitializeCacheMap ( + IN PIRP_CONTEXT IrpContext, + IN PFILE_OBJECT FileObject + ) + +/*++ + +Routine Description: + + The routine performs a CcUnitializeCacheMap to LargeZero synchronously. That + is it waits on the Cc event. This call is useful when we want to be certain + when a close will actually some in. + +Return Value: + + None. + +--*/ + +{ + CACHE_UNINITIALIZE_EVENT UninitializeCompleteEvent; + NTSTATUS WaitStatus; + + UNREFERENCED_PARAMETER( IrpContext ); + + PAGED_CODE(); + + KeInitializeEvent( &UninitializeCompleteEvent.Event, + SynchronizationEvent, + FALSE); + + CcUninitializeCacheMap( FileObject, + &FatLargeZero, + &UninitializeCompleteEvent ); + + // + // Now wait for the cache manager to finish purging the file. + // This will garentee that Mm gets the purge before we + // delete the Vcb. + // + +#pragma prefast( suppress: 28931, "we use WaitStatus in the debug assert, in fre builds prefast complains it's unused" ) + WaitStatus = KeWaitForSingleObject( &UninitializeCompleteEvent.Event, + Executive, + KernelMode, + FALSE, + NULL); + + NT_ASSERT(WaitStatus == STATUS_SUCCESS); +} + +VOID +FatPinMappedData ( + IN PIRP_CONTEXT IrpContext, + IN PDCB Dcb, + IN VBO StartingVbo, + IN ULONG ByteCount, + OUT PBCB *Bcb + ) + +/*++ + +Routine Description: + + This routine pins data that was previously mapped before setting it dirty. + +Arguments: + + Dcb - Pointer to the DCB for the directory + + StartingVbo - The virtual offset of the first desired byte + + ByteCount - Number of bytes desired + + Bcb - Returns a pointer to the BCB which is valid until unpinned + +--*/ + +{ + LARGE_INTEGER Vbo; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatPinMappedData\n", 0); + DebugTrace( 0, Dbg, "Dcb = %p\n", Dcb); + DebugTrace( 0, Dbg, "StartingVbo = %08lx\n", StartingVbo); + DebugTrace( 0, Dbg, "ByteCount = %08lx\n", ByteCount); + + // + // Call the Cache manager to perform the operation. + // + + Vbo.QuadPart = StartingVbo; + + if (!CcPinMappedData( Dcb->Specific.Dcb.DirectoryFile, + &Vbo, + ByteCount, + BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT), + Bcb )) { + + // + // Could not pin the data without waiting (cache miss). + // + + FatRaiseStatus( IrpContext, STATUS_CANT_WAIT ); + } + + DebugTrace(-1, Dbg, "FatReadDirectoryFile -> VOID, *BCB = %p\n", *Bcb); + + return; +} + +#if (NTDDI_VERSION >= NTDDI_WIN8) + +NTSTATUS +FatPrefetchPages ( + IN PIRP_CONTEXT IrpContext, + IN PFILE_OBJECT FileObject, + IN ULONG StartingPage, + IN ULONG PageCount + ) +{ + IO_PRIORITY_INFO PriorityInformation = {0}; + MM_PREFETCH_FLAGS PrefetchFlags; + ULONG PageNo; + NTSTATUS Status; + + PREAD_LIST ReadList = NULL; + + UNREFERENCED_PARAMETER( IrpContext ); + + PAGED_CODE(); + + // + // Succeed zero page prefetch requests. + // + + if (PageCount == 0) { + + return STATUS_SUCCESS; + } + + // + // Mm's prefetch API's "only" support fetching a ULONG worth of pages. + // Make sure we don't overflow. + // + + ASSERT( PageCount < (PFN_NUMBER)MAXULONG ); + + IoInitializePriorityInfo( &PriorityInformation ); + + Status = IoRetrievePriorityInfo( IrpContext->OriginatingIrp, + FileObject, + IrpContext->OriginatingIrp->Tail.Overlay.Thread, + &PriorityInformation ); + + if (!NT_SUCCESS( Status)) { + + goto Cleanup; + } + + ReadList = ExAllocatePoolWithTag( PagedPool, + FIELD_OFFSET( READ_LIST, List ) + PageCount * sizeof( FILE_SEGMENT_ELEMENT ), + ' taF' ); + + if (ReadList == NULL) { + + Status = STATUS_INSUFFICIENT_RESOURCES; + goto Cleanup; + } + + // + // Call Mm to prefetch data. + // + + ReadList->FileObject = FileObject; + ReadList->IsImage = FALSE; + ReadList->NumberOfEntries = PageCount; + + PrefetchFlags.AllFlags = 0; + PrefetchFlags.Flags.Priority = PriorityInformation.PagePriority; + PrefetchFlags.Flags.RepurposePriority = SYSTEM_PAGE_PRIORITY_LEVELS - 1; + PrefetchFlags.Flags.PriorityProtection = 1; + ReadList->List[0].Alignment = StartingPage * PAGE_SIZE; + ReadList->List[0].Alignment |= PrefetchFlags.AllFlags; + + for (PageNo = 1; PageNo < PageCount; PageNo++) { + + ReadList->List[PageNo].Alignment = ReadList->List[PageNo-1].Alignment + PAGE_SIZE; + } + + Status = MmPrefetchPages( 1, &ReadList ); + +Cleanup: + + if (ReadList != NULL) { + + ExFreePoolWithTag( ReadList, ' taF' ); + } + + return Status; +} +#endif + diff --git a/filesys/fastfat/cleanup.c b/filesys/fastfat/cleanup.c new file mode 100644 index 000000000..b42a952be --- /dev/null +++ b/filesys/fastfat/cleanup.c @@ -0,0 +1,1176 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + Cleanup.c + +Abstract: + + This module implements the File Cleanup routine for Fat called by the + dispatch driver. + + +--*/ + +#include "FatProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (FAT_BUG_CHECK_CLEANUP) + +// +// The local debug trace level +// + +#define Dbg (DEBUG_TRACE_CLEANUP) + +// +// The following little routine exists solely because it need a spin lock. +// + +VOID +FatAutoUnlock ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, FatCommonCleanup) +#pragma alloc_text(PAGE, FatFsdCleanup) +#endif + + +_Function_class_(IRP_MJ_CLEANUP) +_Function_class_(DRIVER_DISPATCH) +NTSTATUS +FatFsdCleanup ( + _In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + _Inout_ PIRP Irp + ) + +/*++ + +Routine Description: + + This routine implements the FSD part of closing down a handle to a + file object. + +Arguments: + + VolumeDeviceObject - Supplies the volume device object where the + file being Cleanup exists + + Irp - Supplies the Irp being processed + +Return Value: + + NTSTATUS - The FSD status for the IRP + +--*/ + +{ + NTSTATUS Status; + PIRP_CONTEXT IrpContext = NULL; + + BOOLEAN TopLevel; + + 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 ( FatDeviceIsFatFsdo( VolumeDeviceObject)) { + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = FILE_OPENED; + + IoCompleteRequest( Irp, IO_DISK_INCREMENT ); + + return STATUS_SUCCESS; + } + + DebugTrace(+1, Dbg, "FatFsdCleanup\n", 0); + + // + // Call the common Cleanup routine, with blocking allowed. + // + + FsRtlEnterFileSystem(); + + TopLevel = FatIsIrpTopLevel( Irp ); + + try { + + IrpContext = FatCreateIrpContext( Irp, TRUE ); + + Status = FatCommonCleanup( IrpContext, Irp ); + + } except(FatExceptionFilter( IrpContext, GetExceptionInformation() )) { + + // + // We had some trouble trying to perform the requested + // operation, so we'll abort the I/O request with + // the error status that we get back from the + // execption code + // + + Status = FatProcessException( IrpContext, Irp, GetExceptionCode() ); + } + + if (TopLevel) { IoSetTopLevelIrp( NULL ); } + + FsRtlExitFileSystem(); + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "FatFsdCleanup -> %08lx\n", Status); + + UNREFERENCED_PARAMETER( VolumeDeviceObject ); + + return Status; +} + + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatCommonCleanup ( + IN PIRP_CONTEXT IrpContext, + IN 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. + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + + PIO_STACK_LOCATION IrpSp; + + PFILE_OBJECT FileObject; + + TYPE_OF_OPEN TypeOfOpen; + PVCB Vcb; + PFCB Fcb; + PCCB Ccb; + + BOOLEAN SendUnlockNotification = FALSE; + + PSHARE_ACCESS ShareAccess; + + PLARGE_INTEGER TruncateSize = NULL; + LARGE_INTEGER LocalTruncateSize; + + BOOLEAN AcquiredVcb = FALSE; + BOOLEAN AcquiredFcb = FALSE; + + BOOLEAN ProcessingDeleteOnClose = FALSE; + + PAGED_CODE(); + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "FatCommonCleanup\n", 0); + DebugTrace( 0, Dbg, "Irp = %p\n", Irp); + DebugTrace( 0, Dbg, "->FileObject = %p\n", IrpSp->FileObject); + + // + // Extract and decode the file object + // + + FileObject = IrpSp->FileObject; + TypeOfOpen = FatDecodeFileObject( FileObject, &Vcb, &Fcb, &Ccb ); + + // + // Special case the unopened file object. This will occur only when + // we are initializing Vcb and IoCreateStreamFileObject is being + // called. + // + + if (TypeOfOpen == UnopenedFileObject) { + + DebugTrace(0, Dbg, "Unopened File Object\n", 0); + + FatCompleteRequest( IrpContext, Irp, STATUS_SUCCESS ); + + DebugTrace(-1, Dbg, "FatCommonCleanup -> STATUS_SUCCESS\n", 0); + return STATUS_SUCCESS; + } + + // + // If this is not our first time through (for whatever reason) + // only see if we have to flush the file. + // + + if (FlagOn( FileObject->Flags, FO_CLEANUP_COMPLETE )) { + + if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_DEFERRED_FLUSH) && + FlagOn(FileObject->Flags, FO_FILE_MODIFIED) && + !FlagOn(Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED) && + (TypeOfOpen == UserFileOpen)) { + + // + // Flush the file. + // + + Status = FatFlushFile( IrpContext, Fcb, Flush ); + + if (!NT_SUCCESS(Status)) { + + FatNormalizeAndRaiseStatus( IrpContext, Status ); + } + } + + FatCompleteRequest( IrpContext, Irp, STATUS_SUCCESS ); + + DebugTrace(-1, Dbg, "FatCommonCleanup -> STATUS_SUCCESS\n", 0); + return STATUS_SUCCESS; + } + + // + // If we call change the allocation or call CcUninitialize, + // we have to take the Fcb exclusive + // + + if ((TypeOfOpen == UserFileOpen) || (TypeOfOpen == UserDirectoryOpen)) { + + NT_ASSERT( Fcb != NULL ); + + (VOID)FatAcquireExclusiveFcb( IrpContext, Fcb ); + + AcquiredFcb = TRUE; + + // + // Do a check here if this was a DELETE_ON_CLOSE FileObject, and + // set the Fcb flag appropriately. + // + + if (FlagOn(Ccb->Flags, CCB_FLAG_DELETE_ON_CLOSE)) { + + NT_ASSERT( NodeType(Fcb) != FAT_NTC_ROOT_DCB ); + + // + // Transfer the delete-on-close state to the FCB. We do this rather + // than leave the CCB_FLAG_DELETE_ON_CLOSE flag set so that if we + // end up breaking an oplock and come in again we won't try to break + // the oplock again (and again, and again...). + // + + SetFlag( Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE ); + ClearFlag( Ccb->Flags, CCB_FLAG_DELETE_ON_CLOSE ); + + ProcessingDeleteOnClose = TRUE; + + // + // Report this to the dir notify package for a directory. + // + + if (TypeOfOpen == UserDirectoryOpen) { + +#pragma prefast( suppress:6309, "FullDirectoryName may be NULL if NotifyIrp is also NULL. this indicates the object is being deleted." ) + FsRtlNotifyFullChangeDirectory( Vcb->NotifySync, + &Vcb->DirNotifyList, + FileObject->FsContext, + NULL, + FALSE, + FALSE, + 0, + NULL, + NULL, + NULL ); + } + } + + // + // Now if we may delete the file, drop the Fcb and acquire the Vcb + // first. Note that while we own the Fcb exclusive, a file cannot + // become DELETE_ON_CLOSE and cannot be opened via CommonCreate. + // + + if ((Fcb->UncleanCount == 1) && + FlagOn(Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE) && + (Fcb->FcbCondition != FcbBad) && + !FlagOn(Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED)) { + + FatReleaseFcb( IrpContext, Fcb ); + AcquiredFcb = FALSE; + + (VOID)FatAcquireExclusiveVcb( IrpContext, Vcb ); + AcquiredVcb = TRUE; + + (VOID)FatAcquireExclusiveFcb( IrpContext, Fcb ); + AcquiredFcb = TRUE; + } + } + + // + // For user DASD cleanups, grab the Vcb exclusive. + // + + if (TypeOfOpen == UserVolumeOpen) { + + (VOID)FatAcquireExclusiveVcb( IrpContext, Vcb ); + AcquiredVcb = TRUE; + } + + // + // Complete any Notify Irps on this file handle. + // + + if (TypeOfOpen == UserDirectoryOpen) { + + FsRtlNotifyCleanup( Vcb->NotifySync, + &Vcb->DirNotifyList, + Ccb ); + } + + // + // Determine the Fcb state, Good or Bad, for better or for worse. + // + // We can only read the volume file if VcbCondition is good. + // + + if ( Fcb != NULL) { + + // + // Stop any raises from FatVerifyFcb, unless it is REAL bad. + // + + try { + + try { + + FatVerifyFcb( IrpContext, Fcb ); + + } except( FsRtlIsNtstatusExpected(GetExceptionCode()) ? + EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) { + + FatResetExceptionState( IrpContext ); + } + + } finally { + + if ( AbnormalTermination() ) { + + // + // We will be raising out of here. + // + + if (AcquiredFcb) { FatReleaseFcb( IrpContext, Fcb ); } + if (AcquiredVcb) { FatReleaseVcb( IrpContext, Vcb ); } + } + } + } + + try { + +#if (NTDDI_VERSION >= NTDDI_WIN8) + + // + // See if this is a delete-on-close handle on a file or empty directory. + // If so we may need to break an oplock. We do this in the try block + // so that resources will be properly released. + // + + if (ProcessingDeleteOnClose && + FatIsFileOplockable( Fcb ) && + ((NodeType( Fcb ) != FAT_NTC_DCB) || + FatIsDirectoryEmpty( IrpContext, Fcb ))) { + + Status = FsRtlCheckOplockEx( FatGetFcbOplock(Fcb), + Irp, + OPLOCK_FLAG_CLOSING_DELETE_ON_CLOSE, + IrpContext, + FatOplockComplete, + FatPrePostIrp ); + + if (Status != STATUS_SUCCESS) { + + if (Status == STATUS_PENDING) { + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_CLEANUP_BREAKING_OPLOCK ); + try_return( Status ); + + } else { + + FatNormalizeAndRaiseStatus( IrpContext, Status ); + } + } + } +#endif + + // + // Case on the type of open that we are trying to cleanup. + // For all cases we need to set the share access to point to the + // share access variable (if there is one). After the switch + // we then remove the share access and complete the Irp. + // In the case of UserFileOpen we actually have a lot more work + // to do and we have the FsdLockControl complete the Irp for us. + // + + switch (TypeOfOpen) { + + case DirectoryFile: + case VirtualVolumeFile: + + DebugTrace(0, Dbg, "Cleanup VirtualVolumeFile/DirectoryFile\n", 0); + + ShareAccess = NULL; + + break; + + + case UserVolumeOpen: + + DebugTrace(0, Dbg, "Cleanup UserVolumeOpen\n", 0); + + if (FlagOn( Ccb->Flags, CCB_FLAG_COMPLETE_DISMOUNT )) { + + FatCheckForDismount( IrpContext, Vcb, TRUE ); + + // + // If this handle had write access, and 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 (FileObject->WriteAccess && + FlagOn(FileObject->Flags, FO_FILE_MODIFIED)) { + + (VOID)FatHijackIrpAndFlushDevice( IrpContext, + Irp, + Vcb->TargetDeviceObject ); + + SetFlag(Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME); + } + + // + // If the volume is locked by this file object then release + // the volume and send notification. + // + + if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_LOCKED) && + (Vcb->FileObjectWithVcbLocked == FileObject)) { + + FatAutoUnlock( IrpContext, Vcb ); + SendUnlockNotification = TRUE; + } + + ShareAccess = &Vcb->ShareAccess; + + break; + + case EaFile: + + DebugTrace(0, Dbg, "Cleanup EaFileObject\n", 0); + + ShareAccess = NULL; + + break; + + case UserDirectoryOpen: + + DebugTrace(0, Dbg, "Cleanup UserDirectoryOpen\n", 0); + + ShareAccess = &Fcb->ShareAccess; + + // + // Determine here if we should try do delayed close. + // + + if ((Fcb->UncleanCount == 1) && + (Fcb->OpenCount == 1) && + (Fcb->Specific.Dcb.DirectoryFileOpenCount == 0) && + !FlagOn(Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE) && + Fcb->FcbCondition == FcbGood) { + + // + // Delay our close. + // + + SetFlag( Fcb->FcbState, FCB_STATE_DELAY_CLOSE ); + } + + // + // Clear the deny defrag bit, if the handle we're cleaning up was the one that set it. + // + + if( FlagOn(Fcb->FcbState, FCB_STATE_DENY_DEFRAG) && FlagOn(Ccb->Flags, CCB_FLAG_DENY_DEFRAG) ) { + + ClearFlag(Ccb->Flags, CCB_FLAG_DENY_DEFRAG); + ClearFlag(Fcb->FcbState, FCB_STATE_DENY_DEFRAG ); + } + + if ((VcbGood == Vcb->VcbCondition) && + !FlagOn(Vcb->VcbState, VCB_STATE_FLAG_SHUTDOWN)) { + + FatUpdateDirentFromFcb( IrpContext, FileObject, Fcb, Ccb ); + + // + // If the directory has a unclean count of 1 then we know + // that this is the last handle for the file object. If + // we are supposed to delete it, do so. + // + + if ((Fcb->UncleanCount == 1) && + (NodeType(Fcb) == FAT_NTC_DCB) && + (FlagOn(Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE)) && + (Fcb->FcbCondition == FcbGood) && + !FlagOn(Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED)) { + + if (!FatIsDirectoryEmpty(IrpContext, Fcb)) { + + // + // If there are files in the directory at this point, + // forget that we were trying to delete it. + // + + ClearFlag( Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE ); + + } else { + +#if (NTDDI_VERSION >= NTDDI_WIN8) + NTSTATUS BreakStatus; +#endif + + // + // Even if something goes wrong, we cannot turn back! + // + + try { + + DELETE_CONTEXT DeleteContext; + + + // + // Before truncating file allocation remember this + // info for FatDeleteDirent. + // + + DeleteContext.FileSize = Fcb->Header.FileSize.LowPart; + DeleteContext.FirstClusterOfFile = Fcb->FirstClusterOfFile; + + // + // Synchronize here with paging IO + // + + (VOID)ExAcquireResourceExclusiveLite( Fcb->Header.PagingIoResource, + TRUE ); + + Fcb->Header.FileSize.LowPart = 0; + + ExReleaseResourceLite( Fcb->Header.PagingIoResource ); + + // + // Truncate the file allocation down to zero + // + + DebugTrace(0, Dbg, "Delete File allocation\n", 0); + + FatTruncateFileAllocation( IrpContext, Fcb, 0, TRUE ); + + if (Fcb->Header.AllocationSize.LowPart == 0) { + + // + // Tunnel and remove the dirent for the directory + // + + DebugTrace(0, Dbg, "Delete the directory dirent\n", 0); + + FatTunnelFcbOrDcb( Fcb, NULL ); + + FatDeleteDirent( IrpContext, Fcb, &DeleteContext, TRUE ); + + // + // Report that we have removed an entry. + // + + FatNotifyReportChange( IrpContext, + Vcb, + Fcb, + FILE_NOTIFY_CHANGE_DIR_NAME, + FILE_ACTION_REMOVED ); + } + + } except( FsRtlIsNtstatusExpected(GetExceptionCode()) ? + EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) { + + FatResetExceptionState( IrpContext ); + } + + // + // Remove the entry from the name table. + // This will ensure that + // we will not collide with the Dcb if the user wants + // to recreate the same file over again before we + // get a close irp. + // + + FatRemoveNames( IrpContext, Fcb ); + +#if (NTDDI_VERSION >= NTDDI_WIN8) + // + // We've removed the names so break any parent directory oplock. + // Directory oplock breaks are always advisory, so we will never + // block/get STATUS_PENDING here. + // + + BreakStatus = FsRtlCheckOplockEx( FatGetFcbOplock(Fcb->ParentDcb), + Irp, + (OPLOCK_FLAG_PARENT_OBJECT | + OPLOCK_FLAG_REMOVING_FILE_OR_LINK), + NULL, + NULL, + NULL ); + + ASSERT( BreakStatus != STATUS_PENDING ); +#endif + } + } + } + + // + // Decrement the unclean count. + // + + NT_ASSERT( Fcb->UncleanCount != 0 ); + Fcb->UncleanCount -= 1; + + break; + + case UserFileOpen: + + DebugTrace(0, Dbg, "Cleanup UserFileOpen\n", 0); + + ShareAccess = &Fcb->ShareAccess; + + // + // Determine here if we should do a delayed close. + // + + if ((FileObject->SectionObjectPointer->DataSectionObject == NULL) && + (FileObject->SectionObjectPointer->ImageSectionObject == NULL) && + (Fcb->UncleanCount == 1) && + (Fcb->OpenCount == 1) && + !FlagOn(Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE) && + !FlagOn(Fcb->FcbState, FCB_STATE_PAGING_FILE) && + Fcb->FcbCondition == FcbGood) { + + // + // Delay our close. + // + + SetFlag( Fcb->FcbState, FCB_STATE_DELAY_CLOSE ); + } + + // + // Clear the deny defrag bit, if the handle we're cleaning up was the one that set it. + // + + if( FlagOn(Fcb->FcbState, FCB_STATE_DENY_DEFRAG) && FlagOn(Ccb->Flags, CCB_FLAG_DENY_DEFRAG) ) { + + ClearFlag(Ccb->Flags, CCB_FLAG_DENY_DEFRAG); + ClearFlag(Fcb->FcbState, FCB_STATE_DENY_DEFRAG ); + } + + // + // Unlock all outstanding file locks. + // + + (VOID) FsRtlFastUnlockAll( &Fcb->Specific.Fcb.FileLock, + FileObject, + IoGetRequestorProcess( Irp ), + NULL ); + + // + // We can proceed with on-disk updates only if the volume is mounted + // and we can still write to it if it hasn't been shutdown. Remember that + // we toss all sections in the failed-verify and dismount cases. + // + + if ((Vcb->VcbCondition == VcbGood) && + !FlagOn(Vcb->VcbState, VCB_STATE_FLAG_SHUTDOWN)) { + + if (Fcb->FcbCondition == FcbGood) { + + + FatUpdateDirentFromFcb( IrpContext, FileObject, Fcb, Ccb ); + + } + + // + // If the file has a unclean count of 1 then we know + // that this is the last handle for the file object. + // + + if ( (Fcb->UncleanCount == 1) && (Fcb->FcbCondition == FcbGood) ) { + + DELETE_CONTEXT DeleteContext; + + // + // Check if we should be deleting the file. The + // delete operation really deletes the file but + // keeps the Fcb around for close to do away with. + // + + if (FlagOn(Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE) && + !FlagOn(Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED)) { + + // + // Before truncating file allocation remember this + // info for FatDeleteDirent. + // + + DeleteContext.FileSize = Fcb->Header.FileSize.LowPart; + DeleteContext.FirstClusterOfFile = Fcb->FirstClusterOfFile; + + DebugTrace(0, Dbg, "Delete File allocation\n", 0); + + // + // Synchronize here with paging IO + // + + (VOID)ExAcquireResourceExclusiveLite( Fcb->Header.PagingIoResource, + TRUE ); + + Fcb->Header.FileSize.LowPart = 0; + Fcb->Header.ValidDataLength.LowPart = 0; + Fcb->ValidDataToDisk = 0; + + ExReleaseResourceLite( Fcb->Header.PagingIoResource ); + + try { + + FatSetFileSizeInDirent( IrpContext, Fcb, NULL ); + + } except( FsRtlIsNtstatusExpected(GetExceptionCode()) ? + EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) { + + FatResetExceptionState( IrpContext ); + } + + Fcb->FcbState |= FCB_STATE_TRUNCATE_ON_CLOSE; + + } else { + + // + // We must zero between ValidDataLength and FileSize + // + + if (!FlagOn(Fcb->FcbState, FCB_STATE_PAGING_FILE) && + (Fcb->Header.ValidDataLength.LowPart < Fcb->Header.FileSize.LowPart)) { + + ULONG ValidDataLength; + + ValidDataLength = Fcb->Header.ValidDataLength.LowPart; + + if (ValidDataLength < Fcb->ValidDataToDisk) { + ValidDataLength = Fcb->ValidDataToDisk; + } + + // + // Recheck, VDD can be >= FS + // + + if (ValidDataLength < Fcb->Header.FileSize.LowPart) { + + try { + + (VOID)FatZeroData( IrpContext, + Vcb, + FileObject, + ValidDataLength, + Fcb->Header.FileSize.LowPart - + ValidDataLength ); + + // + // Since we just zeroed this, we can now bump + // up VDL in the Fcb. + // + + Fcb->ValidDataToDisk = + Fcb->Header.ValidDataLength.LowPart = + Fcb->Header.FileSize.LowPart; + + // + // We inform Cc of the motion so that the cache map is updated. + // This prevents optimized zero-page faults in case the cache + // structures are re-used for another handle before they are torn + // down by our soon-to-occur uninitialize. If they were, a noncached + // producer could write into the region we just zeroed and Cc would + // be none the wiser, then our async cached reader comes in and takes + // the optimized path, and we get bad (zero) data. + // + // If this was memory mapped, we don't have to (can't) tell Cc, it'll + // figure it out when a cached handle is opened. + // + + if (CcIsFileCached( FileObject )) { + CcSetFileSizes( FileObject, (PCC_FILE_SIZES)&Fcb->Header.AllocationSize ); + } + + } except( FsRtlIsNtstatusExpected(GetExceptionCode()) ? + EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) { + + FatResetExceptionState( IrpContext ); + } + } + } + } + + // + // See if we are supposed to truncate the file on the last + // close. If we cannot wait we'll ship this off to the fsp + // + + try { + + if (FlagOn(Fcb->FcbState, FCB_STATE_TRUNCATE_ON_CLOSE)) { + + DebugTrace(0, Dbg, "truncate file allocation\n", 0); + + if (Vcb->VcbCondition == VcbGood) { + + FatTruncateFileAllocation( IrpContext, + Fcb, + Fcb->Header.FileSize.LowPart, + FlagOn(Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE) ); + } + + // + // We also have to get rid of the Cache Map because + // this is the only way we have of trashing the + // truncated pages. + // + + LocalTruncateSize = Fcb->Header.FileSize; + TruncateSize = &LocalTruncateSize; + + // + // Mark the Fcb as having now been truncated, just incase + // we have to reship this off to the fsp. + // + + Fcb->FcbState &= ~FCB_STATE_TRUNCATE_ON_CLOSE; + } + + // + // Now check again if we are to delete the file and if + // so then we remove the file from the disk. + // + + if (FlagOn(Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE) && + Fcb->Header.AllocationSize.LowPart == 0) { + + DebugTrace(0, Dbg, "Delete File\n", 0); + + // + // Now tunnel and delete the dirent + // + + FatTunnelFcbOrDcb( Fcb, Ccb ); + + FatDeleteDirent( IrpContext, Fcb, &DeleteContext, TRUE ); + + // + // Report that we have removed an entry. + // + + FatNotifyReportChange( IrpContext, + Vcb, + Fcb, + FILE_NOTIFY_CHANGE_FILE_NAME, + FILE_ACTION_REMOVED ); + } + + } except( FsRtlIsNtstatusExpected(GetExceptionCode()) ? + EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) { + + FatResetExceptionState( IrpContext ); + } + + if (FlagOn(Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE)) { + +#if (NTDDI_VERSION >= NTDDI_WIN8) + NTSTATUS BreakStatus; +#endif + // + // Remove the entry from the splay table. This will + // ensure that we will not collide with the Fcb if the + // user wants to recreate the same file over again + // before we get a close irp. + // + // Note that we remove the name even if we couldn't + // truncate the allocation and remove the dirent above. + // + + FatRemoveNames( IrpContext, Fcb ); + +#if (NTDDI_VERSION >= NTDDI_WIN8) + // + // We've removed the names so break any parent directory oplock. + // Directory oplock breaks are always advisory, so we will never + // block/get STATUS_PENDING here. + // + + BreakStatus = FsRtlCheckOplockEx( FatGetFcbOplock(Fcb->ParentDcb), + Irp, + (OPLOCK_FLAG_PARENT_OBJECT | + OPLOCK_FLAG_REMOVING_FILE_OR_LINK), + NULL, + NULL, + NULL ); + + ASSERT( BreakStatus != STATUS_PENDING ); +#endif + } + } + } + + // + // We've just finished everything associated with an unclean + // fcb so now decrement the unclean count before releasing + // the resource. + // + + NT_ASSERT( Fcb->UncleanCount != 0 ); + Fcb->UncleanCount -= 1; + if (!FlagOn( FileObject->Flags, FO_CACHE_SUPPORTED )) { + NT_ASSERT( Fcb->NonCachedUncleanCount != 0 ); + Fcb->NonCachedUncleanCount -= 1; + } + + // + // If this was the last cached open, and there are open + // non-cached handles, attempt a flush and purge operation + // to avoid cache coherency overhead from these non-cached + // handles later. We ignore any I/O errors from the flush. + // + + if (FlagOn( FileObject->Flags, FO_CACHE_SUPPORTED ) && + (Fcb->NonCachedUncleanCount != 0) && + (Fcb->NonCachedUncleanCount == Fcb->UncleanCount) && + (Fcb->NonPaged->SectionObjectPointers.DataSectionObject != NULL)) { + + CcFlushCache( &Fcb->NonPaged->SectionObjectPointers, NULL, 0, NULL ); + + // + // Grab and release PagingIo to serialize ourselves with the lazy writer. + // This will work to ensure that all IO has completed on the cached + // data and we will succesfully tear away the cache section. + // + + ExAcquireResourceExclusiveLite( Fcb->Header.PagingIoResource, TRUE); + ExReleaseResourceLite( Fcb->Header.PagingIoResource ); + + CcPurgeCacheSection( &Fcb->NonPaged->SectionObjectPointers, + NULL, + 0, + FALSE ); + } + + // + // If the file is invalid, hint to the cache that we should throw everything out. + // + + if ( Fcb->FcbCondition == FcbBad ) { + + TruncateSize = &FatLargeZero; + } + + // + // Cleanup the cache map + // + + CcUninitializeCacheMap( FileObject, TruncateSize, NULL ); + + break; + + default: + +#pragma prefast( suppress: 28159, "if the type of open is unknown then things are very bad." ) + FatBugCheck( TypeOfOpen, 0, 0 ); + } + + // + // 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. + // + + if (ShareAccess != NULL) { + + DebugTrace(0, Dbg, "Cleanup the Share access\n", 0); + IoRemoveShareAccess( FileObject, ShareAccess ); + } + + if ((TypeOfOpen == UserFileOpen) +#if (NTDDI_VERSION >= NTDDI_WIN8) + || + (TypeOfOpen == UserDirectoryOpen) +#endif + ) { + + // + // Coordinate the cleanup operation with the oplock state. + // Cleanup operations can always cleanup immediately. + // + + FsRtlCheckOplock( FatGetFcbOplock(Fcb), + Irp, + IrpContext, + NULL, + NULL ); + + Fcb->Header.IsFastIoPossible = FatIsFastIoPossible( Fcb ); + } + + // + // First set the FO_CLEANUP_COMPLETE flag. + // + + SetFlag( FileObject->Flags, FO_CLEANUP_COMPLETE ); + + Status = STATUS_SUCCESS; + + // + // Now unpin any repinned Bcbs. + // + + FatUnpinRepinnedBcbs( IrpContext ); + + // + // If this was deferred flush media, flush the volume. + // We used to do this in lieu of write through for all removable + // media. + // + + if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_DEFERRED_FLUSH) && + !FlagOn(Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED)) { + + // + // Flush the file. + // + + if ((TypeOfOpen == UserFileOpen) && + FlagOn(FileObject->Flags, FO_FILE_MODIFIED)) { + + Status = FatFlushFile( IrpContext, Fcb, Flush ); + } + + // + // If that worked ok, then see if we should flush the FAT as well. + // + + if (NT_SUCCESS(Status) && Fcb && !FatIsFat12( Vcb) && + FlagOn( Fcb->FcbState, FCB_STATE_FLUSH_FAT)) { + + Status = FatFlushFat( IrpContext, Vcb); + + // + // Also flush the parent directory. + // + + if (NT_SUCCESS(Status) && (Fcb->ParentDcb != NULL)) { + + Status = FatFlushFile( IrpContext, Fcb->ParentDcb, Flush ); + } + } + + if (!NT_SUCCESS(Status)) { + + FatNormalizeAndRaiseStatus( IrpContext, Status ); + } + } + +#if (NTDDI_VERSION >= NTDDI_WIN8) + + try_exit: NOTHING; + +#endif + + } finally { + + DebugUnwind( FatCommonCleanup ); + + if (AcquiredFcb) { FatReleaseFcb( IrpContext, Fcb ); } + if (AcquiredVcb) { FatReleaseVcb( IrpContext, Vcb ); } + + if (SendUnlockNotification) { + + FsRtlNotifyVolumeEvent( FileObject, FSRTL_VOLUME_UNLOCK ); + } + + // + // If this is a normal termination then complete the request + // + + if (!AbnormalTermination() && + (Status != STATUS_PENDING)) { + + FatCompleteRequest( IrpContext, Irp, Status ); + } + + DebugTrace(-1, Dbg, "FatCommonCleanup -> %08lx\n", Status); + } + + return Status; +} + +VOID +FatAutoUnlock ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb + ) +{ + KIRQL SavedIrql; + + // + // Unlock the volume. + // + + UNREFERENCED_PARAMETER( IrpContext ); + + IoAcquireVpbSpinLock( &SavedIrql ); + + ClearFlag( Vcb->Vpb->Flags, (VPB_LOCKED | VPB_DIRECT_WRITES_ALLOWED) ); + + Vcb->VcbState &= ~VCB_STATE_FLAG_LOCKED; + Vcb->FileObjectWithVcbLocked = NULL; + + IoReleaseVpbSpinLock( SavedIrql ); +} + + diff --git a/filesys/fastfat/close.c b/filesys/fastfat/close.c new file mode 100644 index 000000000..1712ad943 --- /dev/null +++ b/filesys/fastfat/close.c @@ -0,0 +1,1280 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + Close.c + +Abstract: + + This module implements the File Close routine for Fat called by the + dispatch driver. + + +--*/ + +#include "FatProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (FAT_BUG_CHECK_CLOSE) + +// +// The local debug trace level +// + +#define Dbg (DEBUG_TRACE_CLOSE) + +ULONG FatMaxDelayedCloseCount; + + +#define FatAcquireCloseMutex() { \ + NT_ASSERT(KeAreApcsDisabled()); \ + ExAcquireFastMutexUnsafe( &FatCloseQueueMutex ); \ +} + +#define FatReleaseCloseMutex() { \ + NT_ASSERT(KeAreApcsDisabled()); \ + ExReleaseFastMutexUnsafe( &FatCloseQueueMutex ); \ +} + +// +// Local procedure prototypes +// + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatQueueClose ( + IN PCLOSE_CONTEXT CloseContext, + IN BOOLEAN DelayClose + ); + +_Requires_lock_held_(_Global_critical_region_) +PCLOSE_CONTEXT +FatRemoveClose ( + PVCB Vcb OPTIONAL, + PVCB LastVcbHint OPTIONAL + ); + +IO_WORKITEM_ROUTINE FatCloseWorker; + +VOID +FatCloseWorker ( + _In_ PDEVICE_OBJECT DeviceObject, + _In_opt_ PVOID Context + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, FatFsdClose) +#pragma alloc_text(PAGE, FatFspClose) +#pragma alloc_text(PAGE, FatRemoveClose) +#pragma alloc_text(PAGE, FatCommonClose) +#pragma alloc_text(PAGE, FatCloseWorker) +#endif + + +_Function_class_(IRP_MJ_CLOSE) +_Function_class_(DRIVER_DISPATCH) +NTSTATUS +FatFsdClose ( + _In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + _Inout_ PIRP Irp + ) + +/*++ + +Routine Description: + + This routine implements the FSD part of Close. + +Arguments: + + VolumeDeviceObject - Supplies the volume device object where the + file exists + + Irp - Supplies the Irp being processed + +Return Value: + + NTSTATUS - The FSD status for the IRP + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + PIO_STACK_LOCATION IrpSp; + PFILE_OBJECT FileObject; + + PVCB Vcb; + PFCB Fcb; + PCCB Ccb; + TYPE_OF_OPEN TypeOfOpen; + + BOOLEAN TopLevel; + BOOLEAN VcbDeleted = FALSE; + + 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 (FatDeviceIsFatFsdo( VolumeDeviceObject)) { + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = FILE_OPENED; + + IoCompleteRequest( Irp, IO_DISK_INCREMENT ); + + return STATUS_SUCCESS; + } + + DebugTrace(+1, Dbg, "FatFsdClose\n", 0); + + // + // Call the common Close routine + // + + FsRtlEnterFileSystem(); + + TopLevel = FatIsIrpTopLevel( Irp ); + + // + // Get a pointer to the current stack location and the file object + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + FileObject = IrpSp->FileObject; + + // + // Decode the file object and set the read-only bit in the Ccb. + // + + TypeOfOpen = FatDecodeFileObject( FileObject, &Vcb, &Fcb, &Ccb ); + + if (Ccb && IsFileObjectReadOnly(FileObject)) { + + SetFlag( Ccb->Flags, CCB_FLAG_READ_ONLY ); + } + + try { + + PCLOSE_CONTEXT CloseContext = NULL; + + // + // If we are top level, WAIT can be TRUE, otherwise make it FALSE + // to avoid deadlocks, unless this is a top + // level request not originating from the system process. + // + + BOOLEAN Wait = TopLevel && (PsGetCurrentProcess() != FatData.OurProcess); + +#if (NTDDI_VERSION >= NTDDI_WIN8) + + // + // To catch the odd case where a close comes in without a preceding cleanup, + // call the oplock package to get rid of any oplock state. This can only + // be safely done in the FSD path. + // + + if ((Fcb != NULL) && + !FlagOn( FileObject->Flags, FO_CLEANUP_COMPLETE ) && + FatIsFileOplockable( Fcb )) { + + // + // This is equivalent to handling cleanup, and it always cleans up any + // oplock immediately. Also, we don't need any locking of the FCB here; + // the oplock's own lock will be sufficient for this purpose. + // + + FsRtlCheckOplockEx( FatGetFcbOplock(Fcb), + Irp, + 0, + NULL, + NULL, + NULL ); + } +#endif + + // + // Metadata streams have had close contexts preallocated. Pull one out now, while we're + // guaranteed the VCB exists. + // + + if ( (TypeOfOpen == VirtualVolumeFile) || (TypeOfOpen == DirectoryFile) || (TypeOfOpen == EaFile) + ) { + + CloseContext = FatAllocateCloseContext( Vcb ); + NT_ASSERT( CloseContext != NULL ); + CloseContext->Free = TRUE; + + } + + // + // Call the common Close routine if we are not delaying this close. + // + + if ((((TypeOfOpen == UserFileOpen) || + (TypeOfOpen == UserDirectoryOpen)) && + FlagOn(Fcb->FcbState, FCB_STATE_DELAY_CLOSE) && + !FatData.ShutdownStarted) || + (FatCommonClose( Vcb, Fcb, Ccb, TypeOfOpen, Wait, TopLevel, &VcbDeleted ) == STATUS_PENDING)) { + + // + // Ship it off to the delayed close queue if we tried to close, and got STATUS_PENDING, or + // if the user open told us to delay the close. + // + + // + // Metadata streams have had close contexts preallocated. If we have a user open, + // pull the close context out of the Ccb. + // + + if( CloseContext == NULL ) { + + // + // Free up any query template strings before using the close context fields, + // which overlap (union) + // + + FatDeallocateCcbStrings( Ccb ); + + CloseContext = &Ccb->CloseContext; + CloseContext->Free = FALSE; + + SetFlag( Ccb->Flags, CCB_FLAG_CLOSE_CONTEXT ); + } + + // + // If the status is pending, then let's get the information we + // need into the close context we already have bagged, complete + // the request, and post it. It is important we allocate nothing + // in the close path. + // + + CloseContext->Vcb = Vcb; + CloseContext->Fcb = Fcb; + CloseContext->TypeOfOpen = TypeOfOpen; + + // + // Send it off, either to an ExWorkerThread or to the async + // close list. + // + + FatQueueClose( CloseContext, + (BOOLEAN)(Fcb && FlagOn(Fcb->FcbState, FCB_STATE_DELAY_CLOSE))); + + } else { + + // + // The close proceeded synchronously, so for the metadata objects we + // can now drop the close context we preallocated. + // + + if ((TypeOfOpen == VirtualVolumeFile) || + (TypeOfOpen == DirectoryFile) || + (TypeOfOpen == EaFile) + ) { + + if (CloseContext != NULL) { + + ExFreePool( CloseContext ); + + } + } + } + + FatCompleteRequest( FatNull, Irp, Status ); + + } + except(FatExceptionFilter( NULL, GetExceptionInformation() )) { + + // + // We had some trouble trying to perform the requested + // operation, so we'll abort the I/O request with the + // error status that we get back from the execption code. + // + + Status = FatProcessException( NULL, Irp, GetExceptionCode() ); + } + + if (TopLevel) { IoSetTopLevelIrp( NULL ); } + + FsRtlExitFileSystem(); + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "FatFsdClose -> %08lx\n", Status); + + UNREFERENCED_PARAMETER( VolumeDeviceObject ); + + return Status; +} + +VOID +FatCloseWorker ( + _In_ PDEVICE_OBJECT DeviceObject, + _In_opt_ PVOID Context + ) +/*++ + +Routine Description: + + This routine is a shim between the IO worker package and FatFspClose. + +Arguments: + + DeviceObject - Registration device object, unused + Context - Context value, unused + +Return Value: + + None. + +--*/ +{ + PAGED_CODE(); + + UNREFERENCED_PARAMETER( DeviceObject ); + + FsRtlEnterFileSystem(); + + FatFspClose (Context); + + FsRtlExitFileSystem(); +} + + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatFspClose ( + IN PVCB Vcb OPTIONAL + ) + +/*++ + +Routine Description: + + This routine implements the FSP part of Close. + +Arguments: + + Vcb - If present, tells us to only close file objects opened on the + specified volume. + +Return Value: + + None. + +--*/ + +{ + PCLOSE_CONTEXT CloseContext; + PVCB CurrentVcb = NULL; + PVCB LastVcb = NULL; + BOOLEAN FreeContext = FALSE; + BOOLEAN TopLevel = FALSE; + + ULONG LoopsWithVcbHeld = 0; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatFspClose\n", 0); + + // + // Set the top level IRP for the true FSP operation. + // + + if (!ARGUMENT_PRESENT( Vcb )) { + + IoSetTopLevelIrp( (PIRP)FSRTL_FSP_TOP_LEVEL_IRP ); + TopLevel = TRUE; + } + + while ((CloseContext = FatRemoveClose(Vcb, LastVcb)) != NULL) { + + // + // If we are in the FSP (i.e. Vcb == NULL), then try to keep ahead of + // creates by doing several closes with one acquisition of the Vcb. + // + // Note that we cannot be holding the Vcb on entry to FatCommonClose + // if this is last close as we will try to acquire FatData, and + // worse the volume (and therefore the Vcb) may go away. + // + + if (!ARGUMENT_PRESENT(Vcb)) { + + if (!FatData.ShutdownStarted) { + + if (CloseContext->Vcb != CurrentVcb) { + + LoopsWithVcbHeld = 0; + + // + // Release a previously held Vcb, if any. + // + + if (CurrentVcb != NULL) { + + ExReleaseResourceLite( &CurrentVcb->Resource); + } + + // + // Get the new Vcb. + // + + CurrentVcb = CloseContext->Vcb; + (VOID)ExAcquireResourceExclusiveLite( &CurrentVcb->Resource, TRUE ); + + } else { + + // + // Share the resource occasionally if we seem to be finding a lot + // of closes for a single volume. + // + + if (++LoopsWithVcbHeld >= 20) { + + if (ExGetSharedWaiterCount( &CurrentVcb->Resource ) + + ExGetExclusiveWaiterCount( &CurrentVcb->Resource )) { + + ExReleaseResourceLite( &CurrentVcb->Resource); + (VOID)ExAcquireResourceExclusiveLite( &CurrentVcb->Resource, TRUE ); + } + + LoopsWithVcbHeld = 0; + } + } + + // + // Now check the Open count. We may be about to delete this volume! + // + // The test below must be <= 1 because there could still be outstanding + // stream references on this VCB that are not counted in the OpenFileCount. + // For example if there are no open files OpenFileCount could be zero and we would + // not release the resource here. The call to FatCommonClose() below may cause + // the VCB to be torn down and we will try to release memory we don't + // own later. + // + + if (CurrentVcb->OpenFileCount <= 1) { + ExReleaseResourceLite( &CurrentVcb->Resource); + CurrentVcb = NULL; + } + // + // If shutdown has started while processing our list, drop the + // current Vcb resource. + // + + } else if (CurrentVcb != NULL) { + + ExReleaseResourceLite( &CurrentVcb->Resource); + CurrentVcb = NULL; + } + } + + LastVcb = CurrentVcb; + + // + // Call the common Close routine. Protected in a try {} except {} + // + + try { + + // + // The close context either is in the CCB, automatically freed, + // or was from pool for a metadata fileobject, CCB is NULL, and + // we'll need to free it. + // + + FreeContext = CloseContext->Free; + + (VOID)FatCommonClose( CloseContext->Vcb, + CloseContext->Fcb, + (FreeContext ? NULL : + CONTAINING_RECORD( CloseContext, CCB, CloseContext)), + CloseContext->TypeOfOpen, + TRUE, + TopLevel, + NULL ); + + } except(FatExceptionFilter( NULL, GetExceptionInformation() )) { + + // + // Ignore anything we expect. + // + + NOTHING; + } + + // + // Drop the context if it came from pool. + // + + if (FreeContext) { + + ExFreePool( CloseContext ); + } + } + + // + // Release a previously held Vcb, if any. + // + + if (CurrentVcb != NULL) { + + ExReleaseResourceLite( &CurrentVcb->Resource); + } + + // + // Clean up the top level IRP hint if we owned it. + // + + if (!ARGUMENT_PRESENT( Vcb )) { + + IoSetTopLevelIrp( NULL ); + } + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "FatFspClose -> NULL\n", 0); +} + + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatQueueClose ( + IN PCLOSE_CONTEXT CloseContext, + IN BOOLEAN DelayClose + ) + +/*++ + +Routine Description: + + Enqueue a deferred close to one of the two delayed close queues. + +Arguments: + + CloseContext - a close context to enqueue for the delayed close thread. + + DelayClose - whether this should go on the delayed close queue (unreferenced + objects). + +Return Value: + + None. + +--*/ + +{ + BOOLEAN StartWorker = FALSE; + + FatAcquireCloseMutex(); + + if (DelayClose) { + + InsertTailList( &FatData.DelayedCloseList, + &CloseContext->GlobalLinks ); + InsertTailList( &CloseContext->Vcb->DelayedCloseList, + &CloseContext->VcbLinks ); + + FatData.DelayedCloseCount += 1; + + if ((FatData.DelayedCloseCount > FatMaxDelayedCloseCount) && + !FatData.AsyncCloseActive) { + + FatData.AsyncCloseActive = TRUE; + StartWorker = TRUE; + } + + } else { + + InsertTailList( &FatData.AsyncCloseList, + &CloseContext->GlobalLinks ); + InsertTailList( &CloseContext->Vcb->AsyncCloseList, + &CloseContext->VcbLinks ); + + FatData.AsyncCloseCount += 1; + + if (!FatData.AsyncCloseActive) { + + FatData.AsyncCloseActive = TRUE; + StartWorker = TRUE; + } + } + + FatReleaseCloseMutex(); + + if (StartWorker) { + + IoQueueWorkItem( FatData.FatCloseItem, FatCloseWorker, CriticalWorkQueue, NULL ); + } +} + + +_Requires_lock_held_(_Global_critical_region_) +PCLOSE_CONTEXT +FatRemoveClose ( + PVCB Vcb OPTIONAL, + PVCB LastVcbHint OPTIONAL + ) + +/*++ + +Routine Description: + + Dequeue a deferred close from one of the two delayed close queues. + +Arguments: + + Vcb - if specified, only returns close for this volume. + + LastVcbHint - if specified and other starvation avoidance is required by + the system condition, will attempt to return closes for this volume. + +Return Value: + + A close to perform. + +--*/ + +{ + PLIST_ENTRY Entry; + PCLOSE_CONTEXT CloseContext; + BOOLEAN WorkerThread; + + PAGED_CODE(); + + FatAcquireCloseMutex(); + + // + // Remember if this is the worker thread, so we can pull down the active + // flag should we run everything out. + // + + WorkerThread = (Vcb == NULL); + + // + // If the queues are above the limits by a significant amount, we have + // to try hard to pull them down. To do this, we will aggresively try + // to find closes for the last volume the caller looked at. This will + // make sure we fully utilize the acquisition of the volume, which can + // be a hugely expensive resource to get (create/close/cleanup use it + // exclusively). + // + // Only do this in the delayed close thread. We will know this is the + // case by seeing a NULL mandatory Vcb. + // + + if (Vcb == NULL && LastVcbHint != NULL) { + + // + // Flip over to aggressive at twice the legal limit, and flip it + // off at the legal limit. + // + + if (!FatData.HighAsync && FatData.AsyncCloseCount > FatMaxDelayedCloseCount*2) { + + FatData.HighAsync = TRUE; + + } else if (FatData.HighAsync && FatData.AsyncCloseCount < FatMaxDelayedCloseCount) { + + FatData.HighAsync = FALSE; + } + + if (!FatData.HighDelayed && FatData.DelayedCloseCount > FatMaxDelayedCloseCount*2) { + + FatData.HighDelayed = TRUE; + + } else if (FatData.HighDelayed && FatData.DelayedCloseCount < FatMaxDelayedCloseCount) { + + FatData.HighDelayed = FALSE; + } + + if (FatData.HighAsync || FatData.HighDelayed) { + + Vcb = LastVcbHint; + } + } + + // + // Do the case when we don't care about which Vcb the close is on. + // This is the case when we are in an ExWorkerThread and aren't + // under pressure. + // + + if (Vcb == NULL) { + + AnyClose: + + // + // First check the list of async closes. + // + + if (!IsListEmpty( &FatData.AsyncCloseList )) { + + Entry = RemoveHeadList( &FatData.AsyncCloseList ); + FatData.AsyncCloseCount -= 1; + + CloseContext = CONTAINING_RECORD( Entry, + CLOSE_CONTEXT, + GlobalLinks ); + + RemoveEntryList( &CloseContext->VcbLinks ); + + // + // Do any delayed closes over half the limit, unless shutdown has + // started (then kill them all). + // + + } else if (!IsListEmpty( &FatData.DelayedCloseList ) && + (FatData.DelayedCloseCount > FatMaxDelayedCloseCount/2 || + FatData.ShutdownStarted)) { + + Entry = RemoveHeadList( &FatData.DelayedCloseList ); + FatData.DelayedCloseCount -= 1; + + CloseContext = CONTAINING_RECORD( Entry, + CLOSE_CONTEXT, + GlobalLinks ); + + RemoveEntryList( &CloseContext->VcbLinks ); + + // + // There are no more closes to perform; show that we are done. + // + + } else { + + CloseContext = NULL; + + if (WorkerThread) { + + FatData.AsyncCloseActive = FALSE; + } + } + + // + // We're running down a specific volume. + // + + } else { + + + // + // First check the list of async closes. + // + + if (!IsListEmpty( &Vcb->AsyncCloseList )) { + + Entry = RemoveHeadList( &Vcb->AsyncCloseList ); + FatData.AsyncCloseCount -= 1; + + CloseContext = CONTAINING_RECORD( Entry, + CLOSE_CONTEXT, + VcbLinks ); + + RemoveEntryList( &CloseContext->GlobalLinks ); + + // + // Do any delayed closes. + // + + } else if (!IsListEmpty( &Vcb->DelayedCloseList )) { + + Entry = RemoveHeadList( &Vcb->DelayedCloseList ); + FatData.DelayedCloseCount -= 1; + + CloseContext = CONTAINING_RECORD( Entry, + CLOSE_CONTEXT, + VcbLinks ); + + RemoveEntryList( &CloseContext->GlobalLinks ); + + // + // If we were trying to run down the queues but didn't find anything for this + // volume, flip over to accept anything and try again. + // + + } else if (LastVcbHint) { + + goto AnyClose; + + // + // There are no more closes to perform; show that we are done. + // + + } else { + + CloseContext = NULL; + } + } + + FatReleaseCloseMutex(); + + return CloseContext; +} + + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatCommonClose ( + IN PVCB Vcb, + IN PFCB Fcb, + IN PCCB Ccb, + IN TYPE_OF_OPEN TypeOfOpen, + IN BOOLEAN Wait, + IN BOOLEAN TopLevel, + OUT PBOOLEAN VcbDeleted OPTIONAL + ) + +/*++ + +Routine Description: + + This is the common routine for closing a file/directory called by both + the fsd and fsp threads. + + Close is invoked whenever the last reference to a file object is deleted. + Cleanup is invoked when the last handle to a file object is closed, and + is called before close. + + The function of close is to completely tear down and remove the fcb/dcb/ccb + structures associated with the file object. + +Arguments: + + Fcb - Supplies the file to process. + + Wait - If this is TRUE we are allowed to block for the Vcb, if FALSE + then we must try to acquire the Vcb anyway. + + TopLevel - If this is TRUE this is a top level request. + + VcbDeleted - Returns whether the VCB was deleted by this call. + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + PDCB ParentDcb; + BOOLEAN RecursiveClose; + BOOLEAN LocalVcbDeleted; + IRP_CONTEXT IrpContext; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatCommonClose...\n", 0); + + // + // Initailize the callers variable, if needed. + // + + LocalVcbDeleted = FALSE; + + if (ARGUMENT_PRESENT( VcbDeleted )) { + + *VcbDeleted = LocalVcbDeleted; + } + + // + // Special case the unopened file object + // + + if (TypeOfOpen == UnopenedFileObject) { + + DebugTrace(0, Dbg, "Close unopened file object\n", 0); + + Status = STATUS_SUCCESS; + + DebugTrace(-1, Dbg, "FatCommonClose -> %08lx\n", Status); + return Status; + } + + // + // Set up our stack IrpContext. + // + + RtlZeroMemory( &IrpContext, sizeof(IRP_CONTEXT) ); + + IrpContext.NodeTypeCode = FAT_NTC_IRP_CONTEXT; + IrpContext.NodeByteSize = sizeof( IrpContext ); + IrpContext.MajorFunction = IRP_MJ_CLOSE; + IrpContext.Vcb = Vcb; + + if (Wait) { + + SetFlag( IrpContext.Flags, IRP_CONTEXT_FLAG_WAIT ); + } + + // + // Acquire exclusive access to the Vcb and enqueue the irp if we didn't + // get access. + // + +#pragma prefast( suppress: 28137, "prefast wants Wait to be a constant, but that's not possible for fastfat" ) + if (!ExAcquireResourceExclusiveLite( &Vcb->Resource, Wait )) { + + return STATUS_PENDING; + } + + // + // The following test makes sure that we don't blow away an Fcb if we + // are trying to do a Supersede/Overwrite open above us. This test + // does not apply for the EA file. + // + + if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_CREATE_IN_PROGRESS) && + Vcb->EaFcb != Fcb) { + + ExReleaseResourceLite( &Vcb->Resource ); + + return STATUS_PENDING; + } + + // + // Setting the following flag prevents recursive closes of directory file + // objects, which are handled in a special case loop. + // + + if ( FlagOn(Vcb->VcbState, VCB_STATE_FLAG_CLOSE_IN_PROGRESS) ) { + + RecursiveClose = TRUE; + + } else { + + + SetFlag(Vcb->VcbState, VCB_STATE_FLAG_CLOSE_IN_PROGRESS); + RecursiveClose = FALSE; + + // + // Since we are at the top of the close chain, we need to add + // a reference to the VCB. This will keep it from going away + // on us until we are ready to check for a dismount below. + // + + Vcb->OpenFileCount += 1; + } + + try { + + // + // Case on the type of open that we are trying to close. + // + + switch (TypeOfOpen) { + + case VirtualVolumeFile: + + DebugTrace(0, Dbg, "Close VirtualVolumeFile\n", 0); + + // + // Remove this internal, residual open from the count. + // + + InterlockedDecrement( (LONG*)&(Vcb->InternalOpenCount) ); + InterlockedDecrement( (LONG*)&(Vcb->ResidualOpenCount) ); + + try_return( Status = STATUS_SUCCESS ); + break; + + case UserVolumeOpen: + + DebugTrace(0, Dbg, "Close UserVolumeOpen\n", 0); + + Vcb->DirectAccessOpenCount -= 1; + Vcb->OpenFileCount -= 1; + if (FlagOn(Ccb->Flags, CCB_FLAG_READ_ONLY)) { Vcb->ReadOnlyCount -= 1; } + + FatDeleteCcb( &IrpContext, &Ccb ); + + try_return( Status = STATUS_SUCCESS ); + break; + + case EaFile: + + DebugTrace(0, Dbg, "Close EaFile\n", 0); + + // + // Remove this internal, residual open from the count. + // + + InterlockedDecrement( (LONG*)&(Vcb->InternalOpenCount) ); + InterlockedDecrement( (LONG*)&(Vcb->ResidualOpenCount) ); + + try_return( Status = STATUS_SUCCESS ); + break; + + case DirectoryFile: + + DebugTrace(0, Dbg, "Close DirectoryFile\n", 0); + + InterlockedDecrement( (LONG*)&Fcb->Specific.Dcb.DirectoryFileOpenCount ); + + // + // Remove this internal open from the count. + // + + InterlockedDecrement( (LONG*)&(Vcb->InternalOpenCount) ); + + // + // If this is the root directory, it is a residual open + // as well. + // + + if (NodeType( Fcb ) == FAT_NTC_ROOT_DCB) { + + InterlockedDecrement( (LONG*)&(Vcb->ResidualOpenCount) ); + } + + // + // If this is a recursive close, just return here. + // + + if ( RecursiveClose ) { + + try_return( Status = STATUS_SUCCESS ); + + } else { + + break; + } + + + case UserDirectoryOpen: + case UserFileOpen: + + DebugTrace(0, Dbg, "Close UserFileOpen/UserDirectoryOpen\n", 0); + + // + // Uninitialize the cache map if we no longer need to use it + // + + if ((NodeType(Fcb) == FAT_NTC_DCB) && + IsListEmpty(&Fcb->Specific.Dcb.ParentDcbQueue) && + (Fcb->OpenCount == 1) && + (Fcb->Specific.Dcb.DirectoryFile != NULL)) { + + PFILE_OBJECT DirectoryFileObject = Fcb->Specific.Dcb.DirectoryFile; + + DebugTrace(0, Dbg, "Uninitialize the stream file object\n", 0); + + CcUninitializeCacheMap( DirectoryFileObject, NULL, NULL ); + + // + // Dereference the directory file. This may cause a close + // Irp to be processed, so we need to do this before we destory + // the Fcb. + // + + Fcb->Specific.Dcb.DirectoryFile = NULL; + ObDereferenceObject( DirectoryFileObject ); + } + + Fcb->OpenCount -= 1; + Vcb->OpenFileCount -= 1; + if (FlagOn(Ccb->Flags, CCB_FLAG_READ_ONLY)) { Vcb->ReadOnlyCount -= 1; } + + FatDeleteCcb( &IrpContext, &Ccb ); + + break; + + default: + +#pragma prefast( suppress: 28159, "if the type of open is unknown, we seriously messed up." ) + FatBugCheck( TypeOfOpen, 0, 0 ); + } + + // + // At this point we've cleaned up any on-disk structure that needs + // to be done, and we can now update the in-memory structures. + // Now if this is an unreferenced FCB or if it is + // an unreferenced DCB (not the root) then we can remove + // the fcb and set our ParentDcb to non null. + // + + if (((NodeType(Fcb) == FAT_NTC_FCB) && + (Fcb->OpenCount == 0)) + + || + + ((NodeType(Fcb) == FAT_NTC_DCB) && + (IsListEmpty(&Fcb->Specific.Dcb.ParentDcbQueue)) && + (Fcb->OpenCount == 0) && + (Fcb->Specific.Dcb.DirectoryFileOpenCount == 0))) { + + ParentDcb = Fcb->ParentDcb; + + SetFlag( Vcb->VcbState, VCB_STATE_FLAG_DELETED_FCB ); + + FatDeleteFcb( &IrpContext, &Fcb ); + + // + // Uninitialize our parent's cache map if we no longer need + // to use it. + // + + while ((NodeType(ParentDcb) == FAT_NTC_DCB) && + IsListEmpty(&ParentDcb->Specific.Dcb.ParentDcbQueue) && + (ParentDcb->OpenCount == 0) && + (ParentDcb->Specific.Dcb.DirectoryFile != NULL)) { + + PFILE_OBJECT DirectoryFileObject; + + DirectoryFileObject = ParentDcb->Specific.Dcb.DirectoryFile; + + DebugTrace(0, Dbg, "Uninitialize our parent Stream Cache Map\n", 0); + + CcUninitializeCacheMap( DirectoryFileObject, NULL, NULL ); + + ParentDcb->Specific.Dcb.DirectoryFile = NULL; + + ObDereferenceObject( DirectoryFileObject ); + + // + // Now, if the ObDereferenceObject() caused the final close + // to come in, then blow away the Fcb and continue up, + // otherwise wait for Mm to to dereference its file objects + // and stop here.. + // + + if ( ParentDcb->Specific.Dcb.DirectoryFileOpenCount == 0) { + + PDCB CurrentDcb; + + CurrentDcb = ParentDcb; + ParentDcb = CurrentDcb->ParentDcb; + + SetFlag( Vcb->VcbState, VCB_STATE_FLAG_DELETED_FCB ); + + FatDeleteFcb( &IrpContext, &CurrentDcb ); + + } else { + + break; + } + } + } + + Status = STATUS_SUCCESS; + + try_exit: NOTHING; + } finally { + + DebugUnwind( FatCommonClose ); + + // + // We are done processing the close. If we are the top of the close + // chain, see if the VCB can go away. We have biased the open count by + // one, so we need to take that into account. + // + + if (!RecursiveClose) { + + // + // See if there is only one open left. If so, it is ours. We only want + // to check for a dismount if a dismount is not already in progress. + // We also only do this if the Vcb condition is not VcbGood and the + // caller can handle the VCB going away. This is determined by whether + // they passed in the VcbDeleted argument. This request also needs + // to be top level. + // + + if (Vcb->OpenFileCount == 1 && + Vcb->VcbCondition != VcbGood && + !FlagOn( Vcb->VcbState, VCB_STATE_FLAG_DISMOUNT_IN_PROGRESS ) && + ARGUMENT_PRESENT( VcbDeleted ) && + TopLevel) { + + // + // We need the global lock, which must be acquired before the + // VCB. Since we already have the VCB, we have to drop and + // reaquire here. Note that we always want to wait from this + // point on. Note that the VCB cannot go away, since we have + // biased the open file count. + // + + FatReleaseVcb( &IrpContext, + Vcb ); + + SetFlag( IrpContext.Flags, IRP_CONTEXT_FLAG_WAIT ); + +#pragma prefast( suppress: 28137, "prefast wants the wait parameter in this macro expansion to be a constant, unfortunately this is not possible" ) + FatAcquireExclusiveGlobal( &IrpContext ); + + FatAcquireExclusiveVcb( &IrpContext, + Vcb ); + + // + // We have our locks in the correct order. Remove our + // extra open and check for a dismount. Note that if + // something changed while we dropped the lock, it will + // not matter, since the dismount code does the correct + // checks to make sure the volume can really go away. + // + + Vcb->OpenFileCount -= 1; + + LocalVcbDeleted = FatCheckForDismount( &IrpContext, + Vcb, + FALSE ); + + FatReleaseGlobal( &IrpContext ); + + // + // Let the caller know what happened, if they want this information. + // + + if (ARGUMENT_PRESENT( VcbDeleted )) { + + *VcbDeleted = LocalVcbDeleted; + } + + } else { + + // + // The volume cannot go away now. Just remove our extra reference. + // + + Vcb->OpenFileCount -= 1; + } + + // + // If the VCB is still around, clear our recursion flag. + // + + if (!LocalVcbDeleted) { + + ClearFlag( Vcb->VcbState, VCB_STATE_FLAG_CLOSE_IN_PROGRESS ); + } + } + + // + // Only release the VCB if it did not go away. + // + + if (!LocalVcbDeleted) { + + FatReleaseVcb( &IrpContext, Vcb ); + } + + DebugTrace(-1, Dbg, "FatCommonClose -> %08lx\n", Status); + } + + return Status; +} + diff --git a/filesys/fastfat/create.c b/filesys/fastfat/create.c new file mode 100644 index 000000000..6de158234 --- /dev/null +++ b/filesys/fastfat/create.c @@ -0,0 +1,6727 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + Create.c + +Abstract: + + This module implements the File Create routine for Fat called by the + dispatch driver. + + +--*/ + +#include "FatProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (FAT_BUG_CHECK_CREATE) + +// +// The debug trace level +// + +#define Dbg (DEBUG_TRACE_CREATE) + + +// +// Macros for incrementing performance counters. +// + +#define CollectCreateHitStatistics(VCB) { \ + PFILE_SYSTEM_STATISTICS Stats = &(VCB)->Statistics[KeGetCurrentProcessorNumber() % FatData.NumberProcessors]; \ + Stats->Fat.CreateHits += 1; \ +} + +#define CollectCreateStatistics(VCB,STATUS) { \ + PFILE_SYSTEM_STATISTICS Stats = &(VCB)->Statistics[KeGetCurrentProcessorNumber() % FatData.NumberProcessors]; \ + if ((STATUS) == STATUS_SUCCESS) { \ + Stats->Fat.SuccessfulCreates += 1; \ + } else { \ + Stats->Fat.FailedCreates += 1; \ + } \ +} + +LUID FatSecurityPrivilege = { SE_SECURITY_PRIVILEGE, 0 }; + +// +// local procedure prototypes +// + +_Requires_lock_held_(_Global_critical_region_) +IO_STATUS_BLOCK +FatOpenVolume ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PFILE_OBJECT FileObject, + _Inout_ PVCB Vcb, + _In_ PACCESS_MASK DesiredAccess, + _In_ USHORT ShareAccess, + _In_ ULONG CreateDisposition + ); + +_Requires_lock_held_(_Global_critical_region_) +IO_STATUS_BLOCK +FatOpenRootDcb ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PFILE_OBJECT FileObject, + _Inout_ PVCB Vcb, + _In_ PACCESS_MASK DesiredAccess, + _In_ USHORT ShareAccess, + _In_ ULONG CreateDisposition + ); + +_Requires_lock_held_(_Global_critical_region_) +IO_STATUS_BLOCK +FatOpenExistingDcb ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PIO_STACK_LOCATION IrpSp, + _Inout_ PFILE_OBJECT FileObject, + _Inout_ PVCB Vcb, + _Inout_ PDCB Dcb, + _In_ PACCESS_MASK DesiredAccess, + _In_ USHORT ShareAccess, + _In_ ULONG CreateDisposition, + _In_ BOOLEAN NoEaKnowledge, + _In_ BOOLEAN DeleteOnClose, + _In_ BOOLEAN OpenRequiringOplock, + _In_ BOOLEAN FileNameOpenedDos, + _Out_ PBOOLEAN OplockPostIrp + ); + +_Requires_lock_held_(_Global_critical_region_) +IO_STATUS_BLOCK +FatOpenExistingFcb ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PIO_STACK_LOCATION IrpSp, + _Inout_ PFILE_OBJECT FileObject, + _Inout_ PVCB Vcb, + _Inout_ PFCB Fcb, + _In_ PACCESS_MASK DesiredAccess, + _In_ USHORT ShareAccess, + _In_ ULONG AllocationSize, + _In_ PFILE_FULL_EA_INFORMATION EaBuffer, + _In_ ULONG EaLength, + _In_ UCHAR FileAttributes, + _In_ ULONG CreateDisposition, + _In_ BOOLEAN NoEaKnowledge, + _In_ BOOLEAN DeleteOnClose, + _In_ BOOLEAN OpenRequiringOplock, + _In_ BOOLEAN FileNameOpenedDos, + _Out_ PBOOLEAN OplockPostIrp + ); + +_Requires_lock_held_(_Global_critical_region_) +IO_STATUS_BLOCK +FatOpenTargetDirectory ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PFILE_OBJECT FileObject, + _Inout_ PDCB Dcb, + _In_ PACCESS_MASK DesiredAccess, + _In_ USHORT ShareAccess, + _In_ BOOLEAN DoesNameExist, + _In_ BOOLEAN FileNameOpenedDos + ); + +_Success_(return.Status == STATUS_SUCCESS) +_Requires_lock_held_(_Global_critical_region_) +IO_STATUS_BLOCK +FatOpenExistingDirectory ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PIO_STACK_LOCATION IrpSp, + _Inout_ PFILE_OBJECT FileObject, + _Inout_ PVCB Vcb, + _Outptr_result_maybenull_ PDCB *Dcb, + _In_ PDCB ParentDcb, + _In_ PDIRENT Dirent, + _In_ ULONG LfnByteOffset, + _In_ ULONG DirentByteOffset, + _In_ PUNICODE_STRING Lfn, + _In_ PACCESS_MASK DesiredAccess, + _In_ USHORT ShareAccess, + _In_ ULONG CreateDisposition, + _In_ BOOLEAN NoEaKnowledge, + _In_ BOOLEAN DeleteOnClose, + _In_ BOOLEAN FileNameOpenedDos, + _In_ BOOLEAN OpenRequiringOplock + ); + +_Requires_lock_held_(_Global_critical_region_) +IO_STATUS_BLOCK +FatOpenExistingFile ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PFILE_OBJECT FileObject, + _Inout_ PVCB Vcb, + _Outptr_result_maybenull_ PFCB *Fcb, + _In_ PDCB ParentDcb, + _In_ PDIRENT Dirent, + _In_ ULONG LfnByteOffset, + _In_ ULONG DirentByteOffset, + _In_ PUNICODE_STRING Lfn, + _In_ PACCESS_MASK DesiredAccess, + _In_ USHORT ShareAccess, + _In_ ULONG AllocationSize, + _In_ PFILE_FULL_EA_INFORMATION EaBuffer, + _In_ ULONG EaLength, + _In_ UCHAR FileAttributes, + _In_ ULONG CreateDisposition, + _In_ BOOLEAN IsPagingFile, + _In_ BOOLEAN NoEaKnowledge, + _In_ BOOLEAN DeleteOnClose, + _In_ BOOLEAN OpenRequiringOplock, + _In_ BOOLEAN FileNameOpenedDos + ); + +_Requires_lock_held_(_Global_critical_region_) +IO_STATUS_BLOCK +FatCreateNewDirectory ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PIO_STACK_LOCATION IrpSp, + _Inout_ PFILE_OBJECT FileObject, + _Inout_ PVCB Vcb, + _Inout_ PDCB ParentDcb, + _In_ POEM_STRING OemName, + _In_ PUNICODE_STRING UnicodeName, + _In_ PACCESS_MASK DesiredAccess, + _In_ USHORT ShareAccess, + _In_ PFILE_FULL_EA_INFORMATION EaBuffer, + _In_ ULONG EaLength, + _In_ UCHAR FileAttributes, + _In_ BOOLEAN NoEaKnowledge, + _In_ BOOLEAN DeleteOnClose, + _In_ BOOLEAN OpenRequiringOplock + ); + +_Requires_lock_held_(_Global_critical_region_) +IO_STATUS_BLOCK +FatCreateNewFile ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PIO_STACK_LOCATION IrpSp, + _Inout_ PFILE_OBJECT FileObject, + _Inout_ PVCB Vcb, + _Inout_ PDCB ParentDcb, + _In_ POEM_STRING OemName, + _In_ PUNICODE_STRING UnicodeName, + _In_ PACCESS_MASK DesiredAccess, + _In_ USHORT ShareAccess, + _In_ ULONG AllocationSize, + _In_ PFILE_FULL_EA_INFORMATION EaBuffer, + _In_ ULONG EaLength, + _In_ UCHAR FileAttributes, + _In_ PUNICODE_STRING LfnBuffer, + _In_ BOOLEAN IsPagingFile, + _In_ BOOLEAN NoEaKnowledge, + _In_ BOOLEAN DeleteOnClose, + _In_ BOOLEAN OpenRequiringOplock, + _In_ BOOLEAN TemporaryFile + ); + +_Requires_lock_held_(_Global_critical_region_) +IO_STATUS_BLOCK +FatSupersedeOrOverwriteFile ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PFILE_OBJECT FileObject, + _Inout_ PFCB Fcb, + _In_ ULONG AllocationSize, + _In_ PFILE_FULL_EA_INFORMATION EaBuffer, + _In_ ULONG EaLength, + _In_ UCHAR FileAttributes, + _In_ ULONG CreateDisposition, + _In_ BOOLEAN NoEaKnowledge + ); + +NTSTATUS +FatCheckSystemSecurityAccess ( + _In_ PIRP_CONTEXT IrpContext + ); + +NTSTATUS +FatCheckShareAccess ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PFILE_OBJECT FileObject, + _In_ PFCB Fcb, + _In_ PACCESS_MASK DesiredAccess, + _In_ ULONG ShareAccess + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, FatCheckShareAccess) +#pragma alloc_text(PAGE, FatCheckSystemSecurityAccess) +#pragma alloc_text(PAGE, FatCommonCreate) +#pragma alloc_text(PAGE, FatCreateNewDirectory) +#pragma alloc_text(PAGE, FatCreateNewFile) +#pragma alloc_text(PAGE, FatFsdCreate) +#pragma alloc_text(PAGE, FatOpenExistingDcb) +#pragma alloc_text(PAGE, FatOpenExistingDirectory) +#pragma alloc_text(PAGE, FatOpenExistingFcb) +#pragma alloc_text(PAGE, FatOpenExistingFile) +#pragma alloc_text(PAGE, FatOpenRootDcb) +#pragma alloc_text(PAGE, FatOpenTargetDirectory) +#pragma alloc_text(PAGE, FatOpenVolume) +#pragma alloc_text(PAGE, FatSupersedeOrOverwriteFile) +#pragma alloc_text(PAGE, FatSetFullNameInFcb) +#endif + + +_Function_class_(IRP_MJ_CREATE) +_Function_class_(DRIVER_DISPATCH) +NTSTATUS +FatFsdCreate ( + _In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + _Inout_ PIRP Irp + ) + +/*++ + +Routine Description: + + This routine implements the FSD part of the NtCreateFile and NtOpenFile + API calls. + +Arguments: + + VolumeDeviceObject - Supplies the volume device object where the + file/directory exists that we are trying to open/create + + Irp - Supplies the Irp being processed + +Return Value: + + NTSTATUS - The Fsd status for the Irp + +--*/ + +{ + NTSTATUS Status; + PIRP_CONTEXT IrpContext = NULL; + + BOOLEAN TopLevel; + BOOLEAN ExceptionCompletedIrp = FALSE; + + + 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 ( FatDeviceIsFatFsdo( VolumeDeviceObject)) { + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = FILE_OPENED; + + IoCompleteRequest( Irp, IO_DISK_INCREMENT ); + + return STATUS_SUCCESS; + } + + TimerStart(Dbg); + + DebugTrace(+1, Dbg, "FatFsdCreate\n", 0); + + // + // Call the common create routine, with block allowed if the operation + // is synchronous. + // + + FsRtlEnterFileSystem(); + + TopLevel = FatIsIrpTopLevel( Irp ); + + try { + + IrpContext = FatCreateIrpContext( Irp, TRUE ); + + Status = FatCommonCreate( IrpContext, Irp ); + + } except(FatExceptionFilter( IrpContext, GetExceptionInformation() )) { + + // + // We had some trouble trying to perform the requested + // operation, so we'll abort the I/O request with + // the error status that we get back from the + // execption code + // + + Status = FatProcessException( IrpContext, Irp, GetExceptionCode() ); + ExceptionCompletedIrp = TRUE; + } + + if (TopLevel) { IoSetTopLevelIrp( NULL ); } + + FsRtlExitFileSystem(); + + + // + // Complete the request, unless we had an exception, in which case it + // was completed in FatProcessException (and the IrpContext freed). + // + // IrpContext is freed inside FatCompleteRequest. + // + + if (!ExceptionCompletedIrp && Status != STATUS_PENDING) { + FatCompleteRequest( IrpContext, Irp, Status ); + } + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "FatFsdCreate -> %08lx\n", Status ); + + TimerStop(Dbg,"FatFsdCreate"); + + UNREFERENCED_PARAMETER( VolumeDeviceObject ); + + return Status; +} + + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatCommonCreate ( + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp + ) + +/*++ + +Routine Description: + + This is the common routine for creating/opening a file 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; + IO_STATUS_BLOCK Iosb = {0}; + PIO_STACK_LOCATION IrpSp; + + PFILE_OBJECT FileObject; + PFILE_OBJECT RelatedFileObject; + UNICODE_STRING FileName; + ULONG AllocationSize; + PFILE_FULL_EA_INFORMATION EaBuffer; + PACCESS_MASK DesiredAccess; + ULONG Options; + UCHAR FileAttributes; + USHORT ShareAccess; + ULONG EaLength; + + BOOLEAN CreateDirectory; + BOOLEAN NoIntermediateBuffering; + BOOLEAN OpenDirectory; + BOOLEAN IsPagingFile; + BOOLEAN OpenTargetDirectory; + BOOLEAN DirectoryFile; + BOOLEAN NonDirectoryFile; + BOOLEAN NoEaKnowledge; + BOOLEAN DeleteOnClose; + BOOLEAN OpenRequiringOplock; + BOOLEAN TemporaryFile; + BOOLEAN FileNameOpenedDos = FALSE; + + ULONG CreateDisposition; + + PVCB Vcb; + PFCB Fcb = NULL; + PCCB Ccb; + PDCB ParentDcb; + PDCB FinalDcb = NULL; + + UNICODE_STRING FinalName = {0}; + UNICODE_STRING RemainingPart; + UNICODE_STRING NextRemainingPart = {0}; + UNICODE_STRING UpcasedFinalName; + WCHAR UpcasedBuffer[ FAT_CREATE_INITIAL_NAME_BUF_SIZE]; + + OEM_STRING OemFinalName; + UCHAR OemBuffer[ FAT_CREATE_INITIAL_NAME_BUF_SIZE*2]; + + PDIRENT Dirent; + PBCB DirentBcb = NULL; + ULONG LfnByteOffset; + ULONG DirentByteOffset; + + BOOLEAN PostIrp = FALSE; + BOOLEAN OplockPostIrp = FALSE; + BOOLEAN TrailingBackslash; + BOOLEAN FirstLoop = TRUE; + + ULONG MatchFlags = 0; + + CCB LocalCcb; + UNICODE_STRING Lfn; + UNICODE_STRING OrigLfn = {0}; + + WCHAR LfnBuffer[ FAT_CREATE_INITIAL_NAME_BUF_SIZE]; + + PAGED_CODE(); + + // + // Get the current IRP stack location + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "FatCommonCreate\n", 0 ); + DebugTrace( 0, Dbg, "Irp = %p\n", Irp ); + DebugTrace( 0, Dbg, "->Flags = %08lx\n", Irp->Flags ); + DebugTrace( 0, Dbg, "->FileObject = %p\n", IrpSp->FileObject ); + DebugTrace( 0, Dbg, " ->RelatedFileObject = %p\n", IrpSp->FileObject->RelatedFileObject ); + DebugTrace( 0, Dbg, " ->FileName = %wZ\n", &IrpSp->FileObject->FileName ); + DebugTrace( 0, Dbg, " ->FileName.Length = 0n%d\n", IrpSp->FileObject->FileName.Length ); + DebugTrace( 0, Dbg, "->AllocationSize.LowPart = %08lx\n", Irp->Overlay.AllocationSize.LowPart ); + DebugTrace( 0, Dbg, "->AllocationSize.HighPart = %08lx\n", Irp->Overlay.AllocationSize.HighPart ); + DebugTrace( 0, Dbg, "->SystemBuffer = %p\n", Irp->AssociatedIrp.SystemBuffer ); + DebugTrace( 0, Dbg, "->DesiredAccess = %08lx\n", IrpSp->Parameters.Create.SecurityContext->DesiredAccess ); + DebugTrace( 0, Dbg, "->Options = %08lx\n", IrpSp->Parameters.Create.Options ); + DebugTrace( 0, Dbg, "->FileAttributes = %04x\n", IrpSp->Parameters.Create.FileAttributes ); + DebugTrace( 0, Dbg, "->ShareAccess = %04x\n", IrpSp->Parameters.Create.ShareAccess ); + DebugTrace( 0, Dbg, "->EaLength = %08lx\n", IrpSp->Parameters.Create.EaLength ); + + // + // This is here because the Win32 layer can't avoid sending me double + // beginning backslashes. + // + + if ((IrpSp->FileObject->FileName.Length > sizeof(WCHAR)) && + (IrpSp->FileObject->FileName.Buffer[1] == L'\\') && + (IrpSp->FileObject->FileName.Buffer[0] == L'\\')) { + + IrpSp->FileObject->FileName.Length -= sizeof(WCHAR); + + RtlMoveMemory( &IrpSp->FileObject->FileName.Buffer[0], + &IrpSp->FileObject->FileName.Buffer[1], + IrpSp->FileObject->FileName.Length ); + + // + // If there are still two beginning backslashes, the name is bogus. + // + + if ((IrpSp->FileObject->FileName.Length > sizeof(WCHAR)) && + (IrpSp->FileObject->FileName.Buffer[1] == L'\\') && + (IrpSp->FileObject->FileName.Buffer[0] == L'\\')) { + + DebugTrace(-1, Dbg, "FatCommonCreate -> STATUS_OBJECT_NAME_INVALID\n", 0); + return STATUS_OBJECT_NAME_INVALID; + } + } + + // + // Reference our input parameters to make things easier + // + + NT_ASSERT( IrpSp->Parameters.Create.SecurityContext != NULL ); + + FileObject = IrpSp->FileObject; + FileName = FileObject->FileName; + RelatedFileObject = FileObject->RelatedFileObject; + AllocationSize = Irp->Overlay.AllocationSize.LowPart; + EaBuffer = Irp->AssociatedIrp.SystemBuffer; + DesiredAccess = &IrpSp->Parameters.Create.SecurityContext->DesiredAccess; + Options = IrpSp->Parameters.Create.Options; + FileAttributes = (UCHAR)(IrpSp->Parameters.Create.FileAttributes & ~FILE_ATTRIBUTE_NORMAL); + ShareAccess = IrpSp->Parameters.Create.ShareAccess; + EaLength = IrpSp->Parameters.Create.EaLength; + + // + // Set up the file object's Vpb pointer in case anything happens. + // This will allow us to get a reasonable pop-up. + // + + if ( RelatedFileObject != NULL ) { + FileObject->Vpb = RelatedFileObject->Vpb; + } + + // + // Force setting the archive bit in the attributes byte to follow OS/2, + // & DOS semantics. Also mask out any extraneous bits, note that + // we can't use the ATTRIBUTE_VALID_FLAGS constant because that has + // the control and normal flags set. + // + // Delay setting ARCHIVE in case this is a directory: 2/16/95 + // + + FileAttributes &= (FILE_ATTRIBUTE_READONLY | + FILE_ATTRIBUTE_HIDDEN | + FILE_ATTRIBUTE_SYSTEM | + FILE_ATTRIBUTE_ARCHIVE ); + + // + // Locate the volume device object and Vcb that we are trying to access + // + + Vcb = &((PVOLUME_DEVICE_OBJECT)IrpSp->DeviceObject)->Vcb; + + // + // Decipher Option flags and values + // + + // + // If this is an open by fileid operation, just fail it explicitly. FAT's + // source of fileids is not reversible for open operations. + // + + if (BooleanFlagOn( Options, FILE_OPEN_BY_FILE_ID )) { + + return STATUS_NOT_IMPLEMENTED; + } + + DirectoryFile = BooleanFlagOn( Options, FILE_DIRECTORY_FILE ); + NonDirectoryFile = BooleanFlagOn( Options, FILE_NON_DIRECTORY_FILE ); + NoIntermediateBuffering = BooleanFlagOn( Options, FILE_NO_INTERMEDIATE_BUFFERING ); + NoEaKnowledge = BooleanFlagOn( Options, FILE_NO_EA_KNOWLEDGE ); + DeleteOnClose = BooleanFlagOn( Options, FILE_DELETE_ON_CLOSE ); +#if (NTDDI_VERSION >= NTDDI_WIN7) + OpenRequiringOplock = BooleanFlagOn( Options, FILE_OPEN_REQUIRING_OPLOCK ); +#else + OpenRequiringOplock = FALSE; +#endif + + TemporaryFile = BooleanFlagOn( IrpSp->Parameters.Create.FileAttributes, + FILE_ATTRIBUTE_TEMPORARY ); + + CreateDisposition = (Options >> 24) & 0x000000ff; + + IsPagingFile = BooleanFlagOn( IrpSp->Flags, SL_OPEN_PAGING_FILE ); + OpenTargetDirectory = BooleanFlagOn( IrpSp->Flags, SL_OPEN_TARGET_DIRECTORY ); + + CreateDirectory = (BOOLEAN)(DirectoryFile && + ((CreateDisposition == FILE_CREATE) || + (CreateDisposition == FILE_OPEN_IF))); + + OpenDirectory = (BOOLEAN)(DirectoryFile && + ((CreateDisposition == FILE_OPEN) || + (CreateDisposition == FILE_OPEN_IF))); + + + // + // Make sure the input large integer is valid and that the dir/nondir + // indicates a storage type we understand. + // + + if (Irp->Overlay.AllocationSize.HighPart != 0 || + (DirectoryFile && NonDirectoryFile)) { + + DebugTrace(-1, Dbg, "FatCommonCreate -> STATUS_INVALID_PARAMETER\n", 0); + return STATUS_INVALID_PARAMETER; + } + + // + // Acquire exclusive access to the vcb, and enqueue the Irp if + // we didn't get it. + // + + if (!FatAcquireExclusiveVcb( IrpContext, Vcb )) { + + DebugTrace(0, Dbg, "Cannot acquire Vcb\n", 0); + + Iosb.Status = FatFsdPostRequest( IrpContext, Irp ); + + DebugTrace(-1, Dbg, "FatCommonCreate -> %08lx\n", Iosb.Status ); + return Iosb.Status; + } + + // + // Make sure we haven't been called recursively by a filter inside an existing + // create request. + // + + if (FlagOn( Vcb->VcbState, VCB_STATE_FLAG_CREATE_IN_PROGRESS)) { + +#pragma prefast( suppress:28159, "this is a serious programming error if it happens" ) + FatBugCheck( 0, 0, 0); + } + + // + // Initialize the DirentBcb to null + // + + DirentBcb = NULL; + + // + // Initialize our temp strings with their stack buffers. + // + + OemFinalName.Length = 0; + OemFinalName.MaximumLength = sizeof( OemBuffer); + OemFinalName.Buffer = (PCHAR)OemBuffer; + + UpcasedFinalName.Length = 0; + UpcasedFinalName.MaximumLength = sizeof( UpcasedBuffer); + UpcasedFinalName.Buffer = UpcasedBuffer; + + Lfn.Length = 0; + Lfn.MaximumLength = sizeof( LfnBuffer); + Lfn.Buffer = LfnBuffer; + + try { + + // + // Make sure the vcb is in a usable condition. This will raise + // and error condition if the volume is unusable + // + + FatVerifyVcb( IrpContext, Vcb ); + + // + // If the Vcb is locked then we cannot open another file + // + + if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_LOCKED)) { + + DebugTrace(0, Dbg, "Volume is locked\n", 0); + + Status = STATUS_ACCESS_DENIED; + if (Vcb->VcbCondition != VcbGood) { + + Status = STATUS_VOLUME_DISMOUNTED; + } + try_return( Iosb.Status = Status ); + } + + // + // Don't allow the DELETE_ON_CLOSE option if the volume is + // write-protected. + // + + if (DeleteOnClose && FlagOn(Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED)) { + + // + // Set the real device for the pop-up info, and set the verify + // bit in the device object, so that we will force a verify + // in case the user put the correct media back in. + // + + IoSetHardErrorOrVerifyDevice( IrpContext->OriginatingIrp, + Vcb->Vpb->RealDevice ); + + SetFlag(Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME); + + FatRaiseStatus( IrpContext, STATUS_MEDIA_WRITE_PROTECTED ); + } + + // + // If this is a fat32 volume, EA's are not supported. + // + + if (EaBuffer != NULL) { + + try_return( Iosb.Status = STATUS_EAS_NOT_SUPPORTED ); + } + + // + // Check if we are opening the volume and not a file/directory. + // We are opening the volume if the name is empty and there + // isn't a related file object. If there is a related file object + // then it is the Vcb itself. + // + + if (FileName.Length == 0) { + + PVCB DecodeVcb = NULL; + + if (RelatedFileObject == NULL || + FatDecodeFileObject( RelatedFileObject, + &DecodeVcb, + &Fcb, + &Ccb ) == UserVolumeOpen) { + + NT_ASSERT( RelatedFileObject == NULL || Vcb == DecodeVcb ); + + // + // Check if we were to open a directory + // + + if (DirectoryFile) { + + DebugTrace(0, Dbg, "Cannot open volume as a directory\n", 0); + + try_return( Iosb.Status = STATUS_NOT_A_DIRECTORY ); + } + + // + // Can't open the TargetDirectory of the DASD volume. + // + + if (OpenTargetDirectory) { + + try_return( Iosb.Status = STATUS_INVALID_PARAMETER ); + } + + DebugTrace(0, Dbg, "Opening the volume, Vcb = %p\n", Vcb); + + CollectCreateHitStatistics(Vcb); + + Iosb = FatOpenVolume( IrpContext, + FileObject, + Vcb, + DesiredAccess, + ShareAccess, + CreateDisposition ); + + Irp->IoStatus.Information = Iosb.Information; + try_return( Iosb.Status ); + } + } + + // + // If there is a related file object then this is a relative open. + // The related file object is the directory to start our search at. + // Return an error if it is not a directory. + // + + if (RelatedFileObject != NULL) { + + PVCB RelatedVcb; + PDCB RelatedDcb; + PCCB RelatedCcb; + TYPE_OF_OPEN TypeOfOpen; + + TypeOfOpen = FatDecodeFileObject( RelatedFileObject, + &RelatedVcb, + &RelatedDcb, + &RelatedCcb ); + + if (TypeOfOpen != UserFileOpen && + TypeOfOpen != UserDirectoryOpen) { + + DebugTrace(0, Dbg, "Invalid related file object\n", 0); + + try_return( Iosb.Status = STATUS_OBJECT_PATH_NOT_FOUND ); + } + + // + // A relative open must be via a relative path. + // + + if (FileName.Length != 0 && + FileName.Buffer[0] == L'\\') { + + try_return( Iosb.Status = STATUS_OBJECT_NAME_INVALID ); + } + + // + // Set up the file object's Vpb pointer in case anything happens. + // + + NT_ASSERT( Vcb == RelatedVcb ); + + FileObject->Vpb = RelatedFileObject->Vpb; + + // + // Now verify the related Fcb so we don't get in trouble later + // by assuming its in good shape. + // + + FatVerifyFcb( IrpContext, RelatedDcb ); + + ParentDcb = RelatedDcb; + + } else { + + // + // This is not a relative open, so check if we're + // opening the root dcb + // + + if ((FileName.Length == sizeof(WCHAR)) && + (FileName.Buffer[0] == L'\\')) { + + // + // Check if we were not supposed to open a directory + // + + if (NonDirectoryFile) { + + DebugTrace(0, Dbg, "Cannot open root directory as a file\n", 0); + + try_return( Iosb.Status = STATUS_FILE_IS_A_DIRECTORY ); + } + + // + // Can't open the TargetDirectory of the root directory. + // + + if (OpenTargetDirectory) { + + try_return( Iosb.Status = STATUS_INVALID_PARAMETER ); + } + + // + // Not allowed to delete root directory. + // + + if (DeleteOnClose) { + + try_return( Iosb.Status = STATUS_CANNOT_DELETE ); + } + + DebugTrace(0, Dbg, "Opening root dcb\n", 0); + + CollectCreateHitStatistics(Vcb); + + Iosb = FatOpenRootDcb( IrpContext, + FileObject, + Vcb, + DesiredAccess, + ShareAccess, + CreateDisposition ); + + Irp->IoStatus.Information = Iosb.Information; + try_return( Iosb.Status ); + } + + // + // Nope, we will be opening relative to the root directory. + // + + ParentDcb = Vcb->RootDcb; + + // + // Now verify the root Dcb so we don't get in trouble later + // by assuming its in good shape. + // + + FatVerifyFcb( IrpContext, ParentDcb ); + } + + // + // FatCommonCreate(): trailing backslash check + // + + + if ((FileName.Length != 0) && + (FileName.Buffer[FileName.Length/sizeof(WCHAR)-1] == L'\\')) { + + FileName.Length -= sizeof(WCHAR); + TrailingBackslash = TRUE; + + } else { + + TrailingBackslash = FALSE; + } + + // + // Check for max path. We might want to tighten this down to DOS MAX_PATH + // for maximal interchange with non-NT platforms, but for now defer to the + // possibility of something depending on it. + // + + if (ParentDcb->FullFileName.Buffer == NULL) { + + FatSetFullFileNameInFcb( IrpContext, ParentDcb ); + } + + if ((USHORT) (ParentDcb->FullFileName.Length + sizeof(WCHAR) + FileName.Length) <= FileName.Length) { + + try_return( Iosb.Status = STATUS_OBJECT_NAME_INVALID ); + } + + // + // We loop here until we land on an Fcb that is in a good + // condition. This way we can reopen files that have stale handles + // to files of the same name but are now different. + // + + while ( TRUE ) { + + Fcb = ParentDcb; + RemainingPart = FileName; + + // + // Now walk down the Dcb tree looking for the longest prefix. + // This one exit condition in the while() is to handle a + // special case condition (relative NULL name open), the main + // exit conditions are at the bottom of the loop. + // + + while (RemainingPart.Length != 0) { + + PFCB NextFcb; + + FsRtlDissectName( RemainingPart, + &FinalName, + &NextRemainingPart ); + + // + // If RemainingPart starts with a backslash the name is + // invalid. + // Check for no more than 255 characters in FinalName + // + + if (((NextRemainingPart.Length != 0) && (NextRemainingPart.Buffer[0] == L'\\')) || + (FinalName.Length > 255*sizeof(WCHAR))) { + + try_return( Iosb.Status = STATUS_OBJECT_NAME_INVALID ); + } + + // + // Now, try to convert this one component into Oem and search + // the splay tree. If it works then that's great, otherwise + // we have to try with the UNICODE name instead. + // + + FatEnsureStringBufferEnough( &OemFinalName, + FinalName.Length); + + Status = RtlUpcaseUnicodeStringToCountedOemString( &OemFinalName, &FinalName, FALSE ); + + + if (NT_SUCCESS(Status)) { + + NextFcb = FatFindFcb( IrpContext, + &Fcb->Specific.Dcb.RootOemNode, + (PSTRING)&OemFinalName, + &FileNameOpenedDos ); + + } else { + + NextFcb = NULL; + OemFinalName.Length = 0; + + if (Status != STATUS_UNMAPPABLE_CHARACTER) { + + try_return( Iosb.Status = Status ); + } + } + + // + // If we didn't find anything searching the Oem space, we + // have to try the Unicode space. To save cycles in the + // common case that this tree is empty, we do a quick check + // here. + // + + if ((NextFcb == NULL) && Fcb->Specific.Dcb.RootUnicodeNode) { + + // + // First downcase, then upcase the string, because this + // is what happens when putting names into the tree (see + // strucsup.c, FatConstructNamesInFcb()). + // + + FatEnsureStringBufferEnough( &UpcasedFinalName, + FinalName.Length); + + Status = RtlDowncaseUnicodeString(&UpcasedFinalName, &FinalName, FALSE ); + NT_ASSERT( NT_SUCCESS( Status )); + + Status = RtlUpcaseUnicodeString( &UpcasedFinalName, &UpcasedFinalName, FALSE ); + NT_ASSERT( NT_SUCCESS( Status )); + + + NextFcb = FatFindFcb( IrpContext, + &Fcb->Specific.Dcb.RootUnicodeNode, + (PSTRING)&UpcasedFinalName, + &FileNameOpenedDos ); + } + + // + // If we got back an Fcb then we consumed the FinalName + // legitimately, so the remaining name is now RemainingPart. + // + + if (NextFcb != NULL) { + Fcb = NextFcb; + RemainingPart = NextRemainingPart; + } + + if ((NextFcb == NULL) || + (NodeType(NextFcb) == FAT_NTC_FCB) || + (NextRemainingPart.Length == 0)) { + + break; + } + } + + // + // Remaining name cannot start with a backslash + // + + if (RemainingPart.Length && (RemainingPart.Buffer[0] == L'\\')) { + + RemainingPart.Length -= sizeof(WCHAR); + RemainingPart.Buffer += 1; + } + + // + // Now verify that everybody up to the longest found prefix is valid. + // + + try { + + FatVerifyFcb( IrpContext, Fcb ); + + } except( (GetExceptionCode() == STATUS_FILE_INVALID) ? + EXCEPTION_EXECUTE_HANDLER : + EXCEPTION_CONTINUE_SEARCH ) { + + FatResetExceptionState( IrpContext ); + } + + if ( Fcb->FcbCondition == FcbGood ) { + + // + // If we are trying to open a paging file and have happened + // upon the DelayedCloseFcb, make it go away, and try again. + // + + if (IsPagingFile && FirstLoop && + (NodeType(Fcb) == FAT_NTC_FCB) && + (!IsListEmpty( &FatData.AsyncCloseList ) || + !IsListEmpty( &FatData.DelayedCloseList ))) { + + FatFspClose(Vcb); + + FirstLoop = FALSE; + + continue; + + } else { + + break; + } + + } else { + + FatRemoveNames( IrpContext, Fcb ); + } + } + + NT_ASSERT( Fcb->FcbCondition == FcbGood ); + + // + // If there is already an Fcb for a paging file open and + // it was not already opened as a paging file, we cannot + // continue as it is too difficult to move a live Fcb to + // non-paged pool. + // + + if (IsPagingFile) { + + if (NodeType(Fcb) == FAT_NTC_FCB && + !FlagOn( Fcb->FcbState, FCB_STATE_PAGING_FILE )) { + + try_return( Iosb.Status = STATUS_SHARING_VIOLATION ); + } + + // + // Check for a system file. + // + + } else if (FlagOn( Fcb->FcbState, FCB_STATE_SYSTEM_FILE )) { + + try_return( Iosb.Status = STATUS_ACCESS_DENIED ); + } + + // + // If the longest prefix is pending delete (either the file or + // some higher level directory), we cannot continue. + // + + if (FlagOn( Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE )) { + + try_return( Iosb.Status = STATUS_DELETE_PENDING ); + } + + // + // Now that we've found the longest matching prefix we'll + // check if there isn't any remaining part because that means + // we've located an existing fcb/dcb to open and we can do the open + // without going to the disk + // + + if (RemainingPart.Length == 0) { + + // + // First check if the user wanted to open the target directory + // and if so then call the subroutine to finish the open. + // + + if (OpenTargetDirectory) { + + CollectCreateHitStatistics(Vcb); + + Iosb = FatOpenTargetDirectory( IrpContext, + FileObject, + Fcb->ParentDcb, + DesiredAccess, + ShareAccess, + TRUE, + FileNameOpenedDos ); + Irp->IoStatus.Information = Iosb.Information; + try_return( Iosb.Status ); + } + + // + // We can open an existing fcb/dcb, now we only need to case + // on which type to open. + // + + if (NodeType(Fcb) == FAT_NTC_DCB || NodeType(Fcb) == FAT_NTC_ROOT_DCB) { + + // + // This is a directory we're opening up so check if + // we were not to open a directory + // + + if (NonDirectoryFile) { + + DebugTrace(0, Dbg, "Cannot open directory as a file\n", 0); + + try_return( Iosb.Status = STATUS_FILE_IS_A_DIRECTORY ); + } + + DebugTrace(0, Dbg, "Open existing dcb, Dcb = %p\n", Fcb); + + CollectCreateHitStatistics(Vcb); + + Iosb = FatOpenExistingDcb( IrpContext, + IrpSp, + FileObject, + Vcb, + (PDCB)Fcb, + DesiredAccess, + ShareAccess, + CreateDisposition, + NoEaKnowledge, + DeleteOnClose, + OpenRequiringOplock, + FileNameOpenedDos, + &OplockPostIrp ); + + if (Iosb.Status != STATUS_PENDING) { + + Irp->IoStatus.Information = Iosb.Information; + + } else { + + DebugTrace(0, Dbg, "Enqueue Irp to FSP\n", 0); + + PostIrp = TRUE; + } + + try_return( Iosb.Status ); + } + + // + // Check if we're trying to open an existing Fcb and that + // the user didn't want to open a directory. Note that this + // call might actually come back with status_pending because + // the user wanted to supersede or overwrite the file and we + // cannot block. If it is pending then we do not complete the + // request, and we fall through the bottom to the code that + // dispatches the request to the fsp. + // + + if (NodeType(Fcb) == FAT_NTC_FCB) { + + // + // Check if we were only to open a directory + // + + if (OpenDirectory) { + + DebugTrace(0, Dbg, "Cannot open file as directory\n", 0); + + try_return( Iosb.Status = STATUS_NOT_A_DIRECTORY ); + } + + DebugTrace(0, Dbg, "Open existing fcb, Fcb = %p\n", Fcb); + + if ( TrailingBackslash ) { + try_return( Iosb.Status = STATUS_OBJECT_NAME_INVALID ); + } + + CollectCreateHitStatistics(Vcb); + + Iosb = FatOpenExistingFcb( IrpContext, + IrpSp, + FileObject, + Vcb, + Fcb, + DesiredAccess, + ShareAccess, + AllocationSize, + EaBuffer, + EaLength, + FileAttributes, + CreateDisposition, + NoEaKnowledge, + DeleteOnClose, + OpenRequiringOplock, + FileNameOpenedDos, + &OplockPostIrp ); + + if (Iosb.Status != STATUS_PENDING) { + + // + // Check if we need to set the cache support flag in + // the file object + // + + if (NT_SUCCESS( Iosb.Status) && !NoIntermediateBuffering) { + + FileObject->Flags |= FO_CACHE_SUPPORTED; + } + + Irp->IoStatus.Information = Iosb.Information; + + } else { + + DebugTrace(0, Dbg, "Enqueue Irp to FSP\n", 0); + + PostIrp = TRUE; + } + + try_return( Iosb.Status ); + } + + // + // Not and Fcb or a Dcb so we bug check + // + +#pragma prefast( suppress:28159, "this is a serious corruption if it happens" ) + FatBugCheck( NodeType(Fcb), (ULONG_PTR) Fcb, 0 ); + } + + // + // There is more in the name to parse than we have in existing + // fcbs/dcbs. So now make sure that fcb we got for the largest + // matching prefix is really a dcb otherwise we can't go any + // further + // + + if ((NodeType(Fcb) != FAT_NTC_DCB) && (NodeType(Fcb) != FAT_NTC_ROOT_DCB)) { + + DebugTrace(0, Dbg, "Cannot open file as subdirectory, Fcb = %p\n", Fcb); + + try_return( Iosb.Status = STATUS_OBJECT_PATH_NOT_FOUND ); + } + + // + // Otherwise we continue on processing the Irp and allowing ourselves + // to block for I/O as necessary. Find/create additional dcb's for + // the one we're trying to open. We loop until either remaining part + // is empty or we get a bad filename. When we exit FinalName is + // the last name in the string we're after, and ParentDcb is the + // parent directory that will contain the opened/created + // file/directory. + // + // Make sure the rest of the name is valid in at least the LFN + // character set (which just happens to be that of HPFS). + // + // If we are not in ChicagoMode, use FAT semantics. + // + + ParentDcb = Fcb; + FirstLoop = TRUE; + + while (TRUE) { + + // + // We do one little optimization here on the first iteration of + // the loop since we know that we have already tried to convert + // FinalOemName from the original UNICODE. + // + + if (FirstLoop) { + + FirstLoop = FALSE; + RemainingPart = NextRemainingPart; + Status = OemFinalName.Length ? STATUS_SUCCESS : STATUS_UNMAPPABLE_CHARACTER; + + } else { + + // + // Dissect the remaining part. + // + + DebugTrace(0, Dbg, "Dissecting the name %wZ\n", &RemainingPart); + + FsRtlDissectName( RemainingPart, + &FinalName, + &RemainingPart ); + + // + // If RemainingPart starts with a backslash the name is + // invalid. + // Check for no more than 255 characters in FinalName + // + + if (((RemainingPart.Length != 0) && (RemainingPart.Buffer[0] == L'\\')) || + (FinalName.Length > 255*sizeof(WCHAR))) { + + try_return( Iosb.Status = STATUS_OBJECT_NAME_INVALID ); + } + + // + // Now, try to convert this one component into Oem. If it works + // then that's great, otherwise we have to try with the UNICODE + // name instead. + // + + FatEnsureStringBufferEnough( &OemFinalName, + FinalName.Length); + + Status = RtlUpcaseUnicodeStringToCountedOemString( &OemFinalName, &FinalName, FALSE ); + } + + if (NT_SUCCESS(Status)) { + + // + // We'll start by trying to locate the dirent for the name. Note + // that we already know that there isn't an Fcb/Dcb for the file + // otherwise we would have found it when we did our prefix lookup. + // + + if (FatIsNameShortOemValid( IrpContext, OemFinalName, FALSE, FALSE, FALSE )) { + + FatStringTo8dot3( IrpContext, + OemFinalName, + &LocalCcb.OemQueryTemplate.Constant ); + + LocalCcb.Flags = 0; + + } else { + + LocalCcb.Flags = CCB_FLAG_SKIP_SHORT_NAME_COMPARE; + } + + } else { + + LocalCcb.Flags = CCB_FLAG_SKIP_SHORT_NAME_COMPARE; + + if (Status != STATUS_UNMAPPABLE_CHARACTER) { + + try_return( Iosb.Status = Status ); + } + } + + // + // Now we know a lot about the final name, so do legal name + // checking here. + // + + if (FatData.ChicagoMode) { + + if (!FatIsNameLongUnicodeValid( IrpContext, &FinalName, FALSE, FALSE, FALSE )) { + + try_return( Iosb.Status = STATUS_OBJECT_NAME_INVALID ); + } + + } else { + + if (FlagOn(LocalCcb.Flags, CCB_FLAG_SKIP_SHORT_NAME_COMPARE)) { + + try_return( Iosb.Status = STATUS_OBJECT_NAME_INVALID ); + } + } + + DebugTrace(0, Dbg, "FinalName is %wZ\n", &FinalName); + DebugTrace(0, Dbg, "RemainingPart is %wZ\n", &RemainingPart); + + FatEnsureStringBufferEnough( &UpcasedFinalName, + FinalName.Length); + + if (!NT_SUCCESS(Status = RtlUpcaseUnicodeString( &UpcasedFinalName, &FinalName, FALSE))) { + + try_return( Iosb.Status = Status ); + } + + LocalCcb.UnicodeQueryTemplate = UpcasedFinalName; + LocalCcb.ContainsWildCards = FALSE; + + Lfn.Length = 0; + + + FatLocateDirent( IrpContext, + ParentDcb, + &LocalCcb, + 0, + &MatchFlags, + &Dirent, + &DirentBcb, + (PVBO)&DirentByteOffset, + &FileNameOpenedDos, + &Lfn, + &OrigLfn); + + // + // Remember we read this Dcb for error recovery. + // + + FinalDcb = ParentDcb; + + // + // If the remaining part is now empty then this is the last name + // in the string and the one we want to open + // + + if (RemainingPart.Length == 0) { + + + break; + } + + // + // We didn't find a dirent, bail. + // + + if (Dirent == NULL) { + + Iosb.Status = STATUS_OBJECT_PATH_NOT_FOUND; + try_return( Iosb.Status ); + } + + // + // We now have a dirent, make sure it is a directory + // + + if (!FlagOn( Dirent->Attributes, FAT_DIRENT_ATTR_DIRECTORY )) { + + Iosb.Status = STATUS_OBJECT_PATH_NOT_FOUND; + try_return( Iosb.Status ); + } + + // + // Compute the LfnByteOffset. + // + + LfnByteOffset = DirentByteOffset - + FAT_LFN_DIRENTS_NEEDED(&Lfn) * sizeof(LFN_DIRENT); + + // + // Create a dcb for the new directory + // + + ParentDcb = FatCreateDcb( IrpContext, + Vcb, + ParentDcb, + LfnByteOffset, + DirentByteOffset, + Dirent, + &Lfn ); + + // + // Remember we created this Dcb for error recovery. + // + + FinalDcb = ParentDcb; + + FatSetFullNameInFcb( IrpContext, ParentDcb, &FinalName ); + } + + // + // First check if the user wanted to open the target directory + // and if so then call the subroutine to finish the open. + // + + if (OpenTargetDirectory) { + + Iosb = FatOpenTargetDirectory( IrpContext, + FileObject, + ParentDcb, + DesiredAccess, + ShareAccess, + Dirent ? TRUE : FALSE, + FileNameOpenedDos); + + Irp->IoStatus.Information = Iosb.Information; + try_return( Iosb.Status ); + } + + if (Dirent != NULL) { + + // + // Compute the LfnByteOffset. + // + + LfnByteOffset = DirentByteOffset - + FAT_LFN_DIRENTS_NEEDED(&Lfn) * sizeof(LFN_DIRENT); + + // + // We were able to locate an existing dirent entry, so now + // see if it is a directory that we're trying to open. + // + + if (FlagOn( Dirent->Attributes, FAT_DIRENT_ATTR_DIRECTORY )) { + + // + // Make sure its okay to open a directory + // + + if (NonDirectoryFile) { + + DebugTrace(0, Dbg, "Cannot open directory as a file\n", 0); + + try_return( Iosb.Status = STATUS_FILE_IS_A_DIRECTORY ); + } + + DebugTrace(0, Dbg, "Open existing directory\n", 0); + + Iosb = FatOpenExistingDirectory( IrpContext, + IrpSp, + FileObject, + Vcb, + &Fcb, + ParentDcb, + Dirent, + LfnByteOffset, + DirentByteOffset, + &Lfn, + DesiredAccess, + ShareAccess, + CreateDisposition, + NoEaKnowledge, + DeleteOnClose, + FileNameOpenedDos, + OpenRequiringOplock ); + + Irp->IoStatus.Information = Iosb.Information; + try_return( Iosb.Status ); + } + + // + // Otherwise we're trying to open and existing file, and we + // need to check if the user only wanted to open a directory. + // + + if (OpenDirectory) { + + DebugTrace(0, Dbg, "Cannot open file as directory\n", 0); + + try_return( Iosb.Status = STATUS_NOT_A_DIRECTORY ); + } + + DebugTrace(0, Dbg, "Open existing file\n", 0); + + if ( TrailingBackslash ) { + try_return( Iosb.Status = STATUS_OBJECT_NAME_INVALID ); + } + + + Iosb = FatOpenExistingFile( IrpContext, + FileObject, + Vcb, + &Fcb, + ParentDcb, + Dirent, + LfnByteOffset, + DirentByteOffset, + &Lfn, + DesiredAccess, + ShareAccess, + AllocationSize, + EaBuffer, + EaLength, + FileAttributes, + CreateDisposition, + IsPagingFile, + NoEaKnowledge, + DeleteOnClose, + OpenRequiringOplock, + FileNameOpenedDos ); + + // + // Check if we need to set the cache support flag in + // the file object + // + + if (NT_SUCCESS(Iosb.Status) && !NoIntermediateBuffering) { + + FileObject->Flags |= FO_CACHE_SUPPORTED; + } + + Irp->IoStatus.Information = Iosb.Information; + try_return( Iosb.Status ); + } + + // + // We can't locate a dirent so this is a new file. + // + + // + // Now check to see if we wanted to only open an existing file. + // And then case on whether we wanted to create a file or a directory. + // + + if ((CreateDisposition == FILE_OPEN) || + (CreateDisposition == FILE_OVERWRITE)) { + + DebugTrace( 0, Dbg, "Cannot open nonexisting file\n", 0); + + try_return( Iosb.Status = STATUS_OBJECT_NAME_NOT_FOUND ); + } + + // + // Skip a few cycles later if we know now that the Oem name is not + // valid 8.3. + // + + if (FlagOn(LocalCcb.Flags, CCB_FLAG_SKIP_SHORT_NAME_COMPARE)) { + + OemFinalName.Length = 0; + } + + // + // Determine the granted access for this operation now. + // + + if (!NT_SUCCESS( Iosb.Status = FatCheckSystemSecurityAccess( IrpContext ))) { + + try_return( Iosb ); + } + + if (CreateDirectory) { + + DebugTrace(0, Dbg, "Create new directory\n", 0); + + // + // If this media is write protected, don't even try the create. + // + + if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED)) { + + // + // Set the real device for the pop-up info, and set the verify + // bit in the device object, so that we will force a verify + // in case the user put the correct media back in. + // + + + IoSetHardErrorOrVerifyDevice( IrpContext->OriginatingIrp, + Vcb->Vpb->RealDevice ); + + SetFlag(Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME); + + FatRaiseStatus( IrpContext, STATUS_MEDIA_WRITE_PROTECTED ); + } + + // + // Don't allow people to create directories with the + // temporary bit set. + // + + if (TemporaryFile) { + + try_return( Iosb.Status = STATUS_INVALID_PARAMETER ); + } + + Iosb = FatCreateNewDirectory( IrpContext, + IrpSp, + FileObject, + Vcb, + ParentDcb, + &OemFinalName, + &FinalName, + DesiredAccess, + ShareAccess, + EaBuffer, + EaLength, + FileAttributes, + NoEaKnowledge, + DeleteOnClose, + OpenRequiringOplock ); + + Irp->IoStatus.Information = Iosb.Information; + try_return( Iosb.Status ); + } + + DebugTrace(0, Dbg, "Create new file\n", 0); + + if ( TrailingBackslash ) { + + try_return( Iosb.Status = STATUS_OBJECT_NAME_INVALID ); + } + + // + // If this media is write protected, don't even try the create. + // + + if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED)) { + + // + // Set the real device for the pop-up info, and set the verify + // bit in the device object, so that we will force a verify + // in case the user put the correct media back in. + // + + + IoSetHardErrorOrVerifyDevice( IrpContext->OriginatingIrp, + Vcb->Vpb->RealDevice ); + + SetFlag(Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME); + + FatRaiseStatus( IrpContext, STATUS_MEDIA_WRITE_PROTECTED ); + } + + + Iosb = FatCreateNewFile( IrpContext, + IrpSp, + FileObject, + Vcb, + ParentDcb, + &OemFinalName, + &FinalName, + DesiredAccess, + ShareAccess, + AllocationSize, + EaBuffer, + EaLength, + FileAttributes, + &Lfn, + IsPagingFile, + NoEaKnowledge, + DeleteOnClose, + OpenRequiringOplock, + TemporaryFile ); + + // + // Check if we need to set the cache support flag in + // the file object + // + + if (NT_SUCCESS(Iosb.Status) && !NoIntermediateBuffering) { + + FileObject->Flags |= FO_CACHE_SUPPORTED; + } + + Irp->IoStatus.Information = Iosb.Information; + + try_exit: NOTHING; + + // + // This is a Beta Fix. Do this at a better place later. + // + + if (NT_SUCCESS(Iosb.Status) && !OpenTargetDirectory) { + + PFCB LocalFcb; + + // + // If there is an Fcb/Dcb, set the long file name. + // + + LocalFcb = FileObject->FsContext; + + if (LocalFcb && + ((NodeType(LocalFcb) == FAT_NTC_FCB) || + (NodeType(LocalFcb) == FAT_NTC_DCB)) && + (LocalFcb->FullFileName.Buffer == NULL)) { + + FatSetFullNameInFcb( IrpContext, LocalFcb, &FinalName ); + } + } + + } finally { + + DebugUnwind( FatCommonCreate ); + +#if (NTDDI_VERSION >= NTDDI_WIN7) + + // + // If we're not getting out with success, and if the caller wanted + // atomic create-with-oplock semantics make sure we back out any + // oplock that may have been granted. + // + + if ((AbnormalTermination() || + !NT_SUCCESS( Iosb.Status )) && + OpenRequiringOplock && + (Iosb.Status != STATUS_CANNOT_BREAK_OPLOCK) && + (IrpContext->ExceptionStatus != STATUS_CANNOT_BREAK_OPLOCK) && + (Fcb != NULL) && + FatIsFileOplockable( Fcb )) { + + FsRtlCheckOplockEx( FatGetFcbOplock(Fcb), + IrpContext->OriginatingIrp, + OPLOCK_FLAG_BACK_OUT_ATOMIC_OPLOCK, + NULL, + NULL, + NULL ); + } +#endif + + // + // There used to be a test here - the ASSERT replaces it. We will + // never have begun enumerating directories if we post the IRP for + // oplock reasons. + // + + NT_ASSERT( !OplockPostIrp || DirentBcb == NULL ); + + FatUnpinBcb( IrpContext, DirentBcb ); + + // + // If we are in an error path, check for any created subdir Dcbs that + // have to be unwound. Don't whack the root directory. + // + // Note this will leave a branch of Dcbs dangling if the directory file + // had not been built on the leaf (case: opening path which has an + // element containing an invalid character name). + // + + if (AbnormalTermination() || !NT_SUCCESS(Iosb.Status)) { + + ULONG SavedFlags; + + // + // Before doing the uninitialize, we have to unpin anything + // that has been repinned, but disable writethrough first. We + // disable raise from unpin-repin since we're already failing. + // + + SavedFlags = IrpContext->Flags; + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_RAISE | + IRP_CONTEXT_FLAG_DISABLE_WRITE_THROUGH ); + + FatUnpinRepinnedBcbs( IrpContext ); + + if ((FinalDcb != NULL) && + (NodeType(FinalDcb) == FAT_NTC_DCB) && + IsListEmpty(&FinalDcb->Specific.Dcb.ParentDcbQueue) && + (FinalDcb->OpenCount == 0) && + (FinalDcb->Specific.Dcb.DirectoryFile != NULL)) { + + PFILE_OBJECT DirectoryFileObject; + + DirectoryFileObject = FinalDcb->Specific.Dcb.DirectoryFile; + + FinalDcb->Specific.Dcb.DirectoryFile = NULL; + + CcUninitializeCacheMap( DirectoryFileObject, NULL, NULL ); + + ObDereferenceObject( DirectoryFileObject ); + } + + IrpContext->Flags = SavedFlags; + } + + if (AbnormalTermination()) { + + FatReleaseVcb( IrpContext, Vcb ); + } + + // + // Free up any string buffers we allocated + // + + FatFreeStringBuffer( &OemFinalName); + + FatFreeStringBuffer( &UpcasedFinalName); + + FatFreeStringBuffer( &Lfn); + } + + // + // The following code is only executed if we are exiting the + // procedure through a normal termination. We complete the request + // and if for any reason that bombs out then we need to unreference + // and possibly delete the fcb and ccb. + // + + try { + + if (PostIrp) { + + // + // If the Irp hasn't already been posted, do it now. + // + + if (!OplockPostIrp) { + + Iosb.Status = FatFsdPostRequest( IrpContext, Irp ); + } + + } else { + + FatUnpinRepinnedBcbs( IrpContext ); + } + + } finally { + + DebugUnwind( FatCommonCreate-in-FatCompleteRequest ); + + if (AbnormalTermination() ) { + + PVCB LocalVcb; + PFCB LocalFcb; + PCCB LocalCcb2; + PFILE_OBJECT DirectoryFileObject; + + // + // Unwind all of our counts. Note that if a write failed, then + // the volume has been marked for verify, and all volume + // structures will be cleaned up automatically. + // + + (VOID) FatDecodeFileObject( FileObject, &LocalVcb, &LocalFcb, &LocalCcb2 ); + + LocalFcb->UncleanCount -= 1; + LocalFcb->OpenCount -= 1; + LocalVcb->OpenFileCount -= 1; + + if (IsFileObjectReadOnly(FileObject)) { LocalVcb->ReadOnlyCount -= 1; } + + + // + // WinSE #307418 "Occasional data corruption when standby/resume + // while copying files to removable FAT formatted media". + // If new file creation request was interrupted by system suspend + // operation we should revert the changes we made to the parent + // directory and to the allocation table. + // + + if (IrpContext->ExceptionStatus == STATUS_VERIFY_REQUIRED && + NodeType( LocalFcb ) == FAT_NTC_FCB) { + + FatTruncateFileAllocation( IrpContext, LocalFcb, 0, TRUE ); + + FatDeleteDirent( IrpContext, LocalFcb, NULL, TRUE ); + } + + // + // If we leafed out on a new Fcb we should get rid of it at this point. + // + // Since the object isn't being opened, we have to do all of the teardown + // here. Our close path will not occur for this fileobject. Note this + // will leave a branch of Dcbs dangling since we do it by hand and don't + // chase to the root. + // + + if (LocalFcb->OpenCount == 0 && + (NodeType( LocalFcb ) == FAT_NTC_FCB || + IsListEmpty(&LocalFcb->Specific.Dcb.ParentDcbQueue))) { + + NT_ASSERT( NodeType( LocalFcb ) != FAT_NTC_ROOT_DCB ); + + if ( (NodeType( LocalFcb ) == FAT_NTC_DCB) && + (LocalFcb->Specific.Dcb.DirectoryFile != NULL) ) { + + DirectoryFileObject = LocalFcb->Specific.Dcb.DirectoryFile; + LocalFcb->Specific.Dcb.DirectoryFile = NULL; + + CcUninitializeCacheMap( DirectoryFileObject, + &FatLargeZero, + NULL ); + + ObDereferenceObject( DirectoryFileObject ); + + } else { + if (ARGUMENT_PRESENT( FileObject )) { + FileObject->SectionObjectPointer = NULL; + } + FatDeleteFcb( IrpContext, &LocalFcb ); + } + } + + FatDeleteCcb( IrpContext, &LocalCcb2 ); + + FatReleaseVcb( IrpContext, LocalVcb ); + + } else { + + FatReleaseVcb( IrpContext, Vcb ); + + if ( !PostIrp ) { + + // + // If this request is successful and the file was opened + // for FILE_EXECUTE access, then set the FileObject bit. + // + + NT_ASSERT( IrpSp->Parameters.Create.SecurityContext != NULL ); + if (FlagOn( *DesiredAccess, FILE_EXECUTE )) { + + SetFlag( FileObject->Flags, FO_FILE_FAST_IO_READ ); + } + + // + // Lock volume in drive if we opened a paging file, allocating a + // reserve MDL to guarantee paging file operations can always + // go forward. + // + + if (IsPagingFile && NT_SUCCESS(Iosb.Status)) { + +#pragma prefast( suppress:28112, "this should be safe" ) + if (!FatReserveMdl) { + + PMDL ReserveMdl = IoAllocateMdl( NULL, + FAT_RESERVE_MDL_SIZE * PAGE_SIZE, + TRUE, + FALSE, + NULL ); + + // + // Stash the MDL, and if it turned out there was already one there + // just free what we got. + // + + InterlockedCompareExchangePointer( &FatReserveMdl, ReserveMdl, NULL ); + +#pragma prefast( suppress:28112, "this should be safe" ) + if (FatReserveMdl != ReserveMdl) { + + IoFreeMdl( ReserveMdl ); + } + } + + SetFlag(Vcb->VcbState, VCB_STATE_FLAG_BOOT_OR_PAGING_FILE); + + if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA)) { + + FatToggleMediaEjectDisable( IrpContext, Vcb, TRUE ); + } + } + + } + } + + DebugTrace(-1, Dbg, "FatCommonCreate -> %08lx\n", Iosb.Status); + } + + CollectCreateStatistics(Vcb, Iosb.Status); + + return Iosb.Status; +} + + +// +// Internal support routine +// + +_Requires_lock_held_(_Global_critical_region_) +IO_STATUS_BLOCK +FatOpenVolume ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PFILE_OBJECT FileObject, + _Inout_ PVCB Vcb, + _In_ PACCESS_MASK DesiredAccess, + _In_ USHORT ShareAccess, + _In_ ULONG CreateDisposition + ) + +/*++ + +Routine Description: + + This routine opens the specified volume for DASD access + +Arguments: + + FileObject - Supplies the File object + + Vcb - Supplies the Vcb denoting the volume being opened + + DesiredAccess - Supplies the desired access of the caller + + ShareAccess - Supplies the share access of the caller + + CreateDisposition - Supplies the create disposition for this operation + +Return Value: + + IO_STATUS_BLOCK - Returns the completion status for the operation + +--*/ + +{ + NTSTATUS Status; + PIO_STACK_LOCATION IrpSp; + + IO_STATUS_BLOCK Iosb = {0,0}; + + BOOLEAN CleanedVolume = FALSE; + + // + // The following variables are for abnormal termination + // + + BOOLEAN UnwindShareAccess = FALSE; + PCCB UnwindCcb = NULL; + BOOLEAN UnwindCounts = FALSE; + BOOLEAN UnwindVolumeLock = FALSE; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatOpenVolume...\n", 0); + + try { + + // + // Check for proper desired access and rights + // + + if ((CreateDisposition != FILE_OPEN) && + (CreateDisposition != FILE_OPEN_IF)) { + + try_return( Iosb.Status = STATUS_ACCESS_DENIED ); + } + + // + // If the user does not want to share write or delete then we will try + // and take out a lock on the volume. + // + + if (!FlagOn(ShareAccess, FILE_SHARE_WRITE) && + !FlagOn(ShareAccess, FILE_SHARE_DELETE)) { + +#if (NTDDI_VERSION >= NTDDI_VISTA) + // + // See if the user has requested write access. If so, they cannot share + // read. There is one exception to this. We allow autochk to get an + // implicit lock on the volume while still allowing readers. Once the + // the system is booted, though, we do not allow this type of access. + // + + if (FlagOn( *DesiredAccess, (FILE_WRITE_DATA | FILE_APPEND_DATA) ) && + FsRtlAreVolumeStartupApplicationsComplete()) { + + ClearFlag( ShareAccess, FILE_SHARE_READ ); + } +#endif + + // + // Do a quick check here for handles on exclusive open. + // + + if (!FlagOn(ShareAccess, FILE_SHARE_READ) && + !FatIsHandleCountZero( IrpContext, Vcb )) { + + try_return( Iosb.Status = STATUS_SHARING_VIOLATION ); + } + + // + // Force Mm to get rid of its referenced file objects. + // + + FatFlushFat( IrpContext, Vcb ); + + FatPurgeReferencedFileObjects( IrpContext, Vcb->RootDcb, Flush ); + + // + // If the user also does not want to share read then we check + // if anyone is already using the volume, and if so then we + // deny the access. If the user wants to share read then + // we allow the current opens to stay provided they are only + // readonly opens and deny further opens. + // + + if (!FlagOn(ShareAccess, FILE_SHARE_READ)) { + + if (Vcb->OpenFileCount != 0) { + + try_return( Iosb.Status = STATUS_SHARING_VIOLATION ); + } + + } else { + + if (Vcb->ReadOnlyCount != Vcb->OpenFileCount) { + + try_return( Iosb.Status = STATUS_SHARING_VIOLATION ); + } + } + + // + // Lock the volume + // + + Vcb->VcbState |= VCB_STATE_FLAG_LOCKED; + Vcb->FileObjectWithVcbLocked = FileObject; + UnwindVolumeLock = TRUE; + + // + // Clean the volume + // + + CleanedVolume = TRUE; + + } else if (FlagOn( *DesiredAccess, FILE_READ_DATA | FILE_WRITE_DATA | FILE_APPEND_DATA )) { + + // + // Flush the volume and let ourselves push the clean bit out if everything + // worked. + // + + if (NT_SUCCESS( FatFlushVolume( IrpContext, Vcb, Flush ))) { + + CleanedVolume = TRUE; + } + } + + // + // Clean the volume if we believe it safe and reasonable. + // + + if (CleanedVolume && + FlagOn( Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DIRTY ) && + !FlagOn( Vcb->VcbState, VCB_STATE_FLAG_MOUNTED_DIRTY ) && + !CcIsThereDirtyData(Vcb->Vpb)) { + + // + // Cancel any pending clean volumes. + // + + (VOID)KeCancelTimer( &Vcb->CleanVolumeTimer ); + (VOID)KeRemoveQueueDpc( &Vcb->CleanVolumeDpc ); + + FatMarkVolume( IrpContext, Vcb, VolumeClean ); + ClearFlag( Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DIRTY ); + + // + // Unlock the volume if it is removable. + // + + if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA) && + !FlagOn(Vcb->VcbState, VCB_STATE_FLAG_BOOT_OR_PAGING_FILE)) { + + FatToggleMediaEjectDisable( IrpContext, Vcb, FALSE ); + } + } + + // + // If the volume is already opened by someone then we need to check + // the share access + // + + if (Vcb->DirectAccessOpenCount > 0) { + + if (!NT_SUCCESS(Iosb.Status = IoCheckShareAccess( *DesiredAccess, + ShareAccess, + FileObject, + &Vcb->ShareAccess, + TRUE ))) { + + try_return( Iosb.Status ); + } + + } else { + + IoSetShareAccess( *DesiredAccess, + ShareAccess, + FileObject, + &Vcb->ShareAccess ); + } + + UnwindShareAccess = TRUE; + + // + // Set up the context and section object pointers, and update + // our reference counts + // + + FatSetFileObject( FileObject, + UserVolumeOpen, + Vcb, + UnwindCcb = FatCreateCcb( IrpContext )); + + FileObject->SectionObjectPointer = &Vcb->SectionObjectPointers; + + Vcb->DirectAccessOpenCount += 1; + Vcb->OpenFileCount += 1; + if (IsFileObjectReadOnly(FileObject)) { Vcb->ReadOnlyCount += 1; } + UnwindCounts = TRUE; + FileObject->Flags |= FO_NO_INTERMEDIATE_BUFFERING; + + // + // At this point the open will succeed, so check if the user is getting explicit access + // to the device. If not, we will note this so we can deny modifying FSCTL to it. + // + + IrpSp = IoGetCurrentIrpStackLocation( IrpContext->OriginatingIrp ); + Status = FatExplicitDeviceAccessGranted( IrpContext, + Vcb->Vpb->RealDevice, + IrpSp->Parameters.Create.SecurityContext->AccessState, + (KPROCESSOR_MODE)( FlagOn( IrpSp->Flags, SL_FORCE_ACCESS_CHECK ) ? + UserMode : + IrpContext->OriginatingIrp->RequestorMode )); + + if (NT_SUCCESS( Status )) { + + SetFlag( UnwindCcb->Flags, CCB_FLAG_MANAGE_VOLUME_ACCESS ); + } + + // + // And set our status to success + // + + Iosb.Status = STATUS_SUCCESS; + Iosb.Information = FILE_OPENED; + + try_exit: NOTHING; + } finally { + + DebugUnwind( FatOpenVolume ); + + // + // If this is an abnormal termination then undo our work + // + + if (AbnormalTermination() || !NT_SUCCESS(Iosb.Status)) { + + if (UnwindCounts) { + Vcb->DirectAccessOpenCount -= 1; + Vcb->OpenFileCount -= 1; + if (IsFileObjectReadOnly(FileObject)) { Vcb->ReadOnlyCount -= 1; } + } + if (UnwindCcb != NULL) { FatDeleteCcb( IrpContext, &UnwindCcb ); } + if (UnwindShareAccess) { IoRemoveShareAccess( FileObject, &Vcb->ShareAccess ); } + if (UnwindVolumeLock) { Vcb->VcbState &= ~VCB_STATE_FLAG_LOCKED; } + } + + DebugTrace(-1, Dbg, "FatOpenVolume -> Iosb.Status = %08lx\n", Iosb.Status); + } + + return Iosb; +} + + +// +// Internal support routine +// + +_Requires_lock_held_(_Global_critical_region_) +IO_STATUS_BLOCK +FatOpenRootDcb ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PFILE_OBJECT FileObject, + _Inout_ PVCB Vcb, + _In_ PACCESS_MASK DesiredAccess, + _In_ USHORT ShareAccess, + _In_ ULONG CreateDisposition + ) + +/*++ + +Routine Description: + + This routine opens the root dcb for the volume + +Arguments: + + FileObject - Supplies the File object + + Vcb - Supplies the Vcb denoting the volume whose dcb is being opened. + + DesiredAccess - Supplies the desired access of the caller + + ShareAccess - Supplies the share access of the caller + + CreateDisposition - Supplies the create disposition for this operation + +Return Value: + + IO_STATUS_BLOCK - Returns the completion status for the operation + +Arguments: + +--*/ + +{ + PDCB RootDcb; + IO_STATUS_BLOCK Iosb = {0}; + + // + // The following variables are for abnormal termination + // + + BOOLEAN UnwindShareAccess = FALSE; + PCCB UnwindCcb = NULL; + BOOLEAN UnwindCounts = FALSE; + BOOLEAN RootDcbAcquired = FALSE; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatOpenRootDcb...\n", 0); + + // + // Locate the root dcb + // + + RootDcb = Vcb->RootDcb; + + // + // Get the Dcb exlcusive. This is important as cleanup does not + // acquire the Vcb. + // + + (VOID)FatAcquireExclusiveFcb( IrpContext, RootDcb ); + RootDcbAcquired = TRUE; + + try { + + // + // Check the create disposition and desired access + // + + if ((CreateDisposition != FILE_OPEN) && + (CreateDisposition != FILE_OPEN_IF)) { + + Iosb.Status = STATUS_ACCESS_DENIED; + try_return( Iosb ); + } + + if (!FatCheckFileAccess( IrpContext, + RootDcb->DirentFatFlags, + DesiredAccess)) { + + Iosb.Status = STATUS_ACCESS_DENIED; + try_return( Iosb ); + } + + // + // If the Root dcb is already opened by someone then we need + // to check the share access + // + + if (RootDcb->OpenCount > 0) { + + if (!NT_SUCCESS(Iosb.Status = IoCheckShareAccess( *DesiredAccess, + ShareAccess, + FileObject, + &RootDcb->ShareAccess, + TRUE ))) { + + try_return( Iosb ); + } + + } else { + + IoSetShareAccess( *DesiredAccess, + ShareAccess, + FileObject, + &RootDcb->ShareAccess ); + } + + UnwindShareAccess = TRUE; + + // + // Setup the context and section object pointers, and update + // our reference counts + // + + FatSetFileObject( FileObject, + UserDirectoryOpen, + RootDcb, + UnwindCcb = FatCreateCcb( IrpContext )); + + RootDcb->UncleanCount += 1; + RootDcb->OpenCount += 1; + Vcb->OpenFileCount += 1; + if (IsFileObjectReadOnly(FileObject)) { Vcb->ReadOnlyCount += 1; } + UnwindCounts = TRUE; + + // + // And set our status to success + // + + Iosb.Status = STATUS_SUCCESS; + Iosb.Information = FILE_OPENED; + + try_exit: NOTHING; + } finally { + + DebugUnwind( FatOpenRootDcb ); + + // + // If this is an abnormal termination then undo our work + // + + if (AbnormalTermination()) { + + if (UnwindCounts) { + RootDcb->UncleanCount -= 1; + RootDcb->OpenCount -= 1; + Vcb->OpenFileCount -= 1; + if (IsFileObjectReadOnly(FileObject)) { Vcb->ReadOnlyCount -= 1; } + } + if (UnwindCcb != NULL) { FatDeleteCcb( IrpContext, &UnwindCcb ); } + if (UnwindShareAccess) { IoRemoveShareAccess( FileObject, &RootDcb->ShareAccess ); } + } + + if (RootDcbAcquired) { + + FatReleaseFcb( IrpContext, RootDcb ); + } + + DebugTrace(-1, Dbg, "FatOpenRootDcb -> Iosb.Status = %08lx\n", Iosb.Status); + } + + return Iosb; +} + + +// +// Internal support routine +// + +_Requires_lock_held_(_Global_critical_region_) +IO_STATUS_BLOCK +FatOpenExistingDcb ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PIO_STACK_LOCATION IrpSp, + _Inout_ PFILE_OBJECT FileObject, + _Inout_ PVCB Vcb, + _Inout_ PDCB Dcb, + _In_ PACCESS_MASK DesiredAccess, + _In_ USHORT ShareAccess, + _In_ ULONG CreateDisposition, + _In_ BOOLEAN NoEaKnowledge, + _In_ BOOLEAN DeleteOnClose, + _In_ BOOLEAN OpenRequiringOplock, + _In_ BOOLEAN FileNameOpenedDos, + _Out_ PBOOLEAN OplockPostIrp + ) + +/*++ + +Routine Description: + + This routine opens the specified existing dcb + +Arguments: + + FileObject - Supplies the File object + + Vcb - Supplies the Vcb denoting the volume containing the dcb + + Dcb - Supplies the already existing dcb + + DesiredAccess - Supplies the desired access of the caller + + ShareAccess - Supplies the share access of the caller + + CreateDisposition - Supplies the create disposition for this operation + + NoEaKnowledge - This opener doesn't understand Ea's and we fail this + open if the file has NeedEa's. + + DeleteOnClose - The caller wants the file gone when the handle is closed + +Return Value: + + IO_STATUS_BLOCK - Returns the completion status for the operation + +--*/ + +{ + IO_STATUS_BLOCK Iosb = {0}; + PBCB DirentBcb = NULL; + PDIRENT Dirent; + + // + // The following variables are for abnormal termination + // + + BOOLEAN UnwindShareAccess = FALSE; + PCCB UnwindCcb = NULL; + BOOLEAN DcbAcquired = FALSE; + +#if (NTDDI_VERSION <= NTDDI_WIN7) + UNREFERENCED_PARAMETER( OpenRequiringOplock ); +#endif + + UNREFERENCED_PARAMETER( IrpSp ); + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatOpenExistingDcb...\n", 0); + + // + // Get the Dcb exlcusive. This is important as cleanup does not + // acquire the Vcb. + // + + (VOID)FatAcquireExclusiveFcb( IrpContext, Dcb ); + DcbAcquired = TRUE; + + try { + + + *OplockPostIrp = FALSE; + + // + // Before spending any noticeable effort, see if we have the odd case + // of someone trying to delete-on-close the root dcb. This will only + // happen if we're hit with a null-filename relative open via the root. + // + + if (NodeType(Dcb) == FAT_NTC_ROOT_DCB && DeleteOnClose) { + + Iosb.Status = STATUS_CANNOT_DELETE; + try_return( Iosb ); + } + +#if (NTDDI_VERSION >= NTDDI_WIN8) + + // + // Let's make sure that if the caller provided an oplock key that it + // gets stored in the file object. + // + + Iosb.Status = FsRtlCheckOplockEx( FatGetFcbOplock(Dcb), + IrpContext->OriginatingIrp, + OPLOCK_FLAG_OPLOCK_KEY_CHECK_ONLY, + NULL, + NULL, + NULL ); + + if (Iosb.Status != STATUS_SUCCESS) { + + try_return( NOTHING ); + } + +#endif + // + // If the caller has no Ea knowledge, we immediately check for + // Need Ea's on the file. We don't need to check for ea's on the + // root directory, because it never has any. Fat32 doesn't have + // any, either. + // + + if (NoEaKnowledge && NodeType(Dcb) != FAT_NTC_ROOT_DCB && + !FatIsFat32(Vcb)) { + + ULONG NeedEaCount; + + // + // Get the dirent for the file and then check that the need + // ea count is 0. + // + + FatGetDirentFromFcbOrDcb( IrpContext, + Dcb, + FALSE, + &Dirent, + &DirentBcb ); + + FatGetNeedEaCount( IrpContext, + Vcb, + Dirent, + &NeedEaCount ); + + FatUnpinBcb( IrpContext, DirentBcb ); + + if (NeedEaCount != 0) { + + Iosb.Status = STATUS_ACCESS_DENIED; + try_return( Iosb ); + } + } + + // + // Check the create disposition and desired access + // + + if ((CreateDisposition != FILE_OPEN) && + (CreateDisposition != FILE_OPEN_IF)) { + + Iosb.Status = STATUS_OBJECT_NAME_COLLISION; + try_return( Iosb ); + } + + if (!FatCheckFileAccess( IrpContext, + Dcb->DirentFatFlags, + DesiredAccess)) { + + Iosb.Status = STATUS_ACCESS_DENIED; + try_return( Iosb ); + } + + // + // If the dcb is already opened by someone then we need + // to check the share access + // + + if (Dcb->OpenCount > 0) { + + if (!NT_SUCCESS(Iosb.Status = FatCheckShareAccess( IrpContext, + FileObject, + Dcb, + DesiredAccess, + ShareAccess ))) { +#if (NTDDI_VERSION >= NTDDI_WIN8) + + NTSTATUS OplockBreakStatus = STATUS_SUCCESS; + + // + // If we got a sharing violation try to break outstanding handle + // oplocks and retry the sharing check. If the caller specified + // FILE_COMPLETE_IF_OPLOCKED we don't bother breaking the oplock; + // we just return the sharing violation. + // + + if ((Iosb.Status == STATUS_SHARING_VIOLATION) && + !FlagOn( IrpSp->Parameters.Create.Options, FILE_COMPLETE_IF_OPLOCKED )) { + + OplockBreakStatus = FsRtlOplockBreakH( FatGetFcbOplock(Dcb), + IrpContext->OriginatingIrp, + 0, + IrpContext, + FatOplockComplete, + FatPrePostIrp ); + + // + // If FsRtlOplockBreakH returned STATUS_PENDING, then the IRP + // has been posted and we need to stop working. + // + + if (OplockBreakStatus == STATUS_PENDING) { + + Iosb.Status = STATUS_PENDING; + *OplockPostIrp = TRUE; + try_return( NOTHING ); + + // + // If FsRtlOplockBreakH returned an error we want to return that now. + // + + } else if (!NT_SUCCESS( OplockBreakStatus )) { + + Iosb.Status = OplockBreakStatus; + try_return( Iosb ); + + // + // Otherwise FsRtlOplockBreakH returned STATUS_SUCCESS, indicating + // that there is no oplock to be broken. The sharing violation is + // returned in that case. + // + + } else { + + NT_ASSERT( OplockBreakStatus == STATUS_SUCCESS ); + + try_return( Iosb ); + } + + // + // The initial sharing check failed with something other than sharing + // violation (which should never happen, but let's be future-proof), + // or we *did* get a sharing violation and the caller specified + // FILE_COMPLETE_IF_OPLOCKED. Either way this create is over. + // + + } else { + + try_return( Iosb ); + } +#else + + try_return( Iosb ); +#endif + } + } + +#if (NTDDI_VERSION >= NTDDI_WIN8) + + // + // Now check that we can continue based on the oplock state of the + // directory. If there are no open handles yet we don't need to do + // this check; oplocks can only exist when there are handles. + // + + if (Dcb->UncleanCount != 0) { + + Iosb.Status = FsRtlCheckOplock( FatGetFcbOplock(Dcb), + IrpContext->OriginatingIrp, + IrpContext, + FatOplockComplete, + FatPrePostIrp ); + } + + // + // if FsRtlCheckOplock returns STATUS_PENDING the IRP has been posted + // to service an oplock break and we need to leave now. + // + + if (Iosb.Status == STATUS_PENDING) { + + *OplockPostIrp = TRUE; + try_return( NOTHING ); + } + + // + // If the caller wants atomic create-with-oplock semantics, tell + // the oplock package. We haven't incremented the Fcb's UncleanCount + // for this create yet, so add that in on the call. + // + + if (OpenRequiringOplock && + (Iosb.Status == STATUS_SUCCESS)) { + + Iosb.Status = FsRtlOplockFsctrl( FatGetFcbOplock(Dcb), + IrpContext->OriginatingIrp, + (Dcb->UncleanCount + 1) ); + } + + // + // If we've encountered a failure we need to leave. FsRtlCheckOplock + // will have returned STATUS_OPLOCK_BREAK_IN_PROGRESS if it initiated + // and oplock break and the caller specified FILE_COMPLETE_IF_OPLOCKED + // on the create call. That's an NT_SUCCESS code, so we need to keep + // going. + // + + if ((Iosb.Status != STATUS_SUCCESS) && + (Iosb.Status != STATUS_OPLOCK_BREAK_IN_PROGRESS)) { + + try_return( NOTHING ); + } + +#endif + + // + // Now that we're done with the oplock work update the share counts. + // If the Dcb isn't yet opened we just set the share access rather than + // update it. + // + + if (Dcb->OpenCount > 0) { + + IoUpdateShareAccess( FileObject, &Dcb->ShareAccess ); + + } else { + + IoSetShareAccess( *DesiredAccess, + ShareAccess, + FileObject, + &Dcb->ShareAccess ); + } + + UnwindShareAccess = TRUE; + + // + // Setup the context and section object pointers, and update + // our reference counts + // + + FatSetFileObject( FileObject, + UserDirectoryOpen, + Dcb, + UnwindCcb = FatCreateCcb( IrpContext )); + + Dcb->UncleanCount += 1; + Dcb->OpenCount += 1; + Vcb->OpenFileCount += 1; + if (IsFileObjectReadOnly(FileObject)) { Vcb->ReadOnlyCount += 1; } + + // + // Mark the delete on close bit if the caller asked for that. + // + + { + PCCB Ccb = (PCCB)FileObject->FsContext2; + + + if (DeleteOnClose) { + + SetFlag( Ccb->Flags, CCB_FLAG_DELETE_ON_CLOSE ); + } + if (FileNameOpenedDos) { + + SetFlag( Ccb->Flags, CCB_FLAG_OPENED_BY_SHORTNAME ); + } + + } + + + // + // In case this was set, clear it now. + // + + ClearFlag(Dcb->FcbState, FCB_STATE_DELAY_CLOSE); + + // + // And set our status to success + // + + Iosb.Status = STATUS_SUCCESS; + Iosb.Information = FILE_OPENED; + + try_exit: NOTHING; + } finally { + + DebugUnwind( FatOpenExistingDcb ); + + // + // Unpin the Dirent Bcb if pinned. + // + + FatUnpinBcb( IrpContext, DirentBcb ); + + // + // If this is an abnormal termination then undo our work + // + + if (AbnormalTermination()) { + + if (UnwindCcb != NULL) { FatDeleteCcb( IrpContext, &UnwindCcb ); } + if (UnwindShareAccess) { IoRemoveShareAccess( FileObject, &Dcb->ShareAccess ); } + } + + if (DcbAcquired) { + + FatReleaseFcb( IrpContext, Dcb ); + } + + DebugTrace(-1, Dbg, "FatOpenExistingDcb -> Iosb.Status = %08lx\n", Iosb.Status); + } + + return Iosb; +} + + +// +// Internal support routine +// + +_Requires_lock_held_(_Global_critical_region_) +IO_STATUS_BLOCK +FatOpenExistingFcb ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PIO_STACK_LOCATION IrpSp, + _Inout_ PFILE_OBJECT FileObject, + _Inout_ PVCB Vcb, + _Inout_ PFCB Fcb, + _In_ PACCESS_MASK DesiredAccess, + _In_ USHORT ShareAccess, + _In_ ULONG AllocationSize, + _In_ PFILE_FULL_EA_INFORMATION EaBuffer, + _In_ ULONG EaLength, + _In_ UCHAR FileAttributes, + _In_ ULONG CreateDisposition, + _In_ BOOLEAN NoEaKnowledge, + _In_ BOOLEAN DeleteOnClose, + _In_ BOOLEAN OpenRequiringOplock, + _In_ BOOLEAN FileNameOpenedDos, + _Out_ PBOOLEAN OplockPostIrp + ) + +/*++ + +Routine Description: + + This routine opens the specified existing fcb + +Arguments: + + FileObject - Supplies the File object + + Vcb - Supplies the Vcb denoting the volume containing the Fcb + + Fcb - Supplies the already existing fcb + + DesiredAccess - Supplies the desired access of the caller + + ShareAccess - Supplies the share access of the caller + + AllocationSize - Supplies the initial allocation if the file is being + superseded or overwritten + + EaBuffer - Supplies the Ea set if the file is being superseded or + overwritten + + EaLength - Supplies the size, in byte, of the EaBuffer + + FileAttributes - Supplies file attributes to use if the file is being + superseded or overwritten + + CreateDisposition - Supplies the create disposition for this operation + + NoEaKnowledge - This opener doesn't understand Ea's and we fail this + open if the file has NeedEa's. + + DeleteOnClose - The caller wants the file gone when the handle is closed + + OpenRequiringOplock - The caller provided the FILE_OPEN_REQUIRING_OPLOCK option. + + FileNameOpenedDos - The caller hit the short side of the name pair finding + this file + + OplockPostIrp - Address to store boolean indicating if the Irp needs to + be posted to the Fsp. + +Return Value: + + IO_STATUS_BLOCK - Returns the completion status for the operation + +--*/ + +{ + IO_STATUS_BLOCK Iosb = {0}; + + PBCB DirentBcb = NULL; + PDIRENT Dirent; + + ACCESS_MASK AddedAccess = 0; + + // + // The following variables are for abnormal termination + // + + BOOLEAN UnwindShareAccess = FALSE; + PCCB UnwindCcb = NULL; + BOOLEAN DecrementFcbOpenCount = FALSE; + BOOLEAN FcbAcquired = FALSE; + + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatOpenExistingFcb...\n", 0); + + // + // Get the Fcb exlcusive. This is important as cleanup does not + // acquire the Vcb. + // + + (VOID)FatAcquireExclusiveFcb( IrpContext, Fcb ); + FcbAcquired = TRUE; + + try { + + + *OplockPostIrp = FALSE; + +#if (NTDDI_VERSION >= NTDDI_WIN7) + + // + // Let's make sure that if the caller provided an oplock key that it + // gets stored in the file object. + // + + Iosb.Status = FsRtlCheckOplockEx( FatGetFcbOplock(Fcb), + IrpContext->OriginatingIrp, + OPLOCK_FLAG_OPLOCK_KEY_CHECK_ONLY, + NULL, + NULL, + NULL ); + + if (Iosb.Status != STATUS_SUCCESS) { + + try_return( NOTHING ); + } +#endif + + // + // Take special action if there is a current batch oplock or + // batch oplock break in process on the Fcb. + // + + if (FsRtlCurrentBatchOplock( FatGetFcbOplock(Fcb) )) { + + // + // We remember if a batch oplock break is underway for the + // case where the sharing check fails. + // + + Iosb.Information = FILE_OPBATCH_BREAK_UNDERWAY; + + Iosb.Status = FsRtlCheckOplock( FatGetFcbOplock(Fcb), + IrpContext->OriginatingIrp, + IrpContext, + FatOplockComplete, + FatPrePostIrp ); + + // + // if FsRtlCheckOplock returns STATUS_PENDING the IRP has been posted + // to service an oplock break and we need to leave now. + // + + if (Iosb.Status == STATUS_PENDING) { + + *OplockPostIrp = TRUE; + try_return( NOTHING ); + } + } + + // + // Check if the user wanted to create the file, also special case + // the supersede and overwrite options. Those add additional, + // possibly only implied, desired accesses to the caller, which + // we must be careful to pull back off if the caller did not actually + // request them. + // + // In other words, check against the implied access, but do not modify + // share access as a result. + // + + if (CreateDisposition == FILE_CREATE) { + + Iosb.Status = STATUS_OBJECT_NAME_COLLISION; + try_return( Iosb ); + + } else if (CreateDisposition == FILE_SUPERSEDE) { + + SetFlag( AddedAccess, + DELETE & ~(*DesiredAccess) ); + + *DesiredAccess |= DELETE; + + } else if ((CreateDisposition == FILE_OVERWRITE) || + (CreateDisposition == FILE_OVERWRITE_IF)) { + + SetFlag( AddedAccess, + (FILE_WRITE_DATA | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES) & ~(*DesiredAccess) ); + + *DesiredAccess |= FILE_WRITE_DATA | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES; + } + + // + // Check the desired access + // + + if (!FatCheckFileAccess( IrpContext, + Fcb->DirentFatFlags, + DesiredAccess )) { + + Iosb.Status = STATUS_ACCESS_DENIED; + try_return( Iosb ); + } + + + // + // Check for trying to delete a read only file. + // + + if (DeleteOnClose && + FlagOn( Fcb->DirentFatFlags, FAT_DIRENT_ATTR_READ_ONLY )) { + + Iosb.Status = STATUS_CANNOT_DELETE; + try_return( Iosb ); + } + + // + // If we are asked to do an overwrite or supersede operation then + // deny access for files where the file attributes for system and + // hidden do not match + // + + if ((CreateDisposition == FILE_SUPERSEDE) || + (CreateDisposition == FILE_OVERWRITE) || + (CreateDisposition == FILE_OVERWRITE_IF)) { + + BOOLEAN Hidden; + BOOLEAN System; + + Hidden = BooleanFlagOn(Fcb->DirentFatFlags, FAT_DIRENT_ATTR_HIDDEN ); + System = BooleanFlagOn(Fcb->DirentFatFlags, FAT_DIRENT_ATTR_SYSTEM ); + + if ((Hidden && !FlagOn(FileAttributes, FILE_ATTRIBUTE_HIDDEN)) || + (System && !FlagOn(FileAttributes, FILE_ATTRIBUTE_SYSTEM))) { + + DebugTrace(0, Dbg, "The hidden and/or system bits do not match\n", 0); + + + Iosb.Status = STATUS_ACCESS_DENIED; + try_return( Iosb ); + } + + // + // If this media is write protected, don't even try the create. + // + + if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED)) { + + // + // Set the real device for the pop-up info, and set the verify + // bit in the device object, so that we will force a verify + // in case the user put the correct media back in. + // + + IoSetHardErrorOrVerifyDevice( IrpContext->OriginatingIrp, + Vcb->Vpb->RealDevice ); + + SetFlag(Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME); + + FatRaiseStatus( IrpContext, STATUS_MEDIA_WRITE_PROTECTED ); + } + } + + // + // Check if the Fcb has the proper share access. This routine will also + // check for writable user secions if the user did not allow write sharing. + // + + if (!NT_SUCCESS(Iosb.Status = FatCheckShareAccess( IrpContext, + FileObject, + Fcb, + DesiredAccess, + ShareAccess ))) { + +#if (NTDDI_VERSION >= NTDDI_WIN7) + + NTSTATUS OplockBreakStatus = STATUS_SUCCESS; + + // + // If we got a sharing violation try to break outstanding handle + // oplocks and retry the sharing check. If the caller specified + // FILE_COMPLETE_IF_OPLOCKED we don't bother breaking the oplock; + // we just return the sharing violation. + // + + if ((Iosb.Status == STATUS_SHARING_VIOLATION) && + !FlagOn( IrpSp->Parameters.Create.Options, FILE_COMPLETE_IF_OPLOCKED )) { + + OplockBreakStatus = FsRtlOplockBreakH( FatGetFcbOplock(Fcb), + IrpContext->OriginatingIrp, + 0, + IrpContext, + FatOplockComplete, + FatPrePostIrp ); + + // + // If FsRtlOplockBreakH returned STATUS_PENDING, then the IRP + // has been posted and we need to stop working. + // + + if (OplockBreakStatus == STATUS_PENDING) { + + Iosb.Status = STATUS_PENDING; + *OplockPostIrp = TRUE; + try_return( NOTHING ); + + // + // If FsRtlOplockBreakH returned an error we want to return that now. + // + + } else if (!NT_SUCCESS( OplockBreakStatus )) { + + Iosb.Status = OplockBreakStatus; + try_return( Iosb ); + + // + // Otherwise FsRtlOplockBreakH returned STATUS_SUCCESS, indicating + // that there is no oplock to be broken. The sharing violation is + // returned in that case. + // + + } else { + + NT_ASSERT( OplockBreakStatus == STATUS_SUCCESS ); + + try_return( Iosb ); + } + + // + // The initial sharing check failed with something other than sharing + // violation (which should never happen, but let's be future-proof), + // or we *did* get a sharing violation and the caller specified + // FILE_COMPLETE_IF_OPLOCKED. Either way this create is over. + // + + } else { + + try_return( Iosb ); + } + +#else + + try_return( Iosb ); + +#endif + } + + // + // Now check that we can continue based on the oplock state of the + // file. If there are no open handles yet we don't need to do this + // check; oplocks can only exist when there are handles. + // + // It is important that we modified the DesiredAccess in place so + // that the Oplock check proceeds against any added access we had + // to give the caller. + // + + if (Fcb->UncleanCount != 0) { + + Iosb.Status = FsRtlCheckOplock( FatGetFcbOplock(Fcb), + IrpContext->OriginatingIrp, + IrpContext, + FatOplockComplete, + FatPrePostIrp ); + } + + // + // if FsRtlCheckOplock returns STATUS_PENDING the IRP has been posted + // to service an oplock break and we need to leave now. + // + + if (Iosb.Status == STATUS_PENDING) { + + *OplockPostIrp = TRUE; + try_return( NOTHING ); + } + + // + // If the caller wants atomic create-with-oplock semantics, tell + // the oplock package. We haven't incremented the Fcb's UncleanCount + // for this create yet, so add that in on the call. + // + + if (OpenRequiringOplock && + (Iosb.Status == STATUS_SUCCESS)) { + + Iosb.Status = FsRtlOplockFsctrl( FatGetFcbOplock(Fcb), + IrpContext->OriginatingIrp, + (Fcb->UncleanCount + 1) ); + } + + // + // If we've encountered a failure we need to leave. FsRtlCheckOplock + // will have returned STATUS_OPLOCK_BREAK_IN_PROGRESS if it initiated + // and oplock break and the caller specified FILE_COMPLETE_IF_OPLOCKED + // on the create call. That's an NT_SUCCESS code, so we need to keep + // going. + // + + if ((Iosb.Status != STATUS_SUCCESS) && + (Iosb.Status != STATUS_OPLOCK_BREAK_IN_PROGRESS)) { + + try_return( NOTHING ); + } + + // + // Set the flag indicating if Fast I/O is possible + // + + Fcb->Header.IsFastIoPossible = FatIsFastIoPossible( Fcb ); + + // + // If the user wants write access access to the file make sure there + // is not a process mapping this file as an image. Any attempt to + // delete the file will be stopped in fileinfo.c + // + // If the user wants to delete on close, we must check at this + // point though. + // + + if (FlagOn(*DesiredAccess, FILE_WRITE_DATA) || DeleteOnClose) { + + Fcb->OpenCount += 1; + DecrementFcbOpenCount = TRUE; + + if (!MmFlushImageSection( &Fcb->NonPaged->SectionObjectPointers, + MmFlushForWrite )) { + + Iosb.Status = DeleteOnClose ? STATUS_CANNOT_DELETE : + STATUS_SHARING_VIOLATION; + try_return( Iosb ); + } + } + + // + // If this is a non-cached open on a non-paging file, and there + // are no open cached handles, but there is a still a data + // section, attempt a flush and purge operation to avoid cache + // coherency overhead later. We ignore any I/O errors from + // the flush. + // + // We set the CREATE_IN_PROGRESS flag to prevent the Fcb from + // going away out from underneath us. + // + + if (FlagOn( FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING ) && + (Fcb->UncleanCount == Fcb->NonCachedUncleanCount) && + (Fcb->NonPaged->SectionObjectPointers.DataSectionObject != NULL) && + !FlagOn( Fcb->FcbState, FCB_STATE_PAGING_FILE )) { + + SetFlag(Fcb->Vcb->VcbState, VCB_STATE_FLAG_CREATE_IN_PROGRESS); + + CcFlushCache( &Fcb->NonPaged->SectionObjectPointers, NULL, 0, NULL ); + + // + // Grab and release PagingIo to serialize ourselves with the lazy writer. + // This will work to ensure that all IO has completed on the cached + // data and we will succesfully tear away the cache section. + // + + ExAcquireResourceExclusiveLite( Fcb->Header.PagingIoResource, TRUE); + ExReleaseResourceLite( Fcb->Header.PagingIoResource ); + + CcPurgeCacheSection( &Fcb->NonPaged->SectionObjectPointers, + NULL, + 0, + FALSE ); + + ClearFlag(Fcb->Vcb->VcbState, VCB_STATE_FLAG_CREATE_IN_PROGRESS); + } + + // + // Check if the user only wanted to open the file + // + + if ((CreateDisposition == FILE_OPEN) || + (CreateDisposition == FILE_OPEN_IF)) { + + DebugTrace(0, Dbg, "Doing open operation\n", 0); + + // + // If the caller has no Ea knowledge, we immediately check for + // Need Ea's on the file. + // + + if (NoEaKnowledge && !FatIsFat32(Vcb)) { + + ULONG NeedEaCount; + + // + // Get the dirent for the file and then check that the need + // ea count is 0. + // + + FatGetDirentFromFcbOrDcb( IrpContext, + Fcb, + FALSE, + &Dirent, + &DirentBcb ); + + FatGetNeedEaCount( IrpContext, + Vcb, + Dirent, + &NeedEaCount ); + + FatUnpinBcb( IrpContext, DirentBcb ); + + if (NeedEaCount != 0) { + + Iosb.Status = STATUS_ACCESS_DENIED; + try_return( Iosb ); + } + } + + // + // Everything checks out okay, so setup the context and + // section object pointers. + // + + FatSetFileObject( FileObject, + UserFileOpen, + Fcb, + UnwindCcb = FatCreateCcb( IrpContext )); + + FileObject->SectionObjectPointer = &Fcb->NonPaged->SectionObjectPointers; + + // + // Fill in the information field, the status field is already + // set. + // + + Iosb.Information = FILE_OPENED; + + try_return( Iosb ); + } + + // + // Check if we are to supersede/overwrite the file, we can wait for + // any I/O at this point + // + + if ((CreateDisposition == FILE_SUPERSEDE) || + (CreateDisposition == FILE_OVERWRITE) || + (CreateDisposition == FILE_OVERWRITE_IF)) { + + NTSTATUS OldStatus; + + DebugTrace(0, Dbg, "Doing supersede/overwrite operation\n", 0); + + // + // We remember the previous status code because it may contain + // information about the oplock status. + // + + OldStatus = Iosb.Status; + + // + // Determine the granted access for this operation now. + // + + if (!NT_SUCCESS( Iosb.Status = FatCheckSystemSecurityAccess( IrpContext ))) { + + try_return( Iosb ); + } + + // + // And overwrite the file. + // + + Iosb = FatSupersedeOrOverwriteFile( IrpContext, + FileObject, + Fcb, + AllocationSize, + EaBuffer, + EaLength, + FileAttributes, + CreateDisposition, + NoEaKnowledge ); + + if (Iosb.Status == STATUS_SUCCESS) { + + Iosb.Status = OldStatus; + } + + try_return( Iosb ); + } + + // + // If we ever get here then the I/O system gave us some bad input + // + +#pragma prefast( suppress:28159, "things are seriously wrong if we get here" ) + FatBugCheck( CreateDisposition, 0, 0 ); + + try_exit: NOTHING; + + // + // Update the share access and counts if successful + // + + if ((Iosb.Status != STATUS_PENDING) && NT_SUCCESS(Iosb.Status)) { + + // + // Now, we may have added some access bits above to indicate the access + // this caller would conflict with (as opposed to what they get) in order + // to perform the overwrite/supersede. We need to make a call to that will + // recalculate the bits in the fileobject to reflect the real access they + // will get. + // + + if (AddedAccess) { + + NTSTATUS Status; + + ClearFlag( *DesiredAccess, AddedAccess ); + +#pragma prefast( suppress:28931, "it needs to be there for debug assert" ); + Status = IoCheckShareAccess( *DesiredAccess, + ShareAccess, + FileObject, + &Fcb->ShareAccess, + TRUE ); + + // + // It must be the case that we are really asking for less access, so + // any conflict must have been detected before this point. + // + + NT_ASSERT( Status == STATUS_SUCCESS ); + + } else { + + IoUpdateShareAccess( FileObject, &Fcb->ShareAccess ); + } + + UnwindShareAccess = TRUE; + + // + // In case this was set, clear it now. + // + + ClearFlag(Fcb->FcbState, FCB_STATE_DELAY_CLOSE); + + Fcb->UncleanCount += 1; + Fcb->OpenCount += 1; + if (FlagOn(FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING)) { + Fcb->NonCachedUncleanCount += 1; + } + Vcb->OpenFileCount += 1; + if (IsFileObjectReadOnly(FileObject)) { Vcb->ReadOnlyCount += 1; } + + { + PCCB Ccb; + + Ccb = (PCCB)FileObject->FsContext2; + + // + // Mark the DeleteOnClose bit if the operation was successful. + // + + if ( DeleteOnClose ) { + + SetFlag( Ccb->Flags, CCB_FLAG_DELETE_ON_CLOSE ); + } + + // + // Mark the OpenedByShortName bit if the operation was successful. + // + + if ( FileNameOpenedDos ) { + + SetFlag( Ccb->Flags, CCB_FLAG_OPENED_BY_SHORTNAME ); + } + + // + // Mark the ManageVolumeAccess bit if the privilege is held. + // + + if (FatCheckManageVolumeAccess( IrpContext, + IrpSp->Parameters.Create.SecurityContext->AccessState, + (KPROCESSOR_MODE)( FlagOn( IrpSp->Flags, SL_FORCE_ACCESS_CHECK ) ? + UserMode : + IrpContext->OriginatingIrp->RequestorMode ))) { + + SetFlag( Ccb->Flags, CCB_FLAG_MANAGE_VOLUME_ACCESS ); + } + } + + + } + + } finally { + + DebugUnwind( FatOpenExistingFcb ); + + // + // Unpin the Dirent Bcb if pinned. + // + + FatUnpinBcb( IrpContext, DirentBcb ); + + // + // If this is an abnormal termination then undo our work + // + + if (AbnormalTermination()) { + + if (UnwindCcb != NULL) { FatDeleteCcb( IrpContext, &UnwindCcb ); } + if (UnwindShareAccess) { IoRemoveShareAccess( FileObject, &Fcb->ShareAccess ); } + } + + if (DecrementFcbOpenCount) { + + Fcb->OpenCount -= 1; + + if (Fcb->OpenCount == 0) { + if (ARGUMENT_PRESENT( FileObject )) { + FileObject->SectionObjectPointer = NULL; + } + FatDeleteFcb( IrpContext, &Fcb ); + FcbAcquired = FALSE; + } + } + + if (FcbAcquired) { + + FatReleaseFcb( IrpContext, Fcb ); + } + + DebugTrace(-1, Dbg, "FatOpenExistingFcb -> Iosb.Status = %08lx\n", Iosb.Status); + } + + return Iosb; +} + +// +// Internal support routine +// + +_Requires_lock_held_(_Global_critical_region_) +IO_STATUS_BLOCK +FatOpenTargetDirectory ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PFILE_OBJECT FileObject, + _Inout_ PDCB Dcb, + _In_ PACCESS_MASK DesiredAccess, + _In_ USHORT ShareAccess, + _In_ BOOLEAN DoesNameExist, + _In_ BOOLEAN FileNameOpenedDos + ) + +/*++ + +Routine Description: + + This routine opens the target directory and replaces the name in the + file object with the remaining name. + +Arguments: + + FileObject - Supplies the File object + + Dcb - Supplies an already existing dcb that we are going to open + + DesiredAccess - Supplies the desired access of the caller + + ShareAccess - Supplies the share access of the caller + + DoesNameExist - Indicates if the file name already exists in the + target directory. + + +Return Value: + + IO_STATUS_BLOCK - Returns the completion status for the operation + +--*/ + +{ + IO_STATUS_BLOCK Iosb = {0}; + + // + // The following variables are for abnormal termination + // + + BOOLEAN UnwindShareAccess = FALSE; + PCCB UnwindCcb = NULL; + BOOLEAN DcbAcquired = FALSE; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatOpenTargetDirectory...\n", 0); + + // + // Get the Dcb exlcusive. This is important as cleanup does not + // acquire the Vcb. + // + + (VOID)FatAcquireExclusiveFcb( IrpContext, Dcb ); + DcbAcquired = TRUE; + + try { + + ULONG i; + + // + // If the Dcb is already opened by someone then we need + // to check the share access + // + + if (Dcb->OpenCount > 0) { + + if (!NT_SUCCESS(Iosb.Status = IoCheckShareAccess( *DesiredAccess, + ShareAccess, + FileObject, + &Dcb->ShareAccess, + TRUE ))) { + + try_return( Iosb ); + } + + } else { + + IoSetShareAccess( *DesiredAccess, + ShareAccess, + FileObject, + &Dcb->ShareAccess ); + } + + UnwindShareAccess = TRUE; + + // + // Setup the context and section object pointers, and update + // our reference counts + // + + FatSetFileObject( FileObject, + UserDirectoryOpen, + Dcb, + UnwindCcb = FatCreateCcb( IrpContext )); + + Dcb->UncleanCount += 1; + Dcb->OpenCount += 1; + Dcb->Vcb->OpenFileCount += 1; + if (IsFileObjectReadOnly(FileObject)) { Dcb->Vcb->ReadOnlyCount += 1; } + + // + // Update the name in the file object, by definition the remaining + // part must be shorter than the original file name so we'll just + // overwrite the file name. + // + + i = FileObject->FileName.Length/sizeof(WCHAR) - 1; + + // + // Get rid of a trailing backslash + // + + if (FileObject->FileName.Buffer[i] == L'\\') { + + NT_ASSERT(i != 0); + + FileObject->FileName.Length -= sizeof(WCHAR); + i -= 1; + } + + // + // Find the first non-backslash character. i will be its index. + // + + while (TRUE) { + + if (FileObject->FileName.Buffer[i] == L'\\') { + + i += 1; + break; + } + + if (i == 0) { + break; + } + + i--; + } + + if (i) { + + FileObject->FileName.Length -= (USHORT)(i * sizeof(WCHAR)); + + RtlMoveMemory( &FileObject->FileName.Buffer[0], + &FileObject->FileName.Buffer[i], + FileObject->FileName.Length ); + } + + // + // And set our status to success + // + + Iosb.Status = STATUS_SUCCESS; + Iosb.Information = (DoesNameExist ? FILE_EXISTS : FILE_DOES_NOT_EXIST); + + if ( ( NT_SUCCESS(Iosb.Status) ) && ( DoesNameExist ) ) { + PCCB Ccb; + + Ccb = (PCCB)FileObject->FsContext2; + + // + // Mark the OpenedByShortName bit if the operation was successful. + // + + if ( FileNameOpenedDos ) { + + SetFlag( Ccb->Flags, CCB_FLAG_OPENED_BY_SHORTNAME ); + } + } + + try_exit: NOTHING; + } finally { + + DebugUnwind( FatOpenTargetDirectory ); + + // + // If this is an abnormal termination then undo our work + // + + if (AbnormalTermination()) { + + if (UnwindCcb != NULL) { FatDeleteCcb( IrpContext, &UnwindCcb ); } + if (UnwindShareAccess) { IoRemoveShareAccess( FileObject, &Dcb->ShareAccess ); } + } + + if (DcbAcquired) { + + FatReleaseFcb( IrpContext, Dcb ); + } + + DebugTrace(-1, Dbg, "FatOpenTargetDirectory -> Iosb.Status = %08lx\n", Iosb.Status); + } + + return Iosb; +} + + + +// +// Internal support routine +// +_Success_(return.Status == STATUS_SUCCESS) +_Requires_lock_held_(_Global_critical_region_) +IO_STATUS_BLOCK +#pragma warning(suppress:6101) // bug in PREFast means the _Success_ annotation is not correctly applied +FatOpenExistingDirectory ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PIO_STACK_LOCATION IrpSp, + _Inout_ PFILE_OBJECT FileObject, + _Inout_ PVCB Vcb, + _Outptr_result_maybenull_ PDCB *Dcb, + _In_ PDCB ParentDcb, + _In_ PDIRENT Dirent, + _In_ ULONG LfnByteOffset, + _In_ ULONG DirentByteOffset, + _In_ PUNICODE_STRING Lfn, + _In_ PACCESS_MASK DesiredAccess, + _In_ USHORT ShareAccess, + _In_ ULONG CreateDisposition, + _In_ BOOLEAN NoEaKnowledge, + _In_ BOOLEAN DeleteOnClose, + _In_ BOOLEAN FileNameOpenedDos, + _In_ BOOLEAN OpenRequiringOplock + ) + +/*++ + +Routine Description: + + This routine opens the specified directory. The directory has not + previously been opened. + +Arguments: + + FileObject - Supplies the File object + + Vcb - Supplies the Vcb denoting the volume containing the dcb + + Dcb - Returns the newly-created DCB for the file. + + ParentDcb - Supplies the parent directory containing the subdirectory + to be opened + + DirectoryName - Supplies the file name of the directory being opened. + + Dirent - Supplies the dirent for the directory being opened + + LfnByteOffset - Tells where the Lfn begins. If there is no Lfn + this field is the same as DirentByteOffset. + + DirentByteOffset - Supplies the Vbo of the dirent within its parent + directory + + Lfn - May supply a long name for the file. + + DesiredAccess - Supplies the desired access of the caller + + ShareAccess - Supplies the share access of the caller + + CreateDisposition - Supplies the create disposition for this operation + + NoEaKnowledge - This opener doesn't understand Ea's and we fail this + open if the file has NeedEa's. + + DeleteOnClose - The caller wants the file gone when the handle is closed + + OpenRequiringOplock - The caller provided the FILE_OPEN_REQUIRING_OPLOCK option. + +Return Value: + + IO_STATUS_BLOCK - Returns the completion status for the operation + +--*/ + +{ + IO_STATUS_BLOCK Iosb = {0}; + + // + // The following variables are for abnormal termination + // + + PDCB UnwindDcb = NULL; + PCCB UnwindCcb = NULL; + + BOOLEAN CountsIncremented = FALSE; + + UNREFERENCED_PARAMETER( DeleteOnClose ); +#if (NTDDI_VERSION <= NTDDI_WIN7) + UNREFERENCED_PARAMETER( OpenRequiringOplock ); +#endif + UNREFERENCED_PARAMETER( IrpSp ); + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatOpenExistingDirectory...\n", 0); + + try { + + // + // If the caller has no Ea knowledge, we immediately check for + // Need Ea's on the file. + // + + if (NoEaKnowledge && !FatIsFat32(Vcb)) { + + ULONG NeedEaCount; + + FatGetNeedEaCount( IrpContext, + Vcb, + Dirent, + &NeedEaCount ); + + if (NeedEaCount != 0) { + + Iosb.Status = STATUS_ACCESS_DENIED; + try_return( Iosb ); + } + } + + // + // Check the create disposition and desired access + // + + if ((CreateDisposition != FILE_OPEN) && + (CreateDisposition != FILE_OPEN_IF)) { + + Iosb.Status = STATUS_OBJECT_NAME_COLLISION; + try_return( Iosb ); + } + + if (!FatCheckFileAccess( IrpContext, + Dirent->Attributes, + DesiredAccess)) { + + Iosb.Status = STATUS_ACCESS_DENIED; + try_return( Iosb ); + } + + // + // Create a new dcb for the directory + // + + *Dcb = UnwindDcb = FatCreateDcb( IrpContext, + Vcb, + ParentDcb, + LfnByteOffset, + DirentByteOffset, + Dirent, + Lfn ); + +#if (NTDDI_VERSION >= NTDDI_WIN8) + + // + // Let's make sure that if the caller provided an oplock key that it + // gets stored in the file object. + // + + Iosb.Status = FsRtlCheckOplockEx( FatGetFcbOplock(*Dcb), + IrpContext->OriginatingIrp, + OPLOCK_FLAG_OPLOCK_KEY_CHECK_ONLY, + NULL, + NULL, + NULL ); + + // + // If the caller wants atomic create-with-oplock semantics, tell + // the oplock package. We haven't incremented the Fcb's UncleanCount + // for this create yet, so add that in on the call. + // + + if (OpenRequiringOplock && + (Iosb.Status == STATUS_SUCCESS)) { + + Iosb.Status = FsRtlOplockFsctrl( FatGetFcbOplock(*Dcb), + IrpContext->OriginatingIrp, + ((*Dcb)->UncleanCount + 1) ); + } + + // + // Get out if either of the above calls failed. Raise to trigger + // cleanup of the new Dcb. + // + + if (Iosb.Status != STATUS_SUCCESS) { + + NT_ASSERT( Iosb.Status != STATUS_PENDING ); + + FatRaiseStatus( IrpContext, Iosb.Status ); + } +#endif + + // + // Setup our share access + // + + IoSetShareAccess( *DesiredAccess, + ShareAccess, + FileObject, + &(*Dcb)->ShareAccess ); + + // + // Setup the context and section object pointers, and update + // our reference counts + // + + FatSetFileObject( FileObject, + UserDirectoryOpen, + (*Dcb), + UnwindCcb = FatCreateCcb( IrpContext )); + + (*Dcb)->UncleanCount += 1; + (*Dcb)->OpenCount += 1; + Vcb->OpenFileCount += 1; + if (IsFileObjectReadOnly(FileObject)) { Vcb->ReadOnlyCount += 1; } + + CountsIncremented = TRUE; + + + // + // And set our status to success + // + + Iosb.Status = STATUS_SUCCESS; + Iosb.Information = FILE_OPENED; + + if ( NT_SUCCESS(Iosb.Status) ) { + PCCB Ccb; + + Ccb = (PCCB)FileObject->FsContext2; + + // + // Mark the OpenedByShortName bit if the operation was successful. + // + + if ( FileNameOpenedDos ) { + + SetFlag( Ccb->Flags, CCB_FLAG_OPENED_BY_SHORTNAME ); + } + } + + try_exit: NOTHING; + } finally { + + DebugUnwind( FatOpenExistingDirectory ); + + // + // If this is an abnormal termination then undo our work + // + + if (AbnormalTermination()) { + + if (CountsIncremented) { + + (*Dcb)->UncleanCount -= 1; + (*Dcb)->OpenCount -= 1; + Vcb->OpenFileCount -= 1; + if (IsFileObjectReadOnly(FileObject)) { Vcb->ReadOnlyCount -= 1; } + } + + if (UnwindDcb != NULL) { + if (ARGUMENT_PRESENT( FileObject )) { + FileObject->SectionObjectPointer = NULL; + } + FatDeleteFcb( IrpContext, &UnwindDcb ); + *Dcb = NULL; + } + if (UnwindCcb != NULL) { FatDeleteCcb( IrpContext, &UnwindCcb ); } + } + + DebugTrace(-1, Dbg, "FatOpenExistingDirectory -> Iosb.Status = %08lx\n", Iosb.Status); + } + + return Iosb; +} + + +// +// Internal support routine +// + +_Requires_lock_held_(_Global_critical_region_) +IO_STATUS_BLOCK +FatOpenExistingFile ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PFILE_OBJECT FileObject, + _Inout_ PVCB Vcb, + _Outptr_result_maybenull_ PFCB *Fcb, + _In_ PDCB ParentDcb, + _In_ PDIRENT Dirent, + _In_ ULONG LfnByteOffset, + _In_ ULONG DirentByteOffset, + _In_ PUNICODE_STRING Lfn, + _In_ PACCESS_MASK DesiredAccess, + _In_ USHORT ShareAccess, + _In_ ULONG AllocationSize, + _In_ PFILE_FULL_EA_INFORMATION EaBuffer, + _In_ ULONG EaLength, + _In_ UCHAR FileAttributes, + _In_ ULONG CreateDisposition, + _In_ BOOLEAN IsPagingFile, + _In_ BOOLEAN NoEaKnowledge, + _In_ BOOLEAN DeleteOnClose, + _In_ BOOLEAN OpenRequiringOplock, + _In_ BOOLEAN FileNameOpenedDos + ) + +/*++ + +Routine Description: + + This routine opens the specified file. The file has not previously + been opened. + +Arguments: + + FileObject - Supplies the File object + + Vcb - Supplies the Vcb denoting the volume containing the file + + Fcb - Returns the newly-created FCB for the file. + + ParentDcb - Supplies the parent directory containing the file to be + opened + + Dirent - Supplies the dirent for the file being opened + + LfnByteOffset - Tells where the Lfn begins. If there is no Lfn + this field is the same as DirentByteOffset. + + DirentByteOffset - Supplies the Vbo of the dirent within its parent + directory + + Lfn - May supply a long name for the file. + + DesiredAccess - Supplies the desired access of the caller + + ShareAccess - Supplies the share access of the caller + + AllocationSize - Supplies the initial allocation if the file is being + superseded, overwritten, or created. + + EaBuffer - Supplies the Ea set if the file is being superseded, + overwritten, or created. + + EaLength - Supplies the size, in byte, of the EaBuffer + + FileAttributes - Supplies file attributes to use if the file is being + superseded, overwritten, or created + + CreateDisposition - Supplies the create disposition for this operation + + IsPagingFile - Indicates if this is the paging file being opened. + + NoEaKnowledge - This opener doesn't understand Ea's and we fail this + open if the file has NeedEa's. + + DeleteOnClose - The caller wants the file gone when the handle is closed + + OpenRequiringOplock - The caller provided the FILE_OPEN_REQUIRING_OPLOCK option. + + FileNameOpenedDos - The caller opened this file by hitting the 8.3 side + of the Lfn/8.3 pair + +Return Value: + + IO_STATUS_BLOCK - Returns the completion status for the operation + +--*/ + +{ + IO_STATUS_BLOCK Iosb = {0}; + + ACCESS_MASK AddedAccess = 0; + + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( IrpContext->OriginatingIrp ); + + // + // The following variables are for abnormal termination + // + + PFCB UnwindFcb = NULL; + PCCB UnwindCcb = NULL; + BOOLEAN CountsIncremented = FALSE; + + +#if (NTDDI_VERSION < NTDDI_WIN7) + UNREFERENCED_PARAMETER( OpenRequiringOplock ); +#endif + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatOpenExistingFile...\n", 0); + + try { + + // + // Check if the user wanted to create the file or if access is + // denied + // + + if (CreateDisposition == FILE_CREATE) { + Iosb.Status = STATUS_OBJECT_NAME_COLLISION; + try_return( Iosb ); + + } else if ((CreateDisposition == FILE_SUPERSEDE) && !IsPagingFile) { + + SetFlag( AddedAccess, + DELETE & ~(*DesiredAccess) ); + + *DesiredAccess |= DELETE; + + } else if (((CreateDisposition == FILE_OVERWRITE) || + (CreateDisposition == FILE_OVERWRITE_IF)) && !IsPagingFile) { + + SetFlag( AddedAccess, + (FILE_WRITE_DATA | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES) & ~(*DesiredAccess) ); + + *DesiredAccess |= FILE_WRITE_DATA | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES; + } + + if (!FatCheckFileAccess( IrpContext, + Dirent->Attributes, + DesiredAccess)) { + + Iosb.Status = STATUS_ACCESS_DENIED; + try_return( Iosb ); + } + + + // + // Check for trying to delete a read only file. + // + + if (DeleteOnClose && + FlagOn( Dirent->Attributes, FAT_DIRENT_ATTR_READ_ONLY )) { + + Iosb.Status = STATUS_CANNOT_DELETE; + try_return( Iosb ); + } + + // + // IF we are asked to do an overwrite or supersede operation then + // deny access for files where the file attributes for system and + // hidden do not match + // + + if ((CreateDisposition == FILE_SUPERSEDE) || + (CreateDisposition == FILE_OVERWRITE) || + (CreateDisposition == FILE_OVERWRITE_IF)) { + + BOOLEAN Hidden; + BOOLEAN System; + + Hidden = BooleanFlagOn(Dirent->Attributes, FAT_DIRENT_ATTR_HIDDEN ); + System = BooleanFlagOn(Dirent->Attributes, FAT_DIRENT_ATTR_SYSTEM ); + + if ((Hidden && !FlagOn(FileAttributes, FILE_ATTRIBUTE_HIDDEN)) || + (System && !FlagOn(FileAttributes, FILE_ATTRIBUTE_SYSTEM))) { + + DebugTrace(0, Dbg, "The hidden and/or system bits do not match\n", 0); + + if ( !IsPagingFile ) { + + Iosb.Status = STATUS_ACCESS_DENIED; + try_return( Iosb ); + } + } + + // + // If this media is write protected, don't even try the create. + // + + if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED)) { + + // + // Set the real device for the pop-up info, and set the verify + // bit in the device object, so that we will force a verify + // in case the user put the correct media back in. + // + + + IoSetHardErrorOrVerifyDevice( IrpContext->OriginatingIrp, + Vcb->Vpb->RealDevice ); + + SetFlag(Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME); + + FatRaiseStatus( IrpContext, STATUS_MEDIA_WRITE_PROTECTED ); + } + } + + // + // Create a new Fcb for the file, and set the file size in + // the fcb. + // + + *Fcb = UnwindFcb = FatCreateFcb( IrpContext, + Vcb, + ParentDcb, + LfnByteOffset, + DirentByteOffset, + Dirent, + Lfn, + IsPagingFile, + FALSE ); + + + (*Fcb)->Header.ValidDataLength.LowPart = (*Fcb)->Header.FileSize.LowPart; + + // + // If this is a paging file, lookup the allocation size so that + // the Mcb is always valid + // + + if (IsPagingFile) { + + FatLookupFileAllocationSize( IrpContext, *Fcb ); + } + +#if (NTDDI_VERSION >= NTDDI_WIN7) + + // + // Let's make sure that if the caller provided an oplock key that it + // gets stored in the file object. + // + + Iosb.Status = FsRtlCheckOplockEx( FatGetFcbOplock(*Fcb), + IrpContext->OriginatingIrp, + OPLOCK_FLAG_OPLOCK_KEY_CHECK_ONLY, + NULL, + NULL, + NULL ); + + // + // If the caller wants atomic create-with-oplock semantics, tell + // the oplock package. We haven't incremented the Fcb's UncleanCount + // for this create yet, so add that in on the call. + // + + if (OpenRequiringOplock && + (Iosb.Status == STATUS_SUCCESS)) { + + Iosb.Status = FsRtlOplockFsctrl( FatGetFcbOplock(*Fcb), + IrpContext->OriginatingIrp, + ((*Fcb)->UncleanCount + 1) ); + } + + // + // Get out if either of the above calls failed. Raise to trigger + // cleanup of the new Fcb. + // + + if (Iosb.Status != STATUS_SUCCESS) { + + NT_ASSERT( Iosb.Status != STATUS_PENDING ); + + FatRaiseStatus( IrpContext, Iosb.Status ); + } +#endif + + // + // Now case on whether we are to simply open, supersede, or + // overwrite the file. + // + + switch (CreateDisposition) { + + case FILE_OPEN: + case FILE_OPEN_IF: + + DebugTrace(0, Dbg, "Doing only an open operation\n", 0); + + // + // If the caller has no Ea knowledge, we immediately check for + // Need Ea's on the file. + // + + if (NoEaKnowledge && !FatIsFat32(Vcb)) { + + ULONG NeedEaCount; + + FatGetNeedEaCount( IrpContext, + Vcb, + Dirent, + &NeedEaCount ); + + if (NeedEaCount != 0) { + + FatRaiseStatus( IrpContext, STATUS_ACCESS_DENIED ); + } + } + + // + // Setup the context and section object pointers. + // + + FatSetFileObject( FileObject, + UserFileOpen, + *Fcb, + UnwindCcb = FatCreateCcb( IrpContext )); + + FileObject->SectionObjectPointer = &(*Fcb)->NonPaged->SectionObjectPointers; + + Iosb.Status = STATUS_SUCCESS; + Iosb.Information = FILE_OPENED; + break; + + case FILE_SUPERSEDE: + case FILE_OVERWRITE: + case FILE_OVERWRITE_IF: + + DebugTrace(0, Dbg, "Doing supersede/overwrite operation\n", 0); + + // + // Determine the granted access for this operation now. + // + + if (!NT_SUCCESS( Iosb.Status = FatCheckSystemSecurityAccess( IrpContext ))) { + + try_return( Iosb ); + } + + Iosb = FatSupersedeOrOverwriteFile( IrpContext, + FileObject, + *Fcb, + AllocationSize, + EaBuffer, + EaLength, + FileAttributes, + CreateDisposition, + NoEaKnowledge ); + break; + + default: + + DebugTrace(0, Dbg, "Illegal Create Disposition\n", 0); + +#pragma prefast( suppress:28159, "things are seriously wrong if we get here" ) + FatBugCheck( CreateDisposition, 0, 0 ); + break; + } + + try_exit: NOTHING; + + // + // Setup our share access and counts if things were successful. + // + + if ((Iosb.Status != STATUS_PENDING) && NT_SUCCESS( Iosb.Status )) { + + // + // Remove any virtual access the caller needed to check against, but will + // not really receive. Overwrite/supersede is a bit of a special case. + // + + ClearFlag( *DesiredAccess, AddedAccess ); + + IoSetShareAccess( *DesiredAccess, + ShareAccess, + FileObject, + &(*Fcb)->ShareAccess ); + + (*Fcb)->UncleanCount += 1; + (*Fcb)->OpenCount += 1; + if (FlagOn(FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING)) { + (*Fcb)->NonCachedUncleanCount += 1; + } + Vcb->OpenFileCount += 1; + if (IsFileObjectReadOnly(FileObject)) { Vcb->ReadOnlyCount += 1; } + + CountsIncremented = TRUE; + } + + { + PCCB Ccb; + + Ccb = (PCCB)FileObject->FsContext2; + + if ( NT_SUCCESS(Iosb.Status) ) { + + // + // Mark the DeleteOnClose bit if the operation was successful. + // + + if ( DeleteOnClose ) { + + SetFlag( Ccb->Flags, CCB_FLAG_DELETE_ON_CLOSE ); + } + + // + // Mark the OpenedByShortName bit if the operation was successful. + // + + if ( FileNameOpenedDos ) { + + SetFlag( Ccb->Flags, CCB_FLAG_OPENED_BY_SHORTNAME ); + } + + // + // Mark the ManageVolumeAccess bit if the privilege is held. + // + + if (FatCheckManageVolumeAccess( IrpContext, + IrpSp->Parameters.Create.SecurityContext->AccessState, + (KPROCESSOR_MODE)( FlagOn( IrpSp->Flags, SL_FORCE_ACCESS_CHECK ) ? + UserMode : + IrpContext->OriginatingIrp->RequestorMode ))) { + + SetFlag( Ccb->Flags, CCB_FLAG_MANAGE_VOLUME_ACCESS ); + } + + } + } + + + } finally { + + DebugUnwind( FatOpenExistingFile ); + + // + // If this is an abnormal termination then undo our work + // + + if (AbnormalTermination()) { + + if (CountsIncremented) { + (*Fcb)->UncleanCount -= 1; + (*Fcb)->OpenCount -= 1; + if (FlagOn(FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING)) { + (*Fcb)->NonCachedUncleanCount -= 1; + } + Vcb->OpenFileCount -= 1; + if (IsFileObjectReadOnly(FileObject)) { Vcb->ReadOnlyCount -= 1; } + } + + if (UnwindFcb != NULL) { + if (ARGUMENT_PRESENT( FileObject )) { + FileObject->SectionObjectPointer = NULL; + } + FatDeleteFcb( IrpContext, &UnwindFcb ); + *Fcb = NULL; + } + + if (UnwindCcb != NULL) { FatDeleteCcb( IrpContext, &UnwindCcb ); } + } + + DebugTrace(-1, Dbg, "FatOpenExistingFile -> Iosb.Status = %08lx\n", Iosb.Status); + } + + return Iosb; +} + + +// +// Internal support routine +// + +_Requires_lock_held_(_Global_critical_region_) +IO_STATUS_BLOCK +FatCreateNewDirectory ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PIO_STACK_LOCATION IrpSp, + _Inout_ PFILE_OBJECT FileObject, + _Inout_ PVCB Vcb, + _Inout_ PDCB ParentDcb, + _In_ POEM_STRING OemName, + _In_ PUNICODE_STRING UnicodeName, + _In_ PACCESS_MASK DesiredAccess, + _In_ USHORT ShareAccess, + _In_ PFILE_FULL_EA_INFORMATION EaBuffer, + _In_ ULONG EaLength, + _In_ UCHAR FileAttributes, + _In_ BOOLEAN NoEaKnowledge, + _In_ BOOLEAN DeleteOnClose, + _In_ BOOLEAN OpenRequiringOplock + ) + +/*++ + +Routine Description: + + This routine creates a new directory. The directory has already been + verified not to exist yet. + +Arguments: + + FileObject - Supplies the file object for the newly created directory + + Vcb - Supplies the Vcb denote the volume to contain the new directory + + ParentDcb - Supplies the parent directory containg the newly created + directory + + OemName - Supplies the Oem name for the newly created directory. It may + or maynot be 8.3 complient, but will be upcased. + + UnicodeName - Supplies the Unicode name for the newly created directory. + It may or maynot be 8.3 complient. This name contains the original + case information. + + DesiredAccess - Supplies the desired access of the caller + + ShareAccess - Supplies the shared access of the caller + + EaBuffer - Supplies the Ea set for the newly created directory + + EaLength - Supplies the length, in bytes, of EaBuffer + + FileAttributes - Supplies the file attributes for the newly created + directory. + + NoEaKnowledge - This opener doesn't understand Ea's and we fail this + open if the file has NeedEa's. + + DeleteOnClose - The caller wants the file gone when the handle is closed + + OpenRequiringOplock - The caller provided the FILE_OPEN_REQUIRING_OPLOCK option. + +Return Value: + + IO_STATUS_BLOCK - Returns the completion status for the operation + +--*/ + +{ + IO_STATUS_BLOCK Iosb; + + PDCB Dcb = NULL; + PCCB Ccb = NULL; + + PDIRENT Dirent = NULL; + PBCB DirentBcb = NULL; + ULONG DirentsNeeded; + ULONG DirentByteOffset; + + PDIRENT ShortDirent; + ULONG ShortDirentByteOffset; + + USHORT EaHandle; + + BOOLEAN AllLowerComponent; + BOOLEAN AllLowerExtension; + BOOLEAN CreateLfn; + + ULONG BytesInFirstPage = 0; + ULONG DirentsInFirstPage = 0; + PDIRENT FirstPageDirent = 0; + + PBCB SecondPageBcb = NULL; + ULONG SecondPageOffset; + PDIRENT SecondPageDirent = NULL; + + BOOLEAN DirentFromPool = FALSE; + + + OEM_STRING ShortName; + UCHAR ShortNameBuffer[12]; + +#if (NTDDI_VERSION <= NTDDI_WIN7) + UNREFERENCED_PARAMETER( OpenRequiringOplock ); +#endif + + UNREFERENCED_PARAMETER( IrpSp ); + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatCreateNewDirectory...\n", 0); + + ShortName.Length = 0; + ShortName.MaximumLength = 12; + ShortName.Buffer = (PCHAR)&ShortNameBuffer[0]; + + EaHandle = 0; + + // + // We fail this operation if the caller doesn't understand Ea's. + // + + if (NoEaKnowledge + && EaLength > 0) { + + Iosb.Status = STATUS_ACCESS_DENIED; + + DebugTrace(-1, Dbg, "FatCreateNewDirectory -> Iosb.Status = %08lx\n", Iosb.Status); + return Iosb; + } + + // + // DeleteOnClose and ReadOnly are not compatible. + // + + if (DeleteOnClose && FlagOn(FileAttributes, FAT_DIRENT_ATTR_READ_ONLY)) { + + Iosb.Status = STATUS_CANNOT_DELETE; + return Iosb; + } + + // Now get the names that we will be using. + // + + FatSelectNames( IrpContext, + ParentDcb, + OemName, + UnicodeName, + &ShortName, + NULL, + &AllLowerComponent, + &AllLowerExtension, + &CreateLfn ); + + // + // If we are not in Chicago mode, ignore the magic bits. + // + + if (!FatData.ChicagoMode) { + + AllLowerComponent = FALSE; + AllLowerExtension = FALSE; + CreateLfn = FALSE; + } + + // + // Create/allocate a new dirent + // + + DirentsNeeded = CreateLfn ? FAT_LFN_DIRENTS_NEEDED(UnicodeName) + 1 : 1; + + DirentByteOffset = FatCreateNewDirent( IrpContext, + ParentDcb, + DirentsNeeded, + FALSE ); + try { + + FatPrepareWriteDirectoryFile( IrpContext, + ParentDcb, + DirentByteOffset, + sizeof(DIRENT), + &DirentBcb, + &Dirent, + FALSE, + TRUE, + &Iosb.Status ); + + NT_ASSERT( NT_SUCCESS( Iosb.Status ) && DirentBcb && Dirent ); + + // + // Deal with the special case of an LFN + Dirent structure crossing + // a page boundry. + // + + if ((DirentByteOffset / PAGE_SIZE) != + ((DirentByteOffset + (DirentsNeeded - 1) * sizeof(DIRENT)) / PAGE_SIZE)) { + + SecondPageBcb; + SecondPageOffset; + SecondPageDirent; + + SecondPageOffset = (DirentByteOffset & ~(PAGE_SIZE - 1)) + PAGE_SIZE; + + BytesInFirstPage = SecondPageOffset - DirentByteOffset; + + DirentsInFirstPage = BytesInFirstPage / sizeof(DIRENT); + + FatPrepareWriteDirectoryFile( IrpContext, + ParentDcb, + SecondPageOffset, + sizeof(DIRENT), + &SecondPageBcb, + &SecondPageDirent, + FALSE, + TRUE, + &Iosb.Status ); + + NT_ASSERT( NT_SUCCESS( Iosb.Status ) && SecondPageBcb && SecondPageDirent ); + + FirstPageDirent = Dirent; + + Dirent = FsRtlAllocatePoolWithTag( PagedPool, + DirentsNeeded * sizeof(DIRENT), + TAG_DIRENT ); + + DirentFromPool = TRUE; + } + + // + // Bump up Dirent and DirentByteOffset + // + + ShortDirent = Dirent + DirentsNeeded - 1; + ShortDirentByteOffset = DirentByteOffset + + (DirentsNeeded - 1) * sizeof(DIRENT); + + NT_ASSERT( NT_SUCCESS( Iosb.Status )); + + + // + // Fill in the fields of the dirent. + // + + FatConstructDirent( IrpContext, + ShortDirent, + &ShortName, + AllLowerComponent, + AllLowerExtension, + CreateLfn ? UnicodeName : NULL, + (UCHAR)(FileAttributes | FAT_DIRENT_ATTR_DIRECTORY), + TRUE, + NULL ); + + // + // If the dirent crossed pages, we have to do some real gross stuff. + // + + if (DirentFromPool) { + + RtlCopyMemory( FirstPageDirent, Dirent, BytesInFirstPage ); + + RtlCopyMemory( SecondPageDirent, + Dirent + DirentsInFirstPage, + DirentsNeeded*sizeof(DIRENT) - BytesInFirstPage ); + + ShortDirent = SecondPageDirent + (DirentsNeeded - DirentsInFirstPage) - 1; + } + + // + // Create a new dcb for the directory. + // + + Dcb = FatCreateDcb( IrpContext, + Vcb, + ParentDcb, + DirentByteOffset, + ShortDirentByteOffset, + ShortDirent, + CreateLfn ? UnicodeName : NULL ); + +#if (NTDDI_VERSION >= NTDDI_WIN8) + // + // The next three FsRtl calls are for oplock work. We deliberately + // do these here so that if either call fails we will be able to + // clean up without adding a bunch of code to unwind counts, fix + // the file object, etc. + // + + // + // Let's make sure that if the caller provided an oplock key that it + // gets stored in the file object. + // + + Iosb.Status = FsRtlCheckOplockEx( FatGetFcbOplock(Dcb), + IrpContext->OriginatingIrp, + OPLOCK_FLAG_OPLOCK_KEY_CHECK_ONLY, + NULL, + NULL, + NULL ); + + // + // If the caller wants atomic create-with-oplock semantics, tell + // the oplock package. We haven't incremented the Dcb's UncleanCount + // for this create yet, so add that in on the call. + // + + if (OpenRequiringOplock && + (Iosb.Status == STATUS_SUCCESS)) { + + Iosb.Status = FsRtlOplockFsctrl( FatGetFcbOplock(Dcb), + IrpContext->OriginatingIrp, + (Dcb->UncleanCount + 1) ); + } + + // + // Break parent directory oplock. Directory oplock breaks are always + // advisory, so we will never block/get STATUS_PENDING here. On the + // off chance this fails with INSUFFICIENT_RESOURCES we do it here + // where we can still tolerate a failure. + // + + if (Iosb.Status == STATUS_SUCCESS) { + + Iosb.Status = FsRtlCheckOplockEx( FatGetFcbOplock(ParentDcb), + IrpContext->OriginatingIrp, + OPLOCK_FLAG_PARENT_OBJECT, + NULL, + NULL, + NULL ); + } + + // + // Get out if any of the oplock calls failed. + // + + if (Iosb.Status != STATUS_SUCCESS) { + + FatRaiseStatus( IrpContext, Iosb.Status ); + } +#endif + + // + // Tentatively add the new Ea's, + // + + if (EaLength > 0) { + + // + // This returns false if we are trying to create a file + // with Need Ea's and don't understand EA's. + // + + FatCreateEa( IrpContext, + Dcb->Vcb, + (PUCHAR) EaBuffer, + EaLength, + &Dcb->ShortName.Name.Oem, + &EaHandle ); + } + + if (!FatIsFat32(Dcb->Vcb)) { + + ShortDirent->ExtendedAttributes = EaHandle; + } + + // + // After this point we cannot just simply mark the dirent deleted, + // we have to deal with the directory file object. + // + + // + // Make the dirent into a directory. Note that even if this call + // raises because of disk space, the diectory file object has been + // created. + // + + FatInitializeDirectoryDirent( IrpContext, Dcb, ShortDirent ); + + // + // Setup the context and section object pointers, and update + // our reference counts. Note that this call cannot fail. + // + + FatSetFileObject( FileObject, + UserDirectoryOpen, + Dcb, + Ccb = FatCreateCcb( IrpContext ) ); + + // + // Initialize the LongFileName if it has not already been set, so that + // FatNotify below won't have to. If there are filesystem filters + // attached to FAT, the LongFileName could have gotten set if the + // filter queried for name information on this file object while + // watching the IO needed in FatInitializeDirectoryDirent. + // + + if (Dcb->FullFileName.Buffer == NULL) { + + FatSetFullNameInFcb( IrpContext, Dcb, UnicodeName ); + } + + // + // We call the notify package to report that the + // we added a file. + // + + FatNotifyReportChange( IrpContext, + Vcb, + Dcb, + FILE_NOTIFY_CHANGE_DIR_NAME, + FILE_ACTION_ADDED ); + + // + // Setup our share access + // + + IoSetShareAccess( *DesiredAccess, + ShareAccess, + FileObject, + &Dcb->ShareAccess ); + + + // + // From this point on, nothing can raise. + // + + Dcb->UncleanCount += 1; + Dcb->OpenCount += 1; + Vcb->OpenFileCount += 1; + if (IsFileObjectReadOnly(FileObject)) { Vcb->ReadOnlyCount += 1; } + + if (DeleteOnClose) { + + SetFlag( Ccb->Flags, CCB_FLAG_DELETE_ON_CLOSE ); + } + + // + // And set our return status + // + + Iosb.Status = STATUS_SUCCESS; + Iosb.Information = FILE_CREATED; + + } except(FatExceptionFilter( IrpContext, GetExceptionInformation() )) { + + // + // We'll catch all exceptions and handle them below. + // + + Iosb.Status = IrpContext->ExceptionStatus; + } + + // + // If we failed then undo our work. + // + + if (!NT_SUCCESS( Iosb.Status )) { + + // + // We always have to delete the Ccb if we created one. + // + + if ( Ccb != NULL ) { + + FatDeleteCcb( IrpContext, &Ccb ); + } + +#pragma prefast( suppress: 28924, "prefast thinks this test is redundant, but DCB can be NULL depending on where we raise" ) + if ( Dcb == NULL) { + + NT_ASSERT( (ParentDcb->Specific.Dcb.UnusedDirentVbo == 0xffffffff) || + RtlAreBitsSet( &ParentDcb->Specific.Dcb.FreeDirentBitmap, + DirentByteOffset / sizeof(DIRENT), + DirentsNeeded ) ); + + RtlClearBits( &ParentDcb->Specific.Dcb.FreeDirentBitmap, + DirentByteOffset / sizeof(DIRENT), + DirentsNeeded ); + + // + // Mark the dirents deleted. The codes is complex because of + // dealing with an LFN than crosses a page boundry. + // + + if (Dirent != NULL) { + + ULONG i; + + // + // We failed before creating a directory file object. + // We can just mark the dirent deleted and exit. + // + + for (i = 0; i < DirentsNeeded; i++) { + + if (DirentFromPool == FALSE) { + + // + // Simple case. + // + + Dirent[i].FileName[0] = FAT_DIRENT_DELETED; + + } else { + + // + // If the second CcPreparePinWrite failed, we have + // to stop early. + // + + if ((SecondPageBcb == NULL) && + (i == DirentsInFirstPage)) { + + break; + } + + // + // Now conditionally update either page. + // + + if (i < DirentsInFirstPage) { + + FirstPageDirent[i].FileName[0] = FAT_DIRENT_DELETED; + + } else { + + SecondPageDirent[i - DirentsInFirstPage].FileName[0] = FAT_DIRENT_DELETED; + } + } + } + } + } + } + + // + // Just drop the Bcbs we have in the parent right now so if we + // failed to create the directory and we take the path to rip apart + // the partially created child, when we sync-uninit we won't cause + // a lazy writer processing the parent to block on us. This would + // consume one of the lazy writers, one of which must be running free + // in order for us to come back from the sync-uninit. + // + // Neat, huh? + // + // Granted, the delete dirent below will be marginally less efficient + // since the Bcb may be reclaimed by the time it executes. Life is + // tough. + // + + FatUnpinBcb( IrpContext, DirentBcb ); + FatUnpinBcb( IrpContext, SecondPageBcb ); + + if (DirentFromPool) { + + ExFreePool( Dirent ); + } + + if (!NT_SUCCESS( Iosb.Status )) { + +#pragma prefast( suppress: 28924, "prefast thinks this test is redundant, but DCB can be NULL depending on where we raise" ) + if (Dcb != NULL) { + + // + // We have created the Dcb. If an error occurred while + // creating the Ea's, there will be no directory file + // object. + // + + PFILE_OBJECT DirectoryFileObject; + + DirectoryFileObject = Dcb->Specific.Dcb.DirectoryFile; + + // + // Knock down all of the repinned data so we can begin to destroy + // this failed child. We don't care about any raising here - we're + // already got a fire going. + // + // Note that if we failed to do this, the repinned initial pieces + // of the child would cause the sync-uninit to block forever. + // + // A previous spin on this fix had us not make the ./.. creation + // "reversible" (bad term) and thus avoid having the Bcb still + // outstanding. This wound up causing very bad things to happen + // on DMF floppies when we tried to do a similar yank-down in the + // create path - we want the purge it does to make sure we never + // try to write the bytes out ... it is just a lot cleaner to + // unpinrepin. I'll leave the reversible logic in place if it ever + // proves useful. + // + + // + // There is a possibility that this may be a generally good idea + // for "live" finally clauses - set in ExceptionFilter, clear in + // ProcessException. Think about this. + // + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_RAISE ); + FatUnpinRepinnedBcbs( IrpContext ); + ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_RAISE ); + + if (Dcb->FirstClusterOfFile != 0) { + + try { + + Dcb->Header.FileSize.LowPart = 0; + + CcSetFileSizes( Dcb->Specific.Dcb.DirectoryFile, + (PCC_FILE_SIZES)&Dcb->Header.AllocationSize ); + + // + // Now zap the allocation backing it. + // + + FatTruncateFileAllocation( IrpContext, Dcb, 0, TRUE ); + + } except(FatExceptionFilter( IrpContext, GetExceptionInformation() )) { + + // + // We catch all exceptions that Fat catches, but don't do + // anything with them. + // + } + } + + if (DirectoryFileObject != NULL) { + + FatSyncUninitializeCacheMap( IrpContext, + DirectoryFileObject ); + } + + + try { + + // + // Remove the directory entry we made in the parent Dcb. + // + + FatDeleteDirent( IrpContext, Dcb, NULL, TRUE ); + + // + // FatDeleteDirent can pin and dirty BCBs, so lets unrepin again. + // + + FatUnpinRepinnedBcbs( IrpContext ); + + } except(FatExceptionFilter( IrpContext, GetExceptionInformation() )) { + + // + // We catch all exceptions that Fat catches, but don't do + // anything with them. + // + } + + // + // Finaly, dereference the directory file object. This will + // cause a close Irp to be processed, blowing away the Fcb. + // + + if (DirectoryFileObject != NULL) { + + // + // Dereference the file object for this DCB. The DCB will + // go away when this file object is closed. + // + + Dcb->Specific.Dcb.DirectoryFile = NULL; + ObDereferenceObject( DirectoryFileObject ); + + } else { + + // + // This was also a PDK fix. If the stream file exists, this would + // be done during the dereference file object operation. Otherwise + // we have to remove the Dcb and check if we should remove the parent. + // For now we will just leave the parent lying around. + // + +#pragma prefast( suppress: 28924, "prefast thinks this test is redundant, but FileObject can be NULL depending on where we raise" ) + if (ARGUMENT_PRESENT( FileObject )) { + FileObject->SectionObjectPointer = NULL; + } + FatDeleteFcb( IrpContext, &Dcb ); + } + } + + DebugTrace(-1, Dbg, "FatCreateNewDirectory -> Iosb.Status = %08lx\n", Iosb.Status); + + FatRaiseStatus( IrpContext, Iosb.Status ); + } + + UNREFERENCED_PARAMETER( EaBuffer ); + UNREFERENCED_PARAMETER( EaLength ); + + return Iosb; +} + + +// +// Internal support routine +// + +_Requires_lock_held_(_Global_critical_region_) +IO_STATUS_BLOCK +FatCreateNewFile ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PIO_STACK_LOCATION IrpSp, + _Inout_ PFILE_OBJECT FileObject, + _Inout_ PVCB Vcb, + _Inout_ PDCB ParentDcb, + _In_ POEM_STRING OemName, + _In_ PUNICODE_STRING UnicodeName, + _In_ PACCESS_MASK DesiredAccess, + _In_ USHORT ShareAccess, + _In_ ULONG AllocationSize, + _In_ PFILE_FULL_EA_INFORMATION EaBuffer, + _In_ ULONG EaLength, + _In_ UCHAR FileAttributes, + _In_ PUNICODE_STRING LfnBuffer, + _In_ BOOLEAN IsPagingFile, + _In_ BOOLEAN NoEaKnowledge, + _In_ BOOLEAN DeleteOnClose, + _In_ BOOLEAN OpenRequiringOplock, + _In_ BOOLEAN TemporaryFile + ) + +/*++ + +Routine Description: + + This routine creates a new file. The file has already been verified + not to exist yet. + +Arguments: + + FileObject - Supplies the file object for the newly created file + + Vcb - Supplies the Vcb denote the volume to contain the new file + + ParentDcb - Supplies the parent directory containg the newly created + File + + OemName - Supplies the Oem name for the newly created file. It may + or maynot be 8.3 complient, but will be upcased. + + UnicodeName - Supplies the Unicode name for the newly created file. + It may or maynot be 8.3 complient. This name contains the original + case information. + + DesiredAccess - Supplies the desired access of the caller + + ShareAccess - Supplies the shared access of the caller + + AllocationSize - Supplies the initial allocation size for the file + + EaBuffer - Supplies the Ea set for the newly created file + + EaLength - Supplies the length, in bytes, of EaBuffer + + FileAttributes - Supplies the file attributes for the newly created + file + + LfnBuffer - A MAX_LFN sized buffer for directory searching + + IsPagingFile - Indicates if this is the paging file being created + + NoEaKnowledge - This opener doesn't understand Ea's and we fail this + open if the file has NeedEa's. + + DeleteOnClose - The caller wants the file gone when the handle is closed + + OpenRequiringOplock - The caller provided the FILE_OPEN_REQUIRING_OPLOCK option. + + TemporaryFile - Signals the lazywriter to not write dirty data unless + absolutely has to. + + +Return Value: + + IO_STATUS_BLOCK - Returns the completion status for the operation + +--*/ + +{ + IO_STATUS_BLOCK Iosb = {0}; + + PFCB Fcb = NULL; + + PDIRENT Dirent = NULL; + PBCB DirentBcb = NULL; + ULONG DirentsNeeded; + ULONG DirentByteOffset; + + PDIRENT ShortDirent; + ULONG ShortDirentByteOffset; + + USHORT EaHandle; + + BOOLEAN AllLowerComponent; + BOOLEAN AllLowerExtension; + BOOLEAN CreateLfn; + + ULONG BytesInFirstPage = 0; + ULONG DirentsInFirstPage = 0; + PDIRENT FirstPageDirent = NULL; + + PBCB SecondPageBcb = NULL; + ULONG SecondPageOffset; + PDIRENT SecondPageDirent = NULL; + + BOOLEAN DirentFromPool = FALSE; + + OEM_STRING ShortName; + UCHAR ShortNameBuffer[12]; + + UNICODE_STRING UniTunneledShortName; + WCHAR UniTunneledShortNameBuffer[12]; + UNICODE_STRING UniTunneledLongName; + WCHAR UniTunneledLongNameBuffer[26]; + LARGE_INTEGER TunneledCreationTime; + ULONG TunneledDataSize; + BOOLEAN HaveTunneledInformation; + BOOLEAN UsingTunneledLfn = FALSE; + + PUNICODE_STRING RealUnicodeName; + + + // + // The following variables are for abnormal termination + // + + PDIRENT UnwindDirent = NULL; + PFCB UnwindFcb = NULL; + BOOLEAN UnwindAllocation = FALSE; + BOOLEAN CountsIncremented = FALSE; + PCCB UnwindCcb = NULL; + + ULONG LocalAbnormalTermination = FALSE; + +#if (NTDDI_VERSION < NTDDI_WIN7) + UNREFERENCED_PARAMETER( OpenRequiringOplock ); +#endif + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatCreateNewFile...\n", 0); + + ShortName.Length = 0; + ShortName.MaximumLength = sizeof(ShortNameBuffer); + ShortName.Buffer = (PCHAR)&ShortNameBuffer[0]; + + UniTunneledShortName.Length = 0; + UniTunneledShortName.MaximumLength = sizeof(UniTunneledShortNameBuffer); + UniTunneledShortName.Buffer = &UniTunneledShortNameBuffer[0]; + + UniTunneledLongName.Length = 0; + UniTunneledLongName.MaximumLength = sizeof(UniTunneledLongNameBuffer); + UniTunneledLongName.Buffer = &UniTunneledLongNameBuffer[0]; + + EaHandle = 0; + + // + // We fail this operation if the caller doesn't understand Ea's. + // + + if (NoEaKnowledge + && EaLength > 0) { + + Iosb.Status = STATUS_ACCESS_DENIED; + + DebugTrace(-1, Dbg, "FatCreateNewFile -> Iosb.Status = %08lx\n", Iosb.Status); + return Iosb; + } + + // + // DeleteOnClose and ReadOnly are not compatible. + // + + if (DeleteOnClose && FlagOn(FileAttributes, FAT_DIRENT_ATTR_READ_ONLY)) { + + Iosb.Status = STATUS_CANNOT_DELETE; + return Iosb; + } + + // + // Look in the tunnel cache for names and timestamps to restore + // + + TunneledDataSize = sizeof(LARGE_INTEGER); + HaveTunneledInformation = FsRtlFindInTunnelCache( &Vcb->Tunnel, + FatDirectoryKey(ParentDcb), + UnicodeName, + &UniTunneledShortName, + &UniTunneledLongName, + &TunneledDataSize, + &TunneledCreationTime ); + NT_ASSERT(TunneledDataSize == sizeof(LARGE_INTEGER)); + + // + // Now get the names that we will be using. + // + + FatSelectNames( IrpContext, + ParentDcb, + OemName, + UnicodeName, + &ShortName, + (HaveTunneledInformation? &UniTunneledShortName : NULL), + &AllLowerComponent, + &AllLowerExtension, + &CreateLfn ); + + // + // If we are not in Chicago mode, ignore the magic bits. + // + + RealUnicodeName = UnicodeName; + + if (!FatData.ChicagoMode) { + + AllLowerComponent = FALSE; + AllLowerExtension = FALSE; + CreateLfn = FALSE; + + } else { + + // + // If the Unicode name was legal for a short name and we got + // a tunneling hit which had a long name associated which is + // avaliable for use, use it. + // + + if (!CreateLfn && + UniTunneledLongName.Length && + !FatLfnDirentExists(IrpContext, ParentDcb, &UniTunneledLongName, LfnBuffer)) { + + UsingTunneledLfn = TRUE; + CreateLfn = TRUE; + + RealUnicodeName = &UniTunneledLongName; + + // + // Short names are always upcase if an LFN exists + // + + AllLowerComponent = FALSE; + AllLowerExtension = FALSE; + } + } + + + // + // Create/allocate a new dirent + // + + DirentsNeeded = CreateLfn ? FAT_LFN_DIRENTS_NEEDED(RealUnicodeName) + 1 : 1; + + DirentByteOffset = FatCreateNewDirent( IrpContext, + ParentDcb, + DirentsNeeded, + FALSE ); + + try { + + FatPrepareWriteDirectoryFile( IrpContext, + ParentDcb, + DirentByteOffset, + sizeof(DIRENT), + &DirentBcb, + &Dirent, + FALSE, + TRUE, + &Iosb.Status ); + + NT_ASSERT( NT_SUCCESS( Iosb.Status ) ); + + UnwindDirent = Dirent; + + // + // Deal with the special case of an LFN + Dirent structure crossing + // a page boundry. + // + + if ((DirentByteOffset / PAGE_SIZE) != + ((DirentByteOffset + (DirentsNeeded - 1) * sizeof(DIRENT)) / PAGE_SIZE)) { + + SecondPageBcb; + SecondPageOffset; + SecondPageDirent; + + SecondPageOffset = (DirentByteOffset & ~(PAGE_SIZE - 1)) + PAGE_SIZE; + + BytesInFirstPage = SecondPageOffset - DirentByteOffset; + + DirentsInFirstPage = BytesInFirstPage / sizeof(DIRENT); + + FatPrepareWriteDirectoryFile( IrpContext, + ParentDcb, + SecondPageOffset, + sizeof(DIRENT), + &SecondPageBcb, + &SecondPageDirent, + FALSE, + TRUE, + &Iosb.Status ); + + NT_ASSERT( NT_SUCCESS( Iosb.Status ) ); + + FirstPageDirent = Dirent; + + Dirent = FsRtlAllocatePoolWithTag( PagedPool, + DirentsNeeded * sizeof(DIRENT), + TAG_DIRENT ); + + DirentFromPool = TRUE; + } + + // + // Bump up Dirent and DirentByteOffset + // + + ShortDirent = Dirent + DirentsNeeded - 1; + ShortDirentByteOffset = DirentByteOffset + + (DirentsNeeded - 1) * sizeof(DIRENT); + + NT_ASSERT( NT_SUCCESS( Iosb.Status )); + + + // + // Fill in the fields of the dirent. + // + + FatConstructDirent( IrpContext, + ShortDirent, + &ShortName, + AllLowerComponent, + AllLowerExtension, + CreateLfn ? RealUnicodeName : NULL, + (UCHAR)(FileAttributes | FILE_ATTRIBUTE_ARCHIVE), + TRUE, + (HaveTunneledInformation ? &TunneledCreationTime : NULL) ); + + // + // If the dirent crossed pages, we have to do some real gross stuff. + // + + if (DirentFromPool) { + + RtlCopyMemory( FirstPageDirent, Dirent, BytesInFirstPage ); + + RtlCopyMemory( SecondPageDirent, + Dirent + DirentsInFirstPage, + DirentsNeeded*sizeof(DIRENT) - BytesInFirstPage ); + + ShortDirent = SecondPageDirent + (DirentsNeeded - DirentsInFirstPage) - 1; + } + + // + // Create a new Fcb for the file. Once the Fcb is created we + // will not need to unwind dirent because delete dirent will + // now do the work. + // + + Fcb = UnwindFcb = FatCreateFcb( IrpContext, + Vcb, + ParentDcb, + DirentByteOffset, + ShortDirentByteOffset, + ShortDirent, + CreateLfn ? RealUnicodeName : NULL, + IsPagingFile, + FALSE ); + UnwindDirent = NULL; + +#if (NTDDI_VERSION >= NTDDI_WIN7) + // + // The next three FsRtl calls are for oplock work. We deliberately + // do these here so that if either call fails we will be able to + // clean up without adding a bunch of code to unwind counts, fix + // the file object, etc. + // + + // + // Let's make sure that if the caller provided an oplock key that it + // gets stored in the file object. + // + + Iosb.Status = FsRtlCheckOplockEx( FatGetFcbOplock(Fcb), + IrpContext->OriginatingIrp, + OPLOCK_FLAG_OPLOCK_KEY_CHECK_ONLY, + NULL, + NULL, + NULL ); + + // + // If the caller wants atomic create-with-oplock semantics, tell + // the oplock package. We haven't incremented the Fcb's UncleanCount + // for this create yet, so add that in on the call. + // + + if (OpenRequiringOplock && + (Iosb.Status == STATUS_SUCCESS)) { + + Iosb.Status = FsRtlOplockFsctrl( FatGetFcbOplock(Fcb), + IrpContext->OriginatingIrp, + (Fcb->UncleanCount + 1) ); + } +#endif + +#if (NTDDI_VERSION >= NTDDI_WIN8) + // + // Break parent directory oplock. Directory oplock breaks are always + // advisory, so we will never block/get STATUS_PENDING here. On the + // off chance this fails with INSUFFICIENT_RESOURCES we do it here + // where we can still tolerate a failure. + // + + if (Iosb.Status == STATUS_SUCCESS) { + + Iosb.Status = FsRtlCheckOplockEx( FatGetFcbOplock(ParentDcb), + IrpContext->OriginatingIrp, + OPLOCK_FLAG_PARENT_OBJECT, + NULL, + NULL, + NULL ); + } + + // + // Get out if any of the oplock calls failed. We raise to provoke + // abnormal termination and ensure that the newly-created Fcb gets + // deleted. + // + + if (Iosb.Status != STATUS_SUCCESS) { + + FatRaiseStatus( IrpContext, Iosb.Status ); + } +#endif + + // + // If this is a temporary file, note it in the FcbState + // + + if (TemporaryFile) { + + SetFlag( Fcb->FcbState, FCB_STATE_TEMPORARY ); + } + + + // + // Add some initial file allocation + // + + FatAddFileAllocation( IrpContext, Fcb, FileObject, AllocationSize ); + UnwindAllocation = TRUE; + + Fcb->FcbState |= FCB_STATE_TRUNCATE_ON_CLOSE; + + // + // Tentatively add the new Ea's + // + + if ( EaLength > 0 ) { + + FatCreateEa( IrpContext, + Fcb->Vcb, + (PUCHAR) EaBuffer, + EaLength, + &Fcb->ShortName.Name.Oem, + &EaHandle ); + } + + if (!FatIsFat32(Fcb->Vcb)) { + + ShortDirent->ExtendedAttributes = EaHandle; + } + + + + // + // Initialize the LongFileName right now so that FatNotify + // below won't have to. + // + + if (Fcb->FullFileName.Buffer == NULL) { + FatSetFullNameInFcb( IrpContext, Fcb, RealUnicodeName ); + } + + // + // Setup the context and section object pointers, and update + // our reference counts + // + + FatSetFileObject( FileObject, + UserFileOpen, + Fcb, + UnwindCcb = FatCreateCcb( IrpContext )); + + FileObject->SectionObjectPointer = &Fcb->NonPaged->SectionObjectPointers; + + // + // We call the notify package to report that the + // we added a file. + // + + FatNotifyReportChange( IrpContext, + Vcb, + Fcb, + FILE_NOTIFY_CHANGE_FILE_NAME, + FILE_ACTION_ADDED ); + + // + // Setup our share access + // + + IoSetShareAccess( *DesiredAccess, + ShareAccess, + FileObject, + &Fcb->ShareAccess ); + + Fcb->UncleanCount += 1; + Fcb->OpenCount += 1; + if (FlagOn(FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING)) { + Fcb->NonCachedUncleanCount += 1; + } + Vcb->OpenFileCount += 1; + if (IsFileObjectReadOnly(FileObject)) { Vcb->ReadOnlyCount += 1; } + CountsIncremented = TRUE; + + + // + // And set our return status + // + + Iosb.Status = STATUS_SUCCESS; + Iosb.Information = FILE_CREATED; + + if ( NT_SUCCESS(Iosb.Status) ) { + + // + // Mark the DeleteOnClose bit if the operation was successful. + // + + if ( DeleteOnClose ) { + + SetFlag( UnwindCcb->Flags, CCB_FLAG_DELETE_ON_CLOSE ); + } + + // + // Mark the OpenedByShortName bit if the operation was successful. + // If we created an Lfn, we have some sort of generated short name + // and thus don't consider ourselves to have opened it - though we + // may have had a case mix Lfn "Foo.bar" and generated "FOO.BAR" + // + // Unless, of course, we wanted to create a short name and hit an + // associated Lfn in the tunnel cache + // + + if ( !CreateLfn && !UsingTunneledLfn ) { + + SetFlag( UnwindCcb->Flags, CCB_FLAG_OPENED_BY_SHORTNAME ); + } + + // + // Mark the ManageVolumeAccess bit if the privilege is held. + // + + if (FatCheckManageVolumeAccess( IrpContext, + IrpSp->Parameters.Create.SecurityContext->AccessState, + (KPROCESSOR_MODE)( FlagOn( IrpSp->Flags, SL_FORCE_ACCESS_CHECK ) ? + UserMode : + IrpContext->OriginatingIrp->RequestorMode ))) { + + SetFlag( UnwindCcb->Flags, CCB_FLAG_MANAGE_VOLUME_ACCESS ); + } + + } + + + } finally { + + DebugUnwind( FatCreateNewFile ); + + if (UniTunneledLongName.Buffer != UniTunneledLongNameBuffer) { + + // + // Tunneling package grew the buffer from pool + // + + ExFreePool( UniTunneledLongName.Buffer ); + } + + + // + // If this is an abnormal termination then undo our work. + // + // The extra exception handling here is complex. We've got + // two places here where an exception can be thrown again. + // + + LocalAbnormalTermination = AbnormalTermination(); + + if (LocalAbnormalTermination) { + + if (CountsIncremented) { + Fcb->UncleanCount -= 1; + Fcb->OpenCount -= 1; + if (FlagOn(FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING)) { + Fcb->NonCachedUncleanCount -= 1; + } + Vcb->OpenFileCount -= 1; + if (IsFileObjectReadOnly(FileObject)) { Vcb->ReadOnlyCount -= 1; } + } + + if (UnwindFcb == NULL) { + + NT_ASSERT( (ParentDcb->Specific.Dcb.UnusedDirentVbo == 0xffffffff) || + RtlAreBitsSet( &ParentDcb->Specific.Dcb.FreeDirentBitmap, + DirentByteOffset / sizeof(DIRENT), + DirentsNeeded ) ); + + RtlClearBits( &ParentDcb->Specific.Dcb.FreeDirentBitmap, + DirentByteOffset / sizeof(DIRENT), + DirentsNeeded ); + } + + // + // Mark the dirents deleted. The code is complex because of + // dealing with an LFN that crosses a page boundary. + // + + if (UnwindDirent != NULL) { + + ULONG i; + + for (i = 0; i < DirentsNeeded; i++) { + + if (DirentFromPool == FALSE) { + + // + // Simple case. + // + + Dirent[i].FileName[0] = FAT_DIRENT_DELETED; + + } else { + + // + // If the second CcPreparePinWrite failed, we have + // to stop early. + // + + if ((SecondPageBcb == NULL) && + (i == DirentsInFirstPage)) { + + break; + } + + // + // Now conditionally update either page. + // + + if (i < DirentsInFirstPage) { + + FirstPageDirent[i].FileName[0] = FAT_DIRENT_DELETED; + + } else { + + SecondPageDirent[i - DirentsInFirstPage].FileName[0] = FAT_DIRENT_DELETED; + } + } + } + } + } + + // + // We must handle exceptions in the following fragments and plow on with the + // unwind of this create operation. This is basically inverted from the + // previous state of the code. Since AbnormalTermination() changes when we + // enter a new enclosure, we cached the original state ... + // + + try { + + if (LocalAbnormalTermination) { + if (UnwindAllocation) { + FatTruncateFileAllocation( IrpContext, Fcb, 0, TRUE ); + } + } + + } finally { + + try { + + if (LocalAbnormalTermination) { + if (UnwindFcb != NULL) { + FatDeleteDirent( IrpContext, UnwindFcb, NULL, TRUE ); + } + } + + } finally { + + if (LocalAbnormalTermination) { + if (UnwindFcb != NULL) { + if (ARGUMENT_PRESENT( FileObject )) { + FileObject->SectionObjectPointer = NULL; + } + FatDeleteFcb( IrpContext, &UnwindFcb ); + } + if (UnwindCcb != NULL) { FatDeleteCcb( IrpContext, &UnwindCcb ); } + } + + // + // This is the normal cleanup code. + // + + FatUnpinBcb( IrpContext, DirentBcb ); + FatUnpinBcb( IrpContext, SecondPageBcb ); + + if (DirentFromPool) { + + ExFreePool( Dirent ); + } + + } + } + + DebugTrace(-1, Dbg, "FatCreateNewFile -> Iosb.Status = %08lx\n", Iosb.Status); + } + + return Iosb; +} + + +// +// Internal support routine +// + +_Requires_lock_held_(_Global_critical_region_) +IO_STATUS_BLOCK +FatSupersedeOrOverwriteFile ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PFILE_OBJECT FileObject, + _Inout_ PFCB Fcb, + _In_ ULONG AllocationSize, + _In_ PFILE_FULL_EA_INFORMATION EaBuffer, + _In_ ULONG EaLength, + _In_ UCHAR FileAttributes, + _In_ ULONG CreateDisposition, + _In_ BOOLEAN NoEaKnowledge + ) + +/*++ + +Routine Description: + + This routine performs a file supersede or overwrite operation. + +Arguments: + + FileObject - Supplies a pointer to the file object + + Fcb - Supplies a pointer to the Fcb + + AllocationSize - Supplies an initial allocation size + + EaBuffer - Supplies the Ea set for the superseded/overwritten file + + EaLength - Supplies the length, in bytes, of EaBuffer + + FileAttributes - Supplies the supersede/overwrite file attributes + + CreateDisposition - Supplies the create disposition for the file + It must be either supersede, overwrite, or overwrite if. + + NoEaKnowledge - This opener doesn't understand Ea's and we fail this + open if the file has NeedEa's. + +Return Value: + + IO_STATUS_BLOCK - Returns the completion status for the operation + +--*/ + +{ + IO_STATUS_BLOCK Iosb = {0}; + + PDIRENT Dirent; + PBCB DirentBcb; + + USHORT EaHandle = 0; + BOOLEAN EaChange = FALSE; + BOOLEAN ReleasePaging = FALSE; + + PCCB Ccb; + + ULONG NotifyFilter; + + // + // The following variables are for abnormal termination + // + + PCCB UnwindCcb = NULL; + USHORT UnwindEa = 0; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatSupersedeOrOverwriteFile...\n", 0); + + DirentBcb = NULL; + + // + // We fail this operation if the caller doesn't understand Ea's. + // + + if (NoEaKnowledge + && EaLength > 0) { + + Iosb.Status = STATUS_ACCESS_DENIED; + + DebugTrace(-1, Dbg, "FatSupersedeOrOverwriteFile -> Iosb.Status = %08lx\n", Iosb.Status); + return Iosb; + } + + try { + + // + // Before we actually truncate, check to see if the purge + // is going to fail. + // + + if (!MmCanFileBeTruncated( &Fcb->NonPaged->SectionObjectPointers, + &FatLargeZero )) { + + try_return( Iosb.Status = STATUS_USER_MAPPED_FILE ); + } + + // + // Setup the context and section object pointers, and update + // our reference counts + // + + FatSetFileObject( FileObject, + UserFileOpen, + Fcb, + Ccb = UnwindCcb = FatCreateCcb( IrpContext )); + + FileObject->SectionObjectPointer = &Fcb->NonPaged->SectionObjectPointers; + + // + // Since this is an supersede/overwrite, purge the section so + // that mappers will see zeros. We set the CREATE_IN_PROGRESS flag + // to prevent the Fcb from going away out from underneath us. + // + + SetFlag(Fcb->Vcb->VcbState, VCB_STATE_FLAG_CREATE_IN_PROGRESS); + + CcPurgeCacheSection( &Fcb->NonPaged->SectionObjectPointers, NULL, 0, FALSE ); + + // + // Tentatively add the new Ea's + // + + if (EaLength > 0) { + + FatCreateEa( IrpContext, + Fcb->Vcb, + (PUCHAR) EaBuffer, + EaLength, + &Fcb->ShortName.Name.Oem, + &EaHandle ); + + UnwindEa = EaHandle; + EaChange = TRUE; + } + +#if (NTDDI_VERSION >= NTDDI_WIN8) + // + // Break parent directory oplock. Directory oplock breaks are always + // advisory, so we will never block/get STATUS_PENDING here. On the + // off chance this fails with INSUFFICIENT_RESOURCES we do it here + // where we can still tolerate a failure. + // + + Iosb.Status = FsRtlCheckOplockEx( FatGetFcbOplock(Fcb->ParentDcb), + IrpContext->OriginatingIrp, + OPLOCK_FLAG_PARENT_OBJECT, + NULL, + NULL, + NULL ); + + if (Iosb.Status != STATUS_SUCCESS) { + + FatRaiseStatus( IrpContext, Iosb.Status ); + } +#endif + + // + // Now set the new allocation size, we do that by first + // zeroing out the current file size. Then we truncate and + // allocate up to the new allocation size + // + + (VOID)ExAcquireResourceExclusiveLite( Fcb->Header.PagingIoResource, TRUE ); + ReleasePaging = TRUE; + + Fcb->Header.FileSize.LowPart = 0; + Fcb->Header.ValidDataLength.LowPart = 0; + Fcb->ValidDataToDisk = 0; + + // + // Tell the cache manager the size went to zero + // This call is unconditional, because MM always wants to know. + // + + CcSetFileSizes( FileObject, + (PCC_FILE_SIZES)&Fcb->Header.AllocationSize ); + + FatTruncateFileAllocation( IrpContext, Fcb, AllocationSize, FALSE ); + + ExReleaseResourceLite( Fcb->Header.PagingIoResource ); + ReleasePaging = FALSE; + + FatAddFileAllocation( IrpContext, Fcb, FileObject, AllocationSize ); + + Fcb->FcbState |= FCB_STATE_TRUNCATE_ON_CLOSE; + + // + // Modify the attributes and time of the file, by first reading + // in the dirent for the file and then updating its attributes + // and time fields. Note that for supersede we replace the file + // attributes as opposed to adding to them. + // + + FatGetDirentFromFcbOrDcb( IrpContext, + Fcb, + FALSE, + &Dirent, + &DirentBcb ); + // + // We should get the dirent since this Fcb is in good condition, verified as + // we crawled down the prefix tree. + // + // Update the appropriate dirent fields, and the fcb fields + // + + Dirent->FileSize = 0; + + + FileAttributes |= FILE_ATTRIBUTE_ARCHIVE; + + if (CreateDisposition == FILE_SUPERSEDE) { + + Dirent->Attributes = FileAttributes; + + } else { + + Dirent->Attributes |= FileAttributes; + } + + Fcb->DirentFatFlags = Dirent->Attributes; + + KeQuerySystemTime( &Fcb->LastWriteTime ); + + (VOID)FatNtTimeToFatTime( IrpContext, + &Fcb->LastWriteTime, + TRUE, + &Dirent->LastWriteTime, + NULL ); + + if (FatData.ChicagoMode) { + + Dirent->LastAccessDate = Dirent->LastWriteTime.Date; + } + + NotifyFilter = FILE_NOTIFY_CHANGE_LAST_WRITE + | FILE_NOTIFY_CHANGE_ATTRIBUTES + | FILE_NOTIFY_CHANGE_SIZE; + + // + // And now delete the previous Ea set if there was one. + // + + if (!FatIsFat32(Fcb->Vcb) && Dirent->ExtendedAttributes != 0) { + + // + // **** SDK fix, we won't fail this if there is + // an error in the Ea's, we'll just leave + // the orphaned Ea's in the file. + // + + EaChange = TRUE; + + try { + + FatDeleteEa( IrpContext, + Fcb->Vcb, + Dirent->ExtendedAttributes, + &Fcb->ShortName.Name.Oem ); + + } except( EXCEPTION_EXECUTE_HANDLER ) { + + FatResetExceptionState( IrpContext ); + } + } + + // + // Update the extended attributes handle in the dirent. + // + + if (EaChange) { + + NT_ASSERT(!FatIsFat32(Fcb->Vcb)); + + Dirent->ExtendedAttributes = EaHandle; + + NotifyFilter |= FILE_NOTIFY_CHANGE_EA; + } + + // + // Now update the dirent to the new ea handle and set the bcb dirty + // Once we do this we can no longer back out the Ea + // + + FatSetDirtyBcb( IrpContext, DirentBcb, Fcb->Vcb, TRUE ); + UnwindEa = 0; + + // + // Indicate that the Eas for this file have changed. + // + + Ccb->EaModificationCount += Fcb->EaModificationCount; + + // + // Check to see if we need to notify outstanding Irps for full + // changes only (i.e., we haven't added, deleted, or renamed the file). + // + + FatNotifyReportChange( IrpContext, + Fcb->Vcb, + Fcb, + NotifyFilter, + FILE_ACTION_MODIFIED ); + + // + // And set our status to success + // + + Iosb.Status = STATUS_SUCCESS; + + if (CreateDisposition == FILE_SUPERSEDE) { + + Iosb.Information = FILE_SUPERSEDED; + + } else { + + Iosb.Information = FILE_OVERWRITTEN; + } + + try_exit: NOTHING; + } finally { + + DebugUnwind( FatSupersedeOfOverwriteFile ); + + if (ReleasePaging) { ExReleaseResourceLite( Fcb->Header.PagingIoResource ); } + + // + // If this is an abnormal termination then undo our work. + // + + if (AbnormalTermination()) { + + if (UnwindEa != 0) { FatDeleteEa( IrpContext, Fcb->Vcb, UnwindEa, &Fcb->ShortName.Name.Oem ); } + if (UnwindCcb != NULL) { FatDeleteCcb( IrpContext, &UnwindCcb ); } + } + + FatUnpinBcb( IrpContext, DirentBcb ); + + ClearFlag(Fcb->Vcb->VcbState, VCB_STATE_FLAG_CREATE_IN_PROGRESS); + + DebugTrace(-1, Dbg, "FatSupersedeOrOverwriteFile -> Iosb.Status = %08lx\n", Iosb.Status); + } + + return Iosb; +} + + +VOID +FatSetFullNameInFcb ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PFCB Fcb, + _In_ PUNICODE_STRING FinalName + ) + +/*++ + +Routine Description: + + This routine attempts a quick form of the full FatSetFullFileNameInFcb + operation. + + NOTE: this routine is probably not worth the code duplication involved, + and is not equipped to handle the cases where the parent doesn't have + the full name set up. + +Arguments: + + Fcb - Supplies a pointer to the Fcb + + FinalName - Supplies the last component of the path to this Fcb's dirent + +Return Value: + + None. May silently fail. + +--*/ + +{ + PAGED_CODE(); + + UNREFERENCED_PARAMETER( IrpContext ); + + NT_ASSERT( Fcb->FullFileName.Buffer == NULL ); + + // + // Prefer the ExactCaseLongName of the file for this operation, if set. In + // this way we avoid building the fullname with a short filename. Several + // operations assume this - the FinalNameLength in particular is the Lfn + // (if existant) length, and we use this to crack the fullname in paths + // such as the FsRtlNotify caller. + // + // If the caller specified a particular name and it is short, it is the + // case that the long name was set up. + // + + if (Fcb->ExactCaseLongName.Buffer) { + + NT_ASSERT( Fcb->ExactCaseLongName.Length != 0 ); + FinalName = &Fcb->ExactCaseLongName; + } + + // + // Special case the root. + // + + if (NodeType(Fcb->ParentDcb) == FAT_NTC_ROOT_DCB) { + + Fcb->FullFileName.Length = + Fcb->FullFileName.MaximumLength = sizeof(WCHAR) + FinalName->Length; + + Fcb->FullFileName.Buffer = FsRtlAllocatePoolWithTag( PagedPool, + Fcb->FullFileName.Length, + TAG_FILENAME_BUFFER ); + + Fcb->FullFileName.Buffer[0] = L'\\'; + + RtlCopyMemory( &Fcb->FullFileName.Buffer[1], + &FinalName->Buffer[0], + FinalName->Length ); + + } else { + + PUNICODE_STRING Prefix; + + Prefix = &Fcb->ParentDcb->FullFileName; + + // + // It is possible our parent's full filename is not set. Simply fail + // this attempt. + // + + if (Prefix->Buffer == NULL) { + + return; + } + + Fcb->FullFileName.Length = + Fcb->FullFileName.MaximumLength = Prefix->Length + sizeof(WCHAR) + FinalName->Length; + + Fcb->FullFileName.Buffer = FsRtlAllocatePoolWithTag( PagedPool, + Fcb->FullFileName.Length, + TAG_FILENAME_BUFFER ); + + RtlCopyMemory( &Fcb->FullFileName.Buffer[0], + &Prefix->Buffer[0], + Prefix->Length ); + + Fcb->FullFileName.Buffer[Prefix->Length / sizeof(WCHAR)] = L'\\'; + + RtlCopyMemory( &Fcb->FullFileName.Buffer[(Prefix->Length / sizeof(WCHAR)) + 1], + &FinalName->Buffer[0], + FinalName->Length ); + + } +} + + +NTSTATUS +FatCheckSystemSecurityAccess ( + _In_ PIRP_CONTEXT IrpContext + ) +{ + PACCESS_STATE AccessState; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( IrpContext->OriginatingIrp ); + + PAGED_CODE(); + + // + // We check if the caller wants ACCESS_SYSTEM_SECURITY access on this + // object and fail the request if he does. + // + + NT_ASSERT( IrpSp->Parameters.Create.SecurityContext != NULL ); + AccessState = IrpSp->Parameters.Create.SecurityContext->AccessState; + + // + // Check if the remaining privilege includes ACCESS_SYSTEM_SECURITY. + // + + if (FlagOn( AccessState->RemainingDesiredAccess, ACCESS_SYSTEM_SECURITY )) { + + if (!SeSinglePrivilegeCheck( FatSecurityPrivilege, + UserMode )) { + + return STATUS_ACCESS_DENIED; + } + + // + // Move this privilege from the Remaining access to Granted access. + // + + ClearFlag( AccessState->RemainingDesiredAccess, ACCESS_SYSTEM_SECURITY ); + SetFlag( AccessState->PreviouslyGrantedAccess, ACCESS_SYSTEM_SECURITY ); + } + + return STATUS_SUCCESS; +} + + +NTSTATUS +FatCheckShareAccess ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PFILE_OBJECT FileObject, + _In_ PFCB FcbOrDcb, + _In_ PACCESS_MASK DesiredAccess, + _In_ ULONG ShareAccess + ) + +/*++ + +Routine Description: + + This routine checks conditions that may result in a sharing violation. + +Arguments: + + FileObject - Pointer to the file object of the current open request. + + FcbOrDcb - Supplies a pointer to the Fcb/Dcb. + + DesiredAccess - Desired access of current open request. + + ShareAccess - Shared access requested by current open request. + +Return Value: + + If the accessor has access to the file, STATUS_SUCCESS is returned. + Otherwise, STATUS_SHARING_VIOLATION is returned. + +--*/ + +{ + PAGED_CODE(); + +#if (NTDDI_VERSION >= NTDDI_VISTA) + // + // Do an extra test for writeable user sections if the user did not allow + // write sharing - this is neccessary since a section may exist with no handles + // open to the file its based against. + // + + if ((NodeType( FcbOrDcb ) == FAT_NTC_FCB) && + !FlagOn( ShareAccess, FILE_SHARE_WRITE ) && + FlagOn( *DesiredAccess, FILE_EXECUTE | FILE_READ_DATA | FILE_WRITE_DATA | FILE_APPEND_DATA | DELETE | MAXIMUM_ALLOWED ) && + MmDoesFileHaveUserWritableReferences( &FcbOrDcb->NonPaged->SectionObjectPointers )) { + + return STATUS_SHARING_VIOLATION; + } +#endif + + // + // Check if the Fcb has the proper share access. + // + + return IoCheckShareAccess( *DesiredAccess, + ShareAccess, + FileObject, + &FcbOrDcb->ShareAccess, + FALSE ); + + UNREFERENCED_PARAMETER( IrpContext ); +} + +// +// Lifted from NTFS. +// + +NTSTATUS +FatCallSelfCompletionRoutine ( + __in PDEVICE_OBJECT DeviceObject, + __in PIRP Irp, + __in PVOID Contxt + ) + +{ + // + // Set the event so that our call will wake up. + // + + KeSetEvent( (PKEVENT)Contxt, 0, FALSE ); + + UNREFERENCED_PARAMETER( DeviceObject ); + UNREFERENCED_PARAMETER( Irp ); + + // + // If we change this return value then FatIoCallSelf needs to reference the + // file object. + // + + return STATUS_MORE_PROCESSING_REQUIRED; +} + diff --git a/filesys/fastfat/devctrl.c b/filesys/fastfat/devctrl.c new file mode 100644 index 000000000..25e574af2 --- /dev/null +++ b/filesys/fastfat/devctrl.c @@ -0,0 +1,412 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + DevCtrl.c + +Abstract: + + This module implements the File System Device Control routines for Fat + called by the dispatch driver. + + +--*/ + +#include "FatProcs.h" + +// +// The local debug trace level +// + +#define Dbg (DEBUG_TRACE_DEVCTRL) + +// +// Local procedure prototypes +// + +// +// Tell prefast this is an IO_COMPLETION_ROUTINE +// +IO_COMPLETION_ROUTINE FatDeviceControlCompletionRoutine; + +NTSTATUS +FatDeviceControlCompletionRoutine( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Contxt + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, FatCommonDeviceControl) +#pragma alloc_text(PAGE, FatFsdDeviceControl) +#endif + + +_Function_class_(IRP_MJ_DEVICE_CONTROL) +_Function_class_(DRIVER_DISPATCH) +NTSTATUS +FatFsdDeviceControl ( + _In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + _Inout_ PIRP Irp + ) + +/*++ + +Routine Description: + + This routine implements the FSD part of Device control operations + +Arguments: + + VolumeDeviceObject - Supplies the volume device object where the + file exists + + Irp - Supplies the Irp being processed + +Return Value: + + NTSTATUS - The FSD status for the IRP + +--*/ + +{ + NTSTATUS Status; + PIRP_CONTEXT IrpContext = NULL; + + BOOLEAN TopLevel; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatFsdDeviceControl\n", 0); + + FsRtlEnterFileSystem(); + + TopLevel = FatIsIrpTopLevel( Irp ); + + try { + + IrpContext = FatCreateIrpContext( Irp, CanFsdWait( Irp )); + + Status = FatCommonDeviceControl( IrpContext, Irp ); + + } except(FatExceptionFilter( IrpContext, GetExceptionInformation() )) { + + // + // We had some trouble trying to perform the requested + // operation, so we'll abort the I/O request with + // the error status that we get back from the + // execption code + // + + Status = FatProcessException( IrpContext, Irp, GetExceptionCode() ); + } + + if (TopLevel) { IoSetTopLevelIrp( NULL ); } + + FsRtlExitFileSystem(); + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "FatFsdDeviceControl -> %08lx\n", Status); + + UNREFERENCED_PARAMETER( VolumeDeviceObject ); + + return Status; +} + + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatCommonDeviceControl ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the common routine for doing Device control operations called + by both the fsd and fsp threads + +Arguments: + + Irp - Supplies the Irp to process + + InFsp - Indicates if this is the fsp thread or someother thread + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status; + PIO_STACK_LOCATION IrpSp; + KEVENT WaitEvent; + PVOID CompletionContext = NULL; + + PVCB Vcb; + PFCB Fcb; + PCCB Ccb; + + PAGED_CODE(); + + // + // Get a pointer to the current Irp stack location + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "FatCommonDeviceControl\n", 0); + DebugTrace( 0, Dbg, "Irp = %p\n", Irp); + DebugTrace( 0, Dbg, "MinorFunction = %08lx\n", IrpSp->MinorFunction); + + // + // Decode the file object, the only type of opens we accept are + // user volume opens. + // + + if (FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb ) != UserVolumeOpen) { + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + + DebugTrace(-1, Dbg, "FatCommonDeviceControl -> %08lx\n", STATUS_INVALID_PARAMETER); + return STATUS_INVALID_PARAMETER; + } + + // + // A few IOCTLs actually require some intervention on our part + // + + switch (IrpSp->Parameters.DeviceIoControl.IoControlCode) { + + case IOCTL_VOLSNAP_FLUSH_AND_HOLD_WRITES: + + // + // This is sent by the Volume Snapshot driver (Lovelace). + // We flush the volume, and hold all file resources + // to make sure that nothing more gets dirty. Then we wait + // for the IRP to complete or cancel. + // + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ); + FatAcquireExclusiveVolume( IrpContext, Vcb ); + + FatFlushAndCleanVolume( IrpContext, + Irp, + Vcb, + FlushWithoutPurge ); + + KeInitializeEvent( &WaitEvent, NotificationEvent, FALSE ); + CompletionContext = &WaitEvent; + + // + // Get the next stack location, and copy over the stack location + // + + IoCopyCurrentIrpStackLocationToNext( Irp ); + + // + // Set up the completion routine + // + + IoSetCompletionRoutine( Irp, + FatDeviceControlCompletionRoutine, + CompletionContext, + TRUE, + TRUE, + TRUE ); + break; + + case IOCTL_DISK_COPY_DATA: + + // + // We cannot allow this IOCTL to be sent unless the volume is locked, + // since this IOCTL allows direct writing of data to the volume. + // We do allow kernel callers to force access via a flag. A handle that + // issued a dismount can send this IOCTL as well. + // + + if (!FlagOn( Vcb->VcbState, VCB_STATE_FLAG_LOCKED ) && + !FlagOn( IrpSp->Flags, SL_FORCE_DIRECT_WRITE ) && + !FlagOn( Ccb->Flags, CCB_FLAG_COMPLETE_DISMOUNT )) { + + FatCompleteRequest( IrpContext, + Irp, + STATUS_ACCESS_DENIED ); + + DebugTrace(-1, Dbg, "FatCommonDeviceControl -> %08lx\n", STATUS_ACCESS_DENIED); + return STATUS_ACCESS_DENIED; + } + + break; + + case IOCTL_SCSI_PASS_THROUGH: + case IOCTL_SCSI_PASS_THROUGH_DIRECT: + case IOCTL_SCSI_PASS_THROUGH_EX: + case IOCTL_SCSI_PASS_THROUGH_DIRECT_EX: + + // + // If someone is issuing a format unit command underneath us, then make + // sure we mark the device as needing verification when they close their + // handle. + // + + if ((!FlagOn( IrpSp->FileObject->Flags, FO_FILE_MODIFIED ) || + !FlagOn( Ccb->Flags, CCB_FLAG_SENT_FORMAT_UNIT )) && + (Irp->AssociatedIrp.SystemBuffer != NULL)) { + + PCDB Cdb = NULL; + + // + // If this is a 32 bit application running on 64 bit then thunk the + // input structures to grab the Cdb. + // + +#if defined (_WIN64) && defined(BUILD_WOW64_ENABLED) + if (IoIs32bitProcess(Irp)) { + + if ( (IrpSp->Parameters.DeviceIoControl.IoControlCode == IOCTL_SCSI_PASS_THROUGH) || + (IrpSp->Parameters.DeviceIoControl.IoControlCode == IOCTL_SCSI_PASS_THROUGH_DIRECT )) { + + if (IrpSp->Parameters.DeviceIoControl.InputBufferLength >= sizeof( SCSI_PASS_THROUGH32 )) { + + Cdb = (PCDB)((PSCSI_PASS_THROUGH32)(Irp->AssociatedIrp.SystemBuffer))->Cdb; + } + } else { + + if (IrpSp->Parameters.DeviceIoControl.InputBufferLength >= sizeof( SCSI_PASS_THROUGH32_EX )) { + + Cdb = (PCDB)((PSCSI_PASS_THROUGH32_EX)(Irp->AssociatedIrp.SystemBuffer))->Cdb; + } + } + + } else { +#endif + if ( (IrpSp->Parameters.DeviceIoControl.IoControlCode == IOCTL_SCSI_PASS_THROUGH) || + (IrpSp->Parameters.DeviceIoControl.IoControlCode == IOCTL_SCSI_PASS_THROUGH_DIRECT )) { + + if (IrpSp->Parameters.DeviceIoControl.InputBufferLength >= sizeof( SCSI_PASS_THROUGH )) { + + Cdb = (PCDB)((PSCSI_PASS_THROUGH)(Irp->AssociatedIrp.SystemBuffer))->Cdb; + } + } else { + + if (IrpSp->Parameters.DeviceIoControl.InputBufferLength >= sizeof( SCSI_PASS_THROUGH_EX )) { + + Cdb = (PCDB)((PSCSI_PASS_THROUGH_EX)(Irp->AssociatedIrp.SystemBuffer))->Cdb; + } + } + +#if defined (_WIN64) && defined(BUILD_WOW64_ENABLED) + } +#endif + + if ((Cdb != NULL) && (Cdb->AsByte[0] == SCSIOP_FORMAT_UNIT)) { + + SetFlag( Ccb->Flags, CCB_FLAG_SENT_FORMAT_UNIT ); + SetFlag( IrpSp->FileObject->Flags, FO_FILE_MODIFIED ); + } + } + + // + // Fall through as we do not need to know the outcome of this operation. + // + + default: + + // + // FAT doesn't need to see this on the way back, so skip ourselves. + // + + IoSkipCurrentIrpStackLocation( Irp ); + break; + } + + // + // Send the request. + // + + Status = IoCallDriver(Vcb->TargetDeviceObject, Irp); + + if (Status == STATUS_PENDING && CompletionContext) { + + KeWaitForSingleObject( &WaitEvent, + Executive, + KernelMode, + FALSE, + NULL ); + + Status = Irp->IoStatus.Status; + } + + // + // If we had a context, the IRP remains for us and we will complete it. + // Handle it appropriately. + // + + if (CompletionContext) { + + // + // Release all the resources that we held because of a + // VOLSNAP_FLUSH_AND_HOLD. + // + + NT_ASSERT( IrpSp->Parameters.DeviceIoControl.IoControlCode == IOCTL_VOLSNAP_FLUSH_AND_HOLD_WRITES ); + + FatReleaseVolume( IrpContext, Vcb ); + + // + // If we had no context, the IRP will complete asynchronously. + // + + } else { + + Irp = NULL; + } + + FatCompleteRequest( IrpContext, Irp, Status ); + + DebugTrace(-1, Dbg, "FatCommonDeviceControl -> %08lx\n", Status); + + return Status; +} + + +// +// Local support routine +// + +NTSTATUS +FatDeviceControlCompletionRoutine( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PIRP Irp, + _In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt + ) + +{ + PKEVENT Event = (PKEVENT) Contxt; + + // + // If there is an event, this is a synch request. Signal and + // let I/O know this isn't done yet. + // + + if (Event) { + + KeSetEvent( Event, 0, FALSE ); + return STATUS_MORE_PROCESSING_REQUIRED; + } + + UNREFERENCED_PARAMETER( DeviceObject ); + UNREFERENCED_PARAMETER( Irp ); + + return STATUS_SUCCESS; +} + diff --git a/filesys/fastfat/deviosup.c b/filesys/fastfat/deviosup.c new file mode 100644 index 000000000..ee46c0166 --- /dev/null +++ b/filesys/fastfat/deviosup.c @@ -0,0 +1,3697 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + DevIoSup.c + +Abstract: + + This module implements the low lever disk read/write support for Fat. + + +--*/ + +#include "FatProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (FAT_BUG_CHECK_DEVIOSUP) + +// +// Local debug trace level +// + +#define Dbg (DEBUG_TRACE_DEVIOSUP) + +#define CollectDiskIoStats(VCB,FUNCTION,IS_USER_IO,COUNT) { \ + PFILESYSTEM_STATISTICS Stats = &(VCB)->Statistics[KeGetCurrentProcessorNumber() % FatData.NumberProcessors].Common; \ + if (IS_USER_IO) { \ + if ((FUNCTION) == IRP_MJ_WRITE) { \ + Stats->UserDiskWrites += (COUNT); \ + } else { \ + Stats->UserDiskReads += (COUNT); \ + } \ + } else { \ + if ((FUNCTION) == IRP_MJ_WRITE) { \ + Stats->MetaDataDiskWrites += (COUNT); \ + } else { \ + Stats->MetaDataDiskReads += (COUNT); \ + } \ + } \ +} + +typedef struct _FAT_SYNC_CONTEXT { + + // + // Io status block for the request + // + + IO_STATUS_BLOCK Iosb; + + // + // Event to be signaled when the request completes + // + + KEVENT Event; + +} FAT_SYNC_CONTEXT, *PFAT_SYNC_CONTEXT; + + +// +// Completion Routine declarations +// + +IO_COMPLETION_ROUTINE FatMultiSyncCompletionRoutine; + +NTSTATUS +FatMultiSyncCompletionRoutine ( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PIRP Irp, + _In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt + ); + +IO_COMPLETION_ROUTINE FatMultiAsyncCompletionRoutine; + +NTSTATUS +FatMultiAsyncCompletionRoutine ( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PIRP Irp, + _In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt + ); + +IO_COMPLETION_ROUTINE FatSpecialSyncCompletionRoutine; + +NTSTATUS +FatSpecialSyncCompletionRoutine ( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PIRP Irp, + _In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt + ); + +IO_COMPLETION_ROUTINE FatSingleSyncCompletionRoutine; + +NTSTATUS +FatSingleSyncCompletionRoutine ( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PIRP Irp, + _In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt + ); + +IO_COMPLETION_ROUTINE FatSingleAsyncCompletionRoutine; + +NTSTATUS +FatSingleAsyncCompletionRoutine ( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PIRP Irp, + _In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt + ); + +IO_COMPLETION_ROUTINE FatPagingFileCompletionRoutine; + +NTSTATUS +FatPagingFileCompletionRoutine ( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PIRP Irp, + _In_reads_opt_(_Inexpressible_("varies")) PVOID MasterIrp + ); + +IO_COMPLETION_ROUTINE FatPagingFileCompletionRoutineCatch; + +NTSTATUS +FatPagingFileCompletionRoutineCatch ( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PIRP Irp, + _In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt + ); + +VOID +FatSingleNonAlignedSync ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PUCHAR Buffer, + IN LBO Lbo, + IN ULONG ByteCount, + IN PIRP Irp + ); + +// +// The following macro decides whether to send a request directly to +// the device driver, or to other routines. It was meant to +// replace IoCallDriver as transparently as possible. It must only be +// called with a read or write Irp. +// +// NTSTATUS +// FatLowLevelReadWrite ( +// PIRP_CONTEXT IrpContext, +// PDEVICE_OBJECT DeviceObject, +// PIRP Irp, +// PVCB Vcb +// ); +// + +#define FatLowLevelReadWrite(IRPCONTEXT,DO,IRP,VCB) ( \ + IoCallDriver((DO),(IRP)) \ +) + +// +// The following macro handles completion-time zeroing of buffers. +// + +#define FatDoCompletionZero( I, C ) \ + if ((C)->ZeroMdl) { \ + NT_ASSERT( (C)->ZeroMdl->MdlFlags & (MDL_MAPPED_TO_SYSTEM_VA | \ + MDL_SOURCE_IS_NONPAGED_POOL));\ + if (NT_SUCCESS((I)->IoStatus.Status)) { \ + RtlZeroMemory( (C)->ZeroMdl->MappedSystemVa, \ + (C)->ZeroMdl->ByteCount ); \ + } \ + IoFreeMdl((C)->ZeroMdl); \ + (C)->ZeroMdl = NULL; \ + } + +#if (NTDDI_VERSION >= NTDDI_WIN8) +#define FatUpdateIOCountersPCW(IsAWrite,Count) \ + FsRtlUpdateDiskCounters( ((IsAWrite) ? 0 : (Count) ), \ + ((IsAWrite) ? (Count) : 0) ) +#else +#define FatUpdateIOCountersPCW(IsAWrite,Count) +#endif + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, FatMultipleAsync) +#pragma alloc_text(PAGE, FatSingleAsync) +#pragma alloc_text(PAGE, FatSingleNonAlignedSync) +#pragma alloc_text(PAGE, FatWaitSync) +#pragma alloc_text(PAGE, FatLockUserBuffer) +#pragma alloc_text(PAGE, FatBufferUserBuffer) +#pragma alloc_text(PAGE, FatMapUserBuffer) +#pragma alloc_text(PAGE, FatNonCachedIo) +#pragma alloc_text(PAGE, FatNonCachedNonAlignedRead) +#pragma alloc_text(PAGE, FatPerformDevIoCtrl) +#endif + +typedef struct FAT_PAGING_FILE_CONTEXT { + KEVENT Event; + PMDL RestoreMdl; +} FAT_PAGING_FILE_CONTEXT, *PFAT_PAGING_FILE_CONTEXT; + + +VOID +FatPagingFileIo ( + IN PIRP Irp, + IN PFCB Fcb + ) + +/*++ + +Routine Description: + + This routine performs the non-cached disk io described in its parameters. + This routine nevers blocks, and should only be used with the paging + file since no completion processing is performed. + +Arguments: + + Irp - Supplies the requesting Irp. + + Fcb - Supplies the file to act on. + +Return Value: + + None. + +--*/ + +{ + // + // Declare some local variables for enumeration through the + // runs of the file. + // + + VBO Vbo; + ULONG ByteCount; + + PMDL Mdl; + LBO NextLbo; + VBO NextVbo = 0; + ULONG NextByteCount; + ULONG RemainingByteCount; + BOOLEAN MustSucceed; + + ULONG FirstIndex; + ULONG CurrentIndex; + ULONG LastIndex; + + LBO LastLbo; + ULONG LastByteCount; + + BOOLEAN MdlIsReserve = FALSE; + BOOLEAN IrpIsMaster = FALSE; + FAT_PAGING_FILE_CONTEXT Context; + LONG IrpCount; + + PIRP AssocIrp; + PIO_STACK_LOCATION IrpSp; + PIO_STACK_LOCATION NextIrpSp; + ULONG BufferOffset; + PDEVICE_OBJECT DeviceObject; + + BOOLEAN IsAWrite = FALSE; + + DebugTrace(+1, Dbg, "FatPagingFileIo\n", 0); + DebugTrace( 0, Dbg, "Irp = %p\n", Irp ); + DebugTrace( 0, Dbg, "Fcb = %p\n", Fcb ); + + NT_ASSERT( FlagOn( Fcb->FcbState, FCB_STATE_PAGING_FILE )); + + // + // Initialize some locals. + // + + BufferOffset = 0; + DeviceObject = Fcb->Vcb->TargetDeviceObject; + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + Vbo = IrpSp->Parameters.Read.ByteOffset.LowPart; + ByteCount = IrpSp->Parameters.Read.Length; + IsAWrite = (IrpSp->MajorFunction == IRP_MJ_WRITE); + + MustSucceed = FatLookupMcbEntry( Fcb->Vcb, &Fcb->Mcb, + Vbo, + &NextLbo, + &NextByteCount, + &FirstIndex); + + // + // If this run isn't present, something is very wrong. + // + + if (!MustSucceed) { + +#pragma prefast( suppress:28159, "things are seriously wrong if we get here" ) + FatBugCheck( Vbo, ByteCount, 0 ); + } + +#if (NTDDI_VERSION >= NTDDI_WIN8) + + // + // Charge the IO to paging file to current thread + // + + if (FatDiskAccountingEnabled) { + + PETHREAD ThreadIssuingIo = PsGetCurrentThread(); + BOOLEAN IsWriteOperation = FALSE; + + if (IrpSp->MajorFunction == IRP_MJ_WRITE) { + IsWriteOperation = TRUE; + } + + PsUpdateDiskCounters( PsGetThreadProcess( ThreadIssuingIo ), + (IsWriteOperation ? 0 : ByteCount ), // bytes to read + (IsWriteOperation ? ByteCount : 0), // bytes to write + (IsWriteOperation ? 0 : 1), // # of reads + (IsWriteOperation ? 1 : 0), // # of writes + 0 ); + } +#endif + + // See if the write covers a single valid run, and if so pass + // it on. + // + + if ( NextByteCount >= ByteCount ) { + + DebugTrace( 0, Dbg, "Passing Irp on to Disk Driver\n", 0 ); + + // + // Setup the next IRP stack location for the disk driver beneath us. + // + + NextIrpSp = IoGetNextIrpStackLocation( Irp ); + + NextIrpSp->MajorFunction = IrpSp->MajorFunction; + NextIrpSp->Parameters.Read.Length = ByteCount; + NextIrpSp->Parameters.Read.ByteOffset.QuadPart = NextLbo; + + // + // Since this is Paging file IO, we'll just ignore the verify bit. + // + + SetFlag( NextIrpSp->Flags, SL_OVERRIDE_VERIFY_VOLUME ); + + // + // Set up the completion routine address in our stack frame. + // This is only invoked on error or cancel, and just copies + // the error Status into master irp's iosb. + // + // If the error implies a media problem, it also enqueues a + // worker item to write out the dirty bit so that the next + // time we run we will do a autochk /r + // + + IoSetCompletionRoutine( Irp, + &FatPagingFileCompletionRoutine, + Irp, + FALSE, + TRUE, + TRUE ); + + // + // Issue the read/write request + // + // If IoCallDriver returns an error, it has completed the Irp + // and the error will be dealt with as a normal IO error. + // + + (VOID)IoCallDriver( DeviceObject, Irp ); + + // + // We just issued an IO to the storage stack, update the counters indicating so. + // + + if (FatDiskAccountingEnabled) { + + FatUpdateIOCountersPCW( IsAWrite, ByteCount ); + } + + DebugTrace(-1, Dbg, "FatPagingFileIo -> VOID\n", 0); + return; + } + + // + // Find out how may runs there are. + // + + MustSucceed = FatLookupMcbEntry( Fcb->Vcb, &Fcb->Mcb, + Vbo + ByteCount - 1, + &LastLbo, + &LastByteCount, + &LastIndex); + + // + // If this run isn't present, something is very wrong. + // + + if (!MustSucceed) { + +#pragma prefast( suppress:28159, "things are seriously wrong if we get here" ) + FatBugCheck( Vbo + ByteCount - 1, 1, 0 ); + } + + CurrentIndex = FirstIndex; + + // + // Now set up the Irp->IoStatus. It will be modified by the + // multi-completion routine in case of error or verify required. + // + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = ByteCount; + + // + // Loop while there are still byte writes to satisfy. The way we'll work this + // is to hope for the best - one associated IRP per run, which will let us be + // completely async after launching all the IO. + // + // IrpCount will indicate the remaining number of associated Irps to launch. + // + // All we have to do is make sure IrpCount doesn't hit zero before we're building + // the very last Irp. If it is positive when we're done, it means we have to + // wait for the rest of the associated Irps to come back before we complete the + // master by hand. + // + // This will keep the master from completing early. + // + + Irp->AssociatedIrp.IrpCount = IrpCount = LastIndex - FirstIndex + 1; + + while (CurrentIndex <= LastIndex) { + + // + // Reset this for unwinding purposes + // + + AssocIrp = NULL; + + // + // If next run is larger than we need, "ya get what ya need". + // + + if (NextByteCount > ByteCount) { + NextByteCount = ByteCount; + } + + RemainingByteCount = 0; + + // + // Allocate and build a partial Mdl for the request. + // + + Mdl = IoAllocateMdl( (PCHAR)Irp->UserBuffer + BufferOffset, + NextByteCount, + FALSE, + FALSE, + AssocIrp ); + + if (Mdl == NULL) { + + // + // Pick up the reserve MDL + // + + KeWaitForSingleObject( &FatReserveEvent, Executive, KernelMode, FALSE, NULL ); + + Mdl = FatReserveMdl; + MdlIsReserve = TRUE; + + // + // Trim to fit the size of the reserve MDL. + // + + if (NextByteCount > FAT_RESERVE_MDL_SIZE * PAGE_SIZE) { + + RemainingByteCount = NextByteCount - FAT_RESERVE_MDL_SIZE * PAGE_SIZE; + NextByteCount = FAT_RESERVE_MDL_SIZE * PAGE_SIZE; + } + } + + IoBuildPartialMdl( Irp->MdlAddress, + Mdl, + (PCHAR)Irp->UserBuffer + BufferOffset, + NextByteCount ); + + // + // Now that we have properly bounded this piece of the transfer, it is + // time to read/write it. We can simplify life slightly by always + // re-using the master IRP for cases where we use the reserve MDL, + // since we'll always be synchronous for those and can use a single + // completion context on our local stack. + // + // We also must prevent ourselves from issuing an associated IRP that would + // complete the master UNLESS this is the very last IRP we'll issue. + // + // This logic looks a bit complicated, but is hopefully understandable. + // + + if (!MdlIsReserve && + (IrpCount != 1 || + (CurrentIndex == LastIndex && + RemainingByteCount == 0))) { + + AssocIrp = IoMakeAssociatedIrp( Irp, (CCHAR)(DeviceObject->StackSize + 1) ); + } + + if (AssocIrp == NULL) { + + AssocIrp = Irp; + IrpIsMaster = TRUE; + + // + // We need to drain the associated Irps so we can reliably figure out if + // the master Irp is showing a failed status, in which case we bail out + // immediately - as opposed to putting the value in the status field in + // jeopardy due to our re-use of the master Irp. + // + + while (Irp->AssociatedIrp.IrpCount != IrpCount) { + + KeDelayExecutionThread (KernelMode, FALSE, &Fat30Milliseconds); + } + + // + // Note that since we failed to launch this associated Irp, that the completion + // code at the bottom will take care of completing the master Irp. + // + + if (!NT_SUCCESS(Irp->IoStatus.Status)) { + + NT_ASSERT( IrpCount ); + break; + } + + } else { + + // + // Indicate we used an associated Irp. + // + + IrpCount -= 1; + } + + // + // With an associated IRP, we must take over the first stack location so + // we can have one to put the completion routine on. When re-using the + // master IRP, its already there. + // + + if (!IrpIsMaster) { + + // + // Get the first IRP stack location in the associated Irp + // + + IoSetNextIrpStackLocation( AssocIrp ); + NextIrpSp = IoGetCurrentIrpStackLocation( AssocIrp ); + + // + // Setup the Stack location to describe our read. + // + + NextIrpSp->MajorFunction = IrpSp->MajorFunction; + NextIrpSp->Parameters.Read.Length = NextByteCount; + NextIrpSp->Parameters.Read.ByteOffset.QuadPart = Vbo; + + // + // We also need the VolumeDeviceObject in the Irp stack in case + // we take the failure path. + // + + NextIrpSp->DeviceObject = IrpSp->DeviceObject; + + } else { + + // + // Save the MDL in the IRP and prepare the stack + // context for the completion routine. + // + + KeInitializeEvent( &Context.Event, SynchronizationEvent, FALSE ); + Context.RestoreMdl = Irp->MdlAddress; + } + + // + // And drop our Mdl into the Irp. + // + + AssocIrp->MdlAddress = Mdl; + + // + // Set up the completion routine address in our stack frame. + // For true associated IRPs, this is only invoked on error or + // cancel, and just copies the error Status into master irp's + // iosb. + // + // If the error implies a media problem, it also enqueues a + // worker item to write out the dirty bit so that the next + // time we run we will do a autochk /r + // + + if (IrpIsMaster) { + + IoSetCompletionRoutine( AssocIrp, + FatPagingFileCompletionRoutineCatch, + &Context, + TRUE, + TRUE, + TRUE ); + + } else { + + IoSetCompletionRoutine( AssocIrp, + FatPagingFileCompletionRoutine, + Irp, + FALSE, + TRUE, + TRUE ); + } + + // + // Setup the next IRP stack location for the disk driver beneath us. + // + + NextIrpSp = IoGetNextIrpStackLocation( AssocIrp ); + + // + // Since this is paging file IO, we'll just ignore the verify bit. + // + + SetFlag( NextIrpSp->Flags, SL_OVERRIDE_VERIFY_VOLUME ); + + // + // Setup the Stack location to do a read from the disk driver. + // + + NextIrpSp->MajorFunction = IrpSp->MajorFunction; + NextIrpSp->Parameters.Read.Length = NextByteCount; + NextIrpSp->Parameters.Read.ByteOffset.QuadPart = NextLbo; + + (VOID)IoCallDriver( DeviceObject, AssocIrp ); + + // + // We just issued an IO to the storage stack, update the counters indicating so. + // + + if (FatDiskAccountingEnabled) { + + FatUpdateIOCountersPCW( IsAWrite, (ULONG64)NextByteCount ); + } + + // + // Wait for the Irp in the catch case and drop the flags. + // + + if (IrpIsMaster) { + + KeWaitForSingleObject( &Context.Event, Executive, KernelMode, FALSE, NULL ); + IrpIsMaster = MdlIsReserve = FALSE; + + // + // If the Irp is showing a failed status, there is no point in continuing. + // In doing so, we get to avoid squirreling away the failed status in case + // we were to re-use the master irp again. + // + // Note that since we re-used the master, we must not have issued the "last" + // associated Irp, and thus the completion code at the bottom will take care + // of that for us. + // + + if (!NT_SUCCESS(Irp->IoStatus.Status)) { + + NT_ASSERT( IrpCount ); + break; + } + } + + // + // Now adjust everything for the next pass through the loop. + // + + Vbo += NextByteCount; + BufferOffset += NextByteCount; + ByteCount -= NextByteCount; + + // + // Try to lookup the next run, if we are not done and we got + // all the way through the current run. + // + + if (RemainingByteCount) { + + // + // Advance the Lbo/Vbo if we have more to do in the current run. + // + + NextLbo += NextByteCount; + NextVbo += NextByteCount; + + NextByteCount = RemainingByteCount; + + } else { + + CurrentIndex += 1; + + if ( CurrentIndex <= LastIndex ) { + + NT_ASSERT( ByteCount != 0 ); + + FatGetNextMcbEntry( Fcb->Vcb, &Fcb->Mcb, + CurrentIndex, + &NextVbo, + &NextLbo, + &NextByteCount ); + + NT_ASSERT( NextVbo == Vbo ); + } + } + } // while ( CurrentIndex <= LastIndex ) + + // + // If we didn't get enough associated Irps going to make this asynchronous, we + // twiddle our thumbs and wait for those we did launch to complete. + // + + if (IrpCount) { + + while (Irp->AssociatedIrp.IrpCount != IrpCount) { + + KeDelayExecutionThread (KernelMode, FALSE, &Fat30Milliseconds); + } + + IoCompleteRequest( Irp, IO_DISK_INCREMENT ); + } + + DebugTrace(-1, Dbg, "FatPagingFileIo -> VOID\n", 0); + return; +} + +#if (NTDDI_VERSION >= NTDDI_WIN8) + +VOID +FatUpdateDiskStats ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp, + IN ULONG ByteCount + ) +/*++ + +Routine Description: + + Charge appropriate process for the IO this IRP will cause. + +Arguments: + + IrpContext- The Irp Context + + Irp - Supplies the requesting Irp. + + ByteCount - The lengh of the operation. + +Return Value: + + None. + +--*/ + +{ + PETHREAD OriginatingThread = NULL; + ULONG NumReads = 0; + ULONG NumWrites = 0; + ULONGLONG BytesToRead = 0; + ULONGLONG BytesToWrite = 0; + + // + // Here we attempt to charge the IO back to the originating process. + // - These checks are intended to cover following cases: + // o Buffered sync reads + // o Unbuffered sync read + // o Inline metadata reads + // o memory mapped reads (in-line faulting of data) + // + + if (IrpContext->MajorFunction == IRP_MJ_READ) { + + NumReads++; + BytesToRead = ByteCount; + + if ((Irp->Tail.Overlay.Thread != NULL) && + !IoIsSystemThread( Irp->Tail.Overlay.Thread )) { + + OriginatingThread = Irp->Tail.Overlay.Thread; + + } else if (!IoIsSystemThread( PsGetCurrentThread() )) { + + OriginatingThread = PsGetCurrentThread(); + + // + // We couldn't find a non-system entity, so this should be charged to system. + // Do so only if we are top level. + // If we are not top-level then the read was initiated by someone like Cc (read ahead) + // who should have already accounted for this IO. + // + + } else if (IoIsSystemThread( PsGetCurrentThread() ) && + (IoGetTopLevelIrp() == Irp)) { + + OriginatingThread = PsGetCurrentThread(); + } + + // + // Charge the write to Originating process. + // Intended to cover the following writes: + // - Unbuffered sync write + // - unbuffered async write + // + // If we re not top-level, then it should already have been accounted for + // somewhere else (Cc). + // + + } else if (IrpContext->MajorFunction == IRP_MJ_WRITE) { + + NumWrites++; + BytesToWrite = ByteCount; + + if (IoGetTopLevelIrp() == Irp) { + + if ((Irp->Tail.Overlay.Thread != NULL) && + !IoIsSystemThread( Irp->Tail.Overlay.Thread )) { + + OriginatingThread = Irp->Tail.Overlay.Thread; + + } else { + + OriginatingThread = PsGetCurrentThread(); + } + + // + // For mapped page writes + // + + } else if (IoGetTopLevelIrp() == (PIRP)FSRTL_MOD_WRITE_TOP_LEVEL_IRP) { + + OriginatingThread = PsGetCurrentThread(); + } + } + + if (OriginatingThread != NULL) { + + PsUpdateDiskCounters( PsGetThreadProcess( OriginatingThread ), + BytesToRead, + BytesToWrite, + NumReads, + NumWrites, + 0 ); + } +} + +#endif + + + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatNonCachedIo ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp, + IN PFCB FcbOrDcb, + IN ULONG StartingVbo, + IN ULONG ByteCount, + IN ULONG UserByteCount, + IN ULONG StreamFlags + ) +/*++ + +Routine Description: + + This routine performs the non-cached disk io described in its parameters. + The choice of a single run is made if possible, otherwise multiple runs + are executed. + +Arguments: + + IrpContext->MajorFunction - Supplies either IRP_MJ_READ or IRP_MJ_WRITE. + + Irp - Supplies the requesting Irp. + + FcbOrDcb - Supplies the file to act on. + + StartingVbo - The starting point for the operation. + + ByteCount - The lengh of the operation. + + UserByteCount - The last byte the user can see, rest to be zeroed. + + StreamFlags - flag to indicate special attributes for a NonCachedIo. + +Return Value: + + None. + +--*/ + +{ + + // + // Declare some local variables for enumeration through the + // runs of the file, and an array to store parameters for + // parallel I/Os + // + + BOOLEAN Wait; + + LBO NextLbo; + VBO NextVbo; + ULONG NextByteCount; + BOOLEAN NextIsAllocated; + + LBO LastLbo; + ULONG LastByteCount; + BOOLEAN LastIsAllocated; + + BOOLEAN EndOnMax; + + ULONG FirstIndex; + ULONG CurrentIndex; + ULONG LastIndex; + + ULONG NextRun; + ULONG BufferOffset; + ULONG OriginalByteCount; + + + + IO_RUN StackIoRuns[FAT_MAX_IO_RUNS_ON_STACK]; + PIO_RUN IoRuns; + + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( StreamFlags ); + + DebugTrace(+1, Dbg, "FatNonCachedIo\n", 0); + DebugTrace( 0, Dbg, "Irp = %p\n", Irp ); + DebugTrace( 0, Dbg, "MajorFunction = %08lx\n", IrpContext->MajorFunction ); + DebugTrace( 0, Dbg, "FcbOrDcb = %p\n", FcbOrDcb ); + DebugTrace( 0, Dbg, "StartingVbo = %08lx\n", StartingVbo ); + DebugTrace( 0, Dbg, "ByteCount = %08lx\n", ByteCount ); + + if (!FlagOn(Irp->Flags, IRP_PAGING_IO)) { + + PFILE_SYSTEM_STATISTICS Stats = + &FcbOrDcb->Vcb->Statistics[KeGetCurrentProcessorNumber() % FatData.NumberProcessors]; + + if (IrpContext->MajorFunction == IRP_MJ_READ) { + Stats->Fat.NonCachedReads += 1; + Stats->Fat.NonCachedReadBytes += ByteCount; + } else { + Stats->Fat.NonCachedWrites += 1; + Stats->Fat.NonCachedWriteBytes += ByteCount; + } + } + + // + // Initialize some locals. + // + + NextRun = 0; + BufferOffset = 0; + OriginalByteCount = ByteCount; + + Wait = BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT); + +#if (NTDDI_VERSION >= NTDDI_WIN8) + + // + // Disk IO accounting + // + + if (FatDiskAccountingEnabled) { + + FatUpdateDiskStats( IrpContext, + Irp, + ByteCount ); + } +#endif + + // + // For nonbuffered I/O, we need the buffer locked in all + // cases. + // + // This call may raise. If this call succeeds and a subsequent + // condition is raised, the buffers are unlocked automatically + // by the I/O system when the request is completed, via the + // Irp->MdlAddress field. + // + + FatLockUserBuffer( IrpContext, + Irp, + (IrpContext->MajorFunction == IRP_MJ_READ) ? + IoWriteAccess : IoReadAccess, + ByteCount ); + + + + // + // No zeroing for trailing sectors if requested. + // Otherwise setup the required zeroing for read requests. + // + + + if (UserByteCount != ByteCount) { + + + PMDL Mdl; + + NT_ASSERT( ByteCount > UserByteCount ); + _Analysis_assume_(ByteCount > UserByteCount); + + Mdl = IoAllocateMdl( (PUCHAR) Irp->UserBuffer + UserByteCount, + ByteCount - UserByteCount, + FALSE, + FALSE, + NULL ); + + if (Mdl == NULL) { + + FatRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES ); + } + + IoBuildPartialMdl( Irp->MdlAddress, + Mdl, + (PUCHAR) Irp->UserBuffer + UserByteCount, + ByteCount - UserByteCount ); + + IrpContext->FatIoContext->ZeroMdl = Mdl; + + // + // Map the MDL now so we can't fail at IO completion time. Note + // that this will be only a single page. + // + + if (MmGetSystemAddressForMdlSafe( Mdl, NormalPagePriority ) == NULL) { + + FatRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES ); + } + } + + + // + // Try to lookup the first run. If there is just a single run, + // we may just be able to pass it on. + // + + FatLookupFileAllocation( IrpContext, + FcbOrDcb, + StartingVbo, + &NextLbo, + &NextByteCount, + &NextIsAllocated, + &EndOnMax, + &FirstIndex ); + + // + // We just added the allocation, thus there must be at least + // one entry in the mcb corresponding to our write, ie. + // NextIsAllocated must be true. If not, the pre-existing file + // must have an allocation error. + // + + if ( !NextIsAllocated ) { + + FatPopUpFileCorrupt( IrpContext, FcbOrDcb ); + + FatRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); + } + + NT_ASSERT( NextByteCount != 0 ); + + // + // If the request was not aligned correctly, read in the first + // part first. + // + + + // + // See if the write covers a single valid run, and if so pass + // it on. We must bias this by the byte that is lost at the + // end of the maximal file. + // + + if ( NextByteCount >= ByteCount - (EndOnMax ? 1 : 0)) { + + if (FlagOn(Irp->Flags, IRP_PAGING_IO)) { + CollectDiskIoStats(FcbOrDcb->Vcb, IrpContext->MajorFunction, + FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_USER_IO), 1); + } else { + + PFILE_SYSTEM_STATISTICS Stats = + &FcbOrDcb->Vcb->Statistics[KeGetCurrentProcessorNumber() % FatData.NumberProcessors]; + + if (IrpContext->MajorFunction == IRP_MJ_READ) { + Stats->Fat.NonCachedDiskReads += 1; + } else { + Stats->Fat.NonCachedDiskWrites += 1; + } + } + + DebugTrace( 0, Dbg, "Passing 1 Irp on to Disk Driver\n", 0 ); + + FatSingleAsync( IrpContext, + FcbOrDcb->Vcb, + NextLbo, + ByteCount, + Irp ); + + } else { + + // + // If there we can't wait, and there are more runs than we can handle, + // we will have to post this request. + // + + FatLookupFileAllocation( IrpContext, + FcbOrDcb, + StartingVbo + ByteCount - 1, + &LastLbo, + &LastByteCount, + &LastIsAllocated, + &EndOnMax, + &LastIndex ); + + // + // Since we already added the allocation for the whole + // write, assert that we find runs until ByteCount == 0 + // Otherwise this file is corrupt. + // + + if ( !LastIsAllocated ) { + + FatPopUpFileCorrupt( IrpContext, FcbOrDcb ); + + FatRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); + } + + if (LastIndex - FirstIndex + 1 > FAT_MAX_IO_RUNS_ON_STACK) { + + IoRuns = FsRtlAllocatePoolWithTag( PagedPool, + (LastIndex - FirstIndex + 1) * sizeof(IO_RUN), + TAG_IO_RUNS ); + + } else { + + IoRuns = StackIoRuns; + } + + NT_ASSERT( LastIndex != FirstIndex ); + + CurrentIndex = FirstIndex; + + // + // Loop while there are still byte writes to satisfy. + // + + while (CurrentIndex <= LastIndex) { + + + NT_ASSERT( NextByteCount != 0); + NT_ASSERT( ByteCount != 0); + + // + // If next run is larger than we need, "ya get what you need". + // + + if (NextByteCount > ByteCount) { + NextByteCount = ByteCount; + } + + // + // Now that we have properly bounded this piece of the + // transfer, it is time to write it. + // + // We remember each piece of a parallel run by saving the + // essential information in the IoRuns array. The tranfers + // are started up in parallel below. + // + + IoRuns[NextRun].Vbo = StartingVbo; + IoRuns[NextRun].Lbo = NextLbo; + IoRuns[NextRun].Offset = BufferOffset; + IoRuns[NextRun].ByteCount = NextByteCount; + NextRun += 1; + + // + // Now adjust everything for the next pass through the loop. + // + + StartingVbo += NextByteCount; + BufferOffset += NextByteCount; + ByteCount -= NextByteCount; + + // + // Try to lookup the next run (if we are not done). + // + + CurrentIndex += 1; + + if ( CurrentIndex <= LastIndex ) { + + NT_ASSERT( ByteCount != 0 ); + + FatGetNextMcbEntry( FcbOrDcb->Vcb, &FcbOrDcb->Mcb, + CurrentIndex, + &NextVbo, + &NextLbo, + &NextByteCount ); + + + NT_ASSERT(NextVbo == StartingVbo); + + + } + + } // while ( CurrentIndex <= LastIndex ) + + // + // Now set up the Irp->IoStatus. It will be modified by the + // multi-completion routine in case of error or verify required. + // + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = OriginalByteCount; + + if (FlagOn(Irp->Flags, IRP_PAGING_IO)) { + CollectDiskIoStats(FcbOrDcb->Vcb, IrpContext->MajorFunction, + FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_USER_IO), NextRun); + } + + // + // OK, now do the I/O. + // + + try { + + DebugTrace( 0, Dbg, "Passing Multiple Irps on to Disk Driver\n", 0 ); + + FatMultipleAsync( IrpContext, + FcbOrDcb->Vcb, + Irp, + NextRun, + IoRuns ); + + } finally { + + if (IoRuns != StackIoRuns) { + + ExFreePool( IoRuns ); + } + } + } + + if (!Wait) { + + DebugTrace(-1, Dbg, "FatNonCachedIo -> STATUS_PENDING\n", 0); + return STATUS_PENDING; + } + + FatWaitSync( IrpContext ); + + + DebugTrace(-1, Dbg, "FatNonCachedIo -> 0x%08lx\n", Irp->IoStatus.Status); + return Irp->IoStatus.Status; +} + + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatNonCachedNonAlignedRead ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp, + IN PFCB FcbOrDcb, + IN ULONG StartingVbo, + IN ULONG ByteCount + ) + +/*++ + +Routine Description: + + This routine performs the non-cached disk io described in its parameters. + This routine differs from the above in that the range does not have to be + sector aligned. This accomplished with the use of intermediate buffers. + +Arguments: + + IrpContext->MajorFunction - Supplies either IRP_MJ_READ or IRP_MJ_WRITE. + + Irp - Supplies the requesting Irp. + + FcbOrDcb - Supplies the file to act on. + + StartingVbo - The starting point for the operation. + + ByteCount - The lengh of the operation. + +Return Value: + + None. + +--*/ + +{ + // + // Declare some local variables for enumeration through the + // runs of the file, and an array to store parameters for + // parallel I/Os + // + + LBO NextLbo; + ULONG NextByteCount; + BOOLEAN NextIsAllocated; + + ULONG SectorSize; + ULONG BytesToCopy; + ULONG OriginalByteCount; + ULONG OriginalStartingVbo; + + BOOLEAN EndOnMax; + + PUCHAR UserBuffer; + PUCHAR DiskBuffer = NULL; + + PMDL Mdl; + PMDL SavedMdl; + PVOID SavedUserBuffer; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatNonCachedNonAlignedRead\n", 0); + DebugTrace( 0, Dbg, "Irp = %p\n", Irp ); + DebugTrace( 0, Dbg, "MajorFunction = %08lx\n", IrpContext->MajorFunction ); + DebugTrace( 0, Dbg, "FcbOrDcb = %p\n", FcbOrDcb ); + DebugTrace( 0, Dbg, "StartingVbo = %08lx\n", StartingVbo ); + DebugTrace( 0, Dbg, "ByteCount = %08lx\n", ByteCount ); + + // + // Initialize some locals. + // + + OriginalByteCount = ByteCount; + OriginalStartingVbo = StartingVbo; + SectorSize = FcbOrDcb->Vcb->Bpb.BytesPerSector; + + NT_ASSERT( FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) ); + + // + // For nonbuffered I/O, we need the buffer locked in all + // cases. + // + // This call may raise. If this call succeeds and a subsequent + // condition is raised, the buffers are unlocked automatically + // by the I/O system when the request is completed, via the + // Irp->MdlAddress field. + // + + FatLockUserBuffer( IrpContext, + Irp, + IoWriteAccess, + ByteCount ); + + UserBuffer = FatMapUserBuffer( IrpContext, Irp ); + + // + // Allocate the local buffer + // + + DiskBuffer = FsRtlAllocatePoolWithTag( NonPagedPoolNxCacheAligned, + (ULONG) ROUND_TO_PAGES( SectorSize ), + TAG_IO_BUFFER ); + + // + // We use a try block here to ensure the buffer is freed, and to + // fill in the correct byte count in the Iosb.Information field. + // + + try { + + // + // If the beginning of the request was not aligned correctly, read in + // the first part first. + // + + if ( StartingVbo & (SectorSize - 1) ) { + + VBO Hole; + + // + // Try to lookup the first run. + // + + FatLookupFileAllocation( IrpContext, + FcbOrDcb, + StartingVbo, + &NextLbo, + &NextByteCount, + &NextIsAllocated, + &EndOnMax, + NULL ); + + // + // We just added the allocation, thus there must be at least + // one entry in the mcb corresponding to our write, ie. + // NextIsAllocated must be true. If not, the pre-existing file + // must have an allocation error. + // + + if ( !NextIsAllocated ) { + + FatPopUpFileCorrupt( IrpContext, FcbOrDcb ); + + FatRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); + } + + FatSingleNonAlignedSync( IrpContext, + FcbOrDcb->Vcb, + DiskBuffer, + NextLbo & ~((LONG)SectorSize - 1), + SectorSize, + Irp ); + + if (!NT_SUCCESS( Irp->IoStatus.Status )) { + + try_return( NOTHING ); + } + + // + // Now copy the part of the first sector that we want to the user + // buffer. + // + + Hole = StartingVbo & (SectorSize - 1); + + BytesToCopy = ByteCount >= SectorSize - Hole ? + SectorSize - Hole : ByteCount; + + RtlCopyMemory( UserBuffer, DiskBuffer + Hole, BytesToCopy ); + + StartingVbo += BytesToCopy; + ByteCount -= BytesToCopy; + + if ( ByteCount == 0 ) { + + try_return( NOTHING ); + } + } + + NT_ASSERT( (StartingVbo & (SectorSize - 1)) == 0 ); + + // + // If there is a tail part that is not sector aligned, read it. + // + + if ( ByteCount & (SectorSize - 1) ) { + + VBO LastSectorVbo; + + LastSectorVbo = StartingVbo + (ByteCount & ~(SectorSize - 1)); + + // + // Try to lookup the last part of the requested range. + // + + FatLookupFileAllocation( IrpContext, + FcbOrDcb, + LastSectorVbo, + &NextLbo, + &NextByteCount, + &NextIsAllocated, + &EndOnMax, + NULL ); + + // + // We just added the allocation, thus there must be at least + // one entry in the mcb corresponding to our write, ie. + // NextIsAllocated must be true. If not, the pre-existing file + // must have an allocation error. + // + + if ( !NextIsAllocated ) { + + FatPopUpFileCorrupt( IrpContext, FcbOrDcb ); + + FatRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); + } + + FatSingleNonAlignedSync( IrpContext, + FcbOrDcb->Vcb, + DiskBuffer, + NextLbo, + SectorSize, + Irp ); + + if (!NT_SUCCESS( Irp->IoStatus.Status )) { + + try_return( NOTHING ); + } + + // + // Now copy over the part of this last sector that we need. + // + + BytesToCopy = ByteCount & (SectorSize - 1); + + UserBuffer += LastSectorVbo - OriginalStartingVbo; + + RtlCopyMemory( UserBuffer, DiskBuffer, BytesToCopy ); + + ByteCount -= BytesToCopy; + + if ( ByteCount == 0 ) { + + try_return( NOTHING ); + } + } + + NT_ASSERT( ((StartingVbo | ByteCount) & (SectorSize - 1)) == 0 ); + + // + // Now build a Mdl describing the sector aligned balance of the transfer, + // and put it in the Irp, and read that part. + // + + SavedMdl = Irp->MdlAddress; + Irp->MdlAddress = NULL; + + SavedUserBuffer = Irp->UserBuffer; + + Irp->UserBuffer = (PUCHAR)MmGetMdlVirtualAddress( SavedMdl ) + + (StartingVbo - OriginalStartingVbo); + + Mdl = IoAllocateMdl( Irp->UserBuffer, + ByteCount, + FALSE, + FALSE, + Irp ); + + if (Mdl == NULL) { + + Irp->MdlAddress = SavedMdl; + Irp->UserBuffer = SavedUserBuffer; + FatRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES ); + } + + IoBuildPartialMdl( SavedMdl, + Mdl, + Irp->UserBuffer, + ByteCount ); + + // + // Try to read in the pages. + // + + try { + + FatNonCachedIo( IrpContext, + Irp, + FcbOrDcb, + StartingVbo, + ByteCount, + ByteCount, + 0 ); + + } finally { + + IoFreeMdl( Irp->MdlAddress ); + + Irp->MdlAddress = SavedMdl; + Irp->UserBuffer = SavedUserBuffer; + } + + try_exit: NOTHING; + + } finally { + + ExFreePool( DiskBuffer ); + + if ( !AbnormalTermination() && NT_SUCCESS(Irp->IoStatus.Status) ) { + + Irp->IoStatus.Information = OriginalByteCount; + + // + // We now flush the user's buffer to memory. + // + + KeFlushIoBuffers( Irp->MdlAddress, TRUE, FALSE ); + } + } + + DebugTrace(-1, Dbg, "FatNonCachedNonAlignedRead -> VOID\n", 0); + return; +} + + +VOID +FatMultipleAsync ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PIRP MasterIrp, + IN ULONG MultipleIrpCount, + IN 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/write + async routines should be called. + + A context parameter is initialized, to serve as a communications area + between here and the common completion routine. This initialization + includes allocation of a spinlock. The spinlock is deallocated in the + FatWaitSync routine, so it is essential that the caller insure that + this routine is always called under all circumstances following a call + to this 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: + + IrpContext->MajorFunction - Supplies either IRP_MJ_READ or IRP_MJ_WRITE. + + Vcb - Supplies the device to be read + + MasterIrp - Supplies the master Irp. + + MulitpleIrpCount - Supplies the number of multiple async requests + that will be issued against the master irp. + + IoRuns - Supplies an array containing the Vbo, Lbo, BufferOffset, and + ByteCount for all the runs to executed in parallel. + +Return Value: + + None. + +--*/ + +{ + PIRP Irp; + PIO_STACK_LOCATION IrpSp; + PMDL Mdl; + BOOLEAN Wait; + PFAT_IO_CONTEXT Context; + BOOLEAN IsAWrite = FALSE; + ULONG Length = 0; + + ULONG UnwindRunCount = 0; + + BOOLEAN ExceptionExpected = TRUE; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatMultipleAsync\n", 0); + DebugTrace( 0, Dbg, "MajorFunction = %08lx\n", IrpContext->MajorFunction ); + DebugTrace( 0, Dbg, "Vcb = %p\n", Vcb ); + DebugTrace( 0, Dbg, "MasterIrp = %p\n", MasterIrp ); + DebugTrace( 0, Dbg, "MultipleIrpCount = %08lx\n", MultipleIrpCount ); + DebugTrace( 0, Dbg, "IoRuns = %08lx\n", IoRuns ); + + // + // If this I/O originating during FatVerifyVolume, bypass the + // verify logic. + // + + if (Vcb->VerifyThread == KeGetCurrentThread()) { + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_OVERRIDE_VERIFY ); + } + + // + // Set up things according to whether this is truely async. + // + + Wait = BooleanFlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ); + + Context = IrpContext->FatIoContext; + + // + // Finish initializing Context, for use in Read/Write Multiple Asynch. + // + + Context->MasterIrp = MasterIrp; + + IrpSp = IoGetCurrentIrpStackLocation( MasterIrp ); + IsAWrite = (IrpSp->MajorFunction == IRP_MJ_WRITE); + Length = IrpSp->Parameters.Read.Length; + + try { + + // + // Itterate through the runs, doing everything that can fail + // + + for ( UnwindRunCount = 0; + UnwindRunCount < MultipleIrpCount; + UnwindRunCount++ ) { + + // + // Create an associated IRP, making sure there is one stack entry for + // us, as well. + // + + IoRuns[UnwindRunCount].SavedIrp = 0; + + Irp = IoMakeAssociatedIrp( MasterIrp, + (CCHAR)(Vcb->TargetDeviceObject->StackSize + 1) ); + + if (Irp == NULL) { + + FatRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES ); + } + + IoRuns[UnwindRunCount].SavedIrp = Irp; + + // + // Allocate and build a partial Mdl for the request. + // + + Mdl = IoAllocateMdl( (PCHAR)MasterIrp->UserBuffer + + IoRuns[UnwindRunCount].Offset, + IoRuns[UnwindRunCount].ByteCount, + FALSE, + FALSE, + Irp ); + + if (Mdl == NULL) { + + FatRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES ); + } + + // + // Sanity Check + // + + NT_ASSERT( Mdl == Irp->MdlAddress ); + + IoBuildPartialMdl( MasterIrp->MdlAddress, + Mdl, + (PCHAR)MasterIrp->UserBuffer + + IoRuns[UnwindRunCount].Offset, + IoRuns[UnwindRunCount].ByteCount ); + + // + // 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 = IrpContext->MajorFunction; + IrpSp->Parameters.Read.Length = IoRuns[UnwindRunCount].ByteCount; + IrpSp->Parameters.Read.ByteOffset.QuadPart = IoRuns[UnwindRunCount].Vbo; + + // + // Set up the completion routine address in our stack frame. + // + + IoSetCompletionRoutine( Irp, + Wait ? + &FatMultiSyncCompletionRoutine : + &FatMultiAsyncCompletionRoutine, + Context, + 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 = IrpContext->MajorFunction; + IrpSp->Parameters.Read.Length = IoRuns[UnwindRunCount].ByteCount; + IrpSp->Parameters.Read.ByteOffset.QuadPart = IoRuns[UnwindRunCount].Lbo; + + // + // If this Irp is the result of a WriteThough operation, + // tell the device to write it through. + // + + if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WRITE_THROUGH )) { + + SetFlag( IrpSp->Flags, SL_WRITE_THROUGH ); + } + + // + // If this I/O requires override verify, bypass the verify logic. + // + + if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_OVERRIDE_VERIFY )) { + + SetFlag( IrpSp->Flags, SL_OVERRIDE_VERIFY_VOLUME ); + } + } + + // + // Now we no longer expect an exception. If the driver raises, we + // must bugcheck, because we do not know how to recover from that + // case. + // + + ExceptionExpected = FALSE; + + // + // 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. + // + + Context->IrpCount = MultipleIrpCount; + MasterIrp->AssociatedIrp.IrpCount = MultipleIrpCount; + + if (Wait) { + + MasterIrp->AssociatedIrp.IrpCount += 1; + } + else if (FlagOn( Context->Wait.Async.ResourceThreadId, 3 )) { + + // + // For async requests if we 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 try to boost priority. + // + + if (Context->Wait.Async.Resource != NULL) { + + ExSetResourceOwnerPointer( Context->Wait.Async.Resource, + (PVOID)Context->Wait.Async.ResourceThreadId ); + } + + if (Context->Wait.Async.Resource2 != NULL) { + + ExSetResourceOwnerPointer( Context->Wait.Async.Resource2, + (PVOID)Context->Wait.Async.ResourceThreadId ); + } + } + + // + // Now that all the dangerous work is done, issue the read requests + // + + for (UnwindRunCount = 0; + UnwindRunCount < MultipleIrpCount; + UnwindRunCount++) { + + Irp = IoRuns[UnwindRunCount].SavedIrp; + + DebugDoit( FatIoCallDriverCount += 1); + + // + // 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)FatLowLevelReadWrite( IrpContext, + Vcb->TargetDeviceObject, + Irp, + Vcb ); + } + + // + // We just issued an IO to the storage stack, update the counters indicating so. + // + + if (FatDiskAccountingEnabled) { + + FatUpdateIOCountersPCW( IsAWrite, Length ); + } + + } finally { + + ULONG i; + + DebugUnwind( FatMultipleAsync ); + + // + // Only allocating the spinlock, making the associated Irps + // and allocating the Mdls can fail. + // + + if ( AbnormalTermination() ) { + + // + // If the driver raised, we are hosed. He is not supposed to raise, + // and it is impossible for us to figure out how to clean up. + // + + if (!ExceptionExpected) { + NT_ASSERT( ExceptionExpected ); +#pragma prefast( suppress:28159, "things are seriously wrong if we get here" ) + FatBugCheck( 0, 0, 0 ); + } + + // + // Unwind + // + + for (i = 0; i <= UnwindRunCount; i++) { + + if ( (Irp = IoRuns[i].SavedIrp) != NULL ) { + + if ( Irp->MdlAddress != NULL ) { + + IoFreeMdl( Irp->MdlAddress ); + } + + IoFreeIrp( Irp ); + } + } + } + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "FatMultipleAsync -> VOID\n", 0); + } + + return; +} + + +VOID +FatSingleAsync ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN LBO Lbo, + IN ULONG ByteCount, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine reads or writes 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: + + IrpContext->MajorFunction - Supplies either IRP_MJ_READ or IRP_MJ_WRITE. + + Vcb - Supplies the device to read + + Lbo - Supplies the starting Logical Byte Offset to begin reading from + + ByteCount - Supplies the number of bytes to read from the device + + Irp - Supplies the master Irp to associated with the async + request. + +Return Value: + + None. + +--*/ + +{ + PIO_STACK_LOCATION IrpSp; + PFAT_IO_CONTEXT Context; + BOOLEAN IsAWrite = FALSE; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatSingleAsync\n", 0); + DebugTrace( 0, Dbg, "MajorFunction = %08lx\n", IrpContext->MajorFunction ); + DebugTrace( 0, Dbg, "Vcb = %p\n", Vcb ); + DebugTrace( 0, Dbg, "Lbo = %08lx\n", Lbo); + DebugTrace( 0, Dbg, "ByteCount = %08lx\n", ByteCount); + DebugTrace( 0, Dbg, "Irp = %p\n", Irp ); + + // + // If this I/O originating during FatVerifyVolume, bypass the + // verify logic. + // + + if (Vcb->VerifyThread == KeGetCurrentThread()) { + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_OVERRIDE_VERIFY ); + } + + // + // Set up the completion routine address in our stack frame. + // + + IoSetCompletionRoutine( Irp, + FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) ? + &FatSingleSyncCompletionRoutine : + &FatSingleAsyncCompletionRoutine, + IrpContext->FatIoContext, + 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 = IrpContext->MajorFunction; + IrpSp->Parameters.Read.Length = ByteCount; + IrpSp->Parameters.Read.ByteOffset.QuadPart = Lbo; + + IsAWrite = (IrpSp->MajorFunction == IRP_MJ_WRITE); + + // + // If this Irp is the result of a WriteThough operation, + // tell the device to write it through. + // + + if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WRITE_THROUGH )) { + + SetFlag( IrpSp->Flags, SL_WRITE_THROUGH ); + } + + // + // If this I/O requires override verify, bypass the verify logic. + // + + if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_OVERRIDE_VERIFY )) { + + SetFlag( IrpSp->Flags, SL_OVERRIDE_VERIFY_VOLUME ); + } + + // + // For async requests if we 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 try to boost priority. + // + + if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ) && + FlagOn( IrpContext->FatIoContext->Wait.Async.ResourceThreadId, 3 )) { + + Context = IrpContext->FatIoContext; + + if (Context->Wait.Async.Resource != NULL) { + + ExSetResourceOwnerPointer( Context->Wait.Async.Resource, + (PVOID)Context->Wait.Async.ResourceThreadId ); + } + + if (Context->Wait.Async.Resource2 != NULL) { + + ExSetResourceOwnerPointer( Context->Wait.Async.Resource2, + (PVOID)Context->Wait.Async.ResourceThreadId ); + } + } + + // + // Issue the read request + // + + DebugDoit( FatIoCallDriverCount += 1); + + // + // 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)FatLowLevelReadWrite( IrpContext, + Vcb->TargetDeviceObject, + Irp, + Vcb ); + + // + // We just issued an IO to the storage stack, update the counters indicating so. + // + + if (FatDiskAccountingEnabled) { + + FatUpdateIOCountersPCW( IsAWrite, ByteCount ); + } + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "FatSingleAsync -> VOID\n", 0); + + return; +} + + +VOID +FatSingleNonAlignedSync ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PUCHAR Buffer, + IN LBO Lbo, + IN ULONG ByteCount, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine reads or writes one or more contiguous sectors from a device + Synchronously, and does so to a buffer that must come from non paged + pool. It saves a pointer to the Irp's original Mdl, and creates a new + one describing the given buffer. 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: + + IrpContext->MajorFunction - Supplies either IRP_MJ_READ or IRP_MJ_WRITE. + + Vcb - Supplies the device to read + + Buffer - Supplies a buffer from non-paged pool. + + Lbo - Supplies the starting Logical Byte Offset to begin reading from + + ByteCount - Supplies the number of bytes to read from the device + + Irp - Supplies the master Irp to associated with the async + request. + +Return Value: + + None. + +--*/ + +{ + PIO_STACK_LOCATION IrpSp; + + PMDL Mdl; + PMDL SavedMdl; + BOOLEAN IsAWrite = FALSE; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatSingleNonAlignedAsync\n", 0); + DebugTrace( 0, Dbg, "MajorFunction = %08lx\n", IrpContext->MajorFunction ); + DebugTrace( 0, Dbg, "Vcb = %p\n", Vcb ); + DebugTrace( 0, Dbg, "Buffer = %p\n", Buffer ); + DebugTrace( 0, Dbg, "Lbo = %08lx\n", Lbo); + DebugTrace( 0, Dbg, "ByteCount = %08lx\n", ByteCount); + DebugTrace( 0, Dbg, "Irp = %p\n", Irp ); + + // + // Create a new Mdl describing the buffer, saving the current one in the + // Irp + // + + SavedMdl = Irp->MdlAddress; + + Irp->MdlAddress = 0; + + Mdl = IoAllocateMdl( Buffer, + ByteCount, + FALSE, + FALSE, + Irp ); + + if (Mdl == NULL) { + + Irp->MdlAddress = SavedMdl; + + FatRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES ); + } + + // + // Lock the new Mdl in memory. + // + + try { + + MmProbeAndLockPages( Mdl, KernelMode, IoWriteAccess ); + + } finally { + + if ( AbnormalTermination() ) { + + IoFreeMdl( Mdl ); + Irp->MdlAddress = SavedMdl; + } + } + + // + // Set up the completion routine address in our stack frame. + // + + IoSetCompletionRoutine( Irp, + &FatSingleSyncCompletionRoutine, + IrpContext->FatIoContext, + 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 = IrpContext->MajorFunction; + IrpSp->Parameters.Read.Length = ByteCount; + IrpSp->Parameters.Read.ByteOffset.QuadPart = Lbo; + + IsAWrite = (IrpSp->MajorFunction == IRP_MJ_WRITE); + + // + // If this I/O originating during FatVerifyVolume, bypass the + // verify logic. + // + + if (Vcb->VerifyThread == KeGetCurrentThread()) { + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_OVERRIDE_VERIFY ); + } + + // + // If this I/O requires override verify, bypass the verify logic. + // + + if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_OVERRIDE_VERIFY )) { + + SetFlag( IrpSp->Flags, SL_OVERRIDE_VERIFY_VOLUME ); + } + + // + // Issue the read request + // + + DebugDoit( FatIoCallDriverCount += 1); + + // + // 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. + // + + try { + + (VOID)FatLowLevelReadWrite( IrpContext, + Vcb->TargetDeviceObject, + Irp, + Vcb ); + + FatWaitSync( IrpContext ); + + } finally { + + MmUnlockPages( Mdl ); + IoFreeMdl( Mdl ); + Irp->MdlAddress = SavedMdl; + } + + // + // We just issued an IO to the storage stack, update the counters indicating so. + // + + if (FatDiskAccountingEnabled) { + + FatUpdateIOCountersPCW( IsAWrite, ByteCount ); + } + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "FatSingleNonAlignedSync -> VOID\n", 0); + + return; +} + + +VOID +FatWaitSync ( + 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(); + + DebugTrace(+1, Dbg, "FatWaitSync, Context = %p\n", IrpContext->FatIoContext ); + + KeWaitForSingleObject( &IrpContext->FatIoContext->Wait.SyncEvent, + Executive, KernelMode, FALSE, NULL ); + + KeClearEvent( &IrpContext->FatIoContext->Wait.SyncEvent ); + + DebugTrace(-1, Dbg, "FatWaitSync -> VOID\n", 0 ); +} + + +// +// Internal Support Routine +// + +NTSTATUS +FatMultiSyncCompletionRoutine ( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PIRP Irp, + _In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt + ) + +/*++ + +Routine Description: + + This is the completion routine for all reads and writes started via + FatRead/WriteMultipleAsynch. It must synchronize its operation for + multiprocessor environments with itself on all other processors, via + a spin lock found via the Context parameter. + + The completion routine 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 + (essentially by Vbo), and if so it must correctly reduce the + byte count 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.) + + Contxt - 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. + +--*/ + +{ + + PFAT_IO_CONTEXT Context = Contxt; + PIRP MasterIrp = Context->MasterIrp; + + DebugTrace(+1, Dbg, "FatMultiSyncCompletionRoutine, Context = %p\n", Context ); + + // + // If we got an error (or verify required), remember it in the Irp + // + + if (!NT_SUCCESS( Irp->IoStatus.Status )) { + + NT_ASSERT( NT_SUCCESS( FatAssertNotStatus ) || Irp->IoStatus.Status != FatAssertNotStatus ); + +#ifdef SYSCACHE_COMPILE + DbgPrint( "FAT SYSCACHE: MultiSync (IRP %08x for Master %08x) -> %08x\n", Irp, MasterIrp, Irp->IoStatus ); +#endif + + MasterIrp->IoStatus = Irp->IoStatus; + } + + NT_ASSERT( !(NT_SUCCESS( Irp->IoStatus.Status ) && Irp->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(&Context->IrpCount) == 0) { + + FatDoCompletionZero( MasterIrp, Context ); + KeSetEvent( &Context->Wait.SyncEvent, 0, FALSE ); + } + + DebugTrace(-1, Dbg, "FatMultiSyncCompletionRoutine -> SUCCESS\n", 0 ); + + UNREFERENCED_PARAMETER( DeviceObject ); + + return STATUS_MORE_PROCESSING_REQUIRED; +} + + +// +// Internal Support Routine +// + +NTSTATUS +FatMultiAsyncCompletionRoutine ( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PIRP Irp, + _In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt + ) + +/*++ + +Routine Description: + + This is the completion routine for all reads and writes started via + FatRead/WriteMultipleAsynch. It must synchronize its operation for + multiprocessor environments with itself on all other processors, via + a spin lock found via the Context parameter. + + 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 + (essentially by Vbo), and if so it must correctly reduce the + byte count 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.) + + Contxt - 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. + +--*/ + +{ + + PFAT_IO_CONTEXT Context = Contxt; + PIRP MasterIrp = Context->MasterIrp; + + DebugTrace(+1, Dbg, "FatMultiAsyncCompletionRoutine, Context = %p\n", Context ); + + // + // If we got an error (or verify required), remember it in the Irp + // + + if (!NT_SUCCESS( Irp->IoStatus.Status )) { + + NT_ASSERT( NT_SUCCESS( FatAssertNotStatus ) || Irp->IoStatus.Status != FatAssertNotStatus ); + +#ifdef SYSCACHE_COMPILE + DbgPrint( "FAT SYSCACHE: MultiAsync (IRP %08x for Master %08x) -> %08x\n", Irp, MasterIrp, Irp->IoStatus ); +#endif + + MasterIrp->IoStatus = Irp->IoStatus; + + } + + NT_ASSERT( !(NT_SUCCESS( Irp->IoStatus.Status ) && Irp->IoStatus.Information == 0 )); + + if (InterlockedDecrement(&Context->IrpCount) == 0) { + + FatDoCompletionZero( MasterIrp, Context ); + + if (NT_SUCCESS(MasterIrp->IoStatus.Status)) { + + MasterIrp->IoStatus.Information = + Context->Wait.Async.RequestedByteCount; + + NT_ASSERT(MasterIrp->IoStatus.Information != 0); + + // + // Now if this wasn't PagingIo, set either the read or write bit. + // + + if (!FlagOn(MasterIrp->Flags, IRP_PAGING_IO)) { + + SetFlag( Context->Wait.Async.FileObject->Flags, + IoGetCurrentIrpStackLocation(MasterIrp)->MajorFunction == IRP_MJ_READ ? + FO_FILE_FAST_IO_READ : FO_FILE_MODIFIED ); + } + } + + // + // If this was a special async write, decrement the count. Set the + // event if this was the final outstanding I/O for the file. We will + // also want to queue an APC to deal with any error conditionions. + // + _Analysis_assume_(!(Context->Wait.Async.NonPagedFcb) && + (ExInterlockedAddUlong( &Context->Wait.Async.NonPagedFcb->OutstandingAsyncWrites, + 0xffffffff, + &FatData.GeneralSpinLock ) != 1)); + if ((Context->Wait.Async.NonPagedFcb) && + (ExInterlockedAddUlong( &Context->Wait.Async.NonPagedFcb->OutstandingAsyncWrites, + 0xffffffff, + &FatData.GeneralSpinLock ) == 1)) { + + KeSetEvent( Context->Wait.Async.NonPagedFcb->OutstandingAsyncEvent, 0, FALSE ); + } + + // + // Now release the resources. + // + + if (Context->Wait.Async.Resource != NULL) { + + ExReleaseResourceForThreadLite( Context->Wait.Async.Resource, + Context->Wait.Async.ResourceThreadId ); + } + + if (Context->Wait.Async.Resource2 != NULL) { + + ExReleaseResourceForThreadLite( Context->Wait.Async.Resource2, + Context->Wait.Async.ResourceThreadId ); + } + + // + // Mark the master Irp pending + // + + IoMarkIrpPending( MasterIrp ); + + // + // and finally, free the context record. + // + + ExFreePool( Context ); + } + + DebugTrace(-1, Dbg, "FatMultiAsyncCompletionRoutine -> SUCCESS\n", 0 ); + + UNREFERENCED_PARAMETER( DeviceObject ); + + return STATUS_SUCCESS; +} + + +NTSTATUS +FatPagingFileErrorHandler ( + IN PIRP Irp, + IN PKEVENT Event OPTIONAL + ) + +/*++ + +Routine Description: + + This routine attempts to guarantee that the media is marked dirty + with the surface test bit if a paging file IO fails. + + The work done here has several basic problems + + 1) when paging file writes start failing, this is a good sign + that the rest of the system is about to fall down around us + + 2) it has no forward progress guarantee + + With Whistler, it is actually quite intentional that we're rejiggering + the paging file write path to make forward progress at all times. This + means that the cases where it *does* fail, we're truly seeing media errors + and this is probably going to mean the paging file is going to stop working + very soon. + + It'd be nice to make this guarantee progress. It would need + + 1) a guaranteed worker thread which can only be used by items which + will make forward progress (i.e., not block out this one) + + 2) the virtual volume file's pages containing the boot sector and + 1st FAT entry would have to be pinned resident and have a guaranteed + mapping address + + 3) mark volume would have to have a stashed irp/mdl and roll the write + irp, or use a generalized mechanism to guarantee issue of the irp + + 4) the lower stack would have to guarantee progress + + Of these, 1 and 4 may actually exist shortly. + +Arguments: + + Irp - Pointer to the associated Irp which is being failed. + + Event - Pointer to optional event to be signalled instead of completing + the IRP + +Return Value: + + Returns STATUS_MORE_PROCESSING_REQUIRED if we managed to queue off the workitem, + STATUS_SUCCESS otherwise. + +--*/ + +{ + NTSTATUS Status; + + // + // If this was a media error, we want to chkdsk /r the next time we boot. + // + + if (FsRtlIsTotalDeviceFailure(Irp->IoStatus.Status)) { + + Status = STATUS_SUCCESS; + + } else { + + PCLEAN_AND_DIRTY_VOLUME_PACKET Packet; + + // + // We are going to try to mark the volume needing recover. + // If we can't get pool, oh well.... + // + + Packet = ExAllocatePoolWithTag(NonPagedPoolNx, sizeof(CLEAN_AND_DIRTY_VOLUME_PACKET), ' taF'); + + if ( Packet ) { + + Packet->Vcb = &((PVOLUME_DEVICE_OBJECT)IoGetCurrentIrpStackLocation(Irp)->DeviceObject)->Vcb; + Packet->Irp = Irp; + Packet->Event = Event; + + ExInitializeWorkItem( &Packet->Item, + &FatFspMarkVolumeDirtyWithRecover, + Packet ); + +#pragma prefast( suppress:28159, "prefast indicates this is obsolete, but it is ok for fastfat to use it" ) + ExQueueWorkItem( &Packet->Item, CriticalWorkQueue ); + + Status = STATUS_MORE_PROCESSING_REQUIRED; + + } else { + + Status = STATUS_SUCCESS; + } + } + + return Status; +} + + +// +// Internal Support Routine +// + +NTSTATUS +FatPagingFileCompletionRoutineCatch ( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PIRP Irp, + _In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt + ) + +/*++ + +Routine Description: + + This is the completion routine for all reads and writes started via + FatPagingFileIo that reuse the master irp (that we have to catch + on the way back). It is always invoked. + + The completion routine has has the following responsibility: + + If the error implies a media problem, it enqueues a + worker item to write out the dirty bit so that the next + time we run we will do a autochk /r. This is not forward + progress guaranteed at the moment. + + Clean up the Mdl used for this partial request. + + Note that if the Irp is failing, the error code is already where + we want it. + +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.) + + MasterIrp - Pointer to the master Irp. + +Return Value: + + Always returns STATUS_MORE_PROCESSING_REQUIRED. + +--*/ + +{ + PFAT_PAGING_FILE_CONTEXT Context = (PFAT_PAGING_FILE_CONTEXT) Contxt; + + UNREFERENCED_PARAMETER( DeviceObject ); + + DebugTrace(+1, Dbg, "FatPagingFileCompletionRoutineCatch, Context = %p\n", Context ); + + // + // Cleanup the existing Mdl, perhaps by returning the reserve. + // + + if (Irp->MdlAddress == FatReserveMdl) { + + MmPrepareMdlForReuse( Irp->MdlAddress ); + KeSetEvent( &FatReserveEvent, 0, FALSE ); + + } else { + + IoFreeMdl( Irp->MdlAddress ); + } + + // + // Restore the original Mdl. + // + + Irp->MdlAddress = Context->RestoreMdl; + + DebugTrace(-1, Dbg, "FatPagingFileCompletionRoutine => (done)\n", 0 ); + + // + // If the IRP is succeeding or the failure handler did not post off the + // completion, we're done and should set the event to let the master + // know the IRP is his again. + // + + if (NT_SUCCESS( Irp->IoStatus.Status ) || + FatPagingFileErrorHandler( Irp, &Context->Event ) == STATUS_SUCCESS) { + + KeSetEvent( &Context->Event, 0, FALSE ); + } + + return STATUS_MORE_PROCESSING_REQUIRED; + +} + + +// +// Internal Support Routine +// + +NTSTATUS +FatPagingFileCompletionRoutine ( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PIRP Irp, + _In_reads_opt_(_Inexpressible_("varies")) PVOID MasterIrp + ) + +/*++ + +Routine Description: + + This is the completion routine for all reads and writes started via + FatPagingFileIo. It should only be invoked on error or cancel. + + The completion routine has has the following responsibility: + + Since the individual request was completed with an error, + this completion routine must stuff it into the master irp. + + If the error implies a media problem, it also enqueues a + worker item to write out the dirty bit so that the next + time we run we will do a autochk /r + +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.) + + MasterIrp - Pointer to the master Irp. + +Return Value: + + Always returns STATUS_SUCCESS. + +--*/ + +{ + DebugTrace(+1, Dbg, "FatPagingFileCompletionRoutine, MasterIrp = %p\n", MasterIrp ); + + // + // If we got an error (or verify required), remember it in the Irp + // + + NT_ASSERT( !NT_SUCCESS( Irp->IoStatus.Status )); + + // + // If we were invoked with an assoicated Irp, copy the error over. + // + + if (Irp != MasterIrp) { + + ((PIRP)MasterIrp)->IoStatus = Irp->IoStatus; + } + + DebugTrace(-1, Dbg, "FatPagingFileCompletionRoutine => (done)\n", 0 ); + + UNREFERENCED_PARAMETER( DeviceObject ); + + return FatPagingFileErrorHandler( Irp, NULL ); +} + + +// +// Internal Support Routine +// + +NTSTATUS +FatSpecialSyncCompletionRoutine ( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PIRP Irp, + _In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt + ) + +/*++ + +Routine Description: + + This is the completion routine for a special set of sub irps + that have to work at APC level. + + The completion routine has has the following responsibilities: + + It sets the event passed as the context to signal that the + request is done. + + By doing this, the caller will be released before final APC + completion with knowledge that the IRP is finished. Final + completion will occur at an indeterminate time after this + occurs, and by using this completion routine the caller expects + to not have any output or status returned. A junk user Iosb + should be used to capture the status without forcing Io to take + an exception on NULL. + +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.) + + Contxt - The context parameter which was specified in the call to + FatRead/WriteSingleAsynch. + +Return Value: + + Currently always returns STATUS_SUCCESS. + +--*/ + +{ + PFAT_SYNC_CONTEXT SyncContext = (PFAT_SYNC_CONTEXT)Contxt; + + UNREFERENCED_PARAMETER( Irp ); + + DebugTrace(+1, Dbg, "FatSpecialSyncCompletionRoutine, Context = %p\n", Contxt ); + + SyncContext->Iosb = Irp->IoStatus; + + KeSetEvent( &SyncContext->Event, 0, FALSE ); + + DebugTrace(-1, Dbg, "FatSpecialSyncCompletionRoutine -> STATUS_SUCCESS\n", 0 ); + + UNREFERENCED_PARAMETER( DeviceObject ); + + return STATUS_SUCCESS; +} + + +// +// Internal Support Routine +// + +NTSTATUS +FatSingleSyncCompletionRoutine ( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PIRP Irp, + _In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt + ) + +/*++ + +Routine Description: + + This is the completion routine for all reads and writes started via + FatRead/WriteSingleAsynch. + + The completion routine has has the following responsibilities: + + Copy the I/O status from the Irp to the Context, since the Irp + will no longer be accessible. + + 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.) + + Contxt - The context parameter which was specified in the call to + FatRead/WriteSingleAsynch. + +Return Value: + + Currently always returns STATUS_SUCCESS. + +--*/ + +{ + PFAT_IO_CONTEXT Context = Contxt; + + DebugTrace(+1, Dbg, "FatSingleSyncCompletionRoutine, Context = %p\n", Context ); + + FatDoCompletionZero( Irp, Context ); + + if (!NT_SUCCESS( Irp->IoStatus.Status )) { + + NT_ASSERT( NT_SUCCESS( FatAssertNotStatus ) || Irp->IoStatus.Status != FatAssertNotStatus ); + } + + NT_ASSERT( !(NT_SUCCESS( Irp->IoStatus.Status ) && Irp->IoStatus.Information == 0 )); + + KeSetEvent( &Context->Wait.SyncEvent, 0, FALSE ); + + DebugTrace(-1, Dbg, "FatSingleSyncCompletionRoutine -> STATUS_MORE_PROCESSING_REQUIRED\n", 0 ); + + UNREFERENCED_PARAMETER( DeviceObject ); + + return STATUS_MORE_PROCESSING_REQUIRED; +} + + +// +// Internal Support Routine +// + +NTSTATUS +FatSingleAsyncCompletionRoutine ( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PIRP Irp, + _In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt + ) + +/*++ + +Routine Description: + + This is the completion routine for all reads and writes started via + FatRead/WriteSingleAsynch. + + The completion routine has has the following responsibilities: + + Copy the I/O status from the Irp to the Context, since the Irp + will no longer be accessible. + + 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.) + + Contxt - The context parameter which was specified in the call to + FatRead/WriteSingleAsynch. + +Return Value: + + Currently always returns STATUS_SUCCESS. + +--*/ + +{ + PFAT_IO_CONTEXT Context = Contxt; + + DebugTrace(+1, Dbg, "FatSingleAsyncCompletionRoutine, Context = %p\n", Context ); + + // + // Fill in the information field correctedly if this worked. + // + + FatDoCompletionZero( Irp, Context ); + + if (NT_SUCCESS(Irp->IoStatus.Status)) { + + NT_ASSERT( Irp->IoStatus.Information != 0 ); + Irp->IoStatus.Information = Context->Wait.Async.RequestedByteCount; + NT_ASSERT( Irp->IoStatus.Information != 0 ); + + // + // Now if this wasn't PagingIo, set either the read or write bit. + // + + if (!FlagOn(Irp->Flags, IRP_PAGING_IO)) { + + SetFlag( Context->Wait.Async.FileObject->Flags, + IoGetCurrentIrpStackLocation(Irp)->MajorFunction == IRP_MJ_READ ? + FO_FILE_FAST_IO_READ : FO_FILE_MODIFIED ); + } + + } else { + + NT_ASSERT( NT_SUCCESS( FatAssertNotStatus ) || Irp->IoStatus.Status != FatAssertNotStatus ); + +#ifdef SYSCACHE_COMPILE + DbgPrint( "FAT SYSCACHE: SingleAsync (IRP %08x) -> %08x\n", Irp, Irp->IoStatus ); +#endif + + } + + // + // If this was a special async write, decrement the count. Set the + // event if this was the final outstanding I/O for the file. We will + // also want to queue an APC to deal with any error conditionions. + // + _Analysis_assume_(!(Context->Wait.Async.NonPagedFcb) && + (ExInterlockedAddUlong( &Context->Wait.Async.NonPagedFcb->OutstandingAsyncWrites, + 0xffffffff, + &FatData.GeneralSpinLock ) != 1)); + + if ((Context->Wait.Async.NonPagedFcb) && + (ExInterlockedAddUlong( &Context->Wait.Async.NonPagedFcb->OutstandingAsyncWrites, + 0xffffffff, + &FatData.GeneralSpinLock ) == 1)) { + + KeSetEvent( Context->Wait.Async.NonPagedFcb->OutstandingAsyncEvent, 0, FALSE ); + } + + // + // Now release the resources + // + + if (Context->Wait.Async.Resource != NULL) { + + ExReleaseResourceForThreadLite( Context->Wait.Async.Resource, + Context->Wait.Async.ResourceThreadId ); + } + + if (Context->Wait.Async.Resource2 != NULL) { + + ExReleaseResourceForThreadLite( Context->Wait.Async.Resource2, + Context->Wait.Async.ResourceThreadId ); + } + + // + // Mark the Irp pending + // + + IoMarkIrpPending( Irp ); + + // + // and finally, free the context record. + // + + ExFreePool( Context ); + + DebugTrace(-1, Dbg, "FatSingleAsyncCompletionRoutine -> STATUS_MORE_PROCESSING_REQUIRED\n", 0 ); + + UNREFERENCED_PARAMETER( DeviceObject ); + + return STATUS_SUCCESS; +} + + +VOID +FatLockUserBuffer ( + IN PIRP_CONTEXT IrpContext, + IN OUT PIRP Irp, + IN LOCK_OPERATION Operation, + IN ULONG BufferLength + ) + +/*++ + +Routine Description: + + This routine locks the specified buffer for the specified type of + access. 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. + + Note that this is the *input/output* buffer. + +Arguments: + + Irp - Pointer to the Irp for which the buffer is to be locked. + + Operation - IoWriteAccess for read operations, or IoReadAccess for + write operations. + + BufferLength - Length of user buffer. + +Return Value: + + None + +--*/ + +{ + PMDL Mdl = NULL; + + PAGED_CODE(); + + if (Irp->MdlAddress == NULL) { + + // + // Allocate the Mdl, and Raise if we fail. + // + + Mdl = IoAllocateMdl( Irp->UserBuffer, BufferLength, FALSE, FALSE, Irp ); + + if (Mdl == NULL) { + + FatRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES ); + } + + // + // 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, + Irp->RequestorMode, + Operation ); + + } except(EXCEPTION_EXECUTE_HANDLER) { + + NTSTATUS Status; + + Status = GetExceptionCode(); + + IoFreeMdl( Mdl ); + Irp->MdlAddress = NULL; + + FatRaiseStatus( IrpContext, + FsRtlIsNtstatusExpected(Status) ? Status : STATUS_INVALID_USER_BUFFER ); + } + } + + UNREFERENCED_PARAMETER( IrpContext ); +} + + +PVOID +FatMapUserBuffer ( + IN PIRP_CONTEXT IrpContext, + IN OUT PIRP Irp + ) + +/*++ + +Routine Description: + + This routine conditionally maps the user buffer for the current I/O + request in the specified mode. If the buffer is already mapped, it + just returns its address. + + Note that this is the *input/output* buffer. + +Arguments: + + Irp - Pointer to the Irp for the request. + +Return Value: + + Mapped address + +--*/ + +{ + UNREFERENCED_PARAMETER( IrpContext ); + + PAGED_CODE(); + + // + // If there is no Mdl, then we must be in the Fsd, and we can simply + // return the UserBuffer field from the Irp. + // + + if (Irp->MdlAddress == NULL) { + + return Irp->UserBuffer; + + } else { + + PVOID Address = MmGetSystemAddressForMdlSafe( Irp->MdlAddress, NormalPagePriority ); + + if (Address == NULL) { + + ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES ); + } + + return Address; + } +} + + +PVOID +FatBufferUserBuffer ( + IN PIRP_CONTEXT IrpContext, + IN OUT PIRP Irp, + IN ULONG BufferLength + ) + +/*++ + +Routine Description: + + This routine conditionally buffers the user buffer for the current I/O + request. If the buffer is already buffered, it just returns its address. + + Note that this is the *input* buffer. + +Arguments: + + Irp - Pointer to the Irp for the request. + + BufferLength - Length of user buffer. + +Return Value: + + Buffered address. + +--*/ + +{ + PUCHAR UserBuffer; + + UNREFERENCED_PARAMETER( IrpContext ); + + PAGED_CODE(); + + // + // Handle the no buffer case. + // + + if (BufferLength == 0) { + + return NULL; + } + + // + // If there is no system buffer we must have been supplied an Mdl + // describing the users input buffer, which we will now snapshot. + // + + if (Irp->AssociatedIrp.SystemBuffer == NULL) { + + UserBuffer = FatMapUserBuffer( IrpContext, Irp ); + + Irp->AssociatedIrp.SystemBuffer = FsRtlAllocatePoolWithQuotaTag( NonPagedPoolNx, + BufferLength, + TAG_IO_USER_BUFFER ); + + // + // Set the flags so that the completion code knows to deallocate the + // buffer. + // + + Irp->Flags |= (IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER); + + try { + + RtlCopyMemory( Irp->AssociatedIrp.SystemBuffer, + UserBuffer, + BufferLength ); + + } except (EXCEPTION_EXECUTE_HANDLER) { + + NTSTATUS Status; + + Status = GetExceptionCode(); + FatRaiseStatus( IrpContext, + FsRtlIsNtstatusExpected(Status) ? Status : STATUS_INVALID_USER_BUFFER ); + } + } + + return Irp->AssociatedIrp.SystemBuffer; +} + + +NTSTATUS +FatToggleMediaEjectDisable ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN BOOLEAN PreventRemoval + ) + +/*++ + +Routine Description: + + The routine either enables or disables the eject button on removable + media. + +Arguments: + + Vcb - Descibes the volume to operate on + + PreventRemoval - TRUE if we should disable the media eject button. FALSE + if we want to enable it. + +Return Value: + + Status of the operation. + +--*/ + +{ + PIRP Irp; + KIRQL SavedIrql; + NTSTATUS Status; + FAT_SYNC_CONTEXT SyncContext; + PREVENT_MEDIA_REMOVAL Prevent; + + UNREFERENCED_PARAMETER( IrpContext ); + + // + // If PreventRemoval is the same as VCB_STATE_FLAG_REMOVAL_PREVENTED, + // no-op this call, otherwise toggle the state of the flag. + // + + KeAcquireSpinLock( &FatData.GeneralSpinLock, &SavedIrql ); + + if ((PreventRemoval ^ + BooleanFlagOn(Vcb->VcbState, VCB_STATE_FLAG_REMOVAL_PREVENTED)) == 0) { + + KeReleaseSpinLock( &FatData.GeneralSpinLock, SavedIrql ); + + return STATUS_SUCCESS; + + } else { + + Vcb->VcbState ^= VCB_STATE_FLAG_REMOVAL_PREVENTED; + + KeReleaseSpinLock( &FatData.GeneralSpinLock, SavedIrql ); + } + + Prevent.PreventMediaRemoval = PreventRemoval; + + KeInitializeEvent( &SyncContext.Event, NotificationEvent, FALSE ); + + // + // We build this IRP using a junk Iosb that will receive the final + // completion status since we won't be around for it. + // + // We fill in the UserIosb manually below, + // So passing NULL for the final parameter is ok in this special case. + // +#pragma warning(suppress: 6387) + Irp = IoBuildDeviceIoControlRequest( IOCTL_DISK_MEDIA_REMOVAL, + Vcb->TargetDeviceObject, + &Prevent, + sizeof(PREVENT_MEDIA_REMOVAL), + NULL, + 0, + FALSE, + NULL, + NULL ); + + if ( Irp != NULL ) { + + // + // Use our special completion routine which will remove the requirement that + // the caller must be below APC level. All it tells us is that the Irp got + // back, but will not tell us if it was succesful or not. We don't care, + // and there is of course no fallback if the attempt to prevent removal + // doesn't work for some mysterious reason. + // + // Normally, all IO is done at passive level. However, MM needs to be able + // to issue IO with fast mutexes locked down, which raises us to APC. The + // overlying IRP is set up to complete in yet another magical fashion even + // though APCs are disabled, and any IRPage we do in these cases has to do + // the same. Marking media dirty (and toggling eject state) is one. + // + + Irp->UserIosb = &Irp->IoStatus; + + IoSetCompletionRoutine( Irp, + FatSpecialSyncCompletionRoutine, + &SyncContext, + TRUE, + TRUE, + TRUE ); + + Status = IoCallDriver( Vcb->TargetDeviceObject, Irp ); + + if (Status == STATUS_PENDING) { + + (VOID) KeWaitForSingleObject( &SyncContext.Event, + Executive, + KernelMode, + FALSE, + NULL ); + + Status = SyncContext.Iosb.Status; + } + + return Status; + } + + return STATUS_INSUFFICIENT_RESOURCES; +} + + +NTSTATUS +FatPerformDevIoCtrl ( + IN PIRP_CONTEXT IrpContext, + IN ULONG IoControlCode, + IN PDEVICE_OBJECT Device, + IN PVOID InputBuffer OPTIONAL, + IN ULONG InputBufferLength, + OUT PVOID OutputBuffer OPTIONAL, + IN ULONG OutputBufferLength, + IN BOOLEAN InternalDeviceIoControl, + IN BOOLEAN OverrideVerify, + OUT PIO_STATUS_BLOCK Iosb OPTIONAL + ) + +/*++ + +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; + } + + return Status; +} + +PMDL +FatBuildZeroMdl ( + __in PIRP_CONTEXT IrpContext, + __in ULONG Length + ) +/*++ + +Routine Description: + + Create an efficient mdl that describe a given length of zeros. We'll only + use a one page buffer and make a mdl that maps all the pages back to the single + physical page. We'll default to a smaller size buffer down to 1 PAGE if memory + is tight. The caller should check the Mdl->ByteCount to see the true size + +Arguments: + + Length - The desired length of the zero buffer. We may return less than this + +Return Value: + + a MDL if successful / NULL if not + +--*/ + +{ + PMDL ZeroMdl; + ULONG SavedByteCount; + PPFN_NUMBER Page; + ULONG i; + + UNREFERENCED_PARAMETER( IrpContext ); + + // + // Spin down trying to get an MDL which can describe our operation. + // + + while (TRUE) { + + ZeroMdl = IoAllocateMdl( FatData.ZeroPage, Length, FALSE, FALSE, NULL ); + + // + // Throttle ourselves to what we've physically allocated. Note that + // we could have started with an odd multiple of this number. If we + // tried for exactly that size and failed, we're toast. + // + + if (ZeroMdl || (Length <= PAGE_SIZE)) { + + break; + } + + // + // Fallback by half and round down to a page multiple. + // + + ASSERT( IrpContext->Vcb->Bpb.BytesPerSector <= PAGE_SIZE ); + Length = BlockAlignTruncate( Length / 2, PAGE_SIZE ); + if (Length < PAGE_SIZE) { + Length = PAGE_SIZE; + } + } + + if (ZeroMdl == NULL) { + return NULL; + } + + // + // If we have throttled all the way down, stop and just build a + // simple MDL describing our previous allocation. + // + + if (Length == PAGE_SIZE) { + + MmBuildMdlForNonPagedPool( ZeroMdl ); + return ZeroMdl; + } + + // + // Now we will temporarily lock the allocated pages + // only, and then replicate the page frame numbers through + // the entire Mdl to keep writing the same pages of zeros. + // + // It would be nice if Mm exported a way for us to not have + // to pull the Mdl apart and rebuild it ourselves, but this + // is so bizzare a purpose as to be tolerable. + // + + SavedByteCount = ZeroMdl->ByteCount; + ZeroMdl->ByteCount = PAGE_SIZE; + MmBuildMdlForNonPagedPool( ZeroMdl ); + + ZeroMdl->MdlFlags &= ~MDL_SOURCE_IS_NONPAGED_POOL; + ZeroMdl->MdlFlags |= MDL_PAGES_LOCKED; + ZeroMdl->MappedSystemVa = NULL; + ZeroMdl->StartVa = NULL; + ZeroMdl->ByteCount = SavedByteCount; + Page = MmGetMdlPfnArray( ZeroMdl ); + for (i = 1; i < (ADDRESS_AND_SIZE_TO_SPAN_PAGES( 0, SavedByteCount )); i++) { + *(Page + i) = *(Page); + } + + + return ZeroMdl; +} + + diff --git a/filesys/fastfat/dirctrl.c b/filesys/fastfat/dirctrl.c new file mode 100644 index 000000000..451acc996 --- /dev/null +++ b/filesys/fastfat/dirctrl.c @@ -0,0 +1,1607 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + DirCtrl.c + +Abstract: + + This module implements the File Directory Control routines for Fat called + by the dispatch driver. + + +--*/ + +#include "FatProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (FAT_BUG_CHECK_DIRCTRL) + +// +// The local debug trace level +// + +#define Dbg (DEBUG_TRACE_DIRCTRL) + +WCHAR Fat8QMdot3QM[12] = { DOS_QM, DOS_QM, DOS_QM, DOS_QM, DOS_QM, DOS_QM, DOS_QM, DOS_QM, + L'.', DOS_QM, DOS_QM, DOS_QM}; + +// +// Local procedure prototypes +// + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatQueryDirectory ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +VOID +FatGetDirTimes( + PIRP_CONTEXT IrpContext, + PDIRENT Dirent, + PFILE_DIRECTORY_INFORMATION DirInfo + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatNotifyChangeDirectory ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, FatCommonDirectoryControl) +#pragma alloc_text(PAGE, FatFsdDirectoryControl) +#pragma alloc_text(PAGE, FatNotifyChangeDirectory) +#pragma alloc_text(PAGE, FatQueryDirectory) +#pragma alloc_text(PAGE, FatGetDirTimes) + +#endif + + +_Function_class_(IRP_MJ_DIRECTORY_CONTROL) +_Function_class_(DRIVER_DISPATCH) +NTSTATUS +FatFsdDirectoryControl ( + _In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + _Inout_ PIRP Irp + ) + +/*++ + +Routine Description: + + This routine implements the FSD part of directory control + +Arguments: + + VolumeDeviceObject - Supplies the volume device object where the + file exists + + Irp - Supplies the Irp being processed + +Return Value: + + NTSTATUS - The FSD status for the IRP + +--*/ + +{ + NTSTATUS Status; + PIRP_CONTEXT IrpContext = NULL; + + BOOLEAN TopLevel; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatFsdDirectoryControl\n", 0); + + // + // Call the common directory Control routine, with blocking allowed if + // synchronous + // + + FsRtlEnterFileSystem(); + + TopLevel = FatIsIrpTopLevel( Irp ); + + try { + + IrpContext = FatCreateIrpContext( Irp, CanFsdWait( Irp ) ); + + Status = FatCommonDirectoryControl( IrpContext, Irp ); + + } except(FatExceptionFilter( IrpContext, GetExceptionInformation() )) { + + // + // We had some trouble trying to perform the requested + // operation, so we'll abort the I/O request with + // the error status that we get back from the + // execption code + // + + Status = FatProcessException( IrpContext, Irp, GetExceptionCode() ); + } + + if (TopLevel) { IoSetTopLevelIrp( NULL ); } + + FsRtlExitFileSystem(); + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "FatFsdDirectoryControl -> %08lx\n", Status); + + UNREFERENCED_PARAMETER( VolumeDeviceObject ); + + return Status; +} + + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatCommonDirectoryControl ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the common routine for doing directory 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; + + PAGED_CODE(); + + // + // Get a pointer to the current Irp stack location + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "FatCommonDirectoryControl\n", 0); + DebugTrace( 0, Dbg, "Irp = %p\n", Irp ); + DebugTrace( 0, Dbg, "MinorFunction = %08lx\n", IrpSp->MinorFunction ); + + // + // 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 = FatQueryDirectory( IrpContext, Irp ); + break; + + case IRP_MN_NOTIFY_CHANGE_DIRECTORY: + + Status = FatNotifyChangeDirectory( IrpContext, Irp ); + break; + + default: + + DebugTrace(0, Dbg, "Invalid Directory Control Minor Function %08lx\n", IrpSp->MinorFunction); + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_DEVICE_REQUEST ); + Status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + + DebugTrace(-1, Dbg, "FatCommonDirectoryControl -> %08lx\n", Status); + + return Status; +} + + +// +// Local Support Routine +// + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatQueryDirectory ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the query directory 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; + + PVCB Vcb; + PDCB Dcb; + PCCB Ccb; + PBCB Bcb; + + ULONG i; + PUCHAR Buffer; + CLONG UserBufferLength; + + PUNICODE_STRING UniArgFileName; + WCHAR LongFileNameBuffer[ FAT_CREATE_INITIAL_NAME_BUF_SIZE]; + UNICODE_STRING LongFileName; + UNICODE_STRING OrigFileName; + FILE_INFORMATION_CLASS FileInformationClass; + ULONG FileIndex; + ULONG MatchFlags = 0; + BOOLEAN RestartScan; + BOOLEAN ReturnSingleEntry; + BOOLEAN IndexSpecified; + + BOOLEAN InitialQuery; + VBO CurrentVbo = 0; + BOOLEAN UpdateCcb; + PDIRENT Dirent; + UCHAR Fat8Dot3Buffer[12]; + OEM_STRING Fat8Dot3String; + ULONG DiskAllocSize; + + ULONG NextEntry; + ULONG LastEntry; + + PFILE_DIRECTORY_INFORMATION DirInfo; + PFILE_FULL_DIR_INFORMATION FullDirInfo; + PFILE_BOTH_DIR_INFORMATION BothDirInfo; + PFILE_ID_FULL_DIR_INFORMATION IdFullDirInfo; + PFILE_ID_BOTH_DIR_INFORMATION IdBothDirInfo; + PFILE_NAMES_INFORMATION NamesInfo; + + PAGED_CODE(); + + // + // Get the current Stack location + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + // + // Display the input values. + // + DebugTrace(+1, Dbg, "FatQueryDirectory...\n", 0); + DebugTrace( 0, Dbg, " Wait = %08lx\n", FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT)); + DebugTrace( 0, Dbg, " Irp = %p\n", Irp); + DebugTrace( 0, Dbg, " ->Length = %08lx\n", IrpSp->Parameters.QueryDirectory.Length); + DebugTrace( 0, Dbg, " ->FileName = %wZ\n", IrpSp->Parameters.QueryDirectory.FileName); + DebugTrace( 0, Dbg, " ->FileInformationClass = %08lx\n", IrpSp->Parameters.QueryDirectory.FileInformationClass); + DebugTrace( 0, Dbg, " ->FileIndex = %08lx\n", IrpSp->Parameters.QueryDirectory.FileIndex); + DebugTrace( 0, Dbg, " ->UserBuffer = %p\n", Irp->AssociatedIrp.SystemBuffer); + DebugTrace( 0, Dbg, " ->RestartScan = %08lx\n", FlagOn( IrpSp->Flags, SL_RESTART_SCAN )); + DebugTrace( 0, Dbg, " ->ReturnSingleEntry = %08lx\n", FlagOn( IrpSp->Flags, SL_RETURN_SINGLE_ENTRY )); + DebugTrace( 0, Dbg, " ->IndexSpecified = %08lx\n", FlagOn( IrpSp->Flags, SL_INDEX_SPECIFIED )); + + // + // Reference our input parameters to make things easier + // + + UserBufferLength = IrpSp->Parameters.QueryDirectory.Length; + + FileInformationClass = IrpSp->Parameters.QueryDirectory.FileInformationClass; + FileIndex = IrpSp->Parameters.QueryDirectory.FileIndex; + + UniArgFileName = IrpSp->Parameters.QueryDirectory.FileName; + + RestartScan = BooleanFlagOn(IrpSp->Flags, SL_RESTART_SCAN); + ReturnSingleEntry = BooleanFlagOn(IrpSp->Flags, SL_RETURN_SINGLE_ENTRY); + IndexSpecified = BooleanFlagOn(IrpSp->Flags, SL_INDEX_SPECIFIED); + + // + // Check on the type of open. We return invalid parameter for all + // but UserDirectoryOpens. Also check that the filename is a valid + // UNICODE string. + // + + if (FatDecodeFileObject( IrpSp->FileObject, + &Vcb, + &Dcb, + &Ccb) != UserDirectoryOpen || + (UniArgFileName && + UniArgFileName->Length % sizeof(WCHAR))) { + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + DebugTrace(-1, Dbg, "FatQueryDirectory -> STATUS_INVALID_PARAMETER\n", 0); + + return STATUS_INVALID_PARAMETER; + } + + // + // Initialize the local variables. + // + + Bcb = NULL; + UpdateCcb = TRUE; + Dirent = NULL; + + Fat8Dot3String.MaximumLength = 12; + Fat8Dot3String.Buffer = (PCHAR)Fat8Dot3Buffer; + + LongFileName.Length = 0; + LongFileName.MaximumLength = sizeof( LongFileNameBuffer); + LongFileName.Buffer = LongFileNameBuffer; + + InitialQuery = (BOOLEAN)((Ccb->UnicodeQueryTemplate.Buffer == NULL) && + !FlagOn(Ccb->Flags, CCB_FLAG_MATCH_ALL)); + Status = STATUS_SUCCESS; + Irp->IoStatus.Information = 0; + + DiskAllocSize = 1 << Vcb->AllocationSupport.LogOfBytesPerCluster; + + // + // If this is the initial query, then grab exclusive access in + // order to update the search string in the Ccb. We may + // discover that we are not the initial query once we grab the Fcb + // and downgrade our status. + // + // If restartscan is set, we may be replacing the query template, + // so take the FCB exclusive to protect against multiple people + // changing the CCB at once. + // + + if (InitialQuery || RestartScan) { + + if (!FatAcquireExclusiveFcb( IrpContext, Dcb )) { + + DebugTrace(0, Dbg, "FatQueryDirectory -> Enqueue to Fsp\n", 0); + Status = FatFsdPostRequest( IrpContext, Irp ); + DebugTrace(-1, Dbg, "FatQueryDirectory -> %08lx\n", Status); + + return Status; + } + + if (!RestartScan && (Ccb->UnicodeQueryTemplate.Buffer != NULL)) { + + InitialQuery = FALSE; + + FatConvertToSharedFcb( IrpContext, Dcb ); + } + + } else { + + if (!FatAcquireSharedFcb( IrpContext, Dcb )) { + + DebugTrace(0, Dbg, "FatQueryDirectory -> Enqueue to Fsp\n", 0); + Status = FatFsdPostRequest( IrpContext, Irp ); + DebugTrace(-1, Dbg, "FatQueryDirectory -> %08lx\n", Status); + + return Status; + + } + } + + try { + + ULONG BaseLength; + ULONG BytesConverted; + + // + // 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. + // + + Buffer = FatMapUserBuffer( IrpContext, Irp ); + + // + // Make sure the Dcb is still good. + // + + FatVerifyFcb( IrpContext, Dcb ); + + // + // Determine where to start the scan. Highest priority is given + // to the file index. Lower priority is the restart flag. If + // neither of these is specified, then the Vbo offset field in the + // Ccb is used. + // + + if (IndexSpecified) { + + CurrentVbo = FileIndex + sizeof( DIRENT ); + + } else if (RestartScan) { + + CurrentVbo = 0; + Ccb->OffsetToStartSearchFrom = 0; + + } else { + + CurrentVbo = Ccb->OffsetToStartSearchFrom; + } + + // + // If this is the first try then allocate a buffer for the file + // name. + // + + if (InitialQuery || + (RestartScan && UniArgFileName != NULL && UniArgFileName->Length != 0)) { + + // + // If we're restarting the scan, clear out the pattern in the Ccb and regenerate it, + // + + if (RestartScan) { + + if (Ccb->UnicodeQueryTemplate.Buffer) { + + if (FlagOn(Ccb->Flags, CCB_FLAG_FREE_UNICODE)) { + + ExFreePoolWithTag(Ccb->UnicodeQueryTemplate.Buffer, TAG_FILENAME_BUFFER); + ClearFlag(Ccb->Flags, CCB_FLAG_FREE_UNICODE); + } + + Ccb->UnicodeQueryTemplate.Buffer = NULL; + Ccb->UnicodeQueryTemplate.Length = 0; + Ccb->UnicodeQueryTemplate.MaximumLength = 0; + } + + if (Ccb->OemQueryTemplate.Wild.Buffer) { + + if (FlagOn(Ccb->Flags, CCB_FLAG_FREE_OEM_BEST_FIT)) { + + RtlFreeOemString( &Ccb->OemQueryTemplate.Wild ); + ClearFlag(Ccb->Flags, CCB_FLAG_FREE_OEM_BEST_FIT); + } + + Ccb->OemQueryTemplate.Wild.Buffer = NULL; + Ccb->OemQueryTemplate.Wild.Length = 0; + Ccb->OemQueryTemplate.Wild.MaximumLength = 0; + } + + Ccb->ContainsWildCards = FALSE; + ClearFlag(Ccb->Flags, CCB_FLAG_MATCH_ALL); + ClearFlag(Ccb->Flags, CCB_FLAG_FREE_UNICODE); + ClearFlag(Ccb->Flags, CCB_FLAG_SKIP_SHORT_NAME_COMPARE); + ClearFlag(Ccb->Flags, CCB_FLAG_QUERY_TEMPLATE_MIXED); + + } + + // + // If either: + // + // - No name was specified + // - An empty name was specified + // - We received a '*' + // - The user specified the DOS equivolent of ????????.??? + // + // then match all names. + // + + if ((UniArgFileName == NULL) || + (UniArgFileName->Length == 0) || + (UniArgFileName->Buffer == NULL) || + ((UniArgFileName->Length == sizeof(WCHAR)) && + (UniArgFileName->Buffer[0] == L'*')) || + ((UniArgFileName->Length == 12*sizeof(WCHAR)) && + (RtlEqualMemory( UniArgFileName->Buffer, + Fat8QMdot3QM, + 12*sizeof(WCHAR) )))) { + + Ccb->ContainsWildCards = TRUE; + + SetFlag( Ccb->Flags, CCB_FLAG_MATCH_ALL ); + + } else { + + BOOLEAN ExtendedName = FALSE; + OEM_STRING LocalBestFit; + + // + // First and formost, see if the name has wild cards. + // + + Ccb->ContainsWildCards = + FsRtlDoesNameContainWildCards( UniArgFileName ); + + // + // Now check to see if the name contains any extended + // characters + // + + for (i=0; i < UniArgFileName->Length / sizeof(WCHAR); i++) { + + if (UniArgFileName->Buffer[i] >= 0x80) { + + ExtendedName = TRUE; + break; + } + } + + // + // OK, now do the conversions we need. + // + + if (ExtendedName) { + + Status = RtlUpcaseUnicodeString( &Ccb->UnicodeQueryTemplate, + UniArgFileName, + TRUE ); + + if (!NT_SUCCESS(Status)) { + + try_return( Status ); + } + + SetFlag( Ccb->Flags, CCB_FLAG_FREE_UNICODE ); + + // + // Upcase the name and convert it to the Oem code page. + // + + Status = RtlUpcaseUnicodeStringToCountedOemString( &LocalBestFit, + UniArgFileName, + TRUE ); + + // + // If this conversion failed for any reason other than + // an unmappable character fail the request. + // + + if (!NT_SUCCESS(Status)) { + + if (Status == STATUS_UNMAPPABLE_CHARACTER) { + + SetFlag( Ccb->Flags, CCB_FLAG_SKIP_SHORT_NAME_COMPARE ); + + } else { + + try_return( Status ); + } + + } else { + + SetFlag( Ccb->Flags, CCB_FLAG_FREE_OEM_BEST_FIT ); + } + + } else { + + PVOID Buffers; + + // + // This case is optimized because I know I only have to + // worry about a-z. + // + + Buffers = FsRtlAllocatePoolWithTag( PagedPool, + UniArgFileName->Length + + UniArgFileName->Length / sizeof(WCHAR), + TAG_FILENAME_BUFFER ); + + Ccb->UnicodeQueryTemplate.Buffer = Buffers; + Ccb->UnicodeQueryTemplate.Length = UniArgFileName->Length; + Ccb->UnicodeQueryTemplate.MaximumLength = UniArgFileName->Length; + + LocalBestFit.Buffer = (PCHAR)Buffers + UniArgFileName->Length; + LocalBestFit.Length = UniArgFileName->Length / sizeof(WCHAR); + LocalBestFit.MaximumLength = LocalBestFit.Length; + + SetFlag( Ccb->Flags, CCB_FLAG_FREE_UNICODE ); + + for (i=0; i < UniArgFileName->Length / sizeof(WCHAR); i++) { + + WCHAR c = UniArgFileName->Buffer[i]; + + LocalBestFit.Buffer[i] = (UCHAR) + (Ccb->UnicodeQueryTemplate.Buffer[i] = + (c < 'a' ? c : c <= 'z' ? c - ('a' - 'A') : c)); + } + } + + // + // At this point we now have the upcased unicode name, + // and the two Oem names if they could be represented in + // this code page. + // + // Now determine if the Oem names are legal for what we + // going to try and do. Mark them as not usable is they + // are not legal. Note that we can optimize extended names + // since they are actually both the same string. + // + + if (!FlagOn( Ccb->Flags, CCB_FLAG_SKIP_SHORT_NAME_COMPARE ) && + !FatIsNameShortOemValid( IrpContext, + LocalBestFit, + Ccb->ContainsWildCards, + FALSE, + FALSE )) { + + if (ExtendedName) { + + RtlFreeOemString( &LocalBestFit ); + ClearFlag( Ccb->Flags, CCB_FLAG_FREE_OEM_BEST_FIT ); + } + + SetFlag( Ccb->Flags, CCB_FLAG_SKIP_SHORT_NAME_COMPARE ); + } + + // + // OK, now both locals oem strings correctly reflect their + // usability. Now we want to load up the Ccb structure. + // + // Now we will branch on two paths of wheather the name + // is wild or not. + // + + if (!FlagOn( Ccb->Flags, CCB_FLAG_SKIP_SHORT_NAME_COMPARE )) { + + if (Ccb->ContainsWildCards) { + + Ccb->OemQueryTemplate.Wild = LocalBestFit; + + } else { + + FatStringTo8dot3( IrpContext, + LocalBestFit, + &Ccb->OemQueryTemplate.Constant ); + + if (FlagOn(Ccb->Flags, CCB_FLAG_FREE_OEM_BEST_FIT)) { + + RtlFreeOemString( &LocalBestFit ); + ClearFlag( Ccb->Flags, CCB_FLAG_FREE_OEM_BEST_FIT ); + } + } + } + } + + // + // We convert to shared access. + // + + FatConvertToSharedFcb( IrpContext, Dcb ); + } + + LastEntry = 0; + NextEntry = 0; + + switch (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: + + try_return( Status = STATUS_INVALID_INFO_CLASS ); + } + + // + // 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 ) { + + VBO NextVbo; + ULONG FileNameLength; + ULONG BytesRemainingInBuffer; + BOOLEAN FileNameDos; + + DebugTrace(0, Dbg, "FatQueryDirectory -> Top of loop\n", 0); + + // + // If the user had requested only a single match and we have + // returned that, then we stop at this point. + // + + if (ReturnSingleEntry && NextEntry != 0) { + + try_return( Status ); + } + + + // + // We call FatLocateDirent to lock down the next matching dirent. + // + + FatLocateDirent( IrpContext, + Dcb, + Ccb, + CurrentVbo, + &MatchFlags, + &Dirent, + &Bcb, + &NextVbo, + &FileNameDos, + &LongFileName, + &OrigFileName ); + + // + // 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 (!Dirent) { + + DebugTrace(0, Dbg, "FatQueryDirectory -> No dirent\n", 0); + + if (NextEntry == 0) { + + UpdateCcb = FALSE; + + if (InitialQuery) { + + Status = STATUS_NO_SUCH_FILE; + + } else { + + Status = STATUS_NO_MORE_FILES; + } + } + + try_return( Status ); + } + + + // + // 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 { + + Fat8dot3ToString( IrpContext, Dirent, TRUE, &Fat8Dot3String ); + + + if (LongFileName.Length == 0) { + + // + // Now we have an entry to return to our caller. We'll convert + // the name from the form in the dirent to a . form. + // We'll case on the type of information requested and fill up + // the user buffer if everything fits. + // + + // + // Determine the UNICODE length of the file name. + // + + FileNameLength = RtlOemStringToCountedUnicodeSize(&Fat8Dot3String); + + // + // 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. + // + + BytesRemainingInBuffer = UserBufferLength - NextEntry; + + if ( (NextEntry != 0) && + ( (BaseLength + FileNameLength > BytesRemainingInBuffer) || + (UserBufferLength < NextEntry) ) ) { + + DebugTrace(0, Dbg, "Next entry won't fit\n", 0); + + try_return( Status = STATUS_SUCCESS ); + } + + NT_ASSERT( BytesRemainingInBuffer >= BaseLength ); + + // + // Zero the base part of the structure. + // + + RtlZeroMemory( &Buffer[NextEntry], BaseLength ); + + switch ( FileInformationClass ) { + + // + // Now fill the base parts of the strucure that are applicable. + // + + case FileBothDirectoryInformation: + case FileFullDirectoryInformation: + case FileIdBothDirectoryInformation: + case FileIdFullDirectoryInformation: + + DebugTrace(0, Dbg, "FatQueryDirectory -> Getting file full directory information\n", 0); + + // + // Get the Ea file length. + // + + FullDirInfo = (PFILE_FULL_DIR_INFORMATION)&Buffer[NextEntry]; + + // + // If the EAs are corrupt, ignore the error. We don't want + // to abort the directory query. + // + + try { + + FatGetEaLength( IrpContext, + Vcb, + Dirent, + &FullDirInfo->EaSize ); + + } except(EXCEPTION_EXECUTE_HANDLER) { + + FatResetExceptionState( IrpContext ); + FullDirInfo->EaSize = 0; + } + + case FileDirectoryInformation: + + DirInfo = (PFILE_DIRECTORY_INFORMATION)&Buffer[NextEntry]; + + FatGetDirTimes( IrpContext, Dirent, DirInfo ); + + DirInfo->EndOfFile.QuadPart = Dirent->FileSize; + + if (!FlagOn( Dirent->Attributes, FAT_DIRENT_ATTR_DIRECTORY )) { + + + DirInfo->AllocationSize.QuadPart = + (((Dirent->FileSize + DiskAllocSize - 1) / DiskAllocSize) * + DiskAllocSize ); + } + + if (Dirent->Attributes != 0) { + DirInfo->FileAttributes = Dirent->Attributes; + + + } else { + + DirInfo->FileAttributes = 0; + + DirInfo->FileAttributes |= FILE_ATTRIBUTE_NORMAL; + } + + DirInfo->FileIndex = NextVbo; + + DirInfo->FileNameLength = FileNameLength; + + DebugTrace(0, Dbg, "FatQueryDirectory -> Name = \"%Z\"\n", &Fat8Dot3String); + + break; + + case FileNamesInformation: + + DebugTrace(0, Dbg, "FatQueryDirectory -> Getting file names information\n", 0); + + NamesInfo = (PFILE_NAMES_INFORMATION)&Buffer[NextEntry]; + + NamesInfo->FileIndex = NextVbo; + + NamesInfo->FileNameLength = FileNameLength; + + DebugTrace(0, Dbg, "FatQueryDirectory -> Name = \"%Z\"\n", &Fat8Dot3String ); + + break; + + default: + +#pragma prefast( suppress:28159, "things are seriously wrong if we get here" ) + FatBugCheck( FileInformationClass, 0, 0 ); + } + + BytesConverted = 0; + + Status = RtlOemToUnicodeN( (PWCH)&Buffer[NextEntry + BaseLength], + BytesRemainingInBuffer - BaseLength, + &BytesConverted, + Fat8Dot3String.Buffer, + Fat8Dot3String.Length ); + + // + // Check for the case that a single entry doesn't fit. + // This should only get this far on the first entry + // + + if (BytesConverted < FileNameLength) { + + NT_ASSERT( NextEntry == 0 ); + Status = STATUS_BUFFER_OVERFLOW; + } + + // + // Set up the previous next entry offset + // + + *((PULONG)(&Buffer[LastEntry])) = NextEntry - LastEntry; + + // + // And indicate how much of the user buffer we have currently + // used up. We must compute this value before we long align + // ourselves for the next entry + // + + Irp->IoStatus.Information = QuadAlign( Irp->IoStatus.Information ) + + BaseLength + BytesConverted; + + // + // If something happened with the conversion, bail here. + // + + if ( !NT_SUCCESS( Status ) ) { + + try_return( NOTHING ); + } + + } else { + + ULONG ShortNameLength; + + FileNameLength = LongFileName.Length; + + // + // 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. + // + + BytesRemainingInBuffer = UserBufferLength - NextEntry; + + if ( (NextEntry != 0) && + ( (BaseLength + FileNameLength > BytesRemainingInBuffer) || + (UserBufferLength < NextEntry) ) ) { + + DebugTrace(0, Dbg, "Next entry won't fit\n", 0); + + try_return( Status = STATUS_SUCCESS ); + } + + NT_ASSERT( BytesRemainingInBuffer >= BaseLength ); + + // + // Zero the base part of the structure. + // + + RtlZeroMemory( &Buffer[NextEntry], BaseLength ); + + switch ( FileInformationClass ) { + + // + // Now fill the base parts of the strucure that are applicable. + // + + case FileBothDirectoryInformation: + case FileIdBothDirectoryInformation: + + BothDirInfo = (PFILE_BOTH_DIR_INFORMATION)&Buffer[NextEntry]; + + // + // Now we have an entry to return to our caller. We'll convert + // the name from the form in the dirent to a . form. + // We'll case on the type of information requested and fill up + // the user buffer if everything fits. + // + + Fat8dot3ToString( IrpContext, Dirent, FALSE, &Fat8Dot3String ); + + NT_ASSERT( Fat8Dot3String.Length <= 12 ); + + Status = RtlOemToUnicodeN( &BothDirInfo->ShortName[0], + 12*sizeof(WCHAR), + &ShortNameLength, + Fat8Dot3String.Buffer, + Fat8Dot3String.Length ); + + NT_ASSERT( Status != STATUS_BUFFER_OVERFLOW ); + NT_ASSERT( ShortNameLength <= 12*sizeof(WCHAR) ); + + // + // Copy the length into the dirinfo structure. Note + // that the LHS below is a USHORT, so it can not + // be specificed as the OUT parameter above. + // + + BothDirInfo->ShortNameLength = (UCHAR)ShortNameLength; + + // + // If something happened with the conversion, bail here. + // + + if ( !NT_SUCCESS( Status ) ) { + + try_return( NOTHING ); + } + + case FileFullDirectoryInformation: + case FileIdFullDirectoryInformation: + + DebugTrace(0, Dbg, "FatQueryDirectory -> Getting file full directory information\n", 0); + + // + // Get the Ea file length. + // + + FullDirInfo = (PFILE_FULL_DIR_INFORMATION)&Buffer[NextEntry]; + + // + // If the EAs are corrupt, ignore the error. We don't want + // to abort the directory query. + // + + try { + + FatGetEaLength( IrpContext, + Vcb, + Dirent, + &FullDirInfo->EaSize ); + + } except(EXCEPTION_EXECUTE_HANDLER) { + + FatResetExceptionState( IrpContext ); + FullDirInfo->EaSize = 0; + } + + case FileDirectoryInformation: + + DirInfo = (PFILE_DIRECTORY_INFORMATION)&Buffer[NextEntry]; + + FatGetDirTimes( IrpContext, Dirent, DirInfo ); + + DirInfo->EndOfFile.QuadPart = Dirent->FileSize; + + if (!FlagOn( Dirent->Attributes, FAT_DIRENT_ATTR_DIRECTORY )) { + + + DirInfo->AllocationSize.QuadPart = ( + (( Dirent->FileSize + + DiskAllocSize - 1 ) + / DiskAllocSize ) + * DiskAllocSize ); + } + + if (Dirent->Attributes != 0) { + DirInfo->FileAttributes = Dirent->Attributes; + + + } else { + + DirInfo->FileAttributes = 0; + + + DirInfo->FileAttributes |= FILE_ATTRIBUTE_NORMAL; + } + + + DirInfo->FileIndex = NextVbo; + + DirInfo->FileNameLength = FileNameLength; + + DebugTrace(0, Dbg, "FatQueryDirectory -> Name = \"%Z\"\n", &Fat8Dot3String); + + break; + + case FileNamesInformation: + + DebugTrace(0, Dbg, "FatQueryDirectory -> Getting file names information\n", 0); + + NamesInfo = (PFILE_NAMES_INFORMATION)&Buffer[NextEntry]; + + NamesInfo->FileIndex = NextVbo; + + NamesInfo->FileNameLength = FileNameLength; + + DebugTrace(0, Dbg, "FatQueryDirectory -> Name = \"%Z\"\n", &Fat8Dot3String ); + + break; + + default: + +#pragma prefast( suppress:28159, "things are seriously wrong if we get here" ) + FatBugCheck( FileInformationClass, 0, 0 ); + } + + BytesConverted = BytesRemainingInBuffer - BaseLength >= FileNameLength ? + FileNameLength : + BytesRemainingInBuffer - BaseLength; + + RtlCopyMemory( &Buffer[NextEntry + BaseLength], + &LongFileName.Buffer[0], + BytesConverted ); + + // + // Set up the previous next entry offset + // + + *((PULONG)(&Buffer[LastEntry])) = NextEntry - LastEntry; + + // + // And indicate how much of the user buffer we have currently + // used up. We must compute this value before we long align + // ourselves for the next entry + // + + Irp->IoStatus.Information = QuadAlign( Irp->IoStatus.Information ) + + BaseLength + BytesConverted; + + // + // Check for the case that a single entry doesn't fit. + // This should only get this far on the first entry. + // + + if (BytesConverted < FileNameLength) { + + NT_ASSERT( NextEntry == 0 ); + + try_return( Status = STATUS_BUFFER_OVERFLOW ); + } + } + + // + // Finish up by filling in the FileId + // + + switch ( FileInformationClass ) { + + case FileIdBothDirectoryInformation: + + IdBothDirInfo = (PFILE_ID_BOTH_DIR_INFORMATION)&Buffer[NextEntry]; + IdBothDirInfo->FileId.QuadPart = FatGenerateFileIdFromDirentAndOffset( Dcb, Dirent, NextVbo ); + break; + + case FileIdFullDirectoryInformation: + + IdFullDirInfo = (PFILE_ID_FULL_DIR_INFORMATION)&Buffer[NextEntry]; + IdFullDirInfo->FileId.QuadPart = FatGenerateFileIdFromDirentAndOffset( Dcb, Dirent, NextVbo ); + break; + + default: + break; + } + + } 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. + // + + Irp->IoStatus.Information = 0; + UpdateCcb = FALSE; + try_return( Status = GetExceptionCode()); + } + + // + // Set ourselves up for the next iteration + // + + LastEntry = NextEntry; + NextEntry += (ULONG)QuadAlign(BaseLength + BytesConverted); + + CurrentVbo = NextVbo + sizeof( DIRENT ); + } + + try_exit: NOTHING; + } finally { + + DebugUnwind( FatQueryDirectory ); + + FatReleaseFcb( IrpContext, Dcb ); + + // + // Unpin data in cache if still held. + // + + FatUnpinBcb( IrpContext, Bcb ); + + // + // Free any dynamically allocated string buffer + // + + FatFreeStringBuffer( &LongFileName); + + // + // Perform any cleanup. If this is the first query, then store + // the filename in the Ccb if successful. Also update the + // VBO index for the next search. This is done by transferring + // from shared access to exclusive access and copying the + // data from the local copies. + // + + if (!AbnormalTermination()) { + + if (UpdateCcb) { + + // + // Store the most recent VBO to use as a starting point for + // the next search. + // + + Ccb->OffsetToStartSearchFrom = CurrentVbo; + } + + FatCompleteRequest( IrpContext, Irp, Status ); + } + + DebugTrace(-1, Dbg, "FatQueryDirectory -> %08lx\n", Status); + + } + + return Status; +} + + +// +// Local Support Routine +// + +VOID +FatGetDirTimes( + PIRP_CONTEXT IrpContext, + PDIRENT Dirent, + PFILE_DIRECTORY_INFORMATION DirInfo + ) + +/*++ + +Routine Description: + + This routine pulls the date/time information from a dirent and fills + in the DirInfo structure. + +Arguments: + + Dirent - Supplies the dirent + DirInfo - Supplies the target structure + +Return Value: + + VOID + +--*/ + + +{ + PAGED_CODE(); + + // + // Start with the Last Write Time. + // + + DirInfo->LastWriteTime = + FatFatTimeToNtTime( IrpContext, + Dirent->LastWriteTime, + 0 ); + + // + // These fields are only non-zero when in Chicago mode. + // + + if (FatData.ChicagoMode) { + + // + // Do a quick check here for Creation and LastAccess + // times that are the same as the LastWriteTime. + // + + if (*((UNALIGNED LONG *)&Dirent->CreationTime) == + *((UNALIGNED LONG *)&Dirent->LastWriteTime)) { + + DirInfo->CreationTime.QuadPart = + + DirInfo->LastWriteTime.QuadPart + + Dirent->CreationMSec * 10 * 1000 * 10; + + } else { + + // + // Only do the really hard work if this field is non-zero. + // + + if (((PUSHORT)Dirent)[8] != 0) { + + DirInfo->CreationTime = + FatFatTimeToNtTime( IrpContext, + Dirent->CreationTime, + Dirent->CreationMSec ); + + } else { + + ExLocalTimeToSystemTime( &FatJanOne1980, + &DirInfo->CreationTime ); + } + } + + // + // Do a quick check for LastAccessDate. + // + + if (*((PUSHORT)&Dirent->LastAccessDate) == + *((PUSHORT)&Dirent->LastWriteTime.Date)) { + + PFAT_TIME WriteTime; + + WriteTime = &Dirent->LastWriteTime.Time; + + DirInfo->LastAccessTime.QuadPart = + DirInfo->LastWriteTime.QuadPart - + UInt32x32To64(((WriteTime->DoubleSeconds * 2) + + (WriteTime->Minute * 60) + + (WriteTime->Hour * 60 * 60)), + 1000 * 1000 * 10); + + } else { + + // + // Only do the really hard work if this field is non-zero. + // + + if (((PUSHORT)Dirent)[9] != 0) { + + DirInfo->LastAccessTime = + FatFatDateToNtTime( IrpContext, + Dirent->LastAccessDate ); + + } else { + + ExLocalTimeToSystemTime( &FatJanOne1980, + &DirInfo->LastAccessTime ); + } + } + } +} + + +// +// Local Support Routine +// + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatNotifyChangeDirectory ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the notify change directory 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; + PVCB Vcb; + PDCB Dcb; + PCCB Ccb; + ULONG CompletionFilter; + BOOLEAN WatchTree; + + BOOLEAN CompleteRequest; + + PAGED_CODE(); + + // + // Get the current Stack location + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "FatNotifyChangeDirectory...\n", 0); + DebugTrace( 0, Dbg, " Wait = %08lx\n", FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT)); + DebugTrace( 0, Dbg, " Irp = %p\n", Irp); + DebugTrace( 0, Dbg, " ->CompletionFilter = %08lx\n", IrpSp->Parameters.NotifyDirectory.CompletionFilter); + + // + // Always set the wait flag in the Irp context for the original request. + // + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ); + + // + // Assume we don't complete request. + // + + CompleteRequest = FALSE; + + // + // Check on the type of open. We return invalid parameter for all + // but UserDirectoryOpens. + // + + if (FatDecodeFileObject( IrpSp->FileObject, + &Vcb, + &Dcb, + &Ccb ) != UserDirectoryOpen) { + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + DebugTrace(-1, Dbg, "FatQueryDirectory -> STATUS_INVALID_PARAMETER\n", 0); + + return STATUS_INVALID_PARAMETER; + + } + + // + // Reference our input parameter to make things easier + // + + CompletionFilter = IrpSp->Parameters.NotifyDirectory.CompletionFilter; + WatchTree = BooleanFlagOn( IrpSp->Flags, SL_WATCH_TREE ); + + // + // Try to acquire exclusive access to the Dcb and enqueue the Irp to the + // Fsp if we didn't get access + // + + if (!FatAcquireExclusiveFcb( IrpContext, Dcb )) { + + DebugTrace(0, Dbg, "FatNotifyChangeDirectory -> Cannot Acquire Fcb\n", 0); + + Status = FatFsdPostRequest( IrpContext, Irp ); + + DebugTrace(-1, Dbg, "FatNotifyChangeDirectory -> %08lx\n", Status); + return Status; + } + + try { + + // + // Make sure the Fcb is still good + // + + FatVerifyFcb( IrpContext, Dcb ); + + // + // We need the full name. + // + + FatSetFullFileNameInFcb( IrpContext, Dcb ); + + // + // If the file is marked as DELETE_PENDING then complete this + // request immediately. + // + + if (FlagOn( Dcb->FcbState, FCB_STATE_DELETE_ON_CLOSE )) { + + FatRaiseStatus( IrpContext, STATUS_DELETE_PENDING ); + } + + // + // Call the Fsrtl package to process the request. + // + + FsRtlNotifyFullChangeDirectory( Vcb->NotifySync, + &Vcb->DirNotifyList, + Ccb, + (PSTRING)&Dcb->FullFileName, + WatchTree, + FALSE, + CompletionFilter, + Irp, + NULL, + NULL ); + + Status = STATUS_PENDING; + + CompleteRequest = TRUE; + + } finally { + + DebugUnwind( FatNotifyChangeDirectory ); + + FatReleaseFcb( IrpContext, Dcb ); + + // + // If the dir notify package is holding the Irp, we discard the + // the IrpContext. + // + + if (CompleteRequest) { + + FatCompleteRequest( IrpContext, FatNull, 0 ); + } + + DebugTrace(-1, Dbg, "FatNotifyChangeDirectory -> %08lx\n", Status); + } + + return Status; +} + diff --git a/filesys/fastfat/dirsup.c b/filesys/fastfat/dirsup.c new file mode 100644 index 000000000..14cc9238f --- /dev/null +++ b/filesys/fastfat/dirsup.c @@ -0,0 +1,3879 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + DirSup.c + +Abstract: + + This module implements the dirent support routines for Fat. + + +--*/ + +#include "FatProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (FAT_BUG_CHECK_DIRSUP) + +// +// Local debug trace level +// + +#define Dbg (DEBUG_TRACE_DIRSUP) + +// +// The following three macro all assume the input dirent has been zeroed. +// + +// +// VOID +// FatConstructDot ( +// IN PIRP_CONTEXT IrpContext, +// IN PDCB Directory, +// IN PDIRENT ParentDirent, +// IN OUT PDIRENT Dirent +// ); +// +// The following macro is called to initalize the "." dirent. +// +// Always setting FirstClusterOfFileHi is OK because it will be zero +// unless we're working on a FAT 32 disk. +// + +#define FatConstructDot(IRPCONTEXT,DCB,PARENT,DIRENT) { \ + \ + RtlCopyMemory( (PUCHAR)(DIRENT), ". ", 11 ); \ + (DIRENT)->Attributes = FAT_DIRENT_ATTR_DIRECTORY; \ + (DIRENT)->LastWriteTime = (PARENT)->LastWriteTime; \ + if (FatData.ChicagoMode) { \ + (DIRENT)->CreationTime = (PARENT)->CreationTime; \ + (DIRENT)->CreationMSec = (PARENT)->CreationMSec; \ + (DIRENT)->LastAccessDate = (PARENT)->LastAccessDate; \ + } \ + (DIRENT)->FirstClusterOfFile = \ + (USHORT)(DCB)->FirstClusterOfFile; \ + (DIRENT)->FirstClusterOfFileHi = \ + (USHORT)((DCB)->FirstClusterOfFile/0x10000); \ +} + +// +// VOID +// FatConstructDotDot ( +// IN PIRP_CONTEXT IrpContext, +// IN PDCB Directory, +// IN PDIRENT ParentDirent, +// IN OUT PDIRENT Dirent +// ); +// +// The following macro is called to initalize the ".." dirent. +// +// Always setting FirstClusterOfFileHi is OK because it will be zero +// unless we're working on a FAT 32 disk. +// + +#define FatConstructDotDot(IRPCONTEXT,DCB,PARENT,DIRENT) { \ + \ + RtlCopyMemory( (PUCHAR)(DIRENT), ".. ", 11 ); \ + (DIRENT)->Attributes = FAT_DIRENT_ATTR_DIRECTORY; \ + (DIRENT)->LastWriteTime = (PARENT)->LastWriteTime; \ + if (FatData.ChicagoMode) { \ + (DIRENT)->CreationTime = (PARENT)->CreationTime; \ + (DIRENT)->CreationMSec = (PARENT)->CreationMSec; \ + (DIRENT)->LastAccessDate = (PARENT)->LastAccessDate; \ + } \ + if (NodeType((DCB)->ParentDcb) == FAT_NTC_ROOT_DCB) { \ + (DIRENT)->FirstClusterOfFile = 0; \ + (DIRENT)->FirstClusterOfFileHi = 0; \ + } else { \ + (DIRENT)->FirstClusterOfFile = (USHORT) \ + ((DCB)->ParentDcb->FirstClusterOfFile); \ + (DIRENT)->FirstClusterOfFileHi = (USHORT) \ + ((DCB)->ParentDcb->FirstClusterOfFile/0x10000); \ + } \ +} + +// +// VOID +// FatConstructEndDirent ( +// IN PIRP_CONTEXT IrpContext, +// IN OUT PDIRENT Dirent +// ); +// +// The following macro created the end dirent. Note that since the +// dirent was zeroed, the first byte of the name already contains 0x0, +// so there is nothing to do. +// + +#define FatConstructEndDirent(IRPCONTEXT,DIRENT) NOTHING + +// +// VOID +// FatReadDirent ( +// IN PIRP_CONTEXT IrpContext, +// IN PDCB Dcb, +// IN VBO Vbo, +// OUT PBCB *Bcb, +// OUT PVOID *Dirent, +// OUT PNTSTATUS Status +// ); +// + +// +// This macro reads in a page of dirents when we step onto a new page, +// or this is the first iteration of a loop and Bcb is NULL. +// + +#define FatReadDirent(IRPCONTEXT,DCB,VBO,BCB,DIRENT,STATUS) \ +if ((VBO) >= (DCB)->Header.AllocationSize.LowPart) { \ + *(STATUS) = STATUS_END_OF_FILE; \ + FatUnpinBcb( (IRPCONTEXT), *(BCB) ); \ +} else if ( ((VBO) % PAGE_SIZE == 0) || (*(BCB) == NULL) ) { \ + FatUnpinBcb( (IRPCONTEXT), *(BCB) ); \ + FatReadDirectoryFile( (IRPCONTEXT), \ + (DCB), \ + (VBO) & ~(PAGE_SIZE - 1), \ + PAGE_SIZE, \ + FALSE, \ + (BCB), \ + (PVOID *)(DIRENT), \ + (STATUS) ); \ + *(DIRENT) = (PVOID)((PUCHAR)*(DIRENT) + ((VBO) % PAGE_SIZE)); \ +} + +// +// Internal support routines +// + +UCHAR +FatComputeLfnChecksum ( + PDIRENT Dirent + ); + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatRescanDirectory ( + PIRP_CONTEXT IrpContext, + PDCB Dcb + ); + +_Requires_lock_held_(_Global_critical_region_) +ULONG +FatDefragDirectory ( + IN PIRP_CONTEXT IrpContext, + IN PDCB Dcb, + IN ULONG DirentsNeeded + ); + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, FatComputeLfnChecksum) +#pragma alloc_text(PAGE, FatConstructDirent) +#pragma alloc_text(PAGE, FatConstructLabelDirent) +#pragma alloc_text(PAGE, FatCreateNewDirent) +#pragma alloc_text(PAGE, FatDefragDirectory) +#pragma alloc_text(PAGE, FatDeleteDirent) +#pragma alloc_text(PAGE, FatGetDirentFromFcbOrDcb) +#pragma alloc_text(PAGE, FatInitializeDirectoryDirent) +#pragma alloc_text(PAGE, FatIsDirectoryEmpty) +#pragma alloc_text(PAGE, FatLfnDirentExists) +#pragma alloc_text(PAGE, FatLocateDirent) +#pragma alloc_text(PAGE, FatLocateSimpleOemDirent) +#pragma alloc_text(PAGE, FatLocateVolumeLabel) +#pragma alloc_text(PAGE, FatRescanDirectory) +#pragma alloc_text(PAGE, FatSetFileSizeInDirent) +#pragma alloc_text(PAGE, FatSetFileSizeInDirentNoRaise) +#pragma alloc_text(PAGE, FatTunnelFcbOrDcb) +#pragma alloc_text(PAGE, FatUpdateDirentFromFcb) + + +#endif + + +_Requires_lock_held_(_Global_critical_region_) +ULONG +FatCreateNewDirent ( + IN PIRP_CONTEXT IrpContext, + IN PDCB ParentDirectory, + IN ULONG DirentsNeeded, + IN BOOLEAN RescanDir + ) + +/*++ + +Routine Description: + + This routine allocates on the disk a new dirent inside of the + parent directory. If a new dirent cannot be allocated (i.e., + because the disk is full or the root directory is full) then + it raises the appropriate status. The dirent itself is + neither initialized nor pinned by this procedure. + +Arguments: + + ParentDirectory - Supplies the DCB for the directory in which + to create the new dirent + + DirentsNeeded - This is the number of continginous dirents required + +Return Value: + + ByteOffset - Returns the VBO within the Parent directory where + the dirent has been allocated + +--*/ + +{ + VBO UnusedVbo; + VBO DeletedHint; + ULONG ByteOffset; + + PBCB Bcb = NULL; + PDIRENT Dirent = NULL; + NTSTATUS Status = STATUS_SUCCESS; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatCreateNewDirent\n", 0); + + DebugTrace( 0, Dbg, " ParentDirectory = %p\n", ParentDirectory); + + // + // If UnusedDirentVbo is within our current file allocation then we + // don't have to search through the directory at all; we know just + // where to put it. + // + // If UnusedDirentVbo is beyond the current file allocation then + // there are no more unused dirents in the current allocation, though + // upon adding another cluster of allocation UnusedDirentVbo + // will point to an unused dirent. Haveing found no unused dirents + // we use the DeletedDirentHint to try and find a deleted dirent in + // the current allocation. In this also runs off the end of the file, + // we finally have to break down and allocate another sector. Note + // that simply writing beyond the current allocation will automatically + // do just this. + // + // We also must deal with the special case where UnusedDirentVbo and + // DeletedDirentHint have yet to be initialized. In this case we must + // first walk through the directory looking for the first deleted entry + // first unused dirent. After this point we continue as before. + // This initial state is denoted by the special value of 0xffffffff. + // + + UnusedVbo = ParentDirectory->Specific.Dcb.UnusedDirentVbo; + DeletedHint = ParentDirectory->Specific.Dcb.DeletedDirentHint; + + // + // Check for our first call to this routine with this Dcb. If so + // we have to correctly set the two hints in the Dcb. + // + + if (UnusedVbo == 0xffffffff || RescanDir) { + + FatRescanDirectory( IrpContext, ParentDirectory ); + + UnusedVbo = ParentDirectory->Specific.Dcb.UnusedDirentVbo; + DeletedHint = ParentDirectory->Specific.Dcb.DeletedDirentHint; + } + + // + // Now we know that UnusedDirentVbo and DeletedDirentHint are correctly + // set so we check if there is already an unused dirent in the the + // current allocation. This is the easy case. + // + + DebugTrace( 0, Dbg, " UnusedVbo = %08lx\n", UnusedVbo); + DebugTrace( 0, Dbg, " DeletedHint = %08lx\n", DeletedHint); + + if (!RescanDir && ( UnusedVbo + (DirentsNeeded * sizeof(DIRENT)) <= + ParentDirectory->Header.AllocationSize.LowPart )) { + + // + // Get this unused dirent for the caller. We have a + // sporting chance that we won't have to wait. + // + + DebugTrace( 0, Dbg, "There is a never used entry.\n", 0); + + ByteOffset = UnusedVbo; + + UnusedVbo += DirentsNeeded * sizeof(DIRENT); + + } else { + + // + // Life is tough. We have to march from the DeletedDirentHint + // looking for a deleted dirent. If we get to EOF without finding + // one, we will have to allocate a new cluster. + // + + ByteOffset = + RtlFindClearBits( &ParentDirectory->Specific.Dcb.FreeDirentBitmap, + DirentsNeeded, + DeletedHint / sizeof(DIRENT) ); + + // + // Do a quick check for a root directory allocation that failed + // simply because of fragmentation. Also, only attempt to defrag + // if the length is less that 0x40000. This is to avoid + // complications arising from crossing a MM view boundary (256kb). + // By default on DOS the root directory is only 0x2000 long. + // + // Don't try to defrag fat32 root dirs. + // + + if (!FatIsFat32(ParentDirectory->Vcb) && + (ByteOffset == -1) && + (NodeType(ParentDirectory) == FAT_NTC_ROOT_DCB) && + (ParentDirectory->Header.AllocationSize.LowPart <= 0x40000)) { + + ByteOffset = FatDefragDirectory( IrpContext, ParentDirectory, DirentsNeeded ); + } + + if (ByteOffset != -1) { + + // + // If we consuemed deleted dirents at Deleted Hint, update. + // We also may have consumed some un-used dirents as well, + // so be sure to check for that as well. + // + + ByteOffset *= sizeof(DIRENT); + + if (ByteOffset == DeletedHint) { + + DeletedHint += DirentsNeeded * sizeof(DIRENT); + } + + if (ByteOffset + DirentsNeeded * sizeof(DIRENT) > UnusedVbo) { + + UnusedVbo = ByteOffset + DirentsNeeded * sizeof(DIRENT); + } + + } else { + + // + // We are going to have to allocate another cluster. Do + // so, update both the UnusedVbo and the DeletedHint and bail. + // + + DebugTrace( 0, Dbg, "We have to allocate another cluster.\n", 0); + + // + // A reason why we might fail, unrelated to physical reasons, + // is that we constrain to 64k directory entries to match the + // restriction on Win95. There are fundamental reasons to do + // this since searching a FAT directory is a linear operation + // and to allow FAT32 to toss us over the cliff is not permissable. + // + + if (ParentDirectory->Header.AllocationSize.LowPart >= (64 * 1024 * sizeof(DIRENT)) || + + // + // Make sure we are not trying to expand the root directory on non + // FAT32. FAT16 and FAT12 have fixed size allocations. + // + + (!FatIsFat32(ParentDirectory->Vcb) && + NodeType(ParentDirectory) == FAT_NTC_ROOT_DCB)) { + + DebugTrace(0, Dbg, "Full root directory or too big on FAT32. Raise Status.\n", 0); + + FatRaiseStatus( IrpContext, STATUS_CANNOT_MAKE ); + } + + // + // Take the last dirent(s) in this cluster. We will allocate + // more clusters below. + // + + ByteOffset = UnusedVbo; + UnusedVbo += DirentsNeeded * sizeof(DIRENT); + + // + // Touch the directory file to cause space for the new dirents + // to be allocated. + // + + Bcb = NULL; + + try { + + PVOID Buffer; + + FatPrepareWriteDirectoryFile( IrpContext, + ParentDirectory, + UnusedVbo, + 1, + &Bcb, + &Buffer, + FALSE, + TRUE, + &Status ); + + } finally { + + FatUnpinBcb( IrpContext, Bcb ); + } + } + } + + // + // If we are only requesting a single dirent, and we did not get the + // first dirent in a directory, then check that the preceding dirent + // is not an orphaned LFN. If it is, then mark it deleted. Thus + // reducing the possibility of an accidental pairing. + // + // Only do this when we are in Chicago Mode. + // + + Bcb = NULL; + + if (FatData.ChicagoMode && + (DirentsNeeded == 1) && + (ByteOffset > (NodeType(ParentDirectory) == FAT_NTC_ROOT_DCB ? + 0 : 2 * sizeof(DIRENT)))) { + try { + + FatReadDirent( IrpContext, + ParentDirectory, + ByteOffset - sizeof(DIRENT), + &Bcb, + &Dirent, + &Status ); + + if ((Status != STATUS_SUCCESS) || + (Dirent->FileName[0] == FAT_DIRENT_NEVER_USED)) { + + FatPopUpFileCorrupt( IrpContext, ParentDirectory ); + + FatRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); + } + + if ((Dirent->Attributes == FAT_DIRENT_ATTR_LFN) && + (Dirent->FileName[0] != FAT_DIRENT_DELETED)) { + + // + // Pin it, mark it, and set it dirty. + // + + FatPinMappedData( IrpContext, + ParentDirectory, + ByteOffset - sizeof(DIRENT), + sizeof(DIRENT), + &Bcb ); + + Dirent->FileName[0] = FAT_DIRENT_DELETED; + + FatSetDirtyBcb( IrpContext, Bcb, ParentDirectory->Vcb, TRUE ); + + NT_ASSERT( RtlAreBitsSet( &ParentDirectory->Specific.Dcb.FreeDirentBitmap, + (ByteOffset - sizeof(DIRENT))/ sizeof(DIRENT), + DirentsNeeded ) ); + + RtlClearBits( &ParentDirectory->Specific.Dcb.FreeDirentBitmap, + (ByteOffset - sizeof(DIRENT))/ sizeof(DIRENT), + DirentsNeeded ); + + } + + } finally { + + FatUnpinBcb( IrpContext, Bcb ); + } + } + + // + // Assert that the dirents are in fact unused + // + + try { + + ULONG i; + + Bcb = NULL; + + for (i = 0; i < DirentsNeeded; i++) { + + FatReadDirent( IrpContext, + ParentDirectory, + ByteOffset + i*sizeof(DIRENT), + &Bcb, + &Dirent, + &Status ); + + if ((Status != STATUS_SUCCESS) || + ((Dirent->FileName[0] != FAT_DIRENT_NEVER_USED) && + (Dirent->FileName[0] != FAT_DIRENT_DELETED))) { + + FatPopUpFileCorrupt( IrpContext, ParentDirectory ); + FatRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); + } + } + + } finally { + + FatUnpinBcb( IrpContext, Bcb ); + } + + // + // Set the Bits in the bitmap and move the Unused Dirent Vbo. + // + + NT_ASSERT( RtlAreBitsClear( &ParentDirectory->Specific.Dcb.FreeDirentBitmap, + ByteOffset / sizeof(DIRENT), + DirentsNeeded ) ); + + RtlSetBits( &ParentDirectory->Specific.Dcb.FreeDirentBitmap, + ByteOffset / sizeof(DIRENT), + DirentsNeeded ); + + // + // Save the newly computed values in the Parent Directory Fcb + // + + ParentDirectory->Specific.Dcb.UnusedDirentVbo = UnusedVbo; + ParentDirectory->Specific.Dcb.DeletedDirentHint = DeletedHint; + + DebugTrace(-1, Dbg, "FatCreateNewDirent -> (VOID)\n", 0); + + return ByteOffset; +} + + + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatInitializeDirectoryDirent ( + IN PIRP_CONTEXT IrpContext, + IN PDCB Dcb, + IN PDIRENT ParentDirent + ) + +/*++ + +Routine Description: + + This routine converts a dirent into a directory on the disk. It does this + setting the directory flag in the dirent, and by allocating the necessary + space for the "." and ".." dirents and initializing them. + + If a new dirent cannot be allocated (i.e., because the disk is full) then + it raises the appropriate status. + +Arguments: + + Dcb - Supplies the Dcb denoting the file that is to be made into a + directory. This must be input a completely empty file with + an allocation size of zero. + + ParentDirent - Provides the parent Dirent for a time-stamp model. + +Return Value: + + None. + +--*/ + +{ + PBCB Bcb; + PVOID Buffer; + NTSTATUS DontCare = STATUS_SUCCESS; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatInitializeDirectoryDirent\n", 0); + + DebugTrace( 0, Dbg, " Dcb = %p\n", Dcb); + + // + // Assert that we are not attempting this on the root directory. + // + + NT_ASSERT( NodeType(Dcb) != FAT_NTC_ROOT_DCB ); + + // + // Assert that this is only attempted on newly created directories. + // + + NT_ASSERT( Dcb->Header.AllocationSize.LowPart == 0 ); + + // + // Prepare the directory file for writing. Note that we can use a single + // Bcb for these two entries because we know they are the first two in + // the directory, and thus together do not span a page boundry. Also + // note that we prepare write 2 entries: one for "." and one for "..". + // The end of directory marker is automatically set since the whole + // directory is initially zero (DIRENT_NEVER_USED). + // + + FatPrepareWriteDirectoryFile( IrpContext, + Dcb, + 0, + 2 * sizeof(DIRENT), + &Bcb, + &Buffer, + FALSE, + TRUE, + &DontCare ); + + NT_ASSERT( NT_SUCCESS( DontCare )); + + // + // Add the . and .. entries + // + + try { + + FatConstructDot( IrpContext, Dcb, ParentDirent, (PDIRENT)Buffer + 0); + + FatConstructDotDot( IrpContext, Dcb, ParentDirent, (PDIRENT)Buffer + 1); + + // + // Unpin the buffer and return to the caller. + // + + } finally { + + FatUnpinBcb( IrpContext, Bcb ); + } + + DebugTrace(-1, Dbg, "FatInitializeDirectoryDirent -> (VOID)\n", 0); + return; +} + + +VOID +FatTunnelFcbOrDcb ( + IN PFCB FcbOrDcb, + IN PCCB Ccb OPTIONAL + ) +/*++ + +Routine Description: + + This routine handles tunneling of an Fcb or Dcb associated with + an object whose name is disappearing from a directory. + +Arguments: + + FcbOrDcb - Supplies the Fcb/Dcb whose name will be going away + + Ccb - Supplies the Ccb for the Fcb (not reqired for a Dcb) so + that we know which name the Fcb was opened by + +Return Value: + + None. + +--*/ +{ + UNICODE_STRING ShortNameWithCase = {0}; + UNICODE_STRING DownCaseSeg; + WCHAR ShortNameBuffer[8+1+3]; + NTSTATUS Status; + USHORT i; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatTunnelFcbOrDcb\n", 0); + + if (NodeType(FcbOrDcb) == FAT_NTC_DCB) { + + // + // Directory deletion. Flush all entries from this directory in + // the cache for this volume + // + + FsRtlDeleteKeyFromTunnelCache( &FcbOrDcb->Vcb->Tunnel, + FatDirectoryKey(FcbOrDcb) ); + + } else { + + // + // Was a file, so throw it into the tunnel cache + // + + // + // Get the short name into UNICODE + // + + ShortNameWithCase.Length = 0; + ShortNameWithCase.MaximumLength = sizeof(ShortNameBuffer); + ShortNameWithCase.Buffer = ShortNameBuffer; + +#pragma prefast( suppress:28931, "needed for debug build" ) + Status = RtlOemStringToCountedUnicodeString( &ShortNameWithCase, + &FcbOrDcb->ShortName.Name.Oem, + FALSE); + + NT_ASSERT(ShortNameWithCase.Length != 0); + + NT_ASSERT(NT_SUCCESS(Status)); + + if (FlagOn(FcbOrDcb->FcbState, FCB_STATE_8_LOWER_CASE | FCB_STATE_3_LOWER_CASE)) { + + // + // Have to repair the case of the short name + // + + for (i = 0; i < (ShortNameWithCase.Length/sizeof(WCHAR)) && + ShortNameWithCase.Buffer[i] != L'.'; i++); + + // + // Now pointing at the '.', or otherwise the end of name component + // + + if (FlagOn(FcbOrDcb->FcbState, FCB_STATE_8_LOWER_CASE)) { + + DownCaseSeg.Buffer = ShortNameWithCase.Buffer; + DownCaseSeg.MaximumLength = DownCaseSeg.Length = i*sizeof(WCHAR); + + RtlDowncaseUnicodeString(&DownCaseSeg, &DownCaseSeg, FALSE); + } + + i++; + + // + // Now pointing at first wchar of the extension. + // + + if (FlagOn(FcbOrDcb->FcbState, FCB_STATE_3_LOWER_CASE)) { + + // + // It is not neccesarily the case that we can rely on the flag + // indicating that we really have an extension. + // + + if ((i*sizeof(WCHAR)) < ShortNameWithCase.Length) { + DownCaseSeg.Buffer = &ShortNameWithCase.Buffer[i]; + DownCaseSeg.MaximumLength = DownCaseSeg.Length = ShortNameWithCase.Length - i*sizeof(WCHAR); + + RtlDowncaseUnicodeString(&DownCaseSeg, &DownCaseSeg, FALSE); + } + } + } + + // + // ... and add it in + // + + FsRtlAddToTunnelCache( &FcbOrDcb->Vcb->Tunnel, + FatDirectoryKey(FcbOrDcb->ParentDcb), + &ShortNameWithCase, + &FcbOrDcb->ExactCaseLongName, + BooleanFlagOn(Ccb->Flags, CCB_FLAG_OPENED_BY_SHORTNAME), + sizeof(LARGE_INTEGER), + &FcbOrDcb->CreationTime ); + } + + DebugTrace(-1, Dbg, "FatTunnelFcbOrDcb -> (VOID)\n", 0); + + return; +} + + + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatDeleteDirent ( + IN PIRP_CONTEXT IrpContext, + IN PFCB FcbOrDcb, + IN PDELETE_CONTEXT DeleteContext OPTIONAL, + IN BOOLEAN DeleteEa + ) + +/*++ + +Routine Description: + + This routine Deletes on the disk the indicated dirent. It does + this by marking the dirent as deleted. + +Arguments: + + FcbOrDcb - Supplies the FCB/DCB for the file/directory being + deleted. For a file the file size and allocation must be zero. + (Zero allocation is implied by a zero cluster index). + For a directory the allocation must be zero. + + DeleteContext - This variable, if speicified, may be used to preserve + the file size and first cluster of file information in the dirent + fot the benefit of unerase utilities. + + DeleteEa - Tells us whether to delete the EA and whether to check + for no allocation/ Mainly TRUE. FALSE passed in from rename. + +Return Value: + + None. + +--*/ + +{ + PBCB Bcb = NULL; + PDIRENT Dirent = NULL; + NTSTATUS DontCare; + ULONG Offset; + ULONG DirentsToDelete; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatDeleteDirent\n", 0); + + DebugTrace( 0, Dbg, " FcbOrDcb = %p\n", FcbOrDcb); + + // + // We must be holding the vcb exclusive here to deal with the locate dirent + // cases where it cannot be holding the parent simply. This is actually + // a true statement from olden daze, lets just wire in our assertion. + // + // Among other reasons, it'd be unfortunate if this raced with the + // rename path. + // + + NT_ASSERT( ExIsResourceAcquiredExclusiveLite( &FcbOrDcb->Vcb->Resource )); + + // + // Assert that we are not attempting this on the root directory. + // + + NT_ASSERT( NodeType(FcbOrDcb) != FAT_NTC_ROOT_DCB ); + + // + // Make sure all requests have zero allocation/file size + // + + if (DeleteEa && + ((FcbOrDcb->Header.AllocationSize.LowPart != 0) || + ((NodeType(FcbOrDcb) == FAT_NTC_FCB) && + (FcbOrDcb->Header.FileSize.LowPart != 0)))) { + + DebugTrace( 0, Dbg, "Called with non zero allocation/file size.\n", 0); + +#pragma prefast( suppress:28159, "things are seriously wrong if we get here" ) + FatBugCheck( 0, 0, 0 ); + } + + // + // Now, mark the dirents deleted, unpin the Bcb, and return to the caller. + // Assert that there isn't any allocation associated with this dirent. + // + // Note that this loop will end with Dirent pointing to the short name. + // + + try { + + // + // We must acquire our parent exclusive to synchronize with enumerators + // who do not hold the vcb (ex: dirctrl). + // + // This relies on our bottom up lockorder. + // + + ExAcquireResourceExclusiveLite( FcbOrDcb->ParentDcb->Header.Resource, TRUE ); + + for ( Offset = FcbOrDcb->LfnOffsetWithinDirectory; + Offset <= FcbOrDcb->DirentOffsetWithinDirectory; + Offset += sizeof(DIRENT), Dirent += 1 ) { + + // + // If we stepped onto a new page, or this is the first iteration, + // unpin the old page, and pin the new one. + // + + if ((Offset == FcbOrDcb->LfnOffsetWithinDirectory) || + ((Offset & (PAGE_SIZE - 1)) == 0)) { + + FatUnpinBcb( IrpContext, Bcb ); + + FatPrepareWriteDirectoryFile( IrpContext, + FcbOrDcb->ParentDcb, + Offset, + sizeof(DIRENT), + &Bcb, + (PVOID *)&Dirent, + FALSE, + TRUE, + &DontCare ); + } + + NT_ASSERT( (Dirent->FirstClusterOfFile == 0) || !DeleteEa ); + Dirent->FileName[0] = FAT_DIRENT_DELETED; + } + + // + // Back Dirent off by one to point back to the short dirent. + // + + Dirent -= 1; + + // + // If there are extended attributes for this dirent, we will attempt + // to remove them. We ignore any errors in removing Eas. + // + + if (!FatIsFat32(FcbOrDcb->Vcb) && + DeleteEa && (Dirent->ExtendedAttributes != 0)) { + + try { + + FatDeleteEa( IrpContext, + FcbOrDcb->Vcb, + Dirent->ExtendedAttributes, + &FcbOrDcb->ShortName.Name.Oem ); + + } except(FatExceptionFilter( IrpContext, GetExceptionInformation() )) { + + // + // We catch all exceptions that Fat catches, but don't do + // anything with them. + // + } + } + + // + // Now clear the bits in the free dirent mask. + // + + DirentsToDelete = (FcbOrDcb->DirentOffsetWithinDirectory - + FcbOrDcb->LfnOffsetWithinDirectory) / sizeof(DIRENT) + 1; + + + NT_ASSERT( (FcbOrDcb->ParentDcb->Specific.Dcb.UnusedDirentVbo == 0xffffffff) || + RtlAreBitsSet( &FcbOrDcb->ParentDcb->Specific.Dcb.FreeDirentBitmap, + FcbOrDcb->LfnOffsetWithinDirectory / sizeof(DIRENT), + DirentsToDelete ) ); + + RtlClearBits( &FcbOrDcb->ParentDcb->Specific.Dcb.FreeDirentBitmap, + FcbOrDcb->LfnOffsetWithinDirectory / sizeof(DIRENT), + DirentsToDelete ); + + // + // Now, if the caller specified a DeleteContext, use it. + // + + if ( ARGUMENT_PRESENT( DeleteContext ) ) { + + Dirent->FileSize = DeleteContext->FileSize; + + + Dirent->FirstClusterOfFile = (USHORT)DeleteContext->FirstClusterOfFile; + } + + // + // If this newly deleted dirent is before the DeletedDirentHint, change + // the DeletedDirentHint to point here. + // + + if (FcbOrDcb->DirentOffsetWithinDirectory < + FcbOrDcb->ParentDcb->Specific.Dcb.DeletedDirentHint) { + + FcbOrDcb->ParentDcb->Specific.Dcb.DeletedDirentHint = + FcbOrDcb->LfnOffsetWithinDirectory; + } + + } finally { + + FatUnpinBcb( IrpContext, Bcb ); + + // + // Release our parent. + // + + ExReleaseResourceLite( FcbOrDcb->ParentDcb->Header.Resource ); + } + + DebugTrace(-1, Dbg, "FatDeleteDirent -> (VOID)\n", 0); + return; +} + +_Requires_lock_held_(_Global_critical_region_) +BOOLEAN +FatLfnDirentExists ( + IN PIRP_CONTEXT IrpContext, + IN PDCB Dcb, + IN PUNICODE_STRING Lfn, + IN PUNICODE_STRING LfnTmp + ) +/*++ + +Routine Description: + + This routine looks for a given Lfn in a directory + +Arguments: + + Dcb - The directory to search + + Lfn - The Lfn to look for + + Lfn - Temporary buffer to use to search for Lfn with (if < MAX_LFN then this + function may cause it to be allocated from pool if not large enough. + +Retrn Value: + + BOOLEAN TRUE if it exists, FALSE if not + +--*/ +{ + CCB Ccb; + PDIRENT Dirent; + PBCB DirentBcb = NULL; + VBO DirentByteOffset; + BOOLEAN Result = FALSE; + ULONG Flags = 0; + + PAGED_CODE(); + + // + // Pay performance penalty by forcing the compares to be case insensitive as + // opposed to grabbing more pool for a monocased copy of the Lfn. This is slight. + // + + Ccb.UnicodeQueryTemplate = *Lfn; + Ccb.ContainsWildCards = FALSE; + Ccb.Flags = CCB_FLAG_SKIP_SHORT_NAME_COMPARE | CCB_FLAG_QUERY_TEMPLATE_MIXED; + + try { + + FatLocateDirent( IrpContext, + Dcb, + &Ccb, + 0, + &Flags, + &Dirent, + &DirentBcb, + &DirentByteOffset, + NULL, + LfnTmp, + NULL ); + + } finally { + + if (DirentBcb) { + + Result = TRUE; + } + + FatUnpinBcb(IrpContext, DirentBcb); + } + + return Result; +} + + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatLocateDirent ( + IN PIRP_CONTEXT IrpContext, + IN PDCB ParentDirectory, + IN PCCB Ccb, + IN VBO OffsetToStartSearchFrom, + IN OUT PULONG Flags, + OUT PDIRENT *Dirent, + OUT PBCB *Bcb, + OUT PVBO ByteOffset, + OUT PBOOLEAN FileNameDos OPTIONAL, + IN OUT PUNICODE_STRING LongFileName OPTIONAL, + IN OUT PUNICODE_STRING OrigLongFileName OPTIONAL + ) + +/*++ + +Routine Description: + + This routine locates on the disk an undeleted dirent matching a given name. + +Arguments: + + ParentDirectory - Supplies the DCB for the directory to search + + Ccb - Contains a context control block with all matching information. + + OffsetToStartSearchFrom - Supplies the VBO within the parent directory + from which to start looking for another real dirent. + + Dirent - Receives a pointer to the located dirent if one was found + or NULL otherwise. + + Bcb - Receives the Bcb for the located dirent if one was found or + NULL otherwise. + + ByteOffset - Receives the VBO within the Parent directory for + the located dirent if one was found, or 0 otherwise. + + FileNameDos - Receives TRUE if the element of the dirent we hit on + was the short (non LFN) side + + LongFileName - If specified, this parameter returns the long file name + associated with the returned dirent. Note that it is the caller's + responsibility to provide the buffer (and set MaximumLength + accordingly) for this unicode string. The Length field is reset + to 0 by this routine on invocation. If the supplied buffer is not + large enough, a new one will be allocated from pool. + +Return Value: + + None. + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + + OEM_STRING Name; + UCHAR NameBuffer[12]; + + BOOLEAN UpcasedLfnValid = FALSE; + UNICODE_STRING UpcasedLfn = {0}; + WCHAR LocalLfnBuffer[32]; + + + BOOLEAN LfnInProgress = FALSE; + UCHAR LfnChecksum = 0; + ULONG LfnSize = 0; + ULONG LfnIndex = 0; + UCHAR Ordinal = 0; + VBO LfnByteOffset = 0; + + TimerStart(Dbg); + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatLocateDirent\n", 0); + + DebugTrace( 0, Dbg, " ParentDirectory = %p\n", ParentDirectory); + DebugTrace( 0, Dbg, " OffsetToStartSearchFrom = %08lx\n", OffsetToStartSearchFrom); + DebugTrace( 0, Dbg, " Dirent = %p\n", Dirent); + DebugTrace( 0, Dbg, " Bcb = %p\n", Bcb); + DebugTrace( 0, Dbg, " ByteOffset = %08lx\n", *ByteOffset); + + // + // We must have acquired the parent or the vcb to synchronize with deletion. This + // is important since we can't survive racing a thread marking a series of lfn + // dirents deleted - we'd get a bogus ordinal, and otherwise get really messed up. + // + // This routine cannot do the acquire since it would be out-of-order with respect + // to the Bcb resources on iterative calls. Our order has Bcbs as the inferior resource. + // + // Deletion always grabs the parent (safely - this used to not be possible until the + // multiple fcb lockorder was fixed to be bottom up!). Deletion always occurs with + // the vcb held exclusive as well, and this will cover the cases where we can't easily + // hold the parent here, see above. + // + + NT_ASSERT( ExIsResourceAcquiredSharedLite( ParentDirectory->Header.Resource ) || + ExIsResourceAcquiredExclusiveLite( ParentDirectory->Header.Resource ) || + ExIsResourceAcquiredSharedLite( &ParentDirectory->Vcb->Resource ) || + ExIsResourceAcquiredExclusiveLite( &ParentDirectory->Vcb->Resource )); + + // + // The algorithm here is pretty simple. We just walk through the + // parent directory until we: + // + // A) Find a matching entry. + // B) Can't Wait + // C) Hit the End of Directory + // D) Hit Eof + // + // In the first case we found it, in the latter three cases we did not. + // + + UNREFERENCED_PARAMETER( Flags ); // future use + + + Name.MaximumLength = 12; + Name.Buffer = (PCHAR)NameBuffer; + + UpcasedLfn.Length = 0; + UpcasedLfn.MaximumLength = sizeof( LocalLfnBuffer); + UpcasedLfn.Buffer = LocalLfnBuffer; + + + // + // If we were given a non-NULL Bcb, compute the new Dirent address + // from the prior one, or unpin the Bcb if the new Dirent is not pinned. + // + + if (*Bcb != NULL) { + + if ((OffsetToStartSearchFrom / PAGE_SIZE) == (*ByteOffset / PAGE_SIZE)) { + + *Dirent += (OffsetToStartSearchFrom - *ByteOffset) / sizeof(DIRENT); + + } else { + + FatUnpinBcb( IrpContext, *Bcb ); + } + } + + // + // Init the Lfn if we were given one. + // + + if (ARGUMENT_PRESENT(LongFileName)) { + + LongFileName->Length = 0; + } + + if (ARGUMENT_PRESENT(OrigLongFileName)) { + + OrigLongFileName->Length = 0; + } + + // + // Init the FileNameDos flag + // + + if (FileNameDos) { + + *FileNameDos = FALSE; + } + + // + // Round up OffsetToStartSearchFrom to the nearest Dirent, and store + // in ByteOffset. Note that this wipes out the prior value. + // + + *ByteOffset = (OffsetToStartSearchFrom + (sizeof(DIRENT) - 1)) + & ~(sizeof(DIRENT) - 1); + + try { + + while ( TRUE ) { + + BOOLEAN FoundValidLfn; + + UpcasedLfnValid = FALSE; + + // + // Try to read in the dirent + // + + FatReadDirent( IrpContext, + ParentDirectory, + *ByteOffset, + Bcb, + Dirent, + &Status ); + + // + // If End Directory dirent or EOF, set all out parameters to + // indicate entry not found and, like, bail. + // + // Note that the order of evaluation here is important since we + // cannot check the first character of the dirent until after we + // know we are not beyond EOF + // + + if ((Status == STATUS_END_OF_FILE) || + ((*Dirent)->FileName[0] == FAT_DIRENT_NEVER_USED)) { + + DebugTrace( 0, Dbg, "End of directory: entry not found.\n", 0); + + // + // If there is a Bcb, unpin it and set it to null + // + + FatUnpinBcb( IrpContext, *Bcb ); + + *Dirent = NULL; + *ByteOffset = 0; + break; + } + + // + // If the entry is marked deleted, skip. If there was an Lfn in + // progress we throw it out at this point. + // + + if ((*Dirent)->FileName[0] == FAT_DIRENT_DELETED) { + + LfnInProgress = FALSE; + goto GetNextDirent; + } + + // + // If we have wandered onto an LFN entry, try to interpret it. + // + + if (FatData.ChicagoMode && + ARGUMENT_PRESENT(LongFileName) && + ((*Dirent)->Attributes == FAT_DIRENT_ATTR_LFN)) { + + PLFN_DIRENT Lfn; + + Lfn = (PLFN_DIRENT)*Dirent; + + if (LfnInProgress) { + + // + // Check for a proper continuation of the Lfn in progress. + // + + if ((Lfn->Ordinal & FAT_LAST_LONG_ENTRY) || + (Lfn->Ordinal == 0) || + (Lfn->Ordinal != Ordinal - 1) || + (Lfn->Checksum != LfnChecksum) || + (Lfn->MustBeZero != 0)) { + + // + // The Lfn is not proper, stop constructing it. + // + + LfnInProgress = FALSE; + + } else { + + NT_ASSERT( ((LfnIndex % 13) == 0) && LfnIndex ); + + LfnIndex -= 13; + + RtlCopyMemory( &LongFileName->Buffer[LfnIndex+0], + &Lfn->Name1[0], + 5*sizeof(WCHAR) ); + + RtlCopyMemory( &LongFileName->Buffer[LfnIndex+5], + &Lfn->Name2[0], + 6 * sizeof(WCHAR) ); + + RtlCopyMemory( &LongFileName->Buffer[LfnIndex+11], + &Lfn->Name3[0], + 2 * sizeof(WCHAR) ); + + Ordinal = Lfn->Ordinal; + LfnByteOffset = *ByteOffset; + } + } + + // + // Now check (maybe again) if we should analyze this entry + // for a possible last entry. + // + + if ((!LfnInProgress) && + (Lfn->Ordinal & FAT_LAST_LONG_ENTRY) && + ((Lfn->Ordinal & ~FAT_LAST_LONG_ENTRY) <= MAX_LFN_DIRENTS) && + (Lfn->MustBeZero == 0)) { + + BOOLEAN CheckTail = FALSE; + + Ordinal = Lfn->Ordinal & ~FAT_LAST_LONG_ENTRY; + + // + // We're usually permissive (following the lead of Win9x) when we find + // malformation of the LFN dirent pile. I'm not sure this is a good idea, + // so I'm going to trigger corruption on this particularly ugly one. Perhaps + // we should come back and redo the original code here with this in mind in the + // future. + // + + if (Ordinal == 0) { + + // + // First LFN in the pile was zero marked as the last. This is never + // possible since oridinals are 1-based. + // + + FatPopUpFileCorrupt( IrpContext, ParentDirectory ); + FatRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); + } + + LfnIndex = (Ordinal - 1) * 13; + + FatEnsureStringBufferEnough( LongFileName, + (USHORT)((LfnIndex + 13) << 1)); + + RtlCopyMemory( &LongFileName->Buffer[LfnIndex+0], + &Lfn->Name1[0], + 5*sizeof(WCHAR)); + + RtlCopyMemory( &LongFileName->Buffer[LfnIndex+5], + &Lfn->Name2[0], + 6 * sizeof(WCHAR) ); + + RtlCopyMemory( &LongFileName->Buffer[LfnIndex+11], + &Lfn->Name3[0], + 2 * sizeof(WCHAR) ); + + // + // Now compute the Lfn size and make sure that the tail + // bytes are correct. + // + + while (LfnIndex != (ULONG)Ordinal * 13) { + + if (!CheckTail) { + + if (LongFileName->Buffer[LfnIndex] == 0x0000) { + + LfnSize = LfnIndex; + CheckTail = TRUE; + } + + } else { + + if (LongFileName->Buffer[LfnIndex] != 0xffff) { + + break; + } + } + + LfnIndex += 1; + } + + // + // If we exited this loop prematurely, the LFN is not valid. + // + + if (LfnIndex == (ULONG)Ordinal * 13) { + + // + // If we didn't find the NULL terminator, then the size + // is LfnIndex. + // + + if (!CheckTail) { + + LfnSize = LfnIndex; + } + + LfnIndex -= 13; + LfnInProgress = TRUE; + LfnChecksum = Lfn->Checksum; + LfnByteOffset = *ByteOffset; + } + } + + // + // Move on to the next dirent. + // + + goto GetNextDirent; + } + + // + // If this is the volume label, skip. Note that we never arrive here + // while building the LFN. If we did, we weren't asked to find LFNs + // and that is another good reason to skip this LFN fragment. + // + + if (FlagOn((*Dirent)->Attributes, FAT_DIRENT_ATTR_VOLUME_ID)) { + + // + // If we actually were asked to hand back volume labels, + // do it. + // + + if (FlagOn(Ccb->Flags, CCB_FLAG_MATCH_VOLUME_ID)) { + + break; + } + + goto GetNextDirent; + } + + // + // We may have just stepped off a valid Lfn run. Check to see if + // it is indeed valid for the following dirent. + // + + if (LfnInProgress && + (*ByteOffset == LfnByteOffset + sizeof(DIRENT)) && + (LfnIndex == 0) && + (FatComputeLfnChecksum(*Dirent) == LfnChecksum)) { + + NT_ASSERT( Ordinal == 1); + + FoundValidLfn = TRUE; + LongFileName->Length = (USHORT)(LfnSize * sizeof(WCHAR)); + + + if (ARGUMENT_PRESENT(OrigLongFileName)) { + *OrigLongFileName = *LongFileName; + } + + } else { + + FoundValidLfn = FALSE; + } + + + + // + // If we are supposed to match all entries, then match this entry. + // + + if (FlagOn(Ccb->Flags, CCB_FLAG_MATCH_ALL)) { + + break; + } + + // + // Check against the short name given if one was. + // + + if (!FlagOn( Ccb->Flags, CCB_FLAG_SKIP_SHORT_NAME_COMPARE )) { + + if (Ccb->ContainsWildCards) { + + // + // If we get one, note that all out parameters are already set. + // + + (VOID)Fat8dot3ToString( IrpContext, (*Dirent), FALSE, &Name ); + + // + // For fat we special case the ".." dirent because we want it to + // match ????????.??? and to do that we change ".." to "." before + // calling the Fsrtl routine. But only do this if the expression + // is greater than one character long. + // + + if ((Name.Length == 2) && + (Name.Buffer[0] == '.') && + (Name.Buffer[1] == '.') && + (Ccb->OemQueryTemplate.Wild.Length > 1)) { + + Name.Length = 1; + } + + if (FatIsNameInExpression( IrpContext, + Ccb->OemQueryTemplate.Wild, + Name)) { + + DebugTrace( 0, Dbg, "Entry found: Name = \"%Z\"\n", &Name); + DebugTrace( 0, Dbg, " VBO = %08lx\n", *ByteOffset); + + if (FileNameDos) { + + *FileNameDos = TRUE; + } + + SetFlag( Ccb->Flags, CCB_FLAG_OPENED_BY_SHORTNAME ); + + break; + } + + } else { + + // + // Do the quickest 8.3 equivalency check possible + // + + if (!FlagOn((*Dirent)->Attributes, FAT_DIRENT_ATTR_VOLUME_ID) && + (*(PULONG)&(Ccb->OemQueryTemplate.Constant[0]) == *(PULONG)&((*Dirent)->FileName[0])) && + (*(PULONG)&(Ccb->OemQueryTemplate.Constant[4]) == *(PULONG)&((*Dirent)->FileName[4])) && + (*(PUSHORT)&(Ccb->OemQueryTemplate.Constant[8]) == *(PUSHORT)&((*Dirent)->FileName[8])) && + (*(PUCHAR)&(Ccb->OemQueryTemplate.Constant[10]) == *(PUCHAR)&((*Dirent)->FileName[10]))) { + + DebugTrace( 0, Dbg, "Entry found.\n", 0); + + if (FileNameDos) { + + *FileNameDos = TRUE; + } + + SetFlag( Ccb->Flags, CCB_FLAG_OPENED_BY_SHORTNAME ); + + break; + } + } + } + + // + // No matches were found with the short name. If an LFN exists, + // use it for the search. + // + + if (FoundValidLfn) { + + + // + // First do a quick check here for different sized constant + // name and expression before upcasing. + // + + if (!Ccb->ContainsWildCards && + (Ccb->UnicodeQueryTemplate.Length != (USHORT)(LfnSize * sizeof(WCHAR)))) { + + // + // Move on to the next dirent. + // + + FoundValidLfn = FALSE; + LongFileName->Length = 0; + if (OrigLongFileName) { + OrigLongFileName->Length = 0; + } + + goto GetNextDirent; + } + + + + if (!UpcasedLfnValid) { + + // + // We need to upcase the name we found on disk. + // We need a buffer. Try to avoid doing an allocation. + // + + FatEnsureStringBufferEnough( &UpcasedLfn, + LongFileName->Length); + + Status = RtlUpcaseUnicodeString( &UpcasedLfn, + LongFileName, + FALSE ); + + if (!NT_SUCCESS(Status)) { + + FatNormalizeAndRaiseStatus( IrpContext, Status ); + } + + + UpcasedLfnValid = TRUE; + + } + + // + // Do the compare + // + + if (Ccb->ContainsWildCards) { + + if (FsRtlIsNameInExpression( &Ccb->UnicodeQueryTemplate, + &UpcasedLfn, + TRUE, + NULL )) { + + break; + } + + } else { + + if (FsRtlAreNamesEqual( &Ccb->UnicodeQueryTemplate, + &UpcasedLfn, + BooleanFlagOn( Ccb->Flags, CCB_FLAG_QUERY_TEMPLATE_MIXED ), + NULL )) { + + break; + } + + + } + + } + + // + // This long name was not a match. Zero out the Length field. + // + + if (FoundValidLfn) { + + FoundValidLfn = FALSE; + LongFileName->Length = 0; + + + if (OrigLongFileName) { + OrigLongFileName->Length = 0; + } + } + +GetNextDirent: + + // + // Move on to the next dirent. + // + + *ByteOffset += sizeof(DIRENT); + *Dirent += 1; + } + + } finally { + + FatFreeStringBuffer( &UpcasedLfn ); + + + } + + DebugTrace(-1, Dbg, "FatLocateDirent -> (VOID)\n", 0); + + TimerStop(Dbg,"FatLocateDirent"); + + return; +} + + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatLocateSimpleOemDirent ( + IN PIRP_CONTEXT IrpContext, + IN PDCB ParentDirectory, + IN POEM_STRING FileName, + OUT PDIRENT *Dirent, + OUT PBCB *Bcb, + OUT PVBO ByteOffset + ) + +/*++ + +Routine Description: + + This routine locates on the disk an undelted simple Oem dirent. By simple + I mean that FileName cannot contain any extended characters, and we do + not search LFNs or return them. + +Arguments: + + ParentDirectory - Supplies the DCB for the directory in which + to search + + FileName - Supplies the filename to search for. The name may contain + wild cards + + OffsetToStartSearchFrom - Supplies the VBO within the parent directory + from which to start looking for another real dirent. + + Dirent - Receives a pointer to the located dirent if one was found + or NULL otherwise. + + Bcb - Receives the Bcb for the located dirent if one was found or + NULL otherwise. + + ByteOffset - Receives the VBO within the Parent directory for + the located dirent if one was found, or 0 otherwise. + +Return Value: + + None. + +--*/ + +{ + CCB LocalCcb; + + PAGED_CODE(); + + // + // Note, this routine is called rarely, so performance is not critical. + // Just fill in a Ccb structure on my stack with the values that are + // required. + // + + FatStringTo8dot3( IrpContext, + *FileName, + &LocalCcb.OemQueryTemplate.Constant ); + LocalCcb.ContainsWildCards = FALSE; + LocalCcb.Flags = 0; + + FatLocateDirent( IrpContext, + ParentDirectory, + &LocalCcb, + 0, + NULL, + Dirent, + Bcb, + ByteOffset, + NULL, + NULL, + NULL ); + + return; +} + + + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatLocateVolumeLabel ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + OUT PDIRENT *Dirent, + OUT PBCB *Bcb, + OUT PVBO ByteOffset + ) + +/*++ + +Routine Description: + + This routine locates on the disk a dirent representing the volume + label. It does this by searching the root directory for a special + volume label dirent. + +Arguments: + + Vcb - Supplies the VCB for the volume to search + + Dirent - Receives a pointer to the located dirent if one was found + or NULL otherwise. + + Bcb - Receives the Bcb for the located dirent if one was found or + NULL otherwise. + + ByteOffset - Receives the VBO within the Parent directory for + the located dirent if one was found, or 0 otherwise. + +Return Value: + + None. + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatLocateVolumeLabel\n", 0); + + DebugTrace( 0, Dbg, " Vcb = %p\n", Vcb); + DebugTrace( 0, Dbg, " Dirent = %p\n", Dirent); + DebugTrace( 0, Dbg, " Bcb = %p\n", Bcb); + DebugTrace( 0, Dbg, " ByteOffset = %08lx\n", *ByteOffset); + + // + // The algorithm here is really simple. We just walk through the + // root directory until we: + // + // A) Find the non-deleted volume label + // B) Can't Wait + // C) Hit the End of Directory + // D) Hit Eof + // + // In the first case we found it, in the latter three cases we did not. + // + + *Bcb = NULL; + *ByteOffset = 0; + + while ( TRUE ) { + + // + // Try to read in the dirent + // + + FatReadDirent( IrpContext, + Vcb->RootDcb, + *ByteOffset, + Bcb, + Dirent, + &Status ); + + // + // If End Directory dirent or EOF, set all out parameters to + // indicate volume label not found and, like, bail. + // + // Note that the order of evaluation here is important since we cannot + // check the first character of the dirent until after we know we + // are not beyond EOF + // + + if ((Status == STATUS_END_OF_FILE) || + ((*Dirent)->FileName[0] == FAT_DIRENT_NEVER_USED)) { + + DebugTrace( 0, Dbg, "Volume label not found.\n", 0); + + // + // If there is a Bcb, unpin it and set it to null + // + + FatUnpinBcb( IrpContext, *Bcb ); + + *Dirent = NULL; + *ByteOffset = 0; + break; + } + + // + // If the entry is the non-deleted volume label break from the loop. + // + // Note that all out parameters are already correctly set. + // + + if ((((*Dirent)->Attributes & ~FAT_DIRENT_ATTR_ARCHIVE) == FAT_DIRENT_ATTR_VOLUME_ID) && + ((*Dirent)->FileName[0] != FAT_DIRENT_DELETED)) { + + DebugTrace( 0, Dbg, "Volume label found at VBO = %08lx\n", *ByteOffset); + + // + // We may set this dirty, so pin it. + // + + FatPinMappedData( IrpContext, + Vcb->RootDcb, + *ByteOffset, + sizeof(DIRENT), + Bcb ); + + break; + } + + // + // Move on to the next dirent. + // + + *ByteOffset += sizeof(DIRENT); + *Dirent += 1; + } + + + DebugTrace(-1, Dbg, "FatLocateVolumeLabel -> (VOID)\n", 0); + + return; +} + + + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatGetDirentFromFcbOrDcb ( + IN PIRP_CONTEXT IrpContext, + IN PFCB FcbOrDcb, + IN BOOLEAN ReturnOnFailure, + OUT PDIRENT *Dirent, + OUT PBCB *Bcb + ) + +/*++ + +Routine Description: + + This routine reads locates on the disk the dirent denoted by the + specified Fcb/Dcb. + +Arguments: + + FcbOrDcb - Supplies the FCB/DCB for the file/directory whose dirent + we are trying to read in. This must not be the root dcb. + + Dirent - Receives a pointer to the dirent + + Bcb - Receives the Bcb for the dirent + +Return Value: + + None. + +--*/ + +{ + NTSTATUS DontCare = STATUS_SUCCESS; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatGetDirentFromFcbOrDcb\n", 0); + + DebugTrace( 0, Dbg, " FcbOrDcb = %p\n", FcbOrDcb); + DebugTrace( 0, Dbg, " Dirent = %p\n", Dirent); + DebugTrace( 0, Dbg, " Bcb = %p\n", Bcb); + + // + // Assert that we are not attempting this on the root directory. + // + + NT_ASSERT( NodeType(FcbOrDcb) != FAT_NTC_ROOT_DCB ); + + // + // We know the offset of the dirent within the directory file, + // so we just read it (with pinning). + // + + FatReadDirectoryFile( IrpContext, + FcbOrDcb->ParentDcb, + FcbOrDcb->DirentOffsetWithinDirectory, + sizeof(DIRENT), + TRUE, + Bcb, + (PVOID *)Dirent, + &DontCare ); + + // + // Previous call can fail. We used to assert success, but we use this + // as part of volume verification (DetermineAndMarkFcbCondition) after + // media has been removed. Clearly the directory could shrink and we + // would try to read beyond filesize. + // + // The caller will note this via NULL pointers for Bcb/Buffer. Note that + // both asserts below are OK since this should never happen fixed media. + // + // This was a Prefix catch. + // + + NT_ASSERT( FlagOn( FcbOrDcb->Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA) || + NT_SUCCESS( DontCare )); + + // + // Note also that the only way this could fail is if the Fcb was being + // verified. This can't happen if the Fcb is in good condition. + // + // Also a Prefix catch. + // + + NT_ASSERT( NT_SUCCESS( DontCare ) || FcbOrDcb->FcbCondition == FcbNeedsToBeVerified ); + + // + // This should never happen except in very specific cases (during volume + // verify) but we'll handle and raise here to save all callers checking the + // pointers. + // + + if ((NULL == *Dirent) && !ReturnOnFailure) { + + NT_ASSERT( FALSE); + FatRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR); + } + + DebugTrace(-1, Dbg, "FatGetDirentFromFcbOrDcb -> (VOID)\n", 0); +} + + + +_Requires_lock_held_(_Global_critical_region_) +BOOLEAN +FatIsDirectoryEmpty ( + IN PIRP_CONTEXT IrpContext, + IN PDCB Dcb + ) + +/*++ + +Routine Description: + + This routine indicates to the caller if the specified directory + is empty. (i.e., it is not the root dcb and it only contains + the "." and ".." entries, or deleted files). + +Arguments: + + Dcb - Supplies the DCB for the directory being queried. + +Return Value: + + BOOLEAN - Returns TRUE if the directory is empty and + FALSE if the directory and is not empty. + +--*/ + +{ + PBCB Bcb; + ULONG ByteOffset; + PDIRENT Dirent = NULL; + + BOOLEAN IsDirectoryEmpty = FALSE; + + NTSTATUS Status = STATUS_SUCCESS; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatIsDirectoryEmpty\n", 0); + + DebugTrace( 0, Dbg, " Dcb = %p\n", Dcb); + DebugTrace( 0, Dbg, " IsDirectoryEmpty = %08lx\n", IsDirectoryEmpty); + + // + // Check to see if the first entry is an and of directory marker. + // For the root directory we check at Vbo = 0, for normal directories + // we check after the "." and ".." entries. + // + + ByteOffset = (NodeType(Dcb) == FAT_NTC_ROOT_DCB) ? 0 : 2*sizeof(DIRENT); + + // + // We just march through the directory looking for anything other + // than deleted files, LFNs, an EOF, or end of directory marker. + // + + Bcb = NULL; + + try { + + while ( TRUE ) { + + // + // Try to read in the dirent + // + + FatReadDirent( IrpContext, + Dcb, + ByteOffset, + &Bcb, + &Dirent, + &Status ); + + // + // If End Directory dirent or EOF, set IsDirectoryEmpty to TRUE and, + // like, bail. + // + // Note that the order of evaluation here is important since we cannot + // check the first character of the dirent until after we know we + // are not beyond EOF + // + + if ((Status == STATUS_END_OF_FILE) || + (Dirent->FileName[0] == FAT_DIRENT_NEVER_USED)) { + + DebugTrace( 0, Dbg, "Empty. Last exempt entry at VBO = %08lx\n", ByteOffset); + + IsDirectoryEmpty = TRUE; + break; + } + + // + // If this dirent is NOT deleted or an LFN set IsDirectoryEmpty to + // FALSE and, like, bail. + // + + if ((Dirent->FileName[0] != FAT_DIRENT_DELETED) && + (Dirent->Attributes != FAT_DIRENT_ATTR_LFN)) { + + + break; + + + } + + // + // Move on to the next dirent. + // + + ByteOffset += sizeof(DIRENT); + Dirent += 1; + } + + } finally { + + FatUnpinBcb( IrpContext, Bcb ); + } + + DebugTrace(-1, Dbg, "FatIsDirectoryEmpty -> %ld\n", IsDirectoryEmpty); + + return IsDirectoryEmpty; +} + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatDeleteFile ( + IN PIRP_CONTEXT IrpContext, + IN PDCB TargetDcb, + IN ULONG LfnOffset, + IN ULONG DirentOffset, + IN PDIRENT Dirent, + IN PUNICODE_STRING Lfn + ) +{ + PFCB Fcb; + PLIST_ENTRY Links; + + PAGED_CODE(); + + // + // We can do the replace by removing the other Fcb(s) from + // the prefix table. + // + + for (Links = TargetDcb->Specific.Dcb.ParentDcbQueue.Flink; + Links != &TargetDcb->Specific.Dcb.ParentDcbQueue; + Links = Links->Flink) { + + Fcb = CONTAINING_RECORD( Links, FCB, ParentDcbLinks ); + + if (FlagOn(Fcb->FcbState, FCB_STATE_NAMES_IN_SPLAY_TREE) && + (Fcb->DirentOffsetWithinDirectory == DirentOffset)) { + + NT_ASSERT( NodeType(Fcb) == FAT_NTC_FCB ); + NT_ASSERT( Fcb->LfnOffsetWithinDirectory == LfnOffset ); + + if ( Fcb->UncleanCount != 0 ) { + +#pragma prefast( suppress:28159, "things are seriously wrong if we get here" ) + FatBugCheck(0,0,0); + + } else { + + PERESOURCE Resource; + + // + // Make this fcb "appear" deleted, synchronizing with + // paging IO. + // + + FatRemoveNames( IrpContext, Fcb ); + + Resource = Fcb->Header.PagingIoResource; + + (VOID)ExAcquireResourceExclusiveLite( Resource, TRUE ); + + SetFlag(Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE); + + Fcb->ValidDataToDisk = 0; + Fcb->Header.FileSize.QuadPart = + Fcb->Header.ValidDataLength.QuadPart = 0; + + Fcb->FirstClusterOfFile = 0; + + ExReleaseResourceLite( Resource ); + } + } + } + + // + // The file is not currently opened so we can delete the file + // that is being overwritten. To do the operation we dummy + // up an fcb, truncate allocation, delete the fcb, and delete + // the dirent. + // + + Fcb = FatCreateFcb( IrpContext, + TargetDcb->Vcb, + TargetDcb, + LfnOffset, + DirentOffset, + Dirent, + Lfn, + FALSE, + FALSE ); + + Fcb->Header.FileSize.LowPart = 0; + + try { + + FatTruncateFileAllocation( IrpContext, Fcb, 0, TRUE ); + + FatDeleteDirent( IrpContext, Fcb, NULL, TRUE ); + + } finally { + + FatDeleteFcb( IrpContext, &Fcb ); + } +} + + + + +VOID +FatConstructDirent ( + IN PIRP_CONTEXT IrpContext, + IN OUT PDIRENT Dirent, + IN POEM_STRING FileName, + IN BOOLEAN ComponentReallyLowercase, + IN BOOLEAN ExtensionReallyLowercase, + IN PUNICODE_STRING Lfn OPTIONAL, + IN UCHAR Attributes, + IN BOOLEAN ZeroAndSetTimeFields, + IN PLARGE_INTEGER SetCreationTime OPTIONAL + ) + +/*++ + +Routine Description: + + This routine modifies the fields of a dirent. + +Arguments: + + Dirent - Supplies the dirent being modified. + + FileName - Supplies the name to store in the Dirent. This + name must not contain wildcards. + + ComponentReallyLowercase - This boolean indicates that the User Specified + compoent name was really all a-z and < 0x80 characters. We set the + magic bit in this case. + + ExtensionReallyLowercase - Same as above, but for the extension. + + Lfn - May supply a long file name. + + Attributes - Supplies the attributes to store in the dirent + + ZeroAndSetTimeFields - Tells whether or not to initially zero the dirent + and update the time fields. + + SetCreationTime - If specified, contains a timestamp to use as the creation + time of this dirent + +Return Value: + + None. + +--*/ + +{ + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatConstructDirent\n", 0); + + DebugTrace( 0, Dbg, " Dirent = %p\n", Dirent); + DebugTrace( 0, Dbg, " FileName = %Z\n", FileName); + DebugTrace( 0, Dbg, " Attributes = %08lx\n", Attributes); + + if (ZeroAndSetTimeFields) { + + RtlZeroMemory( Dirent, sizeof(DIRENT) ); + } + + // + // We just merrily go and fill up the dirent with the fields given. + // + + FatStringTo8dot3( IrpContext, *FileName, (PFAT8DOT3)&Dirent->FileName[0] ); + + if (ZeroAndSetTimeFields || SetCreationTime) { + + LARGE_INTEGER Time, SaveTime; + + KeQuerySystemTime( &Time ); + + if (FatData.ChicagoMode) { + + if (!SetCreationTime || !FatNtTimeToFatTime( IrpContext, + SetCreationTime, + FALSE, + &Dirent->CreationTime, + &Dirent->CreationMSec )) { + + // + // No tunneled time or the tunneled time was bogus. Since we aren't + // responsible for initializing the to-be-created Fcb with creation + // time, we can't do the usual thing and let NtTimeToFatTime perform + // rounding on the timestamp - this would mess up converting to the + // LastWriteTime below. + // + + SaveTime = Time; + + if (!FatNtTimeToFatTime( IrpContext, + &SaveTime, + FALSE, + &Dirent->CreationTime, + &Dirent->CreationMSec )) { + + // + // Failed again. Wow. + // + + RtlZeroMemory( &Dirent->CreationTime, sizeof(FAT_TIME_STAMP)); + Dirent->CreationMSec = 0; + } + } + } + + if (ZeroAndSetTimeFields) { + + // + // We only touch the other timestamps if we are initializing the dirent + // + + if (!FatNtTimeToFatTime( IrpContext, + &Time, + TRUE, + &Dirent->LastWriteTime, + NULL )) { + + DebugTrace( 0, Dbg, "Current time invalid.\n", 0); + + RtlZeroMemory( &Dirent->LastWriteTime, sizeof(FAT_TIME_STAMP) ); + } + + if (FatData.ChicagoMode) { + + Dirent->LastAccessDate = Dirent->LastWriteTime.Date; + } + } + } + + // + // Copy the attributes + // + + Dirent->Attributes = Attributes; + + // + // Set the magic bit here, to tell dirctrl.c that this name is really + // lowercase. + // + + Dirent->NtByte = 0; + + if (ComponentReallyLowercase) { + + SetFlag( Dirent->NtByte, FAT_DIRENT_NT_BYTE_8_LOWER_CASE ); + } + + if (ExtensionReallyLowercase) { + + SetFlag( Dirent->NtByte, FAT_DIRENT_NT_BYTE_3_LOWER_CASE ); + } + + // + // See if we have to create an Lfn entry + // + + if (ARGUMENT_PRESENT(Lfn)) { + + UCHAR DirentChecksum; + UCHAR DirentsInLfn; + UCHAR LfnOrdinal; + PWCHAR LfnBuffer; + PLFN_DIRENT LfnDirent; + + NT_ASSERT( FatData.ChicagoMode ); + + DirentChecksum = FatComputeLfnChecksum( Dirent ); + + LfnOrdinal = + DirentsInLfn = (UCHAR)FAT_LFN_DIRENTS_NEEDED(Lfn); + + LfnBuffer = &Lfn->Buffer[(DirentsInLfn - 1) * 13]; + + NT_ASSERT( DirentsInLfn <= MAX_LFN_DIRENTS ); + + for (LfnDirent = (PLFN_DIRENT)Dirent - DirentsInLfn; + LfnDirent < (PLFN_DIRENT)Dirent; + LfnDirent += 1, LfnOrdinal -= 1, LfnBuffer -= 13) { + + WCHAR FinalLfnBuffer[13]; + PWCHAR Buffer; + + // + // We need to special case the "final" dirent. + // + + if (LfnOrdinal == DirentsInLfn) { + + ULONG i; + ULONG RemainderChars; + + RemainderChars = (Lfn->Length / sizeof(WCHAR)) % 13; + + LfnDirent->Ordinal = LfnOrdinal | FAT_LAST_LONG_ENTRY; + + if (RemainderChars != 0) { + + RtlCopyMemory( FinalLfnBuffer, + LfnBuffer, + RemainderChars * sizeof(WCHAR) ); + + for (i = RemainderChars; i < 13; i++) { + + // + // Figure out which character to use. + // + + if (i == RemainderChars) { + + FinalLfnBuffer[i] = 0x0000; + + } else { + + FinalLfnBuffer[i] = 0xffff; + } + } + + Buffer = FinalLfnBuffer; + + } else { + + Buffer = LfnBuffer; + } + + } else { + + LfnDirent->Ordinal = LfnOrdinal; + + Buffer = LfnBuffer; + } + + // + // Now fill in the name. + // + + RtlCopyMemory( &LfnDirent->Name1[0], + &Buffer[0], + 5 * sizeof(WCHAR) ); + + RtlCopyMemory( &LfnDirent->Name2[0], + &Buffer[5], + 6 * sizeof(WCHAR) ); + + RtlCopyMemory( &LfnDirent->Name3[0], + &Buffer[11], + 2 * sizeof(WCHAR) ); + + // + // And the other fields + // + + LfnDirent->Attributes = FAT_DIRENT_ATTR_LFN; + + LfnDirent->Type = 0; + + LfnDirent->Checksum = DirentChecksum; + + LfnDirent->MustBeZero = 0; + } + } + + DebugTrace(-1, Dbg, "FatConstructDirent -> (VOID)\n", 0); + return; +} + + +VOID +FatConstructLabelDirent ( + IN PIRP_CONTEXT IrpContext, + IN OUT PDIRENT Dirent, + IN POEM_STRING Label + ) + +/*++ + +Routine Description: + + This routine modifies the fields of a dirent to be used for a label. + +Arguments: + + Dirent - Supplies the dirent being modified. + + Label - Supplies the name to store in the Dirent. This + name must not contain wildcards. + +Return Value: + + None. + +--*/ + +{ + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatConstructLabelDirent\n", 0); + + DebugTrace( 0, Dbg, " Dirent = %p\n", Dirent); + DebugTrace( 0, Dbg, " Label = %Z\n", Label); + + RtlZeroMemory( Dirent, sizeof(DIRENT) ); + + // + // We just merrily go and fill up the dirent with the fields given. + // + + RtlCopyMemory( Dirent->FileName, Label->Buffer, Label->Length ); + + // + // Pad the label with spaces, not nulls. + // + + RtlFillMemory( &Dirent->FileName[Label->Length], 11 - Label->Length, ' '); + + Dirent->LastWriteTime = FatGetCurrentFatTime( IrpContext ); + + Dirent->Attributes = FAT_DIRENT_ATTR_VOLUME_ID; + Dirent->ExtendedAttributes = 0; + Dirent->FileSize = 0; + + DebugTrace(-1, Dbg, "FatConstructLabelDirent -> (VOID)\n", 0); + return; +} + + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatSetFileSizeInDirent ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN PULONG AlternativeFileSize OPTIONAL + ) + +/*++ + +Routine Description: + + This routine saves the file size in an fcb into its dirent. + +Arguments: + + Fcb - Supplies the Fcb being referenced + + AlternativeFileSize - If non-null we use the ULONG it points to as + the new file size. Otherwise we use the one in the Fcb. + +Return Value: + + None. + +--*/ + +{ + PDIRENT Dirent; + PBCB DirentBcb; + + PAGED_CODE(); + + NT_ASSERT( Fcb->FcbCondition == FcbGood ); + + FatGetDirentFromFcbOrDcb( IrpContext, + Fcb, + FALSE, + &Dirent, + &DirentBcb ); + try { + + Dirent->FileSize = ARGUMENT_PRESENT( AlternativeFileSize ) ? + *AlternativeFileSize : Fcb->Header.FileSize.LowPart; + + + FatSetDirtyBcb( IrpContext, DirentBcb, Fcb->Vcb, TRUE ); + + } finally { + + FatUnpinBcb( IrpContext, DirentBcb ); + } +} + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatSetFileSizeInDirentNoRaise ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN PULONG AlternativeFileSize OPTIONAL + ) + +/*++ + +Routine Description: + + This routine saves the file size in an fcb into its dirent. + All exceptions thrown from FatSetFileSizeInDirent are + silently swallowed. + +Arguments: + + Fcb - Supplies the Fcb being referenced + + AlternativeFileSize - If non-null we use the ULONG it points to as + the new file size. Otherwise we use the one in the Fcb. + +Return Value: + + None. + +--*/ + +{ + try { + + FatSetFileSizeInDirent( IrpContext, Fcb, AlternativeFileSize ); + + } except(FatExceptionFilter( IrpContext, GetExceptionInformation() )) { + + NOTHING; + } +} + + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatUpdateDirentFromFcb ( + IN PIRP_CONTEXT IrpContext, + IN PFILE_OBJECT FileObject, + IN PFCB FcbOrDcb, + IN PCCB Ccb + ) + + +/*++ + +Routine Description: + + This routine modifies an objects directory entry based on the hints + that have been built up over previous operations on a handle. Notify + change filters are built and fired as a result of these updates. + +Arguments: + + FileObject - Fileobject representing the handle involved + + FcbOrDcb - File/Dir involved + + Ccb - User context involved + +Return Value: + + None. + +--*/ + +{ + BOOLEAN SetArchiveBit; + + BOOLEAN UpdateFileSize; + BOOLEAN UpdateLastWriteTime; + BOOLEAN UpdateLastAccessTime; + BOOLEAN UpdateDirent = FALSE; + + PDIRENT Dirent; + PBCB DirentBcb = NULL; + ULONG NotifyFilter = 0; + FAT_TIME_STAMP CurrentFatTime = {0}; + + LARGE_INTEGER CurrentTime; + LARGE_INTEGER CurrentDay = {0}; + LARGE_INTEGER LastAccessDay; + + PAGED_CODE(); + + // + // Nothing to do if the fcb is bad, volume is readonly or we got the + // root dir. + // + + if (FcbOrDcb->FcbCondition != FcbGood || + NodeType(FcbOrDcb) == FAT_NTC_ROOT_DCB || + FlagOn(FcbOrDcb->Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED)) { + + return; + } + + // + // Check if we should be changing the time or file size and set + // the archive bit on the file. + // + + KeQuerySystemTime( &CurrentTime ); + + // + // Note that we HAVE to use BooleanFlagOn() here because + // FO_FILE_SIZE_CHANGED > 0x80 (i.e., not in the first byte). + // + + SetArchiveBit = BooleanFlagOn(FileObject->Flags, FO_FILE_MODIFIED); + + UpdateLastWriteTime = FlagOn(FileObject->Flags, FO_FILE_MODIFIED) && + !FlagOn(Ccb->Flags, CCB_FLAG_USER_SET_LAST_WRITE); + + UpdateFileSize = NodeType(FcbOrDcb) == FAT_NTC_FCB && + BooleanFlagOn(FileObject->Flags, FO_FILE_SIZE_CHANGED); + + // + // Do one further check here of access time. Only update it if + // the current version is at least one day old. We know that + // the current FcbOrDcb->LastAccessTime corresponds to 12 midnight local + // time, so just see if the current time is on the same day. + // + + if (FatData.ChicagoMode && + (UpdateLastWriteTime || + FlagOn(FileObject->Flags, FO_FILE_FAST_IO_READ)) && + !FlagOn(Ccb->Flags, CCB_FLAG_USER_SET_LAST_ACCESS)) { + + ExSystemTimeToLocalTime( &FcbOrDcb->LastAccessTime, &LastAccessDay ); + ExSystemTimeToLocalTime( &CurrentTime, &CurrentDay ); + + LastAccessDay.QuadPart /= FatOneDay.QuadPart; + CurrentDay.QuadPart /= FatOneDay.QuadPart; + + if (LastAccessDay.LowPart != CurrentDay.LowPart) { + + UpdateLastAccessTime = TRUE; + + } else { + + UpdateLastAccessTime = FALSE; + } + + } else { + + UpdateLastAccessTime = FALSE; + } + + if (SetArchiveBit || + UpdateFileSize || + UpdateLastWriteTime || + UpdateLastAccessTime + ) { + + DebugTrace(0, Dbg, "Update Time and/or file size on File/Dir\n", 0); + + try { + + try { + +#if (NTDDI_VERSION >= NTDDI_WIN8) + // + // Break parent directory oplock. Directory oplock breaks are + // always advisory, so we will never block/get STATUS_PENDING here. + // + + if (FcbOrDcb->ParentDcb != NULL) { + + FsRtlCheckOplockEx( FatGetFcbOplock(FcbOrDcb->ParentDcb), + IrpContext->OriginatingIrp, + OPLOCK_FLAG_PARENT_OBJECT, + NULL, + NULL, + NULL ); + } +#endif + + // + // Get the dirent + // + + FatGetDirentFromFcbOrDcb( IrpContext, + FcbOrDcb, + FALSE, + &Dirent, + &DirentBcb ); + + if (UpdateLastWriteTime || UpdateLastAccessTime) { + + (VOID)FatNtTimeToFatTime( IrpContext, + &CurrentTime, + TRUE, + &CurrentFatTime, + NULL ); + } + + if (SetArchiveBit) { + + Dirent->Attributes |= FILE_ATTRIBUTE_ARCHIVE; + FcbOrDcb->DirentFatFlags |= FILE_ATTRIBUTE_ARCHIVE; + + NotifyFilter |= FILE_NOTIFY_CHANGE_ATTRIBUTES; + UpdateDirent = TRUE; + } + + if (UpdateLastWriteTime) { + + // + // Update its time of last write + // + + FcbOrDcb->LastWriteTime = CurrentTime; + Dirent->LastWriteTime = CurrentFatTime; + + // + // We call the notify package to report that the + // last modification time has changed. + // + + NotifyFilter |= FILE_NOTIFY_CHANGE_LAST_WRITE; + UpdateDirent = TRUE; + } + + if (UpdateLastAccessTime) { + + // + // Now we have to truncate the local time down + // to the current day, then convert back to UTC. + // + + FcbOrDcb->LastAccessTime.QuadPart = + CurrentDay.QuadPart * FatOneDay.QuadPart; + + ExLocalTimeToSystemTime( &FcbOrDcb->LastAccessTime, + &FcbOrDcb->LastAccessTime ); + + Dirent->LastAccessDate = CurrentFatTime.Date; + + // + // We call the notify package to report that the + // last access time has changed. + // + + NotifyFilter |= FILE_NOTIFY_CHANGE_LAST_ACCESS; + UpdateDirent = TRUE; + } + + if (UpdateFileSize) { + + // + // Perhaps we were called to make certain that the + // filesize on disc was updated - don't bother updating + // and firing the filter if nothing changed. + // + + NT_ASSERT( NodeType(FcbOrDcb) == FAT_NTC_FCB ); + + if (Dirent->FileSize != FcbOrDcb->Header.FileSize.LowPart) { + + // + // Update the dirent file size + // + + Dirent->FileSize = FcbOrDcb->Header.FileSize.LowPart; + + // + // We call the notify package to report that the + // size has changed. + // + + NotifyFilter |= FILE_NOTIFY_CHANGE_SIZE; + UpdateDirent = TRUE; + } + + + } + + + FatNotifyReportChange( IrpContext, + FcbOrDcb->Vcb, + FcbOrDcb, + NotifyFilter, + FILE_ACTION_MODIFIED ); + + if (UpdateDirent) { + + // + // If all we did was update last access time, + // don't mark the volume dirty. + // + + FatSetDirtyBcb( IrpContext, + DirentBcb, + NotifyFilter == FILE_NOTIFY_CHANGE_LAST_ACCESS ? + NULL : FcbOrDcb->Vcb, + TRUE ); + } + + } except( FsRtlIsNtstatusExpected(GetExceptionCode()) ? + EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) { + + FatResetExceptionState( IrpContext ); + } + + } finally { + + FatUnpinBcb( IrpContext, DirentBcb ); + } + } + +} + + +// +// Internal support routine +// + +UCHAR +FatComputeLfnChecksum ( + PDIRENT Dirent + ) + +/*++ + +Routine Description: + + This routine computes the Chicago long file name checksum. + +Arguments: + + Dirent - Specifies the dirent that we are to compute a checksum for. + +Return Value: + + The checksum. + +--*/ + +{ + ULONG i; + UCHAR Checksum; + + PAGED_CODE(); + + Checksum = Dirent->FileName[0]; + + for (i=1; i < 11; i++) { + + Checksum = ((Checksum & 1) ? 0x80 : 0) + + (Checksum >> 1) + + Dirent->FileName[i]; + } + + return Checksum; +} + + + +#if 0 // It turns out Win95 is still creating short names without a ~ + +// +// Internal support routine +// + +BOOLEAN +FatIsLfnPairValid ( + PWCHAR Lfn, + ULONG LfnSize, + PDIRENT Dirent + ) + +/*++ + +Routine Description: + + This routine does a few more checks to make sure that a LFN/short + name pairing is legitimate. Basically this is the test: + + Pairing is valid if: + + DIRENT has a ~ character || + (LFN is 8.3 compliant && + (LFN has extended character(s) ? TRUE : + LFN upcases to DIRENT)) + + When checking for the presence of a tilda character in the short + name, note that we purposely do a single byte search instead of + converting the name to UNICODE and looking there for the tilda. + This protects us from accidently missing the tilda if the + preceding byte is a lead byte in the current Oem code page, + but wasn't in the Oem code page that created the file. + + Also note that if the LFN is longer than 12 characters, then the + second clause of the OR must be false. + +Arguments: + + Lfn - Points to a buffer of UNICODE chars. + + LfnSize - This is the size of the LFN in characters. + + Dirent - Specifies the dirent we are to consider. + +Return Value: + + TRUE if the Lfn/DIRENT form a legitimate pair, FALSE otherwise. + +--*/ + +{ + ULONG i; + BOOLEAN ExtendedChars; + ULONG DirentBuffer[3]; + PUCHAR DirentName; + ULONG DirentIndex; + BOOLEAN DotEncountered; + + // + // First, look for a tilda + // + + for (i=0; i<11; i++) { + if (Dirent->FileName[i] == '~') { + return TRUE; + } + } + + // + // No tilda. If the LFN is longer than 12 characters, then it can + // neither upcase to the DIRENT nor be 8.3 complient. + // + + if (LfnSize > 12) { + return FALSE; + } + + // + // Now see if the name is 8.3, and build an upcased DIRENT as well. + // + + DirentBuffer[0] = 0x20202020; + DirentBuffer[1] = 0x20202020; + DirentBuffer[2] = 0x20202020; + + DirentName = (PUCHAR)DirentBuffer; + + ExtendedChars = FALSE; + DirentIndex = 0; + DotEncountered = FALSE; + + for (i=0; i < LfnSize; i++) { + + // + // Do dot transition work + // + + if (Lfn[i] == L'.') { + if (DotEncountered || + (i > 8) || + ((LfnSize - i) > 4) || + (i && Lfn[i-1] == L' ')) { + return FALSE; + } + DotEncountered = TRUE; + DirentIndex = 8; + continue; + } + + // + // The character must be legal in order to be 8.3 + // + + if ((Lfn[i] < 0x80) && + !FsRtlIsAnsiCharacterLegalFat((UCHAR)Lfn[i], FALSE)) { + return FALSE; + } + + // + // If the name contains no extended chars, continue building DIRENT + // + + if (!ExtendedChars) { + if (Lfn[i] > 0x7f) { + ExtendedChars = TRUE; + } else { + DirentName[DirentIndex++] = (UCHAR) ( + Lfn[i] < 'a' ? Lfn[i] : Lfn[i] <= 'z' ? Lfn[i] - ('a' - 'A') : Lfn[i]); + } + } + } + + // + // If the LFN ended in a space, or there was no dot and the name + // has more than 8 characters, then it is not 8.3 compliant. + // + + if ((Lfn[LfnSize - 1] == L' ') || + (!DotEncountered && (LfnSize > 8))) { + return FALSE; + } + + // + // OK, now if we got this far then the LFN is 8dot3. If there are + // no extended characters, then we can also check to make sure that + // the LFN is only a case varient of the DIRENT. + // + + if (!ExtendedChars && + !RtlEqualMemory(Dirent->FileName, DirentName, 11)) { + + return FALSE; + } + + // + // We have now verified this pairing the very best we can without + // knowledge of the code page that the file was created under. + // + + return TRUE; +} +#endif //0 + +// +// Internal support routine +// + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatRescanDirectory ( + PIRP_CONTEXT IrpContext, + PDCB Dcb + ) + +/*++ + +Routine Description: + + This routine rescans the given directory, finding the first unused + dirent, first deleted dirent, and setting the free dirent bitmap + appropriately. + +Arguments: + + Dcb - Supplies the directory to rescan. + +Return Value: + + None. + +--*/ + +{ + PBCB Bcb = NULL; + PDIRENT Dirent = NULL; + NTSTATUS Status = STATUS_SUCCESS; + + ULONG UnusedVbo; + ULONG DeletedHint; + ULONG DirentIndex; + ULONG DirentsThisRun; + ULONG StartIndexOfThisRun; + + enum RunType { + InitialRun, + FreeDirents, + AllocatedDirents, + } CurrentRun; + + PAGED_CODE(); + + DebugTrace( 0, Dbg, "We must scan the whole directory.\n", 0); + + UnusedVbo = 0; + DeletedHint = 0xffffffff; + + // + // To start with, we have to find out if the first dirent is free. + // + + CurrentRun = InitialRun; + DirentIndex = + StartIndexOfThisRun = 0; + + try { + + while ( TRUE ) { + + BOOLEAN DirentDeleted; + + // + // Read a dirent + // + + FatReadDirent( IrpContext, + Dcb, + UnusedVbo, + &Bcb, + &Dirent, + &Status ); + + // + // If EOF, or we found a NEVER_USED entry, we exit the loop + // + + if ( (Status == STATUS_END_OF_FILE ) || + (Dirent->FileName[0] == FAT_DIRENT_NEVER_USED)) { + + break; + } + + // + // If the dirent is DELETED, and it is the first one we found, set + // it in the deleted hint. + // + + if (Dirent->FileName[0] == FAT_DIRENT_DELETED) { + + DirentDeleted = TRUE; + + if (DeletedHint == 0xffffffff) { + + DeletedHint = UnusedVbo; + } + + } else { + + DirentDeleted = FALSE; + } + + // + // Check for the first time through the loop, and determine + // the current run type. + // + + if (CurrentRun == InitialRun) { + + CurrentRun = DirentDeleted ? + FreeDirents : AllocatedDirents; + + } else { + + // + // Are we switching from a free run to an allocated run? + // + + if ((CurrentRun == FreeDirents) && !DirentDeleted) { + + DirentsThisRun = DirentIndex - StartIndexOfThisRun; + + RtlClearBits( &Dcb->Specific.Dcb.FreeDirentBitmap, + StartIndexOfThisRun, + DirentsThisRun ); + + CurrentRun = AllocatedDirents; + StartIndexOfThisRun = DirentIndex; + } + + // + // Are we switching from an allocated run to a free run? + // + + if ((CurrentRun == AllocatedDirents) && DirentDeleted) { + + DirentsThisRun = DirentIndex - StartIndexOfThisRun; + + RtlSetBits( &Dcb->Specific.Dcb.FreeDirentBitmap, + StartIndexOfThisRun, + DirentsThisRun ); + + CurrentRun = FreeDirents; + StartIndexOfThisRun = DirentIndex; + } + } + + // + // Move on to the next dirent. + // + + UnusedVbo += sizeof(DIRENT); + Dirent += 1; + DirentIndex += 1; + } + + // + // Now we have to record the final run we encoutered + // + + DirentsThisRun = DirentIndex - StartIndexOfThisRun; + + if ((CurrentRun == FreeDirents) || (CurrentRun == InitialRun)) { + + RtlClearBits( &Dcb->Specific.Dcb.FreeDirentBitmap, + StartIndexOfThisRun, + DirentsThisRun ); + + } else { + + RtlSetBits( &Dcb->Specific.Dcb.FreeDirentBitmap, + StartIndexOfThisRun, + DirentsThisRun ); + } + + // + // Now if there we bailed prematurely out of the loop because + // we hit an unused entry, set all the rest as free. + // + + if (UnusedVbo < Dcb->Header.AllocationSize.LowPart) { + + StartIndexOfThisRun = UnusedVbo / sizeof(DIRENT); + + DirentsThisRun = (Dcb->Header.AllocationSize.LowPart - + UnusedVbo) / sizeof(DIRENT); + + RtlClearBits( &Dcb->Specific.Dcb.FreeDirentBitmap, + StartIndexOfThisRun, + DirentsThisRun); + } + + } finally { + + FatUnpinBcb( IrpContext, Bcb ); + } + + // + // If there weren't any DELETED entries, set the index to our current + // position. + // + + if (DeletedHint == 0xffffffff) { DeletedHint = UnusedVbo; } + + Dcb->Specific.Dcb.UnusedDirentVbo = UnusedVbo; + Dcb->Specific.Dcb.DeletedDirentHint = DeletedHint; + + return; +} + + +// +// Internal support routine +// + +_Requires_lock_held_(_Global_critical_region_) +ULONG +FatDefragDirectory ( + IN PIRP_CONTEXT IrpContext, + IN PDCB Dcb, + IN ULONG DirentsNeeded + ) + +/*++ + +Routine Description: + + This routine determines if the requested number of dirents can be found + in the directory, looking for deleted dirents and orphaned LFNs. If the + request can be satisifed, orphaned LFNs are marked as deleted, and deleted + dirents are all grouped together at the end of the directory. + + Note that this routine is currently used only on the root directory, but + it is completely general and could be used on any directory. + +Arguments: + + Dcb - Supplies the directory to defrag. + +Return Value: + + The Index of the first dirent available for use, or -1 if the + request cannot be satisfied. + +--*/ + +{ + ULONG SavedIrpContextFlag; + PLIST_ENTRY Links; + ULONG ReturnValue = 0; + PFCB Fcb; + + PBCB Bcb = NULL; + PDIRENT Dirent = NULL; + UNICODE_STRING Lfn = {0,0,NULL}; + + LARGE_MCB Mcb; + BOOLEAN McbInitialized = FALSE; + BOOLEAN InvalidateFcbs = FALSE; + + PUCHAR Directory = NULL; + PUCHAR UnusedDirents; + PUCHAR UnusedDirentBuffer = NULL; + PUCHAR UsedDirents; + PUCHAR UsedDirentBuffer = NULL; + + PBCB *Bcbs = NULL; + ULONG Page; + ULONG PagesPinned = 0; + + ULONG DcbSize; + ULONG TotalBytesAllocated = 0; + + PAGED_CODE(); + + // + // We assume we own the Vcb. + // + + NT_ASSERT( FatVcbAcquiredExclusive(IrpContext, Dcb->Vcb) ); + + // + // We will only attempt this on directories less than 0x40000 bytes + // long (by default on DOS the root directory is only 0x2000 long). + // This is to avoid a cache manager complication. + // + + DcbSize = Dcb->Header.AllocationSize.LowPart; + + if (DcbSize > 0x40000) { + + return (ULONG)-1; + } + + // + // Force wait to TRUE + // + + SavedIrpContextFlag = IrpContext->Flags; + + SetFlag( IrpContext->Flags, + IRP_CONTEXT_FLAG_WAIT | IRP_CONTEXT_FLAG_WRITE_THROUGH ); + + // + // Now acquire all open Fcbs in the Dcb exclusive. + // + + for (Links = Dcb->Specific.Dcb.ParentDcbQueue.Flink; + Links != &Dcb->Specific.Dcb.ParentDcbQueue; + Links = Links->Flink) { + + Fcb = CONTAINING_RECORD( Links, FCB, ParentDcbLinks ); + + (VOID)ExAcquireResourceExclusiveLite( Fcb->Header.Resource, TRUE ); + } + + try { + + CCB Ccb; + ULONG QueryOffset = 0; + ULONG FoundOffset = 0; + ULONGLONG BytesUsed = 0; + + NTSTATUS DontCare; + ULONG Run; + ULONG TotalRuns; + BOOLEAN Result; + PUCHAR Char; + + // + // We are going to build a new bitmap that will show all orphaned + // LFNs as well as deleted dirents as available. + // + // Initialize our local CCB that will match all files and even + // a label if it is here. + // + + RtlZeroMemory( &Ccb, sizeof(CCB) ); + Ccb.Flags = CCB_FLAG_MATCH_ALL | CCB_FLAG_MATCH_VOLUME_ID; + + // + // Init the Long File Name string. + // + + Lfn.MaximumLength = 260 * sizeof(WCHAR); + Lfn.Buffer = FsRtlAllocatePoolWithTag( PagedPool, + 260*sizeof(WCHAR), + TAG_FILENAME_BUFFER ); + + // + // Initalize the Mcb. We use this structure to keep track of runs + // of free and allocated dirents. Runs are identity allocations, and + // holes are free dirents. + // + + FsRtlInitializeLargeMcb( &Mcb, PagedPool ); + + McbInitialized = TRUE; + + do { + + FatLocateDirent( IrpContext, + Dcb, + &Ccb, + QueryOffset, + NULL, + &Dirent, + &Bcb, + (PVBO)&FoundOffset, + NULL, + &Lfn, + NULL ); + + if (Dirent != NULL) { + + ULONG LfnByteOffset; + + // + // Compute the LfnByteOffset. + // + + LfnByteOffset = FoundOffset - + FAT_LFN_DIRENTS_NEEDED(&Lfn) * sizeof(LFN_DIRENT); + + BytesUsed = FoundOffset - LfnByteOffset + sizeof(DIRENT); + + // + // Set a run to represent all the dirents used for this + // file in the Dcb dir. + // + +#pragma prefast( suppress:28931, "needed for debug build" ) + Result = FsRtlAddLargeMcbEntry( &Mcb, + LfnByteOffset, + LfnByteOffset, + BytesUsed ); + + NT_ASSERT( Result ); + + // + // Move on to the next dirent. + // + + TotalBytesAllocated += (ULONG) BytesUsed; + QueryOffset = FoundOffset + sizeof(DIRENT); + } + + } while ((Dirent != NULL) && (QueryOffset < DcbSize)); + + if (Bcb != NULL) { + + FatUnpinBcb( IrpContext, Bcb ); + } + + // + // If we need more dirents than are available, bail. + // + + if (DirentsNeeded > (DcbSize - TotalBytesAllocated)/sizeof(DIRENT)) { + + try_return(ReturnValue = (ULONG)-1); + } + + // + // Now we are going to copy all the used and un-used parts of the + // directory to separate pool. + // + // Allocate these buffers and pin the entire directory. + // + + UnusedDirents = + UnusedDirentBuffer = FsRtlAllocatePoolWithTag( PagedPool, + DcbSize - TotalBytesAllocated, + TAG_DIRENT ); + + UsedDirents = + UsedDirentBuffer = FsRtlAllocatePoolWithTag( PagedPool, + TotalBytesAllocated, + TAG_DIRENT ); + + PagesPinned = (DcbSize + (PAGE_SIZE - 1 )) / PAGE_SIZE; + + Bcbs = FsRtlAllocatePoolWithTag( PagedPool, + PagesPinned * sizeof(PBCB), + TAG_BCB ); + + RtlZeroMemory( Bcbs, PagesPinned * sizeof(PBCB) ); + + for (Page = 0; Page < PagesPinned; Page += 1) { + + ULONG PinSize; + + // + // Don't try to pin beyond the Dcb size. + // + + if ((Page + 1) * PAGE_SIZE > DcbSize) { + + PinSize = DcbSize - (Page * PAGE_SIZE); + + } else { + + PinSize = PAGE_SIZE; + } + + FatPrepareWriteDirectoryFile( IrpContext, + Dcb, + Page * PAGE_SIZE, + PinSize, + &Bcbs[Page], + &Dirent, + FALSE, + TRUE, + &DontCare ); + + if (Page == 0) { + Directory = (PUCHAR)Dirent; + } + } + + TotalRuns = FsRtlNumberOfRunsInLargeMcb( &Mcb ); + + for (Run = 0; Run < TotalRuns; Run++) { + + LBO Vbo; + LBO Lbo; + +#pragma prefast( suppress:28931, "needed for debug build" ) + Result = FsRtlGetNextLargeMcbEntry( &Mcb, + Run, + &Vbo, + &Lbo, + (PLONGLONG)&BytesUsed ); + + NT_ASSERT(Result); + + // + // Copy each run to their specific pool. + // + + if (Lbo != -1) { + + RtlCopyMemory( UsedDirents, + Directory + Vbo, + (ULONG) BytesUsed ); + + UsedDirents += BytesUsed; + + } else { + + RtlCopyMemory( UnusedDirents, + Directory + Vbo, + (ULONG) BytesUsed ); + + UnusedDirents += BytesUsed; + } + } + + // + // Marking all the un-used dirents as "deleted". This will reclaim + // storage used by orphaned LFNs. + // + + for (Char = UnusedDirentBuffer; Char < UnusedDirents; Char += sizeof(DIRENT)) { + + *Char = FAT_DIRENT_DELETED; + } + + // + // Now, for the permanent step. Copy the two pool buffer back to the + // real Dcb directory, and flush the Dcb directory + // + + NT_ASSERT( TotalBytesAllocated == (ULONG)(UsedDirents - UsedDirentBuffer) ); + + RtlCopyMemory( Directory, UsedDirentBuffer, TotalBytesAllocated ); + + RtlCopyMemory( Directory + TotalBytesAllocated, + UnusedDirentBuffer, + UnusedDirents - UnusedDirentBuffer ); + + // + // We need to unpin here so that the UnpinRepinned won't deadlock. + // + + if (Bcbs) { + for (Page = 0; Page < PagesPinned; Page += 1) { + FatUnpinBcb( IrpContext, Bcbs[Page] ); + } + ExFreePool(Bcbs); + Bcbs = NULL; + } + + // + // Now make the free dirent bitmap reflect the new state of the Dcb + // directory. + // + + RtlSetBits( &Dcb->Specific.Dcb.FreeDirentBitmap, + 0, + TotalBytesAllocated / sizeof(DIRENT) ); + + RtlClearBits( &Dcb->Specific.Dcb.FreeDirentBitmap, + TotalBytesAllocated / sizeof(DIRENT), + (DcbSize - TotalBytesAllocated) / sizeof(DIRENT) ); + + ReturnValue = TotalBytesAllocated / sizeof(DIRENT); + + // + // Flush the directory to disk. If we raise, we will need to invalidate + // all of the children. Sorry, guys, but I can't figure out where you are + // now - if this failed I probably can't read the media either. And we + // probably purged the cache to boot. + // + + try { + + FatUnpinRepinnedBcbs( IrpContext ); + + } except(FsRtlIsNtstatusExpected(GetExceptionCode()) ? + EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { + + InvalidateFcbs = TRUE; + } + + // + // OK, now nothing can go wrong. We have two more things to do. + // First, we have to fix up all the dirent offsets in any open Fcbs. + // If we cannot now find the Fcb, the file is marked invalid. Also, + // we skip deleted files. + // + + for (Links = Dcb->Specific.Dcb.ParentDcbQueue.Flink; + Links != &Dcb->Specific.Dcb.ParentDcbQueue; + Links = Links->Flink) { + + PBCB TmpBcb = NULL; + ULONG TmpOffset = 0; + PDIRENT TmpDirent = NULL; + ULONG PreviousLfnSpread; + + Fcb = CONTAINING_RECORD( Links, FCB, ParentDcbLinks ); + + if (IsFileDeleted( IrpContext, Fcb )) { + + continue; + } + + // + // If we aren't already giving up, safely try to pick up the dirent + // to update the Fcb. If this raises, we have to give up and blow + // evenyone else away too. + // + + if (!InvalidateFcbs) { + + try { + + FatLocateSimpleOemDirent( IrpContext, + Dcb, + &Fcb->ShortName.Name.Oem, + &TmpDirent, + &TmpBcb, + (PVBO)&TmpOffset ); + + } except(FsRtlIsNtstatusExpected(GetExceptionCode()) ? + EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { + + InvalidateFcbs = TRUE; + } + } + + if (TmpBcb == NULL || InvalidateFcbs) { + + FatUnpinBcb( IrpContext, TmpBcb ); + FatMarkFcbCondition( IrpContext, Fcb, FcbBad, TRUE ); + + } else { + + FatUnpinBcb( IrpContext, TmpBcb ); + + PreviousLfnSpread = Fcb->DirentOffsetWithinDirectory - + Fcb->LfnOffsetWithinDirectory; + + Fcb->DirentOffsetWithinDirectory = TmpOffset; + Fcb->LfnOffsetWithinDirectory = TmpOffset - PreviousLfnSpread; + } + } + + try_exit: NOTHING; + } finally { + + // + // Free all our resources and stuff. + // + + if (McbInitialized) { + FsRtlUninitializeLargeMcb( &Mcb ); + } + + if (Lfn.Buffer) { + ExFreePool( Lfn.Buffer ); + } + + if (UnusedDirentBuffer) { + ExFreePool( UnusedDirentBuffer ); + } + + if (UsedDirentBuffer) { + ExFreePool( UsedDirentBuffer ); + } + + if (Bcbs) { + for (Page = 0; Page < PagesPinned; Page += 1) { + FatUnpinBcb( IrpContext, Bcbs[Page] ); + } + ExFreePool(Bcbs); + } + + FatUnpinBcb( IrpContext, Bcb ); + + for (Links = Dcb->Specific.Dcb.ParentDcbQueue.Flink; + Links != &Dcb->Specific.Dcb.ParentDcbQueue; + Links = Links->Flink) { + + Fcb = CONTAINING_RECORD( Links, FCB, ParentDcbLinks ); + + ExReleaseResourceLite( Fcb->Header.Resource ); + } + + IrpContext->Flags = SavedIrpContextFlag; + } + + // + // Now return the offset of the first free dirent to the caller. + // + + return ReturnValue; +} + + + diff --git a/filesys/fastfat/dumpsup.c b/filesys/fastfat/dumpsup.c new file mode 100644 index 000000000..a25d22377 --- /dev/null +++ b/filesys/fastfat/dumpsup.c @@ -0,0 +1,381 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + DumpSup.c + +Abstract: + + This module implements a collection of data structure dump routines + for debugging the Fat file system + + +--*/ + +#include "FatProcs.h" + +#ifdef FASTFATDBG + +VOID FatDump(IN PVOID Ptr); + +VOID FatDumpDataHeader(); +VOID FatDumpVcb(IN PVCB Ptr); +VOID FatDumpFcb(IN PFCB Ptr); +VOID FatDumpCcb(IN PCCB Ptr); + +ULONG FatDumpCurrentColumn; + +#define DumpNewLine() { \ + DbgPrint("\n"); \ + FatDumpCurrentColumn = 1; \ +} + +#define DumpLabel(Label,Width) { \ + size_t i, LastPeriod=0; \ + CHAR _Str[20]; \ + for(i=0;i<2;i++) { _Str[i] = UCHAR_SP;} \ + for(i=0;i 80) {DumpNewLine();} \ + FatDumpCurrentColumn += 18 + 9 + 9; \ + DumpLabel(Field,18); \ + DbgPrint(":%p", Ptr->Field); \ + DbgPrint(" "); \ +} + +#define DumpListEntry(Links) { \ + if ((FatDumpCurrentColumn + 18 + 9 + 9) > 80) {DumpNewLine();} \ + FatDumpCurrentColumn += 18 + 9 + 9; \ + DumpLabel(Links,18); \ + DbgPrint(":%p", Ptr->Links.Flink); \ + DbgPrint(":%p", Ptr->Links.Blink); \ +} + +#define DumpName(Field,Width) { \ + ULONG i; \ + CHAR _String[256]; \ + if ((FatDumpCurrentColumn + 18 + Width) > 80) {DumpNewLine();} \ + FatDumpCurrentColumn += 18 + Width; \ + DumpLabel(Field,18); \ + for(i=0;iField[i];} \ + _String[Width] = '\0'; \ + DbgPrint("%s", _String); \ +} + +#define TestForNull(Name) { \ + if (Ptr == NULL) { \ + DbgPrint("%s - Cannot dump a NULL pointer\n", Name); \ + return; \ + } \ +} + + +VOID +FatDump ( + IN PVOID Ptr + ) + +/*++ + +Routine Description: + + This routine determines the type of internal record reference by ptr and + calls the appropriate dump routine. + +Arguments: + + Ptr - Supplies the pointer to the record to be dumped + +Return Value: + + None + +--*/ + +{ + TestForNull("FatDump"); + + switch (NodeType(Ptr)) { + + case FAT_NTC_DATA_HEADER: + + FatDumpDataHeader(); + break; + + case FAT_NTC_VCB: + + FatDumpVcb(Ptr); + break; + + case FAT_NTC_FCB: + case FAT_NTC_DCB: + case FAT_NTC_ROOT_DCB: + + FatDumpFcb(Ptr); + break; + + case FAT_NTC_CCB: + + FatDumpCcb(Ptr); + break; + + default : + + DbgPrint("FatDump - Unknown Node type code %p\n", *((PNODE_TYPE_CODE)(Ptr))); + break; + } + + return; +} + + +VOID +FatDumpDataHeader ( + ) + +/*++ + +Routine Description: + + Dump the top data structures and all Device structures + +Arguments: + + None + +Return Value: + + None + +--*/ + +{ + PFAT_DATA Ptr; + PLIST_ENTRY Links; + + Ptr = &FatData; + + TestForNull("FatDumpDataHeader"); + + DumpNewLine(); + DbgPrint("FatData@ %lx", (Ptr)); + DumpNewLine(); + + DumpField (NodeTypeCode); + DumpField (NodeByteSize); + DumpListEntry (VcbQueue); + DumpField (DriverObject); + DumpField (OurProcess); + DumpNewLine(); + + for (Links = Ptr->VcbQueue.Flink; + Links != &Ptr->VcbQueue; + Links = Links->Flink) { + + FatDumpVcb(CONTAINING_RECORD(Links, VCB, VcbLinks)); + } + + return; +} + + +VOID +FatDumpVcb ( + IN PVCB Ptr + ) + +/*++ + +Routine Description: + + Dump an Device structure, its Fcb queue amd direct access queue. + +Arguments: + + Ptr - Supplies the Device record to be dumped + +Return Value: + + None + +--*/ + +{ + TestForNull("FatDumpVcb"); + + DumpNewLine(); + DbgPrint("Vcb@ %lx", (Ptr)); + DumpNewLine(); + + DumpField (VolumeFileHeader.NodeTypeCode); + DumpField (VolumeFileHeader.NodeByteSize); + DumpListEntry (VcbLinks); + DumpField (TargetDeviceObject); + DumpField (Vpb); + DumpField (VcbState); + DumpField (VcbCondition); + DumpField (RootDcb); + DumpField (DirectAccessOpenCount); + DumpField (OpenFileCount); + DumpField (ReadOnlyCount); + DumpField (AllocationSupport); + DumpField (AllocationSupport.RootDirectoryLbo); + DumpField (AllocationSupport.RootDirectorySize); + DumpField (AllocationSupport.FileAreaLbo); + DumpField (AllocationSupport.NumberOfClusters); + DumpField (AllocationSupport.NumberOfFreeClusters); + DumpField (AllocationSupport.FatIndexBitSize); + DumpField (AllocationSupport.LogOfBytesPerSector); + DumpField (AllocationSupport.LogOfBytesPerCluster); + DumpField (DirtyFatMcb); + DumpField (FreeClusterBitMap); + DumpField (VirtualVolumeFile); + DumpField (SectionObjectPointers.DataSectionObject); + DumpField (SectionObjectPointers.SharedCacheMap); + DumpField (SectionObjectPointers.ImageSectionObject); + DumpField (ClusterHint); + DumpNewLine(); + + FatDumpFcb(Ptr->RootDcb); + + return; +} + + +VOID +FatDumpFcb ( + IN PFCB Ptr + ) + +/*++ + +Routine Description: + + Dump an Fcb structure, its various queues + +Arguments: + + Ptr - Supplies the Fcb record to be dumped + +Return Value: + + None + +--*/ + +{ + PLIST_ENTRY Links; + + TestForNull("FatDumpFcb"); + + DumpNewLine(); + if (NodeType(&Ptr->Header) == FAT_NTC_FCB) {DbgPrint("Fcb@ %lx", (Ptr));} + else if (NodeType(&Ptr->Header) == FAT_NTC_DCB) {DbgPrint("Dcb@ %lx", (Ptr));} + else if (NodeType(&Ptr->Header) == FAT_NTC_ROOT_DCB) {DbgPrint("RootDcb@ %lx", (Ptr));} + else {DbgPrint("NonFcb NodeType @ %lx", (Ptr));} + DumpNewLine(); + + DumpField (Header.NodeTypeCode); + DumpField (Header.NodeByteSize); + DumpListEntry (ParentDcbLinks); + DumpField (ParentDcb); + DumpField (Vcb); + DumpField (FcbState); + DumpField (FcbCondition); + DumpField (UncleanCount); + DumpField (OpenCount); + DumpField (DirentOffsetWithinDirectory); + DumpField (DirentFatFlags); + DumpField (FullFileName.Length); + DumpField (FullFileName.Buffer); + DumpName (FullFileName.Buffer, 32); + DumpField (ShortName.Name.Oem.Length); + DumpField (ShortName.Name.Oem.Buffer); + DumpField (NonPaged); + DumpField (Header.AllocationSize.LowPart); + DumpField (NonPaged->SectionObjectPointers.DataSectionObject); + DumpField (NonPaged->SectionObjectPointers.SharedCacheMap); + DumpField (NonPaged->SectionObjectPointers.ImageSectionObject); + + if ((Ptr->Header.NodeTypeCode == FAT_NTC_DCB) || + (Ptr->Header.NodeTypeCode == FAT_NTC_ROOT_DCB)) { + + DumpListEntry (Specific.Dcb.ParentDcbQueue); + DumpField (Specific.Dcb.DirectoryFileOpenCount); + DumpField (Specific.Dcb.DirectoryFile); + + } else if (Ptr->Header.NodeTypeCode == FAT_NTC_FCB) { + + DumpField (Header.FileSize.LowPart); + + } else { + + DumpNewLine(); + DbgPrint("Illegal Node type code"); + + } + DumpNewLine(); + + if ((Ptr->Header.NodeTypeCode == FAT_NTC_DCB) || + (Ptr->Header.NodeTypeCode == FAT_NTC_ROOT_DCB)) { + + for (Links = Ptr->Specific.Dcb.ParentDcbQueue.Flink; + Links != &Ptr->Specific.Dcb.ParentDcbQueue; + Links = Links->Flink) { + + FatDumpFcb(CONTAINING_RECORD(Links, FCB, ParentDcbLinks)); + } + } + + return; +} + + +VOID +FatDumpCcb ( + IN PCCB Ptr + ) + +/*++ + +Routine Description: + + Dump a Ccb structure + +Arguments: + + Ptr - Supplies the Ccb record to be dumped + +Return Value: + + None + +--*/ + +{ + TestForNull("FatDumpCcb"); + + DumpNewLine(); + DbgPrint("Ccb@ %lx", (Ptr)); + DumpNewLine(); + + DumpField (NodeTypeCode); + DumpField (NodeByteSize); + DumpField (UnicodeQueryTemplate.Length); + DumpName (UnicodeQueryTemplate.Buffer, 32); + DumpField (OffsetToStartSearchFrom); + DumpNewLine(); + + return; +} + +#endif // FASTFATDBG + diff --git a/filesys/fastfat/ea.c b/filesys/fastfat/ea.c new file mode 100644 index 000000000..93cd4ac76 --- /dev/null +++ b/filesys/fastfat/ea.c @@ -0,0 +1,2041 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + Ea.c + +Abstract: + + This module implements the EA routines for Fat called by + the dispatch driver. + + +--*/ + +#include "FatProcs.h" + +// +// The local debug trace level +// + +#define Dbg (DEBUG_TRACE_EA) + +// +// Local procedure prototypes +// + +IO_STATUS_BLOCK +FatQueryEaUserEaList ( + IN PIRP_CONTEXT IrpContext, + OUT PCCB Ccb, + IN PPACKED_EA FirstPackedEa, + IN ULONG PackedEasLength, + OUT PUCHAR UserBuffer, + IN ULONG UserBufferLength, + IN PUCHAR UserEaList, + IN ULONG UserEaListLength, + IN BOOLEAN ReturnSingleEntry + ); + +IO_STATUS_BLOCK +FatQueryEaIndexSpecified ( + IN PIRP_CONTEXT IrpContext, + OUT PCCB Ccb, + IN PPACKED_EA FirstPackedEa, + IN ULONG PackedEasLength, + OUT PUCHAR UserBuffer, + IN ULONG UserBufferLength, + IN ULONG UserEaIndex, + IN BOOLEAN ReturnSingleEntry + ); + +IO_STATUS_BLOCK +FatQueryEaSimpleScan ( + IN PIRP_CONTEXT IrpContext, + OUT PCCB Ccb, + IN PPACKED_EA FirstPackedEa, + IN ULONG PackedEasLength, + OUT PUCHAR UserBuffer, + IN ULONG UserBufferLength, + IN BOOLEAN ReturnSingleEntry, + ULONG StartOffset + ); + +BOOLEAN +FatIsDuplicateEaName ( + IN PIRP_CONTEXT IrpContext, + IN PFILE_GET_EA_INFORMATION GetEa, + IN PUCHAR UserBuffer + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, FatCommonQueryEa) +#pragma alloc_text(PAGE, FatCommonSetEa) +#pragma alloc_text(PAGE, FatFsdQueryEa) +#pragma alloc_text(PAGE, FatFsdSetEa) +#if 0 +#pragma alloc_text(PAGE, FatIsDuplicateEaName) +#pragma alloc_text(PAGE, FatQueryEaIndexSpecified) +#pragma alloc_text(PAGE, FatQueryEaSimpleScan) +#pragma alloc_text(PAGE, FatQueryEaUserEaList) +#endif +#endif + + +_Function_class_(IRP_MJ_QUERY_EA) +_Function_class_(DRIVER_DISPATCH) +NTSTATUS +FatFsdQueryEa ( + _In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + _Inout_ PIRP Irp + ) + +/*++ + +Routine Description: + + This routine implements the Fsd part of the NtQueryEa API + call. + +Arguments: + + VolumeDeviceObject - Supplies the volume device object where the file + being queried exists. + + Irp - Supplies the Irp being processed. + +Return Value: + + NTSTATUS - The FSD status for the Irp. + +--*/ + +{ + NTSTATUS Status; + PIRP_CONTEXT IrpContext = NULL; + + BOOLEAN TopLevel; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatFsdQueryEa\n", 0); + + // + // Call the common query routine, with blocking allowed if synchronous + // + + FsRtlEnterFileSystem(); + + TopLevel = FatIsIrpTopLevel( Irp ); + + try { + + IrpContext = FatCreateIrpContext( Irp, CanFsdWait( Irp ) ); + + Status = FatCommonQueryEa( IrpContext, Irp ); + + } except(FatExceptionFilter( IrpContext, GetExceptionInformation() )) { + + // + // We had some trouble trying to perform the requested + // operation, so we'll abort the I/O request with + // the error status that we get back from the + // execption code + // + + Status = FatProcessException( IrpContext, Irp, GetExceptionCode() ); + } + + if (TopLevel) { IoSetTopLevelIrp( NULL ); } + + FsRtlExitFileSystem(); + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "FatFsdQueryEa -> %08lx\n", Status); + + UNREFERENCED_PARAMETER( VolumeDeviceObject ); + + return Status; +} + + +_Function_class_(IRP_MJ_SET_EA) +_Function_class_(DRIVER_DISPATCH) +NTSTATUS +FatFsdSetEa ( + _In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + _Inout_ PIRP Irp + ) + +/*++ + +Routine Description: + + This routine implements the FSD part of the NtSetEa API + call. + +Arguments: + + VolumeDeviceObject - Supplies the volume device object where the file + being set exists. + + Irp - Supplies the Irp being processed. + +Return Value: + + NTSTATUS - The FSD status for the Irp. + +--*/ + +{ + NTSTATUS Status; + PIRP_CONTEXT IrpContext = NULL; + + BOOLEAN TopLevel; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatFsdSetEa\n", 0); + + // + // Call the common set routine, with blocking allowed if synchronous + // + + FsRtlEnterFileSystem(); + + TopLevel = FatIsIrpTopLevel( Irp ); + + try { + + IrpContext = FatCreateIrpContext( Irp, CanFsdWait( Irp ) ); + + Status = FatCommonSetEa( IrpContext, Irp ); + + } except(FatExceptionFilter( IrpContext, GetExceptionInformation() )) { + + // + // We had some trouble trying to perform the requested + // operation, so we'll abort the I/O request with + // the error status that we get back from the + // execption code + // + + Status = FatProcessException( IrpContext, Irp, GetExceptionCode() ); + } + + if (TopLevel) { IoSetTopLevelIrp( NULL ); } + + FsRtlExitFileSystem(); + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "FatFsdSetEa -> %08lx\n", Status); + + UNREFERENCED_PARAMETER( VolumeDeviceObject ); + + return Status; +} + + +NTSTATUS +FatCommonQueryEa ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the common routine for querying File ea called by both + the fsd and fsp threads. + +Arguments: + + Irp - Supplies the Irp being processed + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ +#if 0 + PIO_STACK_LOCATION IrpSp; + + NTSTATUS Status; + + PUCHAR Buffer; + ULONG UserBufferLength; + + PUCHAR UserEaList; + ULONG UserEaListLength; + ULONG UserEaIndex; + BOOLEAN RestartScan; + BOOLEAN ReturnSingleEntry; + BOOLEAN IndexSpecified; + + PVCB Vcb; + PCCB Ccb; + + PFCB Fcb; + PDIRENT Dirent; + PBCB Bcb; + + PDIRENT EaDirent; + PBCB EaBcb; + BOOLEAN LockedEaFcb; + + PEA_SET_HEADER EaSetHeader; + EA_RANGE EaSetRange; + + USHORT ExtendedAttributes; +#endif + + PAGED_CODE(); + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_DEVICE_REQUEST); + return STATUS_INVALID_DEVICE_REQUEST; + +#if 0 + // + // Get the current Irp stack location + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "FatCommonQueryEa...\n", 0); + DebugTrace( 0, Dbg, " Wait = %08lx\n", FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT)); + DebugTrace( 0, Dbg, " Irp = %p\n", Irp ); + DebugTrace( 0, Dbg, " ->SystemBuffer = %p\n", Irp->AssociatedIrp.SystemBuffer ); + DebugTrace( 0, Dbg, " ->Length = %08lx\n", IrpSp->Parameters.QueryEa.Length ); + DebugTrace( 0, Dbg, " ->EaList = %08lx\n", IrpSp->Parameters.QueryEa.EaList ); + DebugTrace( 0, Dbg, " ->EaListLength = %08lx\n", IrpSp->Parameters.QueryEa.EaListLength ); + DebugTrace( 0, Dbg, " ->EaIndex = %08lx\n", IrpSp->Parameters.QueryEa.EaIndex ); + DebugTrace( 0, Dbg, " ->RestartScan = %08lx\n", FlagOn(IrpSp->Flags, SL_RESTART_SCAN)); + DebugTrace( 0, Dbg, " ->ReturnSingleEntry = %08lx\n", FlagOn(IrpSp->Flags, SL_RETURN_SINGLE_ENTRY)); + DebugTrace( 0, Dbg, " ->IndexSpecified = %08lx\n", FlagOn(IrpSp->Flags, SL_INDEX_SPECIFIED)); + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = 0; + + // + // Check that the file object is associated with either a user file + // or directory open. We don't allow Ea operations on the root + // directory. + // + + { + TYPE_OF_OPEN OpenType; + + if (((OpenType = FatDecodeFileObject( IrpSp->FileObject, + &Vcb, + &Fcb, + &Ccb )) != UserFileOpen + && OpenType != UserDirectoryOpen) || + + (NodeType( Fcb )) == FAT_NTC_ROOT_DCB) { + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + + DebugTrace(-1, Dbg, + "FatCommonQueryEa -> %08lx\n", + STATUS_INVALID_PARAMETER); + + return STATUS_INVALID_PARAMETER; + } + } + + // + // Fat32 does not support ea's. + // + + if (FatIsFat32(Vcb)) { + + FatCompleteRequest( IrpContext, Irp, STATUS_EAS_NOT_SUPPORTED ); + DebugTrace(-1, Dbg, + "FatCommonQueryEa -> %08lx\n", + STATUS_EAS_NOT_SUPPORTED); + return STATUS_EAS_NOT_SUPPORTED; + } + + // + // Acquire shared access to the Fcb and enqueue the Irp if we didn't + // get access. + // + + if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT )) { + + DebugTrace(0, Dbg, "FatCommonQueryEa: Thread can't wait\n", 0); + + Status = FatFsdPostRequest( IrpContext, Irp ); + + DebugTrace(-1, Dbg, "FatCommonQueryEa -> %08lx\n", Status ); + + return Status; + } + + FatAcquireSharedFcb( IrpContext, Fcb ); + + // + // Reference our input parameters to make things easier + // + + UserBufferLength = IrpSp->Parameters.QueryEa.Length; + UserEaList = IrpSp->Parameters.QueryEa.EaList; + UserEaListLength = IrpSp->Parameters.QueryEa.EaListLength; + UserEaIndex = IrpSp->Parameters.QueryEa.EaIndex; + RestartScan = BooleanFlagOn(IrpSp->Flags, SL_RESTART_SCAN); + ReturnSingleEntry = BooleanFlagOn(IrpSp->Flags, SL_RETURN_SINGLE_ENTRY); + IndexSpecified = BooleanFlagOn(IrpSp->Flags, SL_INDEX_SPECIFIED); + + // + // Initialize our local values. + // + + LockedEaFcb = FALSE; + Bcb = NULL; + EaBcb = NULL; + + Status = STATUS_SUCCESS; + + RtlZeroMemory( &EaSetRange, sizeof( EA_RANGE )); + + try { + + PPACKED_EA FirstPackedEa; + ULONG PackedEasLength; + + Buffer = FatMapUserBuffer( IrpContext, Irp ); + + // + // We verify that the Fcb is still valid. + // + + FatVerifyFcb( IrpContext, Fcb ); + + // + // We need to get the dirent for the Fcb to recover the Ea handle. + // + + FatGetDirentFromFcbOrDcb( IrpContext, Fcb, &Dirent, &Bcb ); + + // + // Verify that the Ea file is in a consistant state. If the + // Ea modification count in the Fcb doesn't match that in + // the CCB, then the Ea file has been changed from under + // us. If we are not starting the search from the beginning + // of the Ea set, we return an error. + // + + if (UserEaList == NULL + && Ccb->OffsetOfNextEaToReturn != 0 + && !IndexSpecified + && !RestartScan + && Fcb->EaModificationCount != Ccb->EaModificationCount) { + + DebugTrace(0, Dbg, + "FatCommonQueryEa: Ea file in unknown state\n", 0); + + Status = STATUS_EA_CORRUPT_ERROR; + + try_return( Status ); + } + + // + // Show that the Ea's for this file are consistant for this + // file handle. + // + + Ccb->EaModificationCount = Fcb->EaModificationCount; + + // + // If the handle value is 0, then the file has no Eas. We dummy up + // an ea list to use below. + // + + ExtendedAttributes = Dirent->ExtendedAttributes; + + FatUnpinBcb( IrpContext, Bcb ); + + if (ExtendedAttributes == 0) { + + DebugTrace(0, Dbg, + "FatCommonQueryEa: Zero handle, no Ea's for this file\n", 0); + + FirstPackedEa = (PPACKED_EA) NULL; + + PackedEasLength = 0; + + } else { + + // + // We need to get the Ea file for this volume. If the + // operation doesn't complete due to blocking, then queue the + // Irp to the Fsp. + // + + FatGetEaFile( IrpContext, + Vcb, + &EaDirent, + &EaBcb, + FALSE, + FALSE ); + + LockedEaFcb = TRUE; + + // + // If the above operation completed and the Ea file did not exist, + // the disk has been corrupted. There is an existing Ea handle + // without any Ea data. + // + + if (Vcb->VirtualEaFile == NULL) { + + DebugTrace(0, Dbg, + "FatCommonQueryEa: No Ea file found when expected\n", 0); + + Status = STATUS_NO_EAS_ON_FILE; + + try_return( Status ); + } + + // + // We need to try to get the Ea set for the desired file. If + // blocking is necessary then we'll post the request to the Fsp. + // + + FatReadEaSet( IrpContext, + Vcb, + ExtendedAttributes, + &Fcb->ShortName.Name.Oem, + TRUE, + &EaSetRange ); + + EaSetHeader = (PEA_SET_HEADER) EaSetRange.Data; + + // + // Find the start and length of the Eas. + // + + FirstPackedEa = (PPACKED_EA) EaSetHeader->PackedEas; + + PackedEasLength = GetcbList( EaSetHeader ) - 4; + } + + // + // Protect our access to the user buffer since IO dosn't do this + // for us in this path unless we had specified that our driver + // requires buffering for these large requests. We don't, so ... + // + + try { + + // + // Let's clear the output buffer. + // + + RtlZeroMemory( Buffer, UserBufferLength ); + + // + // We now satisfy the user's request depending on whether he + // specified an Ea name list, an Ea index or restarting the + // search. + // + + // + // The user has supplied a list of Ea names. + // + + if (UserEaList != NULL) { + + Irp->IoStatus = FatQueryEaUserEaList( IrpContext, + Ccb, + FirstPackedEa, + PackedEasLength, + Buffer, + UserBufferLength, + UserEaList, + UserEaListLength, + ReturnSingleEntry ); + + // + // The user supplied an index into the Ea list. + // + + } else if (IndexSpecified) { + + Irp->IoStatus = FatQueryEaIndexSpecified( IrpContext, + Ccb, + FirstPackedEa, + PackedEasLength, + Buffer, + UserBufferLength, + UserEaIndex, + ReturnSingleEntry ); + + // + // Else perform a simple scan, taking into account the restart + // flag and the position of the next Ea stored in the Ccb. + // + + } else { + + Irp->IoStatus = FatQueryEaSimpleScan( IrpContext, + Ccb, + FirstPackedEa, + PackedEasLength, + Buffer, + UserBufferLength, + ReturnSingleEntry, + RestartScan + ? 0 + : Ccb->OffsetOfNextEaToReturn ); + } + + } except (!FsRtlIsNtstatusExpected(GetExceptionCode()) ? + EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { + + // + // We must have had a problem filling in the user's buffer, so fail. + // + + Irp->IoStatus.Status = GetExceptionCode(); + Irp->IoStatus.Information = 0; + } + + Status = Irp->IoStatus.Status; + + try_exit: NOTHING; + } finally { + + DebugUnwind( FatCommonQueryEa ); + + // + // Release the Fcb for the file object, and the Ea Fcb if + // successfully locked. + // + + FatReleaseFcb( IrpContext, Fcb ); + + if (LockedEaFcb) { + + FatReleaseFcb( IrpContext, Vcb->EaFcb ); + } + + // + // Unpin the dirents for the Fcb, EaFcb and EaSetFcb if necessary. + // + + FatUnpinBcb( IrpContext, Bcb ); + FatUnpinBcb( IrpContext, EaBcb ); + + FatUnpinEaRange( IrpContext, &EaSetRange ); + + if (!AbnormalTermination()) { + + FatCompleteRequest( IrpContext, Irp, Status ); + } + + DebugTrace(-1, Dbg, "FatCommonQueryEa -> %08lx\n", Status); + } + + return Status; +#endif +} + + +NTSTATUS +FatCommonSetEa ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine implements the common Set Ea File Api called by the + the Fsd and Fsp threads + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - The appropriate status for the Irp + +--*/ + +{ +#if 0 + PIO_STACK_LOCATION IrpSp; + + NTSTATUS Status; + + USHORT ExtendedAttributes; + + PUCHAR Buffer; + ULONG UserBufferLength; + + PVCB Vcb; + PCCB Ccb; + + PFCB Fcb; + PDIRENT Dirent; + PBCB Bcb = NULL; + + PDIRENT EaDirent = NULL; + PBCB EaBcb = NULL; + + PEA_SET_HEADER EaSetHeader = NULL; + + PEA_SET_HEADER PrevEaSetHeader; + PEA_SET_HEADER NewEaSetHeader; + EA_RANGE EaSetRange; + + BOOLEAN AcquiredVcb = FALSE; + BOOLEAN AcquiredFcb = FALSE; + BOOLEAN AcquiredParentDcb = FALSE; + BOOLEAN AcquiredRootDcb = FALSE; + BOOLEAN AcquiredEaFcb = FALSE; +#endif + + PAGED_CODE(); + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_DEVICE_REQUEST); + return STATUS_INVALID_DEVICE_REQUEST; + +#if 0 + + // + // The following booleans are used in the unwind process. + // + + // + // Get the current Irp stack location + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "FatCommonSetEa...\n", 0); + DebugTrace( 0, Dbg, " Wait = %08lx\n", FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT)); + DebugTrace( 0, Dbg, " Irp = %p\n", Irp ); + DebugTrace( 0, Dbg, " ->SystemBuffer = %p\n", Irp->AssociatedIrp.SystemBuffer ); + DebugTrace( 0, Dbg, " ->Length = %08lx\n", IrpSp->Parameters.SetEa.Length ); + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = 0; + + // + // Check that the file object is associated with either a user file + // or directory open. + // + + { + TYPE_OF_OPEN OpenType; + + if (((OpenType = FatDecodeFileObject( IrpSp->FileObject, + &Vcb, + &Fcb, + &Ccb )) != UserFileOpen + && OpenType != UserDirectoryOpen) || + + (NodeType( Fcb )) == FAT_NTC_ROOT_DCB) { + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + + DebugTrace(-1, Dbg, + "FatCommonSetEa -> %08lx\n", + STATUS_INVALID_PARAMETER); + + return STATUS_INVALID_PARAMETER; + } + } + + // + // Fat32 does not support ea's. + // + + if (FatIsFat32(Vcb)) { + + FatCompleteRequest( IrpContext, Irp, STATUS_EAS_NOT_SUPPORTED ); + DebugTrace(-1, Dbg, + "FatCommonSetEa -> %08lx\n", + STATUS_EAS_NOT_SUPPORTED); + return STATUS_EAS_NOT_SUPPORTED; + } + + // + // Reference our input parameters to make things easier + // + + UserBufferLength = IrpSp->Parameters.SetEa.Length; + + // + // Since we ask for no outside help (direct or buffered IO), it + // is our responsibility to insulate ourselves from the + // deviousness of the user above. Now, buffer and validate the + // contents. + // + + Buffer = FatBufferUserBuffer( IrpContext, Irp, UserBufferLength ); + + // + // Check the validity of the buffer with the new eas. We really + // need to do this always since we don't know, if it was already + // buffered, that we buffered and checked it or some overlying + // filter buffered without checking. + // + + Status = IoCheckEaBufferValidity( (PFILE_FULL_EA_INFORMATION) Buffer, + UserBufferLength, + (PULONG)&Irp->IoStatus.Information ); + + if (!NT_SUCCESS( Status )) { + + FatCompleteRequest( IrpContext, Irp, Status ); + DebugTrace(-1, Dbg, + "FatCommonSetEa -> %08lx\n", + Status); + return Status; + } + + // + // Acquire exclusive access to the Fcb. If this is a write-through operation + // we will need to pick up the other possible streams that can be modified in + // this operation so that the locking order is preserved - the root directory + // (dirent addition if EA database doesn't already exist) and the parent + // directory (addition of the EA handle to the object's dirent). + // + // We are primarily synchronizing with directory enumeration here. + // + // If we cannot wait need to send things off to the fsp. + // + + if (!FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT)) { + + DebugTrace(0, Dbg, "FatCommonSetEa: Set Ea must be waitable\n", 0); + + Status = FatFsdPostRequest( IrpContext, Irp ); + + DebugTrace(-1, Dbg, "FatCommonSetEa -> %08lx\n", Status ); + + return Status; + } + + // + // Set this handle as having modified the file + // + + IrpSp->FileObject->Flags |= FO_FILE_MODIFIED; + + RtlZeroMemory( &EaSetRange, sizeof( EA_RANGE )); + + try { + + ULONG PackedEasLength; + BOOLEAN PreviousEas; + ULONG AllocationLength; + ULONG BytesPerCluster; + USHORT EaHandle; + + PFILE_FULL_EA_INFORMATION FullEa; + + // + // Now go pick up everything + // + + FatAcquireSharedVcb( IrpContext, Fcb->Vcb ); + AcquiredVcb = TRUE; + FatAcquireExclusiveFcb( IrpContext, Fcb ); + AcquiredFcb = TRUE; + + if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WRITE_THROUGH)) { + + if (Fcb->ParentDcb) { + + FatAcquireExclusiveFcb( IrpContext, Fcb->ParentDcb ); + AcquiredParentDcb = TRUE; + } + + FatAcquireExclusiveFcb( IrpContext, Fcb->Vcb->RootDcb ); + AcquiredRootDcb = TRUE; + } + + // + // We verify that the Fcb is still valid. + // + + FatVerifyFcb( IrpContext, Fcb ); + + // + // We need to get the dirent for the Fcb to recover the Ea handle. + // + + FatGetDirentFromFcbOrDcb( IrpContext, Fcb, &Dirent, &Bcb ); + + DebugTrace(0, Dbg, "FatCommonSetEa: Dirent Address -> %p\n", + Dirent ); + DebugTrace(0, Dbg, "FatCommonSetEa: Dirent Bcb -> %p\n", + Bcb); + + // + // If the handle value is 0, then the file has no Eas. In that + // case we allocate memory to hold the Eas to be added. If there + // are existing Eas for the file, then we must read from the + // file and copy the Eas. + // + + ExtendedAttributes = Dirent->ExtendedAttributes; + + FatUnpinBcb( IrpContext, Bcb ); + + if (ExtendedAttributes == 0) { + + PreviousEas = FALSE; + + DebugTrace(0, Dbg, + "FatCommonSetEa: File has no current Eas\n", 0 ); + + } else { + + PreviousEas = TRUE; + + DebugTrace(0, Dbg, "FatCommonSetEa: File has previous Eas\n", 0 ); + + FatGetEaFile( IrpContext, + Vcb, + &EaDirent, + &EaBcb, + FALSE, + TRUE ); + + AcquiredEaFcb = TRUE; + + // + // If we didn't get the file then there is an error on + // the disk. + // + + if (Vcb->VirtualEaFile == NULL) { + + Status = STATUS_NO_EAS_ON_FILE; + try_return( Status ); + } + } + + DebugTrace(0, Dbg, "FatCommonSetEa: EaBcb -> %p\n", EaBcb); + + DebugTrace(0, Dbg, "FatCommonSetEa: EaDirent -> %p\n", EaDirent); + + // + // If the file has existing ea's, we need to read them to + // determine the size of the buffer allocation. + // + + if (PreviousEas) { + + // + // We need to try to get the Ea set for the desired file. + // + + FatReadEaSet( IrpContext, + Vcb, + ExtendedAttributes, + &Fcb->ShortName.Name.Oem, + TRUE, + &EaSetRange ); + + PrevEaSetHeader = (PEA_SET_HEADER) EaSetRange.Data; + + // + // We now must allocate pool memory for our copy of the + // EaSetHeader and then copy the Ea data into it. At that + // time we can unpin the EaSet. + // + + PackedEasLength = GetcbList( PrevEaSetHeader ) - 4; + + // + // Else we will create a dummy EaSetHeader. + // + + } else { + + PackedEasLength = 0; + } + + BytesPerCluster = 1 << Vcb->AllocationSupport.LogOfBytesPerCluster; + + AllocationLength = (PackedEasLength + + SIZE_OF_EA_SET_HEADER + + BytesPerCluster - 1) + & ~(BytesPerCluster - 1); + + EaSetHeader = FsRtlAllocatePoolWithTag( PagedPool, + AllocationLength, + TAG_EA_SET_HEADER ); + + // + // Copy the existing Eas over to pool memory. + // + + if (PreviousEas) { + + RtlCopyMemory( EaSetHeader, PrevEaSetHeader, AllocationLength ); + + FatUnpinEaRange( IrpContext, &EaSetRange ); + + } else { + + RtlZeroMemory( EaSetHeader, AllocationLength ); + + RtlCopyMemory( EaSetHeader->OwnerFileName, + Fcb->ShortName.Name.Oem.Buffer, + Fcb->ShortName.Name.Oem.Length ); + } + + + AllocationLength -= SIZE_OF_EA_SET_HEADER; + + DebugTrace(0, Dbg, "FatCommonSetEa: Initial Ea set -> %p\n", + EaSetHeader); + + // + // At this point we have either read in the current eas for the file + // or we have initialized a new empty buffer for the eas. Now for + // each full ea in the input user buffer we do the specified operation + // on the ea + // + + for (FullEa = (PFILE_FULL_EA_INFORMATION) Buffer; + FullEa < (PFILE_FULL_EA_INFORMATION) &Buffer[UserBufferLength]; + FullEa = (PFILE_FULL_EA_INFORMATION) (FullEa->NextEntryOffset == 0 ? + &Buffer[UserBufferLength] : + (PUCHAR) FullEa + FullEa->NextEntryOffset)) { + + OEM_STRING EaName; + ULONG Offset; + + EaName.MaximumLength = EaName.Length = FullEa->EaNameLength; + EaName.Buffer = &FullEa->EaName[0]; + + DebugTrace(0, Dbg, "FatCommonSetEa: Next Ea name -> %Z\n", + &EaName); + + // + // Make sure the ea name is valid + // + + if (!FatIsEaNameValid( IrpContext,EaName )) { + + Irp->IoStatus.Information = (PUCHAR)FullEa - Buffer; + Status = STATUS_INVALID_EA_NAME; + try_return( Status ); + } + + // + // Check that no invalid ea flags are set. + // + + // + // TEMPCODE We are returning STATUS_INVALID_EA_NAME + // until a more appropriate error code exists. + // + + if (FullEa->Flags != 0 + && FullEa->Flags != FILE_NEED_EA) { + + Irp->IoStatus.Information = (PUCHAR)FullEa - (PUCHAR)Buffer; + try_return( Status = STATUS_INVALID_EA_NAME ); + } + + // + // See if we can locate the ea name in the ea set + // + + if (FatLocateEaByName( IrpContext, + (PPACKED_EA) EaSetHeader->PackedEas, + PackedEasLength, + &EaName, + &Offset )) { + + DebugTrace(0, Dbg, "FatCommonSetEa: Found Ea name\n", 0); + + // + // We found the ea name so now delete the current entry, + // and if the new ea value length is not zero then we + // replace if with the new ea + // + + FatDeletePackedEa( IrpContext, + EaSetHeader, + &PackedEasLength, + Offset ); + } + + if (FullEa->EaValueLength != 0) { + + FatAppendPackedEa( IrpContext, + &EaSetHeader, + &PackedEasLength, + &AllocationLength, + FullEa, + BytesPerCluster ); + } + } + + // + // If there are any ea's not removed, we + // call 'AddEaSet' to insert them into the Fat chain. + // + + if (PackedEasLength != 0) { + + LARGE_INTEGER EaOffset; + + EaOffset.HighPart = 0; + + // + // If the packed eas length (plus 4 bytes) is greater + // than the maximum allowed ea size, we return an error. + // + + if (PackedEasLength + 4 > MAXIMUM_EA_SIZE) { + + DebugTrace( 0, Dbg, "Ea length is greater than maximum\n", 0 ); + + try_return( Status = STATUS_EA_TOO_LARGE ); + } + + // + // We need to now read the ea file if we haven't already. + // + + if (EaDirent == NULL) { + + FatGetEaFile( IrpContext, + Vcb, + &EaDirent, + &EaBcb, + TRUE, + TRUE ); + + AcquiredEaFcb = TRUE; + } + + FatGetDirentFromFcbOrDcb( IrpContext, Fcb, &Dirent, &Bcb ); + + RtlZeroMemory( &EaSetRange, sizeof( EA_RANGE )); + + FatAddEaSet( IrpContext, + Vcb, + PackedEasLength + SIZE_OF_EA_SET_HEADER, + EaBcb, + EaDirent, + &EaHandle, + &EaSetRange ); + + NewEaSetHeader = (PEA_SET_HEADER) EaSetRange.Data; + + DebugTrace(0, Dbg, "FatCommonSetEa: Adding an ea set\n", 0); + + // + // Store the length of the new Ea's into the EaSetHeader. + // This is the PackedEasLength + 4. + // + + PackedEasLength += 4; + + CopyU4char( EaSetHeader->cbList, &PackedEasLength ); + + // + // Copy all but the first four bytes of EaSetHeader into + // NewEaSetHeader. The signature and index fields have + // already been filled in. + // + + RtlCopyMemory( &NewEaSetHeader->NeedEaCount, + &EaSetHeader->NeedEaCount, + PackedEasLength + SIZE_OF_EA_SET_HEADER - 8 ); + + FatMarkEaRangeDirty( IrpContext, Vcb->VirtualEaFile, &EaSetRange ); + FatUnpinEaRange( IrpContext, &EaSetRange ); + + CcFlushCache( Vcb->VirtualEaFile->SectionObjectPointer, NULL, 0, NULL ); + + } else { + + FatGetDirentFromFcbOrDcb( IrpContext, Fcb, &Dirent, &Bcb ); + + EaHandle = 0; + } + + // + // Now we do a wholesale replacement of the ea for the file + // + + if (PreviousEas) { + + FatDeleteEaSet( IrpContext, + Vcb, + EaBcb, + EaDirent, + ExtendedAttributes, + &Fcb->ShortName.Name.Oem ); + + CcFlushCache( Vcb->VirtualEaFile->SectionObjectPointer, NULL, 0, NULL ); + } + + if (PackedEasLength != 0 ) { + + Fcb->EaModificationCount++; + } + + // + // Mark the dirent with the new ea's + // + + Dirent->ExtendedAttributes = EaHandle; + + FatSetDirtyBcb( IrpContext, Bcb, Vcb, TRUE ); + + // + // We call the notify package to report that the ea's were + // modified. + // + + FatNotifyReportChange( IrpContext, + Vcb, + Fcb, + FILE_NOTIFY_CHANGE_EA, + FILE_ACTION_MODIFIED ); + + Irp->IoStatus.Information = 0; + Status = STATUS_SUCCESS; + + try_exit: NOTHING; + + // + // Unpin the dirents for the Fcb and EaFcb if necessary. + // + + FatUnpinBcb( IrpContext, Bcb ); + FatUnpinBcb( IrpContext, EaBcb ); + + FatUnpinRepinnedBcbs( IrpContext ); + + } finally { + + DebugUnwind( FatCommonSetEa ); + + // + // If this is an abnormal termination, we need to clean up + // any locked resources. + // + + if (AbnormalTermination()) { + + // + // Unpin the dirents for the Fcb, EaFcb and EaSetFcb if necessary. + // + + FatUnpinBcb( IrpContext, Bcb ); + FatUnpinBcb( IrpContext, EaBcb ); + + FatUnpinEaRange( IrpContext, &EaSetRange ); + } + + // + // Release the Fcbs/Vcb acquired. + // + + if (AcquiredEaFcb) { + FatReleaseFcb( IrpContext, Vcb->EaFcb ); + } + + if (AcquiredFcb) { + FatReleaseFcb( IrpContext, Fcb ); + } + + if (AcquiredParentDcb) { + FatReleaseFcb( IrpContext, Fcb->ParentDcb ); + } + + if (AcquiredRootDcb) { + FatReleaseFcb( IrpContext, Fcb->Vcb->RootDcb ); + } + + if (AcquiredVcb) { + FatReleaseVcb( IrpContext, Fcb->Vcb ); + } + + // + // Deallocate our Ea buffer. + // + + if (EaSetHeader != NULL) { + + ExFreePool( EaSetHeader ); + } + + // + // Complete the irp. + // + + if (!AbnormalTermination()) { + + FatCompleteRequest( IrpContext, Irp, Status ); + } + + DebugTrace(-1, Dbg, "FatCommonSetEa -> %08lx\n", Status); + } + + // + // And return to our caller + // + + return Status; +#endif +} + + +#if 0 + +// +// Local Support Routine +// + +IO_STATUS_BLOCK +FatQueryEaUserEaList ( + IN PIRP_CONTEXT IrpContext, + OUT PCCB Ccb, + IN PPACKED_EA FirstPackedEa, + IN ULONG PackedEasLength, + OUT PUCHAR UserBuffer, + IN ULONG UserBufferLength, + IN PUCHAR UserEaList, + IN ULONG UserEaListLength, + IN BOOLEAN ReturnSingleEntry + ) + +/*++ + +Routine Description: + + This routine is the work routine for querying EAs given an ea index + +Arguments: + + Ccb - Supplies the Ccb for the query + + FirstPackedEa - Supplies the first ea for the file being queried + + PackedEasLength - Supplies the length of the ea data + + UserBuffer - Supplies the buffer to receive the full eas + + UserBufferLength - Supplies the length, in bytes, of the user buffer + + UserEaList - Supplies the user specified ea name list + + UserEaListLength - Supplies the length, in bytes, of the user ea list + + ReturnSingleEntry - Indicates if we are to return a single entry or not + +Return Value: + + IO_STATUS_BLOCK - Receives the completion status for the operation + +--*/ + +{ + IO_STATUS_BLOCK Iosb; + + ULONG Offset; + ULONG RemainingUserBufferLength; + + PPACKED_EA PackedEa; + ULONG PackedEaSize; + + PFILE_FULL_EA_INFORMATION LastFullEa = NULL; + ULONG LastFullEaSize; + PFILE_FULL_EA_INFORMATION NextFullEa; + + PFILE_GET_EA_INFORMATION GetEa; + + BOOLEAN Overflow; + + DebugTrace(+1, Dbg, "FatQueryEaUserEaList...\n", 0); + + LastFullEa = NULL; + NextFullEa = (PFILE_FULL_EA_INFORMATION) UserBuffer; + RemainingUserBufferLength = UserBufferLength; + + Overflow = FALSE; + + for (GetEa = (PFILE_GET_EA_INFORMATION) &UserEaList[0]; + GetEa < (PFILE_GET_EA_INFORMATION) ((PUCHAR) UserEaList + + UserEaListLength); + GetEa = (GetEa->NextEntryOffset == 0 + ? (PFILE_GET_EA_INFORMATION) MAXUINT_PTR + : (PFILE_GET_EA_INFORMATION) ((PUCHAR) GetEa + + GetEa->NextEntryOffset))) { + + OEM_STRING Str; + OEM_STRING OutputEaName; + + DebugTrace(0, Dbg, "Top of loop, GetEa = %p\n", GetEa); + DebugTrace(0, Dbg, "LastFullEa = %p\n", LastFullEa); + DebugTrace(0, Dbg, "NextFullEa = %p\n", NextFullEa); + DebugTrace(0, Dbg, "RemainingUserBufferLength = %08lx\n", RemainingUserBufferLength); + + // + // Make a string reference to the GetEa and see if we can + // locate the ea by name + // + + Str.MaximumLength = Str.Length = GetEa->EaNameLength; + Str.Buffer = &GetEa->EaName[0]; + + // + // Check for a valid name. + // + + if (!FatIsEaNameValid( IrpContext, Str )) { + + DebugTrace(-1, Dbg, + "FatQueryEaUserEaList: Invalid Ea Name -> %Z\n", + &Str); + + Iosb.Information = (PUCHAR)GetEa - UserEaList; + Iosb.Status = STATUS_INVALID_EA_NAME; + return Iosb; + } + + // + // If this is a duplicate name, we skip to the next. + // + + if (FatIsDuplicateEaName( IrpContext, GetEa, UserEaList )) { + + DebugTrace(0, Dbg, "FatQueryEaUserEaList: Duplicate name\n", 0); + continue; + } + + if (!FatLocateEaByName( IrpContext, + FirstPackedEa, + PackedEasLength, + &Str, + &Offset )) { + + Offset = 0xffffffff; + + DebugTrace(0, Dbg, "Need to dummy up an ea\n", 0); + + // + // We were not able to locate the name therefore we must + // dummy up a entry for the query. The needed Ea size is + // the size of the name + 4 (next entry offset) + 1 (flags) + // + 1 (name length) + 2 (value length) + the name length + + // 1 (null byte). + // + + if ((ULONG)(4+1+1+2+GetEa->EaNameLength+1) + > RemainingUserBufferLength) { + + Overflow = TRUE; + break; + } + + // + // Everything is going to work fine, so copy over the name, + // set the name length and zero out the rest of the ea. + // + + NextFullEa->NextEntryOffset = 0; + NextFullEa->Flags = 0; + NextFullEa->EaNameLength = GetEa->EaNameLength; + NextFullEa->EaValueLength = 0; + RtlCopyMemory( &NextFullEa->EaName[0], + &GetEa->EaName[0], + GetEa->EaNameLength ); + + // + // Upcase the name in the buffer. + // + + OutputEaName.MaximumLength = OutputEaName.Length = Str.Length; + OutputEaName.Buffer = NextFullEa->EaName; + + FatUpcaseEaName( IrpContext, &OutputEaName, &OutputEaName ); + + NextFullEa->EaName[GetEa->EaNameLength] = 0; + + } else { + + DebugTrace(0, Dbg, "Located the ea, Offset = %08lx\n", Offset); + + // + // We were able to locate the packed ea + // Reference the packed ea + // + + PackedEa = (PPACKED_EA) ((PUCHAR) FirstPackedEa + Offset); + SizeOfPackedEa( PackedEa, &PackedEaSize ); + + DebugTrace(0, Dbg, "PackedEaSize = %08lx\n", PackedEaSize); + + // + // We know that the packed ea is 4 bytes smaller than its + // equivalent full ea so we need to check the remaining + // user buffer length against the computed full ea size. + // + + if (PackedEaSize + 4 > RemainingUserBufferLength) { + + Overflow = TRUE; + break; + } + + // + // Everything is going to work fine, so copy over the packed + // ea to the full ea and zero out the next entry offset field. + // + + RtlCopyMemory( &NextFullEa->Flags, + &PackedEa->Flags, + PackedEaSize ); + + NextFullEa->NextEntryOffset = 0; + } + + // + // At this point we've copied a new full ea into the next full ea + // location. So now go back and set the set full eas entry offset + // field to be the difference between out two pointers. + // + + if (LastFullEa != NULL) { + + LastFullEa->NextEntryOffset = (ULONG)((PUCHAR) NextFullEa + - (PUCHAR) LastFullEa); + } + + // + // Set the last full ea to the next full ea, compute + // where the next full should be, and decrement the remaining user + // buffer length appropriately + // + + LastFullEa = NextFullEa; + LastFullEaSize = LongAlign( SizeOfFullEa( LastFullEa )); + RemainingUserBufferLength -= LastFullEaSize; + NextFullEa = (PFILE_FULL_EA_INFORMATION) ((PUCHAR) NextFullEa + + LastFullEaSize); + + // + // Remember the offset of the next ea in case we're asked to + // resume the iteration + // + + Ccb->OffsetOfNextEaToReturn = FatLocateNextEa( IrpContext, + FirstPackedEa, + PackedEasLength, + Offset ); + + // + // If we were to return a single entry then break out of our loop + // now + // + + if (ReturnSingleEntry) { + + break; + } + } + + // + // Now we've iterated all that can and we've exited the preceding loop + // with either all, some or no information stored in the return buffer. + // We can decide if we got everything to fit by checking the local + // Overflow variable + // + + if (Overflow) { + + Iosb.Information = 0; + Iosb.Status = STATUS_BUFFER_OVERFLOW; + + } else { + + // + // Otherwise we've been successful in returing at least one + // ea so we'll compute the number of bytes used to store the + // full ea information. The number of bytes used is the difference + // between the LastFullEa and the start of the buffer, and the + // non-aligned size of the last full ea. + // + + Iosb.Information = ((PUCHAR) LastFullEa - UserBuffer) + + SizeOfFullEa(LastFullEa); + + Iosb.Status = STATUS_SUCCESS; + } + + DebugTrace(-1, Dbg, "FatQueryEaUserEaList -> Iosb.Status = %08lx\n", + Iosb.Status); + + return Iosb; +} + + +// +// Local Support Routine +// + +IO_STATUS_BLOCK +FatQueryEaIndexSpecified ( + IN PIRP_CONTEXT IrpContext, + OUT PCCB Ccb, + IN PPACKED_EA FirstPackedEa, + IN ULONG PackedEasLength, + OUT PUCHAR UserBuffer, + IN ULONG UserBufferLength, + IN ULONG UserEaIndex, + IN BOOLEAN ReturnSingleEntry + ) + +/*++ + +Routine Description: + + This routine is the work routine for querying EAs given an ea index + +Arguments: + + Ccb - Supplies the Ccb for the query + + FirstPackedEa - Supplies the first ea for the file being queried + + PackedEasLength - Supplies the length of the ea data + + UserBuffer - Supplies the buffer to receive the full eas + + UserBufferLength - Supplies the length, in bytes, of the user buffer + + UserEaIndex - Supplies the index of the first ea to return. + + RestartScan - Indicates if the first item to return is at the + beginning of the packed ea list or if we should resume our + previous iteration + +Return Value: + + IO_STATUS_BLOCK - Receives the completion status for the operation + +--*/ + +{ + IO_STATUS_BLOCK Iosb; + + ULONG i; + ULONG Offset; + + DebugTrace(+1, Dbg, "FatQueryEaIndexSpecified...\n", 0); + + // + // Zero out the information field of the iosb + // + + Iosb.Information = 0; + + // + // If the index value is zero or there are no Eas on the file, then + // the specified index can't be returned. + // + + if (UserEaIndex == 0 + || PackedEasLength == 0) { + + DebugTrace( -1, Dbg, "FatQueryEaIndexSpecified: Non-existant entry\n", 0 ); + + Iosb.Status = STATUS_NONEXISTENT_EA_ENTRY; + + return Iosb; + } + + // + // Iterate the eas until we find the index we're after. + // + + for (i = 1, Offset = 0; + (i < UserEaIndex) && (Offset < PackedEasLength); + i += 1, Offset = FatLocateNextEa( IrpContext, + FirstPackedEa, + PackedEasLength, Offset )) { + + NOTHING; + } + + // + // Make sure the offset we're given to the ea is a real offset otherwise + // the ea doesn't exist + // + + if (Offset >= PackedEasLength) { + + // + // If we just passed the last Ea, we will return STATUS_NO_MORE_EAS. + // This is for the caller who may be enumerating the Eas. + // + + if (i == UserEaIndex) { + + Iosb.Status = STATUS_NO_MORE_EAS; + + // + // Otherwise we report that this is a bad ea index. + // + + } else { + + Iosb.Status = STATUS_NONEXISTENT_EA_ENTRY; + } + + DebugTrace(-1, Dbg, "FatQueryEaIndexSpecified -> %08lx\n", Iosb.Status); + return Iosb; + } + + // + // We now have the offset of the first Ea to return to the user. + // We simply call our EaSimpleScan routine to do the actual work. + // + + Iosb = FatQueryEaSimpleScan( IrpContext, + Ccb, + FirstPackedEa, + PackedEasLength, + UserBuffer, + UserBufferLength, + ReturnSingleEntry, + Offset ); + + DebugTrace(-1, Dbg, "FatQueryEaIndexSpecified -> %08lx\n", Iosb.Status); + + return Iosb; + +} + + +// +// Local Support Routine +// + +IO_STATUS_BLOCK +FatQueryEaSimpleScan ( + IN PIRP_CONTEXT IrpContext, + OUT PCCB Ccb, + IN PPACKED_EA FirstPackedEa, + IN ULONG PackedEasLength, + OUT PUCHAR UserBuffer, + IN ULONG UserBufferLength, + IN BOOLEAN ReturnSingleEntry, + ULONG StartOffset + ) + +/*++ + +Routine Description: + + This routine is the work routine for querying EAs from the beginning of + the ea list. + +Arguments: + + Ccb - Supplies the Ccb for the query + + FirstPackedEa - Supplies the first ea for the file being queried + + PackedEasLength - Supplies the length of the ea data + + UserBuffer - Supplies the buffer to receive the full eas + + UserBufferLength - Supplies the length, in bytes, of the user buffer + + ReturnSingleEntry - Indicates if we are to return a single entry or not + + StartOffset - Indicates the offset within the Ea data to return the + first block of data. + +Return Value: + + IO_STATUS_BLOCK - Receives the completion status for the operation + +--*/ + +{ + IO_STATUS_BLOCK Iosb; + + ULONG RemainingUserBufferLength; + + PPACKED_EA PackedEa; + ULONG PackedEaSize; + + PFILE_FULL_EA_INFORMATION LastFullEa; + ULONG LastFullEaSize; + PFILE_FULL_EA_INFORMATION NextFullEa; + BOOLEAN BufferOverflow = FALSE; + + + DebugTrace(+1, Dbg, "FatQueryEaSimpleScan...\n", 0); + + // + // Zero out the information field in the Iosb + // + + Iosb.Information = 0; + + LastFullEa = NULL; + NextFullEa = (PFILE_FULL_EA_INFORMATION) UserBuffer; + RemainingUserBufferLength = UserBufferLength; + + while (StartOffset < PackedEasLength) { + + DebugTrace(0, Dbg, "Top of loop, Offset = %08lx\n", StartOffset); + DebugTrace(0, Dbg, "LastFullEa = %p\n", LastFullEa); + DebugTrace(0, Dbg, "NextFullEa = %p\n", NextFullEa); + DebugTrace(0, Dbg, "RemainingUserBufferLength = %08lx\n", RemainingUserBufferLength); + + // + // Reference the packed ea of interest. + // + + PackedEa = (PPACKED_EA) ((PUCHAR) FirstPackedEa + StartOffset); + + SizeOfPackedEa( PackedEa, &PackedEaSize ); + + DebugTrace(0, Dbg, "PackedEaSize = %08lx\n", PackedEaSize); + + // + // We know that the packed ea is 4 bytes smaller than its + // equivalent full ea so we need to check the remaining + // user buffer length against the computed full ea size. + // + + if (PackedEaSize + 4 > RemainingUserBufferLength) { + + BufferOverflow = TRUE; + break; + } + + // + // Everything is going to work fine, so copy over the packed + // ea to the full ea and zero out the next entry offset field. + // Then go back and set the last full eas entry offset field + // to be the difference between the two pointers. + // + + RtlCopyMemory( &NextFullEa->Flags, &PackedEa->Flags, PackedEaSize ); + NextFullEa->NextEntryOffset = 0; + + if (LastFullEa != NULL) { + + LastFullEa->NextEntryOffset = (ULONG)((PUCHAR) NextFullEa + - (PUCHAR) LastFullEa); + } + + // + // Set the last full ea to the next full ea, compute + // where the next full should be, and decrement the remaining user + // buffer length appropriately + // + + LastFullEa = NextFullEa; + LastFullEaSize = LongAlign( SizeOfFullEa( LastFullEa )); + RemainingUserBufferLength -= LastFullEaSize; + NextFullEa = (PFILE_FULL_EA_INFORMATION) ((PUCHAR) NextFullEa + + LastFullEaSize); + + // + // Remember the offset of the next ea in case we're asked to + // resume the teration + // + + StartOffset = FatLocateNextEa( IrpContext, + FirstPackedEa, + PackedEasLength, + StartOffset ); + + Ccb->OffsetOfNextEaToReturn = StartOffset; + + // + // If we were to return a single entry then break out of our loop + // now + // + + if (ReturnSingleEntry) { + + break; + } + } + + // + // Now we've iterated all that can and we've exited the preceding loop + // with either some or no information stored in the return buffer. + // We can decide which it is by checking if the last full ea is null + // + + if (LastFullEa == NULL) { + + Iosb.Information = 0; + + // + // We were not able to return a single ea entry, now we need to find + // out if it is because we didn't have an entry to return or the + // buffer is too small. If the Offset variable is less than + // PackedEaList->UsedSize then the user buffer is too small + // + + if (PackedEasLength == 0) { + + Iosb.Status = STATUS_NO_EAS_ON_FILE; + + } else if (StartOffset >= PackedEasLength) { + + Iosb.Status = STATUS_NO_MORE_EAS; + + } else { + + Iosb.Status = STATUS_BUFFER_TOO_SMALL; + } + + } else { + + // + // Otherwise we've been successful in returing at least one + // ea so we'll compute the number of bytes used to store the + // full ea information. The number of bytes used is the difference + // between the LastFullEa and the start of the buffer, and the + // non-aligned size of the last full ea. + // + + Iosb.Information = ((PUCHAR) LastFullEa - UserBuffer) + + SizeOfFullEa( LastFullEa ); + + // + // If there are more to return, report the buffer was too small. + // Otherwise return STATUS_SUCCESS. + // + + if (BufferOverflow) { + + Iosb.Status = STATUS_BUFFER_OVERFLOW; + + } else { + + Iosb.Status = STATUS_SUCCESS; + } + } + + DebugTrace(-1, Dbg, "FatQueryEaSimpleScan -> Iosb.Status = %08lx\n", + Iosb.Status); + + return Iosb; + +} + + +// +// Local Support Routine +// + +BOOLEAN +FatIsDuplicateEaName ( + IN PIRP_CONTEXT IrpContext, + IN PFILE_GET_EA_INFORMATION GetEa, + IN PUCHAR UserBuffer + ) + +/*++ + +Routine Description: + + This routine walks through a list of ea names to find a duplicate name. + 'GetEa' is an actual position in the list. We are only interested in + previous matching ea names, as the ea information for that ea name + would have been returned with the previous instance. + +Arguments: + + GetEa - Supplies the Ea name structure for the ea name to match. + + UserBuffer - Supplies a pointer to the user buffer with the list + of ea names to search for. + +Return Value: + + BOOLEAN - TRUE if a previous match is found, FALSE otherwise. + +--*/ + +{ + PFILE_GET_EA_INFORMATION ThisGetEa; + + BOOLEAN DuplicateFound; + OEM_STRING EaString; + + DebugTrace(+1, Dbg, "FatIsDuplicateEaName...\n", 0); + + EaString.MaximumLength = EaString.Length = GetEa->EaNameLength; + EaString.Buffer = &GetEa->EaName[0]; + + FatUpcaseEaName( IrpContext, &EaString, &EaString ); + + DuplicateFound = FALSE; + + for (ThisGetEa = (PFILE_GET_EA_INFORMATION) &UserBuffer[0]; + ThisGetEa < GetEa + && ThisGetEa->NextEntryOffset != 0; + ThisGetEa = (PFILE_GET_EA_INFORMATION) ((PUCHAR) ThisGetEa + + ThisGetEa->NextEntryOffset)) { + + OEM_STRING Str; + + DebugTrace(0, Dbg, "Top of loop, ThisGetEa = %p\n", ThisGetEa); + + // + // Make a string reference to the GetEa and see if we can + // locate the ea by name + // + + Str.MaximumLength = Str.Length = ThisGetEa->EaNameLength; + Str.Buffer = &ThisGetEa->EaName[0]; + + DebugTrace(0, Dbg, "FatIsDuplicateEaName: Next Name -> %Z\n", &Str); + + if ( FatAreNamesEqual(IrpContext, Str, EaString) ) { + + DebugTrace(0, Dbg, "FatIsDuplicateEaName: Duplicate found\n", 0); + DuplicateFound = TRUE; + break; + } + } + + DebugTrace(-1, Dbg, "FatIsDuplicateEaName: Exit -> %04x\n", DuplicateFound); + + return DuplicateFound; +} +#endif + + diff --git a/filesys/fastfat/easup.c b/filesys/fastfat/easup.c new file mode 100644 index 000000000..96ba55dcf --- /dev/null +++ b/filesys/fastfat/easup.c @@ -0,0 +1,3852 @@ +/*++ + +Copyright (c) 1990-2000 Microsoft Corporation + +Module Name: + + EaSup.c + +Abstract: + + This module implements the cluster operations on the EA file for Fat. + + +--*/ + +#include "FatProcs.h" + +// +// Local debug trace level +// + +#define Dbg (DEBUG_TRACE_EA) + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, FatAddEaSet) +#pragma alloc_text(PAGE, FatAppendPackedEa) +#pragma alloc_text(PAGE, FatCreateEa) +#pragma alloc_text(PAGE, FatDeleteEa) +#pragma alloc_text(PAGE, FatDeleteEaSet) +#pragma alloc_text(PAGE, FatDeletePackedEa) +#pragma alloc_text(PAGE, FatGetEaFile) +#pragma alloc_text(PAGE, FatGetEaLength) +#pragma alloc_text(PAGE, FatGetNeedEaCount) +#pragma alloc_text(PAGE, FatIsEaNameValid) +#pragma alloc_text(PAGE, FatLocateEaByName) +#pragma alloc_text(PAGE, FatLocateNextEa) +#pragma alloc_text(PAGE, FatReadEaSet) +#pragma alloc_text(PAGE, FatPinEaRange) +#pragma alloc_text(PAGE, FatMarkEaRangeDirty) +#pragma alloc_text(PAGE, FatUnpinEaRange) +#endif + + +// +// Any access to the Ea file must recognize when a section boundary is being +// crossed. +// + +#define EA_SECTION_SIZE (0x00040000) + + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatGetEaLength ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PDIRENT Dirent, + OUT PULONG EaLength + ) + +/*++ + +Routine Description: + + This routine looks up the Ea length for the Eas of the file. This + length is the length of the packed eas, including the 4 bytes which + contain the Ea length. + + This routine pins down the Ea set for the desired file and copies + this field from the Ea set header. + +Arguments: + + Vcb - Vcb for the volume containing the Eas. + + Dirent - Supplies a pointer to the dirent for the file in question. + + EaLength - Supplies the address to store the length of the Eas. + +Return Value: + + None + +--*/ + +{ + PBCB EaBcb = NULL; + BOOLEAN LockedEaFcb = FALSE; + EA_RANGE EaSetRange; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatGetEaLength ...\n", 0); + + // + // If this is Fat32 volume, or if the handle is 0 then the Ea length is 0. + // + + if (FatIsFat32( Vcb ) || + Dirent->ExtendedAttributes == 0) { + + *EaLength = 0; + DebugTrace(-1, Dbg, "FatGetEaLength -> %08lx\n", TRUE); + return; + } + + RtlZeroMemory( &EaSetRange, sizeof( EA_RANGE )); + + // + // Use a try to facilitate cleanup. + // + + try { + + PDIRENT EaDirent; + OEM_STRING ThisFilename; + UCHAR Buffer[12]; + PEA_SET_HEADER EaSetHeader; + + // + // Initial the local values. + // + + EaBcb = NULL; + LockedEaFcb = FALSE; + + // + // Try to get the Ea file object. Return FALSE on failure. + // + + FatGetEaFile( IrpContext, + Vcb, + &EaDirent, + &EaBcb, + FALSE, + FALSE ); + + LockedEaFcb = TRUE; + + // + // If we didn't get the file because it doesn't exist, then the + // disk is corrupted. + // + + if (Vcb->VirtualEaFile == NULL) { + + DebugTrace(0, Dbg, "FatGetEaLength: Ea file doesn't exist\n", 0); + FatRaiseStatus( IrpContext, STATUS_NO_EAS_ON_FILE ); + } + + // + // Try to pin down the Ea set header for the index in the + // dirent. If the operation doesn't complete, return FALSE + // from this routine. + // + + ThisFilename.Buffer = (PCHAR)Buffer; + Fat8dot3ToString( IrpContext, Dirent, FALSE, &ThisFilename ); + + FatReadEaSet( IrpContext, + Vcb, + Dirent->ExtendedAttributes, + &ThisFilename, + FALSE, + &EaSetRange ); + + EaSetHeader = (PEA_SET_HEADER) EaSetRange.Data; + + // + // We now have the Ea set header for this file. We simply copy + // the Ea length field. + // + + CopyUchar4( EaLength, EaSetHeader->cbList ); + DebugTrace(0, Dbg, "FatGetEaLength: Length of Ea is -> %08lx\n", + *EaLength); + + } finally { + + DebugUnwind( FatGetEaLength ); + + // + // Unpin the EaDirent and the EaSetHeader if pinned. + // + + FatUnpinBcb( IrpContext, EaBcb ); + + FatUnpinEaRange( IrpContext, &EaSetRange ); + + // + // Release the Fcb for the Ea file if locked. + // + + if (LockedEaFcb) { + + FatReleaseFcb( IrpContext, Vcb->EaFcb ); + } + + DebugTrace(-1, Dbg, "FatGetEaLength: Ea length -> %08lx\n", *EaLength); + } + + return; +} + + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatGetNeedEaCount ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PDIRENT Dirent, + OUT PULONG NeedEaCount + ) + +/*++ + +Routine Description: + + This routine looks up the Need Ea count for the file. The value is the + in the ea header for the file. + +Arguments: + + Vcb - Vcb for the volume containing the Eas. + + Dirent - Supplies a pointer to the dirent for the file in question. + + NeedEaCount - Supplies the address to store the Need Ea count. + +Return Value: + + None + +--*/ + +{ + PBCB EaBcb = NULL; + BOOLEAN LockedEaFcb = FALSE; + EA_RANGE EaSetRange; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatGetNeedEaCount ...\n", 0); + + // + // If the handle is 0 then the Need Ea count is 0. + // + + if (Dirent->ExtendedAttributes == 0) { + + *NeedEaCount = 0; + DebugTrace(-1, Dbg, "FatGetNeedEaCount -> %08lx\n", TRUE); + return; + } + + RtlZeroMemory( &EaSetRange, sizeof( EA_RANGE )); + + // + // Use a try to facilitate cleanup. + // + + try { + + PDIRENT EaDirent; + OEM_STRING ThisFilename; + UCHAR Buffer[12]; + PEA_SET_HEADER EaSetHeader; + + // + // Initial the local values. + // + + EaBcb = NULL; + LockedEaFcb = FALSE; + + // + // Try to get the Ea file object. Return FALSE on failure. + // + + FatGetEaFile( IrpContext, + Vcb, + &EaDirent, + &EaBcb, + FALSE, + FALSE ); + + LockedEaFcb = TRUE; + + // + // If we didn't get the file because it doesn't exist, then the + // disk is corrupted. + // + + if (Vcb->VirtualEaFile == NULL) { + + DebugTrace(0, Dbg, "FatGetNeedEaCount: Ea file doesn't exist\n", 0); + FatRaiseStatus( IrpContext, STATUS_NO_EAS_ON_FILE ); + } + + // + // Try to pin down the Ea set header for the index in the + // dirent. If the operation doesn't complete, return FALSE + // from this routine. + // + + ThisFilename.Buffer = (PCHAR)Buffer; + Fat8dot3ToString( IrpContext, Dirent, FALSE, &ThisFilename ); + + FatReadEaSet( IrpContext, + Vcb, + Dirent->ExtendedAttributes, + &ThisFilename, + FALSE, + &EaSetRange ); + + EaSetHeader = (PEA_SET_HEADER) EaSetRange.Data; + + // + // We now have the Ea set header for this file. We simply copy + // the Need Ea field. + // + + *NeedEaCount = EaSetHeader->NeedEaCount; + + } finally { + + DebugUnwind( FatGetNeedEaCount ); + + // + // Unpin the EaDirent and the EaSetHeader if pinned. + // + + FatUnpinBcb( IrpContext, EaBcb ); + + FatUnpinEaRange( IrpContext, &EaSetRange ); + + // + // Release the Fcb for the Ea file if locked. + // + + if (LockedEaFcb) { + + FatReleaseFcb( IrpContext, Vcb->EaFcb ); + } + + DebugTrace(-1, Dbg, "FatGetNeedEaCount: NeedEaCount -> %08lx\n", *NeedEaCount); + } + + return; +} + + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatCreateEa ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PUCHAR Buffer, + IN ULONG Length, + IN POEM_STRING FileName, + OUT PUSHORT EaHandle + ) + +/*++ + +Routine Description: + + This routine adds an entire ea set to the Ea file. The owning file + is specified in 'FileName'. This is used to replace the Ea set attached + to an existing file during a supersede operation. + + NOTE: This routine may block, it should not be called unless the + thread is waitable. + +Arguments: + + Vcb - Supplies the Vcb for the volume. + + Buffer - Buffer with the Ea list to add. + + Length - Length of the buffer. + + FileName - The Ea's will be attached to this file. + + EaHandle - The new ea handle will be assigned to this address. + +Return Value: + + None + +--*/ + +{ + PBCB EaBcb; + BOOLEAN LockedEaFcb; + + PEA_SET_HEADER EaSetHeader = NULL; + EA_RANGE EaSetRange; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatCreateEa...\n", 0); + + EaBcb = NULL; + LockedEaFcb = FALSE; + + RtlZeroMemory( &EaSetRange, sizeof( EA_RANGE )); + + // + // Use 'try' to facilitate cleanup. + // + + try { + + PDIRENT EaDirent; + + ULONG PackedEasLength; + ULONG AllocationLength; + ULONG BytesPerCluster; + + PFILE_FULL_EA_INFORMATION FullEa; + + // + // We will allocate a buffer and copy the Ea list from the user's + // buffer to a FAT packed Ea list. Initial allocation is one + // cluster, our starting offset into the packed Ea list is 0. + // + + PackedEasLength = 0; + + BytesPerCluster = 1 << Vcb->AllocationSupport.LogOfBytesPerCluster; + + AllocationLength = (PackedEasLength + + SIZE_OF_EA_SET_HEADER + + BytesPerCluster - 1) + & ~(BytesPerCluster - 1); + + // + // Allocate the memory and store the file name into it. + // + + EaSetHeader = FsRtlAllocatePoolWithTag( PagedPool, + AllocationLength, + TAG_EA_SET_HEADER ); + + RtlZeroMemory( EaSetHeader, AllocationLength ); + + RtlCopyMemory( EaSetHeader->OwnerFileName, + FileName->Buffer, + FileName->Length ); + + AllocationLength -= SIZE_OF_EA_SET_HEADER; + + // + // Loop through the user's Ea list. Catch any error for invalid + // name or non-existent Ea value. + // + + for ( FullEa = (PFILE_FULL_EA_INFORMATION) Buffer; + FullEa < (PFILE_FULL_EA_INFORMATION) &Buffer[Length]; + FullEa = (PFILE_FULL_EA_INFORMATION) (FullEa->NextEntryOffset == 0 ? + &Buffer[Length] : + (PUCHAR) FullEa + FullEa->NextEntryOffset)) { + + OEM_STRING EaName; + ULONG EaOffset; + + EaName.Length = FullEa->EaNameLength; + EaName.Buffer = &FullEa->EaName[0]; + + // + // Make sure the ea name is valid + // + + if (!FatIsEaNameValid( IrpContext, EaName )) { + + DebugTrace(0, Dbg, + "FatCreateEa: Invalid Ea Name -> %Z\n", + EaName); + + IrpContext->OriginatingIrp->IoStatus.Information = (PUCHAR)FullEa - Buffer; + IrpContext->OriginatingIrp->IoStatus.Status = STATUS_INVALID_EA_NAME; + FatRaiseStatus( IrpContext, STATUS_INVALID_EA_NAME ); + } + + // + // Check that no invalid ea flags are set. + // + + // + // TEMPCODE We are returning STATUS_INVALID_EA_NAME + // until a more appropriate error code exists. + // + + if (FullEa->Flags != 0 + && FullEa->Flags != FILE_NEED_EA) { + + IrpContext->OriginatingIrp->IoStatus.Information = (PUCHAR)FullEa - Buffer; + IrpContext->OriginatingIrp->IoStatus.Status = STATUS_INVALID_EA_NAME; + FatRaiseStatus( IrpContext, STATUS_INVALID_EA_NAME ); + } + + // + // If this is a duplicate name then delete the current ea + // value. + // + + if (FatLocateEaByName( IrpContext, + (PPACKED_EA) EaSetHeader->PackedEas, + PackedEasLength, + &EaName, + &EaOffset )) { + + DebugTrace(0, Dbg, "FatCreateEa: Duplicate name found\n", 0); + + FatDeletePackedEa( IrpContext, + EaSetHeader, + &PackedEasLength, + EaOffset ); + } + + // + // We ignore this value if the eavalue length is zero. + // + + if (FullEa->EaValueLength == 0) { + + DebugTrace(0, Dbg, + "FatCreateEa: Empty ea\n", + 0); + + continue; + } + + FatAppendPackedEa( IrpContext, + &EaSetHeader, + &PackedEasLength, + &AllocationLength, + FullEa, + BytesPerCluster ); + } + + // + // If the resulting length isn't zero, then allocate a FAT cluster + // to store the data. + // + + if (PackedEasLength != 0) { + + PEA_SET_HEADER NewEaSetHeader; + + // + // If the packed eas length (plus 4 bytes) is greater + // than the maximum allowed ea size, we return an error. + // + + if (PackedEasLength + 4 > MAXIMUM_EA_SIZE) { + + DebugTrace( 0, Dbg, "Ea length is greater than maximum\n", 0 ); + + FatRaiseStatus( IrpContext, STATUS_EA_TOO_LARGE ); + } + + // + // Get the Ea file. + // + + FatGetEaFile( IrpContext, + Vcb, + &EaDirent, + &EaBcb, + TRUE, + TRUE ); + + LockedEaFcb = TRUE; + + FatAddEaSet( IrpContext, + Vcb, + PackedEasLength + SIZE_OF_EA_SET_HEADER, + EaBcb, + EaDirent, + EaHandle, + &EaSetRange ); + + NewEaSetHeader = (PEA_SET_HEADER) EaSetRange.Data; + + // + // Store the length of the new Ea's into the NewEaSetHeader. + // This is the PackedEasLength + 4. + // + + PackedEasLength += 4; + + CopyU4char( EaSetHeader->cbList, &PackedEasLength ); + + // + // Copy all but the first four bytes of EaSetHeader into + // the new ea. The signature and index fields have + // already been filled in. + // + + RtlCopyMemory( &NewEaSetHeader->NeedEaCount, + &EaSetHeader->NeedEaCount, + PackedEasLength + SIZE_OF_EA_SET_HEADER - 8 ); + + FatMarkEaRangeDirty( IrpContext, Vcb->VirtualEaFile, &EaSetRange ); + FatUnpinEaRange( IrpContext, &EaSetRange ); + + CcFlushCache( Vcb->VirtualEaFile->SectionObjectPointer, NULL, 0, NULL ); + + // + // There was no data added to the Ea file. Return a handle + // of 0. + // + + } else { + + *EaHandle = 0; + } + + } finally { + + DebugUnwind( FatCreateEa ); + + // + // Deallocate the EaSetHeader if present. + // + + if (EaSetHeader) { + + ExFreePool( EaSetHeader ); + } + + // + // Release the EaFcb if held. + // + + if (LockedEaFcb) { + + FatReleaseFcb( IrpContext, Vcb->EaFcb ); + } + + // + // Unpin the dirents for the EaFcb and EaSetFcb if necessary. + // + + FatUnpinBcb( IrpContext, EaBcb ); + FatUnpinEaRange( IrpContext, &EaSetRange ); + + DebugTrace(-1, Dbg, "FatCreateEa -> Exit\n", 0); + } + + return; +} + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatDeleteEa ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN USHORT EaHandle, + IN POEM_STRING FileName + ) + +/*++ + +Routine Description: + + This routine is called to remove an entire ea set. Most of the work + is done in the call to 'FatDeleteEaSet'. This routine opens the + Ea file and then calls the support routine. + + NOTE: This routine may block, it should not be called unless the + thread is waitable. + +Arguments: + + Vcb - Vcb for the volume + + EaHandle - The handle for the Ea's to remove. This handle will be + verified during this operation. + + FileName - The name of the file whose Ea's are being removed. This + name is compared against the Ea owner's name in the Ea set. + +Return Value: + + None. + +--*/ + +{ + PBCB EaBcb; + BOOLEAN LockedEaFcb; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatDeleteEa...\n", 0); + + // + // Initialize local values. + // + + EaBcb = NULL; + LockedEaFcb = FALSE; + + // + // Use a try statement to facilitate cleanup. + // + + try { + + PDIRENT EaDirent; + + // + // Get the Ea stream file. If the file doesn't exist on the disk + // then the disk has been corrupted. + // + + FatGetEaFile( IrpContext, + Vcb, + &EaDirent, + &EaBcb, + FALSE, + TRUE ); + + LockedEaFcb = TRUE; + + // + // If we didn't get the Ea file, then the disk is corrupt. + // + + if ( EaBcb == NULL ) { + + + DebugTrace(0, Dbg, + "FatDeleteEa: No Ea file exists\n", + 0); + + FatRaiseStatus( IrpContext, STATUS_NO_EAS_ON_FILE ); + } + + // + // We now have everything we need to delete the ea set. Call the + // support routine to do this. + // + + FatDeleteEaSet( IrpContext, + Vcb, + EaBcb, + EaDirent, + EaHandle, + FileName ); + + CcFlushCache( Vcb->VirtualEaFile->SectionObjectPointer, NULL, 0, NULL ); + + } finally { + + DebugUnwind( FatDeleteEa ); + + // + // Release the EaFcb if held. + // + + if (LockedEaFcb) { + + FatReleaseFcb( IrpContext, Vcb->EaFcb ); + } + + // + // Unpin the dirent for the Ea file if pinned. + // + + FatUnpinBcb( IrpContext, EaBcb ); + + DebugTrace(-1, Dbg, "FatDeleteEa -> Exit\n", 0); + } + + return; +} + + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatGetEaFile ( + IN PIRP_CONTEXT IrpContext, + IN OUT PVCB Vcb, + OUT PDIRENT *EaDirent, + OUT PBCB *EaBcb, + IN BOOLEAN CreateFile, + IN BOOLEAN ExclusiveFcb + ) + +/*++ + +Routine Description: + + This routine is used to completely initialize the Vcb and + the Ea file for the Vcb. + + If the Vcb doesn't have the Ea file object, then we first try to + lookup the Ea data file in the root directory and if that fails + we try to create the file. The 'CreateFile' flag is used to check + whether it is necessary to create the Ea file. + + This routine will lock down the Fcb for exclusive or shared access before + performing any operations. If the operation does not complete due + to blocking, exclusive or shared access will be given up before returning. + + If we are creating the Ea file and marking sections of it dirty, + we can't use the repin feature through the cache map. In that case + we use a local IrpContext and then unpin all of the Bcb's before + continuing. + + Note: If this routine will be creating the Ea file, we are guaranteed + to be waitable. + +Arguments: + + Vcb - Vcb for the volume + + EaDirent - Location to store the address of the pinned dirent for the + Ea file. + + EaBcb - Location to store the address of the Bcb for the pinned dirent. + + CreateFile - Boolean indicating whether we should create the Ea file + on the disk. + + ExclusiveFcb - Indicates whether shared or exclusive access is desired + for the EaFcb. + +Return Value: + + None. + +--*/ + +{ + PFILE_OBJECT EaStreamFile = NULL; + EA_RANGE EaFileRange; + + BOOLEAN UnwindLockedEaFcb = FALSE; + BOOLEAN UnwindLockedRootDcb = FALSE; + BOOLEAN UnwindAllocatedDiskSpace = FALSE; + BOOLEAN UnwindEaDirentCreated = FALSE; + BOOLEAN UnwindUpdatedSizes = FALSE; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatGetEaFile ...\n", 0); + + RtlZeroMemory( &EaFileRange, sizeof( EA_RANGE )); + + // + // Use a try to facilitate cleanup + // + + try { + + OEM_STRING EaFileName; + LARGE_INTEGER SectionSize; + + // + // Check if the Vcb already has the file object. If it doesn't, then + // we need to search the root directory for the Ea data file. + // + + if (Vcb->VirtualEaFile == NULL) { + + // + // Always lock the Ea file exclusively if we have to create the file. + // + + if ( !FatAcquireExclusiveFcb( IrpContext, Vcb->EaFcb )) { + + DebugTrace(0, Dbg, "FatGetEaFile: Can't grab exclusive\n", 0); + FatRaiseStatus( IrpContext, STATUS_CANT_WAIT ); + } + + UnwindLockedEaFcb = TRUE; + + // + // Otherwise we acquire the Fcb as the caller requested. + // + + } else { + + if ((ExclusiveFcb && !FatAcquireExclusiveFcb( IrpContext, Vcb->EaFcb )) + || (!ExclusiveFcb && !FatAcquireSharedFcb( IrpContext, Vcb->EaFcb))) { + + DebugTrace(0, Dbg, "FatGetEaFile: Can't grab EaFcb\n", 0); + + FatRaiseStatus( IrpContext, STATUS_CANT_WAIT ); + } + + UnwindLockedEaFcb = TRUE; + + // + // If the file now does not exist we need to release the Fcb and + // reacquire exclusive if we acquired shared. + // + + if ((Vcb->VirtualEaFile == NULL) && !ExclusiveFcb) { + + FatReleaseFcb( IrpContext, Vcb->EaFcb ); + UnwindLockedEaFcb = FALSE; + + if (!FatAcquireExclusiveFcb( IrpContext, Vcb->EaFcb )) { + + DebugTrace(0, Dbg, "FatGetEaFile: Can't grab EaFcb\n", 0); + + FatRaiseStatus( IrpContext, STATUS_CANT_WAIT ); + } + + UnwindLockedEaFcb = TRUE; + } + } + + // + // If the file object is now there we only need to get the + // dirent for the Ea file. + // + + if (Vcb->VirtualEaFile != NULL) { + + FatVerifyFcb( IrpContext, Vcb->EaFcb ); + + FatGetDirentFromFcbOrDcb( IrpContext, + Vcb->EaFcb, + FALSE, + EaDirent, + EaBcb ); + + try_return( NOTHING ); + + } else { + + VBO ByteOffset = 0; + + // + // Always mark the ea fcb as good. + // + + Vcb->EaFcb->FcbCondition = FcbGood; + + // + // We try to lookup the dirent for the Ea Fcb. + // + + EaFileName.Buffer = "EA DATA. SF"; + EaFileName.Length = 11; + EaFileName.MaximumLength = 12; + + // + // Now pick up the root directory to be synchronized with + // deletion/creation of entries. If we may create the file, + // get it exclusive right now. + // + // Again, note how we are relying on bottom-up lockorder. We + // already got the EaFcb. + // + + if (CreateFile) { + ExAcquireResourceExclusiveLite( Vcb->RootDcb->Header.Resource, TRUE ); + } else { + ExAcquireResourceSharedLite( Vcb->RootDcb->Header.Resource, TRUE ); + } + UnwindLockedRootDcb = TRUE; + + FatLocateSimpleOemDirent( IrpContext, + Vcb->EaFcb->ParentDcb, + &EaFileName, + EaDirent, + EaBcb, + &ByteOffset ); + + // + // If the file exists, we need to create the virtual file + // object for it. + // + + if (*EaDirent != NULL) { + + // + // Since we may be modifying the dirent, pin the data now. + // + + FatPinMappedData( IrpContext, + Vcb->EaFcb->ParentDcb, + ByteOffset, + sizeof(DIRENT), + EaBcb ); + + // + // Update the Fcb with information on the file size + // and disk location. Also increment the open/unclean + // counts in the EaFcb and the open count in the + // Vcb. + // + + Vcb->EaFcb->FirstClusterOfFile = (*EaDirent)->FirstClusterOfFile; + Vcb->EaFcb->DirentOffsetWithinDirectory = ByteOffset; + + // + // Find the allocation size. The purpose here is + // really to completely fill in the Mcb for the + // file. + // + + Vcb->EaFcb->Header.AllocationSize.QuadPart = FCB_LOOKUP_ALLOCATIONSIZE_HINT; + + FatLookupFileAllocationSize( IrpContext, Vcb->EaFcb ); + + // + // Start by computing the section size for the cache + // manager. + // + + SectionSize.QuadPart = (*EaDirent)->FileSize; + Vcb->EaFcb->Header.AllocationSize = SectionSize; + Vcb->EaFcb->Header.FileSize = SectionSize; + + // + // Create and initialize the file object for the + // Ea virtual file. + // + + EaStreamFile = FatOpenEaFile( IrpContext, Vcb->EaFcb ); + + Vcb->VirtualEaFile = EaStreamFile; + + // + // Else there was no dirent. If we were instructed to + // create the file object, we will try to create the dirent, + // allocate disk space, initialize the Ea file header and + // return this information to the user. + // + + } else if (CreateFile) { + + ULONG BytesPerCluster; + ULONG OffsetTableSize; + ULONG AllocationSize; + PEA_FILE_HEADER FileHeader; + USHORT AllocatedClusters; + PUSHORT CurrentIndex; + ULONG Index; + NTSTATUS Status = STATUS_SUCCESS; + + DebugTrace(0, Dbg, "FatGetEaFile: Creating local IrpContext\n", 0); + + BytesPerCluster = 1 << Vcb->AllocationSupport.LogOfBytesPerCluster; + + AllocationSize = (((ULONG) sizeof( EA_FILE_HEADER ) << 1) + BytesPerCluster - 1) + & ~(BytesPerCluster - 1); + + AllocatedClusters = (USHORT) (AllocationSize + >> Vcb->AllocationSupport.LogOfBytesPerCluster); + + OffsetTableSize = AllocationSize - sizeof( EA_FILE_HEADER ); + + // + // Allocate disk space, the space allocated is 1024 bytes + // rounded up to the nearest cluster size. + // + + FatAllocateDiskSpace( IrpContext, + Vcb, + 0, + &AllocationSize, + FALSE, + &Vcb->EaFcb->Mcb ); + + UnwindAllocatedDiskSpace = TRUE; + + // + // Allocate and initialize a dirent in the root directory + // to describe this new file. + // + + Vcb->EaFcb->DirentOffsetWithinDirectory = + FatCreateNewDirent( IrpContext, + Vcb->EaFcb->ParentDcb, + 1, + FALSE ); + + FatPrepareWriteDirectoryFile( IrpContext, + Vcb->EaFcb->ParentDcb, + Vcb->EaFcb->DirentOffsetWithinDirectory, + sizeof(DIRENT), + EaBcb, + EaDirent, + FALSE, + TRUE, + &Status ); + + NT_ASSERT( NT_SUCCESS( Status )); + + UnwindEaDirentCreated = TRUE; + + FatConstructDirent( IrpContext, + *EaDirent, + &EaFileName, + FALSE, + FALSE, + NULL, + FAT_DIRENT_ATTR_READ_ONLY + | FAT_DIRENT_ATTR_HIDDEN + | FAT_DIRENT_ATTR_SYSTEM + | FAT_DIRENT_ATTR_ARCHIVE, + TRUE, + NULL ); + + (*EaDirent)->FileSize = AllocationSize; + + // + // Initialize the Fcb for this file and initialize the + // cache map as well. + // + + // + // Start by computing the section size for the cache + // manager. + // + + SectionSize.QuadPart = (*EaDirent)->FileSize; + Vcb->EaFcb->Header.AllocationSize = SectionSize; + Vcb->EaFcb->Header.FileSize = SectionSize; + UnwindUpdatedSizes = TRUE; + + // + // Create and initialize the file object for the + // Ea virtual file. + // + + EaStreamFile = FatOpenEaFile( IrpContext, Vcb->EaFcb ); + + // + // Update the Fcb with information on the file size + // and disk location. Also increment the open/unclean + // counts in the EaFcb and the open count in the + // Vcb. + // + + { + LBO FirstLboOfFile; + + FatLookupMcbEntry( Vcb, &Vcb->EaFcb->Mcb, + 0, + &FirstLboOfFile, + NULL, + NULL ); + + // + // The discerning reader will note that this doesn't take + // FAT32 into account, which is of course intentional. + // + + (*EaDirent)->FirstClusterOfFile = + (USHORT) FatGetIndexFromLbo( Vcb, FirstLboOfFile ); + } + + Vcb->EaFcb->FirstClusterOfFile = (*EaDirent)->FirstClusterOfFile; + + // + // Initialize the Ea file header and mark the Bcb as dirty. + // + + FatPinEaRange( IrpContext, + EaStreamFile, + Vcb->EaFcb, + &EaFileRange, + 0, + AllocationSize, + STATUS_DATA_ERROR ); + + FileHeader = (PEA_FILE_HEADER) EaFileRange.Data; + + RtlZeroMemory( FileHeader, AllocationSize ); + FileHeader->Signature = EA_FILE_SIGNATURE; + + for (Index = MAX_EA_BASE_INDEX, CurrentIndex = FileHeader->EaBaseTable; + Index; + Index--, CurrentIndex++) { + + *CurrentIndex = AllocatedClusters; + } + + // + // Initialize the offset table with the offset set to + // after the just allocated clusters. + // + + for (Index = OffsetTableSize >> 1, + CurrentIndex = (PUSHORT) ((PUCHAR) FileHeader + sizeof( EA_FILE_HEADER )); + Index; + Index--, CurrentIndex++) { + + *CurrentIndex = UNUSED_EA_HANDLE; + } + + // + // Unpin the file header and offset table. + // + + FatMarkEaRangeDirty( IrpContext, EaStreamFile, &EaFileRange ); + FatUnpinEaRange( IrpContext, &EaFileRange ); + + CcFlushCache( EaStreamFile->SectionObjectPointer, NULL, 0, NULL ); + + // + // Return the Ea file object to the user. + // + + Vcb->VirtualEaFile = EaStreamFile; + } + } + try_exit: NOTHING; + } finally { + + DebugUnwind( FatGetEaFile ); + + // + // If this is abnormal termination and disk space has been + // allocated. We deallocate it now. + // + + if (AbnormalTermination()) { + + // + // Deallocate the Ea file + // + + if (UnwindAllocatedDiskSpace) { + + FatDeallocateDiskSpace( IrpContext, + Vcb, + &Vcb->EaFcb->Mcb, + FALSE ); + } + + // + // Delete the dirent for the Ea file, if created. + // + + if (UnwindEaDirentCreated) { + + if (UnwindUpdatedSizes) { + + Vcb->EaFcb->Header.AllocationSize.QuadPart = 0; + Vcb->EaFcb->Header.FileSize.QuadPart = 0; + } + + FatUnpinBcb( IrpContext, *EaBcb ); + FatDeleteDirent( IrpContext, Vcb->EaFcb, NULL, TRUE ); + } + + // + // Release the EA Fcb if held + // + + if (UnwindLockedEaFcb) { + + FatReleaseFcb( IrpContext, Vcb->EaFcb ); + } + + // + // Dereference the Ea stream file if created. + // + + if (EaStreamFile != NULL) { + + ObDereferenceObject( EaStreamFile ); + } + } + + // + // Always release the root Dcb (our caller releases the EA Fcb if we + // do not raise). + // + + if (UnwindLockedRootDcb) { + + FatReleaseFcb( IrpContext, Vcb->RootDcb ); + } + + // + // If the Ea file header is locked down. We unpin it now. + // + + FatUnpinEaRange( IrpContext, &EaFileRange ); + + DebugTrace(-1, Dbg, "FatGetEaFile: Exit\n", 0); + } + + return; +} + + +VOID +FatReadEaSet ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN USHORT EaHandle, + IN POEM_STRING FileName, + IN BOOLEAN ReturnEntireSet, + OUT PEA_RANGE EaSetRange + ) + +/*++ + +Routine Description: + + This routine pins the Ea set for the given ea handle within the + Ea stream file. The EaHandle, after first comparing against valid + index values, is used to compute the cluster offset for this + this Ea set. The Ea set is then verified as belonging to this + index and lying within the Ea data file. + + The caller of this function will have verified that the Ea file + exists and that the Vcb field points to an initialized cache file. + The caller will already have gained exclusive access to the + EaFcb. + +Arguments: + + Vcb - Supplies the Vcb for the volume. + + EaHandle - Supplies the handle for the Ea's to read. + + FileName - Name of the file whose Ea's are being read. + + ReturnEntireSet - Indicates if the caller needs the entire set + as opposed to just the header. + + EaSetRange - Pointer to the EaRange structure which will describe the Ea + on return. + +Return Value: + + None + +--*/ + +{ + ULONG BytesPerCluster = 1 << Vcb->AllocationSupport.LogOfBytesPerCluster; + + ULONG EaOffsetVbo; + EA_RANGE EaOffsetRange; + USHORT EaOffsetCluster; + + EA_RANGE EaHeaderRange; + PEA_FILE_HEADER EaHeader; + + ULONG EaSetVbo; + PEA_SET_HEADER EaSet; + + ULONG CbList; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( FileName ); + + DebugTrace(+1, Dbg, "FatReadEaSet\n", 0); + DebugTrace( 0, Dbg, " Vcb = %p\n", Vcb); + + // + // Verify that the Ea index has a legal value. Raise status + // STATUS_NONEXISTENT_EA_ENTRY if illegal. + // + + if (EaHandle < MIN_EA_HANDLE + || EaHandle > MAX_EA_HANDLE) { + + DebugTrace(-1, Dbg, "FatReadEaSet: Illegal handle value\n", 0); + FatRaiseStatus( IrpContext, STATUS_NONEXISTENT_EA_ENTRY ); + } + + // + // Verify that the virtual Ea file is large enough for us to read + // the EaOffet table for this index. + // + + EaOffsetVbo = sizeof( EA_FILE_HEADER ) + (((ULONGLONG)EaHandle >> 7) << 8); + + // + // Zero the Ea range structures. + // + + RtlZeroMemory( &EaHeaderRange, sizeof( EA_RANGE )); + RtlZeroMemory( &EaOffsetRange, sizeof( EA_RANGE )); + + // + // Use a try statement to clean up on exit. + // + + try { + + // + // Pin down the EA file header. + // + + FatPinEaRange( IrpContext, + Vcb->VirtualEaFile, + Vcb->EaFcb, + &EaHeaderRange, + 0, + sizeof( EA_FILE_HEADER ), + STATUS_NONEXISTENT_EA_ENTRY ); + + EaHeader = (PEA_FILE_HEADER) EaHeaderRange.Data; + + // + // Pin down the Ea offset table for the particular index. + // + + FatPinEaRange( IrpContext, + Vcb->VirtualEaFile, + Vcb->EaFcb, + &EaOffsetRange, + EaOffsetVbo, + sizeof( EA_OFF_TABLE ), + STATUS_NONEXISTENT_EA_ENTRY ); + + // + // Check if the specifific handle is currently being used. + // + + EaOffsetCluster = *((PUSHORT) EaOffsetRange.Data + + (EaHandle & (MAX_EA_OFFSET_INDEX - 1))); + + if (EaOffsetCluster == UNUSED_EA_HANDLE) { + + DebugTrace(0, Dbg, "FatReadEaSet: Ea handle is unused\n", 0); + FatRaiseStatus( IrpContext, STATUS_NONEXISTENT_EA_ENTRY ); + } + + // + // Compute the file offset for the Ea data. + // + + EaSetVbo = (EaHeader->EaBaseTable[EaHandle >> 7] + EaOffsetCluster) + << Vcb->AllocationSupport.LogOfBytesPerCluster; + + // + // Unpin the file header and offset table. + // + + FatUnpinEaRange( IrpContext, &EaHeaderRange ); + FatUnpinEaRange( IrpContext, &EaOffsetRange ); + + // + // Pin the ea set. + // + + FatPinEaRange( IrpContext, + Vcb->VirtualEaFile, + Vcb->EaFcb, + EaSetRange, + EaSetVbo, + BytesPerCluster, + STATUS_DATA_ERROR ); + + // + // Verify that the Ea set is valid and belongs to this index. + // Raise STATUS_DATA_ERROR if there is a data conflict. + // + + EaSet = (PEA_SET_HEADER) EaSetRange->Data; + + if (EaSet->Signature != EA_SET_SIGNATURE + || EaSet->OwnEaHandle != EaHandle ) { + + DebugTrace(0, Dbg, "FatReadEaSet: Ea set header is corrupt\n", 0); + FatRaiseStatus( IrpContext, STATUS_DATA_ERROR ); + } + + // + // At this point we have pinned a single cluster of Ea data. If + // this represents the entire Ea data for the Ea index, we are + // done. Otherwise we need to check on the entire size of + // of the Ea set header and whether it is contained in the allocated + // size of the Ea virtual file. At that point we can unpin + // the partial Ea set header and repin the entire header. + // + + CbList = GetcbList( EaSet ); + + if (ReturnEntireSet + && CbList > BytesPerCluster ) { + + // + // Round up to the cluster size. + // + + CbList = (CbList + EA_CBLIST_OFFSET + BytesPerCluster - 1) + & ~(BytesPerCluster - 1); + + FatUnpinEaRange( IrpContext, EaSetRange ); + + RtlZeroMemory( EaSetRange, sizeof( EA_RANGE )); + + FatPinEaRange( IrpContext, + Vcb->VirtualEaFile, + Vcb->EaFcb, + EaSetRange, + EaSetVbo, + CbList, + STATUS_DATA_ERROR ); + } + + } finally { + + DebugUnwind( FatReadEaSet ); + + // + // Unpin the Ea base and offset tables if locked down. + // + + FatUnpinEaRange( IrpContext, &EaHeaderRange ); + FatUnpinEaRange( IrpContext, &EaOffsetRange ); + + DebugTrace(-1, Dbg, "FatReadEaSet: Exit\n", 0); + } + + return; +} + + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatDeleteEaSet ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PBCB EaBcb, + OUT PDIRENT EaDirent, + IN USHORT EaHandle, + IN POEM_STRING FileName + ) + +/*++ + +Routine Description: + + This routines clips the Ea set for a particular index out of the + Ea file for a volume. The index is verified as belonging to a valid + handle. The clusters are removed and the Ea stream file along with + the Ea base and offset files are updated. + + The caller of this function will have verified that the Ea file + exists and that the Vcb field points to an initialized cache file. + The caller will already have gained exclusive access to the + EaFcb. + +Arguments: + + Vcb - Supplies the Vcb for the volume. + + VirtualEeFile - Pointer to the file object for the virtual Ea file. + + EaFcb - Supplies the pointer to the Fcb for the Ea file. + + EaBcb - Supplies a pointer to the Bcb for the Ea dirent. + + EaDirent - Supplies a pointer to the dirent for the Ea file. + + EaHandle - Supplies the handle for the Ea's to read. + + FileName - Name of the file whose Ea's are being read. + +Return Value: + + None. + +--*/ + +{ + ULONG BytesPerCluster = 1 << Vcb->AllocationSupport.LogOfBytesPerCluster; + ULONG CbList; + LARGE_INTEGER FileOffset; + + LARGE_MCB DataMcb; + BOOLEAN UnwindInitializeDataMcb = FALSE; + BOOLEAN UnwindSplitData = FALSE; + + LARGE_MCB TailMcb; + BOOLEAN UnwindInitializeTailMcb = FALSE; + BOOLEAN UnwindSplitTail = FALSE; + BOOLEAN UnwindMergeTail = FALSE; + + BOOLEAN UnwindModifiedEaHeader = FALSE; + BOOLEAN UnwindCacheValues = FALSE; + ULONG UnwindPrevFileSize = 0; + + ULONG EaOffsetVbo; + USHORT EaOffsetIndex; + EA_RANGE EaOffsetRange; + USHORT EaOffsetCluster; + + PFILE_OBJECT VirtualEaFile = Vcb->VirtualEaFile; + PFCB EaFcb = Vcb->EaFcb; + + EA_RANGE EaHeaderRange; + PEA_FILE_HEADER EaHeader; + USHORT EaHeaderBaseIndex; + + ULONG EaSetVbo = 0; + ULONG EaSetLength; + EA_RANGE EaSetRange; + PEA_SET_HEADER EaSet; + USHORT EaSetClusterCount; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( FileName ); + + // + // Verify that the Ea index has a legal value. Raise status + // STATUS_INVALID_HANDLE if illegal. + // + + if (EaHandle < MIN_EA_HANDLE + || EaHandle > MAX_EA_HANDLE) { + + DebugTrace(-1, Dbg, "FatDeleteEaSet: Illegal handle value\n", 0); + FatRaiseStatus( IrpContext, STATUS_NONEXISTENT_EA_ENTRY ); + } + + // + // Verify that the virtual Ea file is large enough for us to read + // the EaOffet table for this index. + // + + EaOffsetVbo = sizeof( EA_FILE_HEADER ) + (((ULONGLONG)EaHandle >> 7) << 8); + + // + // Zero the Ea range structures. + // + + RtlZeroMemory( &EaHeaderRange, sizeof( EA_RANGE )); + RtlZeroMemory( &EaOffsetRange, sizeof( EA_RANGE )); + RtlZeroMemory( &EaSetRange, sizeof( EA_RANGE )); + + // + // Use a try to facilitate cleanup. + // + + try { + + // + // Pin down the EA file header. + // + + FatPinEaRange( IrpContext, + VirtualEaFile, + EaFcb, + &EaHeaderRange, + 0, + sizeof( EA_FILE_HEADER ), + STATUS_NONEXISTENT_EA_ENTRY ); + + EaHeader = (PEA_FILE_HEADER) EaHeaderRange.Data; + + // + // Pin down the Ea offset table for the particular index. + // + + FatPinEaRange( IrpContext, + VirtualEaFile, + EaFcb, + &EaOffsetRange, + EaOffsetVbo, + sizeof( EA_OFF_TABLE ), + STATUS_NONEXISTENT_EA_ENTRY ); + + // + // Check if the specifific handle is currently being used. + // + + EaOffsetIndex = EaHandle & (MAX_EA_OFFSET_INDEX - 1); + EaOffsetCluster = *((PUSHORT) EaOffsetRange.Data + EaOffsetIndex); + + if (EaOffsetCluster == UNUSED_EA_HANDLE) { + + DebugTrace(0, Dbg, "FatReadEaSet: Ea handle is unused\n", 0); + FatRaiseStatus( IrpContext, STATUS_NONEXISTENT_EA_ENTRY ); + } + + // + // Compute the file offset for the Ea data. + // + + EaHeaderBaseIndex = EaHandle >> 7; + EaSetVbo = (EaHeader->EaBaseTable[EaHeaderBaseIndex] + EaOffsetCluster) + << Vcb->AllocationSupport.LogOfBytesPerCluster; + + // + // Unpin the file header and offset table. + // + + FatUnpinEaRange( IrpContext, &EaHeaderRange ); + FatUnpinEaRange( IrpContext, &EaOffsetRange ); + + // + // Try to pin the requested Ea set. + // + + FatPinEaRange( IrpContext, + VirtualEaFile, + EaFcb, + &EaSetRange, + EaSetVbo, + BytesPerCluster, + STATUS_DATA_ERROR ); + + EaSet = (PEA_SET_HEADER) EaSetRange.Data; + + if (EaSet->Signature != EA_SET_SIGNATURE + || EaSet->OwnEaHandle != EaHandle ) { + + DebugTrace(0, Dbg, "FatReadEaSet: Ea set header is corrupt\n", 0); + FatRaiseStatus( IrpContext, STATUS_DATA_ERROR ); + } + + // + // At this point we have pinned a single cluster of Ea data. If + // this represents the entire Ea data for the Ea index, we know + // the number of clusters to remove. Otherwise we need to check + // on the entire size of the Ea set header and whether it is + // contained in the allocated size of the Ea virtual file. At + // that point we unpin the partial Ea set header and remember the + // starting cluster offset and number of clusters in both cluster + // and Vbo formats. + // + // At that point the following variables have the described + // values. + // + // EaSetVbo - Vbo to start splice at. + // EaSetLength - Number of bytes to splice. + // EaSetClusterCount - Number of clusters to splice. + // + + CbList = GetcbList( EaSet ); + + EaSetClusterCount = (USHORT) ((CbList + EA_CBLIST_OFFSET + BytesPerCluster - 1) + >> Vcb->AllocationSupport.LogOfBytesPerCluster); + + EaSetLength = EaSetClusterCount << Vcb->AllocationSupport.LogOfBytesPerCluster; + + if (EaSetLength > BytesPerCluster) { + + if (EaFcb->Header.FileSize.LowPart - EaSetVbo < EaSetLength) { + + DebugTrace(0, Dbg, "FatDeleteEaSet: Full Ea set not contained in file\n", 0); + + FatRaiseStatus( IrpContext, STATUS_DATA_ERROR ); + } + } + + FatUnpinEaRange( IrpContext, &EaSetRange ); + + // + // Update the cache manager for this file. This is done by + // truncating to the point where the data was spliced and + // reinitializing with the modified size of the file. + // + // NOTE: Even if the all the EA's are removed the Ea file will + // always exist and the header area will never shrink. + // + + FileOffset.LowPart = EaSetVbo; + FileOffset.HighPart = 0; + + // + // Round the cache map down to a system page boundary. + // + + FileOffset.LowPart &= ~(PAGE_SIZE - 1); + + // + // Make sure all the data gets out to the disk. + // + + { + IO_STATUS_BLOCK Iosb; + ULONG PurgeCount = 5; + + while (--PurgeCount) { + + Iosb.Status = STATUS_SUCCESS; + + CcFlushCache( VirtualEaFile->SectionObjectPointer, + NULL, + 0, + &Iosb ); + + NT_ASSERT( Iosb.Status == STATUS_SUCCESS ); + + // + // We do not have to worry about a lazy writer firing in parallel + // with our CcFlushCache since we have the EaFcb exclusive. Thus + // we know all data is out. + // + + // + // We throw the unwanted pages out of the cache and then + // truncate the Ea File for the new size. + // + + if (CcPurgeCacheSection( VirtualEaFile->SectionObjectPointer, + &FileOffset, + 0, + FALSE )) { + + break; + } + } + + if (!PurgeCount) { + + FatRaiseStatus( IrpContext, STATUS_UNABLE_TO_DELETE_SECTION ); + } + } + + FileOffset.LowPart = EaFcb->Header.FileSize.LowPart - EaSetLength; + + // + // Perform the splice operation on the FAT chain. This is done + // by splitting the target clusters out and merging the remaining + // clusters around them. We can ignore the return value from + // the merge and splice functions because we are guaranteed + // to be able to block. + // + + { + FsRtlInitializeLargeMcb( &DataMcb, PagedPool ); + + UnwindInitializeDataMcb = TRUE; + + FatSplitAllocation( IrpContext, + Vcb, + &EaFcb->Mcb, + EaSetVbo, + &DataMcb ); + + UnwindSplitData = TRUE; + + if (EaSetLength + EaSetVbo != EaFcb->Header.FileSize.LowPart) { + + FsRtlInitializeLargeMcb( &TailMcb, PagedPool ); + + UnwindInitializeTailMcb = TRUE; + + FatSplitAllocation( IrpContext, + Vcb, + &DataMcb, + EaSetLength, + &TailMcb ); + + UnwindSplitTail = TRUE; + + FatMergeAllocation( IrpContext, + Vcb, + &EaFcb->Mcb, + &TailMcb ); + + UnwindMergeTail = TRUE; + } + } + + // + // Update the Fcb for the Ea file + // + + UnwindPrevFileSize = EaFcb->Header.FileSize.LowPart; + + (VOID)ExAcquireResourceExclusiveLite( EaFcb->Header.PagingIoResource, + TRUE ); + + EaFcb->Header.FileSize.LowPart = EaFcb->Header.FileSize.LowPart - EaSetLength; + EaFcb->Header.AllocationSize = EaFcb->Header.FileSize; + + + CcSetFileSizes( VirtualEaFile, + (PCC_FILE_SIZES)&EaFcb->Header.AllocationSize ); + + ExReleaseResourceLite( EaFcb->Header.PagingIoResource ); + + UnwindCacheValues = TRUE; + + EaDirent->FileSize = EaFcb->Header.FileSize.LowPart; + + FatSetDirtyBcb( IrpContext, EaBcb, Vcb, TRUE ); + + // + // Update the Ea base and offset tables. For the Ea base table, + // all subsequent index values must be decremented by the number + // of clusters removed. + // + // For the entries in the relevant Ea offset table, all entries + // after this index must also be decreased by the number of + // clusters removed. + // + + // + // Pin down the EA file header. + // + + RtlZeroMemory( &EaHeaderRange, + sizeof( EA_RANGE )); + + FatPinEaRange( IrpContext, + VirtualEaFile, + EaFcb, + &EaHeaderRange, + 0, + sizeof( EA_FILE_HEADER ), + STATUS_NONEXISTENT_EA_ENTRY ); + + EaHeader = (PEA_FILE_HEADER) EaHeaderRange.Data; + + // + // Pin down the Ea offset table for the particular index. + // + + RtlZeroMemory( &EaOffsetRange, + sizeof( EA_RANGE )); + + FatPinEaRange( IrpContext, + VirtualEaFile, + EaFcb, + &EaOffsetRange, + EaOffsetVbo, + sizeof( EA_OFF_TABLE ), + STATUS_NONEXISTENT_EA_ENTRY ); + + { + ULONG Count; + PUSHORT NextEaIndex; + + Count = MAX_EA_BASE_INDEX - EaHeaderBaseIndex - 1; + + NextEaIndex = &EaHeader->EaBaseTable[EaHeaderBaseIndex + 1]; + + while (Count--) { + + *(NextEaIndex++) -= EaSetClusterCount; + } + + FatMarkEaRangeDirty( IrpContext, VirtualEaFile, &EaHeaderRange ); + + Count = MAX_EA_OFFSET_INDEX - EaOffsetIndex - 1; + NextEaIndex = (PUSHORT) EaOffsetRange.Data + EaOffsetIndex; + + *(NextEaIndex++) = UNUSED_EA_HANDLE; + + while (Count--) { + + if (*NextEaIndex != UNUSED_EA_HANDLE) { + + *NextEaIndex -= EaSetClusterCount; + } + + NextEaIndex++; + } + + FatMarkEaRangeDirty( IrpContext, VirtualEaFile, &EaOffsetRange ); + } + + UnwindModifiedEaHeader = TRUE; + + // + // Deallocate the ea set removed + // + + FatDeallocateDiskSpace( IrpContext, + Vcb, + &DataMcb, + FALSE ); + + } finally { + + DebugUnwind( FatDeleteEaSet ); + + // + // Restore file if abnormal termination. + // + // If we have modified the ea file header we ignore this + // error. Otherwise we walk through the state variables. + // + + if (AbnormalTermination() + && !UnwindModifiedEaHeader) { + + // + // If we modified the Ea dirent or Fcb, recover the previous + // values. + // + + if (UnwindPrevFileSize) { + + EaFcb->Header.FileSize.LowPart = UnwindPrevFileSize; + EaFcb->Header.AllocationSize.LowPart = UnwindPrevFileSize; + EaDirent->FileSize = UnwindPrevFileSize; + + if (UnwindCacheValues) { + + CcSetFileSizes( VirtualEaFile, + (PCC_FILE_SIZES)&EaFcb->Header.AllocationSize ); + } + } + + // + // If we merged the tail with the + // ea file header. We split it out + // again. + // + + if (UnwindMergeTail) { + + FatSplitAllocation( IrpContext, + Vcb, + &EaFcb->Mcb, + EaSetVbo, + &TailMcb ); + } + + // + // If we split the tail off we merge the tail back + // with the ea data to remove. + // + + if (UnwindSplitTail) { + + FatMergeAllocation( IrpContext, + Vcb, + &DataMcb, + &TailMcb ); + } + + // + // If the ea set has been split out, we merge that + // cluster string back in the file. Otherwise we + // simply uninitialize the local Mcb. + // + + if (UnwindSplitData) { + + FatMergeAllocation( IrpContext, + Vcb, + &EaFcb->Mcb, + &DataMcb ); + } + } + + // + // Unpin any Bcb's still active. + // + + FatUnpinEaRange( IrpContext, &EaHeaderRange ); + FatUnpinEaRange( IrpContext, &EaOffsetRange ); + FatUnpinEaRange( IrpContext, &EaSetRange ); + + // + // Uninitialize any initialized Mcbs + // + + if (UnwindInitializeDataMcb) { + + FsRtlUninitializeLargeMcb( &DataMcb ); + } + + if (UnwindInitializeTailMcb) { + + FsRtlUninitializeLargeMcb( &TailMcb ); + } + + DebugTrace(-1, Dbg, "FatDeleteEaSet -> Exit\n", 0); + } + + return; +} + + + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatAddEaSet ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN ULONG EaSetLength, + IN PBCB EaBcb, + OUT PDIRENT EaDirent, + OUT PUSHORT EaHandle, + OUT PEA_RANGE EaSetRange + ) + +/*++ + +Routine Description: + + This routine will add the necessary clusters to support a new + Ea set of the given size. This is done by splicing a chain of + clusters into the existing Ea file. An Ea index is assigned to + this new chain and the Ea base and offset tables are updated to + include this new handle. This routine also pins the added + clusters and returns their address and a Bcb. + + The caller of this function will have verified that the Ea file + exists and that the Vcb field points to an initialized cache file. + The caller will already have gained exclusive access to the + EaFcb. + +Arguments: + + Vcb - Supplies the Vcb to fill in. + + EaSetLength - The number of bytes needed to contain the Ea set. This + routine will round this up the next cluster size. + + EaBcb - Supplies a pointer to the Bcb for the Ea dirent. + + EaDirent - Supplies a pointer to the dirent for the Ea file. + + EaHandle - Supplies the address to store the ea index generated here. + + EaSetRange - This is the structure that describes new range in the Ea file. + +Return Value: + + None. + +--*/ + +{ + ULONG BytesPerCluster = 1 << Vcb->AllocationSupport.LogOfBytesPerCluster; + + EA_RANGE EaHeaderRange; + USHORT EaHeaderIndex; + PEA_FILE_HEADER EaHeader; + + EA_RANGE EaOffsetRange; + ULONG EaNewOffsetVbo = 0; + USHORT EaOffsetIndex; + ULONG EaOffsetTableSize; + PUSHORT EaOffsetTable; + + ULONG EaSetClusterOffset; + ULONG EaSetVbo = 0; + USHORT EaSetClusterCount; + PEA_SET_HEADER EaSet; + + PFILE_OBJECT VirtualEaFile = Vcb->VirtualEaFile; + PFCB EaFcb = Vcb->EaFcb; + + LARGE_MCB EaSetMcb; + BOOLEAN UnwindInitializedEaSetMcb = FALSE; + BOOLEAN UnwindAllocatedNewAllocation = FALSE; + BOOLEAN UnwindMergedNewEaSet = FALSE; + + LARGE_MCB EaOffsetMcb; + BOOLEAN UnwindInitializedOffsetMcb = FALSE; + BOOLEAN UnwindSplitNewAllocation = FALSE; + BOOLEAN UnwindMergedNewOffset = FALSE; + + LARGE_MCB EaTailMcb; + BOOLEAN UnwindInitializedTailMcb = FALSE; + BOOLEAN UnwindSplitTail = FALSE; + BOOLEAN UnwindMergedTail = FALSE; + + LARGE_MCB EaInitialEaMcb; + BOOLEAN UnwindInitializedInitialEaMcb = FALSE; + BOOLEAN UnwindSplitInitialEa = FALSE; + BOOLEAN UnwindMergedInitialEa = FALSE; + + USHORT NewEaIndex; + PUSHORT NextEaOffset; + + ULONG NewAllocation; + LARGE_INTEGER FileOffset; + ULONG Count; + + ULONG UnwindPrevFileSize = 0; + BOOLEAN UnwindCacheValues = FALSE; + + BOOLEAN TailExists = FALSE; + BOOLEAN AddedOffsetTableCluster = FALSE; + BOOLEAN UnwindPurgeCacheMap = FALSE; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatAddEaSet\n", 0); + DebugTrace( 0, Dbg, " Vcb = %p\n", Vcb); + DebugTrace( 0, Dbg, " EaSetLength = %ul\n", EaSetLength ); + + // + // Zero the Ea range structures. + // + + RtlZeroMemory( &EaHeaderRange, sizeof( EA_RANGE )); + RtlZeroMemory( &EaOffsetRange, sizeof( EA_RANGE )); + + // + // Use a try statement to facilitate cleanup. + // + + try { + + // + // Pin down the file header. + // + + FatPinEaRange( IrpContext, + VirtualEaFile, + EaFcb, + &EaHeaderRange, + 0, + sizeof( EA_FILE_HEADER ), + STATUS_DATA_ERROR ); + + EaHeader = (PEA_FILE_HEADER) EaHeaderRange.Data; + + // + // Compute the size of the offset table. + // + + EaNewOffsetVbo = EaHeader->EaBaseTable[0] << Vcb->AllocationSupport.LogOfBytesPerCluster; + EaOffsetTableSize = EaNewOffsetVbo - sizeof( EA_FILE_HEADER ); + + // + // Pin down the entire offset table. + // + + FatPinEaRange( IrpContext, + VirtualEaFile, + EaFcb, + &EaOffsetRange, + sizeof( EA_FILE_HEADER ), + EaOffsetTableSize, + STATUS_DATA_ERROR ); + + // + // We now look for a valid handle out of the existing offset table. + // We start at the last entry and walk backwards. We stop at the + // first unused handle which is preceded by a used handle (or handle + // 1). + // + // As we walk backwards, we need to remember the file offset of the + // cluster which will follow the clusters we add. We initially + // remember the end of the file. If the end of the offset table + // consists of a string of used handles, we remember the offset of + // the handle prior to the transition from used to unused handles. + // + + EaSetClusterOffset = EaFcb->Header.FileSize.LowPart + >> Vcb->AllocationSupport.LogOfBytesPerCluster; + + NewEaIndex = (USHORT) ((EaOffsetTableSize >> 1) - 1); + + NextEaOffset = (PUSHORT) EaOffsetRange.Data + NewEaIndex; + + // + // Walk through the used handles at the end of the offset table. + // + + if (*NextEaOffset != UNUSED_EA_HANDLE) { + + while (NewEaIndex != 0) { + + if (*(NextEaOffset - 1) == UNUSED_EA_HANDLE) { + + // + // If the handle is 1, we take no action. Otherwise + // we save the cluster offset of the current handle + // knowing we will use a previous handle and insert + // a chain of clusters. + // + + if (NewEaIndex != 1) { + + EaSetClusterOffset = *NextEaOffset + + EaHeader->EaBaseTable[NewEaIndex >> 7]; + + TailExists = TRUE; + } + + NewEaIndex--; + NextEaOffset--; + + break; + } + + NewEaIndex--; + NextEaOffset--; + } + } + + // + // Walk through looking for the first unused handle in a string + // of unused handles. + // + + while (NewEaIndex) { + + if (*(NextEaOffset - 1) != UNUSED_EA_HANDLE) { + + break; + } + + NextEaOffset--; + NewEaIndex--; + } + + // + // If the handle is zero, we do a special test to see if handle 1 + // is available. Otherwise we will use the first handle of a new + // cluster. A non-zero handle now indicates that a handle was found + // in an existing offset table cluster. + // + + if (NewEaIndex == 0) { + + if (*(NextEaOffset + 1) == UNUSED_EA_HANDLE) { + + NewEaIndex = 1; + + } else { + + NewEaIndex = (USHORT) EaOffsetTableSize >> 1; + AddedOffsetTableCluster = TRUE; + } + } + + // + // If the Ea index is outside the legal range then raise an + // exception. + // + + if (NewEaIndex > MAX_EA_HANDLE) { + + DebugTrace(-1, Dbg, + "FatAddEaSet: Illegal handle value for new handle\n", 0); + + FatRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES ); + } + + // + // Compute the base and offset indexes. + // + + EaHeaderIndex = NewEaIndex >> 7; + EaOffsetIndex = NewEaIndex & (MAX_EA_OFFSET_INDEX - 1); + + // + // Compute the byte offset of the new ea data in the file. + // + + EaSetVbo = EaSetClusterOffset << Vcb->AllocationSupport.LogOfBytesPerCluster; + + // + // Allocate all the required disk space together to insure this + // operation is atomic. We don't want to allocate one block + // of disk space and then fail on a second allocation. + // + + EaSetLength = (EaSetLength + BytesPerCluster - 1) + & ~(BytesPerCluster - 1); + + NewAllocation = EaSetLength + + (AddedOffsetTableCluster ? BytesPerCluster : 0); + + // + // Verify that adding these clusters will not grow the Ea file + // beyond its legal value. The maximum number of clusters is + // 2^16 since the Ea sets are referenced by a 16 bit cluster + // offset value. + // + + if ((ULONG) ((0x0000FFFF << Vcb->AllocationSupport.LogOfBytesPerCluster) + - EaFcb->Header.FileSize.LowPart) + < NewAllocation) { + + DebugTrace(-1, Dbg, + "FatAddEaSet: New Ea file size is too large\n", 0); + + FatRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES ); + } + + FsRtlInitializeLargeMcb( &EaSetMcb, PagedPool ); + + UnwindInitializedEaSetMcb = TRUE; + + FatAllocateDiskSpace( IrpContext, + Vcb, + 0, + &NewAllocation, + FALSE, + &EaSetMcb ); + + UnwindAllocatedNewAllocation = TRUE; + + EaSetClusterCount = (USHORT) (EaSetLength >> Vcb->AllocationSupport.LogOfBytesPerCluster); + + if (AddedOffsetTableCluster) { + + FsRtlInitializeLargeMcb( &EaOffsetMcb, PagedPool ); + + UnwindInitializedOffsetMcb = TRUE; + + FatSplitAllocation( IrpContext, + Vcb, + &EaSetMcb, + EaSetLength, + &EaOffsetMcb ); + + UnwindSplitNewAllocation = TRUE; + } + + FatUnpinEaRange( IrpContext, &EaHeaderRange ); + FatUnpinEaRange( IrpContext, &EaOffsetRange ); + + if (AddedOffsetTableCluster) { + + FileOffset.LowPart = EaNewOffsetVbo; + + } else { + + FileOffset.LowPart = EaSetVbo; + } + + FileOffset.HighPart = 0; + + // + // Round the cache map down to a system page boundary. + // + + FileOffset.LowPart &= ~(PAGE_SIZE - 1); + + { + IO_STATUS_BLOCK Iosb; + ULONG PurgeCount = 5; + + while (--PurgeCount) { + + Iosb.Status = STATUS_SUCCESS; + + CcFlushCache( VirtualEaFile->SectionObjectPointer, + NULL, + 0, + &Iosb ); + + NT_ASSERT( Iosb.Status == STATUS_SUCCESS ); + + // + // We do not have to worry about a lazy writer firing in parallel + // with our CcFlushCache since we have the EaFcb exclusive. Thus + // we know all data is out. + // + + // + // We throw the unwanted pages out of the cache and then + // truncate the Ea File for the new size. + // + // + + if (CcPurgeCacheSection( VirtualEaFile->SectionObjectPointer, + &FileOffset, + 0, + FALSE )) { + + break; + } + } + + if (!PurgeCount) { + + FatRaiseStatus( IrpContext, STATUS_UNABLE_TO_DELETE_SECTION ); + } + } + + UnwindPurgeCacheMap = TRUE; + + FileOffset.LowPart = EaFcb->Header.FileSize.LowPart + NewAllocation; + + // + // If there is a tail to the file, then we initialize an Mcb + // for the file section and split the tail from the file. + // + + if (TailExists) { + + FsRtlInitializeLargeMcb( &EaTailMcb, PagedPool ); + + UnwindInitializedTailMcb = TRUE; + + FatSplitAllocation( IrpContext, + Vcb, + &EaFcb->Mcb, + EaSetVbo, + &EaTailMcb ); + + UnwindSplitTail = TRUE; + } + + // + // If there is an initial section of ea data, we initialize an + // Mcb for that section. + // + + if (AddedOffsetTableCluster + && EaSetVbo != EaNewOffsetVbo) { + + FsRtlInitializeLargeMcb( &EaInitialEaMcb, PagedPool ); + + UnwindInitializedInitialEaMcb = TRUE; + + FatSplitAllocation( IrpContext, + Vcb, + &EaFcb->Mcb, + EaNewOffsetVbo, + &EaInitialEaMcb ); + + UnwindSplitInitialEa = TRUE; + } + + // + // We have now split the new file allocation into the new + // ea set and possibly a new offset table. + // + // We have also split the existing file data into a file + // header, an initial section of ea data and the tail of the + // file. These last 2 may not exist. + // + // Each section is described by an Mcb. + // + + // + // Merge the new offset information if it exists. + // + + if (AddedOffsetTableCluster) { + + FatMergeAllocation( IrpContext, + Vcb, + &EaFcb->Mcb, + &EaOffsetMcb ); + + FsRtlUninitializeLargeMcb( &EaOffsetMcb ); + FsRtlInitializeLargeMcb( &EaOffsetMcb, PagedPool ); + + UnwindMergedNewOffset = TRUE; + } + + // + // Merge the existing initial ea data if it exists. + // + + if (UnwindInitializedInitialEaMcb) { + + FatMergeAllocation( IrpContext, + Vcb, + &EaFcb->Mcb, + &EaInitialEaMcb ); + + FsRtlUninitializeLargeMcb( &EaInitialEaMcb ); + FsRtlInitializeLargeMcb( &EaInitialEaMcb, PagedPool ); + + UnwindMergedInitialEa = TRUE; + } + + // + // We modify the offset of the new ea set by one cluster if + // we added one to the offset table. + // + + if (AddedOffsetTableCluster) { + + EaSetClusterOffset += 1; + EaSetVbo += BytesPerCluster; + } + + // + // Merge the new ea set. + // + + FatMergeAllocation( IrpContext, + Vcb, + &EaFcb->Mcb, + &EaSetMcb ); + + FsRtlUninitializeLargeMcb( &EaSetMcb ); + FsRtlInitializeLargeMcb( &EaSetMcb, PagedPool ); + + UnwindMergedNewEaSet = TRUE; + + // + // Merge the tail if it exists. + // + + if (UnwindInitializedTailMcb) { + + FatMergeAllocation( IrpContext, + Vcb, + &EaFcb->Mcb, + &EaTailMcb ); + + FsRtlUninitializeLargeMcb( &EaTailMcb ); + FsRtlInitializeLargeMcb( &EaTailMcb, PagedPool ); + + UnwindMergedTail = TRUE; + } + + // + // If we added a new cluster for the offset table, we need to + // lock the entire cluster down and initialize all the handles to + // the unused state except the first one. + // + + // + // Update the Fcb information. + // + + UnwindPrevFileSize = EaFcb->Header.FileSize.LowPart; + + EaFcb->Header.FileSize.LowPart += NewAllocation; + EaFcb->Header.AllocationSize = EaFcb->Header.FileSize; + EaDirent->FileSize = EaFcb->Header.FileSize.LowPart; + + FatSetDirtyBcb( IrpContext, EaBcb, Vcb, TRUE ); + + // + // Let Mm and Cc know the new file sizes. + // + + CcSetFileSizes( VirtualEaFile, + (PCC_FILE_SIZES)&EaFcb->Header.AllocationSize ); + + UnwindCacheValues = TRUE; + + // + // Pin down the file header. + // + + RtlZeroMemory( &EaHeaderRange, sizeof( EA_RANGE )); + + FatPinEaRange( IrpContext, + VirtualEaFile, + EaFcb, + &EaHeaderRange, + 0, + sizeof( EA_FILE_HEADER ), + STATUS_DATA_ERROR ); + + EaHeader = (PEA_FILE_HEADER) EaHeaderRange.Data; + + // + // Pin down the entire offset table. + // + + + RtlZeroMemory( &EaOffsetRange, sizeof( EA_RANGE )); + + FatPinEaRange( IrpContext, + VirtualEaFile, + EaFcb, + &EaOffsetRange, + sizeof( EA_FILE_HEADER ) + (((ULONGLONG)NewEaIndex >> 7) << 8), + sizeof( EA_OFF_TABLE ), + STATUS_DATA_ERROR ); + + EaOffsetTable = (PUSHORT) EaOffsetRange.Data; + + // + // Pin the Ea set header for the added clusters and initialize + // the fields of interest. These are the signature field, the + // owning handle field, the need Ea field and the cbList field. + // Also mark the data as dirty. + // + + // + // Pin the ea set. + // + + FatPinEaRange( IrpContext, + VirtualEaFile, + EaFcb, + EaSetRange, + EaSetVbo, + EaSetLength, + STATUS_DATA_ERROR ); + + EaSet = (PEA_SET_HEADER) EaSetRange->Data; + + EaSet->Signature = EA_SET_SIGNATURE; + EaSet->OwnEaHandle = NewEaIndex; + + FatMarkEaRangeDirty( IrpContext, VirtualEaFile, EaSetRange ); + + // + // Update the Ea base and offset tables. For the Ea base table, + // all subsequent index values must be incremented by the number + // of clusters added. + // + // For the entries in the relevant Ea offset table, all entries + // after this index must also be increased by the number of + // clusters added. + // + // If we added another cluster to the offset table, then we increment + // all the base table values by 1. + // + + Count = MAX_EA_BASE_INDEX - EaHeaderIndex - 1; + + NextEaOffset = &EaHeader->EaBaseTable[EaHeaderIndex + 1]; + + while (Count--) { + + *(NextEaOffset++) += EaSetClusterCount; + } + + if (AddedOffsetTableCluster) { + + Count = MAX_EA_BASE_INDEX; + + NextEaOffset = &EaHeader->EaBaseTable[0]; + + while (Count--) { + + *(NextEaOffset++) += 1; + } + } + + FatMarkEaRangeDirty( IrpContext, VirtualEaFile, &EaHeaderRange ); + + // + // If we added an offset table cluster, we need to initialize + // the handles to unused. + // + + if (AddedOffsetTableCluster) { + + Count = (BytesPerCluster >> 1) - 1; + NextEaOffset = EaOffsetTable; + + *NextEaOffset++ = 0; + + while (Count--) { + + *NextEaOffset++ = UNUSED_EA_HANDLE; + } + } + + // + // We need to compute the offset of the added Ea set clusters + // from their base. + // + + NextEaOffset = EaOffsetTable + EaOffsetIndex; + + *NextEaOffset++ = (USHORT) (EaSetClusterOffset + - EaHeader->EaBaseTable[EaHeaderIndex]); + + Count = MAX_EA_OFFSET_INDEX - EaOffsetIndex - 1; + + while (Count--) { + + if (*NextEaOffset != UNUSED_EA_HANDLE) { + + *NextEaOffset += EaSetClusterCount; + } + + NextEaOffset++; + } + + FatMarkEaRangeDirty( IrpContext, VirtualEaFile, &EaOffsetRange ); + + // + // Update the callers parameters. + // + + *EaHandle = NewEaIndex; + + DebugTrace(0, Dbg, "FatAddEaSet: Return values\n", 0); + + DebugTrace(0, Dbg, "FatAddEaSet: New Handle -> %x\n", + *EaHandle); + + } finally { + + DebugUnwind( FatAddEaSet ); + + // + // Handle cleanup for abnormal termination only if we allocated + // disk space for the new ea set. + // + + if (AbnormalTermination() && UnwindAllocatedNewAllocation) { + + // + // If we modified the Ea dirent or Fcb, recover the previous + // values. Even though we are decreasing FileSize here, we + // don't need to synchronize to synchronize with paging Io + // because there was no dirty data generated in the new allocation. + // + + if (UnwindPrevFileSize) { + + EaFcb->Header.FileSize.LowPart = UnwindPrevFileSize; + EaFcb->Header.AllocationSize.LowPart = UnwindPrevFileSize; + EaDirent->FileSize = UnwindPrevFileSize; + + if (UnwindCacheValues) { + + CcSetFileSizes( VirtualEaFile, + (PCC_FILE_SIZES)&EaFcb->Header.AllocationSize ); + } + } + + // + // If we merged the tail then split it off. + // + + if (UnwindMergedTail) { + + VBO NewTailPosition; + + NewTailPosition = EaSetVbo + EaSetLength; + + FatSplitAllocation( IrpContext, + Vcb, + &EaFcb->Mcb, + NewTailPosition, + &EaTailMcb ); + } + + // + // If we merged the new ea data then split it out. + // + + if (UnwindMergedNewEaSet) { + + FatSplitAllocation( IrpContext, + Vcb, + &EaFcb->Mcb, + EaSetVbo, + &EaSetMcb ); + } + + // + // If we merged the initial ea data then split it out. + // + + if (UnwindMergedInitialEa) { + + FatSplitAllocation( IrpContext, + Vcb, + &EaFcb->Mcb, + EaNewOffsetVbo + BytesPerCluster, + &EaInitialEaMcb ); + } + + // + // If we added a new offset cluster, then split it out. + // + + if (UnwindMergedNewOffset) { + + FatSplitAllocation( IrpContext, + Vcb, + &EaFcb->Mcb, + EaNewOffsetVbo, + &EaOffsetMcb ); + } + + // + // If there is an initial ea section prior to the new section, merge + // it with the rest of the file. + // + + if (UnwindSplitInitialEa) { + + FatMergeAllocation( IrpContext, Vcb, &EaFcb->Mcb, &EaInitialEaMcb ); + } + + // + // If there is a file tail split off, merge it with the + // rest of the file. + // + + if (UnwindSplitTail) { + + FatMergeAllocation( IrpContext, Vcb, &EaFcb->Mcb, &EaTailMcb ); + } + + // + // If we modified the cache initialization for the ea file, + // then throw away the ea file object. + // + + if (UnwindPurgeCacheMap) { + + Vcb->VirtualEaFile = NULL; + ObDereferenceObject( VirtualEaFile ); + } + + // + // If we split the allocation, then deallocate the block for + // the new offset information. + // + + if (UnwindSplitNewAllocation) { + + FatDeallocateDiskSpace( IrpContext, Vcb, &EaOffsetMcb, FALSE ); + } + + // + // Deallocate the disk space. + // + + FatDeallocateDiskSpace( IrpContext, Vcb, &EaSetMcb, FALSE ); + } + + // + // Unpin the Ea ranges. + // + + FatUnpinEaRange( IrpContext, &EaHeaderRange ); + FatUnpinEaRange( IrpContext, &EaOffsetRange ); + + // + // Uninitialize any local Mcbs + // + + if (UnwindInitializedEaSetMcb) { + + FsRtlUninitializeLargeMcb( &EaSetMcb ); + } + + if (UnwindInitializedOffsetMcb) { + + FsRtlUninitializeLargeMcb( &EaOffsetMcb ); + } + + if (UnwindInitializedTailMcb) { + + FsRtlUninitializeLargeMcb( &EaTailMcb ); + } + + if (UnwindInitializedInitialEaMcb) { + + FsRtlUninitializeLargeMcb( &EaInitialEaMcb ); + } + + DebugTrace(-1, Dbg, "FatAddEaSet -> Exit\n", 0); + } + + return; +} + + +VOID +FatAppendPackedEa ( + IN PIRP_CONTEXT IrpContext, + IN OUT PEA_SET_HEADER *EaSetHeader, + IN OUT PULONG PackedEasLength, + IN OUT PULONG AllocationLength, + IN PFILE_FULL_EA_INFORMATION FullEa, + IN ULONG BytesPerCluster + ) + +/*++ + +Routine Description: + + This routine appends a new packed ea onto an existing packed ea list, + it also will allocate/dealloate pool as necessary to hold the ea list. + +Arguments: + + EaSetHeader - Supplies the address to store the pointer to pool memory + which contains the Ea list for a file. + + PackedEasLength - Supplies the length of the actual Ea data. The + new Ea data will be appended at this point. + + AllocationLength - Supplies the allocated length available for Ea + data. + + FullEa - Supplies a pointer to the new full ea that is to be appended + (in packed form) to the packed ea list. + + BytesPerCluster - Number of bytes per cluster on this volume. + + NOTE: The EaSetHeader refers to the entire block of Ea data for a + file. This includes the Ea's and their values as well as the + header information. The PackedEasLength and AllocationLength + parameters refer to the name/value pairs only. + +Return Value: + + None. + +--*/ + +{ + ULONG PackedEaSize; + PPACKED_EA ThisPackedEa; + OEM_STRING EaName; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatAppendPackedEa...\n", 0); + + // + // As a quick check see if the computed packed ea size plus the + // current packed ea list size will overflow the buffer. Full Ea and + // packed Ea only differ by 4 in their size + // + + PackedEaSize = SizeOfFullEa( FullEa ) - 4; + + if ( PackedEaSize + *PackedEasLength > *AllocationLength ) { + + // + // We will overflow our current work buffer so allocate a larger + // one and copy over the current buffer + // + + PVOID Temp; + ULONG NewAllocationSize; + ULONG OldAllocationSize; + + DebugTrace(0, Dbg, "Allocate a new ea list buffer\n", 0); + + // + // Compute a new size and allocate space. Always increase the + // allocation in cluster increments. + // + + NewAllocationSize = (SIZE_OF_EA_SET_HEADER + + PackedEaSize + + *PackedEasLength + + BytesPerCluster - 1) + & ~(BytesPerCluster - 1); + + Temp = FsRtlAllocatePoolWithTag( PagedPool, + NewAllocationSize, + TAG_EA_SET_HEADER ); + + // + // Move over the existing ea list, and deallocate the old one + // + + RtlCopyMemory( Temp, + *EaSetHeader, + OldAllocationSize = *AllocationLength + + SIZE_OF_EA_SET_HEADER ); + + ExFreePool( *EaSetHeader ); + + // + // Set up so we will use the new packed ea list + // + + *EaSetHeader = Temp; + + // + // Zero out the added memory. + // + + RtlZeroMemory( &(*EaSetHeader)->PackedEas[*AllocationLength], + NewAllocationSize - OldAllocationSize ); + + *AllocationLength = NewAllocationSize - SIZE_OF_EA_SET_HEADER; + } + + // + // Determine if we need to increment our need ea changes count + // + + if ( FlagOn(FullEa->Flags, FILE_NEED_EA )) { + + // + // The NeedEaCount field is long aligned so we will write + // directly to it. + // + + (*EaSetHeader)->NeedEaCount++; + } + + // + // Now copy over the ea, full ea's and packed ea are identical except + // that full ea also have a next ea offset that we skip over + // + // Before: + // UsedSize Allocated + // | | + // V V + // +xxxxxxxx+-----------------------------+ + // + // After: + // UsedSize Allocated + // | | + // V V + // +xxxxxxxx+yyyyyyyyyyyyyyyy+------------+ + // + + ThisPackedEa = (PPACKED_EA) (RtlOffsetToPointer( (*EaSetHeader)->PackedEas, + *PackedEasLength )); + + RtlCopyMemory( ThisPackedEa, + (PUCHAR) FullEa + 4, + PackedEaSize ); + + // + // Now convert the name to uppercase. + // + + EaName.MaximumLength = EaName.Length = FullEa->EaNameLength; + EaName.Buffer = ThisPackedEa->EaName; + + FatUpcaseEaName( IrpContext, &EaName, &EaName ); + + // + // Increment the used size in the packed ea list structure + // + + *PackedEasLength += PackedEaSize; + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "FatAppendPackedEa -> VOID\n", 0); + + UNREFERENCED_PARAMETER( IrpContext ); + + return; +} + + +VOID +FatDeletePackedEa ( + IN PIRP_CONTEXT IrpContext, + IN OUT PEA_SET_HEADER EaSetHeader, + IN OUT PULONG PackedEasLength, + IN ULONG Offset + ) + +/*++ + +Routine Description: + + This routine deletes an individual packed ea from the supplied + packed ea list. + +Arguments: + + EaSetHeader - Supplies the address to store the pointer to pool memory + which contains the Ea list for a file. + + PackedEasLength - Supplies the length of the actual Ea data. The + new Ea data will be appended at this point. + + Offset - Supplies the offset to the individual ea in the list to delete + + NOTE: The EaSetHeader refers to the entire block of Ea data for a + file. This includes the Ea's and their values as well as the + header information. The PackedEasLength parameter refer to the + name/value pairs only. + +Return Value: + + None. + +--*/ + +{ + PPACKED_EA PackedEa; + ULONG PackedEaSize; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatDeletePackedEa, Offset = %08lx\n", Offset); + + // + // Get a reference to the packed ea and figure out its size + // + + PackedEa = (PPACKED_EA) (&EaSetHeader->PackedEas[Offset]); + + SizeOfPackedEa( PackedEa, &PackedEaSize ); + + // + // Determine if we need to decrement our need ea changes count + // + + if (FlagOn(PackedEa->Flags, EA_NEED_EA_FLAG)) { + + EaSetHeader->NeedEaCount--; + } + + // + // Shrink the ea list over the deleted ea. The amount to copy is the + // total size of the ea list minus the offset to the end of the ea + // we're deleting. + // + // Before: + // Offset Offset+PackedEaSize UsedSize Allocated + // | | | | + // V V V V + // +xxxxxxxx+yyyyyyyyyyyyyyyy+zzzzzzzzzzzzzzzzzz+------------+ + // + // After + // Offset UsedSize Allocated + // | | | + // V V V + // +xxxxxxxx+zzzzzzzzzzzzzzzzzz+-----------------------------+ + // + + RtlCopyMemory( PackedEa, + (PUCHAR) PackedEa + PackedEaSize, + *PackedEasLength - (Offset + PackedEaSize) ); + + // + // And zero out the remaing part of the ea list, to make things + // nice and more robust + // + + RtlZeroMemory( &EaSetHeader->PackedEas[*PackedEasLength - PackedEaSize], + PackedEaSize ); + + // + // Decrement the used size by the amount we just removed + // + + *PackedEasLength -= PackedEaSize; + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "FatDeletePackedEa -> VOID\n", 0); + + UNREFERENCED_PARAMETER( IrpContext ); + + return; +} + + +ULONG +FatLocateNextEa ( + IN PIRP_CONTEXT IrpContext, + IN PPACKED_EA FirstPackedEa, + IN ULONG PackedEasLength, + IN ULONG PreviousOffset + ) + +/*++ + +Routine Description: + + This routine locates the offset for the next individual packed ea + inside of a packed ea list, given the offset to a previous Ea. + Instead of returing boolean to indicate if we've found the next one + we let the return offset be so large that it overuns the used size + of the packed ea list, and that way it's an easy construct to use + in a for loop. + +Arguments: + + FirstPackedEa - Supplies a pointer to the packed ea list structure + + PackedEasLength - Supplies the length of the packed ea list + + PreviousOffset - Supplies the offset to a individual packed ea in the + list + +Return Value: + + ULONG - The offset to the next ea in the list or 0xffffffff of one + does not exist. + +--*/ + +{ + PPACKED_EA PackedEa; + ULONG PackedEaSize; + ULONG Offset; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatLocateNextEa, PreviousOffset = %08lx\n", + PreviousOffset); + + // + // Make sure the previous offset is within the used size range + // + + if ( PreviousOffset >= PackedEasLength ) { + + DebugTrace(-1, Dbg, "FatLocateNextEa -> 0xffffffff\n", 0); + return 0xffffffff; + } + + // + // Get a reference to the previous packed ea, and compute its size + // + + PackedEa = (PPACKED_EA) ((PUCHAR) FirstPackedEa + PreviousOffset ); + SizeOfPackedEa( PackedEa, &PackedEaSize ); + + // + // Compute to the next ea + // + + Offset = PreviousOffset + PackedEaSize; + + // + // Now, if the new offset is beyond the ea size then we know + // that there isn't one so, we return an offset of 0xffffffff. + // otherwise we'll leave the new offset alone. + // + + if ( Offset >= PackedEasLength ) { + + Offset = 0xffffffff; + } + + DebugTrace(-1, Dbg, "FatLocateNextEa -> %08lx\n", Offset); + + UNREFERENCED_PARAMETER( IrpContext ); + + return Offset; +} + + +BOOLEAN +FatLocateEaByName ( + IN PIRP_CONTEXT IrpContext, + IN PPACKED_EA FirstPackedEa, + IN ULONG PackedEasLength, + IN POEM_STRING EaName, + OUT PULONG Offset + ) + +/*++ + +Routine Description: + + This routine locates the offset for the next individual packed ea + inside of a packed ea list, given the name of the ea to locate + +Arguments: + + FirstPackedEa - Supplies a pointer to the packed ea list structure + + PackedEasLength - Supplies the length of the packed ea list + + EaName - Supplies the name of the ea search for + + Offset - Receives the offset to the located individual ea in the list + if one exists. + +Return Value: + + BOOLEAN - TRUE if the named packed ea exists in the list and FALSE + otherwise. + +--*/ + +{ + PPACKED_EA PackedEa; + OEM_STRING Name; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatLocateEaByName, EaName = %Z\n", EaName); + + // + // For each packed ea in the list check its name against the + // ea name we're searching for + // + + for ( *Offset = 0; + *Offset < PackedEasLength; + *Offset = FatLocateNextEa( IrpContext, + FirstPackedEa, + PackedEasLength, + *Offset )) { + + // + // Reference the packed ea and get a string to its name + // + + PackedEa = (PPACKED_EA) ((PUCHAR) FirstPackedEa + *Offset); + + Name.Buffer = &PackedEa->EaName[0]; + Name.Length = PackedEa->EaNameLength; + Name.MaximumLength = PackedEa->EaNameLength; + + // + // Compare the two strings, if they are equal then we've + // found the caller's ea + // + + if ( RtlCompareString( EaName, &Name, TRUE ) == 0 ) { + + DebugTrace(-1, Dbg, "FatLocateEaByName -> TRUE, *Offset = %08lx\n", *Offset); + return TRUE; + } + } + + // + // We've exhausted the ea list without finding a match so return false + // + + DebugTrace(-1, Dbg, "FatLocateEaByName -> FALSE\n", 0); + return FALSE; +} + + +BOOLEAN +FatIsEaNameValid ( + IN PIRP_CONTEXT IrpContext, + IN OEM_STRING Name + ) + +/*++ + +Routine Description: + + This routine simple returns whether the specified file names conforms + to the file system specific rules for legal Ea names. + + For Ea names, the following rules apply: + + A. An Ea name may not contain any of the following characters: + + 0x0000 - 0x001F \ / : * ? " < > | , + = [ ] ; + +Arguments: + + Name - Supllies the name to check. + +Return Value: + + BOOLEAN - TRUE if the name is legal, FALSE otherwise. + +--*/ + +{ + ULONG Index; + + UCHAR Char; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( IrpContext ); + + // + // Empty names are not valid. + // + + if ( Name.Length == 0 ) { return FALSE; } + + // + // At this point we should only have a single name, which can't have + // more than 254 characters + // + + if ( Name.Length > 254 ) { return FALSE; } + + for ( Index = 0; Index < (ULONG)Name.Length; Index += 1 ) { + + Char = Name.Buffer[ Index ]; + + // + // Skip over and Dbcs chacters + // + + if ( FsRtlIsLeadDbcsCharacter( Char ) ) { + + NT_ASSERT( Index != (ULONG)(Name.Length - 1) ); + + Index += 1; + + continue; + } + + // + // Make sure this character is legal, and if a wild card, that + // wild cards are permissible. + // + + if ( !FsRtlIsAnsiCharacterLegalFat(Char, FALSE) ) { + + return FALSE; + } + } + + return TRUE; +} + + +VOID +FatPinEaRange ( + IN PIRP_CONTEXT IrpContext, + IN PFILE_OBJECT VirtualEaFile, + IN PFCB EaFcb, + IN OUT PEA_RANGE EaRange, + IN ULONG StartingVbo, + IN ULONG Length, + IN NTSTATUS ErrorStatus + ) + +/*++ + +Routine Description: + + This routine is called to pin a range within the Ea file. It will follow all the + rules required by the cache manager so that we don't have overlapping pin operations. + If the range being pinned spans a section then the desired data will be copied into + an auxilary buffer. FatMarkEaRangeDirty will know whether to copy the data back + into the cache or whether to simply mark the pinned data dirty. + +Arguments: + + VirtualEaFile - This is the stream file for the Ea file. + + EaFcb - This is the Fcb for the Ea file. + + EaRange - This is the Ea range structure for this request. + + StartingVbo - This is the starting offset in the Ea file to read from. + + Length - This is the length of the read. + + ErrorStatus - This is the error status to use if we are reading outside + of the file. + +Return Value: + + None. + +--*/ + +{ + LARGE_INTEGER LargeVbo; + ULONG ByteCount; + PBCB *NextBcb; + PVOID Buffer; + PCHAR DestinationBuffer = NULL; + BOOLEAN FirstPage = TRUE; + + PAGED_CODE(); + + // + // Verify that the entire read is contained within the Ea file. + // + + if (Length == 0 + || StartingVbo >= EaFcb->Header.AllocationSize.LowPart + || (EaFcb->Header.AllocationSize.LowPart - StartingVbo) < Length) { + + FatRaiseStatus( IrpContext, ErrorStatus ); + } + + // + // If the read will span a section, the system addresses may not be contiguous. + // Allocate a separate buffer in this case. + // + + if (((StartingVbo & (EA_SECTION_SIZE - 1)) + Length) > EA_SECTION_SIZE) { + + EaRange->Data = FsRtlAllocatePoolWithTag( PagedPool, + Length, + TAG_EA_DATA ); + EaRange->AuxilaryBuffer = TRUE; + + DestinationBuffer = EaRange->Data; + + } else { + + // + // PREfix correctly notes that if we don't decide here to have an aux buffer + // and the flag is up in the EaRange, we'll party on random memory since + // DestinationBuffer won't be set; however, this will never happen due to + // initialization of ea ranges and the cleanup in UnpinEaRange. + // + + NT_ASSERT( EaRange->AuxilaryBuffer == FALSE ); + } + + + // + // If the read will require more pages than our structure will hold then + // allocate an auxilary buffer. We have to figure the number of pages + // being requested so we have to include the page offset of the first page of + // the request. + // + + EaRange->BcbChainLength = (USHORT) (((StartingVbo & (PAGE_SIZE - 1)) + Length + PAGE_SIZE - 1) / PAGE_SIZE); + + if (EaRange->BcbChainLength > EA_BCB_ARRAY_SIZE) { + + EaRange->BcbChain = FsRtlAllocatePoolWithTag( PagedPool, + sizeof( PBCB ) * EaRange->BcbChainLength, + TAG_BCB ); + + RtlZeroMemory( EaRange->BcbChain, sizeof( PBCB ) * EaRange->BcbChainLength ); + + } else { + + EaRange->BcbChain = (PBCB *) &EaRange->BcbArray; + } + + // + // Store the byte range data in the Ea Range structure. + // + + EaRange->StartingVbo = StartingVbo; + EaRange->Length = Length; + + // + // Compute the initial pin length. + // + + ByteCount = PAGE_SIZE - (StartingVbo & (PAGE_SIZE - 1)); + + // + // For each page in the range; pin the page and update the Bcb count, copy to + // the auxiliary buffer. + // + + NextBcb = EaRange->BcbChain; + + while (Length != 0) { + + // + // Pin the page and remember the data start. + // + + LargeVbo.QuadPart = StartingVbo; + + if (ByteCount > Length) { + + ByteCount = Length; + } + + if (!CcPinRead( VirtualEaFile, + &LargeVbo, + ByteCount, + BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT), + NextBcb, + &Buffer )) { + + // + // Could not read the data without waiting (cache miss). + // + + FatRaiseStatus( IrpContext, STATUS_CANT_WAIT ); + } + + // + // Increment the Bcb pointer and copy to the auxilary buffer if necessary. + // + + NextBcb += 1; + + if (EaRange->AuxilaryBuffer == TRUE) { + + RtlCopyMemory( DestinationBuffer, + Buffer, + ByteCount ); + + DestinationBuffer = (PCHAR) Add2Ptr( DestinationBuffer, ByteCount ); + } + + StartingVbo += ByteCount; + Length -= ByteCount; + + // + // If this is the first page then update the Ea Range structure. + // + + if (FirstPage) { + + FirstPage = FALSE; + ByteCount = PAGE_SIZE; + + if (EaRange->AuxilaryBuffer == FALSE) { + + EaRange->Data = Buffer; + } + } + } + + return; +} + + +VOID +FatMarkEaRangeDirty ( + IN PIRP_CONTEXT IrpContext, + IN PFILE_OBJECT EaFileObject, + IN OUT PEA_RANGE EaRange + ) + +/*++ + +Routine Description: + + This routine is called to mark a range of the Ea file as dirty. If the modified + data is sitting in an auxilary buffer then we will copy it back into the cache. + In any case we will go through the list of Bcb's and mark them dirty. + +Arguments: + + EaFileObject - This is the file object for the Ea file. + + EaRange - This is the Ea range structure for this request. + +Return Value: + + None. + +--*/ + +{ + PBCB *NextBcb; + ULONG BcbCount; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( IrpContext ); + + // + // If there is an auxilary buffer we need to copy the data back into the cache. + // + + if (EaRange->AuxilaryBuffer == TRUE) { + + LARGE_INTEGER LargeVbo; + + LargeVbo.QuadPart = EaRange->StartingVbo; + + CcCopyWrite( EaFileObject, + &LargeVbo, + EaRange->Length, + TRUE, + EaRange->Data ); + } + + // + // Now walk through the Bcb chain and mark everything dirty. + // + + BcbCount = EaRange->BcbChainLength; + NextBcb = EaRange->BcbChain; + + while (BcbCount--) { + + if (*NextBcb != NULL) { + + CcSetDirtyPinnedData( *NextBcb, NULL ); + } + + NextBcb += 1; + } + + return; +} + + +VOID +FatUnpinEaRange ( + IN PIRP_CONTEXT IrpContext, + IN OUT PEA_RANGE EaRange + ) + +/*++ + +Routine Description: + + This routine is called to unpin a range in the Ea file. Any structures allocated + will be deallocated here. + +Arguments: + + EaRange - This is the Ea range structure for this request. + +Return Value: + + None. + +--*/ + +{ + PBCB *NextBcb; + ULONG BcbCount; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( IrpContext ); + + // + // If we allocated a auxilary buffer, deallocate it here. + // + + if (EaRange->AuxilaryBuffer == TRUE) { + + ExFreePool( EaRange->Data ); + EaRange->AuxilaryBuffer = FALSE; + } + + // + // Walk through the Bcb chain and unpin the data. + // + + if (EaRange->BcbChain != NULL) { + + BcbCount = EaRange->BcbChainLength; + NextBcb = EaRange->BcbChain; + + while (BcbCount--) { + + if (*NextBcb != NULL) { + + CcUnpinData( *NextBcb ); + *NextBcb = NULL; + } + + NextBcb += 1; + } + + // + // If we allocated a Bcb chain, deallocate it here. + // + + if (EaRange->BcbChain != &EaRange->BcbArray[0]) { + + ExFreePool( EaRange->BcbChain ); + } + + EaRange->BcbChain = NULL; + } + + return; +} + diff --git a/filesys/fastfat/fastfat.rc b/filesys/fastfat/fastfat.rc new file mode 100644 index 000000000..e509ff862 --- /dev/null +++ b/filesys/fastfat/fastfat.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 "Fast FAT File System Driver" +#define VER_INTERNALNAME_STR "fastfat.sys" +#define VER_ORIGINALFILENAME_STR "FastFAT.Sys" + +#include "common.ver" diff --git a/filesys/fastfat/fastfat.sln b/filesys/fastfat/fastfat.sln new file mode 100644 index 000000000..1e873e2f9 --- /dev/null +++ b/filesys/fastfat/fastfat.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}") = "fastfat", "fastfat.vcxproj", "{290F0F28-6606-4D9C-A2D4-2A3BCB252E23}" +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 + {290F0F28-6606-4D9C-A2D4-2A3BCB252E23}.Debug|Win32.ActiveCfg = Debug|Win32 + {290F0F28-6606-4D9C-A2D4-2A3BCB252E23}.Debug|Win32.Build.0 = Debug|Win32 + {290F0F28-6606-4D9C-A2D4-2A3BCB252E23}.Release|Win32.ActiveCfg = Release|Win32 + {290F0F28-6606-4D9C-A2D4-2A3BCB252E23}.Release|Win32.Build.0 = Release|Win32 + {290F0F28-6606-4D9C-A2D4-2A3BCB252E23}.Debug|x64.ActiveCfg = Debug|x64 + {290F0F28-6606-4D9C-A2D4-2A3BCB252E23}.Debug|x64.Build.0 = Debug|x64 + {290F0F28-6606-4D9C-A2D4-2A3BCB252E23}.Release|x64.ActiveCfg = Release|x64 + {290F0F28-6606-4D9C-A2D4-2A3BCB252E23}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/filesys/fastfat/fastfat.vcxproj b/filesys/fastfat/fastfat.vcxproj new file mode 100644 index 000000000..a74af4132 --- /dev/null +++ b/filesys/fastfat/fastfat.vcxproj @@ -0,0 +1,359 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {290F0F28-6606-4D9C-A2D4-2A3BCB252E23} + $(MSBuildProjectName) + false + Debug + Win32 + {CF490AB8-7641-46A9-98CE-6EFC136A00F8} + + + + 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) + + + + + + + + + + + + + + + + fastfat + + + fastfat + + + fastfat + + + fastfat + + + + true + Level4 + + + + + true + Level4 + + + + + true + Level4 + + + + + true + Level4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ;%(AdditionalIncludeDirectories) + fatprocs.h + Use + $(IntDir)\fatprocs.h.pch + + + ;%(AdditionalIncludeDirectories) + fatprocs.h + Use + $(IntDir)\fatprocs.h.pch + + + ;%(AdditionalIncludeDirectories) + fatprocs.h + Use + $(IntDir)\fatprocs.h.pch + + + ;%(AdditionalIncludeDirectories) + fatprocs.h + Use + $(IntDir)\fatprocs.h.pch + + + ;%(AdditionalIncludeDirectories) + fatprocs.h + Use + $(IntDir)\fatprocs.h.pch + + + ;%(AdditionalIncludeDirectories) + fatprocs.h + Use + $(IntDir)\fatprocs.h.pch + + + ;%(AdditionalIncludeDirectories) + fatprocs.h + Use + $(IntDir)\fatprocs.h.pch + + + ;%(AdditionalIncludeDirectories) + fatprocs.h + Use + $(IntDir)\fatprocs.h.pch + + + ;%(AdditionalIncludeDirectories) + fatprocs.h + Use + $(IntDir)\fatprocs.h.pch + + + ;%(AdditionalIncludeDirectories) + fatprocs.h + Use + $(IntDir)\fatprocs.h.pch + + + ;%(AdditionalIncludeDirectories) + fatprocs.h + Use + $(IntDir)\fatprocs.h.pch + + + ;%(AdditionalIncludeDirectories) + fatprocs.h + Use + $(IntDir)\fatprocs.h.pch + + + ;%(AdditionalIncludeDirectories) + fatprocs.h + Use + $(IntDir)\fatprocs.h.pch + + + ;%(AdditionalIncludeDirectories) + fatprocs.h + Use + $(IntDir)\fatprocs.h.pch + + + ;%(AdditionalIncludeDirectories) + fatprocs.h + Use + $(IntDir)\fatprocs.h.pch + + + ;%(AdditionalIncludeDirectories) + fatprocs.h + Create + $(IntDir)\fatprocs.h.pch + + + ;%(AdditionalIncludeDirectories) + fatprocs.h + Use + $(IntDir)\fatprocs.h.pch + + + ;%(AdditionalIncludeDirectories) + fatprocs.h + Use + $(IntDir)\fatprocs.h.pch + + + ;%(AdditionalIncludeDirectories) + fatprocs.h + Use + $(IntDir)\fatprocs.h.pch + + + ;%(AdditionalIncludeDirectories) + fatprocs.h + Use + $(IntDir)\fatprocs.h.pch + + + ;%(AdditionalIncludeDirectories) + fatprocs.h + Use + $(IntDir)\fatprocs.h.pch + + + ;%(AdditionalIncludeDirectories) + fatprocs.h + Use + $(IntDir)\fatprocs.h.pch + + + ;%(AdditionalIncludeDirectories) + fatprocs.h + Use + $(IntDir)\fatprocs.h.pch + + + ;%(AdditionalIncludeDirectories) + fatprocs.h + Use + $(IntDir)\fatprocs.h.pch + + + ;%(AdditionalIncludeDirectories) + fatprocs.h + Use + $(IntDir)\fatprocs.h.pch + + + ;%(AdditionalIncludeDirectories) + fatprocs.h + Use + $(IntDir)\fatprocs.h.pch + + + ;%(AdditionalIncludeDirectories) + fatprocs.h + Use + $(IntDir)\fatprocs.h.pch + + + ;%(AdditionalIncludeDirectories) + fatprocs.h + Use + $(IntDir)\fatprocs.h.pch + + + ;%(AdditionalIncludeDirectories) + fatprocs.h + Use + $(IntDir)\fatprocs.h.pch + + + ;%(AdditionalIncludeDirectories) + fatprocs.h + Use + $(IntDir)\fatprocs.h.pch + + + ;%(AdditionalIncludeDirectories) + fatprocs.h + Use + $(IntDir)\fatprocs.h.pch + + + ;%(AdditionalIncludeDirectories) + fatprocs.h + Use + $(IntDir)\fatprocs.h.pch + + + ;%(AdditionalIncludeDirectories) + fatprocs.h + Use + $(IntDir)\fatprocs.h.pch + + + ;%(AdditionalIncludeDirectories) + fatprocs.h + Use + $(IntDir)\fatprocs.h.pch + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/filesys/fastfat/fastfat.vcxproj.Filters b/filesys/fastfat/fastfat.vcxproj.Filters new file mode 100644 index 000000000..3d6452f00 --- /dev/null +++ b/filesys/fastfat/fastfat.vcxproj.Filters @@ -0,0 +1,130 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {73952C13-F65F-4462-8992-EFBA397D88BA} + + + h;hpp;hxx;hm;inl;inc;xsd + {07E2EF4D-1D9B-4EBF-B954-8C70849A5EDE} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {DB83B853-1CD8-4FAD-9B15-041FAC3314CB} + + + inf;inv;inx;mof;mc; + {A2195824-1370-4853-B032-E24348F5B667} + + + + + 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 + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/filesys/fastfat/fat.h b/filesys/fastfat/fat.h new file mode 100644 index 000000000..1e59844a0 --- /dev/null +++ b/filesys/fastfat/fat.h @@ -0,0 +1,755 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + Fat.h + +Abstract: + + This module defines the on-disk structure of the Fat file system. + + +--*/ + +#ifndef _FAT_ +#define _FAT_ + +// +// The following nomenclature is used to describe the Fat on-disk +// structure: +// +// LBN - is the number of a sector relative to the start of the disk. +// +// VBN - is the number of a sector relative to the start of a file, +// directory, or allocation. +// +// LBO - is a byte offset relative to the start of the disk. +// +// VBO - is a byte offset relative to the start of a file, directory +// or allocation. +// + +typedef LONGLONG LBO; /* for Fat32, LBO is >32 bits */ + +typedef LBO *PLBO; + +typedef ULONG32 VBO; +typedef VBO *PVBO; + + +// +// The boot sector is the first physical sector (LBN == 0) on the volume. +// Part of the sector contains a BIOS Parameter Block. The BIOS in the +// sector is packed (i.e., unaligned) so we'll supply a unpacking macro +// to translate a packed BIOS into its unpacked equivalent. The unpacked +// BIOS structure is already defined in ntioapi.h so we only need to define +// the packed BIOS. +// + +// +// Define the Packed and Unpacked BIOS Parameter Block +// + +typedef struct _PACKED_BIOS_PARAMETER_BLOCK { + UCHAR BytesPerSector[2]; // offset = 0x000 0 + UCHAR SectorsPerCluster[1]; // offset = 0x002 2 + UCHAR ReservedSectors[2]; // offset = 0x003 3 + UCHAR Fats[1]; // offset = 0x005 5 + UCHAR RootEntries[2]; // offset = 0x006 6 + UCHAR Sectors[2]; // offset = 0x008 8 + UCHAR Media[1]; // offset = 0x00A 10 + UCHAR SectorsPerFat[2]; // offset = 0x00B 11 + UCHAR SectorsPerTrack[2]; // offset = 0x00D 13 + UCHAR Heads[2]; // offset = 0x00F 15 + UCHAR HiddenSectors[4]; // offset = 0x011 17 + UCHAR LargeSectors[4]; // offset = 0x015 21 +} PACKED_BIOS_PARAMETER_BLOCK; // sizeof = 0x019 25 +typedef PACKED_BIOS_PARAMETER_BLOCK *PPACKED_BIOS_PARAMETER_BLOCK; + +typedef struct _PACKED_BIOS_PARAMETER_BLOCK_EX { + UCHAR BytesPerSector[2]; // offset = 0x000 0 + UCHAR SectorsPerCluster[1]; // offset = 0x002 2 + UCHAR ReservedSectors[2]; // offset = 0x003 3 + UCHAR Fats[1]; // offset = 0x005 5 + UCHAR RootEntries[2]; // offset = 0x006 6 + UCHAR Sectors[2]; // offset = 0x008 8 + UCHAR Media[1]; // offset = 0x00A 10 + UCHAR SectorsPerFat[2]; // offset = 0x00B 11 + UCHAR SectorsPerTrack[2]; // offset = 0x00D 13 + UCHAR Heads[2]; // offset = 0x00F 15 + UCHAR HiddenSectors[4]; // offset = 0x011 17 + UCHAR LargeSectors[4]; // offset = 0x015 21 + UCHAR LargeSectorsPerFat[4]; // offset = 0x019 25 + UCHAR ExtendedFlags[2]; // offset = 0x01D 29 + UCHAR FsVersion[2]; // offset = 0x01F 31 + UCHAR RootDirFirstCluster[4]; // offset = 0x021 33 + UCHAR FsInfoSector[2]; // offset = 0x025 37 + UCHAR BackupBootSector[2]; // offset = 0x027 39 + UCHAR Reserved[12]; // offset = 0x029 41 +} PACKED_BIOS_PARAMETER_BLOCK_EX; // sizeof = 0x035 53 + +typedef PACKED_BIOS_PARAMETER_BLOCK_EX *PPACKED_BIOS_PARAMETER_BLOCK_EX; + +// +// The IsBpbFat32 macro is defined to work with both packed and unpacked +// BPB structures. Since we are only checking for zero, the byte order +// does not matter. +// + +#define IsBpbFat32(bpb) (*(USHORT *)(&(bpb)->SectorsPerFat) == 0) + +typedef struct BIOS_PARAMETER_BLOCK { + USHORT BytesPerSector; + UCHAR SectorsPerCluster; + USHORT ReservedSectors; + UCHAR Fats; + USHORT RootEntries; + USHORT Sectors; + UCHAR Media; + USHORT SectorsPerFat; + USHORT SectorsPerTrack; + USHORT Heads; + ULONG32 HiddenSectors; + ULONG32 LargeSectors; + ULONG32 LargeSectorsPerFat; + union { + USHORT ExtendedFlags; + struct { + ULONG ActiveFat:4; + ULONG Reserved0:3; + ULONG MirrorDisabled:1; + ULONG Reserved1:8; + }; + }; + USHORT FsVersion; + ULONG32 RootDirFirstCluster; + USHORT FsInfoSector; + USHORT BackupBootSector; +} BIOS_PARAMETER_BLOCK, *PBIOS_PARAMETER_BLOCK; + +// +// This macro takes a Packed BIOS and fills in its Unpacked equivalent +// + +#define FatUnpackBios(Bios,Pbios) { \ + CopyUchar2(&(Bios)->BytesPerSector, &(Pbios)->BytesPerSector[0] ); \ + CopyUchar1(&(Bios)->SectorsPerCluster, &(Pbios)->SectorsPerCluster[0]); \ + CopyUchar2(&(Bios)->ReservedSectors, &(Pbios)->ReservedSectors[0] ); \ + CopyUchar1(&(Bios)->Fats, &(Pbios)->Fats[0] ); \ + CopyUchar2(&(Bios)->RootEntries, &(Pbios)->RootEntries[0] ); \ + CopyUchar2(&(Bios)->Sectors, &(Pbios)->Sectors[0] ); \ + CopyUchar1(&(Bios)->Media, &(Pbios)->Media[0] ); \ + CopyUchar2(&(Bios)->SectorsPerFat, &(Pbios)->SectorsPerFat[0] ); \ + CopyUchar2(&(Bios)->SectorsPerTrack, &(Pbios)->SectorsPerTrack[0] ); \ + CopyUchar2(&(Bios)->Heads, &(Pbios)->Heads[0] ); \ + CopyUchar4(&(Bios)->HiddenSectors, &(Pbios)->HiddenSectors[0] ); \ + CopyUchar4(&(Bios)->LargeSectors, &(Pbios)->LargeSectors[0] ); \ + CopyUchar4(&(Bios)->LargeSectorsPerFat,&((PPACKED_BIOS_PARAMETER_BLOCK_EX)Pbios)->LargeSectorsPerFat[0] ); \ + CopyUchar2(&(Bios)->ExtendedFlags, &((PPACKED_BIOS_PARAMETER_BLOCK_EX)Pbios)->ExtendedFlags[0] ); \ + CopyUchar2(&(Bios)->FsVersion, &((PPACKED_BIOS_PARAMETER_BLOCK_EX)Pbios)->FsVersion[0] ); \ + CopyUchar4(&(Bios)->RootDirFirstCluster, \ + &((PPACKED_BIOS_PARAMETER_BLOCK_EX)Pbios)->RootDirFirstCluster[0] ); \ + CopyUchar2(&(Bios)->FsInfoSector, &((PPACKED_BIOS_PARAMETER_BLOCK_EX)Pbios)->FsInfoSector[0] ); \ + CopyUchar2(&(Bios)->BackupBootSector, &((PPACKED_BIOS_PARAMETER_BLOCK_EX)Pbios)->BackupBootSector[0] ); \ +} + +// +// Define the boot sector +// + +typedef struct _PACKED_BOOT_SECTOR { + UCHAR Jump[3]; // offset = 0x000 0 + UCHAR Oem[8]; // offset = 0x003 3 + PACKED_BIOS_PARAMETER_BLOCK PackedBpb; // offset = 0x00B 11 + UCHAR PhysicalDriveNumber; // offset = 0x024 36 + UCHAR CurrentHead; // offset = 0x025 37 + UCHAR Signature; // offset = 0x026 38 + UCHAR Id[4]; // offset = 0x027 39 + UCHAR VolumeLabel[11]; // offset = 0x02B 43 + UCHAR SystemId[8]; // offset = 0x036 54 +} PACKED_BOOT_SECTOR; // sizeof = 0x03E 62 + +typedef PACKED_BOOT_SECTOR *PPACKED_BOOT_SECTOR; + +typedef struct _PACKED_BOOT_SECTOR_EX { + UCHAR Jump[3]; // offset = 0x000 0 + UCHAR Oem[8]; // offset = 0x003 3 + PACKED_BIOS_PARAMETER_BLOCK_EX PackedBpb; // offset = 0x00B 11 + UCHAR PhysicalDriveNumber; // offset = 0x040 64 + UCHAR CurrentHead; // offset = 0x041 65 + UCHAR Signature; // offset = 0x042 66 + UCHAR Id[4]; // offset = 0x043 67 + UCHAR VolumeLabel[11]; // offset = 0x047 71 + UCHAR SystemId[8]; // offset = 0x058 88 +} PACKED_BOOT_SECTOR_EX; // sizeof = 0x060 96 + +typedef PACKED_BOOT_SECTOR_EX *PPACKED_BOOT_SECTOR_EX; + +// +// Define the FAT32 FsInfo sector. +// + +typedef struct _FSINFO_SECTOR { + ULONG SectorBeginSignature; // offset = 0x000 0 + UCHAR ExtraBootCode[480]; // offset = 0x004 4 + ULONG FsInfoSignature; // offset = 0x1e4 484 + ULONG FreeClusterCount; // offset = 0x1e8 488 + ULONG NextFreeCluster; // offset = 0x1ec 492 + UCHAR Reserved[12]; // offset = 0x1f0 496 + ULONG SectorEndSignature; // offset = 0x1fc 508 +} FSINFO_SECTOR, *PFSINFO_SECTOR; + +#define FSINFO_SECTOR_BEGIN_SIGNATURE 0x41615252 +#define FSINFO_SECTOR_END_SIGNATURE 0xAA550000 + +#define FSINFO_SIGNATURE 0x61417272 + +// +// We use the CurrentHead field for our dirty partition info. +// + +#define FAT_BOOT_SECTOR_DIRTY 0x01 +#define FAT_BOOT_SECTOR_TEST_SURFACE 0x02 + +// +// Define a Fat Entry type. +// +// This type is used when representing a fat table entry. It also used +// to be used when dealing with a fat table index and a count of entries, +// but the ensuing type casting nightmare sealed this fate. These other +// two types are represented as ULONGs. +// + +typedef ULONG32 FAT_ENTRY; + +#define FAT32_ENTRY_MASK 0x0FFFFFFFUL + +// +// We use these special index values to set the dirty info for +// DOS/Win9x compatibility. +// + +#define FAT_CLEAN_VOLUME (~FAT32_ENTRY_MASK | 0) +#define FAT_DIRTY_VOLUME (~FAT32_ENTRY_MASK | 1) + +#define FAT_DIRTY_BIT_INDEX 1 + +// +// Physically, the entry is fully set if clean, and the high +// bit knocked out if it is dirty (i.e., it is really a clean +// bit). This means it is different per-FAT size. +// + +#define FAT_CLEAN_ENTRY (~0) + +#define FAT12_DIRTY_ENTRY 0x7ff +#define FAT16_DIRTY_ENTRY 0x7fff +#define FAT32_DIRTY_ENTRY 0x7fffffff + +// +// The following constants the are the valid Fat index values. +// + +#define FAT_CLUSTER_AVAILABLE (FAT_ENTRY)0x00000000 +#define FAT_CLUSTER_RESERVED (FAT_ENTRY)0x0ffffff0 +#define FAT_CLUSTER_BAD (FAT_ENTRY)0x0ffffff7 +#define FAT_CLUSTER_LAST (FAT_ENTRY)0x0fffffff + +// +// Fat files have the following time/date structures. Note that the +// following structure is a 32 bits long but USHORT aligned. +// + +typedef struct _FAT_TIME { + + USHORT DoubleSeconds : 5; + USHORT Minute : 6; + USHORT Hour : 5; + +} FAT_TIME; +typedef FAT_TIME *PFAT_TIME; + +typedef struct _FAT_DATE { + + USHORT Day : 5; + USHORT Month : 4; + USHORT Year : 7; // Relative to 1980 + +} FAT_DATE; +typedef FAT_DATE *PFAT_DATE; + +typedef struct _FAT_TIME_STAMP { + + FAT_TIME Time; + FAT_DATE Date; + +} FAT_TIME_STAMP; +typedef FAT_TIME_STAMP *PFAT_TIME_STAMP; + +// +// Fat files have 8 character file names and 3 character extensions +// + +typedef UCHAR FAT8DOT3[11]; +typedef FAT8DOT3 *PFAT8DOT3; + + +// +// The directory entry record exists for every file/directory on the +// disk except for the root directory. +// + +typedef struct _PACKED_DIRENT { + FAT8DOT3 FileName; // offset = 0 + UCHAR Attributes; // offset = 11 + UCHAR NtByte; // offset = 12 + UCHAR CreationMSec; // offset = 13 + FAT_TIME_STAMP CreationTime; // offset = 14 + FAT_DATE LastAccessDate; // offset = 18 + union { + USHORT ExtendedAttributes; // offset = 20 + USHORT FirstClusterOfFileHi; // offset = 20 + }; + FAT_TIME_STAMP LastWriteTime; // offset = 22 + USHORT FirstClusterOfFile; // offset = 26 + ULONG32 FileSize; // offset = 28 +} PACKED_DIRENT; // sizeof = 32 +typedef PACKED_DIRENT *PPACKED_DIRENT; + +// +// A packed dirent is already quadword aligned so simply declare a dirent as a +// packed dirent +// + +typedef PACKED_DIRENT DIRENT; +typedef DIRENT *PDIRENT; + +// +// The first byte of a dirent describes the dirent. There is also a routine +// to help in deciding how to interpret the dirent. +// + +#define FAT_DIRENT_NEVER_USED 0x00 +#define FAT_DIRENT_REALLY_0E5 0x05 +#define FAT_DIRENT_DIRECTORY_ALIAS 0x2e +#define FAT_DIRENT_DELETED 0xe5 + +// +// Define the NtByte bits. +// + +// +// These two bits are used for EFS on FAT +// 0x1 means the file contents are encrypted +// +// 0x2 means the EFS metadata header is big. +// (this optimization means we don't have to read +// in the first sector of the file stream to get +// the normal header size) +// + +#define FAT_DIRENT_NT_BYTE_ENCRYPTED 0x01 +#define FAT_DIRENT_NT_BYTE_BIG_HEADER 0x02 + +// +// These two bits optimize the case in which either the name +// or extension are all lower case. +// + +#define FAT_DIRENT_NT_BYTE_8_LOWER_CASE 0x08 +#define FAT_DIRENT_NT_BYTE_3_LOWER_CASE 0x10 + +// +// Define the various dirent attributes +// + +#define FAT_DIRENT_ATTR_READ_ONLY 0x01 +#define FAT_DIRENT_ATTR_HIDDEN 0x02 +#define FAT_DIRENT_ATTR_SYSTEM 0x04 +#define FAT_DIRENT_ATTR_VOLUME_ID 0x08 +#define FAT_DIRENT_ATTR_DIRECTORY 0x10 +#define FAT_DIRENT_ATTR_ARCHIVE 0x20 +#define FAT_DIRENT_ATTR_DEVICE 0x40 +#define FAT_DIRENT_ATTR_LFN (FAT_DIRENT_ATTR_READ_ONLY | \ + FAT_DIRENT_ATTR_HIDDEN | \ + FAT_DIRENT_ATTR_SYSTEM | \ + FAT_DIRENT_ATTR_VOLUME_ID) + +// +// On-disk extension for EFS files. +// + +#define FAT_EFS_EXTENSION L".PFILE" +#define FAT_EFS_EXTENSION_CHARCOUNT (6) +#define FAT_EFS_EXTENSION_BYTECOUNT (12) + + +// +// These macros convert a number of fields in the Bpb to bytes from sectors +// +// ULONG +// FatBytesPerCluster ( +// IN PBIOS_PARAMETER_BLOCK Bios +// ); +// +// ULONG +// FatBytesPerFat ( +// IN PBIOS_PARAMETER_BLOCK Bios +// ); +// +// ULONG +// FatReservedBytes ( +// IN PBIOS_PARAMETER_BLOCK Bios +// ); +// + +#define FatBytesPerCluster(B) ((ULONG)((B)->BytesPerSector * (B)->SectorsPerCluster)) + +#define FatBytesPerFat(B) (IsBpbFat32(B)? \ + ((ULONG)((B)->BytesPerSector * (B)->LargeSectorsPerFat)) : \ + ((ULONG)((B)->BytesPerSector * (B)->SectorsPerFat))) + +#define FatReservedBytes(B) ((ULONG)((B)->BytesPerSector * (B)->ReservedSectors)) + +// +// This macro returns the size of the root directory dirent area in bytes +// For Fat32, the root directory is variable in length. This macro returns +// 0 because it is also used to determine the location of cluster 2. +// +// ULONG +// FatRootDirectorySize ( +// IN PBIOS_PARAMETER_BLOCK Bios +// ); +// + +#define FatRootDirectorySize(B) ((ULONG)((B)->RootEntries * sizeof(DIRENT))) + + +// +// This macro returns the first Lbo (zero based) of the root directory on +// the device. This area is after the reserved and fats. +// +// For Fat32, the root directory is moveable. This macro returns the LBO +// for cluster 2 because it is used to determine the location of cluster 2. +// FatRootDirectoryLbo32() returns the actual LBO of the beginning of the +// actual root directory. +// +// LBO +// FatRootDirectoryLbo ( +// IN PBIOS_PARAMETER_BLOCK Bios +// ); +// + +#define FatRootDirectoryLbo(B) (FatReservedBytes(B) + ((B)->Fats * FatBytesPerFat(B))) +#define FatRootDirectoryLbo32(B) (FatFileAreaLbo(B)+((B)->RootDirFirstCluster-2)*FatBytesPerCluster(B)) + +// +// This macro returns the first Lbo (zero based) of the file area on the +// the device. This area is after the reserved, fats, and root directory. +// +// LBO +// FatFirstFileAreaLbo ( +// IN PBIOS_PARAMTER_BLOCK Bios +// ); +// + +#define FatFileAreaLbo(B) (FatRootDirectoryLbo(B) + FatRootDirectorySize(B)) + +// +// This macro returns the number of clusters on the disk. This value is +// computed by taking the total sectors on the disk subtracting up to the +// first file area sector and then dividing by the sectors per cluster count. +// Note that I don't use any of the above macros since far too much +// superfluous sector/byte conversion would take place. +// +// ULONG +// FatNumberOfClusters ( +// IN PBIOS_PARAMETER_BLOCK Bios +// ); +// + +// +// for prior to MS-DOS Version 3.2 +// +// After DOS 4.0, at least one of these, Sectors or LargeSectors, will be zero. +// but DOS version 3.2 case, both of these value might contains some value, +// because, before 3.2, we don't have Large Sector entry, some disk might have +// unexpected value in the field, we will use LargeSectors if Sectors eqaul to zero. +// + +#define FatNumberOfClusters(B) ( \ + \ + IsBpbFat32(B) ? \ + \ + ((((B)->Sectors ? (B)->Sectors : (B)->LargeSectors) \ + \ + - ((B)->ReservedSectors + \ + (B)->Fats * (B)->LargeSectorsPerFat )) \ + \ + / \ + \ + (B)->SectorsPerCluster) \ + : \ + ((((B)->Sectors ? (B)->Sectors : (B)->LargeSectors) \ + \ + - ((B)->ReservedSectors + \ + (B)->Fats * (B)->SectorsPerFat + \ + (B)->RootEntries * sizeof(DIRENT) / (B)->BytesPerSector ) ) \ + \ + / \ + \ + (B)->SectorsPerCluster) \ +) + +// +// This macro returns the fat table bit size (i.e., 12 or 16 bits) +// +// ULONG +// FatIndexBitSize ( +// IN PBIOS_PARAMETER_BLOCK Bios +// ); +// + +#define FatIndexBitSize(B) \ + ((UCHAR)(IsBpbFat32(B) ? 32 : (FatNumberOfClusters(B) < 4087 ? 12 : 16))) + +// +// This macro raises STATUS_FILE_CORRUPT and marks the Fcb bad if an +// index value is not within the proper range. +// Note that the first two index values are invalid (0, 1), so we must +// add two from the top end to make sure the everything is within range +// +// VOID +// FatVerifyIndexIsValid ( +// IN PIRP_CONTEXT IrpContext, +// IN PVCB Vcb, +// IN ULONG Index +// ); +// + +#define FatVerifyIndexIsValid(IC,V,I) { \ + if (((I) < 2) || ((I) > ((V)->AllocationSupport.NumberOfClusters + 1))) { \ + FatRaiseStatus(IC,STATUS_FILE_CORRUPT_ERROR); \ + } \ +} + +// +// These two macros are used to translate between Logical Byte Offsets, +// and fat entry indexes. Note the use of variables stored in the Vcb. +// These two macros are used at a higher level than the other macros +// above. +// +// Note, these indexes are true cluster numbers. +// +// LBO +// GetLboFromFatIndex ( +// IN FAT_ENTRY Fat_Index, +// IN PVCB Vcb +// ); +// +// FAT_ENTRY +// GetFatIndexFromLbo ( +// IN LBO Lbo, +// IN PVCB Vcb +// ); +// + +#define FatGetLboFromIndex(VCB,FAT_INDEX) ( \ + ( (LBO) \ + (VCB)->AllocationSupport.FileAreaLbo + \ + (((LBO)((FAT_INDEX) - 2)) << (VCB)->AllocationSupport.LogOfBytesPerCluster) \ + ) \ +) + +#define FatGetIndexFromLbo(VCB,LBO) ( \ + (ULONG) ( \ + (((LBO) - (VCB)->AllocationSupport.FileAreaLbo) >> \ + (VCB)->AllocationSupport.LogOfBytesPerCluster) + 2 \ + ) \ +) + +// +// The following macro does the shifting and such to lookup an entry +// +// VOID +// FatLookup12BitEntry( +// IN PVOID Fat, +// IN FAT_ENTRY Index, +// OUT PFAT_ENTRY Entry +// ); +// + +#define FatLookup12BitEntry(FAT,INDEX,ENTRY) { \ + \ + CopyUchar2((PUCHAR)(ENTRY), (PUCHAR)(FAT) + (INDEX) * 3 / 2); \ + \ + *ENTRY = (FAT_ENTRY)(0xfff & (((INDEX) & 1) ? (*(ENTRY) >> 4) : \ + *(ENTRY))); \ +} + +// +// The following macro does the tmp shifting and such to store an entry +// +// VOID +// FatSet12BitEntry( +// IN PVOID Fat, +// IN FAT_ENTRY Index, +// IN FAT_ENTRY Entry +// ); +// + +#define FatSet12BitEntry(FAT,INDEX,ENTRY) { \ + \ + FAT_ENTRY TmpFatEntry; \ + \ + CopyUchar2((PUCHAR)&TmpFatEntry, (PUCHAR)(FAT) + (INDEX) * 3 / 2); \ + \ + TmpFatEntry = (FAT_ENTRY) \ + (((INDEX) & 1) ? ((ENTRY) << 4) | (TmpFatEntry & 0xf) \ + : (ENTRY) | (TmpFatEntry & 0xf000)); \ + \ + *((UNALIGNED UCHAR2 *)((PUCHAR)(FAT) + (INDEX) * 3 / 2)) = *((UNALIGNED UCHAR2 *)(&TmpFatEntry)); \ +} + +// +// The following macro compares two FAT_TIME_STAMPs +// + +#define FatAreTimesEqual(TIME1,TIME2) ( \ + RtlEqualMemory((TIME1),(TIME2), sizeof(FAT_TIME_STAMP)) \ +) + + +#define EA_FILE_SIGNATURE (0x4445) // "ED" +#define EA_SET_SIGNATURE (0x4145) // "EA" + +// +// If the volume contains any ea data then there is one EA file called +// "EA DATA. SF" located in the root directory as Hidden, System and +// ReadOnly. +// + +typedef struct _EA_FILE_HEADER { + USHORT Signature; // offset = 0 + USHORT FormatType; // offset = 2 + USHORT LogType; // offset = 4 + USHORT Cluster1; // offset = 6 + USHORT NewCValue1; // offset = 8 + USHORT Cluster2; // offset = 10 + USHORT NewCValue2; // offset = 12 + USHORT Cluster3; // offset = 14 + USHORT NewCValue3; // offset = 16 + USHORT Handle; // offset = 18 + USHORT NewHOffset; // offset = 20 + UCHAR Reserved[10]; // offset = 22 + USHORT EaBaseTable[240]; // offset = 32 +} EA_FILE_HEADER; // sizeof = 512 + +typedef EA_FILE_HEADER *PEA_FILE_HEADER; + +typedef USHORT EA_OFF_TABLE[128]; + +typedef EA_OFF_TABLE *PEA_OFF_TABLE; + +// +// Every file with an extended attribute contains in its dirent an index +// into the EaMapTable. The map table contains an offset within the ea +// file (cluster aligned) of the ea data for the file. The individual +// ea data for each file is prefaced with an Ea Data Header. +// + +typedef struct _EA_SET_HEADER { + USHORT Signature; // offset = 0 + USHORT OwnEaHandle; // offset = 2 + ULONG32 NeedEaCount; // offset = 4 + UCHAR OwnerFileName[14]; // offset = 8 + UCHAR Reserved[4]; // offset = 22 + UCHAR cbList[4]; // offset = 26 + UCHAR PackedEas[1]; // offset = 30 +} EA_SET_HEADER; // sizeof = 30 +typedef EA_SET_HEADER *PEA_SET_HEADER; + +#define SIZE_OF_EA_SET_HEADER 30 + +#define MAXIMUM_EA_SIZE 0x0000ffff + +#define GetcbList(EASET) (((EASET)->cbList[0] << 0) + \ + ((EASET)->cbList[1] << 8) + \ + ((EASET)->cbList[2] << 16) + \ + ((EASET)->cbList[3] << 24)) + +#define SetcbList(EASET,CB) { \ + (EASET)->cbList[0] = (CB >> 0) & 0x0ff; \ + (EASET)->cbList[1] = (CB >> 8) & 0x0ff; \ + (EASET)->cbList[2] = (CB >> 16) & 0x0ff; \ + (EASET)->cbList[3] = (CB >> 24) & 0x0ff; \ +} + +// +// Every individual ea in an ea set is declared the following packed ea +// + +typedef struct _PACKED_EA { + UCHAR Flags; + UCHAR EaNameLength; + UCHAR EaValueLength[2]; + CHAR EaName[1]; +} PACKED_EA; +typedef PACKED_EA *PPACKED_EA; + +// +// The following two macros are used to get and set the ea value length +// field of a packed ea +// +// VOID +// GetEaValueLength ( +// IN PPACKED_EA Ea, +// OUT PUSHORT ValueLength +// ); +// +// VOID +// SetEaValueLength ( +// IN PPACKED_EA Ea, +// IN USHORT ValueLength +// ); +// + +#define GetEaValueLength(EA,LEN) { \ + *(LEN) = 0; \ + CopyUchar2( (LEN), (EA)->EaValueLength ); \ +} + +#define SetEaValueLength(EA,LEN) { \ + CopyUchar2( &((EA)->EaValueLength), (LEN) ); \ +} + +// +// The following macro is used to get the size of a packed ea +// +// VOID +// SizeOfPackedEa ( +// IN PPACKED_EA Ea, +// OUT PUSHORT EaSize +// ); +// + +#define SizeOfPackedEa(EA,SIZE) { \ + ULONG _NL,_DL; _NL = 0; _DL = 0; \ + CopyUchar1(&_NL, &(EA)->EaNameLength); \ + GetEaValueLength(EA, &_DL); \ + *(SIZE) = 1 + 1 + 2 + _NL + 1 + _DL; \ +} + +#define EA_NEED_EA_FLAG 0x80 +#define MIN_EA_HANDLE 1 +#define MAX_EA_HANDLE 30719 +#define UNUSED_EA_HANDLE 0xffff +#define EA_CBLIST_OFFSET 0x1a +#define MAX_EA_BASE_INDEX 240 +#define MAX_EA_OFFSET_INDEX 128 + + +#endif // _FAT_ + diff --git a/filesys/fastfat/fatdata.c b/filesys/fastfat/fatdata.c new file mode 100644 index 000000000..07696d811 --- /dev/null +++ b/filesys/fastfat/fatdata.c @@ -0,0 +1,1527 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + FatData.c + +Abstract: + + This module declares the global data used by the Fat file system. + + +--*/ + +#include "FatProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (FAT_BUG_CHECK_FATDATA) + +// +// The debug trace level +// + +#define Dbg (DEBUG_TRACE_CATCH_EXCEPTIONS) + +#ifdef ALLOC_PRAGMA + +#if DBG +#pragma alloc_text(PAGE, FatBugCheckExceptionFilter) +#endif + +#pragma alloc_text(PAGE, FatCompleteRequest_Real) +#pragma alloc_text(PAGE, FatFastIoCheckIfPossible) +#pragma alloc_text(PAGE, FatFastQueryBasicInfo) +#pragma alloc_text(PAGE, FatFastQueryNetworkOpenInfo) +#pragma alloc_text(PAGE, FatFastQueryStdInfo) +#pragma alloc_text(PAGE, FatIsIrpTopLevel) +#pragma alloc_text(PAGE, FatPopUpFileCorrupt) +#pragma alloc_text(PAGE, FatProcessException) +#endif + + +// +// The global fsd data record, and zero large integer +// + +#pragma prefast( suppress:22112, "only applies to user mode processes" ) +FAT_DATA FatData; + +PDEVICE_OBJECT FatDiskFileSystemDeviceObject; +PDEVICE_OBJECT FatCdromFileSystemDeviceObject; + +LARGE_INTEGER FatLargeZero = {0,0}; +LARGE_INTEGER FatMaxLarge = {MAXULONG,MAXLONG}; + +LARGE_INTEGER Fat30Milliseconds = {(ULONG)(-30 * 1000 * 10), -1}; +LARGE_INTEGER Fat100Milliseconds = {(ULONG)(-30 * 1000 * 10), -1}; +LARGE_INTEGER FatOneDay = {0x2a69c000, 0xc9}; +LARGE_INTEGER FatJanOne1980 = {0xe1d58000,0x01a8e79f}; +LARGE_INTEGER FatDecThirtyOne1979 = {0xb76bc000,0x01a8e6d6}; + +FAT_TIME_STAMP FatTimeJanOne1980 = {{0,0,0},{1,1,0}}; + +LARGE_INTEGER FatMagic10000 = {0xe219652c, 0xd1b71758}; +LARGE_INTEGER FatMagic86400000 = {0xfa67b90e, 0xc6d750eb}; + +#pragma prefast( suppress:22112, "only applies to user mode processes" ) +FAST_IO_DISPATCH FatFastIoDispatch; + +// +// Our lookaside lists. +// + +NPAGED_LOOKASIDE_LIST FatIrpContextLookasideList; +NPAGED_LOOKASIDE_LIST FatNonPagedFcbLookasideList; +NPAGED_LOOKASIDE_LIST FatEResourceLookasideList; + +SLIST_HEADER FatCloseContextSList; + +// +// Synchronization for the close queue +// + +FAST_MUTEX FatCloseQueueMutex; + +// +// Reserve MDL for paging file operations. +// + +PMDL FatReserveMdl = NULL; +KEVENT FatReserveEvent; + +// +// Global disk accounting state, enabled or disabled +// + +LOGICAL FatDiskAccountingEnabled = FALSE; + + +#ifdef FASTFATDBG + +LONG FatDebugTraceLevel = 0x00000009; +LONG FatDebugTraceIndent = 0; + +ULONG FatFsdEntryCount = 0; +ULONG FatFspEntryCount = 0; +ULONG FatIoCallDriverCount = 0; + +LONG FatPerformanceTimerLevel = 0x00000000; + +ULONG FatTotalTicks[32] = { 0 }; + +// +// I need this because C can't support conditional compilation within +// a macro. +// + +PVOID FatNull = NULL; + +NTSTATUS FatInterestingException = 0; + +#endif // FASTFATDBG + +#if DBG + +NTSTATUS FatAssertNotStatus = STATUS_SUCCESS; +BOOLEAN FatTestRaisedStatus = FALSE; + +#endif + + +#if DBG +ULONG +FatBugCheckExceptionFilter ( + IN PEXCEPTION_POINTERS ExceptionPointer + ) + +/*++ + +Routine Description: + + An exception filter which acts as an assert that the exception should + never occur. + + This is only valid on debug builds, we don't want the overhead on retail. + +Arguments: + + ExceptionPointers - The result of GetExceptionInformation() in the context + of the exception. + +Return Value: + + Bugchecks. + +--*/ + +{ + PAGED_CODE(); + + FatBugCheck( (ULONG_PTR)ExceptionPointer->ExceptionRecord, + (ULONG_PTR)ExceptionPointer->ContextRecord, + (ULONG_PTR)ExceptionPointer->ExceptionRecord->ExceptionAddress ); + +// return EXCEPTION_EXECUTE_HANDLER; // unreachable code +} +#endif + + +ULONG +FatExceptionFilter ( + IN PIRP_CONTEXT IrpContext, + IN PEXCEPTION_POINTERS ExceptionPointer + ) + +/*++ + +Routine Description: + + This routine is used to decide if we should or should not handle + an exception status that is being raised. It inserts the status + into the IrpContext and either indicates that we should handle + the exception or bug check the system. + +Arguments: + + ExceptionPointers - The result of GetExceptionInformation() in the context + of the exception. + +Return Value: + + ULONG - returns EXCEPTION_EXECUTE_HANDLER or bugchecks + +--*/ + +{ + NTSTATUS ExceptionCode; + + ExceptionCode = ExceptionPointer->ExceptionRecord->ExceptionCode; + DebugTrace(0, DEBUG_TRACE_UNWIND, "FatExceptionFilter %X\n", ExceptionCode); + DebugDump("FatExceptionFilter\n", Dbg, NULL ); + +#ifdef FASTFATDBG + + if( FatInterestingException != 0 && ExceptionCode == FatInterestingException ) { + NT_ASSERT(FALSE); + } + +#endif + + // + // If the exception is STATUS_IN_PAGE_ERROR, get the I/O error code + // from the exception record. + // + + if (ExceptionCode == STATUS_IN_PAGE_ERROR) { + if (ExceptionPointer->ExceptionRecord->NumberParameters >= 3) { + ExceptionCode = (NTSTATUS)ExceptionPointer->ExceptionRecord->ExceptionInformation[2]; + } + } + + // + // If there is not an irp context, we must have had insufficient resources. + // + + if ( !ARGUMENT_PRESENT( IrpContext ) ) { + + if (!FsRtlIsNtstatusExpected( ExceptionCode )) { + +#pragma prefast( suppress:28159, "things are seriously wrong if we get here" ) + FatBugCheck( (ULONG_PTR)ExceptionPointer->ExceptionRecord, + (ULONG_PTR)ExceptionPointer->ContextRecord, + (ULONG_PTR)ExceptionPointer->ExceptionRecord->ExceptionAddress ); + } + + return EXCEPTION_EXECUTE_HANDLER; + } + + // + // For the purposes of processing this exception, let's mark this + // request as being able to wait and disable write through if we + // aren't posting it. + // + + SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT); + + if ( (ExceptionCode != STATUS_CANT_WAIT) && + (ExceptionCode != STATUS_VERIFY_REQUIRED) ) { + + SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_WRITE_THROUGH); + } + + if ( IrpContext->ExceptionStatus == 0 ) { + + if (FsRtlIsNtstatusExpected( ExceptionCode )) { + + IrpContext->ExceptionStatus = ExceptionCode; + + return EXCEPTION_EXECUTE_HANDLER; + + } else { + +#pragma prefast( suppress:28159, "things are seriously wrong if we get here" ) + FatBugCheck( (ULONG_PTR)ExceptionPointer->ExceptionRecord, + (ULONG_PTR)ExceptionPointer->ContextRecord, + (ULONG_PTR)ExceptionPointer->ExceptionRecord->ExceptionAddress ); + } + + } else { + + // + // We raised this code explicitly ourselves, so it had better be + // expected. + // + + NT_ASSERT( IrpContext->ExceptionStatus == ExceptionCode ); + NT_ASSERT( FsRtlIsNtstatusExpected( ExceptionCode ) ); + } + + return EXCEPTION_EXECUTE_HANDLER; +} + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatProcessException ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp, + IN NTSTATUS ExceptionCode + ) + +/*++ + +Routine Description: + + This routine process an exception. It either completes the request + with the saved exception status or it sends it off to IoRaiseHardError() + +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. + +--*/ + +{ + PVCB Vcb; + PIO_STACK_LOCATION IrpSp; + FAT_VOLUME_STATE TransitionState = VolumeDirty; + ULONG SavedFlags = 0; + + PAGED_CODE(); + + DebugTrace(0, Dbg, "FatProcessException\n", 0); + + // + // If there is not an irp context, we must have had insufficient resources. + // + + if ( !ARGUMENT_PRESENT( IrpContext ) ) { + + FatCompleteRequest( FatNull, Irp, ExceptionCode ); + + return ExceptionCode; + } + + // + // Get the real exception status from IrpContext->ExceptionStatus, and + // reset it. + // + + ExceptionCode = IrpContext->ExceptionStatus; + FatResetExceptionState( IrpContext ); + + // + // If this is an Mdl write request, then take care of the Mdl + // here so that things get cleaned up properly. Cc now leaves + // the MDL in place so a filesystem can retry after clearing an + // internal condition (FAT does not). + // + + if ((IrpContext->MajorFunction == IRP_MJ_WRITE) && + (FlagOn( IrpContext->MinorFunction, IRP_MN_COMPLETE_MDL ) == IRP_MN_COMPLETE_MDL) && + (Irp->MdlAddress != NULL)) { + + PIO_STACK_LOCATION LocalIrpSp = IoGetCurrentIrpStackLocation(Irp); + + CcMdlWriteAbort( LocalIrpSp->FileObject, Irp->MdlAddress ); + Irp->MdlAddress = NULL; + } + + // + // If we are going to post the request, we may have to lock down the + // user's buffer, so do it here in a try except so that we failed the + // request if the LockPages fails. + // + // Also unpin any repinned Bcbs, protected by the try {} except {} filter. + // + + try { + + SavedFlags = IrpContext->Flags; + + // + // Make sure we don't try to write through Bcbs + // + + SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_WRITE_THROUGH); + + FatUnpinRepinnedBcbs( IrpContext ); + + IrpContext->Flags = SavedFlags; + + // + // If we will have to post the request, do it here. Note + // that the last thing FatPrePostIrp() does is mark the Irp pending, + // so it is critical that we actually return PENDING. Nothing + // from this point to return can fail, so we are OK. + // + // We cannot do a verify operations at APC level because we + // have to wait for Io operations to complete. + // + + if (!FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_RECURSIVE_CALL) && +#if (NTDDI_VERSION >= NTDDI_VISTA) + (((ExceptionCode == STATUS_VERIFY_REQUIRED) && KeAreAllApcsDisabled()) || +#else + (((ExceptionCode == STATUS_VERIFY_REQUIRED) && (KeGetCurrentIrql() >= APC_LEVEL)) || +#endif + (ExceptionCode == STATUS_CANT_WAIT))) { + + ExceptionCode = FatFsdPostRequest( IrpContext, Irp ); + } + + } except( FatExceptionFilter( IrpContext, GetExceptionInformation() ) ) { + + ExceptionCode = IrpContext->ExceptionStatus; + IrpContext->ExceptionStatus = 0; + + IrpContext->Flags = SavedFlags; + } + + // + // If we posted the request, just return here. + // + + if (ExceptionCode == STATUS_PENDING) { + + return ExceptionCode; + } + + Irp->IoStatus.Status = ExceptionCode; + + + // + // If this request is not a "top-level" irp, just complete it. + // + + if (FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_RECURSIVE_CALL)) { + + // + // If there is a cache operation above us, commute verify + // to a lock conflict. This will cause retries so that + // we have a chance of getting through without needing + // to return an unaesthetic error for the operation. + // + + if (IoGetTopLevelIrp() == (PIRP)FSRTL_CACHE_TOP_LEVEL_IRP && + ExceptionCode == STATUS_VERIFY_REQUIRED) { + + ExceptionCode = STATUS_FILE_LOCK_CONFLICT; + } + + FatCompleteRequest( IrpContext, Irp, ExceptionCode ); + + return ExceptionCode; + } + + if (IoIsErrorUserInduced(ExceptionCode)) { + + // + // Check for the various error conditions that can be caused by, + // and possibly resolved by the user. + // + + if (ExceptionCode == STATUS_VERIFY_REQUIRED) { + + PDEVICE_OBJECT Device = NULL; + + DebugTrace(0, Dbg, "Perform Verify Operation\n", 0); + + // + // Now we are at the top level file system entry point. + // + // Grab the device to verify from the thread local storage + // and stick it in the information field for transportation + // to the fsp. We also clear the field at this time. + // + + Device = IoGetDeviceToVerify( Irp->Tail.Overlay.Thread ); + IoSetDeviceToVerify( Irp->Tail.Overlay.Thread, NULL ); + + 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; + + } else { + + // + // For FSCTLs, IrpContext->Vcb may not be populated, so get the IrpContext->RealDevice instead + // + + Device = IrpContext->RealDevice; + } + + // + // Let's not BugCheck just because the device to verify is somehow still NULL. + // + + if (Device == NULL) { + + ExceptionCode = STATUS_DRIVER_INTERNAL_ERROR; + + FatCompleteRequest( IrpContext, Irp, ExceptionCode ); + + return ExceptionCode; + } + + // + // FatPerformVerify() will do the right thing with the Irp. + + return FatPerformVerify( 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)) { + + FatCompleteRequest( IrpContext, Irp, ExceptionCode ); + + return ExceptionCode; + + } else { + + // + // Generate a pop-up + // + + PDEVICE_OBJECT RealDevice = NULL; + PVPB Vpb; + PETHREAD Thread; + + 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; + RealDevice = IoGetDeviceToVerify( Thread ); + + if ( RealDevice == NULL ) { + + Thread = PsGetCurrentThread(); + RealDevice = IoGetDeviceToVerify( Thread ); + + NT_ASSERT( RealDevice != 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) { + + RealDevice = IrpContext->Vcb->Vpb->RealDevice; + + } else { + + // + // For FSCTLs, IrpContext->Vcb may not be populated, so get the IrpContext->RealDevice instead + // + + RealDevice = IrpContext->RealDevice; + } + + // + // Let's not BugCheck just because the device to verify is somehow still NULL. + // + + if (RealDevice == NULL) { + + FatCompleteRequest( 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, RealDevice ); + + // + // 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. + // + + FatDeleteIrpContext( IrpContext ); + return STATUS_PENDING; + } + } + + // + // This is just a run of the mill error. If is a STATUS that we + // raised ourselves, and the information would be use for the + // user, raise an informational pop-up. + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + Vcb = IrpContext->Vcb; + + // + // Now, if the Vcb is unknown to us this means that the error was raised + // in the process of a mount and before we even had a chance to build + // a full Vcb - and was really handled there. + // + + if (Vcb != NULL) { + + if ( !FatDeviceIsFatFsdo( IrpSp->DeviceObject) && + !NT_SUCCESS(ExceptionCode) && + !FsRtlIsTotalDeviceFailure(ExceptionCode) ) { + + TransitionState = VolumeDirtyWithSurfaceTest; + } + + // + // If this was a STATUS_FILE_CORRUPT or similar error indicating some + // nastiness out on the media, then mark the volume permanently dirty. + // + + if (!FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_POPUPS) && + ( TransitionState == VolumeDirtyWithSurfaceTest || + (ExceptionCode == STATUS_FILE_CORRUPT_ERROR) || + (ExceptionCode == STATUS_DISK_CORRUPT_ERROR) || + (ExceptionCode == STATUS_EA_CORRUPT_ERROR) || + (ExceptionCode == STATUS_INVALID_EA_NAME) || + (ExceptionCode == STATUS_EA_LIST_INCONSISTENT) || + (ExceptionCode == STATUS_NO_EAS_ON_FILE) )) { + + NT_ASSERT( NodeType(Vcb) == FAT_NTC_VCB ); + NT_ASSERT( !FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_RECURSIVE_CALL)); + + SetFlag( Vcb->VcbState, VCB_STATE_FLAG_MOUNTED_DIRTY ); + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT); + + // + // Do the "dirty" work, ignoring any error. We need to take the Vcb here + // to synchronize against the verify path tearing things down, since + // we dropped all synchronization when backing up due to the exception. + // + + FatAcquireExclusiveVcbNoOpCheck( IrpContext, Vcb); + + try { + + if (VcbGood == Vcb->VcbCondition) { + + FatMarkVolume( IrpContext, Vcb, TransitionState ); + } + } + except( FatExceptionFilter( IrpContext, GetExceptionInformation() ) ) { + + NOTHING; + } + + FatReleaseVcb( IrpContext, Vcb); + } + } + + FatCompleteRequest( IrpContext, Irp, ExceptionCode ); + + return ExceptionCode; +} + + +VOID +FatCompleteRequest_Real ( + IN PIRP_CONTEXT IrpContext OPTIONAL, + IN PIRP Irp OPTIONAL, + IN NTSTATUS Status + ) + +/*++ + +Routine Description: + + This routine completes a Irp + +Arguments: + + Irp - Supplies the Irp being processed + + Status - Supplies the status to complete the Irp with + +Return Value: + + None. + +--*/ + +{ + PAGED_CODE(); + + // + // If we have an Irp Context then unpin all of the repinned bcbs + // we might have collected. + // + + if (IrpContext != NULL) { + + NT_ASSERT( IrpContext->Repinned.Bcb[0] == NULL ); + + FatUnpinRepinnedBcbs( IrpContext ); + } + + // + // Delete the Irp context before completing the IRP so if + // we run into some of the asserts, we can still backtrack + // through the IRP. + // + + if (IrpContext != NULL) { + + FatDeleteIrpContext( IrpContext ); + } + + // + // If we have an Irp then complete the irp. + // + + if (Irp != NULL) { + + // + // We got an error, so zero out the information field before + // completing the request if this was an input operation. + // Otherwise IopCompleteRequest will try to copy to the user's buffer. + // + + if ( NT_ERROR(Status) && + FlagOn(Irp->Flags, IRP_INPUT_OPERATION) ) { + + Irp->IoStatus.Information = 0; + } + + Irp->IoStatus.Status = Status; + + IoCompleteRequest( Irp, IO_DISK_INCREMENT ); + } + + return; +} + +BOOLEAN +FatIsIrpTopLevel ( + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine detects if an Irp is the Top level requestor, ie. if it os OK + to do a verify or pop-up now. If TRUE is returned, then no file system + resources are held above us. + +Arguments: + + Irp - Supplies the Irp being processed + + Status - Supplies the status to complete the Irp with + +Return Value: + + None. + +--*/ + +{ + PAGED_CODE(); + + if ( IoGetTopLevelIrp() == NULL ) { + + IoSetTopLevelIrp( Irp ); + + return TRUE; + + } else { + + return FALSE; + } +} + + +_Function_class_(FAST_IO_CHECK_IF_POSSIBLE) +BOOLEAN +FatFastIoCheckIfPossible ( + 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 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. + +--*/ + +{ + PVCB Vcb; + PFCB Fcb; + PCCB Ccb; + + LARGE_INTEGER LargeLength; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( DeviceObject ); + UNREFERENCED_PARAMETER( IoStatus ); + UNREFERENCED_PARAMETER( Wait ); + // + // Decode the file object to get our fcb, the only one we want + // to deal with is a UserFileOpen + // + + if (FatDecodeFileObject( FileObject, &Vcb, &Fcb, &Ccb ) != UserFileOpen) { + + return FALSE; + } + + LargeLength.QuadPart = Length; + + // + // Based on whether this is a read or write operation we call + // fsrtl check for read/write + // + + if (CheckForReadOperation) { + + if (FsRtlFastCheckLockForRead( &Fcb->Specific.Fcb.FileLock, + FileOffset, + &LargeLength, + LockKey, + FileObject, + PsGetCurrentProcess() )) { + + return TRUE; + } + + } else { + + // + // Also check for a write-protected volume here. + // + + if (!FlagOn(Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED) && + FsRtlFastCheckLockForWrite( &Fcb->Specific.Fcb.FileLock, + FileOffset, + &LargeLength, + LockKey, + FileObject, + PsGetCurrentProcess() )) { + + return TRUE; + } + } + + return FALSE; +} + + +_Function_class_(FAST_IO_QUERY_BASIC_INFO) +BOOLEAN +FatFastQueryBasicInfo ( + IN PFILE_OBJECT FileObject, + IN BOOLEAN Wait, + IN 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 Results = FALSE; + IRP_CONTEXT IrpContext; + + TYPE_OF_OPEN TypeOfOpen; + PVCB Vcb; + PFCB Fcb; + PCCB Ccb; + + BOOLEAN FcbAcquired = FALSE; + + PAGED_CODE(); + UNREFERENCED_PARAMETER( DeviceObject ); + + // + // Prepare the dummy irp context + // + + RtlZeroMemory( &IrpContext, sizeof(IRP_CONTEXT) ); + IrpContext.NodeTypeCode = FAT_NTC_IRP_CONTEXT; + IrpContext.NodeByteSize = sizeof(IRP_CONTEXT); + + if (Wait) { + + SetFlag(IrpContext.Flags, IRP_CONTEXT_FLAG_WAIT); + + } else { + + ClearFlag(IrpContext.Flags, IRP_CONTEXT_FLAG_WAIT); + } + + // + // Determine the type of open for the input file object and only accept + // the user file or directory open + // + + TypeOfOpen = FatDecodeFileObject( FileObject, &Vcb, &Fcb, &Ccb ); + + if ((TypeOfOpen != UserFileOpen) && (TypeOfOpen != UserDirectoryOpen)) { + + return Results; + } + + FsRtlEnterFileSystem(); + + // + // Get access to the Fcb but only if it is not the paging file + // + + if (!FlagOn( Fcb->FcbState, FCB_STATE_PAGING_FILE )) { + + if (!ExAcquireResourceSharedLite( Fcb->Header.Resource, Wait )) { + + FsRtlExitFileSystem(); + return Results; + } + + FcbAcquired = TRUE; + } + + try { + + // + // If the Fcb is not in a good state, return FALSE. + // + + if (Fcb->FcbCondition != FcbGood) { + + try_return( Results ); + } + + Buffer->FileAttributes = 0; + + // + // If the fcb is not the root dcb then we will fill in the + // buffer otherwise it is all setup for us. + // + + if (NodeType(Fcb) != FAT_NTC_ROOT_DCB) { + + // + // Extract the data and fill in the non zero fields of the output + // buffer + // + + Buffer->LastWriteTime = Fcb->LastWriteTime; + Buffer->CreationTime = Fcb->CreationTime; + Buffer->LastAccessTime = Fcb->LastAccessTime; + + // + // Zero out the field we don't support. + // + + Buffer->ChangeTime.QuadPart = 0; + Buffer->FileAttributes = Fcb->DirentFatFlags; + + } else { + + Buffer->LastWriteTime.QuadPart = 0; + Buffer->CreationTime.QuadPart = 0; + Buffer->LastAccessTime.QuadPart = 0; + Buffer->ChangeTime.QuadPart = 0; + + Buffer->FileAttributes = FILE_ATTRIBUTE_DIRECTORY; + } + + + // + // If the temporary flag is set, then set it in the buffer. + // + + if (FlagOn( Fcb->FcbState, FCB_STATE_TEMPORARY )) { + + SetFlag( Buffer->FileAttributes, FILE_ATTRIBUTE_TEMPORARY ); + } + + // + // If no attributes were set, set the normal bit. + // + + if (Buffer->FileAttributes == 0) { + + Buffer->FileAttributes = FILE_ATTRIBUTE_NORMAL; + } + + IoStatus->Status = STATUS_SUCCESS; + IoStatus->Information = sizeof(FILE_BASIC_INFORMATION); + + Results = TRUE; + + try_exit: NOTHING; + } finally { + + if (FcbAcquired) { ExReleaseResourceLite( Fcb->Header.Resource ); } + + FsRtlExitFileSystem(); + } + + // + // And return to our caller + // + + return Results; +} + + +_Function_class_(FAST_IO_QUERY_STANDARD_INFO) +BOOLEAN +FatFastQueryStdInfo ( + IN PFILE_OBJECT FileObject, + IN BOOLEAN Wait, + IN 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 Results = FALSE; + IRP_CONTEXT IrpContext; + + TYPE_OF_OPEN TypeOfOpen; + PVCB Vcb; + PFCB Fcb; + PCCB Ccb; + + BOOLEAN FcbAcquired = FALSE; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( DeviceObject ); + + // + // Prepare the dummy irp context + // + + RtlZeroMemory( &IrpContext, sizeof(IRP_CONTEXT) ); + IrpContext.NodeTypeCode = FAT_NTC_IRP_CONTEXT; + IrpContext.NodeByteSize = sizeof(IRP_CONTEXT); + + if (Wait) { + + SetFlag(IrpContext.Flags, IRP_CONTEXT_FLAG_WAIT); + + } else { + + ClearFlag(IrpContext.Flags, IRP_CONTEXT_FLAG_WAIT); + } + + // + // Determine the type of open for the input file object and only accept + // the user file or directory open + // + + TypeOfOpen = FatDecodeFileObject( FileObject, &Vcb, &Fcb, &Ccb ); + + if ((TypeOfOpen != UserFileOpen) && (TypeOfOpen != UserDirectoryOpen)) { + + return Results; + } + + // + // Get access to the Fcb but only if it is not the paging file + // + + FsRtlEnterFileSystem(); + + if (!FlagOn( Fcb->FcbState, FCB_STATE_PAGING_FILE )) { + + if (!ExAcquireResourceSharedLite( Fcb->Header.Resource, Wait )) { + + FsRtlExitFileSystem(); + return Results; + } + + FcbAcquired = TRUE; + } + + try { + + // + // If the Fcb is not in a good state, return FALSE. + // + + if (Fcb->FcbCondition != FcbGood) { + + try_return( Results ); + } + + Buffer->NumberOfLinks = 1; + Buffer->DeletePending = BooleanFlagOn( Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE ); + + // + // Case on whether this is a file or a directory, and extract + // the information and fill in the fcb/dcb specific parts + // of the output buffer. + // + + if (NodeType(Fcb) == FAT_NTC_FCB) { + + // + // If we don't alread know the allocation size, we cannot look + // it up in the fast path. + // + + if (Fcb->Header.AllocationSize.QuadPart == FCB_LOOKUP_ALLOCATIONSIZE_HINT) { + + try_return( Results ); + } + + Buffer->AllocationSize = Fcb->Header.AllocationSize; + Buffer->EndOfFile = Fcb->Header.FileSize; + + Buffer->Directory = FALSE; + + } else { + + Buffer->AllocationSize = FatLargeZero; + Buffer->EndOfFile = FatLargeZero; + + Buffer->Directory = TRUE; + } + + IoStatus->Status = STATUS_SUCCESS; + IoStatus->Information = sizeof(FILE_STANDARD_INFORMATION); + + Results = TRUE; + + try_exit: NOTHING; + } finally { + + if (FcbAcquired) { ExReleaseResourceLite( Fcb->Header.Resource ); } + + FsRtlExitFileSystem(); + } + + // + // And return to our caller + // + + return Results; +} + + +_Function_class_(FAST_IO_QUERY_NETWORK_OPEN_INFO) +BOOLEAN +FatFastQueryNetworkOpenInfo ( + IN PFILE_OBJECT FileObject, + IN BOOLEAN Wait, + IN 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 open 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 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 Results = FALSE; + IRP_CONTEXT IrpContext; + + TYPE_OF_OPEN TypeOfOpen; + PVCB Vcb; + PFCB Fcb; + PCCB Ccb; + + BOOLEAN FcbAcquired = FALSE; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( DeviceObject ); + + // + // Prepare the dummy irp context + // + + RtlZeroMemory( &IrpContext, sizeof(IRP_CONTEXT) ); + IrpContext.NodeTypeCode = FAT_NTC_IRP_CONTEXT; + IrpContext.NodeByteSize = sizeof(IRP_CONTEXT); + + if (Wait) { + + SetFlag(IrpContext.Flags, IRP_CONTEXT_FLAG_WAIT); + + } else { + + ClearFlag(IrpContext.Flags, IRP_CONTEXT_FLAG_WAIT); + } + + // + // Determine the type of open for the input file object and only accept + // the user file or directory open + // + + TypeOfOpen = FatDecodeFileObject( FileObject, &Vcb, &Fcb, &Ccb ); + + if ((TypeOfOpen != UserFileOpen) && (TypeOfOpen != UserDirectoryOpen)) { + + return Results; + } + + FsRtlEnterFileSystem(); + + // + // Get access to the Fcb but only if it is not the paging file + // + + if (!FlagOn( Fcb->FcbState, FCB_STATE_PAGING_FILE )) { + + if (!ExAcquireResourceSharedLite( Fcb->Header.Resource, Wait )) { + + FsRtlExitFileSystem(); + return Results; + } + + FcbAcquired = TRUE; + } + + try { + + // + // If the Fcb is not in a good state, return FALSE. + // + + if (Fcb->FcbCondition != FcbGood) { + + try_return( Results ); + } + + // + // Extract the data and fill in the non zero fields of the output + // buffer + // + + // + // Default the field we don't support to a reasonable value. + // + + ExLocalTimeToSystemTime( &FatJanOne1980, + &Buffer->ChangeTime ); + + Buffer->FileAttributes = Fcb->DirentFatFlags; + + if (Fcb->Header.NodeTypeCode == FAT_NTC_ROOT_DCB) { + + // + // Reuse the default for the root dir. + // + + Buffer->CreationTime = + Buffer->LastAccessTime = + Buffer->LastWriteTime = Buffer->ChangeTime; + + } else { + + Buffer->LastWriteTime = Fcb->LastWriteTime; + Buffer->CreationTime = Fcb->CreationTime; + Buffer->LastAccessTime = Fcb->LastAccessTime; + + } + + // + // If the temporary flag is set, then set it in the buffer. + // + + if (FlagOn( Fcb->FcbState, FCB_STATE_TEMPORARY )) { + + SetFlag( Buffer->FileAttributes, FILE_ATTRIBUTE_TEMPORARY ); + } + + + + // + // If no attributes were set, set the normal bit. + // + + if (Buffer->FileAttributes == 0) { + + Buffer->FileAttributes = FILE_ATTRIBUTE_NORMAL; + } + + if (NodeType(Fcb) == FAT_NTC_FCB) { + + // + // If we don't already know the allocation size, we cannot + // lock it up in the fast path. + // + + if (Fcb->Header.AllocationSize.QuadPart == FCB_LOOKUP_ALLOCATIONSIZE_HINT) { + + try_return( Results ); + } + + Buffer->AllocationSize = Fcb->Header.AllocationSize; + Buffer->EndOfFile = Fcb->Header.FileSize; + + } else { + + Buffer->AllocationSize = FatLargeZero; + Buffer->EndOfFile = FatLargeZero; + } + + IoStatus->Status = STATUS_SUCCESS; + IoStatus->Information = sizeof(FILE_NETWORK_OPEN_INFORMATION); + + Results = TRUE; + + try_exit: NOTHING; + } finally { + + if (FcbAcquired) { ExReleaseResourceLite( Fcb->Header.Resource ); } + + FsRtlExitFileSystem(); + } + + // + // And return to our caller + // + + return Results; +} + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatPopUpFileCorrupt ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb + ) + +/*++ + +Routine Description: + + The Following routine makes an informational popup that the file + is corrupt. + +Arguments: + + Fcb - The file that is corrupt. + +Return Value: + + None. + +--*/ + +{ + PKTHREAD Thread; + + PAGED_CODE(); + + // + // Disable the popup on the root directory. It is important not + // to generate them on objects which are part of the mount process. + // + + if (NodeType(Fcb) == FAT_NTC_ROOT_DCB) { + + return; + } + + // + // Got to grab the full filename now. + // + + if (Fcb->FullFileName.Buffer == NULL) { + + FatSetFullFileNameInFcb( IrpContext, Fcb ); + } + + // + // We never want to block a system thread waiting for the user to + // press OK. + // + + if (IoIsSystemThread(IrpContext->OriginatingIrp->Tail.Overlay.Thread)) { + + Thread = NULL; + + } else { + + Thread = IrpContext->OriginatingIrp->Tail.Overlay.Thread; + } + + IoRaiseInformationalHardError( STATUS_FILE_CORRUPT_ERROR, + &Fcb->FullFileName, + Thread); +} + diff --git a/filesys/fastfat/fatdata.h b/filesys/fastfat/fatdata.h new file mode 100644 index 000000000..c6dd20c0f --- /dev/null +++ b/filesys/fastfat/fatdata.h @@ -0,0 +1,333 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + FatData.c + +Abstract: + + This module declares the global data used by the Fat file system. + + +--*/ + +#ifndef _FATDATA_ +#define _FATDATA_ + +// +// The global fsd data record, and a global zero large integer +// + +extern FAT_DATA FatData; + +extern IO_STATUS_BLOCK FatGarbageIosb; + +extern NPAGED_LOOKASIDE_LIST FatIrpContextLookasideList; +extern NPAGED_LOOKASIDE_LIST FatNonPagedFcbLookasideList; +extern NPAGED_LOOKASIDE_LIST FatEResourceLookasideList; + +extern SLIST_HEADER FatCloseContextSList; +extern FAST_MUTEX FatCloseQueueMutex; + +extern PDEVICE_OBJECT FatDiskFileSystemDeviceObject; +extern PDEVICE_OBJECT FatCdromFileSystemDeviceObject; + +extern LARGE_INTEGER FatLargeZero; +extern LARGE_INTEGER FatMaxLarge; +extern LARGE_INTEGER Fat30Milliseconds; +extern LARGE_INTEGER Fat100Milliseconds; +extern LARGE_INTEGER FatOneSecond; +extern LARGE_INTEGER FatOneDay; +extern LARGE_INTEGER FatJanOne1980; +extern LARGE_INTEGER FatDecThirtyOne1979; + +extern FAT_TIME_STAMP FatTimeJanOne1980; + +extern LARGE_INTEGER FatMagic10000; +#define FAT_SHIFT10000 13 + +extern LARGE_INTEGER FatMagic86400000; +#define FAT_SHIFT86400000 26 + +#define FatConvert100nsToMilliseconds(LARGE_INTEGER) ( \ + RtlExtendedMagicDivide( (LARGE_INTEGER), FatMagic10000, FAT_SHIFT10000 )\ + ) + +#define FatConvertMillisecondsToDays(LARGE_INTEGER) ( \ + RtlExtendedMagicDivide( (LARGE_INTEGER), FatMagic86400000, FAT_SHIFT86400000 ) \ + ) + +#define FatConvertDaysToMilliseconds(DAYS) ( \ + Int32x32To64( (DAYS), 86400000 ) \ + ) + +// +// Reserve MDL for paging file io forward progress. +// + +#define FAT_RESERVE_MDL_SIZE 16 + +__volatile extern PMDL FatReserveMdl; +extern KEVENT FatReserveEvent; + +// +// The global structure used to contain our fast I/O callbacks +// + +extern FAST_IO_DISPATCH FatFastIoDispatch; + +// +// Global to store disk IO accounting Enabled/Disabled state +// + +extern LOGICAL FatDiskAccountingEnabled; + + +// +// Read ahead amount used for normal data files +// + +#define READ_AHEAD_GRANULARITY (0x10000) + +// +// Define maximum number of parallel Reads or Writes that will be generated +// per one request. +// + +#define FAT_MAX_IO_RUNS_ON_STACK ((ULONG) 5) + +// +// Define the maximum number of delayed closes. +// + +#define FAT_MAX_DELAYED_CLOSES ((ULONG)16) + +extern ULONG FatMaxDelayedCloseCount; + +// +// The maximum chunk size we use when defragmenting files. +// + +#define FAT_DEFAULT_DEFRAG_CHUNK_IN_BYTES (0x10000) + +// +// Define constant for time rounding. +// + +#define TenMSec (10*1000*10) +#define TwoSeconds (2*1000*1000*10) +#define AlmostTenMSec (TenMSec - 1) +#define AlmostTwoSeconds (TwoSeconds - 1) + +// too big #define HighPartPerDay (24*60*60*1000*1000*10 >> 32) + +#define HighPartPerDay (52734375 >> 18) + +// +// The global Fat debug level variable, its values are: +// +// 0x00000000 Always gets printed (used when about to bug check) +// +// 0x00000001 Error conditions +// 0x00000002 Debug hooks +// 0x00000004 Catch exceptions before completing Irp +// 0x00000008 +// +// 0x00000010 +// 0x00000020 +// 0x00000040 +// 0x00000080 +// +// 0x00000100 +// 0x00000200 +// 0x00000400 +// 0x00000800 +// +// 0x00001000 +// 0x00002000 +// 0x00004000 +// 0x00008000 +// +// 0x00010000 +// 0x00020000 +// 0x00040000 +// 0x00080000 +// +// 0x00100000 +// 0x00200000 +// 0x00400000 +// 0x00800000 +// +// 0x01000000 +// 0x02000000 +// 0x04000000 +// 0x08000000 +// +// 0x10000000 +// 0x20000000 +// 0x40000000 +// 0x80000000 +// + + +#ifdef FASTFATDBG + +#define DEBUG_TRACE_ERROR (0x00000001) +#define DEBUG_TRACE_DEBUG_HOOKS (0x00000002) +#define DEBUG_TRACE_CATCH_EXCEPTIONS (0x00000004) +#define DEBUG_TRACE_UNWIND (0x00000008) +#define DEBUG_TRACE_CLEANUP (0x00000010) +#define DEBUG_TRACE_CLOSE (0x00000020) +#define DEBUG_TRACE_CREATE (0x00000040) +#define DEBUG_TRACE_DIRCTRL (0x00000080) +#define DEBUG_TRACE_EA (0x00000100) +#define DEBUG_TRACE_FILEINFO (0x00000200) +#define DEBUG_TRACE_FSCTRL (0x00000400) +#define DEBUG_TRACE_LOCKCTRL (0x00000800) +#define DEBUG_TRACE_READ (0x00001000) +#define DEBUG_TRACE_VOLINFO (0x00002000) +#define DEBUG_TRACE_WRITE (0x00004000) +#define DEBUG_TRACE_FLUSH (0x00008000) +#define DEBUG_TRACE_DEVCTRL (0x00010000) +#define DEBUG_TRACE_SHUTDOWN (0x00020000) +#define DEBUG_TRACE_FATDATA (0x00040000) +#define DEBUG_TRACE_PNP (0x00080000) +#define DEBUG_TRACE_ACCHKSUP (0x00100000) +#define DEBUG_TRACE_ALLOCSUP (0x00200000) +#define DEBUG_TRACE_DIRSUP (0x00400000) +#define DEBUG_TRACE_FILOBSUP (0x00800000) +#define DEBUG_TRACE_NAMESUP (0x01000000) +#define DEBUG_TRACE_VERFYSUP (0x02000000) +#define DEBUG_TRACE_CACHESUP (0x04000000) +#define DEBUG_TRACE_SPLAYSUP (0x08000000) +#define DEBUG_TRACE_DEVIOSUP (0x10000000) +#define DEBUG_TRACE_STRUCSUP (0x20000000) +#define DEBUG_TRACE_FSP_DISPATCHER (0x40000000) +#define DEBUG_TRACE_FSP_DUMP (0x80000000) + +extern LONG FatDebugTraceLevel; +extern LONG FatDebugTraceIndent; + +#define DebugTrace(INDENT,LEVEL,X,Y) { \ + LONG _i; \ + if (((LEVEL) == 0) || (FatDebugTraceLevel & (LEVEL))) { \ + _i = (ULONG)PsGetCurrentThread(); \ + DbgPrint("%08lx:",_i); \ + if ((INDENT) < 0) { \ + FatDebugTraceIndent += (INDENT); \ + } \ + if (FatDebugTraceIndent < 0) { \ + FatDebugTraceIndent = 0; \ + } \ + for (_i = 0; _i < FatDebugTraceIndent; _i += 1) { \ + DbgPrint(" "); \ + } \ + DbgPrint(X,Y); \ + if ((INDENT) > 0) { \ + FatDebugTraceIndent += (INDENT); \ + } \ + } \ +} + +#define DebugDump(STR,LEVEL,PTR) { \ + __pragma(warning(push)) \ + __pragma(warning(disable:4210)) \ + ULONG _i; \ + VOID FatDump(IN PVOID Ptr); \ + if (((LEVEL) == 0) || (FatDebugTraceLevel & (LEVEL))) { \ + _i = (ULONG)PsGetCurrentThread(); \ + DbgPrint("%08lx:",_i); \ + DbgPrint(STR); \ + if (PTR != NULL) {FatDump(PTR);} \ + NT_ASSERT(FALSE); \ + } \ + __pragma(warning(pop)) \ +} + +#define DebugUnwind(X) { \ + if (AbnormalTermination()) { \ + DebugTrace(0, DEBUG_TRACE_UNWIND, #X ", Abnormal termination.\n", 0); \ + } \ +} + +// +// The following variables are used to keep track of the total amount +// of requests processed by the file system, and the number of requests +// that end up being processed by the Fsp thread. The first variable +// is incremented whenever an Irp context is created (which is always +// at the start of an Fsd entry point) and the second is incremented +// by read request. +// + +extern ULONG FatFsdEntryCount; +extern ULONG FatFspEntryCount; +extern ULONG FatIoCallDriverCount; +extern ULONG FatTotalTicks[]; + +#define DebugDoit(X) {X;} + +extern LONG FatPerformanceTimerLevel; + +#define TimerStart(LEVEL) { \ + LARGE_INTEGER TStart, TEnd; \ + LARGE_INTEGER TElapsed; \ + TStart = KeQueryPerformanceCounter( NULL ); \ + +#define TimerStop(LEVEL,s) \ + TEnd = KeQueryPerformanceCounter( NULL ); \ + TElapsed.QuadPart = TEnd.QuadPart - TStart.QuadPart; \ + FatTotalTicks[FatLogOf(LEVEL)] += TElapsed.LowPart; \ + if (FlagOn( FatPerformanceTimerLevel, (LEVEL))) { \ + DbgPrint("Time of %s %ld\n", (s), TElapsed.LowPart ); \ + } \ +} + +// +// I need this because C can't support conditional compilation within +// a macro. +// + +extern PVOID FatNull; +extern NTSTATUS FatInterestingException; + +#else + +#define DebugTrace(INDENT,LEVEL,X,Y) {NOTHING;} +#define DebugDump(STR,LEVEL,PTR) {NOTHING;} +#define DebugUnwind(X) {NOTHING;} +#define DebugDoit(X) {NOTHING;} + +#define TimerStart(LEVEL) +#define TimerStop(LEVEL,s) + +#define FatNull NULL + +#endif // FASTFATDBG + +// +// The following macro is for all people who compile with the DBG switch +// set, not just fastfat dbg users +// + +#if DBG + +#define DbgDoit(X) {X;} + +#else + +#define DbgDoit(X) {NOTHING;} + +#endif // DBG + +#if DBG + +extern NTSTATUS FatAssertNotStatus; +extern BOOLEAN FatTestRaisedStatus; + +#endif + +#endif // _FATDATA_ + + diff --git a/filesys/fastfat/fatinit.c b/filesys/fastfat/fatinit.c new file mode 100644 index 000000000..a7c6342ed --- /dev/null +++ b/filesys/fastfat/fatinit.c @@ -0,0 +1,739 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + FatInit.c + +Abstract: + + This module implements the DRIVER_INITIALIZATION routine for Fat + + +--*/ + +#include "FatProcs.h" + +DRIVER_INITIALIZE DriverEntry; + +NTSTATUS +DriverEntry( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ); + +_Function_class_(DRIVER_UNLOAD) +VOID +FatUnload( + _In_ _Unreferenced_parameter_ PDRIVER_OBJECT DriverObject + ); + +NTSTATUS +FatGetCompatibilityModeValue( + IN PUNICODE_STRING ValueName, + IN OUT PULONG Value + ); + +BOOLEAN +FatIsFujitsuFMR ( + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT, DriverEntry) +#pragma alloc_text(INIT, FatGetCompatibilityModeValue) +#pragma alloc_text(INIT, FatIsFujitsuFMR) +//#pragma alloc_text(PAGE, FatUnload) +#endif + +#define COMPATIBILITY_MODE_KEY_NAME L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\FileSystem" +#define COMPATIBILITY_MODE_VALUE_NAME L"Win31FileSystem" +#define CODE_PAGE_INVARIANCE_VALUE_NAME L"FatDisableCodePageInvariance" + + +#define KEY_WORK_AREA ((sizeof(KEY_VALUE_FULL_INFORMATION) + \ + sizeof(ULONG)) + 64) + +#define REGISTRY_HARDWARE_DESCRIPTION_W \ + L"\\Registry\\Machine\\Hardware\\DESCRIPTION\\System" + +#define REGISTRY_MACHINE_IDENTIFIER_W L"Identifier" + +#define FUJITSU_FMR_NAME_W L"FUJITSU FMR-" + + + +NTSTATUS +DriverEntry( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ) + +/*++ + +Routine Description: + + This is the initialization routine for the Fat 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. + +--*/ + +{ + USHORT MaxDepth; + NTSTATUS Status; + UNICODE_STRING UnicodeString; + FS_FILTER_CALLBACKS FilterCallbacks; + UNICODE_STRING ValueName; + ULONG Value; + + UNREFERENCED_PARAMETER( RegistryPath ); + + // + // Create the device object for disks. To avoid problems with filters who + // know this name, we must keep it. + // + + RtlInitUnicodeString( &UnicodeString, L"\\Fat" ); + Status = IoCreateDevice( DriverObject, + 0, + &UnicodeString, + FILE_DEVICE_DISK_FILE_SYSTEM, + 0, + FALSE, + &FatDiskFileSystemDeviceObject ); + + if (!NT_SUCCESS( Status )) { + return Status; + } + + // + // Create the device object for "cdroms". + // + + RtlInitUnicodeString( &UnicodeString, L"\\FatCdrom" ); + Status = IoCreateDevice( DriverObject, + 0, + &UnicodeString, + FILE_DEVICE_CD_ROM_FILE_SYSTEM, + 0, + FALSE, + &FatCdromFileSystemDeviceObject ); + + if (!NT_SUCCESS( Status )) { + IoDeleteDevice( FatDiskFileSystemDeviceObject); + return Status; + } + +#pragma prefast( push ) +#pragma prefast( disable:28155, "these are all correct" ) +#pragma prefast( disable:28169, "these are all correct" ) +#pragma prefast( disable:28175, "this is a filesystem, touching FastIoDispatch is allowed" ) + + DriverObject->DriverUnload = FatUnload; + + // + // 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. + // + + DriverObject->MajorFunction[IRP_MJ_CREATE] = (PDRIVER_DISPATCH)FatFsdCreate; + DriverObject->MajorFunction[IRP_MJ_CLOSE] = (PDRIVER_DISPATCH)FatFsdClose; + DriverObject->MajorFunction[IRP_MJ_READ] = (PDRIVER_DISPATCH)FatFsdRead; + DriverObject->MajorFunction[IRP_MJ_WRITE] = (PDRIVER_DISPATCH)FatFsdWrite; + DriverObject->MajorFunction[IRP_MJ_QUERY_INFORMATION] = (PDRIVER_DISPATCH)FatFsdQueryInformation; + DriverObject->MajorFunction[IRP_MJ_SET_INFORMATION] = (PDRIVER_DISPATCH)FatFsdSetInformation; + DriverObject->MajorFunction[IRP_MJ_QUERY_EA] = (PDRIVER_DISPATCH)FatFsdQueryEa; + DriverObject->MajorFunction[IRP_MJ_SET_EA] = (PDRIVER_DISPATCH)FatFsdSetEa; + DriverObject->MajorFunction[IRP_MJ_FLUSH_BUFFERS] = (PDRIVER_DISPATCH)FatFsdFlushBuffers; + DriverObject->MajorFunction[IRP_MJ_QUERY_VOLUME_INFORMATION] = (PDRIVER_DISPATCH)FatFsdQueryVolumeInformation; + DriverObject->MajorFunction[IRP_MJ_SET_VOLUME_INFORMATION] = (PDRIVER_DISPATCH)FatFsdSetVolumeInformation; + DriverObject->MajorFunction[IRP_MJ_CLEANUP] = (PDRIVER_DISPATCH)FatFsdCleanup; + DriverObject->MajorFunction[IRP_MJ_DIRECTORY_CONTROL] = (PDRIVER_DISPATCH)FatFsdDirectoryControl; + DriverObject->MajorFunction[IRP_MJ_FILE_SYSTEM_CONTROL] = (PDRIVER_DISPATCH)FatFsdFileSystemControl; + DriverObject->MajorFunction[IRP_MJ_LOCK_CONTROL] = (PDRIVER_DISPATCH)FatFsdLockControl; + DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = (PDRIVER_DISPATCH)FatFsdDeviceControl; + DriverObject->MajorFunction[IRP_MJ_SHUTDOWN] = (PDRIVER_DISPATCH)FatFsdShutdown; + DriverObject->MajorFunction[IRP_MJ_PNP] = (PDRIVER_DISPATCH)FatFsdPnp; + + DriverObject->FastIoDispatch = &FatFastIoDispatch; + + RtlZeroMemory(&FatFastIoDispatch, sizeof(FatFastIoDispatch)); + + FatFastIoDispatch.SizeOfFastIoDispatch = sizeof(FAST_IO_DISPATCH); + FatFastIoDispatch.FastIoCheckIfPossible = FatFastIoCheckIfPossible; // CheckForFastIo + FatFastIoDispatch.FastIoRead = FsRtlCopyRead; // Read + FatFastIoDispatch.FastIoWrite = FsRtlCopyWrite; // Write + FatFastIoDispatch.FastIoQueryBasicInfo = FatFastQueryBasicInfo; // QueryBasicInfo + FatFastIoDispatch.FastIoQueryStandardInfo = FatFastQueryStdInfo; // QueryStandardInfo + FatFastIoDispatch.FastIoLock = FatFastLock; // Lock + FatFastIoDispatch.FastIoUnlockSingle = FatFastUnlockSingle; // UnlockSingle + FatFastIoDispatch.FastIoUnlockAll = FatFastUnlockAll; // UnlockAll + FatFastIoDispatch.FastIoUnlockAllByKey = FatFastUnlockAllByKey; // UnlockAllByKey + FatFastIoDispatch.FastIoQueryNetworkOpenInfo = FatFastQueryNetworkOpenInfo; + FatFastIoDispatch.AcquireForCcFlush = FatAcquireForCcFlush; + FatFastIoDispatch.ReleaseForCcFlush = FatReleaseForCcFlush; + FatFastIoDispatch.MdlRead = FsRtlMdlReadDev; + FatFastIoDispatch.MdlReadComplete = FsRtlMdlReadCompleteDev; + FatFastIoDispatch.PrepareMdlWrite = FsRtlPrepareMdlWriteDev; + FatFastIoDispatch.MdlWriteComplete = FsRtlMdlWriteCompleteDev; + +#pragma prefast( pop ) + + // + // Initialize the filter callbacks we use. + // + + RtlZeroMemory( &FilterCallbacks, + sizeof(FS_FILTER_CALLBACKS) ); + + FilterCallbacks.SizeOfFsFilterCallbacks = sizeof(FS_FILTER_CALLBACKS); + FilterCallbacks.PreAcquireForSectionSynchronization = FatFilterCallbackAcquireForCreateSection; + + Status = FsRtlRegisterFileSystemFilterCallbacks( DriverObject, + &FilterCallbacks ); + + if (!NT_SUCCESS( Status )) { + + IoDeleteDevice( FatDiskFileSystemDeviceObject ); + IoDeleteDevice( FatCdromFileSystemDeviceObject ); + return Status; + } + + // + // Initialize the global data structures + // + + // + // The FatData record + // + + RtlZeroMemory( &FatData, sizeof(FAT_DATA)); + + FatData.NodeTypeCode = FAT_NTC_DATA_HEADER; + FatData.NodeByteSize = sizeof(FAT_DATA); + + InitializeListHead(&FatData.VcbQueue); + + FatData.DriverObject = DriverObject; + FatData.DiskFileSystemDeviceObject = FatDiskFileSystemDeviceObject; + FatData.CdromFileSystemDeviceObject = FatCdromFileSystemDeviceObject; + + // + // This list head keeps track of closes yet to be done. + // + + InitializeListHead( &FatData.AsyncCloseList ); + InitializeListHead( &FatData.DelayedCloseList ); + + FatData.FatCloseItem = IoAllocateWorkItem( FatDiskFileSystemDeviceObject); + + if (FatData.FatCloseItem == NULL) { + IoDeleteDevice (FatDiskFileSystemDeviceObject); + IoDeleteDevice (FatCdromFileSystemDeviceObject); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // Allocate the zero page + // + + FatData.ZeroPage = ExAllocatePoolWithTag( NonPagedPoolNx, PAGE_SIZE, 'ZtaF' ); + if (FatData.ZeroPage == NULL) { + IoDeleteDevice (FatDiskFileSystemDeviceObject); + IoDeleteDevice (FatCdromFileSystemDeviceObject); + return STATUS_INSUFFICIENT_RESOURCES; + } + RtlZeroMemory( FatData.ZeroPage, PAGE_SIZE ); + + + // + // Now initialize our general purpose spinlock (gag) and figure out how + // deep and wide we want our delayed lists (along with fooling ourselves + // about the lookaside depths). + // + + KeInitializeSpinLock( &FatData.GeneralSpinLock ); + + switch ( MmQuerySystemSize() ) { + + case MmSmallSystem: + + MaxDepth = 4; + FatMaxDelayedCloseCount = FAT_MAX_DELAYED_CLOSES; + break; + + case MmMediumSystem: + + MaxDepth = 8; + FatMaxDelayedCloseCount = 4 * FAT_MAX_DELAYED_CLOSES; + break; + + case MmLargeSystem: + default: + + MaxDepth = 16; + FatMaxDelayedCloseCount = 16 * FAT_MAX_DELAYED_CLOSES; + break; + } + + + // + // Initialize the cache manager callback routines + // + + FatData.CacheManagerCallbacks.AcquireForLazyWrite = &FatAcquireFcbForLazyWrite; + FatData.CacheManagerCallbacks.ReleaseFromLazyWrite = &FatReleaseFcbFromLazyWrite; + FatData.CacheManagerCallbacks.AcquireForReadAhead = &FatAcquireFcbForReadAhead; + FatData.CacheManagerCallbacks.ReleaseFromReadAhead = &FatReleaseFcbFromReadAhead; + + FatData.CacheManagerNoOpCallbacks.AcquireForLazyWrite = &FatNoOpAcquire; + FatData.CacheManagerNoOpCallbacks.ReleaseFromLazyWrite = &FatNoOpRelease; + FatData.CacheManagerNoOpCallbacks.AcquireForReadAhead = &FatNoOpAcquire; + FatData.CacheManagerNoOpCallbacks.ReleaseFromReadAhead = &FatNoOpRelease; + + // + // Set up global pointer to our process. + // + + FatData.OurProcess = PsGetCurrentProcess(); + + // + // Setup the number of processors we support for statistics as the current number + // running. + // + +#if (NTDDI_VERSION >= NTDDI_VISTA) + FatData.NumberProcessors = KeQueryActiveProcessorCount( NULL ); +#else + FatData.NumberProcessors = KeNumberProcessors; +#endif + + + // + // Read the registry to determine if we are in ChicagoMode. + // + + ValueName.Buffer = COMPATIBILITY_MODE_VALUE_NAME; + ValueName.Length = sizeof(COMPATIBILITY_MODE_VALUE_NAME) - sizeof(WCHAR); + ValueName.MaximumLength = sizeof(COMPATIBILITY_MODE_VALUE_NAME); + + Status = FatGetCompatibilityModeValue( &ValueName, &Value ); + + if (NT_SUCCESS(Status) && FlagOn(Value, 1)) { + + FatData.ChicagoMode = FALSE; + + } else { + + FatData.ChicagoMode = TRUE; + } + + // + // Read the registry to determine if we are going to generate LFNs + // for valid 8.3 names with extended characters. + // + + ValueName.Buffer = CODE_PAGE_INVARIANCE_VALUE_NAME; + ValueName.Length = sizeof(CODE_PAGE_INVARIANCE_VALUE_NAME) - sizeof(WCHAR); + ValueName.MaximumLength = sizeof(CODE_PAGE_INVARIANCE_VALUE_NAME); + + Status = FatGetCompatibilityModeValue( &ValueName, &Value ); + + if (NT_SUCCESS(Status) && FlagOn(Value, 1)) { + + FatData.CodePageInvariant = FALSE; + + } else { + + FatData.CodePageInvariant = TRUE; + } + + // + // Initialize our global resource and fire up the lookaside lists. + // + + ExInitializeResourceLite( &FatData.Resource ); + + ExInitializeNPagedLookasideList( &FatIrpContextLookasideList, + NULL, + NULL, + POOL_NX_ALLOCATION | POOL_RAISE_IF_ALLOCATION_FAILURE, + sizeof(IRP_CONTEXT), + TAG_IRP_CONTEXT, + MaxDepth ); + + ExInitializeNPagedLookasideList( &FatNonPagedFcbLookasideList, + NULL, + NULL, + POOL_NX_ALLOCATION | POOL_RAISE_IF_ALLOCATION_FAILURE, + sizeof(NON_PAGED_FCB), + TAG_FCB_NONPAGED, + MaxDepth ); + + ExInitializeNPagedLookasideList( &FatEResourceLookasideList, + NULL, + NULL, + POOL_NX_ALLOCATION | POOL_RAISE_IF_ALLOCATION_FAILURE, + sizeof(ERESOURCE), + TAG_ERESOURCE, + MaxDepth ); + + ExInitializeSListHead( &FatCloseContextSList ); + ExInitializeFastMutex( &FatCloseQueueMutex ); + KeInitializeEvent( &FatReserveEvent, SynchronizationEvent, TRUE ); + + // + // Register the file system with the I/O system + // + + IoRegisterFileSystem(FatDiskFileSystemDeviceObject); + ObReferenceObject (FatDiskFileSystemDeviceObject); + IoRegisterFileSystem(FatCdromFileSystemDeviceObject); + ObReferenceObject (FatCdromFileSystemDeviceObject); + + // + // Find out if we are running an a FujitsuFMR machine. + // + + FatData.FujitsuFMR = FatIsFujitsuFMR(); + +#if (NTDDI_VERSION >= NTDDI_WIN8) + + // + // Find out global disk accounting state, cache the result + // + + FatDiskAccountingEnabled = PsIsDiskCountersEnabled(); + +#endif + + // + // And return to our caller + // + + return( STATUS_SUCCESS ); +} + + +_Function_class_(DRIVER_UNLOAD) +VOID +FatUnload( + _In_ _Unreferenced_parameter_ PDRIVER_OBJECT DriverObject + ) + +/*++ + +Routine Description: + + This is the unload routine for the filesystem + +Arguments: + + DriverObject - Pointer to driver object created by the system. + +Return Value: + + None + +--*/ + +{ + UNREFERENCED_PARAMETER( DriverObject ); + + + ExDeleteNPagedLookasideList (&FatEResourceLookasideList); + ExDeleteNPagedLookasideList (&FatNonPagedFcbLookasideList); + ExDeleteNPagedLookasideList (&FatIrpContextLookasideList); + ExDeleteResourceLite( &FatData.Resource ); + IoFreeWorkItem (FatData.FatCloseItem); + ObDereferenceObject( FatDiskFileSystemDeviceObject); + ObDereferenceObject( FatCdromFileSystemDeviceObject); +} + + +// +// Local Support routine +// + +NTSTATUS +FatGetCompatibilityModeValue ( + IN PUNICODE_STRING ValueName, + IN OUT PULONG Value + ) + +/*++ + +Routine Description: + + Given a unicode value name this routine will go into the registry + location for the Chicago compatibilitymode information and get the + value. + +Arguments: + + ValueName - the unicode name for the registry value located in the registry. + Value - a pointer to the ULONG for the result. + +Return Value: + + NTSTATUS + + If STATUS_SUCCESSFUL is returned, the location *Value will be + updated with the DWORD value from the registry. If any failing + status is returned, this value is untouched. + +--*/ + +{ + HANDLE Handle; + NTSTATUS Status; + ULONG RequestLength; + ULONG ResultLength; + UCHAR Buffer[KEY_WORK_AREA]; + UNICODE_STRING KeyName; + OBJECT_ATTRIBUTES ObjectAttributes; + PKEY_VALUE_FULL_INFORMATION KeyValueInformation; + + KeyName.Buffer = COMPATIBILITY_MODE_KEY_NAME; + KeyName.Length = sizeof(COMPATIBILITY_MODE_KEY_NAME) - sizeof(WCHAR); + KeyName.MaximumLength = sizeof(COMPATIBILITY_MODE_KEY_NAME); + + InitializeObjectAttributes(&ObjectAttributes, + &KeyName, + OBJ_CASE_INSENSITIVE, + NULL, + NULL); + + Status = ZwOpenKey(&Handle, + KEY_READ, + &ObjectAttributes); + + if (!NT_SUCCESS(Status)) { + + return Status; + } + + RequestLength = KEY_WORK_AREA; + + KeyValueInformation = (PKEY_VALUE_FULL_INFORMATION)Buffer; + + while (1) { + + Status = ZwQueryValueKey(Handle, + ValueName, + KeyValueFullInformation, + KeyValueInformation, + RequestLength, + &ResultLength); + + NT_ASSERT( Status != STATUS_BUFFER_OVERFLOW ); + + if (Status == STATUS_BUFFER_OVERFLOW) { + + // + // Try to get a buffer big enough. + // + + if (KeyValueInformation != (PKEY_VALUE_FULL_INFORMATION)Buffer) { + + ExFreePool(KeyValueInformation); + } + + RequestLength += 256; + + KeyValueInformation = (PKEY_VALUE_FULL_INFORMATION) + ExAllocatePoolWithTag(PagedPool, + RequestLength, + ' taF'); + + if (!KeyValueInformation) { + + ZwClose(Handle); + return STATUS_NO_MEMORY; + } + + } else { + + break; + } + } + + ZwClose(Handle); + + if (NT_SUCCESS(Status)) { + + if (KeyValueInformation->DataLength != 0) { + + PULONG DataPtr; + + // + // Return contents to the caller. + // + + DataPtr = (PULONG) + ((PUCHAR)KeyValueInformation + KeyValueInformation->DataOffset); + *Value = *DataPtr; + + } else { + + // + // Treat as if no value was found + // + + Status = STATUS_OBJECT_NAME_NOT_FOUND; + } + } + + if (KeyValueInformation != (PKEY_VALUE_FULL_INFORMATION)Buffer) { + + ExFreePool(KeyValueInformation); + } + + return Status; +} + +// +// Local Support routine +// + +BOOLEAN +FatIsFujitsuFMR ( + ) + +/*++ + +Routine Description: + + This routine tells if is we running on a FujitsuFMR machine. + +Arguments: + + +Return Value: + + BOOLEAN - TRUE is we are and FALSE otherwise + +--*/ + +{ + BOOLEAN Result; + HANDLE Handle; + NTSTATUS Status; + ULONG RequestLength; + ULONG ResultLength; + UCHAR Buffer[KEY_WORK_AREA]; + UNICODE_STRING KeyName; + UNICODE_STRING ValueName; + OBJECT_ATTRIBUTES ObjectAttributes; + PKEY_VALUE_FULL_INFORMATION KeyValueInformation; + + // + // Set default as PC/AT + // + + KeyName.Buffer = REGISTRY_HARDWARE_DESCRIPTION_W; + KeyName.Length = sizeof(REGISTRY_HARDWARE_DESCRIPTION_W) - sizeof(WCHAR); + KeyName.MaximumLength = sizeof(REGISTRY_HARDWARE_DESCRIPTION_W); + + InitializeObjectAttributes(&ObjectAttributes, + &KeyName, + OBJ_CASE_INSENSITIVE, + NULL, + NULL); + + Status = ZwOpenKey(&Handle, + KEY_READ, + &ObjectAttributes); + + if (!NT_SUCCESS(Status)) { + + return FALSE; + } + + ValueName.Buffer = REGISTRY_MACHINE_IDENTIFIER_W; + ValueName.Length = sizeof(REGISTRY_MACHINE_IDENTIFIER_W) - sizeof(WCHAR); + ValueName.MaximumLength = sizeof(REGISTRY_MACHINE_IDENTIFIER_W); + + RequestLength = KEY_WORK_AREA; + + KeyValueInformation = (PKEY_VALUE_FULL_INFORMATION)Buffer; + + while (1) { + + Status = ZwQueryValueKey(Handle, + &ValueName, + KeyValueFullInformation, + KeyValueInformation, + RequestLength, + &ResultLength); + + // NT_ASSERT( Status != STATUS_BUFFER_OVERFLOW ); + + if (Status == STATUS_BUFFER_OVERFLOW) { + + // + // Try to get a buffer big enough. + // + + if (KeyValueInformation != (PKEY_VALUE_FULL_INFORMATION)Buffer) { + + ExFreePool(KeyValueInformation); + } + + RequestLength += 256; + + KeyValueInformation = (PKEY_VALUE_FULL_INFORMATION) + ExAllocatePoolWithTag(PagedPool, RequestLength, ' taF'); + + if (!KeyValueInformation) { + + ZwClose(Handle); + return FALSE; + } + + } else { + + break; + } + } + + ZwClose(Handle); + + if (NT_SUCCESS(Status) && + (KeyValueInformation->DataLength >= sizeof(FUJITSU_FMR_NAME_W)) && + (RtlCompareMemory((PUCHAR)KeyValueInformation + KeyValueInformation->DataOffset, + FUJITSU_FMR_NAME_W, + sizeof(FUJITSU_FMR_NAME_W) - sizeof(WCHAR)) == + sizeof(FUJITSU_FMR_NAME_W) - sizeof(WCHAR))) { + + Result = TRUE; + + } else { + + Result = FALSE; + } + + if (KeyValueInformation != (PKEY_VALUE_FULL_INFORMATION)Buffer) { + + ExFreePool(KeyValueInformation); + } + + return Result; +} + diff --git a/filesys/fastfat/fatprocs.h b/filesys/fastfat/fatprocs.h new file mode 100644 index 000000000..555e1aa69 --- /dev/null +++ b/filesys/fastfat/fatprocs.h @@ -0,0 +1,3012 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + FatProcs.h + +Abstract: + + This module defines all of the globally used procedures in the FAT + file system. + + +--*/ + +#ifndef _FATPROCS_ +#define _FATPROCS_ + +#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 +#include +#include +#include + + +#include "nodetype.h" +#include "Fat.h" +#include "Lfn.h" +#include "FatStruc.h" +#include "FatData.h" + + + +#pragma warning( pop ) + +#ifndef INLINE +#define INLINE __inline +#endif + +#define Add2Ptr(P,I) ((PVOID)((PUCHAR)(P) + (I))) + +#ifndef MAX_ULONG +#define MAX_ULONG ((ULONG)-1) +#endif + +// +// We must explicitly tag our allocations. +// + +#undef FsRtlAllocatePool +#undef FsRtlAllocatePoolWithQuota + +// +// A function that returns finished denotes if it was able to complete the +// operation (TRUE) or could not complete the operation (FALSE) because the +// wait value stored in the irp context was false and we would have had +// to block for a resource or I/O +// + +typedef BOOLEAN FINISHED; + +// +// Size (characters) of stack allocated name component buffers in +// the create/rename paths. +// + +#define FAT_CREATE_INITIAL_NAME_BUF_SIZE 32 + + +// +// Some string buffer handling functions, implemented in strucsup.c +// + +VOID +FatFreeStringBuffer ( + _Inout_ PVOID String + ); + +VOID +FatExtendString( + _Inout_ PVOID String, + _In_ USHORT DesiredBufferSize, + _In_ BOOLEAN FreeOldBuffer, + __out_opt PBOOLEAN NeedsFree + ); + +VOID +FatEnsureStringBufferEnough ( + _Inout_ PVOID String, + _In_ USHORT DesiredBufferSize + ); + +BOOLEAN +FatAddMcbEntry ( + IN PVCB Vcb, + IN PLARGE_MCB Mcb, + IN VBO Vbo, + IN LBO Lbo, + IN ULONG SectorCount + ); + +BOOLEAN +FatLookupMcbEntry ( + IN PVCB Vcb, + IN PLARGE_MCB Mcb, + IN VBO Vbo, + OUT PLBO Lbo, + OUT PULONG ByteCount OPTIONAL, + OUT PULONG Index OPTIONAL + ); + +BOOLEAN +FatLookupLastMcbEntry ( + IN PVCB Vcb, + IN PLARGE_MCB Mcb, + OUT PVBO Vbo, + OUT PLBO Lbo, + OUT PULONG Index OPTIONAL + ); + +BOOLEAN +FatGetNextMcbEntry ( + IN PVCB Vcb, + IN PLARGE_MCB Mcb, + IN ULONG RunIndex, + OUT PVBO Vbo, + OUT PLBO Lbo, + OUT PULONG ByteCount + ); + +VOID +FatRemoveMcbEntry ( + IN PVCB Vcb, + IN PLARGE_MCB Mcb, + IN VBO Vbo, + IN ULONG SectorCount + ); + + +// +// File access check routine, implemented in AcChkSup.c +// + +BOOLEAN +FatCheckFileAccess ( + PIRP_CONTEXT IrpContext, + IN UCHAR DirentAttributes, + IN PACCESS_MASK DesiredAccess + ); + +BOOLEAN +FatCheckManageVolumeAccess ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PACCESS_STATE AccessState, + _In_ KPROCESSOR_MODE ProcessorMode + ); + +NTSTATUS +FatExplicitDeviceAccessGranted ( + IN PIRP_CONTEXT IrpContext, + IN PDEVICE_OBJECT DeviceObject, + IN PACCESS_STATE AccessState, + IN KPROCESSOR_MODE ProcessorMode + ); + + +// +// Allocation support routines, implemented in AllocSup.c +// + + +INLINE +BOOLEAN +FatIsIoRangeValid ( + IN PVCB Vcb, + IN LARGE_INTEGER Start, + IN ULONG Length + ) + +/*++ + +Routine Description: + + This routine enforces the restriction that object space must be + representable in 32 bits. + +Arguments: + + Vcb - the volume the range is on + + Start - starting byte (zero based) of the range + + Length - size of the range + + HeaderSize - if the file has a header + +Return Value: + + BOOLEAN - if, considering the cluster size, the neccesary size of + the object to contain the range can be represented in 32 bits. + +--*/ + +{ + + UNREFERENCED_PARAMETER( Vcb ); + + // + // The only restriction on a FAT object is that the filesize must + // fit in 32bits, i.e. <= 0xffffffff. This then implies that the + // range of valid byte offsets is [0, fffffffe]. + // + // Two phases which check for illegality + // + // - if the high 32bits are nonzero + // - if the length would cause a 32bit overflow + // + + return !(Start.HighPart || + Start.LowPart + Length < Start.LowPart); +} + + +// +// This strucure is used by FatLookupFatEntry to remember a pinned page +// of fat. +// + +typedef struct _FAT_ENUMERATION_CONTEXT { + + VBO VboOfPinnedPage; + PBCB Bcb; + PVOID PinnedPage; + +} FAT_ENUMERATION_CONTEXT, *PFAT_ENUMERATION_CONTEXT; + +VOID +FatLookupFatEntry ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN ULONG FatIndex, + IN OUT PULONG FatEntry, + IN OUT PFAT_ENUMERATION_CONTEXT Context + ); + +VOID +FatSetupAllocationSupport ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb + ); + +VOID +FatTearDownAllocationSupport ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb + ); + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatLookupFileAllocation ( + IN PIRP_CONTEXT IrpContext, + IN PFCB FcbOrDcb, + IN VBO Vbo, + OUT PLBO Lbo, + OUT PULONG ByteCount, + OUT PBOOLEAN Allocated, + OUT PBOOLEAN EndOnMax, + OUT PULONG Index OPTIONAL + ); + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatAddFileAllocation ( + IN PIRP_CONTEXT IrpContext, + IN PFCB FcbOrDcb, + IN PFILE_OBJECT FileObject OPTIONAL, + IN ULONG AllocationSize + ); + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatTruncateFileAllocation ( + IN PIRP_CONTEXT IrpContext, + IN PFCB FcbOrDcb, + IN ULONG AllocationSize, + IN BOOLEAN ForDeletion + ); + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatLookupFileAllocationSize ( + IN PIRP_CONTEXT IrpContext, + IN PFCB FcbOrDcb + ); + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatAllocateDiskSpace ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN ULONG AbsoluteClusterHint, + IN OUT PULONG ByteCount, + IN BOOLEAN ExactMatchRequired, + OUT PLARGE_MCB Mcb + ); + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatDeallocateDiskSpace ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PLARGE_MCB Mcb, + IN BOOLEAN ZeroOnDeallocate + ); + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatSplitAllocation ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN OUT PLARGE_MCB Mcb, + IN VBO SplitAtVbo, + OUT PLARGE_MCB RemainingMcb + ); + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatMergeAllocation ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN OUT PLARGE_MCB Mcb, + IN PLARGE_MCB SecondMcb + ); + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatSetFatEntry ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN ULONG FatIndex, + IN FAT_ENTRY FatEntry + ); + +UCHAR +FatLogOf( + IN ULONG Value + ); + + +// +// Buffer control routines for data caching, implemented in CacheSup.c +// + +VOID +FatReadVolumeFile ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN VBO StartingVbo, + IN ULONG ByteCount, + OUT PBCB *Bcb, + OUT PVOID *Buffer + ); + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatPrepareWriteVolumeFile ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN VBO StartingVbo, + IN ULONG ByteCount, + OUT PBCB *Bcb, + OUT PVOID *Buffer, + IN BOOLEAN Reversible, + IN BOOLEAN Zero + ); + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatReadDirectoryFile ( + IN PIRP_CONTEXT IrpContext, + IN PDCB Dcb, + IN VBO StartingVbo, + IN ULONG ByteCount, + IN BOOLEAN Pin, + OUT PBCB *Bcb, + OUT PVOID *Buffer, + OUT PNTSTATUS Status + ); + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatPrepareWriteDirectoryFile ( + IN PIRP_CONTEXT IrpContext, + IN PDCB Dcb, + IN VBO StartingVbo, + IN ULONG ByteCount, + OUT PBCB *Bcb, + OUT PVOID *Buffer, + IN BOOLEAN Zero, + IN BOOLEAN Reversible, + OUT PNTSTATUS Status + ); + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatOpenDirectoryFile ( + IN PIRP_CONTEXT IrpContext, + IN PDCB Dcb + ); + +PFILE_OBJECT +FatOpenEaFile ( + IN PIRP_CONTEXT IrpContext, + IN PFCB EaFcb + ); + +VOID +FatCloseEaFile ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN BOOLEAN FlushFirst + ); + + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatSetDirtyBcb ( + IN PIRP_CONTEXT IrpContext, + IN PBCB Bcb, + IN PVCB Vcb OPTIONAL, + IN BOOLEAN Reversible + ); + +VOID +FatRepinBcb ( + IN PIRP_CONTEXT IrpContext, + IN PBCB Bcb + ); + +VOID +FatUnpinRepinnedBcbs ( + IN PIRP_CONTEXT IrpContext + ); + +FINISHED +FatZeroData ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PFILE_OBJECT FileObject, + IN ULONG StartingZero, + IN ULONG ByteCount + ); + +NTSTATUS +FatCompleteMdl ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +VOID +FatPinMappedData ( + IN PIRP_CONTEXT IrpContext, + IN PDCB Dcb, + IN VBO StartingVbo, + IN ULONG ByteCount, + OUT PBCB *Bcb + ); + +NTSTATUS +FatPrefetchPages ( + IN PIRP_CONTEXT IrpContext, + IN PFILE_OBJECT FileObject, + IN ULONG StartingPage, + IN ULONG PageCount + ); + +// +// VOID +// FatUnpinBcb ( +// IN PIRP_CONTEXT IrpContext, +// IN OUT PBCB Bcb, +// ); +// + +// +// This macro unpins a Bcb, in the checked build make sure all +// requests unpin all Bcbs before leaving. +// + +#if DBG + +#define FatUnpinBcb(IRPCONTEXT,BCB) { \ + if ((BCB) != NULL) { \ + CcUnpinData((BCB)); \ + NT_ASSERT( (IRPCONTEXT)->PinCount );\ + (IRPCONTEXT)->PinCount -= 1; \ + (BCB) = NULL; \ + } \ +} + +#else + +#define FatUnpinBcb(IRPCONTEXT,BCB) { \ + if ((BCB) != NULL) { \ + CcUnpinData((BCB)); \ + (BCB) = NULL; \ + } \ +} + +#endif // DBG + +VOID +FatInitializeCacheMap ( + _In_ PFILE_OBJECT FileObject, + _In_ PCC_FILE_SIZES FileSizes, + _In_ BOOLEAN PinAccess, + _In_ PCACHE_MANAGER_CALLBACKS Callbacks, + _In_ PVOID LazyWriteContext + ); + +VOID +FatSyncUninitializeCacheMap ( + IN PIRP_CONTEXT IrpContext, + IN PFILE_OBJECT FileObject + ); + + +// +// 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. +// + +VOID +FatPagingFileIo ( + IN PIRP Irp, + IN PFCB Fcb + ); + + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatNonCachedIo ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp, + IN PFCB FcbOrDcb, + IN ULONG StartingVbo, + IN ULONG ByteCount, + IN ULONG UserByteCount, + IN ULONG StreamFlags + ); + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatNonCachedNonAlignedRead ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp, + IN PFCB FcbOrDcb, + IN ULONG StartingVbo, + IN ULONG ByteCount + ); + +VOID +FatMultipleAsync ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PIRP Irp, + IN ULONG MultipleIrpCount, + IN PIO_RUN IoRuns + ); + +VOID +FatSingleAsync ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN LBO Lbo, + IN ULONG ByteCount, + IN PIRP Irp + ); + +VOID +FatWaitSync ( + IN PIRP_CONTEXT IrpContext + ); + +VOID +FatLockUserBuffer ( + IN PIRP_CONTEXT IrpContext, + IN OUT PIRP Irp, + IN LOCK_OPERATION Operation, + IN ULONG BufferLength + ); + +PVOID +FatBufferUserBuffer ( + IN PIRP_CONTEXT IrpContext, + IN OUT PIRP Irp, + IN ULONG BufferLength + ); + +PVOID +FatMapUserBuffer ( + IN PIRP_CONTEXT IrpContext, + IN OUT PIRP Irp + ); + +NTSTATUS +FatToggleMediaEjectDisable ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN BOOLEAN PreventRemoval + ); + +NTSTATUS +FatPerformDevIoCtrl ( + IN PIRP_CONTEXT IrpContext, + IN ULONG IoControlCode, + IN PDEVICE_OBJECT Device, + IN PVOID InputBuffer OPTIONAL, + IN ULONG InputBufferLength, + OUT PVOID OutputBuffer OPTIONAL, + IN ULONG OutputBufferLength, + IN BOOLEAN InternalDeviceIoControl, + IN BOOLEAN OverrideVerify, + OUT PIO_STATUS_BLOCK Iosb OPTIONAL + ); + +PMDL +FatBuildZeroMdl ( + __in PIRP_CONTEXT IrpContext, + __in ULONG Length + ); + + +// +// Dirent support routines, implemented in DirSup.c +// + +// +// Tunneling is a deletion precursor (all tunneling cases do +// not involve deleting dirents, however) +// + +VOID +FatTunnelFcbOrDcb ( + IN PFCB FcbOrDcb, + IN PCCB Ccb OPTIONAL + ); + +_Requires_lock_held_(_Global_critical_region_) +ULONG +FatCreateNewDirent ( + IN PIRP_CONTEXT IrpContext, + IN PDCB ParentDirectory, + IN ULONG DirentsNeeded, + IN BOOLEAN RescanDir + ); + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatInitializeDirectoryDirent ( + IN PIRP_CONTEXT IrpContext, + IN PDCB Dcb, + IN PDIRENT ParentDirent + ); + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatDeleteDirent ( + IN PIRP_CONTEXT IrpContext, + IN PFCB FcbOrDcb, + IN PDELETE_CONTEXT DeleteContext OPTIONAL, + IN BOOLEAN DeleteEa + ); + + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatLocateDirent ( + IN PIRP_CONTEXT IrpContext, + IN PDCB ParentDirectory, + IN PCCB Ccb, + IN VBO OffsetToStartSearchFrom, + IN OUT PULONG Flags, + OUT PDIRENT *Dirent, + OUT PBCB *Bcb, + OUT PVBO ByteOffset, + OUT PBOOLEAN FileNameDos OPTIONAL, + IN OUT PUNICODE_STRING Lfn OPTIONAL, + IN OUT PUNICODE_STRING OrigLfn OPTIONAL + ); + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatLocateSimpleOemDirent ( + IN PIRP_CONTEXT IrpContext, + IN PDCB ParentDirectory, + IN POEM_STRING FileName, + OUT PDIRENT *Dirent, + OUT PBCB *Bcb, + OUT PVBO ByteOffset + ); + +_Requires_lock_held_(_Global_critical_region_) +BOOLEAN +FatLfnDirentExists ( + IN PIRP_CONTEXT IrpContext, + IN PDCB Dcb, + IN PUNICODE_STRING Lfn, + IN PUNICODE_STRING LfnTmp + ); + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatLocateVolumeLabel ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + OUT PDIRENT *Dirent, + OUT PBCB *Bcb, + OUT PVBO ByteOffset + ); + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatGetDirentFromFcbOrDcb ( + IN PIRP_CONTEXT IrpContext, + IN PFCB FcbOrDcb, + IN BOOLEAN ReturnOnFailure, + OUT PDIRENT *Dirent, + OUT PBCB *Bcb + ); + +_Requires_lock_held_(_Global_critical_region_) +BOOLEAN +FatIsDirectoryEmpty ( + IN PIRP_CONTEXT IrpContext, + IN PDCB Dcb + ); + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatDeleteFile ( + IN PIRP_CONTEXT IrpContext, + IN PDCB TargetDcb, + IN ULONG LfnOffset, + IN ULONG DirentOffset, + IN PDIRENT Dirent, + IN PUNICODE_STRING Lfn + ); + + +VOID +FatConstructDirent ( + IN PIRP_CONTEXT IrpContext, + IN OUT PDIRENT Dirent, + IN POEM_STRING FileName, + IN BOOLEAN ComponentReallyLowercase, + IN BOOLEAN ExtensionReallyLowercase, + IN PUNICODE_STRING Lfn OPTIONAL, + IN UCHAR Attributes, + IN BOOLEAN ZeroAndSetTimeFields, + IN PLARGE_INTEGER SetCreationTime OPTIONAL + ); + +VOID +FatConstructLabelDirent ( + IN PIRP_CONTEXT IrpContext, + IN OUT PDIRENT Dirent, + IN POEM_STRING Label + ); + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatSetFileSizeInDirent ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN PULONG AlternativeFileSize OPTIONAL + ); + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatSetFileSizeInDirentNoRaise ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN PULONG AlternativeFileSize OPTIONAL + ); + + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatUpdateDirentFromFcb ( + IN PIRP_CONTEXT IrpContext, + IN PFILE_OBJECT FileObject, + IN PFCB FcbOrDcb, + IN PCCB Ccb + ); + + +// +// Generate a relatively unique static 64bit ID from a FAT Fcb/Dcb +// +// ULONGLONG +// FatDirectoryKey (FcbOrDcb); +// + +#define FatDirectoryKey(FcbOrDcb) ((ULONGLONG)((FcbOrDcb)->CreationTime.QuadPart ^ (FcbOrDcb)->FirstClusterOfFile)) + + +// +// The following routines are used to access and manipulate the +// clusters containing EA data in the ea data file. They are +// implemented in EaSup.c +// + +// +// VOID +// FatUpcaseEaName ( +// IN PIRP_CONTEXT IrpContext, +// IN POEM_STRING EaName, +// OUT POEM_STRING UpcasedEaName +// ); +// + +#define FatUpcaseEaName( IRPCONTEXT, NAME, UPCASEDNAME ) \ + RtlUpperString( UPCASEDNAME, NAME ) + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatGetEaLength ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PDIRENT Dirent, + OUT PULONG EaLength + ); + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatGetNeedEaCount ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PDIRENT Dirent, + OUT PULONG NeedEaCount + ); + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatCreateEa ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PUCHAR Buffer, + IN ULONG Length, + IN POEM_STRING FileName, + OUT PUSHORT EaHandle + ); + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatDeleteEa ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN USHORT EaHandle, + IN POEM_STRING FileName + ); + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatGetEaFile ( + IN PIRP_CONTEXT IrpContext, + IN OUT PVCB Vcb, + OUT PDIRENT *EaDirent, + OUT PBCB *EaBcb, + IN BOOLEAN CreateFile, + IN BOOLEAN ExclusiveFcb + ); + +VOID +FatReadEaSet ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN USHORT EaHandle, + IN POEM_STRING FileName, + IN BOOLEAN ReturnEntireSet, + OUT PEA_RANGE EaSetRange + ); + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatDeleteEaSet ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PBCB EaBcb, + OUT PDIRENT EaDirent, + IN USHORT EaHandle, + IN POEM_STRING Filename + ); + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatAddEaSet ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN ULONG EaSetLength, + IN PBCB EaBcb, + OUT PDIRENT EaDirent, + OUT PUSHORT EaHandle, + OUT PEA_RANGE EaSetRange + ); + +VOID +FatDeletePackedEa ( + IN PIRP_CONTEXT IrpContext, + IN OUT PEA_SET_HEADER EaSetHeader, + IN OUT PULONG PackedEasLength, + IN ULONG Offset + ); + +VOID +FatAppendPackedEa ( + IN PIRP_CONTEXT IrpContext, + IN OUT PEA_SET_HEADER *EaSetHeader, + IN OUT PULONG PackedEasLength, + IN OUT PULONG AllocationLength, + IN PFILE_FULL_EA_INFORMATION FullEa, + IN ULONG BytesPerCluster + ); + +ULONG +FatLocateNextEa ( + IN PIRP_CONTEXT IrpContext, + IN PPACKED_EA FirstPackedEa, + IN ULONG PackedEasLength, + IN ULONG PreviousOffset + ); + +BOOLEAN +FatLocateEaByName ( + IN PIRP_CONTEXT IrpContext, + IN PPACKED_EA FirstPackedEa, + IN ULONG PackedEasLength, + IN POEM_STRING EaName, + OUT PULONG Offset + ); + +BOOLEAN +FatIsEaNameValid ( + IN PIRP_CONTEXT IrpContext, + IN OEM_STRING Name + ); + +VOID +FatPinEaRange ( + IN PIRP_CONTEXT IrpContext, + IN PFILE_OBJECT VirtualEaFile, + IN PFCB EaFcb, + IN OUT PEA_RANGE EaRange, + IN ULONG StartingVbo, + IN ULONG Length, + IN NTSTATUS ErrorStatus + ); + +VOID +FatMarkEaRangeDirty ( + IN PIRP_CONTEXT IrpContext, + IN PFILE_OBJECT EaFileObject, + IN OUT PEA_RANGE EaRange + ); + +VOID +FatUnpinEaRange ( + IN PIRP_CONTEXT IrpContext, + IN OUT PEA_RANGE EaRange + ); + +// +// The following macro computes the size of a full ea (not including +// padding to bring it to a longword. A full ea has a 4 byte offset, +// folowed by 1 byte flag, 1 byte name length, 2 bytes value length, +// the name, 1 null byte, and the value. +// +// ULONG +// SizeOfFullEa ( +// IN PFILE_FULL_EA_INFORMATION FullEa +// ); +// + +#define SizeOfFullEa(EA) (4+1+1+2+(EA)->EaNameLength+1+(EA)->EaValueLength) + + +// +// The following routines are used to manipulate the fscontext fields +// of the file object, implemented in FilObSup.c +// + +typedef enum _TYPE_OF_OPEN { + + UnopenedFileObject = 1, + UserFileOpen, + UserDirectoryOpen, + UserVolumeOpen, + VirtualVolumeFile, + DirectoryFile, + EaFile, +} TYPE_OF_OPEN; + +typedef enum _FAT_FLUSH_TYPE { + + NoFlush = 0, + Flush, + FlushAndInvalidate, + FlushWithoutPurge + +} FAT_FLUSH_TYPE; + +VOID +FatSetFileObject ( + IN PFILE_OBJECT FileObject OPTIONAL, + IN TYPE_OF_OPEN TypeOfOpen, + IN PVOID VcbOrFcbOrDcb, + IN PCCB Ccb OPTIONAL + ); + +TYPE_OF_OPEN +FatDecodeFileObject ( + _In_ PFILE_OBJECT FileObject, + _Outptr_ PVCB *Vcb, + _Outptr_ PFCB *FcbOrDcb, + _Outptr_ PCCB *Ccb + ); + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatPurgeReferencedFileObjects ( + IN PIRP_CONTEXT IrpContext, + IN PFCB FcbOrDcb, + IN FAT_FLUSH_TYPE FlushType + ); + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatForceCacheMiss ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN FAT_FLUSH_TYPE FlushType + ); + + +// +// File system control routines, implemented in FsCtrl.c +// + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatFlushAndCleanVolume( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp, + IN PVCB Vcb, + IN FAT_FLUSH_TYPE FlushType + ); + +BOOLEAN +FatIsBootSectorFat ( + IN PPACKED_BOOT_SECTOR BootSector + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatLockVolumeInternal ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PFILE_OBJECT FileObject OPTIONAL + ); + +NTSTATUS +FatUnlockVolumeInternal ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PFILE_OBJECT FileObject OPTIONAL + ); + + +// +// Name support routines, implemented in NameSup.c +// + +// +// BOOLEAN +// FatAreNamesEqual ( +// IN PIRP_CONTEXT IrpContext, +// IN OEM_STRING ConstantNameA, +// IN OEM_STRING ConstantNameB +// ) +// +// /*++ +// +// Routine Description: +// +// This routine simple returns whether the two names are exactly equal. +// If the two names are known to be constant, this routine is much +// faster than FatIsDbcsInExpression. +// +// Arguments: +// +// ConstantNameA - Constant name. +// +// ConstantNameB - Constant name. +// +// Return Value: +// +// BOOLEAN - TRUE if the two names are lexically equal. +// + +#define FatAreNamesEqual(IRPCONTEXT,NAMEA,NAMEB) ( \ + ((ULONG)(NAMEA).Length == (ULONG)(NAMEB).Length) && \ + (RtlEqualMemory( &(NAMEA).Buffer[0], \ + &(NAMEB).Buffer[0], \ + (NAMEA).Length )) \ +) + +// +// BOOLEAN +// FatIsNameShortOemValid ( +// IN PIRP_CONTEXT IrpContext, +// IN OEM_STRING Name, +// IN BOOLEAN CanContainWildCards, +// IN BOOLEAN PathNamePermissible, +// IN BOOLEAN LeadingBackslashPermissible +// ) +// +// /*++ +// +// Routine Description: +// +// This routine scans the input name and verifies that if only +// contains valid characters +// +// Arguments: +// +// Name - Supplies the input name to check. +// +// CanContainWildCards - Indicates if the name can contain wild cards +// (i.e., * and ?). +// +// Return Value: +// +// BOOLEAN - Returns TRUE if the name is valid and FALSE otherwise. +// +// --*/ +// +// The FatIsNameLongOemValid and FatIsNameLongUnicodeValid are similar. +// + +#define FatIsNameShortOemValid(IRPCONTEXT,NAME,CAN_CONTAIN_WILD_CARDS,PATH_NAME_OK,LEADING_BACKSLASH_OK) ( \ + FsRtlIsFatDbcsLegal((NAME), \ + (CAN_CONTAIN_WILD_CARDS), \ + (PATH_NAME_OK), \ + (LEADING_BACKSLASH_OK)) \ +) + +#define FatIsNameLongOemValid(IRPCONTEXT,NAME,CAN_CONTAIN_WILD_CARDS,PATH_NAME_OK,LEADING_BACKSLASH_OK) ( \ + FsRtlIsHpfsDbcsLegal((NAME), \ + (CAN_CONTAIN_WILD_CARDS), \ + (PATH_NAME_OK), \ + (LEADING_BACKSLASH_OK)) \ +) + +INLINE +BOOLEAN +FatIsNameLongUnicodeValid ( + PIRP_CONTEXT IrpContext, + PUNICODE_STRING Name, + BOOLEAN CanContainWildcards, + BOOLEAN PathNameOk, + BOOLEAN LeadingBackslashOk + ) +{ + ULONG i; + + UNREFERENCED_PARAMETER( IrpContext ); + UNREFERENCED_PARAMETER( LeadingBackslashOk ); + UNREFERENCED_PARAMETER( PathNameOk ); + + // + // I'm not bothering to do the whole thing, just enough to make this call look + // the same as the others. + // + + NT_ASSERT( !PathNameOk && !LeadingBackslashOk ); + + for (i=0; i < Name->Length/sizeof(WCHAR); i++) { + + if ((Name->Buffer[i] < 0x80) && + !(FsRtlIsAnsiCharacterLegalHpfs(Name->Buffer[i], CanContainWildcards))) { + + return FALSE; + } + } + + return TRUE; +} + +BOOLEAN +FatIsNameInExpression ( + IN PIRP_CONTEXT IrpContext, + IN OEM_STRING Expression, + IN OEM_STRING Name + ); + +VOID +FatStringTo8dot3 ( + _In_ PIRP_CONTEXT IrpContext, + _In_ OEM_STRING InputString, + _Out_writes_bytes_(11) PFAT8DOT3 Output8dot3 + ); + +VOID +Fat8dot3ToString ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PDIRENT Dirent, + _In_ BOOLEAN RestoreCase, + _Out_ POEM_STRING OutputString + ); + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatGetUnicodeNameFromFcb ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN OUT PUNICODE_STRING Lfn + ); + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatSetFullFileNameInFcb ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb + ); + +VOID +FatSetFullNameInFcb ( + _In_ PIRP_CONTEXT IrpContext, + _Inout_ PFCB Fcb, + _In_ PUNICODE_STRING FinalName + ); + +VOID +FatUnicodeToUpcaseOem ( + IN PIRP_CONTEXT IrpContext, + IN POEM_STRING OemString, + IN PUNICODE_STRING UnicodeString + ); + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatSelectNames ( + IN PIRP_CONTEXT IrpContext, + IN PDCB Parent, + IN POEM_STRING OemName, + IN PUNICODE_STRING UnicodeName, + IN OUT POEM_STRING ShortName, + IN PUNICODE_STRING SuggestedShortName OPTIONAL, + IN OUT BOOLEAN *AllLowerComponent, + IN OUT BOOLEAN *AllLowerExtension, + IN OUT BOOLEAN *CreateLfn + ); + +VOID +FatEvaluateNameCase ( + IN PIRP_CONTEXT IrpContext, + IN PUNICODE_STRING Name, + IN OUT BOOLEAN *AllLowerComponent, + IN OUT BOOLEAN *AllLowerExtension, + IN OUT BOOLEAN *CreateLfn + ); + +BOOLEAN +FatSpaceInName ( + IN PIRP_CONTEXT IrpContext, + IN PUNICODE_STRING UnicodeName + ); + + +// +// Resources support routines/macros, implemented in ResrcSup.c +// +// The following routines/macros are used for gaining shared and exclusive +// access to the global/vcb data structures. The routines are implemented +// in ResrcSup.c. There is a global resources that everyone tries to take +// out shared to do their work, with the exception of mount/dismount which +// take out the global resource exclusive. All other resources only work +// on their individual item. For example, an Fcb resource does not take out +// a Vcb resource. But the way the file system is structured we know +// that when we are processing an Fcb other threads cannot be trying to remove +// or alter the Fcb, so we do not need to acquire the Vcb. +// +// The procedures/macros are: +// +// Macro FatData Vcb Fcb Subsequent macros +// +// AcquireExclusiveGlobal Read/Write None None ReleaseGlobal +// +// AcquireSharedGlobal Read None None ReleaseGlobal +// +// AcquireExclusiveVcb Read Read/Write None ReleaseVcb +// +// AcquireSharedVcb Read Read None ReleaseVcb +// +// AcquireExclusiveFcb Read None Read/Write ConvertToSharFcb +// ReleaseFcb +// +// AcquireSharedFcb Read None Read ReleaseFcb +// +// ConvertToSharedFcb Read None Read ReleaseFcb +// +// ReleaseGlobal +// +// ReleaseVcb +// +// ReleaseFcb +// + +// +// FINISHED +// FatAcquireExclusiveGlobal ( +// IN PIRP_CONTEXT IrpContext +// ); +// +// FINISHED +// FatAcquireSharedGlobal ( +// IN PIRP_CONTEXT IrpContext +// ); +// + +#define FatAcquireExclusiveGlobal(IRPCONTEXT) ( \ + ExAcquireResourceExclusiveLite( &FatData.Resource, BooleanFlagOn((IRPCONTEXT)->Flags, IRP_CONTEXT_FLAG_WAIT) ) \ +) + +#define FatAcquireSharedGlobal(IRPCONTEXT) ( \ + ExAcquireResourceSharedLite( &FatData.Resource, BooleanFlagOn((IRPCONTEXT)->Flags, IRP_CONTEXT_FLAG_WAIT) ) \ +) + +// +// The following macro must only be called when Wait is TRUE! +// +// FatAcquireExclusiveVolume ( +// IN PIRP_CONTEXT IrpContext, +// IN PVCB Vcb +// ); +// +// FatReleaseVolume ( +// IN PIRP_CONTEXT IrpContext, +// IN PVCB Vcb +// ); +// + +#define FatAcquireExclusiveVolume(IRPCONTEXT,VCB) { \ + PFCB __FFFFFcb = NULL; \ + NT_ASSERT(FlagOn((IRPCONTEXT)->Flags, IRP_CONTEXT_FLAG_WAIT)); \ + (VOID)FatAcquireExclusiveVcb( (IRPCONTEXT), (VCB) ); \ + while ( (__FFFFFcb = FatGetNextFcbBottomUp((IRPCONTEXT), __FFFFFcb, (VCB)->RootDcb)) != NULL) { \ + (VOID)FatAcquireExclusiveFcb((IRPCONTEXT), __FFFFFcb ); \ + } \ +} + +#define FatReleaseVolume(IRPCONTEXT,VCB) { \ + PFCB __FFFFFcb = NULL; \ + NT_ASSERT(FlagOn((IRPCONTEXT)->Flags, IRP_CONTEXT_FLAG_WAIT)); \ + while ( (__FFFFFcb = FatGetNextFcbBottomUp((IRPCONTEXT), __FFFFFcb, (VCB)->RootDcb)) != NULL) { \ + (VOID)ExReleaseResourceLite( __FFFFFcb->Header.Resource ); \ + } \ + FatReleaseVcb((IRPCONTEXT), (VCB)); \ +} + +// +// Macro to enable easy tracking of Vcb state transitions. +// + +#ifdef FASTFATDBG +#define FatSetVcbCondition( V, X) { \ + DebugTrace(0,DEBUG_TRACE_VERFYSUP,"%d -> ",(V)->VcbCondition); \ + DebugTrace(0,DEBUG_TRACE_VERFYSUP,"%x\n",(X)); \ + (V)->VcbCondition = (X); \ + } +#else +#define FatSetVcbCondition( V, X) (V)->VcbCondition = (X) +#endif + +// +// These macros can be used to determine what kind of FAT we have for an +// initialized Vcb. It is somewhat more elegant to use these (visually). +// + +#define FatIsFat32(VCB) ((BOOLEAN)((VCB)->AllocationSupport.FatIndexBitSize == 32)) +#define FatIsFat16(VCB) ((BOOLEAN)((VCB)->AllocationSupport.FatIndexBitSize == 16)) +#define FatIsFat12(VCB) ((BOOLEAN)((VCB)->AllocationSupport.FatIndexBitSize == 12)) + + +_Requires_lock_held_(_Global_critical_region_) +_When_(return != FALSE && NoOpCheck != FALSE, _Acquires_exclusive_lock_(Vcb->Resource)) +FINISHED +FatAcquireExclusiveVcb_Real ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN BOOLEAN NoOpCheck + ); + +#define FatAcquireExclusiveVcb( IC, V) FatAcquireExclusiveVcb_Real( IC, V, FALSE) +#define FatAcquireExclusiveVcbNoOpCheck( IC, V) FatAcquireExclusiveVcb_Real( IC, V, TRUE) + +_Requires_lock_held_(_Global_critical_region_) +_When_(return != 0, _Acquires_shared_lock_(Vcb->Resource)) +FINISHED +FatAcquireSharedVcb ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb + ); + +_Requires_lock_held_(_Global_critical_region_) +_Acquires_exclusive_lock_(*Fcb->Header.Resource) +FINISHED +FatAcquireExclusiveFcb ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb + ); + +_Requires_lock_held_(_Global_critical_region_) +_Acquires_shared_lock_(*Fcb->Header.Resource) +FINISHED +FatAcquireSharedFcb ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb + ); + +_Requires_lock_held_(_Global_critical_region_) +_When_(return != 0, _Acquires_shared_lock_(*Fcb->Header.Resource)) +FINISHED +FatAcquireSharedFcbWaitForEx ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb + ); + +#define FatVcbAcquiredExclusive(IRPCONTEXT,VCB) ( \ + ExIsResourceAcquiredExclusiveLite(&(VCB)->Resource) || \ + ExIsResourceAcquiredExclusiveLite(&FatData.Resource) \ +) + +#define FatFcbAcquiredShared(IRPCONTEXT,FCB) ( \ + ExIsResourceAcquiredSharedLite((FCB)->Header.Resource) \ +) + +#define FatFcbAcquiredExclusive(IRPCONTEXT,FCB) ( \ + ExIsResourceAcquiredExclusiveLite((FCB)->Header.Resource) \ +) + +#define FatAcquireDirectoryFileMutex(VCB) { \ + NT_ASSERT(KeAreApcsDisabled()); \ + ExAcquireFastMutexUnsafe(&(VCB)->DirectoryFileCreationMutex); \ +} + +#define FatReleaseDirectoryFileMutex(VCB) { \ + NT_ASSERT(KeAreApcsDisabled()); \ + ExReleaseFastMutexUnsafe(&(VCB)->DirectoryFileCreationMutex); \ +} + +// +// The following are cache manager call backs + +BOOLEAN +FatAcquireVolumeForClose ( + IN PVOID Vcb, + IN BOOLEAN Wait + ); + +VOID +FatReleaseVolumeFromClose ( + IN PVOID Vcb + ); + +_Requires_lock_held_(_Global_critical_region_) +BOOLEAN +FatAcquireFcbForLazyWrite ( + IN PVOID Null, + IN BOOLEAN Wait + ); + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatReleaseFcbFromLazyWrite ( + IN PVOID Null + ); + +_Requires_lock_held_(_Global_critical_region_) +BOOLEAN +FatAcquireFcbForReadAhead ( + IN PVOID Null, + IN BOOLEAN Wait + ); + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatReleaseFcbFromReadAhead ( + IN PVOID Null + ); + +_Function_class_(FAST_IO_ACQUIRE_FOR_CCFLUSH) +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatAcquireForCcFlush ( + IN PFILE_OBJECT FileObject, + IN PDEVICE_OBJECT DeviceObject + ); + +_Function_class_(FAST_IO_RELEASE_FOR_CCFLUSH) +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatReleaseForCcFlush ( + IN PFILE_OBJECT FileObject, + IN PDEVICE_OBJECT DeviceObject + ); + +BOOLEAN +FatNoOpAcquire ( + IN PVOID Fcb, + IN BOOLEAN Wait + ); + +VOID +FatNoOpRelease ( + IN PVOID Fcb + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatFilterCallbackAcquireForCreateSection ( + IN PFS_FILTER_CALLBACK_DATA CallbackData, + OUT PVOID *CompletionContext + ); + +// +// VOID +// FatConvertToSharedFcb ( +// IN PIRP_CONTEXT IrpContext, +// IN PFCB Fcb +// ); +// + +#define FatConvertToSharedFcb(IRPCONTEXT,Fcb) { \ + ExConvertExclusiveToSharedLite( (Fcb)->Header.Resource ); \ + } + +// +// VOID +// FatReleaseGlobal ( +// IN PIRP_CONTEXT IrpContext +// ); +// +// VOID +// FatReleaseVcb ( +// IN PIRP_CONTEXT IrpContext, +// IN PVCB Vcb +// ); +// +// VOID +// FatReleaseFcb ( +// IN PIRP_CONTEXT IrpContext, +// IN PVCB Vcb +// ); +// + +#define FatDeleteResource(RESRC) { \ + ExDeleteResourceLite( (RESRC) ); \ +} + +#define FatReleaseGlobal(IRPCONTEXT) { \ + ExReleaseResourceLite( &(FatData.Resource) ); \ + } + +#define FatReleaseVcb(IRPCONTEXT,Vcb) { \ + ExReleaseResourceLite( &((Vcb)->Resource) ); \ + } + +#define FatReleaseFcb(IRPCONTEXT,Fcb) { \ + ExReleaseResourceLite( (Fcb)->Header.Resource ); \ + } + +// +// 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 FatGetFcbOplock(F) &(F)->Header.Oplock + +#else + +#define FatGetFcbOplock(F) &(F)->Specific.Fcb.Oplock + +#endif + + +// +// In-memory structure support routine, implemented in StrucSup.c +// + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatInitializeVcb ( + IN PIRP_CONTEXT IrpContext, + IN OUT PVCB Vcb, + IN PDEVICE_OBJECT TargetDeviceObject, + IN PVPB Vpb, + IN PDEVICE_OBJECT FsDeviceObject + ); + +VOID +FatTearDownVcb ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb + ); + +VOID +FatDeleteVcb ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb + ); + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatCreateRootDcb ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb + ); + +PFCB +FatCreateFcb ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PDCB ParentDcb, + IN ULONG LfnOffsetWithinDirectory, + IN ULONG DirentOffsetWithinDirectory, + IN PDIRENT Dirent, + IN PUNICODE_STRING Lfn OPTIONAL, + IN BOOLEAN IsPagingFile, + IN BOOLEAN SingleResource + ); + +PDCB +FatCreateDcb ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PDCB ParentDcb, + IN ULONG LfnOffsetWithinDirectory, + IN ULONG DirentOffsetWithinDirectory, + IN PDIRENT Dirent, + IN PUNICODE_STRING Lfn OPTIONAL + ); + +VOID +FatDeleteFcb ( + IN PIRP_CONTEXT IrpContext, + IN PFCB *Fcb + ); + +PCCB +FatCreateCcb ( + IN PIRP_CONTEXT IrpContext + ); + +VOID +FatDeallocateCcbStrings( + IN PCCB Ccb + ); + +VOID +FatDeleteCcb ( + IN PIRP_CONTEXT IrpContext, + IN PCCB *Ccb + ); + +PIRP_CONTEXT +FatCreateIrpContext ( + IN PIRP Irp, + IN BOOLEAN Wait + ); + +VOID +FatDeleteIrpContext_Real ( + IN PIRP_CONTEXT IrpContext + ); + +#ifdef FASTFATDBG +#define FatDeleteIrpContext(IRPCONTEXT) { \ + FatDeleteIrpContext_Real((IRPCONTEXT)); \ + (IRPCONTEXT) = NULL; \ +} +#else +#define FatDeleteIrpContext(IRPCONTEXT) { \ + FatDeleteIrpContext_Real((IRPCONTEXT)); \ +} +#endif // FASTFAT_DBG + +PFCB +FatGetNextFcbTopDown ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN PFCB TerminationFcb + ); + +PFCB +FatGetNextFcbBottomUp ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN PFCB TerminationFcb + ); + +// +// These two macros just make the code a bit cleaner. +// + +#define FatGetFirstChild(DIR) ((PFCB)( \ + IsListEmpty(&(DIR)->Specific.Dcb.ParentDcbQueue) ? NULL : \ + CONTAINING_RECORD((DIR)->Specific.Dcb.ParentDcbQueue.Flink, \ + DCB, \ + ParentDcbLinks.Flink))) + +#define FatGetNextSibling(FILE) ((PFCB)( \ + &(FILE)->ParentDcb->Specific.Dcb.ParentDcbQueue.Flink == \ + (PVOID)(FILE)->ParentDcbLinks.Flink ? NULL : \ + CONTAINING_RECORD((FILE)->ParentDcbLinks.Flink, \ + FCB, \ + ParentDcbLinks.Flink))) + +_Requires_lock_held_(_Global_critical_region_) +BOOLEAN +FatCheckForDismount ( + IN PIRP_CONTEXT IrpContext, + PVCB Vcb, + IN BOOLEAN Force + ); + +VOID +FatConstructNamesInFcb ( + IN PIRP_CONTEXT IrpContext, + PFCB Fcb, + PDIRENT Dirent, + PUNICODE_STRING Lfn OPTIONAL + ); + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatCheckFreeDirentBitmap ( + IN PIRP_CONTEXT IrpContext, + IN PDCB Dcb + ); + +ULONG +FatVolumeUncleanCount ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb + ); + +VOID +FatPreallocateCloseContext ( + IN PVCB Vcb + ); + +PCLOSE_CONTEXT +FatAllocateCloseContext( + IN PVCB Vcb + ); + +// +// BOOLEAN +// FatIsRawDevice ( +// IN PIRP_CONTEXT IrpContext, +// IN NTSTATUS Status +// ); +// + +#define FatIsRawDevice(IC,S) ( \ + ((S) == STATUS_DEVICE_NOT_READY) || \ + ((S) == STATUS_NO_MEDIA_IN_DEVICE) \ +) + + +// +// Routines to support managing file names Fcbs and Dcbs. +// Implemented in SplaySup.c +// + +VOID +FatInsertName ( + IN PIRP_CONTEXT IrpContext, + IN PRTL_SPLAY_LINKS *RootNode, + IN PFILE_NAME_NODE Name + ); + +VOID +FatRemoveNames ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb + ); + +PFCB +FatFindFcb ( + IN PIRP_CONTEXT IrpContext, + IN OUT PRTL_SPLAY_LINKS *RootNode, + IN PSTRING Name, + OUT PBOOLEAN FileNameDos OPTIONAL + ); + +BOOLEAN +FatIsHandleCountZero ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb + ); + +typedef enum _COMPARISON { + IsLessThan, + IsGreaterThan, + IsEqual +} COMPARISON; + +COMPARISON +FatCompareNames ( + IN PSTRING NameA, + IN PSTRING NameB + ); + +// +// Do a macro here to check for a common case. +// + +#define CompareNames(NAMEA,NAMEB) ( \ + *(PUCHAR)(NAMEA)->Buffer != *(PUCHAR)(NAMEB)->Buffer ? \ + *(PUCHAR)(NAMEA)->Buffer < *(PUCHAR)(NAMEB)->Buffer ? \ + IsLessThan : IsGreaterThan : \ + FatCompareNames((PSTRING)(NAMEA), (PSTRING)(NAMEB)) \ +) + +// +// Time conversion support routines, implemented in TimeSup.c +// + +_Success_(return != FALSE) +BOOLEAN +FatNtTimeToFatTime ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PLARGE_INTEGER NtTime, + _In_ BOOLEAN Rounding, + _Out_ PFAT_TIME_STAMP FatTime, + _Out_opt_ PUCHAR TenMsecs + ); + +LARGE_INTEGER +FatFatTimeToNtTime ( + _In_ PIRP_CONTEXT IrpContext, + _In_ FAT_TIME_STAMP FatTime, + _In_ UCHAR TenMilliSeconds + ); + +LARGE_INTEGER +FatFatDateToNtTime ( + _In_ PIRP_CONTEXT IrpContext, + _In_ FAT_DATE FatDate + ); + +FAT_TIME_STAMP +FatGetCurrentFatTime ( + _In_ PIRP_CONTEXT IrpContext + ); + + +// +// Low level verification routines, implemented in VerfySup.c +// +// The first routine is called to help process a verify IRP. Its job is +// to walk every Fcb/Dcb and mark them as need to be verified. +// +// The other routines are used by every dispatch routine to verify that +// an Vcb/Fcb/Dcb is still good. The routine walks as much of the opened +// file/directory tree as necessary to make sure that the path is still valid. +// The function result indicates if the procedure needed to block for I/O. +// If the structure is bad the procedure raise the error condition +// STATUS_FILE_INVALID, otherwise they simply return to their caller +// + +typedef enum _FAT_VOLUME_STATE { + VolumeClean, + VolumeDirty, + VolumeDirtyWithSurfaceTest +} FAT_VOLUME_STATE, *PFAT_VOLUME_STATE; + +VOID +FatMarkFcbCondition ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN FCB_CONDITION FcbCondition, + IN BOOLEAN Recursive + ); + +VOID +FatVerifyVcb ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb + ); + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatVerifyFcb ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb + ); + + +KDEFERRED_ROUTINE FatCleanVolumeDpc; + +VOID +FatCleanVolumeDpc ( + _In_ PKDPC Dpc, + _In_opt_ PVOID DeferredContext, + _In_opt_ PVOID SystemArgument1, + _In_opt_ PVOID SystemArgument2 + ); + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatMarkVolume ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN FAT_VOLUME_STATE VolumeState + ); + +WORKER_THREAD_ROUTINE FatFspMarkVolumeDirtyWithRecover; + +VOID +FatFspMarkVolumeDirtyWithRecover ( + PVOID Parameter + ); + +VOID +FatCheckDirtyBit ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb + ); + +VOID +FatQuickVerifyVcb ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb + ); + +VOID +FatVerifyOperationIsLegal ( + IN PIRP_CONTEXT IrpContext + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatPerformVerify ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PIRP Irp, + _In_ PDEVICE_OBJECT Device + ); + + +// +// Work queue routines for posting and retrieving an Irp, implemented in +// workque.c +// + +VOID +FatOplockComplete ( + IN PVOID Context, + IN PIRP Irp + ); + +VOID +FatPrePostIrp ( + IN PVOID Context, + IN PIRP Irp + ); + +VOID +FatAddToWorkque ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +NTSTATUS +FatFsdPostRequest ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +// +// Miscellaneous support routines +// + +// +// This macro returns TRUE if a flag in a set of flags is on and FALSE +// otherwise. It is followed by two macros for setting and clearing +// flags +// + +//#ifndef BooleanFlagOn +//#define BooleanFlagOn(Flags,SingleFlag) ((BOOLEAN)((((Flags) & (SingleFlag)) != 0))) +//#endif + +//#ifndef SetFlag +//#define SetFlag(Flags,SingleFlag) { \ +// (Flags) |= (SingleFlag); \ +//} +//#endif + +//#ifndef ClearFlag +//#define ClearFlag(Flags,SingleFlag) { \ +// (Flags) &= ~(SingleFlag); \ +//} +//#endif + +// +// ULONG +// PtrOffset ( +// IN PVOID BasePtr, +// IN PVOID OffsetPtr +// ); +// + +#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 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; + +// +// 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)); \ + } + +#define CopyU4char(Dst,Src) { \ + *((UNALIGNED UCHAR4 *)(Dst)) = *((UCHAR4 *)(Src)); \ + } + +// +// VOID +// FatNotifyReportChange ( +// IN PIRP_CONTEXT IrpContext, +// IN PVCB Vcb, +// IN PFCB Fcb, +// IN ULONG Filter, +// IN ULONG Action +// ); +// + +#define FatNotifyReportChange(I,V,F,FL,A) { \ + if ((F)->FullFileName.Buffer == NULL) { \ + FatSetFullFileNameInFcb((I),(F)); \ + } \ + NT_ASSERT( (F)->FullFileName.Length != 0 ); \ + NT_ASSERT( (F)->FinalNameLength != 0 ); \ + NT_ASSERT( (F)->FullFileName.Length > (F)->FinalNameLength ); \ + NT_ASSERT( (F)->FullFileName.Buffer[((F)->FullFileName.Length - (F)->FinalNameLength)/sizeof(WCHAR) - 1] == L'\\' ); \ + FsRtlNotifyFullReportChange( (V)->NotifySync, \ + &(V)->DirNotifyList, \ + (PSTRING)&(F)->FullFileName, \ + (USHORT) ((F)->FullFileName.Length - \ + (F)->FinalNameLength), \ + (PSTRING)NULL, \ + (PSTRING)NULL, \ + (ULONG)FL, \ + (ULONG)A, \ + (PVOID)NULL ); \ +} + + +// +// The FSD Level dispatch routines. These routines are called by the +// I/O system via the dispatch table in the Driver Object. +// +// They each accept as input a pointer to a device object (actually most +// expect a volume device object, with the exception of the file system +// control function which can also take a file system device object), and +// a pointer to the IRP. They either perform the function at the FSD level +// or post the request to the FSP work queue for FSP level processing. +// + +_Function_class_(IRP_MJ_CLEANUP) +_Function_class_(DRIVER_DISPATCH) +NTSTATUS +FatFsdCleanup ( // implemented in Cleanup.c + _In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + _Inout_ PIRP Irp + ); + +_Function_class_(IRP_MJ_CLOSE) +_Function_class_(DRIVER_DISPATCH) +NTSTATUS +FatFsdClose ( // implemented in Close.c + _In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + _Inout_ PIRP Irp + ); + +_Function_class_(IRP_MJ_CREATE) +_Function_class_(DRIVER_DISPATCH) +NTSTATUS +FatFsdCreate ( // implemented in Create.c + _In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + _Inout_ PIRP Irp + ); + + +_Function_class_(IRP_MJ_DEVICE_CONTROL) +_Function_class_(DRIVER_DISPATCH) +NTSTATUS +FatFsdDeviceControl ( // implemented in DevCtrl.c + _In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + _Inout_ PIRP Irp + ); + +_Function_class_(IRP_MJ_DIRECTORY_CONTROL) +_Function_class_(DRIVER_DISPATCH) +NTSTATUS +FatFsdDirectoryControl ( // implemented in DirCtrl.c + _In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + _Inout_ PIRP Irp + ); + +_Function_class_(IRP_MJ_QUERY_EA) +_Function_class_(DRIVER_DISPATCH) +NTSTATUS +FatFsdQueryEa ( // implemented in Ea.c + _In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + _Inout_ PIRP Irp + ); + +_Function_class_(IRP_MJ_SET_EA) +_Function_class_(DRIVER_DISPATCH) +NTSTATUS +FatFsdSetEa ( // implemented in Ea.c + _In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + _Inout_ PIRP Irp + ); + +_Function_class_(IRP_MJ_QUERY_INFORMATION) +_Function_class_(DRIVER_DISPATCH) +NTSTATUS +FatFsdQueryInformation ( // implemented in FileInfo.c + _In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + _Inout_ PIRP Irp + ); + +_Function_class_(IRP_MJ_SET_INFORMATION) +_Function_class_(DRIVER_DISPATCH) +NTSTATUS +FatFsdSetInformation ( // implemented in FileInfo.c + _In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + _Inout_ PIRP Irp + ); + +_Function_class_(IRP_MJ_FLUSH_BUFFERS) +_Function_class_(DRIVER_DISPATCH) +NTSTATUS +FatFsdFlushBuffers ( // implemented in Flush.c + _In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + _Inout_ PIRP Irp + ); + +_Function_class_(IRP_MJ_FILE_SYSTEM_CONTROL) +_Function_class_(DRIVER_DISPATCH) +NTSTATUS +FatFsdFileSystemControl ( // implemented in FsCtrl.c + _In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + _Inout_ PIRP Irp + ); + +_Function_class_(IRP_MJ_LOCK_CONTROL) +_Function_class_(DRIVER_DISPATCH) +NTSTATUS +FatFsdLockControl ( // implemented in LockCtrl.c + _In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + _Inout_ PIRP Irp + ); + +_Function_class_(IRP_MJ_PNP) +_Function_class_(DRIVER_DISPATCH) +NTSTATUS +FatFsdPnp ( // implemented in Pnp.c + _In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + _Inout_ PIRP Irp + ); + +_Function_class_(IRP_MJ_READ) +_Function_class_(DRIVER_DISPATCH) +NTSTATUS +FatFsdRead ( // implemented in Read.c + _In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + _Inout_ PIRP Irp + ); + +_Function_class_(IRP_MJ_SHUTDOWN) +_Function_class_(DRIVER_DISPATCH) +NTSTATUS +FatFsdShutdown ( // implemented in Shutdown.c + _In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + _Inout_ PIRP Irp + ); + +_Function_class_(IRP_MJ_QUERY_VOLUME_INFORMATION) +_Function_class_(DRIVER_DISPATCH) +NTSTATUS +FatFsdQueryVolumeInformation ( // implemented in VolInfo.c + _In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + _Inout_ PIRP Irp + ); + +_Function_class_(IRP_MJ_SET_VOLUME_INFORMATION) +_Function_class_(DRIVER_DISPATCH) +NTSTATUS +FatFsdSetVolumeInformation ( // implemented in VolInfo.c + _In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + _Inout_ PIRP Irp + ); + +_Function_class_(IRP_MJ_WRITE) +_Function_class_(DRIVER_DISPATCH) +NTSTATUS +FatFsdWrite ( // implemented in Write.c + _In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + _Inout_ PIRP Irp + ); + +// +// 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(IRP) IoIsOperationSynchronous(Irp) + + +// +// 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. +// + + +WORKER_THREAD_ROUTINE FatFspDispatch; + +VOID +FatFspDispatch ( // implemented in FspDisp.c + _In_ PVOID Context + ); + +// +// The following routines are the FSP work routines that are called +// by the preceding FatFspDispath routine. Each takes as input a pointer +// to the IRP, perform the function, and return a pointer to the volume +// device object that they just finished servicing (if any). The return +// pointer is then used by the main Fsp dispatch routine to check for +// additional IRPs in the volume's overflow queue. +// +// Each of the following routines is also responsible for completing the IRP. +// We moved this responsibility from the main loop to the individual routines +// to allow them the ability to complete the IRP and continue post processing +// actions. +// + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatCommonCleanup ( // implemented in Cleanup.c + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatCommonClose ( // implemented in Close.c + IN PVCB Vcb, + IN PFCB Fcb, + IN PCCB Ccb, + IN TYPE_OF_OPEN TypeOfOpen, + IN BOOLEAN Wait, + IN BOOLEAN TopLevel, + OUT PBOOLEAN VcbDeleted OPTIONAL + ); + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatFspClose ( // implemented in Close.c + IN PVCB Vcb OPTIONAL + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatCommonCreate ( // implemented in Create.c + _Inout_ PIRP_CONTEXT IrpContext, + _Inout_ PIRP Irp + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatCommonDirectoryControl ( // implemented in DirCtrl.c + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatCommonDeviceControl ( // implemented in DevCtrl.c + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +NTSTATUS +FatCommonQueryEa ( // implemented in Ea.c + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +NTSTATUS +FatCommonSetEa ( // implemented in Ea.c + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatCommonQueryInformation ( // implemented in FileInfo.c + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatCommonSetInformation ( // implemented in FileInfo.c + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatCommonFlushBuffers ( // implemented in Flush.c + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatCommonFileSystemControl ( // implemented in FsCtrl.c + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatCommonLockControl ( // implemented in LockCtrl.c + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatCommonPnp ( // implemented in Pnp.c + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatCommonRead ( // implemented in Read.c + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatCommonShutdown ( // implemented in Shutdown.c + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatCommonQueryVolumeInfo ( // implemented in VolInfo.c + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatCommonSetVolumeInfo ( // implemented in VolInfo.c + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatCommonWrite ( // implemented in Write.c + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +// +// The following is implemented in Flush.c, and does what is says. +// + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatFlushFile ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN FAT_FLUSH_TYPE FlushType + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatFlushDirectory ( + IN PIRP_CONTEXT IrpContext, + IN PDCB Dcb, + IN FAT_FLUSH_TYPE FlushType + ); + +NTSTATUS +FatFlushFat ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatFlushVolume ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN FAT_FLUSH_TYPE FlushType + ); + +NTSTATUS +FatHijackIrpAndFlushDevice ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp, + IN PDEVICE_OBJECT TargetDeviceObject + ); + +VOID +FatFlushFatEntries ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN ULONG Cluster, + IN ULONG Count +); + +VOID +FatFlushDirentForFile ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb +); + + + +// +// The following procedure is used by the FSP and FSD routines to complete +// an IRP. +// +// Note that this macro allows either the Irp or the IrpContext to be +// null, however the only legal order to do this in is: +// +// FatCompleteRequest( NULL, Irp, Status ); // completes Irp & preserves context +// ... +// FatCompleteRequest( IrpContext, NULL, DontCare ); // deallocates context +// +// This would typically be done in order to pass a "naked" IrpContext off to +// the Fsp for post processing, such as read ahead. +// + +VOID +FatCompleteRequest_Real ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp, + IN NTSTATUS Status + ); + +#define FatCompleteRequest(IRPCONTEXT,IRP,STATUS) { \ + FatCompleteRequest_Real(IRPCONTEXT,IRP,STATUS); \ +} + +BOOLEAN +FatIsIrpTopLevel ( + IN PIRP Irp + ); + +// +// The Following routine makes a popup +// + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatPopUpFileCorrupt ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb + ); + +// +// Here are the callbacks used by the I/O system for checking for fast I/O or +// doing a fast query info call, or doing fast lock calls. +// +_Function_class_(FAST_IO_CHECK_IF_POSSIBLE) +BOOLEAN +FatFastIoCheckIfPossible ( + 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 + ); + +_Function_class_(FAST_IO_QUERY_BASIC_INFO) +BOOLEAN +FatFastQueryBasicInfo ( + IN PFILE_OBJECT FileObject, + IN BOOLEAN Wait, + IN OUT PFILE_BASIC_INFORMATION Buffer, + OUT PIO_STATUS_BLOCK IoStatus, + IN PDEVICE_OBJECT DeviceObject + ); + +_Function_class_(FAST_IO_QUERY_STANDARD_INFO) +BOOLEAN +FatFastQueryStdInfo ( + IN PFILE_OBJECT FileObject, + IN BOOLEAN Wait, + IN OUT PFILE_STANDARD_INFORMATION Buffer, + OUT PIO_STATUS_BLOCK IoStatus, + IN PDEVICE_OBJECT DeviceObject + ); + +_Function_class_(FAST_IO_QUERY_NETWORK_OPEN_INFO) +BOOLEAN +FatFastQueryNetworkOpenInfo ( + IN PFILE_OBJECT FileObject, + IN BOOLEAN Wait, + IN OUT PFILE_NETWORK_OPEN_INFORMATION Buffer, + OUT PIO_STATUS_BLOCK IoStatus, + IN PDEVICE_OBJECT DeviceObject + ); + +_Function_class_(FAST_IO_LOCK) +BOOLEAN +FatFastLock ( + IN PFILE_OBJECT FileObject, + IN PLARGE_INTEGER FileOffset, + IN PLARGE_INTEGER Length, + PEPROCESS ProcessId, + ULONG Key, + BOOLEAN FailImmediately, + BOOLEAN ExclusiveLock, + OUT PIO_STATUS_BLOCK IoStatus, + IN PDEVICE_OBJECT DeviceObject + ); + +_Function_class_(FAST_IO_UNLOCK_SINGLE) +BOOLEAN +FatFastUnlockSingle ( + IN PFILE_OBJECT FileObject, + IN PLARGE_INTEGER FileOffset, + IN PLARGE_INTEGER Length, + PEPROCESS ProcessId, + ULONG Key, + OUT PIO_STATUS_BLOCK IoStatus, + IN PDEVICE_OBJECT DeviceObject + ); + +_Function_class_(FAST_IO_UNLOCK_ALL) +BOOLEAN +FatFastUnlockAll ( + IN PFILE_OBJECT FileObject, + PEPROCESS ProcessId, + OUT PIO_STATUS_BLOCK IoStatus, + IN PDEVICE_OBJECT DeviceObject + ); + +_Function_class_(FAST_IO_UNLOCK_ALL_BY_KEY) +BOOLEAN +FatFastUnlockAllByKey ( + IN PFILE_OBJECT FileObject, + PVOID ProcessId, + ULONG Key, + OUT PIO_STATUS_BLOCK IoStatus, + IN PDEVICE_OBJECT DeviceObject + ); + + +VOID +FatExamineFatEntries( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN ULONG StartIndex OPTIONAL, + IN ULONG EndIndex OPTIONAL, + IN BOOLEAN SetupWindows, + IN PFAT_WINDOW SwitchToWindow OPTIONAL, + IN PULONG BitMapBuffer OPTIONAL + ); + +BOOLEAN +FatScanForDataTrack( + IN PIRP_CONTEXT IrpContext, + IN PDEVICE_OBJECT TargetDeviceObject + ); + +// +// The following macro is used to determine is a file has been deleted. +// +// BOOLEAN +// IsFileDeleted ( +// IN PIRP_CONTEXT IrpContext, +// IN PFCB Fcb +// ); +// + +#define IsFileDeleted(IRPCONTEXT,FCB) \ + (FlagOn((FCB)->FcbState, FCB_STATE_DELETE_ON_CLOSE) && \ + ((FCB)->UncleanCount == 0)) + +// +// The following macro is used by the dispatch routines to determine if +// an operation is to be done with or without Write Through. +// +// BOOLEAN +// IsFileWriteThrough ( +// IN PFILE_OBJECT FileObject, +// IN PVCB Vcb +// ); +// + +#define IsFileWriteThrough(FO,VCB) ( \ + BooleanFlagOn((FO)->Flags, FO_WRITE_THROUGH) \ +) + +// +// The following macro is used to set the is fast i/o possible field in +// the common part of the nonpaged fcb. It checks that this is actually +// an FCB (as opposed to a DCB) so that directory oplock code works properly. +// +// +// BOOLEAN +// FatIsFastIoPossible ( +// IN PFCB Fcb +// ); +// + +#define FatIsFastIoPossible(FCB) ((BOOLEAN) \ + ((((FCB)->FcbCondition != FcbGood) || \ + (NodeType( (FCB) ) != FAT_NTC_FCB) || \ + !FsRtlOplockIsFastIoPossible( FatGetFcbOplock(FCB) )) ? \ + FastIoIsNotPossible \ + : \ + (!FsRtlAreThereCurrentFileLocks( &(FCB)->Specific.Fcb.FileLock ) && \ + ((FCB)->NonPaged->OutstandingAsyncWrites == 0) && \ + !FlagOn( (FCB)->Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED ) ? \ + FastIoIsPossible \ + : \ + FastIoIsQuestionable \ + ) \ + ) \ +) + +#if (NTDDI_VERSION >= NTDDI_WIN8) + +// +// Detect whether this file can have an oplock on it. As of Windows 8 file and +// directories can have oplocks. +// + +#define FatIsNodeTypeOplockable(N) ( \ + ((N) == FAT_NTC_FCB) || \ + ((N) == FAT_NTC_ROOT_DCB) || \ + ((N) == FAT_NTC_DCB) \ +) + +#else + +#define FatIsNodeTypeOplockable(N) ( \ + ((N) == FAT_NTC_FCB) \ +) + +#endif + +#define FatIsFileOplockable(F) ( \ + FatIsNodeTypeOplockable( NodeType( (F) )) \ +) + +// +// The following macro is used to detemine if the file object is opened +// for read only access (i.e., it is not also opened for write access or +// delete access). +// +// BOOLEAN +// IsFileObjectReadOnly ( +// IN PFILE_OBJECT FileObject +// ); +// + +#define IsFileObjectReadOnly(FO) (!((FO)->WriteAccess | (FO)->DeleteAccess)) + + +// +// The following two macro are used by the Fsd/Fsp exception handlers to +// process an exception. The first macro is the exception filter used in the +// Fsd/Fsp to decide if an exception should be handled at this level. +// The second macro decides if the exception is to be finished off by +// completing the IRP, and cleaning up the Irp Context, or if we should +// bugcheck. Exception values such as STATUS_FILE_INVALID (raised by +// VerfySup.c) cause us to complete the Irp and cleanup, while exceptions +// such as accvio cause us to bugcheck. +// +// The basic structure for fsd/fsp exception handling is as follows: +// +// FatFsdXxx(...) +// { +// try { +// +// ... +// +// } except(FatExceptionFilter( IrpContext, GetExceptionCode() )) { +// +// Status = FatProcessException( IrpContext, Irp, GetExceptionCode() ); +// } +// +// Return Status; +// } +// +// To explicitly raise an exception that we expect, such as +// STATUS_FILE_INVALID, use the below macro FatRaiseStatus(). To raise a +// status from an unknown origin (such as CcFlushCache()), use the macro +// FatNormalizeAndRaiseStatus. This will raise the status if it is expected, +// or raise STATUS_UNEXPECTED_IO_ERROR if it is not. +// +// If we are vicariously handling exceptions without using FatProcessException(), +// if there is the possibility that we raised that exception, one *must* +// reset the IrpContext so a subsequent raise in the course of handling this +// request that is *not* explicit, i.e. like a pagein error, does not get +// spoofed into believing that the first raise status is the reason the second +// occured. This could have really serious consequences. +// +// It is an excellent idea to always FatResetExceptionState in these cases. +// +// Note that when using these two macros, the original status is placed in +// IrpContext->ExceptionStatus, signaling FatExceptionFilter and +// FatProcessException that the status we actually raise is by definition +// expected. +// + +ULONG +FatExceptionFilter ( + IN PIRP_CONTEXT IrpContext, + IN PEXCEPTION_POINTERS ExceptionPointer + ); + +#if DBG +ULONG +FatBugCheckExceptionFilter ( + IN PEXCEPTION_POINTERS ExceptionPointer + ); +#endif + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatProcessException ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp, + IN NTSTATUS ExceptionCode + ); + +// +// VOID +// FatRaiseStatus ( +// IN PRIP_CONTEXT IrpContext, +// IN NT_STATUS Status +// ); +// +// + +#if DBG +#define DebugBreakOnStatus(S) { \ +__pragma(warning(push)) \ +__pragma(warning(disable:4127)) \ + if (FatTestRaisedStatus) { \ + if ((S) == STATUS_DISK_CORRUPT_ERROR || (S) == STATUS_FILE_CORRUPT_ERROR) { \ +__pragma(warning(pop)) \ + DbgPrint( "FAT: Breaking on interesting raised status (0x%08x)\n", (S) );\ + DbgPrint( "FAT: Set FatTestRaisedStatus @ 0x%p to 0 to disable\n", \ + &FatTestRaisedStatus ); \ + NT_ASSERT(FALSE); \ + } \ + } \ +} +#else +#define DebugBreakOnStatus(S) +#endif + +#define FatRaiseStatus(IRPCONTEXT,STATUS) { \ + (IRPCONTEXT)->ExceptionStatus = (STATUS); \ + DebugBreakOnStatus( (STATUS) ) \ + ExRaiseStatus( (STATUS) ); \ +} + +#define FatResetExceptionState( IRPCONTEXT ) { \ + (IRPCONTEXT)->ExceptionStatus = STATUS_SUCCESS; \ +} + +// +// VOID +// FatNormalAndRaiseStatus ( +// IN PRIP_CONTEXT IrpContext, +// IN NT_STATUS Status +// ); +// + +#define FatNormalizeAndRaiseStatus(IRPCONTEXT,STATUS) { \ + (IRPCONTEXT)->ExceptionStatus = (STATUS); \ + ExRaiseStatus(FsRtlNormalizeNtstatus((STATUS),STATUS_UNEXPECTED_IO_ERROR)); \ +} + + +// +// 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; } + + +CLUSTER_TYPE +FatInterpretClusterType ( + IN PVCB Vcb, + IN FAT_ENTRY Entry + ); + + +// +// These routines define the FileId for FAT. Lacking a fixed/uniquifiable +// notion, we simply come up with one which is unique in a given snapshot +// of the volume. As long as the parent directory is not moved or compacted, +// it may even be permanent. +// + +// +// The internal information used to identify the fcb/dcb on the +// volume is the byte offset of the dirent of the file on disc. +// Our root always has fileid 0. FAT32 roots are chains and can +// use the LBO of the cluster, 12/16 roots use the lbo in the Vcb. +// + +#define FatGenerateFileIdFromDirentOffset(ParentDcb,DirentOffset) \ + ((ParentDcb) ? ((NodeType(ParentDcb) != FAT_NTC_ROOT_DCB || FatIsFat32((ParentDcb)->Vcb)) ? \ + FatGetLboFromIndex( (ParentDcb)->Vcb, \ + (ParentDcb)->FirstClusterOfFile ) : \ + (ParentDcb)->Vcb->AllocationSupport.RootDirectoryLbo) + \ + (DirentOffset) \ + : \ + 0) + +// +// + +#define FatGenerateFileIdFromFcb(Fcb) \ + FatGenerateFileIdFromDirentOffset( (Fcb)->ParentDcb, (Fcb)->DirentOffsetWithinDirectory ) + +// +// Wrap to handle the ./.. cases appropriately. Note that we commute NULL parent to 0. This would +// only occur in an illegal root ".." entry. +// + +#define FATDOT ((ULONG)0x2020202E) +#define FATDOTDOT ((ULONG)0x20202E2E) + +#define FatGenerateFileIdFromDirentAndOffset(Dcb,Dirent,DirentOffset) \ + ((*((PULONG)(Dirent)->FileName)) == FATDOT ? FatGenerateFileIdFromFcb(Dcb) : \ + ((*((PULONG)(Dirent)->FileName)) == FATDOTDOT ? ((Dcb)->ParentDcb ? \ + FatGenerateFileIdFromFcb((Dcb)->ParentDcb) : \ + 0) : \ + FatGenerateFileIdFromDirentOffset(Dcb,DirentOffset))) + + +// +// BOOLEAN +// FatDeviceIsFatFsdo( +// IN PDEVICE_OBJECT D +// ); +// +// Evaluates to TRUE if the supplied device object is one of the file system devices +// we created at initialisation. +// + +#define FatDeviceIsFatFsdo( D) (((D) == FatData.DiskFileSystemDeviceObject) || ((D) == FatData.CdromFileSystemDeviceObject)) + + +// +// BlockAlign(): Aligns P on the next V boundary. +// BlockAlignTruncate(): Aligns P on the prev V boundary. +// + +#define BlockAlign(P,V) ((ASSERT( V != 0)), (((P)) + (V-1) & (0-(V)))) +#define BlockAlignTruncate(P,V) ((P) & (0-(V))) + +#endif // _FATPROCS_ + + diff --git a/filesys/fastfat/fatprocssrc.c b/filesys/fastfat/fatprocssrc.c new file mode 100644 index 000000000..b181cd1c0 --- /dev/null +++ b/filesys/fastfat/fatprocssrc.c @@ -0,0 +1 @@ +#include "fatprocs.h" \ No newline at end of file diff --git a/filesys/fastfat/fatstruc.h b/filesys/fastfat/fatstruc.h new file mode 100644 index 000000000..c4f4e1d70 --- /dev/null +++ b/filesys/fastfat/fatstruc.h @@ -0,0 +1,1707 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + FatStruc.h + +Abstract: + + This module defines the data structures that make up the major internal + part of the Fat file system. + + +--*/ + +#ifndef _FATSTRUC_ +#define _FATSTRUC_ + +typedef PVOID PBCB; //**** Bcb's are now part of the cache module + + +// +// The FAT_DATA record is the top record in the Fat file system in-memory +// data structure. This structure must be allocated from non-paged pool. +// + +typedef struct _FAT_DATA { + + // + // The type and size of this record (must be FAT_NTC_DATA_HEADER) + // + + NODE_TYPE_CODE NodeTypeCode; + NODE_BYTE_SIZE NodeByteSize; + + + PVOID LazyWriteThread; + + + // + // A queue of all the devices that are mounted by the file system. + // + + LIST_ENTRY VcbQueue; + + // + // A pointer to the Driver object we were initialized with + // + + PDRIVER_OBJECT DriverObject; + + // + // A pointer to the filesystem device objects we created. + // + + PVOID DiskFileSystemDeviceObject; + PVOID CdromFileSystemDeviceObject; + + // + // A resource variable to control access to the global Fat data record + // + + ERESOURCE Resource; + + // + // A pointer to our EPROCESS struct, which is a required input to the + // Cache Management subsystem. + // + + PEPROCESS OurProcess; + + // + // Number of processors when the driver loaded. + // + + ULONG NumberProcessors; + + // + // The following tells us if we should use Chicago extensions. + // + + BOOLEAN ChicagoMode:1; + + // + // The following field tells us if we are running on a Fujitsu + // FMR Series. These machines supports extra formats on the + // FAT file system. + // + + BOOLEAN FujitsuFMR:1; + + // + // Inidicates that FspClose is currently processing closes. + // + + BOOLEAN AsyncCloseActive:1; + + // + // The following BOOLEAN says shutdown has started on FAT. It + // instructs FspClose to not keep the Vcb resources anymore. + // + + BOOLEAN ShutdownStarted:1; + + // + // The following flag tells us if we are going to generate LFNs + // for valid 8.3 names with extended characters. + // + + BOOLEAN CodePageInvariant:1; + + // + // The following flags tell us if we are in an aggresive push to lower + // the size of the deferred close queues. + // + + BOOLEAN HighAsync:1; + BOOLEAN HighDelayed:1; + + + // + // The following list entry is used for performing closes that can't + // be done in the context of the original caller. + // + + ULONG AsyncCloseCount; + LIST_ENTRY AsyncCloseList; + + // + // The following two fields record if we are delaying a close. + // + + ULONG DelayedCloseCount; + LIST_ENTRY DelayedCloseList; + + // + // This is the ExWorkerItem that does both kinds of deferred closes. + // + + PIO_WORKITEM FatCloseItem; + + // + // This spinlock protects several rapid-fire operations. NOTE: this is + // pretty horrible style. + // + + KSPIN_LOCK GeneralSpinLock; + + // + // Cache manager call back structures, which must be passed on each call + // to CcInitializeCacheMap. + // + + CACHE_MANAGER_CALLBACKS CacheManagerCallbacks; + CACHE_MANAGER_CALLBACKS CacheManagerNoOpCallbacks; + + + PVOID ZeroPage; + +} FAT_DATA; +typedef FAT_DATA *PFAT_DATA; + +// +// An array of these structures will keep + +typedef struct _FAT_WINDOW { + + ULONG FirstCluster; // The first cluster in this window. + ULONG LastCluster; // The last cluster in this window. + ULONG ClustersFree; // The number of clusters free in this window. + +} FAT_WINDOW; +typedef FAT_WINDOW *PFAT_WINDOW; + +// +// Forward reference some circular referenced structures. +// + +typedef struct _VCB VCB; +typedef VCB *PVCB; + +typedef struct _FCB FCB; +typedef FCB *PFCB; + +// +// This structure is used to keep track of information needed to do a +// deferred close. It is now embedded in a CCB so we don't have to +// allocate one in the close path (with mustsucceed). +// + +typedef struct { + + // + // Two sets of links, one for the global list and one for closes + // on a particular volume. + // + + LIST_ENTRY GlobalLinks; + LIST_ENTRY VcbLinks; + + PVCB Vcb; + PFCB Fcb; + enum _TYPE_OF_OPEN TypeOfOpen; + BOOLEAN Free; + +} CLOSE_CONTEXT; + +typedef CLOSE_CONTEXT *PCLOSE_CONTEXT; + +// +// The Vcb (Volume control Block) record corresponds to every volume mounted +// by the file system. They are ordered in a queue off of FatData.VcbQueue. +// This structure must be allocated from non-paged pool +// + +typedef enum _VCB_CONDITION { + VcbGood = 1, + VcbNotMounted, + VcbBad +} VCB_CONDITION; + +typedef struct _VCB { + + // + // This is a common head for the FAT volume file + // + + FSRTL_ADVANCED_FCB_HEADER VolumeFileHeader; + + // + // The links for the device queue off of FatData.VcbQueue + // + + LIST_ENTRY VcbLinks; + + // + // A pointer the device object passed in by the I/O system on a mount + // This is the target device object that the file system talks to when it + // needs to do any I/O (e.g., the disk stripper device object). + // + // + + PDEVICE_OBJECT TargetDeviceObject; + + // + // A pointer to the VPB for the volume passed in by the I/O system on + // a mount. + // + + PVPB Vpb; + + // + // The internal state of the device. This is a collection of fsd device + // state flags. + // + + ULONG VcbState; + VCB_CONDITION VcbCondition; + + // + // A pointer to the root DCB for this volume + // + + struct _FCB *RootDcb; + + // + // If the FAT has so many entries that the free cluster bitmap would + // be too large, we split the FAT into buckets, and only one bucket's + // worth of bits are kept in the bitmap. + // + + ULONG NumberOfWindows; + PFAT_WINDOW Windows; + PFAT_WINDOW CurrentWindow; + + // + // A count of the number of file objects that have opened the volume + // for direct access, and their share access state. + // + + CLONG DirectAccessOpenCount; + SHARE_ACCESS ShareAccess; + + // + // A count of the number of file objects that have any file/directory + // opened on this volume, not including direct access. And also the + // count of the number of file objects that have a file opened for + // only read access (i.e., they cannot be modifying the disk). + // + + CLONG OpenFileCount; + CLONG ReadOnlyCount; + + // + // A count of the number of internal opens on this VCB. + // + + __volatile ULONG InternalOpenCount; + + // + // A count of the number of residual opens on this volume. + // This is usually two or three. One is for the virutal volume + // file. One is for the root directory. And one is for the + // EA file, if there is one. + // + + __volatile ULONG ResidualOpenCount; + + // + // The bios parameter block field contains + // an unpacked copy of the bpb for the volume, it is initialized + // during mount time and can be read by everyone else after that. + // + + BIOS_PARAMETER_BLOCK Bpb; + + PUCHAR First0x24BytesOfBootSector; + + // + // The following structure contains information useful to the + // allocation support routines. Many of them are computed from + // elements of the Bpb, but are too involved to recompute every time + // they are needed. + // + + struct { + + LBO RootDirectoryLbo; // Lbo of beginning of root directory + LBO FileAreaLbo; // Lbo of beginning of file area + ULONG RootDirectorySize; // size of root directory in bytes + + ULONG NumberOfClusters; // total number of clusters on the volume + ULONG NumberOfFreeClusters; // number of free clusters on the volume + + + UCHAR FatIndexBitSize; // indicates if 12, 16, or 32 bit fat table + + UCHAR LogOfBytesPerSector; // Log(Bios->BytesPerSector) + UCHAR LogOfBytesPerCluster; // Log(Bios->SectorsPerCluster) + + } AllocationSupport; + + // + // The following Mcb is used to keep track of dirty sectors in the Fat. + // Runs of holes denote clean sectors while runs of LBO == VBO denote + // dirty sectors. The VBOs are that of the volume file, starting at + // 0. The granuality of dirt is one sectors, and additions are only + // made in sector chunks to prevent problems with several simultaneous + // updaters. + // + + LARGE_MCB DirtyFatMcb; + + // + // The following MCB contains a list of all the bad clusters on the volume. + // It is empty until the first time the bad sectors on the volume are queried + // by calling FSCTL_GET_RETRIEVAL_POINTERS with a volume handle. + // + + LARGE_MCB BadBlockMcb; + + // + // The FreeClusterBitMap keeps track of all the clusters in the fat. + // A 1 means occupied while a 0 means free. It allows quick location + // of contiguous runs of free clusters. It is initialized on mount + // or verify. + // + + RTL_BITMAP FreeClusterBitMap; + + // + // The following fast mutex controls access to the free cluster bit map + // and the buckets. + // + + FAST_MUTEX FreeClusterBitMapMutex; + + // + // A resource variable to control access to the volume specific data + // structures + // + + ERESOURCE Resource; + + // + // A resource to make sure no one changes the volume bitmap while + // you're using it. Only for volumes with NumberOfWindows > 1. + // + + ERESOURCE ChangeBitMapResource; + + + // + // The following field points to the file object used to do I/O to + // the virtual volume file. The virtual volume file maps sectors + // 0 through the end of fat and is of a fixed size (determined during + // mount) + // + + PFILE_OBJECT VirtualVolumeFile; + + // + // 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 SectionObjectPointers; + + // + // The following fields is a hint cluster index used by the file system + // when allocating a new cluster. + // + + ULONG ClusterHint; + + // + // This field contains the "DeviceObject" that this volume is + // currently mounted on. Note Vcb->Vpb->RealDevice is constant. + // + + PDEVICE_OBJECT CurrentDevice; + + // + // This is a pointer to the file object and the Fcb which represent the ea data. + // + + PFILE_OBJECT VirtualEaFile; + struct _FCB *EaFcb; + + // + // The following field is a pointer to the file object that has the + // volume locked. if the VcbState has the locked flag set. + // + + PFILE_OBJECT FileObjectWithVcbLocked; + + // + // The following is the head of a list of notify Irps. + // + + LIST_ENTRY DirNotifyList; + + // + // The following is used to synchronize the dir notify list. + // + + PNOTIFY_SYNC NotifySync; + + // + // The following fast mutex is used to synchronize directory stream + // file object creation. + // + + FAST_MUTEX DirectoryFileCreationMutex; + + // + // This field holds the thread address of the current (or most recent + // depending on VcbState) thread doing a verify operation on this volume. + // + + PKTHREAD VerifyThread; + + // + // The following two structures are used for CleanVolume callbacks. + // + + KDPC CleanVolumeDpc; + KTIMER CleanVolumeTimer; + + // + // This field records the last time FatMarkVolumeDirty was called, and + // avoids excessive calls to push the CleanVolume forward in time. + // + + LARGE_INTEGER LastFatMarkVolumeDirtyCall; + + // + // The following fields holds a pointer to a struct which is used to + // hold performance counters. + // + + struct _FILE_SYSTEM_STATISTICS *Statistics; + + // + // The property tunneling cache for this volume + // + + TUNNEL Tunnel; + + // + // The media change count is returned by IOCTL_CHECK_VERIFY and + // is used to verify that no user-mode app has swallowed a media change + // notification. This is only meaningful for removable media. + // + + ULONG ChangeCount; + + // + // The device number of the underlying storage device. + // + + ULONG DeviceNumber; + + // + // Preallocated VPB for swapout, so we are not forced to consider + // must succeed pool. + // + + PVPB SwapVpb; + + // + // Per volume threading of the close queues. + // + + LIST_ENTRY AsyncCloseList; + LIST_ENTRY DelayedCloseList; + + // + // Fast mutex used by the ADVANCED FCB HEADER in this structure + // + + FAST_MUTEX AdvancedFcbHeaderMutex; + + // + // How many close contexts were preallocated on this Vcb + // +#if DBG + ULONG CloseContextCount; +#endif +} VCB; +typedef VCB *PVCB; + +#define VCB_STATE_FLAG_LOCKED (0x00000001) +#define VCB_STATE_FLAG_REMOVABLE_MEDIA (0x00000002) +#define VCB_STATE_FLAG_VOLUME_DIRTY (0x00000004) +#define VCB_STATE_FLAG_MOUNTED_DIRTY (0x00000010) +#define VCB_STATE_FLAG_SHUTDOWN (0x00000040) +#define VCB_STATE_FLAG_CLOSE_IN_PROGRESS (0x00000080) +#define VCB_STATE_FLAG_DELETED_FCB (0x00000100) +#define VCB_STATE_FLAG_CREATE_IN_PROGRESS (0x00000200) +#define VCB_STATE_FLAG_BOOT_OR_PAGING_FILE (0x00000800) +#define VCB_STATE_FLAG_DEFERRED_FLUSH (0x00001000) +#define VCB_STATE_FLAG_ASYNC_CLOSE_ACTIVE (0x00002000) +#define VCB_STATE_FLAG_WRITE_PROTECTED (0x00004000) +#define VCB_STATE_FLAG_REMOVAL_PREVENTED (0x00008000) +#define VCB_STATE_FLAG_VOLUME_DISMOUNTED (0x00010000) +#define VCB_STATE_VPB_NOT_ON_DEVICE (0x00020000) +#define VCB_STATE_FLAG_VPB_MUST_BE_FREED (0x00040000) +#define VCB_STATE_FLAG_DISMOUNT_IN_PROGRESS (0x00080000) +#define VCB_STATE_FLAG_BAD_BLOCKS_POPULATED (0x00100000) +#define VCB_STATE_FLAG_HOTPLUGGABLE (0x00200000) + + +// +// N.B - VOLUME_DISMOUNTED is an indication that FSCTL_DISMOUNT volume was +// executed on a volume. It does not replace VcbCondition as an indication +// that the volume is invalid/unrecoverable. +// + +// +// Define the file system statistics struct. Vcb->Statistics points to an +// array of these (one per processor) and they must be 64 byte aligned to +// prevent cache line tearing. +// + +#define FILE_SYSTEM_STATISTICS_WITHOUT_PAD (sizeof( FILESYSTEM_STATISTICS ) + sizeof( FAT_STATISTICS )) + +typedef struct _FILE_SYSTEM_STATISTICS { + + // + // This contains the actual data. + // + + FILESYSTEM_STATISTICS Common; + FAT_STATISTICS Fat; + + // + // Pad this structure to a multiple of 64 bytes. + // + + UCHAR Pad[((FILE_SYSTEM_STATISTICS_WITHOUT_PAD + 0x3f) & ~0x3f) - FILE_SYSTEM_STATISTICS_WITHOUT_PAD]; + +} FILE_SYSTEM_STATISTICS; + +typedef FILE_SYSTEM_STATISTICS *PFILE_SYSTEM_STATISTICS; + + +// +// 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. + // + + 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 a common head for the FAT volume file + // + + FSRTL_COMMON_FCB_HEADER VolumeFileHeader; + + // + // This is the file system specific volume control block. + // + + VCB Vcb; + +} VOLUME_DEVICE_OBJECT; + +typedef VOLUME_DEVICE_OBJECT *PVOLUME_DEVICE_OBJECT; + + +// +// This is the structure used to contains the short name for a file +// + +typedef struct _FILE_NAME_NODE { + + // + // This points back to the Fcb for this file. + // + + struct _FCB *Fcb; + + // + // This is the name of this node. + // + + union { + + OEM_STRING Oem; + + UNICODE_STRING Unicode; + + } Name; + + // + // Marker so we can figure out what kind of name we opened up in + // Fcb searches + // + + BOOLEAN FileNameDos; + + // + // And the links. Our parent Dcb has a pointer to the root entry. + // + + RTL_SPLAY_LINKS Links; + +} FILE_NAME_NODE; +typedef FILE_NAME_NODE *PFILE_NAME_NODE; + +// +// This structure contains fields which must be in non-paged pool. +// + +typedef struct _NON_PAGED_FCB { + + // + // 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 SectionObjectPointers; + + // + // This context is non-zero only if the file currently has asynchronous + // non-cached valid data length extending writes. It allows + // synchronization between pending writes and other operations. + // + + ULONG OutstandingAsyncWrites; + + // + // This event is set when OutstandingAsyncWrites transitions to zero. + // + + PKEVENT OutstandingAsyncEvent; + + // + // This is the mutex that is inserted into the FCB_ADVANCED_HEADER + // FastMutex field + // + + FAST_MUTEX AdvancedFcbHeaderMutex; + + +} NON_PAGED_FCB; + +typedef NON_PAGED_FCB *PNON_PAGED_FCB; + +// +// The Fcb/Dcb record corresponds to every open file and directory, and to +// every directory on an opened path. They are ordered in two queues, one +// queue contains every Fcb/Dcb record off of FatData.FcbQueue, the other +// queue contains only device specific records off of Vcb.VcbSpecificFcbQueue +// + +typedef enum _FCB_CONDITION { + FcbGood = 1, + FcbBad, + FcbNeedsToBeVerified +} FCB_CONDITION; + +typedef struct _FCB { + + // + // The following field is used for fast I/O + // + // The following comments refer to the use of the AllocationSize field + // of the FsRtl-defined header to the nonpaged Fcb. + // + // For a directory when we create a Dcb we will not immediately + // initialize the cache map, instead we will postpone it until our first + // call to FatReadDirectoryFile or FatPrepareWriteDirectoryFile. + // At that time we will search the Fat to find out the current allocation + // size (by calling FatLookupFileAllocationSize) and then initialize the + // cache map to this allocation size. + // + // For a file when we create an Fcb we will not immediately initialize + // the cache map, instead we will postpone it until we need it and + // then we determine the allocation size from either searching the + // fat to determine the real file allocation, or from the allocation + // that we've just allocated if we're creating a file. + // + // A value of -1 indicates that we do not know what the current allocation + // size really is, and need to examine the fat to find it. A value + // of than -1 is the real file/directory allocation size. + // + // Whenever we need to extend the allocation size we call + // FatAddFileAllocation which (if we're really extending the allocation) + // will modify the Fat, Mcb, and update this field. The caller + // of FatAddFileAllocation is then responsible for altering the Cache + // map size. + // + // We are now using the ADVANCED fcb header to support filter contexts + // at the stream level + // + + FSRTL_ADVANCED_FCB_HEADER Header; + + // + // This structure contains fields which must be in non-paged pool. + // + + PNON_PAGED_FCB NonPaged; + + // + // The head of the fat alloaction chain. FirstClusterOfFile == 0 + // means that the file has no current allocation. + // + + ULONG FirstClusterOfFile; + + + // + // The links for the queue of all fcbs for a specific dcb off of + // Dcb.ParentDcbQueue. For the root directory this queue is empty + // For a non-existent fcb this queue is off of the non existent + // fcb queue entry in the vcb. + // + + LIST_ENTRY ParentDcbLinks; + + // + // A pointer to the Dcb that is the parent directory containing + // this fcb. If this record itself is the root dcb then this field + // is null. + // + + struct _FCB *ParentDcb; + + // + // A pointer to the Vcb containing this Fcb + // + + PVCB Vcb; + + // + // The internal state of the Fcb. This is a collection Fcb state flags. + // Also the shared access for each time this file/directory is opened. + // + + ULONG FcbState; + FCB_CONDITION FcbCondition; + SHARE_ACCESS ShareAccess; + +#ifdef SYSCACHE_COMPILE + + // + // For syscache we keep a bitmask that tells us if we have dispatched IO for + // the page aligned chunks of the stream. + // + + PULONG WriteMask; + ULONG WriteMaskData; + +#endif + + // + // A count of the number of file objects that have been opened for + // this file/directory, but not yet been cleaned up yet. This count + // is only used for data file objects, not for the Acl or Ea stream + // file objects. This count gets decremented in FatCommonCleanup, + // while the OpenCount below gets decremented in FatCommonClose. + // + + CLONG UncleanCount; + + // + // A count of the number of file objects that have opened + // this file/directory. For files & directories the FsContext of the + // file object points to this record. + // + + CLONG OpenCount; + + // + // A count of how many of "UncleanCount" handles were opened for + // non-cached I/O. + // + + CLONG NonCachedUncleanCount; + + // + // A count of purge failure mode references. A non zero count means + // purge failure mode is enabled. The count is synchronized by the + // Fcb. + // + + CLONG PurgeFailureModeEnableCount; + + // + // The following field is used to locate the dirent for this fcb/dcb. + // All directory are opened as mapped files so the only additional + // information we need to locate this dirent (beside its parent directory) + // is the byte offset for the dirent. Note that for the root dcb + // this field is not used. + // + + VBO DirentOffsetWithinDirectory; + + // + // The following field is filled in when there is an Lfn associated + // with this file. It is the STARTING offset of the Lfn. + // + + VBO LfnOffsetWithinDirectory; + + // + // Thess entries is kept in ssync with the dirent. It allows a more + // accurate verify capability and speeds up FatFastQueryBasicInfo(). + // + + LARGE_INTEGER CreationTime; + LARGE_INTEGER LastAccessTime; + LARGE_INTEGER LastWriteTime; + + // + // Valid data to disk + // + + ULONG ValidDataToDisk; + + // + // The following field contains the retrieval mapping structure + // for the file/directory. Note that for the Root Dcb this + // structure is set at mount time. Also note that in this + // implementation of Fat the Mcb really maps VBOs to LBOs and not + // VBNs to LBNs. + // + + LARGE_MCB Mcb; + + // + // The following union is cased off of the node type code for the fcb. + // There is a seperate case for the directory versus file fcbs. + // + + union { + + // + // A Directory Control Block (Dcb) + // + + struct { + + // + // A queue of all the fcbs/dcbs that are opened under this + // Dcb. + // + + LIST_ENTRY ParentDcbQueue; + + // + // The following field points to the file object used to do I/O to + // the directory file for this dcb. The directory file maps the + // sectors for the directory. This field is initialized by + // CreateRootDcb but is left null by CreateDcb. It isn't + // until we try to read/write the directory file that we + // create the stream file object for non root dcbs. + // + + __volatile ULONG DirectoryFileOpenCount; + PFILE_OBJECT DirectoryFile; + + + // + // If the UnusedDirentVbo is != 0xffffffff, then the dirent at this + // offset is guarenteed to unused. A value of 0xffffffff means + // it has yet to be initialized. Note that a value beyond the + // end of allocation means that there an unused dirent, but we + // will have to allocate another cluster to use it. + // + // DeletedDirentHint contains lowest possible VBO of a deleted + // dirent (assuming as above that it is not 0xffffffff). + // + + VBO UnusedDirentVbo; + VBO DeletedDirentHint; + + // + // The following two entries links together all the Fcbs + // opened under this Dcb sorted in a splay tree by name. + // + // I'd like to go into why we have (and must have) two separate + // splay trees within the current fastfat architecture. I will + // provide some insight into what would have to change if we + // wanted to have a single UNICODE tree. + // + // What makes FAT unique is that both Oem and Unicode names sit + // side by side on disk. Several unique UNICODE names coming + // into fastfat can match a single OEM on-disk name, and there + // is really no way to enumerate all the possible UNICODE + // source strings that can map to a given OEM name. This argues + // for converting the incomming UNICODE name into OEM, and then + // running through an OEM splay tree of the open files. This + // works well when there are only OEM names on disk. + // + // The UNICODE name on disk can be VERY different from the short + // name in the DIRENT and not even representable in the OEM code + // page. Even if it were representable in OEM, it is possible + // that a case varient of the original UNICODE name would match + // a different OEM name, causing us to miss the Fcb in the + // prefix lookup phase. In these cases, we must put UNICODE + // name in the splay to guarentee that we find any case varient + // of the input UNICODE name. See the routine description of + // FatConstructNamesInFcb() for a detailed analysis of how we + // detect this case. + // + // The fundamental limitation we are imposing here is that if + // an Fcb exists for an open file, we MUST find it during the + // prefix stage. This is a basic premise of the create path + // in fastfat. In fact if we later find it gravelling through + // the disk (but not the splay tree), we will bug check if we + // try to add a duplicate entry to the splay tree (not to + // mention having two Fcbs). If we had some mechanism to deal + // with cases (and they would be rare) that we don't find the + // entry in the splay tree, but the Fcb is actually in there, + // then we could go to a single UNICODE splay tree. While + // this uses more pool for the splay tree, and makes string + // compares maybe take a bit as longer, it would eliminate the + // need for any NLS conversion during the prefix phase, so it + // might really be a net win. + // + // The current scheme was optimized for non-extended names + // (i.e. US names). As soon as you start using extended + // characters, then it is clearly a win as many code paths + // become active that would otherwise not be needed if we + // only had a single UNICODE splay tree. + // + // We may think about changing this someday. + // + + PRTL_SPLAY_LINKS RootOemNode; + PRTL_SPLAY_LINKS RootUnicodeNode; + + // + // The following field keeps track of free dirents, i.e., + // dirents that are either unallocated for deleted. + // + + RTL_BITMAP FreeDirentBitmap; + + // + // Since the FCB specific part of this union is larger, use + // the slack here for an initial bitmap buffer. Currently + // there is enough space here for an 8K cluster. + // + + ULONG FreeDirentBitmapBuffer[1]; + + } Dcb; + + // + // A File Control Block (Fcb) + // + + struct { + + // + // The following field is used by the filelock module + // to maintain current byte range locking information. + // + + FILE_LOCK FileLock; + +#if (NTDDI_VERSION < NTDDI_WIN8) + // + // The following field is used by the oplock module + // to maintain current oplock information. + // + + OPLOCK Oplock; +#endif + + // + // This pointer is used to detect writes that eminated in the + // cache manager's lazywriter. It prevents lazy writer threads, + // who already have the Fcb shared, from trying to acquire it + // exclusive, and thus causing a deadlock. + // + + PVOID LazyWriteThread; + + + } Fcb; + + } Specific; + + // + // The following field is used to verify that the Ea's for a file + // have not changed between calls to query for Ea's. It is compared + // with a similar field in a Ccb. + // + // IMPORTANT!! **** DO NOT MOVE THIS FIELD **** + // + // The slack space in the union above is computed from + // the field offset of the EaModificationCount. + // + + ULONG EaModificationCount; + + // + // The following field is the fully qualified file name for this FCB/DCB + // starting from the root of the volume, and last file name in the + // fully qualified name. + // + + FILE_NAME_NODE ShortName; + + // + // The following field is only filled in if it is needed with the user's + // opened path + // + + UNICODE_STRING FullFileName; + + USHORT FinalNameLength; + + // + // To make life simpler we also keep in the Fcb/Dcb a current copy of + // the fat attribute byte for the file/directory. This field must + // also be updated when we create the Fcb, modify the File, or verify + // the Fcb + // + + UCHAR DirentFatFlags; + + // + // The case preserved long filename + // + + UNICODE_STRING ExactCaseLongName; + + // + // If the UNICODE Lfn is fully expressible in the system Oem code + // page, then we will store it in a prefix table, otherwise we will + // store the last UNICODE name in the Fcb. In both cases the name + // has been upcased. + // + // Note that we may need neither of these fields if an LFN was strict + // 8.3 or differed only in case. Indeed if there wasn't an LFN, we + // don't need them at all. + // + + union { + + // + // This first field is present if FCB_STATE_HAS_OEM_LONG_NAME + // is set in the FcbState. + // + + FILE_NAME_NODE Oem; + + // + // This first field is present if FCB_STATE_HAS_UNICODE_LONG_NAME + // is set in the FcbState. + // + + FILE_NAME_NODE Unicode; + + } LongName; + + // + // Defragmentation / ReallocateOnWrite synchronization object. This + // is filled in by FatMoveFile() and affects the read and write paths. + // + + PKEVENT MoveFileEvent; + +} FCB, *PFCB; + +#ifndef BUILDING_FSKDEXT +// +// DCB clashes with a type defined outside the filesystems, in headers +// pulled in by FSKD. We don't need this typedef for fskd anyway.... +// +typedef FCB DCB; +typedef DCB *PDCB; +#endif + + +// +// Here are the Fcb state fields. +// + +#define FCB_STATE_DELETE_ON_CLOSE (0x00000001) +#define FCB_STATE_TRUNCATE_ON_CLOSE (0x00000002) +#define FCB_STATE_PAGING_FILE (0x00000004) +#define FCB_STATE_FORCE_MISS_IN_PROGRESS (0x00000008) +#define FCB_STATE_FLUSH_FAT (0x00000010) +#define FCB_STATE_TEMPORARY (0x00000020) +#define FCB_STATE_SYSTEM_FILE (0x00000080) +#define FCB_STATE_NAMES_IN_SPLAY_TREE (0x00000100) +#define FCB_STATE_HAS_OEM_LONG_NAME (0x00000200) +#define FCB_STATE_HAS_UNICODE_LONG_NAME (0x00000400) +#define FCB_STATE_DELAY_CLOSE (0x00000800) + +// +// Copies of the dirent's FAT_DIRENT_NT_BYTE_* flags for +// preserving case of the short name of a file +// + +#define FCB_STATE_8_LOWER_CASE (0x00001000) +#define FCB_STATE_3_LOWER_CASE (0x00002000) + +// +// indicates FSCTL_MOVE_FILE is denied on this FCB +// + +#define FCB_STATE_DENY_DEFRAG (0x00004000) + + +// +// Flag to indicate we should zero any deallocations from this file. +// + +#define FCB_STATE_ZERO_ON_DEALLOCATION (0x00080000) + +// +// This is the slack allocation in the Dcb part of the UNION above +// + +#define DCB_UNION_SLACK_SPACE ((ULONG) \ + (FIELD_OFFSET(DCB, EaModificationCount) - \ + FIELD_OFFSET(DCB, Specific.Dcb.FreeDirentBitmapBuffer)) \ +) + +// +// This is the special (64bit) allocation size that indicates the +// real size must be retrieved from disk. Define it here so we +// avoid excessive magic numbering around the driver. +// + +#define FCB_LOOKUP_ALLOCATIONSIZE_HINT ((LONGLONG) -1) + + +// +// The Ccb record is allocated for every file object. Note that this +// record is exactly 0x34 long on x86 so that it will fit into a 0x40 +// piece of pool. Please carefully consider modifications. +// +// Define the Flags field. +// + +#define CCB_FLAG_MATCH_ALL (0x0001) +#define CCB_FLAG_SKIP_SHORT_NAME_COMPARE (0x0002) + +// +// This tells us whether we allocated buffers to hold search templates. +// + +#define CCB_FLAG_FREE_OEM_BEST_FIT (0x0004) +#define CCB_FLAG_FREE_UNICODE (0x0008) + +// +// These flags prevents cleanup from updating the modify time, etc. +// + +#define CCB_FLAG_USER_SET_LAST_WRITE (0x0010) +#define CCB_FLAG_USER_SET_LAST_ACCESS (0x0020) +#define CCB_FLAG_USER_SET_CREATION (0x0040) + +// +// This bit says the file object associated with this Ccb was opened for +// read only access. +// + +#define CCB_FLAG_READ_ONLY (0x0080) + +// +// These flags, are used is DASD handles in read and write. +// + +#define CCB_FLAG_DASD_FLUSH_DONE (0x0100) +#define CCB_FLAG_DASD_PURGE_DONE (0x0200) + +// +// This flag keeps track of a handle that was opened for +// DELETE_ON_CLOSE. +// + +#define CCB_FLAG_DELETE_ON_CLOSE (0x0400) + +// +// This flag keeps track of which side of the name pair on the file +// associated with the handle was opened +// + +#define CCB_FLAG_OPENED_BY_SHORTNAME (0x0800) + +// +// This flag indicates that the query template has not been upcased +// (i.e., query should be case-insensitive) +// + +#define CCB_FLAG_QUERY_TEMPLATE_MIXED (0x1000) + +// +// This flag indicates that reads and writes via this DASD handle +// are allowed to start or extend past the end of file. +// + +#define CCB_FLAG_ALLOW_EXTENDED_DASD_IO (0x2000) + +// +// This flag indicates we want to match volume labels in directory +// searches (important for the root dir defrag). +// + +#define CCB_FLAG_MATCH_VOLUME_ID (0x4000) + +// +// This flag indicates the ccb has been converted over into a +// close context for asynchronous/delayed closing of the handle. +// + +#define CCB_FLAG_CLOSE_CONTEXT (0x8000) + +// +// This flag indicates that when the handle is closed, we want +// a physical dismount to occur. +// + +#define CCB_FLAG_COMPLETE_DISMOUNT (0x10000) + +// +// This flag indicates the handle may not call priveleged +// FSCTL which modify the volume. +// + +#define CCB_FLAG_MANAGE_VOLUME_ACCESS (0x20000) + +// +// This flag indicates that a format unit commmand was issued +// on this handle and all subsequent writes need to ignore verify. +// + +#define CCB_FLAG_SENT_FORMAT_UNIT (0x40000) + +// +// This flag indicates that this CCB was the one that marked the +// handle as non-movable. +// + +#define CCB_FLAG_DENY_DEFRAG (0x80000) + +typedef struct _CCB { + + // + // Type and size of this record (must be FAT_NTC_CCB) + // + + NODE_TYPE_CODE NodeTypeCode; + NODE_BYTE_SIZE NodeByteSize; + + // + // Define a 24bit wide field for Flags, but a UCHAR for Wild Cards Present + // since it is used so often. Line these up on byte boundaries for grins. + // + + ULONG Flags:24; + BOOLEAN ContainsWildCards; + + // + // Overlay a close context on the data of the CCB. The remaining + // fields are not useful during close, and we would like to avoid + // paying extra pool for it. + // + + union { + + struct { + + // + // Save the offset to start search from. + // + + VBO OffsetToStartSearchFrom; + + // + // The query template is used to filter directory query requests. + // It originally is set to null and on the first call the NtQueryDirectory + // it is set to the input filename or "*" if the name is not supplied. + // All subsquent queries then use this template. + // + // The Oem structure are unions because if the name is wild we store + // the arbitrary length string, while if the name is constant we store + // 8.3 representation for fast comparison. + // + + union { + + // + // If the template contains a wild card use this. + // + + OEM_STRING Wild; + + // + // If the name is constant, use this part. + // + + FAT8DOT3 Constant; + + } OemQueryTemplate; + + UNICODE_STRING UnicodeQueryTemplate; + + // + // The field is compared with the similar field in the Fcb to determine + // if the Ea's for a file have been modified. + // + + ULONG EaModificationCount; + + // + // The following field is used as an offset into the Eas for a + // particular file. This will be the offset for the next + // Ea to return. A value of 0xffffffff indicates that the + // Ea's are exhausted. + // + + ULONG OffsetOfNextEaToReturn; + + }; + + CLOSE_CONTEXT CloseContext; + }; + +} CCB; +typedef CCB *PCCB; + +// +// The Irp Context record is allocated for every orginating Irp. It is +// created by the Fsd dispatch routines, and deallocated by the FatComplete +// request routine. It contains a structure called of type REPINNED_BCBS +// which is used to retain pinned bcbs needed to handle abnormal termination +// unwinding. +// + +#define REPINNED_BCBS_ARRAY_SIZE (4) + +typedef struct _REPINNED_BCBS { + + // + // A pointer to the next structure contains additional repinned bcbs + // + + struct _REPINNED_BCBS *Next; + + // + // A fixed size array of pinned bcbs. Whenever a new bcb is added to + // the repinned bcb structure it is added to this array. If the + // array is already full then another repinned bcb structure is allocated + // and pointed to with Next. + // + + PBCB Bcb[ REPINNED_BCBS_ARRAY_SIZE ]; + +} REPINNED_BCBS; +typedef REPINNED_BCBS *PREPINNED_BCBS; + +typedef struct _IRP_CONTEXT { + + // + // Type and size of this record (must be FAT_NTC_IRP_CONTEXT) + // + + NODE_TYPE_CODE NodeTypeCode; + NODE_BYTE_SIZE NodeByteSize; + + // + // This structure is used for posting to the Ex worker threads. + // + + WORK_QUEUE_ITEM WorkQueueItem; + + // + // A pointer to the originating Irp. + // + + PIRP OriginatingIrp; + + // + // Originating Device (required for workque algorithms) + // + + PDEVICE_OBJECT RealDevice; + + // + // Originating Vcb (required for exception handling) + // On mounts, this will be set before any exceptions + // indicating corruption can be thrown. + // + + PVCB Vcb; + + // + // Major and minor function codes copied from the Irp + // + + UCHAR MajorFunction; + UCHAR MinorFunction; + + // + // The following fields indicate if we can wait/block for a resource + // or I/O, if we are to do everything write through, and if this + // entry into the Fsd is a recursive call. + // + + UCHAR PinCount; + + ULONG Flags; + + // + // The following field contains the NTSTATUS value used when we are + // unwinding due to an exception + // + + NTSTATUS ExceptionStatus; + + // + // The following context block is used for non-cached Io + // + + struct _FAT_IO_CONTEXT *FatIoContext; + + // + // For a abnormal termination unwinding this field contains the Bcbs + // that are kept pinned until the Irp is completed. + // + + REPINNED_BCBS Repinned; + + +} IRP_CONTEXT; +typedef IRP_CONTEXT *PIRP_CONTEXT; + +#define IRP_CONTEXT_FLAG_DISABLE_DIRTY (0x00000001) +#define IRP_CONTEXT_FLAG_WAIT (0x00000002) +#define IRP_CONTEXT_FLAG_WRITE_THROUGH (0x00000004) +#define IRP_CONTEXT_FLAG_DISABLE_WRITE_THROUGH (0x00000008) +#define IRP_CONTEXT_FLAG_RECURSIVE_CALL (0x00000010) +#define IRP_CONTEXT_FLAG_DISABLE_POPUPS (0x00000020) +#define IRP_CONTEXT_FLAG_DEFERRED_WRITE (0x00000040) +#define IRP_CONTEXT_FLAG_VERIFY_READ (0x00000080) +#define IRP_CONTEXT_STACK_IO_CONTEXT (0x00000100) +#define IRP_CONTEXT_FLAG_IN_FSP (0x00000200) +#define IRP_CONTEXT_FLAG_USER_IO (0x00000400) // for performance counters +#define IRP_CONTEXT_FLAG_DISABLE_RAISE (0x00000800) +#define IRP_CONTEXT_FLAG_OVERRIDE_VERIFY (0x00001000) +#define IRP_CONTEXT_FLAG_CLEANUP_BREAKING_OPLOCK (0x00002000) + + +#define IRP_CONTEXT_FLAG_PARENT_BY_CHILD (0x80000000) + + +// +// Context structure for non-cached I/O calls. Most of these fields +// are actually only required for the Read/Write Multiple 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 _FAT_IO_CONTEXT { + + // + // These two field are used for multiple run Io + // + + __volatile LONG IrpCount; + PIRP MasterIrp; + + // + // MDL to describe partial sector zeroing + // + + PMDL ZeroMdl; + + union { + + // + // This element handles the asychronous non-cached Io + // + + struct { + PERESOURCE Resource; + PERESOURCE Resource2; + ERESOURCE_THREAD ResourceThreadId; + ULONG RequestedByteCount; + PFILE_OBJECT FileObject; + PNON_PAGED_FCB NonPagedFcb; + } Async; + + // + // and this element the sycnrhonous non-cached Io + // + + KEVENT SyncEvent; + + } Wait; + + +} FAT_IO_CONTEXT; + +typedef FAT_IO_CONTEXT *PFAT_IO_CONTEXT; + +// +// An array of these structures is passed to FatMultipleAsync describing +// a set of runs to execute in parallel. +// + +typedef struct _IO_RUNS { + + LBO Lbo; + VBO Vbo; + ULONG Offset; + ULONG ByteCount; + PIRP SavedIrp; + +} IO_RUN; + +typedef IO_RUN *PIO_RUN; + +// +// This structure is used by FatDeleteDirent to preserve the first cluster +// and file size info for undelete utilities. +// + +typedef struct _DELETE_CONTEXT { + + ULONG FileSize; + ULONG FirstClusterOfFile; + +} DELETE_CONTEXT; + +typedef DELETE_CONTEXT *PDELETE_CONTEXT; + +// +// This record is used with to set a flush to go off one second after the +// first write on slow devices with a physical indication of activity, like +// a floppy. This is an attempt to keep the red light on. +// + +typedef struct _DEFERRED_FLUSH_CONTEXT { + + KDPC Dpc; + KTIMER Timer; + WORK_QUEUE_ITEM Item; + + PFILE_OBJECT File; + +} DEFERRED_FLUSH_CONTEXT; + +typedef DEFERRED_FLUSH_CONTEXT *PDEFERRED_FLUSH_CONTEXT; + +// +// This structure is used for the FatMarkVolumeClean callbacks. +// + +typedef struct _CLEAN_AND_DIRTY_VOLUME_PACKET { + + WORK_QUEUE_ITEM Item; + PIRP Irp; + PVCB Vcb; + PKEVENT Event; +} CLEAN_AND_DIRTY_VOLUME_PACKET, *PCLEAN_AND_DIRTY_VOLUME_PACKET; + +// +// This structure is used when a page fault is running out of stack. +// + +typedef struct _PAGING_FILE_OVERFLOW_PACKET { + PIRP Irp; + PFCB Fcb; +} PAGING_FILE_OVERFLOW_PACKET, *PPAGING_FILE_OVERFLOW_PACKET; + +// +// This structure is used to access the EaFile. +// + +#define EA_BCB_ARRAY_SIZE 8 + +typedef struct _EA_RANGE { + + PCHAR Data; + ULONG StartingVbo; + ULONG Length; + USHORT BcbChainLength; + BOOLEAN AuxilaryBuffer; + PBCB *BcbChain; + PBCB BcbArray[EA_BCB_ARRAY_SIZE]; + +} EA_RANGE, *PEA_RANGE; + +#define EA_RANGE_HEADER_SIZE (FIELD_OFFSET( EA_RANGE, BcbArray )) + +// +// These symbols are used by the upcase/downcase routines. +// + +#define WIDE_LATIN_CAPITAL_A (0xff21) +#define WIDE_LATIN_CAPITAL_Z (0xff3a) +#define WIDE_LATIN_SMALL_A (0xff41) +#define WIDE_LATIN_SMALL_Z (0xff5a) + +// +// These values are returned by FatInterpretClusterType. +// + +typedef enum _CLUSTER_TYPE { + FatClusterAvailable, + FatClusterReserved, + FatClusterBad, + FatClusterLast, + FatClusterNext +} CLUSTER_TYPE; + + +#endif // _FATSTRUC_ + + diff --git a/filesys/fastfat/fileinfo.c b/filesys/fastfat/fileinfo.c new file mode 100644 index 000000000..80d695975 --- /dev/null +++ b/filesys/fastfat/fileinfo.c @@ -0,0 +1,5010 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + FileInfo.c + +Abstract: + + This module implements the File Information routines for Fat called by + the dispatch driver. + + +--*/ + +#include "FatProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (FAT_BUG_CHECK_FILEINFO) + +// +// The local debug trace level +// + +#define Dbg (DEBUG_TRACE_FILEINFO) + +VOID +FatQueryBasicInfo ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN PFILE_OBJECT FileObject, + IN OUT PFILE_BASIC_INFORMATION Buffer, + IN OUT PLONG Length + ); + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatQueryStandardInfo ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN OUT PFILE_STANDARD_INFORMATION Buffer, + IN OUT PLONG Length + ); + +VOID +FatQueryInternalInfo ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN OUT PFILE_INTERNAL_INFORMATION Buffer, + IN OUT PLONG Length + ); + +VOID +FatQueryEaInfo ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN OUT PFILE_EA_INFORMATION Buffer, + IN OUT PLONG Length + ); + +VOID +FatQueryPositionInfo ( + IN PIRP_CONTEXT IrpContext, + IN PFILE_OBJECT FileObject, + IN OUT PFILE_POSITION_INFORMATION Buffer, + IN OUT PLONG Length + ); + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatQueryNameInfo ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN PCCB Ccb, + IN BOOLEAN Normalized, + IN OUT PFILE_NAME_INFORMATION Buffer, + IN OUT PLONG Length + ); + +VOID +FatQueryShortNameInfo ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN OUT PFILE_NAME_INFORMATION Buffer, + IN OUT PLONG Length + ); + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatQueryNetworkInfo ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN PFILE_OBJECT FileObject, + IN OUT PFILE_NETWORK_OPEN_INFORMATION Buffer, + IN OUT PLONG Length + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatSetBasicInfo ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp, + IN PFCB Fcb, + IN PCCB Ccb + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatSetDispositionInfo ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp, + IN PFILE_OBJECT FileObject, + IN PFCB Fcb + ); + +NTSTATUS +FatSetRenameInfo ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp, + IN PVCB Vcb, + IN PFCB Fcb, + IN PCCB Ccb + ); + +NTSTATUS +FatSetPositionInfo ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp, + IN PFILE_OBJECT FileObject + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatSetAllocationInfo ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp, + IN PFCB Fcb, + IN PFILE_OBJECT FileObject + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatSetEndOfFileInfo ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp, + IN PFILE_OBJECT FileObject, + IN PVCB Vcb, + IN PFCB Fcb + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatSetValidDataLengthInfo ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp, + IN PFILE_OBJECT FileObject, + IN PFCB Fcb, + IN PCCB Ccb + ); + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatRenameEAs ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN USHORT ExtendedAttributes, + IN POEM_STRING OldOemName + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, FatCommonQueryInformation) +#pragma alloc_text(PAGE, FatCommonSetInformation) +#pragma alloc_text(PAGE, FatFsdQueryInformation) +#pragma alloc_text(PAGE, FatFsdSetInformation) +#pragma alloc_text(PAGE, FatQueryBasicInfo) +#pragma alloc_text(PAGE, FatQueryEaInfo) +#pragma alloc_text(PAGE, FatQueryInternalInfo) +#pragma alloc_text(PAGE, FatQueryNameInfo) +#pragma alloc_text(PAGE, FatQueryNetworkInfo) +#pragma alloc_text(PAGE, FatQueryShortNameInfo) +#pragma alloc_text(PAGE, FatQueryPositionInfo) +#pragma alloc_text(PAGE, FatQueryStandardInfo) +#pragma alloc_text(PAGE, FatSetAllocationInfo) +#pragma alloc_text(PAGE, FatSetBasicInfo) +#pragma alloc_text(PAGE, FatSetDispositionInfo) +#pragma alloc_text(PAGE, FatSetEndOfFileInfo) +#pragma alloc_text(PAGE, FatSetValidDataLengthInfo) +#pragma alloc_text(PAGE, FatSetPositionInfo) +#pragma alloc_text(PAGE, FatSetRenameInfo) +#pragma alloc_text(PAGE, FatDeleteFile) +#pragma alloc_text(PAGE, FatRenameEAs) +#endif + + +_Function_class_(IRP_MJ_QUERY_INFORMATION) +_Function_class_(DRIVER_DISPATCH) +NTSTATUS +FatFsdQueryInformation ( + _In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + _Inout_ PIRP Irp + ) + +/*++ + +Routine Description: + + This routine implements the Fsd part of the NtQueryInformationFile API + call. + +Arguments: + + VolumeDeviceObject - Supplies the volume device object where the file + being queried exists. + + Irp - Supplies the Irp being processed. + +Return Value: + + NTSTATUS - The FSD status for the Irp. + +--*/ + +{ + NTSTATUS Status; + PIRP_CONTEXT IrpContext = NULL; + + BOOLEAN TopLevel; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatFsdQueryInformation\n", 0); + + // + // Call the common query routine, with blocking allowed if synchronous + // + + FsRtlEnterFileSystem(); + + TopLevel = FatIsIrpTopLevel( Irp ); + + try { + + IrpContext = FatCreateIrpContext( Irp, CanFsdWait( Irp ) ); + + Status = FatCommonQueryInformation( IrpContext, Irp ); + + } except(FatExceptionFilter( IrpContext, GetExceptionInformation() )) { + + // + // We had some trouble trying to perform the requested + // operation, so we'll abort the I/O request with + // the error status that we get back from the + // execption code + // + + Status = FatProcessException( IrpContext, Irp, GetExceptionCode() ); + } + + if (TopLevel) { IoSetTopLevelIrp( NULL ); } + + FsRtlExitFileSystem(); + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "FatFsdQueryInformation -> %08lx\n", Status); + + UNREFERENCED_PARAMETER( VolumeDeviceObject ); + + return Status; +} + + +_Function_class_(IRP_MJ_SET_INFORMATION) +_Function_class_(DRIVER_DISPATCH) +NTSTATUS +FatFsdSetInformation ( + _In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + _Inout_ PIRP Irp + ) + +/*++ + +Routine Description: + + This routine implements the FSD part of the NtSetInformationFile API + call. + +Arguments: + + VolumeDeviceObject - Supplies the volume device object where the file + being set exists. + + Irp - Supplies the Irp being processed. + +Return Value: + + NTSTATUS - The FSD status for the Irp. + +--*/ + +{ + NTSTATUS Status; + PIRP_CONTEXT IrpContext = NULL; + + BOOLEAN TopLevel; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatFsdSetInformation\n", 0); + + // + // Call the common set routine, with blocking allowed if synchronous + // + + FsRtlEnterFileSystem(); + + TopLevel = FatIsIrpTopLevel( Irp ); + + try { + + IrpContext = FatCreateIrpContext( Irp, CanFsdWait( Irp ) ); + + Status = FatCommonSetInformation( IrpContext, Irp ); + + } except(FatExceptionFilter( IrpContext, GetExceptionInformation() )) { + + // + // We had some trouble trying to perform the requested + // operation, so we'll abort the I/O request with + // the error status that we get back from the + // execption code + // + + Status = FatProcessException( IrpContext, Irp, GetExceptionCode() ); + } + + if (TopLevel) { IoSetTopLevelIrp( NULL ); } + + FsRtlExitFileSystem(); + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "FatFsdSetInformation -> %08lx\n", Status); + + UNREFERENCED_PARAMETER( VolumeDeviceObject ); + + return Status; +} + + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatCommonQueryInformation ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the common routine for querying file 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; + + PIO_STACK_LOCATION IrpSp; + + PFILE_OBJECT FileObject; + + LONG Length; + FILE_INFORMATION_CLASS FileInformationClass; + PVOID Buffer; + + TYPE_OF_OPEN TypeOfOpen; + PVCB Vcb; + PFCB Fcb; + PCCB Ccb; + + BOOLEAN FcbAcquired = FALSE; + BOOLEAN VcbAcquired = FALSE; + + PFILE_ALL_INFORMATION AllInfo; + + PAGED_CODE(); + + // + // Get the current stack location + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + FileObject = IrpSp->FileObject; + + DebugTrace(+1, Dbg, "FatCommonQueryInformation...\n", 0); + DebugTrace( 0, Dbg, "Irp = %p\n", Irp); + DebugTrace( 0, Dbg, "->Length = %08lx\n", IrpSp->Parameters.QueryFile.Length); + DebugTrace( 0, Dbg, "->FileInformationClass = %08lx\n", IrpSp->Parameters.QueryFile.FileInformationClass); + DebugTrace( 0, Dbg, "->Buffer = %p\n", Irp->AssociatedIrp.SystemBuffer); + + // + // Reference our input parameters to make things easier + // + + Length = (LONG)IrpSp->Parameters.QueryFile.Length; + FileInformationClass = IrpSp->Parameters.QueryFile.FileInformationClass; + Buffer = Irp->AssociatedIrp.SystemBuffer; + + // + // Decode the file object + // + + TypeOfOpen = FatDecodeFileObject( FileObject, &Vcb, &Fcb, &Ccb ); + + Status = STATUS_SUCCESS; + + try { + + // + // Case on the type of open we're dealing with + // + + switch (TypeOfOpen) { + + case UserVolumeOpen: + + // + // We cannot query the user volume open. + // + + Status = STATUS_INVALID_PARAMETER; + break; + + case UserFileOpen: + case UserDirectoryOpen: + case DirectoryFile: + + + // + // NameInfo requires synchronization with deletion in order to perform + // the full filename query. A lighter-weight way to do this would be per + // directory as the full name is built up and since the multiple Fcb + // lockorder is bottom up, this is conceivable. At this time, though, + // this change is safer. + // + + if (FileInformationClass == FileNameInformation || + FileInformationClass == FileNormalizedNameInformation || + FileInformationClass == FileAllInformation ) { + + if (!FatAcquireExclusiveVcb( IrpContext, Vcb )) { + + DebugTrace(0, Dbg, "Cannot acquire Vcb\n", 0); + + Status = FatFsdPostRequest( IrpContext, Irp ); + IrpContext = NULL; + Irp = NULL; + + try_return( Status ); + } + + VcbAcquired = TRUE; + } + + // + // Acquire shared access to the fcb, except for a paging file + // in order to avoid deadlocks with Mm. + // + // The "removable" check was added specifically for ReadyBoost, + // which opens its cache file on a removable device as a paging file and + // relies on the file system to validate its mapping information after a + // power transition. + // + + if (!FlagOn( Fcb->FcbState, FCB_STATE_PAGING_FILE ) || + FlagOn(Fcb->Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA)) { + + if (!FatAcquireSharedFcb( IrpContext, Fcb )) { + + DebugTrace(0, Dbg, "Cannot acquire Fcb\n", 0); + + Status = FatFsdPostRequest( IrpContext, Irp ); + IrpContext = NULL; + Irp = NULL; + + try_return( Status ); + } + + FcbAcquired = TRUE; + } + + // + // Make sure the Fcb is in a usable condition. This + // will raise an error condition if the fcb is unusable + // + + FatVerifyFcb( 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: + + // + // For the all information class we'll typecast a local + // pointer to the output buffer and then call the + // individual routines to fill in the buffer. + // + + AllInfo = Buffer; + Length -= (sizeof(FILE_ACCESS_INFORMATION) + + sizeof(FILE_MODE_INFORMATION) + + sizeof(FILE_ALIGNMENT_INFORMATION)); + + FatQueryBasicInfo( IrpContext, Fcb, FileObject, &AllInfo->BasicInformation, &Length ); + FatQueryStandardInfo( IrpContext, Fcb, &AllInfo->StandardInformation, &Length ); + FatQueryInternalInfo( IrpContext, Fcb, &AllInfo->InternalInformation, &Length ); + FatQueryEaInfo( IrpContext, Fcb, &AllInfo->EaInformation, &Length ); + FatQueryPositionInfo( IrpContext, FileObject, &AllInfo->PositionInformation, &Length ); + FatQueryNameInfo( IrpContext, Fcb, Ccb, FALSE, &AllInfo->NameInformation, &Length ); + + break; + + case FileBasicInformation: + + FatQueryBasicInfo( IrpContext, Fcb, FileObject, Buffer, &Length ); + break; + + case FileStandardInformation: + + FatQueryStandardInfo( IrpContext, Fcb, Buffer, &Length ); + break; + + case FileInternalInformation: + + FatQueryInternalInfo( IrpContext, Fcb, Buffer, &Length ); + break; + + case FileEaInformation: + + FatQueryEaInfo( IrpContext, Fcb, Buffer, &Length ); + break; + + case FilePositionInformation: + + FatQueryPositionInfo( IrpContext, FileObject, Buffer, &Length ); + break; + + case FileNameInformation: + + FatQueryNameInfo( IrpContext, Fcb, Ccb, FALSE, Buffer, &Length ); + break; + + case FileNormalizedNameInformation: + + FatQueryNameInfo( IrpContext, Fcb, Ccb, TRUE, Buffer, &Length ); + break; + + case FileAlternateNameInformation: + + FatQueryShortNameInfo( IrpContext, Fcb, Buffer, &Length ); + break; + + case FileNetworkOpenInformation: + + FatQueryNetworkInfo( IrpContext, Fcb, FileObject, Buffer, &Length ); + break; + + default: + + Status = STATUS_INVALID_PARAMETER; + break; + } + + break; + + default: + + KdPrintEx((DPFLTR_FASTFAT_ID, + DPFLTR_INFO_LEVEL, + "FATQueryFile, Illegal TypeOfOpen = %08lx\n", + TypeOfOpen)); + + Status = STATUS_INVALID_PARAMETER; + break; + } + + // + // If we overflowed the buffer, set the length to 0 and change the + // status to STATUS_BUFFER_OVERFLOW. + // + + if ( Length < 0 ) { + + Status = STATUS_BUFFER_OVERFLOW; + + Length = 0; + } + + // + // 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; + + try_exit: NOTHING; + } finally { + + DebugUnwind( FatCommonQueryInformation ); + + if (FcbAcquired) { FatReleaseFcb( IrpContext, Fcb ); } + if (VcbAcquired) { FatReleaseVcb( IrpContext, Vcb ); } + + if (!AbnormalTermination()) { + + FatCompleteRequest( IrpContext, Irp, Status ); + } + + DebugTrace(-1, Dbg, "FatCommonQueryInformation -> %08lx\n", Status); + } + + return Status; +} + + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatCommonSetInformation ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the common routine for setting file 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_SUCCESS; + + PIO_STACK_LOCATION IrpSp; + + PFILE_OBJECT FileObject; + FILE_INFORMATION_CLASS FileInformationClass; + + TYPE_OF_OPEN TypeOfOpen; + PVCB Vcb; + PFCB Fcb; + PCCB Ccb; + + BOOLEAN VcbAcquired = FALSE; + BOOLEAN FcbAcquired = FALSE; + + PAGED_CODE(); + + // + // Get the current stack location + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "FatCommonSetInformation...\n", 0); + DebugTrace( 0, Dbg, "Irp = %p\n", Irp); + DebugTrace( 0, Dbg, "->Length = %08lx\n", IrpSp->Parameters.SetFile.Length); + DebugTrace( 0, Dbg, "->FileInformationClass = %08lx\n", IrpSp->Parameters.SetFile.FileInformationClass); + DebugTrace( 0, Dbg, "->FileObject = %p\n", IrpSp->Parameters.SetFile.FileObject); + DebugTrace( 0, Dbg, "->ReplaceIfExists = %08lx\n", IrpSp->Parameters.SetFile.ReplaceIfExists); + DebugTrace( 0, Dbg, "->Buffer = %p\n", Irp->AssociatedIrp.SystemBuffer); + + // + // Reference our input parameters to make things easier + // + + FileInformationClass = IrpSp->Parameters.SetFile.FileInformationClass; + FileObject = IrpSp->FileObject; + + // + // Decode the file object + // + + TypeOfOpen = FatDecodeFileObject( FileObject, &Vcb, &Fcb, &Ccb ); + + try { + + // + // Case on the type of open we're dealing with + // + + switch (TypeOfOpen) { + + case UserVolumeOpen: + + // + // We cannot query the user volume open. + // + + try_return( Status = STATUS_INVALID_PARAMETER ); + break; + + case UserFileOpen: + + if (!FlagOn( Fcb->FcbState, FCB_STATE_PAGING_FILE ) && + ((FileInformationClass == FileEndOfFileInformation) || + (FileInformationClass == FileAllocationInformation) || + (FileInformationClass == FileValidDataLengthInformation))) { + + // + // We check whether we can proceed + // based on the state of the file oplocks. + // + + Status = FsRtlCheckOplock( FatGetFcbOplock(Fcb), + Irp, + IrpContext, + NULL, + NULL ); + + if (Status != STATUS_SUCCESS) { + + try_return( Status ); + } + + // + // Set the flag indicating if Fast I/O is possible + // + + Fcb->Header.IsFastIoPossible = FatIsFastIoPossible( Fcb ); + } + break; + + case UserDirectoryOpen: + + break; + + default: + + try_return( Status = STATUS_INVALID_PARAMETER ); + } + + // + // We can only do a set on a nonroot dcb, so we do the test + // and then fall through to the user file open code. + // + + if (NodeType(Fcb) == FAT_NTC_ROOT_DCB) { + + if (FileInformationClass == FileDispositionInformation) { + + try_return( Status = STATUS_CANNOT_DELETE ); + } + + try_return( Status = STATUS_INVALID_PARAMETER ); + } + + // + // In the following two cases, we cannot have creates occuring + // while we are here, so acquire the volume exclusive. + // + + if ((FileInformationClass == FileDispositionInformation) || + (FileInformationClass == FileRenameInformation)) { + + if (!FatAcquireExclusiveVcb( IrpContext, Vcb )) { + + DebugTrace(0, Dbg, "Cannot acquire Vcb\n", 0); + + Status = FatFsdPostRequest( IrpContext, Irp ); + Irp = NULL; + IrpContext = NULL; + + try_return( Status ); + } + + VcbAcquired = TRUE; + + // + // Make sure we haven't been called recursively by a filter inside an existing + // create request. + // + + if (FlagOn( Vcb->VcbState, VCB_STATE_FLAG_CREATE_IN_PROGRESS)) { + +#pragma prefast( suppress:28159, "things are seriously wrong if we get here" ) + FatBugCheck( 0, 0, 0); + } + } + + // + // Acquire exclusive access to the Fcb, We use exclusive + // because it is probable that one of the subroutines + // that we call will need to monkey with file allocation, + // create/delete extra fcbs. So we're willing to pay the + // cost of exclusive Fcb access. + // + // Note that we do not acquire the resource for paging file + // operations in order to avoid deadlock with Mm. + // + // The "removable" check was added specifically for ReadyBoost, + // which opens its cache file on a removable device as a paging file and + // relies on the file system to validate its mapping information after a + // power transition. + // + + if (!FlagOn( Fcb->FcbState, FCB_STATE_PAGING_FILE ) || + FlagOn(Fcb->Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA)) { + + if (!FatAcquireExclusiveFcb( IrpContext, Fcb )) { + + DebugTrace(0, Dbg, "Cannot acquire Fcb\n", 0); + + Status = FatFsdPostRequest( IrpContext, Irp ); + Irp = NULL; + IrpContext = NULL; + + try_return( Status ); + } + + FcbAcquired = TRUE; + } + + Status = STATUS_SUCCESS; + + // + // Make sure the Fcb is in a usable condition. This + // will raise an error condition if the fcb is unusable + // + + FatVerifyFcb( IrpContext, Fcb ); + + // + // Now that we've acquired the file, do an oplock check if the operation + // so warrants. + // + + if (FatIsFileOplockable( Fcb )&& + ((FileInformationClass == FileRenameInformation) || + ((FileInformationClass == FileDispositionInformation) && + ((PFILE_DISPOSITION_INFORMATION) Irp->AssociatedIrp.SystemBuffer)->DeleteFile))) { + + Status = FsRtlCheckOplock( FatGetFcbOplock(Fcb), + Irp, + IrpContext, + FatOplockComplete, + NULL ); + + // + // Set the flag indicating if Fast I/O is possible + // + + Fcb->Header.IsFastIoPossible = FatIsFastIoPossible( Fcb ); + + // + // If STATUS_PENDING is returned it means the oplock + // package has the Irp. Don't complete the request here. + // + + if (Status == STATUS_PENDING) { + + Irp = NULL; + IrpContext = NULL; + } + + if (!NT_SUCCESS( Status ) || + (Status == STATUS_PENDING)) { + + try_return( Status ); + } + } + + // + // Based on the information class we'll do different + // actions. Each of the procedures that we're calling will either + // complete the request of send the request off to the fsp + // to do the work. + // + + switch (FileInformationClass) { + + case FileBasicInformation: + + Status = FatSetBasicInfo( IrpContext, Irp, Fcb, Ccb ); + break; + + case FileDispositionInformation: + + // + // If this is on deferred flush media, we have to be able to wait. + // + + if ( FlagOn(Vcb->VcbState, VCB_STATE_FLAG_DEFERRED_FLUSH) && + !FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) ) { + + Status = FatFsdPostRequest( IrpContext, Irp ); + Irp = NULL; + IrpContext = NULL; + + } else { + + Status = FatSetDispositionInfo( IrpContext, Irp, FileObject, Fcb ); + } + + break; + + case FileRenameInformation: + + // + // We proceed with this operation only if we can wait + // + + if (!FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT)) { + + Status = FatFsdPostRequest( IrpContext, Irp ); + Irp = NULL; + IrpContext = NULL; + + } else { + + Status = FatSetRenameInfo( IrpContext, Irp, Vcb, Fcb, Ccb ); + + // + // If STATUS_PENDING is returned it means the oplock + // package has the Irp. Don't complete the request here. + // + + if (Status == STATUS_PENDING) { + Irp = NULL; + IrpContext = NULL; + } + } + + break; + + case FilePositionInformation: + + Status = FatSetPositionInfo( IrpContext, Irp, FileObject ); + break; + + case FileLinkInformation: + + Status = STATUS_INVALID_DEVICE_REQUEST; + break; + + case FileAllocationInformation: + + Status = FatSetAllocationInfo( IrpContext, Irp, Fcb, FileObject ); + break; + + case FileEndOfFileInformation: + + Status = FatSetEndOfFileInfo( IrpContext, Irp, FileObject, Vcb, Fcb ); + break; + + case FileValidDataLengthInformation: + + Status = FatSetValidDataLengthInfo( IrpContext, Irp, FileObject, Fcb, Ccb ); + break; + + default: + + Status = STATUS_INVALID_PARAMETER; + break; + } + + if ( IrpContext != NULL ) { + + FatUnpinRepinnedBcbs( IrpContext ); + } + + try_exit: NOTHING; + } finally { + + DebugUnwind( FatCommonSetInformation ); + + if (FcbAcquired) { FatReleaseFcb( IrpContext, Fcb ); } + + if (VcbAcquired) { FatReleaseVcb( IrpContext, Vcb ); } + + if (!AbnormalTermination()) { + + FatCompleteRequest( IrpContext, Irp, Status ); + } + + DebugTrace(-1, Dbg, "FatCommonSetInformation -> %08lx\n", Status); + } + + return Status; +} + + +// +// Internal Support Routine +// + +VOID +FatQueryBasicInfo ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN PFILE_OBJECT FileObject, + IN OUT PFILE_BASIC_INFORMATION Buffer, + IN OUT PLONG Length + ) + +/*++ + Description: + + This routine performs the query basic information function for fat. + +Arguments: + + Fcb - Supplies the Fcb being queried, it has been verified + + FileObject - Supplies the flag bit that indicates the file was modified. + + 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( FileObject ); + UNREFERENCED_PARAMETER( IrpContext ); + + DebugTrace(+1, Dbg, "FatQueryBasicInfo...\n", 0); + + // + // Zero out the output buffer, and set it to indicate that + // the query is a normal file. Later we might overwrite the + // attribute. + // + + RtlZeroMemory( Buffer, sizeof(FILE_BASIC_INFORMATION) ); + + // + // Extract the data and fill in the non zero fields of the output + // buffer + // + + if (Fcb->Header.NodeTypeCode == FAT_NTC_ROOT_DCB) { + + // + // We have to munge a lie on the fly. Every time we have to + // use 1/1/80 we need to convert to GMT since the TZ may have + // changed on us. + // + + ExLocalTimeToSystemTime( &FatJanOne1980, + &Buffer->LastWriteTime ); + Buffer->CreationTime = Buffer->LastAccessTime = Buffer->LastWriteTime; + + } else { + + Buffer->LastWriteTime = Fcb->LastWriteTime; + Buffer->CreationTime = Fcb->CreationTime; + Buffer->LastAccessTime = Fcb->LastAccessTime; + } + + Buffer->FileAttributes = Fcb->DirentFatFlags; + + + // + // If the temporary flag is set, then set it in the buffer. + // + + if (FlagOn( Fcb->FcbState, FCB_STATE_TEMPORARY )) { + + SetFlag( Buffer->FileAttributes, FILE_ATTRIBUTE_TEMPORARY ); + } + + // + // If no attributes were set, set the normal bit. + // + + if (Buffer->FileAttributes == 0) { + + Buffer->FileAttributes = FILE_ATTRIBUTE_NORMAL; + } + + // + // Update the length and status output variables + // + + *Length -= sizeof( FILE_BASIC_INFORMATION ); + + DebugTrace( 0, Dbg, "*Length = %08lx\n", *Length); + + DebugTrace(-1, Dbg, "FatQueryBasicInfo -> VOID\n", 0); + + return; +} + + +// +// Internal Support Routine +// + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatQueryStandardInfo ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN OUT PFILE_STANDARD_INFORMATION Buffer, + IN OUT PLONG Length + ) + +/*++ + +Routine Description: + + This routine performs the query standard information function for fat. + +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(); + + DebugTrace(+1, Dbg, "FatQueryStandardInfo...\n", 0); + + // + // Zero out the output buffer, and fill in the number of links + // and the delete pending flag. + // + + RtlZeroMemory( Buffer, sizeof(FILE_STANDARD_INFORMATION) ); + + Buffer->NumberOfLinks = 1; + Buffer->DeletePending = BooleanFlagOn( Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE ); + + // + // Case on whether this is a file or a directory, and extract + // the information and fill in the fcb/dcb specific parts + // of the output buffer + // + + if (NodeType(Fcb) == FAT_NTC_FCB) { + + if (Fcb->Header.AllocationSize.QuadPart == FCB_LOOKUP_ALLOCATIONSIZE_HINT) { + + FatLookupFileAllocationSize( IrpContext, Fcb ); + } + + Buffer->AllocationSize = Fcb->Header.AllocationSize; + Buffer->EndOfFile = Fcb->Header.FileSize; + + Buffer->Directory = FALSE; + + } else { + + Buffer->Directory = TRUE; + } + + // + // Update the length and status output variables + // + + *Length -= sizeof( FILE_STANDARD_INFORMATION ); + + DebugTrace( 0, Dbg, "*Length = %08lx\n", *Length); + + DebugTrace(-1, Dbg, "FatQueryStandardInfo -> VOID\n", 0); + + return; +} + + +// +// Internal Support Routine +// + +VOID +FatQueryInternalInfo ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN OUT PFILE_INTERNAL_INFORMATION Buffer, + IN OUT PLONG Length + ) + +/*++ + +Routine Description: + + This routine performs the query internal information function for fat. + +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 ); + + DebugTrace(+1, Dbg, "FatQueryInternalInfo...\n", 0); + + try { + + Buffer->IndexNumber.QuadPart = FatGenerateFileIdFromFcb( Fcb ); + + // + // Update the length and status output variables + // + + *Length -= sizeof( FILE_INTERNAL_INFORMATION ); + + } finally { + + DebugUnwind( FatQueryInternalInfo ); + + DebugTrace( 0, Dbg, "*Length = %08lx\n", *Length); + + DebugTrace(-1, Dbg, "FatQueryInternalInfo -> VOID\n", 0); + } + + return; +} + + +// +// Internal Support Routine +// + +VOID +FatQueryEaInfo ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN OUT PFILE_EA_INFORMATION Buffer, + IN OUT PLONG Length + ) + +/*++ + +Routine Description: + + This routine performs the query Ea information function for fat. + +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 + +--*/ + +{ + PBCB Bcb; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( Fcb ); + UNREFERENCED_PARAMETER( IrpContext ); + + DebugTrace(+1, Dbg, "FatQueryEaInfo...\n", 0); + + Bcb = NULL; + + try { + + // + // Zero out the output buffer + // + + RtlZeroMemory( Buffer, sizeof(FILE_EA_INFORMATION) ); + +#if 0 + // + // The Root dcb does not have any EAs so don't look for any. Fat32 + // doesn't have any, either. + // + + if ( NodeType( Fcb ) != FAT_NTC_ROOT_DCB && + !FatIsFat32( Fcb->Vcb )) { + + PDIRENT Dirent; + + // + // Try to get the dirent for this file. + // + + FatGetDirentFromFcbOrDcb( IrpContext, + Fcb, + &Dirent, + &Bcb ); + + if (Dirent != NULL) { + + // + // Get a the size needed to store the full eas for the file. + // + + FatGetEaLength( IrpContext, + Fcb->Vcb, + Dirent, + &Buffer->EaSize ); + } + } +#endif + + // + // Update the length and status output variables + // + + *Length -= sizeof( FILE_EA_INFORMATION ); + + } finally { + + DebugUnwind( FatQueryEaInfo ); + + // + // Unpin the dirent if pinned. + // + + FatUnpinBcb( IrpContext, Bcb ); + + DebugTrace( 0, Dbg, "*Length = %08lx\n", *Length); + + DebugTrace(-1, Dbg, "FatQueryEaInfo -> VOID\n", 0); + } +} + + +// +// Internal Support Routine +// + +VOID +FatQueryPositionInfo ( + IN PIRP_CONTEXT IrpContext, + IN PFILE_OBJECT FileObject, + IN OUT PFILE_POSITION_INFORMATION Buffer, + IN OUT PLONG Length + ) + +/*++ + +Routine Description: + + This routine performs the query position information function for fat. + +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(); + + DebugTrace(+1, Dbg, "FatQueryPositionInfo...\n", 0); + + // + // 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 ); + + DebugTrace( 0, Dbg, "*Length = %08lx\n", *Length); + + DebugTrace(-1, Dbg, "FatQueryPositionInfo -> VOID\n", 0); + + UNREFERENCED_PARAMETER( IrpContext ); + + return; +} + + +// +// Internal Support Routine +// + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatQueryNameInfo ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN PCCB Ccb, + IN BOOLEAN Normalized, + IN OUT PFILE_NAME_INFORMATION Buffer, + IN OUT PLONG Length + ) + +/*++ + +Routine Description: + + This routine performs the query name information function for fat. + +Arguments: + + Fcb - Supplies the Fcb being queried, it has been verified + + Ccb - Supplies the Ccb for the context of the user open + + Normalized - if true the caller wants a normalized name (w/out short names). + This means we're servicing a FileNormalizedNameInformation query. + + 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 + +--*/ + +{ + ULONG BytesToCopy; + LONG TrimLength; + BOOLEAN Overflow = FALSE; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatQueryNameInfo...\n", 0); + + // + // Convert the name to UNICODE + // + + *Length -= FIELD_OFFSET(FILE_NAME_INFORMATION, FileName[0]); + + // + // Use the full filename to build the path up. If we wanted to be + // slick in the future, we'd just build the path directly into the + // return buffer and avoid constructing the full filename, but since + // the full filename winds up being required so often lets not + // over optimize this case yet. + // + + if (Fcb->FullFileName.Buffer == NULL) { + + FatSetFullFileNameInFcb( IrpContext, Fcb ); + } + + // + // Here is where it gets a smidge tricky. FinalNameLength is the length + // of the LFN element if it exists, and since the long name is always used + // to build FullFileName, we have two cases: + // + // 1) short name: use FinalNameLength to tear off the path from FullFileName + // and append the UNICODE converted short name. + // 2) long name: just use FullFileName + // + // We bias to the name the user thinks they opened by. This winds + // up fixing some oddball tunneling cases where intermediate filters + // translate operations like delete into renames - this lets them + // do the operation in the context of the name the user was using. + // + // It also matches what NTFS does, and so we have the definition of + // correct behavior. + // + + // + // + // Assume there is no long name and we are just going to use + // FullFileName. + // + + TrimLength = 0; + + // + // If a LongName exists, the caller isn't asking for the normalized name, + // and the original open was by the short name then set TrimLength to point + // to the place where the short name goes. + // + // Note: The Ccb can be NULL. The lazy writer calls to get the name of + // a DirectoryOpen FILE_OBJECT that it wants to display in the lost + // delayed write popup. Handle this case by just using the FileFullName. + // + + if (!Normalized && + (Fcb->LongName.Unicode.Name.Unicode.Buffer != NULL)) { + + if ((Ccb != NULL) && + FlagOn(Ccb->Flags, CCB_FLAG_OPENED_BY_SHORTNAME)) { + + TrimLength = Fcb->FinalNameLength; + } + } + + if (*Length < Fcb->FullFileName.Length - TrimLength) { + + BytesToCopy = *Length; + Overflow = TRUE; + + } else { + + BytesToCopy = Fcb->FullFileName.Length - TrimLength; + *Length -= BytesToCopy; + } + + RtlCopyMemory( &Buffer->FileName[0], + Fcb->FullFileName.Buffer, + BytesToCopy ); + + // + // Note that this is just the amount of name we've copied so far. It'll + // either be all of it (long) or the path element including the \ (short). + // + + Buffer->FileNameLength = Fcb->FullFileName.Length - TrimLength; + + // + // If we trimmed off the name element, this is the short name case. Pick + // up the UNICODE conversion and append it. + // + + if (TrimLength != 0) { + + UNICODE_STRING ShortName; + WCHAR ShortNameBuffer[12]; + NTSTATUS Status; + + // + // Convert the short name to UNICODE and figure out how much + // of it can fit. Again, we always bump the returned length + // to indicate how much is available even if we can't return it. + // + + ShortName.Length = 0; + ShortName.MaximumLength = sizeof(ShortNameBuffer); + ShortName.Buffer = ShortNameBuffer; + +#pragma prefast( suppress:28931, "needed for debug build" ) + Status = RtlOemStringToCountedUnicodeString( &ShortName, + &Fcb->ShortName.Name.Oem, + FALSE ); + + NT_ASSERT( Status == STATUS_SUCCESS ); + + if (!Overflow) { + + if (*Length < ShortName.Length) { + + BytesToCopy = *Length; + Overflow = TRUE; + + } else { + + BytesToCopy = ShortName.Length; + *Length -= BytesToCopy; + } + + RtlCopyMemory( (PUCHAR)&Buffer->FileName[0] + Buffer->FileNameLength, + ShortName.Buffer, + BytesToCopy ); + } + + Buffer->FileNameLength += ShortName.Length; + } + + if (Overflow) { + + *Length = -1; + } + + // + // Return to caller + // + + DebugTrace( 0, Dbg, "*Length = %08lx\n", *Length); + + DebugTrace(-1, Dbg, "FatQueryNameInfo -> VOID\n", 0); + + UNREFERENCED_PARAMETER( IrpContext ); + + return; +} + + +// +// Internal Support Routine +// + +VOID +FatQueryShortNameInfo ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN OUT PFILE_NAME_INFORMATION Buffer, + IN OUT PLONG Length + ) + +/*++ + +Routine Description: + + This routine queries the short name of the file. + +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 + +--*/ + +{ + NTSTATUS Status; + + ULONG BytesToCopy; + WCHAR ShortNameBuffer[12]; + UNICODE_STRING ShortName; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatQueryNameInfo...\n", 0); + + // + // Convert the name to UNICODE + // + + ShortName.Length = 0; + ShortName.MaximumLength = sizeof(ShortNameBuffer); + ShortName.Buffer = ShortNameBuffer; + + *Length -= FIELD_OFFSET(FILE_NAME_INFORMATION, FileName[0]); + +#pragma prefast( suppress:28931, "needed for debug build" ) + Status = RtlOemStringToCountedUnicodeString( &ShortName, + &Fcb->ShortName.Name.Oem, + FALSE ); + + NT_ASSERT( Status == STATUS_SUCCESS ); + + // + // If we overflow, set *Length to -1 as a flag. + // + + if (*Length < ShortName.Length) { + + BytesToCopy = *Length; + *Length = -1; + + } else { + + BytesToCopy = ShortName.Length; + *Length -= ShortName.Length; + } + + RtlCopyMemory( &Buffer->FileName[0], + &ShortName.Buffer[0], + BytesToCopy ); + + Buffer->FileNameLength = ShortName.Length; + + // + // Return to caller + // + + DebugTrace( 0, Dbg, "*Length = %08lx\n", *Length); + + DebugTrace(-1, Dbg, "FatQueryNameInfo -> VOID\n", 0); + + UNREFERENCED_PARAMETER( IrpContext ); + + return; +} + + +// +// Internal Support Routine +// + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatQueryNetworkInfo ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN PFILE_OBJECT FileObject, + IN OUT PFILE_NETWORK_OPEN_INFORMATION Buffer, + IN OUT PLONG Length + ) + +/*++ + Description: + + This routine performs the query network open information function for fat. + +Arguments: + + Fcb - Supplies the Fcb being queried, it has been verified + + FileObject - Supplies the flag bit that indicates the file was modified. + + 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( FileObject ); + + DebugTrace(+1, Dbg, "FatQueryNetworkInfo...\n", 0); + + // + // Zero out the output buffer, and set it to indicate that + // the query is a normal file. Later we might overwrite the + // attribute. + // + + RtlZeroMemory( Buffer, sizeof(FILE_NETWORK_OPEN_INFORMATION) ); + + // + // Extract the data and fill in the non zero fields of the output + // buffer + // + + if (Fcb->Header.NodeTypeCode == FAT_NTC_ROOT_DCB) { + + // + // We have to munge a lie on the fly. Every time we have to + // use 1/1/80 we need to convert to GMT since the TZ may have + // changed on us. + // + + ExLocalTimeToSystemTime( &FatJanOne1980, + &Buffer->LastWriteTime ); + Buffer->CreationTime = Buffer->LastAccessTime = Buffer->LastWriteTime; + + } else { + + Buffer->LastWriteTime.QuadPart = Fcb->LastWriteTime.QuadPart; + Buffer->CreationTime.QuadPart = Fcb->CreationTime.QuadPart; + Buffer->LastAccessTime.QuadPart = Fcb->LastAccessTime.QuadPart; + } + + Buffer->FileAttributes = Fcb->DirentFatFlags; + + + // + // If the temporary flag is set, then set it in the buffer. + // + + if (FlagOn( Fcb->FcbState, FCB_STATE_TEMPORARY )) { + + SetFlag( Buffer->FileAttributes, FILE_ATTRIBUTE_TEMPORARY ); + } + + // + // If no attributes were set, set the normal bit. + // + + if (Buffer->FileAttributes == 0) { + + Buffer->FileAttributes = FILE_ATTRIBUTE_NORMAL; + } + // + // Case on whether this is a file or a directory, and extract + // the information and fill in the fcb/dcb specific parts + // of the output buffer + // + + if (NodeType(Fcb) == FAT_NTC_FCB) { + + if (Fcb->Header.AllocationSize.QuadPart == FCB_LOOKUP_ALLOCATIONSIZE_HINT) { + + FatLookupFileAllocationSize( IrpContext, Fcb ); + } + + Buffer->AllocationSize.QuadPart = Fcb->Header.AllocationSize.QuadPart; + Buffer->EndOfFile.QuadPart = Fcb->Header.FileSize.QuadPart; + } + + // + // Update the length and status output variables + // + + *Length -= sizeof( FILE_NETWORK_OPEN_INFORMATION ); + + DebugTrace( 0, Dbg, "*Length = %08lx\n", *Length); + + DebugTrace(-1, Dbg, "FatQueryNetworkInfo -> VOID\n", 0); + + return; +} + + +// +// Internal Support routine +// + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatSetBasicInfo ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp, + IN PFCB Fcb, + IN PCCB Ccb + ) + +/*++ + +Routine Description: + + This routine performs the set basic information for fat. It either + completes the request or enqueues it off to the fsp. + +Arguments: + + Irp - Supplies the irp being processed + + Fcb - Supplies the Fcb or Dcb being processed, already known not to + be the root dcb + + Ccb - Supplies the flag bit that control updating the last modify + time on cleanup. + +Return Value: + + NTSTATUS - The result of this operation if it completes without + an exception. + +--*/ + +{ + NTSTATUS Status; + + PFILE_BASIC_INFORMATION Buffer; + + PDIRENT Dirent; + PBCB DirentBcb; + + FAT_TIME_STAMP CreationTime = {0}; + UCHAR CreationMSec = 0; + FAT_TIME_STAMP LastWriteTime = {0}; + FAT_TIME_STAMP LastAccessTime = {0}; + FAT_DATE LastAccessDate = {0}; + UCHAR Attributes; + + BOOLEAN ModifyCreation = FALSE; + BOOLEAN ModifyLastWrite = FALSE; + BOOLEAN ModifyLastAccess = FALSE; + + BOOLEAN ModifiedAttributes = FALSE; + + LARGE_INTEGER LargeCreationTime = {0}; + LARGE_INTEGER LargeLastWriteTime = {0}; + LARGE_INTEGER LargeLastAccessTime = {0}; + + ULONG NotifyFilter = 0; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatSetBasicInfo...\n", 0); + + Buffer = Irp->AssociatedIrp.SystemBuffer; + + // + // If the user is specifying -1 for a field, that means + // we should leave that field unchanged, even if we might + // have otherwise set it ourselves. We'll set the Ccb flag + // saying that the user set the field so that we + // don't do our default updating. + // + // We set the field to 0 then so we know not to actually + // set the field to the user-specified (and in this case, + // illegal) value. + // + + if (Buffer->LastWriteTime.QuadPart == -1) { + + SetFlag( Ccb->Flags, CCB_FLAG_USER_SET_LAST_WRITE ); + Buffer->LastWriteTime.QuadPart = 0; + } + + if (Buffer->LastAccessTime.QuadPart == -1) { + + SetFlag( Ccb->Flags, CCB_FLAG_USER_SET_LAST_ACCESS ); + Buffer->LastAccessTime.QuadPart = 0; + } + + if (Buffer->CreationTime.QuadPart == -1) { + + SetFlag( Ccb->Flags, CCB_FLAG_USER_SET_CREATION ); + Buffer->CreationTime.QuadPart = 0; + } + + DirentBcb = NULL; + + Status = STATUS_SUCCESS; + + try { + + LARGE_INTEGER FatLocalDecThirtyOne1979; + LARGE_INTEGER FatLocalJanOne1980; + + ExLocalTimeToSystemTime( &FatDecThirtyOne1979, + &FatLocalDecThirtyOne1979 ); + + ExLocalTimeToSystemTime( &FatJanOne1980, + &FatLocalJanOne1980 ); + + // + // Get a pointer to the dirent + // + + NT_ASSERT( Fcb->FcbCondition == FcbGood ); + + FatGetDirentFromFcbOrDcb( IrpContext, + Fcb, + FALSE, + &Dirent, + &DirentBcb ); + // + // Check if the user specified a non-zero creation time + // + + if (FatData.ChicagoMode && (Buffer->CreationTime.QuadPart != 0)) { + + LargeCreationTime = Buffer->CreationTime; + + // + // Convert the Nt time to a Fat time + // + + if ( !FatNtTimeToFatTime( IrpContext, + &LargeCreationTime, + FALSE, + &CreationTime, + &CreationMSec )) { + + // + // Special case the value 12/31/79 and treat this as 1/1/80. + // This '79 value can happen because of time zone issues. + // + + if ((LargeCreationTime.QuadPart >= FatLocalDecThirtyOne1979.QuadPart) && + (LargeCreationTime.QuadPart < FatLocalJanOne1980.QuadPart)) { + + CreationTime = FatTimeJanOne1980; + LargeCreationTime = FatLocalJanOne1980; + + } else { + + DebugTrace(0, Dbg, "Invalid CreationTime\n", 0); + try_return( Status = STATUS_INVALID_PARAMETER ); + } + + // + // Don't worry about CreationMSec + // + + CreationMSec = 0; + } + + ModifyCreation = TRUE; + } + + // + // Check if the user specified a non-zero last access time + // + + if (FatData.ChicagoMode && (Buffer->LastAccessTime.QuadPart != 0)) { + + LargeLastAccessTime = Buffer->LastAccessTime; + + // + // Convert the Nt time to a Fat time + // + + if ( !FatNtTimeToFatTime( IrpContext, + &LargeLastAccessTime, + TRUE, + &LastAccessTime, + NULL )) { + + // + // Special case the value 12/31/79 and treat this as 1/1/80. + // This '79 value can happen because of time zone issues. + // + + if ((LargeLastAccessTime.QuadPart >= FatLocalDecThirtyOne1979.QuadPart) && + (LargeLastAccessTime.QuadPart < FatLocalJanOne1980.QuadPart)) { + + LastAccessTime = FatTimeJanOne1980; + LargeLastAccessTime = FatLocalJanOne1980; + + } else { + + DebugTrace(0, Dbg, "Invalid LastAccessTime\n", 0); + try_return( Status = STATUS_INVALID_PARAMETER ); + } + } + + LastAccessDate = LastAccessTime.Date; + ModifyLastAccess = TRUE; + } + + // + // Check if the user specified a non-zero last write time + // + + if (Buffer->LastWriteTime.QuadPart != 0) { + + // + // First do a quick check here if the this time is the same + // time as LastAccessTime. + // + + if (ModifyLastAccess && + (Buffer->LastWriteTime.QuadPart == Buffer->LastAccessTime.QuadPart)) { + + ModifyLastWrite = TRUE; + LastWriteTime = LastAccessTime; + LargeLastWriteTime = LargeLastAccessTime; + + } else { + + LargeLastWriteTime = Buffer->LastWriteTime; + + // + // Convert the Nt time to a Fat time + // + + if ( !FatNtTimeToFatTime( IrpContext, + &LargeLastWriteTime, + TRUE, + &LastWriteTime, + NULL )) { + + + // + // Special case the value 12/31/79 and treat this as 1/1/80. + // This '79 value can happen because of time zone issues. + // + + if ((LargeLastWriteTime.QuadPart >= FatLocalDecThirtyOne1979.QuadPart) && + (LargeLastWriteTime.QuadPart < FatLocalJanOne1980.QuadPart)) { + + LastWriteTime = FatTimeJanOne1980; + LargeLastWriteTime = FatLocalJanOne1980; + + } else { + + DebugTrace(0, Dbg, "Invalid LastWriteTime\n", 0); + try_return( Status = STATUS_INVALID_PARAMETER ); + } + } + + ModifyLastWrite = TRUE; + } + } + + + // + // Check if the user specified a non zero file attributes byte + // + + if (Buffer->FileAttributes != 0) { + + // + // Only permit the attributes that FAT understands. The rest are silently + // dropped on the floor. + // + + Attributes = (UCHAR)(Buffer->FileAttributes & (FILE_ATTRIBUTE_READONLY | + FILE_ATTRIBUTE_HIDDEN | + FILE_ATTRIBUTE_SYSTEM | + FILE_ATTRIBUTE_DIRECTORY | + FILE_ATTRIBUTE_ARCHIVE)); + + // + // Make sure that for a file the directory bit is not set + // and that for a directory the bit is set. + // + + if (NodeType(Fcb) == FAT_NTC_FCB) { + + if (FlagOn(Buffer->FileAttributes, FILE_ATTRIBUTE_DIRECTORY)) { + + DebugTrace(0, Dbg, "Attempt to set dir attribute on file\n", 0); + try_return( Status = STATUS_INVALID_PARAMETER ); + } + + } else { + + Attributes |= FAT_DIRENT_ATTR_DIRECTORY; + } + + // + // Mark the FcbState temporary flag correctly. + // + + if (FlagOn(Buffer->FileAttributes, FILE_ATTRIBUTE_TEMPORARY)) { + + // + // Don't allow the temporary bit to be set on directories. + // + + if (NodeType(Fcb) == FAT_NTC_DCB) { + + DebugTrace(0, Dbg, "No temporary directories\n", 0); + try_return( Status = STATUS_INVALID_PARAMETER ); + } + + SetFlag( Fcb->FcbState, FCB_STATE_TEMPORARY ); + + SetFlag( IoGetCurrentIrpStackLocation(Irp)->FileObject->Flags, + FO_TEMPORARY_FILE ); + + } else { + + ClearFlag( Fcb->FcbState, FCB_STATE_TEMPORARY ); + + ClearFlag( IoGetCurrentIrpStackLocation(Irp)->FileObject->Flags, + FO_TEMPORARY_FILE ); + } + + // + // Now, only proceed if the requested file attributes are different + // than the existing attributes. + // + + if (Dirent->Attributes != Attributes) { + + // + // Set the new attributes byte, and mark the bcb dirty + // + + Fcb->DirentFatFlags = Attributes; + + Dirent->Attributes = Attributes; + + NotifyFilter |= FILE_NOTIFY_CHANGE_ATTRIBUTES; + + ModifiedAttributes = TRUE; + } + } + + if ( ModifyCreation ) { + + // + // Set the new last write time in the dirent, and mark + // the bcb dirty + // + + Fcb->CreationTime = LargeCreationTime; + Dirent->CreationTime = CreationTime; + Dirent->CreationMSec = CreationMSec; + + + NotifyFilter |= FILE_NOTIFY_CHANGE_CREATION; + // + // Now we have to round the time in the Fcb up to the + // nearest tem msec. + // + + Fcb->CreationTime.QuadPart = + + ((Fcb->CreationTime.QuadPart + AlmostTenMSec) / + TenMSec) * TenMSec; + + // + // Now because the user just set the creation time we + // better not set the creation time on close + // + + SetFlag( Ccb->Flags, CCB_FLAG_USER_SET_CREATION ); + } + + if ( ModifyLastAccess ) { + + // + // Set the new last write time in the dirent, and mark + // the bcb dirty + // + + Fcb->LastAccessTime = LargeLastAccessTime; + Dirent->LastAccessDate = LastAccessDate; + + NotifyFilter |= FILE_NOTIFY_CHANGE_LAST_ACCESS; + + // + // Now we have to truncate the time in the Fcb down to the + // current day. This has to be in LocalTime though, so first + // convert to local, trunacate, then set back to GMT. + // + + ExSystemTimeToLocalTime( &Fcb->LastAccessTime, + &Fcb->LastAccessTime ); + + Fcb->LastAccessTime.QuadPart = + + (Fcb->LastAccessTime.QuadPart / + FatOneDay.QuadPart) * FatOneDay.QuadPart; + + ExLocalTimeToSystemTime( &Fcb->LastAccessTime, + &Fcb->LastAccessTime ); + + // + // Now because the user just set the last access time we + // better not set the last access time on close + // + + SetFlag( Ccb->Flags, CCB_FLAG_USER_SET_LAST_ACCESS ); + } + + if ( ModifyLastWrite ) { + + // + // Set the new last write time in the dirent, and mark + // the bcb dirty + // + + Fcb->LastWriteTime = LargeLastWriteTime; + Dirent->LastWriteTime = LastWriteTime; + + NotifyFilter |= FILE_NOTIFY_CHANGE_LAST_WRITE; + + // + // Now we have to round the time in the Fcb up to the + // nearest two seconds. + // + + Fcb->LastWriteTime.QuadPart = + + ((Fcb->LastWriteTime.QuadPart + AlmostTwoSeconds) / + TwoSeconds) * TwoSeconds; + + // + // Now because the user just set the last write time we + // better not set the last write time on close + // + + SetFlag( Ccb->Flags, CCB_FLAG_USER_SET_LAST_WRITE ); + } + + // + // If we modified any of the values, we report this to the notify + // package. + // + // We also take this opportunity to set the current file size and + // first cluster in the Dirent in order to support a server hack. + // + + if (NotifyFilter != 0) { + + if (NodeType(Fcb) == FAT_NTC_FCB) { + + Dirent->FileSize = Fcb->Header.FileSize.LowPart; + + + Dirent->FirstClusterOfFile = (USHORT)Fcb->FirstClusterOfFile; + + if (FatIsFat32(Fcb->Vcb)) { + + Dirent->FirstClusterOfFileHi = + (USHORT)(Fcb->FirstClusterOfFile >> 16); + } + } + + FatNotifyReportChange( IrpContext, + Fcb->Vcb, + Fcb, + NotifyFilter, + FILE_ACTION_MODIFIED ); + + FatSetDirtyBcb( IrpContext, DirentBcb, Fcb->Vcb, TRUE ); + } + +#if (NTDDI_VERSION >= NTDDI_WIN8) + // + // If last-access, last-write, or any attribute bits changed, break + // parent directory oplock. + // + + if ((Fcb->ParentDcb != NULL) && + (ModifyLastAccess || + ModifyLastWrite || + ModifiedAttributes)) { + + FsRtlCheckOplockEx( FatGetFcbOplock(Fcb->ParentDcb), + IrpContext->OriginatingIrp, + OPLOCK_FLAG_PARENT_OBJECT, + NULL, + NULL, + NULL ); + } +#endif + + try_exit: NOTHING; + } finally { + + DebugUnwind( FatSetBasicInfo ); + + FatUnpinBcb( IrpContext, DirentBcb ); + + DebugTrace(-1, Dbg, "FatSetBasicInfo -> %08lx\n", Status); + } + + return Status; +} + +// +// Internal Support Routine +// + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatSetDispositionInfo ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp, + IN PFILE_OBJECT FileObject, + IN PFCB Fcb + ) + +/*++ + +Routine Description: + + This routine performs the set disposition information for fat. It either + completes the request or enqueues it off to the fsp. + +Arguments: + + Irp - Supplies the irp being processed + + FileObject - Supplies the file object being processed + + Fcb - Supplies the Fcb or Dcb being processed, already known not to + be the root dcb + +Return Value: + + NTSTATUS - The result of this operation if it completes without + an exception. + +--*/ + +{ + PFILE_DISPOSITION_INFORMATION Buffer; + PBCB Bcb; + PDIRENT Dirent; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatSetDispositionInfo...\n", 0); + + Buffer = Irp->AssociatedIrp.SystemBuffer; + + // + // Check if the user wants to delete the file or not delete + // the file + // + + if (Buffer->DeleteFile) { + + // + // Check if the file is marked read only + // + + if (FlagOn(Fcb->DirentFatFlags, FAT_DIRENT_ATTR_READ_ONLY)) { + + DebugTrace(-1, Dbg, "Cannot delete readonly file\n", 0); + + return STATUS_CANNOT_DELETE; + } + + // + // Make sure there is no process mapping this file as an image. + // + + if (!MmFlushImageSection( &Fcb->NonPaged->SectionObjectPointers, + MmFlushForDelete )) { + + DebugTrace(-1, Dbg, "Cannot delete user mapped image\n", 0); + + return STATUS_CANNOT_DELETE; + } + + // + // Check if this is a dcb and if so then only allow + // the request if the directory is empty. + // + + if (NodeType(Fcb) == FAT_NTC_ROOT_DCB) { + + DebugTrace(-1, Dbg, "Cannot delete root Directory\n", 0); + + return STATUS_CANNOT_DELETE; + } + + if (NodeType(Fcb) == FAT_NTC_DCB) { + + DebugTrace(-1, Dbg, "User wants to delete a directory\n", 0); + + // + // Check if the directory is empty + // + + if ( !FatIsDirectoryEmpty(IrpContext, Fcb) ) { + + DebugTrace(-1, Dbg, "Directory is not empty\n", 0); + + return STATUS_DIRECTORY_NOT_EMPTY; + } + } + + // + // If this is a floppy, touch the volume so to verify that it + // is not write protected. + // + + if ( FlagOn(Fcb->Vcb->Vpb->RealDevice->Characteristics, FILE_FLOPPY_DISKETTE)) { + + PVCB Vcb; + PBCB LocalBcb = NULL; + UCHAR *LocalBuffer; + UCHAR TmpChar; + ULONG BytesToMap; + + IO_STATUS_BLOCK Iosb; + + Vcb = Fcb->Vcb; + + BytesToMap = Vcb->AllocationSupport.FatIndexBitSize == 12 ? + FatReservedBytes(&Vcb->Bpb) + + FatBytesPerFat(&Vcb->Bpb):PAGE_SIZE; + + FatReadVolumeFile( IrpContext, + Vcb, + 0, + BytesToMap, + &LocalBcb, + (PVOID *)&LocalBuffer ); + + try { + + if (!CcPinMappedData( Vcb->VirtualVolumeFile, + &FatLargeZero, + BytesToMap, + BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT), + &LocalBcb )) { + + // + // Could not pin the data without waiting (cache miss). + // + + FatRaiseStatus( IrpContext, STATUS_CANT_WAIT ); + } + + // + // Make Mm, myself, and Cc think the byte is dirty, and then + // force a writethrough. + // + + LocalBuffer += FatReservedBytes(&Vcb->Bpb); + + TmpChar = LocalBuffer[0]; + LocalBuffer[0] = TmpChar; + + FatAddMcbEntry( Vcb, &Vcb->DirtyFatMcb, + FatReservedBytes( &Vcb->Bpb ), + FatReservedBytes( &Vcb->Bpb ), + Vcb->Bpb.BytesPerSector ); + + } finally { + + if (AbnormalTermination() && (LocalBcb != NULL)) { + + FatUnpinBcb( IrpContext, LocalBcb ); + } + } + + CcRepinBcb( LocalBcb ); + CcSetDirtyPinnedData( LocalBcb, NULL ); + CcUnpinData( LocalBcb ); + DbgDoit( NT_ASSERT( IrpContext->PinCount )); + DbgDoit( IrpContext->PinCount -= 1 ); + CcUnpinRepinnedBcb( LocalBcb, TRUE, &Iosb ); + + // + // If this was not successful, raise the status. + // + + if ( !NT_SUCCESS(Iosb.Status) ) { + + FatNormalizeAndRaiseStatus( IrpContext, Iosb.Status ); + } + + } else { + + // + // Just set a Bcb dirty here. The above code was only there to + // detect a write protected floppy, while the below code works + // for any write protected media and only takes a hit when the + // volume in clean. + // + + FatGetDirentFromFcbOrDcb( IrpContext, + Fcb, + FALSE, + &Dirent, + &Bcb ); + + // + // This has to work for the usual reasons (we verified the Fcb within + // volume synch). + // + + try { + + FatSetDirtyBcb( IrpContext, Bcb, Fcb->Vcb, TRUE ); + + } finally { + + FatUnpinBcb( IrpContext, Bcb ); + } + } + + // + // At this point either we have a file or an empty directory + // so we know the delete can proceed. + // + + SetFlag( Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE ); + FileObject->DeletePending = TRUE; + + // + // If this is a directory then report this delete pending to + // the dir notify package. + // + + if (NodeType(Fcb) == FAT_NTC_DCB) { + + FsRtlNotifyFullChangeDirectory( Fcb->Vcb->NotifySync, + &Fcb->Vcb->DirNotifyList, + FileObject->FsContext, + NULL, + FALSE, + FALSE, + 0, + NULL, + NULL, + NULL ); + } + } else { + + // + // The user doesn't want to delete the file so clear + // the delete on close bit + // + + DebugTrace(0, Dbg, "User want to not delete file\n", 0); + + ClearFlag( Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE ); + FileObject->DeletePending = FALSE; + } + + DebugTrace(-1, Dbg, "FatSetDispositionInfo -> STATUS_SUCCESS\n", 0); + + return STATUS_SUCCESS; +} + + +// +// Internal Support Routine +// + +NTSTATUS +FatSetRenameInfo ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp, + IN PVCB Vcb, + IN PFCB Fcb, + IN PCCB Ccb + ) + +/*++ + +Routine Description: + + This routine performs the set name information for fat. It either + completes the request or enqueues it off to the fsp. + +Arguments: + + Irp - Supplies the irp being processed + + Vcb - Supplies the Vcb being processed + + Fcb - Supplies the Fcb or Dcb being processed, already known not to + be the root dcb + + Ccb - Supplies the Ccb corresponding to the handle opening the source + file + +Return Value: + + NTSTATUS - The result of this operation if it completes without + an exception. + +--*/ + +{ + BOOLEAN AllLowerComponent; + BOOLEAN AllLowerExtension; + BOOLEAN CaseOnlyRename; + BOOLEAN ContinueWithRename; + BOOLEAN CreateLfn = FALSE; + BOOLEAN DeleteSourceDirent; + BOOLEAN DeleteTarget; + BOOLEAN NewDirentFromPool; + BOOLEAN RenamedAcrossDirectories; + BOOLEAN ReplaceIfExists; + + CCB LocalCcb; + PCCB SourceCcb; + + DIRENT Dirent; + + NTSTATUS Status = STATUS_SUCCESS; + + OEM_STRING OldOemName; + OEM_STRING NewOemName; + UCHAR OemNameBuffer[24*2]; + + PBCB DotDotBcb; + PBCB NewDirentBcb; + PBCB OldDirentBcb; + PBCB SecondPageBcb; + PBCB TargetDirentBcb; + + PDCB TargetDcb = NULL; + PDCB OldParentDcb; + + PDIRENT DotDotDirent = NULL; + PDIRENT FirstPageDirent = NULL; + PDIRENT NewDirent = NULL; + PDIRENT OldDirent = NULL; + PDIRENT SecondPageDirent = NULL; + PDIRENT ShortDirent = NULL; + PDIRENT TargetDirent = NULL; + + PFCB TempFcb; + + PFILE_OBJECT TargetFileObject; + PFILE_OBJECT FileObject; + + PIO_STACK_LOCATION IrpSp; + + PLIST_ENTRY Links; + + ULONG BytesInFirstPage = 0; + ULONG DirentsInFirstPage = 0; + ULONG DirentsRequired = 0; + ULONG NewOffset = 0; + ULONG NotifyAction = 0; + ULONG SecondPageOffset = 0; + ULONG ShortDirentOffset = 0; + ULONG TargetDirentOffset = 0; + ULONG TargetLfnOffset = 0; + + UNICODE_STRING NewName; + UNICODE_STRING NewUpcasedName; + UNICODE_STRING OldName; + UNICODE_STRING OldUpcasedName; + UNICODE_STRING TargetLfn; + UNICODE_STRING TargetOrigLfn = {0}; + + PWCHAR UnicodeBuffer; + + UNICODE_STRING UniTunneledShortName; + WCHAR UniTunneledShortNameBuffer[12]; + UNICODE_STRING UniTunneledLongName; + WCHAR UniTunneledLongNameBuffer[26]; + LARGE_INTEGER TunneledCreationTime; + ULONG TunneledDataSize; + BOOLEAN HaveTunneledInformation = FALSE; + BOOLEAN UsingTunneledLfn = FALSE; + + BOOLEAN InvalidateFcbOnRaise = FALSE; + + PFILE_OBJECT DirectoryFileObject = NULL; + ULONG Flags = 0; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatSetRenameInfo...\n", 0); + + // + // P H A S E 0: Initialize some variables. + // + + CaseOnlyRename = FALSE; + ContinueWithRename = FALSE; + DeleteSourceDirent = FALSE; + DeleteTarget = FALSE; + NewDirentFromPool = FALSE; + RenamedAcrossDirectories = FALSE; + + DotDotBcb = NULL; + NewDirentBcb = NULL; + OldDirentBcb = NULL; + SecondPageBcb = NULL; + TargetDirentBcb = NULL; + + NewOemName.Length = 0; + NewOemName.MaximumLength = 24; + NewOemName.Buffer = (PCHAR)&OemNameBuffer[0]; + + OldOemName.Length = 0; + OldOemName.MaximumLength = 24; + OldOemName.Buffer = (PCHAR)&OemNameBuffer[24]; + + UnicodeBuffer = FsRtlAllocatePoolWithTag( PagedPool, + 4 * MAX_LFN_CHARACTERS * sizeof(WCHAR), + TAG_FILENAME_BUFFER ); + + NewUpcasedName.Length = 0; + NewUpcasedName.MaximumLength = MAX_LFN_CHARACTERS * sizeof(WCHAR); + NewUpcasedName.Buffer = &UnicodeBuffer[0]; + + OldName.Length = 0; + OldName.MaximumLength = MAX_LFN_CHARACTERS * sizeof(WCHAR); + OldName.Buffer = &UnicodeBuffer[MAX_LFN_CHARACTERS]; + + OldUpcasedName.Length = 0; + OldUpcasedName.MaximumLength = MAX_LFN_CHARACTERS * sizeof(WCHAR); + OldUpcasedName.Buffer = &UnicodeBuffer[MAX_LFN_CHARACTERS * 2]; + + TargetLfn.Length = 0; + TargetLfn.MaximumLength = MAX_LFN_CHARACTERS * sizeof(WCHAR); + TargetLfn.Buffer = &UnicodeBuffer[MAX_LFN_CHARACTERS * 3]; + + UniTunneledShortName.Length = 0; + UniTunneledShortName.MaximumLength = sizeof(UniTunneledShortNameBuffer); + UniTunneledShortName.Buffer = &UniTunneledShortNameBuffer[0]; + + UniTunneledLongName.Length = 0; + UniTunneledLongName.MaximumLength = sizeof(UniTunneledLongNameBuffer); + UniTunneledLongName.Buffer = &UniTunneledLongNameBuffer[0]; + + // + // Remember the name in case we have to modify the name + // value in the ea. + // + + RtlCopyMemory( OldOemName.Buffer, + Fcb->ShortName.Name.Oem.Buffer, + OldOemName.Length ); + + // + // Get the current stack location + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + // + // Extract information from the Irp to make our life easier + // + + FileObject = IrpSp->FileObject; + SourceCcb = FileObject->FsContext2; + TargetFileObject = IrpSp->Parameters.SetFile.FileObject; + ReplaceIfExists = IrpSp->Parameters.SetFile.ReplaceIfExists; + + RtlZeroMemory( &LocalCcb, sizeof(CCB) ); + + // + // P H A S E 1: + // + // Test if rename is legal. Only small side-effects are not undone. + // + + try { + + // + // Can't rename the root directory + // + + if ( NodeType(Fcb) == FAT_NTC_ROOT_DCB ) { + + try_return( Status = STATUS_INVALID_PARAMETER ); + } + + // + // Check that we were not given a dcb with open handles beneath + // it. If there are only UncleanCount == 0 Fcbs beneath us, then + // remove them from the prefix table, and they will just close + // and go away naturally. + // + + if (NodeType(Fcb) == FAT_NTC_DCB) { + + PFCB BatchOplockFcb; + ULONG BatchOplockCount; + + // + // Loop until there are no batch oplocks in the subtree below + // this directory. + // + + while (TRUE) { + + BatchOplockFcb = NULL; + BatchOplockCount = 0; + + // + // First look for any UncleanCount != 0 Fcbs, and fail if we + // find any. + // + + for ( TempFcb = FatGetNextFcbBottomUp(IrpContext, NULL, Fcb); + TempFcb != Fcb; + TempFcb = FatGetNextFcbBottomUp(IrpContext, TempFcb, Fcb) ) { + + if ( TempFcb->UncleanCount != 0 ) { + + // + // If there is a batch oplock on this file then + // increment our count and remember the Fcb if + // this is the first. + // + + if (FatIsFileOplockable( TempFcb ) && + (FsRtlCurrentBatchOplock( FatGetFcbOplock(TempFcb) ) +#if (NTDDI_VERSION >= NTDDI_WIN7) + || + FsRtlCurrentOplockH( FatGetFcbOplock(TempFcb) ) +#endif + )) { + + BatchOplockCount += 1; + if ( BatchOplockFcb == NULL ) { + + BatchOplockFcb = TempFcb; + } + + } else { + + try_return( Status = STATUS_ACCESS_DENIED ); + } + } + } + + // + // If this is not the first pass for rename and the number + // of batch oplocks has not decreased then give up. + // + + if ( BatchOplockFcb != NULL ) { + + if ( (Irp->IoStatus.Information != 0) && + (BatchOplockCount >= Irp->IoStatus.Information) ) { + + try_return( Status = STATUS_ACCESS_DENIED ); + } + + // + // Try to break this batch oplock. + // + + Irp->IoStatus.Information = BatchOplockCount; + Status = FsRtlCheckOplock( FatGetFcbOplock(BatchOplockFcb), + Irp, + IrpContext, + FatOplockComplete, + NULL ); + + // + // If the oplock was already broken then look for more + // batch oplocks. + // + + if (Status == STATUS_SUCCESS) { + + continue; + } + + // + // Otherwise the oplock package will post or complete the + // request. + // + + try_return( Status = STATUS_PENDING ); + } + + break; + } + + // + // Now try to get as many of these file object, and thus Fcbs + // to go away as possible, flushing first, of course. + // + + FatPurgeReferencedFileObjects( IrpContext, Fcb, Flush ); + + // + // OK, so there are no UncleanCount != 0, Fcbs. Infact, there + // shouldn't really be any Fcbs left at all, except obstinate + // ones from user mapped sections .... Remove the full file name + // and exact case lfn. + // + + for ( TempFcb = FatGetNextFcbBottomUp(IrpContext, NULL, Fcb); + TempFcb != Fcb; + TempFcb = FatGetNextFcbBottomUp(IrpContext, TempFcb, Fcb) ) { + + FatAcquireExclusiveFcb( IrpContext, TempFcb ); + + if (TempFcb->FullFileName.Buffer != NULL) { + + ExFreePool( TempFcb->FullFileName.Buffer ); + TempFcb->FullFileName.Buffer = NULL; + } + + FatReleaseFcb( IrpContext, TempFcb ); + } + } + + // + // Check if this is a simple rename or a fully-qualified rename + // In both cases we need to figure out what the TargetDcb, and + // NewName are. + // + + if (TargetFileObject == NULL) { + + // + // In the case of a simple rename the target dcb is the + // same as the source file's parent dcb, and the new file name + // is taken from the system buffer + // + + PFILE_RENAME_INFORMATION Buffer; + + Buffer = Irp->AssociatedIrp.SystemBuffer; + + TargetDcb = Fcb->ParentDcb; + + NewName.Length = (USHORT) Buffer->FileNameLength; + NewName.Buffer = (PWSTR) &Buffer->FileName; + + // + // Make sure the name is of legal length. + // + + if (NewName.Length > 255*sizeof(WCHAR)) { + + try_return( Status = STATUS_OBJECT_NAME_INVALID ); + } + + } else { + + // + // For a fully-qualified rename the target dcb is taken from + // the target file object, which must be on the same vcb as + // the source. + // + + PVCB TargetVcb; + PCCB TargetCcb; + + if ((FatDecodeFileObject( TargetFileObject, + &TargetVcb, + &TargetDcb, + &TargetCcb ) != UserDirectoryOpen) || + (TargetVcb != Vcb)) { + + try_return( Status = STATUS_INVALID_PARAMETER ); + } + + // + // This name is by definition legal. + // + + NewName = *((PUNICODE_STRING)&TargetFileObject->FileName); + } + + // + // We will need an upcased version of the unicode name and the + // old name as well. + // + + Status = RtlUpcaseUnicodeString( &NewUpcasedName, &NewName, FALSE ); + + if (!NT_SUCCESS(Status)) { + + try_return( Status ); + } + + FatGetUnicodeNameFromFcb( IrpContext, Fcb, &OldName ); + + Status = RtlUpcaseUnicodeString( &OldUpcasedName, &OldName, FALSE ); + + if (!NT_SUCCESS(Status)) { + try_return(Status); + } + + // + // Check if the current name and new name are equal, and the + // DCBs are equal. If they are then our work is already done. + // + + if (TargetDcb == Fcb->ParentDcb) { + + // + // OK, now if we found something then check if it was an exact + // match or just a case match. If it was an exact match, then + // we can bail here. + // + + if (FsRtlAreNamesEqual( &NewName, + &OldName, + FALSE, + NULL )) { + + try_return( Status = STATUS_SUCCESS ); + } + + // + // Check now for a case only rename. + // + + + if (FsRtlAreNamesEqual( &NewUpcasedName, + &OldUpcasedName, + FALSE, + NULL )) { + + CaseOnlyRename = TRUE; + } + + } else { + + RenamedAcrossDirectories = TRUE; + } + + // + // Upcase the name and convert it to the Oem code page. + // + // If the new UNICODE name is already more than 12 characters, + // then we know the Oem name will not be valid + // + + if (NewName.Length <= 12*sizeof(WCHAR)) { + + FatUnicodeToUpcaseOem( IrpContext, &NewOemName, &NewName ); + + // + // If the name is not valid 8.3, zero the length. + // + + if (FatSpaceInName( IrpContext, &NewName ) || + !FatIsNameShortOemValid( IrpContext, NewOemName, FALSE, FALSE, FALSE)) { + + NewOemName.Length = 0; + } + + } else { + + NewOemName.Length = 0; + } + + // + // Look in the tunnel cache for names and timestamps to restore + // + + TunneledDataSize = sizeof(LARGE_INTEGER); + HaveTunneledInformation = FsRtlFindInTunnelCache( &Vcb->Tunnel, + FatDirectoryKey(TargetDcb), + &NewName, + &UniTunneledShortName, + &UniTunneledLongName, + &TunneledDataSize, + &TunneledCreationTime ); + NT_ASSERT(TunneledDataSize == sizeof(LARGE_INTEGER)); + + + // + // Now we need to determine how many dirents this new name will + // require. + // + + if ((NewOemName.Length == 0) || + (FatEvaluateNameCase( IrpContext, + &NewName, + &AllLowerComponent, + &AllLowerExtension, + &CreateLfn ), + CreateLfn)) { + + DirentsRequired = FAT_LFN_DIRENTS_NEEDED(&NewName) + 1; + + } else { + + // + // The user-given name is a short name, but we might still have + // a tunneled long name we want to use. See if we can. + // + + if (UniTunneledLongName.Length && + !FatLfnDirentExists(IrpContext, TargetDcb, &UniTunneledLongName, &TargetLfn)) { + + UsingTunneledLfn = CreateLfn = TRUE; + DirentsRequired = FAT_LFN_DIRENTS_NEEDED(&UniTunneledLongName) + 1; + + } else { + + // + // This really is a simple dirent. Note that the two AllLower BOOLEANs + // are correctly set now. + // + + DirentsRequired = 1; + } + } + + // + // Do some extra checks here if we are not in Chicago mode. + // + + if (!FatData.ChicagoMode) { + + // + // If the name was not 8.3 valid, fail the rename. + // + + if (NewOemName.Length == 0) { + + try_return( Status = STATUS_OBJECT_NAME_INVALID ); + } + + // + // Don't use the magic bits. + // + + AllLowerComponent = FALSE; + AllLowerExtension = FALSE; + CreateLfn = FALSE; + UsingTunneledLfn = FALSE; + } + + if (!CaseOnlyRename) { + + // + // Check if the new name already exists, wait is known to be + // true. + // + + if (NewOemName.Length != 0) { + + FatStringTo8dot3( IrpContext, + NewOemName, + &LocalCcb.OemQueryTemplate.Constant ); + + } else { + + SetFlag( LocalCcb.Flags, CCB_FLAG_SKIP_SHORT_NAME_COMPARE ); + } + + LocalCcb.UnicodeQueryTemplate = NewUpcasedName; + LocalCcb.ContainsWildCards = FALSE; + + Flags = 0; + + + FatLocateDirent( IrpContext, + TargetDcb, + &LocalCcb, + 0, + &Flags, + &TargetDirent, + &TargetDirentBcb, + (PVBO)&TargetDirentOffset, + NULL, + &TargetLfn, + &TargetOrigLfn ); + + if (TargetDirent != NULL) { + + + // + // The name already exists, check if the user wants + // to overwrite the name, and has access to do the overwrite + // We cannot overwrite a directory. + // + + if ((!ReplaceIfExists) || + (FlagOn(TargetDirent->Attributes, FAT_DIRENT_ATTR_DIRECTORY)) || + (FlagOn(TargetDirent->Attributes, FAT_DIRENT_ATTR_READ_ONLY))) { + + try_return( Status = STATUS_OBJECT_NAME_COLLISION ); + } + + // + // Check that the file has no open user handles, if it does + // then we will deny access. We do the check by searching + // down the list of fcbs opened under our parent Dcb, and making + // sure none of the maching Fcbs have a non-zero unclean count or + // outstanding image sections. + // + + for (Links = TargetDcb->Specific.Dcb.ParentDcbQueue.Flink; + Links != &TargetDcb->Specific.Dcb.ParentDcbQueue; ) { + + TempFcb = CONTAINING_RECORD( Links, FCB, ParentDcbLinks ); + + // + // Advance now. The image section flush may cause the final + // close, which will recursively happen underneath of us here. + // It would be unfortunate if we looked through free memory. + // + + Links = Links->Flink; + + if ((TempFcb->DirentOffsetWithinDirectory == TargetDirentOffset) && + ((TempFcb->UncleanCount != 0) || + !MmFlushImageSection( &TempFcb->NonPaged->SectionObjectPointers, + MmFlushForDelete))) { + + // + // If there are batch oplocks on this file then break the + // oplocks before failing the rename. + // + + Status = STATUS_ACCESS_DENIED; + + if (FatIsFileOplockable( TempFcb ) && + (FsRtlCurrentBatchOplock( FatGetFcbOplock(TempFcb) ) +#if (NTDDI_VERSION >= NTDDI_WIN7) + || + FsRtlCurrentOplockH( FatGetFcbOplock(TempFcb) ) +#endif + )) { + + // + // Do all of our cleanup now since the IrpContext + // could go away when this request is posted. + // + + FatUnpinBcb( IrpContext, TargetDirentBcb ); + + Status = FsRtlCheckOplock( FatGetFcbOplock(TempFcb), + Irp, + IrpContext, + FatOplockComplete, + NULL ); + + if (Status != STATUS_PENDING) { + + Status = STATUS_ACCESS_DENIED; + } + } + + try_return( NOTHING ); + } + } + + // + // OK, this target is toast. Remember the Lfn offset. + // + + TargetLfnOffset = TargetDirentOffset - + FAT_LFN_DIRENTS_NEEDED(&TargetOrigLfn) * + sizeof(DIRENT); + + DeleteTarget = TRUE; + } + } + + + // + // If we will need more dirents than we have, allocate them now. + // + + if ((TargetDcb != Fcb->ParentDcb) || + (DirentsRequired != + (Fcb->DirentOffsetWithinDirectory - + Fcb->LfnOffsetWithinDirectory) / sizeof(DIRENT) + 1)) { + + // + // Get some new allocation + // + + NewOffset = FatCreateNewDirent( IrpContext, + TargetDcb, + DirentsRequired, + FALSE ); + + DeleteSourceDirent = TRUE; + + } else { + + NewOffset = Fcb->LfnOffsetWithinDirectory; + } + + ContinueWithRename = TRUE; + + try_exit: NOTHING; + + } finally { + + if (!ContinueWithRename) { + + // + // Undo everything from above. + // + + ExFreePool( UnicodeBuffer ); + FatUnpinBcb( IrpContext, TargetDirentBcb ); + } + } + + // + // Now, if we are already done, return here. + // + + if (!ContinueWithRename) { + + return Status; + } + + // + // P H A S E 2: Actually perform the rename. + // + + try { + + // + // Report the fact that we are going to remove this entry. + // If we renamed within the same directory and the new name for the + // file did not previously exist, we report this as a rename old + // name. Otherwise this is a removed file. + // + + if (!RenamedAcrossDirectories && !DeleteTarget) { + + NotifyAction = FILE_ACTION_RENAMED_OLD_NAME; + + } else { + + NotifyAction = FILE_ACTION_REMOVED; + } + + FatNotifyReportChange( IrpContext, + Vcb, + Fcb, + ((NodeType( Fcb ) == FAT_NTC_FCB) + ? FILE_NOTIFY_CHANGE_FILE_NAME + : FILE_NOTIFY_CHANGE_DIR_NAME ), + NotifyAction ); + + try { + + // + // Capture a copy of the source dirent. + // + + FatGetDirentFromFcbOrDcb( IrpContext, Fcb, FALSE, &OldDirent, &OldDirentBcb ); + + Dirent = *OldDirent; + + // + // Tunnel the source Fcb - the names are disappearing regardless of + // whether the dirent allocation physically changed + // + + FatTunnelFcbOrDcb( Fcb, SourceCcb ); + + // + // From here until very nearly the end of the operation, if we raise there + // is no reasonable way to suppose we'd be able to undo the damage. Not + // being a transactional filesystem, FAT is at the mercy of a lot of things + // (as the astute reader has no doubt realized by now). + // + + InvalidateFcbOnRaise = TRUE; + + // + // Delete our current dirent(s) if we got a new one. + // + + if (DeleteSourceDirent) { + + FatDeleteDirent( IrpContext, Fcb, NULL, FALSE ); + } + + // + // Delete a target conflict if we were meant to. + // + + if (DeleteTarget) { + + FatDeleteFile( IrpContext, + TargetDcb, + TargetLfnOffset, + TargetDirentOffset, + TargetDirent, + &TargetLfn ); + } + + // + // We need to evaluate any short names required. If there were any + // conflicts in existing short names, they would have been deleted above. + // + // It isn't neccesary to worry about the UsingTunneledLfn case. Since we + // actually already know whether CreateLfn will be set either NewName is + // an Lfn and !UsingTunneledLfn is implied or NewName is a short name and + // we can handle that externally. + // + + FatSelectNames( IrpContext, + TargetDcb, + &NewOemName, + &NewName, + &NewOemName, + (HaveTunneledInformation ? &UniTunneledShortName : NULL), + &AllLowerComponent, + &AllLowerExtension, + &CreateLfn ); + + if (!CreateLfn && UsingTunneledLfn) { + + CreateLfn = TRUE; + NewName = UniTunneledLongName; + + // + // Short names are always upcase if an LFN exists + // + + AllLowerComponent = FALSE; + AllLowerExtension = FALSE; + } + + // + // OK, now setup the new dirent(s) for the new name. + // + + FatPrepareWriteDirectoryFile( IrpContext, + TargetDcb, + NewOffset, + sizeof(DIRENT), + &NewDirentBcb, + &NewDirent, + FALSE, + TRUE, + &Status ); + + NT_ASSERT( NT_SUCCESS( Status ) ); + + // + // Deal with the special case of an LFN + Dirent structure crossing + // a page boundry. + // + + if ((NewOffset / PAGE_SIZE) != + ((NewOffset + (DirentsRequired - 1) * sizeof(DIRENT)) / PAGE_SIZE)) { + + SecondPageOffset = (NewOffset & ~(PAGE_SIZE - 1)) + PAGE_SIZE; + + BytesInFirstPage = SecondPageOffset - NewOffset; + + DirentsInFirstPage = BytesInFirstPage / sizeof(DIRENT); + + FatPrepareWriteDirectoryFile( IrpContext, + TargetDcb, + SecondPageOffset, + sizeof(DIRENT), + &SecondPageBcb, + &SecondPageDirent, + FALSE, + TRUE, + &Status ); + + NT_ASSERT( NT_SUCCESS( Status ) ); + + FirstPageDirent = NewDirent; + + NewDirent = FsRtlAllocatePoolWithTag( PagedPool, + DirentsRequired * sizeof(DIRENT), + TAG_DIRENT ); + + NewDirentFromPool = TRUE; + } + + // + // Bump up Dirent and DirentOffset + // + + ShortDirent = NewDirent + DirentsRequired - 1; + ShortDirentOffset = NewOffset + (DirentsRequired - 1) * sizeof(DIRENT); + + // + // Fill in the fields of the dirent. + // + + *ShortDirent = Dirent; + + FatConstructDirent( IrpContext, + ShortDirent, + &NewOemName, + AllLowerComponent, + AllLowerExtension, + CreateLfn ? &NewName : NULL, + Dirent.Attributes, + FALSE, + (HaveTunneledInformation ? &TunneledCreationTime : NULL) ); + + if (HaveTunneledInformation) { + + // + // Need to go in and fix the timestamps in the FCB. Note that we can't use + // the TunneledCreationTime since the conversions may have failed. + // + + Fcb->CreationTime = FatFatTimeToNtTime(IrpContext, ShortDirent->CreationTime, ShortDirent->CreationMSec); + Fcb->LastWriteTime = FatFatTimeToNtTime(IrpContext, ShortDirent->LastWriteTime, 0); + Fcb->LastAccessTime = FatFatDateToNtTime(IrpContext, ShortDirent->LastAccessDate); + } + + + // + // If the dirent crossed pages, split the contents of the + // temporary pool between the two pages. + // + + if (NewDirentFromPool) { + + RtlCopyMemory( FirstPageDirent, NewDirent, BytesInFirstPage ); + + RtlCopyMemory( SecondPageDirent, + NewDirent + DirentsInFirstPage, + DirentsRequired*sizeof(DIRENT) - BytesInFirstPage ); + + ShortDirent = SecondPageDirent + + (DirentsRequired - DirentsInFirstPage) - 1; + } + + Dirent = *ShortDirent; + + } finally { + + // + // Remove the entry from the splay table, and then remove the + // full file name and exact case lfn. It is important that we + // always remove the name from the prefix table regardless of + // other errors. + // + + FatRemoveNames( IrpContext, Fcb ); + + if (Fcb->FullFileName.Buffer != NULL) { + + ExFreePool( Fcb->FullFileName.Buffer ); + Fcb->FullFileName.Buffer = NULL; + } + + if (Fcb->ExactCaseLongName.Buffer) { + + ExFreePool( Fcb->ExactCaseLongName.Buffer ); + Fcb->ExactCaseLongName.Buffer = NULL; + } + + FatUnpinBcb( IrpContext, OldDirentBcb ); + FatUnpinBcb( IrpContext, TargetDirentBcb ); + FatUnpinBcb( IrpContext, NewDirentBcb ); + FatUnpinBcb( IrpContext, SecondPageBcb ); + } + + // + // Now we need to update the location of the file's directory + // offset and move the fcb from its current parent dcb to + // the target dcb. + // + + Fcb->LfnOffsetWithinDirectory = NewOffset; + Fcb->DirentOffsetWithinDirectory = ShortDirentOffset; + + RemoveEntryList( &Fcb->ParentDcbLinks ); + + // + // There is a deep reason we put files on the tail, others on the head, + // which is to allow us to easily enumerate all child directories before + // child files. This is important to let us maintain whole-volume lockorder + // via BottomUp enumeration. + // + + if (NodeType(Fcb) == FAT_NTC_FCB) { + + InsertTailList( &TargetDcb->Specific.Dcb.ParentDcbQueue, + &Fcb->ParentDcbLinks ); + + } else { + + InsertHeadList( &TargetDcb->Specific.Dcb.ParentDcbQueue, + &Fcb->ParentDcbLinks ); + } + + OldParentDcb = Fcb->ParentDcb; + Fcb->ParentDcb = TargetDcb; + +#if (NTDDI_VERSION >= NTDDI_WIN8) + // + // Break parent directory oplock on the old parent. Directory oplock + // breaks are always advisory, so we will never block/get STATUS_PENDING + // here. + // + + FsRtlCheckOplockEx( FatGetFcbOplock(OldParentDcb), + IrpContext->OriginatingIrp, + OPLOCK_FLAG_PARENT_OBJECT, + NULL, + NULL, + NULL ); +#endif + + // + // If we renamed across directories, some cleanup is now in order. + // + + if (RenamedAcrossDirectories) { + +#if (NTDDI_VERSION >= NTDDI_WIN8) + // + // Break parent directory oplock on the new parent. Directory oplock + // breaks are always advisory, so we will never block/get STATUS_PENDING + // here. + // + + FsRtlCheckOplockEx( FatGetFcbOplock(TargetDcb), + IrpContext->OriginatingIrp, + OPLOCK_FLAG_PARENT_OBJECT, + NULL, + NULL, + NULL ); +#endif + + // + // See if we need to uninitialize the cachemap for the source directory. + // Do this now in case we get unlucky and raise trying to finalize the + // operation. + // + + if (IsListEmpty(&OldParentDcb->Specific.Dcb.ParentDcbQueue) && + (OldParentDcb->OpenCount == 0) && + (OldParentDcb->Specific.Dcb.DirectoryFile != NULL)) { + + NT_ASSERT( NodeType(OldParentDcb) == FAT_NTC_DCB ); + + DirectoryFileObject = OldParentDcb->Specific.Dcb.DirectoryFile; + + OldParentDcb->Specific.Dcb.DirectoryFile = NULL; + } + + // + // If we move a directory across directories, we have to change + // the cluster number in its .. entry + // + + if (NodeType(Fcb) == FAT_NTC_DCB) { + + FatPrepareWriteDirectoryFile( IrpContext, + Fcb, + sizeof(DIRENT), + sizeof(DIRENT), + &DotDotBcb, + &DotDotDirent, + FALSE, + TRUE, + &Status ); + + NT_ASSERT( NT_SUCCESS( Status ) ); + + DotDotDirent->FirstClusterOfFile = (USHORT) + ( NodeType(TargetDcb) == FAT_NTC_ROOT_DCB ? + 0 : TargetDcb->FirstClusterOfFile); + + if (FatIsFat32( Vcb )) { + + DotDotDirent->FirstClusterOfFileHi = (USHORT) + ( NodeType( TargetDcb ) == FAT_NTC_ROOT_DCB ? + 0 : (TargetDcb->FirstClusterOfFile >> 16)); + } + } + } + + // + // Now we need to setup the splay table and the name within + // the fcb. Free the old short name at this point. + // + + ExFreePool( Fcb->ShortName.Name.Oem.Buffer ); + Fcb->ShortName.Name.Oem.Buffer = NULL; + + + FatConstructNamesInFcb( IrpContext, + Fcb, + &Dirent, + CreateLfn ? &NewName : NULL ); + + FatSetFullNameInFcb( IrpContext, Fcb, &NewName ); + + // + // The rest of the actions taken are not related to correctness of + // the in-memory structures, so we shouldn't toast the Fcb if we + // raise from here to the end. + // + + InvalidateFcbOnRaise = FALSE; + + // + // If a file, set the file as modified so that the archive bit + // is set. We prevent this from adjusting the write time by + // indicating the user flag in the ccb. + // + + if (Fcb->Header.NodeTypeCode == FAT_NTC_FCB) { + + SetFlag( FileObject->Flags, FO_FILE_MODIFIED ); + SetFlag( Ccb->Flags, CCB_FLAG_USER_SET_LAST_WRITE ); + } + + // + // We have three cases to report. + // + // 1. If we overwrote an existing file, we report this as + // a modified file. + // + // 2. If we renamed to a new directory, then we added a file. + // + // 3. If we renamed in the same directory, then we report the + // the renamednewname. + // + + if (DeleteTarget) { + + FatNotifyReportChange( IrpContext, + Vcb, + Fcb, + FILE_NOTIFY_CHANGE_ATTRIBUTES + | FILE_NOTIFY_CHANGE_SIZE + | FILE_NOTIFY_CHANGE_LAST_WRITE + | FILE_NOTIFY_CHANGE_LAST_ACCESS + | FILE_NOTIFY_CHANGE_CREATION + | FILE_NOTIFY_CHANGE_EA, + FILE_ACTION_MODIFIED ); + + } else if (RenamedAcrossDirectories) { + + FatNotifyReportChange( IrpContext, + Vcb, + Fcb, + ((NodeType( Fcb ) == FAT_NTC_FCB) + ? FILE_NOTIFY_CHANGE_FILE_NAME + : FILE_NOTIFY_CHANGE_DIR_NAME ), + FILE_ACTION_ADDED ); + + } else { + + FatNotifyReportChange( IrpContext, + Vcb, + Fcb, + ((NodeType( Fcb ) == FAT_NTC_FCB) + ? FILE_NOTIFY_CHANGE_FILE_NAME + : FILE_NOTIFY_CHANGE_DIR_NAME ), + FILE_ACTION_RENAMED_NEW_NAME ); + } + + // + // We need to update the file name in the dirent. This value + // is never used elsewhere, so we don't concern ourselves + // with any error we may encounter. We let chkdsk fix the + // disk at some later time. + // + + if (!FatIsFat32(Vcb) && + Dirent.ExtendedAttributes != 0) { + + FatRenameEAs( IrpContext, + Fcb, + Dirent.ExtendedAttributes, + &OldOemName ); + } + + FatUnpinBcb( IrpContext, DotDotBcb ); + + FatUnpinRepinnedBcbs( IrpContext ); + + // + // Set our final status + // + + Status = STATUS_SUCCESS; + + } finally { + + DebugUnwind( FatSetRenameInfo ); + + ExFreePool( UnicodeBuffer ); + + if (UniTunneledLongName.Buffer != UniTunneledLongNameBuffer) { + + // + // Free pool if the buffer was grown on tunneling lookup + // + + ExFreePool(UniTunneledLongName.Buffer); + } + + if (NewDirentFromPool) { + + ExFreePool( NewDirent ); + } + + FatUnpinBcb( IrpContext, TargetDirentBcb ); + FatUnpinBcb( IrpContext, DotDotBcb ); + + // + // Uninitialize the cachemap for the source directory if we need to. + // + + if (DirectoryFileObject) { + + DebugTrace(0, Dbg, "Uninitialize our parent Stream Cache Map\n", 0); + + CcUninitializeCacheMap( DirectoryFileObject, NULL, NULL ); + + ObDereferenceObject( DirectoryFileObject ); + } + + // + // If this was an abnormal termination, then we are in trouble. + // Should the operation have been in a sensitive state there is + // nothing we can do but invalidate the Fcb. + // + + if (AbnormalTermination() && InvalidateFcbOnRaise) { + + Fcb->FcbCondition = FcbBad; + } + + DebugTrace(-1, Dbg, "FatSetRenameInfo -> %08lx\n", Status); + } + + return Status; +} + + +// +// Internal Support Routine +// + +NTSTATUS +FatSetPositionInfo ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp, + IN PFILE_OBJECT FileObject + ) + +/*++ + +Routine Description: + + This routine performs the set position information for fat. It either + completes the request or enqueues it off to the fsp. + +Arguments: + + Irp - Supplies the irp being processed + + FileObject - Supplies the file object being processed + +Return Value: + + NTSTATUS - The result of this operation if it completes without + an exception. + +--*/ + +{ + PFILE_POSITION_INFORMATION Buffer; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatSetPositionInfo...\n", 0); + + 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( FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING )) { + + PDEVICE_OBJECT DeviceObject; + + DeviceObject = IoGetCurrentIrpStackLocation( Irp )->DeviceObject; + + if ((Buffer->CurrentByteOffset.LowPart & DeviceObject->AlignmentRequirement) != 0) { + + DebugTrace(0, Dbg, "Cannot set position due to aligment conflict\n", 0); + DebugTrace(-1, Dbg, "FatSetPositionInfo -> %08lx\n", STATUS_INVALID_PARAMETER); + + return STATUS_INVALID_PARAMETER; + } + } + + // + // The input parameter is fine so set the current byte offset and + // complete the request + // + + DebugTrace(0, Dbg, "Set the new position to %08lx\n", Buffer->CurrentByteOffset); + + FileObject->CurrentByteOffset = Buffer->CurrentByteOffset; + + DebugTrace(-1, Dbg, "FatSetPositionInfo -> %08lx\n", STATUS_SUCCESS); + + UNREFERENCED_PARAMETER( IrpContext ); + + return STATUS_SUCCESS; +} + + +// +// Internal Support Routine +// + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatSetAllocationInfo ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp, + IN PFCB Fcb, + IN PFILE_OBJECT FileObject + ) + +/*++ + +Routine Description: + + This routine performs the set Allocation information for fat. It either + completes the request or enqueues it off to the fsp. + +Arguments: + + Irp - Supplies the irp being processed + + Fcb - Supplies the Fcb or Dcb being processed, already known not to + be the root dcb + + FileObject - Supplies the FileObject being processed, already known not to + be the root dcb + +Return Value: + + NTSTATUS - The result of this operation if it completes without + an exception. + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + PFILE_ALLOCATION_INFORMATION Buffer; + ULONG NewAllocationSize = 0; + ULONG HeaderSize = 0; + + BOOLEAN FileSizeTruncated = FALSE; + BOOLEAN CacheMapInitialized = FALSE; + BOOLEAN ResourceAcquired = FALSE; + ULONG OriginalFileSize = 0; + ULONG OriginalValidDataLength = 0; + ULONG OriginalValidDataToDisk = 0; + + PAGED_CODE(); + + Buffer = Irp->AssociatedIrp.SystemBuffer; + + NewAllocationSize = Buffer->AllocationSize.LowPart; + + DebugTrace(+1, Dbg, "FatSetAllocationInfo.. to %08lx\n", NewAllocationSize); + + // + // Allocation is only allowed on a file and not a directory + // + + if (NodeType(Fcb) == FAT_NTC_DCB) { + + DebugTrace(-1, Dbg, "Cannot change allocation of a directory\n", 0); + + return STATUS_INVALID_DEVICE_REQUEST; + } + + // + // Check that the new file allocation is legal + // + + + if (!FatIsIoRangeValid( Fcb->Vcb, Buffer->AllocationSize, 0)) { + + DebugTrace(-1, Dbg, "Illegal allocation size\n", 0); + + return STATUS_DISK_FULL; + } + + + // + // If we haven't yet looked up the correct AllocationSize, do so. + // + + if (Fcb->Header.AllocationSize.QuadPart == FCB_LOOKUP_ALLOCATIONSIZE_HINT) { + + FatLookupFileAllocationSize( IrpContext, Fcb ); + } + + // + // This is kinda gross, but if the file is not cached, but there is + // a data section, we have to cache the file to avoid a bunch of + // extra work. + // + + if ((FileObject->SectionObjectPointer->DataSectionObject != NULL) && + (FileObject->SectionObjectPointer->SharedCacheMap == NULL) && + !FlagOn(Irp->Flags, IRP_PAGING_IO)) { + + NT_ASSERT( !FlagOn( FileObject->Flags, FO_CLEANUP_COMPLETE ) ); + + // + // Now initialize the cache map. + // + + FatInitializeCacheMap( FileObject, + (PCC_FILE_SIZES)&Fcb->Header.AllocationSize, + FALSE, + &FatData.CacheManagerCallbacks, + Fcb ); + + CacheMapInitialized = TRUE; + } + + // + // Now mark the fact that the file needs to be truncated on close + // + + Fcb->FcbState |= FCB_STATE_TRUNCATE_ON_CLOSE; + + // + // Now mark that the time on the dirent needs to be updated on close. + // + + SetFlag( FileObject->Flags, FO_FILE_MODIFIED ); + + try { + + // + // Increase or decrease the allocation size. + // + + if (NewAllocationSize+HeaderSize > Fcb->Header.AllocationSize.LowPart) { + + FatAddFileAllocation( IrpContext, Fcb, FileObject, NewAllocationSize); + + } else { + + // + // Check here if we will be decreasing file size and synchonize with + // paging IO. + // + + if ( Fcb->Header.FileSize.LowPart > NewAllocationSize ) { + + // + // Before we actually truncate, check to see if the purge + // is going to fail. + // + + if (!MmCanFileBeTruncated( FileObject->SectionObjectPointer, + &Buffer->AllocationSize )) { + + try_return( Status = STATUS_USER_MAPPED_FILE ); + } + + FileSizeTruncated = TRUE; + + OriginalFileSize = Fcb->Header.FileSize.LowPart; + OriginalValidDataLength = Fcb->Header.ValidDataLength.LowPart; + OriginalValidDataToDisk = Fcb->ValidDataToDisk; + + (VOID)ExAcquireResourceExclusiveLite( Fcb->Header.PagingIoResource, TRUE ); + ResourceAcquired = TRUE; + + Fcb->Header.FileSize.LowPart = NewAllocationSize; + + // + // If we reduced the file size to less than the ValidDataLength, + // adjust the VDL. Likewise ValidDataToDisk. + // + + if (Fcb->Header.ValidDataLength.LowPart > Fcb->Header.FileSize.LowPart) { + + Fcb->Header.ValidDataLength.LowPart = Fcb->Header.FileSize.LowPart; + } + if (Fcb->ValidDataToDisk > Fcb->Header.FileSize.LowPart) { + + Fcb->ValidDataToDisk = Fcb->Header.FileSize.LowPart; + } + + } + + // + // Now that File Size is down, actually do the truncate. + // + + FatTruncateFileAllocation( IrpContext, Fcb, NewAllocationSize, FALSE ); + + // + // Now check if we needed to decrease the file size accordingly. + // + + if ( FileSizeTruncated ) { + + // + // Tell the cache manager we reduced the file size. + // The call is unconditional, because MM always wants to know. + // + +#if DBG + try { +#endif + + CcSetFileSizes( FileObject, (PCC_FILE_SIZES)&Fcb->Header.AllocationSize ); + +#if DBG + } except(FatBugCheckExceptionFilter( GetExceptionInformation() )) { + + NOTHING; + } +#endif + + NT_ASSERT( FileObject->DeleteAccess || FileObject->WriteAccess ); + + // + // There is no going back from this. If we run into problems updating + // the dirent we will have to live with the consequences. Not sending + // the notifies is likewise pretty benign compared to failing the entire + // operation and trying to back out everything, which could fail for the + // same reasons. + // + // If you want a transacted filesystem, use NTFS ... + // + + FileSizeTruncated = FALSE; + + FatSetFileSizeInDirent( IrpContext, Fcb, NULL ); + + // + // Report that we just reduced the file size. + // + + FatNotifyReportChange( IrpContext, + Fcb->Vcb, + Fcb, + FILE_NOTIFY_CHANGE_SIZE, + FILE_ACTION_MODIFIED ); + } + } + + try_exit: NOTHING; + + } finally { + + if ( AbnormalTermination() && FileSizeTruncated ) { + + Fcb->Header.FileSize.LowPart = OriginalFileSize; + Fcb->Header.ValidDataLength.LowPart = OriginalValidDataLength; + Fcb->ValidDataToDisk = OriginalValidDataToDisk; + + // + // Make sure Cc knows the right filesize. + // + + if (FileObject->SectionObjectPointer->SharedCacheMap != NULL) { + + *CcGetFileSizePointer(FileObject) = Fcb->Header.FileSize; + } + + NT_ASSERT( Fcb->Header.FileSize.LowPart <= Fcb->Header.AllocationSize.LowPart ); + } + + if (CacheMapInitialized) { + + CcUninitializeCacheMap( FileObject, NULL, NULL ); + } + + if (ResourceAcquired) { + + ExReleaseResourceLite( Fcb->Header.PagingIoResource ); + + } + + } + + DebugTrace(-1, Dbg, "FatSetAllocationInfo -> %08lx\n", STATUS_SUCCESS); + + return Status; +} + + +// +// Internal Support Routine +// + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatSetEndOfFileInfo ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp, + IN PFILE_OBJECT FileObject, + IN PVCB Vcb, + IN PFCB Fcb + ) + +/*++ + +Routine Description: + + This routine performs the set End of File information for fat. It either + completes the request or enqueues it off to the fsp. + +Arguments: + + Irp - Supplies the irp being processed + + FileObject - Supplies the file object being processed + + Vcb - Supplies the Vcb being processed + + Fcb - Supplies the Fcb or Dcb being processed, already known not to + be the root dcb + +Return Value: + + NTSTATUS - The result of this operation if it completes without + an exception. + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + + PFILE_END_OF_FILE_INFORMATION Buffer; + + ULONG NewFileSize = 0; + ULONG InitialFileSize = 0; + ULONG InitialValidDataLength = 0; + ULONG InitialValidDataToDisk = 0; + + BOOLEAN CacheMapInitialized = FALSE; + BOOLEAN UnwindFileSizes = FALSE; + BOOLEAN ResourceAcquired = FALSE; + + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatSetEndOfFileInfo...\n", 0); + + Buffer = Irp->AssociatedIrp.SystemBuffer; + + try { + + // + // File Size changes are only allowed on a file and not a directory + // + + if (NodeType(Fcb) != FAT_NTC_FCB) { + + DebugTrace(0, Dbg, "Cannot change size of a directory\n", 0); + + try_return( Status = STATUS_INVALID_DEVICE_REQUEST ); + } + + + // + // Check that the new file size is legal + // + + + if (!FatIsIoRangeValid( Fcb->Vcb, Buffer->EndOfFile, 0)) { + + DebugTrace(0, Dbg, "Illegal allocation size\n", 0); + + try_return( Status = STATUS_DISK_FULL ); + } + + NewFileSize = Buffer->EndOfFile.LowPart; + + + // + // If we haven't yet looked up the correct AllocationSize, do so. + // + + if ((Fcb->Header.AllocationSize.QuadPart == FCB_LOOKUP_ALLOCATIONSIZE_HINT) && + (Fcb->FcbCondition == FcbGood)) { + + FatLookupFileAllocationSize( IrpContext, Fcb ); + } + + // + // This is kinda gross, but if the file is not cached, but there is + // a data section, we have to cache the file to avoid a bunch of + // extra work. + // + + if ((FileObject->SectionObjectPointer->DataSectionObject != NULL) && + (FileObject->SectionObjectPointer->SharedCacheMap == NULL) && + !FlagOn(Irp->Flags, IRP_PAGING_IO)) { + + if (FlagOn( FileObject->Flags, FO_CLEANUP_COMPLETE )) { + + // + // This IRP has raced (and lost) with a close (=>cleanup) + // on the same fileobject. We don't want to reinitialise the + // cachemap here now because we'll leak it (unless we do so & + // then tear it down again here, which is too much of a change at + // this stage). So we'll just say the file is closed - which + // is arguably the right thing to do anyway, since a caller + // racing operations in this way is broken. The only stumbling + // block is possibly filters - do they operate on cleaned + // up fileobjects? + // + + FatRaiseStatus( IrpContext, STATUS_FILE_CLOSED); + } + + // + // Now initialize the cache map. + // + + FatInitializeCacheMap( FileObject, + (PCC_FILE_SIZES)&Fcb->Header.AllocationSize, + FALSE, + &FatData.CacheManagerCallbacks, + Fcb ); + + CacheMapInitialized = TRUE; + } + + // + // Do a special case here for the lazy write of file sizes. + // + + if (IoGetCurrentIrpStackLocation(Irp)->Parameters.SetFile.AdvanceOnly) { + + // + // Only attempt this if the file hasn't been "deleted on close" and + // this is a good FCB. + // + + if (!IsFileDeleted( IrpContext, Fcb ) && (Fcb->FcbCondition == FcbGood)) { + + PDIRENT Dirent = NULL; + PBCB DirentBcb; + + + // + // Never have the dirent filesize larger than the fcb filesize + // + + if (NewFileSize >= Fcb->Header.FileSize.LowPart) { + + NewFileSize = Fcb->Header.FileSize.LowPart; + } + + // + // Make sure we don't set anything higher than the alloc size. + // + + + NT_ASSERT( NewFileSize <= Fcb->Header.AllocationSize.LowPart ); + + + // + // Only advance the file size, never reduce it with this call + // + + FatGetDirentFromFcbOrDcb( IrpContext, + Fcb, + FALSE, + &Dirent, + &DirentBcb ); + try { + + + if ( NewFileSize > Dirent->FileSize ) { + Dirent->FileSize = NewFileSize; + + FatSetDirtyBcb( IrpContext, DirentBcb, Fcb->Vcb, TRUE ); + + // + // Report that we just changed the file size. + // + + FatNotifyReportChange( IrpContext, + Vcb, + Fcb, + FILE_NOTIFY_CHANGE_SIZE, + FILE_ACTION_MODIFIED ); + + } + + } finally { + + FatUnpinBcb( IrpContext, DirentBcb ); + } + + } else { + + DebugTrace(0, Dbg, "Cannot set size on deleted file.\n", 0); + } + + try_return( Status = STATUS_SUCCESS ); + } + + // + // Check if the new file size is greater than the current + // allocation size. If it is then we need to increase the + // allocation size. + // + + + if ( (NewFileSize) > Fcb->Header.AllocationSize.LowPart ) { + + // + // Change the file allocation + // + + FatAddFileAllocation( IrpContext, Fcb, FileObject, NewFileSize ); + } + + + // + // At this point we have enough allocation for the file. + // So check if we are really changing the file size + // + + if (Fcb->Header.FileSize.LowPart != NewFileSize) { + + if ( NewFileSize < Fcb->Header.FileSize.LowPart ) { + + // + // Before we actually truncate, check to see if the purge + // is going to fail. + // + + if (!MmCanFileBeTruncated( FileObject->SectionObjectPointer, + &Buffer->EndOfFile )) { + + try_return( Status = STATUS_USER_MAPPED_FILE ); + } + + // + // This call is unconditional, because MM always wants to know. + // Also serialize here with paging io since we are truncating + // the file size. + // + + ResourceAcquired = + ExAcquireResourceExclusiveLite( Fcb->Header.PagingIoResource, TRUE ); + } + + // + // Set the new file size + // + + InitialFileSize = Fcb->Header.FileSize.LowPart; + InitialValidDataLength = Fcb->Header.ValidDataLength.LowPart; + InitialValidDataToDisk = Fcb->ValidDataToDisk; + UnwindFileSizes = TRUE; + + Fcb->Header.FileSize.LowPart = NewFileSize; + + // + // If we reduced the file size to less than the ValidDataLength, + // adjust the VDL. Likewise ValidDataToDisk. + // + + if (Fcb->Header.ValidDataLength.LowPart > NewFileSize) { + + Fcb->Header.ValidDataLength.LowPart = NewFileSize; + } + + if (Fcb->ValidDataToDisk > NewFileSize) { + + Fcb->ValidDataToDisk = NewFileSize; + } + + DebugTrace(0, Dbg, "New file size is 0x%08lx.\n", NewFileSize); + + // + // We must now update the cache mapping (benign if not cached). + // + + CcSetFileSizes( FileObject, + (PCC_FILE_SIZES)&Fcb->Header.AllocationSize ); + + FatSetFileSizeInDirent( IrpContext, Fcb, NULL ); + + // + // Report that we just changed the file size. + // + + FatNotifyReportChange( IrpContext, + Vcb, + Fcb, + FILE_NOTIFY_CHANGE_SIZE, + FILE_ACTION_MODIFIED ); + + // + // Mark the fact that the file will need to checked for + // truncation on cleanup. + // + + SetFlag( Fcb->FcbState, FCB_STATE_TRUNCATE_ON_CLOSE ); + } + + // + // Set this handle as having modified the file + // + + FileObject->Flags |= FO_FILE_MODIFIED; + + // + // Set our return status to success + // + + Status = STATUS_SUCCESS; + + try_exit: NOTHING; + + FatUnpinRepinnedBcbs( IrpContext ); + + } finally { + + DebugUnwind( FatSetEndOfFileInfo ); + + if (AbnormalTermination() && UnwindFileSizes) { + + Fcb->Header.FileSize.LowPart = InitialFileSize; + Fcb->Header.ValidDataLength.LowPart = InitialValidDataLength; + Fcb->ValidDataToDisk = InitialValidDataToDisk; + + if (FileObject->SectionObjectPointer->SharedCacheMap != NULL) { + + *CcGetFileSizePointer(FileObject) = Fcb->Header.FileSize; + } + + // + // WinSE bug #307418 "Occasional data corruption when + // standby/resume while copying files to removable FAT + // formatted media". + // On system suspend FatUnpinRepinnedBcbs() can fail + // because the underlying drive is already marked with DO_VERIFY + // flag. FatUnpinRepinnedBcbs() will raise in this case and + // the file size changes will be un-rolled in FCB but the change + // to Dirent file still can make it to the disk since its BCB + // will not be purged by FatUnpinRepinnedBcbs(). In this case + // we'll also try to un-roll the change to Dirent to keep + // in-memory and on-disk metadata in sync. + // + + FatSetFileSizeInDirentNoRaise( IrpContext, Fcb, NULL ); + + } + + if (CacheMapInitialized) { + + CcUninitializeCacheMap( FileObject, NULL, NULL ); + } + + if ( ResourceAcquired ) { + + ExReleaseResourceLite( Fcb->Header.PagingIoResource ); + } + + DebugTrace(-1, Dbg, "FatSetEndOfFileInfo -> %08lx\n", Status); + } + + return Status; +} + + +// +// Internal Support Routine +// + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatSetValidDataLengthInfo ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp, + IN PFILE_OBJECT FileObject, + IN PFCB Fcb, + IN PCCB Ccb + ) + +/*++ + +Routine Description: + + This routine performs the set valid data length information for fat. It either + completes the request or enqueues it off to the fsp. + +Arguments: + + Irp - Supplies the irp being processed + + FileObject - Supplies the file object being processed + + Fcb - Supplies the Fcb or Dcb being processed, already known not to + be the root dcb + + Ccb - Supplies the Ccb corresponding to the handle opening the source + file + +Return Value: + + NTSTATUS - The result of this operation if it completes without + an exception. + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + + PFILE_VALID_DATA_LENGTH_INFORMATION Buffer; + + ULONG NewValidDataLength; + BOOLEAN ResourceAcquired = FALSE; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( IrpContext ); + + DebugTrace(+1, Dbg, "FatSetValidDataLengthInfo...\n", 0); + + Buffer = Irp->AssociatedIrp.SystemBuffer; + + try { + + // + // User must have manage volume privilege to explicitly tweak the VDL + // + + if ((Ccb == NULL) || !FlagOn( Ccb->Flags, CCB_FLAG_MANAGE_VOLUME_ACCESS )) { + + try_return( Status = STATUS_INVALID_PARAMETER ); + } + + // + // Valid data length changes are only allowed on a file and not a directory + // + + if (NodeType(Fcb) != FAT_NTC_FCB) { + + DebugTrace(0, Dbg, "Cannot change VDL of a directory\n", 0); + + try_return( Status = STATUS_INVALID_DEVICE_REQUEST ); + } + + // + // Check that the new file size is legal + // + + + if (!FatIsIoRangeValid( Fcb->Vcb, Buffer->ValidDataLength, 0)) { + + DebugTrace(0, Dbg, "Illegal allocation size\n", 0); + + try_return( Status = STATUS_DISK_FULL ); + } + + + NewValidDataLength = Buffer->ValidDataLength.LowPart; + + // + // VDL can only move forward + // + + if ((NewValidDataLength < Fcb->Header.ValidDataLength.LowPart) || + (NewValidDataLength > Fcb->Header.FileSize.LowPart)) { + + try_return( Status = STATUS_INVALID_PARAMETER ); + } + + // + // We can't change the VDL without being able to purge. This should stay + // constant since we own everything exclusive + // + + if (!MmCanFileBeTruncated( FileObject->SectionObjectPointer, + &Buffer->ValidDataLength )) { + + try_return( Status = STATUS_USER_MAPPED_FILE ); + } + + // + // Flush old data out and purge the cache so we can see new data. + // + + if (FileObject->SectionObjectPointer->DataSectionObject != NULL) { + + ResourceAcquired = + ExAcquireResourceExclusiveLite( Fcb->Header.PagingIoResource, TRUE ); + + CcFlushCache( FileObject->SectionObjectPointer, + NULL, + 0, + &Irp->IoStatus ); + + if (!NT_SUCCESS( Irp->IoStatus.Status )) { + + try_return( Irp->IoStatus.Status ); + } + + CcPurgeCacheSection( FileObject->SectionObjectPointer, + NULL, + 0, + FALSE ); + } + + // + // Set the new ValidDataLength, Likewise ValidDataToDisk. + // + + Fcb->Header.ValidDataLength.LowPart = NewValidDataLength; + Fcb->ValidDataToDisk = NewValidDataLength; + + DebugTrace(0, Dbg, "New VDL is 0x%08lx.\n", NewValidDataLength); + + // + // We must now update the cache mapping. + // + + if (FileObject->SectionObjectPointer->SharedCacheMap != NULL) { + + CcSetFileSizes( FileObject, + (PCC_FILE_SIZES)&Fcb->Header.AllocationSize ); + } + + // + // Set this handle as having modified the file + // + + FileObject->Flags |= FO_FILE_MODIFIED; + + // + // Set our return status to success + // + + Status = STATUS_SUCCESS; + + try_exit: NOTHING; + + } finally { + + DebugUnwind( FatSetValidDataLengthInfo ); + + if (ResourceAcquired) { + + ExReleaseResourceLite( Fcb->Header.PagingIoResource ); + } + + DebugTrace(-1, Dbg, "FatSetValidDataLengthInfo -> %08lx\n", Status); + } + + return Status; +} + + + +// +// Internal Support Routine +// + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatRenameEAs ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN USHORT ExtendedAttributes, + IN POEM_STRING OldOemName + ) +{ + BOOLEAN LockedEaFcb = FALSE; + + PBCB EaBcb = NULL; + PDIRENT EaDirent; + EA_RANGE EaSetRange; + PEA_SET_HEADER EaSetHeader; + + PVCB Vcb; + + PAGED_CODE(); + + RtlZeroMemory( &EaSetRange, sizeof( EA_RANGE )); + + Vcb = Fcb->Vcb; + + try { + + // + // Use a try-except to catch any errors. + // + + try { + + + // + // Try to get the Ea file object. Return FALSE on failure. + // + + FatGetEaFile( IrpContext, + Vcb, + &EaDirent, + &EaBcb, + FALSE, + FALSE ); + + LockedEaFcb = TRUE; + + // + // If we didn't get the file because it doesn't exist, then the + // disk is corrupted. We do nothing here. + // + + if (Vcb->VirtualEaFile != NULL) { + + // + // Try to pin down the Ea set header for the index in the + // dirent. If the operation doesn't complete, return FALSE + // from this routine. + // + + FatReadEaSet( IrpContext, + Vcb, + ExtendedAttributes, + OldOemName, + FALSE, + &EaSetRange ); + + EaSetHeader = (PEA_SET_HEADER) EaSetRange.Data; + + // + // We now have the Ea set header for this file. We simply + // overwrite the owning file name. + // + + RtlZeroMemory( EaSetHeader->OwnerFileName, 14 ); + + RtlCopyMemory( EaSetHeader->OwnerFileName, + Fcb->ShortName.Name.Oem.Buffer, + Fcb->ShortName.Name.Oem.Length ); + + FatMarkEaRangeDirty( IrpContext, Vcb->VirtualEaFile, &EaSetRange ); + FatUnpinEaRange( IrpContext, &EaSetRange ); + + CcFlushCache( Vcb->VirtualEaFile->SectionObjectPointer, NULL, 0, NULL ); + } + + } except(FatExceptionFilter( IrpContext, GetExceptionInformation() )) { + + // + // We catch all exceptions that Fat catches, but don't do + // anything with them. + // + } + + } finally { + + // + // Unpin the EaDirent and the EaSetHeader if pinned. + // + + FatUnpinBcb( IrpContext, EaBcb ); + FatUnpinEaRange( IrpContext, &EaSetRange ); + + // + // Release the Fcb for the Ea file if locked. + // + + if (LockedEaFcb) { + + FatReleaseFcb( IrpContext, Vcb->EaFcb ); + } + } + + return; +} + diff --git a/filesys/fastfat/filobsup.c b/filesys/fastfat/filobsup.c new file mode 100644 index 000000000..2b492f6a9 --- /dev/null +++ b/filesys/fastfat/filobsup.c @@ -0,0 +1,579 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + FilObSup.c + +Abstract: + + This module implements the Fat File object support routines. + + +--*/ + +#include "FatProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (FAT_BUG_CHECK_FILOBSUP) + +// +// The debug trace level +// + +#define Dbg (DEBUG_TRACE_FILOBSUP) + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, FatForceCacheMiss) +#pragma alloc_text(PAGE, FatPurgeReferencedFileObjects) +#pragma alloc_text(PAGE, FatSetFileObject) +#pragma alloc_text(PAGE, FatDecodeFileObject) +#endif + + +VOID +FatSetFileObject ( + IN PFILE_OBJECT FileObject OPTIONAL, + IN TYPE_OF_OPEN TypeOfOpen, + IN PVOID VcbOrFcbOrDcb, + IN PCCB Ccb OPTIONAL + ) + +/*++ + +Routine Description: + + This routine sets the file system pointers within the file object + +Arguments: + + FileObject - Supplies a pointer to the file object being modified, and + can optionally be null. + + TypeOfOpen - Supplies the type of open denoted by the file object. + This is only used by this procedure for sanity checking. + + VcbOrFcbOrDcb - Supplies a pointer to either a vcb, fcb, or dcb + + Ccb - Optionally supplies a pointer to a ccb + +Return Value: + + None. + +--*/ + +{ + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatSetFileObject, FileObject = %p\n", FileObject ); + + NT_ASSERT((Ccb == NULL) || (NodeType(Ccb) == FAT_NTC_CCB)); + + + NT_ASSERT(((TypeOfOpen == UnopenedFileObject)) + + || + + ((TypeOfOpen == UserFileOpen) && + (NodeType(VcbOrFcbOrDcb) == FAT_NTC_FCB) && + (Ccb != NULL)) + + || + + ((TypeOfOpen == EaFile) && + (NodeType(VcbOrFcbOrDcb) == FAT_NTC_FCB) && + (Ccb == NULL)) + + || + + ((TypeOfOpen == UserDirectoryOpen) && + ((NodeType(VcbOrFcbOrDcb) == FAT_NTC_DCB) || (NodeType(VcbOrFcbOrDcb) == FAT_NTC_ROOT_DCB)) && + (Ccb != NULL)) + + || + + ((TypeOfOpen == UserVolumeOpen) && + (NodeType(VcbOrFcbOrDcb) == FAT_NTC_VCB) && + (Ccb != NULL)) + + || + + ((TypeOfOpen == VirtualVolumeFile) && + (NodeType(VcbOrFcbOrDcb) == FAT_NTC_VCB) && + (Ccb == NULL)) + + || + + ((TypeOfOpen == DirectoryFile) && + ((NodeType(VcbOrFcbOrDcb) == FAT_NTC_DCB) || (NodeType(VcbOrFcbOrDcb) == FAT_NTC_ROOT_DCB)) && + (Ccb == NULL)) + ); + + + UNREFERENCED_PARAMETER( TypeOfOpen ); + + // + // If we were given an Fcb, Dcb, or Vcb, we have some processing to do. + // + + NT_ASSERT((Ccb == NULL) || (NodeType(Ccb) == FAT_NTC_CCB)); + + if ( VcbOrFcbOrDcb != NULL ) { + + // + // Set the Vpb field in the file object, and if we were given an + // Fcb or Dcb move the field over to point to the nonpaged Fcb/Dcb + // + + if (NodeType(VcbOrFcbOrDcb) == FAT_NTC_VCB) { + + FileObject->Vpb = ((PVCB)VcbOrFcbOrDcb)->Vpb; + + } else { + + FileObject->Vpb = ((PFCB)VcbOrFcbOrDcb)->Vcb->Vpb; + + // + // If this is a temporary file, note it in the FcbState + // + + if (FlagOn(((PFCB)VcbOrFcbOrDcb)->FcbState, FCB_STATE_TEMPORARY)) { + + SetFlag(FileObject->Flags, FO_TEMPORARY_FILE); + } + } + } + + NT_ASSERT((Ccb == NULL) || (NodeType(Ccb) == FAT_NTC_CCB)); + + // + // Now set the fscontext fields of the file object + // + + if (ARGUMENT_PRESENT( FileObject )) { + + FileObject->FsContext = VcbOrFcbOrDcb; + FileObject->FsContext2 = Ccb; + } + + NT_ASSERT((Ccb == NULL) || (NodeType(Ccb) == FAT_NTC_CCB)); + + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "FatSetFileObject -> VOID\n", 0); + + return; +} + +TYPE_OF_OPEN +FatDecodeFileObject ( + _In_ PFILE_OBJECT FileObject, + _Outptr_ PVCB *Vcb, + _Outptr_ PFCB *FcbOrDcb, + _Outptr_ PCCB *Ccb + ) + +/*++ + +Routine Description: + + This procedure takes a pointer to a file object, that has already been + opened by the Fat file system and figures out what really is opened. + +Arguments: + + FileObject - Supplies the file object pointer being interrogated + + Vcb - Receives a pointer to the Vcb for the file object. + + FcbOrDcb - Receives a pointer to the Fcb/Dcb for the file object, if + one exists. + + Ccb - Receives a pointer to the Ccb for the file object, if one exists. + +Return Value: + + TYPE_OF_OPEN - returns the type of file denoted by the input file object. + + UserFileOpen - The FO represents a user's opened data file. + Ccb, FcbOrDcb, and Vcb are set. FcbOrDcb points to an Fcb. + + UserDirectoryOpen - The FO represents a user's opened directory. + Ccb, FcbOrDcb, and Vcb are set. FcbOrDcb points to a Dcb/RootDcb + + UserVolumeOpen - The FO represents a user's opened volume. + Ccb and Vcb are set. FcbOrDcb is null. + + VirtualVolumeFile - The FO represents the special virtual volume file. + Vcb is set, and Ccb and FcbOrDcb are null. + + DirectoryFile - The FO represents a special directory file. + Vcb and FcbOrDcb are set. Ccb is null. FcbOrDcb points to a + Dcb/RootDcb. + + EaFile - The FO represents an Ea Io stream file. + FcbOrDcb, and Vcb are set. FcbOrDcb points to an Fcb, and Ccb is + null. + +--*/ + +{ + TYPE_OF_OPEN TypeOfOpen; + PVOID FsContext; + PVOID FsContext2; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatDecodeFileObject, FileObject = %p\n", FileObject); + + // + // Reference the fs context fields of the file object, and zero out + // the out pointer parameters. + // + + FsContext = FileObject->FsContext; + FsContext2 = FileObject->FsContext2; + + // + // Special case the situation where FsContext is null + // + + if (FsContext == NULL) { + + *Ccb = NULL; + *FcbOrDcb = NULL; + *Vcb = NULL; + + TypeOfOpen = UnopenedFileObject; + + } else { + + // + // Now we can case on the node type code of the fscontext pointer + // and set the appropriate out pointers + // + + switch (NodeType(FsContext)) { + + case FAT_NTC_VCB: + + *Ccb = FsContext2; + *FcbOrDcb = NULL; + *Vcb = FsContext; + + TypeOfOpen = ( *Ccb == NULL ? VirtualVolumeFile : UserVolumeOpen ); + + break; + + case FAT_NTC_ROOT_DCB: + case FAT_NTC_DCB: + + *Ccb = FsContext2; + *FcbOrDcb = FsContext; + *Vcb = (*FcbOrDcb)->Vcb; + + TypeOfOpen = ( *Ccb == NULL ? DirectoryFile : UserDirectoryOpen ); + + DebugTrace(0, Dbg, "Referencing directory: %wZ\n", &(*FcbOrDcb)->FullFileName); + + break; + + case FAT_NTC_FCB: + + *Ccb = FsContext2; + *FcbOrDcb = FsContext; + *Vcb = (*FcbOrDcb)->Vcb; + + + if (*Ccb == NULL ) { + TypeOfOpen = EaFile; + DebugTrace(0, Dbg, "Referencing EA file: %wZ\n", &(*FcbOrDcb)->FullFileName); + } else { + if (((ULONG_PTR)*Ccb & 0x7) == 0x0) { + TypeOfOpen = UserFileOpen; + DebugTrace(0, Dbg, "Referencing file: %wZ\n", &(*FcbOrDcb)->FullFileName); + } + + + else { +#pragma prefast( suppress:28159, "things are seriously wrong if we get here" ) + FatBugCheck( NodeType(FsContext), 0, 0 ); + } + } + + break; + + default: + +#pragma prefast( suppress:28159, "things are seriously wrong if we get here" ) + FatBugCheck( NodeType(FsContext), 0, 0 ); + } + } + + // + // and return to our caller + // + + DebugTrace(0, Dbg, "FatDecodeFileObject -> VCB(%p)\n", *Vcb); + DebugTrace(0, Dbg, "FatDecodeFileObject -> FCB(%p)\n", *FcbOrDcb); + DebugTrace(0, Dbg, "FatDecodeFileObject -> CCB(%p)\n", *Ccb); + DebugTrace(-1, Dbg, "FatDecodeFileObject -> TypeOfOpen = %08lx\n", TypeOfOpen); + + return TypeOfOpen; +} + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatPurgeReferencedFileObjects ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN FAT_FLUSH_TYPE FlushType + ) + +/*++ + +Routine Description: + + This routine non-recursively walks from the given FcbOrDcb and trys + to force Cc or Mm to close any sections it may be holding on to. + +Arguments: + + Fcb - Supplies a pointer to either an fcb or a dcb + + FlushType - Specifies the kind of flushing to perform + +Return Value: + + None. + +--*/ + +{ + PFCB OriginalFcb = Fcb; + PFCB NextFcb; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatPurgeReferencedFileObjects, Fcb = %p\n", Fcb ); + + NT_ASSERT( FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) ); + + // + // First, if we have a delayed close, force it closed. + // + + FatFspClose(Fcb->Vcb); + + // + // Walk the directory tree forcing sections closed. + // + // Note that it very important to get the next node to visit before + // acting on the current node. This is because acting on a node may + // make it, and an arbitrary number of direct ancestors, vanish. + // Since we never visit ancestors in our top-down enumeration scheme, we + // can safely continue the enumeration even when the tree is vanishing + // beneath us. This is way cool. + // + + while ( Fcb != NULL ) { + + NextFcb = FatGetNextFcbTopDown(IrpContext, Fcb, OriginalFcb); + + // + // Check for the EA file fcb + // + + if ( !FlagOn(Fcb->DirentFatFlags, FAT_DIRENT_ATTR_VOLUME_ID) ) { + + FatForceCacheMiss( IrpContext, Fcb, FlushType ); + } + + Fcb = NextFcb; + } + + DebugTrace(-1, Dbg, "FatPurgeReferencedFileObjects (VOID)\n", 0 ); + + return; +} + + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatForceCacheMiss ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN FAT_FLUSH_TYPE FlushType + ) + +/*++ + +Routine Description: + + The following routine asks either Cc or Mm to get rid of any cached + pages on a file. Note that this will fail if a user has mapped a file. + + If there is a shared cache map, purge the cache section. Otherwise + we have to go and ask Mm to blow away the section. + + NOTE: This caller MUST own the Vcb exclusive. + +Arguments: + + Fcb - Supplies a pointer to an fcb + + FlushType - Specifies the kind of flushing to perform + +Return Value: + + None. + +--*/ + +{ + PVCB Vcb; + BOOLEAN ChildrenAcquired = FALSE; + + PAGED_CODE(); + + // + // If we can't wait, bail. + // + + NT_ASSERT( FatVcbAcquiredExclusive( IrpContext, Fcb->Vcb ) || + FlagOn( Fcb->Vcb->VcbState, VCB_STATE_FLAG_LOCKED ) ); + + if (!FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT)) { + + FatRaiseStatus( IrpContext, STATUS_CANT_WAIT ); + } + + // + // If we are purging a directory file object, we must acquire all the + // FCBs exclusive so that the parent directory is not being pinned. + // Careful, we can collide with something acquiring up the tree like + // an unpin repinned flush (FsRtlAcquireFileForCcFlush ...) of a parent + // dir on extending writethrough of a child file (oops). So get things + // going up the tree, not down. + // + + if ((NodeType(Fcb) != FAT_NTC_FCB) && + !IsListEmpty(&Fcb->Specific.Dcb.ParentDcbQueue)) { + + PLIST_ENTRY Links; + PFCB TempFcb; + + ChildrenAcquired = TRUE; + + for (Links = Fcb->Specific.Dcb.ParentDcbQueue.Flink; + Links != &Fcb->Specific.Dcb.ParentDcbQueue; + Links = Links->Flink) { + + TempFcb = CONTAINING_RECORD( Links, FCB, ParentDcbLinks ); + + (VOID)FatAcquireExclusiveFcb( IrpContext, TempFcb ); + } + } + + (VOID)FatAcquireExclusiveFcb( IrpContext, Fcb ); + + // + // We use this flag to indicate to a close beneath us that + // the Fcb resource should be freed before deleting the Fcb. + // + + Vcb = Fcb->Vcb; + + SetFlag( Fcb->FcbState, FCB_STATE_FORCE_MISS_IN_PROGRESS ); + + ClearFlag( Vcb->VcbState, VCB_STATE_FLAG_DELETED_FCB ); + + try { + + BOOLEAN DataSectionExists; + BOOLEAN ImageSectionExists; + + PSECTION_OBJECT_POINTERS Section; + + if ( FlushType ) { + + (VOID)FatFlushFile( IrpContext, Fcb, FlushType ); + } + + // + // The Flush may have made the Fcb go away + // + + if (!FlagOn(Vcb->VcbState, VCB_STATE_FLAG_DELETED_FCB)) { + + Section = &Fcb->NonPaged->SectionObjectPointers; + + DataSectionExists = (BOOLEAN)(Section->DataSectionObject != NULL); + ImageSectionExists = (BOOLEAN)(Section->ImageSectionObject != NULL); + + // + // Note, it is critical to do the Image section first as the + // purge of the data section may cause the image section to go + // away, but the opposite is not true. + // + + if (ImageSectionExists) { + + (VOID)MmFlushImageSection( Section, MmFlushForWrite ); + } + + if (DataSectionExists) { + + CcPurgeCacheSection( Section, NULL, 0, FALSE ); + } + } + + } finally { + + // + // If we purging a directory file object, release all the Fcb + // resources that we acquired above. The Dcb cannot have vanished + // if there were Fcbs underneath it, and the Fcbs couldn't have gone + // away since I own the Vcb. + // + + if (ChildrenAcquired) { + + PLIST_ENTRY Links; + PFCB TempFcb; + + for (Links = Fcb->Specific.Dcb.ParentDcbQueue.Flink; + Links != &Fcb->Specific.Dcb.ParentDcbQueue; + Links = Links->Flink) { + + TempFcb = CONTAINING_RECORD( Links, FCB, ParentDcbLinks ); + + FatReleaseFcb( IrpContext, TempFcb ); + } + } + + // + // Since we have the Vcb exclusive we know that if any closes + // come in it is because the CcPurgeCacheSection caused the + // Fcb to go away. Also in close, the Fcb was released + // before being freed. + // + + if ( !FlagOn(Vcb->VcbState, VCB_STATE_FLAG_DELETED_FCB) ) { + + ClearFlag( Fcb->FcbState, FCB_STATE_FORCE_MISS_IN_PROGRESS ); + + FatReleaseFcb( (IRPCONTEXT), Fcb ); + } + } +} + + diff --git a/filesys/fastfat/flush.c b/filesys/fastfat/flush.c new file mode 100644 index 000000000..f551c1183 --- /dev/null +++ b/filesys/fastfat/flush.c @@ -0,0 +1,1353 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + Flush.c + +Abstract: + + This module implements the File Flush buffers routine for Fat called by the + dispatch driver. + + +--*/ + +#include "FatProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (FAT_BUG_CHECK_FLUSH) + +// +// The local debug trace level +// + +#define Dbg (DEBUG_TRACE_FLUSH) + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, FatCommonFlushBuffers) +#pragma alloc_text(PAGE, FatFlushDirectory) +#pragma alloc_text(PAGE, FatFlushFat) +#pragma alloc_text(PAGE, FatFlushFile) +#pragma alloc_text(PAGE, FatFlushVolume) +#pragma alloc_text(PAGE, FatFsdFlushBuffers) +#pragma alloc_text(PAGE, FatFlushDirentForFile) +#pragma alloc_text(PAGE, FatFlushFatEntries) +#pragma alloc_text(PAGE, FatHijackIrpAndFlushDevice) +#endif + +// +// Local procedure prototypes +// + +IO_COMPLETION_ROUTINE FatFlushCompletionRoutine; + +NTSTATUS +FatFlushCompletionRoutine ( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PIRP Irp, + _In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt + ); + +IO_COMPLETION_ROUTINE FatHijackCompletionRoutine; + +NTSTATUS +FatHijackCompletionRoutine ( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PIRP Irp, + _In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt + ); + + +_Function_class_(IRP_MJ_FLUSH_BUFFERS) +_Function_class_(DRIVER_DISPATCH) +NTSTATUS +FatFsdFlushBuffers ( + _In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + _Inout_ PIRP Irp + ) + +/*++ + +Routine Description: + + This routine implements the FSD part of Flush buffers. + +Arguments: + + VolumeDeviceObject - Supplies the volume device object where the + file being flushed exists + + Irp - Supplies the Irp being processed + +Return Value: + + NTSTATUS - The FSD status for the IRP + +--*/ + +{ + NTSTATUS Status; + PIRP_CONTEXT IrpContext = NULL; + + BOOLEAN TopLevel; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatFsdFlushBuffers\n", 0); + + // + // Call the common Cleanup routine, with blocking allowed if synchronous + // + + FsRtlEnterFileSystem(); + + TopLevel = FatIsIrpTopLevel( Irp ); + + try { + + IrpContext = FatCreateIrpContext( Irp, CanFsdWait( Irp ) ); + + Status = FatCommonFlushBuffers( IrpContext, Irp ); + + } except(FatExceptionFilter( IrpContext, GetExceptionInformation() )) { + + // + // We had some trouble trying to perform the requested + // operation, so we'll abort the I/O request with + // the error status that we get back from the + // execption code + // + + Status = FatProcessException( IrpContext, Irp, GetExceptionCode() ); + } + + if (TopLevel) { IoSetTopLevelIrp( NULL ); } + + FsRtlExitFileSystem(); + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "FatFsdFlushBuffers -> %08lx\n", Status); + + UNREFERENCED_PARAMETER( VolumeDeviceObject ); + + return Status; +} + + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatCommonFlushBuffers ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the common routine for flushing a buffer. + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status; + + PIO_STACK_LOCATION IrpSp; + + PFILE_OBJECT FileObject; + + TYPE_OF_OPEN TypeOfOpen; + PVCB Vcb; + PFCB Fcb; + PFCB NextFcb; + PCCB Ccb; + + BOOLEAN VcbAcquired = FALSE; + BOOLEAN FcbAcquired = FALSE; + BOOLEAN FatFlushRequired = FALSE; + + PAGED_CODE(); + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "FatCommonFlushBuffers\n", 0); + DebugTrace( 0, Dbg, "Irp = %p\n", Irp); + DebugTrace( 0, Dbg, "->FileObject = %p\n", IrpSp->FileObject); + + // + // Extract and decode the file object + // + + FileObject = IrpSp->FileObject; + TypeOfOpen = FatDecodeFileObject( FileObject, &Vcb, &Fcb, &Ccb ); + + // + // CcFlushCache is always synchronous, so if we can't wait enqueue + // the irp to the Fsp. + // + + if ( !FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) ) { + + Status = FatFsdPostRequest( IrpContext, Irp ); + + DebugTrace(-1, Dbg, "FatCommonFlushBuffers -> %08lx\n", Status ); + return Status; + } + + Status = STATUS_SUCCESS; + + try { + +#if (NTDDI_VERSION >= NTDDI_WIN8) + + if (FatDiskAccountingEnabled) { + + PETHREAD OriginatingThread = NULL; + + // + // Charge the flush to the originating thread. + // Try the Thread in Irp's tail first, if that is NULL, then charge + // the flush to current thread. + // + + if ((Irp->Tail.Overlay.Thread != NULL) && + !IoIsSystemThread( Irp->Tail.Overlay.Thread )) { + + OriginatingThread = Irp->Tail.Overlay.Thread; + + } else { + + OriginatingThread = PsGetCurrentThread(); + } + + NT_ASSERT( OriginatingThread != NULL ); + + PsUpdateDiskCounters( PsGetThreadProcess( OriginatingThread ), + 0, + 0, + 0, + 0, + 1 ); + } + +#endif + + // + // Case on the type of open that we are trying to flush + // + + switch (TypeOfOpen) { + + case VirtualVolumeFile: + case EaFile: + case DirectoryFile: + DebugTrace(0, Dbg, "Flush that does nothing\n", 0); + break; + + case UserFileOpen: + + DebugTrace(0, Dbg, "Flush User File Open\n", 0); + + (VOID)FatAcquireExclusiveFcb( IrpContext, Fcb ); + + FcbAcquired = TRUE; + + FatVerifyFcb( IrpContext, Fcb ); + + // + // If the file is cached then flush its cache + // + + Status = FatFlushFile( IrpContext, Fcb, Flush ); + + // + // Also flush the file's dirent in the parent directory if the file + // flush worked. + // + + if (NT_SUCCESS( Status )) { + + // + // Insure that we get the filesize to disk correctly. This is + // benign if it was already good. + // + + SetFlag(FileObject->Flags, FO_FILE_SIZE_CHANGED); + + + FatUpdateDirentFromFcb( IrpContext, FileObject, Fcb, Ccb ); + + if (FlagOn(Fcb->FcbState, FCB_STATE_FLUSH_FAT)) { + + FatFlushRequired = TRUE; + } + + // + // Flush the parent Dcb's to get any dirent updates to disk. + // + + NextFcb = Fcb->ParentDcb; + + while (NextFcb != NULL) { + + // + // Make sure the Fcb is OK. + // + + try { + + FatVerifyFcb( IrpContext, NextFcb ); + + } except( FsRtlIsNtstatusExpected(GetExceptionCode()) ? + EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) { + + FatResetExceptionState( IrpContext ); + } + + if (NextFcb->FcbCondition == FcbGood) { + + NTSTATUS LocalStatus; + + LocalStatus = FatFlushFile( IrpContext, NextFcb, Flush ); + + if (!NT_SUCCESS(LocalStatus)) { + + Status = LocalStatus; + } + + if (FlagOn(NextFcb->FcbState, FCB_STATE_FLUSH_FAT)) { + + FatFlushRequired = TRUE; + } + } + + NextFcb = NextFcb->ParentDcb; + } + + // + // Flush the volume file to get any allocation information + // updates to disk. + // + + if (FatFlushRequired) { + + Status = FatFlushFat( IrpContext, Vcb ); + + ClearFlag(Fcb->FcbState, FCB_STATE_FLUSH_FAT); + } + + // + // Set the write through bit so that these modifications + // will be completed with the request. + // + + SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WRITE_THROUGH); + } + + break; + + case UserDirectoryOpen: + + // + // If the user had opened the root directory then we'll + // oblige by flushing the volume. + // + + if (NodeType(Fcb) != FAT_NTC_ROOT_DCB) { + + DebugTrace(0, Dbg, "Flush a directory does nothing\n", 0); + break; + } + + case UserVolumeOpen: + + DebugTrace(0, Dbg, "Flush User Volume Open, or root dcb\n", 0); + + // + // Acquire exclusive access to the Vcb. + // + + { + BOOLEAN Finished; +#pragma prefast( suppress:28931, "needed for debug build" ) + Finished = FatAcquireExclusiveVcb( IrpContext, Vcb ); + NT_ASSERT( Finished ); + } + + VcbAcquired = TRUE; + + // + // Mark the volume clean and then flush the volume file, + // and then all directories + // + + Status = FatFlushVolume( IrpContext, Vcb, Flush ); + + // + // If the volume was dirty, do the processing that the delayed + // callback would have done. + // + + if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DIRTY)) { + + // + // Cancel any pending clean volumes. + // + + (VOID)KeCancelTimer( &Vcb->CleanVolumeTimer ); + (VOID)KeRemoveQueueDpc( &Vcb->CleanVolumeDpc ); + + // + // The volume is now clean, note it. + // + + if (!FlagOn(Vcb->VcbState, VCB_STATE_FLAG_MOUNTED_DIRTY)) { + + FatMarkVolume( IrpContext, Vcb, VolumeClean ); + ClearFlag( Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DIRTY ); + } + + // + // Unlock the volume if it is removable. + // + + if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA) && + !FlagOn(Vcb->VcbState, VCB_STATE_FLAG_BOOT_OR_PAGING_FILE)) { + + FatToggleMediaEjectDisable( IrpContext, Vcb, FALSE ); + } + } + + break; + + default: + +#pragma prefast( suppress:28159, "things are seriously wrong if we get here" ) + FatBugCheck( TypeOfOpen, 0, 0 ); + } + + FatUnpinRepinnedBcbs( IrpContext ); + + } finally { + + DebugUnwind( FatCommonFlushBuffers ); + + if (VcbAcquired) { FatReleaseVcb( IrpContext, Vcb ); } + + if (FcbAcquired) { FatReleaseFcb( IrpContext, Fcb ); } + + // + // If this is a normal termination then pass the request on + // to the target device object. + // + + if (!AbnormalTermination()) { + +#if (NTDDI_VERSION >= NTDDI_WIN8) + if ((IrpSp->MinorFunction != IRP_MN_FLUSH_DATA_ONLY) && + (IrpSp->MinorFunction != IRP_MN_FLUSH_NO_SYNC)) { +#endif + + NTSTATUS DriverStatus; + + // + // Get the next stack location, and copy over the stack location + // + + IoCopyCurrentIrpStackLocationToNext(Irp); + + // + // Set up the completion routine + // + + IoSetCompletionRoutine( Irp, + FatFlushCompletionRoutine, + ULongToPtr( Status ), + TRUE, + TRUE, + TRUE ); + + // + // Send the request. + // + + DriverStatus = IoCallDriver(Vcb->TargetDeviceObject, Irp); + + if ((DriverStatus == STATUS_PENDING) || + (!NT_SUCCESS(DriverStatus) && + (DriverStatus != STATUS_INVALID_DEVICE_REQUEST))) { + + Status = DriverStatus; + } + + Irp = NULL; + +#if (NTDDI_VERSION >= NTDDI_WIN8) + } +#endif + + // + // Complete the Irp if necessary and return to the caller. + // + + FatCompleteRequest( IrpContext, Irp, Status ); + + } + + DebugTrace(-1, Dbg, "FatCommonFlushBuffers -> %08lx\n", Status); + } + + return Status; +} + + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatFlushDirectory ( + IN PIRP_CONTEXT IrpContext, + IN PDCB Dcb, + IN FAT_FLUSH_TYPE FlushType + ) + +/*++ + +Routine Description: + + This routine non-recursively flushes a dcb tree. + +Arguments: + + Dcb - Supplies the Dcb being flushed + + FlushType - Specifies the kind of flushing to perform + +Return Value: + + VOID + +--*/ + +{ + PFCB Fcb; + PVCB Vcb; + PFCB NextFcb; + + PDIRENT Dirent; + PBCB DirentBcb = NULL; + + NTSTATUS Status; + NTSTATUS ReturnStatus = STATUS_SUCCESS; + + BOOLEAN ClearWriteThroughOnExit = FALSE; + BOOLEAN ClearWaitOnExit = FALSE; + + PAGED_CODE(); + + NT_ASSERT( FatVcbAcquiredExclusive(IrpContext, Dcb->Vcb) ); + + DebugTrace(+1, Dbg, "FatFlushDirectory, Dcb = %p\n", Dcb); + + // + // First flush all the files, then the directories, to make sure all the + // file sizes and times get sets correctly on disk. + // + // We also have to check here if the "Ea Data. Sf" fcb really + // corressponds to an existing file. + // + + if (!FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WRITE_THROUGH)) { + + ClearWriteThroughOnExit = TRUE; + SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WRITE_THROUGH); + } + + if (!FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT)) { + + ClearWaitOnExit = TRUE; + SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT); + } + + Vcb = Dcb->Vcb; + Fcb = Dcb; + + while (Fcb != NULL) { + + NextFcb = FatGetNextFcbTopDown(IrpContext, Fcb, Dcb); + + if ( (NodeType( Fcb ) == FAT_NTC_FCB) && + (Vcb->EaFcb != Fcb) && + !IsFileDeleted(IrpContext, Fcb)) { + + (VOID)FatAcquireExclusiveFcb( IrpContext, Fcb ); + + ClearFlag( Vcb->VcbState, VCB_STATE_FLAG_DELETED_FCB ); + + // + // Exception handler to catch and commute errors encountered + // doing the flush dance. We may encounter corruption, and + // should continue flushing the volume as much as possible. + // + + try { + + // + // Standard handler to release resources, etc. + // + + try { + + // + // Make sure the Fcb is OK. + // + + try { + + FatVerifyFcb( IrpContext, Fcb ); + + } except( FsRtlIsNtstatusExpected(GetExceptionCode()) ? + EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) { + + FatResetExceptionState( IrpContext ); + } + + // + // If this Fcb is not good skip it. Note that a 'continue' + // here would be very expensive as we inside a try{} body. + // + + if (Fcb->FcbCondition != FcbGood) { + + try_leave( NOTHING); + } + + // + // In case a handle was never closed and the FS and AS are more + // than a cluster different, do this truncate. + // + + if ( FlagOn(Fcb->FcbState, FCB_STATE_TRUNCATE_ON_CLOSE) ) { + + FatTruncateFileAllocation( IrpContext, + Fcb, + Fcb->Header.FileSize.LowPart, + FALSE ); + } + + // + // Also compare the file's dirent in the parent directory + // with the size information in the Fcb and update + // it if neccessary. Note that we don't mark the Bcb dirty + // because we will be flushing the file object presently, and + // Mm knows what's really dirty. + // + + FatGetDirentFromFcbOrDcb( IrpContext, + Fcb, + FALSE, + &Dirent, + &DirentBcb ); + + if (Dirent->FileSize != Fcb->Header.FileSize.LowPart) { + + Dirent->FileSize = Fcb->Header.FileSize.LowPart; + } + + + // + // We must unpin the Bcb before the flush since we recursively tear up + // the tree if Mm decides that the data section is no longer referenced + // and the final close comes in for this file. If this parent has no + // more children as a result, we will try to initiate teardown on it + // and Cc will deadlock against the active count of this Bcb. + // + + FatUnpinBcb( IrpContext, DirentBcb ); + + // + // Now flush the file. Note that this may make the Fcb + // go away if Mm dereferences its file object. + // + + Status = FatFlushFile( IrpContext, Fcb, FlushType ); + + if (!NT_SUCCESS(Status)) { + + ReturnStatus = Status; + } + + } finally { + + FatUnpinBcb( IrpContext, DirentBcb ); + + // + // Since we have the Vcb exclusive we know that if any closes + // come in it is because the CcPurgeCacheSection caused the + // Fcb to go away. + // + + if ( !FlagOn(Vcb->VcbState, VCB_STATE_FLAG_DELETED_FCB) ) { + + FatReleaseFcb( (IRPCONTEXT), Fcb ); + } + } + } except( (FsRtlIsNtstatusExpected( ReturnStatus = GetExceptionCode() ) != 0 ) ? + EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) { + FatResetExceptionState( IrpContext ); + } + + } + + Fcb = NextFcb; + } + + // + // OK, now flush the directories. + // + + Fcb = Dcb; + + while (Fcb != NULL) { + + NextFcb = FatGetNextFcbTopDown(IrpContext, Fcb, Dcb); + + if ( (NodeType( Fcb ) != FAT_NTC_FCB) && + !IsFileDeleted(IrpContext, Fcb) ) { + + // + // Make sure the Fcb is OK. + // + + try { + + FatVerifyFcb( IrpContext, Fcb ); + + } except( FsRtlIsNtstatusExpected(GetExceptionCode()) ? + EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) { + + FatResetExceptionState( IrpContext ); + } + + if (Fcb->FcbCondition == FcbGood) { + + Status = FatFlushFile( IrpContext, Fcb, FlushType ); + + if (!NT_SUCCESS(Status)) { + + ReturnStatus = Status; + } + } + } + + Fcb = NextFcb; + } + + try { + + FatUnpinRepinnedBcbs( IrpContext ); + + } except(FatExceptionFilter( IrpContext, GetExceptionInformation() )) { + + ReturnStatus = IrpContext->ExceptionStatus; + } + + if (ClearWriteThroughOnExit) { + + ClearFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WRITE_THROUGH); + } + if (ClearWaitOnExit) { + + ClearFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT); + } + + DebugTrace(-1, Dbg, "FatFlushDirectory -> 0x%08lx\n", ReturnStatus); + + return ReturnStatus; +} + + +NTSTATUS +FatFlushFat ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb + ) + +/*++ + +Routine Description: + + The function carefully flushes the entire FAT for a volume. It is + nessecary to dance around a bit because of complicated synchronization + reasons. + +Arguments: + + Vcb - Supplies the Vcb whose FAT is being flushed + +Return Value: + + VOID + +--*/ + +{ + PBCB Bcb; + PVOID DontCare; + IO_STATUS_BLOCK Iosb; + LARGE_INTEGER Offset; + + NTSTATUS ReturnStatus = STATUS_SUCCESS; + + PAGED_CODE(); + + // + // If this volume is write protected, no need to flush. + // + + if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED)) { + + return STATUS_SUCCESS; + } + + // + // Make sure the Vcb is OK. + // + + try { + + FatVerifyVcb( IrpContext, Vcb ); + + } except( FsRtlIsNtstatusExpected(GetExceptionCode()) ? + EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) { + + FatResetExceptionState( IrpContext ); + } + + if (Vcb->VcbCondition != VcbGood) { + + return STATUS_FILE_INVALID; + } + + // + // The only way we have to correctly synchronize things is to + // repin stuff, and then unpin repin it. + // + // With NT 5.0, we can use some new cache manager support to make + // this a lot more efficient (important for FAT32). Since we're + // only worried about ranges that are dirty - and since we're a + // modified-no-write stream - we can assume that if there is no + // BCB, there is no work to do in the range. I.e., the lazy writer + // beat us to it. + // + // This is much better than reading the entire FAT in and trying + // to punch it out (see the test in the write path to blow + // off writes that don't correspond to dirty ranges of the FAT). + // For FAT32, this would be a *lot* of reading. + // + + if (Vcb->AllocationSupport.FatIndexBitSize != 12) { + + // + // Walk through the Fat, one page at a time. + // + + ULONG NumberOfPages; + ULONG Page; + + NumberOfPages = ( FatReservedBytes(&Vcb->Bpb) + + FatBytesPerFat(&Vcb->Bpb) + + (PAGE_SIZE - 1) ) / PAGE_SIZE; + + + for ( Page = 0, Offset.QuadPart = 0; + Page < NumberOfPages; + Page++, Offset.LowPart += PAGE_SIZE ) { + + try { + + if (CcPinRead( Vcb->VirtualVolumeFile, + &Offset, + PAGE_SIZE, + PIN_WAIT | PIN_IF_BCB, + &Bcb, + &DontCare )) { + + CcSetDirtyPinnedData( Bcb, NULL ); + CcRepinBcb( Bcb ); + CcUnpinData( Bcb ); + CcUnpinRepinnedBcb( Bcb, TRUE, &Iosb ); + + if (!NT_SUCCESS(Iosb.Status)) { + + ReturnStatus = Iosb.Status; + } + } + + } except(FatExceptionFilter(IrpContext, GetExceptionInformation())) { + + ReturnStatus = IrpContext->ExceptionStatus; + continue; + } + } + + } else { + + // + // We read in the entire fat in the 12 bit case. + // + + Offset.QuadPart = FatReservedBytes( &Vcb->Bpb ); + + try { + + if (CcPinRead( Vcb->VirtualVolumeFile, + &Offset, + FatBytesPerFat( &Vcb->Bpb ), + PIN_WAIT | PIN_IF_BCB, + &Bcb, + &DontCare )) { + + CcSetDirtyPinnedData( Bcb, NULL ); + CcRepinBcb( Bcb ); + CcUnpinData( Bcb ); + CcUnpinRepinnedBcb( Bcb, TRUE, &Iosb ); + + if (!NT_SUCCESS(Iosb.Status)) { + + ReturnStatus = Iosb.Status; + } + } + + } except(FatExceptionFilter(IrpContext, GetExceptionInformation())) { + + ReturnStatus = IrpContext->ExceptionStatus; + } + } + + return ReturnStatus; +} + + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatFlushVolume ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN FAT_FLUSH_TYPE FlushType + ) + +/*++ + +Routine Description: + + The following routine is used to flush a volume to disk, including the + volume file, and ea file. + +Arguments: + + Vcb - Supplies the volume being flushed + + FlushType - Specifies the kind of flushing to perform + +Return Value: + + NTSTATUS - The Status from the flush. + +--*/ + +{ + NTSTATUS Status; + NTSTATUS ReturnStatus = STATUS_SUCCESS; + + PAGED_CODE(); + + // + // If this volume is write protected, no need to flush. + // + + if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED)) { + + return STATUS_SUCCESS; + } + + // + // Flush all the files and directories. + // + + Status = FatFlushDirectory( IrpContext, Vcb->RootDcb, FlushType ); + + if (!NT_SUCCESS(Status)) { + + ReturnStatus = Status; + } + + // + // Now Flush the FAT + // + + Status = FatFlushFat( IrpContext, Vcb ); + + if (!NT_SUCCESS(Status)) { + + ReturnStatus = Status; + } + + // + // Unlock the volume if it is removable. + // + + if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA) && + !FlagOn(Vcb->VcbState, VCB_STATE_FLAG_BOOT_OR_PAGING_FILE)) { + + FatToggleMediaEjectDisable( IrpContext, Vcb, FALSE ); + } + + return ReturnStatus; +} + + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatFlushFile ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN FAT_FLUSH_TYPE FlushType + ) + +/*++ + +Routine Description: + + This routine simply flushes the data section on a file. + +Arguments: + + Fcb - Supplies the file being flushed + + FlushType - Specifies the kind of flushing to perform + +Return Value: + + NTSTATUS - The Status from the flush. + +--*/ + +{ + IO_STATUS_BLOCK Iosb; + PVCB Vcb = Fcb->Vcb; + + PAGED_CODE(); + + CcFlushCache( &Fcb->NonPaged->SectionObjectPointers, NULL, 0, &Iosb ); + + + if ( !FlagOn( Vcb->VcbState, VCB_STATE_FLAG_DELETED_FCB )) { + + // + // Grab and release PagingIo to serialize ourselves with the lazy writer. + // This will work to ensure that all IO has completed on the cached + // data. + // + // If we are to invalidate the file, now is the right time to do it. Do + // it non-recursively so we don't thump children before their time. + // + + ExAcquireResourceExclusiveLite( Fcb->Header.PagingIoResource, TRUE); + + if (FlushType == FlushAndInvalidate) { + + FatMarkFcbCondition( IrpContext, Fcb, FcbBad, FALSE ); + } + + ExReleaseResourceLite( Fcb->Header.PagingIoResource ); + } + + return Iosb.Status; +} + + +NTSTATUS +FatHijackIrpAndFlushDevice ( + IN PIRP_CONTEXT IrpContext, + IN 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 + // + + IoCopyCurrentIrpStackLocationToNext(Irp); + + NextIrpSp = IoGetNextIrpStackLocation( Irp ); + NextIrpSp->MajorFunction = IRP_MJ_FLUSH_BUFFERS; + NextIrpSp->MinorFunction = 0; + + // + // Set up the completion routine + // + + KeInitializeEvent( &Event, NotificationEvent, FALSE ); + + IoSetCompletionRoutine( Irp, + FatHijackCompletionRoutine, + &Event, + TRUE, + TRUE, + TRUE ); + + // + // Send the request. + // + + Status = IoCallDriver( TargetDeviceObject, Irp ); + + if (Status == STATUS_PENDING) { + + 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; +} + + +VOID +FatFlushFatEntries ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN ULONG Cluster, + IN ULONG Count +) + +/*++ + +Routine Description: + + This macro flushes the FAT page(s) containing the passed in run. + +Arguments: + + Vcb - Supplies the volume being flushed + + Cluster - The starting cluster + + Count - The number of FAT entries in the run + +Return Value: + + VOID + +--*/ + +{ + ULONG ByteCount; + LARGE_INTEGER FileOffset; + + IO_STATUS_BLOCK Iosb; + + PAGED_CODE(); + + FileOffset.HighPart = 0; + FileOffset.LowPart = FatReservedBytes( &Vcb->Bpb ); + + if (Vcb->AllocationSupport.FatIndexBitSize == 12) { + + FileOffset.LowPart += Cluster * 3 / 2; + ByteCount = (Count * 3 / 2) + 1; + + } else if (Vcb->AllocationSupport.FatIndexBitSize == 32) { + + FileOffset.LowPart += Cluster * sizeof(ULONG); + ByteCount = Count * sizeof(ULONG); + + } else { + + FileOffset.LowPart += Cluster * sizeof( USHORT ); + ByteCount = Count * sizeof( USHORT ); + + } + + CcFlushCache( &Vcb->SectionObjectPointers, + &FileOffset, + ByteCount, + &Iosb ); + + if (NT_SUCCESS(Iosb.Status)) { + Iosb.Status = FatHijackIrpAndFlushDevice( IrpContext, + IrpContext->OriginatingIrp, + Vcb->TargetDeviceObject ); + } + + if (!NT_SUCCESS(Iosb.Status)) { + FatNormalizeAndRaiseStatus(IrpContext, Iosb.Status); + } +} + + +VOID +FatFlushDirentForFile ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb +) + +/*++ + +Routine Description: + + This macro flushes the page containing a file's DIRENT in its parent. + +Arguments: + + Fcb - Supplies the file whose DIRENT is being flushed + +Return Value: + + VOID + +--*/ + +{ + LARGE_INTEGER FileOffset; + IO_STATUS_BLOCK Iosb; + + PAGED_CODE(); + + FileOffset.QuadPart = Fcb->DirentOffsetWithinDirectory; + + CcFlushCache( &Fcb->ParentDcb->NonPaged->SectionObjectPointers, + &FileOffset, + sizeof( DIRENT ), + &Iosb ); + + if (NT_SUCCESS(Iosb.Status)) { + Iosb.Status = FatHijackIrpAndFlushDevice( IrpContext, + IrpContext->OriginatingIrp, + Fcb->Vcb->TargetDeviceObject ); + } + + if (!NT_SUCCESS(Iosb.Status)) { + FatNormalizeAndRaiseStatus(IrpContext, Iosb.Status); + } +} + + +// +// Local support routine +// + +NTSTATUS +FatFlushCompletionRoutine ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Contxt + ) + +{ + NTSTATUS Status = (NTSTATUS) (ULONG_PTR) Contxt; + + if ( Irp->PendingReturned ) { + + IoMarkIrpPending( Irp ); + } + + // + // If the Irp got STATUS_INVALID_DEVICE_REQUEST, normalize it + // to STATUS_SUCCESS. + // + + if (NT_SUCCESS(Irp->IoStatus.Status) || + (Irp->IoStatus.Status == STATUS_INVALID_DEVICE_REQUEST)) { + + Irp->IoStatus.Status = Status; + } + + UNREFERENCED_PARAMETER( DeviceObject ); + UNREFERENCED_PARAMETER( Contxt ); + + return STATUS_SUCCESS; +} + +// +// Local support routine +// + +NTSTATUS +FatHijackCompletionRoutine ( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PIRP Irp, + _In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt + ) + +{ + // + // Set the event so that our call will wake up. + // + + KeSetEvent( (PKEVENT)Contxt, 0, FALSE ); + + UNREFERENCED_PARAMETER( DeviceObject ); + UNREFERENCED_PARAMETER( Irp ); + + return STATUS_MORE_PROCESSING_REQUIRED; +} + diff --git a/filesys/fastfat/fsctrl.c b/filesys/fastfat/fsctrl.c new file mode 100644 index 000000000..13ef86223 --- /dev/null +++ b/filesys/fastfat/fsctrl.c @@ -0,0 +1,8039 @@ +/*++ + + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + FsCtrl.c + +Abstract: + + This module implements the File System Control routines for Fat called + by the dispatch driver. + + +--*/ + +#include "FatProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (FAT_BUG_CHECK_FSCTRL) + +// +// The local debug trace level +// + +#define Dbg (DEBUG_TRACE_FSCTRL) + +// +// Local procedure prototypes +// + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatMountVolume ( + IN PIRP_CONTEXT IrpContext, + IN PDEVICE_OBJECT TargetDeviceObject, + IN PVPB Vpb, + IN PDEVICE_OBJECT FsDeviceObject + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatVerifyVolume ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +BOOLEAN +FatIsMediaWriteProtected ( + IN PIRP_CONTEXT IrpContext, + IN PDEVICE_OBJECT TargetDeviceObject + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatUserFsCtrl ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatOplockRequest ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PIRP Irp + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatLockVolume ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +NTSTATUS +FatUnlockVolume ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatDismountVolume ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatDirtyVolume ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +NTSTATUS +FatIsVolumeDirty ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +NTSTATUS +FatIsVolumeMounted ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +NTSTATUS +FatIsPathnameValid ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatInvalidateVolumes ( + IN PIRP Irp + ); + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatScanForDismountedVcb ( + IN PIRP_CONTEXT IrpContext + ); + +BOOLEAN +FatPerformVerifyDiskRead ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PVOID Buffer, + IN LBO Lbo, + IN ULONG NumberOfBytesToRead, + IN BOOLEAN ReturnOnError + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatQueryRetrievalPointers ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +NTSTATUS +FatQueryBpb ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +NTSTATUS +FatGetStatistics ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +NTSTATUS +FatAllowExtendedDasdIo ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatGetBootAreaInfo ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PIRP Irp + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatGetRetrievalPointerBase ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PIRP Irp + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatMarkHandle( + _In_ PIRP_CONTEXT IrpContext, + _In_ PIRP Irp + ); + +NTSTATUS +FatSetZeroOnDeallocate ( + __in PIRP_CONTEXT IrpContext, + __in PIRP Irp + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatSetPurgeFailureMode ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PIRP Irp + ); + +// +// Local support routine prototypes +// + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatGetVolumeBitmap ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatGetRetrievalPointers ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatMoveFileNeedsWriteThrough ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PFCB FcbOrDcb, + _In_ ULONG OldWriteThroughFlags + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatMoveFile ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +VOID +FatComputeMoveFileSplicePoints ( + PIRP_CONTEXT IrpContext, + PFCB FcbOrDcb, + ULONG FileOffset, + ULONG TargetCluster, + ULONG BytesToReallocate, + PULONG FirstSpliceSourceCluster, + PULONG FirstSpliceTargetCluster, + PULONG SecondSpliceSourceCluster, + PULONG SecondSpliceTargetCluster, + PLARGE_MCB SourceMcb +); + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatComputeMoveFileParameter ( + IN PIRP_CONTEXT IrpContext, + IN PFCB FcbOrDcb, + IN ULONG BufferSize, + IN ULONG FileOffset, + IN OUT PULONG ByteCount, + OUT PULONG BytesToReallocate, + OUT PULONG BytesToWrite, + OUT PLARGE_INTEGER SourceLbo +); + +NTSTATUS +FatSearchBufferForLabel( + IN PIRP_CONTEXT IrpContext, + IN PVPB Vpb, + IN PVOID Buffer, + IN ULONG Size, + OUT PBOOLEAN LabelFound +); + +VOID +FatVerifyLookupFatEntry ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN ULONG FatIndex, + IN OUT PULONG FatEntry + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, FatAddMcbEntry) +#pragma alloc_text(PAGE, FatAllowExtendedDasdIo) +#pragma alloc_text(PAGE, FatCommonFileSystemControl) +#pragma alloc_text(PAGE, FatComputeMoveFileParameter) +#pragma alloc_text(PAGE, FatComputeMoveFileSplicePoints) +#pragma alloc_text(PAGE, FatDirtyVolume) +#pragma alloc_text(PAGE, FatFsdFileSystemControl) +#pragma alloc_text(PAGE, FatGetRetrievalPointerBase) +#pragma alloc_text(PAGE, FatGetBootAreaInfo) +#pragma alloc_text(PAGE, FatMarkHandle) +#pragma alloc_text(PAGE, FatGetRetrievalPointers) +#pragma alloc_text(PAGE, FatGetStatistics) +#pragma alloc_text(PAGE, FatGetVolumeBitmap) +#pragma alloc_text(PAGE, FatIsMediaWriteProtected) +#pragma alloc_text(PAGE, FatIsPathnameValid) +#pragma alloc_text(PAGE, FatIsVolumeDirty) +#pragma alloc_text(PAGE, FatIsVolumeMounted) +#pragma alloc_text(PAGE, FatLockVolume) +#pragma alloc_text(PAGE, FatLookupLastMcbEntry) +#pragma alloc_text(PAGE, FatGetNextMcbEntry) +#pragma alloc_text(PAGE, FatMountVolume) +#pragma alloc_text(PAGE, FatMoveFileNeedsWriteThrough) +#pragma alloc_text(PAGE, FatMoveFile) +#pragma alloc_text(PAGE, FatOplockRequest) +#pragma alloc_text(PAGE, FatPerformVerifyDiskRead) +#pragma alloc_text(PAGE, FatQueryBpb) +#pragma alloc_text(PAGE, FatQueryRetrievalPointers) +#pragma alloc_text(PAGE, FatRemoveMcbEntry) +#pragma alloc_text(PAGE, FatScanForDismountedVcb) +#pragma alloc_text(PAGE, FatFlushAndCleanVolume) +#pragma alloc_text(PAGE, FatSearchBufferForLabel) +#pragma alloc_text(PAGE, FatSetPurgeFailureMode) +#pragma alloc_text(PAGE, FatUnlockVolume) +#pragma alloc_text(PAGE, FatUserFsCtrl) +#pragma alloc_text(PAGE, FatVerifyLookupFatEntry) +#pragma alloc_text(PAGE, FatVerifyVolume) +#endif + +#if DBG + +BOOLEAN FatMoveFileDebug = 0; + +#endif + +// +// These wrappers go around the MCB package; we scale the LBO's passed +// in (which can be bigger than 32 bits on fat32) by the volume's sector +// size. +// +// Note we now use the real large mcb package. This means these shims +// now also convert the -1 unused LBN number to the 0 of the original +// mcb package. +// + +#define MCB_SCALE_LOG2 (Vcb->AllocationSupport.LogOfBytesPerSector) +#define MCB_SCALE (1 << MCB_SCALE_LOG2) +#define MCB_SCALE_MODULO (MCB_SCALE - 1) + +BOOLEAN +FatNonSparseMcb( + _In_ PVCB Vcb, + _In_ PLARGE_MCB Mcb, + _Out_ PVBO Vbo, + _Out_ PLONGLONG ByteCount + ) +{ + LBO Lbo; + ULONG Index = 0; + LONGLONG llVbo = 0; + + UNREFERENCED_PARAMETER(Vcb); + + while (FsRtlGetNextLargeMcbEntry(Mcb, Index, &llVbo, &Lbo, ByteCount)) { + *Vbo = (VBO)llVbo; + if (((ULONG)Lbo) == -1) { + return FALSE; + } + + Index++; + } + + *Vbo = (VBO)llVbo; + + return TRUE; +} + + +BOOLEAN +FatAddMcbEntry ( + IN PVCB Vcb, + IN PLARGE_MCB Mcb, + IN VBO Vbo, + IN LBO Lbo, + IN ULONG SectorCount + ) + +{ + BOOLEAN Result; +#if DBG + VBO SparseVbo; + LONGLONG SparseByteCount; +#endif + + PAGED_CODE(); + + if (SectorCount) { + + // + // Round up sectors, but be careful as SectorCount approaches 4Gb. + // Note that for x>0, (x+m-1)/m = ((x-1)/m)+(m/m) = ((x-1)/m)+1 + // + + SectorCount--; + SectorCount >>= MCB_SCALE_LOG2; + SectorCount++; + } + + Vbo >>= MCB_SCALE_LOG2; + Lbo >>= MCB_SCALE_LOG2; + + NT_ASSERT( SectorCount != 0 ); + + if (Mcb != &Vcb->DirtyFatMcb) { + NT_ASSERT( FatNonSparseMcb( Vcb, Mcb, &SparseVbo, &SparseByteCount ) || + ((SparseVbo == Vbo) && (SparseByteCount == SectorCount )) ); + } + + Result = FsRtlAddLargeMcbEntry( Mcb, + ((LONGLONG) Vbo), + ((LONGLONG) Lbo), + ((LONGLONG) SectorCount) ); + + if (Mcb != &Vcb->DirtyFatMcb) { + NT_ASSERT( FatNonSparseMcb( Vcb, Mcb, &SparseVbo, &SparseByteCount ) || + ((SparseVbo == Vbo) && (SparseByteCount == SectorCount )) ); + } + + return Result; +} + + +BOOLEAN +FatLookupMcbEntry ( + IN PVCB Vcb, + IN PLARGE_MCB Mcb, + IN VBO Vbo, + OUT PLBO Lbo, + OUT PULONG ByteCount OPTIONAL, + OUT PULONG Index OPTIONAL + ) +{ + BOOLEAN Results; + LONGLONG LiLbo; + LONGLONG LiSectorCount; + ULONG Remainder; + + LiLbo = 0; + LiSectorCount = 0; + + Remainder = Vbo & MCB_SCALE_MODULO; + + Results = FsRtlLookupLargeMcbEntry( Mcb, + (Vbo >> MCB_SCALE_LOG2), + &LiLbo, + ARGUMENT_PRESENT(ByteCount) ? &LiSectorCount : NULL, + NULL, + NULL, + Index ); + + if ((ULONG) LiLbo != -1) { + + *Lbo = (((LBO) LiLbo) << MCB_SCALE_LOG2); + + if (Results) { + + *Lbo += Remainder; + } + + } else { + + *Lbo = 0; + } + + if (ARGUMENT_PRESENT(ByteCount)) { + + *ByteCount = (ULONG) LiSectorCount; + + if (*ByteCount) { + + *ByteCount <<= MCB_SCALE_LOG2; + + // + // If ByteCount overflows, then this is likely the case of + // a file of max-supported size (4GiB - 1), allocated in a + // single continuous run. + // + + if (*ByteCount == 0) { + + *ByteCount = 0xFFFFFFFF; + } + + if (Results) { + + *ByteCount -= Remainder; + } + } + + } + + return Results; +} + +// +// NOTE: Vbo/Lbn undefined if MCB is empty & return code false. +// + +BOOLEAN +FatLookupLastMcbEntry ( + IN PVCB Vcb, + IN PLARGE_MCB Mcb, + OUT PVBO Vbo, + OUT PLBO Lbo, + OUT PULONG Index + ) + +{ + BOOLEAN Results; + LONGLONG LiVbo; + LONGLONG LiLbo; + ULONG LocalIndex; + + PAGED_CODE(); + + LiVbo = LiLbo = 0; + LocalIndex = 0; + + Results = FsRtlLookupLastLargeMcbEntryAndIndex( Mcb, + &LiVbo, + &LiLbo, + &LocalIndex ); + + *Vbo = ((VBO) LiVbo) << MCB_SCALE_LOG2; + + if (((ULONG) LiLbo) != -1) { + + *Lbo = ((LBO) LiLbo) << MCB_SCALE_LOG2; + + *Lbo += (MCB_SCALE - 1); + *Vbo += (MCB_SCALE - 1); + + } else { + + *Lbo = 0; + } + + if (Index) { + *Index = LocalIndex; + } + + return Results; +} + + +BOOLEAN +FatGetNextMcbEntry ( + IN PVCB Vcb, + IN PLARGE_MCB Mcb, + IN ULONG RunIndex, + OUT PVBO Vbo, + OUT PLBO Lbo, + OUT PULONG ByteCount + ) + +{ + BOOLEAN Results; + LONGLONG LiVbo; + LONGLONG LiLbo; + LONGLONG LiSectorCount; + + PAGED_CODE(); + + LiVbo = LiLbo = 0; + + Results = FsRtlGetNextLargeMcbEntry( Mcb, + RunIndex, + &LiVbo, + &LiLbo, + &LiSectorCount ); + + if (Results) { + + *Vbo = ((VBO) LiVbo) << MCB_SCALE_LOG2; + + if (((ULONG) LiLbo) != -1) { + + *Lbo = ((LBO) LiLbo) << MCB_SCALE_LOG2; + + } else { + + *Lbo = 0; + } + + *ByteCount = ((ULONG) LiSectorCount) << MCB_SCALE_LOG2; + + if ((*ByteCount == 0) && (LiSectorCount != 0)) { + + // + // If 'ByteCount' overflows, then this is likely a file of + // max supported size (2^32 - 1) in one contiguous run. + // + + NT_ASSERT( RunIndex == 0 ); + + *ByteCount = 0xFFFFFFFF; + } + } + + return Results; +} + + +VOID +FatRemoveMcbEntry ( + IN PVCB Vcb, + IN PLARGE_MCB Mcb, + IN VBO Vbo, + IN ULONG SectorCount + ) +{ + PAGED_CODE(); + + if ((SectorCount) && (SectorCount != 0xFFFFFFFF)) { + + SectorCount--; + SectorCount >>= MCB_SCALE_LOG2; + SectorCount++; + } + + Vbo >>= MCB_SCALE_LOG2; + +#if DBG + try { +#endif + + FsRtlRemoveLargeMcbEntry( Mcb, + (LONGLONG) Vbo, + (LONGLONG) SectorCount); + +#if DBG + } except(FatBugCheckExceptionFilter( GetExceptionInformation() )) { + + NOTHING; + } +#endif + +} + + +_Function_class_(IRP_MJ_FILE_SYSTEM_CONTROL) +_Function_class_(DRIVER_DISPATCH) +NTSTATUS +FatFsdFileSystemControl ( + _In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + _Inout_ PIRP Irp + ) + +/*++ + +Routine Description: + + This routine implements the FSD part of FileSystem control operations + +Arguments: + + VolumeDeviceObject - Supplies the volume device object where the + file exists + + Irp - Supplies the Irp being processed + +Return Value: + + NTSTATUS - The FSD status for the IRP + +--*/ + +{ + BOOLEAN Wait; + NTSTATUS Status; + PIRP_CONTEXT IrpContext = NULL; + + BOOLEAN TopLevel; + + PAGED_CODE(); + UNREFERENCED_PARAMETER( VolumeDeviceObject ); + + DebugTrace(+1, Dbg,"FatFsdFileSystemControl\n", 0); + + // + // Call the common FileSystem Control routine, with blocking allowed if + // synchronous. This opeation needs to special case the mount + // and verify suboperations because we know they are allowed to block. + // We identify these suboperations by looking at the file object field + // and seeing if its null. + // + + if (IoGetCurrentIrpStackLocation(Irp)->FileObject == NULL) { + + Wait = TRUE; + + } else { + + Wait = CanFsdWait( Irp ); + } + + FsRtlEnterFileSystem(); + + TopLevel = FatIsIrpTopLevel( Irp ); + + try { + + PIO_STACK_LOCATION IrpSp; + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + // + // We need to made a special check here for the InvalidateVolumes + // FSCTL as that comes in with a FileSystem device object instead + // of a volume device object. + // + + if (FatDeviceIsFatFsdo( IrpSp->DeviceObject) && + (IrpSp->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL) && + (IrpSp->MinorFunction == IRP_MN_USER_FS_REQUEST) && + (IrpSp->Parameters.FileSystemControl.FsControlCode == + FSCTL_INVALIDATE_VOLUMES)) { + + Status = FatInvalidateVolumes( Irp ); + + } else { + + IrpContext = FatCreateIrpContext( Irp, Wait ); + + Status = FatCommonFileSystemControl( IrpContext, Irp ); + } + + } except(FatExceptionFilter( IrpContext, GetExceptionInformation() )) { + + // + // We had some trouble trying to perform the requested + // operation, so we'll abort the I/O request with + // the error status that we get back from the + // execption code + // + + Status = FatProcessException( IrpContext, Irp, GetExceptionCode() ); + } + + if (TopLevel) { IoSetTopLevelIrp( NULL ); } + + FsRtlExitFileSystem(); + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "FatFsdFileSystemControl -> %08lx\n", Status); + + return Status; +} + + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatCommonFileSystemControl ( + IN PIRP_CONTEXT IrpContext, + IN 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; + + PAGED_CODE(); + + // + // Get a pointer to the current Irp stack location + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg,"FatCommonFileSystemControl\n", 0); + DebugTrace( 0, Dbg,"Irp = %p\n", Irp); + DebugTrace( 0, Dbg,"MinorFunction = %08lx\n", IrpSp->MinorFunction); + + // + // 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 = FatUserFsCtrl( IrpContext, Irp ); + break; + + case IRP_MN_MOUNT_VOLUME: + + Status = FatMountVolume( IrpContext, + IrpSp->Parameters.MountVolume.DeviceObject, + IrpSp->Parameters.MountVolume.Vpb, + IrpSp->DeviceObject ); + + // + // Complete the request. + // + // We do this here because FatMountVolume can be called recursively, + // but the Irp is only to be completed once. + // + // NOTE: I don't think this is true anymore (danlo 3/15/1999). Probably + // an artifact of the old doublespace attempt. + // + + FatCompleteRequest( IrpContext, Irp, Status ); + break; + + case IRP_MN_VERIFY_VOLUME: + + Status = FatVerifyVolume( IrpContext, Irp ); + break; + + default: + + DebugTrace( 0, Dbg, "Invalid FS Control Minor Function %08lx\n", IrpSp->MinorFunction); + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_DEVICE_REQUEST ); + Status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + + DebugTrace(-1, Dbg, "FatCommonFileSystemControl -> %08lx\n", Status); + + return Status; +} + + +// +// Local Support Routine +// + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatMountVolume ( + IN PIRP_CONTEXT IrpContext, + IN PDEVICE_OBJECT TargetDeviceObject, + IN PVPB Vpb, + IN PDEVICE_OBJECT FsDeviceObject + ) + +/*++ + +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 Fat 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 cached + volume file I/O. + + 2. Read the disk and check if it is a Fat volume. + + 3. If it is not a Fat volume then free the cached volume file, delete + the VCB, and complete the IRP with STATUS_UNRECOGNIZED_VOLUME + + 4. Check if the volume was previously mounted and if it was then do a + remount operation. This involves reinitializing the cached volume + file, checking the dirty bit, resetting up the allocation support, + deleting the VCB, hooking in the old VCB, and completing the IRP. + + 5. Otherwise create a root DCB, create Fsp threads as necessary, and + complete the IRP. + +Arguments: + + TargetDeviceObject - This is where we send all of our requests. + + Vpb - This gives us additional information needed to complete the mount. + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( IrpContext->OriginatingIrp ); + NTSTATUS Status; + + PBCB BootBcb; + PPACKED_BOOT_SECTOR BootSector = NULL; + + PBCB DirentBcb; + PDIRENT Dirent; + ULONG ByteOffset; + + BOOLEAN MountNewVolume = FALSE; + BOOLEAN WeClearedVerifyRequiredBit = FALSE; + BOOLEAN DoARemount = FALSE; + + PVCB OldVcb = NULL; + PVPB OldVpb = NULL; + + PDEVICE_OBJECT RealDevice = NULL; + PVOLUME_DEVICE_OBJECT VolDo = NULL; + PVCB Vcb = NULL; + PFILE_OBJECT RootDirectoryFile = NULL; + + PLIST_ENTRY Links; + + IO_STATUS_BLOCK Iosb; + ULONG ChangeCount = 0; + + DISK_GEOMETRY Geometry; + + PARTITION_INFORMATION_EX PartitionInformation; + NTSTATUS StatusPartInfo; + + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatMountVolume\n", 0); + DebugTrace( 0, Dbg, "TargetDeviceObject = %p\n", TargetDeviceObject); + DebugTrace( 0, Dbg, "Vpb = %p\n", Vpb); + + NT_ASSERT( FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) ); + NT_ASSERT( FatDeviceIsFatFsdo( FsDeviceObject)); + + + // + // Verify that there is a disk here and pick up the change count. + // + + Status = FatPerformDevIoCtrl( IrpContext, + IOCTL_DISK_CHECK_VERIFY, + TargetDeviceObject, + NULL, + 0, + &ChangeCount, + sizeof(ULONG), + FALSE, + TRUE, + &Iosb ); + + if (!NT_SUCCESS( Status )) { + + // + // If we will allow a raw mount then avoid sending the popup. + // + // Only send this on "true" disk devices to handle the accidental + // legacy of FAT. No other FS will throw a harderror on empty + // drives. + // + // Cmd should really handle this per 9x. + // + + if (!FlagOn( IrpSp->Flags, SL_ALLOW_RAW_MOUNT ) && + Vpb->RealDevice->DeviceType == FILE_DEVICE_DISK) { + + FatNormalizeAndRaiseStatus( IrpContext, Status ); + } + + return Status; + } + + if (Iosb.Information != sizeof(ULONG)) { + + // + // Be safe about the count in case the driver didn't fill it in + // + + ChangeCount = 0; + } + + // + // If this is a CD class device, then check to see if there is a + // 'data track' or not. This is to avoid issuing paging reads which will + // fail later in the mount process (e.g. CD-DA or blank CD media) + // + + if ((TargetDeviceObject->DeviceType == FILE_DEVICE_CD_ROM) && + !FatScanForDataTrack( IrpContext, TargetDeviceObject)) { + + return STATUS_UNRECOGNIZED_VOLUME; + } + + // + // Ping the volume with a partition query and pick up the partition + // type. We'll check this later to avoid some scurrilous volumes. + // + + StatusPartInfo = FatPerformDevIoCtrl( IrpContext, + IOCTL_DISK_GET_PARTITION_INFO_EX, + TargetDeviceObject, + NULL, + 0, + &PartitionInformation, + sizeof(PARTITION_INFORMATION_EX), + FALSE, + TRUE, + &Iosb ); + + // + // Make sure we can wait. + // + + SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT); + + // + // Do a quick check to see if there any Vcb's which can be removed. + // + + FatScanForDismountedVcb( IrpContext ); + + // + // Initialize the Bcbs and our final state so that the termination + // handlers will know what to free or unpin + // + + BootBcb = NULL; + DirentBcb = NULL; + + Vcb = NULL; + VolDo = NULL; + MountNewVolume = FALSE; + + try { + + // + // Synchronize with FatCheckForDismount(), which modifies the vpb. + // + +#pragma prefast( push ) +#pragma prefast( disable: 28137, "prefast wants the wait to be a constant, but that isn't possible for the way fastfat is designed" ) +#pragma prefast( disable: 28193, "this will always wait" ) + + (VOID)FatAcquireExclusiveGlobal( IrpContext ); + +#pragma prefast( pop ) + + // + // Create a new volume device object. This will have the Vcb + // hanging off of its end, and set its alignment requirement + // from the device we talk to. + // + + if (!NT_SUCCESS(Status = IoCreateDevice( FatData.DriverObject, + sizeof(VOLUME_DEVICE_OBJECT) - sizeof(DEVICE_OBJECT), + NULL, + FILE_DEVICE_DISK_FILE_SYSTEM, + 0, + FALSE, + (PDEVICE_OBJECT *)&VolDo))) { + + try_return( Status ); + } + + // + // Our alignment requirement is the larger of the processor alignment requirement + // already in the volume device object and that in the TargetDeviceObject + // + + if (TargetDeviceObject->AlignmentRequirement > VolDo->DeviceObject.AlignmentRequirement) { + + VolDo->DeviceObject.AlignmentRequirement = TargetDeviceObject->AlignmentRequirement; + } + + // + // Initialize the overflow queue for the volume + // + + VolDo->OverflowQueueCount = 0; + InitializeListHead( &VolDo->OverflowQueue ); + + VolDo->PostedRequestCount = 0; + KeInitializeSpinLock( &VolDo->OverflowQueueSpinLock ); + + // + // We must initialize the stack size in our device object before + // the following reads, because the I/O system has not done it yet. + // This must be done before we clear the device initializing flag + // otherwise a filter could attach and copy the wrong stack size into + // it's device object. + // + + VolDo->DeviceObject.StackSize = (CCHAR)(TargetDeviceObject->StackSize + 1); + + // + // We must also set the sector size correctly in our device object + // before clearing the device initializing flag. + // + + Status = FatPerformDevIoCtrl( IrpContext, + IOCTL_DISK_GET_DRIVE_GEOMETRY, + TargetDeviceObject, + NULL, + 0, + &Geometry, + sizeof( DISK_GEOMETRY ), + FALSE, + TRUE, + NULL ); + + if (!NT_SUCCESS( Status )) { + + try_return( Status ); + } + +#pragma prefast( suppress: 28175, "this is a filesystem driver, touching SectorSize is fine" ) + VolDo->DeviceObject.SectorSize = (USHORT)Geometry.BytesPerSector; + + // + // Indicate that this device object is now completely initialized + // + + ClearFlag(VolDo->DeviceObject.Flags, DO_DEVICE_INITIALIZING); + + // + // 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. + // This is needed when we create the virtual volume file's file object + // in initialize vcb. + // + + Vpb->DeviceObject = (PDEVICE_OBJECT)VolDo; + + // + // If the real device needs verification, temporarily clear the + // field. + // + + RealDevice = Vpb->RealDevice; + + if ( FlagOn(RealDevice->Flags, DO_VERIFY_VOLUME) ) { + + ClearFlag(RealDevice->Flags, DO_VERIFY_VOLUME); + + WeClearedVerifyRequiredBit = TRUE; + } + + // + // Initialize the new vcb + // + + FatInitializeVcb( IrpContext, + &VolDo->Vcb, + TargetDeviceObject, + Vpb, + FsDeviceObject); + // + // Get a reference to the Vcb hanging off the end of the device object + // + + Vcb = &VolDo->Vcb; + + // + // Read in the boot sector, and have the read be the minumum size + // needed. We know we can wait. + // + + // + // We need to commute errors on CD so that CDFS will get its crack. Audio + // and even data media may not be universally readable on sector zero. + // + + try { + + FatReadVolumeFile( IrpContext, + Vcb, + 0, // Starting Byte + sizeof(PACKED_BOOT_SECTOR), + &BootBcb, + (PVOID *)&BootSector ); + + } except( Vpb->RealDevice->DeviceType == FILE_DEVICE_CD_ROM ? + EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) { + + NOTHING; + } + + // + // Call a routine to check the boot sector to see if it is fat + // + + if (BootBcb == NULL || !FatIsBootSectorFat( BootSector)) { + + DebugTrace(0, Dbg, "Not a Fat Volume\n", 0); + + // + // Complete the request and return to our caller + // + + try_return( Status = STATUS_UNRECOGNIZED_VOLUME ); + } + + + // + // Unpack the BPB. We used to do some sanity checking of the FATs at + // this point, but authoring errors on third-party devices prevent + // us from continuing to safeguard ourselves. We can only hope the + // boot sector check is good enough. + // + // (read: digital cameras) + // + // Win9x does the same. + // + + FatUnpackBios( &Vcb->Bpb, &BootSector->PackedBpb ); + + // + // Check if we have an OS/2 Boot Manager partition and treat it as an + // unknown file system. We'll check the partition type in from the + // partition table and we ensure that it has less than 0x80 sectors, + // which is just a heuristic that will capture all real OS/2 BM partitions + // and avoid the chance we'll discover partitions which erroneously + // (but to this point, harmlessly) put down the OS/2 BM type. + // + // Note that this is only conceivable on good old MBR media. + // + // The OS/2 Boot Manager boot format mimics a FAT16 partition in sector + // zero but does is not a real FAT16 file system. For example, the boot + // sector indicates it has 2 FATs but only really has one, with the boot + // manager code overlaying the second FAT. If we then set clean bits in + // FAT[0] we'll corrupt that code. + // + + if (NT_SUCCESS( StatusPartInfo ) && + (PartitionInformation.PartitionStyle == PARTITION_STYLE_MBR && + PartitionInformation.Mbr.PartitionType == PARTITION_OS2BOOTMGR) && + (Vcb->Bpb.Sectors != 0 && + Vcb->Bpb.Sectors < 0x80)) { + + DebugTrace( 0, Dbg, "OS/2 Boot Manager volume detected, volume not mounted. \n", 0 ); + + // + // Complete the request and return to our caller + // + + try_return( Status = STATUS_UNRECOGNIZED_VOLUME ); + } + + // + // Verify that the sector size recorded in the Bpb matches what the + // device currently reports it's sector size to be. + // + + if ( !NT_SUCCESS( Status) || + (Geometry.BytesPerSector != Vcb->Bpb.BytesPerSector)) { + + try_return( Status = STATUS_UNRECOGNIZED_VOLUME ); + } + + // + // This is a fat volume, so extract the bpb, serial number. The + // label we'll get later after we've created the root dcb. + // + // Note that the way data caching is done, we set neither the + // direct I/O or Buffered I/O bit in the device object flags. + // + + if (Vcb->Bpb.Sectors != 0) { Vcb->Bpb.LargeSectors = 0; } + + if (IsBpbFat32(&BootSector->PackedBpb)) { + + CopyUchar4( &Vpb->SerialNumber, ((PPACKED_BOOT_SECTOR_EX)BootSector)->Id ); + + } else { + + CopyUchar4( &Vpb->SerialNumber, BootSector->Id ); + + // + // Allocate space for the stashed boot sector chunk. This only has meaning on + // FAT12/16 volumes since this only is kept for the FSCTL_QUERY_FAT_BPB and it and + // its users are a bit wierd, thinking that a BPB exists wholly in the first 0x24 + // bytes. + // + + Vcb->First0x24BytesOfBootSector = + FsRtlAllocatePoolWithTag( PagedPool, + 0x24, + TAG_STASHED_BPB ); + + // + // Stash a copy of the first 0x24 bytes + // + + RtlCopyMemory( Vcb->First0x24BytesOfBootSector, + BootSector, + 0x24 ); + } + + // + // Now unpin the boot sector, so when we set up allocation eveything + // works. + // + + FatUnpinBcb( IrpContext, BootBcb ); + + // + // Compute a number of fields for Vcb.AllocationSupport + // + + FatSetupAllocationSupport( IrpContext, Vcb ); + + // + // Sanity check the FsInfo information for FAT32 volumes. Silently deal + // with messed up information by effectively disabling FsInfo updates. + // + + if (FatIsFat32( Vcb )) { + + if (Vcb->Bpb.FsInfoSector >= Vcb->Bpb.ReservedSectors) { + + Vcb->Bpb.FsInfoSector = 0; + } + } + + + // + // Create a root Dcb so we can read in the volume label. If this is FAT32, we can + // discover corruption in the FAT chain. + // + // NOTE: this exception handler presumes that this is the only spot where we can + // discover corruption in the mount process. If this ever changes, this handler + // MUST be expanded. The reason we have this guy here is because we have to rip + // the structures down now (in the finally below) and can't wait for the outer + // exception handling to do it for us, at which point everything will have vanished. + // + + try { + + FatCreateRootDcb( IrpContext, Vcb ); + + } except (GetExceptionCode() == STATUS_FILE_CORRUPT_ERROR ? EXCEPTION_EXECUTE_HANDLER : + EXCEPTION_CONTINUE_SEARCH) { + + // + // The volume needs to be dirtied, do it now. Note that at this point we have built + // enough of the Vcb to pull this off. + // + + FatMarkVolume( IrpContext, Vcb, VolumeDirty ); + + // + // Now keep bailing out ... + // + + FatRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); + } + + FatLocateVolumeLabel( IrpContext, + Vcb, + &Dirent, + &DirentBcb, + (PVBO)&ByteOffset ); + + if (Dirent != NULL) { + + OEM_STRING OemString; + UNICODE_STRING UnicodeString; + + // + // Compute the length of the volume name + // + + OemString.Buffer = (PCHAR)&Dirent->FileName[0]; + OemString.MaximumLength = 11; + + for ( OemString.Length = 11; + OemString.Length > 0; + OemString.Length -= 1) { + + if ( (Dirent->FileName[OemString.Length-1] != 0x00) && + (Dirent->FileName[OemString.Length-1] != 0x20) ) { break; } + } + + UnicodeString.MaximumLength = MAXIMUM_VOLUME_LABEL_LENGTH; + UnicodeString.Buffer = &Vcb->Vpb->VolumeLabel[0]; + + Status = RtlOemStringToCountedUnicodeString( &UnicodeString, + &OemString, + FALSE ); + + if ( !NT_SUCCESS( Status ) ) { + + try_return( Status ); + } + + Vpb->VolumeLabelLength = UnicodeString.Length; + + } else { + + Vpb->VolumeLabelLength = 0; + } + + // + // Use the change count we noted initially *before* doing any work. + // If something came along in the midst of this operation, we'll + // verify and discover the problem. + // + + Vcb->ChangeCount = ChangeCount; + + // + // Now scan the list of previously mounted volumes and compare + // serial numbers and volume labels off not currently mounted + // volumes to see if we have a match. + // + + for (Links = FatData.VcbQueue.Flink; + Links != &FatData.VcbQueue; + Links = Links->Flink) { + + OldVcb = CONTAINING_RECORD( Links, VCB, VcbLinks ); + OldVpb = OldVcb->Vpb; + + // + // Skip over ourselves since we're already in the VcbQueue + // + + if (OldVpb == Vpb) { continue; } + + // + // Check for a match: + // + // Serial Number, VolumeLabel and Bpb must all be the same. + // Also the volume must have failed a verify before (ie. + // VolumeNotMounted), and it must be in the same physical + // drive than it was mounted in before. + // + + if ( (OldVpb->SerialNumber == Vpb->SerialNumber) && + (OldVcb->VcbCondition == VcbNotMounted) && + (OldVpb->RealDevice == RealDevice) && + (OldVpb->VolumeLabelLength == Vpb->VolumeLabelLength) && + (RtlEqualMemory(&OldVpb->VolumeLabel[0], + &Vpb->VolumeLabel[0], + Vpb->VolumeLabelLength)) && + (RtlEqualMemory(&OldVcb->Bpb, + &Vcb->Bpb, + IsBpbFat32(&Vcb->Bpb) ? + sizeof(BIOS_PARAMETER_BLOCK) : + FIELD_OFFSET(BIOS_PARAMETER_BLOCK, + LargeSectorsPerFat) ))) { + + DoARemount = TRUE; + + break; + } + } + + if ( DoARemount ) { + + PVPB *IrpVpb; + + DebugTrace(0, Dbg, "Doing a remount\n", 0); + DebugTrace(0, Dbg, "Vcb = %p\n", Vcb); + DebugTrace(0, Dbg, "Vpb = %p\n", Vpb); + DebugTrace(0, Dbg, "OldVcb = %p\n", OldVcb); + DebugTrace(0, Dbg, "OldVpb = %p\n", OldVpb); + + // + // Swap target device objects between the VCBs. That way + // the old VCB will start using the new target device object, + // and the new VCB will be torn down and deference the old + // target device object. + // + + Vcb->TargetDeviceObject = OldVcb->TargetDeviceObject; + OldVcb->TargetDeviceObject = TargetDeviceObject; + + // + // This is a remount, so link the old vpb in place + // of the new vpb. + // + + NT_ASSERT( !FlagOn( OldVcb->VcbState, VCB_STATE_FLAG_VPB_MUST_BE_FREED ) ); + + FatSetVcbCondition( OldVcb, VcbGood); + OldVpb->RealDevice = Vpb->RealDevice; + ClearFlag( OldVcb->VcbState, VCB_STATE_VPB_NOT_ON_DEVICE); + +#pragma prefast( suppress: 28175, "touching Vpb is ok for a filesystem" ) + OldVpb->RealDevice->Vpb = OldVpb; + + // + // Use the new changecount. + // + + OldVcb->ChangeCount = Vcb->ChangeCount; + + // + // If the new VPB is the VPB referenced in the original Irp, set + // that reference back to the old VPB. + // + + IrpVpb = &IoGetCurrentIrpStackLocation(IrpContext->OriginatingIrp)->Parameters.MountVolume.Vpb; + + if (*IrpVpb == Vpb) { + + *IrpVpb = OldVpb; + } + + // + // We do not want to touch this VPB again. It will get cleaned up when + // the new VCB is cleaned up. + // + + NT_ASSERT( Vcb->Vpb == Vpb ); + + Vpb = NULL; + SetFlag( Vcb->VcbState, VCB_STATE_FLAG_VPB_MUST_BE_FREED ); + FatSetVcbCondition( Vcb, VcbBad ); + + // + // Reinitialize the volume file cache and allocation support. + // + + { + CC_FILE_SIZES FileSizes; + + FileSizes.AllocationSize.QuadPart = + FileSizes.FileSize.QuadPart = ( 0x40000 + 0x1000 ); + FileSizes.ValidDataLength = FatMaxLarge; + + DebugTrace(0, Dbg, "Truncate and reinitialize the volume file\n", 0); + + FatInitializeCacheMap( OldVcb->VirtualVolumeFile, + &FileSizes, + TRUE, + &FatData.CacheManagerNoOpCallbacks, + Vcb ); + + // + // Redo the allocation support + // + + FatSetupAllocationSupport( IrpContext, OldVcb ); + + // + // Get the state of the dirty bit. + // + + FatCheckDirtyBit( IrpContext, OldVcb ); + + // + // Check for write protected media. + // + + if (FatIsMediaWriteProtected(IrpContext, TargetDeviceObject)) { + + SetFlag( OldVcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED ); + + } else { + + ClearFlag( OldVcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED ); + } + } + + // + // Complete the request and return to our caller + // + + try_return( Status = STATUS_SUCCESS ); + } + + DebugTrace(0, Dbg, "Mount a new volume\n", 0); + + // + // This is a new mount + // + // Create a blank ea data file fcb, just not for Fat32. + // + + if (!FatIsFat32(Vcb)) { + + DIRENT TempDirent; + PFCB EaFcb; + + RtlZeroMemory( &TempDirent, sizeof(DIRENT) ); + RtlCopyMemory( &TempDirent.FileName[0], "EA DATA SF", 11 ); + + EaFcb = FatCreateFcb( IrpContext, + Vcb, + Vcb->RootDcb, + 0, + 0, + &TempDirent, + NULL, + FALSE, + TRUE ); + + // + // Deny anybody who trys to open the file. + // + + SetFlag( EaFcb->FcbState, FCB_STATE_SYSTEM_FILE ); + + Vcb->EaFcb = EaFcb; + } + + // + // Get the state of the dirty bit. + // + + FatCheckDirtyBit( IrpContext, Vcb ); + + + // + // Check for write protected media. + // + + if (FatIsMediaWriteProtected(IrpContext, TargetDeviceObject)) { + + SetFlag( Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED ); + + } else { + + ClearFlag( Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED ); + } + + + // + // Lock volume in drive if we just mounted the boot drive. + // + + if (FlagOn(RealDevice->Flags, DO_SYSTEM_BOOT_PARTITION)) { + + SetFlag(Vcb->VcbState, VCB_STATE_FLAG_BOOT_OR_PAGING_FILE); + + if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA)) { + + FatToggleMediaEjectDisable( IrpContext, Vcb, TRUE ); + } + } + + + // + // Indicate to our termination handler that we have mounted + // a new volume. + // + + MountNewVolume = TRUE; + + // + // Complete the request + // + + Status = STATUS_SUCCESS; + + // + // Ref the root dir stream object so we can send mount notification. + // + + RootDirectoryFile = Vcb->RootDcb->Specific.Dcb.DirectoryFile; + ObReferenceObject( RootDirectoryFile ); + + // + // Remove the extra reference to this target DO made on behalf of us + // by the IO system. In the remount case, we permit regular Vcb + // deletion to do this work. + // + + ObDereferenceObject( TargetDeviceObject ); + + + try_exit: NOTHING; + + } finally { + + DebugUnwind( FatMountVolume ); + + FatUnpinBcb( IrpContext, BootBcb ); + FatUnpinBcb( IrpContext, DirentBcb ); + + // + // Check if a volume was mounted. If not then we need to + // mark the Vpb not mounted again. + // + + if ( !MountNewVolume ) { + + if ( Vcb != NULL ) { + + // + // A VCB was created and initialized. We need to try to tear it down. + // + + FatCheckForDismount( IrpContext, + Vcb, + TRUE ); + + IrpContext->Vcb = NULL; + + } else if (VolDo != NULL) { + + // + // The VCB was never initialized, so we need to delete the + // device right here. + // + + IoDeleteDevice( &VolDo->DeviceObject ); + } + + // + // See if a remount failed. + // + + if (DoARemount && AbnormalTermination()) { + + // + // The remount failed. Try to tear down the old VCB as well. + // + + FatCheckForDismount( IrpContext, + OldVcb, + TRUE ); + } + } + + if ( WeClearedVerifyRequiredBit == TRUE ) { + + SetFlag(RealDevice->Flags, DO_VERIFY_VOLUME); + } + + FatReleaseGlobal( IrpContext ); + + DebugTrace(-1, Dbg, "FatMountVolume -> %08lx\n", Status); + } + + // + // Now send mount notification. Note that since this is outside of any + // synchronization since the synchronous delivery of this may go to + // folks that provoke re-entrance to the FS. + // + + if (RootDirectoryFile != NULL) { + +#if (NTDDI_VERSION >= NTDDI_WIN8) + if (FatDiskAccountingEnabled) { + + CcSetAdditionalCacheAttributesEx( RootDirectoryFile, CC_ENABLE_DISK_IO_ACCOUNTING ); + } +#endif + + FsRtlNotifyVolumeEvent( RootDirectoryFile, FSRTL_VOLUME_MOUNT ); + ObDereferenceObject( RootDirectoryFile ); + } + + return Status; +} + + +// +// Local Support Routine +// + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatVerifyVolume ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the verify volume operation by checking the volume + label and serial number physically on the media with the the Vcb + currently claiming to have the volume mounted. It is responsible for + either completing or enqueuing the input Irp. + + Regardless of whether the verify operation succeeds, the following + operations are performed: + + - Set Vcb->VirtualEaFile back to its initial state. + - Purge all cached data (flushing first if verify succeeds) + - Mark all Fcbs as needing verification + + If the volumes verifies correctly we also must: + + - Check the volume dirty bit. + - Reinitialize the allocation support + - Flush any dirty data + + If the volume verify fails, it may never be mounted again. If it is + mounted again, it will happen as a remount operation. In preparation + for that, and to leave the volume in a state that can be "lazy deleted" + the following operations are performed: + + - Set the Vcb condition to VcbNotMounted + - Uninitialize the volume file cachemap + - Tear down the allocation support + + In the case of an abnormal termination we haven't determined the state + of the volume, so we set the Device Object as needing verification again. + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - If the verify operation completes, it will return either + STATUS_SUCCESS or STATUS_WRONG_VOLUME, exactly. If an IO or + other error is encountered, that status will be returned. + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + + PIO_STACK_LOCATION IrpSp; + + PDIRENT RootDirectory = NULL; + PPACKED_BOOT_SECTOR BootSector = NULL; + + BIOS_PARAMETER_BLOCK Bpb; + + PVOLUME_DEVICE_OBJECT VolDo; + PVCB Vcb; + PVPB Vpb; + + ULONG SectorSize; + BOOLEAN ClearVerify = FALSE; + BOOLEAN ReleaseEntireVolume = FALSE; + BOOLEAN VerifyAlreadyDone = FALSE; + + DISK_GEOMETRY DiskGeometry; + + LBO RootDirectoryLbo; + ULONG RootDirectorySize; + BOOLEAN LabelFound; + + ULONG ChangeCount = 0; + IO_STATUS_BLOCK Iosb; + + PAGED_CODE(); + + // + // Get the current Irp stack location + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "FatVerifyVolume\n", 0); + DebugTrace( 0, Dbg, "DeviceObject = %p\n", IrpSp->Parameters.VerifyVolume.DeviceObject); + DebugTrace( 0, Dbg, "Vpb = %p\n", IrpSp->Parameters.VerifyVolume.Vpb); + + // + // Save some references to make our life a little easier. Note the Vcb for the purposes + // of exception handling. + // + + VolDo = (PVOLUME_DEVICE_OBJECT)IrpSp->Parameters.VerifyVolume.DeviceObject; + + Vpb = IrpSp->Parameters.VerifyVolume.Vpb; + IrpContext->Vcb = Vcb = &VolDo->Vcb; + + // + // If we cannot wait then enqueue the irp to the fsp and + // return the status to our caller. + // + + if (!FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT)) { + + DebugTrace(0, Dbg, "Cannot wait for verify.\n", 0); + + Status = FatFsdPostRequest( IrpContext, Irp ); + + DebugTrace(-1, Dbg, "FatVerifyVolume -> %08lx\n", Status ); + return Status; + } + + // + // We are serialized at this point allowing only one thread to + // actually perform the verify operation. Any others will just + // wait and then no-op when checking if the volume still needs + // verification. + // + +#pragma prefast( push ) +#pragma prefast( disable: 28137, "prefast wants the wait to be a constant, but that isn't possible for the way fastfat is designed" ) +#pragma prefast( disable: 28193, "this will always wait" ) + + (VOID)FatAcquireExclusiveGlobal( IrpContext ); + +#pragma prefast( pop ) + + (VOID)FatAcquireExclusiveVcb( IrpContext, Vcb ); + + try { + + BOOLEAN AllowRawMount = BooleanFlagOn( IrpSp->Flags, SL_ALLOW_RAW_MOUNT ); + + // + // Mark ourselves as verifying this volume so that recursive I/Os + // will be able to complete. + // + + NT_ASSERT( Vcb->VerifyThread == NULL ); + Vcb->VerifyThread = KeGetCurrentThread(); + + // + // Check if the real device still needs to be verified. If it doesn't + // then obviously someone beat us here and already did the work + // so complete the verify irp with success. Otherwise reenable + // the real device and get to work. + // + + if (!FlagOn(Vpb->RealDevice->Flags, DO_VERIFY_VOLUME)) { + + DebugTrace(0, Dbg, "RealDevice has already been verified\n", 0); + + VerifyAlreadyDone = TRUE; + try_return( Status = STATUS_SUCCESS ); + } + + // + // Ping the volume with a partition query to make Jeff happy. + // + + { + PARTITION_INFORMATION_EX PartitionInformation; + + (VOID) FatPerformDevIoCtrl( IrpContext, + IOCTL_DISK_GET_PARTITION_INFO_EX, + Vcb->TargetDeviceObject, + NULL, + 0, + &PartitionInformation, + sizeof(PARTITION_INFORMATION_EX), + FALSE, + TRUE, + &Iosb ); + } + + // + // Verify that there is a disk here and pick up the change count. + // + + Status = FatPerformDevIoCtrl( IrpContext, + IOCTL_DISK_CHECK_VERIFY, + Vcb->TargetDeviceObject, + NULL, + 0, + &ChangeCount, + 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 (AllowRawMount) { + + try_return( Status = STATUS_WRONG_VOLUME ); + } + + FatNormalizeAndRaiseStatus( IrpContext, Status ); + } + + if (Iosb.Information != sizeof(ULONG)) { + + // + // Be safe about the count in case the driver didn't fill it in + // + + ChangeCount = 0; + } + + // + // Whatever happens we will have verified this volume at this change + // count, so record that fact. + // + + Vcb->ChangeCount = ChangeCount; + + // + // If this is a CD class device, then check to see if there is a + // 'data track' or not. This is to avoid issuing paging reads which will + // fail later in the mount process (e.g. CD-DA or blank CD media) + // + + if ((Vcb->TargetDeviceObject->DeviceType == FILE_DEVICE_CD_ROM) && + !FatScanForDataTrack( IrpContext, Vcb->TargetDeviceObject)) { + + try_return( Status = STATUS_WRONG_VOLUME); + } + + // + // Some devices can change sector sizes on the fly. Obviously, it + // isn't the same volume if that happens. + // + + Status = FatPerformDevIoCtrl( IrpContext, + IOCTL_DISK_GET_DRIVE_GEOMETRY, + Vcb->TargetDeviceObject, + NULL, + 0, + &DiskGeometry, + sizeof( DISK_GEOMETRY ), + FALSE, + TRUE, + NULL ); + + 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 (AllowRawMount) { + + try_return( Status = STATUS_WRONG_VOLUME ); + } + + FatNormalizeAndRaiseStatus( IrpContext, Status ); + } + + // + // Read in the boot sector + // + + SectorSize = (ULONG)Vcb->Bpb.BytesPerSector; + + if (SectorSize != DiskGeometry.BytesPerSector) { + + try_return( Status = STATUS_WRONG_VOLUME ); + } + + BootSector = FsRtlAllocatePoolWithTag(NonPagedPoolNxCacheAligned, + (ULONG) ROUND_TO_PAGES( SectorSize ), + TAG_VERIFY_BOOTSECTOR); + + // + // If this verify is on behalf of a DASD open, allow a RAW mount. + // + + if (!FatPerformVerifyDiskRead( IrpContext, + Vcb, + BootSector, + 0, + SectorSize, + AllowRawMount )) { + + try_return( Status = STATUS_WRONG_VOLUME ); + } + + // + // Call a routine to check the boot sector to see if it is fat. + // If it is not fat then mark the vcb as not mounted tell our + // caller its the wrong volume + // + + if (!FatIsBootSectorFat( BootSector )) { + + DebugTrace(0, Dbg, "Not a Fat Volume\n", 0); + + try_return( Status = STATUS_WRONG_VOLUME ); + } + + // + // This is a fat volume, so extract serial number and see if it is + // ours. + // + + { + ULONG SerialNumber; + + if (IsBpbFat32(&BootSector->PackedBpb)) { + CopyUchar4( &SerialNumber, ((PPACKED_BOOT_SECTOR_EX)BootSector)->Id ); + } else { + CopyUchar4( &SerialNumber, BootSector->Id ); + } + + if (SerialNumber != Vpb->SerialNumber) { + + DebugTrace(0, Dbg, "Not our serial number\n", 0); + + try_return( Status = STATUS_WRONG_VOLUME ); + } + } + + // + // Make sure the Bpbs are not different. We have to zero out our + // stack version of the Bpb since unpacking leaves holes. + // + + RtlZeroMemory( &Bpb, sizeof(BIOS_PARAMETER_BLOCK) ); + + FatUnpackBios( &Bpb, &BootSector->PackedBpb ); + if (Bpb.Sectors != 0) { Bpb.LargeSectors = 0; } + + if ( !RtlEqualMemory( &Bpb, + &Vcb->Bpb, + IsBpbFat32(&Bpb) ? + sizeof(BIOS_PARAMETER_BLOCK) : + FIELD_OFFSET(BIOS_PARAMETER_BLOCK, + LargeSectorsPerFat) )) { + + DebugTrace(0, Dbg, "Bpb is different\n", 0); + + try_return( Status = STATUS_WRONG_VOLUME ); + } + + // + // Check the volume label. We do this by trying to locate the + // volume label, making two strings one for the saved volume label + // and the other for the new volume label and then we compare the + // two labels. + // + + if (FatRootDirectorySize(&Bpb) > 0) { + + RootDirectorySize = FatRootDirectorySize(&Bpb); + + } else { + + RootDirectorySize = FatBytesPerCluster(&Bpb); + } + + RootDirectory = FsRtlAllocatePoolWithTag( NonPagedPoolNxCacheAligned, + (ULONG) ROUND_TO_PAGES( RootDirectorySize ), + TAG_VERIFY_ROOTDIR); + + if (!IsBpbFat32(&BootSector->PackedBpb)) { + + // + // The Fat12/16 case is simple -- read the root directory in and + // search it. + // + + RootDirectoryLbo = FatRootDirectoryLbo(&Bpb); + + if (!FatPerformVerifyDiskRead( IrpContext, + Vcb, + RootDirectory, + RootDirectoryLbo, + RootDirectorySize, + AllowRawMount )) { + + try_return( Status = STATUS_WRONG_VOLUME ); + } + + Status = FatSearchBufferForLabel(IrpContext, Vpb, + RootDirectory, RootDirectorySize, + &LabelFound); + + if (!NT_SUCCESS(Status)) { + + try_return( Status ); + } + + if (!LabelFound && Vpb->VolumeLabelLength > 0) { + + try_return( Status = STATUS_WRONG_VOLUME ); + } + + } else { + + ULONG RootDirectoryCluster; + + RootDirectoryCluster = Bpb.RootDirFirstCluster; + + while (RootDirectoryCluster != FAT_CLUSTER_LAST) { + + RootDirectoryLbo = FatGetLboFromIndex(Vcb, RootDirectoryCluster); + + if (!FatPerformVerifyDiskRead( IrpContext, + Vcb, + RootDirectory, + RootDirectoryLbo, + RootDirectorySize, + AllowRawMount )) { + + try_return( Status = STATUS_WRONG_VOLUME ); + } + + Status = FatSearchBufferForLabel(IrpContext, Vpb, + RootDirectory, RootDirectorySize, + &LabelFound); + + if (!NT_SUCCESS(Status)) { + + try_return( Status ); + } + + if (LabelFound) { + + // + // Found a matching label. + // + + break; + } + + // + // Set ourselves up for the next loop iteration. + // + + FatVerifyLookupFatEntry( IrpContext, Vcb, + RootDirectoryCluster, + &RootDirectoryCluster ); + + switch (FatInterpretClusterType(Vcb, RootDirectoryCluster)) { + + case FatClusterAvailable: + case FatClusterReserved: + case FatClusterBad: + + // + // Bail all the way out if we have a bad root. + // + + FatRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); + break; + + default: + + break; + } + + } + + if (RootDirectoryCluster == FAT_CLUSTER_LAST && + Vpb->VolumeLabelLength > 0) { + + // + // Should have found a label, didn't find any. + // + + try_return( Status = STATUS_WRONG_VOLUME ); + } + } + + + try_exit: NOTHING; + + // + // Note that we have previously acquired the Vcb to serialize + // the EA file stuff the marking all the Fcbs as NeedToBeVerified. + // + // Put the Ea file back in a initial state. + // + + FatCloseEaFile( IrpContext, Vcb, (BOOLEAN)(Status == STATUS_SUCCESS) ); + + // + // Mark all Fcbs as needing verification, but only if we really have + // to do it. + // + + if (!VerifyAlreadyDone) { + + FatAcquireExclusiveVolume( IrpContext, Vcb ); + ReleaseEntireVolume = TRUE; + + FatMarkFcbCondition( IrpContext, Vcb->RootDcb, FcbNeedsToBeVerified, TRUE ); + } + + // + // If the verify didn't succeed, get the volume ready for a + // remount or eventual deletion. + // + + if (Vcb->VcbCondition == VcbNotMounted) { + + // + // If the volume was already in an unmounted state, just bail + // and make sure we return STATUS_WRONG_VOLUME. + // + + Status = STATUS_WRONG_VOLUME; + + } else if ( Status == STATUS_WRONG_VOLUME ) { + + // + // Grab everything so we can safely transition the volume state without + // having a thread stumble into the torn-down allocation engine. + // + + if (!ReleaseEntireVolume) { + FatAcquireExclusiveVolume( IrpContext, Vcb ); + ReleaseEntireVolume = TRUE; + } + + // + // Get rid of any cached data, without flushing + // + + FatPurgeReferencedFileObjects( IrpContext, Vcb->RootDcb, NoFlush ); + + // + // Uninitialize the volume file cache map. Note that we cannot + // do a "FatSyncUninit" because of deadlock problems. However, + // since this FileObject is referenced by us, and thus included + // in the Vpb residual count, it is OK to do a normal CcUninit. + // + + CcUninitializeCacheMap( Vcb->VirtualVolumeFile, + &FatLargeZero, + NULL ); + + FatTearDownAllocationSupport( IrpContext, Vcb ); + + FatSetVcbCondition( Vcb, VcbNotMounted); + + ClearVerify = TRUE; + + } else if (!VerifyAlreadyDone) { + + // + // Grab everything so we can safely transition the volume state without + // having a thread stumble into the torn-down allocation engine. + // + + if (!ReleaseEntireVolume) { + FatAcquireExclusiveVolume( IrpContext, Vcb ); + ReleaseEntireVolume = TRUE; + } + + // + // Get rid of any cached data, flushing first. + // + // Future work (and for bonus points, around the other flush points) + // could address the possibility that the dirent filesize hasn't been + // updated yet, causing us to fail the re-verification of a file in + // DetermineAndMark. This is pretty subtle and very very uncommon. + // + + FatPurgeReferencedFileObjects( IrpContext, Vcb->RootDcb, Flush ); + + // + // Flush and Purge the volume file. + // + + (VOID)FatFlushFat( IrpContext, Vcb ); + CcPurgeCacheSection( &Vcb->SectionObjectPointers, NULL, 0, FALSE ); + + // + // Redo the allocation support with newly paged stuff. + // + + FatTearDownAllocationSupport( IrpContext, Vcb ); + FatSetupAllocationSupport( IrpContext, Vcb ); + + FatCheckDirtyBit( IrpContext, Vcb ); + + // + // Check for write protected media. + // + + if (FatIsMediaWriteProtected(IrpContext, Vcb->TargetDeviceObject)) { + + SetFlag( Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED ); + + } else { + + ClearFlag( Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED ); + } + + ClearVerify = TRUE; + } + + if (ClearVerify) { + + // + // Mark the device as no longer needing verification. + // + + ClearFlag( Vpb->RealDevice->Flags, DO_VERIFY_VOLUME ); + } + + } finally { + + DebugUnwind( FatVerifyVolume ); + + // + // Free any buffer we may have allocated + // + + if ( BootSector != NULL ) { ExFreePool( BootSector ); } + if ( RootDirectory != NULL ) { ExFreePool( RootDirectory ); } + + // + // Show that we are done with this volume. + // + + NT_ASSERT( Vcb->VerifyThread == KeGetCurrentThread() ); + Vcb->VerifyThread = NULL; + + if (ReleaseEntireVolume) { + + FatReleaseVolume( IrpContext, Vcb ); + } + + FatReleaseVcb( IrpContext, Vcb ); + FatReleaseGlobal( IrpContext ); + + // + // If this was not an abnormal termination, complete the irp. + // + + if (!AbnormalTermination()) { + + FatCompleteRequest( IrpContext, Irp, Status ); + } + + DebugTrace(-1, Dbg, "FatVerifyVolume -> %08lx\n", Status); + } + + return Status; +} + + +// +// Local Support Routine +// + +BOOLEAN +FatIsBootSectorFat ( + IN PPACKED_BOOT_SECTOR BootSector + ) + +/*++ + +Routine Description: + + This routine checks if the boot sector is for a fat file volume. + +Arguments: + + BootSector - Supplies the packed boot sector to check + +Return Value: + + BOOLEAN - TRUE if the volume is Fat and FALSE otherwise. + +--*/ + +{ + BOOLEAN Result; + BIOS_PARAMETER_BLOCK Bpb = {0}; + + DebugTrace(+1, Dbg, "FatIsBootSectorFat, BootSector = %p\n", BootSector); + + // + // The result is true unless we decide that it should be false + // + + Result = TRUE; + + // + // Unpack the bios and then test everything + // + + FatUnpackBios( &Bpb, &BootSector->PackedBpb ); + if (Bpb.Sectors != 0) { Bpb.LargeSectors = 0; } + + if ((BootSector->Jump[0] != 0xe9) && + (BootSector->Jump[0] != 0xeb) && + (BootSector->Jump[0] != 0x49)) { + + Result = FALSE; + + // + // Enforce some sanity on the sector size (easy check) + // + + } else if ((Bpb.BytesPerSector != 128) && + (Bpb.BytesPerSector != 256) && + (Bpb.BytesPerSector != 512) && + (Bpb.BytesPerSector != 1024) && + (Bpb.BytesPerSector != 2048) && + (Bpb.BytesPerSector != 4096)) { + + Result = FALSE; + + // + // Likewise on the clustering. + // + + } else if ((Bpb.SectorsPerCluster != 1) && + (Bpb.SectorsPerCluster != 2) && + (Bpb.SectorsPerCluster != 4) && + (Bpb.SectorsPerCluster != 8) && + (Bpb.SectorsPerCluster != 16) && + (Bpb.SectorsPerCluster != 32) && + (Bpb.SectorsPerCluster != 64) && + (Bpb.SectorsPerCluster != 128)) { + + Result = FALSE; + + // + // Likewise on the reserved sectors (must reflect at least the boot sector!) + // + + } else if (Bpb.ReservedSectors == 0) { + + Result = FALSE; + + // + // No FATs? Wrong ... + // + + } else if (Bpb.Fats == 0) { + + Result = FALSE; + + // + // Prior to DOS 3.2 might contains value in both of Sectors and + // Sectors Large. + // + + } else if ((Bpb.Sectors == 0) && (Bpb.LargeSectors == 0)) { + + Result = FALSE; + + // + // Check that FAT32 (SectorsPerFat == 0) claims some FAT space and + // is of a version we recognize, currently Version 0.0. + // + + } else if (Bpb.SectorsPerFat == 0 && ( Bpb.LargeSectorsPerFat == 0 || + Bpb.FsVersion != 0 )) { + + Result = FALSE; + + } else if ((Bpb.Media != 0xf0) && + (Bpb.Media != 0xf8) && + (Bpb.Media != 0xf9) && + (Bpb.Media != 0xfb) && + (Bpb.Media != 0xfc) && + (Bpb.Media != 0xfd) && + (Bpb.Media != 0xfe) && + (Bpb.Media != 0xff) && + (!FatData.FujitsuFMR || ((Bpb.Media != 0x00) && + (Bpb.Media != 0x01) && + (Bpb.Media != 0xfa)))) { + + Result = FALSE; + + // + // If this isn't FAT32, then there better be a claimed root directory + // size here ... + // + + } else if (Bpb.SectorsPerFat != 0 && Bpb.RootEntries == 0) { + + Result = FALSE; + + // + // If this is FAT32 (i.e., extended BPB), look for and refuse to mount + // mirror-disabled volumes. If we did, we would need to only write to + // the FAT# indicated in the ActiveFat field. The only user of this is + // the FAT->FAT32 converter after the first pass of protected mode work + // (booting into realmode) and NT should absolutely not be attempting + // to mount such an in-transition volume. + // + + } else if (Bpb.SectorsPerFat == 0 && Bpb.MirrorDisabled) { + + Result = FALSE; + } + + DebugTrace(-1, Dbg, "FatIsBootSectorFat -> %08lx\n", Result); + + return Result; +} + + +// +// Local Support Routine +// + +BOOLEAN +FatIsMediaWriteProtected ( + IN PIRP_CONTEXT IrpContext, + IN PDEVICE_OBJECT TargetDeviceObject + ) + +/*++ + +Routine Description: + + This routine determines if the target media is write protected. + +Arguments: + + TargetDeviceObject - The target of the query + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + PIRP Irp; + KEVENT Event; + NTSTATUS Status; + IO_STATUS_BLOCK Iosb; + + PAGED_CODE(); + UNREFERENCED_PARAMETER( IrpContext ); + + // + // Query the partition table + // + + KeInitializeEvent( &Event, NotificationEvent, FALSE ); + + // + // See if the media is write protected. On success or any kind + // of error (possibly illegal device function), assume it is + // writeable, and only complain if he tells us he is write protected. + // + + Irp = IoBuildDeviceIoControlRequest( IOCTL_DISK_IS_WRITABLE, + TargetDeviceObject, + NULL, + 0, + NULL, + 0, + FALSE, + &Event, + &Iosb ); + + // + // Just return FALSE in the unlikely event we couldn't allocate an Irp. + // + + if ( Irp == NULL ) { + + return FALSE; + } + + SetFlag( IoGetNextIrpStackLocation( Irp )->Flags, SL_OVERRIDE_VERIFY_VOLUME ); + + Status = IoCallDriver( TargetDeviceObject, Irp ); + + if ( Status == STATUS_PENDING ) { + + (VOID) KeWaitForSingleObject( &Event, + Executive, + KernelMode, + FALSE, + (PLARGE_INTEGER)NULL ); + + Status = Iosb.Status; + } + + return (BOOLEAN)(Status == STATUS_MEDIA_WRITE_PROTECTED); +} + + +// +// Local Support Routine +// + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatUserFsCtrl ( + IN PIRP_CONTEXT IrpContext, + IN 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; + ULONG FsControlCode; + + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + PAGED_CODE(); + + // + // Save some references to make our life a little easier + // + + FsControlCode = IrpSp->Parameters.FileSystemControl.FsControlCode; + + DebugTrace(+1, Dbg,"FatUserFsCtrl...\n", 0); + DebugTrace( 0, Dbg,"FsControlCode = %08lx\n", FsControlCode); + + // + // Some of these Fs Controls use METHOD_NEITHER buffering. If the previous mode + // of the caller was userspace and this is a METHOD_NEITHER, we have the choice + // of realy buffering the request through so we can possibly post, or making the + // request synchronous. Since the former was not done by design, do the latter. + // + + if (Irp->RequestorMode != KernelMode && (FsControlCode & 3) == METHOD_NEITHER) { + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT); + } + + // + // Case on the control code. + // + + switch ( 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: +#if (NTDDI_VERSION >= NTDDI_WIN7) + case FSCTL_REQUEST_OPLOCK: +#endif + Status = FatOplockRequest( IrpContext, Irp ); + break; + + case FSCTL_LOCK_VOLUME: + + Status = FatLockVolume( IrpContext, Irp ); + break; + + case FSCTL_UNLOCK_VOLUME: + + Status = FatUnlockVolume( IrpContext, Irp ); + break; + + case FSCTL_DISMOUNT_VOLUME: + + Status = FatDismountVolume( IrpContext, Irp ); + break; + + case FSCTL_MARK_VOLUME_DIRTY: + + Status = FatDirtyVolume( IrpContext, Irp ); + break; + + case FSCTL_IS_VOLUME_DIRTY: + + Status = FatIsVolumeDirty( IrpContext, Irp ); + break; + + case FSCTL_IS_VOLUME_MOUNTED: + + Status = FatIsVolumeMounted( IrpContext, Irp ); + break; + + case FSCTL_IS_PATHNAME_VALID: + Status = FatIsPathnameValid( IrpContext, Irp ); + break; + + case FSCTL_QUERY_RETRIEVAL_POINTERS: + Status = FatQueryRetrievalPointers( IrpContext, Irp ); + break; + + case FSCTL_QUERY_FAT_BPB: + Status = FatQueryBpb( IrpContext, Irp ); + break; + + case FSCTL_FILESYSTEM_GET_STATISTICS: + Status = FatGetStatistics( IrpContext, Irp ); + break; + +#if (NTDDI_VERSION >= NTDDI_WIN7) + case FSCTL_GET_RETRIEVAL_POINTER_BASE: + Status = FatGetRetrievalPointerBase( IrpContext, Irp ); + break; + + case FSCTL_GET_BOOT_AREA_INFO: + Status = FatGetBootAreaInfo( IrpContext, Irp ); + break; +#endif + + case FSCTL_GET_VOLUME_BITMAP: + Status = FatGetVolumeBitmap( IrpContext, Irp ); + break; + + case FSCTL_GET_RETRIEVAL_POINTERS: + Status = FatGetRetrievalPointers( IrpContext, Irp ); + break; + + case FSCTL_MOVE_FILE: + Status = FatMoveFile( IrpContext, Irp ); + break; + + case FSCTL_ALLOW_EXTENDED_DASD_IO: + Status = FatAllowExtendedDasdIo( IrpContext, Irp ); + break; + + case FSCTL_MARK_HANDLE: + Status = FatMarkHandle( IrpContext, Irp ); + break; + +#if (NTDDI_VERSION >= NTDDI_WIN8) + + case FSCTL_SET_PURGE_FAILURE_MODE: + Status = FatSetPurgeFailureMode( IrpContext, Irp ); + break; + +#endif + + + case FSCTL_SET_ZERO_ON_DEALLOCATION: + Status = FatSetZeroOnDeallocate( IrpContext, Irp ); + break; + + default : + + DebugTrace(0, Dbg, "Invalid control code -> %08lx\n", FsControlCode ); + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_DEVICE_REQUEST ); + Status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + + DebugTrace(-1, Dbg, "FatUserFsCtrl -> %08lx\n", Status ); + return Status; +} + + + +// +// Local support routine +// + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatOplockRequest ( + _In_ PIRP_CONTEXT IrpContext, + _In_ 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; + ULONG FsControlCode; + PFCB Fcb; + PVCB Vcb; + PCCB Ccb; + + ULONG OplockCount = 0; + + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + BOOLEAN AcquiredVcb = FALSE; + BOOLEAN AcquiredFcb = FALSE; + +#if (NTDDI_VERSION >= NTDDI_WIN7) + PREQUEST_OPLOCK_INPUT_BUFFER InputBuffer = NULL; + ULONG InputBufferLength; + ULONG OutputBufferLength; +#endif + + TYPE_OF_OPEN TypeOfOpen; + + PAGED_CODE(); + + // + // Save some references to make our life a little easier + // + + FsControlCode = IrpSp->Parameters.FileSystemControl.FsControlCode; + + TypeOfOpen = FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb ); + + DebugTrace(+1, Dbg, "FatOplockRequest...\n", 0); + DebugTrace( 0, Dbg, "FsControlCode = %08lx\n", FsControlCode); + + // + // We permit oplock requests on files and directories. + // + + if ((TypeOfOpen != UserFileOpen) +#if (NTDDI_VERSION >= NTDDI_WIN8) + && + (TypeOfOpen != UserDirectoryOpen) +#endif + ) { + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + DebugTrace(-1, Dbg, "FatOplockRequest -> STATUS_INVALID_PARAMETER\n", 0); + return STATUS_INVALID_PARAMETER; + } + +#if (NTDDI_VERSION >= NTDDI_WIN7) + + // + // Get the input & output buffer lengths and pointers. + // + + if (FsControlCode == FSCTL_REQUEST_OPLOCK) { + + InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength; + InputBuffer = (PREQUEST_OPLOCK_INPUT_BUFFER) Irp->AssociatedIrp.SystemBuffer; + + OutputBufferLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength; + + // + // Check for a minimum length on the input and ouput buffers. + // + + if ((InputBufferLength < sizeof( REQUEST_OPLOCK_INPUT_BUFFER )) || + (OutputBufferLength < sizeof( REQUEST_OPLOCK_OUTPUT_BUFFER ))) { + + FatCompleteRequest( IrpContext, Irp, STATUS_BUFFER_TOO_SMALL ); + DebugTrace(-1, Dbg, "FatOplockRequest -> STATUS_BUFFER_TOO_SMALL\n", 0); + return STATUS_BUFFER_TOO_SMALL; + } + } + + // + // If the oplock request is on a directory it must be for a Read or Read-Handle + // oplock only. + // + + if ((TypeOfOpen == UserDirectoryOpen) && + ((FsControlCode != FSCTL_REQUEST_OPLOCK) || + !FsRtlOplockIsSharedRequest( Irp ))) { + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + DebugTrace(-1, Dbg, "FatOplockRequest -> STATUS_INVALID_PARAMETER\n", 0); + return STATUS_INVALID_PARAMETER; + } + +#endif + + // + // Make this a waitable Irpcontext so we don't fail to acquire + // the resources. + // + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ); + + // + // Use a try finally to free the Fcb/Vcb + // + + try { + + // + // We grab the Fcb exclusively for oplock requests, shared for oplock + // break acknowledgement. + // + + if ((FsControlCode == FSCTL_REQUEST_OPLOCK_LEVEL_1) || + (FsControlCode == FSCTL_REQUEST_BATCH_OPLOCK) || + (FsControlCode == FSCTL_REQUEST_FILTER_OPLOCK) || + (FsControlCode == FSCTL_REQUEST_OPLOCK_LEVEL_2) +#if (NTDDI_VERSION >= NTDDI_WIN7) + || + ((FsControlCode == FSCTL_REQUEST_OPLOCK) && FlagOn( InputBuffer->Flags, REQUEST_OPLOCK_INPUT_FLAG_REQUEST )) +#endif + ) { + + FatAcquireSharedVcb( IrpContext, Fcb->Vcb ); + AcquiredVcb = TRUE; + FatAcquireExclusiveFcb( IrpContext, Fcb ); + AcquiredFcb = TRUE; + +#if (NTDDI_VERSION >= NTDDI_WIN7) + if (FsRtlOplockIsSharedRequest( Irp )) { +#else + if (FsControlCode == FSCTL_REQUEST_OPLOCK_LEVEL_2) { +#endif + + // + // Byte-range locks are only valid on files. + // + + if (TypeOfOpen == UserFileOpen) { + + // + // Set OplockCount to nonzero if FsRtl denies access + // based on current byte-range lock state. + // + +#if (NTDDI_VERSION >= NTDDI_WIN8) + OplockCount = (ULONG) !FsRtlCheckLockForOplockRequest( &Fcb->Specific.Fcb.FileLock, &Fcb->Header.AllocationSize ); +#elif (NTDDI_VERSION >= NTDDI_WIN7) + OplockCount = (ULONG) FsRtlAreThereCurrentOrInProgressFileLocks( &Fcb->Specific.Fcb.FileLock ); +#else + OplockCount = (ULONG) FsRtlAreThereCurrentFileLocks( &Fcb->Specific.Fcb.FileLock ); +#endif + + } + + } else { + + OplockCount = Fcb->UncleanCount; + } + + } else if ((FsControlCode == FSCTL_OPLOCK_BREAK_ACKNOWLEDGE) || + (FsControlCode == FSCTL_OPBATCH_ACK_CLOSE_PENDING) || + (FsControlCode == FSCTL_OPLOCK_BREAK_NOTIFY) || + (FsControlCode == FSCTL_OPLOCK_BREAK_ACK_NO_2) +#if (NTDDI_VERSION >= NTDDI_WIN7) + || + ((FsControlCode == FSCTL_REQUEST_OPLOCK) && FlagOn( InputBuffer->Flags, REQUEST_OPLOCK_INPUT_FLAG_ACK )) +#endif + ) { + + FatAcquireSharedFcb( IrpContext, Fcb ); + AcquiredFcb = TRUE; +#if (NTDDI_VERSION >= NTDDI_WIN7) + } else if (FsControlCode == FSCTL_REQUEST_OPLOCK) { + + // + // The caller didn't provide either REQUEST_OPLOCK_INPUT_FLAG_REQUEST or + // REQUEST_OPLOCK_INPUT_FLAG_ACK on the input buffer. + // + + try_leave( Status = STATUS_INVALID_PARAMETER ); + + } else { +#else + } else { +#endif + +#pragma prefast( suppress:28159, "things are seriously wrong if we get here" ) + FatBugCheck( FsControlCode, 0, 0 ); + } + + // + // Fail batch, filter, and handle oplock requests if the file is marked + // for delete. + // + + if (((FsControlCode == FSCTL_REQUEST_FILTER_OPLOCK) || + (FsControlCode == FSCTL_REQUEST_BATCH_OPLOCK) +#if (NTDDI_VERSION >= NTDDI_WIN7) + || + ((FsControlCode == FSCTL_REQUEST_OPLOCK) && FlagOn( InputBuffer->RequestedOplockLevel, OPLOCK_LEVEL_CACHE_HANDLE )) +#endif + ) && + FlagOn( Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE )) { + + try_leave( Status = STATUS_DELETE_PENDING ); + } + + // + // Call the FsRtl routine to grant/acknowledge oplock. + // + + Status = FsRtlOplockFsctrl( FatGetFcbOplock(Fcb), + Irp, + OplockCount ); + + // + // Once we call FsRtlOplockFsctrl, we no longer own the IRP and we should not complete it. + // + + Irp = NULL; + + // + // Set the flag indicating if Fast I/O is possible + // + + Fcb->Header.IsFastIoPossible = FatIsFastIoPossible( Fcb ); + + } finally { + + DebugUnwind( FatOplockRequest ); + + // + // Release all of our resources + // + + if (AcquiredVcb) { + + FatReleaseVcb( IrpContext, Fcb->Vcb ); + } + + if (AcquiredFcb) { + + FatReleaseFcb( IrpContext, Fcb ); + } + + DebugTrace(-1, Dbg, "FatOplockRequest -> %08lx\n", Status ); + } + + FatCompleteRequest( IrpContext, Irp, Status ); + + return Status; +} + + +// +// Local Support Routine +// + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatLockVolume ( + IN PIRP_CONTEXT IrpContext, + IN 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; + + PVCB Vcb; + PFCB Fcb; + PCCB Ccb; + + PAGED_CODE(); + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "FatLockVolume...\n", 0); + + // + // Decode the file object, the only type of opens we accept are + // user volume opens. + // + + if (FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb ) != UserVolumeOpen) { + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + + DebugTrace(-1, Dbg, "FatLockVolume -> %08lx\n", STATUS_INVALID_PARAMETER); + return STATUS_INVALID_PARAMETER; + } + + if ((Ccb == NULL) || !FlagOn( Ccb->Flags, CCB_FLAG_MANAGE_VOLUME_ACCESS )) { + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + + DebugTrace(-1, Dbg, "FatLockVolume -> %08lx\n", 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 and enqueue the Irp if we + // didn't get access. + // + + if (!FatAcquireExclusiveVcb( IrpContext, Vcb )) { + + DebugTrace( 0, Dbg, "Cannot acquire Vcb\n", 0); + + Status = FatFsdPostRequest( IrpContext, Irp ); + + DebugTrace(-1, Dbg, "FatUnlockVolume -> %08lx\n", Status); + return Status; + } + + try { + + Status = FatLockVolumeInternal( IrpContext, Vcb, IrpSp->FileObject ); + + } finally { + + // + // Since we drop and release the vcb while trying to punch the volume + // down, it may be the case that we decide the operation should not + // continue if the user raced a CloeseHandle() with us (and it finished + // the cleanup) while we were waiting for our closes to finish. + // + // In this case, we will have been raised out of the acquire logic with + // STATUS_FILE_CLOSED, and the volume will not be held. + // + + if (!AbnormalTermination() || ExIsResourceAcquiredExclusiveLite( &Vcb->Resource )) { + + FatReleaseVcb( IrpContext, Vcb ); + } + + if (!NT_SUCCESS( Status ) || AbnormalTermination()) { + + // + // The volume lock will be failing. + // + + FsRtlNotifyVolumeEvent( IrpSp->FileObject, FSRTL_VOLUME_LOCK_FAILED ); + } + } + + FatCompleteRequest( IrpContext, Irp, Status ); + + DebugTrace(-1, Dbg, "FatLockVolume -> %08lx\n", Status); + + return Status; +} + + +// +// Local Support Routine +// + +NTSTATUS +FatUnlockVolume ( + IN PIRP_CONTEXT IrpContext, + IN 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; + + PVCB Vcb; + PFCB Fcb; + PCCB Ccb; + + PAGED_CODE(); + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "FatUnlockVolume...\n", 0); + + // + // Decode the file object, the only type of opens we accept are + // user volume opens. + // + + if (FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb ) != UserVolumeOpen) { + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + + DebugTrace(-1, Dbg, "FatUnlockVolume -> %08lx\n", STATUS_INVALID_PARAMETER); + return STATUS_INVALID_PARAMETER; + } + + if ((Ccb == NULL) || !FlagOn( Ccb->Flags, CCB_FLAG_MANAGE_VOLUME_ACCESS )) { + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + + DebugTrace(-1, Dbg, "FatUnlockVolume -> %08lx\n", STATUS_INVALID_PARAMETER); + return STATUS_INVALID_PARAMETER; + } + + Status = FatUnlockVolumeInternal( IrpContext, Vcb, IrpSp->FileObject ); + + // + // Send notification that the volume is avaliable. + // + + if (NT_SUCCESS( Status )) { + + FsRtlNotifyVolumeEvent( IrpSp->FileObject, FSRTL_VOLUME_UNLOCK ); + } + + FatCompleteRequest( IrpContext, Irp, Status ); + + DebugTrace(-1, Dbg, "FatUnlockVolume -> %08lx\n", Status); + + return Status; +} + + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatLockVolumeInternal ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PFILE_OBJECT FileObject OPTIONAL + ) + +/*++ + +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 = STATUS_SUCCESS; + KIRQL SavedIrql; + ULONG RemainingUserReferences = (FileObject? 1: 0); + + NT_ASSERT( ExIsResourceAcquiredExclusiveLite( &Vcb->Resource ) && + !ExIsResourceAcquiredExclusiveLite( &FatData.Resource )); + // + // Go synchronous for the rest of the lock operation. It may be + // reasonable to try to revisit this in the future, but for now + // the purge below expects to be able to wait. + // + // We know it is OK to leave the flag up given how we're used at + // the moment. + // + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ); + + // + // If there are any open handles, this will fail. + // + + if (!FatIsHandleCountZero( IrpContext, Vcb )) { + + return STATUS_ACCESS_DENIED; + } + + // + // Force Mm to get rid of its referenced file objects. + // + + FatFlushFat( IrpContext, Vcb ); + + FatPurgeReferencedFileObjects( IrpContext, Vcb->RootDcb, Flush ); + + FatCloseEaFile( IrpContext, Vcb, TRUE ); + + // + // 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 flushed, 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. + // + + FatReleaseVcb( IrpContext, Vcb ); + + Status = CcWaitForCurrentLazyWriterActivity(); + + FatAcquireExclusiveVcb( IrpContext, Vcb ); + + if (!NT_SUCCESS( Status )) { + + return Status; + } + + // + // The act of closing and purging may have touched pages in various + // parent DCBs. We handle this by purging a second time. + // + + FatPurgeReferencedFileObjects( IrpContext, Vcb->RootDcb, Flush ); + + FatReleaseVcb( IrpContext, Vcb ); + + Status = CcWaitForCurrentLazyWriterActivity(); + + FatAcquireExclusiveVcb( IrpContext, Vcb ); + + if (!NT_SUCCESS( Status )) { + + return Status; + } + + // + // Now rundown the delayed closes one last time. We appear to be able + // to have additional collisions. + // + + FatFspClose( Vcb ); + + // + // Check if the Vcb is already locked, or if the open file count + // is greater than 1 (which implies that someone else also is + // currently using the volume, or a file on the volume), and that the + // VPB reference count only includes our residual and the handle (as + // appropriate). + // + // We used to only check for the vpb refcount. This is unreliable since + // the vpb refcount is dropped immediately before final close, meaning + // that even though we had a good refcount, the close was inflight and + // subsequent operations could get confused. Especially if the PNP path + // was the lock caller, we delete the VCB with an outstanding opencount! + // + + IoAcquireVpbSpinLock( &SavedIrql ); + + if (!FlagOn(Vcb->Vpb->Flags, VPB_LOCKED) && + (Vcb->Vpb->ReferenceCount <= 2 + RemainingUserReferences) && + (Vcb->OpenFileCount == (CLONG)( FileObject? 1: 0 ))) { + + SetFlag(Vcb->Vpb->Flags, (VPB_LOCKED | VPB_DIRECT_WRITES_ALLOWED)); + SetFlag(Vcb->VcbState, VCB_STATE_FLAG_LOCKED); + Vcb->FileObjectWithVcbLocked = FileObject; + + } else { + + Status = STATUS_ACCESS_DENIED; + } + + IoReleaseVpbSpinLock( SavedIrql ); + + // + // If we successully locked the volume, see if it is clean now. + // + + if (NT_SUCCESS( Status ) && + FlagOn( Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DIRTY ) && + !FlagOn( Vcb->VcbState, VCB_STATE_FLAG_MOUNTED_DIRTY ) && + !CcIsThereDirtyData(Vcb->Vpb)) { + + FatMarkVolume( IrpContext, Vcb, VolumeClean ); + ClearFlag( Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DIRTY ); + } + + NT_ASSERT( !NT_SUCCESS(Status) || (Vcb->OpenFileCount == (CLONG)( FileObject? 1: 0 ))); + + return Status; +} + + +NTSTATUS +FatUnlockVolumeInternal ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PFILE_OBJECT FileObject OPTIONAL + ) + +/*++ + +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. + +--*/ + +{ + KIRQL SavedIrql; + NTSTATUS Status = STATUS_NOT_LOCKED; + + UNREFERENCED_PARAMETER( IrpContext ); + + IoAcquireVpbSpinLock( &SavedIrql ); + + if (FlagOn(Vcb->Vpb->Flags, VPB_LOCKED) && FileObject == Vcb->FileObjectWithVcbLocked) { + + // + // This one locked it, unlock the volume + // + + ClearFlag( Vcb->Vpb->Flags, (VPB_LOCKED | VPB_DIRECT_WRITES_ALLOWED) ); + ClearFlag( Vcb->VcbState, VCB_STATE_FLAG_LOCKED ); + Vcb->FileObjectWithVcbLocked = NULL; + + Status = STATUS_SUCCESS; + } + + IoReleaseVpbSpinLock( SavedIrql ); + + return Status; +} + + +// +// Local Support Routine +// + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatDismountVolume ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the dismount 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; + NTSTATUS Status = STATUS_SUCCESS; + BOOLEAN VcbHeld = FALSE; + KIRQL SavedIrql; + + PVCB Vcb; + PFCB Fcb; + PCCB Ccb; + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "FatDismountVolume...\n", 0); + + // + // Decode the file object, the only type of opens we accept are + // user volume opens on media that is not boot/paging and is not + // already dismounted ... (but we need to check that stuff while + // synchronized) + // + + if (FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb ) != UserVolumeOpen) { + + Status = STATUS_INVALID_PARAMETER; + goto fn_return; + } + + if ((Ccb == NULL) || !FlagOn( Ccb->Flags, CCB_FLAG_MANAGE_VOLUME_ACCESS )) { + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + + DebugTrace(-1, Dbg, "FatDismountVolume -> %08lx\n", STATUS_INVALID_PARAMETER); + return STATUS_INVALID_PARAMETER; + } + + // + // Make some unsynchronized checks to see if this operation is possible. + // We will repeat the appropriate ones inside synchronization, but it is + // good to avoid bogus notifications. + // + + if (FlagOn( Vcb->VcbState, VCB_STATE_FLAG_BOOT_OR_PAGING_FILE )) { + + Status = STATUS_ACCESS_DENIED; + goto fn_return; + } + + if (FlagOn( Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DISMOUNTED )) { + + Status = STATUS_VOLUME_DISMOUNTED; + goto fn_return; + } + + // + // A bit of historical comment is in order. + // + // In all versions prior to NT5, we only permitted dismount if the volume had + // previously been locked. Now we must permit a forced dismount, meaning that + // we grab ahold of the whole kit-n-kaboodle - regardless of activity, open + // handles, etc. - to flush and invalidate the volume. + // + // Previously, dismount assumed that lock had come along earlier and done some + // of the work that we are now going to do - i.e., flush, tear down the eas. All + // we had to do here is flush the device out and kill off as many of the orphan + // fcbs as possible. This now changes. + // + // In fact, everything is a forced dismount now. This changes one interesting + // aspect, which is that it used to be the case that the handle used to dismount + // could come back, read, and induce a verify/remount. This is just not possible + // now. The point of forced dismount is that very shortly someone will come along + // and be destructive to the possibility of using the media further - format, eject, + // etc. By using this path, callers are expected to tolerate the consequences. + // + // Note that the volume can still be successfully unlocked by this handle. + // + + // + // Send notification. + // + + FsRtlNotifyVolumeEvent( IrpSp->FileObject, FSRTL_VOLUME_DISMOUNT ); + + // + // Force ourselves to wait and grab everything. + // + + SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT); + +#pragma prefast( push ) +#pragma prefast( disable: 28137, "prefast wants the wait to be a constant, but that isn't possible for the way fastfat is designed" ) +#pragma prefast( disable: 28193, "this will always wait" ) + + (VOID)FatAcquireExclusiveGlobal( IrpContext ); + +#pragma prefast( pop ) + + try { + + // + // Guess what? This can raise if a cleanup on the fileobject we + // got races in ahead of us. + // + + FatAcquireExclusiveVolume( IrpContext, Vcb ); + VcbHeld = TRUE; + + if (FlagOn( Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DISMOUNTED )) { + + try_return( Status = STATUS_VOLUME_DISMOUNTED ); + } + + FatFlushAndCleanVolume( IrpContext, Irp, Vcb, FlushAndInvalidate ); + + // + // We defer the physical dismount until this handle is closed, per symmetric + // implemntation in the other FS. This permits a dismounter to issue IOCTL + // through this handle and perform device manipulation without racing with + // creates attempting to mount the volume again. + // + // Raise a flag to tell the cleanup path to complete the dismount. + // + + SetFlag( Ccb->Flags, CCB_FLAG_COMPLETE_DISMOUNT ); + + // + // Indicate that the volume was dismounted so that we may return the + // correct error code when operations are attempted via open handles. + // + + FatSetVcbCondition( Vcb, VcbBad); + + SetFlag( Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DISMOUNTED ); + + // + // Set a flag in the VPB to let others know that direct volume access is allowed. + // + + IoAcquireVpbSpinLock( &SavedIrql ); + SetFlag( Vcb->Vpb->Flags, VPB_DIRECT_WRITES_ALLOWED ); + IoReleaseVpbSpinLock( SavedIrql ); + + Status = STATUS_SUCCESS; + + try_exit: NOTHING; + + } finally { + +#if (NTDDI_VERSION >= NTDDI_WIN8) + + FsRtlDismountComplete( Vcb->TargetDeviceObject, Status ); + +#endif + + if (VcbHeld) { + + FatReleaseVolume( IrpContext, Vcb ); + } + + FatReleaseGlobal( IrpContext ); + + // + // I do not believe it is possible to raise, but for completeness + // notice and send notification of failure. We absolutely + // cannot have raised in CheckForDismount. + // + // We decline to call an attempt to dismount a dismounted volume + // a failure to do so. + // + + if ((!NT_SUCCESS( Status ) && Status != STATUS_VOLUME_DISMOUNTED) + || AbnormalTermination()) { + + FsRtlNotifyVolumeEvent( IrpSp->FileObject, FSRTL_VOLUME_DISMOUNT_FAILED ); + } + } + + fn_return: + + FatCompleteRequest( IrpContext, Irp, Status ); + DebugTrace(-1, Dbg, "FatDismountVolume -> %08lx\n", Status); + return Status; +} + + +// +// Local Support Routine +// + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatDirtyVolume ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine marks the volume as dirty. + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + PIO_STACK_LOCATION IrpSp; + + PVCB Vcb; + PFCB Fcb; + PCCB Ccb; + + PAGED_CODE(); + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "FatDirtyVolume...\n", 0); + + // + // Decode the file object, the only type of opens we accept are + // user volume opens. + // + + if (FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb ) != UserVolumeOpen) { + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + + DebugTrace(-1, Dbg, "FatDirtyVolume -> %08lx\n", STATUS_INVALID_PARAMETER); + return STATUS_INVALID_PARAMETER; + } + + if ((Ccb == NULL) || !FlagOn( Ccb->Flags, CCB_FLAG_MANAGE_VOLUME_ACCESS )) { + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + + DebugTrace(-1, Dbg, "FatDirtyVolume -> %08lx\n", STATUS_INVALID_PARAMETER); + return STATUS_INVALID_PARAMETER; + } + + + // + // Disable popups, we will just return any error. + // + + SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_POPUPS); + + // + // Verify the Vcb. We want to make sure we don't dirty some + // random chunk of media that happens to be in the drive now. + // + + FatVerifyVcb( IrpContext, Vcb ); + + SetFlag( Vcb->VcbState, VCB_STATE_FLAG_MOUNTED_DIRTY ); + + FatMarkVolume( IrpContext, Vcb, VolumeDirty ); + + FatCompleteRequest( IrpContext, Irp, STATUS_SUCCESS ); + + DebugTrace(-1, Dbg, "FatDirtyVolume -> STATUS_SUCCESS\n", 0); + + return STATUS_SUCCESS; +} + + +// +// Local Support Routine +// + +NTSTATUS +FatIsVolumeDirty ( + IN PIRP_CONTEXT IrpContext, + IN 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; + PVCB Vcb; + 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. Look at the system buffer field in the + // irp first. Then the Irp Mdl. + // + + if (Irp->AssociatedIrp.SystemBuffer != NULL) { + + VolumeState = Irp->AssociatedIrp.SystemBuffer; + + } else if (Irp->MdlAddress != NULL) { + + VolumeState = MmGetSystemAddressForMdlSafe( Irp->MdlAddress, LowPagePriority ); + + if (VolumeState == NULL) { + + FatCompleteRequest( IrpContext, Irp, STATUS_INSUFFICIENT_RESOURCES ); + return STATUS_INSUFFICIENT_RESOURCES; + } + + } else { + + FatCompleteRequest( 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)) { + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + return STATUS_INVALID_PARAMETER; + } + + *VolumeState = 0; + + // + // Decode the file object + // + + TypeOfOpen = FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb ); + + if (TypeOfOpen != UserVolumeOpen) { + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + return STATUS_INVALID_PARAMETER; + } + + if (Vcb->VcbCondition != VcbGood) { + + FatCompleteRequest( IrpContext, Irp, STATUS_VOLUME_DISMOUNTED ); + return STATUS_VOLUME_DISMOUNTED; + } + + // + // Disable PopUps, we want to return any error. + // + + SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_POPUPS); + + // + // Verify the Vcb. We want to make double sure that this volume + // is around so that we know our information is good. + // + + FatVerifyVcb( IrpContext, Vcb ); + + // + // Now set the returned information. We can avoid probing the disk since + // we know our internal state is in sync. + // + + if ( FlagOn(Vcb->VcbState, VCB_STATE_FLAG_MOUNTED_DIRTY) ) { + + SetFlag( *VolumeState, VOLUME_IS_DIRTY ); + } + + Irp->IoStatus.Information = sizeof( ULONG ); + + FatCompleteRequest( IrpContext, Irp, STATUS_SUCCESS ); + return STATUS_SUCCESS; +} + + +// +// Local Support Routine +// + +NTSTATUS +FatIsVolumeMounted ( + IN PIRP_CONTEXT IrpContext, + IN 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 + +--*/ + +{ + NTSTATUS Status; + + PIO_STACK_LOCATION IrpSp; + + PVCB Vcb = NULL; + PFCB Fcb; + PCCB Ccb; + + PAGED_CODE(); + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + Status = STATUS_SUCCESS; + + DebugTrace(+1, Dbg, "FatIsVolumeMounted...\n", 0); + + // + // Decode the file object. + // + + (VOID)FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb ); + + NT_ASSERT( Vcb != NULL ); + _Analysis_assume_( Vcb != NULL ); + + // + // Disable PopUps, we want to return any error. + // + + SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_POPUPS); + + // + // Verify the Vcb. + // + + FatVerifyVcb( IrpContext, Vcb ); + + FatCompleteRequest( IrpContext, Irp, Status ); + + DebugTrace(-1, Dbg, "FatIsVolumeMounted -> %08lx\n", Status); + + return Status; +} + + +// +// Local Support Routine +// + +NTSTATUS +FatIsPathnameValid ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine determines if a pathname is a-priori illegal by inspecting + the the characters used. It is required to be correct on a FALSE return. + + N.B.: current implementation is intentioanlly a no-op. This may change + in the future. A careful reader of the previous implementation of this + FSCTL in FAT would discover that it violated the requirement stated above + and could return FALSE for a valid (createable) pathname. + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatIsPathnameValid...\n", 0); + + FatCompleteRequest( IrpContext, Irp, STATUS_SUCCESS ); + + DebugTrace(-1, Dbg, "FatIsPathnameValid -> %08lx\n", STATUS_SUCCESS); + + return STATUS_SUCCESS; +} + + +// +// Local Support Routine +// + +NTSTATUS +FatQueryBpb ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine simply returns the first 0x24 bytes of sector 0. + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + PIO_STACK_LOCATION IrpSp; + + PVCB Vcb; + + PFSCTL_QUERY_FAT_BPB_BUFFER BpbBuffer; + + PAGED_CODE(); + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "FatQueryBpb...\n", 0); + + // + // Get the Vcb. If we didn't keep the information needed for this call, + // we had a reason ... + // + + Vcb = &((PVOLUME_DEVICE_OBJECT)IrpSp->DeviceObject)->Vcb; + + if (Vcb->First0x24BytesOfBootSector == NULL) { + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_DEVICE_REQUEST ); + DebugTrace(-1, Dbg, "FatQueryBpb -> %08lx\n", STATUS_INVALID_DEVICE_REQUEST ); + return STATUS_INVALID_DEVICE_REQUEST; + } + + // + // Extract the buffer + // + + BpbBuffer = (PFSCTL_QUERY_FAT_BPB_BUFFER)Irp->AssociatedIrp.SystemBuffer; + + // + // Make sure the buffer is big enough. + // + + if (IrpSp->Parameters.FileSystemControl.OutputBufferLength < 0x24) { + + FatCompleteRequest( IrpContext, Irp, STATUS_BUFFER_TOO_SMALL ); + DebugTrace(-1, Dbg, "FatQueryBpb -> %08lx\n", STATUS_BUFFER_TOO_SMALL ); + return STATUS_BUFFER_TOO_SMALL; + } + + // + // Fill in the output buffer + // + + RtlCopyMemory( BpbBuffer->First0x24BytesOfBootSector, + Vcb->First0x24BytesOfBootSector, + 0x24 ); + + Irp->IoStatus.Information = 0x24; + + FatCompleteRequest( IrpContext, Irp, STATUS_SUCCESS ); + DebugTrace(-1, Dbg, "FatQueryBpb -> %08lx\n", STATUS_SUCCESS); + return STATUS_SUCCESS; +} + + +// +// Local Support Routine +// + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatInvalidateVolumes ( + IN 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; + IRP_CONTEXT IrpContext; + PIO_STACK_LOCATION IrpSp; + + LUID TcbPrivilege = {SE_TCB_PRIVILEGE, 0}; + + HANDLE Handle; + + PLIST_ENTRY Links; + + PFILE_OBJECT FileToMarkBad; + PDEVICE_OBJECT DeviceToMarkBad; + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "FatInvalidateVolumes...\n", 0); + + // + // Check for the correct security access. + // The caller must have the SeTcbPrivilege. + // + + if (!SeSinglePrivilegeCheck(TcbPrivilege, Irp->RequestorMode)) { + + FatCompleteRequest( FatNull, Irp, STATUS_PRIVILEGE_NOT_HELD ); + + DebugTrace(-1, Dbg, "FatInvalidateVolumes -> %08lx\n", 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) && defined(BUILD_WOW64_ENABLED) + if (IoIs32bitProcess( Irp )) { + + if (IrpSp->Parameters.FileSystemControl.InputBufferLength != sizeof(UINT32)) { + + FatCompleteRequest( FatNull, Irp, STATUS_INVALID_PARAMETER ); + + DebugTrace(-1, Dbg, "FatInvalidateVolumes -> %08lx\n", STATUS_INVALID_PARAMETER); + return STATUS_INVALID_PARAMETER; + } + + Handle = (HANDLE) LongToHandle( (*(PUINT32)Irp->AssociatedIrp.SystemBuffer) ); + } else { +#endif + if (IrpSp->Parameters.FileSystemControl.InputBufferLength != sizeof(HANDLE)) { + + FatCompleteRequest( FatNull, Irp, STATUS_INVALID_PARAMETER ); + + DebugTrace(-1, Dbg, "FatInvalidateVolumes -> %08lx\n", STATUS_INVALID_PARAMETER); + return STATUS_INVALID_PARAMETER; + } + + Handle = *(PHANDLE)Irp->AssociatedIrp.SystemBuffer; +#if defined(_WIN64) && defined(BUILD_WOW64_ENABLED) + } +#endif + + + Status = ObReferenceObjectByHandle( Handle, + 0, + *IoFileObjectType, + KernelMode, + &FileToMarkBad, + NULL ); + + if (!NT_SUCCESS(Status)) { + + FatCompleteRequest( FatNull, Irp, Status ); + + DebugTrace(-1, Dbg, "FatInvalidateVolumes -> %08lx\n", Status); + return Status; + + } else { + + // + // We only needed the pointer, not a reference. + // + + ObDereferenceObject( FileToMarkBad ); + + // + // Grab the DeviceObject from the FileObject. + // + + DeviceToMarkBad = FileToMarkBad->DeviceObject; + } + + RtlZeroMemory( &IrpContext, sizeof(IRP_CONTEXT) ); + + SetFlag( IrpContext.Flags, IRP_CONTEXT_FLAG_WAIT ); + IrpContext.MajorFunction = IrpSp->MajorFunction; + IrpContext.MinorFunction = IrpSp->MinorFunction; + +#pragma prefast( push ) +#pragma prefast( disable: 28137, "prefast wants the wait to be a constant, but that isn't possible for the way fastfat is designed" ) +#pragma prefast( disable: 28193, "this will always wait" ) + + FatAcquireExclusiveGlobal( &IrpContext ); + +#pragma prefast( pop ) + + // + // First acquire the FatData resource shared, then walk through all the + // mounted VCBs looking for candidates to mark BAD. + // + // On volumes we mark bad, check for dismount possibility (which is + // why we have to get the next link early). + // + + Links = FatData.VcbQueue.Flink; + + while (Links != &FatData.VcbQueue) { + + PVCB ExistingVcb; + + ExistingVcb = 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. + // + + if (ExistingVcb->Vpb->RealDevice == DeviceToMarkBad) { + + BOOLEAN VcbDeleted = FALSE; + + // + // Here we acquire the Vcb exclusive and try to purge + // all the open files. The idea is to have as little as + // possible stale data visible and to hasten the volume + // going away. + // + + (VOID)FatAcquireExclusiveVcb( &IrpContext, ExistingVcb ); + +#pragma prefast( push ) +#pragma prefast( disable: 28175, "touching Vpb is ok for a filesystem" ) + + if (ExistingVcb->Vpb == DeviceToMarkBad->Vpb) { + + KIRQL OldIrql; + + IoAcquireVpbSpinLock( &OldIrql ); + + if (FlagOn( DeviceToMarkBad->Vpb->Flags, VPB_MOUNTED )) { + + PVPB NewVpb; + + NewVpb = ExistingVcb->SwapVpb; + ExistingVcb->SwapVpb = NULL; + SetFlag( ExistingVcb->VcbState, VCB_STATE_FLAG_VPB_MUST_BE_FREED ); + + RtlZeroMemory( NewVpb, sizeof( VPB ) ); + NewVpb->Type = IO_TYPE_VPB; + NewVpb->Size = sizeof( VPB ); + NewVpb->RealDevice = DeviceToMarkBad; + NewVpb->Flags = FlagOn( DeviceToMarkBad->Vpb->Flags, VPB_REMOVE_PENDING ); + + DeviceToMarkBad->Vpb = NewVpb; + } + + NT_ASSERT( DeviceToMarkBad->Vpb->DeviceObject == NULL ); + +#pragma prefast( pop ) + + IoReleaseVpbSpinLock( OldIrql ); + } + + FatSetVcbCondition( ExistingVcb, VcbBad ); + + // + // Process the root directory, if it is present. + // + + if (ExistingVcb->RootDcb != NULL) { + + // + // In order to safely mark all FCBs bad, we must acquire everything. + // + + FatAcquireExclusiveVolume(&IrpContext, ExistingVcb); + FatMarkFcbCondition( &IrpContext, ExistingVcb->RootDcb, FcbBad, TRUE ); + FatReleaseVolume(&IrpContext, ExistingVcb); + + // + // Purging the file objects on this volume could result in the memory manager + // dereferencing it's file pointer which could be the last reference and + // trigger object deletion and VCB deletion. Protect against that here by + // temporarily biasing the file count, and later checking for dismount. + // + + ExistingVcb->OpenFileCount += 1; + + FatPurgeReferencedFileObjects( &IrpContext, + ExistingVcb->RootDcb, + NoFlush ); + + ExistingVcb->OpenFileCount -= 1; + + VcbDeleted = FatCheckForDismount( &IrpContext, ExistingVcb, FALSE ); + } + + // + // Only release the VCB if it did not go away. + // + + if (!VcbDeleted) { + + FatReleaseVcb( &IrpContext, ExistingVcb ); + } + } + } + + FatReleaseGlobal( &IrpContext ); + + FatCompleteRequest( FatNull, Irp, STATUS_SUCCESS ); + + DebugTrace(-1, Dbg, "FatInvalidateVolumes -> STATUS_SUCCESS\n", 0); + + return STATUS_SUCCESS; +} + + +// +// Local Support routine +// + +BOOLEAN +FatPerformVerifyDiskRead ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PVOID Buffer, + IN LBO Lbo, + IN ULONG NumberOfBytesToRead, + IN BOOLEAN ReturnOnError + ) + +/*++ + +Routine Description: + + This routine is used to read in a range of bytes from the disk. It + bypasses all of the caching and regular I/O logic, and builds and issues + the requests itself. It does this operation overriding the verify + volume flag in the device object. + +Arguments: + + Vcb - Supplies the target device object for this operation. + + Buffer - Supplies the buffer that will recieve the results of this operation + + Lbo - Supplies the byte offset of where to start reading + + NumberOfBytesToRead - Supplies the number of bytes to read, this must + be in multiple of bytes units acceptable to the disk driver. + + ReturnOnError - Indicates that we should return on an error, instead + of raising. + +Return Value: + + BOOLEAN - TRUE if the operation succeded, FALSE otherwise. + +--*/ + +{ + KEVENT Event; + PIRP Irp; + LARGE_INTEGER ByteOffset; + NTSTATUS Status; + IO_STATUS_BLOCK Iosb; + + PAGED_CODE(); + + DebugTrace(0, Dbg, "FatPerformVerifyDiskRead, Lbo = %08lx\n", Lbo ); + + // + // Initialize the event we're going to use + // + + KeInitializeEvent( &Event, NotificationEvent, FALSE ); + + // + // Build the irp for the operation and also set the overrride flag + // + + ByteOffset.QuadPart = Lbo; + + Irp = IoBuildSynchronousFsdRequest( IRP_MJ_READ, + Vcb->TargetDeviceObject, + Buffer, + NumberOfBytesToRead, + &ByteOffset, + &Event, + &Iosb ); + + if ( Irp == NULL ) { + + FatRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES ); + } + + SetFlag( IoGetNextIrpStackLocation( Irp )->Flags, SL_OVERRIDE_VERIFY_VOLUME ); + + // + // Call the device to do the read and wait for it to finish. + // + + Status = IoCallDriver( Vcb->TargetDeviceObject, Irp ); + + if (Status == STATUS_PENDING) { + + (VOID)KeWaitForSingleObject( &Event, Executive, KernelMode, FALSE, (PLARGE_INTEGER)NULL ); + + Status = Iosb.Status; + } + + NT_ASSERT( Status != STATUS_VERIFY_REQUIRED ); + + // + // Special case this error code because this probably means we used + // the wrong sector size and we want to reject STATUS_WRONG_VOLUME. + // + + if (Status == STATUS_INVALID_PARAMETER) { + + return FALSE; + } + + // + // If it doesn't succeed then either return or raise the error. + // + + if (!NT_SUCCESS(Status)) { + + if (ReturnOnError) { + + return FALSE; + + } else { + + FatNormalizeAndRaiseStatus( IrpContext, Status ); + } + } + + // + // And return to our caller + // + + return TRUE; +} + + +// +// Local Support Routine +// + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatQueryRetrievalPointers ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the query retrieval pointers operation. + It returns the retrieval pointers for the specified input + file from the start of the file to the request map size specified + in the input buffer. + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + + PIO_STACK_LOCATION IrpSp; + + PVCB Vcb; + PFCB Fcb; + PCCB Ccb; + + PLARGE_INTEGER RequestedMapSize; + PLARGE_INTEGER *MappingPairs; + + ULONG Index; + ULONG i; + ULONG SectorCount; + LBO Lbo; + ULONG Vbo; + ULONG MapSize; + BOOLEAN Result; + + PAGED_CODE(); + + // + // Get the current stack location + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + // + // Make this a synchronous IRP because we need access to the input buffer and + // this Irp is marked METHOD_NEITHER. + // + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ); + + // + // Decode the file object and ensure that it is the paging file + // + // Only Kernel mode clients may query retrieval pointer information about + // a file. Ensure that this is the case for this caller. + // + + (VOID)FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb ); + + if (Irp->RequestorMode != KernelMode || + Fcb == NULL || + !FlagOn(Fcb->FcbState, FCB_STATE_PAGING_FILE) ) { + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + return STATUS_INVALID_PARAMETER; + } + + // + // Extract the input and output buffer information. The input contains + // the requested size of the mappings in terms of VBO. The output + // parameter will receive a pointer to nonpaged pool where the mapping + // pairs are stored. + // + + NT_ASSERT( IrpSp->Parameters.FileSystemControl.InputBufferLength == sizeof(LARGE_INTEGER) ); + NT_ASSERT( IrpSp->Parameters.FileSystemControl.OutputBufferLength == sizeof(PVOID) ); + + RequestedMapSize = IrpSp->Parameters.FileSystemControl.Type3InputBuffer; + MappingPairs = Irp->UserBuffer; + + // + // Acquire exclusive access to the Fcb + // + + FatAcquireExclusiveFcb( IrpContext, Fcb ); + + try { + + // + // Verify the Fcb is still OK + // + + FatVerifyFcb( IrpContext, Fcb ); + + // + // Check if the mapping the caller requested is too large + // + + if ((*RequestedMapSize).QuadPart > Fcb->Header.FileSize.QuadPart) { + + try_leave( Status = STATUS_INVALID_PARAMETER ); + } + + // + // Now get the index for the mcb entry that will contain the + // callers request and allocate enough pool to hold the + // output mapping pairs. Mapping should always be present, but handle + // the case where it isn't. + // + + Result = FatLookupMcbEntry( Fcb->Vcb, + &Fcb->Mcb, + RequestedMapSize->LowPart - 1, + &Lbo, + NULL, + &Index ); + + if (!Result) { + + NT_ASSERT(FALSE); + try_leave( Status = STATUS_FILE_CORRUPT_ERROR); + } + + *MappingPairs = FsRtlAllocatePoolWithTag( NonPagedPoolNx, + (Index + 2) * (2 * sizeof(LARGE_INTEGER)), + TAG_OUTPUT_MAPPINGPAIRS ); + + // + // Now copy over the mapping pairs from the mcb + // to the output buffer. We store in [sector count, lbo] + // mapping pairs and end with a zero sector count. + // + + MapSize = RequestedMapSize->LowPart; + + for (i = 0; i <= Index; i += 1) { + + (VOID)FatGetNextMcbEntry( Fcb->Vcb, &Fcb->Mcb, i, (PVBO)&Vbo, &Lbo, &SectorCount ); + + if (SectorCount > MapSize) { + SectorCount = MapSize; + } + + (*MappingPairs)[ i*2 + 0 ].QuadPart = SectorCount; + (*MappingPairs)[ i*2 + 1 ].QuadPart = Lbo; + + MapSize -= SectorCount; + } + + (*MappingPairs)[ i*2 + 0 ].QuadPart = 0; + + Status = STATUS_SUCCESS; + } + finally { + + DebugUnwind( FatQueryRetrievalPointers ); + + // + // Release all of our resources + // + + FatReleaseFcb( IrpContext, Fcb ); + + // + // If this is an abnormal termination then undo our work, otherwise + // complete the irp + // + + if (!AbnormalTermination()) { + + FatCompleteRequest( IrpContext, Irp, Status ); + } + } + + return Status; +} + + +// +// Local Support Routine +// + +NTSTATUS +FatGetStatistics ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine returns the filesystem performance counters from the + appropriate VCB. + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + PIO_STACK_LOCATION IrpSp; + NTSTATUS Status; + PVCB Vcb; + + PFILE_SYSTEM_STATISTICS Buffer; + ULONG BufferLength; + ULONG StatsSize; + ULONG BytesToCopy; + + PAGED_CODE(); + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "FatGetStatistics...\n", 0); + + // + // Extract the buffer + // + + BufferLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength; + + // + // Get a pointer to the output buffer. + // + + Buffer = Irp->AssociatedIrp.SystemBuffer; + + // + // Make sure the buffer is big enough for at least the common part. + // + + if (BufferLength < sizeof(FILESYSTEM_STATISTICS)) { + + FatCompleteRequest( IrpContext, Irp, STATUS_BUFFER_TOO_SMALL ); + + DebugTrace(-1, Dbg, "FatGetStatistics -> %08lx\n", STATUS_BUFFER_TOO_SMALL ); + + return STATUS_BUFFER_TOO_SMALL; + } + + // + // Now see how many bytes we can copy. + // + + StatsSize = sizeof(FILE_SYSTEM_STATISTICS) * FatData.NumberProcessors; + + if (BufferLength < StatsSize) { + + BytesToCopy = BufferLength; + Status = STATUS_BUFFER_OVERFLOW; + + } else { + + BytesToCopy = StatsSize; + Status = STATUS_SUCCESS; + } + + // + // Get the Vcb. + // + + Vcb = &((PVOLUME_DEVICE_OBJECT)IrpSp->DeviceObject)->Vcb; + + // + // Fill in the output buffer + // + + RtlCopyMemory( Buffer, Vcb->Statistics, BytesToCopy ); + + Irp->IoStatus.Information = BytesToCopy; + + FatCompleteRequest( IrpContext, Irp, Status ); + + DebugTrace(-1, Dbg, "FatGetStatistics -> %08lx\n", Status); + + return Status; +} + +// +// Local Support Routine +// + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatGetVolumeBitmap( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine returns the volume allocation bitmap. + + Input = the STARTING_LCN_INPUT_BUFFER data structure is passed in + through the input buffer. + Output = the VOLUME_BITMAP_BUFFER data structure is returned through + the output buffer. + + We return as much as the user buffer allows starting the specified input + LCN (trucated to a byte). If there is no input buffer, we start at zero. + +Arguments: + + Irp - Supplies the Irp being processed. + +Return Value: + + NTSTATUS - The return status for the operation. + +--*/ +{ + NTSTATUS Status; + PIO_STACK_LOCATION IrpSp; + + PVCB Vcb; + PFCB Fcb; + PCCB Ccb; + + ULONG BytesToCopy; + ULONG TotalClusters; + ULONG DesiredClusters; + ULONG StartingCluster; + ULONG EndingCluster; + ULONG InputBufferLength; + ULONG OutputBufferLength; + LARGE_INTEGER StartingLcn; + PVOLUME_BITMAP_BUFFER OutputBuffer; + + PAGED_CODE(); + + // + // Get the current Irp stack location and save some references. + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "FatGetVolumeBitmap, FsControlCode = %08lx\n", + IrpSp->Parameters.FileSystemControl.FsControlCode); + + // + // Make this a synchronous IRP because we need access to the input buffer and + // this Irp is marked METHOD_NEITHER. + // + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ); + + // + // Extract and decode the file object and check for type of open. + // + + if (FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb ) != UserVolumeOpen) { + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + return STATUS_INVALID_PARAMETER; + } + + if ((Ccb == NULL) || !FlagOn( Ccb->Flags, CCB_FLAG_MANAGE_VOLUME_ACCESS )) { + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + + DebugTrace(-1, Dbg, "FatGetVolumeBitmap -> %08lx\n", STATUS_INVALID_PARAMETER); + return STATUS_INVALID_PARAMETER; + } + + InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength; + OutputBufferLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength; + + OutputBuffer = (PVOLUME_BITMAP_BUFFER)FatMapUserBuffer( IrpContext, Irp ); + + // + // Check for a minimum length on the input and output buffers. + // + + if ((InputBufferLength < sizeof(STARTING_LCN_INPUT_BUFFER)) || + (OutputBufferLength < sizeof(VOLUME_BITMAP_BUFFER))) { + + FatCompleteRequest( IrpContext, Irp, STATUS_BUFFER_TOO_SMALL ); + return STATUS_BUFFER_TOO_SMALL; + } + + // + // Check if a starting cluster was specified. + // + + TotalClusters = Vcb->AllocationSupport.NumberOfClusters; + + // + // Check for valid buffers + // + + try { + + if (Irp->RequestorMode != KernelMode) { + + ProbeForRead( IrpSp->Parameters.FileSystemControl.Type3InputBuffer, + InputBufferLength, + sizeof(UCHAR) ); + + ProbeForWrite( OutputBuffer, OutputBufferLength, sizeof(UCHAR) ); + } + + StartingLcn = ((PSTARTING_LCN_INPUT_BUFFER)IrpSp->Parameters.FileSystemControl.Type3InputBuffer)->StartingLcn; + + } except( Irp->RequestorMode != KernelMode ? EXCEPTION_EXECUTE_HANDLER: EXCEPTION_CONTINUE_SEARCH ) { + + Status = GetExceptionCode(); + + FatRaiseStatus( IrpContext, + FsRtlIsNtstatusExpected(Status) ? + Status : STATUS_INVALID_USER_BUFFER ); + } + + if (StartingLcn.HighPart || StartingLcn.LowPart >= TotalClusters) { + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + return STATUS_INVALID_PARAMETER; + + } else { + + StartingCluster = StartingLcn.LowPart & ~7; + } + + (VOID)FatAcquireExclusiveVcb( IrpContext, Vcb ); + + // + // Only return what will fit in the user buffer. + // + + OutputBufferLength -= FIELD_OFFSET(VOLUME_BITMAP_BUFFER, Buffer); + DesiredClusters = TotalClusters - StartingCluster; + + if (OutputBufferLength < (DesiredClusters + 7) / 8) { + + BytesToCopy = OutputBufferLength; + Status = STATUS_BUFFER_OVERFLOW; + + } else { + + BytesToCopy = (DesiredClusters + 7) / 8; + Status = STATUS_SUCCESS; + } + + // + // Use try/finally for cleanup. + // + + try { + + try { + + // + // Verify the Vcb is still OK + // + + FatQuickVerifyVcb( IrpContext, Vcb ); + + // + // Fill in the fixed part of the output buffer + // + + OutputBuffer->StartingLcn.QuadPart = StartingCluster; + OutputBuffer->BitmapSize.QuadPart = DesiredClusters; + + if (Vcb->NumberOfWindows == 1) { + + // + // Just copy the volume bitmap into the user buffer. + // + + NT_ASSERT( Vcb->FreeClusterBitMap.Buffer != NULL ); + + RtlCopyMemory( &OutputBuffer->Buffer[0], + (PUCHAR)Vcb->FreeClusterBitMap.Buffer + StartingCluster/8, + BytesToCopy ); + } else { + + // + // Call out to analyze the FAT. We must bias by two to account for + // the zero base of this API and FAT's physical reality of starting + // the file heap at cluster 2. + // + // Note that the end index is inclusive - we need to subtract one to + // calculcate it. + // + // I.e.: StartingCluster 0 for one byte of bitmap means a start cluster + // of 2 and end cluster of 9, a run of eight clusters. + // + + EndingCluster = StartingCluster + (BytesToCopy * 8); + + // + // Make sure we do not read past the end of the entries. + // + + if (EndingCluster > TotalClusters) { + + EndingCluster = TotalClusters; + } + + FatExamineFatEntries( IrpContext, + Vcb, + StartingCluster + 2, + EndingCluster + 2 - 1, + FALSE, + NULL, + (PULONG)&OutputBuffer->Buffer[0] ); + } + + } except( Irp->RequestorMode != KernelMode ? EXCEPTION_EXECUTE_HANDLER: EXCEPTION_CONTINUE_SEARCH ) { + + Status = GetExceptionCode(); + + FatRaiseStatus( IrpContext, + FsRtlIsNtstatusExpected(Status) ? + Status : STATUS_INVALID_USER_BUFFER ); + } + + } finally { + + FatReleaseVcb( IrpContext, Vcb ); + } + + Irp->IoStatus.Information = FIELD_OFFSET(VOLUME_BITMAP_BUFFER, Buffer) + + BytesToCopy; + + FatCompleteRequest( IrpContext, Irp, Status ); + + DebugTrace(-1, Dbg, "FatGetVolumeBitmap -> VOID\n", 0); + + return Status; +} + + +// +// Local Support Routine +// + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatGetRetrievalPointers ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine scans the MCB and builds an extent list. The first run in + the output extent list will start at the begining of the contiguous + run specified by the input parameter. + + Input = STARTING_VCN_INPUT_BUFFER; + Output = RETRIEVAL_POINTERS_BUFFER. + +Arguments: + + Irp - Supplies the Irp being processed. + +Return Value: + + NTSTATUS - The return status for the operation. + +--*/ +{ + NTSTATUS Status = STATUS_SUCCESS; + PIO_STACK_LOCATION IrpSp; + + PVCB Vcb; + PFCB FcbOrDcb; + PCCB Ccb; + PLARGE_MCB McbToUse = NULL; + TYPE_OF_OPEN TypeOfOpen; + + ULONG Index; + ULONG ClusterShift = 0; + ULONG ClusterSize; + LONGLONG AllocationSize = 0; + + ULONG Run; + ULONG RunCount; + ULONG StartingRun; + LARGE_INTEGER StartingVcn; + + ULONG InputBufferLength; + ULONG OutputBufferLength; + + VBO LastVbo; + LBO LastLbo; + ULONG LastIndex; + + PRETRIEVAL_POINTERS_BUFFER OutputBuffer; + + PAGED_CODE(); + + // + // Get the current Irp stack location and save some references. + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "FatGetRetrievalPointers, FsControlCode = %08lx\n", + IrpSp->Parameters.FileSystemControl.FsControlCode); + + // + // Make this a synchronous IRP because we need access to the input buffer and + // this Irp is marked METHOD_NEITHER. + // + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ); + + // + // Extract and decode the file object and check for type of open. + // + + TypeOfOpen = FatDecodeFileObject( IrpSp->FileObject, &Vcb, &FcbOrDcb, &Ccb ); + + if ((TypeOfOpen != UserFileOpen) && (TypeOfOpen != UserDirectoryOpen) && (TypeOfOpen != UserVolumeOpen) ) { + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + return STATUS_INVALID_PARAMETER; + } + + // + // Get the input and output buffer lengths and pointers. + // Initialize some variables. + // + + InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength; + OutputBufferLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength; + + OutputBuffer = (PRETRIEVAL_POINTERS_BUFFER)FatMapUserBuffer( IrpContext, Irp ); + + // + // Check for a minimum length on the input and ouput buffers. + // + + if ((InputBufferLength < sizeof(STARTING_VCN_INPUT_BUFFER)) || + (OutputBufferLength < sizeof(RETRIEVAL_POINTERS_BUFFER))) { + + FatCompleteRequest( IrpContext, Irp, STATUS_BUFFER_TOO_SMALL ); + return STATUS_BUFFER_TOO_SMALL; + } + + // + // Acquire the Fcb and enqueue the Irp if we didn't get access. Go for + // shared on read-only media so we can allow prototype XIP to get + // recursive, as well as recognizing this is safe anyway. + // + if( (TypeOfOpen == UserFileOpen) || (TypeOfOpen == UserDirectoryOpen) ) { + + if (FlagOn( FcbOrDcb->Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED )) { + + (VOID)FatAcquireSharedFcb( IrpContext, FcbOrDcb ); + + } else { + + (VOID)FatAcquireExclusiveFcb( IrpContext, FcbOrDcb ); + } + } else if ((TypeOfOpen == UserVolumeOpen )) { + + if ((Ccb == NULL) || !FlagOn( Ccb->Flags, CCB_FLAG_MANAGE_VOLUME_ACCESS )) { + + FatCompleteRequest( IrpContext, Irp, STATUS_ACCESS_DENIED ); + + DebugTrace(-1, Dbg, "FatMoveFile -> 0x%x\n", STATUS_ACCESS_DENIED); + return STATUS_ACCESS_DENIED; + } + + (VOID)FatAcquireExclusiveVcb(IrpContext, Vcb); + } + + try { + + // + // Verify the Fcb is still OK, or if it is a volume handle, the VCB. + // + + if( (TypeOfOpen == UserFileOpen) || (TypeOfOpen == UserDirectoryOpen) ) { + + FatVerifyFcb( IrpContext, FcbOrDcb ); + + // + // If we haven't yet set the correct AllocationSize, do so. + // + + if (FcbOrDcb->Header.AllocationSize.QuadPart == FCB_LOOKUP_ALLOCATIONSIZE_HINT) { + + FatLookupFileAllocationSize( IrpContext, FcbOrDcb ); + + // + // If this is a non-root directory, we have a bit more to + // do since it has not gone through FatOpenDirectoryFile(). + // + + if (NodeType(FcbOrDcb) == FAT_NTC_DCB || + (NodeType(FcbOrDcb) == FAT_NTC_ROOT_DCB && FatIsFat32(Vcb))) { + + FcbOrDcb->Header.FileSize.LowPart = + FcbOrDcb->Header.AllocationSize.LowPart; + } + } + + + ClusterShift = Vcb->AllocationSupport.LogOfBytesPerCluster; + +#pragma prefast( suppress:28931, "calculate it anyway, in case someone adds code that uses this in the future" ) + ClusterSize = 1 << ClusterShift; + + AllocationSize = FcbOrDcb->Header.AllocationSize.LowPart; + McbToUse = &FcbOrDcb->Mcb; + + } else if ((TypeOfOpen == UserVolumeOpen )) { + + FatQuickVerifyVcb( IrpContext, Vcb ); + + if (!FlagOn( Vcb->VcbState, VCB_STATE_FLAG_BAD_BLOCKS_POPULATED)) { + + // + // If the bad cluster mcb isn't populated, something is wrong. (It should have been + // populated during mount when we scanned the FAT. + // + + FatRaiseStatus(IrpContext, STATUS_FILE_CORRUPT_ERROR ); + } + + ClusterShift = Vcb->AllocationSupport.LogOfBytesPerCluster; + ClusterSize = 1 << ClusterShift; + + if (!FatLookupLastMcbEntry(Vcb, &Vcb->BadBlockMcb, &LastVbo, &LastLbo, &LastIndex)) { + AllocationSize = 0; + } else { + + // + // Round the allocation size to a multiple of of the cluster size. + // + + AllocationSize = (LastVbo + ((LONGLONG)ClusterSize-1)) & ~((LONGLONG)ClusterSize-1); + } + + McbToUse = &Vcb->BadBlockMcb; + + } + + // + // Check if a starting cluster was specified. + // + + try { + + if (Irp->RequestorMode != KernelMode) { + + ProbeForRead( IrpSp->Parameters.FileSystemControl.Type3InputBuffer, + InputBufferLength, + sizeof(UCHAR) ); + + ProbeForWrite( OutputBuffer, OutputBufferLength, sizeof(UCHAR) ); + } + + StartingVcn = ((PSTARTING_VCN_INPUT_BUFFER)IrpSp->Parameters.FileSystemControl.Type3InputBuffer)->StartingVcn; + + } except( Irp->RequestorMode != KernelMode ? EXCEPTION_EXECUTE_HANDLER: EXCEPTION_CONTINUE_SEARCH ) { + + Status = GetExceptionCode(); + + FatRaiseStatus( IrpContext, + FsRtlIsNtstatusExpected(Status) ? + Status : STATUS_INVALID_USER_BUFFER ); + } + + if (StartingVcn.HighPart || + StartingVcn.LowPart >= (AllocationSize >> ClusterShift)) { + + try_return( Status = STATUS_END_OF_FILE ); + + } else { + + // + // If we don't find the run, something is very wrong. + // + + LBO Lbo; + + if (!FatLookupMcbEntry( Vcb, McbToUse, + StartingVcn.LowPart << ClusterShift, + &Lbo, + NULL, + &StartingRun)) { + +#pragma prefast( suppress:28159, "things are seriously wrong if we get here" ) + FatBugCheck( (ULONG_PTR)FcbOrDcb, (ULONG_PTR)McbToUse, StartingVcn.LowPart ); + } + } + + // + // Now go fill in the ouput buffer with run information + // + + RunCount = FsRtlNumberOfRunsInLargeMcb( McbToUse ); + + for (Index = 0, Run = StartingRun; Run < RunCount; Index++, Run++) { + + ULONG Vcn; + LBO Lbo; + ULONG ByteLength; + + // + // Check for an exhausted output buffer. + // + + if ((ULONG)FIELD_OFFSET(RETRIEVAL_POINTERS_BUFFER, Extents[Index+1]) > OutputBufferLength) { + + + // + // We've run out of space, so we won't be storing as many runs to the + // user's buffer as we had originally planned. We need to return the + // number of runs that we did have room for. + // + + try { + + OutputBuffer->ExtentCount = Index; + + } except( Irp->RequestorMode != KernelMode ? EXCEPTION_EXECUTE_HANDLER: EXCEPTION_CONTINUE_SEARCH ) { + + Status = GetExceptionCode(); + + FatRaiseStatus( IrpContext, + FsRtlIsNtstatusExpected(Status) ? + Status : STATUS_INVALID_USER_BUFFER ); + } + + Irp->IoStatus.Information = FIELD_OFFSET(RETRIEVAL_POINTERS_BUFFER, Extents[Index]); + try_return( Status = STATUS_BUFFER_OVERFLOW ); + } + + // + // Get the extent. If it's not there or malformed, something is very wrong. + // + + if (!FatGetNextMcbEntry(Vcb, McbToUse, Run, (PVBO)&Vcn, &Lbo, &ByteLength)) { + +#pragma prefast( suppress:28159, "things are seriously wrong if we get here" ) + FatBugCheck( (ULONG_PTR)FcbOrDcb, (ULONG_PTR)McbToUse, Run ); + } + + // + // Fill in the next array element. + // + + try { + + OutputBuffer->Extents[Index].NextVcn.QuadPart = ((LONGLONG)Vcn + ByteLength) >> ClusterShift; + OutputBuffer->Extents[Index].Lcn.QuadPart = FatGetIndexFromLbo( Vcb, Lbo ) - 2; + + // + // If this is the first run, fill in the starting Vcn + // + + if (Index == 0) { + OutputBuffer->ExtentCount = RunCount - StartingRun; + OutputBuffer->StartingVcn.QuadPart = Vcn >> ClusterShift; + } + + } except( Irp->RequestorMode != KernelMode ? EXCEPTION_EXECUTE_HANDLER: EXCEPTION_CONTINUE_SEARCH ) { + + Status = GetExceptionCode(); + + FatRaiseStatus( IrpContext, + FsRtlIsNtstatusExpected(Status) ? + Status : STATUS_INVALID_USER_BUFFER ); + } + } + + // + // We successfully retrieved extent info to the end of the allocation. + // + + Irp->IoStatus.Information = FIELD_OFFSET(RETRIEVAL_POINTERS_BUFFER, Extents[Index]); + Status = STATUS_SUCCESS; + + try_exit: NOTHING; + + } finally { + + DebugUnwind( FatGetRetrievalPointers ); + + // + // Release resources + // + + if( (TypeOfOpen == UserFileOpen) || (TypeOfOpen == UserDirectoryOpen) ) { + + FatReleaseFcb( IrpContext, FcbOrDcb ); + } else if ((TypeOfOpen == UserVolumeOpen )) { + + FatReleaseVcb(IrpContext, Vcb); + } + + // + // If nothing raised then complete the irp. + // + + if (!AbnormalTermination()) { + + FatCompleteRequest( IrpContext, Irp, Status ); + } + + DebugTrace(-1, Dbg, "FatGetRetrievalPointers -> VOID\n", 0); + } + + return Status; +} + + +// +// Local Support Routine +// + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatMoveFileNeedsWriteThrough ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PFCB FcbOrDcb, + _In_ ULONG OldWriteThroughFlags + ) +{ + PAGED_CODE(); + + if (NodeType(FcbOrDcb) == FAT_NTC_FCB) { + + if (FcbOrDcb->Header.ValidDataLength.QuadPart == 0) { + + ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WRITE_THROUGH ); + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_WRITE_THROUGH ); + + } else { + + IrpContext->Flags &= ~(IRP_CONTEXT_FLAG_WRITE_THROUGH|IRP_CONTEXT_FLAG_DISABLE_WRITE_THROUGH); + IrpContext->Flags |= OldWriteThroughFlags; + + } + } +} + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatMoveFile ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + Routine moves a file to the requested Starting Lcn from Starting Vcn for the length + of cluster count. These values are passed in through the the input buffer as a + MOVE_DATA structure. + + The call must be made with a DASD handle. The file to move is passed in as a + parameter. + +Arguments: + + Irp - Supplies the Irp being processed. + +Return Value: + + NTSTATUS - The return status for the operation. + +--*/ + +{ + NTSTATUS Status; + PIO_STACK_LOCATION IrpSp; + + PFILE_OBJECT FileObject; + TYPE_OF_OPEN TypeOfOpen; + PVCB Vcb; + PFCB FcbOrDcb; + PCCB Ccb; + + ULONG InputBufferLength; + PMOVE_FILE_DATA InputBuffer; + + ULONG ClusterShift; + ULONG MaxClusters; + + ULONG FileOffset; + + LBO TargetLbo; + ULONG TargetCluster; + LARGE_INTEGER LargeSourceLbo; + LARGE_INTEGER LargeTargetLbo; + + ULONG ByteCount; + ULONG BytesToWrite; + ULONG BytesToReallocate; + + ULONG FirstSpliceSourceCluster; + ULONG FirstSpliceTargetCluster; + ULONG SecondSpliceSourceCluster; + ULONG SecondSpliceTargetCluster; + + LARGE_MCB SourceMcb; + LARGE_MCB TargetMcb; + + KEVENT StackEvent; + + PVOID Buffer = NULL; + ULONG BufferSize; + + BOOLEAN SourceMcbInitialized = FALSE; + BOOLEAN TargetMcbInitialized = FALSE; + + BOOLEAN FcbAcquired = FALSE; + BOOLEAN EventArmed = FALSE; + BOOLEAN DiskSpaceAllocated = FALSE; + + PDIRENT Dirent; + PBCB DirentBcb = NULL; + + ULONG OldWriteThroughFlags = (IrpContext->Flags & (IRP_CONTEXT_FLAG_WRITE_THROUGH|IRP_CONTEXT_FLAG_DISABLE_WRITE_THROUGH)); + +#if defined(_WIN64) && defined(BUILD_WOW64_ENABLED) + MOVE_FILE_DATA LocalMoveFileData; + PMOVE_FILE_DATA32 MoveFileData32; +#endif + + ULONG LocalAbnormalTermination = 0; + + PAGED_CODE(); + + // + // Get the current Irp stack location and save some references. + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "FatMoveFile, FsControlCode = %08lx\n", + IrpSp->Parameters.FileSystemControl.FsControlCode); + + // + // Force WAIT to true. We have a handle in the input buffer which can only + // be referenced within the originating process. + // + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ); + + // + // Extract and decode the file object and check for type of open. + // + + if (FatDecodeFileObject( IrpSp->FileObject, &Vcb, &FcbOrDcb, &Ccb ) != UserVolumeOpen) { + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + + DebugTrace(-1, Dbg, "FatMoveFile -> %08lx\n", STATUS_INVALID_PARAMETER); + return STATUS_INVALID_PARAMETER; + } + + if ((Ccb == NULL) || !FlagOn( Ccb->Flags, CCB_FLAG_MANAGE_VOLUME_ACCESS )) { + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + + DebugTrace(-1, Dbg, "FatMoveFile -> %08lx\n", STATUS_INVALID_PARAMETER); + return STATUS_INVALID_PARAMETER; + } + + InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength; + InputBuffer = (PMOVE_FILE_DATA)Irp->AssociatedIrp.SystemBuffer; + + // + // Do a quick check on the input buffer. + // + +#if defined(_WIN64) && defined(BUILD_WOW64_ENABLED) + if (IoIs32bitProcess( Irp )) { + + if (InputBuffer == NULL || InputBufferLength < sizeof(MOVE_FILE_DATA32)) { + + FatCompleteRequest( IrpContext, Irp, STATUS_BUFFER_TOO_SMALL ); + return STATUS_BUFFER_TOO_SMALL; + } + + MoveFileData32 = (PMOVE_FILE_DATA32) InputBuffer; + + LocalMoveFileData.FileHandle = (HANDLE) LongToHandle( MoveFileData32->FileHandle ); + LocalMoveFileData.StartingVcn = MoveFileData32->StartingVcn; + LocalMoveFileData.StartingLcn = MoveFileData32->StartingLcn; + LocalMoveFileData.ClusterCount = MoveFileData32->ClusterCount; + + InputBuffer = &LocalMoveFileData; + + } else { +#endif + if (InputBuffer == NULL || InputBufferLength < sizeof(MOVE_FILE_DATA)) { + + FatCompleteRequest( IrpContext, Irp, STATUS_BUFFER_TOO_SMALL ); + return STATUS_BUFFER_TOO_SMALL; + } +#if defined(_WIN64) && defined(BUILD_WOW64_ENABLED) + } +#endif + + MaxClusters = Vcb->AllocationSupport.NumberOfClusters; + TargetCluster = InputBuffer->StartingLcn.LowPart + 2; + + if (InputBuffer->StartingVcn.HighPart || + InputBuffer->StartingLcn.HighPart || + (TargetCluster < 2) || + (TargetCluster + InputBuffer->ClusterCount < TargetCluster) || + (TargetCluster + InputBuffer->ClusterCount > MaxClusters + 2) || + (InputBuffer->StartingVcn.LowPart >= MaxClusters) || + InputBuffer->ClusterCount == 0 + ) { + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + + DebugTrace(-1, Dbg, "FatMoveFile -> %08lx\n", STATUS_INVALID_PARAMETER); + return STATUS_INVALID_PARAMETER; + } + + // + // Try to get a pointer to the file object from the handle passed in. + // + + Status = ObReferenceObjectByHandle( InputBuffer->FileHandle, + 0, + *IoFileObjectType, + Irp->RequestorMode, + &FileObject, + NULL ); + + if (!NT_SUCCESS(Status)) { + + FatCompleteRequest( IrpContext, Irp, Status ); + + DebugTrace(-1, Dbg, "FatMoveFile -> %08lx\n", Status); + return Status; + } + + // + // There are three basic ways this could be an invalid attempt, so + // we need to + // + // - check that this file object is opened on the same volume as the + // DASD handle used to call this routine. + // + // - extract and decode the file object and check for type of open. + // + // - if this is a directory, verify that it's not the root and that + // we are not trying to move the first cluster. We cannot move the + // first cluster because sub-directories have this cluster number + // in them and there is no safe way to simultaneously update them + // all. + // + // We'll allow movefile on the root dir if its fat32, since the root dir + // is a real chained file there. + // + + if (FileObject->Vpb != Vcb->Vpb) { + + ObDereferenceObject( FileObject ); + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + + DebugTrace(-1, Dbg, "FatMoveFile -> %08lx\n", STATUS_INVALID_PARAMETER); + return STATUS_INVALID_PARAMETER; + } + + TypeOfOpen = FatDecodeFileObject( FileObject, &Vcb, &FcbOrDcb, &Ccb ); + + if ((TypeOfOpen != UserFileOpen && + TypeOfOpen != UserDirectoryOpen) || + + ((TypeOfOpen == UserDirectoryOpen) && + ((NodeType(FcbOrDcb) == FAT_NTC_ROOT_DCB && !FatIsFat32(Vcb)) || + (InputBuffer->StartingVcn.QuadPart == 0)))) { + + ObDereferenceObject( FileObject ); + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + + DebugTrace(-1, Dbg, "FatMoveFile -> %08lx\n", STATUS_INVALID_PARAMETER); + return STATUS_INVALID_PARAMETER; + } + + // + // If the VDL of the file is zero, it has no valid data in it anyway. + // So it should be safe to avoid flushing the FAT entries and let them be + // lazily written out. + // + // This is done so that bitlocker's cover file doesn't cause + // unnecessary FAT table I/O when it's moved around. + // (See Win8 bug 106505) + // + + // + // If this is a file, and the VDL is zero, clear write through. + // + + FatMoveFileNeedsWriteThrough(IrpContext, FcbOrDcb, OldWriteThroughFlags); + + // + // Indicate we're getting to parents of this fcb by their child, and that + // this is a sufficient assertion of our ability to by synchronized + // with respect to the parent directory going away. + // + // The defrag path is an example of one which arrives at an Fcb by + // a means which would be unreasonable to duplicate in the assertion + // code. See FatOpenDirectoryFile. + // + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_PARENT_BY_CHILD ); + + ClusterShift = Vcb->AllocationSupport.LogOfBytesPerCluster; + + try { + + // + // Initialize our state variables and the event. + // + + FileOffset = InputBuffer->StartingVcn.LowPart << ClusterShift; + + ByteCount = InputBuffer->ClusterCount << ClusterShift; + + TargetLbo = FatGetLboFromIndex( Vcb, TargetCluster ); + LargeTargetLbo.QuadPart = TargetLbo; + + Buffer = NULL; + + // + // Do a quick check on parameters here + // + + if (FileOffset + ByteCount < FileOffset) { + + try_return( Status = STATUS_INVALID_PARAMETER ); + } + + KeInitializeEvent( &StackEvent, NotificationEvent, FALSE ); + + // + // Initialize two MCBs we will be using + // + + FsRtlInitializeLargeMcb( &SourceMcb, PagedPool ); + SourceMcbInitialized = TRUE; + + FsRtlInitializeLargeMcb( &TargetMcb, PagedPool ); + TargetMcbInitialized = TRUE; + + // + // Ok, now if this is a directory open we need to switch to the internal + // stream fileobject since it is set up for caching. The top-level + // fileobject has no section object pointers in order prevent folks from + // mapping it. + // + + if (TypeOfOpen == UserDirectoryOpen) { + + PFILE_OBJECT DirStreamFileObject; + + // + // Open the stream fileobject if neccesary. We must acquire the Fcb + // now to synchronize with other operations (such as dismount ripping + // apart the allocator). + // + + (VOID)FatAcquireExclusiveFcb( IrpContext, FcbOrDcb ); + FcbAcquired = TRUE; + + FatVerifyFcb( IrpContext, FcbOrDcb ); + + FatOpenDirectoryFile( IrpContext, FcbOrDcb ); + DirStreamFileObject = FcbOrDcb->Specific.Dcb.DirectoryFile; + + // + // Transfer our reference to the internal stream and proceed. Note that + // if we dereferenced first, the user could sneak a teardown through since + // we'd have no references. + // + + ObReferenceObject( DirStreamFileObject ); + ObDereferenceObject( FileObject ); + FileObject = DirStreamFileObject; + + // + // We've referenced the DirStreamFileObject, so it should be ok to drop + // the Dcb now. + // + + FatReleaseFcb( IrpContext, FcbOrDcb ); + FcbAcquired = FALSE; + } + + // + // Determine the size of the buffer we will use to move data. + // + + BufferSize = FAT_DEFAULT_DEFRAG_CHUNK_IN_BYTES; + + if (BufferSize < (ULONG)(1 << ClusterShift)) { + + BufferSize = (1 << ClusterShift); + } + + while (ByteCount) { + + VBO TempVbo; + LBO TempLbo; + ULONG TempByteCount; + + // + // We must throttle our writes. + // + + CcCanIWrite( FileObject, + BufferSize, + TRUE, + FALSE ); + + // + // Aqcuire file resource exclusive to freeze FileSize and block + // user non-cached I/O. Verify the integrity of the fcb - the + // media may have changed (or been dismounted) on us. + // + + if (FcbAcquired == FALSE) { + + (VOID)FatAcquireExclusiveFcb( IrpContext, FcbOrDcb ); + FcbAcquired = TRUE; + + FatVerifyFcb( IrpContext, FcbOrDcb ); + } + + // + // Check if the handle indicates we're allowed to move the file. + // + // FCB_STATE_DENY_DEFRAG indicates that someone blocked move file on this FCB. + // CCB_FLAG_DENY_DEFRAG indicates that this handle was the one that blocked move file, and hence + // it still gets to move the file around. + // + + if ((FcbOrDcb->FcbState & FCB_STATE_DENY_DEFRAG) && !(Ccb->Flags & CCB_FLAG_DENY_DEFRAG)) { + DebugTrace(-1, Dbg, "FatMoveFile -> %08lx\n", STATUS_ACCESS_DENIED); + try_return( Status = STATUS_ACCESS_DENIED ); + } + + // + // Allocate our buffer, if we need to. + // + + if (Buffer == NULL) { + + Buffer = FsRtlAllocatePoolWithTag( NonPagedPoolNx, + BufferSize, + TAG_DEFRAG_BUFFER ); + } + + // + // Analyzes the range of file allocation we are moving + // and determines the actual amount of allocation to be + // moved and how much needs to be written. In addition + // it guarantees that the Mcb in the file is large enough + // so that later MCB operations cannot fail. + // + + FatComputeMoveFileParameter( IrpContext, + FcbOrDcb, + BufferSize, + FileOffset, + &ByteCount, + &BytesToReallocate, + &BytesToWrite, + &LargeSourceLbo ); + + // + // If ByteCount comes back zero, break here. + // + + if (ByteCount == 0) { + break; + } + + // + // At this point (before actually doing anything with the disk + // meta data), calculate the FAT splice clusters and build an + // MCB describing the space to be deallocated. + // + + FatComputeMoveFileSplicePoints( IrpContext, + FcbOrDcb, + FileOffset, + TargetCluster, + BytesToReallocate, + &FirstSpliceSourceCluster, + &FirstSpliceTargetCluster, + &SecondSpliceSourceCluster, + &SecondSpliceTargetCluster, + &SourceMcb ); + + // + // Now attempt to allocate the new disk storage using the + // Target Lcn as a hint. + // + + TempByteCount = BytesToReallocate; + FatAllocateDiskSpace( IrpContext, + Vcb, + TargetCluster, + &TempByteCount, + TRUE, + &TargetMcb ); + + DiskSpaceAllocated = TRUE; + + // + // If we didn't get EXACTLY what we wanted, return immediately. + // + + if ((FsRtlNumberOfRunsInLargeMcb( &TargetMcb ) != 1) || + !FatGetNextMcbEntry( Vcb, &TargetMcb, 0, &TempVbo, &TempLbo, &TempByteCount ) || + (FatGetIndexFromLbo( Vcb, TempLbo) != TargetCluster ) || + (TempByteCount != BytesToReallocate)) { + + // + // It would be nice if we could be more specific, but such is life. + // + try_return( Status = STATUS_INVALID_PARAMETER ); + } + +#if DBG + // + // We are going to attempt a move, note it. + // + + if (FatMoveFileDebug) { + DbgPrint("0x%p: Vcn 0x%lx, Lcn 0x%lx, Count 0x%lx.\n", + PsGetCurrentThread(), + FileOffset >> ClusterShift, + TargetCluster, + BytesToReallocate >> ClusterShift ); + } +#endif + + // + // Now attempt to commit the new allocation to disk. If this + // raises, the allocation will be deallocated. + // + // If the VDL of the file is zero, it has no valid data in it anyway. + // So it should be safe to avoid flushing the FAT entries and let them be + // lazily written out. + // + // This is done so that bitlocker's cover file doesn't cause + // unnecessary FAT table I/O when it's moved around. + // (See Win8 bug 106505) + // + + if ((FcbOrDcb->Header.ValidDataLength.QuadPart != 0) || (NodeType(FcbOrDcb) != FAT_NTC_FCB)) { + + FatFlushFatEntries( IrpContext, + Vcb, + TargetCluster, + BytesToReallocate >> ClusterShift ); + } + + // + // Aqcuire both resources exclusive now, guaranteeing that NOBODY + // is in either the read or write paths. + // + + ExAcquireResourceExclusiveLite( FcbOrDcb->Header.PagingIoResource, TRUE ); + + // + // This is the first part of some tricky synchronization. + // + // Set the Event pointer in the FCB. Any paging I/O will block on + // this event (if set in FCB) after acquiring the PagingIo resource. + // + // This is how I keep ALL I/O out of this path without holding the + // PagingIo resource exclusive for an extended time. + // + + FcbOrDcb->MoveFileEvent = &StackEvent; + EventArmed = TRUE; + + ExReleaseResourceLite( FcbOrDcb->Header.PagingIoResource ); + + // + // Now write out the data, but only if we have to. We don't have + // to copy any file data if the range being reallocated is wholly + // beyond valid data length. + // + + if (BytesToWrite) { + + PIRP IoIrp; + KEVENT IoEvent; + IO_STATUS_BLOCK Iosb; + + KeInitializeEvent( &IoEvent, + NotificationEvent, + FALSE ); + + NT_ASSERT( LargeTargetLbo.QuadPart >= Vcb->AllocationSupport.FileAreaLbo ); + + // + // Read in the data that is being moved. + // + + IoIrp = IoBuildSynchronousFsdRequest( IRP_MJ_READ, + Vcb->TargetDeviceObject, + Buffer, + BytesToWrite, + &LargeSourceLbo, + &IoEvent, + &Iosb ); + + if (IoIrp == NULL) { + + FatRaiseStatus( IrpContext, + STATUS_INSUFFICIENT_RESOURCES ); + } + + Status = IoCallDriver( Vcb->TargetDeviceObject, IoIrp ); + + if (Status == STATUS_PENDING) { + + (VOID)KeWaitForSingleObject( &IoEvent, + Executive, + KernelMode, + FALSE, + (PLARGE_INTEGER)NULL ); + + Status = Iosb.Status; + } + + if (!NT_SUCCESS( Status )) { + + FatNormalizeAndRaiseStatus( IrpContext, + Status ); + } + + // + // Write the data to its new location. + // + + KeClearEvent( &IoEvent ); + + IoIrp = IoBuildSynchronousFsdRequest( IRP_MJ_WRITE, + Vcb->TargetDeviceObject, + Buffer, + BytesToWrite, + &LargeTargetLbo, + &IoEvent, + &Iosb ); + + if (IoIrp == NULL) { + + FatRaiseStatus( IrpContext, + STATUS_INSUFFICIENT_RESOURCES ); + } + + // + // Set a flag indicating that we want to write through any + // cache on the controller. This eliminates the need for + // an explicit flush-device after the write. + // + + SetFlag( IoGetNextIrpStackLocation(IoIrp)->Flags, SL_WRITE_THROUGH ); + + Status = IoCallDriver( Vcb->TargetDeviceObject, IoIrp ); + + if (Status == STATUS_PENDING) { + + (VOID)KeWaitForSingleObject( &IoEvent, + Executive, + KernelMode, + FALSE, + (PLARGE_INTEGER)NULL ); + + Status = Iosb.Status; + } + + if (!NT_SUCCESS( Status )) { + + FatNormalizeAndRaiseStatus( IrpContext, + Status ); + } + } + + // + // Now that the file data has been moved successfully, we'll go + // to fix up the links in the FAT table and perhaps change the + // entry in the parent directory. + // + // First we'll do the second splice and commit it. At that point, + // while the volume is in an inconsistent state, the file is + // still OK. + // + + FatSetFatEntry( IrpContext, + Vcb, + SecondSpliceSourceCluster, + (FAT_ENTRY)SecondSpliceTargetCluster ); + + if ((FcbOrDcb->Header.ValidDataLength.QuadPart != 0) || (NodeType(FcbOrDcb) != FAT_NTC_FCB)) { + + FatFlushFatEntries( IrpContext, Vcb, SecondSpliceSourceCluster, 1 ); + } + + // + // Now do the first splice OR update the dirent in the parent + // and flush the respective object. After this flush the file + // now points to the new allocation. + // + + if (FirstSpliceSourceCluster == 0) { + + NT_ASSERT( NodeType(FcbOrDcb) == FAT_NTC_FCB ); + + // + // We are moving the first cluster of the file, so we need + // to update our parent directory. + // + + FatGetDirentFromFcbOrDcb( IrpContext, + FcbOrDcb, + FALSE, + &Dirent, + &DirentBcb ); + + Dirent->FirstClusterOfFile = (USHORT)FirstSpliceTargetCluster; + + if (FatIsFat32(Vcb)) { + + Dirent->FirstClusterOfFileHi = + (USHORT)(FirstSpliceTargetCluster >> 16); + + } + + FatSetDirtyBcb( IrpContext, DirentBcb, Vcb, TRUE ); + + FatUnpinBcb( IrpContext, DirentBcb ); + DirentBcb = NULL; + + FatFlushDirentForFile( IrpContext, FcbOrDcb ); + + FcbOrDcb->FirstClusterOfFile = FirstSpliceTargetCluster; + + } else { + + FatSetFatEntry( IrpContext, + Vcb, + FirstSpliceSourceCluster, + (FAT_ENTRY)FirstSpliceTargetCluster ); + + if ((FcbOrDcb->Header.ValidDataLength.QuadPart != 0) || (NodeType(FcbOrDcb) != FAT_NTC_FCB)) { + + FatFlushFatEntries( IrpContext, Vcb, FirstSpliceSourceCluster, 1 ); + } + } + + // + // This was successfully committed. We no longer want to free + // this allocation on error. + // + + DiskSpaceAllocated = FALSE; + + // + // Check if we need to turn off write through for this file. + // + + FatMoveFileNeedsWriteThrough(IrpContext, FcbOrDcb, OldWriteThroughFlags); + + // + // Now we just have to free the orphaned space. We don't have + // to commit this right now as the integrity of the file doesn't + // depend on it. + // + + FatDeallocateDiskSpace( IrpContext, Vcb, &SourceMcb, FALSE ); + + FatUnpinRepinnedBcbs( IrpContext ); + + Status = FatHijackIrpAndFlushDevice( IrpContext, + Irp, + Vcb->TargetDeviceObject ); + + if (!NT_SUCCESS(Status)) { + FatNormalizeAndRaiseStatus( IrpContext, Status ); + } + + // + // Finally we must replace the old MCB extent information with + // the new. If this fails from pool allocation, we fix it in + // the finally clause by resetting the file's Mcb. + // + + FatRemoveMcbEntry( Vcb, &FcbOrDcb->Mcb, + FileOffset, + BytesToReallocate ); + + FatAddMcbEntry( Vcb, &FcbOrDcb->Mcb, + FileOffset, + TargetLbo, + BytesToReallocate ); + + // + // Now this is the second part of the tricky synchronization. + // + // We drop the paging I/O here and signal the notification + // event which allows all waiters (present or future) to proceed. + // Then we block again on the PagingIo exclusive. When + // we have it, we again know that there can be nobody in the + // read/write path and thus nobody touching the event, so we + // NULL the pointer to it and then drop the PagingIo resource. + // + // This combined with our synchronization before the write above + // guarantees that while we were moving the allocation, there + // was no other I/O to this file and because we do not hold + // the paging resource across a flush, we are not exposed to + // a deadlock. + // + + KeSetEvent( &StackEvent, 0, FALSE ); + + ExAcquireResourceExclusiveLite( FcbOrDcb->Header.PagingIoResource, TRUE ); + + FcbOrDcb->MoveFileEvent = NULL; + EventArmed = FALSE; + + ExReleaseResourceLite( FcbOrDcb->Header.PagingIoResource ); + + // + // Release the resources and let anyone else access the file before + // looping back. + // + + FatReleaseFcb( IrpContext, FcbOrDcb ); + FcbAcquired = FALSE; + + // + // Advance the state variables. + // + + TargetCluster += BytesToReallocate >> ClusterShift; + + FileOffset += BytesToReallocate; + TargetLbo += BytesToReallocate; + ByteCount -= BytesToReallocate; + + LargeTargetLbo.QuadPart += BytesToReallocate; + + // + // Clear the two Mcbs + // + + FatRemoveMcbEntry( Vcb, &SourceMcb, 0, 0xFFFFFFFF ); + FatRemoveMcbEntry( Vcb, &TargetMcb, 0, 0xFFFFFFFF ); + + // + // Make the event blockable again. + // + + KeClearEvent( &StackEvent ); + } + + Status = STATUS_SUCCESS; + + try_exit: NOTHING; + + } finally { + + DebugUnwind( FatMoveFile ); + + LocalAbnormalTermination |= AbnormalTermination(); + + ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_PARENT_BY_CHILD ); + + // + // Free the data buffer, if it was allocated. + // + + if (Buffer != NULL) { + + ExFreePool( Buffer ); + } + + // + // Use a nested try-finally for cleanup if our unpinrepinned + // encounters write-through errors. This may even be a re-raise. + // + + try { + + // + // If we have some new allocation hanging around, remove it. The + // pages needed to do this are guaranteed to be resident because + // we have already repinned them. + // + + if (DiskSpaceAllocated) { + FatDeallocateDiskSpace( IrpContext, Vcb, &TargetMcb, FALSE ); + FatUnpinRepinnedBcbs( IrpContext ); + } + + } finally { + + LocalAbnormalTermination |= AbnormalTermination(); + + // + // Check on the directory Bcb + // + + if (DirentBcb != NULL) { + FatUnpinBcb( IrpContext, DirentBcb ); + } + + // + // Uninitialize our MCBs + // + + if (SourceMcbInitialized) { + FsRtlUninitializeLargeMcb( &SourceMcb ); + } + + if (TargetMcbInitialized) { + FsRtlUninitializeLargeMcb( &TargetMcb ); + } + + // + // If this is an abnormal termination then presumably something + // bad happened. Set the Allocation size to unknown and clear + // the Mcb, but only if we still own the Fcb. + // + // It is important to make sure we use a 64bit form of -1. This is + // what will convince the fastIO path that it cannot extend the file + // in the cache until we have picked up the mapping pairs again. + // + // Also, we have to do this while owning PagingIo or we can tear the + // Mcb down in the midst of the noncached IO path looking up extents + // (after we drop it and let them all in). + // + + if (LocalAbnormalTermination && FcbAcquired) { + + if (FcbOrDcb->FirstClusterOfFile == 0) { + + FcbOrDcb->Header.AllocationSize.QuadPart = 0; + + } else { + + FcbOrDcb->Header.AllocationSize.QuadPart = FCB_LOOKUP_ALLOCATIONSIZE_HINT; + } + + FatRemoveMcbEntry( Vcb, &FcbOrDcb->Mcb, 0, 0xFFFFFFFF ); + } + + // + // If we broke out of the loop with the Event armed, defuse it + // in the same way we do it after a write. + // + + if (EventArmed) { + KeSetEvent( &StackEvent, 0, FALSE ); + ExAcquireResourceExclusiveLite( FcbOrDcb->Header.PagingIoResource, TRUE ); + FcbOrDcb->MoveFileEvent = NULL; + ExReleaseResourceLite( FcbOrDcb->Header.PagingIoResource ); + } + + // + // Finally release the main file resource. + // + + if (FcbAcquired) { + + FatReleaseFcb( IrpContext, FcbOrDcb ); + } + + // + // Now dereference the fileobject. If the user was a wacko they could have + // tried to nail us by closing the handle right after they threw this move + // down, so we had to keep the fileobject referenced across the entire + // operation. + // + + ObDereferenceObject( FileObject ); + + } + } + + // + // Complete the irp if we terminated normally. + // + + FatCompleteRequest( IrpContext, Irp, Status ); + + return Status; +} + + +// +// Local Support Routine +// + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatComputeMoveFileParameter ( + IN PIRP_CONTEXT IrpContext, + IN PFCB FcbOrDcb, + IN ULONG BufferSize, + IN ULONG FileOffset, + IN OUT PULONG ByteCount, + OUT PULONG BytesToReallocate, + OUT PULONG BytesToWrite, + OUT PLARGE_INTEGER SourceLbo +) + +/*++ + +Routine Description: + + This is a helper routine for FatMoveFile that analyses the range of + file allocation we are moving and determines the actual amount + of allocation to be moved and how much needs to be written. + +Arguments: + + FcbOrDcb - Supplies the file and its various sizes. + + BufferSize - Supplies the size of the buffer we are using to store the data + being moved. + + FileOffset - Supplies the beginning Vbo of the reallocation zone. + + ByteCount - Supplies the request length to reallocate. This will + be bounded by allocation size on return. + + BytesToReallocate - Receives ByteCount bounded by the file allocation size + and buffer size. + + BytesToWrite - Receives BytesToReallocate bounded by ValidDataLength. + + SourceLbo - Receives the logical byte offset of the source data on the volume. + +Return Value: + + VOID + +--*/ + +{ + ULONG ClusterSize; + + ULONG AllocationSize; + ULONG ValidDataLength; + ULONG ClusterAlignedVDL; + LBO RunLbo; + ULONG RunByteCount; + ULONG RunIndex; + BOOLEAN RunAllocated; + BOOLEAN RunEndOnMax; + + PAGED_CODE(); + + // + // If we haven't yet set the correct AllocationSize, do so. + // + + if (FcbOrDcb->Header.AllocationSize.QuadPart == FCB_LOOKUP_ALLOCATIONSIZE_HINT) { + + FatLookupFileAllocationSize( IrpContext, FcbOrDcb ); + + // + // If this is a non-root directory, we have a bit more to + // do since it has not gone through FatOpenDirectoryFile(). + // + + if (NodeType(FcbOrDcb) == FAT_NTC_DCB || + (NodeType(FcbOrDcb) == FAT_NTC_ROOT_DCB && FatIsFat32(FcbOrDcb->Vcb))) { + + FcbOrDcb->Header.FileSize.LowPart = + FcbOrDcb->Header.AllocationSize.LowPart; + } + } + + // + // Get the number of bytes left to write and ensure that it does + // not extend beyond allocation size. We return here if FileOffset + // is beyond AllocationSize which can happn on a truncation. + // + + AllocationSize = FcbOrDcb->Header.AllocationSize.LowPart; + ValidDataLength = FcbOrDcb->Header.ValidDataLength.LowPart; + + if (FileOffset + *ByteCount > AllocationSize) { + + if (FileOffset >= AllocationSize) { + *ByteCount = 0; + *BytesToReallocate = 0; + *BytesToWrite = 0; + + return; + } + + *ByteCount = AllocationSize - FileOffset; + } + + // + // If there is more than our max, then reduce the byte count for this + // pass to our maximum. We must also align the file offset to a + // buffer size byte boundary. + // + + if ((FileOffset & (BufferSize - 1)) + *ByteCount > BufferSize) { + + *BytesToReallocate = BufferSize - (FileOffset & (BufferSize - 1)); + + } else { + + *BytesToReallocate = *ByteCount; + } + + // + // Find where this data exists on the volume. + // + + FatLookupFileAllocation( IrpContext, + FcbOrDcb, + FileOffset, + &RunLbo, + &RunByteCount, + &RunAllocated, + &RunEndOnMax, + &RunIndex ); + + NT_ASSERT( RunAllocated ); + + // + // Limit this run to the contiguous length. + // + + if (RunByteCount < *BytesToReallocate) { + + *BytesToReallocate = RunByteCount; + } + + // + // Set the starting offset of the source. + // + + SourceLbo->QuadPart = RunLbo; + + // + // We may be able to skip some (or all) of the write + // if allocation size is significantly greater than valid data length. + // + + ClusterSize = 1 << FcbOrDcb->Vcb->AllocationSupport.LogOfBytesPerCluster; + + NT_ASSERT( ClusterSize <= BufferSize ); + + ClusterAlignedVDL = (ValidDataLength + (ClusterSize - 1)) & ~(ClusterSize - 1); + + if ((NodeType(FcbOrDcb) == FAT_NTC_FCB) && + (FileOffset + *BytesToReallocate > ClusterAlignedVDL)) { + + if (FileOffset > ClusterAlignedVDL) { + + *BytesToWrite = 0; + + } else { + + *BytesToWrite = ClusterAlignedVDL - FileOffset; + } + + } else { + + *BytesToWrite = *BytesToReallocate; + } +} + + +// +// Local Support Routine +// + +VOID +FatComputeMoveFileSplicePoints ( + IN PIRP_CONTEXT IrpContext, + IN PFCB FcbOrDcb, + IN ULONG FileOffset, + IN ULONG TargetCluster, + IN ULONG BytesToReallocate, + OUT PULONG FirstSpliceSourceCluster, + OUT PULONG FirstSpliceTargetCluster, + OUT PULONG SecondSpliceSourceCluster, + OUT PULONG SecondSpliceTargetCluster, + IN OUT PLARGE_MCB SourceMcb +) + +/*++ + +Routine Description: + + This is a helper routine for FatMoveFile that analyzes the range of + file allocation we are moving and generates the splice points in the + FAT table. + +Arguments: + + FcbOrDcb - Supplies the file and thus Mcb. + + FileOffset - Supplies the beginning Vbo of the reallocation zone. + + TargetCluster - Supplies the beginning cluster of the reallocation target. + + BytesToReallocate - Suppies the length of the reallocation zone. + + FirstSpliceSourceCluster - Receives the last cluster in previous allocation + or zero if we are reallocating from VBO 0. + + FirstSpliceTargetCluster - Receives the target cluster (i.e. new allocation) + + SecondSpliceSourceCluster - Receives the final target cluster. + + SecondSpliceTargetCluster - Receives the first cluster of the remaining + source allocation or FAT_CLUSTER_LAST if the reallocation zone + extends to the end of the file. + + SourceMcb - This supplies an MCB that will be filled in with run + information describing the file allocation being replaced. The Mcb + must be initialized by the caller. + +Return Value: + + VOID + +--*/ + +{ + VBO SourceVbo; + LBO SourceLbo; + ULONG SourceIndex; + ULONG SourceBytesInRun; + ULONG SourceBytesRemaining; + + ULONG SourceMcbVbo = 0; + ULONG SourceMcbBytesInRun = 0; + + PVCB Vcb; + BOOLEAN Result; + + PAGED_CODE(); + + Vcb = FcbOrDcb->Vcb; + + // + // Get information on the final cluster in the previous allocation and + // prepare to enumerate it in the follow loop. + // + + if (FileOffset == 0) { + + SourceIndex = 0; + *FirstSpliceSourceCluster = 0; + Result = FatGetNextMcbEntry( Vcb, &FcbOrDcb->Mcb, + 0, + &SourceVbo, + &SourceLbo, + &SourceBytesInRun ); + + } else { + + Result = FatLookupMcbEntry( Vcb, &FcbOrDcb->Mcb, + FileOffset-1, + &SourceLbo, + &SourceBytesInRun, + &SourceIndex); + + *FirstSpliceSourceCluster = FatGetIndexFromLbo( Vcb, SourceLbo ); + + if ((Result) && (SourceBytesInRun == 1)) { + + SourceIndex += 1; + Result = FatGetNextMcbEntry( Vcb, &FcbOrDcb->Mcb, + SourceIndex, + &SourceVbo, + &SourceLbo, + &SourceBytesInRun); + + } else { + + SourceVbo = FileOffset; + SourceLbo += 1; + SourceBytesInRun -= 1; + } + } + + // + // Run should always be present, but don't bugcheck in the case where it's not. + // + + if (!Result) { + + NT_ASSERT( FALSE); + FatRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR); + } + + // + // At this point the variables: + // + // - SourceIndex - SourceLbo - SourceBytesInRun - + // + // all correctly decribe the allocation to be removed. In the loop + // below we will start here and continue enumerating the Mcb runs + // until we are finished with the allocation to be relocated. + // + + *FirstSpliceTargetCluster = TargetCluster; + + *SecondSpliceSourceCluster = + *FirstSpliceTargetCluster + + (BytesToReallocate >> Vcb->AllocationSupport.LogOfBytesPerCluster) - 1; + + for (SourceBytesRemaining = BytesToReallocate, SourceMcbVbo = 0; + + SourceBytesRemaining > 0; + + SourceIndex += 1, + SourceBytesRemaining -= SourceMcbBytesInRun, + SourceMcbVbo += SourceMcbBytesInRun) { + + if (SourceMcbVbo != 0) { +#pragma prefast( suppress:28931, "needed for debug build" ) + Result = FatGetNextMcbEntry( Vcb, &FcbOrDcb->Mcb, + SourceIndex, + &SourceVbo, + &SourceLbo, + &SourceBytesInRun ); + NT_ASSERT( Result); + } + + NT_ASSERT( SourceVbo == SourceMcbVbo + FileOffset ); + + SourceMcbBytesInRun = + SourceBytesInRun < SourceBytesRemaining ? + SourceBytesInRun : SourceBytesRemaining; + + FatAddMcbEntry( Vcb, SourceMcb, + SourceMcbVbo, + SourceLbo, + SourceMcbBytesInRun ); + } + + // + // Now compute the cluster of the target of the second + // splice. If the final run in the above loop was + // more than we needed, then we can just do arithmetic, + // otherwise we have to look up the next run. + // + + if (SourceMcbBytesInRun < SourceBytesInRun) { + + *SecondSpliceTargetCluster = + FatGetIndexFromLbo( Vcb, SourceLbo + SourceMcbBytesInRun ); + + } else { + + if (FatGetNextMcbEntry( Vcb, &FcbOrDcb->Mcb, + SourceIndex, + &SourceVbo, + &SourceLbo, + &SourceBytesInRun )) { + + *SecondSpliceTargetCluster = FatGetIndexFromLbo( Vcb, SourceLbo ); + + } else { + + *SecondSpliceTargetCluster = FAT_CLUSTER_LAST; + } + } +} + + +NTSTATUS +FatAllowExtendedDasdIo( + IN PIRP_CONTEXT IrpContext, + IN 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 being processed. + +Return Value: + + NTSTATUS - The return status for the operation. + +--*/ +{ + PIO_STACK_LOCATION IrpSp; + PVCB Vcb; + PFCB Fcb; + PCCB Ccb; + + PAGED_CODE(); + + // + // Get the current Irp stack location and save some references. + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + // + // Extract and decode the file object and check for type of open. + // + + if (FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb ) != UserVolumeOpen) { + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + return STATUS_INVALID_PARAMETER; + } + + if ((Ccb == NULL) || !FlagOn( Ccb->Flags, CCB_FLAG_MANAGE_VOLUME_ACCESS )) { + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + + DebugTrace(-1, Dbg, "FatAllowExtendedDasdIo -> %08lx\n", STATUS_INVALID_PARAMETER); + return STATUS_INVALID_PARAMETER; + } + + SetFlag( Ccb->Flags, CCB_FLAG_ALLOW_EXTENDED_DASD_IO ); + + FatCompleteRequest( IrpContext, Irp, STATUS_SUCCESS ); + return STATUS_SUCCESS; +} + +#if (NTDDI_VERSION >= NTDDI_WIN7) + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatGetRetrievalPointerBase ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PIRP Irp + ) +/*++ + +Routine Description: + + This routine retrieves the sector offset to the first allocation unit. + +Arguments: + + IrpContext - Supplies the Irp Context. + Irp - Supplies the Irp being processed. + +Return Value: + + NTSTATUS - The return status for the operation. + +--*/ +{ + PIO_STACK_LOCATION IrpSp = NULL; + PVCB Vcb = NULL; + PFCB Fcb = NULL; + PCCB Ccb = NULL; + ULONG BufferLength = 0; + PRETRIEVAL_POINTER_BASE RetrievalPointerBase = NULL; + + PAGED_CODE(); + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + // + // Force WAIT to true. + // + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT); + + // + // Extract and decode the file object and check for type of open. + // + + if (FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb ) != UserVolumeOpen) { + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + return STATUS_INVALID_PARAMETER; + } + + // + // Extract the buffer + // + + RetrievalPointerBase = Irp->AssociatedIrp.SystemBuffer; + BufferLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength; + + // + // Verify the handle has manage volume access. + // + + if ((Ccb == NULL) || !FlagOn( Ccb->Flags, CCB_FLAG_MANAGE_VOLUME_ACCESS )) { + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + return STATUS_INVALID_PARAMETER; + } + + // + // Validate the output buffer is the right size. + // + // Note that the default size of BOOT_AREA_INFO has enough room for 2 boot sectors, so we're fine. + // + + if (BufferLength < sizeof(RETRIEVAL_POINTER_BASE)) { + + FatCompleteRequest( IrpContext, Irp, STATUS_BUFFER_TOO_SMALL ); + return STATUS_BUFFER_TOO_SMALL; + } + + // + // Fill out the offset to the file area. + // + + RtlZeroMemory( RetrievalPointerBase, BufferLength ); + + try { + + FatAcquireSharedVcb(IrpContext, Vcb); + FatQuickVerifyVcb(IrpContext, Vcb); + + RetrievalPointerBase->FileAreaOffset.QuadPart = Vcb->AllocationSupport.FileAreaLbo >> Vcb->AllocationSupport.LogOfBytesPerSector; + Irp->IoStatus.Information = sizeof( RETRIEVAL_POINTER_BASE ); + + } finally { + + FatReleaseVcb(IrpContext, Vcb); + + } + + FatCompleteRequest( IrpContext, Irp, STATUS_SUCCESS ); + + return STATUS_SUCCESS; + +} + + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatGetBootAreaInfo ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PIRP Irp + ) +/*++ + +Routine Description: + + This routine retrieves information about the boot areas of the filesystem. + +Arguments: + + IrpContext - Supplies the Irp Context. + Irp - Supplies the Irp being processed. + +Return Value: + + NTSTATUS - The return status for the operation. + +--*/ +{ + PIO_STACK_LOCATION IrpSp = NULL; + PVCB Vcb = NULL; + PFCB Fcb = NULL; + PCCB Ccb = NULL; + ULONG BufferLength = 0; + PBOOT_AREA_INFO BootAreaInfo = NULL; + + PAGED_CODE(); + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + // + // Force WAIT to true. + // + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT); + + // + // Extract and decode the file object and check for type of open. + // + + if (FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb ) != UserVolumeOpen) { + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + return STATUS_INVALID_PARAMETER; + } + + // + // Extract the buffer + // + + BootAreaInfo = Irp->AssociatedIrp.SystemBuffer; + BufferLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength; + + // + // Verify the handle has manage volume access. + // + + if ((Ccb == NULL) || !FlagOn( Ccb->Flags, CCB_FLAG_MANAGE_VOLUME_ACCESS )) { + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + return STATUS_INVALID_PARAMETER; + } + + // + // Validate the output buffer is the right size. + // + // Note that the default size of BOOT_AREA_INFO has enough room for 2 boot sectors, so we're fine. + // + + if (BufferLength < sizeof(BOOT_AREA_INFO)) { + + FatCompleteRequest( IrpContext, Irp, STATUS_BUFFER_TOO_SMALL ); + return STATUS_BUFFER_TOO_SMALL; + } + + // + // Fill out our boot areas. + // + + RtlZeroMemory( BootAreaInfo, BufferLength ); + + try { + + FatAcquireSharedVcb(IrpContext, Vcb); + FatQuickVerifyVcb(IrpContext, Vcb); + + if (FatIsFat32( Vcb )) { + + BootAreaInfo->BootSectorCount = 2; + BootAreaInfo->BootSectors[0].Offset.QuadPart = 0; + BootAreaInfo->BootSectors[1].Offset.QuadPart = 6; + } else { + + BootAreaInfo->BootSectorCount = 1; + BootAreaInfo->BootSectors[0].Offset.QuadPart = 0; + } + + Irp->IoStatus.Information = sizeof( BOOT_AREA_INFO ); + + } finally { + + FatReleaseVcb(IrpContext, Vcb); + } + + FatCompleteRequest( IrpContext, Irp, STATUS_SUCCESS ); + return STATUS_SUCCESS; +} + +#endif + + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatMarkHandle ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PIRP Irp + ) +/*++ + +Routine Description: + + This routine is used to attach special properties to a user handle. + +Arguments: + + IrpContext - Supplies the Irp Context. + Irp - Supplies the Irp being processed. + +Return Value: + + NTSTATUS - The return status for the operation. + +--*/ +{ + NTSTATUS Status = STATUS_SUCCESS; + PIO_STACK_LOCATION IrpSp = NULL; + PVCB Vcb = NULL; + PFCB Fcb = NULL; + PCCB Ccb = NULL; + PFCB DasdFcb = NULL; + PCCB DasdCcb = NULL; + TYPE_OF_OPEN TypeOfOpen; + PMARK_HANDLE_INFO HandleInfo = NULL; + PFILE_OBJECT DasdFileObject = NULL; + BOOLEAN ReleaseFcb = FALSE; + +#if defined(_WIN64) && defined(BUILD_WOW64_ENABLED) + MARK_HANDLE_INFO LocalMarkHandleInfo = {0}; +#endif + + PAGED_CODE(); + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + // + // Always make this a synchronous IRP. + // + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ); + + // + // Extract and decode the file object and check for type of open. + // + + TypeOfOpen = FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb ) ; + + // + // We currently support this call for files and directories only. + // + + if ((TypeOfOpen != UserFileOpen) && + (TypeOfOpen != UserDirectoryOpen)) { + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + return STATUS_INVALID_PARAMETER; + } + +#if defined(_WIN64) && defined(BUILD_WOW64_ENABLED) + + // + // Win32/64 thunking code + // + + if (IoIs32bitProcess( Irp )) { + + PMARK_HANDLE_INFO32 MarkHandle32; + + if (IrpSp->Parameters.FileSystemControl.InputBufferLength < sizeof( MARK_HANDLE_INFO32 )) { + + FatCompleteRequest( IrpContext, Irp, STATUS_BUFFER_TOO_SMALL ); + return STATUS_BUFFER_TOO_SMALL; + } + + MarkHandle32 = (PMARK_HANDLE_INFO32) Irp->AssociatedIrp.SystemBuffer; + LocalMarkHandleInfo.HandleInfo = MarkHandle32->HandleInfo; + LocalMarkHandleInfo.UsnSourceInfo = MarkHandle32->UsnSourceInfo; + LocalMarkHandleInfo.VolumeHandle = (HANDLE)(ULONG_PTR)(LONG) MarkHandle32->VolumeHandle; + + HandleInfo = &LocalMarkHandleInfo; + + } else { + +#endif + + // + // Get the input buffer pointer and check its length. + // + + if (IrpSp->Parameters.FileSystemControl.InputBufferLength < sizeof( MARK_HANDLE_INFO )) { + + FatCompleteRequest( IrpContext, Irp, STATUS_BUFFER_TOO_SMALL ); + return STATUS_BUFFER_TOO_SMALL; + } + + HandleInfo = (PMARK_HANDLE_INFO) Irp->AssociatedIrp.SystemBuffer; + +#if defined(_WIN64) && defined(BUILD_WOW64_ENABLED) + } +#endif + + // + // Check that only legal bits are being set. + // We currently only support two bits: protect clusters and unprotect clusters. + // + // Note that we don't actually support the USN journal, but we must ignore the flags in order + // to preserve compatibility. + // + + if (FlagOn( HandleInfo->HandleInfo, + ~(MARK_HANDLE_PROTECT_CLUSTERS)) || + (FlagOn( HandleInfo->HandleInfo, + 0 ) && + (IrpSp->MinorFunction != IRP_MN_KERNEL_CALL)) || + FlagOn(HandleInfo->UsnSourceInfo, + ~(USN_SOURCE_DATA_MANAGEMENT | + USN_SOURCE_AUXILIARY_DATA | + USN_SOURCE_REPLICATION_MANAGEMENT) ) ) { + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + return STATUS_INVALID_PARAMETER; + } + + // + // Check that the user has a valid volume handle or the manage volume + // privilege or is a kernel mode caller + // + // NOTE: the kernel mode check is only valid because the rdr doesn't support this + // FSCTL. + // + + if ((Irp->RequestorMode != KernelMode) && + (IrpSp->MinorFunction != IRP_MN_KERNEL_CALL) && + !FlagOn( Ccb->Flags, CCB_FLAG_MANAGE_VOLUME_ACCESS ) && + ( FlagOn( HandleInfo->HandleInfo, MARK_HANDLE_PROTECT_CLUSTERS ) || (HandleInfo->UsnSourceInfo != 0) )) { + + if (HandleInfo->VolumeHandle == 0) { + FatCompleteRequest( IrpContext, Irp, STATUS_ACCESS_DENIED ); + return STATUS_ACCESS_DENIED; + } + + Status = ObReferenceObjectByHandle( HandleInfo->VolumeHandle, + 0, + *IoFileObjectType, + UserMode, + &DasdFileObject, + NULL ); + + if (!NT_SUCCESS(Status)) { + + FatCompleteRequest( IrpContext, Irp, Status ); + return Status; + } + + // + // Check that this file object is opened on the same volume as the + // handle used to call this routine. + // + + if (DasdFileObject->Vpb != Vcb->Vpb) { + + ObDereferenceObject( DasdFileObject ); + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + return STATUS_INVALID_PARAMETER; + } + + // + // Now decode this FileObject and verify it is a volume handle. + // We don't care to raise on dismounts here because + // we check for that further down anyway. So send FALSE. + // + +#pragma prefast( suppress:28931, "convenient for debugging" ) + TypeOfOpen = FatDecodeFileObject( DasdFileObject, &Vcb, &DasdFcb, &DasdCcb ) ; + + ObDereferenceObject( DasdFileObject ); + + if ((DasdCcb == NULL) || !FlagOn( DasdCcb->Flags, CCB_FLAG_MANAGE_VOLUME_ACCESS )) { + + FatCompleteRequest( IrpContext, Irp, STATUS_ACCESS_DENIED ); + return STATUS_ACCESS_DENIED; + } + + } + + try { + + FatAcquireExclusiveFcb(IrpContext, Fcb); + ReleaseFcb = TRUE; + + FatVerifyFcb( IrpContext, Fcb ); + + if (HandleInfo->HandleInfo & MARK_HANDLE_PROTECT_CLUSTERS) { + + if (Fcb->FcbState & FCB_STATE_DENY_DEFRAG) { + + // + // It's already set, bail out. + // + + try_return( Status = STATUS_ACCESS_DENIED ); + } + + Ccb->Flags |= CCB_FLAG_DENY_DEFRAG; + Fcb->FcbState|= FCB_STATE_DENY_DEFRAG; + + } + + try_exit: NOTHING; + + } finally { + + if (ReleaseFcb) { + + FatReleaseFcb(IrpContext, Fcb); + } + + } + + FatCompleteRequest( IrpContext, Irp, Status ); + return Status; +} + + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatFlushAndCleanVolume( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp, + IN PVCB Vcb, + IN FAT_FLUSH_TYPE FlushType + ) +/*++ + +Routine Description: + + This routine flushes and otherwise preparse a volume to be eligible + for deletion. The dismount and PNP paths share the need for this + common work. + + The Vcb will always be valid on return from this function. It is the + caller's responsibility to attempt the dismount/deletion, and to setup + allocation support again if the volume will be brought back from the + brink. + +Arguments: + + Irp - Irp for the overlying request + + Vcb - the volume being operated on + + FlushType - specifies the kind of flushing desired + +Return Value: + + NTSTATUS - The return status for the operation. + +--*/ +{ + PAGED_CODE(); + + // + // The volume must be held exclusive. + // + + NT_ASSERT( FatVcbAcquiredExclusive( IrpContext, Vcb )); + + // + // There is no fail, flush everything. If invalidating, it is important + // that we invalidate as we flush (eventually, w/ paging io held) so that we + // error out the maximum number of late writes. + // + + if (FlushType != NoFlush) { + + (VOID) FatFlushVolume( IrpContext, Vcb, FlushType ); + } + + FatCloseEaFile( IrpContext, Vcb, FALSE ); + + // + // Now, tell the device to flush its buffers. + // + + if (FlushType != NoFlush) { + + (VOID)FatHijackIrpAndFlushDevice( IrpContext, Irp, Vcb->TargetDeviceObject ); + } + + // + // Now purge everything in sight. We're trying to provoke as many closes as + // soon as possible, this volume may be on its way out. + // + + if (FlushType != FlushWithoutPurge) { + + CcPurgeCacheSection( &Vcb->SectionObjectPointers, + NULL, + 0, + FALSE ); + + (VOID) FatPurgeReferencedFileObjects( IrpContext, Vcb->RootDcb, NoFlush ); + } + + // + // If the volume was dirty and we were allowed to flush, do the processing that + // the delayed callback would have done. + // + + if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DIRTY)) { + + // + // Cancel any pending clean volumes. + // + + (VOID)KeCancelTimer( &Vcb->CleanVolumeTimer ); + (VOID)KeRemoveQueueDpc( &Vcb->CleanVolumeDpc ); + + + if (FlushType != NoFlush) { + + // + // The volume is now clean, note it. + // + + if (!FlagOn(Vcb->VcbState, VCB_STATE_FLAG_MOUNTED_DIRTY)) { + + FatMarkVolume( IrpContext, Vcb, VolumeClean ); + ClearFlag( Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DIRTY ); + } + + // + // Unlock the volume if it is removable. + // + + if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA) && + !FlagOn(Vcb->VcbState, VCB_STATE_FLAG_BOOT_OR_PAGING_FILE)) { + + FatToggleMediaEjectDisable( IrpContext, Vcb, FALSE ); + } + } + } + +} + +#if (NTDDI_VERSION >= NTDDI_WIN8) + + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatSetPurgeFailureMode ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PIRP Irp + ) +/*++ + + This routine is used to enable or disable the purge failure mode + on a file. When in this mode the file system will propagate purge + failures encountered during coherency purges. Normally these are + ignored for application compatibilty purposes. Since the normal + behavior can lead to cache incoherency there needs to be a way to + force error propagation, particulary when a filter has mapped a + section for the purposes of scanning the file in the background. + + The purge failure mode is a reference count because it is set + per mapped section and there may be multiple sections backed by + the file. + +Arguments: + + IrpContext - Supplies the Irp Context. + Irp - Supplies the Irp being processed. + +Return Value: + + NTSTATUS - The return status for the operation. + +--*/ +{ + NTSTATUS Status = STATUS_SUCCESS; + PIO_STACK_LOCATION IrpSp; + TYPE_OF_OPEN TypeOfOpen; + PVCB Vcb; + PFCB Fcb; + PCCB Ccb; + PSET_PURGE_FAILURE_MODE_INPUT SetPurgeInput; + BOOLEAN FcbAcquired = FALSE; + + PAGED_CODE(); + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + // + // Force WAIT to true. + // + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ); + + // + // This has to be a kernel only call. Can't let a user request + // change the purge failure mode count + // + + if (Irp->RequestorMode != KernelMode) { + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + return STATUS_INVALID_PARAMETER; + } + + // + // Extract and decode the file object and check for type of open. + // + + TypeOfOpen = FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb ); + + if (TypeOfOpen == UserDirectoryOpen) { + + FatCompleteRequest( IrpContext, Irp, STATUS_FILE_IS_A_DIRECTORY ); + return STATUS_FILE_IS_A_DIRECTORY; + } + + if (TypeOfOpen != UserFileOpen) { + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + return STATUS_INVALID_PARAMETER; + } + + // + // Get the input buffer pointer and check its length. + // + + if (IrpSp->Parameters.FileSystemControl.InputBufferLength < sizeof( SET_PURGE_FAILURE_MODE_INPUT )) { + + FatCompleteRequest( IrpContext, Irp, STATUS_BUFFER_TOO_SMALL ); + return STATUS_BUFFER_TOO_SMALL; + } + + SetPurgeInput = (PSET_PURGE_FAILURE_MODE_INPUT) Irp->AssociatedIrp.SystemBuffer; + + if (!FlagOn( SetPurgeInput->Flags, SET_PURGE_FAILURE_MODE_ENABLED | SET_PURGE_FAILURE_MODE_DISABLED )) { + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + return STATUS_INVALID_PARAMETER; + } + + try { + + // + // Acquire the FCB exclusively to synchronize with coherency flush + // and purge. + // + + FatAcquireExclusiveFcb( IrpContext, Fcb ); + FcbAcquired = TRUE; + + FatVerifyFcb( IrpContext, Fcb ); + + if (FlagOn( SetPurgeInput->Flags, SET_PURGE_FAILURE_MODE_ENABLED )) { + + if (Fcb->PurgeFailureModeEnableCount == MAXULONG) { + + try_return( Status = STATUS_INVALID_PARAMETER ); + } + + Fcb->PurgeFailureModeEnableCount += 1; + + } else { + + ASSERT( FlagOn( SetPurgeInput->Flags, SET_PURGE_FAILURE_MODE_DISABLED )); + + if (Fcb->PurgeFailureModeEnableCount == 0) { + + try_return( Status = STATUS_INVALID_PARAMETER ); + } + + Fcb->PurgeFailureModeEnableCount -= 1; + } + + try_exit: NOTHING; + + } finally { + + if (FcbAcquired) { + FatReleaseFcb( IrpContext, Fcb ); + } + } + + // + // Complete the irp if we terminated normally. + // + + FatCompleteRequest( IrpContext, Irp, Status ); + + return Status; +} + +#endif + + +NTSTATUS +FatSearchBufferForLabel( + IN PIRP_CONTEXT IrpContext, + IN PVPB Vpb, + IN PVOID Buffer, + IN ULONG Size, + OUT PBOOLEAN LabelFound + ) +/*++ + +Routine Description: + + Search a buffer (taken from the root directory) for a volume label + matching the label in the + +Arguments: + + IrpContext - Supplies our irp context + Vpb - Vpb supplying the volume label + Buffer - Supplies the buffer we'll search + Size - The size of the buffer in bytes. + LabelFound - Returns whether a label was found. + +Return Value: + + There are four interesting cases: + + 1) Some random error occurred - that error returned as status, LabelFound + is indeterminate. + + 2) No label was found - STATUS_SUCCESS returned, LabelFound is FALSE. + + 3) A matching label was found - STATUS_SUCCESS returned, LabelFound is TRUE. + + 4) A non-matching label found - STATUS_WRONG_VOLUME returned, LabelFound + is indeterminate. + +--*/ + +{ + NTSTATUS Status; + WCHAR UnicodeBuffer[11]; + + PDIRENT Dirent; + PDIRENT TerminationDirent; + ULONG VolumeLabelLength; + OEM_STRING OemString; + UNICODE_STRING UnicodeString; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( IrpContext ); + + Dirent = Buffer; + + TerminationDirent = Dirent + Size / sizeof(DIRENT); + + while ( Dirent < TerminationDirent ) { + + if ( Dirent->FileName[0] == FAT_DIRENT_NEVER_USED ) { + + Dirent = TerminationDirent; + break; + } + + // + // If the entry is the non-deleted volume label break from the loop. + // + // Note that all out parameters are already correctly set. + // + + if (((Dirent->Attributes & ~FAT_DIRENT_ATTR_ARCHIVE) == + FAT_DIRENT_ATTR_VOLUME_ID) && + (Dirent->FileName[0] != FAT_DIRENT_DELETED)) { + + break; + } + + Dirent += 1; + } + + if (Dirent >= TerminationDirent) { + + // + // We've run out of buffer. + // + + *LabelFound = FALSE; + return STATUS_SUCCESS; + } + + + // + // Compute the length of the volume name + // + + OemString.Buffer = (PCHAR)&Dirent->FileName[0]; + OemString.MaximumLength = 11; + + for ( OemString.Length = 11; + OemString.Length > 0; + OemString.Length -= 1) { + + if ( (Dirent->FileName[OemString.Length-1] != 0x00) && + (Dirent->FileName[OemString.Length-1] != 0x20) ) { break; } + } + + UnicodeString.MaximumLength = sizeof( UnicodeBuffer ); + UnicodeString.Buffer = &UnicodeBuffer[0]; + + Status = RtlOemStringToCountedUnicodeString( &UnicodeString, + &OemString, + FALSE ); + + if ( !NT_SUCCESS( Status ) ) { + + return Status; + } + + VolumeLabelLength = UnicodeString.Length; + + if ( (VolumeLabelLength != (ULONG)Vpb->VolumeLabelLength) || + (!RtlEqualMemory(&UnicodeBuffer[0], + &Vpb->VolumeLabel[0], + VolumeLabelLength)) ) { + + return STATUS_WRONG_VOLUME; + } + + // + // We found a matching label. + // + + *LabelFound = TRUE; + return STATUS_SUCCESS; +} + + +VOID +FatVerifyLookupFatEntry ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN ULONG FatIndex, + IN OUT PULONG FatEntry + ) +{ + ULONG PageEntryOffset; + ULONG OffsetIntoVolumeFile; + PVOID Buffer; + + PAGED_CODE(); + + NT_ASSERT(Vcb->AllocationSupport.FatIndexBitSize == 32); + + FatVerifyIndexIsValid( IrpContext, Vcb, FatIndex); + + Buffer = FsRtlAllocatePoolWithTag( NonPagedPoolNxCacheAligned, + PAGE_SIZE, + TAG_ENTRY_LOOKUP_BUFFER ); + + OffsetIntoVolumeFile = FatReservedBytes(&Vcb->Bpb) + FatIndex * sizeof(ULONG); + PageEntryOffset = (OffsetIntoVolumeFile % PAGE_SIZE) / sizeof(ULONG); + + try { + + FatPerformVerifyDiskRead( IrpContext, + Vcb, + Buffer, + OffsetIntoVolumeFile & ~(PAGE_SIZE - 1), + PAGE_SIZE, + TRUE ); + + *FatEntry = ((PULONG)(Buffer))[PageEntryOffset]; + + } finally { + + ExFreePool( Buffer ); + } +} + +// +// Local support routine +// + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatScanForDismountedVcb ( + IN 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; + BOOLEAN VcbDeleted; + + + PAGED_CODE(); + + // + // Walk through all of the Vcb's attached to the global data. + // + + NT_ASSERT( FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) ); + +#pragma prefast( push ) +#pragma prefast( disable: 28137, "prefast wants the wait to be a constant, but that isn't possible for the way fastfat is designed" ) +#pragma prefast( disable: 28193, "this will always wait" ) + + FatAcquireExclusiveGlobal( IrpContext ); + +#pragma prefast( pop ) + + Links = FatData.VcbQueue.Flink; + + while (Links != &FatData.VcbQueue) { + + Vcb = CONTAINING_RECORD( Links, VCB, VcbLinks ); + + // + // Move to the next link now since the current Vcb may be deleted. + // + + Links = Links->Flink; + + // + // Try to acquire the VCB for exclusive access. If we cannot, just skip + // it for now. + // + +#pragma prefast( push ) +#pragma prefast( disable:28103,"prefast cannot work out that Vcb->Resource will be released below." ) +#pragma prefast( disable:28109,"prefast cannot work out the Vcb is not already held" ); + + if (!ExAcquireResourceExclusiveLite( &(Vcb->Resource), FALSE )) { + + continue; + } + +#pragma prefast( pop ) + // + // Check if this Vcb can go away. + // + + VcbDeleted = FatCheckForDismount( IrpContext, + Vcb, + FALSE ); + + // + // If the VCB was not deleted, release it. + // + + if (!VcbDeleted) { + + ExReleaseResourceLite( &(Vcb->Resource) ); + } + } + + FatReleaseGlobal( IrpContext); + + return; +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// FatSetZeroOnDeallocate is used when we need to stomp over the contents with zeros when a file is deleted. +// + +NTSTATUS +FatSetZeroOnDeallocate ( + __in PIRP_CONTEXT IrpContext, + __in PIRP Irp + ) +{ + NTSTATUS Status = STATUS_SUCCESS; + + PVCB Vcb; + PFCB FcbOrDcb; + PCCB Ccb; + + TYPE_OF_OPEN TypeOfOpen; + + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + BOOLEAN ReleaseFcb = FALSE; + + PAGED_CODE(); + + // + // This call should always be synchronous. + // + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ); + + TypeOfOpen = FatDecodeFileObject( IrpSp->FileObject, &Vcb, &FcbOrDcb, &Ccb ); + + if ((TypeOfOpen != UserFileOpen) || + (!IrpSp->FileObject->WriteAccess) ) { + return STATUS_ACCESS_DENIED; + } + + // + // Readonly mount should be just that: read only. + // + + if (FlagOn( Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED)) { + + FatCompleteRequest( IrpContext, Irp, STATUS_MEDIA_WRITE_PROTECTED ); + return STATUS_MEDIA_WRITE_PROTECTED; + } + + // + // Acquire main then paging to exclude everyone from this FCB. + // + + FatAcquireExclusiveFcb(IrpContext, FcbOrDcb); + ReleaseFcb = TRUE; + + try { + + SetFlag( FcbOrDcb->FcbState, FCB_STATE_ZERO_ON_DEALLOCATION ); + + } finally { + + if (ReleaseFcb) { + FatReleaseFcb(IrpContext, FcbOrDcb); + } + + } + + FatCompleteRequest( IrpContext, Irp, Status ); + return Status; +} + diff --git a/filesys/fastfat/fspdisp.c b/filesys/fastfat/fspdisp.c new file mode 100644 index 000000000..ef59c7e3b --- /dev/null +++ b/filesys/fastfat/fspdisp.c @@ -0,0 +1,484 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + FspDisp.c + +Abstract: + + This module implements the main dispatch procedure/thread for the Fat + Fsp + + +--*/ + +#include "FatProcs.h" + +// +// Internal support routine, spinlock wrapper. +// + +PVOID +FatRemoveOverflowEntry ( + IN PVOLUME_DEVICE_OBJECT VolDo + ); + +// +// Define our local debug trace level +// + +#define Dbg (DEBUG_TRACE_FSP_DISPATCHER) + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, FatFspDispatch) +#endif + + +VOID +FatFspDispatch ( + _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: + + + Context - Supplies the thread id. + +Return Value: + + None - This routine never exits + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + + + PIRP Irp; + PIRP_CONTEXT IrpContext; + PIO_STACK_LOCATION IrpSp; + BOOLEAN VcbDeleted; + BOOLEAN ExceptionCompletedIrp = FALSE; + + PVOLUME_DEVICE_OBJECT VolDo; + + UCHAR MajorFunction = 0; + + PAGED_CODE(); + + IrpContext = (PIRP_CONTEXT)Context; + + Irp = IrpContext->OriginatingIrp; + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + // + // Now because we are the Fsp we will force the IrpContext to + // indicate true on Wait. + // + + SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT | IRP_CONTEXT_FLAG_IN_FSP); + + // + // If this request has an associated volume device object, remember it. + // + + if ( IrpSp->FileObject != NULL ) { + + VolDo = CONTAINING_RECORD( IrpSp->DeviceObject, + VOLUME_DEVICE_OBJECT, + DeviceObject ); + } else { + + VolDo = NULL; + } + + // + // Now case on the function code. For each major function code, + // either call the appropriate FSP routine or case on the minor + // function and then call the FSP routine. The FSP 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 (e.g., if FatReadSectorsSync has trouble). + // + + while ( TRUE ) { + + ExceptionCompletedIrp = FALSE; + + DebugTrace(0, Dbg, "FatFspDispatch: Irp = %p\n", Irp); + + // + // If this Irp was top level, note it in our thread local storage. + // + + FsRtlEnterFileSystem(); + + if ( FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_RECURSIVE_CALL) ) { + + IoSetTopLevelIrp( (PIRP)FSRTL_FSP_TOP_LEVEL_IRP ); + + } else { + + IoSetTopLevelIrp( Irp ); + } + + MajorFunction = IrpContext->MajorFunction; + + try { + + switch ( MajorFunction ) { + + // + // For Create Operation, + // + + case IRP_MJ_CREATE: + + Status = FatCommonCreate( IrpContext, Irp ); + break; + + // + // For close operations. We do a little kludge here in case + // this close causes a volume to go away. It will NULL the + // VolDo local variable so that we will not try to look at + // the overflow queue. + // + + case IRP_MJ_CLOSE: + + { + PVCB Vcb; + PFCB Fcb; + PCCB Ccb; + TYPE_OF_OPEN TypeOfOpen; + + // + // Extract and decode the file object + // + + TypeOfOpen = FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb ); + + // + // Do the close. We have a slightly different format + // for this call because of the async closes. + // + + Status = FatCommonClose( Vcb, + Fcb, + Ccb, + TypeOfOpen, + TRUE, + TRUE, + &VcbDeleted ); + + // + // If the VCB was deleted, do not try to access it later. + // + + if (VcbDeleted) { + + VolDo = NULL; + } + + NT_ASSERT(Status == STATUS_SUCCESS); + + FatCompleteRequest( IrpContext, Irp, Status ); + + break; + } + + // + // For read operations + // + + case IRP_MJ_READ: + + (VOID) FatCommonRead( IrpContext, Irp ); + break; + + // + // For write operations, + // + + case IRP_MJ_WRITE: + + (VOID) FatCommonWrite( IrpContext, Irp ); + break; + + // + // For Query Information operations, + // + + case IRP_MJ_QUERY_INFORMATION: + + (VOID) FatCommonQueryInformation( IrpContext, Irp ); + break; + + // + // For Set Information operations, + // + + case IRP_MJ_SET_INFORMATION: + + (VOID) FatCommonSetInformation( IrpContext, Irp ); + break; + + // + // For Query EA operations, + // + + case IRP_MJ_QUERY_EA: + + (VOID) FatCommonQueryEa( IrpContext, Irp ); + break; + + // + // For Set EA operations, + // + + case IRP_MJ_SET_EA: + + (VOID) FatCommonSetEa( IrpContext, Irp ); + break; + + // + // For Flush buffers operations, + // + + case IRP_MJ_FLUSH_BUFFERS: + + (VOID) FatCommonFlushBuffers( IrpContext, Irp ); + break; + + // + // For Query Volume Information operations, + // + + case IRP_MJ_QUERY_VOLUME_INFORMATION: + + (VOID) FatCommonQueryVolumeInfo( IrpContext, Irp ); + break; + + // + // For Set Volume Information operations, + // + + case IRP_MJ_SET_VOLUME_INFORMATION: + + (VOID) FatCommonSetVolumeInfo( IrpContext, Irp ); + break; + + // + // For File Cleanup operations, + // + + case IRP_MJ_CLEANUP: + + (VOID) FatCommonCleanup( IrpContext, Irp ); + break; + + // + // For Directory Control operations, + // + + case IRP_MJ_DIRECTORY_CONTROL: + + (VOID) FatCommonDirectoryControl( IrpContext, Irp ); + break; + + // + // For File System Control operations, + // + + case IRP_MJ_FILE_SYSTEM_CONTROL: + + (VOID) FatCommonFileSystemControl( IrpContext, Irp ); + break; + + // + // For Lock Control operations, + // + + case IRP_MJ_LOCK_CONTROL: + + (VOID) FatCommonLockControl( IrpContext, Irp ); + break; + + // + // For Device Control operations, + // + + case IRP_MJ_DEVICE_CONTROL: + + (VOID) FatCommonDeviceControl( IrpContext, Irp ); + break; + + // + // For the Shutdown operation, + // + + case IRP_MJ_SHUTDOWN: + + (VOID) FatCommonShutdown( IrpContext, Irp ); + break; + + // + // For plug and play operations. + // + + case IRP_MJ_PNP: + + // + // I don't believe this should ever occur, but allow for the unexpected. + // + + (VOID) FatCommonPnp( IrpContext, Irp ); + break; + + // + // For any other major operations, return an invalid + // request. + // + + default: + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_DEVICE_REQUEST ); + break; + + } + + } except(FatExceptionFilter( IrpContext, GetExceptionInformation() )) { + + // + // We had some trouble trying to perform the requested + // operation, so we'll abort the I/O request with + // the error status that we get back from the + // execption code. + // + + (VOID) FatProcessException( IrpContext, Irp, GetExceptionCode() ); + ExceptionCompletedIrp = TRUE; + } + + IoSetTopLevelIrp( NULL ); + + FsRtlExitFileSystem(); + + + if (MajorFunction == IRP_MJ_CREATE && !ExceptionCompletedIrp && Status != STATUS_PENDING) { + + // + // Creates are completed here. IrpContext is also freed here. + // + + FatCompleteRequest( IrpContext, Irp, Status ); + } + + // + // If there are any entries on this volume's overflow queue, service + // them. + // + + if ( VolDo != NULL ) { + + PVOID Entry; + + // + // We have a volume device object so see if there is any work + // left to do in its overflow queue. + // + + Entry = FatRemoveOverflowEntry( VolDo ); + + // + // 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, and IrpSp, and loop. + // + + IrpContext = CONTAINING_RECORD( Entry, + IRP_CONTEXT, + WorkQueueItem.List ); + + SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT | IRP_CONTEXT_FLAG_IN_FSP); + + Irp = IrpContext->OriginatingIrp; + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + continue; + + } else { + + break; + } + } + + return; +} + + +// +// Internal support routine, spinlock wrapper. +// + +PVOID +FatRemoveOverflowEntry ( + IN PVOLUME_DEVICE_OBJECT VolDo + ) +{ + PVOID Entry; + KIRQL SavedIrql; + + 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 ); + + return Entry; +} + + diff --git a/filesys/fastfat/lfn.h b/filesys/fastfat/lfn.h new file mode 100644 index 000000000..e9ee5c622 --- /dev/null +++ b/filesys/fastfat/lfn.h @@ -0,0 +1,56 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + Lfn.h + +Abstract: + + This module defines the on-disk structure of long file names on FAT. + + +--*/ + +#ifndef _LFN_ +#define _LFN_ + +// +// This strucure defines the on disk format on long file name dirents. +// + +typedef struct _PACKED_LFN_DIRENT { + UCHAR Ordinal; // offset = 0 + UCHAR Name1[10]; // offset = 1 (Really 5 chars, but not WCHAR aligned) + UCHAR Attributes; // offset = 11 + UCHAR Type; // offset = 12 + UCHAR Checksum; // offset = 13 + WCHAR Name2[6]; // offset = 14 + USHORT MustBeZero; // offset = 26 + WCHAR Name3[2]; // offset = 28 +} PACKED_LFN_DIRENT; // sizeof = 32 +typedef PACKED_LFN_DIRENT *PPACKED_LFN_DIRENT; + +#define FAT_LAST_LONG_ENTRY 0x40 // Ordinal field +#define FAT_LONG_NAME_COMP 0x0 // Type field + +// +// A packed lfn dirent is already quadword aligned so simply declare a +// lfn dirent as a packed lfn dirent. +// + +typedef PACKED_LFN_DIRENT LFN_DIRENT; +typedef LFN_DIRENT *PLFN_DIRENT; + +// +// This is the largest size buffer we would ever need to read an Lfn +// + +#define MAX_LFN_CHARACTERS 260 +#define MAX_LFN_DIRENTS 20 + +#define FAT_LFN_DIRENTS_NEEDED(NAME) (((NAME)->Length/sizeof(WCHAR) + 12)/13) + +#endif // _LFN_ + diff --git a/filesys/fastfat/lockctrl.c b/filesys/fastfat/lockctrl.c new file mode 100644 index 000000000..58aacdcaf --- /dev/null +++ b/filesys/fastfat/lockctrl.c @@ -0,0 +1,763 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + LockCtrl.c + +Abstract: + + This module implements the Lock Control routines for Fat called + by the dispatch driver. + + +--*/ + +#include "FatProcs.h" + +// +// The local debug trace level +// + +#define Dbg (DEBUG_TRACE_LOCKCTRL) + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, FatCommonLockControl) +#pragma alloc_text(PAGE, FatFastLock) +#pragma alloc_text(PAGE, FatFastUnlockAll) +#pragma alloc_text(PAGE, FatFastUnlockAllByKey) +#pragma alloc_text(PAGE, FatFastUnlockSingle) +#pragma alloc_text(PAGE, FatFsdLockControl) +#endif + + +_Function_class_(IRP_MJ_LOCK_CONTROL) +_Function_class_(DRIVER_DISPATCH) +NTSTATUS +FatFsdLockControl ( + _In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + _Inout_ PIRP Irp + ) + +/*++ + +Routine Description: + + This routine implements the FSD part of Lock control operations + +Arguments: + + VolumeDeviceObject - Supplies the volume device object where the + file exists + + Irp - Supplies the Irp being processed + +Return Value: + + NTSTATUS - The FSD status for the IRP + +--*/ + +{ + NTSTATUS Status; + PIRP_CONTEXT IrpContext = NULL; + + BOOLEAN TopLevel; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatFsdLockControl\n", 0); + + // + // Call the common Lock Control routine, with blocking allowed if + // synchronous + // + + FsRtlEnterFileSystem(); + + TopLevel = FatIsIrpTopLevel( Irp ); + + try { + + IrpContext = FatCreateIrpContext( Irp, CanFsdWait( Irp ) ); + + Status = FatCommonLockControl( IrpContext, Irp ); + + } except(FatExceptionFilter( IrpContext, GetExceptionInformation() )) { + + // + // We had some trouble trying to perform the requested + // operation, so we'll abort the I/O request with + // the error status that we get back from the + // execption code + // + + Status = FatProcessException( IrpContext, Irp, GetExceptionCode() ); + } + + if (TopLevel) { IoSetTopLevelIrp( NULL ); } + + FsRtlExitFileSystem(); + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "FatFsdLockControl -> %08lx\n", Status); + + UNREFERENCED_PARAMETER( VolumeDeviceObject ); + + return Status; +} + + +_Function_class_(FAST_IO_LOCK) +BOOLEAN +FatFastLock ( + IN PFILE_OBJECT FileObject, + IN PLARGE_INTEGER FileOffset, + IN PLARGE_INTEGER Length, + PEPROCESS ProcessId, + ULONG Key, + BOOLEAN FailImmediately, + 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; + PVCB Vcb; + PFCB Fcb; + PCCB Ccb; + + PAGED_CODE(); + UNREFERENCED_PARAMETER( DeviceObject ); + + DebugTrace(+1, Dbg, "FatFastLock\n", 0); + + // + // Decode the type of file object we're being asked to process and make + // sure it is only a user file open. + // + + if (FatDecodeFileObject( FileObject, &Vcb, &Fcb, &Ccb ) != UserFileOpen) { + + IoStatus->Status = STATUS_INVALID_PARAMETER; + IoStatus->Information = 0; + + DebugTrace(-1, Dbg, "FatFastLock -> TRUE (STATUS_INVALID_PARAMETER)\n", 0); + return TRUE; + } + + // + // Acquire shared access to the Fcb + // + + FsRtlEnterFileSystem(); + ExAcquireResourceSharedLite( Fcb->Header.Resource, TRUE ); + + try { + + // + // We check whether we can proceed + // based on the state of the file oplocks. + // + + if (!FsRtlOplockIsFastIoPossible( FatGetFcbOplock(Fcb) )) { + + try_return( Results = FALSE ); + } + + // + // Now call the FsRtl routine to do the actual processing of the + // Lock request + // + +#pragma prefast( suppress:28159, "prefast indicates this API is obsolete but it is ok for fastfat to continue using it" ) + Results = FsRtlFastLock( &Fcb->Specific.Fcb.FileLock, + FileObject, + FileOffset, + Length, + ProcessId, + Key, + FailImmediately, + ExclusiveLock, + IoStatus, + NULL, + FALSE ); + + if (Results) { + + // + // Set the flag indicating if Fast I/O is possible + // + + Fcb->Header.IsFastIoPossible = FatIsFastIoPossible( Fcb ); + } + + try_exit: NOTHING; + } finally { + + DebugUnwind( FatFastLock ); + + // + // Release the Fcb, and return to our caller + // + + ExReleaseResourceLite( Fcb->Header.Resource ); + FsRtlExitFileSystem(); + + DebugTrace(-1, Dbg, "FatFastLock -> %08lx\n", Results); + } + + return Results; +} + + +_Function_class_(FAST_IO_UNLOCK_SINGLE) +BOOLEAN +FatFastUnlockSingle ( + IN PFILE_OBJECT FileObject, + IN PLARGE_INTEGER FileOffset, + IN PLARGE_INTEGER Length, + PEPROCESS ProcessId, + 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; + PVCB Vcb; + PFCB Fcb; + PCCB Ccb; + + PAGED_CODE(); + UNREFERENCED_PARAMETER( DeviceObject ); + + DebugTrace(+1, Dbg, "FatFastUnlockSingle\n", 0); + + IoStatus->Information = 0; + + // + // Decode the type of file object we're being asked to process and make sure + // it is only a user file open + // + + if (FatDecodeFileObject( FileObject, &Vcb, &Fcb, &Ccb ) != UserFileOpen) { + + IoStatus->Status = STATUS_INVALID_PARAMETER; + + DebugTrace(-1, Dbg, "FatFastUnlockSingle -> TRUE (STATUS_INVALID_PARAMETER)\n", 0); + return TRUE; + } + + // + // Acquire exclusive access to the Fcb this operation can always wait + // + + FsRtlEnterFileSystem(); + + try { + + // + // We check whether we can proceed based on the state of the file oplocks. + // + + if (!FsRtlOplockIsFastIoPossible( FatGetFcbOplock(Fcb) )) { + + try_return( Results = FALSE ); + } + + // + // 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->Specific.Fcb.FileLock, + FileObject, + FileOffset, + Length, + ProcessId, + Key, + NULL, + FALSE ); + + // + // Set the flag indicating if Fast I/O is possible + // + + Fcb->Header.IsFastIoPossible = FatIsFastIoPossible( Fcb ); + + try_exit: NOTHING; + } finally { + + DebugUnwind( FatFastUnlockSingle ); + + // + // Release the Fcb, and return to our caller + // + + FsRtlExitFileSystem(); + + DebugTrace(-1, Dbg, "FatFastUnlockSingle -> %08lx\n", Results); + } + + return Results; +} + + +_Function_class_(FAST_IO_UNLOCK_ALL) +BOOLEAN +FatFastUnlockAll ( + IN PFILE_OBJECT FileObject, + 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; + PVCB Vcb; + PFCB Fcb; + PCCB Ccb; + + PAGED_CODE(); + UNREFERENCED_PARAMETER( DeviceObject ); + + DebugTrace(+1, Dbg, "FatFastUnlockAll\n", 0); + + IoStatus->Information = 0; + + // + // Decode the type of file object we're being asked to process and make sure + // it is only a user file open. + // + + if (FatDecodeFileObject( FileObject, &Vcb, &Fcb, &Ccb ) != UserFileOpen) { + + IoStatus->Status = STATUS_INVALID_PARAMETER; + + DebugTrace(-1, Dbg, "FatFastUnlockAll -> TRUE (STATUS_INVALID_PARAMETER)\n", 0); + return TRUE; + } + + // + // Acquire exclusive access to the Fcb this operation can always wait + // + + FsRtlEnterFileSystem(); + + (VOID) ExAcquireResourceSharedLite( Fcb->Header.Resource, TRUE ); + + try { + + // + // We check whether we can proceed based on the state of the file oplocks. + // + + if (!FsRtlOplockIsFastIoPossible( FatGetFcbOplock(Fcb) )) { + + try_return( Results = FALSE ); + } + + // + // 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->Specific.Fcb.FileLock, + FileObject, + ProcessId, + NULL ); + + // + // Set the flag indicating if Fast I/O is possible + // + + Fcb->Header.IsFastIoPossible = FatIsFastIoPossible( Fcb ); + + try_exit: NOTHING; + } finally { + + DebugUnwind( FatFastUnlockAll ); + + // + // Release the Fcb, and return to our caller + // + + ExReleaseResourceLite( (Fcb)->Header.Resource ); + + FsRtlExitFileSystem(); + + DebugTrace(-1, Dbg, "FatFastUnlockAll -> %08lx\n", Results); + } + + return Results; +} + + +_Function_class_(FAST_IO_UNLOCK_ALL_BY_KEY) +BOOLEAN +FatFastUnlockAllByKey ( + IN PFILE_OBJECT FileObject, + PVOID ProcessId, + 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; + PVCB Vcb; + PFCB Fcb; + PCCB Ccb; + + PAGED_CODE(); + UNREFERENCED_PARAMETER( DeviceObject ); + + DebugTrace(+1, Dbg, "FatFastUnlockAllByKey\n", 0); + + IoStatus->Information = 0; + + // + // Decode the type of file object we're being asked to process and make sure + // it is only a user file open. + // + + if (FatDecodeFileObject( FileObject, &Vcb, &Fcb, &Ccb ) != UserFileOpen) { + + IoStatus->Status = STATUS_INVALID_PARAMETER; + + DebugTrace(-1, Dbg, "FatFastUnlockAll -> TRUE (STATUS_INVALID_PARAMETER)\n", 0); + return TRUE; + } + + // + // Acquire exclusive access to the Fcb this operation can always wait + // + + FsRtlEnterFileSystem(); + + (VOID) ExAcquireResourceSharedLite( Fcb->Header.Resource, TRUE ); + + try { + + // + // We check whether we can proceed based on the state of the file oplocks. + // + + if (!FsRtlOplockIsFastIoPossible( FatGetFcbOplock(Fcb) )) { + + try_return( Results = FALSE ); + } + + // + // 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->Specific.Fcb.FileLock, + FileObject, + ProcessId, + Key, + NULL ); + + // + // Set the flag indicating if Fast I/O is possible + // + + Fcb->Header.IsFastIoPossible = FatIsFastIoPossible( Fcb ); + + try_exit: NOTHING; + } finally { + + DebugUnwind( FatFastUnlockAllByKey ); + + // + // Release the Fcb, and return to our caller + // + + ExReleaseResourceLite( (Fcb)->Header.Resource ); + + FsRtlExitFileSystem(); + + DebugTrace(-1, Dbg, "FatFastUnlockAllByKey -> %08lx\n", Results); + } + + return Results; +} + + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatCommonLockControl ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the common routine for doing Lock 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 = STATUS_SUCCESS; + PIO_STACK_LOCATION IrpSp; + + TYPE_OF_OPEN TypeOfOpen; + + PVCB Vcb; + PFCB Fcb; + PCCB Ccb; + + BOOLEAN OplockPostIrp = FALSE; + + PAGED_CODE(); + + // + // Get a pointer to the current Irp stack location + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "FatCommonLockControl\n", 0); + DebugTrace( 0, Dbg, "Irp = %p\n", Irp); + DebugTrace( 0, Dbg, "MinorFunction = %08lx\n", IrpSp->MinorFunction); + + // + // Decode the type of file object we're being asked to process + // + + TypeOfOpen = FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb ); + + // + // If the file is not a user file open then we reject the request + // as an invalid parameter + // + + if (TypeOfOpen != UserFileOpen) { + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + + DebugTrace(-1, Dbg, "FatCommonLockControl -> STATUS_INVALID_PARAMETER\n", 0); + return STATUS_INVALID_PARAMETER; + } + + // + // Acquire exclusive access to the Fcb and enqueue the Irp if we didn't + // get access + // + + if (!FatAcquireSharedFcb( IrpContext, Fcb )) { + + Status = FatFsdPostRequest( IrpContext, Irp ); + + DebugTrace(-1, Dbg, "FatCommonLockControl -> %08lx\n", Status); + return Status; + } + + try { + + // + // We check whether we can proceed + // based on the state of the file oplocks. + // + +#if (NTDDI_VERSION >= NTDDI_WIN8) + + if (((IRP_MN_LOCK == IrpSp->MinorFunction) && + ((ULONGLONG)IrpSp->Parameters.LockControl.ByteOffset.QuadPart < + (ULONGLONG)Fcb->Header.AllocationSize.QuadPart)) || + ((IRP_MN_LOCK != IrpSp->MinorFunction) && + FsRtlAreThereWaitingFileLocks( &Fcb->Specific.Fcb.FileLock ))) { + + // + // Check whether we can proceed based on the state of file oplocks if doing + // an operation that interferes with oplocks. Those operations are: + // + // 1. Lock a range within the file's AllocationSize. + // 2. Unlock a range when there are waiting locks on the file. This one + // is not guaranteed to interfere with oplocks, but it could, as + // unlocking this range might cause a waiting lock to be granted + // within AllocationSize! + // + +#endif + Status = FsRtlCheckOplock( FatGetFcbOplock(Fcb), + Irp, + IrpContext, + FatOplockComplete, + NULL ); + +#if (NTDDI_VERSION >= NTDDI_WIN8) + } +#endif + + if (Status != STATUS_SUCCESS) { + + OplockPostIrp = TRUE; + try_return( NOTHING ); + } + + // + // Now call the FsRtl routine to do the actual processing of the + // Lock request + // + + Status = FsRtlProcessFileLock( &Fcb->Specific.Fcb.FileLock, Irp, NULL ); + + // + // Set the flag indicating if Fast I/O is possible + // + + Fcb->Header.IsFastIoPossible = FatIsFastIoPossible( Fcb ); + + try_exit: NOTHING; + } finally { + + DebugUnwind( FatCommonLockControl ); + + // + // Only if this is not an abnormal termination do we delete the + // irp context + // + + if (!AbnormalTermination() && !OplockPostIrp) { + + FatCompleteRequest( IrpContext, FatNull, 0 ); + } + + // + // Release the Fcb, and return to our caller + // + + FatReleaseFcb( IrpContext, Fcb ); + + DebugTrace(-1, Dbg, "FatCommonLockControl -> %08lx\n", Status); + } + + return Status; +} + diff --git a/filesys/fastfat/namesup.c b/filesys/fastfat/namesup.c new file mode 100644 index 000000000..662e4b4d0 --- /dev/null +++ b/filesys/fastfat/namesup.c @@ -0,0 +1,1035 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + NameSup.c + +Abstract: + + This module implements the Fat Name support routines + + +--*/ + +#include "FatProcs.h" + +#define Dbg (DEBUG_TRACE_NAMESUP) + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, Fat8dot3ToString) +#pragma alloc_text(PAGE, FatIsNameInExpression) +#pragma alloc_text(PAGE, FatStringTo8dot3) +#pragma alloc_text(PAGE, FatSetFullFileNameInFcb) +#pragma alloc_text(PAGE, FatGetUnicodeNameFromFcb) +#pragma alloc_text(PAGE, FatUnicodeToUpcaseOem) +#pragma alloc_text(PAGE, FatSelectNames) +#pragma alloc_text(PAGE, FatEvaluateNameCase) +#pragma alloc_text(PAGE, FatSpaceInName) +#endif + + +BOOLEAN +FatIsNameInExpression ( + IN PIRP_CONTEXT IrpContext, + IN OEM_STRING Expression, + IN OEM_STRING Name + ) + +/*++ + +Routine Description: + + This routine compare a name and an expression and tells the caller if + the name is equal to or not equal to the expression. The input name + cannot contain wildcards, while the expression may contain wildcards. + +Arguments: + + Expression - Supplies the input expression to check against + The caller must have already upcased the Expression. + + Name - Supplies the input name to check for. The caller must have + already upcased the name. + +Return Value: + + BOOLEAN - TRUE if Name is an element in the set of strings denoted + by the input Expression and FALSE otherwise. + +--*/ + +{ + PAGED_CODE(); + + // + // Call the appropriate FsRtl routine do to the real work + // + + return FsRtlIsDbcsInExpression( &Expression, + &Name ); + + UNREFERENCED_PARAMETER( IrpContext ); +} + + +VOID +FatStringTo8dot3 ( + _In_ PIRP_CONTEXT IrpContext, + _In_ OEM_STRING InputString, + _Out_writes_bytes_(11) PFAT8DOT3 Output8dot3 + ) + +/*++ + +Routine Description: + + Convert a string into fat 8.3 format. The string must not contain + any wildcards. + +Arguments: + + InputString - Supplies the input string to convert + + Output8dot3 - Receives the converted string, the memory must be supplied + by the caller. + +Return Value: + + None. + +--*/ + +{ + ULONG i; + ULONG j; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatStringTo8dot3\n", 0); + DebugTrace( 0, Dbg, "InputString = %Z\n", &InputString); + + NT_ASSERT( InputString.Length <= 12 ); + + // + // Make the output name all blanks + // + + RtlFillMemory( Output8dot3, 11, UCHAR_SP ); + + // + // Copy over the first part of the file name. Stop when we get to + // the end of the input string or a dot. + // + + for (i = 0; + (i < (ULONG)InputString.Length) && (InputString.Buffer[i] != '.') && (i < 11); + i += 1) { + + (*Output8dot3)[i] = InputString.Buffer[i]; + } + + // + // Check if we need to process an extension + // + + if (i < (ULONG)InputString.Length) { + + // + // Make sure we have a dot and then skip over it. + // + + NT_ASSERT( (InputString.Length - i) <= 4 ); + NT_ASSERT( InputString.Buffer[i] == '.' ); + + i += 1; + + // + // Copy over the extension. Stop when we get to the + // end of the input string. + // + + for (j = 8; (i < (ULONG)InputString.Length); j += 1, i += 1) { + + (*Output8dot3)[j] = InputString.Buffer[i]; + } + } + + // + // Before we return check if we should translate the first character + // from 0xe5 to 0x5. + // + + if ((*Output8dot3)[0] == 0xe5) { + + (*Output8dot3)[0] = FAT_DIRENT_REALLY_0E5; + } + + DebugTrace(-1, Dbg, "FatStringTo8dot3 -> (VOID)\n", 0); + + UNREFERENCED_PARAMETER( IrpContext ); + + return; +} + + +VOID +Fat8dot3ToString ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PDIRENT Dirent, + _In_ BOOLEAN RestoreCase, + _Out_ POEM_STRING OutputString + ) + +/*++ + +Routine Description: + + Convert fat 8.3 format into a string. The 8.3 name must be well formed. + +Arguments: + + Dirent - Supplies the input 8.3 name to convert + + RestoreCase - If TRUE, then the magic reserved bits are used to restore + the original case. + + OutputString - Receives the converted name, the memory must be supplied + by the caller. + +Return Value: + + None + +--*/ + +{ + ULONG StringIndex; + ULONG BaseLength, ExtensionLength; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "Fat8dot3ToString\n", 0); + + // + // First, find the length of the base component. + // + + for (BaseLength = 8; BaseLength > 0; BaseLength -= 1) { + + if (Dirent->FileName[BaseLength - 1] != UCHAR_SP) { + + break; + } + } + + // + // Now find the length of the extension. + // + + for (ExtensionLength = 3; ExtensionLength > 0; ExtensionLength -= 1) { + + if (Dirent->FileName[8 + ExtensionLength - 1] != UCHAR_SP) { + + break; + } + } + + // + // If there was a base part, copy it and check the case. Don't forget + // if the first character needs to be changed from 0x05 to 0xe5. + // + + if (BaseLength != 0) { + + RtlCopyMemory( OutputString->Buffer, Dirent->FileName, BaseLength ); + + if (OutputString->Buffer[0] == FAT_DIRENT_REALLY_0E5) { + + OutputString->Buffer[0] = 0xe5; + } + + // + // Now if we are to restore case, look for A-Z + // + + if (FatData.ChicagoMode && + RestoreCase && + FlagOn(Dirent->NtByte, FAT_DIRENT_NT_BYTE_8_LOWER_CASE)) { + + for (StringIndex = 0; StringIndex < BaseLength; StringIndex += 1) { + + // + // Depending on whether the media was built in a system that was + // running with "code page invariance" (see FatEvaluateNameCase), + // there could be double-byte OEM characters lying in wait here. + // Gotta skip them. + // + + if (FsRtlIsLeadDbcsCharacter(OutputString->Buffer[StringIndex])) { + + StringIndex += 1; + continue; + } + + if ((OutputString->Buffer[StringIndex] >= 'A') && + (OutputString->Buffer[StringIndex] <= 'Z')) { + + OutputString->Buffer[StringIndex] += 'a' - 'A'; + } + } + } + } + + // + // If there was an extension, copy that over. Else we now know the + // size of the string. + // + + if (ExtensionLength != 0) { + + PUCHAR o, d; + + // + // Now add the dot + // + + OutputString->Buffer[BaseLength++] = '.'; + + // + // Copy over the extension into the output buffer. + // + + o = (PUCHAR)&OutputString->Buffer[BaseLength]; + d = &Dirent->FileName[8]; + + switch (ExtensionLength) { + case 3: + *o++ = *d++; + case 2: + *o++ = *d++; + case 1: + *o++ = *d++; + } + + // + // Set the output string length + // + + OutputString->Length = (USHORT)(BaseLength + ExtensionLength); + + // + // Now if we are to restore case, look for A-Z + // + + if (FatData.ChicagoMode && + RestoreCase && + FlagOn(Dirent->NtByte, FAT_DIRENT_NT_BYTE_3_LOWER_CASE)) { + + for (StringIndex = BaseLength; + StringIndex < OutputString->Length; + StringIndex++ ) { + + // + // Depending on whether the media was built in a system that was + // running with "code page invariance" (see FatEvaluateNameCase), + // there could be double-byte OEM characters lying in wait here. + // Gotta skip them. + // + + if (FsRtlIsLeadDbcsCharacter(OutputString->Buffer[StringIndex])) { + + StringIndex += 1; + continue; + } + + if ((OutputString->Buffer[StringIndex] >= 'A') && + (OutputString->Buffer[StringIndex] <= 'Z')) { + + OutputString->Buffer[StringIndex] += 'a' - 'A'; + } + } + } + + } else { + + // + // Set the output string length + // + + OutputString->Length = (USHORT)BaseLength; + } + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "Fat8dot3ToString, OutputString = \"%Z\" -> VOID\n", OutputString); + + UNREFERENCED_PARAMETER( IrpContext ); + + return; +} + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatGetUnicodeNameFromFcb ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN OUT PUNICODE_STRING Lfn + ) + +/*++ + +Routine Description: + + This routine will return the unicode name for a given Fcb. If the + file has an LFN, it will return this. Otherwise it will return + the UNICODE conversion of the Oem name, properly cased. + +Arguments: + + Fcb - Supplies the Fcb to query. + + Lfn - Supplies a string that already has enough storage for the + full unicode name. + +Return Value: + + None + +--*/ + +{ + PDIRENT Dirent; + PBCB DirentBcb = NULL; + ULONG DirentByteOffset; + + CCB LocalCcb; + + PAGED_CODE(); + + NT_ASSERT((MAX_LFN_CHARACTERS * sizeof( WCHAR)) == Lfn->MaximumLength); + + // + // We'll start by locating the dirent for the name. + // + + FatStringTo8dot3( IrpContext, + Fcb->ShortName.Name.Oem, + &LocalCcb.OemQueryTemplate.Constant ); + + LocalCcb.Flags = 0; + LocalCcb.UnicodeQueryTemplate.Length = 0; + LocalCcb.ContainsWildCards = FALSE; + + FatLocateDirent( IrpContext, + Fcb->ParentDcb, + &LocalCcb, + Fcb->LfnOffsetWithinDirectory, + NULL, + &Dirent, + &DirentBcb, + (PVBO)&DirentByteOffset, + NULL, + Lfn, + NULL ); + try { + + // + // If we didn't find the Dirent, something is terribly wrong. + // + + if ((DirentBcb == NULL) || + (DirentByteOffset != Fcb->DirentOffsetWithinDirectory)) { + + FatRaiseStatus( IrpContext, STATUS_FILE_INVALID ); + } + + // + // Check for the easy case. + // + + if (Lfn->Length == 0) { + + NTSTATUS Status; + OEM_STRING ShortName; + UCHAR ShortNameBuffer[12]; + + // + // If we thought that there was an LFN here and didn't find one, + // we're as dead. This shouldn't happen in normal operation, but + // if someone scrambles a directory by hand ... + // + + NT_ASSERT( Fcb->LfnOffsetWithinDirectory == Fcb->DirentOffsetWithinDirectory ); + + if (Fcb->LfnOffsetWithinDirectory != Fcb->DirentOffsetWithinDirectory) { + + FatRaiseStatus( IrpContext, STATUS_FILE_INVALID ); + } + + // + // There is no LFN, so manufacture a UNICODE name. + // + + ShortName.Length = 0; + ShortName.MaximumLength = 12; + ShortName.Buffer = (PCHAR)ShortNameBuffer; + + Fat8dot3ToString( IrpContext, Dirent, TRUE, &ShortName ); + + // + // OK, now convert this string to UNICODE + // + +#pragma prefast( suppress:28931, "needed for debug build" ) + Status = RtlOemStringToCountedUnicodeString( Lfn, + &ShortName, + FALSE ); + + NT_ASSERT( Status == STATUS_SUCCESS ); + } + + } finally { + + FatUnpinBcb( IrpContext, DirentBcb ); + } +} + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatSetFullFileNameInFcb ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb + ) + +/*++ + +Routine Description: + + If the FullFileName field in the Fcb has not yet been filled in, we + proceed to do so. + +Arguments: + + Fcb - Supplies the file. + +Return Value: + + None + +--*/ + +{ + PAGED_CODE(); + + if (Fcb->FullFileName.Buffer == NULL) { + + UNICODE_STRING Lfn; + PFCB TmpFcb = Fcb; + PFCB StopFcb; + PWCHAR TmpBuffer; + ULONG PathLength = 0; + + // + // We will assume we do this infrequently enough, that it's OK to + // to a pool allocation here. + // + + Lfn.Length = 0; + Lfn.MaximumLength = MAX_LFN_CHARACTERS * sizeof(WCHAR); + Lfn.Buffer = FsRtlAllocatePoolWithTag( PagedPool, + MAX_LFN_CHARACTERS * sizeof(WCHAR), + TAG_FILENAME_BUFFER ); + + try { + + // + // First determine how big the name will be. If we find an + // ancestor with a FullFileName, our work is easier. + // + + while (TmpFcb != Fcb->Vcb->RootDcb) { + + if ((TmpFcb != Fcb) && (TmpFcb->FullFileName.Buffer != NULL)) { + + PathLength += TmpFcb->FullFileName.Length; + + Fcb->FullFileName.Buffer = FsRtlAllocatePoolWithTag( PagedPool, + PathLength, + TAG_FILENAME_BUFFER ); + + RtlCopyMemory( Fcb->FullFileName.Buffer, + TmpFcb->FullFileName.Buffer, + TmpFcb->FullFileName.Length ); + + break; + } + + PathLength += TmpFcb->FinalNameLength + sizeof(WCHAR); + + TmpFcb = TmpFcb->ParentDcb; + } + + // + // If FullFileName.Buffer is still NULL, allocate it. + // + + if (Fcb->FullFileName.Buffer == NULL) { + + Fcb->FullFileName.Buffer = FsRtlAllocatePoolWithTag( PagedPool, + PathLength, + TAG_FILENAME_BUFFER ); + } + + StopFcb = TmpFcb; + + TmpFcb = Fcb; + TmpBuffer = Fcb->FullFileName.Buffer + PathLength / sizeof(WCHAR); + + Fcb->FullFileName.Length = + Fcb->FullFileName.MaximumLength = (USHORT)PathLength; + + while (TmpFcb != StopFcb) { + + FatGetUnicodeNameFromFcb( IrpContext, + TmpFcb, + &Lfn ); + + TmpBuffer -= Lfn.Length / sizeof(WCHAR); + + RtlCopyMemory( TmpBuffer, Lfn.Buffer, Lfn.Length ); + + TmpBuffer -= 1; + + *TmpBuffer = L'\\'; + + TmpFcb = TmpFcb->ParentDcb; + } + + } finally { + + if (AbnormalTermination()) { + + if (Fcb->FullFileName.Buffer) { + + ExFreePool( Fcb->FullFileName.Buffer ); + Fcb->FullFileName.Buffer = NULL; + } + } + + ExFreePool( Lfn.Buffer ); + } + } +} + +VOID +FatUnicodeToUpcaseOem ( + IN PIRP_CONTEXT IrpContext, + IN POEM_STRING OemString, + IN PUNICODE_STRING UnicodeString + ) + +/*++ + +Routine Description: + + This routine is our standard routine for trying to use stack space + if possible when calling RtlUpcaseUnicodeStringToCountedOemString(). + + If an unmappable character is encountered, we set the destination + length to 0. + +Arguments: + + OemString - Specifies the destination string. Space is already assumed to + be allocated. If there is not enough, then we allocate enough + space. + + UnicodeString - Specifies the source string. + +Return Value: + + None. + +--*/ + +{ + NTSTATUS Status; + + PAGED_CODE(); + + Status = RtlUpcaseUnicodeStringToCountedOemString( OemString, + UnicodeString, + FALSE ); + + if (Status == STATUS_BUFFER_OVERFLOW) { + + OemString->Buffer = NULL; + OemString->Length = 0; + OemString->MaximumLength = 0; + + Status = RtlUpcaseUnicodeStringToCountedOemString( OemString, + UnicodeString, + TRUE ); + } + + if (!NT_SUCCESS(Status)) { + + if (Status == STATUS_UNMAPPABLE_CHARACTER) { + + OemString->Length = 0; + + } else { + + FatNormalizeAndRaiseStatus( IrpContext, Status ); + } + } + + return; +} + + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatSelectNames ( + IN PIRP_CONTEXT IrpContext, + IN PDCB Parent, + IN POEM_STRING OemName, + IN PUNICODE_STRING UnicodeName, + IN OUT POEM_STRING ShortName, + IN PUNICODE_STRING SuggestedShortName OPTIONAL, + IN OUT BOOLEAN *AllLowerComponent, + IN OUT BOOLEAN *AllLowerExtension, + IN OUT BOOLEAN *CreateLfn + ) + +/*++ + +Routine Description: + + This routine takes the original UNICODE string that the user specified, + and the upcased Oem equivalent. This routine then decides if the OemName + is acceptable for dirent, or whether a short name has to be manufactured. + + Two values are returned to the caller. One tells the caller if the name + happens to be all lower case < 0x80. In this special case we don't + have to create an Lfn. Also we tell the caller if it must create an LFN. + +Arguments: + + OemName - Supplies the proposed short Oem name. + + ShortName - If this OemName is OK for storeage in a dirent it is copied to + this string, otherwise this string is filled with a name that is OK. + If OemName and ShortName are the same string, no copy is done. + + UnicodeName - Provides the original final name. + + SuggestedShortName - a first-try short name to try before auto-generation + is used + + AllLowerComponent - Returns whether this component was all lower case. + + AllLowerExtension - Returns wheather the extension was all lower case. + + CreateLfn - Tells the caller if we must create an LFN for the UnicodeName or + SuggestedLongName + +Return Value: + + None. + +--*/ + +{ + BOOLEAN GenerateShortName; + + PAGED_CODE(); + + // + // First see if we must generate a short name. + // + + if ((OemName->Length == 0) || + !FatIsNameShortOemValid( IrpContext, *OemName, FALSE, FALSE, FALSE ) || + FatSpaceInName( IrpContext, UnicodeName )) { + + WCHAR ShortNameBuffer[12]; + UNICODE_STRING ShortUnicodeName; + GENERATE_NAME_CONTEXT Context; + BOOLEAN TrySuggestedShortName; + + PDIRENT Dirent; + PBCB Bcb = NULL; + ULONG ByteOffset; + NTSTATUS Status; + + GenerateShortName = TRUE; + + TrySuggestedShortName = (SuggestedShortName != NULL); + + // + // Now generate a short name. + // + + ShortUnicodeName.Length = 0; + ShortUnicodeName.MaximumLength = 12 * sizeof(WCHAR); + ShortUnicodeName.Buffer = ShortNameBuffer; + + RtlZeroMemory( &Context, sizeof( GENERATE_NAME_CONTEXT ) ); + + try { + + while ( TRUE ) { + + FatUnpinBcb( IrpContext, Bcb ); + + if (TrySuggestedShortName) { + + // + // Try our caller's candidate first. Note that this must have + // been uppercased previously. + // + + ShortUnicodeName.Length = SuggestedShortName->Length; + ShortUnicodeName.MaximumLength = SuggestedShortName->MaximumLength; + ShortUnicodeName.Buffer = SuggestedShortName->Buffer; + + TrySuggestedShortName = FALSE; + + } else { + + RtlGenerate8dot3Name( UnicodeName, TRUE, &Context, &ShortUnicodeName ); + } + + // + // We have a candidate, make sure it doesn't exist. + // + +#pragma prefast( suppress:28931, "needed for debug build" ) + Status = RtlUnicodeStringToCountedOemString( ShortName, + &ShortUnicodeName, + FALSE ); + + NT_ASSERT( Status == STATUS_SUCCESS ); + + FatLocateSimpleOemDirent( IrpContext, + Parent, + ShortName, + &Dirent, + &Bcb, + (PVBO)&ByteOffset ); + + if (Bcb == NULL) { + + leave; + + } + } + + } finally { + + FatUnpinBcb( IrpContext, Bcb ); + } + + } else { + + // + // Only do this copy if the two string are indeed different. + // + + if (ShortName != OemName) { + ShortName->Length = OemName->Length; + + // + // If FsRtlIsFatDbcsLegal() on OemName fails, we will not + // be in this code path, so we infer that ShortName's + // buffer is big enough to hold the full FAT file name in + // OemName. + // + + _Analysis_assume_(ShortName->MaximumLength <= OemName->Length); + + RtlCopyMemory( ShortName->Buffer, OemName->Buffer, OemName->Length ); + } + + GenerateShortName = FALSE; + } + + // + // Now see if the caller will have to use unicode string as an LFN + // + + if (GenerateShortName) { + + *CreateLfn = TRUE; + *AllLowerComponent = FALSE; + *AllLowerExtension = FALSE; + + } else { + + FatEvaluateNameCase( IrpContext, + UnicodeName, + AllLowerComponent, + AllLowerExtension, + CreateLfn ); + } + + return; +} + + +VOID +FatEvaluateNameCase ( + IN PIRP_CONTEXT IrpContext, + IN PUNICODE_STRING UnicodeName, + IN OUT BOOLEAN *AllLowerComponent, + IN OUT BOOLEAN *AllLowerExtension, + IN OUT BOOLEAN *CreateLfn + ) + +/*++ + +Routine Description: + + This routine takes a UNICODE string and sees if it is eligible for + the special case optimization. + +Arguments: + + UnicodeName - Provides the original final name. + + AllLowerComponent - Returns whether this compoent was all lower case. + + AllLowerExtension - Returns wheather the extension was all lower case. + + CreateLfn - Tells the call if we must create an LFN for the UnicodeName. + +Return Value: + + None. + +--*/ + +{ + ULONG i; + UCHAR Uppers = 0; + UCHAR Lowers = 0; + + BOOLEAN ExtensionPresent = FALSE; + + PAGED_CODE(); + UNREFERENCED_PARAMETER( IrpContext ); + + *CreateLfn = FALSE; + + for (i = 0; i < UnicodeName->Length / sizeof(WCHAR); i++) { + + WCHAR c; + + c = UnicodeName->Buffer[i]; + + if ((c >= 'A') && (c <= 'Z')) { + + Uppers += 1; + + } else if ((c >= 'a') && (c <= 'z')) { + + Lowers += 1; + + } else if ((c >= 0x0080) && FatData.CodePageInvariant) { + + break; + } + + // + // If we come to a period, figure out if the extension was + // all one case. + // + + if (c == L'.') { + + *CreateLfn = (Lowers != 0) && (Uppers != 0); + + *AllLowerComponent = !(*CreateLfn) && (Lowers != 0); + + ExtensionPresent = TRUE; + + // + // Now reset the uppers and lowers count. + // + + Uppers = Lowers = 0; + } + } + + // + // Now check again for creating an LFN. + // + + *CreateLfn = (*CreateLfn || + (i != UnicodeName->Length / sizeof(WCHAR)) || + ((Lowers != 0) && (Uppers != 0))); + + // + // Now we know the final state of CreateLfn, update the two + // "AllLower" booleans. + // + + if (ExtensionPresent) { + + *AllLowerComponent = !(*CreateLfn) && *AllLowerComponent; + *AllLowerExtension = !(*CreateLfn) && (Lowers != 0); + + } else { + + *AllLowerComponent = !(*CreateLfn) && (Lowers != 0); + *AllLowerExtension = FALSE; + } + + return; +} + + +BOOLEAN +FatSpaceInName ( + IN PIRP_CONTEXT IrpContext, + IN PUNICODE_STRING UnicodeName + ) + +/*++ + +Routine Description: + + This routine takes a UNICODE string and sees if it contains any spaces. + +Arguments: + + UnicodeName - Provides the final name. + +Return Value: + + BOOLEAN - TRUE if it does, FALSE if it doesn't. + +--*/ + +{ + ULONG i; + + PAGED_CODE(); + UNREFERENCED_PARAMETER( IrpContext ); + + for (i=0; i < UnicodeName->Length/sizeof(WCHAR); i++) { + + if (UnicodeName->Buffer[i] == L' ') { + return TRUE; + } + } + + return FALSE; +} + + diff --git a/filesys/fastfat/nodetype.h b/filesys/fastfat/nodetype.h new file mode 100644 index 000000000..c2ed4085a --- /dev/null +++ b/filesys/fastfat/nodetype.h @@ -0,0 +1,194 @@ +/*++ + +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 _FATNODETYPE_ +#define _FATNODETYPE_ + +typedef USHORT NODE_TYPE_CODE; +typedef NODE_TYPE_CODE *PNODE_TYPE_CODE; + +#define NTC_UNDEFINED ((NODE_TYPE_CODE)0x0000) + +#define FAT_NTC_DATA_HEADER ((NODE_TYPE_CODE)0x0500) +#define FAT_NTC_VCB ((NODE_TYPE_CODE)0x0501) +#define FAT_NTC_FCB ((NODE_TYPE_CODE)0x0502) +#define FAT_NTC_DCB ((NODE_TYPE_CODE)0x0503) +#define FAT_NTC_ROOT_DCB ((NODE_TYPE_CODE)0x0504) +#define FAT_NTC_CCB ((NODE_TYPE_CODE)0x0507) +#define FAT_NTC_IRP_CONTEXT ((NODE_TYPE_CODE)0x0508) + +typedef CSHORT NODE_BYTE_SIZE; + +#ifndef NodeType + +// +// So all records start with +// +// typedef struct _RECORD_NAME { +// NODE_TYPE_CODE NodeTypeCode; +// NODE_BYTE_SIZE NodeByteSize; +// : +// } RECORD_NAME; +// typedef RECORD_NAME *PRECORD_NAME; +// + +#define NodeType(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 +// FAT_BUG_CHECK_ values defined below and then use FatBugCheck to bugcheck +// the system. +// + + +#define FAT_BUG_CHECK_ACCHKSUP (0x00010000) +#define FAT_BUG_CHECK_ALLOCSUP (0x00020000) +#define FAT_BUG_CHECK_CACHESUP (0x00030000) +#define FAT_BUG_CHECK_CLEANUP (0x00040000) +#define FAT_BUG_CHECK_CLOSE (0x00050000) +#define FAT_BUG_CHECK_CREATE (0x00060000) +#define FAT_BUG_CHECK_DEVCTRL (0x00070000) +#define FAT_BUG_CHECK_DEVIOSUP (0x00080000) +#define FAT_BUG_CHECK_DIRCTRL (0x00090000) +#define FAT_BUG_CHECK_DIRSUP (0x000a0000) +#define FAT_BUG_CHECK_DUMPSUP (0x000b0000) +#define FAT_BUG_CHECK_EA (0x000c0000) +#define FAT_BUG_CHECK_EASUP (0x000d0000) +#define FAT_BUG_CHECK_FATDATA (0x000e0000) +#define FAT_BUG_CHECK_FATINIT (0x000f0000) +#define FAT_BUG_CHECK_FILEINFO (0x00100000) +#define FAT_BUG_CHECK_FILOBSUP (0x00110000) +#define FAT_BUG_CHECK_FLUSH (0x00120000) +#define FAT_BUG_CHECK_FSCTRL (0x00130000) +#define FAT_BUG_CHECK_FSPDISP (0x00140000) +#define FAT_BUG_CHECK_LOCKCTRL (0x00150000) +#define FAT_BUG_CHECK_NAMESUP (0x00160000) +#define FAT_BUG_CHECK_PNP (0x00170000) +#define FAT_BUG_CHECK_READ (0x00180000) +#define FAT_BUG_CHECK_RESRCSUP (0x00190000) +#define FAT_BUG_CHECK_SHUTDOWN (0x001a0000) +#define FAT_BUG_CHECK_SPLAYSUP (0x001b0000) +#define FAT_BUG_CHECK_STRUCSUP (0x001c0000) +#define FAT_BUG_CHECK_TIMESUP (0x001d0000) +#define FAT_BUG_CHECK_VERFYSUP (0x001e0000) +#define FAT_BUG_CHECK_VOLINFO (0x001f0000) +#define FAT_BUG_CHECK_WORKQUE (0x00200000) +#define FAT_BUG_CHECK_WRITE (0x00210000) + + +#define FatBugCheck(A,B,C) { KeBugCheckEx(FAT_FILE_SYSTEM, BugCheckFileId | __LINE__, A, B, C ); } + + +// +// In this module we'll also define some globally known constants +// + +#define UCHAR_NUL 0x00 +#define UCHAR_SOH 0x01 +#define UCHAR_STX 0x02 +#define UCHAR_ETX 0x03 +#define UCHAR_EOT 0x04 +#define UCHAR_ENQ 0x05 +#define UCHAR_ACK 0x06 +#define UCHAR_BEL 0x07 +#define UCHAR_BS 0x08 +#define UCHAR_HT 0x09 +#define UCHAR_LF 0x0a +#define UCHAR_VT 0x0b +#define UCHAR_FF 0x0c +#define UCHAR_CR 0x0d +#define UCHAR_SO 0x0e +#define UCHAR_SI 0x0f +#define UCHAR_DLE 0x10 +#define UCHAR_DC1 0x11 +#define UCHAR_DC2 0x12 +#define UCHAR_DC3 0x13 +#define UCHAR_DC4 0x14 +#define UCHAR_NAK 0x15 +#define UCHAR_SYN 0x16 +#define UCHAR_ETB 0x17 +#define UCHAR_CAN 0x18 +#define UCHAR_EM 0x19 +#define UCHAR_SUB 0x1a +#define UCHAR_ESC 0x1b +#define UCHAR_FS 0x1c +#define UCHAR_GS 0x1d +#define UCHAR_RS 0x1e +#define UCHAR_US 0x1f +#define UCHAR_SP 0x20 + +#ifndef BUILDING_FSKDEXT + +// +// These are the tags we use to uniquely identify pool allocations. +// + +#define TAG_CCB 'CtaF' +#define TAG_FCB 'FtaF' +#define TAG_FCB_NONPAGED 'NtaF' +#define TAG_ERESOURCE 'EtaF' +#define TAG_IRP_CONTEXT 'ItaF' + +#define TAG_BCB 'btaF' +#define TAG_DIRENT 'DtaF' +#define TAG_DIRENT_BITMAP 'TtaF' +#define TAG_EA_DATA 'dtaF' +#define TAG_EA_SET_HEADER 'etaF' +#define TAG_EVENT 'ttaF' +#define TAG_FAT_BITMAP 'BtaF' +#define TAG_FAT_CLOSE_CONTEXT 'xtaF' +#define TAG_FAT_IO_CONTEXT 'XtaF' +#define TAG_FAT_WINDOW 'WtaF' +#define TAG_FILENAME_BUFFER 'ntaF' +#define TAG_IO_RUNS 'itaF' +#define TAG_REPINNED_BCB 'RtaF' +#define TAG_STASHED_BPB 'StaF' +#define TAG_VCB_STATS 'VtaF' +#define TAG_DEFERRED_FLUSH_CONTEXT 'ftaF' + +#define TAG_VPB 'vtaF' + +#define TAG_VERIFY_BOOTSECTOR 'staF' +#define TAG_VERIFY_ROOTDIR 'rtaF' + +#define TAG_OUTPUT_MAPPINGPAIRS 'PtaF' + +#define TAG_ENTRY_LOOKUP_BUFFER 'LtaF' + +#define TAG_IO_BUFFER 'OtaF' +#define TAG_IO_USER_BUFFER 'QtaF' + +#define TAG_DYNAMIC_NAME_BUFFER 'ctaF' + +#define TAG_DEFRAG_BUFFER 'GtaF' + +#endif + +#endif // _NODETYPE_ + + diff --git a/filesys/fastfat/pnp.c b/filesys/fastfat/pnp.c new file mode 100644 index 000000000..e61626846 --- /dev/null +++ b/filesys/fastfat/pnp.c @@ -0,0 +1,889 @@ +/*++ + +Copyright (c) 1997-2000 Microsoft Corporation + +Module Name: + + Pnp.c + +Abstract: + + This module implements the Plug and Play routines for FAT called by + the dispatch driver. + + +--*/ + +#include "FatProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (FAT_BUG_CHECK_PNP) + +#define Dbg (DEBUG_TRACE_PNP) + +_Requires_lock_held_(_Global_critical_region_) +_Requires_lock_held_(FatData.Resource) +NTSTATUS +FatPnpQueryRemove ( + PIRP_CONTEXT IrpContext, + PIRP Irp, + PVCB Vcb + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatPnpRemove ( + PIRP_CONTEXT IrpContext, + PIRP Irp, + PVCB Vcb + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatPnpSurpriseRemove ( + PIRP_CONTEXT IrpContext, + PIRP Irp, + PVCB Vcb + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatPnpCancelRemove ( + PIRP_CONTEXT IrpContext, + PIRP Irp, + PVCB Vcb + ); + +IO_COMPLETION_ROUTINE FatPnpCompletionRoutine; + +NTSTATUS +FatPnpCompletionRoutine ( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PIRP Irp, + _In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, FatCommonPnp) +#pragma alloc_text(PAGE, FatFsdPnp) +#pragma alloc_text(PAGE, FatPnpCancelRemove) +#pragma alloc_text(PAGE, FatPnpQueryRemove) +#pragma alloc_text(PAGE, FatPnpRemove) +#pragma alloc_text(PAGE, FatPnpSurpriseRemove) +#endif + + +_Function_class_(IRP_MJ_PNP) +_Function_class_(DRIVER_DISPATCH) +NTSTATUS +FatFsdPnp ( + _In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + _Inout_ PIRP Irp + ) + +/*++ + +Routine Description: + + This routine implements the FSD part of PnP operations + +Arguments: + + VolumeDeviceObject - Supplies the volume device object where the + file exists + + Irp - Supplies the Irp being processed + +Return Value: + + NTSTATUS - The FSD status for the IRP + +--*/ + +{ + NTSTATUS Status; + PIRP_CONTEXT IrpContext = NULL; + + BOOLEAN TopLevel; + BOOLEAN Wait; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatFsdPnp\n", 0); + + FsRtlEnterFileSystem(); + + TopLevel = FatIsIrpTopLevel( Irp ); + + try { + + // + // We expect there to never be a fileobject, in which case we will always + // wait. Since at the moment we don't have any concept of pending Pnp + // operations, this is a bit nitpicky. + // + + if (IoGetCurrentIrpStackLocation( Irp )->FileObject == NULL) { + + Wait = TRUE; + + } else { + + Wait = CanFsdWait( Irp ); + } + + IrpContext = FatCreateIrpContext( Irp, Wait ); + + Status = FatCommonPnp( IrpContext, Irp ); + + } except(FatExceptionFilter( IrpContext, GetExceptionInformation() )) { + + // + // We had some trouble trying to perform the requested + // operation, so we'll abort the I/O request with + // the error status that we get back from the + // execption code + // + + Status = FatProcessException( IrpContext, Irp, GetExceptionCode() ); + } + + if (TopLevel) { IoSetTopLevelIrp( NULL ); } + + FsRtlExitFileSystem(); + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "FatFsdPnp -> %08lx\n", Status); + + UNREFERENCED_PARAMETER( VolumeDeviceObject ); + + return Status; +} + + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatCommonPnp ( + IN PIRP_CONTEXT IrpContext, + IN 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; + + PIO_STACK_LOCATION IrpSp; + + PVOLUME_DEVICE_OBJECT OurDeviceObject; + PVCB Vcb; + + PAGED_CODE(); + + // + // Force everything to wait. + // + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT); + + // + // 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; + + // + // Take the global lock to synchronise against volume teardown. + // + +#pragma prefast( push ) +#pragma prefast( disable: 28137, "prefast wants the wait to be a constant, but that isn't possible for the way fastfat is designed" ) +#pragma prefast( disable: 28193, "this will always wait" ) + + FatAcquireExclusiveGlobal( IrpContext ); + +#pragma prefast( pop ) + + // + // 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, "touching Size is ok for a filesystem" ) + if (OurDeviceObject->DeviceObject.Size != sizeof(VOLUME_DEVICE_OBJECT) || + NodeType( &OurDeviceObject->Vcb ) != FAT_NTC_VCB) { + + // + // We were called with something we don't understand. + // + + FatReleaseGlobal( IrpContext ); + + Status = STATUS_INVALID_PARAMETER; + FatCompleteRequest( IrpContext, Irp, Status ); + return Status; + } + + Vcb = &OurDeviceObject->Vcb; + + // + // Case on the minor code. + // + + switch ( IrpSp->MinorFunction ) { + + case IRP_MN_QUERY_REMOVE_DEVICE: + + Status = FatPnpQueryRemove( IrpContext, Irp, Vcb ); + break; + + case IRP_MN_SURPRISE_REMOVAL: + + Status = FatPnpSurpriseRemove( IrpContext, Irp, Vcb ); + break; + + case IRP_MN_REMOVE_DEVICE: + + Status = FatPnpRemove( IrpContext, Irp, Vcb ); + break; + + case IRP_MN_CANCEL_REMOVE_DEVICE: + + Status = FatPnpCancelRemove( IrpContext, Irp, Vcb ); + break; + + default: + + FatReleaseGlobal( 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. + // + + FatCompleteRequest( IrpContext, NULL, STATUS_SUCCESS ); + + break; + } + + return Status; +} + + +VOID +FatPnpAdjustVpbRefCount( + IN PVCB Vcb, + IN ULONG Delta + ) +{ + KIRQL OldIrql; + + IoAcquireVpbSpinLock( &OldIrql); + Vcb->Vpb->ReferenceCount += Delta; + IoReleaseVpbSpinLock( OldIrql); +} + +_Requires_lock_held_(_Global_critical_region_) +_Requires_lock_held_(FatData.Resource) +NTSTATUS +FatPnpQueryRemove ( + PIRP_CONTEXT IrpContext, + PIRP Irp, + 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 = STATUS_SUCCESS; + KEVENT Event; + BOOLEAN VcbDeleted = FALSE; + BOOLEAN GlobalHeld = TRUE; + + PAGED_CODE(); + + // + // 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. + // + + FatAcquireExclusiveVcb( IrpContext, Vcb ); + + FatReleaseGlobal( IrpContext); + GlobalHeld = FALSE; + + try { + + Status = FatLockVolumeInternal( IrpContext, Vcb, NULL ); + + // + // Drop an additional reference on the Vpb so that the volume cannot be + // torn down when we drop all the locks below. + // + + FatPnpAdjustVpbRefCount( Vcb, 1); + + // + // Drop and reacquire the resources in the right order. + // + + FatReleaseVcb( IrpContext, Vcb ); + + NT_ASSERT( FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) ); + +#pragma prefast( push ) +#pragma prefast( disable: 28137, "prefast wants the wait to be a constant, but that isn't possible for the way fastfat is designed" ) +#pragma prefast( disable: 28193, "this will always wait" ) + + FatAcquireExclusiveGlobal( IrpContext ); + GlobalHeld = TRUE; + +#pragma prefast( pop ) + + FatAcquireExclusiveVcb( IrpContext, Vcb ); + + // + // Drop the reference we added above. + // + + FatPnpAdjustVpbRefCount( Vcb, (ULONG)-1); + + if (NT_SUCCESS( Status )) { + + // + // With the volume held locked, note that we must finalize as much + // as possible right now. + // + + FatFlushAndCleanVolume( IrpContext, Irp, Vcb, Flush ); + + // + // 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, + FatPnpCompletionRoutine, + &Event, + TRUE, + TRUE, + TRUE ); + + // + // Send the request and wait. + // + + Status = IoCallDriver(Vcb->TargetDeviceObject, Irp); + + if (Status == STATUS_PENDING) { + + 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 )) { + + VcbDeleted = FatCheckForDismount( IrpContext, Vcb, TRUE ); + + NT_ASSERT( VcbDeleted || Vcb->VcbCondition == VcbBad ); + + } + } + + } finally { + + // + // Release the Vcb if it could still remain. + // + + if (!VcbDeleted) { + + FatReleaseVcb( IrpContext, Vcb ); + } + + if (GlobalHeld) { + + FatReleaseGlobal( IrpContext ); + } + } + + // + // Cleanup our IrpContext and complete the IRP if neccesary. + // + + FatCompleteRequest( IrpContext, Irp, Status ); + + return Status; +} + + + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatPnpRemove ( + PIRP_CONTEXT IrpContext, + PIRP Irp, + 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 of 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 VcbDeleted = FALSE; + + PAGED_CODE(); + + // + // 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. + // + + FatAcquireExclusiveVcb( IrpContext, Vcb ); + + // + // The device will be going away. Remove our lock (benign + // if we never had it). + // + + (VOID) FatUnlockVolumeInternal( IrpContext, Vcb, NULL ); + + // + // 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, + FatPnpCompletionRoutine, + &Event, + TRUE, + TRUE, + TRUE ); + + // + // Send the request and wait. + // + + Status = IoCallDriver(Vcb->TargetDeviceObject, Irp); + + if (Status == STATUS_PENDING) { + + KeWaitForSingleObject( &Event, + Executive, + KernelMode, + FALSE, + NULL ); + + Status = Irp->IoStatus.Status; + } + + try { + + // + // Knock as many files down for this volume as we can. + // + + FatFlushAndCleanVolume( IrpContext, Irp, Vcb, NoFlush ); + + // + // 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. + // + + VcbDeleted = FatCheckForDismount( IrpContext, Vcb, TRUE ); + + } finally { + + // + // Release the Vcb if it could still remain. + // + + if (!VcbDeleted) { + + FatReleaseVcb( IrpContext, Vcb ); + } + + FatReleaseGlobal( IrpContext ); + } + + // + // Cleanup our IrpContext and complete the IRP. + // + + FatCompleteRequest( IrpContext, Irp, Status ); + + return Status; +} + + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatPnpSurpriseRemove ( + PIRP_CONTEXT IrpContext, + PIRP Irp, + 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 VcbDeleted = FALSE; + + PAGED_CODE(); + + // + // SURPRISE - a device was physically yanked away without + // any warning. This means external forces. + // + + FatAcquireExclusiveVcb( 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, + FatPnpCompletionRoutine, + &Event, + TRUE, + TRUE, + TRUE ); + + // + // Send the request and wait. + // + + Status = IoCallDriver(Vcb->TargetDeviceObject, Irp); + + if (Status == STATUS_PENDING) { + + KeWaitForSingleObject( &Event, + Executive, + KernelMode, + FALSE, + NULL ); + + Status = Irp->IoStatus.Status; + } + + try { + + // + // Knock as many files down for this volume as we can. + // + + FatFlushAndCleanVolume( IrpContext, Irp, Vcb, NoFlush ); + + // + // 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. + // + + VcbDeleted = FatCheckForDismount( IrpContext, Vcb, TRUE ); + + } finally { + + // + // Release the Vcb if it could still remain. + // + + if (!VcbDeleted) { + + FatReleaseVcb( IrpContext, Vcb ); + } + + FatReleaseGlobal( IrpContext ); + } + + // + // Cleanup our IrpContext and complete the IRP. + // + + FatCompleteRequest( IrpContext, Irp, Status ); + + return Status; +} + + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatPnpCancelRemove ( + PIRP_CONTEXT IrpContext, + PIRP Irp, + 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 = STATUS_SUCCESS; + + PAGED_CODE(); + + // + // 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 FAT, 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. + // + + FatAcquireExclusiveVcb( IrpContext, Vcb ); + FatReleaseGlobal( IrpContext); + + // + // Unlock the volume. This is benign if we never had seen + // a QUERY. + // + + (VOID)FatUnlockVolumeInternal( IrpContext, Vcb, NULL ); + + try { + + // + // 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); + } + finally { + + FatReleaseVcb( IrpContext, Vcb ); + } + + FatCompleteRequest( IrpContext, NULL, STATUS_SUCCESS ); + + return Status; +} + + +// +// Local support routine +// + +NTSTATUS +FatPnpCompletionRoutine ( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PIRP Irp, + _In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt + ) +{ + PKEVENT Event = (PKEVENT) Contxt; + + KeSetEvent( Event, 0, FALSE ); + + return STATUS_MORE_PROCESSING_REQUIRED; + + UNREFERENCED_PARAMETER( DeviceObject ); + UNREFERENCED_PARAMETER( Contxt ); + UNREFERENCED_PARAMETER( Irp ); +} + + diff --git a/filesys/fastfat/read.c b/filesys/fastfat/read.c new file mode 100644 index 000000000..0f3d3e55d --- /dev/null +++ b/filesys/fastfat/read.c @@ -0,0 +1,1735 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + Read.c + +Abstract: + + This module implements the File Read routine for Read called by the + dispatch driver. + + +--*/ + +#include "FatProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (FAT_BUG_CHECK_READ) + +// +// The local debug trace level +// + +#define Dbg (DEBUG_TRACE_READ) + +// +// Define stack overflow read threshhold. For the x86 we'll use a smaller +// threshold than for a risc platform. +// +// Empirically, the limit is a result of the (large) amount of stack +// neccesary to throw an exception. +// + +#if defined(_M_IX86) +#define OVERFLOW_READ_THRESHHOLD (0xE00) +#else +#define OVERFLOW_READ_THRESHHOLD (0x1000) +#endif // defined(_M_IX86) + + +// +// The following procedures handles read stack overflow operations. +// + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatStackOverflowRead ( + IN PVOID Context, + IN PKEVENT Event + ); + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatPostStackOverflowRead ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp, + IN PFCB Fcb + ); + +VOID +FatOverflowPagingFileRead ( + IN PVOID Context, + IN PKEVENT Event + ); + +// +// VOID +// SafeZeroMemory ( +// IN PUCHAR At, +// IN ULONG ByteCount +// ); +// + +// +// This macro just puts a nice little try-except around RtlZeroMemory +// + +#define SafeZeroMemory(AT,BYTE_COUNT) { \ + try { \ + RtlZeroMemory((AT), (BYTE_COUNT)); \ + } except(EXCEPTION_EXECUTE_HANDLER) { \ + FatRaiseStatus( IrpContext, STATUS_INVALID_USER_BUFFER ); \ + } \ +} + +// +// Macro to increment appropriate performance counters. +// + +#define CollectReadStats(VCB,OPEN_TYPE,BYTE_COUNT) { \ + PFILESYSTEM_STATISTICS Stats = &(VCB)->Statistics[KeGetCurrentProcessorNumber() % FatData.NumberProcessors].Common; \ + if (((OPEN_TYPE) == UserFileOpen)) { \ + Stats->UserFileReads += 1; \ + Stats->UserFileReadBytes += (ULONG)(BYTE_COUNT); \ + } else if (((OPEN_TYPE) == VirtualVolumeFile || ((OPEN_TYPE) == DirectoryFile))) { \ + Stats->MetaDataReads += 1; \ + Stats->MetaDataReadBytes += (ULONG)(BYTE_COUNT); \ + } \ +} + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, FatStackOverflowRead) +#pragma alloc_text(PAGE, FatPostStackOverflowRead) +#pragma alloc_text(PAGE, FatCommonRead) +#endif + + +_Function_class_(IRP_MJ_READ) +_Function_class_(DRIVER_DISPATCH) +NTSTATUS +FatFsdRead ( + _In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + _Inout_ PIRP Irp + ) + +/*++ + +Routine Description: + + This is the driver entry to the common read routine for NtReadFile calls. + For synchronous requests, the CommonRead is called with Wait == TRUE, + which means the request will always be completed in the current thread, + and never passed to the Fsp. If it is not a synchronous request, + CommonRead is called with Wait == FALSE, which means the request + will be passed to the Fsp only if there is a need to block. + +Arguments: + + VolumeDeviceObject - Supplies the volume device object where the + file being Read exists + + Irp - Supplies the Irp being processed + +Return Value: + + NTSTATUS - The FSD status for the IRP + +--*/ + +{ + PFCB Fcb = NULL; + NTSTATUS Status; + PIRP_CONTEXT IrpContext = NULL; + + BOOLEAN TopLevel = FALSE; + + DebugTrace(+1, Dbg, "FatFsdRead\n", 0); + + // + // Call the common Read routine, with blocking allowed if synchronous + // + + FsRtlEnterFileSystem(); + + // + // We are first going to do a quick check for paging file IO. Since this + // is a fast path, we must replicate the check for the fsdo. + // + + if (!FatDeviceIsFatFsdo( IoGetCurrentIrpStackLocation(Irp)->DeviceObject)) { + + Fcb = (PFCB)(IoGetCurrentIrpStackLocation(Irp)->FileObject->FsContext); + + if ((NodeType(Fcb) == FAT_NTC_FCB) && + FlagOn(Fcb->FcbState, FCB_STATE_PAGING_FILE)) { + + // + // Do the usual STATUS_PENDING things. + // + + IoMarkIrpPending( Irp ); + + // + // If there is not enough stack to do this read, then post this + // read to the overflow queue. + // + + if (IoGetRemainingStackSize() < OVERFLOW_READ_THRESHHOLD) { + + KEVENT Event; + PAGING_FILE_OVERFLOW_PACKET Packet; + + Packet.Irp = Irp; + Packet.Fcb = Fcb; + + KeInitializeEvent( &Event, NotificationEvent, FALSE ); + + FsRtlPostPagingFileStackOverflow( &Packet, &Event, FatOverflowPagingFileRead ); + + // + // And wait for the worker thread to complete the item + // + + (VOID) KeWaitForSingleObject( &Event, Executive, KernelMode, FALSE, NULL ); + + } else { + + // + // Perform the actual IO, it will be completed when the io finishes. + // + + FatPagingFileIo( Irp, Fcb ); + } + + FsRtlExitFileSystem(); + + return STATUS_PENDING; + } + } + + try { + + TopLevel = FatIsIrpTopLevel( Irp ); + + IrpContext = FatCreateIrpContext( Irp, CanFsdWait( Irp ) ); + + // + // If this is an Mdl complete request, don't go through + // common read. + // + + if ( FlagOn(IrpContext->MinorFunction, IRP_MN_COMPLETE) ) { + + DebugTrace(0, Dbg, "Calling FatCompleteMdl\n", 0 ); + try_return( Status = FatCompleteMdl( IrpContext, Irp )); + } + + // + // Check if we have enough stack space to process this request. If there + // isn't enough then we will pass the request off to the stack overflow thread. + // + + if (IoGetRemainingStackSize() < OVERFLOW_READ_THRESHHOLD) { + + DebugTrace(0, Dbg, "Passing StackOverflowRead off\n", 0 ); + try_return( Status = FatPostStackOverflowRead( IrpContext, Irp, Fcb ) ); + } + + Status = FatCommonRead( IrpContext, Irp ); + + try_exit: NOTHING; + } except(FatExceptionFilter( IrpContext, GetExceptionInformation() )) { + + // + // We had some trouble trying to perform the requested + // operation, so we'll abort the I/O request with + // the error status that we get back from the + // execption code + // + + Status = FatProcessException( IrpContext, Irp, GetExceptionCode() ); + } + + if (TopLevel) { IoSetTopLevelIrp( NULL ); } + + FsRtlExitFileSystem(); + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "FatFsdRead -> %08lx\n", Status); + + UNREFERENCED_PARAMETER( VolumeDeviceObject ); + + return Status; +} + + +// +// Internal support routine +// + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatPostStackOverflowRead ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp, + IN PFCB Fcb + ) + +/*++ + +Routine Description: + + This routine posts a read request that could not be processed by + the fsp thread because of stack overflow potential. + +Arguments: + + Irp - Supplies the request to process. + + Fcb - Supplies the file. + +Return Value: + + STATUS_PENDING. + +--*/ + +{ + KEVENT Event; + PERESOURCE Resource; + PVCB Vcb; + + PAGED_CODE(); + + DebugTrace(0, Dbg, "Getting too close to stack limit pass request to Fsp\n", 0 ); + + // + // Initialize an event and get shared on the resource we will + // be later using the common read. + // + + KeInitializeEvent( &Event, NotificationEvent, FALSE ); + + // + // Preacquire the resource the read path will require so we know the + // worker thread can proceed without waiting. + // + + if (FlagOn(Irp->Flags, IRP_PAGING_IO) && (Fcb->Header.PagingIoResource != NULL)) { + + Resource = Fcb->Header.PagingIoResource; + + } else { + + Resource = Fcb->Header.Resource; + } + + // + // If there are no resources assodicated with the file (case: the virtual + // volume file), it is OK. No resources will be acquired on the other side + // as well. + // + + if (Resource) { + + ExAcquireResourceSharedLite( Resource, TRUE ); + } + + if (NodeType( Fcb ) == FAT_NTC_VCB) { + + Vcb = (PVCB) Fcb; + + } else { + + Vcb = Fcb->Vcb; + } + + try { + + // + // Make the Irp just like a regular post request and + // then send the Irp to the special overflow thread. + // After the post we will wait for the stack overflow + // read routine to set the event that indicates we can + // now release the scb resource and return. + // + + FatPrePostIrp( IrpContext, Irp ); + + // + // If this read is the result of a verify, we have to + // tell the overflow read routne to temporarily + // hijack the Vcb->VerifyThread field so that reads + // can go through. + // + + if (Vcb->VerifyThread == KeGetCurrentThread()) { + + SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_VERIFY_READ); + } + + FsRtlPostStackOverflow( IrpContext, &Event, FatStackOverflowRead ); + + // + // And wait for the worker thread to complete the item + // + + KeWaitForSingleObject( &Event, Executive, KernelMode, FALSE, NULL ); + + } finally { + + if (Resource) { + + ExReleaseResourceLite( Resource ); + } + } + + return STATUS_PENDING; +} + + +// +// Internal support routine +// + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatStackOverflowRead ( + IN PVOID Context, + IN PKEVENT Event + ) + +/*++ + +Routine Description: + + This routine processes a read request that could not be processed by + the fsp thread because of stack overflow potential. + +Arguments: + + Context - Supplies the IrpContext being processed + + Event - Supplies the event to be signaled when we are done processing this + request. + +Return Value: + + None. + +--*/ + +{ + PIRP_CONTEXT IrpContext = Context; + PKTHREAD SavedVerifyThread = NULL; + PVCB Vcb = NULL; + + PAGED_CODE(); + + // + // Make it now look like we can wait for I/O to complete + // + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ); + + // + // If this read was as the result of a verify we have to fake out the + // the Vcb->VerifyThread field. + // + + if (FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_VERIFY_READ)) { + + PFCB Fcb = (PFCB)IoGetCurrentIrpStackLocation(IrpContext->OriginatingIrp)-> + FileObject->FsContext; + + if (NodeType( Fcb ) == FAT_NTC_VCB) { + + Vcb = (PVCB) Fcb; + + } else { + + Vcb = Fcb->Vcb; + } + + NT_ASSERT( Vcb->VerifyThread != NULL ); + SavedVerifyThread = Vcb->VerifyThread; + Vcb->VerifyThread = KeGetCurrentThread(); + } + + // + // Do the read operation protected by a try-except clause + // + + try { + + (VOID) FatCommonRead( IrpContext, IrpContext->OriginatingIrp ); + + } except(FatExceptionFilter( IrpContext, GetExceptionInformation() )) { + + NTSTATUS ExceptionCode; + + // + // We had some trouble trying to perform the requested + // operation, so we'll abort the I/O request with + // the error status that we get back from the + // execption code + // + + ExceptionCode = GetExceptionCode(); + + if (ExceptionCode == STATUS_FILE_DELETED) { + + IrpContext->ExceptionStatus = ExceptionCode = STATUS_END_OF_FILE; + IrpContext->OriginatingIrp->IoStatus.Information = 0; + } + + (VOID) FatProcessException( IrpContext, IrpContext->OriginatingIrp, ExceptionCode ); + } + + // + // Restore the original VerifyVolumeThread + // + + if (SavedVerifyThread != NULL) { + + NT_ASSERT( Vcb->VerifyThread == KeGetCurrentThread() ); + Vcb->VerifyThread = SavedVerifyThread; + } + + // + // Set the stack overflow item's event to tell the original + // thread that we're done. + // + + KeSetEvent( Event, 0, FALSE ); +} + + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatCommonRead ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the common read routine for NtReadFile, called from both + the Fsd, or from the Fsp if a request could not be completed without + blocking in the Fsd. This routine has no code where it determines + whether it is running in the Fsd or Fsp. Instead, its actions are + conditionalized by the Wait input parameter, which determines whether + it is allowed to block or not. If a blocking condition is encountered + with Wait == FALSE, however, the request is posted to the Fsp, who + always calls with WAIT == TRUE. + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + PVCB Vcb; + PFCB FcbOrDcb; + PCCB Ccb; + + VBO StartingVbo; + ULONG ByteCount; + ULONG RequestedByteCount; + + PIO_STACK_LOCATION IrpSp; + PFILE_OBJECT FileObject; + TYPE_OF_OPEN TypeOfOpen; + + BOOLEAN PostIrp = FALSE; + BOOLEAN OplockPostIrp = FALSE; + + BOOLEAN FcbOrDcbAcquired = FALSE; + + BOOLEAN Wait; + BOOLEAN PagingIo; + BOOLEAN NonCachedIo; + BOOLEAN SynchronousIo; + + + NTSTATUS Status = STATUS_SUCCESS; + + FAT_IO_CONTEXT StackFatIoContext; + + // + // A system buffer is only used if we have to access the + // buffer directly from the Fsp to clear a portion or to + // do a synchronous I/O, or a cached transfer. It is + // possible that our caller may have already mapped a + // system buffer, in which case we must remember this so + // we do not unmap it on the way out. + // + + PVOID SystemBuffer = NULL; + + LARGE_INTEGER StartingByte; + + PAGED_CODE(); + + // + // Get current Irp stack location. + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + FileObject = IrpSp->FileObject; + + // + // Initialize the appropriate local variables. + // + + Wait = BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT); + PagingIo = BooleanFlagOn(Irp->Flags, IRP_PAGING_IO); + NonCachedIo = BooleanFlagOn(Irp->Flags,IRP_NOCACHE); + SynchronousIo = BooleanFlagOn(FileObject->Flags, FO_SYNCHRONOUS_IO); + + DebugTrace(+1, Dbg, "CommonRead\n", 0); + DebugTrace( 0, Dbg, " Irp = %p\n", Irp); + DebugTrace( 0, Dbg, " ->ByteCount = %8lx\n", IrpSp->Parameters.Read.Length); + DebugTrace( 0, Dbg, " ->ByteOffset.LowPart = %8lx\n", IrpSp->Parameters.Read.ByteOffset.LowPart); + DebugTrace( 0, Dbg, " ->ByteOffset.HighPart = %8lx\n", IrpSp->Parameters.Read.ByteOffset.HighPart); + + // + // Extract starting Vbo and offset. + // + + StartingByte = IrpSp->Parameters.Read.ByteOffset; + + StartingVbo = StartingByte.LowPart; + + ByteCount = IrpSp->Parameters.Read.Length; + RequestedByteCount = ByteCount; + + // + // Check for a null request, and return immediately + // + + if (ByteCount == 0) { + + Irp->IoStatus.Information = 0; + FatCompleteRequest( IrpContext, Irp, STATUS_SUCCESS ); + return STATUS_SUCCESS; + } + + // + // Extract the nature of the read from the file object, and case on it + // + + TypeOfOpen = FatDecodeFileObject(FileObject, &Vcb, &FcbOrDcb, &Ccb); + + NT_ASSERT( Vcb != NULL ); + + // + // Save callers who try to do cached IO to the raw volume from themselves. + // + + if (TypeOfOpen == UserVolumeOpen) { + + NonCachedIo = TRUE; + } + + NT_ASSERT(!(NonCachedIo == FALSE && TypeOfOpen == VirtualVolumeFile)); + + // + // Collect interesting statistics. The FLAG_USER_IO bit will indicate + // what type of io we're doing in the FatNonCachedIo function. + // + + if (PagingIo) { + CollectReadStats(Vcb, TypeOfOpen, ByteCount); + + if (TypeOfOpen == UserFileOpen) { + SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_USER_IO); + } else { + ClearFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_USER_IO); + } + } + + NT_ASSERT(!FlagOn( IrpContext->Flags, IRP_CONTEXT_STACK_IO_CONTEXT )); + + // + // Allocate if necessary and initialize a FAT_IO_CONTEXT block for + // all non cached Io. For synchronous Io we use stack storage, + // otherwise we allocate pool. + // + + if (NonCachedIo) { + + if (IrpContext->FatIoContext == NULL) { + + if (!Wait) { + + IrpContext->FatIoContext = + FsRtlAllocatePoolWithTag( NonPagedPoolNx, + sizeof(FAT_IO_CONTEXT), + TAG_FAT_IO_CONTEXT ); + + } else { + + IrpContext->FatIoContext = &StackFatIoContext; + + SetFlag( IrpContext->Flags, IRP_CONTEXT_STACK_IO_CONTEXT ); + } + } + + RtlZeroMemory( IrpContext->FatIoContext, sizeof(FAT_IO_CONTEXT) ); + + if (Wait) { + + KeInitializeEvent( &IrpContext->FatIoContext->Wait.SyncEvent, + NotificationEvent, + FALSE ); + + } else { + + if (PagingIo) { + + IrpContext->FatIoContext->Wait.Async.ResourceThreadId = + ExGetCurrentResourceThread(); + + } else { + + IrpContext->FatIoContext->Wait.Async.ResourceThreadId = + ((ULONG_PTR)IrpContext->FatIoContext) | 3; + } + + IrpContext->FatIoContext->Wait.Async.RequestedByteCount = + ByteCount; + + IrpContext->FatIoContext->Wait.Async.FileObject = FileObject; + } + } + + + // + // These two cases correspond to either a general opened volume, ie. + // open ("a:"), or a read of the volume file (boot sector + fat(s)) + // + + if ((TypeOfOpen == VirtualVolumeFile) || + (TypeOfOpen == UserVolumeOpen)) { + + LBO StartingLbo; + + StartingLbo = StartingByte.QuadPart; + + DebugTrace(0, Dbg, "Type of read is User Volume or virtual volume file\n", 0); + + if (TypeOfOpen == UserVolumeOpen) { + + // + // Verify that the volume for this handle is still valid + // + + // + // Verify that the volume for this handle is still valid, permitting + // operations to proceed on dismounted volumes via the handle which + // performed the dismount. + // + + if (!FlagOn( Ccb->Flags, CCB_FLAG_COMPLETE_DISMOUNT | CCB_FLAG_SENT_FORMAT_UNIT )) { + + FatQuickVerifyVcb( IrpContext, Vcb ); + } + + // + // If the caller previously sent a format unit command, then we will allow + // their read/write requests to ignore the verify flag on the device, since some + // devices send a media change event after format unit, but we don't want to + // process it yet since we're probably in the process of formatting the + // media. + // + + if (FlagOn( Ccb->Flags, CCB_FLAG_SENT_FORMAT_UNIT )) { + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_OVERRIDE_VERIFY ); + } + + if (!FlagOn( Ccb->Flags, CCB_FLAG_DASD_FLUSH_DONE )) { + + (VOID)ExAcquireResourceExclusiveLite( &Vcb->Resource, TRUE ); + + try { + + // + // If the volume isn't locked, flush it. + // + + if (!FlagOn(Vcb->VcbState, VCB_STATE_FLAG_LOCKED)) { + + FatFlushVolume( IrpContext, Vcb, Flush ); + } + + } finally { + + ExReleaseResourceLite( &Vcb->Resource ); + } + + SetFlag( Ccb->Flags, CCB_FLAG_DASD_FLUSH_DONE ); + } + + if (!FlagOn( Ccb->Flags, CCB_FLAG_ALLOW_EXTENDED_DASD_IO )) { + + LBO VolumeSize; + + // + // Make sure we don't try to read past end of volume, + // reducing the byte count if necessary. + // + + VolumeSize = (LBO) Vcb->Bpb.BytesPerSector * + (Vcb->Bpb.Sectors != 0 ? Vcb->Bpb.Sectors : + Vcb->Bpb.LargeSectors); + + if (StartingLbo >= VolumeSize) { + Irp->IoStatus.Information = 0; + FatCompleteRequest( IrpContext, Irp, STATUS_END_OF_FILE ); + return STATUS_END_OF_FILE; + } + + if (ByteCount > VolumeSize - StartingLbo) { + + ByteCount = RequestedByteCount = (ULONG) (VolumeSize - StartingLbo); + + // + // For async reads we had set the byte count in the FatIoContext + // above, so fix that here. + // + + if (!Wait) { + + IrpContext->FatIoContext->Wait.Async.RequestedByteCount = + ByteCount; + } + } + } + + // + // For DASD we have to probe and lock the user's buffer + // + + FatLockUserBuffer( IrpContext, Irp, IoWriteAccess, ByteCount ); + + + } else { + + // + // Virtual volume file open -- increment performance counters. + // + + Vcb->Statistics[KeGetCurrentProcessorNumber() % FatData.NumberProcessors].Common.MetaDataDiskReads += 1; + + } + + // + // Read the data and wait for the results + // + + FatSingleAsync( IrpContext, + Vcb, + StartingLbo, + ByteCount, + Irp ); + +#if (NTDDI_VERSION >= NTDDI_WIN8) + + // + // Account for DASD Ios + // + + if (FatDiskAccountingEnabled) { + + PETHREAD ThreadIssuingIo = PsGetCurrentThread(); + + PsUpdateDiskCounters( PsGetThreadProcess( ThreadIssuingIo ), + ByteCount, + 0, + 1, + 0, + 0 ); + } + +#endif + + if (!Wait) { + + // + // We, nor anybody else, need the IrpContext any more. + // + + IrpContext->FatIoContext = NULL; + + FatDeleteIrpContext( IrpContext ); + + DebugTrace(-1, Dbg, "FatNonCachedIo -> STATUS_PENDING\n", 0); + + return STATUS_PENDING; + } + + FatWaitSync( IrpContext ); + + // + // If the call didn't succeed, raise the error status + // + + if (!NT_SUCCESS( Status = Irp->IoStatus.Status )) { + + NT_ASSERT( KeGetCurrentThread() != Vcb->VerifyThread || Status != STATUS_VERIFY_REQUIRED ); + FatNormalizeAndRaiseStatus( IrpContext, Status ); + } + + // + // Update the current file position + // + + if (SynchronousIo && !PagingIo) { + FileObject->CurrentByteOffset.QuadPart = + StartingLbo + Irp->IoStatus.Information; + } + + DebugTrace(-1, Dbg, "CommonRead -> %08lx\n", Status ); + + FatCompleteRequest( IrpContext, Irp, Status ); + return Status; + } + + // + // At this point we know there is an Fcb/Dcb. + // + + NT_ASSERT( FcbOrDcb != NULL ); + + // + // Check for a non-zero high part offset + // + + if ( StartingByte.HighPart != 0 ) { + + Irp->IoStatus.Information = 0; + FatCompleteRequest( IrpContext, Irp, STATUS_END_OF_FILE ); + return STATUS_END_OF_FILE; + } + + // + // Use a try-finally to free Fcb/Dcb and buffers on the way out. + // + + try { + + // + // This case corresponds to a normal user read file. + // + + if ( TypeOfOpen == UserFileOpen + ) { + + ULONG FileSize; + ULONG ValidDataLength; + + DebugTrace(0, Dbg, "Type of read is user file open\n", 0); + + // + // If this is a noncached transfer and is not a paging I/O, and + // the file has a data section, then we will do a flush here + // to avoid stale data problems. Note that we must flush before + // acquiring the Fcb shared since the write may try to acquire + // it exclusive. + // + + if (!PagingIo && NonCachedIo + + && + + (FileObject->SectionObjectPointer->DataSectionObject != NULL)) { + + IO_STATUS_BLOCK IoStatus = {0}; + +#ifndef REDUCE_SYNCHRONIZATION + if (!FatAcquireExclusiveFcb( IrpContext, FcbOrDcb )) { + + try_return( PostIrp = TRUE ); + } + + ExAcquireResourceExclusiveLite( FcbOrDcb->Header.PagingIoResource, TRUE ); +#endif //REDUCE_SYNCHRONIZATION + + CcFlushCache( FileObject->SectionObjectPointer, + &StartingByte, + ByteCount, + &IoStatus ); + +#ifndef REDUCE_SYNCHRONIZATION + ExReleaseResourceLite( FcbOrDcb->Header.PagingIoResource ); + FatReleaseFcb( IrpContext, FcbOrDcb ); +#endif //REDUCE_SYNCHRONIZATION + + if (!NT_SUCCESS( IoStatus.Status)) { + + try_return( IoStatus.Status ); + } + +#ifndef REDUCE_SYNCHRONIZATION + ExAcquireResourceExclusiveLite( FcbOrDcb->Header.PagingIoResource, TRUE ); + ExReleaseResourceLite( FcbOrDcb->Header.PagingIoResource ); +#endif //REDUCE_SYNCHRONIZATION + } + + // + // We need shared access to the Fcb/Dcb before proceeding. + // + + if ( PagingIo ) { + + if (!ExAcquireResourceSharedLite( FcbOrDcb->Header.PagingIoResource, + TRUE )) { + + DebugTrace( 0, Dbg, "Cannot acquire FcbOrDcb = %p shared without waiting\n", FcbOrDcb ); + + try_return( PostIrp = TRUE ); + } + + if (!Wait) { + + IrpContext->FatIoContext->Wait.Async.Resource = + FcbOrDcb->Header.PagingIoResource; + } + + } else { + + // + // If this is async I/O, we will wait if there is an + // exclusive waiter. + // + + if (!Wait && NonCachedIo) { + + if (!FatAcquireSharedFcbWaitForEx( IrpContext, FcbOrDcb )) { + + DebugTrace( 0, + Dbg, + "Cannot acquire FcbOrDcb = %p shared without waiting\n", + FcbOrDcb ); + + try_return( PostIrp = TRUE ); + } + + IrpContext->FatIoContext->Wait.Async.Resource = + FcbOrDcb->Header.Resource; + + } else { + + if (!FatAcquireSharedFcb( IrpContext, FcbOrDcb )) { + + DebugTrace( 0, + Dbg, + "Cannot acquire FcbOrDcb = %p shared without waiting\n", + FcbOrDcb ); + + try_return( PostIrp = TRUE ); + } + } + } + + FcbOrDcbAcquired = TRUE; + + // + // Make sure the FcbOrDcb is still good + // + + FatVerifyFcb( IrpContext, FcbOrDcb ); + + // + // We now check whether we can proceed based on the state of + // the file oplocks. + // + + if (!PagingIo) { + + Status = FsRtlCheckOplock( FatGetFcbOplock(FcbOrDcb), + Irp, + IrpContext, + FatOplockComplete, + FatPrePostIrp ); + + if (Status != STATUS_SUCCESS) { + + OplockPostIrp = TRUE; + PostIrp = TRUE; + try_return( NOTHING ); + } + + // + // Reset the flag indicating if Fast I/O is possible since the oplock + // check could have broken existing (conflicting) oplocks. + // + + FcbOrDcb->Header.IsFastIoPossible = FatIsFastIoPossible( FcbOrDcb ); + + // + // We have to check for read access according to the current + // state of the file locks, and set FileSize from the Fcb. + // + + if (!PagingIo && + !FsRtlCheckLockForReadAccess( &FcbOrDcb->Specific.Fcb.FileLock, + Irp )) { + + try_return( Status = STATUS_FILE_LOCK_CONFLICT ); + } + } + + // + // Pick up our sizes and check/trim the IO. + // + + FileSize = FcbOrDcb->Header.FileSize.LowPart; + ValidDataLength = FcbOrDcb->Header.ValidDataLength.LowPart; + + // + // If the read starts beyond End of File, return EOF. + // + + if (StartingVbo >= FileSize) { + + DebugTrace( 0, Dbg, "End of File\n", 0 ); + + try_return ( Status = STATUS_END_OF_FILE ); + } + + // + // If the read extends beyond EOF, truncate the read + // + + if (ByteCount > FileSize - StartingVbo) { + + ByteCount = RequestedByteCount = FileSize - StartingVbo; + + if (NonCachedIo && !Wait) { + + IrpContext->FatIoContext->Wait.Async.RequestedByteCount = + RequestedByteCount; + + } + } + + // + // HANDLE THE NON-CACHED CASE + // + + if ( NonCachedIo ) { + + ULONG SectorSize; + ULONG BytesToRead; + + DebugTrace(0, Dbg, "Non cached read.\n", 0); + + // + // Get the sector size + // + + SectorSize = (ULONG)Vcb->Bpb.BytesPerSector; + + // + // Start by zeroing any part of the read after Valid Data + // + + if (ValidDataLength < FcbOrDcb->ValidDataToDisk) { + + ValidDataLength = FcbOrDcb->ValidDataToDisk; + } + + + if ( StartingVbo + ByteCount > ValidDataLength ) { + + SystemBuffer = FatMapUserBuffer( IrpContext, Irp ); + + if (StartingVbo < ValidDataLength) { + + ULONG ZeroingOffset; + + // + // Now zero out the user's request sector aligned beyond + // vdl. We will handle the straddling sector at completion + // time via the bytecount reduction which immediately + // follows this check. + // + // Note that we used to post in this case for async requests. + // Note also that if the request was wholly beyond VDL that + // we did not post, therefore this is consistent. Synchronous + // zeroing is fine for async requests. + // + + ZeroingOffset = ((ValidDataLength - StartingVbo) + (SectorSize - 1)) + & ~(SectorSize - 1); + + // + // If the offset is at or above the byte count, no harm: just means + // that the read ends in the last sector and the zeroing will be + // done at completion. + // + + if (ByteCount > ZeroingOffset) { + + SafeZeroMemory( (PUCHAR) SystemBuffer + ZeroingOffset, + ByteCount - ZeroingOffset); + + } + + } else { + + // + // All we have to do now is sit here and zero the + // user's buffer, no reading is required. + // + + SafeZeroMemory( (PUCHAR)SystemBuffer, ByteCount ); + + Irp->IoStatus.Information = ByteCount; + + + try_return ( Status = STATUS_SUCCESS ); + } + } + + + // + // Reduce the byte count to actually read if it extends beyond + // Valid Data Length + // + + ByteCount = (ValidDataLength - StartingVbo < ByteCount) ? + ValidDataLength - StartingVbo : ByteCount; + + // + // Round up to a sector boundary, and remember that if we are + // reading extra bytes we will zero them out during completion. + // + + BytesToRead = (ByteCount + (SectorSize - 1)) + & ~(SectorSize - 1); + + // + // Just to help alleviate confusion. At this point: + // + // RequestedByteCount - is the number of bytes originally + // taken from the Irp, but constrained + // to filesize. + // + // ByteCount - is RequestedByteCount constrained to + // ValidDataLength. + // + // BytesToRead - is ByteCount rounded up to sector + // boundry. This is the number of bytes + // that we must physically read. + // + + // + // If this request is not properly aligned, or extending + // to a sector boundary would overflow the buffer, send it off + // on a special-case path. + // + + if ( (StartingVbo & (SectorSize - 1)) || + (BytesToRead > IrpSp->Parameters.Read.Length) ) { + + // + // If we can't wait, we must post this. + // + + if (!Wait) { + + try_return( PostIrp = TRUE ); + } + + // + // Do the physical read + // + + FatNonCachedNonAlignedRead( IrpContext, + Irp, + FcbOrDcb, + StartingVbo, + ByteCount ); + + // + // Set BytesToRead to ByteCount to satify the following ASSERT. + // + +#pragma prefast( suppress:28931, "needed for debug build" ) + BytesToRead = ByteCount; + + } else { + + // + // Perform the actual IO + // + + + if (FatNonCachedIo( IrpContext, + Irp, + FcbOrDcb, + StartingVbo, + BytesToRead, + ByteCount, + 0) == STATUS_PENDING) { + + + IrpContext->FatIoContext = NULL; + + Irp = NULL; + + try_return( Status = STATUS_PENDING ); + } + } + + // + // If the call didn't succeed, raise the error status + // + + if (!NT_SUCCESS( Status = Irp->IoStatus.Status )) { + + NT_ASSERT( KeGetCurrentThread() != Vcb->VerifyThread || Status != STATUS_VERIFY_REQUIRED ); + FatNormalizeAndRaiseStatus( IrpContext, Status ); + + } else { + + // + // Else set the Irp information field to reflect the + // entire desired read. + // + + NT_ASSERT( Irp->IoStatus.Information == BytesToRead ); + + Irp->IoStatus.Information = RequestedByteCount; + } + + // + // The transfer is complete. + // + + try_return( Status ); + + } // if No Intermediate Buffering + + + // + // HANDLE CACHED CASE + // + + else { + + // + // We delay setting up the file cache until now, in case the + // caller never does any I/O to the file, and thus + // FileObject->PrivateCacheMap == NULL. + // + + if (FileObject->PrivateCacheMap == NULL) { + + DebugTrace(0, Dbg, "Initialize cache mapping.\n", 0); + + // + // Get the file allocation size, and if it is less than + // the file size, raise file corrupt error. + // + + if (FcbOrDcb->Header.AllocationSize.QuadPart == FCB_LOOKUP_ALLOCATIONSIZE_HINT) { + + FatLookupFileAllocationSize( IrpContext, FcbOrDcb ); + } + + if ( FileSize > FcbOrDcb->Header.AllocationSize.LowPart ) { + + FatPopUpFileCorrupt( IrpContext, FcbOrDcb ); + + FatRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); + } + + // + // Now initialize the cache map. + // + + FatInitializeCacheMap( FileObject, + (PCC_FILE_SIZES)&FcbOrDcb->Header.AllocationSize, + FALSE, + &FatData.CacheManagerCallbacks, + FcbOrDcb ); + + CcSetReadAheadGranularity( FileObject, READ_AHEAD_GRANULARITY ); + } + + + // + // DO A NORMAL CACHED READ, if the MDL bit is not set, + // + + DebugTrace(0, Dbg, "Cached read.\n", 0); + + if (!FlagOn(IrpContext->MinorFunction, IRP_MN_MDL)) { + + // + // Get hold of the user's buffer. + // + + SystemBuffer = FatMapUserBuffer( IrpContext, Irp ); + + // + // Now try to do the copy. + // + +#if (NTDDI_VERSION >= NTDDI_WIN8) + if (!CcCopyReadEx( FileObject, + &StartingByte, + ByteCount, + Wait, + SystemBuffer, + &Irp->IoStatus, + Irp->Tail.Overlay.Thread )) { +#else + if (!CcCopyRead( FileObject, + &StartingByte, + ByteCount, + Wait, + SystemBuffer, + &Irp->IoStatus )) { +#endif + + DebugTrace( 0, Dbg, "Cached Read could not wait\n", 0 ); + + try_return( PostIrp = TRUE ); + } + + Status = Irp->IoStatus.Status; + + NT_ASSERT( NT_SUCCESS( Status )); + + try_return( Status ); + } + + + // + // HANDLE A MDL READ + // + + else { + + DebugTrace(0, Dbg, "MDL read.\n", 0); + + NT_ASSERT( Wait ); + + CcMdlRead( FileObject, + &StartingByte, + ByteCount, + &Irp->MdlAddress, + &Irp->IoStatus ); + + Status = Irp->IoStatus.Status; + + NT_ASSERT( NT_SUCCESS( Status )); + + try_return( Status ); + } + } + } + + // + // These cases correspond to a system read directory file or + // ea file. + // + + if (( TypeOfOpen == DirectoryFile ) || ( TypeOfOpen == EaFile) + ) { + + ULONG SectorSize; + + +#if FASTFATDBG + if ( TypeOfOpen == DirectoryFile ) { + DebugTrace(0, Dbg, "Type of read is directoryfile\n", 0); + } else if ( TypeOfOpen == EaFile) { + DebugTrace(0, Dbg, "Type of read is eafile\n", 0); + } + +#endif + + // + // For the noncached case, assert that everything is sector + // alligned. + // + +#pragma prefast( suppress:28931, "needed for debug build" ) + SectorSize = (ULONG)Vcb->Bpb.BytesPerSector; + + // + // We make several assumptions about these two types of files. + // Make sure all of them are true. + // + + NT_ASSERT( NonCachedIo && PagingIo ); + NT_ASSERT( ((StartingVbo | ByteCount) & (SectorSize - 1)) == 0 ); + + + // + // These calls must allways be within the allocation size + // + + if (StartingVbo >= FcbOrDcb->Header.AllocationSize.LowPart) { + + DebugTrace( 0, Dbg, "PagingIo dirent started beyond EOF.\n", 0 ); + + Irp->IoStatus.Information = 0; + + try_return( Status = STATUS_SUCCESS ); + } + + if ( StartingVbo + ByteCount > FcbOrDcb->Header.AllocationSize.LowPart ) { + + DebugTrace( 0, Dbg, "PagingIo dirent extending beyond EOF.\n", 0 ); + ByteCount = FcbOrDcb->Header.AllocationSize.LowPart - StartingVbo; + } + + + // + // Perform the actual IO + // + + if (FatNonCachedIo( IrpContext, + Irp, + FcbOrDcb, + StartingVbo, + ByteCount, + ByteCount, + 0 ) == STATUS_PENDING) { + + IrpContext->FatIoContext = NULL; + + Irp = NULL; + + try_return( Status = STATUS_PENDING ); + } + + // + // If the call didn't succeed, raise the error status + // + + if (!NT_SUCCESS( Status = Irp->IoStatus.Status )) { + + NT_ASSERT( KeGetCurrentThread() != Vcb->VerifyThread || Status != STATUS_VERIFY_REQUIRED ); + FatNormalizeAndRaiseStatus( IrpContext, Status ); + + } else { + + NT_ASSERT( Irp->IoStatus.Information == ByteCount ); + } + + try_return( Status ); + } + + // + // This is the case of a user who openned a directory. No reading is + // allowed. + // + + if ( TypeOfOpen == UserDirectoryOpen ) { + + DebugTrace( 0, Dbg, "CommonRead -> STATUS_INVALID_PARAMETER\n", 0); + + try_return( Status = STATUS_INVALID_PARAMETER ); + } + + // + // If we get this far, something really serious is wrong. + // + + DebugDump("Illegal TypeOfOpen\n", 0, FcbOrDcb ); + +#pragma prefast( suppress:28159, "things are seriously wrong if we get here" ) + FatBugCheck( TypeOfOpen, (ULONG_PTR) FcbOrDcb, 0 ); + + try_exit: NOTHING; + + // + // If the request was not posted and there's an Irp, deal with it. + // + + if ( Irp ) { + + if ( !PostIrp ) { + + ULONG ActualBytesRead; + + DebugTrace( 0, Dbg, "Completing request with status = %08lx\n", + Status); + + DebugTrace( 0, Dbg, " Information = %08lx\n", + Irp->IoStatus.Information); + + // + // Record the total number of bytes actually read + // + + ActualBytesRead = (ULONG)Irp->IoStatus.Information; + + // + // If the file was opened for Synchronous IO, update the current + // file position. + // + + if (SynchronousIo && !PagingIo) { + + FileObject->CurrentByteOffset.LowPart = + StartingVbo + (NT_ERROR( Status ) ? 0 : ActualBytesRead); + } + + // + // If this was not PagingIo, mark that the last access + // time on the dirent needs to be updated on close. + // + + if (NT_SUCCESS(Status) && !PagingIo) { + + SetFlag( FileObject->Flags, FO_FILE_FAST_IO_READ ); + } + + } else { + + DebugTrace( 0, Dbg, "Passing request to Fsp\n", 0 ); + + if (!OplockPostIrp) { + + Status = FatFsdPostRequest( IrpContext, Irp ); + } + } + } + + } finally { + + DebugUnwind( FatCommonRead ); + + // + // If the FcbOrDcb has been acquired, release it. + // + + if (FcbOrDcbAcquired && Irp) { + + if ( PagingIo ) { + + ExReleaseResourceLite( FcbOrDcb->Header.PagingIoResource ); + + } else { + + FatReleaseFcb( NULL, FcbOrDcb ); + } + } + + // + // Complete the request if we didn't post it and no exception + // + // Note that FatCompleteRequest does the right thing if either + // IrpContext or Irp are NULL + // + + if (!PostIrp) { + + // + // If we had a stack io context, we have to make sure the contents + // are cleaned up before we leave. + // + // At present with zero mdls, this will only really happen on exceptional + // termination where we failed to dispatch the IO. Cleanup of zero mdls + // normally occurs during completion, but when we bail we must make sure + // the cleanup occurs here or the fatiocontext will go out of scope. + // + // If the operation was posted, cleanup occured there. + // + + if (FlagOn(IrpContext->Flags, IRP_CONTEXT_STACK_IO_CONTEXT)) { + + if (IrpContext->FatIoContext->ZeroMdl) { + IoFreeMdl( IrpContext->FatIoContext->ZeroMdl ); + } + + ClearFlag(IrpContext->Flags, IRP_CONTEXT_STACK_IO_CONTEXT); + IrpContext->FatIoContext = NULL; + } + + if (!AbnormalTermination()) { + + FatCompleteRequest( IrpContext, Irp, Status ); + } + } + + DebugTrace(-1, Dbg, "CommonRead -> %08lx\n", Status ); + } + + return Status; +} + +// +// Local support routine +// + +VOID +FatOverflowPagingFileRead ( + IN PVOID Context, + IN PKEVENT Event + ) + +/*++ + +Routine Description: + + The routine simply call FatPagingFileIo. It is invoked in cases when + there was not enough stack space to perform the pagefault in the + original thread. It is also responsible for freeing the packet pool. + +Arguments: + + Irp - Supplies the Irp being processed + + Fcb - Supplies the paging file Fcb, since we have it handy. + +Return Value: + + VOID + +--*/ + +{ + PPAGING_FILE_OVERFLOW_PACKET Packet = Context; + + FatPagingFileIo( Packet->Irp, Packet->Fcb ); + + // + // Set the stack overflow item's event to tell the original + // thread that we're done. + // + + KeSetEvent( Event, 0, FALSE ); + + return; +} + + + diff --git a/filesys/fastfat/resrcsup.c b/filesys/fastfat/resrcsup.c new file mode 100644 index 000000000..528d6300f --- /dev/null +++ b/filesys/fastfat/resrcsup.c @@ -0,0 +1,953 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + ResrcSup.c + +Abstract: + + This module implements the Fat Resource acquisition routines + + +--*/ + +#include "FatProcs.h" + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, FatAcquireFcbForLazyWrite) +#pragma alloc_text(PAGE, FatAcquireFcbForReadAhead) +#pragma alloc_text(PAGE, FatAcquireExclusiveFcb) +#pragma alloc_text(PAGE, FatAcquireSharedFcb) +#pragma alloc_text(PAGE, FatAcquireSharedFcbWaitForEx) +#pragma alloc_text(PAGE, FatAcquireExclusiveVcb_Real) +#pragma alloc_text(PAGE, FatAcquireSharedVcb) +#pragma alloc_text(PAGE, FatNoOpAcquire) +#pragma alloc_text(PAGE, FatNoOpRelease) +#pragma alloc_text(PAGE, FatReleaseFcbFromLazyWrite) +#pragma alloc_text(PAGE, FatReleaseFcbFromReadAhead) +#pragma alloc_text(PAGE, FatAcquireForCcFlush) +#pragma alloc_text(PAGE, FatReleaseForCcFlush) +#pragma alloc_text(PAGE, FatFilterCallbackAcquireForCreateSection) +#endif + +_Requires_lock_held_(_Global_critical_region_) +_When_(return != FALSE && NoOpCheck != FALSE, _Acquires_exclusive_lock_(Vcb->Resource)) +FINISHED +FatAcquireExclusiveVcb_Real ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN BOOLEAN NoOpCheck + ) + +/*++ + +Routine Description: + + This routine acquires exclusive access to the Vcb. + + After we acquire the resource check to see if this operation is legal. + If it isn't (ie. we get an exception), release the resource. + +Arguments: + + Vcb - Supplies the Vcb to acquire + + NoOpCheck - if TRUE then don't do any verification of the request/volume state. + +Return Value: + + FINISHED - TRUE if we have the resource and FALSE if we needed to block + for the resource but Wait is FALSE. + +--*/ + +{ + PAGED_CODE(); + +#pragma prefast( suppress: 28137, "prefast wants the wait to be a constant, but that isn't possible for the way fastfat is designed" ) + if (ExAcquireResourceExclusiveLite( &Vcb->Resource, BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT))) { + + if (!NoOpCheck) { + + try { + + FatVerifyOperationIsLegal( IrpContext ); + + } finally { + + if ( AbnormalTermination() ) { + + FatReleaseVcb( IrpContext, Vcb ); + } + } + } + + return TRUE; + } + + return FALSE; +} + +_Requires_lock_held_(_Global_critical_region_) +_When_(return != 0, _Acquires_shared_lock_(Vcb->Resource)) +FINISHED +FatAcquireSharedVcb ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb + ) + +/*++ + +Routine Description: + + This routine acquires shared access to the Vcb. + + After we acquire the resource check to see if this operation is legal. + If it isn't (ie. we get an exception), release the resource. + +Arguments: + + Vcb - Supplies the Vcb to acquire + +Return Value: + + FINISHED - TRUE if we have the resource and FALSE if we needed to block + for the resource but Wait is FALSE. + +--*/ + +{ + PAGED_CODE(); + + if (ExAcquireResourceSharedLite( &Vcb->Resource, + BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT))) { + + try { + + FatVerifyOperationIsLegal( IrpContext ); + + } finally { + + if ( AbnormalTermination() ) { + + FatReleaseVcb( IrpContext, Vcb ); + } + } + + return TRUE; + } + + return FALSE; +} + +_Requires_lock_held_(_Global_critical_region_) +_Acquires_exclusive_lock_(*Fcb->Header.Resource) +FINISHED +FatAcquireExclusiveFcb ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb + ) + +/*++ + +Routine Description: + + This routine acquires exclusive access to the Fcb. + + After we acquire the resource check to see if this operation is legal. + If it isn't (ie. we get an exception), release the resource. + +Arguments: + + Fcb - Supplies the Fcb to acquire + +Return Value: + + FINISHED - TRUE if we have the resource and FALSE if we needed to block + for the resource but Wait is FALSE. + +--*/ + +{ + PAGED_CODE(); + +RetryFcbExclusive: + +#pragma prefast( suppress: 28137, "prefast wants the wait to be a constant, but that isn't possible for the way fastfat is designed" ) + if (ExAcquireResourceExclusiveLite( Fcb->Header.Resource, BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT))) { + + // + // Check for anything other than a non-cached write if the + // async count is non-zero in the Fcb, or if others are waiting + // for the resource. Then wait for all outstanding I/O to finish, + // drop the resource, and wait again. + // + + if ((Fcb->NonPaged->OutstandingAsyncWrites != 0) && + ((IrpContext->MajorFunction != IRP_MJ_WRITE) || + !FlagOn(IrpContext->OriginatingIrp->Flags, IRP_NOCACHE) || + (ExGetSharedWaiterCount(Fcb->Header.Resource) != 0) || + (ExGetExclusiveWaiterCount(Fcb->Header.Resource) != 0))) { + + KeWaitForSingleObject( Fcb->NonPaged->OutstandingAsyncEvent, + Executive, + KernelMode, + FALSE, + (PLARGE_INTEGER) NULL ); + + FatReleaseFcb( IrpContext, Fcb ); + + goto RetryFcbExclusive; + } + + try { + + FatVerifyOperationIsLegal( IrpContext ); + + } finally { + + if ( AbnormalTermination() ) { + + FatReleaseFcb( IrpContext, Fcb ); + } + } + + return TRUE; + } + + return FALSE; +} + + + _Requires_lock_held_(_Global_critical_region_) +_Acquires_shared_lock_(*Fcb->Header.Resource) +FINISHED +FatAcquireSharedFcb ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb + ) + +/*++ + +Routine Description: + + This routine acquires shared access to the Fcb. + + After we acquire the resource check to see if this operation is legal. + If it isn't (ie. we get an exception), release the resource. + +Arguments: + + Fcb - Supplies the Fcb to acquire + +Return Value: + + FINISHED - TRUE if we have the resource and FALSE if we needed to block + for the resource but Wait is FALSE. + +--*/ + +{ + PAGED_CODE(); + +RetryFcbShared: + + if (ExAcquireResourceSharedLite( Fcb->Header.Resource, BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT))) { + + // + // Check for anything other than a non-cached write if the + // async count is non-zero in the Fcb, or if others are waiting + // for the resource. Then wait for all outstanding I/O to finish, + // drop the resource, and wait again. + // + + if ((Fcb->NonPaged->OutstandingAsyncWrites != 0) && + ((IrpContext->MajorFunction != IRP_MJ_WRITE) || + !FlagOn(IrpContext->OriginatingIrp->Flags, IRP_NOCACHE) || + (ExGetSharedWaiterCount(Fcb->Header.Resource) != 0) || + (ExGetExclusiveWaiterCount(Fcb->Header.Resource) != 0))) { + + KeWaitForSingleObject( Fcb->NonPaged->OutstandingAsyncEvent, + Executive, + KernelMode, + FALSE, + (PLARGE_INTEGER) NULL ); + + FatReleaseFcb( IrpContext, Fcb ); + + goto RetryFcbShared; + } + + try { + + FatVerifyOperationIsLegal( IrpContext ); + + } finally { + + if ( AbnormalTermination() ) { + + FatReleaseFcb( IrpContext, Fcb ); + } + } + + + return TRUE; + + } else { + + return FALSE; + } +} + + +_Requires_lock_held_(_Global_critical_region_) +_When_(return != 0, _Acquires_shared_lock_(*Fcb->Header.Resource)) +FINISHED +FatAcquireSharedFcbWaitForEx ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb + ) + +/*++ + +Routine Description: + + This routine acquires shared access to the Fcb, waiting first for any + exclusive accessors to get the Fcb first. + + After we acquire the resource check to see if this operation is legal. + If it isn't (ie. we get an exception), release the resource. + +Arguments: + + Fcb - Supplies the Fcb to acquire + +Return Value: + + FINISHED - TRUE if we have the resource and FALSE if we needed to block + for the resource but Wait is FALSE. + +--*/ + +{ + PAGED_CODE(); + + NT_ASSERT( FlagOn(IrpContext->OriginatingIrp->Flags, IRP_NOCACHE) ); + NT_ASSERT( !FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) ); + +RetryFcbSharedWaitEx: + + if (ExAcquireSharedWaitForExclusive( Fcb->Header.Resource, FALSE )) { + + // + // Check for anything other than a non-cached write if the + // async count is non-zero in the Fcb. Then wait for all + // outstanding I/O to finish, drop the resource, and wait again. + // + + if ((Fcb->NonPaged->OutstandingAsyncWrites != 0) && + (IrpContext->MajorFunction != IRP_MJ_WRITE)) { + + KeWaitForSingleObject( Fcb->NonPaged->OutstandingAsyncEvent, + Executive, + KernelMode, + FALSE, + (PLARGE_INTEGER) NULL ); + + FatReleaseFcb( IrpContext, Fcb ); + + goto RetryFcbSharedWaitEx; + } + + try { + + FatVerifyOperationIsLegal( IrpContext ); + + } finally { + + if ( AbnormalTermination() ) { + + FatReleaseFcb( IrpContext, Fcb ); + } + } + + + return TRUE; + + } else { + + return FALSE; + } +} + + +_Requires_lock_held_(_Global_critical_region_) +BOOLEAN +FatAcquireFcbForLazyWrite ( + IN PVOID 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 prior to its + performing lazy writes to the file. + +Arguments: + + Fcb - The Fcb which was specified as a context parameter for this + routine. + + Wait - TRUE if the caller is willing to block. + +Return Value: + + FALSE - if Wait was specified as FALSE and blocking would have + been required. The Fcb is not acquired. + + TRUE - if the Fcb has been acquired + +--*/ + +{ + PAGED_CODE(); + + // + // Check here for the EA File. It turns out we need the normal + // resource shared in this case. Otherwise we take the paging + // I/O resource shared. + // + + // + // Note that we do not need to disable APC delivery to guard + // against a rogue user issuing a suspend APC. That is because + // it is guaranteed that the caller is either in the system context, + // to which a user cannot deliver a suspend APC, or the caller has + // already disabled kernel APC delivery before calling. This is true + // for all the other pre-acquire routines as well. + // + + if (!ExAcquireResourceSharedLite( Fcb == ((PFCB)Fcb)->Vcb->EaFcb ? + ((PFCB)Fcb)->Header.Resource : + ((PFCB)Fcb)->Header.PagingIoResource, + Wait )) { + + return FALSE; + } + + // + // We assume the Lazy Writer only acquires this Fcb once. + // Therefore, it should be guaranteed that this flag is currently + // clear (the ASSERT), and then we will set this flag, to insure + // that the Lazy Writer will never try to advance Valid Data, and + // also not deadlock by trying to get the Fcb exclusive. + // + + + NT_ASSERT( NodeType(((PFCB)Fcb)) == FAT_NTC_FCB ); + NT_ASSERT( ((PFCB)Fcb)->Specific.Fcb.LazyWriteThread == NULL ); + + ((PFCB)Fcb)->Specific.Fcb.LazyWriteThread = PsGetCurrentThread(); + + NT_ASSERT( NULL != PsGetCurrentThread() ); + + if (NULL == FatData.LazyWriteThread) { + + FatData.LazyWriteThread = PsGetCurrentThread(); + } + + // + // This is a kludge because Cc is really the top level. When it + // enters the file system, we will think it is a resursive call + // and complete the request with hard errors or verify. It will + // then have to deal with them, somehow.... + // + + NT_ASSERT(IoGetTopLevelIrp() == NULL); + + IoSetTopLevelIrp((PIRP)FSRTL_CACHE_TOP_LEVEL_IRP); + + return TRUE; +} + + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatReleaseFcbFromLazyWrite ( + IN PVOID Fcb + ) + +/*++ + +Routine Description: + + The address of this routine is specified when creating a CacheMap for + a file. It is subsequently called by the Lazy Writer after its + performing lazy writes to the file. + +Arguments: + + Fcb - The Fcb which was specified as a context parameter for this + routine. + +Return Value: + + None + +--*/ + +{ + PAGED_CODE(); + + // + // Assert that this really is an fcb and that this thread really owns + // the lazy writer mark in the fcb. + // + + NT_ASSERT( NodeType(((PFCB)Fcb)) == FAT_NTC_FCB ); + NT_ASSERT( NULL != PsGetCurrentThread() ); + NT_ASSERT( ((PFCB)Fcb)->Specific.Fcb.LazyWriteThread == PsGetCurrentThread() ); + + // + // Release the lazy writer mark. + // + + ((PFCB)Fcb)->Specific.Fcb.LazyWriteThread = NULL; + + // + // Check here for the EA File. It turns out we needed the normal + // resource shared in this case. Otherwise it was the PagingIoResource. + // + + ExReleaseResourceLite( Fcb == ((PFCB)Fcb)->Vcb->EaFcb ? + ((PFCB)Fcb)->Header.Resource : + ((PFCB)Fcb)->Header.PagingIoResource ); + + // + // Clear the kludge at this point. + // + + NT_ASSERT(IoGetTopLevelIrp() == (PIRP)FSRTL_CACHE_TOP_LEVEL_IRP); + + IoSetTopLevelIrp( NULL ); + + return; +} + + +_Requires_lock_held_(_Global_critical_region_) +BOOLEAN +FatAcquireFcbForReadAhead ( + IN PVOID 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 prior to its + performing read ahead to the file. + +Arguments: + + Fcb - The Fcb which was specified as a context parameter for this + routine. + + Wait - TRUE if the caller is willing to block. + +Return Value: + + FALSE - if Wait was specified as FALSE and blocking would have + been required. The Fcb is not acquired. + + TRUE - if the Fcb has been acquired + +--*/ + +{ + PAGED_CODE(); + + // + // We acquire the normal file resource shared here to synchronize + // correctly with purges. + // + + // + // Note that we do not need to disable APC delivery to guard + // against a rogue user issuing a suspend APC. That is because + // it is guaranteed that the caller is either in the system context, + // to which a user cannot deliver a suspend APC, or the caller has + // already disabled kernel APC delivery before calling. This is true + // for all the other pre-acquire routines as well. + // + + if (!ExAcquireResourceSharedLite( ((PFCB)Fcb)->Header.Resource, + Wait )) { + + return FALSE; + } + + // + // This is a kludge because Cc is really the top level. We it + // enters the file system, we will think it is a resursive call + // and complete the request with hard errors or verify. It will + // have to deal with them, somehow.... + // + + NT_ASSERT(IoGetTopLevelIrp() == NULL); + + IoSetTopLevelIrp((PIRP)FSRTL_CACHE_TOP_LEVEL_IRP); + + return TRUE; +} + + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatReleaseFcbFromReadAhead ( + IN PVOID Fcb + ) + +/*++ + +Routine Description: + + The address of this routine is specified when creating a CacheMap for + a file. It is subsequently called by the Lazy Writer after its + read ahead. + +Arguments: + + Fcb - The Fcb which was specified as a context parameter for this + routine. + +Return Value: + + None + +--*/ + +{ + PAGED_CODE(); + + // + // Clear the kludge at this point. + // + + NT_ASSERT(IoGetTopLevelIrp() == (PIRP)FSRTL_CACHE_TOP_LEVEL_IRP); + + IoSetTopLevelIrp( NULL ); + + ExReleaseResourceLite( ((PFCB)Fcb)->Header.Resource ); + + return; +} + + +_Function_class_(FAST_IO_ACQUIRE_FOR_CCFLUSH) +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatAcquireForCcFlush ( + IN PFILE_OBJECT FileObject, + IN PDEVICE_OBJECT DeviceObject + ) +{ + PFCB Fcb; + PCCB Ccb; + PVCB Vcb; + PFSRTL_COMMON_FCB_HEADER Header; + TYPE_OF_OPEN Type; + + PAGED_CODE(); + UNREFERENCED_PARAMETER( DeviceObject ); + + // + // Once again, the hack for making this look like + // a recursive call if needed. We cannot let ourselves + // verify under something that has resources held. + // + // This value is good. We should never try to acquire + // the file this way underneath of the cache. + // + + NT_ASSERT( IoGetTopLevelIrp() != (PIRP)FSRTL_CACHE_TOP_LEVEL_IRP ); + + if (IoGetTopLevelIrp() == NULL) { + + IoSetTopLevelIrp((PIRP)FSRTL_CACHE_TOP_LEVEL_IRP); + } + + // + // Time for some exposition. + // + // Lockorder for FAT is main->bcb->pagingio. Invert this at your obvious peril. + // The default logic for AcquireForCcFlush breaks this since in writethrough + // unpinrepinned we will grab the bcb then Mm will use the callback (which + // orders us with respect to the MmCollidedFlushEvent) to help us. If for + // directories/ea we then grab the main we are out of order. + // + // Fortunately, we do not need main. We only need paging - just look at the write + // path. This is basic pre-acquisition. + // + // Regular files require both resources, and are safe since we never pin them. + // + + // + // Note that we do not need to disable APC delivery to guard + // against a rogue user issuing a suspend APC. That is because + // it is guaranteed that the caller is either in the system context, + // to which a user cannot deliver a suspend APC, or the caller has + // already disabled kernel APC delivery before calling. This is true + // for all the other pre-acquire routines as well. + // + + Type = FatDecodeFileObject( FileObject, &Vcb, &Fcb, &Ccb ); + Header = (PFSRTL_COMMON_FCB_HEADER) FileObject->FsContext; + + if (Type < DirectoryFile) { + + if (Header->Resource) { + + if (!ExIsResourceAcquiredSharedLite( Header->Resource )) { + + ExAcquireResourceExclusiveLite( Header->Resource, TRUE ); + + } else { + + ExAcquireResourceSharedLite( Header->Resource, TRUE ); + } + } + } + + if (Header->PagingIoResource) { + + ExAcquireResourceSharedLite( Header->PagingIoResource, TRUE ); + } + + return STATUS_SUCCESS; +} + + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatReleaseForCcFlush ( + IN PFILE_OBJECT FileObject, + IN PDEVICE_OBJECT DeviceObject + ) +{ + PFCB Fcb; + PCCB Ccb; + PVCB Vcb; + PFSRTL_COMMON_FCB_HEADER Header; + TYPE_OF_OPEN Type; + + PAGED_CODE(); + UNREFERENCED_PARAMETER( DeviceObject ); + + // + // Clear up our hint. + // + + if (IoGetTopLevelIrp() == (PIRP)FSRTL_CACHE_TOP_LEVEL_IRP) { + + IoSetTopLevelIrp( NULL ); + } + + Type = FatDecodeFileObject( FileObject, &Vcb, &Fcb, &Ccb ); + Header = (PFSRTL_COMMON_FCB_HEADER) FileObject->FsContext; + + if (Type < DirectoryFile) { + + if (Header->Resource) { + + ExReleaseResourceLite( Header->Resource ); + } + } + + if (Header->PagingIoResource) { + + ExReleaseResourceLite( Header->PagingIoResource ); + } + + return STATUS_SUCCESS; +} + + +BOOLEAN +FatNoOpAcquire ( + IN PVOID Fcb, + IN BOOLEAN Wait + ) + +/*++ + +Routine Description: + + This routine does nothing. + +Arguments: + + Fcb - The Fcb/Dcb/Vcb which was specified as a context parameter for this + routine. + + Wait - TRUE if the caller is willing to block. + +Return Value: + + TRUE + +--*/ + +{ + UNREFERENCED_PARAMETER( Fcb ); + UNREFERENCED_PARAMETER( Wait ); + + PAGED_CODE(); + + // + // This is a kludge because Cc is really the top level. We it + // enters the file system, we will think it is a resursive call + // and complete the request with hard errors or verify. It will + // have to deal with them, somehow.... + // + + NT_ASSERT(IoGetTopLevelIrp() == NULL); + + IoSetTopLevelIrp((PIRP)FSRTL_CACHE_TOP_LEVEL_IRP); + + return TRUE; +} + + +VOID +FatNoOpRelease ( + IN PVOID Fcb + ) + +/*++ + +Routine Description: + + This routine does nothing. + +Arguments: + + Fcb - The Fcb/Dcb/Vcb which was specified as a context parameter for this + routine. + +Return Value: + + None + +--*/ + +{ + PAGED_CODE(); + + // + // Clear the kludge at this point. + // + + NT_ASSERT(IoGetTopLevelIrp() == (PIRP)FSRTL_CACHE_TOP_LEVEL_IRP); + + IoSetTopLevelIrp( NULL ); + + UNREFERENCED_PARAMETER( Fcb ); + + return; +} + + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatFilterCallbackAcquireForCreateSection ( + IN PFS_FILTER_CALLBACK_DATA CallbackData, + OUT PVOID *CompletionContext + ) + +/*++ + +Routine Description: + + This is the callback routine for MM to use to acquire the file exclusively. + + NOTE: This routine expects the default FSRTL routine to be used to release + the resource. If this routine is ever changed to acquire something + other than main, a corresponding release routine will be required. + +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 whether there + are any writers to this file. Note that main is acquired, so new handles cannot be opened. + +--*/ + +{ + PFCB Fcb; + + PAGED_CODE(); + + NT_ASSERT( CallbackData->Operation == FS_FILTER_ACQUIRE_FOR_SECTION_SYNCHRONIZATION ); + NT_ASSERT( CallbackData->SizeOfFsFilterCallbackData == sizeof(FS_FILTER_CALLBACK_DATA) ); + + // + // Grab the Fcb from the callback data file object. + // + + Fcb = CallbackData->FileObject->FsContext; + + // + // Take main exclusive. + // + + // + // Note that we do not need to disable APC delivery to guard + // against a rogue user issuing a suspend APC. That is because + // it is guaranteed that the caller is either in the system context, + // to which a user cannot deliver a suspend APC, or the caller has + // already disabled kernel APC delivery before calling. This is true + // for all the other pre-acquire routines as well. + // + + if (Fcb->Header.Resource) { + + ExAcquireResourceExclusiveLite( Fcb->Header.Resource, TRUE ); + } + + // + // Return the appropriate status based on the type of synchronization and whether anyone + // has write access to this file. + // + + if (CallbackData->Parameters.AcquireForSectionSynchronization.SyncType != SyncTypeCreateSection) { + + return STATUS_FSFILTER_OP_COMPLETED_SUCCESSFULLY; + + } else if (Fcb->ShareAccess.Writers == 0) { + + return STATUS_FILE_LOCKED_WITH_ONLY_READERS; + + } else { + + return STATUS_FILE_LOCKED_WITH_WRITERS; + } + + UNREFERENCED_PARAMETER( CompletionContext ); +} + diff --git a/filesys/fastfat/shutdown.c b/filesys/fastfat/shutdown.c new file mode 100644 index 000000000..d661f022a --- /dev/null +++ b/filesys/fastfat/shutdown.c @@ -0,0 +1,313 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + Shutdown.c + +Abstract: + + This module implements the file system shutdown routine for Fat + + +--*/ + +#include "FatProcs.h" + +// +// Local debug trace level +// + +#define Dbg (DEBUG_TRACE_SHUTDOWN) + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, FatCommonShutdown) +#pragma alloc_text(PAGE, FatFsdShutdown) +#endif + + +_Function_class_(IRP_MJ_SHUTDOWN) +_Function_class_(DRIVER_DISPATCH) +NTSTATUS +FatFsdShutdown ( + _In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + _Inout_ PIRP Irp + ) + +/*++ + +Routine Description: + + This routine implements the FSD part of shutdown. Note that Shutdown will + never be done asynchronously so we will never need the Fsp counterpart + to shutdown. + + This is the shutdown routine for the Fat file system device driver. + This routine locks the global file system lock and then syncs all the + mounted volumes. + +Arguments: + + VolumeDeviceObject - Supplies the volume device object where the + file exists + + Irp - Supplies the Irp being processed + +Return Value: + + NTSTATUS - Always STATUS_SUCCESS + +--*/ + +{ + NTSTATUS Status; + PIRP_CONTEXT IrpContext = NULL; + + BOOLEAN TopLevel; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatFsdShutdown\n", 0); + + // + // Call the common shutdown routine. + // + + FsRtlEnterFileSystem(); + + TopLevel = FatIsIrpTopLevel( Irp ); + + try { + + IrpContext = FatCreateIrpContext( Irp, TRUE ); + + Status = FatCommonShutdown( IrpContext, Irp ); + + } except(FatExceptionFilter( IrpContext, GetExceptionInformation() )) { + + // + // We had some trouble trying to perform the requested + // operation, so we'll abort the I/O request with + // the error status that we get back from the + // execption code + // + + Status = FatProcessException( IrpContext, Irp, GetExceptionCode() ); + } + + if (TopLevel) { IoSetTopLevelIrp( NULL ); } + + FsRtlExitFileSystem(); + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "FatFsdShutdown -> %08lx\n", Status); + + UNREFERENCED_PARAMETER( VolumeDeviceObject ); + + return Status; +} + + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatCommonShutdown ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the common routine for shutdown called by both the fsd and + fsp threads. + +Arguments: + + Irp - Supplies the Irp being processed + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + KEVENT Event; + PLIST_ENTRY Links; + PVCB Vcb; + PIRP NewIrp; + IO_STATUS_BLOCK Iosb; + BOOLEAN VcbDeleted; + + PAGED_CODE(); + + // + // Make sure we don't get any pop-ups, and write everything through. + // + + SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_POPUPS | + IRP_CONTEXT_FLAG_WRITE_THROUGH); + + // + // Initialize an event for doing calls down to + // our target device objects. + // + + KeInitializeEvent( &Event, NotificationEvent, FALSE ); + + // + // Indicate that shutdown has started. This is used in FatFspClose. + // + + FatData.ShutdownStarted = TRUE; + + // + // Get everyone else out of the way + // + + NT_ASSERT( FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) ); + +#pragma prefast( push ) +#pragma prefast( disable: 28137, "prefast wants the wait to be a constant, but that isn't possible for the way fastfat is designed" ) +#pragma prefast( disable: 28193, "this will always wait" ) + + (VOID) FatAcquireExclusiveGlobal( IrpContext ); + +#pragma prefast( pop ) + + try { + + // + // For every volume that is mounted we will flush the + // volume and then shutdown the target device objects. + // + + Links = FatData.VcbQueue.Flink; + while (Links != &FatData.VcbQueue) { + + Vcb = CONTAINING_RECORD(Links, VCB, VcbLinks); + + 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_FLAG_SHUTDOWN) || + (Vcb->VcbCondition != VcbGood) ) { + + continue; + } + + FatAcquireExclusiveVolume( IrpContext, Vcb ); + + try { + + (VOID)FatFlushVolume( IrpContext, Vcb, Flush ); + + // + // The volume is now clean, note it. We purge the + // volume file cache map before marking the volume + // clean incase there is a stale Bpb in the cache. + // + + if (!FlagOn(Vcb->VcbState, VCB_STATE_FLAG_MOUNTED_DIRTY)) { + + CcPurgeCacheSection( &Vcb->SectionObjectPointers, + NULL, + 0, + FALSE ); + + FatMarkVolume( IrpContext, Vcb, VolumeClean ); + } + + } except( EXCEPTION_EXECUTE_HANDLER ) { + + FatResetExceptionState( IrpContext ); + } + + // + // Sometimes we take an excepion while flushing the volume, such + // as when autoconv has converted the volume and is rebooting. + // Even in that case we want to send the shutdown irp to the + // target device so it can know to flush its cache, if it has one. + // + + try { + + NewIrp = IoBuildSynchronousFsdRequest( IRP_MJ_SHUTDOWN, + Vcb->TargetDeviceObject, + NULL, + 0, + NULL, + &Event, + &Iosb ); + + if (NewIrp != NULL) { + + if (NT_SUCCESS(IoCallDriver( Vcb->TargetDeviceObject, NewIrp ))) { + + (VOID) KeWaitForSingleObject( &Event, + Executive, + KernelMode, + FALSE, + NULL ); + + KeClearEvent( &Event ); + } + } + + } except( EXCEPTION_EXECUTE_HANDLER ) { + + FatResetExceptionState( IrpContext ); + } + + SetFlag( Vcb->VcbState, VCB_STATE_FLAG_SHUTDOWN ); + + // + // Attempt to punch the volume down. + // + + VcbDeleted = FatCheckForDismount( IrpContext, + Vcb, + FALSE ); + + if (!VcbDeleted) { + + FatReleaseVolume( IrpContext, Vcb ); + } + } + + } finally { + + + FatReleaseGlobal( IrpContext ); + + // + // Unregister the file system. + // + + IoUnregisterFileSystem( FatDiskFileSystemDeviceObject); + IoUnregisterFileSystem( FatCdromFileSystemDeviceObject); + IoDeleteDevice( FatDiskFileSystemDeviceObject); + IoDeleteDevice( FatCdromFileSystemDeviceObject); + + FatCompleteRequest( IrpContext, Irp, STATUS_SUCCESS ); + } + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "FatFsdShutdown -> STATUS_SUCCESS\n", 0); + + return STATUS_SUCCESS; +} + diff --git a/filesys/fastfat/splaysup.c b/filesys/fastfat/splaysup.c new file mode 100644 index 000000000..effa225df --- /dev/null +++ b/filesys/fastfat/splaysup.c @@ -0,0 +1,486 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + PrefxSup.c + +Abstract: + + This module implements the Fat Name lookup Suport routines + + +--*/ + +#include "FatProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (FAT_BUG_CHECK_SPLAYSUP) + +// +// The debug trace level for this module +// + +#define Dbg (DEBUG_TRACE_SPLAYSUP) + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, FatInsertName) +#pragma alloc_text(PAGE, FatRemoveNames) +#pragma alloc_text(PAGE, FatFindFcb) +#pragma alloc_text(PAGE, FatCompareNames) +#endif + + +VOID +FatInsertName ( + IN PIRP_CONTEXT IrpContext, + IN PRTL_SPLAY_LINKS *RootNode, + IN PFILE_NAME_NODE Name + ) + +/*++ + +Routine Description: + + This routine will insert a name in the splay tree pointed to + by RootNode. + + The name must not already exist in the splay tree. + +Arguments: + + RootNode - Supplies a pointer to the table. + + Name - Contains the New name to enter. + +Return Value: + + None. + +--*/ + +{ + COMPARISON Comparison; + PFILE_NAME_NODE Node; + + PAGED_CODE(); + + RtlInitializeSplayLinks(&Name->Links); + +Restart: + + // + // If we are the first entry in the tree, just become the root. + // + + if (*RootNode == NULL) { + + *RootNode = &Name->Links; + + return; + } + + Node = CONTAINING_RECORD( *RootNode, FILE_NAME_NODE, Links ); + + while (TRUE) { + + // + // Compare the prefix in the tree with the prefix we want + // to insert. Note that Oem here doesn't mean anything. + // + + Comparison = CompareNames(&Node->Name.Oem, &Name->Name.Oem); + + // + // We should never find the name in the table already. + // + + if (Comparison == IsEqual) { + + // + // Almost. If the removable media was taken to another machine and + // back, and we have something like: + // + // Old: abcdef~1 / abcdefxyz + // New: abcdef~1 / abcdefxyzxyz + // + // but a handle was kept open to abcdefxyz so we couldn't purge + // away the Fcb in the verify path ... opening abcdefxyzxyz will + // try to insert a duplicate shortname. Bang! + // + // Invalidate it and the horse it came in on. This new one wins. + // The old one is gone. Only if the old one is in normal state + // do we really have a problem. + // + + if (Node->Fcb->FcbState == FcbGood) { + +#pragma prefast( suppress:28159, "things are seriously wrong if we get here" ) + FatBugCheck( (ULONG_PTR)*RootNode, (ULONG_PTR)Name, (ULONG_PTR)Node ); + } + + // + // Note, once we zap the prefix links we need to restart our walk + // of the tree. Note that we aren't properly synchronized to + // recursively mark bad. + // + + FatMarkFcbCondition( IrpContext, Node->Fcb, FcbBad, FALSE ); + FatRemoveNames( IrpContext, Node->Fcb ); + + goto Restart; + } + + // + // If the tree prefix is greater than the new prefix then + // we go down the left subtree + // + + if (Comparison == IsGreaterThan) { + + // + // 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, &Name->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), + FILE_NAME_NODE, + Links ); + } + + } else { + + // + // The tree prefix is either less than or a proper prefix + // of the new string. We treat both cases a 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, &Name->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), + FILE_NAME_NODE, + Links ); + } + + } + } + + return; +} + +VOID +FatRemoveNames ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb + ) + +/*++ + +Routine Description: + + This routine will remove the short name and any long names associated + with the files from their repsective splay tree. + +Arguments: + + Name - Supplies the Fcb to process. + +Return Value: + + None. + +--*/ + +{ + PDCB Parent; + PRTL_SPLAY_LINKS NewRoot; + + PAGED_CODE(); + UNREFERENCED_PARAMETER( IrpContext ); + + Parent = Fcb->ParentDcb; + + // + // We used to assert this condition, but it really isn't good. If + // someone rapidly renames a directory multiple times and we can't + // flush the lower fcbs fast enough (that didn't go away synch.) + // well, well hit some of them again. + // + // NT_ASSERT( FlagOn( Fcb->FcbState, FCB_STATE_NAMES_IN_SPLAY_TREE )); + // + + if (FlagOn( Fcb->FcbState, FCB_STATE_NAMES_IN_SPLAY_TREE )) { + + // + // Delete the node short name. + // + + NewRoot = RtlDelete(&Fcb->ShortName.Links); + + Parent->Specific.Dcb.RootOemNode = NewRoot; + + // + // Now check for the presence of long name and delete it. + // + + if (FlagOn( Fcb->FcbState, FCB_STATE_HAS_OEM_LONG_NAME )) { + + NewRoot = RtlDelete(&Fcb->LongName.Oem.Links); + + Parent->Specific.Dcb.RootOemNode = NewRoot; + + RtlFreeOemString( &Fcb->LongName.Oem.Name.Oem ); + + ClearFlag( Fcb->FcbState, FCB_STATE_HAS_OEM_LONG_NAME ); + } + + if (FlagOn( Fcb->FcbState, FCB_STATE_HAS_UNICODE_LONG_NAME )) { + + NewRoot = RtlDelete(&Fcb->LongName.Unicode.Links); + + Parent->Specific.Dcb.RootUnicodeNode = NewRoot; + + RtlFreeUnicodeString( &Fcb->LongName.Unicode.Name.Unicode ); + + ClearFlag( Fcb->FcbState, FCB_STATE_HAS_UNICODE_LONG_NAME ); + } + + ClearFlag( Fcb->FcbState, FCB_STATE_NAMES_IN_SPLAY_TREE ); + } + + return; +} + + +PFCB +FatFindFcb ( + IN PIRP_CONTEXT IrpContext, + IN OUT PRTL_SPLAY_LINKS *RootNode, + IN PSTRING Name, + OUT PBOOLEAN FileNameDos OPTIONAL + ) + +/*++ + +Routine Description: + + This routine searches either the Oem or Unicode splay tree looking + for an Fcb with the specified name. In the case the Fcb is found, + rebalance the tree. + +Arguments: + + RootNode - Supplies the parent to search. + + Name - If present, search the Oem tree. + + UnicodeName - If present, search the Unicode tree. + +Return Value: + + PFCB - The Fcb, or NULL if none was found. + +--*/ + +{ + COMPARISON Comparison; + PFILE_NAME_NODE Node; + PRTL_SPLAY_LINKS Links; + + PAGED_CODE(); + UNREFERENCED_PARAMETER( IrpContext ); + + Links = *RootNode; + + while (Links != NULL) { + + Node = CONTAINING_RECORD(Links, FILE_NAME_NODE, Links); + + // + // Compare the prefix in the tree with the full name + // + + Comparison = CompareNames(&Node->Name.Oem, Name); + + // + // See if they don't match + // + + if (Comparison == IsGreaterThan) { + + // + // 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 == IsLessThan) { + + // + // 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); + + // + // Tell the caller what kind of name we hit + // + + if (FileNameDos) { + + *FileNameDos = Node->FileNameDos; + } + + return Node->Fcb; + } + } + + // + // We didn't find the Fcb. + // + + return NULL; +} + + +// +// Local support routine +// + +COMPARISON +FatCompareNames ( + IN PSTRING NameA, + IN PSTRING NameB + ) + +/*++ + +Routine Description: + + This function compares two names as fast as possible. Note that since + this comparison is case sensitive, I neither know nor case if the names + are UNICODE or OEM. All that is important is that the result is + deterministic. + +Arguments: + + NameA & NameB - The names to compare. + +Return Value: + + COMPARISON - returns + + IsLessThan if NameA < NameB lexicalgraphically, + IsGreaterThan if NameA > NameB lexicalgraphically, + IsEqual if NameA is equal to NameB + +--*/ + +{ + ULONG i; + ULONG MinLength; + + PAGED_CODE(); + + // + // Figure out the minimum of the two lengths + // + + MinLength = NameA->Length < NameB->Length ? NameA->Length : + NameB->Length; + + // + // Loop through looking at all of the characters in both strings + // testing for equalilty, less than, and greater than + // + + i = (ULONG)RtlCompareMemory( NameA->Buffer, NameB->Buffer, MinLength ); + + + if (i < MinLength) { + + return NameA->Buffer[i] < NameB->Buffer[i] ? IsLessThan : + IsGreaterThan; + } + + if (NameA->Length < NameB->Length) { + + return IsLessThan; + } + + if (NameA->Length > NameB->Length) { + + return IsGreaterThan; + } + + return IsEqual; +} + diff --git a/filesys/fastfat/strucsup.c b/filesys/fastfat/strucsup.c new file mode 100644 index 000000000..7593278db --- /dev/null +++ b/filesys/fastfat/strucsup.c @@ -0,0 +1,3876 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + StrucSup.c + +Abstract: + + This module implements the Fat in-memory data structure manipulation + routines + + +--*/ + +#include "FatProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (FAT_BUG_CHECK_STRUCSUP) + +// +// The debug trace level +// + +#define Dbg (DEBUG_TRACE_STRUCSUP) + +#define FillMemory(BUF,SIZ,MASK) { \ + ULONG i; \ + for (i = 0; i < (((SIZ)/4) - 1); i += 2) { \ + ((PULONG)(BUF))[i] = (MASK); \ + ((PULONG)(BUF))[i+1] = (ULONG)PsGetCurrentThread(); \ + } \ +} + +#define IRP_CONTEXT_HEADER (sizeof( IRP_CONTEXT ) * 0x10000 + FAT_NTC_IRP_CONTEXT) + +// +// Local macros. +// +// Define our lookaside list allocators. For the time being, and perhaps +// permanently, the paged structures don't come off of lookasides. This +// is due to complications with clean unload as FAT can be in the paging +// path, making it really hard to find the right time to empty them. +// +// Fortunately, the hit rates on the Fcb/Ccb lists weren't stunning. +// + +#define FAT_FILL_FREE 0 + +INLINE +PCCB +FatAllocateCcb ( + ) +{ + return (PCCB) FsRtlAllocatePoolWithTag( PagedPool, sizeof(CCB), TAG_CCB ); +} + +INLINE +VOID +FatFreeCcb ( + IN PCCB Ccb + ) +{ +#if FAT_FILL_FREE + RtlFillMemoryUlong(Ccb, sizeof(CCB), FAT_FILL_FREE); +#endif + + ExFreePool( Ccb ); +} + +INLINE +PFCB +FatAllocateFcb ( + ) +{ + return (PFCB) FsRtlAllocatePoolWithTag( PagedPool, sizeof(FCB), TAG_FCB ); +} + +INLINE +VOID +FatFreeFcb ( + IN PFCB Fcb + ) +{ +#if FAT_FILL_FREE + RtlFillMemoryUlong(Fcb, sizeof(FCB), FAT_FILL_FREE); +#endif + + ExFreePool( Fcb ); +} + +INLINE +PNON_PAGED_FCB +FatAllocateNonPagedFcb ( + ) +{ + return (PNON_PAGED_FCB) ExAllocateFromNPagedLookasideList( &FatNonPagedFcbLookasideList ); +} + +INLINE +VOID +FatFreeNonPagedFcb ( + PNON_PAGED_FCB NonPagedFcb + ) +{ +#if FAT_FILL_FREE + RtlFillMemoryUlong(NonPagedFcb, sizeof(NON_PAGED_FCB), FAT_FILL_FREE); +#endif + + ExFreeToNPagedLookasideList( &FatNonPagedFcbLookasideList, (PVOID) NonPagedFcb ); +} + +INLINE +PERESOURCE +FatAllocateResource ( + ) +{ + PERESOURCE Resource; + + Resource = (PERESOURCE) ExAllocateFromNPagedLookasideList( &FatEResourceLookasideList ); + + ExInitializeResourceLite( Resource ); + + return Resource; +} + +INLINE +VOID +FatFreeResource ( + IN PERESOURCE Resource + ) +{ + ExDeleteResourceLite( Resource ); + +#if FAT_FILL_FREE + RtlFillMemoryUlong(Resource, sizeof(ERESOURCE), FAT_FILL_FREE); +#endif + + ExFreeToNPagedLookasideList( &FatEResourceLookasideList, (PVOID) Resource ); +} + +INLINE +PIRP_CONTEXT +FatAllocateIrpContext ( + ) +{ + return (PIRP_CONTEXT) ExAllocateFromNPagedLookasideList( &FatIrpContextLookasideList ); +} + +INLINE +VOID +FatFreeIrpContext ( + IN PIRP_CONTEXT IrpContext + ) +{ +#if FAT_FILL_FREE + RtlFillMemoryUlong(IrpContext, sizeof(IRP_CONTEXT), FAT_FILL_FREE); +#endif + + ExFreeToNPagedLookasideList( &FatIrpContextLookasideList, (PVOID) IrpContext ); +} + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, FatInitializeVcb) +#pragma alloc_text(PAGE, FatTearDownVcb) +#pragma alloc_text(PAGE, FatDeleteVcb) +#pragma alloc_text(PAGE, FatCreateRootDcb) +#pragma alloc_text(PAGE, FatCreateFcb) +#pragma alloc_text(PAGE, FatCreateDcb) +#pragma alloc_text(PAGE, FatDeleteFcb) +#pragma alloc_text(PAGE, FatCreateCcb) +#pragma alloc_text(PAGE, FatDeallocateCcbStrings) +#pragma alloc_text(PAGE, FatDeleteCcb) +#pragma alloc_text(PAGE, FatGetNextFcbTopDown) +#pragma alloc_text(PAGE, FatGetNextFcbBottomUp) +#pragma alloc_text(PAGE, FatConstructNamesInFcb) +#pragma alloc_text(PAGE, FatCheckFreeDirentBitmap) +#pragma alloc_text(PAGE, FatCreateIrpContext) +#pragma alloc_text(PAGE, FatDeleteIrpContext_Real) +#pragma alloc_text(PAGE, FatIsHandleCountZero) +#pragma alloc_text(PAGE, FatAllocateCloseContext) +#pragma alloc_text(PAGE, FatPreallocateCloseContext) +#pragma alloc_text(PAGE, FatEnsureStringBufferEnough) +#pragma alloc_text(PAGE, FatFreeStringBuffer) +#pragma alloc_text(PAGE, FatScanForDataTrack) +#endif + + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatInitializeVcb ( + IN PIRP_CONTEXT IrpContext, + IN OUT PVCB Vcb, + IN PDEVICE_OBJECT TargetDeviceObject, + IN PVPB Vpb, + IN PDEVICE_OBJECT FsDeviceObject + ) + +/*++ + +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. + + FsDeviceObject - The filesystem device object that the mount was directed + too. + +Return Value: + + None. + +--*/ + +{ + CC_FILE_SIZES FileSizes; + PDEVICE_OBJECT RealDevice; + ULONG i; + + STORAGE_HOTPLUG_INFO HotplugInfo; + STORAGE_DEVICE_NUMBER StorDeviceNumber; + NTSTATUS Status; + + // + // The following variables are used for abnormal unwind + // + + PLIST_ENTRY UnwindEntryList = NULL; + PERESOURCE UnwindResource = NULL; + PERESOURCE UnwindResource2 = NULL; + PFILE_OBJECT UnwindFileObject = NULL; + PFILE_OBJECT UnwindCacheMap = NULL; + BOOLEAN UnwindWeAllocatedMcb = FALSE; + PFILE_SYSTEM_STATISTICS UnwindStatistics = NULL; + BOOLEAN UnwindWeAllocatedBadBlockMap = FALSE; + BOOLEAN CloseContextAllocated = FALSE; + + PAGED_CODE(); + UNREFERENCED_PARAMETER( FsDeviceObject ); + + DebugTrace(+1, Dbg, "FatInitializeVcb, Vcb = %p\n", Vcb); + + try { + + // + // 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->VolumeFileHeader.NodeTypeCode = FAT_NTC_VCB; + Vcb->VolumeFileHeader.NodeByteSize = sizeof(VCB); + + // + // Initialize the tunneling cache + // + + FsRtlInitializeTunnelCache(&Vcb->Tunnel); + + // + // Insert this Vcb record on the FatData.VcbQueue + // + + NT_ASSERT( FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) ); + + +#pragma prefast( push ) +#pragma prefast( disable: 28137, "prefast wants the wait to be a constant, but that isn't possible for the way fastfat is designed" ) +#pragma prefast( disable: 28193, "this will always wait" ) + + (VOID)FatAcquireExclusiveGlobal( IrpContext ); + +#pragma prefast( pop ) + + InsertTailList( &FatData.VcbQueue, &Vcb->VcbLinks ); + FatReleaseGlobal( IrpContext ); + UnwindEntryList = &Vcb->VcbLinks; + + // + // Set the Target Device Object, Vpb, and Vcb State fields + // + + + ObReferenceObject( TargetDeviceObject ); + Vcb->TargetDeviceObject = TargetDeviceObject; + Vcb->Vpb = Vpb; + + Vcb->CurrentDevice = Vpb->RealDevice; + + // + // Set the removable media and defflush flags based on the storage + // inquiry and the old characteristic bits. + // + + Status = FatPerformDevIoCtrl( IrpContext, + IOCTL_STORAGE_GET_HOTPLUG_INFO, + TargetDeviceObject, + NULL, + 0, + &HotplugInfo, + sizeof(HotplugInfo), + FALSE, + TRUE, + NULL ); + + if (NT_SUCCESS( Status )) { + + if (HotplugInfo.MediaRemovable) { + + SetFlag( Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA ); + } + + // + // If the media or device is hot-pluggable, then set this flag. + // + + if (HotplugInfo.MediaHotplug || HotplugInfo.DeviceHotplug) { + + SetFlag( Vcb->VcbState, VCB_STATE_FLAG_HOTPLUGGABLE ); + } + + if (!HotplugInfo.WriteCacheEnableOverride) { + + // + // If the device or media is hotplug and the override is not + // set, force defflush behavior for the device. + // + + if (HotplugInfo.MediaHotplug || HotplugInfo.DeviceHotplug) { + + NT_ASSERT( FlagOn( Vcb->VcbState, VCB_STATE_FLAG_HOTPLUGGABLE )); + + SetFlag( Vcb->VcbState, VCB_STATE_FLAG_DEFERRED_FLUSH ); + + // + // Now, for removables that claim to be lockable, lob a lock + // request and see if it works. There can unfortunately be + // transient, media dependent reasons that it can fail. If + // it does not, we must force defflush on. + // + + } else if (HotplugInfo.MediaRemovable && + !HotplugInfo.MediaHotplug) { + + Status = FatToggleMediaEjectDisable( IrpContext, Vcb, TRUE ); + + if (!NT_SUCCESS( Status )) { + + SetFlag( Vcb->VcbState, VCB_STATE_FLAG_DEFERRED_FLUSH ); + + } + + (VOID)FatToggleMediaEjectDisable( IrpContext, Vcb, FALSE ); + } + } + } + + if (FlagOn(Vpb->RealDevice->Characteristics, FILE_REMOVABLE_MEDIA)) { + + SetFlag( Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA ); + } + + // + // Make sure we turn on deferred flushing for floppies like we always + // have. + // + + if (FlagOn(Vpb->RealDevice->Characteristics, FILE_FLOPPY_DISKETTE)) { + + SetFlag( Vcb->VcbState, VCB_STATE_FLAG_DEFERRED_FLUSH ); + } + + // + // Query the storage device number. + // + + Status = FatPerformDevIoCtrl( IrpContext, + IOCTL_STORAGE_GET_DEVICE_NUMBER, + TargetDeviceObject, + NULL, + 0, + &StorDeviceNumber, + sizeof(StorDeviceNumber), + FALSE, + TRUE, + NULL ); + + if (NT_SUCCESS( Status )) { + + Vcb->DeviceNumber = StorDeviceNumber.DeviceNumber; + + } else { + + Vcb->DeviceNumber = (ULONG)(-1); + } + + FatSetVcbCondition( Vcb, VcbGood); + + // + // Initialize the resource variable for the Vcb + // + + ExInitializeResourceLite( &Vcb->Resource ); + UnwindResource = &Vcb->Resource; + + ExInitializeResourceLite( &Vcb->ChangeBitMapResource ); + UnwindResource2 = &Vcb->ChangeBitMapResource; + + // + // Initialize the free cluster bitmap mutex. + // + + ExInitializeFastMutex( &Vcb->FreeClusterBitMapMutex ); + + // + // Create the special file object for the virtual volume file with a close + // context, its pointers back to the Vcb and the section object pointer. + // + // We don't have to unwind the close context. That will happen in the close + // path automatically. + // + + RealDevice = Vcb->CurrentDevice; + + FatPreallocateCloseContext(Vcb); + CloseContextAllocated = TRUE; + + Vcb->VirtualVolumeFile = UnwindFileObject = IoCreateStreamFileObject( NULL, RealDevice ); + + FatSetFileObject( Vcb->VirtualVolumeFile, + VirtualVolumeFile, + Vcb, + NULL ); + + // + // Remember this internal, residual open. + // + + InterlockedIncrement( (LONG*)&(Vcb->InternalOpenCount) ); + InterlockedIncrement( (LONG*)&(Vcb->ResidualOpenCount) ); + + Vcb->VirtualVolumeFile->SectionObjectPointer = &Vcb->SectionObjectPointers; + + Vcb->VirtualVolumeFile->ReadAccess = TRUE; + Vcb->VirtualVolumeFile->WriteAccess = TRUE; + Vcb->VirtualVolumeFile->DeleteAccess = TRUE; + + // + // Initialize the notify structures. + // + + InitializeListHead( &Vcb->DirNotifyList ); + + FsRtlNotifyInitializeSync( &Vcb->NotifySync ); + + // + // Initialize the Cache Map for the volume file. The size is + // initially set to that of our first read. It will be extended + // when we know how big the Fat is. + // + + FileSizes.AllocationSize.QuadPart = + FileSizes.FileSize.QuadPart = sizeof(PACKED_BOOT_SECTOR); + FileSizes.ValidDataLength = FatMaxLarge; + + FatInitializeCacheMap( Vcb->VirtualVolumeFile, + &FileSizes, + TRUE, + &FatData.CacheManagerNoOpCallbacks, + Vcb ); + + UnwindCacheMap = Vcb->VirtualVolumeFile; + + // + // Initialize the structure that will keep track of dirty fat sectors. + // The largest possible Mcb structures are less than 1K, so we use + // non paged pool. + // + + FsRtlInitializeLargeMcb( &Vcb->DirtyFatMcb, PagedPool ); + + UnwindWeAllocatedMcb = TRUE; + + // + // Initialize the structure that will keep track of bad clusters on the volume. + // + // It will be empty until it is populated by FSCTL_GET_RETRIEVAL_POINTERS with a volume handle. + // + + FsRtlInitializeLargeMcb( &Vcb->BadBlockMcb, PagedPool ); + UnwindWeAllocatedBadBlockMap = TRUE; + + // + // Set the cluster index hint to the first valid cluster of a fat: 2 + // + + Vcb->ClusterHint = 2; + + // + // Initialize the directory stream file object creation event. + // This event is also "borrowed" for async non-cached writes. + // + + ExInitializeFastMutex( &Vcb->DirectoryFileCreationMutex ); + + // + // Initialize the clean volume callback Timer and DPC. + // + + KeInitializeTimer( &Vcb->CleanVolumeTimer ); + + KeInitializeDpc( &Vcb->CleanVolumeDpc, FatCleanVolumeDpc, Vcb ); + + // + // Initialize the performance counters. + // + + Vcb->Statistics = FsRtlAllocatePoolWithTag( NonPagedPoolNx, + sizeof(FILE_SYSTEM_STATISTICS) * FatData.NumberProcessors, + TAG_VCB_STATS ); + UnwindStatistics = Vcb->Statistics; + + RtlZeroMemory( Vcb->Statistics, sizeof(FILE_SYSTEM_STATISTICS) * FatData.NumberProcessors ); + + for (i = 0; i < FatData.NumberProcessors; i += 1) { + Vcb->Statistics[i].Common.FileSystemType = FILESYSTEM_STATISTICS_TYPE_FAT; + Vcb->Statistics[i].Common.Version = 1; + Vcb->Statistics[i].Common.SizeOfCompleteStructure = + sizeof(FILE_SYSTEM_STATISTICS); + } + + // + // Pick up a VPB right now so we know we can pull this filesystem stack off + // of the storage stack on demand. + // + + Vcb->SwapVpb = FsRtlAllocatePoolWithTag( NonPagedPoolNx, + sizeof( VPB ), + TAG_VPB ); + + RtlZeroMemory( Vcb->SwapVpb, sizeof( VPB ) ); + + // + // Initialize the close queue listheads. + // + + InitializeListHead( &Vcb->AsyncCloseList ); + InitializeListHead( &Vcb->DelayedCloseList ); + + // + // Initialize the Advanced FCB Header + // + + ExInitializeFastMutex( &Vcb->AdvancedFcbHeaderMutex ); + FsRtlSetupAdvancedHeader( &Vcb->VolumeFileHeader, + &Vcb->AdvancedFcbHeaderMutex ); + + // + // With the Vcb now set up, set the IrpContext Vcb field. + // + + IrpContext->Vcb = Vcb; + + } finally { + + DebugUnwind( FatInitializeVcb ); + + // + // If this is an abnormal termination then undo our work + // + + if (AbnormalTermination()) { + + if (UnwindCacheMap != NULL) { FatSyncUninitializeCacheMap( IrpContext, UnwindCacheMap ); } + if (UnwindFileObject != NULL) { ObDereferenceObject( UnwindFileObject ); } + if (UnwindResource != NULL) { FatDeleteResource( UnwindResource ); } + if (UnwindResource2 != NULL) { FatDeleteResource( UnwindResource2 ); } + if (UnwindWeAllocatedMcb) { FsRtlUninitializeLargeMcb( &Vcb->DirtyFatMcb ); } + if (UnwindWeAllocatedBadBlockMap) { FsRtlUninitializeLargeMcb(&Vcb->BadBlockMcb ); } + if (UnwindEntryList != NULL) { +#pragma prefast( suppress: 28137, "prefast wants the wait to be a constant, but that isn't possible for the way fastfat is designed" ) + (VOID)FatAcquireExclusiveGlobal( IrpContext ); + RemoveEntryList( UnwindEntryList ); + FatReleaseGlobal( IrpContext ); + } + if (UnwindStatistics != NULL) { ExFreePool( UnwindStatistics ); } + + // + // Cleanup the close context we preallocated above. + // + + if (CloseContextAllocated && (Vcb->VirtualVolumeFile == NULL)) { + + // + // FatAllocateCloseContext does not allocate memory, it + // pulls a close context off the preallocated slist queue. + // + // Doing this here is necessary to balance out the one we + // preallocated for the Vcb earlier in this function, but + // only if we failed to create the virtual volume file. + // + // If VirtualVolumeFile is not NULL, then this CloseContext + // will get cleaned up when the close comes in for it during + // Vcb teardown. + // + + PCLOSE_CONTEXT CloseContext = FatAllocateCloseContext(Vcb); + + ExFreePool( CloseContext ); + CloseContextAllocated = FALSE; + } + } + + DebugTrace(-1, Dbg, "FatInitializeVcb -> VOID\n", 0); + } + + // + // and return to our caller + // + + UNREFERENCED_PARAMETER( IrpContext ); + + return; +} + + +VOID +FatTearDownVcb ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb + ) + +/*++ + +Routine Description: + + This routine tries to remove all internal opens from the volume. + +Arguments: + + IrpContext - Supplies the context for the overall request. + + Vcb - Supplies the Vcb to be torn down. + +Return Value: + + None + +--*/ + +{ + PFILE_OBJECT DirectoryFileObject; + + + PAGED_CODE(); + + // + // Get rid of the virtual volume file, if we need to. + // + + if (Vcb->VirtualVolumeFile != NULL) { + + // + // Uninitialize the cache + // + + CcUninitializeCacheMap( Vcb->VirtualVolumeFile, + &FatLargeZero, + NULL ); + + FsRtlTeardownPerStreamContexts( &Vcb->VolumeFileHeader ); + + ObDereferenceObject( Vcb->VirtualVolumeFile ); + + Vcb->VirtualVolumeFile = NULL; + } + + // + // Close down the EA file. + // + + FatCloseEaFile( IrpContext, Vcb, FALSE ); + + // + // Close down the root directory stream.. + // + + if (Vcb->RootDcb != NULL) { + + DirectoryFileObject = Vcb->RootDcb->Specific.Dcb.DirectoryFile; + + if (DirectoryFileObject != NULL) { + + // + // Tear down this directory file. + // + + CcUninitializeCacheMap( DirectoryFileObject, + &FatLargeZero, + NULL ); + + Vcb->RootDcb->Specific.Dcb.DirectoryFile = NULL; + ObDereferenceObject( DirectoryFileObject ); + } + } + + // + // The VCB can no longer be used. + // + + FatSetVcbCondition( Vcb, VcbBad ); +} + + +VOID +FatDeleteVcb ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb + ) + +/*++ + +Routine Description: + + This routine removes the Vcb record from Fat's in-memory data + structures. It also will remove all associated underlings + (i.e., FCB records). + +Arguments: + + Vcb - Supplies the Vcb to be removed + +Return Value: + + None + +--*/ + +{ + PFCB Fcb; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatDeleteVcb, Vcb = %p\n", Vcb); + + // + // If the IrpContext points to the VCB being deleted NULL out the stail + // pointer. + // + + if (IrpContext->Vcb == Vcb) { + + IrpContext->Vcb = NULL; + + } + + // + // Chuck the backpocket Vpb we kept just in case. + // + + if (Vcb->SwapVpb) { + + ExFreePool( Vcb->SwapVpb ); + + } + + // + // Free the VPB, if we need to. + // + + if (FlagOn( Vcb->VcbState, VCB_STATE_FLAG_VPB_MUST_BE_FREED )) { + + // + // We swapped the VPB, so we need to free the main one. + // + + ExFreePool( Vcb->Vpb ); + } + + // + // Remove this record from the global list of all Vcb records. + // Note that the global lock must already be held when calling + // this function. + // + + RemoveEntryList( &(Vcb->VcbLinks) ); + + // + // Make sure the direct access open count is zero, and the open file count + // is also zero. + // + + if ((Vcb->DirectAccessOpenCount != 0) || (Vcb->OpenFileCount != 0)) { + +#pragma prefast( suppress:28159, "things are seriously wrong if we get here" ) + FatBugCheck( 0, 0, 0 ); + } + + // + // Remove the EaFcb and dereference the Fcb for the Ea file if it + // exists. + // + + if (Vcb->EaFcb != NULL) { + + Vcb->EaFcb->OpenCount = 0; + FatDeleteFcb( IrpContext, &Vcb->EaFcb ); + } + + // + // Remove the Root Dcb + // + + if (Vcb->RootDcb != NULL) { + + // + // Rundown stale child Fcbs that may be hanging around. Yes, this + // can happen. No, the create path isn't perfectly defensive about + // tearing down branches built up on creates that don't wind up + // succeeding. Normal system operation usually winds up having + // cleaned them out through re-visiting, but ... + // + // Just pick off Fcbs from the bottom of the tree until we run out. + // Then we delete the root Dcb. + // + + while( (Fcb = FatGetNextFcbBottomUp( IrpContext, NULL, Vcb->RootDcb )) != Vcb->RootDcb ) { + + FatDeleteFcb( IrpContext, &Fcb ); + } + + FatDeleteFcb( IrpContext, &Vcb->RootDcb ); + } + + // + // Uninitialize the notify sychronization object. + // + + FsRtlNotifyUninitializeSync( &Vcb->NotifySync ); + + // + // Uninitialize the resource variable for the Vcb + // + + FatDeleteResource( &Vcb->Resource ); + FatDeleteResource( &Vcb->ChangeBitMapResource ); + + // + // If allocation support has been setup, free it. + // + + if (Vcb->FreeClusterBitMap.Buffer != NULL) { + + FatTearDownAllocationSupport( IrpContext, Vcb ); + } + + // + // UnInitialize the Mcb structure that kept track of dirty fat sectors. + // + + FsRtlUninitializeLargeMcb( &Vcb->DirtyFatMcb ); + + // + // Uninitialize the Mcb structure that kept track of bad sectors. + // + + FsRtlUninitializeLargeMcb( &Vcb->BadBlockMcb ); + + // + // Free the pool for the stached copy of the boot sector + // + + if ( Vcb->First0x24BytesOfBootSector ) { + + ExFreePool( Vcb->First0x24BytesOfBootSector ); + Vcb->First0x24BytesOfBootSector = NULL; + } + + // + // Cancel the CleanVolume Timer and Dpc + // + + (VOID)KeCancelTimer( &Vcb->CleanVolumeTimer ); + + (VOID)KeRemoveQueueDpc( &Vcb->CleanVolumeDpc ); + + // + // Free the performance counters memory + // + + ExFreePool( Vcb->Statistics ); + + // + // Clean out the tunneling cache + // + + FsRtlDeleteTunnelCache(&Vcb->Tunnel); + + // + // Dereference the target device object. + // + + ObDereferenceObject( Vcb->TargetDeviceObject ); + + // + // We better have used all the close contexts we allocated. There could be + // one remaining if we're doing teardown due to a final close coming in on + // a directory file stream object. It will be freed on the way back up. + // + + NT_ASSERT( Vcb->CloseContextCount <= 1); + + // + // And zero out the Vcb, this will help ensure that any stale data is + // wiped clean + // + + RtlZeroMemory( Vcb, sizeof(VCB) ); + + // + // return and tell the caller + // + + DebugTrace(-1, Dbg, "FatDeleteVcb -> VOID\n", 0); + + return; +} + + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatCreateRootDcb ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb + ) + +/*++ + +Routine Description: + + This routine allocates, initializes, and inserts a new root DCB record + into the in memory data structure. + +Arguments: + + Vcb - Supplies the Vcb to associate the new DCB under + +Return Value: + + None. The Vcb is modified in-place. + +--*/ + +{ + PDCB Dcb = NULL; + + // + // The following variables are used for abnormal unwind + // + + PVOID UnwindStorage[2] = { NULL, NULL }; + PERESOURCE UnwindResource = NULL; + PERESOURCE UnwindResource2 = NULL; + PLARGE_MCB UnwindMcb = NULL; + PFILE_OBJECT UnwindFileObject = NULL; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatCreateRootDcb, Vcb = %p\n", Vcb); + + try { + + // + // Make sure we don't already have a root dcb for this vcb + // + + if (Vcb->RootDcb != NULL) { + + DebugDump("Error trying to create multiple root dcbs\n", 0, Vcb); +#pragma prefast( suppress:28159, "things are seriously wrong if we get here" ) + FatBugCheck( 0, 0, 0 ); + } + + // + // Allocate a new DCB and zero it out, we use Dcb locally so we don't + // have to continually reference through the Vcb + // + + UnwindStorage[0] = Dcb = Vcb->RootDcb = FsRtlAllocatePoolWithTag( NonPagedPoolNx, + sizeof(DCB), + TAG_FCB ); + + RtlZeroMemory( Dcb, sizeof(DCB)); + + UnwindStorage[1] = + Dcb->NonPaged = FatAllocateNonPagedFcb(); + + RtlZeroMemory( Dcb->NonPaged, sizeof( NON_PAGED_FCB ) ); + + // + // Set the proper node type code, node byte size, and call backs + // + + Dcb->Header.NodeTypeCode = FAT_NTC_ROOT_DCB; + Dcb->Header.NodeByteSize = sizeof(DCB); + + Dcb->FcbCondition = FcbGood; + + // + // The parent Dcb, initial state, open count, dirent location + // information, and directory change count fields are already zero so + // we can skip setting them + // + + // + // Initialize the resource variable + // + + UnwindResource = + Dcb->Header.Resource = FatAllocateResource(); + + // + // Initialize the PagingIo Resource. We no longer use the FsRtl common + // shared pool because this led to a) deadlocks due to cases where files + // and their parent directories shared a resource and b) there is no way + // to anticipate inter-driver induced deadlock via recursive operation. + // + + UnwindResource2 = + Dcb->Header.PagingIoResource = FatAllocateResource(); + + // + // The root Dcb has an empty parent dcb links field + // + + InitializeListHead( &Dcb->ParentDcbLinks ); + + // + // Set the Vcb + // + + Dcb->Vcb = Vcb; + + // + // initialize the parent dcb queue. + // + + InitializeListHead( &Dcb->Specific.Dcb.ParentDcbQueue ); + + // + // Set the full file name up. + // + + Dcb->FullFileName.Buffer = L"\\"; + Dcb->FullFileName.Length = (USHORT)2; + Dcb->FullFileName.MaximumLength = (USHORT)4; + + Dcb->ShortName.Name.Oem.Buffer = "\\"; + Dcb->ShortName.Name.Oem.Length = (USHORT)1; + Dcb->ShortName.Name.Oem.MaximumLength = (USHORT)2; + + // + // Construct a lie about file properties since we don't + // have a proper "." entry to look at. + // + + Dcb->DirentFatFlags = FILE_ATTRIBUTE_DIRECTORY; + + // + // Initialize Advanced FCB Header fields + // + + ExInitializeFastMutex( &Dcb->NonPaged->AdvancedFcbHeaderMutex ); + FsRtlSetupAdvancedHeader( &Dcb->Header, + &Dcb->NonPaged->AdvancedFcbHeaderMutex ); + + // + // Initialize the Mcb, and setup its mapping. Note that the root + // directory is a fixed size so we can set it everything up now. + // + + FsRtlInitializeLargeMcb( &Dcb->Mcb, NonPagedPoolNx ); + UnwindMcb = &Dcb->Mcb; + + if (FatIsFat32(Vcb)) { + + // + // The first cluster of fat32 roots comes from the BPB + // + + Dcb->FirstClusterOfFile = Vcb->Bpb.RootDirFirstCluster; + + } else { + + FatAddMcbEntry( Vcb, &Dcb->Mcb, + 0, + FatRootDirectoryLbo( &Vcb->Bpb ), + FatRootDirectorySize( &Vcb->Bpb )); + } + + if (FatIsFat32(Vcb)) { + + // + // Find the size of the fat32 root. As a side-effect, this will create + // MCBs for the entire root. In the process of doing this, we may + // discover that the FAT chain is bogus and raise corruption. + // + + Dcb->Header.AllocationSize.LowPart = 0xFFFFFFFF; + FatLookupFileAllocationSize( IrpContext, Dcb); + + Dcb->Header.FileSize.QuadPart = + Dcb->Header.AllocationSize.QuadPart; + } else { + + // + // set the allocation size to real size of the root directory + // + + Dcb->Header.FileSize.QuadPart = + Dcb->Header.AllocationSize.QuadPart = FatRootDirectorySize( &Vcb->Bpb ); + + } + + // + // Set our two create dirent aids to represent that we have yet to + // enumerate the directory for never used or deleted dirents. + // + + Dcb->Specific.Dcb.UnusedDirentVbo = 0xffffffff; + Dcb->Specific.Dcb.DeletedDirentHint = 0xffffffff; + + // + // Setup the free dirent bitmap buffer. + // + + RtlInitializeBitMap( &Dcb->Specific.Dcb.FreeDirentBitmap, + NULL, + 0 ); + + FatCheckFreeDirentBitmap( IrpContext, Dcb ); + +#if (NTDDI_VERSION >= NTDDI_WIN8) + // + // Initialize the oplock structure. + // + + FsRtlInitializeOplock( FatGetFcbOplock(Dcb) ); +#endif + + } finally { + + DebugUnwind( FatCreateRootDcb ); + + // + // If this is an abnormal termination then undo our work + // + + if (AbnormalTermination()) { + + ULONG i; + + if (UnwindFileObject != NULL) { ObDereferenceObject( UnwindFileObject ); } + if (UnwindMcb != NULL) { FsRtlUninitializeLargeMcb( UnwindMcb ); } + if (UnwindResource != NULL) { FatFreeResource( UnwindResource ); } + if (UnwindResource2 != NULL) { FatFreeResource( UnwindResource2 ); } + + for (i = 0; i < sizeof(UnwindStorage)/sizeof(PVOID); i += 1) { + if (UnwindStorage[i] != NULL) { ExFreePool( UnwindStorage[i] ); } + } + + // + // Re-zero the entry in the Vcb. + // + + Vcb->RootDcb = NULL; + } + + DebugTrace(-1, Dbg, "FatCreateRootDcb -> %p\n", Dcb); + } + + return; +} + + +PFCB +FatCreateFcb ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PDCB ParentDcb, + IN ULONG LfnOffsetWithinDirectory, + IN ULONG DirentOffsetWithinDirectory, + IN PDIRENT Dirent, + IN PUNICODE_STRING Lfn OPTIONAL, + IN BOOLEAN IsPagingFile, + IN BOOLEAN SingleResource + ) + +/*++ + +Routine Description: + + This routine allocates, initializes, and inserts a new Fcb record into + the in-memory data structures. + +Arguments: + + Vcb - Supplies the Vcb to associate the new FCB under. + + ParentDcb - Supplies the parent dcb that the new FCB is under. + + LfnOffsetWithinDirectory - Supplies the offset of the LFN. If there is + no LFN associated with this file then this value is same as + DirentOffsetWithinDirectory. + + DirentOffsetWithinDirectory - Supplies the offset, in bytes from the + start of the directory file where the dirent for the fcb is located + + Dirent - Supplies the dirent for the fcb being created + + Lfn - Supplies a long UNICODE name associated with this file. + + IsPagingFile - Indicates if we are creating an FCB for a paging file + or some other type of file. + + SingleResource - Indicates if this Fcb should share a single resource + as both main and paging. + +Return Value: + + PFCB - Returns a pointer to the newly allocated FCB + +--*/ + +{ + PFCB Fcb = NULL; + POOL_TYPE PoolType; + + // + // The following variables are used for abnormal unwind + // + + PVOID UnwindStorage[2] = { NULL, NULL }; + PERESOURCE UnwindResource = NULL; + PERESOURCE UnwindResource2 = NULL; + PLIST_ENTRY UnwindEntryList = NULL; + PLARGE_MCB UnwindMcb = NULL; + PFILE_LOCK UnwindFileLock = NULL; + POPLOCK UnwindOplock = NULL; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatCreateFcb\n", 0); + + try { + + // + // Determine the pool type we should be using for the fcb and the + // mcb structure + // + + if (IsPagingFile) { + + PoolType = NonPagedPoolNx; + Fcb = UnwindStorage[0] = FsRtlAllocatePoolWithTag( NonPagedPoolNx, + sizeof(FCB), + TAG_FCB ); + } else { + + PoolType = PagedPool; + Fcb = UnwindStorage[0] = FatAllocateFcb(); + + } + + // + // ... and zero it out + // + + RtlZeroMemory( Fcb, sizeof(FCB) ); + + UnwindStorage[1] = + Fcb->NonPaged = FatAllocateNonPagedFcb(); + + RtlZeroMemory( Fcb->NonPaged, sizeof( NON_PAGED_FCB ) ); + + // + // Set the proper node type code, node byte size, and call backs + // + + Fcb->Header.NodeTypeCode = FAT_NTC_FCB; + Fcb->Header.NodeByteSize = sizeof(FCB); + + Fcb->FcbCondition = FcbGood; + + // + // Check to see if we need to set the Fcb state to indicate that this + // is a paging/system file. This will prevent it from being opened + // again. + // + + if (IsPagingFile) { + + SetFlag( Fcb->FcbState, FCB_STATE_PAGING_FILE | FCB_STATE_SYSTEM_FILE ); + } + + // + // The initial state, open count, and segment objects fields are already + // zero so we can skip setting them + // + + // + // Initialize the resource variable + // + + + UnwindResource = + Fcb->Header.Resource = FatAllocateResource(); + + // + // Initialize the PagingIo Resource. We no longer use the FsRtl common + // shared pool because this led to a) deadlocks due to cases where files + // and their parent directories shared a resource and b) there is no way + // to anticipate inter-driver induced deadlock via recursive operation. + // + + if (SingleResource) { + + Fcb->Header.PagingIoResource = Fcb->Header.Resource; + + } else { + + UnwindResource2 = + Fcb->Header.PagingIoResource = FatAllocateResource(); + } + + // + // Insert this fcb into our parent dcb's queue. + // + // There is a deep reason why this goes on the tail, to allow us + // to easily enumerate all child directories before child files. + // This is important to let us maintain whole-volume lockorder + // via BottomUp enumeration. + // + + InsertTailList( &ParentDcb->Specific.Dcb.ParentDcbQueue, + &Fcb->ParentDcbLinks ); + UnwindEntryList = &Fcb->ParentDcbLinks; + + // + // Point back to our parent dcb + // + + Fcb->ParentDcb = ParentDcb; + + // + // Set the Vcb + // + + Fcb->Vcb = Vcb; + + // + // Set the dirent offset within the directory + // + + Fcb->LfnOffsetWithinDirectory = LfnOffsetWithinDirectory; + Fcb->DirentOffsetWithinDirectory = DirentOffsetWithinDirectory; + + // + // Set the DirentFatFlags and LastWriteTime + // + + Fcb->DirentFatFlags = Dirent->Attributes; + + Fcb->LastWriteTime = FatFatTimeToNtTime( IrpContext, + Dirent->LastWriteTime, + 0 ); + + // + // These fields are only non-zero when in Chicago mode. + // + + if (FatData.ChicagoMode) { + + LARGE_INTEGER FatSystemJanOne1980 = {0}; + + // + // If either date is possibly zero, get the system + // version of 1/1/80. + // + + if ((((PUSHORT)Dirent)[9] & ((PUSHORT)Dirent)[8]) == 0) { + + ExLocalTimeToSystemTime( &FatJanOne1980, + &FatSystemJanOne1980 ); + } + + // + // Only do the really hard work if this field is non-zero. + // + + if (((PUSHORT)Dirent)[9] != 0) { + + Fcb->LastAccessTime = + FatFatDateToNtTime( IrpContext, + Dirent->LastAccessDate ); + + } else { + + Fcb->LastAccessTime = FatSystemJanOne1980; + } + + // + // Only do the really hard work if this field is non-zero. + // + + if (((PUSHORT)Dirent)[8] != 0) { + + Fcb->CreationTime = + FatFatTimeToNtTime( IrpContext, + Dirent->CreationTime, + Dirent->CreationMSec ); + + } else { + + Fcb->CreationTime = FatSystemJanOne1980; + } + } + + // + // Initialize Advanced FCB Header fields + // + + ExInitializeFastMutex( &Fcb->NonPaged->AdvancedFcbHeaderMutex ); + FsRtlSetupAdvancedHeader( &Fcb->Header, + &Fcb->NonPaged->AdvancedFcbHeaderMutex ); + + // + // To make FAT match the present functionality of NTFS, disable + // stream contexts on paging files + // + + if (IsPagingFile) { + + SetFlag( Fcb->Header.Flags2, FSRTL_FLAG2_IS_PAGING_FILE ); + ClearFlag( Fcb->Header.Flags2, FSRTL_FLAG2_SUPPORTS_FILTER_CONTEXTS ); + } + + // + // Initialize the Mcb + // + + FsRtlInitializeLargeMcb( &Fcb->Mcb, PoolType ); + UnwindMcb = &Fcb->Mcb; + + // + // Set the file size, valid data length, first cluster of file, + // and allocation size based on the information stored in the dirent + // + + Fcb->Header.FileSize.LowPart = Dirent->FileSize; + + Fcb->Header.ValidDataLength.LowPart = Dirent->FileSize; + + Fcb->ValidDataToDisk = Dirent->FileSize; + + Fcb->FirstClusterOfFile = (ULONG)Dirent->FirstClusterOfFile; + + if ( FatIsFat32(Vcb) ) { + + Fcb->FirstClusterOfFile += Dirent->FirstClusterOfFileHi << 16; + } + + if ( Fcb->FirstClusterOfFile == 0 ) { + + Fcb->Header.AllocationSize.QuadPart = 0; + + } else { + + Fcb->Header.AllocationSize.QuadPart = FCB_LOOKUP_ALLOCATIONSIZE_HINT; + } + + + // + // Initialize the Fcb's file lock record + // + + FsRtlInitializeFileLock( &Fcb->Specific.Fcb.FileLock, NULL, NULL ); + UnwindFileLock = &Fcb->Specific.Fcb.FileLock; + + // + // Initialize the oplock structure. + // + + FsRtlInitializeOplock( FatGetFcbOplock(Fcb) ); + UnwindOplock = FatGetFcbOplock(Fcb); + + // + // Indicate that Fast I/O is possible + // + + Fcb->Header.IsFastIoPossible = TRUE; + + // + // Set the file names. This must be the last thing we do. + // + + FatConstructNamesInFcb( IrpContext, + Fcb, + Dirent, + Lfn ); + + // + // Drop the shortname hint so prefix searches can figure out + // what they found + // + + Fcb->ShortName.FileNameDos = TRUE; + + } finally { + + DebugUnwind( FatCreateFcb ); + + // + // If this is an abnormal termination then undo our work + // + + if (AbnormalTermination()) { + + ULONG i; + + if (UnwindOplock != NULL) { FsRtlUninitializeOplock( UnwindOplock ); } + if (UnwindFileLock != NULL) { FsRtlUninitializeFileLock( UnwindFileLock ); } + if (UnwindMcb != NULL) { FsRtlUninitializeLargeMcb( UnwindMcb ); } + if (UnwindEntryList != NULL) { RemoveEntryList( UnwindEntryList ); } + if (UnwindResource != NULL) { FatFreeResource( UnwindResource ); } + if (UnwindResource2 != NULL) { FatFreeResource( UnwindResource2 ); } + + for (i = 0; i < sizeof(UnwindStorage)/sizeof(PVOID); i += 1) { + if (UnwindStorage[i] != NULL) { ExFreePool( UnwindStorage[i] ); } + } + } + + DebugTrace(-1, Dbg, "FatCreateFcb -> %p\n", Fcb); + } + + // + // return and tell the caller + // + + return Fcb; +} + + +PDCB +FatCreateDcb ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PDCB ParentDcb, + IN ULONG LfnOffsetWithinDirectory, + IN ULONG DirentOffsetWithinDirectory, + IN PDIRENT Dirent, + IN PUNICODE_STRING Lfn OPTIONAL + ) + +/*++ + +Routine Description: + + This routine allocates, initializes, and inserts a new Dcb record into + the in memory data structures. + +Arguments: + + Vcb - Supplies the Vcb to associate the new DCB under. + + ParentDcb - Supplies the parent dcb that the new DCB is under. + + LfnOffsetWithinDirectory - Supplies the offset of the LFN. If there is + no LFN associated with this file then this value is same as + DirentOffsetWithinDirectory. + + DirentOffsetWithinDirectory - Supplies the offset, in bytes from the + start of the directory file where the dirent for the fcb is located + + Dirent - Supplies the dirent for the dcb being created + + FileName - Supplies the file name of the file relative to the directory + it's in (e.g., the file \config.sys is called "CONFIG.SYS" without + the preceding backslash). + + Lfn - Supplies a long UNICODE name associated with this directory. + +Return Value: + + PDCB - Returns a pointer to the newly allocated DCB + +--*/ + +{ + PDCB Dcb = NULL; + + // + // The following variables are used for abnormal unwind + // + + PVOID UnwindStorage[2] = { NULL, NULL }; + PERESOURCE UnwindResource = NULL; + PERESOURCE UnwindResource2 = NULL; + PLIST_ENTRY UnwindEntryList = NULL; + PLARGE_MCB UnwindMcb = NULL; + POPLOCK UnwindOplock = NULL; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatCreateDcb\n", 0); + + try { + + // + // assert that the only time we are called is if wait is true + // + + NT_ASSERT( FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) ); + + // + // Allocate a new DCB, and zero it out + // + + UnwindStorage[0] = Dcb = FatAllocateFcb(); + + RtlZeroMemory( Dcb, sizeof(DCB) ); + + UnwindStorage[1] = + Dcb->NonPaged = FatAllocateNonPagedFcb(); + + RtlZeroMemory( Dcb->NonPaged, sizeof( NON_PAGED_FCB ) ); + + // + // Set the proper node type code, node byte size and call backs + // + + Dcb->Header.NodeTypeCode = FAT_NTC_DCB; + Dcb->Header.NodeByteSize = sizeof(DCB); + + Dcb->FcbCondition = FcbGood; + + // + // The initial state, open count, and directory change count fields are + // already zero so we can skip setting them + // + + // + // Initialize the resource variable + // + + + UnwindResource = + Dcb->Header.Resource = FatAllocateResource(); + + // + // Initialize the PagingIo Resource. We no longer use the FsRtl common + // shared pool because this led to a) deadlocks due to cases where files + // and their parent directories shared a resource and b) there is no way + // to anticipate inter-driver induced deadlock via recursive operation. + // + + UnwindResource2 = + Dcb->Header.PagingIoResource = FatAllocateResource(); + + // + // Insert this Dcb into our parent dcb's queue + // + // There is a deep reason why this goes on the head, to allow us + // to easily enumerate all child directories before child files. + // This is important to let us maintain whole-volume lockorder + // via BottomUp enumeration. + // + + InsertHeadList( &ParentDcb->Specific.Dcb.ParentDcbQueue, + &Dcb->ParentDcbLinks ); + UnwindEntryList = &Dcb->ParentDcbLinks; + + // + // Point back to our parent dcb + // + + Dcb->ParentDcb = ParentDcb; + + // + // Set the Vcb + // + + Dcb->Vcb = Vcb; + + // + // Set the dirent offset within the directory + // + + Dcb->LfnOffsetWithinDirectory = LfnOffsetWithinDirectory; + Dcb->DirentOffsetWithinDirectory = DirentOffsetWithinDirectory; + + // + // Set the DirentFatFlags and LastWriteTime + // + + Dcb->DirentFatFlags = Dirent->Attributes; + + Dcb->LastWriteTime = FatFatTimeToNtTime( IrpContext, + Dirent->LastWriteTime, + 0 ); + + // + // These fields are only non-zero when in Chicago mode. + // + + if (FatData.ChicagoMode) { + + LARGE_INTEGER FatSystemJanOne1980 = {0}; + + // + // If either date is possibly zero, get the system + // version of 1/1/80. + // + + if ((((PUSHORT)Dirent)[9] & ((PUSHORT)Dirent)[8]) == 0) { + + ExLocalTimeToSystemTime( &FatJanOne1980, + &FatSystemJanOne1980 ); + } + + // + // Only do the really hard work if this field is non-zero. + // + + if (((PUSHORT)Dirent)[9] != 0) { + + Dcb->LastAccessTime = + FatFatDateToNtTime( IrpContext, + Dirent->LastAccessDate ); + + } else { + + Dcb->LastAccessTime = FatSystemJanOne1980; + } + + // + // Only do the really hard work if this field is non-zero. + // + + if (((PUSHORT)Dirent)[8] != 0) { + + Dcb->CreationTime = + FatFatTimeToNtTime( IrpContext, + Dirent->CreationTime, + Dirent->CreationMSec ); + + } else { + + Dcb->CreationTime = FatSystemJanOne1980; + } + } + + // + // Initialize Advanced FCB Header fields + // + + ExInitializeFastMutex( &Dcb->NonPaged->AdvancedFcbHeaderMutex ); + FsRtlSetupAdvancedHeader( &Dcb->Header, + &Dcb->NonPaged->AdvancedFcbHeaderMutex ); + + // + // Initialize the Mcb + // + + FsRtlInitializeLargeMcb( &Dcb->Mcb, PagedPool ); + UnwindMcb = &Dcb->Mcb; + + // + // Set the file size, first cluster of file, and allocation size + // based on the information stored in the dirent + // + + Dcb->FirstClusterOfFile = (ULONG)Dirent->FirstClusterOfFile; + + if ( FatIsFat32(Dcb->Vcb) ) { + + Dcb->FirstClusterOfFile += Dirent->FirstClusterOfFileHi << 16; + } + + if ( Dcb->FirstClusterOfFile == 0 ) { + + Dcb->Header.AllocationSize.QuadPart = 0; + + } else { + + Dcb->Header.AllocationSize.QuadPart = FCB_LOOKUP_ALLOCATIONSIZE_HINT; + } + + + // initialize the notify queues, and the parent dcb queue. + // + + InitializeListHead( &Dcb->Specific.Dcb.ParentDcbQueue ); + + // + // Setup the free dirent bitmap buffer. Since we don't know the + // size of the directory, leave it zero for now. + // + + RtlInitializeBitMap( &Dcb->Specific.Dcb.FreeDirentBitmap, + NULL, + 0 ); + + // + // Set our two create dirent aids to represent that we have yet to + // enumerate the directory for never used or deleted dirents. + // + + Dcb->Specific.Dcb.UnusedDirentVbo = 0xffffffff; + Dcb->Specific.Dcb.DeletedDirentHint = 0xffffffff; + +#if (NTDDI_VERSION >= NTDDI_WIN8) + // + // Initialize the oplock structure. + // + + FsRtlInitializeOplock( FatGetFcbOplock(Dcb) ); + UnwindOplock = FatGetFcbOplock(Dcb); +#endif + + // + // Postpone initializing the cache map until we need to do a read/write + // of the directory file. + + + // + // set the file names. This must be the last thing we do. + // + + FatConstructNamesInFcb( IrpContext, + Dcb, + Dirent, + Lfn ); + + Dcb->ShortName.FileNameDos = TRUE; + + } finally { + + DebugUnwind( FatCreateDcb ); + + // + // If this is an abnormal termination then undo our work + // + + if (AbnormalTermination()) { + + ULONG i; + + if (UnwindOplock != NULL) { FsRtlUninitializeOplock( UnwindOplock ); } + if (UnwindMcb != NULL) { FsRtlUninitializeLargeMcb( UnwindMcb ); } + if (UnwindEntryList != NULL) { RemoveEntryList( UnwindEntryList ); } + if (UnwindResource != NULL) { FatFreeResource( UnwindResource ); } + if (UnwindResource2 != NULL) { FatFreeResource( UnwindResource2 ); } + + for (i = 0; i < sizeof(UnwindStorage)/sizeof(PVOID); i += 1) { + if (UnwindStorage[i] != NULL) { ExFreePool( UnwindStorage[i] ); } + } + } + + DebugTrace(-1, Dbg, "FatCreateDcb -> %p\n", Dcb); + } + + // + // return and tell the caller + // + + DebugTrace(-1, Dbg, "FatCreateDcb -> %p\n", Dcb); + + return Dcb; +} + + +VOID +FatDeleteFcb ( + IN PIRP_CONTEXT IrpContext, + IN PFCB *FcbPtr + ) + +/*++ + +Routine Description: + + This routine deallocates and removes an FCB, DCB, or ROOT DCB record + from Fat's in-memory data structures. It also will remove all + associated underlings (i.e., Notify irps, and child FCB/DCB records). + +Arguments: + + Fcb - Supplies the FCB/DCB/ROOT DCB to be removed + +Return Value: + + None + +--*/ + +{ + PFCB Fcb = *FcbPtr; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatDeleteFcb, Fcb = %p\n", Fcb); + + // + // We can only delete this record if the open count is zero. + // + + if (Fcb->OpenCount != 0) { + + DebugDump("Error deleting Fcb, Still Open\n", 0, Fcb); +#pragma prefast( suppress:28159, "things are seriously wrong if we get here" ) + FatBugCheck( 0, 0, 0 ); + } + + // + // Better be an FCB/DCB. + // + + if ((Fcb->Header.NodeTypeCode != FAT_NTC_DCB) && + (Fcb->Header.NodeTypeCode != FAT_NTC_ROOT_DCB) && + (Fcb->Header.NodeTypeCode != FAT_NTC_FCB)) { + +#pragma prefast( suppress:28159, "things are seriously wrong if we get here" ) + FatBugCheck( 0, 0, 0 ); + } + + // + // If this is a DCB then remove every Notify record from the two + // notify queues + // + + if ((Fcb->Header.NodeTypeCode == FAT_NTC_DCB) || + (Fcb->Header.NodeTypeCode == FAT_NTC_ROOT_DCB)) { + + // + // If we allocated a free dirent bitmap buffer, free it. + // + + if ((Fcb->Specific.Dcb.FreeDirentBitmap.Buffer != NULL) && + (Fcb->Specific.Dcb.FreeDirentBitmap.Buffer != + &Fcb->Specific.Dcb.FreeDirentBitmapBuffer[0])) { + + ExFreePool(Fcb->Specific.Dcb.FreeDirentBitmap.Buffer); + } + +#if (NTDDI_VERSION >= NTDDI_WIN8) + // + // Uninitialize the oplock. + // + + FsRtlUninitializeOplock( FatGetFcbOplock(Fcb) ); +#endif + + NT_ASSERT( Fcb->Specific.Dcb.DirectoryFileOpenCount == 0 ); + NT_ASSERT( IsListEmpty(&Fcb->Specific.Dcb.ParentDcbQueue) ); + NT_ASSERT( NULL == Fcb->Specific.Dcb.DirectoryFile); + + } else { + + // + // Uninitialize the byte range file locks and opportunistic locks + // + + FsRtlUninitializeFileLock( &Fcb->Specific.Fcb.FileLock ); + FsRtlUninitializeOplock( FatGetFcbOplock(Fcb) ); + } + + + // + // Release any Filter Context structures associated with this FCB + // + + FsRtlTeardownPerStreamContexts( &Fcb->Header ); + + // + // Uninitialize the Mcb + // + + FsRtlUninitializeLargeMcb( &Fcb->Mcb ); + + // + // If this is not the root dcb then we need to remove ourselves from + // our parents Dcb queue + // + + if (Fcb->Header.NodeTypeCode != FAT_NTC_ROOT_DCB) { + + RemoveEntryList( &(Fcb->ParentDcbLinks) ); + } + + // + // Remove the entry from the splay table if there is still is one. + // + + if (FlagOn( Fcb->FcbState, FCB_STATE_NAMES_IN_SPLAY_TREE )) { + + FatRemoveNames( IrpContext, Fcb ); + } + + // + // Free the file name pool if allocated. + // + + if (Fcb->Header.NodeTypeCode != FAT_NTC_ROOT_DCB) { + + // + // If we blew up at inconvenient times, the shortname + // could be null even though you will *never* see this + // normally. Rename is a good example of this case. + // + + if (Fcb->ShortName.Name.Oem.Buffer) { + + ExFreePool( Fcb->ShortName.Name.Oem.Buffer ); + } + + if (Fcb->FullFileName.Buffer) { + + ExFreePool( Fcb->FullFileName.Buffer ); + } + } + + if (Fcb->ExactCaseLongName.Buffer) { + + ExFreePool(Fcb->ExactCaseLongName.Buffer); + } + +#ifdef SYSCACHE_COMPILE + + if (Fcb->WriteMask) { + + ExFreePool( Fcb->WriteMask ); + } + +#endif + + // + // Finally deallocate the Fcb and non-paged fcb records + // + + FatFreeResource( Fcb->Header.Resource ); + + if (Fcb->Header.PagingIoResource != Fcb->Header.Resource) { + + FatFreeResource( Fcb->Header.PagingIoResource ); + } + + // + // If an Event was allocated, get rid of it. + // + + if (Fcb->NonPaged->OutstandingAsyncEvent) { + + ExFreePool( Fcb->NonPaged->OutstandingAsyncEvent ); + } + + FatFreeNonPagedFcb( Fcb->NonPaged ); + + Fcb->Header.NodeTypeCode = NTC_UNDEFINED; + + FatFreeFcb( Fcb ); + *FcbPtr = NULL; + + // + // and return to our caller + // + + DebugTrace(-1, Dbg, "FatDeleteFcb -> VOID\n", 0); +} + + +PCCB +FatCreateCcb ( + IN PIRP_CONTEXT IrpContext + ) + +/*++ + +Routine Description: + + This routine creates a new CCB record + +Arguments: + +Return Value: + + CCB - returns a pointer to the newly allocate CCB + +--*/ + +{ + PCCB Ccb; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatCreateCcb\n", 0); + + // + // Allocate a new CCB Record + // + + Ccb = FatAllocateCcb(); + + RtlZeroMemory( Ccb, sizeof(CCB) ); + + // + // Set the proper node type code and node byte size + // + + Ccb->NodeTypeCode = FAT_NTC_CCB; + Ccb->NodeByteSize = sizeof(CCB); + + // + // return and tell the caller + // + + DebugTrace(-1, Dbg, "FatCreateCcb -> %p\n", Ccb); + + UNREFERENCED_PARAMETER( IrpContext ); + + return Ccb; +} + + + +VOID +FatDeallocateCcbStrings( + IN PCCB Ccb + ) +/*++ + +Routine Description: + + This routine deallocates CCB query templates + +Arguments: + + Ccb - Supplies the CCB + +Return Value: + + None + +--*/ +{ + PAGED_CODE(); + + // + // If we allocated query template buffers, deallocate them now. + // + + if (FlagOn(Ccb->Flags, CCB_FLAG_FREE_UNICODE)) { + + NT_ASSERT( Ccb->UnicodeQueryTemplate.Buffer); + NT_ASSERT( !FlagOn( Ccb->Flags, CCB_FLAG_CLOSE_CONTEXT)); + RtlFreeUnicodeString( &Ccb->UnicodeQueryTemplate ); + } + + if (FlagOn(Ccb->Flags, CCB_FLAG_FREE_OEM_BEST_FIT)) { + + NT_ASSERT( Ccb->OemQueryTemplate.Wild.Buffer ); + NT_ASSERT( !FlagOn( Ccb->Flags, CCB_FLAG_CLOSE_CONTEXT)); + RtlFreeOemString( &Ccb->OemQueryTemplate.Wild ); + } + + ClearFlag( Ccb->Flags, CCB_FLAG_FREE_OEM_BEST_FIT | CCB_FLAG_FREE_UNICODE); +} + + + +VOID +FatDeleteCcb ( + IN PIRP_CONTEXT IrpContext, + IN PCCB *Ccb + ) + +/*++ + +Routine Description: + + This routine deallocates and removes the specified CCB record + from the Fat in memory data structures + +Arguments: + + Ccb - Supplies the CCB to remove + +Return Value: + + None + +--*/ + +{ + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatDeleteCcb, Ccb = %p\n", *Ccb); + + FatDeallocateCcbStrings( *Ccb); + + // + // Deallocate the Ccb record + // + + FatFreeCcb( *Ccb ); + *Ccb = NULL; + + // + // return and tell the caller + // + + DebugTrace(-1, Dbg, "FatDeleteCcb -> VOID\n", 0); + + UNREFERENCED_PARAMETER( IrpContext ); +} + + +PIRP_CONTEXT +FatCreateIrpContext ( + IN PIRP Irp, + IN BOOLEAN Wait + ) + +/*++ + +Routine Description: + + This routine creates a new IRP_CONTEXT record + +Arguments: + + Irp - Supplies the originating Irp. + + Wait - Supplies the wait value to store in the context + +Return Value: + + PIRP_CONTEXT - returns a pointer to the newly allocate IRP_CONTEXT Record + +--*/ + +{ + PIRP_CONTEXT IrpContext; + PIO_STACK_LOCATION IrpSp; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatCreateIrpContext\n", 0); + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + // + // 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 (FatDeviceIsFatFsdo( IrpSp->DeviceObject)) { + + 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 ); + } + + // + // Attemtp to allocate from the region first and failing that allocate + // from pool. + // + + DebugDoit( FatFsdEntryCount += 1); + + IrpContext = FatAllocateIrpContext(); + + // + // Zero out the irp context. + // + + RtlZeroMemory( IrpContext, sizeof(IRP_CONTEXT) ); + + // + // Set the proper node type code and node byte size + // + + IrpContext->NodeTypeCode = FAT_NTC_IRP_CONTEXT; + IrpContext->NodeByteSize = sizeof(IRP_CONTEXT); + + // + // Set the originating Irp field + // + + IrpContext->OriginatingIrp = Irp; + + // + // Major/Minor Function codes + // + + IrpContext->MajorFunction = IrpSp->MajorFunction; + IrpContext->MinorFunction = IrpSp->MinorFunction; + + // + // Copy RealDevice for workque algorithms, and also set Write Through + // and Removable Media if there is a file object. Only file system + // control Irps won't have a file object, and they should all have + // a Vpb as the first IrpSp location. + // + + if (IrpSp->FileObject != NULL) { + + PFILE_OBJECT FileObject = IrpSp->FileObject; + + IrpContext->RealDevice = FileObject->DeviceObject; + IrpContext->Vcb = &((PVOLUME_DEVICE_OBJECT)(IrpSp->DeviceObject))->Vcb; + + // + // See if the request is Write Through. Look for both FileObjects opened + // as write through, and non-cached requests with the SL_WRITE_THROUGH flag set. + // + // The latter can only originate from kernel components. (Note - NtWriteFile() + // does redundantly set the SL_W_T flag for all requests it issues on write + // through file objects) + // + + if (IsFileWriteThrough( FileObject, IrpContext->Vcb ) || + ( (IrpSp->MajorFunction == IRP_MJ_WRITE) && + BooleanFlagOn( Irp->Flags, IRP_NOCACHE) && + BooleanFlagOn( IrpSp->Flags, SL_WRITE_THROUGH))) { + + SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WRITE_THROUGH); + } + } else if (IrpContext->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL) { + + IrpContext->RealDevice = IrpSp->Parameters.MountVolume.Vpb->RealDevice; + } + + // + // Set the wait parameter + // + + if (Wait) { SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT); } + + // + // Set the recursive file system call parameter. We set it true if + // the TopLevelIrp field in the thread local storage is not the current + // irp, otherwise we leave it as FALSE. + // + + if ( IoGetTopLevelIrp() != Irp) { + + SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_RECURSIVE_CALL); + } + + // + // return and tell the caller + // + + DebugTrace(-1, Dbg, "FatCreateIrpContext -> %p\n", IrpContext); + + return IrpContext; +} + + + +VOID +FatDeleteIrpContext_Real ( + IN PIRP_CONTEXT IrpContext + ) + +/*++ + +Routine Description: + + This routine deallocates and removes the specified IRP_CONTEXT record + from the Fat in memory data structures. It should only be called + by FatCompleteRequest. + +Arguments: + + IrpContext - Supplies the IRP_CONTEXT to remove + +Return Value: + + None + +--*/ + +{ + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatDeleteIrpContext, IrpContext = %p\n", IrpContext); + + NT_ASSERT( IrpContext->NodeTypeCode == FAT_NTC_IRP_CONTEXT ); + NT_ASSERT( IrpContext->PinCount == 0 ); + + + // + // If there is a FatIoContext that was allocated, free it. + // + + if (IrpContext->FatIoContext != NULL) { + + if (!FlagOn(IrpContext->Flags, IRP_CONTEXT_STACK_IO_CONTEXT)) { + + if (IrpContext->FatIoContext->ZeroMdl) { + IoFreeMdl( IrpContext->FatIoContext->ZeroMdl ); + } + + ExFreePool( IrpContext->FatIoContext ); + } + } + + // + // Drop the IrpContext. + // + + FatFreeIrpContext( IrpContext ); + + // + // return and tell the caller + // + + DebugTrace(-1, Dbg, "FatDeleteIrpContext -> VOID\n", 0); + + return; +} + + +PFCB +FatGetNextFcbBottomUp ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb OPTIONAL, + IN PFCB TerminationFcb + ) + +/*++ + +Routine Description: + + This routine is used to iterate through Fcbs in a tree. In order to match + the lockorder for getting multiple Fcbs (so this can be used for acquiring + all Fcbs), this version does a bottom-up enumeration. + + This is different than the old one, now called TopDown. The problem with + lockorder was very well hidden. + + The transition rule is still pretty simple: + + A) If you have an adjacent sibling, go to it + 1) Descend to its leftmost child + B) Else go to your parent + + If this routine is called with in invalid TerminationFcb it will fail, + badly. + + The TerminationFcb is the last Fcb returned in the enumeration. + + This method is incompatible with the possibility that ancestors may vanish + based on operations done on the last returned node. For instance, + FatPurgeReferencedFileObjects cannot use BottomUp enumeration. + +Arguments: + + Fcb - Supplies the current Fcb. This is NULL if enumeration is starting. + + TerminationFcb - The root Fcb of the tree in which the enumeration starts + and at which it inclusively stops. + +Return Value: + + The next Fcb in the enumeration, or NULL if Fcb was the final one. + +--*/ + +{ + PFCB NextFcb; + + PAGED_CODE(); + UNREFERENCED_PARAMETER( IrpContext ); + + NT_ASSERT( FatVcbAcquiredExclusive( IrpContext, TerminationFcb->Vcb ) || + FlagOn( TerminationFcb->Vcb->VcbState, VCB_STATE_FLAG_LOCKED ) ); + + // + // Do we need to begin the enumeration? + // + + if (Fcb != NULL) { + + // + // Did we complete? + // + + if (Fcb == TerminationFcb) { + + return NULL; + } + + // + // Do we have a sibling to return? + // + + NextFcb = FatGetNextSibling( Fcb ); + + // + // If not, return our parent. We are done with this branch. + // + + if (NextFcb == NULL) { + + return Fcb->ParentDcb; + } + + } else { + + NextFcb = TerminationFcb; + } + + // + // Decend to its furthest child (if it exists) and return it. + // + + for (; + NodeType( NextFcb ) != FAT_NTC_FCB && FatGetFirstChild( NextFcb ) != NULL; + NextFcb = FatGetFirstChild( NextFcb )) { + } + + return NextFcb; +} + +PFCB +FatGetNextFcbTopDown ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN PFCB TerminationFcb + ) + +/*++ + +Routine Description: + + This routine is used to iterate through Fcbs in a tree, from the top down. + + The rule is very simple: + + A) If you have a child, go to it, else + B) If you have an older sibling, go to it, else + C) Go to your parent's older sibling. + + If this routine is called with in invalid TerminationFcb it will fail, + badly. + + The Termination Fcb is never returned. If it is the root of the tree you + are traversing, visit it first. + + This routine never returns direct ancestors of Fcb, and thus is useful when + making Fcb's go away (which may tear up the tree). + +Arguments: + + Fcb - Supplies the current Fcb + + TerminationFcb - The Fcb at which the enumeration should (non-inclusivly) + stop. Assumed to be a directory. + +Return Value: + + The next Fcb in the enumeration, or NULL if Fcb was the final one. + +--*/ + +{ + PFCB Sibling; + + PAGED_CODE(); + UNREFERENCED_PARAMETER( IrpContext ); + + NT_ASSERT( FatVcbAcquiredExclusive( IrpContext, Fcb->Vcb ) || + FlagOn( Fcb->Vcb->VcbState, VCB_STATE_FLAG_LOCKED ) ); + + // + // If this was a directory (ie. not a file), get the child. If + // there aren't any children and this is our termination Fcb, + // return NULL. + // + + if ( ((NodeType(Fcb) == FAT_NTC_DCB) || + (NodeType(Fcb) == FAT_NTC_ROOT_DCB)) && + !IsListEmpty(&Fcb->Specific.Dcb.ParentDcbQueue) ) { + + return FatGetFirstChild( Fcb ); + } + + // + // Were we only meant to do one iteration? + // + + if ( Fcb == TerminationFcb ) { + + return NULL; + } + + Sibling = FatGetNextSibling(Fcb); + + while (TRUE) { + + // + // Do we still have an "older" sibling in this directory who is + // not the termination Fcb? + // + + if ( Sibling != NULL ) { + + return (Sibling != TerminationFcb) ? Sibling : NULL; + } + + // + // OK, let's move on to out parent and see if he is the termination + // node or has any older siblings. + // + + if ( Fcb->ParentDcb == TerminationFcb ) { + + return NULL; + } + + Fcb = Fcb->ParentDcb; + + Sibling = FatGetNextSibling(Fcb); + } +} + + +BOOLEAN +FatSwapVpb ( + IN PIRP_CONTEXT IrpContext, + PVCB Vcb + ) + +/*++ + +Routine Description: + + This routine swaps the VPB for this VCB if it has not been done already. + This means the device object will get our spare VPB and we will cleanup + the one used while the volume was mounted. + +Arguments: + + IrpContext - Supplies the context for the overall request. + + Vcb - Supplies the VCB to swap the VPB on. + +Return Value: + + TRUE - If the VPB was actually swapped. + + FALSE - If the VPB was already swapped. + +--*/ + +{ + BOOLEAN Result = FALSE; + PVPB OldVpb; + PIO_STACK_LOCATION IrpSp; + + + // + // Make sure we have not already swapped it. + // + + OldVpb = Vcb->Vpb; + +#pragma prefast( push ) +#pragma prefast( disable: 28175, "touching Vpb is ok for a filesystem" ) + + if (!FlagOn( Vcb->VcbState, VCB_STATE_FLAG_VPB_MUST_BE_FREED ) && OldVpb->RealDevice->Vpb == OldVpb) { + + // + // If not the final reference and we are forcing the disconnect, + // 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. + // + + NT_ASSERT( Vcb->SwapVpb != NULL ); + + Vcb->SwapVpb->Type = IO_TYPE_VPB; + Vcb->SwapVpb->Size = sizeof( VPB ); + Vcb->SwapVpb->RealDevice = OldVpb->RealDevice; + + Vcb->SwapVpb->RealDevice->Vpb = Vcb->SwapVpb; + + Vcb->SwapVpb->Flags = FlagOn( OldVpb->Flags, VPB_REMOVE_PENDING ); + + // + // If we are working on a mount request, we need to make sure we update + // the VPB in the IRP, since the one it points to may no longer be valid. + // + + if (IrpContext->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL && IrpContext->MinorFunction == IRP_MN_MOUNT_VOLUME) { + + // + // Get the IRP stack. + // + + IrpSp = IoGetCurrentIrpStackLocation( IrpContext->OriginatingIrp ); + + NT_ASSERT( IrpSp->FileObject == NULL ); + + // + // Check the VPB in the IRP to see if it is the one we are swapping. + // + + if (IrpSp->Parameters.MountVolume.Vpb == OldVpb) { + + // + // Change the IRP to point to the swap VPB. + // + + IrpSp->Parameters.MountVolume.Vpb = Vcb->SwapVpb; + } + } + +#pragma prefast( pop ) + + // + // We place the volume in the Bad state (as opposed to NotMounted) so + // that it is not eligible for a remount. Also indicate we used up + // the swap. + // + + Vcb->SwapVpb = NULL; + FatSetVcbCondition( Vcb, VcbBad); + SetFlag( Vcb->VcbState, VCB_STATE_FLAG_VPB_MUST_BE_FREED ); + + Result = TRUE; + } + + return Result; +} + + +_Requires_lock_held_(_Global_critical_region_) +BOOLEAN +FatCheckForDismount ( + IN PIRP_CONTEXT IrpContext, + PVCB Vcb, + IN BOOLEAN Force + ) + +/*++ + +Routine Description: + + This routine determines if a volume is ready for deletion. It + correctly synchronizes with creates en-route to the file system. + +Arguments: + + Vcb - Supplies the volume to examine + + Force - Specifies whether we want this Vcb forcibly disconnected + from the driver stack if it will not be deleted (a new vpb will + be installed if neccesary). Caller is responsible for making + sure that the volume has been marked in such a way that attempts + to operate through the realdevice are blocked (i.e., move the vcb + out of the mounted state). + +Return Value: + + BOOLEAN - TRUE if the volume was deleted, FALSE otherwise. + +--*/ + +{ + KIRQL SavedIrql; + BOOLEAN VcbDeleted = FALSE; + + + // + // If the VCB condition is good and we are not forcing, just return. + // + + if (Vcb->VcbCondition == VcbGood && !Force) { + + return FALSE; + } + + // + // Now check for a zero Vpb count on an unmounted volume. These + // volumes will be deleted as they now have no file objects and + // there are no creates en route to this volume. + // + + IoAcquireVpbSpinLock( &SavedIrql ); + + if (Vcb->Vpb->ReferenceCount == Vcb->ResidualOpenCount && Vcb->OpenFileCount == 0) { + + PVPB Vpb = Vcb->Vpb; + +#if DBG + UNICODE_STRING VolumeLabel; + + // + // Setup the VolumeLabel string + // + + VolumeLabel.Length = Vcb->Vpb->VolumeLabelLength; + VolumeLabel.MaximumLength = MAXIMUM_VOLUME_LABEL_LENGTH; + VolumeLabel.Buffer = &Vcb->Vpb->VolumeLabel[0]; + + KdPrintEx((DPFLTR_FASTFAT_ID, + DPFLTR_INFO_LEVEL, + "FASTFAT: Dismounting Volume %Z\n", + &VolumeLabel)); +#endif // DBG + + // + // Swap this VCB's VPB. + // + + FatSwapVpb( IrpContext, + Vcb ); + + // + // Clear the VPB_MOUNTED bit. New opens should not come in due + // to the swapped VPB, but having the flag cleared helps debugging. + // Note that we must leave the Vpb->DeviceObject field set until + // after the FatTearDownVcb call as closes will have to make their + // way back to us. + // + + ClearFlag( Vpb->Flags, VPB_MOUNTED ); + + // + // If this Vpb was locked, clear this flag now. + // + + ClearFlag( Vpb->Flags, (VPB_LOCKED | VPB_DIRECT_WRITES_ALLOWED) ); + + IoReleaseVpbSpinLock( SavedIrql ); + + // + // We are going to attempt the dismount, so mark the VCB as having + // a dismount in progress. + // + + NT_ASSERT( !FlagOn( Vcb->VcbState, VCB_STATE_FLAG_DISMOUNT_IN_PROGRESS ) ); + SetFlag( Vcb->VcbState, VCB_STATE_FLAG_DISMOUNT_IN_PROGRESS ); + + // + // Close down our internal opens. + // + + FatTearDownVcb( IrpContext, + Vcb ); + + // + // Process any delayed closes. + // + + FatFspClose( Vcb ); + + // + // Grab the VPB lock again so that we can recheck our counts. + // + + IoAcquireVpbSpinLock( &SavedIrql ); + + // + // See if we can delete this VCB. + // + + if (Vcb->Vpb->ReferenceCount == 0 && Vcb->InternalOpenCount == 0) { + + Vpb->DeviceObject = NULL; + + IoReleaseVpbSpinLock( SavedIrql ); + + FatDeleteVcb( IrpContext, Vcb ); + + IoDeleteDevice( (PDEVICE_OBJECT) + CONTAINING_RECORD( Vcb, + VOLUME_DEVICE_OBJECT, + Vcb ) ); + + VcbDeleted = TRUE; + + } else { + + IoReleaseVpbSpinLock( SavedIrql ); + + NT_ASSERT( FlagOn( Vcb->VcbState, VCB_STATE_FLAG_DISMOUNT_IN_PROGRESS ) ); + ClearFlag( Vcb->VcbState, VCB_STATE_FLAG_DISMOUNT_IN_PROGRESS ); + } + + } else if (Force) { + + // + // The requester is forcing the issue. We need to swap the VPB with our spare. + // + + FatSwapVpb( IrpContext, + Vcb ); + + IoReleaseVpbSpinLock( SavedIrql ); + + } else { + + // + // Just drop the Vpb spinlock. + // + + IoReleaseVpbSpinLock( SavedIrql ); + } + + return VcbDeleted; +} + + +VOID +FatConstructNamesInFcb ( + IN PIRP_CONTEXT IrpContext, + PFCB Fcb, + PDIRENT Dirent, + PUNICODE_STRING Lfn OPTIONAL + ) + +/*++ + +Routine Description: + + This routine places the short name in the dirent in the first set of + STRINGs in the Fcb. If a long file name (Lfn) was specified, then + we must decide whether we will store its Oem equivolent in the same + prefix table as the short name, or rather just save the upcased + version of the UNICODE string in the FCB. + + For looking up Fcbs, the first approach will be faster, so we want to + do this as much as possible. Here are the rules that I have thought + through extensively to determine when it is safe to store only Oem + version of the UNICODE name. + + - If the UNICODE name contains no extended characters (>0x80), use Oem. + + - Let U be the upcased version of the UNICODE name. + Let Up(x) be the function that upcases a UNICODE string. + Let Down(x) be the function that upcases a UNICODE string. + Let OemToUni(x) be the function that converts an Oem string to Unicode. + Let UniToOem(x) be the function that converts a Unicode string to Oem. + Let BestOemFit(x) be the function that creates the Best uppercase Oem + fit for the UNICODE string x. + + BestOemFit(x) = UniToOem(Up(OemToUni(UniToOem(x)))) <1> + + if (BestOemFit(U) == BestOemFit(Down(U)) <2> + + then I know that there exists no UNICODE string Y such that: + + Up(Y) == Up(U) <3> + + AND + + BestOemFit(U) != BestOemFit(Y) <4> + + Consider string U as a collection of one character strings. The + conjecture is clearly true for each sub-string, thus it is true + for the entire string. + + Equation <1> is what we use to convert an incoming unicode name in + FatCommonCreate() to Oem. The double conversion is done to provide + better visual best fitting for characters in the Ansi code page but + not in the Oem code page. A single Nls routine is provided to do + this conversion efficiently. + + The idea is that with U, I only have to worry about a case varient Y + matching it in a unicode compare, and I have shown that any case varient + of U (the set Y defined in equation <3>), when filtered through <1> + (as in create), will match the Oem string defined in <1>. + + Thus I do not have to worry about another UNICODE string missing in + the prefix lookup, but matching when comparing LFNs in the directory. + +Arguments: + + Fcb - The Fcb we are supposed to fill in. Note that ParentDcb must + already be filled in. + + Dirent - The gives up the short name. + + Lfn - If provided, this gives us the long name. + +Return Value: + + None + +--*/ + +{ + NTSTATUS Status; + ULONG i; + + OEM_STRING OemA; + OEM_STRING OemB; + UNICODE_STRING Unicode; + POEM_STRING ShortName; + POEM_STRING LongOemName; + PUNICODE_STRING LongUniName; + + PAGED_CODE(); + + ShortName = &Fcb->ShortName.Name.Oem; + + NT_ASSERT( ShortName->Buffer == NULL ); + + try { + + // + // First do the short name. + // + + // + // Copy over the case flags for the short name of the file + // + + if (FlagOn(Dirent->NtByte, FAT_DIRENT_NT_BYTE_8_LOWER_CASE)) { + + SetFlag(Fcb->FcbState, FCB_STATE_8_LOWER_CASE); + + } else { + + ClearFlag(Fcb->FcbState, FCB_STATE_8_LOWER_CASE); + } + + if (FlagOn(Dirent->NtByte, FAT_DIRENT_NT_BYTE_3_LOWER_CASE)) { + + SetFlag(Fcb->FcbState, FCB_STATE_3_LOWER_CASE); + + } else { + + ClearFlag(Fcb->FcbState, FCB_STATE_3_LOWER_CASE); + } + + ShortName->MaximumLength = 16; + ShortName->Buffer = FsRtlAllocatePoolWithTag( PagedPool, + 16, + TAG_FILENAME_BUFFER ); + + Fat8dot3ToString( IrpContext, Dirent, FALSE, ShortName ); + + Fcb->ShortName.FileNameDos = TRUE; + + // + // If no Lfn was specified, we are done. In either case, set the + // final name length. + // + + NT_ASSERT( Fcb->ExactCaseLongName.Buffer == NULL ); + + if (!ARGUMENT_PRESENT(Lfn) || (Lfn->Length == 0)) { + + Fcb->FinalNameLength = (USHORT) RtlOemStringToCountedUnicodeSize( ShortName ); + Fcb->ExactCaseLongName.Length = Fcb->ExactCaseLongName.MaximumLength = 0; + + try_return( NOTHING ); + } + + // + // If we already set up the full filename, we could be in trouble. If the fast + // path for doing it already fired, FatSetFullFileNameInFcb, it will have missed + // this and could have built the full filename out of the shortname of the file. + // + // At that point, disaster could be inevitable since the final name length will not + // match. We use this to tell the notify package what to do - FatNotifyReportChange. + // + + NT_ASSERT( Fcb->FullFileName.Buffer == NULL ); + + // + // We know now we have an Lfn, save away a copy. + // + + Fcb->FinalNameLength = Lfn->Length; + + Fcb->ExactCaseLongName.Length = Fcb->ExactCaseLongName.MaximumLength = Lfn->Length; + Fcb->ExactCaseLongName.Buffer = FsRtlAllocatePoolWithTag( PagedPool, + Lfn->Length, + TAG_FILENAME_BUFFER ); + RtlCopyMemory(Fcb->ExactCaseLongName.Buffer, Lfn->Buffer, Lfn->Length); + + // + // First check for no extended characters. + // + + for (i=0; i < Lfn->Length/sizeof(WCHAR); i++) { + + if (Lfn->Buffer[i] >= 0x80) { + + break; + } + } + + if (i == Lfn->Length/sizeof(WCHAR)) { + + // + // Cool, I can go with the Oem, upcase it fast by hand. + // + + LongOemName = &Fcb->LongName.Oem.Name.Oem; + + + LongOemName->Buffer = FsRtlAllocatePoolWithTag( PagedPool, + Lfn->Length/sizeof(WCHAR), + TAG_FILENAME_BUFFER ); + LongOemName->Length = + LongOemName->MaximumLength = Lfn->Length/sizeof(WCHAR); + + for (i=0; i < Lfn->Length/sizeof(WCHAR); i++) { + + WCHAR c; + + c = Lfn->Buffer[i]; + +#pragma warning( push ) +#pragma warning( disable:4244 ) + LongOemName->Buffer[i] = c < 'a' ? + (UCHAR)c : + c <= 'z' ? + c - (UCHAR)('a'-'A') : + (UCHAR)c; +#pragma warning( pop ) + } + + // + // If this name happens to be exactly the same as the short + // name, don't add it to the splay table. + // + + if (FatAreNamesEqual(IrpContext, *ShortName, *LongOemName) || + (FatFindFcb( IrpContext, + &Fcb->ParentDcb->Specific.Dcb.RootOemNode, + LongOemName, + NULL) != NULL)) { + + ExFreePool( LongOemName->Buffer ); + + LongOemName->Buffer = NULL; + LongOemName->Length = + LongOemName->MaximumLength = 0; + + } else { + + SetFlag( Fcb->FcbState, FCB_STATE_HAS_OEM_LONG_NAME ); + } + + try_return( NOTHING ); + } + + // + // Now we have the fun part. Make a copy of the Lfn. + // + + OemA.Buffer = NULL; + OemB.Buffer = NULL; + Unicode.Buffer = NULL; + + Unicode.Length = + Unicode.MaximumLength = Lfn->Length; + Unicode.Buffer = FsRtlAllocatePoolWithTag( PagedPool, + Lfn->Length, + TAG_FILENAME_BUFFER ); + + RtlCopyMemory( Unicode.Buffer, Lfn->Buffer, Lfn->Length ); + + Status = STATUS_SUCCESS; + +#if TRUE + // + // Unfortunately, this next block of code breaks down when you have + // two long Unicode filenames that both map to the same Oem (and are, + // well, long, i.e. are not the short names). In this case, with one + // in the prefix table first, the other will hit the common Oem + // representation. This leads to several forms of user astonishment. + // + // It isn't worth it, or probably even possible, to try to figure out + // when this is really safe to go through. Simply omit the attempt. + // + // Ex: ANSI 0x82 and 0x84 in the 1252 ANSI->UNI and 437 UNI->OEM codepages. + // + // 0x82 => 0x201a => 0x2c + // 0x84 => 0x201e => 0x2c + // + // 0x2c is comma, so is FAT Oem illegal and forces shortname generation. + // Since it is otherwise well-formed by the rules articulated previously, + // we would have put 0x2c in the Oem prefix tree. In terms of the + // argument given above, even though there exist no Y and U s.t. + // + // Up(Y) == Up(U) && BestOemFit(U) != BestOemFit(Y) + // + // there most certainly exist Y and U s.t. + // + // Up(Y) != Up(U) && BestOemFit(U) == BestOemFit(Y) + // + // and that is enough to keep us from doing this. Note that the < 0x80 + // case is OK since we know that the mapping in the OEM codepages are + // the identity in that range. + // + // We still need to monocase it, though. Do this through a full down/up + // transition. + // + + (VOID)RtlDowncaseUnicodeString( &Unicode, &Unicode, FALSE ); + (VOID)RtlUpcaseUnicodeString( &Unicode, &Unicode, FALSE ); +#else + // + // Downcase and convert to upcased Oem. Only continue if we can + // convert without error. Any error other than UNMAPPABLE_CHAR + // is a fatal error and we raise. + // + // Note that even if the conversion fails, we must leave Unicode + // in an upcased state. + // + // NB: The Rtl doesn't NULL .Buffer on error. + // + + (VOID)RtlDowncaseUnicodeString( &Unicode, &Unicode, FALSE ); + Status = RtlUpcaseUnicodeStringToCountedOemString( &OemA, &Unicode, TRUE ); + (VOID)RtlUpcaseUnicodeString( &Unicode, &Unicode, FALSE ); + + if (!NT_SUCCESS(Status)) { + + if (Status != STATUS_UNMAPPABLE_CHARACTER) { + + NT_ASSERT( Status == STATUS_NO_MEMORY ); + ExFreePool(Unicode.Buffer); + FatNormalizeAndRaiseStatus( IrpContext, Status ); + } + + } else { + + // + // The same as above except upcase. + // + + Status = RtlUpcaseUnicodeStringToCountedOemString( &OemB, &Unicode, TRUE ); + + if (!NT_SUCCESS(Status)) { + + RtlFreeOemString( &OemA ); + + if (Status != STATUS_UNMAPPABLE_CHARACTER) { + + NT_ASSERT( Status == STATUS_NO_MEMORY ); + ExFreePool(Unicode.Buffer); + FatNormalizeAndRaiseStatus( IrpContext, Status ); + } + } + } + + // + // If the final OemNames are equal, I can use save only the Oem + // name. If the name did not map, then I have to go with the UNICODE + // name because I could get a case varient that didn't convert + // in create, but did match the LFN. + // + + if (NT_SUCCESS(Status) && FatAreNamesEqual( IrpContext, OemA, OemB )) { + + // + // Cool, I can go with the Oem. If we didn't convert correctly, + // get a fresh convert from the original LFN. + // + + ExFreePool(Unicode.Buffer); + + RtlFreeOemString( &OemB ); + + Fcb->LongName.Oem.Name.Oem = OemA; + + // + // If this name happens to be exactly the same as the short + // name, or a similar short name already exists don't add it + // to the splay table (note the final condition implies a + // corrupt disk. + // + + if (FatAreNamesEqual(IrpContext, *ShortName, OemA) || + (FatFindFcb( IrpContext, + &Fcb->ParentDcb->Specific.Dcb.RootOemNode, + &OemA, + NULL) != NULL)) { + + RtlFreeOemString( &OemA ); + + } else { + + SetFlag( Fcb->FcbState, FCB_STATE_HAS_OEM_LONG_NAME ); + } + + try_return( NOTHING ); + } + + // + // The long name must be left in UNICODE. Free the two Oem strings + // if we got here just because they weren't equal. + // + + if (NT_SUCCESS(Status)) { + + RtlFreeOemString( &OemA ); + RtlFreeOemString( &OemB ); + } +#endif + + LongUniName = &Fcb->LongName.Unicode.Name.Unicode; + + LongUniName->Length = + LongUniName->MaximumLength = Unicode.Length; + LongUniName->Buffer = Unicode.Buffer; + + SetFlag(Fcb->FcbState, FCB_STATE_HAS_UNICODE_LONG_NAME); + + try_exit: NOTHING; + } finally { + + if (AbnormalTermination()) { + + if (ShortName->Buffer != NULL) { + + ExFreePool( ShortName->Buffer ); + ShortName->Buffer = NULL; + } + + } else { + + // + // Creating all the names worked, so add all the names + // to the splay tree. + // + + FatInsertName( IrpContext, + &Fcb->ParentDcb->Specific.Dcb.RootOemNode, + &Fcb->ShortName ); + + Fcb->ShortName.Fcb = Fcb; + + if (FlagOn(Fcb->FcbState, FCB_STATE_HAS_OEM_LONG_NAME)) { + + FatInsertName( IrpContext, + &Fcb->ParentDcb->Specific.Dcb.RootOemNode, + &Fcb->LongName.Oem ); + + Fcb->LongName.Oem.Fcb = Fcb; + } + + if (FlagOn(Fcb->FcbState, FCB_STATE_HAS_UNICODE_LONG_NAME)) { + + FatInsertName( IrpContext, + &Fcb->ParentDcb->Specific.Dcb.RootUnicodeNode, + &Fcb->LongName.Unicode ); + + Fcb->LongName.Unicode.Fcb = Fcb; + } + + SetFlag(Fcb->FcbState, FCB_STATE_NAMES_IN_SPLAY_TREE); + } + } + + return; +} + + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatCheckFreeDirentBitmap ( + IN PIRP_CONTEXT IrpContext, + IN PDCB Dcb + ) + +/*++ + +Routine Description: + + This routine checks if the size of the free dirent bitmap is + sufficient to for the current directory size. It is called + whenever we grow a directory. + +Arguments: + + Dcb - Supplies the directory in question. + +Return Value: + + None + +--*/ + +{ + ULONG OldNumberOfDirents; + ULONG NewNumberOfDirents; + + PAGED_CODE(); + UNREFERENCED_PARAMETER( IrpContext ); + + // + // Setup the Bitmap buffer if it is not big enough already + // + + NT_ASSERT( Dcb->Header.AllocationSize.QuadPart != FCB_LOOKUP_ALLOCATIONSIZE_HINT ); + + OldNumberOfDirents = Dcb->Specific.Dcb.FreeDirentBitmap.SizeOfBitMap; + NewNumberOfDirents = Dcb->Header.AllocationSize.LowPart / sizeof(DIRENT); + + // + // Do the usual unsync/sync check. + // + + if (NewNumberOfDirents > OldNumberOfDirents) { + + FatAcquireDirectoryFileMutex( Dcb->Vcb ); + + try { + + PULONG OldBitmapBuffer; + PULONG BitmapBuffer; + + ULONG BytesInBitmapBuffer; + ULONG BytesInOldBitmapBuffer; + + OldNumberOfDirents = Dcb->Specific.Dcb.FreeDirentBitmap.SizeOfBitMap; + NewNumberOfDirents = Dcb->Header.AllocationSize.LowPart / sizeof(DIRENT); + + if (NewNumberOfDirents > OldNumberOfDirents) { + + // + // Remember the old bitmap + // + + OldBitmapBuffer = Dcb->Specific.Dcb.FreeDirentBitmap.Buffer; + + // + // Now make a new bitmap bufffer + // + + BytesInBitmapBuffer = NewNumberOfDirents / 8; + + BytesInOldBitmapBuffer = OldNumberOfDirents / 8; + + if (DCB_UNION_SLACK_SPACE >= BytesInBitmapBuffer) { + + BitmapBuffer = &Dcb->Specific.Dcb.FreeDirentBitmapBuffer[0]; + + } else { + + BitmapBuffer = FsRtlAllocatePoolWithTag( PagedPool, + BytesInBitmapBuffer, + TAG_DIRENT_BITMAP ); + } + + // + // Copy the old buffer to the new buffer, free the old one, and zero + // the rest of the new one. Only do the first two steps though if + // we moved out of the initial buffer. + // + + if ((OldNumberOfDirents != 0) && + (BitmapBuffer != &Dcb->Specific.Dcb.FreeDirentBitmapBuffer[0])) { + + RtlCopyMemory( BitmapBuffer, + OldBitmapBuffer, + BytesInOldBitmapBuffer ); + + if (OldBitmapBuffer != &Dcb->Specific.Dcb.FreeDirentBitmapBuffer[0]) { + + ExFreePool( OldBitmapBuffer ); + } + } + + NT_ASSERT( BytesInBitmapBuffer > BytesInOldBitmapBuffer ); + + RtlZeroMemory( (PUCHAR)BitmapBuffer + BytesInOldBitmapBuffer, + BytesInBitmapBuffer - BytesInOldBitmapBuffer ); + + // + // Now initialize the new bitmap. + // + + RtlInitializeBitMap( &Dcb->Specific.Dcb.FreeDirentBitmap, + BitmapBuffer, + NewNumberOfDirents ); + } + + } finally { + + FatReleaseDirectoryFileMutex( Dcb->Vcb ); + } + } +} + + +BOOLEAN +FatIsHandleCountZero ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb + ) + +/*++ + +Routine Description: + + This routine decides if the handle count on the volume is zero. + +Arguments: + + Vcb - The volume in question + +Return Value: + + BOOLEAN - TRUE if there are no open handles on the volume, FALSE + otherwise. + +--*/ + +{ + PFCB Fcb; + + PAGED_CODE(); + + Fcb = Vcb->RootDcb; + + while (Fcb != NULL) { + + if (Fcb->UncleanCount != 0) { + + return FALSE; + } + + Fcb = FatGetNextFcbTopDown(IrpContext, Fcb, Vcb->RootDcb); + } + + return TRUE; +} + + +PCLOSE_CONTEXT + +FatAllocateCloseContext( + OPTIONAL PVCB Vcb + ) +/*++ + +Routine Description: + + This routine preallocates a close context, presumeably on behalf + of a fileobject which does not have a structure we can embed one + in. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + PAGED_CODE(); + UNREFERENCED_PARAMETER( Vcb ); + +#if DBG + if (ARGUMENT_PRESENT(Vcb)) { + + NT_ASSERT( 0 != Vcb->CloseContextCount); + InterlockedDecrement( (LONG*)&Vcb->CloseContextCount); + } +#endif + return (PCLOSE_CONTEXT)ExInterlockedPopEntrySList( &FatCloseContextSList, + &FatData.GeneralSpinLock ); +} + + +VOID +FatPreallocateCloseContext ( + PVCB Vcb + ) + +/*++ + +Routine Description: + + This routine preallocates a close context, presumeably on behalf + of a fileobject which does not have a structure we can embed one + in. + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + PCLOSE_CONTEXT CloseContext; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER( Vcb ); + + CloseContext = FsRtlAllocatePoolWithTag( PagedPool, + sizeof(CLOSE_CONTEXT), + TAG_FAT_CLOSE_CONTEXT ); + + ExInterlockedPushEntrySList( &FatCloseContextSList, + (PSLIST_ENTRY) CloseContext, + &FatData.GeneralSpinLock ); + + DbgDoit( InterlockedIncrement( (LONG*)&Vcb->CloseContextCount)); +} + + + +VOID +FatEnsureStringBufferEnough ( + _Inout_ PVOID String, + _In_ USHORT DesiredBufferSize + ) + +/*++ + +Routine Description: + + Ensures that a string string (STRING, UNICODE_STRING, ANSI_STRING, OEM_STRING) + has a buffer >= DesiredBufferSize, allocating from pool if neccessary. Any + existing pool buffer will be freed if a new one is allocated. + + NOTE: No copy of old buffer contents is performed on reallocation. + + Will raise on allocation failure. + +Arguments: + + String - pointer to string structure + + DesiredBufferSize - (bytes) minimum required buffer size + +--*/ + +{ + PSTRING LocalString = String; + + PAGED_CODE(); + + if (LocalString->MaximumLength < DesiredBufferSize) { + + FatFreeStringBuffer( LocalString); + + LocalString->Buffer = FsRtlAllocatePoolWithTag( PagedPool, + DesiredBufferSize, + TAG_DYNAMIC_NAME_BUFFER); + NT_ASSERT( LocalString->Buffer); + + LocalString->MaximumLength = DesiredBufferSize; + } +} + + +VOID +FatFreeStringBuffer ( + _Inout_ PVOID String + ) + +/*++ + +Routine Description: + + Frees the buffer of an string (STRING, UNICODE_STRING, ANSI_STRING, OEM_STRING) + structure if it is not within the current thread's stack limits. + + Regardless of action performed, on exit String->Buffer will be set to NULL and + String->MaximumLength to zero. + +Arguments: + + String - pointer to string structure + +--*/ + +{ + ULONG_PTR High, Low; + PSTRING LocalString = String; + + PAGED_CODE(); + + if (NULL != LocalString->Buffer) { + + IoGetStackLimits( &Low, &High ); + + if (((ULONG_PTR)(LocalString->Buffer) < Low) || + ((ULONG_PTR)(LocalString->Buffer) > High)) { + + ExFreePool( LocalString->Buffer); + } + + LocalString->Buffer = NULL; + } + + LocalString->MaximumLength = LocalString->Length = 0; +} + + +BOOLEAN +FatScanForDataTrack( + IN PIRP_CONTEXT IrpContext, + IN PDEVICE_OBJECT TargetDeviceObject + ) + +/*++ + +Routine Description: + + This routine is called to verify and process the TOC for this disk. + + FAT queries for the TOC to avoid trying to mount on CD-DA/CD-E media, Doing data reads on + audio/leadin of that media sends a lot of drives into what could charitably be called + "conniptions" which take a couple seconds to clear and would also convince FAT that the + device was busted, and fail the mount (not letting CDFS get its crack). + + There is special handling of PD media. These things fail the TOC read, but return + a special error code so FAT knows to continue to try the mount anyway. + +Arguments: + + TargetDeviceObject - Device object to send TOC request to. + +Return Value: + + BOOLEAN - TRUE if we found a TOC with a single data track. + +--*/ + +{ + NTSTATUS Status; + IO_STATUS_BLOCK Iosb; + + ULONG LocalTrackCount; + ULONG LocalTocLength; + + PCDROM_TOC CdromToc; + BOOLEAN Result = FALSE; + + PAGED_CODE(); + + CdromToc = FsRtlAllocatePoolWithTag( PagedPool, + sizeof( CDROM_TOC ), + TAG_IO_BUFFER ); + + RtlZeroMemory( CdromToc, sizeof( CDROM_TOC )); + + try { + + // + // Go ahead and read the table of contents + // + + Status = FatPerformDevIoCtrl( IrpContext, + IOCTL_CDROM_READ_TOC, + TargetDeviceObject, + NULL, + 0, + CdromToc, + sizeof( CDROM_TOC ), + FALSE, + TRUE, + &Iosb ); + + // + // Nothing to process if this request fails. + // + + if (Status != STATUS_SUCCESS) { + + // + // If we get the special error indicating a failed TOC read on PD media just + // plow ahead with the mount (see comments above). + // + + if ((Status == STATUS_IO_DEVICE_ERROR) || (Status == STATUS_INVALID_DEVICE_REQUEST)) { + + Result = TRUE; + + } + + try_leave( NOTHING ); + } + + // + // Get the number of tracks and stated size of this structure. + // + + LocalTrackCount = CdromToc->LastTrack - CdromToc->FirstTrack + 1; + LocalTocLength = PtrOffset( CdromToc, &CdromToc->TrackData[LocalTrackCount + 1] ); + + // + // Get out if there is an immediate problem with the TOC, or more than + // one track. + // + + if ((LocalTocLength > Iosb.Information) || + (CdromToc->FirstTrack > CdromToc->LastTrack) || + (LocalTrackCount != 1)) { + + try_leave( NOTHING); + } + + // + // Is it a data track? DVD-RAM reports single, data, track. + // + + Result = BooleanFlagOn( CdromToc->TrackData[ 0].Control, 0x04 ); + } + finally { + + ExFreePool( CdromToc); + } + + return Result; +} + + diff --git a/filesys/fastfat/timesup.c b/filesys/fastfat/timesup.c new file mode 100644 index 000000000..659c9514c --- /dev/null +++ b/filesys/fastfat/timesup.c @@ -0,0 +1,371 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + TimeSup.c + +Abstract: + + This module implements the Fat Time conversion support routines + + +--*/ + +#include "FatProcs.h" + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, FatNtTimeToFatTime) +#pragma alloc_text(PAGE, FatFatDateToNtTime) +#pragma alloc_text(PAGE, FatFatTimeToNtTime) +#pragma alloc_text(PAGE, FatGetCurrentFatTime) +#endif + +_Success_(return != FALSE) +BOOLEAN +FatNtTimeToFatTime ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PLARGE_INTEGER NtTime, + _In_ BOOLEAN Rounding, + _Out_ PFAT_TIME_STAMP FatTime, + _Out_opt_ PUCHAR TenMsecs + ) + +/*++ + +Routine Description: + + This routine converts an NtTime value to its corresponding Fat time value. + +Arguments: + + NtTime - Supplies the Nt GMT Time value to convert from + + Rounding - Indicates whether the NT time should be rounded up to a FAT boundary. + This should only be done *once* in the lifetime of a timestamp (important + for tunneling, which will cause a timestamp to pass through at least twice). + If true, rounded up. If false, rounded down to 10ms boundary. This obeys + the rules for non-creation time and creation times (respectively). + + FatTime - Receives the equivalent Fat time value + + TenMsecs - Optionally receive the number of tens of milliseconds the NtTime, after + any rounding, is greater than the FatTime + +Return Value: + + BOOLEAN - TRUE if the Nt time value is within the range of Fat's + time range, and FALSE otherwise + +--*/ + +{ + TIME_FIELDS TimeFields; + + PAGED_CODE(); + + // + // Convert the input to the a time field record. + // + + if (Rounding) { + + // + // Add almost two seconds to round up to the nearest double second. + // + + NtTime->QuadPart = NtTime->QuadPart + AlmostTwoSeconds; + } + + ExSystemTimeToLocalTime( NtTime, NtTime ); + + RtlTimeToTimeFields( NtTime, &TimeFields ); + + // + // Check the range of the date found in the time field record + // + + if ((TimeFields.Year < 1980) || (TimeFields.Year > (1980 + 127))) { + + ExLocalTimeToSystemTime( NtTime, NtTime ); + + return FALSE; + } + + // + // The year will fit in Fat so simply copy over the information + // + + FatTime->Time.DoubleSeconds = (USHORT)(TimeFields.Second / 2); + FatTime->Time.Minute = (USHORT)(TimeFields.Minute); + FatTime->Time.Hour = (USHORT)(TimeFields.Hour); + + FatTime->Date.Year = (USHORT)(TimeFields.Year - 1980); + FatTime->Date.Month = (USHORT)(TimeFields.Month); + FatTime->Date.Day = (USHORT)(TimeFields.Day); + + if (TenMsecs) { + + if (!Rounding) { + + // + // If the number of seconds was not divisible by two, then there + // is another second of time (1 sec, 3 sec, etc.) Note we round down + // the number of milleconds onto tens of milleseconds boundaries. + // + +#pragma warning( push ) +#pragma warning( disable: 4244 ) + *TenMsecs = (TimeFields.Milliseconds / 10) + + ((TimeFields.Second % 2) * 100); +#pragma warning( pop ) + } else { + + // + // If we rounded up, we have in effect changed the NT time. Therefore, + // it does not differ from the FAT time. + // + + *TenMsecs = 0; + } + } + + if (Rounding) { + + // + // Slice off non-FAT boundary time and convert back to 64bit form + // + + TimeFields.Milliseconds = 0; + TimeFields.Second -= TimeFields.Second % 2; + + } else { + + // + // Round down to 10ms boundary + // + + TimeFields.Milliseconds -= TimeFields.Milliseconds % 10; + } + + // + // Convert back to NT time + // + + (VOID) RtlTimeFieldsToTime(&TimeFields, NtTime); + + ExLocalTimeToSystemTime( NtTime, NtTime ); + + UNREFERENCED_PARAMETER( IrpContext ); + + return TRUE; +} + + +LARGE_INTEGER +FatFatDateToNtTime ( + _In_ PIRP_CONTEXT IrpContext, + _In_ FAT_DATE FatDate + ) + +/*++ + +Routine Description: + + This routine converts a Fat datev value to its corresponding Nt GMT + Time value. + +Arguments: + + FatDate - Supplies the Fat Date to convert from + +Return Value: + + LARGE_INTEGER - Receives the corresponding Nt Time value + +--*/ + +{ + TIME_FIELDS TimeFields; + LARGE_INTEGER Time; + + PAGED_CODE(); + + // + // Pack the input time/date into a time field record + // + + TimeFields.Year = (USHORT)(FatDate.Year + 1980); + TimeFields.Month = (USHORT)(FatDate.Month); + TimeFields.Day = (USHORT)(FatDate.Day); + TimeFields.Hour = (USHORT)0; + TimeFields.Minute = (USHORT)0; + TimeFields.Second = (USHORT)0; + TimeFields.Milliseconds = (USHORT)0; + + // + // Convert the time field record to Nt LARGE_INTEGER, and set it to zero + // if we were given a bogus time. + // + + if (!RtlTimeFieldsToTime( &TimeFields, &Time )) { + + Time.LowPart = 0; + Time.HighPart = 0; + + } else { + + ExLocalTimeToSystemTime( &Time, &Time ); + } + + return Time; + + UNREFERENCED_PARAMETER( IrpContext ); +} + + +LARGE_INTEGER +FatFatTimeToNtTime ( + _In_ PIRP_CONTEXT IrpContext, + _In_ FAT_TIME_STAMP FatTime, + _In_ UCHAR TenMilliSeconds + ) + +/*++ + +Routine Description: + + This routine converts a Fat time value pair to its corresponding Nt GMT + Time value. + +Arguments: + + FatTime - Supplies the Fat Time to convert from + + TenMilliSeconds - A 10 Milisecond resolution + +Return Value: + + LARGE_INTEGER - Receives the corresponding Nt GMT Time value + +--*/ + +{ + TIME_FIELDS TimeFields; + LARGE_INTEGER Time; + + PAGED_CODE(); + + // + // Pack the input time/date into a time field record + // + + TimeFields.Year = (USHORT)(FatTime.Date.Year + 1980); + TimeFields.Month = (USHORT)(FatTime.Date.Month); + TimeFields.Day = (USHORT)(FatTime.Date.Day); + TimeFields.Hour = (USHORT)(FatTime.Time.Hour); + TimeFields.Minute = (USHORT)(FatTime.Time.Minute); + TimeFields.Second = (USHORT)(FatTime.Time.DoubleSeconds * 2); + + if (TenMilliSeconds != 0) { + + TimeFields.Second += (USHORT)(TenMilliSeconds / 100); + TimeFields.Milliseconds = (USHORT)((TenMilliSeconds % 100) * 10); + + } else { + + TimeFields.Milliseconds = (USHORT)0; + } + + // + // If the second value is greater than 59 then we truncate it to 0. + // Note that this can't happen with a proper FAT timestamp. + // + + if (TimeFields.Second > 59) { + + TimeFields.Second = 0; + } + + // + // Convert the time field record to Nt LARGE_INTEGER, and set it to zero + // if we were given a bogus time. + // + + if (!RtlTimeFieldsToTime( &TimeFields, &Time )) { + + Time.LowPart = 0; + Time.HighPart = 0; + + } else { + + ExLocalTimeToSystemTime( &Time, &Time ); + } + + return Time; + + UNREFERENCED_PARAMETER( IrpContext ); +} + + +FAT_TIME_STAMP +FatGetCurrentFatTime ( + _In_ PIRP_CONTEXT IrpContext + ) + +/*++ + +Routine Description: + + This routine returns the current system time in Fat time + +Arguments: + +Return Value: + + FAT_TIME_STAMP - Receives the current system time + +--*/ + +{ + LARGE_INTEGER Time; + TIME_FIELDS TimeFields; + FAT_TIME_STAMP FatTime; + + PAGED_CODE(); + + // + // Get the current system time, and map it into a time field record. + // + + KeQuerySystemTime( &Time ); + + ExSystemTimeToLocalTime( &Time, &Time ); + + // + // Always add almost two seconds to round up to the nearest double second. + // + + Time.QuadPart = Time.QuadPart + AlmostTwoSeconds; + + (VOID)RtlTimeToTimeFields( &Time, &TimeFields ); + + // + // Now simply copy over the information + // + + FatTime.Time.DoubleSeconds = (USHORT)(TimeFields.Second / 2); + FatTime.Time.Minute = (USHORT)(TimeFields.Minute); + FatTime.Time.Hour = (USHORT)(TimeFields.Hour); + + FatTime.Date.Year = (USHORT)(TimeFields.Year - 1980); + FatTime.Date.Month = (USHORT)(TimeFields.Month); + FatTime.Date.Day = (USHORT)(TimeFields.Day); + + UNREFERENCED_PARAMETER( IrpContext ); + + return FatTime; +} + + diff --git a/filesys/fastfat/verfysup.c b/filesys/fastfat/verfysup.c new file mode 100644 index 000000000..5336bdf18 --- /dev/null +++ b/filesys/fastfat/verfysup.c @@ -0,0 +1,2072 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + VerfySup.c + +Abstract: + + This module implements the Fat Verify volume and fcb/dcb support + routines + + +--*/ + +#include "FatProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (FAT_BUG_CHECK_VERFYSUP) + +// +// The Debug trace level for this module +// + +#define Dbg (DEBUG_TRACE_VERFYSUP) + +// +// Local procedure prototypes +// + +VOID +FatResetFcb ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb + ); + +BOOLEAN +FatMatchFileSize ( + __in PIRP_CONTEXT IrpContext, + __in PDIRENT Dirent, + __in PFCB Fcb + ); + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatDetermineAndMarkFcbCondition ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb + ); + +WORKER_THREAD_ROUTINE FatDeferredCleanVolume; + +VOID +FatDeferredCleanVolume ( + _In_ PVOID Parameter + ); + +IO_COMPLETION_ROUTINE FatMarkVolumeCompletionRoutine; + +NTSTATUS +FatMarkVolumeCompletionRoutine( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PIRP Irp, + _In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, FatCheckDirtyBit) +#pragma alloc_text(PAGE, FatVerifyOperationIsLegal) +#pragma alloc_text(PAGE, FatDeferredCleanVolume) +#pragma alloc_text(PAGE, FatMatchFileSize) +#pragma alloc_text(PAGE, FatDetermineAndMarkFcbCondition) +#pragma alloc_text(PAGE, FatQuickVerifyVcb) +#pragma alloc_text(PAGE, FatPerformVerify) +#pragma alloc_text(PAGE, FatMarkFcbCondition) +#pragma alloc_text(PAGE, FatResetFcb) +#pragma alloc_text(PAGE, FatVerifyVcb) +#pragma alloc_text(PAGE, FatVerifyFcb) +#endif + + +VOID +FatMarkFcbCondition ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN FCB_CONDITION FcbCondition, + IN BOOLEAN Recursive + ) + +/*++ + +Routine Description: + + This routines marks the entire Fcb/Dcb structure from Fcb down with + FcbCondition. + +Arguments: + + Fcb - Supplies the Fcb/Dcb being marked + + FcbCondition - Supplies the setting to use for the Fcb Condition + + Recursive - Specifies whether this condition should be applied to + all children (see the case where we are invalidating a live volume + for a case where this is now desireable). + +Return Value: + + None. + +--*/ + +{ + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatMarkFcbCondition, Fcb = %p\n", Fcb ); + + // + // If we are marking this Fcb something other than Good, we will need + // to have the Vcb exclusive. + // + + NT_ASSERT( FcbCondition != FcbNeedsToBeVerified ? TRUE : + FatVcbAcquiredExclusive(IrpContext, Fcb->Vcb) ); + + // + // If this is a PagingFile it has to be good unless media underneath is + // removable. The "removable" check was added specifically for ReadyBoost, + // which opens its cache file on a removable device as a paging file and + // relies on the file system to validate its mapping information after a + // power transition. + // + + if (!FlagOn(Fcb->Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA) && + FlagOn(Fcb->FcbState, FCB_STATE_PAGING_FILE)) { + + Fcb->FcbCondition = FcbGood; + return; + } + + // + // Update the condition of the Fcb. + // + + Fcb->FcbCondition = FcbCondition; + + DebugTrace(0, Dbg, "MarkFcb: %wZ\n", &Fcb->FullFileName); + + // + // This FastIo flag is based on FcbCondition, so update it now. This only + // applies to regular FCBs, of course. + // + + if (Fcb->Header.NodeTypeCode == FAT_NTC_FCB) { + + Fcb->Header.IsFastIoPossible = FatIsFastIoPossible( Fcb ); + } + + if (FcbCondition == FcbNeedsToBeVerified) { + FatResetFcb( IrpContext, Fcb ); + } + + // + // Now if we marked NeedsVerify or Bad a directory then we also need to + // go and mark all of our children with the same condition. + // + + if ( ((FcbCondition == FcbNeedsToBeVerified) || + (FcbCondition == FcbBad)) && + Recursive && + ((Fcb->Header.NodeTypeCode == FAT_NTC_DCB) || + (Fcb->Header.NodeTypeCode == FAT_NTC_ROOT_DCB)) ) { + + PFCB OriginalFcb = Fcb; + + while ( (Fcb = FatGetNextFcbTopDown(IrpContext, Fcb, OriginalFcb)) != NULL ) { + + DebugTrace(0, Dbg, "MarkFcb: %wZ\n", &Fcb->FullFileName); + + Fcb->FcbCondition = FcbCondition; + + // + // We already know that FastIo is not possible since we are propagating + // a parent's bad/verify flag down the tree - IO to the children must + // take the long route for now. + // + + Fcb->Header.IsFastIoPossible = FastIoIsNotPossible; + + // + // Leave all the Fcbs in a condition to be verified. + // + + if (FcbCondition == FcbNeedsToBeVerified) { + FatResetFcb( IrpContext, Fcb ); + } + + } + } + + DebugTrace(-1, Dbg, "FatMarkFcbCondition -> VOID\n", 0); + + return; +} + +BOOLEAN +FatMarkDevForVerifyIfVcbMounted( + IN 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( push ) +#pragma prefast( disable: 28175, "touching Vpb is ok for a filesystem" ) + + if (Vcb->Vpb->RealDevice->Vpb == Vcb->Vpb) { + + SetFlag( Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME); + Marked = TRUE; + } + else { + + // + // Flag this to avoid the VPB spinlock in future passes. + // + + SetFlag( Vcb->VcbState, VCB_STATE_VPB_NOT_ON_DEVICE); + } + +#pragma prefast( pop ) + + IoReleaseVpbSpinLock( SavedIrql ); + + return Marked; +} + + +VOID +FatVerifyVcb ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb + ) + +/*++ + +Routine Description: + + This routines verifies that the Vcb still denotes a valid Volume + If the Vcb is bad it raises an error condition. + +Arguments: + + Vcb - Supplies the Vcb being verified + +Return Value: + + None. + +--*/ + +{ + ULONG ChangeCount = 0; + BOOLEAN DevMarkedForVerify; + NTSTATUS Status = STATUS_SUCCESS; + IO_STATUS_BLOCK Iosb; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatVerifyVcb, Vcb = %p\n", Vcb ); + + // + // 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. + // + // Note that we only force this ping for create operations. + // For others we take a sporting chance. If in the end we + // have to physically access the disk, the right thing will happen. + // + + DevMarkedForVerify = BooleanFlagOn(Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME); + + if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA)) { + + Status = FatPerformDevIoCtrl( IrpContext, + ( Vcb->Vpb->RealDevice->DeviceType == FILE_DEVICE_CD_ROM ? + IOCTL_CDROM_CHECK_VERIFY : + IOCTL_DISK_CHECK_VERIFY ), + Vcb->TargetDeviceObject, + NULL, + 0, + &ChangeCount, + sizeof(ULONG), + FALSE, + TRUE, + &Iosb ); + + if (Iosb.Information != sizeof(ULONG)) { + + // + // Be safe about the count in case the driver didn't fill it in + // + + ChangeCount = 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 == VcbGood) && + FatIsRawDevice( IrpContext, Status )) + || + (Status == STATUS_VERIFY_REQUIRED) + || + (NT_SUCCESS(Status) && + (Vcb->ChangeCount != ChangeCount))) { + + // + // 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 = FatMarkDevForVerifyIfVcbMounted( Vcb); + } + } + } + + // + // 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. So we take + // the verify path for synchronisation, and the request will eventually + // be bounced back to IO with STATUS_REPARSE by our verify handler. + // + + if (!DevMarkedForVerify && + (IrpContext->MajorFunction == IRP_MJ_CREATE) && + (IrpContext->OriginatingIrp != NULL)) { + + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( IrpContext->OriginatingIrp); + + if ((IrpSp->FileObject->RelatedFileObject == NULL) && + (Vcb->VcbCondition == VcbNotMounted)) { + + DevMarkedForVerify = TRUE; + } + } + + // + // Raise any error condition otherwise. + // + + if (!NT_SUCCESS( Status ) || DevMarkedForVerify) { + + DebugTrace(0, Dbg, "The Vcb needs to be verified\n", 0); + + IoSetHardErrorOrVerifyDevice( IrpContext->OriginatingIrp, + Vcb->Vpb->RealDevice ); + + FatNormalizeAndRaiseStatus( IrpContext, DevMarkedForVerify + ? STATUS_VERIFY_REQUIRED + : Status ); + } + + // + // Check the operation is legal for current Vcb state. + // + + FatQuickVerifyVcb( IrpContext, Vcb ); + + DebugTrace(-1, Dbg, "FatVerifyVcb -> VOID\n", 0); +} + + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatVerifyFcb ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb + ) + +/*++ + +Routine Description: + + This routines verifies that the Fcb still denotes the same file. + If the Fcb is bad it raises a error condition. + +Arguments: + + Fcb - Supplies the Fcb being verified + +Return Value: + + None. + +--*/ + +{ + PFCB CurrentFcb; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatVerifyFcb, Vcb = %p\n", Fcb ); + + // + // Always refuse operations on dismounted volumes. + // + + if (FlagOn( Fcb->Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DISMOUNTED )) { + + FatRaiseStatus( IrpContext, STATUS_VOLUME_DISMOUNTED ); + } + + // + // If this is the Fcb of a deleted dirent or our parent is deleted, + // no-op this call with the hope that the caller will do the right thing. + // The only caller we really have to worry about is the AdvanceOnly + // callback for setting valid data length from Cc, this will happen after + // cleanup (and file deletion), just before the SCM is ripped down. + // + + if (IsFileDeleted( IrpContext, Fcb ) || + ((NodeType(Fcb) != FAT_NTC_ROOT_DCB) && + IsFileDeleted( IrpContext, Fcb->ParentDcb ))) { + + return; + } + + // + // If we are not in the process of doing a verify, + // first do a quick spot check on the Vcb. + // + + if ( Fcb->Vcb->VerifyThread != KeGetCurrentThread() ) { + + FatQuickVerifyVcb( IrpContext, Fcb->Vcb ); + } + + // + // Now based on the condition of the Fcb we'll either return + // immediately to the caller, raise a condition, or do some work + // to verify the Fcb. + // + + switch (Fcb->FcbCondition) { + + case FcbGood: + + DebugTrace(0, Dbg, "The Fcb is good\n", 0); + break; + + case FcbBad: + + FatRaiseStatus( IrpContext, STATUS_FILE_INVALID ); + break; + + case FcbNeedsToBeVerified: + + // + // We loop here checking our ancestors until we hit an Fcb which + // is either good or bad. + // + + CurrentFcb = Fcb; + + while (CurrentFcb->FcbCondition == FcbNeedsToBeVerified) { + + FatDetermineAndMarkFcbCondition(IrpContext, CurrentFcb); + + // + // If this Fcb didn't make it, or it was the Root Dcb, exit + // the loop now, else continue with out parent. + // + + if ( (CurrentFcb->FcbCondition != FcbGood) || + (NodeType(CurrentFcb) == FAT_NTC_ROOT_DCB) ) { + + break; + } + + CurrentFcb = CurrentFcb->ParentDcb; + } + + // + // Now we can just look at ourselves to see how we did. + // + + if (Fcb->FcbCondition != FcbGood) { + + FatRaiseStatus( IrpContext, STATUS_FILE_INVALID ); + } + + break; + + default: + + DebugDump("Invalid FcbCondition\n", 0, Fcb); + +#pragma prefast( suppress:28159, "things are seriously wrong if we get here" ) + FatBugCheck( Fcb->FcbCondition, 0, 0 ); + } + + DebugTrace(-1, Dbg, "FatVerifyFcb -> VOID\n", 0); + + return; +} + + +VOID +FatDeferredCleanVolume ( + _In_ PVOID Parameter + ) + +/*++ + +Routine Description: + + This is the routine that performs the actual FatMarkVolumeClean call. + It assures that the target volume still exists as there ia a race + condition between queueing the ExWorker item and volumes going away. + +Arguments: + + Parameter - Points to a clean volume packet that was allocated from pool + +Return Value: + + None. + +--*/ + +{ + PCLEAN_AND_DIRTY_VOLUME_PACKET Packet; + PLIST_ENTRY Links; + PVCB Vcb; + IRP_CONTEXT IrpContext; + BOOLEAN VcbExists = FALSE; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatDeferredCleanVolume\n", 0); + + Packet = (PCLEAN_AND_DIRTY_VOLUME_PACKET)Parameter; + + Vcb = Packet->Vcb; + + // + // Make us appear as a top level FSP request so that we will + // receive any errors from the operation. + // + + IoSetTopLevelIrp( (PIRP)FSRTL_FSP_TOP_LEVEL_IRP ); + + // + // Dummy up and Irp Context so we can call our worker routines + // + + RtlZeroMemory( &IrpContext, sizeof(IRP_CONTEXT)); + + SetFlag(IrpContext.Flags, IRP_CONTEXT_FLAG_WAIT); + + // + // Acquire shared access to the global lock and make sure this volume + // still exists. + // + +#pragma prefast( push ) +#pragma prefast( disable: 28193, "this will always wait" ) + FatAcquireSharedGlobal( &IrpContext ); +#pragma prefast( pop ) + + for (Links = FatData.VcbQueue.Flink; + Links != &FatData.VcbQueue; + Links = Links->Flink) { + + PVCB ExistingVcb; + + ExistingVcb = CONTAINING_RECORD(Links, VCB, VcbLinks); + + if ( Vcb == ExistingVcb ) { + + VcbExists = TRUE; + break; + } + } + + // + // If the vcb is good then mark it clean. Ignore any problems. + // + + if ( VcbExists && + (Vcb->VcbCondition == VcbGood) && + !FlagOn(Vcb->VcbState, VCB_STATE_FLAG_SHUTDOWN) ) { + + try { + + if (!FlagOn(Vcb->VcbState, VCB_STATE_FLAG_MOUNTED_DIRTY)) { + + FatMarkVolume( &IrpContext, Vcb, VolumeClean ); + } + + // + // Check for a pathological race condition, and fix it. + // + + if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DIRTY)) { + + FatMarkVolume( &IrpContext, Vcb, VolumeDirty ); + + } else { + + // + // Unlock the volume if it is removable. + // + + if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA) && + !FlagOn(Vcb->VcbState, VCB_STATE_FLAG_BOOT_OR_PAGING_FILE)) { + + FatToggleMediaEjectDisable( &IrpContext, Vcb, FALSE ); + } + } + + } except( FsRtlIsNtstatusExpected(GetExceptionCode()) ? + EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) { + + NOTHING; + } + } + + // + // Release the global resource, unpin and repinned Bcbs and return. + // + + FatReleaseGlobal( &IrpContext ); + + try { + + FatUnpinRepinnedBcbs( &IrpContext ); + + } except( FsRtlIsNtstatusExpected(GetExceptionCode()) ? + EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) { + + NOTHING; + } + + IoSetTopLevelIrp( NULL ); + + // + // and finally free the packet. + // + + ExFreePool( Packet ); + + return; +} + + + +VOID +FatCleanVolumeDpc ( + _In_ PKDPC Dpc, + _In_opt_ PVOID DeferredContext, + _In_opt_ PVOID SystemArgument1, + _In_opt_ PVOID SystemArgument2 + ) + +/*++ + +Routine Description: + + This routine is dispatched 5 seconds after the last disk structure was + modified in a specific volume, and exqueues an execuative worker thread + to perform the actual task of marking the volume dirty. + +Arguments: + + DefferedContext - Contains the Vcb to process. + +Return Value: + + None. + +--*/ + +{ + PVCB Vcb; + PCLEAN_AND_DIRTY_VOLUME_PACKET Packet; + + UNREFERENCED_PARAMETER( SystemArgument1 ); + UNREFERENCED_PARAMETER( SystemArgument2 ); + UNREFERENCED_PARAMETER( Dpc ); + + Vcb = (PVCB)DeferredContext; + + + // + // If there is still dirty data (highly unlikely), set the timer for a + // second in the future. + // + + if (CcIsThereDirtyData(Vcb->Vpb)) { + + LARGE_INTEGER TwoSecondsFromNow; + + TwoSecondsFromNow.QuadPart = (LONG)-2*1000*1000*10; + + KeSetTimer( &Vcb->CleanVolumeTimer, + TwoSecondsFromNow, + &Vcb->CleanVolumeDpc ); + + return; + } + + // + // If we couldn't get pool, oh well.... + // + + Packet = ExAllocatePoolWithTag(NonPagedPoolNx, sizeof(CLEAN_AND_DIRTY_VOLUME_PACKET), ' taF'); + + if ( Packet ) { + + Packet->Vcb = Vcb; + Packet->Irp = NULL; + + // + // Clear the dirty flag now since we cannot synchronize after this point. + // + + ClearFlag( Packet->Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DIRTY ); + + ExInitializeWorkItem( &Packet->Item, &FatDeferredCleanVolume, Packet ); + +#pragma prefast( suppress:28159, "prefast indicates this is an obsolete API, but it is ok for fastfat to keep using it" ) + ExQueueWorkItem( &Packet->Item, CriticalWorkQueue ); + } + + return; +} + + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatMarkVolume ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN FAT_VOLUME_STATE VolumeState + ) + +/*++ + +Routine Description: + + This routine moves the physically marked volume state between the clean + and dirty states. For compatibility with Win9x, we manipulate both the + historical DOS (on==clean in index 1 of the FAT) and NT (on==dirty in + the CurrentHead field of the BPB) dirty bits. + +Arguments: + + Vcb - Supplies the Vcb being modified + + VolumeState - Supplies the state the volume is transitioning to + +Return Value: + + None. + +--*/ + +{ + PCHAR Sector; + PBCB Bcb = NULL; + KEVENT Event; + PIRP Irp = NULL; + NTSTATUS Status; + BOOLEAN FsInfoUpdate = FALSE; + ULONG FsInfoOffset = 0; + ULONG ThisPass; + LARGE_INTEGER Offset; + BOOLEAN abort = FALSE; + + DebugTrace(+1, Dbg, "FatMarkVolume, Vcb = %p\n", Vcb); + + // + // We had best not be trying to scribble dirty/clean bits if the + // volume is write protected. The responsibility lies with the + // callers to make sure that operations that could cause a state + // change cannot happen. There are a few, though, that show it + // just doesn't make sense to force everyone to do the dinky + // check. + // + + // + // If we were called for FAT12 or readonly media, return immediately. + // + + if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED) || + FatIsFat12( Vcb )) { + + return; + } + + // + // We have two possible additional tasks to do to mark a volume + // + // Pass 0) Flip the dirty bit in the Bpb + // Pass 1) Rewrite the FsInfo sector for FAT32 if needed + // + // In most cases we can collapse these two either because the volume + // is either not FAT32 or the FsInfo sector is adjacent to the boot sector. + // + + for (ThisPass = 0; ThisPass < 2; ThisPass++) { + + // + // If this volume is being dirtied, or isn't FAT32, or if it is and + // we were able to perform the fast update, or the bpb lied to us + // about where the FsInfo went, we're done - no FsInfo to update in + // a seperate write. + // + + if (ThisPass == 1 && (!FatIsFat32( Vcb ) || + VolumeState != VolumeClean || + FsInfoUpdate || + Vcb->Bpb.FsInfoSector == 0)) { + + break; + } + + // + // Bail if we get an IO error. + // + + try { + + ULONG PinLength; + ULONG WriteLength; + + // + // If the FAT table is 12-bit then our strategy is to pin the entire + // thing when any of it is modified. Here we're going to pin the + // first page, so in the 12-bit case we also want to pin the rest + // of the FAT table. + // + + Offset.QuadPart = 0; + + if (Vcb->AllocationSupport.FatIndexBitSize == 12) { + + // + // But we only write back the first sector. + // + + PinLength = FatReservedBytes(&Vcb->Bpb) + FatBytesPerFat(&Vcb->Bpb); + WriteLength = Vcb->Bpb.BytesPerSector; + + } else { + + WriteLength = PinLength = Vcb->Bpb.BytesPerSector; + + // + // If this is a FAT32 volume going into the clean state, + // see about doing the FsInfo sector. + // + + if (FatIsFat32( Vcb ) && VolumeState == VolumeClean) { + + // + // If the FsInfo sector immediately follows the boot sector, + // we can do this in a single operation by rewriting both + // sectors at once. + // + + if (Vcb->Bpb.FsInfoSector == 1) { + + NT_ASSERT( ThisPass == 0 ); + + FsInfoUpdate = TRUE; + FsInfoOffset = Vcb->Bpb.BytesPerSector; + WriteLength = PinLength = Vcb->Bpb.BytesPerSector * 2; + + } else if (ThisPass == 1) { + + // + // We are doing an explicit write to the FsInfo sector. + // + + FsInfoUpdate = TRUE; + FsInfoOffset = 0; + + Offset.QuadPart = Vcb->Bpb.BytesPerSector * Vcb->Bpb.FsInfoSector; + } + } + } + + // + // Call Cc directly here so that we can avoid overhead and push this + // right down to the disk. + // + + CcPinRead( Vcb->VirtualVolumeFile, + &Offset, + PinLength, + TRUE, + &Bcb, + (PVOID *)&Sector ); + + DbgDoit( IrpContext->PinCount += 1 ) + + // + // Set the Bpb on Pass 0 always + // + + if (ThisPass == 0) { + + PCHAR CurrentHead; + + // + // Before we do anything, doublecheck that this still looks like a + // FAT bootsector. If it doesn't, something remarkable happened + // and we should avoid touching the volume. + // + // THIS IS TEMPORARY (but may last a while) + // + + if (!FatIsBootSectorFat( (PPACKED_BOOT_SECTOR) Sector )) { + abort = TRUE; + leave; + } + + if (FatIsFat32( Vcb )) { + + CurrentHead = (PCHAR)&((PPACKED_BOOT_SECTOR_EX) Sector)->CurrentHead; + + } else { + + CurrentHead = (PCHAR)&((PPACKED_BOOT_SECTOR) Sector)->CurrentHead; + } + + if (VolumeState == VolumeClean) { + + ClearFlag( *CurrentHead, FAT_BOOT_SECTOR_DIRTY ); + + } else { + + SetFlag( *CurrentHead, FAT_BOOT_SECTOR_DIRTY ); + + // + // In addition, if this request received an error that may indicate + // media corruption, have autochk perform a surface test. + // + + if ( VolumeState == VolumeDirtyWithSurfaceTest ) { + + SetFlag( *CurrentHead, FAT_BOOT_SECTOR_TEST_SURFACE ); + } + } + } + + // + // Update the FsInfo as appropriate. + // + + if (FsInfoUpdate) { + + PFSINFO_SECTOR FsInfoSector = (PFSINFO_SECTOR) ((PCHAR)Sector + FsInfoOffset); + + // + // We just rewrite all of the spec'd fields. Note that we don't + // care to synchronize with the allocation package - this will be + // quickly taken care of by a re-dirtying of the volume if a change + // is racing with us. Remember that this is all a compatibility + // deference for Win9x FAT32 - NT will never look at this information. + // + + FsInfoSector->SectorBeginSignature = FSINFO_SECTOR_BEGIN_SIGNATURE; + FsInfoSector->FsInfoSignature = FSINFO_SIGNATURE; + FsInfoSector->FreeClusterCount = Vcb->AllocationSupport.NumberOfFreeClusters; + FsInfoSector->NextFreeCluster = Vcb->ClusterHint; + FsInfoSector->SectorEndSignature = FSINFO_SECTOR_END_SIGNATURE; + } + + // + // Initialize the event we're going to use + // + + KeInitializeEvent( &Event, NotificationEvent, FALSE ); + + // + // Build the irp for the operation and also set the override flag. + // Note that we may be at APC level, so do this asyncrhonously and + // use an event for synchronization as normal request completion + // cannot occur at APC level. + // + + Irp = IoBuildAsynchronousFsdRequest( IRP_MJ_WRITE, + Vcb->TargetDeviceObject, + (PVOID)Sector, + WriteLength, + &Offset, + NULL ); + + if ( Irp == NULL ) { + + try_return(NOTHING); + } + + // + // Make this operation write-through. It never hurts to try to be + // safer about this, even though we aren't logged. + // + + SetFlag( IoGetNextIrpStackLocation( Irp )->Flags, SL_WRITE_THROUGH ); + + // + // Set up the completion routine + // + + IoSetCompletionRoutine( Irp, + FatMarkVolumeCompletionRoutine, + &Event, + TRUE, + TRUE, + TRUE ); + + // + // Call the device to do the write and wait for it to finish. + // Igmore any return status. + // + + Status = IoCallDriver( Vcb->TargetDeviceObject, Irp ); + + if (Status == STATUS_PENDING) { + + (VOID)KeWaitForSingleObject( &Event, Executive, KernelMode, FALSE, (PLARGE_INTEGER)NULL ); + } + + try_exit: NOTHING; + } finally { + + // + // Clean up the Irp and Mdl + // + + + if (Irp) { + + // + // If there is an MDL (or MDLs) associated with this I/O + // request, Free it (them) here. This is accomplished by + // walking the MDL list hanging off of the IRP and deallocating + // each MDL encountered. + // + + while (Irp->MdlAddress != NULL) { + + PMDL NextMdl; + + NextMdl = Irp->MdlAddress->Next; + + MmUnlockPages( Irp->MdlAddress ); + + IoFreeMdl( Irp->MdlAddress ); + + Irp->MdlAddress = NextMdl; + } + + IoFreeIrp( Irp ); + } + + if (Bcb != NULL) { + + FatUnpinBcb( IrpContext, Bcb ); + } + } + } + + if (!abort) { + + // + // Flip the dirty bit in the FAT + // + + if (VolumeState == VolumeDirty) { + + FatSetFatEntry( IrpContext, Vcb, FAT_DIRTY_BIT_INDEX, FAT_DIRTY_VOLUME); + + } else { + + FatSetFatEntry( IrpContext, Vcb, FAT_DIRTY_BIT_INDEX, FAT_CLEAN_VOLUME); + } + } + + DebugTrace(-1, Dbg, "FatMarkVolume -> VOID\n", 0); + + return; +} + + +VOID +FatFspMarkVolumeDirtyWithRecover( + PVOID Parameter + ) + +/*++ + +Routine Description: + + This is the routine that performs the actual FatMarkVolume Dirty call + on a paging file Io that encounters a media error. It is responsible + for completing the PagingIo Irp as soon as this is done. + + Note: this routine (and thus FatMarkVolume()) must be resident as + the paging file might be damaged at this point. + +Arguments: + + Parameter - Points to a dirty volume packet that was allocated from pool + +Return Value: + + None. + +--*/ + +{ + PCLEAN_AND_DIRTY_VOLUME_PACKET Packet; + PVCB Vcb; + IRP_CONTEXT IrpContext; + PIRP Irp; + + DebugTrace(+1, Dbg, "FatFspMarkVolumeDirtyWithRecover\n", 0); + + Packet = (PCLEAN_AND_DIRTY_VOLUME_PACKET)Parameter; + + Vcb = Packet->Vcb; + Irp = Packet->Irp; + + // + // Dummy up the IrpContext so we can call our worker routines + // + + RtlZeroMemory( &IrpContext, sizeof(IRP_CONTEXT)); + + SetFlag(IrpContext.Flags, IRP_CONTEXT_FLAG_WAIT); + IrpContext.OriginatingIrp = Irp; + + // + // Make us appear as a top level FSP request so that we will + // receive any errors from the operation. + // + + IoSetTopLevelIrp( (PIRP)FSRTL_FSP_TOP_LEVEL_IRP ); + + // + // Try to write out the dirty bit. If something goes wrong, we + // tried. + // + + try { + + SetFlag( Vcb->VcbState, VCB_STATE_FLAG_MOUNTED_DIRTY ); + + FatMarkVolume( &IrpContext, Vcb, VolumeDirtyWithSurfaceTest ); + + } except(FatExceptionFilter( &IrpContext, GetExceptionInformation() )) { + + NOTHING; + } + + IoSetTopLevelIrp( NULL ); + + // + // Now complete the originating Irp or set the synchronous event. + // + + if (Packet->Event) { + KeSetEvent( Packet->Event, 0, FALSE ); + } else { + IoCompleteRequest( Irp, IO_DISK_INCREMENT ); + } + + DebugTrace(-1, Dbg, "FatFspMarkVolumeDirtyWithRecover -> VOID\n", 0); +} + + +VOID +FatCheckDirtyBit ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb + ) + +/*++ + +Routine Description: + + This routine looks at the volume dirty bit, and depending on the state of + VCB_STATE_FLAG_MOUNTED_DIRTY, the appropriate action is taken. + +Arguments: + + Vcb - Supplies the Vcb being queried. + +Return Value: + + None. + +--*/ + +{ + BOOLEAN Dirty; + + PPACKED_BOOT_SECTOR BootSector; + PBCB BootSectorBcb; + + UNICODE_STRING VolumeLabel; + + PAGED_CODE(); + + // + // Look in the boot sector + // + + FatReadVolumeFile( IrpContext, + Vcb, + 0, + sizeof(PACKED_BOOT_SECTOR), + &BootSectorBcb, + (PVOID *)&BootSector ); + + try { + + // + // Check if the magic bit is set + // + + if (IsBpbFat32(&BootSector->PackedBpb)) { + Dirty = BooleanFlagOn( ((PPACKED_BOOT_SECTOR_EX)BootSector)->CurrentHead, + FAT_BOOT_SECTOR_DIRTY ); + } else { + Dirty = BooleanFlagOn( BootSector->CurrentHead, FAT_BOOT_SECTOR_DIRTY ); + } + + // + // Setup the VolumeLabel string + // + + VolumeLabel.Length = Vcb->Vpb->VolumeLabelLength; + VolumeLabel.MaximumLength = MAXIMUM_VOLUME_LABEL_LENGTH; + VolumeLabel.Buffer = &Vcb->Vpb->VolumeLabel[0]; + + if ( Dirty ) { + + // + // Do not trigger the mounted dirty bit if this is a verify + // and the volume is a boot or paging device. We know that + // a boot or paging device cannot leave the system, and thus + // that on its mount we will have figured this out correctly. + // + // This logic is a reasonable change. Why? + // 'cause setup cracked a non-exclusive DASD handle near the + // end of setup, wrote some data, closed the handle and we + // set the verify bit ... came back around and saw that other + // arbitrary activity had left the volume in a temporarily dirty + // state. + // + // Of course, the real problem is that we don't have a journal. + // + + if (!(IrpContext->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL && + IrpContext->MinorFunction == IRP_MN_VERIFY_VOLUME && + FlagOn( Vcb->VcbState, VCB_STATE_FLAG_BOOT_OR_PAGING_FILE))) { + + KdPrintEx((DPFLTR_FASTFAT_ID, + DPFLTR_INFO_LEVEL, + "FASTFAT: WARNING! Mounting Dirty Volume %Z\n", + &VolumeLabel)); + + SetFlag( Vcb->VcbState, VCB_STATE_FLAG_MOUNTED_DIRTY ); + } + + } else { + + if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_MOUNTED_DIRTY)) { + + KdPrintEx((DPFLTR_FASTFAT_ID, + DPFLTR_INFO_LEVEL, + "FASTFAT: Volume %Z has been cleaned.\n", + &VolumeLabel)); + + ClearFlag( Vcb->VcbState, VCB_STATE_FLAG_MOUNTED_DIRTY ); + + } else { + + (VOID)FsRtlBalanceReads( Vcb->TargetDeviceObject ); + } + } + + } finally { + + FatUnpinBcb( IrpContext, BootSectorBcb ); + } +} + + +VOID +FatVerifyOperationIsLegal ( + IN PIRP_CONTEXT IrpContext + ) + +/*++ + +Routine Description: + + This routine determines is the requested operation should be allowed to + continue. It either returns to the user if the request is Okay, or + raises an appropriate status. + +Arguments: + + Irp - Supplies the Irp to check + +Return Value: + + None. + +--*/ + +{ + PIRP Irp; + PFILE_OBJECT FileObject; + + PAGED_CODE(); + + Irp = IrpContext->OriginatingIrp; + + // + // If the Irp is not present, then we got here via close. + // + // + + if ( Irp == NULL ) { + + return; + } + + FileObject = IoGetCurrentIrpStackLocation(Irp)->FileObject; + + // + // If there is not a file object, we cannot continue. + // + + if ( FileObject == NULL ) { + + return; + } + + // + // If the file object has already been cleaned up, and + // + // A) This request is a paging io read or write, or + // B) This request is a close operation, or + // C) This request is a set or query info call (for Lou) + // D) This is an MDL complete + // + // let it pass, otherwise return STATUS_FILE_CLOSED. + // + + if ( FlagOn(FileObject->Flags, FO_CLEANUP_COMPLETE) ) { + + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + if ( (FlagOn(Irp->Flags, IRP_PAGING_IO)) || + (IrpSp->MajorFunction == IRP_MJ_CLOSE ) || + (IrpSp->MajorFunction == IRP_MJ_SET_INFORMATION) || + (IrpSp->MajorFunction == IRP_MJ_QUERY_INFORMATION) || + ( ( (IrpSp->MajorFunction == IRP_MJ_READ) || + (IrpSp->MajorFunction == IRP_MJ_WRITE) ) && + FlagOn(IrpSp->MinorFunction, IRP_MN_COMPLETE) ) ) { + + NOTHING; + + } else { + + FatRaiseStatus( IrpContext, STATUS_FILE_CLOSED ); + } + } + + return; +} + + + +// +// Internal support routine +// + +VOID +FatResetFcb ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb + ) + +/*++ + +Routine Description: + + This routine is called when an Fcb has been marked as needs to be verified. + + It does the following tasks: + + - Reset Mcb mapping information + - For directories, reset dirent hints + - Set allocation size to unknown + +Arguments: + + Fcb - Supplies the Fcb to reset + +Return Value: + + None. + +--*/ + +{ + LOGICAL IsRealPagingFile; + + PAGED_CODE(); + UNREFERENCED_PARAMETER( IrpContext ); + + // + // Don't do the two following operations for the Root Dcb + // of a non FAT32 volume or paging files. Paging files!? + // Yes, if someone diddles a volume we try to reverify all + // of the Fcbs just in case; however, there is no safe way + // to chuck and retrieve the mapping pair information for + // a real paging file. Lose it and die. + // + // An exception is made for ReadyBoost cache files, which + // are created as paging files on removable devices and + // require validation after a power transition. + // + + if (!FlagOn(Fcb->Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA) && + FlagOn(Fcb->FcbState, FCB_STATE_PAGING_FILE)) { + + IsRealPagingFile = TRUE; + + } else { + + IsRealPagingFile = FALSE; + } + + if ( (NodeType(Fcb) != FAT_NTC_ROOT_DCB || + FatIsFat32( Fcb->Vcb )) && + !IsRealPagingFile ) { + + // + // Reset the mcb mapping. + // + + FsRtlRemoveLargeMcbEntry( &Fcb->Mcb, 0, 0xFFFFFFFF ); + + // + // Reset the allocation size to 0 or unknown + // + + if ( Fcb->FirstClusterOfFile == 0 ) { + + Fcb->Header.AllocationSize.QuadPart = 0; + + } else { + + Fcb->Header.AllocationSize.QuadPart = FCB_LOOKUP_ALLOCATIONSIZE_HINT; + } + } + + // + // If this is a directory, reset the hints. + // + + if ( (NodeType(Fcb) == FAT_NTC_DCB) || + (NodeType(Fcb) == FAT_NTC_ROOT_DCB) ) { + + // + // Force a rescan of the directory + // + + Fcb->Specific.Dcb.UnusedDirentVbo = 0xffffffff; + Fcb->Specific.Dcb.DeletedDirentHint = 0xffffffff; + } +} + + + +BOOLEAN +FatMatchFileSize ( + __in PIRP_CONTEXT IrpContext, + __in PDIRENT Dirent, + __in PFCB Fcb + ) +{ + + UNREFERENCED_PARAMETER(IrpContext); + + if (NodeType(Fcb) != FAT_NTC_FCB) { + return TRUE; + } + + + if (Fcb->Header.FileSize.LowPart != Dirent->FileSize) { + return FALSE; + } + + + return TRUE; +} + +// +// Internal support routine +// + +_Requires_lock_held_(_Global_critical_region_) +VOID +FatDetermineAndMarkFcbCondition ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb + ) + +/*++ + +Routine Description: + + This routine checks a specific Fcb to see if it is different from what's + on the disk. The following things are checked: + + - File Name + - File Size (if not directory) + - First Cluster Of File + - Dirent Attributes + +Arguments: + + Fcb - Supplies the Fcb to examine + +Return Value: + + None. + +--*/ + +{ + PDIRENT Dirent; + PBCB DirentBcb; + ULONG FirstClusterOfFile; + + OEM_STRING Name; + CHAR Buffer[16]; + + PAGED_CODE(); + + // + // If this is the Root Dcb, special case it. That is, we know + // by definition that it is good since it is fixed in the volume + // structure. + // + + if ( NodeType(Fcb) == FAT_NTC_ROOT_DCB ) { + + FatMarkFcbCondition( IrpContext, Fcb, FcbGood, FALSE ); + + return; + } + + // The first thing we need to do to verify ourselves is + // locate the dirent on the disk. + // + + FatGetDirentFromFcbOrDcb( IrpContext, + Fcb, + TRUE, + &Dirent, + &DirentBcb ); + // + // If we couldn't get the dirent, this fcb must be bad (case of + // enclosing directory shrinking during the time it was ejected). + // + + if (DirentBcb == NULL) { + + FatMarkFcbCondition( IrpContext, Fcb, FcbBad, FALSE ); + + return; + } + + // + // We located the dirent for ourselves now make sure it + // is really ours by comparing the Name and FatFlags. + // Then for a file we also check the file size. + // + // Note that we have to unpin the Bcb before calling FatResetFcb + // in order to avoid a deadlock in CcUninitializeCacheMap. + // + + try { + + Name.MaximumLength = 16; + Name.Buffer = &Buffer[0]; + + Fat8dot3ToString( IrpContext, Dirent, FALSE, &Name ); + + // + // We need to calculate the first cluster 'cause FAT32 splits + // this field across the dirent. + // + + FirstClusterOfFile = Dirent->FirstClusterOfFile; + + if (FatIsFat32( Fcb->Vcb )) { + + FirstClusterOfFile += Dirent->FirstClusterOfFileHi << 16; + } + + if (!RtlEqualString( &Name, &Fcb->ShortName.Name.Oem, TRUE ) + + || + + !FatMatchFileSize(IrpContext, Dirent, Fcb ) + + || + + (FirstClusterOfFile != Fcb->FirstClusterOfFile) + + || + + (Dirent->Attributes != Fcb->DirentFatFlags) ) { + + FatMarkFcbCondition( IrpContext, Fcb, FcbBad, FALSE ); + + } else { + + // + // We passed. Get the Fcb ready to use again. + // + + FatMarkFcbCondition( IrpContext, Fcb, FcbGood, FALSE ); + } + + } finally { + + FatUnpinBcb( IrpContext, DirentBcb ); + } + + return; +} + + + +// +// Internal support routine +// + +VOID +FatQuickVerifyVcb ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb + ) + +/*++ + +Routine Description: + + This routines just checks the verify bit in the real device and the + Vcb condition and raises an appropriate exception if so warented. + It is called when verifying both Fcbs and Vcbs. + +Arguments: + + Vcb - Supplies the Vcb to check the condition of. + +Return Value: + + None. + +--*/ + +{ + PAGED_CODE(); + + // + // If the real device needs to be verified we'll set the + // DeviceToVerify to be our real device and raise VerifyRequired. + // + + if (FlagOn(Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME)) { + + DebugTrace(0, Dbg, "The Vcb needs to be verified\n", 0); + + IoSetHardErrorOrVerifyDevice( IrpContext->OriginatingIrp, + Vcb->Vpb->RealDevice ); + + FatRaiseStatus( IrpContext, STATUS_VERIFY_REQUIRED ); + } + + // + // Based on the condition of the Vcb we'll either return to our + // caller or raise an error condition + // + + switch (Vcb->VcbCondition) { + + case VcbGood: + + DebugTrace(0, Dbg, "The Vcb is good\n", 0); + + // + // Do a check here of an operation that would try to modify a + // write protected media. + // + + if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED) && + ((IrpContext->MajorFunction == IRP_MJ_WRITE) || + (IrpContext->MajorFunction == IRP_MJ_SET_INFORMATION) || + (IrpContext->MajorFunction == IRP_MJ_SET_EA) || + (IrpContext->MajorFunction == IRP_MJ_FLUSH_BUFFERS) || + (IrpContext->MajorFunction == IRP_MJ_SET_VOLUME_INFORMATION) || + (IrpContext->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL && + IrpContext->MinorFunction == IRP_MN_USER_FS_REQUEST && + IoGetCurrentIrpStackLocation(IrpContext->OriginatingIrp)->Parameters.FileSystemControl.FsControlCode == + FSCTL_MARK_VOLUME_DIRTY))) { + + // + // Set the real device for the pop-up info, and set the verify + // bit in the device object, so that we will force a verify + // in case the user put the correct media back in. + // + + + IoSetHardErrorOrVerifyDevice( IrpContext->OriginatingIrp, + Vcb->Vpb->RealDevice ); + + FatMarkDevForVerifyIfVcbMounted(Vcb); + + FatRaiseStatus( IrpContext, STATUS_MEDIA_WRITE_PROTECTED ); + } + + break; + + case VcbNotMounted: + + DebugTrace(0, Dbg, "The Vcb is not mounted\n", 0); + + // + // Set the real device for the pop-up info, and set the verify + // bit in the device object, so that we will force a verify + // in case the user put the correct media back in. + // + + IoSetHardErrorOrVerifyDevice( IrpContext->OriginatingIrp, + Vcb->Vpb->RealDevice ); + + FatRaiseStatus( IrpContext, STATUS_WRONG_VOLUME ); + + break; + + case VcbBad: + + DebugTrace(0, Dbg, "The Vcb is bad\n", 0); + + if (FlagOn( Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DISMOUNTED )) { + + FatRaiseStatus( IrpContext, STATUS_VOLUME_DISMOUNTED ); + + } else { + + FatRaiseStatus( IrpContext, STATUS_FILE_INVALID ); + } + break; + + default: + + DebugDump("Invalid VcbCondition\n", 0, Vcb); +#pragma prefast( suppress:28159, "things are seriously wrong if we get here" ) + FatBugCheck( Vcb->VcbCondition, 0, 0 ); + } +} + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatPerformVerify ( + _In_ PIRP_CONTEXT IrpContext, + _In_ PIRP Irp, + _In_ PDEVICE_OBJECT Device + ) + +/*++ + +Routine Description: + + This routines performs an IoVerifyVolume operation and takes the + appropriate action. After the Verify is complete the originating + Irp is sent off to an Ex Worker Thread. This routine is called + from the exception handler. + +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; + PFILE_OBJECT FileObject = IoGetCurrentIrpStackLocation(Irp)->FileObject; + BOOLEAN AllowRawMount = FALSE; + BOOLEAN VcbDeleted = FALSE; + + PAGED_CODE(); + + // + // 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 FatFsdPostRequest( IrpContext, Irp ); + } + + DebugTrace(0, Dbg, "Verify Required, DeviceObject = %p\n", Device); + + // + // 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; + + // + // Check if the volume still thinks it needs to be verified, + // if it doesn't then we can skip doing a verify because someone + // else beat us to it. + // + + try { + + // + // We will allow Raw to mount this volume if we were doing a + // a DASD open. + // + + if ( (IrpContext->MajorFunction == IRP_MJ_CREATE) && + (IrpSp->FileObject->FileName.Length == 0) && + (IrpSp->FileObject->RelatedFileObject == NULL) ) { + + AllowRawMount = TRUE; + } + + // + // Send down the verify. This could be going to a different + // filesystem. + // + + Status = IoVerifyVolume( Device, AllowRawMount ); + + // + // If the verify operation completed it will return + // either STATUS_SUCCESS or STATUS_WRONG_VOLUME, exactly. + // + // If FatVerifyVolume encountered an error during + // processing, it will return that error. If we got + // STATUS_WRONG_VOLUME from the verfy, and our volume + // is now mounted, commute the status to STATUS_SUCCESS. + // + // Acquire the Vcb so we're working with a stable Vcb condition. + // + + FatAcquireSharedVcb(IrpContext, Vcb); + + if ( (Status == STATUS_WRONG_VOLUME) && + (Vcb->VcbCondition == VcbGood) ) { + + Status = STATUS_SUCCESS; + } + else if ((STATUS_SUCCESS == Status) && (Vcb->VcbCondition != VcbGood)) { + + 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 ((VcbGood != Vcb->VcbCondition) && + (0 == Vcb->OpenFileCount) ) { + + FatReleaseVcb( IrpContext, Vcb); + +#pragma prefast( push ) +#pragma prefast( disable: 28137, "prefast wants the wait to be a constant, but that isn't possible for the way fastfat is designed" ) +#pragma prefast( disable: 28193 ) + FatAcquireExclusiveGlobal( IrpContext ); +#pragma prefast( pop ) + + FatAcquireExclusiveVcb( IrpContext, + Vcb ); + + VcbDeleted = FatCheckForDismount( IrpContext, + Vcb, + FALSE ); + + if (!VcbDeleted) { + + FatReleaseVcb( IrpContext, + Vcb ); + } + + FatReleaseGlobal( IrpContext ); + } + else { + + FatReleaseVcb( IrpContext, Vcb); + } + + // + // If the IopMount in IoVerifyVolume did something, and + // this is an absolute open, force a reparse. + // + + if ((IrpContext->MajorFunction == IRP_MJ_CREATE) && + (FileObject->RelatedFileObject == NULL) && + ((Status == STATUS_SUCCESS) || (Status == STATUS_WRONG_VOLUME))) { + + Irp->IoStatus.Information = IO_REMOUNT; + + FatCompleteRequest( IrpContext, Irp, STATUS_REPARSE ); + Status = STATUS_REPARSE; + Irp = NULL; + } + + if ( (Irp != NULL) && !NT_SUCCESS(Status) ) { + + // + // Fill in the device object if required. + // + + if ( IoIsErrorUserInduced( Status ) ) { + + IoSetHardErrorOrVerifyDevice( Irp, Device ); + } + + NT_ASSERT( STATUS_VERIFY_REQUIRED != Status); + + FatNormalizeAndRaiseStatus( IrpContext, Status ); + } + + // + // If there is still an Irp, send it off to an Ex Worker thread. + // + + if ( Irp != NULL ) { + + Status = FatFsdPostRequest( IrpContext, Irp ); + } + + } + except (FatExceptionFilter( 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 = FatProcessException( IrpContext, Irp, GetExceptionCode() ); + } + + return Status; +} + +// +// Local support routine +// + +NTSTATUS +FatMarkVolumeCompletionRoutine( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PIRP Irp, + _In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt + ) + +{ + // + // Set the event so that our call will wake up. + // + + KeSetEvent( (PKEVENT)Contxt, 0, FALSE ); + + UNREFERENCED_PARAMETER( DeviceObject ); + UNREFERENCED_PARAMETER( Irp ); + + return STATUS_MORE_PROCESSING_REQUIRED; +} + + diff --git a/filesys/fastfat/volinfo.c b/filesys/fastfat/volinfo.c new file mode 100644 index 000000000..0374bf3f4 --- /dev/null +++ b/filesys/fastfat/volinfo.c @@ -0,0 +1,1362 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + VolInfo.c + +Abstract: + + This module implements the volume information routines for Fat called by + the dispatch driver. + + +--*/ + +#include "FatProcs.h" + +// +// The local debug trace level +// + +#define Dbg (DEBUG_TRACE_VOLINFO) + +NTSTATUS +FatQueryFsVolumeInfo ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PFILE_FS_VOLUME_INFORMATION Buffer, + IN OUT PULONG Length + ); + +NTSTATUS +FatQueryFsSizeInfo ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PFILE_FS_SIZE_INFORMATION Buffer, + IN OUT PULONG Length + ); + +NTSTATUS +FatQueryFsDeviceInfo ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PFILE_FS_DEVICE_INFORMATION Buffer, + IN OUT PULONG Length + ); + +NTSTATUS +FatQueryFsAttributeInfo ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PFILE_FS_ATTRIBUTE_INFORMATION Buffer, + IN OUT PULONG Length + ); + +NTSTATUS +FatQueryFsFullSizeInfo ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PFILE_FS_FULL_SIZE_INFORMATION Buffer, + IN OUT PULONG Length + ); + +NTSTATUS +FatSetFsLabelInfo ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PFILE_FS_LABEL_INFORMATION Buffer + ); + +NTSTATUS +FatQueryFsSectorSizeInfo ( + _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, FatCommonQueryVolumeInfo) +#pragma alloc_text(PAGE, FatCommonSetVolumeInfo) +#pragma alloc_text(PAGE, FatFsdQueryVolumeInformation) +#pragma alloc_text(PAGE, FatFsdSetVolumeInformation) +#pragma alloc_text(PAGE, FatQueryFsAttributeInfo) +#pragma alloc_text(PAGE, FatQueryFsDeviceInfo) +#pragma alloc_text(PAGE, FatQueryFsSizeInfo) +#pragma alloc_text(PAGE, FatQueryFsVolumeInfo) +#pragma alloc_text(PAGE, FatQueryFsFullSizeInfo) +#pragma alloc_text(PAGE, FatSetFsLabelInfo) +#pragma alloc_text(PAGE, FatQueryFsSectorSizeInfo) +#endif + + +_Function_class_(IRP_MJ_QUERY_VOLUME_INFORMATION) +_Function_class_(DRIVER_DISPATCH) +NTSTATUS +FatFsdQueryVolumeInformation ( + _In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + _Inout_ PIRP Irp + ) + +/*++ + +Routine Description: + + This routine implements the Fsd part of the NtQueryVolumeInformation API + call. + +Arguments: + + VolumeDeviceObject - Supplies the volume device object where the file + being queried exists. + + Irp - Supplies the Irp being processed. + +Return Value: + + NTSTATUS - The FSD status for the Irp. + +--*/ + +{ + NTSTATUS Status; + PIRP_CONTEXT IrpContext = NULL; + + BOOLEAN TopLevel; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatFsdQueryVolumeInformation\n", 0); + + // + // Call the common query routine, with blocking allowed if synchronous + // + + FsRtlEnterFileSystem(); + + TopLevel = FatIsIrpTopLevel( Irp ); + + try { + + IrpContext = FatCreateIrpContext( Irp, CanFsdWait( Irp ) ); + + Status = FatCommonQueryVolumeInfo( IrpContext, Irp ); + + } except(FatExceptionFilter( IrpContext, GetExceptionInformation() )) { + + // + // We had some trouble trying to perform the requested + // operation, so we'll abort the I/O request with + // the error status that we get back from the + // execption code + // + + Status = FatProcessException( IrpContext, Irp, GetExceptionCode() ); + } + + if (TopLevel) { IoSetTopLevelIrp( NULL ); } + + FsRtlExitFileSystem(); + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "FatFsdQueryVolumeInformation -> %08lx\n", Status); + + UNREFERENCED_PARAMETER( VolumeDeviceObject ); + + return Status; +} + + +_Function_class_(IRP_MJ_SET_VOLUME_INFORMATION) +_Function_class_(DRIVER_DISPATCH) +NTSTATUS +FatFsdSetVolumeInformation ( + _In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + _Inout_ PIRP Irp + ) + +/*++ + +Routine Description: + + This routine implements the FSD part of the NtSetVolumeInformation API + call. + +Arguments: + + VolumeDeviceObject - Supplies the volume device object where the file + being set exists. + + Irp - Supplies the Irp being processed. + +Return Value: + + NTSTATUS - The FSD status for the Irp. + +--*/ + +{ + NTSTATUS Status; + PIRP_CONTEXT IrpContext = NULL; + + BOOLEAN TopLevel; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatFsdSetVolumeInformation\n", 0); + + // + // Call the common set routine + // + + FsRtlEnterFileSystem(); + + TopLevel = FatIsIrpTopLevel( Irp ); + + try { + + IrpContext = FatCreateIrpContext( Irp, CanFsdWait( Irp ) ); + + Status = FatCommonSetVolumeInfo( IrpContext, Irp ); + + } except(FatExceptionFilter( IrpContext, GetExceptionInformation() )) { + + // + // We had some trouble trying to perform the requested + // operation, so we'll abort the I/O request with + // the error status that we get back from the + // execption code + // + + Status = FatProcessException( IrpContext, Irp, GetExceptionCode() ); + } + + if (TopLevel) { IoSetTopLevelIrp( NULL ); } + + FsRtlExitFileSystem(); + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "FatFsdSetVolumeInformation -> %08lx\n", Status); + + UNREFERENCED_PARAMETER( VolumeDeviceObject ); + + return Status; +} + + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatCommonQueryVolumeInfo ( + IN PIRP_CONTEXT IrpContext, + IN 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_SUCCESS; + PIO_STACK_LOCATION IrpSp; + + PVCB Vcb; + PFCB Fcb; + PCCB Ccb; + + ULONG Length; + FS_INFORMATION_CLASS FsInformationClass; + PVOID Buffer; + + BOOLEAN WeAcquiredVcb = FALSE; + + PAGED_CODE(); + + // + // Get the current stack location + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "FatCommonQueryVolumeInfo...\n", 0); + DebugTrace( 0, Dbg, "Irp = %p\n", Irp ); + DebugTrace( 0, Dbg, "->Length = %08lx\n", IrpSp->Parameters.QueryVolume.Length); + DebugTrace( 0, Dbg, "->FsInformationClass = %08lx\n", IrpSp->Parameters.QueryVolume.FsInformationClass); + DebugTrace( 0, Dbg, "->Buffer = %p\n", Irp->AssociatedIrp.SystemBuffer); + + // + // Reference our input parameters to make things easier + // + + Length = IrpSp->Parameters.QueryVolume.Length; + FsInformationClass = IrpSp->Parameters.QueryVolume.FsInformationClass; + Buffer = Irp->AssociatedIrp.SystemBuffer; + + // + // Decode the file object to get the Vcb + // + + (VOID) FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb ); + + NT_ASSERT( Vcb != NULL ); + _Analysis_assume_( Vcb != NULL ); + + try { + + // + // Make sure the vcb is in a usable condition. This will raise + // and error condition if the volume is unusable + // + // Also verify the Root Dcb since we need info from there. + // + + FatVerifyFcb( IrpContext, Vcb->RootDcb ); + + // + // 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 (FsInformationClass) { + + case FileFsVolumeInformation: + + // + // This is the only routine we need the Vcb shared because of + // copying the volume label. All other routines copy fields that + // cannot change or are just manifest constants. + // + + if (!FatAcquireSharedVcb( IrpContext, Vcb )) { + + DebugTrace(0, Dbg, "Cannot acquire Vcb\n", 0); + + Status = FatFsdPostRequest( IrpContext, Irp ); + IrpContext = NULL; + Irp = NULL; + + } else { + + WeAcquiredVcb = TRUE; + + Status = FatQueryFsVolumeInfo( IrpContext, Vcb, Buffer, &Length ); + } + + break; + + case FileFsSizeInformation: + + Status = FatQueryFsSizeInfo( IrpContext, Vcb, Buffer, &Length ); + break; + + case FileFsDeviceInformation: + + Status = FatQueryFsDeviceInfo( IrpContext, Vcb, Buffer, &Length ); + break; + + case FileFsAttributeInformation: + + Status = FatQueryFsAttributeInfo( IrpContext, Vcb, Buffer, &Length ); + break; + + case FileFsFullSizeInformation: + + Status = FatQueryFsFullSizeInfo( IrpContext, Vcb, Buffer, &Length ); + break; + +#if (NTDDI_VERSION >= NTDDI_WIN8) + case FileFsSectorSizeInformation: + + Status = FatQueryFsSectorSizeInfo( IrpContext, Vcb, Buffer, &Length ); + break; +#endif + + default: + + Status = STATUS_INVALID_PARAMETER; + break; + } + + // + // Set the information field to the number of bytes actually filled in. + // + + if (Irp != NULL) { + + Irp->IoStatus.Information = IrpSp->Parameters.QueryVolume.Length - Length; + } + + } finally { + + DebugUnwind( FatCommonQueryVolumeInfo ); + + if (WeAcquiredVcb) { + + FatReleaseVcb( IrpContext, Vcb ); + } + + if (!AbnormalTermination()) { + + FatCompleteRequest( IrpContext, Irp, Status ); + } + + DebugTrace(-1, Dbg, "FatCommonQueryVolumeInfo -> %08lx\n", Status); + } + + return Status; +} + + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatCommonSetVolumeInfo ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the common routine for setting 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_SUCCESS; + PIO_STACK_LOCATION IrpSp; + + PVCB Vcb; + PFCB Fcb; + PCCB Ccb; + TYPE_OF_OPEN TypeOfOpen; + + FS_INFORMATION_CLASS FsInformationClass; + PVOID Buffer; + + PAGED_CODE(); + + // + // Get the current stack location + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "FatCommonSetVolumeInfo...\n", 0); + DebugTrace( 0, Dbg, "Irp = %p\n", Irp ); + DebugTrace( 0, Dbg, "->Length = %08lx\n", IrpSp->Parameters.SetVolume.Length); + DebugTrace( 0, Dbg, "->FsInformationClass = %08lx\n", IrpSp->Parameters.SetVolume.FsInformationClass); + DebugTrace( 0, Dbg, "->Buffer = %p\n", Irp->AssociatedIrp.SystemBuffer); + + // + // Reference our input parameters to make things easier + // + + FsInformationClass = IrpSp->Parameters.SetVolume.FsInformationClass; + Buffer = Irp->AssociatedIrp.SystemBuffer; + + // + // Decode the file object to get the Vcb + // + + TypeOfOpen = FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb ); + + if (TypeOfOpen != UserVolumeOpen) { + + FatCompleteRequest( IrpContext, Irp, STATUS_ACCESS_DENIED ); + + DebugTrace(-1, Dbg, "FatCommonSetVolumeInfo -> STATUS_ACCESS_DENIED\n", 0); + + return STATUS_ACCESS_DENIED; + } + + // + // Acquire exclusive access to the Vcb and enqueue the Irp if we didn't + // get access + // + + if (!FatAcquireExclusiveVcb( IrpContext, Vcb )) { + + DebugTrace(0, Dbg, "Cannot acquire Vcb\n", 0); + + Status = FatFsdPostRequest( IrpContext, Irp ); + + DebugTrace(-1, Dbg, "FatCommonSetVolumeInfo -> %08lx\n", Status ); + return Status; + } + + try { + + // + // Make sure the vcb is in a usable condition. This will raise + // and error condition if the volume is unusable + // + // Also verify the Root Dcb since we need info from there. + // + + FatVerifyFcb( IrpContext, Vcb->RootDcb ); + + // + // Based on the information class we'll do different actions. Each + // of the procedures that we're calling performs the action if + // possible and returns true if it successful and false if it couldn't + // wait for any I/O to complete. + // + + switch (FsInformationClass) { + + case FileFsLabelInformation: + + Status = FatSetFsLabelInfo( IrpContext, Vcb, Buffer ); + break; + + default: + + Status = STATUS_INVALID_PARAMETER; + break; + } + + FatUnpinRepinnedBcbs( IrpContext ); + + } finally { + + DebugUnwind( FatCommonSetVolumeInfo ); + + FatReleaseVcb( IrpContext, Vcb ); + + if (!AbnormalTermination()) { + + FatCompleteRequest( IrpContext, Irp, Status ); + } + + DebugTrace(-1, Dbg, "FatCommonSetVolumeInfo -> %08lx\n", Status); + } + + return Status; +} + + +// +// Internal support routine +// + +NTSTATUS +FatQueryFsVolumeInfo ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PFILE_FS_VOLUME_INFORMATION Buffer, + IN OUT PULONG Length + ) + +/*++ + +Routine Description: + + This routine implements the query volume info call + +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 recieves the remaining bytes free in the buffer + +Return Value: + + NTSTATUS - Returns the status for the query + +--*/ + +{ + ULONG BytesToCopy; + + NTSTATUS Status; + + PAGED_CODE(); + + DebugTrace(0, Dbg, "FatQueryFsVolumeInfo...\n", 0); + + // + // Zero out the buffer, then extract and fill up the non zero fields. + // + + RtlZeroMemory( Buffer, sizeof(FILE_FS_VOLUME_INFORMATION) ); + + 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; + + Status = STATUS_SUCCESS; + + } else { + + BytesToCopy = *Length; + + Status = STATUS_BUFFER_OVERFLOW; + } + + // + // Copy over what we can of the volume label, and adjust *Length + // + + Buffer->VolumeLabelLength = Vcb->Vpb->VolumeLabelLength; + + RtlCopyMemory( &Buffer->VolumeLabel[0], + &Vcb->Vpb->VolumeLabel[0], + BytesToCopy ); + + *Length -= BytesToCopy; + + // + // Set our status and return to our caller + // + + UNREFERENCED_PARAMETER( IrpContext ); + + return Status; +} + + +// +// Internal support routine +// + +NTSTATUS +FatQueryFsSizeInfo ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PFILE_FS_SIZE_INFORMATION Buffer, + IN OUT PULONG Length + ) + +/*++ + +Routine Description: + + This routine implements the query volume size call + +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 recieves the remaining bytes free in the buffer + +Return Value: + + Status - Returns the status for the query + +--*/ + +{ + PAGED_CODE(); + + DebugTrace(0, Dbg, "FatQueryFsSizeInfo...\n", 0); + + RtlZeroMemory( Buffer, sizeof(FILE_FS_SIZE_INFORMATION) ); + + // + // Set the output buffer. + // + + Buffer->TotalAllocationUnits.LowPart = + Vcb->AllocationSupport.NumberOfClusters; + Buffer->AvailableAllocationUnits.LowPart = + Vcb->AllocationSupport.NumberOfFreeClusters; + + Buffer->SectorsPerAllocationUnit = Vcb->Bpb.SectorsPerCluster; + Buffer->BytesPerSector = Vcb->Bpb.BytesPerSector; + + // + // Adjust the length variable + // + + *Length -= sizeof(FILE_FS_SIZE_INFORMATION); + + // + // And return success to our caller + // + + UNREFERENCED_PARAMETER( IrpContext ); + + return STATUS_SUCCESS; +} + + +// +// Internal support routine +// + +NTSTATUS +FatQueryFsDeviceInfo ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PFILE_FS_DEVICE_INFORMATION Buffer, + IN OUT PULONG Length + ) + +/*++ + +Routine Description: + + This routine implements the query volume device call + +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 recieves the remaining bytes free in the buffer + +Return Value: + + Status - Returns the status for the query + +--*/ + +{ + PAGED_CODE(); + + DebugTrace(0, Dbg, "FatQueryFsDeviceInfo...\n", 0); + + RtlZeroMemory( Buffer, sizeof(FILE_FS_DEVICE_INFORMATION) ); + + // + // Set the output buffer + // + + Buffer->DeviceType = FILE_DEVICE_DISK; + + Buffer->Characteristics = Vcb->TargetDeviceObject->Characteristics; + + // + // Adjust the length variable + // + + *Length -= sizeof(FILE_FS_DEVICE_INFORMATION); + + // + // And return success to our caller + // + + UNREFERENCED_PARAMETER( IrpContext ); + + return STATUS_SUCCESS; +} + + +// +// Internal support routine +// + +NTSTATUS +FatQueryFsAttributeInfo ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PFILE_FS_ATTRIBUTE_INFORMATION Buffer, + IN OUT PULONG Length + ) + +/*++ + +Routine Description: + + This routine implements the query volume attribute call + +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 recieves the remaining bytes free in the buffer + +Return Value: + + Status - Returns the status for the query + +--*/ + +{ + ULONG BytesToCopy; + + NTSTATUS Status; + + PAGED_CODE(); + + DebugTrace(0, Dbg, "FatQueryFsAttributeInfo...\n", 0); + + // + // Set the output buffer + // + + Buffer->FileSystemAttributes = FILE_CASE_PRESERVED_NAMES | + FILE_UNICODE_ON_DISK; + + if (FlagOn( Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED )) { + + SetFlag( Buffer->FileSystemAttributes, FILE_READ_ONLY_VOLUME ); + } + + + Buffer->MaximumComponentNameLength = FatData.ChicagoMode ? 255 : 12; + + if (FatIsFat32(Vcb)) { + + // + // Determine how much of the file system name will fit. + // + + if ( (*Length - FIELD_OFFSET( FILE_FS_ATTRIBUTE_INFORMATION, + FileSystemName[0] )) >= 10 ) { + + BytesToCopy = 10; + *Length -= FIELD_OFFSET( FILE_FS_ATTRIBUTE_INFORMATION, + FileSystemName[0] ) + 10; + Status = STATUS_SUCCESS; + + } else { + + BytesToCopy = *Length - FIELD_OFFSET( FILE_FS_ATTRIBUTE_INFORMATION, + FileSystemName[0]); + *Length = 0; + + Status = STATUS_BUFFER_OVERFLOW; + } + + RtlCopyMemory( &Buffer->FileSystemName[0], L"FAT32", BytesToCopy ); + + } else { + + // + // Determine how much of the file system name will fit. + // + + if ( (*Length - FIELD_OFFSET( FILE_FS_ATTRIBUTE_INFORMATION, + FileSystemName[0] )) >= 6 ) { + + BytesToCopy = 6; + *Length -= FIELD_OFFSET( FILE_FS_ATTRIBUTE_INFORMATION, + FileSystemName[0] ) + 6; + Status = STATUS_SUCCESS; + + } else { + + BytesToCopy = *Length - FIELD_OFFSET( FILE_FS_ATTRIBUTE_INFORMATION, + FileSystemName[0]); + *Length = 0; + + Status = STATUS_BUFFER_OVERFLOW; + } + + + RtlCopyMemory( &Buffer->FileSystemName[0], L"FAT", BytesToCopy ); + } + + Buffer->FileSystemNameLength = BytesToCopy; + + // + // And return success to our caller + // + + UNREFERENCED_PARAMETER( IrpContext ); + UNREFERENCED_PARAMETER( Vcb ); + + return Status; +} + + +// +// Internal support routine +// + +NTSTATUS +FatQueryFsFullSizeInfo ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PFILE_FS_FULL_SIZE_INFORMATION Buffer, + IN OUT PULONG Length + ) + +/*++ + +Routine Description: + + This routine implements the query volume full size call + +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 recieves the remaining bytes free in the buffer + +Return Value: + + Status - Returns the status for the query + +--*/ + +{ + PAGED_CODE(); + + DebugTrace(0, Dbg, "FatQueryFsSizeInfo...\n", 0); + + RtlZeroMemory( Buffer, sizeof(FILE_FS_FULL_SIZE_INFORMATION) ); + + Buffer->TotalAllocationUnits.LowPart = + Vcb->AllocationSupport.NumberOfClusters; + Buffer->CallerAvailableAllocationUnits.LowPart = + Vcb->AllocationSupport.NumberOfFreeClusters; + Buffer->ActualAvailableAllocationUnits.LowPart = + Buffer->CallerAvailableAllocationUnits.LowPart; + Buffer->SectorsPerAllocationUnit = Vcb->Bpb.SectorsPerCluster; + Buffer->BytesPerSector = Vcb->Bpb.BytesPerSector; + + // + // Adjust the length variable + // + + *Length -= sizeof(FILE_FS_FULL_SIZE_INFORMATION); + + // + // And return success to our caller + // + + UNREFERENCED_PARAMETER( IrpContext ); + + return STATUS_SUCCESS; +} + + +// +// Internal support routine +// + +NTSTATUS +FatSetFsLabelInfo ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PFILE_FS_LABEL_INFORMATION Buffer + ) + +/*++ + +Routine Description: + + This routine implements the set volume label call + +Arguments: + + Vcb - Supplies the Vcb being queried + + Buffer - Supplies the input where the information is stored. + +Return Value: + + NTSTATUS - Returns the status for the operation + +--*/ + +{ + NTSTATUS Status; + + PDIRENT Dirent; + PBCB DirentBcb = NULL; + ULONG ByteOffset; + + WCHAR TmpBuffer[11]; + UCHAR OemBuffer[11]; + OEM_STRING OemLabel; + UNICODE_STRING UnicodeString; + UNICODE_STRING UpcasedLabel; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatSetFsLabelInfo...\n", 0); + + // + // Setup our local variable + // + + UnicodeString.Length = (USHORT)Buffer->VolumeLabelLength; + UnicodeString.MaximumLength = UnicodeString.Length; + UnicodeString.Buffer = (PWSTR) &Buffer->VolumeLabel[0]; + + // + // Make sure the name can fit into the stack buffer + // + + if ( UnicodeString.Length > 11*sizeof(WCHAR) ) { + + return STATUS_INVALID_VOLUME_LABEL; + } + + // + // Upcase the name and convert it to the Oem code page. + // + + OemLabel.Buffer = (PCHAR)&OemBuffer[0]; + OemLabel.Length = 0; + OemLabel.MaximumLength = 11; + + Status = RtlUpcaseUnicodeStringToCountedOemString( &OemLabel, + &UnicodeString, + FALSE ); + + // + // Volume label that fits in 11 unicode character length limit + // is not necessary within 11 characters in OEM character set. + // + + if (!NT_SUCCESS( Status )) { + + DebugTrace(-1, Dbg, "FatSetFsLabelInfo: Label must be too long. %08lx\n", Status ); + + return STATUS_INVALID_VOLUME_LABEL; + } + + // + // Strip spaces off of the label. + // + + if (OemLabel.Length > 0) { + + USHORT i; + USHORT LastSpaceIndex = MAXUSHORT; + + // + // Check the label for illegal characters + // + + for ( i = 0; i < (ULONG)OemLabel.Length; i += 1 ) { + + if ( FsRtlIsLeadDbcsCharacter( OemLabel.Buffer[i] ) ) { + + LastSpaceIndex = MAXUSHORT; + i += 1; + continue; + } + + if (!FsRtlIsAnsiCharacterLegalFat(OemLabel.Buffer[i], FALSE) || + (OemLabel.Buffer[i] == '.')) { + + return STATUS_INVALID_VOLUME_LABEL; + } + + // + // Watch for the last run of spaces, so we can strip them. + // + + if (OemLabel.Buffer[i] == ' ' && + LastSpaceIndex == MAXUSHORT) { + LastSpaceIndex = i; + } else { + LastSpaceIndex = MAXUSHORT; + } + } + + if (LastSpaceIndex != MAXUSHORT) { + OemLabel.Length = LastSpaceIndex; + } + } + + // + // Get the Unicode upcased string to store in the VPB. + // + + UpcasedLabel.Length = UnicodeString.Length; + UpcasedLabel.MaximumLength = 11*sizeof(WCHAR); + UpcasedLabel.Buffer = &TmpBuffer[0]; + + Status = RtlOemStringToCountedUnicodeString( &UpcasedLabel, + &OemLabel, + FALSE ); + + if (!NT_SUCCESS( Status )) { + + DebugTrace(-1, Dbg, "FatSetFsLabelInfo: Label must be too long. %08lx\n", Status ); + + return STATUS_INVALID_VOLUME_LABEL; + } + + DirentBcb = NULL; + + // + // Make this look like a write through to disk. This is important to + // avoid a unpleasant window where it looks like we have the wrong volume. + // + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WRITE_THROUGH ); + + try { + + // + // Are we setting or removing the label? Note that shaving spaces could + // make this different than wondering if the input buffer is non-zero length. + // + + if (OemLabel.Length > 0) { + + // + // Locate the volume label if there already is one + // + + FatLocateVolumeLabel( IrpContext, + Vcb, + &Dirent, + &DirentBcb, + (PVBO)&ByteOffset ); + + // + // Check that we really got one, if not then we need to create + // a new one. The procedure we call will raise an appropriate + // status if we are not able to allocate a new dirent + // + + if (Dirent == NULL) { + + ByteOffset = FatCreateNewDirent( IrpContext, + Vcb->RootDcb, + 1, + FALSE ); + + FatPrepareWriteDirectoryFile( IrpContext, + Vcb->RootDcb, + ByteOffset, + sizeof(DIRENT), + &DirentBcb, + &Dirent, + FALSE, + TRUE, + &Status ); + + NT_ASSERT( NT_SUCCESS( Status )); + + } else { + + // + // Just mark this guy dirty now. + // + + FatSetDirtyBcb( IrpContext, DirentBcb, Vcb, TRUE ); + } + + // + // Now reconstruct the volume label dirent. + // + + FatConstructLabelDirent( IrpContext, + Dirent, + &OemLabel ); + + // + // Unpin the Bcb here so that we will get any IO errors + // here before changing the VPB label. + // + + FatUnpinBcb( IrpContext, DirentBcb ); + FatUnpinRepinnedBcbs( IrpContext ); + + // + // Now set the upcased label in the VPB + // + + RtlCopyMemory( &Vcb->Vpb->VolumeLabel[0], + &UpcasedLabel.Buffer[0], + UpcasedLabel.Length ); + + Vcb->Vpb->VolumeLabelLength = UpcasedLabel.Length; + + } else { + + // + // Otherwise we're trying to delete the label + // Locate the current volume label if there already is one + // + + FatLocateVolumeLabel( IrpContext, + Vcb, + &Dirent, + &DirentBcb, + (PVBO)&ByteOffset ); + + // + // Check that we really got one + // + + if (Dirent == NULL) { + + try_return( Status = STATUS_SUCCESS ); + } + + // + // Now delete the current label. + // + + Dirent->FileName[0] = FAT_DIRENT_DELETED; + + NT_ASSERT( (Vcb->RootDcb->Specific.Dcb.UnusedDirentVbo == 0xffffffff) || + RtlAreBitsSet( &Vcb->RootDcb->Specific.Dcb.FreeDirentBitmap, + ByteOffset / sizeof(DIRENT), + 1 ) ); + + RtlClearBits( &Vcb->RootDcb->Specific.Dcb.FreeDirentBitmap, + ByteOffset / sizeof(DIRENT), + 1 ); + + FatSetDirtyBcb( IrpContext, DirentBcb, Vcb, TRUE ); + + // + // Unpin the Bcb here so that we will get any IO errors + // here before changing the VPB label. + // + + FatUnpinBcb( IrpContext, DirentBcb ); + FatUnpinRepinnedBcbs( IrpContext ); + + // + // Now set the label in the VPB + // + + Vcb->Vpb->VolumeLabelLength = 0; + } + + Status = STATUS_SUCCESS; + + try_exit: NOTHING; + } finally { + + DebugUnwind( FatSetFsALabelInfo ); + + FatUnpinBcb( IrpContext, DirentBcb ); + + DebugTrace(-1, Dbg, "FatSetFsALabelInfo -> STATUS_SUCCESS\n", 0); + } + + return Status; +} + +#if (NTDDI_VERSION >= NTDDI_WIN8) + +NTSTATUS +FatQueryFsSectorSizeInfo ( + _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/fastfat/workque.c b/filesys/fastfat/workque.c new file mode 100644 index 000000000..cb89b6b61 --- /dev/null +++ b/filesys/fastfat/workque.c @@ -0,0 +1,368 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + WorkQue.c + +Abstract: + + This module implements the Work queue routines for the Fat File + system. + + +--*/ + +#include "FatProcs.h" + +// +// 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) + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, FatOplockComplete) +#pragma alloc_text(PAGE, FatPrePostIrp) +#pragma alloc_text(PAGE, FatFsdPostRequest) +#endif + + +VOID +FatOplockComplete ( + IN PVOID Context, + IN 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. + +Arguments: + + Context - Pointer to the IrpContext to be queued to the Fsp + + Irp - I/O Request Packet. + +Return Value: + + None. + +--*/ + +{ + PAGED_CODE(); + + // + // Check on the return value in the Irp. + // + + if (Irp->IoStatus.Status == STATUS_SUCCESS) { + + // + // Insert the Irp context in the workqueue. + // + + FatAddToWorkque( (PIRP_CONTEXT) Context, Irp ); + + // + // Otherwise complete the request. + // + + } else { + + FatCompleteRequest( (PIRP_CONTEXT) Context, Irp, Irp->IoStatus.Status ); + } + + return; +} + + +VOID +FatPrePostIrp ( + IN PVOID Context, + IN 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; + PIRP_CONTEXT IrpContext; + + PAGED_CODE(); + + // + // If there is no Irp, we are done. + // + + if (Irp == NULL) { + + return; + } + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + IrpContext = (PIRP_CONTEXT) Context; + + // + // If there is a STACK FatIoContext pointer, clean and NULL it. + // + + if ((IrpContext->FatIoContext != NULL) && + FlagOn(IrpContext->Flags, IRP_CONTEXT_STACK_IO_CONTEXT)) { + + ClearFlag(IrpContext->Flags, IRP_CONTEXT_STACK_IO_CONTEXT); + IrpContext->FatIoContext = NULL; + } + + // + // We need to lock the user's buffer, unless this is an MDL-read, + // in which case there is no user buffer. + // + // **** we need a better test than non-MDL (read or write)! + + if (IrpContext->MajorFunction == IRP_MJ_READ || + IrpContext->MajorFunction == IRP_MJ_WRITE) { + + // + // If not an Mdl request, lock the user's buffer. + // + + if (!FlagOn( IrpContext->MinorFunction, IRP_MN_MDL )) { + + FatLockUserBuffer( IrpContext, + Irp, + (IrpContext->MajorFunction == IRP_MJ_READ) ? + IoWriteAccess : IoReadAccess, + (IrpContext->MajorFunction == IRP_MJ_READ) ? + IrpSp->Parameters.Read.Length : IrpSp->Parameters.Write.Length ); + } + + // + // We also need to check whether this is a query file operation. + // + + } else if (IrpContext->MajorFunction == IRP_MJ_DIRECTORY_CONTROL + && IrpContext->MinorFunction == IRP_MN_QUERY_DIRECTORY) { + + FatLockUserBuffer( IrpContext, + Irp, + IoWriteAccess, + IrpSp->Parameters.QueryDirectory.Length ); + + // + // We also need to check whether this is a query ea operation. + // + + } else if (IrpContext->MajorFunction == IRP_MJ_QUERY_EA) { + + FatLockUserBuffer( IrpContext, + Irp, + IoWriteAccess, + IrpSp->Parameters.QueryEa.Length ); + + // + // We also need to check whether this is a set ea operation. + // + + } else if (IrpContext->MajorFunction == IRP_MJ_SET_EA) { + + FatLockUserBuffer( IrpContext, + Irp, + IoReadAccess, + IrpSp->Parameters.SetEa.Length ); + + // + // These two FSCTLs use neither I/O, so check for them. + // + + } else if ((IrpContext->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL) && + (IrpContext->MinorFunction == IRP_MN_USER_FS_REQUEST) && + ((IrpSp->Parameters.FileSystemControl.FsControlCode == FSCTL_GET_VOLUME_BITMAP) || + (IrpSp->Parameters.FileSystemControl.FsControlCode == FSCTL_GET_RETRIEVAL_POINTERS))) { + + FatLockUserBuffer( IrpContext, + Irp, + IoWriteAccess, + IrpSp->Parameters.FileSystemControl.OutputBufferLength ); + } + + // + // Mark that we've already returned pending to the user + // + + IoMarkIrpPending( Irp ); + + return; +} + + +NTSTATUS +FatFsdPostRequest( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine enqueues the request packet specified by IrpContext to the + Ex Worker threads. This is a FSD routine. + +Arguments: + + IrpContext - Pointer to the IrpContext to be queued to the Fsp + + Irp - I/O Request Packet, or NULL if it has already been completed. + +Return Value: + + STATUS_PENDING + + +--*/ + +{ + PAGED_CODE(); + + NT_ASSERT( ARGUMENT_PRESENT(Irp) ); + NT_ASSERT( IrpContext->OriginatingIrp == Irp ); + + FatPrePostIrp( IrpContext, Irp ); + + FatAddToWorkque( IrpContext, Irp ); + + // + // And return to our caller + // + + return STATUS_PENDING; +} + + +// +// Local support routine. +// + +VOID +FatAddToWorkque ( + IN PIRP_CONTEXT IrpContext, + IN 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. + +--*/ + +{ + KIRQL SavedIrql; + PIO_STACK_LOCATION IrpSp; + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + // + // Check if this request has an associated file object, and thus volume + // device object. + // + + if ( IrpSp->FileObject != NULL ) { + + PVOLUME_DEVICE_OBJECT Vdo; + + 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..... + // + + ExInitializeWorkItem( &IrpContext->WorkQueueItem, + FatFspDispatch, + IrpContext ); + +#pragma prefast( suppress:28159, "prefast indicates this is an obsolete API but it is ok for fastfat to keep using it." ) + ExQueueWorkItem( &IrpContext->WorkQueueItem, CriticalWorkQueue ); + + return; +} + + diff --git a/filesys/fastfat/write.c b/filesys/fastfat/write.c new file mode 100644 index 000000000..84aefe83b --- /dev/null +++ b/filesys/fastfat/write.c @@ -0,0 +1,3050 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + Write.c + +Abstract: + + This module implements the File Write routine for Write called by the + dispatch driver. + + +--*/ + +#include "FatProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (FAT_BUG_CHECK_WRITE) + +// +// The local debug trace level +// + +#define Dbg (DEBUG_TRACE_WRITE) + +// +// Macros to increment the appropriate performance counters. +// + +#define CollectWriteStats(VCB,OPEN_TYPE,BYTE_COUNT) { \ + PFILESYSTEM_STATISTICS Stats = &(VCB)->Statistics[KeGetCurrentProcessorNumber() % FatData.NumberProcessors].Common; \ + if (((OPEN_TYPE) == UserFileOpen)) { \ + Stats->UserFileWrites += 1; \ + Stats->UserFileWriteBytes += (ULONG)(BYTE_COUNT); \ + } else if (((OPEN_TYPE) == VirtualVolumeFile || ((OPEN_TYPE) == DirectoryFile))) { \ + Stats->MetaDataWrites += 1; \ + Stats->MetaDataWriteBytes += (ULONG)(BYTE_COUNT); \ + } \ +} + +BOOLEAN FatNoAsync = FALSE; + +// +// Local support routines +// + +KDEFERRED_ROUTINE FatDeferredFlushDpc; + +VOID +FatDeferredFlushDpc ( + _In_ PKDPC Dpc, + _In_opt_ PVOID DeferredContext, + _In_opt_ PVOID SystemArgument1, + _In_opt_ PVOID SystemArgument2 + ); + +WORKER_THREAD_ROUTINE FatDeferredFlush; + +VOID +FatDeferredFlush ( + _In_ PVOID Parameter + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, FatDeferredFlush) +#pragma alloc_text(PAGE, FatCommonWrite) +#endif + + +_Function_class_(IRP_MJ_WRITE) +_Function_class_(DRIVER_DISPATCH) +NTSTATUS +FatFsdWrite ( + _In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + _Inout_ PIRP Irp + ) + +/*++ + +Routine Description: + + This routine implements the FSD part of the NtWriteFile API call + +Arguments: + + VolumeDeviceObject - Supplies the volume device object where the + file being Write exists + + Irp - Supplies the Irp being processed + +Return Value: + + NTSTATUS - The FSD status for the IRP + +--*/ + +{ + PFCB Fcb; + NTSTATUS Status; + PIRP_CONTEXT IrpContext = NULL; + + BOOLEAN ModWriter = FALSE; + BOOLEAN TopLevel = FALSE; + + DebugTrace(+1, Dbg, "FatFsdWrite\n", 0); + + // + // Call the common Write routine, with blocking allowed if synchronous + // + + FsRtlEnterFileSystem(); + + // + // We are first going to do a quick check for paging file IO. Since this + // is a fast path, we must replicate the check for the fsdo. + // + + if (!FatDeviceIsFatFsdo( IoGetCurrentIrpStackLocation(Irp)->DeviceObject)) { + + Fcb = (PFCB)(IoGetCurrentIrpStackLocation(Irp)->FileObject->FsContext); + + if ((NodeType(Fcb) == FAT_NTC_FCB) && + FlagOn(Fcb->FcbState, FCB_STATE_PAGING_FILE)) { + + // + // Do the usual STATUS_PENDING things. + // + + IoMarkIrpPending( Irp ); + + // + // Perform the actual IO, it will be completed when the io finishes. + // + + FatPagingFileIo( Irp, Fcb ); + + FsRtlExitFileSystem(); + + return STATUS_PENDING; + } + } + + try { + + TopLevel = FatIsIrpTopLevel( Irp ); + + IrpContext = FatCreateIrpContext( Irp, CanFsdWait( Irp ) ); + + // + // This is a kludge for the mod writer case. The correct state + // of recursion is set in IrpContext, however, we much with the + // actual top level Irp field to get the correct WriteThrough + // behaviour. + // + + if (IoGetTopLevelIrp() == (PIRP)FSRTL_MOD_WRITE_TOP_LEVEL_IRP) { + + ModWriter = TRUE; + + IoSetTopLevelIrp( Irp ); + } + + // + // If this is an Mdl complete request, don't go through + // common write. + // + + if (FlagOn( IrpContext->MinorFunction, IRP_MN_COMPLETE )) { + + DebugTrace(0, Dbg, "Calling FatCompleteMdl\n", 0 ); + Status = FatCompleteMdl( IrpContext, Irp ); + + } else { + + Status = FatCommonWrite( IrpContext, Irp ); + } + + } except(FatExceptionFilter( IrpContext, GetExceptionInformation() )) { + + // + // We had some trouble trying to perform the requested + // operation, so we'll abort the I/O request with + // the error status that we get back from the + // execption code + // + + Status = FatProcessException( IrpContext, Irp, GetExceptionCode() ); + } + +// NT_ASSERT( !(ModWriter && (Status == STATUS_CANT_WAIT)) ); + + NT_ASSERT( !(ModWriter && TopLevel) ); + + if (ModWriter) { IoSetTopLevelIrp((PIRP)FSRTL_MOD_WRITE_TOP_LEVEL_IRP); } + + if (TopLevel) { IoSetTopLevelIrp( NULL ); } + + FsRtlExitFileSystem(); + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "FatFsdWrite -> %08lx\n", Status); + + UNREFERENCED_PARAMETER( VolumeDeviceObject ); + + return Status; +} + + +_Requires_lock_held_(_Global_critical_region_) +NTSTATUS +FatCommonWrite ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the common write routine for NtWriteFile, called from both + the Fsd, or from the Fsp if a request could not be completed without + blocking in the Fsd. This routine's actions are + conditionalized by the Wait input parameter, which determines whether + it is allowed to block or not. If a blocking condition is encountered + with Wait == FALSE, however, the request is posted to the Fsp, who + always calls with WAIT == TRUE. + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + PVCB Vcb; + PFCB FcbOrDcb; + PCCB Ccb; + + VBO StartingVbo; + ULONG ByteCount; + ULONG FileSize = 0; + ULONG InitialFileSize = 0; + ULONG InitialValidDataLength = 0; + + PIO_STACK_LOCATION IrpSp; + PFILE_OBJECT FileObject; + TYPE_OF_OPEN TypeOfOpen; + + BOOLEAN PostIrp = FALSE; + BOOLEAN OplockPostIrp = FALSE; + BOOLEAN ExtendingFile = FALSE; + BOOLEAN FcbOrDcbAcquired = FALSE; + BOOLEAN SwitchBackToAsync = FALSE; + BOOLEAN CalledByLazyWriter = FALSE; + BOOLEAN ExtendingValidData = FALSE; + BOOLEAN FcbAcquiredExclusive = FALSE; + BOOLEAN FcbCanDemoteToShared = FALSE; + BOOLEAN WriteFileSizeToDirent = FALSE; + BOOLEAN RecursiveWriteThrough = FALSE; + BOOLEAN UnwindOutstandingAsync = FALSE; + BOOLEAN PagingIoResourceAcquired = FALSE; + BOOLEAN SuccessfulPurge = FALSE; + + BOOLEAN SynchronousIo; + BOOLEAN WriteToEof; + BOOLEAN PagingIo; + BOOLEAN NonCachedIo; + BOOLEAN Wait; + NTSTATUS Status = STATUS_SUCCESS; + + FAT_IO_CONTEXT StackFatIoContext; + + // + // A system buffer is only used if we have to access the buffer directly + // from the Fsp to clear a portion or to do a synchronous I/O, or a + // cached transfer. It is possible that our caller may have already + // mapped a system buffer, in which case we must remember this so + // we do not unmap it on the way out. + // + + PVOID SystemBuffer = (PVOID) NULL; + + LARGE_INTEGER StartingByte; + + PAGED_CODE(); + + // + // Get current Irp stack location and file object + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + FileObject = IrpSp->FileObject; + + + DebugTrace(+1, Dbg, "FatCommonWrite\n", 0); + DebugTrace( 0, Dbg, "Irp = %p\n", Irp); + DebugTrace( 0, Dbg, "ByteCount = %8lx\n", IrpSp->Parameters.Write.Length); + DebugTrace( 0, Dbg, "ByteOffset.LowPart = %8lx\n", IrpSp->Parameters.Write.ByteOffset.LowPart); + DebugTrace( 0, Dbg, "ByteOffset.HighPart = %8lx\n", IrpSp->Parameters.Write.ByteOffset.HighPart); + + // + // Initialize the appropriate local variables. + // + + Wait = BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT); + PagingIo = BooleanFlagOn(Irp->Flags, IRP_PAGING_IO); + NonCachedIo = BooleanFlagOn(Irp->Flags,IRP_NOCACHE); + SynchronousIo = BooleanFlagOn(FileObject->Flags, FO_SYNCHRONOUS_IO); + + //NT_ASSERT( PagingIo || FileObject->WriteAccess ); + + // + // Extract the bytecount and do our noop/throttle checking. + // + + ByteCount = IrpSp->Parameters.Write.Length; + + // + // If there is nothing to write, return immediately. + // + + if (ByteCount == 0) { + + Irp->IoStatus.Information = 0; + FatCompleteRequest( IrpContext, Irp, STATUS_SUCCESS ); + return STATUS_SUCCESS; + } + + // + // See if we have to defer the write. + // + + if (!NonCachedIo && + !CcCanIWrite(FileObject, + ByteCount, + (BOOLEAN)(Wait && !BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_IN_FSP)), + BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_DEFERRED_WRITE))) { + + BOOLEAN Retrying = BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_DEFERRED_WRITE); + + FatPrePostIrp( IrpContext, Irp ); + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_DEFERRED_WRITE ); + + CcDeferWrite( FileObject, + (PCC_POST_DEFERRED_WRITE)FatAddToWorkque, + IrpContext, + Irp, + ByteCount, + Retrying ); + + return STATUS_PENDING; + } + + // + // Determine our starting position and type. If we are writing + // at EOF, then we will need additional synchronization before + // the IO is issued to determine where the data will go. + // + + StartingByte = IrpSp->Parameters.Write.ByteOffset; + StartingVbo = StartingByte.LowPart; + + WriteToEof = ( (StartingByte.LowPart == FILE_WRITE_TO_END_OF_FILE) && + (StartingByte.HighPart == -1) ); + + // + // Extract the nature of the write from the file object, and case on it + // + + TypeOfOpen = FatDecodeFileObject(FileObject, &Vcb, &FcbOrDcb, &Ccb); + + NT_ASSERT( Vcb != NULL ); + + // + // Save callers who try to do cached IO to the raw volume from themselves. + // + + if (TypeOfOpen == UserVolumeOpen) { + + NonCachedIo = TRUE; + } + + NT_ASSERT(!(NonCachedIo == FALSE && TypeOfOpen == VirtualVolumeFile)); + + // + // Collect interesting statistics. The FLAG_USER_IO bit will indicate + // what type of io we're doing in the FatNonCachedIo function. + // + + if (PagingIo) { + CollectWriteStats(Vcb, TypeOfOpen, ByteCount); + + if (TypeOfOpen == UserFileOpen) { + SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_USER_IO); + } else { + ClearFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_USER_IO); + } + } + + // + // We must disallow writes to regular objects that would require us + // to maintain an AllocationSize of greater than 32 significant bits. + // + // If this is paging IO, this is simply a case where we need to trim. + // This will occur in due course. + // + + if (!PagingIo && !WriteToEof && (TypeOfOpen != UserVolumeOpen)) { + + + if (!FatIsIoRangeValid( Vcb, StartingByte, ByteCount)) { + + + Irp->IoStatus.Information = 0; + FatCompleteRequest( IrpContext, Irp, STATUS_DISK_FULL ); + + return STATUS_DISK_FULL; + } + } + + // + // Allocate if necessary and initialize a FAT_IO_CONTEXT block for + // all non cached Io. For synchronous Io + // we use stack storage, otherwise we allocate pool. + // + + if (NonCachedIo) { + + if (IrpContext->FatIoContext == NULL) { + + if (!Wait) { + + IrpContext->FatIoContext = + FsRtlAllocatePoolWithTag( NonPagedPoolNx, + sizeof(FAT_IO_CONTEXT), + TAG_FAT_IO_CONTEXT ); + + } else { + + IrpContext->FatIoContext = &StackFatIoContext; + + SetFlag( IrpContext->Flags, IRP_CONTEXT_STACK_IO_CONTEXT ); + } + } + + RtlZeroMemory( IrpContext->FatIoContext, sizeof(FAT_IO_CONTEXT) ); + + if (Wait) { + + KeInitializeEvent( &IrpContext->FatIoContext->Wait.SyncEvent, + NotificationEvent, + FALSE ); + + } else { + + if (PagingIo) { + + IrpContext->FatIoContext->Wait.Async.ResourceThreadId = + ExGetCurrentResourceThread(); + + } else { + + IrpContext->FatIoContext->Wait.Async.ResourceThreadId = + ((ULONG_PTR)IrpContext->FatIoContext) | 3; + } + + IrpContext->FatIoContext->Wait.Async.RequestedByteCount = + ByteCount; + + IrpContext->FatIoContext->Wait.Async.FileObject = FileObject; + } + } + + // + // Check if this volume has already been shut down. If it has, fail + // this write request. + // + + if ( FlagOn(Vcb->VcbState, VCB_STATE_FLAG_SHUTDOWN) ) { + + Irp->IoStatus.Information = 0; + FatCompleteRequest( IrpContext, Irp, STATUS_TOO_LATE ); + return STATUS_TOO_LATE; + } + + // + // This case corresponds to a write of the volume file (only the first + // fat allowed, the other fats are written automatically in parallel). + // + // We use an Mcb keep track of dirty sectors. Actual entries are Vbos + // and Lbos (ie. bytes), though they are all added in sector chunks. + // Since Vbo == Lbo for the volume file, the Mcb entries + // alternate between runs of Vbo == Lbo, and holes (Lbo == 0). We use + // the prior to represent runs of dirty fat sectors, and the latter + // for runs of clean fat. Note that since the first part of the volume + // file (boot sector) is always clean (a hole), and an Mcb never ends in + // a hole, there must always be an even number of runs(entries) in the Mcb. + // + // The strategy is to find the first and last dirty run in the desired + // write range (which will always be a set of pages), and write from the + // former to the later. The may result in writing some clean data, but + // will generally be more efficient than writing each runs seperately. + // + + if (TypeOfOpen == VirtualVolumeFile) { + + LBO DirtyLbo; + LBO CleanLbo; + + VBO DirtyVbo; + VBO StartingDirtyVbo; + + ULONG DirtyByteCount; + ULONG CleanByteCount; + + ULONG WriteLength; + + BOOLEAN MoreDirtyRuns = TRUE; + + IO_STATUS_BLOCK RaiseIosb; + + DebugTrace(0, Dbg, "Type of write is Virtual Volume File\n", 0); + + // + // If we can't wait we have to post this. + // + + if (!Wait) { + + DebugTrace( 0, Dbg, "Passing request to Fsp\n", 0 ); + + Status = FatFsdPostRequest(IrpContext, Irp); + + return Status; + } + + // + // If we weren't called by the Lazy Writer, then this write + // must be the result of a write-through or flush operation. + // Setting the IrpContext flag, will cause DevIoSup.c to + // write-through the data to the disk. + // + + if (!FlagOn((ULONG_PTR)IoGetTopLevelIrp(), FSRTL_CACHE_TOP_LEVEL_IRP)) { + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WRITE_THROUGH ); + } + + // + // Assert an even number of entries in the Mcb, an odd number would + // mean that the Mcb is corrupt. + // + + NT_ASSERT( (FsRtlNumberOfRunsInLargeMcb( &Vcb->DirtyFatMcb ) & 1) == 0); + + // + // We need to skip over any clean sectors at the start of the write. + // + // Also check the two cases where there are no dirty fats in the + // desired write range, and complete them with success. + // + // 1) There is no Mcb entry corresponding to StartingVbo, meaning + // we are beyond the end of the Mcb, and thus dirty fats. + // + // 2) The run at StartingVbo is clean and continues beyond the + // desired write range. + // + + if (!FatLookupMcbEntry( Vcb, &Vcb->DirtyFatMcb, + StartingVbo, + &DirtyLbo, + &DirtyByteCount, + NULL ) + + || ( (DirtyLbo == 0) && (DirtyByteCount >= ByteCount) ) ) { + + DebugTrace(0, DEBUG_TRACE_DEBUG_HOOKS, + "No dirty fat sectors in the write range.\n", 0); + + FatCompleteRequest( IrpContext, Irp, STATUS_SUCCESS ); + return STATUS_SUCCESS; + } + + DirtyVbo = (VBO)DirtyLbo; + + // + // If the last run was a hole (clean), up DirtyVbo to the next + // run, which must be dirty. + // + + if (DirtyVbo == 0) { + + DirtyVbo = StartingVbo + DirtyByteCount; + } + + // + // This is where the write will start. + // + + StartingDirtyVbo = DirtyVbo; + + // + // + // Now start enumerating the dirty fat sectors spanning the desired + // write range, this first one of which is now DirtyVbo. + // + + while ( MoreDirtyRuns ) { + + // + // Find the next dirty run, if it is not there, the Mcb ended + // in a hole, or there is some other corruption of the Mcb. + // + + if (!FatLookupMcbEntry( Vcb, &Vcb->DirtyFatMcb, + DirtyVbo, + &DirtyLbo, + &DirtyByteCount, + NULL )) { + +#pragma prefast( suppress:28931, "needed for debug build" ) + DirtyVbo = (VBO)DirtyLbo; + + DebugTrace(0, Dbg, "Last dirty fat Mcb entry was a hole: corrupt.\n", 0); + +#pragma prefast( suppress:28159, "things are seriously wrong if we get here" ) + FatBugCheck( 0, 0, 0 ); + + } else { + + DirtyVbo = (VBO)DirtyLbo; + + // + // This has to correspond to a dirty run, and must start + // within the write range since we check it at entry to, + // and at the bottom of this loop. + // + + NT_ASSERT((DirtyVbo != 0) && (DirtyVbo < StartingVbo + ByteCount)); + + // + // There are three ways we can know that this was the + // last dirty run we want to write. + // + // 1) The current dirty run extends beyond or to the + // desired write range. + // + // 2) On trying to find the following clean run, we + // discover that this is the last run in the Mcb. + // + // 3) The following clean run extend beyond the + // desired write range. + // + // In any of these cases we set MoreDirtyRuns = FALSE. + // + + // + // If the run is larger than we are writing, we also + // must truncate the WriteLength. This is benign in + // the equals case. + // + + if (DirtyVbo + DirtyByteCount >= StartingVbo + ByteCount) { + + DirtyByteCount = StartingVbo + ByteCount - DirtyVbo; + + MoreDirtyRuns = FALSE; + + } else { + + // + // Scan the clean hole after this dirty run. If this + // run was the last, prepare to exit the loop + // + + if (!FatLookupMcbEntry( Vcb, &Vcb->DirtyFatMcb, + DirtyVbo + DirtyByteCount, + &CleanLbo, + &CleanByteCount, + NULL )) { + + MoreDirtyRuns = FALSE; + + } else { + + // + // Assert that we actually found a clean run. + // and compute the start of the next dirty run. + // + + NT_ASSERT (CleanLbo == 0); + + // + // If the next dirty run starts beyond the desired + // write, we have found all the runs we need, so + // prepare to exit. + // + + if (DirtyVbo + DirtyByteCount + CleanByteCount >= + StartingVbo + ByteCount) { + + MoreDirtyRuns = FALSE; + + } else { + + // + // Compute the start of the next dirty run. + // + + DirtyVbo += DirtyByteCount + CleanByteCount; + } + } + } + } + } // while ( MoreDirtyRuns ) + + // + // At this point DirtyVbo and DirtyByteCount correctly reflect the + // final dirty run, constrained to the desired write range. + // + // Now compute the length we finally must write. + // + + WriteLength = (DirtyVbo + DirtyByteCount) - StartingDirtyVbo; + + // + // We must now assume that the write will complete with success, + // and initialize our expected status in RaiseIosb. It will be + // modified below if an error occurs. + // + + RaiseIosb.Status = STATUS_SUCCESS; + RaiseIosb.Information = ByteCount; + + // + // Loop through all the fats, setting up a multiple async to + // write them all. If there are more than FAT_MAX_PARALLEL_IOS + // then we do several muilple asyncs. + // + + { + ULONG Fat; + ULONG BytesPerFat; + IO_RUN StackIoRuns[2]; + PIO_RUN IoRuns; + + BytesPerFat = FatBytesPerFat( &Vcb->Bpb ); + + if ((ULONG)Vcb->Bpb.Fats > 2) { + + IoRuns = FsRtlAllocatePoolWithTag( PagedPool, + (ULONG)(Vcb->Bpb.Fats*sizeof(IO_RUN)), + TAG_IO_RUNS ); + + } else { + + IoRuns = StackIoRuns; + } + + for (Fat = 0; Fat < (ULONG)Vcb->Bpb.Fats; Fat++) { + + IoRuns[Fat].Vbo = StartingDirtyVbo; + IoRuns[Fat].Lbo = Fat * BytesPerFat + StartingDirtyVbo; + IoRuns[Fat].Offset = StartingDirtyVbo - StartingVbo; + IoRuns[Fat].ByteCount = WriteLength; + } + + // + // Keep track of meta-data disk ios. + // + + Vcb->Statistics[KeGetCurrentProcessorNumber() % FatData.NumberProcessors].Common.MetaDataDiskWrites += Vcb->Bpb.Fats; + + try { + + FatMultipleAsync( IrpContext, + Vcb, + Irp, + (ULONG)Vcb->Bpb.Fats, + IoRuns ); + + } finally { + + if (IoRuns != StackIoRuns) { + + ExFreePool( IoRuns ); + } + } + +#if (NTDDI_VERSION >= NTDDI_WIN8) + + // + // Account for DASD Ios + // + + if (FatDiskAccountingEnabled) { + + PETHREAD ThreadIssuingIo = PsGetCurrentThread(); + + PsUpdateDiskCounters( PsGetThreadProcess( ThreadIssuingIo ), + 0, + WriteLength, + 0, + 1, + 0 ); + } + +#endif + // + // Wait for all the writes to finish + // + + FatWaitSync( IrpContext ); + + // + // If we got an error, or verify required, remember it. + // + + if (!NT_SUCCESS( Irp->IoStatus.Status )) { + + DebugTrace( 0, + Dbg, + "Error %X while writing volume file.\n", + Irp->IoStatus.Status ); + + RaiseIosb = Irp->IoStatus; + } + } + + // + // If the writes were a success, set the sectors clean, else + // raise the error status and mark the volume as needing + // verification. This will automatically reset the volume + // structures. + // + // If not, then mark this volume as needing verification to + // automatically cause everything to get cleaned up. + // + + Irp->IoStatus = RaiseIosb; + + if ( NT_SUCCESS( Status = Irp->IoStatus.Status )) { + + FatRemoveMcbEntry( Vcb, &Vcb->DirtyFatMcb, + StartingDirtyVbo, + WriteLength ); + + } else { + + FatNormalizeAndRaiseStatus( IrpContext, Status ); + } + + DebugTrace(-1, Dbg, "CommonRead -> %08lx\n", Status ); + + FatCompleteRequest( IrpContext, Irp, Status ); + return Status; + } + + // + // This case corresponds to a general opened volume (DASD), ie. + // open ("a:"). + // + + if (TypeOfOpen == UserVolumeOpen) { + + LBO StartingLbo; + LBO VolumeSize; + + // + // Precalculate the volume size since we're nearly always going + // to be wanting to use it. + // + + VolumeSize = (LBO) Int32x32To64( Vcb->Bpb.BytesPerSector, + (Vcb->Bpb.Sectors != 0 ? Vcb->Bpb.Sectors : + Vcb->Bpb.LargeSectors)); + + StartingLbo = StartingByte.QuadPart; + + DebugTrace(0, Dbg, "Type of write is User Volume.\n", 0); + + // + // If this is a write on a disk-based volume that is not locked, we need to limit + // the sectors we allow to be written within the volume. Specifically, we only + // allow writes to the reserved area. Note that extended DASD can still be used + // to write past the end of the volume. We also allow kernel mode callers to force + // access via a flag in the IRP. A handle that issued a dismount can write anywhere + // as well. + // + + if ((Vcb->TargetDeviceObject->DeviceType == FILE_DEVICE_DISK) && + !FlagOn( Vcb->VcbState, VCB_STATE_FLAG_LOCKED ) && + !FlagOn( IrpSp->Flags, SL_FORCE_DIRECT_WRITE ) && + !FlagOn( Ccb->Flags, CCB_FLAG_COMPLETE_DISMOUNT )) { + + // + // First check for a write beyond the end of the volume. + // + + if (!WriteToEof && (StartingLbo < VolumeSize)) { + + // + // This write is within the volume. Make sure it is not beyond the reserved section. + // + + if ((StartingLbo >= FatReservedBytes( &(Vcb->Bpb) )) || + (ByteCount > (FatReservedBytes( &(Vcb->Bpb) ) - StartingLbo))) { + + FatCompleteRequest( IrpContext, Irp, STATUS_ACCESS_DENIED ); + return STATUS_ACCESS_DENIED; + } + } + } + + // + // Verify that the volume for this handle is still valid, permitting + // operations to proceed on dismounted volumes via the handle which + // performed the dismount or sent a format unit command. + // + + if (!FlagOn( Ccb->Flags, CCB_FLAG_COMPLETE_DISMOUNT | CCB_FLAG_SENT_FORMAT_UNIT )) { + + FatQuickVerifyVcb( IrpContext, Vcb ); + } + + // + // If the caller previously sent a format unit command, then we will allow + // their read/write requests to ignore the verify flag on the device, since some + // devices send a media change event after format unit, but we don't want to + // process it yet since we're probably in the process of formatting the + // media. + // + + if (FlagOn( Ccb->Flags, CCB_FLAG_SENT_FORMAT_UNIT )) { + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_OVERRIDE_VERIFY ); + } + + if (!FlagOn( Ccb->Flags, CCB_FLAG_DASD_PURGE_DONE )) { + + BOOLEAN PreviousWait = BooleanFlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ); + + // + // Grab the entire volume so that even the normally unsafe action + // of writing to an unlocked volume won't open us to a race between + // the flush and purge of the FAT below. + // + // I really don't think this is particularly important to worry about, + // but a repro case for another bug happens to dance into this race + // condition pretty easily. Eh. + // + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ); + FatAcquireExclusiveVolume( IrpContext, Vcb ); + + try { + + // + // If the volume isn't locked, flush and purge it. + // + + if (!FlagOn(Vcb->VcbState, VCB_STATE_FLAG_LOCKED)) { + + FatFlushFat( IrpContext, Vcb ); + CcPurgeCacheSection( &Vcb->SectionObjectPointers, + NULL, + 0, + FALSE ); + + FatPurgeReferencedFileObjects( IrpContext, Vcb->RootDcb, Flush ); + } + + } finally { + + FatReleaseVolume( IrpContext, Vcb ); + if (!PreviousWait) { + ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ); + } + } + + SetFlag( Ccb->Flags, CCB_FLAG_DASD_PURGE_DONE | + CCB_FLAG_DASD_FLUSH_DONE ); + } + + if (!FlagOn( Ccb->Flags, CCB_FLAG_ALLOW_EXTENDED_DASD_IO )) { + + // + // Make sure we don't try to write past end of volume, + // reducing the requested byte count if necessary. + // + + if (WriteToEof || StartingLbo >= VolumeSize) { + FatCompleteRequest( IrpContext, Irp, STATUS_SUCCESS ); + return STATUS_SUCCESS; + } + + if (ByteCount > VolumeSize - StartingLbo) { + + ByteCount = (ULONG) (VolumeSize - StartingLbo); + + // + // For async writes we had set the byte count in the FatIoContext + // above, so fix that here. + // + + if (!Wait) { + + IrpContext->FatIoContext->Wait.Async.RequestedByteCount = + ByteCount; + } + } + } else { + + // + // This has a peculiar interpretation, but just adjust the starting + // byte to the end of the visible volume. + // + + if (WriteToEof) { + + StartingLbo = VolumeSize; + } + } + + // + // For DASD we have to probe and lock the user's buffer + // + + FatLockUserBuffer( IrpContext, Irp, IoReadAccess, ByteCount ); + + // + // 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( FileObject->Flags, FO_FILE_MODIFIED ); + + // + // Write the data and wait for the results + // + + FatSingleAsync( IrpContext, + Vcb, + StartingLbo, + ByteCount, + Irp ); + +#if (NTDDI_VERSION >= NTDDI_WIN8) + + // + // Account for DASD Ios + // + + if (FatDiskAccountingEnabled) { + + PETHREAD ThreadIssuingIo = PsGetCurrentThread(); + + PsUpdateDiskCounters( PsGetThreadProcess( ThreadIssuingIo ), + 0, + ByteCount, + 0, + 1, + 0 ); + } + +#endif + + if (!Wait) { + + // + // We, nor anybody else, need the IrpContext any more. + // + + IrpContext->FatIoContext = NULL; + + FatDeleteIrpContext( IrpContext ); + + DebugTrace(-1, Dbg, "FatNonCachedIo -> STATUS_PENDING\n", 0); + + return STATUS_PENDING; + } + + FatWaitSync( IrpContext ); + + // + // If the call didn't succeed, raise the error status + // + // Also mark this volume as needing verification to automatically + // cause everything to get cleaned up. + // + + if (!NT_SUCCESS( Status = Irp->IoStatus.Status )) { + + FatNormalizeAndRaiseStatus( IrpContext, Status ); + } + + // + // Update the current file position. We assume that + // open/create zeros out the CurrentByteOffset field. + // + + if (SynchronousIo && !PagingIo) { + FileObject->CurrentByteOffset.QuadPart = + StartingLbo + Irp->IoStatus.Information; + } + + DebugTrace(-1, Dbg, "FatCommonWrite -> %08lx\n", Status ); + + FatCompleteRequest( IrpContext, Irp, Status ); + return Status; + } + + // + // At this point we know there is an Fcb/Dcb. + // + + NT_ASSERT( FcbOrDcb != NULL ); + + // + // Use a try-finally to free Fcb/Dcb and buffers on the way out. + // + + try { + + // + // This case corresponds to a normal user write file. + // + + if ( TypeOfOpen == UserFileOpen + ) { + + ULONG ValidDataLength; + ULONG ValidDataToDisk; + ULONG ValidDataToCheck; + + DebugTrace(0, Dbg, "Type of write is user file open\n", 0); + + // + // If this is a noncached transfer and is not a paging I/O, and + // the file has been opened cached, then we will do a flush here + // to avoid stale data problems. Note that we must flush before + // acquiring the Fcb shared since the write may try to acquire + // it exclusive. + // + // The Purge following the flush will guarentee cache coherency. + // + + if (NonCachedIo && !PagingIo && + (FileObject->SectionObjectPointer->DataSectionObject != NULL)) { + + IO_STATUS_BLOCK IoStatus = {0}; + + // + // We need the Fcb exclsuive to do the CcPurgeCache + // + + if (!FatAcquireExclusiveFcb( IrpContext, FcbOrDcb )) { + + DebugTrace( 0, Dbg, "Cannot acquire FcbOrDcb = %p shared without waiting\n", FcbOrDcb ); + + try_return( PostIrp = TRUE ); + } + + FcbOrDcbAcquired = TRUE; + FcbAcquiredExclusive = TRUE; + + // + // Preacquire pagingio for the flush. + // + + ExAcquireResourceExclusiveLite( FcbOrDcb->Header.PagingIoResource, TRUE ); + +#if (NTDDI_VERSION >= NTDDI_WIN7) + + // + // Remember that we are holding the paging I/O resource. + // + + PagingIoResourceAcquired = TRUE; + + // + // We hold so that we will prevent a pagefault from occuring and seeing + // soon-to-be stale data from the disk. We used to believe this was + // something to be left to the app to synchronize; we now realize that + // noncached IO on a fileserver is doomed without the filesystem forcing + // the coherency issue. By only penalizing noncached coherency when + // needed, this is about the best we can do. + // + + // + // Now perform the coherency flush and purge operation. This version of the call + // will try to invalidate mapped pages to prevent data corruption. + // + + CcCoherencyFlushAndPurgeCache( FileObject->SectionObjectPointer, + WriteToEof ? &FcbOrDcb->Header.FileSize : &StartingByte, + ByteCount, + &IoStatus, + 0 ); + + SuccessfulPurge = NT_SUCCESS( IoStatus.Status ); + +#else + + CcFlushCache( FileObject->SectionObjectPointer, + WriteToEof ? &FcbOrDcb->Header.FileSize : &StartingByte, + ByteCount, + &IoStatus ); + + if (!NT_SUCCESS( IoStatus.Status )) { + + ExReleaseResourceLite( FcbOrDcb->Header.PagingIoResource ); + try_return( IoStatus.Status ); + } + + // + // Remember that we are holding the paging I/O resource. + // + + PagingIoResourceAcquired = TRUE; + + // + // We hold so that we will prevent a pagefault from occuring and seeing + // soon-to-be stale data from the disk. We used to believe this was + // something to be left to the app to synchronize; we now realize that + // noncached IO on a fileserver is doomed without the filesystem forcing + // the coherency issue. By only penalizing noncached coherency when + // needed, this is about the best we can do. + // + + SuccessfulPurge = CcPurgeCacheSection( FileObject->SectionObjectPointer, + WriteToEof ? &FcbOrDcb->Header.FileSize : &StartingByte, + ByteCount, + FALSE ); + +#endif + + if (!SuccessfulPurge && (FcbOrDcb->PurgeFailureModeEnableCount > 0)) { + + // + // Purge failure mode only applies to user files. + // + + NT_ASSERT( TypeOfOpen == UserFileOpen ); + + // + // Do not swallow the purge failure if in purge failure + // mode. Someone outside the file system intends to handle + // the error and prevent any application compatibilty + // issue. + // + // NOTE: If the file system were not preventing a pagefault + // from processing while this write is in flight, which it does + // by holding the paging resource across the write, it would + // need to fail the operation even if a purge succeeded. If + // not a memory mapped read could bring in a stale page before + // the write makes it to disk. + // + + try_return( Status = STATUS_PURGE_FAILED ); + } + + // + // Indicate we're OK with the fcb being demoted to shared access + // if that turns out to be possible later on after VDL extension + // is checked for. + // + // PagingIo must be held all the way through. + // + + FcbCanDemoteToShared = TRUE; + } + + // + // We assert that Paging Io writes will never WriteToEof. + // + + NT_ASSERT( WriteToEof ? !PagingIo : TRUE ); + + // + // First let's acquire the Fcb shared. Shared is enough if we + // are not writing beyond EOF. + // + + if ( PagingIo ) { + + (VOID)ExAcquireResourceSharedLite( FcbOrDcb->Header.PagingIoResource, TRUE ); + PagingIoResourceAcquired = TRUE; + + if (!Wait) { + + IrpContext->FatIoContext->Wait.Async.Resource = + FcbOrDcb->Header.PagingIoResource; + } + + // + // Check to see if we colided with a MoveFile call, and if + // so block until it completes. + // + + if (FcbOrDcb->MoveFileEvent) { + + (VOID)KeWaitForSingleObject( FcbOrDcb->MoveFileEvent, + Executive, + KernelMode, + FALSE, + NULL ); + } + + } else { + + // + // We may already have the Fcb due to noncached coherency + // work done just above; however, we may still have to extend + // valid data length. We can't demote this to shared, matching + // what occured before, until we figure that out a bit later. + // + // We kept ahold of it since our lockorder is main->paging, + // and paging must now held across the noncached write from + // the purge on. + // + + // + // If this is async I/O, we will wait if there is an exclusive + // waiter. + // + + if (!Wait && NonCachedIo) { + + if (!FcbOrDcbAcquired && + !FatAcquireSharedFcbWaitForEx( IrpContext, FcbOrDcb )) { + + DebugTrace( 0, Dbg, "Cannot acquire FcbOrDcb = %p shared without waiting\n", FcbOrDcb ); + try_return( PostIrp = TRUE ); + } + + // + // Note we will have to release this resource elsewhere. If we came + // out of the noncached coherency path, we will also have to drop + // the paging io resource. + // + + IrpContext->FatIoContext->Wait.Async.Resource = FcbOrDcb->Header.Resource; + + if (FcbCanDemoteToShared) { + + IrpContext->FatIoContext->Wait.Async.Resource2 = FcbOrDcb->Header.PagingIoResource; + } + } else { + + if (!FcbOrDcbAcquired && + !FatAcquireSharedFcb( IrpContext, FcbOrDcb )) { + + DebugTrace( 0, Dbg, "Cannot acquire FcbOrDcb = %p shared without waiting\n", FcbOrDcb ); + try_return( PostIrp = TRUE ); + } + } + + FcbOrDcbAcquired = TRUE; + } + + // + // Get a first tentative file size and valid data length. + // We must get ValidDataLength first since it is always + // increased second (in case we are unprotected) and + // we don't want to capture ValidDataLength > FileSize. + // + + ValidDataToDisk = FcbOrDcb->ValidDataToDisk; + ValidDataLength = FcbOrDcb->Header.ValidDataLength.LowPart; + FileSize = FcbOrDcb->Header.FileSize.LowPart; + + NT_ASSERT( ValidDataLength <= FileSize ); + + // + // If are paging io, then we do not want + // to write beyond end of file. If the base is beyond Eof, we will just + // Noop the call. If the transfer starts before Eof, but extends + // beyond, we will truncate the transfer to the last sector + // boundary. + // + + // + // Just in case this is paging io, limit write to file size. + // Otherwise, in case of write through, since Mm rounds up + // to a page, we might try to acquire the resource exclusive + // when our top level guy only acquired it shared. Thus, =><=. + // + + if ( PagingIo ) { + + if (StartingVbo >= FileSize) { + + DebugTrace( 0, Dbg, "PagingIo started beyond EOF.\n", 0 ); + + Irp->IoStatus.Information = 0; + + try_return( Status = STATUS_SUCCESS ); + } + + if (ByteCount > FileSize - StartingVbo) { + + DebugTrace( 0, Dbg, "PagingIo extending beyond EOF.\n", 0 ); + + ByteCount = FileSize - StartingVbo; + } + } + + // + // Determine if we were called by the lazywriter. + // (see resrcsup.c) + // + + if (FcbOrDcb->Specific.Fcb.LazyWriteThread == PsGetCurrentThread()) { + + CalledByLazyWriter = TRUE; + + if (FlagOn( FcbOrDcb->Header.Flags, FSRTL_FLAG_USER_MAPPED_FILE )) { + + // + // Fail if the start of this request is beyond valid data length. + // Don't worry if this is an unsafe test. MM and CC won't + // throw this page away if it is really dirty. + // + + if ((StartingVbo + ByteCount > ValidDataLength) && + (StartingVbo < FileSize)) { + + // + // It's OK if byte range is within the page containing valid data length, + // since we will use ValidDataToDisk as the start point. + // + + if (StartingVbo + ByteCount > ((ValidDataLength + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))) { + + // + // Don't flush this now. + // + + try_return( Status = STATUS_FILE_LOCK_CONFLICT ); + } + } + } + } + + // + // This code detects if we are a recursive synchronous page write + // on a write through file object. + // + + if (FlagOn(Irp->Flags, IRP_SYNCHRONOUS_PAGING_IO) && + FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_RECURSIVE_CALL)) { + + PIRP TopIrp; + + TopIrp = IoGetTopLevelIrp(); + + // + // This clause determines if the top level request was + // in the FastIo path. Gack. Since we don't have a + // real sharing protocol for the top level IRP field ... + // yet ... if someone put things other than a pure IRP in + // there we best be careful. + // + + if ((ULONG_PTR)TopIrp > FSRTL_MAX_TOP_LEVEL_IRP_FLAG && + NodeType(TopIrp) == IO_TYPE_IRP) { + + PIO_STACK_LOCATION IrpStack; + + IrpStack = IoGetCurrentIrpStackLocation(TopIrp); + + // + // Finally this routine detects if the Top irp was a + // cached write to this file and thus we are the writethrough. + // + + if ((IrpStack->MajorFunction == IRP_MJ_WRITE) && + (IrpStack->FileObject->FsContext == FileObject->FsContext) && + !FlagOn(TopIrp->Flags,IRP_NOCACHE)) { + + RecursiveWriteThrough = TRUE; + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WRITE_THROUGH ); + } + } + } + + // + // Here is the deal with ValidDataLength and FileSize: + // + // Rule 1: PagingIo is never allowed to extend file size. + // + // Rule 2: Only the top level requestor may extend Valid + // Data Length. This may be paging IO, as when a + // a user maps a file, but will never be as a result + // of cache lazy writer writes since they are not the + // top level request. + // + // Rule 3: If, using Rules 1 and 2, we decide we must extend + // file size or valid data, we take the Fcb exclusive. + // + + // + // Now see if we are writing beyond valid data length, and thus + // maybe beyond the file size. If so, then we must + // release the Fcb and reacquire it exclusive. Note that it is + // important that when not writing beyond EOF that we check it + // while acquired shared and keep the FCB acquired, in case some + // turkey truncates the file. + // + + // + // Note that the lazy writer must not be allowed to try and + // acquire the resource exclusive. This is not a problem since + // the lazy writer is paging IO and thus not allowed to extend + // file size, and is never the top level guy, thus not able to + // extend valid data length. + // + + if ( !CalledByLazyWriter && + + !RecursiveWriteThrough && + + (WriteToEof || + StartingVbo + ByteCount > ValidDataLength)) { + + // + // If this was an asynchronous write, we are going to make + // the request synchronous at this point, but only kinda. + // At the last moment, before sending the write off to the + // driver, we may shift back to async. + // + // The modified page writer already has the resources + // he requires, so this will complete in small finite + // time. + // + + if (!Wait) { + + Wait = TRUE; + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ); + + if (NonCachedIo) { + + NT_ASSERT( TypeOfOpen == UserFileOpen ); + + SwitchBackToAsync = TRUE; + } + } + + // + // We need Exclusive access to the Fcb/Dcb since we will + // probably have to extend valid data and/or file. + // + + // + // Y'know, the PagingIo case is a mapped page writer, and + // MmFlushSection or the mapped page writer itself already + // snatched up the main exclusive for us via the AcquireForCcFlush + // or AcquireForModWrite logic (the default logic parallels FAT's + // requirements since this order/model came first). Should ASSERT + // this since it'll just go 1->2, and a few more unnecesary DPC + // transitions. + // + // The preacquire is done to avoid inversion over the collided flush + // meta-resource in Mm. The one time this is not true is at final + // system shutdown time, when Mm goes off and flushes all the dirty + // pages. Since the callback is defined as Wait == FALSE he can't + // guarantee acquisition (though with clean process shutdown being + // enforced, it really should be now). Permit this to float. + // + // Note that since we're going to fall back on the acquisition aleady + // done for us, don't confuse things by thinking we did the work + // for it. + // + + if ( PagingIo ) { + + ExReleaseResourceLite( FcbOrDcb->Header.PagingIoResource ); + PagingIoResourceAcquired = FALSE; + + } else { + + // + // The Fcb may already be acquired exclusive due to coherency + // work performed earlier. If so, obviously no work to do. + // + + if (!FcbAcquiredExclusive) { + + FatReleaseFcb( IrpContext, FcbOrDcb ); + FcbOrDcbAcquired = FALSE; + + if (!FatAcquireExclusiveFcb( IrpContext, FcbOrDcb )) { + + DebugTrace( 0, Dbg, "Cannot acquire FcbOrDcb = %p shared without waiting\n", FcbOrDcb ); + + try_return( PostIrp = TRUE ); + } + + FcbOrDcbAcquired = TRUE; + +#pragma prefast( suppress:28931, "convenient for debugging" ) + FcbAcquiredExclusive = TRUE; + } + } + + // + // Now that we have the Fcb exclusive, see if this write + // qualifies for being made async again. The key point + // here is that we are going to update ValidDataLength in + // the Fcb before returning. We must make sure this will + // not cause a problem. One thing we must do is keep out + // the FastIo path. + // + + if (SwitchBackToAsync) { + + if ((FcbOrDcb->NonPaged->SectionObjectPointers.DataSectionObject != NULL) || + (StartingVbo + ByteCount > FcbOrDcb->Header.ValidDataLength.LowPart) || + FatNoAsync) { + + RtlZeroMemory( IrpContext->FatIoContext, sizeof(FAT_IO_CONTEXT) ); + + KeInitializeEvent( &IrpContext->FatIoContext->Wait.SyncEvent, + NotificationEvent, + FALSE ); + + SwitchBackToAsync = FALSE; + + } else { + + if (!FcbOrDcb->NonPaged->OutstandingAsyncEvent) { + + FcbOrDcb->NonPaged->OutstandingAsyncEvent = + FsRtlAllocatePoolWithTag( NonPagedPoolNx, + sizeof(KEVENT), + TAG_EVENT ); + + KeInitializeEvent( FcbOrDcb->NonPaged->OutstandingAsyncEvent, + NotificationEvent, + FALSE ); + } + + // + // If we are transitioning from 0 to 1, reset the event. + // + + if (ExInterlockedAddUlong( &FcbOrDcb->NonPaged->OutstandingAsyncWrites, + 1, + &FatData.GeneralSpinLock ) == 0) { + + KeClearEvent( FcbOrDcb->NonPaged->OutstandingAsyncEvent ); + } + + UnwindOutstandingAsync = TRUE; + + IrpContext->FatIoContext->Wait.Async.NonPagedFcb = FcbOrDcb->NonPaged; + } + } + + // + // Now that we have the Fcb exclusive, get a new batch of + // filesize and ValidDataLength. + // + + ValidDataToDisk = FcbOrDcb->ValidDataToDisk; + ValidDataLength = FcbOrDcb->Header.ValidDataLength.LowPart; + FileSize = FcbOrDcb->Header.FileSize.LowPart; + + // + // If this is PagingIo check again if any pruning is + // required. It is important to start from basic + // princples in case the file was *grown* ... + // + + if ( PagingIo ) { + + if (StartingVbo >= FileSize) { + Irp->IoStatus.Information = 0; + try_return( Status = STATUS_SUCCESS ); + } + + ByteCount = IrpSp->Parameters.Write.Length; + + if (ByteCount > FileSize - StartingVbo) { + ByteCount = FileSize - StartingVbo; + } + } + } + + // + // Remember the final requested byte count + // + + if (NonCachedIo && !Wait) { + + IrpContext->FatIoContext->Wait.Async.RequestedByteCount = + ByteCount; + } + + // + // Remember the initial file size and valid data length, + // just in case ..... + // + + InitialFileSize = FileSize; + + InitialValidDataLength = ValidDataLength; + + // + // Make sure the FcbOrDcb is still good + // + + FatVerifyFcb( IrpContext, FcbOrDcb ); + + // + // Check for writing to end of File. If we are, then we have to + // recalculate a number of fields. + // + + if ( WriteToEof ) { + + StartingVbo = FileSize; + StartingByte = FcbOrDcb->Header.FileSize; + + // + // Since we couldn't know this information until now, perform the + // necessary bounds checking that we ommited at the top because + // this is a WriteToEof operation. + // + + + if (!FatIsIoRangeValid( Vcb, StartingByte, ByteCount)) { + + Irp->IoStatus.Information = 0; + try_return( Status = STATUS_DISK_FULL ); + } + + + } + + // + // If this is a non paging write to a data stream object we have to + // check for access according to the current state op/filelocks. + // + // Note that after this point, operations will be performed on the file. + // No modifying activity can occur prior to this point in the write + // path. + // + + if (!PagingIo && TypeOfOpen == UserFileOpen) { + + Status = FsRtlCheckOplock( FatGetFcbOplock(FcbOrDcb), + Irp, + IrpContext, + FatOplockComplete, + FatPrePostIrp ); + + if (Status != STATUS_SUCCESS) { + + OplockPostIrp = TRUE; + PostIrp = TRUE; + try_return( NOTHING ); + } + + // + // This oplock call can affect whether fast IO is possible. + // We may have broken an oplock to no oplock held. If the + // current state of the file is FastIoIsNotPossible then + // recheck the fast IO state. + // + + if (FcbOrDcb->Header.IsFastIoPossible == FastIoIsNotPossible) { + + FcbOrDcb->Header.IsFastIoPossible = FatIsFastIoPossible( FcbOrDcb ); + } + + // + // And finally check the regular file locks. + // + + if (!FsRtlCheckLockForWriteAccess( &FcbOrDcb->Specific.Fcb.FileLock, Irp )) { + + try_return( Status = STATUS_FILE_LOCK_CONFLICT ); + } + } + + // + // Determine if we will deal with extending the file. Note that + // this implies extending valid data, and so we already have all + // of the required synchronization done. + // + + if (!PagingIo && (StartingVbo + ByteCount > FileSize)) { + + ExtendingFile = TRUE; + } + + if ( ExtendingFile ) { + + + // + // EXTENDING THE FILE + // + + // + // For an extending write on hotplug media, we are going to defer the metadata + // updates via Cc's lazy writer. They will also be flushed when the handle is closed. + // + + if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_DEFERRED_FLUSH)) { + + SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_WRITE_THROUGH); + } + + // + // Update our local copy of FileSize + // + + FileSize = StartingVbo + ByteCount; + + + if (FcbOrDcb->Header.AllocationSize.QuadPart == FCB_LOOKUP_ALLOCATIONSIZE_HINT) { + + FatLookupFileAllocationSize( IrpContext, FcbOrDcb ); + } + + // + // If the write goes beyond the allocation size, add some + // file allocation. + // + + + if ( (FileSize) > FcbOrDcb->Header.AllocationSize.LowPart ) { + + + BOOLEAN AllocateMinimumSize = TRUE; + + // + // Only do allocation chuncking on writes if this is + // not the first allocation added to the file. + // + + if (FcbOrDcb->Header.AllocationSize.LowPart != 0 ) { + + ULONGLONG ApproximateClusterCount; + ULONGLONG TargetAllocation; + ULONGLONG AddedAllocation; + ULONGLONG Multiplier; + ULONG BytesPerCluster; + ULONG ClusterAlignedFileSize; + + // + // We are going to try and allocate a bigger chunk than + // we actually need in order to maximize FastIo usage. + // + // The multiplier is computed as follows: + // + // + // (FreeDiskSpace ) + // Mult = ( (-------------------------) / 32 ) + 1 + // (FileSize - AllocationSize) + // + // and max out at 32. + // + // With this formula we start winding down chunking + // as we get near the disk space wall. + // + // For instance on an empty 1 MEG floppy doing an 8K + // write, the multiplier is 6, or 48K to allocate. + // When this disk is half full, the multipler is 3, + // and when it is 3/4 full, the mupltiplier is only 1. + // + // On a larger disk, the multiplier for a 8K read will + // reach its maximum of 32 when there is at least ~8 Megs + // available. + // + + // + // Small write performance note, use cluster aligned + // file size in above equation. + // + + // + // We need to carefully consider what happens when we approach + // a 2^32 byte filesize. Overflows will cause problems. + // + + BytesPerCluster = 1 << Vcb->AllocationSupport.LogOfBytesPerCluster; + + // + // This can overflow if the target filesize is in the last cluster. + // In this case, we can obviously skip over all of this fancy + // logic and just max out the file right now. + // + + + ClusterAlignedFileSize = ((FileSize) + (BytesPerCluster - 1)) & + ~(BytesPerCluster - 1); + + + if (ClusterAlignedFileSize != 0) { + + // + // This actually has a chance but the possibility of overflowing + // the numerator is pretty unlikely, made more unlikely by moving + // the divide by 32 up to scale the BytesPerCluster. However, even if it does the + // effect is completely benign. + // + // FAT32 with a 64k cluster and over 2^21 clusters would do it (and + // so forth - 2^(16 - 5 + 21) == 2^32). Since this implies a partition + // of 32gb and a number of clusters (and cluster size) we plan to + // disallow in format for FAT32, the odds of this happening are pretty + // low anyway. + Multiplier = ((Vcb->AllocationSupport.NumberOfFreeClusters * + (BytesPerCluster >> 5)) / + (ClusterAlignedFileSize - + FcbOrDcb->Header.AllocationSize.LowPart)) + 1; + + if (Multiplier > 32) { Multiplier = 32; } + + // These computations will never overflow a ULONGLONG because a file is capped at 4GB, and + // a single write can be a max of 4GB. + AddedAllocation = Multiplier * (ClusterAlignedFileSize - FcbOrDcb->Header.AllocationSize.LowPart); + + TargetAllocation = FcbOrDcb->Header.AllocationSize.LowPart + AddedAllocation; + + // + // We know that TargetAllocation is in whole clusters. Now + // we check if it exceeded the maximum valid FAT file size. + // If it did, we fall back to allocating up to the maximum legal size. + // + + if (TargetAllocation > ~BytesPerCluster + 1) { + + TargetAllocation = ~BytesPerCluster + 1; + AddedAllocation = TargetAllocation - FcbOrDcb->Header.AllocationSize.LowPart; + } + + // + // Now do an unsafe check here to see if we should even + // try to allocate this much. If not, just allocate + // the minimum size we need, if so so try it, but if it + // fails, just allocate the minimum size we need. + // + + ApproximateClusterCount = (AddedAllocation / BytesPerCluster); + + if (ApproximateClusterCount <= Vcb->AllocationSupport.NumberOfFreeClusters) { + + try { + + FatAddFileAllocation( IrpContext, + FcbOrDcb, + FileObject, + (ULONG)TargetAllocation ); + + AllocateMinimumSize = FALSE; + SetFlag( FcbOrDcb->FcbState, FCB_STATE_TRUNCATE_ON_CLOSE ); + + } except( GetExceptionCode() == STATUS_DISK_FULL ? + EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) { + + FatResetExceptionState( IrpContext ); + } + } + } + } + + if ( AllocateMinimumSize ) { + + + FatAddFileAllocation( IrpContext, + FcbOrDcb, + FileObject, + FileSize ); + + + } + + // + // Assert that the allocation worked + // + + + NT_ASSERT( FcbOrDcb->Header.AllocationSize.LowPart >= FileSize ); + + + } + + // + // Set the new file size in the Fcb + // + + + NT_ASSERT( FileSize <= FcbOrDcb->Header.AllocationSize.LowPart ); + + + FcbOrDcb->Header.FileSize.LowPart = FileSize; + + // + // Extend the cache map, letting mm knows the new file size. + // We only have to do this if the file is cached. + // + + if (CcIsFileCached(FileObject)) { + CcSetFileSizes( FileObject, (PCC_FILE_SIZES)&FcbOrDcb->Header.AllocationSize ); + } + } + + // + // Determine if we will deal with extending valid data. + // + + if ( !CalledByLazyWriter && + !RecursiveWriteThrough && + (StartingVbo + ByteCount > ValidDataLength) ) { + + ExtendingValidData = TRUE; + + } else { + + // + // If not extending valid data, and we otherwise believe we + // could demote from exclusive to shared, do so. This will + // occur when we synchronize tight for noncached coherency + // but must defer the demotion until after we decide about + // valid data length, which requires it exclusive. Since we + // can't drop/re-pick the resources without letting a pagefault + // squirt through, the resource decision was kept up in the air + // until now. + // + // Note that we've still got PagingIo exclusive in these cases. + // + + if (FcbCanDemoteToShared) { + + NT_ASSERT( FcbAcquiredExclusive && ExIsResourceAcquiredExclusiveLite( FcbOrDcb->Header.Resource )); + ExConvertExclusiveToSharedLite( FcbOrDcb->Header.Resource ); + FcbAcquiredExclusive = FALSE; + } + } + + if (ValidDataToDisk > ValidDataLength) { + + ValidDataToCheck = ValidDataToDisk; + + } else { + + ValidDataToCheck = ValidDataLength; + } + + // + // HANDLE THE NON-CACHED CASE + // + + if ( NonCachedIo ) { + + // + // Declare some local variables for enumeration through the + // runs of the file, and an array to store parameters for + // parallel I/Os + // + + ULONG SectorSize; + + ULONG BytesToWrite; + + DebugTrace(0, Dbg, "Non cached write.\n", 0); + + // + // Round up to sector boundry. The end of the write interval + // must, however, be beyond EOF. + // + + SectorSize = (ULONG)Vcb->Bpb.BytesPerSector; + + BytesToWrite = (ByteCount + (SectorSize - 1)) + & ~(SectorSize - 1); + + // + // All requests should be well formed and + // make sure we don't wipe out any data + // + + if (((StartingVbo & (SectorSize - 1)) != 0) || + + ((BytesToWrite != ByteCount) && + (StartingVbo + ByteCount < ValidDataLength))) { + + NT_ASSERT( FALSE ); + + DebugTrace( 0, Dbg, "FatCommonWrite -> STATUS_NOT_IMPLEMENTED\n", 0); + try_return( Status = STATUS_NOT_IMPLEMENTED ); + } + + // + // If this noncached transfer is at least one sector beyond + // the current ValidDataLength in the Fcb, then we have to + // zero the sectors in between. This can happen if the user + // has opened the file noncached, or if the user has mapped + // the file and modified a page beyond ValidDataLength. It + // *cannot* happen if the user opened the file cached, because + // ValidDataLength in the Fcb is updated when he does the cached + // write (we also zero data in the cache at that time), and + // therefore, we will bypass this test when the data + // is ultimately written through (by the Lazy Writer). + // + // For the paging file we don't care about security (ie. + // stale data), do don't bother zeroing. + // + // We can actually get writes wholly beyond valid data length + // from the LazyWriter because of paging Io decoupling. + // + + if (!CalledByLazyWriter && + !RecursiveWriteThrough && + (StartingVbo > ValidDataToCheck)) { + + FatZeroData( IrpContext, + Vcb, + FileObject, + ValidDataToCheck, + StartingVbo - ValidDataToCheck ); + } + + // + // Make sure we write FileSize to the dirent if we + // are extending it and we are successful. (This may or + // may not occur Write Through, but that is fine.) + // + + WriteFileSizeToDirent = TRUE; + + // + // Perform the actual IO + // + + if (SwitchBackToAsync) { + + Wait = FALSE; + ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ); + } + +#ifdef SYSCACHE_COMPILE + +#define MY_SIZE 0x1000000 +#define LONGMAP_COUNTER + +#ifdef BITMAP + // + // Maintain a bitmap of IO started on this file. + // + + { + PULONG WriteMask = FcbOrDcb->WriteMask; + + if (NULL == WriteMask) { + + WriteMask = FsRtlAllocatePoolWithTag( NonPagedPoolNx, + (MY_SIZE/PAGE_SIZE) / 8, + 'wtaF' ); + + FcbOrDcb->WriteMask = WriteMask; + RtlZeroMemory(WriteMask, (MY_SIZE/PAGE_SIZE) / 8); + } + + if (StartingVbo < MY_SIZE) { + + ULONG Off = StartingVbo; + ULONG Len = BytesToWrite; + + if (Off + Len > MY_SIZE) { + Len = MY_SIZE - Off; + } + + while (Len != 0) { + WriteMask[(Off/PAGE_SIZE) / 32] |= + 1 << (Off/PAGE_SIZE) % 32; + + Off += PAGE_SIZE; + if (Len <= PAGE_SIZE) { + break; + } + Len -= PAGE_SIZE; + } + } + } +#endif + +#ifdef LONGMAP_COUNTER + // + // Maintain a longmap of IO started on this file, each ulong containing + // the value of an ascending counter per write (gives us order information). + // + // Unlike the old bitmask stuff, this is mostly well synchronized. + // + + { + PULONG WriteMask = (PULONG)FcbOrDcb->WriteMask; + + if (NULL == WriteMask) { + + WriteMask = FsRtlAllocatePoolWithTag( NonPagedPoolNx, + (MY_SIZE/PAGE_SIZE) * sizeof(ULONG), + 'wtaF' ); + + FcbOrDcb->WriteMask = WriteMask; + RtlZeroMemory(WriteMask, (MY_SIZE/PAGE_SIZE) * sizeof(ULONG)); + } + + if (StartingVbo < MY_SIZE) { + + ULONG Off = StartingVbo; + ULONG Len = BytesToWrite; + ULONG Tick = InterlockedIncrement( &FcbOrDcb->WriteMaskData ); + + if (Off + Len > MY_SIZE) { + Len = MY_SIZE - Off; + } + + while (Len != 0) { + InterlockedExchange( WriteMask + Off/PAGE_SIZE, Tick ); + + Off += PAGE_SIZE; + if (Len <= PAGE_SIZE) { + break; + } + Len -= PAGE_SIZE; + } + } + } +#endif + +#endif + + + if (FatNonCachedIo( IrpContext, + Irp, + FcbOrDcb, + StartingVbo, + BytesToWrite, + BytesToWrite, + 0) == STATUS_PENDING) { + + + UnwindOutstandingAsync = FALSE; + +#pragma prefast( suppress:28931, "convenient for debugging" ) + Wait = TRUE; + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ); + + IrpContext->FatIoContext = NULL; + Irp = NULL; + + // + // As a matter of fact, if we hit this we are in deep trouble + // if VDL is being extended. We are no longer attached to the + // IRP, and have thus lost synchronization. Note that we should + // not hit this case anymore since we will not re-async vdl extension. + // + + NT_ASSERT( !ExtendingValidData ); + + try_return( Status = STATUS_PENDING ); + } + + // + // If the call didn't succeed, raise the error status + // + + if (!NT_SUCCESS( Status = Irp->IoStatus.Status )) { + + FatNormalizeAndRaiseStatus( IrpContext, Status ); + + } else { + + ULONG NewValidDataToDisk; + + // + // Else set the context block to reflect the entire write + // Also assert we got how many bytes we asked for. + // + + NT_ASSERT( Irp->IoStatus.Information == BytesToWrite ); + + Irp->IoStatus.Information = ByteCount; + + // + // Take this opportunity to update ValidDataToDisk. + // + + NewValidDataToDisk = StartingVbo + ByteCount; + + if (NewValidDataToDisk > FileSize) { + NewValidDataToDisk = FileSize; + } + + if (FcbOrDcb->ValidDataToDisk < NewValidDataToDisk) { + FcbOrDcb->ValidDataToDisk = NewValidDataToDisk; + } + } + + // + // The transfer is either complete, or the Iosb contains the + // appropriate status. + // + + try_return( Status ); + + } // if No Intermediate Buffering + + + // + // HANDLE CACHED CASE + // + + else { + + NT_ASSERT( !PagingIo ); + + // + // We delay setting up the file cache until now, in case the + // caller never does any I/O to the file, and thus + // FileObject->PrivateCacheMap == NULL. + // + + if ( FileObject->PrivateCacheMap == NULL ) { + + DebugTrace(0, Dbg, "Initialize cache mapping.\n", 0); + + // + // Get the file allocation size, and if it is less than + // the file size, raise file corrupt error. + // + + if (FcbOrDcb->Header.AllocationSize.QuadPart == FCB_LOOKUP_ALLOCATIONSIZE_HINT) { + + FatLookupFileAllocationSize( IrpContext, FcbOrDcb ); + } + + if ( FileSize > FcbOrDcb->Header.AllocationSize.LowPart ) { + + FatPopUpFileCorrupt( IrpContext, FcbOrDcb ); + + FatRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); + } + + // + // Now initialize the cache map. + // + + FatInitializeCacheMap( FileObject, + (PCC_FILE_SIZES)&FcbOrDcb->Header.AllocationSize, + FALSE, + &FatData.CacheManagerCallbacks, + FcbOrDcb ); + + CcSetReadAheadGranularity( FileObject, READ_AHEAD_GRANULARITY ); + + // + // Special case large floppy tranfers, and make the file + // object write through. For small floppy transfers, + // set a timer to go off in a second and flush the file. + // + // + + if (!FlagOn( FileObject->Flags, FO_WRITE_THROUGH ) && + FlagOn(Vcb->VcbState, VCB_STATE_FLAG_DEFERRED_FLUSH)) { + + if (((StartingByte.LowPart & (PAGE_SIZE-1)) == 0) && + (ByteCount >= PAGE_SIZE)) { + + SetFlag( FileObject->Flags, FO_WRITE_THROUGH ); + + } else { + + LARGE_INTEGER OneSecondFromNow; + PDEFERRED_FLUSH_CONTEXT FlushContext; + + // + // Get pool and initialize the timer and DPC + // + + FlushContext = FsRtlAllocatePoolWithTag( NonPagedPoolNx, + sizeof(DEFERRED_FLUSH_CONTEXT), + TAG_DEFERRED_FLUSH_CONTEXT ); + + KeInitializeTimer( &FlushContext->Timer ); + + KeInitializeDpc( &FlushContext->Dpc, + FatDeferredFlushDpc, + FlushContext ); + + + // + // We have to reference the file object here. + // + + ObReferenceObject( FileObject ); + + FlushContext->File = FileObject; + + // + // Let'er rip! + // + + OneSecondFromNow.QuadPart = (LONG)-1*1000*1000*10; + + KeSetTimer( &FlushContext->Timer, + OneSecondFromNow, + &FlushContext->Dpc ); + } + } + } + + // + // If this write is beyond valid data length, then we + // must zero the data in between. + // + + if ( StartingVbo > ValidDataToCheck ) { + + // + // Call the Cache Manager to zero the data. + // + + if (!FatZeroData( IrpContext, + Vcb, + FileObject, + ValidDataToCheck, + StartingVbo - ValidDataToCheck )) { + + DebugTrace( 0, Dbg, "Cached Write could not wait to zero\n", 0 ); + + try_return( PostIrp = TRUE ); + } + } + + WriteFileSizeToDirent = BooleanFlagOn(IrpContext->Flags, + IRP_CONTEXT_FLAG_WRITE_THROUGH); + + + // + // DO A NORMAL CACHED WRITE, if the MDL bit is not set, + // + + if (!FlagOn(IrpContext->MinorFunction, IRP_MN_MDL)) { + + DebugTrace(0, Dbg, "Cached write.\n", 0); + + // + // Get hold of the user's buffer. + // + + SystemBuffer = FatMapUserBuffer( IrpContext, Irp ); + + // + // Do the write, possibly writing through + // + +#if (NTDDI_VERSION >= NTDDI_WIN8) + if (!CcCopyWriteEx( FileObject, + &StartingByte, + ByteCount, + Wait, + SystemBuffer, + Irp->Tail.Overlay.Thread )) { +#else + if (!CcCopyWrite( FileObject, + &StartingByte, + ByteCount, + Wait, + SystemBuffer )) { +#endif + + DebugTrace( 0, Dbg, "Cached Write could not wait\n", 0 ); + + try_return( PostIrp = TRUE ); + } + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = ByteCount; + + try_return( Status = STATUS_SUCCESS ); + + } else { + + // + // DO AN MDL WRITE + // + + DebugTrace(0, Dbg, "MDL write.\n", 0); + + NT_ASSERT( Wait ); + + CcPrepareMdlWrite( FileObject, + &StartingByte, + ByteCount, + &Irp->MdlAddress, + &Irp->IoStatus ); + + Status = Irp->IoStatus.Status; + + try_return( Status ); + } + } + } + + // + // These two cases correspond to a system write directory file and + // ea file. + // + + if (( TypeOfOpen == DirectoryFile ) || ( TypeOfOpen == EaFile) + ) { + + ULONG SectorSize; + +#if FASTFATDBG + if ( TypeOfOpen == DirectoryFile ) { + DebugTrace(0, Dbg, "Type of write is directoryfile\n", 0); + } else if ( TypeOfOpen == EaFile) { + DebugTrace(0, Dbg, "Type of write is eafile\n", 0); + } +#endif + + // + // Make sure the FcbOrDcb is still good + // + + FatVerifyFcb( IrpContext, FcbOrDcb ); + + // + // Synchronize here with people deleting directories and + // mucking with the internals of the EA file. + // + + if (!ExAcquireSharedStarveExclusive( FcbOrDcb->Header.PagingIoResource, + Wait )) { + + DebugTrace( 0, Dbg, "Cannot acquire FcbOrDcb = %p shared without waiting\n", FcbOrDcb ); + + try_return( PostIrp = TRUE ); + } + + PagingIoResourceAcquired = TRUE; + + if (!Wait) { + + IrpContext->FatIoContext->Wait.Async.Resource = + FcbOrDcb->Header.PagingIoResource; + } + + // + // Check to see if we colided with a MoveFile call, and if + // so block until it completes. + // + + if (FcbOrDcb->MoveFileEvent) { + + (VOID)KeWaitForSingleObject( FcbOrDcb->MoveFileEvent, + Executive, + KernelMode, + FALSE, + NULL ); + } + + // + // If we weren't called by the Lazy Writer, then this write + // must be the result of a write-through or flush operation. + // Setting the IrpContext flag, will cause DevIoSup.c to + // write-through the data to the disk. + // + + if (!FlagOn((ULONG_PTR)IoGetTopLevelIrp(), FSRTL_CACHE_TOP_LEVEL_IRP)) { + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WRITE_THROUGH ); + } + + // + // For the noncached case, assert that everything is sector + // alligned. + // + +#pragma prefast( suppress:28931, "needed for debug build" ) + SectorSize = (ULONG)Vcb->Bpb.BytesPerSector; + + // + // We make several assumptions about these two types of files. + // Make sure all of them are true. + // + + NT_ASSERT( NonCachedIo && PagingIo ); + NT_ASSERT( ((StartingVbo | ByteCount) & (SectorSize - 1)) == 0 ); + + + // + // These calls must always be within the allocation size, which is + // convienently the same as filesize, which conveniently doesn't + // get reset to a hint value when we verify the volume. + // + + if (StartingVbo >= FcbOrDcb->Header.FileSize.LowPart) { + + DebugTrace( 0, Dbg, "PagingIo dirent started beyond EOF.\n", 0 ); + + Irp->IoStatus.Information = 0; + + try_return( Status = STATUS_SUCCESS ); + } + + if ( StartingVbo + ByteCount > FcbOrDcb->Header.FileSize.LowPart ) { + + DebugTrace( 0, Dbg, "PagingIo dirent extending beyond EOF.\n", 0 ); + ByteCount = FcbOrDcb->Header.FileSize.LowPart - StartingVbo; + } + + + // + // Perform the actual IO + // + + if (FatNonCachedIo( IrpContext, + Irp, + FcbOrDcb, + StartingVbo, + ByteCount, + ByteCount, + 0 ) == STATUS_PENDING) { + + IrpContext->FatIoContext = NULL; + + Irp = NULL; + + try_return( Status = STATUS_PENDING ); + } + + // + // The transfer is either complete, or the Iosb contains the + // appropriate status. + // + // Also, mark the volume as needing verification to automatically + // clean up stuff. + // + + if (!NT_SUCCESS( Status = Irp->IoStatus.Status )) { + + FatNormalizeAndRaiseStatus( IrpContext, Status ); + } + + try_return( Status ); + } + + // + // This is the case of a user who openned a directory. No writing is + // allowed. + // + + if ( TypeOfOpen == UserDirectoryOpen ) { + + DebugTrace( 0, Dbg, "FatCommonWrite -> STATUS_INVALID_PARAMETER\n", 0); + + try_return( Status = STATUS_INVALID_PARAMETER ); + } + + // + // If we get this far, something really serious is wrong. + // + + DebugDump("Illegal TypeOfOpen\n", 0, FcbOrDcb ); + +#pragma prefast( suppress:28159, "things are seriously wrong if we get here" ) + FatBugCheck( TypeOfOpen, (ULONG_PTR) FcbOrDcb, 0 ); + + try_exit: NOTHING; + + + // + // If the request was not posted and there is still an Irp, + // deal with it. + // + + if (Irp) { + + if ( !PostIrp ) { + + ULONG ActualBytesWrote; + + DebugTrace( 0, Dbg, "Completing request with status = %08lx\n", + Status); + + DebugTrace( 0, Dbg, " Information = %08lx\n", + Irp->IoStatus.Information); + + // + // Record the total number of bytes actually written + // + + ActualBytesWrote = (ULONG)Irp->IoStatus.Information; + + // + // If the file was opened for Synchronous IO, update the current + // file position. + // + + if (SynchronousIo && !PagingIo) { + + FileObject->CurrentByteOffset.LowPart = + StartingVbo + (NT_ERROR( Status ) ? 0 : ActualBytesWrote); + } + + // + // The following are things we only do if we were successful + // + + if ( NT_SUCCESS( Status ) ) { + + // + // If this was not PagingIo, mark that the modify + // time on the dirent needs to be updated on close. + // + + if ( !PagingIo ) { + + SetFlag( FileObject->Flags, FO_FILE_MODIFIED ); + } + + // + // If we extended the file size and we are meant to + // immediately update the dirent, do so. (This flag is + // set for either Write Through or noncached, because + // in either case the data and any necessary zeros are + // actually written to the file.) + // + + if ( ExtendingFile && WriteFileSizeToDirent ) { + + NT_ASSERT( FileObject->DeleteAccess || FileObject->WriteAccess ); + + FatSetFileSizeInDirent( IrpContext, FcbOrDcb, NULL ); + + // + // Report that a file size has changed. + // + + FatNotifyReportChange( IrpContext, + Vcb, + FcbOrDcb, + FILE_NOTIFY_CHANGE_SIZE, + FILE_ACTION_MODIFIED ); + } + + if ( ExtendingFile && !WriteFileSizeToDirent ) { + + SetFlag( FileObject->Flags, FO_FILE_SIZE_CHANGED ); + } + + if ( ExtendingValidData ) { + + ULONG EndingVboWritten = StartingVbo + ActualBytesWrote; + + // + // Never set a ValidDataLength greater than FileSize. + // + + if ( FileSize < EndingVboWritten ) { + + FcbOrDcb->Header.ValidDataLength.LowPart = FileSize; + + } else { + + FcbOrDcb->Header.ValidDataLength.LowPart = EndingVboWritten; + } + + // + // Now, if we are noncached and the file is cached, we must + // tell the cache manager about the VDL extension so that + // async cached IO will not be optimized into zero-page faults + // beyond where it believes VDL is. + // + // In the cached case, since Cc did the work, it has updated + // itself already. + // + + if (NonCachedIo && CcIsFileCached(FileObject)) { + CcSetFileSizes( FileObject, (PCC_FILE_SIZES)&FcbOrDcb->Header.AllocationSize ); + } + } + } + + // + // Note that we have to unpin repinned Bcbs here after the above + // work, but if we are going to post the request, we must do this + // before the post (below). + // + + FatUnpinRepinnedBcbs( IrpContext ); + + } else { + + // + // Take action if the Oplock package is not going to post the Irp. + // + + if (!OplockPostIrp) { + + FatUnpinRepinnedBcbs( IrpContext ); + + if ( ExtendingFile ) { + + // + // We need the PagingIo resource exclusive whenever we + // pull back either file size or valid data length. + // + + NT_ASSERT( FcbOrDcb->Header.PagingIoResource != NULL ); + + (VOID)ExAcquireResourceExclusiveLite(FcbOrDcb->Header.PagingIoResource, TRUE); + + FcbOrDcb->Header.FileSize.LowPart = InitialFileSize; + + NT_ASSERT( FcbOrDcb->Header.FileSize.LowPart <= FcbOrDcb->Header.AllocationSize.LowPart ); + + // + // Pull back the cache map as well + // + + if (FileObject->SectionObjectPointer->SharedCacheMap != NULL) { + + *CcGetFileSizePointer(FileObject) = FcbOrDcb->Header.FileSize; + } + + ExReleaseResourceLite( FcbOrDcb->Header.PagingIoResource ); + } + + DebugTrace( 0, Dbg, "Passing request to Fsp\n", 0 ); + + Status = FatFsdPostRequest(IrpContext, Irp); + } + } + } + + } finally { + + DebugUnwind( FatCommonWrite ); + + if (AbnormalTermination()) { + + // + // Restore initial file size and valid data length + // + + if (ExtendingFile || ExtendingValidData) { + + // + // We got an error, pull back the file size if we extended it. + // + + FcbOrDcb->Header.FileSize.LowPart = InitialFileSize; + FcbOrDcb->Header.ValidDataLength.LowPart = InitialValidDataLength; + + NT_ASSERT( FcbOrDcb->Header.FileSize.LowPart <= FcbOrDcb->Header.AllocationSize.LowPart ); + + // + // Pull back the cache map as well + // + + if (FileObject->SectionObjectPointer->SharedCacheMap != NULL) { + + *CcGetFileSizePointer(FileObject) = FcbOrDcb->Header.FileSize; + } + } + } + + // + // Check if this needs to be backed out. + // + + if (UnwindOutstandingAsync) { + + ExInterlockedAddUlong( &FcbOrDcb->NonPaged->OutstandingAsyncWrites, + 0xffffffff, + &FatData.GeneralSpinLock ); + } + + // + // If the FcbOrDcb has been acquired, release it. + // + + if (FcbOrDcbAcquired && Irp) { + + FatReleaseFcb( NULL, FcbOrDcb ); + } + + if (PagingIoResourceAcquired && Irp) { + + ExReleaseResourceLite( FcbOrDcb->Header.PagingIoResource ); + } + + // + // Complete the request if we didn't post it and no exception + // + // Note that FatCompleteRequest does the right thing if either + // IrpContext or Irp are NULL + // + + if ( !PostIrp && !AbnormalTermination() ) { + + FatCompleteRequest( IrpContext, Irp, Status ); + } + + DebugTrace(-1, Dbg, "FatCommonWrite -> %08lx\n", Status ); + } + + return Status; +} + + +// +// Local support routine +// + +VOID +FatDeferredFlushDpc ( + _In_ PKDPC Dpc, + _In_opt_ PVOID DeferredContext, + _In_opt_ PVOID SystemArgument1, + _In_opt_ PVOID SystemArgument2 + ) + +/*++ + +Routine Description: + + This routine is dispatched 1 second after a small write to a deferred + write device that initialized the cache map. It exqueues an executive + worker thread to perform the actual task of flushing the file. + +Arguments: + + DeferredContext - Contains the deferred flush context. + +Return Value: + + None. + +--*/ + +{ + PDEFERRED_FLUSH_CONTEXT FlushContext; + + UNREFERENCED_PARAMETER( SystemArgument1 ); + UNREFERENCED_PARAMETER( SystemArgument2 ); + UNREFERENCED_PARAMETER( Dpc ); + + FlushContext = (PDEFERRED_FLUSH_CONTEXT)DeferredContext; + + // + // Send it off + // + + ExInitializeWorkItem( &FlushContext->Item, + FatDeferredFlush, + FlushContext ); + +#pragma prefast( suppress:28159, "prefast indicates this API is obsolete, but it's ok for fastfat to keep using it" ) + ExQueueWorkItem( &FlushContext->Item, CriticalWorkQueue ); +} + + +// +// Local support routine +// + +VOID +FatDeferredFlush ( + _In_ PVOID Parameter + ) + +/*++ + +Routine Description: + + This routine performs the actual task of flushing the file. + +Arguments: + + DeferredContext - Contains the deferred flush context. + +Return Value: + + None. + +--*/ + +{ + + PFILE_OBJECT File; + PVCB Vcb; + PFCB FcbOrDcb; + PCCB Ccb; + + PAGED_CODE(); + + File = ((PDEFERRED_FLUSH_CONTEXT)Parameter)->File; + + FatDecodeFileObject(File, &Vcb, &FcbOrDcb, &Ccb); + NT_ASSERT( FcbOrDcb != NULL ); + + // + // Make us appear as a top level FSP request so that we will + // receive any errors from the flush. + // + + IoSetTopLevelIrp( (PIRP)FSRTL_FSP_TOP_LEVEL_IRP ); + + ExAcquireResourceExclusiveLite( FcbOrDcb->Header.Resource, TRUE ); + ExAcquireResourceSharedLite( FcbOrDcb->Header.PagingIoResource, TRUE ); + + CcFlushCache( File->SectionObjectPointer, NULL, 0, NULL ); + + ExReleaseResourceLite( FcbOrDcb->Header.PagingIoResource ); + ExReleaseResourceLite( FcbOrDcb->Header.Resource ); + + IoSetTopLevelIrp( NULL ); + + ObDereferenceObject( File ); + + ExFreePool( Parameter ); + +} + + diff --git a/filesys/miniFilter/MetadataManager/MetadataManager.sln b/filesys/miniFilter/MetadataManager/MetadataManager.sln index 6f4e3cb4b..ff256398e 100644 --- a/filesys/miniFilter/MetadataManager/MetadataManager.sln +++ b/filesys/miniFilter/MetadataManager/MetadataManager.sln @@ -3,7 +3,7 @@ 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", "{D7BF32EC-9C86-43C5-B512-9D40E9617518}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fmm", "fmm.vcxproj", "{D0851590-5695-47F6-A7AA-7AF737527A14}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {D7BF32EC-9C86-43C5-B512-9D40E9617518}.Debug|Win32.ActiveCfg = Debug|Win32 - {D7BF32EC-9C86-43C5-B512-9D40E9617518}.Debug|Win32.Build.0 = Debug|Win32 - {D7BF32EC-9C86-43C5-B512-9D40E9617518}.Release|Win32.ActiveCfg = Release|Win32 - {D7BF32EC-9C86-43C5-B512-9D40E9617518}.Release|Win32.Build.0 = Release|Win32 - {D7BF32EC-9C86-43C5-B512-9D40E9617518}.Debug|x64.ActiveCfg = Debug|x64 - {D7BF32EC-9C86-43C5-B512-9D40E9617518}.Debug|x64.Build.0 = Debug|x64 - {D7BF32EC-9C86-43C5-B512-9D40E9617518}.Release|x64.ActiveCfg = Release|x64 - {D7BF32EC-9C86-43C5-B512-9D40E9617518}.Release|x64.Build.0 = Release|x64 + {D0851590-5695-47F6-A7AA-7AF737527A14}.Debug|Win32.ActiveCfg = Debug|Win32 + {D0851590-5695-47F6-A7AA-7AF737527A14}.Debug|Win32.Build.0 = Debug|Win32 + {D0851590-5695-47F6-A7AA-7AF737527A14}.Release|Win32.ActiveCfg = Release|Win32 + {D0851590-5695-47F6-A7AA-7AF737527A14}.Release|Win32.Build.0 = Release|Win32 + {D0851590-5695-47F6-A7AA-7AF737527A14}.Debug|x64.ActiveCfg = Debug|x64 + {D0851590-5695-47F6-A7AA-7AF737527A14}.Debug|x64.Build.0 = Debug|x64 + {D0851590-5695-47F6-A7AA-7AF737527A14}.Release|x64.ActiveCfg = Release|x64 + {D0851590-5695-47F6-A7AA-7AF737527A14}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/filesys/miniFilter/MetadataManager/fmm.inf b/filesys/miniFilter/MetadataManager/fmm.inf index f859f9b01..5c39ed069 100644 --- a/filesys/miniFilter/MetadataManager/fmm.inf +++ b/filesys/miniFilter/MetadataManager/fmm.inf @@ -9,7 +9,7 @@ Signature = "$Windows NT$" Class = "ActivityMonitor" ;This is determined by the work this filter driver does ClassGuid = {b86dff51-a31e-4bac-b3cf-e8cfe75c9fc2} -Provider = %Msft% +Provider = %ProviderString% DriverVer = 06/16/2007,1.0.0.1 CatalogFile = fmm.cat @@ -83,7 +83,7 @@ fmm.sys = 1,, ;; [Strings] -Msft = "Microsoft Corporation" +ProviderString = "TODO-Set-Provider" ServiceDescription = "Metadata Management File System Filter Driver Sample" ServiceName = "FMM" DriverName = "fmm" diff --git a/filesys/miniFilter/MetadataManager/fmm.vcxproj b/filesys/miniFilter/MetadataManager/fmm.vcxproj index 08369fc56..e685a5ba4 100644 --- a/filesys/miniFilter/MetadataManager/fmm.vcxproj +++ b/filesys/miniFilter/MetadataManager/fmm.vcxproj @@ -19,11 +19,11 @@ - {D7BF32EC-9C86-43C5-B512-9D40E9617518} + {D0851590-5695-47F6-A7AA-7AF737527A14} $(MSBuildProjectName) Debug Win32 - {7C86E1A6-1B03-4023-BC29-22867C198C7F} + {74268674-1799-41EE-9098-83ACA2C799CE} @@ -169,7 +169,6 @@ - diff --git a/filesys/miniFilter/MetadataManager/fmm.vcxproj.Filters b/filesys/miniFilter/MetadataManager/fmm.vcxproj.Filters index 49f80878d..3718963b8 100644 --- a/filesys/miniFilter/MetadataManager/fmm.vcxproj.Filters +++ b/filesys/miniFilter/MetadataManager/fmm.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {50875A86-C80E-4F00-B5E6-F8750B4A63D5} + {187830FB-8908-439C-BF80-5C0780B32314} h;hpp;hxx;hm;inl;inc;xsd - {47046E6F-7445-430F-9F9F-33D5F2C15DDA} + {B09D4328-8735-45A6-906F-EAB82EB9BABF} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {392DBF7C-4449-4E3A-9354-2F7AC7DCDE78} + {66540091-6A2A-4CF6-A515-4EAD98842317} inf;inv;inx;mof;mc; - {E94FBEAE-1CA4-4F77-93A5-934B5212B838} + {EA31B3A1-9791-4836-B438-98109CD714F1} diff --git a/filesys/miniFilter/NameChanger/NameChanger.inf b/filesys/miniFilter/NameChanger/NameChanger.inf index a79016a6d..cd74fb44c 100644 --- a/filesys/miniFilter/NameChanger/NameChanger.inf +++ b/filesys/miniFilter/NameChanger/NameChanger.inf @@ -9,7 +9,7 @@ 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% +Provider = %ProviderString% DriverVer = 06/05/2009,1.0.0.1 CatalogFile = NameChanger.cat @@ -87,7 +87,7 @@ HKR,,"RealMapping",0x00000000,%RealMapping% ;; [Strings] -Msft = "Microsoft Corporation" +ProviderString = "TODO-Set-Provider" ServiceDescription = "NameChanger Mini-Filter Driver Sample" ServiceName = "NameChanger" DriverName = "NameChanger" diff --git a/filesys/miniFilter/NameChanger/NameChanger.sln b/filesys/miniFilter/NameChanger/NameChanger.sln index f4ae94209..01a02361d 100644 --- a/filesys/miniFilter/NameChanger/NameChanger.sln +++ b/filesys/miniFilter/NameChanger/NameChanger.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0 MinimumVisualStudioVersion = 12.0 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NameChanger", "NameChanger.vcxproj", "{5CA32B6F-182F-4028-837A-041E30696D95}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NameChanger", "NameChanger.vcxproj", "{FBF5C5B2-2918-4813-992E-41C3B17FB801}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {5CA32B6F-182F-4028-837A-041E30696D95}.Debug|Win32.ActiveCfg = Debug|Win32 - {5CA32B6F-182F-4028-837A-041E30696D95}.Debug|Win32.Build.0 = Debug|Win32 - {5CA32B6F-182F-4028-837A-041E30696D95}.Release|Win32.ActiveCfg = Release|Win32 - {5CA32B6F-182F-4028-837A-041E30696D95}.Release|Win32.Build.0 = Release|Win32 - {5CA32B6F-182F-4028-837A-041E30696D95}.Debug|x64.ActiveCfg = Debug|x64 - {5CA32B6F-182F-4028-837A-041E30696D95}.Debug|x64.Build.0 = Debug|x64 - {5CA32B6F-182F-4028-837A-041E30696D95}.Release|x64.ActiveCfg = Release|x64 - {5CA32B6F-182F-4028-837A-041E30696D95}.Release|x64.Build.0 = Release|x64 + {FBF5C5B2-2918-4813-992E-41C3B17FB801}.Debug|Win32.ActiveCfg = Debug|Win32 + {FBF5C5B2-2918-4813-992E-41C3B17FB801}.Debug|Win32.Build.0 = Debug|Win32 + {FBF5C5B2-2918-4813-992E-41C3B17FB801}.Release|Win32.ActiveCfg = Release|Win32 + {FBF5C5B2-2918-4813-992E-41C3B17FB801}.Release|Win32.Build.0 = Release|Win32 + {FBF5C5B2-2918-4813-992E-41C3B17FB801}.Debug|x64.ActiveCfg = Debug|x64 + {FBF5C5B2-2918-4813-992E-41C3B17FB801}.Debug|x64.Build.0 = Debug|x64 + {FBF5C5B2-2918-4813-992E-41C3B17FB801}.Release|x64.ActiveCfg = Release|x64 + {FBF5C5B2-2918-4813-992E-41C3B17FB801}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/filesys/miniFilter/NameChanger/NameChanger.vcxproj b/filesys/miniFilter/NameChanger/NameChanger.vcxproj index 5b088fcbe..bd5aadb6b 100644 --- a/filesys/miniFilter/NameChanger/NameChanger.vcxproj +++ b/filesys/miniFilter/NameChanger/NameChanger.vcxproj @@ -19,11 +19,11 @@ - {5CA32B6F-182F-4028-837A-041E30696D95} + {FBF5C5B2-2918-4813-992E-41C3B17FB801} $(MSBuildProjectName) Debug Win32 - {505C6DEB-79DC-43DC-AE00-5A573EF44152} + {F3F4D763-7A72-41B7-AE5A-14B6EB1D1A29} @@ -191,7 +191,6 @@ - diff --git a/filesys/miniFilter/NameChanger/NameChanger.vcxproj.Filters b/filesys/miniFilter/NameChanger/NameChanger.vcxproj.Filters index 6520d3053..5693affdc 100644 --- a/filesys/miniFilter/NameChanger/NameChanger.vcxproj.Filters +++ b/filesys/miniFilter/NameChanger/NameChanger.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {817F7394-22D9-49B6-A7EE-2CC524F1CBF2} + {CAC6310D-A1D1-44F5-813E-D0FF05C58115} h;hpp;hxx;hm;inl;inc;xsd - {DF2CCD0F-541A-4EFC-AA37-579066F38CD0} + {97A530F5-1AB7-4A5D-8906-59E1F3E1AFCF} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {86478AE0-8DEC-4E96-924F-3D6DA78E254F} + {636CD1E3-EA03-4FE7-B9BE-CB9F3684FC25} inf;inv;inx;mof;mc; - {D01D724E-D3A3-464D-8307-CE7E057FA327} + {DA8408AD-EBC7-41EB-8A65-4ED4BB457530} diff --git a/filesys/miniFilter/avscan/avscan.inf b/filesys/miniFilter/avscan/avscan.inf index e536395e9..2162f0b4d 100644 --- a/filesys/miniFilter/avscan/avscan.inf +++ b/filesys/miniFilter/avscan/avscan.inf @@ -9,7 +9,7 @@ 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% +Provider = %ProviderString% DriverVer = 06/16/2011,1.0.0.1 CatalogFile = avscan.cat @@ -90,7 +90,7 @@ avscan.exe = 1,, ;; [Strings] -Msft = "Microsoft Corporation" +ProviderString = "TODO-Set-Provider" ServiceDescription = "Anti-virus Mini-Filter Driver" ServiceName = "avscan" DriverName = "avscan" diff --git a/filesys/miniFilter/avscan/avscan.sln b/filesys/miniFilter/avscan/avscan.sln index d5f163331..ab13b900e 100644 --- a/filesys/miniFilter/avscan/avscan.sln +++ b/filesys/miniFilter/avscan/avscan.sln @@ -3,13 +3,13 @@ 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", "{D78D0CAE-EF4B-44FC-A328-A04817C7A02D}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Filter", "Filter", "{752EF0BD-A05B-4CFE-93E1-1F34EA041029}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "User", "User", "{8B8F8102-C54A-4300-966E-EC924824AB8A}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "User", "User", "{924B6153-4A9C-4B7A-8402-71C7D157FE10}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "avscan", "filter\avscan.vcxproj", "{E4CD5CAB-EC77-4E94-88DE-174D361CD4B3}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "avscan", "filter\avscan.vcxproj", "{AB6754C7-D6E4-492F-84D1-31B3A5AE4C97}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "avscan", "user\avscan.vcxproj", "{71D634DA-3CBE-46FD-B793-50EE8660A13A}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "avscan", "user\avscan.vcxproj", "{41E2DC8A-7DCA-4590-B9B9-466AC5EA85F3}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -19,28 +19,28 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {E4CD5CAB-EC77-4E94-88DE-174D361CD4B3}.Debug|Win32.ActiveCfg = Debug|Win32 - {E4CD5CAB-EC77-4E94-88DE-174D361CD4B3}.Debug|Win32.Build.0 = Debug|Win32 - {E4CD5CAB-EC77-4E94-88DE-174D361CD4B3}.Release|Win32.ActiveCfg = Release|Win32 - {E4CD5CAB-EC77-4E94-88DE-174D361CD4B3}.Release|Win32.Build.0 = Release|Win32 - {E4CD5CAB-EC77-4E94-88DE-174D361CD4B3}.Debug|x64.ActiveCfg = Debug|x64 - {E4CD5CAB-EC77-4E94-88DE-174D361CD4B3}.Debug|x64.Build.0 = Debug|x64 - {E4CD5CAB-EC77-4E94-88DE-174D361CD4B3}.Release|x64.ActiveCfg = Release|x64 - {E4CD5CAB-EC77-4E94-88DE-174D361CD4B3}.Release|x64.Build.0 = Release|x64 - {71D634DA-3CBE-46FD-B793-50EE8660A13A}.Debug|Win32.ActiveCfg = Debug|Win32 - {71D634DA-3CBE-46FD-B793-50EE8660A13A}.Debug|Win32.Build.0 = Debug|Win32 - {71D634DA-3CBE-46FD-B793-50EE8660A13A}.Release|Win32.ActiveCfg = Release|Win32 - {71D634DA-3CBE-46FD-B793-50EE8660A13A}.Release|Win32.Build.0 = Release|Win32 - {71D634DA-3CBE-46FD-B793-50EE8660A13A}.Debug|x64.ActiveCfg = Debug|x64 - {71D634DA-3CBE-46FD-B793-50EE8660A13A}.Debug|x64.Build.0 = Debug|x64 - {71D634DA-3CBE-46FD-B793-50EE8660A13A}.Release|x64.ActiveCfg = Release|x64 - {71D634DA-3CBE-46FD-B793-50EE8660A13A}.Release|x64.Build.0 = Release|x64 + {AB6754C7-D6E4-492F-84D1-31B3A5AE4C97}.Debug|Win32.ActiveCfg = Debug|Win32 + {AB6754C7-D6E4-492F-84D1-31B3A5AE4C97}.Debug|Win32.Build.0 = Debug|Win32 + {AB6754C7-D6E4-492F-84D1-31B3A5AE4C97}.Release|Win32.ActiveCfg = Release|Win32 + {AB6754C7-D6E4-492F-84D1-31B3A5AE4C97}.Release|Win32.Build.0 = Release|Win32 + {AB6754C7-D6E4-492F-84D1-31B3A5AE4C97}.Debug|x64.ActiveCfg = Debug|x64 + {AB6754C7-D6E4-492F-84D1-31B3A5AE4C97}.Debug|x64.Build.0 = Debug|x64 + {AB6754C7-D6E4-492F-84D1-31B3A5AE4C97}.Release|x64.ActiveCfg = Release|x64 + {AB6754C7-D6E4-492F-84D1-31B3A5AE4C97}.Release|x64.Build.0 = Release|x64 + {41E2DC8A-7DCA-4590-B9B9-466AC5EA85F3}.Debug|Win32.ActiveCfg = Debug|Win32 + {41E2DC8A-7DCA-4590-B9B9-466AC5EA85F3}.Debug|Win32.Build.0 = Debug|Win32 + {41E2DC8A-7DCA-4590-B9B9-466AC5EA85F3}.Release|Win32.ActiveCfg = Release|Win32 + {41E2DC8A-7DCA-4590-B9B9-466AC5EA85F3}.Release|Win32.Build.0 = Release|Win32 + {41E2DC8A-7DCA-4590-B9B9-466AC5EA85F3}.Debug|x64.ActiveCfg = Debug|x64 + {41E2DC8A-7DCA-4590-B9B9-466AC5EA85F3}.Debug|x64.Build.0 = Debug|x64 + {41E2DC8A-7DCA-4590-B9B9-466AC5EA85F3}.Release|x64.ActiveCfg = Release|x64 + {41E2DC8A-7DCA-4590-B9B9-466AC5EA85F3}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {E4CD5CAB-EC77-4E94-88DE-174D361CD4B3} = {D78D0CAE-EF4B-44FC-A328-A04817C7A02D} - {71D634DA-3CBE-46FD-B793-50EE8660A13A} = {8B8F8102-C54A-4300-966E-EC924824AB8A} + {AB6754C7-D6E4-492F-84D1-31B3A5AE4C97} = {752EF0BD-A05B-4CFE-93E1-1F34EA041029} + {41E2DC8A-7DCA-4590-B9B9-466AC5EA85F3} = {924B6153-4A9C-4B7A-8402-71C7D157FE10} EndGlobalSection EndGlobal diff --git a/filesys/miniFilter/avscan/filter/avscan.vcxproj b/filesys/miniFilter/avscan/filter/avscan.vcxproj index 1ffed6533..53f0c0f3b 100644 --- a/filesys/miniFilter/avscan/filter/avscan.vcxproj +++ b/filesys/miniFilter/avscan/filter/avscan.vcxproj @@ -19,11 +19,11 @@ - {E4CD5CAB-EC77-4E94-88DE-174D361CD4B3} + {AB6754C7-D6E4-492F-84D1-31B3A5AE4C97} $(MSBuildProjectName) Debug Win32 - {1067E8CA-F2D6-4D98-BC94-B86E241AAA17} + {8EDC5406-9213-42C2-AE6C-EE0CA68F12C6} @@ -171,7 +171,6 @@ - diff --git a/filesys/miniFilter/avscan/filter/avscan.vcxproj.Filters b/filesys/miniFilter/avscan/filter/avscan.vcxproj.Filters index 366bc9165..a270c7e77 100644 --- a/filesys/miniFilter/avscan/filter/avscan.vcxproj.Filters +++ b/filesys/miniFilter/avscan/filter/avscan.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {1A8B08AD-02ED-4F8D-9692-2C34A5799CEE} + {AA91F4AE-57B6-4E58-87D7-7B4EBF6EAF56} h;hpp;hxx;hm;inl;inc;xsd - {8DD0310C-BC99-44D2-A386-3B07A6947AAA} + {449E613D-2943-404D-982C-B94A94A7740A} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {C38EDF08-31B6-4B26-BA29-02805AA5D698} + {AFB579FC-5EB4-4CE7-930D-E951C76CE714} inf;inv;inx;mof;mc; - {151004C3-679F-4485-BF94-3CC5842AF213} + {3642C4F9-F826-4B8D-B4A7-ECBEBC07E91C} diff --git a/filesys/miniFilter/avscan/filter/communication.c b/filesys/miniFilter/avscan/filter/communication.c index ab3b00dd0..a38effb79 100644 --- a/filesys/miniFilter/avscan/filter/communication.c +++ b/filesys/miniFilter/avscan/filter/communication.c @@ -1381,7 +1381,7 @@ Return Value: break; default: FLT_ASSERTMSG( "No such connection type.\n", FALSE); - break; + return STATUS_INVALID_PARAMETER; } RtlInitUnicodeString( &uniString, portName ); diff --git a/filesys/miniFilter/avscan/user/avscan.vcxproj b/filesys/miniFilter/avscan/user/avscan.vcxproj index 95b470acb..743741217 100644 --- a/filesys/miniFilter/avscan/user/avscan.vcxproj +++ b/filesys/miniFilter/avscan/user/avscan.vcxproj @@ -19,11 +19,11 @@ - {71D634DA-3CBE-46FD-B793-50EE8660A13A} + {41E2DC8A-7DCA-4590-B9B9-466AC5EA85F3} $(MSBuildProjectName) Debug Win32 - {E649BB1A-4B9F-4AF5-B5E2-9DED21AA79C6} + {6DFC7CBA-13B8-4D58-9EBB-A732D5B0726E} @@ -180,7 +180,6 @@ - diff --git a/filesys/miniFilter/avscan/user/avscan.vcxproj.Filters b/filesys/miniFilter/avscan/user/avscan.vcxproj.Filters index 75b4df39f..8d30d0ba7 100644 --- a/filesys/miniFilter/avscan/user/avscan.vcxproj.Filters +++ b/filesys/miniFilter/avscan/user/avscan.vcxproj.Filters @@ -3,15 +3,15 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {6330BD20-823F-41C7-8759-F6597157F670} + {B2D2EC97-482F-4194-BE54-A3D1793321E7} h;hpp;hxx;hm;inl;inc;xsd - {AC298499-B215-4A19-85AB-4A72B4A56B49} + {09D0E94F-1C21-4F8C-83BC-1F1BEFCFB375} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {C0A004FA-FFA1-4504-9AF4-3671E5079F64} + {DD9E6634-04B3-4D88-A606-B9991BFF640F} diff --git a/filesys/miniFilter/cancelSafe/cancelSafe.inf b/filesys/miniFilter/cancelSafe/cancelSafe.inf index d13586f74..132eea1e5 100644 --- a/filesys/miniFilter/cancelSafe/cancelSafe.inf +++ b/filesys/miniFilter/cancelSafe/cancelSafe.inf @@ -9,7 +9,7 @@ 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% +Provider = %ProviderString% DriverVer = 06/16/2007,1.0.0.0 CatalogFile = cancelsafe.cat @@ -85,7 +85,7 @@ cancelsafe.sys = 1,, ;; [Strings] -Msft = "Microsoft Corporation" +ProviderString = "TODO-Set-Provider" ServiceDescription = "CancelSafe Mini-Filter Driver" ServiceName = "CancelSafe" DriverName = "CancelSafe" diff --git a/filesys/miniFilter/cancelSafe/cancelSafe.sln b/filesys/miniFilter/cancelSafe/cancelSafe.sln index a85db67b3..f87b6605b 100644 --- a/filesys/miniFilter/cancelSafe/cancelSafe.sln +++ b/filesys/miniFilter/cancelSafe/cancelSafe.sln @@ -3,7 +3,7 @@ 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", "{17EA55B5-5F8B-49FE-AF79-20F8F5E9CBB8}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cancelSafe", "cancelSafe.vcxproj", "{AEE2C107-87E7-4A24-B75F-2D3C50FA071C}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {17EA55B5-5F8B-49FE-AF79-20F8F5E9CBB8}.Debug|Win32.ActiveCfg = Debug|Win32 - {17EA55B5-5F8B-49FE-AF79-20F8F5E9CBB8}.Debug|Win32.Build.0 = Debug|Win32 - {17EA55B5-5F8B-49FE-AF79-20F8F5E9CBB8}.Release|Win32.ActiveCfg = Release|Win32 - {17EA55B5-5F8B-49FE-AF79-20F8F5E9CBB8}.Release|Win32.Build.0 = Release|Win32 - {17EA55B5-5F8B-49FE-AF79-20F8F5E9CBB8}.Debug|x64.ActiveCfg = Debug|x64 - {17EA55B5-5F8B-49FE-AF79-20F8F5E9CBB8}.Debug|x64.Build.0 = Debug|x64 - {17EA55B5-5F8B-49FE-AF79-20F8F5E9CBB8}.Release|x64.ActiveCfg = Release|x64 - {17EA55B5-5F8B-49FE-AF79-20F8F5E9CBB8}.Release|x64.Build.0 = Release|x64 + {AEE2C107-87E7-4A24-B75F-2D3C50FA071C}.Debug|Win32.ActiveCfg = Debug|Win32 + {AEE2C107-87E7-4A24-B75F-2D3C50FA071C}.Debug|Win32.Build.0 = Debug|Win32 + {AEE2C107-87E7-4A24-B75F-2D3C50FA071C}.Release|Win32.ActiveCfg = Release|Win32 + {AEE2C107-87E7-4A24-B75F-2D3C50FA071C}.Release|Win32.Build.0 = Release|Win32 + {AEE2C107-87E7-4A24-B75F-2D3C50FA071C}.Debug|x64.ActiveCfg = Debug|x64 + {AEE2C107-87E7-4A24-B75F-2D3C50FA071C}.Debug|x64.Build.0 = Debug|x64 + {AEE2C107-87E7-4A24-B75F-2D3C50FA071C}.Release|x64.ActiveCfg = Release|x64 + {AEE2C107-87E7-4A24-B75F-2D3C50FA071C}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/filesys/miniFilter/cancelSafe/cancelSafe.vcxproj b/filesys/miniFilter/cancelSafe/cancelSafe.vcxproj index c5e30e9e1..a1c3a8e31 100644 --- a/filesys/miniFilter/cancelSafe/cancelSafe.vcxproj +++ b/filesys/miniFilter/cancelSafe/cancelSafe.vcxproj @@ -19,11 +19,11 @@ - {17EA55B5-5F8B-49FE-AF79-20F8F5E9CBB8} + {AEE2C107-87E7-4A24-B75F-2D3C50FA071C} $(MSBuildProjectName) Debug Win32 - {1A4310B8-0486-411A-876C-A46B9F451585} + {10C7EEB9-1BA0-466E-A4B0-F39614E9D82F} @@ -170,7 +170,6 @@ - diff --git a/filesys/miniFilter/cancelSafe/cancelSafe.vcxproj.Filters b/filesys/miniFilter/cancelSafe/cancelSafe.vcxproj.Filters index db122ac62..9ae34e107 100644 --- a/filesys/miniFilter/cancelSafe/cancelSafe.vcxproj.Filters +++ b/filesys/miniFilter/cancelSafe/cancelSafe.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {FAEAE193-5200-4618-812C-70AFA581370C} + {52C4C7D8-1B22-49D0-84EF-98E084EDBEB9} h;hpp;hxx;hm;inl;inc;xsd - {9F8F1EC3-3CF7-4411-BEE9-5DEFA1D2AE58} + {F3CCF591-8A99-4E3E-9568-50A0B12DD115} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {3331A654-2D71-4859-AC8B-FC9157021A59} + {9E1012D3-C962-4D0D-B9CF-577189184D95} inf;inv;inx;mof;mc; - {1877FF63-A2D6-4D22-85FF-5158F35E2AAF} + {2F385EC0-52C9-4DAD-A6D0-730E61439EDB} diff --git a/filesys/miniFilter/cdo/cdo.inf b/filesys/miniFilter/cdo/cdo.inf index 327fba11f..52dc6c068 100644 --- a/filesys/miniFilter/cdo/cdo.inf +++ b/filesys/miniFilter/cdo/cdo.inf @@ -9,7 +9,7 @@ Signature = "$Windows NT$" Class = "ActivityMonitor" ;This is determined by the work this filter driver does ClassGuid = {b86dff51-a31e-4bac-b3cf-e8cfe75c9fc2} -Provider = %Msft% +Provider = %ProviderString% DriverVer = 06/16/2007,1.0.0.1 CatalogFile = cdo.cat @@ -83,7 +83,7 @@ cdo.sys = 1,, ;; [Strings] -Msft = "Microsoft Corporation" +ProviderString = "TODO-Set-Provider" ServiceDescription = "Control Device Object File System Filter Driver Sample" ServiceName = "CDO" DriverName = "cdo" diff --git a/filesys/miniFilter/cdo/cdo.sln b/filesys/miniFilter/cdo/cdo.sln index b6441b60a..7648b7e2f 100644 --- a/filesys/miniFilter/cdo/cdo.sln +++ b/filesys/miniFilter/cdo/cdo.sln @@ -3,7 +3,7 @@ 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", "{4A98BEA4-7D70-4642-814D-B3E69149E496}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cdo", "cdo.vcxproj", "{EB236D74-D9A7-431F-B19C-FF09D8ED8205}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {4A98BEA4-7D70-4642-814D-B3E69149E496}.Debug|Win32.ActiveCfg = Debug|Win32 - {4A98BEA4-7D70-4642-814D-B3E69149E496}.Debug|Win32.Build.0 = Debug|Win32 - {4A98BEA4-7D70-4642-814D-B3E69149E496}.Release|Win32.ActiveCfg = Release|Win32 - {4A98BEA4-7D70-4642-814D-B3E69149E496}.Release|Win32.Build.0 = Release|Win32 - {4A98BEA4-7D70-4642-814D-B3E69149E496}.Debug|x64.ActiveCfg = Debug|x64 - {4A98BEA4-7D70-4642-814D-B3E69149E496}.Debug|x64.Build.0 = Debug|x64 - {4A98BEA4-7D70-4642-814D-B3E69149E496}.Release|x64.ActiveCfg = Release|x64 - {4A98BEA4-7D70-4642-814D-B3E69149E496}.Release|x64.Build.0 = Release|x64 + {EB236D74-D9A7-431F-B19C-FF09D8ED8205}.Debug|Win32.ActiveCfg = Debug|Win32 + {EB236D74-D9A7-431F-B19C-FF09D8ED8205}.Debug|Win32.Build.0 = Debug|Win32 + {EB236D74-D9A7-431F-B19C-FF09D8ED8205}.Release|Win32.ActiveCfg = Release|Win32 + {EB236D74-D9A7-431F-B19C-FF09D8ED8205}.Release|Win32.Build.0 = Release|Win32 + {EB236D74-D9A7-431F-B19C-FF09D8ED8205}.Debug|x64.ActiveCfg = Debug|x64 + {EB236D74-D9A7-431F-B19C-FF09D8ED8205}.Debug|x64.Build.0 = Debug|x64 + {EB236D74-D9A7-431F-B19C-FF09D8ED8205}.Release|x64.ActiveCfg = Release|x64 + {EB236D74-D9A7-431F-B19C-FF09D8ED8205}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/filesys/miniFilter/cdo/cdo.vcxproj b/filesys/miniFilter/cdo/cdo.vcxproj index 3e5171f46..82c67a4ab 100644 --- a/filesys/miniFilter/cdo/cdo.vcxproj +++ b/filesys/miniFilter/cdo/cdo.vcxproj @@ -19,11 +19,11 @@ - {4A98BEA4-7D70-4642-814D-B3E69149E496} + {EB236D74-D9A7-431F-B19C-FF09D8ED8205} $(MSBuildProjectName) Debug Win32 - {59E189BC-6395-42FB-BA5D-85848C0F7789} + {B56D7C2D-5ABC-4177-95C8-9452EB707FC2} @@ -139,7 +139,6 @@ - diff --git a/filesys/miniFilter/cdo/cdo.vcxproj.Filters b/filesys/miniFilter/cdo/cdo.vcxproj.Filters index 9741b1526..b4428d762 100644 --- a/filesys/miniFilter/cdo/cdo.vcxproj.Filters +++ b/filesys/miniFilter/cdo/cdo.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {4EDD470E-081B-42DB-AC5F-16296A253546} + {0809536C-C0F6-48F9-B6DB-0D09DFEED741} h;hpp;hxx;hm;inl;inc;xsd - {B80F0DF0-E538-4454-9BE3-551C8D46C3EE} + {32A1BA9C-A9CF-43BF-A599-E7E88C92F71C} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {35CA4FF2-18D8-4F39-98A2-81E504C473CA} + {533F23EF-FBED-40C8-81BD-C63D302EF13A} inf;inv;inx;mof;mc; - {D5FEDC88-C91B-4136-8438-A7A7A7A7F1DC} + {9C5AAF7D-7DC6-4B25-A5D2-C41A2A6B5196} diff --git a/filesys/miniFilter/change/change.inf b/filesys/miniFilter/change/change.inf index 71d602fba..9a495e76c 100644 --- a/filesys/miniFilter/change/change.inf +++ b/filesys/miniFilter/change/change.inf @@ -9,7 +9,7 @@ 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% +Provider = %ProviderString% DriverVer = 06/16/2011,1.0.0.1 CatalogFile = change.cat @@ -82,7 +82,7 @@ change.sys = 1,, ;; [Strings] -Msft = "Microsoft Corporation" +ProviderString = "TODO-Set-Provider" ServiceDescription = "A File Change Monitoring Mini-Filter Driver" ServiceName = "change" DriverName = "change" diff --git a/filesys/miniFilter/change/change.sln b/filesys/miniFilter/change/change.sln index 62a967edb..549f61d3d 100644 --- a/filesys/miniFilter/change/change.sln +++ b/filesys/miniFilter/change/change.sln @@ -3,7 +3,7 @@ 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", "{60E86F7F-16E4-4F3C-91A5-F8E202386F10}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "change", "change.vcxproj", "{179ABF76-7125-47BF-B27B-05A3E0444306}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {60E86F7F-16E4-4F3C-91A5-F8E202386F10}.Debug|Win32.ActiveCfg = Debug|Win32 - {60E86F7F-16E4-4F3C-91A5-F8E202386F10}.Debug|Win32.Build.0 = Debug|Win32 - {60E86F7F-16E4-4F3C-91A5-F8E202386F10}.Release|Win32.ActiveCfg = Release|Win32 - {60E86F7F-16E4-4F3C-91A5-F8E202386F10}.Release|Win32.Build.0 = Release|Win32 - {60E86F7F-16E4-4F3C-91A5-F8E202386F10}.Debug|x64.ActiveCfg = Debug|x64 - {60E86F7F-16E4-4F3C-91A5-F8E202386F10}.Debug|x64.Build.0 = Debug|x64 - {60E86F7F-16E4-4F3C-91A5-F8E202386F10}.Release|x64.ActiveCfg = Release|x64 - {60E86F7F-16E4-4F3C-91A5-F8E202386F10}.Release|x64.Build.0 = Release|x64 + {179ABF76-7125-47BF-B27B-05A3E0444306}.Debug|Win32.ActiveCfg = Debug|Win32 + {179ABF76-7125-47BF-B27B-05A3E0444306}.Debug|Win32.Build.0 = Debug|Win32 + {179ABF76-7125-47BF-B27B-05A3E0444306}.Release|Win32.ActiveCfg = Release|Win32 + {179ABF76-7125-47BF-B27B-05A3E0444306}.Release|Win32.Build.0 = Release|Win32 + {179ABF76-7125-47BF-B27B-05A3E0444306}.Debug|x64.ActiveCfg = Debug|x64 + {179ABF76-7125-47BF-B27B-05A3E0444306}.Debug|x64.Build.0 = Debug|x64 + {179ABF76-7125-47BF-B27B-05A3E0444306}.Release|x64.ActiveCfg = Release|x64 + {179ABF76-7125-47BF-B27B-05A3E0444306}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/filesys/miniFilter/change/change.vcxproj b/filesys/miniFilter/change/change.vcxproj index 7f34ea33a..39eaaf00a 100644 --- a/filesys/miniFilter/change/change.vcxproj +++ b/filesys/miniFilter/change/change.vcxproj @@ -19,11 +19,11 @@ - {60E86F7F-16E4-4F3C-91A5-F8E202386F10} + {179ABF76-7125-47BF-B27B-05A3E0444306} $(MSBuildProjectName) Debug Win32 - {04E1BD24-A9C6-471E-942D-B4A4F77125FE} + {81F82398-E931-45F3-9A26-7540184D9585} @@ -167,7 +167,6 @@ - diff --git a/filesys/miniFilter/change/change.vcxproj.Filters b/filesys/miniFilter/change/change.vcxproj.Filters index ebad34d59..30c0d3e19 100644 --- a/filesys/miniFilter/change/change.vcxproj.Filters +++ b/filesys/miniFilter/change/change.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {68724F11-A0FA-48CE-85FD-A786F328F757} + {9D2A4EE4-34F7-4E91-B38A-EFFB34E0EEFD} h;hpp;hxx;hm;inl;inc;xsd - {FEAB8F27-1AD0-4230-BB82-A9FA8EF7DF67} + {B98AFCE6-22CB-4AF9-AFE8-B28E58AE48B3} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {E66AFE53-4910-4644-A9A4-63861F5375BF} + {E1EBFDCA-B504-47B5-B91C-A3A7E19469E0} inf;inv;inx;mof;mc; - {380AD108-3AC4-428B-8916-067667866507} + {5BF2CEEB-6121-44F3-868C-C6570E05CD51} diff --git a/filesys/miniFilter/ctx/ctx.inf b/filesys/miniFilter/ctx/ctx.inf index 9ba06e185..27a7f6742 100644 --- a/filesys/miniFilter/ctx/ctx.inf +++ b/filesys/miniFilter/ctx/ctx.inf @@ -9,7 +9,7 @@ Signature = "$Windows NT$" Class = "ActivityMonitor" ;This is determined by the work this filter driver does ClassGuid = {b86dff51-a31e-4bac-b3cf-e8cfe75c9fc2} -Provider = %Msft% +Provider = %ProviderString% DriverVer = 06/16/2007,1.0.0.1 CatalogFile = ctx.cat @@ -83,7 +83,7 @@ ctx.sys = 1,, ;; [Strings] -Msft = "Microsoft Corporation" +ProviderString = "TODO-Set-Provider" ServiceDescription = "Context File System Filter Driver Sample" ServiceName = "Ctx" DriverName = "ctx" diff --git a/filesys/miniFilter/ctx/ctx.sln b/filesys/miniFilter/ctx/ctx.sln index 8dfec33ae..9e05d3537 100644 --- a/filesys/miniFilter/ctx/ctx.sln +++ b/filesys/miniFilter/ctx/ctx.sln @@ -3,7 +3,7 @@ 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", "{D00E3EDA-0BA1-428E-8DBE-D3D1DD586CD0}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ctx", "ctx.vcxproj", "{1081D6E4-E64C-47E0-9738-9314BD2BA8B3}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {D00E3EDA-0BA1-428E-8DBE-D3D1DD586CD0}.Debug|Win32.ActiveCfg = Debug|Win32 - {D00E3EDA-0BA1-428E-8DBE-D3D1DD586CD0}.Debug|Win32.Build.0 = Debug|Win32 - {D00E3EDA-0BA1-428E-8DBE-D3D1DD586CD0}.Release|Win32.ActiveCfg = Release|Win32 - {D00E3EDA-0BA1-428E-8DBE-D3D1DD586CD0}.Release|Win32.Build.0 = Release|Win32 - {D00E3EDA-0BA1-428E-8DBE-D3D1DD586CD0}.Debug|x64.ActiveCfg = Debug|x64 - {D00E3EDA-0BA1-428E-8DBE-D3D1DD586CD0}.Debug|x64.Build.0 = Debug|x64 - {D00E3EDA-0BA1-428E-8DBE-D3D1DD586CD0}.Release|x64.ActiveCfg = Release|x64 - {D00E3EDA-0BA1-428E-8DBE-D3D1DD586CD0}.Release|x64.Build.0 = Release|x64 + {1081D6E4-E64C-47E0-9738-9314BD2BA8B3}.Debug|Win32.ActiveCfg = Debug|Win32 + {1081D6E4-E64C-47E0-9738-9314BD2BA8B3}.Debug|Win32.Build.0 = Debug|Win32 + {1081D6E4-E64C-47E0-9738-9314BD2BA8B3}.Release|Win32.ActiveCfg = Release|Win32 + {1081D6E4-E64C-47E0-9738-9314BD2BA8B3}.Release|Win32.Build.0 = Release|Win32 + {1081D6E4-E64C-47E0-9738-9314BD2BA8B3}.Debug|x64.ActiveCfg = Debug|x64 + {1081D6E4-E64C-47E0-9738-9314BD2BA8B3}.Debug|x64.Build.0 = Debug|x64 + {1081D6E4-E64C-47E0-9738-9314BD2BA8B3}.Release|x64.ActiveCfg = Release|x64 + {1081D6E4-E64C-47E0-9738-9314BD2BA8B3}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/filesys/miniFilter/ctx/ctx.vcxproj b/filesys/miniFilter/ctx/ctx.vcxproj index 02105e0ac..f5c09b767 100644 --- a/filesys/miniFilter/ctx/ctx.vcxproj +++ b/filesys/miniFilter/ctx/ctx.vcxproj @@ -19,11 +19,11 @@ - {D00E3EDA-0BA1-428E-8DBE-D3D1DD586CD0} + {1081D6E4-E64C-47E0-9738-9314BD2BA8B3} $(MSBuildProjectName) Debug Win32 - {EA561ABD-0088-4EF5-875C-CE05C0BC76D4} + {CB78F7E3-CFFD-4EEF-A89F-212D44065AC2} @@ -169,7 +169,6 @@ - diff --git a/filesys/miniFilter/ctx/ctx.vcxproj.Filters b/filesys/miniFilter/ctx/ctx.vcxproj.Filters index 22a081479..918745b32 100644 --- a/filesys/miniFilter/ctx/ctx.vcxproj.Filters +++ b/filesys/miniFilter/ctx/ctx.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {36F385EE-28D4-41AE-A395-DD2DDE392541} + {58C25697-664D-4A7B-B92D-D77AB896E1A2} h;hpp;hxx;hm;inl;inc;xsd - {B72DF203-BB8E-4EA1-AAA9-F25126CC8C2B} + {803EF3E0-4FF0-476A-9393-7FC0D12FADEE} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {6968CBCC-DC1D-49E1-BCBE-1FBAC7ED57E6} + {B607D689-0BE1-43E0-BC3A-21C334711CF0} inf;inv;inx;mof;mc; - {C038C42C-4536-4CB3-8645-69BE91B63491} + {AE599C40-3283-4930-9FAE-74B87C47917A} diff --git a/filesys/miniFilter/delete/delete.inf b/filesys/miniFilter/delete/delete.inf index 350f6df75..aa974b472 100644 --- a/filesys/miniFilter/delete/delete.inf +++ b/filesys/miniFilter/delete/delete.inf @@ -9,7 +9,7 @@ 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% +Provider = %ProviderString% DriverVer = 06/16/2007,1.0.0.1 CatalogFile = delete.cat @@ -83,7 +83,7 @@ delete.sys = 1,, ;; [Strings] -Msft = "Microsoft Corporation" +ProviderString = "TODO-Set-Provider" ServiceDescription = "Delete Notification Mini-Filter Driver" ServiceName = "delete" DriverName = "delete" diff --git a/filesys/miniFilter/delete/delete.sln b/filesys/miniFilter/delete/delete.sln index 2c13b91b3..d4cd99a13 100644 --- a/filesys/miniFilter/delete/delete.sln +++ b/filesys/miniFilter/delete/delete.sln @@ -3,7 +3,7 @@ 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", "{70924271-22E0-474E-BB48-4176AB921E8F}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "delete", "delete.vcxproj", "{410C84AC-DE40-4A38-9563-058909292244}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {70924271-22E0-474E-BB48-4176AB921E8F}.Debug|Win32.ActiveCfg = Debug|Win32 - {70924271-22E0-474E-BB48-4176AB921E8F}.Debug|Win32.Build.0 = Debug|Win32 - {70924271-22E0-474E-BB48-4176AB921E8F}.Release|Win32.ActiveCfg = Release|Win32 - {70924271-22E0-474E-BB48-4176AB921E8F}.Release|Win32.Build.0 = Release|Win32 - {70924271-22E0-474E-BB48-4176AB921E8F}.Debug|x64.ActiveCfg = Debug|x64 - {70924271-22E0-474E-BB48-4176AB921E8F}.Debug|x64.Build.0 = Debug|x64 - {70924271-22E0-474E-BB48-4176AB921E8F}.Release|x64.ActiveCfg = Release|x64 - {70924271-22E0-474E-BB48-4176AB921E8F}.Release|x64.Build.0 = Release|x64 + {410C84AC-DE40-4A38-9563-058909292244}.Debug|Win32.ActiveCfg = Debug|Win32 + {410C84AC-DE40-4A38-9563-058909292244}.Debug|Win32.Build.0 = Debug|Win32 + {410C84AC-DE40-4A38-9563-058909292244}.Release|Win32.ActiveCfg = Release|Win32 + {410C84AC-DE40-4A38-9563-058909292244}.Release|Win32.Build.0 = Release|Win32 + {410C84AC-DE40-4A38-9563-058909292244}.Debug|x64.ActiveCfg = Debug|x64 + {410C84AC-DE40-4A38-9563-058909292244}.Debug|x64.Build.0 = Debug|x64 + {410C84AC-DE40-4A38-9563-058909292244}.Release|x64.ActiveCfg = Release|x64 + {410C84AC-DE40-4A38-9563-058909292244}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/filesys/miniFilter/delete/delete.vcxproj b/filesys/miniFilter/delete/delete.vcxproj index b827a7f2b..a240aa811 100644 --- a/filesys/miniFilter/delete/delete.vcxproj +++ b/filesys/miniFilter/delete/delete.vcxproj @@ -19,11 +19,11 @@ - {70924271-22E0-474E-BB48-4176AB921E8F} + {410C84AC-DE40-4A38-9563-058909292244} $(MSBuildProjectName) Debug Win32 - {983B8DC5-A107-4899-88AE-7E3EF643B95B} + {051F6346-8D3B-4D83-AB05-BB76110E004D} @@ -166,7 +166,6 @@ - diff --git a/filesys/miniFilter/delete/delete.vcxproj.Filters b/filesys/miniFilter/delete/delete.vcxproj.Filters index 2502ad885..60c84cad6 100644 --- a/filesys/miniFilter/delete/delete.vcxproj.Filters +++ b/filesys/miniFilter/delete/delete.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {18043465-F44B-4DEF-8BAE-4836B5B96347} + {0EEAE985-A09E-4685-BAA3-DFD5B6467C4E} h;hpp;hxx;hm;inl;inc;xsd - {1305C3BE-0E2E-4103-8DFB-4B06F55C5FD5} + {7C40FC3F-061F-42B5-9ADD-4CC3D3E72944} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {C66CE925-8A7F-4B95-B92B-E3EC6C00BD69} + {96A10710-A80F-4195-8674-D5C51CC6D15E} inf;inv;inx;mof;mc; - {D2AA6CEC-4B22-4779-B62F-D83B3AB88B56} + {C3920BC4-FB12-491E-B103-14EB0437F1FA} diff --git a/filesys/miniFilter/minispy/filter/minispy.vcxproj b/filesys/miniFilter/minispy/filter/minispy.vcxproj index a75aceb5b..9d5edee0f 100644 --- a/filesys/miniFilter/minispy/filter/minispy.vcxproj +++ b/filesys/miniFilter/minispy/filter/minispy.vcxproj @@ -19,11 +19,11 @@ - {F59D1435-1559-4BFF-90FF-F6B2E081ADBA} + {AB0153F1-D5E4-45E0-9308-5EF32974E9A1} $(MSBuildProjectName) Debug Win32 - {B0F09019-3767-45D4-87E2-FF17FA11652A} + {2F2D5822-720A-47AB-8E4A-7337206F83F5} @@ -184,7 +184,6 @@ - diff --git a/filesys/miniFilter/minispy/filter/minispy.vcxproj.Filters b/filesys/miniFilter/minispy/filter/minispy.vcxproj.Filters index 4c51d4ffa..747769105 100644 --- a/filesys/miniFilter/minispy/filter/minispy.vcxproj.Filters +++ b/filesys/miniFilter/minispy/filter/minispy.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {ADEB4B1F-7275-4D86-A8AC-1268384B7530} + {01075B84-BB81-4AA3-8C21-A7041C702B6F} h;hpp;hxx;hm;inl;inc;xsd - {AF75460A-0706-4819-B584-BA557565BD9E} + {EC4FB901-91AF-470A-B669-B79EAE9CD07B} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {57EBC54C-C6CD-46B7-8093-C4810A40C233} + {2ECFFA50-EC77-4CA0-8217-F41C7A6E893D} inf;inv;inx;mof;mc; - {32B54034-2B3E-4AE9-9F0D-2D256FC4276D} + {39F948D3-F781-4C4B-A2E9-788D724DC0F3} diff --git a/filesys/miniFilter/minispy/minispy.inf b/filesys/miniFilter/minispy/minispy.inf index 88cdcd404..c5cc24bb5 100644 --- a/filesys/miniFilter/minispy/minispy.inf +++ b/filesys/miniFilter/minispy/minispy.inf @@ -9,7 +9,7 @@ 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% +Provider = %ProviderString% DriverVer = 06/16/2007,1.0.0.0 CatalogFile = minispy.cat @@ -91,7 +91,7 @@ minispy.exe = 1,, ;; [Strings] -Msft = "Microsoft Corporation" +ProviderString = "TODO-Set-Provider" ServiceDescription = "Minispy mini-filter driver" ServiceName = "Minispy" DriverName = "minispy" diff --git a/filesys/miniFilter/minispy/minispy.sln b/filesys/miniFilter/minispy/minispy.sln index 22ea27432..2c51ddd14 100644 --- a/filesys/miniFilter/minispy/minispy.sln +++ b/filesys/miniFilter/minispy/minispy.sln @@ -3,13 +3,13 @@ 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", "{39A7F6C2-D480-4C5C-B6FB-48ACDDA45B59}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Filter", "Filter", "{FE789860-CDFE-48E5-AE02-E418DD3A0C6D}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "User", "User", "{42CC0135-35A4-4AD0-97D5-140EA747A3CD}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "User", "User", "{94944192-75BD-4A9E-B4DB-811E26C3A6E5}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "minispy", "filter\minispy.vcxproj", "{F59D1435-1559-4BFF-90FF-F6B2E081ADBA}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "minispy", "filter\minispy.vcxproj", "{AB0153F1-D5E4-45E0-9308-5EF32974E9A1}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "minispy", "user\minispy.vcxproj", "{8193E678-7717-4820-BBA6-A15A283160B1}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "minispy", "user\minispy.vcxproj", "{DD45D2A9-C50A-4900-8F7C-991B2ADB061B}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -19,28 +19,28 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {F59D1435-1559-4BFF-90FF-F6B2E081ADBA}.Debug|Win32.ActiveCfg = Debug|Win32 - {F59D1435-1559-4BFF-90FF-F6B2E081ADBA}.Debug|Win32.Build.0 = Debug|Win32 - {F59D1435-1559-4BFF-90FF-F6B2E081ADBA}.Release|Win32.ActiveCfg = Release|Win32 - {F59D1435-1559-4BFF-90FF-F6B2E081ADBA}.Release|Win32.Build.0 = Release|Win32 - {F59D1435-1559-4BFF-90FF-F6B2E081ADBA}.Debug|x64.ActiveCfg = Debug|x64 - {F59D1435-1559-4BFF-90FF-F6B2E081ADBA}.Debug|x64.Build.0 = Debug|x64 - {F59D1435-1559-4BFF-90FF-F6B2E081ADBA}.Release|x64.ActiveCfg = Release|x64 - {F59D1435-1559-4BFF-90FF-F6B2E081ADBA}.Release|x64.Build.0 = Release|x64 - {8193E678-7717-4820-BBA6-A15A283160B1}.Debug|Win32.ActiveCfg = Debug|Win32 - {8193E678-7717-4820-BBA6-A15A283160B1}.Debug|Win32.Build.0 = Debug|Win32 - {8193E678-7717-4820-BBA6-A15A283160B1}.Release|Win32.ActiveCfg = Release|Win32 - {8193E678-7717-4820-BBA6-A15A283160B1}.Release|Win32.Build.0 = Release|Win32 - {8193E678-7717-4820-BBA6-A15A283160B1}.Debug|x64.ActiveCfg = Debug|x64 - {8193E678-7717-4820-BBA6-A15A283160B1}.Debug|x64.Build.0 = Debug|x64 - {8193E678-7717-4820-BBA6-A15A283160B1}.Release|x64.ActiveCfg = Release|x64 - {8193E678-7717-4820-BBA6-A15A283160B1}.Release|x64.Build.0 = Release|x64 + {AB0153F1-D5E4-45E0-9308-5EF32974E9A1}.Debug|Win32.ActiveCfg = Debug|Win32 + {AB0153F1-D5E4-45E0-9308-5EF32974E9A1}.Debug|Win32.Build.0 = Debug|Win32 + {AB0153F1-D5E4-45E0-9308-5EF32974E9A1}.Release|Win32.ActiveCfg = Release|Win32 + {AB0153F1-D5E4-45E0-9308-5EF32974E9A1}.Release|Win32.Build.0 = Release|Win32 + {AB0153F1-D5E4-45E0-9308-5EF32974E9A1}.Debug|x64.ActiveCfg = Debug|x64 + {AB0153F1-D5E4-45E0-9308-5EF32974E9A1}.Debug|x64.Build.0 = Debug|x64 + {AB0153F1-D5E4-45E0-9308-5EF32974E9A1}.Release|x64.ActiveCfg = Release|x64 + {AB0153F1-D5E4-45E0-9308-5EF32974E9A1}.Release|x64.Build.0 = Release|x64 + {DD45D2A9-C50A-4900-8F7C-991B2ADB061B}.Debug|Win32.ActiveCfg = Debug|Win32 + {DD45D2A9-C50A-4900-8F7C-991B2ADB061B}.Debug|Win32.Build.0 = Debug|Win32 + {DD45D2A9-C50A-4900-8F7C-991B2ADB061B}.Release|Win32.ActiveCfg = Release|Win32 + {DD45D2A9-C50A-4900-8F7C-991B2ADB061B}.Release|Win32.Build.0 = Release|Win32 + {DD45D2A9-C50A-4900-8F7C-991B2ADB061B}.Debug|x64.ActiveCfg = Debug|x64 + {DD45D2A9-C50A-4900-8F7C-991B2ADB061B}.Debug|x64.Build.0 = Debug|x64 + {DD45D2A9-C50A-4900-8F7C-991B2ADB061B}.Release|x64.ActiveCfg = Release|x64 + {DD45D2A9-C50A-4900-8F7C-991B2ADB061B}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {F59D1435-1559-4BFF-90FF-F6B2E081ADBA} = {39A7F6C2-D480-4C5C-B6FB-48ACDDA45B59} - {8193E678-7717-4820-BBA6-A15A283160B1} = {42CC0135-35A4-4AD0-97D5-140EA747A3CD} + {AB0153F1-D5E4-45E0-9308-5EF32974E9A1} = {FE789860-CDFE-48E5-AE02-E418DD3A0C6D} + {DD45D2A9-C50A-4900-8F7C-991B2ADB061B} = {94944192-75BD-4A9E-B4DB-811E26C3A6E5} EndGlobalSection EndGlobal diff --git a/filesys/miniFilter/minispy/user/minispy.vcxproj b/filesys/miniFilter/minispy/user/minispy.vcxproj index 14af62cc4..2f319d168 100644 --- a/filesys/miniFilter/minispy/user/minispy.vcxproj +++ b/filesys/miniFilter/minispy/user/minispy.vcxproj @@ -19,11 +19,11 @@ - {8193E678-7717-4820-BBA6-A15A283160B1} + {DD45D2A9-C50A-4900-8F7C-991B2ADB061B} $(MSBuildProjectName) Debug Win32 - {FF833B16-7C46-43F5-911C-228791B84F56} + {6E48C68A-702A-40C5-99C4-4C7AAD8EE118} @@ -179,7 +179,6 @@ - diff --git a/filesys/miniFilter/minispy/user/minispy.vcxproj.Filters b/filesys/miniFilter/minispy/user/minispy.vcxproj.Filters index e9cd4bba8..a4d623541 100644 --- a/filesys/miniFilter/minispy/user/minispy.vcxproj.Filters +++ b/filesys/miniFilter/minispy/user/minispy.vcxproj.Filters @@ -3,15 +3,15 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {1FD3EC74-2C96-4C1D-BB60-19DC7C1F0008} + {9EAF64A2-15CB-4825-AE64-33D70752B086} h;hpp;hxx;hm;inl;inc;xsd - {0B6C07D3-9F4A-499D-B097-2BC02C31091A} + {AC5F1AAD-A0DB-4BE1-884D-234ABCC16253} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {36CB183B-6EAB-4767-957A-87186CA11D68} + {04DFEF0C-C42D-4BAF-84AE-C49DFBAF1C6D} diff --git a/filesys/miniFilter/nullFilter/nullFilter.inf b/filesys/miniFilter/nullFilter/nullFilter.inf index e1cb7db1a..5f1c45c21 100644 --- a/filesys/miniFilter/nullFilter/nullFilter.inf +++ b/filesys/miniFilter/nullFilter/nullFilter.inf @@ -9,7 +9,7 @@ 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% +Provider = %ProviderString% DriverVer = 06/16/2007,1.0.0.0 CatalogFile = nullfilter.cat @@ -82,7 +82,7 @@ nullfilter.sys = 1,, ;; [Strings] -Msft = "Microsoft Corporation" +ProviderString = "TODO-Set-Provider" ServiceDescription = "NullFilter mini-filter driver" ServiceName = "NullFilter" DriverName = "NullFilter" diff --git a/filesys/miniFilter/nullFilter/nullFilter.sln b/filesys/miniFilter/nullFilter/nullFilter.sln index 1a5db6f9b..d0ad28453 100644 --- a/filesys/miniFilter/nullFilter/nullFilter.sln +++ b/filesys/miniFilter/nullFilter/nullFilter.sln @@ -3,7 +3,7 @@ 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", "{50FE4D46-70DC-4104-8360-9E84D58B1ABD}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nullFilter", "nullFilter.vcxproj", "{BC90CE20-0CDF-4B17-9561-76EB460963DF}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {50FE4D46-70DC-4104-8360-9E84D58B1ABD}.Debug|Win32.ActiveCfg = Debug|Win32 - {50FE4D46-70DC-4104-8360-9E84D58B1ABD}.Debug|Win32.Build.0 = Debug|Win32 - {50FE4D46-70DC-4104-8360-9E84D58B1ABD}.Release|Win32.ActiveCfg = Release|Win32 - {50FE4D46-70DC-4104-8360-9E84D58B1ABD}.Release|Win32.Build.0 = Release|Win32 - {50FE4D46-70DC-4104-8360-9E84D58B1ABD}.Debug|x64.ActiveCfg = Debug|x64 - {50FE4D46-70DC-4104-8360-9E84D58B1ABD}.Debug|x64.Build.0 = Debug|x64 - {50FE4D46-70DC-4104-8360-9E84D58B1ABD}.Release|x64.ActiveCfg = Release|x64 - {50FE4D46-70DC-4104-8360-9E84D58B1ABD}.Release|x64.Build.0 = Release|x64 + {BC90CE20-0CDF-4B17-9561-76EB460963DF}.Debug|Win32.ActiveCfg = Debug|Win32 + {BC90CE20-0CDF-4B17-9561-76EB460963DF}.Debug|Win32.Build.0 = Debug|Win32 + {BC90CE20-0CDF-4B17-9561-76EB460963DF}.Release|Win32.ActiveCfg = Release|Win32 + {BC90CE20-0CDF-4B17-9561-76EB460963DF}.Release|Win32.Build.0 = Release|Win32 + {BC90CE20-0CDF-4B17-9561-76EB460963DF}.Debug|x64.ActiveCfg = Debug|x64 + {BC90CE20-0CDF-4B17-9561-76EB460963DF}.Debug|x64.Build.0 = Debug|x64 + {BC90CE20-0CDF-4B17-9561-76EB460963DF}.Release|x64.ActiveCfg = Release|x64 + {BC90CE20-0CDF-4B17-9561-76EB460963DF}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/filesys/miniFilter/nullFilter/nullFilter.vcxproj b/filesys/miniFilter/nullFilter/nullFilter.vcxproj index 68b1d5da2..8ba94e195 100644 --- a/filesys/miniFilter/nullFilter/nullFilter.vcxproj +++ b/filesys/miniFilter/nullFilter/nullFilter.vcxproj @@ -19,11 +19,11 @@ - {50FE4D46-70DC-4104-8360-9E84D58B1ABD} + {BC90CE20-0CDF-4B17-9561-76EB460963DF} $(MSBuildProjectName) Debug Win32 - {0B2B36C6-26D3-49D5-962E-C42C0A37B0D7} + {4420F370-92FD-41BD-93E4-3F2AF8F12A65} @@ -138,7 +138,6 @@ - diff --git a/filesys/miniFilter/nullFilter/nullFilter.vcxproj.Filters b/filesys/miniFilter/nullFilter/nullFilter.vcxproj.Filters index 33afb0779..c532b6ad4 100644 --- a/filesys/miniFilter/nullFilter/nullFilter.vcxproj.Filters +++ b/filesys/miniFilter/nullFilter/nullFilter.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {F188061D-FE31-43EE-A670-3276C6F0AD16} + {E01F7D72-4DC2-420E-9147-6532FECF63A8} h;hpp;hxx;hm;inl;inc;xsd - {11EA27C6-A0E0-4F0A-8981-6592E27784D9} + {E6E911AB-5725-46B5-8BDA-268D1D9B8388} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {03863453-B4FD-4A2F-AA85-A2448B28F0DC} + {A287192A-CE91-44F6-B0A3-7A120AB72676} inf;inv;inx;mof;mc; - {714F8ED5-D3FD-4FDF-9650-251E97E2D0B6} + {F599A534-6357-4DB9-9AD6-2B17B6AC4C43} diff --git a/filesys/miniFilter/passThrough/passThrough.inf b/filesys/miniFilter/passThrough/passThrough.inf index 28eb9b4a3..91d59655e 100644 --- a/filesys/miniFilter/passThrough/passThrough.inf +++ b/filesys/miniFilter/passThrough/passThrough.inf @@ -9,7 +9,7 @@ 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% +Provider = %ProviderString% DriverVer = 06/16/2007,1.0.0.1 CatalogFile = passthrough.cat @@ -83,7 +83,7 @@ passthrough.sys = 1,, ;; [Strings] -Msft = "Microsoft Corporation" +ProviderString = "TODO-Set-Provider" ServiceDescription = "PassThrough Mini-Filter Driver" ServiceName = "PassThrough" DriverName = "PassThrough" diff --git a/filesys/miniFilter/passThrough/passThrough.sln b/filesys/miniFilter/passThrough/passThrough.sln index 1d49e685c..1201cecd6 100644 --- a/filesys/miniFilter/passThrough/passThrough.sln +++ b/filesys/miniFilter/passThrough/passThrough.sln @@ -3,7 +3,7 @@ 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", "{8CE4EFE9-8B8E-4785-A3AF-7DD92E105868}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "passThrough", "passThrough.vcxproj", "{AA5895EB-53F3-4888-AF0E-29923C1C6E50}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {8CE4EFE9-8B8E-4785-A3AF-7DD92E105868}.Debug|Win32.ActiveCfg = Debug|Win32 - {8CE4EFE9-8B8E-4785-A3AF-7DD92E105868}.Debug|Win32.Build.0 = Debug|Win32 - {8CE4EFE9-8B8E-4785-A3AF-7DD92E105868}.Release|Win32.ActiveCfg = Release|Win32 - {8CE4EFE9-8B8E-4785-A3AF-7DD92E105868}.Release|Win32.Build.0 = Release|Win32 - {8CE4EFE9-8B8E-4785-A3AF-7DD92E105868}.Debug|x64.ActiveCfg = Debug|x64 - {8CE4EFE9-8B8E-4785-A3AF-7DD92E105868}.Debug|x64.Build.0 = Debug|x64 - {8CE4EFE9-8B8E-4785-A3AF-7DD92E105868}.Release|x64.ActiveCfg = Release|x64 - {8CE4EFE9-8B8E-4785-A3AF-7DD92E105868}.Release|x64.Build.0 = Release|x64 + {AA5895EB-53F3-4888-AF0E-29923C1C6E50}.Debug|Win32.ActiveCfg = Debug|Win32 + {AA5895EB-53F3-4888-AF0E-29923C1C6E50}.Debug|Win32.Build.0 = Debug|Win32 + {AA5895EB-53F3-4888-AF0E-29923C1C6E50}.Release|Win32.ActiveCfg = Release|Win32 + {AA5895EB-53F3-4888-AF0E-29923C1C6E50}.Release|Win32.Build.0 = Release|Win32 + {AA5895EB-53F3-4888-AF0E-29923C1C6E50}.Debug|x64.ActiveCfg = Debug|x64 + {AA5895EB-53F3-4888-AF0E-29923C1C6E50}.Debug|x64.Build.0 = Debug|x64 + {AA5895EB-53F3-4888-AF0E-29923C1C6E50}.Release|x64.ActiveCfg = Release|x64 + {AA5895EB-53F3-4888-AF0E-29923C1C6E50}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/filesys/miniFilter/passThrough/passThrough.vcxproj b/filesys/miniFilter/passThrough/passThrough.vcxproj index cef246fc0..75615c148 100644 --- a/filesys/miniFilter/passThrough/passThrough.vcxproj +++ b/filesys/miniFilter/passThrough/passThrough.vcxproj @@ -19,11 +19,11 @@ - {8CE4EFE9-8B8E-4785-A3AF-7DD92E105868} + {AA5895EB-53F3-4888-AF0E-29923C1C6E50} $(MSBuildProjectName) Debug Win32 - {43A19993-BF9D-4DAD-A4CB-892DA37962D9} + {32A5FBD2-142B-49C5-8BDD-01D1BE06EFD4} @@ -138,7 +138,6 @@ - diff --git a/filesys/miniFilter/passThrough/passThrough.vcxproj.Filters b/filesys/miniFilter/passThrough/passThrough.vcxproj.Filters index 53c358b3a..d310379d6 100644 --- a/filesys/miniFilter/passThrough/passThrough.vcxproj.Filters +++ b/filesys/miniFilter/passThrough/passThrough.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {A3FF7F10-8FB5-4702-A04E-1FB975F5F7BC} + {60091B92-D84A-4E27-ADDB-D89680AD7C35} h;hpp;hxx;hm;inl;inc;xsd - {A60ED505-D6D8-4912-8E60-31F9A98408D4} + {0408A889-447A-4011-9D63-5D577FBC63A4} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {6E1D373B-63DD-434D-9219-9E9F06913A45} + {D371111D-1519-4E9C-9D10-FB2DFB84B14B} inf;inv;inx;mof;mc; - {48A890C1-A2B2-447D-8746-A08B9C046052} + {48901523-D551-4144-A596-CF7C204FA6BE} diff --git a/filesys/miniFilter/scanner/filter/scanner.vcxproj b/filesys/miniFilter/scanner/filter/scanner.vcxproj index 9f737bb86..bd57aea1f 100644 --- a/filesys/miniFilter/scanner/filter/scanner.vcxproj +++ b/filesys/miniFilter/scanner/filter/scanner.vcxproj @@ -19,11 +19,11 @@ - {6DA01722-898E-478D-AA5D-7477B31F2E3C} + {405E5558-9053-4BC3-9FF4-760C3D487476} $(MSBuildProjectName) Debug Win32 - {3C220F60-96A1-444C-A61D-4CD384F249A2} + {767DF53C-6E68-4169-AD80-FB3D48818F9B} @@ -178,7 +178,6 @@ - diff --git a/filesys/miniFilter/scanner/filter/scanner.vcxproj.Filters b/filesys/miniFilter/scanner/filter/scanner.vcxproj.Filters index f33f50379..c27a7abe9 100644 --- a/filesys/miniFilter/scanner/filter/scanner.vcxproj.Filters +++ b/filesys/miniFilter/scanner/filter/scanner.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {21DA36D6-0604-456B-948A-ED26A6DE69DE} + {E4572A93-5A55-4C6F-8F48-E2733C02A7AD} h;hpp;hxx;hm;inl;inc;xsd - {4DE93ED6-CF7E-4EED-90C7-AFED0DC52266} + {24365CE1-3829-455B-96F0-F2EB7ED5A342} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {59D2E5FD-012C-4647-A2E2-61A3E8E63CA5} + {6651233C-61D2-43E8-A29F-3C45DBDCE7CC} inf;inv;inx;mof;mc; - {CDB9F3CF-9FAB-4B3D-A857-7D6FA6680A05} + {87D3BEF2-B470-499B-B232-4DD08E2178A6} diff --git a/filesys/miniFilter/scanner/scanner.inf b/filesys/miniFilter/scanner/scanner.inf index e4a06fc50..df5cc857e 100644 --- a/filesys/miniFilter/scanner/scanner.inf +++ b/filesys/miniFilter/scanner/scanner.inf @@ -9,7 +9,7 @@ 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% +Provider = %ProviderString% DriverVer = 06/16/2007,1.0.0.0 CatalogFile = scanner.cat @@ -90,7 +90,7 @@ scanuser.exe = 1,, ;; [Strings] -Msft = "Microsoft Corporation" +ProviderString = "TODO-Set-Provider" ServiceDescription = "Scanner mini-filter driver" ServiceName = "Scanner" DriverName = "scanner" diff --git a/filesys/miniFilter/scanner/scanner.sln b/filesys/miniFilter/scanner/scanner.sln index 5414efd7b..b761659b5 100644 --- a/filesys/miniFilter/scanner/scanner.sln +++ b/filesys/miniFilter/scanner/scanner.sln @@ -3,13 +3,13 @@ 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", "{FD39AB44-4D29-4235-AF0B-25A200E98950}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Filter", "Filter", "{C50DADE3-B060-4E30-892B-C14675ACD024}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "User", "User", "{38839ABD-FCE8-455C-AFAC-3AF46756066C}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "User", "User", "{AB4A5F23-9E0F-46E9-9A2E-F9DB3646BEDD}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "scanner", "filter\scanner.vcxproj", "{6DA01722-898E-478D-AA5D-7477B31F2E3C}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "scanner", "filter\scanner.vcxproj", "{405E5558-9053-4BC3-9FF4-760C3D487476}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "scanuser", "user\scanuser.vcxproj", "{F5A0D233-ABA9-41FE-A4FE-3EA88CDE90BB}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "scanuser", "user\scanuser.vcxproj", "{9F7E0E08-F370-462D-A8DD-885DB7B074A6}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -19,28 +19,28 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {6DA01722-898E-478D-AA5D-7477B31F2E3C}.Debug|Win32.ActiveCfg = Debug|Win32 - {6DA01722-898E-478D-AA5D-7477B31F2E3C}.Debug|Win32.Build.0 = Debug|Win32 - {6DA01722-898E-478D-AA5D-7477B31F2E3C}.Release|Win32.ActiveCfg = Release|Win32 - {6DA01722-898E-478D-AA5D-7477B31F2E3C}.Release|Win32.Build.0 = Release|Win32 - {6DA01722-898E-478D-AA5D-7477B31F2E3C}.Debug|x64.ActiveCfg = Debug|x64 - {6DA01722-898E-478D-AA5D-7477B31F2E3C}.Debug|x64.Build.0 = Debug|x64 - {6DA01722-898E-478D-AA5D-7477B31F2E3C}.Release|x64.ActiveCfg = Release|x64 - {6DA01722-898E-478D-AA5D-7477B31F2E3C}.Release|x64.Build.0 = Release|x64 - {F5A0D233-ABA9-41FE-A4FE-3EA88CDE90BB}.Debug|Win32.ActiveCfg = Debug|Win32 - {F5A0D233-ABA9-41FE-A4FE-3EA88CDE90BB}.Debug|Win32.Build.0 = Debug|Win32 - {F5A0D233-ABA9-41FE-A4FE-3EA88CDE90BB}.Release|Win32.ActiveCfg = Release|Win32 - {F5A0D233-ABA9-41FE-A4FE-3EA88CDE90BB}.Release|Win32.Build.0 = Release|Win32 - {F5A0D233-ABA9-41FE-A4FE-3EA88CDE90BB}.Debug|x64.ActiveCfg = Debug|x64 - {F5A0D233-ABA9-41FE-A4FE-3EA88CDE90BB}.Debug|x64.Build.0 = Debug|x64 - {F5A0D233-ABA9-41FE-A4FE-3EA88CDE90BB}.Release|x64.ActiveCfg = Release|x64 - {F5A0D233-ABA9-41FE-A4FE-3EA88CDE90BB}.Release|x64.Build.0 = Release|x64 + {405E5558-9053-4BC3-9FF4-760C3D487476}.Debug|Win32.ActiveCfg = Debug|Win32 + {405E5558-9053-4BC3-9FF4-760C3D487476}.Debug|Win32.Build.0 = Debug|Win32 + {405E5558-9053-4BC3-9FF4-760C3D487476}.Release|Win32.ActiveCfg = Release|Win32 + {405E5558-9053-4BC3-9FF4-760C3D487476}.Release|Win32.Build.0 = Release|Win32 + {405E5558-9053-4BC3-9FF4-760C3D487476}.Debug|x64.ActiveCfg = Debug|x64 + {405E5558-9053-4BC3-9FF4-760C3D487476}.Debug|x64.Build.0 = Debug|x64 + {405E5558-9053-4BC3-9FF4-760C3D487476}.Release|x64.ActiveCfg = Release|x64 + {405E5558-9053-4BC3-9FF4-760C3D487476}.Release|x64.Build.0 = Release|x64 + {9F7E0E08-F370-462D-A8DD-885DB7B074A6}.Debug|Win32.ActiveCfg = Debug|Win32 + {9F7E0E08-F370-462D-A8DD-885DB7B074A6}.Debug|Win32.Build.0 = Debug|Win32 + {9F7E0E08-F370-462D-A8DD-885DB7B074A6}.Release|Win32.ActiveCfg = Release|Win32 + {9F7E0E08-F370-462D-A8DD-885DB7B074A6}.Release|Win32.Build.0 = Release|Win32 + {9F7E0E08-F370-462D-A8DD-885DB7B074A6}.Debug|x64.ActiveCfg = Debug|x64 + {9F7E0E08-F370-462D-A8DD-885DB7B074A6}.Debug|x64.Build.0 = Debug|x64 + {9F7E0E08-F370-462D-A8DD-885DB7B074A6}.Release|x64.ActiveCfg = Release|x64 + {9F7E0E08-F370-462D-A8DD-885DB7B074A6}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {6DA01722-898E-478D-AA5D-7477B31F2E3C} = {FD39AB44-4D29-4235-AF0B-25A200E98950} - {F5A0D233-ABA9-41FE-A4FE-3EA88CDE90BB} = {38839ABD-FCE8-455C-AFAC-3AF46756066C} + {405E5558-9053-4BC3-9FF4-760C3D487476} = {C50DADE3-B060-4E30-892B-C14675ACD024} + {9F7E0E08-F370-462D-A8DD-885DB7B074A6} = {AB4A5F23-9E0F-46E9-9A2E-F9DB3646BEDD} EndGlobalSection EndGlobal diff --git a/filesys/miniFilter/scanner/user/scanuser.vcxproj b/filesys/miniFilter/scanner/user/scanuser.vcxproj index dbab32667..c357ca880 100644 --- a/filesys/miniFilter/scanner/user/scanuser.vcxproj +++ b/filesys/miniFilter/scanner/user/scanuser.vcxproj @@ -19,11 +19,11 @@ - {F5A0D233-ABA9-41FE-A4FE-3EA88CDE90BB} + {9F7E0E08-F370-462D-A8DD-885DB7B074A6} $(MSBuildProjectName) Debug Win32 - {F6714426-3D4C-4E07-AF32-F6C97BB4E3A6} + {AFC074E2-1EBC-483A-9FC8-58096F057282} @@ -178,7 +178,6 @@ - diff --git a/filesys/miniFilter/scanner/user/scanuser.vcxproj.Filters b/filesys/miniFilter/scanner/user/scanuser.vcxproj.Filters index 0a7635cca..cda88648f 100644 --- a/filesys/miniFilter/scanner/user/scanuser.vcxproj.Filters +++ b/filesys/miniFilter/scanner/user/scanuser.vcxproj.Filters @@ -3,15 +3,15 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {2CC6A14A-A1EB-4502-B4F5-DEC93B1962CD} + {2283E0B4-0268-4650-BE85-A2FA93908595} h;hpp;hxx;hm;inl;inc;xsd - {FC5F9B5A-4DE1-4EFB-A76D-C4E632BF4E43} + {A4F1A0C4-9F84-4903-892E-977BAF91917D} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {F349C8BC-1724-4FC6-AB45-98CE70B47971} + {27540953-32D1-4CEB-9A26-1A00BA5A354B} diff --git a/filesys/miniFilter/simrep/simrep.inf b/filesys/miniFilter/simrep/simrep.inf index ba9f8af9a..2d01803d2 100644 --- a/filesys/miniFilter/simrep/simrep.inf +++ b/filesys/miniFilter/simrep/simrep.inf @@ -9,7 +9,7 @@ Signature = "$Windows NT$" Class = "ActivityMonitor" ;This is determined by the work this filter driver does ClassGuid = {b86dff51-a31e-4bac-b3cf-e8cfe75c9fc2} -Provider = %Msft% +Provider = %ProviderString% DriverVer = 01/01/2004,1.0.0.1 CatalogFile = simrep.cat @@ -89,7 +89,7 @@ simrep.sys ;; [Strings] -Msft = "Microsoft Corporation" +ProviderString = "TODO-Set-Provider" SimRepServiceDesc = "Simulate Reparse File System Filter Driver Sample" SimRepServiceName = "SimRep" SimRepDebugLevel = "DebugLevel" diff --git a/filesys/miniFilter/simrep/simrep.sln b/filesys/miniFilter/simrep/simrep.sln index f2a7496bc..820fab274 100644 --- a/filesys/miniFilter/simrep/simrep.sln +++ b/filesys/miniFilter/simrep/simrep.sln @@ -3,7 +3,7 @@ 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", "{6B29B971-97ED-48CF-BFE0-D1F1E77D75BD}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "simrep", "simrep.vcxproj", "{EC584CEA-EE54-4673-9934-2E77A90EC210}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {6B29B971-97ED-48CF-BFE0-D1F1E77D75BD}.Debug|Win32.ActiveCfg = Debug|Win32 - {6B29B971-97ED-48CF-BFE0-D1F1E77D75BD}.Debug|Win32.Build.0 = Debug|Win32 - {6B29B971-97ED-48CF-BFE0-D1F1E77D75BD}.Release|Win32.ActiveCfg = Release|Win32 - {6B29B971-97ED-48CF-BFE0-D1F1E77D75BD}.Release|Win32.Build.0 = Release|Win32 - {6B29B971-97ED-48CF-BFE0-D1F1E77D75BD}.Debug|x64.ActiveCfg = Debug|x64 - {6B29B971-97ED-48CF-BFE0-D1F1E77D75BD}.Debug|x64.Build.0 = Debug|x64 - {6B29B971-97ED-48CF-BFE0-D1F1E77D75BD}.Release|x64.ActiveCfg = Release|x64 - {6B29B971-97ED-48CF-BFE0-D1F1E77D75BD}.Release|x64.Build.0 = Release|x64 + {EC584CEA-EE54-4673-9934-2E77A90EC210}.Debug|Win32.ActiveCfg = Debug|Win32 + {EC584CEA-EE54-4673-9934-2E77A90EC210}.Debug|Win32.Build.0 = Debug|Win32 + {EC584CEA-EE54-4673-9934-2E77A90EC210}.Release|Win32.ActiveCfg = Release|Win32 + {EC584CEA-EE54-4673-9934-2E77A90EC210}.Release|Win32.Build.0 = Release|Win32 + {EC584CEA-EE54-4673-9934-2E77A90EC210}.Debug|x64.ActiveCfg = Debug|x64 + {EC584CEA-EE54-4673-9934-2E77A90EC210}.Debug|x64.Build.0 = Debug|x64 + {EC584CEA-EE54-4673-9934-2E77A90EC210}.Release|x64.ActiveCfg = Release|x64 + {EC584CEA-EE54-4673-9934-2E77A90EC210}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/filesys/miniFilter/simrep/simrep.vcxproj b/filesys/miniFilter/simrep/simrep.vcxproj index c90050839..c5f593200 100644 --- a/filesys/miniFilter/simrep/simrep.vcxproj +++ b/filesys/miniFilter/simrep/simrep.vcxproj @@ -19,11 +19,11 @@ - {6B29B971-97ED-48CF-BFE0-D1F1E77D75BD} + {EC584CEA-EE54-4673-9934-2E77A90EC210} $(MSBuildProjectName) Debug Win32 - {1A3F112D-0CEE-4178-81FF-E9B1C64AF142} + {E984D0ED-160E-4FD6-BEF1-1757750C5048} @@ -166,7 +166,6 @@ - diff --git a/filesys/miniFilter/simrep/simrep.vcxproj.Filters b/filesys/miniFilter/simrep/simrep.vcxproj.Filters index 290230b8a..6164fa356 100644 --- a/filesys/miniFilter/simrep/simrep.vcxproj.Filters +++ b/filesys/miniFilter/simrep/simrep.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {FB7CDB88-6130-4057-81D1-930F6AC33A28} + {27314838-BE3C-4784-A1B3-F9602A9963E7} h;hpp;hxx;hm;inl;inc;xsd - {9D798EBC-3174-4AE0-921F-1AD1C2FC42EE} + {015E9141-968D-497D-92A0-A3A5E9AC9E79} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {0DA2AD7A-574E-4412-8DBB-2C95DCFAF0C2} + {9711FBE9-3E19-4FA7-B349-84AF2E4CF11C} inf;inv;inx;mof;mc; - {D53692A7-3396-456F-B5EA-DDB999B67D80} + {86BE30D7-22F0-413F-AE02-B4D5A5104ED8} diff --git a/filesys/miniFilter/swapBuffers/swapBuffers.inf b/filesys/miniFilter/swapBuffers/swapBuffers.inf index d6a5fc3af..34298e6ba 100644 --- a/filesys/miniFilter/swapBuffers/swapBuffers.inf +++ b/filesys/miniFilter/swapBuffers/swapBuffers.inf @@ -9,7 +9,7 @@ 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% +Provider = %ProviderString% DriverVer = 06/16/2007,1.0.0.3 CatalogFile = swapbuffers.cat @@ -83,7 +83,7 @@ swapbuffers.sys = 1,, ;; [Strings] -Msft = "Microsoft Corporation" +ProviderString = "TODO-Set-Provider" ServiceDescription = "Swap Buffers Sample Mini-Filter Driver" ServiceName = "SwapBuffers" DriverName = "SwapBuffers" diff --git a/filesys/miniFilter/swapBuffers/swapBuffers.sln b/filesys/miniFilter/swapBuffers/swapBuffers.sln index bfb948415..4da41d0ad 100644 --- a/filesys/miniFilter/swapBuffers/swapBuffers.sln +++ b/filesys/miniFilter/swapBuffers/swapBuffers.sln @@ -3,7 +3,7 @@ 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", "{BF51955F-EC2D-4746-9F6D-A38D203F2AB6}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "swapBuffers", "swapBuffers.vcxproj", "{BA465874-4CF2-44E3-915C-025AA92540DF}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {BF51955F-EC2D-4746-9F6D-A38D203F2AB6}.Debug|Win32.ActiveCfg = Debug|Win32 - {BF51955F-EC2D-4746-9F6D-A38D203F2AB6}.Debug|Win32.Build.0 = Debug|Win32 - {BF51955F-EC2D-4746-9F6D-A38D203F2AB6}.Release|Win32.ActiveCfg = Release|Win32 - {BF51955F-EC2D-4746-9F6D-A38D203F2AB6}.Release|Win32.Build.0 = Release|Win32 - {BF51955F-EC2D-4746-9F6D-A38D203F2AB6}.Debug|x64.ActiveCfg = Debug|x64 - {BF51955F-EC2D-4746-9F6D-A38D203F2AB6}.Debug|x64.Build.0 = Debug|x64 - {BF51955F-EC2D-4746-9F6D-A38D203F2AB6}.Release|x64.ActiveCfg = Release|x64 - {BF51955F-EC2D-4746-9F6D-A38D203F2AB6}.Release|x64.Build.0 = Release|x64 + {BA465874-4CF2-44E3-915C-025AA92540DF}.Debug|Win32.ActiveCfg = Debug|Win32 + {BA465874-4CF2-44E3-915C-025AA92540DF}.Debug|Win32.Build.0 = Debug|Win32 + {BA465874-4CF2-44E3-915C-025AA92540DF}.Release|Win32.ActiveCfg = Release|Win32 + {BA465874-4CF2-44E3-915C-025AA92540DF}.Release|Win32.Build.0 = Release|Win32 + {BA465874-4CF2-44E3-915C-025AA92540DF}.Debug|x64.ActiveCfg = Debug|x64 + {BA465874-4CF2-44E3-915C-025AA92540DF}.Debug|x64.Build.0 = Debug|x64 + {BA465874-4CF2-44E3-915C-025AA92540DF}.Release|x64.ActiveCfg = Release|x64 + {BA465874-4CF2-44E3-915C-025AA92540DF}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/filesys/miniFilter/swapBuffers/swapBuffers.vcxproj b/filesys/miniFilter/swapBuffers/swapBuffers.vcxproj index d39257548..9e6aae5a8 100644 --- a/filesys/miniFilter/swapBuffers/swapBuffers.vcxproj +++ b/filesys/miniFilter/swapBuffers/swapBuffers.vcxproj @@ -19,11 +19,11 @@ - {BF51955F-EC2D-4746-9F6D-A38D203F2AB6} + {BA465874-4CF2-44E3-915C-025AA92540DF} $(MSBuildProjectName) Debug Win32 - {0B907EAE-2E1A-4640-BF08-A4CB8D10A921} + {5B91FF85-2F51-495B-A642-4BBC6954AB83} @@ -166,7 +166,6 @@ - diff --git a/filesys/miniFilter/swapBuffers/swapBuffers.vcxproj.Filters b/filesys/miniFilter/swapBuffers/swapBuffers.vcxproj.Filters index 362bb7b10..c2817dd1d 100644 --- a/filesys/miniFilter/swapBuffers/swapBuffers.vcxproj.Filters +++ b/filesys/miniFilter/swapBuffers/swapBuffers.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {547C6A8B-43E4-4D52-86E9-F04019916975} + {159547E7-C82E-4A51-A93D-C8DE79977FEE} h;hpp;hxx;hm;inl;inc;xsd - {43DE78CC-2ACC-400B-9144-EFB61C1A7E2E} + {AB3B367F-927B-4FA4-8067-72568D0512C0} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {54FF1AAF-29C0-4B36-A01E-964EC486668A} + {E0EC87A0-D8CE-4087-AD03-3E0C756787BC} inf;inv;inx;mof;mc; - {DB21CD94-ADB1-4062-9C3D-0063158FA18F} + {8D4C070B-DBC0-4CCB-9D2D-2FF993AA33E5} diff --git a/general/PLX9x5x/PLX9x5x.sln b/general/PLX9x5x/PLX9x5x.sln index cfe73a66a..175675b2e 100644 --- a/general/PLX9x5x/PLX9x5x.sln +++ b/general/PLX9x5x/PLX9x5x.sln @@ -3,13 +3,13 @@ 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", "{78FB30BE-39D1-4BE2-A805-515DFEC13B00}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sys", "Sys", "{96B4D999-7E79-4250-A5D9-E18315D8404E}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Test", "Test", "{12DE6B87-7F07-4817-98D9-7F9CB6CDB946}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Test", "Test", "{839113C4-BEC1-4845-AE0C-A753C2EC3C2D}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Pci9x5x", "sys\Pci9x5x.vcxproj", "{116683DC-66A4-4386-93D3-32A935D96B37}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Pci9x5x", "sys\Pci9x5x.vcxproj", "{1DE5F13E-88E1-4AE6-A063-C35A5472D3FF}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plx", "test\plx.vcxproj", "{235EA7C6-B4BC-4762-B7C6-F519089D31B8}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plx", "test\plx.vcxproj", "{4D1B1905-50F1-4C12-9754-E8AF3E69B87D}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -19,28 +19,28 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {116683DC-66A4-4386-93D3-32A935D96B37}.Debug|Win32.ActiveCfg = Debug|Win32 - {116683DC-66A4-4386-93D3-32A935D96B37}.Debug|Win32.Build.0 = Debug|Win32 - {116683DC-66A4-4386-93D3-32A935D96B37}.Release|Win32.ActiveCfg = Release|Win32 - {116683DC-66A4-4386-93D3-32A935D96B37}.Release|Win32.Build.0 = Release|Win32 - {116683DC-66A4-4386-93D3-32A935D96B37}.Debug|x64.ActiveCfg = Debug|x64 - {116683DC-66A4-4386-93D3-32A935D96B37}.Debug|x64.Build.0 = Debug|x64 - {116683DC-66A4-4386-93D3-32A935D96B37}.Release|x64.ActiveCfg = Release|x64 - {116683DC-66A4-4386-93D3-32A935D96B37}.Release|x64.Build.0 = Release|x64 - {235EA7C6-B4BC-4762-B7C6-F519089D31B8}.Debug|Win32.ActiveCfg = Debug|Win32 - {235EA7C6-B4BC-4762-B7C6-F519089D31B8}.Debug|Win32.Build.0 = Debug|Win32 - {235EA7C6-B4BC-4762-B7C6-F519089D31B8}.Release|Win32.ActiveCfg = Release|Win32 - {235EA7C6-B4BC-4762-B7C6-F519089D31B8}.Release|Win32.Build.0 = Release|Win32 - {235EA7C6-B4BC-4762-B7C6-F519089D31B8}.Debug|x64.ActiveCfg = Debug|x64 - {235EA7C6-B4BC-4762-B7C6-F519089D31B8}.Debug|x64.Build.0 = Debug|x64 - {235EA7C6-B4BC-4762-B7C6-F519089D31B8}.Release|x64.ActiveCfg = Release|x64 - {235EA7C6-B4BC-4762-B7C6-F519089D31B8}.Release|x64.Build.0 = Release|x64 + {1DE5F13E-88E1-4AE6-A063-C35A5472D3FF}.Debug|Win32.ActiveCfg = Debug|Win32 + {1DE5F13E-88E1-4AE6-A063-C35A5472D3FF}.Debug|Win32.Build.0 = Debug|Win32 + {1DE5F13E-88E1-4AE6-A063-C35A5472D3FF}.Release|Win32.ActiveCfg = Release|Win32 + {1DE5F13E-88E1-4AE6-A063-C35A5472D3FF}.Release|Win32.Build.0 = Release|Win32 + {1DE5F13E-88E1-4AE6-A063-C35A5472D3FF}.Debug|x64.ActiveCfg = Debug|x64 + {1DE5F13E-88E1-4AE6-A063-C35A5472D3FF}.Debug|x64.Build.0 = Debug|x64 + {1DE5F13E-88E1-4AE6-A063-C35A5472D3FF}.Release|x64.ActiveCfg = Release|x64 + {1DE5F13E-88E1-4AE6-A063-C35A5472D3FF}.Release|x64.Build.0 = Release|x64 + {4D1B1905-50F1-4C12-9754-E8AF3E69B87D}.Debug|Win32.ActiveCfg = Debug|Win32 + {4D1B1905-50F1-4C12-9754-E8AF3E69B87D}.Debug|Win32.Build.0 = Debug|Win32 + {4D1B1905-50F1-4C12-9754-E8AF3E69B87D}.Release|Win32.ActiveCfg = Release|Win32 + {4D1B1905-50F1-4C12-9754-E8AF3E69B87D}.Release|Win32.Build.0 = Release|Win32 + {4D1B1905-50F1-4C12-9754-E8AF3E69B87D}.Debug|x64.ActiveCfg = Debug|x64 + {4D1B1905-50F1-4C12-9754-E8AF3E69B87D}.Debug|x64.Build.0 = Debug|x64 + {4D1B1905-50F1-4C12-9754-E8AF3E69B87D}.Release|x64.ActiveCfg = Release|x64 + {4D1B1905-50F1-4C12-9754-E8AF3E69B87D}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {116683DC-66A4-4386-93D3-32A935D96B37} = {78FB30BE-39D1-4BE2-A805-515DFEC13B00} - {235EA7C6-B4BC-4762-B7C6-F519089D31B8} = {12DE6B87-7F07-4817-98D9-7F9CB6CDB946} + {1DE5F13E-88E1-4AE6-A063-C35A5472D3FF} = {96B4D999-7E79-4250-A5D9-E18315D8404E} + {4D1B1905-50F1-4C12-9754-E8AF3E69B87D} = {839113C4-BEC1-4845-AE0C-A753C2EC3C2D} EndGlobalSection EndGlobal diff --git a/general/PLX9x5x/sys/Init.c b/general/PLX9x5x/sys/Init.c index b18c80c68..fbedaa0c4 100644 --- a/general/PLX9x5x/sys/Init.c +++ b/general/PLX9x5x/sys/Init.c @@ -59,6 +59,12 @@ PVOID LocalMmMapIoSpace( PAGE_READWRITE | PAGE_NOCACHE); } + // + // Supress warning that MmMapIoSpace allocates executable memory. + // This function is only used if the preferred API, MmMapIoSpaceEx + // is not present. MmMapIoSpaceEx is available starting in WIN10. + // + #pragma warning(suppress: 30029) return MmMapIoSpace(PhysicalAddress, NumberOfBytes, MmNonCached); } diff --git a/general/PLX9x5x/sys/Pci9x5x.vcxproj b/general/PLX9x5x/sys/Pci9x5x.vcxproj index 8922ce83e..8ecc182b6 100644 --- a/general/PLX9x5x/sys/Pci9x5x.vcxproj +++ b/general/PLX9x5x/sys/Pci9x5x.vcxproj @@ -19,13 +19,13 @@ - {116683DC-66A4-4386-93D3-32A935D96B37} + {1DE5F13E-88E1-4AE6-A063-C35A5472D3FF} $(MSBuildProjectName) 1 9 Debug Win32 - {BE8B0450-C794-43FB-8906-0F2F924845CC} + {1D35F3A3-A9F3-4F69-BFDD-0D12D5AED768} @@ -207,7 +207,6 @@ - diff --git a/general/PLX9x5x/sys/Pci9x5x.vcxproj.Filters b/general/PLX9x5x/sys/Pci9x5x.vcxproj.Filters index ece7bbbf1..571879e30 100644 --- a/general/PLX9x5x/sys/Pci9x5x.vcxproj.Filters +++ b/general/PLX9x5x/sys/Pci9x5x.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {FB26A925-4DA1-470E-9D80-9D5F544E0DDD} + {B669D948-3401-4ED3-A372-17F5ACB06C88} h;hpp;hxx;hm;inl;inc;xsd - {140FC8D5-2FBF-4CA2-8DE1-1ABF15927EF7} + {6BF69C07-3C57-4B3E-B476-2FE25863C4E7} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {2C3C95FB-9EC1-4B9A-B35E-A8534768915B} + {C8335578-FDE2-4937-A4B4-983F51CC74A2} inf;inv;inx;mof;mc; - {D48F873E-36BD-45C1-A62F-4D9C8745F283} + {CE1E380F-0E2C-4AB0-A135-46775B8C4371} @@ -39,9 +39,6 @@ - - Driver Files - Driver Files diff --git a/general/PLX9x5x/sys/pci9x5x.inx b/general/PLX9x5x/sys/pci9x5x.inx index 43bd8dec4..8fc7def6d 100644 --- a/general/PLX9x5x/sys/pci9x5x.inx +++ b/general/PLX9x5x/sys/pci9x5x.inx @@ -20,7 +20,7 @@ Signature="$WINDOWS NT$" Class=Sample ClassGuid={78A1C341-4539-11d3-B88D-00C04FAD5171} -Provider=%MSFT% +Provider=%ProviderString% DriverVer=03/20/2003,5.00.3788 CatalogFile=KmdfSamples.cat @@ -45,7 +45,7 @@ HKR,,Security,,"D:P(A;;GA;;;SY)(A;;GA;;;BA)" ;Allow generic all access to system ExcludeFromSelect=* [Manufacturer] -%MSFT%=MSFT,NT$ARCH$ +%ManufacturerString%=MSFT,NT$ARCH$ [SourceDisksFiles] Pci9x5x.sys=1 @@ -82,7 +82,8 @@ KmdfService = Pci9x5x, Pci9x5x_wdfsect KmdfLibraryVersion = $KMDFVERSION$ [Strings] -MSFT = "Microsoft" +ProviderString = "TODO-Set-Provider" +ManufacturerString = "TODO-Set-Manufacturer" ClassName = "Sample Device" Pci9x5x.SVCDESC = "Sample Driver Service for the PCI9x5xRDK-Lite adapter" Pci9056.DRVDESC = "Sample Driver for the PCI9056RDK-Lite adapter" diff --git a/general/PLX9x5x/test/plx.vcxproj b/general/PLX9x5x/test/plx.vcxproj index 8634bd39d..f5aee4981 100644 --- a/general/PLX9x5x/test/plx.vcxproj +++ b/general/PLX9x5x/test/plx.vcxproj @@ -19,11 +19,11 @@ - {235EA7C6-B4BC-4762-B7C6-F519089D31B8} + {D12B451F-9593-4418-8E9D-3876568F0735} $(MSBuildProjectName) Debug Win32 - {8C1C6BDF-C1D1-4007-A1EB-5D3354422309} + {14470144-508A-4DC0-A4FB-05B235920C7D} @@ -170,7 +170,6 @@ - diff --git a/general/PLX9x5x/test/plx.vcxproj.Filters b/general/PLX9x5x/test/plx.vcxproj.Filters index daeb5b79a..836927a3a 100644 --- a/general/PLX9x5x/test/plx.vcxproj.Filters +++ b/general/PLX9x5x/test/plx.vcxproj.Filters @@ -3,15 +3,15 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {6D507CB2-9D2C-4ACA-9A1B-E26E912641A7} + {000E7888-9A7D-4186-A5B1-CD11AABC5214} h;hpp;hxx;hm;inl;inc;xsd - {A2D15E88-BFAF-4AC4-90C6-D28A212C15E6} + {C8A54ADD-C416-4801-951A-201A6C356E22} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {063653C5-4B37-4C7C-BB1B-92BFD43D344F} + {A98EC5F5-DB3F-4CFD-8487-F6901096E6D6} diff --git a/general/SystemDma/wdm/SystemDma.sln b/general/SystemDma/wdm/SystemDma.sln index baa7294c9..6a27f1f44 100644 --- a/general/SystemDma/wdm/SystemDma.sln +++ b/general/SystemDma/wdm/SystemDma.sln @@ -3,13 +3,13 @@ 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", "{5C97DEF5-8A33-4405-AC60-96F0EC22ABEC}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Exe", "Exe", "{40D8A4EB-CEC0-4AC2-BFB4-38ED5242C772}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sys", "Sys", "{07DEA55E-F7CF-45F9-B169-834815EC1AFD}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sys", "Sys", "{9444CA2A-D65A-4EE0-A962-274E2F3CF233}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SystemDmaApp", "exe\SystemDmaApp.vcxproj", "{23E9BB4A-11FD-4BC6-9CA7-0BE6A4188C1F}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SystemDmaApp", "exe\SystemDmaApp.vcxproj", "{442B1B6F-AFAA-45A4-BF7E-2085841BBCAE}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SDma", "sys\SDma.vcxproj", "{3FE750B5-4CAD-4ECD-B443-12E16404FB11}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SDma", "sys\SDma.vcxproj", "{3985BC4B-064D-4593-A898-9C73AE59F11C}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -19,28 +19,28 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {23E9BB4A-11FD-4BC6-9CA7-0BE6A4188C1F}.Debug|Win32.ActiveCfg = Debug|Win32 - {23E9BB4A-11FD-4BC6-9CA7-0BE6A4188C1F}.Debug|Win32.Build.0 = Debug|Win32 - {23E9BB4A-11FD-4BC6-9CA7-0BE6A4188C1F}.Release|Win32.ActiveCfg = Release|Win32 - {23E9BB4A-11FD-4BC6-9CA7-0BE6A4188C1F}.Release|Win32.Build.0 = Release|Win32 - {23E9BB4A-11FD-4BC6-9CA7-0BE6A4188C1F}.Debug|x64.ActiveCfg = Debug|x64 - {23E9BB4A-11FD-4BC6-9CA7-0BE6A4188C1F}.Debug|x64.Build.0 = Debug|x64 - {23E9BB4A-11FD-4BC6-9CA7-0BE6A4188C1F}.Release|x64.ActiveCfg = Release|x64 - {23E9BB4A-11FD-4BC6-9CA7-0BE6A4188C1F}.Release|x64.Build.0 = Release|x64 - {3FE750B5-4CAD-4ECD-B443-12E16404FB11}.Debug|Win32.ActiveCfg = Debug|Win32 - {3FE750B5-4CAD-4ECD-B443-12E16404FB11}.Debug|Win32.Build.0 = Debug|Win32 - {3FE750B5-4CAD-4ECD-B443-12E16404FB11}.Release|Win32.ActiveCfg = Release|Win32 - {3FE750B5-4CAD-4ECD-B443-12E16404FB11}.Release|Win32.Build.0 = Release|Win32 - {3FE750B5-4CAD-4ECD-B443-12E16404FB11}.Debug|x64.ActiveCfg = Debug|x64 - {3FE750B5-4CAD-4ECD-B443-12E16404FB11}.Debug|x64.Build.0 = Debug|x64 - {3FE750B5-4CAD-4ECD-B443-12E16404FB11}.Release|x64.ActiveCfg = Release|x64 - {3FE750B5-4CAD-4ECD-B443-12E16404FB11}.Release|x64.Build.0 = Release|x64 + {442B1B6F-AFAA-45A4-BF7E-2085841BBCAE}.Debug|Win32.ActiveCfg = Debug|Win32 + {442B1B6F-AFAA-45A4-BF7E-2085841BBCAE}.Debug|Win32.Build.0 = Debug|Win32 + {442B1B6F-AFAA-45A4-BF7E-2085841BBCAE}.Release|Win32.ActiveCfg = Release|Win32 + {442B1B6F-AFAA-45A4-BF7E-2085841BBCAE}.Release|Win32.Build.0 = Release|Win32 + {442B1B6F-AFAA-45A4-BF7E-2085841BBCAE}.Debug|x64.ActiveCfg = Debug|x64 + {442B1B6F-AFAA-45A4-BF7E-2085841BBCAE}.Debug|x64.Build.0 = Debug|x64 + {442B1B6F-AFAA-45A4-BF7E-2085841BBCAE}.Release|x64.ActiveCfg = Release|x64 + {442B1B6F-AFAA-45A4-BF7E-2085841BBCAE}.Release|x64.Build.0 = Release|x64 + {3985BC4B-064D-4593-A898-9C73AE59F11C}.Debug|Win32.ActiveCfg = Debug|Win32 + {3985BC4B-064D-4593-A898-9C73AE59F11C}.Debug|Win32.Build.0 = Debug|Win32 + {3985BC4B-064D-4593-A898-9C73AE59F11C}.Release|Win32.ActiveCfg = Release|Win32 + {3985BC4B-064D-4593-A898-9C73AE59F11C}.Release|Win32.Build.0 = Release|Win32 + {3985BC4B-064D-4593-A898-9C73AE59F11C}.Debug|x64.ActiveCfg = Debug|x64 + {3985BC4B-064D-4593-A898-9C73AE59F11C}.Debug|x64.Build.0 = Debug|x64 + {3985BC4B-064D-4593-A898-9C73AE59F11C}.Release|x64.ActiveCfg = Release|x64 + {3985BC4B-064D-4593-A898-9C73AE59F11C}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {23E9BB4A-11FD-4BC6-9CA7-0BE6A4188C1F} = {5C97DEF5-8A33-4405-AC60-96F0EC22ABEC} - {3FE750B5-4CAD-4ECD-B443-12E16404FB11} = {07DEA55E-F7CF-45F9-B169-834815EC1AFD} + {442B1B6F-AFAA-45A4-BF7E-2085841BBCAE} = {40D8A4EB-CEC0-4AC2-BFB4-38ED5242C772} + {3985BC4B-064D-4593-A898-9C73AE59F11C} = {9444CA2A-D65A-4EE0-A962-274E2F3CF233} EndGlobalSection EndGlobal diff --git a/general/SystemDma/wdm/exe/SystemDmaApp.vcxproj b/general/SystemDma/wdm/exe/SystemDmaApp.vcxproj index 7f966315a..a7609715b 100644 --- a/general/SystemDma/wdm/exe/SystemDmaApp.vcxproj +++ b/general/SystemDma/wdm/exe/SystemDmaApp.vcxproj @@ -19,11 +19,11 @@ - {23E9BB4A-11FD-4BC6-9CA7-0BE6A4188C1F} + {442B1B6F-AFAA-45A4-BF7E-2085841BBCAE} $(MSBuildProjectName) Debug Win32 - {24697793-CB49-4265-8991-872727B15BC3} + {8454C72A-8659-4B42-923C-9318CEBE1D9E} @@ -182,7 +182,6 @@ - diff --git a/general/SystemDma/wdm/exe/SystemDmaApp.vcxproj.Filters b/general/SystemDma/wdm/exe/SystemDmaApp.vcxproj.Filters index 39258507e..581ec22dd 100644 --- a/general/SystemDma/wdm/exe/SystemDmaApp.vcxproj.Filters +++ b/general/SystemDma/wdm/exe/SystemDmaApp.vcxproj.Filters @@ -3,15 +3,15 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {F3AEB441-C240-4DAC-894F-32249F376A28} + {9165AF3C-7BC3-4657-8EF7-4F494D41F49D} h;hpp;hxx;hm;inl;inc;xsd - {8B83FC49-81EA-4DD1-BD8E-ED4F3858F05B} + {50BBBADC-20CA-47BE-BC07-9242E4E4BD93} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {84E5DCD8-475D-4896-90D3-9F4398A303DB} + {E382FB1B-6C4B-4558-8C22-8A69FDDE6B6F} diff --git a/general/SystemDma/wdm/sys/SDma.vcxproj b/general/SystemDma/wdm/sys/SDma.vcxproj index 35bf5531a..838042d32 100644 --- a/general/SystemDma/wdm/sys/SDma.vcxproj +++ b/general/SystemDma/wdm/sys/SDma.vcxproj @@ -19,11 +19,11 @@ - {3FE750B5-4CAD-4ECD-B443-12E16404FB11} + {3985BC4B-064D-4593-A898-9C73AE59F11C} $(MSBuildProjectName) Debug Win32 - {19364197-D291-4DC9-B08E-DD477FD42358} + {7EE6B6B0-9980-4C86-AA78-EF29D53BEF17} @@ -126,7 +126,6 @@ - diff --git a/general/SystemDma/wdm/sys/SDma.vcxproj.Filters b/general/SystemDma/wdm/sys/SDma.vcxproj.Filters index 7926613ec..116400e15 100644 --- a/general/SystemDma/wdm/sys/SDma.vcxproj.Filters +++ b/general/SystemDma/wdm/sys/SDma.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {98D22585-F401-429B-BDB2-B5C6F436A255} + {59F0B344-1374-4E15-A8B7-9C8E3213A541} h;hpp;hxx;hm;inl;inc;xsd - {0B161BA1-4402-4873-9564-A47349B20298} + {5483EB90-1466-4DB6-9E7F-3DB593CD79FB} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {B1F31E58-0465-4A38-BBB2-DF6F4EF598D8} + {315FEF3C-524B-47DA-99BF-6C5B211CE531} inf;inv;inx;mof;mc; - {5C6DF922-F9E0-48E6-B4D9-B472082A0400} + {4997B2F3-3A07-4B16-AB97-AC5CF9527734} diff --git a/general/cancel/cancel.sln b/general/cancel/cancel.sln index fe8e97577..f0c190ec1 100644 --- a/general/cancel/cancel.sln +++ b/general/cancel/cancel.sln @@ -3,17 +3,17 @@ 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", "{FF154CE0-9DFD-405E-B895-7B7705DBB3BC}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sys", "Sys", "{DEC3F1B6-5DEC-468C-B8BB-C1DC2C421850}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Exe", "Exe", "{A5E4BBE9-6842-4F12-91FD-1038D7FE8288}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Exe", "Exe", "{23E633E4-C580-4996-9178-02E7FA6E4DC5}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Startio", "Startio", "{B061A5AB-4CE5-4B99-A993-BF1AA7FD9A7A}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Startio", "Startio", "{7621E5ED-5028-4A69-9728-A44E6FF16C4B}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cancel", "sys\cancel.vcxproj", "{D9569E28-5AA8-422B-8909-3B52E3A9E0A2}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cancel", "sys\cancel.vcxproj", "{7624E1DC-F66B-40BD-961E-D847560EBD80}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "canclapp", "exe\canclapp.vcxproj", "{181B9B9A-1D10-4DF8-8864-2F2CD58AAACD}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "canclapp", "exe\canclapp.vcxproj", "{531B7E42-B149-471F-B8FC-8983CCCB2DBF}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cancel", "startio\cancel.vcxproj", "{9123CC18-82D4-477B-B20E-6EA2D454E046}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cancel", "startio\cancel.vcxproj", "{CE95297F-417A-4A1E-B842-AD545D354500}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -23,37 +23,37 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {D9569E28-5AA8-422B-8909-3B52E3A9E0A2}.Debug|Win32.ActiveCfg = Debug|Win32 - {D9569E28-5AA8-422B-8909-3B52E3A9E0A2}.Debug|Win32.Build.0 = Debug|Win32 - {D9569E28-5AA8-422B-8909-3B52E3A9E0A2}.Release|Win32.ActiveCfg = Release|Win32 - {D9569E28-5AA8-422B-8909-3B52E3A9E0A2}.Release|Win32.Build.0 = Release|Win32 - {D9569E28-5AA8-422B-8909-3B52E3A9E0A2}.Debug|x64.ActiveCfg = Debug|x64 - {D9569E28-5AA8-422B-8909-3B52E3A9E0A2}.Debug|x64.Build.0 = Debug|x64 - {D9569E28-5AA8-422B-8909-3B52E3A9E0A2}.Release|x64.ActiveCfg = Release|x64 - {D9569E28-5AA8-422B-8909-3B52E3A9E0A2}.Release|x64.Build.0 = Release|x64 - {181B9B9A-1D10-4DF8-8864-2F2CD58AAACD}.Debug|Win32.ActiveCfg = Debug|Win32 - {181B9B9A-1D10-4DF8-8864-2F2CD58AAACD}.Debug|Win32.Build.0 = Debug|Win32 - {181B9B9A-1D10-4DF8-8864-2F2CD58AAACD}.Release|Win32.ActiveCfg = Release|Win32 - {181B9B9A-1D10-4DF8-8864-2F2CD58AAACD}.Release|Win32.Build.0 = Release|Win32 - {181B9B9A-1D10-4DF8-8864-2F2CD58AAACD}.Debug|x64.ActiveCfg = Debug|x64 - {181B9B9A-1D10-4DF8-8864-2F2CD58AAACD}.Debug|x64.Build.0 = Debug|x64 - {181B9B9A-1D10-4DF8-8864-2F2CD58AAACD}.Release|x64.ActiveCfg = Release|x64 - {181B9B9A-1D10-4DF8-8864-2F2CD58AAACD}.Release|x64.Build.0 = Release|x64 - {9123CC18-82D4-477B-B20E-6EA2D454E046}.Debug|Win32.ActiveCfg = Debug|Win32 - {9123CC18-82D4-477B-B20E-6EA2D454E046}.Debug|Win32.Build.0 = Debug|Win32 - {9123CC18-82D4-477B-B20E-6EA2D454E046}.Release|Win32.ActiveCfg = Release|Win32 - {9123CC18-82D4-477B-B20E-6EA2D454E046}.Release|Win32.Build.0 = Release|Win32 - {9123CC18-82D4-477B-B20E-6EA2D454E046}.Debug|x64.ActiveCfg = Debug|x64 - {9123CC18-82D4-477B-B20E-6EA2D454E046}.Debug|x64.Build.0 = Debug|x64 - {9123CC18-82D4-477B-B20E-6EA2D454E046}.Release|x64.ActiveCfg = Release|x64 - {9123CC18-82D4-477B-B20E-6EA2D454E046}.Release|x64.Build.0 = Release|x64 + {7624E1DC-F66B-40BD-961E-D847560EBD80}.Debug|Win32.ActiveCfg = Debug|Win32 + {7624E1DC-F66B-40BD-961E-D847560EBD80}.Debug|Win32.Build.0 = Debug|Win32 + {7624E1DC-F66B-40BD-961E-D847560EBD80}.Release|Win32.ActiveCfg = Release|Win32 + {7624E1DC-F66B-40BD-961E-D847560EBD80}.Release|Win32.Build.0 = Release|Win32 + {7624E1DC-F66B-40BD-961E-D847560EBD80}.Debug|x64.ActiveCfg = Debug|x64 + {7624E1DC-F66B-40BD-961E-D847560EBD80}.Debug|x64.Build.0 = Debug|x64 + {7624E1DC-F66B-40BD-961E-D847560EBD80}.Release|x64.ActiveCfg = Release|x64 + {7624E1DC-F66B-40BD-961E-D847560EBD80}.Release|x64.Build.0 = Release|x64 + {531B7E42-B149-471F-B8FC-8983CCCB2DBF}.Debug|Win32.ActiveCfg = Debug|Win32 + {531B7E42-B149-471F-B8FC-8983CCCB2DBF}.Debug|Win32.Build.0 = Debug|Win32 + {531B7E42-B149-471F-B8FC-8983CCCB2DBF}.Release|Win32.ActiveCfg = Release|Win32 + {531B7E42-B149-471F-B8FC-8983CCCB2DBF}.Release|Win32.Build.0 = Release|Win32 + {531B7E42-B149-471F-B8FC-8983CCCB2DBF}.Debug|x64.ActiveCfg = Debug|x64 + {531B7E42-B149-471F-B8FC-8983CCCB2DBF}.Debug|x64.Build.0 = Debug|x64 + {531B7E42-B149-471F-B8FC-8983CCCB2DBF}.Release|x64.ActiveCfg = Release|x64 + {531B7E42-B149-471F-B8FC-8983CCCB2DBF}.Release|x64.Build.0 = Release|x64 + {CE95297F-417A-4A1E-B842-AD545D354500}.Debug|Win32.ActiveCfg = Debug|Win32 + {CE95297F-417A-4A1E-B842-AD545D354500}.Debug|Win32.Build.0 = Debug|Win32 + {CE95297F-417A-4A1E-B842-AD545D354500}.Release|Win32.ActiveCfg = Release|Win32 + {CE95297F-417A-4A1E-B842-AD545D354500}.Release|Win32.Build.0 = Release|Win32 + {CE95297F-417A-4A1E-B842-AD545D354500}.Debug|x64.ActiveCfg = Debug|x64 + {CE95297F-417A-4A1E-B842-AD545D354500}.Debug|x64.Build.0 = Debug|x64 + {CE95297F-417A-4A1E-B842-AD545D354500}.Release|x64.ActiveCfg = Release|x64 + {CE95297F-417A-4A1E-B842-AD545D354500}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {D9569E28-5AA8-422B-8909-3B52E3A9E0A2} = {FF154CE0-9DFD-405E-B895-7B7705DBB3BC} - {181B9B9A-1D10-4DF8-8864-2F2CD58AAACD} = {A5E4BBE9-6842-4F12-91FD-1038D7FE8288} - {9123CC18-82D4-477B-B20E-6EA2D454E046} = {B061A5AB-4CE5-4B99-A993-BF1AA7FD9A7A} + {7624E1DC-F66B-40BD-961E-D847560EBD80} = {DEC3F1B6-5DEC-468C-B8BB-C1DC2C421850} + {531B7E42-B149-471F-B8FC-8983CCCB2DBF} = {23E633E4-C580-4996-9178-02E7FA6E4DC5} + {CE95297F-417A-4A1E-B842-AD545D354500} = {7621E5ED-5028-4A69-9728-A44E6FF16C4B} EndGlobalSection EndGlobal diff --git a/general/cancel/exe/canclapp.vcxproj b/general/cancel/exe/canclapp.vcxproj index 0271461ad..9d773ba4d 100644 --- a/general/cancel/exe/canclapp.vcxproj +++ b/general/cancel/exe/canclapp.vcxproj @@ -19,11 +19,11 @@ - {181B9B9A-1D10-4DF8-8864-2F2CD58AAACD} + {531B7E42-B149-471F-B8FC-8983CCCB2DBF} $(MSBuildProjectName) Debug Win32 - {BEFC1AEC-4158-4133-B070-197A3BCB3324} + {2D283D6D-F460-4609-A38C-0C9A1C261663} @@ -166,7 +166,6 @@ - diff --git a/general/cancel/exe/canclapp.vcxproj.Filters b/general/cancel/exe/canclapp.vcxproj.Filters index 630a370d8..9fe9f9995 100644 --- a/general/cancel/exe/canclapp.vcxproj.Filters +++ b/general/cancel/exe/canclapp.vcxproj.Filters @@ -3,15 +3,15 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {A9970D5F-6A08-4685-BCB3-BC7260733555} + {0FEAE906-189A-421D-A495-02BE6BD56D5B} h;hpp;hxx;hm;inl;inc;xsd - {751D0E38-6054-404C-9749-DE882ACAACF4} + {63CE3A5E-8192-4CE8-A08A-5BB7DFA40D49} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {D2D88B2C-9B81-4181-8BF9-3F8E289E770D} + {7625AB82-3F98-4B7F-89BF-D72AD913D45B} diff --git a/general/cancel/startio/cancel.vcxproj b/general/cancel/startio/cancel.vcxproj index da4a61db9..5afc6e510 100644 --- a/general/cancel/startio/cancel.vcxproj +++ b/general/cancel/startio/cancel.vcxproj @@ -19,11 +19,11 @@ - {9123CC18-82D4-477B-B20E-6EA2D454E046} + {CE95297F-417A-4A1E-B842-AD545D354500} $(MSBuildProjectName) Debug Win32 - {64C91FEC-568F-4B05-A91E-F6B96212BA27} + {E44B3998-5BDD-49A8-8715-0C8F72742A35} @@ -138,7 +138,6 @@ - diff --git a/general/cancel/startio/cancel.vcxproj.Filters b/general/cancel/startio/cancel.vcxproj.Filters index 7184abafa..6ac2e051d 100644 --- a/general/cancel/startio/cancel.vcxproj.Filters +++ b/general/cancel/startio/cancel.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {2B25101E-8842-4A6A-8AFE-E6EE3BB048FD} + {E494A40A-3958-4EDF-B7C6-32C06EFEB65B} h;hpp;hxx;hm;inl;inc;xsd - {274A07A9-D5CD-40E9-A368-7B2405C97AAF} + {BC27F0CD-8873-4D29-B724-3C033F0BC89C} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {46F2B62E-AC82-4992-8A9F-472BA3B4912D} + {FBA31D4F-3A5E-43F1-92EC-1FD1D62F9C4C} inf;inv;inx;mof;mc; - {D0E05008-9870-4B85-88BD-6F86F9D5E37F} + {A13A12DF-D40B-4522-895F-36227156A823} diff --git a/general/cancel/sys/cancel.vcxproj b/general/cancel/sys/cancel.vcxproj index 8ec424282..81e495ef9 100644 --- a/general/cancel/sys/cancel.vcxproj +++ b/general/cancel/sys/cancel.vcxproj @@ -19,11 +19,11 @@ - {D9569E28-5AA8-422B-8909-3B52E3A9E0A2} + {7624E1DC-F66B-40BD-961E-D847560EBD80} $(MSBuildProjectName) Debug Win32 - {DA39B940-2FD0-474D-86EE-DF9796879A51} + {94C84B2A-BA1D-4247-891F-E8E06A3674DB} @@ -138,7 +138,6 @@ - diff --git a/general/cancel/sys/cancel.vcxproj.Filters b/general/cancel/sys/cancel.vcxproj.Filters index 5e7869852..db33c650d 100644 --- a/general/cancel/sys/cancel.vcxproj.Filters +++ b/general/cancel/sys/cancel.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {3711485D-9620-4878-846C-DE26288C24AA} + {76ACAF4D-8EE9-4FDC-9D42-16B803FD21C2} h;hpp;hxx;hm;inl;inc;xsd - {02DB91A6-1A11-484A-A01E-212829E426DE} + {35AC8B42-7A9B-4609-8960-B23407C28987} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {643643F8-399E-478C-9A56-C0948E4C4562} + {2353E056-A752-4437-ACB6-C9F190C6920F} inf;inv;inx;mof;mc; - {22E6AE16-CFAD-4ED0-BE35-3664BF6E578B} + {20F1A0AF-5FD4-422C-B62D-35E37135815F} diff --git a/general/echo/kmdf/ReadMe.md b/general/echo/kmdf/ReadMe.md index d1171f19d..3b89850c3 100644 --- a/general/echo/kmdf/ReadMe.md +++ b/general/echo/kmdf/ReadMe.md @@ -8,9 +8,6 @@ It also shows how to synchronize execution of these events with other asynchrono ## Universal Windows Driver Compliant This sample builds a Universal Windows Driver. It uses only APIs and DDIs that are included in OneCoreUAP. -**Note** -Due to a known bug in the current Windows Driver Kit tools, when building this sample in Debug mode, the exe project may report some ApiValidator warnings. These can be safely ignored. - Related technologies -------------------- diff --git a/general/echo/kmdf/driver/AutoSync/echo.inx b/general/echo/kmdf/driver/AutoSync/echo.inx index fa0e4f6e4..24d4370da 100644 --- a/general/echo/kmdf/driver/AutoSync/echo.inx +++ b/general/echo/kmdf/driver/AutoSync/echo.inx @@ -17,7 +17,7 @@ Signature="$WINDOWS NT$" Class=Sample ClassGuid={78A1C341-4539-11d3-B88D-00C04FAD5171} -Provider=%MSFT% +Provider=%ProviderString% DriverVer=03/20/2003,5.00.3788 CatalogFile=KmdfSamples.cat @@ -96,7 +96,7 @@ KmdfLibraryVersion = $KMDFVERSION$ [Strings] SPSVCINST_ASSOCSERVICE= 0x00000002 -MSFT = "Microsoft" +ProviderString = "TODO-Set-Provider" StdMfg = "(Standard system devices)" DiskId1 = "WDF Sample ECHO Installation Disk #1" ECHO.DeviceDesc = "Sample WDF ECHO Driver" diff --git a/general/echo/kmdf/driver/AutoSync/echo.vcxproj b/general/echo/kmdf/driver/AutoSync/echo.vcxproj index 4dd50d316..0c1b0ac0d 100644 --- a/general/echo/kmdf/driver/AutoSync/echo.vcxproj +++ b/general/echo/kmdf/driver/AutoSync/echo.vcxproj @@ -19,12 +19,12 @@ - {CDC025E9-1761-4E64-A166-BD45DE3FECDC} + {C4DF013B-7414-4D27-B108-DC70B6A8DD80} $(MSBuildProjectName) 1 Debug Win32 - {1A19D04A-789A-4E5E-B6CB-8267327D49B2} + {3A2C1022-6A0D-49CE-8375-AD5ED987BCF5} @@ -154,7 +154,6 @@ - diff --git a/general/echo/kmdf/driver/AutoSync/echo.vcxproj.Filters b/general/echo/kmdf/driver/AutoSync/echo.vcxproj.Filters index e16a90eb8..0d3f7346e 100644 --- a/general/echo/kmdf/driver/AutoSync/echo.vcxproj.Filters +++ b/general/echo/kmdf/driver/AutoSync/echo.vcxproj.Filters @@ -3,25 +3,22 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {5315796E-E2AE-40C4-88D3-37088B83F7D8} + {CAC61770-4C68-4C08-8954-C2EE9F4615B7} h;hpp;hxx;hm;inl;inc;xsd - {F294BBB0-2191-4523-82C7-31490BDD8A37} + {D8218D2B-4511-4292-A88E-8FADB0F1397E} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {00A6B984-C7C3-4E03-9616-073B2F83B7A3} + {E024B75C-350B-4462-936B-54AFDBE3B003} inf;inv;inx;mof;mc; - {0046A610-D3DC-4635-AF3C-395E2E6D2900} + {AEECFCB2-863B-4F2E-8286-C6819113B132} - - Driver Files - Driver Files diff --git a/general/echo/kmdf/driver/DriverSync/echo_2.inx b/general/echo/kmdf/driver/DriverSync/echo_2.inx index af09757fe..3cb9b163c 100644 --- a/general/echo/kmdf/driver/DriverSync/echo_2.inx +++ b/general/echo/kmdf/driver/DriverSync/echo_2.inx @@ -17,7 +17,7 @@ Signature="$WINDOWS NT$" Class=Sample ClassGuid={78A1C341-4539-11d3-B88D-00C04FAD5171} -Provider=%MSFT% +Provider=%ProviderString% DriverVer=03/20/2003,5.00.3788 CatalogFile=KmdfSamples.cat @@ -97,7 +97,7 @@ KmdfLibraryVersion = $KMDFVERSION$ [Strings] SPSVCINST_ASSOCSERVICE= 0x00000002 -MSFT = "Microsoft" +ProviderString = "TODO-Set-Provider" StdMfg = "(Standard system devices)" DiskId1 = "WDF Sample ECHO Installation Disk #1 (DriverSync)" ECHO.DeviceDesc = "Sample WDF ECHO Driver (DriverSync)" diff --git a/general/echo/kmdf/driver/DriverSync/echo_2.vcxproj b/general/echo/kmdf/driver/DriverSync/echo_2.vcxproj index 893895a10..ad731fd9a 100644 --- a/general/echo/kmdf/driver/DriverSync/echo_2.vcxproj +++ b/general/echo/kmdf/driver/DriverSync/echo_2.vcxproj @@ -19,12 +19,12 @@ - {349EBD20-0306-46A9-8921-622D7C07D20B} + {B405FDF0-4BAD-4E15-86D6-48501103E91F} $(MSBuildProjectName) 1 Debug Win32 - {76F69B56-EF56-432F-8691-1C773793EB51} + {5D9EA73C-DEA4-4E2D-A61B-27BD444A4EE7} @@ -166,7 +166,6 @@ - diff --git a/general/echo/kmdf/driver/DriverSync/echo_2.vcxproj.Filters b/general/echo/kmdf/driver/DriverSync/echo_2.vcxproj.Filters index cc1cf7dec..274afb0b6 100644 --- a/general/echo/kmdf/driver/DriverSync/echo_2.vcxproj.Filters +++ b/general/echo/kmdf/driver/DriverSync/echo_2.vcxproj.Filters @@ -3,25 +3,22 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {C1DDA8F3-CE1F-4E9F-B893-ABD8D39FA214} + {97EF35D4-90F8-4D46-979C-74DCEB007F84} h;hpp;hxx;hm;inl;inc;xsd - {9EBBB66D-9D37-402F-833E-2CCF7E34CCD5} + {7E927353-FF78-45C4-837F-EDD782088E83} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {53508F5F-9B47-4922-91FF-3A0BB6BF6D49} + {B669A8E3-E681-47BD-BF49-1FD5410AAD61} inf;inv;inx;mof;mc; - {59652897-649C-440A-B1AA-1AE8A8038E8E} + {918A2EDA-630F-4822-AF89-DFEBD8BDA328} - - Driver Files - Driver Files diff --git a/general/echo/kmdf/exe/echoapp.vcxproj b/general/echo/kmdf/exe/echoapp.vcxproj index 628211462..611109de2 100644 --- a/general/echo/kmdf/exe/echoapp.vcxproj +++ b/general/echo/kmdf/exe/echoapp.vcxproj @@ -19,11 +19,11 @@ - {84FCC75B-B284-4321-8108-140360BF56AA} + {6064ECE2-B8F6-4272-85EC-C80BD65DE700} $(MSBuildProjectName) Debug Win32 - {5D8FBCFF-DE4D-4607-9577-F4D8A83F971D} + {622CAFC6-E185-4727-952B-9B24EE9F7F80} @@ -169,7 +169,6 @@ - diff --git a/general/echo/kmdf/exe/echoapp.vcxproj.Filters b/general/echo/kmdf/exe/echoapp.vcxproj.Filters index 51d355179..16c9210f5 100644 --- a/general/echo/kmdf/exe/echoapp.vcxproj.Filters +++ b/general/echo/kmdf/exe/echoapp.vcxproj.Filters @@ -3,15 +3,15 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {66A85886-4DDE-45CB-9A3F-E641ACCDBA7E} + {F7DC8A79-8789-4F30-AAF9-84E75E74662B} h;hpp;hxx;hm;inl;inc;xsd - {A498F6A0-D3A5-4F4B-8D99-056B439FB180} + {978DA09C-AAC5-4A3C-BDD6-C06EE55BF876} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {0B82D018-961F-414C-8BBC-6D96DAEA3805} + {069F7D15-D8CC-4677-BD02-7013C88EF23B} diff --git a/general/echo/kmdf/kmdfecho.sln b/general/echo/kmdf/kmdfecho.sln index 4cc77bf9d..87d4ca91d 100644 --- a/general/echo/kmdf/kmdfecho.sln +++ b/general/echo/kmdf/kmdfecho.sln @@ -3,19 +3,19 @@ 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", "{52FB1641-AFB0-4F37-8BE9-4138A77068CB}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Exe", "Exe", "{2CB6609B-4511-4F2F-89DD-0F70D2D4978C}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AutoSync", "AutoSync", "{D05198DA-5478-4E5A-B530-5412FDB8A1DD}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AutoSync", "AutoSync", "{DA4A75C3-4783-40D0-87FF-48BE5D4C347A}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Driver", "Driver", "{4713DF3C-3B4C-4E76-9FB3-111280705220}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Driver", "Driver", "{63B83859-9E79-4DA8-8A37-A657790D1DE9}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DriverSync", "DriverSync", "{7010B108-FC52-4EC9-B504-AF41533C82A7}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DriverSync", "DriverSync", "{284392FE-8376-4F89-9793-46999E72B1CD}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "echoapp", "exe\echoapp.vcxproj", "{84FCC75B-B284-4321-8108-140360BF56AA}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "echoapp", "exe\echoapp.vcxproj", "{8E32E155-7C69-4746-A8F3-E66BBAE7FDFB}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "echo", "driver\AutoSync\echo.vcxproj", "{CDC025E9-1761-4E64-A166-BD45DE3FECDC}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "echo", "driver\AutoSync\echo.vcxproj", "{C4DF013B-7414-4D27-B108-DC70B6A8DD80}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "echo_2", "driver\DriverSync\echo_2.vcxproj", "{349EBD20-0306-46A9-8921-622D7C07D20B}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "echo_2", "driver\DriverSync\echo_2.vcxproj", "{F95F639B-5A29-4D6A-A0BF-60558789C717}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -25,39 +25,39 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {84FCC75B-B284-4321-8108-140360BF56AA}.Debug|Win32.ActiveCfg = Debug|Win32 - {84FCC75B-B284-4321-8108-140360BF56AA}.Debug|Win32.Build.0 = Debug|Win32 - {84FCC75B-B284-4321-8108-140360BF56AA}.Release|Win32.ActiveCfg = Release|Win32 - {84FCC75B-B284-4321-8108-140360BF56AA}.Release|Win32.Build.0 = Release|Win32 - {84FCC75B-B284-4321-8108-140360BF56AA}.Debug|x64.ActiveCfg = Debug|x64 - {84FCC75B-B284-4321-8108-140360BF56AA}.Debug|x64.Build.0 = Debug|x64 - {84FCC75B-B284-4321-8108-140360BF56AA}.Release|x64.ActiveCfg = Release|x64 - {84FCC75B-B284-4321-8108-140360BF56AA}.Release|x64.Build.0 = Release|x64 - {CDC025E9-1761-4E64-A166-BD45DE3FECDC}.Debug|Win32.ActiveCfg = Debug|Win32 - {CDC025E9-1761-4E64-A166-BD45DE3FECDC}.Debug|Win32.Build.0 = Debug|Win32 - {CDC025E9-1761-4E64-A166-BD45DE3FECDC}.Release|Win32.ActiveCfg = Release|Win32 - {CDC025E9-1761-4E64-A166-BD45DE3FECDC}.Release|Win32.Build.0 = Release|Win32 - {CDC025E9-1761-4E64-A166-BD45DE3FECDC}.Debug|x64.ActiveCfg = Debug|x64 - {CDC025E9-1761-4E64-A166-BD45DE3FECDC}.Debug|x64.Build.0 = Debug|x64 - {CDC025E9-1761-4E64-A166-BD45DE3FECDC}.Release|x64.ActiveCfg = Release|x64 - {CDC025E9-1761-4E64-A166-BD45DE3FECDC}.Release|x64.Build.0 = Release|x64 - {349EBD20-0306-46A9-8921-622D7C07D20B}.Debug|Win32.ActiveCfg = Debug|Win32 - {349EBD20-0306-46A9-8921-622D7C07D20B}.Debug|Win32.Build.0 = Debug|Win32 - {349EBD20-0306-46A9-8921-622D7C07D20B}.Release|Win32.ActiveCfg = Release|Win32 - {349EBD20-0306-46A9-8921-622D7C07D20B}.Release|Win32.Build.0 = Release|Win32 - {349EBD20-0306-46A9-8921-622D7C07D20B}.Debug|x64.ActiveCfg = Debug|x64 - {349EBD20-0306-46A9-8921-622D7C07D20B}.Debug|x64.Build.0 = Debug|x64 - {349EBD20-0306-46A9-8921-622D7C07D20B}.Release|x64.ActiveCfg = Release|x64 - {349EBD20-0306-46A9-8921-622D7C07D20B}.Release|x64.Build.0 = Release|x64 + {8E32E155-7C69-4746-A8F3-E66BBAE7FDFB}.Debug|Win32.ActiveCfg = Debug|Win32 + {8E32E155-7C69-4746-A8F3-E66BBAE7FDFB}.Debug|Win32.Build.0 = Debug|Win32 + {8E32E155-7C69-4746-A8F3-E66BBAE7FDFB}.Release|Win32.ActiveCfg = Release|Win32 + {8E32E155-7C69-4746-A8F3-E66BBAE7FDFB}.Release|Win32.Build.0 = Release|Win32 + {8E32E155-7C69-4746-A8F3-E66BBAE7FDFB}.Debug|x64.ActiveCfg = Debug|x64 + {8E32E155-7C69-4746-A8F3-E66BBAE7FDFB}.Debug|x64.Build.0 = Debug|x64 + {8E32E155-7C69-4746-A8F3-E66BBAE7FDFB}.Release|x64.ActiveCfg = Release|x64 + {8E32E155-7C69-4746-A8F3-E66BBAE7FDFB}.Release|x64.Build.0 = Release|x64 + {C4DF013B-7414-4D27-B108-DC70B6A8DD80}.Debug|Win32.ActiveCfg = Debug|Win32 + {C4DF013B-7414-4D27-B108-DC70B6A8DD80}.Debug|Win32.Build.0 = Debug|Win32 + {C4DF013B-7414-4D27-B108-DC70B6A8DD80}.Release|Win32.ActiveCfg = Release|Win32 + {C4DF013B-7414-4D27-B108-DC70B6A8DD80}.Release|Win32.Build.0 = Release|Win32 + {C4DF013B-7414-4D27-B108-DC70B6A8DD80}.Debug|x64.ActiveCfg = Debug|x64 + {C4DF013B-7414-4D27-B108-DC70B6A8DD80}.Debug|x64.Build.0 = Debug|x64 + {C4DF013B-7414-4D27-B108-DC70B6A8DD80}.Release|x64.ActiveCfg = Release|x64 + {C4DF013B-7414-4D27-B108-DC70B6A8DD80}.Release|x64.Build.0 = Release|x64 + {F95F639B-5A29-4D6A-A0BF-60558789C717}.Debug|Win32.ActiveCfg = Debug|Win32 + {F95F639B-5A29-4D6A-A0BF-60558789C717}.Debug|Win32.Build.0 = Debug|Win32 + {F95F639B-5A29-4D6A-A0BF-60558789C717}.Release|Win32.ActiveCfg = Release|Win32 + {F95F639B-5A29-4D6A-A0BF-60558789C717}.Release|Win32.Build.0 = Release|Win32 + {F95F639B-5A29-4D6A-A0BF-60558789C717}.Debug|x64.ActiveCfg = Debug|x64 + {F95F639B-5A29-4D6A-A0BF-60558789C717}.Debug|x64.Build.0 = Debug|x64 + {F95F639B-5A29-4D6A-A0BF-60558789C717}.Release|x64.ActiveCfg = Release|x64 + {F95F639B-5A29-4D6A-A0BF-60558789C717}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {84FCC75B-B284-4321-8108-140360BF56AA} = {52FB1641-AFB0-4F37-8BE9-4138A77068CB} - {CDC025E9-1761-4E64-A166-BD45DE3FECDC} = {D05198DA-5478-4E5A-B530-5412FDB8A1DD} - {349EBD20-0306-46A9-8921-622D7C07D20B} = {7010B108-FC52-4EC9-B504-AF41533C82A7} - {D05198DA-5478-4E5A-B530-5412FDB8A1DD} = {4713DF3C-3B4C-4E76-9FB3-111280705220} - {7010B108-FC52-4EC9-B504-AF41533C82A7} = {4713DF3C-3B4C-4E76-9FB3-111280705220} + {8E32E155-7C69-4746-A8F3-E66BBAE7FDFB} = {2CB6609B-4511-4F2F-89DD-0F70D2D4978C} + {C4DF013B-7414-4D27-B108-DC70B6A8DD80} = {DA4A75C3-4783-40D0-87FF-48BE5D4C347A} + {F95F639B-5A29-4D6A-A0BF-60558789C717} = {284392FE-8376-4F89-9793-46999E72B1CD} + {DA4A75C3-4783-40D0-87FF-48BE5D4C347A} = {63B83859-9E79-4DA8-8A37-A657790D1DE9} + {284392FE-8376-4F89-9793-46999E72B1CD} = {63B83859-9E79-4DA8-8A37-A657790D1DE9} EndGlobalSection EndGlobal diff --git a/general/echo/umdf/WUDFEchoDriver.inx b/general/echo/umdf/WUDFEchoDriver.inx index b2fb16aad..03f727eea 100644 --- a/general/echo/umdf/WUDFEchoDriver.inx +++ b/general/echo/umdf/WUDFEchoDriver.inx @@ -6,14 +6,14 @@ Signature="$WINDOWS NT$" Class=Sample ClassGuid={78A1C341-4539-11d3-B88D-00C04FAD5171} -Provider=%MSFTWUDF% +Provider=%ProviderName% CatalogFile=WUDF.cat DriverVer=03/20/2003,5.00.3788 [Manufacturer] -%MSFTWUDF%=Microsoft,NT$ARCH$ +%ManufacturerName%=Standard,NT$ARCH$ -[Microsoft.NT$ARCH$] +[Standard.NT$ARCH$] %EchoDeviceName%=Echo_Install,WUDF\Echo [ClassInstall32] @@ -80,8 +80,9 @@ WUDFEchoDriver.dll,,,0x00004000 ; COPYFLG_IN_USE_RENAME ; =================== Generic ================================== [Strings] -MSFTWUDF="Microsoft Internal (WUDF)" -MediaDescription="Microsoft WUDF Sample Driver Installation Media" +ProviderName="TODO-Set-Provider" +ManufacturerName="TODO-Set-Manufacturer" +MediaDescription="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 index 0b231b04c..8e5f45589 100644 --- a/general/echo/umdf/WUDFEchoDriver.vcxproj +++ b/general/echo/umdf/WUDFEchoDriver.vcxproj @@ -19,12 +19,12 @@ - {E9829567-BB6F-432B-A8A1-4B4971928159} + {ED527E05-B9E4-4B58-AEF4-A52994AE2829} $(MSBuildProjectName) 1 Debug Win32 - {6143696E-E68A-4B3D-87FA-ADE6B7E50DB4} + {95603758-8BCA-495F-A2B2-13FCC5FF53FD} @@ -242,7 +242,6 @@ - diff --git a/general/echo/umdf/WUDFEchoDriver.vcxproj.Filters b/general/echo/umdf/WUDFEchoDriver.vcxproj.Filters index c3a104788..86c4a54a9 100644 --- a/general/echo/umdf/WUDFEchoDriver.vcxproj.Filters +++ b/general/echo/umdf/WUDFEchoDriver.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {78D910D7-E92B-4734-A811-EE671E018AD7} + {437C2853-EB63-4965-9736-A8FDBF0928A7} h;hpp;hxx;hm;inl;inc;xsd - {D9527C10-A566-4A05-839C-005DACECC7AF} + {8B078219-E9D2-4EA5-9056-172A3917798B} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {F244522B-A2AF-46A0-BF42-64F811F1646C} + {936B986E-FAD4-44C8-8D5B-2AA3FA81ADAE} inf;inv;inx;mof;mc; - {D6D8C255-A574-4240-9F8E-EDE56A98BC63} + {66578744-D55B-452B-88ED-8F86F6BACB82} @@ -39,9 +39,6 @@ - - Driver Files - Driver Files diff --git a/general/echo/umdf/echo.sln b/general/echo/umdf/echo.sln index e749b3130..376bf8307 100644 --- a/general/echo/umdf/echo.sln +++ b/general/echo/umdf/echo.sln @@ -3,7 +3,7 @@ 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", "{E9829567-BB6F-432B-A8A1-4B4971928159}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WUDFEchoDriver", "WUDFEchoDriver.vcxproj", "{ED527E05-B9E4-4B58-AEF4-A52994AE2829}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {E9829567-BB6F-432B-A8A1-4B4971928159}.Debug|Win32.ActiveCfg = Debug|Win32 - {E9829567-BB6F-432B-A8A1-4B4971928159}.Debug|Win32.Build.0 = Debug|Win32 - {E9829567-BB6F-432B-A8A1-4B4971928159}.Release|Win32.ActiveCfg = Release|Win32 - {E9829567-BB6F-432B-A8A1-4B4971928159}.Release|Win32.Build.0 = Release|Win32 - {E9829567-BB6F-432B-A8A1-4B4971928159}.Debug|x64.ActiveCfg = Debug|x64 - {E9829567-BB6F-432B-A8A1-4B4971928159}.Debug|x64.Build.0 = Debug|x64 - {E9829567-BB6F-432B-A8A1-4B4971928159}.Release|x64.ActiveCfg = Release|x64 - {E9829567-BB6F-432B-A8A1-4B4971928159}.Release|x64.Build.0 = Release|x64 + {ED527E05-B9E4-4B58-AEF4-A52994AE2829}.Debug|Win32.ActiveCfg = Debug|Win32 + {ED527E05-B9E4-4B58-AEF4-A52994AE2829}.Debug|Win32.Build.0 = Debug|Win32 + {ED527E05-B9E4-4B58-AEF4-A52994AE2829}.Release|Win32.ActiveCfg = Release|Win32 + {ED527E05-B9E4-4B58-AEF4-A52994AE2829}.Release|Win32.Build.0 = Release|Win32 + {ED527E05-B9E4-4B58-AEF4-A52994AE2829}.Debug|x64.ActiveCfg = Debug|x64 + {ED527E05-B9E4-4B58-AEF4-A52994AE2829}.Debug|x64.Build.0 = Debug|x64 + {ED527E05-B9E4-4B58-AEF4-A52994AE2829}.Release|x64.ActiveCfg = Release|x64 + {ED527E05-B9E4-4B58-AEF4-A52994AE2829}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/general/echo/umdf2/ReadMe.md b/general/echo/umdf2/ReadMe.md index adc0a6cff..3229f61b5 100644 --- a/general/echo/umdf2/ReadMe.md +++ b/general/echo/umdf2/ReadMe.md @@ -8,10 +8,6 @@ It also shows how to synchronize execution of these events with other asynchrono ## Universal Windows Driver Compliant This sample builds a Universal Windows Driver. It uses only APIs and DDIs that are included in OneCoreUAP. -**Note** -Due to a known bug in the current Windows Driver Kit tools, when building this sample in Debug mode, the exe project may report some ApiValidator warnings. These can be safely ignored. - - Related technologies -------------------- diff --git a/general/echo/umdf2/driver/AutoSync/echo.vcxproj b/general/echo/umdf2/driver/AutoSync/echo.vcxproj index 1a42c503b..93fc544bf 100644 --- a/general/echo/umdf2/driver/AutoSync/echo.vcxproj +++ b/general/echo/umdf2/driver/AutoSync/echo.vcxproj @@ -19,12 +19,12 @@ - {7B51533F-3B85-437B-843B-FEC865C81376} + {407818D1-E32A-406A-9023-D0413C0733E4} $(MSBuildProjectName) 2 Debug Win32 - {7014B7B7-69CF-441E-9264-AD0DBCCA6690} + {A6CA93A2-E3F5-40E0-A4AE-4930BB05BF0F} @@ -166,7 +166,6 @@ - diff --git a/general/echo/umdf2/driver/AutoSync/echo.vcxproj.Filters b/general/echo/umdf2/driver/AutoSync/echo.vcxproj.Filters index be4e54d63..55d8a04f4 100644 --- a/general/echo/umdf2/driver/AutoSync/echo.vcxproj.Filters +++ b/general/echo/umdf2/driver/AutoSync/echo.vcxproj.Filters @@ -3,25 +3,22 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {9B3693A5-EB57-4FA2-8C1B-F85253695D04} + {58D2BB85-1D40-4837-A558-C21C3266B208} h;hpp;hxx;hm;inl;inc;xsd - {E32C0D43-4943-449E-82C0-2596D5D3BCEE} + {6036C97F-A229-4B81-B07B-34A70C91F167} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {ADEC5939-ED9C-4182-879E-AD54FFCB84ED} + {8D6CC9F5-9AC8-45DD-8987-861FD69E167C} inf;inv;inx;mof;mc; - {216463FB-3A2F-40C2-A52E-263D862032D6} + {6FC299B3-583D-4209-A259-CF65F4D02C10} - - Driver Files - Driver Files diff --git a/general/echo/umdf2/driver/AutoSync/echoum.inx b/general/echo/umdf2/driver/AutoSync/echoum.inx index fa5d29a9b..87db7cad1 100644 --- a/general/echo/umdf2/driver/AutoSync/echoum.inx +++ b/general/echo/umdf2/driver/AutoSync/echoum.inx @@ -17,7 +17,7 @@ Signature="$WINDOWS NT$" Class=Sample ClassGuid={78A1C341-4539-11d3-B88D-00C04FAD5171} -Provider=%MSFT% +Provider=%ProviderString% DriverVer=03/20/2003,5.00.3788 CatalogFile=wudf.cat @@ -81,7 +81,7 @@ UmdfLibraryVersion=$UMDFVERSION$ ServiceBinary=%12%\UMDF\echo.dll [Strings] -MSFT = "Microsoft" +ProviderString = "TODO-Set-Provider" StdMfg = "(Standard system devices)" DiskId1 = "WDF Sample ECHO Installation Disk #1" ECHO.DeviceDesc = "Sample UMDF v2 ECHO Driver" diff --git a/general/echo/umdf2/exe/echoapp.vcxproj b/general/echo/umdf2/exe/echoapp.vcxproj index 213947f1e..120c89bf9 100644 --- a/general/echo/umdf2/exe/echoapp.vcxproj +++ b/general/echo/umdf2/exe/echoapp.vcxproj @@ -19,11 +19,11 @@ - {312B4737-78DE-40B0-AEFB-D20A133B2A99} + {4527D01F-6A3D-4E3C-95BE-866DA38F0A0A} $(MSBuildProjectName) Debug Win32 - {1AEE3F20-6611-4775-ACF4-2871153EE1BF} + {56E9CA0F-2FCF-4CC0-83D7-8179527F8288} @@ -169,7 +169,6 @@ - diff --git a/general/echo/umdf2/exe/echoapp.vcxproj.Filters b/general/echo/umdf2/exe/echoapp.vcxproj.Filters index 8e64dba6c..e502be53e 100644 --- a/general/echo/umdf2/exe/echoapp.vcxproj.Filters +++ b/general/echo/umdf2/exe/echoapp.vcxproj.Filters @@ -3,15 +3,15 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {E47A65CC-C272-4052-B8E3-DFA70BFA5CE8} + {8314F6CA-5E08-4F38-A79C-490CF0B1921E} h;hpp;hxx;hm;inl;inc;xsd - {49E408BF-7F56-41B9-BC23-1B36BFACD38A} + {B852C8A0-1F3F-4216-BE3B-027A9FCEA14C} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {4C476049-FA54-4F33-86B7-650A8CCB4500} + {32AE256A-842E-4EC3-B190-09460CD4FA0A} diff --git a/general/echo/umdf2/umdf2echo.sln b/general/echo/umdf2/umdf2echo.sln index 2e4f7d204..71f8fa759 100644 --- a/general/echo/umdf2/umdf2echo.sln +++ b/general/echo/umdf2/umdf2echo.sln @@ -3,15 +3,15 @@ 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", "{E72CA533-5276-42E4-B2B2-B64188523BD4}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Exe", "Exe", "{E49BA847-A88E-4272-BC2D-D7ECB6937401}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AutoSync", "AutoSync", "{F31A130C-2441-4FE4-95D6-BB7071D6D69A}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AutoSync", "AutoSync", "{5AC4DDBC-AC4B-41E3-AE1A-21DC1570A05E}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Driver", "Driver", "{86AE3BF7-2160-454A-90FB-454DAF617943}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Driver", "Driver", "{48DED557-0B57-44D5-A364-F76A9BD5D83B}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "echoapp", "exe\echoapp.vcxproj", "{312B4737-78DE-40B0-AEFB-D20A133B2A99}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "echoapp", "exe\echoapp.vcxproj", "{FFCCCB8B-74AB-4C85-9200-B33171AAF119}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "echo", "driver\AutoSync\echo.vcxproj", "{7B51533F-3B85-437B-843B-FEC865C81376}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "echo", "driver\AutoSync\echo.vcxproj", "{407818D1-E32A-406A-9023-D0413C0733E4}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -21,29 +21,29 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {312B4737-78DE-40B0-AEFB-D20A133B2A99}.Debug|Win32.ActiveCfg = Debug|Win32 - {312B4737-78DE-40B0-AEFB-D20A133B2A99}.Debug|Win32.Build.0 = Debug|Win32 - {312B4737-78DE-40B0-AEFB-D20A133B2A99}.Release|Win32.ActiveCfg = Release|Win32 - {312B4737-78DE-40B0-AEFB-D20A133B2A99}.Release|Win32.Build.0 = Release|Win32 - {312B4737-78DE-40B0-AEFB-D20A133B2A99}.Debug|x64.ActiveCfg = Debug|x64 - {312B4737-78DE-40B0-AEFB-D20A133B2A99}.Debug|x64.Build.0 = Debug|x64 - {312B4737-78DE-40B0-AEFB-D20A133B2A99}.Release|x64.ActiveCfg = Release|x64 - {312B4737-78DE-40B0-AEFB-D20A133B2A99}.Release|x64.Build.0 = Release|x64 - {7B51533F-3B85-437B-843B-FEC865C81376}.Debug|Win32.ActiveCfg = Debug|Win32 - {7B51533F-3B85-437B-843B-FEC865C81376}.Debug|Win32.Build.0 = Debug|Win32 - {7B51533F-3B85-437B-843B-FEC865C81376}.Release|Win32.ActiveCfg = Release|Win32 - {7B51533F-3B85-437B-843B-FEC865C81376}.Release|Win32.Build.0 = Release|Win32 - {7B51533F-3B85-437B-843B-FEC865C81376}.Debug|x64.ActiveCfg = Debug|x64 - {7B51533F-3B85-437B-843B-FEC865C81376}.Debug|x64.Build.0 = Debug|x64 - {7B51533F-3B85-437B-843B-FEC865C81376}.Release|x64.ActiveCfg = Release|x64 - {7B51533F-3B85-437B-843B-FEC865C81376}.Release|x64.Build.0 = Release|x64 + {FFCCCB8B-74AB-4C85-9200-B33171AAF119}.Debug|Win32.ActiveCfg = Debug|Win32 + {FFCCCB8B-74AB-4C85-9200-B33171AAF119}.Debug|Win32.Build.0 = Debug|Win32 + {FFCCCB8B-74AB-4C85-9200-B33171AAF119}.Release|Win32.ActiveCfg = Release|Win32 + {FFCCCB8B-74AB-4C85-9200-B33171AAF119}.Release|Win32.Build.0 = Release|Win32 + {FFCCCB8B-74AB-4C85-9200-B33171AAF119}.Debug|x64.ActiveCfg = Debug|x64 + {FFCCCB8B-74AB-4C85-9200-B33171AAF119}.Debug|x64.Build.0 = Debug|x64 + {FFCCCB8B-74AB-4C85-9200-B33171AAF119}.Release|x64.ActiveCfg = Release|x64 + {FFCCCB8B-74AB-4C85-9200-B33171AAF119}.Release|x64.Build.0 = Release|x64 + {407818D1-E32A-406A-9023-D0413C0733E4}.Debug|Win32.ActiveCfg = Debug|Win32 + {407818D1-E32A-406A-9023-D0413C0733E4}.Debug|Win32.Build.0 = Debug|Win32 + {407818D1-E32A-406A-9023-D0413C0733E4}.Release|Win32.ActiveCfg = Release|Win32 + {407818D1-E32A-406A-9023-D0413C0733E4}.Release|Win32.Build.0 = Release|Win32 + {407818D1-E32A-406A-9023-D0413C0733E4}.Debug|x64.ActiveCfg = Debug|x64 + {407818D1-E32A-406A-9023-D0413C0733E4}.Debug|x64.Build.0 = Debug|x64 + {407818D1-E32A-406A-9023-D0413C0733E4}.Release|x64.ActiveCfg = Release|x64 + {407818D1-E32A-406A-9023-D0413C0733E4}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {312B4737-78DE-40B0-AEFB-D20A133B2A99} = {E72CA533-5276-42E4-B2B2-B64188523BD4} - {7B51533F-3B85-437B-843B-FEC865C81376} = {F31A130C-2441-4FE4-95D6-BB7071D6D69A} - {F31A130C-2441-4FE4-95D6-BB7071D6D69A} = {86AE3BF7-2160-454A-90FB-454DAF617943} + {FFCCCB8B-74AB-4C85-9200-B33171AAF119} = {E49BA847-A88E-4272-BC2D-D7ECB6937401} + {407818D1-E32A-406A-9023-D0413C0733E4} = {5AC4DDBC-AC4B-41E3-AE1A-21DC1570A05E} + {5AC4DDBC-AC4B-41E3-AE1A-21DC1570A05E} = {48DED557-0B57-44D5-A364-F76A9BD5D83B} EndGlobalSection EndGlobal diff --git a/general/echo/umdfSocketEcho/Driver/SocketEcho.inx b/general/echo/umdfSocketEcho/Driver/SocketEcho.inx index d9ed27ea6..c7150d7ce 100644 --- a/general/echo/umdfSocketEcho/Driver/SocketEcho.inx +++ b/general/echo/umdfSocketEcho/Driver/SocketEcho.inx @@ -6,12 +6,12 @@ Signature="$WINDOWS NT$" Class=Sample ClassGuid={78A1C341-4539-11d3-B88D-00C04FAD5171} -Provider=%MSFT% +Provider=%ProviderString% CatalogFile=wudf.cat DriverVer=03/20/2003,5.00.3788 [Manufacturer] -%MSFTWUDF%=Microsoft,NT$ARCH$ +%ManufacturerString%=Microsoft,NT$ARCH$ [Microsoft.NT$ARCH$] %SocketEchoName%=SocketEcho_Install,WUDF\SocketEcho @@ -82,8 +82,8 @@ SocketEcho.dll,,,0x00004000 ; COPYFLG_IN_USE_RENAME ; =================== Generic ================================== [Strings] -MSFT="Microsoft" -MSFTWUDF="Microsoft Internal (WUDF)" +ProviderString="TODO-Set-Provider" +ManufacturerString="TODO-Set-Manufacturer" MediaDescription="Microsoft WUDF Sample Driver Installation Media" ClassName="Sample Device" SocketEchoName="Sample WUDF SocketEcho Driver" diff --git a/general/echo/umdfSocketEcho/Driver/SocketEcho.vcxproj b/general/echo/umdfSocketEcho/Driver/SocketEcho.vcxproj index d46fb4f57..cfe950b08 100644 --- a/general/echo/umdfSocketEcho/Driver/SocketEcho.vcxproj +++ b/general/echo/umdfSocketEcho/Driver/SocketEcho.vcxproj @@ -19,13 +19,13 @@ - {A6553663-4D94-47EA-A6D7-EF2A81C8FB48} + {E10361E8-3F60-4FA4-9E72-CA0703AF2D4B} $(MSBuildProjectName) 1 1 Debug Win32 - {7817F01F-7524-4D2D-A443-8DDE220A0F02} + {57A14DB7-700E-4B8F-BB83-0476F61B04AB} @@ -231,7 +231,6 @@ - diff --git a/general/echo/umdfSocketEcho/Driver/SocketEcho.vcxproj.Filters b/general/echo/umdfSocketEcho/Driver/SocketEcho.vcxproj.Filters index 0cfba6a86..7406bc8a4 100644 --- a/general/echo/umdfSocketEcho/Driver/SocketEcho.vcxproj.Filters +++ b/general/echo/umdfSocketEcho/Driver/SocketEcho.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {ED119046-004F-4EA8-88D2-ED4E43A285D2} + {4634173A-AFBA-4CB0-A38B-3039057FE4C2} h;hpp;hxx;hm;inl;inc;xsd - {81E40625-7071-4159-B4B0-94DC1ECBC3F8} + {5C46A0BC-B376-47CD-99FF-19195273DC2E} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {BF6D7038-8154-4206-84CC-15F05F014BCC} + {368D33EB-7143-4726-935B-5B0A0F2C042A} inf;inv;inx;mof;mc; - {8574D23A-10D4-4701-885F-95D7059BCA72} + {3B3E9AC4-246E-4375-BAAE-6F6938AB5BC9} @@ -39,9 +39,6 @@ - - Driver Files - Driver Files diff --git a/general/echo/umdfSocketEcho/Exe/socketechoserver.vcxproj b/general/echo/umdfSocketEcho/Exe/socketechoserver.vcxproj index 4017bff4a..e83fa9f9f 100644 --- a/general/echo/umdfSocketEcho/Exe/socketechoserver.vcxproj +++ b/general/echo/umdfSocketEcho/Exe/socketechoserver.vcxproj @@ -19,11 +19,11 @@ - {6230EF04-CC9D-47CA-ACB4-90D68F65BC09} + {B832D7D3-C7DE-4E44-8812-FDB1991E503D} $(MSBuildProjectName) Debug Win32 - {CCBB9F0B-704E-4DDF-BDB4-ED2B2EB0330A} + {B28D7831-D20D-4451-9D56-95D026F4AF7F} @@ -165,7 +165,6 @@ - diff --git a/general/echo/umdfSocketEcho/Exe/socketechoserver.vcxproj.Filters b/general/echo/umdfSocketEcho/Exe/socketechoserver.vcxproj.Filters index b75a0709d..71857f2a9 100644 --- a/general/echo/umdfSocketEcho/Exe/socketechoserver.vcxproj.Filters +++ b/general/echo/umdfSocketEcho/Exe/socketechoserver.vcxproj.Filters @@ -3,15 +3,15 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {B299C82C-4620-4309-92E4-590D9694D4C7} + {6E35CF09-8803-4962-92AA-CEDE880104E3} h;hpp;hxx;hm;inl;inc;xsd - {E30D2942-24C0-4E0F-9BE4-069374516060} + {CC742E22-4156-475F-8A37-A96412F8D67C} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {7F4BBF43-DCE0-4376-A01F-F47446EFE8B0} + {6334BFC0-C39E-48DC-95E5-A3B1997E3C9A} diff --git a/general/echo/umdfSocketEcho/umdfsocketecho.sln b/general/echo/umdfSocketEcho/umdfsocketecho.sln index ff9d14cf7..96eb22b61 100644 --- a/general/echo/umdfSocketEcho/umdfsocketecho.sln +++ b/general/echo/umdfSocketEcho/umdfsocketecho.sln @@ -3,13 +3,13 @@ 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", "{2CC915B5-4289-4A00-9330-BC299F498C6B}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Driver", "Driver", "{A07EE392-4980-445C-B7D5-62A433133943}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Exe", "Exe", "{326CD5AE-45A8-4C81-BA8C-42FF5CAE00D8}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Exe", "Exe", "{8B570B91-8F7B-4FC0-B8D7-C169B3B9F3A0}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SocketEcho", "Driver\SocketEcho.vcxproj", "{A6553663-4D94-47EA-A6D7-EF2A81C8FB48}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SocketEcho", "Driver\SocketEcho.vcxproj", "{E10361E8-3F60-4FA4-9E72-CA0703AF2D4B}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "socketechoserver", "Exe\socketechoserver.vcxproj", "{6230EF04-CC9D-47CA-ACB4-90D68F65BC09}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "socketechoserver", "Exe\socketechoserver.vcxproj", "{B5998C0F-42A9-48E6-9CE5-60E65FF79DE3}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -19,28 +19,28 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {A6553663-4D94-47EA-A6D7-EF2A81C8FB48}.Debug|Win32.ActiveCfg = Debug|Win32 - {A6553663-4D94-47EA-A6D7-EF2A81C8FB48}.Debug|Win32.Build.0 = Debug|Win32 - {A6553663-4D94-47EA-A6D7-EF2A81C8FB48}.Release|Win32.ActiveCfg = Release|Win32 - {A6553663-4D94-47EA-A6D7-EF2A81C8FB48}.Release|Win32.Build.0 = Release|Win32 - {A6553663-4D94-47EA-A6D7-EF2A81C8FB48}.Debug|x64.ActiveCfg = Debug|x64 - {A6553663-4D94-47EA-A6D7-EF2A81C8FB48}.Debug|x64.Build.0 = Debug|x64 - {A6553663-4D94-47EA-A6D7-EF2A81C8FB48}.Release|x64.ActiveCfg = Release|x64 - {A6553663-4D94-47EA-A6D7-EF2A81C8FB48}.Release|x64.Build.0 = Release|x64 - {6230EF04-CC9D-47CA-ACB4-90D68F65BC09}.Debug|Win32.ActiveCfg = Debug|Win32 - {6230EF04-CC9D-47CA-ACB4-90D68F65BC09}.Debug|Win32.Build.0 = Debug|Win32 - {6230EF04-CC9D-47CA-ACB4-90D68F65BC09}.Release|Win32.ActiveCfg = Release|Win32 - {6230EF04-CC9D-47CA-ACB4-90D68F65BC09}.Release|Win32.Build.0 = Release|Win32 - {6230EF04-CC9D-47CA-ACB4-90D68F65BC09}.Debug|x64.ActiveCfg = Debug|x64 - {6230EF04-CC9D-47CA-ACB4-90D68F65BC09}.Debug|x64.Build.0 = Debug|x64 - {6230EF04-CC9D-47CA-ACB4-90D68F65BC09}.Release|x64.ActiveCfg = Release|x64 - {6230EF04-CC9D-47CA-ACB4-90D68F65BC09}.Release|x64.Build.0 = Release|x64 + {E10361E8-3F60-4FA4-9E72-CA0703AF2D4B}.Debug|Win32.ActiveCfg = Debug|Win32 + {E10361E8-3F60-4FA4-9E72-CA0703AF2D4B}.Debug|Win32.Build.0 = Debug|Win32 + {E10361E8-3F60-4FA4-9E72-CA0703AF2D4B}.Release|Win32.ActiveCfg = Release|Win32 + {E10361E8-3F60-4FA4-9E72-CA0703AF2D4B}.Release|Win32.Build.0 = Release|Win32 + {E10361E8-3F60-4FA4-9E72-CA0703AF2D4B}.Debug|x64.ActiveCfg = Debug|x64 + {E10361E8-3F60-4FA4-9E72-CA0703AF2D4B}.Debug|x64.Build.0 = Debug|x64 + {E10361E8-3F60-4FA4-9E72-CA0703AF2D4B}.Release|x64.ActiveCfg = Release|x64 + {E10361E8-3F60-4FA4-9E72-CA0703AF2D4B}.Release|x64.Build.0 = Release|x64 + {B5998C0F-42A9-48E6-9CE5-60E65FF79DE3}.Debug|Win32.ActiveCfg = Debug|Win32 + {B5998C0F-42A9-48E6-9CE5-60E65FF79DE3}.Debug|Win32.Build.0 = Debug|Win32 + {B5998C0F-42A9-48E6-9CE5-60E65FF79DE3}.Release|Win32.ActiveCfg = Release|Win32 + {B5998C0F-42A9-48E6-9CE5-60E65FF79DE3}.Release|Win32.Build.0 = Release|Win32 + {B5998C0F-42A9-48E6-9CE5-60E65FF79DE3}.Debug|x64.ActiveCfg = Debug|x64 + {B5998C0F-42A9-48E6-9CE5-60E65FF79DE3}.Debug|x64.Build.0 = Debug|x64 + {B5998C0F-42A9-48E6-9CE5-60E65FF79DE3}.Release|x64.ActiveCfg = Release|x64 + {B5998C0F-42A9-48E6-9CE5-60E65FF79DE3}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {A6553663-4D94-47EA-A6D7-EF2A81C8FB48} = {2CC915B5-4289-4A00-9330-BC299F498C6B} - {6230EF04-CC9D-47CA-ACB4-90D68F65BC09} = {326CD5AE-45A8-4C81-BA8C-42FF5CAE00D8} + {E10361E8-3F60-4FA4-9E72-CA0703AF2D4B} = {A07EE392-4980-445C-B7D5-62A433133943} + {B5998C0F-42A9-48E6-9CE5-60E65FF79DE3} = {8B570B91-8F7B-4FC0-B8D7-C169B3B9F3A0} EndGlobalSection EndGlobal diff --git a/general/event/eventsample.sln b/general/event/eventsample.sln index cc831cd56..fd6ce73a9 100644 --- a/general/event/eventsample.sln +++ b/general/event/eventsample.sln @@ -3,13 +3,13 @@ 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", "{69B6F5B9-DDF7-400F-BE17-476DE539E4DA}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Exe", "Exe", "{A9909D6D-8A29-406C-A968-49D288571564}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Wdm", "Wdm", "{E4DA1FA9-CAA1-4781-B138-3EA4166B922B}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Wdm", "Wdm", "{6D384D47-52EE-4445-9F2F-A5947E8D36BE}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "event", "exe\event.vcxproj", "{D5CE2F64-0EB5-420B-A867-31106AFA3731}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "event", "exe\event.vcxproj", "{A35911F0-5F6A-425B-90C8-1B6A25B0B6AA}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "event", "wdm\event.vcxproj", "{FBF7BF86-1A35-46BB-8BC6-67B2C1E431A6}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "event", "wdm\event.vcxproj", "{D93E9D2A-3306-4650-ADB5-BB6720C58FDB}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -19,28 +19,28 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {D5CE2F64-0EB5-420B-A867-31106AFA3731}.Debug|Win32.ActiveCfg = Debug|Win32 - {D5CE2F64-0EB5-420B-A867-31106AFA3731}.Debug|Win32.Build.0 = Debug|Win32 - {D5CE2F64-0EB5-420B-A867-31106AFA3731}.Release|Win32.ActiveCfg = Release|Win32 - {D5CE2F64-0EB5-420B-A867-31106AFA3731}.Release|Win32.Build.0 = Release|Win32 - {D5CE2F64-0EB5-420B-A867-31106AFA3731}.Debug|x64.ActiveCfg = Debug|x64 - {D5CE2F64-0EB5-420B-A867-31106AFA3731}.Debug|x64.Build.0 = Debug|x64 - {D5CE2F64-0EB5-420B-A867-31106AFA3731}.Release|x64.ActiveCfg = Release|x64 - {D5CE2F64-0EB5-420B-A867-31106AFA3731}.Release|x64.Build.0 = Release|x64 - {FBF7BF86-1A35-46BB-8BC6-67B2C1E431A6}.Debug|Win32.ActiveCfg = Debug|Win32 - {FBF7BF86-1A35-46BB-8BC6-67B2C1E431A6}.Debug|Win32.Build.0 = Debug|Win32 - {FBF7BF86-1A35-46BB-8BC6-67B2C1E431A6}.Release|Win32.ActiveCfg = Release|Win32 - {FBF7BF86-1A35-46BB-8BC6-67B2C1E431A6}.Release|Win32.Build.0 = Release|Win32 - {FBF7BF86-1A35-46BB-8BC6-67B2C1E431A6}.Debug|x64.ActiveCfg = Debug|x64 - {FBF7BF86-1A35-46BB-8BC6-67B2C1E431A6}.Debug|x64.Build.0 = Debug|x64 - {FBF7BF86-1A35-46BB-8BC6-67B2C1E431A6}.Release|x64.ActiveCfg = Release|x64 - {FBF7BF86-1A35-46BB-8BC6-67B2C1E431A6}.Release|x64.Build.0 = Release|x64 + {A35911F0-5F6A-425B-90C8-1B6A25B0B6AA}.Debug|Win32.ActiveCfg = Debug|Win32 + {A35911F0-5F6A-425B-90C8-1B6A25B0B6AA}.Debug|Win32.Build.0 = Debug|Win32 + {A35911F0-5F6A-425B-90C8-1B6A25B0B6AA}.Release|Win32.ActiveCfg = Release|Win32 + {A35911F0-5F6A-425B-90C8-1B6A25B0B6AA}.Release|Win32.Build.0 = Release|Win32 + {A35911F0-5F6A-425B-90C8-1B6A25B0B6AA}.Debug|x64.ActiveCfg = Debug|x64 + {A35911F0-5F6A-425B-90C8-1B6A25B0B6AA}.Debug|x64.Build.0 = Debug|x64 + {A35911F0-5F6A-425B-90C8-1B6A25B0B6AA}.Release|x64.ActiveCfg = Release|x64 + {A35911F0-5F6A-425B-90C8-1B6A25B0B6AA}.Release|x64.Build.0 = Release|x64 + {D93E9D2A-3306-4650-ADB5-BB6720C58FDB}.Debug|Win32.ActiveCfg = Debug|Win32 + {D93E9D2A-3306-4650-ADB5-BB6720C58FDB}.Debug|Win32.Build.0 = Debug|Win32 + {D93E9D2A-3306-4650-ADB5-BB6720C58FDB}.Release|Win32.ActiveCfg = Release|Win32 + {D93E9D2A-3306-4650-ADB5-BB6720C58FDB}.Release|Win32.Build.0 = Release|Win32 + {D93E9D2A-3306-4650-ADB5-BB6720C58FDB}.Debug|x64.ActiveCfg = Debug|x64 + {D93E9D2A-3306-4650-ADB5-BB6720C58FDB}.Debug|x64.Build.0 = Debug|x64 + {D93E9D2A-3306-4650-ADB5-BB6720C58FDB}.Release|x64.ActiveCfg = Release|x64 + {D93E9D2A-3306-4650-ADB5-BB6720C58FDB}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {D5CE2F64-0EB5-420B-A867-31106AFA3731} = {69B6F5B9-DDF7-400F-BE17-476DE539E4DA} - {FBF7BF86-1A35-46BB-8BC6-67B2C1E431A6} = {E4DA1FA9-CAA1-4781-B138-3EA4166B922B} + {A35911F0-5F6A-425B-90C8-1B6A25B0B6AA} = {A9909D6D-8A29-406C-A968-49D288571564} + {D93E9D2A-3306-4650-ADB5-BB6720C58FDB} = {6D384D47-52EE-4445-9F2F-A5947E8D36BE} EndGlobalSection EndGlobal diff --git a/general/event/exe/event.vcxproj b/general/event/exe/event.vcxproj index e670d8089..c766e610a 100644 --- a/general/event/exe/event.vcxproj +++ b/general/event/exe/event.vcxproj @@ -19,11 +19,11 @@ - {D5CE2F64-0EB5-420B-A867-31106AFA3731} + {A35911F0-5F6A-425B-90C8-1B6A25B0B6AA} $(MSBuildProjectName) Debug Win32 - {1DA134F1-988F-4D83-8663-343393FCF8DD} + {F9033692-F4E7-4095-BC2B-D5BADC007D30} @@ -166,7 +166,6 @@ - diff --git a/general/event/exe/event.vcxproj.Filters b/general/event/exe/event.vcxproj.Filters index 1536b3e3a..086ec75ca 100644 --- a/general/event/exe/event.vcxproj.Filters +++ b/general/event/exe/event.vcxproj.Filters @@ -3,15 +3,15 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {3F33EAAB-7921-43F5-9263-4D4F98669ACE} + {4A49E5E2-7CFB-4C09-A2FC-0DFC1BDF6F29} h;hpp;hxx;hm;inl;inc;xsd - {E071C862-9C8B-4C31-8B57-CDB34DF842FD} + {9B2A6E91-192B-4E3B-8FEB-CB07E83DC175} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {3C9792F4-01B3-4045-A0CB-4E09CE0A28AD} + {89F7E440-8EFA-40ED-869F-A9B66BAFD9CE} diff --git a/general/event/wdm/event.vcxproj b/general/event/wdm/event.vcxproj index fefc851e5..e04e27f4e 100644 --- a/general/event/wdm/event.vcxproj +++ b/general/event/wdm/event.vcxproj @@ -19,11 +19,11 @@ - {FBF7BF86-1A35-46BB-8BC6-67B2C1E431A6} + {D93E9D2A-3306-4650-ADB5-BB6720C58FDB} $(MSBuildProjectName) Debug Win32 - {060F5D78-0639-46D2-82D0-178EDF598138} + {4CDA076B-602B-41D2-B44E-C5A30A5FAEC9} @@ -142,7 +142,6 @@ - diff --git a/general/event/wdm/event.vcxproj.Filters b/general/event/wdm/event.vcxproj.Filters index f3fdde44f..51e6e7a28 100644 --- a/general/event/wdm/event.vcxproj.Filters +++ b/general/event/wdm/event.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {64E2D106-4544-49A8-91BA-6151D707621E} + {FBA431BE-C574-4FA3-A9A7-AC53BC5FA3D8} h;hpp;hxx;hm;inl;inc;xsd - {E819D815-E057-47BD-BBEB-7A4BD55F9D61} + {59F6A01C-DE89-40FA-974F-C7A4F9FE7382} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {C45F0550-45D7-404C-98DB-F9916B5185A5} + {3030B68B-29A8-42DA-97F3-9E142944B42D} inf;inv;inx;mof;mc; - {50C64871-D47E-47D2-99E0-6AF3BFC203C5} + {FE8A95D1-C7A7-4574-95F7-8A2D87F51FC9} diff --git a/general/filehistory/exe/fhsetup.vcxproj b/general/filehistory/exe/fhsetup.vcxproj index ba08d0f6a..6fcf831aa 100644 --- a/general/filehistory/exe/fhsetup.vcxproj +++ b/general/filehistory/exe/fhsetup.vcxproj @@ -19,11 +19,11 @@ - {4F3542D9-0265-46EF-AC1C-E9077754389D} + {E161CE2A-151F-4F83-B672-B7820C773F04} $(MSBuildProjectName) Debug Win32 - {7C399721-D8AC-42CB-AFD9-8A6F4507D84D} + {2A54C912-9D2E-44BC-B3FE-6C2960D95CE3} @@ -177,7 +177,6 @@ - diff --git a/general/filehistory/exe/fhsetup.vcxproj.Filters b/general/filehistory/exe/fhsetup.vcxproj.Filters index 828bbfdd0..63bf971f0 100644 --- a/general/filehistory/exe/fhsetup.vcxproj.Filters +++ b/general/filehistory/exe/fhsetup.vcxproj.Filters @@ -3,15 +3,15 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {4A29C27D-C667-4A42-8519-09FCF0137B81} + {F9F98705-F949-4652-8769-DD49818FBAA9} h;hpp;hxx;hm;inl;inc;xsd - {36854BB1-3791-4B72-BAFE-F63E471284B3} + {5CBA5790-2B5E-4F9D-BB5B-E0E080B92068} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {08ADDCDC-38B0-425F-8E1A-15E9B3A9B7D5} + {7D32D1EF-09B7-44BA-B460-66874FB0FFEB} diff --git a/general/filehistory/filehistory.sln b/general/filehistory/filehistory.sln index 1df30962c..81a5235b9 100644 --- a/general/filehistory/filehistory.sln +++ b/general/filehistory/filehistory.sln @@ -3,7 +3,7 @@ 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", "{4F3542D9-0265-46EF-AC1C-E9077754389D}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fhsetup", "exe\fhsetup.vcxproj", "{E161CE2A-151F-4F83-B672-B7820C773F04}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {4F3542D9-0265-46EF-AC1C-E9077754389D}.Debug|Win32.ActiveCfg = Debug|Win32 - {4F3542D9-0265-46EF-AC1C-E9077754389D}.Debug|Win32.Build.0 = Debug|Win32 - {4F3542D9-0265-46EF-AC1C-E9077754389D}.Release|Win32.ActiveCfg = Release|Win32 - {4F3542D9-0265-46EF-AC1C-E9077754389D}.Release|Win32.Build.0 = Release|Win32 - {4F3542D9-0265-46EF-AC1C-E9077754389D}.Debug|x64.ActiveCfg = Debug|x64 - {4F3542D9-0265-46EF-AC1C-E9077754389D}.Debug|x64.Build.0 = Debug|x64 - {4F3542D9-0265-46EF-AC1C-E9077754389D}.Release|x64.ActiveCfg = Release|x64 - {4F3542D9-0265-46EF-AC1C-E9077754389D}.Release|x64.Build.0 = Release|x64 + {E161CE2A-151F-4F83-B672-B7820C773F04}.Debug|Win32.ActiveCfg = Debug|Win32 + {E161CE2A-151F-4F83-B672-B7820C773F04}.Debug|Win32.Build.0 = Debug|Win32 + {E161CE2A-151F-4F83-B672-B7820C773F04}.Release|Win32.ActiveCfg = Release|Win32 + {E161CE2A-151F-4F83-B672-B7820C773F04}.Release|Win32.Build.0 = Release|Win32 + {E161CE2A-151F-4F83-B672-B7820C773F04}.Debug|x64.ActiveCfg = Debug|x64 + {E161CE2A-151F-4F83-B672-B7820C773F04}.Debug|x64.Build.0 = Debug|x64 + {E161CE2A-151F-4F83-B672-B7820C773F04}.Release|x64.ActiveCfg = Release|x64 + {E161CE2A-151F-4F83-B672-B7820C773F04}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/general/installwdf/InstallWdf.vcxproj b/general/installwdf/InstallWdf.vcxproj index 4629b8955..786a2115b 100644 --- a/general/installwdf/InstallWdf.vcxproj +++ b/general/installwdf/InstallWdf.vcxproj @@ -19,11 +19,11 @@ - {33B59758-52DF-4853-B3EF-10AB314C0B4F} + {3ACC106E-6FE9-4D7E-895A-1633E1CABC27} $(MSBuildProjectName) Debug Win32 - {E35D4009-C471-4212-97F4-BB773763DCBC} + {5A7DAFFE-E936-414D-A9C8-B476C90C5EC5} @@ -136,36 +136,64 @@ %(AdditionalDependencies);setupapi.lib;user32.lib + %(PreprocessorDefinitions);___TARGETNAME="""InstallWdf.PROGRAM""" + + %(PreprocessorDefinitions);___TARGETNAME="""InstallWdf.PROGRAM""" + + + %(PreprocessorDefinitions);___TARGETNAME="""InstallWdf.PROGRAM""" + %(AdditionalDependencies);setupapi.lib;user32.lib + %(PreprocessorDefinitions);___TARGETNAME="""InstallWdf.PROGRAM""" + + %(PreprocessorDefinitions);___TARGETNAME="""InstallWdf.PROGRAM""" + + + %(PreprocessorDefinitions);___TARGETNAME="""InstallWdf.PROGRAM""" + %(AdditionalDependencies);setupapi.lib;user32.lib + %(PreprocessorDefinitions);___TARGETNAME="""InstallWdf.PROGRAM""" + + %(PreprocessorDefinitions);___TARGETNAME="""InstallWdf.PROGRAM""" + + + %(PreprocessorDefinitions);___TARGETNAME="""InstallWdf.PROGRAM""" + %(AdditionalDependencies);setupapi.lib;user32.lib + %(PreprocessorDefinitions);___TARGETNAME="""InstallWdf.PROGRAM""" + + %(PreprocessorDefinitions);___TARGETNAME="""InstallWdf.PROGRAM""" + + + %(PreprocessorDefinitions);___TARGETNAME="""InstallWdf.PROGRAM""" + @@ -173,7 +201,6 @@ - diff --git a/general/installwdf/InstallWdf.vcxproj.Filters b/general/installwdf/InstallWdf.vcxproj.Filters index 876d73d9e..15cb6967b 100644 --- a/general/installwdf/InstallWdf.vcxproj.Filters +++ b/general/installwdf/InstallWdf.vcxproj.Filters @@ -3,15 +3,15 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {B14F5D40-7162-4516-827A-BDEDFCC8051D} + {5A4ACA1D-68FE-421F-8ED9-5D51A0E65913} h;hpp;hxx;hm;inl;inc;xsd - {D3DD2DE2-BCB4-4D09-8C7E-0EAE804C69ED} + {1126A1D0-DA51-4E84-B49B-3B52FDAF5EDB} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {C6E040CC-8B14-482F-8D7E-8DC793538B92} + {CB19DA30-E0FB-413E-B16A-09AC70493E34} diff --git a/general/installwdf/installwdf.sln b/general/installwdf/installwdf.sln index 9c1dc0dda..31aad1254 100644 --- a/general/installwdf/installwdf.sln +++ b/general/installwdf/installwdf.sln @@ -3,7 +3,7 @@ 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", "{33B59758-52DF-4853-B3EF-10AB314C0B4F}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "InstallWdf", "InstallWdf.vcxproj", "{3ACC106E-6FE9-4D7E-895A-1633E1CABC27}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {33B59758-52DF-4853-B3EF-10AB314C0B4F}.Debug|Win32.ActiveCfg = Debug|Win32 - {33B59758-52DF-4853-B3EF-10AB314C0B4F}.Debug|Win32.Build.0 = Debug|Win32 - {33B59758-52DF-4853-B3EF-10AB314C0B4F}.Release|Win32.ActiveCfg = Release|Win32 - {33B59758-52DF-4853-B3EF-10AB314C0B4F}.Release|Win32.Build.0 = Release|Win32 - {33B59758-52DF-4853-B3EF-10AB314C0B4F}.Debug|x64.ActiveCfg = Debug|x64 - {33B59758-52DF-4853-B3EF-10AB314C0B4F}.Debug|x64.Build.0 = Debug|x64 - {33B59758-52DF-4853-B3EF-10AB314C0B4F}.Release|x64.ActiveCfg = Release|x64 - {33B59758-52DF-4853-B3EF-10AB314C0B4F}.Release|x64.Build.0 = Release|x64 + {3ACC106E-6FE9-4D7E-895A-1633E1CABC27}.Debug|Win32.ActiveCfg = Debug|Win32 + {3ACC106E-6FE9-4D7E-895A-1633E1CABC27}.Debug|Win32.Build.0 = Debug|Win32 + {3ACC106E-6FE9-4D7E-895A-1633E1CABC27}.Release|Win32.ActiveCfg = Release|Win32 + {3ACC106E-6FE9-4D7E-895A-1633E1CABC27}.Release|Win32.Build.0 = Release|Win32 + {3ACC106E-6FE9-4D7E-895A-1633E1CABC27}.Debug|x64.ActiveCfg = Debug|x64 + {3ACC106E-6FE9-4D7E-895A-1633E1CABC27}.Debug|x64.Build.0 = Debug|x64 + {3ACC106E-6FE9-4D7E-895A-1633E1CABC27}.Release|x64.ActiveCfg = Release|x64 + {3ACC106E-6FE9-4D7E-895A-1633E1CABC27}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/general/ioctl/kmdf/ReadMe.md b/general/ioctl/kmdf/ReadMe.md new file mode 100644 index 000000000..4cdee4d49 --- /dev/null +++ b/general/ioctl/kmdf/ReadMe.md @@ -0,0 +1,67 @@ +Non-PnP Driver Sample +==================== + +This sample is primarily meant to demonstrate how to write a NON-PNP driver using the Kernel Mode Driver Framework. + +It also illustrates several other important framework interfaces. Following table gives a typical usage scenario and summarizes all the features used in this sample. + +This sample would be useful for writing a driver that does not interact with any hardware. Typically, such drivers are written to provide some kernel-level services to a user application. These drivers are dynamically loaded by the application when it is run and unloaded when it exits. + +(Examples: FileMon, Regmon, DeviceTree are examples of tools that use this type of driver.) + +- How to Write a NON PNP driver + +- How to register EvtPreProcessCallback to handle requests in the context of the calling thread + +- Show how to probe and lock buffers in the preprocess callback for METHOD\_NEITHER IOCTL requests + +- Also show how to handle other 3 types of IOCTLs (METHOD\_BUFFERED, METHOD\_IN\_DIRECT & METHOD\_OUT\_DIRECT) + +- How to open a file in Kernel-mode and Read & Write to it + +- Finally show event tracing and dumping variable length data in the tracelog using HEXDUMP format. + +The sample is accompanied by a simple multithreaded Win32 console application to test the driver. + +This sample is adapted from the original IOCTL sample present in WDK (src\\general\\ioctl). + +*Disclaimer*: 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. + + +Build the sample +---------------- + +For information on how to build a driver solution using Microsoft Visual Studio, see [Building a Driver](http://msdn.microsoft.com/en-us/library/windows/hardware/ff554644). + +If the build succeeds, you will find the driver, nonpnp.sys, and the test application, nonpnpapp.exe, in the binary output directory specified for the build environment. + +To test this driver, copy the nonpnp.inf into the same folder as the nonpnpapp.exe and the wdfcoinstaller\.dll . + +**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. + +Next, run nonpnpapp.exe, a simple Win32 multithreaded console mode application. The driver will be automatically loaded and started. When you exit the app, the driver will be stopped and removed. + +Usage: nonpnpapp.exe (-l) (-v version) + +**Note**: This application first tries to open the device (\\Device\\FileIo). If the device doesn't exist, it takes that as a hint that the driver is not loaded and tries to load the driver using service control manager API. If the service is loaded successfully, it tries to open the device again. If successful, it makes all four different types of DeviceControl calls to the driver. After that it makes a WriteFile call with an arbitrary size buffer. The driver, in response, writes that buffer to a file opened in the Create request. The name of the file was provided by the application as part of the device name and the directory path is hardcoded to %WINDIR%\\temp. When the WriteFile returns, the application makes a ReadFile call to read the file through the driver, and then compares the data returned by the driver with the one it originally wrote. If you specify -l option in command line, the application does this Write and Read operation in an infinite loop. The -v command line option is used to specify the version of the KMDF coinstaller (wdfcoinstaller\.dll) to load. If none is specified then it loads the coinstaller for v1.0 (wdfcoinstaller01000.dll) + +### WDF SECTION + +Nonpnp drivers typically don't need an INF file to install. Since we are using framework interfaces, we have to use the Kmdf coinstaller to install the framework binaries on the target machine. The Kmdf coinstaller needs a WDF specific section in the INF to get the driver service name and the version of the Kmdf library the driver is bound to. The syntax and description of the section is given below. Any non inf based driver using Kmdf library will need to have a dummy inf file with the wdf section in it. The format of the Wdf section is given below: + +[Version] + +Signature="\$WINDOWS NT\$" + +[\.NT.Wdf] + +KmdfService = \, \ + +[\] + +KmdfLibraryVersion = \ + +For example, for V1.0 KmdfLibraryVersion is "1.0" + diff --git a/general/ioctl/kmdf/exe/install.c b/general/ioctl/kmdf/exe/install.c new file mode 100644 index 000000000..7060243ac --- /dev/null +++ b/general/ioctl/kmdf/exe/install.c @@ -0,0 +1,812 @@ +/*++ +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 +_Analysis_mode_(_Analysis_code_type_user_code_) + +#include +#include +#include +#include +#include +#include "public.h" + +#include + +#define ARRAY_SIZE(x) (sizeof(x) /sizeof(x[0])) + +extern +PCHAR +GetCoinstallerVersion( + VOID + ) ; + +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 + ); + +#define SYSTEM32_DRIVERS "\\System32\\Drivers\\" +#define NONPNP_INF_FILENAME L"\\nonpnp.inf" +#define WDF_SECTION_NAME L"nonpnp.NT.Wdf" + +//---------------------------------------------------------------------------- +// +//---------------------------------------------------------------------------- +PFN_WDFPREDEVICEINSTALLEX pfnWdfPreDeviceInstallEx; +PFN_WDFPOSTDEVICEINSTALL pfnWdfPostDeviceInstall; +PFN_WDFPREDEVICEREMOVE pfnWdfPreDeviceRemove; +PFN_WDFPOSTDEVICEREMOVE pfnWdfPostDeviceRemove; + +//----------------------------------------------------------------------------- +// 4127 -- Conditional Expression is Constant warning +//----------------------------------------------------------------------------- +#define WHILE(a) \ +__pragma(warning(suppress:4127)) while(a) + +LONG +GetPathToInf( + _Out_writes_(InfFilePathSize) PWCHAR InfFilePath, + IN ULONG InfFilePathSize + ) +{ + LONG error = ERROR_SUCCESS; + + if (GetCurrentDirectoryW(InfFilePathSize, InfFilePath) == 0) { + error = GetLastError(); + printf("InstallDriver failed! Error = %d \n", error); + return error; + } + if (FAILED( StringCchCatW(InfFilePath, + InfFilePathSize, + NONPNP_INF_FILENAME) )) { + error = ERROR_BUFFER_OVERFLOW; + return error; + } + return error; + +} + +//---------------------------------------------------------------------------- +// +//---------------------------------------------------------------------------- +BOOLEAN +InstallDriver( + IN SC_HANDLE SchSCManager, + IN LPCTSTR DriverName, + IN LPCTSTR ServiceExe + ) +/*++ + +Routine Description: + +Arguments: + +Return Value: + +--*/ +{ + SC_HANDLE schService; + DWORD err; + WCHAR infPath[MAX_PATH]; + WDF_COINSTALLER_INSTALL_OPTIONS clientOptions; + + WDF_COINSTALLER_INSTALL_OPTIONS_INIT(&clientOptions); + + // + // 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.). + // + // + // PRE-INSTALL for WDF support + // + err = GetPathToInf(infPath, ARRAY_SIZE(infPath) ); + if (err != ERROR_SUCCESS) { + return FALSE; + } + err = pfnWdfPreDeviceInstallEx(infPath, WDF_SECTION_NAME, &clientOptions); + + if (err != ERROR_SUCCESS) { + if (err == ERROR_SUCCESS_REBOOT_REQUIRED) { + printf("System needs to be rebooted, before the driver installation can proceed.\n"); + } + + return FALSE; + } + + // + // 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. + // + CloseServiceHandle(schService); + + // + // POST-INSTALL for WDF support + // + err = pfnWdfPostDeviceInstall( infPath, WDF_SECTION_NAME ); + + if (err != ERROR_SUCCESS) { + return FALSE; + } + + // + // 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. + // + CloseServiceHandle(schSCManager); + + return rCode; + +} // ManageDriver + + +BOOLEAN +RemoveDriver( + IN SC_HANDLE SchSCManager, + IN LPCTSTR DriverName + ) +{ + SC_HANDLE schService; + BOOLEAN rCode; + DWORD err; + WCHAR infPath[MAX_PATH]; + + err = GetPathToInf(infPath, ARRAY_SIZE(infPath) ); + if (err != ERROR_SUCCESS) { + return FALSE; + } + + // + // PRE-REMOVE of WDF support + // + err = pfnWdfPreDeviceRemove( infPath, WDF_SECTION_NAME ); + + if (err != ERROR_SUCCESS) { + return FALSE; + } + + // + // 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. + // + CloseServiceHandle(schService); + + // + // POST-REMOVE of WDF support + // + err = pfnWdfPostDeviceRemove(infPath, WDF_SECTION_NAME ); + + if (err != ERROR_SUCCESS) { + rCode = FALSE; + } + + return rCode; + +} // RemoveDriver + + + +BOOLEAN +StartDriver( + IN SC_HANDLE SchSCManager, + IN LPCTSTR DriverName + ) +{ + SC_HANDLE schService; + DWORD err; + BOOL ok; + + // + // Open the handle to the existing service. + // + schService = OpenService(SchSCManager, + DriverName, + SERVICE_ALL_ACCESS + ); + + if (schService == NULL) { + // + // Indicate failure. + // + printf("OpenService failed! Error = %d\n", GetLastError()); + return FALSE; + } + + // + // Start the execution of the service (i.e. start the driver). + // + ok = StartService( schService, 0, NULL ); + + if (!ok) { + + err = GetLastError(); + + if (err == ERROR_SERVICE_ALREADY_RUNNING) { + // + // Ignore this error. + // + return TRUE; + + } else { + // + // Indicate failure. + // Fall through to properly close the service handle. + // + printf("StartService failure! Error = %d\n", err ); + 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", 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. + // + CloseServiceHandle (schService); + + return rCode; + +} // StopDriver + + +// +// Caller must free returned pathname string. +// +PCHAR +BuildDriversDirPath( + _In_ PSTR DriverName + ) +{ + size_t remain; + size_t len; + PCHAR dir; + + if (!DriverName || strlen(DriverName) == 0) { + return NULL; + } + + remain = MAX_PATH; + + // + // Allocate string space + // + dir = (PCHAR) malloc( remain + 1 ); + + if (!dir) { + return NULL; + } + + // + // Get the base windows directory path. + // + len = GetWindowsDirectory( dir, (UINT) remain ); + + if (len == 0 || + (remain - len) < sizeof(SYSTEM32_DRIVERS)) { + free(dir); + return NULL; + } + remain -= len; + + // + // Build dir to have "%windir%\System32\Drivers\". + // + if (FAILED( StringCchCat(dir, remain, SYSTEM32_DRIVERS) )) { + free(dir); + return NULL; + } + + remain -= sizeof(SYSTEM32_DRIVERS); + len += sizeof(SYSTEM32_DRIVERS); + len += strlen(DriverName); + + if (remain < len) { + free(dir); + return NULL; + } + + if (FAILED( StringCchCat(dir, remain, DriverName) )) { + free(dir); + return NULL; + } + + dir[len] = '\0'; // keeps prefast happy + + return dir; +} + + +BOOLEAN +SetupDriverName( + _Inout_updates_all_(BufferLength) PCHAR DriverLocation, + _In_ ULONG BufferLength + ) +{ + HANDLE fileHandle; + DWORD driverLocLen = 0; + BOOL ok; + PCHAR driversDir; + + // + // Setup path name to driver file. + // + driverLocLen = + GetCurrentDirectory(BufferLength, DriverLocation); + + if (!driverLocLen) { + + printf("GetCurrentDirectory failed! Error = %d \n", + GetLastError()); + + return FALSE; + } + + if (FAILED( StringCchCat(DriverLocation, BufferLength, "\\" DRIVER_NAME ".sys") )) { + return FALSE; + } + + // + // Insure driver file is in the specified directory. + // + fileHandle = CreateFile( DriverLocation, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL ); + + if (fileHandle == INVALID_HANDLE_VALUE) { + // + // Indicate failure. + // + printf("Driver: %s.SYS is not in the %s directory. \n", + DRIVER_NAME, DriverLocation ); + return FALSE; + } + + // + // Build %windir%\System32\Drivers\ path. + // Copy the driver to %windir%\system32\drivers + // + driversDir = BuildDriversDirPath( DRIVER_NAME ".sys" ); + + if (!driversDir) { + printf("BuildDriversDirPath failed!\n"); + return FALSE; + } + + ok = CopyFile( DriverLocation, driversDir, FALSE ); + + if(!ok) { + printf("CopyFile failed: error(%d) - \"%s\"\n", + GetLastError(), driversDir ); + free(driversDir); + return FALSE; + } + + if (FAILED( StringCchCopy(DriverLocation, BufferLength, driversDir) )) { + free(driversDir); + return FALSE; + } + + free(driversDir); + + // + // Close open file handle. + // + if (fileHandle) { + CloseHandle(fileHandle); + } + + // + // Indicate success. + // + return TRUE; + +} // SetupDriverName + + +HMODULE +LoadWdfCoInstaller( + VOID + ) +{ + HMODULE library = NULL; + DWORD error = ERROR_SUCCESS; + CHAR szCurDir[MAX_PATH]; + CHAR tempCoinstallerName[MAX_PATH]; + PCHAR coinstallerVersion; + + do { + + if (GetCurrentDirectory(MAX_PATH, szCurDir) == 0) { + + printf("GetCurrentDirectory failed! Error = %d \n", GetLastError()); + break; + } + coinstallerVersion = GetCoinstallerVersion(); + if (FAILED( StringCchPrintf(tempCoinstallerName, + MAX_PATH, + "\\WdfCoInstaller%s.dll", + coinstallerVersion) )) { + break; + } + if (FAILED( StringCchCat(szCurDir, MAX_PATH, tempCoinstallerName) )) { + break; + } + + library = LoadLibrary(szCurDir); + + if (library == NULL) { + error = GetLastError(); + printf("LoadLibrary(%s) failed: %d\n", szCurDir, error); + break; + } + + pfnWdfPreDeviceInstallEx = + (PFN_WDFPREDEVICEINSTALLEX) GetProcAddress( library, "WdfPreDeviceInstallEx" ); + + if (pfnWdfPreDeviceInstallEx == NULL) { + error = GetLastError(); + printf("GetProcAddress(\"WdfPreDeviceInstallEx\") failed: %d\n", error); + return NULL; + } + + pfnWdfPostDeviceInstall = + (PFN_WDFPOSTDEVICEINSTALL) GetProcAddress( library, "WdfPostDeviceInstall" ); + + if (pfnWdfPostDeviceInstall == NULL) { + error = GetLastError(); + printf("GetProcAddress(\"WdfPostDeviceInstall\") failed: %d\n", error); + return NULL; + } + + pfnWdfPreDeviceRemove = + (PFN_WDFPREDEVICEREMOVE) GetProcAddress( library, "WdfPreDeviceRemove" ); + + if (pfnWdfPreDeviceRemove == NULL) { + error = GetLastError(); + printf("GetProcAddress(\"WdfPreDeviceRemove\") failed: %d\n", error); + return NULL; + } + + pfnWdfPostDeviceRemove = + (PFN_WDFPREDEVICEREMOVE) GetProcAddress( library, "WdfPostDeviceRemove" ); + + if (pfnWdfPostDeviceRemove == NULL) { + error = GetLastError(); + printf("GetProcAddress(\"WdfPostDeviceRemove\") failed: %d\n", error); + return NULL; + } + + } WHILE (0); + + if (error != ERROR_SUCCESS) { + if (library) { + FreeLibrary( library ); + } + library = NULL; + } + + return library; +} + + +VOID +UnloadWdfCoInstaller( + HMODULE Library + ) +{ + if (Library) { + FreeLibrary( Library ); + } +} + diff --git a/general/ioctl/kmdf/exe/nonpnp.inf b/general/ioctl/kmdf/exe/nonpnp.inf new file mode 100644 index 000000000..b20a58b48 --- /dev/null +++ b/general/ioctl/kmdf/exe/nonpnp.inf @@ -0,0 +1,8 @@ +[Version] +Signature="$WINDOWS NT$" + +[nonpnp.NT.Wdf] +KmdfService = nonpnp, nonpnp_Service_kmdfInst + +[nonpnp_Service_kmdfInst] +KmdfLibraryVersion = 1.11 diff --git a/general/ioctl/kmdf/exe/nonpnpapp.vcxproj b/general/ioctl/kmdf/exe/nonpnpapp.vcxproj new file mode 100644 index 000000000..2c94c4139 --- /dev/null +++ b/general/ioctl/kmdf/exe/nonpnpapp.vcxproj @@ -0,0 +1,197 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {0EF21B20-E757-4FD0-8A1C-4ABF6FD7E466} + $(MSBuildProjectName) + 1 + 11 + Debug + Win32 + {FF8A5123-63A1-4527-A466-ED494318076D} + + + + 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 + + + + nonpnpapp + + + nonpnpapp + + + nonpnpapp + + + nonpnpapp + + + + %(AdditionalDependencies);setupapi.lib;user32.lib + + + %(AdditionalIncludeDirectories);..\sys + %(PreprocessorDefinitions);KMDF_VERSION_MAJOR=1;KMDF_VERSION_MINOR=11 + %(AdditionalIncludeDirectories);$(WDKContentRoot)\Include\wdf\kmdf\1.11 + + + %(AdditionalIncludeDirectories);..\sys + %(PreprocessorDefinitions);KMDF_VERSION_MAJOR=1;KMDF_VERSION_MINOR=11 + %(AdditionalIncludeDirectories);$(WDKContentRoot)\Include\wdf\kmdf\1.11 + + + + + %(AdditionalIncludeDirectories);..\sys + %(PreprocessorDefinitions);KMDF_VERSION_MAJOR=1;KMDF_VERSION_MINOR=11 + %(AdditionalIncludeDirectories);$(WDKContentRoot)\Include\wdf\kmdf\1.11 + + + + + %(AdditionalDependencies);setupapi.lib;user32.lib + + + %(AdditionalIncludeDirectories);..\sys + %(PreprocessorDefinitions);KMDF_VERSION_MAJOR=1;KMDF_VERSION_MINOR=11 + %(AdditionalIncludeDirectories);$(WDKContentRoot)\Include\wdf\kmdf\1.11 + + + %(AdditionalIncludeDirectories);..\sys + %(PreprocessorDefinitions);KMDF_VERSION_MAJOR=1;KMDF_VERSION_MINOR=11 + %(AdditionalIncludeDirectories);$(WDKContentRoot)\Include\wdf\kmdf\1.11 + + + + + %(AdditionalIncludeDirectories);..\sys + %(PreprocessorDefinitions);KMDF_VERSION_MAJOR=1;KMDF_VERSION_MINOR=11 + %(AdditionalIncludeDirectories);$(WDKContentRoot)\Include\wdf\kmdf\1.11 + + + + + %(AdditionalDependencies);setupapi.lib;user32.lib + + + %(AdditionalIncludeDirectories);..\sys + %(PreprocessorDefinitions);KMDF_VERSION_MAJOR=1;KMDF_VERSION_MINOR=11 + %(AdditionalIncludeDirectories);$(WDKContentRoot)\Include\wdf\kmdf\1.11 + + + %(AdditionalIncludeDirectories);..\sys + %(PreprocessorDefinitions);KMDF_VERSION_MAJOR=1;KMDF_VERSION_MINOR=11 + %(AdditionalIncludeDirectories);$(WDKContentRoot)\Include\wdf\kmdf\1.11 + + + + + %(AdditionalIncludeDirectories);..\sys + %(PreprocessorDefinitions);KMDF_VERSION_MAJOR=1;KMDF_VERSION_MINOR=11 + %(AdditionalIncludeDirectories);$(WDKContentRoot)\Include\wdf\kmdf\1.11 + + + + + %(AdditionalDependencies);setupapi.lib;user32.lib + + + %(AdditionalIncludeDirectories);..\sys + %(PreprocessorDefinitions);KMDF_VERSION_MAJOR=1;KMDF_VERSION_MINOR=11 + %(AdditionalIncludeDirectories);$(WDKContentRoot)\Include\wdf\kmdf\1.11 + + + %(AdditionalIncludeDirectories);..\sys + %(PreprocessorDefinitions);KMDF_VERSION_MAJOR=1;KMDF_VERSION_MINOR=11 + %(AdditionalIncludeDirectories);$(WDKContentRoot)\Include\wdf\kmdf\1.11 + + + + + %(AdditionalIncludeDirectories);..\sys + %(PreprocessorDefinitions);KMDF_VERSION_MAJOR=1;KMDF_VERSION_MINOR=11 + %(AdditionalIncludeDirectories);$(WDKContentRoot)\Include\wdf\kmdf\1.11 + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/general/ioctl/kmdf/exe/nonpnpapp.vcxproj.Filters b/general/ioctl/kmdf/exe/nonpnpapp.vcxproj.Filters new file mode 100644 index 000000000..221896d4a --- /dev/null +++ b/general/ioctl/kmdf/exe/nonpnpapp.vcxproj.Filters @@ -0,0 +1,25 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {438A7A19-8784-4ADC-BB59-D3D149EA5459} + + + h;hpp;hxx;hm;inl;inc;xsd + {A4461789-7186-462B-9A53-73365178FC6A} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {323846A7-7FFC-4D78-8D57-B470FE54DE4B} + + + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/general/ioctl/kmdf/exe/testapp.c b/general/ioctl/kmdf/exe/testapp.c new file mode 100644 index 000000000..a6cd07c1c --- /dev/null +++ b/general/ioctl/kmdf/exe/testapp.c @@ -0,0 +1,642 @@ +/*++ + +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: + + Purpose of this app to test the NONPNP sample driver. The app + makes four different ioctl calls to test all the buffer types, write + some random buffer content to a file created by the driver in \SystemRoot\Temp + directory, and reads the same file and matches the content. + If -l option is specified, it does the write and read operation in a loop + until the app is terminated by pressing ^C. + + Make sure you have the \SystemRoot\Temp directory exists before you run the test. + +Environment: + + Win32 console application. + +--*/ + + +#include +_Analysis_mode_(_Analysis_code_type_user_code_) + +#include + +#pragma warning(disable:4201) // nameless struct/union +#include +#pragma warning(default:4201) + +#include +#include +#include +#include +#include +#include "public.h" + + +BOOLEAN +ManageDriver( + IN LPCTSTR DriverName, + IN LPCTSTR ServiceName, + IN USHORT Function + ); + +HMODULE +LoadWdfCoInstaller( + VOID + ); + +VOID +UnloadWdfCoInstaller( + HMODULE Library + ); + +BOOLEAN +SetupDriverName( + _Inout_updates_all_(BufferLength) PCHAR DriverLocation, + _In_ ULONG BufferLength + ); + +BOOLEAN +DoFileReadWrite( + HANDLE HDevice + ); + +VOID +DoIoctls( + HANDLE hDevice + ); + +// for example, WDF 1.9 is "01009". the size 6 includes the ending NULL marker +// +#define MAX_VERSION_SIZE 6 + +CHAR G_coInstallerVersion[MAX_VERSION_SIZE] = {0}; +BOOLEAN G_fLoop = FALSE; +BOOL G_versionSpecified = FALSE; + + + +//----------------------------------------------------------------------------- +// 4127 -- Conditional Expression is Constant warning +//----------------------------------------------------------------------------- +#define WHILE(constant) \ +__pragma(warning(disable: 4127)) while(constant); __pragma(warning(default: 4127)) + + +#define USAGE \ +"Usage: nonpnpapp <-V version> <-l> \n" \ + " -V version {if no version is specified the version specified in the build environment will be used.}\n" \ + " The version is the version of the KMDF coinstaller to use \n" \ + " The format of version is MMmmm where MM -- major #, mmm - serial# \n" \ + " -l { option to continuously read & write to the file} \n" + +BOOL +ValidateCoinstallerVersion( + _In_ PSTR Version + ) +{ BOOL ok = FALSE; + INT i; + + for(i= 0; i 1 ) {// give usage if invoked with no parms + error = Parse(argc, argv); + if (error != ERROR_SUCCESS) { + return; + } + } + + if (!G_versionSpecified ) { + coinstallerVersion = GetCoinstallerVersion(); + + // + // if no version is specified or an invalid one is specified use default version + // + printf("No version specified. Using default version:%s\n", + coinstallerVersion); + + } else { + coinstallerVersion = (PCHAR)&G_coInstallerVersion; + } + + // + // open the device + // + hDevice = CreateFile(DEVICE_NAME, + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL); + + if(hDevice == INVALID_HANDLE_VALUE) { + + errNum = GetLastError(); + + if (!(errNum == ERROR_FILE_NOT_FOUND || + errNum == ERROR_PATH_NOT_FOUND)) { + + printf("CreateFile failed! ERROR_FILE_NOT_FOUND = %d\n", + errNum); + return ; + } + + // + // Load WdfCoInstaller.dll. + // + library = LoadWdfCoInstaller(); + + if (library == NULL) { + printf("The WdfCoInstaller%s.dll library needs to be " + "in same directory as nonpnpapp.exe\n", coinstallerVersion); + return; + } + + // + // The driver is not started yet so let us the install the driver. + // First setup full path to driver name. + // + ok = SetupDriverName( driverLocation, MAX_PATH ); + + if (!ok) { + return ; + } + + ok = ManageDriver( DRIVER_NAME, + driverLocation, + DRIVER_FUNC_INSTALL ); + + if (!ok) { + + printf("Unable to install driver. \n"); + + // + // Error - remove driver. + // + ManageDriver( DRIVER_NAME, + driverLocation, + DRIVER_FUNC_REMOVE ); + return; + } + + hDevice = CreateFile( DEVICE_NAME, + 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; + } + } + + DoIoctls(hDevice); + + do { + + if(!DoFileReadWrite(hDevice)) { + break; + } + + if(!G_fLoop) { + break; + } + Sleep(1000); // sleep for 1 sec. + + } WHILE (TRUE); + + // + // Close the handle to the device before unloading the driver. + // + CloseHandle ( hDevice ); + + // + // Unload the driver. Ignore any errors. + // + ManageDriver( DRIVER_NAME, + driverLocation, + DRIVER_FUNC_REMOVE ); + + // + // Unload WdfCoInstaller.dll + // + if ( library ) { + UnloadWdfCoInstaller( library ); + } + return; +} + + +VOID +DoIoctls( + HANDLE hDevice + ) +{ + char OutputBuffer[100]; + char InputBuffer[200]; + BOOL bRc; + ULONG bytesReturned; + + // + // 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 + // + + if(FAILED(StringCchCopy(InputBuffer, sizeof(InputBuffer), + "this String is from User Application; using METHOD_BUFFERED"))){ + return; + } + + printf("\nCalling DeviceIoControl METHOD_BUFFERED:\n"); + + memset(OutputBuffer, 0, sizeof(OutputBuffer)); + + bRc = DeviceIoControl ( hDevice, + (DWORD) IOCTL_NONPNP_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"); + + if(FAILED(StringCchCopy(InputBuffer, sizeof(InputBuffer), + "this String is from User Application; using METHOD_NEITHER"))) { + return; + } + + memset(OutputBuffer, 0, sizeof(OutputBuffer)); + + bRc = DeviceIoControl ( hDevice, + (DWORD) IOCTL_NONPNP_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"); + + if(FAILED(StringCchCopy(InputBuffer, sizeof(InputBuffer), + "this String is from User Application; using METHOD_IN_DIRECT"))) { + return; + } + + if(FAILED(StringCchCopy(OutputBuffer, sizeof(OutputBuffer), + "This String is from User Application in OutBuffer; using METHOD_IN_DIRECT"))) { + return; + } + + bRc = DeviceIoControl ( hDevice, + (DWORD) IOCTL_NONPNP_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"); + if(FAILED(StringCchCopy(InputBuffer, sizeof(InputBuffer), + "this String is from User Application; using METHOD_OUT_DIRECT"))){ + return; + } + + memset(OutputBuffer, 0, sizeof(OutputBuffer)); + + bRc = DeviceIoControl ( hDevice, + (DWORD) IOCTL_NONPNP_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); + + return; + +} + + +BOOLEAN +DoFileReadWrite( + HANDLE HDevice + ) +{ + ULONG bufLength, index; + PUCHAR readBuf = NULL; + PUCHAR writeBuf = NULL; + BOOLEAN ret; + ULONG bytesWritten, bytesRead; + + // + // Seed the random-number generator with current time so that + // the numbers will be different every time we run. + // + srand( (unsigned)time( NULL ) ); + + // + // rand function returns a pseudorandom integer in the range 0 to RAND_MAX + // (0x7fff) + // + bufLength = rand(); + // + // Try until the bufLength is not zero. + // + while(bufLength == 0) { + bufLength = rand(); + } + + // + // Allocate a buffer of that size to use for write operation. + // + writeBuf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, bufLength); + if(!writeBuf) { + ret = FALSE; + goto End; + } + // + // Fill the buffer with randon number less than UCHAR_MAX. + // + index = bufLength; + while(index){ + writeBuf[index-1] = (UCHAR) rand() % UCHAR_MAX; + index--; + } + + printf("Write %d bytes to file\n", bufLength); + + // + // Tell the driver to write the buffer content to the file from the + // begining of the file. + // + + if (!WriteFile(HDevice, + writeBuf, + bufLength, + &bytesWritten, + NULL)) { + + printf("ReadFile failed with error 0x%x\n", GetLastError()); + + ret = FALSE; + goto End; + + } + + // + // Allocate another buffer of same size. + // + readBuf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, bufLength); + if(!readBuf) { + + ret = FALSE; + goto End; + } + + printf("Read %d bytes from the same file\n", bufLength); + + // + // Tell the driver to read the file from the begining. + // + if (!ReadFile(HDevice, + readBuf, + bufLength, + &bytesRead, + NULL)) { + + printf("Error: ReadFile failed with error 0x%x\n", GetLastError()); + + ret = FALSE; + goto End; + + } + + // + // Now compare the readBuf and writeBuf content. They should be the same. + // + + if(bytesRead != bytesWritten) { + printf("bytesRead(%d) != bytesWritten(%d)\n", bytesRead, bytesWritten); + ret = FALSE; + goto End; + } + + if(memcmp(readBuf, writeBuf, bufLength) != 0){ + printf("Error: ReadBuf and WriteBuf contents are not the same\n"); + ret = FALSE; + goto End; + } + + ret = TRUE; + +End: + + if(readBuf){ + HeapFree (GetProcessHeap(), 0, readBuf); + } + + if(writeBuf){ + HeapFree (GetProcessHeap(), 0, writeBuf); + } + + return ret; + + +} + + diff --git a/general/ioctl/kmdf/ioctl.sln b/general/ioctl/kmdf/ioctl.sln new file mode 100644 index 000000000..aab612131 --- /dev/null +++ b/general/ioctl/kmdf/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", "{B028EBDA-6423-4F60-AA5E-FA6D82D7724A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sys", "Sys", "{4DA95BFC-3253-4E4A-92C3-7C8BAE993A05}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nonpnpapp", "exe\nonpnpapp.vcxproj", "{75AE3580-0818-45CF-B6F0-FBA6FA7BE8EB}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nonpnp", "sys\nonpnp.vcxproj", "{B622D542-4942-46A1-B976-F530446DD6A5}" +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 + {75AE3580-0818-45CF-B6F0-FBA6FA7BE8EB}.Debug|Win32.ActiveCfg = Debug|Win32 + {75AE3580-0818-45CF-B6F0-FBA6FA7BE8EB}.Debug|Win32.Build.0 = Debug|Win32 + {75AE3580-0818-45CF-B6F0-FBA6FA7BE8EB}.Release|Win32.ActiveCfg = Release|Win32 + {75AE3580-0818-45CF-B6F0-FBA6FA7BE8EB}.Release|Win32.Build.0 = Release|Win32 + {75AE3580-0818-45CF-B6F0-FBA6FA7BE8EB}.Debug|x64.ActiveCfg = Debug|x64 + {75AE3580-0818-45CF-B6F0-FBA6FA7BE8EB}.Debug|x64.Build.0 = Debug|x64 + {75AE3580-0818-45CF-B6F0-FBA6FA7BE8EB}.Release|x64.ActiveCfg = Release|x64 + {75AE3580-0818-45CF-B6F0-FBA6FA7BE8EB}.Release|x64.Build.0 = Release|x64 + {B622D542-4942-46A1-B976-F530446DD6A5}.Debug|Win32.ActiveCfg = Debug|Win32 + {B622D542-4942-46A1-B976-F530446DD6A5}.Debug|Win32.Build.0 = Debug|Win32 + {B622D542-4942-46A1-B976-F530446DD6A5}.Release|Win32.ActiveCfg = Release|Win32 + {B622D542-4942-46A1-B976-F530446DD6A5}.Release|Win32.Build.0 = Release|Win32 + {B622D542-4942-46A1-B976-F530446DD6A5}.Debug|x64.ActiveCfg = Debug|x64 + {B622D542-4942-46A1-B976-F530446DD6A5}.Debug|x64.Build.0 = Debug|x64 + {B622D542-4942-46A1-B976-F530446DD6A5}.Release|x64.ActiveCfg = Release|x64 + {B622D542-4942-46A1-B976-F530446DD6A5}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {75AE3580-0818-45CF-B6F0-FBA6FA7BE8EB} = {B028EBDA-6423-4F60-AA5E-FA6D82D7724A} + {B622D542-4942-46A1-B976-F530446DD6A5} = {4DA95BFC-3253-4E4A-92C3-7C8BAE993A05} + EndGlobalSection +EndGlobal diff --git a/general/ioctl/kmdf/sys/localwpp.ini b/general/ioctl/kmdf/sys/localwpp.ini new file mode 100644 index 000000000..c290070a7 --- /dev/null +++ b/general/ioctl/kmdf/sys/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/ioctl/kmdf/sys/nonpnp.c b/general/ioctl/kmdf/sys/nonpnp.c new file mode 100644 index 000000000..519ae8386 --- /dev/null +++ b/general/ioctl/kmdf/sys/nonpnp.c @@ -0,0 +1,1309 @@ +/*++ + +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: + + nonpnp.c + +Abstract: + + Purpose of this driver is to demonstrate how to write a legacy (NON WDM) + driver using framework, show how to handle 4 different ioctls - + METHOD_NEITHER - in particular and also show how to read & write to file + from KernelMode using Zw functions. + + For a non-framework version of sample on how to handle IOCTLs in driver, + study src\general\IOCTL in the DDK. + +Environment: + + Kernel mode only. + +--*/ + +#include "nonpnp.h" + +// +// The trace message header file must be included in a source file +// before any WPP macro calls and after defining a WPP_CONTROL_GUIDS +// macro. During the compilation, WPP scans the source files for +// TraceEvents() 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 "nonpnp.tmh" + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text( INIT, DriverEntry ) +#pragma alloc_text( PAGE, NonPnpDeviceAdd) +#pragma alloc_text( PAGE, NonPnpEvtDriverContextCleanup) +#pragma alloc_text( PAGE, NonPnpEvtDriverUnload) +#pragma alloc_text( PAGE, NonPnpEvtDeviceIoInCallerContext) +#pragma alloc_text( PAGE, NonPnpEvtDeviceFileCreate) +#pragma alloc_text( PAGE, NonPnpEvtFileClose) +#pragma alloc_text( PAGE, FileEvtIoRead) +#pragma alloc_text( PAGE, FileEvtIoWrite) +#pragma alloc_text( PAGE, FileEvtIoDeviceControl) +#endif // ALLOC_PRAGMA + + +NTSTATUS +DriverEntry( + IN OUT 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 status; + WDF_DRIVER_CONFIG config; + WDFDRIVER hDriver; + PWDFDEVICE_INIT pInit = NULL; + WDF_OBJECT_ATTRIBUTES attributes; + + KdPrint(("Driver Frameworks NONPNP Legacy Driver Example\n")); + + + WDF_DRIVER_CONFIG_INIT( + &config, + WDF_NO_EVENT_CALLBACK // This is a non-pnp driver. + ); + + // + // Tell the framework that this is non-pnp driver so that it doesn't + // set the default AddDevice routine. + // + config.DriverInitFlags |= WdfDriverInitNonPnpDriver; + + // + // NonPnp driver must explicitly register an unload routine for + // the driver to be unloaded. + // + config.EvtDriverUnload = NonPnpEvtDriverUnload; + + // + // 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 = NonPnpEvtDriverContextCleanup; + + // + // Create a framework driver object to represent our driver. + // + status = WdfDriverCreate(DriverObject, + RegistryPath, + &attributes, + &config, + &hDriver); + if (!NT_SUCCESS(status)) { + KdPrint (("NonPnp: WdfDriverCreate failed with status 0x%x\n", status)); + return status; + } + + // + // Since we are calling WPP_CLEANUP in the DriverContextCleanup + // callback we should initialize WPP Tracing after WDFDRIVER + // object is created to ensure that we cleanup WPP properly + // if we return failure status from DriverEntry. This + // eliminates the need to call WPP_CLEANUP in every path + // of DriverEntry. + // + WPP_INIT_TRACING( DriverObject, RegistryPath ); + + // + // On Win2K system, you will experience some delay in getting trace events + // due to the way the ETW is activated to accept trace messages. + // + KdPrint(("NonPnp: DriverEntry: tracing enabled\n")); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, + "Driver Frameworks NONPNP Legacy Driver Example"); + + // + // + // In order to create a control device, we first need to allocate a + // WDFDEVICE_INIT structure and set all properties. + // + pInit = WdfControlDeviceInitAllocate( + hDriver, + &SDDL_DEVOBJ_SYS_ALL_ADM_RWX_WORLD_RW_RES_R + ); + + if (pInit == NULL) { + status = STATUS_INSUFFICIENT_RESOURCES; + return status; + } + + // + // Call NonPnpDeviceAdd to create a deviceobject to represent our + // software device. + // + status = NonPnpDeviceAdd(hDriver, pInit); + + return status; +} + +NTSTATUS +NonPnpDeviceAdd( + IN WDFDRIVER Driver, + IN PWDFDEVICE_INIT DeviceInit + ) +/*++ + +Routine Description: + + Called by the DriverEntry to create a control-device. This call is + responsible for freeing the memory for DeviceInit. + +Arguments: + + DriverObject - a pointer to the object that represents this device + driver. + + DeviceInit - Pointer to a driver-allocated WDFDEVICE_INIT structure. + +Return Value: + + STATUS_SUCCESS if initialized; an error otherwise. + +--*/ +{ + NTSTATUS status; + WDF_OBJECT_ATTRIBUTES attributes; + WDF_IO_QUEUE_CONFIG ioQueueConfig; + WDF_FILEOBJECT_CONFIG fileConfig; + WDFQUEUE queue; + WDFDEVICE controlDevice; + DECLARE_CONST_UNICODE_STRING(ntDeviceName, NTDEVICE_NAME_STRING) ; + DECLARE_CONST_UNICODE_STRING(symbolicLinkName, SYMBOLIC_NAME_STRING) ; + + UNREFERENCED_PARAMETER( Driver ); + + PAGED_CODE(); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, + "NonPnpDeviceAdd DeviceInit %p\n", DeviceInit); + // + // Set exclusive to TRUE so that no more than one app can talk to the + // control device at any time. + // + WdfDeviceInitSetExclusive(DeviceInit, TRUE); + + WdfDeviceInitSetIoType(DeviceInit, WdfDeviceIoBuffered); + + + status = WdfDeviceInitAssignName(DeviceInit, &ntDeviceName); + + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, "WdfDeviceInitAssignName failed %!STATUS!", status); + goto End; + } + + WdfControlDeviceInitSetShutdownNotification(DeviceInit, + NonPnpShutdown, + WdfDeviceShutdown); + + // + // Initialize WDF_FILEOBJECT_CONFIG_INIT struct to tell the + // framework whether you are interested in handling Create, Close and + // Cleanup requests that gets generated when an application or another + // kernel component opens an handle to the device. If you don't register + // the framework default behaviour would be to 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, + NonPnpEvtDeviceFileCreate, + NonPnpEvtFileClose, + WDF_NO_EVENT_CALLBACK // not interested in Cleanup + ); + + WdfDeviceInitSetFileObjectConfig(DeviceInit, + &fileConfig, + WDF_NO_OBJECT_ATTRIBUTES); + + // + // In order to support METHOD_NEITHER Device controls, or + // NEITHER device I/O type, we need to register for the + // EvtDeviceIoInProcessContext callback so that we can handle the request + // in the calling threads context. + // + WdfDeviceInitSetIoInCallerContextCallback(DeviceInit, + NonPnpEvtDeviceIoInCallerContext); + + // + // Specify the size of device context + // + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, + CONTROL_DEVICE_EXTENSION); + + status = WdfDeviceCreate(&DeviceInit, + &attributes, + &controlDevice); + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, "WdfDeviceCreate failed %!STATUS!", status); + goto End; + } + + // + // Create a symbolic link for the control object so that usermode can open + // the device. + // + + + status = WdfDeviceCreateSymbolicLink(controlDevice, + &symbolicLinkName); + + if (!NT_SUCCESS(status)) { + // + // Control device will be deleted automatically by the framework. + // + TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, "WdfDeviceCreateSymbolicLink failed %!STATUS!", status); + goto End; + } + + // + // 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(&ioQueueConfig, + WdfIoQueueDispatchSequential); + + ioQueueConfig.EvtIoRead = FileEvtIoRead; + ioQueueConfig.EvtIoWrite = FileEvtIoWrite; + ioQueueConfig.EvtIoDeviceControl = FileEvtIoDeviceControl; + + WDF_OBJECT_ATTRIBUTES_INIT(&attributes); + // + // Since we are using Zw function set execution level to passive so that + // framework ensures that our Io callbacks called at only passive-level + // even if the request came in at DISPATCH_LEVEL from another driver. + // + //attributes.ExecutionLevel = WdfExecutionLevelPassive; + + // + // 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(controlDevice, + &ioQueueConfig, + &attributes, + &queue // pointer to default queue + ); + __analysis_assume(ioQueueConfig.EvtIoStop == 0); + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, "WdfIoQueueCreate failed %!STATUS!", status); + goto End; + } + + // + // Control devices must notify WDF when they are done initializing. I/O is + // rejected until this call is made. + // + WdfControlFinishInitializing(controlDevice); + +End: + // + // If the device is created successfully, framework would clear the + // DeviceInit value. Otherwise device create must have failed so we + // should free the memory ourself. + // + if (DeviceInit != NULL) { + WdfDeviceInitFree(DeviceInit); + } + + return status; + +} + +VOID +NonPnpEvtDriverContextCleanup( + IN WDFOBJECT Driver + ) +/*++ +Routine Description: + + Called when the driver object is deleted during driver unload. + You can free all the resources created in DriverEntry that are + not automatically freed by the framework. + +Arguments: + + Driver - Handle to a framework driver object created in DriverEntry + +Return Value: + + NTSTATUS + +--*/ +{ + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, + "Entered NonPnpEvtDriverContextCleanup\n"); + + PAGED_CODE(); + + // + // No need to free the controldevice object explicitly because it will + // be deleted when the Driver object is deleted due to the default parent + // child relationship between Driver and ControlDevice. + // + WPP_CLEANUP( WdfDriverWdmGetDriverObject( (WDFDRIVER)Driver ) ); + +} + + + +VOID +NonPnpEvtDeviceFileCreate ( + IN WDFDEVICE Device, + IN WDFREQUEST Request, + IN WDFFILEOBJECT FileObject + ) +/*++ + +Routine Description: + + The framework calls a driver's EvtDeviceFileCreate callback + when it 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 a file. + This callback is called synchronously, 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 of IO_STACK_LOCATION for create + +Return Value: + + NT status code + +--*/ +{ + PUNICODE_STRING fileName; + UNICODE_STRING absFileName, directory; + OBJECT_ATTRIBUTES fileAttributes; + IO_STATUS_BLOCK ioStatus; + PCONTROL_DEVICE_EXTENSION devExt; + NTSTATUS status; + USHORT length = 0; + + + UNREFERENCED_PARAMETER( FileObject ); + + PAGED_CODE (); + + devExt = ControlGetData(Device); + + // + // Assume the directory is a temp directory under %windir% + // + RtlInitUnicodeString(&directory, L"\\SystemRoot\\temp"); + + // + // Parsed filename has "\" in the begining. The object manager strips + // of all "\", except one, after the device name. + // + fileName = WdfFileObjectGetFileName(FileObject); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "NonPnpEvtDeviceFileCreate %wZ%wZ", + &directory, fileName); + + // + // Find the total length of the directory + filename + // + length = directory.Length + fileName->Length; + + absFileName.Buffer = ExAllocatePoolWithTag(PagedPool, length, POOL_TAG); + if(absFileName.Buffer == NULL) { + status = STATUS_INSUFFICIENT_RESOURCES; + TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, "ExAllocatePoolWithTag failed"); + goto End; + } + absFileName.Length = 0; + absFileName.MaximumLength = length; + + status = RtlAppendUnicodeStringToString(&absFileName, &directory); + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, + "RtlAppendUnicodeStringToString failed with status %!STATUS!", + status); + goto End; + } + + status = RtlAppendUnicodeStringToString(&absFileName, fileName); + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, + "RtlAppendUnicodeStringToString failed with status %!STATUS!", + status); + goto End; + } + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "Absolute Filename %wZ", &absFileName); + + InitializeObjectAttributes( &fileAttributes, + &absFileName, + OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, + NULL, // RootDirectory + NULL // SecurityDescriptor + ); + + status = ZwCreateFile ( + &devExt->FileHandle, + SYNCHRONIZE | GENERIC_WRITE | GENERIC_READ, + &fileAttributes, + &ioStatus, + NULL,// alloc size = none + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ, + FILE_OPEN_IF, + FILE_SYNCHRONOUS_IO_NONALERT |FILE_NON_DIRECTORY_FILE, + NULL,// eabuffer + 0// ealength + ); + + if (!NT_SUCCESS(status)) { + + TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, + "ZwCreateFile failed with status %!STATUS!", status); + devExt->FileHandle = NULL; + } + +End: + if(absFileName.Buffer != NULL) { + ExFreePool(absFileName.Buffer); + } + + WdfRequestComplete(Request, status); + + return; +} + + +VOID +NonPnpEvtFileClose ( + 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: + + VOID + +--*/ +{ + PCONTROL_DEVICE_EXTENSION devExt; + + PAGED_CODE (); + + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "NonPnpEvtFileClose\n"); + + devExt = ControlGetData(WdfFileObjectGetDevice(FileObject)); + + if(devExt->FileHandle) { + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, + "Closing File Handle %p", devExt->FileHandle); + ZwClose(devExt->FileHandle); + } + + return; +} + + +VOID +FileEvtIoRead( + IN WDFQUEUE Queue, + IN WDFREQUEST Request, + IN size_t Length + ) +/*++ + +Routine Description: + + This event is called when the framework receives IRP_MJ_READ requests. + We will just read the file. + +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. + Queue is by default configured to fail zero length read & write requests. + +Return Value: + + None. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + PVOID outBuf; + IO_STATUS_BLOCK ioStatus; + PCONTROL_DEVICE_EXTENSION devExt; + FILE_POSITION_INFORMATION position; + ULONG_PTR bytesRead = 0; + size_t bufLength; + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_RW, "FileEvtIoRead: Request: 0x%p, Queue: 0x%p\n", + Request, Queue); + + PAGED_CODE (); + + // + // Get the request buffer. Since the device is set to do buffered + // I/O, this function will retrieve Irp->AssociatedIrp.SystemBuffer. + // + status = WdfRequestRetrieveOutputBuffer(Request, 0, &outBuf, &bufLength); + if(!NT_SUCCESS(status)) { + WdfRequestComplete(Request, status); + return; + + } + + devExt = ControlGetData(WdfIoQueueGetDevice(Queue)); + + if(devExt->FileHandle) { + + // + // Set the file position to the beginning of the file. + // + position.CurrentByteOffset.QuadPart = 0; + status = ZwSetInformationFile(devExt->FileHandle, + &ioStatus, + &position, + sizeof(FILE_POSITION_INFORMATION), + FilePositionInformation); + if (NT_SUCCESS(status)) { + + status = ZwReadFile (devExt->FileHandle, + NULL,// Event, + NULL,// PIO_APC_ROUTINE ApcRoutine + NULL,// PVOID ApcContext + &ioStatus, + outBuf, + (ULONG)Length, + 0, // ByteOffset + NULL // Key + ); + + if (!NT_SUCCESS(status)) { + + TraceEvents(TRACE_LEVEL_ERROR, DBG_RW, + "ZwReadFile failed with status 0x%x", + status); + } + + status = ioStatus.Status; + bytesRead = ioStatus.Information; + } + } + + WdfRequestCompleteWithInformation(Request, status, bytesRead); + +} + + + +VOID +FileEvtIoWrite( + IN WDFQUEUE Queue, + IN WDFREQUEST Request, + IN size_t Length + ) +/*++ + +Routine Description: + + 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. + + Length - number of bytes to be written. + Queue is by default configured to fail zero length read & write requests. + + +Return Value: + + None +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + PVOID inBuf; + IO_STATUS_BLOCK ioStatus; + PCONTROL_DEVICE_EXTENSION devExt; + FILE_POSITION_INFORMATION position; + ULONG_PTR bytesWritten = 0; + size_t bufLength; + + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_RW, "FileEvtIoWrite: Request: 0x%p, Queue: 0x%p\n", + Request, Queue); + PAGED_CODE (); + + // + // Get the request buffer. Since the device is set to do buffered + // I/O, this function will retrieve Irp->AssociatedIrp.SystemBuffer. + // + status = WdfRequestRetrieveInputBuffer(Request, 0, &inBuf, &bufLength); + if(!NT_SUCCESS(status)) { + WdfRequestComplete(Request, status); + return; + + } + + devExt = ControlGetData(WdfIoQueueGetDevice(Queue)); + + if(devExt->FileHandle) { + + // + // Set the file position to the beginning of the file. + // + position.CurrentByteOffset.QuadPart = 0; + + status = ZwSetInformationFile(devExt->FileHandle, + &ioStatus, + &position, + sizeof(FILE_POSITION_INFORMATION), + FilePositionInformation); + if (NT_SUCCESS(status)) + { + + status = ZwWriteFile(devExt->FileHandle, + NULL,// Event, + NULL,// PIO_APC_ROUTINE ApcRoutine + NULL,// PVOID ApcContext + &ioStatus, + inBuf, + (ULONG)Length, + 0, // ByteOffset + NULL // Key + ); + if (!NT_SUCCESS(status)) + { + TraceEvents(TRACE_LEVEL_ERROR, DBG_RW, + "ZwWriteFile failed with status 0x%x", + status); + } + + status = ioStatus.Status; + bytesWritten = ioStatus.Information; + } + } + + WdfRequestCompleteWithInformation(Request, status, bytesWritten); + +} + +VOID +FileEvtIoDeviceControl( + 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;// Assume success + PCHAR inBuf = NULL, outBuf = NULL; // pointer to Input and output buffer + PCHAR data = "this String is from Device Driver !!!"; + ULONG datalen = (ULONG) strlen(data)+1;//Length of data including null + PCHAR buffer = NULL; + PREQUEST_CONTEXT reqContext = NULL; + size_t bufSize; + + UNREFERENCED_PARAMETER( Queue ); + + PAGED_CODE(); + + if(!OutputBufferLength || !InputBufferLength) + { + WdfRequestComplete(Request, STATUS_INVALID_PARAMETER); + return; + } + + // + // Determine which I/O control code was specified. + // + + switch (IoControlCode) + { + case IOCTL_NONPNP_METHOD_BUFFERED: + + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL, "Called IOCTL_NONPNP_METHOD_BUFFERED\n"); + + // + // For bufffered ioctls WdfRequestRetrieveInputBuffer & + // WdfRequestRetrieveOutputBuffer return the same buffer + // pointer (Irp->AssociatedIrp.SystemBuffer), so read the + // content of the buffer before writing to it. + // + status = WdfRequestRetrieveInputBuffer(Request, 0, &inBuf, &bufSize); + if(!NT_SUCCESS(status)) { + status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + ASSERT(bufSize == InputBufferLength); + + // + // Read the input buffer content. + // We are using the following function to print characters instead + // TraceEvents with %s format because the string we get may or + // may not be null terminated. The buffer may contain non-printable + // characters also. + // + Hexdump((TRACE_LEVEL_VERBOSE, DBG_IOCTL, "Data from User : %!HEXDUMP!\n", + log_xstr(inBuf, (USHORT)InputBufferLength))); + PrintChars(inBuf, InputBufferLength ); + + + status = WdfRequestRetrieveOutputBuffer(Request, 0, &outBuf, &bufSize); + if(!NT_SUCCESS(status)) { + status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + ASSERT(bufSize == OutputBufferLength); + + // + // Writing to the buffer over-writes the input buffer content + // + + RtlCopyMemory(outBuf, data, OutputBufferLength); + + Hexdump((TRACE_LEVEL_VERBOSE, DBG_IOCTL, "Data to User : %!HEXDUMP!\n", + log_xstr(outBuf, (USHORT)datalen))); + PrintChars(outBuf, datalen ); + + // + // Assign the length of the data copied to IoStatus.Information + // of the request and complete the request. + // + WdfRequestSetInformation(Request, + OutputBufferLength < datalen? OutputBufferLength:datalen); + + // + // When the request is completed the content of the SystemBuffer + // is copied to the User output buffer and the SystemBuffer is + // is freed. + // + + break; + + + case IOCTL_NONPNP_METHOD_IN_DIRECT: + + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL, "Called IOCTL_NONPNP_METHOD_IN_DIRECT\n"); + + // + // Get the Input buffer. WdfRequestRetrieveInputBuffer returns + // Irp->AssociatedIrp.SystemBuffer. + // + status = WdfRequestRetrieveInputBuffer(Request, 0, &inBuf, &bufSize); + if(!NT_SUCCESS(status)) { + status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + ASSERT(bufSize == InputBufferLength); + + Hexdump((TRACE_LEVEL_VERBOSE, DBG_IOCTL, "Data from User : %!HEXDUMP!\n", + log_xstr(inBuf, (USHORT)InputBufferLength))); + PrintChars(inBuf, InputBufferLength); + + // + // Get the output buffer. Framework calls MmGetSystemAddressForMdlSafe + // on the Irp->MdlAddress and returns the system address. + // Oddity: For this method, this buffer is intended for transfering data + // from the application to the driver. + // + + status = WdfRequestRetrieveOutputBuffer(Request, 0, &buffer, &bufSize); + if(!NT_SUCCESS(status)) { + break; + } + + ASSERT(bufSize == OutputBufferLength); + + Hexdump((TRACE_LEVEL_VERBOSE, DBG_IOCTL, "Data from User in OutputBuffer: %!HEXDUMP!\n", + log_xstr(buffer, (USHORT)OutputBufferLength))); + PrintChars(buffer, OutputBufferLength); + + // + // Return total bytes read from the output buffer. + // Note OutputBufferLength = MmGetMdlByteCount(Irp->MdlAddress) + // + + WdfRequestSetInformation(Request, OutputBufferLength); + + // + // NOTE: Changes made to the SystemBuffer are not copied + // to the user input buffer by the I/O manager + // + + break; + + case IOCTL_NONPNP_METHOD_OUT_DIRECT: + + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL, "Called IOCTL_NONPNP_METHOD_OUT_DIRECT\n"); + + // + // Get the Input buffer. WdfRequestRetrieveInputBuffer returns + // Irp->AssociatedIrp.SystemBuffer. + // + status = WdfRequestRetrieveInputBuffer(Request, 0, &inBuf, &bufSize); + if(!NT_SUCCESS(status)) { + status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + ASSERT(bufSize == InputBufferLength); + + Hexdump((TRACE_LEVEL_VERBOSE, DBG_IOCTL, "Data from User : %!HEXDUMP!\n", + log_xstr(inBuf, (USHORT)InputBufferLength))); + PrintChars(inBuf, InputBufferLength); + + // + // Get the output buffer. Framework calls MmGetSystemAddressForMdlSafe + // on the Irp->MdlAddress and returns the system address. + // For this method, this buffer is intended for transfering data from the + // driver to the application. + // + status = WdfRequestRetrieveOutputBuffer(Request, 0, &buffer, &bufSize); + if(!NT_SUCCESS(status)) { + break; + } + + ASSERT(bufSize == OutputBufferLength); + + // + // Write data to be sent to the user in this buffer + // + RtlCopyMemory(buffer, data, OutputBufferLength); + + Hexdump((TRACE_LEVEL_VERBOSE, DBG_IOCTL, "Data to User : %!HEXDUMP!\n", + log_xstr(buffer, (USHORT)datalen))); + PrintChars(buffer, datalen); + + WdfRequestSetInformation(Request, + OutputBufferLength < datalen? OutputBufferLength: datalen); + + // + // NOTE: Changes made to the SystemBuffer are not copied + // to the user input buffer by the I/O manager + // + + break; + + case IOCTL_NONPNP_METHOD_NEITHER: + { + size_t inBufLength, outBufLength; + + // + // The NonPnpEvtDeviceIoInCallerContext has already probe and locked the + // pages and mapped the user buffer into system address space and + // stored memory buffer pointers in the request context. We can get the + // buffer pointer by calling WdfMemoryGetBuffer. + // + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL, "Called IOCTL_NONPNP_METHOD_NEITHER\n"); + + reqContext = GetRequestContext(Request); + + inBuf = WdfMemoryGetBuffer(reqContext->InputMemoryBuffer, &inBufLength); + outBuf = WdfMemoryGetBuffer(reqContext->OutputMemoryBuffer, &outBufLength); + + if(inBuf == NULL || outBuf == NULL) { + status = STATUS_INVALID_PARAMETER; + } + + ASSERT(inBufLength == InputBufferLength); + ASSERT(outBufLength == OutputBufferLength); + + // + // Now you can safely read the data from the buffer in any arbitrary + // context. + // + Hexdump((TRACE_LEVEL_VERBOSE, DBG_IOCTL, "Data from User : %!HEXDUMP!\n", + log_xstr(inBuf, (USHORT)inBufLength))); + PrintChars(inBuf, inBufLength); + + // + // Write to the buffer in any arbitrary context. + // + RtlCopyMemory(outBuf, data, outBufLength); + + Hexdump((TRACE_LEVEL_VERBOSE, DBG_IOCTL, "Data to User : %!HEXDUMP!\n", + log_xstr(outBuf, (USHORT)datalen))); + PrintChars(outBuf, datalen); + + // + // Assign the length of the data copied to IoStatus.Information + // of the Irp and complete the Irp. + // + WdfRequestSetInformation(Request, + outBufLength < datalen? outBufLength:datalen); + + break; + } + default: + + // + // The specified I/O control code is unrecognized by this driver. + // + status = STATUS_INVALID_DEVICE_REQUEST; + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, "ERROR: unrecognized IOCTL %x\n", IoControlCode); + break; + } + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL, "Completing Request %p with status %X", + Request, status ); + + WdfRequestComplete( Request, status); + +} + +VOID +NonPnpEvtDeviceIoInCallerContext( + IN WDFDEVICE Device, + IN WDFREQUEST Request + ) +/*++ +Routine Description: + + This I/O in-process callback is called in the calling threads context/address + space before the request is subjected to any framework locking or queueing + scheme based on the device pnp/power or locking attributes set by the + driver. The process context of the calling app is guaranteed as long as + this driver is a top-level driver and no other filter driver is attached + to it. + + This callback is only required if you are handling method-neither IOCTLs, + or want to process requests in the context of the calling process. + + Driver developers should avoid defining neither IOCTLs and access user + buffers, and use much safer I/O tranfer methods such as buffered I/O + or direct I/O. + +Arguments: + + Device - Handle to a framework device object. + + Request - Handle to a framework request object. Framework calls + PreProcess callback only for Read/Write/ioctls and internal + ioctl requests. + +Return Value: + + VOID + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + PREQUEST_CONTEXT reqContext = NULL; + WDF_OBJECT_ATTRIBUTES attributes; + WDF_REQUEST_PARAMETERS params; + size_t inBufLen, outBufLen; + PVOID inBuf, outBuf; + + PAGED_CODE(); + + WDF_REQUEST_PARAMETERS_INIT(¶ms); + + WdfRequestGetParameters(Request, ¶ms ); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL, "Entered NonPnpEvtDeviceIoInCallerContext %p \n", + Request); + + // + // Check to see whether we have recevied a METHOD_NEITHER IOCTL. if not + // just send the request back to framework because we aren't doing + // any pre-processing in the context of the calling thread process. + // + if(!(params.Type == WdfRequestTypeDeviceControl && + params.Parameters.DeviceIoControl.IoControlCode == + IOCTL_NONPNP_METHOD_NEITHER)) { + // + // Forward it for processing by the I/O package + // + status = WdfDeviceEnqueueRequest(Device, Request); + if( !NT_SUCCESS(status) ) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, + "Error forwarding Request 0x%x", status); + goto End; + } + + return; + } + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL, "EvtIoPreProcess: received METHOD_NEITHER ioctl \n"); + + // + // In this type of transfer, the I/O manager assigns the user input + // to Type3InputBuffer and the output buffer to UserBuffer of the Irp. + // The I/O manager doesn't copy or map the buffers to the kernel + // buffers. + // + status = WdfRequestRetrieveUnsafeUserInputBuffer(Request, 0, &inBuf, &inBufLen); + if(!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, + "Error WdfRequestRetrieveUnsafeUserInputBuffer failed 0x%x", status); + goto End; + } + + status = WdfRequestRetrieveUnsafeUserOutputBuffer(Request, 0, &outBuf, &outBufLen); + if(!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, + "Error WdfRequestRetrieveUnsafeUserOutputBuffer failed 0x%x", status); + goto End; + } + + // + // Allocate a context for this request so that we can store the memory + // objects created for input and output buffer. + // + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, REQUEST_CONTEXT); + + status = WdfObjectAllocateContext(Request, &attributes, &reqContext); + if(!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, + "Error WdfObjectAllocateContext failed 0x%x", status); + goto End; + } + + // + // WdfRequestProbleAndLockForRead/Write function checks to see + // whether the caller in the right thread context, creates an MDL, + // probe and locks the pages, and map the MDL to system address + // space and finally creates a WDFMEMORY object representing this + // system buffer address. This memory object is associated with the + // request. So it will be freed when the request is completed. If we + // are accessing this memory buffer else where, we should store these + // pointers in the request context. + // + + #pragma prefast(suppress:6387, "If inBuf==NULL at this point, then inBufLen==0") + status = WdfRequestProbeAndLockUserBufferForRead(Request, + inBuf, + inBufLen, + &reqContext->InputMemoryBuffer); + + if(!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, + "Error WdfRequestProbeAndLockUserBufferForRead failed 0x%x", status); + goto End; + } + + #pragma prefast(suppress:6387, "If outBuf==NULL at this point, then outBufLen==0") + status = WdfRequestProbeAndLockUserBufferForWrite(Request, + outBuf, + outBufLen, + &reqContext->OutputMemoryBuffer); + if(!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, + "Error WdfRequestProbeAndLockUserBufferForWrite failed 0x%x", status); + goto End; + } + + // + // Finally forward it for processing by the I/O package + // + status = WdfDeviceEnqueueRequest(Device, Request); + if(!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, + "Error WdfDeviceEnqueueRequest failed 0x%x", status); + goto End; + } + + return; + +End: + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL, "EvtIoPreProcess failed %x \n", status); + WdfRequestComplete(Request, status); + return; +} + +VOID +NonPnpShutdown( + WDFDEVICE Device + ) +/*++ + +Routine Description: + Callback invoked when the machine is shutting down. If you register for + a last chance shutdown notification you cannot do the following: + o Call any pageable routines + o Access pageable memory + o Perform any file I/O operations + + If you register for a normal shutdown notification, all of these are + available to you. + + This function implementation does nothing, but if you had any outstanding + file handles open, this is where you would close them. + +Arguments: + Device - The device which registered the notification during init + +Return Value: + None + + --*/ + +{ + UNREFERENCED_PARAMETER(Device); + return; +} + + +VOID +NonPnpEvtDriverUnload( + IN WDFDRIVER Driver + ) +/*++ +Routine Description: + + Called by the I/O subsystem just before unloading the driver. + You can free the resources created in the DriverEntry either + in this routine or in the EvtDriverContextCleanup callback. + +Arguments: + + Driver - Handle to a framework driver object created in DriverEntry + +Return Value: + + NTSTATUS + +--*/ +{ + UNREFERENCED_PARAMETER(Driver); + + PAGED_CODE(); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "Entered NonPnpDriverUnload\n"); + + return; +} + +VOID +PrintChars( + _In_reads_(CountChars) PCHAR BufferAddress, + _In_ size_t CountChars + ) +{ + if (CountChars) { + + while (CountChars--) { + + if (*BufferAddress > 31 + && *BufferAddress != 127) { + + KdPrint (( "%c", *BufferAddress) ); + + } else { + + KdPrint(( ".") ); + + } + BufferAddress++; + } + KdPrint (("\n")); + } + return; +} + diff --git a/general/ioctl/kmdf/sys/nonpnp.h b/general/ioctl/kmdf/sys/nonpnp.h new file mode 100644 index 000000000..281760388 --- /dev/null +++ b/general/ioctl/kmdf/sys/nonpnp.h @@ -0,0 +1,90 @@ +/*++ + +Copyright (c) 1997 Microsoft Corporation + +Module Name: + + nonpnp.h + +Abstract: + + Contains function prototypes and includes other neccessary header files. + +Environment: + + Kernel mode only. + +--*/ + +#include +#include + +#define NTSTRSAFE_LIB +#include +#include // for SDDLs +#include "public.h" // contains IOCTL definitions +#include "Trace.h" // contains macros for WPP tracing + +#define NTDEVICE_NAME_STRING L"\\Device\\NONPNP" +#define SYMBOLIC_NAME_STRING L"\\DosDevices\\NONPNP" +#define POOL_TAG 'ELIF' + +typedef struct _CONTROL_DEVICE_EXTENSION { + + HANDLE FileHandle; // Store your control data here + +} CONTROL_DEVICE_EXTENSION, *PCONTROL_DEVICE_EXTENSION; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(CONTROL_DEVICE_EXTENSION, + ControlGetData) + +// +// Following request context is used only for the method-neither ioctl case. +// +typedef struct _REQUEST_CONTEXT { + + WDFMEMORY InputMemoryBuffer; + WDFMEMORY OutputMemoryBuffer; + +} REQUEST_CONTEXT, *PREQUEST_CONTEXT; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(REQUEST_CONTEXT, GetRequestContext) + +// +// Device driver routine declarations. +// + +DRIVER_INITIALIZE DriverEntry; + +// +// Don't use EVT_WDF_DRIVER_DEVICE_ADD for NonPnpDeviceAdd even though +// the signature is same because this is not an event called by the +// framework. +// +NTSTATUS +NonPnpDeviceAdd( + IN WDFDRIVER Driver, + IN PWDFDEVICE_INIT DeviceInit + ); + +EVT_WDF_DRIVER_UNLOAD NonPnpEvtDriverUnload; + +EVT_WDF_DEVICE_CONTEXT_CLEANUP NonPnpEvtDriverContextCleanup; +EVT_WDF_DEVICE_SHUTDOWN_NOTIFICATION NonPnpShutdown; + +EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL FileEvtIoDeviceControl; +EVT_WDF_IO_QUEUE_IO_READ FileEvtIoRead; +EVT_WDF_IO_QUEUE_IO_WRITE FileEvtIoWrite; + +EVT_WDF_IO_IN_CALLER_CONTEXT NonPnpEvtDeviceIoInCallerContext; +EVT_WDF_DEVICE_FILE_CREATE NonPnpEvtDeviceFileCreate; +EVT_WDF_FILE_CLOSE NonPnpEvtFileClose; + +VOID +PrintChars( + _In_reads_(CountChars) PCHAR BufferAddress, + _In_ size_t CountChars + ); + +#pragma warning(disable:4127) + diff --git a/general/ioctl/kmdf/sys/nonpnp.rc b/general/ioctl/kmdf/sys/nonpnp.rc new file mode 100644 index 000000000..28e9a0e38 --- /dev/null +++ b/general/ioctl/kmdf/sys/nonpnp.rc @@ -0,0 +1,10 @@ +#include + +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "Sample Non-PNP Driver using WDF" +#define VER_INTERNALNAME_STR "NONPNP.sys" + +#include "common.ver" diff --git a/general/ioctl/kmdf/sys/nonpnp.vcxproj b/general/ioctl/kmdf/sys/nonpnp.vcxproj new file mode 100644 index 000000000..615ccb8ee --- /dev/null +++ b/general/ioctl/kmdf/sys/nonpnp.vcxproj @@ -0,0 +1,196 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {B622D542-4942-46A1-B976-F530446DD6A5} + $(MSBuildProjectName) + 1 + Debug + Win32 + {AC100EE8-8981-47E5-ABCA-B8DCCFFA5682} + + + + 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 + + + true + true + TraceEvents(LEVEL,FLAGS,MSG,...);Hexdump((LEVEL,FLAGS,MSG,...)) + {km-WdfDefault.tpl}*.tmh + + + + nonpnp + + + nonpnp + + + nonpnp + + + nonpnp + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ntstrsafe.lib;$(DDK_LIB_PATH)\wdmsec.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;$(DDK_LIB_PATH)\wdmsec.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;$(DDK_LIB_PATH)\wdmsec.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;$(DDK_LIB_PATH)\wdmsec.lib + + + %(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE + %(AdditionalIncludeDirectories);.. + + + + + %(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE + %(AdditionalIncludeDirectories);.. + + + %(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE + %(AdditionalIncludeDirectories);.. + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/general/ioctl/kmdf/sys/nonpnp.vcxproj.Filters b/general/ioctl/kmdf/sys/nonpnp.vcxproj.Filters new file mode 100644 index 000000000..eb46c5bba --- /dev/null +++ b/general/ioctl/kmdf/sys/nonpnp.vcxproj.Filters @@ -0,0 +1,31 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {D4EF066B-A145-401A-8669-BAAC70BA8F50} + + + h;hpp;hxx;hm;inl;inc;xsd + {C68D949D-F770-4FF0-883D-729F5284E601} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {724D8E55-13DB-47B2-AB8D-3A86DF521511} + + + inf;inv;inx;mof;mc; + {A4CB0903-2F69-4A15-8F3D-AF05EFE1323F} + + + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/general/ioctl/kmdf/sys/public.h b/general/ioctl/kmdf/sys/public.h new file mode 100644 index 000000000..4d53666e0 --- /dev/null +++ b/general/ioctl/kmdf/sys/public.h @@ -0,0 +1,53 @@ +/*++ + +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: + + + 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 FILEIO_TYPE 40001 +// +// The IOCTL function codes from 0x800 to 0xFFF are for customer use. +// +#define IOCTL_NONPNP_METHOD_IN_DIRECT \ + CTL_CODE( FILEIO_TYPE, 0x900, METHOD_IN_DIRECT, FILE_ANY_ACCESS ) + +#define IOCTL_NONPNP_METHOD_OUT_DIRECT \ + CTL_CODE( FILEIO_TYPE, 0x901, METHOD_OUT_DIRECT , FILE_ANY_ACCESS ) + +#define IOCTL_NONPNP_METHOD_BUFFERED \ + CTL_CODE( FILEIO_TYPE, 0x902, METHOD_BUFFERED, FILE_ANY_ACCESS ) + +#define IOCTL_NONPNP_METHOD_NEITHER \ + CTL_CODE( FILEIO_TYPE, 0x903, METHOD_NEITHER , FILE_ANY_ACCESS ) + + +#define DRIVER_FUNC_INSTALL 0x01 +#define DRIVER_FUNC_REMOVE 0x02 + +#define DRIVER_NAME "NONPNP" +#define DEVICE_NAME "\\\\.\\NONPNP\\nonpnpsamp.log" diff --git a/general/ioctl/kmdf/sys/trace.h b/general/ioctl/kmdf/sys/trace.h new file mode 100644 index 000000000..c9459ed23 --- /dev/null +++ b/general/ioctl/kmdf/sys/trace.h @@ -0,0 +1,68 @@ +/*++ + +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 + +--*/ + +// +// 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. +// +// {71ae54db-0862-41bf-a24f-5330cec3c7f6} +// +#define WPP_CHECK_FOR_NULL_STRING //to prevent exceptions due to NULL strings + +#define WPP_CONTROL_GUIDS \ + WPP_DEFINE_CONTROL_GUID( FileIoTraceGuid, \ + (71ae54db,0862,41bf,a24f,5330cec3c7f6), \ + WPP_DEFINE_BIT(DBG_INIT) \ + WPP_DEFINE_BIT(DBG_RW) \ + WPP_DEFINE_BIT(DBG_IOCTL) \ + ) + +#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: +// +// Hexdump((FLAG,"%!HEXDUMP!\n", log_xstr(buffersize,(char *)buffer) )); +// +// +#define WPP_LOGHEXDUMP(x) WPP_LOGPAIR(2, &((x)._len)) WPP_LOGPAIR((x)._len, (x)._buf) + + diff --git a/general/ioctl/wdm/exe/ioctlapp.vcxproj b/general/ioctl/wdm/exe/ioctlapp.vcxproj index a4fe1dc96..9157b73ee 100644 --- a/general/ioctl/wdm/exe/ioctlapp.vcxproj +++ b/general/ioctl/wdm/exe/ioctlapp.vcxproj @@ -19,11 +19,11 @@ - {0F1154FD-BDCC-4DCD-A452-C04E4AD94CEA} + {2B0BDC9E-ABF1-48DD-9814-7553B8D52725} $(MSBuildProjectName) Debug Win32 - {A8666C84-4408-4F56-834E-989B3B03ED5A} + {CD28B759-C8BC-4F51-AB5E-AFF59331509B} @@ -182,7 +182,6 @@ - diff --git a/general/ioctl/wdm/exe/ioctlapp.vcxproj.Filters b/general/ioctl/wdm/exe/ioctlapp.vcxproj.Filters index af420f3f3..4129898eb 100644 --- a/general/ioctl/wdm/exe/ioctlapp.vcxproj.Filters +++ b/general/ioctl/wdm/exe/ioctlapp.vcxproj.Filters @@ -3,15 +3,15 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {96F80F7E-43A8-4FF2-833B-0F4E0B486149} + {73262017-91DD-45A8-8A68-9CCCAB2CA1C0} h;hpp;hxx;hm;inl;inc;xsd - {49685EE8-840B-4F1C-92CA-EF94838E1143} + {744376AF-8B58-45ED-828B-AA01CE10E194} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {F87C331A-2013-470A-A6AC-100426820F7F} + {CB96AC92-B6DE-4B43-A7B4-EEB29C73C2BB} diff --git a/general/ioctl/wdm/ioctl.sln b/general/ioctl/wdm/ioctl.sln index ff5233dca..d1d7b7fea 100644 --- a/general/ioctl/wdm/ioctl.sln +++ b/general/ioctl/wdm/ioctl.sln @@ -3,13 +3,13 @@ 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", "{784A1AB1-AE41-4D83-99A8-82848B129B48}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Exe", "Exe", "{05ABA65F-2F21-4F5E-A1D5-DD008F644663}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sys", "Sys", "{8D7246D9-D426-4517-9631-BCDC343A98A5}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sys", "Sys", "{D5D0F833-0D75-4099-9A7F-B016159FC352}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ioctlapp", "exe\ioctlapp.vcxproj", "{0F1154FD-BDCC-4DCD-A452-C04E4AD94CEA}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ioctlapp", "exe\ioctlapp.vcxproj", "{2B0BDC9E-ABF1-48DD-9814-7553B8D52725}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sioctl", "sys\sioctl.vcxproj", "{7B626BD2-8EA8-42C1-BCC4-BBBBF52E5E68}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sioctl", "sys\sioctl.vcxproj", "{8A61FFBC-9D75-43B0-9715-B75E1BD3BFC0}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -19,28 +19,28 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {0F1154FD-BDCC-4DCD-A452-C04E4AD94CEA}.Debug|Win32.ActiveCfg = Debug|Win32 - {0F1154FD-BDCC-4DCD-A452-C04E4AD94CEA}.Debug|Win32.Build.0 = Debug|Win32 - {0F1154FD-BDCC-4DCD-A452-C04E4AD94CEA}.Release|Win32.ActiveCfg = Release|Win32 - {0F1154FD-BDCC-4DCD-A452-C04E4AD94CEA}.Release|Win32.Build.0 = Release|Win32 - {0F1154FD-BDCC-4DCD-A452-C04E4AD94CEA}.Debug|x64.ActiveCfg = Debug|x64 - {0F1154FD-BDCC-4DCD-A452-C04E4AD94CEA}.Debug|x64.Build.0 = Debug|x64 - {0F1154FD-BDCC-4DCD-A452-C04E4AD94CEA}.Release|x64.ActiveCfg = Release|x64 - {0F1154FD-BDCC-4DCD-A452-C04E4AD94CEA}.Release|x64.Build.0 = Release|x64 - {7B626BD2-8EA8-42C1-BCC4-BBBBF52E5E68}.Debug|Win32.ActiveCfg = Debug|Win32 - {7B626BD2-8EA8-42C1-BCC4-BBBBF52E5E68}.Debug|Win32.Build.0 = Debug|Win32 - {7B626BD2-8EA8-42C1-BCC4-BBBBF52E5E68}.Release|Win32.ActiveCfg = Release|Win32 - {7B626BD2-8EA8-42C1-BCC4-BBBBF52E5E68}.Release|Win32.Build.0 = Release|Win32 - {7B626BD2-8EA8-42C1-BCC4-BBBBF52E5E68}.Debug|x64.ActiveCfg = Debug|x64 - {7B626BD2-8EA8-42C1-BCC4-BBBBF52E5E68}.Debug|x64.Build.0 = Debug|x64 - {7B626BD2-8EA8-42C1-BCC4-BBBBF52E5E68}.Release|x64.ActiveCfg = Release|x64 - {7B626BD2-8EA8-42C1-BCC4-BBBBF52E5E68}.Release|x64.Build.0 = Release|x64 + {2B0BDC9E-ABF1-48DD-9814-7553B8D52725}.Debug|Win32.ActiveCfg = Debug|Win32 + {2B0BDC9E-ABF1-48DD-9814-7553B8D52725}.Debug|Win32.Build.0 = Debug|Win32 + {2B0BDC9E-ABF1-48DD-9814-7553B8D52725}.Release|Win32.ActiveCfg = Release|Win32 + {2B0BDC9E-ABF1-48DD-9814-7553B8D52725}.Release|Win32.Build.0 = Release|Win32 + {2B0BDC9E-ABF1-48DD-9814-7553B8D52725}.Debug|x64.ActiveCfg = Debug|x64 + {2B0BDC9E-ABF1-48DD-9814-7553B8D52725}.Debug|x64.Build.0 = Debug|x64 + {2B0BDC9E-ABF1-48DD-9814-7553B8D52725}.Release|x64.ActiveCfg = Release|x64 + {2B0BDC9E-ABF1-48DD-9814-7553B8D52725}.Release|x64.Build.0 = Release|x64 + {8A61FFBC-9D75-43B0-9715-B75E1BD3BFC0}.Debug|Win32.ActiveCfg = Debug|Win32 + {8A61FFBC-9D75-43B0-9715-B75E1BD3BFC0}.Debug|Win32.Build.0 = Debug|Win32 + {8A61FFBC-9D75-43B0-9715-B75E1BD3BFC0}.Release|Win32.ActiveCfg = Release|Win32 + {8A61FFBC-9D75-43B0-9715-B75E1BD3BFC0}.Release|Win32.Build.0 = Release|Win32 + {8A61FFBC-9D75-43B0-9715-B75E1BD3BFC0}.Debug|x64.ActiveCfg = Debug|x64 + {8A61FFBC-9D75-43B0-9715-B75E1BD3BFC0}.Debug|x64.Build.0 = Debug|x64 + {8A61FFBC-9D75-43B0-9715-B75E1BD3BFC0}.Release|x64.ActiveCfg = Release|x64 + {8A61FFBC-9D75-43B0-9715-B75E1BD3BFC0}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {0F1154FD-BDCC-4DCD-A452-C04E4AD94CEA} = {784A1AB1-AE41-4D83-99A8-82848B129B48} - {7B626BD2-8EA8-42C1-BCC4-BBBBF52E5E68} = {8D7246D9-D426-4517-9631-BCDC343A98A5} + {2B0BDC9E-ABF1-48DD-9814-7553B8D52725} = {05ABA65F-2F21-4F5E-A1D5-DD008F644663} + {8A61FFBC-9D75-43B0-9715-B75E1BD3BFC0} = {D5D0F833-0D75-4099-9A7F-B016159FC352} EndGlobalSection EndGlobal diff --git a/general/ioctl/wdm/sys/sioctl.vcxproj b/general/ioctl/wdm/sys/sioctl.vcxproj index 7941eb1d1..8b12549ae 100644 --- a/general/ioctl/wdm/sys/sioctl.vcxproj +++ b/general/ioctl/wdm/sys/sioctl.vcxproj @@ -19,11 +19,11 @@ - {7B626BD2-8EA8-42C1-BCC4-BBBBF52E5E68} + {8A61FFBC-9D75-43B0-9715-B75E1BD3BFC0} $(MSBuildProjectName) Debug Win32 - {FCA3A913-3152-480C-8F1A-78460EBE1F76} + {57EBFE5D-4194-4628-B3DB-243FA7D03AC9} @@ -126,7 +126,6 @@ - diff --git a/general/ioctl/wdm/sys/sioctl.vcxproj.Filters b/general/ioctl/wdm/sys/sioctl.vcxproj.Filters index 2c4c9583d..4f493adc7 100644 --- a/general/ioctl/wdm/sys/sioctl.vcxproj.Filters +++ b/general/ioctl/wdm/sys/sioctl.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {0293F632-CB51-45F8-8DF6-6CE8AB8DEB1D} + {E9EE9FFB-2EBC-4879-8005-029F0C26829C} h;hpp;hxx;hm;inl;inc;xsd - {8214575D-756B-4919-88AE-CDECEFA1921F} + {7C81EF4D-3554-45D3-A116-B443830349FD} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {7306FA3C-F664-4252-9566-E061F0301808} + {4B6FECB8-7A2A-4AFD-95F2-94D7C452E97E} inf;inv;inx;mof;mc; - {8721C5CF-CC9A-4480-B324-73C98BFD444A} + {D4A26FDD-3685-4E62-BFD4-51FD89171C68} diff --git a/general/obcallback/control/ObCallbackTestCtrl.vcxproj b/general/obcallback/control/ObCallbackTestCtrl.vcxproj index 2f9a4a437..c7910b474 100644 --- a/general/obcallback/control/ObCallbackTestCtrl.vcxproj +++ b/general/obcallback/control/ObCallbackTestCtrl.vcxproj @@ -19,11 +19,11 @@ - {DB8864B4-169E-4815-9922-F552FAA028AF} + {6E438FB8-A5A5-4A81-8858-2AE023797676} $(MSBuildProjectName) Debug Win32 - {CE20F975-1A88-43A5-8976-68A8BEBDC3ED} + {9B16F24F-0454-42EA-8B35-8BE1B2FE9C7D} @@ -222,7 +222,6 @@ - diff --git a/general/obcallback/control/ObCallbackTestCtrl.vcxproj.Filters b/general/obcallback/control/ObCallbackTestCtrl.vcxproj.Filters index 0850a1354..53c03cc34 100644 --- a/general/obcallback/control/ObCallbackTestCtrl.vcxproj.Filters +++ b/general/obcallback/control/ObCallbackTestCtrl.vcxproj.Filters @@ -3,15 +3,15 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {C20EE14F-B2D9-4FBF-BC7E-0DF705B150AD} + {B3C6FD4B-F6ED-46BB-8F78-6AF5432A5DD4} h;hpp;hxx;hm;inl;inc;xsd - {8C949FC8-F2A5-423F-8F4F-D215168CEC6C} + {C356DDA6-5AAE-4201-8065-C13B466DE260} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {0FB11462-6C7F-4485-9D39-632D8C9CF2FD} + {FD9ED782-F61C-46D5-9D36-20E514618602} diff --git a/general/obcallback/driver/ObCallbackTest.vcxproj b/general/obcallback/driver/ObCallbackTest.vcxproj index 2e6348ae9..c5defcfc0 100644 --- a/general/obcallback/driver/ObCallbackTest.vcxproj +++ b/general/obcallback/driver/ObCallbackTest.vcxproj @@ -19,11 +19,11 @@ - {2C2188DF-F19F-4886-B4C1-5DD95909A64A} + {43C24BD9-11B3-4793-A7F8-029602B80C96} $(MSBuildProjectName) Debug Win32 - {BBA1F9F1-637A-40C4-A643-A0766AB87027} + {03286AEF-DE78-4332-84DB-7F31CF6228D9} @@ -196,7 +196,6 @@ - diff --git a/general/obcallback/driver/ObCallbackTest.vcxproj.Filters b/general/obcallback/driver/ObCallbackTest.vcxproj.Filters index 621e0f791..2a09974a8 100644 --- a/general/obcallback/driver/ObCallbackTest.vcxproj.Filters +++ b/general/obcallback/driver/ObCallbackTest.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {030D466C-6D17-4995-AC90-E04818AEEA75} + {ECF6E7A0-2E50-45CA-AF8D-DBAA9A4F1C28} h;hpp;hxx;hm;inl;inc;xsd - {D351C05E-ED86-4240-946A-9CCAA4B2916A} + {D91FD909-AA67-488E-9E0D-54463C5160B0} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {6381A0AA-76E1-4A9F-B058-3F7A67153834} + {625071DD-8755-4C84-8237-46892091663F} inf;inv;inx;mof;mc; - {35FB336D-920E-4CCB-A47B-72D212D86AB0} + {ABAB7A03-1F6A-4667-ABE6-351CA30F3988} diff --git a/general/obcallback/obcallback.sln b/general/obcallback/obcallback.sln index 3ede4fc01..dbbcebe2e 100644 --- a/general/obcallback/obcallback.sln +++ b/general/obcallback/obcallback.sln @@ -3,13 +3,13 @@ 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", "{70CADFEA-4EA6-46ED-8C0E-966D4F3E7280}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Control", "Control", "{D04BB44A-89ED-4933-BD10-F593D3879737}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Driver", "Driver", "{4A168675-E825-4328-87A1-46ADBFB75DB5}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Driver", "Driver", "{E0BAFFAC-0DAA-40BB-B49D-9B3AFA3B21F4}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ObCallbackTestCtrl", "control\ObCallbackTestCtrl.vcxproj", "{DB8864B4-169E-4815-9922-F552FAA028AF}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ObCallbackTestCtrl", "control\ObCallbackTestCtrl.vcxproj", "{6E438FB8-A5A5-4A81-8858-2AE023797676}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ObCallbackTest", "driver\ObCallbackTest.vcxproj", "{2C2188DF-F19F-4886-B4C1-5DD95909A64A}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ObCallbackTest", "driver\ObCallbackTest.vcxproj", "{43C24BD9-11B3-4793-A7F8-029602B80C96}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -19,28 +19,28 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {DB8864B4-169E-4815-9922-F552FAA028AF}.Debug|Win32.ActiveCfg = Debug|Win32 - {DB8864B4-169E-4815-9922-F552FAA028AF}.Debug|Win32.Build.0 = Debug|Win32 - {DB8864B4-169E-4815-9922-F552FAA028AF}.Release|Win32.ActiveCfg = Release|Win32 - {DB8864B4-169E-4815-9922-F552FAA028AF}.Release|Win32.Build.0 = Release|Win32 - {DB8864B4-169E-4815-9922-F552FAA028AF}.Debug|x64.ActiveCfg = Debug|x64 - {DB8864B4-169E-4815-9922-F552FAA028AF}.Debug|x64.Build.0 = Debug|x64 - {DB8864B4-169E-4815-9922-F552FAA028AF}.Release|x64.ActiveCfg = Release|x64 - {DB8864B4-169E-4815-9922-F552FAA028AF}.Release|x64.Build.0 = Release|x64 - {2C2188DF-F19F-4886-B4C1-5DD95909A64A}.Debug|Win32.ActiveCfg = Debug|Win32 - {2C2188DF-F19F-4886-B4C1-5DD95909A64A}.Debug|Win32.Build.0 = Debug|Win32 - {2C2188DF-F19F-4886-B4C1-5DD95909A64A}.Release|Win32.ActiveCfg = Release|Win32 - {2C2188DF-F19F-4886-B4C1-5DD95909A64A}.Release|Win32.Build.0 = Release|Win32 - {2C2188DF-F19F-4886-B4C1-5DD95909A64A}.Debug|x64.ActiveCfg = Debug|x64 - {2C2188DF-F19F-4886-B4C1-5DD95909A64A}.Debug|x64.Build.0 = Debug|x64 - {2C2188DF-F19F-4886-B4C1-5DD95909A64A}.Release|x64.ActiveCfg = Release|x64 - {2C2188DF-F19F-4886-B4C1-5DD95909A64A}.Release|x64.Build.0 = Release|x64 + {6E438FB8-A5A5-4A81-8858-2AE023797676}.Debug|Win32.ActiveCfg = Debug|Win32 + {6E438FB8-A5A5-4A81-8858-2AE023797676}.Debug|Win32.Build.0 = Debug|Win32 + {6E438FB8-A5A5-4A81-8858-2AE023797676}.Release|Win32.ActiveCfg = Release|Win32 + {6E438FB8-A5A5-4A81-8858-2AE023797676}.Release|Win32.Build.0 = Release|Win32 + {6E438FB8-A5A5-4A81-8858-2AE023797676}.Debug|x64.ActiveCfg = Debug|x64 + {6E438FB8-A5A5-4A81-8858-2AE023797676}.Debug|x64.Build.0 = Debug|x64 + {6E438FB8-A5A5-4A81-8858-2AE023797676}.Release|x64.ActiveCfg = Release|x64 + {6E438FB8-A5A5-4A81-8858-2AE023797676}.Release|x64.Build.0 = Release|x64 + {43C24BD9-11B3-4793-A7F8-029602B80C96}.Debug|Win32.ActiveCfg = Debug|Win32 + {43C24BD9-11B3-4793-A7F8-029602B80C96}.Debug|Win32.Build.0 = Debug|Win32 + {43C24BD9-11B3-4793-A7F8-029602B80C96}.Release|Win32.ActiveCfg = Release|Win32 + {43C24BD9-11B3-4793-A7F8-029602B80C96}.Release|Win32.Build.0 = Release|Win32 + {43C24BD9-11B3-4793-A7F8-029602B80C96}.Debug|x64.ActiveCfg = Debug|x64 + {43C24BD9-11B3-4793-A7F8-029602B80C96}.Debug|x64.Build.0 = Debug|x64 + {43C24BD9-11B3-4793-A7F8-029602B80C96}.Release|x64.ActiveCfg = Release|x64 + {43C24BD9-11B3-4793-A7F8-029602B80C96}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {DB8864B4-169E-4815-9922-F552FAA028AF} = {70CADFEA-4EA6-46ED-8C0E-966D4F3E7280} - {2C2188DF-F19F-4886-B4C1-5DD95909A64A} = {4A168675-E825-4328-87A1-46ADBFB75DB5} + {6E438FB8-A5A5-4A81-8858-2AE023797676} = {D04BB44A-89ED-4933-BD10-F593D3879737} + {43C24BD9-11B3-4793-A7F8-029602B80C96} = {E0BAFFAC-0DAA-40BB-B49D-9B3AFA3B21F4} EndGlobalSection EndGlobal diff --git a/general/pcidrv/kmdf/HW/PCIDRV.vcxproj b/general/pcidrv/kmdf/HW/PCIDRV.vcxproj index 353263b89..f37c79b19 100644 --- a/general/pcidrv/kmdf/HW/PCIDRV.vcxproj +++ b/general/pcidrv/kmdf/HW/PCIDRV.vcxproj @@ -19,12 +19,12 @@ - {981ADEB0-2F42-4F65-B118-8553CD64ACFD} + {147B5094-8C9D-4C84-B266-297D1E03D038} $(MSBuildProjectName) 1 Debug Win32 - {4FF76CFE-2641-4D5A-983A-622FA892000D} + {31AFCCDA-DE2F-4533-AFB7-F8D0C668C5AE} @@ -346,7 +346,6 @@ - diff --git a/general/pcidrv/kmdf/HW/PCIDRV.vcxproj.Filters b/general/pcidrv/kmdf/HW/PCIDRV.vcxproj.Filters index 98b4bbfe8..ceed75711 100644 --- a/general/pcidrv/kmdf/HW/PCIDRV.vcxproj.Filters +++ b/general/pcidrv/kmdf/HW/PCIDRV.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {6D6B3E6C-B12D-41CD-B77E-CFE7772C651E} + {0952D163-791F-4123-BCD0-F58AFDBD257F} h;hpp;hxx;hm;inl;inc;xsd - {C3F0DB26-8DB1-4AC1-BAAF-D84FAA82A17A} + {AF230497-BE0C-4FA2-95A2-9BD4EFC3EB49} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {2BDDF51F-F342-422D-8E77-6D18E65A4FE3} + {38573357-97D6-4F88-9261-F38ABE26C075} inf;inv;inx;mof;mc; - {9B95345C-42E1-4A98-8A1F-71F76466D9AF} + {8DB6212C-6D77-4437-87CE-60D7E13C7314} @@ -57,9 +57,6 @@ - - Driver Files - Driver Files diff --git a/general/pcidrv/kmdf/HW/nic_init.c b/general/pcidrv/kmdf/HW/nic_init.c index 947b1d658..55f4199bf 100644 --- a/general/pcidrv/kmdf/HW/nic_init.c +++ b/general/pcidrv/kmdf/HW/nic_init.c @@ -73,6 +73,12 @@ PVOID LocalMmMapIoSpace( PAGE_READWRITE | PAGE_NOCACHE); } + // + // Supress warning that MmMapIoSpace allocates executable memory. + // This function is only used if the preferred API, MmMapIoSpaceEx + // is not present. MmMapIoSpaceEx is available starting in WIN10. + // + #pragma warning(suppress: 30029) return MmMapIoSpace(PhysicalAddress, NumberOfBytes, MmNonCached); } diff --git a/general/pcidrv/kmdf/genpci.inx b/general/pcidrv/kmdf/genpci.inx index c66464eda..9711236b2 100644 --- a/general/pcidrv/kmdf/genpci.inx +++ b/general/pcidrv/kmdf/genpci.inx @@ -20,7 +20,7 @@ Signature = "$WINDOWS NT$" Class = Sample ClassGuid = {78A1C341-4539-11d3-B88D-00C04FAD5171} -Provider = %MSFT% +Provider = %ProviderString% DriverVer = 03/20/2003,5.00.3788 CatalogFile = KmdfSamples.cat @@ -46,7 +46,7 @@ HKR,,Security,,"D:P(A;;GA;;;SY)(A;;GA;;;BA)" ;Allow generic all access to system ExcludeFromSelect = * [Manufacturer] -%MSFT%=MSFT,NT$ARCH$ +%ManufacturerString%=MSFT,NT$ARCH$ [SourceDisksFiles] pcidrv.sys = 1 @@ -111,8 +111,9 @@ KmdfLibraryVersion = $KMDFVERSION$ ;------------------------------------------------------------------------------ [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" +ProviderString = "TODO-Set-Provider" +ManufacturerString = "TODO-Set-Manufacturer" +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/pcidrv.sln b/general/pcidrv/pcidrv.sln index cfbe21e54..d7720b8ee 100644 --- a/general/pcidrv/pcidrv.sln +++ b/general/pcidrv/pcidrv.sln @@ -3,15 +3,15 @@ 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", "{1177EB4D-108A-42D1-86E1-323626CD6975}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Test", "Test", "{C5095E94-8EC9-45BE-9EDF-B080381F0ABE}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "HW", "HW", "{D8F67AFA-F8CE-4531-A387-DB50915BB347}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "HW", "HW", "{4926BA95-0E65-44E7-9A0D-0FB3190E4141}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Kmdf", "Kmdf", "{9FD6367B-8E6E-4F7D-B260-D93B3D0150B6}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Kmdf", "Kmdf", "{18525246-BE0C-4DA9-91ED-74DD067860C8}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "myping", "test\myping.vcxproj", "{E270A551-A250-4432-975F-99FC5C7271D0}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "myping", "test\myping.vcxproj", "{B04477EF-85F2-47B6-A669-39E90D52DFB9}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PCIDRV", "kmdf\HW\PCIDRV.vcxproj", "{981ADEB0-2F42-4F65-B118-8553CD64ACFD}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PCIDRV", "kmdf\HW\PCIDRV.vcxproj", "{147B5094-8C9D-4C84-B266-297D1E03D038}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -21,29 +21,29 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {E270A551-A250-4432-975F-99FC5C7271D0}.Debug|Win32.ActiveCfg = Debug|Win32 - {E270A551-A250-4432-975F-99FC5C7271D0}.Debug|Win32.Build.0 = Debug|Win32 - {E270A551-A250-4432-975F-99FC5C7271D0}.Release|Win32.ActiveCfg = Release|Win32 - {E270A551-A250-4432-975F-99FC5C7271D0}.Release|Win32.Build.0 = Release|Win32 - {E270A551-A250-4432-975F-99FC5C7271D0}.Debug|x64.ActiveCfg = Debug|x64 - {E270A551-A250-4432-975F-99FC5C7271D0}.Debug|x64.Build.0 = Debug|x64 - {E270A551-A250-4432-975F-99FC5C7271D0}.Release|x64.ActiveCfg = Release|x64 - {E270A551-A250-4432-975F-99FC5C7271D0}.Release|x64.Build.0 = Release|x64 - {981ADEB0-2F42-4F65-B118-8553CD64ACFD}.Debug|Win32.ActiveCfg = Debug|Win32 - {981ADEB0-2F42-4F65-B118-8553CD64ACFD}.Debug|Win32.Build.0 = Debug|Win32 - {981ADEB0-2F42-4F65-B118-8553CD64ACFD}.Release|Win32.ActiveCfg = Release|Win32 - {981ADEB0-2F42-4F65-B118-8553CD64ACFD}.Release|Win32.Build.0 = Release|Win32 - {981ADEB0-2F42-4F65-B118-8553CD64ACFD}.Debug|x64.ActiveCfg = Debug|x64 - {981ADEB0-2F42-4F65-B118-8553CD64ACFD}.Debug|x64.Build.0 = Debug|x64 - {981ADEB0-2F42-4F65-B118-8553CD64ACFD}.Release|x64.ActiveCfg = Release|x64 - {981ADEB0-2F42-4F65-B118-8553CD64ACFD}.Release|x64.Build.0 = Release|x64 + {B04477EF-85F2-47B6-A669-39E90D52DFB9}.Debug|Win32.ActiveCfg = Debug|Win32 + {B04477EF-85F2-47B6-A669-39E90D52DFB9}.Debug|Win32.Build.0 = Debug|Win32 + {B04477EF-85F2-47B6-A669-39E90D52DFB9}.Release|Win32.ActiveCfg = Release|Win32 + {B04477EF-85F2-47B6-A669-39E90D52DFB9}.Release|Win32.Build.0 = Release|Win32 + {B04477EF-85F2-47B6-A669-39E90D52DFB9}.Debug|x64.ActiveCfg = Debug|x64 + {B04477EF-85F2-47B6-A669-39E90D52DFB9}.Debug|x64.Build.0 = Debug|x64 + {B04477EF-85F2-47B6-A669-39E90D52DFB9}.Release|x64.ActiveCfg = Release|x64 + {B04477EF-85F2-47B6-A669-39E90D52DFB9}.Release|x64.Build.0 = Release|x64 + {147B5094-8C9D-4C84-B266-297D1E03D038}.Debug|Win32.ActiveCfg = Debug|Win32 + {147B5094-8C9D-4C84-B266-297D1E03D038}.Debug|Win32.Build.0 = Debug|Win32 + {147B5094-8C9D-4C84-B266-297D1E03D038}.Release|Win32.ActiveCfg = Release|Win32 + {147B5094-8C9D-4C84-B266-297D1E03D038}.Release|Win32.Build.0 = Release|Win32 + {147B5094-8C9D-4C84-B266-297D1E03D038}.Debug|x64.ActiveCfg = Debug|x64 + {147B5094-8C9D-4C84-B266-297D1E03D038}.Debug|x64.Build.0 = Debug|x64 + {147B5094-8C9D-4C84-B266-297D1E03D038}.Release|x64.ActiveCfg = Release|x64 + {147B5094-8C9D-4C84-B266-297D1E03D038}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {E270A551-A250-4432-975F-99FC5C7271D0} = {1177EB4D-108A-42D1-86E1-323626CD6975} - {981ADEB0-2F42-4F65-B118-8553CD64ACFD} = {D8F67AFA-F8CE-4531-A387-DB50915BB347} - {D8F67AFA-F8CE-4531-A387-DB50915BB347} = {9FD6367B-8E6E-4F7D-B260-D93B3D0150B6} + {B04477EF-85F2-47B6-A669-39E90D52DFB9} = {C5095E94-8EC9-45BE-9EDF-B080381F0ABE} + {147B5094-8C9D-4C84-B266-297D1E03D038} = {4926BA95-0E65-44E7-9A0D-0FB3190E4141} + {4926BA95-0E65-44E7-9A0D-0FB3190E4141} = {18525246-BE0C-4DA9-91ED-74DD067860C8} EndGlobalSection EndGlobal diff --git a/general/pcidrv/test/myping.vcxproj b/general/pcidrv/test/myping.vcxproj index 9bea4622e..921944fcc 100644 --- a/general/pcidrv/test/myping.vcxproj +++ b/general/pcidrv/test/myping.vcxproj @@ -19,11 +19,11 @@ - {E270A551-A250-4432-975F-99FC5C7271D0} + {B04477EF-85F2-47B6-A669-39E90D52DFB9} $(MSBuildProjectName) Debug Win32 - {10CF0CFF-7ED4-4C36-B12F-E6AE18422A4E} + {7BAACFF2-0887-4947-85E0-1A609F5CAA8A} @@ -187,7 +187,6 @@ - diff --git a/general/pcidrv/test/myping.vcxproj.Filters b/general/pcidrv/test/myping.vcxproj.Filters index cf2b75b4a..c3c1c20d8 100644 --- a/general/pcidrv/test/myping.vcxproj.Filters +++ b/general/pcidrv/test/myping.vcxproj.Filters @@ -3,15 +3,15 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {0D2B8F8A-7DF3-4F12-8A73-720F90D3AD69} + {E227B2E2-02E5-416E-87EA-041459F6A688} h;hpp;hxx;hm;inl;inc;xsd - {CF7CB9C0-EAFC-44C5-8277-390F35F3D21E} + {3723BC8D-C247-4CC3-9752-9E4A013C403A} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {A6BEDE27-A5E1-46BF-BA34-C345F92F4873} + {82B8C96C-7352-4531-A71B-137B4FD15E6C} diff --git a/general/perfcounters/kcs/kcs.sln b/general/perfcounters/kcs/kcs.sln index 2d6fe420d..884b1df4a 100644 --- a/general/perfcounters/kcs/kcs.sln +++ b/general/perfcounters/kcs/kcs.sln @@ -3,7 +3,7 @@ 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", "{7596269F-A8F8-465D-A627-3B5878A73DDF}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "kcs", "kcs.vcxproj", "{F3ED0AEC-F87B-4F79-B156-B56586B944E4}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {7596269F-A8F8-465D-A627-3B5878A73DDF}.Debug|Win32.ActiveCfg = Debug|Win32 - {7596269F-A8F8-465D-A627-3B5878A73DDF}.Debug|Win32.Build.0 = Debug|Win32 - {7596269F-A8F8-465D-A627-3B5878A73DDF}.Release|Win32.ActiveCfg = Release|Win32 - {7596269F-A8F8-465D-A627-3B5878A73DDF}.Release|Win32.Build.0 = Release|Win32 - {7596269F-A8F8-465D-A627-3B5878A73DDF}.Debug|x64.ActiveCfg = Debug|x64 - {7596269F-A8F8-465D-A627-3B5878A73DDF}.Debug|x64.Build.0 = Debug|x64 - {7596269F-A8F8-465D-A627-3B5878A73DDF}.Release|x64.ActiveCfg = Release|x64 - {7596269F-A8F8-465D-A627-3B5878A73DDF}.Release|x64.Build.0 = Release|x64 + {F3ED0AEC-F87B-4F79-B156-B56586B944E4}.Debug|Win32.ActiveCfg = Debug|Win32 + {F3ED0AEC-F87B-4F79-B156-B56586B944E4}.Debug|Win32.Build.0 = Debug|Win32 + {F3ED0AEC-F87B-4F79-B156-B56586B944E4}.Release|Win32.ActiveCfg = Release|Win32 + {F3ED0AEC-F87B-4F79-B156-B56586B944E4}.Release|Win32.Build.0 = Release|Win32 + {F3ED0AEC-F87B-4F79-B156-B56586B944E4}.Debug|x64.ActiveCfg = Debug|x64 + {F3ED0AEC-F87B-4F79-B156-B56586B944E4}.Debug|x64.Build.0 = Debug|x64 + {F3ED0AEC-F87B-4F79-B156-B56586B944E4}.Release|x64.ActiveCfg = Release|x64 + {F3ED0AEC-F87B-4F79-B156-B56586B944E4}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/general/perfcounters/kcs/kcs.vcxproj b/general/perfcounters/kcs/kcs.vcxproj index 1dcd38af7..8752a69c4 100644 --- a/general/perfcounters/kcs/kcs.vcxproj +++ b/general/perfcounters/kcs/kcs.vcxproj @@ -19,11 +19,11 @@ - {7596269F-A8F8-465D-A627-3B5878A73DDF} + {F3ED0AEC-F87B-4F79-B156-B56586B944E4} $(MSBuildProjectName) Debug Win32 - {69F72AB3-96CC-43AD-A68F-CB688463DDA0} + {645520EA-5297-4972-B738-B46537C50294} @@ -160,7 +160,6 @@ - diff --git a/general/perfcounters/kcs/kcs.vcxproj.Filters b/general/perfcounters/kcs/kcs.vcxproj.Filters index fc8b9ec1e..95035aea2 100644 --- a/general/perfcounters/kcs/kcs.vcxproj.Filters +++ b/general/perfcounters/kcs/kcs.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {355E458C-12D5-4C11-ADC3-406D93CFB8A8} + {72EAD35D-F168-46BE-8F02-E79EA9A404BF} h;hpp;hxx;hm;inl;inc;xsd - {EDE44450-36F4-47D8-B151-48C17BDF62DD} + {C66195DA-5B37-4759-8370-E727989B500E} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {ADDD9180-1AB7-4BDB-931B-6D108B189EB5} + {1422332E-6F0D-4F3F-9F12-C8F1DD344B02} inf;inv;inx;mof;mc; - {36374B68-325A-4EE5-B9E7-523B2544F1D3} + {F6F0837E-DC90-49DE-8AD8-F746B0CD1AEB} diff --git a/general/registry/regfltr/exe/common.h b/general/registry/regfltr/exe/common.h index 7b2ca958e..7ed20690c 100644 --- a/general/registry/regfltr/exe/common.h +++ b/general/registry/regfltr/exe/common.h @@ -1,168 +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; - - +/*++ +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/regctrl.vcxproj b/general/registry/regfltr/exe/regctrl.vcxproj index 60d7f41cd..4a4bff139 100644 --- a/general/registry/regfltr/exe/regctrl.vcxproj +++ b/general/registry/regfltr/exe/regctrl.vcxproj @@ -19,11 +19,11 @@ - {C1237DB4-2E21-436F-A4E7-7C5B7632D1A2} + {80F2FF9A-F64D-4BC8-B469-C498268F0893} $(MSBuildProjectName) Debug Win32 - {D3AEFA3D-C8F6-4B1F-9977-D2D0D6A8B318} + {0D1260D8-BC17-4D6D-827F-EAE520A16BA1} @@ -190,7 +190,6 @@ - diff --git a/general/registry/regfltr/exe/regctrl.vcxproj.Filters b/general/registry/regfltr/exe/regctrl.vcxproj.Filters index 176ee63c2..231ab1d9e 100644 --- a/general/registry/regfltr/exe/regctrl.vcxproj.Filters +++ b/general/registry/regfltr/exe/regctrl.vcxproj.Filters @@ -3,15 +3,15 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {9E122CD6-5345-418A-AFA8-B17887197386} + {6DAFAEA8-8FF3-494E-A725-A2DC7670CC24} h;hpp;hxx;hm;inl;inc;xsd - {95638411-9C24-4D8D-955D-26586E3BD517} + {7E18F6C2-880E-479F-BAF0-92350FF8521F} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {E8182A13-CD10-438C-A333-32F784173C22} + {D47D5E7A-8D65-4127-A011-6E2CCBE4F537} diff --git a/general/registry/regfltr/regfltr.sln b/general/registry/regfltr/regfltr.sln index 59193da58..152bf0a3e 100644 --- a/general/registry/regfltr/regfltr.sln +++ b/general/registry/regfltr/regfltr.sln @@ -3,13 +3,13 @@ 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", "{FADD4059-9BCA-43E7-93E1-D841DDC3359C}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Exe", "Exe", "{54DDD01C-98B2-4DF0-ABD4-FFFA5E8AF6E6}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sys", "Sys", "{10003933-73CE-44B3-BB13-A248CD5862A8}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sys", "Sys", "{4DC3A66B-04A0-4561-8E12-9E7C15E84931}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "regctrl", "exe\regctrl.vcxproj", "{C1237DB4-2E21-436F-A4E7-7C5B7632D1A2}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "regctrl", "exe\regctrl.vcxproj", "{80F2FF9A-F64D-4BC8-B469-C498268F0893}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "regfltr", "sys\regfltr.vcxproj", "{5FA7255C-4B69-42A2-8511-FA5CF23789C1}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "regfltr", "sys\regfltr.vcxproj", "{5DD375CD-55ED-4473-ACF1-603E816B1B7F}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -19,28 +19,28 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {C1237DB4-2E21-436F-A4E7-7C5B7632D1A2}.Debug|Win32.ActiveCfg = Debug|Win32 - {C1237DB4-2E21-436F-A4E7-7C5B7632D1A2}.Debug|Win32.Build.0 = Debug|Win32 - {C1237DB4-2E21-436F-A4E7-7C5B7632D1A2}.Release|Win32.ActiveCfg = Release|Win32 - {C1237DB4-2E21-436F-A4E7-7C5B7632D1A2}.Release|Win32.Build.0 = Release|Win32 - {C1237DB4-2E21-436F-A4E7-7C5B7632D1A2}.Debug|x64.ActiveCfg = Debug|x64 - {C1237DB4-2E21-436F-A4E7-7C5B7632D1A2}.Debug|x64.Build.0 = Debug|x64 - {C1237DB4-2E21-436F-A4E7-7C5B7632D1A2}.Release|x64.ActiveCfg = Release|x64 - {C1237DB4-2E21-436F-A4E7-7C5B7632D1A2}.Release|x64.Build.0 = Release|x64 - {5FA7255C-4B69-42A2-8511-FA5CF23789C1}.Debug|Win32.ActiveCfg = Debug|Win32 - {5FA7255C-4B69-42A2-8511-FA5CF23789C1}.Debug|Win32.Build.0 = Debug|Win32 - {5FA7255C-4B69-42A2-8511-FA5CF23789C1}.Release|Win32.ActiveCfg = Release|Win32 - {5FA7255C-4B69-42A2-8511-FA5CF23789C1}.Release|Win32.Build.0 = Release|Win32 - {5FA7255C-4B69-42A2-8511-FA5CF23789C1}.Debug|x64.ActiveCfg = Debug|x64 - {5FA7255C-4B69-42A2-8511-FA5CF23789C1}.Debug|x64.Build.0 = Debug|x64 - {5FA7255C-4B69-42A2-8511-FA5CF23789C1}.Release|x64.ActiveCfg = Release|x64 - {5FA7255C-4B69-42A2-8511-FA5CF23789C1}.Release|x64.Build.0 = Release|x64 + {80F2FF9A-F64D-4BC8-B469-C498268F0893}.Debug|Win32.ActiveCfg = Debug|Win32 + {80F2FF9A-F64D-4BC8-B469-C498268F0893}.Debug|Win32.Build.0 = Debug|Win32 + {80F2FF9A-F64D-4BC8-B469-C498268F0893}.Release|Win32.ActiveCfg = Release|Win32 + {80F2FF9A-F64D-4BC8-B469-C498268F0893}.Release|Win32.Build.0 = Release|Win32 + {80F2FF9A-F64D-4BC8-B469-C498268F0893}.Debug|x64.ActiveCfg = Debug|x64 + {80F2FF9A-F64D-4BC8-B469-C498268F0893}.Debug|x64.Build.0 = Debug|x64 + {80F2FF9A-F64D-4BC8-B469-C498268F0893}.Release|x64.ActiveCfg = Release|x64 + {80F2FF9A-F64D-4BC8-B469-C498268F0893}.Release|x64.Build.0 = Release|x64 + {5DD375CD-55ED-4473-ACF1-603E816B1B7F}.Debug|Win32.ActiveCfg = Debug|Win32 + {5DD375CD-55ED-4473-ACF1-603E816B1B7F}.Debug|Win32.Build.0 = Debug|Win32 + {5DD375CD-55ED-4473-ACF1-603E816B1B7F}.Release|Win32.ActiveCfg = Release|Win32 + {5DD375CD-55ED-4473-ACF1-603E816B1B7F}.Release|Win32.Build.0 = Release|Win32 + {5DD375CD-55ED-4473-ACF1-603E816B1B7F}.Debug|x64.ActiveCfg = Debug|x64 + {5DD375CD-55ED-4473-ACF1-603E816B1B7F}.Debug|x64.Build.0 = Debug|x64 + {5DD375CD-55ED-4473-ACF1-603E816B1B7F}.Release|x64.ActiveCfg = Release|x64 + {5DD375CD-55ED-4473-ACF1-603E816B1B7F}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {C1237DB4-2E21-436F-A4E7-7C5B7632D1A2} = {FADD4059-9BCA-43E7-93E1-D841DDC3359C} - {5FA7255C-4B69-42A2-8511-FA5CF23789C1} = {10003933-73CE-44B3-BB13-A248CD5862A8} + {80F2FF9A-F64D-4BC8-B469-C498268F0893} = {54DDD01C-98B2-4DF0-ABD4-FFFA5E8AF6E6} + {5DD375CD-55ED-4473-ACF1-603E816B1B7F} = {4DC3A66B-04A0-4561-8E12-9E7C15E84931} EndGlobalSection EndGlobal diff --git a/general/registry/regfltr/sys/regfltr.c b/general/registry/regfltr/sys/regfltr.c index 5cd85c6c4..730459b28 100644 --- a/general/registry/regfltr/sys/regfltr.c +++ b/general/registry/regfltr/sys/regfltr.c @@ -1,871 +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; - -} +/*++ +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.vcxproj b/general/registry/regfltr/sys/regfltr.vcxproj index bd877cee3..a41d67182 100644 --- a/general/registry/regfltr/sys/regfltr.vcxproj +++ b/general/registry/regfltr/sys/regfltr.vcxproj @@ -19,11 +19,11 @@ - {5FA7255C-4B69-42A2-8511-FA5CF23789C1} + {5DD375CD-55ED-4473-ACF1-603E816B1B7F} $(MSBuildProjectName) Debug Win32 - {72113749-69A5-4D2B-A747-75C97C239409} + {7D592DE5-96F0-40B9-8248-2CF66A8598FE} @@ -200,7 +200,6 @@ - diff --git a/general/registry/regfltr/sys/regfltr.vcxproj.Filters b/general/registry/regfltr/sys/regfltr.vcxproj.Filters index 5c90e0f78..102cb3f2b 100644 --- a/general/registry/regfltr/sys/regfltr.vcxproj.Filters +++ b/general/registry/regfltr/sys/regfltr.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {0AA7B9CC-6AAF-487B-9036-B939FD9A9B0E} + {F62D61F7-DCE8-4A41-9279-8004A63F38C1} h;hpp;hxx;hm;inl;inc;xsd - {BB821D94-5629-40BE-B5B8-809B58382931} + {752F76C0-8A4C-47F7-86DC-3765B9DB318C} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {28C582AD-3C88-4631-A980-E0EB0684D9FA} + {CD06AB4D-22C8-4DC9-8B81-34F857E144FC} inf;inv;inx;mof;mc; - {87216377-AE9B-47DE-995E-F11370E84553} + {289E8C44-65C6-44CF-B6B3-6D551292E0C6} diff --git a/general/toaster/toastDrv/Package/package.VcxProj b/general/toaster/toastDrv/Package/package.VcxProj new file mode 100644 index 000000000..df14540a3 --- /dev/null +++ b/general/toaster/toastDrv/Package/package.VcxProj @@ -0,0 +1,111 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + + {EA6EA609-654B-4B46-ABA1-1092BD3FA0AC} + + + {EA66789C-5ABF-44C0-9BFD-C7618507E9C8} + + + {965C0E80-0715-4376-A138-48D08EFDDC09} + + + {C79A9F42-BDB4-4B5A-B7B5-1BEBF7752207} + + + {5094C704-35B0-4A25-AD30-570C49BCAE82} + + + {CA0E203E-4C96-466F-BF19-24F42DFC0C89} + + + {FB919684-BF55-4E3D-95FA-CBCB7B0D8759} + + + {A5E50982-981B-40B9-BEE6-1E15581FE947} + + + {B439C142-C570-44A6-B13C-43F70AE05A69} + + + {D9D1EDA9-B9AD-4C14-BCDE-DBE39CED25B7} + + + + WindowsKernelModeDriver10.0 + Utility + Package + true + Debug + + + + {D72A34B9-86A5-4801-B7E3-256CF1F382AD} + {DC08D3F2-EDC5-409B-800B-8F8AC3703227} + $(MSBuildProjectName) + + + Windows10 + true + + + Windows10 + false + + + Windows10 + true + + + Windows10 + false + + + + + + + + + + + DbgengKernelDebugger + False + None + + + + + + %PathToInf% + False + False + True + + 133563 + + + + + + + \ No newline at end of file diff --git a/general/toaster/toastDrv/Package/package.VcxProj.Filters b/general/toaster/toastDrv/Package/package.VcxProj.Filters new file mode 100644 index 000000000..6ac14cf22 --- /dev/null +++ b/general/toaster/toastDrv/Package/package.VcxProj.Filters @@ -0,0 +1,21 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {326C9B70-6DC4-4EA3-92EB-476DC4DBD40B} + + + h;hpp;hxx;hm;inl;inc;xsd + {24CE830C-B385-46E4-83A3-B26A795BF457} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {695BFD83-FE23-4414-BBAC-E44BB2BBAE00} + + + inf;inv;inx;mof;mc; + {8A6D9B6E-0F3C-448E-948B-DCA1A5066C25} + + + \ No newline at end of file diff --git a/general/toaster/toastDrv/ReadMe.md b/general/toaster/toastDrv/ReadMe.md new file mode 100644 index 000000000..108811a42 --- /dev/null +++ b/general/toaster/toastDrv/ReadMe.md @@ -0,0 +1,134 @@ +Toaster Sample Driver +===================== +The Toaster collection is an iterative series of samples that demonstrate fundamental aspects of Windows driver development for both Kernel-Mode Driver Framework (KMDF) and User-Mode Driver Framework (UMDF) version 1. + +All the samples work with a hypothetical toaster bus, over which toaster devices can be connected to a PC. + +The Toaster sample collection comprises driver projects (.vcxproj files) that are contained in the toaster.sln solution file (in general\\toaster\\toastdrv). + +Related technologies +-------------------- +[Windows Driver Frameworks](http://msdn.microsoft.com/en-us/library/windows/hardware/ff557565) + +For detailed descriptions and code walkthroughs of each project, see [Sample Toaster Driver Programming Tour](http://msdn.microsoft.com/en-us/library/windows/hardware/dn569312). To learn how to build and run the samples, read on. + + +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 components of the Toaster Sample automatically or manually. Here, we install the wdfsimple driver on the target computer. + +### Specifying which projects to deploy + +Before doing this, you should back up your package.vcxproj file, located in your sample directory, for example C:\\Toaster\\C++\\Package. + +1. In the Properties for the package project, navigate to **Common Properties \> References**. +2. Remove all references except WdfSimple. (Use the **Remove Reference** button at the bottom.) + +### 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 the **package** project (within the package folder), 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**, use the drop down to select the name of a target computer that you provisioned previously. Select **Hardware ID Driver Update**, and enter **{b85b7c50-6a01-11d2-b841-00c04fad5171}\\MsToaster** for the hardware ID. (You can find this value in the WdfSimple.inx file.) Click **Apply** and **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:\\WdfSimplePackage). +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 WdfSimple.inf {b85b7c50-6a01-11d2-b841-00c04fad5171}\\MsToaster** + +### 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 **Microsoft WDF Simple Toaster (No Class Installer)**. + +In Device Manager, on the **View** menu, choose **Devices by connection**. Locate **Microsoft WDF Simple Toaster (No Class Installer)** as a child of the root node of the device tree. + +Build the sample using MSBuild +------------------------------ + +As an alternative to building the Toaster 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, Toaster.sln. Use the MSBuild command to build the solution. Here are some examples: + +**msbuild /p:configuration=â€Win7 Debug†/p:platform=â€x64†Toaster.sln** + +**msbuild /p:configuration=â€Win8 Release†/p:platform=â€Win32†Toaster.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). + +UMDF Toaster File Manifest +-------------------------- +#### WUDFToaster.idl +Component Interface file + +#### WUDFToaster.cpp +DLL Support code - provides the DLL's entry point as well as the DllGetClassObject export. + +#### WUDFToaster.def +This file lists the functions that the driver DLL exports. + +#### stdafx.h +This is the main header file for the sample driver. + +#### driver.cpp & driver.h +Definition and implementation of the IDriverEntry callbacks in CDriver class. + +#### device.cpp & device.h +Definition and implementation of various interfaces and their callbacks in CDevice class. Add your PnP and Power interfaces specific for your hardware. + +#### queue.cpp & queue.h +Definition and implementation of the base queue callback class (CQueue). IQueueCallbackDevicekIoControl, IQueueCallbackRead and IQueueCallBackWrite callbacks are implemented to handle I/O control requests. + +#### WUDFToaster.rc +This file defines resource information for the WUDF Toaster sample driver. + +#### WUDFToaster.inf +Sample INF for installing the sample WUDF Toaster driver under the Toaster class of devices. + +#### WUDFtoaster.ctl, internal.h +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. + +Toastmon File Manifest +---------------------- +#### comsup.cpp & comsup.h +Boilerplate COM Support code - specifically base classes which provide implementations for the standard COM interfaces IUnknown and IClassFactory which are used throughout the 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 +Boilerplate 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. + +#### makefile +This file redirects to the real makefile, which is shared by all the driver components of the Windows Driver Kit. + +#### internal.h +This is the main header file for the ToastMon driver + +#### driver.cpp & driver.h +Definition and implementation of the driver callback class for the ToastMon sample. + +#### device.cpp & device.h +Definition and implementation of the device callback class for the ToastMon sample. This is mostly boilerplate, but also registers for RemoteInterface Arrival notifications. When a RemoteInterface arrival callback occurs, it calls CreateRemoteInterface and creates a CMyRemoteTarget callback object to handle I/O on that RemoteInterface. + +#### RemoteTarget.cpp & RemoteTarget.h +Definition and implementation of the remote target callback class for the ToastMon sample. + +#### list.h +Doubly-linked-list code + +#### ToastMon.rc +This file defines resource information for the ToastMon sample driver. + +#### UMDFToastMon.inf +Sample INF for installing the Skeleton driver to control a root enumerated device with a hardware ID of UMDFSamples\\ToastMon + diff --git a/general/toaster/toastDrv/classinstaller/classInst.c b/general/toaster/toastDrv/classinstaller/classInst.c new file mode 100644 index 000000000..9aa3294a1 --- /dev/null +++ b/general/toaster/toastDrv/classinstaller/classInst.c @@ -0,0 +1,513 @@ +//+--------------------------------------------------------------------------- +// +// 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. +// +// File: ClassINST.C +// +// Contents: class-installer for toaster. +// +// Notes: This is a sample class installer. Class installer is not required +// for every class of device. I have included here to provide +// an icon for the toaster class and to demonstrate how one +// can provide a customer property sheet in the device manager +// in response to DIF_ADDPROPERTYPAGE_ADVANCED. +// The property sheet has a check box. If you enable the box +// and press OK, it will restart the device. This is required +// if the user changes the device properties, and it needs to +// be restarted to apply the new attributes. +// For a complete description of ClassInstallers, please see the +// Microsoft Windows 2000 DDK Documentation. +// +// +// Revision: Added property page - Nov 14, 2000 +// +// +//---------------------------------------------------------------------------- + +// +// Annotation to indicate to prefast that this is nondriver user-mode code. +// +#include +__user_code + +#include +#include +#include +#include +#include "resource.h" +#include +//+--------------------------------------------------------------------------- +// +// WARNING! +// +// Installer must not generate any popup to the user. +// It should provide appropriate defaults. +// +// OutputDebugString should be fine... +// +#if DBG +#define DbgOut(Text) OutputDebugString(TEXT("ClassInstaller: " Text "\n")) +#else +#define DbgOut(Text) +#endif + +typedef struct _TOASTER_PROP_PARAMS +{ + + HDEVINFO DeviceInfoSet; + PSP_DEVINFO_DATA DeviceInfoData; + BOOL Restart; + +} TOASTER_PROP_PARAMS, *PTOASTER_PROP_PARAMS; + + +INT_PTR +PropPageDlgProc(_In_ HWND hDlg, + _In_ UINT uMessage, + _In_ WPARAM wParam, + _In_ LPARAM lParam); + +UINT +CALLBACK +PropPageDlgCallback(HWND hwnd, + UINT uMsg, + LPPROPSHEETPAGE ppsp); +DWORD +PropPageProvider( + _In_ HDEVINFO DeviceInfoSet, + _In_ PSP_DEVINFO_DATA DeviceInfoData OPTIONAL +); + +BOOL +OnNotify( + HWND ParentHwnd, + LPNMHDR NmHdr, + PTOASTER_PROP_PARAMS Params + ); + +HMODULE ModuleInstance; + +BOOL WINAPI +DllMain( + HINSTANCE DllInstance, + DWORD Reason, + PVOID Reserved + ) +{ + + UNREFERENCED_PARAMETER( Reserved ); + + switch(Reason) { + + case DLL_PROCESS_ATTACH: { + + ModuleInstance = DllInstance; + DisableThreadLibraryCalls(DllInstance); + InitCommonControls(); + break; + } + + case DLL_PROCESS_DETACH: { + ModuleInstance = NULL; + break; + } + + default: { + break; + } + } + + return TRUE; +} + +DWORD CALLBACK +ToasterClassInstaller( + _In_ DI_FUNCTION InstallFunction, + _In_ HDEVINFO DeviceInfoSet, + _In_ PSP_DEVINFO_DATA DeviceInfoData OPTIONAL + ) +/*++ + +Routine Description: + + Responds to Class-installer messages + . +Arguments: + + InstallFunction [in] + DeviceInfoSet [in] + DeviceInfoData [in] + +Return Value: + +Returns: NO_ERROR, ERROR_DI_POSTPROCESSING_REQUIRED, or an error code. + +--*/ +{ + switch (InstallFunction) + { + case DIF_INSTALLDEVICE: + // + // Sent twice: once before installing the device and once + // after installing device, if you have returned + // ERROR_DI_POSTPROCESSING_REQUIRED during the first pass. + // + DbgOut("DIF_INSTALLDEVICE"); + break; + + case DIF_ADDPROPERTYPAGE_ADVANCED: + // + // Sent when you check the properties of the device in the + // device manager. + // + DbgOut("DIF_ADDPROPERTYPAGE_ADVANCED"); + return PropPageProvider(DeviceInfoSet, DeviceInfoData); + + case DIF_POWERMESSAGEWAKE: + // + // Sent when you check the power management tab + // + DbgOut("DIF_POWERMESSAGEWAKE"); + break; + + case DIF_PROPERTYCHANGE: + // + // Sent when you change the property of the device using + // SetupDiSetDeviceInstallParams. (Enable/Disable/Restart) + // + DbgOut("DIF_PROPERTYCHANGE"); + break; + case DIF_REMOVE: + // + // Sent when you uninstall the device. + // + DbgOut("DIF_REMOVE"); + break; + + case DIF_NEWDEVICEWIZARD_FINISHINSTALL: + // + // Sent near the end of installation to allow + // an installer to supply wizard page(s) to the user. + // These wizard pages are different from the device manager + // property sheet.There are popped only once during install. + // + DbgOut("DIF_NEWDEVICEWIZARD_FINISHINSTALL"); + break; + + case DIF_SELECTDEVICE: + DbgOut("DIF_SELECTDEVICE"); + break; + case DIF_DESTROYPRIVATEDATA: + // + // Sent when Setup destroys a device information set + // or an SP_DEVINFO_DATA element, or when Setup discards + // its list of co-installers and class installer for a device + // + DbgOut("DIF_DESTROYPRIVATEDATA"); + break; + case DIF_INSTALLDEVICEFILES: + DbgOut("DIF_INSTALLDEVICEFILES"); + break; + case DIF_ALLOW_INSTALL: + // + // Sent to confirm whether the installer wants to allow + // the installation of device. + // + DbgOut("DIF_ALLOW_INSTALL"); + break; + case DIF_SELECTBESTCOMPATDRV: + DbgOut("DIF_SELECTBESTCOMPATDRV"); + break; + + case DIF_INSTALLINTERFACES: + DbgOut("DIF_INSTALLINTERFACES"); + break; + case DIF_REGISTER_COINSTALLERS: + DbgOut("DIF_REGISTER_COINSTALLERS"); + break; + default: + DbgOut("DIF_???"); + break; + } + return ERROR_DI_DO_DEFAULT; +} + +DWORD +PropPageProvider( + _In_ HDEVINFO DeviceInfoSet, + _In_ PSP_DEVINFO_DATA DeviceInfoData OPTIONAL +) + +/*++ + +Routine Description: + + Entry-point for adding additional device manager property + sheet pages. +Arguments: + +Return Value: + +Returns: NO_ERROR, ERROR_DI_DO_DEFAULT, or an error code. + +--*/ +{ + HPROPSHEETPAGE pageHandle; + PROPSHEETPAGE page; + PTOASTER_PROP_PARAMS params = NULL; + SP_ADDPROPERTYPAGE_DATA AddPropertyPageData = {0}; + + // + // DeviceInfoSet is NULL if setup is requesting property pages for + // the device setup class. We don't want to do anything in this + // case. + // + if (DeviceInfoData==NULL) { + return ERROR_DI_DO_DEFAULT; + } + + AddPropertyPageData.ClassInstallHeader.cbSize = + sizeof(SP_CLASSINSTALL_HEADER); + + // + // Get the current class install parameters for the device + // + + if (SetupDiGetClassInstallParams(DeviceInfoSet, DeviceInfoData, + (PSP_CLASSINSTALL_HEADER)&AddPropertyPageData, + sizeof(SP_ADDPROPERTYPAGE_DATA), NULL )) + { + + // + // Ensure that the maximum number of dynamic pages for the + // device has not yet been met + // + if(AddPropertyPageData.NumDynamicPages >= MAX_INSTALLWIZARD_DYNAPAGES){ + return NO_ERROR; + } + params = HeapAlloc(GetProcessHeap(), 0, sizeof(TOASTER_PROP_PARAMS)); + if (params) + { + // + // Save DeviceInfoSet and DeviceInfoData + // + params->DeviceInfoSet = DeviceInfoSet; + params->DeviceInfoData = DeviceInfoData; + params->Restart = FALSE; + + // + // Create custom property sheet page + // + memset(&page, 0, sizeof(PROPSHEETPAGE)); + + page.dwSize = sizeof(PROPSHEETPAGE); + page.dwFlags = PSP_USECALLBACK; + page.hInstance = ModuleInstance; + page.pszTemplate = MAKEINTRESOURCE(DLG_TOASTER_PORTSETTINGS); + page.pfnDlgProc = (DLGPROC) PropPageDlgProc; + page.pfnCallback = PropPageDlgCallback; + + page.lParam = (LPARAM) params; + + pageHandle = CreatePropertySheetPage(&page); + if(!pageHandle) + { + HeapFree(GetProcessHeap(), 0, params); + return NO_ERROR; + } + + // + // Add the new page to the list of dynamic property + // sheets + // + AddPropertyPageData.DynamicPages[ + AddPropertyPageData.NumDynamicPages++]=pageHandle; + + SetupDiSetClassInstallParams(DeviceInfoSet, + DeviceInfoData, + (PSP_CLASSINSTALL_HEADER)&AddPropertyPageData, + sizeof(SP_ADDPROPERTYPAGE_DATA)); + } + } + return NO_ERROR; +} + +INT_PTR +PropPageDlgProc(_In_ HWND hDlg, + _In_ UINT uMessage, + _In_ WPARAM wParam, + _In_ LPARAM lParam) +/*++ + +Routine Description: PropPageDlgProc + + The windows control function for the custom property page window + +Arguments: + + hDlg, uMessage, wParam, lParam: standard windows DlgProc parameters + +Return Value: + + BOOL: FALSE if function fails, TRUE if function passes + +--*/ +{ + PTOASTER_PROP_PARAMS params; + + UNREFERENCED_PARAMETER( wParam ); + + params = (PTOASTER_PROP_PARAMS) GetWindowLongPtr(hDlg, DWLP_USER); + + switch(uMessage) { + case WM_COMMAND: + break; + + case WM_CONTEXTMENU: + break; + + case WM_HELP: + break; + + case WM_INITDIALOG: + + // + // on WM_INITDIALOG call, lParam points to the property + // sheet page. + // + // The lParam field in the property sheet page struct is set by the + // caller. This was set when we created the property sheet. + // Save this in the user window long so that we can access it on later + // on later messages. + // + + params = (PTOASTER_PROP_PARAMS) ((LPPROPSHEETPAGE)lParam)->lParam; + SetWindowLongPtr(hDlg, DWLP_USER, (LONG_PTR) params); + break; + + + case WM_NOTIFY: + OnNotify(hDlg, (NMHDR *)lParam, params); + break; + + default: + return FALSE; + } + + return TRUE; +} + + +UINT +CALLBACK +PropPageDlgCallback(HWND hwnd, + UINT uMsg, + LPPROPSHEETPAGE ppsp) +{ + PTOASTER_PROP_PARAMS params; + + UNREFERENCED_PARAMETER( hwnd ); + + switch (uMsg) { + + case PSPCB_CREATE: + // + // Called when the property sheet is first displayed + // + return TRUE; // return TRUE to continue with creation of page + + case PSPCB_RELEASE: + // + // Called when property page is destroyed, even if the page + // was never displayed. This is the correct way to release data. + // + params = (PTOASTER_PROP_PARAMS) ppsp->lParam; + LocalFree(params); + return 0; // return value ignored + default: + break; + } + + return TRUE; +} + + +BOOL +OnNotify( + HWND ParentHwnd, + LPNMHDR NmHdr, + PTOASTER_PROP_PARAMS Params + ) +{ + SP_DEVINSTALL_PARAMS spDevInstall = {0}; + TCHAR friendlyName[LINE_LEN] ={0}; + size_t charCount; + BOOL fSuccess; + + switch (NmHdr->code) { + case PSN_APPLY: + // + // Sent when the user clicks on Apply OR OK !! + // + + GetDlgItemText(ParentHwnd, IDC_FRIENDLYNAME, friendlyName, + LINE_LEN-1 ); + friendlyName[LINE_LEN-1] = UNICODE_NULL; + if(friendlyName[0]) { + + if (FAILED(StringCchLength(friendlyName, + STRSAFE_MAX_CCH, + &charCount))) { + DbgOut("StringCchLength failed!"); + break; + } + fSuccess = SetupDiSetDeviceRegistryProperty(Params->DeviceInfoSet, + Params->DeviceInfoData, + SPDRP_FRIENDLYNAME, + (BYTE *)friendlyName, + (DWORD)((charCount + 1) * sizeof(TCHAR)) + ); + if(!fSuccess) { + DbgOut("SetupDiSetDeviceRegistryProperty failed!"); + break; + } + + // + // Inform setup about property change so that it can + // restart the device. + // + + spDevInstall.cbSize = sizeof(SP_DEVINSTALL_PARAMS); + + if (Params && SetupDiGetDeviceInstallParams(Params->DeviceInfoSet, + Params->DeviceInfoData, + &spDevInstall)) { + // + // If your device requires a reboot to restart, you can + // specify that by setting DI_NEEDREBOOT as shown below + // + // if(NeedReboot) { + // spDevInstall.Flags |= DI_PROPERTIES_CHANGE | DI_NEEDREBOOT; + // } + // + spDevInstall.FlagsEx |= DI_FLAGSEX_PROPCHANGE_PENDING; + + SetupDiSetDeviceInstallParams(Params->DeviceInfoSet, + Params->DeviceInfoData, + &spDevInstall); + } + + } + return TRUE; + + default: + return FALSE; + } + return FALSE; +} + diff --git a/general/toaster/toastDrv/classinstaller/classinst.rc b/general/toaster/toastDrv/classinstaller/classinst.rc new file mode 100644 index 000000000..7320e436d --- /dev/null +++ b/general/toaster/toastDrv/classinstaller/classinst.rc @@ -0,0 +1,65 @@ +/*++ + +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: + + classinst.rc + +Abstract: + + Resource Script for Toaster Class Installer Sample + +Environment: + + user mode only + +Revision History: + +--*/ + + +#include "resource.h" +#include +#include + +#define VER_FILETYPE VFT_APP +#define VER_FILESUBTYPE VFT2_UNKNOWN +#define VER_FILEDESCRIPTION_STR "Class-Installer DLL for Toaster Sample" +#define VER_INTERNALNAME_STR "TostrCls.dll" +#define VER_ORIGINALFILENAME_STR "TostrCls.dll" + +#include + + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +DLG_TOASTER_PORTSETTINGS DIALOGEX 0, 0, 257, 215 +STYLE DS_MODALFRAME | DS_3DLOOK | WS_POPUP | WS_VISIBLE | WS_CAPTION | + WS_SYSMENU +CAPTION "Custom Property Page" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "You can change the friendly name of the device with the following edit box. If you give a name and click OK, it will cause the device to restart.", + IDC_STATIC,43,24,171,27,WS_BORDER,WS_EX_STATICEDGE + EDITTEXT IDC_FRIENDLYNAME,97,78,118,14,ES_AUTOHSCROLL + LTEXT "FriendlyName",204,46,81,46,10 +END + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +IDI_CLASS_ICON ICON DISCARDABLE "TOASTER.ICO" + + diff --git a/general/toaster/toastDrv/classinstaller/resource.h b/general/toaster/toastDrv/classinstaller/resource.h new file mode 100644 index 000000000..e8331ee0b --- /dev/null +++ b/general/toaster/toastDrv/classinstaller/resource.h @@ -0,0 +1,38 @@ +/*++ + +Copyright (c) 1999-2000 Microsoft Corporation + +Module Name: + + Resource.h + +Abstract: + + Resource def. + +Environment: + + user mode only + +Notes: + + 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) 1999-2000 Microsoft Corporation. All Rights Reserved. + + +Revision History: + +--*/ + +#define IDI_CLASS_ICON 100 + +#define DLG_TOASTER_PORTSETTINGS 200 + +#define IDC_STATIC 202 + +#define IDC_FRIENDLYNAME 203 + diff --git a/general/toaster/toastDrv/classinstaller/toaster.ico b/general/toaster/toastDrv/classinstaller/toaster.ico new file mode 100644 index 0000000000000000000000000000000000000000..77ad2081ae46c21521d966a06d53d8429cacd307 GIT binary patch literal 4534 zcmeH~KTKRl5Ql#UNSrL>3k?;L&lFMUkhY?7M33Z#8dqD>VJUxj6{bY8gtV1i#8FbD zA!VAYQkbowF|s8JEEI`H#Yv7b`F7qr42q>RQ5wHJW@l%1cjkMu`%Z_{)0Bq8FI3w5 zDd|h}%8Hx%>ArM-TKWoehdIta0qPghfwLKG<_bQz*F8)J0F1usdegW*>e<{r1AHzjeoMNW@bkHeqVEQbDE!@*TTYr78e(_w6vt<sC-RpgO5{(}Cc%(k zNYtSm8&&bA$AhLX$4Ct_`7&%7j!Zs3+QLB%G90;Z@L+!2Y|5}?STYcqgnk8J$!yFl z$~ZDC{2|2QOfoFF)WccIZ78RJ4+ew5U@#aA27|$`6AT#!gTY|PFc1b|Jq!ke!C){D z8o>{P!C){L07m{yFk_>!C=w%LM>x!5mo-rj^-7zYo8rq(ySuwOI5^PJ(UA@h4|Q^K zqSMn;ot>TO;^IPAS63R3$ErV=et7wk_qd|E + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {EA6EA609-654B-4B46-ABA1-1092BD3FA0AC} + $(MSBuildProjectName) + false + true + Debug + Win32 + {EB79C9FC-7142-4AC9-BF03-D873407A31DE} + + + + 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) + + + + + + + + + + + + + + + + tostrcls + + + tostrcls + + + tostrcls + + + tostrcls + + + + true + Level4 + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + true + Level4 + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + true + Level4 + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + true + Level4 + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + + + %(AdditionalDependencies);setupapi.lib;kernel32.lib;advapi32.lib;user32.lib;comctl32.lib + 0x2000000 + tostrcls.def + + + + + + + + + %(AdditionalDependencies);setupapi.lib;kernel32.lib;advapi32.lib;user32.lib;comctl32.lib + 0x2000000 + tostrcls.def + + + + + + + + + %(AdditionalDependencies);setupapi.lib;kernel32.lib;advapi32.lib;user32.lib;comctl32.lib + 0x2000000 + tostrcls.def + + + + + + + + + %(AdditionalDependencies);setupapi.lib;kernel32.lib;advapi32.lib;user32.lib;comctl32.lib + 0x2000000 + tostrcls.def + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/general/toaster/toastDrv/classinstaller/tostrcls.vcxproj.Filters b/general/toaster/toastDrv/classinstaller/tostrcls.vcxproj.Filters new file mode 100644 index 000000000..841ba6f64 --- /dev/null +++ b/general/toaster/toastDrv/classinstaller/tostrcls.vcxproj.Filters @@ -0,0 +1,30 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {DFE60A11-D228-4BF1-B20A-708606E63695} + + + h;hpp;hxx;hm;inl;inc;xsd + {692DFF5B-CE0A-4047-8EC8-27394EDFB2B6} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {20E0445D-854A-4108-AAF5-8DF38235343B} + + + + + Source Files + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/general/toaster/toastDrv/coinstaller/coinst.c b/general/toaster/toastDrv/coinstaller/coinst.c new file mode 100644 index 000000000..a78223c56 --- /dev/null +++ b/general/toaster/toastDrv/coinstaller/coinst.c @@ -0,0 +1,343 @@ +// 45678901234567890123456789012345678901234567890123456789012345678901234567890 +//+--------------------------------------------------------------------------- +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation, 1999. +// +// File: COINST.C +// +// Contents: co-installer hook. +// +// Notes: For a complete description of CoInstallers, please see the +// Microsoft Windows 2000 DDK Documentation +// +// +// Revision History: +// Added FriendlyName interface +// +// Modified to create a friendlyname in the first callback to +// to install device rather than in post-processing. +// - July 28, 2000 +// +//---------------------------------------------------------------------------- + +// +// Annotation to indicate to prefast that this is nondriver user-mode code. +// +#include +__user_code + +#include +#include +#include +#include + +//+--------------------------------------------------------------------------- +// +// WARNING! +// +// A Coinstaller must not generate any popup to the user. +// it should provide appropriate defaults. +// +// OutputDebugString should be fine... +// +#if DBG +#define DbgOut(Text) OutputDebugString(TEXT("CoInstaller: " Text "\n")) +#else +#define DbgOut(Text) +#endif + +//+--------------------------------------------------------------------------- +// +// Function: MyOpenInfFile +// +// Purpose: Will open the handle to the INF file being installed +// +// Arguments: +// DeviceInfoSet [in] +// DeviceInfoData [in] +// ErrorLine [out] // Optional, See SetupOpenInfFile +// +// Returns: HINF Handle of INF file +// +HINF MyOpenInfFile ( + _In_ HDEVINFO DeviceInfoSet, + _In_ PSP_DEVINFO_DATA DeviceInfoData, + _Out_opt_ PUINT ErrorLine + ) +{ + SP_DRVINFO_DATA DriverInfoData; + SP_DRVINFO_DETAIL_DATA DriverInfoDetailData; + DWORD Status; + HINF FileHandle; + + if (NULL != ErrorLine) + { + *ErrorLine = 0; + } + + DriverInfoData.cbSize = sizeof(SP_DRVINFO_DATA); + if (!SetupDiGetSelectedDriver( DeviceInfoSet, + DeviceInfoData, + &DriverInfoData)) + { + DbgOut("Fail: SetupDiGetSelectedDriver"); + return INVALID_HANDLE_VALUE; + } + + DriverInfoDetailData.cbSize = sizeof(SP_DRVINFO_DETAIL_DATA); + if (!SetupDiGetDriverInfoDetail(DeviceInfoSet, + DeviceInfoData, + &DriverInfoData, + &DriverInfoDetailData, + sizeof(SP_DRVINFO_DETAIL_DATA), + NULL)) + { + if ((Status = GetLastError()) == ERROR_INSUFFICIENT_BUFFER) + { + // We don't need the extended information. Ignore. + } + else + { + DbgOut("Fail: SetupDiGetDriverInfoDetail"); + return INVALID_HANDLE_VALUE; + } + } + + if (INVALID_HANDLE_VALUE == (FileHandle = SetupOpenInfFile( + DriverInfoDetailData.InfFileName, + NULL, + INF_STYLE_WIN4, + ErrorLine))) + { + DbgOut("Fail: SetupOpenInfFile"); + } + return FileHandle; +} + +//+--------------------------------------------------------------------------- +// +// Function: ToasterCoInstaller +// +// Purpose: Responds to co-installer messages +// +// Arguments: +// InstallFunction [in] +// DeviceInfoSet [in] +// DeviceInfoData [in] +// Context [inout] +// +// Returns: NO_ERROR, ERROR_DI_POSTPROCESSING_REQUIRED, or an error code. +// +DWORD CALLBACK +ToasterCoInstaller ( + _In_ DI_FUNCTION InstallFunction, + _In_ HDEVINFO DeviceInfoSet, + _In_ PSP_DEVINFO_DATA DeviceInfoData, OPTIONAL + _Inout_ PCOINSTALLER_CONTEXT_DATA Context + ) +{ + switch (InstallFunction) + { + case DIF_INSTALLDEVICE: + if(!Context->PostProcessing) + { + TCHAR FriendlyName[MAX_PATH]; + BOOL fSuccess=FALSE; + DWORD dwRegType, UINumber; + size_t len; + + DbgOut("DIF_INSTALLDEVICE"); + + // + // We wil create here a friendly name for this device + // based on it's serial number. + // The bus driver returns the serial No. in the UINumber. + // field of the device capabiliities structure. + // So let us get that first . + // + fSuccess = + SetupDiGetDeviceRegistryProperty(DeviceInfoSet, + DeviceInfoData, + SPDRP_UI_NUMBER, + &dwRegType, + (BYTE*) &UINumber, + sizeof(UINumber), + NULL); + if (fSuccess) + { + // + // Cook a FriendlyName and add it to the registry + // + if (SUCCEEDED(StringCbPrintf(FriendlyName, + sizeof(FriendlyName), + TEXT("ToasterDevice%02u"), + UINumber))) { + + if (SUCCEEDED(StringCbLength(FriendlyName, + sizeof(FriendlyName), + &len))) { + fSuccess = SetupDiSetDeviceRegistryProperty(DeviceInfoSet, + DeviceInfoData, + SPDRP_FRIENDLYNAME, + (BYTE *)FriendlyName, + (DWORD)len + ); + if(!fSuccess) { + DbgOut("SetupDiSetDeviceRegistryProperty failed!"); + } + } + + } + + } + + // You can use PrivateData to pass Data needed for PostProcessing + // Context->PrivateData = Something; + + return ERROR_DI_POSTPROCESSING_REQUIRED; //Set for PostProcessing + } + else // post processing + { + INFCONTEXT InfContext; + HINF InfFile; + + // + // Sample code to show how you can process a custom section + // in your INF file. + // + DbgOut("DIF_INSTALLDEVICE PostProcessing"); + + if (INVALID_HANDLE_VALUE == (InfFile = + MyOpenInfFile(DeviceInfoSet,DeviceInfoData,NULL))) + { + return GetLastError(); + } + + if (SetupFindFirstLine(InfFile, // InfHandle + TEXT("MySection"), + TEXT("MySpecialFlag"), + &InfContext)) + { + DbgOut("DIF_INSTALLDEVICE MySpecicalFlag, Do something here!"); + + } + + } + break; + + case DIF_REMOVE: + DbgOut("DIF_REMOVE"); + break; + + case DIF_SELECTDEVICE: + DbgOut("DIF_SELECTDEVICE"); + break; + case DIF_ASSIGNRESOURCES: + DbgOut("DIF_ASSIGNRESOURCES"); + break; + case DIF_PROPERTIES: + DbgOut("DIF_PROPERTIES"); + break; + case DIF_FIRSTTIMESETUP: + DbgOut("DIF_FIRSTTIMESETUP"); + break; + case DIF_FOUNDDEVICE: + DbgOut("DIF_FOUNDDEVICE"); + break; + case DIF_SELECTCLASSDRIVERS: + DbgOut("DIF_SELECTCLASSDRIVERS"); + break; + case DIF_VALIDATECLASSDRIVERS: + DbgOut("DIF_VALIDATECLASSDRIVERS"); + break; + case DIF_INSTALLCLASSDRIVERS: + DbgOut("DIF_INSTALLCLASSDRIVERS"); + break; + case DIF_CALCDISKSPACE: + DbgOut("DIF_CALCDISKSPACE"); + break; + case DIF_DESTROYPRIVATEDATA: + DbgOut("DIF_DESTROYPRIVATEDATA"); + break; + case DIF_VALIDATEDRIVER: + DbgOut("DIF_VALIDATEDRIVER"); + break; + case DIF_MOVEDEVICE: + DbgOut("DIF_MOVEDEVICE"); + break; + case DIF_DETECT: + DbgOut("DIF_DETECT"); + break; + case DIF_INSTALLWIZARD: + DbgOut("DIF_INSTALLWIZARD"); + break; + case DIF_DESTROYWIZARDDATA: + DbgOut("DIF_DESTROYWIZARDDATA"); + break; + case DIF_PROPERTYCHANGE: + DbgOut("DIF_PROPERTYCHANGE"); + break; + case DIF_ENABLECLASS: + DbgOut("DIF_ENABLECLASS"); + break; + case DIF_DETECTVERIFY: + DbgOut("DIF_DETECTVERIFY"); + break; + case DIF_INSTALLDEVICEFILES: + DbgOut("DIF_INSTALLDEVICEFILES"); + break; + case DIF_ALLOW_INSTALL: + DbgOut("DIF_ALLOW_INSTALL"); + break; + case DIF_SELECTBESTCOMPATDRV: + DbgOut("DIF_SELECTBESTCOMPATDRV"); + break; + case DIF_REGISTERDEVICE: + DbgOut("DIF_REGISTERDEVICE"); + break; + case DIF_NEWDEVICEWIZARD_PRESELECT: + DbgOut("DIF_NEWDEVICEWIZARD_PRESELECT"); + break; + case DIF_NEWDEVICEWIZARD_SELECT: + DbgOut("DIF_NEWDEVICEWIZARD_SELECT"); + break; + case DIF_NEWDEVICEWIZARD_PREANALYZE: + DbgOut("DIF_NEWDEVICEWIZARD_PREANALYZE"); + break; + case DIF_NEWDEVICEWIZARD_POSTANALYZE: + DbgOut("DIF_NEWDEVICEWIZARD_POSTANALYZE"); + break; + case DIF_NEWDEVICEWIZARD_FINISHINSTALL: + DbgOut("DIF_NEWDEVICEWIZARD_FINISHINSTALL"); + break; + case DIF_INSTALLINTERFACES: + DbgOut("DIF_INSTALLINTERFACES"); + break; + case DIF_DETECTCANCEL: + DbgOut("DIF_DETECTCANCEL"); + break; + case DIF_REGISTER_COINSTALLERS: + DbgOut("DIF_REGISTER_COINSTALLERS"); + break; + case DIF_ADDPROPERTYPAGE_ADVANCED: + DbgOut("DIF_ADDPROPERTYPAGE_ADVANCED"); + break; + case DIF_ADDPROPERTYPAGE_BASIC: + DbgOut("DIF_ADDPROPERTYPAGE_BASIC"); + break; + case DIF_TROUBLESHOOTER: + DbgOut("DIF_TROUBLESHOOTER"); + break; + case DIF_POWERMESSAGEWAKE: + DbgOut("DIF_POWERMESSAGEWAKE"); + break; + default: + DbgOut("?????"); + break; + } + + return NO_ERROR; +} + + diff --git a/general/toaster/toastDrv/coinstaller/tostrco1.def b/general/toaster/toastDrv/coinstaller/tostrco1.def new file mode 100644 index 000000000..8927a8870 --- /dev/null +++ b/general/toaster/toastDrv/coinstaller/tostrco1.def @@ -0,0 +1,6 @@ +LIBRARY TOSTRCO1 + +EXPORTS + ToasterCoInstaller + + diff --git a/general/toaster/toastDrv/coinstaller/tostrco1.vcxproj b/general/toaster/toastDrv/coinstaller/tostrco1.vcxproj new file mode 100644 index 000000000..efbef4888 --- /dev/null +++ b/general/toaster/toastDrv/coinstaller/tostrco1.vcxproj @@ -0,0 +1,210 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {70FAA53D-F08B-4131-A5BD-FCAA3EFC48FF} + $(MSBuildProjectName) + Debug + Win32 + {2C2FDE7E-AF76-431C-B346-457A23F65F6D} + + + + 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) + + + + + + + + + + + + + + + + tostrco1 + + + tostrco1 + + + tostrco1 + + + tostrco1 + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + %(AdditionalDependencies);setupapi.lib;kernel32.lib;advapi32.lib;user32.lib + 0x2000000 + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + %(AdditionalDependencies);setupapi.lib;kernel32.lib;advapi32.lib;user32.lib + 0x2000000 + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + %(AdditionalDependencies);setupapi.lib;kernel32.lib;advapi32.lib;user32.lib + 0x2000000 + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH) + + + %(AdditionalDependencies);setupapi.lib;kernel32.lib;advapi32.lib;user32.lib + 0x2000000 + + + + + tostrco1.def + + + + + tostrco1.def + + + + + tostrco1.def + + + + + tostrco1.def + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/general/toaster/toastDrv/coinstaller/tostrco1.vcxproj.Filters b/general/toaster/toastDrv/coinstaller/tostrco1.vcxproj.Filters new file mode 100644 index 000000000..a9cd7e888 --- /dev/null +++ b/general/toaster/toastDrv/coinstaller/tostrco1.vcxproj.Filters @@ -0,0 +1,25 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {DA0ECA8C-145B-4D3A-8C0F-7DADEEABEE7C} + + + h;hpp;hxx;hm;inl;inc;xsd + {19EA12EE-E368-463D-B066-44D1E82DD776} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {E5E47602-89BD-4D0E-93C1-4EA3331105AA} + + + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/general/toaster/toastDrv/exe/enum/Enum.vcxproj b/general/toaster/toastDrv/exe/enum/Enum.vcxproj new file mode 100644 index 000000000..175e78978 --- /dev/null +++ b/general/toaster/toastDrv/exe/enum/Enum.vcxproj @@ -0,0 +1,182 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {A6E7CA25-D12B-4C39-9A01-BEEE71E04F37} + $(MSBuildProjectName) + Debug + Win32 + {9C22B158-73CF-4D66-B85C-4E4AE9DB0AC4} + + + + Windows10 + False + Universal + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + True + Universal + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + False + Universal + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + True + Universal + + WindowsApplicationForDrivers10.0 + Application + + + + $(IntDir) + + + + + + + + + + + + + + + + Enum + + + Enum + + + Enum + + + Enum + + + + %(AdditionalIncludeDirectories);..\..\kmdf\inc + %(PreprocessorDefinitions);EVENT_TRACING;UNICODE;_UNICODE + + + %(AdditionalIncludeDirectories);..\..\kmdf\inc + %(PreprocessorDefinitions);EVENT_TRACING;UNICODE;_UNICODE + + + + + %(AdditionalIncludeDirectories);..\..\kmdf\inc + %(PreprocessorDefinitions);EVENT_TRACING;UNICODE;_UNICODE + + + %(AdditionalDependencies);mincore.lib + + + + + %(AdditionalIncludeDirectories);..\..\kmdf\inc + %(PreprocessorDefinitions);EVENT_TRACING;UNICODE;_UNICODE + + + %(AdditionalIncludeDirectories);..\..\kmdf\inc + %(PreprocessorDefinitions);EVENT_TRACING;UNICODE;_UNICODE + + + + + %(AdditionalIncludeDirectories);..\..\kmdf\inc + %(PreprocessorDefinitions);EVENT_TRACING;UNICODE;_UNICODE + + + %(AdditionalDependencies);mincore.lib + + + + + %(AdditionalIncludeDirectories);..\..\kmdf\inc + %(PreprocessorDefinitions);EVENT_TRACING;UNICODE;_UNICODE + + + %(AdditionalIncludeDirectories);..\..\kmdf\inc + %(PreprocessorDefinitions);EVENT_TRACING;UNICODE;_UNICODE + + + + + %(AdditionalIncludeDirectories);..\..\kmdf\inc + %(PreprocessorDefinitions);EVENT_TRACING;UNICODE;_UNICODE + + + %(AdditionalDependencies);mincore.lib + + + + + %(AdditionalIncludeDirectories);..\..\kmdf\inc + %(PreprocessorDefinitions);EVENT_TRACING;UNICODE;_UNICODE + + + %(AdditionalIncludeDirectories);..\..\kmdf\inc + %(PreprocessorDefinitions);EVENT_TRACING;UNICODE;_UNICODE + + + + + %(AdditionalIncludeDirectories);..\..\kmdf\inc + %(PreprocessorDefinitions);EVENT_TRACING;UNICODE;_UNICODE + + + %(AdditionalDependencies);mincore.lib + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/general/toaster/toastDrv/exe/enum/Enum.vcxproj.Filters b/general/toaster/toastDrv/exe/enum/Enum.vcxproj.Filters new file mode 100644 index 000000000..dbbc99c69 --- /dev/null +++ b/general/toaster/toastDrv/exe/enum/Enum.vcxproj.Filters @@ -0,0 +1,22 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {4DF85533-7B9E-4C98-8769-7C6420BCE72E} + + + h;hpp;hxx;hm;inl;inc;xsd + {871CA74F-21C6-45DD-888C-CA8EA3F48965} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {5B1A9C9F-B7FB-4466-9E15-80E147D55C1A} + + + + + Source Files + + + \ No newline at end of file diff --git a/general/toaster/toastDrv/exe/enum/enum.c b/general/toaster/toastDrv/exe/enum/enum.c new file mode 100644 index 000000000..a8799fa74 --- /dev/null +++ b/general/toaster/toastDrv/exe/enum/enum.c @@ -0,0 +1,307 @@ +/*++ + +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 +#include "public.h" +#include + +// +// Prototypes +// +BOOLEAN +GetDevicePath( + _In_ LPCGUID InterfaceGuid, + _Out_writes_(BufLen) PWCHAR DevicePath, + _In_ size_t BufLen + ); + +BOOLEAN +OpenBusInterface( + _In_z_ LPCWSTR DevicePath + ); + +#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" + +#define MAX_DEVPATH_LENGTH 256 + +BOOLEAN bPlugIn, bUnplug, bEject; +ULONG SerialNo; + +INT __cdecl +main( + _In_ ULONG argc, + _In_reads_(argc) PCHAR argv[] + ) +{ + WCHAR devicePath[MAX_DEVPATH_LENGTH] = { 0 }; + + 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; + + if (GetDevicePath(&GUID_DEVINTERFACE_BUSENUM_TOASTER, + devicePath, + sizeof(devicePath) / sizeof(devicePath[0]))) { + OpenBusInterface(devicePath); + } + + return 0; +usage: + printf(USAGE); + exit(0); +} + +BOOLEAN +OpenBusInterface ( + _In_z_ LPCWSTR DevicePath + ) +{ + HANDLE file; + ULONG bytes; + BUSENUM_UNPLUG_HARDWARE unplug; + BUSENUM_EJECT_HARDWARE eject; + PBUSENUM_PLUGIN_HARDWARE hardware; + BOOLEAN bSuccess = FALSE; + + printf("Opening %ws\n", DevicePath); + + file = CreateFile(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()); + goto End; + } + + printf("Bus interface opened!!!\n"); + + // + // 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: + if (INVALID_HANDLE_VALUE != file) { + CloseHandle(file); + } + return bSuccess; +} + +BOOLEAN +GetDevicePath( + _In_ LPCGUID InterfaceGuid, + _Out_writes_(BufLen) PWCHAR DevicePath, + _In_ size_t BufLen +) +{ + CONFIGRET cr = CR_SUCCESS; + PWSTR deviceInterfaceList = NULL; + ULONG deviceInterfaceListLength = 0; + PWSTR nextInterface; + HRESULT hr = E_FAIL; + BOOLEAN bRet = TRUE; + + cr = CM_Get_Device_Interface_List_Size( + &deviceInterfaceListLength, + (LPGUID)InterfaceGuid, + NULL, + CM_GET_DEVICE_INTERFACE_LIST_PRESENT); + if (cr != CR_SUCCESS) { + printf("Error 0x%x retrieving device interface list size.\n", cr); + goto clean0; + } + + if (deviceInterfaceListLength <= 1) { + bRet = FALSE; + printf("Error: No active device interfaces found.\n" + " Is the sample driver loaded?"); + goto clean0; + } + + deviceInterfaceList = (PWSTR)malloc(deviceInterfaceListLength * sizeof(WCHAR)); + if (deviceInterfaceList == NULL) { + printf("Error allocating memory for device interface list.\n"); + goto clean0; + } + ZeroMemory(deviceInterfaceList, deviceInterfaceListLength * sizeof(WCHAR)); + + cr = CM_Get_Device_Interface_List( + (LPGUID)InterfaceGuid, + NULL, + deviceInterfaceList, + deviceInterfaceListLength, + CM_GET_DEVICE_INTERFACE_LIST_PRESENT); + if (cr != CR_SUCCESS) { + printf("Error 0x%x retrieving device interface list.\n", cr); + goto clean0; + } + + nextInterface = deviceInterfaceList + wcslen(deviceInterfaceList) + 1; + if (*nextInterface != UNICODE_NULL) { + printf("Warning: More than one device interface instance found. \n" + "Selecting first matching device.\n\n"); + } + + hr = StringCchCopy(DevicePath, BufLen, deviceInterfaceList); + if (FAILED(hr)) { + bRet = FALSE; + printf("Error: StringCchCopy failed with HRESULT 0x%x", hr); + goto clean0; + } + +clean0: + if (deviceInterfaceList != NULL) { + free(deviceInterfaceList); + } + if (CR_SUCCESS != cr) { + bRet = FALSE; + } + + return bRet; +} diff --git a/general/toaster/toastDrv/exe/notify/notify.c b/general/toaster/toastDrv/exe/notify/notify.c new file mode 100644 index 000000000..3362ce3d9 --- /dev/null +++ b/general/toaster/toastDrv/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/toastDrv/exe/notify/notify.h b/general/toaster/toastDrv/exe/notify/notify.h new file mode 100644 index 000000000..98db2918b --- /dev/null +++ b/general/toaster/toastDrv/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/toastDrv/exe/notify/notify.rc b/general/toaster/toastDrv/exe/notify/notify.rc new file mode 100644 index 000000000..eb15657f9 --- /dev/null +++ b/general/toaster/toastDrv/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/toastDrv/exe/notify/notify.vcxproj b/general/toaster/toastDrv/exe/notify/notify.vcxproj new file mode 100644 index 000000000..ded7bd0bd --- /dev/null +++ b/general/toaster/toastDrv/exe/notify/notify.vcxproj @@ -0,0 +1,171 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {AFCA7BE8-3FDB-4257-AAAF-EBE85BC05EDD} + $(MSBuildProjectName) + Debug + Win32 + {C26150C1-ABE5-4264-BD23-6449114CA46C} + + + + 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);..\..\kmdf\inc + + + %(AdditionalIncludeDirectories);..\..\kmdf\inc + + + + + %(AdditionalIncludeDirectories);..\..\kmdf\inc + + + %(AdditionalDependencies);setupapi.lib + + + + + %(AdditionalIncludeDirectories);..\..\kmdf\inc + + + %(AdditionalIncludeDirectories);..\..\kmdf\inc + + + + + %(AdditionalIncludeDirectories);..\..\kmdf\inc + + + %(AdditionalDependencies);setupapi.lib + + + + + %(AdditionalIncludeDirectories);..\..\kmdf\inc + + + %(AdditionalIncludeDirectories);..\..\kmdf\inc + + + + + %(AdditionalIncludeDirectories);..\..\kmdf\inc + + + %(AdditionalDependencies);setupapi.lib + + + + + %(AdditionalIncludeDirectories);..\..\kmdf\inc + + + %(AdditionalIncludeDirectories);..\..\kmdf\inc + + + + + %(AdditionalIncludeDirectories);..\..\kmdf\inc + + + %(AdditionalDependencies);setupapi.lib + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/general/toaster/toastDrv/exe/notify/notify.vcxproj.Filters b/general/toaster/toastDrv/exe/notify/notify.vcxproj.Filters new file mode 100644 index 000000000..1ba140e26 --- /dev/null +++ b/general/toaster/toastDrv/exe/notify/notify.vcxproj.Filters @@ -0,0 +1,27 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {D1EC8595-68A9-4660-8CDA-5C17574C450B} + + + h;hpp;hxx;hm;inl;inc;xsd + {75CBFBEA-4EA9-4872-8030-99429611E8A1} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {1F8FFCF9-E0C1-4EBE-8C4E-76C812637E15} + + + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/general/toaster/toastDrv/exe/notify/toaster.ico b/general/toaster/toastDrv/exe/notify/toaster.ico new file mode 100644 index 0000000000000000000000000000000000000000..77ad2081ae46c21521d966a06d53d8429cacd307 GIT binary patch literal 4534 zcmeH~KTKRl5Ql#UNSrL>3k?;L&lFMUkhY?7M33Z#8dqD>VJUxj6{bY8gtV1i#8FbD zA!VAYQkbowF|s8JEEI`H#Yv7b`F7qr42q>RQ5wHJW@l%1cjkMu`%Z_{)0Bq8FI3w5 zDd|h}%8Hx%>ArM-TKWoehdIta0qPghfwLKG<_bQz*F8)J0F1usdegW*>e<{r1AHzjeoMNW@bkHeqVEQbDE!@*TTYr78e(_w6vt<sC-RpgO5{(}Cc%(k zNYtSm8&&bA$AhLX$4Ct_`7&%7j!Zs3+QLB%G90;Z@L+!2Y|5}?STYcqgnk8J$!yFl z$~ZDC{2|2QOfoFF)WccIZ78RJ4+ew5U@#aA27|$`6AT#!gTY|PFc1b|Jq!ke!C){D z8o>{P!C){L07m{yFk_>!C=w%LM>x!5mo-rj^-7zYo8rq(ySuwOI5^PJ(UA@h4|Q^K zqSMn;ot>TO;^IPAS63R3$ErV=et7wk_qd|E +#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/toastDrv/exe/toast/toast.vcxproj b/general/toaster/toastDrv/exe/toast/toast.vcxproj new file mode 100644 index 000000000..d06a207d5 --- /dev/null +++ b/general/toaster/toastDrv/exe/toast/toast.vcxproj @@ -0,0 +1,170 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {36EF502E-38C8-4BAB-B7FE-C9ED9194292E} + $(MSBuildProjectName) + Debug + Win32 + {9B71A258-9AC3-4501-B5D5-03FD83F9F40A} + + + + 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);..\..\kmdf\inc + + + %(AdditionalIncludeDirectories);..\..\kmdf\inc + + + + + %(AdditionalIncludeDirectories);..\..\kmdf\inc + + + %(AdditionalDependencies);setupapi.lib + + + + + %(AdditionalIncludeDirectories);..\..\kmdf\inc + + + %(AdditionalIncludeDirectories);..\..\kmdf\inc + + + + + %(AdditionalIncludeDirectories);..\..\kmdf\inc + + + %(AdditionalDependencies);setupapi.lib + + + + + %(AdditionalIncludeDirectories);..\..\kmdf\inc + + + %(AdditionalIncludeDirectories);..\..\kmdf\inc + + + + + %(AdditionalIncludeDirectories);..\..\kmdf\inc + + + %(AdditionalDependencies);setupapi.lib + + + + + %(AdditionalIncludeDirectories);..\..\kmdf\inc + + + %(AdditionalIncludeDirectories);..\..\kmdf\inc + + + + + %(AdditionalIncludeDirectories);..\..\kmdf\inc + + + %(AdditionalDependencies);setupapi.lib + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/general/toaster/toastDrv/exe/toast/toast.vcxproj.Filters b/general/toaster/toastDrv/exe/toast/toast.vcxproj.Filters new file mode 100644 index 000000000..a8533f0bc --- /dev/null +++ b/general/toaster/toastDrv/exe/toast/toast.vcxproj.Filters @@ -0,0 +1,22 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {8C1E3DAE-53E3-457C-98F3-FB405BC9A120} + + + h;hpp;hxx;hm;inl;inc;xsd + {C2C50665-6AB2-4502-B4A2-29496B9935F0} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {D0127FBE-76C6-4B7C-91D7-DC444B6B223D} + + + + + Source Files + + + \ No newline at end of file diff --git a/general/toaster/toastDrv/exe/wmi/WmiToast.vcxproj b/general/toaster/toastDrv/exe/wmi/WmiToast.vcxproj new file mode 100644 index 000000000..a1ab7caf8 --- /dev/null +++ b/general/toaster/toastDrv/exe/wmi/WmiToast.vcxproj @@ -0,0 +1,176 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {9FF1CCE5-AD65-444C-97C1-179619AA2479} + $(MSBuildProjectName) + Debug + Win32 + {519C06B4-3DEE-4917-9D48-4EA2905EF515} + + + + 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) + + + + + + + + + + + + + + + + WmiToast + + + WmiToast + + + WmiToast + + + WmiToast + + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\vccomsup.lib + %(AdditionalDependencies);ole32.lib;oleaut32.lib;uuid.lib;wbemuuid.lib + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\vccomsup.lib + %(AdditionalDependencies);ole32.lib;oleaut32.lib;uuid.lib;wbemuuid.lib + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\vccomsup.lib + %(AdditionalDependencies);ole32.lib;oleaut32.lib;uuid.lib;wbemuuid.lib + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\vccomsup.lib + %(AdditionalDependencies);ole32.lib;oleaut32.lib;uuid.lib;wbemuuid.lib + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/general/toaster/toastDrv/exe/wmi/WmiToast.vcxproj.Filters b/general/toaster/toastDrv/exe/wmi/WmiToast.vcxproj.Filters new file mode 100644 index 000000000..d9b9e30b3 --- /dev/null +++ b/general/toaster/toastDrv/exe/wmi/WmiToast.vcxproj.Filters @@ -0,0 +1,28 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {779E4F7E-89F5-41F9-92C2-C9E8B7060B92} + + + h;hpp;hxx;hm;inl;inc;xsd + {BFEAC96B-E61B-4729-B62C-A7F52A16E99C} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {D5FE79F8-F00F-4D49-B219-CB749AD7DD7A} + + + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/general/toaster/toastDrv/exe/wmi/wmiexecute.cpp b/general/toaster/toastDrv/exe/wmi/wmiexecute.cpp new file mode 100644 index 000000000..85b7f0fa6 --- /dev/null +++ b/general/toaster/toastDrv/exe/wmi/wmiexecute.cpp @@ -0,0 +1,532 @@ +#include "wmitoast.h" + +#define TOASTER_METHOD_CLASS L"ToasterControl" +#define TOASTER_METHOD_1 L"ToasterControl1" +#define TOASTER_METHOD_2 L"ToasterControl2" +#define TOASTER_METHOD_3 L"ToasterControl3" +#define IN_DATA1_VALUE 25 +#define IN_DATA2_VALUE 30 + +// +// Private methods. +// + +HRESULT +ExecuteMethod1InInstance( + _In_ IWbemServices* WbemServices, + _In_ IWbemClassObject* ClassObj, + _In_ const BSTR InstancePath, + _In_ ULONG InData + ); + +HRESULT +ExecuteMethod2InInstance( + _In_ IWbemServices* WbemServices, + _In_ IWbemClassObject* ClassObj, + _In_ const BSTR InstancePath, + _In_ ULONG InData1, + _In_ ULONG InData2 + ); + +HRESULT +ExecuteMethod3InInstance( + _In_ IWbemServices* WbemServices, + _In_ IWbemClassObject* ClassObj, + _In_ const BSTR InstancePath, + _In_ ULONG InData1, + _In_ ULONG InData2 + ); + + +HRESULT +ExecuteMethodsInClass( + _In_ IWbemServices* WbemServices, + _In_opt_ PWSTR UserId, + _In_opt_ PWSTR Password, + _In_opt_ PWSTR DomainName + ) + +/*++ + +Routine Description: + + This routine enumerates the instances of the Toaster method class and + executes the methods in the class for each instance. + +Arguments: + + WbemServices - Pointer to the WBEM services interface used for accessing + the WMI services. + + UserId - Pointer to the user id information or NULL. + + Password - Pointer to password or NULL. If the user id is not specified, + this parameter is ignored. + + DomainName - Pointer to domain name or NULL. If the user id is not specified, + this parameter is ignored. + +Return Value: + + HRESULT Status code. + +--*/ + +{ + HRESULT status = S_OK; + + IEnumWbemClassObject* enumerator = NULL; + IWbemClassObject* classObj = NULL; + IWbemClassObject* instanceObj = NULL; + + const BSTR className = SysAllocString(TOASTER_METHOD_CLASS); + + VARIANT pathVariable; + _bstr_t instancePath; + ULONG nbrObjsSought = 1; + ULONG nbrObjsReturned; + + // + // Create an Enumeration object to enumerate the instances of the given class. + // + status = WbemServices->CreateInstanceEnum(className, + WBEM_FLAG_SHALLOW | WBEM_FLAG_RETURN_IMMEDIATELY | WBEM_FLAG_FORWARD_ONLY, + NULL, + &enumerator); + if (FAILED(status)) { + goto exit; + } + + // + // Set authentication information for the interface. + // + status = SetInterfaceSecurity(enumerator, UserId, Password, DomainName); + if (FAILED(status)) { + goto exit; + } + + // + // Get the class object for the method definition. + // + status = WbemServices->GetObject(className, 0, NULL, &classObj, NULL); + if (FAILED(status) || NULL == classObj) { + goto exit; + } + + do { + + // + // Get the instance object for each instance of the class. + // + status = enumerator->Next(WBEM_INFINITE, + nbrObjsSought, + &instanceObj, + &nbrObjsReturned); + + if (status == WBEM_S_FALSE) { + status = S_OK; + break; + } + + if (FAILED(status)) { + if (status == WBEM_E_INVALID_CLASS) { + printf("ERROR: Toaster driver may not be active on the system.\n"); + } + goto exit; + } + + // + // To obtain the object path of the object for which the method has to be + // executed, query the "__PATH" property of the WMI instance object. + // + status = instanceObj->Get(_bstr_t(L"__PATH"), 0, &pathVariable, NULL, NULL); + if (FAILED(status)) { + goto exit; + } + + instancePath = pathVariable.bstrVal; + instanceObj->Release(); + instanceObj = NULL; + + // + // Execute the methods in this instance of the class. + // + status = ExecuteMethod1InInstance(WbemServices, classObj, instancePath, IN_DATA1_VALUE); + if (FAILED(status)) { + goto exit; + } + + status = ExecuteMethod2InInstance(WbemServices, classObj, instancePath, IN_DATA1_VALUE, IN_DATA2_VALUE); + if (FAILED(status)) { + goto exit; + } + + status = ExecuteMethod3InInstance(WbemServices, classObj, instancePath, IN_DATA1_VALUE, IN_DATA2_VALUE); + if (FAILED(status)) { + goto exit; + } + } while (!FAILED(status)); + +exit: + + if (className != NULL) { + SysFreeString(className); + } + + if (classObj != NULL) { + classObj->Release(); + } + + if (enumerator != NULL) { + enumerator->Release(); + } + + if (instanceObj != NULL) { + instanceObj->Release(); + } + + return status; +} + + +HRESULT +ExecuteMethod1InInstance( + _In_ IWbemServices* WbemServices, + _In_ IWbemClassObject* ClassObj, + _In_ const BSTR InstancePath, + _In_ ULONG InData + ) +{ + HRESULT status; + + IWbemClassObject* inputParamsObj = NULL; + IWbemClassObject* inputParamsInstanceObj = NULL; + IWbemClassObject* outputParamsInstanceObj = NULL; + + const BSTR methodName = SysAllocString(TOASTER_METHOD_1); + VARIANT funcParam; + + // + // Get the input parameters class objects for the method. + // + status = ClassObj->GetMethod(methodName, 0, &inputParamsObj, NULL); + if (FAILED(status)) { + goto exit; + } + + // + // Spawn an instance of the input parameters class object. + // + status = inputParamsObj->SpawnInstance(0, &inputParamsInstanceObj); + if (FAILED(status)) { + goto exit; + } + + // + // Set the input variables values (i.e., inData for ToasterMethod1). + // + funcParam.vt = VT_I4; + funcParam.ulVal = InData; + + status = inputParamsInstanceObj->Put(L"InData", 0, &funcParam, 0); + if (FAILED(status)) { + goto exit; + } + + // + // Call the method. + // + printf("\n"); + printf("Instance Path .: %ws\n", (wchar_t*)InstancePath); + printf(" Method Name..: %ws\n", (wchar_t*)methodName); + status = WbemServices->ExecMethod(InstancePath, + methodName, + 0, + NULL, + inputParamsInstanceObj, + &outputParamsInstanceObj, + NULL); + + if (FAILED(status) || NULL == outputParamsInstanceObj) { + goto exit; + } + + // + // Get the "in" Parameter values from the input parameters object. + // + status = inputParamsInstanceObj->Get(L"InData", 0, &funcParam, NULL, NULL); + if (FAILED(status)) { + goto exit; + } + printf(" InData....: %d\n", funcParam.ulVal); + + // + // Get the "out" Parameter values from the output parameters object. + // + status = outputParamsInstanceObj->Get(L"OutData", 0, &funcParam, NULL, NULL); + if (FAILED(status)) { + goto exit; + } + printf(" OutData...: %d\n", funcParam.ulVal); + +exit: + + if (methodName != NULL) { + SysFreeString(methodName); + } + + if (inputParamsObj != NULL) { + inputParamsObj->Release(); + } + + if (inputParamsInstanceObj != NULL) { + inputParamsInstanceObj->Release(); + } + + if (outputParamsInstanceObj != NULL) { + outputParamsInstanceObj->Release(); + } + + return status; +} + + +HRESULT +ExecuteMethod2InInstance( + _In_ IWbemServices* WbemServices, + _In_ IWbemClassObject* ClassObj, + _In_ const BSTR InstancePath, + _In_ ULONG InData1, + _In_ ULONG InData2 + ) +{ + HRESULT status; + + IWbemClassObject* inputParamsObj = NULL; + IWbemClassObject* inputParamsInstanceObj = NULL; + IWbemClassObject* outputParamsInstanceObj = NULL; + + const BSTR methodName = SysAllocString(TOASTER_METHOD_2); + VARIANT funcParam; + + // + // Get the input parameters class objects for the method. + // + status = ClassObj->GetMethod(methodName, 0, &inputParamsObj, NULL); + if (FAILED(status)) { + goto exit; + } + + // + // Spawn an instance of the input parameters class object. + // + status = inputParamsObj->SpawnInstance(0, &inputParamsInstanceObj); + if (FAILED(status)) { + goto exit; + } + + // + // Set the input variables values (i.e., inData1, inData2 for ToasterMethod2). + // + funcParam.vt = VT_I4; + funcParam.ulVal = InData1; + + status = inputParamsInstanceObj->Put(L"InData1", 0, &funcParam, 0); + if (FAILED(status)) { + goto exit; + } + + funcParam.vt = VT_I4; + funcParam.ulVal = InData2; + + status = inputParamsInstanceObj->Put(L"InData2", 0, &funcParam, 0); + if (FAILED(status)) { + goto exit; + } + + // + // Call the method. + // + printf("\n"); + printf("Instance Path .: %ws\n", (wchar_t*)InstancePath); + printf(" Method Name..: %ws\n", (wchar_t*)methodName); + status = WbemServices->ExecMethod(InstancePath, + methodName, + 0, + NULL, + inputParamsInstanceObj, + &outputParamsInstanceObj, + NULL); + + if (FAILED(status) || NULL == outputParamsInstanceObj) { + goto exit; + } + + // + // Get the "in" Parameter values from the input parameters object. + // + status = inputParamsInstanceObj->Get(L"InData1", 0, &funcParam, NULL, NULL); + if (FAILED(status)) { + goto exit; + } + printf(" InData1...: %d\n", funcParam.ulVal); + + status = inputParamsInstanceObj->Get(L"InData2", 0, &funcParam, NULL, NULL); + if (FAILED(status)) { + goto exit; + } + printf(" InData2...: %d\n", funcParam.ulVal); + + // + // Get the "out" Parameter values from the output parameters object. + // + status = outputParamsInstanceObj->Get(L"OutData", 0, &funcParam, NULL, NULL); + if (FAILED(status)) { + goto exit; + } + printf(" OutData...: %d\n", funcParam.ulVal); + +exit: + + if (methodName != NULL) { + SysFreeString(methodName); + } + + if (inputParamsObj != NULL) { + inputParamsObj->Release(); + } + + if (inputParamsInstanceObj != NULL) { + inputParamsInstanceObj->Release(); + } + + if (outputParamsInstanceObj != NULL) { + outputParamsInstanceObj->Release(); + } + + return status; +} + + +HRESULT +ExecuteMethod3InInstance( + _In_ IWbemServices* WbemServices, + _In_ IWbemClassObject* ClassObj, + _In_ const BSTR InstancePath, + _In_ ULONG InData1, + _In_ ULONG InData2 + ) +{ + HRESULT status; + + IWbemClassObject* inputParamsObj = NULL; + IWbemClassObject* inputParamsInstanceObj = NULL; + IWbemClassObject* outputParamsInstanceObj = NULL; + + const BSTR methodName = SysAllocString(TOASTER_METHOD_3); + VARIANT funcParam; + + // + // Get the input parameters class objects for the method. + // + status = ClassObj->GetMethod(methodName, 0, &inputParamsObj, NULL); + if (FAILED(status)) { + goto exit; + } + + // + // Spawn an instance of the input parameters class object. + // + status = inputParamsObj->SpawnInstance(0, &inputParamsInstanceObj); + if (FAILED(status)) { + goto exit; + } + + // + // Set the input variables values (i.e., inData1, inData2 for ToasterMethod3). + // + funcParam.vt = VT_I4; + funcParam.ulVal = InData1; + + status = inputParamsInstanceObj->Put(L"InData1", 0, &funcParam, 0); + if (FAILED(status)) { + goto exit; + } + + funcParam.vt = VT_I4; + funcParam.ulVal = InData2; + + status = inputParamsInstanceObj->Put(L"InData2", 0, &funcParam, 0); + if (FAILED(status)) { + goto exit; + } + + // + // Call the method. + // + printf("\n"); + printf("Instance Path .: %ws\n", (wchar_t*)InstancePath); + printf(" Method Name..: %ws\n", (wchar_t*)methodName); + status = WbemServices->ExecMethod(InstancePath, + methodName, + 0, + NULL, + inputParamsInstanceObj, + &outputParamsInstanceObj, + NULL); + + if (FAILED(status) || NULL == outputParamsInstanceObj) { + goto exit; + } + + // + // Get the "in" Parameter values from the input parameters object. + // + status = inputParamsInstanceObj->Get(L"InData1", 0, &funcParam, NULL, NULL); + if (FAILED(status)) { + goto exit; + } + printf(" InData1...: %d\n", funcParam.ulVal); + + status = inputParamsInstanceObj->Get(L"InData2", 0, &funcParam, NULL, NULL); + if (FAILED(status)) { + goto exit; + } + printf(" InData2...: %d\n", funcParam.ulVal); + + // + // Get the "out" Parameter values from the output parameters object. + // + status = outputParamsInstanceObj->Get(L"OutData1", 0, &funcParam, NULL, NULL); + if (FAILED(status)) { + goto exit; + } + printf(" OutData1..: %d\n", funcParam.ulVal); + + status = outputParamsInstanceObj->Get(L"OutData2", 0, &funcParam, NULL, NULL); + if (FAILED(status)) { + goto exit; + } + printf(" OutData2..: %d\n", funcParam.ulVal); + +exit: + + if (methodName != NULL) { + SysFreeString(methodName); + } + + if (inputParamsObj != NULL) { + inputParamsObj->Release(); + } + + if (inputParamsInstanceObj != NULL) { + inputParamsInstanceObj->Release(); + } + + if (outputParamsInstanceObj != NULL) { + outputParamsInstanceObj->Release(); + } + + return status; +} + diff --git a/general/toaster/toastDrv/exe/wmi/wmigetset.cpp b/general/toaster/toastDrv/exe/wmi/wmigetset.cpp new file mode 100644 index 000000000..07735c2cd --- /dev/null +++ b/general/toaster/toastDrv/exe/wmi/wmigetset.cpp @@ -0,0 +1,168 @@ +#include "wmitoast.h" + +#define TOASTER_DEVICE_INFO_CLASS L"ToasterControl" +#define TOASTER_VAR1 L"ControlValue" + + +HRESULT +GetAndSetValuesInClass( + _In_ IWbemServices* WbemServices, + _In_opt_ PWSTR UserId, + _In_opt_ PWSTR Password, + _In_opt_ PWSTR DomainName + ) + +/*++ + +Routine Description: + + This routine enumerates the instances of the Toaster Device Information + class and gets/sets the value of one of its Properties. + +Arguments: + + WbemServices - Pointer to the WBEM services interface used for accessing + the WMI services. + + UserId - Pointer to the user id information or NULL. + + Password - Pointer to password or NULL. If the user id is not specified, + this parameter is ignored. + + DomainName - Pointer to domain name or NULL. If the user id is not specified, + this parameter is ignored. + +Return Value: + + HRESULT Status code. + +--*/ + +{ + HRESULT status = S_OK; + + IEnumWbemClassObject* enumerator = NULL; + IWbemClassObject* classObj = NULL; + IWbemClassObject* instanceObj = NULL; + + const BSTR className = SysAllocString(TOASTER_DEVICE_INFO_CLASS); + + VARIANT instProperty; + ULONG nbrObjsSought = 1; + ULONG nbrObjsReturned; + + // + // Create an Enumeration object to enumerate the instances of the given class. + // + status = WbemServices->CreateInstanceEnum(className, + WBEM_FLAG_SHALLOW | WBEM_FLAG_RETURN_IMMEDIATELY | WBEM_FLAG_FORWARD_ONLY, + NULL, + &enumerator); + if (FAILED(status)) { + goto exit; + } + + // + // Set authentication information for the interface. + // + status = SetInterfaceSecurity(enumerator, UserId, Password, DomainName); + if (FAILED(status)) { + goto exit; + } + + do { + + // + // Get the instance object for each instance of the class. + // + status = enumerator->Next(WBEM_INFINITE, + nbrObjsSought, + &instanceObj, + &nbrObjsReturned); + + if (status == WBEM_S_FALSE) { + status = S_OK; + break; + } + + if (FAILED(status)) { + if (status == WBEM_E_INVALID_CLASS) { + printf("ERROR: Toaster driver may not be active on the system.\n"); + } + goto exit; + } + + // + // To obtain the object path of the object for which the method has to be + // executed, query the "__PATH" property of the WMI instance object. + // + status = instanceObj->Get(_bstr_t(L"__PATH"), 0, &instProperty, 0, 0); + if (FAILED(status)) { + goto exit; + } + + printf("\n"); + printf("Instance Path .: %ws\n", (wchar_t*)instProperty.bstrVal); + + // + // Get the current value of the DummyValue property. + // + status = instanceObj->Get(TOASTER_VAR1, 0, &instProperty, NULL, NULL); + if (FAILED(status)) { + goto exit; + } + + printf(" Property ....: %ws\n", TOASTER_VAR1); + printf(" Old Value ...: %d\n", instProperty.lVal); + + // + // Set a new value for the DummyValue property. + // + instProperty.lVal++; + + status = instanceObj->Put(TOASTER_VAR1, 0, &instProperty, 0); + if (FAILED(status)) { + goto exit; + } + + status = WbemServices->PutInstance(instanceObj, + WBEM_FLAG_UPDATE_ONLY, + NULL, + NULL); + if (FAILED(status)) { + goto exit; + } + + status = instanceObj->Get(_bstr_t(TOASTER_VAR1), 0, &instProperty, NULL, NULL); + if (FAILED(status)) { + goto exit; + } + + printf(" New Value ...: %d\n", instProperty.lVal); + + instanceObj->Release(); + instanceObj = NULL; + + } while (!FAILED(status)); + +exit: + + if (className != NULL) { + SysFreeString(className); + } + + if (classObj != NULL) { + classObj->Release(); + } + + if (enumerator != NULL) { + enumerator->Release(); + } + + if (instanceObj != NULL) { + instanceObj->Release(); + } + + return status; +} + diff --git a/general/toaster/toastDrv/exe/wmi/wmitoast.cpp b/general/toaster/toastDrv/exe/wmi/wmitoast.cpp new file mode 100644 index 000000000..921fcb555 --- /dev/null +++ b/general/toaster/toastDrv/exe/wmi/wmitoast.cpp @@ -0,0 +1,535 @@ +#include "wmitoast.h" + + +// +// Private methods. +// + +bool +ParseCommandLine( + _In_ ULONG Argc, + _In_reads_(Argc) PWSTR Argv[], + _Outptr_result_maybenull_ PWSTR* ComputerName, + _Outptr_result_maybenull_ PWSTR* UserId, + _Outptr_result_maybenull_ PWSTR* Password, + _Outptr_result_maybenull_ PWSTR* DomainName + ); + +void +DisplayUsage(); + + + +ULONG +_cdecl +wmain( + _In_ ULONG Argc, + _In_reads_(Argc) PWSTR Argv[] + ) +{ + HRESULT status = S_OK; + BOOLEAN initialized = FALSE; + + BSTR temp = NULL; + BSTR wmiRoot = NULL; + BSTR userIdString = NULL; + BSTR passwordString = NULL; + + PWSTR ComputerName = NULL; + PWSTR userId = NULL; + PWSTR password = NULL; + PWSTR domain = NULL; + + IWbemLocator* wbemLocator = NULL; + IWbemServices* wbemServices = NULL; + + // + // Parse the input command line parameters. + // + if (!ParseCommandLine(Argc, + Argv, + &ComputerName, + &userId, + &password, + &domain)) { + // + // Display the usage information if there was an error parsing the input + // parameters or if usage information was requested. + // + status = E_INVALIDARG; + DisplayUsage(); + goto exit; + } + + // + // Initialize COM environment for multi-threaded concurrency. + // + status = CoInitializeEx(NULL, COINIT_MULTITHREADED); + if (FAILED(status)) { + goto exit; + } + + initialized = TRUE; + + // + // Initialize the security layer and set the specified values as the + // security default for the process. + // + status = CoInitializeSecurity(NULL, + -1, + NULL, + NULL, + RPC_C_AUTHN_LEVEL_PKT, + RPC_C_IMP_LEVEL_IMPERSONATE, + NULL, + EOAC_NONE, + 0); + + if (FAILED(status)) { + goto exit; + } + + // + // Create a single uninitialized object associated with the class id + // CLSID_WbemLocator. + // + status = CoCreateInstance(CLSID_WbemLocator, + NULL, + CLSCTX_INPROC_SERVER, + IID_IWbemLocator, + (PVOID*)&wbemLocator); + + if (FAILED(status)) { + goto exit; + } + + // + // Construct the object path for the WMI namespace. For local access to the + // WMI namespace, use a simple object path: "\\.\root\WMI". For access to + // the WMI namespace on a remote computer, include the computer name in the + // object path: "\\myserver\root\WMI". + // + if (ComputerName != NULL) { + + status = VarBstrCat(_bstr_t(L"\\\\"), _bstr_t(ComputerName), &temp); + if (FAILED(status)) { + goto exit; + } + + } else { + + status = VarBstrCat(_bstr_t(L"\\\\"), _bstr_t(L"."), &temp); + if (FAILED(status)) { + goto exit; + } + } + + status = VarBstrCat(temp, _bstr_t(L"\\root\\WMI"), &wmiRoot); + if (FAILED(status)) { + goto exit; + } + + SysFreeString(temp); + temp = NULL; + + // + // Construct the user id and password strings. + // + if (userId != NULL) { + + if (domain != NULL) { + + status = VarBstrCat(_bstr_t(domain), _bstr_t(L"\\"), &temp); + if (FAILED(status)) { + goto exit; + } + + status = VarBstrCat(temp, _bstr_t(userId), &userIdString); + if (FAILED(status)) { + goto exit; + } + + SysFreeString(temp); + temp = NULL; + + } else { + + userIdString = SysAllocString(userId); + if (userIdString == NULL) { + status = E_OUTOFMEMORY; + goto exit; + } + } + + passwordString = SysAllocString(password); + if (passwordString == NULL) { + status = E_OUTOFMEMORY; + goto exit; + } + } + + // + // Connect to the WMI server on this computer and, possibly, through it to another system. + // + status = wbemLocator->ConnectServer(wmiRoot, + userIdString, + passwordString, + NULL, + 0, + NULL, + NULL, + &wbemServices); + if (FAILED(status)) { + if (status != WBEM_E_LOCAL_CREDENTIALS) { + goto exit; + } + + // + // Use the identity inherited from the current process. + // + status = wbemLocator->ConnectServer(wmiRoot, + NULL, + NULL, + NULL, + 0, + NULL, + NULL, + &wbemServices); + if (FAILED(status)) { + goto exit; + } + } + + // + // Set authentication information for the interface. + // + status = SetInterfaceSecurity(wbemServices, userId, password, domain); + if (FAILED(status)) { + goto exit; + } + + // + // Execute the methods in each instance of the desired class. + // + printf("\n1. Execute Methods in class ...\n"); + status = ExecuteMethodsInClass(wbemServices, + userId, + password, + domain); + if (FAILED(status)) { + goto exit; + } + + // + // Get and Set the property values in each instance of the desired class. + // + printf("\n2. Get/Set Property Values in class ...\n"); + status = GetAndSetValuesInClass(wbemServices, + userId, + password, + domain); + if (FAILED(status)) { + goto exit; + } + +exit: + + if (temp != NULL) { + SysFreeString(temp); + } + + if (userIdString != NULL) { + SysFreeString(userIdString); + } + + if (passwordString != NULL) { + SysFreeString(passwordString); + } + + if (wmiRoot != NULL) { + SysFreeString(wmiRoot); + } + + if (wbemServices != NULL) { + wbemServices->Release(); + } + + if (wbemLocator != NULL) { + wbemLocator->Release(); + } + + if (initialized == TRUE) { + CoUninitialize(); + } + + if (FAILED(status)) { + printf("FAILED with Status = 0x%08x\n", status); + } + return status; +} + + +HRESULT +SetInterfaceSecurity( + _In_ IUnknown* InterfaceObj, + _In_opt_ PWSTR UserId, + _In_opt_ PWSTR Password, + _In_opt_ PWSTR DomainName + ) + +/*++ + +Routine Description: + + Set the interface security to allow the server to impersonate the specified + user. + +Arguments: + + InterfaceObj - Pointer to interface for which the security settings need + to be applied. + + UserId - Pointer to the user id information or NULL. + + Password - Pointer to password or NULL. If the user id is not specified, + this parameter is ignored. + + DomainName - Pointer to domain name or NULL. If the user id is not specified, + this parameter is ignored. + +Return Value: + + HRESULT Status code. + +--*/ + +{ + HRESULT hr; + + COAUTHIDENTITY AuthIdentity; + DWORD AuthnSvc; + DWORD AuthzSvc; + DWORD AuthnLevel; + DWORD ImpLevel; + DWORD Capabilities; + PWSTR pServerPrinName = NULL; + RPC_AUTH_IDENTITY_HANDLE pAuthHndl = NULL; + + // + // Get current authentication information for interface. + // + hr = CoQueryProxyBlanket(InterfaceObj, + &AuthnSvc, + &AuthzSvc, + &pServerPrinName, + &AuthnLevel, + &ImpLevel, + &pAuthHndl, + &Capabilities); + + if (FAILED(hr)) { + goto exit; + } + + if (UserId == NULL) { + + AuthIdentity.User = NULL; + AuthIdentity.UserLength = 0; + AuthIdentity.Password = NULL; + AuthIdentity.PasswordLength = 0; + AuthIdentity.Domain = NULL; + AuthIdentity.DomainLength = 0; + + } else { + + AuthIdentity.User = (USHORT *) UserId; +#pragma prefast(suppress:6387, "0 length UserId is valid") + AuthIdentity.UserLength = (ULONG)wcslen(UserId); + AuthIdentity.Password = (USHORT *) Password; +#pragma prefast(suppress:6387, "0 length Password is valid") + AuthIdentity.PasswordLength = (ULONG)wcslen(Password); + AuthIdentity.Domain = (USHORT *) DomainName; + AuthIdentity.DomainLength = (DomainName == NULL) ? 0 : (ULONG)wcslen(DomainName); + + } + + AuthIdentity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE; + + // + // Change authentication information for interface, providing the identity + // information and "echoing back" everything else. + // + hr = CoSetProxyBlanket(InterfaceObj, + AuthnSvc, + AuthzSvc, + pServerPrinName, + AuthnLevel, + ImpLevel, + &AuthIdentity, + Capabilities); + + if (FAILED(hr)) { + goto exit; + } + +exit: + return hr; +} + + +bool +ParseCommandLine( + _In_ ULONG Argc, + _In_reads_(Argc) PWSTR Argv[], + _Outptr_result_maybenull_ PWSTR* ComputerName, + _Outptr_result_maybenull_ PWSTR* UserId, + _Outptr_result_maybenull_ PWSTR* Password, + _Outptr_result_maybenull_ PWSTR* DomainName + ) + +/*++ + +Routine Description: + + This routine parses the command line input and extracts the computer name, + user id, password and domain name information. + +Arguments: + + Argc - Number of elements in the Agrv variable. + + Argv - Array of strings received as input to the wmain function. + + ComputerName - Pointer to the location that receives the address of the + computer name. + + UserId - Pointer to the location that receives the address of the UserId. + + Password - Pointer to the location that receives the adddress of the + Password string for the given user id. + + DomainName - Pointer to location that recieves the address of DomainName. + of the user. + +Return Value: + + TRUE, if the command line could be parsed successfully. + FALSE, otherwise. + +--*/ + +{ + ULONG i; + + *ComputerName = NULL; + *UserId = NULL; + *Password = NULL; + *DomainName = NULL; + + // + // Parameters are (when supplied): + // + // 0 -- Program name. Always supplied (by OS). + // 1 -- System name. Optional. + // 2 -- Userid. Optional but reconized only if system name supplied. + // 3 -- Password. Optional but recognized only if system name and UserId supplied. + // 4 -- DomainName. Optional but recognized only if system name, UserId and Password supplied. + // + + // + // Validate the number of input parameters. + // + if (!(Argc == 1 || Argc==2 || Argc==4 || Argc==5)) { + + printf("\nInvalid input parameters\n"); + return FALSE; + } + + // + // Parse the input parameters. + // + for (i = 1; i < Argc; i++) { + switch(i) { + case 1: + + // + // System name. + // + *ComputerName = Argv[i]; + break; + + case 2: + + // + // User ID. + // + *UserId = Argv[i]; + break; + + case 3: + + // + // Password. + // + *Password = Argv[i]; + break; + + case 4: + + // + // Domain Name. + // + *DomainName = Argv[i]; + break; + + default: + + printf("\nInvalid input parameters\n"); + return FALSE; + } + } + + // + // Verify that if User Id was specified, then the Password is sepcified too. + // + if (((*UserId != NULL) && (*Password == NULL)) || + ((*UserId == NULL) && (*Password != NULL))) { + + printf("\nIllegal UserId/Password combination\n\n"); + return FALSE; + } + + // + // Check whether the usage help was requested. + // + if (Argc == 2) { + PWSTR HelpCmds[] = {L"help", L"?"}; + ULONG ArgLength = (ULONG)wcslen(Argv[1]); + + for (i = 0; i < ARRAY_SIZE(HelpCmds); i ++) { + if (_wcsnicmp(Argv[1], HelpCmds[i], ArgLength) == 0) { + + *ComputerName = NULL; + return FALSE; + } + } + } + + return TRUE; +} + + +void +DisplayUsage() +{ + printf("\nParameter information --\n\n\n"); + printf(" \n\n"); + printf(" Run on local system with current identity.\n\n"); + printf(" \n\n"); + printf(" Run on specified system with current identity.\n\n"); + printf(" >\n\n"); + printf(" Run on specified system with specified UserId and Password and, possibly, DomainName.\n\n"); + printf(" help\n\n"); + printf(" ?\n\n"); +} + diff --git a/general/toaster/toastDrv/exe/wmi/wmitoast.h b/general/toaster/toastDrv/exe/wmi/wmitoast.h new file mode 100644 index 000000000..c43b4760e --- /dev/null +++ b/general/toaster/toastDrv/exe/wmi/wmitoast.h @@ -0,0 +1,32 @@ +#include +#include +#include +#include +#include + +#define ARRAY_SIZE(_X_) (sizeof((_X_))/sizeof((_X_)[0])) + + +HRESULT +SetInterfaceSecurity( + _In_ IUnknown* InterfaceObj, + _In_opt_ PWSTR UserId, + _In_opt_ PWSTR Password, + _In_opt_ PWSTR DomainName + ); + +HRESULT +ExecuteMethodsInClass( + _In_ IWbemServices* WbemServices, + _In_opt_ PWSTR UserId, + _In_opt_ PWSTR Password, + _In_opt_ PWSTR DomainName + ); + +HRESULT +GetAndSetValuesInClass( + _In_ IWbemServices* WbemServices, + _In_opt_ PWSTR UserId, + _In_opt_ PWSTR Password, + _In_opt_ PWSTR DomainName + ); diff --git a/general/toaster/toastDrv/kmdf/bus/dynamic/busenum.c b/general/toaster/toastDrv/kmdf/bus/dynamic/busenum.c new file mode 100644 index 000000000..8b79bff15 --- /dev/null +++ b/general/toaster/toastDrv/kmdf/bus/dynamic/busenum.c @@ -0,0 +1,774 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + BUSENUM.C + +Abstract: + + This module contains routies to handle the function driver + aspect of the bus driver. + +Environment: + + kernel mode only + +--*/ + +#include "busenum.h" + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (INIT, DriverEntry) +#pragma alloc_text (PAGE, Bus_EvtDeviceAdd) +#pragma alloc_text (PAGE, Bus_EvtIoDeviceControl) +#pragma alloc_text (PAGE, Bus_PlugInDevice) +#pragma alloc_text (PAGE, Bus_UnPlugDevice) +#pragma alloc_text (PAGE, Bus_EjectDevice) +#endif + +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; + WDFDRIVER driver; + + KdPrint(("WDF Toaster Bus Driver Sample Dynamic 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 specifing one in the Config structure. + // + + WDF_DRIVER_CONFIG_INIT( + &config, + Bus_EvtDeviceAdd + ); + + // + // Create a framework driver object to represent our driver. + // + status = WdfDriverCreate(DriverObject, + RegistryPath, + WDF_NO_OBJECT_ATTRIBUTES, + &config, + &driver); + + if (!NT_SUCCESS(status)) { + KdPrint( ("WdfDriverCreate failed with status 0x%x\n", status)); + } + + return status; + +} + + +NTSTATUS +Bus_EvtDeviceAdd( + IN WDFDRIVER Driver, + IN PWDFDEVICE_INIT DeviceInit + ) +/*++ +Routine Description: + + Bus_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 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_CHILD_LIST_CONFIG config; + WDF_OBJECT_ATTRIBUTES fdoAttributes; + NTSTATUS status; + WDFDEVICE device; + WDF_IO_QUEUE_CONFIG queueConfig; + PNP_BUS_INFORMATION busInfo; + //PFDO_DEVICE_DATA deviceData; + WDFQUEUE queue; + + UNREFERENCED_PARAMETER(Driver); + + PAGED_CODE (); + + KdPrint(("Bus_EvtDeviceAdd: 0x%p\n", Driver)); + + // + // 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); + WdfDeviceInitSetExclusive(DeviceInit, TRUE); + + // + // Since this is pure software bus enumerator, we don't have to register for + // any PNP/Power callbacks. Framework will take the default action for + // all the PNP and Power IRPs. + // + + + // + // 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), + Bus_EvtDeviceListCreatePdo // callback to create a child device. + ); + // + // This function pointer will be called when the framework needs to copy a + // identification description from one location to another. An implementation + // of this function is only necessary if the description contains description + // relative pointer values (like LIST_ENTRY for instance) . + // If set to NULL, the framework will use RtlCopyMemory to copy an identification . + // description. In this sample, it's not required to provide these callbacks. + // they are added just for illustration. + // + config.EvtChildListIdentificationDescriptionDuplicate = + Bus_EvtChildListIdentificationDescriptionDuplicate; + + // + // This function pointer will be called when the framework needs to compare + // two identificaiton descriptions. If left NULL a call to RtlCompareMemory + // will be used to compare two identificaiton descriptions. + // + config.EvtChildListIdentificationDescriptionCompare = + Bus_EvtChildListIdentificationDescriptionCompare; + // + // This function pointer will be called when the framework needs to free a + // identification description. An implementation of this function is only + // necessary if the description contains dynamically allocated memory + // (by the driver writer) that needs to be freed. The actual identification + // description pointer itself will be freed by the framework. + // + config.EvtChildListIdentificationDescriptionCleanup = + Bus_EvtChildListIdentificationDescriptionCleanup; + + // + // 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); + + // + // Initialize attributes structure to specify size and accessor function + // for storing device context. + // + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&fdoAttributes, FDO_DEVICE_DATA); + + // + // Create a framework device object. In response to this call, framework + // creates a WDM deviceobject and attach to the PDO. + // + status = WdfDeviceCreate(&DeviceInit, &fdoAttributes, &device); + + if (!NT_SUCCESS(status)) { + KdPrint(("Error creating device 0x%x\n", status)); + return status; + } + + // + // 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 + ); + + queueConfig.EvtIoDeviceControl = Bus_EvtIoDeviceControl; + + // + // 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 status 0x%x\n", status)); + return status; + } + + // + // Get the device context. + // + //deviceData = FdoGetData(device); + + // + // Create device interface for this device. The interface will be + // enabled by the framework when we return from StartDevice successfully. + // Clients of this driver will open this interface and send ioctls. + // + status = WdfDeviceCreateDeviceInterface( + device, + &GUID_DEVINTERFACE_BUSENUM_TOASTER, + NULL // No Reference String. If you provide one it will appended to the + ); // symbolic link. Some drivers register multiple interfaces for the same device + // and use the reference string to distinguish between them + if (!NT_SUCCESS(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_DEVCLASS_TOASTER; + busInfo.LegacyBusType = PNPBus; + busInfo.BusNumber = 0; + + WdfDeviceSetBusInformationForChildren(device, &busInfo); + + status = Bus_WmiRegistration(device); + if (!NT_SUCCESS(status)) { + return status; + } + + // + // Check the registry to see if we need to enumerate child devices during + // start. + // + status = Bus_DoStaticEnumeration(device); + + return status; +} + + +VOID +Bus_EvtIoDeviceControl( + IN WDFQUEUE Queue, + IN WDFREQUEST Request, + IN size_t OutputBufferLength, + IN size_t InputBufferLength, + IN ULONG IoControlCode + ) + +/*++ +Routine Description: + + Handle user mode PlugIn, UnPlug and device Eject requests. + +Arguments: + + Queue - Handle to the framework queue object that is associated + with the I/O request. + + Request - Handle to a framework request object. This one represents + the IRP_MJ_DEVICE_CONTROL IRP received by the framework. + + OutputBufferLength - Length, in bytes, of the request's output buffer, + if an output buffer is available. + + InputBufferLength - Length, in bytes, of the request's input buffer, + if an input buffer is available. + IoControlCode - Driver-defined or system-defined I/O control code (IOCTL) + that is associated with the request. + +Return Value: + + VOID + +--*/ +{ + NTSTATUS status = STATUS_INVALID_PARAMETER; + WDFDEVICE hDevice; + size_t length = 0; + PBUSENUM_PLUGIN_HARDWARE plugIn = NULL; + PBUSENUM_UNPLUG_HARDWARE unPlug = NULL; + PBUSENUM_EJECT_HARDWARE eject = NULL; + + + UNREFERENCED_PARAMETER(OutputBufferLength); + + PAGED_CODE (); + + hDevice = WdfIoQueueGetDevice(Queue); + + KdPrint(("Bus_EvtIoDeviceControl: 0x%p\n", hDevice)); + + switch (IoControlCode) { + case IOCTL_BUSENUM_PLUGIN_HARDWARE: + + status = WdfRequestRetrieveInputBuffer (Request, + sizeof (BUSENUM_PLUGIN_HARDWARE) + + (sizeof(UNICODE_NULL) * 2), // 2 for double NULL termination (MULTI_SZ) + &plugIn, &length); + if( !NT_SUCCESS(status) ) { + KdPrint(("WdfRequestRetrieveInputBuffer failed 0x%x\n", status)); + break; + } + + ASSERT(length == InputBufferLength); + + if (sizeof (BUSENUM_PLUGIN_HARDWARE) == plugIn->Size) + { + + length = (InputBufferLength - sizeof (BUSENUM_PLUGIN_HARDWARE))/sizeof(WCHAR); + // + // Make sure the IDs is two NULL terminated. + // + if ((UNICODE_NULL != plugIn->HardwareIDs[length - 1]) || + (UNICODE_NULL != plugIn->HardwareIDs[length - 2])) { + + status = STATUS_INVALID_PARAMETER; + break; + } + + status = Bus_PlugInDevice( hDevice, + plugIn->HardwareIDs, + length, + plugIn->SerialNo ); + } + + break; + + case IOCTL_BUSENUM_UNPLUG_HARDWARE: + + status = WdfRequestRetrieveInputBuffer( Request, + sizeof(BUSENUM_UNPLUG_HARDWARE), + &unPlug, + &length ); + if( !NT_SUCCESS(status) ) { + KdPrint(("WdfRequestRetrieveInputBuffer failed 0x%x\n", status)); + break; + } + + if (unPlug->Size == InputBufferLength) + { + + status= Bus_UnPlugDevice(hDevice, unPlug->SerialNo ); + + } + + break; + + case IOCTL_BUSENUM_EJECT_HARDWARE: + + status = WdfRequestRetrieveInputBuffer (Request, + sizeof (BUSENUM_EJECT_HARDWARE), + &eject, &length); + if( !NT_SUCCESS(status) ) { + KdPrint(("WdfRequestRetrieveInputBuffer failed 0x%x\n", status)); + break; + } + + if (eject->Size == InputBufferLength) + { + status= Bus_EjectDevice(hDevice, eject->SerialNo); + + } + + break; + + default: + break; // default status is STATUS_INVALID_PARAMETER + } + + WdfRequestCompleteWithInformation(Request, status, length); +} + +NTSTATUS +Bus_PlugInDevice( + _In_ WDFDEVICE Device, + _In_ PWCHAR HardwareIds, + _In_ size_t CchHardwareIds, + _In_ ULONG SerialNo + ) + +/*++ + +Routine Description: + + The user application has told us 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; +} + +NTSTATUS +Bus_UnPlugDevice( + 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. + +Arguments: + + +Returns: + + STATUS_SUCCESS upon successful removal from the list + STATUS_INVALID_PARAMETER if the removal was unsuccessful + +--*/ + +{ + NTSTATUS status; + WDFCHILDLIST list; + + PAGED_CODE (); + + list = WdfFdoGetDefaultChildList(Device); + + if (0 == SerialNo) { + // + // Unplug everybody. We do this by starting a scan and then not reporting + // any children upon its completion + // + status = STATUS_SUCCESS; + + WdfChildListBeginScan(list); + // + // A call to WdfChildListBeginScan indicates to the framework that the + // driver is about to scan for dynamic children. After this call has + // returned, all previously reported children associated with this will be + // marked as potentially missing. A call to either + // WdfChildListUpdateChildDescriptionAsPresent or + // WdfChildListMarkAllChildDescriptionsPresent will mark all previuosly + // reported missing children as present. If any children currently + // present are not reported present by calling + // WdfChildListUpdateChildDescriptionAsPresent at the time of + // WdfChildListEndScan, they will be reported as missing to the PnP subsystem + // After WdfChildListEndScan call has returned, the framework will + // invalidate the device relations for the FDO associated with the list + // and report the changes + // + WdfChildListEndScan(list); + + } + else { + PDO_IDENTIFICATION_DESCRIPTION description; + + WDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER_INIT( + &description.Header, + sizeof(description) + ); + + description.SerialNo = SerialNo; + // + // WdfFdoUpdateChildDescriptionAsMissing indicates to the framework that a + // child device that was previuosly detected is no longe present on the bus. + // This API can be called by itself or after a call to WdfChildListBeginScan. + // After this call has returned, the framework will invalidate the device + // relations for the FDO associated with the list and report the changes. + // + status = WdfChildListUpdateChildDescriptionAsMissing(list, + &description.Header); + if (status == STATUS_NO_SUCH_DEVICE) { + // + // serial number didn't exist. Remap it to a status that user + // application can understand when it gets translated to win32 + // error code. + // + status = STATUS_INVALID_PARAMETER; + } + } + + return status; +} + +NTSTATUS +Bus_EjectDevice( + WDFDEVICE Device, + ULONG SerialNo + ) + +/*++ + +Routine Description: + + The user application has told us to eject the device from the bus. + In a real situation the driver gets notified by an interrupt when the + user presses the Eject button on the device. + +Arguments: + + +Returns: + + STATUS_SUCCESS upon successful removal from the list + STATUS_INVALID_PARAMETER if the ejection was unsuccessful + +--*/ + +{ + WDFDEVICE hChild; + NTSTATUS status = STATUS_INVALID_PARAMETER; + WDFCHILDLIST list; + + PAGED_CODE (); + + list = WdfFdoGetDefaultChildList(Device); + + // + // A zero serial number means eject all children + // + if (0 == SerialNo) { + WDF_CHILD_LIST_ITERATOR iterator; + + WDF_CHILD_LIST_ITERATOR_INIT( &iterator, + WdfRetrievePresentChildren ); + + WdfChildListBeginIteration(list, &iterator); + + for ( ; ; ) { + WDF_CHILD_RETRIEVE_INFO childInfo; + PDO_IDENTIFICATION_DESCRIPTION description; + BOOLEAN ret; + + // + // Init the structures. + // + WDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER_INIT(&description.Header, sizeof(description)); + WDF_CHILD_RETRIEVE_INFO_INIT(&childInfo, &description.Header); + + WDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER_INIT( + &description.Header, + sizeof(description) + ); + // + // Get the device identification description + // + status = WdfChildListRetrieveNextDevice(list, + &iterator, + &hChild, + &childInfo); + + if (!NT_SUCCESS(status) || status == STATUS_NO_MORE_ENTRIES) { + break; + } + + ASSERT(childInfo.Status == WdfChildListRetrieveDeviceSuccess); + + // + // Use that description to request an eject. + // + ret = WdfChildListRequestChildEject(list, &description.Header); + if(!ret) { + WDFVERIFY(ret); + } + + } + + WdfChildListEndIteration(list, &iterator); + + if (status == STATUS_NO_MORE_ENTRIES) { + status = STATUS_SUCCESS; + } + + } + else { + + PDO_IDENTIFICATION_DESCRIPTION description; + + WDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER_INIT( + &description.Header, + sizeof(description) + ); + + description.SerialNo = SerialNo; + + if (WdfChildListRequestChildEject(list, &description.Header)) { + status = STATUS_SUCCESS; + } + } + + return status; +} + +NTSTATUS +Bus_DoStaticEnumeration( + IN WDFDEVICE Device + ) +/*++ +Routine Description: + + The routine enables you to statically enumerate child devices + during start instead of running the enum.exe/notify.exe to + enumerate toaster devices. + + In order to statically enumerate, user must specify the number + of toasters in the Toaster Bus driver's device registry. The + default value is zero. + + HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\Root\SYSTEM\0002\ + Device Parameters + NumberOfToasters:REG_DWORD:2 + + You can also configure this value in the Toaster Bus Inf file. + +--*/ + +{ + WDFKEY hKey = NULL; + NTSTATUS status; + ULONG value, i; + DECLARE_CONST_UNICODE_STRING(valueName, L"NumberOfToasters"); + + // + // If the registry value doesn't exist, we will use the + // hardcoded default number. + // + value = DEF_STATICALLY_ENUMERATED_TOASTERS; + + // + // Open the device registry and read the "NumberOfToasters" value. + // + status = WdfDeviceOpenRegistryKey(Device, + PLUGPLAY_REGKEY_DEVICE, + STANDARD_RIGHTS_ALL, + NULL, // PWDF_OBJECT_ATTRIBUTES + &hKey); + + if (NT_SUCCESS (status)) { + + status = WdfRegistryQueryULong(hKey, + &valueName, + &value); + + WdfRegistryClose(hKey); + hKey = NULL; // Set hKey to NULL to catch any accidental subsequent use. + + if (NT_SUCCESS (status)) { + // + // Make sure it doesn't exceed the max. This is required to prevent + // denial of service by enumerating large number of child devices. + // + value = min(value, MAX_STATICALLY_ENUMERATED_TOASTERS); + }else { + return STATUS_SUCCESS; // This is an optional property. + } + } + + KdPrint(("Enumerating %d toaster devices\n", value)); + + for(i=1; i<= value; i++) { + // + // Value of i is used as serial number. + // + status = Bus_PlugInDevice(Device, + BUS_HARDWARE_IDS, + BUS_HARDWARE_IDS_LENGTH / sizeof(WCHAR), + i ); + } + + return status; +} + + diff --git a/general/toaster/toastDrv/kmdf/bus/dynamic/busenum.h b/general/toaster/toastDrv/kmdf/bus/dynamic/busenum.h new file mode 100644 index 000000000..36aa83450 --- /dev/null +++ b/general/toaster/toastDrv/kmdf/bus/dynamic/busenum.h @@ -0,0 +1,206 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + busenum.h + +Abstract: + + This module contains the common private declarations + for the Toaster Bus enumerator. + +Environment: + + kernel mode only + +--*/ + +#include +#include +#define NTSTRSAFE_LIB +#include +#include +#include +#include "driver.h" +#include "public.h" +#include + + +#ifndef BUSENUM_H +#define BUSENUM_H + +extern ULONG BusEnumDebugLevel; + +#define BUS_TAG 'EsuB' +#define BUSRESOURCENAME L"MofResourceName" + +#define DEF_STATICALLY_ENUMERATED_TOASTERS 0 +#define MAX_STATICALLY_ENUMERATED_TOASTERS 10 +#define MAX_INSTANCE_ID_LEN 80 + +#ifndef min +#define min(_a, _b) (((_a) < (_b)) ? (_a) : (_b)) +#endif + +#ifndef max +#define max(_a, _b) (((_a) > (_b)) ? (_a) : (_b)) +#endif + +// +// Structure for reporting data to WMI +// + +typedef ToasterBusInformation TOASTER_BUS_WMI_STD_DATA, * PTOASTER_BUS_WMI_STD_DATA; + +// +// 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; + +// +// This is PDO device-extension. +// +typedef struct _PDO_DEVICE_DATA +{ + // Unique serail number of the device on the bus + + ULONG SerialNo; + +} PDO_DEVICE_DATA, *PPDO_DEVICE_DATA; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(PDO_DEVICE_DATA, PdoGetData) + +// +// The device extension of the bus itself. From whence the PDO's are born. +// + +typedef struct _FDO_DEVICE_DATA +{ + TOASTER_BUS_WMI_STD_DATA StdToasterBusData; + +} FDO_DEVICE_DATA, *PFDO_DEVICE_DATA; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(FDO_DEVICE_DATA, FdoGetData) + +// +// Prototypes of functions +// + +DRIVER_INITIALIZE DriverEntry; + +EVT_WDF_DRIVER_DEVICE_ADD Bus_EvtDeviceAdd; + +EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL Bus_EvtIoDeviceControl; + +EVT_WDF_CHILD_LIST_CREATE_DEVICE Bus_EvtDeviceListCreatePdo; +EVT_WDF_CHILD_LIST_IDENTIFICATION_DESCRIPTION_COMPARE Bus_EvtChildListIdentificationDescriptionCompare; +EVT_WDF_CHILD_LIST_IDENTIFICATION_DESCRIPTION_CLEANUP Bus_EvtChildListIdentificationDescriptionCleanup; +EVT_WDF_CHILD_LIST_IDENTIFICATION_DESCRIPTION_DUPLICATE Bus_EvtChildListIdentificationDescriptionDuplicate; + +NTSTATUS +Bus_PlugInDevice( + _In_ WDFDEVICE Device, + _In_ PWCHAR HardwareIds, + _In_ size_t CchHardwareIds, + _In_ ULONG SerialNo + ); + +NTSTATUS +Bus_UnPlugDevice( + WDFDEVICE Device, + ULONG SerialNo + ); + + +NTSTATUS +Bus_EjectDevice( + WDFDEVICE Device, + ULONG SerialNo + ); + +NTSTATUS +Bus_CreatePdo( + _In_ WDFDEVICE Device, + _In_ PWDFDEVICE_INIT ChildInit, + _In_reads_(MAX_INSTANCE_ID_LEN) PCWSTR HardwareIds, + _In_ ULONG SerialNo + ); + +NTSTATUS +Bus_DoStaticEnumeration( + IN WDFDEVICE Device + ); + + +// +// Interface functions +// + +BOOLEAN +Bus_GetCrispinessLevel( + IN WDFDEVICE ChildDevice, + OUT PUCHAR Level + ); + +BOOLEAN +Bus_SetCrispinessLevel( + IN WDFDEVICE ChildDevice, + OUT UCHAR Level + ); + +BOOLEAN +Bus_IsSafetyLockEnabled( + IN PVOID Context + ); + +// +// Defined in wmi.c +// + +NTSTATUS +Bus_WmiRegistration( + WDFDEVICE Device + ); + +EVT_WDF_WMI_INSTANCE_SET_ITEM Bus_EvtStdDataSetItem; +EVT_WDF_WMI_INSTANCE_SET_INSTANCE Bus_EvtStdDataSetInstance; +EVT_WDF_WMI_INSTANCE_QUERY_INSTANCE Bus_EvtStdDataQueryInstance; + +#endif + diff --git a/general/toaster/toastDrv/kmdf/bus/dynamic/busenum.mof b/general/toaster/toastDrv/kmdf/bus/dynamic/busenum.mof new file mode 100644 index 000000000..e29eeec3f --- /dev/null +++ b/general/toaster/toastDrv/kmdf/bus/dynamic/busenum.mof @@ -0,0 +1,26 @@ +#PRAGMA AUTORECOVER + +[Dynamic, Provider("WMIProv"), + WMI, + Description("Toaster Bus driver information"), + guid("{0006A660-8F12-11d2-B854-00C04FAD5171}"), + locale("MS\\0x409")] +class ToasterBusInformation +{ + [key, read] + string InstanceName; + [read] boolean Active; + + [WmiDataId(1), + read, + Description("Number of errors that occurred on this device")] + uint32 ErrorCount; + + [WmiDataId(2), + read, + write, + Description("The DebugPrintLevel property indicates the debug output level of toaster bus device.")] + uint32 DebugPrintLevel; + +}; + diff --git a/general/toaster/toastDrv/kmdf/bus/dynamic/busenum.rc b/general/toaster/toastDrv/kmdf/bus/dynamic/busenum.rc new file mode 100644 index 000000000..b42c38664 --- /dev/null +++ b/general/toaster/toastDrv/kmdf/bus/dynamic/busenum.rc @@ -0,0 +1,13 @@ +#include + +#include + +#define VER_FILETYPE VFT_DLL +#define VER_FILESUBTYPE VFT2_UNKNOWN +#define VER_FILEDESCRIPTION_STR "Framework Version of Toaster Dynamic Bus Enumerator" +#define VER_INTERNALNAME_STR "dynambus.sys" +#define VER_ORIGINALFILENAME_STR "dynambus.sys" + +#include "common.ver" + +MofResourceName MOFDATA busenum.bmf diff --git a/general/toaster/toastDrv/kmdf/bus/dynamic/buspdo.c b/general/toaster/toastDrv/kmdf/bus/dynamic/buspdo.c new file mode 100644 index 000000000..5c908770c --- /dev/null +++ b/general/toaster/toastDrv/kmdf/bus/dynamic/buspdo.c @@ -0,0 +1,522 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + BusPdo.c + +Abstract: + + This module handles plug & play calls for the child device (PDO). + +Environment: + + kernel mode only + +--*/ + +#include "busenum.h" + +ULONG BusEnumDebugLevel; + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, Bus_CreatePdo) +#pragma alloc_text(PAGE, Bus_EvtDeviceListCreatePdo) +#endif + +NTSTATUS +Bus_EvtChildListIdentificationDescriptionDuplicate( + WDFCHILDLIST DeviceList, + PWDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER SourceIdentificationDescription, + PWDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER DestinationIdentificationDescription + ) +/*++ + +Routine Description: + + It is called when the framework needs to make a copy of a description. + This happens when a request is made to create a new child device by + calling WdfChildListAddOrUpdateChildDescriptionAsPresent. + If this function is left unspecified, RtlCopyMemory will be used to copy the + source description to destination. Memory for the description is managed by the + framework. + + NOTE: Callback is invoked with an internal lock held. So do not call out + to any WDF function which will require this lock + (basically any other WDFCHILDLIST api) + +Arguments: + + DeviceList - Handle to the default WDFCHILDLIST created by the framework. + SourceIdentificationDescription - Description of the child being created -memory in + the calling thread stack. + DestinationIdentificationDescription - Created by the framework in nonpaged pool. + +Return Value: + + NT Status code. + +--*/ +{ + PPDO_IDENTIFICATION_DESCRIPTION src, dst; + size_t safeMultResult; + NTSTATUS status; + + UNREFERENCED_PARAMETER(DeviceList); + + src = CONTAINING_RECORD(SourceIdentificationDescription, + PDO_IDENTIFICATION_DESCRIPTION, + Header); + dst = CONTAINING_RECORD(DestinationIdentificationDescription, + PDO_IDENTIFICATION_DESCRIPTION, + Header); + + dst->SerialNo = src->SerialNo; + dst->CchHardwareIds = src->CchHardwareIds; + status = RtlSizeTMult(dst->CchHardwareIds, + sizeof(WCHAR), + &safeMultResult + ); + if(!NT_SUCCESS(status)){ + return status; + } + + dst->HardwareIds = (PWCHAR) ExAllocatePoolWithTag( + NonPagedPool, + safeMultResult, + BUS_TAG); + + if (dst->HardwareIds == NULL) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlCopyMemory(dst->HardwareIds, + src->HardwareIds, + dst->CchHardwareIds * sizeof(WCHAR)); + + return STATUS_SUCCESS; +} + +BOOLEAN +Bus_EvtChildListIdentificationDescriptionCompare( + WDFCHILDLIST DeviceList, + PWDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER FirstIdentificationDescription, + PWDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER SecondIdentificationDescription + ) +/*++ + +Routine Description: + + It is called when the framework needs to compare one description with another. + Typically this happens whenever a request is made to add a new child device. + If this function is left unspecified, RtlCompareMemory will be used to compare the + descriptions. + + NOTE: Callback is invoked with an internal lock held. So do not call out + to any WDF function which will require this lock + (basically any other WDFCHILDLIST api) + +Arguments: + + DeviceList - Handle to the default WDFCHILDLIST created by the framework. + +Return Value: + + TRUE or FALSE. + +--*/ +{ + PPDO_IDENTIFICATION_DESCRIPTION lhs, rhs; + + UNREFERENCED_PARAMETER(DeviceList); + + lhs = CONTAINING_RECORD(FirstIdentificationDescription, + PDO_IDENTIFICATION_DESCRIPTION, + Header); + rhs = CONTAINING_RECORD(SecondIdentificationDescription, + PDO_IDENTIFICATION_DESCRIPTION, + Header); + + return (lhs->SerialNo == rhs->SerialNo) ? TRUE : FALSE; +} + +#pragma prefast(push) +#pragma prefast(disable:6101, "No need to assign IdentificationDescription") + +VOID +Bus_EvtChildListIdentificationDescriptionCleanup( + _In_ WDFCHILDLIST DeviceList, + _Out_ PWDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER IdentificationDescription + ) +/*++ + +Routine Description: + + It is called to free up any memory resources allocated as part of the description. + This happens when a child device is unplugged or ejected from the bus. + Memory for the description itself will be freed by the framework. + +Arguments: + + DeviceList - Handle to the default WDFCHILDLIST created by the framework. + + IdentificationDescription - Description of the child being deleted + +Return Value: + + +--*/ +{ + PPDO_IDENTIFICATION_DESCRIPTION pDesc; + + + UNREFERENCED_PARAMETER(DeviceList); + + pDesc = CONTAINING_RECORD(IdentificationDescription, + PDO_IDENTIFICATION_DESCRIPTION, + Header); + + if (pDesc->HardwareIds != NULL) { + ExFreePool(pDesc->HardwareIds); + pDesc->HardwareIds = NULL; + } +} + +#pragma prefast(pop) // disable:6101 + +NTSTATUS +Bus_EvtDeviceListCreatePdo( + 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 Bus_CreatePdo(WdfChildListGetDevice(DeviceList), + ChildInit, + pDesc->HardwareIds, + pDesc->SerialNo); +} + + +NTSTATUS +Bus_CreatePdo( + _In_ WDFDEVICE Device, + _In_ PWDFDEVICE_INIT DeviceInit, + _In_reads_(MAX_INSTANCE_ID_LEN) PCWSTR HardwareIds, + _In_ ULONG SerialNo + ) +/*++ + +Routine Description: + + This routine creates and initialize a PDO. + +Arguments: + +Return Value: + + NT Status code. + +--*/ +{ + NTSTATUS status; + PPDO_DEVICE_DATA pdoData = NULL; + WDFDEVICE hChild = NULL; + WDF_QUERY_INTERFACE_CONFIG qiConfig; + WDF_OBJECT_ATTRIBUTES pdoAttributes; + WDF_DEVICE_PNP_CAPABILITIES pnpCaps; + WDF_DEVICE_POWER_CAPABILITIES powerCaps; + TOASTER_INTERFACE_STANDARD ToasterInterface; + DECLARE_CONST_UNICODE_STRING(compatId, BUSENUM_COMPATIBLE_IDS); + DECLARE_CONST_UNICODE_STRING(deviceLocation, L"Toaster Bus 0"); + DECLARE_UNICODE_STRING_SIZE(buffer, MAX_INSTANCE_ID_LEN); + DECLARE_UNICODE_STRING_SIZE(deviceId, MAX_INSTANCE_ID_LEN); + + PAGED_CODE(); + + UNREFERENCED_PARAMETER(Device); + + KdPrint(("Entered Bus_CreatePdo\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"Microsoft_Eliyas_Toaster_%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_DEVICE_DATA); + + status = WdfDeviceCreate(&DeviceInit, &pdoAttributes, &hChild); + if (!NT_SUCCESS(status)) { + return status; + } + + // + // Get the device context. + // + pdoData = PdoGetData(hChild); + + pdoData->SerialNo = SerialNo; + + // + // Set some properties for the child device. + // + WDF_DEVICE_PNP_CAPABILITIES_INIT(&pnpCaps); + pnpCaps.Removable = WdfTrue; + pnpCaps.EjectSupported = WdfTrue; + pnpCaps.SurpriseRemovalOK = WdfTrue; + + pnpCaps.Address = SerialNo; + pnpCaps.UINumber = SerialNo; + + WdfDeviceSetPnpCapabilities(hChild, &pnpCaps); + + WDF_DEVICE_POWER_CAPABILITIES_INIT(&powerCaps); + + powerCaps.DeviceD1 = WdfTrue; + powerCaps.WakeFromD1 = WdfTrue; + powerCaps.DeviceWake = PowerDeviceD1; + + powerCaps.DeviceState[PowerSystemWorking] = PowerDeviceD1; + powerCaps.DeviceState[PowerSystemSleeping1] = PowerDeviceD1; + powerCaps.DeviceState[PowerSystemSleeping2] = PowerDeviceD2; + powerCaps.DeviceState[PowerSystemSleeping3] = PowerDeviceD2; + powerCaps.DeviceState[PowerSystemHibernate] = PowerDeviceD3; + powerCaps.DeviceState[PowerSystemShutdown] = PowerDeviceD3; + + WdfDeviceSetPowerCapabilities(hChild, &powerCaps); + + // + // Create a custom interface so that other drivers can + // query (IRP_MN_QUERY_INTERFACE) and use our callbacks directly. + // + RtlZeroMemory(&ToasterInterface, sizeof(ToasterInterface)); + + ToasterInterface.InterfaceHeader.Size = sizeof(ToasterInterface); + ToasterInterface.InterfaceHeader.Version = 1; + ToasterInterface.InterfaceHeader.Context = (PVOID) hChild; + + // + // Let the framework handle reference counting. + // + ToasterInterface.InterfaceHeader.InterfaceReference = + WdfDeviceInterfaceReferenceNoOp; + ToasterInterface.InterfaceHeader.InterfaceDereference = + WdfDeviceInterfaceDereferenceNoOp; + + ToasterInterface.GetCrispinessLevel = Bus_GetCrispinessLevel; + ToasterInterface.SetCrispinessLevel = Bus_SetCrispinessLevel; + ToasterInterface.IsSafetyLockEnabled = Bus_IsSafetyLockEnabled; + + WDF_QUERY_INTERFACE_CONFIG_INIT(&qiConfig, + (PINTERFACE) &ToasterInterface, + &GUID_TOASTER_INTERFACE_STANDARD, + NULL); + // + // If you have multiple interfaces, you can call WdfDeviceAddQueryInterface + // multiple times to add additional interfaces. + // + status = WdfDeviceAddQueryInterface(hChild, &qiConfig); + + if (!NT_SUCCESS(status)) { + return status; + } + + return status; +} + + +BOOLEAN +Bus_GetCrispinessLevel( + IN WDFDEVICE ChildDevice, + OUT PUCHAR Level + ) +/*++ + +Routine Description: + + This routine gets the current crispiness level of the toaster. + +Arguments: + + Context pointer to PDO device extension + Level crispiness level of the device + +Return Value: + + TRUE or FALSE + +--*/ +{ + UNREFERENCED_PARAMETER(ChildDevice); + + // + // Validate the context to see if it's really a pointer + // to PDO's device extension. You can store some kind + // of signature in the PDO for this purpose + // + + KdPrint(("GetCrispnessLevel\n")); + + *Level = 10; + return TRUE; +} + +BOOLEAN +Bus_SetCrispinessLevel( + IN WDFDEVICE ChildDevice, + IN UCHAR Level + ) +/*++ + +Routine Description: + + This routine sets the current crispiness level of the toaster. + +Arguments: + + Context pointer to PDO device extension + Level crispiness level of the device + +Return Value: + + TRUE or FALSE + +--*/ +{ + UNREFERENCED_PARAMETER(ChildDevice); + UNREFERENCED_PARAMETER(Level); + + KdPrint(("SetCrispnessLevel\n")); + + return TRUE; +} + +BOOLEAN +Bus_IsSafetyLockEnabled( + IN WDFDEVICE ChildDevice + ) +/*++ + +Routine Description: + + Routine to check whether safety lock is enabled + +Arguments: + + Context pointer to PDO device extension + +Return Value: + + TRUE or FALSE + +--*/ +{ + UNREFERENCED_PARAMETER(ChildDevice); + + KdPrint(("IsSafetyLockEnabled\n")); + + return TRUE; +} + diff --git a/general/toaster/toastDrv/kmdf/bus/dynamic/dynambus.inx b/general/toaster/toastDrv/kmdf/bus/dynamic/dynambus.inx new file mode 100644 index 000000000..e45f43296 --- /dev/null +++ b/general/toaster/toastDrv/kmdf/bus/dynamic/dynambus.inx @@ -0,0 +1,102 @@ +;/*++ +; +;Copyright (c) 1990-1999 Microsoft Corporation All rights Reserved +; +;Module Name: +; +; DYNAMBUS.INF +; +;Abstract: +; INF file for installing toaster bus enumerator driver +; +;Installation Notes: +; Using Devcon: Type "devcon install dynambus.inf root\dynambus" to install +; +;--*/ +[Version] +Signature="$WINDOWS NT$" +Class=System +ClassGuid={4D36E97D-E325-11CE-BFC1-08002BE10318} +Provider=%ProviderName% +DriverVer=06/17/1999, 5.00.2064 +CatalogFile=KmdfSamples.cat + + +[DestinationDirs] +DefaultDestDir = 12 + +[SourceDisksNames] +1 = %DiskId1%,,,"" + +[SourceDisksFiles] +dynambus.sys = 1,, + +;***************************************** +; ToasterDynamicBus Install Section +;***************************************** + +[Manufacturer] +%StdMfg%=Standard,NT$ARCH$ + +[Standard.NT$ARCH$] +%ToasterDynamicBus.DeviceDesc%=ToasterDynamicBus_Device, root\dynambus + +[ToasterDynamicBus_Device.NT] +CopyFiles=Drivers_Dir + +[ToasterDynamicBus_Device.NT.HW] +AddReg=ToasterDynamicBus_Device.NT.AddReg + +[ToasterDynamicBus_Device.NT.AddReg] +HKR,,DeviceCharacteristics,0x10001,0x0100 ; Use same security checks on relative opens +HKR,,Security,,"D:P(A;;GA;;;BA)(A;;GA;;;SY)" ; Allow generic-all access to Built-in administrators and Local system + +[Drivers_Dir] +dynambus.sys + + +;-------------- Service installation +[ToasterDynamicBus_Device.NT.Services] +AddService = dynambus,%SPSVCINST_ASSOCSERVICE%, dynambus_Service_Inst + +; -------------- dynambus driver install sections +[dynambus_Service_Inst] +DisplayName = %dynambus.SVCDESC% +ServiceType = 1 ; SERVICE_KERNEL_DRIVER +StartType = 3 ; SERVICE_DEMAND_START +ErrorControl = 1 ; SERVICE_ERROR_NORMAL +ServiceBinary = %12%\dynambus.sys + + +; +;--- ToasterDynamicBus_Device Coinstaller installation ------ +; +[DestinationDirs] +ToasterDynamicBus_Device_CoInstaller_CopyFiles = 11 + +[ToasterDynamicBus_Device.NT.CoInstallers] +AddReg=ToasterDynamicBus_Device_CoInstaller_AddReg +CopyFiles=ToasterDynamicBus_Device_CoInstaller_CopyFiles + +[ToasterDynamicBus_Device_CoInstaller_AddReg] +HKR,,CoInstallers32,0x00010000, "WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll,WdfCoInstaller" + +[ToasterDynamicBus_Device_CoInstaller_CopyFiles] +WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll + +[SourceDisksFiles] +WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll=1 ; make sure the number matches with SourceDisksNames + +[ToasterDynamicBus_Device.NT.Wdf] +KmdfService = dynambus, dynambus_wdfsect +[dynambus_wdfsect] +KmdfLibraryVersion = $KMDFVERSION$ + + +[Strings] +SPSVCINST_ASSOCSERVICE = 0x00000002 +ProviderName = "TODO-Set-Provider" +StdMfg = "(Standard system devices)" +DiskId1 = "Toaster Dynamic Bus Installation Disk #1" +ToasterDynamicBus.DeviceDesc = "Toaster Dynamic Bus Enumerator" +dynambus.SVCDESC = "Toaster Dynamic Bus Enumerator" diff --git a/general/toaster/toastDrv/kmdf/bus/dynamic/dynambus.vcxproj b/general/toaster/toastDrv/kmdf/bus/dynamic/dynambus.vcxproj new file mode 100644 index 000000000..042c93efc --- /dev/null +++ b/general/toaster/toastDrv/kmdf/bus/dynamic/dynambus.vcxproj @@ -0,0 +1,192 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {EA66789C-5ABF-44C0-9BFD-C7618507E9C8} + $(MSBuildProjectName) + 1 + false + true + Debug + Win32 + {168C515B-E0DC-4CCE-AB5D-4B3FE501CC58} + + + + 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)\dynambus.inf + + + ".\$(IntDir)\busenum.bmf" + + + + true + .\$(IntDir)\busenumMof.h + .\$(IntDir)\htm + true + + + + dynambus + + + dynambus + + + dynambus + + + dynambus + + + + %(AdditionalIncludeDirectories);..\..\inc + + + %(AdditionalIncludeDirectories);..\..\inc + + + + + %(AdditionalIncludeDirectories);..\..\inc + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ntstrsafe.lib + + + + + %(AdditionalIncludeDirectories);..\..\inc + + + %(AdditionalIncludeDirectories);..\..\inc + + + + + %(AdditionalIncludeDirectories);..\..\inc + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ntstrsafe.lib + + + + + %(AdditionalIncludeDirectories);..\..\inc + + + %(AdditionalIncludeDirectories);..\..\inc + + + + + %(AdditionalIncludeDirectories);..\..\inc + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ntstrsafe.lib + + + + + %(AdditionalIncludeDirectories);..\..\inc + + + %(AdditionalIncludeDirectories);..\..\inc + + + + + %(AdditionalIncludeDirectories);..\..\inc + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ntstrsafe.lib + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/general/toaster/toastDrv/kmdf/bus/dynamic/dynambus.vcxproj.Filters b/general/toaster/toastDrv/kmdf/bus/dynamic/dynambus.vcxproj.Filters new file mode 100644 index 000000000..46ffc89f1 --- /dev/null +++ b/general/toaster/toastDrv/kmdf/bus/dynamic/dynambus.vcxproj.Filters @@ -0,0 +1,45 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {F47FA196-0B9F-4630-A6DC-87B41000BC35} + + + h;hpp;hxx;hm;inl;inc;xsd + {62AF8524-CB5B-468E-9398-455D5E9DC688} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {812EE7F2-C44C-4C50-AC55-CE56B09EF7AC} + + + inf;inv;inx;mof;mc; + {00095F3E-A751-4C8E-9052-D3E64DF88F1E} + + + + + Driver Files + + + Driver Files + + + + + Source Files + + + Source Files + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/general/toaster/toastDrv/kmdf/bus/dynamic/wmi.c b/general/toaster/toastDrv/kmdf/bus/dynamic/wmi.c new file mode 100644 index 000000000..935937899 --- /dev/null +++ b/general/toaster/toastDrv/kmdf/bus/dynamic/wmi.c @@ -0,0 +1,254 @@ +/*++ + +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 handles all the WMI Irps. + +Environment: + + Kernel mode + +--*/ + +#include "busenum.h" + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE,Bus_WmiRegistration) +#pragma alloc_text(PAGE,Bus_EvtStdDataSetItem) +#pragma alloc_text(PAGE,Bus_EvtStdDataSetInstance) +#pragma alloc_text(PAGE,Bus_EvtStdDataQueryInstance) +#endif + +NTSTATUS +Bus_WmiRegistration( + 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; + PFDO_DEVICE_DATA deviceData; + NTSTATUS status; + DECLARE_CONST_UNICODE_STRING(busRsrcName, BUSRESOURCENAME); + + PAGED_CODE(); + + deviceData = FdoGetData(Device); + + // + // Register WMI classes. + // First specify the resource name which contain the binary mof resource. + // + status = WdfDeviceAssignMofResourceName(Device, &busRsrcName); + if (!NT_SUCCESS(status)) { + return status; + } + + WDF_WMI_PROVIDER_CONFIG_INIT(&providerConfig, &ToasterBusInformation_GUID); + providerConfig.MinInstanceBufferSize = sizeof(TOASTER_BUS_WMI_STD_DATA); + + // + // You would want to create a WDFWMIPROVIDER handle separately if you are + // going to dynamically create instances on the provider. Since we are + // statically creating one instance, there is no need to create the provider + // handle. + // + + WDF_WMI_INSTANCE_CONFIG_INIT_PROVIDER_CONFIG(&instanceConfig, &providerConfig); + + // + // By setting Regsiter to TRUE, we tell the framework to create a provider + // as part of the Instance creation call. This eliminates the need to + // call WdfWmiProviderRegister. + // + instanceConfig.Register = TRUE; + instanceConfig.EvtWmiInstanceQueryInstance = Bus_EvtStdDataQueryInstance; + instanceConfig.EvtWmiInstanceSetInstance = Bus_EvtStdDataSetInstance; + instanceConfig.EvtWmiInstanceSetItem = Bus_EvtStdDataSetItem; + + status = WdfWmiInstanceCreate( + Device, + &instanceConfig, + WDF_NO_OBJECT_ATTRIBUTES, + WDF_NO_HANDLE + ); + + if (NT_SUCCESS(status)) { + deviceData->StdToasterBusData.ErrorCount = 0; + } + + return status; +} + +// +// WMI System Call back functions +// +NTSTATUS +Bus_EvtStdDataSetItem( + IN WDFWMIINSTANCE WmiInstance, + IN ULONG DataItemId, + IN ULONG InBufferSize, + IN PVOID InBuffer + ) +/*++ + +Routine Description: + + This routine is a callback into the driver to set for the contents of + an instance. + +Arguments: + + WmiInstance is the instance being set + + DataItemId has the id of the data item being set + + InBufferSize has the size of the data item passed + + InBuffer has the new values for the data item + +Return Value: + + status + +--*/ +{ + PFDO_DEVICE_DATA fdoData; + + PAGED_CODE(); + + fdoData = FdoGetData(WdfWmiInstanceGetDevice(WmiInstance)); + + // + // TODO: Use generated header's #defines for constants and sizes + // (for the remainder of the file) + // + + if (DataItemId == 2) { + if (InBufferSize < sizeof(ULONG)) { + return STATUS_BUFFER_TOO_SMALL; + } + + BusEnumDebugLevel = fdoData->StdToasterBusData.DebugPrintLevel = + *((PULONG)InBuffer); + + return STATUS_SUCCESS; + } + + // + // All other fields are read only + // + return STATUS_WMI_READ_ONLY; +} + +NTSTATUS +Bus_EvtStdDataSetInstance( + IN WDFWMIINSTANCE WmiInstance, + IN ULONG InBufferSize, + IN PVOID InBuffer + ) +/*++ + +Routine Description: + + This routine is a callback into the driver to set for the contents of + an instance. + +Arguments: + + WmiInstance is the instance being set + + BufferSize has the size of the data block passed + + Buffer has the new values for the data block + +Return Value: + + status + +--*/ +{ + PFDO_DEVICE_DATA fdoData; + + UNREFERENCED_PARAMETER(InBufferSize); + + PAGED_CODE(); + + fdoData = FdoGetData(WdfWmiInstanceGetDevice(WmiInstance)); + + // + // We will update only writable elements. + // + BusEnumDebugLevel = fdoData->StdToasterBusData.DebugPrintLevel = + ((PTOASTER_BUS_WMI_STD_DATA)InBuffer)->DebugPrintLevel; + + return STATUS_SUCCESS; +} + +NTSTATUS +Bus_EvtStdDataQueryInstance( + _In_ WDFWMIINSTANCE WmiInstance, + _In_ ULONG OutBufferSize, + _Out_writes_bytes_to_(OutBufferSize, *BufferUsed) PVOID OutBuffer, + _Out_ PULONG BufferUsed + ) +/*++ + +Routine Description: + + This routine is a callback into the driver to set for the contents of + a wmi instance + +Arguments: + + WmiInstance is the instance being set + + OutBufferSize on has the maximum size available to write the data + block. + + OutBuffer on return is filled with the returned data block + + BufferUsed pointer containing how many bytes are required (upon failure) or + how many bytes were used (upon success) + +Return Value: + + status + +--*/ +{ + PFDO_DEVICE_DATA fdoData; + + UNREFERENCED_PARAMETER(OutBufferSize); + + PAGED_CODE(); + + fdoData = FdoGetData(WdfWmiInstanceGetDevice(WmiInstance)); + + *BufferUsed = sizeof (fdoData->StdToasterBusData); + if (OutBufferSize < sizeof(fdoData->StdToasterBusData)) { + return STATUS_BUFFER_TOO_SMALL; + } + + * (PTOASTER_BUS_WMI_STD_DATA) OutBuffer = fdoData->StdToasterBusData; + + return STATUS_SUCCESS; +} + diff --git a/general/toaster/toastDrv/kmdf/bus/static/StatBus.vcxproj b/general/toaster/toastDrv/kmdf/bus/static/StatBus.vcxproj new file mode 100644 index 000000000..b78e8a0c6 --- /dev/null +++ b/general/toaster/toastDrv/kmdf/bus/static/StatBus.vcxproj @@ -0,0 +1,192 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {965C0E80-0715-4376-A138-48D08EFDDC09} + $(MSBuildProjectName) + 1 + false + true + Debug + Win32 + {23FA1088-5777-47FF-8254-EFD0B687928E} + + + + 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)\statbus.inf + + + ".\$(IntDir)\busenum.bmf" + + + + true + .\$(IntDir)\busenumMof.h + .\$(IntDir)\htm + true + + + + StatBus + + + StatBus + + + StatBus + + + StatBus + + + + %(AdditionalIncludeDirectories);..\..\inc + + + %(AdditionalIncludeDirectories);..\..\inc + + + + + %(AdditionalIncludeDirectories);..\..\inc + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ntstrsafe.lib + + + + + %(AdditionalIncludeDirectories);..\..\inc + + + %(AdditionalIncludeDirectories);..\..\inc + + + + + %(AdditionalIncludeDirectories);..\..\inc + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ntstrsafe.lib + + + + + %(AdditionalIncludeDirectories);..\..\inc + + + %(AdditionalIncludeDirectories);..\..\inc + + + + + %(AdditionalIncludeDirectories);..\..\inc + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ntstrsafe.lib + + + + + %(AdditionalIncludeDirectories);..\..\inc + + + %(AdditionalIncludeDirectories);..\..\inc + + + + + %(AdditionalIncludeDirectories);..\..\inc + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ntstrsafe.lib + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/general/toaster/toastDrv/kmdf/bus/static/StatBus.vcxproj.Filters b/general/toaster/toastDrv/kmdf/bus/static/StatBus.vcxproj.Filters new file mode 100644 index 000000000..f4363bf06 --- /dev/null +++ b/general/toaster/toastDrv/kmdf/bus/static/StatBus.vcxproj.Filters @@ -0,0 +1,45 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {86D36061-8E62-43AE-9677-EF29528CAAE9} + + + h;hpp;hxx;hm;inl;inc;xsd + {67A8D587-DF81-4D99-B5F5-A02788E04594} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {5EB6269C-B636-406C-A13C-A22C2CF8D4DA} + + + inf;inv;inx;mof;mc; + {26ED96D4-93DD-490C-84FF-7B474E8AE395} + + + + + Driver Files + + + Driver Files + + + + + Source Files + + + Source Files + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/general/toaster/toastDrv/kmdf/bus/static/busenum.c b/general/toaster/toastDrv/kmdf/bus/static/busenum.c new file mode 100644 index 000000000..41e79f0f8 --- /dev/null +++ b/general/toaster/toastDrv/kmdf/bus/static/busenum.c @@ -0,0 +1,685 @@ +/*++ + +Copyright (c) 2003 Microsoft Corporation All Rights Reserved + +Module Name: + + BUSENUM.C + +Abstract: + + This module contains routines to handle the function driver + aspect of the bus driver. + +Environment: + + kernel mode only + +--*/ + +#include "busenum.h" + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (INIT, DriverEntry) +#pragma alloc_text (PAGE, Bus_EvtDeviceAdd) +#pragma alloc_text (PAGE, Bus_EvtIoDeviceControl) +#pragma alloc_text (PAGE, Bus_PlugInDevice) +#pragma alloc_text (PAGE, Bus_UnPlugDevice) +#pragma alloc_text (PAGE, Bus_EjectDevice) +#endif + + + +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; + + KdPrint(("Toaster Static Bus 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 specifing one in the Config structure. + // + + WDF_DRIVER_CONFIG_INIT( + &config, + Bus_EvtDeviceAdd + ); + + // + // Create a framework driver object to represent our driver. + // + status = WdfDriverCreate(DriverObject, + RegistryPath, + WDF_NO_OBJECT_ATTRIBUTES, + &config, + WDF_NO_HANDLE); + + if (!NT_SUCCESS(status)) { + KdPrint(("WdfDriverCreate failed with status 0x%x\n", status)); + } + + return status; + +} + + +NTSTATUS +Bus_EvtDeviceAdd( + IN WDFDRIVER Driver, + IN PWDFDEVICE_INIT DeviceInit + ) +/*++ +Routine Description: + + Bus_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 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_DEVICE_DATA deviceData; + PNP_BUS_INFORMATION busInfo; + WDFQUEUE queue; + + UNREFERENCED_PARAMETER(Driver); + + PAGED_CODE (); + + KdPrint(("Bus_EvtDeviceAdd: 0x%p\n", Driver)); + + // + // 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); + WdfDeviceInitSetExclusive(DeviceInit, TRUE); + + // + // Initialize attributes structure to specify size and accessor function + // for storing device context. + // + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, FDO_DEVICE_DATA); + + // + // Create a framework device object. In response to this call, framework + // creates a WDM deviceobject. + // + status = WdfDeviceCreate(&DeviceInit, &attributes, &device); + if (!NT_SUCCESS(status)) { + return status; + } + + // + // Get the device context. + // + deviceData = FdoGetData(device); + + WDF_OBJECT_ATTRIBUTES_INIT(&attributes); + attributes.ParentObject = device; + // + // Purpose of this lock is documented in Bus_PlugInDevice routine below. + // + status = WdfWaitLockCreate(&attributes, &deviceData->ChildLock); + if (!NT_SUCCESS(status)) { + return status; + } + + // + // 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 + ); + + queueConfig.EvtIoDeviceControl = Bus_EvtIoDeviceControl; + + // + // 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 with status 0x%x\n", status)); + return status; + } + + // + // Create device interface for this device. The interface will be + // enabled by the framework when we return from StartDevice successfully. + // + status = WdfDeviceCreateDeviceInterface( + device, + &GUID_DEVINTERFACE_BUSENUM_TOASTER, + NULL // No Reference String + ); + if (!NT_SUCCESS(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_DEVCLASS_TOASTER; + busInfo.LegacyBusType = PNPBus; + busInfo.BusNumber = 0; + + WdfDeviceSetBusInformationForChildren(device, &busInfo); + + status = Bus_WmiRegistration(device); + if (!NT_SUCCESS(status)) { + return status; + } + + status = Bus_DoStaticEnumeration(device); + + return status; +} + +VOID +Bus_EvtIoDeviceControl( + IN WDFQUEUE Queue, + IN WDFREQUEST Request, + IN size_t OutputBufferLength, + IN size_t InputBufferLength, + IN ULONG IoControlCode + ) + +/*++ +Routine Description: + + Handle user mode PlugIn, UnPlug and device Eject requests. + +Arguments: + + Queue - Handle to the framework queue object that is associated + with the I/O request. + + Request - Handle to a framework request object. This one represents + the IRP_MJ_DEVICE_CONTROL IRP received by the framework. + + OutputBufferLength - Length, in bytes, of the request's output buffer, + if an output buffer is available. + + InputBufferLength - Length, in bytes, of the request's input buffer, + if an input buffer is available. + IoControlCode - Driver-defined or system-defined I/O control code (IOCTL) + that is associated with the request. + +Return Value: + + VOID + +--*/ +{ + NTSTATUS status = STATUS_INVALID_PARAMETER; + WDFDEVICE hDevice; + size_t length = 0; + PBUSENUM_PLUGIN_HARDWARE plugIn = NULL; + PBUSENUM_UNPLUG_HARDWARE unPlug = NULL; + PBUSENUM_EJECT_HARDWARE eject = NULL; + + PAGED_CODE (); + + UNREFERENCED_PARAMETER(OutputBufferLength); + + hDevice = WdfIoQueueGetDevice(Queue); + + KdPrint(("Bus_EvtIoDeviceControl: 0x%p\n", hDevice)); + + switch (IoControlCode) { + case IOCTL_BUSENUM_PLUGIN_HARDWARE: + + status = WdfRequestRetrieveInputBuffer (Request, + sizeof (BUSENUM_PLUGIN_HARDWARE) + + (sizeof(UNICODE_NULL) * 2), // 2 for double NULL termination (MULTI_SZ) + &plugIn, + &length); + if( !NT_SUCCESS(status) ) { + KdPrint(("WdfRequestRetrieveInputBuffer failed 0x%x\n", status)); + break; + } + + if (sizeof (BUSENUM_PLUGIN_HARDWARE) == plugIn->Size) + { + + length = (InputBufferLength - sizeof (BUSENUM_PLUGIN_HARDWARE))/sizeof(WCHAR); + // + // Make sure the IDs is double NULL terminated. TODO: + // + if ((UNICODE_NULL != plugIn->HardwareIDs[length - 1]) || + (UNICODE_NULL != plugIn->HardwareIDs[length - 2])) { + + status = STATUS_INVALID_PARAMETER; + break; + } + + status = Bus_PlugInDevice(hDevice, + plugIn->HardwareIDs, + plugIn->SerialNo); + + } + break; + + case IOCTL_BUSENUM_UNPLUG_HARDWARE: + + status = WdfRequestRetrieveInputBuffer (Request, + sizeof (BUSENUM_UNPLUG_HARDWARE), + &unPlug, &length); + if( !NT_SUCCESS(status) ) { + KdPrint(("WdfRequestRetrieveInputBuffer failed 0x%x\n", status)); + break; + } + + if (unPlug->Size == InputBufferLength) + { + + status= Bus_UnPlugDevice(hDevice, unPlug->SerialNo ); + + } + + break; + + case IOCTL_BUSENUM_EJECT_HARDWARE: + + status = WdfRequestRetrieveInputBuffer (Request, + sizeof (BUSENUM_EJECT_HARDWARE), + &eject, &length); + if( !NT_SUCCESS(status) ) { + KdPrint(("WdfRequestRetrieveInputBuffer failed 0x%x\n", status)); + break; + } + + if (eject->Size == InputBufferLength) + { + status= Bus_EjectDevice(hDevice, eject->SerialNo); + + } + break; + + default: + break; // default status is STATUS_INVALID_PARAMETER + } + + WdfRequestCompleteWithInformation(Request, status, length); +} + +NTSTATUS +Bus_PlugInDevice( + _In_ WDFDEVICE Device, + _In_ PWSTR HardwareIds, + _In_ ULONG SerialNo + ) + +/*++ + +Routine Description: + + The user application has told us that a new device on the bus has arrived. + + We therefore need to create a new PDO, initialize it, add it to the list + of PDOs for this FDO bus, and then tell Plug and Play that all of this + happened so that it will start sending prodding IRPs. + +--*/ + +{ + NTSTATUS status = STATUS_SUCCESS; + BOOLEAN unique = TRUE; + WDFDEVICE hChild; + PPDO_DEVICE_DATA pdoData; + PFDO_DEVICE_DATA deviceData; + + PAGED_CODE (); + + // + // 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. + // + deviceData = FdoGetData(Device); + hChild = 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(deviceData->ChildLock, NULL); + WdfFdoLockStaticChildListForIteration(Device); + + while ((hChild = WdfFdoRetrieveNextStaticChild(Device, + hChild, 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. + // + pdoData = PdoGetData(hChild); + + // + // 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 == pdoData->SerialNo) { + unique = FALSE; + status = STATUS_INVALID_PARAMETER; + break; + } + } + + if (unique) { + // + // 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 = Bus_CreatePdo(Device, HardwareIds, SerialNo); + } + + WdfFdoUnlockStaticChildListFromIteration(Device); + WdfWaitLockRelease(deviceData->ChildLock); + + return status; +} + +NTSTATUS +Bus_UnPlugDevice( + 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: + + +Returns: + + STATUS_SUCCESS upon successful removal from the list + STATUS_INVALID_PARAMETER if the removal was unsuccessful + +--*/ + +{ + PPDO_DEVICE_DATA pdoData; + BOOLEAN found = FALSE; + BOOLEAN plugOutAll; + WDFDEVICE hChild; + NTSTATUS status = STATUS_INVALID_PARAMETER; + + PAGED_CODE (); + + plugOutAll = (0 == SerialNo) ? TRUE : FALSE; + + hChild = NULL; + + WdfFdoLockStaticChildListForIteration(Device); + + while ((hChild = WdfFdoRetrieveNextStaticChild(Device, + hChild, WdfRetrieveAddedChildren)) != NULL) { + + if (plugOutAll) { + + status = WdfPdoMarkMissing(hChild); + if(!NT_SUCCESS(status)) { + KdPrint(("WdfPdoMarkMissing failed 0x%x\n", status)); + break; + } + + found = TRUE; + } + else { + pdoData = PdoGetData(hChild); + + if (SerialNo == pdoData->SerialNo) { + + status = WdfPdoMarkMissing(hChild); + if(!NT_SUCCESS(status)) { + KdPrint(("WdfPdoMarkMissing failed 0x%x\n", status)); + break; + } + + found = TRUE; + break; + } + } + } + + WdfFdoUnlockStaticChildListFromIteration(Device); + + if (found) { + status = STATUS_SUCCESS; + } + + return status; +} + +NTSTATUS +Bus_EjectDevice( + WDFDEVICE Device, + ULONG SerialNo + ) + +/*++ + +Routine Description: + + The user application has told us to eject the device from the bus. + In a real situation the driver gets notified by an interrupt when the + user presses the Eject button on the device. + +Arguments: + + +Returns: + + STATUS_SUCCESS upon successful removal from the list + STATUS_INVALID_PARAMETER if the removal was unsuccessful + +--*/ + +{ + PPDO_DEVICE_DATA pdoData; + BOOLEAN ejectAll; + WDFDEVICE hChild; + NTSTATUS status = STATUS_INVALID_PARAMETER; + + PAGED_CODE (); + + // + // Scan the list to find matching PDOs + // + ejectAll = (0 == SerialNo) ? TRUE : FALSE; + hChild = NULL; + + WdfFdoLockStaticChildListForIteration(Device); + + while ((hChild = WdfFdoRetrieveNextStaticChild(Device, + hChild, + WdfRetrieveAddedChildren)) != NULL) { + + pdoData = PdoGetData(hChild); + + if (ejectAll || SerialNo == pdoData->SerialNo) { + + status = STATUS_SUCCESS; + WdfPdoRequestEject(hChild); + if (!ejectAll) { + break; + } + } + } + + WdfFdoUnlockStaticChildListFromIteration(Device); + + return status; +} + +NTSTATUS +Bus_DoStaticEnumeration( + IN WDFDEVICE Device + ) +/*++ +Routine Description: + + The routine enables you to statically enumerate child devices + during start instead of running the enum.exe/notify.exe to + enumerate toaster devices. + + In order to statically enumerate, user must specify the number + of toasters in the Toaster Bus driver's device registry. The + default value is zero. + + HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\Root\SYSTEM\0002\ + Device Parameters + NumberOfToasters:REG_DWORD:2 + + You can also configure this value in the Toaster Bus Inf file. + +--*/ + +{ + WDFKEY hKey = NULL; + NTSTATUS status; + ULONG value, i; + DECLARE_CONST_UNICODE_STRING(valueName, L"NumberOfToasters"); + + // + // If the registry value doesn't exist, we will use the + // hardcoded default number. + // + value = DEF_STATICALLY_ENUMERATED_TOASTERS; + + // + // Open the device registry and read the "NumberOfToasters" value. + // + status = WdfDeviceOpenRegistryKey(Device, + PLUGPLAY_REGKEY_DEVICE, + STANDARD_RIGHTS_ALL, + NULL, // PWDF_OBJECT_ATTRIBUTES + &hKey); + + if (NT_SUCCESS (status)) { + + status = WdfRegistryQueryULong(hKey, + &valueName, + &value); + + WdfRegistryClose(hKey); + hKey = NULL; // Set hKey to NULL to catch any accidental subsequent use. + + if (NT_SUCCESS (status)) { + // + // Make sure it doesn't exceed the max. This is required to prevent + // denial of service by enumerating large number of child devices. + // + value = min(value, MAX_STATICALLY_ENUMERATED_TOASTERS); + }else { + return STATUS_SUCCESS; // This is an optional property. + } + } + + KdPrint(("Enumerating %d toaster devices\n", value)); + + for(i=1; i<= value; i++) { + // + // Value of i is used as serial number. + // + status = Bus_PlugInDevice(Device, + BUS_HARDWARE_IDS, + i ); + } + + return status; +} + + + diff --git a/general/toaster/toastDrv/kmdf/bus/static/busenum.h b/general/toaster/toastDrv/kmdf/bus/static/busenum.h new file mode 100644 index 000000000..b13b21b40 --- /dev/null +++ b/general/toaster/toastDrv/kmdf/bus/static/busenum.h @@ -0,0 +1,168 @@ +/*++ + +Copyright (c) 2003 Microsoft Corporation All Rights Reserved + +Module Name: + + busenum.h + +Abstract: + + This module contains the common private declarations + for the Toaster Bus enumerator. + +Environment: + + kernel mode only + +--*/ + +#include +#include +#define NTSTRSAFE_LIB +#include +#include +#include "driver.h" +#include "public.h" +#include + +#ifndef BUSENUM_H +#define BUSENUM_H + +extern ULONG BusEnumDebugLevel; + +#define BUSRESOURCENAME L"MofResourceName" + +#define DEF_STATICALLY_ENUMERATED_TOASTERS 0 +#define MAX_STATICALLY_ENUMERATED_TOASTERS 10 + +#ifndef min +#define min(_a, _b) (((_a) < (_b)) ? (_a) : (_b)) +#endif + +#ifndef max +#define max(_a, _b) (((_a) > (_b)) ? (_a) : (_b)) +#endif + +// +// Structure for reporting data to WMI +// + +typedef ToasterBusInformation TOASTER_BUS_WMI_STD_DATA, * PTOASTER_BUS_WMI_STD_DATA; + +// +// The device extension for the PDOs. +// That's of the toaster device which this bus driver enumerates. +// + +typedef struct _PDO_DEVICE_DATA +{ + // Unique serail number of the device on the bus + + ULONG SerialNo; + +} PDO_DEVICE_DATA, *PPDO_DEVICE_DATA; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(PDO_DEVICE_DATA, PdoGetData) + +// +// The device extension of the bus itself. From whence the PDO's are born. +// + +typedef struct _FDO_DEVICE_DATA +{ + TOASTER_BUS_WMI_STD_DATA StdToasterBusData; + + WDFWAITLOCK ChildLock; + +} FDO_DEVICE_DATA, *PFDO_DEVICE_DATA; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(FDO_DEVICE_DATA, FdoGetData) + +typedef struct _QUEUE_DATA +{ + PFDO_DEVICE_DATA FdoData; + +} QUEUE_DATA, *PQUEUE_DATA; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(QUEUE_DATA, QueueGetData) + +// +// Prototypes of functions +// + +DRIVER_INITIALIZE DriverEntry; + +EVT_WDF_DRIVER_DEVICE_ADD Bus_EvtDeviceAdd; + +EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL Bus_EvtIoDeviceControl; + +NTSTATUS +Bus_PlugInDevice( + _In_ WDFDEVICE Device, + _In_ PWSTR HardwareIds, + _In_ ULONG SerialNo + ); + +NTSTATUS +Bus_UnPlugDevice( + WDFDEVICE Device, + ULONG SerialNo + ); + + +NTSTATUS +Bus_EjectDevice( + WDFDEVICE Device, + ULONG SerialNo + ); + +NTSTATUS +Bus_CreatePdo( + _In_ WDFDEVICE Device, + _In_ PWSTR HardwareIds, + _In_ ULONG SerialNo + ); + + +NTSTATUS +Bus_DoStaticEnumeration( + IN WDFDEVICE Device + ); + +// +// Interface functions +// + +BOOLEAN +Bus_GetCrispinessLevel( + IN WDFDEVICE ChildDevice, + OUT PUCHAR Level + ); + +BOOLEAN +Bus_SetCrispinessLevel( + IN WDFDEVICE ChildDevice, + OUT UCHAR Level + ); + +BOOLEAN +Bus_IsSafetyLockEnabled( + IN PVOID Context + ); + +// +// Defined in wmi.c +// + +NTSTATUS +Bus_WmiRegistration( + WDFDEVICE Device + ); + +EVT_WDF_WMI_INSTANCE_SET_ITEM Bus_EvtStdDataSetItem; +EVT_WDF_WMI_INSTANCE_SET_INSTANCE Bus_EvtStdDataSetInstance; +EVT_WDF_WMI_INSTANCE_QUERY_INSTANCE Bus_EvtStdDataQueryInstance; + +#endif + diff --git a/general/toaster/toastDrv/kmdf/bus/static/busenum.mof b/general/toaster/toastDrv/kmdf/bus/static/busenum.mof new file mode 100644 index 000000000..e29eeec3f --- /dev/null +++ b/general/toaster/toastDrv/kmdf/bus/static/busenum.mof @@ -0,0 +1,26 @@ +#PRAGMA AUTORECOVER + +[Dynamic, Provider("WMIProv"), + WMI, + Description("Toaster Bus driver information"), + guid("{0006A660-8F12-11d2-B854-00C04FAD5171}"), + locale("MS\\0x409")] +class ToasterBusInformation +{ + [key, read] + string InstanceName; + [read] boolean Active; + + [WmiDataId(1), + read, + Description("Number of errors that occurred on this device")] + uint32 ErrorCount; + + [WmiDataId(2), + read, + write, + Description("The DebugPrintLevel property indicates the debug output level of toaster bus device.")] + uint32 DebugPrintLevel; + +}; + diff --git a/general/toaster/toastDrv/kmdf/bus/static/busenum.rc b/general/toaster/toastDrv/kmdf/bus/static/busenum.rc new file mode 100644 index 000000000..a803bd28e --- /dev/null +++ b/general/toaster/toastDrv/kmdf/bus/static/busenum.rc @@ -0,0 +1,13 @@ +#include + +#include + +#define VER_FILETYPE VFT_DLL +#define VER_FILESUBTYPE VFT2_UNKNOWN +#define VER_FILEDESCRIPTION_STR "Framework Version of Toaster Static Bus Enumerator" +#define VER_INTERNALNAME_STR "statbus.sys" +#define VER_ORIGINALFILENAME_STR "statbus.sys" + +#include "common.ver" + +MofResourceName MOFDATA busenum.bmf diff --git a/general/toaster/toastDrv/kmdf/bus/static/buspdo.c b/general/toaster/toastDrv/kmdf/bus/static/buspdo.c new file mode 100644 index 000000000..cb68afeb8 --- /dev/null +++ b/general/toaster/toastDrv/kmdf/bus/static/buspdo.c @@ -0,0 +1,360 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + BusPdo.c + +Abstract: + + This module handles plug & play calls for the child device (PDO). + +Environment: + + kernel mode only + +--*/ + +#include "busenum.h" + +ULONG BusEnumDebugLevel; + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, Bus_CreatePdo) +#endif + +#define MAX_ID_LEN 80 + +NTSTATUS +Bus_CreatePdo( + _In_ WDFDEVICE Device, + _In_ PWSTR HardwareIds, + _In_ ULONG SerialNo +) +/*++ + +Routine Description: + + This routine creates and initialize a PDO. + +Arguments: + +Return Value: + + NT Status code. + +--*/ +{ + NTSTATUS status; + PWDFDEVICE_INIT pDeviceInit = NULL; + PPDO_DEVICE_DATA pdoData = NULL; + WDFDEVICE hChild = NULL; + WDF_QUERY_INTERFACE_CONFIG qiConfig; + WDF_OBJECT_ATTRIBUTES pdoAttributes; + WDF_DEVICE_PNP_CAPABILITIES pnpCaps; + WDF_DEVICE_POWER_CAPABILITIES powerCaps; + TOASTER_INTERFACE_STANDARD ToasterInterface; + DECLARE_CONST_UNICODE_STRING(compatId, BUSENUM_COMPATIBLE_IDS); + DECLARE_CONST_UNICODE_STRING(deviceLocation, L"Toaster Bus 0"); + UNICODE_STRING deviceId; + DECLARE_UNICODE_STRING_SIZE(buffer, MAX_ID_LEN); + + KdPrint(("BusEnum: Entered Bus_CreatePdo\n")); + + PAGED_CODE(); + + // + // Allocate a WDFDEVICE_INIT structure and set the properties + // so that we can create a device object for the child. + // + pDeviceInit = WdfPdoInitAllocate(Device); + + if (pDeviceInit == NULL) { + status = STATUS_INSUFFICIENT_RESOURCES; + goto Cleanup; + } + + // + // Set DeviceType + // + WdfDeviceInitSetDeviceType(pDeviceInit, FILE_DEVICE_BUS_EXTENDER); + + // + // Provide DeviceID, HardwareIDs, CompatibleIDs and InstanceId + // + RtlInitUnicodeString(&deviceId,HardwareIds); + + status = WdfPdoInitAssignDeviceID(pDeviceInit, &deviceId); + if (!NT_SUCCESS(status)) { + goto Cleanup; + } + + // + // Note same string is used to initialize hardware id too + // + status = WdfPdoInitAddHardwareID(pDeviceInit, &deviceId); + if (!NT_SUCCESS(status)) { + goto Cleanup; + } + + status = WdfPdoInitAddCompatibleID(pDeviceInit, &compatId); + if (!NT_SUCCESS(status)) { + goto Cleanup; + } + + status = RtlUnicodeStringPrintf(&buffer, L"%02d", SerialNo); + if (!NT_SUCCESS(status)) { + goto Cleanup; + } + + status = WdfPdoInitAssignInstanceID(pDeviceInit, &buffer); + if (!NT_SUCCESS(status)) { + goto Cleanup; + } + + // + // 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"Microsoft_Eliyas_Toaster_%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. + // + status = WdfPdoInitAddDeviceText(pDeviceInit, + &buffer, + &deviceLocation, + 0x409); + if (!NT_SUCCESS(status)) { + goto Cleanup; + } + + WdfPdoInitSetDefaultLocale(pDeviceInit, 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_DEVICE_DATA); + + status = WdfDeviceCreate(&pDeviceInit, &pdoAttributes, &hChild); + if (!NT_SUCCESS(status)) { + goto Cleanup; + + } + + // + // Once the device is created successfully, framework frees the + // DeviceInit memory and sets the pDeviceInit to NULL. So don't + // call any WdfDeviceInit functions after that. + // + // Get the device context. + // + pdoData = PdoGetData(hChild); + + pdoData->SerialNo = SerialNo; + + // + // Set some properties for the child device. + // + WDF_DEVICE_PNP_CAPABILITIES_INIT(&pnpCaps); + pnpCaps.Removable = WdfTrue; + pnpCaps.EjectSupported = WdfTrue; + pnpCaps.SurpriseRemovalOK = WdfTrue; + + pnpCaps.Address = SerialNo; + pnpCaps.UINumber = SerialNo; + + WdfDeviceSetPnpCapabilities(hChild, &pnpCaps); + + WDF_DEVICE_POWER_CAPABILITIES_INIT(&powerCaps); + + powerCaps.DeviceD1 = WdfTrue; + powerCaps.WakeFromD1 = WdfTrue; + powerCaps.DeviceWake = PowerDeviceD1; + + powerCaps.DeviceState[PowerSystemWorking] = PowerDeviceD0; + powerCaps.DeviceState[PowerSystemSleeping1] = PowerDeviceD1; + powerCaps.DeviceState[PowerSystemSleeping2] = PowerDeviceD3; + powerCaps.DeviceState[PowerSystemSleeping3] = PowerDeviceD3; + powerCaps.DeviceState[PowerSystemHibernate] = PowerDeviceD3; + powerCaps.DeviceState[PowerSystemShutdown] = PowerDeviceD3; + + WdfDeviceSetPowerCapabilities(hChild, &powerCaps); + + // + // Create a custom interface so that other drivers can + // query (IRP_MN_QUERY_INTERFACE) and use our callbacks directly. + // + RtlZeroMemory(&ToasterInterface, sizeof(ToasterInterface)); + + ToasterInterface.InterfaceHeader.Size = sizeof(ToasterInterface); + ToasterInterface.InterfaceHeader.Version = 1; + ToasterInterface.InterfaceHeader.Context = (PVOID) hChild; + + // + // Let the framework handle reference counting. + // + ToasterInterface.InterfaceHeader.InterfaceReference = + WdfDeviceInterfaceReferenceNoOp; + ToasterInterface.InterfaceHeader.InterfaceDereference = + WdfDeviceInterfaceDereferenceNoOp; + + ToasterInterface.GetCrispinessLevel = Bus_GetCrispinessLevel; + ToasterInterface.SetCrispinessLevel = Bus_SetCrispinessLevel; + ToasterInterface.IsSafetyLockEnabled = Bus_IsSafetyLockEnabled; + + WDF_QUERY_INTERFACE_CONFIG_INIT(&qiConfig, + (PINTERFACE) &ToasterInterface, + &GUID_TOASTER_INTERFACE_STANDARD, + NULL); + // + // If you have multiple interfaces, you can call WdfDeviceAddQueryInterface + // multiple times to add additional interfaces. + // + status = WdfDeviceAddQueryInterface(hChild, &qiConfig); + 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, hChild); + if (!NT_SUCCESS(status)) { + goto Cleanup; + } + + return status; + +Cleanup: + KdPrint(("BusEnum: Bus_CreatePdo failed %x\n", status)); + + // + // Call WdfDeviceInitFree if you encounter an error before the + // device is created. Once the device is created, framework + // NULLs the pDeviceInit value. + // + if (pDeviceInit != NULL) { + WdfDeviceInitFree(pDeviceInit); + } + + if(hChild) { + WdfObjectDelete(hChild); + } + + return status; +} + + +BOOLEAN +Bus_GetCrispinessLevel( + IN WDFDEVICE ChildDevice, + OUT PUCHAR Level + ) +/*++ + +Routine Description: + + This routine gets the current crispiness level of the toaster. + +Arguments: + + Context pointer to PDO device extension + Level crispiness level of the device + +Return Value: + + TRUE or FALSE + +--*/ +{ + UNREFERENCED_PARAMETER(ChildDevice); + + // + // Validate the context to see if it's really a pointer + // to PDO's device extension. You can store some kind + // of signature in the PDO for this purpose + // + + KdPrint(("BusEnum: GetCrispnessLevel\n")); + + *Level = 10; + return TRUE; +} + +BOOLEAN +Bus_SetCrispinessLevel( + IN WDFDEVICE ChildDevice, + IN UCHAR Level + ) +/*++ + +Routine Description: + + This routine sets the current crispiness level of the toaster. + +Arguments: + + Context pointer to PDO device extension + Level crispiness level of the device + +Return Value: + + TRUE or FALSE + +--*/ +{ + UNREFERENCED_PARAMETER(ChildDevice); + UNREFERENCED_PARAMETER(Level); + + KdPrint(("BusEnum: SetCrispnessLevel\n")); + + return TRUE; +} + +BOOLEAN +Bus_IsSafetyLockEnabled( + IN WDFDEVICE ChildDevice + ) +/*++ + +Routine Description: + + Routine to check whether safety lock is enabled + +Arguments: + + Context pointer to PDO device extension + +Return Value: + + TRUE or FALSE + +--*/ +{ + UNREFERENCED_PARAMETER(ChildDevice); + + KdPrint(("BusEnum: IsSafetyLockEnabled\n")); + + return TRUE; +} + diff --git a/general/toaster/toastDrv/kmdf/bus/static/statbus.inx b/general/toaster/toastDrv/kmdf/bus/static/statbus.inx new file mode 100644 index 000000000..9e8cb03a7 --- /dev/null +++ b/general/toaster/toastDrv/kmdf/bus/static/statbus.inx @@ -0,0 +1,102 @@ +;/*++ +; +;Copyright (c) 1990-1999 Microsoft Corporation All rights Reserved +; +;Module Name: +; +; STATBUS.INF +; +;Abstract: +; INF file for installing toaster bus enumerator driver +; +;Installation Notes: +; Using Devcon: Type "devcon install statbus.inf root\statbus" to install +; +;--*/ +[Version] +Signature="$WINDOWS NT$" +Class=System +ClassGuid={4D36E97D-E325-11CE-BFC1-08002BE10318} +Provider=%ProviderName% +DriverVer=06/17/1999, 5.00.2064 +CatalogFile=KmdfSamples.cat + +[DestinationDirs] +DefaultDestDir = 12 + +[SourceDisksNames] +1 = %DiskId1%,,,"" + +[SourceDisksFiles] +statbus.sys = 1,, + +;***************************************** +; ToasterStatBus Install Section +;***************************************** + +[Manufacturer] +%StdMfg%=Standard,NT$ARCH$ + +; For XP and later +[Standard.NT$ARCH$] +%ToasterStatBus.DeviceDesc%=ToasterStatBus_Device, root\statbus + +[ToasterStatBus_Device.NT] +CopyFiles=Drivers_Dir + +[ToasterStatBus_Device.NT.HW] +AddReg=ToasterStatBus_Device.NT.AddReg + +[ToasterStatBus_Device.NT.AddReg] +HKR,,DeviceCharacteristics,0x10001,0x0100 ; Use same security checks on relative opens +HKR,,Security,,"D:P(A;;GA;;;BA)(A;;GA;;;SY)" ; Allow generic-all access to Built-in administrators and Local system + +[Drivers_Dir] +statbus.sys + + +;-------------- Service installation +[ToasterStatBus_Device.NT.Services] +AddService = statbus,%SPSVCINST_ASSOCSERVICE%, Statbus_Service_Inst + +; -------------- statbus driver install sections +[Statbus_Service_Inst] +DisplayName = %statbus.SVCDESC% +ServiceType = 1 ; SERVICE_KERNEL_DRIVER +StartType = 3 ; SERVICE_DEMAND_START +ErrorControl = 1 ; SERVICE_ERROR_NORMAL +ServiceBinary = %12%\statbus.sys + +; +;--- ToasterStatBus_Device WDF Coinstaller installation ------ +; +[DestinationDirs] +ToasterStatBus_Device_CoInstaller_CopyFiles = 11 + +[ToasterStatBus_Device.NT.CoInstallers] +AddReg=ToasterStatBus_Device_CoInstaller_AddReg +CopyFiles=ToasterStatBus_Device_CoInstaller_CopyFiles + +[ToasterStatBus_Device_CoInstaller_AddReg] +HKR,,CoInstallers32,0x00010000, "WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll,WdfCoInstaller" + +[ToasterStatBus_Device_CoInstaller_CopyFiles] +WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll + +[SourceDisksFiles] +WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll=1 ; make sure the number matches with SourceDisksNames + +[ToasterStatBus_Device.NT.Wdf] +KmdfService = statbus, statbus_wdfsect +[statbus_wdfsect] +KmdfLibraryVersion = $KMDFVERSION$ + + + +[Strings] +SPSVCINST_ASSOCSERVICE = 0x00000002 +ProviderName = "TODO-Set-Provider" +StdMfg = "(Standard system devices)" +DiskId1 = "Toaster Static Bus Installation Disk #1" +ToasterStatBus.DeviceDesc = "Toaster Static Bus Enumerator" +statbus.SVCDESC = "Toaster Static Bus Enumerator" diff --git a/general/toaster/toastDrv/kmdf/bus/static/wmi.c b/general/toaster/toastDrv/kmdf/bus/static/wmi.c new file mode 100644 index 000000000..3b6ef9168 --- /dev/null +++ b/general/toaster/toastDrv/kmdf/bus/static/wmi.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: + + WMI.C + +Abstract: + + This module handles all the WMI Irps. + +Environment: + + Kernel mode + +--*/ + +#include "busenum.h" + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE,Bus_WmiRegistration) +#pragma alloc_text(PAGE,Bus_EvtStdDataSetItem) +#pragma alloc_text(PAGE,Bus_EvtStdDataSetInstance) +#pragma alloc_text(PAGE,Bus_EvtStdDataQueryInstance) +#endif + +NTSTATUS +Bus_WmiRegistration( + 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; + PFDO_DEVICE_DATA deviceData; + NTSTATUS status; + DECLARE_CONST_UNICODE_STRING(busRsrcName, BUSRESOURCENAME); + + PAGED_CODE(); + + deviceData = FdoGetData(Device); + + // + // Register WMI classes. + // First specify the resource name which contain the binary mof resource. + // + status = WdfDeviceAssignMofResourceName(Device, &busRsrcName); + if (!NT_SUCCESS(status)) { + return status; + } + + WDF_WMI_PROVIDER_CONFIG_INIT(&providerConfig, &ToasterBusInformation_GUID); + providerConfig.MinInstanceBufferSize = sizeof(TOASTER_BUS_WMI_STD_DATA); + + // + // You would want to create a WDFWMIPROVIDER handle separately if you are + // going to dynamically create instances on the provider. Since we are + // statically creating one instance, there is no need to create the provider + // handle. + // + WDF_WMI_INSTANCE_CONFIG_INIT_PROVIDER_CONFIG(&instanceConfig, &providerConfig); + + // + // By setting Regsiter to TRUE, we tell the framework to create a provider + // as part of the Instance creation call. This eliminates the need to + // call WdfWmiProviderRegister. + // + instanceConfig.Register = TRUE; + instanceConfig.EvtWmiInstanceQueryInstance = Bus_EvtStdDataQueryInstance; + instanceConfig.EvtWmiInstanceSetInstance = Bus_EvtStdDataSetInstance; + instanceConfig.EvtWmiInstanceSetItem = Bus_EvtStdDataSetItem; + + status = WdfWmiInstanceCreate( + Device, + &instanceConfig, + WDF_NO_OBJECT_ATTRIBUTES, + WDF_NO_HANDLE + ); + + if (NT_SUCCESS(status)) { + deviceData->StdToasterBusData.ErrorCount = 0; + } + + return status; +} + +// +// WMI System Call back functions +// +NTSTATUS +Bus_EvtStdDataSetItem( + IN WDFWMIINSTANCE WmiInstance, + IN ULONG DataItemId, + IN ULONG InBufferSize, + IN PVOID InBuffer + ) +/*++ + +Routine Description: + + This routine is a callback into the driver to set for the contents of + an instance. + +Arguments: + + WmiInstance is the instance being set + + DataItemId has the id of the data item being set + + InBufferSize has the size of the data item passed + + InBuffer has the new values for the data item + +Return Value: + + status + +--*/ +{ + PFDO_DEVICE_DATA fdoData; + + PAGED_CODE(); + + fdoData = FdoGetData(WdfWmiInstanceGetDevice(WmiInstance)); + // + // TODO: Use generated header's #defines for constants and sizes + // (for the remainder of the file) + // + if (DataItemId == 2) { + if (InBufferSize < sizeof(ULONG)) { + return STATUS_BUFFER_TOO_SMALL; + } + + BusEnumDebugLevel = fdoData->StdToasterBusData.DebugPrintLevel = + *((PULONG)InBuffer); + + return STATUS_SUCCESS; + } + + // + // All other fields are read only + // + return STATUS_WMI_READ_ONLY; +} + +NTSTATUS +Bus_EvtStdDataSetInstance( + IN WDFWMIINSTANCE WmiInstance, + IN ULONG InBufferSize, + IN PVOID InBuffer + ) +/*++ + +Routine Description: + + This routine is a callback into the driver to set for the contents of + an instance. + +Arguments: + + WmiInstance is the instance being set + + BufferSize has the size of the data block passed + + Buffer has the new values for the data block + +Return Value: + + status + +--*/ +{ + PFDO_DEVICE_DATA fdoData; + + UNREFERENCED_PARAMETER(InBufferSize); + + PAGED_CODE(); + + fdoData = FdoGetData(WdfWmiInstanceGetDevice(WmiInstance)); + + // + // We will update only writable elements. + // + BusEnumDebugLevel = fdoData->StdToasterBusData.DebugPrintLevel = + ((PTOASTER_BUS_WMI_STD_DATA)InBuffer)->DebugPrintLevel; + + return STATUS_SUCCESS; +} + +NTSTATUS +Bus_EvtStdDataQueryInstance( + _In_ WDFWMIINSTANCE WmiInstance, + _In_ ULONG OutBufferSize, + _Out_writes_bytes_to_(OutBufferSize, *BufferUsed) PVOID OutBuffer, + _Out_ PULONG BufferUsed + ) +/*++ + +Routine Description: + + This routine is a callback into the driver to set for the contents of + a wmi instance + +Arguments: + + WmiInstance is the instance being set + + OutBufferSize on has the maximum size available to write the data + block. + + OutBuffer on return is filled with the returned data block + + BufferUsed pointer containing how many bytes are required (upon failure) or + how many bytes were used (upon success) + +Return Value: + + status + +--*/ +{ + PFDO_DEVICE_DATA fdoData; + + UNREFERENCED_PARAMETER(OutBufferSize); + + PAGED_CODE(); + + fdoData = FdoGetData(WdfWmiInstanceGetDevice(WmiInstance)); + + *BufferUsed = sizeof (fdoData->StdToasterBusData); + if (OutBufferSize < sizeof(fdoData->StdToasterBusData)) { + return STATUS_BUFFER_TOO_SMALL; + } + + * (PTOASTER_BUS_WMI_STD_DATA) OutBuffer = fdoData->StdToasterBusData; + + return STATUS_SUCCESS; +} + diff --git a/general/toaster/toastDrv/kmdf/filter/filter.inx b/general/toaster/toastDrv/kmdf/filter/filter.inx new file mode 100644 index 000000000..db58c43d6 --- /dev/null +++ b/general/toaster/toastDrv/kmdf/filter/filter.inx @@ -0,0 +1,140 @@ +;/*++ +; +;Copyright (c) 1990-1999 Microsoft Corporation All rights Reserved +; +;Module Name: +; +; filter.INF +; +;Abstract: +; INF file for installing toaster device drivers +; This file is dependant on wdffeatured.inf +; +;--*/ + +[Version] +Signature = "$WINDOWS NT$" +Class = TOASTER +ClassGuid = {B85B7C50-6A01-11d2-B841-00C04FAD5171} +Provider = %ProviderName% +DriverVer = 06/16/1999,5.00.2064 +CatalogFile = KmdfSamples.cat + +[DestinationDirs] +DefaultDestDir = 12 +ToasterClassInstallerCopyFiles = 11 +ToasterFilter_CoInstaller_CopyFiles = 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 +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 + +[ToasterClassInstallerCopyFiles] +tostrcls.dll + +;***************************************** +; Toaster Device Filter Install Section +;***************************************** + +[Manufacturer] +%StdMfg%=Standard,NT$ARCH$ + +[Standard.NT$ARCH$] +%ToasterFilter.DeviceDesc%=ToasterFilter, {b85b7c50-6a01-11d2-b841-00c04fad5171}\MsToaster + +[ToasterFilter.NT] +CopyFiles=ToasterFilter.NT.Copy + +[ToasterFilter.NT.Copy] +filter.sys +wdffeatured.sys + +[ToasterFilter.NT.HW] +AddReg = ToasterFilter.NT.HW.AddReg + + +[ToasterFilter.NT.HW.AddReg] +HKR,,"UpperFilters",0x00010000,"ToasterFilter" + +;***************************************** +; Toaster Device Filter Service Section +;***************************************** + +[ToasterFilter.NT.Services] +;Do not specify SPSVCINST_ASSOCSERVICE on filter drivers. +AddService = ToasterFilter,, filter_Service_Inst +AddService = wdffeatured, %SPSVCINST_ASSOCSERVICE%, wdffeatured_Service_Inst + +[filter_Service_Inst] +DisplayName = %filter.SvcDesc% +ServiceType = 1 ; SERVICE_KERNEL_DRIVER +StartType = 3 ; SERVICE_DEMAND_START +ErrorControl = 1 ; SERVICE_ERROR_NORMAL +ServiceBinary = %12%\filter.sys + + +[wdffeatured_Service_Inst] +DisplayName = %Toaster.SVCDESC% +ServiceType = 1 ; SERVICE_KERNEL_DRIVER +StartType = 3 ; SERVICE_DEMAND_START +ErrorControl = 1 ; SERVICE_ERROR_NORMAL +ServiceBinary = %12%\wdffeatured.sys + + +;************************* +; Source file information +;************************* + + +[SourceDisksNames] +1 = %DiskId1%,,,"" + +[SourceDisksFiles] +filter.sys = 1,, +wdffeatured.sys = 1,, +tostrcls.dll = 1,, +WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll = 1 + +; +;--- ToasterFilter Coinstaller installation ------ +; +[ToasterFilter.NT.CoInstallers] +AddReg = ToasterFilter_CoInstaller_AddReg +CopyFiles = ToasterFilter_CoInstaller_CopyFiles + +[ToasterFilter_CoInstaller_AddReg] +HKR,,CoInstallers32,0x00010000, "WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll,WdfCoInstaller" + +[ToasterFilter_CoInstaller_CopyFiles] +WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll + +[ToasterFilter.NT.Wdf] +KmdfService = ToasterFilter, Filter_wdfsect +KmdfService = wdffeatured, wdffeatured_wdfsect + +[Filter_wdfsect] +KmdfLibraryVersion = $KMDFVERSION$ + +[wdffeatured_wdfsect] +KmdfLibraryVersion = $KMDFVERSION$ + +[Strings] +SPSVCINST_ASSOCSERVICE = 0x00000002 +ProviderName = "TODO-Set-Provider" +StdMfg = "(Standard system devices)" +ClassName = "Toaster" +DiskId1 = "Toaster Filter Installation Disk #1" +ToasterFilter.DeviceDesc = "Toaster Filter" +filter.SvcDesc = "Toaster Filter Driver Refactored" +Toaster.SVCDESC = "Toaster Featured Device Driver" + diff --git a/general/toaster/toastDrv/kmdf/filter/generic/filter.c b/general/toaster/toastDrv/kmdf/filter/generic/filter.c new file mode 100644 index 000000000..c1dadac2e --- /dev/null +++ b/general/toaster/toastDrv/kmdf/filter/generic/filter.c @@ -0,0 +1,384 @@ +/*++ + +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: + + Kernel 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 + // + } + + 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/toastDrv/kmdf/filter/generic/filter.h b/general/toaster/toastDrv/kmdf/filter/generic/filter.h new file mode 100644 index 000000000..5ae9bf401 --- /dev/null +++ b/general/toaster/toastDrv/kmdf/filter/generic/filter.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: + + filter.h + +Abstract: + + Contains structure definitions and function prototypes for a generic filter driver. + +Environment: + + Kernel mode + +--*/ + +#include +#include +#include // for SDDLs +#define NTSTRSAFE_LIB +#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/toastDrv/kmdf/filter/generic/filter.rc b/general/toaster/toastDrv/kmdf/filter/generic/filter.rc new file mode 100644 index 000000000..990e40a31 --- /dev/null +++ b/general/toaster/toastDrv/kmdf/filter/generic/filter.rc @@ -0,0 +1,12 @@ +#include +#include +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "Filter Driver for the Toaster Stack" +#define VER_INTERNALNAME_STR DRIVERNAME +#define VER_ORIGINALFILENAME_STR DRIVERNAME + +#include "common.ver" + diff --git a/general/toaster/toastDrv/kmdf/filter/generic/filter.vcxproj b/general/toaster/toastDrv/kmdf/filter/generic/filter.vcxproj new file mode 100644 index 000000000..e59da4b85 --- /dev/null +++ b/general/toaster/toastDrv/kmdf/filter/generic/filter.vcxproj @@ -0,0 +1,188 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {C79A9F42-BDB4-4B5A-B7B5-1BEBF7752207} + $(MSBuildProjectName) + 1 + false + true + Debug + Win32 + {319B1451-754B-4F6E-9EBC-FDF268C22624} + + + + 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)\filter.inf + + + + filter + + + filter + + + filter + + + filter + + + + true + Level4 + %(AdditionalIncludeDirectories);..\..\inc + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\wdmsec.lib + + + %(AdditionalIncludeDirectories);..\..\inc + + + %(AdditionalIncludeDirectories);..\..\inc + + + + + true + Level4 + %(AdditionalIncludeDirectories);..\..\inc + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\wdmsec.lib + + + %(AdditionalIncludeDirectories);..\..\inc + + + %(AdditionalIncludeDirectories);..\..\inc + + + + + true + Level4 + %(AdditionalIncludeDirectories);..\..\inc + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\wdmsec.lib + + + %(AdditionalIncludeDirectories);..\..\inc + + + %(AdditionalIncludeDirectories);..\..\inc + + + + + true + Level4 + %(AdditionalIncludeDirectories);..\..\inc + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\wdmsec.lib + + + %(AdditionalIncludeDirectories);..\..\inc + + + %(AdditionalIncludeDirectories);..\..\inc + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/general/toaster/toastDrv/kmdf/filter/generic/filter.vcxproj.Filters b/general/toaster/toastDrv/kmdf/filter/generic/filter.vcxproj.Filters new file mode 100644 index 000000000..d5f0682e7 --- /dev/null +++ b/general/toaster/toastDrv/kmdf/filter/generic/filter.vcxproj.Filters @@ -0,0 +1,36 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {57623C7D-A559-470E-82F1-5855F3D3E968} + + + h;hpp;hxx;hm;inl;inc;xsd + {1425FE66-5130-4EBE-BC59-706EFFACDBAC} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {557E8DA6-D010-47FD-A26B-A6BC0D90716E} + + + inf;inv;inx;mof;mc; + {BD774D78-091B-4F17-86D1-EDA291D0D2F4} + + + + + Driver Files + + + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/general/toaster/toastDrv/kmdf/filter/sideband/filter.c b/general/toaster/toastDrv/kmdf/filter/sideband/filter.c new file mode 100644 index 000000000..d018b58e6 --- /dev/null +++ b/general/toaster/toastDrv/kmdf/filter/sideband/filter.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: + + filter.c + +Abstract: + + This module shows how to a write a sideband filter driver. The driver creates + a control device object, which represents a legacy non-Plug and Play device or + control interface through which a Plug and Play driver receives so-called + "sideband" I/O requests. This control object is not a part of the Plug and Play + and there is one control object for all instances of the device. + The module also shows using a collection to keep track of all device objects. + It is very important to delete the control device object so that driver can + unload when the filter device is deleted. + + An alternative approach is to enumerate a raw PDO for every device + the filter attaches to so that it can provide a direct sideband communication + with the usermode application. The KbFilter driver demonstrates that approach. + + + +Environment: + + Kernel mode + +--*/ + +#include "filter.h" + +// +// Collection object is used to store all the FilterDevice objects so +// that any event callback routine can easily walk thru the list and pick a +// specific instance of the device for filtering. +// +WDFCOLLECTION FilterDeviceCollection; +WDFWAITLOCK FilterDeviceCollectionLock; + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (INIT, DriverEntry) +#pragma alloc_text (PAGE, FilterEvtDeviceAdd) +#pragma alloc_text (PAGE, FilterEvtDeviceContextCleanup) +#endif + + +// +// ControlDevice provides a sideband communication to the filter from +// usermode. This is required if the filter driver is sitting underneath +// another driver that fails custom ioctls defined by the Filter driver. +// Since there is one control-device for all instances of the device the +// filter is attached to, we will store the device handle in a global variable. +// + +WDFDEVICE ControlDevice = NULL; + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (PAGE, FilterEvtIoDeviceControl) +#pragma alloc_text (PAGE, FilterCreateControlDevice) +#pragma alloc_text (PAGE, FilterDeleteControlDevice) +#endif + +_Use_decl_annotations_ +NTSTATUS +DriverEntry( + PDRIVER_OBJECT DriverObject, + 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 SideBand 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)); + } + + // + // Since there is only one control-device for all the instances + // of the physical device, we need an ability to get to particular instance + // of the device in our FilterEvtIoDeviceControlForControl. For that we + // will create a collection object and store filter device objects. + // The collection object has the driver object as a default parent. + // + + status = WdfCollectionCreate(WDF_NO_OBJECT_ATTRIBUTES, + &FilterDeviceCollection); + if (!NT_SUCCESS(status)) + { + KdPrint( ("WdfCollectionCreate failed with status 0x%x\n", status)); + return status; + } + + // + // The wait-lock object has the driver object as a default parent. + // + + status = WdfWaitLockCreate(WDF_NO_OBJECT_ATTRIBUTES, + &FilterDeviceCollectionLock); + if (!NT_SUCCESS(status)) + { + KdPrint( ("WdfWaitLockCreate failed with status 0x%x\n", status)); + return status; + } + + return status; +} + +_Use_decl_annotations_ +NTSTATUS +FilterEvtDeviceAdd( + WDFDRIVER Driver, + 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; + ULONG serialNo; + ULONG returnSize; + + PAGED_CODE (); + + UNREFERENCED_PARAMETER(Driver); + + // + // Get some property of the device you are about to attach and check + // to see if that's the one you are interested. For demonstration + // we will get the UINumber of the device. The bus driver reports the + // serial number as the UINumber. + // + status = WdfFdoInitQueryProperty(DeviceInit, + DevicePropertyUINumber, + sizeof(serialNo), + &serialNo, + &returnSize); + if(!NT_SUCCESS(status)){ + KdPrint(("Failed to get the property of PDO: 0x%p\n", DeviceInit)); + } + + // + // 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); + + // + // We will just register for cleanup notification because we have to + // delete the control-device when the last instance of the device goes + // away. If we don't delete, the driver wouldn't get unloaded automatcially + // by the PNP subsystem. + // + deviceAttributes.EvtCleanupCallback = FilterEvtDeviceContextCleanup; + + // + // 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); + filterExt->SerialNo = serialNo; + + // + // Add this device to the FilterDevice collection. + // + WdfWaitLockAcquire(FilterDeviceCollectionLock, NULL); + // + // WdfCollectionAdd takes a reference on the item object and removes + // it when you call WdfCollectionRemove. + // + status = WdfCollectionAdd(FilterDeviceCollection, device); + if (!NT_SUCCESS(status)) { + KdPrint( ("WdfCollectionAdd failed with status code 0x%x\n", status)); + } + WdfWaitLockRelease(FilterDeviceCollectionLock); + + // + // Create a control device + // + status = FilterCreateControlDevice(device); + if (!NT_SUCCESS(status)) { + KdPrint( ("FilterCreateControlDevice failed with status 0x%x\n", + status)); + // + // Let us not fail AddDevice just because we weren't able to create the + // control device. + // + status = STATUS_SUCCESS; + } + + return status; +} + +#pragma warning(push) +#pragma warning(disable:28118) // this callback will run at IRQL=PASSIVE_LEVEL +_Use_decl_annotations_ +VOID +FilterEvtDeviceContextCleanup( + WDFOBJECT Device + ) +/*++ + +Routine Description: + + EvtDeviceRemove event callback must perform any operations that are + necessary before the specified device is removed. The framework calls + the driver's EvtDeviceRemove callback when the PnP manager sends + an IRP_MN_REMOVE_DEVICE request to the driver stack. + +Arguments: + + Device - Handle to a framework device object. + +Return Value: + + WDF status code + +--*/ +{ + ULONG count; + + PAGED_CODE(); + + KdPrint(("Entered FilterEvtDeviceContextCleanup\n")); + + WdfWaitLockAcquire(FilterDeviceCollectionLock, NULL); + + count = WdfCollectionGetCount(FilterDeviceCollection); + + if(count == 1) + { + // + // We are the last instance. So let us delete the control-device + // so that driver can unload when the FilterDevice is deleted. + // We absolutely have to do the deletion of control device with + // the collection lock acquired because we implicitly use this + // lock to protect ControlDevice global variable. We need to make + // sure another thread doesn't attempt to create while we are + // deleting the device. + // + FilterDeleteControlDevice((WDFDEVICE)Device); + } + + WdfCollectionRemove(FilterDeviceCollection, Device); + + WdfWaitLockRelease(FilterDeviceCollectionLock); +} +#pragma warning(pop) // enable 28118 again + +_Use_decl_annotations_ +NTSTATUS +FilterCreateControlDevice( + WDFDEVICE Device + ) +/*++ + +Routine Description: + + This routine is called to create a control deviceobject so that application + can talk to the filter driver directly instead of going through the entire + device stack. This kind of control device object is useful if the filter + driver is underneath another driver which prevents ioctls not known to it + or if the driver's dispatch routine is owned by some other (port/class) + driver and it doesn't allow any custom ioctls. + + NOTE: Since the control device is global to the driver and accessible to + all instances of the device this filter is attached to, we create only once + when the first instance of the device is started and delete it when the + last instance gets removed. + +Arguments: + + Device - Handle to a filter device object. + +Return Value: + + WDF status code + +--*/ +{ + PWDFDEVICE_INIT pInit = NULL; + WDFDEVICE controlDevice = NULL; + WDF_OBJECT_ATTRIBUTES controlAttributes; + WDF_IO_QUEUE_CONFIG ioQueueConfig; + BOOLEAN bCreate = FALSE; + NTSTATUS status; + WDFQUEUE queue; + DECLARE_CONST_UNICODE_STRING(ntDeviceName, NTDEVICE_NAME_STRING) ; + DECLARE_CONST_UNICODE_STRING(symbolicLinkName, SYMBOLIC_NAME_STRING) ; + + PAGED_CODE(); + + // + // First find out whether any ControlDevice has been created. If the + // collection has more than one device then we know somebody has already + // created or in the process of creating the device. + // + WdfWaitLockAcquire(FilterDeviceCollectionLock, NULL); + + if(WdfCollectionGetCount(FilterDeviceCollection) == 1) { + bCreate = TRUE; + } + + WdfWaitLockRelease(FilterDeviceCollectionLock); + + if(!bCreate) { + // + // Control device is already created. So return success. + // + return STATUS_SUCCESS; + } + + KdPrint(("Creating Control Device\n")); + + // + // + // In order to create a control device, we first need to allocate a + // WDFDEVICE_INIT structure and set all properties. + // + pInit = WdfControlDeviceInitAllocate( + WdfDeviceGetDriver(Device), + &SDDL_DEVOBJ_SYS_ALL_ADM_RWX_WORLD_RW_RES_R + ); + + if (pInit == NULL) { + status = STATUS_INSUFFICIENT_RESOURCES; + goto Error; + } + + // + // Set exclusive to false so that more than one app can talk to the + // control device simultaneously. + // + WdfDeviceInitSetExclusive(pInit, FALSE); + + status = WdfDeviceInitAssignName(pInit, &ntDeviceName); + + if (!NT_SUCCESS(status)) { + goto Error; + } + + // + // Specify the size of device context + // + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&controlAttributes, + CONTROL_DEVICE_EXTENSION); + status = WdfDeviceCreate(&pInit, + &controlAttributes, + &controlDevice); + if (!NT_SUCCESS(status)) { + goto Error; + } + + // + // Create a symbolic link for the control object so that usermode can open + // the device. + // + + status = WdfDeviceCreateSymbolicLink(controlDevice, + &symbolicLinkName); + + if (!NT_SUCCESS(status)) { + goto Error; + } + + // + // Configure the default queue associated with the control device object + // to be Serial so that request passed to EvtIoDeviceControl are serialized. + // + + WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&ioQueueConfig, + WdfIoQueueDispatchSequential); + + ioQueueConfig.EvtIoDeviceControl = FilterEvtIoDeviceControl; + + // + // Framework by default creates non-power managed queues for + // filter drivers. + // + status = WdfIoQueueCreate(controlDevice, + &ioQueueConfig, + WDF_NO_OBJECT_ATTRIBUTES, + &queue // pointer to default queue + ); + if (!NT_SUCCESS(status)) { + goto Error; + } + + // + // Control devices must notify WDF when they are done initializing. I/O is + // rejected until this call is made. + // + WdfControlFinishInitializing(controlDevice); + + ControlDevice = controlDevice; + + return STATUS_SUCCESS; + +Error: + + if (pInit != NULL) { + WdfDeviceInitFree(pInit); + } + + if (controlDevice != NULL) { + // + // Release the reference on the newly created object, since + // we couldn't initialize it. + // + WdfObjectDelete(controlDevice); + controlDevice = NULL; + } + + return status; +} + +_Use_decl_annotations_ +VOID +FilterDeleteControlDevice( + WDFDEVICE Device + ) +/*++ + +Routine Description: + + This routine deletes the control by doing a simple dereference. + +Arguments: + + Device - Handle to a framework filter device object. + +Return Value: + + WDF status code + +--*/ +{ + UNREFERENCED_PARAMETER(Device); + + PAGED_CODE(); + + KdPrint(("Deleting Control Device\n")); + + if (ControlDevice) { + WdfObjectDelete(ControlDevice); + ControlDevice = NULL; + } +} + +#pragma warning(push) +#pragma warning(disable:28118) // this callback will run at IRQL=PASSIVE_LEVEL +_Use_decl_annotations_ +VOID +FilterEvtIoDeviceControl( + WDFQUEUE Queue, + WDFREQUEST Request, + size_t OutputBufferLength, + size_t InputBufferLength, + 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 + +--*/ +{ + ULONG i; + ULONG noItems; + WDFDEVICE hFilterDevice; + PFILTER_EXTENSION filterExt; + + UNREFERENCED_PARAMETER(Queue); + UNREFERENCED_PARAMETER(OutputBufferLength); + UNREFERENCED_PARAMETER(InputBufferLength); + UNREFERENCED_PARAMETER(IoControlCode); + + PAGED_CODE(); + + KdPrint(("Ioctl recieved into filter control object.\n")); + + WdfWaitLockAcquire(FilterDeviceCollectionLock, NULL); + + noItems = WdfCollectionGetCount(FilterDeviceCollection); + + for(i=0; iSerialNo)); + } + + WdfWaitLockRelease(FilterDeviceCollectionLock); + + WdfRequestCompleteWithInformation(Request, STATUS_SUCCESS, 0); +} +#pragma warning(pop) // enable 28118 again + + + diff --git a/general/toaster/toastDrv/kmdf/filter/sideband/filter.h b/general/toaster/toastDrv/kmdf/filter/sideband/filter.h new file mode 100644 index 000000000..74346d9c3 --- /dev/null +++ b/general/toaster/toastDrv/kmdf/filter/sideband/filter.h @@ -0,0 +1,78 @@ +/*++ + +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 sideband filter driver. + +Environment: + + Kernel mode + +--*/ + +#include +#include +#include // for SDDLs +#define NTSTRSAFE_LIB +#include + +#if !defined(_FILTER_H_) +#define _FILTER_H_ + + +#define DRIVERNAME "SideBand.sys: " + + +typedef struct _FILTER_EXTENSION { + ULONG SerialNo; +} FILTER_EXTENSION, *PFILTER_EXTENSION; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(FILTER_EXTENSION, + FilterGetData) + +#define NTDEVICE_NAME_STRING L"\\Device\\ToasterFilter" +#define SYMBOLIC_NAME_STRING L"\\DosDevices\\ToasterFilter" + +typedef struct _CONTROL_DEVICE_EXTENSION { + + PVOID ControlData; // Store your control data here + +} CONTROL_DEVICE_EXTENSION, *PCONTROL_DEVICE_EXTENSION; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(CONTROL_DEVICE_EXTENSION, + ControlGetData) + +DRIVER_INITIALIZE DriverEntry; + +EVT_WDF_DRIVER_DEVICE_ADD FilterEvtDeviceAdd; +EVT_WDF_DRIVER_UNLOAD FilterEvtDriverUnload; +EVT_WDF_DEVICE_CONTEXT_CLEANUP FilterEvtDeviceContextCleanup; +EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL FilterEvtIoDeviceControl; + +_Must_inspect_result_ +_Success_(return==STATUS_SUCCESS) +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS +FilterCreateControlDevice( + _In_ WDFDEVICE Device + ); + +_IRQL_requires_max_(PASSIVE_LEVEL) +VOID +FilterDeleteControlDevice( + _In_ WDFDEVICE Device + ); + +#endif + diff --git a/general/toaster/toastDrv/kmdf/filter/sideband/filter.rc b/general/toaster/toastDrv/kmdf/filter/sideband/filter.rc new file mode 100644 index 000000000..fc6980ecc --- /dev/null +++ b/general/toaster/toastDrv/kmdf/filter/sideband/filter.rc @@ -0,0 +1,12 @@ +#include +#include +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "Toaster Filter Driver with SideBand Control Device" +#define VER_INTERNALNAME_STR DRIVERNAME +#define VER_ORIGINALFILENAME_STR DRIVERNAME + +#include "common.ver" + diff --git a/general/toaster/toastDrv/kmdf/filter/sideband/filter.vcxproj b/general/toaster/toastDrv/kmdf/filter/sideband/filter.vcxproj new file mode 100644 index 000000000..bf2991118 --- /dev/null +++ b/general/toaster/toastDrv/kmdf/filter/sideband/filter.vcxproj @@ -0,0 +1,200 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {5094C704-35B0-4A25-AD30-570C49BCAE82} + $(MSBuildProjectName) + 1 + false + true + Debug + Win32 + {45B5BA9D-00B6-4AC4-BAC8-C9458CEE7190} + + + + 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)\filter.inf + + + + filter + + + filter + + + filter + + + filter + + + + %(PreprocessorDefinitions);IOCTL_INTERFACE=1 + true + Level4 + %(AdditionalIncludeDirectories);..\..\inc + + + + + %(PreprocessorDefinitions);IOCTL_INTERFACE=1 + %(AdditionalIncludeDirectories);..\..\inc + + + %(PreprocessorDefinitions);IOCTL_INTERFACE=1 + %(AdditionalIncludeDirectories);..\..\inc + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\wdmsec.lib + + + + + %(PreprocessorDefinitions);IOCTL_INTERFACE=1 + true + Level4 + %(AdditionalIncludeDirectories);..\..\inc + + + + + %(PreprocessorDefinitions);IOCTL_INTERFACE=1 + %(AdditionalIncludeDirectories);..\..\inc + + + %(PreprocessorDefinitions);IOCTL_INTERFACE=1 + %(AdditionalIncludeDirectories);..\..\inc + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\wdmsec.lib + + + + + %(PreprocessorDefinitions);IOCTL_INTERFACE=1 + true + Level4 + %(AdditionalIncludeDirectories);..\..\inc + + + + + %(PreprocessorDefinitions);IOCTL_INTERFACE=1 + %(AdditionalIncludeDirectories);..\..\inc + + + %(PreprocessorDefinitions);IOCTL_INTERFACE=1 + %(AdditionalIncludeDirectories);..\..\inc + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\wdmsec.lib + + + + + %(PreprocessorDefinitions);IOCTL_INTERFACE=1 + true + Level4 + %(AdditionalIncludeDirectories);..\..\inc + + + + + %(PreprocessorDefinitions);IOCTL_INTERFACE=1 + %(AdditionalIncludeDirectories);..\..\inc + + + %(PreprocessorDefinitions);IOCTL_INTERFACE=1 + %(AdditionalIncludeDirectories);..\..\inc + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\wdmsec.lib + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/general/toaster/toastDrv/kmdf/filter/sideband/filter.vcxproj.Filters b/general/toaster/toastDrv/kmdf/filter/sideband/filter.vcxproj.Filters new file mode 100644 index 000000000..f78f59e44 --- /dev/null +++ b/general/toaster/toastDrv/kmdf/filter/sideband/filter.vcxproj.Filters @@ -0,0 +1,36 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {CDD79DAB-0B3B-443A-9490-7EC7FA5C9B21} + + + h;hpp;hxx;hm;inl;inc;xsd + {DC29ED4D-9D57-4982-AD99-B5099AA50460} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {EA8061CA-59F3-441F-8AD7-74F675F1B50F} + + + inf;inv;inx;mof;mc; + {3EAB2CF5-86A8-4A52-839A-15A3878E8426} + + + + + Driver Files + + + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/general/toaster/toastDrv/kmdf/func/featured/power.c b/general/toaster/toastDrv/kmdf/func/featured/power.c new file mode 100644 index 000000000..5008a047f --- /dev/null +++ b/general/toaster/toastDrv/kmdf/func/featured/power.c @@ -0,0 +1,411 @@ +/*++ + +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" + +#include "trace.h" +// +// This tmh is generated by the WPP Preprocessor +// +#include "power.tmh" + +#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. + +--*/ +{ + PFDO_DATA fdoData; + + UNREFERENCED_PARAMETER(RecentPowerState); + + fdoData = ToasterFdoGetData(Device); + + WppPrintDevice(fdoData->WppRecorderLog, + "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. +--*/ +{ + PFDO_DATA fdoData; + + UNREFERENCED_PARAMETER(PowerState); + + PAGED_CODE(); + + fdoData = ToasterFdoGetData(Device); + + WppPrintDevice(fdoData->WppRecorderLog, + "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. + +--*/ +{ + PFDO_DATA fdoData; + + PAGED_CODE(); + + fdoData = ToasterFdoGetData(Device); + + WppPrintDevice(fdoData->WppRecorderLog, "--> ToasterEvtDeviceArmWakeFromS0\n"); + + WppPrintDevice(fdoData->WppRecorderLog, "<-- 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. + +--*/ +{ + PFDO_DATA fdoData; + + PAGED_CODE(); + + fdoData = ToasterFdoGetData(Device); + + WppPrintDevice(fdoData->WppRecorderLog, "--> ToasterEvtDeviceArmWakeFromSx\n"); + + WppPrintDevice(fdoData->WppRecorderLog, "<-- 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. + +--*/ +{ + PFDO_DATA fdoData; + + fdoData = ToasterFdoGetData(Device); + + WppPrintDevice(fdoData->WppRecorderLog, "--> ToasterEvtDeviceDisarmWakeFromS0\n"); + + WppPrintDevice(fdoData->WppRecorderLog, "<-- 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. + +--*/ +{ + PFDO_DATA fdoData; + + fdoData = ToasterFdoGetData(Device); + + WppPrintDevice(fdoData->WppRecorderLog, "--> ToasterEvtDeviceDisarmWakeFromSx\n"); + + WppPrintDevice(fdoData->WppRecorderLog, "<-- 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 + +--*/ +{ + PFDO_DATA fdoData; + + PAGED_CODE(); + + fdoData = ToasterFdoGetData(Device); + + WppPrintDevice(fdoData->WppRecorderLog, "--> ToasterEvtDeviceWakeFromS0Triggered\n"); + + + WppPrintDevice(fdoData->WppRecorderLog, "<-- 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 + +--*/ +{ + PFDO_DATA fdoData; + + fdoData = ToasterFdoGetData(Device); + + WppPrintDevice(fdoData->WppRecorderLog, "--> ToasterEvtDeviceWakeFromSxTriggered\n"); + + WppPrintDevice(fdoData->WppRecorderLog, "<-- 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/toastDrv/kmdf/func/featured/toaster.c b/general/toaster/toastDrv/kmdf/func/featured/toaster.c new file mode 100644 index 000000000..be826982c --- /dev/null +++ b/general/toaster/toastDrv/kmdf/func/featured/toaster.c @@ -0,0 +1,1038 @@ +/*++ + +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, handle WMI set and query events, fire WMI notification + events. + +Environment: + + Kernel mode + +--*/ + +#include "toaster.h" + +#include "trace.h" +// +// This tmh is generated by the WPP Preprocessor +// +#include "toaster.tmh" + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (INIT, DriverEntry) +#pragma alloc_text (PAGE, ToasterEvtDriverUnload) +#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) +#pragma alloc_text (PAGE, ToasterEvtDeviceSelfManagedIoInit) +#endif + +// +// Used by WMI +// +ULONG DebugLevel = 3; + +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; + RECORDER_CONFIGURE_PARAMS recorderConfigureParams; + WDF_DRIVER_CONFIG config; + + // + // WPP Initialization + // + WPP_INIT_TRACING(DriverObject, RegistryPath); + + // + // The CreateDefaultLog member is set to TRUE by default by + // RECORDER_CONFIGURE_PARAMS_INIT(). + // The DefaultLog is used when you print a WPP trace statement without + // specifying a RECORDER_LOG handle. + // + // You can prevent the DefaultLog from being created by setting this + // property to FALSE. You may want to do this if you plan on creating + // WppRecorder log buffers later on and will not be using the the default log. + // + RECORDER_CONFIGURE_PARAMS_INIT(&recorderConfigureParams); + + WppRecorderConfigure(&recorderConfigureParams); + + // + // The WPP Preprocessor replaces existing KdPrints (see trace.h). All KdPrints, + // will now print to the default WPP Recorder Log. + // + 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 + ); + + // + // We need to register for the EvtDriverUnload event so that we can release + // the WPP resources we just allocated. + // + config.EvtDriverUnload = ToasterEvtDriverUnload; + + // + // 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)); + + // + // EvtDriverUnload callback will not be called, so we need to clean + // up the WPP resources here. + // + WPP_CLEANUP(DriverObject); + } + + return status; +} + +VOID +ToasterEvtDriverUnload( + IN WDFDRIVER Driver + ) +/*++ +Routine Description: + + ToasterEvtDriverUnload will clean up the WPP resources that was allocated + for this driver. + +Arguments: + + Driver - Handle to a framework driver object created in DriverEntry + +--*/ +{ + PDRIVER_OBJECT driverObject; + + PAGED_CODE(); + + KdPrint(("ToasterEvtDriverUnload called.")); + + driverObject = WdfDriverWdmGetDriverObject(Driver); + + WPP_CLEANUP(driverObject); +} + +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; + RECORDER_LOG_CREATE_PARAMS recorderLogCreateParams; + + 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); + + // + // Create a WppRecorder log that we'll use specifically for this device. + // This can help debugging because we will not have other device messages + // intertwined with this one. + // + RECORDER_LOG_CREATE_PARAMS_INIT(&recorderLogCreateParams, + TOASTER_FUNC_DEVICE_LOG_ID); + + status = WppRecorderLogCreate(&recorderLogCreateParams, + &(fdoData->WppRecorderLog)); + if (!NT_SUCCESS(status)) { + KdPrint(("WppRecorderLogCreate failed with Status code 0x%x\n", status)); + return status; + } + + // + // 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)) { + WppPrintDeviceError(fdoData->WppRecorderLog, + "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)) { + + WppPrintDeviceError(fdoData->WppRecorderLog, + "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)) { + WppPrintDeviceError(fdoData->WppRecorderLog, + "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. + // + WppPrintDeviceError(fdoData->WppRecorderLog, + "WdfDeviceAssignSxWakeSettings failed 0x%x\n", + status); + status = STATUS_SUCCESS; + } + + + // + // Finally register all our WMI datablocks with WMI subsystem. + // + status = ToasterWmiRegistration(device); + + // + // Please note that if this event fails or eventually device gets removed + // the framework will automatically take care of deregistering with + // WMI, detaching and deleting the deviceobject and cleaning up other + // resources. Framework does most of the resource cleanup during device + // remove and driver unload. + // + + 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; + + UNREFERENCED_PARAMETER(ResourcesRaw); + + PAGED_CODE(); + + fdoData = ToasterFdoGetData(Device); + + WppPrintDevice(fdoData->WppRecorderLog, "ToasterEvtDevicePrepareHardware called\n"); + + // + // 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: + + WppPrintDevice(fdoData->WppRecorderLog, + "I/O Port: (%x) Length: (%d)\n", + descriptor->u.Port.Start.LowPart, + descriptor->u.Port.Length); + + break; + + case CmResourceTypeMemory: + + WppPrintDevice(fdoData->WppRecorderLog, + "Memory: (%x) Length: (%d)\n", + descriptor->u.Memory.Start.LowPart, + descriptor->u.Memory.Length); + break; + case CmResourceTypeInterrupt: + + WppPrintDevice(fdoData->WppRecorderLog, + "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; + } + + } + + + // + // Fire device arrival event. + // + ToasterFireArrivalEvent(Device); + + 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); + + PAGED_CODE(); + + fdoData = ToasterFdoGetData(Device); + + WppPrintDevice(fdoData->WppRecorderLog, "ToasterEvtDeviceReleaseHardware called\n"); + + // + // 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. + +Arguments: + + Device - Handle to a framework device object. + +Return Value: + + NTSTATUS - Failures will result in the device stack being torn down. + +--*/ +{ + NTSTATUS status; + PFDO_DATA fdoData; + + PAGED_CODE(); + + fdoData = ToasterFdoGetData(Device); + + WppPrintDevice(fdoData->WppRecorderLog, "ToasterEvtDeviceSelfManagedIoInit called\n"); + + // + // We will provide an example on how to get a bus-specific direct + // call interface from a bus driver. + // + status = WdfFdoQueryForInterface(Device, + &GUID_TOASTER_INTERFACE_STANDARD, + (PINTERFACE) &fdoData->BusInterface, + sizeof(TOASTER_INTERFACE_STANDARD), + 1, + NULL);// InterfaceSpecific Data + if(NT_SUCCESS(status)) + { + UCHAR powerlevel; + + // + // Call the direct callback functions to get the property or + // configuration information of the device. + // + (*fdoData->BusInterface.GetCrispinessLevel)(fdoData->BusInterface.InterfaceHeader.Context, + &powerlevel); + (*fdoData->BusInterface.SetCrispinessLevel)(fdoData->BusInterface.InterfaceHeader.Context, 8); + (*fdoData->BusInterface.IsSafetyLockEnabled)(fdoData->BusInterface.InterfaceHeader.Context); + + // + // Provider of this interface may have taken a reference on it. + // So we must release the interface as soon as we are done using it. + // + (*fdoData->BusInterface.InterfaceHeader.InterfaceDereference) + ((PVOID)fdoData->BusInterface.InterfaceHeader.Context); + + + } else { + // + // In this sample, we don't want to fail start just because we weren't + // able to get the direct-call interface. If this driver is loaded on top + // of a bus other than toaster, ToasterGetStandardInterface will return + // an error. + // + status = STATUS_SUCCESS; + } + + return status; +} + + +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; + PAGED_CODE(); + + fdoData = ToasterFdoGetData((WDFDEVICE)Device); + + WppPrintDevice(fdoData->WppRecorderLog, "ToasterEvtDeviceContextCleanup called\n"); + + WppRecorderLogDelete(fdoData->WppRecorderLog); + fdoData->WppRecorderLog = NULL; + + 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); + + PAGED_CODE (); + + // + // Get the device context given the device handle. + // + fdoData = ToasterFdoGetData(Device); + + WppPrintDevice(fdoData->WppRecorderLog, "ToasterEvtDeviceFileCreate %p\n", 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; + PAGED_CODE (); + + fdoData = ToasterFdoGetData(WdfFileObjectGetDevice(FileObject)); + + WppPrintDevice(fdoData->WppRecorderLog, "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 + +--*/ +{ + PFDO_DATA fdoData; + NTSTATUS status; + ULONG_PTR bytesCopied =0; + WDFMEMORY memory; + + UNREFERENCED_PARAMETER(Length); + + PAGED_CODE(); + + fdoData = ToasterFdoGetData(WdfIoQueueGetDevice(Queue)); + + WppPrintDevice(fdoData->WppRecorderLog, + "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; + PFDO_DATA fdoData; + + PAGED_CODE(); + + fdoData = ToasterFdoGetData(WdfIoQueueGetDevice(Queue)); + + WppPrintDevice(fdoData->WppRecorderLog, + "ToasterEvtIoWrite. Request: 0x%p, Queue: 0x%p\n", + Request, + Queue); + // + // 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); + PFDO_DATA fdoData; + + + UNREFERENCED_PARAMETER(OutputBufferLength); + UNREFERENCED_PARAMETER(InputBufferLength); + + PAGED_CODE(); + + fdoData = ToasterFdoGetData(hDevice); + + WppPrintDevice(fdoData->WppRecorderLog, + "ToasterEvtIoDeviceControl called\n"); + + 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/toastDrv/kmdf/func/featured/toaster.mof b/general/toaster/toastDrv/kmdf/func/featured/toaster.mof new file mode 100644 index 000000000..c828eb4bc --- /dev/null +++ b/general/toaster/toastDrv/kmdf/func/featured/toaster.mof @@ -0,0 +1,109 @@ +#PRAGMA AUTORECOVER + +[Dynamic, Provider("WMIProv"), + WMI, + Description("Toaster driver information"), + guid("{BBA21300-6DD3-11d2-B844-00C04FAD5171}"), + locale("MS\\0x409")] +class ToasterDeviceInformation +{ + [key, read] + string InstanceName; + + [read] + boolean Active; + + [WmiDataId(1), + read, + WmiEnum{"0=I8042 Connector" + "1=Serial Connector", + "2=Parallel Connector", + "3=USB Connector" }, + Description("How the toaster is connected to the computer")] + uint32 ConnectorType; + + [WmiDataId(2), + read, + Description("This indicates the capacity in Kilo Watts of the toaster device.")] + uint32 Capacity; + + [WmiDataId(3), + read, + Description("Number of errors that occurred on this device")] + uint32 ErrorCount; + + [WmiDataId(4), + read, + Description("Indicates the number of controls on the toaster device.")] + uint32 Controls; + + [WmiDataId(5), + read, + write, + Description("The DebugPrintLevel property indicates the debug output level of toaster device.")] + uint32 DebugPrintLevel; + + [WmiDataId(6), + read, + Description("The Toaster Model Name.")] + string ModelName; + +}; + + +[WMI, Dynamic, Provider("WMIProv"), + guid("{01CDAFF1-C901-45b4-B359-B5542725E29C}"), + locale("MS\\0x409"), + WmiExpense(1), + Description("Notify Toaster Arrival")] +class ToasterNotifyDeviceArrival : WMIEvent +{ + [key, read] + string InstanceName; + + [read] + boolean Active; + + [read, + Description("Device Model Name"), + WmiDataId(1)] + string ModelName; +}; + + +[WMI, + Dynamic, + Provider("WmiProv"), + Locale("MS\\0x409"), + Description("WMI method") : amended, + guid("CAAE7D9F-ACF7-4737-A4E9-01C29D3FE194")] +class ToasterControl +{ + [key, read] + string InstanceName; + + [read] + boolean Active; + + [WmiDataId(1), + read, + write, + Description("Toaster Control Property")] + uint32 ControlValue; + + [Implemented, WmiMethodId(1)] + void ToasterControl1([in] uint32 InData, + [out] uint32 OutData); + + [Implemented, WmiMethodId(2)] + void ToasterControl2([in] uint32 InData1, + [in] uint32 InData2, + [out] uint32 OutData); + + [Implemented, WmiMethodId(3)] + void ToasterControl3([in] uint32 InData1, + [in] uint32 InData2, + [out] uint32 OutData1, + [out] uint32 OutData2); + +}; \ No newline at end of file diff --git a/general/toaster/toastDrv/kmdf/func/featured/toaster.rc b/general/toaster/toastDrv/kmdf/func/featured/toaster.rc new file mode 100644 index 000000000..64d632dad --- /dev/null +++ b/general/toaster/toastDrv/kmdf/func/featured/toaster.rc @@ -0,0 +1,15 @@ +#include + +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "WDF Toaster Device Driver" +#define VER_INTERNALNAME_STR "wdftoaster.sys" +#define VER_ORIGINALFILENAME_STR "wdftoaster.sys" + +#include "common.ver" + +ToasterWMI MOFDATA toaster.bmf + + diff --git a/general/toaster/toastDrv/kmdf/func/featured/trace.h b/general/toaster/toastDrv/kmdf/func/featured/trace.h new file mode 100644 index 000000000..226f29003 --- /dev/null +++ b/general/toaster/toastDrv/kmdf/func/featured/trace.h @@ -0,0 +1,66 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved + +Module Name: + + trace.h + +Abstract: + + This module contains the required definitions to enable WppRecorder + for the KMDF Toaster driver sample. + +Environment: + + Windows Kernel-Mode Driver Framework (KMDF) + +--*/ + +#pragma once + +// {32134778-E6AA-4089-B5A8-9E58E9EE4687} +#define WPP_CONTROL_GUIDS \ + WPP_DEFINE_CONTROL_GUID( \ + ToasterSampleKMDF, \ + (32134778,E6AA,4089,B5A8,9E58E9EE4687), \ + WPP_DEFINE_BIT(TRACE_FLAG_DRIVER) \ + WPP_DEFINE_BIT(TRACE_FLAG_DEVICE) \ + ) + +#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. +// +// These trace functions, located between "begin_wpp config" and "end_wpp", will define the functions +// to call to print traces. The "begin_wpp config" and "end_wpp" lines must be present or else +// the WPP Preprocessor will not pick up your trace functions. +// +// Notice the double parenthesis in the parameters of the function KdPrint. This is only done to ensure +// backward compatibility with the regular KdPrint. Future functions should not use double parenthesis +// for readability. +// +// The "WPP_FLAGS(-public:);" lines make TMF information appear in the public PDBs so that +// those with the public PDB can still see the traces. Remove these lines if you do not wish to have +// the public viewing the trace messages. +// +// Notice the IFRLOG parameter in WppPrintDevice and WppPrintDeviceError. Adding that parameter here, +// allows traces statements to specify which WppRecorder Log to print to. +// +// begin_wpp config +// +// FUNC KdPrint{LEVEL=TRACE_LEVEL_INFORMATION, FLAGS=TRACE_FLAG_DRIVER}((MSG, ...)); +// WPP_FLAGS(-public:KdPrint); +// +// FUNC WppPrintDevice{LEVEL=TRACE_LEVEL_INFORMATION, FLAGS=TRACE_FLAG_DEVICE}(IFRLOG, MSG, ...); +// WPP_FLAGS(-public:WppPrintDevice); +// +// FUNC WppPrintDeviceError{LEVEL=TRACE_LEVEL_ERROR, FLAGS=TRACE_FLAG_DEVICE}(IFRLOG, MSG, ...); +// WPP_FLAGS(-public:WppPrintDeviceError); +// +// end_wpp +// + + diff --git a/general/toaster/toastDrv/kmdf/func/featured/wdffeatured.inx b/general/toaster/toastDrv/kmdf/func/featured/wdffeatured.inx new file mode 100644 index 000000000..6a256f2c6 --- /dev/null +++ b/general/toaster/toastDrv/kmdf/func/featured/wdffeatured.inx @@ -0,0 +1,120 @@ +;/*++ +; +;Copyright (c) Microsoft Corporation. All rights reserved. +; +;Module Name: +; +; TOASTER.INF +; +;Abstract: +; INF file for installing toaster device drivers +; NOTE: ClassInstall32 section gets executed only if the Toaster +; doesn't exist in the registry - +; HKLM\System\CurrentControlSet\Control\Class\{B85B7C50-6A01-11d2-B841-00C04FAD5171} +; +;--*/ +[Version] +Signature = "$WINDOWS NT$" +Class = TOASTER +ClassGuid = {B85B7C50-6A01-11d2-B841-00C04FAD5171} +Provider = %ProviderName% +DriverVer = 06/16/1999,5.00.2064 +CatalogFile = KmdfSamples.cat + + +[DestinationDirs] +DefaultDestDir = 12 + +; ================= 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 +HKR,,Security,,"D:P(A;;GA;;;SY)(A;;GA;;;BA)(A;;GA;;;LS)" ;Allow generic all access to system, built-in Admin, and Local System. + ;This one overrides the security set by the driver + +[ToasterClassInstallerCopyFiles] +tostrcls.dll + +;***************************************** +; Toaster Device Install Section +;***************************************** + +[Manufacturer] +%StdMfg%=Standard,NT$ARCH$ + +[Standard.NT$ARCH$] +%ToasterDevice.DeviceDesc%=Toaster_Device, {b85b7c50-6a01-11d2-b841-00c04fad5171}\MsToaster + +[Toaster_Device.NT] +CopyFiles=Toaster_Device.NT.Copy + +[Toaster_Device.NT.Copy] +wdffeatured.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 = wdffeatured, %SPSVCINST_ASSOCSERVICE%, wdffeatured_Service_Inst + +[wdffeatured_Service_Inst] +DisplayName = %Toaster.SVCDESC% +ServiceType = 1 ; SERVICE_KERNEL_DRIVER +StartType = 3 ; SERVICE_DEMAND_START +ErrorControl = 1 ; SERVICE_ERROR_NORMAL +ServiceBinary = %12%\wdffeatured.sys + +[SourceDisksNames] +1 = %DiskId1%,,,"" + +[SourceDisksFiles] +wdffeatured.sys = 1,, +tostrcls.dll = 1,, + + +; +;--- Toaster_Device Coinstaller installation ------ +; +[DestinationDirs] +ToasterClassInstallerCopyFiles = 11 +Toaster_Device_CoInstaller_CopyFiles = 11 + +[Toaster_Device.NT.CoInstallers] +AddReg = Toaster_Device_CoInstaller_AddReg +CopyFiles = Toaster_Device_CoInstaller_CopyFiles + +[Toaster_Device_CoInstaller_AddReg] +HKR,,CoInstallers32,0x00010000, "WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll,WdfCoInstaller" + +[Toaster_Device_CoInstaller_CopyFiles] +WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll + +[SourceDisksFiles] +WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll=1 ; make sure the number matches with SourceDisksNames + +[Toaster_Device.NT.Wdf] +KmdfService = wdffeatured, wdffeatured_wdfsect + +[wdffeatured_wdfsect] +KmdfLibraryVersion = $KMDFVERSION$ + +[Strings] +SPSVCINST_ASSOCSERVICE = 0x00000002 +ProviderName = "TODO-Set-Provider" +StdMfg = "(Standard system devices)" +ClassName = "Toaster" +DiskId1 = "Toaster Device Installation Disk #1" +ToasterDevice.DeviceDesc = "WDF Featured Toaster" +Toaster.SVCDESC = "WDF Toaster Featured Device Driver" diff --git a/general/toaster/toastDrv/kmdf/func/featured/wdffeatured.vcxproj b/general/toaster/toastDrv/kmdf/func/featured/wdffeatured.vcxproj new file mode 100644 index 000000000..468b7716b --- /dev/null +++ b/general/toaster/toastDrv/kmdf/func/featured/wdffeatured.vcxproj @@ -0,0 +1,206 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {CA0E203E-4C96-466F-BF19-24F42DFC0C89} + $(MSBuildProjectName) + 1 + false + true + Debug + Win32 + {2EBCE232-7DA0-4C77-9EEA-8DE675F4ADC7} + + + + 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 + ENABLE_WPP_RECORDER=1 + trace.h + + + $(InfArch) + true + .\$(IntDir)\wdffeatured.inf + + + ".\$(IntDir)\toaster.bmf" + + + true + .\$(IntDir)\ToasterMof.h + .\$(IntDir)\htm + true + + + + wdffeatured + + + wdffeatured + + + wdffeatured + + + wdffeatured + + + + %(AdditionalIncludeDirectories);..\..\inc;..\shared + %(PreprocessorDefinitions);TOASTER_FUNC_FEATURED + + + %(AdditionalIncludeDirectories);..\..\inc;..\shared + %(PreprocessorDefinitions);TOASTER_FUNC_FEATURED + + + + + %(AdditionalIncludeDirectories);..\..\inc;..\shared + %(PreprocessorDefinitions);TOASTER_FUNC_FEATURED + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ntstrsafe.lib;$(DDK_LIB_PATH)\wpprecorder.lib + + + + + %(AdditionalIncludeDirectories);..\..\inc;..\shared + %(PreprocessorDefinitions);TOASTER_FUNC_FEATURED + + + %(AdditionalIncludeDirectories);..\..\inc;..\shared + %(PreprocessorDefinitions);TOASTER_FUNC_FEATURED + + + + + %(AdditionalIncludeDirectories);..\..\inc;..\shared + %(PreprocessorDefinitions);TOASTER_FUNC_FEATURED + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ntstrsafe.lib;$(DDK_LIB_PATH)\wpprecorder.lib + + + + + %(AdditionalIncludeDirectories);..\..\inc;..\shared + %(PreprocessorDefinitions);TOASTER_FUNC_FEATURED + + + %(AdditionalIncludeDirectories);..\..\inc;..\shared + %(PreprocessorDefinitions);TOASTER_FUNC_FEATURED + + + + + %(AdditionalIncludeDirectories);..\..\inc;..\shared + %(PreprocessorDefinitions);TOASTER_FUNC_FEATURED + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ntstrsafe.lib;$(DDK_LIB_PATH)\wpprecorder.lib + + + + + %(AdditionalIncludeDirectories);..\..\inc;..\shared + %(PreprocessorDefinitions);TOASTER_FUNC_FEATURED + + + %(AdditionalIncludeDirectories);..\..\inc;..\shared + %(PreprocessorDefinitions);TOASTER_FUNC_FEATURED + + + + + %(AdditionalIncludeDirectories);..\..\inc;..\shared + %(PreprocessorDefinitions);TOASTER_FUNC_FEATURED + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ntstrsafe.lib;$(DDK_LIB_PATH)\wpprecorder.lib + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/general/toaster/toastDrv/kmdf/func/featured/wdffeatured.vcxproj.Filters b/general/toaster/toastDrv/kmdf/func/featured/wdffeatured.vcxproj.Filters new file mode 100644 index 000000000..c37a360d6 --- /dev/null +++ b/general/toaster/toastDrv/kmdf/func/featured/wdffeatured.vcxproj.Filters @@ -0,0 +1,45 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {5AEF704A-4799-414D-BC85-F523B601FF9D} + + + h;hpp;hxx;hm;inl;inc;xsd + {8688F2E4-3E07-4E21-947E-9A0917D3799F} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {D979FABA-F85A-4E60-8402-9E1603B7F223} + + + inf;inv;inx;mof;mc; + {91E1B29C-05DE-4125-9FB5-C19173D157DD} + + + + + Source Files + + + Source Files + + + Source Files + + + + + Driver Files + + + Driver Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/general/toaster/toastDrv/kmdf/func/featured/wmi.c b/general/toaster/toastDrv/kmdf/func/featured/wmi.c new file mode 100644 index 000000000..2dc8775f9 --- /dev/null +++ b/general/toaster/toastDrv/kmdf/func/featured/wmi.c @@ -0,0 +1,922 @@ +/*++ + +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 handles WMI support for the featured WDF toaster driver. + +Environment: + + Kernel mode + +--*/ + +#include "toaster.h" +#include "trace.h" +#include +#include +#include + +// +// This tmh is generated by the WPP Preprocessor +// +#include "wmi.tmh" + +static +NTSTATUS +GetDeviceFriendlyName( + _In_ WDFDEVICE Device, + _Out_ WDFMEMORY* DeviceName + ); + +static +ULONG +ToasterHelperFunction1( + _In_ ULONG InData + ); + +static +ULONG +ToasterHelperFunction2( + _In_ ULONG InData1, + _In_ ULONG InData2 + ); + +static +VOID +ToasterHelperFunction3( + _In_ ULONG InData1, + _In_ ULONG InData2, + _Out_ PULONG OutData1, + _Out_ PULONG OutData2 + ); + + +#define ToasterDeviceInformation_SIZE FIELD_OFFSET(ToasterDeviceInformation, VariableData) + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(ToasterDeviceInformation, ToasterWmiGetData) +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(ToasterControl, ToasterWmiGetControlData) + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, ToasterWmiRegistration) +#pragma alloc_text(PAGE, EvtWmiInstanceStdDeviceDataQueryInstance) +#pragma alloc_text(PAGE, EvtWmiInstanceStdDeviceDataSetInstance) +#pragma alloc_text(PAGE, EvtWmiInstanceStdDeviceDataSetItem) +#pragma alloc_text(PAGE, EvtWmiInstanceToasterControlQueryInstance) +#pragma alloc_text(PAGE, EvtWmiInstanceToasterControlExecuteMethod) +#pragma alloc_text(PAGE, EvtWmiInstanceToasterControlSetInstance) +#pragma alloc_text(PAGE, ToasterHelperFunction1) +#pragma alloc_text(PAGE, ToasterHelperFunction2) +#pragma alloc_text(PAGE, ToasterHelperFunction3) +#endif + +// +// Global debug error level +// + +extern ULONG DebugLevel; + +NTSTATUS +ToasterWmiRegistration( + _In_ WDFDEVICE Device + ) + +/*++ + +Routine Description + + Registers with WMI as a data provider for this instance of the device. + +Arguments: + + Device - The Framework device object for which the WMI provider instances + are to be created and registered. This device object will be the parent + object of the new WMI instance objects. + +Return Value: + + NT Status code. + +--*/ + +{ + NTSTATUS status; + PFDO_DATA fdoData; + PToasterDeviceInformation pData; + PToasterControl controlData; + + WDFWMIINSTANCE instance; + WDF_OBJECT_ATTRIBUTES woa; + WDF_WMI_PROVIDER_CONFIG providerConfig; + WDF_WMI_INSTANCE_CONFIG instanceConfig; + DECLARE_CONST_UNICODE_STRING(mofResourceName, MOFRESOURCENAME); + + PAGED_CODE(); + + fdoData = ToasterFdoGetData(Device); + + // + // Register the MOF resource names of any customized WMI data providers + // that are not defined in wmicore.mof. + // + status = WdfDeviceAssignMofResourceName(Device, &mofResourceName); + if (!NT_SUCCESS(status)) { + + WppPrintDeviceError(fdoData->WppRecorderLog, + "[Toaster] Status = 0x%08x, WdfDeviceAssignMofResourceName failed\n", + status); + return status; + } + + // + // Initialize the config structures for the Provider and the Instance and + // define event callback functions that support a WMI client's request to + // access the driver's WMI data blocks. + // + + WDF_WMI_PROVIDER_CONFIG_INIT(&providerConfig, &ToasterDeviceInformation_GUID); + + // + // Specify minimum expected buffer size for query and set instance requests. + // Since the query block size is different than the set block size, set it + // to zero and manually check for the buffer size for each operation. + // + providerConfig.MinInstanceBufferSize = 0; + + // + // The WDFWMIPROVIDER handle is needed if multiple instances for the provider + // has to be created or if the instances have to be created sometime after + // the provider is created. In case below, the provider handle is not needed + // because only one instance is needed and can be created when the provider + // is created. + // + WDF_WMI_INSTANCE_CONFIG_INIT_PROVIDER_CONFIG(&instanceConfig, &providerConfig); + + // + // Create the Provider object as part of the Instance creation call by setting + // the Register value in the Instance Config to TRUE. This eliminates the + // need to call WdfWmiProviderRegister. + // + instanceConfig.Register = TRUE; + + instanceConfig.EvtWmiInstanceQueryInstance = EvtWmiInstanceStdDeviceDataQueryInstance; + instanceConfig.EvtWmiInstanceSetInstance = EvtWmiInstanceStdDeviceDataSetInstance; + instanceConfig.EvtWmiInstanceSetItem = EvtWmiInstanceStdDeviceDataSetItem; + + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&woa, ToasterDeviceInformation); + + // + // Create the WMI instance object for this data block. + // + status = WdfWmiInstanceCreate(Device, + &instanceConfig, + &woa, + &instance); + if (!NT_SUCCESS(status)) { + + WppPrintDeviceError(fdoData->WppRecorderLog, + "[Toaster] Status = 0x%08x, WdfWmiInstanceCreate failed\n", + status); + return status; + } + + pData = ToasterWmiGetData(instance); + + pData->ConnectorType = TOASTER_WMI_STD_USB; + pData->Capacity = 2000; + pData->ErrorCount = 0; + pData->Controls = 5; + pData->DebugPrintLevel = DebugLevel; + + WDF_WMI_PROVIDER_CONFIG_INIT(&providerConfig, &TOASTER_NOTIFY_DEVICE_ARRIVAL_EVENT); + providerConfig.Flags = WdfWmiProviderEventOnly; + + // + // Specify minimum expected buffer size for query and set instance requests. + // Since the query block size is different than the set block size, set it + // to zero and manually check for the buffer size for each operation. + // + providerConfig.MinInstanceBufferSize = 0; + + WDF_WMI_INSTANCE_CONFIG_INIT_PROVIDER_CONFIG(&instanceConfig, &providerConfig); + instanceConfig.Register = TRUE; + + // + // Create the WMI instance object for this data block. + // + status = WdfWmiInstanceCreate(Device, + &instanceConfig, + WDF_NO_OBJECT_ATTRIBUTES, + &fdoData->WmiDeviceArrivalEvent); + + if (!NT_SUCCESS(status)) { + + WppPrintDeviceError(fdoData->WppRecorderLog, + "[Toaster] Status = 0x%08x, WdfWmiInstanceCreate failed\n", + status); + return status; + } + + + // + // Register the Toaster Control class. + // + + // + // Initialize the config structures for the Provider and the Instance and + // define event callback functions that support a WMI client's request to + // access the driver's WMI data blocks. + // + + WDF_WMI_PROVIDER_CONFIG_INIT(&providerConfig, &ToasterControl_GUID); + + // + // Specify minimum expected buffer size for query and set instance requests. + // + providerConfig.MinInstanceBufferSize = ToasterControl_SIZE; + + // + // The WDFWMIPROVIDER handle is needed if multiple instances for the provider + // has to be created or if the instances have to be created sometime after + // the provider is created. In case below, the provider handle is not needed + // because only one instance is needed and can be created when the provider + // is created. + // + WDF_WMI_INSTANCE_CONFIG_INIT_PROVIDER_CONFIG(&instanceConfig, &providerConfig); + + // + // Create the Provider object as part of the Instance creation call by setting + // the Register value in the Instance Config to TRUE. This eliminates the + // need to call WdfWmiProviderRegister. + // + instanceConfig.Register = TRUE; + + instanceConfig.EvtWmiInstanceQueryInstance = EvtWmiInstanceToasterControlQueryInstance; + instanceConfig.EvtWmiInstanceSetInstance = EvtWmiInstanceToasterControlSetInstance; + instanceConfig.EvtWmiInstanceSetItem = EvtWmiInstanceToasterControlSetItem; + instanceConfig.EvtWmiInstanceExecuteMethod = EvtWmiInstanceToasterControlExecuteMethod; + + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&woa, ToasterControl); + + // + // Create the WMI instance object for this data block. + // + status = WdfWmiInstanceCreate(Device, + &instanceConfig, + &woa, + &instance); + if (!NT_SUCCESS(status)) { + + WppPrintDeviceError(fdoData->WppRecorderLog, + "[Toaster] Status = 0x%08x, WdfWmiInstanceCreate failed\n", + status); + return status; + } + + controlData = ToasterWmiGetControlData(instance); + controlData->ControlValue = 25; + + return status; +} + +// +// WMI System Call back functions +// + +NTSTATUS +EvtWmiInstanceStdDeviceDataQueryInstance( + _In_ WDFWMIINSTANCE WmiInstance, + _In_ ULONG OutBufferSize, + _Out_writes_bytes_to_(OutBufferSize, *BufferUsed) PVOID OutBuffer, + _Out_ PULONG BufferUsed + ) +{ + PUCHAR pBuf; + ULONG size; + UNICODE_STRING string; + NTSTATUS status; + + PAGED_CODE(); + + string.Buffer = L"Aishwarya\0\0"; + string.Length = (USHORT) (wcslen(string.Buffer) + 1) * sizeof(WCHAR); + string.MaximumLength = string.Length + sizeof(UNICODE_NULL); + + // + // A USHORT value is needed to contain the length of the buffer in the data + // + size = ToasterDeviceInformation_SIZE + string.Length + sizeof(USHORT); + + *BufferUsed = size; + + if (OutBufferSize < size) { + return STATUS_BUFFER_TOO_SMALL; + } + + pBuf = (PUCHAR) OutBuffer; + + // + // Copy the structure information + // + RtlCopyMemory(pBuf, + ToasterWmiGetData(WmiInstance), + ToasterDeviceInformation_SIZE); + + pBuf += ToasterDeviceInformation_SIZE; + + // + // Copy the string. Put length of string ahead of string. + // + status = WDF_WMI_BUFFER_APPEND_STRING(pBuf, + size - ToasterDeviceInformation_SIZE, + &string, + &size); + + return status; +} + + +NTSTATUS +EvtWmiInstanceStdDeviceDataSetInstance( + _In_ WDFWMIINSTANCE WmiInstance, + _In_ ULONG InBufferSize, + _In_reads_bytes_(InBufferSize) PVOID InBuffer + ) +{ + PAGED_CODE(); + + if (InBufferSize < (ULONG)ToasterDeviceInformation_SIZE) { + return STATUS_BUFFER_TOO_SMALL; + } + + // + // We will update only writable elements. + // + DebugLevel = ToasterWmiGetData(WmiInstance)->DebugPrintLevel = + ((PToasterDeviceInformation)InBuffer)->DebugPrintLevel; + + return STATUS_SUCCESS; +} + + +NTSTATUS +EvtWmiInstanceStdDeviceDataSetItem( + _In_ WDFWMIINSTANCE WmiInstance, + _In_ ULONG DataItemId, + _In_ ULONG InBufferSize, + _In_reads_bytes_(InBufferSize) PVOID InBuffer + ) +{ + PAGED_CODE(); + + if (DataItemId == ToasterDeviceInformation_DebugPrintLevel_ID) { + if (InBufferSize < sizeof(ULONG)) { + return STATUS_BUFFER_TOO_SMALL; + } + + DebugLevel = ToasterWmiGetData(WmiInstance)->DebugPrintLevel = + *((PULONG)InBuffer); + + return STATUS_SUCCESS; + + } else { + return STATUS_WMI_READ_ONLY; + } +} + + +NTSTATUS +ToasterFireArrivalEvent( + _In_ WDFDEVICE Device + ) +{ + WDFMEMORY memory; + PWNODE_SINGLE_INSTANCE wnode; + ULONG wnodeSize; + ULONG wnodeDataBlockSize; + ULONG wnodeInstanceNameSize; + ULONG size; + ULONG length; + UNICODE_STRING deviceName; + UNICODE_STRING modelName; + NTSTATUS status; + PFDO_DATA fdoData; + + fdoData = ToasterFdoGetData(Device); + + // + // *NOTE* + // WdfWmiFireEvent only fires single instance events at the moment so + // continue to use this method of firing events + // *NOTE* + // + + RtlInitUnicodeString(&modelName, L"Sonali\0\0"); + // + // Get the device name. + // + status = GetDeviceFriendlyName(Device, &memory); + + if (!NT_SUCCESS(status)) { + return status; + } + + RtlInitUnicodeString(&deviceName, (PWSTR) WdfMemoryGetBuffer(memory, NULL)); + + // + // Determine the amount of wnode information we need. + // + wnodeSize = sizeof(WNODE_SINGLE_INSTANCE); + wnodeInstanceNameSize = deviceName.Length + sizeof(USHORT); + wnodeDataBlockSize = modelName.Length + sizeof(USHORT); + + size = wnodeSize + wnodeInstanceNameSize + wnodeDataBlockSize; + + // + // Allocate memory for the WNODE from NonPagedPool + // + wnode = ExAllocatePoolWithTag(NonPagedPool, size, TOASTER_POOL_TAG); + + if (NULL != wnode) { + RtlZeroMemory(wnode, size); + + wnode->WnodeHeader.BufferSize = size; + wnode->WnodeHeader.ProviderId = + IoWMIDeviceObjectToProviderId( + WdfDeviceWdmGetDeviceObject(Device)); + wnode->WnodeHeader.Version = 1; + KeQuerySystemTime(&wnode->WnodeHeader.TimeStamp); + + RtlCopyMemory(&wnode->WnodeHeader.Guid, + &TOASTER_NOTIFY_DEVICE_ARRIVAL_EVENT, + sizeof(GUID)); + + // + // *NOTE* + // WdfWmiFireEvent only fires single instance events at the moment so + // continue to use this method of firing events + // *NOTE* + // + + // + // Set flags to indicate that you are creating dynamic instance names. + // The reason we chose to do dynamic instance is becuase we can fire + // the events anytime. If we do static instance names, we can only + // fire events after WMI queries for IRP_MN_REGINFO, which happens + // after the device has been started. Another point to note is that if you + // are firing an event after the device is started, you should + // check whether the event is enabled, because that + // indicates that someone is interested in receiving the event. + // Why fire an event when nobody is interested and waste + // system resources? + // + + wnode->WnodeHeader.Flags = WNODE_FLAG_EVENT_ITEM | + WNODE_FLAG_SINGLE_INSTANCE; + + wnode->OffsetInstanceName = wnodeSize; + wnode->DataBlockOffset = wnodeSize + wnodeInstanceNameSize; + wnode->SizeDataBlock = wnodeDataBlockSize; + + // + // Write the instance name + // + size -= wnodeSize; + status = WDF_WMI_BUFFER_APPEND_STRING( + WDF_PTR_ADD_OFFSET(wnode, wnode->OffsetInstanceName), + size, + &deviceName, + &length + ); + + // + // Size was precomputed, this should never fail + // + ASSERT(NT_SUCCESS(status)); + + + // + // Write the data, which is the model name as a string + // + size -= wnodeInstanceNameSize; + WDF_WMI_BUFFER_APPEND_STRING( + WDF_PTR_ADD_OFFSET(wnode, wnode->DataBlockOffset), + size, + &modelName, + &length + ); + + // + // Size was precomputed, this should never fail + // + ASSERT(NT_SUCCESS(status)); + + // + // Indicate the event to WMI. WMI will take care of freeing + // the WMI struct back to pool. + // + status = IoWMIWriteEvent(wnode); + + if (!NT_SUCCESS(status)) { + WppPrintDeviceError(fdoData->WppRecorderLog, + "IoWMIWriteEvent failed %x\n", + status); + ExFreePool(wnode); + } + } + else { + status = STATUS_INSUFFICIENT_RESOURCES; + } + + // + // Free the memory allocated by GetDeviceFriendlyName function. + // + WdfObjectDelete(memory); + + return status; +} + + +NTSTATUS +GetDeviceFriendlyName( + _In_ WDFDEVICE Device, + _Out_ WDFMEMORY* DeviceName + ) + +/*++ + +Routine Description: + + Return the friendly name associated with the given device object. + +Arguments: + +Return Value: + + NT status + +--*/ + +{ + NTSTATUS status; + PFDO_DATA fdoData; + + fdoData = ToasterFdoGetData(Device); + + // + // First get the length of the string. If the FriendlyName + // is not there then get the lenght of device description. + // + status = WdfDeviceAllocAndQueryProperty(Device, + DevicePropertyFriendlyName, + NonPagedPool, + WDF_NO_OBJECT_ATTRIBUTES, + DeviceName); + + if (!NT_SUCCESS(status) && status != STATUS_INSUFFICIENT_RESOURCES) { + status = WdfDeviceAllocAndQueryProperty(Device, + DevicePropertyDeviceDescription, + NonPagedPool, + WDF_NO_OBJECT_ATTRIBUTES, + DeviceName); + + } + + if (!NT_SUCCESS(status)) { + WppPrintDeviceError(fdoData->WppRecorderLog, + "WdfDeviceQueryProperty returned %x\n", + status); + } + + return status; +} + + +NTSTATUS +EvtWmiInstanceToasterControlQueryInstance( + _In_ WDFWMIINSTANCE WmiInstance, + _In_ ULONG OutBufferSize, + _Out_writes_bytes_to_(OutBufferSize, *BufferUsed) PVOID OutBuffer, + _Out_ PULONG BufferUsed + ) + +/*++ + +Routine Description: + + This is the callback routine for the WMI Query irp on the Instance representing + the ToasterControl Class. This method copies the ToasterControl Class data into + the given buffer. + +Arguments: + + WmiInstance - The handle to the WMI instance object. + + OutBufferSize - The size (in bytes) of the output buffer into which the + instance data is to be copied. + + OutBuffer - Pointer to the output buffer. + + BufferUsed - Pointer to the location that receives the number of bytes that + were copied into the output buffer. + +Return Value: + + NT Status code. + +--*/ + +{ + UNREFERENCED_PARAMETER(OutBufferSize); + PAGED_CODE(); + + // + // Since the minimum buffer size required for querying the instance data was + // already specified during the WMI instance setup, the Framework will make + // sure that the incoming buffer size is large enough for this instance query. + // There is no need to check the size of the given buffer again. The instance + // information can be copied directly to the given buffer. + // + _Analysis_assume_(ToasterControl_SIZE <= OutBufferSize); + RtlCopyMemory(OutBuffer, + ToasterWmiGetControlData(WmiInstance), + ToasterControl_SIZE); + *BufferUsed = ToasterControl_SIZE; + + return STATUS_SUCCESS; +} + + +NTSTATUS +EvtWmiInstanceToasterControlSetInstance( + _In_ WDFWMIINSTANCE WmiInstance, + _In_ ULONG InBufferSize, + _In_reads_bytes_(InBufferSize) PVOID InBuffer + ) + +/*++ + +Routine Description: + + This is the callback routine for the WMI Set instance information irp on the + Instance representing the ToasterControl Class. This method reads the data + from the input buffer and updates the writable elements of the Instance. + +Arguments: + + WmiInstance - The handle to the WMI instance object. + + InBufferSize - The size (in bytes) of the input buffer. + + InBuffer - Pointer to the input buffer containing the data for the instance + updates. + +Return Value: + + NT Status code. + +--*/ + +{ + UNREFERENCED_PARAMETER(InBufferSize); + PAGED_CODE(); + + // + // Since the minimum buffer size required for setting the instance data was + // already specified during the WMI instance setup, the Framework will make + // sure that the incoming buffer size is large enough for this set requrest. + // There is no need to check the size of the given buffer again. The instance + // information can be copied directly from the given buffer. Also, note that + // only the wriable elements need to be copied. + // + ToasterWmiGetControlData(WmiInstance)->ControlValue = ((PToasterControl)InBuffer)->ControlValue; + + return STATUS_SUCCESS; +} + + +NTSTATUS +EvtWmiInstanceToasterControlSetItem( + _In_ WDFWMIINSTANCE WmiInstance, + _In_ ULONG DataItemId, + _In_ ULONG InBufferSize, + _In_reads_bytes_(InBufferSize) PVOID InBuffer + ) +{ + if (DataItemId == ToasterControl_ControlValue_ID) { + if (InBufferSize < sizeof(ULONG)) { + return STATUS_BUFFER_TOO_SMALL; + } + + ToasterWmiGetControlData(WmiInstance)->ControlValue = *((PULONG)InBuffer); + return STATUS_SUCCESS; + + } else { + return STATUS_WMI_READ_ONLY; + } +} + + +NTSTATUS +EvtWmiInstanceToasterControlExecuteMethod( + _In_ WDFWMIINSTANCE WmiInstance, + _In_ ULONG MethodId, + _In_ ULONG InBufferSize, + _In_ ULONG OutBufferSize, + _When_(InBufferSize >= OutBufferSize, _Inout_updates_bytes_(InBufferSize)) + _When_(InBufferSize < OutBufferSize, _Inout_updates_bytes_(OutBufferSize)) + PVOID Buffer, + _Out_ PULONG BufferUsed + ) + +/*++ + +Routine Description: + + This routine handles the Execute Method WMI request to the Toaster device. + +Arguments: + + WmiInstance - The handle to the WMI instance object. + + MethodId - The method id of the method in ToasterControl class to execute. + + InBufferSize - The size (in bytes) of the input buffer from which the input + parameter values are read. + + OutBufferSize - The size (in bytes) of the output buffer into which the + instance data is to be copied. + + Buffer - Pointer to the buffer used for both input and output of data. + + BufferUsed - Pointer to the location that receives the number of bytes that + were copied into the output buffer. + +Return Value: + + NT Status code. + +--*/ + +{ + NTSTATUS status = STATUS_SUCCESS; + + UNREFERENCED_PARAMETER(WmiInstance); + PAGED_CODE(); + + switch (MethodId) { + case ToasterControl1: + { + PToasterControl1_IN InBuffer = (PToasterControl1_IN) Buffer; + PToasterControl1_OUT OutBuffer = (PToasterControl1_OUT)Buffer; + + // + // Check the size of the input and output buffers. + // + if (InBufferSize < ToasterControl1_IN_SIZE) { + status = STATUS_INVALID_PARAMETER_MIX; + break; + } + + if (OutBufferSize < ToasterControl1_OUT_SIZE) { + status = STATUS_BUFFER_TOO_SMALL; + break; + } + + // + // Call the helper function for the Toaster Method 1. + // + OutBuffer->OutData = ToasterHelperFunction1(InBuffer->InData); + + // + // Update the size of the output buffer used. + // + *BufferUsed = ToasterControl1_OUT_SIZE; + + } + break; + + case ToasterControl2: + { + PToasterControl2_IN InBuffer = (PToasterControl2_IN) Buffer; + PToasterControl2_OUT OutBuffer = (PToasterControl2_OUT)Buffer; + + // + // Check the size of the input and output buffers. + // + if (InBufferSize < ToasterControl2_IN_SIZE) { + status = STATUS_INVALID_PARAMETER_MIX; + break; + } + + if (OutBufferSize < ToasterControl2_OUT_SIZE) { + status = STATUS_BUFFER_TOO_SMALL; + break; + } + + // + // Call the helper function for the Toaster Method 2. + // + OutBuffer->OutData = ToasterHelperFunction2(InBuffer->InData1, InBuffer->InData2); + + // + // Update the size of the output buffer used. + // + *BufferUsed = ToasterControl2_OUT_SIZE; + + } + break; + + case ToasterControl3: + { + PToasterControl3_IN InBuffer = (PToasterControl3_IN) Buffer; + PToasterControl3_OUT OutBuffer = (PToasterControl3_OUT)Buffer; + + // + // Check the size of the input and output buffers. + // + if (InBufferSize < ToasterControl3_IN_SIZE) { + status = STATUS_INVALID_PARAMETER_MIX; + break; + } + + if (OutBufferSize < ToasterControl3_OUT_SIZE) { + status = STATUS_BUFFER_TOO_SMALL; + break; + } + + // + // Call the helper function for the Toaster Method 3. + // + ToasterHelperFunction3(InBuffer->InData1, + InBuffer->InData2, + &(OutBuffer->OutData1), + &(OutBuffer->OutData2)); + + // + // Update the size of the output buffer used. + // + *BufferUsed = ToasterControl3_OUT_SIZE; + + } + break; + + default: + status = STATUS_WMI_ITEMID_NOT_FOUND; + break; + } + + return status; +} + + +ULONG +ToasterHelperFunction1( + _In_ ULONG InData + ) +{ + PAGED_CODE(); + + return (InData + 1); +} + + +ULONG +ToasterHelperFunction2( + _In_ ULONG InData1, + _In_ ULONG InData2 + ) +{ + PAGED_CODE(); + + return (InData1 + InData2); +} + + +VOID +ToasterHelperFunction3( + _In_ ULONG InData1, + _In_ ULONG InData2, + _Out_ PULONG OutData1, + _Out_ PULONG OutData2 + ) +{ + PAGED_CODE(); + + *OutData1 = InData1 + 1; + *OutData2 = InData2 + 1; + + return; +} + diff --git a/general/toaster/toastDrv/kmdf/func/shared/toaster.h b/general/toaster/toastDrv/kmdf/func/shared/toaster.h new file mode 100644 index 000000000..4dde5c420 --- /dev/null +++ b/general/toaster/toastDrv/kmdf/func/shared/toaster.h @@ -0,0 +1,134 @@ +/*++ + +Copyright (c) 1990-2000 Microsoft Corporation All Rights Reserved + +Module Name: + + Toaster.h + +Abstract: + + Header file for the toaster driver modules. + +Environment: + + Kernel mode + +--*/ + + +#if !defined(_TOASTER_H_) +#define _TOASTER_H_ + +#include +#include + +#define NTSTRSAFE_LIB +#include + +#include "wmilib.h" +#include +#include "..\inc\driver.h" +#include "..\inc\public.h" + +// For Featured driver only +#ifdef TOASTER_FUNC_FEATURED +#include +#endif // TOASTER_FUNC_FEATURED + +#define TOASTER_POOL_TAG (ULONG) 'saoT' + +#define MOFRESOURCENAME L"ToasterWMI" + +#define TOASTER_FUNC_DEVICE_LOG_ID "ToasterDevice" +// +// The device extension for the device object +// +typedef struct _FDO_DATA +{ + + WDFWMIINSTANCE WmiDeviceArrivalEvent; + + BOOLEAN WmiPowerDeviceEnableRegistered; + + TOASTER_INTERFACE_STANDARD BusInterface; + +// For Featured driver only +#ifdef TOASTER_FUNC_FEATURED + RECORDER_LOG WppRecorderLog; +#endif // TOASTER_FUNC_FEATURED + +} 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_UNLOAD ToasterEvtDriverUnload; + +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/toastDrv/kmdf/func/simple/toaster.c b/general/toaster/toastDrv/kmdf/func/simple/toaster.c new file mode 100644 index 000000000..6ed215dbd --- /dev/null +++ b/general/toaster/toastDrv/kmdf/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/toastDrv/kmdf/func/simple/wdfsimple.inx b/general/toaster/toastDrv/kmdf/func/simple/wdfsimple.inx new file mode 100644 index 000000000..9d07ff93f --- /dev/null +++ b/general/toaster/toastDrv/kmdf/func/simple/wdfsimple.inx @@ -0,0 +1,114 @@ +;/*++ +; +;Copyright (c) Microsoft Corporation. All rights reserved. +; +;Module Name: +; +; SIMPLE.INF +; +;Abstract: +; INF file for installing toaster device drivers. This is an +; extremely simple form of INF. This one uses one of the system +; defined icon for its class instead of one the provided by the +; toaster class installer DLL. To test this INF, make sure +; the toaster class is not previously installed on the system. +; If it is installed, you must remove the existing Toaster class key +; (HKLM\System\CurrentControlSet\Control\Class\{B85B7C50-6A01-11d2-B841-00C04FAD5171}) +; from the registry. Otherwise the setup ignores the entire +; [ClassInstall32] section if it finds the toaster class guid +; in the registy. +; +;--*/ + +[Version] +Signature="$WINDOWS NT$" +Class=TOASTER +ClassGuid={B85B7C50-6A01-11d2-B841-00C04FAD5171} +Provider=%ProviderName% +DriverVer=06/16/1999,5.00.2064 +CatalogFile=KmdfSamples.cat + +[DestinationDirs] +DefaultDestDir = 12 + +; ================= Class section ===================== + +[ClassInstall32] +Addreg=ToasterClassReg + +[ToasterClassReg] +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)(A;;GA;;;LS)" ;Allow generic all access to system, built-in Admin, and Local System. + ;This one overrides the security set by the driver + +;***************************************** +; Toaster Device Install Section +;***************************************** + +[Manufacturer] +%StdMfg%=Standard,NT$ARCH$ + +[Standard.NT$ARCH$] +%WdfSimpleDevice.DeviceDesc%=Toaster_Device, {b85b7c50-6a01-11d2-b841-00c04fad5171}\MsToaster + +[Toaster_Device.NT] +CopyFiles=Toaster_Device.NT.Copy + +[Toaster_Device.NT.Copy] +wdfsimple.sys + +;-------------- Service installation + +[Toaster_Device.NT.Services] +AddService = wdfsimple, %SPSVCINST_ASSOCSERVICE%, wdfsimple_Service_Inst + +[wdfsimple_Service_Inst] +DisplayName = %WDFSIMPLE.SVCDESC% +ServiceType = 1 ; SERVICE_KERNEL_DRIVER +StartType = 3 ; SERVICE_DEMAND_START +ErrorControl = 1 ; SERVICE_ERROR_NORMAL +ServiceBinary = %12%\wdfsimple.sys + +[SourceDisksNames] +1 = %DiskId1%,,,"" + +[SourceDisksFiles] +wdfsimple.sys = 1,, + + +; +;--- Toaster_Device WDF Coinstaller installation ------ +; +[DestinationDirs] +Toaster_Device_CoInstaller_CopyFiles = 11 + +[Toaster_Device.NT.CoInstallers] +AddReg=Toaster_Device_CoInstaller_AddReg +CopyFiles=Toaster_Device_CoInstaller_CopyFiles + +[Toaster_Device_CoInstaller_AddReg] +HKR,,CoInstallers32,0x00010000, "WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll,WdfCoInstaller" + +[Toaster_Device_CoInstaller_CopyFiles] +WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll + +[SourceDisksFiles] +WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll=1 ; make sure the number matches with SourceDisksNames + +[Toaster_Device.NT.Wdf] +KmdfService = wdfsimple, wdfsimple_wdfsect +[wdfsimple_wdfsect] +KmdfLibraryVersion = $KMDFVERSION$ + + + +[Strings] +SPSVCINST_ASSOCSERVICE = 0x00000002 +ProviderName = "TODO-Set-Provider" +StdMfg = "(Standard system devices)" +ClassName = "Toaster" +DiskId1 = "Toaster Device Installation Disk #1" +WdfSimpleDevice.DeviceDesc = "WDF Simple Toaster (No Class Installer)" +WDFSIMPLE.SVCDESC = "WDF Simple Toaster Device Driver" diff --git a/general/toaster/toastDrv/kmdf/func/simple/wdfsimple.vcxproj b/general/toaster/toastDrv/kmdf/func/simple/wdfsimple.vcxproj new file mode 100644 index 000000000..921867015 --- /dev/null +++ b/general/toaster/toastDrv/kmdf/func/simple/wdfsimple.vcxproj @@ -0,0 +1,167 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {FB919684-BF55-4E3D-95FA-CBCB7B0D8759} + $(MSBuildProjectName) + 1 + false + true + Debug + Win32 + {B609B6B5-97C5-4918-AD38-A4E7EE1B43BD} + + + + 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)\wdfsimple.inf + + + + wdfsimple + + + wdfsimple + + + wdfsimple + + + wdfsimple + + + + %(AdditionalIncludeDirectories);..\..\inc;..\shared + + + %(AdditionalIncludeDirectories);..\..\inc;..\shared + + + + + %(AdditionalIncludeDirectories);..\..\inc;..\shared + + + + + %(AdditionalIncludeDirectories);..\..\inc;..\shared + + + %(AdditionalIncludeDirectories);..\..\inc;..\shared + + + + + %(AdditionalIncludeDirectories);..\..\inc;..\shared + + + + + %(AdditionalIncludeDirectories);..\..\inc;..\shared + + + %(AdditionalIncludeDirectories);..\..\inc;..\shared + + + + + %(AdditionalIncludeDirectories);..\..\inc;..\shared + + + + + %(AdditionalIncludeDirectories);..\..\inc;..\shared + + + %(AdditionalIncludeDirectories);..\..\inc;..\shared + + + + + %(AdditionalIncludeDirectories);..\..\inc;..\shared + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/general/toaster/toastDrv/kmdf/func/simple/wdfsimple.vcxproj.Filters b/general/toaster/toastDrv/kmdf/func/simple/wdfsimple.vcxproj.Filters new file mode 100644 index 000000000..cadde112b --- /dev/null +++ b/general/toaster/toastDrv/kmdf/func/simple/wdfsimple.vcxproj.Filters @@ -0,0 +1,31 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {63D238C3-D6F1-4A4D-8E9A-CD96C3EF19BA} + + + h;hpp;hxx;hm;inl;inc;xsd + {D146F406-1C35-4F03-8623-AD0E44BDE5C0} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {17782FC8-CA51-4C10-8D28-E9EA73162C1A} + + + inf;inv;inx;mof;mc; + {8DF8F970-514A-42CA-BEB9-7B0BBD19E87E} + + + + + Driver Files + + + + + Source Files + + + \ No newline at end of file diff --git a/general/toaster/toastDrv/kmdf/inc/driver.h b/general/toaster/toastDrv/kmdf/inc/driver.h new file mode 100644 index 000000000..8e7ade01e --- /dev/null +++ b/general/toaster/toastDrv/kmdf/inc/driver.h @@ -0,0 +1,80 @@ +/*++ +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 + ); + +// +// Interface for getting and setting power level etc., +// +typedef struct _TOASTER_INTERFACE_STANDARD { + INTERFACE InterfaceHeader; + PTOASTER_GET_CRISPINESS_LEVEL GetCrispinessLevel; + PTOASTER_SET_CRISPINESS_LEVEL SetCrispinessLevel; + PTOASTER_IS_CHILD_PROTECTED IsSafetyLockEnabled; //): +} TOASTER_INTERFACE_STANDARD, *PTOASTER_INTERFACE_STANDARD; + + +#endif + diff --git a/general/toaster/toastDrv/kmdf/inc/public.h b/general/toaster/toastDrv/kmdf/inc/public.h new file mode 100644 index 000000000..0628f95f9 --- /dev/null +++ b/general/toaster/toastDrv/kmdf/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/toastDrv/kmdf/toastmon/toastmon.H b/general/toaster/toastDrv/kmdf/toastmon/toastmon.H new file mode 100644 index 000000000..896fc1b0d --- /dev/null +++ b/general/toaster/toastDrv/kmdf/toastmon/toastmon.H @@ -0,0 +1,128 @@ +/*++ + +Copyright (c) 1990-2000 Microsoft Corporation, All Rights Reserved + +Module Name: + + toastmon.h + +Abstract: + +Environment: + + Kernel mode + +--*/ + +#if !defined(__TOASTMON_H__) +#define __TOASTMON_H__ + +#include //wdm.h> +#include // Driver Framework. + +#define DRIVER_TAG 'NOMT' +#define READ_BUF_SIZE 100 +#define WRITE_BUF_SIZE 120 + +typedef struct _DEVICE_EXTENSION { + WDFDEVICE WdfDevice; + WDFIOTARGET ToasterTarget; + PVOID NotificationHandle; // Interface notification handle + WDFCOLLECTION TargetDeviceCollection; + WDFWAITLOCK TargetDeviceCollectionLock; + PVOID WMIDeviceArrivalNotificationObject; +} DEVICE_EXTENSION, *PDEVICE_EXTENSION; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(DEVICE_EXTENSION, GetDeviceExtension) + + +typedef struct _TARGET_DEVICE_INFO { + PDEVICE_EXTENSION DeviceExtension; // Our FDO device extension + LIST_ENTRY ListEntry; // Entry to chain to the listhead + WDFREQUEST ReadRequest; + WDFREQUEST WriteRequest; + WDFTIMER TimerForPostingRequests; + + // + // Set to TRUE while the target is opened. Will be set to FALSE at query remove (for a graceful remove) + // or removal complete (surprise removal of the target). Can be set back to TRUE if the graceful remove + // fails and the query remove is canceled. + // + // Guarded by DeviceExtension->TargetDeviceCollectionLock + // + BOOLEAN Opened; +} TARGET_DEVICE_INFO, *PTARGET_DEVICE_INFO; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(TARGET_DEVICE_INFO, GetTargetDeviceInfo) + +typedef struct _TIMER_CONTEXT { + + WDFIOTARGET IoTarget; + +} TIMER_CONTEXT, *PTIMER_CONTEXT ; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(TIMER_CONTEXT, GetTimerContext) + +#if DBG +#define DebugPrint(_x_) \ + DbgPrint ("ToastMon:"); \ + DbgPrint _x_; + +#define TRAP() DbgBreakPoint() + +#else +#define DebugPrint(_x_) +#define TRAP() +#endif + + +/********************* function prototypes ***********************************/ +// +DRIVER_INITIALIZE DriverEntry; + +EVT_WDF_DRIVER_DEVICE_ADD ToastMon_EvtDeviceAdd; + +EVT_WDF_DEVICE_CONTEXT_CLEANUP ToastMon_EvtDeviceContextCleanup; + +EVT_WDF_IO_TARGET_QUERY_REMOVE ToastMon_EvtIoTargetQueryRemove; +EVT_WDF_IO_TARGET_REMOVE_CANCELED ToastMon_EvtIoTargetRemoveCanceled; +EVT_WDF_IO_TARGET_REMOVE_COMPLETE ToastMon_EvtIoTargetRemoveComplete; + +EVT_WDF_TIMER Toastmon_EvtTimerPostRequests; + +EVT_WDF_REQUEST_COMPLETION_ROUTINE Toastmon_ReadRequestCompletionRoutine; +EVT_WDF_REQUEST_COMPLETION_ROUTINE Toastmon_WriteRequestCompletionRoutine; + +DRIVER_NOTIFICATION_CALLBACK_ROUTINE ToastMon_PnpNotifyInterfaceChange; + +NTSTATUS +Toastmon_OpenDevice( + WDFDEVICE Device, + PUNICODE_STRING SymbolicLink, + WDFIOTARGET *Target + ); + +NTSTATUS +ToastMon_PostReadRequests( + IN WDFIOTARGET IoTarget + ); + +NTSTATUS +ToastMon_PostWriteRequests( + IN WDFIOTARGET IoTarget + ); + +NTSTATUS +RegisterForWMINotification( + PDEVICE_EXTENSION DeviceExt + ); + +VOID +UnregisterForWMINotification( + PDEVICE_EXTENSION DeviceExt + ); + +FWMI_NOTIFICATION_CALLBACK WmiNotificationCallback; + +#endif + diff --git a/general/toaster/toastDrv/kmdf/toastmon/toastmon.RC b/general/toaster/toastDrv/kmdf/toastmon/toastmon.RC new file mode 100644 index 000000000..e1a5d4a28 --- /dev/null +++ b/general/toaster/toastDrv/kmdf/toastmon/toastmon.RC @@ -0,0 +1,12 @@ +#include + +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "Toaster PnP Monitor" +#define VER_INTERNALNAME_STR "wdftoastmon.sys" + +#include "common.ver" + + \ No newline at end of file diff --git a/general/toaster/toastDrv/kmdf/toastmon/toastmon.c b/general/toaster/toastDrv/kmdf/toastmon/toastmon.c new file mode 100644 index 000000000..e06c523bd --- /dev/null +++ b/general/toaster/toastDrv/kmdf/toastmon/toastmon.c @@ -0,0 +1,1109 @@ +/*++ + +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: + + toastmon.c + +Abstract: + This sample demonstrates how to register PnP event notification + for an interface class, how to open the target device in the + callback and register and respond to device change notification. + + To schedule sending Read and Write requests to the target device + the sample uses a "passive" timer. This feature enables getting + a timer callback at PASSIVE_LEVEL without having to create and + queue a WDFWORKITEM objects on its own. + + +Environment: + + Kernel mode + +--*/ + +#include "toastmon.h" +#include +#include +#include + +#include "public.h" + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (INIT, DriverEntry) +#pragma alloc_text (PAGE, ToastMon_EvtDeviceAdd) +#pragma alloc_text (PAGE, ToastMon_EvtDeviceContextCleanup) +#pragma alloc_text (PAGE, ToastMon_PnpNotifyInterfaceChange) +#pragma alloc_text (PAGE, ToastMon_EvtIoTargetQueryRemove) +#pragma alloc_text (PAGE, ToastMon_EvtIoTargetRemoveCanceled) +#pragma alloc_text (PAGE, ToastMon_EvtIoTargetRemoveComplete) +#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 + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + WDF_DRIVER_CONFIG config; + + KdPrint(("ToastMon 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, + ToastMon_EvtDeviceAdd + ); + + // + // 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 +ToastMon_EvtDeviceAdd( + IN WDFDRIVER Driver, + IN PWDFDEVICE_INIT DeviceInit + ) +/*++ +Routine Description: + + ToastMon_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 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 + +--*/ +{ + WDF_OBJECT_ATTRIBUTES attributes; + NTSTATUS status = STATUS_SUCCESS; + WDFDEVICE device; + PDEVICE_EXTENSION deviceExtension; + + KdPrint( ("ToastMon_EvtDeviceAdd routine\n")); + + UNREFERENCED_PARAMETER(Driver); + + PAGED_CODE(); + + // + // Specify the size of device extension where we track per device + // context. + // + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, DEVICE_EXTENSION); + + attributes.EvtCleanupCallback = ToastMon_EvtDeviceContextCleanup; + + // + // 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, &attributes, &device); + if (!NT_SUCCESS(status)) { + KdPrint( ("WdfDeviceCreate failed with Status 0x%x\n", status)); + return status; + } + + // + // Get the DeviceExtension and initialize it. + // + deviceExtension = GetDeviceExtension(device); + + deviceExtension->WdfDevice = device; + + // + // Create a collection to store information about target devices. + // + WDF_OBJECT_ATTRIBUTES_INIT(&attributes); + attributes.ParentObject = device; + + status = WdfCollectionCreate(&attributes, + &deviceExtension->TargetDeviceCollection); + if (!NT_SUCCESS(status)) + { + KdPrint( ("WdfCollectionCreate failed with status 0x%x\n", status)); + return status; + } + + WDF_OBJECT_ATTRIBUTES_INIT(&attributes); + attributes.ParentObject = device; + + status = WdfWaitLockCreate(&attributes, + &deviceExtension->TargetDeviceCollectionLock); + if (!NT_SUCCESS(status)) + { + KdPrint( ("WdfWaitLockCreate failed with status 0x%x\n", status)); + return status; + } + + // + // Register for TOASTER device interface change notification. + // We will get GUID_DEVICE_INTERFACE_ARRIVAL and + // GUID_DEVICE_INTERFACE_REMOVAL notification when the toaster + // device is started and removed. + // Framework doesn't provide a WDF interface to register for interface change + // notification. However if the target device is opened by symboliclink using + // IoTarget, framework registers itself EventCategoryTargetDeviceChange + // notification on the handle and responds to the pnp notifications. + // + // Note that as soon as you register, arrival notification will be sent + // about all existing toaster devices even before this device is started. So if + // you cannot handle these notification before start then you should register this in + // PrepareHardware or SelfManagedIoInit callback. + // You must unregister this notification when the device is removed in the + // DeviceContextCleanup callback. This call takes a reference on the driverobject. + // So if you don't unregister it will prevent the driver from unloading. + // + status = IoRegisterPlugPlayNotification ( + EventCategoryDeviceInterfaceChange, + PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES, + (PVOID)&GUID_DEVINTERFACE_TOASTER, + WdfDriverWdmGetDriverObject(WdfDeviceGetDriver(device)), + (PDRIVER_NOTIFICATION_CALLBACK_ROUTINE) + ToastMon_PnpNotifyInterfaceChange, + (PVOID)deviceExtension, + &deviceExtension->NotificationHandle); + + if (!NT_SUCCESS(status)) { + KdPrint(("RegisterPnPNotifiction failed: 0x%x\n", status)); + return status; + } + + RegisterForWMINotification(deviceExtension); + + return status; +} + +#pragma warning(push) +#pragma warning(disable:28118) // this callback will run at IRQL=PASSIVE_LEVEL +_Use_decl_annotations_ +VOID +ToastMon_EvtDeviceContextCleanup( + 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 EvtDeviceRemove callback when the PnP manager sends + an IRP_MN_REMOVE_DEVICE request to the driver stack. Function driver + typically undo whatever they did in EvtDeviceAdd callback - free + structures, cleanup collections, etc. + +Arguments: + + Device - Handle to a framework device object. + +Return Value: + + VOID + +--*/ +{ + PDEVICE_EXTENSION deviceExtension; + + KdPrint( ("ToastMon_EvtDeviceContextCleanup\n")); + + PAGED_CODE(); + + deviceExtension = GetDeviceExtension((WDFDEVICE)Device); + + // + // Unregister the interface notification + // + if(deviceExtension->NotificationHandle) { + IoUnregisterPlugPlayNotification(deviceExtension->NotificationHandle); + } + + // + // TargetDeviceCollection will get deleted automatically when + // the Device is deleted due to the association we made when + // we created the object in EvtDeviceAdd. + // + // Any targets remaining in the collection will also be automaticaly closed + // and deleted. + // + deviceExtension->TargetDeviceCollection = NULL; + + UnregisterForWMINotification(deviceExtension); + + return; +} +#pragma warning(pop) // enable 28118 again + +__drv_functionClass(DRIVER_NOTIFICATION_CALLBACK_ROUTINE) +__drv_maxIRQL(PASSIVE_LEVEL) +NTSTATUS +ToastMon_PnpNotifyInterfaceChange( + _In_ PVOID NotificationStruct, + _Inout_opt_ PVOID Context + ) +/*++ + +Routine Description: + + This routine is the PnP "interface change notification" callback routine. + + This gets called on a Toaster triggered device interface arrival or + removal. + - Interface arrival corresponds to a Toaster device being STARTED + - Interface removal corresponds to a Toaster device being REMOVED + + On arrival: + - Create a IoTarget and open it by using the symboliclink. WDF will + Register for EventCategoryTargetDeviceChange notification on the fileobject + so it can cleanup whenever associated device is removed. + + On removal: + - This callback is a NO-OP for interface removal because framework registers + for PnP EventCategoryTargetDeviceChange callbacks and + uses that callback to clean up when the associated toaster device goes + away. + +Arguments: + + NotificationStruct - Structure defining the change. + + Context - pointer to the device extension. + (supplied as the "context" when we + registered for this callback) +Return Value: + + STATUS_SUCCESS - always, even if something goes wrong + + status is only used during query removal notifications and the OS ignores other + cases + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + PDEVICE_EXTENSION deviceExtension = Context; + WDFIOTARGET ioTarget; + PDEVICE_INTERFACE_CHANGE_NOTIFICATION devNotificationStruct = NotificationStruct; + + _Analysis_assume_(NULL != deviceExtension); + + PAGED_CODE(); + + KdPrint(("Entered ToastMon_PnpNotifyInterfaceChange\n")); + + // + // Verify that interface class is a toaster device interface. + // + ASSERT(IsEqualGUID( (LPGUID)&(devNotificationStruct->InterfaceClassGuid), + (LPGUID)&GUID_DEVINTERFACE_TOASTER)); + + // + // Check the callback event. + // + if(IsEqualGUID( (LPGUID)&(devNotificationStruct->Event), + (LPGUID)&GUID_DEVICE_INTERFACE_ARRIVAL )) { + + KdPrint(("Arrival Notification\n")); + + status = Toastmon_OpenDevice((WDFDEVICE)deviceExtension->WdfDevice, + (PUNICODE_STRING)devNotificationStruct->SymbolicLinkName, + &ioTarget); + if (!NT_SUCCESS(status)) { + KdPrint( ("Unable to open control device 0x%x\n", status)); + return status; + } + + // + // Add this one to the collection. + // + WdfWaitLockAcquire(deviceExtension->TargetDeviceCollectionLock, NULL); + + // + // WdfCollectionAdd takes a reference on the request object and removes + // it when you call WdfCollectionRemove. + // + status = WdfCollectionAdd(deviceExtension->TargetDeviceCollection, ioTarget); + if (!NT_SUCCESS(status)) { + KdPrint( ("WdfCollectionAdd failed 0x%x\n", status)); + WdfObjectDelete(ioTarget); // Delete will also close the target + } + + WdfWaitLockRelease(deviceExtension->TargetDeviceCollectionLock); + + + } else { + + KdPrint(("Removal Interface Notification\n")); + } + return STATUS_SUCCESS; +} + + +NTSTATUS +Toastmon_OpenDevice( + WDFDEVICE Device, + PUNICODE_STRING SymbolicLink, + WDFIOTARGET *Target + ) +/*++ + +Routine Description: + + Open the I/O target and preallocate any resources required + to communicate with the target device. + +Arguments: + +Return Value: + + NTSTATUS + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + PTARGET_DEVICE_INFO targetDeviceInfo = NULL; + WDF_IO_TARGET_OPEN_PARAMS openParams; + WDFIOTARGET ioTarget; + WDF_OBJECT_ATTRIBUTES attributes; + PDEVICE_EXTENSION deviceExtension = GetDeviceExtension(Device); + WDF_TIMER_CONFIG wdfTimerConfig; + + + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, TARGET_DEVICE_INFO); + + status = WdfIoTargetCreate(deviceExtension->WdfDevice, + &attributes, + &ioTarget); + if (!NT_SUCCESS(status)) { + KdPrint(("WdfIoTargetCreate failed 0x%x\n", status)); + return status; + } + + targetDeviceInfo = GetTargetDeviceInfo(ioTarget); + targetDeviceInfo->DeviceExtension = deviceExtension; + + // + // Warning: It's not recommended to open the targetdevice + // from a pnp notification callback routine, because if + // the target device initiates any kind of PnP action as + // a result of this open, the PnP manager could deadlock. + // You should queue a workitem to do that. + // For example, SWENUM devices in conjunction with KS + // initiate an enumeration of a device when you do the + // open on the device interface. + // We can open the target device here because we know the + // toaster function driver doesn't trigger any pnp action. + // + + WDF_IO_TARGET_OPEN_PARAMS_INIT_OPEN_BY_NAME( + &openParams, + SymbolicLink, + STANDARD_RIGHTS_ALL); + + openParams.ShareAccess = FILE_SHARE_WRITE | FILE_SHARE_READ; + // + // Framework provides default action for all of these if you don't register + // these callbacks -it will close the handle to the target when the device is + // being query-removed and reopen it if the query-remove fails. + // In this sample, we use a periodic timers to post requests to the target. + // So we need to register these callbacks so that we can start and stop + // the timer when the state of the target device changes. Since we are + // registering these callbacks, we are now responsbile for closing and + // reopening the target. + // + openParams.EvtIoTargetQueryRemove = ToastMon_EvtIoTargetQueryRemove; + openParams.EvtIoTargetRemoveCanceled = ToastMon_EvtIoTargetRemoveCanceled; + openParams.EvtIoTargetRemoveComplete = ToastMon_EvtIoTargetRemoveComplete; + + + status = WdfIoTargetOpen(ioTarget, &openParams); + + if (!NT_SUCCESS(status)) { + KdPrint(("WdfIoTargetOpen failed with status 0x%x\n", status)); + WdfObjectDelete(ioTarget); + return status; + } + + // + // NOTE: + // + // These WdfIoTargetWdmGetXxxx calls can be made outside of holding TargetDeviceCollectionLock + // because we are in the interface arrival notification callback and the target cannot be + // gracefully or surprise removed until we return back to the kernel. + // + // If the target is being opened in another context outside of interface arrival callback, + // proper synchronization between the target's state change and the API calls must be made. + // + KdPrint(("Target Device 0x%p, PDO 0x%p, Fileobject 0x%p, Filehandle 0x%p\n", + WdfIoTargetWdmGetTargetDeviceObject(ioTarget), + WdfIoTargetWdmGetTargetPhysicalDevice(ioTarget), + WdfIoTargetWdmGetTargetFileObject(ioTarget), + WdfIoTargetWdmGetTargetFileHandle(ioTarget))); + + // + // Create two requests - one for read and one for write. + // + WDF_OBJECT_ATTRIBUTES_INIT(&attributes); + attributes.ParentObject = ioTarget; + + status = WdfRequestCreate(&attributes, + ioTarget, + &targetDeviceInfo->ReadRequest); + + if (!NT_SUCCESS(status)) { + WdfObjectDelete(ioTarget); + return status; + } + + WDF_OBJECT_ATTRIBUTES_INIT(&attributes); + attributes.ParentObject = ioTarget; + + status = WdfRequestCreate(&attributes, + ioTarget, + &targetDeviceInfo->WriteRequest); + + if (!NT_SUCCESS(status)) { + WdfObjectDelete(ioTarget); + return status; + } + + // + // Create a passive timer to post requests to the I/O target. + // + WDF_TIMER_CONFIG_INIT(&wdfTimerConfig, + Toastmon_EvtTimerPostRequests); + + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, TIMER_CONTEXT); + + // + // Make IoTarget as parent of the timer to prevent the ioTarget + // from deleted until the dpc has runto completion. + // + attributes.ParentObject = ioTarget; + + // + // By specifying WdfExecutionLevelPassive the framework will invoke + // the timer callback Toastmon_EvtTimerPostRequests at PASSIVE_LEVEL. + // + attributes.ExecutionLevel = WdfExecutionLevelPassive; + + // + // Setting the AutomaticSerialization to FALSE prevents + // WdfTimerCreate to fail if the parent device object's + // execution level is set to WdfExecutionLevelPassive. + // + wdfTimerConfig.AutomaticSerialization = FALSE; + + status = WdfTimerCreate(&wdfTimerConfig, + &attributes, + &targetDeviceInfo->TimerForPostingRequests + ); + + if(!NT_SUCCESS(status)) { + KdPrint(("WdfTimerCreate failed 0x%x\n", status)); + WdfObjectDelete(ioTarget); + return status; + } + + GetTimerContext(targetDeviceInfo->TimerForPostingRequests)->IoTarget = ioTarget; + + // + // Start the passive timer. The first timer will be queued after 1ms interval and + // after that it will be requeued in the timer callback function. + // The value of 1 ms (lowest timer resoltion allowed on NT) is chosen here so + // that timer would fire right away. + // + WdfTimerStart(targetDeviceInfo->TimerForPostingRequests, + WDF_REL_TIMEOUT_IN_MS(1)); + + // + // Can be set outside of holding TargetDeviceCollectionLock because we are + // creating the target in a notification callback and all subsequent state changes notifications + // can only happen after the arrival notification returns back to the kernel + // + targetDeviceInfo->Opened = TRUE; + + *Target = ioTarget; + + return status; + +} + +NTSTATUS +ToastMon_EvtIoTargetQueryRemove( + WDFIOTARGET IoTarget +) +/*++ + +Routine Description: + + Called when the Target device receives IRP_MN_QUERY_REMOVE. + This happens when somebody disables, ejects or uninstalls the target + device driver in usermode. Here close the handle to the + target device. If the system fails to remove the device for + some reason, you will get RemoveCancelled callback where + you can reopen and continue to interact with the target device. + +Arguments: + + IoTarget - + +Return Value: + + +--*/ +{ + PTARGET_DEVICE_INFO targetDeviceInfo = NULL; + WDFWAITLOCK targetDeviceCollectionLock; + + PAGED_CODE(); + + targetDeviceInfo = GetTargetDeviceInfo(IoTarget); + + KdPrint((("Device Removal (query remove) Notification\n"))); + + // + // Stop the timer + // + + WdfTimerStop(targetDeviceInfo->TimerForPostingRequests, TRUE); + + targetDeviceCollectionLock = targetDeviceInfo->DeviceExtension->TargetDeviceCollectionLock; + + // + // The target is being query removed, set Opened to FALSE to match this state change. + // + WdfWaitLockAcquire(targetDeviceCollectionLock, NULL); + targetDeviceInfo->Opened = FALSE; + WdfWaitLockRelease(targetDeviceCollectionLock); + + WdfIoTargetCloseForQueryRemove(IoTarget); + + return STATUS_SUCCESS; + +} + +VOID +ToastMon_EvtIoTargetRemoveCanceled( + WDFIOTARGET IoTarget + ) +/*++ + +Routine Description: + + Called when the Target device received IRP_MN_CANCEL_REMOVE. + This happens if another app or driver talking to the target + device doesn't close handle or veto query-remove notification. + +Arguments: + + IoTarget - + +Return Value: + + +--*/ +{ + PTARGET_DEVICE_INFO targetDeviceInfo = NULL; + WDFWAITLOCK targetDeviceCollectionLock; + WDF_IO_TARGET_OPEN_PARAMS openParams; + NTSTATUS status; + + PAGED_CODE(); + + KdPrint((("Device Removal (remove cancelled) Notification\n"))); + + targetDeviceInfo = GetTargetDeviceInfo(IoTarget); + + // + // Reopen the Target. + // + WDF_IO_TARGET_OPEN_PARAMS_INIT_REOPEN(&openParams); + + status = WdfIoTargetOpen(IoTarget, &openParams); + + if (!NT_SUCCESS(status)) { + KdPrint(("WdfIoTargetOpen failed 0x%x\n", status)); + WdfObjectDelete(IoTarget); + return; + } + + targetDeviceCollectionLock = targetDeviceInfo->DeviceExtension->TargetDeviceCollectionLock; + + // + // The query remove has failed and the target has been successfully reopened. Set Opened + // back to TRUE to reflect the state change. + // + WdfWaitLockAcquire(targetDeviceCollectionLock, NULL); + targetDeviceInfo->Opened = TRUE; + WdfWaitLockRelease(targetDeviceCollectionLock); + + + // + // Restart the timer. + // + WdfTimerStart(targetDeviceInfo->TimerForPostingRequests, + WDF_REL_TIMEOUT_IN_SEC(1)); + +} + +VOID +ToastMon_EvtIoTargetRemoveComplete( + WDFIOTARGET IoTarget +) +/*++ + +Routine Description: + + Called when the Target device is removed ( either the target + received IRP_MN_REMOVE_DEVICE or IRP_MN_SURPRISE_REMOVAL) + +Arguments: + + IoTarget - + +Return Value: + + +--*/ +{ + PDEVICE_EXTENSION deviceExtension; + PTARGET_DEVICE_INFO targetDeviceInfo = NULL; + + KdPrint((("Device Removal (remove complete) Notification\n"))); + + PAGED_CODE(); + + targetDeviceInfo = GetTargetDeviceInfo(IoTarget); + deviceExtension = targetDeviceInfo->DeviceExtension; + + // + // Stop the timer + // + WdfTimerStop(targetDeviceInfo->TimerForPostingRequests, TRUE); + + // + // Remove the target device from the collection and set Opened to FALSE to match + // the state change (in the case of a surprise removal of the target, + // ToastMon_EvtIoTargetQueryRemove is not called so Opened is still TRUE). + // + WdfWaitLockAcquire(deviceExtension->TargetDeviceCollectionLock, NULL); + + WdfCollectionRemove(deviceExtension->TargetDeviceCollection, IoTarget); + targetDeviceInfo->Opened = FALSE; + + WdfWaitLockRelease(deviceExtension->TargetDeviceCollectionLock); + + // + // Finally delete the target. + // + WdfObjectDelete(IoTarget); + + return; + +} + +VOID +Toastmon_EvtTimerPostRequests( + IN WDFTIMER Timer + ) +/*++ + +Routine Description: + + Passive timer event to post read and write reqeuests. + +Return Value: + + None + +--*/ +{ + PTARGET_DEVICE_INFO targetInfo; + + WDFIOTARGET ioTarget = GetTimerContext(Timer)->IoTarget; + + targetInfo = GetTargetDeviceInfo(ioTarget); + + // + // Even though this routine and the completion routine check the + // ReadRequest/WriteRequest field outside a lock, no harm is done. + // Depending on how far the completion-routine has run, timer + // may miss an opportunity to post a request. Even if we use a lock, + // this race condition will still exist. + // + + // + // Send a read request to the target device + // + if(targetInfo->ReadRequest) { + ToastMon_PostReadRequests(ioTarget); + } + + // + // Send a write request to the target device + // + if(targetInfo->WriteRequest) { + ToastMon_PostWriteRequests(ioTarget); + } + + // + // Restart the passive timer. + // + WdfTimerStart(targetInfo->TimerForPostingRequests, + WDF_REL_TIMEOUT_IN_SEC(1)); + + return; +} + +NTSTATUS +ToastMon_PostReadRequests( + IN WDFIOTARGET IoTarget + ) +/*++ + +Routine Description: + + Called by the timer callback to send a read request to the target device. + +Return Value: + + NT Status code + +--*/ +{ + + WDFREQUEST request; + NTSTATUS status; + PTARGET_DEVICE_INFO targetInfo; + WDFMEMORY memory; + WDF_OBJECT_ATTRIBUTES attributes; + + targetInfo = GetTargetDeviceInfo(IoTarget); + + request = targetInfo->ReadRequest; + + // + // Allocate memory for read. Ideally I should have allocated the memory upfront along + // with the request because the sizeof the memory buffer is constant. + // But for demonstration, I have choosen to allocate a memory + // object everytime I send a request down and delete it when the request + // is completed. This memory is parented to the request. + // + WDF_OBJECT_ATTRIBUTES_INIT(&attributes); + attributes.ParentObject = request; + status = WdfMemoryCreate( + &attributes, + NonPagedPool, + DRIVER_TAG, + READ_BUF_SIZE, + &memory, + NULL); // buffer pointer + + if (!NT_SUCCESS(status)) { + KdPrint(("WdfMemoryCreate failed 0x%x\n", status)); + return status; + } + + status = WdfIoTargetFormatRequestForRead( + IoTarget, + request, + memory, + NULL, // Buffer offset + NULL); // OutputBufferOffset + if (!NT_SUCCESS(status)) { + KdPrint(("WdfIoTargetFormatRequestForRead failed 0x%x\n", status)); + return status; + } + + WdfRequestSetCompletionRoutine(request, + Toastmon_ReadRequestCompletionRoutine, + targetInfo); + + // + // Clear the ReadRequest field in the context to avoid + // being reposted even before the reqeuest completes. + // This will be reset in the complete routine when the request completes. + // + targetInfo->ReadRequest = NULL; + + if(WdfRequestSend(request, IoTarget, WDF_NO_SEND_OPTIONS) == FALSE) { + status = WdfRequestGetStatus(request); + KdPrint(("WdfRequestSend failed 0x%x\n", status)); + targetInfo->ReadRequest = request; + } + + return status; +} + +NTSTATUS +ToastMon_PostWriteRequests( + IN WDFIOTARGET IoTarget + ) +/*++ + +Routine Description: + + Called by the timer callback to send a write request to the target device. + +Return Value: + + NT Status code + +--*/ +{ + + WDFREQUEST request; + NTSTATUS status; + PTARGET_DEVICE_INFO targetInfo; + WDFMEMORY memory; + WDF_OBJECT_ATTRIBUTES attributes; + + targetInfo = GetTargetDeviceInfo(IoTarget); + + request = targetInfo->WriteRequest; + + // + // Allocate memory for write. Ideally I should have allocated the memory upfront along + // with the request because the sizeof the memory buffer is constant. + // But for demonstration, I have choosen to allocate a memory + // object everytime I send a request down and delete it when the request + // is completed. This memory is parented to the request. + // + WDF_OBJECT_ATTRIBUTES_INIT(&attributes); + attributes.ParentObject = request; + status = WdfMemoryCreate( + &attributes, + NonPagedPool, + DRIVER_TAG, + WRITE_BUF_SIZE, + &memory, + NULL); // buffer pointer + + if (!NT_SUCCESS(status)) { + KdPrint(("WdfMemoryCreate failed 0x%x\n", status)); + return status; + } + + status = WdfIoTargetFormatRequestForWrite( + IoTarget, + request, + memory, + NULL, // Buffer offset + NULL); // OutputBufferOffset + if (!NT_SUCCESS(status)) { + KdPrint(("WdfIoTargetFormatRequestForWrite failed 0x%x\n", status)); + return status; + } + + WdfRequestSetCompletionRoutine(request, + Toastmon_WriteRequestCompletionRoutine, + targetInfo); + + + // + // Clear the WriteRequest field in the context to avoid + // being reposted even before the reqeuest completes. + // This will be reset in the complete routine when the request completes. + // + targetInfo->WriteRequest = NULL; + + if(WdfRequestSend(request, IoTarget, WDF_NO_SEND_OPTIONS) == FALSE) { + status = WdfRequestGetStatus(request); + KdPrint(("WdfRequestSend failed 0x%x\n", status)); + targetInfo->WriteRequest = request; + } + return status; +} + +VOID +Toastmon_ReadRequestCompletionRoutine( + IN WDFREQUEST Request, + IN WDFIOTARGET Target, + PWDF_REQUEST_COMPLETION_PARAMS CompletionParams, + IN WDFCONTEXT Context + ) +/*++ + +Routine Description: + + Completion Routine + +Arguments: + + CompletionParams - Contains the results of the transfer such as + IoStatus, Length, Buffer, etc. + + Context - context value specified in the WdfRequestSetCompletionRoutine + +Return Value: + + VOID + +--*/ +{ + WDF_REQUEST_REUSE_PARAMS params; + PTARGET_DEVICE_INFO targetInfo; + NTSTATUS status; + + UNREFERENCED_PARAMETER(Context); + + targetInfo = GetTargetDeviceInfo(Target); + + // + // Delete the memory object because we create a new one every time we post + // the request. For perf reason, it would be better to preallocate the memory + // object once. + // Also for driver created requests, do not call WdfRequestRetrieve functions to get + // the buffers. They can be called only for requests delivered by the queue. + // + WdfObjectDelete(CompletionParams->Parameters.Read.Buffer); + + // + // Scrub the request for reuse. + // + WDF_REQUEST_REUSE_PARAMS_INIT(¶ms, WDF_REQUEST_REUSE_NO_FLAGS, STATUS_SUCCESS); + + status = WdfRequestReuse(Request, ¶ms); + ASSERT(NT_SUCCESS(status)); + _Analysis_assume_(NT_SUCCESS(status)); + // + // RequestReuse zero all the values in structure pointed by CompletionParams. + // So you must get all the information from completion params before + // calling RequestReuse. + // + + targetInfo->ReadRequest = Request; + + // + // Don't repost the request in the completion routine because it may lead to recursion + // if the driver below completes the request synchronously. + // + return; +} + +VOID +Toastmon_WriteRequestCompletionRoutine( + IN WDFREQUEST Request, + IN WDFIOTARGET Target, + PWDF_REQUEST_COMPLETION_PARAMS CompletionParams, + IN WDFCONTEXT Context + ) +/*++ + +Routine Description: + + Completion Routine + +Arguments: + + CompletionParams - Contains the results of the transfer such as + IoStatus, Length, Buffer, etc. + + Context - context value specified in the WdfRequestSetCompletionRoutine + +Return Value: + + VOID + +--*/ +{ + WDF_REQUEST_REUSE_PARAMS params; + PTARGET_DEVICE_INFO targetInfo; + NTSTATUS status; + + UNREFERENCED_PARAMETER(Context); + + targetInfo = GetTargetDeviceInfo(Target); + + // + // Delete the memory object because we create a new one every time we post + // the request. For perf reason, it would be better to preallocate even memory object. + // Also for driver created requests, do not call WdfRequestRetrieve functions to get + // the buffers. WdfRequestRetrieveBuffer functions can be called only for requests + // delivered by the queue. + // + WdfObjectDelete(CompletionParams->Parameters.Write.Buffer); + + // + // Scrub the request for reuse. + // + WDF_REQUEST_REUSE_PARAMS_INIT(¶ms, WDF_REQUEST_REUSE_NO_FLAGS, STATUS_SUCCESS); + + status = WdfRequestReuse(Request, ¶ms); + ASSERT(NT_SUCCESS(status)); + _Analysis_assume_(NT_SUCCESS(status)); + + // + // RequestReuse zero all the values in structure pointed by CompletionParams. + // So you must get all the information from completion params before + // calling RequestReuse. + // + + targetInfo->WriteRequest = Request; + + // + // Don't repost the request in the completion routine because it may lead to recursion + // if the driver below completes the request synchronously. + // + return; +} + + diff --git a/general/toaster/toastDrv/kmdf/toastmon/wdftoastmon.inx b/general/toaster/toastDrv/kmdf/toastmon/wdftoastmon.inx new file mode 100644 index 000000000..214e22730 --- /dev/null +++ b/general/toaster/toastDrv/kmdf/toastmon/wdftoastmon.inx @@ -0,0 +1,94 @@ +;/*++ +; +;Copyright (c) 1990-2000 Microsoft Corporation All rights Reserved +; +;Module Name: +; +; ToastMon.INF +; +;Abstract: +; INF file for installing sample WDF toastmon driver +; +;Installation Notes: +; Using Devcon: Type "devcon install wdftoastmon.inf root\toastmon" to install +;--*/ + +[Version] +Signature="$WINDOWS NT$" +Class=Sample +ClassGuid={78A1C341-4539-11d3-B88D-00C04FAD5171} +Provider=%ProviderName% +DriverVer=06/16/1999,5.00.2072 +CatalogFile=KmdfSamples.cat + +[DestinationDirs] +DefaultDestDir = 12 +ToastMon_Inst_CoInstaller_CopyFiles = 11 + +; ================= Class section ===================== + +[ClassInstall32] +Addreg=SampleClassReg + +[SampleClassReg] +HKR,,,0,%ClassName% +HKR,,Icon,,-5 + +; ================= Device Install section ===================== + +[Manufacturer] +%MfgName%=Toast,NT$ARCH$ + +[SourceDisksFiles] +wdftoastmon.sys=1 +WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll=1 + +[SourceDisksNames] +1=%DISK_NAME%, + +[Toast.NT$ARCH$] +%WdfToastMon.DRVDESC%=ToastMon_Inst,root\toastmon + +[ToastMon_Inst.NT] +CopyFiles=ToastMon.CopyFiles + +[ToastMon.CopyFiles] +wdftoastmon.sys + +[ToastMon_Inst.NT.Services] +AddService=wdfToastMon,0x00000002,wdfToastMon_Service + +[wdfToastMon_Service] +DisplayName = %wdfToastMon.SVCDESC% +ServiceType = 1 ; SERVICE_KERNEL_DRIVER +StartType = 3 ; SERVICE_DEMAND_START +ErrorControl = 1 ; SERVICE_ERROR_NORMAL +ServiceBinary = %12%\wdftoastmon.sys + +; +;--- ToastMon_Inst Coinstaller installation ------ +; + +[ToastMon_Inst.NT.CoInstallers] +AddReg=ToastMon_Inst_CoInstaller_AddReg +CopyFiles=ToastMon_Inst_CoInstaller_CopyFiles + +[ToastMon_Inst_CoInstaller_AddReg] +HKR,,CoInstallers32,0x00010000, "WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll,WdfCoInstaller" + +[ToastMon_Inst_CoInstaller_CopyFiles] +WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll + +[ToastMon_Inst.NT.Wdf] +KmdfService = wdfToastMon, wdfToastMon_wdfsect + +[wdfToastMon_wdfsect] +KmdfLibraryVersion = $KMDFVERSION$ + +[Strings] +ProviderName = "TODO-Set-Provider" +MfgName = "TODO-Set-Manufacturer" +ClassName = "Sample Device" +WdfToastMon.SVCDESC = "Sample WDF ToastMon Service" +WdfToastMon.DRVDESC = "Sample WDF ToastMon Driver" +DISK_NAME = "Toastmon Install Disk" diff --git a/general/toaster/toastDrv/kmdf/toastmon/wdftoastmon.vcxproj b/general/toaster/toastDrv/kmdf/toastmon/wdftoastmon.vcxproj new file mode 100644 index 000000000..b6981f743 --- /dev/null +++ b/general/toaster/toastDrv/kmdf/toastmon/wdftoastmon.vcxproj @@ -0,0 +1,181 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {A5E50982-981B-40B9-BEE6-1E15581FE947} + $(MSBuildProjectName) + 1 + false + true + Debug + Win32 + {9CAC55A2-7E42-4350-840F-0EE864DEC954} + + + + 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)\wdftoastmon.inf + + + + wdftoastmon + + + wdftoastmon + + + wdftoastmon + + + wdftoastmon + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ntstrsafe.lib + + + %(AdditionalIncludeDirectories);..\inc + + + %(AdditionalIncludeDirectories);..\inc + + + + + %(AdditionalIncludeDirectories);..\inc + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ntstrsafe.lib + + + %(AdditionalIncludeDirectories);..\inc + + + %(AdditionalIncludeDirectories);..\inc + + + + + %(AdditionalIncludeDirectories);..\inc + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ntstrsafe.lib + + + %(AdditionalIncludeDirectories);..\inc + + + %(AdditionalIncludeDirectories);..\inc + + + + + %(AdditionalIncludeDirectories);..\inc + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ntstrsafe.lib + + + %(AdditionalIncludeDirectories);..\inc + + + %(AdditionalIncludeDirectories);..\inc + + + + + %(AdditionalIncludeDirectories);..\inc + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/general/toaster/toastDrv/kmdf/toastmon/wdftoastmon.vcxproj.Filters b/general/toaster/toastDrv/kmdf/toastmon/wdftoastmon.vcxproj.Filters new file mode 100644 index 000000000..2f2921ed2 --- /dev/null +++ b/general/toaster/toastDrv/kmdf/toastmon/wdftoastmon.vcxproj.Filters @@ -0,0 +1,39 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {734E963A-2784-4259-A850-7B527681AB96} + + + h;hpp;hxx;hm;inl;inc;xsd + {1C35D44B-4EF9-4C91-A466-1C08A009F227} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {8588E893-BBAD-441F-A814-9664F0BBEF97} + + + inf;inv;inx;mof;mc; + {9C5C6D96-ABDC-4238-9471-4DD2FF83DC29} + + + + + Driver Files + + + + + Source Files + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/general/toaster/toastDrv/kmdf/toastmon/wmi.c b/general/toaster/toastDrv/kmdf/toastmon/wmi.c new file mode 100644 index 000000000..eb2ef2a3f --- /dev/null +++ b/general/toaster/toastDrv/kmdf/toastmon/wmi.c @@ -0,0 +1,302 @@ +/*++ + +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 demonstrates how to receive WMI notification fired by + another driver. The code here basically registers for toaster + device arrival WMI notification fired by the toaster function driver. + You can use similar technique to receive media sense notification + (GUID_NDIS_STATUS_MEDIA_CONNECT/GUID_NDIS_STATUS_MEDIA_DISCONNECT) + fired by NDIS whenever the network cable is plugged or unplugged. + +Environment: + + Kernel mode + + +--*/ + +#include "toastmon.h" +#include "public.h" +#include + +// +// These typedefs required to avoid compilation errors in Win2K build environment. +// +typedef +VOID +(*WMI_NOTIFICATION_CALLBACK)( // Copied from WDM.H + PVOID Wnode, + PVOID Context + ); + +typedef +NTSTATUS +(*PFN_IO_WMI_OPEN_BLOCK)( + IN GUID * DataBlockGuid, + IN ULONG DesiredAccess, + OUT PVOID * DataBlockObject + ); + +typedef +NTSTATUS +(*PFN_IO_WMI_SET_NOTIFICATION_CALLBACK)( + IN PVOID Object, + IN WMI_NOTIFICATION_CALLBACK Callback, + IN PVOID Context + ); + +NTSTATUS +GetTargetFriendlyName( + WDFIOTARGET Target, + IN WDFMEMORY* TargetName + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, RegisterForWMINotification) +#pragma alloc_text(PAGE, UnregisterForWMINotification) +#pragma alloc_text(PAGE, GetTargetFriendlyName) +#pragma alloc_text(PAGE, WmiNotificationCallback) +#endif + + +NTSTATUS +RegisterForWMINotification( + PDEVICE_EXTENSION DeviceExt + ) +{ + NTSTATUS status = STATUS_SUCCESS; + GUID wmiGuid; + UNICODE_STRING funcName; + + PFN_IO_WMI_OPEN_BLOCK WmiOpenBlock; + PFN_IO_WMI_SET_NOTIFICATION_CALLBACK WmiSetNotificationCallback; + + PAGED_CODE(); + + // + // APIs to open WMI interfaces are available on XP and beyond, so let us + // first check to see whether there are exported in the kernel we are + // running before using them. + // + RtlInitUnicodeString(&funcName, L"IoWMIOpenBlock"); + + WmiOpenBlock = + (PFN_IO_WMI_OPEN_BLOCK) (ULONG_PTR) + MmGetSystemRoutineAddress(&funcName); + + RtlInitUnicodeString(&funcName, L"IoWMISetNotificationCallback"); + + WmiSetNotificationCallback = + (PFN_IO_WMI_SET_NOTIFICATION_CALLBACK) (ULONG_PTR) + MmGetSystemRoutineAddress(&funcName); + + if(WmiOpenBlock == NULL || WmiSetNotificationCallback == NULL) { + // + // Not available. + // + return STATUS_NOT_SUPPORTED; + } + + // + // Check to make sure we are not called multiple times. + // + ASSERT(DeviceExt->WMIDeviceArrivalNotificationObject == NULL); + + // + // Register WMI callbacks for toaster device arrival events + // + wmiGuid = TOASTER_NOTIFY_DEVICE_ARRIVAL_EVENT; + + status = WmiOpenBlock( + &wmiGuid, + WMIGUID_NOTIFICATION, + &DeviceExt->WMIDeviceArrivalNotificationObject + ); + if (!NT_SUCCESS(status)) { + + KdPrint(("Unable to open wmi data block status 0x%x\n", status)); + DeviceExt->WMIDeviceArrivalNotificationObject = NULL; + + } else { + + status = WmiSetNotificationCallback( + DeviceExt->WMIDeviceArrivalNotificationObject, + WmiNotificationCallback, + DeviceExt + ); + if (!NT_SUCCESS(status)) { + KdPrint(("Unable to register for wmi notification 0x%x\n", status)); + ObDereferenceObject(DeviceExt->WMIDeviceArrivalNotificationObject); + DeviceExt->WMIDeviceArrivalNotificationObject = NULL; + } + } + + return status; +} + + +VOID +UnregisterForWMINotification( + PDEVICE_EXTENSION DeviceExt +) +{ + PAGED_CODE(); + + if (DeviceExt->WMIDeviceArrivalNotificationObject != NULL) { + ObDereferenceObject(DeviceExt->WMIDeviceArrivalNotificationObject); + DeviceExt->WMIDeviceArrivalNotificationObject = NULL; + } +} + + +NTSTATUS +GetTargetFriendlyName( + WDFIOTARGET Target, + IN WDFMEMORY* TargetName + ) +/*++ + +Routine Description: + + Return the friendly name associated with the given device object. + +Arguments: + +Return Value: + + NT status + +--*/ +{ + NTSTATUS status; + + PAGED_CODE(); + + // + // First get the length of the string. If the FriendlyName + // is not there then get the lenght of device description. + // + status = WdfIoTargetAllocAndQueryTargetProperty(Target, + DevicePropertyFriendlyName, + NonPagedPool, + WDF_NO_OBJECT_ATTRIBUTES, + TargetName); + + if (!NT_SUCCESS(status) && status != STATUS_INSUFFICIENT_RESOURCES) { + status = WdfIoTargetAllocAndQueryTargetProperty(Target, + DevicePropertyDeviceDescription, + NonPagedPool, + WDF_NO_OBJECT_ATTRIBUTES, + TargetName); + + } + + if (!NT_SUCCESS(status)) { + KdPrint(("WdfDeviceQueryProperty returned %x\n", status)); + } + + return status; +} + +VOID +WmiNotificationCallback( + IN PVOID Wnode, + IN PVOID Context + ) +/*++ + +Routine Description: + + WMI calls this function to notify the caller that the specified event has occurred. + +Arguments: + + Wnode - points to the WNODE_EVENT_ITEM structure returned by the driver triggering the event. + + Context - points to the value specified in the Context parameter of the + IoWMISetNotificationCallback routine. + +Return Value: + + NT status + +--*/ +{ + PWNODE_SINGLE_INSTANCE wnode = (PWNODE_SINGLE_INSTANCE) Wnode; + WDFMEMORY memory; + UNICODE_STRING deviceName; + PDEVICE_OBJECT devobj; + NTSTATUS status; + PDEVICE_EXTENSION deviceExt = Context; + WDFCOLLECTION hCollection = deviceExt->TargetDeviceCollection; + WDFIOTARGET ioTarget; + ULONG i; + + PAGED_CODE(); + + WdfWaitLockAcquire(deviceExt->TargetDeviceCollectionLock, NULL); + + for(i=0; i< WdfCollectionGetCount(hCollection); i++){ + + ioTarget = WdfCollectionGetItem(hCollection, i); + + // + // Before calling WdfIoTargetWdmGetTargetDeviceObject, make sure the target is still open. + // The WdfIoTargetWdmGetXxxDeviceObject APIs can only be called while the target is opened, otherwise + // they can return undefined values. + // + if (GetTargetDeviceInfo(ioTarget)->Opened == FALSE) { + KdPrint(("WDFIOTARGET %p not in an opened state.\n", ioTarget)); + continue; + } + + devobj = WdfIoTargetWdmGetTargetDeviceObject(ioTarget); + + if(devobj && + IoWMIDeviceObjectToProviderId(devobj) == wnode->WnodeHeader.ProviderId) { + + if( IsEqualGUID( (LPGUID)&(wnode->WnodeHeader.Guid), + (LPGUID)&TOASTER_NOTIFY_DEVICE_ARRIVAL_EVENT) ) { + // + // found the device. Just for demonstration, get the friendlyname of the + // target device and print it. + // + status = GetTargetFriendlyName(ioTarget, &memory); + if (!NT_SUCCESS(status)) { + KdPrint(("GetDeviceFriendlyName returned %x\n", status)); + break; + } + + RtlInitUnicodeString(&deviceName, (PWSTR) WdfMemoryGetBuffer(memory, NULL)); + KdPrint(("%wZ fired a device arrival event\n", &deviceName)); + + // + // Free the memory allocated by GetDeviceFriendlyName. + // + WdfObjectDelete(memory); + + break; + + } else { + KdPrint(("Unknown event.\n")); + } + } + + } + + WdfWaitLockRelease(deviceExt->TargetDeviceCollectionLock); +} + + + + diff --git a/general/toaster/toastDrv/toaster.sln b/general/toaster/toastDrv/toaster.sln new file mode 100644 index 000000000..cffb0f49a --- /dev/null +++ b/general/toaster/toastDrv/toaster.sln @@ -0,0 +1,256 @@ + +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", "{ADC63E58-1F2E-4A3A-A760-779FD9EAD00E}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Classinstaller", "Classinstaller", "{2C550FE4-29CD-40A8-836D-313696A3442B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Coinstaller", "Coinstaller", "{470F597C-0DBC-44AC-B69B-594ACB7B1ED0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Wmi", "Wmi", "{F39421E1-95F5-4DEE-A78F-B522BE05FEB0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Exe", "Exe", "{29C3C959-4CBC-4D9D-B660-2135BF00CC55}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Notify", "Notify", "{9C9637D5-A764-49F0-AF7D-C7F123CEB343}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Toast", "Toast", "{0293B300-378A-4D97-B93C-0690C8BC8019}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Enum", "Enum", "{F1085E78-8F4D-4044-B58E-BDA9E7E35A0F}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Toastmon", "Toastmon", "{0BFA46E4-075E-475A-B73E-6FAB41B71B68}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Kmdf", "Kmdf", "{929B678F-2875-4DEE-AA34-2C255D4B84EA}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Simple", "Simple", "{AC2EC9A6-11CC-4F9C-BBA6-2E78FFFF5C84}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Func", "Func", "{1EFF08AC-2EAE-4E76-BAA7-0361BDA16DB2}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Featured", "Featured", "{A6681C1E-BA3B-4DD1-A641-DA27776A0319}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sideband", "Sideband", "{B2431307-5557-4D2E-AB33-F38E7D8680A4}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Filter", "Filter", "{A457C9CB-C02C-4835-8B4F-D3C03956C246}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Generic", "Generic", "{F956DAF1-330C-458A-B659-A30D5CE74374}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Static", "Static", "{0A0AA60F-8EAD-4BC1-AB00-315A13174032}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Bus", "Bus", "{C1623324-F98A-40DF-B5BD-A1D4465653C5}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Dynamic", "Dynamic", "{E2B2526B-3B31-4309-A22A-1236F16CB840}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Toastmon", "Toastmon", "{85556EF1-3D39-442F-BB0A-B3B5D6DE7352}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Umdf", "Umdf", "{B9DF6659-83A8-42FF-80CD-A03012E07CCE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Func", "Func", "{BF917E30-8925-4467-A1BF-CBD9E0AC7D12}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "package", "Package\package.VcxProj", "{D72A34B9-86A5-4801-B7E3-256CF1F382AD}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tostrcls", "classinstaller\tostrcls.vcxproj", "{EA6EA609-654B-4B46-ABA1-1092BD3FA0AC}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tostrco1", "coinstaller\tostrco1.vcxproj", "{70FAA53D-F08B-4131-A5BD-FCAA3EFC48FF}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WmiToast", "exe\wmi\WmiToast.vcxproj", "{9FF1CCE5-AD65-444C-97C1-179619AA2479}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "notify", "exe\notify\notify.vcxproj", "{AFCA7BE8-3FDB-4257-AAAF-EBE85BC05EDD}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "toast", "exe\toast\toast.vcxproj", "{36EF502E-38C8-4BAB-B7FE-C9ED9194292E}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Enum", "exe\enum\Enum.vcxproj", "{A6E7CA25-D12B-4C39-9A01-BEEE71E04F37}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wdftoastmon", "kmdf\toastmon\wdftoastmon.vcxproj", "{A5E50982-981B-40B9-BEE6-1E15581FE947}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wdfsimple", "kmdf\func\simple\wdfsimple.vcxproj", "{FB919684-BF55-4E3D-95FA-CBCB7B0D8759}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wdffeatured", "kmdf\func\featured\wdffeatured.vcxproj", "{CA0E203E-4C96-466F-BF19-24F42DFC0C89}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "filter", "kmdf\filter\sideband\filter.vcxproj", "{5094C704-35B0-4A25-AD30-570C49BCAE82}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "filter", "kmdf\filter\generic\filter.vcxproj", "{C79A9F42-BDB4-4B5A-B7B5-1BEBF7752207}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "StatBus", "kmdf\bus\static\StatBus.vcxproj", "{965C0E80-0715-4376-A138-48D08EFDDC09}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dynambus", "kmdf\bus\dynamic\dynambus.vcxproj", "{EA66789C-5ABF-44C0-9BFD-C7618507E9C8}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WUDFToastMon", "umdf\Toastmon\WUDFToastMon.vcxproj", "{D9D1EDA9-B9AD-4C14-BCDE-DBE39CED25B7}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WUDFToaster", "umdf\func\WUDFToaster.vcxproj", "{B439C142-C570-44A6-B13C-43F70AE05A69}" +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 + {D72A34B9-86A5-4801-B7E3-256CF1F382AD}.Debug|Win32.ActiveCfg = Debug|Win32 + {D72A34B9-86A5-4801-B7E3-256CF1F382AD}.Debug|Win32.Build.0 = Debug|Win32 + {D72A34B9-86A5-4801-B7E3-256CF1F382AD}.Release|Win32.ActiveCfg = Release|Win32 + {D72A34B9-86A5-4801-B7E3-256CF1F382AD}.Release|Win32.Build.0 = Release|Win32 + {D72A34B9-86A5-4801-B7E3-256CF1F382AD}.Debug|x64.ActiveCfg = Debug|x64 + {D72A34B9-86A5-4801-B7E3-256CF1F382AD}.Debug|x64.Build.0 = Debug|x64 + {D72A34B9-86A5-4801-B7E3-256CF1F382AD}.Release|x64.ActiveCfg = Release|x64 + {D72A34B9-86A5-4801-B7E3-256CF1F382AD}.Release|x64.Build.0 = Release|x64 + {EA6EA609-654B-4B46-ABA1-1092BD3FA0AC}.Debug|Win32.ActiveCfg = Debug|Win32 + {EA6EA609-654B-4B46-ABA1-1092BD3FA0AC}.Debug|Win32.Build.0 = Debug|Win32 + {EA6EA609-654B-4B46-ABA1-1092BD3FA0AC}.Release|Win32.ActiveCfg = Release|Win32 + {EA6EA609-654B-4B46-ABA1-1092BD3FA0AC}.Release|Win32.Build.0 = Release|Win32 + {EA6EA609-654B-4B46-ABA1-1092BD3FA0AC}.Debug|x64.ActiveCfg = Debug|x64 + {EA6EA609-654B-4B46-ABA1-1092BD3FA0AC}.Debug|x64.Build.0 = Debug|x64 + {EA6EA609-654B-4B46-ABA1-1092BD3FA0AC}.Release|x64.ActiveCfg = Release|x64 + {EA6EA609-654B-4B46-ABA1-1092BD3FA0AC}.Release|x64.Build.0 = Release|x64 + {70FAA53D-F08B-4131-A5BD-FCAA3EFC48FF}.Debug|Win32.ActiveCfg = Debug|Win32 + {70FAA53D-F08B-4131-A5BD-FCAA3EFC48FF}.Debug|Win32.Build.0 = Debug|Win32 + {70FAA53D-F08B-4131-A5BD-FCAA3EFC48FF}.Release|Win32.ActiveCfg = Release|Win32 + {70FAA53D-F08B-4131-A5BD-FCAA3EFC48FF}.Release|Win32.Build.0 = Release|Win32 + {70FAA53D-F08B-4131-A5BD-FCAA3EFC48FF}.Debug|x64.ActiveCfg = Debug|x64 + {70FAA53D-F08B-4131-A5BD-FCAA3EFC48FF}.Debug|x64.Build.0 = Debug|x64 + {70FAA53D-F08B-4131-A5BD-FCAA3EFC48FF}.Release|x64.ActiveCfg = Release|x64 + {70FAA53D-F08B-4131-A5BD-FCAA3EFC48FF}.Release|x64.Build.0 = Release|x64 + {9FF1CCE5-AD65-444C-97C1-179619AA2479}.Debug|Win32.ActiveCfg = Debug|Win32 + {9FF1CCE5-AD65-444C-97C1-179619AA2479}.Debug|Win32.Build.0 = Debug|Win32 + {9FF1CCE5-AD65-444C-97C1-179619AA2479}.Release|Win32.ActiveCfg = Release|Win32 + {9FF1CCE5-AD65-444C-97C1-179619AA2479}.Release|Win32.Build.0 = Release|Win32 + {9FF1CCE5-AD65-444C-97C1-179619AA2479}.Debug|x64.ActiveCfg = Debug|x64 + {9FF1CCE5-AD65-444C-97C1-179619AA2479}.Debug|x64.Build.0 = Debug|x64 + {9FF1CCE5-AD65-444C-97C1-179619AA2479}.Release|x64.ActiveCfg = Release|x64 + {9FF1CCE5-AD65-444C-97C1-179619AA2479}.Release|x64.Build.0 = Release|x64 + {AFCA7BE8-3FDB-4257-AAAF-EBE85BC05EDD}.Debug|Win32.ActiveCfg = Debug|Win32 + {AFCA7BE8-3FDB-4257-AAAF-EBE85BC05EDD}.Debug|Win32.Build.0 = Debug|Win32 + {AFCA7BE8-3FDB-4257-AAAF-EBE85BC05EDD}.Release|Win32.ActiveCfg = Release|Win32 + {AFCA7BE8-3FDB-4257-AAAF-EBE85BC05EDD}.Release|Win32.Build.0 = Release|Win32 + {AFCA7BE8-3FDB-4257-AAAF-EBE85BC05EDD}.Debug|x64.ActiveCfg = Debug|x64 + {AFCA7BE8-3FDB-4257-AAAF-EBE85BC05EDD}.Debug|x64.Build.0 = Debug|x64 + {AFCA7BE8-3FDB-4257-AAAF-EBE85BC05EDD}.Release|x64.ActiveCfg = Release|x64 + {AFCA7BE8-3FDB-4257-AAAF-EBE85BC05EDD}.Release|x64.Build.0 = Release|x64 + {36EF502E-38C8-4BAB-B7FE-C9ED9194292E}.Debug|Win32.ActiveCfg = Debug|Win32 + {36EF502E-38C8-4BAB-B7FE-C9ED9194292E}.Debug|Win32.Build.0 = Debug|Win32 + {36EF502E-38C8-4BAB-B7FE-C9ED9194292E}.Release|Win32.ActiveCfg = Release|Win32 + {36EF502E-38C8-4BAB-B7FE-C9ED9194292E}.Release|Win32.Build.0 = Release|Win32 + {36EF502E-38C8-4BAB-B7FE-C9ED9194292E}.Debug|x64.ActiveCfg = Debug|x64 + {36EF502E-38C8-4BAB-B7FE-C9ED9194292E}.Debug|x64.Build.0 = Debug|x64 + {36EF502E-38C8-4BAB-B7FE-C9ED9194292E}.Release|x64.ActiveCfg = Release|x64 + {36EF502E-38C8-4BAB-B7FE-C9ED9194292E}.Release|x64.Build.0 = Release|x64 + {A6E7CA25-D12B-4C39-9A01-BEEE71E04F37}.Debug|Win32.ActiveCfg = Debug|Win32 + {A6E7CA25-D12B-4C39-9A01-BEEE71E04F37}.Debug|Win32.Build.0 = Debug|Win32 + {A6E7CA25-D12B-4C39-9A01-BEEE71E04F37}.Release|Win32.ActiveCfg = Release|Win32 + {A6E7CA25-D12B-4C39-9A01-BEEE71E04F37}.Release|Win32.Build.0 = Release|Win32 + {A6E7CA25-D12B-4C39-9A01-BEEE71E04F37}.Debug|x64.ActiveCfg = Debug|x64 + {A6E7CA25-D12B-4C39-9A01-BEEE71E04F37}.Debug|x64.Build.0 = Debug|x64 + {A6E7CA25-D12B-4C39-9A01-BEEE71E04F37}.Release|x64.ActiveCfg = Release|x64 + {A6E7CA25-D12B-4C39-9A01-BEEE71E04F37}.Release|x64.Build.0 = Release|x64 + {A5E50982-981B-40B9-BEE6-1E15581FE947}.Debug|Win32.ActiveCfg = Debug|Win32 + {A5E50982-981B-40B9-BEE6-1E15581FE947}.Debug|Win32.Build.0 = Debug|Win32 + {A5E50982-981B-40B9-BEE6-1E15581FE947}.Release|Win32.ActiveCfg = Release|Win32 + {A5E50982-981B-40B9-BEE6-1E15581FE947}.Release|Win32.Build.0 = Release|Win32 + {A5E50982-981B-40B9-BEE6-1E15581FE947}.Debug|x64.ActiveCfg = Debug|x64 + {A5E50982-981B-40B9-BEE6-1E15581FE947}.Debug|x64.Build.0 = Debug|x64 + {A5E50982-981B-40B9-BEE6-1E15581FE947}.Release|x64.ActiveCfg = Release|x64 + {A5E50982-981B-40B9-BEE6-1E15581FE947}.Release|x64.Build.0 = Release|x64 + {FB919684-BF55-4E3D-95FA-CBCB7B0D8759}.Debug|Win32.ActiveCfg = Debug|Win32 + {FB919684-BF55-4E3D-95FA-CBCB7B0D8759}.Debug|Win32.Build.0 = Debug|Win32 + {FB919684-BF55-4E3D-95FA-CBCB7B0D8759}.Release|Win32.ActiveCfg = Release|Win32 + {FB919684-BF55-4E3D-95FA-CBCB7B0D8759}.Release|Win32.Build.0 = Release|Win32 + {FB919684-BF55-4E3D-95FA-CBCB7B0D8759}.Debug|x64.ActiveCfg = Debug|x64 + {FB919684-BF55-4E3D-95FA-CBCB7B0D8759}.Debug|x64.Build.0 = Debug|x64 + {FB919684-BF55-4E3D-95FA-CBCB7B0D8759}.Release|x64.ActiveCfg = Release|x64 + {FB919684-BF55-4E3D-95FA-CBCB7B0D8759}.Release|x64.Build.0 = Release|x64 + {CA0E203E-4C96-466F-BF19-24F42DFC0C89}.Debug|Win32.ActiveCfg = Debug|Win32 + {CA0E203E-4C96-466F-BF19-24F42DFC0C89}.Debug|Win32.Build.0 = Debug|Win32 + {CA0E203E-4C96-466F-BF19-24F42DFC0C89}.Release|Win32.ActiveCfg = Release|Win32 + {CA0E203E-4C96-466F-BF19-24F42DFC0C89}.Release|Win32.Build.0 = Release|Win32 + {CA0E203E-4C96-466F-BF19-24F42DFC0C89}.Debug|x64.ActiveCfg = Debug|x64 + {CA0E203E-4C96-466F-BF19-24F42DFC0C89}.Debug|x64.Build.0 = Debug|x64 + {CA0E203E-4C96-466F-BF19-24F42DFC0C89}.Release|x64.ActiveCfg = Release|x64 + {CA0E203E-4C96-466F-BF19-24F42DFC0C89}.Release|x64.Build.0 = Release|x64 + {5094C704-35B0-4A25-AD30-570C49BCAE82}.Debug|Win32.ActiveCfg = Debug|Win32 + {5094C704-35B0-4A25-AD30-570C49BCAE82}.Debug|Win32.Build.0 = Debug|Win32 + {5094C704-35B0-4A25-AD30-570C49BCAE82}.Release|Win32.ActiveCfg = Release|Win32 + {5094C704-35B0-4A25-AD30-570C49BCAE82}.Release|Win32.Build.0 = Release|Win32 + {5094C704-35B0-4A25-AD30-570C49BCAE82}.Debug|x64.ActiveCfg = Debug|x64 + {5094C704-35B0-4A25-AD30-570C49BCAE82}.Debug|x64.Build.0 = Debug|x64 + {5094C704-35B0-4A25-AD30-570C49BCAE82}.Release|x64.ActiveCfg = Release|x64 + {5094C704-35B0-4A25-AD30-570C49BCAE82}.Release|x64.Build.0 = Release|x64 + {C79A9F42-BDB4-4B5A-B7B5-1BEBF7752207}.Debug|Win32.ActiveCfg = Debug|Win32 + {C79A9F42-BDB4-4B5A-B7B5-1BEBF7752207}.Debug|Win32.Build.0 = Debug|Win32 + {C79A9F42-BDB4-4B5A-B7B5-1BEBF7752207}.Release|Win32.ActiveCfg = Release|Win32 + {C79A9F42-BDB4-4B5A-B7B5-1BEBF7752207}.Release|Win32.Build.0 = Release|Win32 + {C79A9F42-BDB4-4B5A-B7B5-1BEBF7752207}.Debug|x64.ActiveCfg = Debug|x64 + {C79A9F42-BDB4-4B5A-B7B5-1BEBF7752207}.Debug|x64.Build.0 = Debug|x64 + {C79A9F42-BDB4-4B5A-B7B5-1BEBF7752207}.Release|x64.ActiveCfg = Release|x64 + {C79A9F42-BDB4-4B5A-B7B5-1BEBF7752207}.Release|x64.Build.0 = Release|x64 + {965C0E80-0715-4376-A138-48D08EFDDC09}.Debug|Win32.ActiveCfg = Debug|Win32 + {965C0E80-0715-4376-A138-48D08EFDDC09}.Debug|Win32.Build.0 = Debug|Win32 + {965C0E80-0715-4376-A138-48D08EFDDC09}.Release|Win32.ActiveCfg = Release|Win32 + {965C0E80-0715-4376-A138-48D08EFDDC09}.Release|Win32.Build.0 = Release|Win32 + {965C0E80-0715-4376-A138-48D08EFDDC09}.Debug|x64.ActiveCfg = Debug|x64 + {965C0E80-0715-4376-A138-48D08EFDDC09}.Debug|x64.Build.0 = Debug|x64 + {965C0E80-0715-4376-A138-48D08EFDDC09}.Release|x64.ActiveCfg = Release|x64 + {965C0E80-0715-4376-A138-48D08EFDDC09}.Release|x64.Build.0 = Release|x64 + {EA66789C-5ABF-44C0-9BFD-C7618507E9C8}.Debug|Win32.ActiveCfg = Debug|Win32 + {EA66789C-5ABF-44C0-9BFD-C7618507E9C8}.Debug|Win32.Build.0 = Debug|Win32 + {EA66789C-5ABF-44C0-9BFD-C7618507E9C8}.Release|Win32.ActiveCfg = Release|Win32 + {EA66789C-5ABF-44C0-9BFD-C7618507E9C8}.Release|Win32.Build.0 = Release|Win32 + {EA66789C-5ABF-44C0-9BFD-C7618507E9C8}.Debug|x64.ActiveCfg = Debug|x64 + {EA66789C-5ABF-44C0-9BFD-C7618507E9C8}.Debug|x64.Build.0 = Debug|x64 + {EA66789C-5ABF-44C0-9BFD-C7618507E9C8}.Release|x64.ActiveCfg = Release|x64 + {EA66789C-5ABF-44C0-9BFD-C7618507E9C8}.Release|x64.Build.0 = Release|x64 + {D9D1EDA9-B9AD-4C14-BCDE-DBE39CED25B7}.Debug|Win32.ActiveCfg = Debug|Win32 + {D9D1EDA9-B9AD-4C14-BCDE-DBE39CED25B7}.Debug|Win32.Build.0 = Debug|Win32 + {D9D1EDA9-B9AD-4C14-BCDE-DBE39CED25B7}.Release|Win32.ActiveCfg = Release|Win32 + {D9D1EDA9-B9AD-4C14-BCDE-DBE39CED25B7}.Release|Win32.Build.0 = Release|Win32 + {D9D1EDA9-B9AD-4C14-BCDE-DBE39CED25B7}.Debug|x64.ActiveCfg = Debug|x64 + {D9D1EDA9-B9AD-4C14-BCDE-DBE39CED25B7}.Debug|x64.Build.0 = Debug|x64 + {D9D1EDA9-B9AD-4C14-BCDE-DBE39CED25B7}.Release|x64.ActiveCfg = Release|x64 + {D9D1EDA9-B9AD-4C14-BCDE-DBE39CED25B7}.Release|x64.Build.0 = Release|x64 + {B439C142-C570-44A6-B13C-43F70AE05A69}.Debug|Win32.ActiveCfg = Debug|Win32 + {B439C142-C570-44A6-B13C-43F70AE05A69}.Debug|Win32.Build.0 = Debug|Win32 + {B439C142-C570-44A6-B13C-43F70AE05A69}.Release|Win32.ActiveCfg = Release|Win32 + {B439C142-C570-44A6-B13C-43F70AE05A69}.Release|Win32.Build.0 = Release|Win32 + {B439C142-C570-44A6-B13C-43F70AE05A69}.Debug|x64.ActiveCfg = Debug|x64 + {B439C142-C570-44A6-B13C-43F70AE05A69}.Debug|x64.Build.0 = Debug|x64 + {B439C142-C570-44A6-B13C-43F70AE05A69}.Release|x64.ActiveCfg = Release|x64 + {B439C142-C570-44A6-B13C-43F70AE05A69}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {D72A34B9-86A5-4801-B7E3-256CF1F382AD} = {ADC63E58-1F2E-4A3A-A760-779FD9EAD00E} + {EA6EA609-654B-4B46-ABA1-1092BD3FA0AC} = {2C550FE4-29CD-40A8-836D-313696A3442B} + {70FAA53D-F08B-4131-A5BD-FCAA3EFC48FF} = {470F597C-0DBC-44AC-B69B-594ACB7B1ED0} + {9FF1CCE5-AD65-444C-97C1-179619AA2479} = {F39421E1-95F5-4DEE-A78F-B522BE05FEB0} + {AFCA7BE8-3FDB-4257-AAAF-EBE85BC05EDD} = {9C9637D5-A764-49F0-AF7D-C7F123CEB343} + {36EF502E-38C8-4BAB-B7FE-C9ED9194292E} = {0293B300-378A-4D97-B93C-0690C8BC8019} + {A6E7CA25-D12B-4C39-9A01-BEEE71E04F37} = {F1085E78-8F4D-4044-B58E-BDA9E7E35A0F} + {A5E50982-981B-40B9-BEE6-1E15581FE947} = {0BFA46E4-075E-475A-B73E-6FAB41B71B68} + {FB919684-BF55-4E3D-95FA-CBCB7B0D8759} = {AC2EC9A6-11CC-4F9C-BBA6-2E78FFFF5C84} + {CA0E203E-4C96-466F-BF19-24F42DFC0C89} = {A6681C1E-BA3B-4DD1-A641-DA27776A0319} + {5094C704-35B0-4A25-AD30-570C49BCAE82} = {B2431307-5557-4D2E-AB33-F38E7D8680A4} + {C79A9F42-BDB4-4B5A-B7B5-1BEBF7752207} = {F956DAF1-330C-458A-B659-A30D5CE74374} + {965C0E80-0715-4376-A138-48D08EFDDC09} = {0A0AA60F-8EAD-4BC1-AB00-315A13174032} + {EA66789C-5ABF-44C0-9BFD-C7618507E9C8} = {E2B2526B-3B31-4309-A22A-1236F16CB840} + {D9D1EDA9-B9AD-4C14-BCDE-DBE39CED25B7} = {85556EF1-3D39-442F-BB0A-B3B5D6DE7352} + {B439C142-C570-44A6-B13C-43F70AE05A69} = {BF917E30-8925-4467-A1BF-CBD9E0AC7D12} + {F39421E1-95F5-4DEE-A78F-B522BE05FEB0} = {29C3C959-4CBC-4D9D-B660-2135BF00CC55} + {9C9637D5-A764-49F0-AF7D-C7F123CEB343} = {29C3C959-4CBC-4D9D-B660-2135BF00CC55} + {0293B300-378A-4D97-B93C-0690C8BC8019} = {29C3C959-4CBC-4D9D-B660-2135BF00CC55} + {F1085E78-8F4D-4044-B58E-BDA9E7E35A0F} = {29C3C959-4CBC-4D9D-B660-2135BF00CC55} + {0BFA46E4-075E-475A-B73E-6FAB41B71B68} = {929B678F-2875-4DEE-AA34-2C255D4B84EA} + {AC2EC9A6-11CC-4F9C-BBA6-2E78FFFF5C84} = {1EFF08AC-2EAE-4E76-BAA7-0361BDA16DB2} + {1EFF08AC-2EAE-4E76-BAA7-0361BDA16DB2} = {929B678F-2875-4DEE-AA34-2C255D4B84EA} + {A6681C1E-BA3B-4DD1-A641-DA27776A0319} = {1EFF08AC-2EAE-4E76-BAA7-0361BDA16DB2} + {B2431307-5557-4D2E-AB33-F38E7D8680A4} = {A457C9CB-C02C-4835-8B4F-D3C03956C246} + {A457C9CB-C02C-4835-8B4F-D3C03956C246} = {929B678F-2875-4DEE-AA34-2C255D4B84EA} + {F956DAF1-330C-458A-B659-A30D5CE74374} = {A457C9CB-C02C-4835-8B4F-D3C03956C246} + {0A0AA60F-8EAD-4BC1-AB00-315A13174032} = {C1623324-F98A-40DF-B5BD-A1D4465653C5} + {C1623324-F98A-40DF-B5BD-A1D4465653C5} = {929B678F-2875-4DEE-AA34-2C255D4B84EA} + {E2B2526B-3B31-4309-A22A-1236F16CB840} = {C1623324-F98A-40DF-B5BD-A1D4465653C5} + {85556EF1-3D39-442F-BB0A-B3B5D6DE7352} = {B9DF6659-83A8-42FF-80CD-A03012E07CCE} + {BF917E30-8925-4467-A1BF-CBD9E0AC7D12} = {B9DF6659-83A8-42FF-80CD-A03012E07CCE} + EndGlobalSection +EndGlobal diff --git a/general/toaster/toastDrv/umdf/Toastmon/Device.cpp b/general/toaster/toastDrv/umdf/Toastmon/Device.cpp new file mode 100644 index 000000000..5b2bde411 --- /dev/null +++ b/general/toaster/toastDrv/umdf/Toastmon/Device.cpp @@ -0,0 +1,318 @@ +/*++ + +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. + + This sample demonstrates how to register for PnP event notification + for an interface class, and how to handle arrival events. + +Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + +#include "internal.h" +#include "device.tmh" + +HRESULT +CMyDevice::CreateInstance( + _In_ IWDFDriver * FxDriver, + _In_ IWDFDeviceInitialize * FxDeviceInit, + _Out_ PCMyDevice * MyDevice + ) +/*++ + + Routine Description: + + This method creates and initializs an instance of the driver's + device callback object. + + Arguments: + + FxDeviceInit - the settings for the device. + + MyDevice - a location to store the referenced pointer to the device object. + + Return Value: + + Status + +--*/ +{ + PCMyDevice myDevice; + HRESULT hr; + + // + // Allocate a new instance of the device class. + // + + myDevice = new CMyDevice(); + + if (NULL == myDevice) + { + return E_OUTOFMEMORY; + } + + // + // Initialize the instance. + // + + hr = myDevice->Initialize(FxDriver, FxDeviceInit); + + if (SUCCEEDED(hr)) + { + *MyDevice = myDevice; + } + else + { + myDevice->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. + + Then it registers for toaster device notifications. + + + Arguments: + + FxDeviceInit - the settings for this device. + + Return Value: + + status. + +--*/ +{ + CComPtr fxDevice; + HRESULT hr; + + // + // Save a weak reference to the Fx driver object. We'll need it to create + // CMyRemoteTarget objects. + // + + m_FxDriver = FxDriver; + + // + // 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(); + + // + // Create a new FX device object and assign the new callback object to + // handle any device level events that occur. + // + hr = FxDriver->CreateDevice(FxDeviceInit, unknown, &fxDevice); + + unknown->Release(); + } + + // + // If that succeeded then set our FxDevice member variable. + // + + CComPtr fxDevice2; + if (SUCCEEDED(hr)) + { + // + // Q.I. for the latest version of this interface. + // + hr = fxDevice->QueryInterface(IID_PPV_ARGS(&fxDevice2)); + } + + if (SUCCEEDED(hr)) + { + // + // Store a weak reference to the IWDFDevice2 interface. Since this object + // is partnered with the framework object they have the same lifespan - + // there is no need for an additional reference. + // + + m_FxDevice = fxDevice2; + } + + 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. + + Return Value: + + status + +--*/ +{ + HRESULT hr = S_OK; + + // + // Register for TOASTER device interface change notification. + // We will get OnRemoteInterfaceArrival() calls when a remote toaster + // device is started. + // + // Arrival notification will be sent for all existing and future toaster + // devices. + // + // The framework will take care of unregistration when the device unloads. + // + hr = m_FxDevice->RegisterRemoteInterfaceNotification(&GUID_DEVINTERFACE_TOASTER, + true); + + 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. + + 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(IPnpCallbackRemoteInterfaceNotification))) + { + *Object = QueryIPnpCallbackRemoteInterfaceNotification(); + hr = S_OK; + } + else + { + hr = CUnknown::QueryInterface(InterfaceId, Object); + } + + return hr; +} + +// +// IPnpCallbackRemoteInterfaceNotification +// + +void +STDMETHODCALLTYPE +CMyDevice::OnRemoteInterfaceArrival( + _In_ IWDFRemoteInterfaceInitialize * FxRemoteInterfaceInit + ) +/*++ + + Routine Description: + + This method is called by the framework when a new remote interface has come + online. These calls will only occur one at a time. + + Arguments: + + FxRemoteInterfaceInit - An identifier for the remote interface. + +--*/ +{ + HRESULT hr = S_OK; + + + // + // Create a new FX remote interface object and assign a NULL callback + // object since we don't care to handle any remote interface level events + // that occur. + // + + CComPtr fxRemoteInterface; + + hr = m_FxDevice->CreateRemoteInterface(FxRemoteInterfaceInit, + NULL, + &fxRemoteInterface); + + // + // Create an instance of CMyRemoteTarget which will open the remote device + // and post I/O requests to it. + // + + PCMyRemoteTarget myRemoteTarget = NULL; + if (SUCCEEDED(hr)) + { + hr = CMyRemoteTarget::CreateInstance(this, + m_FxDriver, + m_FxDevice, + fxRemoteInterface, + &myRemoteTarget); + } + + if (SUCCEEDED(hr)) + { + if (myRemoteTarget != NULL) + { + // + // Add to our list + // + InsertHeadList(&m_MyRemoteTargets, &myRemoteTarget->m_Entry); + + // + // Release, since framework will keep a reference + // + myRemoteTarget->Release(); + } + } + + if (FAILED(hr)) + { + if (fxRemoteInterface != NULL) + { + // + // We failed to create the CMyRemoteTarget, delete the + // RemoteInterface object + // + fxRemoteInterface->DeleteWdfObject(); + } + } +} + diff --git a/general/toaster/toastDrv/umdf/Toastmon/Device.h b/general/toaster/toastDrv/umdf/Toastmon/Device.h new file mode 100644 index 000000000..c457a8da8 --- /dev/null +++ b/general/toaster/toastDrv/umdf/Toastmon/Device.h @@ -0,0 +1,162 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved + +Module Name: + + Device.h + +Abstract: + + This module contains the type definitions for the UMDF sample + driver's device callback class. + +Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + +#pragma once +#include "initguid.h" + + +//{781EF630-72B2-11d2-B852-00C04FAD5171} +DEFINE_GUID(GUID_DEVINTERFACE_TOASTER, 0x781EF630, 0x72B2, 0x11d2, 0xB8, 0x52, 0x00, 0xC0, 0x4F, 0xAD, 0x51, 0x71); + + +// +// Class for the sample device. +// + +class CMyDevice : public CUnknown, + public IPnpCallbackRemoteInterfaceNotification +{ + // + // Private data members. + // +private: + + // + // Weak reference to the WDF device object. + // + + IWDFDevice2 *m_FxDevice; + + // + // Weak reference to the WDF driver object + // + + IWDFDriver *m_FxDriver; + + // + // Head of remote target list + // + + LIST_ENTRY m_MyRemoteTargets; + + // + // Private methods. + // +private: + + // + // Protected methods + // +protected: + CMyDevice( + VOID + ) : + m_FxDevice(NULL), + m_FxDriver(NULL) + { + InitializeListHead(&m_MyRemoteTargets); + } + + 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 * MyDevice + ); + + HRESULT + Configure( + VOID + ); + + ~CMyDevice( + VOID + ) + { + ATLASSERT(IsListEmpty(&m_MyRemoteTargets)); + } + + // + // 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 + ); + + // + // IPnpCallbackRemoteInterfaceNotification + // + void + STDMETHODCALLTYPE + OnRemoteInterfaceArrival( + _In_ IWDFRemoteInterfaceInitialize * FxRemoteInterfaceInit + ); + IPnpCallbackRemoteInterfaceNotification * + QueryIPnpCallbackRemoteInterfaceNotification( + VOID + ) + { + AddRef(); + return static_cast(this); + } + +}; diff --git a/general/toaster/toastDrv/umdf/Toastmon/Driver.cpp b/general/toaster/toastDrv/umdf/Toastmon/Driver.cpp new file mode 100644 index 000000000..adf74dc09 --- /dev/null +++ b/general/toaster/toastDrv/umdf/Toastmon/Driver.cpp @@ -0,0 +1,207 @@ +/*++ + +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 * MyDriver + ) +/*++ + + 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: + + MyDriver - a location to store a referenced pointer to the new instance + + Return Value: + + S_OK if successful, or error otherwise. + +--*/ +{ + PCMyDriver myDriver; + HRESULT hr; + + // + // Allocate the callback object. + // + + myDriver = new CMyDriver(); + + if (NULL == myDriver) + { + return E_OUTOFMEMORY; + } + + // + // Initialize the callback object. + // + + hr = myDriver->Initialize(); + + if (SUCCEEDED(hr)) + { + // + // Store a pointer to the new, initialized object in the output + // parameter. + // + + *MyDriver = myDriver; + } + else + { + + // + // Release the reference on the driver object to get it to delete + // itself. + // + + myDriver->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 *FxDriver, + _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 + +--*/ +{ + PCMyDevice myDevice = NULL; + + HRESULT hr; + + // + // Create a new instance of our device callback object + // + + hr = CMyDevice::CreateInstance(FxDriver, FxDeviceInit, &myDevice); + + // + // 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 = myDevice->Configure(); + } + + // + // Release the reference on the device callback object now that it's been + // associated with an fx device object. + // + + if (NULL != myDevice) + { + myDevice->Release(); + } + + return hr; +} diff --git a/general/toaster/toastDrv/umdf/Toastmon/Driver.h b/general/toaster/toastDrv/umdf/Toastmon/Driver.h new file mode 100644 index 000000000..aecb97655 --- /dev/null +++ b/general/toaster/toastDrv/umdf/Toastmon/Driver.h @@ -0,0 +1,146 @@ +/*++ + +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 referenced 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 * MyDriver + ); + + // + // COM methods + // +public: + + // + // IDriverEntry methods + // + + virtual + HRESULT + STDMETHODCALLTYPE + OnInitialize( + _In_ IWDFDriver * /* FxDriver */ + ) + { + return S_OK; + } + + virtual + HRESULT + STDMETHODCALLTYPE + OnDeviceAdd( + _In_ IWDFDriver * FxDriver, + _In_ IWDFDeviceInitialize * FxDeviceInit + ); + + virtual + VOID + STDMETHODCALLTYPE + OnDeinitialize( + _In_ IWDFDriver * /* FxDriver */ + ) + { + return; + } + + // + // IUnknown methods. + // + // We have to implement basic ones here that redirect to the + // base class because 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/toaster/toastDrv/umdf/Toastmon/RemoteTarget.cpp b/general/toaster/toastDrv/umdf/Toastmon/RemoteTarget.cpp new file mode 100644 index 000000000..88ea7f8a6 --- /dev/null +++ b/general/toaster/toastDrv/umdf/Toastmon/RemoteTarget.cpp @@ -0,0 +1,538 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved. + +Module Name: + + RemoteTarget.cpp + +Abstract: + + This module contains the implementation of the UMDF sample driver's + remote interface and remote target callback object. + + This sample demonstrates how to open the remote target and register + and respond to device change notification. + +Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + +#include "internal.h" +#include "remotetarget.tmh" + + +DWORD WINAPI _ThreadProc( + _In_ LPVOID lpParameter + ) +{ + CMyRemoteTarget *MyRemoteTarget = (CMyRemoteTarget*)lpParameter; + + return MyRemoteTarget->ThreadProc(); +} + +HRESULT +CMyRemoteTarget::CreateInstance( + _In_ CMyDevice * MyDevice, + _In_ IWDFDriver * FxDriver, + _In_ IWDFDevice2 * FxDevice, + _In_ IWDFRemoteInterface * FxRemoteInterface, + _Out_ PCMyRemoteTarget * MyRemoteTarget + ) +/*++ + + Routine Description: + + This method creates and initializs an instance of the driver's + remote target callback object. + + Arguments: + + FxRemoteInterfaceInit - An identifier that specifies the remote toaster device. + + MyDevice - a location to store the referenced pointer to the device object. + + Return Value: + + Status + +--*/ +{ + PCMyRemoteTarget myRemoteTarget; + HRESULT hr; + + // + // Allocate a new instance of the remote target class. + // + + myRemoteTarget = new CMyRemoteTarget(); + + if (NULL == myRemoteTarget) + { + return E_OUTOFMEMORY; + } + + // + // Initialize the instance. + // + + hr = myRemoteTarget->Initialize(MyDevice, + FxDriver, + FxDevice, + FxRemoteInterface); + + if (SUCCEEDED(hr)) + { + *MyRemoteTarget = myRemoteTarget; + } + else + { + myRemoteTarget->Release(); + } + + return hr; +} + +HRESULT +CMyRemoteTarget::Initialize( + _In_ CMyDevice * MyDevice, + _In_ IWDFDriver * FxDriver, + _In_ IWDFDevice2 * FxDevice, + _In_ IWDFRemoteInterface * FxRemoteInterface + ) +/*++ + + Routine Description: + + This method initializes the remote target callback object and creates + the partner remote target object. + + Arguments: + + FxRemoteInterface - the identifier for the remote toaster device. + + Return Value: + + status. + +--*/ +{ + HRESULT hr; + + CComPtr fxWriteRequest; + CComPtr fxReadRequest; + + // + // Save a weak reference to the class that created us + // so we can notify it when we get removed + // + + m_MyDevice = MyDevice; + + // + // 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(); + + + // + // Create a new FX remote target object and assign the new callback + // object to handle any remote target level events that occur. + // + hr = FxDevice->CreateRemoteTarget(unknown, + FxRemoteInterface, + &m_FxTarget); + + if (SUCCEEDED(hr)) + { + // + // Open and start the remote target. Note that this sample doesn't + // perform any impersonation, so we're running as "Local Service" + // since that is what UMDF driver runs as. + // + // Make sure that your Toaster devices all have security ACLs that + // permit "Local Service" to access the device. The Win7 toaster + // sample driver INFs have been updated for this. However, if you + // had previously installed pre-Win7 versions of the Toaster sample + // driver, the security settings will not be updated by a new driver + // install, unless the Toaster device class key is deleted. + // + + hr = m_FxTarget->OpenRemoteInterface(FxRemoteInterface, + NULL, + GENERIC_READ | GENERIC_WRITE, + NULL); + } + + if (SUCCEEDED(hr)) + { + // + // Create a new FX request object and assign the new callback + // object to handle any request level events that occur. + // + hr = FxDevice->CreateRequest(unknown, + FxRemoteInterface, + &fxWriteRequest); + } + + if (SUCCEEDED(hr)) + { + // + // We want to save the newer IWDFIoRequest2 interface instead. + // + hr = fxWriteRequest->QueryInterface(IID_PPV_ARGS(&m_FxWriteRequest)); + } + + if (SUCCEEDED(hr)) + { + // + // Create a buffer for the write request + // + hr = FxDriver->CreateWdfMemory(WRITE_BUF_SIZE, + NULL, + FxRemoteInterface, + &m_FxWriteMemory); + } + + if (SUCCEEDED(hr)) + { + // + // Create a new FX remote target object and assign the new callback + // object to handle any remote target level events that occur. + // + hr = FxDevice->CreateRequest(unknown, + FxRemoteInterface, + &fxReadRequest); + } + + if (SUCCEEDED(hr)) + { + // + // We want to save the newer IWDFIoRequest2 interface instead. + // + hr = fxReadRequest->QueryInterface(IID_PPV_ARGS(&m_FxReadRequest)); + } + + if (SUCCEEDED(hr)) + { + // + // Create a buffer for the read request + // + hr = FxDriver->CreateWdfMemory(READ_BUF_SIZE, + NULL, + FxRemoteInterface, + &m_FxReadMemory); + } + + if (SUCCEEDED(hr)) + { + // + // Create/Start the thread which will post I/O requests to the remote + // target. + // + // NOTE: This would not be a typical driver pattern. Normally, your + // driver would receive some I/O from another caller and you'd + // use this I/O as a trigger to post I/O to the remote target. + // You may choose to forward the request directly with no changes, + // modify the request before sending, or create an entirely + // separate request. + // + m_hThread = CreateThread(NULL, + 0, + _ThreadProc, + this, + 0, + NULL); + + if (m_hThread == NULL) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + } + } + + unknown->Release(); + + return hr; +} + +DWORD WINAPI CMyRemoteTarget::ThreadProc( + VOID + ) +/*++ + + Routine Description: + + This method is the main loop for a thread that posts I/O requests to + the remote target. The main loop continues until the remote target + is deleted, which happens in the Dispose() method. + +--*/ +{ + BOOL bContinue = TRUE; + + while(bContinue) + { + Sleep(100); + + switch(m_FxTarget->GetState()) + { + case WdfIoTargetStarted: + PostIoRequests(); + break; + + case WdfIoTargetClosedForQueryRemove: + break; + + case WdfIoTargetClosed: + case WdfIoTargetDeleted: + default: + bContinue = false; + break; + } + } + + return 0; +} + +void +CMyRemoteTarget::PostIoRequests( + VOID + ) +{ + HRESULT hr; + + if (!m_WriteInProgress) + { + // + // Mark that the request has been sent, so we don't try to send + // again before the completion routine runs. + // + m_WriteInProgress = true; + + hr = m_FxTarget->FormatRequestForWrite(m_FxWriteRequest, + NULL, + m_FxWriteMemory, + 0, + 0); + + if (SUCCEEDED(hr)) + { + m_FxWriteRequest->SetCompletionCallback(this, NULL); + + hr = m_FxWriteRequest->Send(m_FxTarget, 0, 0); + } + + if (FAILED(hr)) + { + m_WriteInProgress = false; + } + } + + if (!m_ReadInProgress) + { + // + // Mark that the request has been sent, so we don't try to send + // again before the completion routine runs. + // + m_ReadInProgress = true; + + hr = m_FxTarget->FormatRequestForRead(m_FxReadRequest, + NULL, + m_FxReadMemory, + 0, + 0); + + if (SUCCEEDED(hr)) + { + m_FxReadRequest->SetCompletionCallback(this, NULL); + + hr = m_FxReadRequest->Send(m_FxTarget, 0, 0); + } + + if (FAILED(hr)) + { + m_ReadInProgress = true; + } + } +} + +HRESULT +CMyRemoteTarget::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. + + 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(IRequestCallbackRequestCompletion))) + { + *Object = QueryIRequestCallbackRequestCompletion(); + hr = S_OK; + } + else if(IsEqualIID(InterfaceId, __uuidof(IRemoteTargetCallbackRemoval))) + { + *Object = QueryIRemoteTargetCallbackRemoval(); + hr = S_OK; + } + else if(IsEqualIID(InterfaceId, __uuidof(IObjectCleanup))) + { + *Object = QueryIObjectCleanup(); + hr = S_OK; + } + else + { + hr = CUnknown::QueryInterface(InterfaceId, Object); + } + + return hr; +} + +// +// IRequestCallbackRequestCompletion +// +void +STDMETHODCALLTYPE +CMyRemoteTarget::OnCompletion( + _In_ IWDFIoRequest * FxRequest, + _In_ IWDFIoTarget * /* FxTarget */, + _In_ IWDFRequestCompletionParams * /* Params */, + _In_ void* /* Context */ + ) +{ + IWDFRequestCompletionParams * CompletionParams; + + FxRequest->GetCompletionParams(&CompletionParams); + + if (CompletionParams->GetCompletedRequestType() == WdfRequestRead) + { + m_FxReadRequest->Reuse(E_FAIL); + m_ReadInProgress = false; + } + if (CompletionParams->GetCompletedRequestType() == WdfRequestWrite) + { + m_FxWriteRequest->Reuse(E_FAIL); + m_WriteInProgress = false; + } + + CompletionParams->Release(); +} + +// +// IRemoteTargetCallbackRemoval +// +BOOL +STDMETHODCALLTYPE +CMyRemoteTarget::OnRemoteTargetQueryRemove( + _In_ IWDFRemoteTarget * /* FxTarget */ + ) +{ + m_FxTarget->CloseForQueryRemove(); + + // + // Return FALSE if you want to VETO the Query + // + + return TRUE; +} + +VOID +STDMETHODCALLTYPE +CMyRemoteTarget::OnRemoteTargetRemoveCanceled( + _In_ IWDFRemoteTarget * /* FxTarget */ + ) +{ + if (FAILED(m_FxTarget->Reopen())) + { + m_FxTarget->Close(); + } +} + +VOID +STDMETHODCALLTYPE +CMyRemoteTarget::OnRemoteTargetRemoveComplete( + _In_ IWDFRemoteTarget * /* FxTarget */ + ) +{ + // + // The remote device has been removed, so we close the target for good. + // The rest of cleanup will occur when the framework handles the removal + // of the RemoteInterface + // + + m_FxTarget->Close(); +} + + +// +// IObjectCleanup +// + +void +STDMETHODCALLTYPE +CMyRemoteTarget::OnCleanup( + _In_ IWDFObject * /* FxObject */ + ) +{ + if (m_hThread != NULL) + { + if (m_FxTarget->GetState() != WdfIoTargetClosed) + { + // + // Close the target if it's still open. + // + // If the target has already gone through RemoveComplete, it should + // already be closed. + // + // If the Target interface is disabled without the device being + // removed, the target will still be open until now. + // + // If the ToastMon device itself is removed while a target is + // still active, we'll now close it. + // + m_FxTarget->Close(); + } + + // + // Wait for the Thread to complete + // + WaitForSingleObject(m_hThread, INFINITE); + + CloseHandle(m_hThread); + + m_hThread = NULL; + + // + // Remove ourselves from the list of Remote Targets + // + RemoveEntryList(&m_Entry); + } + + m_FxTarget = NULL; + m_FxWriteRequest = NULL; + m_FxReadRequest = NULL; +} + diff --git a/general/toaster/toastDrv/umdf/Toastmon/RemoteTarget.h b/general/toaster/toastDrv/umdf/Toastmon/RemoteTarget.h new file mode 100644 index 000000000..15d91dad5 --- /dev/null +++ b/general/toaster/toastDrv/umdf/Toastmon/RemoteTarget.h @@ -0,0 +1,228 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved + +Module Name: + + RemoteTarget.h + +Abstract: + + This module contains the type definitions for the UMDF sample + driver's remote target callback class. + +Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + +#pragma once + + +#define READ_BUF_SIZE 100 +#define WRITE_BUF_SIZE 120 + + +class CMyRemoteTarget : public CUnknown, + public IRequestCallbackRequestCompletion, + public IRemoteTargetCallbackRemoval, + public IObjectCleanup +{ + // + // Public data members. + // +public: + + // + // This is the Entry for the list of Remote Targets. + // The List head is held by CMyDevice. + // + LIST_ENTRY m_Entry; + + + // + // Private data members. + // +private: + + CMyDevice * m_MyDevice; // Weak reference + + // + // The handle for a thread to post I/O requests to the remote target + // + HANDLE m_hThread; + + CComPtr m_FxTarget; + + bool m_WriteInProgress; + CComPtr m_FxWriteRequest; + CComPtr m_FxWriteMemory; + + bool m_ReadInProgress; + CComPtr m_FxReadRequest; + CComPtr m_FxReadMemory; + + // + // Private methods. + // +private: + + void + PostIoRequests( + VOID + ); + + // + // Protected methods + // +protected: + + CMyRemoteTarget( + VOID + ) : + m_MyDevice(NULL), + m_hThread(NULL), + m_WriteInProgress(false), + m_ReadInProgress(false) + + { + } + + HRESULT + Initialize( + _In_ CMyDevice * MyDevice, + _In_ IWDFDriver * FxDriver, + _In_ IWDFDevice2 * FxDevice, + _In_ IWDFRemoteInterface * FxRemoteInterface + ); + + // + // Public methods + // +public: + + // + // The factory method used to create an instance of this driver. + // + static + HRESULT + CreateInstance( + _In_ CMyDevice * MyDevice, + _In_ IWDFDriver * FxDriver, + _In_ IWDFDevice2 * FxDevice, + _In_ IWDFRemoteInterface * FxRemoteInterface, + _Out_ PCMyRemoteTarget * MyRemoteTarget + ); + + ~CMyRemoteTarget( + VOID + ) + { + } + + DWORD WINAPI + ThreadProc( + 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 + ); + + // + // IRequestCallbackRequestCompletion + // + void + STDMETHODCALLTYPE + OnCompletion( + _In_ IWDFIoRequest * FxRequest, + _In_ IWDFIoTarget * /* FxTarget */, + _In_ IWDFRequestCompletionParams * /* Params */, + _In_ void* /* Context */ + ); + IRequestCallbackRequestCompletion * + QueryIRequestCallbackRequestCompletion( + VOID + ) + { + AddRef(); + return static_cast(this); + } + + // + // IRemoteTargetCallbackRemoval + // + BOOL + STDMETHODCALLTYPE + OnRemoteTargetQueryRemove( + _In_ IWDFRemoteTarget * /* FxTarget */ + ); + VOID + STDMETHODCALLTYPE + OnRemoteTargetRemoveCanceled( + _In_ IWDFRemoteTarget * /* FxTarget */ + ); + VOID + STDMETHODCALLTYPE + OnRemoteTargetRemoveComplete( + _In_ IWDFRemoteTarget * /* FxTarget */ + ); + IRemoteTargetCallbackRemoval * + QueryIRemoteTargetCallbackRemoval( + VOID + ) + { + AddRef(); + return static_cast(this); + } + + // + // IObjectCleanup + // + void + STDMETHODCALLTYPE + OnCleanup( + _In_ IWDFObject* /* FxObject */ + ); + IObjectCleanup * + QueryIObjectCleanup( + VOID + ) + { + AddRef(); + return static_cast(this); + } + +}; diff --git a/general/toaster/toastDrv/umdf/Toastmon/ToastMon.rc b/general/toaster/toastDrv/umdf/Toastmon/ToastMon.rc new file mode 100644 index 000000000..57983bbc8 --- /dev/null +++ b/general/toaster/toastDrv/umdf/Toastmon/ToastMon.rc @@ -0,0 +1,21 @@ +//--------------------------------------------------------------------------- +// ToastMon.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 ToastMon User-Mode Driver Sample" +#define VER_INTERNALNAME_STR "ToastMon" +#define VER_ORIGINALFILENAME_STR "ToastMon.dll" + +#include "common.ver" diff --git a/general/toaster/toastDrv/umdf/Toastmon/WUDFToastMon.inx b/general/toaster/toastDrv/umdf/Toastmon/WUDFToastMon.inx new file mode 100644 index 000000000..240674619 --- /dev/null +++ b/general/toaster/toastDrv/umdf/Toastmon/WUDFToastMon.inx @@ -0,0 +1,84 @@ +WUDFToastMon; +; WUDFToastMon.inf +; + +[Version] +Signature="$Windows NT$" +Class=Sample +ClassGuid={78A1C341-4539-11d3-B88D-00C04FAD5171} +Provider=%ProviderName% +CatalogFile=WUDF.cat +DriverVer=03/25/2005,0.0.0.1 + +[Manufacturer] +%ManufacturerName%=Standard,NT$ARCH$ + +[Standard.NT$ARCH$] +%ToastMonDeviceName%=ToastMon_Install,UMDFSamples\ToastMon + +[ClassInstall32] +AddReg=SampleClass_RegistryAdd + +[SampleClass_RegistryAdd] +HKR,,,,%ClassName% +HKR,,Icon,,"-10" + +[SourceDisksFiles] +WUDFToastMon.dll=1 +WudfUpdate_$UMDFCOINSTALLERVERSION$.dll=1 + +[SourceDisksNames] +1 = %MediaDescription% + +; =================== UMDF ToastMon Device ================================== + +[ToastMon_Install.NT] +CopyFiles=UMDriverCopy + +[ToastMon_Install.NT.hw] + +[ToastMon_Install.NT.Services] +AddService=WUDFRd,0x000001fa,WUDFRD_ServiceInstall + +[ToastMon_Install.NT.CoInstallers] +CopyFiles=CoInstallers_CopyFiles +AddReg=CoInstallers_AddReg + +[ToastMon_Install.NT.Wdf] +UmdfService=ToastMon,ToastMon_Install +UmdfServiceOrder=ToastMon + +[ToastMon_Install] +UmdfLibraryVersion=$UMDFVERSION$ +ServiceBinary=%12%\UMDF\WUDFToastMon.dll +DriverCLSID={8d4ec202-1076-4895-a072-297da88e6005} + +[WUDFRD_ServiceInstall] +DisplayName = %WudfRdDisplayName% +ServiceType = 1 +StartType = 3 +ErrorControl = 1 +ServiceBinary = %12%\WUDFRd.sys + +[CoInstallers_CopyFiles] +WudfUpdate_$UMDFCOINSTALLERVERSION$.dll + +[CoInstallers_AddReg] +HKR,,CoInstallers32,0x00010000,"WUDFUpdate_$UMDFCOINSTALLERVERSION$.dll" + +[DestinationDirs] +UMDriverCopy=12,UMDF ; copy to drivers\UMDF +CoInstallers_CopyFiles=11 + +[UMDriverCopy] +WUDFToastMon.dll + +; =================== Generic ================================== + +[Strings] +ProviderName="TODO-Set-Provider" +ManufacturerName="TODO-Set-Manufacturer" +MediaDescription="Sample Driver Installation Media" +ClassName="Sample Device" +WudfRdDisplayName="Windows Driver Foundation - User-mode Driver Framework Reflector" +ToastMonDeviceName="ToastMon User-Mode Device Sample" diff --git a/general/toaster/toastDrv/umdf/Toastmon/WUDFToastMon.vcxproj b/general/toaster/toastDrv/umdf/Toastmon/WUDFToastMon.vcxproj new file mode 100644 index 000000000..7055b6719 --- /dev/null +++ b/general/toaster/toastDrv/umdf/Toastmon/WUDFToastMon.vcxproj @@ -0,0 +1,273 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {D9D1EDA9-B9AD-4C14-BCDE-DBE39CED25B7} + $(MSBuildProjectName) + 1 + false + true + Debug + Win32 + {6713742F-8981-4BDD-8232-744F6ACE78A0} + + + + 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)\WUDFToastMon.inf + + + true + true + internal.h + + + + WUDFToastMon + Dynamic + + + WUDFToastMon + Dynamic + + + WUDFToastMon + Dynamic + + + WUDFToastMon + Dynamic + + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions);_UNICODE;UNICODE + + + %(AdditionalIncludeDirectories) + %(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;$(SDK_LIB_PATH)\oleaut32.lib + exports.def + + + + + true + Level4 + + + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\strsafe.lib;$(SDK_LIB_PATH)\kernel32.lib;$(SDK_LIB_PATH)\advapi32.lib;$(SDK_LIB_PATH)\oleaut32.lib + exports.def + + + + + true + Level4 + + + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\strsafe.lib;$(SDK_LIB_PATH)\kernel32.lib;$(SDK_LIB_PATH)\advapi32.lib;$(SDK_LIB_PATH)\oleaut32.lib + exports.def + + + + + true + Level4 + + + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\strsafe.lib;$(SDK_LIB_PATH)\kernel32.lib;$(SDK_LIB_PATH)\advapi32.lib;$(SDK_LIB_PATH)\oleaut32.lib + exports.def + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/general/toaster/toastDrv/umdf/Toastmon/WUDFToastMon.vcxproj.Filters b/general/toaster/toastDrv/umdf/Toastmon/WUDFToastMon.vcxproj.Filters new file mode 100644 index 000000000..ba04cdaee --- /dev/null +++ b/general/toaster/toastDrv/umdf/Toastmon/WUDFToastMon.vcxproj.Filters @@ -0,0 +1,51 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {C958237F-A7DD-499B-8D1E-59D0ED81A78A} + + + h;hpp;hxx;hm;inl;inc;xsd + {6167785E-EAD2-49EE-9D85-0DCE3CDCA899} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {A3079E54-E051-4EDD-8669-23098CEF4A4F} + + + inf;inv;inx;mof;mc; + {EB7A1810-4971-4585-938E-98F65782861E} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Driver Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/general/toaster/toastDrv/umdf/Toastmon/comsup.cpp b/general/toaster/toastDrv/umdf/Toastmon/comsup.cpp new file mode 100644 index 000000000..fd2984700 --- /dev/null +++ b/general/toaster/toastDrv/umdf/Toastmon/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/toaster/toastDrv/umdf/Toastmon/comsup.h b/general/toaster/toastDrv/umdf/Toastmon/comsup.h new file mode 100644 index 000000000..b96fd9828 --- /dev/null +++ b/general/toaster/toastDrv/umdf/Toastmon/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/toaster/toastDrv/umdf/Toastmon/dllsup.cpp b/general/toaster/toastDrv/umdf/Toastmon/dllsup.cpp new file mode 100644 index 000000000..8b4b5426d --- /dev/null +++ b/general/toaster/toastDrv/umdf/Toastmon/dllsup.cpp @@ -0,0 +1,174 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved. + +Module Name: + + dllsup.cpp + +Abstract: + + This module contains the implementation of the UMDF 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. See internal.h for definition. + + 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 + +--*/ +{ + if (DLL_PROCESS_ATTACH == Reason) + { + // + // Initialize tracing. + // + + WPP_INIT_TRACING(MYDRIVER_TRACING_ID); + + DisableThreadLibraryCalls(ModuleHandle); + } + 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/toaster/toastDrv/umdf/Toastmon/exports.def b/general/toaster/toastDrv/umdf/Toastmon/exports.def new file mode 100644 index 000000000..f8ac59f6e --- /dev/null +++ b/general/toaster/toastDrv/umdf/Toastmon/exports.def @@ -0,0 +1,4 @@ +; Exports.def : Declares the module parameters. + +EXPORTS + DllGetClassObject PRIVATE diff --git a/general/toaster/toastDrv/umdf/Toastmon/internal.h b/general/toaster/toastDrv/umdf/Toastmon/internal.h new file mode 100644 index 000000000..5a8fc9e2d --- /dev/null +++ b/general/toaster/toastDrv/umdf/Toastmon/internal.h @@ -0,0 +1,109 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved + +Module Name: + + Internal.h + +Abstract: + + This module contains the local type definitions for the + 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" + +// +// Include ATL to provide basic COM support. +// + +#define _ATL_FREE_THREADED +#define _ATL_NO_AUTOMATIC_NAMESPACE + +#include +#include + +using namespace ATL; + +extern CComModule _Module; +// +// Forward definitions of classes in the other header files. +// + +typedef class CMyDriver *PCMyDriver; +typedef class CMyDevice *PCMyDevice; +typedef class CMyRemoteTarget *PCMyRemoteTarget; + +// +// Define the tracing flags. +// +// TODO: Choose a different trace control GUID +// + +#define WPP_CONTROL_GUIDS \ + WPP_DEFINE_CONTROL_GUID( \ + MyDriverTraceControl, (dee2c67c,6328,48d9,876b,64682c4e9e9b), \ + \ + 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\\ToastMon" +#define MYDRIVER_CLASS_ID { 0x8d4ec202, 0x1076, 0x4895, {0xa0, 0x72, 0x29, 0x7d, 0xa8, 0x8e, 0x60, 0x05} } + +// +// Include simple doubly-linked list macros +// +#include "list.h" + +// +// Include the type specific headers. +// + +#include "comsup.h" +#include "driver.h" +#include "remotetarget.h" +#include "device.h" diff --git a/general/toaster/toastDrv/umdf/Toastmon/list.h b/general/toaster/toastDrv/umdf/Toastmon/list.h new file mode 100644 index 000000000..38d0b1e90 --- /dev/null +++ b/general/toaster/toastDrv/umdf/Toastmon/list.h @@ -0,0 +1,77 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved + +Module Name: + + list.h + +Abstract: + + This module contains doubly linked list macros + +Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + +#pragma once + + +FORCEINLINE +VOID +InitializeListHead( + IN PLIST_ENTRY ListHead + ) +{ + ListHead->Flink = ListHead->Blink = 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 +VOID +InsertHeadList( + IN PLIST_ENTRY ListHead, + IN PLIST_ENTRY Entry + ) +{ + PLIST_ENTRY Flink; + + Flink = ListHead->Flink; + Entry->Flink = Flink; + Entry->Blink = ListHead; + Flink->Blink = Entry; + ListHead->Flink = Entry; +} + +FORCEINLINE +VOID +InsertTailList( + IN PLIST_ENTRY ListHead, + IN PLIST_ENTRY Entry + ) +{ + PLIST_ENTRY Blink; + + Blink = ListHead->Blink; + Entry->Flink = ListHead; + Entry->Blink = Blink; + Blink->Flink = Entry; + ListHead->Blink = Entry; +} diff --git a/general/toaster/toastDrv/umdf/func/Device.cpp b/general/toaster/toastDrv/umdf/func/Device.cpp new file mode 100644 index 000000000..eaf0a5455 --- /dev/null +++ b/general/toaster/toastDrv/umdf/func/Device.cpp @@ -0,0 +1,228 @@ +/*++ + + Copyright (c) Microsoft Corporation, All Rights Reserved + + Module Name: + + Device.cpp + + Abstract: + + This file contains the device callback object implementation. + + Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ +#include "stdafx.h" +#include "Device.h" + +#include "internal.h" +#include "device.tmh" + +HRESULT +CDevice::QueryInterface( + _In_ REFIID riid, + _Out_ LPVOID* ppvObject + ) +/*++ + +Routine Description: + + The framework calls this function to determine which callback + interfaces we support. + +Arguments: + + riid - GUID for a given callback interface. + ppvObject - We set this pointer to our object if we support the + interface indicated by riid. + +Return Value: + + HRESULT S_OK - Interface is supported. + +--*/ +{ + if (ppvObject == NULL) + { + return E_INVALIDARG; + } + + *ppvObject = NULL; + + if ( riid == _uuidof(IUnknown) ) + { + *ppvObject = static_cast(this); + } + else if ( riid == _uuidof(IPnpCallbackHardware) ) + { + *ppvObject = static_cast(this); + } + else + { + return E_NOINTERFACE; + } + + this->AddRef(); + + return S_OK; +} + + +ULONG CDevice::AddRef() +/*++ + +Routine Description: + + Increments the ref count on this object. + +Arguments: + + None. + +Return Value: + + ULONG - new ref count. + +--*/ +{ + LONG cRefs = InterlockedIncrement( &m_cRefs ); + + return cRefs; +} + + +_At_(this, __drv_freesMem(object)) +ULONG CDevice::Release() +/*++ + +Routine Description: + + Decrements the ref count on this object. + +Arguments: + + None. + +Return Value: + + ULONG - new ref count. + +--*/ +{ + LONG cRefs; + + cRefs = InterlockedDecrement( &m_cRefs ); + + if( 0 == cRefs ) + { + delete this; + } + + return cRefs; +} + + +HRESULT +CDevice::OnPrepareHardware( + _In_ IWDFDevice* pDevice) +/*++ + +Routine Description: + + The framework calls this function after IDriverEntry::OnDeviceAdd + returns and before the device enters the working power state. + This callback prepares the device and the driver to enter the working + state after enumeration. + +Arguments: + + pWdfDevice - A pointer to the IWDFDevice interface for the device + object of the device to make accessible. + +Return Value: + + S_OK in case of success + HRESULT correponding to one of the error codes that are defined in Winerror.h. + +--*/ +{ + PWSTR deviceName = NULL; + DWORD deviceNameCch = 0; + + HRESULT hr; + + Trace(TRACE_LEVEL_INFORMATION,"%!FUNC!"); + + // + // Get the device name. + // Get the length to allocate first + // + + hr = pDevice->RetrieveDeviceName(NULL, &deviceNameCch); + + // + // Allocate the buffer + // + + if (SUCCEEDED(hr)) + { + deviceName = new WCHAR[deviceNameCch]; + + if (deviceName == NULL) + { + hr = E_OUTOFMEMORY; + } + } + + // + // Get the actual name + // + + if (SUCCEEDED(hr)) + { + hr = pDevice->RetrieveDeviceName(deviceName, &deviceNameCch); + + } + + // + // Do your hardware operations here + // + + delete[] deviceName; + + return hr; +} + +HRESULT +CDevice::OnReleaseHardware( + _In_ IWDFDevice* /*pDevice*/) +/*++ + +Routine Description: + + This routine is invoked when the device is being removed or stopped + It releases all resources allocated for this device. The framework + calls this callback after the device exits from the working power + state but before its queues are purged. + + +Arguments: + + pWdfDevice - A pointer to the IWDFDevice interface for the device object + of the device that is no longer accessible. + + +Return Value: + HRESULT - Always succeeds. +--*/ +{ + Trace(TRACE_LEVEL_INFORMATION,"%!FUNC!"); + + return S_OK; +} + + + diff --git a/general/toaster/toastDrv/umdf/func/Device.h b/general/toaster/toastDrv/umdf/func/Device.h new file mode 100644 index 000000000..4bb68dc70 --- /dev/null +++ b/general/toaster/toastDrv/umdf/func/Device.h @@ -0,0 +1,78 @@ +/*++ + + Copyright (c) Microsoft Corporation, All Rights Reserved + + Module Name: + + Device.h + + Abstract: + + This file contains the class definition for device callback object. + + Environment: + + Windows User-Mode Driver Framework (UMDF) + +--*/ + +#pragma once +#include "resource.h" +#include "WUDFToaster.h" + +// +// To inform the framework about the callbacks we are interested in, we +// simply derive from the desired set of interfaces. +// +class CDevice : public IPnpCallbackHardware +{ +public: + CDevice() : m_cRefs(0) + { + } + + +public: + + // + // Static method that creates a device callback object. + // + static HRESULT CreateInstance(_Out_ IUnknown ** ppUnkwn) + { + *ppUnkwn = NULL; + +#pragma warning( suppress : 6014 )// PFD ISSUE: counted memory locks + CDevice *pMyDevice = new CDevice(); + + if (NULL == pMyDevice) + { + return E_OUTOFMEMORY; + } + + return (pMyDevice->QueryInterface( __uuidof(IUnknown), (void **) ppUnkwn )); + } + + // + // IUnknown + // + virtual HRESULT __stdcall QueryInterface(_In_ REFIID riid, _Out_ LPVOID* ppvObject); + virtual ULONG __stdcall AddRef(); + _At_(this, __drv_freesMem(object)) + virtual ULONG __stdcall Release(); + + + // IPnpCallbackHardware + // + virtual HRESULT __stdcall OnPrepareHardware(_In_ IWDFDevice* pDevice); + virtual HRESULT __stdcall OnReleaseHardware(_In_ IWDFDevice* pDevice); + + // + // TODO: Add your interfaces here + // + +private: + + LONG m_cRefs; + +}; + diff --git a/general/toaster/toastDrv/umdf/func/Driver.cpp b/general/toaster/toastDrv/umdf/func/Driver.cpp new file mode 100644 index 000000000..053217b95 --- /dev/null +++ b/general/toaster/toastDrv/umdf/func/Driver.cpp @@ -0,0 +1,205 @@ +/*++ + + Copyright (c) Microsoft Corporation, All Rights Reserved + + Module Name: + + Driver.cpp + + Abstract: + + This file contains the implementation for the driver object. + + Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + +#include "stdafx.h" +#include "Driver.h" +#include "Device.h" +#include "Queue.h" + +#include "internal.h" +#include "driver.tmh" + +//Idle setting for the Toaster device +#define IDLEWAKE_TIMEOUT_MSEC 6000 + + +HRESULT +CDriver::OnDeviceAdd( + _In_ IWDFDriver* pDriver, + _In_ IWDFDeviceInitialize* pDeviceInit + ) +/*++ + +Routine Description: + + The framework calls this function when a device is being added to + the driver stack. + +Arguments: + + IWDFDriver - Framework interface. The driver uses this + interface to create device objects. + IWDFDeviceInitialize - Framework interface. The driver uses this + interface to set device parameters before + creating the device obeject. + +Return Value: + + HRESULT S_OK - Device added successfully + +--*/ +{ + IUnknown *pDeviceCallback = NULL; + IWDFDevice *pIWDFDevice = NULL; + IWDFDevice2 *pIWDFDevice2 = NULL; + IUnknown *pIUnkQueue = NULL; + + // + // UMDF Toaster is a function driver so set is as the power policy owner (PPO) + // + pDeviceInit->SetPowerPolicyOwnership(TRUE); + + // + // Create our device callback object. + // + HRESULT hr = CDevice::CreateInstance(&pDeviceCallback); + + // + // Ask the framework to create a device object for us. + // We pass in the callback object and device init object + // as creation parameters. + // + if (SUCCEEDED(hr)) + { + hr = pDriver->CreateDevice(pDeviceInit, + pDeviceCallback, + &pIWDFDevice); + } + + // + // Create the queue callback object. + // + + if (SUCCEEDED(hr)) + { + hr = CQueue::CreateInstance(&pIUnkQueue); + } + + // + // Configure the default queue. We pass in our queue callback + // object to inform the framework about the callbacks we want. + // + + if (SUCCEEDED(hr)) + { + IWDFIoQueue * pDefaultQueue = NULL; + hr = pIWDFDevice->CreateIoQueue( + pIUnkQueue, + TRUE, // bDefaultQueue + WdfIoQueueDispatchParallel, + TRUE, // bPowerManaged + FALSE, // bAllowZeroLengthRequests + &pDefaultQueue); + SAFE_RELEASE(pDefaultQueue); + } + + // + // Enable the device interface. + // + + if (SUCCEEDED(hr)) + { + hr = pIWDFDevice->CreateDeviceInterface(&GUID_DEVINTERFACE_TOASTER, + NULL); + } + + // + // IWDFDevice2 interface is an extension of IWDFDevice interface that enables + // Idle and Wake support. + // + + // + // Get a pointer to IWDFDevice2 interface + // + + if (SUCCEEDED(hr)) + { + hr = pIWDFDevice->QueryInterface(__uuidof(IWDFDevice2), (void**) &pIWDFDevice2); + } + + // + // 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. + // + + if (SUCCEEDED(hr)) + { + + hr = pIWDFDevice2->AssignS0IdleSettings( + IdleCannotWakeFromS0, + PowerDeviceD3, //the lowest-powered device sleeping state + IDLEWAKE_TIMEOUT_MSEC, //idle timeout + IdleAllowUserControl, //user can control the device's idle behavior. + WdfTrue); + + } + + // + // TODO: Add the Idle and Wake suupport specific for your hardware + // + + SAFE_RELEASE(pDeviceCallback); + SAFE_RELEASE(pIWDFDevice); + SAFE_RELEASE(pIWDFDevice2); + SAFE_RELEASE(pIUnkQueue); + + return hr; +} + +VOID +CDriver::OnDeinitialize( + _In_ IWDFDriver * /* pDriver */ + ) +/*++ + +Routine Description: + + The framework calls this function just before de-initializing itself. All + WDF framework resources should be released by driver before returning from this call. + +Arguments: + +Return Value: + +--*/ +{ + return ; +} + +HRESULT +CDriver::OnInitialize( + _In_ IWDFDriver * /* pDriver */ + ) +/*++ + +Routine Description: + + The framework calls this function just after loading the driver. The driver can + perform any global, device independent intialization in this routine. + +Arguments: + +Return Value: + +--*/ +{ + return S_OK; +} + + diff --git a/general/toaster/toastDrv/umdf/func/Driver.h b/general/toaster/toastDrv/umdf/func/Driver.h new file mode 100644 index 000000000..b60cffb51 --- /dev/null +++ b/general/toaster/toastDrv/umdf/func/Driver.h @@ -0,0 +1,59 @@ +/*++ + + Copyright (c) Microsoft Corporation, All Rights Reserved + + Module Name: + + Driver.h + + Abstract: + + This file contains the class definition for the driver object. + + Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + +#pragma once +#include "resource.h" +#include "WUDFToaster.h" + +class ATL_NO_VTABLE CDriver : + public CComObjectRootEx, + public CComCoClass, + public IDriverEntry +{ +public: + CDriver() + { + } + + DECLARE_REGISTRY_RESOURCEID(IDR_TOASTERDRIVER) + DECLARE_NOT_AGGREGATABLE(CDriver) + + // + // The driver object suppports the IDriverEntry interface. + // + BEGIN_COM_MAP(CDriver) + COM_INTERFACE_ENTRY(IDriverEntry) + END_COM_MAP() + +public: + // + // IDriverEntry + // + STDMETHOD (OnDeviceAdd)( + _In_ IWDFDriver *pDriver, + _In_ IWDFDeviceInitialize *pDeviceInit + ); + STDMETHOD (OnInitialize)( + _In_ IWDFDriver* pDriver + ); + STDMETHOD_ (void, OnDeinitialize)( + _In_ IWDFDriver* pDriver + ); +}; + +OBJECT_ENTRY_AUTO(__uuidof(WUDFToaster), CDriver) diff --git a/general/toaster/toastDrv/umdf/func/Queue.cpp b/general/toaster/toastDrv/umdf/func/Queue.cpp new file mode 100644 index 000000000..dec296a06 --- /dev/null +++ b/general/toaster/toastDrv/umdf/func/Queue.cpp @@ -0,0 +1,283 @@ +/*++ + + Copyright (c) Microsoft Corporation, All Rights Reserved + + Module Name: + + Queue.cpp + + Abstract: + + This file contains the queue callback object implementation. + + Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + +#include "stdafx.h" +#include "Queue.h" +#include +#include + + +#include "internal.h" +#include "queue.tmh" + +HRESULT +CQueue::QueryInterface( + _In_ REFIID riid, + _Out_ LPVOID* ppvObject + ) +/*++ + +Routine Description: + + The framework calls this function to determine which callback + interfaces we support. + +Arguments: + + riid - GUID for a given callback interface. + ppvObject - We set this pointer to our object if we support the + interface indicated by riid. + +Return Value: + + HRESULT S_OK - Interface is supported. + +--*/ +{ + if (ppvObject == NULL) + { + return E_INVALIDARG; + } + *ppvObject = NULL; + + if ( riid == _uuidof(IUnknown) ) + { + *ppvObject = static_cast (this); + } + else if ( riid == _uuidof(IQueueCallbackDeviceIoControl) ) + { + *ppvObject = static_cast(this); + } + else if ( riid == _uuidof(IQueueCallbackRead) ) + { + *ppvObject = static_cast(this); + } + else if ( riid == _uuidof(IQueueCallbackWrite) ) + { + *ppvObject = static_cast(this); + } + else + { + return E_NOINTERFACE; + } + + this->AddRef(); + + return S_OK; +} + + + +ULONG CQueue::AddRef() +/*++ + +Routine Description: + + Increments the ref count on this object. + +Arguments: + + None. + +Return Value: + + ULONG - new ref count. + +--*/ +{ + LONG cRefs = InterlockedIncrement( &m_cRefs ); + return cRefs; +} + +_At_(this, __drv_freesMem(object)) +ULONG CQueue::Release() +/*++ + +Routine Description: + + Decrements the ref count on this object. + +Arguments: + + None. + +Return Value: + + ULONG - new ref count. + +--*/ +{ + LONG cRefs; + + cRefs = InterlockedDecrement( &m_cRefs ); + + if( 0 == cRefs ) + { + delete this; + } + + return cRefs; +} + + +void +CQueue::OnDeviceIoControl( + _In_ IWDFIoQueue* pQueue, + _In_ IWDFIoRequest* pRequest, + _In_ ULONG ControlCode, + _In_ SIZE_T /*InputBufferSizeInBytes*/, + _In_ SIZE_T /*OutputBufferSizeInBytes*/ + ) +/*++ + +Routine Description: + + The framework calls this function when somone has called + DeviceIoControl on the device. + +Arguments: + +Return Value: + None + +--*/ +{ + HRESULT hr = S_OK; + IWDFDevice *pDevice = NULL; + + Trace(TRACE_LEVEL_INFORMATION,"%!FUNC!"); + + // + // Retrieve the queue's parent device object + // + pQueue->GetDevice(&pDevice); + + WUDF_TEST_DRIVER_ASSERT(pDevice); + + switch (ControlCode) + { + 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 your code when you adapt this + // sample for your hardware. + // + pDevice->SetPnpState(WdfPnpStateDontDisplayInUI, WdfTrue); + pDevice->CommitPnpState(); + + break; + + default: + hr = E_FAIL; //invalid request + + Trace(TRACE_LEVEL_ERROR,"%!FUNC! Invalid IOCTL %!hresult!",hr); + } + pRequest->Complete(hr); + + return; +} + +void +CQueue::OnRead( + _In_ IWDFIoQueue* /* pQueue */, + _In_ IWDFIoRequest* pRequest, + _In_ SIZE_T SizeInBytes + ) +/*++ + +Routine Description: + + + Read dispatch routine + IQueueCallbackRead + +Arguments: + + pQueue - Framework Queue instance + pRequest - Framework Request instance + SizeInBytes - Length of bytes in the read buffer + + Copy available data into the read buffer + +Return Value: + None. + +--*/ +{ + Trace(TRACE_LEVEL_INFORMATION,"%!FUNC!"); + + // + // No need to check for zero-length reads. + // + // The framework queue is created with the flag bAllowZeroLengthRequests = FALSE. + // FALSE indicates that the framework completes zero-length I/O requests instead + // of putting them in the I/O queue. + // + + // + // TODO: Put your Read request processing here + // + + pRequest->CompleteWithInformation(S_OK, SizeInBytes); + + return; + +} + +void +CQueue::OnWrite( + _In_ IWDFIoQueue * /* pQueue */, + _In_ IWDFIoRequest * pRequest, + _In_ SIZE_T BytesToWrite + ) +/*++ + +Routine Description: + + Write dispatch routine + IQueueCallbackWrite + +Arguments: + + pQueue - Framework Queue instance + pRequest - Framework Request instance + BytesToWrite - Length of bytes in the write buffer + + Allocate and copy data to local buffer + +Return Value: + None. + +--*/ +{ + Trace(TRACE_LEVEL_INFORMATION,"%!FUNC!"); + + // + // No need to check for zero-length writes. + // + + // + // TODO: Put your Write request processing here + // + + pRequest->CompleteWithInformation(S_OK, BytesToWrite); + + return; +} + diff --git a/general/toaster/toastDrv/umdf/func/Queue.h b/general/toaster/toastDrv/umdf/func/Queue.h new file mode 100644 index 000000000..6668ac355 --- /dev/null +++ b/general/toaster/toastDrv/umdf/func/Queue.h @@ -0,0 +1,96 @@ +/*++ + + Copyright (c) Microsoft Corporation, All Rights Reserved + + Module Name: + + Queue.h + + Abstract: + + This file contains the class definition for the queue + callback object. + + Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + +#pragma once +#include "WUDFToaster.h" + +class CQueue : + public IQueueCallbackDeviceIoControl, + public IQueueCallbackRead, + public IQueueCallbackWrite +{ + public: + + CQueue() : m_cRefs(0) + { + } + + public: + + // + // Static method that creates a queue callback object. + // + static HRESULT CreateInstance(_Out_ IUnknown **ppUkwn) + { + *ppUkwn = NULL; + +#pragma warning( suppress : 6014 )// PFD ISSUE: counted memory locks + CQueue *pMyQueue = new CQueue(); + + if (NULL == pMyQueue) + { + return E_OUTOFMEMORY; + } + return (pMyQueue->QueryInterface(__uuidof(IUnknown), (void **)ppUkwn )); + } + + // + // IUnknown + // + virtual HRESULT __stdcall QueryInterface(_In_ REFIID riid, _Out_ LPVOID* ppvObject); + virtual ULONG __stdcall AddRef(); + _At_(this, __drv_freesMem(object)) + virtual ULONG __stdcall Release(); + + // + // IQueueCallbackDeviceIoControl + // + virtual void __stdcall OnDeviceIoControl( + _In_ IWDFIoQueue* pQueue, + _In_ IWDFIoRequest* pRequest, + _In_ ULONG ControlCode, + _In_ SIZE_T InputBufferSizeInBytes, + _In_ SIZE_T OutputBufferSizeInBytes + ); + + // + // IQueueCallbackRead + // + virtual void __stdcall OnRead( + _In_ IWDFIoQueue *pWdfQueue, + _In_ IWDFIoRequest *pWdfRequest, + _In_ SIZE_T NumOfBytesToRead + ); + + // + // IQueueCallbackWrite + // + virtual void __stdcall OnWrite( + _In_ IWDFIoQueue *pWdfQueue, + _In_ IWDFIoRequest *pWdfRequest, + _In_ SIZE_T NumOfBytesToWrite + ); + + // + // TODO: Add your interfaces here + // + + private: + LONG m_cRefs; +}; diff --git a/general/toaster/toastDrv/umdf/func/WUDFToaster.cpp b/general/toaster/toastDrv/umdf/func/WUDFToaster.cpp new file mode 100644 index 000000000..fd66163d9 --- /dev/null +++ b/general/toaster/toastDrv/umdf/func/WUDFToaster.cpp @@ -0,0 +1,88 @@ +/*++ + + Copyright (c) Microsoft Corporation, All Rights Reserved + + Module Name: + + WUDFToaster.cpp + + Abstract: + + Implementation of DLL Exports. + + Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + +#include "stdafx.h" +#include "resource.h" + +#include "WUDFToaster.h" + +#include "internal.h" +#include "WUDFToaster.tmh" + +const GUID CLSID_MyDriverCoClass = MYDRIVER_CLASS_ID; + +class CToasterDriverModule : public CAtlDllModuleT< CToasterDriverModule > +{ +public : + DECLARE_LIBID(LIBID_WUDFToasterLib) +}; + +CToasterDriverModule _AtlModule; + +// DLL Entry Point +extern "C" BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) +{ + hInstance; + + if(dwReason == DLL_PROCESS_ATTACH) + { + // + // Initialize tracing. + // + + WPP_INIT_TRACING(MYDRIVER_TRACING_ID); + } + else if(dwReason == DLL_PROCESS_DETACH) + { + // + // Cleanup tracing. + // + + WPP_CLEANUP(); + } + + return _AtlModule.DllMain(dwReason, lpReserved); +} + +// Returns a class factory to create an object of the requested type +_Check_return_ +STDAPI DllGetClassObject(_In_ REFCLSID rclsid, _In_ REFIID riid, _Outptr_ LPVOID* ppv) +{ + return _AtlModule.DllGetClassObject(rclsid, riid, ppv); +} + +// Used to determine whether the DLL can be unloaded by OLE +STDAPI DllCanUnloadNow(void) +{ + return _AtlModule.DllCanUnloadNow(); +} + +// DllRegisterServer - Adds entries to the system registry +STDAPI DllRegisterServer(void) +{ + // registers object, typelib and all interfaces in typelib + HRESULT hr = _AtlModule.DllRegisterServer(); + return hr; +} + +// DllUnregisterServer - Removes entries from the system registry +STDAPI DllUnregisterServer(void) +{ + HRESULT hr = _AtlModule.DllUnregisterServer(); + return hr; +} diff --git a/general/toaster/toastDrv/umdf/func/WUDFToaster.ctl b/general/toaster/toastDrv/umdf/func/WUDFToaster.ctl new file mode 100644 index 000000000..cfcbb8668 --- /dev/null +++ b/general/toaster/toastDrv/umdf/func/WUDFToaster.ctl @@ -0,0 +1,2 @@ +9B6DE419-5658-46c9-A358-54FDEBB6B89D WudfToasterTraceGuid + diff --git a/general/toaster/toastDrv/umdf/func/WUDFToaster.def b/general/toaster/toastDrv/umdf/func/WUDFToaster.def new file mode 100644 index 000000000..4aa746ad4 --- /dev/null +++ b/general/toaster/toastDrv/umdf/func/WUDFToaster.def @@ -0,0 +1,6 @@ +; WUDFToaster.def : Declares the module parameters. + +LIBRARY "WUDFToaster.DLL" + +EXPORTS + DllGetClassObject PRIVATE diff --git a/general/toaster/toastDrv/umdf/func/WUDFToaster.idl b/general/toaster/toastDrv/umdf/func/WUDFToaster.idl new file mode 100644 index 000000000..da21b41e8 --- /dev/null +++ b/general/toaster/toastDrv/umdf/func/WUDFToaster.idl @@ -0,0 +1,40 @@ +/*++ + + Copyright (c) Microsoft Corporation, All Rights Reserved + + Module Name: + + WUDFToaster.idl + + Abstract: + + Definition of the WUDF Toaster sample's COM class + + Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + +import "oaidl.idl"; +import "ocidl.idl"; +import "wudfddi.idl"; + +[ + uuid(04170C5A-388E-410f-9BAA-E7724196AD17), + version(1.0), + helpstring("WUDF Toaster Driver 1.0 Type Library") +] +library WUDFToasterLib +{ + importlib("stdole2.tlb"); + [ + uuid(5ED5997F-FAED-4a35-BA74-FCF7B43AEBA7), + helpstring("WudfToaster Class") + ] + coclass WUDFToaster + { + [default] interface IDriverEntry; + }; +}; + diff --git a/general/toaster/toastDrv/umdf/func/WUDFToaster.inx b/general/toaster/toastDrv/umdf/func/WUDFToaster.inx new file mode 100644 index 000000000..7c65bd092 --- /dev/null +++ b/general/toaster/toastDrv/umdf/func/WUDFToaster.inx @@ -0,0 +1,98 @@ +; +; WUDFToaster.inf +; + +[Version] +Signature="$Windows NT$" +Class=TOASTER +ClassGuid={B85B7C50-6A01-11d2-B841-00C04FAD5171} +Provider=%ProviderString% +CatalogFile=WUDF.cat +DriverVer=03/10/2004,0.0.0.1 + +; ================= Class section ============================ + +[ClassInstall32] +AddReg=WDF_RegistryAdd +CopyFiles=ToasterClassInstallerCopyFiles + +[WDF_RegistryAdd] +HKR,,,,%ClassName% +HKR,,Icon,,"100" +HKR,,Installer32,,"tostrcls.dll,ToasterClassInstaller" +HKR,,DeviceCharacteristics,0x10001,0x100 ;Use same security checks on relative opens +HKR,,Security,,"D:P(A;;GA;;;SY)(A;;GA;;;BA)(A;;GA;;;LS)" ;Allow generic all access to system, built-in Admin and Local Service + ;This one overrides the security set by the driver +[ToasterClassInstallerCopyFiles] +tostrcls.dll + +; =================== WUDF Toaster Device ===================== + +[Manufacturer] +%ManufacturerString%=Microsoft,NT$ARCH$ + +[Microsoft.NT$ARCH$] +%ToasterDeviceName%=Toaster_Install,{b85b7c50-6a01-11d2-b841-00c04fad5171}\MsToaster +%ToasterDeviceName%=Toaster_Install,Root\MsToaster + + +[Toaster_Install.NT] +CopyFiles=UMDFDriverCopy + +[Toaster_Install.NT.HW] + +[Toaster_Install.NT.Services] +AddService=WUDFRd,0x000001fa,WUDFRD_ServiceInstall + +[Toaster_Install.NT.CoInstallers] +AddReg = CoInstallers_AddReg +CopyFiles = CoInstallers_CopyFiles + +[Toaster_Install.NT.Wdf] +UmdfService=WUDFToaster, WUDFToaster_Install +UmdfServiceOrder=WUDFToaster + +[WUDFToaster_Install] +UmdfLibraryVersion=$UMDFVERSION$ +DriverCLSID="{5ED5997F-FAED-4a35-BA74-FCF7B43AEBA7}" +ServiceBinary=%12%\UMDF\WUDFToaster.dll + +[WUDFRD_ServiceInstall] +DisplayName = %WudfRdDisplayName% +ServiceType=1 +StartType=3 +ErrorControl=1 +ServiceBinary=%12%\WUDFRd.sys + +[SourceDisksFiles] +WUDFToaster.dll=1 +tostrcls.dll = 1 +WudfUpdate_$UMDFCOINSTALLERVERSION$.dll=1 + +[SourceDisksNames] +1 = %MediaDescription% + +[UMDFDriverCopy] +WUDFToaster.dll + +; =================== WUDF Toaster Device Coinstaller ============= + +[CoInstallers_AddReg] +HKR,,CoInstallers32,0x00010000,"WudfUpdate_$UMDFCOINSTALLERVERSION$.dll" + +[CoInstallers_CopyFiles] +WudfUpdate_$UMDFCOINSTALLERVERSION$.dll + +[DestinationDirs] +UMDFDriverCopy=12,UMDF ; copy to drivers\umdf +CoInstallers_CopyFiles=11 ; copy to system32 + +; =================== Generic ===================================== + +[Strings] +ProviderString = "TODO-Set-Provider" +ManufacturerString = "TODO-Set-Manufacturer" +MediaDescription="Microsoft WUDF Sample Driver Installation Media" +ClassName="Toaster" +WudfRdDisplayName="Windows Driver Foundation - User-mode Driver Framework Reflector" +ToasterDeviceName="Sample WUDF Toaster Driver" diff --git a/general/toaster/toastDrv/umdf/func/WUDFToaster.rc b/general/toaster/toastDrv/umdf/func/WUDFToaster.rc new file mode 100644 index 000000000..74453a023 --- /dev/null +++ b/general/toaster/toastDrv/umdf/func/WUDFToaster.rc @@ -0,0 +1,22 @@ +//--------------------------------------------------------------------------- +// Skeleton.rc +// +// Copyright (c) Microsoft Corporation, All Rights Reserved +//--------------------------------------------------------------------------- + + +#include +#include +#include "resource.h" + +// +// 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 User-Mode Toaster Driver Sample" +#define VER_INTERNALNAME_STR "WUDFToaster" +#define VER_ORIGINALFILENAME_STR "WUDFToaster.dll" + +#include "common.ver" diff --git a/general/toaster/toastDrv/umdf/func/WUDFToaster.vcxproj b/general/toaster/toastDrv/umdf/func/WUDFToaster.vcxproj new file mode 100644 index 000000000..26aa3b699 --- /dev/null +++ b/general/toaster/toastDrv/umdf/func/WUDFToaster.vcxproj @@ -0,0 +1,326 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {B439C142-C570-44A6-B13C-43F70AE05A69} + $(MSBuildProjectName) + 1 + false + true + Debug + Win32 + {71A982A9-366B-42AD-AEFD-F60AAD4820F2} + + + + 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 + + + true + true + internal.h + ;%(AdditionalIncludeDirectories) + stdAfx.h + Use + $(IntDir)\stdafx.pch + + + true + true + internal.h + ;%(AdditionalIncludeDirectories) + stdAfx.h + Use + $(IntDir)\stdafx.pch + + + true + true + internal.h + ;%(AdditionalIncludeDirectories) + stdAfx.h + Use + $(IntDir)\stdafx.pch + + + true + true + internal.h + ;%(AdditionalIncludeDirectories) + stdAfx.h + Use + $(IntDir)\stdafx.pch + + + true + true + internal.h + ;%(AdditionalIncludeDirectories) + stdAfx.h + Use + $(IntDir)\stdafx.pch + + + $(InfArch) + true + true + .\$(IntDir)\WUDFToaster.inf + + + true + true + internal.h + + + + WUDFToaster + + + WUDFToaster + + + WUDFToaster + + + WUDFToaster + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + %(PreprocessorDefinitions);DEBUG + true + Level4 + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + %(PreprocessorDefinitions);DEBUG + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + %(PreprocessorDefinitions);DEBUG + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + %(PreprocessorDefinitions);DEBUG + true + Level4 + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + %(PreprocessorDefinitions);DEBUG + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + %(PreprocessorDefinitions);DEBUG + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + %(PreprocessorDefinitions);DEBUG + true + Level4 + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + %(PreprocessorDefinitions);DEBUG + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + %(PreprocessorDefinitions);DEBUG + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + %(PreprocessorDefinitions);DEBUG + true + Level4 + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + %(PreprocessorDefinitions);DEBUG + + + %(PreprocessorDefinitions);_UNICODE;UNICODE + %(PreprocessorDefinitions);DEBUG + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + %(AdditionalIncludeDirectories);.;.\$(IntDir);..\..\kmdf\inc + + + %(AdditionalIncludeDirectories);.;.\$(IntDir);..\..\kmdf\inc + + + + + %(AdditionalIncludeDirectories);.;.\$(IntDir);..\..\kmdf\inc + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\strsafe.lib;$(SDK_LIB_PATH)\kernel32.lib;$(SDK_LIB_PATH)\ole32.lib;$(SDK_LIB_PATH)\oleaut32.lib;$(SDK_LIB_PATH)\uuid.lib;$(SDK_LIB_PATH)\user32.lib;$(SDK_LIB_PATH)\advapi32.lib + WUDFToaster.def + + + + + %(AdditionalIncludeDirectories);.;.\$(IntDir);..\..\kmdf\inc + + + %(AdditionalIncludeDirectories);.;.\$(IntDir);..\..\kmdf\inc + + + + + %(AdditionalIncludeDirectories);.;.\$(IntDir);..\..\kmdf\inc + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\strsafe.lib;$(SDK_LIB_PATH)\kernel32.lib;$(SDK_LIB_PATH)\ole32.lib;$(SDK_LIB_PATH)\oleaut32.lib;$(SDK_LIB_PATH)\uuid.lib;$(SDK_LIB_PATH)\user32.lib;$(SDK_LIB_PATH)\advapi32.lib + WUDFToaster.def + + + + + %(AdditionalIncludeDirectories);.;.\$(IntDir);..\..\kmdf\inc + + + %(AdditionalIncludeDirectories);.;.\$(IntDir);..\..\kmdf\inc + + + + + %(AdditionalIncludeDirectories);.;.\$(IntDir);..\..\kmdf\inc + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\strsafe.lib;$(SDK_LIB_PATH)\kernel32.lib;$(SDK_LIB_PATH)\ole32.lib;$(SDK_LIB_PATH)\oleaut32.lib;$(SDK_LIB_PATH)\uuid.lib;$(SDK_LIB_PATH)\user32.lib;$(SDK_LIB_PATH)\advapi32.lib + WUDFToaster.def + + + + + %(AdditionalIncludeDirectories);.;.\$(IntDir);..\..\kmdf\inc + + + %(AdditionalIncludeDirectories);.;.\$(IntDir);..\..\kmdf\inc + + + + + %(AdditionalIncludeDirectories);.;.\$(IntDir);..\..\kmdf\inc + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\strsafe.lib;$(SDK_LIB_PATH)\kernel32.lib;$(SDK_LIB_PATH)\ole32.lib;$(SDK_LIB_PATH)\oleaut32.lib;$(SDK_LIB_PATH)\uuid.lib;$(SDK_LIB_PATH)\user32.lib;$(SDK_LIB_PATH)\advapi32.lib + WUDFToaster.def + + + + + ;%(AdditionalIncludeDirectories) + stdAfx.h + Create + $(IntDir)\stdafx.pch + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/general/toaster/toastDrv/umdf/func/WUDFToaster.vcxproj.Filters b/general/toaster/toastDrv/umdf/func/WUDFToaster.vcxproj.Filters new file mode 100644 index 000000000..46ae82b5a --- /dev/null +++ b/general/toaster/toastDrv/umdf/func/WUDFToaster.vcxproj.Filters @@ -0,0 +1,60 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {170A06AF-2C82-4ACA-A945-09EC764D4AD0} + + + h;hpp;hxx;hm;inl;inc;xsd + {9D2AA52F-CCFD-4741-8446-4BC64BE329AB} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {298C639B-301D-4144-BD66-7074B07ED001} + + + inf;inv;inx;mof;mc; + {2BF2C9E3-92D6-43CA-B2A3-98DF1231AABC} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Driver Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/general/toaster/toastDrv/umdf/func/internal.h b/general/toaster/toastDrv/umdf/func/internal.h new file mode 100644 index 000000000..1a098a19d --- /dev/null +++ b/general/toaster/toastDrv/umdf/func/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 Toaster + 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 CDriver *PCDriver; +typedef class CDevice *PCDevice; +typedef class CQueue *PCQueue; + +// +// Define the tracing flags. +// +// TODO: Choose a different trace control GUID +// + +#define WPP_CONTROL_GUIDS \ + WPP_DEFINE_CONTROL_GUID( \ + MyDriverTraceControl, (9B6DE419,5658,46c9,A358,54FDEBB6B89D), \ + \ + 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\\WUDF\\Toaster" +#define MYDRIVER_CLASS_ID {0x7ab7dcf5, 0xd1d4, 0x4085, {0x95, 0x47, 0x1d, 0xb9, 0x68, 0xcc, 0xa7, 0x20}} + +// +// Include the type specific headers. +// + +#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(); \ + } \ +} + + + diff --git a/general/toaster/toastDrv/umdf/func/resource.h b/general/toaster/toastDrv/umdf/func/resource.h new file mode 100644 index 000000000..800c5a813 --- /dev/null +++ b/general/toaster/toastDrv/umdf/func/resource.h @@ -0,0 +1,13 @@ +#define IDS_PROJNAME 100 +#define IDR_TOASTERDRIVER 101 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 201 +#define _APS_NEXT_COMMAND_VALUE 32768 +#define _APS_NEXT_CONTROL_VALUE 201 +#define _APS_NEXT_SYMED_VALUE 105 +#endif +#endif diff --git a/general/toaster/toastDrv/umdf/func/stdAfxsrc.cpp b/general/toaster/toastDrv/umdf/func/stdAfxsrc.cpp new file mode 100644 index 000000000..15f34744f --- /dev/null +++ b/general/toaster/toastDrv/umdf/func/stdAfxsrc.cpp @@ -0,0 +1 @@ +#include "stdAfx.h" \ No newline at end of file diff --git a/general/toaster/toastDrv/umdf/func/stdafx.cpp b/general/toaster/toastDrv/umdf/func/stdafx.cpp new file mode 100644 index 000000000..16521f2af --- /dev/null +++ b/general/toaster/toastDrv/umdf/func/stdafx.cpp @@ -0,0 +1,27 @@ +/*++ + + Copyright (c) Microsoft Corporation, All Rights Reserved + + Module Name: + + stdafx.cpp + + Abstract: + + This is used to build the precompiled header. + + Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + + +#include "stdafx.h" + +#include "initguid.h" + +DEFINE_GUID (GUID_DEVINTERFACE_TOASTER, + 0x781EF630, 0x72B2, 0x11d2, 0xB8, 0x52, 0x00, 0xC0, 0x4F, 0xAD, 0x51, 0x71); +//{781EF630-72B2-11d2-B852-00C04FAD5171} + diff --git a/general/toaster/toastDrv/umdf/func/stdafx.h b/general/toaster/toastDrv/umdf/func/stdafx.h new file mode 100644 index 000000000..7e4324968 --- /dev/null +++ b/general/toaster/toastDrv/umdf/func/stdafx.h @@ -0,0 +1,47 @@ +/*++ + + Copyright (c) Microsoft Corporation, All Rights Reserved + + Module Name: + + stdafx.h + + Abstract: + + Include file for standard system include filesor project specific + include files that are used frequently, but are changed infrequently + + Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + + +#pragma once + +#ifndef STRICT +#define STRICT +#endif + + +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0501 // Windows XP and newer. +#endif + +#define _ATL_FREE_THREADED +#define _ATL_NO_AUTOMATIC_NAMESPACE + +// turns off ATL's hiding of some common and often safely ignored warning messages +#define _ATL_ALL_WARNINGS + +#define SAFE_RELEASE(p) {if ((p)) { (p)->Release(); (p) = NULL; }} + +#include "resource.h" +#include +#include + +extern const GUID GUID_DEVINTERFACE_TOASTER; + +using namespace ATL; + diff --git a/general/toaster/toastpkg/inf/toastpkg.inf b/general/toaster/toastpkg/inf/toastpkg.inf index f427ae873..f1edae867 100644 --- a/general/toaster/toastpkg/inf/toastpkg.inf +++ b/general/toaster/toastpkg/inf/toastpkg.inf @@ -23,7 +23,7 @@ Signature="$WINDOWS NT$" Class=TOASTER ClassGuid={B85B7C50-6A01-11d2-B841-00C04FAD5171} -Provider=%ToastRUs% +Provider=%ProviderName% DriverVer=09/21/2006,6.0.5736.1 CatalogFile.NTx86 = tostx86.cat CatalogFile.NTIA64 = tostia64.cat @@ -56,11 +56,6 @@ tostrcls.dll [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 @@ -129,9 +124,10 @@ tostrcls.dll = 1,, [Strings] SPSVCINST_ASSOCSERVICE= 0x00000002 +ProviderName = "TODO-Set-Provider" ToastRUs = "Toast'R'Us" ClassName = "Toaster" DiskId1 = "Toaster Device Installation Disk #1" ToasterDevice.DeviceDesc = "Toaster Package Sample Toaster" -toaster.SVCDESC = "Microsoft Toaster Device Driver" +toaster.SVCDESC = "Toaster Device Driver" FriendlyNameFormat = "ToasterDevice%1!u!" diff --git a/general/toaster/toastpkg/toastapp/toastapp.vcxproj b/general/toaster/toastpkg/toastapp/toastapp.vcxproj index 5deac4a68..772c27444 100644 --- a/general/toaster/toastpkg/toastapp/toastapp.vcxproj +++ b/general/toaster/toastpkg/toastapp/toastapp.vcxproj @@ -19,11 +19,11 @@ - {A8A63599-6EC4-4103-93F2-67403B310ECE} + {4BE9B424-9C7C-495D-BECD-A04867DB2802} $(MSBuildProjectName) Debug Win32 - {52D1A92C-A8CF-4ED9-A4C5-DE0E54683CC9} + {00FF9348-781D-4F7C-B665-3C46FF4F4DBE} @@ -195,7 +195,6 @@ - diff --git a/general/toaster/toastpkg/toastapp/toastapp.vcxproj.Filters b/general/toaster/toastpkg/toastapp/toastapp.vcxproj.Filters index 3bd997c3f..21f9ddec4 100644 --- a/general/toaster/toastpkg/toastapp/toastapp.vcxproj.Filters +++ b/general/toaster/toastpkg/toastapp/toastapp.vcxproj.Filters @@ -3,15 +3,15 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {3CAB87A0-84A1-442D-A0DA-AFE6311FDF5F} + {67DF9821-5DF2-4390-8645-AA3893DBEB53} h;hpp;hxx;hm;inl;inc;xsd - {5745867E-8275-4764-8224-38EDA6FF5215} + {7FB050B7-6DF1-4D3D-BE1C-B6D7D27C122B} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {A5E2F374-BB6F-4016-AAEA-BFBB7C4B7DE1} + {08ED54F9-B7BC-42BA-B064-47CA51C6FE48} diff --git a/general/toaster/toastpkg/toastcd/ToastApp/setup.exe b/general/toaster/toastpkg/toastcd/ToastApp/setup.exe new file mode 100644 index 0000000000000000000000000000000000000000..9fcedf961d2be6611236959e53b33cb3ed2264d0 GIT binary patch literal 44032 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 literal 0 HcmV?d00001 diff --git a/general/toaster/toastpkg/toastcd/amd64/toastva.exe b/general/toaster/toastpkg/toastcd/amd64/toastva.exe new file mode 100644 index 0000000000000000000000000000000000000000..ebd46df9aaa7d51f38252422ca714d934ca52c39 GIT binary patch literal 98304 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 AIsgCw literal 0 HcmV?d00001 diff --git a/general/toaster/toastpkg/toastcd/amd64/tostrcls.dll b/general/toaster/toastpkg/toastcd/amd64/tostrcls.dll new file mode 100644 index 0000000000000000000000000000000000000000..80fe0dc0b93e2ebb09d12babc42a9b6b1d0842c0 GIT binary patch literal 16384 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 PHEX|ht4oUiCK32=EOXeG literal 0 HcmV?d00001 diff --git a/general/toaster/toastpkg/toastcd/amd64/tostrco2.dll b/general/toaster/toastpkg/toastcd/amd64/tostrco2.dll new file mode 100644 index 0000000000000000000000000000000000000000..58dfab320d9623ecf638fda32faf2b08b573e6b4 GIT binary patch literal 17920 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 literal 0 HcmV?d00001 diff --git a/general/toaster/toastpkg/toastcd/i386/toastva.exe b/general/toaster/toastpkg/toastcd/i386/toastva.exe new file mode 100644 index 0000000000000000000000000000000000000000..7bbb4a47c565e5733ac8ee93f0fcb702ff0f3597 GIT binary patch literal 95744 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 Kw7~y83;ciUe?Dpe literal 0 HcmV?d00001 diff --git a/general/toaster/toastpkg/toastcd/i386/tostrcls.dll b/general/toaster/toastpkg/toastcd/i386/tostrcls.dll new file mode 100644 index 0000000000000000000000000000000000000000..7ac337f86704b9496ca678db4bf4c1093e7c5350 GIT binary patch literal 15872 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 literal 0 HcmV?d00001 diff --git a/general/toaster/toastpkg/toastcd/i386/tostrco2.dll b/general/toaster/toastpkg/toastcd/i386/tostrco2.dll new file mode 100644 index 0000000000000000000000000000000000000000..3f7b3ed127635a312596a31ff19a1d0b67754e57 GIT binary patch literal 14336 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π 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 Xx*Q#b`eXXh0zX>dM+^KPwZMM?d0?M9 literal 0 HcmV?d00001 diff --git a/general/toaster/toastpkg/toastcd/toastpkg.inf b/general/toaster/toastpkg/toastcd/toastpkg.inf index f427ae873..f1edae867 100644 --- a/general/toaster/toastpkg/toastcd/toastpkg.inf +++ b/general/toaster/toastpkg/toastcd/toastpkg.inf @@ -23,7 +23,7 @@ Signature="$WINDOWS NT$" Class=TOASTER ClassGuid={B85B7C50-6A01-11d2-B841-00C04FAD5171} -Provider=%ToastRUs% +Provider=%ProviderName% DriverVer=09/21/2006,6.0.5736.1 CatalogFile.NTx86 = tostx86.cat CatalogFile.NTIA64 = tostia64.cat @@ -56,11 +56,6 @@ tostrcls.dll [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 @@ -129,9 +124,10 @@ tostrcls.dll = 1,, [Strings] SPSVCINST_ASSOCSERVICE= 0x00000002 +ProviderName = "TODO-Set-Provider" ToastRUs = "Toast'R'Us" ClassName = "Toaster" DiskId1 = "Toaster Device Installation Disk #1" ToasterDevice.DeviceDesc = "Toaster Package Sample Toaster" -toaster.SVCDESC = "Microsoft Toaster Device Driver" +toaster.SVCDESC = "Toaster Device Driver" FriendlyNameFormat = "ToasterDevice%1!u!" diff --git a/general/toaster/toastpkg/toastcd/tostx86.cat b/general/toaster/toastpkg/toastcd/tostx86.cat index 47a4fdf5d63c034ccb93717f3297cd879d691013..b16db9e9b981e375a07df95ec518c11f9f7b2cb2 100644 GIT binary patch delta 3967 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 vRh#ACP1znB^n7?izZ^ESEn4=LLSp delta 3712 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 diff --git a/general/toaster/toastpkg/toastcd/tstamd64.cat b/general/toaster/toastpkg/toastcd/tstamd64.cat index 5d5001306d953990a02339a56596345d1b17b275..637df4dbbb7acb427fbd9afd989be2a40660df3d 100644 GIT binary patch delta 3968 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 diff --git a/general/toaster/toastpkg/toastco/tostrco2.vcxproj b/general/toaster/toastpkg/toastco/tostrco2.vcxproj index e0cfe4d91..afaed3ff3 100644 --- a/general/toaster/toastpkg/toastco/tostrco2.vcxproj +++ b/general/toaster/toastpkg/toastco/tostrco2.vcxproj @@ -19,11 +19,11 @@ - {2C6F3F5F-06F7-4F86-8914-DB04CFDD9B19} + {2603EF56-C013-4A84-897A-76EA07FDBB27} $(MSBuildProjectName) Debug Win32 - {E58D7499-1030-49A4-9FD8-CB60E7F71D05} + {94575B57-4141-4421-94D8-C4826C2077AF} @@ -229,7 +229,6 @@ - diff --git a/general/toaster/toastpkg/toastco/tostrco2.vcxproj.Filters b/general/toaster/toastpkg/toastco/tostrco2.vcxproj.Filters index 4766e098a..f176be76f 100644 --- a/general/toaster/toastpkg/toastco/tostrco2.vcxproj.Filters +++ b/general/toaster/toastpkg/toastco/tostrco2.vcxproj.Filters @@ -3,15 +3,15 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {0202E791-D56D-4726-BDAF-E96655EA47F7} + {1EC35066-5352-4C66-AB55-7064A1D2710A} h;hpp;hxx;hm;inl;inc;xsd - {C5D6053C-04B1-46D4-94A3-6DDCD2C1D19C} + {461AEF17-4B21-4416-983F-205E529241F9} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {ECD2A7C2-F1AA-4184-B6BE-7CD1E8BBBCAE} + {D2257EDC-D839-4646-836B-75D16A94E4F0} diff --git a/general/toaster/toastpkg/toastpkg.sln b/general/toaster/toastpkg/toastpkg.sln index 7fd134359..dd1e999c7 100644 --- a/general/toaster/toastpkg/toastpkg.sln +++ b/general/toaster/toastpkg/toastpkg.sln @@ -3,19 +3,19 @@ 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", "{9A41588C-FCFB-4217-9153-99D10B0829EB}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Toastapp", "Toastapp", "{8638CA9F-2C57-4198-A01B-002B6A1226B2}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Toastco", "Toastco", "{F540D936-4986-46C1-94C2-EB4E7172BBEA}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Toastco", "Toastco", "{9B6AB1AE-E802-4AF9-B72B-D2262CFF65AD}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Toastva", "Toastva", "{E02E6DD1-0612-4CEE-A1F2-566FA71FB390}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Toastva", "Toastva", "{80ACC4C0-C750-4914-9A4F-D36076590E22}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "toastapp", "toastapp\toastapp.vcxproj", "{A8A63599-6EC4-4103-93F2-67403B310ECE}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "toastapp", "toastapp\toastapp.vcxproj", "{4BE9B424-9C7C-495D-BECD-A04867DB2802}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tostrco2", "toastco\tostrco2.vcxproj", "{2C6F3F5F-06F7-4F86-8914-DB04CFDD9B19}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tostrco2", "toastco\tostrco2.vcxproj", "{2603EF56-C013-4A84-897A-76EA07FDBB27}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "toastva", "toastva\toastva.vcxproj", "{85967D7E-7E0A-4DEC-950F-19C263C05B6A}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "toastva", "toastva\toastva.vcxproj", "{B5B06D58-AAF5-4E69-B2F0-FF835B5F7421}" ProjectSection(ProjectDependencies) = postProject - {2C6F3F5F-06F7-4F86-8914-DB04CFDD9B19} = {2C6F3F5F-06F7-4F86-8914-DB04CFDD9B19} + {2603EF56-C013-4A84-897A-76EA07FDBB27} = {2603EF56-C013-4A84-897A-76EA07FDBB27} EndProjectSection EndProject Global @@ -26,37 +26,37 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {A8A63599-6EC4-4103-93F2-67403B310ECE}.Debug|Win32.ActiveCfg = Debug|Win32 - {A8A63599-6EC4-4103-93F2-67403B310ECE}.Debug|Win32.Build.0 = Debug|Win32 - {A8A63599-6EC4-4103-93F2-67403B310ECE}.Release|Win32.ActiveCfg = Release|Win32 - {A8A63599-6EC4-4103-93F2-67403B310ECE}.Release|Win32.Build.0 = Release|Win32 - {A8A63599-6EC4-4103-93F2-67403B310ECE}.Debug|x64.ActiveCfg = Debug|x64 - {A8A63599-6EC4-4103-93F2-67403B310ECE}.Debug|x64.Build.0 = Debug|x64 - {A8A63599-6EC4-4103-93F2-67403B310ECE}.Release|x64.ActiveCfg = Release|x64 - {A8A63599-6EC4-4103-93F2-67403B310ECE}.Release|x64.Build.0 = Release|x64 - {2C6F3F5F-06F7-4F86-8914-DB04CFDD9B19}.Debug|Win32.ActiveCfg = Debug|Win32 - {2C6F3F5F-06F7-4F86-8914-DB04CFDD9B19}.Debug|Win32.Build.0 = Debug|Win32 - {2C6F3F5F-06F7-4F86-8914-DB04CFDD9B19}.Release|Win32.ActiveCfg = Release|Win32 - {2C6F3F5F-06F7-4F86-8914-DB04CFDD9B19}.Release|Win32.Build.0 = Release|Win32 - {2C6F3F5F-06F7-4F86-8914-DB04CFDD9B19}.Debug|x64.ActiveCfg = Debug|x64 - {2C6F3F5F-06F7-4F86-8914-DB04CFDD9B19}.Debug|x64.Build.0 = Debug|x64 - {2C6F3F5F-06F7-4F86-8914-DB04CFDD9B19}.Release|x64.ActiveCfg = Release|x64 - {2C6F3F5F-06F7-4F86-8914-DB04CFDD9B19}.Release|x64.Build.0 = Release|x64 - {85967D7E-7E0A-4DEC-950F-19C263C05B6A}.Debug|Win32.ActiveCfg = Debug|Win32 - {85967D7E-7E0A-4DEC-950F-19C263C05B6A}.Debug|Win32.Build.0 = Debug|Win32 - {85967D7E-7E0A-4DEC-950F-19C263C05B6A}.Release|Win32.ActiveCfg = Release|Win32 - {85967D7E-7E0A-4DEC-950F-19C263C05B6A}.Release|Win32.Build.0 = Release|Win32 - {85967D7E-7E0A-4DEC-950F-19C263C05B6A}.Debug|x64.ActiveCfg = Debug|x64 - {85967D7E-7E0A-4DEC-950F-19C263C05B6A}.Debug|x64.Build.0 = Debug|x64 - {85967D7E-7E0A-4DEC-950F-19C263C05B6A}.Release|x64.ActiveCfg = Release|x64 - {85967D7E-7E0A-4DEC-950F-19C263C05B6A}.Release|x64.Build.0 = Release|x64 + {4BE9B424-9C7C-495D-BECD-A04867DB2802}.Debug|Win32.ActiveCfg = Debug|Win32 + {4BE9B424-9C7C-495D-BECD-A04867DB2802}.Debug|Win32.Build.0 = Debug|Win32 + {4BE9B424-9C7C-495D-BECD-A04867DB2802}.Release|Win32.ActiveCfg = Release|Win32 + {4BE9B424-9C7C-495D-BECD-A04867DB2802}.Release|Win32.Build.0 = Release|Win32 + {4BE9B424-9C7C-495D-BECD-A04867DB2802}.Debug|x64.ActiveCfg = Debug|x64 + {4BE9B424-9C7C-495D-BECD-A04867DB2802}.Debug|x64.Build.0 = Debug|x64 + {4BE9B424-9C7C-495D-BECD-A04867DB2802}.Release|x64.ActiveCfg = Release|x64 + {4BE9B424-9C7C-495D-BECD-A04867DB2802}.Release|x64.Build.0 = Release|x64 + {2603EF56-C013-4A84-897A-76EA07FDBB27}.Debug|Win32.ActiveCfg = Debug|Win32 + {2603EF56-C013-4A84-897A-76EA07FDBB27}.Debug|Win32.Build.0 = Debug|Win32 + {2603EF56-C013-4A84-897A-76EA07FDBB27}.Release|Win32.ActiveCfg = Release|Win32 + {2603EF56-C013-4A84-897A-76EA07FDBB27}.Release|Win32.Build.0 = Release|Win32 + {2603EF56-C013-4A84-897A-76EA07FDBB27}.Debug|x64.ActiveCfg = Debug|x64 + {2603EF56-C013-4A84-897A-76EA07FDBB27}.Debug|x64.Build.0 = Debug|x64 + {2603EF56-C013-4A84-897A-76EA07FDBB27}.Release|x64.ActiveCfg = Release|x64 + {2603EF56-C013-4A84-897A-76EA07FDBB27}.Release|x64.Build.0 = Release|x64 + {B5B06D58-AAF5-4E69-B2F0-FF835B5F7421}.Debug|Win32.ActiveCfg = Debug|Win32 + {B5B06D58-AAF5-4E69-B2F0-FF835B5F7421}.Debug|Win32.Build.0 = Debug|Win32 + {B5B06D58-AAF5-4E69-B2F0-FF835B5F7421}.Release|Win32.ActiveCfg = Release|Win32 + {B5B06D58-AAF5-4E69-B2F0-FF835B5F7421}.Release|Win32.Build.0 = Release|Win32 + {B5B06D58-AAF5-4E69-B2F0-FF835B5F7421}.Debug|x64.ActiveCfg = Debug|x64 + {B5B06D58-AAF5-4E69-B2F0-FF835B5F7421}.Debug|x64.Build.0 = Debug|x64 + {B5B06D58-AAF5-4E69-B2F0-FF835B5F7421}.Release|x64.ActiveCfg = Release|x64 + {B5B06D58-AAF5-4E69-B2F0-FF835B5F7421}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {A8A63599-6EC4-4103-93F2-67403B310ECE} = {9A41588C-FCFB-4217-9153-99D10B0829EB} - {2C6F3F5F-06F7-4F86-8914-DB04CFDD9B19} = {F540D936-4986-46C1-94C2-EB4E7172BBEA} - {85967D7E-7E0A-4DEC-950F-19C263C05B6A} = {E02E6DD1-0612-4CEE-A1F2-566FA71FB390} + {4BE9B424-9C7C-495D-BECD-A04867DB2802} = {8638CA9F-2C57-4198-A01B-002B6A1226B2} + {2603EF56-C013-4A84-897A-76EA07FDBB27} = {9B6AB1AE-E802-4AF9-B72B-D2262CFF65AD} + {B5B06D58-AAF5-4E69-B2F0-FF835B5F7421} = {80ACC4C0-C750-4914-9A4F-D36076590E22} EndGlobalSection EndGlobal diff --git a/general/toaster/toastpkg/toastva/toastva.vcxproj b/general/toaster/toastpkg/toastva/toastva.vcxproj index e9aa44324..55a4cfb60 100644 --- a/general/toaster/toastpkg/toastva/toastva.vcxproj +++ b/general/toaster/toastpkg/toastva/toastva.vcxproj @@ -19,11 +19,11 @@ - {85967D7E-7E0A-4DEC-950F-19C263C05B6A} + {B5B06D58-AAF5-4E69-B2F0-FF835B5F7421} $(MSBuildProjectName) Debug Win32 - {D7B39220-AA74-4EA0-B07C-59CA1E85D809} + {43F6E849-808D-47F0-9F7A-4D173795892D} @@ -207,7 +207,6 @@ - diff --git a/general/toaster/toastpkg/toastva/toastva.vcxproj.Filters b/general/toaster/toastpkg/toastva/toastva.vcxproj.Filters index bc07fd8a6..af5a1160b 100644 --- a/general/toaster/toastpkg/toastva/toastva.vcxproj.Filters +++ b/general/toaster/toastpkg/toastva/toastva.vcxproj.Filters @@ -3,15 +3,15 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {A355FE6D-8F54-4AE5-BB0A-8097742C6850} + {69A8D8BA-92F5-4CB0-A45E-6F00B9831369} h;hpp;hxx;hm;inl;inc;xsd - {D196262B-5F82-4ABA-AC81-0469C32AA57A} + {37B69B6B-8428-4579-9C66-618C8E6B9003} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {2A7EB223-FB84-4B57-A4F3-78C1D05435A6} + {9F8FFEAF-405F-44A2-BAE8-E44AC9BDF69D} diff --git a/general/toaster/umdf2/Package/package.VcxProj b/general/toaster/umdf2/Package/package.VcxProj new file mode 100644 index 000000000..0b9bc62d9 --- /dev/null +++ b/general/toaster/umdf2/Package/package.VcxProj @@ -0,0 +1,87 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + + {322F1E83-402B-49F0-B206-FD44F046091F} + + + {2FB86AE0-CD30-4E4E-93D1-306A8982263C} + + + + WindowsKernelModeDriver10.0 + Utility + Package + true + Debug + + + + {AF4C3FCE-D27E-4E5E-9914-D8D7D1BD5B76} + {1DF2195C-0F0C-4679-8995-133027CE291F} + $(MSBuildProjectName) + + + Windows10 + true + + + Windows10 + false + + + Windows10 + true + + + Windows10 + false + + + + + + + + + + + DbgengRemoteDebugger + False + None + + + + + + %PathToInf% + False + False + True + + 133563 + + + + + + + \ 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 000000000..1761b725b --- /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;* + {D2DAB9A9-5425-40C2-87AA-D9786B3E42F5} + + + h;hpp;hxx;hm;inl;inc;xsd + {7D7C232A-C3B7-4775-B0F1-9EF3256E4A00} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {BF9AF006-025D-4D97-8CC6-848B3618B69C} + + + inf;inv;inx;mof;mc; + {E1B01869-FAF8-4E60-93B6-1DEA6295B589} + + + \ 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 000000000..7f2b6a569 --- /dev/null +++ b/general/toaster/umdf2/ReadMe.md @@ -0,0 +1,51 @@ +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. + + +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 000000000..555242148 --- /dev/null +++ b/general/toaster/umdf2/exe/enum/Enum.vcxproj @@ -0,0 +1,182 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {12CC02AE-DC1B-4B19-966F-9F324828D94D} + $(MSBuildProjectName) + Debug + Win32 + {EAE54135-6F6A-4694-B2BE-8EF7AD38CFDE} + + + + Windows10 + False + Universal + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + True + Universal + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + False + Universal + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + True + Universal + + WindowsApplicationForDrivers10.0 + Application + + + + $(IntDir) + + + + + + + + + + + + + + + + Enum + + + Enum + + + Enum + + + Enum + + + + %(AdditionalIncludeDirectories);..\..\inc + %(PreprocessorDefinitions);EVENT_TRACING;UNICODE;_UNICODE + + + %(AdditionalIncludeDirectories);..\..\inc + %(PreprocessorDefinitions);EVENT_TRACING;UNICODE;_UNICODE + + + + + %(AdditionalIncludeDirectories);..\..\inc + %(PreprocessorDefinitions);EVENT_TRACING;UNICODE;_UNICODE + + + %(AdditionalDependencies);mincore.lib + + + + + %(AdditionalIncludeDirectories);..\..\inc + %(PreprocessorDefinitions);EVENT_TRACING;UNICODE;_UNICODE + + + %(AdditionalIncludeDirectories);..\..\inc + %(PreprocessorDefinitions);EVENT_TRACING;UNICODE;_UNICODE + + + + + %(AdditionalIncludeDirectories);..\..\inc + %(PreprocessorDefinitions);EVENT_TRACING;UNICODE;_UNICODE + + + %(AdditionalDependencies);mincore.lib + + + + + %(AdditionalIncludeDirectories);..\..\inc + %(PreprocessorDefinitions);EVENT_TRACING;UNICODE;_UNICODE + + + %(AdditionalIncludeDirectories);..\..\inc + %(PreprocessorDefinitions);EVENT_TRACING;UNICODE;_UNICODE + + + + + %(AdditionalIncludeDirectories);..\..\inc + %(PreprocessorDefinitions);EVENT_TRACING;UNICODE;_UNICODE + + + %(AdditionalDependencies);mincore.lib + + + + + %(AdditionalIncludeDirectories);..\..\inc + %(PreprocessorDefinitions);EVENT_TRACING;UNICODE;_UNICODE + + + %(AdditionalIncludeDirectories);..\..\inc + %(PreprocessorDefinitions);EVENT_TRACING;UNICODE;_UNICODE + + + + + %(AdditionalIncludeDirectories);..\..\inc + %(PreprocessorDefinitions);EVENT_TRACING;UNICODE;_UNICODE + + + %(AdditionalDependencies);mincore.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 000000000..952d81568 --- /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;* + {58733B34-2D37-4533-A53B-61D148D97CC2} + + + h;hpp;hxx;hm;inl;inc;xsd + {4C9923EA-9B5C-4DD4-BF2F-EBAA7B6815C0} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {A563728A-CB57-4506-BEC0-BA328E4BC76C} + + + + + 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 000000000..a8799fa74 --- /dev/null +++ b/general/toaster/umdf2/exe/enum/enum.c @@ -0,0 +1,307 @@ +/*++ + +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 +#include "public.h" +#include + +// +// Prototypes +// +BOOLEAN +GetDevicePath( + _In_ LPCGUID InterfaceGuid, + _Out_writes_(BufLen) PWCHAR DevicePath, + _In_ size_t BufLen + ); + +BOOLEAN +OpenBusInterface( + _In_z_ LPCWSTR DevicePath + ); + +#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" + +#define MAX_DEVPATH_LENGTH 256 + +BOOLEAN bPlugIn, bUnplug, bEject; +ULONG SerialNo; + +INT __cdecl +main( + _In_ ULONG argc, + _In_reads_(argc) PCHAR argv[] + ) +{ + WCHAR devicePath[MAX_DEVPATH_LENGTH] = { 0 }; + + 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; + + if (GetDevicePath(&GUID_DEVINTERFACE_BUSENUM_TOASTER, + devicePath, + sizeof(devicePath) / sizeof(devicePath[0]))) { + OpenBusInterface(devicePath); + } + + return 0; +usage: + printf(USAGE); + exit(0); +} + +BOOLEAN +OpenBusInterface ( + _In_z_ LPCWSTR DevicePath + ) +{ + HANDLE file; + ULONG bytes; + BUSENUM_UNPLUG_HARDWARE unplug; + BUSENUM_EJECT_HARDWARE eject; + PBUSENUM_PLUGIN_HARDWARE hardware; + BOOLEAN bSuccess = FALSE; + + printf("Opening %ws\n", DevicePath); + + file = CreateFile(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()); + goto End; + } + + printf("Bus interface opened!!!\n"); + + // + // 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: + if (INVALID_HANDLE_VALUE != file) { + CloseHandle(file); + } + return bSuccess; +} + +BOOLEAN +GetDevicePath( + _In_ LPCGUID InterfaceGuid, + _Out_writes_(BufLen) PWCHAR DevicePath, + _In_ size_t BufLen +) +{ + CONFIGRET cr = CR_SUCCESS; + PWSTR deviceInterfaceList = NULL; + ULONG deviceInterfaceListLength = 0; + PWSTR nextInterface; + HRESULT hr = E_FAIL; + BOOLEAN bRet = TRUE; + + cr = CM_Get_Device_Interface_List_Size( + &deviceInterfaceListLength, + (LPGUID)InterfaceGuid, + NULL, + CM_GET_DEVICE_INTERFACE_LIST_PRESENT); + if (cr != CR_SUCCESS) { + printf("Error 0x%x retrieving device interface list size.\n", cr); + goto clean0; + } + + if (deviceInterfaceListLength <= 1) { + bRet = FALSE; + printf("Error: No active device interfaces found.\n" + " Is the sample driver loaded?"); + goto clean0; + } + + deviceInterfaceList = (PWSTR)malloc(deviceInterfaceListLength * sizeof(WCHAR)); + if (deviceInterfaceList == NULL) { + printf("Error allocating memory for device interface list.\n"); + goto clean0; + } + ZeroMemory(deviceInterfaceList, deviceInterfaceListLength * sizeof(WCHAR)); + + cr = CM_Get_Device_Interface_List( + (LPGUID)InterfaceGuid, + NULL, + deviceInterfaceList, + deviceInterfaceListLength, + CM_GET_DEVICE_INTERFACE_LIST_PRESENT); + if (cr != CR_SUCCESS) { + printf("Error 0x%x retrieving device interface list.\n", cr); + goto clean0; + } + + nextInterface = deviceInterfaceList + wcslen(deviceInterfaceList) + 1; + if (*nextInterface != UNICODE_NULL) { + printf("Warning: More than one device interface instance found. \n" + "Selecting first matching device.\n\n"); + } + + hr = StringCchCopy(DevicePath, BufLen, deviceInterfaceList); + if (FAILED(hr)) { + bRet = FALSE; + printf("Error: StringCchCopy failed with HRESULT 0x%x", hr); + goto clean0; + } + +clean0: + if (deviceInterfaceList != NULL) { + free(deviceInterfaceList); + } + if (CR_SUCCESS != cr) { + bRet = FALSE; + } + + return bRet; +} diff --git a/general/toaster/umdf2/exe/notify/notify.c b/general/toaster/umdf2/exe/notify/notify.c new file mode 100644 index 000000000..3362ce3d9 --- /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 000000000..98db2918b --- /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 000000000..eb15657f9 --- /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 000000000..ecee40860 --- /dev/null +++ b/general/toaster/umdf2/exe/notify/notify.vcxproj @@ -0,0 +1,171 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {A26CCD87-3E7D-47C0-A7D2-46FF83F3D248} + $(MSBuildProjectName) + Debug + Win32 + {0CE9F05C-A3F4-4E23-9057-DB90E8DB24F1} + + + + 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 000000000..ec0b3c289 --- /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;* + {6B905269-3A91-4F64-9E49-E5A9BA33D6F2} + + + h;hpp;hxx;hm;inl;inc;xsd + {A9BEAF80-2298-41A9-A7CF-3315D06823C1} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {E8D961C9-3289-486E-87E7-A27F36334C88} + + + + + 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 0000000000000000000000000000000000000000..77ad2081ae46c21521d966a06d53d8429cacd307 GIT binary patch literal 4534 zcmeH~KTKRl5Ql#UNSrL>3k?;L&lFMUkhY?7M33Z#8dqD>VJUxj6{bY8gtV1i#8FbD zA!VAYQkbowF|s8JEEI`H#Yv7b`F7qr42q>RQ5wHJW@l%1cjkMu`%Z_{)0Bq8FI3w5 zDd|h}%8Hx%>ArM-TKWoehdIta0qPghfwLKG<_bQz*F8)J0F1usdegW*>e<{r1AHzjeoMNW@bkHeqVEQbDE!@*TTYr78e(_w6vt<sC-RpgO5{(}Cc%(k zNYtSm8&&bA$AhLX$4Ct_`7&%7j!Zs3+QLB%G90;Z@L+!2Y|5}?STYcqgnk8J$!yFl z$~ZDC{2|2QOfoFF)WccIZ78RJ4+ew5U@#aA27|$`6AT#!gTY|PFc1b|Jq!ke!C){D z8o>{P!C){L07m{yFk_>!C=w%LM>x!5mo-rj^-7zYo8rq(ySuwOI5^PJ(UA@h4|Q^K zqSMn;ot>TO;^IPAS63R3$ErV=et7wk_qd|E +#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 000000000..8eb09f0b0 --- /dev/null +++ b/general/toaster/umdf2/exe/toast/toast.vcxproj @@ -0,0 +1,170 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {73C36066-9CDD-4D33-AACB-4A1B54083FC9} + $(MSBuildProjectName) + Debug + Win32 + {7EA1DD91-8456-47F7-BDE0-4F27F5EA522F} + + + + 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 000000000..6705206b7 --- /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;* + {CBE9C966-D1FD-4F05-95F6-AF6BD7E02A4D} + + + h;hpp;hxx;hm;inl;inc;xsd + {75AE28AC-313C-498E-9FD3-86F7E7849E1E} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {9C3CDC3D-85F9-430D-9FCB-A2769D5699F4} + + + + + 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 000000000..d245c2bab --- /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 000000000..e9042c291 --- /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 000000000..0c108f448 --- /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 000000000..431550a0e --- /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=%ProviderString% +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 +ProviderString = "TODO-Set-Provider" +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 000000000..0f32ed2dd --- /dev/null +++ b/general/toaster/umdf2/filter/generic/filterum.vcxproj @@ -0,0 +1,199 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {322F1E83-402B-49F0-B206-FD44F046091F} + $(MSBuildProjectName) + 2 + false + true + Debug + Win32 + {27CAA789-59E6-4CE6-A3C4-1E32A3B836B7} + + + + 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 + %(PreprocessorDefinitions);___TARGETNAME="""filterum.DYNLINK""" + + + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib + + + %(AdditionalIncludeDirectories);..\..\inc + %(PreprocessorDefinitions);___TARGETNAME="""filterum.DYNLINK""" + + + %(AdditionalIncludeDirectories);..\..\inc + %(PreprocessorDefinitions);___TARGETNAME="""filterum.DYNLINK""" + + + + + true + Level4 + %(AdditionalIncludeDirectories);..\..\inc + %(PreprocessorDefinitions);___TARGETNAME="""filterum.DYNLINK""" + + + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib + + + %(AdditionalIncludeDirectories);..\..\inc + %(PreprocessorDefinitions);___TARGETNAME="""filterum.DYNLINK""" + + + %(AdditionalIncludeDirectories);..\..\inc + %(PreprocessorDefinitions);___TARGETNAME="""filterum.DYNLINK""" + + + + + true + Level4 + %(AdditionalIncludeDirectories);..\..\inc + %(PreprocessorDefinitions);___TARGETNAME="""filterum.DYNLINK""" + + + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib + + + %(AdditionalIncludeDirectories);..\..\inc + %(PreprocessorDefinitions);___TARGETNAME="""filterum.DYNLINK""" + + + %(AdditionalIncludeDirectories);..\..\inc + %(PreprocessorDefinitions);___TARGETNAME="""filterum.DYNLINK""" + + + + + true + Level4 + %(AdditionalIncludeDirectories);..\..\inc + %(PreprocessorDefinitions);___TARGETNAME="""filterum.DYNLINK""" + + + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib + + + %(AdditionalIncludeDirectories);..\..\inc + %(PreprocessorDefinitions);___TARGETNAME="""filterum.DYNLINK""" + + + %(AdditionalIncludeDirectories);..\..\inc + %(PreprocessorDefinitions);___TARGETNAME="""filterum.DYNLINK""" + + + + + + + + + + + + + + + + + + + \ 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 000000000..fbc2255e9 --- /dev/null +++ b/general/toaster/umdf2/filter/generic/filterum.vcxproj.Filters @@ -0,0 +1,31 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {D2172684-87C0-4852-875A-0CA8BD491BD7} + + + h;hpp;hxx;hm;inl;inc;xsd + {4613B345-34B0-4251-93CF-3414D7B1C840} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {9BB65910-D35E-42C5-BF39-F62B4D9F973B} + + + inf;inv;inx;mof;mc; + {3FF9C7B5-8269-4401-8BB5-225709D52804} + + + + + 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 000000000..60b5df783 --- /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 000000000..c25eebd4d --- /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 000000000..e9ba2ea8a --- /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 000000000..b6c650ff2 --- /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=%ProviderString% +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 +ProviderString = "TODO-Set-Provider" +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 000000000..e580d5409 --- /dev/null +++ b/general/toaster/umdf2/func/featured/wdffeaturedum.vcxproj @@ -0,0 +1,179 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {F0EA96C5-8CF6-4978-9DB0-179579D4C6D5} + $(MSBuildProjectName) + 2 + Debug + Win32 + {70A815C1-5D9C-419C-8379-F61A04BA3DD1} + + + + 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 000000000..bdfdc4f9d --- /dev/null +++ b/general/toaster/umdf2/func/featured/wdffeaturedum.vcxproj.Filters @@ -0,0 +1,39 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {4C00BD29-A08C-48FF-9326-65CB99A38B85} + + + h;hpp;hxx;hm;inl;inc;xsd + {588F07FC-8AC0-4132-B7D8-A2C7C589CBA1} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {F07FF097-C35A-47EB-A0F6-1B7AF7677990} + + + inf;inv;inx;mof;mc; + {AEBC341B-92EB-4BFD-980F-F69DD9AEC99C} + + + + + 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 000000000..006166e79 --- /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 000000000..6ed215dbd --- /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 000000000..0cd117e2b --- /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=%ProviderString% +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 +ProviderString = "TODO-Set-Provider" +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 000000000..f947f97e6 --- /dev/null +++ b/general/toaster/umdf2/func/simple/wdfsimpleum.vcxproj @@ -0,0 +1,191 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {2FB86AE0-CD30-4E4E-93D1-306A8982263C} + $(MSBuildProjectName) + 2 + false + true + Debug + Win32 + {DDC9A998-E2C4-47F7-AB33-D0F4C54E1A07} + + + + 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 + %(PreprocessorDefinitions);___TARGETNAME="""wdfsimpleum.DYNLINK""" + + + %(AdditionalIncludeDirectories);..\..\inc;..\shared + %(PreprocessorDefinitions);___TARGETNAME="""wdfsimpleum.DYNLINK""" + + + + + %(AdditionalIncludeDirectories);..\..\inc;..\shared + %(PreprocessorDefinitions);___TARGETNAME="""wdfsimpleum.DYNLINK""" + + + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib + + + %(AdditionalIncludeDirectories);..\..\inc;..\shared + %(PreprocessorDefinitions);___TARGETNAME="""wdfsimpleum.DYNLINK""" + + + %(AdditionalIncludeDirectories);..\..\inc;..\shared + %(PreprocessorDefinitions);___TARGETNAME="""wdfsimpleum.DYNLINK""" + + + + + %(AdditionalIncludeDirectories);..\..\inc;..\shared + %(PreprocessorDefinitions);___TARGETNAME="""wdfsimpleum.DYNLINK""" + + + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib + + + %(AdditionalIncludeDirectories);..\..\inc;..\shared + %(PreprocessorDefinitions);___TARGETNAME="""wdfsimpleum.DYNLINK""" + + + %(AdditionalIncludeDirectories);..\..\inc;..\shared + %(PreprocessorDefinitions);___TARGETNAME="""wdfsimpleum.DYNLINK""" + + + + + %(AdditionalIncludeDirectories);..\..\inc;..\shared + %(PreprocessorDefinitions);___TARGETNAME="""wdfsimpleum.DYNLINK""" + + + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib + + + %(AdditionalIncludeDirectories);..\..\inc;..\shared + %(PreprocessorDefinitions);___TARGETNAME="""wdfsimpleum.DYNLINK""" + + + %(AdditionalIncludeDirectories);..\..\inc;..\shared + %(PreprocessorDefinitions);___TARGETNAME="""wdfsimpleum.DYNLINK""" + + + + + %(AdditionalIncludeDirectories);..\..\inc;..\shared + %(PreprocessorDefinitions);___TARGETNAME="""wdfsimpleum.DYNLINK""" + + + + + + + + + + + + + + + + + + + \ 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 000000000..fb8738bb1 --- /dev/null +++ b/general/toaster/umdf2/func/simple/wdfsimpleum.vcxproj.Filters @@ -0,0 +1,31 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {DC7382CC-718D-4177-8EBD-6A3AAA546FA2} + + + h;hpp;hxx;hm;inl;inc;xsd + {0B35AC27-FBA9-482C-AB2D-A0B9A44DFBFB} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {648AB4A2-DE81-48B3-9E36-5A767F698567} + + + inf;inv;inx;mof;mc; + {73524C9E-B6C0-4702-B212-55BDE5F61FFB} + + + + + 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 000000000..b10733632 --- /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 000000000..0628f95f9 --- /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 000000000..e47cbb9c4 --- /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", "{ED63C611-D43D-42E8-A083-AB082C31A0DF}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Notify", "Notify", "{3055CBF7-B33A-4DED-BFB5-19DE53027CF2}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Exe", "Exe", "{06CC3519-A442-4DC8-93B5-3320863FB409}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Toast", "Toast", "{48DB3120-7565-4863-8C99-C0DE5FD2DC11}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Enum", "Enum", "{B0E9361C-6773-4E78-870D-10887BBE6D40}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Simple", "Simple", "{CE2CDD3A-ED87-475C-BE33-4ED4C288884D}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Func", "Func", "{38DD140E-6B82-43DA-8269-FF850206DAD6}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Featured", "Featured", "{8A73614A-6351-42D3-A518-9C6B504D9E55}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Generic", "Generic", "{E7B355DF-2054-45A0-BF14-B78D6E61DDF1}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Filter", "Filter", "{8CADBFA7-5C4A-46FA-9171-10827B7BB3F9}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "package", "Package\package.VcxProj", "{AF4C3FCE-D27E-4E5E-9914-D8D7D1BD5B76}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "notify", "exe\notify\notify.vcxproj", "{A26CCD87-3E7D-47C0-A7D2-46FF83F3D248}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "toast", "exe\toast\toast.vcxproj", "{73C36066-9CDD-4D33-AACB-4A1B54083FC9}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Enum", "exe\enum\Enum.vcxproj", "{12CC02AE-DC1B-4B19-966F-9F324828D94D}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wdfsimpleum", "func\simple\wdfsimpleum.vcxproj", "{2FB86AE0-CD30-4E4E-93D1-306A8982263C}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wdffeaturedum", "func\featured\wdffeaturedum.vcxproj", "{F0EA96C5-8CF6-4978-9DB0-179579D4C6D5}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "filterum", "filter\generic\filterum.vcxproj", "{322F1E83-402B-49F0-B206-FD44F046091F}" +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 + {AF4C3FCE-D27E-4E5E-9914-D8D7D1BD5B76}.Debug|Win32.ActiveCfg = Debug|Win32 + {AF4C3FCE-D27E-4E5E-9914-D8D7D1BD5B76}.Debug|Win32.Build.0 = Debug|Win32 + {AF4C3FCE-D27E-4E5E-9914-D8D7D1BD5B76}.Release|Win32.ActiveCfg = Release|Win32 + {AF4C3FCE-D27E-4E5E-9914-D8D7D1BD5B76}.Release|Win32.Build.0 = Release|Win32 + {AF4C3FCE-D27E-4E5E-9914-D8D7D1BD5B76}.Debug|x64.ActiveCfg = Debug|x64 + {AF4C3FCE-D27E-4E5E-9914-D8D7D1BD5B76}.Debug|x64.Build.0 = Debug|x64 + {AF4C3FCE-D27E-4E5E-9914-D8D7D1BD5B76}.Release|x64.ActiveCfg = Release|x64 + {AF4C3FCE-D27E-4E5E-9914-D8D7D1BD5B76}.Release|x64.Build.0 = Release|x64 + {A26CCD87-3E7D-47C0-A7D2-46FF83F3D248}.Debug|Win32.ActiveCfg = Debug|Win32 + {A26CCD87-3E7D-47C0-A7D2-46FF83F3D248}.Debug|Win32.Build.0 = Debug|Win32 + {A26CCD87-3E7D-47C0-A7D2-46FF83F3D248}.Release|Win32.ActiveCfg = Release|Win32 + {A26CCD87-3E7D-47C0-A7D2-46FF83F3D248}.Release|Win32.Build.0 = Release|Win32 + {A26CCD87-3E7D-47C0-A7D2-46FF83F3D248}.Debug|x64.ActiveCfg = Debug|x64 + {A26CCD87-3E7D-47C0-A7D2-46FF83F3D248}.Debug|x64.Build.0 = Debug|x64 + {A26CCD87-3E7D-47C0-A7D2-46FF83F3D248}.Release|x64.ActiveCfg = Release|x64 + {A26CCD87-3E7D-47C0-A7D2-46FF83F3D248}.Release|x64.Build.0 = Release|x64 + {73C36066-9CDD-4D33-AACB-4A1B54083FC9}.Debug|Win32.ActiveCfg = Debug|Win32 + {73C36066-9CDD-4D33-AACB-4A1B54083FC9}.Debug|Win32.Build.0 = Debug|Win32 + {73C36066-9CDD-4D33-AACB-4A1B54083FC9}.Release|Win32.ActiveCfg = Release|Win32 + {73C36066-9CDD-4D33-AACB-4A1B54083FC9}.Release|Win32.Build.0 = Release|Win32 + {73C36066-9CDD-4D33-AACB-4A1B54083FC9}.Debug|x64.ActiveCfg = Debug|x64 + {73C36066-9CDD-4D33-AACB-4A1B54083FC9}.Debug|x64.Build.0 = Debug|x64 + {73C36066-9CDD-4D33-AACB-4A1B54083FC9}.Release|x64.ActiveCfg = Release|x64 + {73C36066-9CDD-4D33-AACB-4A1B54083FC9}.Release|x64.Build.0 = Release|x64 + {12CC02AE-DC1B-4B19-966F-9F324828D94D}.Debug|Win32.ActiveCfg = Debug|Win32 + {12CC02AE-DC1B-4B19-966F-9F324828D94D}.Debug|Win32.Build.0 = Debug|Win32 + {12CC02AE-DC1B-4B19-966F-9F324828D94D}.Release|Win32.ActiveCfg = Release|Win32 + {12CC02AE-DC1B-4B19-966F-9F324828D94D}.Release|Win32.Build.0 = Release|Win32 + {12CC02AE-DC1B-4B19-966F-9F324828D94D}.Debug|x64.ActiveCfg = Debug|x64 + {12CC02AE-DC1B-4B19-966F-9F324828D94D}.Debug|x64.Build.0 = Debug|x64 + {12CC02AE-DC1B-4B19-966F-9F324828D94D}.Release|x64.ActiveCfg = Release|x64 + {12CC02AE-DC1B-4B19-966F-9F324828D94D}.Release|x64.Build.0 = Release|x64 + {2FB86AE0-CD30-4E4E-93D1-306A8982263C}.Debug|Win32.ActiveCfg = Debug|Win32 + {2FB86AE0-CD30-4E4E-93D1-306A8982263C}.Debug|Win32.Build.0 = Debug|Win32 + {2FB86AE0-CD30-4E4E-93D1-306A8982263C}.Release|Win32.ActiveCfg = Release|Win32 + {2FB86AE0-CD30-4E4E-93D1-306A8982263C}.Release|Win32.Build.0 = Release|Win32 + {2FB86AE0-CD30-4E4E-93D1-306A8982263C}.Debug|x64.ActiveCfg = Debug|x64 + {2FB86AE0-CD30-4E4E-93D1-306A8982263C}.Debug|x64.Build.0 = Debug|x64 + {2FB86AE0-CD30-4E4E-93D1-306A8982263C}.Release|x64.ActiveCfg = Release|x64 + {2FB86AE0-CD30-4E4E-93D1-306A8982263C}.Release|x64.Build.0 = Release|x64 + {F0EA96C5-8CF6-4978-9DB0-179579D4C6D5}.Debug|Win32.ActiveCfg = Debug|Win32 + {F0EA96C5-8CF6-4978-9DB0-179579D4C6D5}.Debug|Win32.Build.0 = Debug|Win32 + {F0EA96C5-8CF6-4978-9DB0-179579D4C6D5}.Release|Win32.ActiveCfg = Release|Win32 + {F0EA96C5-8CF6-4978-9DB0-179579D4C6D5}.Release|Win32.Build.0 = Release|Win32 + {F0EA96C5-8CF6-4978-9DB0-179579D4C6D5}.Debug|x64.ActiveCfg = Debug|x64 + {F0EA96C5-8CF6-4978-9DB0-179579D4C6D5}.Debug|x64.Build.0 = Debug|x64 + {F0EA96C5-8CF6-4978-9DB0-179579D4C6D5}.Release|x64.ActiveCfg = Release|x64 + {F0EA96C5-8CF6-4978-9DB0-179579D4C6D5}.Release|x64.Build.0 = Release|x64 + {322F1E83-402B-49F0-B206-FD44F046091F}.Debug|Win32.ActiveCfg = Debug|Win32 + {322F1E83-402B-49F0-B206-FD44F046091F}.Debug|Win32.Build.0 = Debug|Win32 + {322F1E83-402B-49F0-B206-FD44F046091F}.Release|Win32.ActiveCfg = Release|Win32 + {322F1E83-402B-49F0-B206-FD44F046091F}.Release|Win32.Build.0 = Release|Win32 + {322F1E83-402B-49F0-B206-FD44F046091F}.Debug|x64.ActiveCfg = Debug|x64 + {322F1E83-402B-49F0-B206-FD44F046091F}.Debug|x64.Build.0 = Debug|x64 + {322F1E83-402B-49F0-B206-FD44F046091F}.Release|x64.ActiveCfg = Release|x64 + {322F1E83-402B-49F0-B206-FD44F046091F}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {AF4C3FCE-D27E-4E5E-9914-D8D7D1BD5B76} = {ED63C611-D43D-42E8-A083-AB082C31A0DF} + {A26CCD87-3E7D-47C0-A7D2-46FF83F3D248} = {3055CBF7-B33A-4DED-BFB5-19DE53027CF2} + {73C36066-9CDD-4D33-AACB-4A1B54083FC9} = {48DB3120-7565-4863-8C99-C0DE5FD2DC11} + {12CC02AE-DC1B-4B19-966F-9F324828D94D} = {B0E9361C-6773-4E78-870D-10887BBE6D40} + {2FB86AE0-CD30-4E4E-93D1-306A8982263C} = {CE2CDD3A-ED87-475C-BE33-4ED4C288884D} + {F0EA96C5-8CF6-4978-9DB0-179579D4C6D5} = {8A73614A-6351-42D3-A518-9C6B504D9E55} + {322F1E83-402B-49F0-B206-FD44F046091F} = {E7B355DF-2054-45A0-BF14-B78D6E61DDF1} + {3055CBF7-B33A-4DED-BFB5-19DE53027CF2} = {06CC3519-A442-4DC8-93B5-3320863FB409} + {48DB3120-7565-4863-8C99-C0DE5FD2DC11} = {06CC3519-A442-4DC8-93B5-3320863FB409} + {B0E9361C-6773-4E78-870D-10887BBE6D40} = {06CC3519-A442-4DC8-93B5-3320863FB409} + {CE2CDD3A-ED87-475C-BE33-4ED4C288884D} = {38DD140E-6B82-43DA-8269-FF850206DAD6} + {8A73614A-6351-42D3-A518-9C6B504D9E55} = {38DD140E-6B82-43DA-8269-FF850206DAD6} + {E7B355DF-2054-45A0-BF14-B78D6E61DDF1} = {8CADBFA7-5C4A-46FA-9171-10827B7BB3F9} + EndGlobalSection +EndGlobal diff --git a/general/tracing/SystemTraceControl/SystemTraceControl.sln b/general/tracing/SystemTraceControl/SystemTraceControl.sln index c67a9a929..2afb4ba49 100644 --- a/general/tracing/SystemTraceControl/SystemTraceControl.sln +++ b/general/tracing/SystemTraceControl/SystemTraceControl.sln @@ -3,7 +3,7 @@ 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", "{968DA402-7888-42A5-B2C3-676B80134771}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SystemTraceControl", "SystemTraceControl.vcxproj", "{E4F6F3AB-051E-4B70-B942-2E5E17C12F6A}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {968DA402-7888-42A5-B2C3-676B80134771}.Debug|Win32.ActiveCfg = Debug|Win32 - {968DA402-7888-42A5-B2C3-676B80134771}.Debug|Win32.Build.0 = Debug|Win32 - {968DA402-7888-42A5-B2C3-676B80134771}.Release|Win32.ActiveCfg = Release|Win32 - {968DA402-7888-42A5-B2C3-676B80134771}.Release|Win32.Build.0 = Release|Win32 - {968DA402-7888-42A5-B2C3-676B80134771}.Debug|x64.ActiveCfg = Debug|x64 - {968DA402-7888-42A5-B2C3-676B80134771}.Debug|x64.Build.0 = Debug|x64 - {968DA402-7888-42A5-B2C3-676B80134771}.Release|x64.ActiveCfg = Release|x64 - {968DA402-7888-42A5-B2C3-676B80134771}.Release|x64.Build.0 = Release|x64 + {E4F6F3AB-051E-4B70-B942-2E5E17C12F6A}.Debug|Win32.ActiveCfg = Debug|Win32 + {E4F6F3AB-051E-4B70-B942-2E5E17C12F6A}.Debug|Win32.Build.0 = Debug|Win32 + {E4F6F3AB-051E-4B70-B942-2E5E17C12F6A}.Release|Win32.ActiveCfg = Release|Win32 + {E4F6F3AB-051E-4B70-B942-2E5E17C12F6A}.Release|Win32.Build.0 = Release|Win32 + {E4F6F3AB-051E-4B70-B942-2E5E17C12F6A}.Debug|x64.ActiveCfg = Debug|x64 + {E4F6F3AB-051E-4B70-B942-2E5E17C12F6A}.Debug|x64.Build.0 = Debug|x64 + {E4F6F3AB-051E-4B70-B942-2E5E17C12F6A}.Release|x64.ActiveCfg = Release|x64 + {E4F6F3AB-051E-4B70-B942-2E5E17C12F6A}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/general/tracing/SystemTraceControl/SystemTraceControl.vcxproj b/general/tracing/SystemTraceControl/SystemTraceControl.vcxproj index a40899b50..adc3b39e8 100644 --- a/general/tracing/SystemTraceControl/SystemTraceControl.vcxproj +++ b/general/tracing/SystemTraceControl/SystemTraceControl.vcxproj @@ -19,11 +19,11 @@ - {968DA402-7888-42A5-B2C3-676B80134771} + {E4F6F3AB-051E-4B70-B942-2E5E17C12F6A} $(MSBuildProjectName) Debug Win32 - {E2000ABD-F961-4FA7-A959-005FC6E3451F} + {6C3A654A-1721-4D65-9EEC-E6C8CBEA4716} @@ -165,7 +165,6 @@ - diff --git a/general/tracing/SystemTraceControl/SystemTraceControl.vcxproj.Filters b/general/tracing/SystemTraceControl/SystemTraceControl.vcxproj.Filters index 1d664bf19..f6f26513d 100644 --- a/general/tracing/SystemTraceControl/SystemTraceControl.vcxproj.Filters +++ b/general/tracing/SystemTraceControl/SystemTraceControl.vcxproj.Filters @@ -3,15 +3,15 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {4EF85DAA-B6CF-4318-A2C6-C468645C29C1} + {3B6D39BB-727A-4200-BB47-9DBB3C2B1976} h;hpp;hxx;hm;inl;inc;xsd - {23309F83-06E8-4B91-ACAA-F7132B6276A2} + {684A7E16-CFF9-4768-84ED-6E58F714AD74} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {EA06C1A4-65B4-4551-9760-BEDD786ACB50} + {706A7968-4A64-4FC5-B349-598AF05146FC} diff --git a/general/tracing/evntdrv/Eventdrv/Eventdrv.vcxproj b/general/tracing/evntdrv/Eventdrv/Eventdrv.vcxproj index 122b99386..91136ea2e 100644 --- a/general/tracing/evntdrv/Eventdrv/Eventdrv.vcxproj +++ b/general/tracing/evntdrv/Eventdrv/Eventdrv.vcxproj @@ -19,11 +19,11 @@ - {0AA897A5-DD5B-4C2F-826C-46C4C8261641} + {4E197F82-2DC1-43FF-A99E-98B048E9C3D8} $(MSBuildProjectName) Debug Win32 - {77776F12-8749-4CD8-BCB8-45AFB9EF64A4} + {289B3077-48C8-425F-9F0F-ACC1D952D174} @@ -181,7 +181,6 @@ - diff --git a/general/tracing/evntdrv/Eventdrv/Eventdrv.vcxproj.Filters b/general/tracing/evntdrv/Eventdrv/Eventdrv.vcxproj.Filters index 5b94af026..992d3374d 100644 --- a/general/tracing/evntdrv/Eventdrv/Eventdrv.vcxproj.Filters +++ b/general/tracing/evntdrv/Eventdrv/Eventdrv.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {653669AD-2AB5-4E15-A1E3-F56E6B5D988D} + {16BE6722-D66E-42AD-8D3A-537B024A5240} h;hpp;hxx;hm;inl;inc;xsd - {EFAB409C-D315-4FC3-91AB-3AD0C028C1E7} + {B85CD8EB-3F58-42C9-85A3-D261714CF85C} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {65A4753A-3174-490A-8A34-C3F655C95643} + {5F8F8ABD-E5D2-467E-A709-3105C86DB79F} inf;inv;inx;mof;mc; - {B7FEA61C-954A-4DA6-B0DB-65F77FD5396E} + {983C0BB6-79E6-4C60-B678-49B68E167DBD} diff --git a/general/tracing/evntdrv/eventdrv.sln b/general/tracing/evntdrv/eventdrv.sln index c6e89bde4..653dc1b17 100644 --- a/general/tracing/evntdrv/eventdrv.sln +++ b/general/tracing/evntdrv/eventdrv.sln @@ -3,13 +3,13 @@ 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", "{17F2C037-F5B1-4C73-A09B-EA70DCF80BC5}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Eventdrv", "Eventdrv", "{7424CDFF-B958-4C5C-BE6B-B17FCB5F25FC}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Evntctrl", "Evntctrl", "{1BDAAB5F-D67B-419D-857C-2A921360552B}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Evntctrl", "Evntctrl", "{783F6114-71F8-41E4-BCC2-185C18B3ADAB}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Eventdrv", "Eventdrv\Eventdrv.vcxproj", "{0AA897A5-DD5B-4C2F-826C-46C4C8261641}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Eventdrv", "Eventdrv\Eventdrv.vcxproj", "{4E197F82-2DC1-43FF-A99E-98B048E9C3D8}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "evntctrl", "evntctrl\evntctrl.vcxproj", "{6D8C5584-FF3C-4C12-BE99-64A3A63CFA2D}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "evntctrl", "evntctrl\evntctrl.vcxproj", "{F8C2B5F9-4838-4427-9C15-37EA99DD4DB9}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -19,28 +19,28 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {0AA897A5-DD5B-4C2F-826C-46C4C8261641}.Debug|Win32.ActiveCfg = Debug|Win32 - {0AA897A5-DD5B-4C2F-826C-46C4C8261641}.Debug|Win32.Build.0 = Debug|Win32 - {0AA897A5-DD5B-4C2F-826C-46C4C8261641}.Release|Win32.ActiveCfg = Release|Win32 - {0AA897A5-DD5B-4C2F-826C-46C4C8261641}.Release|Win32.Build.0 = Release|Win32 - {0AA897A5-DD5B-4C2F-826C-46C4C8261641}.Debug|x64.ActiveCfg = Debug|x64 - {0AA897A5-DD5B-4C2F-826C-46C4C8261641}.Debug|x64.Build.0 = Debug|x64 - {0AA897A5-DD5B-4C2F-826C-46C4C8261641}.Release|x64.ActiveCfg = Release|x64 - {0AA897A5-DD5B-4C2F-826C-46C4C8261641}.Release|x64.Build.0 = Release|x64 - {6D8C5584-FF3C-4C12-BE99-64A3A63CFA2D}.Debug|Win32.ActiveCfg = Debug|Win32 - {6D8C5584-FF3C-4C12-BE99-64A3A63CFA2D}.Debug|Win32.Build.0 = Debug|Win32 - {6D8C5584-FF3C-4C12-BE99-64A3A63CFA2D}.Release|Win32.ActiveCfg = Release|Win32 - {6D8C5584-FF3C-4C12-BE99-64A3A63CFA2D}.Release|Win32.Build.0 = Release|Win32 - {6D8C5584-FF3C-4C12-BE99-64A3A63CFA2D}.Debug|x64.ActiveCfg = Debug|x64 - {6D8C5584-FF3C-4C12-BE99-64A3A63CFA2D}.Debug|x64.Build.0 = Debug|x64 - {6D8C5584-FF3C-4C12-BE99-64A3A63CFA2D}.Release|x64.ActiveCfg = Release|x64 - {6D8C5584-FF3C-4C12-BE99-64A3A63CFA2D}.Release|x64.Build.0 = Release|x64 + {4E197F82-2DC1-43FF-A99E-98B048E9C3D8}.Debug|Win32.ActiveCfg = Debug|Win32 + {4E197F82-2DC1-43FF-A99E-98B048E9C3D8}.Debug|Win32.Build.0 = Debug|Win32 + {4E197F82-2DC1-43FF-A99E-98B048E9C3D8}.Release|Win32.ActiveCfg = Release|Win32 + {4E197F82-2DC1-43FF-A99E-98B048E9C3D8}.Release|Win32.Build.0 = Release|Win32 + {4E197F82-2DC1-43FF-A99E-98B048E9C3D8}.Debug|x64.ActiveCfg = Debug|x64 + {4E197F82-2DC1-43FF-A99E-98B048E9C3D8}.Debug|x64.Build.0 = Debug|x64 + {4E197F82-2DC1-43FF-A99E-98B048E9C3D8}.Release|x64.ActiveCfg = Release|x64 + {4E197F82-2DC1-43FF-A99E-98B048E9C3D8}.Release|x64.Build.0 = Release|x64 + {F8C2B5F9-4838-4427-9C15-37EA99DD4DB9}.Debug|Win32.ActiveCfg = Debug|Win32 + {F8C2B5F9-4838-4427-9C15-37EA99DD4DB9}.Debug|Win32.Build.0 = Debug|Win32 + {F8C2B5F9-4838-4427-9C15-37EA99DD4DB9}.Release|Win32.ActiveCfg = Release|Win32 + {F8C2B5F9-4838-4427-9C15-37EA99DD4DB9}.Release|Win32.Build.0 = Release|Win32 + {F8C2B5F9-4838-4427-9C15-37EA99DD4DB9}.Debug|x64.ActiveCfg = Debug|x64 + {F8C2B5F9-4838-4427-9C15-37EA99DD4DB9}.Debug|x64.Build.0 = Debug|x64 + {F8C2B5F9-4838-4427-9C15-37EA99DD4DB9}.Release|x64.ActiveCfg = Release|x64 + {F8C2B5F9-4838-4427-9C15-37EA99DD4DB9}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {0AA897A5-DD5B-4C2F-826C-46C4C8261641} = {17F2C037-F5B1-4C73-A09B-EA70DCF80BC5} - {6D8C5584-FF3C-4C12-BE99-64A3A63CFA2D} = {1BDAAB5F-D67B-419D-857C-2A921360552B} + {4E197F82-2DC1-43FF-A99E-98B048E9C3D8} = {7424CDFF-B958-4C5C-BE6B-B17FCB5F25FC} + {F8C2B5F9-4838-4427-9C15-37EA99DD4DB9} = {783F6114-71F8-41E4-BCC2-185C18B3ADAB} EndGlobalSection EndGlobal diff --git a/general/tracing/evntdrv/evntctrl/evntctrl.vcxproj b/general/tracing/evntdrv/evntctrl/evntctrl.vcxproj index 7d1f8cc62..3a76a9095 100644 --- a/general/tracing/evntdrv/evntctrl/evntctrl.vcxproj +++ b/general/tracing/evntdrv/evntctrl/evntctrl.vcxproj @@ -19,11 +19,11 @@ - {6D8C5584-FF3C-4C12-BE99-64A3A63CFA2D} + {F8C2B5F9-4838-4427-9C15-37EA99DD4DB9} $(MSBuildProjectName) Debug Win32 - {9B65A1F6-8697-49A7-B65C-CCFC5E769D1C} + {7DB91795-3010-4412-9B49-0E2FA0D06E45} @@ -138,7 +138,6 @@ - diff --git a/general/tracing/evntdrv/evntctrl/evntctrl.vcxproj.Filters b/general/tracing/evntdrv/evntctrl/evntctrl.vcxproj.Filters index 055f3f38e..ece40e381 100644 --- a/general/tracing/evntdrv/evntctrl/evntctrl.vcxproj.Filters +++ b/general/tracing/evntdrv/evntctrl/evntctrl.vcxproj.Filters @@ -3,15 +3,15 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {732FEB8C-759C-4315-8FE6-91C32E227CB8} + {94268968-6C96-413B-84E4-EF86A33B459E} h;hpp;hxx;hm;inl;inc;xsd - {1EB4D098-5821-4C5A-9534-1C28B7B0D2A4} + {44C9DD55-05C1-41E5-B952-5B39D5AE06A5} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {0D82C9BE-D601-4B17-BE82-6F426FC0EACF} + {D535F9F2-0117-4AA3-A0AB-7168A5D53D41} diff --git a/general/tracing/tracedriver/tracectl/tracectl.vcxproj b/general/tracing/tracedriver/tracectl/tracectl.vcxproj index 36d8f30ee..549100604 100644 --- a/general/tracing/tracedriver/tracectl/tracectl.vcxproj +++ b/general/tracing/tracedriver/tracectl/tracectl.vcxproj @@ -19,11 +19,11 @@ - {3A8F1F69-AD42-492B-9F57-5F95EFCD75C6} + {20F77495-27B1-41EE-96F1-49163E52FA3E} $(MSBuildProjectName) Debug Win32 - {3DEA2D5B-55CB-46FF-BA56-6FC217FF19AD} + {FEF5126F-48E0-43E4-A4BA-4F878B3E27A3} @@ -138,7 +138,6 @@ - diff --git a/general/tracing/tracedriver/tracectl/tracectl.vcxproj.Filters b/general/tracing/tracedriver/tracectl/tracectl.vcxproj.Filters index 1482e22ec..7d9d3ff56 100644 --- a/general/tracing/tracedriver/tracectl/tracectl.vcxproj.Filters +++ b/general/tracing/tracedriver/tracectl/tracectl.vcxproj.Filters @@ -3,15 +3,15 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {F36F2396-6719-4C9E-AF20-5AD2738A351E} + {D6531772-EF8C-451A-81CA-C5152CAD1C11} h;hpp;hxx;hm;inl;inc;xsd - {27EB4DB8-FEAE-4B53-A49E-FB8F17DD47A0} + {34A13EE0-94EC-46AB-AEAD-AF35CE120EFD} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {39E92D32-6DE6-4B95-A5B0-D7DCB33A2256} + {2D93CD19-45A2-44F3-A274-0DC36195669B} diff --git a/general/tracing/tracedriver/tracedrv.sln b/general/tracing/tracedriver/tracedrv.sln index 6ad93c592..8a32d2b15 100644 --- a/general/tracing/tracedriver/tracedrv.sln +++ b/general/tracing/tracedriver/tracedrv.sln @@ -3,13 +3,13 @@ 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", "{6924D03F-364D-445C-975D-882FB14B0058}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tracectl", "Tracectl", "{1D6048E3-023F-42F8-A4D3-30727C5CCB40}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tracedrv", "Tracedrv", "{C8CBD4D8-1015-4418-87FD-AC97AFF374CA}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tracedrv", "Tracedrv", "{EA04F055-585E-4A70-BA18-CB21DF0317A5}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tracectl", "tracectl\tracectl.vcxproj", "{3A8F1F69-AD42-492B-9F57-5F95EFCD75C6}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tracectl", "tracectl\tracectl.vcxproj", "{20F77495-27B1-41EE-96F1-49163E52FA3E}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tracedrv", "tracedrv\tracedrv.vcxproj", "{19455661-BB6E-4AB3-A48C-33ABBB7BAF8B}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tracedrv", "tracedrv\tracedrv.vcxproj", "{984FEF4C-00E5-40B8-96A8-04CDD124BC3A}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -19,28 +19,28 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {3A8F1F69-AD42-492B-9F57-5F95EFCD75C6}.Debug|Win32.ActiveCfg = Debug|Win32 - {3A8F1F69-AD42-492B-9F57-5F95EFCD75C6}.Debug|Win32.Build.0 = Debug|Win32 - {3A8F1F69-AD42-492B-9F57-5F95EFCD75C6}.Release|Win32.ActiveCfg = Release|Win32 - {3A8F1F69-AD42-492B-9F57-5F95EFCD75C6}.Release|Win32.Build.0 = Release|Win32 - {3A8F1F69-AD42-492B-9F57-5F95EFCD75C6}.Debug|x64.ActiveCfg = Debug|x64 - {3A8F1F69-AD42-492B-9F57-5F95EFCD75C6}.Debug|x64.Build.0 = Debug|x64 - {3A8F1F69-AD42-492B-9F57-5F95EFCD75C6}.Release|x64.ActiveCfg = Release|x64 - {3A8F1F69-AD42-492B-9F57-5F95EFCD75C6}.Release|x64.Build.0 = Release|x64 - {19455661-BB6E-4AB3-A48C-33ABBB7BAF8B}.Debug|Win32.ActiveCfg = Debug|Win32 - {19455661-BB6E-4AB3-A48C-33ABBB7BAF8B}.Debug|Win32.Build.0 = Debug|Win32 - {19455661-BB6E-4AB3-A48C-33ABBB7BAF8B}.Release|Win32.ActiveCfg = Release|Win32 - {19455661-BB6E-4AB3-A48C-33ABBB7BAF8B}.Release|Win32.Build.0 = Release|Win32 - {19455661-BB6E-4AB3-A48C-33ABBB7BAF8B}.Debug|x64.ActiveCfg = Debug|x64 - {19455661-BB6E-4AB3-A48C-33ABBB7BAF8B}.Debug|x64.Build.0 = Debug|x64 - {19455661-BB6E-4AB3-A48C-33ABBB7BAF8B}.Release|x64.ActiveCfg = Release|x64 - {19455661-BB6E-4AB3-A48C-33ABBB7BAF8B}.Release|x64.Build.0 = Release|x64 + {20F77495-27B1-41EE-96F1-49163E52FA3E}.Debug|Win32.ActiveCfg = Debug|Win32 + {20F77495-27B1-41EE-96F1-49163E52FA3E}.Debug|Win32.Build.0 = Debug|Win32 + {20F77495-27B1-41EE-96F1-49163E52FA3E}.Release|Win32.ActiveCfg = Release|Win32 + {20F77495-27B1-41EE-96F1-49163E52FA3E}.Release|Win32.Build.0 = Release|Win32 + {20F77495-27B1-41EE-96F1-49163E52FA3E}.Debug|x64.ActiveCfg = Debug|x64 + {20F77495-27B1-41EE-96F1-49163E52FA3E}.Debug|x64.Build.0 = Debug|x64 + {20F77495-27B1-41EE-96F1-49163E52FA3E}.Release|x64.ActiveCfg = Release|x64 + {20F77495-27B1-41EE-96F1-49163E52FA3E}.Release|x64.Build.0 = Release|x64 + {984FEF4C-00E5-40B8-96A8-04CDD124BC3A}.Debug|Win32.ActiveCfg = Debug|Win32 + {984FEF4C-00E5-40B8-96A8-04CDD124BC3A}.Debug|Win32.Build.0 = Debug|Win32 + {984FEF4C-00E5-40B8-96A8-04CDD124BC3A}.Release|Win32.ActiveCfg = Release|Win32 + {984FEF4C-00E5-40B8-96A8-04CDD124BC3A}.Release|Win32.Build.0 = Release|Win32 + {984FEF4C-00E5-40B8-96A8-04CDD124BC3A}.Debug|x64.ActiveCfg = Debug|x64 + {984FEF4C-00E5-40B8-96A8-04CDD124BC3A}.Debug|x64.Build.0 = Debug|x64 + {984FEF4C-00E5-40B8-96A8-04CDD124BC3A}.Release|x64.ActiveCfg = Release|x64 + {984FEF4C-00E5-40B8-96A8-04CDD124BC3A}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {3A8F1F69-AD42-492B-9F57-5F95EFCD75C6} = {6924D03F-364D-445C-975D-882FB14B0058} - {19455661-BB6E-4AB3-A48C-33ABBB7BAF8B} = {C8CBD4D8-1015-4418-87FD-AC97AFF374CA} + {20F77495-27B1-41EE-96F1-49163E52FA3E} = {1D6048E3-023F-42F8-A4D3-30727C5CCB40} + {984FEF4C-00E5-40B8-96A8-04CDD124BC3A} = {EA04F055-585E-4A70-BA18-CB21DF0317A5} EndGlobalSection EndGlobal diff --git a/general/tracing/tracedriver/tracedrv/tracedrv.vcxproj b/general/tracing/tracedriver/tracedrv/tracedrv.vcxproj index 31e6316fc..74b6cbfcd 100644 --- a/general/tracing/tracedriver/tracedrv/tracedrv.vcxproj +++ b/general/tracing/tracedriver/tracedrv/tracedrv.vcxproj @@ -19,11 +19,11 @@ - {19455661-BB6E-4AB3-A48C-33ABBB7BAF8B} + {984FEF4C-00E5-40B8-96A8-04CDD124BC3A} $(MSBuildProjectName) Debug Win32 - {46E71AFA-97EB-4402-8982-491F04EE159C} + {F4226244-8FCF-48DD-9DF7-0A94935660D8} @@ -150,7 +150,6 @@ - diff --git a/general/tracing/tracedriver/tracedrv/tracedrv.vcxproj.Filters b/general/tracing/tracedriver/tracedrv/tracedrv.vcxproj.Filters index 41133cade..472ebc32a 100644 --- a/general/tracing/tracedriver/tracedrv/tracedrv.vcxproj.Filters +++ b/general/tracing/tracedriver/tracedrv/tracedrv.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {8E3F54BF-DC75-4CA8-BC24-30E385BF6DE2} + {913AB4A1-B44D-412D-AB23-77CB7A8A3EB3} h;hpp;hxx;hm;inl;inc;xsd - {3627B81B-D94D-4485-A492-73C31C368899} + {662E59C8-472B-4305-BC87-986241758908} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {51DDE1C7-D8F3-45E4-A8C3-B9FBEF4E0364} + {A138E98B-E594-4896-BEB1-5EC7D6AB48FE} inf;inv;inx;mof;mc; - {AA4D7E47-8D1B-407A-BA84-A2D3F956CEE3} + {C5A203CB-517B-45B2-BF2B-E96F16FBAEA3} diff --git a/general/umdfSkeleton/UMDFSkeleton.vcxproj b/general/umdfSkeleton/UMDFSkeleton.vcxproj index f0462cf38..95f66045f 100644 --- a/general/umdfSkeleton/UMDFSkeleton.vcxproj +++ b/general/umdfSkeleton/UMDFSkeleton.vcxproj @@ -19,7 +19,7 @@ - {70388898-AB89-4971-A750-E49A07A4180B} + {3434D648-433A-4D74-822F-DDBE22400E6D} $(MSBuildProjectName) 1 1 @@ -27,7 +27,7 @@ 9 Debug Win32 - {AF73135B-9C65-43D8-8AA0-B878E28778DD} + {86CA24C3-2AB5-4664-9B33-3D9F1F5CDA36} @@ -250,7 +250,6 @@ - diff --git a/general/umdfSkeleton/UMDFSkeleton.vcxproj.Filters b/general/umdfSkeleton/UMDFSkeleton.vcxproj.Filters index 90efaa531..45fa3257e 100644 --- a/general/umdfSkeleton/UMDFSkeleton.vcxproj.Filters +++ b/general/umdfSkeleton/UMDFSkeleton.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {4071D9FA-8523-40C2-AD37-B96CB8438837} + {74888656-E3AB-4255-BBFE-4122D66F5FEB} h;hpp;hxx;hm;inl;inc;xsd - {FA619144-425B-4A1D-BE9D-C67C3DF67348} + {BB9F8E9F-F645-4AC6-92EC-13CBBF59E4DF} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {A3F17163-2904-4021-9EF6-73D05E31169D} + {D7FA8D20-66EC-4F15-B0EA-0E7435261BE2} inf;inv;inx;mof;mc; - {0EE7AE28-07A8-4B93-8708-F261564A77B5} + {752766FE-0F66-4394-B24F-D1A229EC5B85} @@ -36,12 +36,6 @@ - - Driver Files - - - Driver Files - Driver Files diff --git a/general/umdfSkeleton/UMDFSkeleton_OSR.inx b/general/umdfSkeleton/UMDFSkeleton_OSR.inx index af5b6e2d4..70a7b47c8 100644 --- a/general/umdfSkeleton/UMDFSkeleton_OSR.inx +++ b/general/umdfSkeleton/UMDFSkeleton_OSR.inx @@ -5,12 +5,12 @@ Signature="$Windows NT$" Class=Sample ClassGuid={78A1C341-4539-11d3-B88D-00C04FAD5171} -Provider=%MSFTUMDF% +Provider=%ProviderString% DriverVer=03/25/2005,0.0.0.1 CatalogFile=wudf.cat [Manufacturer] -%MSFTUMDF%=Microsoft,NT$ARCH$ +%ManufacturerString%=Microsoft,NT$ARCH$ [Microsoft.NT$ARCH$] %SkeletonDeviceName%=Skeleton_Install, USB\Vid_045e&Pid_94aa&mi_00 @@ -89,7 +89,8 @@ UMDFSkeleton.dll,,,0x00004000 ; COPYFLG_IN_USE_RENAME ; =================== Generic ================================== [Strings] -MSFTUMDF="Microsoft Internal (WDF:UMDF)" +ProviderString="TODO-Set-Provider" +ManufacturerString="TODO-Set-Manufacturer" MediaDescription="Microsoft Sample Driver Installation Media" ClassName="Sample Device" WudfRdDisplayName="Windows Driver Foundation - User-mode Driver Framework Reflector" diff --git a/general/umdfSkeleton/UMDFSkeleton_Root.inx b/general/umdfSkeleton/UMDFSkeleton_Root.inx index 22b5459a4..3a31df129 100644 --- a/general/umdfSkeleton/UMDFSkeleton_Root.inx +++ b/general/umdfSkeleton/UMDFSkeleton_Root.inx @@ -6,12 +6,12 @@ Signature="$Windows NT$" Class=Sample ClassGuid={78A1C341-4539-11d3-B88D-00C04FAD5171} -Provider=%MSFTUMDF% +Provider=%ProviderString% CatalogFile=WUDF.cat DriverVer=03/25/2005,0.0.0.1 [Manufacturer] -%MSFTUMDF%=Microsoft,NT$ARCH$ +%ManufacturerString%=Microsoft,NT$ARCH$ [Microsoft.NT$ARCH$] %SkeletonDeviceName%=Skeleton_Install,UMDFSamples\Skeleton @@ -70,7 +70,8 @@ UMDFSkeleton.dll,,,0x00004000 ; COPYFLG_IN_USE_RENAME ; =================== Generic ================================== [Strings] -MSFTUMDF="Microsoft Internal (WDF:UMDF)" +ProviderString="TODO-Set-Provider" +ManufacturerString="TODO-Set-Manufacturer" MediaDescription="Microsoft Sample Driver Installation Media" ClassName="Sample Device" WudfRdDisplayName="Windows Driver Foundation - User-mode Driver Framework Reflector" diff --git a/general/umdfSkeleton/umdfSkeleton.sln b/general/umdfSkeleton/umdfSkeleton.sln index cba8f90bb..dd9e04c70 100644 --- a/general/umdfSkeleton/umdfSkeleton.sln +++ b/general/umdfSkeleton/umdfSkeleton.sln @@ -3,7 +3,7 @@ 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", "{70388898-AB89-4971-A750-E49A07A4180B}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UMDFSkeleton", "UMDFSkeleton.vcxproj", "{3434D648-433A-4D74-822F-DDBE22400E6D}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {70388898-AB89-4971-A750-E49A07A4180B}.Debug|Win32.ActiveCfg = Debug|Win32 - {70388898-AB89-4971-A750-E49A07A4180B}.Debug|Win32.Build.0 = Debug|Win32 - {70388898-AB89-4971-A750-E49A07A4180B}.Release|Win32.ActiveCfg = Release|Win32 - {70388898-AB89-4971-A750-E49A07A4180B}.Release|Win32.Build.0 = Release|Win32 - {70388898-AB89-4971-A750-E49A07A4180B}.Debug|x64.ActiveCfg = Debug|x64 - {70388898-AB89-4971-A750-E49A07A4180B}.Debug|x64.Build.0 = Debug|x64 - {70388898-AB89-4971-A750-E49A07A4180B}.Release|x64.ActiveCfg = Release|x64 - {70388898-AB89-4971-A750-E49A07A4180B}.Release|x64.Build.0 = Release|x64 + {3434D648-433A-4D74-822F-DDBE22400E6D}.Debug|Win32.ActiveCfg = Debug|Win32 + {3434D648-433A-4D74-822F-DDBE22400E6D}.Debug|Win32.Build.0 = Debug|Win32 + {3434D648-433A-4D74-822F-DDBE22400E6D}.Release|Win32.ActiveCfg = Release|Win32 + {3434D648-433A-4D74-822F-DDBE22400E6D}.Release|Win32.Build.0 = Release|Win32 + {3434D648-433A-4D74-822F-DDBE22400E6D}.Debug|x64.ActiveCfg = Debug|x64 + {3434D648-433A-4D74-822F-DDBE22400E6D}.Debug|x64.Build.0 = Debug|x64 + {3434D648-433A-4D74-822F-DDBE22400E6D}.Release|x64.ActiveCfg = Release|x64 + {3434D648-433A-4D74-822F-DDBE22400E6D}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/gpio/samples/sim.sln b/gpio/samples/sim.sln index bfd63b866..6ebae3ad3 100644 --- a/gpio/samples/sim.sln +++ b/gpio/samples/sim.sln @@ -3,21 +3,17 @@ 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", "{08989298-000B-48DE-B970-0BE88F9A5537}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Simdevice", "Simdevice", "{D0AF0B11-69A3-4059-87E4-4948CF1D7F26}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Simdeviceumdf", "Simdeviceumdf", "{99AB1B58-560E-47F1-AC02-F709DE1F03FF}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Simgpio", "Simgpio", "{BEDE4521-7FFA-4BF4-B60A-3291CCFA92C2}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Simgpio", "Simgpio", "{B1F4E416-8293-4557-A064-A0EBAB34B7B3}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Simgpio_i2c", "Simgpio_i2c", "{644D6533-C6A3-491F-801A-CC0E336442AB}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Simgpio_i2c", "Simgpio_i2c", "{EC78C5C6-E325-44DC-9548-095850571F96}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "simdevice", "simdevice\simdevice.vcxproj", "{C68A56B6-2E14-451F-99F5-5FA2D4DC5B64}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "simdevice", "simdevice\simdevice.vcxproj", "{1683AFEC-30BD-4F42-B05C-070C4502CCC3}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "simgpio", "simgpio\simgpio.vcxproj", "{7E9DAF8F-8B01-40F0-9014-0F68D3AFF8D8}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SimdeviceUMDF", "simdeviceumdf\SimdeviceUMDF.vcxproj", "{D16242B2-F3C4-48C0-98C2-D35778C4B71F}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "simgpio", "simgpio\simgpio.vcxproj", "{AEF88128-15A0-4F64-ABAD-504B75C24C6B}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "simgpio_i2c", "simgpio_i2c\simgpio_i2c.vcxproj", "{8D155F90-F6A1-4D96-A07F-31844C0587C5}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "simgpio_i2c", "simgpio_i2c\simgpio_i2c.vcxproj", "{C966B8DA-2679-49D0-B3CB-5DB181407EFA}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -27,46 +23,37 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {1683AFEC-30BD-4F42-B05C-070C4502CCC3}.Debug|Win32.ActiveCfg = Debug|Win32 - {1683AFEC-30BD-4F42-B05C-070C4502CCC3}.Debug|Win32.Build.0 = Debug|Win32 - {1683AFEC-30BD-4F42-B05C-070C4502CCC3}.Release|Win32.ActiveCfg = Release|Win32 - {1683AFEC-30BD-4F42-B05C-070C4502CCC3}.Release|Win32.Build.0 = Release|Win32 - {1683AFEC-30BD-4F42-B05C-070C4502CCC3}.Debug|x64.ActiveCfg = Debug|x64 - {1683AFEC-30BD-4F42-B05C-070C4502CCC3}.Debug|x64.Build.0 = Debug|x64 - {1683AFEC-30BD-4F42-B05C-070C4502CCC3}.Release|x64.ActiveCfg = Release|x64 - {1683AFEC-30BD-4F42-B05C-070C4502CCC3}.Release|x64.Build.0 = Release|x64 - {D16242B2-F3C4-48C0-98C2-D35778C4B71F}.Debug|Win32.ActiveCfg = Debug|Win32 - {D16242B2-F3C4-48C0-98C2-D35778C4B71F}.Debug|Win32.Build.0 = Debug|Win32 - {D16242B2-F3C4-48C0-98C2-D35778C4B71F}.Release|Win32.ActiveCfg = Release|Win32 - {D16242B2-F3C4-48C0-98C2-D35778C4B71F}.Release|Win32.Build.0 = Release|Win32 - {D16242B2-F3C4-48C0-98C2-D35778C4B71F}.Debug|x64.ActiveCfg = Debug|x64 - {D16242B2-F3C4-48C0-98C2-D35778C4B71F}.Debug|x64.Build.0 = Debug|x64 - {D16242B2-F3C4-48C0-98C2-D35778C4B71F}.Release|x64.ActiveCfg = Release|x64 - {D16242B2-F3C4-48C0-98C2-D35778C4B71F}.Release|x64.Build.0 = Release|x64 - {AEF88128-15A0-4F64-ABAD-504B75C24C6B}.Debug|Win32.ActiveCfg = Debug|Win32 - {AEF88128-15A0-4F64-ABAD-504B75C24C6B}.Debug|Win32.Build.0 = Debug|Win32 - {AEF88128-15A0-4F64-ABAD-504B75C24C6B}.Release|Win32.ActiveCfg = Release|Win32 - {AEF88128-15A0-4F64-ABAD-504B75C24C6B}.Release|Win32.Build.0 = Release|Win32 - {AEF88128-15A0-4F64-ABAD-504B75C24C6B}.Debug|x64.ActiveCfg = Debug|x64 - {AEF88128-15A0-4F64-ABAD-504B75C24C6B}.Debug|x64.Build.0 = Debug|x64 - {AEF88128-15A0-4F64-ABAD-504B75C24C6B}.Release|x64.ActiveCfg = Release|x64 - {AEF88128-15A0-4F64-ABAD-504B75C24C6B}.Release|x64.Build.0 = Release|x64 - {8D155F90-F6A1-4D96-A07F-31844C0587C5}.Debug|Win32.ActiveCfg = Debug|Win32 - {8D155F90-F6A1-4D96-A07F-31844C0587C5}.Debug|Win32.Build.0 = Debug|Win32 - {8D155F90-F6A1-4D96-A07F-31844C0587C5}.Release|Win32.ActiveCfg = Release|Win32 - {8D155F90-F6A1-4D96-A07F-31844C0587C5}.Release|Win32.Build.0 = Release|Win32 - {8D155F90-F6A1-4D96-A07F-31844C0587C5}.Debug|x64.ActiveCfg = Debug|x64 - {8D155F90-F6A1-4D96-A07F-31844C0587C5}.Debug|x64.Build.0 = Debug|x64 - {8D155F90-F6A1-4D96-A07F-31844C0587C5}.Release|x64.ActiveCfg = Release|x64 - {8D155F90-F6A1-4D96-A07F-31844C0587C5}.Release|x64.Build.0 = Release|x64 + {C68A56B6-2E14-451F-99F5-5FA2D4DC5B64}.Debug|Win32.ActiveCfg = Debug|Win32 + {C68A56B6-2E14-451F-99F5-5FA2D4DC5B64}.Debug|Win32.Build.0 = Debug|Win32 + {C68A56B6-2E14-451F-99F5-5FA2D4DC5B64}.Release|Win32.ActiveCfg = Release|Win32 + {C68A56B6-2E14-451F-99F5-5FA2D4DC5B64}.Release|Win32.Build.0 = Release|Win32 + {C68A56B6-2E14-451F-99F5-5FA2D4DC5B64}.Debug|x64.ActiveCfg = Debug|x64 + {C68A56B6-2E14-451F-99F5-5FA2D4DC5B64}.Debug|x64.Build.0 = Debug|x64 + {C68A56B6-2E14-451F-99F5-5FA2D4DC5B64}.Release|x64.ActiveCfg = Release|x64 + {C68A56B6-2E14-451F-99F5-5FA2D4DC5B64}.Release|x64.Build.0 = Release|x64 + {7E9DAF8F-8B01-40F0-9014-0F68D3AFF8D8}.Debug|Win32.ActiveCfg = Debug|Win32 + {7E9DAF8F-8B01-40F0-9014-0F68D3AFF8D8}.Debug|Win32.Build.0 = Debug|Win32 + {7E9DAF8F-8B01-40F0-9014-0F68D3AFF8D8}.Release|Win32.ActiveCfg = Release|Win32 + {7E9DAF8F-8B01-40F0-9014-0F68D3AFF8D8}.Release|Win32.Build.0 = Release|Win32 + {7E9DAF8F-8B01-40F0-9014-0F68D3AFF8D8}.Debug|x64.ActiveCfg = Debug|x64 + {7E9DAF8F-8B01-40F0-9014-0F68D3AFF8D8}.Debug|x64.Build.0 = Debug|x64 + {7E9DAF8F-8B01-40F0-9014-0F68D3AFF8D8}.Release|x64.ActiveCfg = Release|x64 + {7E9DAF8F-8B01-40F0-9014-0F68D3AFF8D8}.Release|x64.Build.0 = Release|x64 + {C966B8DA-2679-49D0-B3CB-5DB181407EFA}.Debug|Win32.ActiveCfg = Debug|Win32 + {C966B8DA-2679-49D0-B3CB-5DB181407EFA}.Debug|Win32.Build.0 = Debug|Win32 + {C966B8DA-2679-49D0-B3CB-5DB181407EFA}.Release|Win32.ActiveCfg = Release|Win32 + {C966B8DA-2679-49D0-B3CB-5DB181407EFA}.Release|Win32.Build.0 = Release|Win32 + {C966B8DA-2679-49D0-B3CB-5DB181407EFA}.Debug|x64.ActiveCfg = Debug|x64 + {C966B8DA-2679-49D0-B3CB-5DB181407EFA}.Debug|x64.Build.0 = Debug|x64 + {C966B8DA-2679-49D0-B3CB-5DB181407EFA}.Release|x64.ActiveCfg = Release|x64 + {C966B8DA-2679-49D0-B3CB-5DB181407EFA}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {1683AFEC-30BD-4F42-B05C-070C4502CCC3} = {08989298-000B-48DE-B970-0BE88F9A5537} - {D16242B2-F3C4-48C0-98C2-D35778C4B71F} = {99AB1B58-560E-47F1-AC02-F709DE1F03FF} - {AEF88128-15A0-4F64-ABAD-504B75C24C6B} = {B1F4E416-8293-4557-A064-A0EBAB34B7B3} - {8D155F90-F6A1-4D96-A07F-31844C0587C5} = {EC78C5C6-E325-44DC-9548-095850571F96} + {C68A56B6-2E14-451F-99F5-5FA2D4DC5B64} = {D0AF0B11-69A3-4059-87E4-4948CF1D7F26} + {7E9DAF8F-8B01-40F0-9014-0F68D3AFF8D8} = {BEDE4521-7FFA-4BF4-B60A-3291CCFA92C2} + {C966B8DA-2679-49D0-B3CB-5DB181407EFA} = {644D6533-C6A3-491F-801A-CC0E336442AB} EndGlobalSection EndGlobal diff --git a/gpio/samples/simdevice/simdevice.inx b/gpio/samples/simdevice/simdevice.inx index 34e65a689..330ed46cf 100644 --- a/gpio/samples/simdevice/simdevice.inx +++ b/gpio/samples/simdevice/simdevice.inx @@ -15,7 +15,7 @@ Signature="$WINDOWS NT$" Class=System ClassGuid={4d36e97d-e325-11ce-bfc1-08002be10318} -Provider=%MSFT% +Provider=%ProviderName% DriverVer=05/07/2010 CatalogFile=gpiosamples.cat @@ -37,9 +37,9 @@ ExcludeFromSelect=* ;***************************************** [Manufacturer] -%MSFT%=Microsoft,NT$ARCH$ +%ManufacturerName%=Standard,NT$ARCH$ -[Microsoft.NT$ARCH$] +[Standard.NT$ARCH$] %DeviceDesc%=DriverInstall,ACPI\TEST0003 [DriverInstall.NT] @@ -60,8 +60,8 @@ ServiceBinary = %12%\Simdevice.sys [Strings] ;Localizable Strings -MSFT = "Microsoft" -Std = "(Standard system devices) Test device" +ProviderName = "TODO-Set-Provider" +ManufacturerName = "TODO-Set-Manufacturer" SvcDesc = "Test device service" DeviceDesc = "Test device description" diff --git a/gpio/samples/simdevice/simdevice.vcxproj b/gpio/samples/simdevice/simdevice.vcxproj index 3df57b242..6f18d8a5f 100644 --- a/gpio/samples/simdevice/simdevice.vcxproj +++ b/gpio/samples/simdevice/simdevice.vcxproj @@ -19,12 +19,12 @@ - {1683AFEC-30BD-4F42-B05C-070C4502CCC3} + {C68A56B6-2E14-451F-99F5-5FA2D4DC5B64} $(MSBuildProjectName) 1 Debug Win32 - {59D8C9BB-479F-4995-BDB1-3493C6F6EE6C} + {3B61F907-9D2D-4BB5-A02F-A9B3AD146899} @@ -175,7 +175,6 @@ - diff --git a/gpio/samples/simdevice/simdevice.vcxproj.Filters b/gpio/samples/simdevice/simdevice.vcxproj.Filters index c031c4fe5..f848b4ab1 100644 --- a/gpio/samples/simdevice/simdevice.vcxproj.Filters +++ b/gpio/samples/simdevice/simdevice.vcxproj.Filters @@ -3,25 +3,22 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {A4E7927D-2349-4071-BB6A-5A88985C9C5C} + {7EEBC80E-7D1E-487B-9B5C-640CA9103354} h;hpp;hxx;hm;inl;inc;xsd - {77C0EAF1-5ED7-4F36-95C8-F195123751D7} + {CDA01452-B2D7-4FFE-B79F-1927EC219EEC} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {FA74BC3C-6CFA-48DF-8233-655E0197C9A5} + {1C43D598-56AE-41AF-A280-5971AF597A4A} inf;inv;inx;mof;mc; - {ABBAD374-35FA-4C9E-A4F6-EBBBA3F54511} + {CFBD4D42-FFFB-478D-86FD-F543854F66ED} - - Driver Files - Driver Files diff --git a/gpio/samples/simgpio/simgpio.inx b/gpio/samples/simgpio/simgpio.inx index 0a00ec79911017b59a005329dc1a34acd096a493..96239192a70021532112bd139a9d52ddcd7fb7af 100644 GIT binary patch delta 216 zcmZ1=(^ z0+h}JDoq3`O_{uwRhA!3Z1Zi_)6A2@m;~6N8Yjo|ifSq`gfRFsxB#&((2!K1F}e)s uHY+jkGH^}KXO&ckS*gGPfwn*ziwPjLlhe2aCV%5pnXJc^vw0m?5i - {AEF88128-15A0-4F64-ABAD-504B75C24C6B} + {7E9DAF8F-8B01-40F0-9014-0F68D3AFF8D8} $(MSBuildProjectName) 1 Debug Win32 - {D6E40FD6-4479-4E69-AC17-CC75141D18E7} + {261B9C8E-1394-46A8-B4ED-221B93C772CE} @@ -147,7 +147,6 @@ - diff --git a/gpio/samples/simgpio/simgpio.vcxproj.Filters b/gpio/samples/simgpio/simgpio.vcxproj.Filters index 4e09f0937..86b896580 100644 --- a/gpio/samples/simgpio/simgpio.vcxproj.Filters +++ b/gpio/samples/simgpio/simgpio.vcxproj.Filters @@ -3,25 +3,22 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {65A688B8-1C22-40C7-B809-921DFABC7438} + {4F40F0E9-E7FD-4DF5-9DB2-E0727194390C} h;hpp;hxx;hm;inl;inc;xsd - {AF50110C-16B2-4D79-BA2E-7F9F66038B4D} + {18DDA8A2-6F76-4F8F-A945-BBFD2E79DB05} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {DFE00DCD-7A69-497A-8757-78398D64C66F} + {A611CED7-587B-4E4D-A9FB-518D3271E040} inf;inv;inx;mof;mc; - {56A85DDD-FDD8-451F-B882-8D792758B4DC} + {5BCE76B9-F5C5-4BD3-AB63-8B437AAAD065} - - Driver Files - Driver Files diff --git a/gpio/samples/simgpio_i2c/simgpio_i2c.inx b/gpio/samples/simgpio_i2c/simgpio_i2c.inx index ab8a130d839bbb13d952bf85ea44ef3d8af1d4bf..c81e39551939949c52a9fa48fe582bb9d2be39ba 100644 GIT binary patch delta 220 zcmaDMb4hkX6_a=XLlHwhLm5LRLkdGGknP8i$dJpBy19|bka_YXRxxR)0D~%nEkiIv z2~au@s5B9%G-dKxR#|>DvCY3(A2Ux*V-jG8YMlI$S5#ApA%wx7!3BtQfrg|4jnQR5 zw^@mSmw{_?KdYoV%t{3Y2($&#SWEz^oxF@oV6q;U%H(%kZj;w=>1}?;RmBVd&1f-@ delta 192 zcmca4`$A?z6%)HJLokCIL&)ZdOoq&!U?zhqgDrzELncEqLlHwhLopDiF_bXqF!(Wq zFsLv%G6XR=Gk5@5ybN4m9F3`Zvpm~lW|-NN1No#UZ{Xt+R>D*`c|Dh@Ackd=tr?vs Pt8klOm)Sgn+lm - {8D155F90-F6A1-4D96-A07F-31844C0587C5} + {C966B8DA-2679-49D0-B3CB-5DB181407EFA} $(MSBuildProjectName) 1 Debug Win32 - {84D9693D-232E-4A2A-AB11-F8477CEC5836} + {E40DE355-3DB4-48DF-B9E9-8B723EDF19F5} @@ -186,7 +186,6 @@ - diff --git a/gpio/samples/simgpio_i2c/simgpio_i2c.vcxproj.Filters b/gpio/samples/simgpio_i2c/simgpio_i2c.vcxproj.Filters index 30f81d7aa..0056ffd5f 100644 --- a/gpio/samples/simgpio_i2c/simgpio_i2c.vcxproj.Filters +++ b/gpio/samples/simgpio_i2c/simgpio_i2c.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {8F86BB64-A9E3-4E97-B7A8-47D504663D4A} + {39110185-C639-44F2-BCBE-8301771C6230} h;hpp;hxx;hm;inl;inc;xsd - {FC609C6E-4259-4CA8-BAA6-C8020B667EB0} + {64BD4AC8-F08E-4A63-818C-C69CB18068FA} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {308B7634-8106-4651-A527-67C836627552} + {7F8021D6-3384-4B61-A714-6D80F0FD5E3B} inf;inv;inx;mof;mc; - {1BDB4EDB-22EC-48BE-BFD3-47646B9F8771} + {B50BE1A8-1620-4DD8-B2A2-4AB004EE8A49} @@ -27,9 +27,6 @@ - - Driver Files - Driver Files diff --git a/hid/firefly/ReadMe.md b/hid/firefly/ReadMe.md new file mode 100644 index 000000000..1a144aba6 --- /dev/null +++ b/hid/firefly/ReadMe.md @@ -0,0 +1,85 @@ +KMDF filter driver for a HID device +=================================== +Firefly is a KMDF-based filter driver for a HID device. Along with illustrating how to write a filter driver, this sample shows how to use remote I/O target interfaces to open a HID collection in kernel-mode and send IOCTL requests to set and get feature reports, as well as how an application can use WMI interfaces to send commands to a filter driver. + +Related topics +-------------- + +[Human Input Devices Design Guide](http://msdn.microsoft.com/en-us/library/windows/hardware/ff539952) + +[Human Input Devices Reference](http://msdn.microsoft.com/en-us/library/windows/hardware/ff539956) + +Related technologies +-------------------- + +[Creating Framework-based HID Minidrivers](http://msdn.microsoft.com/en-us/library/windows/hardware/ff540774) + + +Build the sample +---------------- + +For information on how to build a driver using Microsoft Visual Studio, see [Building a Driver](http://msdn.microsoft.com/en-us/library/windows/hardware/ff554644). When you build the sample, MSBuild.exe creates luminous.lib, firefly.sys, flicker.exe, and sauron.dll. Copy these files as well as the KMDF coinstaller (wdfcoinstallerMMmmm.dll) and the INF file (firefly.inf) to a floppy disk or a temporary directory on the target system. + +**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. + +Installation +------------ + +To install the driver on Windows 7 and later operating systems: + +1. Plug the Microsoft USB Optical mouse into your target machine and verify that the mouse works. The drivers for this mouse come with the operating system so the device will start working automatically when you plug in. +2. You may need to make Group Policy changes in order to replace the existing mouse driver. If you are unable to perform steps 3-9, do the following: + 1. Open **gpedit.msd**. + 2. In the Group Policy Object Editor navigation pane, open the Computer Configuration folder. Then open Administrative Templates, open System, open Device Installation, and then open Device Installation Restrictions. + 3. Enable *Prevent installation of devices not described by other policy settings*. This will prevent Windows from automatically installing the default mouse driver so that you can then install Firefly. + 4. Enable *Allow administrators to override device installation policy*. This will allow you to bypass the ""The installation of this device is forbidden by system policy" error that you may otherwise receive when you attempt to install Firefly. + 5. You may need to reboot. + +3. Bring up the Device Manager (type **devmgmt.msc** in the Start/Run window and press enter). +4. Find the Microsoft Optical mouse under "Mice and other pointing devices" +5. Right click on the device and choose "Update Driver Software." +6. Select "Browse my computer for driver software." +7. Browse to the temporary folder you created earlier. Click Next. Click through the warning. +8. You will see "Windows has successfully updated your driver software" for the "Shiny Things Firefly Mouse" device. +9. The system will copy all the files and restart the mouse device to install the upper filter. Click Close and you are ready to run the test app. + +Testing the Sample +------------------ + +Copy the flicker.exe to the target machine and run it from an elevated command prompt. The usage is: + +Usage: Flicker \<-0 | -1 | -2\> + +-0 turns off light + +-1 turns on light + +-2 flashes light + +The following description applies to Windows Media Player 12 running on Windows 7: + +**Testing the DLL** + +1. Copy the sauron.dll to the Windows Media player Visualization directory (C:\\Program Files\\Windows Media Player\\Visualizations). +2. Register the DLL with COM by calling "regsvr32 sauron.dll" in command shell. +3. Run Windows Media Player *as administrator*. +4. Click on the "Switch to Now Playing" button in the lower right of the application. +5. Right click, select Visualizations, and you will see a menu item called "Sauron." +6. Choose either Firefly Bars or Firefly Flash and play some music. +7. You will see the mouse light dancing to the tune of the music. +8. You can unregister the DLL by calling "regsvr32 -u sauron.dll". + +Programming Tour +---------------- + +The Firefly sample is installed as an upper filter driver for the Microsoft USB Intellimouse Optical. An application provided with the sample can cause the light of the optical mouse to blink by sending commands to the filter driver using the WMI interface. + +The sample consists of: + +- Driver (firefly.sys): The Firefly driver is an upper device filter driver for the mouse driver (mouhid.sys). Firefly is a generic filter driver based on the toaster filter driver sample available in the WDK. During start device, the driver registers a WMI class (FireflyDeviceInformation). The user mode application connects to the WMI namespace (root\\wmi) and opens this class using COM interfaces. Then the application can make requests to read ("get") or change ("set") the current value of the TailLit data value from this class. In response to a set WMI request, the driver opens the HID collection using IoTarget and sends [**IOCTL\_HID\_GET\_COLLECTION\_INFORMATION**](http://msdn.microsoft.com/en-us/library/windows/hardware/ff541092) and [**IOCTL\_HID\_GET\_COLLECTION\_DESCRIPTOR**](http://msdn.microsoft.com/en-us/library/windows/hardware/ff541089) requests to get the preparsed data. The driver then calls [**HidP\_GetCaps**](http://msdn.microsoft.com/en-us/library/windows/hardware/ff539715) using the preparsed data to retrieve the capabilities of the device. After getting the capabilities of the device, the driver creates a feature report to set or clear the feature that causes the light to toggle. +- Library (luminous.lib): The sources for this file are located in the WINDDK\\src\\hid\\firefly\\lib folder. You will need to build the library before using it. This library is shared by the WDM and WDF samples. All the interfaces required to access the WMI is defined in this library and exposed as CLuminous class. +- Application (flicker.exe): The sources for this file are located in the WINDDK\\src\\hid\\firefly\\app folder. You will need to build the application before using it. This application is shared by the WDM and WDF samples. The application links to luminous.lib to open the WMI interfaces and send set requests to toggle the light. +- Sauron (sauron.dll): The sources for this file are located in the WINDDK\\src\\hid\\firefly\\sauron folder. You will need to build this dll before using it. The library is shared by the WDM and WDF samples. Sauron is a Windows Media Player visualization DLL, and is based on a sample from the Windows Media Player SDK kit. By using this DLL, you can cause the mouse lights to blink to the beats of the music. + diff --git a/hid/firefly/app/firefly.cpp b/hid/firefly/app/firefly.cpp new file mode 100644 index 000000000..1e0ecb89a --- /dev/null +++ b/hid/firefly/app/firefly.cpp @@ -0,0 +1,135 @@ +/*++ + +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: + + Firefly.c + +Abstract: + + App to change the tail light state of MS optical mouse with the help of + firefly driver. This app uses WMI interface to talk to the driver. + +Environment: + + User Mode console application + +--*/ +#include "luminous.h" +#include + +#define USAGE \ +_T("Usage: Flicker <-0 | -1 | -2>\n\ + \t\t-0 turns off light \n\ + \t\t-1 turns on light \n\ + \t\t-2 flashes light ") + +int __cdecl +main( + _In_ ULONG argc, + _In_reads_(argc) PCHAR argv[] + ) +{ + int i, j, k; + BOOL bAdjustLight = FALSE; + BOOL bSuccessful; + ULONG lightSetting = 0; + CLuminous *luminous; + + if (argc == 2) { + + if (argv[1][0] == '-') { + + if ((argv[1][1] >= '0') && (argv[1][1] <= '2')) { + + bAdjustLight = TRUE; + lightSetting = (argv[1][1] - '0'); + } + } + } + + if (FALSE == bAdjustLight) + { + _tprintf(USAGE); + exit(0); + } + + luminous = new CLuminous(); + + if (luminous == NULL) { + + _tprintf(_T("Problem creating Luminous\n")); + return 0; + } + + if (!luminous->Open()) { + + _tprintf(_T("Problem opening Luminous\n")); + delete(luminous); + return 0; + } + + if (bAdjustLight) { + + if (lightSetting < 2) { + + bSuccessful = luminous->Set((BOOL) lightSetting); + + if (bSuccessful) { + + _tprintf(_T("Adjusted light to %x\n"), lightSetting); + + } else { + + _tprintf(_T("Problem occured while adjusting light: %x\n"), GetLastError()); + } + + } else { + + k=0; + j=1; + for(i = 500; (i>0)&&(j>0); i-=j) { + + j = (i*9/100); + Sleep(i); + if(!luminous->Set((BOOL) k)) { + _tprintf(_T("Set operation on Luminous failed.\n")); + goto End; + } + k=1-k; + } + for(i = 12; i<500; i+=j) { + + j = (i*9/100); + Sleep(i); + if(!luminous->Set((BOOL) k)) { + _tprintf(_T("Set operation on Luminous failed.\n")); + goto End; + } + k=1-k; + } + if (k) { + if(!luminous->Set((BOOL) k)) { + _tprintf(_T("Set operation on Luminous failed.\n")); + } + } + } + } + + luminous->Set(TRUE); + +End: + luminous->Close(); + + delete(luminous); + + return 0; +} + + diff --git a/hid/firefly/app/flicker.vcxproj b/hid/firefly/app/flicker.vcxproj new file mode 100644 index 000000000..a8007a997 --- /dev/null +++ b/hid/firefly/app/flicker.vcxproj @@ -0,0 +1,194 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {BC8A0B60-3D51-403C-838F-DBDB7FDB2154} + $(MSBuildProjectName) + Debug + Win32 + {4734AE46-4244-46F9-BB43-9EF5D8DE879E} + + + + 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) + + + + + + + + + + + + + + + + flicker + + + flicker + + + flicker + + + flicker + + + + 0x400000 + %(AdditionalDependencies);.\..\lib\$(IntDir)\luminous.lib;ole32.lib;oleaut32.lib;wbemuuid.lib + + + true + Level4 + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);..\shared + + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);..\shared + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);..\shared + + + + + 0x400000 + %(AdditionalDependencies);.\..\lib\$(IntDir)\luminous.lib;ole32.lib;oleaut32.lib;wbemuuid.lib + + + true + Level4 + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);..\shared + + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);..\shared + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);..\shared + + + + + 0x400000 + %(AdditionalDependencies);.\..\lib\$(IntDir)\luminous.lib;ole32.lib;oleaut32.lib;wbemuuid.lib + + + true + Level4 + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);..\shared + + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);..\shared + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);..\shared + + + + + 0x400000 + %(AdditionalDependencies);.\..\lib\$(IntDir)\luminous.lib;ole32.lib;oleaut32.lib;wbemuuid.lib + + + true + Level4 + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);..\shared + + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);..\shared + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);..\shared + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/hid/firefly/app/flicker.vcxproj.Filters b/hid/firefly/app/flicker.vcxproj.Filters new file mode 100644 index 000000000..a6525150c --- /dev/null +++ b/hid/firefly/app/flicker.vcxproj.Filters @@ -0,0 +1,22 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {7827BF1D-B424-4A2B-8404-408D7F878257} + + + h;hpp;hxx;hm;inl;inc;xsd + {1131E412-C497-467D-938B-A032FF253D2B} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {0F63A61D-7CA9-4EC4-BEC0-448A54A7A2B6} + + + + + Source Files + + + \ No newline at end of file diff --git a/hid/firefly/driver/device.c b/hid/firefly/driver/device.c new file mode 100644 index 000000000..c6bae7210 --- /dev/null +++ b/hid/firefly/driver/device.c @@ -0,0 +1,128 @@ +/*++ + +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 the Windows Driver Framework Device object + handlers for the firefly filter driver. + +Environment: + + Kernel mode + +--*/ + +#include "FireFly.h" + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, FireFlyEvtDeviceAdd) +#endif + +NTSTATUS +FireFlyEvtDeviceAdd( + WDFDRIVER Driver, + 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 to be part of the device stack as a filter. + +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 attributes; + NTSTATUS status; + PDEVICE_CONTEXT pDeviceContext; + WDFDEVICE device; + WDFMEMORY memory; + size_t bufferLength; + + UNREFERENCED_PARAMETER(Driver); + + PAGED_CODE(); + + // + // Configure the device as a filter driver + // + WdfFdoInitSetFilter(DeviceInit); + + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, DEVICE_CONTEXT); + + status = WdfDeviceCreate(&DeviceInit, &attributes, &device); + if (!NT_SUCCESS(status)) { + KdPrint(("FireFly: WdfDeviceCreate, Error %x\n", status)); + return status; + } + + // + // Driver Framework always zero initializes an objects context memory + // + pDeviceContext = WdfObjectGet_DEVICE_CONTEXT(device); + + // + // Initialize our WMI support + // + status = WmiInitialize(device, pDeviceContext); + if (!NT_SUCCESS(status)) { + KdPrint(("FireFly: Error initializing WMI 0x%x\n", status)); + return status; + } + + // + // In order to send ioctls to our PDO, we have open to open it + // by name so that we have a valid filehandle (fileobject). + // When we send ioctls using the IoTarget, framework automatically + // sets the filobject in the stack location. + // + WDF_OBJECT_ATTRIBUTES_INIT(&attributes); + // + // By parenting it to device, we don't have to worry about + // deleting explicitly. It will be deleted along witht the device. + // + attributes.ParentObject = device; + + status = WdfDeviceAllocAndQueryProperty(device, + DevicePropertyPhysicalDeviceObjectName, + NonPagedPool, + &attributes, + &memory); + + if (!NT_SUCCESS(status)) { + KdPrint(("FireFly: WdfDeviceAllocAndQueryProperty failed 0x%x\n", status)); + return STATUS_UNSUCCESSFUL; + } + + pDeviceContext->PdoName.Buffer = WdfMemoryGetBuffer(memory, &bufferLength); + + if (pDeviceContext->PdoName.Buffer == NULL) { + return STATUS_UNSUCCESSFUL; + } + + pDeviceContext->PdoName.MaximumLength = (USHORT) bufferLength; + pDeviceContext->PdoName.Length = (USHORT) bufferLength-sizeof(UNICODE_NULL); + + return status; +} diff --git a/hid/firefly/driver/device.h b/hid/firefly/driver/device.h new file mode 100644 index 000000000..13a051341 --- /dev/null +++ b/hid/firefly/driver/device.h @@ -0,0 +1,43 @@ +/*++ + +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.h + +Abstract: + + This module contains the Windows Driver Framework Device object + handlers for the firefly filter driver. + +Environment: + + Kernel mode + +--*/ + +// +// The device context performs the same job as +// a WDM device extension in the driver framework +// +typedef struct _DEVICE_CONTEXT +{ + // Our WMI data generated from firefly.mof + FireflyDeviceInformation WmiInstance; + + UNICODE_STRING PdoName; + +} DEVICE_CONTEXT, *PDEVICE_CONTEXT; + +WDF_DECLARE_CONTEXT_TYPE(DEVICE_CONTEXT) + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(FireflyDeviceInformation, InstanceGetInfo) + +EVT_WDF_DEVICE_CONTEXT_CLEANUP EvtDeviceContextCleanup; + diff --git a/hid/firefly/driver/driver.c b/hid/firefly/driver/driver.c new file mode 100644 index 000000000..acf7c4064 --- /dev/null +++ b/hid/firefly/driver/driver.c @@ -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: + + driver.c + +Abstract: + + This modules contains the Windows Driver Framework Driver object + handlers for the firefly filter driver. + +Environment: + + Kernel mode + +--*/ + +#include "FireFly.h" + +// +// Main driver entry, initialize the framework, register +// driver event handlers +// +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ) +{ + WDF_DRIVER_CONFIG params; + NTSTATUS status; + + KdPrint(("FireFly: DriverEntry - WDF version built on %s %s\n", + __DATE__, __TIME__)); + + WDF_DRIVER_CONFIG_INIT( + ¶ms, + FireFlyEvtDeviceAdd + ); + + // + // Create the framework WDFDRIVER object, with the handle + // to it returned in Driver. + // + status = WdfDriverCreate(DriverObject, + RegistryPath, + WDF_NO_OBJECT_ATTRIBUTES, + ¶ms, + WDF_NO_HANDLE); + if (!NT_SUCCESS(status)) { + // + // Framework will automatically cleanup on error Status return + // + KdPrint(("FireFly: Error Creating WDFDRIVER 0x%x\n", status)); + } + + return status; +} diff --git a/hid/firefly/driver/firefly.h b/hid/firefly/driver/firefly.h new file mode 100644 index 000000000..9da989c14 --- /dev/null +++ b/hid/firefly/driver/firefly.h @@ -0,0 +1,52 @@ +/*++ + +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: + + FireFly.h + +Abstract: + + This is the header for the Windows Driver Framework version + of the firefly filter driver. + +Environment: + + Kernel mode + +--*/ + +#include +#include +#define NTSTRSAFE_LIB +#include +#include +#include + +// +// Our drivers generated include from firefly.mof +// See makefile.inc for wmi commands +// +#include "fireflymof.h" + +// Our drivers modules includes +#include "device.h" +#include "wmi.h" +#include "vfeature.h" +#include "magic.h" + +// +// WDFDRIVER Object Events +// + +DRIVER_INITIALIZE DriverEntry; + +EVT_WDF_DRIVER_DEVICE_ADD FireFlyEvtDeviceAdd; + diff --git a/hid/firefly/driver/firefly.inx b/hid/firefly/driver/firefly.inx new file mode 100644 index 000000000..ad800da9d --- /dev/null +++ b/hid/firefly/driver/firefly.inx @@ -0,0 +1,128 @@ +[Version] +Signature="$Windows NT$" +Class=Mouse +ClassGUID={4D36E96F-E325-11CE-BFC1-08002BE10318} +Provider=%Provider% +DriverVer=03/17/2001,1.0.0.1 +CatalogFile=KmdfSamples.cat + +[DestinationDirs] +DefaultDestDir = 12 ; DIRID_DRIVERS + +[ControlFlags] +; We don't want our device to be installable via the non-PnP hardware dialogs +ExcludeFromSelect = * + +; Manufacturer Section +; --------------------------------------------------------- +[Manufacturer] +%ShinyThings%=ShinyThingsMfg,NT$ARCH$ + +; Devices Section +; --------------------------------------------------------- +[ShinyThingsMfg.NT$ARCH$] +%HID\Vid_045E&Pid_001E.DeviceDesc%=Firefly_Inst, HID\Vid_045E&Pid_001E +%HID\Vid_045E&Pid_0029.DeviceDesc%=Firefly_Inst, HID\Vid_045E&Pid_0029 +%HID\Vid_045E&Pid_0039.DeviceDesc%=Firefly_Inst, HID\Vid_045E&Pid_0039 +%HID\Vid_045E&Pid_0040.DeviceDesc%=Firefly_Inst, HID\Vid_045E&Pid_0040 +%HID\Vid_045E&Pid_0047.DeviceDesc%=Firefly_Inst, HID\Vid_045E&Pid_0047 + +; Install Section +; --------------------------------------------------------- +[Firefly_Inst.NT] +Include = MSMOUSE.INF +Needs = HID_Mouse_Inst.NT +CopyFiles = Firefly_Inst_CopyFiles.NT + +[Firefly_Inst.NT.HW] +Include = MSMOUSE.INF +Needs = HID_Mouse_Inst.NT.Hw +AddReg = Firefly_Inst_HWAddReg.NT + +[Firefly_Inst_HWAddReg.NT] +HKR,,"UpperFilters",0x00010000,"Firefly" + +[Firefly_Inst_CopyFiles.NT] +Firefly.sys + +[Firefly_Inst.NT.Services] +Include = MSMOUSE.INF +Needs = HID_Mouse_Inst.NT.Services +AddService = Firefly, , Firefly_Service_Inst + +[Firefly_Service_Inst] +DisplayName = %Firefly.SvcDesc% +ServiceType = 1 ; SERVICE_KERNEL_DRIVER +StartType = 3 ; SERVICE_DEMAND_START +ErrorControl = 1 ; SERVICE_ERROR_NORMAL +ServiceBinary = %12%\Firefly.sys + +; Source Media Section +; --------------------------------------------------------- +[SourceDisksNames] +1 = %DiskName% + +[SourceDisksFiles] +Firefly.sys = 1 + +; +;--- Firefly_Inst WDF Coinstaller installation ------ +; + +[DestinationDirs] +Firefly_Inst_CoInstaller_CopyFiles = 11 + +[Firefly_Inst.NT.CoInstallers] +AddReg=Firefly_Inst_CoInstaller_AddReg +CopyFiles=Firefly_Inst_CoInstaller_CopyFiles + +[Firefly_Inst_CoInstaller_AddReg] +HKR,,CoInstallers32,0x00010000, "WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll,WdfCoInstaller" + +[Firefly_Inst_CoInstaller_CopyFiles] +WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll + +[SourceDisksFiles] +WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll=1 ; make sure the number matches with SourceDisksNames + +[Firefly_Inst.NT.Wdf] +KmdfService = Firefly, Firefly_wdfsect +[Firefly_wdfsect] +KmdfLibraryVersion = $KMDFVERSION$ + + +; Strings Section +; --------------------------------------------------------- +[Strings] +; Provider names +Provider = "TODO-Set-Provider" + +; Mfg names +ShinyThings = "Shiny Things" + +; Service names +Firefly.SvcDesc = "Firefly Service" + +; Media names +DiskName = "Firefly Driver Disk" + +; HID device IDs +HID\VID_045E&PID_001E.DeviceDesc = "Shiny Things Firefly Mouse" +HID\VID_045E&PID_0029.DeviceDesc = "Shiny Things Firefly Mouse" +HID\VID_045E&PID_0039.DeviceDesc = "Shiny Things Firefly Mouse" +HID\VID_045E&PID_0040.DeviceDesc = "Shiny Things Firefly Mouse" +HID\VID_045E&PID_0047.DeviceDesc = "Shiny Things Firefly Mouse" + +; Standard defs +SPSVCINST_TAGTOFRONT = 0x00000001 +SPSVCINST_ASSOCSERVICE= 0x00000002 +SERVICE_KERNEL_DRIVER = 1 +SERVICE_BOOT_START = 0 +SERVICE_SYSTEM_START = 1 +SERVICE_AUTO_START = 2 +SERVICE_ERROR_NORMAL = 1 +SERVICE_ERROR_IGNORE = 0 +REG_EXPAND_SZ = 0x00020000 +REG_DWORD = 0x00010001 +REG_SZ = 0x00000000 + diff --git a/hid/firefly/driver/firefly.mof b/hid/firefly/driver/firefly.mof new file mode 100644 index 000000000..3a0ea7774 --- /dev/null +++ b/hid/firefly/driver/firefly.mof @@ -0,0 +1,20 @@ +#PRAGMA AUTORECOVER + +[Dynamic, Provider("WMIProv"), + WMI, + Description("Firefly driver information"), + guid("{AB27DB29-DB25-42E6-A3E7-28BD46BDB666}"), + locale("MS\\0x409")] +class FireflyDeviceInformation +{ + [key, read] + string InstanceName; + [read] boolean Active; + + [WmiDataId(1), + read, + write, + Description("Current state of the tail light.")] + boolean TailLit; +}; + diff --git a/hid/firefly/driver/firefly.rc b/hid/firefly/driver/firefly.rc new file mode 100644 index 000000000..6688379c3 --- /dev/null +++ b/hid/firefly/driver/firefly.rc @@ -0,0 +1,13 @@ +#include +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "Filter Driver for the Firefly Device (Framework Version)" +#define VER_INTERNALNAME_STR "firefly.sys" +#define VER_ORIGINALFILENAME_STR "firefly.sys" + +#include "common.ver" + +FireflyWMI MOFDATA Firefly.bmf + diff --git a/hid/firefly/driver/firefly.vcxproj b/hid/firefly/driver/firefly.vcxproj new file mode 100644 index 000000000..1f23a2dab --- /dev/null +++ b/hid/firefly/driver/firefly.vcxproj @@ -0,0 +1,188 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {72EF0AC2-AF89-4FAF-B50B-CFD1CBCFC1FA} + $(MSBuildProjectName) + 1 + Debug + Win32 + {3BBB9A3C-6D04-44EA-94CD-1BCBB6785ABE} + + + + 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)\firefly.inf + + + ".\$(IntDir)\firefly.bmf" + + + + .\$(IntDir)\fireflymof.h + + + + firefly + + + firefly + + + firefly + + + firefly + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\hidparse.lib + + + %(PreprocessorDefinitions) + + + + + %(PreprocessorDefinitions) + + + %(PreprocessorDefinitions) + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\hidparse.lib + + + %(PreprocessorDefinitions) + + + + + %(PreprocessorDefinitions) + + + %(PreprocessorDefinitions) + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\hidparse.lib + + + %(PreprocessorDefinitions) + + + + + %(PreprocessorDefinitions) + + + %(PreprocessorDefinitions) + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\hidparse.lib + + + %(PreprocessorDefinitions) + + + + + %(PreprocessorDefinitions) + + + %(PreprocessorDefinitions) + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/hid/firefly/driver/firefly.vcxproj.Filters b/hid/firefly/driver/firefly.vcxproj.Filters new file mode 100644 index 000000000..0ebb78403 --- /dev/null +++ b/hid/firefly/driver/firefly.vcxproj.Filters @@ -0,0 +1,48 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {E4869018-74E3-4289-AD22-20F70C981AE2} + + + h;hpp;hxx;hm;inl;inc;xsd + {1F21272C-56E1-4F3E-9CCC-6B4A3A4C789B} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {22D17D30-9550-4068-801A-6533F5191361} + + + inf;inv;inx;mof;mc; + {3970B561-8552-40C0-89DC-8AE1A82F3034} + + + + + Driver Files + + + Driver Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/hid/firefly/driver/magic.h b/hid/firefly/driver/magic.h new file mode 100644 index 000000000..7fce47c75 --- /dev/null +++ b/hid/firefly/driver/magic.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: + + magic.h + +Abstract: + + This header contains private defines pertaining to Microsoft Optical mice. + +Environment: + + Kernel mode + +--*/ + +#define TAILLIGHT_PAGE 0xFF +#define TAILLIGHT_FEATURE 0x02 + + + diff --git a/hid/firefly/driver/vfeature.c b/hid/firefly/driver/vfeature.c new file mode 100644 index 000000000..8b76db1f0 --- /dev/null +++ b/hid/firefly/driver/vfeature.c @@ -0,0 +1,250 @@ +/*++ + +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: + + vfeature.c + +Abstract: + + This module sets the state of a vendor specific HID feature. + +Environment: + + Kernel mode + +--*/ + +#include "firefly.h" + +#pragma warning(disable:4201) // nameless struct/union +#pragma warning(disable:4214) // bit field types other than int + +#include +#include + +#pragma warning(default:4201) +#pragma warning(default:4214) + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, FireflySetFeature) +#endif + +NTSTATUS +FireflySetFeature( + IN PDEVICE_CONTEXT DeviceContext, + IN UCHAR PageId, + IN USHORT FeatureId, + IN BOOLEAN EnableFeature + ) +/*++ + +Routine Description: + + This routine sets the HID feature by sending HID ioctls to our device. + These IOCTLs will be handled by HIDUSB and converted into USB requests + and send to the device. + +Arguments: + + DeviceContext - Context for our device + + PageID - UsagePage of the light control feature. + + FeatureId - Usage ID of the feature. + + EnanbleFeature - True to turn the light on, Falst to turn if off. + + +Return Value: + + NT Status code + +--*/ +{ + WDF_MEMORY_DESCRIPTOR inputDescriptor, outputDescriptor; + NTSTATUS status; + HID_COLLECTION_INFORMATION collectionInformation = {0}; + PHIDP_PREPARSED_DATA preparsedData; + HIDP_CAPS caps; + USAGE usage; + ULONG usageLength; + PCHAR report; + WDFIOTARGET hidTarget; + WDF_IO_TARGET_OPEN_PARAMS openParams; + + PAGED_CODE(); + + // + // Preinit for error. + // + preparsedData = NULL; + report = NULL; + hidTarget = NULL; + + status = WdfIoTargetCreate(WdfObjectContextGetObject(DeviceContext), + WDF_NO_OBJECT_ATTRIBUTES, + &hidTarget); + if (!NT_SUCCESS(status)) { + KdPrint(("FireFly: WdfIoTargetCreate failed 0x%x\n", status)); + return status; + } + + // + // Open it up, write access only! + // + WDF_IO_TARGET_OPEN_PARAMS_INIT_OPEN_BY_NAME( + &openParams, + &DeviceContext->PdoName, + FILE_WRITE_ACCESS); + + // + // We will let the framework to respond automatically to the pnp + // state changes of the target by closing and opening the handle. + // + openParams.ShareAccess = FILE_SHARE_WRITE | FILE_SHARE_READ; + + status = WdfIoTargetOpen(hidTarget, &openParams); + if (!NT_SUCCESS(status)) { + KdPrint(("FireFly: WdfIoTargetOpen failed 0x%x\n", status)); + goto ExitAndFree; + } + + + WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&outputDescriptor, + (PVOID) &collectionInformation, + sizeof(HID_COLLECTION_INFORMATION)); + + // + // Now get the collection information for this device + // + status = WdfIoTargetSendIoctlSynchronously(hidTarget, + NULL, + IOCTL_HID_GET_COLLECTION_INFORMATION, + NULL, + &outputDescriptor, + NULL, + NULL); + + if (!NT_SUCCESS(status)) { + KdPrint(("FireFly: WdfIoTargetSendIoctlSynchronously failed 0x%x\n", status)); + goto ExitAndFree; + } + + preparsedData = (PHIDP_PREPARSED_DATA) ExAllocatePoolWithTag( + NonPagedPool, collectionInformation.DescriptorSize, 'ffly'); + + if (preparsedData == NULL) { + status = STATUS_INSUFFICIENT_RESOURCES; + goto ExitAndFree; + } + + WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&outputDescriptor, + (PVOID) preparsedData, + collectionInformation.DescriptorSize); + + status = WdfIoTargetSendIoctlSynchronously(hidTarget, + NULL, + IOCTL_HID_GET_COLLECTION_DESCRIPTOR, + NULL, + &outputDescriptor, + NULL, + NULL); + + if (!NT_SUCCESS(status)) { + KdPrint(("FireFly: WdfIoTargetSendIoctlSynchronously failed 0x%x\n", status)); + goto ExitAndFree; + } + + // + // Now get the capabilities. + // + RtlZeroMemory(&caps, sizeof(HIDP_CAPS)); + + status = HidP_GetCaps(preparsedData, &caps); + + if (!NT_SUCCESS(status)) { + + goto ExitAndFree; + } + + // + // Create a report to send to the device. + // + report = (PCHAR) ExAllocatePoolWithTag( + NonPagedPool, caps.FeatureReportByteLength, 'ffly'); + + if (report == NULL) { + goto ExitAndFree; + } + + // + // Start with a zeroed report. If we are disabling the feature, this might + // be all we need to do. + // + RtlZeroMemory(report, caps.FeatureReportByteLength); + status = STATUS_SUCCESS; + + if (EnableFeature) { + + // + // Edit the report to reflect the enabled feature + // + usage = FeatureId; + usageLength = 1; + + status = HidP_SetUsages( + HidP_Feature, + PageId, + 0, + &usage, // pointer to the usage list + &usageLength, // number of usages in the usage list + preparsedData, + report, + caps.FeatureReportByteLength + ); + if (!NT_SUCCESS(status)) { + KdPrint(("FireFly: HidP_SetUsages failed 0x%x\n", status)); + goto ExitAndFree; + } + } + + WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&inputDescriptor, + report, + caps.FeatureReportByteLength); + status = WdfIoTargetSendIoctlSynchronously(hidTarget, + NULL, + IOCTL_HID_SET_FEATURE, + &inputDescriptor, + NULL, + NULL, + NULL); + if (!NT_SUCCESS(status)) { + KdPrint(("FireFly: WdfIoTargetSendIoctlSynchronously failed 0x%x\n", status)); + goto ExitAndFree; + } + +ExitAndFree: + + if (preparsedData != NULL) { + ExFreePool(preparsedData); + preparsedData = NULL; + } + + if (report != NULL) { + ExFreePool(report); + report = NULL; + } + + if (hidTarget != NULL) { + WdfObjectDelete(hidTarget); + } + + return status; +} diff --git a/hid/firefly/driver/vfeature.h b/hid/firefly/driver/vfeature.h new file mode 100644 index 000000000..e4a213b88 --- /dev/null +++ b/hid/firefly/driver/vfeature.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: + + vfeature.h + +Abstract: + + This module contains the Windows Driver Framework HID I/O Target + handlers for the firefly filter driver. + +Environment: + + Kernel mode + +--*/ + +#if !defined(_VFEATURE_H_) +#define _VFEATURE_H_ + +NTSTATUS +FireflySetFeature( + IN PDEVICE_CONTEXT DeviceContext, + IN UCHAR PageId, + IN USHORT FeatureId, + IN BOOLEAN EnableFeature + ); + +#endif // _VFEATURE_H diff --git a/hid/firefly/driver/wmi.c b/hid/firefly/driver/wmi.c new file mode 100644 index 000000000..3b1e87804 --- /dev/null +++ b/hid/firefly/driver/wmi.c @@ -0,0 +1,192 @@ +/*++ + +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 modules contains the Windows Driver Framework WMI + handlers for the firefly filter driver. + +Environment: + + Kernel mode + +--*/ + +#include "FireFly.h" + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, WmiInitialize) +#pragma alloc_text(PAGE, EvtWmiInstanceQueryInstance) +#pragma alloc_text(PAGE, EvtWmiInstanceSetInstance) +#pragma alloc_text(PAGE, EvtWmiInstanceSetItem) +#endif + +// +// Register our GUID and Datablock generated from the Firefly.mof file. +// +NTSTATUS +WmiInitialize( + WDFDEVICE Device, + PDEVICE_CONTEXT DeviceContext + ) +{ + WDF_WMI_PROVIDER_CONFIG providerConfig; + WDF_WMI_INSTANCE_CONFIG instanceConfig; + WDF_OBJECT_ATTRIBUTES woa; + WDFWMIINSTANCE instance; + NTSTATUS status; + DECLARE_CONST_UNICODE_STRING(mofRsrcName, MOFRESOURCENAME); + + UNREFERENCED_PARAMETER(DeviceContext); + + PAGED_CODE(); + + status = WdfDeviceAssignMofResourceName(Device, &mofRsrcName); + if (!NT_SUCCESS(status)) { + KdPrint(("FireFly: Error in WdfDeviceAssignMofResourceName %x\n", status)); + return status; + } + + WDF_WMI_PROVIDER_CONFIG_INIT(&providerConfig, &FireflyDeviceInformation_GUID); + providerConfig.MinInstanceBufferSize = sizeof(FireflyDeviceInformation); + + WDF_WMI_INSTANCE_CONFIG_INIT_PROVIDER_CONFIG(&instanceConfig, &providerConfig); + instanceConfig.Register = TRUE; + instanceConfig.EvtWmiInstanceQueryInstance = EvtWmiInstanceQueryInstance; + instanceConfig.EvtWmiInstanceSetInstance = EvtWmiInstanceSetInstance; + instanceConfig.EvtWmiInstanceSetItem = EvtWmiInstanceSetItem; + + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&woa, FireflyDeviceInformation); + + // + // No need to store the WDFWMIINSTANCE in the device context because it is + // passed back in the WMI instance callbacks and is not referenced outside + // of those callbacks. + // + status = WdfWmiInstanceCreate(Device, &instanceConfig, &woa, &instance); + + if (NT_SUCCESS(status)) { + FireflyDeviceInformation* info; + + info = InstanceGetInfo(instance); + info->TailLit = TRUE; + } + + return status; +} + +NTSTATUS +EvtWmiInstanceQueryInstance( + IN WDFWMIINSTANCE WmiInstance, + IN ULONG OutBufferSize, + IN PVOID OutBuffer, + OUT PULONG BufferUsed + ) +{ + FireflyDeviceInformation* pInfo; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER(OutBufferSize); + + pInfo = InstanceGetInfo(WmiInstance); + + // + // Our mininum buffer size has been checked by the Framework + // and failed automatically if too small. + // + *BufferUsed = sizeof(*pInfo); + + RtlCopyMemory(OutBuffer, pInfo, sizeof(*pInfo)); + + return STATUS_SUCCESS; +} + +NTSTATUS +EvtWmiInstanceSetInstance( + IN WDFWMIINSTANCE WmiInstance, + IN ULONG InBufferSize, + IN PVOID InBuffer + ) +{ + FireflyDeviceInformation* pInfo; + ULONG length; + NTSTATUS status; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER(InBufferSize); + + pInfo = InstanceGetInfo(WmiInstance); + + // + // Our mininum buffer size has been checked by the Framework + // and failed automatically if too small. + // + length = sizeof(*pInfo); + + RtlMoveMemory(pInfo, InBuffer, length); + + // + // Tell the HID device about the new tail light state + // + status = FireflySetFeature( + WdfObjectGet_DEVICE_CONTEXT(WdfWmiInstanceGetDevice(WmiInstance)), + TAILLIGHT_PAGE, + TAILLIGHT_FEATURE, + pInfo->TailLit + ); + + return status; +} + +NTSTATUS +EvtWmiInstanceSetItem( + IN WDFWMIINSTANCE WmiInstance, + IN ULONG DataItemId, + IN ULONG InBufferSize, + IN PVOID InBuffer + ) +{ + NTSTATUS status; + FireflyDeviceInformation* pInfo; + + PAGED_CODE(); + + pInfo = InstanceGetInfo(WmiInstance); + + if (DataItemId == 1) { + if (InBufferSize < FireflyDeviceInformation_TailLit_SIZE) { + return STATUS_BUFFER_TOO_SMALL; + } + + pInfo->TailLit = (*(PBOOLEAN) InBuffer) ? TRUE : FALSE; + + // + // Tell the HID device about the new tail light state + // + status = FireflySetFeature( + WdfObjectGet_DEVICE_CONTEXT(WdfWmiInstanceGetDevice(WmiInstance)), + TAILLIGHT_PAGE, + TAILLIGHT_FEATURE, + pInfo->TailLit + ); + + return status; + } + else { + return STATUS_INVALID_DEVICE_REQUEST; + } +} + diff --git a/hid/firefly/driver/wmi.h b/hid/firefly/driver/wmi.h new file mode 100644 index 000000000..fcaaa1d35 --- /dev/null +++ b/hid/firefly/driver/wmi.h @@ -0,0 +1,43 @@ +/*++ + +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.h + +Abstract: + + This module contains the Windows Driver Framework WMI + handlers for the firefly filter driver. + +Environment: + + Kernel mode + +--*/ + +// +// Where they are described. +// +#define MOFRESOURCENAME L"FireflyWMI" + +// +// Initialize the FireFly drivers WMI support +// +NTSTATUS +WmiInitialize( + WDFDEVICE Device, + PDEVICE_CONTEXT DeviceContext + ); + +EVT_WDF_WMI_INSTANCE_QUERY_INSTANCE EvtWmiInstanceQueryInstance; +EVT_WDF_WMI_INSTANCE_SET_INSTANCE EvtWmiInstanceSetInstance; +EVT_WDF_WMI_INSTANCE_SET_ITEM EvtWmiInstanceSetItem; + diff --git a/hid/firefly/firefly.htm b/hid/firefly/firefly.htm new file mode 100644 index 000000000..c74ee2a96 --- /dev/null +++ b/hid/firefly/firefly.htm @@ -0,0 +1,101 @@ + + + + +Firefly + + + +

Firefly

+ + + +

SUMMARY

+Firefly is a filter driver for an HID device. Along with illustrating how to write a filter driver, this sample shows how to use remote I/O target interfaces to open a HID collection in kernel-mode and send IOCTL requests to set and get feature reports, how an application can use WMI interfaces to send commands to a filter driver. + +

Introduction to firefly sample

+ +Firefly sample is installed as an upper filter driver of Microsoft USB Intellimouse Optical. An application provided with the sample can cause the light of the optical mouse to blink by sending commands to the filter driver using WMI interface. The sample consists of: +

+Driver (firefly.sys): It's an upper device filter driver of mouse (mouhid.sys). This is a generic filter driver based on toaster filter driver sample available in this DDK. During start device, the driver registers a WMI class (FireflyDeviceInformation). The usermode application connects to WMI namespace (root\wmi) and opens this class using COM interfaces and makes call to set or get dataitem value (TailLit) of this class. In response to set WMI request, driver opens the HID collection using IoTarget and sends IOCTL_HID_GET_COLLECTION_INFORMATION & IOCTL_HID_GET_COLLECTION_DESCRIPTOR ioctls to get the preparsed data. It then calls HidP_GetCaps using the preparsed data to get the capabilities of the device. After getting the capabilities of the device, the driver creates a feature report to set or clear the feature that causes the light to toggle. +

+Library (luminous.lib): The sources for this file is located in the WINDDK\\src\wdm\hid\firefly\lib folder. You will need to build it before using it. This is shared by the WDM and WDF sample. All the interfaces required to access the WMI is defined in this library and exposed as CLuminous class. +

+Application (flicker.exe): The sources for this file is located in the WINDDK\\src\wdm\hid\firefly\app folder. You will need to build it before using it. This is shared by the WDM and WDF sample. The application links to luminous lib to open the WMI interfaces and send set request to toggle the light. +

+Sauron (sauron.dll): The sources for this file is located in the WINDDK\\src\wdm\hid\firefly\sauron folder. You will need to build it before using it. This is shared by the WDM and WDF sample. This is a windows media player visualization DLL. This DLL is created based on a sample from the Windows Media player SDK kit. By using this DLL, you can cause the mouse lights to blink to the beats of the music. Fun stuff to try out. +

+ + +

BUILDING THE SAMPLE

+To build the sample driver and test applications, you must first set up the DDK environment on your host machine. The WDK documentation has complete description on how to do this. +

    +
  1. +Once the build environment is set up, run the build –ceZ command in the firefly directory. This will build the luminous.lib, firefly.sys, flicker.exe, and sauron.dll and place all these bits under the appropriate target build and platform specific sub directories of firefly/disk directory.

    +

  2. +Copy the KMDF coinstaller (wdfcoinstallerMMmmm.dll), .sys, .exe and the INF file (firefly.inf) to a floppy disk or a temporary directory on the target system. +
+

+ + +

INSTALLING THE SAMPLE

+To install the driver on Windows XP and later operating systems:

+

    +
  1. Get Microsoft's USB Optical mouse and plug that into your target machine and make sure the mouse works. The drivers for this mouse comes with the operating system so the device will start working automatically when you plug in. +
  2. Copy the firefly.sys and the INF file (firefly.inf) to a floppy disk or a temporary directory on the target system. +
  3. Bring up the device manager (type devmgmt.msc in the Start/Run window and press enter). +
  4. Find the Microsoft Optical mouse under "Mice and other pointing devices" +
  5. Right click on the device and choose "Update Driver..." +
  6. Select the "Install from a list of specific location (Advanced)", and click Next. +
  7. Select "Don't search. I will choose the driver to Install" and click Next. +
  8. Click on "Have Disk..." and specify the floppy or temporary directory where the INF and the sys files are present. +
  9. The system will show "Shiny Things Firefly mouse" in the list of compatible hardware. Choose that and click Next. +
  10. You will get a warning about installing a driver that the system cannot verify whether it's compatible with your hardware. Ignore that and say Yes to installing the driver. +
  11. The system will go ahead and install the driver. At this point, you will get a warning dialog about installing an unsigned driver. Ignore that and click on "Continue Anyway" button. +
  12. The system will copy all the files and restart the mouse device to install the upper filter. Click on Finish button and you are ready to run the test app. +
+ + +To install the driver on Windows 2000:

+ +

    +
  1. Find the mouse device in the Device manager. On Windows 2000, this device might come up as a "HID compliant mouse" under "Mice and other pointing devices". +
  2. Double click on the device to bring up the properties dialog. +
  3. Go to the Driver Tab and click on "Update Driver..." button. Click Next. +
  4. Select "Display a list of the known driver..." option and click Next. +
  5. Click on "Have Disk..." and specify the floppy or temporary directory where the INF and the sys files are present. +
  6. Click Next. You will get warning about installing an unsigned driver. Click on Yes to continue the driver installation. +
  7. The system will copy all the files and restart the mouse device to install the upper filter. Click on Finish and close the Device manager property dialog. You are ready to run the test app. +
+ +

TESTING THE SAMPLE

+ +Copy the flicker.exe to the target machine and run it from command line. The usage is: +

+Usage: Flicker <-0 | -1 | -2>
+ -0 turns off light
+ -1 turns on light
+ -2 flashes light +
+

+Testing the DLL: +

    +
  1. Copy the sauron.dll to the Windows Media player Visualization directory (C:\Program Files\Windows Media Player\Visualizations). +
  2. Register the DLL with COM by calling "regsvr32 sauron.dll" in command shell. +
  3. Start the Windows Media player and click on "Now Playing". +
  4. Right click on the Visualization window and you will see a menu item called "Sauron". +
  5. Choose either Firefly Bars or Firefly Flash and play some music. +
  6. You will see the mouse light dancing to the tune of the music. +
  7. You can unregister the DLL by calling "regsvr32 -u sauron.dll". +
+ +

+

Top of page

+ + + +
+

+ +

© Microsoft Corporation 2007

+ \ No newline at end of file diff --git a/hid/firefly/firefly.sln b/hid/firefly/firefly.sln new file mode 100644 index 000000000..b78d86c85 --- /dev/null +++ b/hid/firefly/firefly.sln @@ -0,0 +1,78 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Lib", "Lib", "{4F25A8F2-2B4F-49C5-AA10-51610C5E6F1D}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "App", "App", "{E194EF42-DCCA-45F0-8C95-BCA0BF229BEB}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Driver", "Driver", "{C86EDC2A-0353-4B65-9429-5A7161C37728}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sauron", "Sauron", "{623654E2-2950-4FD1-A2DB-C16EABC46175}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Luminous", "lib\Luminous.vcxproj", "{B77AD939-7683-4EB8-A724-319959963AE2}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "flicker", "app\flicker.vcxproj", "{BC8A0B60-3D51-403C-838F-DBDB7FDB2154}" + ProjectSection(ProjectDependencies) = postProject + {B77AD939-7683-4EB8-A724-319959963AE2} = {B77AD939-7683-4EB8-A724-319959963AE2} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "firefly", "driver\firefly.vcxproj", "{72EF0AC2-AF89-4FAF-B50B-CFD1CBCFC1FA}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SAURON", "sauron\SAURON.vcxproj", "{4CAAD2A0-0151-45F6-A742-0591E33070ED}" + ProjectSection(ProjectDependencies) = postProject + {B77AD939-7683-4EB8-A724-319959963AE2} = {B77AD939-7683-4EB8-A724-319959963AE2} + 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 + {B77AD939-7683-4EB8-A724-319959963AE2}.Debug|Win32.ActiveCfg = Debug|Win32 + {B77AD939-7683-4EB8-A724-319959963AE2}.Debug|Win32.Build.0 = Debug|Win32 + {B77AD939-7683-4EB8-A724-319959963AE2}.Release|Win32.ActiveCfg = Release|Win32 + {B77AD939-7683-4EB8-A724-319959963AE2}.Release|Win32.Build.0 = Release|Win32 + {B77AD939-7683-4EB8-A724-319959963AE2}.Debug|x64.ActiveCfg = Debug|x64 + {B77AD939-7683-4EB8-A724-319959963AE2}.Debug|x64.Build.0 = Debug|x64 + {B77AD939-7683-4EB8-A724-319959963AE2}.Release|x64.ActiveCfg = Release|x64 + {B77AD939-7683-4EB8-A724-319959963AE2}.Release|x64.Build.0 = Release|x64 + {BC8A0B60-3D51-403C-838F-DBDB7FDB2154}.Debug|Win32.ActiveCfg = Debug|Win32 + {BC8A0B60-3D51-403C-838F-DBDB7FDB2154}.Debug|Win32.Build.0 = Debug|Win32 + {BC8A0B60-3D51-403C-838F-DBDB7FDB2154}.Release|Win32.ActiveCfg = Release|Win32 + {BC8A0B60-3D51-403C-838F-DBDB7FDB2154}.Release|Win32.Build.0 = Release|Win32 + {BC8A0B60-3D51-403C-838F-DBDB7FDB2154}.Debug|x64.ActiveCfg = Debug|x64 + {BC8A0B60-3D51-403C-838F-DBDB7FDB2154}.Debug|x64.Build.0 = Debug|x64 + {BC8A0B60-3D51-403C-838F-DBDB7FDB2154}.Release|x64.ActiveCfg = Release|x64 + {BC8A0B60-3D51-403C-838F-DBDB7FDB2154}.Release|x64.Build.0 = Release|x64 + {72EF0AC2-AF89-4FAF-B50B-CFD1CBCFC1FA}.Debug|Win32.ActiveCfg = Debug|Win32 + {72EF0AC2-AF89-4FAF-B50B-CFD1CBCFC1FA}.Debug|Win32.Build.0 = Debug|Win32 + {72EF0AC2-AF89-4FAF-B50B-CFD1CBCFC1FA}.Release|Win32.ActiveCfg = Release|Win32 + {72EF0AC2-AF89-4FAF-B50B-CFD1CBCFC1FA}.Release|Win32.Build.0 = Release|Win32 + {72EF0AC2-AF89-4FAF-B50B-CFD1CBCFC1FA}.Debug|x64.ActiveCfg = Debug|x64 + {72EF0AC2-AF89-4FAF-B50B-CFD1CBCFC1FA}.Debug|x64.Build.0 = Debug|x64 + {72EF0AC2-AF89-4FAF-B50B-CFD1CBCFC1FA}.Release|x64.ActiveCfg = Release|x64 + {72EF0AC2-AF89-4FAF-B50B-CFD1CBCFC1FA}.Release|x64.Build.0 = Release|x64 + {4CAAD2A0-0151-45F6-A742-0591E33070ED}.Debug|Win32.ActiveCfg = Debug|Win32 + {4CAAD2A0-0151-45F6-A742-0591E33070ED}.Debug|Win32.Build.0 = Debug|Win32 + {4CAAD2A0-0151-45F6-A742-0591E33070ED}.Release|Win32.ActiveCfg = Release|Win32 + {4CAAD2A0-0151-45F6-A742-0591E33070ED}.Release|Win32.Build.0 = Release|Win32 + {4CAAD2A0-0151-45F6-A742-0591E33070ED}.Debug|x64.ActiveCfg = Debug|x64 + {4CAAD2A0-0151-45F6-A742-0591E33070ED}.Debug|x64.Build.0 = Debug|x64 + {4CAAD2A0-0151-45F6-A742-0591E33070ED}.Release|x64.ActiveCfg = Release|x64 + {4CAAD2A0-0151-45F6-A742-0591E33070ED}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {B77AD939-7683-4EB8-A724-319959963AE2} = {4F25A8F2-2B4F-49C5-AA10-51610C5E6F1D} + {BC8A0B60-3D51-403C-838F-DBDB7FDB2154} = {E194EF42-DCCA-45F0-8C95-BCA0BF229BEB} + {72EF0AC2-AF89-4FAF-B50B-CFD1CBCFC1FA} = {C86EDC2A-0353-4B65-9429-5A7161C37728} + {4CAAD2A0-0151-45F6-A742-0591E33070ED} = {623654E2-2950-4FD1-A2DB-C16EABC46175} + EndGlobalSection +EndGlobal diff --git a/hid/firefly/lib/Luminous.vcxproj b/hid/firefly/lib/Luminous.vcxproj new file mode 100644 index 000000000..4d15bee0d --- /dev/null +++ b/hid/firefly/lib/Luminous.vcxproj @@ -0,0 +1,190 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {B77AD939-7683-4EB8-A724-319959963AE2} + $(MSBuildProjectName) + Debug + Win32 + {AD6E90BF-22BC-4775-968A-CF709A0ACA6E} + + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + StaticLibrary + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + StaticLibrary + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + StaticLibrary + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + StaticLibrary + + + + $(IntDir) + + + + + + + + + + + + + + + + Luminous + + + Luminous + + + Luminous + + + Luminous + + + + true + Level4 + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);..\shared + + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);..\shared + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);..\shared + + + %(AdditionalDependencies);ole32.lib;oleaut32.lib + + + + + true + Level4 + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);..\shared + + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);..\shared + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);..\shared + + + %(AdditionalDependencies);ole32.lib;oleaut32.lib + + + + + true + Level4 + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);..\shared + + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);..\shared + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);..\shared + + + %(AdditionalDependencies);ole32.lib;oleaut32.lib + + + + + true + Level4 + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);..\shared + + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);..\shared + + + %(PreprocessorDefinitions);UNICODE;_UNICODE + %(AdditionalIncludeDirectories);..\shared + + + %(AdditionalDependencies);ole32.lib;oleaut32.lib + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/hid/firefly/lib/Luminous.vcxproj.Filters b/hid/firefly/lib/Luminous.vcxproj.Filters new file mode 100644 index 000000000..5214e4a62 --- /dev/null +++ b/hid/firefly/lib/Luminous.vcxproj.Filters @@ -0,0 +1,22 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {5E1EEE07-49F3-4A37-99A5-404AA140C949} + + + h;hpp;hxx;hm;inl;inc;xsd + {927CE0D7-A6F6-406C-A38A-4B579C4B31AA} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {40EDECEC-06EE-4C46-8048-46A257FF4852} + + + + + Source Files + + + \ No newline at end of file diff --git a/hid/firefly/lib/luminous.cpp b/hid/firefly/lib/luminous.cpp new file mode 100644 index 000000000..467453ad0 --- /dev/null +++ b/hid/firefly/lib/luminous.cpp @@ -0,0 +1,509 @@ +/*++ + +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: + + Luminous.c + +Abstract: + + Library for managing the state of all firefly devices + +Environment: + + User Mode library + +--*/ +#include "luminous.h" + +CLuminous::CLuminous( + VOID + ) +{ + m_pIWbemServices = NULL; + m_pIWbemClassObject = NULL; + m_bCOMInitialized = FALSE; +} + + +CLuminous::~CLuminous( + VOID + ) +{ + Close(); +} + + +BOOL +CLuminous::Open( + VOID + ) +{ + HRESULT hResult; + BOOL bRet = FALSE; + + if (m_pIWbemServices) { + + return TRUE; + } + + // + // Initialize COM library. Must be done before invoking any + // other COM function. + // + + hResult = CoInitialize( NULL ); + + if ( FAILED (hResult)) { + _tprintf( TEXT("Error %lx: Failed to initialize COM library\n"), hResult ); + goto OpenCleanup; + } + + m_bCOMInitialized = TRUE; + + m_pIWbemServices = ConnectToNamespace( NAME_SPACE); + if (!m_pIWbemServices ) { + _tprintf( TEXT("Could not connect name.\n") ); + goto OpenCleanup; + } + + m_pIWbemClassObject = GetInstanceReference( m_pIWbemServices, CLASS_NAME); + if ( !m_pIWbemClassObject ) { + _tprintf( TEXT("Could not find the instance.\n") ); + goto OpenCleanup; + } + + bRet = TRUE; + +OpenCleanup: + if(!bRet) { + if(m_pIWbemClassObject) { + m_pIWbemClassObject->Release(); + m_pIWbemClassObject = NULL; + } + if(m_pIWbemServices) { + m_pIWbemServices->Release(); + m_pIWbemServices = NULL; + } + if(m_bCOMInitialized) { + CoUninitialize(); + m_bCOMInitialized = FALSE; + } + } + + return bRet; +} + +VOID +CLuminous::Close( + VOID + ) +{ + if (m_pIWbemServices) { + m_pIWbemServices->Release(); + m_pIWbemServices = NULL; + } + + if (m_pIWbemClassObject) { + m_pIWbemClassObject->Release(); + m_pIWbemClassObject = NULL; + } + + if(m_bCOMInitialized) { + CoUninitialize(); + m_bCOMInitialized = FALSE; + } + +} + + +BOOL +CLuminous::Get( + _In_ BOOL *Enabled + ) +{ + + VARIANT varPropVal; + BSTR bstrPropertyName = NULL; + HRESULT hResult; + CIMTYPE cimType; + BOOL bRet= FALSE; + + if (!m_pIWbemServices || !m_pIWbemClassObject) { + return FALSE; + } + + VariantInit( &varPropVal ); + + bstrPropertyName = AnsiToBstr( PROPERTY_NAME, -1 ); + + if ( !bstrPropertyName ) { + _tprintf( TEXT("Error out of memory.\n") ); + VariantClear( &varPropVal ); + return FALSE ; + } + + // + // Get the property value. + // + + hResult = m_pIWbemClassObject->Get( + bstrPropertyName, + 0, + &varPropVal, + &cimType, + NULL ); + + if ( hResult != WBEM_S_NO_ERROR ) { + + _tprintf( TEXT("Error %lX: Failed to read property value of %s.\n"), + hResult, PROPERTY_NAME); + + } else { + if(varPropVal.vt == VT_BOOL) { + *Enabled = varPropVal.boolVal; + bRet = TRUE; + } + } + + if(bstrPropertyName) { + SysFreeString( bstrPropertyName ); + } + + VariantClear( &varPropVal ); + + return bRet; + +} + +BOOL +CLuminous::Set( + _In_ BOOL Enabled + ) +{ + VARIANT varPropVal; + BSTR bstrPropertyName = NULL; + HRESULT hResult; + CIMTYPE cimType; + BOOL bRet = FALSE; + LPTSTR lpProperty = PROPERTY_NAME; + + if (!m_pIWbemServices || !m_pIWbemClassObject) { + return FALSE; + } + + VariantInit( &varPropVal ); + + bstrPropertyName = AnsiToBstr( lpProperty, -1 ); + + if ( !bstrPropertyName ) { + _tprintf( TEXT("Error out of memory.\n") ); + goto End ; + } + + // + // Get the property value. + // + + hResult = m_pIWbemClassObject->Get( + bstrPropertyName, + 0, + &varPropVal, + &cimType, + NULL ); + + if ( hResult != WBEM_S_NO_ERROR ) { + + _tprintf( TEXT("Error %lX: Failed to read property value of %s.\n"), + hResult, lpProperty ); + goto End; + + } + + if(varPropVal.vt == VT_BOOL) { + varPropVal.boolVal = (VARIANT_BOOL)Enabled; + + // + // Set the property value + // + + hResult = m_pIWbemClassObject->Put( + bstrPropertyName, + 0, + &varPropVal, + cimType + ); + + + if ( hResult == WBEM_S_NO_ERROR ) { + hResult = m_pIWbemServices->PutInstance( + m_pIWbemClassObject, + WBEM_FLAG_UPDATE_ONLY, + NULL, + NULL ); + + if ( hResult != WBEM_S_NO_ERROR ) { + _tprintf( TEXT("Failed to save the instance,") + TEXT(" %s will not be updated.\n"), + lpProperty ); + } else { + bRet = TRUE; + } + } + else { + + _tprintf( TEXT("Error %lX: Failed to set property value of %s.\n"), + hResult, lpProperty ); + } + } + +End: + + if(bstrPropertyName) { + SysFreeString( bstrPropertyName ); + } + + VariantClear( &varPropVal ); + + return bRet; +} + +// +// The function connects to the namespace specified by the user. +// + +IWbemServices *ConnectToNamespace (_In_ LPTSTR chNamespace) +{ + IWbemServices *pIWbemServices = NULL; + IWbemLocator *pIWbemLocator = NULL; + BSTR bstrNamespace; + HRESULT hResult; + + // + // Create an instance of WbemLocator interface. + // + + hResult = CoCreateInstance( + CLSID_WbemLocator, + NULL, + CLSCTX_INPROC_SERVER, + IID_IWbemLocator, + (LPVOID *)&pIWbemLocator ); + + if ( hResult != S_OK ) { + _tprintf( TEXT("Error %lX: Could not create instance of IWbemLocator interface.\n"), + hResult ); + return NULL; + } + + // + // Namespaces are passed to COM in BSTRs. + // + + bstrNamespace = AnsiToBstr( chNamespace, -1 ); + + if ( !bstrNamespace ) { + _tprintf( TEXT("Out of memory.\n") ); + pIWbemLocator->Release( ); + return NULL; + } + + // + // Using the locator, connect to COM in the given namespace. + // + + hResult = pIWbemLocator->ConnectServer( + bstrNamespace, + NULL, // NULL means current account, for simplicity. + NULL, // NULL means current password, for simplicity. + 0L, // locale + 0L, // securityFlags + NULL, // authority (domain for NTLM) + NULL, // context + &pIWbemServices ); // Returned IWbemServices. + + // + // Done with Namespace. + // + + SysFreeString( bstrNamespace ); + + if ( hResult != WBEM_S_NO_ERROR) { + _tprintf( TEXT("Error %lX: Failed to connect to namespace %s.\n"), + hResult, chNamespace ); + + pIWbemLocator->Release( ); + return NULL; + } + + // + // Switch the security level to IMPERSONATE so that provider(s) + // will grant access to system-level objects, and so that + // CALL authorization will be used. + // + + hResult = CoSetProxyBlanket( + (IUnknown *)pIWbemServices, // proxy + RPC_C_AUTHN_WINNT, // authentication service + RPC_C_AUTHZ_NONE, // authorization service + NULL, // server principle name + RPC_C_AUTHN_LEVEL_CALL, // authentication level + RPC_C_IMP_LEVEL_IMPERSONATE, // impersonation level + NULL, // identity of the client + 0 ); // capability flags + + if ( hResult != S_OK ) { + _tprintf( TEXT("Error %lX: Failed to impersonate.\n"), hResult ); + + pIWbemLocator->Release(); + pIWbemServices->Release(); + return NULL; + } + + pIWbemLocator->Release(); + return pIWbemServices; +} + +// +// The function returns an interface pointer to the instance given its +// list-index. +// + +IWbemClassObject *GetInstanceReference ( + IWbemServices *pIWbemServices, + _In_ LPTSTR lpClassName) +{ + IWbemClassObject *pInst = NULL; + IEnumWbemClassObject *pEnumInst; + BSTR bstrClassName; + BOOL bFound; + ULONG ulCount; + HRESULT hResult; + + + bstrClassName = AnsiToBstr( lpClassName, -1 ); + + if ( !bstrClassName ) { + _tprintf( TEXT("Out of memory.\n") ); + return NULL; + } + + // + // Get Instance Enumerator Interface. + // + + pEnumInst = NULL; + + hResult = pIWbemServices->CreateInstanceEnum( + bstrClassName, // Name of the root class. + WBEM_FLAG_SHALLOW | // Enumerate at current root only. + WBEM_FLAG_FORWARD_ONLY, // Forward-only enumeration. + NULL, // Context. + &pEnumInst ); // pointer to class enumerator + + if ( hResult != WBEM_S_NO_ERROR || pEnumInst == NULL ) { + _tprintf( TEXT("Error %lX: Failed to get a reference") + TEXT(" to instance enumerator.\n"), hResult ); + } + else { + // + // Get pointer to the instance. + // + + hResult = WBEM_S_NO_ERROR; + bFound = FALSE; + + while ( (hResult == WBEM_S_NO_ERROR) && (bFound == FALSE) ) { + + hResult = pEnumInst->Next( + 2000, // two seconds timeout + 1, // return just one instance. + &pInst, // pointer to instance. + &ulCount); // Number of instances returned. + + if ( ulCount > 0 ) { + + bFound = TRUE; + break; + } + } + + if ( bFound == FALSE && pInst) { + pInst->Release( ); + pInst = NULL; + } + } + + // + // Done with the instance enumerator. + // + + if(pEnumInst) { + pEnumInst->Release( ); + } + SysFreeString( bstrClassName ); + + return pInst; +} + +// +// The function converts an ANSI string into BSTR and returns it in an +// allocated memory. The memory must be freed by the caller using free() +// function. If nLenSrc is -1, the string is null terminated. +// + +BSTR AnsiToBstr (_In_ LPTSTR lpSrc, _In_ int nLenSrc) +{ + BSTR lpDest; + + // + // In case of ANSI version, we need to change the ANSI string to UNICODE since + // BSTRs are essentially UNICODE strings. + // + +#ifndef UNICODE + int nLenDest; + + nLenDest = MultiByteToWideChar( CP_ACP, 0, lpSrc, nLenSrc, NULL, 0); + + lpDest = SysAllocStringLen( NULL, nLenDest ); + + if ( lpDest ) { + MultiByteToWideChar( CP_ACP, 0, lpSrc, nLenSrc, lpDest, nLenDest ); + } + + // + // In case of UNICODE version, we simply allocate memory and copy the string. + // + +#else + if ( lpSrc == NULL ) { + nLenSrc = 0; + } + else { + if ( nLenSrc == - 1 ) { + size_t temp = _tcslen( lpSrc ); + if (temp > INT_MAX - 1) { + return NULL; + } + nLenSrc = (int) temp + 1; + } + } + + lpDest = SysAllocStringLen( lpSrc, nLenSrc ); +#endif + + return lpDest; +} + + + + + diff --git a/hid/firefly/sauron/SAURON.vcxproj b/hid/firefly/sauron/SAURON.vcxproj new file mode 100644 index 000000000..1c915a7c9 --- /dev/null +++ b/hid/firefly/sauron/SAURON.vcxproj @@ -0,0 +1,302 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {4CAAD2A0-0151-45F6-A742-0591E33070ED} + $(MSBuildProjectName) + Debug + Win32 + {4E744468-3511-4BC0-9924-62253F1EA525} + + + + 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) + + + + + + + + + + + + + + + + SAURON + .DLL + $(OutDir)$(TargetName)$(TargetExt) + + + SAURON + .DLL + $(OutDir)$(TargetName)$(TargetExt) + + + SAURON + .DLL + $(OutDir)$(TargetName)$(TargetExt) + + + SAURON + .DLL + $(OutDir)$(TargetName)$(TargetExt) + + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + %(AdditionalIncludeDirectories);..\shared + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(AdditionalIncludeDirectories);..\shared + true + Level4 + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(AdditionalIncludeDirectories);..\shared + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(AdditionalDependencies);advapi32.lib;comctl32.lib;kernel32.lib;ole32.lib;oleaut32.lib;user32.lib;gdi32.lib;uuid.lib;wbemuuid.lib;.\..\lib\$(IntDir)\luminous.lib + + + + + %(AdditionalIncludeDirectories);..\shared + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(AdditionalIncludeDirectories);..\shared + true + Level4 + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(AdditionalIncludeDirectories);..\shared + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(AdditionalDependencies);advapi32.lib;comctl32.lib;kernel32.lib;ole32.lib;oleaut32.lib;user32.lib;gdi32.lib;uuid.lib;wbemuuid.lib;.\..\lib\$(IntDir)\luminous.lib + + + + + %(AdditionalIncludeDirectories);..\shared + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(AdditionalIncludeDirectories);..\shared + true + Level4 + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(AdditionalIncludeDirectories);..\shared + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(AdditionalDependencies);advapi32.lib;comctl32.lib;kernel32.lib;ole32.lib;oleaut32.lib;user32.lib;gdi32.lib;uuid.lib;wbemuuid.lib;.\..\lib\$(IntDir)\luminous.lib + + + + + %(AdditionalIncludeDirectories);..\shared + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(AdditionalIncludeDirectories);..\shared + true + Level4 + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(AdditionalIncludeDirectories);..\shared + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(AdditionalDependencies);advapi32.lib;comctl32.lib;kernel32.lib;ole32.lib;oleaut32.lib;user32.lib;gdi32.lib;uuid.lib;wbemuuid.lib;.\..\lib\$(IntDir)\luminous.lib + + + + + %(AdditionalOptions) -n + + + + + %(AdditionalOptions) -n + + + + + %(AdditionalOptions) -n + + + + + %(AdditionalOptions) -n + + + + Static + + + Static + + + Static + + + Static + + + + Sync + true + + + SauronDll.def + + + + + Sync + true + + + SauronDll.def + + + + + Sync + true + + + SauronDll.def + + + + + Sync + true + + + SauronDll.def + + + + + ;%(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/hid/firefly/sauron/SAURON.vcxproj.Filters b/hid/firefly/sauron/SAURON.vcxproj.Filters new file mode 100644 index 000000000..4bdee15c2 --- /dev/null +++ b/hid/firefly/sauron/SAURON.vcxproj.Filters @@ -0,0 +1,42 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {FA3DB6F7-580F-4E8E-93FB-E6DA0E745578} + + + h;hpp;hxx;hm;inl;inc;xsd + {CE2E7ACF-15AF-4CB2-ADE3-FE868EF27CAE} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {DD3A68F1-5287-4D8B-A890-AC37234B2BEC} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/hid/firefly/sauron/Sauron.cpp b/hid/firefly/sauron/Sauron.cpp new file mode 100644 index 000000000..16cb12d4c --- /dev/null +++ b/hid/firefly/sauron/Sauron.cpp @@ -0,0 +1,538 @@ +/*++ + +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: Sauron.cpp + + +Abstract: Implementation of CSauron, Windows Media Player Visualization + that communicates with Firefly to blink the mouse light to + the beat of music. + + +Environment: + + User mode only. + +--*/ + +#include "stdafx.h" +#include "iSauron.h" +#include "Sauron.h" +#include "luminous.h" +#include + +///////////////////////////////////////////////////////////////////////////// +// CSauron::CSauron +// Constructor + +CSauron::CSauron() : +m_clrForeground(0x0000FF), +m_nPreset(0) +{ + m_Luminous = NULL; +} + +///////////////////////////////////////////////////////////////////////////// +// CSauron::~CSauron +// Destructor + +CSauron::~CSauron() +{ + if (m_Luminous) { + + delete m_Luminous; + m_Luminous = NULL; + } +} + +///////////////////////////////////////////////////////////////////////////// +// CSauron:::FinalConstruct +// Called when an effect is first loaded. Use this function to do one-time +// intializations that could fail (i.e. creating offscreen buffers) instead +// of doing this in the constructor, which cannot return an error. + +HRESULT CSauron::FinalConstruct() +{ + if (m_Luminous == NULL) { + + m_Luminous = new CLuminous(); + + if (m_Luminous) { + + m_Luminous->Open(); + } + } + + return S_OK; +} + +///////////////////////////////////////////////////////////////////////////// +// CSauron:::FinalRelease +// Called when an effect is unloaded. Use this function to free any +// resources allocated in FinalConstruct. + +void CSauron::FinalRelease() +{ + if (m_Luminous) { + + m_Luminous->Set(TRUE); + m_Luminous->Close(); + + delete m_Luminous; + + m_Luminous = NULL; + } +} + + +////////////////////////////////////////////////////////////////////////////// +// CSauron::Render +// Called when an effect should render itself to the screen. +////////////////////////////////////////////////////////////////////////////// +STDMETHODIMP CSauron::Render(TimedLevel *pLevels, HDC hdc, RECT *prc) +{ + // Fill background with black + HBRUSH hWipeBrush = ::CreateSolidBrush( 0 ); + HBRUSH hFillBrush = ::CreateSolidBrush( m_clrForeground ); + HPEN hNewPen = ::CreatePen( PS_SOLID, 0, m_clrForeground ); + HPEN hOldPen = static_cast(::SelectObject( hdc, hNewPen )); + BOOL setLight; + int vertical, left; + int right = 0; + int chunkSize = 3; + int c, barWidth; + int x, y; + float barWidthFloat, barIndex, barCount; + RECT rect; + + // draw using the current preset + switch (m_nPreset) + { + case PRESET_BARS: + case PRESET_FLASH: + { + setLight = FALSE; + + // + // Compute average of 20-80Hz band + // + vertical = 0; + for(c = 0; c < chunkSize; c++) { + + vertical += pLevels->frequency[0][c]; + } + vertical /= chunkSize; + + // + // If it passes our threshold, check the upper bands as well + // + if (vertical > 170) { + + // + // Take the average from 21090 to 22050Hz + // + vertical = 0; + for(c = 0; c < chunkSize; c++) { + + vertical += pLevels->frequency[0][SA_BUFFER_SIZE - c - 1]; + } + vertical /= chunkSize; + if (vertical < 10) { + + // + // No high frequency noise, call it a beat + // + setLight = TRUE; + } + } + + if (m_Luminous) { + + m_Luminous->Set(setLight); + } + + if (m_nPreset == PRESET_FLASH) { + + if (setLight) { + + ::FillRect( hdc, prc, hFillBrush ); + + } else { + + ::FillRect( hdc, prc, hWipeBrush ); + } + + } else { + + // + // Walk through the frequencies until we run out of levels or drawing surface. + // + x = 0; + barWidthFloat = 1.0; + barWidth = 1; + barIndex = 0.0; + barCount = 75.0; + for(x=0; x < SA_BUFFER_SIZE; x += barWidth) { + + barWidth = (int) (((float) barWidth) * barWidthFloat); + barWidthFloat *= (float) 1.011; + + vertical = 0; + for(c=0; c= SA_BUFFER_SIZE) { + + break; + } + + if (pLevels->frequency[0][x+c] > vertical) { + + vertical = pLevels->frequency[0][x+c]; + } + } + + y = static_cast(((prc->bottom - prc->top)/256.0f) * vertical); + + left = (int) (((prc->right - prc->left) * barIndex / barCount) + (float) prc->left); + right = (int) (((prc->right - prc->left) * (barIndex+1) / barCount) + (float) (prc->left - 1)); + + ::SetRect( + &rect, + left, + prc->top, + right, + prc->bottom - y + ); + + ::FillRect( hdc, &rect, hWipeBrush ); + + ::SetRect( + &rect, + right, + prc->top, + right+1, + prc->bottom + ); + + ::FillRect( hdc, &rect, hWipeBrush ); + + ::SetRect( + &rect, + left, + prc->bottom - y, + right, + prc->bottom + ); + + ::FillRect( hdc, &rect, hFillBrush ); + + barIndex += 1.0; + } + + ::SetRect( + &rect, + right, + prc->top, + prc->right, + prc->bottom + ); + + ::FillRect( hdc, &rect, hWipeBrush ); + } + } + break; + } + + if ( hWipeBrush ) + { + ::DeleteObject( hWipeBrush ); + } + + if ( hFillBrush ) + { + ::DeleteObject( hFillBrush ); + } + + if (hNewPen) + { + ::SelectObject( hdc, hOldPen ); + ::DeleteObject( hNewPen ); + } + + return S_OK; +} + +////////////////////////////////////////////////////////////////////////////// +// CSauron::MediaInfo +// Everytime new media is loaded, this method is called to pass the +// number of channels (mono/stereo), the sample rate of the media, and the +// title of the media +////////////////////////////////////////////////////////////////////////////// +STDMETHODIMP CSauron::MediaInfo(LONG /* lChannelCount */, + LONG /* lSampleRate */, + BSTR /* bstrTitle */ ) +{ + return S_OK; +} + + +////////////////////////////////////////////////////////////////////////////// +// CSauron::GetCapabilities +// Returns the capabilities of this effect. Flags that can be returned are: +// EFFECT_CANGOFULLSCREEN -- effect supports full-screen rendering +// EFFECT_HASPROPERTYPAGE -- effect supports a property page +////////////////////////////////////////////////////////////////////////////// +STDMETHODIMP CSauron::GetCapabilities(DWORD * pdwCapabilities) +{ + if (NULL == pdwCapabilities) + { + return E_POINTER; + } + + *pdwCapabilities = 0; + return S_OK; +} + +////////////////////////////////////////////////////////////////////////////// +// CSauron::GetTitle +// Invoked when a host wants to obtain the title of the effect +////////////////////////////////////////////////////////////////////////////// +STDMETHODIMP CSauron::GetTitle(BSTR* bstrTitle) +{ + USES_CONVERSION; + + if (NULL == bstrTitle) + { + return E_POINTER; + } + + CComBSTR bstrTemp; + bstrTemp.LoadString(IDS_EFFECTNAME); + + if ((!bstrTemp) || (0 == bstrTemp.Length())) + { + return E_FAIL; + } + + *bstrTitle = bstrTemp.Detach(); + + return S_OK; +} + +////////////////////////////////////////////////////////////////////////////// +// CSauron::GetPresetTitle +// Invoked when a host wants to obtain the title of the given preset +////////////////////////////////////////////////////////////////////////////// +STDMETHODIMP CSauron::GetPresetTitle(LONG nPreset, BSTR *bstrPresetTitle) +{ + USES_CONVERSION; + + if (NULL == bstrPresetTitle) + { + return E_POINTER; + } + + if ((nPreset < 0) || (nPreset >= PRESET_COUNT)) + { + return E_INVALIDARG; + } + + CComBSTR bstrTemp; + + switch (nPreset) + { + case PRESET_BARS: + bstrTemp.LoadString(IDS_BARSPRESETNAME); + break; + case PRESET_FLASH: + bstrTemp.LoadString(IDS_FLASHPRESETNAME); + break; + } + + if ((!bstrTemp) || (0 == bstrTemp.Length())) + { + return E_FAIL; + } + + *bstrPresetTitle = bstrTemp.Detach(); + + return S_OK; +} + +////////////////////////////////////////////////////////////////////////////// +// CSauron::GetPresetCount +// Invoked when a host wants to obtain the number of supported presets +////////////////////////////////////////////////////////////////////////////// +STDMETHODIMP CSauron::GetPresetCount(LONG *pnPresetCount) +{ + if (NULL == pnPresetCount) + { + return E_POINTER; + } + + *pnPresetCount = PRESET_COUNT; + + return (S_OK); +} + +////////////////////////////////////////////////////////////////////////////// +// CSauron::SetCurrentPreset +// Invoked when a host wants to change the index of the current preset +////////////////////////////////////////////////////////////////////////////// +STDMETHODIMP CSauron::SetCurrentPreset(LONG nPreset) +{ + if ((nPreset < 0) || (nPreset >= PRESET_COUNT)) + { + return E_INVALIDARG; + } + + m_nPreset = nPreset; + + return (S_OK); +} + +////////////////////////////////////////////////////////////////////////////// +// CSauron::GetCurrentPreset +// Invoked when a host wants to obtain the index of the current preset +////////////////////////////////////////////////////////////////////////////// +STDMETHODIMP CSauron::GetCurrentPreset(LONG *pnPreset) +{ + if (NULL == pnPreset) + { + return E_POINTER; + } + + *pnPreset = m_nPreset; + + return (S_OK); +} + +////////////////////////////////////////////////////////////////////////////// +// CSauron::get_foregroundColor +// Property get to retrieve the foregroundColor prop via the public interface. +////////////////////////////////////////////////////////////////////////////// +STDMETHODIMP CSauron::get_foregroundColor(BSTR *pVal) +{ + return ColorToWz( pVal, m_clrForeground); +} + + +////////////////////////////////////////////////////////////////////////////// +// CSauron::put_foregroundColor +// Property put to set the foregroundColor prop via the public interface. +////////////////////////////////////////////////////////////////////////////// +STDMETHODIMP CSauron::put_foregroundColor(BSTR newVal) +{ + return WzToColor(newVal, &m_clrForeground); +} + + +////////////////////////////////////////////////////////////////////////////// +// CSauron::WzToColor +// Helper function used to convert a string into a COLORREF. +////////////////////////////////////////////////////////////////////////////// +HRESULT CSauron::WzToColor(const WCHAR *pwszColor, COLORREF *pcrColor) +{ + if (NULL == pwszColor) + { + //NULL color string passed in + return E_POINTER; + } + + if (0 == lstrlenW(pwszColor)) + { + //Empty color string passed in + return E_INVALIDARG; + } + + if (NULL == pcrColor) + { + //NULL output color DWORD passed in + return E_POINTER; + } + + if (lstrlenW(pwszColor) != 7) + { + //hex color string is not of the correct length + return E_INVALIDARG; + } + + DWORD dwRet = 0; + for (int i = 1; i < 7; i++) + { + // shift dwRet by 4 + dwRet <<= 4; + // and add in the value of this string + + if ((pwszColor[i] >= L'0') && (pwszColor[i] <= L'9')) + { + dwRet += pwszColor[i] - '0'; + } + else if ((pwszColor[i] >= L'A') && (pwszColor[i] <= L'F')) + { + dwRet += 10 + (pwszColor[i] - L'A'); + } + else if ((pwszColor[i] >= L'a') && (pwszColor[i] <= L'f')) + { + dwRet += 10 + (pwszColor[i] - L'a'); + } + else + { + //Invalid hex digit in color string + return E_INVALIDARG; + } + } + + *pcrColor = SwapBytes(dwRet); + + return S_OK; +} + + +////////////////////////////////////////////////////////////////////////////// +// CSauron::ColorToWz +// Helper function used to convert a COLORREF to a BSTR. +////////////////////////////////////////////////////////////////////////////// +HRESULT CSauron::ColorToWz( BSTR* pbstrColor, COLORREF crColor) +{ + _ASSERT( NULL != pbstrColor ); + _ASSERT( (crColor & 0x00FFFFFF) == crColor ); + + *pbstrColor = NULL; + + WCHAR wsz[8]; + HRESULT hr = S_OK; + + hr = StringCbPrintfW( wsz, sizeof(wsz), L"#%06X", SwapBytes(crColor) ); + if (FAILED(hr)) { + return hr; + } + + *pbstrColor = ::SysAllocString( wsz ); + + if (!pbstrColor) + { + hr = E_FAIL; + } + + return hr; +} + + +////////////////////////////////////////////////////////////////////////////// +// CSauron::SwapBytes +// Used to convert between a DWORD and COLORREF. Simply swaps the lowest +// and 3rd order bytes. +////////////////////////////////////////////////////////////////////////////// +inline DWORD CSauron::SwapBytes(DWORD dwRet) +{ + return ((dwRet & 0x0000FF00) | ((dwRet & 0x00FF0000) >> 16) | ((dwRet & 0x000000FF) << 16)); +} + + diff --git a/hid/firefly/sauron/Sauron.h b/hid/firefly/sauron/Sauron.h new file mode 100644 index 000000000..4760d10a1 --- /dev/null +++ b/hid/firefly/sauron/Sauron.h @@ -0,0 +1,76 @@ +///////////////////////////////////////////////////////////////////////////// +// +// Sauron.h : Declaration of the CSauron +// +///////////////////////////////////////////////////////////////////////////// + +#ifndef __SAURON_H_ +#define __SAURON_H_ + +#include "resource.h" +#include "effects.h" +#include "luminous.h" + +// preset values +enum { + PRESET_BARS = 0, + PRESET_FLASH, + PRESET_COUNT +}; + +///////////////////////////////////////////////////////////////////////////// +// CSauron +class ATL_NO_VTABLE CSauron : + public CComObjectRootEx, + public CComCoClass, + public IDispatchImpl, + public IWMPEffects +{ +private: + COLORREF m_clrForeground; // foreground color + LONG m_nPreset; // current preset + CLuminous *m_Luminous; // light control + + HRESULT WzToColor(const WCHAR *pwszColor, COLORREF *pcrColor); + HRESULT ColorToWz( BSTR* pbstrColor, COLORREF crColor); + DWORD SwapBytes(DWORD dwRet); + +public: + CSauron(); + ~CSauron(); + +DECLARE_REGISTRY_RESOURCEID(IDR_SAURON) + +DECLARE_PROTECT_FINAL_CONSTRUCT() + +BEGIN_COM_MAP(CSauron) + COM_INTERFACE_ENTRY(ISauron) + COM_INTERFACE_ENTRY(IDispatch) + COM_INTERFACE_ENTRY(IWMPEffects) +END_COM_MAP() + +public: + + // CComCoClass Overrides + HRESULT FinalConstruct(); + void FinalRelease(); + + // ISauron + STDMETHOD(get_foregroundColor)(/*[out, retval]*/ BSTR *pVal); + STDMETHOD(put_foregroundColor)(/*[in]*/ BSTR newVal); + + // IEffects + STDMETHOD(Render)(TimedLevel *pLevels, HDC hdc, RECT *rc); + STDMETHOD(MediaInfo)(LONG lChannelCount, LONG lSampleRate, BSTR bstrTitle); + STDMETHOD(GetCapabilities)(DWORD * pdwCapabilities); + STDMETHOD(GoFullscreen)(BOOL /* fFullScreen */) { return E_NOTIMPL; }; + STDMETHOD(RenderFullScreen)(TimedLevel * /* pLevels */) { return E_NOTIMPL; }; + STDMETHOD(DisplayPropertyPage)(HWND /* hwndOwner */) { return E_NOTIMPL; }; + STDMETHOD(GetTitle)(BSTR *bstrTitle); + STDMETHOD(GetPresetTitle)(LONG nPreset, BSTR *bstrPresetTitle); + STDMETHOD(GetPresetCount)(LONG *pnPresetCount); + STDMETHOD(SetCurrentPreset)(LONG nPreset); + STDMETHOD(GetCurrentPreset)(LONG *pnPreset); +}; + +#endif //__SAURON_H_ diff --git a/hid/firefly/sauron/Sauron.rgs b/hid/firefly/sauron/Sauron.rgs new file mode 100644 index 000000000..f5f50f0fa --- /dev/null +++ b/hid/firefly/sauron/Sauron.rgs @@ -0,0 +1,41 @@ +HKCR +{ + NoRemove CLSID + { + ForceRemove {C06687F6-D0F8-4627-A113-2654249C67F9} = s 'Sauron Class' + { + InprocServer32 = s '%MODULE%' + { + val ThreadingModel = s 'Apartment' + } + 'TypeLib' = s '{185A2923-9B3C-4816-BB77-1BDA42E35605}' + } + } +} +HKEY_LOCAL_MACHINE +{ + NoRemove SOFTWARE + { + NoRemove Microsoft + { + NoRemove MediaPlayer + { + NoRemove Objects + { + NoRemove Effects + { + ForceRemove Sauron + { + Properties + { + val classid = s '{C06687F6-D0F8-4627-A113-2654249C67F9}' + val name = s 'Sauron' + val description = s 'This collection includes the Sauron Bars and Sauron Wave effects.' + } + } + } + } + } + } + } +} diff --git a/hid/firefly/sauron/Saurondll.cpp b/hid/firefly/sauron/Saurondll.cpp new file mode 100644 index 000000000..a28842962 --- /dev/null +++ b/hid/firefly/sauron/Saurondll.cpp @@ -0,0 +1,71 @@ +// Saurondll.cpp : Implementation of DLL Exports. + + +// Note: Proxy/Stub Information +// To build a separate proxy/stub DLL, +// run nmake -f Saurondllps.mk in the project directory. + +#include "stdafx.h" +#include "resource.h" +#include +#include "iSauron.h" + +#include "iSauron_i.c" +#include "Sauron.h" + + +CComModule _Module; + +BEGIN_OBJECT_MAP(ObjectMap) +OBJECT_ENTRY(CLSID_Sauron, CSauron) +END_OBJECT_MAP() + +///////////////////////////////////////////////////////////////////////////// +// DLL Entry Point + +extern "C" +BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpReserved*/) +{ + if (dwReason == DLL_PROCESS_ATTACH) + { + _Module.Init(ObjectMap, hInstance, &LIBID_SAURONLib); + DisableThreadLibraryCalls(hInstance); + } + else if (dwReason == DLL_PROCESS_DETACH) + _Module.Term(); + return TRUE; // ok +} + +///////////////////////////////////////////////////////////////////////////// +// Used to determine whether the DLL can be unloaded by OLE + +STDAPI DllCanUnloadNow(void) +{ + return (_Module.GetLockCount()==0) ? S_OK : S_FALSE; +} + +///////////////////////////////////////////////////////////////////////////// +// Returns a class factory to create an object of the requested type +STDAPI DllGetClassObject(_In_ REFCLSID rclsid, _In_ REFIID riid, _Outptr_ LPVOID* ppv) +{ + return _Module.GetClassObject(rclsid, riid, ppv); +} + +///////////////////////////////////////////////////////////////////////////// +// DllRegisterServer - Adds entries to the system registry + +STDAPI DllRegisterServer(void) +{ + // registers object, typelib and all interfaces in typelib + return _Module.RegisterServer(TRUE); +} + +///////////////////////////////////////////////////////////////////////////// +// DllUnregisterServer - Removes entries from the system registry + +STDAPI DllUnregisterServer(void) +{ + return _Module.UnregisterServer(TRUE); +} + + diff --git a/hid/firefly/sauron/Saurondll.def b/hid/firefly/sauron/Saurondll.def new file mode 100644 index 000000000..0dfb06c89 --- /dev/null +++ b/hid/firefly/sauron/Saurondll.def @@ -0,0 +1,9 @@ +; Sauron.def : Declares the module parameters. + +LIBRARY "SAURON.DLL" + +EXPORTS + DllCanUnloadNow PRIVATE + DllGetClassObject PRIVATE + DllRegisterServer PRIVATE + DllUnregisterServer PRIVATE diff --git a/hid/firefly/sauron/Saurondll.rc b/hid/firefly/sauron/Saurondll.rc new file mode 100644 index 000000000..1456d1c86 --- /dev/null +++ b/hid/firefly/sauron/Saurondll.rc @@ -0,0 +1,131 @@ +//Microsoft Developer Studio 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 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "1 TYPELIB ""iSauron.tlb""\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +#ifndef _MAC +///////////////////////////////////////////////////////////////////////////// +// +// 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 "040904b0" + BEGIN + VALUE "Comments", "\0" + VALUE "CompanyName", "Microsoft Corporation\0" + VALUE "FileDescription", "Sauron Module\0" + VALUE "FileVersion", "1, 0, 0, 1\0" + VALUE "InternalName", "Sauron\0" + VALUE "LegalCopyright", "Copyright 2002\0" + VALUE "LegalTrademarks", "\0" + VALUE "OLESelfRegister", "\0" + VALUE "OriginalFilename", "Sauron.dll\0" + VALUE "PrivateBuild", "\0" + VALUE "ProductName", "Sauron Module\0" + VALUE "ProductVersion", "1, 0, 0, 1\0" + VALUE "SpecialBuild", "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif // !_MAC + + +///////////////////////////////////////////////////////////////////////////// +// +// REGISTRY +// + +IDR_SAURON REGISTRY DISCARDABLE "Sauron.rgs" + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE DISCARDABLE +BEGIN + IDS_PROJNAME "Sauron" + IDS_EFFECTNAME "Sauron" + IDS_BARSPRESETNAME "Firefly Bars" + IDS_FLASHPRESETNAME "Firefly Flash" +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// +1 TYPELIB "iSauron.tlb" + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/hid/firefly/sauron/StdAfx.cpp b/hid/firefly/sauron/StdAfx.cpp new file mode 100644 index 000000000..a5eea178f --- /dev/null +++ b/hid/firefly/sauron/StdAfx.cpp @@ -0,0 +1,12 @@ +// stdafx.cpp : source file that includes just the standard includes +// stdafx.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +#ifdef _ATL_STATIC_REGISTRY +#include +#include +#endif + +#include diff --git a/hid/firefly/sauron/StdAfx.h b/hid/firefly/sauron/StdAfx.h new file mode 100644 index 000000000..df6259fc7 --- /dev/null +++ b/hid/firefly/sauron/StdAfx.h @@ -0,0 +1,28 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, +// but are changed infrequently + +#if !defined(AFX_STDAFX_H__970E5960_5209_4AE7_8DD2_ACD4C1165941__INCLUDED_) +#define AFX_STDAFX_H__970E5960_5209_4AE7_8DD2_ACD4C1165941__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#define STRICT +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0400 +#endif +#define _ATL_APARTMENT_THREADED + +#include +//You may derive a class from CComModule and use it if you want to override +//something, but do not change the name of _Module +extern CComModule _Module; +#include +#include // Added by ClassView + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_STDAFX_H__970E5960_5209_4AE7_8DD2_ACD4C1165941__INCLUDED) diff --git a/hid/firefly/sauron/effects.h b/hid/firefly/sauron/effects.h new file mode 100644 index 000000000..b868424de --- /dev/null +++ b/hid/firefly/sauron/effects.h @@ -0,0 +1,462 @@ + + +/* this ALWAYS GENERATED file contains the definitions for the interfaces */ + + + /* File created by MIDL compiler version 6.00.0361 */ +/* Compiler settings for effects.idl: + Oicf, W1, Zp8, env=Win32 (32b run) + protocol : dce , ms_ext, c_ext, robust + error checks: allocation ref bounds_check enum stub_data + VC __declspec() decoration level: + __declspec(uuid()), __declspec(selectany), __declspec(novtable) + DECLSPEC_UUID(), MIDL_INTERFACE() +*/ +//@@MIDL_FILE_HEADING( ) + +#pragma warning( disable: 4049 ) /* more than 64k source lines */ + + +/* verify that the version is high enough to compile this file*/ +#ifndef __REQUIRED_RPCNDR_H_VERSION__ +#define __REQUIRED_RPCNDR_H_VERSION__ 475 +#endif + +#include "rpc.h" +#include "rpcndr.h" + +#ifndef __RPCNDR_H_VERSION__ +#error this stub requires an updated version of +#endif // __RPCNDR_H_VERSION__ + +#ifndef COM_NO_WINDOWS_H +#include "windows.h" +#include "ole2.h" +#endif /*COM_NO_WINDOWS_H*/ + +#ifndef __effects_h__ +#define __effects_h__ + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +/* Forward Declarations */ + +#ifndef __IWMPEffects_FWD_DEFINED__ +#define __IWMPEffects_FWD_DEFINED__ +typedef interface IWMPEffects IWMPEffects; +#endif /* __IWMPEffects_FWD_DEFINED__ */ + + +/* header files for imported files */ +#include "oaidl.h" +#include "ocidl.h" + +#ifdef __cplusplus +extern "C"{ +#endif + +_Must_inspect_result_ +_Ret_maybenull_ _Post_writable_byte_size_(size) +void * __RPC_USER MIDL_user_allocate(size_t size); +void __RPC_USER MIDL_user_free(_Pre_maybenull_ _Post_invalid_ void * ); + +/* interface __MIDL_itf_effects_0000 */ +/* [local] */ + +#define EFFECT_CANGOFULLSCREEN ( 0x1 ) + +#define EFFECT_HASPROPERTYPAGE ( 0x2 ) + +#define EFFECT_VARIABLEFREQSTEP ( 0x4 ) + +#define SA_BUFFER_SIZE ( 1024 ) + + +enum PlayerState + { stop_state = 0, + pause_state = 1, + play_state = 2 + } ; + +//********************************************************************** +// Define the minimum and maximum frequency ranges returned in our +// TimedLevel frequency array (i.e. first index in TimedLevel.frequency +// is at 20Hz and last is at 22050Hz). +//********************************************************************** +const float kfltTimedLevelMaximumFrequency = 22050.0F; +const float kfltTimedLevelMinimumFrequency = 20.0F; + +/* + * FREQUENCY_INDEX() returns the index into TimedLevel.frequency[] where + * the specified frequency is located in the power spectrum + */ +#define FREQUENCY_INDEX(FREQ)\ + (int)(((FREQ) - kfltTimedLevelMinimumFrequency) /\ + (((kfltTimedLevelMaximumFrequency - kfltTimedLevelMinimumFrequency) / SA_BUFFER_SIZE))) + +typedef struct tagTimedLevel + { + unsigned char frequency[ 2 ][ 1024 ]; + unsigned char waveform[ 2 ][ 1024 ]; + int state; + hyper timeStamp; + } TimedLevel; + + + +extern RPC_IF_HANDLE __MIDL_itf_effects_0000_v0_0_c_ifspec; +extern RPC_IF_HANDLE __MIDL_itf_effects_0000_v0_0_s_ifspec; + +#ifndef __IWMPEffects_INTERFACE_DEFINED__ +#define __IWMPEffects_INTERFACE_DEFINED__ + +/* interface IWMPEffects */ +/* [unique][helpstring][uuid][object] */ + + +EXTERN_C const IID IID_IWMPEffects; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("D3984C13-C3CB-48e2-8BE5-5168340B4F35") + IWMPEffects : public IUnknown + { + public: + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE Render( + /* [in] */ TimedLevel *pLevels, + /* [in] */ HDC hdc, + /* [in] */ RECT *prc) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE MediaInfo( + /* [in] */ LONG lChannelCount, + /* [in] */ LONG lSampleRate, + /* [in] */ BSTR bstrTitle) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE GetCapabilities( + /* [out] */ DWORD *pdwCapabilities) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE GetTitle( + /* [out] */ BSTR *bstrTitle) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE GetPresetTitle( + /* [in] */ LONG nPreset, + /* [out] */ BSTR *bstrPresetTitle) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE GetPresetCount( + /* [out] */ LONG *pnPresetCount) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE SetCurrentPreset( + /* [in] */ LONG nPreset) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE GetCurrentPreset( + /* [out] */ LONG *pnPreset) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE DisplayPropertyPage( + /* [in] */ HWND hwndOwner) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE GoFullscreen( + /* [in] */ BOOL fFullScreen) = 0; + + virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE RenderFullScreen( + /* [in] */ TimedLevel *pLevels) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMPEffectsVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IWMPEffects * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IWMPEffects * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IWMPEffects * This); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE *Render )( + IWMPEffects * This, + /* [in] */ TimedLevel *pLevels, + /* [in] */ HDC hdc, + /* [in] */ RECT *prc); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE *MediaInfo )( + IWMPEffects * This, + /* [in] */ LONG lChannelCount, + /* [in] */ LONG lSampleRate, + /* [in] */ BSTR bstrTitle); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE *GetCapabilities )( + IWMPEffects * This, + /* [out] */ DWORD *pdwCapabilities); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE *GetTitle )( + IWMPEffects * This, + /* [out] */ BSTR *bstrTitle); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE *GetPresetTitle )( + IWMPEffects * This, + /* [in] */ LONG nPreset, + /* [out] */ BSTR *bstrPresetTitle); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE *GetPresetCount )( + IWMPEffects * This, + /* [out] */ LONG *pnPresetCount); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE *SetCurrentPreset )( + IWMPEffects * This, + /* [in] */ LONG nPreset); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE *GetCurrentPreset )( + IWMPEffects * This, + /* [out] */ LONG *pnPreset); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE *DisplayPropertyPage )( + IWMPEffects * This, + /* [in] */ HWND hwndOwner); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE *GoFullscreen )( + IWMPEffects * This, + /* [in] */ BOOL fFullScreen); + + /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE *RenderFullScreen )( + IWMPEffects * This, + /* [in] */ TimedLevel *pLevels); + + END_INTERFACE + } IWMPEffectsVtbl; + + interface IWMPEffects + { + CONST_VTBL struct IWMPEffectsVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMPEffects_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMPEffects_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMPEffects_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMPEffects_Render(This,pLevels,hdc,prc) \ + (This)->lpVtbl -> Render(This,pLevels,hdc,prc) + +#define IWMPEffects_MediaInfo(This,lChannelCount,lSampleRate,bstrTitle) \ + (This)->lpVtbl -> MediaInfo(This,lChannelCount,lSampleRate,bstrTitle) + +#define IWMPEffects_GetCapabilities(This,pdwCapabilities) \ + (This)->lpVtbl -> GetCapabilities(This,pdwCapabilities) + +#define IWMPEffects_GetTitle(This,bstrTitle) \ + (This)->lpVtbl -> GetTitle(This,bstrTitle) + +#define IWMPEffects_GetPresetTitle(This,nPreset,bstrPresetTitle) \ + (This)->lpVtbl -> GetPresetTitle(This,nPreset,bstrPresetTitle) + +#define IWMPEffects_GetPresetCount(This,pnPresetCount) \ + (This)->lpVtbl -> GetPresetCount(This,pnPresetCount) + +#define IWMPEffects_SetCurrentPreset(This,nPreset) \ + (This)->lpVtbl -> SetCurrentPreset(This,nPreset) + +#define IWMPEffects_GetCurrentPreset(This,pnPreset) \ + (This)->lpVtbl -> GetCurrentPreset(This,pnPreset) + +#define IWMPEffects_DisplayPropertyPage(This,hwndOwner) \ + (This)->lpVtbl -> DisplayPropertyPage(This,hwndOwner) + +#define IWMPEffects_GoFullscreen(This,fFullScreen) \ + (This)->lpVtbl -> GoFullscreen(This,fFullScreen) + +#define IWMPEffects_RenderFullScreen(This,pLevels) \ + (This)->lpVtbl -> RenderFullScreen(This,pLevels) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IWMPEffects_Render_Proxy( + IWMPEffects * This, + /* [in] */ TimedLevel *pLevels, + /* [in] */ HDC hdc, + /* [in] */ RECT *prc); + + +void __RPC_STUB IWMPEffects_Render_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IWMPEffects_MediaInfo_Proxy( + IWMPEffects * This, + /* [in] */ LONG lChannelCount, + /* [in] */ LONG lSampleRate, + /* [in] */ BSTR bstrTitle); + + +void __RPC_STUB IWMPEffects_MediaInfo_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IWMPEffects_GetCapabilities_Proxy( + IWMPEffects * This, + /* [out] */ DWORD *pdwCapabilities); + + +void __RPC_STUB IWMPEffects_GetCapabilities_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IWMPEffects_GetTitle_Proxy( + IWMPEffects * This, + /* [out] */ BSTR *bstrTitle); + + +void __RPC_STUB IWMPEffects_GetTitle_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IWMPEffects_GetPresetTitle_Proxy( + IWMPEffects * This, + /* [in] */ LONG nPreset, + /* [out] */ BSTR *bstrPresetTitle); + + +void __RPC_STUB IWMPEffects_GetPresetTitle_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IWMPEffects_GetPresetCount_Proxy( + IWMPEffects * This, + /* [out] */ LONG *pnPresetCount); + + +void __RPC_STUB IWMPEffects_GetPresetCount_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IWMPEffects_SetCurrentPreset_Proxy( + IWMPEffects * This, + /* [in] */ LONG nPreset); + + +void __RPC_STUB IWMPEffects_SetCurrentPreset_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IWMPEffects_GetCurrentPreset_Proxy( + IWMPEffects * This, + /* [out] */ LONG *pnPreset); + + +void __RPC_STUB IWMPEffects_GetCurrentPreset_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IWMPEffects_DisplayPropertyPage_Proxy( + IWMPEffects * This, + /* [in] */ HWND hwndOwner); + + +void __RPC_STUB IWMPEffects_DisplayPropertyPage_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IWMPEffects_GoFullscreen_Proxy( + IWMPEffects * This, + /* [in] */ BOOL fFullScreen); + + +void __RPC_STUB IWMPEffects_GoFullscreen_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [helpstring] */ HRESULT STDMETHODCALLTYPE IWMPEffects_RenderFullScreen_Proxy( + IWMPEffects * This, + /* [in] */ TimedLevel *pLevels); + + +void __RPC_STUB IWMPEffects_RenderFullScreen_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMPEffects_INTERFACE_DEFINED__ */ + + +/* Additional Prototypes for ALL interfaces */ + +unsigned long __RPC_USER BSTR_UserSize( __RPC__in unsigned long *, unsigned long , __RPC__in BSTR * ); +unsigned char * __RPC_USER BSTR_UserMarshal( __RPC__in unsigned long *, __RPC__inout_xcount(0) unsigned char *, __RPC__in BSTR * ); +unsigned char * __RPC_USER BSTR_UserUnmarshal(__RPC__in unsigned long *, __RPC__in_xcount(0) unsigned char *, __RPC__out BSTR * ); +void __RPC_USER BSTR_UserFree( __RPC__in unsigned long *, __RPC__in BSTR * ); + +unsigned long __RPC_USER HDC_UserSize( __RPC__in unsigned long *, unsigned long , __RPC__in HDC * ); +unsigned char * __RPC_USER HDC_UserMarshal( __RPC__in unsigned long *, __RPC__inout_xcount(0) unsigned char *, __RPC__in HDC * ); +unsigned char * __RPC_USER HDC_UserUnmarshal(__RPC__in unsigned long *, __RPC__in_xcount(0) unsigned char *, __RPC__out HDC * ); +void __RPC_USER HDC_UserFree( __RPC__in unsigned long *, __RPC__in HDC * ); + +unsigned long __RPC_USER HWND_UserSize( __RPC__in unsigned long *, unsigned long , __RPC__in HWND * ); +unsigned char * __RPC_USER HWND_UserMarshal( __RPC__in unsigned long *, __RPC__inout_xcount(0) unsigned char *, __RPC__in HWND * ); +unsigned char * __RPC_USER HWND_UserUnmarshal(__RPC__in unsigned long *, __RPC__in_xcount(0) unsigned char *, __RPC__out HWND * ); +void __RPC_USER HWND_UserFree( __RPC__in unsigned long *, __RPC__in HWND * ); + +/* end of Additional Prototypes */ + +#ifdef __cplusplus +} +#endif + +#endif + + diff --git a/hid/firefly/sauron/effects.idl b/hid/firefly/sauron/effects.idl new file mode 100644 index 000000000..071057dc4 --- /dev/null +++ b/hid/firefly/sauron/effects.idl @@ -0,0 +1,128 @@ +/////////////////////////////////////////////////////////////////////////////// +// Microsoft Windows Media Player +// Copyright (C) Microsoft Corporation, 1999-2000 +// +// Filename: effects.idl +// +// Abstract: Interface implemented by all Windows Media Player effects. +// +/////////////////////////////////////////////////////////////////////////////// + +cpp_quote("#include ") + +#pragma region Desktop Family +cpp_quote("#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)") + +#include + +import "oaidl.idl"; +import "ocidl.idl"; + + +// These flags are to be returned in the GetCapabilities method. They +// indicate to the effects host what the effect supports + +const DWORD EFFECT_CANGOFULLSCREEN = 0x00000001; // can the effect go full screen? +const DWORD EFFECT_HASPROPERTYPAGE = 0x00000002; // does the effect have a property page? +const DWORD EFFECT_VARIABLEFREQSTEP = 0x00000004; // should effect return frequency data with variable size steps? + +// This structure is passed to the Render() method of the effect. It holds the frequency, +// waveform and state data need to render the effect. +// +// The first dimension of each array corresponds to the channel: 0-left/mono 1-right(stereo only) +// The second dimension contains the sampled levels. The frequency data ranges +// from 0..255. The wave form data represents -128..127 but is stored as +// 0..255. The state contains the current audio playback state. The time stamp provides +// the relative time of these samples in the audio stream. + +const int SA_BUFFER_SIZE = 1024; // number of frequency/waveform samples + +enum PlayerState // audio playback states +{ + stop_state = 0, // audio is currently stopped + pause_state = 1, // audio is currently paused + play_state = 2 // audio is currently playing +}; + +cpp_quote("") +cpp_quote("//**********************************************************************") +cpp_quote("// Define the minimum and maximum frequency ranges returned in our") +cpp_quote("// TimedLevel frequency array (i.e. first index in TimedLevel.frequency") +cpp_quote("// is at 20Hz and last is at 22050Hz).") +cpp_quote("//**********************************************************************") +cpp_quote("const float kfltTimedLevelMaximumFrequency = 22050.0F;") +cpp_quote("const float kfltTimedLevelMinimumFrequency = 20.0F;") +cpp_quote("") +cpp_quote("/*") +cpp_quote(" * FREQUENCY_INDEX() returns the index into TimedLevel.frequency[] where ") +cpp_quote(" * the specified frequency is located in the power spectrum") +cpp_quote(" */") +cpp_quote("#define FREQUENCY_INDEX(FREQ)\\") +cpp_quote(" (int)(((FREQ) - kfltTimedLevelMinimumFrequency) /\\") +cpp_quote(" (((kfltTimedLevelMaximumFrequency - kfltTimedLevelMinimumFrequency) / SA_BUFFER_SIZE)))") +cpp_quote("") + +typedef struct tagTimedLevel +{ + unsigned char frequency[2][SA_BUFFER_SIZE]; + unsigned char waveform [2][SA_BUFFER_SIZE]; + int state; + hyper timeStamp; +} TimedLevel; + +[ + object, + uuid(D3984C13-C3CB-48e2-8BE5-5168340B4F35), + helpstring("IEffects Interface"), + pointer_default(unique) +] +interface IWMPEffects : IUnknown +{ + // render effect to the rectangle on the provided dc - the dc is normalized + [helpstring("method Render")] + HRESULT Render([in] TimedLevel *pLevels, [in] HDC hdc, [in] RECT *prc); + + // provides the no. channels, sample rate and title of the audio currently playing + [helpstring("method MediaInfo")] + HRESULT MediaInfo([in] LONG lChannelCount, [in] LONG lSampleRate, [in] BSTR bstrTitle ); + + // called to retrieive the capabilities of the effect (fullscreen? property page?, etc.) + [helpstring("method GetCapabilities")] + HRESULT GetCapabilities([out] DWORD * pdwCapabilities); + + // retrieve the display title of the effect + [helpstring("method GetTitle")] + HRESULT GetTitle([out] BSTR *bstrTitle); + + // retrieve the title for a preset + [helpstring("method GetPresetTitle")] + HRESULT GetPresetTitle([in] LONG nPreset, [out] BSTR *bstrPresetTitle); + + // retrieve the number of presets this effect supports + [helpstring("method GetPresetCount")] + HRESULT GetPresetCount([out] LONG * pnPresetCount); + + // set / get the current preset + [helpstring("method SetPreset")] + HRESULT SetCurrentPreset([in] LONG nPreset); + [helpstring("method GetPreset")] + HRESULT GetCurrentPreset([out] LONG * pnPreset); + + // display the property page of the effect (if there is one) + [helpstring("method DisplayPropertyPage")] + HRESULT DisplayPropertyPage([in] HWND hwndOwner); + + // This method will be called when the effect is to start and stop full screen + // rendering (if supported) + [helpstring("method GoFullscreen")] + HRESULT GoFullscreen([in] BOOL fFullScreen); + + // This method will get called after a successful call to GoFullScreen to render + // the effect. Return failure from this method to signal loss of full screen. + [helpstring("method RenderFullScreen")] + HRESULT RenderFullScreen([in] TimedLevel *pLevels); +}; + +cpp_quote("#endif /* WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) */") +#pragma endregion + diff --git a/hid/firefly/sauron/iSauron.idl b/hid/firefly/sauron/iSauron.idl new file mode 100644 index 000000000..5327a0d86 --- /dev/null +++ b/hid/firefly/sauron/iSauron.idl @@ -0,0 +1,40 @@ +// iSauron.idl : IDL source for Sauron.dll +// + +// This file will be processed by the MIDL tool to +// produce the type library (iSauron.tlb) and marshalling code. + +import "oaidl.idl"; +import "ocidl.idl"; + [ + object, + uuid(85A2A542-7E3A-4757-85A8-34AE3E9805BA), + dual, + helpstring("ISauron Interface"), + pointer_default(unique) + ] + interface ISauron : IDispatch + { + [propget, id(1), helpstring("property foregroundColor")] HRESULT foregroundColor([out, retval] BSTR *pVal); + [propput, id(1), helpstring("property foregroundColor")] HRESULT foregroundColor([in] BSTR newVal); + }; + +[ + uuid(185A2923-9B3C-4816-BB77-1BDA42E35605), + version(1.0), + helpstring("Sauron 1.0 Type Library") +] +library SAURONLib +{ + importlib("stdole32.tlb"); + importlib("stdole2.tlb"); + + [ + uuid(C06687F6-D0F8-4627-A113-2654249C67F9), + helpstring("Sauron Class") + ] + coclass Sauron + { + [default] interface ISauron; + }; +}; diff --git a/hid/firefly/sauron/resource.h b/hid/firefly/sauron/resource.h new file mode 100644 index 000000000..f22f9e705 --- /dev/null +++ b/hid/firefly/sauron/resource.h @@ -0,0 +1,20 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by Saurondll.rc +// +#define IDS_PROJNAME 100 +#define IDS_EFFECTNAME 101 +#define IDS_BARSPRESETNAME 102 +#define IDS_FLASHPRESETNAME 103 +#define IDR_SAURON 104 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 201 +#define _APS_NEXT_COMMAND_VALUE 32768 +#define _APS_NEXT_CONTROL_VALUE 201 +#define _APS_NEXT_SYMED_VALUE 105 +#endif +#endif diff --git a/hid/firefly/sauron/stdafxsrc.cpp b/hid/firefly/sauron/stdafxsrc.cpp new file mode 100644 index 000000000..1577c4e3b --- /dev/null +++ b/hid/firefly/sauron/stdafxsrc.cpp @@ -0,0 +1 @@ +#include "stdafx.h" \ No newline at end of file diff --git a/hid/firefly/shared/luminous.h b/hid/firefly/shared/luminous.h new file mode 100644 index 000000000..a04a090c1 --- /dev/null +++ b/hid/firefly/shared/luminous.h @@ -0,0 +1,76 @@ +/*++ + +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: + + luminous.h + +Abstract: + + This header exposes definitions for managing firefly devices. + +Environment: + + User Mode. + +--*/ +#ifndef _LUMINOUS_H_ +#define _LUMINOUS_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define NAME_SPACE TEXT("root\\WMI") +#define CLASS_NAME TEXT("FireflyDeviceInformation") +#define PROPERTY_NAME TEXT("TailLit") + +class CLuminous { + +public: + + CLuminous(VOID); + + virtual ~CLuminous(VOID); + + BOOL Open(VOID); + + VOID Close(VOID); + + BOOL Set(_In_ BOOL Enabled); + + BOOL Get(_In_ BOOL *Enabled); + +private: + + IWbemServices *m_pIWbemServices; + IWbemClassObject *m_pIWbemClassObject; + BOOL m_bCOMInitialized; + +}; + + +IWbemServices *ConnectToNamespace ( + _In_ LPTSTR chNamespace); +IWbemClassObject *GetInstanceReference ( + IWbemServices *pIWbemServices, + _In_ LPTSTR lpClassName); + +BSTR AnsiToBstr ( + _In_ LPTSTR lpSrc, + _In_ int nLenSrc); + +#endif // _LUMINOUS_H_ + + diff --git a/hid/firefly/shared/project.mk b/hid/firefly/shared/project.mk new file mode 100644 index 000000000..1903c4aa9 --- /dev/null +++ b/hid/firefly/shared/project.mk @@ -0,0 +1,7 @@ +INCLUDES= ..\shared + +!IFNDEF BUILD_ALT_DIR +SAMPLE_COMMON_DIR=$(OBJ_PATH)\..\$(O)\disk +!ELSE +SAMPLE_COMMON_DIR=$(OBJ_PATH)\..\$(O)\disk\$(BUILD_ALT_DIR) +!ENDIF diff --git a/hid/hclient/hclient.sln b/hid/hclient/hclient.sln index 69314509e..a2ed0c444 100644 --- a/hid/hclient/hclient.sln +++ b/hid/hclient/hclient.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0 MinimumVisualStudioVersion = 12.0 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hclient", "hclient.vcxproj", "{AC9126E3-E293-428A-AEDB-6E3F0F7ACD21}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hclient", "hclient.vcxproj", "{7A7D6960-DBAA-4A93-8C8C-833FD9F31D44}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {AC9126E3-E293-428A-AEDB-6E3F0F7ACD21}.Debug|Win32.ActiveCfg = Debug|Win32 - {AC9126E3-E293-428A-AEDB-6E3F0F7ACD21}.Debug|Win32.Build.0 = Debug|Win32 - {AC9126E3-E293-428A-AEDB-6E3F0F7ACD21}.Release|Win32.ActiveCfg = Release|Win32 - {AC9126E3-E293-428A-AEDB-6E3F0F7ACD21}.Release|Win32.Build.0 = Release|Win32 - {AC9126E3-E293-428A-AEDB-6E3F0F7ACD21}.Debug|x64.ActiveCfg = Debug|x64 - {AC9126E3-E293-428A-AEDB-6E3F0F7ACD21}.Debug|x64.Build.0 = Debug|x64 - {AC9126E3-E293-428A-AEDB-6E3F0F7ACD21}.Release|x64.ActiveCfg = Release|x64 - {AC9126E3-E293-428A-AEDB-6E3F0F7ACD21}.Release|x64.Build.0 = Release|x64 + {7A7D6960-DBAA-4A93-8C8C-833FD9F31D44}.Debug|Win32.ActiveCfg = Debug|Win32 + {7A7D6960-DBAA-4A93-8C8C-833FD9F31D44}.Debug|Win32.Build.0 = Debug|Win32 + {7A7D6960-DBAA-4A93-8C8C-833FD9F31D44}.Release|Win32.ActiveCfg = Release|Win32 + {7A7D6960-DBAA-4A93-8C8C-833FD9F31D44}.Release|Win32.Build.0 = Release|Win32 + {7A7D6960-DBAA-4A93-8C8C-833FD9F31D44}.Debug|x64.ActiveCfg = Debug|x64 + {7A7D6960-DBAA-4A93-8C8C-833FD9F31D44}.Debug|x64.Build.0 = Debug|x64 + {7A7D6960-DBAA-4A93-8C8C-833FD9F31D44}.Release|x64.ActiveCfg = Release|x64 + {7A7D6960-DBAA-4A93-8C8C-833FD9F31D44}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/hid/hclient/hclient.vcxproj b/hid/hclient/hclient.vcxproj index 6ef71350b..1ad38b148 100644 --- a/hid/hclient/hclient.vcxproj +++ b/hid/hclient/hclient.vcxproj @@ -19,11 +19,11 @@
- {AC9126E3-E293-428A-AEDB-6E3F0F7ACD21} + {7A7D6960-DBAA-4A93-8C8C-833FD9F31D44} $(MSBuildProjectName) Debug Win32 - {A2706ED9-AB39-4810-94CD-F7BE251F6456} + {D88F2866-9D6E-437A-8F8D-056B18E851AF} @@ -171,7 +171,6 @@ - diff --git a/hid/hclient/hclient.vcxproj.Filters b/hid/hclient/hclient.vcxproj.Filters index 929de5a4b..b2f5d62f8 100644 --- a/hid/hclient/hclient.vcxproj.Filters +++ b/hid/hclient/hclient.vcxproj.Filters @@ -3,15 +3,15 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {3CD69E69-D314-43C2-988D-6A3C580F61BE} + {55093100-B347-4736-B0EC-F835DDA0D98D} h;hpp;hxx;hm;inl;inc;xsd - {D93C41DB-1154-4AB9-8FAB-EF653138D12B} + {43D41B7E-82A6-4508-B577-4B2702FA8C8C} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {BF7E346B-B570-4400-8C2B-765D723A7FBF} + {2B0259E8-4B79-49E6-BAEE-57D0FB83FFC8} diff --git a/hid/hidusbfx2/Package/package.VcxProj b/hid/hidusbfx2/Package/package.VcxProj new file mode 100644 index 000000000..779e4aadf --- /dev/null +++ b/hid/hidusbfx2/Package/package.VcxProj @@ -0,0 +1,87 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + + {213466E9-93D3-4D42-9BEE-F6DFBDC290BD} + + + {0C56E492-CEB9-49C4-B127-FE437B55A879} + + + + WindowsKernelModeDriver10.0 + Utility + Package + true + Debug + + + + {01899CCE-8A2B-4134-85E6-F81E7A9D30BE} + {065BAC02-7D5D-4099-AB61-0A2F323D7131} + $(MSBuildProjectName) + + + Windows10 + true + + + Windows10 + false + + + Windows10 + true + + + Windows10 + false + + + + + + + + + + + DbgengKernelDebugger + False + None + + + + + + %PathToInf% + False + False + True + + 133563 + + + + + + + \ No newline at end of file diff --git a/hid/hidusbfx2/Package/package.VcxProj.Filters b/hid/hidusbfx2/Package/package.VcxProj.Filters new file mode 100644 index 000000000..5452b6325 --- /dev/null +++ b/hid/hidusbfx2/Package/package.VcxProj.Filters @@ -0,0 +1,21 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {FB119E32-2776-4AAA-AE28-BB0915F08F14} + + + h;hpp;hxx;hm;inl;inc;xsd + {A272D3D1-380E-485F-9B54-2C4890966EF6} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {37428764-C503-4DAC-A17E-ECE3A971BD91} + + + inf;inv;inx;mof;mc; + {C8FCCCAD-C2B7-4697-8144-3FB5D565425E} + + + \ No newline at end of file diff --git a/hid/hidusbfx2/ReadMe.md b/hid/hidusbfx2/ReadMe.md new file mode 100644 index 000000000..c999817bd --- /dev/null +++ b/hid/hidusbfx2/ReadMe.md @@ -0,0 +1,412 @@ +HIDUSBFX2 sample driver +======================= +The HIDUSBFX2 sample driver (hidusbfx2.sys) demonstrates how to map a non-HID USB device to a HID device. + +The sample also demonstrates how to write a HID minidriver using Windows Driver Frameworks (WDF). The minidriver is written for the [OSR USB-FX2 Learning Kit](http://www.osronline.com/hardware/OSRFX2_32.pdf). Although the device is not HID-compliant, the sample exposes it as a HID device. + +Related topics +-------------- + +[Human Input Devices Design Guide](http://msdn.microsoft.com/en-us/library/windows/hardware/ff539952) + +[Human Input Devices Reference](http://msdn.microsoft.com/en-us/library/windows/hardware/ff539956) + +Related technologies +-------------------- + +[Creating Framework-based HID Minidrivers](http://msdn.microsoft.com/en-us/library/windows/hardware/ff540774) , [Creating UMDF-based HID Minidrivers](http://msdn.microsoft.com/en-us/library/windows/hardware/hh439579) + +Build the sample +---------------- + +For information on how to build a driver solution using Microsoft Visual Studio, see [Building a Driver](http://msdn.microsoft.com/en-us/library/windows/hardware/ff554644). + +Theory of Operation +------------------- + +A HID USB device provides a HID descriptor (through an interface descriptor) that identifies the device as HID-compliant and enables the system-supplied HID minidriver (**hidusb.sys**) and the HID class driver to load, parse the HID descriptor, and enumerate child HID device stacks. The system provides strong support for HID devices, so you do not typically have to write a HID minidriver. However, there are cases in which you might need to write your own HID minidriver (for example, if it is difficult to make desired changes to HID-compliant device firmware or if you need to make a non-HID compliant device into a HID device without updating the firmware). + +**Overview of the Device** + +You can view the specification for the device in the [Using the OSR USB FX-2 Learning Kit](http://go.microsoft.com/fwlink/p/?linkid=64091) document. + +The device is loosely based on the development board that is supplied with the Cypress EZ-USB FX2 Development Kit (CY3681) and contains one interface and three endpoints (Interrupt IN, Bulk Out, and Bulk IN). The firmware supports vendor commands to query or set the LED bar graph display and 7-segment LED display, and to query toggle switch states. + +The interrupt endpoint sends an 8-bit value that represents the state of the switches. This value is sent on startup, resume from suspend, and whenever the switch pack setting changes. The firmware does not de-bounce the switch pack. One switch change can cause multiple bytes to be sent. The bits are in the reverse order of the labels on the pack (for example, bit 0x80 is labeled 1 on the pack). + +Bulk endpoints are configured for loopback. + +**Overview of the Driver Stack** + +Kernel-Mode Driver Framework (KMDF) does not support HID minidrivers natively because the HID architecture requires that the HID class driver (**hidclass.sys**) own the driver dispatch table for HID minidrivers. This requirement conflicts with the KMDF requirement that it own the driver dispatch table in order to handle Plug and Play (PnP), power, and I/O requests correctly. + +You can resolve this ownership conflict by using a driver stack that consists of a minimal WDM driver as a function driver and a complete KMDF driver as a lower filter driver. The function driver registers with the HID class (so**hidclass.sys** owns its dispatch table) and forwards all of the requests to the lower filter driver. The lower filter driver (KMDF owns the dispatch table) processes all of the requests. + +The minimal function driver code is located in the src\\hid\\hidusbfx2\\hidkmdf directory (the driver binary is named **hidkmdf.sys**), and the lower filter driver code is located in the src\\hid\\hidusbfx2\\sys folder (the binary is named **hidusbfx2.sys**). The function driver is a minimal WDM driver and you can reuse it without any modification. Remember to rename the driver binary when you reuse it, to avoid a name conflict. You need to modify the KMDF filter driver according to your device's requirements. + +**Mapping a Non-HID USB Device to HID** + +When the HIDclass driver queries the minidriver, the minidriver returns a hard-coded report descriptor that enables the HID class driver to create child devices as described by the report descriptor. The report descriptor has three top-level application collections: + +- Consumer control + +- System control + +- Vendor-defined + +The HID class driver creates a driver stack for each top-level collection. The operating system opens the consumer control and system control collections. These collections have input buttons and obtain data from the interrupt endpoint of the USB device. The vendor-defined collection exposes a feature button to control the 7-segment display and bar graph display. Any client application can open the vendor-defined collection to send feature requests. + +**Switch Pack Mapping** + +The switch pack on the USB device is mapped as hot keys that are commonly found on modern keyboards. This mapping is possible by exposing the switch pack as two system-supported collections: consumer control and system control. The consumer control collection provides a mapping for some application-launch and application-action keys, as shown in the following table. The system control collection provides a mapping for the power sleep function. + +Switch + +1 + +2 + +3 + +4 + +5 + +6 + +7 + +8 + +Mapping + +Sleep + +Calculator + +Mail + +Favorites + +Refresh + +Forward + +Back + +Browser + +**Segment Display and Bar Graph** + +The segment display and bar graph are mapped as HID feature controls that you can manipulate by using the **HidD\_SetFeature** function from a user-mode application. The feature controls are mapped as vendor-defined usage page 0xff00. The SEVEN\_SEGMENT\_REPORT\_ID and BARGRAPH\_REPORT\_ID usages are listed in the following tables. You can also use **Hidclient.exe**, an application that is available in the Windows Driver Kit (WDK), to manipulate the segment display and bar graph. For more information about this mapping, see the following two tables. + +**Segment Display Mapping** + +Usage ID + +0xD7 + +0x06 + +0xB3 + +0xA7 + +0x66 + +0xE5 + +0xF4 + +0x07 + +0xF7 + +0x67 + +Mapping + +Display 0 + +Display 1 + +Display 2 + +Display 3 + +Display 4 + +Display 5 + +Display 6 + +Display 7 + +Display 8 + +Display 9 + +**Bar Graph Mapping** + +Note that you can OR these values to light multiple LEDs. + +Usage ID + +0x01 + +0x02 + +0x04 + +0x08 + +0x10 + +0x20 + +0x40 + +0x80 + +0xFF + +0x00 + +Mapping + +LED 1 ON + +LED 2ON + +LED 3ON + +LED 4ON + +LED 5ON + +LED 6ON + +LED 7ON + +LED 8ON + +All LEDS ON + +All LEDS OFF + +**Support for Selective Suspend** + +The HID class driver provides support for selective suspend. The minidriver participates in this feature by handling HID class IOCTLs appropriately. To enable the selective suspend feature for your device, you need to add a "SelectiveSuspend" = 1 value in the registry in the device hardware key through the INF file. For an example, see the**hidusbfx2.inf** file. + +Installing the sample +--------------------- + +If you adapt this driver for your device, update the INF file to match the hardware ID (VID, PID) and the device description text to match your test board/device. + +To start installing the sample, you must: + +1. Build the driver and copy the following files to a folder on your hard drive: + + - **hidusbfx2.inf** + - **hidusbfx2.sys** + - **Hidkmdf.sys** + - The WDF coinstaller from the *\\\redist\\wdf\\\* directory. + + **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 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. + +2. Plug in the device and follow these steps, depending on the operating system that you are using. + - On Windows 7 and later operating systems: + 1. Launch Device Manager by executing command devmgmt.msc in a command window, or from the **Hardware and Sound** program group in **Control Panel**. + 2. Select **OSR USB-FX2 device** from **Other Devices** category and select **Update Driver Software...** from the right-click menu. + 3. Select **Browse my computer for software** and provide the location of the driver files. + 4. Select **Install this driver software anyway** when the Windows Security dialog box appears. + 5. After the driver is installed, you should see the device in Device Manager under Human Interface Devices. + - On Windows Vista and Windows Server 2008: + 1. In the **Found New Hardware** dialog box, select **Locate and install driver software (recommended)**. + 2. Select **Don't search online**. + 3. Select **I don't have the disc. Show me other options**. + 4. Select **Browse my computer for driver software (advanced)**. + 5. Enter the location of the driver files, and select **Install this driver software anyway** when the **Windows Security** dialog box appears. After the driver is installed, you'll see the device in Device Manager under Human Interface Devices. + +Testing +------- + +**Testing Switches** + +- To open a Web browser, toggle switch number 8 on the device board to the On position (toggle down). + +- To start the calculator application, toggle switch number 2 on the device board to the On position (toggle down). + +**Testing Bar Graph and 7-Segment Display** + +1. Start the **hidclient.exe** GUI application from the WDK. The application source code is located in the *src\\hid\\hclient* directory, and you build it by using the appropriate build environment. + +2. From the **HID Device to examine** menu, select the device that contains "UsagePage 0ff00, Usage 01" as a substring. + +3. Click **Modify Features**. The **Feature Data** dialog box opens. + +4. Click **Modify Features**. The **Modify features** dialog box opens. + +5. In the input box, type **7** and click **Send to Device**. You'll see number 7 appear in the 7-segment display. + +6. Type any number from 1-8, and you'll see the respective number displayed in the 7-segment display. + +7. Type any number from (and including) 9-17, and you will see one of the LEDs on the bar graph turn on. For mapping information, see the previous table. + +**Report Descriptor** + + +++ + + + + + +
  // Consumer control collection
+ 
+   0x05,0x0C,  // USAGE_PAGE (Consumer Page)
+ 
+   0x09,0x01,  // USAGE (Consumer Control Usage 0x01)
+ 
+   0xA1,0x01,  // COLLECTION (Application)
+ 
+   0x85,0x01,  //  REPORT_ID 
+ 
+   0x0A, 0x23, 0x02,  //  USAGE (Usage Browser)
+ 
+   0x0A, 0x24, 0x02,  //  USAGE (Usage AC Back)
+ 
+   0x0A, 0x25, 0x02,  //  USAGE (Usage AC Forward)
+ 
+   0x0A, 0x27, 0x02,  //  USAGE (Usage AC Refresh)
+ 
+   0x0A, 0x2A, 0x02,  //  USAGE (Usage AC BookMarks)
+ 
+   0x0A, 0x8A, 0x01,  //  USAGE (Usage AL Mail)
+ 
+   0x0A, 0x92, 0x01,  //  USAGE (Usage AL Calculator )
+ 
+   0x15, 0x00,  //  LOGICAL_MINIMUM(0)
+ 
+   0x25, 0x01,  //  LOGICAL_MAXIMUM(1)  
+ 
+   0x75, 0x01,  //  REPORT_SIZE 
+ 
+   0x95, 0x07,  //  REPORT_COUNT 
+ 
+   0x81, 0x02,  //  INPUT (Data, Variable,Abs)
+ 
+   0x75, 0x01,  //  REPORT_SIZE 
+ 
+   0x95, 0x01,  //  REPORT_COUNT
+ 
+   0x81, 0x07,  //  INPUT (const)
+ 
+   0xC0,  // END_COLLECTION
+ 
+   // system control collection
+ 
+   0x05, 0x01,  // Usage Page (Generic Desktop)
+ 
+   0x09, 0x80,  // Usage (System Control)
+ 
+   0xA1, 0x01,  // Collection (Application)
+ 
+   0x85, 0x02,  //  Report ID 
+ 
+   0x95, 0x07,  //  Report Count 
+ 
+   0x81, 0x07,  //  Input (Constant)  
+ 
+   0x09, 0x82,  //  Usage (System Sleep)
+ 
+   0x95, 0x01,  //  Report Count (2)
+ 
+   0x81, 0x06,  //  Input (Data, Variable, Relative, Preferred)
+ 
+   0xC0,  // End Collection
+ 
+   // Feature collection
+ 
+   0x06,0x00, 0xFF,  // USAGE_PAGE (Vender Defined Usage Page)
+ 
+   0x09,0x01,  // USAGE (Vendor Usage 0x01)
+ 
+   0xA1,0x01,  // COLLECTION (Application)
+ 
+   0x85,0x03,  // Report ID 
+ 
+   0x19,0x01,  //  USAGE MINIMUM 
+ 
+   0x29,0x20,  //  USAGE MAXIMUM 
+ 
+   0x15,0x00,  //  LOGICAL_MINIMUM(1)
+ 
+   0x26,0xff, 0x00,  //  LOGICAL_MAXIMUM(255)
+ 
+   0x75,0x08,  //  REPORT_SIZE 
+ 
+   0x95,0x01,  //  REPORT_COUNT 
+ 
+   0xB1,0x00,  //  Feature (Data,Ary,Abs)
+ 
+   0xC0  // END_COLLECTION 
+ +Code Tour +--------- + +This section includes a file manifest of all the files in the src\\hid\\hidusbfx2 directory. + +**File Manifest** + +src\\hid\\hidusbfx2\\hidkmdf + + ++++ + + + + + + + + + + + +
File +Description

hidkmdf.c

+

Contains code for driver entry and dispatch

Sources

+

WDK sources file

+ +src\\hid\\hidusbfx2\\sys + + ++++ + + + + + + + + + + + +
File +Description

Driver.c

+

Contains code for driver entry and dispatch functions

hid.c

+

Contains code for handling HID IOCTLS

+ + diff --git a/hid/hidusbfx2/hidkmdf/hidkmdf.c b/hid/hidusbfx2/hidkmdf/hidkmdf.c new file mode 100644 index 000000000..cff9b89cc --- /dev/null +++ b/hid/hidusbfx2/hidkmdf/hidkmdf.c @@ -0,0 +1,277 @@ +/*++ + +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: + + HidKmdf.C + +Abstract: + + Hid miniport to be used as an upper layer for supporting + KMDF based driver for HID devices. + +Author: + + +Environment: + + Kernel mode only +--*/ + +#include + +#pragma warning(disable:4201) // suppress nameless struct/union warning +#pragma warning(disable:4214) // suppress bit field types other than int warning +#include + +#pragma warning(default:4201) // suppress nameless struct/union warning +#pragma warning(default:4214) // suppress bit field types other than int warning + +#define GET_NEXT_DEVICE_OBJECT(DO) \ + (((PHID_DEVICE_EXTENSION)(DO)->DeviceExtension)->NextDeviceObject) + + +// +// This type of function declaration is for Prefast for drivers. +// Because this declaration specifies the function type, PREfast for Drivers +// does not need to infer the type or to report an inference. The declaration +// also prevents PREfast for Drivers from misinterpreting the function type +// and applying inappropriate rules to the function. For example, PREfast for +// Drivers would not apply rules for completion routines to functions of type +// DRIVER_CANCEL. The preferred way to avoid Warning 28101 is to declare the +// function type explicitly. In the following example, the DriverEntry function +// is declared to be of type DRIVER_INITIALIZE. +// +DRIVER_INITIALIZE DriverEntry; +DRIVER_ADD_DEVICE HidKmdfAddDevice; +_Dispatch_type_(IRP_MJ_OTHER) +DRIVER_DISPATCH HidKmdfPassThrough; +_Dispatch_type_(IRP_MJ_POWER) +DRIVER_DISPATCH HidKmdfPowerPassThrough; +DRIVER_UNLOAD HidKmdfUnload; + +#ifdef ALLOC_PRAGMA +#pragma alloc_text( INIT, DriverEntry ) +#pragma alloc_text( PAGE, HidKmdfAddDevice ) +#pragma alloc_text( PAGE, HidKmdfUnload ) +#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. + +--*/ +{ + HID_MINIDRIVER_REGISTRATION hidMinidriverRegistration; + NTSTATUS status; + ULONG i; + + KdPrint(("Enter DriverEntry()\n")); + + // + // Initialize the dispatch table to pass through all the IRPs. + // + for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++) { + DriverObject->MajorFunction[i] = HidKmdfPassThrough; + } + + // + // Special case power irps so that we call PoCallDriver instead of IoCallDriver + // when sending the IRP down the stack. + // + DriverObject->MajorFunction[IRP_MJ_POWER] = HidKmdfPowerPassThrough; + + + DriverObject->DriverExtension->AddDevice = HidKmdfAddDevice; + DriverObject->DriverUnload = HidKmdfUnload; + + RtlZeroMemory(&hidMinidriverRegistration, + sizeof(hidMinidriverRegistration)); + + // + // Revision must be set to HID_REVISION by the minidriver + // + hidMinidriverRegistration.Revision = HID_REVISION; + hidMinidriverRegistration.DriverObject = DriverObject; + hidMinidriverRegistration.RegistryPath = RegistryPath; + hidMinidriverRegistration.DeviceExtensionSize = 0; + + // + // if "DevicesArePolled" is False then the hidclass driver does not do + // polling and instead reuses a few Irps (ping-pong) if the device has + // an Input item. Otherwise, it will do polling at regular interval. USB + // HID devices do not need polling by the HID classs driver. Some leagcy + // devices may need polling. + // + hidMinidriverRegistration.DevicesArePolled = FALSE; + + // + // Register with hidclass + // + status = HidRegisterMinidriver(&hidMinidriverRegistration); + if (!NT_SUCCESS(status) ){ + KdPrint(("HidRegisterMinidriver FAILED, returnCode=%x\n", status)); + } + + return status; +} + + +NTSTATUS +HidKmdfAddDevice( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PDEVICE_OBJECT FunctionalDeviceObject + ) +/*++ + +Routine Description: + + HidClass Driver calls our AddDevice routine after creating an FDO for us. + We do not need to create a device object or attach it to the PDO. + Hidclass driver will do it for us. + +Arguments: + + DriverObject - pointer to the driver object. + + FunctionalDeviceObject - pointer to the FDO created by the + Hidclass driver for us. + +Return Value: + + NT status code. + +--*/ +{ + PAGED_CODE(); + + UNREFERENCED_PARAMETER(DriverObject); + + + FunctionalDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING; + + return STATUS_SUCCESS; +} + +NTSTATUS +HidKmdfPassThrough( + _In_ PDEVICE_OBJECT DeviceObject, + _Inout_ PIRP Irp + ) +/*++ + +Routine Description: + + Pass through routine for all the IRPs except power. + +Arguments: + + DeviceObject - pointer to a device object. + + Irp - pointer to an I/O Request Packet. + +Return Value: + + NT status code + +--*/ +{ + // + // Copy current stack to next instead of skipping. We do this to preserve + // current stack information provided by hidclass driver to the minidriver + // + IoCopyCurrentIrpStackLocationToNext(Irp); + return IoCallDriver(GET_NEXT_DEVICE_OBJECT(DeviceObject), Irp); +} + + +NTSTATUS +HidKmdfPowerPassThrough( + _In_ PDEVICE_OBJECT DeviceObject, + _Inout_ PIRP Irp + ) +/*++ + +Routine Description: + + Pass through routine for power IRPs . + +Arguments: + + DeviceObject - pointer to a device object. + + Irp - pointer to an I/O Request Packet. + +Return Value: + + NT status code + +--*/ +{ + // + // Must start the next power irp before skipping to the next stack location + // + PoStartNextPowerIrp(Irp); + + // + // Copy current stack to next instead of skipping. We do this to preserve + // current stack information provided by hidclass driver to the minidriver + // + IoCopyCurrentIrpStackLocationToNext(Irp); + return PoCallDriver(GET_NEXT_DEVICE_OBJECT(DeviceObject), Irp); +} + + +VOID +HidKmdfUnload( + _In_ PDRIVER_OBJECT DriverObject + ) +/*++ + +Routine Description: + + Free all the allocated resources, etc. + +Arguments: + + DriverObject - pointer to a driver object. + +Return Value: + + VOID. + +--*/ +{ + UNREFERENCED_PARAMETER(DriverObject); + + PAGED_CODE (); + + return; +} + diff --git a/hid/hidusbfx2/hidkmdf/hidkmdf.rc b/hid/hidusbfx2/hidkmdf/hidkmdf.rc new file mode 100644 index 000000000..aeef2096a --- /dev/null +++ b/hid/hidusbfx2/hidkmdf/hidkmdf.rc @@ -0,0 +1,10 @@ +#include +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "Pass-through HID to KMDF Filter Driver" +#define VER_INTERNALNAME_STR "HIDKMDF.SYS" +#define VER_ORIGINALFILENAME_STR "HIDKMDF.SYS" + +#include "common.ver" diff --git a/hid/hidusbfx2/hidkmdf/hidkmdf.vcxproj b/hid/hidusbfx2/hidkmdf/hidkmdf.vcxproj new file mode 100644 index 000000000..5eb7bff2c --- /dev/null +++ b/hid/hidusbfx2/hidkmdf/hidkmdf.vcxproj @@ -0,0 +1,145 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {213466E9-93D3-4D42-9BEE-F6DFBDC290BD} + $(MSBuildProjectName) + false + true + Debug + Win32 + {792ED1CC-8C69-41A2-87D7-773F79512AE6} + + + + 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) + + + + + + + + + + + + + + + + hidkmdf + + + hidkmdf + + + hidkmdf + + + hidkmdf + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\hidclass.lib + + + + + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\hidclass.lib + + + + + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\hidclass.lib + + + + + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\hidclass.lib + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/hid/hidusbfx2/hidkmdf/hidkmdf.vcxproj.Filters b/hid/hidusbfx2/hidkmdf/hidkmdf.vcxproj.Filters new file mode 100644 index 000000000..d84e2296d --- /dev/null +++ b/hid/hidusbfx2/hidkmdf/hidkmdf.vcxproj.Filters @@ -0,0 +1,31 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {C5F48F0A-25A4-45EA-BD85-1A3D7F5F6AFC} + + + h;hpp;hxx;hm;inl;inc;xsd + {2E4FEC0E-9752-44A5-A6F3-8E74C3104B63} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {25558E5A-CA73-4F97-B8B5-E71F91EE6D42} + + + inf;inv;inx;mof;mc; + {D86AF2FC-0173-48FE-A3D9-7389EAE4EB60} + + + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/hid/hidusbfx2/hidusbfx2.sln b/hid/hidusbfx2/hidusbfx2.sln new file mode 100644 index 000000000..cd19d80f0 --- /dev/null +++ b/hid/hidusbfx2/hidusbfx2.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}") = "Package", "Package", "{F992E151-FEAC-4E8C-AF6A-082C91D6E54C}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sys", "Sys", "{6D46B6EF-E350-4AFB-8AE4-470C61F8F756}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Hidkmdf", "Hidkmdf", "{9A9322D5-3F21-4358-A801-916E04F462C8}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "package", "Package\package.VcxProj", "{01899CCE-8A2B-4134-85E6-F81E7A9D30BE}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hidusbfx2", "sys\hidusbfx2.vcxproj", "{0C56E492-CEB9-49C4-B127-FE437B55A879}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hidkmdf", "hidkmdf\hidkmdf.vcxproj", "{213466E9-93D3-4D42-9BEE-F6DFBDC290BD}" +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 + {01899CCE-8A2B-4134-85E6-F81E7A9D30BE}.Debug|Win32.ActiveCfg = Debug|Win32 + {01899CCE-8A2B-4134-85E6-F81E7A9D30BE}.Debug|Win32.Build.0 = Debug|Win32 + {01899CCE-8A2B-4134-85E6-F81E7A9D30BE}.Release|Win32.ActiveCfg = Release|Win32 + {01899CCE-8A2B-4134-85E6-F81E7A9D30BE}.Release|Win32.Build.0 = Release|Win32 + {01899CCE-8A2B-4134-85E6-F81E7A9D30BE}.Debug|x64.ActiveCfg = Debug|x64 + {01899CCE-8A2B-4134-85E6-F81E7A9D30BE}.Debug|x64.Build.0 = Debug|x64 + {01899CCE-8A2B-4134-85E6-F81E7A9D30BE}.Release|x64.ActiveCfg = Release|x64 + {01899CCE-8A2B-4134-85E6-F81E7A9D30BE}.Release|x64.Build.0 = Release|x64 + {0C56E492-CEB9-49C4-B127-FE437B55A879}.Debug|Win32.ActiveCfg = Debug|Win32 + {0C56E492-CEB9-49C4-B127-FE437B55A879}.Debug|Win32.Build.0 = Debug|Win32 + {0C56E492-CEB9-49C4-B127-FE437B55A879}.Release|Win32.ActiveCfg = Release|Win32 + {0C56E492-CEB9-49C4-B127-FE437B55A879}.Release|Win32.Build.0 = Release|Win32 + {0C56E492-CEB9-49C4-B127-FE437B55A879}.Debug|x64.ActiveCfg = Debug|x64 + {0C56E492-CEB9-49C4-B127-FE437B55A879}.Debug|x64.Build.0 = Debug|x64 + {0C56E492-CEB9-49C4-B127-FE437B55A879}.Release|x64.ActiveCfg = Release|x64 + {0C56E492-CEB9-49C4-B127-FE437B55A879}.Release|x64.Build.0 = Release|x64 + {213466E9-93D3-4D42-9BEE-F6DFBDC290BD}.Debug|Win32.ActiveCfg = Debug|Win32 + {213466E9-93D3-4D42-9BEE-F6DFBDC290BD}.Debug|Win32.Build.0 = Debug|Win32 + {213466E9-93D3-4D42-9BEE-F6DFBDC290BD}.Release|Win32.ActiveCfg = Release|Win32 + {213466E9-93D3-4D42-9BEE-F6DFBDC290BD}.Release|Win32.Build.0 = Release|Win32 + {213466E9-93D3-4D42-9BEE-F6DFBDC290BD}.Debug|x64.ActiveCfg = Debug|x64 + {213466E9-93D3-4D42-9BEE-F6DFBDC290BD}.Debug|x64.Build.0 = Debug|x64 + {213466E9-93D3-4D42-9BEE-F6DFBDC290BD}.Release|x64.ActiveCfg = Release|x64 + {213466E9-93D3-4D42-9BEE-F6DFBDC290BD}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {01899CCE-8A2B-4134-85E6-F81E7A9D30BE} = {F992E151-FEAC-4E8C-AF6A-082C91D6E54C} + {0C56E492-CEB9-49C4-B127-FE437B55A879} = {6D46B6EF-E350-4AFB-8AE4-470C61F8F756} + {213466E9-93D3-4D42-9BEE-F6DFBDC290BD} = {9A9322D5-3F21-4358-A801-916E04F462C8} + EndGlobalSection +EndGlobal diff --git a/hid/hidusbfx2/readme.htm b/hid/hidusbfx2/readme.htm new file mode 100644 index 000000000..116fb5b3a --- /dev/null +++ b/hid/hidusbfx2/readme.htm @@ -0,0 +1,1797 @@ + + + + + + + + +HID USB FX2 Sample + + + + + + + + + +
+ +

HIDUSBFX2

+ +

 

+ +

 

+ +

Summary

+ +

 

+ +

The +HIDUSBFX2 sample demonstrates how to write a HID minidriver by using the +Microsoft Windows Driver Foundation (WDF). The sample also demonstrates how to map +a non-HID USB device to a HID device. The minidriver is written for the OSR +USB-FX2 Learning Kit. This device is not HID-compliant and the sample exposes +the device as a HID device.

+ +

 

+ +

Background +Information

+ +

 

+ +

A +HID USB device provides a HID descriptor (through an interface descriptor) that +identifies the device as HID-compliant and enables the system-supplied HID +minidriver (hidusb.sys) and the HID class +driver to load, parse the HID descriptor, and enumerate child HID +device stacks. The system provides strong support for HID devices, so you do +not typically have to write a HID minidriver. However, there are cases in +which you might need to write your own HID minidriver (for example, if it is +difficult to make desired changes to HID-compliant device firmware or if you +need to make a non-HID compliant device a HID device without updating the +firmware).

+ +

Overview +of the Device

+ +

You +can view the specification for the device in the Using the OSR USB FX-2 Learning Kit document.

+ +

 

+ +

The +device is loosely based on the development board that is supplied with the +Cypress EZ-USB FX2 Development Kit (CY3681) and contains one interface +and three endpoints (Interrupt IN, Bulk Out, and Bulk IN). The firmware supports +vendor commands to query or set the LED bar graph display, 7-segment LED +display, and query toggle switch states.

+ +

The +interrupt endpoint sends an 8-bit value that represents the state of the +switches. This value is sent on startup, resume from suspend, and whenever the +switch pack setting changes. The firmware does not de-bounce the switch pack. +One switch change can cause multiple bytes to be sent. The bits are in the +reverse order of the labels on the pack (for example, bit 0x80 is labeled +1 on the pack). Bulk endpoints are configured for loopback.

+ +

Overview of the +Driver Stack

+ + + +

Kernel-Mode +Driver Framework (KMDF) does not support HID minidrivers natively because +the HID architecture requires that the HID class driver (hidclass.sys) own the driver dispatch table for +HID minidrivers. This requirement conflicts with KMDF’s +requirement that it own the driver dispatch table in order to handle +Plug and Play (PnP), power, and I/O requests correctly.

+ +

 

+ +

You +can resolve this ownership conflict by using a driver stack that consists of a +minimal WDM driver (also called a shim driver) as function driver and a +complete KMDF driver as lower filter driver. The shim driver registers with the +HID class (so hidclass.sys owns its dispatch +table) and forwards all of the requests to the lower filter driver. The +lower filter driver (KMDF owns the dispatch table) processes all of the +requests. 

+ +

 

+ +

The +WDK provides code for both of the drivers. The shim driver code is located +in the src\hid\hidmapper\kmdf folder (the driver binary is named hidkmdf.sys), and the lower filter driver code is +located in the src\hid\hidusbfx2\sys folder (the binary is named hidusbfx2.sys). The shim driver is a minimal WDM driver +and you can reuse it without any modification. Remember to rename the +shim driver binary when you reuse it, to avoid a name conflict. Also, note that +shim driver is an inbox driver on Windows 7 and later operating systems (mshidkmdf.sys). INF file for this sample contains a +specific section that allows using the inbox shim driver on Windows 7 and later +OS.

+ +

 

+ +

 

+ +

Mapping +non-HID USB device to HID

+ +

 

+ +

When the +HID class driver queries the minidriver, the minidriver returns a hard-coded +report descriptor that enables the HID class driver to create child +devices as described by the report descriptor. The report descriptor +has three top-level application collections:

+ +
    +
  • Consumer Control
  • +
  • System Control
  • +
  • Vendor-defined
  • +
+ +

The +HID class driver creates a driver stack for each top-level collection. The operating +system opens the Consumer Control and System Control collections. These +collections have input buttons and obtain data from the interrupt endpoint of +the USB device. The vendor-defined collection exposes a feature button to +control the 7-segment display and bar graph display. Any client application can +open the vendor-defined collection to send feature requests.

+ +

 

+ +

 

+ +

Switch +Pack Mapping

+ +

 

+ +

The +switch pack on the USB device is mapped as hot keys that are commonly found on +modern keyboards. This mapping is possible by exposing the switch pack as +two system-supported collections: Consumer Control and System Control. The +Consumer Control collection provides a mapping for some application-launch and +application-action keys, as shown in the following table. The System +Control collection provides a mapping for the power sleep function. 

+ +

 

+ + + + + + + + + + + + + + + + + + + + + + + + +
+

Switch

+
+

1

+
+

2

+
+

3

+
+

4

+
+

5

+
+

6

+
+

7

+
+

8

+
+

Mapping

+
+

Sleep

+
+

Calculator

+
+

Mail

+
+

Favorites

+
+

Refresh

+
+

Forward

+
+

Back

+
+

Browser

+
+ +

 

+ +

 

+ +

Segment +Display and Bar Graph Mapping

+ +

 

+ +

Segment +display and Bar Graph are mapped as HID feature controls that can be +manipulated by using HidD_SetFeature() api from a user mode application. The +feature controls are mapped as Vendor-defined Usage Page 0xff00 with Usage ID +from 1 to 18. Hidclient.exe, a GUI application available in WDK, can also be +used to do this. See following sections for more information.

+ +

The +segment display and bar graph are mapped as HID feature controls that you +can manipulate by using the HidD_SetFeature function from a user-mode +application. The feature controls are mapped as vendor-defined usage page +0xff00 with a usage ID from 1-18. You can also use Hidclient.exe, an application that is available in the +WDK, to manipulate the segment display and bar graph. For more +information about this mapping, see the following two tables.

+ +

 

+ +

 

+ +

Segment Display +Mapping

+ +

 

+ + + + + + + + + + + + + + + + + + + + + + + + +
+

Usage + ID

+
+

1

+
+

2

+
+

3

+
+

4

+
+

5

+
+

6

+
+

7

+
+

8

+
+

Mapping

+
+

Display 1

+
+

Display 2

+
+

Display 3

+
+

Display 4

+
+

Display 5

+
+

Display 6

+
+

Display 7

+
+

Display 8

+
+ +

 

+ +

Bar Graph Mapping

+ +

 

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

Usage + ID

+
+

9

+
+

10

+
+

11

+
+

12

+
+

13

+
+

14

+
+

15

+
+

16

+
+

17

+
+

18

+
+

 Mapping

+
+

LED 1 ON

+
+

LED 2

+

ON

+
+

LED 3

+

ON

+
+

LED 4

+

ON

+
+

LED 5

+

ON

+
+

LED 6

+

ON

+
+

LED 7

+

ON

+
+

LED 8

+

ON

+
+

All LEDS ON

+
+

All LEDS OFF

+
+ +

 

+ +

 

+ +

Support +for Selective Suspend

+ +

 

+ +

The +HID class driver provides support for selective suspend. The minidriver +participates in this feature by handling HID class IOCTLs appropriately. To +enable the selective suspend feature for your device, you need to add a +“SelectiveSuspend” = 1 value in the registry in the device hardware key through +the INF file. For an example, see the hidusbfx2.inf file.

+ +

 

+ +

Installing +the Sample

+ +

 

+ +

If +you adapt this driver for your device, update the INF file to match the hardware +ID (VID, PID) and the device description text to match your test board/device.

+ +

 

+ +

To +start installing the sample, you must:

+ +
    +
  1. Build the driver and copy the + following files to a folder on your hard drive:
  2. +
      +
    • hidusbfx2.inf +
    • +
    • Hidusbfx2.sys +
    • +
    • Hidkmdf.sys + (build <WDK ROOT>/src/hid/hidmapper/kmdf folder for this)
    • +
    • The WDF + coinstaller from the <WDK + ROOT>\redist\wdf\<platform> folder.
    • +
    +
  3. Plug in the device and do the + following, depending on the operating system that you are using:
  4. +
      +
    • On Windows 7 + and later operating systems:
    • +
        +
      1. Launch Device Manager by executing + command devmgmt.msc in a command window, or from Hardware and Sound program + group in Control Panel.
      2. +
      3. Select OSR + USB-FX2 device from Other Devices category + and click Update + Driver Software… from right-click + menu.
      4. +
      5. Select Browse my computer for software and provide the location + of the driver files.
      6. +
      7. Select Install this driver software + anyway when the Windows + Security dialog box appears.
      8. +
      9. After the + driver is installed, you should see the device in Device Manager under + "Human Interface Devices".
      10. +
      +
    • On Windows + Vista and Windows Server 2008:
    • +
        +
      1. In the Found New Hardware dialog + box, select Locate + and install driver software (recommended).
      2. +
      3. Select Don't search online. +
      4. +
      5. Select I don't have the disc. Show + me other options.
      6. +
      7. Select Browse my computer for driver + software (advanced).
      8. +
      9. Enter the + location of the driver files, and select Install this driver software anyway when + the Windows + Security dialog box appears. After the driver is + installed, you should see the device in Device Manager under "Human + Interface Devices".
      10. +
      +
    • On Windows + Server 2003 and Windows XP:
    • +
        +
      1. In the Found New Hardware Wizard dialog + box, select Install + from a list of specific location (Advanced).
      2. +
      3. Select Search the best driver in + these locations, select Include this location in the search path, + and then specify the target media or the directory where the INF and SYS + files are copied. The system will scan the directory and pick up the + matching INF file and start the installation.
      4. +
      5. When you + receive a Hardware + Installation Warning dialog box that states that your + driver has not passed Windows Logo Testing, click Continue Anyway. + The system will copy the driver and INF file, load the driver, and start + the device.
      6. +
      7. When you see + the Completing the + Found New Hardware Wizard page, click Finish. The + installation is complete. You should be able to see the device in Device + Manager under "Human Interface Devices".
      8. +
      +
    • On Windows + 2000:
    • +
        +
      1. In the Found New Hardware Wizard + dialog box, click Next. +
      2. +
      3. Select Display a list of known + drivers for this device, and then click Next.
      4. +
      5. Select Other Devices, + and then click Next. +
      6. +
      7. Click Have Disk, + specify the target media or the directory where the INF and SYS files + are copied, and then click Okay. +
      8. +
      9. In the Manufacturer section, + select Vendor + Name, and the click Next.
      10. +
      11. Click Next, and + then click Finish + in the Completing + the Add/Remove Hardware Wizard page.
      12. +
      +
    +
+ +

  

+ +

Testing

+ +

 

+ +

Testing Switches

+ +
    +
  • Toggle switch number 8 on the device + board to the on position (toggle down) to open a Web browser.
  • +
  • Toggle switch number 2 on the device + board to the on position (toggle down) to start the calculator + application.
  • +
+ +

 

+ +

Testing Bar Graph +and 7-Segment Display

+ +
    +
  1. Start the hidclient.exe GUI application from the WDK. + The application source code is located in the <WDK Root>\src\hid\hclient folder, and + you need to build it by using the appropriate build environment.
  2. +
  3. From the HID Device to examine menu, select + the device that contains “UsagePage 0ff00, Usage 01” as a substring.
  4. +
  5. Click Modify Features. The Feature Data dialog box opens.
  6. +
  7. Click Modify Features. The Modify features dialog box opens.
  8. +
  9. In the input box, type 7 and click Send to Device. You should see number 7 + appear in the 7-segment display.
  10. +
  11. Type any number from 1-8, and you will + see the respective number displayed in the 7-segment display.
  12. +
  13. Type any number from (and + including) 9-17, and you will see one of the LEDs on the bar graph turn + on. For mapping information, see the earlier table.
  14. +
+ +

 

+ +

Report +Descriptor

+ +

 

+ +

    +// Consumer control collection

+ +

    +0x05,0x0C,              +// USAGE_PAGE (Consumer Page)

+ +

    +0x09,0x01,              +// USAGE (Consumer Control Usage 0x01)

+ +

    +0xA1,0x01,              +// COLLECTION (Application)

+ +

    +0x85,0x01,              +//   REPORT_ID

+ +

    +0x0A, 0x23, 0x02,    //   USAGE (Usage Browser)

+ +

    +0x0A, 0x24, 0x02,    //   USAGE (Usage AC Back)

+ +

    +0x0A, 0x25, 0x02,    //   USAGE (Usage AC Forward)

+ +

    +0x0A, 0x27, 0x02,    //   USAGE (Usage AC Refresh)

+ +

    +0x0A, 0x2A, 0x02,    //   USAGE (Usage AC BookMarks)

+ +

    +0x0A, 0x8A, 0x01,    //   USAGE (Usage AL Mail)

+ +

    +0x0A, 0x92, 0x01,    //   USAGE (Usage AL Calculator )

+ +

    +0x15, 0x00,            +//   LOGICAL_MINIMUM(0)

+ +

    +0x25, 0x01,            +//   LOGICAL_MAXIMUM(1) 

+ +

    +0x75, 0x01,            +//   REPORT_SIZE

+ +

    +0x95, 0x07,            +//   REPORT_COUNT

+ +

    +0x81, 0x02,            +//   INPUT (Data, Variable,Abs)

+ +

    +0x75, 0x01,            +//   REPORT_SIZE

+ +

    +0x95, 0x01,            +//   REPORT_COUNT

+ +

    +0x81, 0x07,            +//   INPUT (const)

+ +

    +0xC0,                    +// END_COLLECTION

+ +

    +// system control collection

+ +

    +0x05, 0x01,           + // Usage Page (Generic Desktop)

+ +

    +0x09, 0x80,           + // Usage (System Control)

+ +

    +0xA1, 0x01,           // +Collection (Application)

+ +

    +0x85, 0x02,          + //   Report ID

+ +

    +0x95, 0x07,          + //   Report Count

+ +

    +0x81, 0x07,        +   //   Input (Constant)  

+ +

    +0x09, 0x82,          + //   Usage (System Sleep)

+ +

    +0x95, 0x01,          + //   Report Count (2)

+ +

    +0x81, 0x06,          +//   Input (Data, Variable, Relative, Preferred)

+ +

    +0xC0,                  +// End Collection

+ +

    +// Feature collection

+ +

    +0x06,0x00, 0xFF,    // USAGE_PAGE (Vender Defined Usage Page)

+ +

    +0x09,0x01,            // +USAGE (Vendor Usage 0x01)

+ +

    +0xA1,0x01,            // +COLLECTION (Application)

+ +

    +0x85,0x03,            // +Report ID

+ +

    +0x19,0x01,           + //   USAGE MINIMUM

+ +

    +0x29,0x20,           + //   USAGE MAXIMUM

+ +

    +0x15,0x00,           + //   LOGICAL_MINIMUM(1)

+ +

    +0x26,0xff, 0x00,    //   LOGICAL_MAXIMUM(255)

+ +

    +0x75,0x08,          + //   REPORT_SIZE

+ +

    +0x95,0x01,          + //   REPORT_COUNT

+ +

    +0xB1,0x00,          + //   Feature (Data,Ary,Abs)

+ +

    +0xC0                   +// END_COLLECTION

+ +

 

+ +

 

+ +

CODE +TOUR

+ +

This section includes a file manifest of all the files +in the src\hid\hidusbfx2 directory and src\hid\hidmapper\kmdf directory..

+ +

 

+ +

File Manifest

+ +

src\hid\hidmapper\kmdf

+ +

 

+ +

Files Description

+ +

hidkmdf.c Contains code for +driver entry and dispatch.

+ +

Sources WDK sources file.

+ +

Makefile WDK build environment +makefile.

+ +

hidkmdf.rc Resource file for +the driver.

+ +

 

+ +

src\hid\hidusbfx2\sys

+ +

 

+ +

Files            Description

+ +

Driver.c         Contains code for driver entry and +dispatch functions.

+ +

hid.c            Contains code for handling HID +ioctls.

+ +

usb.c            Contains code for communicating +with USB stack.

+ +

Trace.h          Contains trace related definitions.

+ +

Hidusbfx2.h      Contains type definitions and function +declarations

+ +

Hidusbfx2.rc     Resource file for the driver.

+ +

Hidusbfx2.inx    INX file for the driver.

+ +

Sources          WDK sources file.

+ +

Makefile         WDK build environment make file.

+ +

Makefile.inc     A makefile that defines custom build +actions. This includes the conversion of the .INX file into a .INF file.

+ +

 

+ +
+ + + + diff --git a/hid/hidusbfx2/sys/driver.c b/hid/hidusbfx2/sys/driver.c new file mode 100644 index 000000000..8aaaf294d --- /dev/null +++ b/hid/hidusbfx2/sys/driver.c @@ -0,0 +1,371 @@ +/*++ + +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: + + Code for main entry point of KMDF driver + +Author: + + +Environment: + + kernel mode only + +Revision History: + +--*/ + +#include + +#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 "driver.tmh" +#else +ULONG DebugLevel = TRACE_LEVEL_INFORMATION; +ULONG DebugFlag = 0xff; +#endif + +#ifdef ALLOC_PRAGMA + #pragma alloc_text( INIT, DriverEntry ) + #pragma alloc_text( PAGE, HidFx2EvtDeviceAdd) + #pragma alloc_text( PAGE, HidFx2EvtDriverContextCleanup) +#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. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + WDF_DRIVER_CONFIG config; + WDF_OBJECT_ATTRIBUTES attributes; + + // + // Initialize WPP Tracing + // + WPP_INIT_TRACING( DriverObject, RegistryPath ); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, + "HIDUSBFX2 Driver Sample Built %s %s\n", __DATE__, __TIME__); + + WDF_DRIVER_CONFIG_INIT(&config, HidFx2EvtDeviceAdd); + + // + // 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 = HidFx2EvtDriverContextCleanup; + + // + // Create a framework driver object to represent our driver. + // + status = WdfDriverCreate(DriverObject, + RegistryPath, + &attributes, // Driver Attributes + &config, // Driver Config Info + WDF_NO_HANDLE + ); + + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, + "WdfDriverCreate failed with status 0x%x\n", status); + + WPP_CLEANUP(DriverObject); + } + + return status; +} + + +NTSTATUS +HidFx2EvtDeviceAdd( + IN WDFDRIVER Driver, + IN PWDFDEVICE_INIT DeviceInit + ) +/*++ +Routine Description: + + HidFx2EvtDeviceAdd 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; + WDF_IO_QUEUE_CONFIG queueConfig; + WDF_OBJECT_ATTRIBUTES attributes; + WDFDEVICE hDevice; + PDEVICE_EXTENSION devContext = NULL; + WDFQUEUE queue; + WDF_PNPPOWER_EVENT_CALLBACKS pnpPowerCallbacks; + WDF_TIMER_CONFIG timerConfig; + WDFTIMER timerHandle; + + UNREFERENCED_PARAMETER(Driver); + + PAGED_CODE(); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, + "HidFx2EvtDeviceAdd called\n"); + + // + // Tell framework this is a filter driver. Filter drivers by default are + // not power policy owners. This works well for this driver because + // HIDclass driver is the power policy owner for HID minidrivers. + // + WdfFdoInitSetFilter(DeviceInit); + + // + // Initialize pnp-power callbacks, attributes and a context area for the device object. + // + // + WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpPowerCallbacks); + + // + // For usb devices, PrepareHardware callback is the to place select the + // interface and configure the device. + // + pnpPowerCallbacks.EvtDevicePrepareHardware = HidFx2EvtDevicePrepareHardware; + + // + // These two callbacks start and stop the wdfusb pipe continuous reader + // as we go in and out of the D0-working state. + // + pnpPowerCallbacks.EvtDeviceD0Entry = HidFx2EvtDeviceD0Entry; + pnpPowerCallbacks.EvtDeviceD0Exit = HidFx2EvtDeviceD0Exit; + + WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpPowerCallbacks); + + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, DEVICE_EXTENSION); + + // + // 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, &attributes, &hDevice); + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "WdfDeviceCreate failed with status code 0x%x\n", status); + return status; + } + + devContext = GetDeviceContext(hDevice); + + WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&queueConfig, WdfIoQueueDispatchParallel); + queueConfig.EvtIoInternalDeviceControl = HidFx2EvtInternalDeviceControl; + + status = WdfIoQueueCreate(hDevice, + &queueConfig, + WDF_NO_OBJECT_ATTRIBUTES, + &queue + ); + if (!NT_SUCCESS (status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "WdfIoQueueCreate failed 0x%x\n", status); + return status; + } + + // + // Register a manual I/O queue for handling Interrupt Message Read Requests. + // This queue will be used for storing Requests that need to wait for an + // interrupt to occur before they can be completed. + // + WDF_IO_QUEUE_CONFIG_INIT(&queueConfig, WdfIoQueueDispatchManual); + + // + // This queue is used for requests that dont directly access the device. The + // requests in this queue are serviced only when the device is in a fully + // powered state and sends an interrupt. So we can use a non-power managed + // queue to park the requests since we dont care whether the device is idle + // or fully powered up. + // + queueConfig.PowerManaged = WdfFalse; + + status = WdfIoQueueCreate(hDevice, + &queueConfig, + WDF_NO_OBJECT_ATTRIBUTES, + &devContext->InterruptMsgQueue + ); + + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "WdfIoQueueCreate failed 0x%x\n", status); + return status; + } + + // + // Create a timer to handle debouncing of switchpack + // + WDF_TIMER_CONFIG_INIT( + &timerConfig, + HidFx2EvtTimerFunction + ); + timerConfig.AutomaticSerialization = FALSE; + + WDF_OBJECT_ATTRIBUTES_INIT(&attributes); + attributes.ParentObject = hDevice; + status = WdfTimerCreate( + &timerConfig, + &attributes, + &timerHandle + ); + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "WdfTimerCreate failed status:0x%x\n", status); + return status; + } + + devContext->DebounceTimer = timerHandle; + return status; +} + + +VOID +HidFx2EvtDriverContextCleanup( + IN WDFOBJECT Object + ) +/*++ +Routine Description: + + Free resources allocated in DriverEntry that are not automatically + cleaned up framework. + +Arguments: + + Driver - handle to a WDF Driver object. + +Return Value: + + VOID. + +--*/ +{ + PAGED_CODE (); + UNREFERENCED_PARAMETER(Object); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "Exit HidFx2EvtDriverContextCleanup\n"); + + WPP_CLEANUP(WdfDriverWdmGetDriverObject((WDFDRIVER) Object)); +} + + +#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 0x%x\n", status); + return; + } + if (TraceEventsLevel <= TRACE_LEVEL_ERROR || + (TraceEventsLevel <= DebugLevel && + ((TraceEventsFlag & DebugFlag) == TraceEventsFlag))) { + DbgPrint("%s%s", _DRIVER_NAME_, debugMessageBuffer); + } + } + va_end(list); + + return; +#else + UNREFERENCED_PARAMETER(TraceEventsLevel); + UNREFERENCED_PARAMETER(TraceEventsFlag); + UNREFERENCED_PARAMETER(DebugMessage); +#endif +} + +#endif + diff --git a/hid/hidusbfx2/sys/hid.c b/hid/hidusbfx2/sys/hid.c new file mode 100644 index 000000000..362ed5152 --- /dev/null +++ b/hid/hidusbfx2/sys/hid.c @@ -0,0 +1,1020 @@ +/*++ + +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: + + hid.c + +Abstract: + + Code for handling HID related requests + +Author: + + +Environment: + + kernel mode only + +Revision History: + +--*/ + +#define USE_HARDCODED_HID_REPORT_DESCRIPTOR + +#include + +#if defined(EVENT_TRACING) +#include "hid.tmh" +#endif + +#ifdef ALLOC_PRAGMA + #pragma alloc_text( PAGE, HidFx2SetFeature) + #pragma alloc_text( PAGE, HidFx2GetFeature) + #pragma alloc_text( PAGE, SendVendorCommand) + #pragma alloc_text( PAGE, GetVendorData) +#endif + +VOID +HidFx2EvtInternalDeviceControl( + 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_INTERNAL 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; + WDFDEVICE device; + PDEVICE_EXTENSION devContext = NULL; + ULONG bytesReturned = 0; + + UNREFERENCED_PARAMETER(OutputBufferLength); + UNREFERENCED_PARAMETER(InputBufferLength); + + device = WdfIoQueueGetDevice(Queue); + devContext = GetDeviceContext(device); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_IOCTL, + "%s, Queue:0x%p, Request:0x%p\n", + DbgHidInternalIoctlString(IoControlCode), + Queue, + Request + ); + + // + // Please note that HIDCLASS provides the buffer in the Irp->UserBuffer + // field irrespective of the ioctl buffer type. However, framework is very + // strict about type checking. You cannot get Irp->UserBuffer by using + // WdfRequestRetrieveOutputMemory if the ioctl is not a METHOD_NEITHER + // internal ioctl. So depending on the ioctl code, we will either + // use retreive function or escape to WDM to get the UserBuffer. + // + + switch(IoControlCode) { + + case IOCTL_HID_GET_DEVICE_DESCRIPTOR: + // + // Retrieves the device's HID descriptor. + // + status = HidFx2GetHidDescriptor(device, Request); + break; + + case IOCTL_HID_GET_DEVICE_ATTRIBUTES: + // + //Retrieves a device's attributes in a HID_DEVICE_ATTRIBUTES structure. + // + status = HidFx2GetDeviceAttributes(Request); + break; + + case IOCTL_HID_GET_REPORT_DESCRIPTOR: + // + //Obtains the report descriptor for the HID device. + // + status = HidFx2GetReportDescriptor(device, Request); + break; + + case IOCTL_HID_READ_REPORT: + + // + // Returns a report from the device into a class driver-supplied buffer. + // For now queue the request to the manual queue. The request will + // be retrived and completd when continuous reader reads new data + // from the device. + // + status = WdfRequestForwardToIoQueue(Request, devContext->InterruptMsgQueue); + + if(!NT_SUCCESS(status)){ + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, + "WdfRequestForwardToIoQueue failed with status: 0x%x\n", status); + + WdfRequestComplete(Request, status); + } + + return; + +// +// This feature is only supported on WinXp and later. Compiling in W2K +// build environment will fail without this conditional preprocessor statement. +// + case IOCTL_HID_SEND_IDLE_NOTIFICATION_REQUEST: + + // + // Hidclass sends this IOCTL for devices that have opted-in for Selective + // Suspend feature. This feature is enabled by adding a registry value + // "SelectiveSuspendEnabled" = 1 in the hardware key through inf file + // (see hidusbfx2.inf). Since hidclass is the power policy owner for + // this stack, it controls when to send idle notification and when to + // cancel it. This IOCTL is passed to USB stack. USB stack pends it. + // USB stack completes the request when it determines that the device is + // idle. Hidclass's idle notification callback get called that requests a + // wait-wake Irp and subsequently powers down the device. + // The device is powered-up either when a handle is opened for the PDOs + // exposed by hidclass, or when usb stack completes wait + // wake request. In the first case, hidclass cancels the notification + // request (pended with usb stack), cancels wait-wake Irp and powers up + // the device. In the second case, an external wake event triggers completion + // of wait-wake irp and powering up of device. + // + status = HidFx2SendIdleNotification(Request); + + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, + "SendIdleNotification failed with status: 0x%x\n", status); + + WdfRequestComplete(Request, status); + } + + return; + + case IOCTL_HID_SET_FEATURE: + // + // This sends a HID class feature report to a top-level collection of + // a HID class device. + // + status = HidFx2SetFeature(Request); + WdfRequestComplete(Request, status); + return; + + case IOCTL_HID_GET_FEATURE: + // + // Get a HID class feature report from a top-level collection of + // a HID class device. + // + status = HidFx2GetFeature(Request, &bytesReturned); + WdfRequestCompleteWithInformation(Request, status, bytesReturned); + return; + + case IOCTL_HID_WRITE_REPORT: + // + //Transmits a class driver-supplied report to the device. + // + case IOCTL_HID_GET_STRING: + // + // Requests that the HID minidriver retrieve a human-readable string + // for either the manufacturer ID, the product ID, or the serial number + // from the string descriptor of the device. The minidriver must send + // a Get String Descriptor request to the device, in order to retrieve + // the string descriptor, then it must extract the string at the + // appropriate index from the string descriptor and return it in the + // output buffer indicated by the IRP. Before sending the Get String + // Descriptor request, the minidriver must retrieve the appropriate + // index for the manufacturer ID, the product ID or the serial number + // from the device extension of a top level collection associated with + // the device. + // + case IOCTL_HID_ACTIVATE_DEVICE: + // + // Makes the device ready for I/O operations. + // + case IOCTL_HID_DEACTIVATE_DEVICE: + // + // Causes the device to cease operations and terminate all outstanding + // I/O requests. + // + default: + status = STATUS_NOT_SUPPORTED; + break; + } + + WdfRequestComplete(Request, status); + + return; +} + + +NTSTATUS +HidFx2SetFeature( + IN WDFREQUEST Request + ) +/*++ + +Routine Description + + This routine sets the state of the Feature: in this + case Segment Display on the USB FX2 board. + +Arguments: + + Request - Wdf Request + +Return Value: + + NT status value + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + PHID_XFER_PACKET transferPacket = NULL; + WDF_REQUEST_PARAMETERS params; + PHIDFX2_FEATURE_REPORT featureReport = NULL; + WDFDEVICE device; + UCHAR featureUsage = 0; + + PAGED_CODE(); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL, "HidFx2SetFeature Enter\n"); + + device = WdfIoQueueGetDevice(WdfRequestGetIoQueue(Request)); + + WDF_REQUEST_PARAMETERS_INIT(¶ms); + WdfRequestGetParameters(Request, ¶ms); + + // + // IOCTL_HID_SET_FEATURE & IOCTL_HID_GET_FEATURE are not METHOD_NIEHTER + // IOCTLs. So you cannot retreive UserBuffer from the IRP using Wdf + // function. As a result we have to escape out to WDM to get the UserBuffer + // directly from the IRP. + // + if (params.Parameters.DeviceIoControl.InputBufferLength < sizeof(HID_XFER_PACKET)) { + status = STATUS_BUFFER_TOO_SMALL; + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, + "Userbuffer is small 0x%x\n", status); + return status; + } + + // + // This is a kernel buffer so no need for try/except block when accesssing + // Irp->UserBuffer. + // + transferPacket = (PHID_XFER_PACKET) WdfRequestWdmGetIrp(Request)->UserBuffer; + if (transferPacket == NULL) { + status = STATUS_INVALID_DEVICE_REQUEST; + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, + "Irp->UserBuffer is NULL 0x%x\n", status); + return status; + } + + if (transferPacket->reportBufferLen == 0){ + status = STATUS_BUFFER_TOO_SMALL; + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, + "HID_XFER_PACKET->reportBufferLen is 0, 0x%x\n", status); + return status; + } + + if (transferPacket->reportBufferLen < sizeof(UCHAR)){ + status = STATUS_BUFFER_TOO_SMALL; + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, + "HID_XFER_PACKET->reportBufferLen is too small, 0x%x\n", status); + return status; + } + + featureReport = (PHIDFX2_FEATURE_REPORT)transferPacket->reportBuffer; + featureUsage = featureReport->FeatureData; + + // + // The feature reports map directly to the command + // data that is sent down to the device. + // + if (transferPacket->reportId == SEVEN_SEGMENT_REPORT_ID) + { + status = SendVendorCommand( + device, + HIDFX2_SET_7SEGMENT_DISPLAY, + &featureUsage + ); + } + else if (transferPacket->reportId == BARGRAPH_REPORT_ID) + { + status = SendVendorCommand( + device, + HIDFX2_SET_BARGRAPH_DISPLAY, + &featureUsage + ); + } + else + { + status = STATUS_INVALID_DEVICE_REQUEST; + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, + "Incorrect report ID, 0x%x\n", status); + return status; + } + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL, "HidFx2SetFeature Exit\n"); + return status; +} + +NTSTATUS +HidFx2GetFeature( + IN WDFREQUEST Request, + OUT PULONG BytesReturned + ) +/*++ + +Routine Description + + This routine gets the state of the Feature: in this + case Segment Display or bargraph display on the USB FX2 board. + +Arguments: + + Request - Wdf Request + + BytesReturned - Size of buffer returned for the request + +Return Value: + + NT status value + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + PHID_XFER_PACKET transferPacket = NULL; + WDF_REQUEST_PARAMETERS params; + PHIDFX2_FEATURE_REPORT featureReport = NULL; + WDFDEVICE device; + + PAGED_CODE(); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL, "HidFx2GetFeature Enter\n"); + + *BytesReturned = 0; + + device = WdfIoQueueGetDevice(WdfRequestGetIoQueue(Request)); + + WDF_REQUEST_PARAMETERS_INIT(¶ms); + WdfRequestGetParameters(Request, ¶ms); + + // + // IOCTL_HID_SET_FEATURE & IOCTL_HID_GET_FEATURE are not METHOD_NIEHTER + // IOCTLs. So you cannot retreive UserBuffer from the IRP using Wdf + // function. As a result we have to escape out to WDM to get the UserBuffer + // directly from the IRP. + // + if (params.Parameters.DeviceIoControl.OutputBufferLength < sizeof(HID_XFER_PACKET)) { + status = STATUS_BUFFER_TOO_SMALL; + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, + "Userbuffer is small 0x%x\n", status); + return status; + } + + // + // This is a kernel buffer so no need for try/except block when accesssing + // Irp->UserBuffer. + // + transferPacket = (PHID_XFER_PACKET) WdfRequestWdmGetIrp(Request)->UserBuffer; + if (transferPacket == NULL) { + status = STATUS_INVALID_DEVICE_REQUEST; + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, + "Irp->UserBuffer is NULL 0x%x\n", status); + return status; + } + + if (transferPacket->reportBufferLen == 0){ + status = STATUS_BUFFER_TOO_SMALL; + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, + "HID_XFER_PACKET->reportBufferLen is 0, 0x%x\n", status); + return status; + } + + if (transferPacket->reportBufferLen < sizeof(UCHAR)){ + status = STATUS_BUFFER_TOO_SMALL; + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, + "HID_XFER_PACKET->reportBufferLen is too small, 0x%x\n", status); + return status; + } + + featureReport = (PHIDFX2_FEATURE_REPORT)transferPacket->reportBuffer; + + if (transferPacket->reportId == SEVEN_SEGMENT_REPORT_ID) + { + status = GetVendorData( + device, + HIDFX2_READ_7SEGMENT_DISPLAY, + &featureReport->FeatureData + ); + } + else if (transferPacket->reportId == BARGRAPH_REPORT_ID) + { + status = GetVendorData( + device, + HIDFX2_READ_BARGRAPH_DISPLAY, + &featureReport->FeatureData + ); + } + else + { + status = STATUS_INVALID_DEVICE_REQUEST; + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, + "Incorrect report ID, 0x%x\n", status); + return status; + } + + *BytesReturned = sizeof (HIDFX2_FEATURE_REPORT); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL, "HidFx2GetFeature Exit\n"); + return status; +} + +NTSTATUS +SendVendorCommand( + IN WDFDEVICE Device, + IN UCHAR VendorCommand, + IN PUCHAR CommandData + ) +/*++ + +Routine Description + + This routine sets the state of the Feature: in this + case Segment Display on the USB FX2 board. + +Arguments: + + Request - Wdf Request + +Return Value: + + NT status value + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + ULONG bytesTransferred =0; + PDEVICE_EXTENSION pDevContext = NULL; + WDF_MEMORY_DESCRIPTOR memDesc; + WDF_USB_CONTROL_SETUP_PACKET controlSetupPacket; + WDF_REQUEST_SEND_OPTIONS sendOptions; + + PAGED_CODE(); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL, "SendVendorCommand Enter\n"); + + pDevContext = GetDeviceContext(Device); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_IOCTL, + "SendVendorCommand: Command:0x%x, data: 0x%x\n", + VendorCommand, *CommandData); + + // + // set the segment state on the USB device + // + // Send the I/O with a timeout. We do that because we send the + // I/O in the context of the user thread and if it gets stuck, it would + // prevent the user process from existing. + // + WDF_REQUEST_SEND_OPTIONS_INIT(&sendOptions, + WDF_REQUEST_SEND_OPTION_TIMEOUT); + + WDF_REQUEST_SEND_OPTIONS_SET_TIMEOUT(&sendOptions, + WDF_REL_TIMEOUT_IN_SEC(5)); + + WDF_USB_CONTROL_SETUP_PACKET_INIT_VENDOR(&controlSetupPacket, + BmRequestHostToDevice, + BmRequestToDevice, + VendorCommand, // Request + 0, // Value + 0); // Index + + WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&memDesc, + CommandData, + sizeof(UCHAR)); + + status = WdfUsbTargetDeviceSendControlTransferSynchronously( + pDevContext->UsbDevice, + WDF_NO_HANDLE, // Optional WDFREQUEST + &sendOptions, // PWDF_REQUEST_SEND_OPTIONS + &controlSetupPacket, + &memDesc, + &bytesTransferred + ); + + if(!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, + "SendtVendorCommand: Failed to set Segment Display state - 0x%x \n", + status); + } + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL, "SendVendorCommand Exit\n"); + + return status; +} + +NTSTATUS +GetVendorData( + IN WDFDEVICE Device, + IN UCHAR VendorCommand, + IN PUCHAR CommandData + ) +/*++ + +Routine Description + + This routine sets the state of the Feature: in this + case Segment Display on the USB FX2 board. + +Arguments: + + Request - Wdf Request + +Return Value: + + NT status value + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + ULONG bytesTransferred =0; + PDEVICE_EXTENSION pDevContext = NULL; + WDF_MEMORY_DESCRIPTOR memDesc; + WDF_USB_CONTROL_SETUP_PACKET controlSetupPacket; + WDF_REQUEST_SEND_OPTIONS sendOptions; + + PAGED_CODE(); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL, "GetVendorData Enter\n"); + + pDevContext = GetDeviceContext(Device); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_IOCTL, + "GetVendorData: Command:0x%x, data: 0x%x\n", + VendorCommand, *CommandData); + + // + // Get the display state from the USB device + // + // Send the I/O with a timeout. We do that because we send the + // I/O in the context of the user thread and if it gets stuck, it would + // prevent the user process from existing. + // + WDF_REQUEST_SEND_OPTIONS_INIT(&sendOptions, + WDF_REQUEST_SEND_OPTION_TIMEOUT); + + WDF_REQUEST_SEND_OPTIONS_SET_TIMEOUT(&sendOptions, + WDF_REL_TIMEOUT_IN_SEC(5)); + + WDF_USB_CONTROL_SETUP_PACKET_INIT_VENDOR(&controlSetupPacket, + BmRequestDeviceToHost, + BmRequestToDevice, + VendorCommand, // Request + 0, // Value + 0); // Index + + WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&memDesc, + CommandData, + sizeof(UCHAR)); + + status = WdfUsbTargetDeviceSendControlTransferSynchronously( + pDevContext->UsbDevice, + WDF_NO_HANDLE, // Optional WDFREQUEST + &sendOptions, // PWDF_REQUEST_SEND_OPTIONS + &controlSetupPacket, + &memDesc, + &bytesTransferred + ); + + if(!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, + "GetVendorData: Failed to get state - 0x%x \n", + status); + } + else + { + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_IOCTL, + "GetVendorData: Command:0x%x, data after command: 0x%x\n", + VendorCommand, *CommandData); + } + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL, "GetVendorData Exit\n"); + + return status; +} + + +NTSTATUS +HidFx2GetHidDescriptor( + IN WDFDEVICE Device, + IN WDFREQUEST Request + ) +/*++ + +Routine Description: + + Finds the HID descriptor and copies it into the buffer provided by the + Request. + +Arguments: + + Device - Handle to WDF Device Object + + Request - Handle to request object + +Return Value: + + NT status code. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + size_t bytesToCopy = 0; + WDFMEMORY memory; + + UNREFERENCED_PARAMETER(Device); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL, + "HidFx2GetHidDescriptor Entry\n"); + + // + // This IOCTL is METHOD_NEITHER so WdfRequestRetrieveOutputMemory + // will correctly retrieve buffer from Irp->UserBuffer. + // Remember that HIDCLASS provides the buffer in the Irp->UserBuffer + // field irrespective of the ioctl buffer type. However, framework is very + // strict about type checking. You cannot get Irp->UserBuffer by using + // WdfRequestRetrieveOutputMemory if the ioctl is not a METHOD_NEITHER + // internal ioctl. + // + status = WdfRequestRetrieveOutputMemory(Request, &memory); + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, + "WdfRequestRetrieveOutputMemory failed 0x%x\n", status); + return status; + } + + // + // Use hardcoded "HID Descriptor" + // + bytesToCopy = G_DefaultHidDescriptor.bLength; + + if (bytesToCopy == 0) { + status = STATUS_INVALID_DEVICE_STATE; + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, + "G_DefaultHidDescriptor is zero, 0x%x\n", status); + return status; + } + + status = WdfMemoryCopyFromBuffer(memory, + 0, // Offset + (PVOID) &G_DefaultHidDescriptor, + bytesToCopy); + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, + "WdfMemoryCopyFromBuffer failed 0x%x\n", status); + return status; + } + + // + // Report how many bytes were copied + // + WdfRequestSetInformation(Request, bytesToCopy); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL, + "HidFx2GetHidDescriptor Exit = 0x%x\n", status); + return status; +} + +NTSTATUS +HidFx2GetReportDescriptor( + IN WDFDEVICE Device, + IN WDFREQUEST Request + ) +/*++ + +Routine Description: + + Finds the Report descriptor and copies it into the buffer provided by the + Request. + +Arguments: + + Device - Handle to WDF Device Object + + Request - Handle to request object + +Return Value: + + NT status code. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + ULONG_PTR bytesToCopy; + WDFMEMORY memory; + + UNREFERENCED_PARAMETER(Device); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL, + "HidFx2GetReportDescriptor Entry\n"); + + // + // This IOCTL is METHOD_NEITHER so WdfRequestRetrieveOutputMemory + // will correctly retrieve buffer from Irp->UserBuffer. + // Remember that HIDCLASS provides the buffer in the Irp->UserBuffer + // field irrespective of the ioctl buffer type. However, framework is very + // strict about type checking. You cannot get Irp->UserBuffer by using + // WdfRequestRetrieveOutputMemory if the ioctl is not a METHOD_NEITHER + // internal ioctl. + // + status = WdfRequestRetrieveOutputMemory(Request, &memory); + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, + "WdfRequestRetrieveOutputMemory failed 0x%x\n", status); + return status; + } + + // + // Use hardcoded Report descriptor + // + bytesToCopy = G_DefaultHidDescriptor.DescriptorList[0].wReportLength; + + if (bytesToCopy == 0) { + status = STATUS_INVALID_DEVICE_STATE; + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, + "G_DefaultHidDescriptor's reportLenght is zero, 0x%x\n", status); + return status; + } + + status = WdfMemoryCopyFromBuffer(memory, + 0, + (PVOID) G_DefaultReportDescriptor, + bytesToCopy); + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, + "WdfMemoryCopyFromBuffer failed 0x%x\n", status); + return status; + } + + // + // Report how many bytes were copied + // + WdfRequestSetInformation(Request, bytesToCopy); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL, + "HidFx2GetReportDescriptor Exit = 0x%x\n", status); + return status; +} + + +NTSTATUS +HidFx2GetDeviceAttributes( + IN WDFREQUEST Request + ) +/*++ + +Routine Description: + + Fill in the given struct _HID_DEVICE_ATTRIBUTES + +Arguments: + + Request - Pointer to Request object. + +Return Value: + + NT status code. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + PHID_DEVICE_ATTRIBUTES deviceAttributes = NULL; + PUSB_DEVICE_DESCRIPTOR usbDeviceDescriptor = NULL; + PDEVICE_EXTENSION deviceInfo = NULL; + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL, + "HidFx2GetDeviceAttributes Entry\n"); + + deviceInfo = GetDeviceContext(WdfIoQueueGetDevice(WdfRequestGetIoQueue(Request))); + + // + // This IOCTL is METHOD_NEITHER so WdfRequestRetrieveOutputMemory + // will correctly retrieve buffer from Irp->UserBuffer. + // Remember that HIDCLASS provides the buffer in the Irp->UserBuffer + // field irrespective of the ioctl buffer type. However, framework is very + // strict about type checking. You cannot get Irp->UserBuffer by using + // WdfRequestRetrieveOutputMemory if the ioctl is not a METHOD_NEITHER + // internal ioctl. + // + status = WdfRequestRetrieveOutputBuffer(Request, + sizeof (HID_DEVICE_ATTRIBUTES), + &deviceAttributes, + NULL); + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, + "WdfRequestRetrieveOutputBuffer failed 0x%x\n", status); + return status; + } + + // + // Retrieve USB device descriptor saved in device context + // + usbDeviceDescriptor = WdfMemoryGetBuffer(deviceInfo->DeviceDescriptor, NULL); + + deviceAttributes->Size = sizeof (HID_DEVICE_ATTRIBUTES); + deviceAttributes->VendorID = usbDeviceDescriptor->idVendor; + deviceAttributes->ProductID = usbDeviceDescriptor->idProduct;; + deviceAttributes->VersionNumber = usbDeviceDescriptor->bcdDevice; + + // + // Report how many bytes were copied + // + WdfRequestSetInformation(Request, sizeof (HID_DEVICE_ATTRIBUTES)); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL, + "HidFx2GetDeviceAttributes Exit = 0x%x\n", status); + return status; +} + + +NTSTATUS +HidFx2SendIdleNotification( + IN WDFREQUEST Request + ) +/*++ + +Routine Description: + + Pass down Idle notification request to lower driver + +Arguments: + + Request - Pointer to Request object. + +Return Value: + + NT status code. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + BOOLEAN sendStatus = FALSE; + WDF_REQUEST_SEND_OPTIONS options; + WDFIOTARGET nextLowerDriver; + WDFDEVICE device; + PIO_STACK_LOCATION currentIrpStack = NULL; + IO_STACK_LOCATION nextIrpStack; + + device = WdfIoQueueGetDevice(WdfRequestGetIoQueue(Request)); + currentIrpStack = IoGetCurrentIrpStackLocation(WdfRequestWdmGetIrp(Request)); + + // + // Convert the request to corresponding USB Idle notification request + // + if (currentIrpStack->Parameters.DeviceIoControl.InputBufferLength < + sizeof(HID_SUBMIT_IDLE_NOTIFICATION_CALLBACK_INFO)) { + + status = STATUS_BUFFER_TOO_SMALL; + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, + "DeviceIoControl.InputBufferLength too small, 0x%x\n", status); + return status; + } + + ASSERT(sizeof(HID_SUBMIT_IDLE_NOTIFICATION_CALLBACK_INFO) + == sizeof(USB_IDLE_CALLBACK_INFO)); + + #pragma warning(suppress :4127) // conditional expression is constant warning + if (sizeof(HID_SUBMIT_IDLE_NOTIFICATION_CALLBACK_INFO) != sizeof(USB_IDLE_CALLBACK_INFO)) { + + status = STATUS_INFO_LENGTH_MISMATCH; + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, + "Incorrect DeviceIoControl.InputBufferLength, 0x%x\n", status); + return status; + } + + // + // prepare next stack location + // + RtlZeroMemory(&nextIrpStack, sizeof(IO_STACK_LOCATION)); + + nextIrpStack.MajorFunction = currentIrpStack->MajorFunction; + nextIrpStack.Parameters.DeviceIoControl.InputBufferLength = + currentIrpStack->Parameters.DeviceIoControl.InputBufferLength; + nextIrpStack.Parameters.DeviceIoControl.Type3InputBuffer = + currentIrpStack->Parameters.DeviceIoControl.Type3InputBuffer; + nextIrpStack.Parameters.DeviceIoControl.IoControlCode = + IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION; + nextIrpStack.DeviceObject = + WdfIoTargetWdmGetTargetDeviceObject(WdfDeviceGetIoTarget(device)); + + // + // Format the I/O request for the driver's local I/O target by using the + // contents of the specified WDM I/O stack location structure. + // + WdfRequestWdmFormatUsingStackLocation( + Request, + &nextIrpStack + ); + + // + // Send the request down using Fire and forget option. + // + WDF_REQUEST_SEND_OPTIONS_INIT( + &options, + WDF_REQUEST_SEND_OPTION_SEND_AND_FORGET + ); + + nextLowerDriver = WdfDeviceGetIoTarget(device); + + sendStatus = WdfRequestSend( + Request, + nextLowerDriver, + &options + ); + + if (sendStatus == FALSE) { + status = STATUS_UNSUCCESSFUL; + } + + return status; +} + +PCHAR +DbgHidInternalIoctlString( + IN ULONG IoControlCode + ) +/*++ + +Routine Description: + + Returns Ioctl string helpful for debugging + +Arguments: + + IoControlCode - IO Control code + +Return Value: + + Name String + +--*/ +{ + switch (IoControlCode) + { + case IOCTL_HID_GET_DEVICE_DESCRIPTOR: + return "IOCTL_HID_GET_DEVICE_DESCRIPTOR"; + case IOCTL_HID_GET_REPORT_DESCRIPTOR: + return "IOCTL_HID_GET_REPORT_DESCRIPTOR"; + case IOCTL_HID_READ_REPORT: + return "IOCTL_HID_READ_REPORT"; + case IOCTL_HID_GET_DEVICE_ATTRIBUTES: + return "IOCTL_HID_GET_DEVICE_ATTRIBUTES"; + case IOCTL_HID_WRITE_REPORT: + return "IOCTL_HID_WRITE_REPORT"; + case IOCTL_HID_SET_FEATURE: + return "IOCTL_HID_SET_FEATURE"; + case IOCTL_HID_GET_FEATURE: + return "IOCTL_HID_GET_FEATURE"; + case IOCTL_HID_GET_STRING: + return "IOCTL_HID_GET_STRING"; + case IOCTL_HID_ACTIVATE_DEVICE: + return "IOCTL_HID_ACTIVATE_DEVICE"; + case IOCTL_HID_DEACTIVATE_DEVICE: + return "IOCTL_HID_DEACTIVATE_DEVICE"; + case IOCTL_HID_SEND_IDLE_NOTIFICATION_REQUEST: + return "IOCTL_HID_SEND_IDLE_NOTIFICATION_REQUEST"; + default: + return "Unknown IOCTL"; + } +} + + diff --git a/hid/hidusbfx2/sys/hidusbfx2.h b/hid/hidusbfx2/sys/hidusbfx2.h new file mode 100644 index 000000000..ee8d2ed28 --- /dev/null +++ b/hid/hidusbfx2/sys/hidusbfx2.h @@ -0,0 +1,476 @@ +/*++ + +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: + + hidusbfx2.h + +Abstract: + + common header file + +Author: + + +Environment: + + kernel mode only + +Notes: + + +Revision History: + + +--*/ +#ifndef _HIDUSBFX2_H_ + +#define _HIDUSBFX2_H_ + +#pragma warning(disable:4200) // suppress nameless struct/union warning +#pragma warning(disable:4201) // suppress nameless struct/union warning +#pragma warning(disable:4214) // suppress bit field types other than int warning +#include +#include +#include "usbdi.h" +#include "usbdlib.h" + +#pragma warning(default:4200) +#pragma warning(default:4201) +#pragma warning(default:4214) +#include +#include "wdfusb.h" + +#pragma warning(disable:4201) // suppress nameless struct/union warning +#pragma warning(disable:4214) // suppress bit field types other than int warning +#include + +#define NTSTRSAFE_LIB +#include + +#include "trace.h" + +#define _DRIVER_NAME_ "HIDUSBFX2: " +#define POOL_TAG (ULONG) 'H2XF' +#define INPUT_REPORT_BYTES 1 + +#define CONSUMER_CONTROL_REPORT_ID 1 +#define SYSTEM_CONTROL_REPORT_ID 2 +#define DIP_SWITCHES_REPORT_ID 3 +#define SEVEN_SEGMENT_REPORT_ID 4 +#define BARGRAPH_REPORT_ID 5 + +#define CONSUMER_CONTROL_BUTTONS_BIT_MASK ((UCHAR)0x7f) // (first 7 bits) +#define SYSTEM_CONTROL_BUTTONS_BIT_MASK ((UCHAR)0x80) + +#define INTERRUPT_ENDPOINT_INDEX (0) + +#define SWICTHPACK_DEBOUNCE_TIME_IN_MS 10 + +typedef UCHAR HID_REPORT_DESCRIPTOR, *PHID_REPORT_DESCRIPTOR; + +// +// Define the vendor commands supported by device +// +#define HIDFX2_READ_SWITCH_STATE 0xD6 +#define HIDFX2_READ_7SEGMENT_DISPLAY 0xD4 +#define HIDFX2_READ_BARGRAPH_DISPLAY 0xD7 +#define HIDFX2_SET_BARGRAPH_DISPLAY 0xD8 +#define HIDFX2_IS_HIGH_SPEED 0xD9 +#define HIDFX2_REENUMERATE 0xDA +#define HIDFX2_SET_7SEGMENT_DISPLAY 0xDB + +// +// Bit masks for 7 segment display to be used with vendor command +// +#define SEGMENT_BIT_1 (UCHAR)0x1 +#define SEGMENT_BIT_2 (UCHAR)0x2 +#define SEGMENT_BIT_3 (UCHAR)0x4 +#define SEGMENT_BIT_4 (UCHAR)0x8 +#define SEGMENT_BIT_5 (UCHAR)0x10 +#define SEGMENT_BIT_6 (UCHAR)0x20 +#define SEGMENT_BIT_7 (UCHAR)0x40 +#define SEGMENT_BIT_8 (UCHAR)0x80 + +#define SEGMENT_DISPLAY_1 \ + SEGMENT_BIT_2 \ + | SEGMENT_BIT_3 + +#define SEGMENT_DISPLAY_2 \ + SEGMENT_BIT_1 \ + | SEGMENT_BIT_2 \ + | SEGMENT_BIT_6 \ + | SEGMENT_BIT_5 \ + | SEGMENT_BIT_8 + +#define SEGMENT_DISPLAY_3 \ + SEGMENT_BIT_1 \ + | SEGMENT_BIT_2 \ + | SEGMENT_BIT_6 \ + | SEGMENT_BIT_3 \ + | SEGMENT_BIT_8 + +#define SEGMENT_DISPLAY_4 \ + SEGMENT_BIT_2 \ + | SEGMENT_BIT_3 \ + | SEGMENT_BIT_6 \ + | SEGMENT_BIT_7 + +#define SEGMENT_DISPLAY_5 \ + SEGMENT_BIT_1 \ + | SEGMENT_BIT_3 \ + | SEGMENT_BIT_6 \ + | SEGMENT_BIT_7 \ + | SEGMENT_BIT_8 + +#define SEGMENT_DISPLAY_6 \ + SEGMENT_BIT_1 \ + | SEGMENT_BIT_3 \ + | SEGMENT_BIT_5 \ + | SEGMENT_BIT_6 \ + | SEGMENT_BIT_7 \ + | SEGMENT_BIT_8 + +#define SEGMENT_DISPLAY_7 \ + SEGMENT_BIT_1 \ + | SEGMENT_BIT_2 \ + | SEGMENT_BIT_3 + +#define SEGMENT_DISPLAY_8 \ + SEGMENT_BIT_1 \ + | SEGMENT_BIT_2 \ + | SEGMENT_BIT_3 \ + | SEGMENT_BIT_5 \ + | SEGMENT_BIT_6 \ + | SEGMENT_BIT_7 \ + | SEGMENT_BIT_8 + +#define BARGRAPH_LED_1_ON 0x1 +#define BARGRAPH_LED_2_ON 0x2 +#define BARGRAPH_LED_3_ON 0x4 +#define BARGRAPH_LED_4_ON 0x8 +#define BARGRAPH_LED_5_ON 0x10 +#define BARGRAPH_LED_6_ON 0x20 +#define BARGRAPH_LED_7_ON 0x40 +#define BARGRAPH_LED_8_ON 0x80 +#define BARGRAPH_LED_ALL_ON 0xff +#define BARGRAPH_LED_ALL_OFF 0x00 + +// +// This is the default report descriptor for the Hid device provided +// by the mini driver in response to IOCTL_HID_GET_REPORT_DESCRIPTOR. +// +// AC: Aplication Control +// AL: Application Launch + +#ifdef USE_HARDCODED_HID_REPORT_DESCRIPTOR + +// +// The default descriptor exposes three top level collections. +// The DIP switches trigger consumer control and system control +// buttons. +// + +CONST HID_REPORT_DESCRIPTOR G_DefaultReportDescriptor[] = { + // Consumer control collection + 0x05,0x0C, // USAGE_PAGE (Consumer Page) + 0x09,0x01, // USAGE (Consumer Control Usage 0x01) + 0xA1,0x01, // COLLECTION (Application) + 0x85,CONSUMER_CONTROL_REPORT_ID,// REPORT_ID + 0x0A, 0x23, 0x02, // USAGE (Usage Browser) + 0x0A, 0x24, 0x02, // USAGE (Usage AC Back) + 0x0A, 0x25, 0x02, // USAGE (Usage AC Forward) + 0x0A, 0x27, 0x02, // USAGE (Usage AC Refresh) + 0x0A, 0x2A, 0x02, // USAGE (Usage AC BookMarks) + 0x0A, 0x8A, 0x01, // USAGE (Usage AL Mail) + 0x0A, 0x92, 0x01, // USAGE (Usage AL Calculator ) + 0x15, 0x00, // LOGICAL_MINIMUM(0) + 0x25, 0x01, // LOGICAL_MAXIMUM(1) + 0x75, 0x01, // REPORT_SIZE + 0x95, 0x07, // REPORT_COUNT + 0x81, 0x02, // INPUT (Data, Variable,Abs) + 0x75, 0x01, // REPORT_SIZE + 0x95, 0x01, // REPORT_COUNT + 0x81, 0x07, // INPUT (const) + 0xC0, // END_COLLECTION + // system control collection + 0x05, 0x01, // Usage Page (Generic Desktop) + 0x09, 0x80, // Usage (System Control) + 0xA1, 0x01, // Collection (Application) + 0x85, SYSTEM_CONTROL_REPORT_ID, // Report ID + 0x95, 0x07, // Report Count + 0x81, 0x07, // Input (Constant) PADDING + 0x09, 0x82, // Usage (System Sleep) + 0x95, 0x01, // Report Count (1) + 0x81, 0x06, // Input (Data, Variable, Relative, Preferred) + 0xC0, // End Collection + // Feature collection + 0x06,0x00, 0xFF, // USAGE_PAGE (Vender Defined Usage Page) + 0x09,0x01, // USAGE (Vendor Usage 0x01) + 0xA1,0x01, // COLLECTION (Application) + 0x85,SEVEN_SEGMENT_REPORT_ID, // Report ID for 7 segment display + 0x19,0x00, // USAGE MINIMUM + 0x29,0xff, // USAGE MAXIMUM + 0x15,0x00, // LOGICAL_MINIMUM(1) + 0x26,0xff, 0x00, // LOGICAL_MAXIMUM(255) + 0x75,0x08, // REPORT_SIZE + 0x95,0x01, // REPORT_COUNT + 0xB1,0x00, // Feature (Data,Ary,Abs) + 0x85,BARGRAPH_REPORT_ID, // Report ID for bargraph display + 0x19,0x00, // USAGE MINIMUM + 0x29,0xff, // USAGE MAXIMUM + 0x15,0x00, // LOGICAL_MINIMUM(1) + 0x26,0xff, 0x00, // LOGICAL_MAXIMUM(255) + 0x75,0x08, // REPORT_SIZE + 0x95,0x01, // REPORT_COUNT + 0xB1,0x00, // Feature (Data,Ary,Abs) + 0xC0 // END_COLLECTION +}; + +// +// This is the default HID descriptor returned by the mini driver +// in response to IOCTL_HID_GET_DEVICE_DESCRIPTOR. The size +// of report descriptor is currently the size of G_DefaultReportDescriptor. +// +CONST HID_DESCRIPTOR G_DefaultHidDescriptor = { + 0x09, // length of HID descriptor + 0x21, // descriptor type == HID 0x21 + 0x0100, // hid spec release + 0x00, // country code == Not Specified + 0x01, // number of HID class descriptors + { 0x22, // descriptor type + sizeof(G_DefaultReportDescriptor) } // total length of report descriptor +}; + +#endif // USE_HARDCODED_HID_REPORT_DESCRIPTOR + +#include +typedef struct _HIDFX2_INPUT_REPORT { + + // + //Report ID for the collection + // + BYTE ReportId; + + union { + struct { + // + // Individual switches starting from the + // right of the set of switches + // + BYTE SwitchBrowser : 1; + BYTE SwitchBack : 1; + BYTE SwitchForward : 1; + BYTE SwitchRefresh : 1; + BYTE SwitchBookMarks : 1; + BYTE SwitchMail : 1; + BYTE SwitchCalculator : 1; + BYTE Padding : 1; + + } ConsumerControlReport; + + struct { + // + // Individual switches starting from the + // right of the set of switches + // + BYTE Padding : 7; + BYTE SwitchPowerSleep : 1; + + } SystemControlReport; + + // + // The state of all the switches as a single + // UCHAR + // + + BYTE SwitchStateAsByte; + }; +}HIDFX2_INPUT_REPORT, *PHIDFX2_INPUT_REPORT; + + +typedef struct _HIDFX2_FEATURE_REPORT { + // + //Report ID for the collection + // + BYTE ReportId; + + // + //one-byte feature data from 7-segment display or bar graph + // + BYTE FeatureData; + +}HIDFX2_FEATURE_REPORT, *PHIDFX2_FEATURE_REPORT; +#include + + +typedef struct _DEVICE_EXTENSION{ + + // + //WDF handles for USB Target + // + WDFUSBDEVICE UsbDevice; + WDFUSBINTERFACE UsbInterface; + WDFUSBPIPE InterruptPipe; + + // + //Device descriptor for the USB device + // + WDFMEMORY DeviceDescriptor; + + // + // Switch state. + // + UCHAR CurrentSwitchState; + + // + // This variable stores state for the swicth that got toggled most recently + // (the device returns the state of all the switches and not just the + // one that got toggled). + // + UCHAR LatestToggledSwitch; + + // + // Interrupt endpoints sends switch state when first started + // or when resuming from suspend. We need to ignore that data. + // + BOOLEAN IsPowerUpSwitchState; + + // + // WDF Queue for read IOCTLs from hidclass that get satisfied from + // USB interrupt endpoint + // + WDFQUEUE InterruptMsgQueue; + + // + // Handle debouncing of switchpack + // + WDFTIMER DebounceTimer; + +} DEVICE_EXTENSION, * PDEVICE_EXTENSION; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(DEVICE_EXTENSION, GetDeviceContext) + +// +// driver routine declarations +// +// This type of function declaration is for Prefast for drivers. +// Because this declaration specifies the function type, PREfast for Drivers +// does not need to infer the type or to report an inference. The declaration +// also prevents PREfast for Drivers from misinterpreting the function type +// and applying inappropriate rules to the function. For example, PREfast for +// Drivers would not apply rules for completion routines to functions of type +// DRIVER_CANCEL. The preferred way to avoid Warning 28101 is to declare the +// function type explicitly. In the following example, the DriverEntry function +// is declared to be of type DRIVER_INITIALIZE. +// +DRIVER_INITIALIZE DriverEntry; + +EVT_WDF_DRIVER_DEVICE_ADD HidFx2EvtDeviceAdd; + +EVT_WDF_IO_QUEUE_IO_INTERNAL_DEVICE_CONTROL HidFx2EvtInternalDeviceControl; + +NTSTATUS +HidFx2GetHidDescriptor( + IN WDFDEVICE Device, + IN WDFREQUEST Request + ); + +NTSTATUS +HidFx2GetReportDescriptor( + IN WDFDEVICE Device, + IN WDFREQUEST Request + ); + + +NTSTATUS +HidFx2GetDeviceAttributes( + IN WDFREQUEST Request + ); + +EVT_WDF_DEVICE_PREPARE_HARDWARE HidFx2EvtDevicePrepareHardware; + +EVT_WDF_DEVICE_D0_ENTRY HidFx2EvtDeviceD0Entry; + +EVT_WDF_DEVICE_D0_EXIT HidFx2EvtDeviceD0Exit; + +PCHAR +DbgDevicePowerString( + IN WDF_POWER_DEVICE_STATE Type + ); + +NTSTATUS +HidFx2ConfigContReaderForInterruptEndPoint( + PDEVICE_EXTENSION DeviceContext + ); + +EVT_WDF_USB_READER_COMPLETION_ROUTINE HidFx2EvtUsbInterruptPipeReadComplete; + +VOID +HidFx2CompleteReadReport( + WDFDEVICE Device + ); + +EVT_WDF_OBJECT_CONTEXT_CLEANUP HidFx2EvtDriverContextCleanup; + +PCHAR +DbgHidInternalIoctlString( + IN ULONG IoControlCode + ); + +NTSTATUS +HidFx2SendIdleNotification( + IN WDFREQUEST Request + ); + +NTSTATUS +HidFx2SetFeature( + IN WDFREQUEST Request + ); + +NTSTATUS +HidFx2GetFeature( + IN WDFREQUEST Request, + OUT PULONG BytesReturned + ); + +EVT_WDF_IO_QUEUE_IO_CANCELED_ON_QUEUE HidFx2EvtIoCanceledOnQueue; + +NTSTATUS +HidFx2GetSwitchState( + IN WDFDEVICE Device, + OUT PUCHAR SwitchState + ); + +NTSTATUS +SendVendorCommand( + IN WDFDEVICE Device, + IN UCHAR VendorCommand, + IN PUCHAR CommandData + ); + +NTSTATUS +GetVendorData( + IN WDFDEVICE Device, + IN UCHAR VendorCommand, + IN PUCHAR CommandData + ); + + +USBD_STATUS +HidFx2ValidateConfigurationDescriptor( + IN PUSB_CONFIGURATION_DESCRIPTOR ConfigDesc, + IN ULONG BufferLength, + _Inout_ PUCHAR *Offset + ); + +EVT_WDF_TIMER HidFx2EvtTimerFunction; + +#endif //_HIDUSBFX2_H_ + diff --git a/hid/hidusbfx2/sys/hidusbfx2.inx b/hid/hidusbfx2/sys/hidusbfx2.inx new file mode 100644 index 000000000..da8e8831d --- /dev/null +++ b/hid/hidusbfx2/sys/hidusbfx2.inx @@ -0,0 +1,186 @@ +[Version] +Signature = "$WINDOWS NT$" +Class = HIDClass +ClassGuid = {745a17a0-74d3-11d0-b6fe-00a0c90f57da} +Provider = %ProviderName% +DriverVer = 01/10/2007,1.0.0.0 +CatalogFile = kmdfsamples.cat + +; +; In order to use IHV drivers, the SourceDisksNames section must list the +; disk(s) containing the drivers and the SourceDisksFiles section must list +; which disk number each file to be copied is found on. +; +; Files used in a driver installation need to be digitally signed otherwise +; installation may fail. See documentation elsewhere in the DDK regarding +; driver signing. + +[SourceDisksFiles] +hidusbfx2.sys = 99 +hidkmdf.sys = 99 + +[SourceDisksNames] +99 = %DISK_NAME%,,,"" + +[DestinationDirs] +CopyFunctionDriver = 12 +CopyFilterDriver = 12 + +[Manufacturer] +%ManufacturerName% = Standard,NT$ARCH$,NT$ARCH$.6.1 + +; For XP and later +[Standard.NT$ARCH$] +%hidusbfx2% = hidusbfx2.Inst, USB\VID_0547&PID_1002 +%customCollection% = customCollection.Inst, HID_DEVICE_UP:FF00_U:0001 + +; For Win7 and later so that we can use inbox HID-KMDF mapper +[Standard.NT$ARCH$.6.1] +%hidusbfx2% = hidusbfx2.Inst.Win7, USB\VID_0547&PID_1002 +%customCollection% = customCollection.Inst, HID_DEVICE_UP:FF00_U:0001 + +;=============================================================== +; Install section for XP thru Vista +;=============================================================== +[hidusbfx2.Inst.NT] +CopyFiles = CopyFunctionDriver, CopyFilterDriver + +[hidusbfx2.Inst.NT.HW] +AddReg = hidusbfx2_Parameters.AddReg + +; +; hidkmdf is the function driver and hidusbfx2 is the lower filter +; +[hidusbfx2.Inst.NT.Services] +AddService = hidkmdf,0x00000002,hidkmdf_Service_Inst, +AddService = hidusbfx2,, hidusbfx2_Service_Inst + +[CopyFunctionDriver] +hidkmdf.sys + +[hidusbfx2_Parameters.AddReg] +HKR,,"LowerFilters",0x00010000,"hidusbfx2" + +[hidkmdf_Service_Inst] +DisplayName = %hidkmdf.SVCDESC% +ServiceType = 1 ; SERVICE_KERNEL_DRIVER +StartType = 3 ; SERVICE_DEMAND_START +ErrorControl = 1 ; SERVICE_ERROR_NORMAL +ServiceBinary = %12%\hidkmdf.sys + +;=============================================================== +; Install section for Win7 and later +; Use the inbox mshidkmdf.sys as the shim +;=============================================================== +[hidusbfx2.Inst.Win7.NT] +; Just copy the driver. No neeed to copy other system binaries. +CopyFiles = CopyFilterDriver + +[hidusbfx2.Inst.Win7.NT.HW] +AddReg = hidusbfx2_Win7_Parameters.AddReg + +; +; mshidkmdf is the function driver and hidusbfx2 is the lower filter +; +[hidusbfx2.Inst.Win7.NT.Services] +AddService = hidusbfx2,, hidusbfx2_Service_Inst +AddService = mshidkmdf, 0x000001fa, mshidkmdf.AddService ;flag 0x2 sets this as the service for the device + +[CopyFilterDriver] +hidusbfx2.sys + +[mshidkmdf.AddService] +ServiceType = 1 ; SERVICE_KERNEL_DRIVER +StartType = 3 ; SERVICE_DEMAND_START +ErrorControl = 1 ; SERVICE_ERROR_NORMAL +ServiceBinary = %10%\System32\Drivers\mshidkmdf.sys + +[hidusbfx2_Win7_Parameters.AddReg] +HKR,,"LowerFilters",0x00010000,"hidusbfx2" +HKR,,"AllowIdleIrpInD3",0x00010001,0x1 + +;=============================================================== +; Service section (common to all OS versions) +;=============================================================== + +[hidusbfx2_Service_Inst] +DisplayName = %hidusbfx2% +ServiceType = %SERVICE_KERNEL_DRIVER% +StartType = %SERVICE_DEMAND_START% +ErrorControl = %SERVICE_ERROR_IGNORE% +ServiceBinary = %12%\hidusbfx2.sys + + +;=============================================================== +; Custom Collection install section +; - Only a Null service is installed. +;=============================================================== +[customCollection.Inst.NT] +; NULL section + +[customCollection.Inst.NT.HW] +AddReg = customCollection.Inst.AddReg.NT.HW + +[customCollection.Inst.AddReg.NT.HW] +HKR,,"SelectiveSuspendEnabled",0x00000001,0x1 + +[customCollection.Inst.NT.Services] +AddService = ,0x00000002, ; NULL Service + +;================================================================ +; WDF Coinstaller installation +;=============================================================== + +[DestinationDirs] +hidusbfx2.Inst_CoInstaller_CopyFiles = 11 + +[hidusbfx2.Inst.NT.CoInstallers] +AddReg=hidusbfx2.Inst_CoInstaller_AddReg +CopyFiles=hidusbfx2.Inst_CoInstaller_CopyFiles + +[hidusbfx2.Inst_CoInstaller_AddReg] +HKR,,CoInstallers32,0x00010000, "WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll,WdfCoInstaller" + +[hidusbfx2.Inst_CoInstaller_CopyFiles] +WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll,,,0x00000010 ;COPYFLG_NO_OVERWRITE (for win2k) + +[SourceDisksFiles] +WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll=99 ; make sure the number matches with SourceDisksNames + +[hidusbfx2.Inst.NT.Wdf] +KmdfService = hidusbfx2, hidusbfx2_wdfsect + +[hidusbfx2_wdfsect] +KmdfLibraryVersion = $KMDFVERSION$ + +;================================================================ +; Strings section +;=============================================================== + +[Strings] +;Localizable +ProviderName = "TODO-Set-Provider" +ManufacturerName = "TODO-Set-Manufacturer" +hidusbfx2 = "KMDF HID Minidriver for OSR USB-FX2 Device" +customCollection = "HID Vendor-defined Collection for OSR USB-FX2" +DISK_NAME = "HID USB FX2 Device Sample Install Disk" +hidkmdf.SVCDESC = "Filter Driver Service for HID-KMDF Interface layer" + +;Non-Localizable +SERVICE_BOOT_START = 0x0 +SERVICE_SYSTEM_START = 0x1 +SERVICE_AUTO_START = 0x2 +SERVICE_DEMAND_START = 0x3 +SERVICE_DISABLED = 0x4 + +SERVICE_KERNEL_DRIVER = 0x1 +SERVICE_ERROR_IGNORE = 0x0 +SERVICE_ERROR_NORMAL = 0x1 +SERVICE_ERROR_SEVERE = 0x2 +SERVICE_ERROR_CRITICAL = 0x3 + +REG_EXPAND_SZ = 0x00020000 +REG_DWORD = 0x00010001 +REG_MULTI_SZ = 0x00010000 +REG_BINARY = 0x00000001 +REG_SZ = 0x00000000 diff --git a/hid/hidusbfx2/sys/hidusbfx2.rc b/hid/hidusbfx2/sys/hidusbfx2.rc new file mode 100644 index 000000000..9391a4efd --- /dev/null +++ b/hid/hidusbfx2/sys/hidusbfx2.rc @@ -0,0 +1,11 @@ +#include + +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "HID mini driver for USB Fx2 Device" +#define VER_INTERNALNAME_STR "hidusbfx2.sys" + +#include "common.ver" + diff --git a/hid/hidusbfx2/sys/hidusbfx2.vcxproj b/hid/hidusbfx2/sys/hidusbfx2.vcxproj new file mode 100644 index 000000000..41260039b --- /dev/null +++ b/hid/hidusbfx2/sys/hidusbfx2.vcxproj @@ -0,0 +1,227 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {0C56E492-CEB9-49C4-B127-FE437B55A879} + $(MSBuildProjectName) + 1 + false + true + Debug + Win32 + {FFEC78A4-6826-4649-94F7-7886ABCAA11A} + + + + 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 + TraceEvents(LEVEL,FLAGS,MSG,...) + {km-WdfDefault.tpl}*.tmh + + + $(InfArch) + true + .\$(IntDir)\hidusbfx2.inf + + + true + true + TraceEvents(LEVEL,FLAGS,MSG,...) + {km-WdfDefault.tpl}*.tmh + + + + hidusbfx2 + + + hidusbfx2 + + + hidusbfx2 + + + hidusbfx2 + + + + true + Level4 + %(PreprocessorDefinitions);EVENT_TRACING + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\hidclass.lib;$(DDK_LIB_PATH)\ntstrsafe.lib;$(DDK_LIB_PATH)\usbd.lib + + + %(PreprocessorDefinitions);EVENT_TRACING + + + %(PreprocessorDefinitions);EVENT_TRACING + + + + + true + Level4 + %(PreprocessorDefinitions);EVENT_TRACING + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\hidclass.lib;$(DDK_LIB_PATH)\ntstrsafe.lib;$(DDK_LIB_PATH)\usbd.lib + + + %(PreprocessorDefinitions);EVENT_TRACING + + + %(PreprocessorDefinitions);EVENT_TRACING + + + + + true + Level4 + %(PreprocessorDefinitions);EVENT_TRACING + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\hidclass.lib;$(DDK_LIB_PATH)\ntstrsafe.lib;$(DDK_LIB_PATH)\usbd.lib + + + %(PreprocessorDefinitions);EVENT_TRACING + + + %(PreprocessorDefinitions);EVENT_TRACING + + + + + true + Level4 + %(PreprocessorDefinitions);EVENT_TRACING + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\hidclass.lib;$(DDK_LIB_PATH)\ntstrsafe.lib;$(DDK_LIB_PATH)\usbd.lib + + + %(PreprocessorDefinitions);EVENT_TRACING + + + %(PreprocessorDefinitions);EVENT_TRACING + + + + 1 + + + 1 + + + 1 + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/hid/hidusbfx2/sys/hidusbfx2.vcxproj.Filters b/hid/hidusbfx2/sys/hidusbfx2.vcxproj.Filters new file mode 100644 index 000000000..82e402412 --- /dev/null +++ b/hid/hidusbfx2/sys/hidusbfx2.vcxproj.Filters @@ -0,0 +1,42 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {962D8603-CE23-4D62-A83B-90CEFA6AC176} + + + h;hpp;hxx;hm;inl;inc;xsd + {538F2C38-4B67-4B18-BBC4-35B05919B868} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {53D44B18-A36F-437E-8732-300635680D6F} + + + inf;inv;inx;mof;mc; + {942D8837-E92B-4E87-8730-98BC45059E2D} + + + + + Source Files + + + Source Files + + + Source Files + + + + + Driver Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/hid/hidusbfx2/sys/trace.h b/hid/hidusbfx2/sys/trace.h new file mode 100644 index 000000000..073906fc0 --- /dev/null +++ b/hid/hidusbfx2/sys/trace.h @@ -0,0 +1,102 @@ +/*++ + +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 !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_IOCTL 0x00000004 + +VOID +TraceEvents ( + IN ULONG DebugPrintLevel, + IN ULONG DebugPrintFlag, + IN PCCHAR DebugMessage, + ... + ); + +#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. +// +// NOTE: If you are adopting this sample for your driver, please generate +// a new guid, using tools\other\i386\guidgen.exe present in the +// DDK. +// +// Name of the logger is HidUsbFx2 and the guid is +// {D23A0C5A-D307-4f0e-AE8E-E2A355AD5DAC} +// (0xd23a0c5a, 0xd307, 0x4f0e, 0xae, 0x8e, 0xe2, 0xa3, 0x55, 0xad, 0x5d, 0xac); +// + +#define WPP_CHECK_FOR_NULL_STRING //to prevent exceptions due to NULL strings + +#define WPP_CONTROL_GUIDS \ + WPP_DEFINE_CONTROL_GUID(HidUsbFx2TraceGuid,(d23a0c5a,d307,4f0e,ae8e,E2A355AD5DAC), \ + WPP_DEFINE_BIT(DBG_INIT) /* bit 0 = 0x00000001 */ \ + WPP_DEFINE_BIT(DBG_PNP) /* bit 1 = 0x00000002 */ \ + WPP_DEFINE_BIT(DBG_IOCTL) /* bit 2 = 0x00000004 */ \ + /* 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) + + +#endif + + diff --git a/hid/hidusbfx2/sys/usb.c b/hid/hidusbfx2/sys/usb.c new file mode 100644 index 000000000..ca5feff00 --- /dev/null +++ b/hid/hidusbfx2/sys/usb.c @@ -0,0 +1,864 @@ +/*++ + +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: + + usb.c + +Abstract: + + Code for handling USB related requests + +Author: + + +Environment: + + kernel mode only + +Revision History: + +--*/ + +#include + +#if defined(EVENT_TRACING) +#include "usb.tmh" +#endif + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, HidFx2EvtDevicePrepareHardware) +#pragma alloc_text(PAGE, HidFx2EvtDeviceD0Exit) +#pragma alloc_text(PAGE, HidFx2ConfigContReaderForInterruptEndPoint) +#pragma alloc_text(PAGE, HidFx2ValidateConfigurationDescriptor) +#endif + +NTSTATUS +HidFx2EvtDevicePrepareHardware( + IN WDFDEVICE Device, + IN WDFCMRESLIST ResourceList, + IN WDFCMRESLIST ResourceListTranslated + ) +/*++ + +Routine Description: + + In this callback, the driver does whatever is necessary to make the + hardware ready to use. In the case of a USB device, this involves + reading and selecting descriptors. + +Arguments: + + Device - handle to a device + + ResourceList - A handle to a framework resource-list object that + identifies the raw hardware resourcest + + ResourceListTranslated - A handle to a framework resource-list object + that identifies the translated hardware resources + +Return Value: + + NT status value + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + PDEVICE_EXTENSION devContext = NULL; + WDF_USB_DEVICE_SELECT_CONFIG_PARAMS configParams; + WDF_OBJECT_ATTRIBUTES attributes; + PUSB_DEVICE_DESCRIPTOR usbDeviceDescriptor = NULL; + + UNREFERENCED_PARAMETER(ResourceList); + UNREFERENCED_PARAMETER(ResourceListTranslated); + + PAGED_CODE (); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, + "HidFx2EvtDevicePrepareHardware Enter\n"); + + devContext = GetDeviceContext(Device); + + // + // Create a WDFUSBDEVICE object. WdfUsbTargetDeviceCreate obtains the + // USB device descriptor and the first USB configuration descriptor from + // the device and stores them. It also creates a framework USB interface + // object for each interface in the device's first configuration. + // + // The parent of each USB device object is the driver's framework driver + // object. The driver cannot change this parent, and the ParentObject + // member or the WDF_OBJECT_ATTRIBUTES structure must be NULL. + // + // We only create device the first time PrepareHardware is called. If + // the device is restarted by pnp manager for resource rebalance, we + // will use the same device handle but then select the interfaces again + // because the USB stack could reconfigure the device on restart. + // + if (devContext->UsbDevice == NULL) { + status = WdfUsbTargetDeviceCreate(Device, + WDF_NO_OBJECT_ATTRIBUTES, + &devContext->UsbDevice); + + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "WdfUsbTargetDeviceCreate failed 0x%x\n", status); + return status; + } + + // + // TODO: If you are fetching configuration descriptor from device for + // selecting a configuration or to parse other descriptors, call + // HidFx2ValidateConfigurationDescriptor + // to do basic validation on the descriptors before you access them. + // + + } + + // + // Select a device configuration by using a + // WDF_USB_DEVICE_SELECT_CONFIG_PARAMS structure to specify USB + // descriptors, a URB, or handles to framework USB interface objects. + // + WDF_USB_DEVICE_SELECT_CONFIG_PARAMS_INIT_SINGLE_INTERFACE( &configParams); + + status = WdfUsbTargetDeviceSelectConfig(devContext->UsbDevice, + WDF_NO_OBJECT_ATTRIBUTES, + &configParams); + if(!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "WdfUsbTargetDeviceSelectConfig failed %!STATUS!\n", + status); + return status; + } + + devContext->UsbInterface = + configParams.Types.SingleInterface.ConfiguredUsbInterface; + + // + // Get the device descriptor and store it in device context + // + WDF_OBJECT_ATTRIBUTES_INIT(&attributes); + attributes.ParentObject = Device; + status = WdfMemoryCreate( + &attributes, + NonPagedPool, + 0, + sizeof(USB_DEVICE_DESCRIPTOR), + &devContext->DeviceDescriptor, + &usbDeviceDescriptor + ); + + if(!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "WdfMemoryCreate for Device Descriptor failed %!STATUS!\n", + status); + return status; + } + + WdfUsbTargetDeviceGetDeviceDescriptor( + devContext->UsbDevice, + usbDeviceDescriptor + ); + + // + // Get the Interrupt pipe. There are other endpoints but we are only + // interested in interrupt endpoint since our HID data comes from that + // endpoint. Another way to get the interrupt endpoint is by enumerating + // through all the pipes in a loop and looking for pipe of Interrupt type. + // + devContext->InterruptPipe = WdfUsbInterfaceGetConfiguredPipe( + devContext->UsbInterface, + INTERRUPT_ENDPOINT_INDEX, + NULL);// pipeInfo + + if (NULL == devContext->InterruptPipe) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "Failed to get interrupt pipe info\n"); + status = STATUS_INVALID_DEVICE_STATE; + return status; + } + + // + // Tell the framework that it's okay to read less than + // MaximumPacketSize + // + WdfUsbTargetPipeSetNoMaximumPacketSizeCheck(devContext->InterruptPipe); + + // + //configure continuous reader + // + status = HidFx2ConfigContReaderForInterruptEndPoint(devContext); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, + "HidFx2EvtDevicePrepareHardware Exit, Status:0x%x\n", status); + + return status; +} + + +NTSTATUS +HidFx2ConfigContReaderForInterruptEndPoint( + PDEVICE_EXTENSION DeviceContext + ) +/*++ + +Routine Description: + + This routine configures a continuous reader on the + interrupt endpoint. It's called from the PrepareHarware event. + +Arguments: + + DeviceContext - Pointer to device context structure + +Return Value: + + NT status value + +--*/ +{ + WDF_USB_CONTINUOUS_READER_CONFIG contReaderConfig; + NTSTATUS status = STATUS_SUCCESS; + + PAGED_CODE (); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, + "HidFx2ConfigContReaderForInterruptEndPoint Enter\n"); + + WDF_USB_CONTINUOUS_READER_CONFIG_INIT(&contReaderConfig, + HidFx2EvtUsbInterruptPipeReadComplete, + DeviceContext, // Context + sizeof(UCHAR)); // TransferLength + // + // Reader requests are not posted to the target automatically. + // Driver must explictly call WdfIoTargetStart to kick start the + // reader. In this sample, it's done in D0Entry. + // By defaut, framework queues two requests to the target + // endpoint. Driver can configure up to 10 requests with CONFIG macro. + // + status = WdfUsbTargetPipeConfigContinuousReader(DeviceContext->InterruptPipe, + &contReaderConfig); + + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, + "HidFx2ConfigContReaderForInterruptEndPoint failed %x\n", + status); + return status; + } + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, + "HidFx2ConfigContReaderForInterruptEndPoint Exit, status:0x%x\n", status); + + return status; +} + + +VOID +HidFx2EvtUsbInterruptPipeReadComplete( + WDFUSBPIPE Pipe, + WDFMEMORY Buffer, + size_t NumBytesTransferred, + WDFCONTEXT Context + ) +/*++ + +Routine Description: + + This the completion routine of the continuous reader. This can + called concurrently on multiprocessor system if there are + more than one readers configured. So make sure to protect + access to global resources. + +Arguments: + + Pipe - Handle to WDF USB pipe object + + Buffer - This buffer is freed when this call returns. + If the driver wants to delay processing of the buffer, it + can take an additional referrence. + + NumBytesTransferred - number of bytes of data that are in the read buffer. + + Context - Provided in the WDF_USB_CONTINUOUS_READER_CONFIG_INIT macro + +Return Value: + + NT status value + +--*/ +{ + PDEVICE_EXTENSION devContext = Context; + UCHAR toggledSwitch = 0; + PUCHAR switchState = NULL; + UCHAR currentSwitchState = 0; + UCHAR previousSwitchState = 0; + + UNREFERENCED_PARAMETER(NumBytesTransferred); + UNREFERENCED_PARAMETER(Pipe); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, + "HidFx2EvtUsbInterruptPipeReadComplete Enter\n"); + + // + // Interrupt endpoints sends switch state when first started + // or when resuming from suspend. We need to ignore that data since + // user did not change the switch state. + // + if (devContext->IsPowerUpSwitchState) { + devContext->IsPowerUpSwitchState = FALSE; + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, + "Dropping interrupt message since received during powerup/resume\n"); + return; + } + + + // + // Make sure that there is data in the read packet. Depending on the device + // specification, it is possible for it to return a 0 length read in + // certain conditions. + // + + if (NumBytesTransferred == 0) { + TraceEvents(TRACE_LEVEL_WARNING, DBG_INIT, + "HidFx2EvtUsbInterruptPipeReadComplete Zero length read " + "occured on the Interrupt Pipe's Continuous Reader\n" + ); + return; + } + + switchState = WdfMemoryGetBuffer(Buffer, NULL); + + currentSwitchState = *switchState; + previousSwitchState = devContext->CurrentSwitchState; + + // + // we want to know which switch got toggled from 0 to 1 + // Since the device returns the state of all the swicthes and not just the + // one that got toggled, we need to store previous state and xor + // it with current state to know whcih one swicth got toggled. + // Further, the toggle is considered "on" only when it changes from 0 to 1 + // (and not when it changes from 1 to 0). + // + toggledSwitch = (previousSwitchState ^ currentSwitchState) & currentSwitchState; + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, + "HidFx2EvtUsbInterruptPipeReadComplete SwitchState %x, " + "prevSwitch:0x%x, x0R:0x%x\n", + currentSwitchState, + previousSwitchState, + toggledSwitch + ); + + // + // Store switch state in device context + // + devContext->CurrentSwitchState = *switchState; + //if (toggledSwitch != 0) { + devContext->LatestToggledSwitch = toggledSwitch; + //} + + // + // Complete pending Read requests if there is at least one switch toggled + // to on position. + // + if (toggledSwitch != 0) { + BOOLEAN inTimerQueue; + + // + // Debounce the switchpack. A simple logic is used for debouncing. + // A timer is started for 10 ms everytime there is a switch toggled on. + // If within 10 ms same or another switch gets toggled, the timer gets + // reset for another 10 ms. The HID read request is completed in timer + // function if there is still a switch in toggled-on state. Note that + // debouncing happens at the whole switch pack level (not individual + // switches) which means if two different switches are toggled-on within + // 10 ms only one of them (later one in this case) will get accepted and + // sent to hidclass driver + // + inTimerQueue = WdfTimerStart( + devContext->DebounceTimer, + WDF_REL_TIMEOUT_IN_MS(SWICTHPACK_DEBOUNCE_TIME_IN_MS) + ); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, + "Debounce Timer started with timeout of %d ms" + " (TimerReturnValue:%d)\n", + SWICTHPACK_DEBOUNCE_TIME_IN_MS, inTimerQueue); + } + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, + "HidFx2EvtUsbInterruptPipeReadComplete Exit\n"); +} + + +VOID +HidFx2CompleteReadReport( + WDFDEVICE Device + ) +/*++ + +Routine Description + + This method handles the completion of the pended request for the + IOCTL_HID_READ_REPORT + +Arguments: + + Device - Handle to a framework device. + +Return Value: + + None. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + WDFREQUEST request; + PDEVICE_EXTENSION pDevContext = NULL; + size_t bytesReturned = 0; +#ifndef USE_ALTERNATE_HID_REPORT_DESCRIPTOR + UCHAR toggledSwitch = 0; +#endif // USE_ALTERNATE_HID_REPORT_DESCRIPTOR + ULONG bytesToCopy = 0; + PHIDFX2_INPUT_REPORT inputReport = NULL; + + pDevContext = GetDeviceContext(Device); + + // + // Check if there are any pending requests in the Interrupt Message Queue. + // If a request is found then complete the pending request. + // + status = WdfIoQueueRetrieveNextRequest(pDevContext->InterruptMsgQueue, &request); + + if (NT_SUCCESS(status)) { + // + // IOCTL_HID_READ_REPORT is METHOD_NEITHER so WdfRequestRetrieveOutputBuffer + // will correctly retrieve buffer from Irp->UserBuffer. Remember that + // HIDCLASS provides the buffer in the Irp->UserBuffer field + // irrespective of the ioctl buffer type. However, framework is very + // strict about type checking. You cannot get Irp->UserBuffer by using + // WdfRequestRetrieveOutputMemory if the ioctl is not a METHOD_NEITHER + // internal ioctl. + // + bytesToCopy = sizeof(HIDFX2_INPUT_REPORT); + status = WdfRequestRetrieveOutputBuffer(request, + bytesToCopy, + &inputReport, + &bytesReturned);// BufferLength + + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, + "WdfRequestRetrieveOutputBuffer failed with status: 0x%x\n", status); + } else { + +#ifndef USE_ALTERNATE_HID_REPORT_DESCRIPTOR + // + // Map switch pack state. The lower 7 bits of switch pack + // state are mapped to usages in consumer control collection + // while the highest one bit is mapped to sleep usage in system + // control collection + // + toggledSwitch = pDevContext->LatestToggledSwitch; + + if (toggledSwitch & CONSUMER_CONTROL_BUTTONS_BIT_MASK) { + // + //these are consumer control buttons + // + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_IOCTL, + "Consumer control SwitchState: 0x%x\n", toggledSwitch); + + inputReport->ReportId = CONSUMER_CONTROL_REPORT_ID; + inputReport->SwitchStateAsByte = toggledSwitch; + bytesReturned = bytesToCopy; + } + else if (toggledSwitch & SYSTEM_CONTROL_BUTTONS_BIT_MASK) { + // + // these are system control buttons + // + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_IOCTL, + "System Control SwitchState: 0x%x\n", toggledSwitch); + + inputReport->ReportId = SYSTEM_CONTROL_REPORT_ID; + inputReport->SwitchStateAsByte = toggledSwitch; + bytesReturned = bytesToCopy; + } + else { + // + // We can't be here since we already rejected the switch + // state with no swicthes turned on + // + ASSERT(FALSE); + } +#else + // + // Using vendor collection reports instead of HID collections that integrate + // into consumer and system control + // + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_IOCTL, + "Vendor SwitchState: 0x%x\n", pDevContext->CurrentSwitchState); + + inputReport->ReportId = DIP_SWITCHES_REPORT_ID; + inputReport->SwitchStateAsByte = pDevContext->CurrentSwitchState; + bytesReturned = bytesToCopy; + +#endif // USE_ALTERNATE_HID_REPORT_DESCRIPTOR + + } + + WdfRequestCompleteWithInformation(request, status, bytesReturned); + + } else if (status != STATUS_NO_MORE_ENTRIES) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, + "WdfIoQueueRetrieveNextRequest status %08x\n", status); + } + + return; +} + + +NTSTATUS +HidFx2EvtDeviceD0Entry( + 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 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 + +--*/ +{ + PDEVICE_EXTENSION devContext = NULL; + NTSTATUS status = STATUS_SUCCESS; + UCHAR switchState = 0; + + devContext = GetDeviceContext(Device); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_PNP, + "HidFx2EvtDeviceD0Entry Enter - coming from %s\n", + DbgDevicePowerString(PreviousState)); + + // + // Retrieve the current switch state and store it in device context + // + status = HidFx2GetSwitchState(Device, &switchState); + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "Failed to get current swicth state, status: 0x%x\n", status); + return status; + } + + devContext->CurrentSwitchState = switchState; + + // + // Start the target. This will start the continuous reader + // + status = WdfIoTargetStart(WdfUsbTargetPipeGetIoTarget(devContext->InterruptPipe)); + if (NT_SUCCESS(status)) { + devContext->IsPowerUpSwitchState = TRUE; + } + + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "HidFx2EvtDeviceD0Entry Exit, status: 0x%x\n", status); + + return status; +} + + +NTSTATUS +HidFx2EvtDeviceD0Exit( + 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. + + + 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. + +--*/ +{ + PDEVICE_EXTENSION devContext; + + PAGED_CODE(); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_PNP, + "HidFx2EvtDeviceD0Exit Enter- moving to %s\n", + DbgDevicePowerString(TargetState)); + + devContext = GetDeviceContext(Device); + + WdfIoTargetStop(WdfUsbTargetPipeGetIoTarget( + devContext->InterruptPipe), WdfIoTargetCancelSentIo); + + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "HidFx2EvtDeviceD0Exit Exit\n"); + + return STATUS_SUCCESS; +} + + +NTSTATUS +HidFx2GetSwitchState( + IN WDFDEVICE Device, + OUT PUCHAR SwitchState + ) +/*++ + +Routine Description: + + This function gets the swicth state of teh USB device + +Arguments: + + Device - Handle to a framework device object. + + SwitchState - Pointer to a variable that receives the switch state + +Return Value: + + Success implies that the device can be used. Failure will result in the + device stack being torn down. + +--*/ +{ + PDEVICE_EXTENSION devContext = NULL; + NTSTATUS status = STATUS_SUCCESS; + WDF_MEMORY_DESCRIPTOR memDesc; + WDF_USB_CONTROL_SETUP_PACKET controlSetupPacket; + ULONG bytesTransferred = 0; + + devContext = GetDeviceContext(Device); + + // + // set the segment state on the USB device + // + WDF_USB_CONTROL_SETUP_PACKET_INIT_VENDOR(&controlSetupPacket, + BmRequestDeviceToHost, + BmRequestToDevice, + HIDFX2_READ_SWITCH_STATE, // Request + 0, // Value + 0); // Index + + WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&memDesc, + SwitchState, + sizeof(UCHAR)); + + status = WdfUsbTargetDeviceSendControlTransferSynchronously( + devContext->UsbDevice, + NULL, // Optional WDFREQUEST + NULL, // PWDF_REQUEST_SEND_OPTIONS + &controlSetupPacket, + &memDesc, + &bytesTransferred + ); + + if(!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, + "GetSwitchState: Failed to read switch state - 0x%x \n", status); + + } else { + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL, + "GetSwitchState: Switch state is 0x%x\n", *SwitchState); + } + + return status; +} + + +VOID +HidFx2EvtTimerFunction( + IN WDFTIMER Timer + ) +/*++ + +Routine Description: + + This function gets called when the timeout period of debounce timer elapses. + +Arguments: + + Timer - Handle to a framework timer object. + +Return Value: + + none + +--*/ +{ +#ifndef USE_ALTERNATE_HID_REPORT_DESCRIPTOR + PDEVICE_EXTENSION devContext = + GetDeviceContext(WdfTimerGetParentObject(Timer)); + + // + // Complete the request if there is a swicthed in toggled-on position + // + if (devContext->LatestToggledSwitch != 0) { + HidFx2CompleteReadReport(WdfTimerGetParentObject(Timer)); + } + +#else + + // + // Always complete the read request for the vendor collection + // input report. + // + HidFx2CompleteReadReport(WdfTimerGetParentObject(Timer)); + +#endif // USE_ALTERNATE_HID_REPORT_DESCRIPTOR +} + +USBD_STATUS +HidFx2ValidateConfigurationDescriptor( + IN PUSB_CONFIGURATION_DESCRIPTOR ConfigDesc, + IN ULONG BufferLength, + _Inout_ PUCHAR *Offset + ) +/*++ + +Routine Description: + + Validates a USB Configuration Descriptor + +Parameters: + + ConfigDesc: Pointer to the entire USB Configuration descriptor returned by the device + + BufferLength: Known size of buffer pointed to by ConfigDesc (Not wTotalLength) + + Offset: if the USBD_STATUS returned is not USBD_STATUS_SUCCESS, offet will + be set to the address within the ConfigDesc buffer where the failure occured. + +Return Value: + + USBD_STATUS + Success implies the configuration descriptor is valid. + +--*/ +{ + + + USBD_STATUS status = USBD_STATUS_SUCCESS; + USHORT ValidationLevel = 3; + + PAGED_CODE(); + + // + // Call USBD_ValidateConfigurationDescriptor to validate the descriptors which are present in this supplied configuration descriptor. + // USBD_ValidateConfigurationDescriptor validates that all descriptors are completely contained within the configuration descriptor buffer. + // It also checks for interface numbers, number of endpoints in an interface etc. + // Please refer to msdn documentation for this function for more information. + // + + status = USBD_ValidateConfigurationDescriptor( ConfigDesc, BufferLength , ValidationLevel , Offset , POOL_TAG ); + if (!(NT_SUCCESS (status)) ){ + return status; + } + + // + // TODO: You should validate the correctness of other descriptors which are not taken care by USBD_ValidateConfigurationDescriptor + // Check that all such descriptors have size >= sizeof(the descriptor they point to) + // Check for any association between them if required + // + + 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"; + } +} diff --git a/hid/vhidmini2/ReadMe.md b/hid/vhidmini2/ReadMe.md index 567ff0633..7679a8492 100644 --- a/hid/vhidmini2/ReadMe.md +++ b/hid/vhidmini2/ReadMe.md @@ -1,13 +1,9 @@ HID Minidriver Sample (UMDF V2) ====================================== - The *HID minidriver* sample demonstrates how to write a HID minidriver using User-Mode Driver Framework (UMDF). The sample demonstrates how to communicate with an HID minidriver from an HID client using a custom-feature item in order to control certain features of the HID minidriver. This is needed since other conventional modes for communicating with a driver, like custom IOCTL or WMI, do not work with the HID minidriver. The sample also is useful in testing the correctness of a HID report descriptor without using a physical device. -**Note** -Due to a known bug in the current Windows Driver Kit tools, when building this sample in Debug mode, the exe project may report some ApiValidator warnings. These can be safely ignored. - Related topics -------------- diff --git a/hid/vhidmini2/app/testvhid.vcxproj b/hid/vhidmini2/app/testvhid.vcxproj index c076e3153..3e000d1e7 100644 --- a/hid/vhidmini2/app/testvhid.vcxproj +++ b/hid/vhidmini2/app/testvhid.vcxproj @@ -19,11 +19,11 @@
- {33D1A6C5-8347-43BB-853C-0820EF7203C6} + {52B4AF60-09E2-4C74-804C-08E5BF7ACA1E} $(MSBuildProjectName) Debug Win32 - {B610E167-90BB-4434-B532-3E50E9698B60} + {0C5D2471-0E43-4210-8174-8165AA2D514A} @@ -174,7 +174,6 @@ - diff --git a/hid/vhidmini2/app/testvhid.vcxproj.Filters b/hid/vhidmini2/app/testvhid.vcxproj.Filters index ab6384922..f43619c2f 100644 --- a/hid/vhidmini2/app/testvhid.vcxproj.Filters +++ b/hid/vhidmini2/app/testvhid.vcxproj.Filters @@ -3,15 +3,15 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {E0769A0A-01B1-4870-A7A0-251E075207E9} + {33CB7352-5B0B-4FFD-90AA-C71D97B42B9B} h;hpp;hxx;hm;inl;inc;xsd - {1E43CCDB-2472-497A-AE91-22BD2D4C046C} + {BEC094B2-A429-4464-B266-5A5A38C318B0} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {EEC98E9E-7C67-4A59-917F-31BCA04175E9} + {78368937-093B-4F35-A915-C0D74A6F5602} diff --git a/hid/vhidmini2/driver/kmdf/vhidmini.inx b/hid/vhidmini2/driver/kmdf/vhidmini.inx index c4f776f67..8189e4078 100644 --- a/hid/vhidmini2/driver/kmdf/vhidmini.inx +++ b/hid/vhidmini2/driver/kmdf/vhidmini.inx @@ -17,7 +17,7 @@ Signature="$WINDOWS NT$" Class=Sample ClassGuid={78A1C341-4539-11d3-B88D-00C04FAD5171} -Provider=%MSFT% +Provider=%ProviderString% DriverVer=10/01/2002,6.0.5058.0 CatalogFile=wudf.cat @@ -33,7 +33,7 @@ HKR,,Icon,,-5 ; ================= Device section ===================== [Manufacturer] -%MfgName%=Microsoft,NT$ARCH$.6.1 +%ManufacturerString%=Microsoft,NT$ARCH$.6.1 ; Works on Win7 and later because we use inbox HID-KMDF mapper [Microsoft.NT$ARCH$.6.1] @@ -88,9 +88,9 @@ KMDriverCopy=12 ;---------------------------------------------------------------; [Strings] -MSFT = "Microsoft" -MfgName = "Microsoft" -ClassName = "Sample Device" -DeviceDesc = "HID minidriver (KMDF version) Device" -ServiceDesc= "HID minidriver (KMDF version) Service" -DiskDesc = "HID minidriver (KMDF version) Installation Disk" +ProviderString = "TODO-Set-Provider" +ManufacturerString = "TODO-Set-Manufacturer" +ClassName = "Sample Device" +DeviceDesc = "HID minidriver (KMDF version) Device" +ServiceDesc = "HID minidriver (KMDF version) Service" +DiskDesc = "HID minidriver (KMDF version) Installation Disk" diff --git a/hid/vhidmini2/driver/kmdf/vhidmini.vcxproj b/hid/vhidmini2/driver/kmdf/vhidmini.vcxproj index 98ed709a8..fc65fd10a 100644 --- a/hid/vhidmini2/driver/kmdf/vhidmini.vcxproj +++ b/hid/vhidmini2/driver/kmdf/vhidmini.vcxproj @@ -19,12 +19,12 @@ - {20843589-DA00-4ADD-BDA0-62C7065C80CB} + {E6FB12D0-E6DD-447C-844D-B6B3B5F5693F} $(MSBuildProjectName) 1 Debug Win32 - {60CAABFB-B417-4ED4-816E-6E6F27119888} + {FBE937B8-E7D3-4F76-AEE5-8E21378A6E64} @@ -166,7 +166,6 @@ - diff --git a/hid/vhidmini2/driver/kmdf/vhidmini.vcxproj.Filters b/hid/vhidmini2/driver/kmdf/vhidmini.vcxproj.Filters index d658706d6..00c9879a5 100644 --- a/hid/vhidmini2/driver/kmdf/vhidmini.vcxproj.Filters +++ b/hid/vhidmini2/driver/kmdf/vhidmini.vcxproj.Filters @@ -3,25 +3,22 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {529C6B82-33F1-447C-8745-EA15DF3B320A} + {ACC3297A-03C9-45B6-B62C-0A0A3E089CD0} h;hpp;hxx;hm;inl;inc;xsd - {14FD4557-FD3C-46E8-91BC-EB85C1102DEC} + {7BBF913C-90ED-455D-9A8D-EB2ACA2113FB} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {120DA6E4-65A4-4995-ACDA-1401660F7DE9} + {04EBC112-1A1A-4383-91F9-823EEF5FE3B4} inf;inv;inx;mof;mc; - {9161B866-C7A9-49F8-99BF-816F32DCDC37} + {7FD6281F-9EA9-46FF-9767-C95E9A9FEEBB} - - Driver Files - Driver Files diff --git a/hid/vhidmini2/driver/umdf2/VhidminiUm.inx b/hid/vhidmini2/driver/umdf2/VhidminiUm.inx index 48512f38b..9c4872c5b 100644 --- a/hid/vhidmini2/driver/umdf2/VhidminiUm.inx +++ b/hid/vhidmini2/driver/umdf2/VhidminiUm.inx @@ -17,7 +17,7 @@ Signature="$WINDOWS NT$" Class=Sample ClassGuid={78A1C341-4539-11d3-B88D-00C04FAD5171} -Provider=%MSFT% +Provider=%ProviderString% DriverVer=10/01/2002,6.0.5058.0 CatalogFile=wudf.cat @@ -33,7 +33,7 @@ HKR,,Icon,,-5 ; ================= Device section ===================== [Manufacturer] -%MfgName%=Microsoft, NT$ARCH$.6.3 +%ManufacturerString%=Microsoft, NT$ARCH$.6.3 [Microsoft.NT$ARCH$.6.3] %DeviceDesc%=VhidminiUm, root\VhidminiUm @@ -93,9 +93,9 @@ UMDriverCopy=12,UMDF ; copy to drivers\umdf ;---------------------------------------------------------------; [Strings] -MSFT="Microsoft" -MfgName="Microsoft" -ClassName="Sample Device" -Disk_Description ="HID minidriver (UMDF v2) Installation Disk" -DeviceDesc ="HID minidriver (UMDF v2) Device" -WudfRdDisplayName="Windows Driver Foundation - User-mode Driver Framework Reflector" +ProviderString ="TODO-Set-Provider" +ManufacturerString ="TODO-Set-Manufacturer" +ClassName ="Sample Device" +Disk_Description ="HID minidriver (UMDF v2) Installation Disk" +DeviceDesc ="HID minidriver (UMDF v2) Device" +WudfRdDisplayName ="Windows Driver Foundation - User-mode Driver Framework Reflector" diff --git a/hid/vhidmini2/driver/umdf2/VhidminiUm.vcxproj b/hid/vhidmini2/driver/umdf2/VhidminiUm.vcxproj index 4e183bbb6..fc8d27cab 100644 --- a/hid/vhidmini2/driver/umdf2/VhidminiUm.vcxproj +++ b/hid/vhidmini2/driver/umdf2/VhidminiUm.vcxproj @@ -19,12 +19,12 @@ - {80837437-FDA1-43E1-9656-79CA6F3C84DA} + {CD1D0FA6-08E7-46FC-B119-D65B8D52C2E1} $(MSBuildProjectName) 2 Debug Win32 - {449E70EF-5742-49E4-9EC9-022E79311E53} + {35F5415D-57BD-487A-BB64-040C2B51C1A9} @@ -194,7 +194,6 @@ - diff --git a/hid/vhidmini2/driver/umdf2/VhidminiUm.vcxproj.Filters b/hid/vhidmini2/driver/umdf2/VhidminiUm.vcxproj.Filters index 5ef2ab6ab..6ebaf4837 100644 --- a/hid/vhidmini2/driver/umdf2/VhidminiUm.vcxproj.Filters +++ b/hid/vhidmini2/driver/umdf2/VhidminiUm.vcxproj.Filters @@ -3,25 +3,22 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {1E695972-0767-48CC-92BD-16A66C3FEFB5} + {BF2270C6-A805-4981-BA76-511CA736A185} h;hpp;hxx;hm;inl;inc;xsd - {AA23C292-0CD2-4784-A71A-43C0B1A98BDE} + {557A560B-8241-4573-BE5A-FC0B1CF86C1F} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {09822CD3-C75B-411F-A5C9-D1957BBD9B81} + {8456BF4B-1629-491B-851E-F56B8F4C9DD1} inf;inv;inx;mof;mc; - {CF1C68C7-1F4A-49CB-9417-711A4031F270} + {161882E3-1C88-4B50-A3E4-AC0513E42847} - - Driver Files - Driver Files diff --git a/hid/vhidmini2/vhidmini2.sln b/hid/vhidmini2/vhidmini2.sln index e8e2bbf6a..198230486 100644 --- a/hid/vhidmini2/vhidmini2.sln +++ b/hid/vhidmini2/vhidmini2.sln @@ -3,19 +3,19 @@ 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", "{72955C82-582A-4ECE-AB2B-5F17AA6A7D35}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "App", "App", "{A0CA14C5-80C0-4782-B7A5-0F7AADAA97E0}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Umdf2", "Umdf2", "{EE026314-C7B7-4A0C-949F-8A8BD0232329}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Umdf2", "Umdf2", "{C6FE2D11-F8D0-4B8B-9243-FFF05ABA95E7}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Driver", "Driver", "{B9945C2D-CF78-4A77-8474-D872AF45B5AF}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Driver", "Driver", "{0759E509-C2B6-4C7D-A6E1-832150847530}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Kmdf", "Kmdf", "{283408F4-D822-4E3C-A0F4-4466FDB51D25}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Kmdf", "Kmdf", "{0E53ABAA-6885-47FE-95A0-7ED6585A1FE9}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testvhid", "app\testvhid.vcxproj", "{33D1A6C5-8347-43BB-853C-0820EF7203C6}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testvhid", "app\testvhid.vcxproj", "{52B4AF60-09E2-4C74-804C-08E5BF7ACA1E}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "VhidminiUm", "driver\umdf2\VhidminiUm.vcxproj", "{80837437-FDA1-43E1-9656-79CA6F3C84DA}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "VhidminiUm", "driver\umdf2\VhidminiUm.vcxproj", "{CD1D0FA6-08E7-46FC-B119-D65B8D52C2E1}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "vhidmini", "driver\kmdf\vhidmini.vcxproj", "{20843589-DA00-4ADD-BDA0-62C7065C80CB}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "vhidmini", "driver\kmdf\vhidmini.vcxproj", "{E6FB12D0-E6DD-447C-844D-B6B3B5F5693F}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -25,39 +25,39 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {33D1A6C5-8347-43BB-853C-0820EF7203C6}.Debug|Win32.ActiveCfg = Debug|Win32 - {33D1A6C5-8347-43BB-853C-0820EF7203C6}.Debug|Win32.Build.0 = Debug|Win32 - {33D1A6C5-8347-43BB-853C-0820EF7203C6}.Release|Win32.ActiveCfg = Release|Win32 - {33D1A6C5-8347-43BB-853C-0820EF7203C6}.Release|Win32.Build.0 = Release|Win32 - {33D1A6C5-8347-43BB-853C-0820EF7203C6}.Debug|x64.ActiveCfg = Debug|x64 - {33D1A6C5-8347-43BB-853C-0820EF7203C6}.Debug|x64.Build.0 = Debug|x64 - {33D1A6C5-8347-43BB-853C-0820EF7203C6}.Release|x64.ActiveCfg = Release|x64 - {33D1A6C5-8347-43BB-853C-0820EF7203C6}.Release|x64.Build.0 = Release|x64 - {80837437-FDA1-43E1-9656-79CA6F3C84DA}.Debug|Win32.ActiveCfg = Debug|Win32 - {80837437-FDA1-43E1-9656-79CA6F3C84DA}.Debug|Win32.Build.0 = Debug|Win32 - {80837437-FDA1-43E1-9656-79CA6F3C84DA}.Release|Win32.ActiveCfg = Release|Win32 - {80837437-FDA1-43E1-9656-79CA6F3C84DA}.Release|Win32.Build.0 = Release|Win32 - {80837437-FDA1-43E1-9656-79CA6F3C84DA}.Debug|x64.ActiveCfg = Debug|x64 - {80837437-FDA1-43E1-9656-79CA6F3C84DA}.Debug|x64.Build.0 = Debug|x64 - {80837437-FDA1-43E1-9656-79CA6F3C84DA}.Release|x64.ActiveCfg = Release|x64 - {80837437-FDA1-43E1-9656-79CA6F3C84DA}.Release|x64.Build.0 = Release|x64 - {20843589-DA00-4ADD-BDA0-62C7065C80CB}.Debug|Win32.ActiveCfg = Debug|Win32 - {20843589-DA00-4ADD-BDA0-62C7065C80CB}.Debug|Win32.Build.0 = Debug|Win32 - {20843589-DA00-4ADD-BDA0-62C7065C80CB}.Release|Win32.ActiveCfg = Release|Win32 - {20843589-DA00-4ADD-BDA0-62C7065C80CB}.Release|Win32.Build.0 = Release|Win32 - {20843589-DA00-4ADD-BDA0-62C7065C80CB}.Debug|x64.ActiveCfg = Debug|x64 - {20843589-DA00-4ADD-BDA0-62C7065C80CB}.Debug|x64.Build.0 = Debug|x64 - {20843589-DA00-4ADD-BDA0-62C7065C80CB}.Release|x64.ActiveCfg = Release|x64 - {20843589-DA00-4ADD-BDA0-62C7065C80CB}.Release|x64.Build.0 = Release|x64 + {52B4AF60-09E2-4C74-804C-08E5BF7ACA1E}.Debug|Win32.ActiveCfg = Debug|Win32 + {52B4AF60-09E2-4C74-804C-08E5BF7ACA1E}.Debug|Win32.Build.0 = Debug|Win32 + {52B4AF60-09E2-4C74-804C-08E5BF7ACA1E}.Release|Win32.ActiveCfg = Release|Win32 + {52B4AF60-09E2-4C74-804C-08E5BF7ACA1E}.Release|Win32.Build.0 = Release|Win32 + {52B4AF60-09E2-4C74-804C-08E5BF7ACA1E}.Debug|x64.ActiveCfg = Debug|x64 + {52B4AF60-09E2-4C74-804C-08E5BF7ACA1E}.Debug|x64.Build.0 = Debug|x64 + {52B4AF60-09E2-4C74-804C-08E5BF7ACA1E}.Release|x64.ActiveCfg = Release|x64 + {52B4AF60-09E2-4C74-804C-08E5BF7ACA1E}.Release|x64.Build.0 = Release|x64 + {CD1D0FA6-08E7-46FC-B119-D65B8D52C2E1}.Debug|Win32.ActiveCfg = Debug|Win32 + {CD1D0FA6-08E7-46FC-B119-D65B8D52C2E1}.Debug|Win32.Build.0 = Debug|Win32 + {CD1D0FA6-08E7-46FC-B119-D65B8D52C2E1}.Release|Win32.ActiveCfg = Release|Win32 + {CD1D0FA6-08E7-46FC-B119-D65B8D52C2E1}.Release|Win32.Build.0 = Release|Win32 + {CD1D0FA6-08E7-46FC-B119-D65B8D52C2E1}.Debug|x64.ActiveCfg = Debug|x64 + {CD1D0FA6-08E7-46FC-B119-D65B8D52C2E1}.Debug|x64.Build.0 = Debug|x64 + {CD1D0FA6-08E7-46FC-B119-D65B8D52C2E1}.Release|x64.ActiveCfg = Release|x64 + {CD1D0FA6-08E7-46FC-B119-D65B8D52C2E1}.Release|x64.Build.0 = Release|x64 + {E6FB12D0-E6DD-447C-844D-B6B3B5F5693F}.Debug|Win32.ActiveCfg = Debug|Win32 + {E6FB12D0-E6DD-447C-844D-B6B3B5F5693F}.Debug|Win32.Build.0 = Debug|Win32 + {E6FB12D0-E6DD-447C-844D-B6B3B5F5693F}.Release|Win32.ActiveCfg = Release|Win32 + {E6FB12D0-E6DD-447C-844D-B6B3B5F5693F}.Release|Win32.Build.0 = Release|Win32 + {E6FB12D0-E6DD-447C-844D-B6B3B5F5693F}.Debug|x64.ActiveCfg = Debug|x64 + {E6FB12D0-E6DD-447C-844D-B6B3B5F5693F}.Debug|x64.Build.0 = Debug|x64 + {E6FB12D0-E6DD-447C-844D-B6B3B5F5693F}.Release|x64.ActiveCfg = Release|x64 + {E6FB12D0-E6DD-447C-844D-B6B3B5F5693F}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {33D1A6C5-8347-43BB-853C-0820EF7203C6} = {72955C82-582A-4ECE-AB2B-5F17AA6A7D35} - {80837437-FDA1-43E1-9656-79CA6F3C84DA} = {EE026314-C7B7-4A0C-949F-8A8BD0232329} - {20843589-DA00-4ADD-BDA0-62C7065C80CB} = {283408F4-D822-4E3C-A0F4-4466FDB51D25} - {EE026314-C7B7-4A0C-949F-8A8BD0232329} = {B9945C2D-CF78-4A77-8474-D872AF45B5AF} - {283408F4-D822-4E3C-A0F4-4466FDB51D25} = {B9945C2D-CF78-4A77-8474-D872AF45B5AF} + {52B4AF60-09E2-4C74-804C-08E5BF7ACA1E} = {A0CA14C5-80C0-4782-B7A5-0F7AADAA97E0} + {CD1D0FA6-08E7-46FC-B119-D65B8D52C2E1} = {C6FE2D11-F8D0-4B8B-9243-FFF05ABA95E7} + {E6FB12D0-E6DD-447C-844D-B6B3B5F5693F} = {0E53ABAA-6885-47FE-95A0-7ED6585A1FE9} + {C6FE2D11-F8D0-4B8B-9243-FFF05ABA95E7} = {0759E509-C2B6-4C7D-A6E1-832150847530} + {0E53ABAA-6885-47FE-95A0-7ED6585A1FE9} = {0759E509-C2B6-4C7D-A6E1-832150847530} EndGlobalSection EndGlobal diff --git a/input/hiddigi/SynapticsTouch/SynapticsTouch.inx b/input/hiddigi/SynapticsTouch/SynapticsTouch.inx new file mode 100644 index 000000000..680ba5926 --- /dev/null +++ b/input/hiddigi/SynapticsTouch/SynapticsTouch.inx @@ -0,0 +1,199 @@ +; SynapticsTouch.INF +; Copyright (c) 2000,2011 Microsoft Corporation +; +; Sample code. Dealpoint ID #843729. +; + +[Version] +Signature = "$WINDOWS NT$" +Class = HIDClass +ClassGUID = {745A17A0-74D3-11D0-B6FE-00A0C90F57DA} +Provider = %Provider_Name% +DriverVer = 08/01/2011,1.0 +; Uncomment the following line when you have a valid catalog file. +; If you use bogus catalog file installation will fail. +CatalogFile=KmdfSamples.cat + +[DestinationDirs] +DefaultDestDir = 12 + +[SourceDisksNames] +1 = %Installation_Disk%,,,"" + +[SourceDisksFiles] +SynapticsTouch.sys = 1,, + +[Manufacturer] +%Manufacturer_Name% = Standard,NTARM + +[Standard.NTARM] +%TchDriver_Device_Desc% = TchDriver_Device.NT, ACPI\OEM_TOUCH + +;***************************************** +; TchDriver client driver Install Section +;***************************************** + +[TchDriver_Device.NT] +CopyFiles = TchDriver_Device.NT.CopyFiles + +[TchDriver_Device.NT.CopyFiles] +SynapticsTouch.sys + +[TchDriver_Device.NT.HW] +AddReg = FilterInst.NT.HW.AddReg +AddReg = CapButtons.NT.HW.AddReg +AddReg = PowerConfig.NT.HW.AddReg +AddReg = Configuration.NT.HW.AddReg +AddReg = Resolutions.NT.HW.AddReg +AddReg = DisplaySize.NT.HW.AddReg +AddReg = ButtonBacklights.NT.HW.AddReg +AddReg = LegacyTouchScaling.NT.HW.AddReg + +[FilterInst.NT.HW.AddReg] +HKR,,"UpperFilters", %REG_MULTI_SZ%, mshidkmdf +HKR,,"MultiportDevice", %REG_DWORD%, 0 + +[CapButtons.NT.HW.AddReg] +HKLM,"SYSTEM\TOUCH\BUTTONS", "Area", %REG_SZ%, "0,1280 768,1390" +HKLM,"SYSTEM\TOUCH\BUTTONS", "Count", %REG_DWORD%, 0x3 +HKLM,"SYSTEM\TOUCH\BUTTONS", "Vibrate", %REG_DWORD%, 0x1 +HKLM,"SYSTEM\TOUCH\BUTTONS", "Duration", %REG_DWORD%, 0x19 +HKLM,"SYSTEM\TOUCH\BUTTONS", "Intensity",%REG_DWORD%, 0x32 +HKLM,"SYSTEM\TOUCH\BUTTONS\0", "Area", %REG_SZ%, "0,1295 236,1390" +HKLM,"SYSTEM\TOUCH\BUTTONS\0", "Name", %REG_SZ% "Back" +HKLM,"SYSTEM\TOUCH\BUTTONS\0", "VKey", %REG_DWORD%, 0x1B +HKLM,"SYSTEM\TOUCH\BUTTONS\1", "Area", %REG_SZ%, "277,1295 492,1390" +HKLM,"SYSTEM\TOUCH\BUTTONS\1", "Name", %REG_SZ%, "Start" +HKLM,"SYSTEM\TOUCH\BUTTONS\1", "VKey", %REG_DWORD%, 0x71 +HKLM,"SYSTEM\TOUCH\BUTTONS\2", "Area", %REG_SZ%, "533,1295 768,1390" +HKLM,"SYSTEM\TOUCH\BUTTONS\2", "Name", %REG_SZ%, "Search" +HKLM,"SYSTEM\TOUCH\BUTTONS\2", "VKey", %REG_DWORD%, 0x72 + +[PowerConfig.NT.HW.AddReg] +HKR,,"EnhancedPowerManagementEnabled", %REG_DWORD%, 1 +HKR,,"EnhancedPowerManagementUseMonitor", %REG_DWORD%, 1 + +[Configuration.NT.HW.AddReg] +HKR,%Configuration%, "PepRemovesVoltageInD3", %REG_DWORD%, 0x0 +HKR,%Configuration%, "SleepMode", %REG_DWORD%, 0x0 +HKR,%Configuration%, "NoSleep", %REG_DWORD%, 0x1 +HKR,%Configuration%, "ReportRate", %REG_DWORD%, 0x0 +HKR,%Configuration%, "Configured", %REG_DWORD%, 0x1 +HKR,%Configuration%, "InterruptEnable", %REG_DWORD%, 0xf +HKR,%Configuration%, "DozeInterval", %REG_DWORD%, 0x3 +HKR,%Configuration%, "DozeThreshold", %REG_DWORD%, 0x1e +HKR,%Configuration%, "DozeHoldoff", %REG_DWORD%, 0x5 +HKR,%Configuration%, "ReportingMode", %REG_DWORD%, 0x1 +HKR,%Configuration%, "AbsPosFilt", %REG_DWORD%, 0x1 +HKR,%Configuration%, "RelPosFilt", %REG_DWORD%, 0x0 +HKR,%Configuration%, "RelBallistics", %REG_DWORD%, 0x0 +HKR,%Configuration%, "Dribble", %REG_DWORD%, 0x0 +HKR,%Configuration%, "PalmDetectThreshold", %REG_DWORD%, 0xb +HKR,%Configuration%, "MotionSensitivity", %REG_DWORD%, 0x0 +HKR,%Configuration%, "ManTrackEn", %REG_DWORD%, 0x0 +HKR,%Configuration%, "ManTrackedFinger", %REG_DWORD%, 0x0 +HKR,%Configuration%, "DeltaXPosThreshold", %REG_DWORD%, 0x0 +HKR,%Configuration%, "DeltaYPosThreshold", %REG_DWORD%, 0x0 +HKR,%Configuration%, "Velocity", %REG_DWORD%, 0x0 +HKR,%Configuration%, "Acceleration", %REG_DWORD%, 0x0 +HKR,%Configuration%, "SensorMaxXPos", %REG_DWORD%, 0x330 +HKR,%Configuration%, "SensorMaxYPos", %REG_DWORD%, 0x572 +HKR,%Configuration%, "ZTouchThreshold", %REG_DWORD%, 0x1e +HKR,%Configuration%, "ZHysteresis", %REG_DWORD%, 0x5 +HKR,%Configuration%, "SmallZThreshold", %REG_DWORD%, 0x28 +HKR,%Configuration%, "SmallZScaleFactor", %REG_DWORD%, 0x28f5 +HKR,%Configuration%, "LargeZScaleFactor", %REG_DWORD%, 0x051e +HKR,%Configuration%, "AlgorithmSelection", %REG_DWORD%, 0x1 +HKR,%Configuration%, "WxScaleFactor", %REG_DWORD%, 0x30 +HKR,%Configuration%, "WxOffset", %REG_DWORD%, 0x0 +HKR,%Configuration%, "WyScaleFactor", %REG_DWORD%, 0x30 +HKR,%Configuration%, "WyOffset", %REG_DWORD%, 0x0 +HKR,%Configuration%, "XPitch", %REG_DWORD%, 0x4800 +HKR,%Configuration%, "YPitch", %REG_DWORD%, 0x4800 +HKR,%Configuration%, "FingerWidthX", %REG_DWORD%, 0xea4f +HKR,%Configuration%, "FingerWidthY", %REG_DWORD%, 0xdf6c +HKR,%Configuration%, "ReportMeasuredSize", %REG_DWORD%, 0x0 +HKR,%Configuration%, "SegmentationSensitivity", %REG_DWORD%, 0x70 +HKR,%Configuration%, "XClipLo", %REG_DWORD%, 0x0 +HKR,%Configuration%, "XClipHi", %REG_DWORD%, 0x0 +HKR,%Configuration%, "YClipLo", %REG_DWORD%, 0x0 +HKR,%Configuration%, "YClipHi", %REG_DWORD%, 0x0 +HKR,%Configuration%, "MinFingerSeparation", %REG_DWORD%, 0xa +HKR,%Configuration%, "MaxFingerMovement", %REG_DWORD%, 0x4 + +[Resolutions.NT.HW.AddReg] +HKLM,%ScreenProps%,"TouchSwapAxes", %REG_DWORD%, 0x0 +HKLM,%ScreenProps%,"TouchInvertXAxis", %REG_DWORD%, 0x0 +HKLM,%ScreenProps%,"TouchInvertYAxis", %REG_DWORD%, 0x1 +HKLM,%ScreenProps%,"TouchPhysicalWidth", %REG_DWORD%, 0x330 +HKLM,%ScreenProps%,"TouchPhysicalHeight", %REG_DWORD%, 0x572 +HKLM,%ScreenProps%,"TouchPhysicalButtonHeight", %REG_DWORD%, 0x6E +HKLM,%ScreenProps%,"TouchPillarBoxWidthLeft", %REG_DWORD%, 0x8 +HKLM,%ScreenProps%,"TouchPillarBoxWidthRight", %REG_DWORD%, 0x8 +HKLM,%ScreenProps%,"TouchLetterBoxHeightTop", %REG_DWORD%, 0x4 +HKLM,%ScreenProps%,"TouchLetterBoxHeightBottom", %REG_DWORD%, 0x0 +HKLM,%ScreenProps%,"DisplayPhysicalWidth", %REG_DWORD%, 0x320 +HKLM,%ScreenProps%,"DisplayPhysicalHeight", %REG_DWORD%, 0x500 +HKLM,%ScreenProps%,"DisplayPillarBoxWidthLeft", %REG_DWORD%, 0x10 +HKLM,%ScreenProps%,"DisplayPillarBoxWidthRight", %REG_DWORD%, 0x10 +HKLM,%ScreenProps%,"DisplayLetterBoxHeightTop", %REG_DWORD%, 0x0 +HKLM,%ScreenProps%,"DisplayLetterBoxHeightBottom", %REG_DWORD%, 0x0 +HKLM,%ScreenProps%,"DisplayViewableWidth", %REG_DWORD%, 0x1e0 +HKLM,%ScreenProps%,"DisplayViewableHeight", %REG_DWORD%, 0x320 + +[DisplaySize.NT.HW.AddReg] +; The following values will be used to convert mm to display pixels. +; These are the dimensions of the Lumia 920, please remove this line +; when you've updated the dimensions to match your windows phone. +HKLM,%DisplaySize%,"DisplayHeight10um", %REG_DWORD%, 0x25E4 ; 97 mm +HKLM,%DisplaySize%,"DisplayWidth10um", %REG_DWORD%, 0x1702 ; 58.9 mm + +[ButtonBacklights.NT.HW.AddReg] +HKLM,"SYSTEM\TOUCH\BUTTONS\BACKLIGHT", "LedCount", %REG_DWORD%, 0x3 +HKLM,"SYSTEM\TOUCH\BUTTONS\BACKLIGHT", "LedIndexList", %REG_MULTI_SZ%, "0","1","2" +HKLM,"SYSTEM\TOUCH\BUTTONS\BACKLIGHT", "InactivityTimeout", %REG_DWORD%, 0 ; No timeout (msec) + +[LegacyTouchScaling.NT.HW.AddReg] +; This implies that the correct physical/logical min/max/unit/exp +; values for X/Y in the HID descriptor are being used -- don't just +; report pixels +HKR,,"LegacyTouchScaling", %REG_DWORD%, 0x0 + +;-------------- Service installation +[TchDriver_Device.NT.Services] +AddService = SynapticsTouch, %SPSVCINST_ASSOCSERVICE%, TchDriver_Service_Inst + +; ------------- TchDriver driver install section +[TchDriver_Service_Inst] +DisplayName = %TchDriver_Service_Desc% +ServiceType = %SERVICE_KERNEL_DRIVER% +StartType = %SERVICE_DEMAND_START% +ErrorControl = %SERVICE_ERROR_NORMAL% +ServiceBinary = %12%\SynapticsTouch.sys +LoadOrderGroup = Base + +; ------------- Strings section +[Strings] +Provider_Name = "TODO-Set-Provider" +Manufacturer_Name = "Synaptics" +Installation_Disk = "Touch Driver Installation Disk" +TchDriver_Device_Desc = "Touch Driver" +TchDriver_Service_Desc = "Synaptics Touch Driver Service" + +; ------------- non-localizable strings +Configuration = "Settings" +ScreenProps = "SYSTEM\TOUCH\SCREENPROPERTIES" +DisplaySize = "SYSTEM\TOUCH\DISPLAYPROPERTIES" +SPSVCINST_ASSOCSERVICE = 0x00000002 +SERVICE_KERNEL_DRIVER = 1 +SERVICE_BOOT_START = 0 +SERVICE_SYSTEM_START = 1 +SERVICE_DEMAND_START = 3 +SERVICE_ERROR_NORMAL = 1 +SERVICE_ERROR_IGNORE = 0 +SERVICE_ERROR_CRITICAL = 3 +REG_EXPAND_SZ = 0x00020000 +REG_MULTI_SZ = 0x00010000 +REG_DWORD = 0x00010001 +REG_SZ = 0x00000000 + diff --git a/input/hiddigi/SynapticsTouch/SynapticsTouch.sln b/input/hiddigi/SynapticsTouch/SynapticsTouch.sln new file mode 100644 index 000000000..d0bb5610a --- /dev/null +++ b/input/hiddigi/SynapticsTouch/SynapticsTouch.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}") = "SynapticsTouch", "SynapticsTouch.vcxproj", "{B07A4DCF-0F5D-4F7A-9C90-C972527A69CD}" +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 + {B07A4DCF-0F5D-4F7A-9C90-C972527A69CD}.Debug|Win32.ActiveCfg = Debug|Win32 + {B07A4DCF-0F5D-4F7A-9C90-C972527A69CD}.Debug|Win32.Build.0 = Debug|Win32 + {B07A4DCF-0F5D-4F7A-9C90-C972527A69CD}.Release|Win32.ActiveCfg = Release|Win32 + {B07A4DCF-0F5D-4F7A-9C90-C972527A69CD}.Release|Win32.Build.0 = Release|Win32 + {B07A4DCF-0F5D-4F7A-9C90-C972527A69CD}.Debug|x64.ActiveCfg = Debug|x64 + {B07A4DCF-0F5D-4F7A-9C90-C972527A69CD}.Debug|x64.Build.0 = Debug|x64 + {B07A4DCF-0F5D-4F7A-9C90-C972527A69CD}.Release|x64.ActiveCfg = Release|x64 + {B07A4DCF-0F5D-4F7A-9C90-C972527A69CD}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/input/hiddigi/SynapticsTouch/SynapticsTouch.vcxproj b/input/hiddigi/SynapticsTouch/SynapticsTouch.vcxproj new file mode 100644 index 000000000..924a97e20 --- /dev/null +++ b/input/hiddigi/SynapticsTouch/SynapticsTouch.vcxproj @@ -0,0 +1,200 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {B07A4DCF-0F5D-4F7A-9C90-C972527A69CD} + $(MSBuildProjectName) + 1 + Debug + Win32 + {6FA87448-3C43-47E0-8863-483C7DA26875} + + + + 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 + Trace(LEVEL,FLAGS,MSG,...) + trace.h + + + $(InfArch) + true + .\$(IntDir)\SynapticsTouch.inf + + + + SynapticsTouch + + + SynapticsTouch + + + SynapticsTouch + + + SynapticsTouch + + + + true + Level4 + %(PreprocessorDefinitions);DRIVER;_WIN32_WINNT=0x602;_WINNT_;_SAMPLE_DESCRIPTOR_ + %(AdditionalIncludeDirectories);.;$(DDK_INC_PATH);$(DDK_INC_PATH)\wdm\ + + + + + %(PreprocessorDefinitions);DRIVER;_WIN32_WINNT=0x602;_WINNT_;_SAMPLE_DESCRIPTOR_ + %(AdditionalIncludeDirectories);.;$(DDK_INC_PATH);$(DDK_INC_PATH)\wdm\ + + + %(PreprocessorDefinitions);DRIVER;_WIN32_WINNT=0x602;_WINNT_;_SAMPLE_DESCRIPTOR_ + %(AdditionalIncludeDirectories);.;$(DDK_INC_PATH);$(DDK_INC_PATH)\wdm\ + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\HidClass.lib + + + + + true + Level4 + %(PreprocessorDefinitions);DRIVER;_WIN32_WINNT=0x602;_WINNT_;_SAMPLE_DESCRIPTOR_ + %(AdditionalIncludeDirectories);.;$(DDK_INC_PATH);$(DDK_INC_PATH)\wdm\ + + + + + %(PreprocessorDefinitions);DRIVER;_WIN32_WINNT=0x602;_WINNT_;_SAMPLE_DESCRIPTOR_ + %(AdditionalIncludeDirectories);.;$(DDK_INC_PATH);$(DDK_INC_PATH)\wdm\ + + + %(PreprocessorDefinitions);DRIVER;_WIN32_WINNT=0x602;_WINNT_;_SAMPLE_DESCRIPTOR_ + %(AdditionalIncludeDirectories);.;$(DDK_INC_PATH);$(DDK_INC_PATH)\wdm\ + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\HidClass.lib + + + + + true + Level4 + %(PreprocessorDefinitions);DRIVER;_WIN32_WINNT=0x602;_WINNT_;_SAMPLE_DESCRIPTOR_ + %(AdditionalIncludeDirectories);.;$(DDK_INC_PATH);$(DDK_INC_PATH)\wdm\ + + + + + %(PreprocessorDefinitions);DRIVER;_WIN32_WINNT=0x602;_WINNT_;_SAMPLE_DESCRIPTOR_ + %(AdditionalIncludeDirectories);.;$(DDK_INC_PATH);$(DDK_INC_PATH)\wdm\ + + + %(PreprocessorDefinitions);DRIVER;_WIN32_WINNT=0x602;_WINNT_;_SAMPLE_DESCRIPTOR_ + %(AdditionalIncludeDirectories);.;$(DDK_INC_PATH);$(DDK_INC_PATH)\wdm\ + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\HidClass.lib + + + + + true + Level4 + %(PreprocessorDefinitions);DRIVER;_WIN32_WINNT=0x602;_WINNT_;_SAMPLE_DESCRIPTOR_ + %(AdditionalIncludeDirectories);.;$(DDK_INC_PATH);$(DDK_INC_PATH)\wdm\ + + + + + %(PreprocessorDefinitions);DRIVER;_WIN32_WINNT=0x602;_WINNT_;_SAMPLE_DESCRIPTOR_ + %(AdditionalIncludeDirectories);.;$(DDK_INC_PATH);$(DDK_INC_PATH)\wdm\ + + + %(PreprocessorDefinitions);DRIVER;_WIN32_WINNT=0x602;_WINNT_;_SAMPLE_DESCRIPTOR_ + %(AdditionalIncludeDirectories);.;$(DDK_INC_PATH);$(DDK_INC_PATH)\wdm\ + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\HidClass.lib + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/input/hiddigi/SynapticsTouch/SynapticsTouch.vcxproj.Filters b/input/hiddigi/SynapticsTouch/SynapticsTouch.vcxproj.Filters new file mode 100644 index 000000000..fa3943ae9 --- /dev/null +++ b/input/hiddigi/SynapticsTouch/SynapticsTouch.vcxproj.Filters @@ -0,0 +1,61 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {FFD83EEB-ECDA-42ED-9B34-C8F74E64369E} + + + h;hpp;hxx;hm;inl;inc;xsd + {1DCE09F2-2C5B-42B6-9AB0-8995F5C04B01} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {99D0F0CC-6C33-4FEB-969F-839D0D2D8B45} + + + inf;inv;inx;mof;mc; + {6CA5C1EB-79DF-4295-B67B-20594E06BE0B} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Driver Files + + + \ No newline at end of file diff --git a/input/hiddigi/SynapticsTouch/controller.h b/input/hiddigi/SynapticsTouch/controller.h new file mode 100644 index 000000000..c3fdde1f5 --- /dev/null +++ b/input/hiddigi/SynapticsTouch/controller.h @@ -0,0 +1,203 @@ +/*++ + Copyright (c) Microsoft Corporation. All Rights Reserved. + Sample code. Dealpoint ID #843729. + + Module Name: + + controller.h + + Abstract: + + This module contains touch driver public definitions. + + Environment: + + Kernel mode + + Revision History: + +--*/ + +#pragma once + +#include +#include +#include +#define RESHUB_USE_HELPER_ROUTINES +#include +#include +#include "trace.h" +#include "spb.h" + +#define TOUCH_POOL_TAG (ULONG)'cuoT' + +// +// Device descriptions +// + +#define OEM_MAX_TOUCHES 10 + +// +// Constants +// +#define MODE_MOUSE 0x00 +#define MODE_SINGLE_TOUCH 0x01 +#define MODE_MULTI_TOUCH 0x02 + +#define MAX_MOUSE_COORD 0x7FFF +#define MAX_TOUCH_COORD 0x0FFF + +#define MOUSE_LEFT_BUTTON_DOWN 0x0001 +#define MOUSE_LEFT_BUTTON_UP 0x0002 +#define MOUSE_MOVE_TOUCH_ABSOLUTE 0x8000 + +#define FINGER_STATUS 0x01 // finger down +#define RANGE_STATUS 0x02 // in range +#define RANGE_FINGER_STATUS 0x03 // finger down (range + finger) + +#define KEY_DOWN_START (1 << 0) +#define KEY_DOWN_SEARCH (1 << 1) +#define KEY_DOWN_BACK (1 << 2) + +#define REPORTID_MTOUCH 1 +#define REPORTID_MOUSE 3 +#define REPORTID_FEATURE 7 +#define REPORTID_MAX_COUNT 8 +#define REPORTID_CAPKEY 9 + +// +// Type defintions +// + +#pragma warning(push) +#pragma warning(disable:4201) // (nameless struct/union) +#include + +typedef struct _HID_TOUCH_REPORT +{ + union + { + struct + { + UCHAR bStatus; + UCHAR ContactId; + USHORT wXData; + USHORT wYData; + UCHAR bStatus2; + UCHAR ContactId2; + USHORT wXData2; + USHORT wYData2; + UCHAR ActualCount; + } InputReport; + UCHAR RawInput[13]; + }; +} HID_TOUCH_REPORT, *PHID_TOUCH_REPORT; + +typedef struct _HID_MOUSE_REPORT { + union + { + struct + { + UCHAR bButtons; + USHORT wXData; + USHORT wYData; + USHORT wReserved; + }InputReport; + UCHAR RawInput[7]; + }; +} HID_MOUSE_REPORT, *PHID_MOUSE_REPORT; + +typedef struct _HID_KEY_REPORT { + union + { + struct + { + UCHAR bKeys; + UCHAR bReserved; + USHORT wReserved; + }InputReport; + UCHAR RawInput[4]; + }; +} HID_KEY_REPORT, *PHID_KEY_REPORT; + + +typedef struct _HID_FEATURE_REPORT +{ + UCHAR ReportID; + UCHAR InputMode; + UCHAR DeviceIndex; +} HID_FEATURE_REPORT, *PHID_FEATURE_REPORT; + +typedef struct _HID_MAX_COUNT_REPORT +{ + UCHAR ReportID; + UCHAR MaxCount; +}HID_MAX_COUNT_REPORT, *PHID_MAX_COUNT_REPORT; + +typedef struct _HID_INPUT_REPORT +{ + UCHAR ReportID; + union + { + HID_TOUCH_REPORT TouchReport; + HID_MOUSE_REPORT MouseReport; + HID_KEY_REPORT KeyReport; + }; +#ifdef _TIMESTAMP_ + LARGE_INTEGER TimeStamp; +#endif +} HID_INPUT_REPORT, *PHID_INPUT_REPORT; + +#include +#pragma warning(pop) + +NTSTATUS +TchAllocateContext( + OUT VOID **ControllerContext, + IN WDFDEVICE FxDevice + ); + +NTSTATUS +TchFreeContext( + IN VOID *ControllerContext + ); + +NTSTATUS +TchStartDevice( + IN VOID *ControllerContext, + IN SPB_CONTEXT *SpbContext + ); + +NTSTATUS +TchStopDevice( + IN VOID *ControllerContext, + IN SPB_CONTEXT *SpbContext + ); + +NTSTATUS +TchStandbyDevice( + IN VOID *ControllerContext, + IN SPB_CONTEXT *SpbContext + ); + +NTSTATUS +TchWakeDevice( + IN VOID *ControllerContext, + IN SPB_CONTEXT *SpbContext + ); + +NTSTATUS +TchRegistryGetControllerSettings( + IN VOID *ControllerContext, + IN WDFDEVICE FxDevice + ); + +NTSTATUS +TchServiceInterrupts( + IN VOID *ControllerContext, + IN SPB_CONTEXT *SpbContext, + IN PHID_INPUT_REPORT HidReport, + IN UCHAR InputMode, + OUT BOOLEAN *ServicingComplete + ); + diff --git a/input/hiddigi/SynapticsTouch/device.c b/input/hiddigi/SynapticsTouch/device.c new file mode 100644 index 000000000..dad83df94 --- /dev/null +++ b/input/hiddigi/SynapticsTouch/device.c @@ -0,0 +1,480 @@ +/*++ + Copyright (c) Microsoft Corporation. All Rights Reserved. + Sample code. Dealpoint ID #843729. + + Module Name: + + device.c + + Abstract: + + Code for handling WDF device-specific requests + + Environment: + + Kernel mode + + Revision History: + +--*/ + +#include "internal.h" +#include "controller.h" +#include "device.h" +#include "spb.h" +#include "idle.h" +#include "device.tmh" + +#ifdef ALLOC_PRAGMA + #pragma alloc_text(PAGE, OnD0Exit) +#endif + +BOOLEAN +OnInterruptIsr( + IN WDFINTERRUPT Interrupt, + IN ULONG MessageID + ) +/*++ + + Routine Description: + + This routine responds to interrupts generated by the + controller. If one is recognized, it queues a DPC for + processing. + + This is a PASSIVE_LEVEL ISR. ACPI should specify + level-triggered interrupts when using Synaptics 3202. + + Arguments: + + Interrupt - a handle to a framework interrupt object + MessageID - message number identifying the device's + hardware interrupt message (if using MSI) + + Return Value: + + TRUE if interrupt recognized. + +--*/ +{ + PDEVICE_EXTENSION devContext; + NTSTATUS status; + WDFREQUEST request; + BOOLEAN servicingComplete; + HID_INPUT_REPORT hidReportFromDriver; + PHID_INPUT_REPORT hidReportRequestBuffer; + size_t hidReportRequestBufferLength; + + UNREFERENCED_PARAMETER(MessageID); + + status = STATUS_SUCCESS; + servicingComplete = FALSE; + devContext = GetDeviceContext(WdfInterruptGetDevice(Interrupt)); + request = NULL; + + + // + // If we're in diagnostic mode, let the diagnostic application handle + // interrupt servicing + // + if (devContext->DiagnosticMode != FALSE) + { + goto exit; + } + + + // + // Service the device interrupt + // + while (servicingComplete == FALSE) + { + // + // Service touch interrupts. Success indicates we have a report + // to complete to Hid. ServicingComplete indicates another report + // is required to continue servicing this interrupt. + // + if (!NT_SUCCESS(TchServiceInterrupts( + devContext->TouchContext, + &devContext->I2CContext, + &hidReportFromDriver, + devContext->InputMode, + &servicingComplete))) + { + // + // hidReportFromDriver was not filled + // + continue; + } + + // + // Complete a HIDClass request if one is available + // + status = WdfIoQueueRetrieveNextRequest( + devContext->PingPongQueue, + &request); + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_REPORTING, + "No request pending from HIDClass, ignoring report - %!STATUS!", + status); + + continue; + } + + // + // Validate an output buffer was provided + // + status = WdfRequestRetrieveOutputBuffer( + request, + sizeof(HID_INPUT_REPORT), + &hidReportRequestBuffer, + &hidReportRequestBufferLength); + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_VERBOSE, + TRACE_FLAG_SAMPLES, + "Error retrieving HID read request output buffer - %!STATUS!", + status); + } + else + { + // + // Validate the size of the output buffer + // + if (hidReportRequestBufferLength < sizeof(HID_INPUT_REPORT)) + { + status = STATUS_BUFFER_TOO_SMALL; + + Trace( + TRACE_LEVEL_VERBOSE, + TRACE_FLAG_SAMPLES, + "Error HID read request buffer is too small (%I64x bytes) - %!STATUS!", + hidReportRequestBufferLength, + status); + } + else + { + RtlCopyMemory( + hidReportRequestBuffer, + &hidReportFromDriver, + sizeof(HID_INPUT_REPORT)); + + WdfRequestSetInformation(request, sizeof(HID_INPUT_REPORT)); + } + } + + WdfRequestComplete(request, status); + } + +exit: + return TRUE; +} + +NTSTATUS +OnD0Entry( + IN WDFDEVICE Device, + IN WDF_POWER_DEVICE_STATE PreviousState + ) +/*++ + +Routine Description: + + This routine will power on the hardware + +Arguments: + + Device - WDF device to power on + PreviousState - Prior power state + +Return Value: + + NTSTATUS indicating success or failure + +*/ +{ + NTSTATUS status; + PDEVICE_EXTENSION devContext; + + devContext = GetDeviceContext(Device); + + UNREFERENCED_PARAMETER(PreviousState); + + status = TchWakeDevice(devContext->TouchContext, &devContext->I2CContext); + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_POWER, + "Error setting device to D0 - %!STATUS!", + status); + } + + // + // N.B. This RMI chip's IRQ is level-triggered, but cannot be enabled in + // ACPI until passive-level interrupt handling is added to the driver. + // Service chip in case we missed an edge during D3 or boot-up. + // + devContext->ServiceInterruptsAfterD0Entry = TRUE; + + // + // Complete any pending Idle IRPs + // + TchCompleteIdleIrp(devContext); + + return status; +} + +NTSTATUS +OnD0Exit( + IN WDFDEVICE Device, + IN WDF_POWER_DEVICE_STATE TargetState + ) +/*++ + +Routine Description: + + This routine will power down the hardware + +Arguments: + + Device - WDF device to power off + + PreviousState - Prior power state + +Return Value: + + NTSTATUS indicating success or failure + +*/ +{ + NTSTATUS status; + PDEVICE_EXTENSION devContext; + + PAGED_CODE(); + + devContext = GetDeviceContext(Device); + + UNREFERENCED_PARAMETER(TargetState); + + status = TchStandbyDevice(devContext->TouchContext, &devContext->I2CContext); + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_POWER, + "Error exiting D0 - %!STATUS!", + status); + } + + return status; +} + +NTSTATUS +OnPrepareHardware( + IN WDFDEVICE FxDevice, + IN WDFCMRESLIST FxResourcesRaw, + IN WDFCMRESLIST FxResourcesTranslated + ) +/*++ + + Routine Description: + + This routine is called by the PnP manager and supplies thie device instance + with it's SPB resources (CmResourceTypeConnection) needed to find the I2C + driver. + + Arguments: + + FxDevice - a handle to the framework device object + FxResourcesRaw - list of translated hardware resources that + the PnP manager has assigned to the device + FxResourcesTranslated - list of raw hardware resources that + the PnP manager has assigned to the device + + Return Value: + + NTSTATUS indicating sucess or failure + +--*/ +{ + NTSTATUS status; + PCM_PARTIAL_RESOURCE_DESCRIPTOR res; + PDEVICE_EXTENSION devContext; + ULONG resourceCount; + ULONG i; + + UNREFERENCED_PARAMETER(FxResourcesRaw); + + status = STATUS_INSUFFICIENT_RESOURCES; + devContext = GetDeviceContext(FxDevice); + + // + // Get the resouce hub connection ID for our I2C driver + // + resourceCount = WdfCmResourceListGetCount(FxResourcesTranslated); + + for (i=0; i < resourceCount; i++) + { + res = WdfCmResourceListGetDescriptor(FxResourcesTranslated, i); + + if (res->Type == CmResourceTypeConnection && + res->u.Connection.Class == CM_RESOURCE_CONNECTION_CLASS_SERIAL && + res->u.Connection.Type == CM_RESOURCE_CONNECTION_TYPE_SERIAL_I2C) + { + devContext->I2CContext.I2cResHubId.LowPart = + res->u.Connection.IdLowPart; + devContext->I2CContext.I2cResHubId.HighPart = + res->u.Connection.IdHighPart; + + status = STATUS_SUCCESS; + } + } + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "Error finding CmResourceTypeConnection resource - %!STATUS!", + status); + + goto exit; + } + + // + // Initialize Spb so the driver can issue reads/writes + // + status = SpbTargetInitialize(FxDevice, &devContext->I2CContext); + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "Error in Spb initialization - %!STATUS!", + status); + + goto exit; + } + + // + // Prepare the hardware for touch scanning + // + status = TchAllocateContext(&devContext->TouchContext, FxDevice); + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "Error allocating touch context - %!STATUS!", + status); + + goto exit; + } + + // + // Fetch controller settings from registry + // + status = TchRegistryGetControllerSettings( + devContext->TouchContext, + devContext->FxDevice); + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "Error retrieving controller settings from registry - %!STATUS!", + status); + + goto exit; + } + + // + // Start the controller + // + status = TchStartDevice(devContext->TouchContext, &devContext->I2CContext); + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "Error starting touch device - %!STATUS!", + status); + + goto exit; + } + +exit: + + return status; +} + +NTSTATUS +OnReleaseHardware( + IN WDFDEVICE FxDevice, + IN WDFCMRESLIST FxResourcesTranslated + ) +/*++ + + Routine Description: + + This routine cleans up any resources provided. + + Arguments: + + FxDevice - a handle to the framework device object + FxResourcesRaw - list of translated hardware resources that + the PnP manager has assigned to the device + FxResourcesTranslated - list of raw hardware resources that + the PnP manager has assigned to the device + + Return Value: + + NTSTATUS indicating sucesss or failure + +--*/ +{ + NTSTATUS status; + PDEVICE_EXTENSION devContext; + + UNREFERENCED_PARAMETER(FxResourcesTranslated); + + devContext = GetDeviceContext(FxDevice); + + status = TchStopDevice(devContext->TouchContext, &devContext->I2CContext); + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "Error stopping device - %!STATUS!", + status); + } + + status = TchFreeContext(devContext->TouchContext); + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "Error freeing touch context - %!STATUS!", + status); + } + + SpbTargetDeinitialize(FxDevice, &GetDeviceContext(FxDevice)->I2CContext); + + return status; +} + diff --git a/input/hiddigi/SynapticsTouch/device.h b/input/hiddigi/SynapticsTouch/device.h new file mode 100644 index 000000000..c21a409a9 --- /dev/null +++ b/input/hiddigi/SynapticsTouch/device.h @@ -0,0 +1,32 @@ +/*++ + Copyright (c) Microsoft Corporation. All Rights Reserved. + Sample code. Dealpoint ID #843729. + + Module Name: + + device.h + + Abstract: + + Declarations of WDF device specific entry points + + Environment: + + Kernel mode + + Revision History: + +--*/ + +#pragma once + + +EVT_WDF_DEVICE_D0_ENTRY OnD0Entry; + +EVT_WDF_DEVICE_D0_EXIT OnD0Exit; + +EVT_WDF_INTERRUPT_ISR OnInterruptIsr; + +EVT_WDF_DEVICE_PREPARE_HARDWARE OnPrepareHardware; + +EVT_WDF_DEVICE_RELEASE_HARDWARE OnReleaseHardware; \ No newline at end of file diff --git a/input/hiddigi/SynapticsTouch/driver.c b/input/hiddigi/SynapticsTouch/driver.c new file mode 100644 index 000000000..6812fcb3b --- /dev/null +++ b/input/hiddigi/SynapticsTouch/driver.c @@ -0,0 +1,319 @@ +/*++ + Copyright (c) Microsoft Corporation. All Rights Reserved. + Sample code. Dealpoint ID #843729. + + Module Name: + + driver.c + + Abstract: + + Contains driver-specific WDF functions + + Environment: + + Kernel mode + + Revision History: + +--*/ + +#include "internal.h" +#include "controller.h" +#include "driver.h" +#include "device.h" +#include "hid.h" +#include "queue.h" +#include "driver.tmh" + +#ifdef ALLOC_PRAGMA + #pragma alloc_text(PAGE, OnDeviceAdd) + #pragma alloc_text(PAGE, OnContextCleanup) +#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, error code otherwise. + +--*/ +{ + WDF_OBJECT_ATTRIBUTES attributes; + WDF_DRIVER_CONFIG config; + NTSTATUS status; + + // + // Initialize tracing via WPP + // + WPP_INIT_TRACING(DriverObject, RegistryPath); + + // + // Create a framework driver object + // + WDF_DRIVER_CONFIG_INIT(&config, OnDeviceAdd); + config.DriverPoolTag = TOUCH_POOL_TAG; + + WDF_OBJECT_ATTRIBUTES_INIT(&attributes); + attributes.EvtCleanupCallback = OnContextCleanup; + + status = WdfDriverCreate( + DriverObject, + RegistryPath, + &attributes, + &config, + WDF_NO_HANDLE + ); + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "Error creating WDF driver object - %!STATUS!", + status); + + WPP_CLEANUP(DriverObject); + + goto exit; + } + +exit: + + return status; +} + +NTSTATUS +OnDeviceAdd( + IN WDFDRIVER Driver, + IN PWDFDEVICE_INIT DeviceInit + ) +/*++ + +Routine Description: + + OnDeviceAdd is called by the framework in response to AddDevice + call from the PnP manager when a device is found. We create and + initialize a WDF device object to represent the new instance of + an touch device. Per-device objects are also instantiated. + +Arguments: + + Driver - Handle to a framework driver object created in DriverEntry + DeviceInit - Pointer to a framework-allocated WDFDEVICE_INIT structure. + +Return Value: + + NTSTATUS indicating success or failure + +--*/ +{ + WDF_OBJECT_ATTRIBUTES attributes; + PDEVICE_EXTENSION devContext; + WDFDEVICE fxDevice; + WDF_INTERRUPT_CONFIG interruptConfig; + WDF_PNPPOWER_EVENT_CALLBACKS pnpPowerCallbacks; + WDF_IO_QUEUE_CONFIG queueConfig; + NTSTATUS status; + + UNREFERENCED_PARAMETER(Driver); + PAGED_CODE(); + + // + // Relinquish power policy ownership because HIDCLASS acts a power + // policy owner for ther HID stack. + // + WdfDeviceInitSetPowerPolicyOwnership(DeviceInit, FALSE); + + // + // This driver handles D0 Entry and Exit to power on and off the + // controller. It also handles prepare/release hardware so that it + // can acquire and free SPB resources needed to communicate with the + // touch controller. + // + WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpPowerCallbacks); + + pnpPowerCallbacks.EvtDeviceD0Entry = OnD0Entry; + pnpPowerCallbacks.EvtDeviceD0Exit = OnD0Exit; + pnpPowerCallbacks.EvtDevicePrepareHardware = OnPrepareHardware; + pnpPowerCallbacks.EvtDeviceReleaseHardware = OnReleaseHardware; + + WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpPowerCallbacks); + + // + // 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. + // + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, DEVICE_EXTENSION); + + status = WdfDeviceCreate(&DeviceInit, &attributes, &fxDevice); + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "WdfDeviceCreate failed - %!STATUS!", + status); + + goto exit; + } + + devContext = GetDeviceContext(fxDevice); + devContext->FxDevice = fxDevice; + devContext->InputMode = MODE_MULTI_TOUCH; + + // + // Create a parallel dispatch queue to handle requests from HID Class + // + WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE( + &queueConfig, + WdfIoQueueDispatchParallel); + + queueConfig.EvtIoInternalDeviceControl = OnInternalDeviceControl; + queueConfig.PowerManaged = WdfFalse; + + status = WdfIoQueueCreate( + fxDevice, + &queueConfig, + WDF_NO_OBJECT_ATTRIBUTES, + &devContext->DefaultQueue); + + if (!NT_SUCCESS (status)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "Error creating WDF default queue - %!STATUS!", + status); + + goto exit; + } + + // + // Register a manual I/O queue for Read Requests. This queue will be used + // for storing HID read requests until touch data is available to + // complete them. + // + WDF_IO_QUEUE_CONFIG_INIT(&queueConfig, WdfIoQueueDispatchManual); + + queueConfig.PowerManaged = WdfFalse; + + status = WdfIoQueueCreate( + fxDevice, + &queueConfig, + WDF_NO_OBJECT_ATTRIBUTES, + &devContext->PingPongQueue); + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "Error creating WDF read request queue - %!STATUS!", + status); + + goto exit; + } + + // + // Register one last manual I/O queue for parking HIDClass's idle power + // requests. This queue stores idle requests until they're cancelled, + // and could be used to complete a wait/wake request. + // + + WDF_IO_QUEUE_CONFIG_INIT(&queueConfig, WdfIoQueueDispatchManual); + + queueConfig.PowerManaged = WdfFalse; + + status = WdfIoQueueCreate( + fxDevice, + &queueConfig, + WDF_NO_OBJECT_ATTRIBUTES, + &devContext->IdleQueue + ); + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "Error creating WDF idle request queue - %!STATUS!", + status); + + goto exit; + } + + // + // Create an interrupt object for hardware notifications + // + WDF_INTERRUPT_CONFIG_INIT( + &interruptConfig, + OnInterruptIsr, + NULL); + interruptConfig.PassiveHandling = TRUE; + + status = WdfInterruptCreate( + fxDevice, + &interruptConfig, + WDF_NO_OBJECT_ATTRIBUTES, + &devContext->InterruptObject); + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "Error creating WDF interrupt object - %!STATUS!", + status); + + goto exit; + } + +exit: + + return status; +} + +VOID +OnContextCleanup( + IN WDFOBJECT Driver + ) +/*++ +Routine Description: + + Free resources allocated in DriverEntry that are not automatically + cleaned up framework. + +Arguments: + + Driver - handle to a WDF Driver object. + +Return Value: + + VOID. + +--*/ +{ + PAGED_CODE(); + + WPP_CLEANUP(WdfDriverWdmGetDriverObject(Driver)); +} + \ No newline at end of file diff --git a/input/hiddigi/SynapticsTouch/driver.h b/input/hiddigi/SynapticsTouch/driver.h new file mode 100644 index 000000000..66036d935 --- /dev/null +++ b/input/hiddigi/SynapticsTouch/driver.h @@ -0,0 +1,29 @@ +/*++ + Copyright (c) Microsoft Corporation. All Rights Reserved. + Sample code. Dealpoint ID #843729. + + Module Name: + + driver.h + + Abstract: + + Contains WDF driver-specific function declarations + + Environment: + + Kernel mode + + Revision History: + +--*/ + +#pragma once + + +DRIVER_INITIALIZE DriverEntry; + +EVT_WDF_DEVICE_CONTEXT_CLEANUP OnContextCleanup; + +EVT_WDF_DRIVER_DEVICE_ADD OnDeviceAdd; + diff --git a/input/hiddigi/SynapticsTouch/hid.c b/input/hiddigi/SynapticsTouch/hid.c new file mode 100644 index 000000000..233b6e238 --- /dev/null +++ b/input/hiddigi/SynapticsTouch/hid.c @@ -0,0 +1,769 @@ +/*++ + Copyright (c) Microsoft Corporation. All Rights Reserved. + Sample code. Dealpoint ID #843729. + + Module Name: + + hid.c + + Abstract: + + Code for handling HID related requests + + Environment: + + Kernel mode + + Revision History: + +--*/ + +#include "internal.h" +#include "controller.h" +#include "hid.h" +#include "hid.tmh" + + +// +// HID Report Descriptor for a touch device +// + +#ifndef _SAMPLE_DESCRIPTOR_ +#error This HID descriptor only works for a touch screen with resolution = 768 x 1280 and dimensions = 2.32" x 3.82" +#endif + +const UCHAR gReportDescriptor[] = { + 0x05, 0x0d, // USAGE_PAGE (Digitizers) + 0x09, 0x04, // USAGE (Touch Screen) + 0xa1, 0x01, // COLLECTION (Application) + 0x85, REPORTID_MTOUCH, // REPORT_ID (Touch) + 0x09, 0x22, // USAGE (Finger) + 0xa1, 0x02, // COLLECTION (Logical) + 0x09, 0x42, // USAGE (Tip Switch) + 0x15, 0x00, // LOGICAL_MINIMUM (0) + 0x25, 0x01, // LOGICAL_MAXIMUM (1) + 0x75, 0x01, // REPORT_SIZE (1) + 0x95, 0x01, // REPORT_COUNT (1) + 0x81, 0x02, // INPUT (Data,Var,Abs) + 0x09, 0x32, // USAGE (In Range) + 0x81, 0x02, // INPUT (Data,Var,Abs) + 0x95, 0x06, // REPORT_COUNT (6) + 0x81, 0x03, // INPUT (Cnst,Ary,Abs) + 0x75, 0x08, // REPORT_SIZE (8) + 0x09, 0x51, // USAGE (Contact Identifier) + 0x95, 0x01, // REPORT_COUNT (1) + 0x81, 0x02, // INPUT (Data,Var,Abs) + 0x05, 0x01, // USAGE_PAGE (Generic Desk.. + 0x26, 0x00, 0x03, // LOGICAL_MAXIMUM (768) NOTE: ADJUST LOGICAL MAXIMUM FOR X BASED ON TOUCH SCREEN RESOLUTION! + 0x75, 0x10, // REPORT_SIZE (16) + 0x55, 0x0e, // UNIT_EXPONENT (-2) + 0x65, 0x13, // UNIT (Inch,EngLinear) + 0x09, 0x30, // USAGE (X) + 0x35, 0x00, // PHYSICAL_MINIMUM (0) + 0x46, 0xe8, 0x00, // PHYSICAL_MAXIMUM (232) NOTE: ADJUST PHYSICAL MAXIMUM FOR X BASED ON TOUCH SCREEN DIMENSION (100ths of an inch)! + 0x81, 0x02, // INPUT (Data,Var,Abs) + 0x46, 0x7e, 0x01, // PHYSICAL_MAXIMUM (382) NOTE: ADJUST PHYSICAL MAXIMUM FOR Y BASED ON TOUCH SCREEN DIMENSION (100ths of an inch)! + 0x26, 0x00, 0x05, // LOGICAL_MAXIMUM (1280) NOTE: ADJUST LOGICAL MAXIMUM FOR Y BASED ON TOUCH SCREEN RESOLUTION! + 0x09, 0x31, // USAGE (Y) + 0x81, 0x02, // INPUT (Data,Var,Abs) + 0xc0, // END_COLLECTION + 0xa1, 0x02, // COLLECTION (Logical) + 0x05, 0x0d, // USAGE_PAGE (Digitizers) + 0x09, 0x42, // USAGE (Tip Switch) + 0x15, 0x00, // LOGICAL_MINIMUM (0) + 0x25, 0x01, // LOGICAL_MAXIMUM (1) + 0x75, 0x01, // REPORT_SIZE (1) + 0x95, 0x01, // REPORT_COUNT (1) + 0x81, 0x02, // INPUT (Data,Var,Abs) + 0x09, 0x32, // USAGE (In Range) + 0x81, 0x02, // INPUT (Data,Var,Abs) + 0x95, 0x06, // REPORT_COUNT (6) + 0x81, 0x03, // INPUT (Cnst,Ary,Abs) + 0x75, 0x08, // REPORT_SIZE (8) + 0x09, 0x51, // USAGE (Contact Identifier) + 0x95, 0x01, // REPORT_COUNT (1) + 0x81, 0x02, // INPUT (Data,Var,Abs) + 0x05, 0x01, // USAGE_PAGE (Generic Desk.. + 0x26, 0x00, 0x03, // LOGICAL_MAXIMUM (768) NOTE: ADJUST LOGICAL MAXIMUM FOR X BASED ON TOUCH SCREEN RESOLUTION! + 0x75, 0x10, // REPORT_SIZE (16) + 0x55, 0x0e, // UNIT_EXPONENT (-2) + 0x65, 0x13, // UNIT (Inch,EngLinear) + 0x09, 0x30, // USAGE (X) + 0x35, 0x00, // PHYSICAL_MINIMUM (0) + 0x46, 0xe8, 0x00, // PHYSICAL_MAXIMUM (232) NOTE: ADJUST PHYSICAL MAXIMUM FOR X BASED ON TOUCH SCREEN DIMENSION (100ths of an inch)! + 0x81, 0x02, // INPUT (Data,Var,Abs) + 0x46, 0x7e, 0x01, // PHYSICAL_MAXIMUM (382) NOTE: ADJUST PHYSICAL MAXIMUM FOR Y BASED ON TOUCH SCREEN DIMENSION (100ths of an inch)! + 0x26, 0x00, 0x05, // LOGICAL_MAXIMUM (1280) NOTE: ADJUST LOGICAL MAXIMUM FOR Y BASED ON TOUCH SCREEN RESOLUTION! + 0x09, 0x31, // USAGE (Y) + 0x81, 0x02, // INPUT (Data,Var,Abs) + 0xc0, // END_COLLECTION + 0x05, 0x0d, // USAGE_PAGE (Digitizers) + 0x09, 0x54, // USAGE (Actual count) + 0x95, 0x01, // REPORT_COUNT (1) + 0x75, 0x08, // REPORT_SIZE (8) + 0x81, 0x02, // INPUT (Data,Var,Abs) + 0x85, REPORTID_MAX_COUNT, // REPORT_ID (Feature) + 0x09, 0x55, // USAGE(Maximum Count) + 0x25, 0x02, // LOGICAL_MAXIMUM (2) + 0xb1, 0x02, // FEATURE (Data,Var,Abs) + 0xc0, // END_COLLECTION + 0x09, 0x0E, // USAGE (Configuration) + 0xa1, 0x01, // COLLECTION (Application) + 0x85, REPORTID_FEATURE, // REPORT_ID (Feature) + 0x09, 0x22, // USAGE (Finger) + 0xa1, 0x00, // COLLECTION (physical) + 0x09, 0x52, // USAGE (Input Mode) + 0x09, 0x53, // USAGE (Device Index + 0x15, 0x00, // LOGICAL_MINIMUM (0) + 0x25, 0x0a, // LOGICAL_MAXIMUM (10) + 0x75, 0x08, // REPORT_SIZE (8) + 0x95, 0x02, // REPORT_COUNT (2) + 0xb1, 0x02, // FEATURE (Data,Var,Abs) + 0xc0, // END_COLLECTION + 0xc0, // END_COLLECTION + 0x05, 0x01, // USAGE_PAGE (Generic Desktop) +#ifdef HID_MOUSE_PATH_SUPPORT + 0x09, 0x02, // USAGE (Mouse) + 0xa1, 0x01, // COLLECTION (Application) + 0x85, REPORTID_MOUSE, // REPORT_ID (Mouse) + 0x09, 0x01, // USAGE (Pointer) + 0xa1, 0x00, // COLLECTION (Physical) + 0x05, 0x09, // USAGE_PAGE (Button) + 0x19, 0x01, // USAGE_MINIMUM (Button 1) + 0x29, 0x02, // USAGE_MAXIMUM (Button 2) + 0x15, 0x00, // LOGICAL_MINIMUM (0) + 0x25, 0x01, // LOGICAL_MAXIMUM (1) + 0x75, 0x01, // REPORT_SIZE (1) + 0x95, 0x02, // REPORT_COUNT (2) + 0x81, 0x02, // INPUT (Data,Var,Abs) + 0x95, 0x06, // REPORT_COUNT (6) + 0x81, 0x03, // INPUT (Cnst,Var,Abs) + 0x05, 0x01, // USAGE_PAGE (Generic Desktop) + 0x09, 0x30, // USAGE (X) + 0x09, 0x31, // USAGE (Y) + 0x75, 0x10, // REPORT_SIZE (16) + 0x95, 0x02, // REPORT_COUNT (2) + 0x15, 0x00, // LOGICAL_MINIMUM (0) + 0x26, 0xff, 0x7f, // LOGICAL_MAXIMUM (32767) + 0x81, 0x02, // INPUT (Data,Var,Abs) + 0xc0, // END_COLLECTION + 0xc0, // END_COLLECTION +#endif + 0x05, 0x01, // USAGE_PAGE (Generic Desktop) + 0x09, 0xEE, // USAGE (HID_USAGE_KEYBOARD_MOBILE) + 0xa1, 0x01, // COLLECTION (Application) + 0x85, REPORTID_CAPKEY, // REPORT_ID + 0x05, 0x07, // USAGE_PAGE (Key Codes) + 0x09, 0x3B, // USAGE(F2 Key) - Start/Home + 0x09, 0x3C, // USAGE(F3 Key) - Search + 0x09, 0x29, // USAGE(Esc Key) - Back + 0x15, 0x00, // LOGICAL_MINIMUM (0) + 0x25, 0x01, // LOGICAL_MAXIMUM (1) + 0x75, 0x01, // REPORT_SIZE (1) + 0x95, 0x03, // REPORT_COUNT (3) + 0x81, 0x02, // INPUT (Data,Var,Abs) + 0x95, 0x1d, // REPORT_COUNT (29) + 0x81, 0x03, // INPUT (Cnst,Var,Abs) + 0xc0, // END_COLLECTION +}; +const ULONG gdwcbReportDescriptor = sizeof(gReportDescriptor); + +// +// HID Descriptor for a touch device +// +const HID_DESCRIPTOR gHidDescriptor = +{ + sizeof(HID_DESCRIPTOR), //bLength + HID_HID_DESCRIPTOR_TYPE, //bDescriptorType + HID_REVISION, //bcdHID + 0, //bCountry - not localized + 1, //bNumDescriptors + { //DescriptorList[0] + HID_REPORT_DESCRIPTOR_TYPE, //bReportType + sizeof(gReportDescriptor) //wReportLength + } +}; + +NTSTATUS +TchReadReport( + IN WDFDEVICE Device, + IN WDFREQUEST Request, + OUT BOOLEAN *Pending + ) +/*++ + +Routine Description: + + Handles read requests from HIDCLASS, by forwarding the request. + +Arguments: + + Device - Handle to WDF Device Object + + Request - Handle to request object + + Pending - flag to monitor if the request was sent down the stack + +Return Value: + + On success, the function returns STATUS_SUCCESS + On failure it passes the relevant error code to the caller. + +--*/ +{ + PDEVICE_EXTENSION devContext; + NTSTATUS status; + + devContext = GetDeviceContext(Device); + + status = WdfRequestForwardToIoQueue( + Request, + devContext->PingPongQueue); + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_HID, + "Failed to forward HID request to I/O queue - %!STATUS!", + status); + + goto exit; + } + + if (NULL != Pending) + { + *Pending = TRUE; + } + + // + // Service any interrupt that may have asserted while the framework had + // interrupts disabled, or occurred before a read request was queued. + // + if (devContext->ServiceInterruptsAfterD0Entry == TRUE) + { + HID_INPUT_REPORT hidReport; + BOOLEAN servicingComplete = FALSE; + + while (servicingComplete == FALSE) + { + TchServiceInterrupts( + devContext->TouchContext, + &devContext->I2CContext, + &hidReport, + devContext->InputMode, + &servicingComplete); + } + + devContext->ServiceInterruptsAfterD0Entry = FALSE; + } + +exit: + + return status; +} + +NTSTATUS +TchGetString( + IN WDFDEVICE Device, + IN WDFREQUEST Request + ) +/*++ + +Routine Description: + + Returns string requested by the HIDCLASS driver + +Arguments: + + Device - Handle to WDF Device Object + + Request - Handle to request object + +Return Value: + + NTSTATUS indicating success or failure + +--*/ +{ + PIRP irp; + PIO_STACK_LOCATION irpSp; + ULONG_PTR lenId; + NTSTATUS status; + PWSTR strId; + + UNREFERENCED_PARAMETER(Device); + + status = STATUS_SUCCESS; + + irp = WdfRequestWdmGetIrp(Request); + irpSp = IoGetCurrentIrpStackLocation(irp); + switch ((ULONG_PTR)irpSp->Parameters.DeviceIoControl.Type3InputBuffer & + 0xffff) + { + case HID_STRING_ID_IMANUFACTURER: + strId = gpwstrManufacturerID; + break; + + case HID_STRING_ID_IPRODUCT: + strId = gpwstrProductID; + break; + + case HID_STRING_ID_ISERIALNUMBER: + strId = gpwstrSerialNumber; + break; + + default: + strId = NULL; + break; + } + + lenId = strId ? (wcslen(strId)*sizeof(WCHAR) + sizeof(UNICODE_NULL)) : 0; + if (strId == NULL) + { + status = STATUS_INVALID_PARAMETER; + } + else if (irpSp->Parameters.DeviceIoControl.OutputBufferLength < lenId) + { + status = STATUS_BUFFER_TOO_SMALL; + } + else + { + RtlCopyMemory(irp->UserBuffer, strId, lenId); + irp->IoStatus.Information = lenId; + } + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_HID, + "Error getting device string - %!STATUS!", + status); + } + + return status; +} + +NTSTATUS +TchGetHidDescriptor( + IN WDFDEVICE Device, + IN WDFREQUEST Request + ) +/*++ + +Routine Description: + + Finds the HID descriptor and copies it into the buffer provided by the + Request. + +Arguments: + + Device - Handle to WDF Device Object + + Request - Handle to request object + +Return Value: + + NTSTATUS indicating success or failure + +--*/ +{ + WDFMEMORY memory; + NTSTATUS status; + + UNREFERENCED_PARAMETER(Device); + + // + // This IOCTL is METHOD_NEITHER so WdfRequestRetrieveOutputMemory + // will correctly retrieve buffer from Irp->UserBuffer. + // Remember that HIDCLASS provides the buffer in the Irp->UserBuffer + // field irrespective of the ioctl buffer type. However, framework is very + // strict about type checking. You cannot get Irp->UserBuffer by using + // WdfRequestRetrieveOutputMemory if the ioctl is not a METHOD_NEITHER + // internal ioctl. + // + status = WdfRequestRetrieveOutputMemory(Request, &memory); + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_HID, + "Error getting HID descriptor request memory - %!STATUS!", + status); + goto exit; + } + + // + // Use hardcoded global HID Descriptor + // + status = WdfMemoryCopyFromBuffer( + memory, + 0, + (PUCHAR) &gHidDescriptor, + sizeof(gHidDescriptor)); + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_HID, + "Error copying HID descriptor to request memory - %!STATUS!", + status); + goto exit; + } + + // + // Report how many bytes were copied + // + WdfRequestSetInformation(Request, sizeof(gHidDescriptor)); + +exit: + + return status; +} + +NTSTATUS +TchGetReportDescriptor( + IN WDFDEVICE Device, + IN WDFREQUEST Request + ) +/*++ + +Routine Description: + + Finds the report descriptor and copies it into the buffer provided by the + Request. + +Arguments: + + Device - Handle to WDF Device Object + + Request - Handle to request object + +Return Value: + + NT status code. + success - STATUS_SUCCESS + failure: + STATUS_INVALID_PARAMETER - An invalid parameter was detected. + +--*/ +{ + WDFMEMORY memory; + NTSTATUS status; + + UNREFERENCED_PARAMETER(Device); + + // + // This IOCTL is METHOD_NEITHER so WdfRequestRetrieveOutputMemory + // will correctly retrieve buffer from Irp->UserBuffer. + // Remember that HIDCLASS provides the buffer in the Irp->UserBuffer + // field irrespective of the ioctl buffer type. However, framework is very + // strict about type checking. You cannot get Irp->UserBuffer by using + // WdfRequestRetrieveOutputMemory if the ioctl is not a METHOD_NEITHER + // internal ioctl. + // + status = WdfRequestRetrieveOutputMemory(Request, &memory); + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_HID, + "Error getting HID report descriptor request memory - %!STATUS!", + status); + goto exit; + } + + // + // Use hardcoded Report descriptor + // + status = WdfMemoryCopyFromBuffer( + memory, + 0, + (PUCHAR) gReportDescriptor, + gdwcbReportDescriptor); + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_HID, + "Error copying HID report descriptor to request memory - %!STATUS!", + status); + goto exit; + } + + // + // Report how many bytes were copied + // + WdfRequestSetInformation(Request, gdwcbReportDescriptor); + +exit: + + return status; +} + +NTSTATUS +TchGetDeviceAttributes( + IN WDFREQUEST Request + ) +/*++ + +Routine Description: + + Fill in the given struct _HID_DEVICE_ATTRIBUTES + +Arguments: + + Request - Pointer to Request object. + +Return Value: + + NTSTATUS indicating success or failure + +--*/ +{ + PHID_DEVICE_ATTRIBUTES deviceAttributes; + NTSTATUS status; + + // + // This IOCTL is METHOD_NEITHER so WdfRequestRetrieveOutputMemory + // will correctly retrieve buffer from Irp->UserBuffer. + // Remember that HIDCLASS provides the buffer in the Irp->UserBuffer + // field irrespective of the ioctl buffer type. However, framework is very + // strict about type checking. You cannot get Irp->UserBuffer by using + // WdfRequestRetrieveOutputMemory if the ioctl is not a METHOD_NEITHER + // internal ioctl. + // + status = WdfRequestRetrieveOutputBuffer( + Request, + sizeof (HID_DEVICE_ATTRIBUTES), + &deviceAttributes, + NULL); + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_HID, + "Error retrieving device attribute output buffer - %!STATUS!", + status); + goto exit; + } + + deviceAttributes->Size = sizeof (HID_DEVICE_ATTRIBUTES); + deviceAttributes->VendorID = gOEMVendorID; + deviceAttributes->ProductID = gOEMProductID; + deviceAttributes->VersionNumber = gOEMVersionID; + + // + // Report how many bytes were copied + // + WdfRequestSetInformation(Request, sizeof (HID_DEVICE_ATTRIBUTES)); + +exit: + + return status; +} + +NTSTATUS +TchSetFeatureReport( + IN WDFDEVICE Device, + IN WDFREQUEST Request + ) +/*++ + +Routine Description: + + Emulates setting a HID Feature report + +Arguments: + + Device - Framework device object + Request - Framework request object + +Return Value: + + NTSTATUS indicating success or failure + +--*/ +{ + PDEVICE_EXTENSION devContext; + PHID_XFER_PACKET featurePacket; + WDF_REQUEST_PARAMETERS params; + NTSTATUS status; + + devContext = GetDeviceContext(Device); + status = STATUS_SUCCESS; + + // + // Validate Request Parameters + // + + WDF_REQUEST_PARAMETERS_INIT(¶ms); + WdfRequestGetParameters(Request, ¶ms); + + if (params.Parameters.DeviceIoControl.InputBufferLength < + sizeof(HID_XFER_PACKET)) + { + status = STATUS_BUFFER_TOO_SMALL; + goto exit; + } + + featurePacket = + (PHID_XFER_PACKET) WdfRequestWdmGetIrp(Request)->UserBuffer; + + if (featurePacket == NULL) + { + status = STATUS_INVALID_DEVICE_REQUEST; + goto exit; + } + + // + // Process Request + // + + switch (*(PUCHAR)featurePacket->reportBuffer) + { + case REPORTID_FEATURE: + { + PHID_FEATURE_REPORT inputModeReport = + (PHID_FEATURE_REPORT) featurePacket->reportBuffer; + + if (featurePacket->reportBufferLen < sizeof(HID_FEATURE_REPORT)) + { + status = STATUS_BUFFER_TOO_SMALL; + goto exit; + } + + if ((inputModeReport->InputMode == MODE_MOUSE) || + (inputModeReport->InputMode == MODE_MULTI_TOUCH)) + { + devContext->InputMode = inputModeReport->InputMode; + } + else + { + status = STATUS_INVALID_PARAMETER; + goto exit; + } + + break; + } + + default: + { + status = STATUS_INVALID_PARAMETER; + goto exit; + } + } + +exit: + + return status; +} + +NTSTATUS +TchGetFeatureReport( + IN WDFDEVICE Device, + IN WDFREQUEST Request + ) +/*++ + +Routine Description: + + Emulates retrieval of a HID Feature report + +Arguments: + + Device - Framework device object + Request - Framework request object + +Return Value: + + NTSTATUS indicating success or failure + +--*/ +{ + PDEVICE_EXTENSION devContext; + PHID_XFER_PACKET featurePacket; + WDF_REQUEST_PARAMETERS params; + NTSTATUS status; + + devContext = GetDeviceContext(Device); + status = STATUS_SUCCESS; + + // + // Validate Request Parameters + // + + WDF_REQUEST_PARAMETERS_INIT(¶ms); + WdfRequestGetParameters(Request, ¶ms); + + if (params.Parameters.DeviceIoControl.OutputBufferLength < + sizeof(HID_XFER_PACKET)) + { + status = STATUS_BUFFER_TOO_SMALL; + goto exit; + } + + featurePacket = + (PHID_XFER_PACKET) WdfRequestWdmGetIrp(Request)->UserBuffer; + + if (featurePacket == NULL) + { + status = STATUS_INVALID_DEVICE_REQUEST; + goto exit; + } + + // + // Process Request + // + + switch (*(PUCHAR)featurePacket->reportBuffer) + { + case REPORTID_FEATURE: + { + PHID_FEATURE_REPORT inputModeReport = + (PHID_FEATURE_REPORT) featurePacket->reportBuffer; + + if (featurePacket->reportBufferLen < sizeof(HID_FEATURE_REPORT)) + { + status = STATUS_BUFFER_TOO_SMALL; + goto exit; + } + + inputModeReport->InputMode = devContext->InputMode; + + break; + } + + case REPORTID_MAX_COUNT: + { + PHID_MAX_COUNT_REPORT maxCountReport = + (PHID_MAX_COUNT_REPORT) featurePacket->reportBuffer; + + if (featurePacket->reportBufferLen < sizeof(HID_MAX_COUNT_REPORT)) + { + status = STATUS_BUFFER_TOO_SMALL; + goto exit; + } + + maxCountReport->MaxCount = OEM_MAX_TOUCHES; + + break; + } + + default: + { + status = STATUS_INVALID_PARAMETER; + goto exit; + } + } + +exit: + + return status; +} diff --git a/input/hiddigi/SynapticsTouch/hid.h b/input/hiddigi/SynapticsTouch/hid.h new file mode 100644 index 000000000..1f0e7912f --- /dev/null +++ b/input/hiddigi/SynapticsTouch/hid.h @@ -0,0 +1,85 @@ +/*++ + Copyright (c) Microsoft Corporation. All Rights Reserved. + Sample code. Dealpoint ID #843729. + + Module Name: + + hid.h + + Abstract: + + HID related function declarations, types, and defines + + Environment: + + Kernel mode + + Revision History: + +--*/ + +#pragma once + +// +// Global Data Declarations +// +extern const PWSTR gpwstrManufacturerID; +extern const PWSTR gpwstrProductID; +extern const PWSTR gpwstrSerialNumber; +extern const USHORT gOEMVendorID; +extern const USHORT gOEMProductID; +extern const USHORT gOEMVersionID; + +// +// Function prototypes +// + +NTSTATUS +TchGetDeviceAttributes( + IN WDFREQUEST Request + ); + +NTSTATUS +TchGetFeatureReport( + IN WDFDEVICE Device, + IN WDFREQUEST Request + ); + +NTSTATUS +TchGetHidDescriptor( + IN WDFDEVICE Device, + IN WDFREQUEST Request + ); + +NTSTATUS +TchGetReportDescriptor( + IN WDFDEVICE Device, + IN WDFREQUEST Request + ); + +NTSTATUS +TchGetString( + IN WDFDEVICE Device, + IN WDFREQUEST Request + ); + +NTSTATUS +TchProcessIdleRequest( + IN WDFDEVICE Device, + IN WDFREQUEST Request, + OUT BOOLEAN *Pending + ); + +NTSTATUS +TchSetFeatureReport( + IN WDFDEVICE Device, + IN WDFREQUEST Request + ); + +NTSTATUS +TchReadReport( + IN WDFDEVICE Device, + IN WDFREQUEST Request, + OUT BOOLEAN *Pending + ); + diff --git a/input/hiddigi/SynapticsTouch/idle.c b/input/hiddigi/SynapticsTouch/idle.c new file mode 100644 index 000000000..d15b0b98e --- /dev/null +++ b/input/hiddigi/SynapticsTouch/idle.c @@ -0,0 +1,328 @@ +/*++ + Copyright (c) Microsoft Corporation. All Rights Reserved. + Sample code. Dealpoint ID #843729. + + Module Name: + + idle.c + + Abstract: + + This file contains the declarations for Power Idle specific callbacks + and function definitions + + Environment: + + Kernel mode + + Revision History: + +--*/ + +#include "internal.h" +#include "controller.h" +#include "idle.h" +#include "idle.tmh" + +NTSTATUS +TchProcessIdleRequest( + IN WDFDEVICE Device, + IN WDFREQUEST Request, + OUT BOOLEAN *Pending + ) +/*++ + +Routine Description: + + Handles HIDClass's idle notification request. + + This request is provided to a HID miniport, and provides a callback + routine typically used by HID miniports to self-manage power. + + The callback routine is invoked by the miniport to indicate that all + peripherals are idle, and in response the HID class driver will: + 1) Queue a wait/wake IRP to the device, and + 2) Set the device to D3 + + In the case of this touch miniport, we are using the HID class driver's + enhanced power management functionality, whereby invoking the callback + results in an immediate exit from D0, powering off touch. + + The Request will be completed when either HIDCLASS cancels it or + there is a device wake signal that will cause us to complete it. + +Arguments: + + Device - Handle to WDF Device Object + + Request - Handle to request object + + Pending - flag to monitor if the request was sent down the stack + +Return Value: + + On success, the function returns STATUS_SUCCESS + On failure it passes the relevant error code to the caller. + +--*/ +{ + PDEVICE_EXTENSION devContext; + PHID_SUBMIT_IDLE_NOTIFICATION_CALLBACK_INFO idleCallbackInfo; + PIRP irp; + PIO_STACK_LOCATION irpSp; + NTSTATUS status; + + devContext = GetDeviceContext(Device); + + NT_ASSERT(Pending != NULL); + *Pending = FALSE; + + // + // Retrieve request parameters and validate + // + irp = WdfRequestWdmGetIrp(Request); + irpSp = IoGetCurrentIrpStackLocation(irp); + + if (irpSp->Parameters.DeviceIoControl.InputBufferLength < + sizeof(HID_SUBMIT_IDLE_NOTIFICATION_CALLBACK_INFO)) + { + status = STATUS_INVALID_BUFFER_SIZE; + + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_HID, + "Error: Input buffer is too small to process idle request - %!STATUS!", + status); + + goto exit; + } + + // + // Grab the callback + // + idleCallbackInfo = (PHID_SUBMIT_IDLE_NOTIFICATION_CALLBACK_INFO) + irpSp->Parameters.DeviceIoControl.Type3InputBuffer; + + NT_ASSERT(idleCallbackInfo != NULL); + + if (idleCallbackInfo == NULL || idleCallbackInfo->IdleCallback == NULL) + { + status = STATUS_NO_CALLBACK_ACTIVE; + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_HID, + "Error: Idle Notification request %p has no idle callback info - %!STATUS!", + Request, + status); + goto exit; + } + + { + // + // Create a workitem for the idle callback + // + WDF_OBJECT_ATTRIBUTES workItemAttributes; + WDF_WORKITEM_CONFIG workitemConfig; + WDFWORKITEM idleWorkItem; + PIDLE_WORKITEM_CONTEXT idleWorkItemContext; + + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&workItemAttributes, IDLE_WORKITEM_CONTEXT); + workItemAttributes.ParentObject = devContext->FxDevice; + + WDF_WORKITEM_CONFIG_INIT(&workitemConfig, TchIdleIrpWorkitem); + + status = WdfWorkItemCreate( + &workitemConfig, + &workItemAttributes, + &idleWorkItem + ); + + if (!NT_SUCCESS(status)) { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_HID, + "Error creating creating idle work item - %!STATUS!", + status); + goto exit; + } + + // + // Set the workitem context + // + idleWorkItemContext = GetWorkItemContext(idleWorkItem); + idleWorkItemContext->FxDevice = devContext->FxDevice; + idleWorkItemContext->FxRequest = Request; + + // + // Enqueue a workitem for the idle callback + // + WdfWorkItemEnqueue(idleWorkItem); + + // + // Mark the request as pending so that + // we can complete it when we come out of idle + // + *Pending = TRUE; + } + +exit: + + return status; +} + +VOID +TchIdleIrpWorkitem( + IN WDFWORKITEM IdleWorkItem + ) +/*++ + +Routine Description: + + This is a workitem routine that TchProcessIdleRequest queues when + handling the HIDClass's idle notification IRP, so the idle callback can be made in + a different thread context, instead of the Idle Irp's dispatch call. + +Arguments: + + IdleWorkItem - Handle to a WDF workitem object + +Return Value: + + VOID + +--*/ +{ + NTSTATUS status; + PIDLE_WORKITEM_CONTEXT idleWorkItemContext; + PDEVICE_EXTENSION deviceContext; + PHID_SUBMIT_IDLE_NOTIFICATION_CALLBACK_INFO idleCallbackInfo; + + idleWorkItemContext = GetWorkItemContext(IdleWorkItem); + NT_ASSERT(idleWorkItemContext != NULL); + + deviceContext = GetDeviceContext(idleWorkItemContext->FxDevice); + NT_ASSERT(deviceContext != NULL); + + // + // Get the idle callback info from the workitem context + // + idleCallbackInfo = (PHID_SUBMIT_IDLE_NOTIFICATION_CALLBACK_INFO) + IoGetCurrentIrpStackLocation(WdfRequestWdmGetIrp(idleWorkItemContext->FxRequest))->\ + Parameters.DeviceIoControl.Type3InputBuffer; + + // + // idleCallbackInfo is validated already, so invoke idle callback + // + idleCallbackInfo->IdleCallback(idleCallbackInfo->IdleContext); + + // + // Park this request in our IdleQueue and mark it as pending + // This way if the IRP was cancelled, WDF will cancel it for us + // + status = WdfRequestForwardToIoQueue( + idleWorkItemContext->FxRequest, + deviceContext->IdleQueue); + + if (!NT_SUCCESS(status)) + { + // + // IdleQueue is a manual-dispatch, non-power-managed queue. This should + // *never* fail. + // + + NT_ASSERTMSG("WdfRequestForwardToIoQueue to IdleQueue failed!", FALSE); + + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_IDLE, + "Error forwarding idle notification Request:0x%p to IdleQueue:0x%p - %!STATUS!", + idleWorkItemContext->FxRequest, + deviceContext->IdleQueue, + status); + + // + // Complete the request if we couldnt forward to the Idle Queue + // + WdfRequestComplete(idleWorkItemContext->FxRequest, status); + } + else + { + Trace( + TRACE_LEVEL_INFORMATION, + TRACE_FLAG_IDLE, + "Forwarded idle notification Request:0x%p to IdleQueue:0x%p - %!STATUS!", + idleWorkItemContext->FxRequest, + deviceContext->IdleQueue, + status); + } + + // + // Delete the workitem since we're done with it + // + WdfObjectDelete(IdleWorkItem); + + return; +} + + +VOID +TchCompleteIdleIrp( + IN PDEVICE_EXTENSION FxDeviceContext + ) +/*++ + +Routine Description: + + This is invoked when we enter D0. + We simply complete the Idle Irp if it hasn't been cancelled already. + +Arguments: + + FxDeviceContext - Pointer to Device Context for the device + +Return Value: + + + +--*/ +{ + NTSTATUS status; + WDFREQUEST request = NULL; + + // + // Lets try to retrieve the Idle IRP from the Idle queue + // + status = WdfIoQueueRetrieveNextRequest( + FxDeviceContext->IdleQueue, + &request); + + // + // We did not find the Idle IRP, maybe it was cancelled + // + if (!NT_SUCCESS(status) || (request == NULL)) + { + Trace( + TRACE_LEVEL_WARNING, + TRACE_FLAG_IDLE, + "Error finding idle notification request in IdleQueue:0x%p - %!STATUS!", + FxDeviceContext->IdleQueue, + status); + } + else + { + // + // Complete the Idle IRP + // + WdfRequestComplete(request, status); + + Trace( + TRACE_LEVEL_INFORMATION, + TRACE_FLAG_IDLE, + "Completed idle notification Request:0x%p from IdleQueue:0x%p - %!STATUS!", + request, + FxDeviceContext->IdleQueue, + status); + } + + return; +} \ No newline at end of file diff --git a/input/hiddigi/SynapticsTouch/idle.h b/input/hiddigi/SynapticsTouch/idle.h new file mode 100644 index 000000000..1539f88cb --- /dev/null +++ b/input/hiddigi/SynapticsTouch/idle.h @@ -0,0 +1,54 @@ +/*++ + Copyright (c) Microsoft Corporation. All Rights Reserved. + Sample code. Dealpoint ID #843729. + + Module Name: + + idle.c + + Abstract: + + This file contains the declarations for Power Idle specific callbacks + and function declarations + + Environment: + + Kernel mode + + Revision History: + +--*/ + +#pragma once + + +// +// Power Idle Workitem context +// +typedef struct _IDLE_WORKITEM_CONTEXT +{ + // Handle to a WDF device object + WDFDEVICE FxDevice; + + // Handle to a WDF request object + WDFREQUEST FxRequest; + +} IDLE_WORKITEM_CONTEXT, *PIDLE_WORKITEM_CONTEXT; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(IDLE_WORKITEM_CONTEXT, GetWorkItemContext) + +NTSTATUS +TchProcessIdleRequest( + IN WDFDEVICE Device, + IN WDFREQUEST Request, + OUT BOOLEAN *Pending + ); + +VOID +TchCompleteIdleIrp( + IN PDEVICE_EXTENSION FxDeviceContext + ); + +EVT_WDF_WORKITEM TchIdleIrpWorkitem; + + diff --git a/input/hiddigi/SynapticsTouch/init.c b/input/hiddigi/SynapticsTouch/init.c new file mode 100644 index 000000000..fa72c9ba0 --- /dev/null +++ b/input/hiddigi/SynapticsTouch/init.c @@ -0,0 +1,1167 @@ +/*++ + Copyright (c) Microsoft Corporation. All Rights Reserved. + Sample code. Dealpoint ID #843729. + + Module Name: + + init.c + + Abstract: + + Contains Synaptics initialization code + + Environment: + + Kernel mode + + Revision History: + +--*/ + +#include "rmiinternal.h" +#include "spb.h" +#include "init.tmh" + + +#pragma warning(push) +#pragma warning(disable:4242) // Conversion, possible loss of data + +// +// The logical values come from the registry and are hence DWORDs but the +// physical registers are only 8 bits wide so we use the lower 8 bits of the +// logical value. +// +#define LOGICAL_TO_PHYSICAL(LOGICAL_VALUE) ((LOGICAL_VALUE) & 0xff) + + +NTSTATUS +RmiChangePage( + IN RMI4_CONTROLLER_CONTEXT* ControllerContext, + IN SPB_CONTEXT* SpbContext, + IN int DesiredPage + ) +/*++ + + Routine Description: + + This utility function changes the current register address page. + + Arguments: + + ControllerContext - A pointer to the current touch controller context + SpbContext - A pointer to the current i2c context + DesiredPage - The page the caller expects to be mapped in + + Return Value: + + NTSTATUS indicating success or failure + +--*/ +{ + BYTE page; + NTSTATUS status; + + // + // If we're on this page already return success + // + if (ControllerContext->CurrentPage == DesiredPage) + { + status = STATUS_SUCCESS; + } + else + { + page = (BYTE) DesiredPage; + + status = SpbWriteDataSynchronously( + SpbContext, + RMI4_PAGE_SELECT_ADDRESS, + &page, + sizeof(BYTE)); + + if (NT_SUCCESS(status)) + { + ControllerContext->CurrentPage = DesiredPage; + } + } + + return status; +} + +int +RmiGetFunctionIndex( + IN RMI4_FUNCTION_DESCRIPTOR* FunctionDescriptors, + IN int FunctionCount, + IN int FunctionDesired + ) +/*++ + + Routine Description: + + Returns the descriptor table index that corresponds to the + desired RMI function. + + Arguments: + + FunctionDescriptors - A pointer to the touch controllers + full list of function descriptors + + FunctionCount - The count of function descriptors contained + in the above FunctionDescriptors list + + FunctionDesired - The RMI function number (note they are always + in hexadecimal the RMI4 specification) + + Return Value: + + NTSTATUS indicating success or failure + +--*/ +{ + UCHAR i; + + for (i=0; i < FunctionCount; i++) + { + // + // Break if we found the index + // + if (FunctionDescriptors[i].Number == FunctionDesired) + { + break; + } + } + + // + // Return the count if the index wasn't found + // + return i; +} + +NTSTATUS +RmiGetFirmwareVersion( + IN RMI4_CONTROLLER_CONTEXT *ControllerContext, + IN SPB_CONTEXT *SpbContext + ) +/*++ + + Routine Description: + + This function queries the firmware version of the current chip for + debugging purposes. + + Arguments: + + ControllerContext - A pointer to the current touch controller context + SpbContext - A pointer to the current i2c context + + Return Value: + + NTSTATUS indicating success or failure + +--*/ +{ + int index; + NTSTATUS status; + + // + // Find RMI device control function and configure it + // + index = RmiGetFunctionIndex( + ControllerContext->Descriptors, + ControllerContext->FunctionCount, + RMI4_F01_RMI_DEVICE_CONTROL); + + if (index == ControllerContext->FunctionCount) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "Unexpected - RMI Function 01 missing"); + + status = STATUS_INVALID_DEVICE_STATE; + goto exit; + } + + status = RmiChangePage( + ControllerContext, + SpbContext, + ControllerContext->FunctionOnPage[index]); + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "Could not change register page"); + + goto exit; + } + + // + // Store all F01 query registers, which contain the product ID + // + // TODO: Fix transfer size when SPB can support larger I2C + // transactions + // + status = SpbReadDataSynchronously( + SpbContext, + ControllerContext->Descriptors[index].QueryBase, + &ControllerContext->F01QueryRegisters, + sizeof(BYTE) * FIELD_OFFSET(RMI4_F01_QUERY_REGISTERS, ProductID10)); + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "Error reading RMI F01 Query registers - %!STATUS!", + status); + + goto exit; + } + +exit: + + return status; +} + +VOID +RmiConvertF01ToPhysical( + IN RMI4_F01_CTRL_REGISTERS_LOGICAL* Logical, + IN RMI4_F01_CTRL_REGISTERS* Physical + ) +/*++ + + Routine Description: + + Registry configuration values for F01 must be specified as + 4-byte REG_DWORD values logically, however the chip interprets these + values as bits or bytes physically. This function converts + the registry parameters into a structure that can be programmed + into the controller's memory. + + Arguments: + + Logical - a pointer to the logical settings + + Physical - a pointer to the controller memory-mapped structure + + Return Value: + + None. Function may print warnings in the future when truncating. + +--*/ +{ + RtlZeroMemory(Physical, sizeof(RMI4_F01_CTRL_REGISTERS)); + + // + // Note that truncation of registry values is possible if + // the data was incorrectly provided by the OEM, we may + // print warning messages in the future. + // + + Physical->DeviceControl.SleepMode = LOGICAL_TO_PHYSICAL(Logical->SleepMode); + Physical->DeviceControl.NoSleep = LOGICAL_TO_PHYSICAL(Logical->NoSleep); + Physical->DeviceControl.ReportRate = LOGICAL_TO_PHYSICAL(Logical->ReportRate); + Physical->DeviceControl.Configured = LOGICAL_TO_PHYSICAL(Logical->Configured); + + Physical->InterruptEnable = LOGICAL_TO_PHYSICAL(Logical->InterruptEnable); + Physical->DozeInterval = LOGICAL_TO_PHYSICAL(Logical->DozeInterval); + Physical->DozeThreshold = LOGICAL_TO_PHYSICAL(Logical->DozeThreshold); + Physical->DozeHoldoff = LOGICAL_TO_PHYSICAL(Logical->DozeHoldoff); +} + +VOID +RmiConvertF11ToPhysical( + IN RMI4_F11_CTRL_REGISTERS_LOGICAL* Logical, + IN RMI4_F11_CTRL_REGISTERS* Physical + ) +/*++ + + Routine Description: + + Registry configuration values for F11 must be specified as + 4-byte REG_DWORD values logically, however the chip interprets these + values as bits or bytes physically. This function converts + the registry parameters into a structure that can be programmed + into the controller's memory. + + Arguments: + + Logical - a pointer to the logical settings + + Physical - a pointer to the controller memory-mapped structure + + Return Value: + + None. Function may print warnings in the future when truncating. + +--*/ +{ + RtlZeroMemory(Physical, sizeof(RMI4_F11_CTRL_REGISTERS)); + + // + // Note that truncation of registry values is possible if + // the data was incorrectly provided by the OEM, we may + // print warning messages in the future. + // + + Physical->ReportingMode = LOGICAL_TO_PHYSICAL(Logical->ReportingMode); + Physical->AbsPosFilt = LOGICAL_TO_PHYSICAL(Logical->AbsPosFilt); + Physical->RelPosFilt = LOGICAL_TO_PHYSICAL(Logical->RelPosFilt); + Physical->RelBallistics = LOGICAL_TO_PHYSICAL(Logical->RelBallistics); + Physical->Dribble = LOGICAL_TO_PHYSICAL(Logical->Dribble); + + Physical->PalmDetectThreshold = LOGICAL_TO_PHYSICAL(Logical->PalmDetectThreshold); + Physical->MotionSensitivity = LOGICAL_TO_PHYSICAL(Logical->MotionSensitivity); + Physical->ManTrackEn = LOGICAL_TO_PHYSICAL(Logical->ManTrackEn); + Physical->ManTrackedFinger = LOGICAL_TO_PHYSICAL(Logical->ManTrackedFinger); + + Physical->DeltaXPosThreshold = LOGICAL_TO_PHYSICAL(Logical->DeltaXPosThreshold); + Physical->DeltaYPosThreshold = LOGICAL_TO_PHYSICAL(Logical->DeltaYPosThreshold); + Physical->Velocity = LOGICAL_TO_PHYSICAL(Logical->Velocity); + Physical->Acceleration = LOGICAL_TO_PHYSICAL(Logical->Acceleration); + + Physical->SensorMaxXPosLo = (Logical->SensorMaxXPos & 0xFF) >> 0; + Physical->SensorMaxXPosHi = (Logical->SensorMaxXPos & 0xF00) >> 8; + + Physical->SensorMaxYPosLo = (Logical->SensorMaxYPos & 0xFF) >> 0; + Physical->SensorMaxYPosHi = (Logical->SensorMaxYPos & 0xF00) >> 8; + + Physical->ZTouchThreshold = LOGICAL_TO_PHYSICAL(Logical->ZTouchThreshold); + Physical->ZHysteresis = LOGICAL_TO_PHYSICAL(Logical->ZHysteresis); + Physical->SmallZThreshold = LOGICAL_TO_PHYSICAL(Logical->SmallZThreshold); + + Physical->SmallZScaleFactor[0] = (Logical->SmallZScaleFactor & 0xFF) >> 0; + Physical->SmallZScaleFactor[1] = (Logical->SmallZScaleFactor & 0xFF00) >> 8; + + Physical->LargeZScaleFactor[0] = (Logical->LargeZScaleFactor & 0xFF) >> 0; + Physical->LargeZScaleFactor[1] = (Logical->LargeZScaleFactor & 0xFF00) >> 8; + + Physical->AlgorithmSelection = LOGICAL_TO_PHYSICAL((Logical->AlgorithmSelection)); + Physical->WxScaleFactor = LOGICAL_TO_PHYSICAL((Logical->WxScaleFactor)); + Physical->WxOffset = LOGICAL_TO_PHYSICAL((Logical->WxOffset)); + Physical->WyScaleFactor = LOGICAL_TO_PHYSICAL((Logical->WyScaleFactor)); + Physical->WyOffset = LOGICAL_TO_PHYSICAL((Logical->WyOffset)); + + Physical->XPitch[0] = (Logical->XPitch & 0xFF) >> 0; + Physical->XPitch[1] = (Logical->XPitch & 0xFF00) >> 8; + + Physical->YPitch[0] = (Logical->YPitch & 0xFF) >> 0; + Physical->YPitch[1] = (Logical->YPitch & 0xFF00) >> 8; + + Physical->FingerWidthX[0] = (Logical->FingerWidthX & 0xFF) >> 0; + Physical->FingerWidthX[1] = (Logical->FingerWidthX & 0xFF00) >> 8; + + Physical->FingerWidthY[0] = (Logical->FingerWidthY & 0xFF) >> 0; + Physical->FingerWidthY[1] = (Logical->FingerWidthY & 0xFF00) >> 8; + + Physical->ReportMeasuredSize = LOGICAL_TO_PHYSICAL(Logical->ReportMeasuredSize); + Physical->SegmentationSensitivity = LOGICAL_TO_PHYSICAL(Logical->SegmentationSensitivity); + Physical->XClipLo = LOGICAL_TO_PHYSICAL(Logical->XClipLo); + Physical->XClipHi = LOGICAL_TO_PHYSICAL(Logical->XClipHi); + Physical->YClipLo = LOGICAL_TO_PHYSICAL(Logical->YClipLo); + Physical->YClipHi = LOGICAL_TO_PHYSICAL(Logical->YClipHi); + Physical->MinFingerSeparation = LOGICAL_TO_PHYSICAL(Logical->MinFingerSeparation); + Physical->MaxFingerMovement = LOGICAL_TO_PHYSICAL(Logical->MaxFingerMovement); +} + +NTSTATUS +RmiConfigureFunctions( + IN RMI4_CONTROLLER_CONTEXT *ControllerContext, + IN SPB_CONTEXT *SpbContext + ) +/*++ + + Routine Description: + + RMI4 devices such as this Synaptics touch controller are organized + as collections of logical functions. Discovered functions must be + configured, which is done in this function (things like sleep + timeouts, interrupt enables, report rates, etc.) + + Arguments: + + ControllerContext - A pointer to the current touch controller + context + + SpbContext - A pointer to the current i2c context + + Return Value: + + NTSTATUS indicating success or failure + +--*/ +{ + int index; + NTSTATUS status; + + RMI4_F01_CTRL_REGISTERS controlF01 = {0}; + RMI4_F11_CTRL_REGISTERS controlF11 = {0}; + + // + // Find 2D touch sensor function and configure it + // + index = RmiGetFunctionIndex( + ControllerContext->Descriptors, + ControllerContext->FunctionCount, + RMI4_F11_2D_TOUCHPAD_SENSOR); + + if (index == ControllerContext->FunctionCount) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "Unexpected - RMI Function 11 missing"); + + status = STATUS_INVALID_DEVICE_STATE; + goto exit; + } + + status = RmiChangePage( + ControllerContext, + SpbContext, + ControllerContext->FunctionOnPage[index]); + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "Could not change register page"); + + goto exit; + } + + RmiConvertF11ToPhysical( + &ControllerContext->Config.TouchSettings, + &controlF11); + + // + // Write settings to controller + // + status = SpbWriteDataSynchronously( + SpbContext, + ControllerContext->Descriptors[index].ControlBase, + &controlF11, + sizeof(controlF11) + ); + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "Error writing RMI F11 Ctrl settings - %!STATUS!", + status); + goto exit; + } + + // + // Find 0D capacitive button sensor function and configure it if it exists + // + index = RmiGetFunctionIndex( + ControllerContext->Descriptors, + ControllerContext->FunctionCount, + RMI4_F1A_0D_CAP_BUTTON_SENSOR); + + if (index != ControllerContext->FunctionCount) + { + ControllerContext->HasButtons = TRUE; + + // + // TODO: Get configuration data from registry once Synaptics + // provides sane default values. Until then, assume the + // object is configured for the desired product scenario + // by default. + // + } + + // + // Find RMI device control function and configure it + // + index = RmiGetFunctionIndex( + ControllerContext->Descriptors, + ControllerContext->FunctionCount, + RMI4_F01_RMI_DEVICE_CONTROL); + + if (index == ControllerContext->FunctionCount) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "Unexpected - RMI Function 01 missing"); + + status = STATUS_INVALID_DEVICE_STATE; + goto exit; + } + + status = RmiChangePage( + ControllerContext, + SpbContext, + ControllerContext->FunctionOnPage[index]); + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "Could not change register page"); + + goto exit; + } + + RmiConvertF01ToPhysical( + &ControllerContext->Config.DeviceSettings, + &controlF01); + + // + // Write settings to controller + // + status = SpbWriteDataSynchronously( + SpbContext, + ControllerContext->Descriptors[index].ControlBase, + &controlF01, + sizeof(controlF01) + ); + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "Error writing RMI F01 Ctrl settings - %!STATUS!", + status); + goto exit; + } + + // + // Note whether the device configuration settings initialized the + // controller in an operating state, to prevent a double-start from + // the D0 entry dispatch routine (TchWakeDevice) + // + if (RMI4_F11_DEVICE_CONTROL_SLEEP_MODE_OPERATING == + controlF01.DeviceControl.SleepMode) + { + ControllerContext->DevicePowerState = PowerDeviceD0; + } + else + { + ControllerContext->DevicePowerState = PowerDeviceD3; + } + +exit: + + return status; +} + +NTSTATUS +RmiBuildFunctionsTable( + IN RMI4_CONTROLLER_CONTEXT *ControllerContext, + IN SPB_CONTEXT *SpbContext + ) +/*++ + + Routine Description: + + RMI4 devices such as this Synaptics touch controller are organized + as collections of logical functions. When initially communicating + with the chip, a driver must build a table of available functions, + as is done in this routine. + + Arguments: + + ControllerContext - A pointer to the current touch controller context + + SpbContext - A pointer to the current i2c context + + Return Value: + + NTSTATUS indicating success or failure + +--*/ +{ + UCHAR address; + int function; + int page; + NTSTATUS status; + + + // + // First function is at a fixed address + // + function = 0; + address = RMI4_FIRST_FUNCTION_ADDRESS; + page = 0; + + // + // Discover chip functions one by one + // + do + { + // + // Read function descriptor + // + status = SpbReadDataSynchronously( + SpbContext, + address, + &ControllerContext->Descriptors[function], + sizeof(RMI4_FUNCTION_DESCRIPTOR)); + + if (!(NT_SUCCESS(status))) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "Error returned from SPB/I2C read attempt %d - %!STATUS!", + function, + status); + goto exit; + } + + // + // Function number 0 implies "last function" on this register page, + // and if this "last function" is the first function on the page, there + // are no more functions to discover. + // + if (ControllerContext->Descriptors[function].Number == 0 && + address == RMI4_FIRST_FUNCTION_ADDRESS) + { + break; + } + // + // If we've exhausted functions on this page, look for more functoins + // on the next register page + // + else if (ControllerContext->Descriptors[function].Number == 0 && + address != RMI4_FIRST_FUNCTION_ADDRESS) + { + page++; + address = RMI4_FIRST_FUNCTION_ADDRESS; + + status = RmiChangePage( + ControllerContext, + SpbContext, + page); + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "Error attempting to change page - %!STATUS!", + status); + goto exit; + } + } + // + // Descriptor stored, look for next or terminator + // + else + { + Trace( + TRACE_LEVEL_VERBOSE, + TRACE_FLAG_INIT, + "Discovered function $%x", + ControllerContext->Descriptors[function].Number); + + ControllerContext->FunctionOnPage[function] = page; + function++; + address = address - sizeof(RMI4_FUNCTION_DESCRIPTOR); + } + + } while ( + (address > 0) && + (function < RMI4_MAX_FUNCTIONS)); + + // + // If we swept the address space without finding an "end function" + // or maxed-out the total number of functions supported by the + // driver, note the error and exit. + // + if (function >= RMI4_MAX_FUNCTIONS) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "Error, encountered more than %d functions, must extend driver", + RMI4_MAX_FUNCTIONS); + + status = STATUS_INVALID_DEVICE_STATE; + goto exit; + } + if (address <= 0) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "Error, did not find terminator function 0, address down to %d", + address); + + status = STATUS_INVALID_DEVICE_STATE; + goto exit; + } + + // + // Note the total number of functions that exist + // + ControllerContext->FunctionCount = function; + + Trace( + TRACE_LEVEL_VERBOSE, + TRACE_FLAG_INIT, + "Discovered %d RMI functions total", + function); + +exit: + + return status; +} + +NTSTATUS +RmiCheckInterrupts( + IN RMI4_CONTROLLER_CONTEXT *ControllerContext, + IN SPB_CONTEXT *SpbContext, + IN ULONG* InterruptStatus + ) +/*++ + + Routine Description: + + This function handles controller interrupts. It currently only + supports valid touch interrupts. Any other interrupt sources (such as + device losing configuration or being reset) are unhandled, but noted + in the controller context. + + Arguments: + + ControllerContext - A pointer to the current touch controller + context + + SpbContext - A pointer to the current i2c context + + Return Value: + + NTSTATUS indicating success or failure + +--*/ +{ + RMI4_F01_DATA_REGISTERS data; + int index; + NTSTATUS status; + + RtlZeroMemory(&data, sizeof(data)); + *InterruptStatus = 0; + + // + // Locate RMI data base address + // + index = RmiGetFunctionIndex( + ControllerContext->Descriptors, + ControllerContext->FunctionCount, + RMI4_F01_RMI_DEVICE_CONTROL); + + if (index == ControllerContext->FunctionCount) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "Unexpected - RMI Function 01 missing"); + + status = STATUS_INVALID_DEVICE_STATE; + goto exit; + } + + status = RmiChangePage( + ControllerContext, + SpbContext, + ControllerContext->FunctionOnPage[index]); + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "Could not change register page"); + + goto exit; + } + + // + // Read interrupt status registers + // + status = SpbReadDataSynchronously( + SpbContext, + ControllerContext->Descriptors[index].DataBase, + &data, + sizeof(data)); + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INTERRUPT, + "Error reading interrupt status - %!STATUS!", + status); + + goto exit; + } + + // + // Check for catastrophic failures, simply store in context for + // debugging should these errors occur. + // + switch (data.DeviceStatus.Status) + { + case RMI4_F01_DATA_STATUS_NO_ERROR: + { + break; + } + case RMI4_F01_DATA_STATUS_RESET_OCCURRED: + { + ControllerContext->ResetOccurred = TRUE; + break; + } + case RMI4_F01_DATA_STATUS_INVALID_CONFIG: + { + ControllerContext->InvalidConfiguration = TRUE; + + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INTERRUPT, + "Received status code 2 - invalid configuration"); + + break; + } + case RMI4_F01_DATA_STATUS_DEVICE_FAILURE: + { + ControllerContext->DeviceFailure = TRUE; + + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INTERRUPT, + "Received status code 4 - device failure"); + + break; + } + default: + { + ControllerContext->UnknownStatus = TRUE; + ControllerContext->UnknownStatusMessage = data.DeviceStatus.Status; + + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INTERRUPT, + "Received unknown status code - %d", + ControllerContext->UnknownStatusMessage); + + break; + } + } + + // + // If we're in flash programming mode, report an error + // + if (data.DeviceStatus.FlashProg) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INTERRUPT, + "Error, device status indicates chip in programming mode"); + + goto exit; + } + + // + // If the chip has lost it's configuration, reconfigure + // + if (data.DeviceStatus.Unconfigured) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INTERRUPT, + "Error, device status indicates chip is unconfigured"); + + status = RmiConfigureFunctions( + ControllerContext, + SpbContext); + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INTERRUPT, + "Could not reconfigure chip - %!STATUS!", + status); + + goto exit; + } + + } + + if (data.InterruptStatus[0]) + { + *InterruptStatus = data.InterruptStatus[0] & 0xFF; + } + else + { + Trace( + TRACE_LEVEL_VERBOSE, + TRACE_FLAG_INTERRUPT, + "Unexpected -- no interrupt status bit set"); + } + +exit: + return status; +} + +NTSTATUS +TchStartDevice( + IN VOID *ControllerContext, + IN SPB_CONTEXT *SpbContext + ) +/*++ + + Routine Description: + + This routine is called in response to the KMDF prepare hardware call + to initialize the touch controller for use. + + Arguments: + + ControllerContext - A pointer to the current touch controller + context + + SpbContext - A pointer to the current i2c context + + Return Value: + + NTSTATUS indicating success or failure + +--*/ +{ + RMI4_CONTROLLER_CONTEXT* controller; + ULONG interruptStatus; + NTSTATUS status; + + controller = (RMI4_CONTROLLER_CONTEXT*) ControllerContext; + interruptStatus = 0; + status = STATUS_SUCCESS; + + // + // Populate context with RMI function descriptors + // + status = RmiBuildFunctionsTable( + ControllerContext, + SpbContext); + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "Could not build table of RMI functions - %!STATUS!", + status); + goto exit; + } + + // + // Initialize RMI function control registers + // + status = RmiConfigureFunctions( + ControllerContext, + SpbContext); + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "Could not configure RMI functions - %!STATUS!", + status); + goto exit; + } + + // + // Read and store the firmware version + // + status = RmiGetFirmwareVersion( + ControllerContext, + SpbContext); + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "Could not get RMI firmware version - %!STATUS!", + status); + goto exit; + } + + // + // Clear any pending interrupts + // + status = RmiCheckInterrupts( + ControllerContext, + SpbContext, + &interruptStatus + ); + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "Could not get interrupt status - %!STATUS!%", + status); + } + +exit: + + return status; +} + +NTSTATUS +TchStopDevice( + IN VOID *ControllerContext, + IN SPB_CONTEXT *SpbContext + ) +/*++ + +Routine Description: + + This routine cleans up the device that is stopped. + +Argument: + + ControllerContext - Touch controller context + + SpbContext - A pointer to the current i2c context + +Return Value: + + NTSTATUS indicating sucess or failure +--*/ +{ + RMI4_CONTROLLER_CONTEXT* controller; + + UNREFERENCED_PARAMETER(SpbContext); + + controller = (RMI4_CONTROLLER_CONTEXT*) ControllerContext; + + return STATUS_SUCCESS; +} + +NTSTATUS +TchAllocateContext( + OUT VOID **ControllerContext, + IN WDFDEVICE FxDevice + ) +/*++ + +Routine Description: + + This routine allocates a controller context. + +Argument: + + ControllerContext - Touch controller context + FxDevice - Framework device object + +Return Value: + + NTSTATUS indicating sucess or failure +--*/ +{ + RMI4_CONTROLLER_CONTEXT* context; + NTSTATUS status; + + context = ExAllocatePoolWithTag( + NonPagedPool, + sizeof(RMI4_CONTROLLER_CONTEXT), + TOUCH_POOL_TAG); + + if (NULL == context) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "Could not allocate controller context!"); + + status = STATUS_UNSUCCESSFUL; + goto exit; + } + + RtlZeroMemory(context, sizeof(RMI4_CONTROLLER_CONTEXT)); + context->FxDevice = FxDevice; + + // + // Get screen properties and populate context + // + TchGetScreenProperties(&context->Props); + + // + // Allocate a WDFWAITLOCK for guarding access to the + // controller HW and driver controller context + // + status = WdfWaitLockCreate( + WDF_NO_OBJECT_ATTRIBUTES, + &context->ControllerLock); + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "Could not allocate controller context - %!STATUS!", + status); + + goto exit; + + } + + *ControllerContext = context; + +exit: + + return status; +} + +NTSTATUS +TchFreeContext( + IN VOID *ControllerContext + ) +/*++ + +Routine Description: + + This routine frees a controller context. + +Argument: + + ControllerContext - Touch controller context + +Return Value: + + NTSTATUS indicating sucess or failure +--*/ +{ + RMI4_CONTROLLER_CONTEXT* controller; + + controller = (RMI4_CONTROLLER_CONTEXT*) ControllerContext; + + if (controller != NULL) + { + + if (controller->ControllerLock != NULL) + { + WdfObjectDelete(controller->ControllerLock); + } + + ExFreePoolWithTag(controller, TOUCH_POOL_TAG); + } + + return STATUS_SUCCESS; +} + +#pragma warning(pop) diff --git a/input/hiddigi/SynapticsTouch/internal.h b/input/hiddigi/SynapticsTouch/internal.h new file mode 100644 index 000000000..39b8bfdc1 --- /dev/null +++ b/input/hiddigi/SynapticsTouch/internal.h @@ -0,0 +1,73 @@ +/*++ + Copyright (c) Microsoft Corporation. All Rights Reserved. + Sample code. Dealpoint ID #843729. + + Module Name: + + internal.h + + Abstract: + + Contains common types and defintions used internally + by the multi touch screen driver. + + Environment: + + Kernel mode + + Revision History: + +--*/ + +#pragma once + +#include "controller.h" + +// +// Device context +// + +typedef struct _DEVICE_EXTENSION +{ + // + // HID Touch input mode (touch vs. mouse) + // + UCHAR InputMode; + + // + // Device related + // + WDFDEVICE FxDevice; + WDFQUEUE DefaultQueue; + WDFQUEUE PingPongQueue; + + // + // Interrupt servicing + // + WDFINTERRUPT InterruptObject; + BOOLEAN ServiceInterruptsAfterD0Entry; + + // + // Spb (I2C) related members used for the lifetime of the device + // + SPB_CONTEXT I2CContext; + + // + // Test related + // + WDFQUEUE TestQueue; + volatile LONG TestSessionRefCnt; + BOOLEAN DiagnosticMode; + + // + // Power related + // + WDFQUEUE IdleQueue; + + // + // Touch related members used for the lifetime of the device + // + VOID *TouchContext; +} DEVICE_EXTENSION, *PDEVICE_EXTENSION; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(DEVICE_EXTENSION, GetDeviceContext) diff --git a/input/hiddigi/SynapticsTouch/power.c b/input/hiddigi/SynapticsTouch/power.c new file mode 100644 index 000000000..b234afb8f --- /dev/null +++ b/input/hiddigi/SynapticsTouch/power.c @@ -0,0 +1,272 @@ +/*++ + Copyright (c) Microsoft Corporation. All Rights Reserved. + Sample code. Dealpoint ID #843729. + + Module Name: + + power.c + + Abstract: + + Contains Synaptics power-on and power-off functionality + + Environment: + + Kernel mode + + Revision History: + +--*/ + +#include "controller.h" +#include "rmiinternal.h" +#include "spb.h" +#include "power.tmh" + +NTSTATUS +RmiChangeSleepState( + IN RMI4_CONTROLLER_CONTEXT* ControllerContext, + IN SPB_CONTEXT *SpbContext, + IN UCHAR SleepState + ) +/*++ + +Routine Description: + + Changes the SleepMode bits on the controller as specified + +Arguments: + + ControllerContext - Touch controller context + + SpbContext - A pointer to the current i2c context + + SleepState - Either RMI4_F11_DEVICE_CONTROL_SLEEP_MODE_OPERATING + or RMI4_F11_DEVICE_CONTROL_SLEEP_MODE_SLEEPING + +Return Value: + + NTSTATUS indicating success or failure + +--*/ +{ + RMI4_F01_CTRL_REGISTERS* controlF01; + UCHAR deviceControl; + int index; + NTSTATUS status; + + controlF01 = (RMI4_F01_CTRL_REGISTERS*) &deviceControl; + + // + // Find RMI device control function housing sleep settings + // + index = RmiGetFunctionIndex( + ControllerContext->Descriptors, + ControllerContext->FunctionCount, + RMI4_F01_RMI_DEVICE_CONTROL); + + if (index == ControllerContext->FunctionCount) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_POWER, + "Power change failure - RMI Function 01 missing"); + + status = STATUS_INVALID_DEVICE_STATE; + goto exit; + } + + status = RmiChangePage( + ControllerContext, + SpbContext, + ControllerContext->FunctionOnPage[index]); + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_POWER, + "Could not change register page"); + + goto exit; + } + + // + // Read Device Control register + // + status = SpbReadDataSynchronously( + SpbContext, + ControllerContext->Descriptors[index].ControlBase, + &deviceControl, + sizeof(deviceControl) + ); + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_POWER, + "Could not read sleep register - %!STATUS!", + status); + + goto exit; + } + + // + // Assign new sleep state + // + controlF01->DeviceControl.SleepMode = SleepState; + + // + // Write setting back to the controller + // + status = SpbWriteDataSynchronously( + SpbContext, + ControllerContext->Descriptors[index].ControlBase, + &deviceControl, + sizeof(deviceControl) + ); + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_POWER, + "Could not write sleep register - %X", + status); + + goto exit; + } + +exit: + + return status; +} + +NTSTATUS +TchWakeDevice( + IN VOID *ControllerContext, + IN SPB_CONTEXT *SpbContext + ) +/*++ + +Routine Description: + + Enables multi-touch scanning + +Arguments: + + ControllerContext - Touch controller context + + SpbContext - A pointer to the current i2c context + +Return Value: + + NTSTATUS indicating success or failure + +--*/ +{ + RMI4_CONTROLLER_CONTEXT* controller; + NTSTATUS status; + + controller = (RMI4_CONTROLLER_CONTEXT*) ControllerContext; + + // + // Check if we were already on + // + if (controller->DevicePowerState == PowerDeviceD0) + { + goto exit; + } + + controller->DevicePowerState = PowerDeviceD0; + + // + // Attempt to put the controller into operating mode + // + status = RmiChangeSleepState( + controller, + SpbContext, + RMI4_F11_DEVICE_CONTROL_SLEEP_MODE_OPERATING); + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_POWER, + "Error waking touch controller - %!STATUS!", + status); + } + +exit: + + return STATUS_SUCCESS; +} + +NTSTATUS +TchStandbyDevice( + IN VOID *ControllerContext, + IN SPB_CONTEXT *SpbContext + ) +/*++ + +Routine Description: + + Disables multi-touch scanning to conserve power + +Arguments: + + ControllerContext - Touch controller context + + SpbContext - A pointer to the current i2c context + +Return Value: + + NTSTATUS indicating success or failure + +--*/ +{ + RMI4_CONTROLLER_CONTEXT* controller; + NTSTATUS status; + + controller = (RMI4_CONTROLLER_CONTEXT*) ControllerContext; + + // + // Interrupts are now disabled but the ISR may still be + // executing, so grab the controller lock to ensure ISR + // is finished touching HW and controller state. + // + WdfWaitLockAcquire(controller->ControllerLock, NULL); + + // + // Put the chip in sleep mode + // + status = RmiChangeSleepState( + ControllerContext, + SpbContext, + RMI4_F11_DEVICE_CONTROL_SLEEP_MODE_SLEEPING); + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_POWER, + "Error sleeping touch controller - %!STATUS!", + status); + } + + controller->DevicePowerState = PowerDeviceD3; + + // + // Invalidate state + // + controller->TouchesReported = 0; + controller->TouchesTotal = 0; + controller->Cache.FingerSlotValid = 0; + controller->Cache.FingerSlotDirty = 0; + controller->Cache.FingerDownCount = 0; + + WdfWaitLockRelease(controller->ControllerLock); + + return STATUS_SUCCESS; +} \ No newline at end of file diff --git a/input/hiddigi/SynapticsTouch/queue.c b/input/hiddigi/SynapticsTouch/queue.c new file mode 100644 index 000000000..e765df8b5 --- /dev/null +++ b/input/hiddigi/SynapticsTouch/queue.c @@ -0,0 +1,180 @@ +/*++ + Copyright (c) Microsoft Corporation. All Rights Reserved. + Sample code. Dealpoint ID #843729. + + Module Name: + + queue.c + + Abstract: + + Contains WDF queue related code, namely the top-level queue + handler for all device IOCTL requests. + + Environment: + + Kernel mode + + Revision History: + +--*/ + +#include "internal.h" +#include "controller.h" +#include "queue.h" +#include "hid.h" +#include "idle.h" +#include "queue.tmh" + +VOID +OnInternalDeviceControl( + 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_INTERNAL 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, status is indicated when completing the request + +--*/ +{ + NTSTATUS status; + PDEVICE_EXTENSION devContext; + WDFDEVICE device; + BOOLEAN requestPending; + + UNREFERENCED_PARAMETER(OutputBufferLength); + UNREFERENCED_PARAMETER(InputBufferLength); + + device = WdfIoQueueGetDevice(Queue); + devContext = GetDeviceContext(device); + requestPending = FALSE; + + // + // Please note that HIDCLASS provides the buffer in the Irp->UserBuffer + // field irrespective of the ioctl buffer type. However, framework is very + // strict about type checking. You cannot get Irp->UserBuffer by using + // WdfRequestRetrieveOutputMemory if the ioctl is not a METHOD_NEITHER + // internal ioctl. So depending on the ioctl code, we will either + // use retreive function or escape to WDM to get the UserBuffer. + // + + switch(IoControlCode) { + + case IOCTL_HID_GET_DEVICE_DESCRIPTOR: + // + // Retrieves master HID descriptor + // + + status = TchGetHidDescriptor(device, Request); + break; + + case IOCTL_HID_GET_DEVICE_ATTRIBUTES: + // + // Retrieves device attributes in a HID_DEVICE_ATTRIBUTES structure + // + + status = TchGetDeviceAttributes(Request); + break; + + case IOCTL_HID_GET_REPORT_DESCRIPTOR: + // + // Obtains the report descriptor for the HID device + // + + status = TchGetReportDescriptor(device, Request); + break; + + case IOCTL_HID_GET_STRING: + // + // Obtains strings associated with the HID device + // + + status = TchGetString(device, Request); + break; + + case IOCTL_HID_READ_REPORT: + // + // Dangling read requests for passing up touch data + // + + status = TchReadReport(device, Request, &requestPending); + break; + + case IOCTL_HID_SET_FEATURE: + // + // This sends a HID class feature report to a top-level collection of + // a HID class device. + // + + status = TchSetFeatureReport(device, Request); + break; + + case IOCTL_HID_GET_FEATURE: + // + // Returns a feature report associated with a top-level collection + // + + status = TchGetFeatureReport(device, Request); + break; + + case IOCTL_HID_SEND_IDLE_NOTIFICATION_REQUEST: + // + // Hidclass sends this IOCTL to notify miniports that it wants + // them to go into idle + // + + status = TchProcessIdleRequest(device, Request, &requestPending); + break; + + case IOCTL_HID_WRITE_REPORT: + // + // Transmits a class driver-supplied report to the device. + // + case IOCTL_HID_ACTIVATE_DEVICE: + // + // Makes the device ready for I/O operations. + // + case IOCTL_HID_DEACTIVATE_DEVICE: + // + // Causes the device to cease operations and terminate all outstanding + // I/O requests. + // + + default: + status = STATUS_NOT_SUPPORTED; + break; + } + + if (!requestPending) + { + WdfRequestComplete(Request, status); + } + + return; +} diff --git a/input/hiddigi/SynapticsTouch/queue.h b/input/hiddigi/SynapticsTouch/queue.h new file mode 100644 index 000000000..777ee04c4 --- /dev/null +++ b/input/hiddigi/SynapticsTouch/queue.h @@ -0,0 +1,24 @@ +/*++ + Copyright (c) Microsoft Corporation. All Rights Reserved. + Sample code. Dealpoint ID #843729. + + Module Name: + + queue.h + + Abstract: + + Contains WDF queue related function declarations + + Environment: + + Kernel mode + + Revision History: + +--*/ + +#pragma once + + +EVT_WDF_IO_QUEUE_IO_INTERNAL_DEVICE_CONTROL OnInternalDeviceControl; diff --git a/input/hiddigi/SynapticsTouch/readme.md b/input/hiddigi/SynapticsTouch/readme.md new file mode 100644 index 000000000..190da7363 --- /dev/null +++ b/input/hiddigi/SynapticsTouch/readme.md @@ -0,0 +1,72 @@ +Synaptics Touch Sample +====================== +The Synaptics Touch (KMDF) sample demonstrates how to write a HID miniport driver for the Synaptics 3202 touch controller. + +## Universal Windows Driver Compliant +This sample builds a Universal Windows Driver. It uses only APIs and DDIs that are included in OneCoreUAP. + +Related technologies +-------------------- + +[Kernel-Mode Driver Framework](http://msdn.microsoft.com/en-us/library/windows/hardware/ff544396) + +[Human Interface Devices](http://msdn.microsoft.com/en-us/library/windows/hardware/jj126202) + +[Windows Pointer Device](http://msdn.microsoft.com/en-us/library/windows/hardware/jj151570) + + +##System requirements +------------------- +**Client:** Windows 10 Technical Preview + +**Server:** Windows 10 Technical Preview + +**Phone:** Windows 10 Technical Preview + + +File Manifest +------------- +#### 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. + +#### hid.c, hid.h +Contains the HID descriptor and functions to handle to HID requests. + +#### idle.c, idle.h +Contains the declarations for Power Idle specific callbacks and function definitions. + +#### internal.h +Contains common types and defintions used internally by the multi touch screen driver. + +#### spb.c +Contains all I2C-specific functionality. + +#### init.c +Contains Synaptics initialization code. + +#### power.c +Contains Synaptics power-on and power-off functionality. + +#### registry.c +This module retrieves platform-specific controller configuration from the registry, or assigns default values if no registry configuration is present. + +#### report.c +Contains Synaptics specific code for reporting samples. + +#### resolutions.c +Contains resolution translation defines and types. + +#### rmiinternal.h +Contains common types and defintions used internally by the multi touch screen driver. + +#### SynapticsTouch.inx +File that describes the installation of this driver. The build process converts this into an INF file. + + + diff --git a/input/hiddigi/SynapticsTouch/registry.c b/input/hiddigi/SynapticsTouch/registry.c new file mode 100644 index 000000000..29c66be3f --- /dev/null +++ b/input/hiddigi/SynapticsTouch/registry.c @@ -0,0 +1,722 @@ +/*++ + Copyright (c) Microsoft Corporation. All Rights Reserved. + Sample code. Dealpoint ID #843729. + + Module Name: + + registry.c + + Abstract: + + This module retrieves platform-specific controller + configuration from the registry, or assigns default + values if no registry configuration is present. + + Environment: + + Kernel mode + + Revision History: + +--*/ + +#include "rmiinternal.h" +#include "registry.tmh" + +// +// Default RMI4 configuration values can be changed here. Please refer to the +// RMI4 specification for a full description of the fields and value meanings +// + +static RMI4_CONFIGURATION gDefaultConfiguration = +{ + // + // RMI4 F01 - Device control settings + // + { + 0, // Sleep Mode (normal) + 1, // No Sleep (do sleep) + 0, // Report Rate (standard) + 1, // Configured + 0xf, // Interrupt Enable + RMI4_MILLISECONDS_TO_TENTH_MILLISECONDS(20), // Doze Interval + 10, // Doze Threshold + RMI4_SECONDS_TO_HALF_SECONDS(2) // Doze Holdoff + }, + + // + // RMI4 F11 - 2D Touchpad sensor settings + // + { + 1, // Reporting mode (throttle) + 1, // Abs position filter + 0, // Rel position filter + 0, // Rel ballistics + 0, // Dribble + 0xb, // PalmDetectThreshold + 3, // MotionSensitivity + 0, // ManTrackEn + 0, // ManTrackedFinger + 0, // DeltaXPosThreshold + 0, // DeltaYPosThreshold + 0, // Velocity + 0, // Acceleration + TOUCH_DEVICE_RESOLUTION_X, // Sensor Max X Position + TOUCH_DEVICE_RESOLUTION_Y, // Sensor Max Y Position + 0x1e, // ZTouchThreshold + 0x05, // ZHysteresis + 0x28, // SmallZThreshold + 0x28f5, // SmallZScaleFactor + 0x051e, // LargeZScaleFactor + 0x1, // AlgorithmSelection + 0x30, // WxScaleFactor + 0x0, // WxOffset + 0x30, // WyScaleFactor + 0x0, // WyOffset + 0x4800, // XPitch + 0x4800, // YPitch + 0xea4f, // FingerWidthX + 0xdf6c, // FingerWidthY + 0, // ReportMeasuredSize + 0x70, // SegmentationSensitivity + 0x0, // XClipLo + 0x0, // XClipHi + 0x0, // YClipLo + 0x0, // YClipHi + 0x0a, // MinFingerSeparation + 0x04 // MaxFingerMovement + }, + + // + // Internal driver settings + // + { + 0x0, // Controller stays powered in D3 + }, +}; + +RTL_QUERY_REGISTRY_TABLE gRegistryTable[] = +{ + // + // RMI4 F01 - Device control settings + // + { + NULL, RTL_QUERY_REGISTRY_DIRECT, + L"SleepMode", + (PVOID) (FIELD_OFFSET(RMI4_CONFIGURATION, DeviceSettings) + + FIELD_OFFSET(RMI4_F01_CTRL_REGISTERS_LOGICAL, SleepMode)), + REG_DWORD, + &gDefaultConfiguration.DeviceSettings.SleepMode, + sizeof(UINT32) + }, + { + NULL, RTL_QUERY_REGISTRY_DIRECT, + L"NoSleep", + (PVOID) (FIELD_OFFSET(RMI4_CONFIGURATION, DeviceSettings) + + FIELD_OFFSET(RMI4_F01_CTRL_REGISTERS_LOGICAL, NoSleep)), + REG_DWORD, + &gDefaultConfiguration.DeviceSettings.NoSleep, + sizeof(UINT32) + }, + { + NULL, RTL_QUERY_REGISTRY_DIRECT, + L"ReportRate", + (PVOID) (FIELD_OFFSET(RMI4_CONFIGURATION, DeviceSettings) + + FIELD_OFFSET(RMI4_F01_CTRL_REGISTERS_LOGICAL, ReportRate)), + REG_DWORD, + &gDefaultConfiguration.DeviceSettings.ReportRate, + sizeof(UINT32) + }, + { + NULL, RTL_QUERY_REGISTRY_DIRECT, + L"Configured", + (PVOID) (FIELD_OFFSET(RMI4_CONFIGURATION, DeviceSettings) + + FIELD_OFFSET(RMI4_F01_CTRL_REGISTERS_LOGICAL, Configured)), + REG_DWORD, + &gDefaultConfiguration.DeviceSettings.Configured, + sizeof(UINT32) + }, + { + NULL, RTL_QUERY_REGISTRY_DIRECT, + L"InterruptEnable", + (PVOID) (FIELD_OFFSET(RMI4_CONFIGURATION, DeviceSettings) + + FIELD_OFFSET(RMI4_F01_CTRL_REGISTERS_LOGICAL, InterruptEnable)), + REG_DWORD, + &gDefaultConfiguration.DeviceSettings.InterruptEnable, + sizeof(UINT32) + }, + { + NULL, RTL_QUERY_REGISTRY_DIRECT, + L"DozeInterval", + (PVOID) (FIELD_OFFSET(RMI4_CONFIGURATION, DeviceSettings) + + FIELD_OFFSET(RMI4_F01_CTRL_REGISTERS_LOGICAL, DozeInterval)), + REG_DWORD, + &gDefaultConfiguration.DeviceSettings.DozeInterval, + sizeof(UINT32) + }, + { + NULL, RTL_QUERY_REGISTRY_DIRECT, + L"DozeThreshold", + (PVOID) (FIELD_OFFSET(RMI4_CONFIGURATION, DeviceSettings) + + FIELD_OFFSET(RMI4_F01_CTRL_REGISTERS_LOGICAL, DozeThreshold)), + REG_DWORD, + &gDefaultConfiguration.DeviceSettings.DozeThreshold, + sizeof(UINT32) + }, + { + NULL, RTL_QUERY_REGISTRY_DIRECT, + L"DozeHoldoff", + (PVOID) (FIELD_OFFSET(RMI4_CONFIGURATION, DeviceSettings) + + FIELD_OFFSET(RMI4_F01_CTRL_REGISTERS_LOGICAL, DozeHoldoff)), + REG_DWORD, + &gDefaultConfiguration.DeviceSettings.DozeHoldoff, + sizeof(UINT32) + }, + + // + // RMI4 F11 - 2D Touchpad sensor settings + // + { + NULL, RTL_QUERY_REGISTRY_DIRECT, + L"ReportingMode", + (PVOID) (FIELD_OFFSET(RMI4_CONFIGURATION, TouchSettings) + + FIELD_OFFSET(RMI4_F11_CTRL_REGISTERS_LOGICAL, ReportingMode)), + REG_DWORD, + &gDefaultConfiguration.TouchSettings.ReportingMode, + sizeof(UINT32) + }, + { + NULL, RTL_QUERY_REGISTRY_DIRECT, + L"AbsPosFilt", + (PVOID) (FIELD_OFFSET(RMI4_CONFIGURATION, TouchSettings) + + FIELD_OFFSET(RMI4_F11_CTRL_REGISTERS_LOGICAL, AbsPosFilt)), + REG_DWORD, + &gDefaultConfiguration.TouchSettings.AbsPosFilt, + sizeof(UINT32) + }, + { + NULL, RTL_QUERY_REGISTRY_DIRECT, + L"RelPosFilt", + (PVOID) (FIELD_OFFSET(RMI4_CONFIGURATION, TouchSettings) + + FIELD_OFFSET(RMI4_F11_CTRL_REGISTERS_LOGICAL, RelPosFilt)), + REG_DWORD, + &gDefaultConfiguration.TouchSettings.RelPosFilt, + sizeof(UINT32) + }, + { + NULL, RTL_QUERY_REGISTRY_DIRECT, + L"RelBallistics", + (PVOID) (FIELD_OFFSET(RMI4_CONFIGURATION, TouchSettings) + + FIELD_OFFSET(RMI4_F11_CTRL_REGISTERS_LOGICAL, RelBallistics)), + REG_DWORD, + &gDefaultConfiguration.TouchSettings.RelBallistics, + sizeof(UINT32) + }, + { + NULL, RTL_QUERY_REGISTRY_DIRECT, + L"Dribble", + (PVOID) (FIELD_OFFSET(RMI4_CONFIGURATION, TouchSettings) + + FIELD_OFFSET(RMI4_F11_CTRL_REGISTERS_LOGICAL, Dribble)), + REG_DWORD, + &gDefaultConfiguration.TouchSettings.Dribble, + sizeof(UINT32) + }, + { + NULL, RTL_QUERY_REGISTRY_DIRECT, + L"PalmDetectThreshold", + (PVOID) (FIELD_OFFSET(RMI4_CONFIGURATION, TouchSettings) + + FIELD_OFFSET(RMI4_F11_CTRL_REGISTERS_LOGICAL, PalmDetectThreshold)), + REG_DWORD, + &gDefaultConfiguration.TouchSettings.PalmDetectThreshold, + sizeof(UINT32) + }, + { + NULL, RTL_QUERY_REGISTRY_DIRECT, + L"MotionSensitivity", + (PVOID) (FIELD_OFFSET(RMI4_CONFIGURATION, TouchSettings) + + FIELD_OFFSET(RMI4_F11_CTRL_REGISTERS_LOGICAL, MotionSensitivity)), + REG_DWORD, + &gDefaultConfiguration.TouchSettings.MotionSensitivity, + sizeof(UINT32) + }, + { + NULL, RTL_QUERY_REGISTRY_DIRECT, + L"ManTrackEn", + (PVOID) (FIELD_OFFSET(RMI4_CONFIGURATION, TouchSettings) + + FIELD_OFFSET(RMI4_F11_CTRL_REGISTERS_LOGICAL, ManTrackEn)), + REG_DWORD, + &gDefaultConfiguration.TouchSettings.ManTrackEn, + sizeof(UINT32) + }, + { + NULL, RTL_QUERY_REGISTRY_DIRECT, + L"ManTrackedFinger", + (PVOID) (FIELD_OFFSET(RMI4_CONFIGURATION, TouchSettings) + + FIELD_OFFSET(RMI4_F11_CTRL_REGISTERS_LOGICAL, ManTrackedFinger)), + REG_DWORD, + &gDefaultConfiguration.TouchSettings.ManTrackedFinger, + sizeof(UINT32) + }, + { + NULL, RTL_QUERY_REGISTRY_DIRECT, + L"DeltaXPosThreshold", + (PVOID) (FIELD_OFFSET(RMI4_CONFIGURATION, TouchSettings) + + FIELD_OFFSET(RMI4_F11_CTRL_REGISTERS_LOGICAL, DeltaXPosThreshold)), + REG_DWORD, + &gDefaultConfiguration.TouchSettings.DeltaXPosThreshold, + sizeof(UINT32) + }, + { + NULL, RTL_QUERY_REGISTRY_DIRECT, + L"DeltaYPosThreshold", + (PVOID) (FIELD_OFFSET(RMI4_CONFIGURATION, TouchSettings) + + FIELD_OFFSET(RMI4_F11_CTRL_REGISTERS_LOGICAL, DeltaYPosThreshold)), + REG_DWORD, + &gDefaultConfiguration.TouchSettings.DeltaYPosThreshold, + sizeof(UINT32) + }, + { + NULL, RTL_QUERY_REGISTRY_DIRECT, + L"Velocity", + (PVOID) (FIELD_OFFSET(RMI4_CONFIGURATION, TouchSettings) + + FIELD_OFFSET(RMI4_F11_CTRL_REGISTERS_LOGICAL, Velocity)), + REG_DWORD, + &gDefaultConfiguration.TouchSettings.Velocity, + sizeof(UINT32) + }, + { + NULL, RTL_QUERY_REGISTRY_DIRECT, + L"Acceleration", + (PVOID) (FIELD_OFFSET(RMI4_CONFIGURATION, TouchSettings) + + FIELD_OFFSET(RMI4_F11_CTRL_REGISTERS_LOGICAL, Acceleration)), + REG_DWORD, + &gDefaultConfiguration.TouchSettings.Acceleration, + sizeof(UINT32) + }, + { + NULL, RTL_QUERY_REGISTRY_DIRECT, + L"SensorMaxXPos", + (PVOID) (FIELD_OFFSET(RMI4_CONFIGURATION, TouchSettings) + + FIELD_OFFSET(RMI4_F11_CTRL_REGISTERS_LOGICAL, SensorMaxXPos)), + REG_DWORD, + &gDefaultConfiguration.TouchSettings.SensorMaxXPos, + sizeof(UINT32) + }, + { + NULL, RTL_QUERY_REGISTRY_DIRECT, + L"SensorMaxYPos", + (PVOID) (FIELD_OFFSET(RMI4_CONFIGURATION, TouchSettings) + + FIELD_OFFSET(RMI4_F11_CTRL_REGISTERS_LOGICAL, SensorMaxYPos)), + REG_DWORD, + &gDefaultConfiguration.TouchSettings.SensorMaxYPos, + sizeof(UINT32) + }, + { + NULL, RTL_QUERY_REGISTRY_DIRECT, + L"ZTouchThreshold", + (PVOID) (FIELD_OFFSET(RMI4_CONFIGURATION, TouchSettings) + + FIELD_OFFSET(RMI4_F11_CTRL_REGISTERS_LOGICAL, ZTouchThreshold)), + REG_DWORD, + &gDefaultConfiguration.TouchSettings.ZTouchThreshold, + sizeof(UINT32) + }, + { + NULL, RTL_QUERY_REGISTRY_DIRECT, + L"ZHysteresis", + (PVOID) (FIELD_OFFSET(RMI4_CONFIGURATION, TouchSettings) + + FIELD_OFFSET(RMI4_F11_CTRL_REGISTERS_LOGICAL, ZHysteresis)), + REG_DWORD, + &gDefaultConfiguration.TouchSettings.ZHysteresis, + sizeof(UINT32) + }, + { + NULL, RTL_QUERY_REGISTRY_DIRECT, + L"SmallZThreshold", + (PVOID) (FIELD_OFFSET(RMI4_CONFIGURATION, TouchSettings) + + FIELD_OFFSET(RMI4_F11_CTRL_REGISTERS_LOGICAL, SmallZThreshold)), + REG_DWORD, + &gDefaultConfiguration.TouchSettings.SmallZThreshold, + sizeof(UINT32) + }, + { + NULL, RTL_QUERY_REGISTRY_DIRECT, + L"SmallZScaleFactor", + (PVOID) (FIELD_OFFSET(RMI4_CONFIGURATION, TouchSettings) + + FIELD_OFFSET(RMI4_F11_CTRL_REGISTERS_LOGICAL, SmallZScaleFactor)), + REG_DWORD, + &gDefaultConfiguration.TouchSettings.SmallZScaleFactor, + sizeof(UINT32) + }, + { + NULL, RTL_QUERY_REGISTRY_DIRECT, + L"LargeZScaleFactor", + (PVOID) (FIELD_OFFSET(RMI4_CONFIGURATION, TouchSettings) + + FIELD_OFFSET(RMI4_F11_CTRL_REGISTERS_LOGICAL, LargeZScaleFactor)), + REG_DWORD, + &gDefaultConfiguration.TouchSettings.LargeZScaleFactor, + sizeof(UINT32) + }, + { + NULL, RTL_QUERY_REGISTRY_DIRECT, + L"AlgorithmSelection", + (PVOID) (FIELD_OFFSET(RMI4_CONFIGURATION, TouchSettings) + + FIELD_OFFSET(RMI4_F11_CTRL_REGISTERS_LOGICAL, AlgorithmSelection)), + REG_DWORD, + &gDefaultConfiguration.TouchSettings.AlgorithmSelection, + sizeof(UINT32) + }, + { + NULL, RTL_QUERY_REGISTRY_DIRECT, + L"WxScaleFactor", + (PVOID) (FIELD_OFFSET(RMI4_CONFIGURATION, TouchSettings) + + FIELD_OFFSET(RMI4_F11_CTRL_REGISTERS_LOGICAL, WxScaleFactor)), + REG_DWORD, + &gDefaultConfiguration.TouchSettings.WxScaleFactor, + sizeof(UINT32) + }, + { + NULL, RTL_QUERY_REGISTRY_DIRECT, + L"WxOffset", + (PVOID) (FIELD_OFFSET(RMI4_CONFIGURATION, TouchSettings) + + FIELD_OFFSET(RMI4_F11_CTRL_REGISTERS_LOGICAL, WxOffset)), + REG_DWORD, + &gDefaultConfiguration.TouchSettings.WxOffset, + sizeof(UINT32) + }, + { + NULL, RTL_QUERY_REGISTRY_DIRECT, + L"WyScaleFactor", + (PVOID) (FIELD_OFFSET(RMI4_CONFIGURATION, TouchSettings) + + FIELD_OFFSET(RMI4_F11_CTRL_REGISTERS_LOGICAL, WyScaleFactor)), + REG_DWORD, + &gDefaultConfiguration.TouchSettings.WyScaleFactor, + sizeof(UINT32) + }, + { + NULL, RTL_QUERY_REGISTRY_DIRECT, + L"WyOffset", + (PVOID) (FIELD_OFFSET(RMI4_CONFIGURATION, TouchSettings) + + FIELD_OFFSET(RMI4_F11_CTRL_REGISTERS_LOGICAL, WyOffset)), + REG_DWORD, + &gDefaultConfiguration.TouchSettings.WyOffset, + sizeof(UINT32) + }, + { + NULL, RTL_QUERY_REGISTRY_DIRECT, + L"XPitch", + (PVOID) (FIELD_OFFSET(RMI4_CONFIGURATION, TouchSettings) + + FIELD_OFFSET(RMI4_F11_CTRL_REGISTERS_LOGICAL, XPitch)), + REG_DWORD, + &gDefaultConfiguration.TouchSettings.XPitch, + sizeof(UINT32) + }, + { + NULL, RTL_QUERY_REGISTRY_DIRECT, + L"YPitch", + (PVOID) (FIELD_OFFSET(RMI4_CONFIGURATION, TouchSettings) + + FIELD_OFFSET(RMI4_F11_CTRL_REGISTERS_LOGICAL, YPitch)), + REG_DWORD, + &gDefaultConfiguration.TouchSettings.YPitch, + sizeof(UINT32) + }, + { + NULL, RTL_QUERY_REGISTRY_DIRECT, + L"FingerWidthX", + (PVOID) (FIELD_OFFSET(RMI4_CONFIGURATION, TouchSettings) + + FIELD_OFFSET(RMI4_F11_CTRL_REGISTERS_LOGICAL, FingerWidthX)), + REG_DWORD, + &gDefaultConfiguration.TouchSettings.FingerWidthX, + sizeof(UINT32) + }, + { + NULL, RTL_QUERY_REGISTRY_DIRECT, + L"FingerWidthY", + (PVOID) (FIELD_OFFSET(RMI4_CONFIGURATION, TouchSettings) + + FIELD_OFFSET(RMI4_F11_CTRL_REGISTERS_LOGICAL, FingerWidthY)), + REG_DWORD, + &gDefaultConfiguration.TouchSettings.FingerWidthY, + sizeof(UINT32) + }, + { + NULL, RTL_QUERY_REGISTRY_DIRECT, + L"ReportMeasuredSize", + (PVOID) (FIELD_OFFSET(RMI4_CONFIGURATION, TouchSettings) + + FIELD_OFFSET(RMI4_F11_CTRL_REGISTERS_LOGICAL, ReportMeasuredSize)), + REG_DWORD, + &gDefaultConfiguration.TouchSettings.ReportMeasuredSize, + sizeof(UINT32) + }, + { + NULL, RTL_QUERY_REGISTRY_DIRECT, + L"SegmentationSensitivity", + (PVOID) (FIELD_OFFSET(RMI4_CONFIGURATION, TouchSettings) + + FIELD_OFFSET(RMI4_F11_CTRL_REGISTERS_LOGICAL, SegmentationSensitivity)), + REG_DWORD, + &gDefaultConfiguration.TouchSettings.SegmentationSensitivity, + sizeof(UINT32) + }, + { + NULL, RTL_QUERY_REGISTRY_DIRECT, + L"XClipLo", + (PVOID) (FIELD_OFFSET(RMI4_CONFIGURATION, TouchSettings) + + FIELD_OFFSET(RMI4_F11_CTRL_REGISTERS_LOGICAL, XClipLo)), + REG_DWORD, + &gDefaultConfiguration.TouchSettings.XClipLo, + sizeof(UINT32) + }, + { + NULL, RTL_QUERY_REGISTRY_DIRECT, + L"XClipHi", + (PVOID) (FIELD_OFFSET(RMI4_CONFIGURATION, TouchSettings) + + FIELD_OFFSET(RMI4_F11_CTRL_REGISTERS_LOGICAL, XClipHi)), + REG_DWORD, + &gDefaultConfiguration.TouchSettings.XClipHi, + sizeof(UINT32) + }, + { + NULL, RTL_QUERY_REGISTRY_DIRECT, + L"YClipLo", + (PVOID) (FIELD_OFFSET(RMI4_CONFIGURATION, TouchSettings) + + FIELD_OFFSET(RMI4_F11_CTRL_REGISTERS_LOGICAL, YClipLo)), + REG_DWORD, + &gDefaultConfiguration.TouchSettings.YClipLo, + sizeof(UINT32) + }, + { + NULL, RTL_QUERY_REGISTRY_DIRECT, + L"YClipHi", + (PVOID) (FIELD_OFFSET(RMI4_CONFIGURATION, TouchSettings) + + FIELD_OFFSET(RMI4_F11_CTRL_REGISTERS_LOGICAL, YClipHi)), + REG_DWORD, + &gDefaultConfiguration.TouchSettings.YClipHi, + sizeof(UINT32) + }, + { + NULL, RTL_QUERY_REGISTRY_DIRECT, + L"MinFingerSeparation", + (PVOID) (FIELD_OFFSET(RMI4_CONFIGURATION, TouchSettings) + + FIELD_OFFSET(RMI4_F11_CTRL_REGISTERS_LOGICAL, MinFingerSeparation)), + REG_DWORD, + &gDefaultConfiguration.TouchSettings.MinFingerSeparation, + sizeof(UINT32) + }, + { + NULL, RTL_QUERY_REGISTRY_DIRECT, + L"MaxFingerMovement", + (PVOID) (FIELD_OFFSET(RMI4_CONFIGURATION, TouchSettings) + + FIELD_OFFSET(RMI4_F11_CTRL_REGISTERS_LOGICAL, MaxFingerMovement)), + REG_DWORD, + &gDefaultConfiguration.TouchSettings.MaxFingerMovement, + sizeof(UINT32) + }, + + // + // Add new supported functions here + // + + // + // Internal driver settings + // + { + NULL, RTL_QUERY_REGISTRY_DIRECT, + L"PepRemovesVoltageInD3", + (PVOID) (FIELD_OFFSET(RMI4_CONFIGURATION, PepRemovesVoltageInD3)), + REG_DWORD, + &gDefaultConfiguration.PepRemovesVoltageInD3, + sizeof(UINT32) + }, + + // + // List Terminator + // + { + NULL, 0, + NULL, + 0, + REG_DWORD, + NULL, + 0 + } +}; +static const ULONG gcbRegistryTable = sizeof(gRegistryTable); +static const ULONG gcRegistryTable = + sizeof(gRegistryTable) / sizeof(gRegistryTable[0]); + + +NTSTATUS +TchRegistryGetControllerSettings( + IN VOID* ControllerContext, + IN WDFDEVICE FxDevice + ) +/*++ + + Routine Description: + + This routine retrieves controller wide settings + from the registry. + + Arguments: + + FxDevice - a handle to the framework device object + Settings - A pointer to the chip settings structure + + Return Value: + + NTSTATUS indicating success or failure + +--*/ +{ + RMI4_CONTROLLER_CONTEXT* controller; + HANDLE hKey; + ULONG i; + WDFKEY key; + PRTL_QUERY_REGISTRY_TABLE regTable; + NTSTATUS status; + WDFKEY subkey; + DECLARE_CONST_UNICODE_STRING(subkeyName, L"Settings"); + + controller = (RMI4_CONTROLLER_CONTEXT*) ControllerContext; + + hKey = NULL; + key = NULL; + regTable = NULL; + subkey = NULL; + + // + // Obtain a WDM hkey for RtlQueryRegistryValues + // + + status = WdfDeviceOpenRegistryKey( + FxDevice, + PLUGPLAY_REGKEY_DEVICE, + KEY_READ, + WDF_NO_OBJECT_ATTRIBUTES, + &key); + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_REGISTRY, + "Error opening device registry key - %!STATUS!", + status); + + goto exit; + } + + status = WdfRegistryOpenKey( + key, + &subkeyName, + KEY_READ, + WDF_NO_OBJECT_ATTRIBUTES, + &subkey); + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_REGISTRY, + "Error opening device registry subkey - %!STATUS!", + status); + + goto exit; + } + + hKey = WdfRegistryWdmGetHandle(subkey); + + if (NULL == hKey) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_REGISTRY, + "Error getting WDM handle to WDF subkey"); + + goto exit; + } + + // + // RtlQueryRegistryValues table must be allocated from NonPagedPool + // + + regTable = ExAllocatePoolWithTag( + NonPagedPool, + gcbRegistryTable, + TOUCH_POOL_TAG); + + RtlCopyMemory( + regTable, + gRegistryTable, + gcbRegistryTable); + + // + // Update offset values with base pointer + // + + for (i=0; i < gcRegistryTable-1; i++) + { + regTable[i].EntryContext = (PVOID) ( + ((SIZE_T) regTable[i].EntryContext) + + ((ULONG_PTR) &controller->Config)); + } + + // + // Populate device context with registry or default configurations + // + + status = RtlQueryRegistryValues( + RTL_REGISTRY_HANDLE, + (PCWSTR) hKey, + regTable, + NULL, + NULL); + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_REGISTRY, + "Error retrieving registry configuration - %!STATUS!", + status); + + goto exit; + } + +exit: + + if (!NT_SUCCESS(status)) + { + // + // Revert to default configuration values if there was an + // issue reading configuration data from the registry + // + RtlCopyMemory( + &controller->Config, + &gDefaultConfiguration, + sizeof(RMI4_CONFIGURATION)); + + Trace( + TRACE_LEVEL_WARNING, + TRACE_FLAG_REGISTRY, + "Error reading registry config, using defaults! - %!STATUS!", + status); + + status = STATUS_SUCCESS; + } + + if (subkey != NULL) + { + WdfRegistryClose(subkey); + } + + if (key != NULL) + { + WdfRegistryClose(key); + } + + if (regTable != NULL) + { + ExFreePoolWithTag(regTable, TOUCH_POOL_TAG); + } + + return status; +} \ No newline at end of file diff --git a/input/hiddigi/SynapticsTouch/report.c b/input/hiddigi/SynapticsTouch/report.c new file mode 100644 index 000000000..e2e58406f --- /dev/null +++ b/input/hiddigi/SynapticsTouch/report.c @@ -0,0 +1,864 @@ +/*++ + Copyright (c) Microsoft Corporation. All Rights Reserved. + Sample code. Dealpoint ID #843729. + + Module Name: + + report.c + + Abstract: + + Contains Synaptics specific code for reporting samples + + Environment: + + Kernel mode + + Revision History: + +--*/ + +#include "controller.h" +#include "rmiinternal.h" +#include "spb.h" +#include "report.tmh" + +const USHORT gOEMVendorID = 0x7379; // "sy" +const USHORT gOEMProductID = 0x726D; // "rm" +const USHORT gOEMVersionID = 3200; + +const PWSTR gpwstrManufacturerID = L"Synaptics"; +const PWSTR gpwstrProductID = L"3200"; +const PWSTR gpwstrSerialNumber = L"4"; + +NTSTATUS +RmiServiceCapacitiveButtonInterrupt( + IN RMI4_CONTROLLER_CONTEXT* ControllerContext, + IN SPB_CONTEXT* SpbContext, + IN PHID_INPUT_REPORT HidReport + ) +/*++ + +Routine Description: + + This routine services capacitive button (F$1A) interrupts, it reads + button data and fills a HID keyboard report with the relevant information + +Arguments: + + ControllerContext - Touch controller context + SpbContext - A pointer to the current i2c context + HidReport- A HID report buffer to be filled with button data + +Return Value: + + NTSTATUS, where success indicates the request memory was updated with + button press information. + +--*/ +{ + RMI4_F1A_DATA_REGISTERS dataF1A; + PHID_KEY_REPORT hidKeys; + int index; + NTSTATUS status; + + // + // If the controller doesn't support buttons, ignore this interrupt + // + if (ControllerContext->HasButtons == FALSE) + { + status = STATUS_NOT_IMPLEMENTED; + goto exit; + } + + // + // Get the the key press/release information from the controller + // + index = RmiGetFunctionIndex( + ControllerContext->Descriptors, + ControllerContext->FunctionCount, + RMI4_F1A_0D_CAP_BUTTON_SENSOR); + + if (index == ControllerContext->FunctionCount) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "Unexpected - RMI Function 1A missing"); + + status = STATUS_INVALID_DEVICE_STATE; + goto exit; + } + + status = RmiChangePage( + ControllerContext, + SpbContext, + ControllerContext->FunctionOnPage[index]); + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "Could not change register page"); + + goto exit; + } + + // + // Read button press/release data + // + status = SpbReadDataSynchronously( + SpbContext, + ControllerContext->Descriptors[index].DataBase, + &dataF1A, + sizeof(dataF1A)); + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INTERRUPT, + "Error reading finger status data - %!STATUS!", + status); + + goto exit; + } + + RtlZeroMemory(HidReport, sizeof(HID_INPUT_REPORT)); + + // + // Update button states. This mapping should be made registry configurable + // + HidReport->ReportID = REPORTID_CAPKEY; + hidKeys = &(HidReport->KeyReport); + hidKeys->InputReport.bKeys |= (dataF1A.Button0) ? KEY_DOWN_SEARCH : 0; + hidKeys->InputReport.bKeys |= (dataF1A.Button1) ? KEY_DOWN_START : 0; + hidKeys->InputReport.bKeys |= (dataF1A.Button2) ? KEY_DOWN_BACK : 0; + + // + // On return of success, this request will be completed up the stack + // + +exit: + + return status; +} + +NTSTATUS +RmiGetTouchesFromController( + IN VOID *ControllerContext, + IN SPB_CONTEXT *SpbContext, + IN RMI4_F11_DATA_REGISTERS *Data + ) +/*++ + +Routine Description: + + This routine reads raw touch messages from hardware. If there is + no touch data available (if a non-touch interrupt fired), the + function will not return success and no touch data was transferred. + +Arguments: + + ControllerContext - Touch controller context + SpbContext - A pointer to the current i2c context + Data - A pointer to any returned F11 touch data + +Return Value: + + NTSTATUS, where only success indicates data was returned + +--*/ +{ + NTSTATUS status; + RMI4_CONTROLLER_CONTEXT* controller; + int index, i; + int fingerStatus[RMI4_MAX_TOUCHES] = {0}; + int highestSlot; + + controller = (RMI4_CONTROLLER_CONTEXT*) ControllerContext; + + // + // Locate RMI data base address of 2D touch function + // + index = RmiGetFunctionIndex( + controller->Descriptors, + controller->FunctionCount, + RMI4_F11_2D_TOUCHPAD_SENSOR); + + if (index == controller->FunctionCount) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "Unexpected - RMI Function 11 missing"); + + status = STATUS_INVALID_DEVICE_STATE; + goto exit; + } + + status = RmiChangePage( + ControllerContext, + SpbContext, + controller->FunctionOnPage[index]); + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "Could not change register page"); + + goto exit; + } + + // + // Read finger statuses first, to determine how much data we need to read + // + status = SpbReadDataSynchronously( + SpbContext, + controller->Descriptors[index].DataBase, + &Data->Status, + sizeof(RMI4_F11_DATA_REGISTERS_STATUS_BLOCK)); + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INTERRUPT, + "Error reading finger status data - %!STATUS!", + status); + + goto exit; + } + + fingerStatus[0] = Data->Status.FingerState0; + fingerStatus[1] = Data->Status.FingerState1; + fingerStatus[2] = Data->Status.FingerState2; + fingerStatus[3] = Data->Status.FingerState3; + fingerStatus[4] = Data->Status.FingerState4; + fingerStatus[5] = Data->Status.FingerState5; + fingerStatus[6] = Data->Status.FingerState6; + fingerStatus[7] = Data->Status.FingerState7; + fingerStatus[8] = Data->Status.FingerState8; + fingerStatus[9] = Data->Status.FingerState9; + + // + // Compute the last slot containing data of interest + // + highestSlot = 0; + + for (i=0; iCache.FingerSlotValid & (1 << i)) + { + highestSlot = i; + } + } + + for (i=highestSlot+1; iDescriptors[index].DataBase + + sizeof(RMI4_F11_DATA_REGISTERS_STATUS_BLOCK), + &Data->Finger[0], + sizeof(RMI4_F11_DATA_POSITION) * (highestSlot+1)); + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INTERRUPT, + "Error reading finger status data - %!STATUS!", + status); + + goto exit; + } + +exit: + return status; +} + +VOID +RmiUpdateLocalFingerCache( + IN RMI4_F11_DATA_REGISTERS *Data, + IN RMI4_FINGER_CACHE *Cache + ) +/*++ + +Routine Description: + + This routine takes raw data reported by the Synaptics hardware and + parses it to update a local cache of finger states. This routine manages + removing lifted touches from the cache, and manages a map between the + order of reported touches in hardware, and the order the driver should + use in reporting. + +Arguments: + + Data - A pointer to the new data returned from hardware + Cache - A data structure holding various current finger state info + +Return Value: + + None. + +--*/ +{ + int fingerStatus[RMI4_MAX_TOUCHES] = {0}; + int i, j; + + // + // Unpack the finger statuses into an array to ease dealing with each + // finger uniformly in loops + // + fingerStatus[0] = Data->Status.FingerState0; + fingerStatus[1] = Data->Status.FingerState1; + fingerStatus[2] = Data->Status.FingerState2; + fingerStatus[3] = Data->Status.FingerState3; + fingerStatus[4] = Data->Status.FingerState4; + fingerStatus[5] = Data->Status.FingerState5; + fingerStatus[6] = Data->Status.FingerState6; + fingerStatus[7] = Data->Status.FingerState7; + fingerStatus[8] = Data->Status.FingerState8; + fingerStatus[9] = Data->Status.FingerState9; + + // + // When hardware was last read, if any slots reported as lifted, we + // must clean out the slot and old touch info. There may be new + // finger data using the slot. + // + for (i=0; iFingerSlotDirty & (1 << i))) + { + continue; + } + + NT_ASSERT(Cache->FingerDownCount > 0); + + // + // Find the slot in the reporting list + // + for (j=0; jFingerDownOrder[j] == i) + { + break; + } + } + + NT_ASSERT(j != RMI4_MAX_TOUCHES); + + // + // Remove the slot. If the finger lifted was the last in the list, + // we just decrement the list total by one. If it was not last, we + // shift the trailing list items up by one. + // + for (; (jFingerDownCount-1) && (jFingerDownOrder[j] = Cache->FingerDownOrder[j+1]; + } + Cache->FingerDownCount--; + + // + // Finished, clobber the dirty bit + // + Cache->FingerSlotDirty &= ~(1 << i); + } + + // + // Cache the new set of finger data reported by hardware + // + for (i=0; iFingerSlotValid & (1 << i)) == 0) && + (Cache->FingerDownCount < RMI4_MAX_TOUCHES)) + { + Cache->FingerSlotValid |= (1 << i); + Cache->FingerDownOrder[Cache->FingerDownCount++] = i; + } + + // + // Ignore slots with no new information + // + if (!(Cache->FingerSlotValid & (1 << i))) + { + continue; + } + + // + // Update local cache with new information from the controller + // + Cache->FingerSlot[i].fingerStatus = (UCHAR) fingerStatus[i]; + Cache->FingerSlot[i].x = (Data->Finger[i].XPosLo & 0xF) | + ((Data->Finger[i].XPosHi & 0xFF) << 4); + Cache->FingerSlot[i].y = (Data->Finger[i].YPosLo & 0xF) | + ((Data->Finger[i].YPosHi & 0xFF) << 4); + + // + // If a finger lifted, note the slot is now inactive so that any + // cached data is cleaned out before we read hardware again. + // + if (Cache->FingerSlot[i].fingerStatus == RMI4_FINGER_STATE_NOT_PRESENT) + { + Cache->FingerSlotDirty |= (1 << i); + Cache->FingerSlotValid &= ~(1 << i); + } + } +} + +VOID +RmiFillNextHidReportFromCache( + IN PHID_INPUT_REPORT HidReport, + IN RMI4_FINGER_CACHE *Cache, + IN PTOUCH_SCREEN_PROPERTIES Props, + IN int *TouchesReported, + IN int TouchesTotal + ) +/*++ + +Routine Description: + + This routine fills a HID report with the next one or two touch entries in + the local device finger cache. + + The routine also adjusts X/Y coordinates to match the desired display + coordinates. + +Arguments: + + HidReport - pointer to the HID report structure to fill + Cache - pointer to the local device finger cache + Props - information on how to adjust X/Y coordinates to match the display + TouchesReported - On entry, the number of touches (against total) that + have already been reported. As touches are transferred from the local + device cache to a HID report, this number is incremented. + TouchesTotal - total number of touches in the touch cache + +Return Value: + + None. + +--*/ +{ + PHID_TOUCH_REPORT hidTouch = NULL; + int currentlyReporting; + + HidReport->ReportID = REPORTID_MTOUCH; + hidTouch = &(HidReport->TouchReport); + + // + // Report the next available finger + // + currentlyReporting = Cache->FingerDownOrder[*TouchesReported]; + + hidTouch->InputReport.ContactId = (UCHAR) currentlyReporting; + hidTouch->InputReport.wXData = (USHORT) Cache->FingerSlot[currentlyReporting].x; + hidTouch->InputReport.wYData = (USHORT) Cache->FingerSlot[currentlyReporting].y; + + // + // Perform per-platform x/y adjustments to controller coordinates + // + TchTranslateToDisplayCoordinates( + &hidTouch->InputReport.wXData, + &hidTouch->InputReport.wYData, + Props); + + if (Cache->FingerSlot[currentlyReporting].fingerStatus) + { + hidTouch->InputReport.bStatus = RANGE_FINGER_STATUS; + } + + (*TouchesReported)++; + + // + // A single HID report can contain two touches, so see if there's more + // + if (TouchesTotal - *TouchesReported > 0) + { + currentlyReporting = Cache->FingerDownOrder[*TouchesReported]; + + hidTouch->InputReport.ContactId2 = (UCHAR) currentlyReporting; + hidTouch->InputReport.wXData2 = (USHORT) Cache->FingerSlot[currentlyReporting].x; + hidTouch->InputReport.wYData2 = (USHORT) Cache->FingerSlot[currentlyReporting].y; + + // + // Perform per-platform x/y adjustments to controller coordinates + // + TchTranslateToDisplayCoordinates( + &hidTouch->InputReport.wXData2, + &hidTouch->InputReport.wYData2, + Props); + + if (Cache->FingerSlot[currentlyReporting].fingerStatus) + { + hidTouch->InputReport.bStatus2 = RANGE_FINGER_STATUS; + } + + (*TouchesReported)++; + } + + // + // Though a single HID report can contain up to two touches, but more + // can be on the screen, ActualCount reflects the total number + // of touches that will be reported when the first report (of possibly + // many) is sent up + // + if (*TouchesReported == 1 || *TouchesReported == 2) + { + hidTouch->InputReport.ActualCount = (UCHAR) TouchesTotal; + } +} + +NTSTATUS +RmiServiceTouchDataInterrupt( + IN RMI4_CONTROLLER_CONTEXT* ControllerContext, + IN SPB_CONTEXT* SpbContext, + IN PHID_INPUT_REPORT HidReport, + IN UCHAR InputMode, + OUT BOOLEAN* PendingTouches + ) +/*++ + +Routine Description: + + Called when a touch interrupt needs service. Because we fill HID reports + with two touches at a time, if more than two touches were read from + hardware, we may need to complete this request from local cached state. + +Arguments: + + ControllerContext - Touch controller context + SpbContext - A pointer to the current SPB context (I2C, etc) + HidReport- Buffer to fill with a hid report if touch data is available + InputMode - Specifies mouse, single-touch, or multi-touch reporting modes + PendingTouches - Notifies caller if there are more touches to report, to + complete reporting the full state of fingers on the screen + +Return Value: + + NTSTATUS indicating whether or not the current hid report buffer was filled + + PendingTouches also indicates whether the caller should expect more than + one request to be completed to indicate the full state of fingers on + the screen +--*/ +{ + RMI4_F11_DATA_REGISTERS data; + NTSTATUS status; + + status = STATUS_SUCCESS; + RtlZeroMemory(&data, sizeof(data)); + NT_ASSERT(PendingTouches != NULL); + *PendingTouches = FALSE; + + // + // If no touches are unreported in our cache, read the next set of touches + // from hardware. + // + if (ControllerContext->TouchesReported == ControllerContext->TouchesTotal) + { + // + // See if new touch data is available + // + status = RmiGetTouchesFromController( + ControllerContext, + SpbContext, + &data + ); + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_VERBOSE, + TRACE_FLAG_SAMPLES, + "No touch data to report - %!STATUS!", + status); + + goto exit; + } + + // + // Process the new touch data by updating our cached state + // + // + RmiUpdateLocalFingerCache( + &data, + &ControllerContext->Cache); + + // + // Prepare to report touches via HID reports + // + ControllerContext->TouchesReported = 0; + ControllerContext->TouchesTotal = + ControllerContext->Cache.FingerDownCount; + + // + // If no touches are present return that no data needed to be reported + // + if (ControllerContext->TouchesTotal == 0) + { + status = STATUS_NO_DATA_DETECTED; + goto exit; + } + } + + RtlZeroMemory(HidReport, sizeof(HID_INPUT_REPORT)); + + // + // Single-finger and HID-mouse input modes not implemented + // + if (MODE_MULTI_TOUCH != InputMode) + { + Trace( + TRACE_LEVEL_VERBOSE, + TRACE_FLAG_SAMPLES, + "Unable to report touches, only multitouch mode is supported"); + + status = STATUS_NOT_IMPLEMENTED; + goto exit; + } + + // + // Fill report with the next (max of two) cached touches + // + RmiFillNextHidReportFromCache( + HidReport, + &ControllerContext->Cache, + &ControllerContext->Props, + &ControllerContext->TouchesReported, + ControllerContext->TouchesTotal + ); + + // + // Update the caller if we still have outstanding touches to report + // + if (ControllerContext->TouchesReported < ControllerContext->TouchesTotal) + { + *PendingTouches = TRUE; + } + else + { + *PendingTouches = FALSE; + } + +exit: + + return status; +} + + +NTSTATUS +TchServiceInterrupts( + IN VOID *ControllerContext, + IN SPB_CONTEXT *SpbContext, + IN PHID_INPUT_REPORT HidReport, + IN UCHAR InputMode, + IN BOOLEAN *ServicingComplete + ) +/*++ + +Routine Description: + + This routine is called in response to an interrupt. The driver will + service chip interrupts, and if data is available to report to HID, + fill the Request object buffer with a HID report. + +Arguments: + + ControllerContext - Touch controller context + SpbContext - A pointer to the current i2c context + HidReport - Pointer to a HID_INPUT_REPORT structure to report to the OS + InputMode - Specifies mouse, single-touch, or multi-touch reporting modes + ServicingComplete - Notifies caller if there are more reports needed to + complete servicing interrupts coming from the hardware. + +Return Value: + + NTSTATUS indicating whether or not the current HidReport has been filled + + ServicingComplete indicates whether or not a new report buffer is required + to complete interrupt processing. +--*/ +{ + NTSTATUS status = STATUS_NO_DATA_DETECTED; + RMI4_CONTROLLER_CONTEXT* controller; + + controller = (RMI4_CONTROLLER_CONTEXT*) ControllerContext; + + NT_ASSERT(ServicingComplete != NULL); + + // + // Grab a waitlock to ensure the ISR executes serially and is + // protected against power state transitions + // + WdfWaitLockAcquire(controller->ControllerLock, NULL); + + // + // Check the interrupt source if no interrupts are pending processing + // + if (controller->InterruptStatus == 0) + { + status = RmiCheckInterrupts( + controller, + SpbContext, + &controller->InterruptStatus); + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INTERRUPT, + "Error servicing interrupts - %!STATUS!", + status); + + *ServicingComplete = FALSE; + goto exit; + } + } + + // + // Driver only services 0D cap button and 2D touch messages currently + // + if (controller->InterruptStatus & + ~(RMI4_INTERRUPT_BIT_0D_CAP_BUTTON | RMI4_INTERRUPT_BIT_2D_TOUCH)) + { + Trace( + TRACE_LEVEL_WARNING, + TRACE_FLAG_INTERRUPT, + "Ignoring following interrupt flags - %!STATUS!", + controller->InterruptStatus & + ~(RMI4_INTERRUPT_BIT_0D_CAP_BUTTON | + RMI4_INTERRUPT_BIT_2D_TOUCH)); + + // + // Mask away flags we don't service + // + controller->InterruptStatus &= + (RMI4_INTERRUPT_BIT_0D_CAP_BUTTON | + RMI4_INTERRUPT_BIT_2D_TOUCH); + } + + // + // RmiServiceXXX routine will change status to STATUS_SUCCESS if there + // is a HID report to process. + // + status = STATUS_UNSUCCESSFUL; + + // + // Service a capacitive button event if indicated by hardware + // + if (controller->InterruptStatus & RMI4_INTERRUPT_BIT_0D_CAP_BUTTON) + { + status = RmiServiceCapacitiveButtonInterrupt( + ControllerContext, + SpbContext, + HidReport); + + controller->InterruptStatus &= ~RMI4_INTERRUPT_BIT_0D_CAP_BUTTON; + + // + // Success indicates the report is ready to be sent, otherwise, + // continue to service interrupts. + // + if (NT_SUCCESS(status)) + { + goto exit; + } + else + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INTERRUPT, + "Error processing cap button event - %!STATUS!", + status); + } + } + + // + // Service a touch data event if indicated by hardware + // + if (controller->InterruptStatus & RMI4_INTERRUPT_BIT_2D_TOUCH) + { + BOOLEAN pendingTouches = FALSE; + + status = RmiServiceTouchDataInterrupt( + ControllerContext, + SpbContext, + HidReport, + InputMode, + &pendingTouches); + + // + // If there are more touches to report, servicing is incomplete + // + if (pendingTouches == FALSE) + { + controller->InterruptStatus &= ~RMI4_INTERRUPT_BIT_2D_TOUCH; + } + + // + // Success indicates the report is ready to be sent, otherwise, + // continue to service interrupts. + // + if (NT_SUCCESS(status)) + { + goto exit; + } + else + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INTERRUPT, + "Error processing touch event - %!STATUS!", + status); + } + } + + // + // Add servicing for additional touch interrupts here + // + +exit: + + // + // Indicate whether or not we're done servicing interrupts + // + if (controller->InterruptStatus == 0) + { + *ServicingComplete = TRUE; + } + else + { + *ServicingComplete = FALSE; + } + + WdfWaitLockRelease(controller->ControllerLock); + + return status; +} diff --git a/input/hiddigi/SynapticsTouch/resolutions.c b/input/hiddigi/SynapticsTouch/resolutions.c new file mode 100644 index 000000000..7cd1ea748 --- /dev/null +++ b/input/hiddigi/SynapticsTouch/resolutions.c @@ -0,0 +1,526 @@ +/*++ + Copyright (c) Microsoft Corporation. All Rights Reserved. + Sample code. Dealpoint ID #843729. + + Module Name: + + resolutions.c + + Abstract: + + This module retrieves platform-specific configuration + parameters from the registry, and translates touch + controller pixel units to display pixel units. + + Environment: + + Kernel Mode + + Revision History: + +--*/ + +#include "rmiinternal.h" +#include "resolutions.tmh" + +// +// Registry values explaining the relationship of the touch +// controller coordinates to the physical LCD, as well as +// any differences between the physical LCD dimensons and +// viewable LCD area, are required. If not provided for whatever +// reason, we will assume everything is 480x800 and perfectly +// aligned. +// + +TOUCH_SCREEN_PROPERTIES gDefaultProperties = +{ + 0, + 0, + 0, + TOUCH_DEFAULT_RESOLUTION_X, + TOUCH_DEFAULT_RESOLUTION_Y, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + TOUCH_DEFAULT_RESOLUTION_X, + TOUCH_DEFAULT_RESOLUTION_Y, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + TOUCH_DEFAULT_RESOLUTION_X, + TOUCH_DEFAULT_RESOLUTION_Y +}; + + +RTL_QUERY_REGISTRY_TABLE gResParamsRegTable[] = +{ + { + NULL, RTL_QUERY_REGISTRY_DIRECT, + L"TouchSwapAxes", + (PVOID) FIELD_OFFSET(TOUCH_SCREEN_PROPERTIES, TouchSwapAxes), + REG_DWORD, + &gDefaultProperties.TouchSwapAxes, + sizeof(ULONG) + }, + { + NULL, RTL_QUERY_REGISTRY_DIRECT, + L"TouchInvertXAxis", + (PVOID) FIELD_OFFSET(TOUCH_SCREEN_PROPERTIES, TouchInvertXAxis), + REG_DWORD, + &gDefaultProperties.TouchInvertXAxis, + sizeof(ULONG) + }, + { + NULL, RTL_QUERY_REGISTRY_DIRECT, + L"TouchInvertYAxis", + (PVOID) FIELD_OFFSET(TOUCH_SCREEN_PROPERTIES, TouchInvertYAxis), + REG_DWORD, + &gDefaultProperties.TouchInvertYAxis, + sizeof(ULONG) + }, + { + NULL, RTL_QUERY_REGISTRY_DIRECT, + L"TouchPhysicalWidth", + (PVOID) FIELD_OFFSET(TOUCH_SCREEN_PROPERTIES, TouchPhysicalWidth), + REG_DWORD, + &gDefaultProperties.TouchPhysicalWidth, + sizeof(ULONG) + }, + { + NULL, RTL_QUERY_REGISTRY_DIRECT, + L"TouchPhysicalHeight", + (PVOID) FIELD_OFFSET(TOUCH_SCREEN_PROPERTIES, TouchPhysicalHeight), + REG_DWORD, + &gDefaultProperties.TouchPhysicalHeight, + sizeof(ULONG) + }, + { + NULL, RTL_QUERY_REGISTRY_DIRECT, + L"TouchPhysicalButtonHeight", + (PVOID) FIELD_OFFSET(TOUCH_SCREEN_PROPERTIES, TouchPhysicalButtonHeight), + REG_DWORD, + &gDefaultProperties.TouchPhysicalButtonHeight, + sizeof(ULONG) + }, + { + NULL, RTL_QUERY_REGISTRY_DIRECT, + L"TouchPillarBoxWidthLeft", + (PVOID) FIELD_OFFSET(TOUCH_SCREEN_PROPERTIES, TouchPillarBoxWidthLeft), + REG_DWORD, + &gDefaultProperties.TouchPillarBoxWidthLeft, + sizeof(ULONG) + }, + { + NULL, RTL_QUERY_REGISTRY_DIRECT, + L"TouchPillarBoxWidthRight", + (PVOID) FIELD_OFFSET(TOUCH_SCREEN_PROPERTIES, TouchPillarBoxWidthRight), + REG_DWORD, + &gDefaultProperties.TouchPillarBoxWidthRight, + sizeof(ULONG) + }, + { + NULL, RTL_QUERY_REGISTRY_DIRECT, + L"TouchLetterBoxHeightTop", + (PVOID) FIELD_OFFSET(TOUCH_SCREEN_PROPERTIES, TouchLetterBoxHeightTop), + REG_DWORD, + &gDefaultProperties.TouchLetterBoxHeightTop, + sizeof(ULONG) + }, + { + NULL, RTL_QUERY_REGISTRY_DIRECT, + L"TouchLetterBoxHeightBottom", + (PVOID) FIELD_OFFSET(TOUCH_SCREEN_PROPERTIES, TouchLetterBoxHeightBottom), + REG_DWORD, + &gDefaultProperties.TouchLetterBoxHeightBottom, + sizeof(ULONG) + }, + { + NULL, RTL_QUERY_REGISTRY_DIRECT, + L"DisplayPhysicalWidth", + (PVOID) FIELD_OFFSET(TOUCH_SCREEN_PROPERTIES, DisplayPhysicalWidth), + REG_DWORD, + &gDefaultProperties.DisplayPhysicalWidth, + sizeof(ULONG) + }, + { + NULL, RTL_QUERY_REGISTRY_DIRECT, + L"DisplayPhysicalHeight", + (PVOID) FIELD_OFFSET(TOUCH_SCREEN_PROPERTIES, DisplayPhysicalHeight), + REG_DWORD, + &gDefaultProperties.DisplayPhysicalHeight, + sizeof(ULONG) + }, + { + NULL, RTL_QUERY_REGISTRY_DIRECT, + L"DisplayViewableWidth", + (PVOID) FIELD_OFFSET(TOUCH_SCREEN_PROPERTIES, DisplayViewableWidth), + REG_DWORD, + &gDefaultProperties.DisplayViewableWidth, + sizeof(ULONG) + }, + { + NULL, RTL_QUERY_REGISTRY_DIRECT, + L"DisplayViewableHeight", + (PVOID) FIELD_OFFSET(TOUCH_SCREEN_PROPERTIES, DisplayViewableHeight), + REG_DWORD, + &gDefaultProperties.DisplayViewableHeight, + sizeof(ULONG) + }, + { + NULL, RTL_QUERY_REGISTRY_DIRECT, + L"DisplayPillarBoxWidthLeft", + (PVOID) FIELD_OFFSET(TOUCH_SCREEN_PROPERTIES, DisplayPillarBoxWidthLeft), + REG_DWORD, + &gDefaultProperties.DisplayPillarBoxWidthLeft, + sizeof(ULONG) + }, + { + NULL, RTL_QUERY_REGISTRY_DIRECT, + L"DisplayPillarBoxWidthRight", + (PVOID) FIELD_OFFSET(TOUCH_SCREEN_PROPERTIES, DisplayPillarBoxWidthRight), + REG_DWORD, + &gDefaultProperties.DisplayPillarBoxWidthRight, + sizeof(ULONG) + }, + { + NULL, RTL_QUERY_REGISTRY_DIRECT, + L"DisplayLetterBoxHeightTop", + (PVOID) FIELD_OFFSET(TOUCH_SCREEN_PROPERTIES, DisplayLetterBoxHeightTop), + REG_DWORD, + &gDefaultProperties.DisplayLetterBoxHeightTop, + sizeof(ULONG) + }, + { + NULL, RTL_QUERY_REGISTRY_DIRECT, + L"DisplayLetterBoxHeightBottom", + (PVOID) FIELD_OFFSET(TOUCH_SCREEN_PROPERTIES, DisplayLetterBoxHeightBottom), + REG_DWORD, + &gDefaultProperties.DisplayLetterBoxHeightBottom, + sizeof(ULONG) + }, + // + // List Terminator - set to NULL to indicate end of table + // + { + NULL, 0, + NULL, + 0, + REG_DWORD, + NULL, + 0 + } +}; + +static const ULONG gcbRegistryTable = sizeof(gResParamsRegTable); +static const ULONG gcRegistryTable = + sizeof(gResParamsRegTable) / sizeof(gResParamsRegTable[0]); + + +VOID +TchTranslateToDisplayCoordinates( + IN PUSHORT PX, + IN PUSHORT PY, + IN PTOUCH_SCREEN_PROPERTIES Props + ) +/*++ + + Routine Description: + + This routine performs translations on touch coordinates + to ensure points reported to the OS match pixels on the + display. + + Arguments: + + X - pointer to the pre-processed X coordinate + Y - pointer the pre-processed Y coordinate + Props - pointer to screen information + + Return Value: + + None. The X/Y values will be modified by this function. + +--*/ +{ + ULONG X; + ULONG Y; + + // + // Avoid overflow + // + X = (ULONG) *PX; + Y = (ULONG) *PY; + + // + // Swap the axes reported by the touch controller if requested + // + if (Props->TouchSwapAxes) + { + ULONG temp = Y; + Y = X; + X = temp; + } + + // + // Invert the coordinates as requested + // + if (Props->TouchInvertXAxis) + { + if (X >= Props->TouchPhysicalWidth) + { + X = Props->TouchPhysicalWidth - 1u; + } + + X = Props->TouchPhysicalWidth - X - 1u; + } + if (Props->TouchInvertYAxis) + { + if (Y >= Props->TouchPhysicalHeight) + { + Y = Props->TouchPhysicalHeight - 1u; + } + + Y = Props->TouchPhysicalHeight - Y - 1u; + } + + // + // Handle touch clipping boundaries so touch matches + // the physical display + // + if (X <= Props->TouchPillarBoxWidthLeft) + { + X = 0; + } + else + { + X -= Props->TouchPillarBoxWidthLeft; + } + if (Y <= Props->TouchLetterBoxHeightTop) + { + Y = 0; + } + else + { + Y -= Props->TouchLetterBoxHeightTop; + } + if (X >= Props->TouchAdjustedWidth) + { + X = Props->TouchAdjustedWidth - 1u; + } + if (Y >= Props->TouchAdjustedHeight) + { + Y = Props->TouchAdjustedHeight - 1u; + } + + // + // Scale the raw touch pixel units into physical display pixels, + // leaving off the capacitive button region. + // + X = X * Props->DisplayPhysicalWidth / Props->TouchAdjustedWidth; + Y = Y * Props->DisplayPhysicalHeight / + (Props->TouchAdjustedHeight - Props->TouchPhysicalButtonHeight); + + // + // If the display is additionally being letterboxed or pillarboxed, make + // further adjustments to the touch coordinates. + // + if (X <= Props->DisplayPillarBoxWidthLeft) + { + X = 0; + } + else + { + X -= Props->DisplayPillarBoxWidthLeft; + } + if (Y <= Props->DisplayLetterBoxHeightTop) + { + Y = 0; + } + else + { + Y -= Props->DisplayLetterBoxHeightTop; + } + if (X >= Props->DisplayAdjustedWidth) + { + X = Props->DisplayAdjustedWidth - 1u; + } + if (Y >= Props->DisplayAdjustedHeight) + { + Y = Props->DisplayAdjustedHeight - 1u; + } + + X = X * Props->DisplayViewableWidth / Props->DisplayAdjustedWidth; + Y = Y * Props->DisplayViewableHeight / + (Props->DisplayAdjustedHeight - Props->DisplayAdjustedButtonHeight); + + Trace( + TRACE_LEVEL_INFORMATION, + TRACE_FLAG_REPORTING, + "In (%d,%d), Out (%d,%d)", + *PX, *PY, X, Y); + + *PX = (USHORT) X; + *PY = (USHORT) Y; +} + +VOID +TchGetScreenProperties( + IN PTOUCH_SCREEN_PROPERTIES Props + ) +/*++ + + Routine Description: + + This routine retrieves coordinate translation settings + from the registry. + + Arguments: + + Props - receives the Props + + Return Value: + + None. On failure, defaults are returned. + +--*/ +{ + ULONG i; + PRTL_QUERY_REGISTRY_TABLE regTable; + NTSTATUS status; + + regTable = NULL; + + // + // Table passed to RtlQueryRegistryValues must be allocated + // from NonPagedPool + // + regTable = ExAllocatePoolWithTag( + NonPagedPool, + gcbRegistryTable, + TOUCH_POOL_TAG); + + RtlCopyMemory( + regTable, + gResParamsRegTable, + gcbRegistryTable); + + // + // Update offset values with base pointer + // + for (i=0; i < gcRegistryTable-1; i++) + { + regTable[i].EntryContext = (PVOID) ( + ((SIZE_T) regTable[i].EntryContext) + + ((ULONG_PTR) Props)); + } + + // + // Start with default values + // + RtlCopyMemory( + Props, + &gDefaultProperties, + sizeof(TOUCH_SCREEN_PROPERTIES)); + + // + // Populate device context with registry overrides (or defaults) + // + status = RtlQueryRegistryValues( + RTL_REGISTRY_ABSOLUTE, + TOUCH_SCREEN_PROPERTIES_REG_KEY, + regTable, + NULL, + NULL); + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_WARNING, + TRACE_FLAG_REGISTRY, + "Error retrieving registry configuration - %!STATUS!", + status); + } + + // + // Sanity check values provided from the registry + // + + if (Props->TouchPillarBoxWidthLeft + + Props->TouchPillarBoxWidthRight >= + Props->TouchPhysicalWidth) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_REGISTRY, + "Invalid pillar box widths provided (%d,%d for %d)", + Props->TouchPillarBoxWidthLeft, + Props->TouchPillarBoxWidthRight, + Props->TouchPhysicalWidth); + + Props->TouchPillarBoxWidthLeft = + gDefaultProperties.TouchPillarBoxWidthLeft; + Props->TouchPillarBoxWidthRight = + gDefaultProperties.TouchPillarBoxWidthRight; + + } + + if (Props->TouchLetterBoxHeightTop + + Props->TouchLetterBoxHeightBottom >= + Props->TouchPhysicalHeight) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_REGISTRY, + "Invalid letter box heights provided (%d,%d for %d)", + Props->TouchLetterBoxHeightTop, + Props->TouchLetterBoxHeightBottom, + Props->TouchPhysicalHeight); + + Props->TouchLetterBoxHeightTop = + gDefaultProperties.TouchLetterBoxHeightTop; + Props->TouchLetterBoxHeightBottom = + gDefaultProperties.TouchLetterBoxHeightBottom; + } + + // + // Calculate a few parameters for later use + // + Props->TouchAdjustedWidth = + Props->TouchPhysicalWidth - + Props->TouchPillarBoxWidthLeft - + Props->TouchPillarBoxWidthRight; + + Props->TouchAdjustedHeight = + Props->TouchPhysicalHeight - + Props->TouchLetterBoxHeightTop - + Props->TouchLetterBoxHeightBottom; + + Props->DisplayAdjustedWidth = + Props->DisplayPhysicalWidth - + Props->DisplayPillarBoxWidthLeft - + Props->DisplayPillarBoxWidthRight; + + Props->DisplayAdjustedButtonHeight = + Props->TouchPhysicalButtonHeight * + Props->DisplayPhysicalHeight / + (Props->TouchAdjustedHeight - Props->TouchPhysicalButtonHeight); + + Props->DisplayAdjustedHeight = + Props->DisplayPhysicalHeight - + Props->DisplayLetterBoxHeightTop - + Props->DisplayLetterBoxHeightBottom + + Props->DisplayAdjustedButtonHeight; + + if (regTable != NULL) + { + ExFreePoolWithTag(regTable, TOUCH_POOL_TAG); + } +} \ No newline at end of file diff --git a/input/hiddigi/SynapticsTouch/resolutions.h b/input/hiddigi/SynapticsTouch/resolutions.h new file mode 100644 index 000000000..e6de12c47 --- /dev/null +++ b/input/hiddigi/SynapticsTouch/resolutions.h @@ -0,0 +1,66 @@ +/*++ + Copyright (c) Microsoft Corporation. All Rights Reserved. + Sample code. Dealpoint ID #843729. + + Module Name: + + resolutions.h + + Abstract: + + Contains resolution translation defines and types + + Environment: + + Kernel mode + + Revision History: + +--*/ + +#pragma once + +#define TOUCH_SCREEN_PROPERTIES_REG_KEY L"\\Registry\\Machine\\System\\TOUCH\\SCREENPROPERTIES" +#define TOUCH_DEFAULT_RESOLUTION_X 480 +#define TOUCH_DEFAULT_RESOLUTION_Y 800 +#define TOUCH_DEVICE_RESOLUTION_X 816 +#define TOUCH_DEVICE_RESOLUTION_Y 1394 + +typedef struct _TOUCH_SCREEN_PROPERTIES +{ + ULONG TouchSwapAxes; + ULONG TouchInvertXAxis; + ULONG TouchInvertYAxis; + ULONG TouchPhysicalWidth; + ULONG TouchPhysicalHeight; + ULONG TouchPhysicalButtonHeight; + ULONG TouchPillarBoxWidthLeft; + ULONG TouchPillarBoxWidthRight; + ULONG TouchLetterBoxHeightTop; + ULONG TouchLetterBoxHeightBottom; + ULONG TouchAdjustedWidth; + ULONG TouchAdjustedHeight; + ULONG DisplayPhysicalWidth; + ULONG DisplayPhysicalHeight; + ULONG DisplayAdjustedButtonHeight; + ULONG DisplayPillarBoxWidthLeft; + ULONG DisplayPillarBoxWidthRight; + ULONG DisplayLetterBoxHeightTop; + ULONG DisplayLetterBoxHeightBottom; + ULONG DisplayAdjustedWidth; + ULONG DisplayAdjustedHeight; + ULONG DisplayViewableWidth; + ULONG DisplayViewableHeight; +} TOUCH_SCREEN_PROPERTIES, *PTOUCH_SCREEN_PROPERTIES; + +VOID +TchGetScreenProperties( + IN PTOUCH_SCREEN_PROPERTIES Props + ); + +VOID +TchTranslateToDisplayCoordinates( + IN PUSHORT X, + IN PUSHORT Y, + IN PTOUCH_SCREEN_PROPERTIES Props + ); diff --git a/input/hiddigi/SynapticsTouch/rmiinternal.h b/input/hiddigi/SynapticsTouch/rmiinternal.h new file mode 100644 index 000000000..ae767e54d --- /dev/null +++ b/input/hiddigi/SynapticsTouch/rmiinternal.h @@ -0,0 +1,579 @@ +/*++ + Copyright (c) Microsoft Corporation. All Rights Reserved. + Sample code. Dealpoint ID #843729. + + Module Name: + + rmiinternal.h + + Abstract: + + Contains common types and defintions used internally + by the multi touch screen driver. + + Environment: + + Kernel mode + + Revision History: + +--*/ + +#pragma once + +#include +#include +#include "controller.h" +#include "resolutions.h" + + +// Ignore warning C4152: nonstandard extension, function/data pointer conversion in expression +#pragma warning (disable : 4152) + +// Ignore warning C4201: nonstandard extension used : nameless struct/union +#pragma warning (disable : 4201) + +// Ignore warning C4201: nonstandard extension used : bit field types other than in +#pragma warning (disable : 4214) + +// Ignore warning C4324: 'xxx' : structure was padded due to __declspec(align()) +#pragma warning (disable : 4324) + + +// +// Defines from Synaptics RMI4 Data Sheet, please refer to +// the spec for details about the fields and values. +// + +#define RMI4_FIRST_FUNCTION_ADDRESS 0xE9 +#define RMI4_PAGE_SELECT_ADDRESS 0xFF + +#define RMI4_F01_RMI_DEVICE_CONTROL 0x01 +#define RMI4_F11_2D_TOUCHPAD_SENSOR 0x11 +#define RMI4_F1A_0D_CAP_BUTTON_SENSOR 0x1A +#define RMI4_F34_FLASH_MEMORY_MANAGEMENT 0x34 +#define RMI4_F54_TEST_REPORTING 0x54 + +#define RMI4_MAX_FUNCTIONS 10 +#define RMI4_MAX_TOUCHES 10 + +typedef struct _RMI4_FUNCTION_DESCRIPTOR +{ + BYTE QueryBase; + BYTE CommandBase; + BYTE ControlBase; + BYTE DataBase; + union + { + BYTE All; + struct + { + BYTE IrqCount : 3; + BYTE Reserved0 : 2; + BYTE FuncVer : 2; + BYTE Reserved1 : 1; + }; + } VersionIrq; + BYTE Number; +} RMI4_FUNCTION_DESCRIPTOR; + +// +// Function $01 - RMI Device Control +// + +typedef struct _RMI4_F01_QUERY_REGISTERS +{ + BYTE ManufacturerID; + union + { + BYTE All; + struct + { + BYTE CustomMap : 1; + BYTE NonCompliant : 1; + BYTE Reserved0 : 1; + BYTE HasSensorID : 1; + BYTE Reserved1 : 1; + BYTE HasAdjDoze : 1; + BYTE HasAdJDozeHold : 1; + BYTE Reserved2 : 1; + }; + } ProductProperties; + BYTE ProductInfo0; + BYTE ProductInfo1; + BYTE Date0; + BYTE Date1; + BYTE WaferLotId0Lo; + BYTE WaferLotId0Hi; + BYTE WaferLotId1Lo; + BYTE WaferLotId1Hi; + BYTE WaferLotId2Lo; + BYTE ProductID1; + BYTE ProductID2; + BYTE ProductID3; + BYTE ProductID4; + BYTE ProductID5; + BYTE ProductID6; + BYTE ProductID7; + BYTE ProductID8; + BYTE ProductID9; + BYTE ProductID10; + BYTE Reserved21; + BYTE SendorID; + BYTE Reserved23; + BYTE Reserved24; + BYTE Reserved25; + BYTE Reserved26; + BYTE Reserved27; + BYTE Reserved28; + BYTE Reserved29; + BYTE Reserved30; + BYTE Reserved31; +} RMI4_F01_QUERY_REGISTERS; + +typedef struct _RMI4_F01_CTRL_REGISTERS +{ + union + { + BYTE All; + struct + { + BYTE SleepMode :2; + BYTE NoSleep :1; + BYTE Reserved0 :3; + BYTE ReportRate :1; + BYTE Configured :1; + }; + } DeviceControl; + BYTE InterruptEnable; + BYTE DozeInterval; + BYTE DozeThreshold; + BYTE DozeHoldoff; +} RMI4_F01_CTRL_REGISTERS; + +#define RMI4_F11_DEVICE_CONTROL_SLEEP_MODE_OPERATING 0 +#define RMI4_F11_DEVICE_CONTROL_SLEEP_MODE_SLEEPING 1 + +// +// Logical structure for getting registry config settings +// +typedef struct _RM4_F01_CTRL_REGISTERS_LOGICAL +{ + UINT32 SleepMode; + UINT32 NoSleep; + UINT32 ReportRate; + UINT32 Configured; + UINT32 InterruptEnable; + UINT32 DozeInterval; + UINT32 DozeThreshold; + UINT32 DozeHoldoff; +} RMI4_F01_CTRL_REGISTERS_LOGICAL; + +#define RMI4_MILLISECONDS_TO_TENTH_MILLISECONDS(n) n/10 +#define RMI4_SECONDS_TO_HALF_SECONDS(n) 2*n + +typedef struct _RMI4_F01_DATA_REGISTERS +{ + union { + BYTE All; + struct + { + BYTE Status : 4; + BYTE Reserved0 : 2; + BYTE FlashProg : 1; + BYTE Unconfigured : 1; + }; + } DeviceStatus; + BYTE InterruptStatus[1]; +} RMI4_F01_DATA_REGISTERS; + +#define RMI4_INTERRUPT_BIT_2D_TOUCH 0x04 +#define RMI4_INTERRUPT_BIT_0D_CAP_BUTTON 0x20 + +#define RMI4_F01_DATA_STATUS_NO_ERROR 0 +#define RMI4_F01_DATA_STATUS_RESET_OCCURRED 1 +#define RMI4_F01_DATA_STATUS_INVALID_CONFIG 2 +#define RMI4_F01_DATA_STATUS_DEVICE_FAILURE 3 +#define RMI4_F01_DATA_STATUS_CONFIG_CRC_FAILURE 4 +#define RMI4_F01_DATA_STATUS_FW_CRC_FAILURE 5 +#define RMI4_F01_DATA_STATUS_CRC_IN_PROGRESS 6 + +typedef struct _RMI4_F01_COMMAND_REGISTERS +{ + BYTE Reset; +} RMI4_F01_COMMAND_REGISTERS; + +// +// Function $11 - 2-D Touch Sensor +// + +typedef struct _RMI4_F11_QUERY_REGISTERS +{ + struct + { + BYTE NumberOfSensors : 3; + BYTE Reserved0 : 1; + BYTE HasQuery11 : 1; + BYTE Reserved1 : 3; + }; + struct + { + BYTE NumberOfFingers : 3; + BYTE HasRelative : 1; + BYTE HasAbsolute : 1; + BYTE HasGestures : 1; + BYTE HasSensitivity : 1; + BYTE Configurable : 1; + }; + BYTE NumXElectrodes; + BYTE NumYElectrodes; + BYTE MaxElectrodes; + struct + { + BYTE AbsDataSize : 2; + BYTE HasAnchoredFin : 1; + BYTE HasAdjHyst : 1; + BYTE HasDribble : 1; + BYTE Reserved2 : 3; + }; + struct + { + BYTE HasZTuning : 1; + BYTE HasAlgoSelect : 1; + BYTE HasWTuning : 1; + BYTE HasPitchInfo : 1; + BYTE HasFingerSize : 1; + BYTE HasObjSensAdj : 1; + BYTE HasXYClip : 1; + BYTE HasDrummingAdj : 1; + }; +} RMI4_F11_QUERY_REGISTERS; + +typedef struct _RMI4_F11_CTRL_REGISTERS +{ + struct + { + BYTE ReportingMode : 3; + BYTE AbsPosFilt : 1; + BYTE RelPosFilt : 1; + BYTE RelBallistics : 1; + BYTE Dribble : 1; + BYTE Reserved0 : 1; + }; + struct + { + BYTE PalmDetectThreshold : 4; + BYTE MotionSensitivity : 2; + BYTE ManTrackEn : 1; + BYTE ManTrackedFinger : 1; + }; + BYTE DeltaXPosThreshold; + BYTE DeltaYPosThreshold; + BYTE Velocity; + BYTE Acceleration; + BYTE SensorMaxXPosLo; + struct + { + BYTE SensorMaxXPosHi : 4; + BYTE Reserved1 : 4; + }; + BYTE SensorMaxYPosLo; + struct + { + BYTE SensorMaxYPosHi : 4; + BYTE Reserved2 : 4; + }; + // + // Note gap in registers on 3200 + // + BYTE ZTouchThreshold; + BYTE ZHysteresis; + BYTE SmallZThreshold; + BYTE SmallZScaleFactor[2]; + BYTE LargeZScaleFactor[2]; + BYTE AlgorithmSelection; + BYTE WxScaleFactor; + BYTE WxOffset; + BYTE WyScaleFactor; + BYTE WyOffset; + BYTE XPitch[2]; + BYTE YPitch[2]; + BYTE FingerWidthX[2]; + BYTE FingerWidthY[2]; + BYTE ReportMeasuredSize; + BYTE SegmentationSensitivity; + BYTE XClipLo; + BYTE XClipHi; + BYTE YClipLo; + BYTE YClipHi; + BYTE MinFingerSeparation; + BYTE MaxFingerMovement; +} RMI4_F11_CTRL_REGISTERS; + +// +// Logical structure for getting registry config settings +// +typedef struct _RMI4_F11_CTRL_REGISTERS_LOGICAL +{ + UINT32 ReportingMode; + UINT32 AbsPosFilt; + UINT32 RelPosFilt; + UINT32 RelBallistics; + UINT32 Dribble; + UINT32 PalmDetectThreshold; + UINT32 MotionSensitivity; + UINT32 ManTrackEn; + UINT32 ManTrackedFinger; + UINT32 DeltaXPosThreshold; + UINT32 DeltaYPosThreshold; + UINT32 Velocity; + UINT32 Acceleration; + UINT32 SensorMaxXPos; + UINT32 SensorMaxYPos; + UINT32 ZTouchThreshold; + UINT32 ZHysteresis; + UINT32 SmallZThreshold; + UINT32 SmallZScaleFactor; + UINT32 LargeZScaleFactor; + UINT32 AlgorithmSelection; + UINT32 WxScaleFactor; + UINT32 WxOffset; + UINT32 WyScaleFactor; + UINT32 WyOffset; + UINT32 XPitch; + UINT32 YPitch; + UINT32 FingerWidthX; + UINT32 FingerWidthY; + UINT32 ReportMeasuredSize; + UINT32 SegmentationSensitivity; + UINT32 XClipLo; + UINT32 XClipHi; + UINT32 YClipLo; + UINT32 YClipHi; + UINT32 MinFingerSeparation; + UINT32 MaxFingerMovement; +} RMI4_F11_CTRL_REGISTERS_LOGICAL; + +typedef struct _RMI4_F11_DATA_POSITION +{ + BYTE XPosHi; + BYTE YPosHi; + struct + { + BYTE XPosLo : 4; + BYTE YPosLo : 4; + }; + struct + { + BYTE XWidth : 4; + BYTE YWidth : 4; + }; + BYTE ZAmplitude; +} RMI4_F11_DATA_POSITION; + +typedef struct _RMI4_F11_DATA_REGISTERS_STATUS_BLOCK +{ + struct + { + BYTE FingerState0 : 2; + BYTE FingerState1 : 2; + BYTE FingerState2 : 2; + BYTE FingerState3 : 2; + }; + struct + { + BYTE FingerState4 : 2; + BYTE FingerState5 : 2; + BYTE FingerState6 : 2; + BYTE FingerState7 : 2; + }; + struct + { + BYTE FingerState8 : 2; + BYTE FingerState9 : 2; + BYTE Reserved0 : 4; + }; +} RMI4_F11_DATA_REGISTERS_STATUS_BLOCK; + +typedef struct _RMI4_F11_DATA_REGISTERS +{ + RMI4_F11_DATA_REGISTERS_STATUS_BLOCK Status; + RMI4_F11_DATA_POSITION Finger[RMI4_MAX_TOUCHES]; +} RMI4_F11_DATA_REGISTERS; + +#define RMI4_FINGER_STATE_NOT_PRESENT 0 +#define RMI4_FINGER_STATE_PRESENT_WITH_ACCURATE_POS 1 +#define RMI4_FINGER_STATE_PRESENT_WITH_INACCURATE_POS 2 +#define RMI4_FINGER_STATE_RESERVED 3 + +// +// Function $1A - 0-D Capacitive Button Sensors +// + +typedef struct _RMI4_F1A_QUERY_REGISTERS +{ + struct + { + BYTE MaxButtonCount : 2; + BYTE Reserved0 : 5; + }; + struct + { + BYTE HasGenControl : 1; + BYTE HasIntEnable : 1; + BYTE HasMultiButSel : 1; + BYTE HasTxRxMapping : 1; + BYTE HasPerButThresh : 1; + BYTE HasRelThresh : 1; + BYTE HasStrongButHyst: 1; + BYTE HasFiltStrength : 1; + }; +} RMI4_F1A_QUERY_REGISTERS; + +typedef struct _RMI4_F1A_CTRL_REGISTERS +{ + struct + { + BYTE MultiButtonRpt : 2; + BYTE FilterMode : 2; + BYTE Reserved0 : 4; + }; + struct + { + BYTE IntEnBtn0 : 1; + BYTE IntEnBtn1 : 1; + BYTE IntEnBtn2 : 1; + BYTE IntEnBtn3 : 1; + BYTE Reserved1 : 4; + }; + struct + { + BYTE MultiBtn0 : 1; + BYTE MultiBtn1 : 1; + BYTE MultiBtn2 : 1; + BYTE MultiBtn3 : 1; + BYTE Reserved2 : 4; + }; + BYTE PhysicalTx0; + BYTE PhysicalRx0; + BYTE PhysicalTx1; + BYTE PhysicalRx1; + BYTE PhysicalTx2; + BYTE PhysicalRx2; + BYTE PhysicalTx3; + BYTE PhysicalRx3; + BYTE Threshold0; + BYTE Threshold1; + BYTE Threshold2; + BYTE Threshold3; + BYTE ReleaseThreshold; + BYTE StrongButtonHyst; + BYTE FilterStrength; +} RMI4_F1A_CTRL_REGISTERS; + +typedef struct _RMI4_F1A_DATA_REGISTERS +{ + struct + { + BYTE Button0 : 1; + BYTE Button1 : 1; + BYTE Button2 : 1; + BYTE Button3 : 1; + BYTE Reserved0 : 4; + }; +} RMI4_F1A_DATA_REGISTERS; + +// +// Function $XX - Add new support functions here +// + + +// +// Driver structures +// + +typedef struct _RMI4_CONFIGURATION +{ + RMI4_F01_CTRL_REGISTERS_LOGICAL DeviceSettings; + RMI4_F11_CTRL_REGISTERS_LOGICAL TouchSettings; + UINT32 PepRemovesVoltageInD3; +} RMI4_CONFIGURATION; + +typedef struct _RMI4_FINGER_INFO +{ + int x; + int y; + UCHAR fingerStatus; +} RMI4_FINGER_INFO; + +typedef struct _RMI4_FINGER_CACHE +{ + RMI4_FINGER_INFO FingerSlot[RMI4_MAX_TOUCHES]; + UINT32 FingerSlotValid; + UINT32 FingerSlotDirty; + int FingerDownOrder[RMI4_MAX_TOUCHES]; + int FingerDownCount; +} RMI4_FINGER_CACHE; + +typedef struct _RMI4_CONTROLLER_CONTEXT +{ + WDFDEVICE FxDevice; + WDFWAITLOCK ControllerLock; + + // + // Controller state + // + int FunctionCount; + RMI4_FUNCTION_DESCRIPTOR Descriptors[RMI4_MAX_FUNCTIONS]; + int FunctionOnPage[RMI4_MAX_FUNCTIONS]; + int CurrentPage; + + ULONG InterruptStatus; + BOOLEAN HasButtons; + BOOLEAN ResetOccurred; + BOOLEAN InvalidConfiguration; + BOOLEAN DeviceFailure; + BOOLEAN UnknownStatus; + BYTE UnknownStatusMessage; + RMI4_F01_QUERY_REGISTERS F01QueryRegisters; + + // + // Power state + // + DEVICE_POWER_STATE DevicePowerState; + + // + // Register configuration programmed to chip + // + TOUCH_SCREEN_PROPERTIES Props; + RMI4_CONFIGURATION Config; + + // + // Current touch state + // + int TouchesReported; + int TouchesTotal; + RMI4_FINGER_CACHE Cache; + +} RMI4_CONTROLLER_CONTEXT; + +NTSTATUS +RmiCheckInterrupts( + IN RMI4_CONTROLLER_CONTEXT *ControllerContext, + IN SPB_CONTEXT *SpbContext, + IN ULONG* InterruptStatus + ); + +int +RmiGetFunctionIndex( + IN RMI4_FUNCTION_DESCRIPTOR* FunctionDescriptors, + IN int FunctionCount, + IN int FunctionDesired + ); + +NTSTATUS +RmiChangePage( + IN RMI4_CONTROLLER_CONTEXT* ControllerContext, + IN SPB_CONTEXT* SpbContext, + IN int DesiredPage + ); diff --git a/input/hiddigi/SynapticsTouch/spb.c b/input/hiddigi/SynapticsTouch/spb.c new file mode 100644 index 000000000..4ec7c179f --- /dev/null +++ b/input/hiddigi/SynapticsTouch/spb.c @@ -0,0 +1,512 @@ +/*++ + Copyright (c) Microsoft Corporation. All Rights Reserved. + Sample code. Dealpoint ID #843729. + + Module Name: + + spb.c + + Abstract: + + Contains all I2C-specific functionality + + Environment: + + Kernel mode + + Revision History: + +--*/ + +#include "internal.h" +#include "controller.h" +#include "spb.tmh" + +NTSTATUS +SpbDoWriteDataSynchronously( + IN SPB_CONTEXT *SpbContext, + IN UCHAR Address, + IN PVOID Data, + IN ULONG Length + ) +/*++ + + Routine Description: + + This helper routine abstracts creating and sending an I/O + request (I2C Write) to the Spb I/O target. + + Arguments: + + SpbContext - Pointer to the current device context + Address - The I2C register address to write to + Data - A buffer to receive the data at at the above address + Length - The amount of data to be read from the above address + + Return Value: + + NTSTATUS Status indicating success or failure + +--*/ +{ + PUCHAR buffer; + ULONG length; + WDFMEMORY memory; + WDF_MEMORY_DESCRIPTOR memoryDescriptor; + NTSTATUS status; + + // + // The address pointer and data buffer must be combined + // into one contiguous buffer representing the write transaction. + // + length = Length + 1; + memory = NULL; + + if (length > DEFAULT_SPB_BUFFER_SIZE) + { + status = WdfMemoryCreate( + WDF_NO_OBJECT_ATTRIBUTES, + NonPagedPool, + TOUCH_POOL_TAG, + length, + &memory, + &buffer); + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_SPB, + "Error allocating memory for Spb write - %!STATUS!", + status); + goto exit; + } + + WDF_MEMORY_DESCRIPTOR_INIT_HANDLE( + &memoryDescriptor, + memory, + NULL); + } + else + { + buffer = (PUCHAR) WdfMemoryGetBuffer(SpbContext->WriteMemory, NULL); + + WDF_MEMORY_DESCRIPTOR_INIT_BUFFER( + &memoryDescriptor, + (PVOID) buffer, + length); + } + + // + // Transaction starts by specifying the address bytes + // + RtlCopyMemory(buffer, &Address, sizeof(Address)); + + // + // Address is followed by the data payload + // + RtlCopyMemory((buffer+sizeof(Address)), Data, length-sizeof(Address)); + + status = WdfIoTargetSendWriteSynchronously( + SpbContext->SpbIoTarget, + NULL, + &memoryDescriptor, + NULL, + NULL, + NULL); + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_SPB, + "Error writing to Spb - %!STATUS!", + status); + goto exit; + } + +exit: + + if (NULL != memory) + { + WdfObjectDelete(memory); + } + + return status; +} + +NTSTATUS +SpbWriteDataSynchronously( + IN SPB_CONTEXT *SpbContext, + IN UCHAR Address, + IN PVOID Data, + IN ULONG Length + ) +/*++ + + Routine Description: + + This routine abstracts creating and sending an I/O + request (I2C Write) to the Spb I/O target and utilizes + a helper routine to do work inside of locked code. + + Arguments: + + SpbContext - Pointer to the current device context + Address - The I2C register address to write to + Data - A buffer to receive the data at at the above address + Length - The amount of data to be read from the above address + + Return Value: + + NTSTATUS Status indicating success or failure + +--*/ +{ + NTSTATUS status; + + WdfWaitLockAcquire(SpbContext->SpbLock, NULL); + + status = SpbDoWriteDataSynchronously( + SpbContext, + Address, + Data, + Length); + + WdfWaitLockRelease(SpbContext->SpbLock); + + return status; +} + +NTSTATUS +SpbReadDataSynchronously( + _In_ SPB_CONTEXT *SpbContext, + _In_ UCHAR Address, + _In_reads_bytes_(Length) PVOID Data, + _In_ ULONG Length + ) +/*++ + + Routine Description: + + This helper routine abstracts creating and sending an I/O + request (I2C Read) to the Spb I/O target. + + Arguments: + + SpbContext - Pointer to the current device context + Address - The I2C register address to read from + Data - A buffer to receive the data at at the above address + Length - The amount of data to be read from the above address + + Return Value: + + NTSTATUS Status indicating success or failure + +--*/ +{ + PUCHAR buffer; + WDFMEMORY memory; + WDF_MEMORY_DESCRIPTOR memoryDescriptor; + NTSTATUS status; + ULONG_PTR bytesRead; + + WdfWaitLockAcquire(SpbContext->SpbLock, NULL); + + memory = NULL; + status = STATUS_INVALID_PARAMETER; + bytesRead = 0; + + // + // Read transactions start by writing an address pointer + // + status = SpbDoWriteDataSynchronously( + SpbContext, + Address, + NULL, + 0); + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_SPB, + "Error setting address pointer for Spb read - %!STATUS!", + status); + goto exit; + } + + if (Length > DEFAULT_SPB_BUFFER_SIZE) + { + status = WdfMemoryCreate( + WDF_NO_OBJECT_ATTRIBUTES, + NonPagedPool, + TOUCH_POOL_TAG, + Length, + &memory, + &buffer); + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_SPB, + "Error allocating memory for Spb read - %!STATUS!", + status); + goto exit; + } + + WDF_MEMORY_DESCRIPTOR_INIT_HANDLE( + &memoryDescriptor, + memory, + NULL); + } + else + { + buffer = (PUCHAR) WdfMemoryGetBuffer(SpbContext->ReadMemory, NULL); + + WDF_MEMORY_DESCRIPTOR_INIT_BUFFER( + &memoryDescriptor, + (PVOID) buffer, + Length); + } + + + status = WdfIoTargetSendReadSynchronously( + SpbContext->SpbIoTarget, + NULL, + &memoryDescriptor, + NULL, + NULL, + &bytesRead); + + if (!NT_SUCCESS(status) || + bytesRead != Length) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_SPB, + "Error reading from Spb - %!STATUS!", + status); + goto exit; + } + + // + // Copy back to the caller's buffer + // + RtlCopyMemory(Data, buffer, Length); + +exit: + if (NULL != memory) + { + WdfObjectDelete(memory); + } + + WdfWaitLockRelease(SpbContext->SpbLock); + + return status; +} + +VOID +SpbTargetDeinitialize( + IN WDFDEVICE FxDevice, + IN SPB_CONTEXT *SpbContext + ) +/*++ + + Routine Description: + + This helper routine is used to free any members added to the SPB_CONTEXT, + note the SPB I/O target is parented to the device and will be + closed and free'd when the device is removed. + + Arguments: + + FxDevice - Handle to the framework device object + SpbContext - Pointer to the current device context + + Return Value: + + NTSTATUS Status indicating success or failure + +--*/ +{ + UNREFERENCED_PARAMETER(FxDevice); + UNREFERENCED_PARAMETER(SpbContext); + + // + // Free any SPB_CONTEXT allocations here + // + if (SpbContext->SpbLock != NULL) + { + WdfObjectDelete(SpbContext->SpbLock); + } + + if (SpbContext->ReadMemory != NULL) + { + WdfObjectDelete(SpbContext->ReadMemory); + } + + if (SpbContext->WriteMemory != NULL) + { + WdfObjectDelete(SpbContext->WriteMemory); + } +} + +NTSTATUS +SpbTargetInitialize( + IN WDFDEVICE FxDevice, + IN SPB_CONTEXT *SpbContext + ) +/*++ + + Routine Description: + + This helper routine opens the Spb I/O target and + initializes a request object used for the lifetime + of communication between this driver and Spb. + + Arguments: + + FxDevice - Handle to the framework device object + SpbContext - Pointer to the current device context + + Return Value: + + NTSTATUS Status indicating success or failure + +--*/ +{ + WDF_OBJECT_ATTRIBUTES objectAttributes; + WDF_IO_TARGET_OPEN_PARAMS openParams; + UNICODE_STRING spbDeviceName; + WCHAR spbDeviceNameBuffer[RESOURCE_HUB_PATH_SIZE]; + NTSTATUS status; + + WDF_OBJECT_ATTRIBUTES_INIT(&objectAttributes); + objectAttributes.ParentObject = FxDevice; + + status = WdfIoTargetCreate( + FxDevice, + &objectAttributes, + &SpbContext->SpbIoTarget); + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_SPB, + "Error creating IoTarget object - %!STATUS!", + status); + + WdfObjectDelete(SpbContext->SpbIoTarget); + goto exit; + } + + RtlInitEmptyUnicodeString( + &spbDeviceName, + spbDeviceNameBuffer, + sizeof(spbDeviceNameBuffer)); + + status = RESOURCE_HUB_CREATE_PATH_FROM_ID( + &spbDeviceName, + SpbContext->I2cResHubId.LowPart, + SpbContext->I2cResHubId.HighPart); + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_SPB, + "Error creating Spb resource hub path string - %!STATUS!", + status); + goto exit; + } + + WDF_IO_TARGET_OPEN_PARAMS_INIT_OPEN_BY_NAME( + &openParams, + &spbDeviceName, + (GENERIC_READ | GENERIC_WRITE)); + + openParams.ShareAccess = 0; + openParams.CreateDisposition = FILE_OPEN; + openParams.FileAttributes = FILE_ATTRIBUTE_NORMAL; + + status = WdfIoTargetOpen(SpbContext->SpbIoTarget, &openParams); + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_SPB, + "Error opening Spb target for communication - %!STATUS!", + status); + goto exit; + } + + // + // Allocate some fixed-size buffers from NonPagedPool for typical + // Spb transaction sizes to avoid pool fragmentation in most cases + // + status = WdfMemoryCreate( + WDF_NO_OBJECT_ATTRIBUTES, + NonPagedPool, + TOUCH_POOL_TAG, + DEFAULT_SPB_BUFFER_SIZE, + &SpbContext->WriteMemory, + NULL); + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_SPB, + "Error allocating default memory for Spb write - %!STATUS!", + status); + goto exit; + } + + status = WdfMemoryCreate( + WDF_NO_OBJECT_ATTRIBUTES, + NonPagedPool, + TOUCH_POOL_TAG, + DEFAULT_SPB_BUFFER_SIZE, + &SpbContext->ReadMemory, + NULL); + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_SPB, + "Error allocating default memory for Spb read - %!STATUS!", + status); + goto exit; + } + + // + // Allocate a waitlock to guard access to the default buffers + // + status = WdfWaitLockCreate( + WDF_NO_OBJECT_ATTRIBUTES, + &SpbContext->SpbLock); + + if (!NT_SUCCESS(status)) + { + Trace( + TRACE_LEVEL_ERROR, + TRACE_FLAG_SPB, + "Error creating Spb Waitlock - %!STATUS!", + status); + goto exit; + } + +exit: + + if (!NT_SUCCESS(status)) + { + SpbTargetDeinitialize(FxDevice, SpbContext); + } + + return status; +} diff --git a/input/hiddigi/SynapticsTouch/spb.h b/input/hiddigi/SynapticsTouch/spb.h new file mode 100644 index 000000000..15ba8ae67 --- /dev/null +++ b/input/hiddigi/SynapticsTouch/spb.h @@ -0,0 +1,67 @@ +/*++ + Copyright (c) Microsoft Corporation. All Rights Reserved. + Sample code. Dealpoint ID #843729. + + Module Name: + + spb.h + + Abstract: + + This module contains the touch driver I2C helper definitions. + + Environment: + + Kernel Mode + + Revision History: + +--*/ + +#pragma once + +#include +#include + +#define DEFAULT_SPB_BUFFER_SIZE 64 + +// +// SPB (I2C) context +// + +typedef struct _SPB_CONTEXT +{ + WDFIOTARGET SpbIoTarget; + LARGE_INTEGER I2cResHubId; + WDFMEMORY WriteMemory; + WDFMEMORY ReadMemory; + WDFWAITLOCK SpbLock; +} SPB_CONTEXT; + +NTSTATUS +SpbReadDataSynchronously( + _In_ SPB_CONTEXT *SpbContext, + _In_ UCHAR Address, + _In_reads_bytes_(Length) PVOID Data, + _In_ ULONG Length + ); + +VOID +SpbTargetDeinitialize( + IN WDFDEVICE FxDevice, + IN SPB_CONTEXT *SpbContext + ); + +NTSTATUS +SpbTargetInitialize( + IN WDFDEVICE FxDevice, + IN SPB_CONTEXT *SpbContext + ); + +NTSTATUS +SpbWriteDataSynchronously( + IN SPB_CONTEXT *SpbContext, + IN UCHAR Address, + IN PVOID Data, + IN ULONG Length + ); diff --git a/input/hiddigi/SynapticsTouch/trace.h b/input/hiddigi/SynapticsTouch/trace.h new file mode 100644 index 000000000..0af971944 --- /dev/null +++ b/input/hiddigi/SynapticsTouch/trace.h @@ -0,0 +1,49 @@ +/*++ + Copyright (c) Microsoft Corporation. All Rights Reserved. + Sample code. Dealpoint ID #843729. + + Module Name: + + trace.h + + Abstract: + + Contains prototypes and definitions related to debugging/tracing + + Environment: + + Kernel mode + + Revision History: + +--*/ + +#pragma once + +// +// Control GUID: +// {64BAF936-E94C-4747-91E3-BB4CB8328E5F} +// + +#define WPP_CONTROL_GUIDS \ + WPP_DEFINE_CONTROL_GUID( \ + TouchDriverTraceGuid, \ + (64BAF936,E94C,4747,91E3,BB4CB8328E5F), \ + WPP_DEFINE_BIT(TRACE_FLAG_INIT) \ + WPP_DEFINE_BIT(TRACE_FLAG_REGISTRY) \ + WPP_DEFINE_BIT(TRACE_FLAG_HID) \ + WPP_DEFINE_BIT(TRACE_FLAG_PNP) \ + WPP_DEFINE_BIT(TRACE_FLAG_POWER) \ + WPP_DEFINE_BIT(TRACE_FLAG_SPB) \ + WPP_DEFINE_BIT(TRACE_FLAG_CONFIG) \ + WPP_DEFINE_BIT(TRACE_FLAG_REPORTING) \ + WPP_DEFINE_BIT(TRACE_FLAG_INTERRUPT) \ + WPP_DEFINE_BIT(TRACE_FLAG_SAMPLES) \ + WPP_DEFINE_BIT(TRACE_FLAG_OTHER) \ + WPP_DEFINE_BIT(TRACE_FLAG_IDLE) \ + ) + +#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) + diff --git a/input/kbfiltr/ReadMe.md b/input/kbfiltr/ReadMe.md new file mode 100644 index 000000000..d902befe1 --- /dev/null +++ b/input/kbfiltr/ReadMe.md @@ -0,0 +1,119 @@ +Keyboard Input WDF Filter Driver (Kbfiltr) +========================================== + +The Kbdfltr sample is an example of a keyboard input filter driver. + +This sample is WDF version of the original WDM filter driver sample. The WDM version of this sample has been deprecated. + +This is an upper device filter driver sample for PS/2 keyboard. This driver layers in between the KbdClass driver and i8042prt driver and hooks the callback routine that moves keyboard inputs from the port driver to class driver. In its current state, it only hooks into the keyboard packet report chain, the keyboard initialization function, and the keyboard ISR, but does not do any processing of the data that it sees. (The hooking of the initialization function and ISR is only available in the i8042prt stack.) With additions to this current filter-only code base, the filter could conceivably add, remove, or modify input as needed. + +This sample also creates a raw PDO and registers an interface so that applications can talk to the filter driver directly without going through the PS/2 devicestack. The reason for providing this additional interface is because the keyboard device is an exclusive secure device and it's not possible to open the device from usermode and send custom ioctls through it. + +This driver filters input for a particular keyboard on the system. If you want to filter keyboard inputs from all the keyboards plugged into the system, you can install this driver as a class filter below the KbdClass filter driver by adding the service name of this filter driver before the KbdClass filter in the registry at: +`HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\{4D36E96B-E325-11CE-BFC1-08002BE10318}\UpperFilters` + +## Universal Windows Driver Compliant +This sample builds a Universal Windows Driver. It uses only APIs and DDIs that are included in OneCoreUAP. + +Set the hardware ID in the inx file +----------------------------------- + +This step is required for automatic deployment (described later) to work properly. In the kbfiltr.inx file (located with the driver source files), find the [DDK\_Ex.Mfg.NT\$ARCH\$] section. Change the hardware ID in the %DDK\_Ex% entry from the dummy value to the hardware ID of the PS/2 keyboard on the target computer. The following example shows the hardware ID change. + +``` {.syntax xml:space="preserve"} +; For XP and above +[DDK_Ex.Mfg.NT$ARCH$] +;%DDK_Ex% = kbfiltr, *PNP0BAAD +%DDK_Ex% = kbfiltr, ACPI\VEN_PNP&DEV_0303 +``` + +Build the sample using Visual Studio +------------------------------------ + +In Visual Studio, on the **Build** menu, choose **Build Solution**. + +For more information about using Visual Studio to build a driver package, see [Building a Driver](http://msdn.microsoft.com/en-us/library/windows/hardware/ff554644). + +The test application, *kbftest.exe* is also built as part of the solution under the 'exe' folder. + +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 Win7 Debug and x64, the package is your solution folder under x64\\Win7Debug\\Package. + +The package contains these files: + + ++++ + + + + + + + + + + + +
File +Description
Kmdfsamples.cat +A signed catalog file, which serves as the signature for the entire package.kbfiltr.inf +An information (INF) file that contains information needed to install the driver.
+ +Using MSBuild +------------- + +As an alternative to building the Kbfiltr Filter 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, kbfiltr.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†kbfiltr.sln** + +**msbuild /p:configuration=â€Win8 Release†/p:platform=â€win32†kbfiltr.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). + +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 kbfiltr 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/). + +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 **Install and Verify**, and choose **Default Driver Package Installation Task** in the list. Click **OK**. +3. On the **Build** menu, choose **Deploy Package** or **Build Solution**. + +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). + +1. Copy all of the files in your driver package to a folder on the target computer (for example, c:\\KbfiltrDriverPackage). +2. On the target computer, open a Command Prompt window as Administrator. Navigate to your driver package folder, and enter the Devcon command with the correct hardware ID, such as: + + **Devcon install kbfiltr.inf ACPI\\VEN\_PNP&DEV\_0303** + + -or- + + Using Device Manager, update the driver for the PS/2 Keyboard by manually selecting kbfiltr.inf from the location where you copied the driver files. + +View the installed 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 **DDK Example Device that needs filtering** under the **Keyboards** node. + +Testing +------- + +To use the test application provided with the sample, it must be copied to the target computer manually. Save the kbftest.exe file from the folder where the build result is placed (for example, exe\\x64\\Win7Debug). This file is copied somewhere on the target, possibly where the driver package files are located. The test application is the executed on the target computer in a Command Prompt using **kbftest** as the command. + +**Tip**  To avoid DLL dependencies for kbftext.exe, and the need to copy additional files, select the statically linked run-time library when building. + diff --git a/input/kbfiltr/exe/kbftest.c b/input/kbfiltr/exe/kbftest.c new file mode 100644 index 000000000..b4c43acbe --- /dev/null +++ b/input/kbfiltr/exe/kbftest.c @@ -0,0 +1,239 @@ +/*++ + +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: + + KBFTEST.C + +Abstract: + + +Environment: + + usermode console application + +--*/ + + +#include +#include +#include +#include +#include +#include +#include +#include + +#pragma warning(disable:4201) + +#include +#include + +#pragma warning(default:4201) + +#include "..\sys\public.h" + +//----------------------------------------------------------------------------- +// 4127 -- Conditional Expression is Constant warning +//----------------------------------------------------------------------------- +#define WHILE(constant) \ +__pragma(warning(disable: 4127)) while(constant); __pragma(warning(default: 4127)) + +DEFINE_GUID(GUID_DEVINTERFACE_KBFILTER, +0x3fb7299d, 0x6847, 0x4490, 0xb0, 0xc9, 0x99, 0xe0, 0x98, 0x6a, 0xb8, 0x86); +// {3FB7299D-6847-4490-B0C9-99E0986AB886} + + +int +_cdecl +main( + _In_ int argc, + _In_ char *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; + ULONG i =0; + KEYBOARD_ATTRIBUTES kbdattrib; + + UNREFERENCED_PARAMETER(argc); + UNREFERENCED_PARAMETER(argv); + + // + // Open a handle to the device interface information set of all + // present toaster class interfaces. + // + + hardwareDeviceInfo = SetupDiGetClassDevs ( + (LPGUID)&GUID_DEVINTERFACE_KBFILTER, + 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 KBFILTER Device Interfaces\n"); + printf("---------------------------------\n"); + + i = 0; + + // + // Enumerate devices of toaster class + // + + do { + if (SetupDiEnumDeviceInterfaces (hardwareDeviceInfo, + 0, // No care about specific PDOs + (LPGUID)&GUID_DEVINTERFACE_KBFILTER, + 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; + + } WHILE (TRUE); + + + 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; + } + + // + // Send an IOCTL to retrive the keyboard attributes + // These are cached in the kbfiltr + // + + if (!DeviceIoControl (file, + IOCTL_KBFILTR_GET_KEYBOARD_ATTRIBUTES, + NULL, 0, + &kbdattrib, sizeof(kbdattrib), + &bytes, NULL)) { + printf("Retrieve Keyboard Attributes request failed:0x%x\n", GetLastError()); + free (deviceInterfaceDetailData); + CloseHandle(file); + return 0; + } + + printf("\nKeyboard Attributes:\n" + " KeyboardMode: 0x%x\n" + " NumberOfFunctionKeys: 0x%x\n" + " NumberOfIndicators: 0x%x\n" + " NumberOfKeysTotal: 0x%x\n" + " InputDataQueueLength: 0x%x\n", + kbdattrib.KeyboardMode, + kbdattrib.NumberOfFunctionKeys, + kbdattrib.NumberOfIndicators, + kbdattrib.NumberOfKeysTotal, + kbdattrib.InputDataQueueLength); + + free (deviceInterfaceDetailData); + CloseHandle(file); + return 0; +} + + + diff --git a/input/kbfiltr/exe/kbftest.vcxproj b/input/kbfiltr/exe/kbftest.vcxproj new file mode 100644 index 000000000..e51147f80 --- /dev/null +++ b/input/kbfiltr/exe/kbftest.vcxproj @@ -0,0 +1,142 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {4D3A4233-FEF4-479F-9874-8C239D8FF78E} + $(MSBuildProjectName) + Debug + Win32 + {6FB0E6F4-D53A-4112-BCA0-F1760AC63AEB} + + + + 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) + + + + + + + + + + + + + + + + kbftest + + + kbftest + + + kbftest + + + kbftest + + + + %(AdditionalDependencies);setupapi.lib + + + + + + + + + %(AdditionalDependencies);setupapi.lib + + + + + + + + + %(AdditionalDependencies);setupapi.lib + + + + + + + + + %(AdditionalDependencies);setupapi.lib + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/input/kbfiltr/exe/kbftest.vcxproj.Filters b/input/kbfiltr/exe/kbftest.vcxproj.Filters new file mode 100644 index 000000000..2f49b7514 --- /dev/null +++ b/input/kbfiltr/exe/kbftest.vcxproj.Filters @@ -0,0 +1,22 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {A3CB6490-2256-4820-8332-9513E8921148} + + + h;hpp;hxx;hm;inl;inc;xsd + {6E637087-7080-45AE-889F-39D5089A8A25} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {B1978376-1D0B-4A14-83B0-E6921A530878} + + + + + Source Files + + + \ No newline at end of file diff --git a/input/kbfiltr/kbfiltr.htm b/input/kbfiltr/kbfiltr.htm new file mode 100644 index 000000000..b282cca42 --- /dev/null +++ b/input/kbfiltr/kbfiltr.htm @@ -0,0 +1,465 @@ + + + + + + + + +Kbfiltr + + + + + + + +
+ +

Kbfiltr

+ +

SUMMARY

+ +

This sample is WDF +version of WDM filter driver. The WDM version of this filter has been deprecated. This is an upper device filter driver sample for +PS/2 keyboard. This driver layers in between the KbdClass +driver and i8042prt driver and hooks the callback routine that moves keyboard +inputs from the port driver to class driver. In its current state, it only +hooks into the keyboard packet report chain, the keyboard initialization +function, and the keyboard ISR, but does not do any processing of the data that +it sees. (The hooking of the initialization function and ISR is only available +in the i8042prt stack.) With additions to this current filter-only code base, +the filter could conceivably add, remove, or modify input as needed.

+ +

This sample also creates +a raw PDO and registers an interface so that application can talk to the filter +driver directly without going thru the PS/2 devicestack. +The reason for providing this additional interface is because the keyboard +device is an exclusive secure device and it's not possible to open the device +from usermode and send custom ioctls +through it.

+ +

This driver filters input +for a particular keyboard on the system. If you want to filter keyboard inputs +from all the keyboards plugged into the system then you can install this driver +as a class filter below the kbdclass filter driver by +adding the service name of this filter driver before the kbdclass +filter in the registry at "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\{4D36E96B-E325-11CE-BFC1-08002BE10318}\UpperFilters". +

+ +

BUILDING THE SAMPLE

+ +

Click the Free Build +Environment or Checked Build Environment icon under Development Kits program +group to set basic environment variables.

+ +

Change to the directory +containing the device source code, such as CD src\input\kbfiltr. +

+ +

Run build -ceZ, or use the macro BLD. This command invokes +the Microsoft make routines to build the components. If the build succeeds, you will find the driver, +kbfiltr.sys, +in the binary output directory specified for the build environment. You can get the output path from the buildxxx.log file. If it fails you can find errors and warnings in the buildxxx.err +and buildxxx.wrn respectively, where xxx is either chk or fre +depending on the build environment.

+ +

INSTALLATION

+ +

+Copy the KMDF coinstaller (wdfcoinstallerMMmmm.dll), driver binary and the kbfiltr.inf file to a floppy disk or a temp folder.

+ +

This sample is installed +via an .inf file. The .inf +file included in this sample is designed to filter a PS/2 keyboard.

+ +

Use the following rules +regarding the .inf file that installs the filter +driver.

+ +

·  The .inf +file must install the class driver (Kbdclass) and the +port driver (i8042prt, Kbdhid, etc.) by using Keyboard.inf and the INF directives "Needs" and +"Include".

+ +

·  The .inf +file must add the correct registry values for the class and port driver, as +well as using the new directives.

+ +

To +install this filter, follow these steps:

+ +
    +
  1. Open the Device Manager.
  2. +
  3. Open the Properties of the PS/2 keyboard + installed on the system.
  4. +
  5. Click the Driver tab, and then click Update + Driver.
  6. +
  7. Click the Browse my computer for drivers software.
  8. +
  9. Click the Let me pick from a list of device drivers on my computer.
  10. +
  11. Click Have Disk and point to the location of + the .inf file.
  12. +
  13. Proceed through the rest of the install. You will + need to reboot the machine if you are filtering a PS/2 device.
  14. +
+ +

To install this driver as +a class filter, you have to use registry APIs to directly update the registry +with an installer and reboot your machine.

+ +

Once +built, the sample produces Kbfiltr.sys and Kbftest.exe.

+ +

 

+ +

NULL INF file for Windows 2000:

+ +

 

+ +

On +Windows 2000, you have to provide an INF file to install a NULL driver for the +RAW PDO enumerated by the filter driver for sideband communication from usermode. Without this INF file, the raw PDO wouldn’t get +started. On XP and later OSes, you don’t need to +provide an INF. Here are steps for installing the NULL driver:

+ +
    +
  1. Open the Device + Manager.
  2. +
  3. Select “Show Hidden Devices” in View Menu.
  4. +
  5. Find the ‘Keyboard_Filter_nnn’ + device with a yellow “!” in “Other” class of devices. ‘nnn’ stands for the instance number of the + device.
  6. +
  7. Double click on the device to open the properties.
  8. +
  9. Click the Driver tab, and then click Update Driver.
  10. +
  11. Follow the wizard's prompts, and when prompted, select + the Search for a suitable driver + for my device (recommended) option, and then click Next.
  12. +
  13. Select Specify + a location, clear the other choices, and click Next. Point to the location of the sideband.inf file.
  14. +
  15. Proceed through the rest of the install.
  16. +
  17. After install, you should see the Keyboard device + under “Sample Device” class in the device manager.
  18. +
+ +

CODE TOUR

+ +

File Manifest

+ +
File           Description
 
 
Kbfiltr.htm    The documentation for this sample (this file)
Kbfiltr.c      Hooks into the reporting chain, the initialization of a PS/2 keyboard, and the PS/2 keyboard ISR
Kbfiltr.h      Definitions
public.h       Shared definitions with the exe
Kbfiltr.rc     Resources
Kbfiltr.inx    Sample .inf file 
Sideband.inf   INF to install a NULL driver for the Raw PDO on Windows 2000
Kbftest.c      Simple test application
Makefile.inc   A makefile that defines custom build actions.  This includes the conversion of the .INX file into a .INF file
+
 
 
 
+ +

Top of page +

+ +
 
+ + + + + +
+

 

+
+ +
 
 
+ +

© +Microsoft Corporation 1999

+ +
+ + + + diff --git a/input/kbfiltr/kbfiltr.sln b/input/kbfiltr/kbfiltr.sln new file mode 100644 index 000000000..e2138f5d2 --- /dev/null +++ b/input/kbfiltr/kbfiltr.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", "{4A3D2BF5-DE3F-4CA9-95A3-E3FE41000D94}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sys", "Sys", "{75FDC7AD-767A-4C07-975E-332B3D4DD47D}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "kbftest", "exe\kbftest.vcxproj", "{4D3A4233-FEF4-479F-9874-8C239D8FF78E}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "kbfiltr", "sys\kbfiltr.vcxproj", "{CABE9083-8EBA-41D4-A0D4-F0E3BFDD9E79}" +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 + {4D3A4233-FEF4-479F-9874-8C239D8FF78E}.Debug|Win32.ActiveCfg = Debug|Win32 + {4D3A4233-FEF4-479F-9874-8C239D8FF78E}.Debug|Win32.Build.0 = Debug|Win32 + {4D3A4233-FEF4-479F-9874-8C239D8FF78E}.Release|Win32.ActiveCfg = Release|Win32 + {4D3A4233-FEF4-479F-9874-8C239D8FF78E}.Release|Win32.Build.0 = Release|Win32 + {4D3A4233-FEF4-479F-9874-8C239D8FF78E}.Debug|x64.ActiveCfg = Debug|x64 + {4D3A4233-FEF4-479F-9874-8C239D8FF78E}.Debug|x64.Build.0 = Debug|x64 + {4D3A4233-FEF4-479F-9874-8C239D8FF78E}.Release|x64.ActiveCfg = Release|x64 + {4D3A4233-FEF4-479F-9874-8C239D8FF78E}.Release|x64.Build.0 = Release|x64 + {CABE9083-8EBA-41D4-A0D4-F0E3BFDD9E79}.Debug|Win32.ActiveCfg = Debug|Win32 + {CABE9083-8EBA-41D4-A0D4-F0E3BFDD9E79}.Debug|Win32.Build.0 = Debug|Win32 + {CABE9083-8EBA-41D4-A0D4-F0E3BFDD9E79}.Release|Win32.ActiveCfg = Release|Win32 + {CABE9083-8EBA-41D4-A0D4-F0E3BFDD9E79}.Release|Win32.Build.0 = Release|Win32 + {CABE9083-8EBA-41D4-A0D4-F0E3BFDD9E79}.Debug|x64.ActiveCfg = Debug|x64 + {CABE9083-8EBA-41D4-A0D4-F0E3BFDD9E79}.Debug|x64.Build.0 = Debug|x64 + {CABE9083-8EBA-41D4-A0D4-F0E3BFDD9E79}.Release|x64.ActiveCfg = Release|x64 + {CABE9083-8EBA-41D4-A0D4-F0E3BFDD9E79}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {4D3A4233-FEF4-479F-9874-8C239D8FF78E} = {4A3D2BF5-DE3F-4CA9-95A3-E3FE41000D94} + {CABE9083-8EBA-41D4-A0D4-F0E3BFDD9E79} = {75FDC7AD-767A-4C07-975E-332B3D4DD47D} + EndGlobalSection +EndGlobal diff --git a/input/kbfiltr/sys/kbfiltr.c b/input/kbfiltr/sys/kbfiltr.c new file mode 100644 index 000000000..e0009172b --- /dev/null +++ b/input/kbfiltr/sys/kbfiltr.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: + + kbfiltr.c + +Abstract: This is an upper device filter driver sample for PS/2 keyboard. This + driver layers in between the KbdClass driver and i8042prt driver and + hooks the callback routine that moves keyboard inputs from the port + driver to class driver. With this filter, you can remove or insert + additional keys into the stream. This sample also creates a raw + PDO and registers an interface so that application can talk to + the filter driver directly without going thru the PS/2 devicestack. + The reason for providing this additional interface is because the keyboard + device is an exclusive secure device and it's not possible to open the + device from usermode and send custom ioctls. + + If you want to filter keyboard inputs from all the keyboards (ps2, usb) + plugged into the system then you can install this driver as a class filter + and make it sit below the kbdclass filter driver by adding the service + name of this filter driver before the kbdclass filter in the registry at + " HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\ + {4D36E96B-E325-11CE-BFC1-08002BE10318}\UpperFilters" + + +Environment: + + Kernel mode only. + +--*/ + +#include "kbfiltr.h" + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (INIT, DriverEntry) +#pragma alloc_text (PAGE, KbFilter_EvtDeviceAdd) +#pragma alloc_text (PAGE, KbFilter_EvtIoInternalDeviceControl) +#endif + +ULONG InstanceNo = 0; + +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; + + DebugPrint(("Keyboard Filter Driver Sample - Driver Framework Edition.\n")); + DebugPrint(("Built %s %s\n", __DATE__, __TIME__)); + + // + // 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, + KbFilter_EvtDeviceAdd + ); + + // + // Create a framework driver object to represent our driver. + // + status = WdfDriverCreate(DriverObject, + RegistryPath, + WDF_NO_OBJECT_ATTRIBUTES, + &config, + WDF_NO_HANDLE); // hDriver optional + if (!NT_SUCCESS(status)) { + DebugPrint(("WdfDriverCreate failed with status 0x%x\n", status)); + } + + return status; +} + +NTSTATUS +KbFilter_EvtDeviceAdd( + 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; + NTSTATUS status; + WDFDEVICE hDevice; + WDFQUEUE hQueue; + PDEVICE_EXTENSION filterExt; + WDF_IO_QUEUE_CONFIG ioQueueConfig; + + UNREFERENCED_PARAMETER(Driver); + + PAGED_CODE(); + + DebugPrint(("Enter FilterEvtDeviceAdd \n")); + + // + // 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); + + WdfDeviceInitSetDeviceType(DeviceInit, FILE_DEVICE_KEYBOARD); + + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&deviceAttributes, DEVICE_EXTENSION); + + // + // Create a framework device object. This call will in turn create + // a WDM deviceobject, attach to the lower stack and set the + // appropriate flags and attributes. + // + status = WdfDeviceCreate(&DeviceInit, &deviceAttributes, &hDevice); + if (!NT_SUCCESS(status)) { + DebugPrint(("WdfDeviceCreate failed with status code 0x%x\n", status)); + return status; + } + + filterExt = FilterGetData(hDevice); + + // + // Configure the default queue to be Parallel. Do not use sequential queue + // if this driver is going to be filtering PS2 ports because it can lead to + // deadlock. The PS2 port driver sends a request to the top of the stack when it + // receives an ioctl request and waits for it to be completed. If you use a + // a sequential queue, this request will be stuck in the queue because of the + // outstanding ioctl request sent earlier to the port driver. + // + WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&ioQueueConfig, + WdfIoQueueDispatchParallel); + + // + // Framework by default creates non-power managed queues for + // filter drivers. + // + ioQueueConfig.EvtIoInternalDeviceControl = KbFilter_EvtIoInternalDeviceControl; + + status = WdfIoQueueCreate(hDevice, + &ioQueueConfig, + WDF_NO_OBJECT_ATTRIBUTES, + WDF_NO_HANDLE // pointer to default queue + ); + if (!NT_SUCCESS(status)) { + DebugPrint( ("WdfIoQueueCreate failed 0x%x\n", status)); + return status; + } + + // + // Create a new queue to handle IOCTLs that will be forwarded to us from + // the rawPDO. + // + WDF_IO_QUEUE_CONFIG_INIT(&ioQueueConfig, + WdfIoQueueDispatchParallel); + + // + // Framework by default creates non-power managed queues for + // filter drivers. + // + ioQueueConfig.EvtIoDeviceControl = KbFilter_EvtIoDeviceControlFromRawPdo; + + status = WdfIoQueueCreate(hDevice, + &ioQueueConfig, + WDF_NO_OBJECT_ATTRIBUTES, + &hQueue + ); + if (!NT_SUCCESS(status)) { + DebugPrint( ("WdfIoQueueCreate failed 0x%x\n", status)); + return status; + } + + filterExt->rawPdoQueue = hQueue; + + // + // Create a RAW pdo so we can provide a sideband communication with + // the application. Please note that not filter drivers desire to + // produce such a communication and not all of them are contrained + // by other filter above which prevent communication thru the device + // interface exposed by the main stack. So use this only if absolutely + // needed. Also look at the toaster filter driver sample for an alternate + // approach to providing sideband communication. + // + status = KbFiltr_CreateRawPdo(hDevice, ++InstanceNo); + + return status; +} + +VOID +KbFilter_EvtIoDeviceControlFromRawPdo( + 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 + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + WDFDEVICE hDevice; + WDFMEMORY outputMemory; + PDEVICE_EXTENSION devExt; + size_t bytesTransferred = 0; + + UNREFERENCED_PARAMETER(InputBufferLength); + + DebugPrint(("Entered KbFilter_EvtIoInternalDeviceControl\n")); + + hDevice = WdfIoQueueGetDevice(Queue); + devExt = FilterGetData(hDevice); + + // + // Process the ioctl and complete it when you are done. + // + + switch (IoControlCode) { + case IOCTL_KBFILTR_GET_KEYBOARD_ATTRIBUTES: + + // + // Buffer is too small, fail the request + // + if (OutputBufferLength < sizeof(KEYBOARD_ATTRIBUTES)) { + status = STATUS_BUFFER_TOO_SMALL; + break; + } + + status = WdfRequestRetrieveOutputMemory(Request, &outputMemory); + + if (!NT_SUCCESS(status)) { + DebugPrint(("WdfRequestRetrieveOutputMemory failed %x\n", status)); + break; + } + + status = WdfMemoryCopyFromBuffer(outputMemory, + 0, + &devExt->KeyboardAttributes, + sizeof(KEYBOARD_ATTRIBUTES)); + + if (!NT_SUCCESS(status)) { + DebugPrint(("WdfMemoryCopyFromBuffer failed %x\n", status)); + break; + } + + bytesTransferred = sizeof(KEYBOARD_ATTRIBUTES); + + break; + default: + status = STATUS_NOT_IMPLEMENTED; + break; + } + + WdfRequestCompleteWithInformation(Request, status, bytesTransferred); + + return; +} + +VOID +KbFilter_EvtIoInternalDeviceControl( + 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. + There are two specific control codes that are of interest: + + IOCTL_INTERNAL_KEYBOARD_CONNECT: + Store the old context and function pointer and replace it with our own. + This makes life much simpler than intercepting IRPs sent by the RIT and + modifying them on the way back up. + + IOCTL_INTERNAL_I8042_HOOK_KEYBOARD: + Add in the necessary function pointers and context values so that we can + alter how the ps/2 keyboard is initialized. + + NOTE: Handling IOCTL_INTERNAL_I8042_HOOK_KEYBOARD is *NOT* necessary if + all you want to do is filter KEYBOARD_INPUT_DATAs. You can remove + the handling code and all related device extension fields and + functions to conserve space. + +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 + +--*/ +{ + PDEVICE_EXTENSION devExt; + PINTERNAL_I8042_HOOK_KEYBOARD hookKeyboard = NULL; + PCONNECT_DATA connectData = NULL; + NTSTATUS status = STATUS_SUCCESS; + size_t length; + WDFDEVICE hDevice; + BOOLEAN forwardWithCompletionRoutine = FALSE; + BOOLEAN ret = TRUE; + WDFCONTEXT completionContext = WDF_NO_CONTEXT; + WDF_REQUEST_SEND_OPTIONS options; + WDFMEMORY outputMemory; + UNREFERENCED_PARAMETER(OutputBufferLength); + UNREFERENCED_PARAMETER(InputBufferLength); + + + PAGED_CODE(); + + DebugPrint(("Entered KbFilter_EvtIoInternalDeviceControl\n")); + + hDevice = WdfIoQueueGetDevice(Queue); + devExt = FilterGetData(hDevice); + + switch (IoControlCode) { + + // + // Connect a keyboard class device driver to the port driver. + // + case IOCTL_INTERNAL_KEYBOARD_CONNECT: + // + // Only allow one connection. + // + if (devExt->UpperConnectData.ClassService != NULL) { + status = STATUS_SHARING_VIOLATION; + break; + } + + // + // Get the input buffer from the request + // (Parameters.DeviceIoControl.Type3InputBuffer). + // + status = WdfRequestRetrieveInputBuffer(Request, + sizeof(CONNECT_DATA), + &connectData, + &length); + if(!NT_SUCCESS(status)){ + DebugPrint(("WdfRequestRetrieveInputBuffer failed %x\n", status)); + break; + } + + NT_ASSERT(length == InputBufferLength); + + devExt->UpperConnectData = *connectData; + + // + // Hook into the report chain. Everytime a keyboard packet is reported + // to the system, KbFilter_ServiceCallback will be called + // + + connectData->ClassDeviceObject = WdfDeviceWdmGetDeviceObject(hDevice); + +#pragma warning(disable:4152) //nonstandard extension, function/data pointer conversion + + connectData->ClassService = KbFilter_ServiceCallback; + +#pragma warning(default:4152) + + break; + + // + // Disconnect a keyboard class device driver from the port driver. + // + case IOCTL_INTERNAL_KEYBOARD_DISCONNECT: + + // + // Clear the connection parameters in the device extension. + // + // devExt->UpperConnectData.ClassDeviceObject = NULL; + // devExt->UpperConnectData.ClassService = NULL; + + status = STATUS_NOT_IMPLEMENTED; + break; + + // + // Attach this driver to the initialization and byte processing of the + // i8042 (ie PS/2) keyboard. This is only necessary if you want to do PS/2 + // specific functions, otherwise hooking the CONNECT_DATA is sufficient + // + case IOCTL_INTERNAL_I8042_HOOK_KEYBOARD: + + DebugPrint(("hook keyboard received!\n")); + + // + // Get the input buffer from the request + // (Parameters.DeviceIoControl.Type3InputBuffer) + // + status = WdfRequestRetrieveInputBuffer(Request, + sizeof(INTERNAL_I8042_HOOK_KEYBOARD), + &hookKeyboard, + &length); + if(!NT_SUCCESS(status)){ + DebugPrint(("WdfRequestRetrieveInputBuffer failed %x\n", status)); + break; + } + + NT_ASSERT(length == InputBufferLength); + + // + // Enter our own initialization routine and record any Init routine + // that may be above us. Repeat for the isr hook + // + devExt->UpperContext = hookKeyboard->Context; + + // + // replace old Context with our own + // + hookKeyboard->Context = (PVOID) devExt; + + if (hookKeyboard->InitializationRoutine) { + devExt->UpperInitializationRoutine = + hookKeyboard->InitializationRoutine; + } + hookKeyboard->InitializationRoutine = + (PI8042_KEYBOARD_INITIALIZATION_ROUTINE) + KbFilter_InitializationRoutine; + + if (hookKeyboard->IsrRoutine) { + devExt->UpperIsrHook = hookKeyboard->IsrRoutine; + } + hookKeyboard->IsrRoutine = (PI8042_KEYBOARD_ISR) KbFilter_IsrHook; + + // + // Store all of the other important stuff + // + devExt->IsrWritePort = hookKeyboard->IsrWritePort; + devExt->QueueKeyboardPacket = hookKeyboard->QueueKeyboardPacket; + devExt->CallContext = hookKeyboard->CallContext; + + status = STATUS_SUCCESS; + break; + + + case IOCTL_KEYBOARD_QUERY_ATTRIBUTES: + forwardWithCompletionRoutine = TRUE; + completionContext = devExt; + break; + + // + // Might want to capture these in the future. For now, then pass them down + // the stack. These queries must be successful for the RIT to communicate + // with the keyboard. + // + case IOCTL_KEYBOARD_QUERY_INDICATOR_TRANSLATION: + case IOCTL_KEYBOARD_QUERY_INDICATORS: + case IOCTL_KEYBOARD_SET_INDICATORS: + case IOCTL_KEYBOARD_QUERY_TYPEMATIC: + case IOCTL_KEYBOARD_SET_TYPEMATIC: + break; + } + + 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 (forwardWithCompletionRoutine) { + + // + // Format the request with the output memory so the completion routine + // can access the return data in order to cache it into the context area + // + + status = WdfRequestRetrieveOutputMemory(Request, &outputMemory); + + if (!NT_SUCCESS(status)) { + DebugPrint(("WdfRequestRetrieveOutputMemory failed: 0x%x\n", status)); + WdfRequestComplete(Request, status); + return; + } + + status = WdfIoTargetFormatRequestForInternalIoctl(WdfDeviceGetIoTarget(hDevice), + Request, + IoControlCode, + NULL, + NULL, + outputMemory, + NULL); + + if (!NT_SUCCESS(status)) { + DebugPrint(("WdfIoTargetFormatRequestForInternalIoctl failed: 0x%x\n", status)); + WdfRequestComplete(Request, status); + return; + } + + // + // Set our completion routine with a context area that we will save + // the output data into + // + WdfRequestSetCompletionRoutine(Request, + KbFilterRequestCompletionRoutine, + completionContext); + + ret = WdfRequestSend(Request, + WdfDeviceGetIoTarget(hDevice), + WDF_NO_SEND_OPTIONS); + + if (ret == FALSE) { + status = WdfRequestGetStatus (Request); + DebugPrint( ("WdfRequestSend failed: 0x%x\n", status)); + WdfRequestComplete(Request, status); + } + + } + else + { + + // + // 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, WdfDeviceGetIoTarget(hDevice), &options); + + if (ret == FALSE) { + status = WdfRequestGetStatus (Request); + DebugPrint(("WdfRequestSend failed: 0x%x\n", status)); + WdfRequestComplete(Request, status); + } + + } + + return; +} + +NTSTATUS +KbFilter_InitializationRoutine( + IN PVOID InitializationContext, + IN PVOID SynchFuncContext, + IN PI8042_SYNCH_READ_PORT ReadPort, + IN PI8042_SYNCH_WRITE_PORT WritePort, + OUT PBOOLEAN TurnTranslationOn + ) +/*++ + +Routine Description: + + This routine gets called after the following has been performed on the kb + 1) a reset + 2) set the typematic + 3) set the LEDs + + i8042prt specific code, if you are writing a packet only filter driver, you + can remove this function + +Arguments: + + DeviceObject - Context passed during IOCTL_INTERNAL_I8042_HOOK_KEYBOARD + + SynchFuncContext - Context to pass when calling Read/WritePort + + Read/WritePort - Functions to synchronoulsy read and write to the kb + + TurnTranslationOn - If TRUE when this function returns, i8042prt will not + turn on translation on the keyboard + +Return Value: + + Status is returned. + +--*/ +{ + PDEVICE_EXTENSION devExt; + NTSTATUS status = STATUS_SUCCESS; + + devExt = (PDEVICE_EXTENSION)InitializationContext; + + // + // Do any interesting processing here. We just call any other drivers + // in the chain if they exist. Make sure Translation is turned on as well + // + if (devExt->UpperInitializationRoutine) { + status = (*devExt->UpperInitializationRoutine) ( + devExt->UpperContext, + SynchFuncContext, + ReadPort, + WritePort, + TurnTranslationOn + ); + + if (!NT_SUCCESS(status)) { + return status; + } + } + + *TurnTranslationOn = TRUE; + return status; +} + +BOOLEAN +KbFilter_IsrHook( + PVOID IsrContext, + PKEYBOARD_INPUT_DATA CurrentInput, + POUTPUT_PACKET CurrentOutput, + UCHAR StatusByte, + PUCHAR DataByte, + PBOOLEAN ContinueProcessing, + PKEYBOARD_SCAN_STATE ScanState + ) +/*++ + +Routine Description: + + This routine gets called at the beginning of processing of the kb interrupt. + + i8042prt specific code, if you are writing a packet only filter driver, you + can remove this function + +Arguments: + + DeviceObject - Our context passed during IOCTL_INTERNAL_I8042_HOOK_KEYBOARD + + CurrentInput - Current input packet being formulated by processing all the + interrupts + + CurrentOutput - Current list of bytes being written to the keyboard or the + i8042 port. + + StatusByte - Byte read from I/O port 60 when the interrupt occurred + + DataByte - Byte read from I/O port 64 when the interrupt occurred. + This value can be modified and i8042prt will use this value + if ContinueProcessing is TRUE + + ContinueProcessing - If TRUE, i8042prt will proceed with normal processing of + the interrupt. If FALSE, i8042prt will return from the + interrupt after this function returns. Also, if FALSE, + it is this functions responsibilityt to report the input + packet via the function provided in the hook IOCTL or via + queueing a DPC within this driver and calling the + service callback function acquired from the connect IOCTL + +Return Value: + + Status is returned. + +--*/ +{ + PDEVICE_EXTENSION devExt; + BOOLEAN retVal = TRUE; + + devExt = (PDEVICE_EXTENSION)IsrContext; + + if (devExt->UpperIsrHook) { + retVal = (*devExt->UpperIsrHook) ( + devExt->UpperContext, + CurrentInput, + CurrentOutput, + StatusByte, + DataByte, + ContinueProcessing, + ScanState + ); + + if (!retVal || !(*ContinueProcessing)) { + return retVal; + } + } + + *ContinueProcessing = TRUE; + return retVal; +} + +VOID +KbFilter_ServiceCallback( + IN PDEVICE_OBJECT DeviceObject, + IN PKEYBOARD_INPUT_DATA InputDataStart, + IN PKEYBOARD_INPUT_DATA InputDataEnd, + IN OUT PULONG InputDataConsumed + ) +/*++ + +Routine Description: + + Called when there are keyboard packets to report to the Win32 subsystem. + You can do anything you like to the packets. For instance: + + o Drop a packet altogether + o Mutate the contents of a packet + o Insert packets into the stream + +Arguments: + + DeviceObject - Context passed during the connect IOCTL + + InputDataStart - First packet to be reported + + InputDataEnd - One past the last packet to be reported. Total number of + packets is equal to InputDataEnd - InputDataStart + + InputDataConsumed - Set to the total number of packets consumed by the RIT + (via the function pointer we replaced in the connect + IOCTL) + +Return Value: + + Status is returned. + +--*/ +{ + PDEVICE_EXTENSION devExt; + WDFDEVICE hDevice; + + hDevice = WdfWdmDeviceGetWdfDeviceHandle(DeviceObject); + + devExt = FilterGetData(hDevice); + + (*(PSERVICE_CALLBACK_ROUTINE)(ULONG_PTR) devExt->UpperConnectData.ClassService)( + devExt->UpperConnectData.ClassDeviceObject, + InputDataStart, + InputDataEnd, + InputDataConsumed); +} + +VOID +KbFilterRequestCompletionRoutine( + WDFREQUEST Request, + WDFIOTARGET Target, + PWDF_REQUEST_COMPLETION_PARAMS CompletionParams, + WDFCONTEXT Context + ) +/*++ + +Routine Description: + + Completion Routine + +Arguments: + + Target - Target handle + Request - Request handle + Params - request completion params + Context - Driver supplied context + + +Return Value: + + VOID + +--*/ +{ + WDFMEMORY buffer = CompletionParams->Parameters.Ioctl.Output.Buffer; + NTSTATUS status = CompletionParams->IoStatus.Status; + + UNREFERENCED_PARAMETER(Target); + + // + // Save the keyboard attributes in our context area so that we can return + // them to the app later. + // + if (NT_SUCCESS(status) && + CompletionParams->Type == WdfRequestTypeDeviceControlInternal && + CompletionParams->Parameters.Ioctl.IoControlCode == IOCTL_KEYBOARD_QUERY_ATTRIBUTES) { + + if( CompletionParams->Parameters.Ioctl.Output.Length >= sizeof(KEYBOARD_ATTRIBUTES)) { + + status = WdfMemoryCopyToBuffer(buffer, + CompletionParams->Parameters.Ioctl.Output.Offset, + &((PDEVICE_EXTENSION)Context)->KeyboardAttributes, + sizeof(KEYBOARD_ATTRIBUTES) + ); + } + } + + WdfRequestComplete(Request, status); + + return; +} + + diff --git a/input/kbfiltr/sys/kbfiltr.h b/input/kbfiltr/sys/kbfiltr.h new file mode 100644 index 000000000..00e149dc1 --- /dev/null +++ b/input/kbfiltr/sys/kbfiltr.h @@ -0,0 +1,205 @@ +/*++ +Copyright (c) 1997 Microsoft Corporation + +Module Name: + + kbfilter.h + +Abstract: + + This module contains the common private declarations for the keyboard + packet filter + +Environment: + + kernel mode only + +--*/ + +#ifndef KBFILTER_H +#define KBFILTER_H + +#pragma warning(disable:4201) + +#include "ntddk.h" +#include "kbdmou.h" +#include +#include + +#pragma warning(default:4201) + +#include + +#define NTSTRSAFE_LIB +#include + +#include +#include + +#include "public.h" + +#define KBFILTER_POOL_TAG (ULONG) 'tlfK' + +#if DBG + +#define TRAP() DbgBreakPoint() + +#define DebugPrint(_x_) DbgPrint _x_ + +#else // DBG + +#define TRAP() + +#define DebugPrint(_x_) + +#endif + +#define MIN(_A_,_B_) (((_A_) < (_B_)) ? (_A_) : (_B_)) + +typedef struct _DEVICE_EXTENSION +{ + WDFDEVICE WdfDevice; + + // + // Queue for handling requests that come from the rawPdo + // + WDFQUEUE rawPdoQueue; + + // + // Number of creates sent down + // + LONG EnableCount; + + // + // The real connect data that this driver reports to + // + CONNECT_DATA UpperConnectData; + + // + // Previous initialization and hook routines (and context) + // + PVOID UpperContext; + PI8042_KEYBOARD_INITIALIZATION_ROUTINE UpperInitializationRoutine; + PI8042_KEYBOARD_ISR UpperIsrHook; + + // + // Write function from within KbFilter_IsrHook + // + IN PI8042_ISR_WRITE_PORT IsrWritePort; + + // + // Queue the current packet (ie the one passed into KbFilter_IsrHook) + // + IN PI8042_QUEUE_PACKET QueueKeyboardPacket; + + // + // Context for IsrWritePort, QueueKeyboardPacket + // + IN PVOID CallContext; + + // + // Cached Keyboard Attributes + // + KEYBOARD_ATTRIBUTES KeyboardAttributes; + +} DEVICE_EXTENSION, *PDEVICE_EXTENSION; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(DEVICE_EXTENSION, + FilterGetData) + + +typedef struct _WORKER_ITEM_CONTEXT { + + WDFREQUEST Request; + WDFIOTARGET IoTarget; + +} WORKER_ITEM_CONTEXT, *PWORKER_ITEM_CONTEXT; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(WORKER_ITEM_CONTEXT, GetWorkItemContext) + +// +// Prototypes +// +DRIVER_INITIALIZE DriverEntry; + +EVT_WDF_DRIVER_DEVICE_ADD KbFilter_EvtDeviceAdd; +EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL KbFilter_EvtIoDeviceControlForRawPdo; +EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL KbFilter_EvtIoDeviceControlFromRawPdo; +EVT_WDF_IO_QUEUE_IO_INTERNAL_DEVICE_CONTROL KbFilter_EvtIoInternalDeviceControl; + +NTSTATUS +KbFilter_InitializationRoutine( + IN PVOID InitializationContext, + IN PVOID SynchFuncContext, + IN PI8042_SYNCH_READ_PORT ReadPort, + IN PI8042_SYNCH_WRITE_PORT WritePort, + OUT PBOOLEAN TurnTranslationOn + ); + +BOOLEAN +KbFilter_IsrHook( + PVOID IsrContext, + PKEYBOARD_INPUT_DATA CurrentInput, + POUTPUT_PACKET CurrentOutput, + UCHAR StatusByte, + PUCHAR DataByte, + PBOOLEAN ContinueProcessing, + PKEYBOARD_SCAN_STATE ScanState + ); + +VOID +KbFilter_ServiceCallback( + IN PDEVICE_OBJECT DeviceObject, + IN PKEYBOARD_INPUT_DATA InputDataStart, + IN PKEYBOARD_INPUT_DATA InputDataEnd, + IN OUT PULONG InputDataConsumed + ); + +EVT_WDF_REQUEST_COMPLETION_ROUTINE +KbFilterRequestCompletionRoutine; + + +// +// IOCTL Related defintions +// + +// +// Used to identify kbfilter bus. This guid is used as the enumeration string +// for the device id. +DEFINE_GUID(GUID_BUS_KBFILTER, +0xa65c87f9, 0xbe02, 0x4ed9, 0x92, 0xec, 0x1, 0x2d, 0x41, 0x61, 0x69, 0xfa); +// {A65C87F9-BE02-4ed9-92EC-012D416169FA} + +DEFINE_GUID(GUID_DEVINTERFACE_KBFILTER, +0x3fb7299d, 0x6847, 0x4490, 0xb0, 0xc9, 0x99, 0xe0, 0x98, 0x6a, 0xb8, 0x86); +// {3FB7299D-6847-4490-B0C9-99E0986AB886} + + +#define KBFILTR_DEVICE_ID L"{A65C87F9-BE02-4ed9-92EC-012D416169FA}\\KeyboardFilter\0" + + +typedef struct _RPDO_DEVICE_DATA +{ + + ULONG InstanceNo; + + // + // Queue of the parent device we will forward requests to + // + WDFQUEUE ParentQueue; + +} RPDO_DEVICE_DATA, *PRPDO_DEVICE_DATA; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(RPDO_DEVICE_DATA, PdoGetData) + + +NTSTATUS +KbFiltr_CreateRawPdo( + WDFDEVICE Device, + ULONG InstanceNo +); + + + +#endif // KBFILTER_H + diff --git a/input/kbfiltr/sys/kbfiltr.inx b/input/kbfiltr/sys/kbfiltr.inx new file mode 100644 index 000000000..bc9c438ae --- /dev/null +++ b/input/kbfiltr/sys/kbfiltr.inx @@ -0,0 +1,139 @@ +; kbfiltr.inf +; +; Installation inf for the Device that needs filtering adapter. +; +; (c) Copyright 1999 Microsoft +; + +[Version] +Signature="$Windows NT$" +Provider=%ProviderName% +ClassGUID={4D36E96B-E325-11CE-BFC1-08002BE10318} +Class=Keyboard +DriverVer=07/20/1999, 1.0.0.0 +; Uncomment the following line when you have a valid catalog file. +; If you use bogus catalog file installation will fail. +CatalogFile=KmdfSamples.cat + +[DestinationDirs] +DefaultDestDir = 12 +kbfiltr_CoInstaller_CopyFiles = 11 + +; +; Driver information +; + +[Manufacturer] +%MfgName% = Standard,NT$ARCH$ + +[Standard.NT$ARCH$] +%kbfiltr.DeviceDesc% = kbfiltr, *PNP0BAAD + +; +; General installation section +; + +[kbfiltr.NT] +; perform port related actions from keyboard.inf +Include=keyboard.inf +Needs=STANDARD_Inst + +; Copy the driver over +CopyFiles=kbfiltr.CopyFiles + + +; +; File sections +; + +[kbfiltr.CopyFiles] +kbfiltr.sys + + +; +; Service Installation +; + +[kbfiltr.NT.Services] +AddService = kbfiltr, , kbfiltr_Service_Inst +; Install the port driver and mouclass from keyboard.inf +Include=keyboard.inf +Needs=STANDARD_Inst.Services + +[kbfiltr_Service_Inst] +DisplayName = %kbfiltr.SvcDesc% +ServiceType = 1 ; SERVICE_KERNEL_DRIVER +StartType = 3 ; SERVICE_DEMAND_START +ErrorControl = 0 ; SERVICE_ERROR_IGNORE +ServiceBinary = %12%\kbfiltr.sys + +[kbfiltr.NT.HW] +; Add the device upper filter +AddReg = kbfiltr.HW.AddReg + +; run the directives need by the port driver +Include=keyboard.inf +Needs=STANDARD_Inst.HW + +[kbfiltr.HW.AddReg] +HKR,,"UpperFilters",0x00010000,"kbfiltr" + + +; +; Source file information +; + + +[SourceDisksNames] +1 = %DiskId1%,,, + +[SourceDisksFiles] +kbfiltr.sys = 1 +WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll = 1 + +; +;--- kbfiltr Coinstaller installation ------ +; + +[kbfiltr.NT.CoInstallers] +AddReg=kbfiltr_CoInstaller_AddReg +CopyFiles=kbfiltr_CoInstaller_CopyFiles + +[kbfiltr_CoInstaller_AddReg] +HKR,,CoInstallers32,0x00010000, "WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll,WdfCoInstaller" + +[kbfiltr_CoInstaller_CopyFiles] +WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll + +[kbfiltr.NT.Wdf] +KmdfService = kbfiltr, kbfiltr_wdfsect + +[kbfiltr_wdfsect] +KmdfLibraryVersion = $KMDFVERSION$ + + +[Strings] + +; +; Non-Localizable Strings +; + +REG_SZ = 0x00000000 +REG_MULTI_SZ = 0x00010000 +REG_EXPAND_SZ = 0x00020000 +REG_BINARY = 0x00000001 +REG_DWORD = 0x00010001 + +; +; Localizable Strings +; + +ProviderName = "TODO-Set-Provider" +MfgName = "TODO-Set-Manufacturer" + +kbfiltr.DeviceDesc = "Keyboard Filter Sample Device" + +; Make sure the service description is unique to avoid collision with another INF. +kbfiltr.SvcDesc = "Keyboard Filter Sample Driver" + +DiskId1 = "Keyboard Filter Install Disk" diff --git a/input/kbfiltr/sys/kbfiltr.rc b/input/kbfiltr/sys/kbfiltr.rc new file mode 100644 index 000000000..48695904d --- /dev/null +++ b/input/kbfiltr/sys/kbfiltr.rc @@ -0,0 +1,12 @@ +#include + +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "Example Keyboard Filter Driver" +#define VER_INTERNALNAME_STR "kbfiltr.sys" + +#include "common.ver" + + diff --git a/input/kbfiltr/sys/kbfiltr.vcxproj b/input/kbfiltr/sys/kbfiltr.vcxproj new file mode 100644 index 000000000..3a6ab3aa2 --- /dev/null +++ b/input/kbfiltr/sys/kbfiltr.vcxproj @@ -0,0 +1,159 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {CABE9083-8EBA-41D4-A0D4-F0E3BFDD9E79} + $(MSBuildProjectName) + 1 + Debug + Win32 + {05A1F2D0-FD58-49DA-881D-497087488E7D} + + + + 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)\kbfiltr.inf + + + + kbfiltr + + + kbfiltr + + + kbfiltr + + + kbfiltr + + + + true + Level4 + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\wdmsec.lib;$(DDK_LIB_PATH)\ntstrsafe.lib;$(DDK_LIB_PATH)\rtlver.lib + + + + + true + Level4 + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\wdmsec.lib;$(DDK_LIB_PATH)\ntstrsafe.lib;$(DDK_LIB_PATH)\rtlver.lib + + + + + true + Level4 + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\wdmsec.lib;$(DDK_LIB_PATH)\ntstrsafe.lib;$(DDK_LIB_PATH)\rtlver.lib + + + + + true + Level4 + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\wdmsec.lib;$(DDK_LIB_PATH)\ntstrsafe.lib;$(DDK_LIB_PATH)\rtlver.lib + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/input/kbfiltr/sys/kbfiltr.vcxproj.Filters b/input/kbfiltr/sys/kbfiltr.vcxproj.Filters new file mode 100644 index 000000000..7e848d3b8 --- /dev/null +++ b/input/kbfiltr/sys/kbfiltr.vcxproj.Filters @@ -0,0 +1,39 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {BC392AAF-022B-4F26-BED3-9CAECC2514DA} + + + h;hpp;hxx;hm;inl;inc;xsd + {EE24B2F3-A2C8-487E-A546-D09434A690B9} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {167CCCE9-D380-4055-A99C-40FBD167AAA4} + + + inf;inv;inx;mof;mc; + {6B67A3D6-3C6D-4A28-9198-2603047EB5E6} + + + + + Driver Files + + + + + Source Files + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/input/kbfiltr/sys/public.h b/input/kbfiltr/sys/public.h new file mode 100644 index 000000000..1119c5801 --- /dev/null +++ b/input/kbfiltr/sys/public.h @@ -0,0 +1,11 @@ +#ifndef _PUBLIC_H +#define _PUBLIC_H + +#define IOCTL_INDEX 0x800 + +#define IOCTL_KBFILTR_GET_KEYBOARD_ATTRIBUTES CTL_CODE( FILE_DEVICE_KEYBOARD, \ + IOCTL_INDEX, \ + METHOD_BUFFERED, \ + FILE_READ_DATA) + +#endif diff --git a/input/kbfiltr/sys/rawpdo.c b/input/kbfiltr/sys/rawpdo.c new file mode 100644 index 000000000..e0603211a --- /dev/null +++ b/input/kbfiltr/sys/rawpdo.c @@ -0,0 +1,369 @@ +/*-- + +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: + + RawPdo.c + +Abstract: This module have the code enumerate a raw PDO for every device + the filter attaches to so that it can provide a direct + sideband communication with the usermode application. + + The toaster filter driver sample demonstrates an alternation + approach where you can create one control-device for all the + instances of the filter device. + +Environment: + + Kernel mode only. + +--*/ + +#include "kbfiltr.h" +#include "public.h" + +VOID +KbFilter_EvtIoDeviceControlForRawPdo( + 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 + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + WDFDEVICE parent = WdfIoQueueGetDevice(Queue); + PRPDO_DEVICE_DATA pdoData; + WDF_REQUEST_FORWARD_OPTIONS forwardOptions; + + pdoData = PdoGetData(parent); + + UNREFERENCED_PARAMETER(OutputBufferLength); + UNREFERENCED_PARAMETER(InputBufferLength); + + DebugPrint(("Entered KbFilter_EvtIoDeviceControlForRawPdo\n")); + + // + // Process the ioctl and complete it when you are done. + // Since the queue is configured for serial dispatch, you will + // not receive another ioctl request until you complete this one. + // + + switch (IoControlCode) { + case IOCTL_KBFILTR_GET_KEYBOARD_ATTRIBUTES: + WDF_REQUEST_FORWARD_OPTIONS_INIT(&forwardOptions); + status = WdfRequestForwardToParentDeviceIoQueue(Request, pdoData->ParentQueue, &forwardOptions); + if (!NT_SUCCESS(status)) { + WdfRequestComplete(Request, status); + } + break; + default: + WdfRequestComplete(Request, status); + break; + } + + return; +} + +#define MAX_ID_LEN 128 + +NTSTATUS +KbFiltr_CreateRawPdo( + WDFDEVICE Device, + ULONG InstanceNo + ) +/*++ + +Routine Description: + + This routine creates and initialize a PDO. + +Arguments: + +Return Value: + + NT Status code. + +--*/ +{ + NTSTATUS status; + PWDFDEVICE_INIT pDeviceInit = NULL; + PRPDO_DEVICE_DATA pdoData = NULL; + WDFDEVICE hChild = NULL; + WDF_OBJECT_ATTRIBUTES pdoAttributes; + WDF_DEVICE_PNP_CAPABILITIES pnpCaps; + WDF_IO_QUEUE_CONFIG ioQueueConfig; + WDFQUEUE queue; + WDF_DEVICE_STATE deviceState; + PDEVICE_EXTENSION devExt; + DECLARE_CONST_UNICODE_STRING(deviceId,KBFILTR_DEVICE_ID ); + DECLARE_CONST_UNICODE_STRING(hardwareId,KBFILTR_DEVICE_ID ); + DECLARE_CONST_UNICODE_STRING(deviceLocation,L"Keyboard Filter\0" ); + DECLARE_UNICODE_STRING_SIZE(buffer, MAX_ID_LEN); + + DebugPrint(("Entered KbFiltr_CreateRawPdo\n")); + + // + // Allocate a WDFDEVICE_INIT structure and set the properties + // so that we can create a device object for the child. + // + pDeviceInit = WdfPdoInitAllocate(Device); + + if (pDeviceInit == NULL) { + status = STATUS_INSUFFICIENT_RESOURCES; + goto Cleanup; + } + + // + // Mark the device RAW so that the child device can be started + // and accessed without requiring a function driver. Since we are + // creating a RAW PDO, we must provide a class guid. + // + status = WdfPdoInitAssignRawDevice(pDeviceInit, &GUID_DEVCLASS_KEYBOARD); + if (!NT_SUCCESS(status)) { + goto Cleanup; + } + + // + // Since keyboard is secure device, we must protect ourselves from random + // users sending ioctls and creating trouble. + // + status = WdfDeviceInitAssignSDDLString(pDeviceInit, + &SDDL_DEVOBJ_SYS_ALL_ADM_ALL); + if (!NT_SUCCESS(status)) { + goto Cleanup; + } + + // + // Assign DeviceID - This will be reported to IRP_MN_QUERY_ID/BusQueryDeviceID + // + status = WdfPdoInitAssignDeviceID(pDeviceInit, &deviceId); + if (!NT_SUCCESS(status)) { + goto Cleanup; + } + + // + // For RAW PDO, there is no need to provide BusQueryHardwareIDs + // and BusQueryCompatibleIDs IDs unless we are running on + // Windows 2000. + // + if (!RtlIsNtDdiVersionAvailable(NTDDI_WINXP)) { + // + // On Win2K, we must provide a HWID for the device to get enumerated. + // Since we are providing a HWID, we will have to provide a NULL inf + // to avoid the "found new device" popup and get the device installed + // silently. + // + status = WdfPdoInitAddHardwareID(pDeviceInit, &hardwareId); + if (!NT_SUCCESS(status)) { + goto Cleanup; + } + } + + // + // We could be enumerating more than one children if the filter attaches + // to multiple instances of keyboard, so we must provide a + // BusQueryInstanceID. If we don't, system will throw CA bugcheck. + // + status = RtlUnicodeStringPrintf(&buffer, L"%02d", InstanceNo); + if (!NT_SUCCESS(status)) { + goto Cleanup; + } + + status = WdfPdoInitAssignInstanceID(pDeviceInit, &buffer); + if (!NT_SUCCESS(status)) { + goto Cleanup; + } + + // + // 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 to display in the device manager. + // Since our device is raw device and we don't provide any hardware ID + // to match with an INF, this text will be displayed in the device manager. + // + status = RtlUnicodeStringPrintf(&buffer,L"Keyboard_Filter_%02d", InstanceNo ); + 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. + // + status = WdfPdoInitAddDeviceText(pDeviceInit, + &buffer, + &deviceLocation, + 0x409 + ); + if (!NT_SUCCESS(status)) { + goto Cleanup; + } + + WdfPdoInitSetDefaultLocale(pDeviceInit, 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, RPDO_DEVICE_DATA); + + // + // Set up our queue to allow forwarding of requests to the parent + // This is done so that the cached Keyboard Attributes can be retrieved + // + WdfPdoInitAllowForwardingRequestToParent(pDeviceInit); + + status = WdfDeviceCreate(&pDeviceInit, &pdoAttributes, &hChild); + if (!NT_SUCCESS(status)) { + goto Cleanup; + } + + // + // Get the device context. + // + pdoData = PdoGetData(hChild); + + pdoData->InstanceNo = InstanceNo; + + // + // Get the parent queue we will be forwarding to + // + devExt = FilterGetData(Device); + pdoData->ParentQueue = devExt->rawPdoQueue; + + // + // Configure the default queue associated with the control device object + // to be Serial so that request passed to EvtIoDeviceControl are serialized. + // A default queue gets all the requests that are not + // configure-fowarded using WdfDeviceConfigureRequestDispatching. + // + + WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&ioQueueConfig, + WdfIoQueueDispatchSequential); + + ioQueueConfig.EvtIoDeviceControl = KbFilter_EvtIoDeviceControlForRawPdo; + + status = WdfIoQueueCreate(hChild, + &ioQueueConfig, + WDF_NO_OBJECT_ATTRIBUTES, + &queue // pointer to default queue + ); + if (!NT_SUCCESS(status)) { + DebugPrint( ("WdfIoQueueCreate failed 0x%x\n", status)); + goto Cleanup; + } + + // + // Set some properties for the child device. + // + WDF_DEVICE_PNP_CAPABILITIES_INIT(&pnpCaps); + + pnpCaps.Removable = WdfTrue; + pnpCaps.SurpriseRemovalOK = WdfTrue; + pnpCaps.NoDisplayInUI = WdfTrue; + + pnpCaps.Address = InstanceNo; + pnpCaps.UINumber = InstanceNo; + + WdfDeviceSetPnpCapabilities(hChild, &pnpCaps); + + // + // TODO: In addition to setting NoDisplayInUI in DeviceCaps, we + // have to do the following to hide the device. Following call + // tells the framework to report the device state in + // IRP_MN_QUERY_DEVICE_STATE request. + // + WDF_DEVICE_STATE_INIT(&deviceState); + deviceState.DontDisplayInUI = WdfTrue; + WdfDeviceSetDeviceState(hChild, &deviceState); + + // + // Tell the Framework that this device will need an interface so that + // application can find our device and talk to it. + // + status = WdfDeviceCreateDeviceInterface( + hChild, + &GUID_DEVINTERFACE_KBFILTER, + NULL + ); + + if (!NT_SUCCESS (status)) { + DebugPrint( ("WdfDeviceCreateDeviceInterface failed 0x%x\n", 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, hChild); + if (!NT_SUCCESS(status)) { + goto Cleanup; + } + + // + // pDeviceInit will be freed by WDF. + // + return STATUS_SUCCESS; + +Cleanup: + + DebugPrint(("KbFiltr_CreatePdo failed %x\n", status)); + + // + // Call WdfDeviceInitFree if you encounter an error while initializing + // a new framework device object. If you call WdfDeviceInitFree, + // do not call WdfDeviceCreate. + // + if (pDeviceInit != NULL) { + WdfDeviceInitFree(pDeviceInit); + } + + if(hChild) { + WdfObjectDelete(hChild); + } + + return status; +} + diff --git a/input/moufiltr/Moufiltr.htm b/input/moufiltr/Moufiltr.htm new file mode 100644 index 000000000..85faea725 --- /dev/null +++ b/input/moufiltr/Moufiltr.htm @@ -0,0 +1,699 @@ + + + + + + + + +Moufiltr + + + + + + + + + +
+ +

Moufiltr

+ +

SUMMARY

+ +

This sample is WDF version of WDM +filter driver sample. The WDM version of this sample has been deprecated.

+ +

This driver filters input for a +particular mouse on the system. In its current state, it only hooks into the +mouse packet report chain and the mouse ISR, and does not do any processing of +the data that it sees. (The hooking of the ISR is only available in the +i8042prt stack.) With additions to this current filter-only code base, the +filter could conceivably add, remove, or modify input as needed.

+ +

BUILDING THE SAMPLE

+ +

Click the Free Build +Environment or Checked Build Environment icon under Development Kits program +group to set basic environment variables.

+ +

Change to the directory +containing the device source code, such as CD src\input\moufiltr. +

+ +

Run build -ceZ, or use the macro BLD. This command invokes +the Microsoft make routines to build the components. If the build succeeds, you will find the driver, +moufiltr.sys, +in the binary output directory specified for the build environment. You can get the output path from the buildxxx.log file. If it fails you can find errors and warnings in the buildxxx.err +and buildxxx.wrn respectively, where xxx is either chk or fre +depending on the build environment.

+ +

INSTALLATION

+ +

+Copy the KMDF coinstaller (wdfcoinstallerMMmmm.dll), driver binary and the moufiltr.inf file to a floppy disk or a temp folder.

+ +

This +sample is installed via an .inf file. The .inf file included in this sample is designed to filter a +PS/2 mouse.

+ +

Use the +following rules regarding the .inf file that installs +the filter driver.

+ +
    +
  • The .inf file + must install the class driver (Mouclass) and the + port driver (i8042prt, Mouhid, Sermouse, etc.) by using Msmouse.inf and the INF + directives "Needs" and "Include".
  • +
  • The .inf + file must add the correct registry values for the class and port driver, + as well as using the new directives.
  • +
+ +

To +install this filter, follow these steps:

+ +
    +
  1. Open the Device Manager.
  2. +
  3. Open the Properties of the PS/2 mouse + installed on the system.
  4. +
  5. Click the Driver tab, and then click Update + Driver.
  6. +
  7. Click the Browse my computer for drivers software.
  8. +
  9. Click the Let me pick from a list of device drivers on my computer.
  10. +
  11. Click Have Disk and point to the location of + the .inf file.
  12. +
  13. Proceed through the rest of the install. You will + need to reboot the machine if you are filtering a PS/2 device.
  14. +
+ +

CODE TOUR

+ +

File Manifest

+ +
File           Description
 
Moufiltr.htm   The documentation for this sample (this file).
+
Moufiltr.c     Hooks into the reporting chain, the initialization of a PS/2 mouse, and the PS/2 ISR
+
Moufiltr.h     Definitions
+
Moufiltr.rc    Resources
+
Moufiltr.inx   Sample .inx file 
+
Makefile.inc   A makefile that defines custom build actions.  This includes the conversion of the .INX file into a .INF file
+
 
+ +

Top of page

+ +
 
+ + + + + +
+

 

+
+ +
 
 
+ +

© 2004 Microsoft Corporation

+ +
+ + + + diff --git a/input/moufiltr/ReadMe.md b/input/moufiltr/ReadMe.md new file mode 100644 index 000000000..e0cc32c57 --- /dev/null +++ b/input/moufiltr/ReadMe.md @@ -0,0 +1,20 @@ +Mouse Input WDF Filter Driver (Moufiltr) +======================================== +The Moufiltr sample is an example of a mouse input filter driver. + +This sample is WDF version of the original WDM filter driver sample. The WDM version of this sample has been deprecated. + +This driver filters input for a particular mouse on the system. In its current state, it only hooks into the mouse packet report chain and the mouse ISR, and does not do any processing of the data that it sees. (The hooking of the ISR is only available in the i8042prt stack.) With additions to this current filter-only code base, the filter could conceivably add, remove, or modify input as needed. + +## Universal Windows Driver Compliant +This sample builds a Universal Windows Driver. It uses only APIs and DDIs that are included in OneCoreUAP. + +Installation +------------ + +This sample is installed via an .inf file. The .inf file included in this sample is designed to filter a PS/2 mouse. + +The .inf file must install the class driver (Mouclass) and the port driver (i8042prt, Mouhid, Sermouse, etc.) by using Msmouse.inf and the INF directives "Needs" and "Include". + +The .inf file must add the correct registry values for the class and port driver, as well as using the new directives. + diff --git a/input/moufiltr/moufiltr.c b/input/moufiltr/moufiltr.c new file mode 100644 index 000000000..3e97a7a19 --- /dev/null +++ b/input/moufiltr/moufiltr.c @@ -0,0 +1,504 @@ +/*-- +Copyright (c) 2008 Microsoft Corporation + +Module Name: + + moufiltr.c + +Abstract: + +Environment: + + Kernel mode only- Framework Version + +Notes: + + +--*/ + +#include "moufiltr.h" + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (INIT, DriverEntry) +#pragma alloc_text (PAGE, MouFilter_EvtDeviceAdd) +#pragma alloc_text (PAGE, MouFilter_EvtIoInternalDeviceControl) +#endif + +#pragma warning(push) +#pragma warning(disable:4055) // type case from PVOID to PSERVICE_CALLBACK_ROUTINE +#pragma warning(disable:4152) // function/data pointer conversion in expression + +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. + +--*/ +{ + WDF_DRIVER_CONFIG config; + NTSTATUS status; + + DebugPrint(("Mouse Filter Driver Sample - Driver Framework Edition.\n")); + DebugPrint(("Built %s %s\n", __DATE__, __TIME__)); + + // 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, + MouFilter_EvtDeviceAdd + ); + + // + // Create a framework driver object to represent our driver. + // + status = WdfDriverCreate(DriverObject, + RegistryPath, + WDF_NO_OBJECT_ATTRIBUTES, + &config, + WDF_NO_HANDLE); // hDriver optional + if (!NT_SUCCESS(status)) { + DebugPrint( ("WdfDriverCreate failed with status 0x%x\n", status)); + } + + return status; +} + +NTSTATUS +MouFilter_EvtDeviceAdd( + 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; + NTSTATUS status; + WDFDEVICE hDevice; + WDF_IO_QUEUE_CONFIG ioQueueConfig; + + UNREFERENCED_PARAMETER(Driver); + + PAGED_CODE(); + + DebugPrint(("Enter FilterEvtDeviceAdd \n")); + + // + // 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); + + WdfDeviceInitSetDeviceType(DeviceInit, FILE_DEVICE_MOUSE); + + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&deviceAttributes, + DEVICE_EXTENSION); + + + // + // Create a framework device object. This call will in turn create + // a WDM deviceobject, attach to the lower stack and set the + // appropriate flags and attributes. + // + status = WdfDeviceCreate(&DeviceInit, &deviceAttributes, &hDevice); + if (!NT_SUCCESS(status)) { + DebugPrint(("WdfDeviceCreate failed with status code 0x%x\n", status)); + return status; + } + + + // + // Configure the default queue to be Parallel. Do not use sequential queue + // if this driver is going to be filtering PS2 ports because it can lead to + // deadlock. The PS2 port driver sends a request to the top of the stack when it + // receives an ioctl request and waits for it to be completed. If you use a + // a sequential queue, this request will be stuck in the queue because of the + // outstanding ioctl request sent earlier to the port driver. + // + WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&ioQueueConfig, + WdfIoQueueDispatchParallel); + + // + // Framework by default creates non-power managed queues for + // filter drivers. + // + ioQueueConfig.EvtIoInternalDeviceControl = MouFilter_EvtIoInternalDeviceControl; + + status = WdfIoQueueCreate(hDevice, + &ioQueueConfig, + WDF_NO_OBJECT_ATTRIBUTES, + WDF_NO_HANDLE // pointer to default queue + ); + if (!NT_SUCCESS(status)) { + DebugPrint( ("WdfIoQueueCreate failed 0x%x\n", status)); + return status; + } + + return status; +} + + + +VOID +MouFilter_DispatchPassThrough( + _In_ WDFREQUEST Request, + _In_ WDFIOTARGET Target + ) +/*++ +Routine Description: + + Passes a request on to the lower driver. + + +--*/ +{ + // + // Pass the IRP to the target + // + + WDF_REQUEST_SEND_OPTIONS options; + BOOLEAN ret; + NTSTATUS status = STATUS_SUCCESS; + + // + // 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); + DebugPrint( ("WdfRequestSend failed: 0x%x\n", status)); + WdfRequestComplete(Request, status); + } + + return; +} + +VOID +MouFilter_EvtIoInternalDeviceControl( + 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. + There are two specific control codes that are of interest: + + IOCTL_INTERNAL_MOUSE_CONNECT: + Store the old context and function pointer and replace it with our own. + This makes life much simpler than intercepting IRPs sent by the RIT and + modifying them on the way back up. + + IOCTL_INTERNAL_I8042_HOOK_MOUSE: + Add in the necessary function pointers and context values so that we can + alter how the ps/2 mouse is initialized. + + NOTE: Handling IOCTL_INTERNAL_I8042_HOOK_MOUSE is *NOT* necessary if + all you want to do is filter MOUSE_INPUT_DATAs. You can remove + the handling code and all related device extension fields and + functions to conserve space. + + +--*/ +{ + + PDEVICE_EXTENSION devExt; + PCONNECT_DATA connectData; + PINTERNAL_I8042_HOOK_MOUSE hookMouse; + NTSTATUS status = STATUS_SUCCESS; + WDFDEVICE hDevice; + size_t length; + + UNREFERENCED_PARAMETER(OutputBufferLength); + UNREFERENCED_PARAMETER(InputBufferLength); + + PAGED_CODE(); + + hDevice = WdfIoQueueGetDevice(Queue); + devExt = FilterGetData(hDevice); + + switch (IoControlCode) { + + // + // Connect a mouse class device driver to the port driver. + // + case IOCTL_INTERNAL_MOUSE_CONNECT: + // + // Only allow one connection. + // + if (devExt->UpperConnectData.ClassService != NULL) { + status = STATUS_SHARING_VIOLATION; + break; + } + + // + // Copy the connection parameters to the device extension. + // + status = WdfRequestRetrieveInputBuffer(Request, + sizeof(CONNECT_DATA), + &connectData, + &length); + if(!NT_SUCCESS(status)){ + DebugPrint(("WdfRequestRetrieveInputBuffer failed %x\n", status)); + break; + } + + + devExt->UpperConnectData = *connectData; + + // + // Hook into the report chain. Everytime a mouse packet is reported to + // the system, MouFilter_ServiceCallback will be called + // + connectData->ClassDeviceObject = WdfDeviceWdmGetDeviceObject(hDevice); + connectData->ClassService = MouFilter_ServiceCallback; + + break; + + // + // Disconnect a mouse class device driver from the port driver. + // + case IOCTL_INTERNAL_MOUSE_DISCONNECT: + + // + // Clear the connection parameters in the device extension. + // + // devExt->UpperConnectData.ClassDeviceObject = NULL; + // devExt->UpperConnectData.ClassService = NULL; + + status = STATUS_NOT_IMPLEMENTED; + break; + + // + // Attach this driver to the initialization and byte processing of the + // i8042 (ie PS/2) mouse. This is only necessary if you want to do PS/2 + // specific functions, otherwise hooking the CONNECT_DATA is sufficient + // + case IOCTL_INTERNAL_I8042_HOOK_MOUSE: + + DebugPrint(("hook mouse received!\n")); + + // Get the input buffer from the request + // (Parameters.DeviceIoControl.Type3InputBuffer) + // + status = WdfRequestRetrieveInputBuffer(Request, + sizeof(INTERNAL_I8042_HOOK_MOUSE), + &hookMouse, + &length); + if(!NT_SUCCESS(status)){ + DebugPrint(("WdfRequestRetrieveInputBuffer failed %x\n", status)); + break; + } + + // + // Set isr routine and context and record any values from above this driver + // + devExt->UpperContext = hookMouse->Context; + hookMouse->Context = (PVOID) devExt; + + if (hookMouse->IsrRoutine) { + devExt->UpperIsrHook = hookMouse->IsrRoutine; + } + hookMouse->IsrRoutine = (PI8042_MOUSE_ISR) MouFilter_IsrHook; + + // + // Store all of the other functions we might need in the future + // + devExt->IsrWritePort = hookMouse->IsrWritePort; + devExt->CallContext = hookMouse->CallContext; + devExt->QueueMousePacket = hookMouse->QueueMousePacket; + + status = STATUS_SUCCESS; + break; + + // + // Might want to capture this in the future. For now, then pass it down + // the stack. These queries must be successful for the RIT to communicate + // with the mouse. + // + case IOCTL_MOUSE_QUERY_ATTRIBUTES: + default: + break; + } + + if (!NT_SUCCESS(status)) { + WdfRequestComplete(Request, status); + return ; + } + + MouFilter_DispatchPassThrough(Request,WdfDeviceGetIoTarget(hDevice)); +} + + +BOOLEAN +MouFilter_IsrHook ( + PVOID DeviceExtension, + PMOUSE_INPUT_DATA CurrentInput, + POUTPUT_PACKET CurrentOutput, + UCHAR StatusByte, + PUCHAR DataByte, + PBOOLEAN ContinueProcessing, + PMOUSE_STATE MouseState, + PMOUSE_RESET_SUBSTATE ResetSubState +) +/*++ + +Remarks: + i8042prt specific code, if you are writing a packet only filter driver, you + can remove this function + +Arguments: + + DeviceExtension - Our context passed during IOCTL_INTERNAL_I8042_HOOK_MOUSE + + CurrentInput - Current input packet being formulated by processing all the + interrupts + + CurrentOutput - Current list of bytes being written to the mouse or the + i8042 port. + + StatusByte - Byte read from I/O port 60 when the interrupt occurred + + DataByte - Byte read from I/O port 64 when the interrupt occurred. + This value can be modified and i8042prt will use this value + if ContinueProcessing is TRUE + + ContinueProcessing - If TRUE, i8042prt will proceed with normal processing of + the interrupt. If FALSE, i8042prt will return from the + interrupt after this function returns. Also, if FALSE, + it is this functions responsibilityt to report the input + packet via the function provided in the hook IOCTL or via + queueing a DPC within this driver and calling the + service callback function acquired from the connect IOCTL + +Return Value: + + Status is returned. + + --+*/ +{ + PDEVICE_EXTENSION devExt; + BOOLEAN retVal = TRUE; + + devExt = DeviceExtension; + + if (devExt->UpperIsrHook) { + retVal = (*devExt->UpperIsrHook) (devExt->UpperContext, + CurrentInput, + CurrentOutput, + StatusByte, + DataByte, + ContinueProcessing, + MouseState, + ResetSubState + ); + + if (!retVal || !(*ContinueProcessing)) { + return retVal; + } + } + + *ContinueProcessing = TRUE; + return retVal; +} + + + +VOID +MouFilter_ServiceCallback( + IN PDEVICE_OBJECT DeviceObject, + IN PMOUSE_INPUT_DATA InputDataStart, + IN PMOUSE_INPUT_DATA InputDataEnd, + IN OUT PULONG InputDataConsumed + ) +/*++ + +Routine Description: + + Called when there are mouse packets to report to the RIT. You can do + anything you like to the packets. For instance: + + o Drop a packet altogether + o Mutate the contents of a packet + o Insert packets into the stream + +Arguments: + + DeviceObject - Context passed during the connect IOCTL + + InputDataStart - First packet to be reported + + InputDataEnd - One past the last packet to be reported. Total number of + packets is equal to InputDataEnd - InputDataStart + + InputDataConsumed - Set to the total number of packets consumed by the RIT + (via the function pointer we replaced in the connect + IOCTL) + +Return Value: + + Status is returned. + +--*/ +{ + PDEVICE_EXTENSION devExt; + WDFDEVICE hDevice; + + hDevice = WdfWdmDeviceGetWdfDeviceHandle(DeviceObject); + + devExt = FilterGetData(hDevice); + // + // UpperConnectData must be called at DISPATCH + // + (*(PSERVICE_CALLBACK_ROUTINE) devExt->UpperConnectData.ClassService)( + devExt->UpperConnectData.ClassDeviceObject, + InputDataStart, + InputDataEnd, + InputDataConsumed + ); +} + +#pragma warning(pop) diff --git a/input/moufiltr/moufiltr.h b/input/moufiltr/moufiltr.h new file mode 100644 index 000000000..f94bc57ae --- /dev/null +++ b/input/moufiltr/moufiltr.h @@ -0,0 +1,126 @@ +/*++ +Copyright (c) 2008 Microsoft Corporation + +Module Name: + + moufiltr.h + +Abstract: + + This module contains the common private declarations for the mouse + packet filter + +Environment: + + kernel mode only + +Notes: + + +Revision History: + + +--*/ + +#ifndef MOUFILTER_H +#define MOUFILTER_H + +#include +#include +#include +#include +#include + + + +#if DBG + +#define TRAP() DbgBreakPoint() + +#define DebugPrint(_x_) DbgPrint _x_ + +#else // DBG + +#define TRAP() + +#define DebugPrint(_x_) + +#endif + + +typedef struct _DEVICE_EXTENSION +{ + + // + // Previous hook routine and context + // + PVOID UpperContext; + + PI8042_MOUSE_ISR UpperIsrHook; + + // + // Write to the mouse in the context of MouFilter_IsrHook + // + IN PI8042_ISR_WRITE_PORT IsrWritePort; + + // + // Context for IsrWritePort, QueueMousePacket + // + IN PVOID CallContext; + + // + // Queue the current packet (ie the one passed into MouFilter_IsrHook) + // to be reported to the class driver + // + IN PI8042_QUEUE_PACKET QueueMousePacket; + + // + // The real connect data that this driver reports to + // + CONNECT_DATA UpperConnectData; + + +} DEVICE_EXTENSION, *PDEVICE_EXTENSION; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(DEVICE_EXTENSION, + FilterGetData) + +// +// Prototypes +// +DRIVER_INITIALIZE DriverEntry; + +EVT_WDF_DRIVER_DEVICE_ADD MouFilter_EvtDeviceAdd; +EVT_WDF_IO_QUEUE_IO_INTERNAL_DEVICE_CONTROL MouFilter_EvtIoInternalDeviceControl; + + + +VOID +MouFilter_DispatchPassThrough( + _In_ WDFREQUEST Request, + _In_ WDFIOTARGET Target + ); + +BOOLEAN +MouFilter_IsrHook ( + PVOID DeviceExtension, + PMOUSE_INPUT_DATA CurrentInput, + POUTPUT_PACKET CurrentOutput, + UCHAR StatusByte, + PUCHAR DataByte, + PBOOLEAN ContinueProcessing, + PMOUSE_STATE MouseState, + PMOUSE_RESET_SUBSTATE ResetSubState +); + +VOID +MouFilter_ServiceCallback( + IN PDEVICE_OBJECT DeviceObject, + IN PMOUSE_INPUT_DATA InputDataStart, + IN PMOUSE_INPUT_DATA InputDataEnd, + IN OUT PULONG InputDataConsumed + ); + +#endif // MOUFILTER_H + + diff --git a/input/moufiltr/moufiltr.inx b/input/moufiltr/moufiltr.inx new file mode 100644 index 000000000..50878d682 --- /dev/null +++ b/input/moufiltr/moufiltr.inx @@ -0,0 +1,139 @@ +; moufiltr.inf +; +; Installation inf for the Device that needs filtering adapter. +; +; (c) Copyright 2008 Microsoft +; + +[Version] +Signature="$Windows NT$" +Provider=%ProviderName% +ClassGUID={4D36E96F-E325-11CE-BFC1-08002BE10318} +Class=Mouse +DriverVer=02/05/2008, 1.0.0.0 +; Uncomment the following line when you have a valid catalog file. +; If you use bogus catalog file installation will fail. +CatalogFile=KmdfSamples.cat + +[DestinationDirs] +DefaultDestDir = 12 +moufiltr_CoInstaller_CopyFiles = 11 + +; +; Driver information +; + +[Manufacturer] +%MfgName% = Standard,NT$ARCH$ + +[Standard.NT$ARCH$] +%moufiltr.DeviceDesc% = moufiltr, *PNP0FAKE + +; +; General installation section +; + +[moufiltr.NT] +; perform port related actions from mouse.inf +Include=msmouse.inf +Needs=PS2_Inst + +; Copy the driver over +CopyFiles=moufiltr.CopyFiles + + +; +; File sections +; + +[moufiltr.CopyFiles] +moufiltr.sys + + +; +; Service Installation +; + +[moufiltr.NT.Services] +AddService = moufiltr, , moufiltr_Service_Inst +; Install the port driver and mouclass from msmouse.inf +Include=msmouse.inf +Needs=PS2_Inst.Services + +[moufiltr_Service_Inst] +DisplayName = %moufiltr.SvcDesc% +ServiceType = 1 ; SERVICE_KERNEL_DRIVER +StartType = 3 ; SERVICE_DEMAND_START +ErrorControl = 0 ; SERVICE_ERROR_IGNORE +ServiceBinary = %12%\moufiltr.sys + +[moufiltr.NT.HW] +; Add the device upper filter +AddReg = moufiltr.HW.AddReg + +; run the directives need by the port driver +Include=msmouse.inf +Needs=PS2_Inst.HW + +[moufiltr.HW.AddReg] +HKR,,"UpperFilters",0x00010000,"moufiltr" + + +; +; Source file information +; + + +[SourceDisksNames] +1 = %DiskId1%,,, + +[SourceDisksFiles] +moufiltr.sys = 1 +WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll = 1 + +; +;--- moufiltr Coinstaller installation ------ +; + +[moufiltr.NT.CoInstallers] +AddReg=moufiltr_CoInstaller_AddReg +CopyFiles=moufiltr_CoInstaller_CopyFiles + +[moufiltr_CoInstaller_AddReg] +HKR,,CoInstallers32,0x00010000, "WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll,WdfCoInstaller" + +[moufiltr_CoInstaller_CopyFiles] +WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll + +[moufiltr.NT.Wdf] +KmdfService = moufiltr, moufiltr_wdfsect + +[moufiltr_wdfsect] +KmdfLibraryVersion = $KMDFVERSION$ + + +[Strings] + +; +; Non-Localizable Strings +; + +REG_SZ = 0x00000000 +REG_MULTI_SZ = 0x00010000 +REG_EXPAND_SZ = 0x00020000 +REG_BINARY = 0x00000001 +REG_DWORD = 0x00010001 + +; +; Localizable Strings +; + +ProviderName = "TODO-Set-Provider" +MfgName = "TODO-Set-Manufacturer" + +moufiltr.DeviceDesc = "Mouse Filter Sample Device" + +; Make sure the service description is unique to avoid collision with another INF. +moufiltr.SvcDesc = "Mouse Filter Sample Driver" + +DiskId1 = "Mouse Filter Install Disk" diff --git a/input/moufiltr/moufiltr.rc b/input/moufiltr/moufiltr.rc new file mode 100644 index 000000000..af1df62fd --- /dev/null +++ b/input/moufiltr/moufiltr.rc @@ -0,0 +1,11 @@ +#include + +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "Mouse Filter Driver" +#define VER_INTERNALNAME_STR "moufiltr.sys" + +#include "common.ver" + diff --git a/input/moufiltr/moufiltr.sln b/input/moufiltr/moufiltr.sln new file mode 100644 index 000000000..0f1efdd91 --- /dev/null +++ b/input/moufiltr/moufiltr.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}") = "moufiltr", "moufiltr.vcxproj", "{28C26795-633E-42B3-BAC7-F276679CDF90}" +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 + {28C26795-633E-42B3-BAC7-F276679CDF90}.Debug|Win32.ActiveCfg = Debug|Win32 + {28C26795-633E-42B3-BAC7-F276679CDF90}.Debug|Win32.Build.0 = Debug|Win32 + {28C26795-633E-42B3-BAC7-F276679CDF90}.Release|Win32.ActiveCfg = Release|Win32 + {28C26795-633E-42B3-BAC7-F276679CDF90}.Release|Win32.Build.0 = Release|Win32 + {28C26795-633E-42B3-BAC7-F276679CDF90}.Debug|x64.ActiveCfg = Debug|x64 + {28C26795-633E-42B3-BAC7-F276679CDF90}.Debug|x64.Build.0 = Debug|x64 + {28C26795-633E-42B3-BAC7-F276679CDF90}.Release|x64.ActiveCfg = Release|x64 + {28C26795-633E-42B3-BAC7-F276679CDF90}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/input/moufiltr/moufiltr.vcxproj b/input/moufiltr/moufiltr.vcxproj new file mode 100644 index 000000000..9840820e2 --- /dev/null +++ b/input/moufiltr/moufiltr.vcxproj @@ -0,0 +1,146 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {28C26795-633E-42B3-BAC7-F276679CDF90} + $(MSBuildProjectName) + 1 + Debug + Win32 + {FFF2D813-0AFF-43E7-9070-27C799AE8C4A} + + + + 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)\moufiltr.inf + + + + moufiltr + + + moufiltr + + + moufiltr + + + moufiltr + + + + true + Level4 + + + + + + + true + Level4 + + + + + + + true + Level4 + + + + + + + true + Level4 + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/input/moufiltr/moufiltr.vcxproj.Filters b/input/moufiltr/moufiltr.vcxproj.Filters new file mode 100644 index 000000000..100d3c05c --- /dev/null +++ b/input/moufiltr/moufiltr.vcxproj.Filters @@ -0,0 +1,36 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {73708C87-AE82-4681-9C01-042DF504D5E3} + + + h;hpp;hxx;hm;inl;inc;xsd + {3531B16D-3C4F-4E1C-ABD4-50E16BFD7670} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {3FD872CD-6DDF-47C5-ADEC-958E7540E672} + + + inf;inv;inx;mof;mc; + {E86BBEC3-C79D-42D9-98BA-B087184AAB24} + + + + + Driver Files + + + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/network/config/bindview/bindview.sln b/network/config/bindview/bindview.sln index 921d1d4f7..edfa137ae 100644 --- a/network/config/bindview/bindview.sln +++ b/network/config/bindview/bindview.sln @@ -3,7 +3,7 @@ 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", "{34897AE3-000C-4986-8DA8-45DA5317354F}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bindview", "bindview.vcxproj", "{E60EDFAC-946E-4410-8969-0DD6EF6083C4}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {34897AE3-000C-4986-8DA8-45DA5317354F}.Debug|Win32.ActiveCfg = Debug|Win32 - {34897AE3-000C-4986-8DA8-45DA5317354F}.Debug|Win32.Build.0 = Debug|Win32 - {34897AE3-000C-4986-8DA8-45DA5317354F}.Release|Win32.ActiveCfg = Release|Win32 - {34897AE3-000C-4986-8DA8-45DA5317354F}.Release|Win32.Build.0 = Release|Win32 - {34897AE3-000C-4986-8DA8-45DA5317354F}.Debug|x64.ActiveCfg = Debug|x64 - {34897AE3-000C-4986-8DA8-45DA5317354F}.Debug|x64.Build.0 = Debug|x64 - {34897AE3-000C-4986-8DA8-45DA5317354F}.Release|x64.ActiveCfg = Release|x64 - {34897AE3-000C-4986-8DA8-45DA5317354F}.Release|x64.Build.0 = Release|x64 + {E60EDFAC-946E-4410-8969-0DD6EF6083C4}.Debug|Win32.ActiveCfg = Debug|Win32 + {E60EDFAC-946E-4410-8969-0DD6EF6083C4}.Debug|Win32.Build.0 = Debug|Win32 + {E60EDFAC-946E-4410-8969-0DD6EF6083C4}.Release|Win32.ActiveCfg = Release|Win32 + {E60EDFAC-946E-4410-8969-0DD6EF6083C4}.Release|Win32.Build.0 = Release|Win32 + {E60EDFAC-946E-4410-8969-0DD6EF6083C4}.Debug|x64.ActiveCfg = Debug|x64 + {E60EDFAC-946E-4410-8969-0DD6EF6083C4}.Debug|x64.Build.0 = Debug|x64 + {E60EDFAC-946E-4410-8969-0DD6EF6083C4}.Release|x64.ActiveCfg = Release|x64 + {E60EDFAC-946E-4410-8969-0DD6EF6083C4}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/network/config/bindview/bindview.vcxproj b/network/config/bindview/bindview.vcxproj index 8bb389285..ae65f8904 100644 --- a/network/config/bindview/bindview.vcxproj +++ b/network/config/bindview/bindview.vcxproj @@ -19,11 +19,11 @@
- {34897AE3-000C-4986-8DA8-45DA5317354F} + {E60EDFAC-946E-4410-8969-0DD6EF6083C4} $(MSBuildProjectName) Debug Win32 - {142C0734-824B-4C41-9B69-E7F6D7214F0F} + {F75CE060-85A8-443C-9041-CB0C7CEA7EA7} @@ -197,7 +197,6 @@ - diff --git a/network/config/bindview/bindview.vcxproj.Filters b/network/config/bindview/bindview.vcxproj.Filters index 822557720..ceeb0d013 100644 --- a/network/config/bindview/bindview.vcxproj.Filters +++ b/network/config/bindview/bindview.vcxproj.Filters @@ -3,15 +3,15 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {DD6BF01A-E882-42FA-9582-205A36B5FDE9} + {214E38B2-429C-45AE-ACAE-2A37535D22D9} h;hpp;hxx;hm;inl;inc;xsd - {3AA8C4B2-E02E-4C24-BF11-8C30E62ED7FB} + {5BE39D34-8513-4474-B353-E1B582C95C04} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {860821E6-FF0C-4292-B8E6-144043F7E832} + {4CF860BF-19B6-4CFF-986A-76D1F51834CC} diff --git a/network/modem/fakemodem/fakemodem.sln b/network/modem/fakemodem/fakemodem.sln index e521285a9..9d5f20f82 100644 --- a/network/modem/fakemodem/fakemodem.sln +++ b/network/modem/fakemodem/fakemodem.sln @@ -3,7 +3,7 @@ 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", "{021EA388-9BD1-4B52-A954-AF1ECC14959A}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fakemodem", "fakemodem.vcxproj", "{D5EE22D4-47C4-48F2-8210-41F8D1B80B62}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {021EA388-9BD1-4B52-A954-AF1ECC14959A}.Debug|Win32.ActiveCfg = Debug|Win32 - {021EA388-9BD1-4B52-A954-AF1ECC14959A}.Debug|Win32.Build.0 = Debug|Win32 - {021EA388-9BD1-4B52-A954-AF1ECC14959A}.Release|Win32.ActiveCfg = Release|Win32 - {021EA388-9BD1-4B52-A954-AF1ECC14959A}.Release|Win32.Build.0 = Release|Win32 - {021EA388-9BD1-4B52-A954-AF1ECC14959A}.Debug|x64.ActiveCfg = Debug|x64 - {021EA388-9BD1-4B52-A954-AF1ECC14959A}.Debug|x64.Build.0 = Debug|x64 - {021EA388-9BD1-4B52-A954-AF1ECC14959A}.Release|x64.ActiveCfg = Release|x64 - {021EA388-9BD1-4B52-A954-AF1ECC14959A}.Release|x64.Build.0 = Release|x64 + {D5EE22D4-47C4-48F2-8210-41F8D1B80B62}.Debug|Win32.ActiveCfg = Debug|Win32 + {D5EE22D4-47C4-48F2-8210-41F8D1B80B62}.Debug|Win32.Build.0 = Debug|Win32 + {D5EE22D4-47C4-48F2-8210-41F8D1B80B62}.Release|Win32.ActiveCfg = Release|Win32 + {D5EE22D4-47C4-48F2-8210-41F8D1B80B62}.Release|Win32.Build.0 = Release|Win32 + {D5EE22D4-47C4-48F2-8210-41F8D1B80B62}.Debug|x64.ActiveCfg = Debug|x64 + {D5EE22D4-47C4-48F2-8210-41F8D1B80B62}.Debug|x64.Build.0 = Debug|x64 + {D5EE22D4-47C4-48F2-8210-41F8D1B80B62}.Release|x64.ActiveCfg = Release|x64 + {D5EE22D4-47C4-48F2-8210-41F8D1B80B62}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/network/modem/fakemodem/fakemodem.vcxproj b/network/modem/fakemodem/fakemodem.vcxproj index 0c24e2d3c..6cfa9ad0d 100644 --- a/network/modem/fakemodem/fakemodem.vcxproj +++ b/network/modem/fakemodem/fakemodem.vcxproj @@ -19,12 +19,12 @@ - {021EA388-9BD1-4B52-A954-AF1ECC14959A} + {D5EE22D4-47C4-48F2-8210-41F8D1B80B62} $(MSBuildProjectName) 1 Debug Win32 - {40F9DBFF-3A37-43BB-A11E-8C4F0672CAE2} + {B351256B-F713-442B-BD96-EBAA08ADC277} @@ -154,7 +154,6 @@ - diff --git a/network/modem/fakemodem/fakemodem.vcxproj.Filters b/network/modem/fakemodem/fakemodem.vcxproj.Filters index 5a8b5bf55..32888bd3f 100644 --- a/network/modem/fakemodem/fakemodem.vcxproj.Filters +++ b/network/modem/fakemodem/fakemodem.vcxproj.Filters @@ -3,25 +3,22 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {B7848512-B788-41BD-B77C-7E17CD2BD418} + {A97FFE22-8053-4254-B732-19813EA56D71} h;hpp;hxx;hm;inl;inc;xsd - {3C0E579D-D582-4FF2-A923-CF6AA3816295} + {0EFEA207-D953-4022-8525-8F1E692F1396} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {7868BAAE-9CBF-493B-A1FA-7B4DC86F11D3} + {A02D00E6-E88A-4342-908F-28483AEF11A5} inf;inv;inx;mof;mc; - {204657EA-2714-45FB-A126-7BE909214868} + {26FC65B7-1969-4954-BAE5-68FD772BE13E} - - Driver Files - Driver Files diff --git a/network/modem/fakemodem/mdmfake.inx b/network/modem/fakemodem/mdmfake.inx index 4342404a1..404679ce5 100644 --- a/network/modem/fakemodem/mdmfake.inx +++ b/network/modem/fakemodem/mdmfake.inx @@ -27,28 +27,25 @@ Signature="$WINDOWS NT$" Class=Modem ClassGUID={4D36E96D-E325-11CE-BFC1-08002BE10318} -Provider=%Mfg% +Provider=%ProviderString% 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$ +%ManufacturerName% = Standard,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$] +[Standard.NT$ARCH$] %ModemX% = ModemX, {b85b7c50-6a01-11d2-b841-00c04fad5171}\fakemodem ;------------------------------------------------------------------------------------------------------- @@ -260,8 +257,8 @@ HKR, Responses, "NO DIALTONE", 1, 05, 00, 00,00,00,00, 0 ; For example, ModemX will appear as "FakeModem DDK Sample controllerless driver" [Strings] -mfg = "Microsoft" +ProviderString = "TODO-Set-Provider" +ManufacturerName = "TODO-Set-Manufacturer" FakeDisk = "Fake Modem Install Disk" -Generic = "(Standard Modem Types)" ModemX = "FakeModem DDK Sample controllerless driver" ServiceName = "Fakemodem" diff --git a/network/ndis/extension/base/SxBase.c b/network/ndis/extension/base/SxBase.c index b76ef7a41..f3a9de11b 100644 --- a/network/ndis/extension/base/SxBase.c +++ b/network/ndis/extension/base/SxBase.c @@ -29,17 +29,17 @@ NDIS_STRING SxExtensionGuid; NDIS_STATUS SxpNdisProcessSetOid( - __in PSX_SWITCH_OBJECT Switch, - __inout PNDIS_OID_REQUEST OidRequest, - __out PBOOLEAN Complete + _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 + _In_ PSX_SWITCH_OBJECT Switch, + _Inout_ PNDIS_OID_REQUEST OidRequest, + _Out_ PBOOLEAN Complete, + _Out_ PULONG BytesNeeded ); @@ -852,9 +852,9 @@ SxNdisStatus( NDIS_STATUS SxpNdisProcessSetOid( - __in PSX_SWITCH_OBJECT Switch, - __inout PNDIS_OID_REQUEST OidRequest, - __out PBOOLEAN Complete + _In_ PSX_SWITCH_OBJECT Switch, + _Inout_ PNDIS_OID_REQUEST OidRequest, + _Out_ PBOOLEAN Complete ) { NDIS_STATUS status = NDIS_STATUS_SUCCESS; @@ -1150,10 +1150,10 @@ SxpNdisProcessSetOid( NDIS_STATUS SxpNdisProcessMethodOid( - __in PSX_SWITCH_OBJECT Switch, - __inout PNDIS_OID_REQUEST OidRequest, - __out PBOOLEAN Complete, - __out PULONG BytesNeeded + _In_ PSX_SWITCH_OBJECT Switch, + _Inout_ PNDIS_OID_REQUEST OidRequest, + _Out_ PBOOLEAN Complete, + _Out_ PULONG BytesNeeded ) { NDIS_STATUS status = NDIS_STATUS_SUCCESS; @@ -1325,9 +1325,9 @@ SxpNdisProcessMethodOid( VOID SxpNdisCompleteInternalOidRequest( - __in PSX_SWITCH_OBJECT Switch, - __in PNDIS_OID_REQUEST NdisRequest, - __in NDIS_STATUS Status + _In_ PSX_SWITCH_OBJECT Switch, + _In_ PNDIS_OID_REQUEST NdisRequest, + _In_ NDIS_STATUS Status ) /*++ diff --git a/network/ndis/extension/base/SxBase.h b/network/ndis/extension/base/SxBase.h index 1e57b0f17..5d34ee03e 100644 --- a/network/ndis/extension/base/SxBase.h +++ b/network/ndis/extension/base/SxBase.h @@ -129,9 +129,9 @@ FILTER_NET_PNP_EVENT SxNdisNetPnPEvent; VOID SxpNdisCompleteInternalOidRequest( - __in PSX_SWITCH_OBJECT Switch, - __in PNDIS_OID_REQUEST NdisRequest, - __in NDIS_STATUS Status + _In_ PSX_SWITCH_OBJECT Switch, + _In_ PNDIS_OID_REQUEST NdisRequest, + _In_ NDIS_STATUS Status ); diff --git a/network/ndis/extension/base/sxbase.vcxproj b/network/ndis/extension/base/sxbase.vcxproj index d49f21c60..13a49e61a 100644 --- a/network/ndis/extension/base/sxbase.vcxproj +++ b/network/ndis/extension/base/sxbase.vcxproj @@ -11,11 +11,11 @@ - {0AF344BA-2F72-4E63-85D3-6C649C493113} + {B838EAEA-2046-4E93-AC2D-CB203C9D4036} $(MSBuildProjectName) Debug x64 - {5E6D6149-F14D-47BE-A505-3C14582EB73D} + {F44EC1A6-6C23-4D60-B89C-39A239AABA69} @@ -116,7 +116,6 @@ - diff --git a/network/ndis/extension/base/sxbase.vcxproj.Filters b/network/ndis/extension/base/sxbase.vcxproj.Filters index fa2b2bc8b..a75c71be4 100644 --- a/network/ndis/extension/base/sxbase.vcxproj.Filters +++ b/network/ndis/extension/base/sxbase.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {06AF9F9E-A01B-4811-AC1D-93436B508BA2} + {A16DB2BA-3F19-47BD-B171-7D2A069155B0} h;hpp;hxx;hm;inl;inc;xsd - {8622716C-DDFC-41F0-AA13-E1FA6FF65319} + {594FE438-B8A6-4B44-B108-9504E84B573E} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {A71D839D-75EE-4F0F-AEF8-2892391ED123} + {BCEFAB4C-06AA-4C9F-81B8-057321327165} inf;inv;inx;mof;mc; - {6647DA6B-FEFD-492E-B3FD-A3AFCDAC7AFD} + {3B1FC56B-F654-4AA2-9F91-AB9E496D8160} diff --git a/network/ndis/extension/extensions.sln b/network/ndis/extension/extensions.sln index 2ebff25c5..75fb7b839 100644 --- a/network/ndis/extension/extensions.sln +++ b/network/ndis/extension/extensions.sln @@ -3,24 +3,24 @@ 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", "{91A9E693-3D97-4069-B2A4-84CD10712B8E}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Base", "Base", "{28219E37-D912-456C-A3F7-03912CD5C2C4}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Forward", "Forward", "{5CC88476-FA8E-43F7-BE93-29147910AE87}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Forward", "Forward", "{B3C1B1A2-7677-428B-9D67-61DEE9DF4035}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{0357B6CB-FF60-41D3-8879-A04837F014CD}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{9212747D-6D63-4D9C-8961-FA8AD5A2DEDD}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Passthrough", "Passthrough", "{7A74F8F6-D891-40BB-9CB5-308ACDA3B778}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Passthrough", "Passthrough", "{875DC1CC-D5FA-4D39-94DE-A059F05174C9}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sxbase", "base\sxbase.vcxproj", "{0AF344BA-2F72-4E63-85D3-6C649C493113}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sxbase", "base\sxbase.vcxproj", "{B838EAEA-2046-4E93-AC2D-CB203C9D4036}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "msforwardext", "samples\forward\msforwardext.vcxproj", "{947BD508-8C40-4109-AA40-0E6B54FD7E07}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "msforwardext", "samples\forward\msforwardext.vcxproj", "{D112446E-9C5A-42CF-9C9B-CDA3E7DC5B3A}" ProjectSection(ProjectDependencies) = postProject - {0AF344BA-2F72-4E63-85D3-6C649C493113} = {0AF344BA-2F72-4E63-85D3-6C649C493113} + {B838EAEA-2046-4E93-AC2D-CB203C9D4036} = {B838EAEA-2046-4E93-AC2D-CB203C9D4036} EndProjectSection EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mspassthroughext", "samples\passthrough\mspassthroughext.vcxproj", "{33DC46DA-90C8-42E0-9411-8CE8A02519C2}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mspassthroughext", "samples\passthrough\mspassthroughext.vcxproj", "{C60917BE-A59D-4FDB-BEFD-98DC106BDCA9}" ProjectSection(ProjectDependencies) = postProject - {0AF344BA-2F72-4E63-85D3-6C649C493113} = {0AF344BA-2F72-4E63-85D3-6C649C493113} + {B838EAEA-2046-4E93-AC2D-CB203C9D4036} = {B838EAEA-2046-4E93-AC2D-CB203C9D4036} EndProjectSection EndProject Global @@ -29,27 +29,27 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {0AF344BA-2F72-4E63-85D3-6C649C493113}.Debug|x64.ActiveCfg = Debug|x64 - {0AF344BA-2F72-4E63-85D3-6C649C493113}.Debug|x64.Build.0 = Debug|x64 - {0AF344BA-2F72-4E63-85D3-6C649C493113}.Release|x64.ActiveCfg = Release|x64 - {0AF344BA-2F72-4E63-85D3-6C649C493113}.Release|x64.Build.0 = Release|x64 - {947BD508-8C40-4109-AA40-0E6B54FD7E07}.Debug|x64.ActiveCfg = Debug|x64 - {947BD508-8C40-4109-AA40-0E6B54FD7E07}.Debug|x64.Build.0 = Debug|x64 - {947BD508-8C40-4109-AA40-0E6B54FD7E07}.Release|x64.ActiveCfg = Release|x64 - {947BD508-8C40-4109-AA40-0E6B54FD7E07}.Release|x64.Build.0 = Release|x64 - {33DC46DA-90C8-42E0-9411-8CE8A02519C2}.Debug|x64.ActiveCfg = Debug|x64 - {33DC46DA-90C8-42E0-9411-8CE8A02519C2}.Debug|x64.Build.0 = Debug|x64 - {33DC46DA-90C8-42E0-9411-8CE8A02519C2}.Release|x64.ActiveCfg = Release|x64 - {33DC46DA-90C8-42E0-9411-8CE8A02519C2}.Release|x64.Build.0 = Release|x64 + {B838EAEA-2046-4E93-AC2D-CB203C9D4036}.Debug|x64.ActiveCfg = Debug|x64 + {B838EAEA-2046-4E93-AC2D-CB203C9D4036}.Debug|x64.Build.0 = Debug|x64 + {B838EAEA-2046-4E93-AC2D-CB203C9D4036}.Release|x64.ActiveCfg = Release|x64 + {B838EAEA-2046-4E93-AC2D-CB203C9D4036}.Release|x64.Build.0 = Release|x64 + {D112446E-9C5A-42CF-9C9B-CDA3E7DC5B3A}.Debug|x64.ActiveCfg = Debug|x64 + {D112446E-9C5A-42CF-9C9B-CDA3E7DC5B3A}.Debug|x64.Build.0 = Debug|x64 + {D112446E-9C5A-42CF-9C9B-CDA3E7DC5B3A}.Release|x64.ActiveCfg = Release|x64 + {D112446E-9C5A-42CF-9C9B-CDA3E7DC5B3A}.Release|x64.Build.0 = Release|x64 + {C60917BE-A59D-4FDB-BEFD-98DC106BDCA9}.Debug|x64.ActiveCfg = Debug|x64 + {C60917BE-A59D-4FDB-BEFD-98DC106BDCA9}.Debug|x64.Build.0 = Debug|x64 + {C60917BE-A59D-4FDB-BEFD-98DC106BDCA9}.Release|x64.ActiveCfg = Release|x64 + {C60917BE-A59D-4FDB-BEFD-98DC106BDCA9}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {0AF344BA-2F72-4E63-85D3-6C649C493113} = {91A9E693-3D97-4069-B2A4-84CD10712B8E} - {947BD508-8C40-4109-AA40-0E6B54FD7E07} = {5CC88476-FA8E-43F7-BE93-29147910AE87} - {33DC46DA-90C8-42E0-9411-8CE8A02519C2} = {7A74F8F6-D891-40BB-9CB5-308ACDA3B778} - {5CC88476-FA8E-43F7-BE93-29147910AE87} = {0357B6CB-FF60-41D3-8879-A04837F014CD} - {7A74F8F6-D891-40BB-9CB5-308ACDA3B778} = {0357B6CB-FF60-41D3-8879-A04837F014CD} + {B838EAEA-2046-4E93-AC2D-CB203C9D4036} = {28219E37-D912-456C-A3F7-03912CD5C2C4} + {D112446E-9C5A-42CF-9C9B-CDA3E7DC5B3A} = {B3C1B1A2-7677-428B-9D67-61DEE9DF4035} + {C60917BE-A59D-4FDB-BEFD-98DC106BDCA9} = {875DC1CC-D5FA-4D39-94DE-A059F05174C9} + {B3C1B1A2-7677-428B-9D67-61DEE9DF4035} = {9212747D-6D63-4D9C-8961-FA8AD5A2DEDD} + {875DC1CC-D5FA-4D39-94DE-A059F05174C9} = {9212747D-6D63-4D9C-8961-FA8AD5A2DEDD} EndGlobalSection EndGlobal diff --git a/network/ndis/extension/samples/forward/MsForwardExt.c b/network/ndis/extension/samples/forward/MsForwardExt.c index 147a36943..bf6d337a6 100644 --- a/network/ndis/extension/samples/forward/MsForwardExt.c +++ b/network/ndis/extension/samples/forward/MsForwardExt.c @@ -1530,7 +1530,9 @@ Routine Description: // Ethernet Header is a guaranteed safe access. // curMdl = (NET_BUFFER_LIST_FIRST_NB(curNbl))->CurrentMdl; - curBuffer = MmGetSystemAddressForMdlSafe(curMdl, LowPagePriority); + curBuffer = MmGetSystemAddressForMdlSafe( + curMdl, + LowPagePriority | MdlMappingNoExecute); curHeader = (PMSFORWARD_ETHERNET_HEADER) (curBuffer + (NET_BUFFER_LIST_FIRST_NB(curNbl))->CurrentMdlOffset); diff --git a/network/ndis/extension/samples/forward/msforwardext.inf b/network/ndis/extension/samples/forward/msforwardext.inf index dd849d8b9..9d3d587c2 100644 --- a/network/ndis/extension/samples/forward/msforwardext.inf +++ b/network/ndis/extension/samples/forward/msforwardext.inf @@ -6,20 +6,20 @@ Signature = "$Windows NT$" Class = NetService ClassGUID = {4D36E974-E325-11CE-BFC1-08002BE10318} -Provider = %Msft% +Provider = %ProviderString% CatalogFile = msforwardext.cat DriverVer = 07/29/2011,2.0 [Manufacturer] -%Msft%=MSFT,NTx86,NTia64,NTamd64 +%ManufacturerName%=Standard,NTx86,NTia64,NTamd64 -[MSFT.NTx86] +[Standard.NTx86] %MSForwardExt_Desc%=Install, MS_forwardext -[MSFT.NTia64] +[Standard.NTia64] %MSForwardExt_Desc%=Install, MS_forwardext -[MSFT.NTamd64] +[Standard.NTamd64] %MSForwardExt_Desc%=Install, MS_forwardext ;------------------------------------------------------------------------- @@ -74,12 +74,12 @@ 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" +ProviderString = "TODO-Set-Provider" +ManufacturerName="TODO-Set-Manufacturer" +MSForwardExt_Desc = "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 index 5117b28a9..be2eea6b6 100644 --- a/network/ndis/extension/samples/forward/msforwardext.vcxproj +++ b/network/ndis/extension/samples/forward/msforwardext.vcxproj @@ -11,11 +11,11 @@ - {947BD508-8C40-4109-AA40-0E6B54FD7E07} + {D112446E-9C5A-42CF-9C9B-CDA3E7DC5B3A} $(MSBuildProjectName) Debug x64 - {B5988C94-9BF7-4CAC-AC9A-12A76777AAA2} + {EA90456F-BB1A-4F16-B579-F296EF617DD6} @@ -113,7 +113,6 @@ - diff --git a/network/ndis/extension/samples/forward/msforwardext.vcxproj.Filters b/network/ndis/extension/samples/forward/msforwardext.vcxproj.Filters index 41e0eaf26..b9d7e74c2 100644 --- a/network/ndis/extension/samples/forward/msforwardext.vcxproj.Filters +++ b/network/ndis/extension/samples/forward/msforwardext.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {C51012E1-9AEC-4F0F-A787-C2268CE8C1A9} + {F68973A5-CE87-470D-BA57-416A25240DF3} h;hpp;hxx;hm;inl;inc;xsd - {DAABAD75-23CE-441B-8FBE-035AE3D33165} + {97E8830C-C990-4BF0-A9E1-C9E672F737B4} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {ED47846F-6D56-4808-A9C1-D6868F535E4B} + {9274D3AF-075C-4C05-A5FF-E9D3DD0229C2} inf;inv;inx;mof;mc; - {AFD09367-9B69-4871-AB79-6D868F6FB8FD} + {34ADA108-C24C-4574-921B-055BF882ADC6} diff --git a/network/ndis/extension/samples/passthrough/mspassthroughext.inf b/network/ndis/extension/samples/passthrough/mspassthroughext.inf index 6a6b941d2..099851323 100644 --- a/network/ndis/extension/samples/passthrough/mspassthroughext.inf +++ b/network/ndis/extension/samples/passthrough/mspassthroughext.inf @@ -6,20 +6,20 @@ Signature = "$Windows NT$" Class = NetService ClassGUID = {4D36E974-E325-11CE-BFC1-08002BE10318} -Provider = %Msft% +Provider = %ProviderString% CatalogFile = mspassthroughext.cat DriverVer = 08/29/2011,1.0 [Manufacturer] -%Msft%=MSFT,NTx86,NTia64,NTamd64 +%ManufacturerName%=Standard,NTx86,NTia64,NTamd64 -[MSFT.NTx86] +[Standard.NTx86] %MSPassthroughExt_Desc%=Install, MS_passthroughext -[MSFT.NTia64] +[Standard.NTia64] %MSPassthroughExt_Desc%=Install, MS_passthroughext -[MSFT.NTamd64] +[Standard.NTamd64] %MSPassthroughExt_Desc%=Install, MS_passthroughext ;------------------------------------------------------------------------- @@ -74,16 +74,12 @@ 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" +ManufacturerName="TODO-Set-Manufacturer" +ProviderString = "TODO-Set-Provider" +MSPassthroughExt_Desc = "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 index fd443f8bf..c64db7eaf 100644 --- a/network/ndis/extension/samples/passthrough/mspassthroughext.vcxproj +++ b/network/ndis/extension/samples/passthrough/mspassthroughext.vcxproj @@ -11,11 +11,11 @@ - {33DC46DA-90C8-42E0-9411-8CE8A02519C2} + {C60917BE-A59D-4FDB-BEFD-98DC106BDCA9} $(MSBuildProjectName) Debug x64 - {1B22CD34-E0FD-4F26-B929-B65361F77899} + {2B0884D6-EEF4-4FA0-BA85-91716EF53A20} @@ -113,7 +113,6 @@ - diff --git a/network/ndis/extension/samples/passthrough/mspassthroughext.vcxproj.Filters b/network/ndis/extension/samples/passthrough/mspassthroughext.vcxproj.Filters index b4236aa62..e08626d6c 100644 --- a/network/ndis/extension/samples/passthrough/mspassthroughext.vcxproj.Filters +++ b/network/ndis/extension/samples/passthrough/mspassthroughext.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {B04858F7-C21F-4968-B318-A24019C8BA93} + {359A1D8D-C03D-495A-A725-8463CB68AD28} h;hpp;hxx;hm;inl;inc;xsd - {533FF752-AB17-45D8-BB7F-A79F3526AEE3} + {B413F3C3-1A7A-47F1-871C-DD463CF462FA} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {B8C9F068-4F22-4D43-9727-2DE3BEE27340} + {1A7DD397-5E2F-460C-8CDA-7CB5C2EB8B84} inf;inv;inx;mof;mc; - {7BBC969B-9C83-4B37-816D-2A5D73801C39} + {9110AEBB-3824-490A-9B6E-92BED551B86D} diff --git a/network/ndis/filter/device.c b/network/ndis/filter/device.c index 48a048764..eee91a991 100644 --- a/network/ndis/filter/device.c +++ b/network/ndis/filter/device.c @@ -53,14 +53,14 @@ FilterRegisterDevice( Status = NdisRegisterDeviceEx( FilterDriverHandle, &DeviceAttribute, - &DeviceObject, + &NdisDeviceObject, &NdisFilterDeviceHandle ); if (Status == NDIS_STATUS_SUCCESS) { - FilterDeviceExtension = NdisGetDeviceReservedExtension(DeviceObject); + FilterDeviceExtension = NdisGetDeviceReservedExtension(NdisDeviceObject); FilterDeviceExtension->Signature = 'FTDR'; FilterDeviceExtension->Handle = FilterDriverHandle; diff --git a/network/ndis/filter/filter.c b/network/ndis/filter/filter.c index 0ab6f1ab9..7145b8878 100644 --- a/network/ndis/filter/filter.c +++ b/network/ndis/filter/filter.c @@ -28,7 +28,7 @@ Module Name: NDIS_HANDLE FilterDriverHandle; // NDIS handle for filter driver NDIS_HANDLE FilterDriverObject; NDIS_HANDLE NdisFilterDeviceHandle = NULL; -PDEVICE_OBJECT DeviceObject = NULL; +PDEVICE_OBJECT NdisDeviceObject = NULL; FILTER_LOCK FilterListLock; LIST_ENTRY FilterModuleList; diff --git a/network/ndis/filter/filter.h b/network/ndis/filter/filter.h index 42c5d9138..bf8da200e 100644 --- a/network/ndis/filter/filter.h +++ b/network/ndis/filter/filter.h @@ -47,7 +47,7 @@ Module Name: extern NDIS_HANDLE FilterDriverHandle; // NDIS handle for filter driver extern NDIS_HANDLE FilterDriverObject; extern NDIS_HANDLE NdisFilterDeviceHandle; -extern PDEVICE_OBJECT DeviceObject; +extern PDEVICE_OBJECT NdisDeviceObject; extern FILTER_LOCK FilterListLock; extern LIST_ENTRY FilterModuleList; diff --git a/network/ndis/filter/filter.sln b/network/ndis/filter/filter.sln index 1c4ff1c4b..af8aa594e 100644 --- a/network/ndis/filter/filter.sln +++ b/network/ndis/filter/filter.sln @@ -3,7 +3,7 @@ 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", "{0984ADBF-81CE-478A-9D69-CFDA813C015F}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ndislwf", "ndislwf.vcxproj", "{BA1E550F-9F59-4729-98F9-2E9135355331}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {0984ADBF-81CE-478A-9D69-CFDA813C015F}.Debug|Win32.ActiveCfg = Debug|Win32 - {0984ADBF-81CE-478A-9D69-CFDA813C015F}.Debug|Win32.Build.0 = Debug|Win32 - {0984ADBF-81CE-478A-9D69-CFDA813C015F}.Release|Win32.ActiveCfg = Release|Win32 - {0984ADBF-81CE-478A-9D69-CFDA813C015F}.Release|Win32.Build.0 = Release|Win32 - {0984ADBF-81CE-478A-9D69-CFDA813C015F}.Debug|x64.ActiveCfg = Debug|x64 - {0984ADBF-81CE-478A-9D69-CFDA813C015F}.Debug|x64.Build.0 = Debug|x64 - {0984ADBF-81CE-478A-9D69-CFDA813C015F}.Release|x64.ActiveCfg = Release|x64 - {0984ADBF-81CE-478A-9D69-CFDA813C015F}.Release|x64.Build.0 = Release|x64 + {BA1E550F-9F59-4729-98F9-2E9135355331}.Debug|Win32.ActiveCfg = Debug|Win32 + {BA1E550F-9F59-4729-98F9-2E9135355331}.Debug|Win32.Build.0 = Debug|Win32 + {BA1E550F-9F59-4729-98F9-2E9135355331}.Release|Win32.ActiveCfg = Release|Win32 + {BA1E550F-9F59-4729-98F9-2E9135355331}.Release|Win32.Build.0 = Release|Win32 + {BA1E550F-9F59-4729-98F9-2E9135355331}.Debug|x64.ActiveCfg = Debug|x64 + {BA1E550F-9F59-4729-98F9-2E9135355331}.Debug|x64.Build.0 = Debug|x64 + {BA1E550F-9F59-4729-98F9-2E9135355331}.Release|x64.ActiveCfg = Release|x64 + {BA1E550F-9F59-4729-98F9-2E9135355331}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/network/ndis/filter/ndislwf.vcxproj b/network/ndis/filter/ndislwf.vcxproj index 982667caa..503d9301b 100644 --- a/network/ndis/filter/ndislwf.vcxproj +++ b/network/ndis/filter/ndislwf.vcxproj @@ -19,11 +19,11 @@ - {0984ADBF-81CE-478A-9D69-CFDA813C015F} + {BA1E550F-9F59-4729-98F9-2E9135355331} $(MSBuildProjectName) Debug Win32 - {612455BF-B8D2-4EF2-B654-C71F38EB41E6} + {63E20C70-DE9B-4A15-9F7D-B388D4B6E048} @@ -269,7 +269,6 @@ - diff --git a/network/ndis/filter/ndislwf.vcxproj.Filters b/network/ndis/filter/ndislwf.vcxproj.Filters index 33446f05f..b08d85b98 100644 --- a/network/ndis/filter/ndislwf.vcxproj.Filters +++ b/network/ndis/filter/ndislwf.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {165E8E33-1000-4BA1-B9C0-13563E035EDF} + {84396FDB-160B-48B8-8764-F838E443D514} h;hpp;hxx;hm;inl;inc;xsd - {DBD7275E-19E6-4008-B9DB-F4BA06822D7B} + {DAFBC6E8-0471-4153-B9EA-04B4264C4757} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {C863DF64-1C80-42D6-80E1-A06EE203FD47} + {A57EFF04-85E0-4EC9-A9A3-E3FE3A0E7F56} inf;inv;inx;mof;mc; - {CE7F2FE4-DF42-4BE1-9027-2941C09DED39} + {9DFDBF97-3CA3-458D-830D-083C1414E124} diff --git a/network/ndis/filter/netlwf.inf b/network/ndis/filter/netlwf.inf index 1f55291ed..ae7287758 100644 --- a/network/ndis/filter/netlwf.inf +++ b/network/ndis/filter/netlwf.inf @@ -15,7 +15,7 @@ ClassGUID = {4D36E974-E325-11CE-BFC1-08002BE10318} CatalogFile = netlwf.cat ; TODO: Customize this string for your company name -Provider = %Msft% +Provider = %ProviderString% ; TODO: Customize this string for the driver version DriverVer = 10/01/2002,6.0.5019.0 @@ -25,17 +25,17 @@ DriverVer = 10/01/2002,6.0.5019.0 ; compiled binary. If you do not supply a driver compiled for ia64, delete the ; NTia64 section. [Manufacturer] -%Msft%=MSFT,NTx86,NTia64,NTamd64 +%ManufacturerName%=Standard,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] +[Standard.NTx86] %NdisLwf_Desc%=Install, MS_NdisLwf -[MSFT.NTia64] +[Standard.NTia64] %NdisLwf_Desc%=Install, MS_NdisLwf -[MSFT.NTamd64] +[Standard.NTamd64] %NdisLwf_Desc%=Install, MS_NdisLwf ;------------------------------------------------------------------------- @@ -193,7 +193,8 @@ HKR, Parameters, NdisImPlatformBindingOptions,0x00010001,0 ; Subscribe to defaul [Strings] ; TODO: Customize these strings. -Msft = "Your Company" +ProviderString = "TODO-Set-Provider" +ManufacturerName = "TODO-Set-Manufacturer" NdisLwf_Desc = "NDIS Sample LightWeight Filter" NdisLwf_HelpText = "Sample to demonstrate NDIS LightWeight Filters" diff --git a/network/ndis/mux/driver/60/mux_mp.inf b/network/ndis/mux/driver/60/mux_mp.inf index 5d4d8c2d2..9c5f998c5 100644 --- a/network/ndis/mux/driver/60/mux_mp.inf +++ b/network/ndis/mux/driver/60/mux_mp.inf @@ -8,22 +8,22 @@ Signature = "$Windows NT$" Class = Net ClassGUID = {4d36e972-e325-11ce-bfc1-08002be10318} -Provider = %Msft% +Provider = %ProviderString% DriverVer =10/01/2002,6.0.5019.0 [ControlFlags] ExcludeFromSelect = MS_MUXMP [Manufacturer] -%Msft% = MSFT,NTx86,NTia64,NTamd64 +%ManufacturerName% = Standard,NTx86,NTia64,NTamd64 -[MSFT.NTx86] +[Standard.NTx86] %MUXMP_Desc% = MUXMP.ndi, MS_MUXMP -[MSFT.NTia64] +[Standard.NTia64] %MUXMP_Desc% = MUXMP.ndi, MS_MUXMP -[MSFT.NTamd64] +[Standard.NTamd64] %MUXMP_Desc% = MUXMP.ndi, MS_MUXMP [MUXMP.ndi] @@ -61,7 +61,8 @@ Description = %MUXMP_Desc% [Strings] -Msft = "Your Company" +ProviderString = "TODO-Set-Provider" +ManufacturerName="TODO-Set-Manufacturer" 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 index eeeb6f8a3..836e60e1d 100644 --- a/network/ndis/mux/driver/60/muxp.inf +++ b/network/ndis/mux/driver/60/muxp.inf @@ -8,19 +8,19 @@ Signature = "$Windows NT$" Class = NetTrans ClassGUID = {4D36E975-E325-11CE-BFC1-08002BE10318} -Provider = %Msft% +Provider = %ProviderString% DriverVer =10/01/2002,6.0.5019.0 [Manufacturer] -%Msft% = MSFT,NTx86,NTia64,NTamd64 +%ManufacturerName% = Standard,NTx86,NTia64,NTamd64 -[MSFT.NTx86] +[Standard.NTx86] %MUXP_Desc% = MUXP.ndi, MS_MUXP -[MSFT.NTia64] +[Standard.NTia64] %MUXP_Desc% = MUXP.ndi, MS_MUXP -[MSFT.NTamd64] +[Standard.NTamd64] %MUXP_Desc% = MUXP.ndi, MS_MUXP ; Note: @@ -92,9 +92,8 @@ DelService = MUXP DelFiles = MUXP.CopyFiles.DLL, MUXP.CopyFiles.sys [Strings] -Msft = "Your Company" +ProviderString = "TODO-Set-Provider" +ManufacturerName="TODO-Set-Manufacturer" 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 index 75fc34317..d3848065f 100644 --- a/network/ndis/mux/driver/60/novlan/mux.vcxproj +++ b/network/ndis/mux/driver/60/novlan/mux.vcxproj @@ -19,11 +19,11 @@ - {397D0084-5C84-4AD1-87DF-2B3C669BCA78} + {B5858D53-B828-4CA4-BB86-D9E9C31B7F77} $(MSBuildProjectName) Debug Win32 - {CB06DB9D-C97D-417D-8752-1CBC12C4226B} + {114EA080-E360-4DFF-933D-32330FBCE32D} @@ -273,7 +273,6 @@ - diff --git a/network/ndis/mux/driver/60/novlan/mux.vcxproj.Filters b/network/ndis/mux/driver/60/novlan/mux.vcxproj.Filters index 812a5c6b7..abae9d849 100644 --- a/network/ndis/mux/driver/60/novlan/mux.vcxproj.Filters +++ b/network/ndis/mux/driver/60/novlan/mux.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {18AD59A7-F2A1-4691-AA03-199BD103CF5A} + {D4B49EC5-E87F-461E-8F43-16C86C0D27B7} h;hpp;hxx;hm;inl;inc;xsd - {E6656927-1183-47F1-85CE-57A302CBDA79} + {BB7C27BC-A288-4C98-A16A-4F35D1DD7D26} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {0E73A07F-C22B-466E-B6B2-2D47F04BC109} + {0466D1AB-867D-4829-A10F-4C6E5F154268} inf;inv;inx;mof;mc; - {7C6C2990-74A1-4E06-AD1B-02AD245372EC} + {33D5CCAF-775D-45F9-B08A-C2951B557268} diff --git a/network/ndis/mux/driver/60/vlan/muxvlan.vcxproj b/network/ndis/mux/driver/60/vlan/muxvlan.vcxproj index 8f79c893e..52825b7ab 100644 --- a/network/ndis/mux/driver/60/vlan/muxvlan.vcxproj +++ b/network/ndis/mux/driver/60/vlan/muxvlan.vcxproj @@ -19,11 +19,11 @@ - {D6FCE02C-B772-45F2-9941-F5E6B1E32877} + {C2CFEECC-2281-4F99-B855-185BEE431415} $(MSBuildProjectName) Debug Win32 - {47080E55-F8E1-4968-8893-18ADE0ED7268} + {43683E92-7DA7-43A8-958C-057E3A015D28} @@ -285,7 +285,6 @@ - diff --git a/network/ndis/mux/driver/60/vlan/muxvlan.vcxproj.Filters b/network/ndis/mux/driver/60/vlan/muxvlan.vcxproj.Filters index 97e1f9ff6..e16514443 100644 --- a/network/ndis/mux/driver/60/vlan/muxvlan.vcxproj.Filters +++ b/network/ndis/mux/driver/60/vlan/muxvlan.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {45E3DA30-48FB-4471-9DF1-F983CB9A3286} + {94237C24-6B64-43C1-A5E1-10FCDCD13519} h;hpp;hxx;hm;inl;inc;xsd - {6490648B-7BD7-4B6D-A5E7-3B3AC865A4E6} + {B7E57BF5-A4DA-44A1-8A61-BEF5A6016D5D} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {22C10F71-ECE2-4C46-9255-E145C77B5EE2} + {373729B5-7B52-4DA0-9380-18CBE8AE382A} inf;inv;inx;mof;mc; - {90F61B4E-C8EC-4AAF-92C9-5808F7EBDB2C} + {6DEAB32E-549A-4629-AE3C-8BBC83887CDE} diff --git a/network/ndis/mux/mux.sln b/network/ndis/mux/mux.sln index 56ec1e721..f9483243e 100644 --- a/network/ndis/mux/mux.sln +++ b/network/ndis/mux/mux.sln @@ -3,21 +3,21 @@ 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", "{5649382B-2263-47B2-9B96-EC867F4948B6}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Vlan", "Vlan", "{9817776F-C460-4A80-BD6D-201A2CB3A0A4}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "60", "60", "{77C1D298-5F95-4683-922F-D345F833177C}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "60", "60", "{8844C087-57E1-4934-AA51-CF9CF8F50313}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Driver", "Driver", "{A47240B9-D403-4B0B-B4CD-DC7B8A0C19B6}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Driver", "Driver", "{F830CF4F-2AA8-4AE1-9068-5F63F12A3ACF}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Novlan", "Novlan", "{DCE5E59B-B3F0-4698-9A27-7D9A68A41C83}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Novlan", "Novlan", "{D477BF5F-6512-4E1E-9115-44CF150CBD69}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Notifyob", "Notifyob", "{91B5E151-6E95-4C95-9D34-35D1AEB20F39}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Notifyob", "Notifyob", "{98C539D7-554A-4D00-B2C5-7897ADA27626}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "muxvlan", "driver\60\vlan\muxvlan.vcxproj", "{D6FCE02C-B772-45F2-9941-F5E6B1E32877}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "muxvlan", "driver\60\vlan\muxvlan.vcxproj", "{C2CFEECC-2281-4F99-B855-185BEE431415}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mux", "driver\60\novlan\mux.vcxproj", "{397D0084-5C84-4AD1-87DF-2B3C669BCA78}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mux", "driver\60\novlan\mux.vcxproj", "{B5858D53-B828-4CA4-BB86-D9E9C31B7F77}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mux", "notifyob\mux.vcxproj", "{0124A324-0D8F-4DAC-B63A-4615FFB9DCA6}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mux", "notifyob\mux.vcxproj", "{285AA43E-0509-4716-8586-405D086C370D}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -27,40 +27,40 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {D6FCE02C-B772-45F2-9941-F5E6B1E32877}.Debug|Win32.ActiveCfg = Debug|Win32 - {D6FCE02C-B772-45F2-9941-F5E6B1E32877}.Debug|Win32.Build.0 = Debug|Win32 - {D6FCE02C-B772-45F2-9941-F5E6B1E32877}.Release|Win32.ActiveCfg = Release|Win32 - {D6FCE02C-B772-45F2-9941-F5E6B1E32877}.Release|Win32.Build.0 = Release|Win32 - {D6FCE02C-B772-45F2-9941-F5E6B1E32877}.Debug|x64.ActiveCfg = Debug|x64 - {D6FCE02C-B772-45F2-9941-F5E6B1E32877}.Debug|x64.Build.0 = Debug|x64 - {D6FCE02C-B772-45F2-9941-F5E6B1E32877}.Release|x64.ActiveCfg = Release|x64 - {D6FCE02C-B772-45F2-9941-F5E6B1E32877}.Release|x64.Build.0 = Release|x64 - {397D0084-5C84-4AD1-87DF-2B3C669BCA78}.Debug|Win32.ActiveCfg = Debug|Win32 - {397D0084-5C84-4AD1-87DF-2B3C669BCA78}.Debug|Win32.Build.0 = Debug|Win32 - {397D0084-5C84-4AD1-87DF-2B3C669BCA78}.Release|Win32.ActiveCfg = Release|Win32 - {397D0084-5C84-4AD1-87DF-2B3C669BCA78}.Release|Win32.Build.0 = Release|Win32 - {397D0084-5C84-4AD1-87DF-2B3C669BCA78}.Debug|x64.ActiveCfg = Debug|x64 - {397D0084-5C84-4AD1-87DF-2B3C669BCA78}.Debug|x64.Build.0 = Debug|x64 - {397D0084-5C84-4AD1-87DF-2B3C669BCA78}.Release|x64.ActiveCfg = Release|x64 - {397D0084-5C84-4AD1-87DF-2B3C669BCA78}.Release|x64.Build.0 = Release|x64 - {0124A324-0D8F-4DAC-B63A-4615FFB9DCA6}.Debug|Win32.ActiveCfg = Debug|Win32 - {0124A324-0D8F-4DAC-B63A-4615FFB9DCA6}.Debug|Win32.Build.0 = Debug|Win32 - {0124A324-0D8F-4DAC-B63A-4615FFB9DCA6}.Release|Win32.ActiveCfg = Release|Win32 - {0124A324-0D8F-4DAC-B63A-4615FFB9DCA6}.Release|Win32.Build.0 = Release|Win32 - {0124A324-0D8F-4DAC-B63A-4615FFB9DCA6}.Debug|x64.ActiveCfg = Debug|x64 - {0124A324-0D8F-4DAC-B63A-4615FFB9DCA6}.Debug|x64.Build.0 = Debug|x64 - {0124A324-0D8F-4DAC-B63A-4615FFB9DCA6}.Release|x64.ActiveCfg = Release|x64 - {0124A324-0D8F-4DAC-B63A-4615FFB9DCA6}.Release|x64.Build.0 = Release|x64 + {C2CFEECC-2281-4F99-B855-185BEE431415}.Debug|Win32.ActiveCfg = Debug|Win32 + {C2CFEECC-2281-4F99-B855-185BEE431415}.Debug|Win32.Build.0 = Debug|Win32 + {C2CFEECC-2281-4F99-B855-185BEE431415}.Release|Win32.ActiveCfg = Release|Win32 + {C2CFEECC-2281-4F99-B855-185BEE431415}.Release|Win32.Build.0 = Release|Win32 + {C2CFEECC-2281-4F99-B855-185BEE431415}.Debug|x64.ActiveCfg = Debug|x64 + {C2CFEECC-2281-4F99-B855-185BEE431415}.Debug|x64.Build.0 = Debug|x64 + {C2CFEECC-2281-4F99-B855-185BEE431415}.Release|x64.ActiveCfg = Release|x64 + {C2CFEECC-2281-4F99-B855-185BEE431415}.Release|x64.Build.0 = Release|x64 + {B5858D53-B828-4CA4-BB86-D9E9C31B7F77}.Debug|Win32.ActiveCfg = Debug|Win32 + {B5858D53-B828-4CA4-BB86-D9E9C31B7F77}.Debug|Win32.Build.0 = Debug|Win32 + {B5858D53-B828-4CA4-BB86-D9E9C31B7F77}.Release|Win32.ActiveCfg = Release|Win32 + {B5858D53-B828-4CA4-BB86-D9E9C31B7F77}.Release|Win32.Build.0 = Release|Win32 + {B5858D53-B828-4CA4-BB86-D9E9C31B7F77}.Debug|x64.ActiveCfg = Debug|x64 + {B5858D53-B828-4CA4-BB86-D9E9C31B7F77}.Debug|x64.Build.0 = Debug|x64 + {B5858D53-B828-4CA4-BB86-D9E9C31B7F77}.Release|x64.ActiveCfg = Release|x64 + {B5858D53-B828-4CA4-BB86-D9E9C31B7F77}.Release|x64.Build.0 = Release|x64 + {285AA43E-0509-4716-8586-405D086C370D}.Debug|Win32.ActiveCfg = Debug|Win32 + {285AA43E-0509-4716-8586-405D086C370D}.Debug|Win32.Build.0 = Debug|Win32 + {285AA43E-0509-4716-8586-405D086C370D}.Release|Win32.ActiveCfg = Release|Win32 + {285AA43E-0509-4716-8586-405D086C370D}.Release|Win32.Build.0 = Release|Win32 + {285AA43E-0509-4716-8586-405D086C370D}.Debug|x64.ActiveCfg = Debug|x64 + {285AA43E-0509-4716-8586-405D086C370D}.Debug|x64.Build.0 = Debug|x64 + {285AA43E-0509-4716-8586-405D086C370D}.Release|x64.ActiveCfg = Release|x64 + {285AA43E-0509-4716-8586-405D086C370D}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {D6FCE02C-B772-45F2-9941-F5E6B1E32877} = {5649382B-2263-47B2-9B96-EC867F4948B6} - {397D0084-5C84-4AD1-87DF-2B3C669BCA78} = {DCE5E59B-B3F0-4698-9A27-7D9A68A41C83} - {0124A324-0D8F-4DAC-B63A-4615FFB9DCA6} = {91B5E151-6E95-4C95-9D34-35D1AEB20F39} - {5649382B-2263-47B2-9B96-EC867F4948B6} = {77C1D298-5F95-4683-922F-D345F833177C} - {77C1D298-5F95-4683-922F-D345F833177C} = {A47240B9-D403-4B0B-B4CD-DC7B8A0C19B6} - {DCE5E59B-B3F0-4698-9A27-7D9A68A41C83} = {77C1D298-5F95-4683-922F-D345F833177C} + {C2CFEECC-2281-4F99-B855-185BEE431415} = {9817776F-C460-4A80-BD6D-201A2CB3A0A4} + {B5858D53-B828-4CA4-BB86-D9E9C31B7F77} = {D477BF5F-6512-4E1E-9115-44CF150CBD69} + {285AA43E-0509-4716-8586-405D086C370D} = {98C539D7-554A-4D00-B2C5-7897ADA27626} + {9817776F-C460-4A80-BD6D-201A2CB3A0A4} = {8844C087-57E1-4934-AA51-CF9CF8F50313} + {8844C087-57E1-4934-AA51-CF9CF8F50313} = {F830CF4F-2AA8-4AE1-9068-5F63F12A3ACF} + {D477BF5F-6512-4E1E-9115-44CF150CBD69} = {8844C087-57E1-4934-AA51-CF9CF8F50313} EndGlobalSection EndGlobal diff --git a/network/ndis/mux/notifyob/mux.vcxproj b/network/ndis/mux/notifyob/mux.vcxproj index a52d3dfab..bd1a8bdd1 100644 --- a/network/ndis/mux/notifyob/mux.vcxproj +++ b/network/ndis/mux/notifyob/mux.vcxproj @@ -19,11 +19,11 @@ - {0124A324-0D8F-4DAC-B63A-4615FFB9DCA6} + {285AA43E-0509-4716-8586-405D086C370D} $(MSBuildProjectName) Debug Win32 - {1EB42396-C115-40FD-9536-AB4C17CD9579} + {D12584A6-2527-4C8D-B135-1ECB73201A21} @@ -260,7 +260,6 @@ - diff --git a/network/ndis/mux/notifyob/mux.vcxproj.Filters b/network/ndis/mux/notifyob/mux.vcxproj.Filters index 2dfc715b8..5e6a78d7b 100644 --- a/network/ndis/mux/notifyob/mux.vcxproj.Filters +++ b/network/ndis/mux/notifyob/mux.vcxproj.Filters @@ -3,15 +3,15 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {3070F81A-13F3-4A51-B701-3CF116C59B41} + {D2FB3445-77C3-4BD9-BA51-E5B0D6C08BEE} h;hpp;hxx;hm;inl;inc;xsd - {9F1DE3DD-04BA-4F2B-9E2F-9CD8949EA47C} + {B20B5A99-B397-40F6-A904-F5239D1E7041} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {D3F79307-0791-4B6C-8EB7-8483DC785BA7} + {EE9C4162-6737-4614-941D-1942E86D02B7} diff --git a/network/ndis/ndisprot/6x/ndisprot60.sln b/network/ndis/ndisprot/6x/ndisprot60.sln index 3bf5708e3..69c4b38f1 100644 --- a/network/ndis/ndisprot/6x/ndisprot60.sln +++ b/network/ndis/ndisprot/6x/ndisprot60.sln @@ -3,19 +3,19 @@ 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", "{DAF28310-CB34-4AD4-9B7A-04E46AF3AA20}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "60", "60", "{1D62C0DD-3166-4212-A396-0C32A8133256}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sys", "Sys", "{EA1F1CD0-CDF2-48BD-853C-3012A20E5CE6}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sys", "Sys", "{464025BB-16D1-4B86-ACF9-3F9EF08B865E}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "630", "630", "{082733D0-2C57-4E4D-A3FA-AF692CF90E8A}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "630", "630", "{247D6A97-6A59-4E29-BA6F-B20EEE3AE3B6}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Test", "Test", "{A171447E-D0A4-4F2A-8900-6473D81C0269}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Test", "Test", "{8E714E74-8167-4A2E-B5D9-97506C801DB0}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ndisprot60", "sys\60\ndisprot60.vcxproj", "{7EA443E2-5964-417F-B418-CCFC6C30BF45}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ndisprot60", "sys\60\ndisprot60.vcxproj", "{D8336C3A-8FB7-46CA-82A9-209B7EB4339D}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ndisprot630", "sys\630\ndisprot630.vcxproj", "{310AF693-B1C5-4CF1-A7E4-99F9D18D1574}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ndisprot630", "sys\630\ndisprot630.vcxproj", "{6EE064DA-28D2-478C-BC25-FD04382FAD44}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "prottest", "test\prottest.vcxproj", "{C5DBFAFB-4867-4605-B432-337BD17B972C}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "prottest", "test\prottest.vcxproj", "{62009D19-D80B-46B8-8360-D84AA57DD567}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -25,39 +25,39 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {7EA443E2-5964-417F-B418-CCFC6C30BF45}.Debug|Win32.ActiveCfg = Debug|Win32 - {7EA443E2-5964-417F-B418-CCFC6C30BF45}.Debug|Win32.Build.0 = Debug|Win32 - {7EA443E2-5964-417F-B418-CCFC6C30BF45}.Release|Win32.ActiveCfg = Release|Win32 - {7EA443E2-5964-417F-B418-CCFC6C30BF45}.Release|Win32.Build.0 = Release|Win32 - {7EA443E2-5964-417F-B418-CCFC6C30BF45}.Debug|x64.ActiveCfg = Debug|x64 - {7EA443E2-5964-417F-B418-CCFC6C30BF45}.Debug|x64.Build.0 = Debug|x64 - {7EA443E2-5964-417F-B418-CCFC6C30BF45}.Release|x64.ActiveCfg = Release|x64 - {7EA443E2-5964-417F-B418-CCFC6C30BF45}.Release|x64.Build.0 = Release|x64 - {310AF693-B1C5-4CF1-A7E4-99F9D18D1574}.Debug|Win32.ActiveCfg = Debug|Win32 - {310AF693-B1C5-4CF1-A7E4-99F9D18D1574}.Debug|Win32.Build.0 = Debug|Win32 - {310AF693-B1C5-4CF1-A7E4-99F9D18D1574}.Release|Win32.ActiveCfg = Release|Win32 - {310AF693-B1C5-4CF1-A7E4-99F9D18D1574}.Release|Win32.Build.0 = Release|Win32 - {310AF693-B1C5-4CF1-A7E4-99F9D18D1574}.Debug|x64.ActiveCfg = Debug|x64 - {310AF693-B1C5-4CF1-A7E4-99F9D18D1574}.Debug|x64.Build.0 = Debug|x64 - {310AF693-B1C5-4CF1-A7E4-99F9D18D1574}.Release|x64.ActiveCfg = Release|x64 - {310AF693-B1C5-4CF1-A7E4-99F9D18D1574}.Release|x64.Build.0 = Release|x64 - {C5DBFAFB-4867-4605-B432-337BD17B972C}.Debug|Win32.ActiveCfg = Debug|Win32 - {C5DBFAFB-4867-4605-B432-337BD17B972C}.Debug|Win32.Build.0 = Debug|Win32 - {C5DBFAFB-4867-4605-B432-337BD17B972C}.Release|Win32.ActiveCfg = Release|Win32 - {C5DBFAFB-4867-4605-B432-337BD17B972C}.Release|Win32.Build.0 = Release|Win32 - {C5DBFAFB-4867-4605-B432-337BD17B972C}.Debug|x64.ActiveCfg = Debug|x64 - {C5DBFAFB-4867-4605-B432-337BD17B972C}.Debug|x64.Build.0 = Debug|x64 - {C5DBFAFB-4867-4605-B432-337BD17B972C}.Release|x64.ActiveCfg = Release|x64 - {C5DBFAFB-4867-4605-B432-337BD17B972C}.Release|x64.Build.0 = Release|x64 + {D8336C3A-8FB7-46CA-82A9-209B7EB4339D}.Debug|Win32.ActiveCfg = Debug|Win32 + {D8336C3A-8FB7-46CA-82A9-209B7EB4339D}.Debug|Win32.Build.0 = Debug|Win32 + {D8336C3A-8FB7-46CA-82A9-209B7EB4339D}.Release|Win32.ActiveCfg = Release|Win32 + {D8336C3A-8FB7-46CA-82A9-209B7EB4339D}.Release|Win32.Build.0 = Release|Win32 + {D8336C3A-8FB7-46CA-82A9-209B7EB4339D}.Debug|x64.ActiveCfg = Debug|x64 + {D8336C3A-8FB7-46CA-82A9-209B7EB4339D}.Debug|x64.Build.0 = Debug|x64 + {D8336C3A-8FB7-46CA-82A9-209B7EB4339D}.Release|x64.ActiveCfg = Release|x64 + {D8336C3A-8FB7-46CA-82A9-209B7EB4339D}.Release|x64.Build.0 = Release|x64 + {6EE064DA-28D2-478C-BC25-FD04382FAD44}.Debug|Win32.ActiveCfg = Debug|Win32 + {6EE064DA-28D2-478C-BC25-FD04382FAD44}.Debug|Win32.Build.0 = Debug|Win32 + {6EE064DA-28D2-478C-BC25-FD04382FAD44}.Release|Win32.ActiveCfg = Release|Win32 + {6EE064DA-28D2-478C-BC25-FD04382FAD44}.Release|Win32.Build.0 = Release|Win32 + {6EE064DA-28D2-478C-BC25-FD04382FAD44}.Debug|x64.ActiveCfg = Debug|x64 + {6EE064DA-28D2-478C-BC25-FD04382FAD44}.Debug|x64.Build.0 = Debug|x64 + {6EE064DA-28D2-478C-BC25-FD04382FAD44}.Release|x64.ActiveCfg = Release|x64 + {6EE064DA-28D2-478C-BC25-FD04382FAD44}.Release|x64.Build.0 = Release|x64 + {62009D19-D80B-46B8-8360-D84AA57DD567}.Debug|Win32.ActiveCfg = Debug|Win32 + {62009D19-D80B-46B8-8360-D84AA57DD567}.Debug|Win32.Build.0 = Debug|Win32 + {62009D19-D80B-46B8-8360-D84AA57DD567}.Release|Win32.ActiveCfg = Release|Win32 + {62009D19-D80B-46B8-8360-D84AA57DD567}.Release|Win32.Build.0 = Release|Win32 + {62009D19-D80B-46B8-8360-D84AA57DD567}.Debug|x64.ActiveCfg = Debug|x64 + {62009D19-D80B-46B8-8360-D84AA57DD567}.Debug|x64.Build.0 = Debug|x64 + {62009D19-D80B-46B8-8360-D84AA57DD567}.Release|x64.ActiveCfg = Release|x64 + {62009D19-D80B-46B8-8360-D84AA57DD567}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {7EA443E2-5964-417F-B418-CCFC6C30BF45} = {DAF28310-CB34-4AD4-9B7A-04E46AF3AA20} - {310AF693-B1C5-4CF1-A7E4-99F9D18D1574} = {082733D0-2C57-4E4D-A3FA-AF692CF90E8A} - {C5DBFAFB-4867-4605-B432-337BD17B972C} = {A171447E-D0A4-4F2A-8900-6473D81C0269} - {DAF28310-CB34-4AD4-9B7A-04E46AF3AA20} = {EA1F1CD0-CDF2-48BD-853C-3012A20E5CE6} - {082733D0-2C57-4E4D-A3FA-AF692CF90E8A} = {EA1F1CD0-CDF2-48BD-853C-3012A20E5CE6} + {D8336C3A-8FB7-46CA-82A9-209B7EB4339D} = {1D62C0DD-3166-4212-A396-0C32A8133256} + {6EE064DA-28D2-478C-BC25-FD04382FAD44} = {247D6A97-6A59-4E29-BA6F-B20EEE3AE3B6} + {62009D19-D80B-46B8-8360-D84AA57DD567} = {8E714E74-8167-4A2E-B5D9-97506C801DB0} + {1D62C0DD-3166-4212-A396-0C32A8133256} = {464025BB-16D1-4B86-ACF9-3F9EF08B865E} + {247D6A97-6A59-4E29-BA6F-B20EEE3AE3B6} = {464025BB-16D1-4B86-ACF9-3F9EF08B865E} EndGlobalSection EndGlobal diff --git a/network/ndis/ndisprot/6x/sys/60/ndisprot60.inf b/network/ndis/ndisprot/6x/sys/60/ndisprot60.inf index 5618bfcf0..985a651a8 100644 --- a/network/ndis/ndisprot/6x/sys/60/ndisprot60.inf +++ b/network/ndis/ndisprot/6x/sys/60/ndisprot60.inf @@ -7,21 +7,21 @@ Signature = "$Windows NT$" Class = NetTrans ClassGUID = {4d36e975-e325-11ce-bfc1-08002be10318} -Provider = %Msft% +Provider = %ProviderString% DriverVer = 10/01/2002,4.2 CatalogFile = ndisprot60.cat [Manufacturer] -%Msft%=MSFT,NTx86,NTia64,NTamd64 +%ManufacturerName%=Standard,NTx86,NTia64,NTamd64 -[MSFT.NTx86] +[Standard.NTx86] %NDISPROT_Desc%=Install, MS_NDISPROT -[MSFT.NTia64] +[Standard.NTia64] %NDISPROT_Desc%=Install, MS_NDISPROT -[MSFT.NTamd64] +[Standard.NTamd64] %NDISPROT_Desc%=Install, MS_NDISPROT ;------------------------------------------------------------------------- @@ -79,12 +79,8 @@ CpyFiles_Sys = 12 ; DIRID_DRIVERS Ndisprot60.sys,,,2 [Strings] -Msft = "Your Company" -DiskDescription = "Microsoft Ndisprot Sample Protocol Driver Disk" +ManufacturerName = "TODO-Set-Manufacturer" +ProviderString = "TODO-Set-Provider" +DiskDescription = "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.vcxproj b/network/ndis/ndisprot/6x/sys/60/ndisprot60.vcxproj index 9fb2af46c..de9c18777 100644 --- a/network/ndis/ndisprot/6x/sys/60/ndisprot60.vcxproj +++ b/network/ndis/ndisprot/6x/sys/60/ndisprot60.vcxproj @@ -19,11 +19,11 @@ - {7EA443E2-5964-417F-B418-CCFC6C30BF45} + {D8336C3A-8FB7-46CA-82A9-209B7EB4339D} $(MSBuildProjectName) Debug Win32 - {BF705255-3868-4036-A1E7-638D7932F1A5} + {D4489723-0609-411D-B3A9-258AF098B21A} @@ -251,7 +251,6 @@ - diff --git a/network/ndis/ndisprot/6x/sys/60/ndisprot60.vcxproj.Filters b/network/ndis/ndisprot/6x/sys/60/ndisprot60.vcxproj.Filters index 6e7019c51..7e2f4a078 100644 --- a/network/ndis/ndisprot/6x/sys/60/ndisprot60.vcxproj.Filters +++ b/network/ndis/ndisprot/6x/sys/60/ndisprot60.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {BB88F7B9-FEE7-4A81-BF8E-B5660FC419B0} + {26EA05C5-4E1D-434E-B936-33352970A161} h;hpp;hxx;hm;inl;inc;xsd - {B0674E26-B054-4482-B925-109A349CB29C} + {F4571230-8339-4A49-AD38-78962F103829} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {E6E18B22-C6BF-4A30-8120-E0706E138F57} + {DFDB6D3D-533A-4562-BD43-338AB736860B} inf;inv;inx;mof;mc; - {31078238-19E1-4DD5-9F8B-29286B133DF4} + {0C4980F7-5BA2-400E-A1D2-29520A8F0761} diff --git a/network/ndis/ndisprot/6x/sys/630/ndisprot630.inf b/network/ndis/ndisprot/6x/sys/630/ndisprot630.inf index 0fc1eb672..6c152a020 100644 --- a/network/ndis/ndisprot/6x/sys/630/ndisprot630.inf +++ b/network/ndis/ndisprot/6x/sys/630/ndisprot630.inf @@ -7,27 +7,27 @@ Signature = "$Windows NT$" Class = NetTrans ClassGUID = {4d36e975-e325-11ce-bfc1-08002be10318} -Provider = %Msft% +Provider = %ProviderString% DriverVer = 07/16/2010,4.2 CatalogFile = ndisprot630.cat [Manufacturer] -%Msft%=MSFT,NTx86,NTia64,NTamd64,NTarm,NTarm64 +%ManufacturerName%=Standard,NTx86,NTia64,NTamd64,NTarm,NTarm64 -[MSFT.NTx86] +[Standard.NTx86] %NDISPROT_Desc%=Install, MS_NDISPROT -[MSFT.NTia64] +[Standard.NTia64] %NDISPROT_Desc%=Install, MS_NDISPROT -[MSFT.NTamd64] +[Standard.NTamd64] %NDISPROT_Desc%=Install, MS_NDISPROT -[MSFT.NTarm] +[Standard.NTarm] %NDISPROT_Desc%=Install, MS_NDISPROT -[MSFT.NTarm64] +[Standard.NTarm64] %NDISPROT_Desc%=Install, MS_NDISPROT ;------------------------------------------------------------------------- @@ -98,12 +98,8 @@ CpyFiles_Sys = 12 ; DIRID_DRIVERS Ndisprot630.sys,,,2 [Strings] -Msft = "Your Company" -DiskDescription = "Microsoft Ndisprot Sample Protocol Driver Disk" +ManufacturerName = "TODO-Set-Manufacturer" +ProviderString = "TODO-Set-Provider" +DiskDescription = "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.vcxproj b/network/ndis/ndisprot/6x/sys/630/ndisprot630.vcxproj index 12fc5739c..676449b8e 100644 --- a/network/ndis/ndisprot/6x/sys/630/ndisprot630.vcxproj +++ b/network/ndis/ndisprot/6x/sys/630/ndisprot630.vcxproj @@ -19,11 +19,11 @@ - {310AF693-B1C5-4CF1-A7E4-99F9D18D1574} + {6EE064DA-28D2-478C-BC25-FD04382FAD44} $(MSBuildProjectName) Debug Win32 - {1AA7BDDF-4140-446D-A380-EDC92457D53B} + {A72934EE-A162-44C2-9268-572F34455685} @@ -251,7 +251,6 @@ - diff --git a/network/ndis/ndisprot/6x/sys/630/ndisprot630.vcxproj.Filters b/network/ndis/ndisprot/6x/sys/630/ndisprot630.vcxproj.Filters index 770220acd..545dfd913 100644 --- a/network/ndis/ndisprot/6x/sys/630/ndisprot630.vcxproj.Filters +++ b/network/ndis/ndisprot/6x/sys/630/ndisprot630.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {6703BE47-96E4-43E8-A07F-66EDCDE49673} + {92710B14-4232-4967-AEA5-6DA05EE52B03} h;hpp;hxx;hm;inl;inc;xsd - {3C46BFAA-5B89-490D-97E0-683EBE47B2F7} + {4CE674DD-0F36-49B2-AF98-8252D938425C} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {FCADAC43-F392-48DD-80AC-5956347871B5} + {8F3006B3-3A4B-4094-AA85-3461E4C4FEF2} inf;inv;inx;mof;mc; - {AB5F4882-673A-4F5F-AA55-82ECF7EC3896} + {682C4A58-1F91-422F-8E77-84FBAF36215A} diff --git a/network/ndis/ndisprot/6x/test/prottest.vcxproj b/network/ndis/ndisprot/6x/test/prottest.vcxproj index c4d5dde3a..a0c8b84a9 100644 --- a/network/ndis/ndisprot/6x/test/prottest.vcxproj +++ b/network/ndis/ndisprot/6x/test/prottest.vcxproj @@ -19,11 +19,11 @@ - {C5DBFAFB-4867-4605-B432-337BD17B972C} + {62009D19-D80B-46B8-8360-D84AA57DD567} $(MSBuildProjectName) Debug Win32 - {5F9D6D0B-B0FB-4DB3-BCBC-062001EC3BB3} + {97FEB721-B7B8-47E2-8626-1014E491740F} @@ -229,7 +229,6 @@ - diff --git a/network/ndis/ndisprot/6x/test/prottest.vcxproj.Filters b/network/ndis/ndisprot/6x/test/prottest.vcxproj.Filters index 298052d22..b234c0e71 100644 --- a/network/ndis/ndisprot/6x/test/prottest.vcxproj.Filters +++ b/network/ndis/ndisprot/6x/test/prottest.vcxproj.Filters @@ -3,15 +3,15 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {0061A859-F60A-4C25-BEBE-07969B9A5F3F} + {850BEAB1-E8E0-47E1-8E5A-6658B932B7D8} h;hpp;hxx;hm;inl;inc;xsd - {DC35B160-6C91-4008-98BB-CA550D5A4B2E} + {F8344599-BDE9-4143-A6B1-F26CC295C368} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {3E18084F-3C14-4C0D-98A1-490E38D175D7} + {9473E458-864C-4149-B1B7-95A8BFEF1BCE} diff --git a/network/ndis/ndisprot_kmdf/60/debug.c b/network/ndis/ndisprot_kmdf/60/debug.c new file mode 100644 index 000000000..59471e3fd --- /dev/null +++ b/network/ndis/ndisprot_kmdf/60/debug.c @@ -0,0 +1,423 @@ +/*++ + +Copyright (c) 1997 Microsoft Corporation + +Module Name: + + debug.c + +Abstract: + + This module contains all debug-related code. + +--*/ + +#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("%p ", 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 = NPROTL_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_kmdf/60/debug.h b/network/ndis/ndisprot_kmdf/60/debug.h new file mode 100644 index 000000000..ecc2092b1 --- /dev/null +++ b/network/ndis/ndisprot_kmdf/60/debug.h @@ -0,0 +1,218 @@ +/*++ + +Copyright (c) 2000 Microsoft Corporation + +Module Name: + + debug.h + +Abstract: + + Debug macros for NDISPROT + +--*/ + +#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", \ + (PUCHAR)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) _Analysis_assume_(exp) +#define NPROT_SET_SIGNATURE(s, t) +#define NPROT_STRUCT_ASSERT(s, t) + +#endif // DBG + + +#endif // _NPROTDEBUG__H + + diff --git a/network/ndis/ndisprot_kmdf/60/excallbk.c b/network/ndis/ndisprot_kmdf/60/excallbk.c new file mode 100644 index 000000000..59e8eb994 --- /dev/null +++ b/network/ndis/ndisprot_kmdf/60/excallbk.c @@ -0,0 +1,190 @@ +/*++ + +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 + +--*/ + +#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; + + UNREFERENCED_PARAMETER(CallBackContext); + + 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; + } + +#pragma warning (disable:4055) + func = (NOTIFY_PRESENCE_CALLBACK)CallbackAddr; +#pragma warning (default:4055) + + func(CALLBACK_SOURCE_NDISPROT); + } + + DEBUGP(DL_LOUD, ("<==ndisprotoCallback: Source, %lx\n", Source)); + +} + +#endif + + diff --git a/network/ndis/ndisprot_kmdf/60/macros.h b/network/ndis/ndisprot_kmdf/60/macros.h new file mode 100644 index 000000000..079f4f27e --- /dev/null +++ b/network/ndis/ndisprot_kmdf/60/macros.h @@ -0,0 +1,245 @@ +/*++ + +Copyright (c) 2000 Microsoft Corporation + +Module Name: + + macros.h + +Abstract: + + Some macros for NDISPROT. + +Environment: + + Kernel mode only. + +--*/ + + +#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)((_pNbl)->ProtocolReserved))->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_REQUEST_FROM_SEND_NBL(_pNbl) \ + (((PNPROT_SEND_NETBUFLIST_RSVD)((_pNbl)->Context->ContextData + (_pNbl)->Context->Offset))->Request) + +#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)) + + +// +// Block the calling thread for the given duration: +// +#define NPROT_SLEEP(_Seconds) \ +{ \ + NDIS_EVENT _SleepEvent; \ + NdisInitializeEvent(&_SleepEvent); \ + if ( NdisWaitEvent(&_SleepEvent, _Seconds*1000) ){}; \ +} + + +#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_kmdf/60/ndisbind.c b/network/ndis/ndisprot_kmdf/60/ndisbind.c new file mode 100644 index 000000000..0878e198f --- /dev/null +++ b/network/ndis/ndisprot_kmdf/60/ndisbind.c @@ -0,0 +1,2175 @@ +/*++ + +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. + +--*/ + + +#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; + WDF_IO_QUEUE_CONFIG queueConfig; + NTSTATUS ntStatus; + + 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); + // + // Manual queue for pending IRP_MJ_READ requests. We will + // manually remove the requests from the queue and service it in our + // ProtocolRecv indication handler. + // + + WDF_IO_QUEUE_CONFIG_INIT( + &queueConfig, + WdfIoQueueDispatchManual + ); + + ntStatus = WdfIoQueueCreate ( + Globals.ControlDevice, + &queueConfig, + WDF_NO_OBJECT_ATTRIBUTES, + &pOpenContext->ReadQueue + ); + + if(!NT_SUCCESS (ntStatus)){ + Status = NDIS_STATUS_FAILURE; + DEBUGP(DL_ERROR, ("WdfIoQueueCreate for read Queue failed 0x%x\n", ntStatus)); + break; + } + + // + // Register a notification so that we get notified whenever a new + // request shows up when the queue is idle. + // + ntStatus = WdfIoQueueReadyNotify(pOpenContext->ReadQueue, + ndisprotEvtNotifyReadQueue, + pOpenContext); + if(!NT_SUCCESS (ntStatus)){ + Status = NDIS_STATUS_FAILURE; + DEBUGP(DL_ERROR, ("WdfIoQueueReadyNotify for read queue failed 0x%x\n", ntStatus)); + break; + } + + // + // Create another manual queue to hold pending status-indication ioctl requests. + // + WDF_IO_QUEUE_CONFIG_INIT( + &queueConfig, + WdfIoQueueDispatchManual + ); + + ntStatus = WdfIoQueueCreate ( + Globals.ControlDevice, + &queueConfig, + WDF_NO_OBJECT_ATTRIBUTES, + &pOpenContext->StatusIndicationQueue + ); + + if(!NT_SUCCESS (ntStatus)){ + Status = NDIS_STATUS_FAILURE; + DEBUGP(DL_ERROR, ("WdfIoQueueCreate for ioctl queue failed 0x%x\n", ntStatus)); + break; + } + + 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); + + // + // Wait for any I/O in progress to complete. + // + ndisprotWaitForPendingIO(pOpenContext, FALSE); + + // + // 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); + if(!ndisprotRegisterExCallBack()){ + DEBUGP(DL_ERROR, ("NdisProtPnPEventHandler: ndisprotRegisterExCallBack failed\n")); + } + 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)); + + NPROT_SLEEP(1); + 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) + { + 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 [%ws]\n", + pOpenContext, pOpenContext->Flags, (PWSTR)pBindingInfo)); + + Status = NDIS_STATUS_SUCCESS; + + do + { + // + // Check if we already have a binding to this device. + // +#if DBG + pTmpOpenContext = ndisprotLookupDevice(pBindingInfo, BindingInfoLength); + + NPROT_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", + (ULONG)(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); + + NPROT_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)) + { + NPROT_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)); + } + + // + // Cancel any pending reads. + // + WdfIoQueuePurgeSynchronously(pOpenContext->ReadQueue); + // + // Cancel pending control request for status indication. + // + WdfIoQueuePurgeSynchronously(pOpenContext->StatusIndicationQueue); + + // + // 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; + } + + if (pOpenContext->ReadQueue) { + WdfObjectDelete(pOpenContext->ReadQueue); + pOpenContext->ReadQueue = NULL; + } + + if (pOpenContext->StatusIndicationQueue) { + WdfObjectDelete(pOpenContext->StatusIndicationQueue); + pOpenContext->StatusIndicationQueue = 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) + { + NPROT_ASSERT(pOpenContext->ClosingEvent == NULL); + } + else + { + NPROT_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. + // + WdfIoQueuePurgeSynchronously(pOpenContext->ReadQueue); + + NPROT_SLEEP(1); + } + } + +} + + +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 %p\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. + // + if ( NPROT_WAIT_EVENT(&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)) + { + NPROT_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 == NULL ? 0 : 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 +ndisServiceIndicateStatusIrp( + IN PNDISPROT_OPEN_CONTEXT OpenContext, + IN NDIS_STATUS GeneralStatus, + IN PVOID StatusBuffer, + IN UINT StatusBufferSize + ) +/*++ + +Routine Description: + + We process the Request based on the input arguments and complete + the Request. If the Request was cancelled for some reason we will let + the cancel routine do the Request completion. + +Arguments: + + ProtocolBindingContext - pointer to open context + GeneralStatus - status code + StatusBuffer - status-specific additional information + StatusBufferSize - size of the above + Cancel - Should the Request be cancelled right away. + +Return Value: + + None + +--*/ +{ + PNDISPROT_INDICATE_STATUS pIndicateStatus = NULL; + WDFREQUEST request = NULL; + NTSTATUS ntStatus; + ULONG bytes = 0; + size_t outBufLength; + + DEBUGP(DL_LOUD, ("-->ndisServiceIndicateStatusIrp\n")); + + NPROT_ACQUIRE_LOCK(&OpenContext->Lock, FALSE); + + do { + // + // Get the first pended Read Request + // + ntStatus = WdfIoQueueRetrieveNextRequest( + OpenContext->StatusIndicationQueue, + &request + ); + if(!NT_SUCCESS(ntStatus)){ + ASSERTMSG("WdfIoQueueRetrieveNextRequest failed", ntStatus == STATUS_NO_MORE_ENTRIES); + break; + } + + ntStatus = WdfRequestRetrieveOutputBuffer(request, + sizeof(NDISPROT_INDICATE_STATUS), + &pIndicateStatus, + &outBufLength); + if( !NT_SUCCESS(ntStatus) ) { + DEBUGP(DL_ERROR, ("WdfRequestRetrieveOutputBuffer failed 0x%x\n", ntStatus)); + break; + } + + // + // Check to see whether the buffer is large enough to accomadate the + // status buffer data. + // + if(outBufLength - sizeof(NDISPROT_INDICATE_STATUS) >= StatusBufferSize){ + + pIndicateStatus->IndicatedStatus = GeneralStatus; + pIndicateStatus->StatusBufferLength = StatusBufferSize; + pIndicateStatus->StatusBufferOffset = sizeof(NDISPROT_INDICATE_STATUS); + + NPROT_COPY_MEM((PUCHAR)pIndicateStatus + + pIndicateStatus->StatusBufferOffset, + StatusBuffer, + StatusBufferSize); + + + ntStatus = STATUS_SUCCESS; + + } else { + ntStatus = STATUS_BUFFER_OVERFLOW; + } + // + // Number of bytes copied or number of bytes required. + // + bytes = sizeof(NDISPROT_INDICATE_STATUS) + StatusBufferSize; + }while(FALSE); + + NPROT_RELEASE_LOCK(&OpenContext->Lock, FALSE); + + if(request){ + WdfRequestCompleteWithInformation(request, ntStatus, bytes); + } + + DEBUGP(DL_LOUD, ("<--ndisServiceIndicateStatusIrp\n")); + + return; + +} + +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)); + + ndisServiceIndicateStatusIrp(pOpenContext, + GeneralStatus, + StatusIndication->StatusBuffer, + StatusIndication->StatusBufferSize + ); + + 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: + + NPROT_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 +ndisprotQueryBinding( + IN 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 Remaining; + ULONG BindingIndex; + NDIS_STATUS Status; + + do + { + if (InputLength < sizeof(NDISPROT_QUERY_BINDING)) + { + Status = NDIS_STATUS_RESOURCES; + break; + } + + if (OutputLength < sizeof(NDISPROT_QUERY_BINDING)) + { + Status = NDIS_STATUS_BUFFER_OVERFLOW; + break; + } + + Remaining = OutputLength - sizeof(NDISPROT_QUERY_BINDING); + + 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->DeviceNameLength = pOpenContext->DeviceName.Length; + pQueryBinding->DeviceDescrLength = pOpenContext->DeviceDescr.Length; + if (Remaining < (pQueryBinding->DeviceNameLength + sizeof(WCHAR)) + + (pQueryBinding->DeviceDescrLength + sizeof(WCHAR))) + { + NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); + Status = NDIS_STATUS_BUFFER_OVERFLOW; + break; + } + + NPROT_ZERO_MEM((PUCHAR)pBuffer + sizeof(NDISPROT_QUERY_BINDING), + (pQueryBinding->DeviceNameLength + sizeof (WCHAR)) + + (pQueryBinding->DeviceDescrLength + sizeof(WCHAR))); + + pQueryBinding->DeviceNameOffset = sizeof(NDISPROT_QUERY_BINDING); + NPROT_COPY_MEM((PUCHAR)pBuffer + pQueryBinding->DeviceNameOffset, + pOpenContext->DeviceName.Buffer, + pOpenContext->DeviceName.Length); + + pQueryBinding->DeviceDescrOffset = pQueryBinding->DeviceNameOffset + + pQueryBinding->DeviceNameLength + sizeof(WCHAR); + NPROT_COPY_MEM((PUCHAR)pBuffer + pQueryBinding->DeviceDescrOffset, + pOpenContext->DeviceDescr.Buffer, + pOpenContext->DeviceDescr.Length); + + NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); + + *pBytesReturned = pQueryBinding->DeviceDescrOffset + pQueryBinding->DeviceDescrLength + sizeof(WCHAR); + 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)) + { + NPROT_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)) + { + NPROT_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 +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 128 + + 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; + + Length = BufferLength + sizeof(USHORT); + TotalLength += Length; + + 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_kmdf/60/ndisprot.h b/network/ndis/ndisprot_kmdf/60/ndisprot.h new file mode 100644 index 000000000..b85142736 --- /dev/null +++ b/network/ndis/ndisprot_kmdf/60/ndisprot.h @@ -0,0 +1,539 @@ +/*++ + +Copyright (c) 2000 Microsoft Corporation + +Module Name: + + ndisprot.h + +Abstract: + + Data structures, defines and function prototypes for NDISPROT. + +Environment: + + Kernel mode only. + +--*/ + +#ifndef __NDISPROT__H +#define __NDISPROT__H + +#pragma once + +#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 + +//----------------------------------------------------------------------------- +// 4127 -- Conditional Expression is Constant warning +//----------------------------------------------------------------------------- +#define WHILE(constant) \ +__pragma(warning(disable: 4127)) while(constant); __pragma(warning(default: 4127)) + + + +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; + + WDFFILEOBJECT 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; + + ULONG PendedSendCount; + + WDFQUEUE ReadQueue; + 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]; + + WDFQUEUE StatusIndicationQueue; +} NDISPROT_OPEN_CONTEXT, *PNDISPROT_OPEN_CONTEXT; + +typedef struct _FILEO_BJECT_CONTEXT { + + PNDISPROT_OPEN_CONTEXT OpenContext; + +} FILE_OBJECT_CONTEXT, *PFILE_OBJECT_CONTEXT; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(FILE_OBJECT_CONTEXT, GetFileObjectContext) + +typedef struct _REQUEST_CONTEXT { + + PNET_BUFFER_LIST NetBufferList; // used if we had to partial-map + ULONG Length; + +} REQUEST_CONTEXT, *PREQUEST_CONTEXT; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(REQUEST_CONTEXT, GetRequestContext) + +#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 DriverObject; + WDFDEVICE ControlDevice; + 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 +{ + WDFREQUEST Request; + 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; + +EVT_WDF_DRIVER_UNLOAD NdisProtEvtDriverUnload; +EVT_WDF_DEVICE_FILE_CREATE NdisProtEvtDeviceFileCreate; +EVT_WDF_FILE_CLOSE NdisProtEvtFileClose; +EVT_WDF_FILE_CLEANUP NdisProtEvtFileCleanup; +EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL NdisProtEvtIoDeviceControl; +EVT_WDF_IO_QUEUE_IO_READ NdisProtEvtIoRead; +EVT_WDF_IO_QUEUE_STATE ndisprotEvtNotifyReadQueue; +EVT_WDF_IO_QUEUE_IO_WRITE NdisProtEvtIoWrite; + + +NTSTATUS +NdisProtCreateControlDevice( + IN WDFDRIVER Driver, + IN PWDFDEVICE_INIT DeviceInit + ); + +NTSTATUS +ndisprotOpenDevice( + _In_reads_bytes_(DeviceNameLength) IN PUCHAR pDeviceName, + IN ULONG DeviceNameLength, + IN WDFFILEOBJECT FileObject, + 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( + IN 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 + ); + + + +VOID +ndisprotServiceReads( + IN PNDISPROT_OPEN_CONTEXT pOpenContext + ); + +PROTOCOL_RECEIVE_NET_BUFFER_LISTS NdisprotReceiveNetBufferLists; + +VOID +ndisprotShutdownBinding( + IN PNDISPROT_OPEN_CONTEXT pOpenContext + ); + +VOID +ndisprotQueueReceiveNetBufferList( + IN PNDISPROT_OPEN_CONTEXT pOpenContext, + IN PNET_BUFFER_LIST pRcvNetBufList, + BOOLEAN DispatchLevel + ); + +PNET_BUFFER_LIST +ndisprotAllocateReceiveNetBufferList( + IN PNDISPROT_OPEN_CONTEXT pOpenContext, + IN UINT DataLength, + OUT PUCHAR * ppDataBuffer + ); + +VOID +ndisprotFreeReceiveNetBufferList( + IN PNDISPROT_OPEN_CONTEXT pOpenContext, + IN PNET_BUFFER_LIST pNetBufferList, + IN BOOLEAN DispatchLevel + ); + +VOID +ndisprotFlushReceiveQueue( + IN PNDISPROT_OPEN_CONTEXT pOpenContext + ); + +PROTOCOL_SEND_NET_BUFFER_LISTS_COMPLETE NdisprotSendComplete; + +NTSTATUS +ndisprotCopyMdlToMdl( + IN PMDL SourceMdl, + IN SIZE_T SourceOffset, + IN PMDL TargetMdl, + IN SIZE_T TargetOffset, + IN SIZE_T BytesToCopy, + OUT SIZE_T* BytesCopied + ); + +VOID +ndisprotRestart( + IN PNDISPROT_OPEN_CONTEXT pOpenContext, + IN PNDIS_PROTOCOL_RESTART_PARAMETERS RestartParameters + ); + + +#ifdef EX_CALLBACK + +BOOLEAN +ndisprotRegisterExCallBack(); + +VOID +ndisprotUnregisterExCallBack(); + +VOID +ndisprotCallback( + PVOID CallBackContext, + PVOID Source, + PVOID NotifyPresenceCallback + ); + +#else + +#define ndisprotRegisterExCallBack() TRUE +#define ndisprotUnregisterExCallBack() + +#endif + +#endif // __NDISPROT__H + diff --git a/network/ndis/ndisprot_kmdf/60/ndisprot.inx b/network/ndis/ndisprot_kmdf/60/ndisprot.inx new file mode 100644 index 000000000..c45f23e59 --- /dev/null +++ b/network/ndis/ndisprot_kmdf/60/ndisprot.inx @@ -0,0 +1,118 @@ +;------------------------------------------------------------------------- +; NDISPROT.INF -- Sample NDIS Protocol Driver +; +; Copyright (c) 2003, Microsoft Corporation +;------------------------------------------------------------------------- +[version] +Signature = "$Windows NT$" +Class = NetTrans +ClassGUID = {4d36e975-e325-11ce-bfc1-08002be10318} +CatalogFile = nprt6wdf.cat +Provider = %ProviderString% + +[Manufacturer] +%ManufacturerName%=Standard,NT$ARCH$ + +[Standard.NT$ARCH$] +%NDISPROT_Desc%=Install, MS_NDISPROT + +[SourceDisksNames] +1 = %NDISPROT_Disk%,, + +[SourceDisksFiles] +nprt6wdf.sys = 1,, +ProtNotify.dll = 1,, +WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll = 1,, + +;------------------------------------------------------------------------- +; Installation Section +;------------------------------------------------------------------------- +[Install] +AddReg = Inst_Ndi +CopyFiles = CpyCoInstaller_DLL, CpyFilesNotify_DLL, CpyFiles_Sys +Characteristics=0x0 ; + +;------------------------------------------------------------------------- +; Ndi installation support +;------------------------------------------------------------------------- +[Inst_Ndi] +HKR, Ndi , ClsID ,0, {21e7e731-f286-4116-b3c2-d43ccba29f07} +HKR, Ndi , Service , , "Ndisprot" +HKR, Ndi , ComponentDll , , ProtNotify.dll +HKR, Ndi , HelpText , , %NDISPROT_HelpText% +HKR, Ndi\Interfaces , UpperRange , , noupper +HKR, "Ndi\Interfaces", "LowerRange" , , "ndis5,ndis4,ndis5_prot" + +;------------------------------------------------------------------------- +; NdisProt keys used by Component Dll +;------------------------------------------------------------------------- +[NdisProt_AddReg] +HKR, Parameters , SourceInfFile, , %1%\ndisprot.inf +HKR, Parameters , WdfSection , , "WdfSection" + +;------------------------------------------------------------------------- +; 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%\nprt6wdf.sys +Description = %NDISPROT_Desc% +AddReg = NdisProt_AddReg, NdisImPlatformBinding_AddReg + +[NdisImPlatformBinding_AddReg] +; 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 +;------------------------------------------------------------------------- +[DestinationDirs] +CpyCoInstaller_DLL = 11 +CpyFilesNotify_DLL = 11 +CpyFiles_Sys = 12 ; DIRID_DRIVERS + +;------------------------------------------------------------------------- +; Files to Copy/Delete - Referenced by Install and Remove sections above +;------------------------------------------------------------------------- +[CpyCoInstaller_DLL] +WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll,,,2 + +[CpyFilesNotify_DLL] +ProtNotify.dll,,,2 + +[CpyFiles_Sys] +nprt6wdf.sys,,,2 + +;------------------------------------------------------------------------- +; NDISPROT Coinstaller installation +;------------------------------------------------------------------------- +[WdfSection] +KmdfService = NdisProt, NdisProt_WdfSection + +[NdisProt_WdfSection] +KmdfLibraryVersion = $KMDFVERSION$ + + +[Strings] +ProviderString = "TODO-Set-Provider" +ManufacturerName="TODO-Set-Manufacturer" +NDISPROT_Desc = "Sample NDIS Protocol Driver" +NDISPROT_Disk = "Sample NDIS Protocol Driver Files" +NDISPROT_HelpText = "A driver to support user-mode I/O on NDIS devices" diff --git a/network/ndis/ndisprot_kmdf/60/ndisprot.rc b/network/ndis/ndisprot_kmdf/60/ndisprot.rc new file mode 100644 index 000000000..42f838384 --- /dev/null +++ b/network/ndis/ndisprot_kmdf/60/ndisprot.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 "NDIS User mode I/O Driver" +#define VER_INTERNALNAME_STR "NDISPROT.SYS" +#define VER_ORIGINALFILENAME_STR "NDISPROT.SYS" + +#include "common.ver" + + diff --git a/network/ndis/ndisprot_kmdf/60/nprt6wdf.vcxproj b/network/ndis/ndisprot_kmdf/60/nprt6wdf.vcxproj new file mode 100644 index 000000000..0dfe75d5a --- /dev/null +++ b/network/ndis/ndisprot_kmdf/60/nprt6wdf.vcxproj @@ -0,0 +1,237 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {85BE7B07-69F5-4BC1-A755-483A9051AAC4} + $(MSBuildProjectName) + 1 + false + true + Debug + Win32 + {5FFC7B32-58A8-4689-BB54-F9599B4ADBA4} + + + + 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) + + + + + + + + + + + + + + + + $(InfArch) + true + .\$(IntDir)\ndisprot.inf + + + + nprt6wdf + + + nprt6wdf + + + nprt6wdf + + + nprt6wdf + + + + %(PreprocessorDefinitions);NDIS_WDM=1;NDIS60=1 + + + %(PreprocessorDefinitions);NDIS_WDM=1;NDIS60=1 + + + %(PreprocessorDefinitions);NDIS_WDM=1;NDIS60=1 + + + + + %(PreprocessorDefinitions);NDIS_WDM=1;NDIS60=1 + + + %(PreprocessorDefinitions);NDIS_WDM=1;NDIS60=1 + + + %(PreprocessorDefinitions);NDIS_WDM=1;NDIS60=1 + + + + + %(PreprocessorDefinitions);NDIS_WDM=1;NDIS60=1 + + + %(PreprocessorDefinitions);NDIS_WDM=1;NDIS60=1 + + + %(PreprocessorDefinitions);NDIS_WDM=1;NDIS60=1 + + + + + %(PreprocessorDefinitions);NDIS_WDM=1;NDIS60=1 + + + %(PreprocessorDefinitions);NDIS_WDM=1;NDIS60=1 + + + %(PreprocessorDefinitions);NDIS_WDM=1;NDIS60=1 + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ndis.lib;$(DDK_LIB_PATH)\wdmsec.lib + + + + + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ndis.lib;$(DDK_LIB_PATH)\wdmsec.lib + + + + + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ndis.lib;$(DDK_LIB_PATH)\wdmsec.lib + + + + + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ndis.lib;$(DDK_LIB_PATH)\wdmsec.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 + Use + $(IntDir)\precomp.pch + + + ;%(AdditionalIncludeDirectories) + precomp.h + Create + $(IntDir)\precomp.pch + + + ;%(AdditionalIncludeDirectories) + precomp.h + Use + $(IntDir)\precomp.pch + + + ;%(AdditionalIncludeDirectories) + precomp.h + Use + $(IntDir)\precomp.pch + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/network/ndis/ndisprot_kmdf/60/nprt6wdf.vcxproj.Filters b/network/ndis/ndisprot_kmdf/60/nprt6wdf.vcxproj.Filters new file mode 100644 index 000000000..f7dd56579 --- /dev/null +++ b/network/ndis/ndisprot_kmdf/60/nprt6wdf.vcxproj.Filters @@ -0,0 +1,54 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {E4D652D6-CACA-4396-AD3B-663DF32E592D} + + + h;hpp;hxx;hm;inl;inc;xsd + {6E73277A-3F21-4FCF-A20D-60158B809E3F} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {054D1114-7FD2-462E-A612-2ED4BC48F6EF} + + + inf;inv;inx;mof;mc; + {3B2480E6-3700-4AEB-A0B5-7D3C29E29B1B} + + + + + Driver 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/ndisprot_kmdf/60/ntdisp.c b/network/ndis/ndisprot_kmdf/60/ntdisp.c new file mode 100644 index 000000000..9c832daad --- /dev/null +++ b/network/ndis/ndisprot_kmdf/60/ntdisp.c @@ -0,0 +1,1086 @@ +/*++ + +Copyright (c) 2000 Microsoft Corporation + +Module Name: + + ntdisp.c + +Abstract: + + NT Entry points and dispatch routines for NDISPROT. + +Environment: + + Kernel mode only. + +--*/ + +#include "precomp.h" + +#define __FILENUMBER 'PSID' + + +#ifdef ALLOC_PRAGMA + +#pragma alloc_text(INIT, DriverEntry) + +#endif // ALLOC_PRAGMA + + +// +// Globals: +// +NDISPROT_GLOBALS Globals = {0}; + +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ) +/*++ + +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; + NTSTATUS status = STATUS_SUCCESS; + NDIS_STRING protoName = NDIS_STRING_CONST("NDISPROT"); + WDF_DRIVER_CONFIG config; + WDFDRIVER hDriver; + PWDFDEVICE_INIT pInit = NULL; + + UNREFERENCED_PARAMETER(RegistryPath); + + DEBUGP(DL_LOUD, ("DriverEntry\n")); + + Globals.DriverObject = DriverObject; + Globals.EthType = NPROT_ETH_TYPE; + NPROT_INIT_EVENT(&Globals.BindsComplete); + + WDF_DRIVER_CONFIG_INIT( + &config, + WDF_NO_EVENT_CALLBACK // This is a non-pnp driver. + ); + + // + // Tell the framework that this is non-pnp driver so that it doesn't + // set the default AddDevice routine. + // + config.DriverInitFlags |= WdfDriverInitNonPnpDriver; + + + // + // We need an unload routine to free control device created below. For + // non-pnp drivers, framework doesn't provide Unload routine. + // + config.EvtDriverUnload = NdisProtEvtDriverUnload; + + // + // Create a framework driver object to represent our driver. + // + status = WdfDriverCreate(DriverObject, + RegistryPath, + WDF_NO_OBJECT_ATTRIBUTES, + &config, + &hDriver); + if (!NT_SUCCESS(status)) { + DEBUGP(DL_ERROR, ("WdfDriverCreate failed with status 0x%x\n", status)); + return status; + } + + // + // + // In order to create a control device, we first need to allocate a + // WDFDEVICE_INIT structure and set all properties. + // + pInit = WdfControlDeviceInitAllocate( + hDriver, + &SDDL_DEVOBJ_SYS_ALL_ADM_RWX_WORLD_RW_RES_R + ); + + if (pInit == NULL) { + status = STATUS_INSUFFICIENT_RESOURCES; + return status; + } + + // + // Call NdisProtDeviceAdd to create WDFDEVICE to represent our + // software device. + // + status = NdisProtCreateControlDevice(hDriver, pInit); + if (!NT_SUCCESS(status)) { + DEBUGP (DL_ERROR, ("NdisProtCreateControlDevice failed with status 0x%x\n", status)); + return status; + } + + // + // Initialize the protocol characterstic structure + // + + NdisZeroMemory(&protocolChar,sizeof(NDIS_PROTOCOL_DRIVER_CHARACTERISTICS)); + + + protocolChar.Header.Type = NDIS_OBJECT_TYPE_PROTOCOL_DRIVER_CHARACTERISTICS, + protocolChar.Header.Size = sizeof(NDIS_PROTOCOL_DRIVER_CHARACTERISTICS); + protocolChar.Header.Revision = NDIS_PROTOCOL_DRIVER_CHARACTERISTICS_REVISION_1; + + protocolChar.MajorNdisVersion = 6; + protocolChar.MinorNdisVersion = 0; + 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(NULL, // driver context + &protocolChar, + &Globals.NdisProtocolHandle); + + if (status != NDIS_STATUS_SUCCESS) + { + DEBUGP(DL_WARN, ("Failed to register protocol with NDIS\n")); + return STATUS_UNSUCCESSFUL; + } + + NPROT_INIT_LIST_HEAD(&Globals.OpenList); + NPROT_INIT_LOCK(&Globals.GlobalLock); + + Globals.PartialCancelId = NdisGeneratePartialCancelId(); + Globals.PartialCancelId <<= ((sizeof(PVOID) - 1) * 8); + DEBUGP(DL_LOUD, ("DriverEntry: CancelId %lx\n", Globals.PartialCancelId)); + + return status; + +} + +NTSTATUS +NdisProtCreateControlDevice( + IN WDFDRIVER Driver, + IN PWDFDEVICE_INIT DeviceInit + ) +/*++ + +Routine Description: + + Called by the DriverEntry to create a control-device. This call is + responsible for freeing the memory for DeviceInit. + +Arguments: + + Driver - a pointer to the framework object that represents this device + driver. + + DeviceInit - Pointer to a driver-allocated WDFDEVICE_INIT structure. + +Return Value: + + STATUS_SUCCESS if initialized; an error otherwise. + +--*/ +{ + NTSTATUS status; + WDF_OBJECT_ATTRIBUTES objectAttribs; + WDF_IO_QUEUE_CONFIG ioQueueConfig; + WDF_FILEOBJECT_CONFIG fileConfig; + WDFDEVICE controlDevice = NULL; + WDFQUEUE queue; + DECLARE_CONST_UNICODE_STRING(ntDeviceName, NT_DEVICE_NAME) ; + DECLARE_CONST_UNICODE_STRING(dosDeviceName, DOS_DEVICE_NAME) ; + + UNREFERENCED_PARAMETER( Driver ); + + DEBUGP(DL_LOUD, ("NdisProtCreateControlDevice DeviceInit %p\n", DeviceInit)); + + // + // I/O type is Buffered by default. We want to do direct I/O for Reads + // and Writes so set it explicitly. + // + WdfDeviceInitSetIoType(DeviceInit, WdfDeviceIoDirect); + + status = WdfDeviceInitAssignName(DeviceInit, &ntDeviceName); + + if (!NT_SUCCESS(status)) { + goto Error; + } + + // + // Initialize WDF_FILEOBJECT_CONFIG_INIT struct to tell the + // framework whether you are interested in handle Create, Close and + // Cleanup requests that gets genereated 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, + NdisProtEvtDeviceFileCreate, + NdisProtEvtFileClose, + NdisProtEvtFileCleanup + ); + + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&objectAttribs, + FILE_OBJECT_CONTEXT); + + WdfDeviceInitSetFileObjectConfig(DeviceInit, + &fileConfig, + &objectAttribs); + + + WDF_OBJECT_ATTRIBUTES_INIT(&objectAttribs); + + status = WdfDeviceCreate(&DeviceInit, + &objectAttribs, + &controlDevice); + if (!NT_SUCCESS(status)) { + goto Error; + } + + // + // DeviceInit is set to NULL if the device is created successfully. + // + + // + // Create a symbolic link for the control object so that usermode can open + // the device. + // + status = WdfDeviceCreateSymbolicLink(controlDevice, &dosDeviceName); + + if (!NT_SUCCESS(status)) { + goto Error; + } + + // + // Configure a default queue associated with the control device to + // to receive read, write, and ioctl requests in parallel. + // A default queue gets all the requests that are not + // configure-fowarded using WdfDeviceConfigureRequestDispatching. + // + + WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&ioQueueConfig, + WdfIoQueueDispatchParallel); + + ioQueueConfig.EvtIoWrite = NdisProtEvtIoWrite; + ioQueueConfig.EvtIoRead = NdisProtEvtIoRead; + ioQueueConfig.EvtIoDeviceControl = NdisProtEvtIoDeviceControl; + + status = WdfIoQueueCreate(controlDevice, + &ioQueueConfig, + WDF_NO_OBJECT_ATTRIBUTES, + &queue // pointer to default queue + ); + if (!NT_SUCCESS(status)) { + goto Error; + } + + // + // Control devices must notify WDF when they are done initializing. I/O is + // rejected until this call is made. + // + WdfControlFinishInitializing(controlDevice); + + // + // Create our device object using which an application can + // access NDIS devices. + // + + Globals.ControlDevice = controlDevice; + + return status; + +Error: + + if(DeviceInit != NULL) { + // + // Free the WDFDEVICE_INIT structure only if the device + // creation fails. Otherwise framework frees the memory + // itself. + // + WdfDeviceInitFree(DeviceInit); + } + + return status; + +} + +VOID +NdisProtEvtDriverUnload( + IN WDFDRIVER Driver + ) +/*++ + +Routine Description: + + Free all the allocated resources, etc. + +Arguments: + + Driver - pointer to a framework driver object. + +Return Value: + + VOID. + +--*/ +{ + UNREFERENCED_PARAMETER(Driver); + + DEBUGP(DL_LOUD, ("Unload Enter\n")); + + ndisprotUnregisterExCallBack(); + + ndisprotDoProtocolUnload(); + +#if DBG + ndisprotAuditShutdown(); +#endif + + // + // All the other resources such as control-device, symbolic link, + // queue associated with the device will be automatically + // deleted by the framework. + // + Globals.ControlDevice = NULL; + + DEBUGP(DL_LOUD, ("Unload Exit\n")); +} + + +VOID +NdisProtEvtDeviceFileCreate( + 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 a file. + This callback is called synchronously, 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 of IO_STACK_LOCATION for create + +Return Value: + + NT status code + +--*/ +{ + NTSTATUS NtStatus = STATUS_SUCCESS; + PFILE_OBJECT_CONTEXT fileContext; + + UNREFERENCED_PARAMETER(Device); + + DEBUGP(DL_INFO, ("Open: FileObject %p\n", FileObject)); + + fileContext = GetFileObjectContext(FileObject); + + fileContext->OpenContext = NULL; + + WdfRequestComplete(Request, NtStatus); + + return; +} + +VOID +NdisProtEvtFileClose( + 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: + + VOID + +--*/ +{ + PNDISPROT_OPEN_CONTEXT pOpenContext; + PFILE_OBJECT_CONTEXT fileContext; + + fileContext = GetFileObjectContext(FileObject); + + pOpenContext = fileContext->OpenContext; + + DEBUGP(DL_INFO, ("Close: FileObject %p\n", FileObject)); + + if (pOpenContext != NULL) + { + NPROT_STRUCT_ASSERT(pOpenContext, oc); + + // + // Deref the endpoint + // + NPROT_DEREF_OPEN(pOpenContext); // Close + } + + fileContext->OpenContext = NULL; + + return; +} + +VOID +NdisProtEvtFileCleanup( + IN WDFFILEOBJECT FileObject + ) +/*++ + +Routine Description: + + EvtFileCleanup is called when the handle represented by the FileObject + is closed. This callback is invoked in the context of the thread that + closed the handle. + +Arguments: + + FileObject - Pointer to fileobject that represents the open handle. + +Return Value: + + VOID + +--*/ +{ + NTSTATUS NtStatus; + NDIS_STATUS NdisStatus; + PNDISPROT_OPEN_CONTEXT pOpenContext; + ULONG PacketFilter; + ULONG BytesProcessed; + PFILE_OBJECT_CONTEXT fileContext; + + fileContext = GetFileObjectContext(FileObject); + + pOpenContext = fileContext->OpenContext; + + DEBUGP(DL_VERY_LOUD, ("Cleanup: FileObject %p, Open %p\n", + FileObject, pOpenContext)); + + if (pOpenContext != NULL) + { + NPROT_STRUCT_ASSERT(pOpenContext, oc); + + // + // 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); + + // + // 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; + } + + + NPROT_ACQUIRE_LOCK(&pOpenContext->Lock, FALSE); + + if (NPROT_TEST_FLAGS(pOpenContext->Flags, NPROTO_BIND_FLAGS, NPROTO_BIND_ACTIVE)){ + + NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); + + // + // Cancel any pending reads. + // + WdfIoQueuePurgeSynchronously(pOpenContext->ReadQueue); + // + // Cancel pending control request for status indication. + // + WdfIoQueuePurgeSynchronously(pOpenContext->StatusIndicationQueue); + } else { + + NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); + } + + // + // Clean up the receive packet queue + // + ndisprotFlushReceiveQueue(pOpenContext); + } + + NtStatus = STATUS_SUCCESS; + + DEBUGP(DL_INFO, ("Cleanup: OpenContext %p\n", pOpenContext)); + + return; +} + +VOID +NdisProtEvtIoDeviceControl( + 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 NtStatus; + NDIS_STATUS Status; + PNDISPROT_OPEN_CONTEXT pOpenContext; + ULONG BytesReturned; + PVOID sysBuffer; + WDFFILEOBJECT fileObject = WdfRequestGetFileObject(Request); + size_t bufSize; + + UNREFERENCED_PARAMETER(Queue); + UNREFERENCED_PARAMETER(OutputBufferLength); + UNREFERENCED_PARAMETER(InputBufferLength); + + DEBUGP(DL_LOUD, ("IoControl: Irp %p\n", Request)); + + pOpenContext = GetFileObjectContext(fileObject)->OpenContext; + + BytesReturned = 0; + + + switch (IoControlCode) + { + 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((IoControlCode & 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((IoControlCode & 0x3) == METHOD_BUFFERED); + + NtStatus = WdfRequestRetrieveOutputBuffer(Request, + sizeof(NDISPROT_QUERY_BINDING), + &sysBuffer, + &bufSize); + if( !NT_SUCCESS(NtStatus) ) { + DEBUGP(DL_FATAL, ("WdfRequestRetrieveOutputBuffer failed %x\n", NtStatus)); + break; + } + + Status = ndisprotQueryBinding( + sysBuffer, + (ULONG) bufSize, + (ULONG) bufSize, + &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((IoControlCode & 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", fileObject, pOpenContext)); + + NtStatus = STATUS_DEVICE_BUSY; + break; + } + + NtStatus = WdfRequestRetrieveInputBuffer(Request, + 0, + &sysBuffer, + &bufSize); + if( !NT_SUCCESS(NtStatus) ) { + DEBUGP(DL_FATAL, ("WdfRequestRetrieveInputBuffer failed %x\n", NtStatus)); + break; + } + + NtStatus = ndisprotOpenDevice( + sysBuffer, + (ULONG)bufSize, + fileObject, + &pOpenContext + ); + + if (NT_SUCCESS(NtStatus)) + { + + DEBUGP(DL_VERY_LOUD, ("IoControl OPEN_DEVICE: Open %p <-> FileObject %p\n", + pOpenContext, fileObject)); + } + + break; + + case IOCTL_NDISPROT_QUERY_OID_VALUE: + + NPROT_ASSERT((IoControlCode & 0x3) == METHOD_BUFFERED); + + NtStatus = WdfRequestRetrieveOutputBuffer(Request, + sizeof(NDISPROT_QUERY_OID), + &sysBuffer, + &bufSize); + if( !NT_SUCCESS(NtStatus) ) { + DEBUGP(DL_FATAL, ("WdfRequestRetrieveOutputBuffer failed %x\n", NtStatus)); + break; + } + + if (pOpenContext != NULL) + { + Status = ndisprotQueryOidValue( + pOpenContext, + sysBuffer, + (ULONG)bufSize, + &BytesReturned + ); + + NDIS_STATUS_TO_NT_STATUS(Status, &NtStatus); + } + else + { + NtStatus = STATUS_DEVICE_NOT_CONNECTED; + } + break; + + case IOCTL_NDISPROT_SET_OID_VALUE: + + NPROT_ASSERT((IoControlCode & 0x3) == METHOD_BUFFERED); + + NtStatus = WdfRequestRetrieveInputBuffer(Request, + sizeof(NDISPROT_SET_OID), + &sysBuffer, + &bufSize); + if( !NT_SUCCESS(NtStatus) ) { + DEBUGP(DL_FATAL, ("WdfRequestRetrieveInputBuffer failed %x\n", NtStatus)); + break; + } + + + if (pOpenContext != NULL) + { + Status = ndisprotSetOidValue( + pOpenContext, + sysBuffer, + (ULONG)bufSize + ); + + BytesReturned = 0; + + NDIS_STATUS_TO_NT_STATUS(Status, &NtStatus); + } + else + { + NtStatus = STATUS_DEVICE_NOT_CONNECTED; + } + break; + + case IOCTL_NDISPROT_INDICATE_STATUS: + + NPROT_ASSERT((IoControlCode & 0x3) == METHOD_BUFFERED); + + if (pOpenContext != NULL) + { + NtStatus = WdfRequestForwardToIoQueue(Request, + pOpenContext->StatusIndicationQueue + ); + if(NT_SUCCESS(NtStatus)) { + NtStatus = STATUS_PENDING; + } + } + else + { + NtStatus = STATUS_DEVICE_NOT_CONNECTED; + } + break; + + default: + + NtStatus = STATUS_NOT_SUPPORTED; + break; + } + + if (NtStatus != STATUS_PENDING) + { + WdfRequestCompleteWithInformation(Request, NtStatus, BytesReturned); + } + + return; +} + + +NTSTATUS +ndisprotOpenDevice( + _In_reads_bytes_(DeviceNameLength) IN PUCHAR pDeviceName, + IN ULONG DeviceNameLength, + IN WDFFILEOBJECT FileObject, + 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; + PFILE_OBJECT_CONTEXT fileContext; + + pOpenContext = NULL; + fileContext = GetFileObjectContext(FileObject); + + 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 (& (fileContext->OpenContext), 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", + FileObject, pCurrentOpenContext, pCurrentOpenContext->Flags)); //BUG + + NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); + + NPROT_DEREF_OPEN(pOpenContext); // ndisprotOpenDevice failure + NtStatus = STATUS_INVALID_DEVICE_REQUEST; + break; + } + + pOpenContext->pFileObject = FileObject; + + // + // Start the queue because it may be in a purged state if some body + // has already opened the device earlier. + // + WdfIoQueueStart(pOpenContext->ReadQueue); + WdfIoQueueStart(pOpenContext->StatusIndicationQueue); + + 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 (& (fileContext->OpenContext), 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_kmdf/60/precomp.h b/network/ndis/ndisprot_kmdf/60/precomp.h new file mode 100644 index 000000000..90acd01e3 --- /dev/null +++ b/network/ndis/ndisprot_kmdf/60/precomp.h @@ -0,0 +1,23 @@ +#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 + +#define WIN9X_COMPAT_SPINLOCK + +#include "ndis.h" +#include "ntddk.h" +#include +#include +#include +#include +#include "debug.h" +#include "ndisprot.h" +#include "macros.h" +#include "protuser.h" +#define NTSTRSAFE_LIB +#include diff --git a/network/ndis/ndisprot_kmdf/60/precompsrc.c b/network/ndis/ndisprot_kmdf/60/precompsrc.c new file mode 100644 index 000000000..5944cf515 --- /dev/null +++ b/network/ndis/ndisprot_kmdf/60/precompsrc.c @@ -0,0 +1 @@ +#include "precomp.h" \ No newline at end of file diff --git a/network/ndis/ndisprot_kmdf/60/protuser.h b/network/ndis/ndisprot_kmdf/60/protuser.h new file mode 100644 index 000000000..44a57c953 --- /dev/null +++ b/network/ndis/ndisprot_kmdf/60/protuser.h @@ -0,0 +1,107 @@ +/*++ + +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 __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) + + +#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; + 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; + +// +// 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 // __NPROTUSER__H + + diff --git a/network/ndis/ndisprot_kmdf/60/recv.c b/network/ndis/ndisprot_kmdf/60/recv.c new file mode 100644 index 000000000..75b4da4d2 --- /dev/null +++ b/network/ndis/ndisprot_kmdf/60/recv.c @@ -0,0 +1,1101 @@ +/*++ + +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. + +--*/ + +#include "precomp.h" + +#define __FILENUMBER 'VCER' + + +VOID +NdisProtEvtIoRead( + IN WDFQUEUE Queue, + IN WDFREQUEST Request, + IN size_t Length + ) +/*++ + +Routine Description: + + This event is called when the framework receives IRP_MJ_READ requests. + We will just read the file. + +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. + +Return Value: + + None. + +--*/ +{ + NTSTATUS NtStatus; + PNDISPROT_OPEN_CONTEXT pOpenContext; + WDFFILEOBJECT fileObject; + + UNREFERENCED_PARAMETER(Queue); + UNREFERENCED_PARAMETER(Length); + + fileObject = WdfRequestGetFileObject(Request); + + pOpenContext = GetFileObjectContext(fileObject)->OpenContext; + + do + { + // + // Validate! + // + if (pOpenContext == NULL) + { + DEBUGP(DL_FATAL, ("Read: NULL FsContext on FileObject %p\n", fileObject)); + NtStatus = STATUS_INVALID_HANDLE; + break; + } + + NPROT_STRUCT_ASSERT(pOpenContext, oc); + + 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; + } + + NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); + + // + // Forward this request to the pending read Request queue. We don't have to + // worry about cancelation of requests pending in the queue because + // framework takes care of that. + // + NtStatus = WdfRequestForwardToIoQueue(Request, + pOpenContext->ReadQueue); + + } + while (FALSE); + + if (!NT_SUCCESS(NtStatus)) + { + WdfRequestCompleteWithInformation(Request, NtStatus, 0); + } + + return; +} + +VOID +ndisprotEvtNotifyReadQueue( + IN WDFQUEUE Queue, + IN WDFCONTEXT Context + ) +/*++ + +Routine Description: + + This event will be called every time the number of requests in the + queue changes from 0 to 1. + +Arguments: + + Queue - Read queue + pOpenContext - pointer to open context + +Return Value: + + None + +--*/ +{ + PNDISPROT_OPEN_CONTEXT pOpenContext = (PNDISPROT_OPEN_CONTEXT)Context; + + UNREFERENCED_PARAMETER(Queue); + + ndisprotServiceReads(pOpenContext); +} + + +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 + +--*/ +{ + PNET_BUFFER_LIST pRcvNetBufList; + PLIST_ENTRY pRcvNetBufListEntry; + PUCHAR pSrc, pDst; + ULONG BytesRemaining; // at pDst + PMDL pMdl; + ULONG BytesAvailable; + NTSTATUS ntStatus = STATUS_UNSUCCESSFUL; + WDFREQUEST request; + ULONG bytesCopied = 0, totalLength; + + 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->RecvNetBufListQueue) == FALSE) + { + // + // Get the first pended Read Request + // + ntStatus = WdfIoQueueRetrieveNextRequest( + pOpenContext->ReadQueue, + &request + ); + if(!NT_SUCCESS(ntStatus)){ + ASSERTMSG("WdfIoQueueRetrieveNextRequest failed", ntStatus == STATUS_NO_MORE_ENTRIES); + break; + } + + ntStatus = WdfRequestRetrieveOutputWdmMdl(request, &pMdl); + if (!NT_SUCCESS(ntStatus)) + { + DEBUGP(DL_FATAL, ("Read: WdfRequestRetrieveOutputWdmMdl %x\n", ntStatus)); + break; + } + + pDst = MmGetSystemAddressForMdlSafe(pMdl, NormalPagePriority); + if (pDst == NULL) + { + DEBUGP(DL_FATAL, ("Read: MmGetSystemAddr failed for Request %p, MDL %p\n", + request, pDst)); + ntStatus = STATUS_INSUFFICIENT_RESOURCES; + 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_RCV_NBL_FROM_LIST_ENTRY(pRcvNetBufListEntry) = NULL; + + NPROT_ASSERT(pRcvNetBufList != NULL); + NPROT_ASSERT(pRcvNetBufList->FirstNetBuffer != NULL); + + totalLength = BytesRemaining = MmGetMdlByteCount(pMdl); + pMdl = pRcvNetBufList->FirstNetBuffer->MdlChain; + + // + // 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. + // + + while (BytesRemaining && (pMdl != NULL)) + { + 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; + } + + if (BytesAvailable) + { + ULONG BytesToCopy = MIN(BytesAvailable, BytesRemaining); + + NPROT_COPY_MEM(pDst, pSrc, BytesToCopy); + BytesRemaining -= BytesToCopy; + pDst += BytesToCopy; + } + + NdisGetNextMdl(pMdl, &pMdl); + } + + // + // Complete the request. + // + bytesCopied = totalLength - BytesRemaining; + + DEBUGP(DL_INFO, ("ServiceReads: Open %p, IRP %p completed with %d bytes\n", + pOpenContext, request, bytesCopied)); + + WdfRequestCompleteWithInformation(request, STATUS_SUCCESS, bytesCopied); + + ndisprotFreeReceiveNetBufferList(pOpenContext, pRcvNetBufList,FALSE); + + 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; + UINT TotalLength; + SIZE_T BytesCopied; + PNET_BUFFER_LIST pNetBufList; + 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; + + 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 = pNetBufList->FirstNetBuffer->CurrentMdl; + TotalLength = pNetBufList->FirstNetBuffer->DataLength; + Offset = pNetBufList->FirstNetBuffer->CurrentMdlOffset; + 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); + + if (NDIS_TEST_RECEIVE_CANNOT_PEND(ReceiveFlags)) + { + 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 = ndisprotCopyMdlToMdl( + pNetBufList->FirstNetBuffer->MdlChain, + pNetBufList->FirstNetBuffer->DataOffset, + pCopyNetBufList->FirstNetBuffer->MdlChain, + 0, + TotalLength, + &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); + 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)) + { + 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); +} + + +PNET_BUFFER_LIST +ndisprotAllocateReceiveNetBufferList( + IN PNDISPROT_OPEN_CONTEXT pOpenContext, + IN UINT DataLength, + OUT 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; + + 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 = pNetBufferList->FirstNetBuffer->MdlChain; + TotalLength = pNetBufferList->FirstNetBuffer->DataLength; + + 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 +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 +} + + + +NTSTATUS +ndisprotCopyMdlToMdl( + IN PMDL SourceMdl, + IN SIZE_T SourceOffset, + IN PMDL TargetMdl, + IN SIZE_T TargetOffset, + IN SIZE_T BytesToCopy, + OUT SIZE_T* BytesCopied + ) + +/*++ + +Routine Description: + + This routine copies the contents of one MDL chain into another MDL chain. + A maximum of BytesToCopy bytes will be copied. + The actual number of bytes copied is returned in BytesCopied. + +Arguments: + + SourceMdl - Supplies the source of the data to be copied. + + SourceOffset - Supplies the offset into the source from which to begin. + + TargetMdl - Supplies the target to which data should be copied. + + TargetOffset - Supplies the offset into the target at which to begin. + + BytesToCopy - Supplies the number of bytes to copy. + + BytesCopied - Returns the actual number of bytes copied. + +Return Value: + + STATUS_SUCCESS - BytesCopied indicates the number of bytes successfully + transferred. + + STATUS_INSUFFICIENT_RESOURCES - if one of the MDLs could not be mapped. + +Caller IRQL: Must be running at IRQL <= DISPATCH_LEVEL. + +--*/ + +{ + SIZE_T SourceByteCount, TargetByteCount, BytesRemaining, CopySize; + PUCHAR SourceVa, TargetVa; + + // + // Skip any offsets specified by the caller. Note that this also serves + // to skip any zero-length MDLs at the front of the chains, + // simplifying the logic below. + // + + while (SourceMdl && SourceOffset >= MmGetMdlByteCount(SourceMdl)) + { + SourceOffset -= MmGetMdlByteCount(SourceMdl); + SourceMdl = SourceMdl->Next; + } + + while (TargetMdl && TargetOffset >= MmGetMdlByteCount(TargetMdl)) + { + TargetOffset -= MmGetMdlByteCount(TargetMdl); + TargetMdl = TargetMdl->Next; + } + + // + // Determine whether any data transfer will actually occur and, + // if so, enter the main transfer stage. + // + if (BytesToCopy && SourceMdl && TargetMdl) + { + BytesRemaining = BytesToCopy; + + // + // Compute the length for the first source MDL, + // and obtain a virtual address for it. + // + + SourceByteCount = MmGetMdlByteCount(SourceMdl) - SourceOffset; + if (SourceByteCount > BytesRemaining) + { + SourceByteCount = BytesRemaining; + } + + SourceVa = MmGetSystemAddressForMdlSafe(SourceMdl, LowPagePriority); + if (SourceVa == NULL) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + SourceVa += SourceOffset; + + // + // Compute the length for the first target MDL, + // and obtain a virtual address for it. + // + + TargetByteCount = MmGetMdlByteCount(TargetMdl) - TargetOffset; + + TargetVa = MmGetSystemAddressForMdlSafe(TargetMdl, LowPagePriority); + if (TargetVa == NULL) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + TargetVa += TargetOffset; + + // + // Transfer data between the MDL chains until the data are exhausted + // or the end of one of the MDL chains is encountered. + // + + for (;;) + { + + // + // Copy the current installment and return if done. + // Otherwise, update the count of bytes remaining. + // + CopySize = min(TargetByteCount, SourceByteCount); + RtlCopyMemory(TargetVa, SourceVa, CopySize); + + if (BytesRemaining == CopySize) + { + *BytesCopied = BytesToCopy; + return STATUS_SUCCESS; + } + + BytesRemaining -= CopySize; + + // + // Advance to the next MDL in the target chain if necessary, + // otherwise update our pointer into the current entry. + // + + if (TargetByteCount == CopySize) + { + + do + { + TargetMdl = TargetMdl->Next; + if (TargetMdl == NULL) + { + *BytesCopied = BytesToCopy - BytesRemaining; + return STATUS_SUCCESS; + } + TargetByteCount = MmGetMdlByteCount(TargetMdl); + } while(TargetByteCount == 0); + + TargetVa = MmGetSystemAddressForMdlSafe(TargetMdl, + LowPagePriority); + if (TargetVa == NULL) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + } + else + { + TargetVa += CopySize; + TargetByteCount -= CopySize; + } + + // + // Advance to the next MDL in the source chain if necessary, + // otherwise update our pointer into the current entry. + // + if (SourceByteCount == CopySize) + { + + do + { + SourceMdl = SourceMdl->Next; + if (SourceMdl == NULL) + { + *BytesCopied = BytesToCopy - BytesRemaining; + return STATUS_SUCCESS; + } + SourceByteCount = MmGetMdlByteCount(SourceMdl); + } while(SourceByteCount == 0); + + if (SourceByteCount > BytesRemaining) + { + SourceByteCount = BytesRemaining; + } + SourceVa = MmGetSystemAddressForMdlSafe(SourceMdl, + LowPagePriority); + if (SourceVa == NULL) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + } + else + { + SourceVa += CopySize; + SourceByteCount -= CopySize; + } + } + } + + *BytesCopied = 0; + return STATUS_SUCCESS; +} + + + diff --git a/network/ndis/ndisprot_kmdf/60/send.c b/network/ndis/ndisprot_kmdf/60/send.c new file mode 100644 index 000000000..561c49f06 --- /dev/null +++ b/network/ndis/ndisprot_kmdf/60/send.c @@ -0,0 +1,337 @@ +/*++ + +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. + +--*/ + +#include "precomp.h" + +#define __FILENUMBER 'DNES' + +VOID +NdisProtEvtIoWrite( + IN WDFQUEUE Queue, + IN WDFREQUEST Request, + IN size_t Length + ) +/*++ + +Routine Description: + + Dispatch routine to handle Request_MJ_WRITE. + +Arguments: + + Queue - Default queue handle + Request - Handle to the read/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: + + VOID + +--*/ +{ + ULONG DataLength; + NTSTATUS NtStatus; + PNDISPROT_OPEN_CONTEXT pOpenContext; + PNET_BUFFER_LIST pNetBufferList; + NDISPROT_ETH_HEADER UNALIGNED *pEthHeader; + PVOID CancelId; + ULONG SendFlags = 0; + PMDL pMdl = NULL; + WDFFILEOBJECT fileObject; + PREQUEST_CONTEXT reqContext; + WDF_OBJECT_ATTRIBUTES attributes; + + UNREFERENCED_PARAMETER(Queue); + + fileObject = WdfRequestGetFileObject(Request); + pOpenContext = GetFileObjectContext(fileObject)->OpenContext; + + do + { + // + // Create a context to track the length of transfer and NDIS packet + // associated with this request. + // + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, REQUEST_CONTEXT); + + NtStatus = WdfObjectAllocateContext(Request, &attributes, &reqContext); + if(!NT_SUCCESS(NtStatus)){ + DEBUGP(DL_WARN, ("Write: WdfObjectAllocateContext failed: %x\n", NtStatus)); + NtStatus = STATUS_INVALID_HANDLE; + break; + + } + + reqContext->Length = (ULONG) Length; + + if (pOpenContext == NULL) + { + DEBUGP(DL_WARN, ("Write: FileObject %p not yet associated with a device\n", + fileObject)); + NtStatus = STATUS_INVALID_HANDLE; + break; + } + + NPROT_STRUCT_ASSERT(pOpenContext, oc); + + NtStatus = WdfRequestRetrieveInputWdmMdl(Request, &pMdl); + if (!NT_SUCCESS(NtStatus)) + { + DEBUGP(DL_FATAL, ("Write: WdfRequestRetrieveInputWdmMdl failed %x\n", NtStatus)); + break; + } + + // + // Try to get a virtual address for the MDL. + // + pEthHeader = MmGetSystemAddressForMdlSafe(pMdl, NormalPagePriority); + + if (pEthHeader == NULL) + { + DEBUGP(DL_FATAL, ("Write: MmGetSystemAddr failed for" + " Request %p, MDL %p\n", + Request, pMdl)); + NtStatus = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + // + // Sanity-check the length. + // + DataLength = MmGetMdlByteCount(pMdl); + 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; + } + + // + // To prevent applications from sending packets with spoofed + // mac address, we will do the following check to make sure the source + // address in the packet is same as the current MAC address of the NIC. + // + if ((WdfRequestGetRequestorMode(Request) == UserMode) && + !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 == NdisprotPaused) + || (pOpenContext->State == NdisprotPausing)) + { + NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); + + DEBUGP(DL_INFO, ("Device is paused.\n")); + NtStatus = STATUS_UNSUCCESSFUL; + break; + } + + 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 + + // + // 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. + // + // Note that this sample code does not implement the cancellation logic. An actual + // driver may find value in implementing this. + // + + CancelId = NPROT_GET_NEXT_CANCEL_ID(); + NDIS_SET_NET_BUFFER_LIST_CANCEL_ID(pNetBufferList, CancelId); + reqContext->NetBufferList = (PVOID)pNetBufferList; + + NPROT_RELEASE_LOCK(&pOpenContext->Lock, FALSE); + + // + // Set a back pointer from the packet to the IRP. + // + NPROT_REQUEST_FROM_SEND_NBL(pNetBufferList) = Request; + + NtStatus = STATUS_PENDING; + + pNetBufferList->SourceHandle = pOpenContext->BindingHandle; + NPROT_ASSERT (pMdl->Next == NULL); + + SendFlags |= NDIS_SEND_FLAGS_CHECK_FOR_LOOPBACK; + + NdisSendNetBufferLists( + pOpenContext->BindingHandle, + pNetBufferList, + NDIS_DEFAULT_PORT_NUMBER, + SendFlags); + + } + while (FALSE); + + if (NtStatus != STATUS_PENDING) + { + WdfRequestComplete(Request, NtStatus); + } + + return; +} + + +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 + +--*/ +{ + PNDISPROT_OPEN_CONTEXT pOpenContext; + PNET_BUFFER_LIST CurrNetBufferList = NULL; + PNET_BUFFER_LIST NextNetBufferList; + NDIS_STATUS CompletionStatus; + BOOLEAN DispatchLevel; + WDFREQUEST request; + PREQUEST_CONTEXT reqContext; + + + 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); + + request = NPROT_REQUEST_FROM_SEND_NBL(CurrNetBufferList); + reqContext = GetRequestContext(request); + CompletionStatus = NET_BUFFER_LIST_STATUS(CurrNetBufferList); + + DEBUGP(DL_INFO, ("SendComplete: NetBufferList %p/IRP %p/Length %d " + "completed with status %x\n", + CurrNetBufferList, request, reqContext->Length, + CompletionStatus)); + + // + // We are done with the NDIS_PACKET: + // + NPROT_DEREF_SEND_NBL(CurrNetBufferList, DispatchLevel); + CurrNetBufferList = NULL; + + if (CompletionStatus == NDIS_STATUS_SUCCESS) + { + WdfRequestCompleteWithInformation(request, STATUS_SUCCESS, reqContext->Length); + } + else + { + WdfRequestCompleteWithInformation(request, STATUS_UNSUCCESSFUL, 0); + } + + NPROT_ACQUIRE_LOCK(&pOpenContext->Lock, DispatchLevel); + pOpenContext->PendedSendCount--; + + if ((NPROT_TEST_FLAGS(pOpenContext->Flags, NPROTO_BIND_FLAGS, NPROTO_BIND_CLOSING)) + && (pOpenContext->PendedSendCount == 0)) + { + NPROT_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_kmdf/Package/package.VcxProj b/network/ndis/ndisprot_kmdf/Package/package.VcxProj new file mode 100644 index 000000000..4f36efdbb --- /dev/null +++ b/network/ndis/ndisprot_kmdf/Package/package.VcxProj @@ -0,0 +1,87 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + + {85BE7B07-69F5-4BC1-A755-483A9051AAC4} + + + {9B2221A3-32D0-4769-8092-4F9F98043A63} + + + + WindowsKernelModeDriver10.0 + Utility + Package + true + Debug + + + + {613FEF0A-2AE9-46B7-994C-69D95973B366} + {86285A85-2FEA-4AEF-8233-356041B6282B} + $(MSBuildProjectName) + + + Windows10 + true + + + Windows10 + false + + + Windows10 + true + + + Windows10 + false + + + + + + + + + + + DbgengKernelDebugger + False + None + + + + + + %PathToInf% + False + False + True + + 133563 + + + + + + + \ No newline at end of file diff --git a/network/ndis/ndisprot_kmdf/Package/package.VcxProj.Filters b/network/ndis/ndisprot_kmdf/Package/package.VcxProj.Filters new file mode 100644 index 000000000..237950072 --- /dev/null +++ b/network/ndis/ndisprot_kmdf/Package/package.VcxProj.Filters @@ -0,0 +1,21 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {89CB9D79-7DA6-49D6-806B-B901625DE978} + + + h;hpp;hxx;hm;inl;inc;xsd + {55E7969D-0357-4614-BFC9-8A045E04B4E1} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {A4F0352B-5991-430C-815D-30F30B63E33B} + + + inf;inv;inx;mof;mc; + {593070BD-C864-4041-9213-2688C7CC3A7D} + + + \ No newline at end of file diff --git a/network/ndis/ndisprot_kmdf/ReadMe.md b/network/ndis/ndisprot_kmdf/ReadMe.md new file mode 100644 index 000000000..422a7acee --- /dev/null +++ b/network/ndis/ndisprot_kmdf/ReadMe.md @@ -0,0 +1,158 @@ +Connection-less NDIS 6.0 Sample Protocol Driver +=============================================== +This sample demonstrates a connection-less NDIS 6.0 protocol driver. + +The driver supports sending and receiving raw Ethernet frames using ReadFile/WriteFile calls from user-mode. 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. + +The sample also demonstrates how to write a Notify Object dll. The Notify Object is used for calling into the Wdf Coinstaller to install and load the framework library. + +Related technologies +-------------------- + +[Creating Framework-based Miniport Drivers](http://msdn.microsoft.com/en-us/library/windows/hardware/ff540778) + +Build the sample +---------------- + +For information on how to build a driver solution using Microsoft Visual Studio, see [Building a Driver](http://msdn.microsoft.com/en-us/library/windows/hardware/ff554644). + +The 60 subdirectory (src\\network\\ndis\\ndisprot\_kmdf\\60) indicates that the built sample will be NDIS 6.0 compatible and will work on Windows Vista and later operating systems. + +Installation +------------ + +Use the following steps to install the sample. + +1. When you build the sample, the build engine produces ndisprot.inf in the build target directory. Copy nprt6wdf.sys, protnotify.dll, and ndisprot.inf to a directory. +2. Copy the KMDF coinstaller (wdfcoinstaller*MMmmm*.dll) to the same directory. + + **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. In Control Panel, in the **Network and Internet** group, open **Network Connections**, select an adapter, and then open **Properties**. + +4. Click **Install**, and then click **Protocol**. + +5. Click **Add**, and then click **Have disk**. + +6. Point to the location of the INF file and driver, click **Sample NDIS Protocol Driver**, and then click **OK**. + +7. After installing the protocol, copy the test application Uiotest.exe to a convenient location. 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 the driver. + +Usage +----- + +From an administrator command prompt, to start the driver, type **Net start ndisprot**. + +To stop the driver, type **Net stop ndisprot**. + +You can build Prottest.exe from source code located in the src\\network\\ndis\\ndisprot\\6x\\test directory. + +To test the NDIS 6.0 driver, run prottest. For help on usage, run **prottest -?**. + + +++ + + + + + +
usage: PROTTEST [options] <devicename>
+options:
+ -e: Enumerate devices
+ -r: Read
+ -w: Write (default)
+ -l <length>: length of each packet (default: 100)
+ -n <count>: number of packets (defaults to infinity)
+ -m <MAC address> (defaults to local MAC)
+ -f Use a fake address to send out the packets.
+ +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 buffer that is large enough to contain the data returned. 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. + +For an NDIS 6.0 driver, use the -e option on prottest to enumerate all devices to which NDISPROT is bound: + + +++ + + + + + +
C:\prot>prottest -e
+ 0. \DEVICE\{9273DA7D-5275-4B9A-AC56-68A49D121F1F}
+ - Intel-Based 10/100 Ethernet Card
+ +The following command sends and receives 2 packets on a device. Since these packets are sent to the local MAC address (default), both packets are received. The device name parameter to prottest is picked up from the output of **prottest -e** (see above). + + +++ + + + + + +
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
+ +For security reasons, this driver does not allow packets with fake MAC addresses to be sent from user-mode applications. + +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. + +File Manifest +------------- + +**Directory: 60** + + ++++ + + + + + + + + + + + +
File +Description

debug.c

+

Routines to aid debugging

debug.h

+

Debug macro definitions

+ +**Directory: NotifyOb** + + ++++ + + + + + + + + + + + +
File +Description

Common.hpp

+

Header file containing the common include files for the project

dllmain.cpp

+

Handles loading/unloading of Wdf Coinstaller and the notify object dll

+ + diff --git a/network/ndis/ndisprot_kmdf/ndisprot.htm b/network/ndis/ndisprot_kmdf/ndisprot.htm new file mode 100644 index 000000000..88dc4d38f --- /dev/null +++ b/network/ndis/ndisprot_kmdf/ndisprot.htm @@ -0,0 +1,197 @@ + + + +Sample NDIS connection-less protocol driver sample + + + + +
+ +

NDIS connection-less protocol driver sample

+ +

SUMMARY

+ +

This sample demonstrates a connection-less +NDIS 5.0, 5.1 or 6.0 protocol driver. The driver supports sending and receiving raw Ethernet +frames using ReadFile/WriteFile calls from user-mode. 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.

+ +

The sample works on Windows 2000 and later +platforms.

+ +

The sample also demonstrates how to write +a Notify Object dll. The Notify Object is used for calling into the Wdf Coinstaller to install +and load the framework library.

+ +

BUILDING THE SAMPLE

+ +

From the Free or Checked Build environment, +execute build -ceZ in the ndisprot directory.

+

+

Depending on the build environment, the 50 +subdirectory produces either NDIS5.0 or NDIS 5.1 compatible driver. If built in the Windows 2000 +build environment, it will produce NDIS 5.0 compatible driver. If it's built in the Windows XP or +Window Server 2003 build environment, it will produce NDIS 5.1 compatible driver.

+

+

The 60 subdirectory can be built only in the +Windows Vista build environment. The sample built in this sub directory will be NDIS 6.0 compatible +and will work on Windows Vista and later operating systems.

+ + +

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 files uiotest.exe and +protest.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 NDIS 5.x driver, run uiotest. +For help on usage, run uiotest -?

+ +
+usage: UIOTEST [options] <devicename>
+options:
+       -e: Enumerate devices
+       -r: Read
+       -w: Write (default)
+       -l <length>: length of each packet (default: 100)
+       -n <count>: number of packets (defaults to infinity)
+       -m <MAC address> (defaults to local MAC)
+ +

To test the NDIS 6.0 driver, run prottest. +For help on usage, run prottest -?

+ +
+usage: PROTTEST [options] <devicename>
+options:
+       -e: Enumerate devices
+       -r: Read
+       -w: Write (default)
+       -l <length>: length of each packet (default: 100)
+       -n <count>: number of packets (defaults to infinity)
+       -m <MAC address> (defaults to local MAC)
+ +

Uiotest/Prottest exercises the IOCTLs supported by +NDISPROT, and sends and/or receives data on the selected device. In order to use uiotest/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.

+ +

For NDIS 5.x driver, use the –e option on uiotest +to enumerate all devices to which NDISPROT is bound:

+ +
+C:\uio>uiotest -e
+ 0. \DEVICE\{9273DA7D-5275-4B9A-AC56-68A49D121F1F}
+     - Intel-Based 10/100 Ethernet Card
+ +

The following command sends and receives 2 packets +on a device. Since these packets are sent to the local MAC address (default), both packets are received. +The device name parameter to uiotest is picked up from the output of uiotest –e (see above).

+ +
+C:\uio>uiotest -n 2 \DEVICE\{9273DA7D-5275-4B9A-AC56-68A49D121F1F}
+DoWriteProc: finished sending 2 packets of 100 bytes each
+DoReadProc finished: read 2 packets
+ +

For NDIS 6.0 driver, use the –e option on prottest +to enumerate all devices to which NDISPROT is bound:

+ +
+C:\prot>prottest -e
+ 0. \DEVICE\{9273DA7D-5275-4B9A-AC56-68A49D121F1F}
+     - Intel-Based 10/100 Ethernet Card
+ +

The following command sends and receives 2 packets +on a device. Since these packets are sent to the local MAC address (default), both packets are received. +The device name parameter to prottest is picked up from the output of protest –e (see above).

+ +
+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
+ +

For security reasons, this driver does not allow +packets with fake MAC addresses to be sent from usermode applications.

+ + +

TIPS

+ +

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.

+ +

CODE TOUR

+ +

File Manifest

+ +
Directory: 50, 60
+File             Description
+debug.c          Routines to aid debugging
+debug.h          Debug macro definitions
+excallbk.c       Handles load order dependency between this sample and NDISWDM sample
+macros.h         Spinlock, event, referencing macros
+ndisbind.c       NDIS protocol entry points to handle binding/unbinding from adapters
+ndisprot.h       Data structure definitions
+precomp.h        Contains the  precompiled headers
+protuser.h       Has the definitions of ioctls issued by protuser.exe application used on NDIS 6.0  
+nuiouser.h       Has the definitions of ioctls issued by nuiouser.exe application used on NDIS 5.0  
+ndisprot.inf     INF file for installing NDISPROT
+ntdisp.c         NT Entry points and dispatch routines for NDISPROT
+recv.c           NDIS protocol entry points for receiving data, and IRP_MJ_READ processing
+send.c           NDIS protocol routines for sending data, and IRP_MJ_WRITE processing
+ +
Directory: NotifyOb
+File             Description
+Common.hpp       Header file containing the common include files for the project
+dllmain.cpp      Handles loading/unloading of Wdf Coinstaller and the notify object dll
+ProtNotify.cpp   Handles loading/unloading the WDF loader during the device installation/removal
+ProtNotify.idl   Defines the interfaces for the notify object dll
+ProtNotify.def   Defines the exports of notify object dll
+
+ProtNotify.rc    Resource file for the notify object dll
+resource.h       Defines the resource ids used by the notify object
+ +

Top of page

+ + + + + +
+

 

+
+ +

© Microsoft Corporation +2000

+ +
+ + + + + + diff --git a/network/ndis/ndisprot_kmdf/ndisprot_kmdf.sln b/network/ndis/ndisprot_kmdf/ndisprot_kmdf.sln new file mode 100644 index 000000000..15a50f20b --- /dev/null +++ b/network/ndis/ndisprot_kmdf/ndisprot_kmdf.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}") = "Package", "Package", "{FE73654B-E5D8-428F-967F-B9396A8B0DA3}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Notifyob", "Notifyob", "{DB46D8C9-4FFA-41D6-93A9-7315ADD0879A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "60", "60", "{AA65D22A-3101-4BE1-9F7B-1835DEA3D22A}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "package", "Package\package.VcxProj", "{613FEF0A-2AE9-46B7-994C-69D95973B366}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ProtNotify", "notifyob\ProtNotify.vcxproj", "{9B2221A3-32D0-4769-8092-4F9F98043A63}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nprt6wdf", "60\nprt6wdf.vcxproj", "{85BE7B07-69F5-4BC1-A755-483A9051AAC4}" +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 + {613FEF0A-2AE9-46B7-994C-69D95973B366}.Debug|Win32.ActiveCfg = Debug|Win32 + {613FEF0A-2AE9-46B7-994C-69D95973B366}.Debug|Win32.Build.0 = Debug|Win32 + {613FEF0A-2AE9-46B7-994C-69D95973B366}.Release|Win32.ActiveCfg = Release|Win32 + {613FEF0A-2AE9-46B7-994C-69D95973B366}.Release|Win32.Build.0 = Release|Win32 + {613FEF0A-2AE9-46B7-994C-69D95973B366}.Debug|x64.ActiveCfg = Debug|x64 + {613FEF0A-2AE9-46B7-994C-69D95973B366}.Debug|x64.Build.0 = Debug|x64 + {613FEF0A-2AE9-46B7-994C-69D95973B366}.Release|x64.ActiveCfg = Release|x64 + {613FEF0A-2AE9-46B7-994C-69D95973B366}.Release|x64.Build.0 = Release|x64 + {9B2221A3-32D0-4769-8092-4F9F98043A63}.Debug|Win32.ActiveCfg = Debug|Win32 + {9B2221A3-32D0-4769-8092-4F9F98043A63}.Debug|Win32.Build.0 = Debug|Win32 + {9B2221A3-32D0-4769-8092-4F9F98043A63}.Release|Win32.ActiveCfg = Release|Win32 + {9B2221A3-32D0-4769-8092-4F9F98043A63}.Release|Win32.Build.0 = Release|Win32 + {9B2221A3-32D0-4769-8092-4F9F98043A63}.Debug|x64.ActiveCfg = Debug|x64 + {9B2221A3-32D0-4769-8092-4F9F98043A63}.Debug|x64.Build.0 = Debug|x64 + {9B2221A3-32D0-4769-8092-4F9F98043A63}.Release|x64.ActiveCfg = Release|x64 + {9B2221A3-32D0-4769-8092-4F9F98043A63}.Release|x64.Build.0 = Release|x64 + {85BE7B07-69F5-4BC1-A755-483A9051AAC4}.Debug|Win32.ActiveCfg = Debug|Win32 + {85BE7B07-69F5-4BC1-A755-483A9051AAC4}.Debug|Win32.Build.0 = Debug|Win32 + {85BE7B07-69F5-4BC1-A755-483A9051AAC4}.Release|Win32.ActiveCfg = Release|Win32 + {85BE7B07-69F5-4BC1-A755-483A9051AAC4}.Release|Win32.Build.0 = Release|Win32 + {85BE7B07-69F5-4BC1-A755-483A9051AAC4}.Debug|x64.ActiveCfg = Debug|x64 + {85BE7B07-69F5-4BC1-A755-483A9051AAC4}.Debug|x64.Build.0 = Debug|x64 + {85BE7B07-69F5-4BC1-A755-483A9051AAC4}.Release|x64.ActiveCfg = Release|x64 + {85BE7B07-69F5-4BC1-A755-483A9051AAC4}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {613FEF0A-2AE9-46B7-994C-69D95973B366} = {FE73654B-E5D8-428F-967F-B9396A8B0DA3} + {9B2221A3-32D0-4769-8092-4F9F98043A63} = {DB46D8C9-4FFA-41D6-93A9-7315ADD0879A} + {85BE7B07-69F5-4BC1-A755-483A9051AAC4} = {AA65D22A-3101-4BE1-9F7B-1835DEA3D22A} + EndGlobalSection +EndGlobal diff --git a/network/ndis/ndisprot_kmdf/notifyob/Common.hpp b/network/ndis/ndisprot_kmdf/notifyob/Common.hpp new file mode 100644 index 000000000..e1bdc85cb --- /dev/null +++ b/network/ndis/ndisprot_kmdf/notifyob/Common.hpp @@ -0,0 +1,36 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + Common.hpp + +Abstract: + + This is the header file containing the common include files for the project. + +--*/ + +#ifndef _COMMON_HPP_ +#define _COMMON_HPP + +#include +#include + +extern CComModule _Module; // required by atlcom.h +#include + +#include + +#include "Resource.h" +#include "ProtNotify.h" + +extern PFN_WDFPREDEVICEINSTALL pfnWdfPreDeviceInstall; +extern PFN_WDFPOSTDEVICEINSTALL pfnWdfPostDeviceInstall; +extern PFN_WDFPREDEVICEREMOVE pfnWdfPreDeviceRemove; +extern PFN_WDFPOSTDEVICEREMOVE pfnWdfPostDeviceRemove; +extern ATL::_ATL_OBJMAP_ENTRY* ObjectMap; + +#endif // _COMMON_HPP_ + diff --git a/network/ndis/ndisprot_kmdf/notifyob/ProtNotify.cpp b/network/ndis/ndisprot_kmdf/notifyob/ProtNotify.cpp new file mode 100644 index 000000000..0c120ba1c --- /dev/null +++ b/network/ndis/ndisprot_kmdf/notifyob/ProtNotify.cpp @@ -0,0 +1,422 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + ProtNotify.cpp + +Abstract: + + This module contains the component that calls into the Wdf Installer to + to load/unload the WDF loader during the device installation/removal. + +--*/ + +#include "Common.hpp" + +#ifdef _ATL_STATIC_REGISTRY +// +// Disable warnings generated in the standard headers +// +// Disable warning C4100: unreferenced formal parameter +// Disable warning C4189: local variable is initialized but not referenced +// +#pragma warning (disable:4100 4189) + +#include + +#pragma warning (default:4100 4189) +#endif + + +// +// The CProtNotify class implements the interfaces necessary to monitor when +// the NdisProt driver is being installed or uninstalled so that the WDF library +// can be loaded or unloaded, respectively. +// +class CProtNotify : public CComObjectRoot, + public CComCoClass, + public INetCfgComponentControl, + public INetCfgComponentSetup +{ +private: + + INetCfg* _NetCfgObj; + INetCfgComponent* _NetCfgComponentObj; + + // + // The name of the Wdf Section in the inf file. + // + LPWSTR _WdfSectionName; + + // + // The name of the original inf file. + // + LPWSTR _SourceInfFileName; + +public: + + CProtNotify(); + ~CProtNotify(); + + BEGIN_COM_MAP(CProtNotify) + COM_INTERFACE_ENTRY(INetCfgComponentControl) + COM_INTERFACE_ENTRY(INetCfgComponentSetup) + END_COM_MAP() + + DECLARE_REGISTRY_RESOURCEID(NDIS_NOTIFY_RESOURCE_ID2) + + // + // INetCfgComponentControl methods. + // + + STDMETHOD (Initialize)( + IN INetCfgComponent* pIComp, + IN INetCfg* pINetCfg, + IN BOOL fInstalling + ); + + STDMETHOD (CancelChanges)( + ); + + STDMETHOD (ApplyRegistryChanges)( + ); + + STDMETHOD (ApplyPnpChanges) ( + IN INetCfgPnpReconfigCallback* pICallback + ); + + // + // INetCfgComponentSetup methods. + // + + STDMETHOD (Install)( + IN DWORD dwSetupFlags + ); + + STDMETHOD (Upgrade)( + IN DWORD dwSetupFlags, + IN DWORD dwUpgradeFromBuildNo + ); + + STDMETHOD (ReadAnswerFile)( + IN PCWSTR szAnswerFile, + IN PCWSTR szAnswerSections + ); + + STDMETHOD (Removing)( + ); + +private: + + // + // A routine to query the registry for the Inf file name and the Wdf Section + // name. + // + STDMETHOD (QueryInfNameAndWdfSection)( + ); + +}; + + +// +// Define the Object Map array using the CProtNotify class. +// +BEGIN_OBJECT_MAP(ObjectMapArray) + OBJECT_ENTRY(CLSID_CProtNotify, CProtNotify) +END_OBJECT_MAP() + +ATL::_ATL_OBJMAP_ENTRY* ObjectMap = ObjectMapArray; + + +CProtNotify::CProtNotify() +{ + _NetCfgObj = NULL; + _NetCfgComponentObj = NULL; + _SourceInfFileName = NULL; + _WdfSectionName = NULL; +} + + +CProtNotify::~CProtNotify() +{ + if (_NetCfgObj != NULL) { + _NetCfgObj->Release(); + } + if (_NetCfgComponentObj != NULL) { + _NetCfgComponentObj->Release(); + } + if (_SourceInfFileName != NULL) { + CoTaskMemFree(_SourceInfFileName); + } + if (_WdfSectionName != NULL) { + CoTaskMemFree(_WdfSectionName); + } +} + + +STDMETHODIMP CProtNotify::Initialize( + IN INetCfgComponent* pIComp, + IN INetCfg* pINetCfg, + IN BOOL fInstalling + ) +{ + if (fInstalling) { + + _NetCfgObj = pINetCfg; + _NetCfgComponentObj = pIComp; + + if (_NetCfgObj != NULL) { + _NetCfgObj->AddRef(); + } + if (_NetCfgComponentObj != NULL) { + _NetCfgComponentObj->AddRef(); + } + } + + return S_OK; +} + + +STDMETHODIMP CProtNotify::CancelChanges( + ) +{ + return S_OK; +} + + +STDMETHODIMP CProtNotify::ApplyRegistryChanges( + ) +{ + return S_OK; +} + + +STDMETHODIMP CProtNotify::ApplyPnpChanges( + IN INetCfgPnpReconfigCallback* pICallback + ) +{ + UNREFERENCED_PARAMETER(pICallback); + + return S_OK; +} + + +STDMETHODIMP CProtNotify::Install( + IN DWORD dwSetupFlags + ) +{ + HRESULT hr = S_OK; + ULONG winError; + + UNREFERENCED_PARAMETER(dwSetupFlags); + + // + // Query the Source Inf File name and the Wdf Section name if it hasnt + // been already queried. + // + hr = QueryInfNameAndWdfSection(); + if (SUCCEEDED(hr)) { + + winError = (pfnWdfPreDeviceInstall)(_SourceInfFileName, _WdfSectionName); + if (winError == ERROR_SUCCESS) { + winError = (pfnWdfPostDeviceInstall)(_SourceInfFileName, _WdfSectionName); + } + + hr = HRESULT_FROM_WIN32(winError); + } + + return hr; +} + + +STDMETHODIMP CProtNotify::Upgrade( + IN DWORD dwSetupFlags, + IN DWORD dwUpgradeFromBuildNo + ) +{ + UNREFERENCED_PARAMETER(dwSetupFlags); + UNREFERENCED_PARAMETER(dwUpgradeFromBuildNo); + + return S_OK; +} + + +STDMETHODIMP CProtNotify::ReadAnswerFile( + IN PCWSTR szAnswerFile, + IN PCWSTR szAnswerSections + ) +{ + UNREFERENCED_PARAMETER(szAnswerFile); + UNREFERENCED_PARAMETER(szAnswerSections); + + return S_OK; +} + + +STDMETHODIMP CProtNotify::Removing( + ) +{ + HRESULT hr; + ULONG winError; + + // + // Call the Wdf Pre device removal routine. + // + winError = (pfnWdfPreDeviceRemove)(_SourceInfFileName, _WdfSectionName); + + hr = HRESULT_FROM_WIN32(winError); + if (FAILED(hr)) { + return hr; + } + + // + // Call the Wdf Post device removal routine. + // + winError = (pfnWdfPostDeviceRemove)(_SourceInfFileName, _WdfSectionName); + + hr = HRESULT_FROM_WIN32(winError); + if (FAILED(hr)) { + return hr; + } + + return hr; +} + + +STDMETHODIMP CProtNotify::QueryInfNameAndWdfSection( + ) +{ + HRESULT hr = S_OK; + ULONG winError = ERROR_SUCCESS; + HKEY paramKey = NULL; + DWORD length = 0; + + // + // Query the source inf file name and wdf section name from the registry + // only if it has not been successfully queried before. + // + if ((_SourceInfFileName != NULL) || (_WdfSectionName != NULL)) { + goto exit; + } + + // + // Get the handle to the parameters key for the component. + // + hr = _NetCfgComponentObj->OpenParamKey(¶mKey); + if (FAILED(hr)) { + goto exit; + } + + // + // Flush the key before querying values off of it. + // + hr = RegFlushKey(paramKey); + if (FAILED(hr)) { + goto exit; + } + + // + // Query the number of bytes required to copy the SourceInfFile data. + // + winError = RegQueryValueEx(paramKey, + L"SourceInfFile", + NULL, + NULL, + NULL, + &length); + hr = HRESULT_FROM_WIN32(winError); + if (FAILED(hr)) { + goto exit; + } + + // + // Allocate memory to hold the SourceInfFile data. + // + _SourceInfFileName = (LPWSTR)CoTaskMemAlloc(length); + if (_SourceInfFileName == NULL) { + hr = E_OUTOFMEMORY; + goto exit; + } + + // + // Query the SourceInfFile data. + // + winError = RegQueryValueEx(paramKey, + L"SourceInfFile", + NULL, + NULL, + (LPBYTE)_SourceInfFileName, + &length); + hr = HRESULT_FROM_WIN32(winError); + if (FAILED(hr)) { + goto exit; + } + + // + // Query the number of bytes required to copy the WdfSection data. + // + length = 0; + winError = RegQueryValueEx(paramKey, + L"WdfSection", + NULL, + NULL, + NULL, + &length); + hr = HRESULT_FROM_WIN32(winError); + if (FAILED(hr)) { + + // + // Its ok if the WdfSection value is not defined. This means that the + // WDF Coinstaller will by default look for "WdfSection" in the inf + // file. + // + hr = S_OK; + goto exit; + } + + // + // Allocate memory to hold the WdfSection data. + // + _WdfSectionName = (LPWSTR)CoTaskMemAlloc(length); + if (_WdfSectionName == NULL) { + hr = E_OUTOFMEMORY; + goto exit; + } + + // + // Query the SourceInfFile data. + // + winError = RegQueryValueEx(paramKey, + L"WdfSection", + NULL, + NULL, + (LPBYTE)_WdfSectionName, + &length); + hr = HRESULT_FROM_WIN32(winError); + if (FAILED(hr)) { + goto exit; + } + +exit: + + if (paramKey != NULL) { + RegCloseKey(paramKey); + } + + if (FAILED(hr)) { + if (_SourceInfFileName != NULL) { + CoTaskMemFree(_SourceInfFileName); + _SourceInfFileName = NULL; + } + if (_WdfSectionName != NULL) { + CoTaskMemFree(_WdfSectionName); + _WdfSectionName = NULL; + } + } + + return hr; +} + + diff --git a/network/ndis/ndisprot_kmdf/notifyob/ProtNotify.def b/network/ndis/ndisprot_kmdf/notifyob/ProtNotify.def new file mode 100644 index 000000000..5a4bf66a9 --- /dev/null +++ b/network/ndis/ndisprot_kmdf/notifyob/ProtNotify.def @@ -0,0 +1,6 @@ +LIBRARY ProtNotify +EXPORTS + DllCanUnloadNow PRIVATE + DllGetClassObject PRIVATE + DllRegisterServer PRIVATE + DllUnregisterServer PRIVATE \ No newline at end of file diff --git a/network/ndis/ndisprot_kmdf/notifyob/ProtNotify.idl b/network/ndis/ndisprot_kmdf/notifyob/ProtNotify.idl new file mode 100644 index 000000000..87a4f7ecc --- /dev/null +++ b/network/ndis/ndisprot_kmdf/notifyob/ProtNotify.idl @@ -0,0 +1,19 @@ +#include + +[ + uuid(d5a293af-371a-4694-a9b7-fc9d409def3b), + version(1.0), + helpstring("WDF NDISPROT driver install Notify Object 1.0 Type Library") +] +library NotifyLib +{ + [ + uuid(21e7e731-f286-4116-b3c2-d43ccba29f07), + helpstring("WDF NDISPROT driver install Notify Class") + ] + coclass CProtNotify + { + [restricted] interface INetCfgComponentControl; + [restricted] interface INetCfgComponentSetup; + }; +}; \ No newline at end of file diff --git a/network/ndis/ndisprot_kmdf/notifyob/ProtNotify.rc b/network/ndis/ndisprot_kmdf/notifyob/ProtNotify.rc new file mode 100644 index 000000000..6ff3050aa --- /dev/null +++ b/network/ndis/ndisprot_kmdf/notifyob/ProtNotify.rc @@ -0,0 +1,14 @@ +#include +#include + +#define VER_FILETYPE VFT_DLL +#define VER_FILESUBTYPE VFT2_UNKNOWN +#define VER_FILEDESCRIPTION_STR "WDF NDISPROT driver install Notify Object" +#define VER_INTERNALNAME_STR "ProtNotify.dll" + +#include + +#include "Resource.h" + +NDIS_NOTIFY_RESOURCE_ID1 TYPELIB "ProtNotify.tlb" +NDIS_NOTIFY_RESOURCE_ID2 REGISTRY "ProtNotify.rgs" diff --git a/network/ndis/ndisprot_kmdf/notifyob/ProtNotify.rgs b/network/ndis/ndisprot_kmdf/notifyob/ProtNotify.rgs new file mode 100644 index 000000000..feb68d7d8 --- /dev/null +++ b/network/ndis/ndisprot_kmdf/notifyob/ProtNotify.rgs @@ -0,0 +1,15 @@ +HKCR +{ + NoRemove CLSID + { + ForceRemove {21e7e731-f286-4116-b3c2-d43ccba29f07} = s 'WDF NDISPROT driver install Notify Object' + { + InProcServer32 = s '%MODULE%' + { + val ThreadingModel = s 'Both' + } + } + } +} + + diff --git a/network/ndis/ndisprot_kmdf/notifyob/ProtNotify.vcxproj b/network/ndis/ndisprot_kmdf/notifyob/ProtNotify.vcxproj new file mode 100644 index 000000000..eede7f0c3 --- /dev/null +++ b/network/ndis/ndisprot_kmdf/notifyob/ProtNotify.vcxproj @@ -0,0 +1,321 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {9B2221A3-32D0-4769-8092-4F9F98043A63} + $(MSBuildProjectName) + 1 + 11 + false + true + Debug + Win32 + {3264ACE7-D19C-4948-B829-8E1BE32CC07D} + + + + 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) + + + + + + + + + + + + + + + + ProtNotify + + + ProtNotify + + + ProtNotify + + + ProtNotify + + + + %(PreprocessorDefinitions);WIN32;_WINDOWS;_USRDLL;USE_STDAFX;UNICODE;_UNICODE + + + %(PreprocessorDefinitions);WIN32;_WINDOWS;_USRDLL;USE_STDAFX;UNICODE;_UNICODE + + + %(PreprocessorDefinitions);WIN32;_WINDOWS;_USRDLL;USE_STDAFX;UNICODE;_UNICODE + + + + + %(PreprocessorDefinitions);WIN32;_WINDOWS;_USRDLL;USE_STDAFX;UNICODE;_UNICODE + + + %(PreprocessorDefinitions);WIN32;_WINDOWS;_USRDLL;USE_STDAFX;UNICODE;_UNICODE + + + %(PreprocessorDefinitions);WIN32;_WINDOWS;_USRDLL;USE_STDAFX;UNICODE;_UNICODE + + + + + %(PreprocessorDefinitions);WIN32;_WINDOWS;_USRDLL;USE_STDAFX;UNICODE;_UNICODE + + + %(PreprocessorDefinitions);WIN32;_WINDOWS;_USRDLL;USE_STDAFX;UNICODE;_UNICODE + + + %(PreprocessorDefinitions);WIN32;_WINDOWS;_USRDLL;USE_STDAFX;UNICODE;_UNICODE + + + + + %(PreprocessorDefinitions);WIN32;_WINDOWS;_USRDLL;USE_STDAFX;UNICODE;_UNICODE + + + %(PreprocessorDefinitions);WIN32;_WINDOWS;_USRDLL;USE_STDAFX;UNICODE;_UNICODE + + + %(PreprocessorDefinitions);WIN32;_WINDOWS;_USRDLL;USE_STDAFX;UNICODE;_UNICODE + + + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + true + Sync + + + + + true + Sync + + + + + true + Sync + + + + + true + Sync + + + + Static + + + Static + + + Static + + + Static + + + + %(AdditionalDependencies);advapi32.lib;comctl32.lib;kernel32.lib;setupapi.lib;ole32.lib;oleaut32.lib;user32.lib;uuid.lib + + + + + %(AdditionalDependencies);advapi32.lib;comctl32.lib;kernel32.lib;setupapi.lib;ole32.lib;oleaut32.lib;user32.lib;uuid.lib + + + + + %(AdditionalDependencies);advapi32.lib;comctl32.lib;kernel32.lib;setupapi.lib;ole32.lib;oleaut32.lib;user32.lib;uuid.lib + + + + + %(AdditionalDependencies);advapi32.lib;comctl32.lib;kernel32.lib;setupapi.lib;ole32.lib;oleaut32.lib;user32.lib;uuid.lib + + + + + %(PreprocessorDefinitions);KMDF_VERSION_MAJOR=1;KMDF_VERSION_MINOR=11 + %(AdditionalIncludeDirectories);$(WDKContentRoot)\Include\wdf\kmdf\1.11 + + + %(PreprocessorDefinitions);KMDF_VERSION_MAJOR=1;KMDF_VERSION_MINOR=11 + %(AdditionalIncludeDirectories);$(WDKContentRoot)\Include\wdf\kmdf\1.11 + + + %(PreprocessorDefinitions);KMDF_VERSION_MAJOR=1;KMDF_VERSION_MINOR=11 + %(AdditionalIncludeDirectories);$(WDKContentRoot)\Include\wdf\kmdf\1.11 + + + ProtNotify.def + + + + + %(PreprocessorDefinitions);KMDF_VERSION_MAJOR=1;KMDF_VERSION_MINOR=11 + %(AdditionalIncludeDirectories);$(WDKContentRoot)\Include\wdf\kmdf\1.11 + + + %(PreprocessorDefinitions);KMDF_VERSION_MAJOR=1;KMDF_VERSION_MINOR=11 + %(AdditionalIncludeDirectories);$(WDKContentRoot)\Include\wdf\kmdf\1.11 + + + %(PreprocessorDefinitions);KMDF_VERSION_MAJOR=1;KMDF_VERSION_MINOR=11 + %(AdditionalIncludeDirectories);$(WDKContentRoot)\Include\wdf\kmdf\1.11 + + + ProtNotify.def + + + + + %(PreprocessorDefinitions);KMDF_VERSION_MAJOR=1;KMDF_VERSION_MINOR=11 + %(AdditionalIncludeDirectories);$(WDKContentRoot)\Include\wdf\kmdf\1.11 + + + %(PreprocessorDefinitions);KMDF_VERSION_MAJOR=1;KMDF_VERSION_MINOR=11 + %(AdditionalIncludeDirectories);$(WDKContentRoot)\Include\wdf\kmdf\1.11 + + + %(PreprocessorDefinitions);KMDF_VERSION_MAJOR=1;KMDF_VERSION_MINOR=11 + %(AdditionalIncludeDirectories);$(WDKContentRoot)\Include\wdf\kmdf\1.11 + + + ProtNotify.def + + + + + %(PreprocessorDefinitions);KMDF_VERSION_MAJOR=1;KMDF_VERSION_MINOR=11 + %(AdditionalIncludeDirectories);$(WDKContentRoot)\Include\wdf\kmdf\1.11 + + + %(PreprocessorDefinitions);KMDF_VERSION_MAJOR=1;KMDF_VERSION_MINOR=11 + %(AdditionalIncludeDirectories);$(WDKContentRoot)\Include\wdf\kmdf\1.11 + + + %(PreprocessorDefinitions);KMDF_VERSION_MAJOR=1;KMDF_VERSION_MINOR=11 + %(AdditionalIncludeDirectories);$(WDKContentRoot)\Include\wdf\kmdf\1.11 + + + ProtNotify.def + + + + + ;%(AdditionalIncludeDirectories) + common.hpp + Create + $(IntDir)\common.pch + + + ;%(AdditionalIncludeDirectories) + common.hpp + Use + $(IntDir)\common.pch + + + ;%(AdditionalIncludeDirectories) + common.hpp + Use + $(IntDir)\common.pch + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/network/ndis/ndisprot_kmdf/notifyob/ProtNotify.vcxproj.Filters b/network/ndis/ndisprot_kmdf/notifyob/ProtNotify.vcxproj.Filters new file mode 100644 index 000000000..66da43998 --- /dev/null +++ b/network/ndis/ndisprot_kmdf/notifyob/ProtNotify.vcxproj.Filters @@ -0,0 +1,39 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {46131C3C-10A8-41E3-9DA7-8E3B89E16D30} + + + h;hpp;hxx;hm;inl;inc;xsd + {00C80488-71A9-4598-BCF9-717F096DF9B1} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {2DF3CEF8-49DE-4280-A200-9E821B9D3C04} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/network/ndis/ndisprot_kmdf/notifyob/commonsrc.cpp b/network/ndis/ndisprot_kmdf/notifyob/commonsrc.cpp new file mode 100644 index 000000000..7691e9d7e --- /dev/null +++ b/network/ndis/ndisprot_kmdf/notifyob/commonsrc.cpp @@ -0,0 +1 @@ +#include "common.hpp" \ No newline at end of file diff --git a/network/ndis/ndisprot_kmdf/notifyob/dllmain.cpp b/network/ndis/ndisprot_kmdf/notifyob/dllmain.cpp new file mode 100644 index 000000000..b3e3a66c5 --- /dev/null +++ b/network/ndis/ndisprot_kmdf/notifyob/dllmain.cpp @@ -0,0 +1,253 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + dllmain.cpp + +Abstract: + + The module contains the routines to handle the loading and unloading of the + notify object dll and the Wdf Coinstaller library. + +--*/ + +#include "Common.hpp" +#include +_Analysis_mode_(_Analysis_code_type_user_code_) +#include +#include "ProtNotify_i.c" + + +CComModule _Module; + +// for example, WDF 1.9 is "01009". the size 6 includes the ending NULL marker +// +#define MAX_VERSION_SIZE 6 + +WCHAR G_coInstallerVersion[MAX_VERSION_SIZE] = {0}; + +HMODULE CoinstallerLibrary = NULL; +PFN_WDFPREDEVICEINSTALL pfnWdfPreDeviceInstall = NULL; +PFN_WDFPOSTDEVICEINSTALL pfnWdfPostDeviceInstall = NULL; +PFN_WDFPREDEVICEREMOVE pfnWdfPreDeviceRemove = NULL; +PFN_WDFPOSTDEVICEREMOVE pfnWdfPostDeviceRemove = NULL; + +// +// Private methods. +// +HMODULE +LoadWdfCoInstaller( + ); + +VOID +UnloadWdfCoInstaller( + HMODULE Library + ); + + +extern "C" +BOOL +WINAPI +DllMain( + _In_ HINSTANCE Instance, + _In_ DWORD Reason, + _In_ LPVOID Reserved + ) +{ + UNREFERENCED_PARAMETER(Reserved); + + if (Reason == DLL_PROCESS_ATTACH) { + // + // Initialize the COM Server module with the object map. + // Do this prior to loading the coinstaller as the detach + // assumes the Module is initialized. + // + _Module.Init(ObjectMap, Instance); + DisableThreadLibraryCalls(Instance); + + // + // Load the Wdf Coinstaller library. + // + CoinstallerLibrary = LoadWdfCoInstaller(); + if (CoinstallerLibrary == NULL) { + return FALSE; + } + + } + else if (Reason == DLL_PROCESS_DETACH) { + + // + // Relesae the COM Server. + // + _Module.Term(); + + // + // Unload the Wdf Coinstaller library. + // + UnloadWdfCoInstaller(CoinstallerLibrary); + } + + return TRUE; +} + + +STDAPI DllCanUnloadNow( + ) +{ + // + // Determine whether the DLL can be unloaded by OLE + // + return (_Module.GetLockCount() == 0) ? S_OK : S_FALSE; +} + +_Check_return_ +STDAPI +DllGetClassObject( + _In_ REFCLSID rclsid, + _In_ REFIID riid, + _Outptr_ LPVOID FAR* ppv + ) +{ + // + // Return a class factory to create an object of the requested type + // + return _Module.GetClassObject(rclsid, riid, ppv); +} + + +STDAPI DllRegisterServer( + ) +{ + + // + // Register object, typelib and all interfaces in typelib + // + return _Module.RegisterServer(TRUE); +} + + +STDAPI DllUnregisterServer( + ) +{ + // + // Remove entries from the system registry + // + _Module.UnregisterServer(); + return S_OK; +} + +PWCHAR +GetCoinstallerVersion( + VOID + ) +{ + if (FAILED( StringCchPrintf(G_coInstallerVersion, + MAX_VERSION_SIZE, + L"%02d%03d", // for example, "01009" + KMDF_VERSION_MAJOR, + KMDF_VERSION_MINOR))) + { + printf("StringCchCopy failed with error \n"); + } + + return (PWCHAR)&G_coInstallerVersion; +} + +HMODULE +LoadWdfCoInstaller( + ) +{ + + #pragma prefast(suppress:6262, "Supprress overflow warnings") + HRESULT hr = S_OK; + HMODULE library = NULL; + WCHAR coinstaller[MAX_PATH] = {0}; + WCHAR coinstallerName[MAX_PATH/2]; + PWCHAR coinstallerVersion; + + // + // Construct the full file name for the Wdf Coinstaller dll. + // + if (GetSystemDirectory(coinstaller, MAX_PATH) == 0) { + hr = GetLastError(); + goto exit; + } + coinstallerVersion = GetCoinstallerVersion(); + if (FAILED( StringCchPrintf(coinstallerName, + MAX_PATH/2, + L"\\WdfCoInstaller%s.dll", + coinstallerVersion) )) { + goto exit; + } + + hr = StringCchCat(coinstaller, MAX_PATH, coinstallerName); + if (FAILED(hr)) { + goto exit; + } + + // + // Load the Wdf Coinstaller library. + // +#pragma prefast(suppress:28160, "Suppressing false positive from PFD") + library = LoadLibrary(coinstaller); + if (library == NULL) { + hr = GetLastError(); + goto exit; + } + + pfnWdfPreDeviceInstall = (PFN_WDFPREDEVICEINSTALL) GetProcAddress(library, "WdfPreDeviceInstall"); + if (pfnWdfPreDeviceInstall == NULL) { + hr = GetLastError(); + goto exit; + } + + pfnWdfPostDeviceInstall = (PFN_WDFPOSTDEVICEINSTALL) GetProcAddress(library, "WdfPostDeviceInstall"); + if (pfnWdfPostDeviceInstall == NULL) { + hr = GetLastError(); + goto exit; + } + + pfnWdfPreDeviceRemove = (PFN_WDFPREDEVICEREMOVE) GetProcAddress(library, "WdfPreDeviceRemove"); + if (pfnWdfPreDeviceRemove == NULL) { + hr = GetLastError(); + goto exit; + } + + pfnWdfPostDeviceRemove = (PFN_WDFPREDEVICEREMOVE) GetProcAddress(library, "WdfPostDeviceRemove"); + if (pfnWdfPostDeviceRemove == NULL) { + hr = GetLastError(); + goto exit; + } + +exit: + + // + // Unload the Wdf Coinstaller library in case of an error. + // + if (FAILED(hr)) { + UnloadWdfCoInstaller(library); + library = NULL; + } + + SetLastError(hr); + return library; +} + + +VOID +UnloadWdfCoInstaller( + HMODULE Library + ) +{ + if (Library) { + FreeLibrary( Library ); + } + + pfnWdfPreDeviceInstall = NULL; + pfnWdfPostDeviceInstall = NULL; + pfnWdfPreDeviceRemove = NULL; + pfnWdfPostDeviceRemove = NULL; +} + diff --git a/network/ndis/ndisprot_kmdf/notifyob/resource.h b/network/ndis/ndisprot_kmdf/notifyob/resource.h new file mode 100644 index 000000000..c523465ac --- /dev/null +++ b/network/ndis/ndisprot_kmdf/notifyob/resource.h @@ -0,0 +1,17 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + resource.h + +Abstract: + + This header file defines the resource id used by the notify object. + +--*/ + +#define NDIS_NOTIFY_RESOURCE_ID1 1 +#define NDIS_NOTIFY_RESOURCE_ID2 2 + diff --git a/network/ndis/netvmini/6x/60/netvmini60.inf b/network/ndis/netvmini/6x/60/netvmini60.inf index 043aff2e179a629c8b6768587d0247ea322dc6fd..bbfdef53408a87fb38ea26608ca147f70191e224 100644 GIT binary patch delta 335 zcmeBh_~fu*36pdHLlHwhLm5LRLkdGGkR8lW0wgmT@)*)LuV$LaXqpIQmNKLvb8>{i4JGa)K|| tVK}@nxshLb@&N%hZ6yXw^Kj^#EHCFi`3#@Igg+CSGuvY$G2t@q)_aKSDy2cgaV=m}Uw# E0J*g^Jpcdz diff --git a/network/ndis/netvmini/6x/60/netvmini60.vcxproj b/network/ndis/netvmini/6x/60/netvmini60.vcxproj index b5bf188f4..d278b9345 100644 --- a/network/ndis/netvmini/6x/60/netvmini60.vcxproj +++ b/network/ndis/netvmini/6x/60/netvmini60.vcxproj @@ -19,11 +19,11 @@
- {4D117295-96D2-498D-BDE1-C560BFA3C03B} + {B141DA60-040A-41CF-8D57-477AA9ABB743} $(MSBuildProjectName) Debug Win32 - {25A2CEE6-93FE-4CB1-B8FD-8C1718FD45AD} + {F6634E5E-8F90-40D9-BE58-42CF856B00D2} @@ -204,7 +204,6 @@ - diff --git a/network/ndis/netvmini/6x/60/netvmini60.vcxproj.Filters b/network/ndis/netvmini/6x/60/netvmini60.vcxproj.Filters index 664e91f79..68dd24744 100644 --- a/network/ndis/netvmini/6x/60/netvmini60.vcxproj.Filters +++ b/network/ndis/netvmini/6x/60/netvmini60.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {8262BA1D-7108-4104-949F-865D05666127} + {1FAC931A-0EDD-4CD0-9017-5A2412CE9732} h;hpp;hxx;hm;inl;inc;xsd - {1AFDB572-ED64-40CA-9AB7-020E47C3489D} + {2B2AAD08-E00A-4ED1-BBD5-77F9535D796D} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {CE5B8EAF-CC06-4B59-9BC0-2C3A29D3CCDC} + {A563DE90-B977-4364-BF64-6CAC13B90435} inf;inv;inx;mof;mc; - {753BD939-F310-44FF-9240-B67FF5C8417C} + {36A97725-AE42-4EBD-8FEB-1F91A7030AF2} diff --git a/network/ndis/netvmini/6x/620/netvmini620.inf b/network/ndis/netvmini/6x/620/netvmini620.inf index 307f43a03cecf2c3e565b9d702a20215b0b67c96..60c4b68b3eb0afbe80c6b5aa0fb199c64318f2fe 100644 GIT binary patch delta 290 zcmbP~`X_b65+>;Yh9ZW1hBAgsh7^WWAUl|$1W0BwI|Eh5~{Zdcd+t sx)PHG*hD73(UF?`K~G>3zrtiM-8djqXmWy{z~p^hLX+unWi&}f|(4e3K45#6lE!NCl-#Fg7$5yqLUsBby(f(UOg vLn1>iLjgk$Ln?#P - {B4150693-0DE4-4CE8-8FFB-183E2A0DFFF8} + {F6C9791E-8975-472B-9851-63C91C220C77} $(MSBuildProjectName) Debug Win32 - {BBE8763B-C933-4BD8-93FA-001C816AACA6} + {380FA876-25EB-43FF-97D9-BDA9335C398D} @@ -204,7 +204,6 @@ - diff --git a/network/ndis/netvmini/6x/620/netvmini620.vcxproj.Filters b/network/ndis/netvmini/6x/620/netvmini620.vcxproj.Filters index 2dbcbafaa..a6dec852b 100644 --- a/network/ndis/netvmini/6x/620/netvmini620.vcxproj.Filters +++ b/network/ndis/netvmini/6x/620/netvmini620.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {5B72A6DB-853E-4641-BCA1-10EF5DD4E413} + {FBACAA72-1D30-4675-B421-2DA331729033} h;hpp;hxx;hm;inl;inc;xsd - {A4A9A35F-8A74-4BE3-9EB0-E626ECDA38BD} + {264EB6DE-5BE0-4C2C-A950-150060D39476} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {EB77FECD-C552-486B-9539-1E9B52B3466B} + {3F3AB143-503A-4CA4-8F97-49EF709CC3EC} inf;inv;inx;mof;mc; - {02165F6F-54E8-4D4C-832B-CDF2877A781B} + {BEB3FA42-4236-45C6-A2F9-A5DD3E2C283B} diff --git a/network/ndis/netvmini/6x/630/netvmini630.inf b/network/ndis/netvmini/6x/630/netvmini630.inf index dc6bb49a29fb5dbf481338aa3451d0b6bcc54dd9..21c50a099986a39c1bc2c27abb252d739d3f6400 100644 GIT binary patch delta 390 zcmZpunpC}E6_a!TLlHwhLm5LRLkdGGkR8lW0wgmT@)*)LZ)BRzXqpIQmNKLxU7$l#flk$BK-Z(hz{|kJ;0yKyPVY{h uC@DR8gFcJ45(B1*IP^~5Xy!hdNz`Dnjd|Q;CvBz47tCWo^yVasc`N|v0azpe delta 188 zcmbPK-B7h*6%)HJLoq`dL&@f&Ow$=f!Au5K1_cINAcjg!p2!jiSN4%blo`$u - {FD239BD3-EB8B-4BAA-BC83-43F1E1D1EE89} + {CB768EFE-18C4-4621-BCD2-A08AD88E782E} $(MSBuildProjectName) Debug Win32 - {F1A4113F-E795-4CA8-AF38-FBA3D9B88251} + {0DDF32DB-1657-4CE9-9F4A-DABA52BBDDE5} @@ -204,7 +204,6 @@ - diff --git a/network/ndis/netvmini/6x/630/netvmini630.vcxproj.Filters b/network/ndis/netvmini/6x/630/netvmini630.vcxproj.Filters index fba2414b1..57acadcd2 100644 --- a/network/ndis/netvmini/6x/630/netvmini630.vcxproj.Filters +++ b/network/ndis/netvmini/6x/630/netvmini630.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {ABA6EAD2-FED4-495B-8FA0-1529A966F67E} + {CE65B986-17A1-4A63-8E68-A81A032C10DE} h;hpp;hxx;hm;inl;inc;xsd - {B0D5F6C7-747C-4556-9082-1BC1AC0D4FA1} + {93F7C1D6-DA0E-4F71-A7FB-A13F40A9F36D} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {77321221-711B-4C2A-B73F-69E0D1F99853} + {769948AE-BBF6-4212-AB7B-EBE9A721768B} inf;inv;inx;mof;mc; - {60C02FC3-8D34-4BD3-8E28-866B4C29D3E0} + {1E0F6197-640A-42EE-8B60-19F429CF8546} diff --git a/network/ndis/netvmini/6x/netvmini.sln b/network/ndis/netvmini/6x/netvmini.sln index fbbe870cc..c9f7b86e6 100644 --- a/network/ndis/netvmini/6x/netvmini.sln +++ b/network/ndis/netvmini/6x/netvmini.sln @@ -3,17 +3,17 @@ 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", "{5F9487AF-05E7-4436-B499-5EE19925E384}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "60", "60", "{946F3B99-3F33-4464-B3A6-588ABFD005AA}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "620", "620", "{EC37AF20-58A7-44FE-AA97-7EBB2736E9DD}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "620", "620", "{64A56FB2-256D-4064-957E-D65A8503EF37}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "630", "630", "{7F09E94A-63F3-442B-ABEB-843EF656383E}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "630", "630", "{1BBB3B2F-E670-4DED-B558-FADB5856C2DC}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "netvmini60", "60\netvmini60.vcxproj", "{4D117295-96D2-498D-BDE1-C560BFA3C03B}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "netvmini60", "60\netvmini60.vcxproj", "{B141DA60-040A-41CF-8D57-477AA9ABB743}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "netvmini620", "620\netvmini620.vcxproj", "{B4150693-0DE4-4CE8-8FFB-183E2A0DFFF8}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "netvmini620", "620\netvmini620.vcxproj", "{F6C9791E-8975-472B-9851-63C91C220C77}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "netvmini630", "630\netvmini630.vcxproj", "{FD239BD3-EB8B-4BAA-BC83-43F1E1D1EE89}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "netvmini630", "630\netvmini630.vcxproj", "{CB768EFE-18C4-4621-BCD2-A08AD88E782E}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -23,37 +23,37 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {4D117295-96D2-498D-BDE1-C560BFA3C03B}.Debug|Win32.ActiveCfg = Debug|Win32 - {4D117295-96D2-498D-BDE1-C560BFA3C03B}.Debug|Win32.Build.0 = Debug|Win32 - {4D117295-96D2-498D-BDE1-C560BFA3C03B}.Release|Win32.ActiveCfg = Release|Win32 - {4D117295-96D2-498D-BDE1-C560BFA3C03B}.Release|Win32.Build.0 = Release|Win32 - {4D117295-96D2-498D-BDE1-C560BFA3C03B}.Debug|x64.ActiveCfg = Debug|x64 - {4D117295-96D2-498D-BDE1-C560BFA3C03B}.Debug|x64.Build.0 = Debug|x64 - {4D117295-96D2-498D-BDE1-C560BFA3C03B}.Release|x64.ActiveCfg = Release|x64 - {4D117295-96D2-498D-BDE1-C560BFA3C03B}.Release|x64.Build.0 = Release|x64 - {B4150693-0DE4-4CE8-8FFB-183E2A0DFFF8}.Debug|Win32.ActiveCfg = Debug|Win32 - {B4150693-0DE4-4CE8-8FFB-183E2A0DFFF8}.Debug|Win32.Build.0 = Debug|Win32 - {B4150693-0DE4-4CE8-8FFB-183E2A0DFFF8}.Release|Win32.ActiveCfg = Release|Win32 - {B4150693-0DE4-4CE8-8FFB-183E2A0DFFF8}.Release|Win32.Build.0 = Release|Win32 - {B4150693-0DE4-4CE8-8FFB-183E2A0DFFF8}.Debug|x64.ActiveCfg = Debug|x64 - {B4150693-0DE4-4CE8-8FFB-183E2A0DFFF8}.Debug|x64.Build.0 = Debug|x64 - {B4150693-0DE4-4CE8-8FFB-183E2A0DFFF8}.Release|x64.ActiveCfg = Release|x64 - {B4150693-0DE4-4CE8-8FFB-183E2A0DFFF8}.Release|x64.Build.0 = Release|x64 - {FD239BD3-EB8B-4BAA-BC83-43F1E1D1EE89}.Debug|Win32.ActiveCfg = Debug|Win32 - {FD239BD3-EB8B-4BAA-BC83-43F1E1D1EE89}.Debug|Win32.Build.0 = Debug|Win32 - {FD239BD3-EB8B-4BAA-BC83-43F1E1D1EE89}.Release|Win32.ActiveCfg = Release|Win32 - {FD239BD3-EB8B-4BAA-BC83-43F1E1D1EE89}.Release|Win32.Build.0 = Release|Win32 - {FD239BD3-EB8B-4BAA-BC83-43F1E1D1EE89}.Debug|x64.ActiveCfg = Debug|x64 - {FD239BD3-EB8B-4BAA-BC83-43F1E1D1EE89}.Debug|x64.Build.0 = Debug|x64 - {FD239BD3-EB8B-4BAA-BC83-43F1E1D1EE89}.Release|x64.ActiveCfg = Release|x64 - {FD239BD3-EB8B-4BAA-BC83-43F1E1D1EE89}.Release|x64.Build.0 = Release|x64 + {B141DA60-040A-41CF-8D57-477AA9ABB743}.Debug|Win32.ActiveCfg = Debug|Win32 + {B141DA60-040A-41CF-8D57-477AA9ABB743}.Debug|Win32.Build.0 = Debug|Win32 + {B141DA60-040A-41CF-8D57-477AA9ABB743}.Release|Win32.ActiveCfg = Release|Win32 + {B141DA60-040A-41CF-8D57-477AA9ABB743}.Release|Win32.Build.0 = Release|Win32 + {B141DA60-040A-41CF-8D57-477AA9ABB743}.Debug|x64.ActiveCfg = Debug|x64 + {B141DA60-040A-41CF-8D57-477AA9ABB743}.Debug|x64.Build.0 = Debug|x64 + {B141DA60-040A-41CF-8D57-477AA9ABB743}.Release|x64.ActiveCfg = Release|x64 + {B141DA60-040A-41CF-8D57-477AA9ABB743}.Release|x64.Build.0 = Release|x64 + {F6C9791E-8975-472B-9851-63C91C220C77}.Debug|Win32.ActiveCfg = Debug|Win32 + {F6C9791E-8975-472B-9851-63C91C220C77}.Debug|Win32.Build.0 = Debug|Win32 + {F6C9791E-8975-472B-9851-63C91C220C77}.Release|Win32.ActiveCfg = Release|Win32 + {F6C9791E-8975-472B-9851-63C91C220C77}.Release|Win32.Build.0 = Release|Win32 + {F6C9791E-8975-472B-9851-63C91C220C77}.Debug|x64.ActiveCfg = Debug|x64 + {F6C9791E-8975-472B-9851-63C91C220C77}.Debug|x64.Build.0 = Debug|x64 + {F6C9791E-8975-472B-9851-63C91C220C77}.Release|x64.ActiveCfg = Release|x64 + {F6C9791E-8975-472B-9851-63C91C220C77}.Release|x64.Build.0 = Release|x64 + {CB768EFE-18C4-4621-BCD2-A08AD88E782E}.Debug|Win32.ActiveCfg = Debug|Win32 + {CB768EFE-18C4-4621-BCD2-A08AD88E782E}.Debug|Win32.Build.0 = Debug|Win32 + {CB768EFE-18C4-4621-BCD2-A08AD88E782E}.Release|Win32.ActiveCfg = Release|Win32 + {CB768EFE-18C4-4621-BCD2-A08AD88E782E}.Release|Win32.Build.0 = Release|Win32 + {CB768EFE-18C4-4621-BCD2-A08AD88E782E}.Debug|x64.ActiveCfg = Debug|x64 + {CB768EFE-18C4-4621-BCD2-A08AD88E782E}.Debug|x64.Build.0 = Debug|x64 + {CB768EFE-18C4-4621-BCD2-A08AD88E782E}.Release|x64.ActiveCfg = Release|x64 + {CB768EFE-18C4-4621-BCD2-A08AD88E782E}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {4D117295-96D2-498D-BDE1-C560BFA3C03B} = {5F9487AF-05E7-4436-B499-5EE19925E384} - {B4150693-0DE4-4CE8-8FFB-183E2A0DFFF8} = {EC37AF20-58A7-44FE-AA97-7EBB2736E9DD} - {FD239BD3-EB8B-4BAA-BC83-43F1E1D1EE89} = {7F09E94A-63F3-442B-ABEB-843EF656383E} + {B141DA60-040A-41CF-8D57-477AA9ABB743} = {946F3B99-3F33-4464-B3A6-588ABFD005AA} + {F6C9791E-8975-472B-9851-63C91C220C77} = {64A56FB2-256D-4064-957E-D65A8503EF37} + {CB768EFE-18C4-4621-BCD2-A08AD88E782E} = {1BBB3B2F-E670-4DED-B558-FADB5856C2DC} EndGlobalSection EndGlobal diff --git a/network/radio/HidSwitchDriverSample/HidSwitchDriverSample.sln b/network/radio/HidSwitchDriverSample/HidSwitchDriverSample.sln index 63278f582..4be1a1347 100644 --- a/network/radio/HidSwitchDriverSample/HidSwitchDriverSample.sln +++ b/network/radio/HidSwitchDriverSample/HidSwitchDriverSample.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0 MinimumVisualStudioVersion = 12.0 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RadioSwitchHidUsbFx2", "RadioSwitchHidUsbFx2.vcxproj", "{299CE7FF-033A-4968-906D-5B8D00C1FE0C}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RadioSwitchHidUsbFx2", "RadioSwitchHidUsbFx2.vcxproj", "{B5BA81A6-4044-4AAA-9101-F6B84922B842}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {299CE7FF-033A-4968-906D-5B8D00C1FE0C}.Debug|Win32.ActiveCfg = Debug|Win32 - {299CE7FF-033A-4968-906D-5B8D00C1FE0C}.Debug|Win32.Build.0 = Debug|Win32 - {299CE7FF-033A-4968-906D-5B8D00C1FE0C}.Release|Win32.ActiveCfg = Release|Win32 - {299CE7FF-033A-4968-906D-5B8D00C1FE0C}.Release|Win32.Build.0 = Release|Win32 - {299CE7FF-033A-4968-906D-5B8D00C1FE0C}.Debug|x64.ActiveCfg = Debug|x64 - {299CE7FF-033A-4968-906D-5B8D00C1FE0C}.Debug|x64.Build.0 = Debug|x64 - {299CE7FF-033A-4968-906D-5B8D00C1FE0C}.Release|x64.ActiveCfg = Release|x64 - {299CE7FF-033A-4968-906D-5B8D00C1FE0C}.Release|x64.Build.0 = Release|x64 + {B5BA81A6-4044-4AAA-9101-F6B84922B842}.Debug|Win32.ActiveCfg = Debug|Win32 + {B5BA81A6-4044-4AAA-9101-F6B84922B842}.Debug|Win32.Build.0 = Debug|Win32 + {B5BA81A6-4044-4AAA-9101-F6B84922B842}.Release|Win32.ActiveCfg = Release|Win32 + {B5BA81A6-4044-4AAA-9101-F6B84922B842}.Release|Win32.Build.0 = Release|Win32 + {B5BA81A6-4044-4AAA-9101-F6B84922B842}.Debug|x64.ActiveCfg = Debug|x64 + {B5BA81A6-4044-4AAA-9101-F6B84922B842}.Debug|x64.Build.0 = Debug|x64 + {B5BA81A6-4044-4AAA-9101-F6B84922B842}.Release|x64.ActiveCfg = Release|x64 + {B5BA81A6-4044-4AAA-9101-F6B84922B842}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/network/radio/HidSwitchDriverSample/RadioSwitchHidUsbFx2.inx b/network/radio/HidSwitchDriverSample/RadioSwitchHidUsbFx2.inx index 5089dfdd9..69dee305a 100644 --- a/network/radio/HidSwitchDriverSample/RadioSwitchHidUsbFx2.inx +++ b/network/radio/HidSwitchDriverSample/RadioSwitchHidUsbFx2.inx @@ -2,7 +2,7 @@ Signature="$Windows NT$" Class=HIDClass ClassGuid={745a17a0-74d3-11d0-b6fe-00a0c90f57da} -Provider=%VENDOR% +Provider=%ProviderString% DriverVer=5/3/2011,1.0.0.0 CatalogFile=HidRadioSwitchDrv.cat @@ -18,10 +18,10 @@ CopyFunctionDriver = 12 CopyFilterDriver = 12 [Manufacturer] -%VENDOR%=Vendor, NT$ARCH$.6.1 +%ManufacturerName%=Standard, NT$ARCH$.6.1 ; For Win7 and later so that we can use inbox HID-KMDF mapper -[Vendor.NT$ARCH$.6.1] +[Standard.NT$ARCH$.6.1] %RadioSwitchHidUsbFx2% = RadioSwitchHidUsbFx2.Inst.Win7, USB\VID_0547&PID_1002 %customCollection% = customCollection.Inst, HID_DEVICE_UP:FF00_U:0001 @@ -87,10 +87,11 @@ WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll=99 ; make sure the number matches wit [Strings] ; *******Localizable Strings******* -VENDOR = "Vendor Name" -RadioSwitchHidUsbFx2 = "Radio Switch HID Mini-driver for OSR USB-FX-2 Device" -customCollection = "Radio Switch Collection for OSR USB-FX-2" -DISK_NAME = "HID Radio Switch USB FX-2 Install Disk" +ProviderString = "TODO-Set-Provider" +ManufacturerName = "TODO-Set-Manufacturer" +RadioSwitchHidUsbFx2 = "Radio Switch HID Mini-driver for OSR USB-FX-2 Device" +customCollection = "Radio Switch Collection for OSR USB-FX-2" +DISK_NAME = "HID Radio Switch USB FX-2 Install Disk" ; *******Non Localizable Strings******* @@ -111,4 +112,3 @@ REG_DWORD = 0x00010001 REG_MULTI_SZ = 0x00010000 REG_BINARY = 0x00000001 REG_SZ = 0x00000000 - diff --git a/network/radio/HidSwitchDriverSample/RadioSwitchHidUsbFx2.vcxproj b/network/radio/HidSwitchDriverSample/RadioSwitchHidUsbFx2.vcxproj index 690f4f3f5..a2c21dec8 100644 --- a/network/radio/HidSwitchDriverSample/RadioSwitchHidUsbFx2.vcxproj +++ b/network/radio/HidSwitchDriverSample/RadioSwitchHidUsbFx2.vcxproj @@ -19,12 +19,12 @@ - {299CE7FF-033A-4968-906D-5B8D00C1FE0C} + {B5BA81A6-4044-4AAA-9101-F6B84922B842} $(MSBuildProjectName) 1 Debug Win32 - {060E6040-ACF0-4A53-A87E-2D541F54BDC3} + {B02E786E-7854-4691-9A77-2FC43BADC689} @@ -168,7 +168,6 @@ - diff --git a/network/radio/HidSwitchDriverSample/RadioSwitchHidUsbFx2.vcxproj.Filters b/network/radio/HidSwitchDriverSample/RadioSwitchHidUsbFx2.vcxproj.Filters index b4198ba45..0bd5107d5 100644 --- a/network/radio/HidSwitchDriverSample/RadioSwitchHidUsbFx2.vcxproj.Filters +++ b/network/radio/HidSwitchDriverSample/RadioSwitchHidUsbFx2.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {FDCC489B-556E-43F5-BD3B-71316D442DEA} + {53A053E8-9459-4AAE-8121-C8E18D611B9A} h;hpp;hxx;hm;inl;inc;xsd - {18150D25-6522-47D8-9CB4-5108F5F98F28} + {CD5A6D68-BA75-419E-8BC0-ECBAB00BE55B} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {A79E57E4-3B90-45E9-95EB-A0541E4A8DF4} + {25DF7F05-F2BC-4531-AEFD-F9A4B083145B} inf;inv;inx;mof;mc; - {0F70BE0E-ED35-4FD6-88BE-92687BE1BBB6} + {D805F501-79BE-4A7A-B472-8EE48E64C4E2} @@ -30,9 +30,6 @@ - - Driver Files - Driver Files diff --git a/network/radio/RadioManagerSample/RadioManagerSample.sln b/network/radio/RadioManagerSample/RadioManagerSample.sln index d5d0a144e..37eb8bd09 100644 --- a/network/radio/RadioManagerSample/RadioManagerSample.sln +++ b/network/radio/RadioManagerSample/RadioManagerSample.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0 MinimumVisualStudioVersion = 12.0 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SampleRM", "cpp\SampleRM.vcxproj", "{165D39D6-68AC-4E1F-89ED-483E13E84F9B}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SampleRM", "cpp\SampleRM.vcxproj", "{3A2C6344-A162-433C-B8DE-2282825DE084}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {165D39D6-68AC-4E1F-89ED-483E13E84F9B}.Debug|Win32.ActiveCfg = Debug|Win32 - {165D39D6-68AC-4E1F-89ED-483E13E84F9B}.Debug|Win32.Build.0 = Debug|Win32 - {165D39D6-68AC-4E1F-89ED-483E13E84F9B}.Release|Win32.ActiveCfg = Release|Win32 - {165D39D6-68AC-4E1F-89ED-483E13E84F9B}.Release|Win32.Build.0 = Release|Win32 - {165D39D6-68AC-4E1F-89ED-483E13E84F9B}.Debug|x64.ActiveCfg = Debug|x64 - {165D39D6-68AC-4E1F-89ED-483E13E84F9B}.Debug|x64.Build.0 = Debug|x64 - {165D39D6-68AC-4E1F-89ED-483E13E84F9B}.Release|x64.ActiveCfg = Release|x64 - {165D39D6-68AC-4E1F-89ED-483E13E84F9B}.Release|x64.Build.0 = Release|x64 + {3A2C6344-A162-433C-B8DE-2282825DE084}.Debug|Win32.ActiveCfg = Debug|Win32 + {3A2C6344-A162-433C-B8DE-2282825DE084}.Debug|Win32.Build.0 = Debug|Win32 + {3A2C6344-A162-433C-B8DE-2282825DE084}.Release|Win32.ActiveCfg = Release|Win32 + {3A2C6344-A162-433C-B8DE-2282825DE084}.Release|Win32.Build.0 = Release|Win32 + {3A2C6344-A162-433C-B8DE-2282825DE084}.Debug|x64.ActiveCfg = Debug|x64 + {3A2C6344-A162-433C-B8DE-2282825DE084}.Debug|x64.Build.0 = Debug|x64 + {3A2C6344-A162-433C-B8DE-2282825DE084}.Release|x64.ActiveCfg = Release|x64 + {3A2C6344-A162-433C-B8DE-2282825DE084}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/network/radio/RadioManagerSample/cpp/SampleRM.vcxproj b/network/radio/RadioManagerSample/cpp/SampleRM.vcxproj index f26b9385c..740c33999 100644 --- a/network/radio/RadioManagerSample/cpp/SampleRM.vcxproj +++ b/network/radio/RadioManagerSample/cpp/SampleRM.vcxproj @@ -19,11 +19,11 @@ - {165D39D6-68AC-4E1F-89ED-483E13E84F9B} + {3A2C6344-A162-433C-B8DE-2282825DE084} $(MSBuildProjectName) Debug Win32 - {6D846F43-0EBE-4FCD-90E5-915DE0BCBA42} + {0029FEAE-6B50-419B-8919-DFCC68EE0819} @@ -312,7 +312,6 @@ - diff --git a/network/radio/RadioManagerSample/cpp/SampleRM.vcxproj.Filters b/network/radio/RadioManagerSample/cpp/SampleRM.vcxproj.Filters index 837a0aabe..8393d3943 100644 --- a/network/radio/RadioManagerSample/cpp/SampleRM.vcxproj.Filters +++ b/network/radio/RadioManagerSample/cpp/SampleRM.vcxproj.Filters @@ -3,15 +3,15 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {6C044E6F-8320-46E5-9EBE-BDB0427DF29D} + {6E87A5A2-6E58-40A3-8118-0E8CFBE8A8B4} h;hpp;hxx;hm;inl;inc;xsd - {55F7F46D-B2E7-422F-8140-2F6D4D30D4FE} + {BDDFBC01-9DDC-4697-AE54-4EA76DE17211} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {EB9B7D59-264D-421A-8077-FA89E50F57CF} + {FF6DC7A4-CA52-411E-A705-BB9DAEBFED69} diff --git a/network/trans/ReadMe.md b/network/trans/ReadMe.md new file mode 100644 index 000000000..298ab4f0c --- /dev/null +++ b/network/trans/ReadMe.md @@ -0,0 +1,277 @@ +Windows Filtering Platform Sample +================================= + +The WFPSampler sample driver is a sample firewall. It has a command-line interface which allows adding filters at various WFP layers with a wide variety of conditions. Additionally it exposes callout functions for injection, basic action, proxying, and stream inspection. + +WFPSampler.Exe is the command-line interface used by the user to define the policy. + +WFPSamplerService.Exe is the service which instructs BFE to add or remove policies. + +WFPSamplerCalloutDriver.Sys is the driver which houses the various callout functions. + +WFPSamplerProxyService.Exe is the service which listens for connections to proxy. + +WFPSampler.Lib is a library of user mode helper functions used throughout the project. + +WFPSamplerSys.Lib is a library of kernel mode helper functions used throughout the project. + +"WFPSamplerInstall.cmd" will copy the necessary binaries to their appropriate location, and install each component. + +"WFPSamplerInstall.cmd -r" will uninstall each component and remove the binaries from the appropriate location. + +Once you have downloaded the sample, the .mht files in the sample's docs directory describe the various WFP filtering scenarios that you can try. + +For more information about WFP callout drivers, see [Windows Filtering Platform Callout Drivers](http://msdn.microsoft.com/en-us/library/windows/hardware/ff571068). + +Related topics +-------------- + +[Windows Filtering Platform Callout Drivers](http://msdn.microsoft.com/en-us/library/windows/hardware/ff571068) + +Open the driver solution in Visual Studio +----------------------------------------- + +Navigate to the folder that has the extracted sample. Double click the solution file, WFPSampler.sln. In 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 has 6 projects: + +- a user-mode application project named **WFPSampler** (under the **Exe** node) +- a user-mode library project named **WFPSampler** (under the **Lib** node) +- a package project named **package** (lower case) (under the **Package** node) +- a user-mode service project named **WFPSamplerService** (under the **Svc** node) +- a driver project named **WFPSamplerCalloutDriver** (under the **Sys** node) +- a kernel-mode library project named **WFPSampler** (under the **Syslib** node) + +Set the configuration and platform in Visual Studio +--------------------------------------------------- + +In Visual Studio, in Solution Explorer, right click **Solution ‘WFPSampler’ (6 projects)**, and choose **Configuration Manager**. Set the configuration and the platform. Make sure that the configuration and platform are the same for all three projects. Do not check the **Deploy** boxes. SHere are some examples of configuration and platform settings. + + +++++ + + + + + + + + + + + +
Configuration +Platform +Description
Win8.1 Debug +x64 +The driver will run on an x64 hardware platform that is running Windows 8.1. The driver will not run on any earlier versions of Windows.Win7 Debug +x64 +The driver will run on an x64 hardware platform that is running Windows 7 or a later version of Windows.
+ +Set the KMDF version for the driver and kernel-mode library +----------------------------------------------------------- + +The operating system that you specified in your configuration is called the *target operating system*. For example, if you specified Win7 Debug in your configuration, your target operating system is Windows 7. In Solution Explorer, right-click **WFPSamplerCalloutDriver** (under the **Sys** node), and choose **Properties**. Navigate to **Configuration Properties \> Driver Model Settings**. Set **KMDF Version Major** to 1. Set **KMDF Version Minor** according to your target operating system. + + ++++ + + + + + + + + + + + +
Target operating system +KMDF minor version
Windows 7 +9Windows 8 +11
+ +Repeat this process for the **WFPSampler** kernel-mode library (under the **Syslib** node). + +Set the runtime library for the user-mode application, library, and service +--------------------------------------------------------------------------- + +In Solution Explorer, right-click the **WFPSampler** user-mode application project (under the **Exe** node), and choose **Properties.** Navigate to **Configuration Properties \> C/C++ \> Code Generation**. For **Runtime Library**, select **Multi-threaded Debug (/MTd)**. Click **OK**. + +Repeat this process for the **WFPSampler** user-mode library (under the **Lib** node) and the **WFPSampler** user-mode service (under the **Svc** node). + +Edit the restart setting in the sample installation script +---------------------------------------------------------- + +Open the WfpSamplerInstall.cmd file (in the scripts folder) in Visual Studio. + +Change this line: + + +++ + + + + + +
RunDLL32.Exe syssetup,SetupInfObjectInstallAction DefaultInstall 131 %WinDir%\System32\Drivers\WFPSamplerCalloutDriver.Inf
+ +to this: + + +++ + + + + + +
RunDLL32.Exe syssetup,SetupInfObjectInstallAction DefaultInstall 132 %WinDir%\System32\Drivers\WFPSamplerCalloutDriver.Inf
+ +For more information about this setting, see the Remarks section for the [**InstallHinfSection**](http://msdn.microsoft.com/en-us/library/windows/hardware/aa376957) function. + +Build the sample using Visual Studio +------------------------------------ + +In Visual Studio, on the **Build** menu, choose **Build Solution**. + +For more information about using Microsoft Visual Studio to build a driver package, see [Building a Driver](http://msdn.microsoft.com/en-us/library/windows/hardware/ff554644). + +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 Win7 Debug and x64, the package is in your sample folder under x64\\Win7Debug\\package. + +The package folder contains these 4 files: + + ++++ + + + + + + + + + + + +
File +Description
wfpsamplercalloutdriver.cat +A signed catalog file, which serves as the signature for the entire package.WFPSamplerCalloutDriver.inf +An information (INF) file that contains information needed to install the driver.
+ +**Note**   + +The build process might also put WdfCoinstaller010*xx*.dll in the package folder, but this file is not really part of the driver package. The INF file does not reference any coinstallers. The driver package, which is test-signed, actually contains only three files: + +- wfpsamplercalloutdriver.cat +- WFPSamplerCalloutDriver.inf +- WFPSamplerCalloutDriver.sys + +Because the package does not contain a KMDF coinstaller, it is important that you set the KMDF minor version (as described previously) according to your target operating system when you built the driver. + +Locate the symbol file (PDB) for the driver +------------------------------------------- + +In **File Explorer**, locate the symbol file, WFPSamplerCalloutDriver.pdb. The location of this file varies depending on what you set for configuration and platform. For example, if your settings are Win7 Debug and x64, the PDB file is in your sample folder under sys\\x64\\Win7Debug. + +Locate the user-mode application and its symbol file (PDB) +---------------------------------------------------------- + +In **File Explorer**, locate the user-mode application (WFPSampler.exe) and its symbol file (WFPSampler.pdb). The location of these files varies depending on what you set for configuration and platform. For example, if your settings are Win7 Debug and x64, WFPSampler.exe and WFPSampler.pdb are in your sample folder under exe\\x64\\Win7Debug. + +Locate the kernel-mode service and its symbol file (PDB) +-------------------------------------------------------- + +In **File Explorer**, locate the kernel-mode library, WFPSamplerService.exe. The location of this file varies depending on what you set for configuration and platform. For example, if your settings are Win7 Debug and x64, WFPSamplerService.exe and WFPSamplerService.pdb are in your sample folder under svc\\x64\\Win7Debug. + +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 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. In the **Build** menu, choose **Build Solution**. +4. Copy the following files to the DriverTest\\Drivers folder on the target computer: + - The user-mode application (WFPSampler.exe) file + - The kernel-mode service (WFPSamplerService.exe) file + +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, copy the following files to a folder on the target computer (for example, c:\\WFPSamplerSamplePackage): + +- The 4 files in your driver package folder +- The user-mode application (WFPSampler.exe) file +- The kernel-mode service (WFPSamplerService.exe) file + +Copy additional files to the target computer +-------------------------------------------- + +Copy the driver's PDB file (WFPSamplerCalloutDriver.pdb), the user-mode service's PDB file (WFPSamplerService.pdb) and the user-mode application's PDB file (WFPSampler.pdb) to a folder on the target computer (for example, c:\\Symbols). + +Copy the [TraceView](http://msdn.microsoft.com/en-us/library/windows/hardware/ff553872) and [**SignTool**](http://msdn.microsoft.com/en-us/library/windows/hardware/ff551778) tools to a folder on the target computer (for example c:\\Tools). + +- [TraceView](http://msdn.microsoft.com/en-us/library/windows/hardware/ff553872) 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). +- [**SignTool**](http://msdn.microsoft.com/en-us/library/windows/hardware/ff551778) also comes with the WDK. You can find it in your WDK installation folder under bin (for example, c:\\Program Files (x86)\\Windows Kits\\8.1\\bin\\x64\\SignTool.exe). + +Installing the driver +--------------------- + +1. On the target computer, open a Command Prompt window as Administrator. Navigate to the folder that contains the installation script: + - For manual deployment, this will be the folder that you copied the driver page files into (for example, c:\\WFPSamplerSamplePackage). + - For automatic deployment, this will be DriverTest\\Drivers. + +2. Enter **WFPSamplerInstall.cmd** to run the installation script. + **Note**  If you need to uninstall a previous version of the driver, enter **WFPSamplerInstall.cmd -r**. + +Running the user-mode application +--------------------------------- + +On the target computer, open a Command Prompt window as Administrator. + +If you just want to see whether you can run the application, enter **WFPSampler.exe -?**. + +The .mht files in the docs directory describe the various WFP filtering scenarios that you can try. + +For example, you can test the basic packet examination scenario by using the following command line: + +**WFPSampler.exe -s BASIC\_PACKET\_EXAMINATION -l FWPM\_LAYER\_INBOUND\_IPPACKET\_V4 -v** + +This command line adds a dynamic filter (-v) at the FWPM\_LAYER\_INBOUND\_IPPACKET\_V4 layer (-l) which references the appropriate callout driver function. This filter will have no conditions, so it will act on all traffic seen at this layer. + +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, WFPSamplerCalloutDriver.pdb. Click **OK** and click **Next**. Click the **\>\>** button next to **Set Flags and Level**, double-click the **L** button next to **Level**, and set the **Level** to **Information**. Click **OK** and click **Finish**. + +If you want to test whether your TraceView.exe session is working, you can enter the following commands and see what the trace output looks like: + +- **net stop WFPSamplerCallouts** +- **net start WFPSamplerCallouts** + +For more information, see [Creating a Trace Session with a PDB File](http://msdn.microsoft.com/en-us/library/windows/hardware/ff543582). + +Tracing for the sample driver can be started at any time before the driver is started or while the driver is already running. + diff --git a/network/trans/WFPSampler/HCK/WFPLogo_WFPSampler.Answer b/network/trans/WFPSampler/HCK/WFPLogo_WFPSampler.Answer new file mode 100644 index 000000000..cf9ce4d56 --- /dev/null +++ b/network/trans/WFPSampler/HCK/WFPLogo_WFPSampler.Answer @@ -0,0 +1,260 @@ +################################### +# Timer sufficient enough to # +# allow execution of command # +# and return (in seconds) # +################################### + configurationTimer = 10; + + +################################### +# ArchitecturalDesign.StreamInjection.NoStreamStarvation.1 + LOGO_ADD = %WinDir%\System32\WFPSampler.exe -s BASIC_STREAM_INJECTION -l FWPM_LAYER_STREAM_V4 -ipla %LOCAL_IP% -ipra %REMOTE_IP% -iplp %LOCAL_PORT% -iprp %REMOTE_PORT% -v ; + LOGO_DELETE = %WinDir%\System32\WFPSampler.exe -s BASIC_STREAM_INJECTION -l FWPM_LAYER_STREAM_V4 -ipla %LOCAL_IP% -ipra %REMOTE_IP% -iplp %LOCAL_PORT% -iprp %REMOTE_PORT% -v -r ; + +# ArchitecturalDesign.StreamInjection.NoStreamStarvation.2 + LOGO_ADD = %WinDir%\System32\WFPSampler.exe -s BASIC_STREAM_INJECTION -l FWPM_LAYER_STREAM_V6 -ipla %LOCAL_IP% -ipra %REMOTE_IP% -iplp %LOCAL_PORT% -iprp %REMOTE_PORT% -v ; + LOGO_DELETE = %WinDir%\System32\WFPSampler.exe -s BASIC_STREAM_INJECTION -l FWPM_LAYER_STREAM_V6 -ipla %LOCAL_IP% -ipra %REMOTE_IP% -iplp %LOCAL_PORT% -iprp %REMOTE_PORT% -v -r ; + +################################### +# ArchitecturalDesign.PacketInjection.NoDeadlocks + numPacketInjectionCommands = 2; + +# ArchitecturalDesign.PacketInjection.NoDeadlocks.1 + LOGO_ADD = %WinDir%\System32\WFPSampler.exe -s BASIC_PACKET_INJECTION -l FWPM_LAYER_%DIRECTION%BOUND_IPPACKET_V%IP_VERSION% -ipla %LOCAL_IP% -ipra %REMOTE_IP% -v ; + LOGO_DELETE = %WinDir%\System32\WFPSampler.exe -s BASIC_PACKET_INJECTION -l FWPM_LAYER_%DIRECTION%BOUND_IPPACKET_V%IP_VERSION% -ipla %LOCAL_IP% -ipra %REMOTE_IP% -v -r ; + +# ArchitecturalDesign.PacketInjection.NoDeadlocks.2 + LOGO_ADD = %WinDir%\System32\WFPSampler.exe -s BASIC_PACKET_INJECTION -l FWPM_LAYER_%DIRECTION%BOUND_TRANSPORT_V%IP_VERSION% -ipla %LOCAL_IP% -ipra %REMOTE_IP% -v ; + LOGO_DELETE = %WinDir%\System32\WFPSampler.exe -s BASIC_PACKET_INJECTION -l FWPM_LAYER_%DIRECTION%BOUND_TRANSPORT_V%IP_VERSION% -ipla %LOCAL_IP% -ipra %REMOTE_IP% -v -r ; + +################################### +# ArchitecturalDesign.SupportPowerManagedStates.1 + LOGO_ADD = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_PERMIT -l FWPM_LAYER_ALE_AUTH_CONNECT_V4 -aaid %APPLICATION% -ipla %LOCAL_IP% -ipra %REMOTE_IP% -ipp %PROTOCOL% -iplp %LOCAL_PORT% -iprp %REMOTE_PORT% -c -v ; + LOGO_DELETE = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_PERMIT -l FWPM_LAYER_ALE_AUTH_CONNECT_V4 -aaid %APPLICATION% -ipla %LOCAL_IP% -ipra %REMOTE_IP% -ipp %PROTOCOL% -iplp %LOCAL_PORT% -iprp %REMOTE_PORT% -c -v -r ; + +# ArchitecturalDesign.SupportPowerManagedStates.2 + LOGO_ADD = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_BLOCK -l FWPM_LAYER_ALE_AUTH_CONNECT_V4 -aaid %APPLICATION% -ipla %LOCAL_IP% -ipra %REMOTE_IP% -ipp %PROTOCOL% -iplp %LOCAL_PORT% -iprp %REMOTE_PORT% -c -v ; + LOGO_DELETE = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_BLOCK -l FWPM_LAYER_ALE_AUTH_CONNECT_V4 -aaid %APPLICATION% -ipla %LOCAL_IP% -ipra %REMOTE_IP% -ipp %PROTOCOL% -iplp %LOCAL_PORT% -iprp %REMOTE_PORT% -c -v -r ; + +# ArchitecturalDesign.SupportPowerManagedStates.3 + LOGO_ADD = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_PERMIT -l FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 -aaid %APPLICATION% -ipla %LOCAL_IP% -ipra %REMOTE_IP% -ipp %PROTOCOL% -iplp %LOCAL_PORT% -iprp %REMOTE_PORT% -c -v ; + LOGO_DELETE = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_PERMIT -l FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 -aaid %APPLICATION% -ipla %LOCAL_IP% -ipra %REMOTE_IP% -ipp %PROTOCOL% -iplp %LOCAL_PORT% -iprp %REMOTE_PORT% -c -v -r ; + +# ArchitecturalDesign.SupportPowerManagedStates.4 + LOGO_ADD = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_BLOCK -l FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 -aaid %APPLICATION% -ipla %LOCAL_IP% -ipra %REMOTE_IP% -ipp %PROTOCOL% -iplp %LOCAL_PORT% -iprp %REMOTE_PORT% -c -v ; + LOGO_DELETE = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_BLOCK -l FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 -aaid %APPLICATION% -ipla %LOCAL_IP% -ipra %REMOTE_IP% -ipp %PROTOCOL% -iplp %LOCAL_PORT% -iprp %REMOTE_PORT% -c -v -r ; + +# ArchitecturalDesign.SupportPowerManagedStates.5 + LOGO_ADD = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_PERMIT -l FWPM_LAYER_ALE_AUTH_CONNECT_V6 -aaid %APPLICATION% -ipla %LOCAL_IP% -ipra %REMOTE_IP% -ipp %PROTOCOL% -iplp %LOCAL_PORT% -iprp %REMOTE_PORT% -c -v ; + LOGO_DELETE = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_PERMIT -l FWPM_LAYER_ALE_AUTH_CONNECT_V6 -aaid %APPLICATION% -ipla %LOCAL_IP% -ipra %REMOTE_IP% -ipp %PROTOCOL% -iplp %LOCAL_PORT% -iprp %REMOTE_PORT% -c -v -r ; + +# ArchitecturalDesign.SupportPowerManagedStates.6 + LOGO_ADD = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_BLOCK -l FWPM_LAYER_ALE_AUTH_CONNECT_V6 -aaid %APPLICATION% -ipla %LOCAL_IP% -ipra %REMOTE_IP% -ipp %PROTOCOL% -iplp %LOCAL_PORT% -iprp %REMOTE_PORT% -c -v ; + LOGO_DELETE = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_BLOCK -l FWPM_LAYER_ALE_AUTH_CONNECT_V6 -aaid %APPLICATION% -ipla %LOCAL_IP% -ipra %REMOTE_IP% -ipp %PROTOCOL% -iplp %LOCAL_PORT% -iprp %REMOTE_PORT% -c -v -r ; + +# ArchitecturalDesign.SupportPowerManagedStates.7 + LOGO_ADD = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_PERMIT -l FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 -aaid %APPLICATION% -ipla %LOCAL_IP% -ipra %REMOTE_IP% -ipp %PROTOCOL% -iplp %LOCAL_PORT% -iprp %REMOTE_PORT% -c -v ; + LOGO_DELETE = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_PERMIT -l FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 -aaid %APPLICATION% -ipla %LOCAL_IP% -ipra %REMOTE_IP% -ipp %PROTOCOL% -iplp %LOCAL_PORT% -iprp %REMOTE_PORT% -c -v -r ; + +# ArchitecturalDesign.SupportPowerManagedStates.8 + LOGO_ADD = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_BLOCK -l FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 -aaid %APPLICATION% -ipla %LOCAL_IP% -ipra %REMOTE_IP% -ipp %PROTOCOL% -iplp %LOCAL_PORT% -iprp %REMOTE_PORT% -c -v ; + LOGO_DELETE = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_BLOCK -l FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 -aaid %APPLICATION% -ipla %LOCAL_IP% -ipra %REMOTE_IP% -ipp %PROTOCOL% -iplp %LOCAL_PORT% -iprp %REMOTE_PORT% -c -v -r ; + +################################### +# Firewall.SupportMACAddressExceptions.1 + LOGO_ADD = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_PERMIT -l FWPM_LAYER_OUTBOUND_MAC_FRAME_ETHERNET -mla %LOCAL_MAC% -mra %REMOTE_MAC% -c -v ; + LOGO_DELETE = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_PERMIT -l FWPM_LAYER_OUTBOUND_MAC_FRAME_ETHERNET -mla %LOCAL_MAC% -mra %REMOTE_MAC% -c -v -r ; + +# Firewall.SupportMACAddressExceptions.2 + LOGO_ADD = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_BLOCK -l FWPM_LAYER_OUTBOUND_MAC_FRAME_ETHERNET -mla %LOCAL_MAC% -mra %REMOTE_MAC% -c -v ; + LOGO_DELETE = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_BLOCK -l FWPM_LAYER_OUTBOUND_MAC_FRAME_ETHERNET -mla %LOCAL_MAC% -mra %REMOTE_MAC% -c -v -r ; + +# Firewall.SupportMACAddressExceptions.3 + LOGO_ADD = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_PERMIT -l FWPM_LAYER_INBOUND_MAC_FRAME_ETHERNET -mla %LOCAL_MAC% -mra %REMOTE_MAC% -c -v ; + LOGO_DELETE = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_PERMIT -l FWPM_LAYER_INBOUND_MAC_FRAME_ETHERNET -mla %LOCAL_MAC% -mra %REMOTE_MAC% -c -v -r ; + +# Firewall.SupportMACAddressExceptions.4 + LOGO_ADD = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_BLOCK -l FWPM_LAYER_INBOUND_MAC_FRAME_ETHERNET -mla %LOCAL_MAC% -mra %REMOTE_MAC% -c -v ; + LOGO_DELETE = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_BLOCK -l FWPM_LAYER_INBOUND_MAC_FRAME_ETHERNET -mla %LOCAL_MAC% -mra %REMOTE_MAC% -c -v -r ; + +# Firewall.SupportMACAddressExceptions.5 + LOGO_ADD = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_PERMIT -l FWPM_LAYER_OUTBOUND_MAC_FRAME_ETHERNET -mla %LOCAL_MAC% -mra %REMOTE_MAC% -c -v ; + LOGO_DELETE = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_PERMIT -l FWPM_LAYER_OUTBOUND_MAC_FRAME_ETHERNET -mla %LOCAL_MAC% -mra %REMOTE_MAC% -c -v -r ; + +# Firewall.SupportMACAddressExceptions.6 + LOGO_ADD = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_BLOCK -l FWPM_LAYER_OUTBOUND_MAC_FRAME_ETHERNET -mla %LOCAL_MAC% -mra %REMOTE_MAC% -c -v ; + LOGO_DELETE = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_BLOCK -l FWPM_LAYER_OUTBOUND_MAC_FRAME_ETHERNET -mla %LOCAL_MAC% -mra %REMOTE_MAC% -c -v -r ; + +# Firewall.SupportMACAddressExceptions.7 + LOGO_ADD = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_PERMIT -l FWPM_LAYER_INBOUND_MAC_FRAME_ETHERNET -mla %LOCAL_MAC% -mra %REMOTE_MAC% -c -v ; + LOGO_DELETE = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_PERMIT -l FWPM_LAYER_INBOUND_MAC_FRAME_ETHERNET -mla %LOCAL_MAC% -mra %REMOTE_MAC% -c -v -r ; + +# Firewall.SupportMACAddressExceptions.8 + LOGO_ADD = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_BLOCK -l FWPM_LAYER_INBOUND_MAC_FRAME_ETHERNET -mla %LOCAL_MAC% -mra %REMOTE_MAC% -c -v ; + LOGO_DELETE = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_BLOCK -l FWPM_LAYER_INBOUND_MAC_FRAME_ETHERNET -mla %LOCAL_MAC% -mra %REMOTE_MAC% -c -v -r ; + +################################### +# Firewall.Support5TupleExceptions.IPAddressExceptions.1 + LOGO_ADD = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_PERMIT -l FWPM_LAYER_ALE_AUTH_CONNECT_V4 -ipla %LOCAL_IP% -ipra %REMOTE_IP% -c -v ; + LOGO_DELETE = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_PERMIT -l FWPM_LAYER_ALE_AUTH_CONNECT_V4 -ipla %LOCAL_IP% -ipra %REMOTE_IP% -c -v -r ; + +# Firewall.Support5TupleExceptions.IPAddressExceptions.2 + LOGO_ADD = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_BLOCK -l FWPM_LAYER_ALE_AUTH_CONNECT_V4 -ipla %LOCAL_IP% -ipra %REMOTE_IP% -c -v ; + LOGO_DELETE = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_BLOCK -l FWPM_LAYER_ALE_AUTH_CONNECT_V4 -ipla %LOCAL_IP% -ipra %REMOTE_IP% -c -v -r ; + +# Firewall.Support5TupleExceptions.IPAddressExceptions.3 + LOGO_ADD = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_PERMIT -l FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 -ipla %LOCAL_IP% -ipra %REMOTE_IP% -c -v ; + LOGO_DELETE = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_PERMIT -l FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 -ipla %LOCAL_IP% -ipra %REMOTE_IP% -c -v -r ; + +# Firewall.Support5TupleExceptions.IPAddressExceptions.4 + LOGO_ADD = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_BLOCK -l FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 -ipla %LOCAL_IP% -ipra %REMOTE_IP% -c -v ; + LOGO_DELETE = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_BLOCK -l FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 -ipla %LOCAL_IP% -ipra %REMOTE_IP% -c -v -r ; + +# Firewall.Support5TupleExceptions.IPAddressExceptions.5 + LOGO_ADD = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_PERMIT -l FWPM_LAYER_ALE_AUTH_CONNECT_V6 -ipla %LOCAL_IP% -ipra %REMOTE_IP% -c -v ; + LOGO_DELETE = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_PERMIT -l FWPM_LAYER_ALE_AUTH_CONNECT_V6 -ipla %LOCAL_IP% -ipra %REMOTE_IP% -c -v -r ; + +# Firewall.Support5TupleExceptions.IPAddressExceptions.6 + LOGO_ADD = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_BLOCK -l FWPM_LAYER_ALE_AUTH_CONNECT_V6 -ipla %LOCAL_IP% -ipra %REMOTE_IP% -c -v ; + LOGO_DELETE = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_BLOCK -l FWPM_LAYER_ALE_AUTH_CONNECT_V6 -ipla %LOCAL_IP% -ipra %REMOTE_IP% -c -v -r ; + +# Firewall.Support5TupleExceptions.IPAddressExceptions.7 + LOGO_ADD = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_PERMIT -l FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 -ipla %LOCAL_IP% -ipra %REMOTE_IP% -c -v ; + LOGO_DELETE = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_PERMIT -l FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 -ipla %LOCAL_IP% -ipra %REMOTE_IP% -c -v -r ; + +# Firewall.Support5TupleExceptions.IPAddressExceptions.8 + LOGO_ADD = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_BLOCK -l FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 -ipla %LOCAL_IP% -ipra %REMOTE_IP% -c -v ; + LOGO_DELETE = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_BLOCK -l FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 -ipla %LOCAL_IP% -ipra %REMOTE_IP% -c -v -r ; + +################################### +# Firewall.Support5TupleExceptions.PortExceptions.1 + LOGO_ADD = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_PERMIT -l FWPM_LAYER_ALE_AUTH_CONNECT_V4 -ipp %PROTOCOL% -iplp %LOCAL_PORT% -iprp %REMOTE_PORT% -c -v ; + LOGO_DELETE = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_PERMIT -l FWPM_LAYER_ALE_AUTH_CONNECT_V4 -ipp %PROTOCOL% -iplp %LOCAL_PORT% -iprp %REMOTE_PORT% -c -v -r ; + +# Firewall.Support5TupleExceptions.PortExceptions.2 + LOGO_ADD = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_BLOCK -l FWPM_LAYER_ALE_AUTH_CONNECT_V4 -ipp %PROTOCOL% -iplp %LOCAL_PORT% -iprp %REMOTE_PORT% -c -v ; + LOGO_DELETE = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_BLOCK -l FWPM_LAYER_ALE_AUTH_CONNECT_V4 -ipp %PROTOCOL% -iplp %LOCAL_PORT% -iprp %REMOTE_PORT% -c -v -r ; + +# Firewall.Support5TupleExceptions.PortExceptions.3 + LOGO_ADD = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_PERMIT -l FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 -ipp %PROTOCOL% -iplp %LOCAL_PORT% -iprp %REMOTE_PORT% -c -v ; + LOGO_DELETE = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_PERMIT -l FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 -ipp %PROTOCOL% -iplp %LOCAL_PORT% -iprp %REMOTE_PORT% -c -v -r ; + +# Firewall.Support5TupleExceptions.PortExceptions.4 + LOGO_ADD = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_BLOCK -l FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 -ipp %PROTOCOL% -iplp %LOCAL_PORT% -iprp %REMOTE_PORT% -c -v ; + LOGO_DELETE = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_BLOCK -l FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 -ipp %PROTOCOL% -iplp %LOCAL_PORT% -iprp %REMOTE_PORT% -c -v -r ; + +# Firewall.Support5TupleExceptions.PortExceptions.5 + LOGO_ADD = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_PERMIT -l FWPM_LAYER_ALE_AUTH_CONNECT_V6 -ipp %PROTOCOL% -iplp %LOCAL_PORT% -iprp %REMOTE_PORT% -c -v ; + LOGO_DELETE = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_PERMIT -l FWPM_LAYER_ALE_AUTH_CONNECT_V6 -ipp %PROTOCOL% -iplp %LOCAL_PORT% -iprp %REMOTE_PORT% -c -v -r ; + +# Firewall.Support5TupleExceptions.PortExceptions.6 + LOGO_ADD = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_BLOCK -l FWPM_LAYER_ALE_AUTH_CONNECT_V6 -ipp %PROTOCOL% -iplp %LOCAL_PORT% -iprp %REMOTE_PORT% -c -v ; + LOGO_DELETE = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_BLOCK -l FWPM_LAYER_ALE_AUTH_CONNECT_V6 -ipp %PROTOCOL% -iplp %LOCAL_PORT% -iprp %REMOTE_PORT% -c -v -r ; + +# Firewall.Support5TupleExceptions.PortExceptions.7 + LOGO_ADD = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_PERMIT -l FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 -ipp %PROTOCOL% -iplp %LOCAL_PORT% -iprp %REMOTE_PORT% -c -v ; + LOGO_DELETE = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_PERMIT -l FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 -ipp %PROTOCOL% -iplp %LOCAL_PORT% -iprp %REMOTE_PORT% -c -v -r ; + +# Firewall.Support5TupleExceptions.PortExceptions.8 + LOGO_ADD = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_BLOCK -l FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 -ipp %PROTOCOL% -iplp %LOCAL_PORT% -iprp %REMOTE_PORT% -c -v ; + LOGO_DELETE = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_BLOCK -l FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 -ipp %PROTOCOL% -iplp %LOCAL_PORT% -iprp %REMOTE_PORT% -c -v -r ; + +################################### +# Firewall.Support5TupleExceptions.ProtocolExceptions.1 + LOGO_ADD = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_PERMIT -l FWPM_LAYER_ALE_AUTH_CONNECT_V4 -ipp %PROTOCOL% -c -v ; + LOGO_DELETE = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_PERMIT -l FWPM_LAYER_ALE_AUTH_CONNECT_V4 -ipp %PROTOCOL% -c -v -r ; + +# Firewall.Support5TupleExceptions.ProtocolExceptions.2 + LOGO_ADD = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_BLOCK -l FWPM_LAYER_ALE_AUTH_CONNECT_V4 -ipp %PROTOCOL% -c -v ; + LOGO_DELETE = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_BLOCK -l FWPM_LAYER_ALE_AUTH_CONNECT_V4 -ipp %PROTOCOL% -c -v -r ; + +# Firewall.Support5TupleExceptions.ProtocolExceptions.3 + LOGO_ADD = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_PERMIT -l FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 -ipp %PROTOCOL% -c -v ; + LOGO_DELETE = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_PERMIT -l FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 -ipp %PROTOCOL% -c -v -r ; + +# Firewall.Support5TupleExceptions.ProtocolExceptions.4 + LOGO_ADD = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_BLOCK -l FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 -ipp %PROTOCOL% -c -v ; + LOGO_DELETE = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_BLOCK -l FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 -ipp %PROTOCOL% -c -v -r ; + +# Firewall.Support5TupleExceptions.ProtocolExceptions.5 + LOGO_ADD = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_PERMIT -l FWPM_LAYER_ALE_AUTH_CONNECT_V6 -ipp %PROTOCOL% -c -v ; + LOGO_DELETE = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_PERMIT -l FWPM_LAYER_ALE_AUTH_CONNECT_V6 -ipp %PROTOCOL% -c -v -r ; + +# Firewall.Support5TupleExceptions.ProtocolExceptions.6 + LOGO_ADD = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_BLOCK -l FWPM_LAYER_ALE_AUTH_CONNECT_V6 -ipp %PROTOCOL% -c -v ; + LOGO_DELETE = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_BLOCK -l FWPM_LAYER_ALE_AUTH_CONNECT_V6 -ipp %PROTOCOL% -c -v -r ; + +# Firewall.Support5TupleExceptions.ProtocolExceptions.7 + LOGO_ADD = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_PERMIT -l FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 -ipp %PROTOCOL% -c -v ; + LOGO_DELETE = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_PERMIT -l FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 -ipp %PROTOCOL% -c -v -r ; + +# Firewall.Support5TupleExceptions.ProtocolExceptions.8 + LOGO_ADD = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_BLOCK -l FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 -ipp %PROTOCOL% -c -v ; + LOGO_DELETE = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_BLOCK -l FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 -ipp %PROTOCOL% -c -v -r ; + +################################### +# Firewall.Support5TupleExceptions.ICMPExceptions.1 + LOGO_ADD = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_PERMIT -l FWPM_LAYER_OUTBOUND_TRANSPORT_V4 -ipp %PROTOCOL% -icmpt %ICMP_TYPE% -icmpc %ICMP_CODE% -v ; + LOGO_DELETE = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_PERMIT -l FWPM_LAYER_OUTBOUND_TRANSPORT_V4 -ipp %PROTOCOL% -icmpt %ICMP_TYPE% -icmpc %ICMP_CODE% -v -r ; + +# Firewall.Support5TupleExceptions.ICMPExceptions.2 + LOGO_ADD = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_BLOCK -l FWPM_LAYER_OUTBOUND_TRANSPORT_V4 -ipp %PROTOCOL% -icmpt %ICMP_TYPE% -icmpc %ICMP_CODE% -v ; + LOGO_DELETE = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_BLOCK -l FWPM_LAYER_OUTBOUND_TRANSPORT_V4 -ipp %PROTOCOL% -icmpt %ICMP_TYPE% -icmpc %ICMP_CODE% -v -r ; + +# Firewall.Support5TupleExceptions.ICMPExceptions.3 + LOGO_ADD = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_PERMIT -l FWPM_LAYER_INBOUND_TRANSPORT_V4 -ipp %PROTOCOL% -icmpt %ICMP_TYPE% -icmpc %ICMP_CODE% -v ; + LOGO_DELETE = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_PERMIT -l FWPM_LAYER_INBOUND_TRANSPORT_V4 -ipp %PROTOCOL% -icmpt %ICMP_TYPE% -icmpc %ICMP_CODE% -v -r ; + +# Firewall.Support5TupleExceptions.ICMPExceptions.4 + LOGO_ADD = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_BLOCK -l FWPM_LAYER_INBOUND_TRANSPORT_V4 -ipp %PROTOCOL% -icmpt %ICMP_TYPE% -icmpc %ICMP_CODE% -v ; + LOGO_DELETE = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_BLOCK -l FWPM_LAYER_INBOUND_TRANSPORT_V4 -ipp %PROTOCOL% -icmpt %ICMP_TYPE% -icmpc %ICMP_CODE% -v -r ; + +# Firewall.Support5TupleExceptions.ICMPExceptions.5 + LOGO_ADD = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_PERMIT -l FWPM_LAYER_OUTBOUND_TRANSPORT_V6 -ipp %PROTOCOL% -icmpt %ICMP_TYPE% -icmpc %ICMP_CODE% -v ; + LOGO_DELETE = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_PERMIT -l FWPM_LAYER_OUTBOUND_TRANSPORT_V6 -ipp %PROTOCOL% -icmpt %ICMP_TYPE% -icmpc %ICMP_CODE% -v -r ; + +# Firewall.Support5TupleExceptions.ICMPExceptions.6 + LOGO_ADD = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_BLOCK -l FWPM_LAYER_OUTBOUND_TRANSPORT_V6 -ipp %PROTOCOL% -icmpt %ICMP_TYPE% -icmpc %ICMP_CODE% -v ; + LOGO_DELETE = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_BLOCK -l FWPM_LAYER_OUTBOUND_TRANSPORT_V6 -ipp %PROTOCOL% -icmpt %ICMP_TYPE% -icmpc %ICMP_CODE% -v -r ; + +# Firewall.Support5TupleExceptions.ICMPExceptions.7 + LOGO_ADD = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_PERMIT -l FWPM_LAYER_INBOUND_TRANSPORT_V6 -ipp %PROTOCOL% -icmpt %ICMP_TYPE% -icmpc %ICMP_CODE% -v ; + LOGO_DELETE = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_PERMIT -l FWPM_LAYER_INBOUND_TRANSPORT_V6 -ipp %PROTOCOL% -icmpt %ICMP_TYPE% -icmpc %ICMP_CODE% -v -r ; + +# Firewall.Support5TupleExceptions.ICMPExceptions.8 + LOGO_ADD = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_BLOCK -l FWPM_LAYER_INBOUND_TRANSPORT_V6 -ipp %PROTOCOL% -icmpt %ICMP_TYPE% -icmpc %ICMP_CODE% -v ; + LOGO_DELETE = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_BLOCK -l FWPM_LAYER_INBOUND_TRANSPORT_V6 -ipp %PROTOCOL% -icmpt %ICMP_TYPE% -icmpc %ICMP_CODE% -v -r ; + +################################### +# Firewall.SupportApplicationExceptions.1 + LOGO_ADD = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_PERMIT -l FWPM_LAYER_ALE_AUTH_CONNECT_V4 -aaid %APPLICATION% -c -v ; + LOGO_DELETE = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_PERMIT -l FWPM_LAYER_ALE_AUTH_CONNECT_V4 -aaid %APPLICATION% -c -v -r ; + +# Firewall.SupportApplicationExceptions.2 + LOGO_ADD = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_BLOCK -l FWPM_LAYER_ALE_AUTH_CONNECT_V4 -aaid %APPLICATION% -c -v ; + LOGO_DELETE = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_BLOCK -l FWPM_LAYER_ALE_AUTH_CONNECT_V4 -aaid %APPLICATION% -c -v -r ; + +# Firewall.SupportApplicationExceptions.3 + LOGO_ADD = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_PERMIT -l FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 -aaid %APPLICATION% -c -v ; + LOGO_DELETE = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_PERMIT -l FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 -aaid %APPLICATION% -c -v -r ; + +# Firewall.SupportApplicationExceptions.4 + LOGO_ADD = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_BLOCK -l FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 -aaid %APPLICATION% -c -v ; + LOGO_DELETE = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_BLOCK -l FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 -aaid %APPLICATION% -c -v -r ; + +# Firewall.SupportApplicationExceptions.5 + LOGO_ADD = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_PERMIT -l FWPM_LAYER_ALE_AUTH_CONNECT_V6 -aaid %APPLICATION% -c -v ; + LOGO_DELETE = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_PERMIT -l FWPM_LAYER_ALE_AUTH_CONNECT_V6 -aaid %APPLICATION% -c -v -r ; + +# Firewall.SupportApplicationExceptions.6 + LOGO_ADD = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_BLOCK -l FWPM_LAYER_ALE_AUTH_CONNECT_V6 -aaid %APPLICATION% -c -v ; + LOGO_DELETE = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_BLOCK -l FWPM_LAYER_ALE_AUTH_CONNECT_V6 -aaid %APPLICATION% -c -v -r ; + +# Firewall.SupportApplicationExceptions.7 + LOGO_ADD = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_PERMIT -l FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 -aaid %APPLICATION% -c -v ; + LOGO_DELETE = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_PERMIT -l FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 -aaid %APPLICATION% -c -v -r ; + +# Firewall.SupportApplicationExceptions.8 + LOGO_ADD = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_BLOCK -l FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 -aaid %APPLICATION% -c -v ; + LOGO_DELETE = %WinDir%\System32\WFPSampler.exe -s BASIC_ACTION_BLOCK -l FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 -aaid %APPLICATION% -c -v -r ; + diff --git a/network/trans/WFPSampler/HCK/WFPLogo_WFPSampler.Info b/network/trans/WFPSampler/HCK/WFPLogo_WFPSampler.Info new file mode 100644 index 000000000..3e43e6081 --- /dev/null +++ b/network/trans/WFPSampler/HCK/WFPLogo_WFPSampler.Info @@ -0,0 +1,301 @@ +####################################### +# # +# Provider Information # +# # +####################################### + + CompanyName = "Microsoft Corporation"; + ProductName = "WFPSampler"; + DriverName = "C:\Windows\System32\Drivers\WFPSamplerCalloutDriver.sys"; + +####################################### +# # +# Enable Driver Verifier # +# # +# 0 to not attach verifier # +# 1 to attach verifier # +# # +####################################### + + EnableDriverVerifier = 1; + +####################################### +# # +# Use the answer file to script the # +# addition and removal of the filters # +# # +# Use only if the answer file has # +# been modified for your needs. # +# # +# 0 if not implemented # +# 1 if implemented # +# # +####################################### + + UseAnswerFile = 1; + +####################################### +# # +# Requirement Information # +# # +# 0 if not implemented # +# 1 if implemented # +# # +####################################### + + CalloutDriver = 1; + + IsAFirewall = 1; + + LayeredOnMicrosoftWindowsFirewall = 0; + + DoesMACFiltering = 1; + + DoesVSwitchFiltering = 1; + + DoesPacketInjection = 1; + + DoesStreamInjection = 1; + + DoesConnectionProxying = 1; + +####################################### +# # +# Attestations # +# # +# 0 if FALSE # +# 1 if TRUE # +# # +####################################### + +# NETWORK-0XXX Filter.Driver.WindowsFilteringPlatform.ArchitecturalDesign.AppContainers.SupportModernApplications +# WFP-based products must not block App Container apps operating within their declared network intentions by default, and should only do so when following specific user/admin intention or protecting the system against a specific threat. + + SupportModernApplications = 1; + +# NETWORK-0270 Filter.Driver.WindowsFilteringPlatform.ArchitecturalDesign.CleanUninstall +# WFP-based products stop cleanly and clean up all running state upon uninstall. + + CleanUninstall = 1; + +# NETWORK-0XXX Filter.Driver.WindowsFilteringPlatform.ArchitecturalDesign.ConnectionProxying.NoDeadlocks +# WFP-based products which redirect or proxy at redirect layers (connect redirect), must use the new proxying API so that other WFP-based products can determine that the connection has been proxied. + + NoProxyDeadlocks = 1; + +# NETWORK-0262 Filter.Driver.WindowsFilteringPlatform.ArchitecturalDesign.FwpmProviders.MaintainIdentifying +# WFP-based products must create and maintain at least 1 identifying FWPM_PROVIDER provider object. + + IdentifyingProvider = 1; + +# NETWORK-0265 Filter.Driver.WindowsFilteringPlatform.ArchitecturalDesign.FwpmProviders.AssociateWithObjects +# WFP-based products must associate all of their Provider Contexts, Filters, Sublayers, and Callouts with their corresponding identifying provider object. + + AssociateProvider = 1; + +# NETWORK-0263 Filter.Driver.WindowsFilteringPlatform.ArchitecturalDesign.FwpmFilters.MaintainOneTerminating +# WFP-based products must create and maintain at least 1 terminating FWPM_FILTER object. + + TerminatingFilter = 1; + +# NETWORK-0264 Filter.Driver.WindowsFilteringPlatform.ArchitecturalDesign.FwpmSublayers.UseOwnOrBuiltIn +# WFP-based products must use only their own sublayer or one of the built-in sublayers + + UseOwnSubLayer = 1; + +# NETWORK-0288 Filter.Driver.WindowsFilteringPlatform.ArchitecturalDesign.NetworkDiagnosticsFramework.HelperClass +# WFP-based products must include a Network Diagnostics Framework (NDF) helper class that extends the Filtering Platform helper class (FPHC) + + MaintainHelperClass = 1; + +# NETWORK-0269 Filter.Driver.WindowsFilteringPlatform.ArchitecturalDesign.NoAccessViolations +# WFP-based products must not be the resulting cause of any Access Violation under high load or during driver load/unload. + + NoAVs = 1; + +# NETWORK-0261 Filter.Driver.WindowsFilteringPlatform.ArchitecturalDesign.NoTamperingWith3rdPartyObjects +# WFP-based products must not attempt to remove or alter another WFP-based product’s WFP objects and built-in objects. + + NonTampered3rdPartyObjects = 1; + +# NETWORK-0287 Filter.Driver.WindowsFilteringPlatform.ArchitecturalDesign.PacketInjection.NoDeadlocks +# WFP-based products must not continually modify network packets that have already been modified and re-injected, so as to create potential deadlocks. + + NoPacketInjectionDeadlocks = 1; + +# NETWORK-0267 Filter.Driver.WindowsFilteringPlatform.ArchitecturalDesign.StreamInjection.NoStreamStarvation +# WFP-based product callouts at FWPM_LAYER_STREAM must not starve the data throughput. + + NoStreamStarvation = 1; + +# NETWORK-0268 Filter.Driver.WindowsFilteringPlatform.ArchitecturalDesign.SupportPowerManagedStates +# WFP-based products must ensure network connectivity upon recovering from power managed states. + + SupportPowerManagement = 1; + +# NETWORK-0260 Filter.Driver.WindowsFilteringPlatform.ArchitecturalDesign.WFPObjectACLs +# WFP-based products must ACL all of their objects in a way that any other WFP-based product can at least enumerate those objects using the corresponding WFP enumeration APIs. + + WFPObjectEnumAndACLs = 1; + +# NETWORK-0XXX Filter.Driver.WindowsFilteringPlatform.ArchitecturalDesign.WinSock +# Kernel Mode Filter Drivers are architected to maximize the reliability and functionality of Windows Sockets, as well as interact accurately with the core components of the operating system. + + MaxWinSock = 1; + +# NETWORK-0271 Filter.Driver.WindowsFilteringPlatform.Firewall.DisableWindowsFirewallProperly +# Host firewalls must disable Windows Firewall using only the supported method. + + ProperlyDisableWindowsFirewall = 1; + +# NETWORK-0266 Filter.Driver.WindowsFilteringPlatform.Firewall.NotOnlyPermitAllFilters +# Host firewalls must not have only “permit_all” filters. + + NoPermitBlockAll = 1; + +# NETWORK-0XXX Filter.Driver.WindowsFilteringPlatform.Firewall.Support5TupleExceptions +# All host based firewalls must be able to Block/Allow by 5-Tuple Parts (including Port (ICMP Type and Code, UDP and TCP) IP Address, Protocol (e.g. UDP/TCP/ICMP) + + SupportTupleExceptions = 1; + +# NETWORK-0253 Filter.Driver.WindowsFilteringPlatform.Firewall.SupportApplicationExceptions +# WFP-based products must support exceptions from corresponding applications. + + SupportAppExceptions = 1; + +# NETWORK-0XXX Filter.Driver.WindowsFilteringPlatform.Firewall.SupportMACAddressExceptions +# All host based firewalls which have filters in L2 (Native/Mac) layers must be able to Block or Allow by Mac Address. + + SupportMACAddressExceptions = 1; + +# NETWORK-0259 Filter.Driver.WindowsFilteringPlatform.Firewall.UseWindowsFilteringPlatform +# Firewalls must comply with Windows Filtering Platform based APIs for filtering network traffic on home user solutions. + + UseWFP = 1; + +# NETWORK-0247 Filter.Driver.WindowsFilteringPlatform.NetworkingFundamentals.SupportARP +# WFP-based products must support support allowing for successful ARP exchanges. + + SupportARP = 1; + + SupportNeighborDiscovery = 1; + +# NETWORK-0245 Filter.Driver.WindowsFilteringPlatform.NetworkingFundamentals.SupportDynamicAddressing +# WFP-based products support allowing for successful DHCP exchanges over both IPv4 and IPv6. + + SupportDHCP = 1; + +# NETWORK-0243 Filter.Driver.WindowsFilteringPlatform.NetworkingFundamentals.SupportIPv4 +# WFP-based products must support IPv4 traffic. + + SupportIPv4 = 1; + +# NETWORK-0244 Filter.Driver.WindowsFilteringPlatform.NetworkingFundamentals.SupportIPv6 +# WFP-based products must support IPv6 traffic. + + SupportIPv6 = 1; + +# NETWORK-0246 Filter.Driver.WindowsFilteringPlatform.NetworkingFundamentals.SupportNameResolution +# WFP-based products must support allowing for successful DNS client queries. + + SupportDNS = 1; + +# NETWORK-0249 Filter.Driver.WindowsFilteringPlatform.Scenarios.Support6To4 +# WFP-based products must support 6to4. + + Support6To4 = 1; + +# NETWORK-0257 Filter.Driver.WindowsFilteringPlatform.Scenarios.SupportAutomaticUpdates +# WFP-based products must support Automatic Updates in Windows. + + SupportAutomaticUpdates = 1; + +# NETWORK-0251 Filter.Driver.WindowsFilteringPlatform.Scenarios.SupportBasicWebsiteBrowsing +# WFP-based products must support basic internet browsing experiences. + + SupportBasicWebsiteBrowsing = 1; + +# NETWORK-0252 Filter.Driver.WindowsFilteringPlatform.Scenarios.SupportFileAndPrinterSharing +# WFP-based products must support file and printer sharing. + + SupportFileAndPrinterSharing = 1; + +# NETWORK-0250 Filter.Driver.WindowsFilteringPlatform.Scenarios.SupportICMPErrorMesages +# WFP-based products must support ICMP error messages and discovery functions. + + SupportICMPErrorMesages = 1; + +# NETWORK-0256 Filter.Driver.WindowsFilteringPlatform.Scenarios.SupportInternetStreaming +# WFP-based products must support Internet streaming and Media sharing for media player network sharing services. + + SupportInternetStreaming = 1; + +# NETWORK-0285 Filter.Driver.WindowsFilteringPlatform.Scenarios.SupportMediaExtenderStreaming +# WFP_based products must support media streaming scenarios based on extender technologies. + + SupportMediaExtenderStreaming = 1; + +# NETWORK-0XXX Filter.Driver.WindowsFilteringPlatform.Scenarios.SupportMobileBroadBand +# WFP-based products must allow mobile broadband devices that are compliant with Windows mobile broadband driver model to function correctly. + + SupportMobileBroadBand = 1; + +# NETWORK-0284 Filter.Driver.WindowsFilteringPlatform.Scenarios.SupportPeerNameResolution +# WFP-based products must support Peer Name Resolution Protocol and the Peer-to-Peer Grouping Protocol. + + SupportPeerNameResolution = 1; + +# NETWORK-255 Filter.Driver.WindowsFilteringPlatform.Scenarios.SupportRemoteAssistance +# WFP-based products must support Remote Assistance scenarios. + + SupportRemoteAssistance = 1; + +# NETWORK-0254 Filter.Driver.WindowsFilteringPlatform.Scenarios.SupportRemoteDesktop +# WFP-based products must support Remote Desktop. + + SupportRemoteDesktop = 1; + +# NETWORK-0248 Filter.Driver.WindowsFilteringPlatform.Scenarios.SupportTeredo +# WFP-based products must support Teredo. + + SupportTeredo = 1; + +# NETWORK-0258 Filter.Driver.WindowsFilteringPlatform.Scenarios.SupportVirtualPrivateNetworking +# WFP-based products must support VPN scenarios in Windows. + + SupportVirtualPrivateNetworking = 1; + +# NETWORK-0XXX Filter.Driver.WindowsFilteringPlatform.Scenarios.vSwitch.InteropWithOtherExtensions +# WFP-based products must not block traffic from another vSwitch extension (WFP or LWF) by default, and should only do so when following specific user/admin intention or protecting the system against a specific threat. + + InteropWithOtherExtensions = 1; + +# NETWORK-0XXX Filter.Driver.WindowsFilteringPlatform.Scenarios.vSwitch.NoEgressModification +# WFP-based products that operate in the vSwitch must not modify packets on the Egress path of the vSwitch. + + NoEgressModification = 1; + +# NETWORK-0XXX Filter.Driver.WindowsFilteringPlatform.Scenarios.vSwitch.SupportLiveMigration +# WFP-based products that operate in the vSwitch must present a minimal MOF for Live Migration. + + SupportLiveMigration = 1; + +# NETWORK-0XXX Filter.Driver.WindowsFilteringPlatform.Scenarios.vSwitch.SupportRemoval +# WFP-based products that operate in the vSwitch must present be allowed to be removed when the admin disabled WFP for the vSwitch instance. + + SupportRemoval = 1; + +# NETWORK-0XXX Filter.Driver.WindowsFilteringPlatform.Scenarios.vSwitch.SupportReordering +# WFP-based products that operate in the vSwitch must respond to WFP vmSwitch reorder events. + + SupportReordering = 1; + +#Attestation +# I have performed interop testing with my product and the WFPSampler in various supported scenarios + + InteropWithWFPSampler = 1; + +# Attestation +# I hereby confirm that the information included within is accurate to the best of my knowledge in regards to the Product mentioned. + + RunBy = "Dusty Harper"; \ No newline at end of file diff --git a/network/trans/WFPSampler/WFPSampler.sln b/network/trans/WFPSampler/WFPSampler.sln new file mode 100644 index 000000000..8e0094edf --- /dev/null +++ b/network/trans/WFPSampler/WFPSampler.sln @@ -0,0 +1,94 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Lib", "Lib", "{6CEF8C70-B826-4826-A0F3-84D28B1D1C47}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Syslib", "Syslib", "{922391A4-38BC-4EEE-B2A5-10B9E1F73C95}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sys", "Sys", "{D11B19F4-F0DC-40E6-BF4D-B64C3F118A0D}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Svc", "Svc", "{95B605C0-B839-4E59-A860-F9703D1C5466}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Exe", "Exe", "{30FEBC98-C9BE-4329-9B06-666F2A0FC94C}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WFPSampler", "lib\WFPSampler.vcxproj", "{9049BF3E-29C4-4934-90C3-5E68C79036B4}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WFPSampler", "syslib\WFPSampler.vcxproj", "{512C5A72-2415-45CA-A1B2-851C97781F9E}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WFPSamplerCalloutDriver", "sys\WFPSamplerCalloutDriver.vcxproj", "{6F3FAA0B-ABE5-47D5-9110-DB54E8FF0EE3}" + ProjectSection(ProjectDependencies) = postProject + {512C5A72-2415-45CA-A1B2-851C97781F9E} = {512C5A72-2415-45CA-A1B2-851C97781F9E} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WFPSamplerService", "svc\WFPSamplerService.vcxproj", "{312373E3-9E58-48CC-A61C-73F3A3B0179B}" + ProjectSection(ProjectDependencies) = postProject + {9049BF3E-29C4-4934-90C3-5E68C79036B4} = {9049BF3E-29C4-4934-90C3-5E68C79036B4} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WFPSampler", "exe\WFPSampler.vcxproj", "{649E7C26-3C81-45CB-98E1-D40349D7B3F2}" + ProjectSection(ProjectDependencies) = postProject + {9049BF3E-29C4-4934-90C3-5E68C79036B4} = {9049BF3E-29C4-4934-90C3-5E68C79036B4} + 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 + {9049BF3E-29C4-4934-90C3-5E68C79036B4}.Debug|Win32.ActiveCfg = Debug|Win32 + {9049BF3E-29C4-4934-90C3-5E68C79036B4}.Debug|Win32.Build.0 = Debug|Win32 + {9049BF3E-29C4-4934-90C3-5E68C79036B4}.Release|Win32.ActiveCfg = Release|Win32 + {9049BF3E-29C4-4934-90C3-5E68C79036B4}.Release|Win32.Build.0 = Release|Win32 + {9049BF3E-29C4-4934-90C3-5E68C79036B4}.Debug|x64.ActiveCfg = Debug|x64 + {9049BF3E-29C4-4934-90C3-5E68C79036B4}.Debug|x64.Build.0 = Debug|x64 + {9049BF3E-29C4-4934-90C3-5E68C79036B4}.Release|x64.ActiveCfg = Release|x64 + {9049BF3E-29C4-4934-90C3-5E68C79036B4}.Release|x64.Build.0 = Release|x64 + {512C5A72-2415-45CA-A1B2-851C97781F9E}.Debug|Win32.ActiveCfg = Debug|Win32 + {512C5A72-2415-45CA-A1B2-851C97781F9E}.Debug|Win32.Build.0 = Debug|Win32 + {512C5A72-2415-45CA-A1B2-851C97781F9E}.Release|Win32.ActiveCfg = Release|Win32 + {512C5A72-2415-45CA-A1B2-851C97781F9E}.Release|Win32.Build.0 = Release|Win32 + {512C5A72-2415-45CA-A1B2-851C97781F9E}.Debug|x64.ActiveCfg = Debug|x64 + {512C5A72-2415-45CA-A1B2-851C97781F9E}.Debug|x64.Build.0 = Debug|x64 + {512C5A72-2415-45CA-A1B2-851C97781F9E}.Release|x64.ActiveCfg = Release|x64 + {512C5A72-2415-45CA-A1B2-851C97781F9E}.Release|x64.Build.0 = Release|x64 + {6F3FAA0B-ABE5-47D5-9110-DB54E8FF0EE3}.Debug|Win32.ActiveCfg = Debug|Win32 + {6F3FAA0B-ABE5-47D5-9110-DB54E8FF0EE3}.Debug|Win32.Build.0 = Debug|Win32 + {6F3FAA0B-ABE5-47D5-9110-DB54E8FF0EE3}.Release|Win32.ActiveCfg = Release|Win32 + {6F3FAA0B-ABE5-47D5-9110-DB54E8FF0EE3}.Release|Win32.Build.0 = Release|Win32 + {6F3FAA0B-ABE5-47D5-9110-DB54E8FF0EE3}.Debug|x64.ActiveCfg = Debug|x64 + {6F3FAA0B-ABE5-47D5-9110-DB54E8FF0EE3}.Debug|x64.Build.0 = Debug|x64 + {6F3FAA0B-ABE5-47D5-9110-DB54E8FF0EE3}.Release|x64.ActiveCfg = Release|x64 + {6F3FAA0B-ABE5-47D5-9110-DB54E8FF0EE3}.Release|x64.Build.0 = Release|x64 + {312373E3-9E58-48CC-A61C-73F3A3B0179B}.Debug|Win32.ActiveCfg = Debug|Win32 + {312373E3-9E58-48CC-A61C-73F3A3B0179B}.Debug|Win32.Build.0 = Debug|Win32 + {312373E3-9E58-48CC-A61C-73F3A3B0179B}.Release|Win32.ActiveCfg = Release|Win32 + {312373E3-9E58-48CC-A61C-73F3A3B0179B}.Release|Win32.Build.0 = Release|Win32 + {312373E3-9E58-48CC-A61C-73F3A3B0179B}.Debug|x64.ActiveCfg = Debug|x64 + {312373E3-9E58-48CC-A61C-73F3A3B0179B}.Debug|x64.Build.0 = Debug|x64 + {312373E3-9E58-48CC-A61C-73F3A3B0179B}.Release|x64.ActiveCfg = Release|x64 + {312373E3-9E58-48CC-A61C-73F3A3B0179B}.Release|x64.Build.0 = Release|x64 + {649E7C26-3C81-45CB-98E1-D40349D7B3F2}.Debug|Win32.ActiveCfg = Debug|Win32 + {649E7C26-3C81-45CB-98E1-D40349D7B3F2}.Debug|Win32.Build.0 = Debug|Win32 + {649E7C26-3C81-45CB-98E1-D40349D7B3F2}.Release|Win32.ActiveCfg = Release|Win32 + {649E7C26-3C81-45CB-98E1-D40349D7B3F2}.Release|Win32.Build.0 = Release|Win32 + {649E7C26-3C81-45CB-98E1-D40349D7B3F2}.Debug|x64.ActiveCfg = Debug|x64 + {649E7C26-3C81-45CB-98E1-D40349D7B3F2}.Debug|x64.Build.0 = Debug|x64 + {649E7C26-3C81-45CB-98E1-D40349D7B3F2}.Release|x64.ActiveCfg = Release|x64 + {649E7C26-3C81-45CB-98E1-D40349D7B3F2}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {9049BF3E-29C4-4934-90C3-5E68C79036B4} = {6CEF8C70-B826-4826-A0F3-84D28B1D1C47} + {512C5A72-2415-45CA-A1B2-851C97781F9E} = {922391A4-38BC-4EEE-B2A5-10B9E1F73C95} + {6F3FAA0B-ABE5-47D5-9110-DB54E8FF0EE3} = {D11B19F4-F0DC-40E6-BF4D-B64C3F118A0D} + {312373E3-9E58-48CC-A61C-73F3A3B0179B} = {95B605C0-B839-4E59-A860-F9703D1C5466} + {649E7C26-3C81-45CB-98E1-D40349D7B3F2} = {30FEBC98-C9BE-4329-9B06-666F2A0FC94C} + EndGlobalSection +EndGlobal diff --git a/network/trans/WFPSampler/docs/ADVANCED_PACKET_INJECTION.mht b/network/trans/WFPSampler/docs/ADVANCED_PACKET_INJECTION.mht new file mode 100644 index 000000000..1529bcbb9 --- /dev/null +++ b/network/trans/WFPSampler/docs/ADVANCED_PACKET_INJECTION.mht @@ -0,0 +1,3126 @@ +MIME-Version: 1.0 +Content-Type: multipart/related; boundary="----=_NextPart_01CEEB82.34D42FC0" + +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_01CEEB82.34D42FC0 +Content-Location: file:///C:/0E5B2E2E/ADVANCED_PACKET_INJECTION.htm +Content-Transfer-Encoding: quoted-printable +Content-Type: text/html; charset="windows-1252" + + + + + + + + + + + + +Basic Packet Injection + + + + + + + + + + +
+ +
+ +

ADVANCED PAC= +KET +INJECTION

+ +
+ +

Overview

+ +

The Advanced Packet Injection scenario will create a n= +ew +packet based off of the original packet and inject it back to the same +layer.  No modification is performe= +d on +the packet, with the following exceptions:

+ +

·         +The packet is loopback and only one of eithe= +r the +source or destination address is that of the software loopback.  In this case, the IP header is modified= + by +changing the software loopback address to that of an address on the local +machine.  This is required due to T= +CPIP’s +stack validation logic.

+ +

·         +The packet is received at an inbound transpo= +rt +layer, and had been IPsec secured. In this case, it is necessary to reconst= +ruct +the IP header as IPsec processing does not remove the original ESP or AH +information from it.

+ +

All filters added sit in WFPSampl= +er's +sublayer (which is weighted just below IPsec's sublayer), unless otherwise +specified using the -sl +<SUBLAYER> command line option.  +All filters are associated with WFPSampler's +provider.

+ +

The following diagram shows how the code flows for this +callout:

+ +


+Figure A. Code flow for Advanced Packet Injection Scenario= +

+ +

When traffic matches a filter at the specified layer, = +ClassifyAdvancedPacketInjection() is invoked by the Filtering +Engine.  This function validates th= +at we +can perform the injection by looking at the pClassifyO= +ut +rights.  It will then create the +INJECTION_DATA which consists of the injectionHandle +and the injectionState.  If the injectionSt= +ate +indicates that we haven’t injected this packet before, then the injection +method is determined (default is asynchronous), and the appropriate triggerFn is called.= +   +At this point, the original packet will be blocked.

+ +

If the injection method is synchronous (inline), TriggerAdvancedPacketInjectionInline() is invoked.  This function creates the CLASSIFY_DATA, +which consists of the data that was passed into the cl= +assifyFn.  Depending on which layer the injection = +is +happening, the appropriate performFn is called.= +

+ +

If the injection method is asynchronous (out of band),= + TriggerAdvancedPacketInjectionOutOfBand() is invoked.  This function creates the CLASSIFY_DATA= + which +consists of copies and references of the data that was passed into the classifyFn.  = +Based on +the queuing method, the appropriate queueFn (de= +fault +is DeferredProcedureCalls) is invoked.

+ +

Regardless of which queueFn is +used, each will call the appropriate performFn = +based +on the layer the injection is happening.

+ +

Each of the performFns are tailored to inject for their respective layers.  Each will get the required data for its +specific injectionFn.  +Depending on the layer, the offsets are adjusted on the original so = +the +whole packet is available.  Once the +offsets are adjusted, a new NBL is created, the original data is copied ove= +r, +and the offsets of the original are returned to the original place.  If any of the exceptions noted above are +required, then modification of the IP header occurs. Finally, the appropria= +te injectionFn is invoked.

+ +

Upon successful injection, CompleteAdvancedPack= +etInjection() will be called by the TCPIP +stack.  This function will show the +status of the injected packet.  +Additionally, any memory that was allocated from the functions above, will be freed and any references released.

+ +

Applicable Layers

+ +

v  +FWPM_LAYER_INBOUND_IPPACKET_V4

+ +

v  +FWPM_LAYER_INBOUND_IPPACKET_V6

+ +

v  +FWPM_LAYER_OUTBOUND_IPPACKET_V4

+ +

v  +FWPM_LAYER_OUTBOUND_IPPACKET_V6

+ +

v  +FWPM_LAYER_IPFORWARD_V4

+ +

v  +FWPM_LAYER_IPFORWARD_V6

+ +

v  +FWPM_LAYER_INBOUND_TRANSPORT_V4

+ +

v  +FWPM_LAYER_INBOUND_TRANSPORT_V6

+ +

v  +FWPM_LAYER_OUTBOUND_TRANSPORT_V4

+ +

v  +FWPM_LAYER_OUTBOUND_TRANSPORT_V6

+ +

v  +FWPM_LAYER_DATAGRAM_DATA_V4

+ +

v  +FWPM_LAYER_DATAGRAM_DATA_V6

+ +

v  +FWPM_LAYER_INBOUND_ICMP_ERROR_V4

+ +

v  +FWPM_LAYER_INBOUND_ICMP_ERROR_V6

+ +

v  +FWPM_LAYER_OUTBOUND_ICMP_ERROR_V4

+ +

v  +FWPM_LAYER_OUTBOUND_ICMP_ERROR_V6

+ +

v  +FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4

+ +

v  +FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6

+ +

v  +FWPM_LAYER_ALE_AUTH_CONNECT_V4

+ +

v  +FWPM_LAYER_ALE_AUTH_CONNECT_V6

+ +

v  +FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4

+ +

v  +FWPM_LAYER_ALE_FLOW_ESTABLISHED_V6

+ +

v  +FWPM_LAYER_STREAM_PACKET_V4                                     (Win7+= +)

+ +

v  +FWPM_LAYER_STREAM_PACKET_V6                                     (Win7+= +)

+ +

v  +FWPM_LAYER_INBOUND_MAC_FRAME_ETHERNET       (Win8+)

+ +

v  +FWPM_LAYER_OUTBOUND_MAC_FRAME_ETHERNET   (Win8+)

+ +

v  +FWPM_LAYER_INBOUND_MAC_FRAME_NATIVE            (Win8+)

+ +

v  +FWPM_LAYER_OUTBOUND_MAC_FRAME_NATIVE        (Win8+)

+ +

v  +FWPM_LAYER_INGRESS_VSWITCH_ETHERNET                  (Win8+)

+ +

v  +FWPM_LAYER_EGRESS_VSWITCH_ETHERNET                    (Win8+)

+ +

Command Line Usage

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

Option

+
+

Argument

+
+

Meaning

+
+

-s

+
+

ADVANCED_PACKET_INJECTION

+
+

Implement the ADVANCED_PACKET_INJECTION scenario

+
+

-l

+
+

Applicable Layer

+
+

Layer at which this filter will apply

+
+

-sl

+
+

Applicable subLayer

+
+

SubLayer to associate with the filter= +.  [default is + WFPSAMPLER_SUBLAYER].

+
+

-v

+
+

 

+
+

Make the objects associated with this scenario’s instance dynamic= +

+
+

-b

+
+

 

+
+

Make the objects associated with this scenario’s instance availab= +le + during boot-time

+
+

-in

+
+

 

+
+

Perform the injection synchronously (inline)

+
+

-tdpc

+
+

 

+
+

Use threaded DPCs for asynchronous (out of band) queuing method +

+

-wi

+
+

 

+
+

Use work items for asynchronous (out of band) queuing method

+
+

-r

+
+

 

+
+

Remove objects associated with this scenario instance

+
+

-ab

+
+

Additional Bytes to allocate

+
+

Create a new packet the size of the original + these additional b= +ytes

+
+

-?

+
+

 

+
+

Display help

+
+ +

 

+ +

WFPSampler.E= +xe -s ADVANCED_PACKET_INJECTION +-? +provides help output

+ +

WFPSampler.E= +xe -s ADVANCED_PACKET_INJECTION +-l FWPM_LAYER_INBOUND_IPPACKET_V4 -v“ adds a +dynamic filter (-v) at +FWPM_LAYER_INBOUND_IPPACKET_V4 (-l= +) +which references the appropriate callout.  +This filter will have no conditions, meaning it will act on all traf= +fic +seen at this layer.

+ +

WFPSampler.E= +xe -s ADVANCED_PACKET_INJECTION +-l FWPM_LAYER_INBOUND_IPPACKET_V4 –v -r“ removes (-r) the dynamic filter (-v) at FWPM_LAYER_INBOUND_IPPACKE= +T_V4 (-l) which references the appropri= +ate +callout.

+ +

WFPSampler.E= +xe -s ADVANCED_PACKET_INJECTION +-l FWPM_LAYER_INBOUND_IPPACKET_V4 -ipla 1.0.0.1= + –ipra 1.0.0.254“ adds a +persistent filter at FWPM_LAYER_INBOUND_IPPACKET_V4 (-l) which references the appropriate callout.  This filter will have 2 conditions; +FWPM_CONDITION_IP_LOCAL_ADDRESS (-= +ipla) equals 1.0.0.1, and +FWPM_CONDITION_IP_REMOTE_ADDRESS (= +-ipra) equals 1.0.0.254.

+ +

WFPSampler.E= +xe -s ADVANCED_PACKET_INJECTION +-l FWPM_LAYER_INBOUND_TRANSPORT_V4 -ipla 1.0.0.= +1 –ipra 1.0.0.254 –ipp TCP -= +in“ adds a persistent filter at +FWPM_LAYER_INBOUND_IPPACKET_V4  (-l) which references the appropri= +ate +callout.  This filter will have 3 +conditions; FWPM_CONDITION_IP_LOCAL_ADDRESS (-ipla) equals 1.0.0.1, +FWPM_CONDITION_IP_REMOTE_ADDRESS (= +-ipra) equals 1.0.0.254, and FWPM_CONDITION_IP_PRO= +TOCOL +(-ipp<= +/b>) +equals TCP.  The injection will be +performed synchronously (-in).= +

+ +

For a list of conditions applicable to each layer, ref= +er to Filtering +Conditions Available at Each Filtering Layer.

+ +

For a list of command line parameters for configuring = +each +condition, refer to Co= +nditions +for Command Line.

+ +
+ + + + + +------=_NextPart_01CEEB82.34D42FC0 +Content-Location: file:///C:/0E5B2E2E/ADVANCED_PACKET_INJECTION_files/themedata.thmx +Content-Transfer-Encoding: base64 +Content-Type: application/vnd.ms-officetheme + +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 +cmVsc1BLBQYAAAAABQAFAF0BAACtCgAAAAA= + +------=_NextPart_01CEEB82.34D42FC0 +Content-Location: file:///C:/0E5B2E2E/ADVANCED_PACKET_INJECTION_files/colorschememapping.xml +Content-Transfer-Encoding: quoted-printable +Content-Type: text/xml + + + +------=_NextPart_01CEEB82.34D42FC0 +Content-Location: file:///C:/0E5B2E2E/ADVANCED_PACKET_INJECTION_files/image001.emz +Content-Transfer-Encoding: base64 +Content-Type: image/x-emz + +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 + +------=_NextPart_01CEEB82.34D42FC0 +Content-Location: file:///C:/0E5B2E2E/ADVANCED_PACKET_INJECTION_files/image002.png +Content-Transfer-Encoding: base64 +Content-Type: image/png + +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 +yhoAAAAASUVORK5CYIJ= + +------=_NextPart_01CEEB82.34D42FC0 +Content-Location: file:///C:/0E5B2E2E/ADVANCED_PACKET_INJECTION_files/oledata.mso +Content-Transfer-Encoding: base64 +Content-Type: application/x-mso + +0M8R4KGxGuEAAAAAAAAAAAAAAAAAAAAAPgADAP7/CQAGAAAAAAAAAAAAAAABAAAAAQAAAAAAAAAA +EAAA/v///wAAAAD+////AAAAAAAAAAD///////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////9 +/////v///wMAAAAEAAAABQAAAAYAAAAHAAAACAAAAAkAAAAKAAAACwAAAAwAAAANAAAADgAAAA8A +AAAQAAAAEQAAABIAAAATAAAAFAAAABUAAAAWAAAAFwAAABgAAAAZAAAAGgAAABsAAAAcAAAAHQAA +AB4AAAAfAAAAIAAAACEAAAAiAAAAIwAAACQAAAAlAAAAJgAAACcAAAAoAAAAKQAAACoAAAArAAAA +LAAAAC0AAAAuAAAALwAAADAAAAAxAAAAMgAAADMAAAA0AAAANQAAADYAAAA3AAAAOAAAADkAAAA6 +AAAAOwAAAP7///////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////1IA +bwBvAHQAIABFAG4AdAByAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAWAAUA//////////8BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGB+DEPF684B +/v///wAAAAAAAAAAXwAxADQANAA3ADAANgA5ADkAMgA2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAABgAAgH///////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAACAAAA8HIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///////////////wAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////// +////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANwA +AHic7F0HXBTH95/dKxz1CtxRBO8QQVSUcqBi4Q4OFRXlRM4SoxQFsYIISYyFU2yJGsBuQgL+xGgs +WBKMSVRI1Jj8jAJJjCkqIFGaCXeIgI37z+zs6sFPoig///r5/BYf35mdsrtv3rx57+2wFhcJy3Yc +digHbY4AwAItBlPANTpH0EQdAgBIOt9iMBiY04b/Ha/U8QCSFz2GsyByIKExN4HEg2QKyQySOSQL +SJaQrCDxsQgAISQRJGtINpDEkCSQbCHZQbKH5ACpCyRHSE6QukKSQpJBcobUDZILpO6QXCG5QeoB +yR1ST0i9IPWG5AGpD6S+kDzpe54N0QeSHJIvJD9I/SD1hzQAkj+kgZAGQRoMaQgl2wAoICkhBUIK +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 + +------=_NextPart_01CEEB82.34D42FC0 +Content-Location: file:///C:/0E5B2E2E/ADVANCED_PACKET_INJECTION_files/filelist.xml +Content-Transfer-Encoding: quoted-printable +Content-Type: text/xml; charset="utf-8" + + + + + + + + + + +------=_NextPart_01CEEB82.34D42FC0-- diff --git a/network/trans/WFPSampler/docs/BASIC_ACTION.mht b/network/trans/WFPSampler/docs/BASIC_ACTION.mht new file mode 100644 index 000000000..d56863f8b --- /dev/null +++ b/network/trans/WFPSampler/docs/BASIC_ACTION.mht @@ -0,0 +1,2482 @@ +MIME-Version: 1.0 +Content-Type: multipart/related; boundary="----=_NextPart_01CEBF8A.2314F090" + +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_01CEBF8A.2314F090 +Content-Location: file:///C:/465B2E2E/BASIC_ACTION.htm +Content-Transfer-Encoding: quoted-printable +Content-Type: text/html; charset="windows-1252" + + + + + + + + + + + + +Basic Action + + + + + + + + + + +
+ +
+ +

BASIC ACTION= +

+ +
+ +

Overview

+ +

The Basic Action scenarios are designed to return simp= +le +actions.  No injection takes place,= + and +by default, they utilize static filters.

+ +

All filters added sit in WFPSampl= +er’s +sublayer (which is weighted just below IPsec’s sublayer), unless otherwise +specified using the –sl +<SUBLAYER> command line option.  +All filters are associated with WFPSampler’s +provider.

+ +

When a callout is used, the following diagram shows ho= +w the +code flows:

+ +


+Figure A. Code flow for Basic Action Scenario + +

When traffic matches a filter at the specified layer, = +and +the filter uses a callout, then the ClassifyBasicActio= +nFn +is invoked by the Filtering Engine.  +These functions make sure it can update the action and dictates which +action to return via PerformBasicAction().

+ +

The ClassifyBasicActionBlock() function will always return +FWP_ACTION_BLOCK, causing the traffic to be dropped.

+ +

The ClassifyBasicActionContinue() function will always return +FWP_ACTION_CONTINUE, causing the traffic to be allowed unless another filte= +r in +a different sublayer returns FWP_ACTION_BLOCK.

+ +

The ClassifyBasicActionPermit<= +/span>() function will always return +FWP_ACTION_PERMIT, causing the traffic to be allowed unless another filter = +in a +different sublayer returns FWP_ACTION_BLOCK.

+ +

The ClassifyBasicActionRandom<= +/span>() function (which always uses a +callout) will randomly return an action of FWP_ACTION_BLOCK, +FWP_ACTION_CONTINUE, or FWP_ACTION_PERMIT.  +This callout is meant to be for testing purposes only, and would nev= +er +dictate real world behavior of a callout.  +The chance of each action return can be controlled by command-line +parameters.

+ +

Applicable Layers

+ +

v  +FWPM_LAYER_INBOUND_IPPACKET_V4

+ +

v  +FWPM_LAYER_INBOUND_IPPACKET_V6

+ +

v  +FWPM_LAYER_OUTBOUND_IPPACKET_V4

+ +

v  +FWPM_LAYER_OUTBOUND_IPPACKET_V6

+ +

v  +FWPM_LAYER_IPFORWARD_V4

+ +

v  +FWPM_LAYER_IPFORWARD_V6

+ +

v  +FWPM_LAYER_INBOUND_TRANSPORT_V4

+ +

v  +FWPM_LAYER_INBOUND_TRANSPORT_V6

+ +

v  +FWPM_LAYER_OUTBOUND_TRANSPORT_V4

+ +

v  +FWPM_LAYER_OUTBOUND_TRANSPORT_V6

+ +

v  +FWPM_LAYER_STREAM_V4

+ +

v  +FWPM_LAYER_STREAM_V6

+ +

v  +FWPM_LAYER_DATAGRAM_DATA_V4

+ +

v  +FWPM_LAYER_DATAGRAM_DATA_V6

+ +

v  +FWPM_LAYER_INBOUND_ICMP_ERROR_V4

+ +

v  +FWPM_LAYER_INBOUND_ICMP_ERROR_V6

+ +

v  +FWPM_LAYER_OUTBOUND_ICMP_ERROR_V4

+ +

v  +FWPM_LAYER_OUTBOUND_ICMP_ERROR_V6

+ +

v  +FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT _V4

+ +

v  +FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V6

+ +

v  +FWPM_LAYER_ALE_AUTH_LISTEN_V4

+ +

v  +FWPM_LAYER_ALE_AUTH_LISTEN_V6

+ +

v  +FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4

+ +

v  +FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6

+ +

v  +FWPM_LAYER_ALE_AUTH_CONNECT_V4

+ +

v  +FWPM_LAYER_ALE_AUTH_CONNECT_V6

+ +

v  +FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4

+ +

v  +FWPM_LAYER_ALE_FLOW_ESTABLISHED_V6

+ +

v  +FWPM_LAYER_STREAM_PACKET_V4                                     (Win7+= +)

+ +

v  +FWPM_LAYER_STREAM_PACKET_V6                                     (Win7+= +)

+ +

v  +FWPM_LAYER_INBOUND_MAC_FRAME_ETHERNET       (Win8+)

+ +

v  +FWPM_LAYER_OUTBOUND_MAC_FRAME_ETHERNET   (Win8+)

+ +

v  +FWPM_LAYER_INBOUND_MAC_FRAME_NATIVE                            (Win8+)

+ +

v  +FWPM_LAYER_OUTBOUND_MAC_FRAME_NATIVE        (Win8+)

+ +

v  +FWPM_LAYER_INGRESS_VSWITCH_ETHERNET                  (Win8+)

+ +

v  +FWPM_LAYER_EGRESS_VSWITCH_ETHERNET                    (Win8+)

+ +

v  +FWPM_LAYER_INGRESS_VSWITCH_TRANSPORT_V4      (Win8+)

+ +

v  +FWPM_LAYER_INGRESS_VSWITCH_TRANSPORT_V6      (Win8+)

+ +

v  +FWPM_LAYER_EGRESS_VSWITCH_TRANSPORT_V4        (Win8+)

+ +

v  +FWPM_LAYER_EGRESS_VSWITCH_TRANSPORT_V6        (Win8+)

+ +

Command Line Usage

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

Option

+
+

Argument

+
+

Meaning

+
+

-s

+
+

BASIC_ACTION_XXXX

+
+

Implement the BASIC_PACKET_INJECTION scenario

+
+

-l

+
+

Applicable layer

+
+

Layer at which this filter will apply

+
+

-rab

+
+

Integer (0-100)

+
+

Determines chance of returning FWP_ACTION_BLOCK for + BASIC_ACTION_RANDOM. [default is 50]*

+
+

-rac

+
+

Integer (0-100)

+
+

Determines chance of returning FWP_ACTION_CONTINUE for + BASIC_ACTION_RANDOM. [default is 25]*

+
+

-rap

+
+

Integer (0-100)

+
+

Determines chance of returning FWP_ACTION_PERMIT for + BASIC_ACTION_RANDOM. [default is 25]*

+
+

-c

+
+

 

+
+

Causes the action to be returned via a callout, rather than the + filter’s action.

+
+

-sl

+
+

Applicable subLayer

+
+

SubLayer to associate with the filter= +.  [default is WFPSAMPLER_SUBLAYER].

+
+

-v

+
+

 

+
+

Make the objects associated with this scenario’s instance dynamic= +

+
+

-b

+
+

 

+
+

Make the objects associated with this scenario’s instance availab= +le + during boot-time

+
+

-r

+
+

 

+
+

Remove objects associated with this scenario instance

+
+

-?

+
+

 

+
+

Display help

+
+ +


+*   -rab, -rac, and -rap should total 100. If this is not the ca= +se +then the code will try to balance the values out (i.e. if you specify only = +-rab, then -rac, and -rap are calculated as best as possible).

+ +

WFPSampler.E= +xe -s +BASIC_ACTION_BLOCK -? +provides help output

+ +

WFPSampler.E= +xe -s +BASIC_ACTION_BLOCK -l FWPM_LAYER_INBOUND_IPPACKET_V4 -v“ +adds a dynamic filter (-v) at +FWPM_LAYER_INBOUND_IPPACKET_V4 (-l= +) +This filter will have no conditions, meaning it will block all traffic seen= + at +this layer.

+ +

WFPSampler.E= +xe -s +BASIC_ACTION_PERMIT  -l +FWPM_LAYER_INBOUND_IPPACKET_V4 -v -c“ adds a dynamic filter (-v) at FWPM_LAYER_INBOUND_IPPACKE= +T_V4 (-l) which invokes a callout (-c) This filter will have no +conditions, meaning it will allow all traffic seen at this layer (unless +another filter at a different sublayer blocks it).

+ +

WFPSampler.E= +xe -s +BASIC_ACTION_PERMIT -l FWPM_LAYER_INBOUND_IPPACKET_V4 -v -c -r“ removes (-r= +) +the dynamic filter (-v) at +FWPM_LAYER_INBOUND_IPPACKET_V4 (-l= +) +which references the appropriate callout (-c). +

+ +

 WFPSampler.Exe -s BASIC_ACTION_CONTINUE -l +FWPM_LAYER_INBOUND_TRANSPORT_V4  -ipla +1.0.0.1 -ipra 1.0.0.254 -i= +pp +TCP -c“ adds a persistent filter at FWPM_LAYER_INBOUND_IPPACKET_V4  (-l) +which references the appropriate callout (-c).  This filter will have 3 conditions; +FWPM_CONDITION_IP_LOCAL_ADDRESS (-= +ipla) equals 1.0.0.1, FWPM_CONDITION_IP_REMOTE_AD= +DRESS +(-ipra= +) +equals 1.0.0.254, and FWPM_CONDITION_IP_PROTOCOL (-ipp) equals TCP.

+ +

WFPSampler.E= +xe -s +BASIC_ACTION_RANDOM -l FWPM_LAYER_INBOUND_TRANSPORT_V4  -ipla 1.0.0= +.1 -ipra 1.0.0.254 -ipp TCP -= +rab 50 -rac 25 -rap 25“ adds +a persistent filter at FWPM_LAYER_INBOUND_IPPACKET_V4  (-l) +which references the appropriate callout.  +This filter will have 3 conditions; FWPM_CONDITION_IP_LOCAL_ADDRESS = +(-ipla= +) equals +1.0.0.1, FWPM_CONDITION_IP_REMOTE_ADDRESS (-ipra) equals 1.0.0.254, and FWPM_CONDITION_IP_PRO= +TOCOL +(-ipp<= +/b>) +equals TCP.  The callout will return +FWP_ACTION_BLOCK 50% of the time (= +-rab), FWP_ACTION_CONTINUE 25% of the time (-rac)= +, and +FWP_ACTION_PERMIT 25% of the time (-rap).  In theory, the traffic should be allowe= +d 50% +of the time, and blocked the 50% of the time.  +Notice that we do not have to specify “-c” for this scenario, as it = +can +only be implemented via a callout.

+ +

For a list of conditions applicable to each layer, ref= +er to Filtering +Conditions Available at Each Filtering Layer.

+ +

For a list of command line parameters for configuring = +each +condition, refer to Co= +nditions +for Command Line.

+ +
+ + + + + +------=_NextPart_01CEBF8A.2314F090 +Content-Location: file:///C:/465B2E2E/BASIC_ACTION_files/themedata.thmx +Content-Transfer-Encoding: base64 +Content-Type: application/vnd.ms-officetheme + +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 +cmVsc1BLBQYAAAAABQAFAF0BAACtCgAAAAA= + +------=_NextPart_01CEBF8A.2314F090 +Content-Location: file:///C:/465B2E2E/BASIC_ACTION_files/colorschememapping.xml +Content-Transfer-Encoding: quoted-printable +Content-Type: text/xml + + + +------=_NextPart_01CEBF8A.2314F090 +Content-Location: file:///C:/465B2E2E/BASIC_ACTION_files/image001.emz +Content-Transfer-Encoding: base64 +Content-Type: image/x-emz + +H4sIAAAAAAAEC61Wb0idVRz+va9evYmry9KLt5xeY2vqDC5hw6D06KXFysqNtX0Z5Dan3aw1bEQL +gndisaA/C2W0P6ZDcJv2QRirWKturaAPmkqRiyK0IoqghL4Mim7Pc857rldxcHEefe7zO7/z73ee +c95zjiMiTwMOUAh0uiIVgE01D4v8XiMSfeCRLaxVeovI4yjMtRV8HgyI9OSI1KCjj5eUzfbmSuxX +V9CBoCuJAuhuk6PQH+wQ4IaS3zOGVh+s+ySwG2DdmHJ1PTOu11CucnW8KJYyFUjb65RIEXxBgNMo +x0+BkqQdRySV4jg5iGJcUk6FX3cvuBBg2XofIt79MP20YLPfjUAJwPpMKUOyAWz7sTZi8XbCHwMg +EUYWmQQYG2Sj343JfMV8IUFbXNs2H22tjapeRmyNyPvJS9uMjdr1AUcBahdU4irYtQB1BslX/EEq +mBmqZ5tM0G/rwUwFlZPDtpW+nzGL4lzK3UmNqNaTeXEc5wiKi5WTsV7ZafoE2tUBjIXMZJlK63GN +e9Gv1YftrI26ac0d+MuAaYB7m/0s1Zt523YlmlMbrjtjoP4HMWgbQP2rlZl/CfwiySZNaTuq1mxu +aAwqNy9TYxmtrmcVxs6EmCQujdIsW6VJtuNXZLNyvSAq/MsKWCfWJYqBOL7qPfIc/hLSLofRhrmE +7EMf++QQrGflACI9IPLep/USjtbL3J+a1R9OA/Pqn7BmTyo1JwP3mrwX9/3bjP+e/SYffUmzKnlV +8+zJt0z+6nHNg18MaJYL72puHbyoORn4SHM0/pnhDyc0z971nWbv2k+asaANmJifMu218NlvjhrT +ZgoDtOnbDnBNuD7zqZT9XJETuUwnUr7kQrc9UC4he6ULvytL76O/FjS9KsmH2MOZltdObHuUlkle +4lwurdofz2sO+v6Qzypp/F73sC6fnejQJeGT3WXXsBGmPjHlllsv1cRZ4ZXRmUay1YZ7himkfxfa +Jd0hHkMy987fp+1+4Z6per2Hn4neP+SWGTOuja/7W5O38bMNU0j/Yo9Pd0jb8/roTX+HlHYHMA5s +BLgeFwEOxLFHRoYb3/j5XCPLbgfYZzOQmbBcOr0AZ/b7+pAOhAIy/jwfzAd8300+s4z+fKDAZ+tj +HYJlTBsAzmENZN4CPgb0APzON6mFvbcaZ+569FsC8CzWWxT9T8qA0+cMODxnb+SM5QaoA5gsZ3PG +sp3VADp6u5BnnBNAPxb0OHAGYLy4zyIqMh+x9aGhZ20UZ3WXVaIi9ea41Jt7QwHmXnNyamBHAWrE +NWRhr3MKfwNOrzMEnKJWR4qU44VQrOvgrLT9ItQV3fdoppOdD/u5FWCstK0/iHnuRJ5x9qIA0WgM +gRlLLDKGv/lILBK7jbZtR62W6w9NstKNWlltqFVBxhuoZczcFf1OpzOO2+GGtRhrwWgLc15u/jGU +c390AglsjmlwAL6pyFh4KhJad+xOgvZYOFMDa6Nq1vPuQ+WjgNkj4irYtQD3CGhV3j79zrSTcIlO +hzoyvxpvH+7zOgaJZHkl3yX3XAzgfktQd2j+dVrzxXpTf6vzanyjpRgnBFD/anX9t0/F3GS868HR +ON8xvyHWHwCk9DtmLTItsh83cTteK13yzHVeMXLaf8Hk/2VeMP8VmhdDVanh81WGTyjNySuPaVaH +u4z/7pcNX3pT8+B9b5v8i/2aWzuGNSd3XNAcbbqs2Tt7RfPsL18a3v2NZmzVZV8qnI89H0p8G7To +nWLXAcul780PwJ9Dy13AVji5lvhP35ssawfYdzOQmey9eRDObHTMvA8DaMN8HoC7TttBP89zC/tk +2bsS7vTZZ+/IUvhCAPdD5h3JeNsAzpW6PAUjCtg8z4lioEpGUGrT8tra0qV8Bxzsm1rwTMyMxVWm +DG65GSiigRQCaP8PKe5hDBwPAAA= + +------=_NextPart_01CEBF8A.2314F090 +Content-Location: file:///C:/465B2E2E/BASIC_ACTION_files/image002.png +Content-Transfer-Encoding: base64 +Content-Type: image/png + +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 +ABCcIDgBAEBwguAEAADBCQhOAAAQnCA4AQBAcILgBAAAwQkITgAAEJwgOAEAQHCC4AQAAMEJCE4A +ABCcIDgBAEBwguAEAADBCQhOAAAQnCA4AQBAcILgBAAAwQkITgAAEJwgOAEAQHCC4AQAAMEJCE4A +ABCcIDgBAEBwguAEAADBCQhOAAAQnCA4AQBAcILgBAAAwQkITgAAEJwgOAEAQHCC4AQAAMEJCE4A +ABCcIDgBAEBwguAEAADBCQhOAAAQnCA4AQBAcILgBAAAwQmCU3ACAIDgBMEJAACCEwQnAAAIThCc +ghMAAAQnCE4AABCcIDgBAEBwguD0QwQAAAQnCE4AABCcIDgBAEBwguAEAAAEJwhOAAAQnCA4AQBA +cILgBAAABCcITgAAEJwgOAEAQHCC4AQAAAQnCE4AABCcIDgBAEBwguAEAAAEJwhOAAAQnCA4AQBA +cILgBAAABCcITgAAEJwgOAEAQHCC4AQAAAQnCE4AABCcIDgBAEBwguAEAAAEJwhOAAAQnCA4AQBA +cILgBAAABCcITgAAEJwgOAEAQHCC4AQAAMEpOEFwAgCA4ATBCQAAghMEJwAACE7BCYITAAAEJwhO +AAAQnCA4AQBAcApOEJwAACA4QXACAIDgBMEJAACCU3CC4AQAAMEJghMAAAQnCE4AABCcQgIEJwAA +CE4QnAAAIDhBcAIAgOAUEyA4AQBAcILgBAAAwQmCEwAABCcgOAEAQHCC4AQAAMEJghMAAAQnIDgB +AEBwguAEAADBCYITAAAEJyA4AQBAcILgBAAAwQmCEwAABCcgOAEAQHCC4AQAAMEJghMAAAQnIDgB +AEBwguAEAADBCYITAAAEJyA4AQBAcILgBAAAwQmCEwAABCcgOAEAQHCC4AQAAMEJghMAAAQnIDgB +AEBwguAEAADBCYITAAAEJyA4AQBAcILgBAAAwQmCEwAABCcgOAEAQHCC4AQAAMEJghMAAAQnIDgB +AEBwguAEAADBCYITAAAEJwhOwQkAAIITBCcAAAhOEJwAACA4QXAKTgAAEJwgOAEAQHCC4AQAAMEJ +gtMPEQAAEJwgOAEAQHCC4AQAAMEJgtMPEgAAEJwgOAEAQHCC4AQAAMEJghMAABCcIDgBAEBwguAE +AADBCYITAAAQnCA4AQBAcILgBAAAwQmCEwAAEJwgOAEAQHCC4AQAAMEJghMAABCcIDgBAEBwguAE +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 + +------=_NextPart_01CEBF8A.2314F090 +Content-Location: file:///C:/465B2E2E/BASIC_ACTION_files/oledata.mso +Content-Transfer-Encoding: base64 +Content-Type: application/x-mso + +0M8R4KGxGuEAAAAAAAAAAAAAAAAAAAAAPgADAP7/CQAGAAAAAAAAAAAAAAABAAAAAQAAAAAAAAAA +EAAA/v///wAAAAD+////AAAAAAAAAAD///////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////9 +/////v///wMAAAAEAAAABQAAAAYAAAAHAAAACAAAAAkAAAAKAAAACwAAAAwAAAANAAAADgAAAA8A +AAAQAAAAEQAAABIAAAATAAAAFAAAABUAAAAWAAAAFwAAABgAAAAZAAAAGgAAABsAAAAcAAAAHQAA +AB4AAAAfAAAAIAAAACEAAAAiAAAAIwAAACQAAAAlAAAA/v////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////1IA +bwBvAHQAIABFAG4AdAByAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAWAAUA//////////8BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGDbhs/Ev84B +/v///wAAAAAAAAAAXwAxADQANAAyADIAMwA1ADQANwA4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAABgAAgH///////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAACAAAA4kYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///////////////wAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////// +////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHIA +AHic7Ht5XFTV+/9z7tzZ2GZjBhB0BnBBRRk2t5SZAUkxDVTGpVIYkE2FQYTM1AYXBEsE3NLS0Nyx +RDNsU6HMslzAPpqWJSgJCBZ3AFlE5v7OnSXJT4v1+fzx+7xe30NvzrnnnnuW5zzneT/PwaoqRTW7 +j/W5BY+lEGCBieYDp1cdssKchACE9dlE07Stmv6/9D+VejDsrXtI4pyNwew5F4OHwceww3DAGIEx +EsMJQ2BRARBhiDEkGM4YUgwZhguGK4YbRh8MdwwPjL4Y/TDkGAoMTwwvDG+M/hgDMAZiDMLwwRiM +MQRjKIYvxjCM4Rh+GEoMf4wAjECMIIxg61ps+L/052ka6PFPJt6LcEjDeQYsfdwU/GmSYY2x9cXo +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 +AAAAAAAAAAAAAAAAAAAAAAAA + +------=_NextPart_01CEBF8A.2314F090 +Content-Location: file:///C:/465B2E2E/BASIC_ACTION_files/filelist.xml +Content-Transfer-Encoding: quoted-printable +Content-Type: text/xml; charset="utf-8" + + + + + + + + + + +------=_NextPart_01CEBF8A.2314F090-- diff --git a/network/trans/WFPSampler/docs/BASIC_PACKET_EXAMINATION.mht b/network/trans/WFPSampler/docs/BASIC_PACKET_EXAMINATION.mht new file mode 100644 index 000000000..e8be663ac --- /dev/null +++ b/network/trans/WFPSampler/docs/BASIC_PACKET_EXAMINATION.mht @@ -0,0 +1,2577 @@ +MIME-Version: 1.0 +Content-Type: multipart/related; boundary="----=_NextPart_01CEEB83.11050D70" + +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_01CEEB83.11050D70 +Content-Location: file:///C:/E03B2E2E/BASIC_PACKET_EXAMINATION.htm +Content-Transfer-Encoding: quoted-printable +Content-Type: text/html; charset="windows-1252" + + + + + + + + + + + + +Basic Packet Examination + + + + + + + + + + +
+ +
+ +

BASIC PACKET +EXAMINATION

+ +
+ +

Overview

+ +

The Basic Packet Examination scenario will log the pac= +ket’s +headers at that layer by parsing the NBL and using ETW tracing.  This is performed inline.  For this scenario, no injection occurs.= +

+ +

All filters added sit in FWPM_SUBLAYER_INSPECTION.  All filters are associated with WFPSampler’s provider.

+ +

The following diagram shows how the code flows for this +callout:

+ +


+Figure A. Code flow for Basic Packet Examination Scenario<= +/span>

+ +

When traffic matches a filter at the specified layer, = +ClassifyBasicPacketExamination() is invoked by the Filtering +Engine.  This function creates the +CLASSIFY_DATA and invokes the appropriate performFn.

+ +

Each of the performFns are tailored to log the headers for their layer using = +ETW +tracing.  The main need for multipl= +e performFns in this scenario is due to the data offset= + and +information available at each layer.  +Within the performFn, the offset of the +original NBL is manipulated to get to the appropriate header, after which, = +the +logging function is invoked.  When +logging has finished, the data offset is adjusted to the next header.  Before exiting the performFn, +the data offset is returned to the original position.

+ +

When the performFn is fini= +shed, +the classifyFn is allowed to continue.  The classifyFn will +set the action to FWP_ACTION_CONTINUE, and exit.

+ +

Applicable Layers

+ +

v  +FWPM_LAYER_INBOUND_IPPACKET_V4

+ +

v  +FWPM_LAYER_INBOUND_IPPACKET_V4_DISCARD<= +/o:p>

+ +

v  +FWPM_LAYER_INBOUND_IPPACKET_V6

+ +

v  +FWPM_LAYER_INBOUND_IPPACKET_V6_DISCARD

+ +

v  +FWPM_LAYER_OUTBOUND_IPPACKET_V4

+ +

v  +FWPM_LAYER_OUTBOUND_IPPACKET_V4_DISCARD= +

+ +

v  +FWPM_LAYER_OUTBOUND_IPPACKET_V6 + +

v  +FWPM_LAYER_OUTBOUND_IPPACKET_V6_DISCARD

+ +

v  +FWPM_LAYER_IPFORWARD_V4

+ +

v  +FWPM_LAYER_IPFORWARD_V4_DISCARD + +

v  +FWPM_LAYER_IPFORWARD_V6

+ +

v  +FWPM_LAYER_IPFORWARD_V6_DISCARD

+ +

v  +FWPM_LAYER_INBOUND_TRANSPORT_V4

+ +

v  +FWPM_LAYER_INBOUND_TRANSPORT_V4_DISCARD

+ +

v  +FWPM_LAYER_INBOUND_TRANSPORT_V6 + +

v  +FWPM_LAYER_INBOUND_TRANSPORT_V6_DISCARD

+ +

v  +FWPM_LAYER_OUTBOUND_TRANSPORT_V4

+ +

v  +FWPM_LAYER_OUTBOUND_TRANSPORT_V4_DISCARD

+ +

v  +FWPM_LAYER_OUTBOUND_TRANSPORT_V6<= +/p> + +

v  +FWPM_LAYER_OUTBOUND_TRANSPORT_V6_DISCARD

+ +

v  +FWPM_LAYER_STREAM_V4

+ +

v  +FWPM_LAYER_STREAM_V4_DISCARD

+ +

v  +FWPM_LAYER_STREAM_V6

+ +

v  +FWPM_LAYER_STREAM_V6_DISCARD

+ +

v  +FWPM_LAYER_DATAGRAM_DATA_V4

+ +

v  +FWPM_LAYER_DATAGRAM_DATA_V4_DISCARD

+ +

v  +FWPM_LAYER_DATAGRAM_DATA_V6

+ +

v  +FWPM_LAYER_DATAGRAM_DATA_V6_DISCARD

+ +

v  +FWPM_LAYER_INBOUND_ICMP_ERROR_V4

+ +

v  +FWPM_LAYER_INBOUND_ICMP_ERROR_V4_DISCARD

+ +

v  +FWPM_LAYER_INBOUND_ICMP_ERROR_V6

+ +

v  +FWPM_LAYER_INBOUND_ICMP_ERROR_V6_DISCARD

+ +

v  +FWPM_LAYER_OUTBOUND_ICMP_ERROR_V4

+ +

v  +FWPM_LAYER_OUTBOUND_ICMP_ERROR_V4_DISCARD

+ +

v  +FWPM_LAYER_OUTBOUND_ICMP_ERROR_V6

+ +

v  +FWPM_LAYER_OUTBOUND_ICMP_ERROR_V6_DISCARD

+ +

v  +FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V4

+ +

v  +FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V4_DISCAR= +D

+ +

v  +FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V6

+ +

v  +FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V6_DISCAR= +D

+ +

v  +FWPM_LAYER_ALE_AUTH_LISTEN_V4

+ +

v  +FWPM_LAYER_ALE_AUTH_LISTEN_V4_DISCARD

+ +

v  +FWPM_LAYER_ALE_AUTH_LISTEN_V6

+ +

v  +FWPM_LAYER_ALE_AUTH_LISTEN_V6_DISCARD

+ +

v  +FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4

+ +

v  +FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4_DISCARD + +

v  +FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6

+ +

v  +FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6_DISCARD + +

v  +FWPM_LAYER_ALE_AUTH_CONNECT_V4

+ +

v  +FWPM_LAYER_ALE_AUTH_CONNECT_V4_DISCARD

+ +

v  +FWPM_LAYER_ALE_AUTH_CONNECT_V6

+ +

v  +FWPM_LAYER_ALE_AUTH_CONNECT_V6_DISCARD

+ +

v  +FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4

+ +

v  +FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4_DISCARD

+ +

v  +FWPM_LAYER_ALE_FLOW_ESTABLISHED_V6

+ +

v  +FWPM_LAYER_ALE_FLOW_ESTABLISHED_V6_DISCARD + +

v  +FWPM_LAYER_ALE_RESOURCE_RELEASE_V4                   (Win7+)

+ +

v  +FWPM_LAYER_ALE_RESOURCE_RELEASE_V6                   (Win7+)

+ +

v  +FWPM_LAYER_ALE_ENDPOINT_CLOSURE_V4                  (Win7+)

+ +

v  +FWPM_LAYER_ALE_ENDPOINT_CLOSURE_V6                  (Win7+)

+ +

v  +FWPM_LAYER_ALE_CONNECT_REDIRECT_V4                  (Win7+)

+ +

v  +FWPM_LAYER_ALE_CONNECT_REDIRECT_V6                  (Win7+)

+ +

v  +FWPM_LAYER_ALE_BIND_REDIRECT_V4                 = +          (Win7+)

+ +

v  +FWPM_LAYER_ALE_BIND_REDIRECT_V6                 = +          (Win7+)

+ +

v  +FWPM_LAYER_STREAM_PACKET_V4                                     (Win7+= +)

+ +

v  +FWPM_LAYER_STREAM_PACKET_V6                                     (Win7+= +)

+ +

v  +FWPM_LAYER_INBOUND_MAC_FRAME_ETHERNET       (Win8+)

+ +

v  +FWPM_LAYER_OUTBOUND_MAC_FRAME_ETHERNET   (Win8+)

+ +

v  +FWPM_LAYER_INBOUND_MAC_FRAME_NATIVE            (Win8+)

+ +

v  +FWPM_LAYER_OUTBOUND_MAC_FRAME_NATIVE        (Win8+)

+ +

v  +FWPM_LAYER_INGRESS_VSWITCH_ETHERNET                  (Win8+)

+ +

v  +FWPM_LAYER_EGRESS_VSWITCH_ETHERNET                    (Win8+)

+ +

v  +FWPM_LAYER_INGRESS_VSWITCH_TRANSPORT_V4      (Win8+)

+ +

v  +FWPM_LAYER_INGRESS_VSWITCH_TRANSPORT_V6      (Win8+)

+ +

v  +FWPM_LAYER_EGRESS_VSWITCH_TRANSPORT_V4        (Win8+)

+ +

v  +FWPM_LAYER_EGRESS_VSWITCH_TRANSPORT_V6        (Win8+)

+ +

Command Line Usage

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

Option

+
+

Argument

+
+

Meaning

+
+

-s

+
+

BASIC_PACKET_EXAMINATION

+
+

Implement the BASIC_PACKET_EXAMINATION scenario

+
+

-l

+
+

Applicable Layer

+
+

Layer at which this filter will apply

+
+

-v

+
+

 

+
+

Make the objects associated with this scenario’s instance dynamic= +

+
+

-b

+
+

 

+
+

Make the objects associated with this scenario’s instance availab= +le + during boot-time

+
+

-r

+
+

 

+
+

Remove objects associated with this scenario instance

+
+

-?

+
+

 

+
+

Display help

+
+ +

 

+ +

WFPSampler.E= +xe -s +BASIC_PACKET_EXAMINATION -? +provides help output

+ +

WFPSampler.E= +xe -s +BASIC_PACKET_EXAMINATION -l FWPM_LAYER_INBOUND_IPPACKET_V4 -v  adds a dyna= +mic +filter (-v) at FWPM_LAYER_INBOUND_IPPACKET_V4 (-l) which references the +appropriate callout.  This filter w= +ill +have no conditions, meaning it will act on all traffic seen at this layer.<= +/p> + +

WFPSampler.E= +xe -s +BASIC_PACKET_EXAMINATION -l FWPM_LAYER_INBOUND_IPPACKET_V4 –v -r  removes (-r) the dynamic filter (-v) at FWPM_LAYER_INBOUND_IPPACKE= +T_V4 (-l) which references the appropri= +ate +callout.

+ +

 WFPSampler.Exe -s BASIC_PACKET_EXAMINATION -l +FWPM_LAYER_INBOUND_TRANSPORT_V4  -ipla +1.0.0.1 –ipra 1.0.0.254 –i= +pp +TCP “ adds a persistent filter at FWPM_LAYER_INBOUND_IPPACKET_V4  (-l) +which references the appropriate callout.  +This filter will have 3 conditions; FWPM_CONDITION_IP_LOCAL_ADDRESS = +(-ipla= +) equals +1.0.0.1, FWPM_CONDITION_IP_REMOTE_ADDRESS (-ipra) equals 1.0.0.254, and FWPM_CONDITION_IP_PROTOCOL  (-ipp)= + equals +TCP.

+ +

For a list of conditions applicable to each layer, ref= +er to Filtering +Conditions Available at Each Filtering Layer.

+ +

For a list of command line parameters for configuring = +each +condition, refer to Co= +nditions +for Command Line.

+ +

Notes

+ +

Traces

+ +

In order to get the traces, use a trace utility such as +TraceLog.exe and TraceFmt.exe

+ +

TraceLog.exe -start wfpsampler -guid #5350465= +7-6d61-6c70-6572-5f496e746572 +-f wfpsampler.etl -flags 0xFFFF -level 7

+ +

Run the scenario

+ +

TraceLog.exe -stop wfpsampler

+ +

TraceFmt.exe wfpsampler.etl +-pdb +<SYMBOLS.PRI_PATH>\WFPSamplerCalloutDriver_0x603.= +pdb  -o \WFPSampler.txt

+ +
+ + + + + +------=_NextPart_01CEEB83.11050D70 +Content-Location: file:///C:/E03B2E2E/BASIC_PACKET_EXAMINATION_files/themedata.thmx +Content-Transfer-Encoding: base64 +Content-Type: application/vnd.ms-officetheme + +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 +cmVsc1BLBQYAAAAABQAFAF0BAACtCgAAAAA= + +------=_NextPart_01CEEB83.11050D70 +Content-Location: file:///C:/E03B2E2E/BASIC_PACKET_EXAMINATION_files/colorschememapping.xml +Content-Transfer-Encoding: quoted-printable +Content-Type: text/xml + + + +------=_NextPart_01CEEB83.11050D70 +Content-Location: file:///C:/E03B2E2E/BASIC_PACKET_EXAMINATION_files/image001.emz +Content-Transfer-Encoding: base64 +Content-Type: image/x-emz + +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 + +------=_NextPart_01CEEB83.11050D70 +Content-Location: file:///C:/E03B2E2E/BASIC_PACKET_EXAMINATION_files/image002.png +Content-Transfer-Encoding: base64 +Content-Type: image/png + +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 +ff3YsbcEJwAACE4QnAAAIDhBcAIAgOAEwSk4AQBAcILgBAAAwQmCEwAABCcITh8iAAAgOEFwAgCA +4ATBCQAAghMEJwAAIDhBcAIAgOAEwQkAAIITBCcAACA4QXACAIDgBMEJAACCEwQnAAAgOEFwAgCA +4ATBCQAAghMEJwAAIDhBcAIAgOAEwQkAAIITBCcAACA4QXACAIDgBMEJAACCEwQnAAAgOEFwAgCA +4ATBCQAAghMEJwAAIDhBcAIAgOAEwQkAAIITBCcAACA4QXACAIDgBMEJAACCEwQnAAAITsEJghMA +AAQnCE4AABCcIDgBAEBwCk4QnAAAIDhBcAIAgOAEwQkAAIJTcILgBAAAwQmCEwAABCcITgAAEJyC +EwQnAAAIThCcAAAgOEFwAgCA4BQSIDgBAEBwguAEAADBCYITAAAEJyA4AQBAcILgBAAAwQmCEwAA +BCcgOAEAQHCC4AQAAMEJghMAAAQnIDgBAEBwguAEAADBCYITAAAEJyA4AQBAcILgBAAAwQmCEwAA +BCcgOAEAQHCC4AQAAMEJghMAAAQnIDgBAEBwguAEAADBCYITAAAEJyA4AQBAcILgBAAAwQmCEwAA +BCcgOAEAQHCC4AQAAMEJghMAAAQnIDgBAEBwguAEAADBCYITAAAEJyA4AQBAcILgBAAAwQmCEwAA +BCcgOAEAQHCC4AQAAMEJghMAAAQnIDgBAEBwguAEAADBCYITAAAEJwhOwQkAAIITBCcAAAhOEJwA +ACA4QXAKTgAAEJwgOAEAQHCC4AQAAMEJgtOHCAAACE4QnAAAIDhBcAIAgOAEwQkAAAhOEJwAACA4 +QXACAIDgBMEJAAAIThCcAAAgOEFwAgCA4ATBCQAACE4QnAAAIDhBcAIAgOAEwQkAAAhOEJwAACA4 +QXACAIDgBMEJAAAIThCcAAAgOEFwAgCA4ATBCQAACE4QnAAAIDhBcAIAgOAEwQkAAAhOEJwAACA4 +QXACAIDgBMEJAAAIThCcAAAgOEFwAgCA4ATBCQAAglNwguAEAADBCYITAAAEJwhOAAAQnIITBCcA +AAhOEJwAACA4QXACAIDgFJwgOAEAQHCC4AQAAMEJghMAAASn4ATBCQAAghMEJwAACE4QnAAAIDiF +BAhOAAAQnCA4AQBAcILgBAAAwSkmQHACAIDgBMEJAACCEwQnAAAITkBwAgCA4ATBCQAAghMEJwAA +CE5AcAIAgOAEwQkAAIITBCcAAAhOQHACAIDgBMEJAACCEwQnAAAITkBwAgCA4ATBCQAAghMEJwAA +CE5AcAIAgOAEwQkAAIITBCcAAAhOQHACAIDgBMEJAACCEwQnAAAITkBwAgCA4ATBCQAAghMEJwAA +CE5AcAIAgOAEwQkAAIITBCcAAAhOQHACAIDgBMEJAACCEwQnAAAITkBwAgCA4ATBCQAAghMEJwAA +CE5AcAIAgOAEwQkAAIITBCcAAAhOEJyCEwAABCcITgAAEJwgOAEAQHCC4BScAAAgOEFwAgCA4ATB +CQAAghMEpw8RAAAQnCA4AQBAcILgBAAAwQmC0wcJAAAIThCcAAAgOEFwAgCA4ATBCQAACE4QnAAA +IDhBcAIAgOAEwQkAAAhOEJwAACA4QXACAIDgBMEJAAAIThCcAAAgOEFwAgCA4ATBCQAACE4QnAAA +IDhBcAIAgOAEwQkAAAhOEJwAACA4QXACAIDgBMEJAAAIThCcAAAgOEFwAgCA4ATBCQAACE4QnAAA +IDhBcAIAgOAEwQkAAIJTcILgBAAAwQmCEwAABCcITgAAEJyCEwQnAAAIThCcAAAgOEFwAgCA4BSc +IDgBAEBwguAEAADBCYITAAAEp+AEwQkAAIITBCcAAAhOEJwAACA4BScITgAAEJwgOAEAQHCC4AQA +AMEpJkBwAgCA4ATBCQAAghMEJwAACE5AcAIAgOAEwQkAAIITBCcAAAhOQHACAIDgBMEJAACCEwQn +AAAITkBwAgCA4ATBCQAAghMEJwAACE5AcAIAgOAEwQkAAIITBCcAAAhOQHACAIDgBMEJAACCEwQn +AAAITkBwAgCA4ATBCQAAghMEJwAACE5AcAIAgOAEwQkAAIITBCcAAAhOQHACAIDgBMEJAACCEwQn +AAAITkBwAgCA4ATBCQAAghMEJwAACE5AcAIAgOAEwQkAAIITBCcAAAhOQHACAIDgBMEJAACCEwQn +AAAIThCcghMAAAQnCE4AABCcIDgBAEBwguAUnAAAIDhBcAIAgOAEwQkAAIITBKfgBAAAwQmCEwAA +BCcITgAAEJwgOH2IAACA4ATBCQAAghMEJwAACE4QnAAAgOAEwQkAAIITBCcAAAhOEJwAAIDgBMEJ +AACCEwQnAAAIThCcAACA4ATBCQAAghMEJwAACE4QnAAAgOAEwQkAAIITBCcAAAhOEJwAAIDgBMEJ +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 + +------=_NextPart_01CEEB83.11050D70 +Content-Location: file:///C:/E03B2E2E/BASIC_PACKET_EXAMINATION_files/oledata.mso +Content-Transfer-Encoding: base64 +Content-Type: application/x-mso + +0M8R4KGxGuEAAAAAAAAAAAAAAAAAAAAAPgADAP7/CQAGAAAAAAAAAAAAAAABAAAAAQAAAAAAAAAA +EAAA/v///wAAAAD+////AAAAAAAAAAD///////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////9 +/////v///wMAAAAEAAAABQAAAAYAAAAHAAAACAAAAAkAAAAKAAAACwAAAAwAAAANAAAADgAAAA8A +AAAQAAAAEQAAABIAAAATAAAAFAAAABUAAAAWAAAAFwAAABgAAAAZAAAAGgAAABsAAAAcAAAAHQAA +AB4AAAAfAAAAIAAAACEAAAAiAAAAIwAAACQAAAAlAAAA/v////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////1IA +bwBvAHQAIABFAG4AdAByAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAWAAUA//////////8BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALAAOh/G684B +/v///wAAAAAAAAAAXwAxADQANAA3ADAANwAwADIAOQAyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAABgAAgH///////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAACAAAABkcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///////////////wAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////// +////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHIA +AHic7Ht3XFRH9/6Zu3cbbRu7gKC7gAUVZWm2KLuLEsEGImtJVFiQpsIiJWrULBYEE5GiGE00aCwx +mIjGkGqExBQTC5g3JiYmghKpJtwFpKjs/c3dXSLxTX/fP37v5/MdfJi5c+dOOXPmPOeMWlMtqjt4 +atBNeCQFAQtMNB84A+qQFeYkBCCszyaapvur6f9L/1OpD8PWuockztkYzJ5zMXgYfAwbDDuMcRjj +MRwwBBYVABGGGEOC4YghxZBhOGE4Y7hgDMJwxXDDGIwxBEOOocBwx/DA8MQYijEMYzjGCAwvjJEY +ozBGY3hjjMEYi+GDocTwxfDD8McIwAi0rqUf/5f+OEWCHv9k4r0IgVScp8PaR03BHyYZ1pj+vhj9 +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 +AAAAAAAAAAAAAAAAAAAAAAAA + +------=_NextPart_01CEEB83.11050D70 +Content-Location: file:///C:/E03B2E2E/BASIC_PACKET_EXAMINATION_files/filelist.xml +Content-Transfer-Encoding: quoted-printable +Content-Type: text/xml; charset="utf-8" + + + + + + + + + + +------=_NextPart_01CEEB83.11050D70-- diff --git a/network/trans/WFPSampler/docs/BASIC_PACKET_INJECTION.mht b/network/trans/WFPSampler/docs/BASIC_PACKET_INJECTION.mht new file mode 100644 index 000000000..95a4ac5b3 --- /dev/null +++ b/network/trans/WFPSampler/docs/BASIC_PACKET_INJECTION.mht @@ -0,0 +1,2927 @@ +MIME-Version: 1.0 +Content-Type: multipart/related; boundary="----=_NextPart_01CEBF8D.D4965900" + +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_01CEBF8D.D4965900 +Content-Location: file:///C:/0E5B2E2E/BASIC_PACKET_INJECTION.htm +Content-Transfer-Encoding: quoted-printable +Content-Type: text/html; charset="windows-1252" + + + + + + + + + + + + +Basic Packet Injection + + + + + + + + + + +
+ +
+ +

BASIC PACKET= + INJECTION

+ +
+ +

Overview

+ +

The Basic Packet Injection scenario will clone the pac= +ket +and inject it back to the same layer.  No +modification is performed on the packet, with the following exceptions:

+ +

·         +The packet is loopback and only one of either +the source or destination address is that of the software loopback.  In this case, the IP header is modified= + by +changing the software loopback address to that of an address on the local +machine.  This is required due to T= +CPIP’s +stack validation logic.

+ +

·         +The packet is received at an inbound transpo= +rt +layer, and had been IPsec secured. In this case, it is necessary to reconst= +ruct +the IP header as IPsec processing does not remove the original ESP or AH +information from it.

+ +

All filters added sit in WFPSampl= +er's +sublayer (which is weighted just below IPsec's sublayer), unless otherwise +specified using the -sl +<SUBLAYER> command line option.  +All filters are associated with WFPSampler's +provider.

+ +

The following diagram shows how the code flows for this +callout:

+ +


+Figure A. Code flow for Basic Packet Injection Scenario

+ +

When traffic matches a filter at the specified layer, = +ClassifyBasicPacketInjection() is invoked by the Filtering +Engine.  This function validates th= +at we +can perform the injection by looking at the pClassifyO= +ut +rights.  It will then create the +INJECTION_DATA which consists of the injectionHandle +and the injectionState.  If the injectionSt= +ate +indicates that we haven’t injected this packet before, then the injection +method is determined (default is asynchronous), and the appropriate triggerFn is called.= +   +At this point, the original packet will be blocked.

+ +

If the injection method is synchronous (inline), TriggerBasicPacketInjectionInline() is invoked.  This function creates the CLASSIFY_DATA, +which consists of the data that was passed into the cl= +assifyFn.  Depending on which layer the injection = +is +happening, the appropriate performFn is called.= +

+ +

If the injection method is asynchronous (out of band),= + TriggerBasicPacketInjectionOutOfBand() is invoked.  This function creates the CLASSIFY_DATA= + which +consists of copies and references of the data that was passed into the classifyFn.  = +Based on +the queuing method, the appropriate queueFn (de= +fault +is DeferredProcedureCalls) is invoked.

+ +

Regardless of which queueFn is +used, each will call the appropriate performFn = +based +on the layer the injection is happening.

+ +

Each of the performFns are tailored to inject for their respective layers.  Each will get the required data for its +specific injectionFn.  +Depending on the layer, the offsets are adjusted on the original so = +the +whole packet is available.  Once the +offsets are adjusted, the original is cloned, and the offsets of the origin= +al +are returned to the original place.  If +any of the exceptions noted above are required, then modification of the IP +header occurs. Finally, the appropriate injectionFn +is invoked.

+ +

Upon successful injection, CompleteBasicPacketI= +njection() will be called by the TCPIP +stack.  This function will show the +status of the injected packet.  +Additionally, any memory that was allocated from the functions above, will be freed and any references released.

+ +

Applicable Layers

+ +

v  +FWPM_LAYER_INBOUND_IPPACKET_V4

+ +

v  +FWPM_LAYER_INBOUND_IPPACKET_V6

+ +

v  +FWPM_LAYER_OUTBOUND_IPPACKET_V4

+ +

v  +FWPM_LAYER_OUTBOUND_IPPACKET_V6

+ +

v  +FWPM_LAYER_IPFORWARD_V4

+ +

v  +FWPM_LAYER_IPFORWARD_V6

+ +

v  +FWPM_LAYER_INBOUND_TRANSPORT_V4

+ +

v  +FWPM_LAYER_INBOUND_TRANSPORT_V6

+ +

v  +FWPM_LAYER_OUTBOUND_TRANSPORT_V4

+ +

v  +FWPM_LAYER_OUTBOUND_TRANSPORT_V6

+ +

v  +FWPM_LAYER_DATAGRAM_DATA_V4

+ +

v  +FWPM_LAYER_DATAGRAM_DATA_V6

+ +

v  +FWPM_LAYER_INBOUND_ICMP_ERROR_V4

+ +

v  +FWPM_LAYER_INBOUND_ICMP_ERROR_V6

+ +

v  +FWPM_LAYER_OUTBOUND_ICMP_ERROR_V4

+ +

v  +FWPM_LAYER_OUTBOUND_ICMP_ERROR_V6

+ +

v  +FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4

+ +

v  +FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6

+ +

v  +FWPM_LAYER_ALE_AUTH_CONNECT_V4

+ +

v  +FWPM_LAYER_ALE_AUTH_CONNECT_V6

+ +

v  +FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4

+ +

v  +FWPM_LAYER_ALE_FLOW_ESTABLISHED_V6

+ +

v  +FWPM_LAYER_STREAM_PACKET_V4                                     (Win7+= +)

+ +

v  +FWPM_LAYER_STREAM_PACKET_V6                                     (Win7+= +)

+ +

v  +FWPM_LAYER_INBOUND_MAC_FRAME_ETHERNET       (Win8+)

+ +

v  +FWPM_LAYER_OUTBOUND_MAC_FRAME_ETHERNET   (Win8+)

+ +

v  +FWPM_LAYER_INBOUND_MAC_FRAME_NATIVE            (Win8+)

+ +

v  +FWPM_LAYER_OUTBOUND_MAC_FRAME_NATIVE        (Win8+)

+ +

v  +FWPM_LAYER_INGRESS_VSWITCH_ETHERNET                  (Win8+)

+ +

v  +FWPM_LAYER_EGRESS_VSWITCH_ETHERNET                    (Win8+)

+ +

Command Line Usage

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

Option

+
+

Argument

+
+

Meaning

+
+

-s

+
+

BASIC_PACKET_INJECTION

+
+

Implement the BASIC_PACKET_INJECTION scenario

+
+

-l

+
+

Applicable Layer

+
+

Layer at which this filter will apply

+
+

-sl

+
+

Applicable subLayer

+
+

SubLayer to associate with the filter= +.  [default is + WFPSAMPLER_SUBLAYER].

+
+

-v

+
+

 

+
+

Make the objects associated with this scenario’s instance dynamic= +

+
+

-b

+
+

 

+
+

Make the objects associated with this scenario’s instance availab= +le + during boot-time

+
+

-in

+
+

 

+
+

Perform the injection synchronously (inline)

+
+

-tdpc

+
+

 

+
+

Use threaded DPCs for asynchronous (out of band) queuing method +

+

-wi

+
+

 

+
+

Use work items for asynchronous (out of band) queuing method

+
+

-r

+
+

 

+
+

Remove objects associated with this scenario instance

+
+

-?

+
+

 

+
+

Display help

+
+ +

 

+ +

WFPSampler.E= +xe -s +BASIC_PACKET_INJECTION -? +provides help output

+ +

WFPSampler.E= +xe -s +BASIC_PACKET_INJECTION -l FWPM_LAYER_INBOUND_IPPACKET_V4 -v“ adds a dynamic filter (-v) at FWPM_LAYER_INBOUND_IPPACKET_V4 (-l) which references the appropriate callout.  This filter will have no conditions, me= +aning +it will act on all traffic seen at this layer.

+ +

WFPSampler.E= +xe -s +BASIC_PACKET_INJECTION -l FWPM_LAYER_INBOUND_IPPACKET_V4 –v -r“ removes (-r= +) +the dynamic filter (-v) at +FWPM_LAYER_INBOUND_IPPACKET_V4 (-l= +) +which references the appropriate callout.

+ +

WFPSampler.E= +xe -s +BASIC_PACKET_INJECTION -l FWPM_LAYER_INBOUND_IPPACKET_V4 -ipla +1.0.0.1 –ipra 1.0.0.254= +“ adds +a persistent filter at FWPM_LAYER_INBOUND_IPPACKET_V4 (-l) which references the appropriate callout.  This filter will have 2 conditions; +FWPM_CONDITION_IP_LOCAL_ADDRESS (-= +ipla) equals 1.0.0.1, and +FWPM_CONDITION_IP_REMOTE_ADDRESS (= +-ipra) equals 1.0.0.254.

+ +

WFPSampler.E= +xe -s +BASIC_PACKET_INJECTION -l FWPM_LAYER_INBOUND_TRANSPORT_V4 -ipla +1.0.0.1 –ipra 1.0.0.254 –i= +pp +TCP -in“ adds a persistent filter at +FWPM_LAYER_INBOUND_IPPACKET_V4  (-l) which references the appropri= +ate +callout.  This filter will have 3 +conditions; FWPM_CONDITION_IP_LOCAL_ADDRESS (-ipla) equals 1.0.0.1, +FWPM_CONDITION_IP_REMOTE_ADDRESS (= +-ipra) equals 1.0.0.254, and FWPM_CONDITION_IP_PRO= +TOCOL +(-ipp<= +/b>) +equals TCP.  The injection will be +performed synchronously (-in).= +

+ +

For a list of conditions applicable to each layer, ref= +er to Filtering +Conditions Available at Each Filtering Layer.

+ +

For a list of command line parameters for configuring = +each +condition, refer to Co= +nditions +for Command Line.

+ +
+ + + + + +------=_NextPart_01CEBF8D.D4965900 +Content-Location: file:///C:/0E5B2E2E/BASIC_PACKET_INJECTION_files/themedata.thmx +Content-Transfer-Encoding: base64 +Content-Type: application/vnd.ms-officetheme + +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 +cmVsc1BLBQYAAAAABQAFAF0BAACtCgAAAAA= + +------=_NextPart_01CEBF8D.D4965900 +Content-Location: file:///C:/0E5B2E2E/BASIC_PACKET_INJECTION_files/colorschememapping.xml +Content-Transfer-Encoding: quoted-printable +Content-Type: text/xml + + + +------=_NextPart_01CEBF8D.D4965900 +Content-Location: file:///C:/0E5B2E2E/BASIC_PACKET_INJECTION_files/image001.emz +Content-Transfer-Encoding: base64 +Content-Type: image/x-emz + +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 + +------=_NextPart_01CEBF8D.D4965900 +Content-Location: file:///C:/0E5B2E2E/BASIC_PACKET_INJECTION_files/image002.png +Content-Transfer-Encoding: base64 +Content-Type: image/png + +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 +sWPHByNs1qxZ86cLZYQzg/OWW275udiWsu3xD2avRMJnpxsHUxnhzCjM7WjW/8JMjExONzi7Hn8u +wTmTI311xOXPZmOEcyaC0wgnIDgRnCA4mQERl/HmvB1r8fPmDfeVMYU1povGz3M6az11NO/bNS03 +wjQe27491xXLydtj2XXI9tuGjLochezah3xMri9Cst7vervq9eZj6lG4ehsjkNpR196OOvTqY9M1 +5ba17E39gjFHLOvtqqf01o/N4MrR6tinZtRuXHDmtsUy8/dx3zqyMrzq5TfbfG/X/du35+PK/d6c +/zGQgV5vQ/s/OXK9+dzVr83Yv1xvez1dwRnbEz+L5yl/H8e8Y6T1bHDG9tbHO7+vb+uaspsB3e92 +AMGJ4ATBKTi//QY83ly/sH79+t+rposOtafU7tq160e6prhG1MSb+wmm7g41b8jH3R4jrRkwGQ7t +beg3wllPqe1ab9nWH43Hd21XRlh7Sm3XNuZ62iOcuR2xD7GsiKGu7SgR8+pBlx3BVB+HPDa5nfVj +y/oONsdn3FTfJrxeFJx9ju/Z+9frycdECLaXH/tbXkevyFBs337w4MFdXdtb71suP8K/bM+X2vet +Rytj+c0xHndcc+S0HZzNa/xMWe7vt5eb2z3ZlNr8vr2MvH+9jHr5W7Zs+fhMnocLIDgFp5gAwbmo +gjNDrXhfV3DWI2nNfcYCJm7PZZQ33R+L32fYZPi1R7zat9cjdfU2dEVcBEicO1kHZy431l89Zij3 +OW6P72NUKkfY2sGZARHxklGZcdTvHM4cbYvtL/v05bh/fTzbYTjRsiNA27EZI2zxs9jn+rhnQA06 +wlnfL45FM736hTqK248p6/xq6zn7cv2c1fsUj8l9im3oGuFsB2fZhp/tt2/l+b2zXkfep15uvcx2 +cOZ2Nds6bpmDBmfuax6XJnyXN/+JcSZvb/bvTAa3kU5AcCI4QXAKzo7gbF9AaJDgbALv7Bv/dnBG +kEXk1Y+tzic9uy39LijTHuFshdLYFMgYKaunnNbBWY9wxjbVoVoHZ25jjljmcnNabXuEsz0lOMR9 +c8psnuMZ+zPosquRuJFcZjmev9Yc14/n/mV05SjfIOdw5vHNWIz7NKOLZ5dTPyaet1xu+zmLkMvb +M8La+9R1Dmf7Oc7lZ/CGHEVv36c9qpqBOckI5/KuZQ4anBmPsV/VMV7RjGaPrSOPTXXsHhCcgOBE +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 + +------=_NextPart_01CEBF8D.D4965900 +Content-Location: file:///C:/0E5B2E2E/BASIC_PACKET_INJECTION_files/oledata.mso +Content-Transfer-Encoding: base64 +Content-Type: application/x-mso + +0M8R4KGxGuEAAAAAAAAAAAAAAAAAAAAAPgADAP7/CQAGAAAAAAAAAAAAAAABAAAAAQAAAAAAAAAA +EAAA/v///wAAAAD+////AAAAAAAAAAD///////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////9 +/////v///wMAAAAEAAAABQAAAAYAAAAHAAAACAAAAAkAAAAKAAAACwAAAAwAAAANAAAADgAAAA8A +AAAQAAAAEQAAABIAAAATAAAAFAAAABUAAAAWAAAAFwAAABgAAAAZAAAAGgAAABsAAAAcAAAAHQAA +AB4AAAAfAAAAIAAAACEAAAAiAAAAIwAAACQAAAAlAAAAJgAAACcAAAAoAAAAKQAAACoAAAArAAAA +/v////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////1IA +bwBvAHQAIABFAG4AdAByAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAWAAUA//////////8BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANDSBYHIv84B +/v///wAAAAAAAAAAXwAxADQANAAyADIAMwA3ADAANgA2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAABgAAgH///////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAACAAAAcVMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///////////////wAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////// +////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHQA +AHic7Jl5XBPX+v/PmZlsbEkmC6hoQoKAFSVsilYJiwpYkYDEtQgBA1KRIIte1+CC4IIsotaqxb2K +rbRVbK1LsLZqqxVs1W5WEJRVy4RNEMj8TgLeb+/9/n73tt/fX9/X6x79kJkzyTznzDnned7nmeoq +fu2RT4Y/Af9U/AEOTDQHMP9QB4dkKTwAsKFzE03Tr6vp/5T/VWUAiRgaw4lDn+YxZyGxkThIVkjW +SDZItkh2SNzBKQD4SCSSAEmIJEISI9kjOSANQxqONALJEWkk0igkCZIUyQlJhiRHckYajeSC5Irk +hjQG6Q2ksUjuSOOQxiN5ICmQPJG8kLyRfJB8kSYM9cNv6PM/5f9dooAO/ctEYzEdpKLPdLD6n13B +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 +AAAAAAAAAAAAAAAA + +------=_NextPart_01CEBF8D.D4965900 +Content-Location: file:///C:/0E5B2E2E/BASIC_PACKET_INJECTION_files/filelist.xml +Content-Transfer-Encoding: quoted-printable +Content-Type: text/xml; charset="utf-8" + + + + + + + + + + +------=_NextPart_01CEBF8D.D4965900-- diff --git a/network/trans/WFPSampler/docs/BASIC_PACKET_MODIFICATION.mht b/network/trans/WFPSampler/docs/BASIC_PACKET_MODIFICATION.mht new file mode 100644 index 000000000..7c93f0eb7 --- /dev/null +++ b/network/trans/WFPSampler/docs/BASIC_PACKET_MODIFICATION.mht @@ -0,0 +1,3944 @@ +MIME-Version: 1.0 +Content-Type: multipart/related; boundary="----=_NextPart_01CEBF8E.483FB900" + +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_01CEBF8E.483FB900 +Content-Location: file:///C:/CA3B2E2E/BASIC_PACKET_MODIFICATION.htm +Content-Transfer-Encoding: quoted-printable +Content-Type: text/html; charset="windows-1252" + + + + + + + + + + + +Basic Packet Modification + + + + + + + + + + +
+ +
+ +

BASIC PACKET +MODIFICATION

+ +
+ +

Overview

+ +

The Basic Packet Modification scenario will clone the = +packet +and inject it back to the same layer.  +Any requested modifications will be performed prior to the injection= +

+ +

All filters added sit in WFPSampl= +er's +sublayer (which is weighted just below IPsec's sublayer), unless otherwise +specified using the -sl +<SUBLAYER> command line option.  +All filters are associated with WFPSampler's +provider.

+ +

The following diagram shows how the code flows for this +callout:

+ +


+Figure A. Code flow for Basic Packet Modification Scenario= +

+ +

When traffic matches a filter at the specified layer, = +ClassifyBasicPacketModification() is invoked by the Filtering +Engine.  This function validates th= +at we +can perform the injection by looking at the pClassifyO= +ut +rights.  It will then create the +INJECTION_DATA which consists of the injectionHandle +and the injectionState.  If the injectionSt= +ate +indicates that we haven’t injected this packet before, then the injection +method is determined (default is asynchronous), and the appropriate triggerFn is called.= +   +At this point, the original packet will be blocked.

+ +

If the injection method is synchronous (inline), TriggerBasicPacketModificationInline() is invoked.  This function creates the CLASSIFY_DATA, +which consists of the data that was passed into the cl= +assifyFn.  Depending on which layer the injection = +is +happening, the appropriate performFn is called.= +

+ +

If the injection method is asynchronous (out of band),= + TriggerBasicPacketModificationOutOfBand() is invoked.  This function creates the CLASSIFY_DATA= + which +consists of copies and references of the data that was passed into the classifyFn.  = +Based on +the queuing method (default is DPC), the appropriate q= +ueueFn +is invoked.

+ +

Regardless of which queueFn is +used, each will call the appropriate performFn = +based +on the layer the injection is happening.

+ +

Each of the performFns are tailored to inject for their respective layers.  Each will get the required data for its +specific injectionFn.  +Depending on the layer, the offsets are adjusted on the original so = +the +whole packet is available.  Once the +offsets are adjusted, the original is cloned, and the offsets of the origin= +al +are returned to the original place.  Any +modification requested will be done to the clone.  Finally, the injec= +tionFn +is invoked.

+ +

Upon successful injection, CompleteBasicPacketM= +odification() will be called by the TCPIP +stack.  This function will show the +status of the injected packet.  +Additionally, any memory that was allocated from the functions above, will be freed and any references released.

+ +

Applicable Layers

+ +

v  +FWPM_LAYER_INBOUND_IPPACKET_V4

+ +

v  +FWPM_LAYER_INBOUND_IPPACKET_V6

+ +

v  +FWPM_LAYER_OUTBOUND_IPPACKET_V4

+ +

v  +FWPM_LAYER_OUTBOUND_IPPACKET_V6

+ +

v  +FWPM_LAYER_IPFORWARD_V4

+ +

v  +FWPM_LAYER_IPFORWARD_V6

+ +

v  +FWPM_LAYER_INBOUND_TRANSPORT_V4

+ +

v  +FWPM_LAYER_INBOUND_TRANSPORT_V6

+ +

v  +FWPM_LAYER_OUTBOUND_TRANSPORT_V4

+ +

v  +FWPM_LAYER_OUTBOUND_TRANSPORT_V6

+ +

v  +FWPM_LAYER_DATAGRAM_DATA_V4

+ +

v  +FWPM_LAYER_DATAGRAM_DATA_V6

+ +

v  +FWPM_LAYER_INBOUND_ICMP_ERROR_V4

+ +

v  +FWPM_LAYER_INBOUND_ICMP_ERROR_V6

+ +

v  +FWPM_LAYER_OUTBOUND_ICMP_ERROR_V4

+ +

v  +FWPM_LAYER_OUTBOUND_ICMP_ERROR_V6

+ +

v  +FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4

+ +

v  +FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6

+ +

v  +FWPM_LAYER_ALE_AUTH_CONNECT_V4

+ +

v  +FWPM_LAYER_ALE_AUTH_CONNECT_V6

+ +

v  +FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4

+ +

v  +FWPM_LAYER_ALE_FLOW_ESTABLISHED_V6

+ +

v  +FWPM_LAYER_STREAM_PACKET_V4                                     (Win7+= +)

+ +

v  +FWPM_LAYER_STREAM_PACKET_V6                                     (Win7+= +)

+ +

v  +FWPM_LAYER_INBOUND_MAC_FRAME_ETHERNET       (Win8+)

+ +

v  +FWPM_LAYER_OUTBOUND_MAC_FRAME_ETHERNET   (Win8+)

+ +

v  +FWPM_LAYER_INBOUND_MAC_FRAME_NATIVE            (Win8+)

+ +

v  +FWPM_LAYER_OUTBOUND_MAC_FRAME_NATIVE        (Win8+)

+ +

v  +FWPM_LAYER_INGRESS_VSWITCH_ETHERNET                  (Win8+)

+ +

v  +FWPM_LAYER_EGRESS_VSWITCH_ETHERNET                    (Win8+)

+ +

Command Line Usage

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

Option

+
+

Argument

+
+

Meaning

+
+

-s

+
+

BASIC_PACKET_MODIFICATION

+
+

Implement the BASIC_PACKET_MODIFICATION scenario

+
+

-l

+
+

Applicable Layer

+
+

Layer at which this filter will apply

+
+

-mmsa

+
+

MAC Address

+
+

Modify the source MAC address to the specified MAC address (Ether= +net + layers only)

+
+

-mmda

+
+

MAC address

+
+

Modify the destination MAC address to the specified MAC address + (Ethernet layers only)

+
+

-misa

+
+

IP address

+
+

Modify the source IP address to the specified IP address

+
+

-mida

+
+

IP address

+
+

Modify the destination IP address to the specified IP address

+
+

-mtsp

+
+

Port

+
+

Modify the source port to the specified port (UDP / TCP) or ICMP = +Type

+
+

-mtdp

+
+

Port

+
+

Modify the destination port to the specified port (UDP / TCP) or = +ICMP + Type

+
+

-sl

+
+

Applicable subLayer

+
+

SubLayer to associate with the filter= +.  [default is + WFPSAMPLER_SUBLAYER].

+
+

-v

+
+

 

+
+

Make the objects associated with this scenario’s instance dynamic= +

+
+

-b

+
+

 

+
+

Make the objects associated with this scenario’s instance availab= +le + during boot-time

+
+

-in

+
+

 

+
+

Perform the injection synchronously (inline)

+
+

-tdpc

+
+

 

+
+

Use threaded DPCs for asynchronous (out of band) queuing method +

+

-wi

+
+

 

+
+

Use work items for asynchronous (out of band) queuing method

+
+

-r

+
+

 

+
+

Remove objects associated with this scenario instance

+
+

-?

+
+

 

+
+

Display help

+
+ +

 

+ +

WFPSampler.E= +xe -s BASIC_PACKET_MODIFICATION +-? +provides help output

+ +

WFPSampler.E= +xe -s +BASIC_PACKET_MODIFICATION -l FWPM_LAYER_INBOUND_IPPACKET_V4 -v -mida 1.0.0.1“ adds a d= +ynamic +filter (-v) at +FWPM_LAYER_INBOUND_IPPACKET_V4 (-l= +) +which references the appropriate callout.  This filter will have no conditions, mea= +ning +it will act on all traffic seen at this layer.  +The destination IP address for all packets will be modified to 1.0.0= +.1.

+ +

WFPSampler.E= +xe -s +BASIC_PACKET_MODIFICATION -l FWPM_LAYER_INBOUND_IPPACKET_V4 -v -mida 1.0.0.1 -r“ removes (-r) the dynamic filter (-v) at FWPM_LAYER_INBOUND_IPPACKE= +T_V4 (-l) which references the appropri= +ate +callout.

+ +

WFPSampler.E= +xe -s +BASIC_PACKET_MODIFICATION -l FWPM_LAYER_INBOUND_IPPACKET_V4  -ipla +1.0.0.1 -ipra 1.0.0.254 -m= +ida +2.0.0.1“ adds a persistent filter at FWPM_LAYER_INBOUND_IPPACKET_V4 (-l) which references the appropri= +ate +callout.  This filter will have 2 +conditions; FWPM_CONDITION_IP_LOCAL_ADDRESS (-ipla) equals 1.0.0.1, and +FWPM_CONDITION_IP_REMOTE_ADDRESS (= +-ipra) equals 1.0.0.254.  +Packets matching this filter will have their destination IP address +changed to 2.0.0.1.

+ +

WFPSampler.E= +xe -s +BASIC_PACKET_MODIFICATION -l FWPM_LAYER_INBOUND_TRANSPORT_V4  -ipla +1.0.0.1 -ipra 1.0.0.254 -i= +pp +TCP -mida 2.0.0.1 -mtdp +35000 -in“ adds a persistent filter at FWPM_LAYER_INBOUND_IPPACKET_V4  (-l) +which references the appropriate callout.  +This filter will have 3 conditions; FWPM_CONDITION_IP_LOCAL_ADDRESS = +(-ipla= +) equals +1.0.0.1, FWPM_CONDITION_IP_REMOTE_ADDRESS (-ipra) equals 1.0.0.254, and FWPM_CONDITION_IP_PROTOCOL  (-ipp)= + equals +TCP.  All packets matching this fil= +ter +will have their destination IP address changed to 2.0.0.1 and their TCP +destination port changed to 35000.  The +injection will be performed synchronously (-in).

+ +

For a list of conditions applicable to each layer, ref= +er to Filtering +Conditions Available at Each Filtering Layer.

+ +

For a list of command line parameters for configuring = +each condition, +refer to Conditions +for Command Line.

+ +
+ + + + + +------=_NextPart_01CEBF8E.483FB900 +Content-Location: file:///C:/CA3B2E2E/BASIC_PACKET_MODIFICATION_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_01CEBF8E.483FB900 +Content-Location: file:///C:/CA3B2E2E/BASIC_PACKET_MODIFICATION_files/colorschememapping.xml +Content-Transfer-Encoding: quoted-printable +Content-Type: text/xml + + + +------=_NextPart_01CEBF8E.483FB900 +Content-Location: file:///C:/CA3B2E2E/BASIC_PACKET_MODIFICATION_files/image001.png +Content-Transfer-Encoding: base64 +Content-Type: image/png + +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 +4MlMUG5nWnm0U0lBKSgFpaAUlODJTFDeblp59FNJQSkoBaWgFJTgyUxQ3nxaaSopKAceO+cNQSko +N/id+ab2raAUlAhKQXlE08r379+bSgrKVY+dF+Hdyz/VHglKQbnG78y34XfmbROXglJQIigF5RFM +K4ufT6qdO/XKz/9WNfqD84c1X5f3t/Sm9us991fRz+aq9r2gFJRrBmXjc+2V7SIoEZSCkgeZoI7+ +5X9+WPPH07e39PzAgvJzmFSKxxVBSc/r2v/zWiQoEZSCEru8j3uX98cwmTyzy9uEcs0J5acwmXzk +cSQoEZSCEkHpsfMo93gSj4Jyxe/MWW3s8SMoEZSCEjyWVj6exKOgRFAiKAUlCEpBKSgRlCAoBSWC +UlAKShCUCEpBiaDcQ2dn3rm8wpXfEQQlglJQgqAEBCWC0oNIUCIoAQQlglJQIigBBCWCcseC8sqx +XTgWDhCUICgBAEGJoAQABCWCEgAQlAhKAEBQgqAEAAQlghIAEJQISgBAUCIoAQBBCYISABCUCEoA +QFAiKAEAQYmgBAAEJYLSAwkABKWgRFACAIISQQkACEoEJQAgKBGUAICgBEEJAAhKBCUAICgRlACA +oERQAgCCEgQlACAoEZQAgKBEUAIAghJBCQAIShCUAICgRFACAIISQQkACEoEJQAgKBGUghIAEJQI +SgBAUCIoAQBBiaAEAAQlghIAEJQiCUEJAAhKBCUAICgRlACAoERQAgCCEgQlACAoEZQAgKBEUAIA +ghJBCQAIShCUAICgRFACAIISQQkACEoEJQAgKEFQAgCCEkEJAAhKBCUAICgRlACAoERQAgCCUlAi +KAEAQYmgBAAEJYISABCUCEoAQFCCoAQABCWCEgAQlAhKAEBQIigBAEEJghIAEJQISgBAUCIoAQBB +iaAEAAQlCEoAQFAiKAEAQYmgBAAEJYISABCUCEoPIgAQlIISQQkAbCMoz0IgwAauPIgAABsBAIBb ++f+v30Qeixbu9QAAAABJRU5ErkJggk== + +------=_NextPart_01CEBF8E.483FB900 +Content-Location: file:///C:/CA3B2E2E/BASIC_PACKET_MODIFICATION_files/filelist.xml +Content-Transfer-Encoding: quoted-printable +Content-Type: text/xml; charset="utf-8" + + + + + + + + +------=_NextPart_01CEBF8E.483FB900-- diff --git a/network/trans/WFPSampler/docs/BASIC_STREAM_INJECTION.mht b/network/trans/WFPSampler/docs/BASIC_STREAM_INJECTION.mht new file mode 100644 index 000000000..51bdb3cd6 --- /dev/null +++ b/network/trans/WFPSampler/docs/BASIC_STREAM_INJECTION.mht @@ -0,0 +1,2597 @@ +MIME-Version: 1.0 +Content-Type: multipart/related; boundary="----=_NextPart_01CEBF8E.4008E8B0" + +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_01CEBF8E.4008E8B0 +Content-Location: file:///C:/0E5B2E2E/BASIC_STREAM_INJECTION.htm +Content-Transfer-Encoding: quoted-printable +Content-Type: text/html; charset="windows-1252" + + + + + + + + + + + + +Basic Stream Injection + + + + + + + + + + +
+ +
+ +

BASIC STREAM= + INJECTION

+ +
+ +

Overview

+ +

The Basic Stream Injection scenario will clone the dat= +a and +inject it back to the stream.  No +modification is performed on the data.

+ +

All filters added sit in WFPSampl= +er's +sublayer (which is weighted just below IPsec's sublayer), unless otherwise +specified using the -sl +<SUBLAYER> command line option.  +All filters are associated with WFPSampler's +provider.

+ +

The following diagram shows how the code flows for this +callout:

+ +


+Figure A. Code flow for Basic Stream Injection Scenario

+ +

When traffic matches a filter at the specified layer, = +ClassifyBasicStreamInjection() is invoked by the Filtering +Engine.  This function validates th= +at we +can perform the injection by looking at the pClassifyO= +ut +rights.  It will then create the +INJECTION_DATA which consists of the injectionHandle +and the injectionState.  The injection method is determined (def= +ault +is Asynchronous), and the appropriate triggerFn= + is +called.   At this point, the origin= +al +data will be blocked.

+ +

If the injection method is synchronous (inline), TriggerBasicStreamInjectionInline() is invoked.  This function creates the CLASSIFY_DATA, +which consists of the data that was passed into the cl= +assifyFn.  The performFn is +then called.

+ +

If the injection method is asynchronous (out of band),= + TriggerBasicStreamInjectionOutOfBand() is invoked.  This function creates the CLASSIFY_DATA= + which +consists of copies and references of the data that was passed into the classifyFn.  = +Based on +the queuing method, the appropriate queueFn is +invoked.

+ +

Regardless of which queueFn is +used, each will call PerformBasicStreamInjection().

+ +

The PerformBasicStreamInjection() will gather the required inject= +ion +information.  The data is cloned, a= +nd +then injected back.

+ +

Upon successful injection, CompleteBasicStreamI= +njection() will be called by the TCPIP +stack.  This function will show the +status of the injected data.  +Additionally, any memory that was allocated from the functions above, +will be freed and any references released.

+ +

Applicable Layers

+ +

v  +FWPM_LAYER_STREAM_V4

+ +

v  +FWPM_LAYER_STREAM_V6

+ +

Command Line Usage

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

Option

+
+

Argument

+
+

Meaning

+
+

-s

+
+

BASIC_STREAM_INJECTION

+
+

Implement the BASIC_STREAM_INJECTION scenario

+
+

-l

+
+

Applicable Layer

+
+

Layer at which this filter will apply

+
+

-sl

+
+

Applicable subLayer

+
+

SubLayer to associate with the filter= +.  [default is + WFPSAMPLER_SUBLAYER].

+
+

-v

+
+

 

+
+

Make the objects associated with this scenario’s instance dynamic= +

+
+

-b

+
+

 

+
+

Make the objects associated with this scenario’s instance availab= +le + during boot-time

+
+

-in

+
+

 

+
+

Perform the injection synchronously (inline)

+
+

-tdpc

+
+

 

+
+

Use threaded DPCs for asynchronous (out of band) queuing method +

+

-wi

+
+

 

+
+

Use work items for asynchronous (out of band) queuing method

+
+

-r

+
+

 

+
+

Remove objects associated with this scenario instance

+
+

-?

+
+

 

+
+

Display help

+
+ +

 

+ +

WFPSampler.E= +xe -s BASIC_STREAM_INJECTION +-? +provides help output

+ +

WFPSampler.E= +xe -s +BASIC_STREAM_INJECTION -l FWPM_LAYER_STREAM_V4 -v“ = +adds +a dynamic filter (-v) at +FWPM_LAYER_STREAM_V4 (-l) which +references the appropriate callout.  This +filter will have no conditions, meaning it will act on all traffic seen at = +this +layer.

+ +

WFPSampler.E= +xe -s +BASIC_STREAM_INJECTION -l FWPM_LAYER_STREAM_V4 –v -r“ +removes (-r) the dynamic +filter (-v) at FWPM_LAYER_STRE= +AM_V4 +(-l) which references the +appropriate callout.

+ +

WFPSampler.E= +xe -s BASIC_STREAM_INJECTION +-l FWPM_LAYER_STREAM_V4 -ipla 1.0.0.1 –ipra 1.0.0.254“ adds a +persistent filter at FWPM_LAYER_STREAM_V4 (-l) +which references the appropriate callout.  +This filter will have 2 conditions; FWPM_CONDITION_IP_LOCAL_ADDRESS = +(-ipla= +) equals +1.0.0.1, and FWPM_CONDITION_IP_REMOTE_ADDRESS (-ipra) equals 1.0.0.254.

+ +

WFPSampler.E= +xe -s +BASIC_STREAM_INJECTION -l FWPM_LAYER_STREAM_V4 -ipla +1.0.0.1 –ipra 1.0.0.254 -in“ +adds a persistent filter at FWPM_LAYER_STREAM_V4  (-l) +which references the appropriate callout.  +This filter will have 2 conditions; FWPM_CONDITION_IP_LOCAL_ADDRESS = +(-ipla= +) equals +1.0.0.1, and FWPM_CONDITION_IP_REMOTE_ADDRESS (-ipra) equals 1.0.0.254.  The injection will be performed synchro= +nously +(-in).

+ +

For a list of conditions applicable to each layer, ref= +er to Filtering +Conditions Available at Each Filtering Layer.

+ +

For a list of command line parameters for configuring = +each +condition, refer to Co= +nditions +for Command Line.

+ +
+ + + + + +------=_NextPart_01CEBF8E.4008E8B0 +Content-Location: file:///C:/0E5B2E2E/BASIC_STREAM_INJECTION_files/themedata.thmx +Content-Transfer-Encoding: base64 +Content-Type: application/vnd.ms-officetheme + +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 +cmVsc1BLBQYAAAAABQAFAF0BAACtCgAAAAA= + +------=_NextPart_01CEBF8E.4008E8B0 +Content-Location: file:///C:/0E5B2E2E/BASIC_STREAM_INJECTION_files/colorschememapping.xml +Content-Transfer-Encoding: quoted-printable +Content-Type: text/xml + + + +------=_NextPart_01CEBF8E.4008E8B0 +Content-Location: file:///C:/0E5B2E2E/BASIC_STREAM_INJECTION_files/image001.emz +Content-Transfer-Encoding: base64 +Content-Type: image/x-emz + +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 +UgAA + +------=_NextPart_01CEBF8E.4008E8B0 +Content-Location: file:///C:/0E5B2E2E/BASIC_STREAM_INJECTION_files/image002.png +Content-Transfer-Encoding: base64 +Content-Type: image/png + +iVBORw0KGgoAAAANSUhEUgAAA5wAAAJ9CAYAAABZ3X1CAAAAAXNSR0ICQMB9xQAAAAlwSFlzAAAO +xAAADsQBlSsOGwAAABl0RVh0U29mdHdhcmUATWljcm9zb2Z0IE9mZmljZX/tNXEAAGxeSURBVHja +7d0LsF13fdh7SZZsA8IcywbLIEsKsS3ZwLUk7FhYCnYUxdW4RrFxNMIYYVWkihFYodckcp4uNwQr +IQmZlpTiENwhyRAExJB24t7eFpGEjnObpL6PBEOSG3rnJjhzp62H0Bvj577/38n6nf7199rnIZ33 ++ZyZz0hnP9Zee+2to/09//9aa9n73ve+ZQAAADDdbAQAAAAEJwAAAIITAAAAwWkjAAAAIDgBAAAQ +nAAAwGzZu3fvW5cvX/7MTTfddPDEiRMrbBMEJwAALEAHDhy4fsOGDV8sgff8smXLBlddddXHDx8+ +fEX5+/K5WqcSmndFcO7evfsH9+3bd9ZMPc769eu/UB7nuXjeafv27b9w7Nixl53psrdu3frPy7Kf +Ldv3tRNty3KbN5bX4GSuy/XXX/+Te/bsWbkQ3j+33nrr7WW9ny7vmwcvv/zyVVO5T3md/8FMvL7l +/XtZWf63prJOghMAAKbZ/v37b46wq4MrlA/pn5vL4JlqcOaI6FQDtXx9q33uGZ1n+vwnG5yHDh3a +Vm73VLsO5Xm8YTAYLM84m+n4PsPX6unLLrvs8+V9c/ZsB2e1fd6Tr5ngBACAOXbs2LG1JWyebkf1 +IkK3bdv2wEIKztMdEc3gLPfZHnGXy4ngns5Qmcy6Z+TG6xKxWkL1O+p1WkzBOc2P/wNtcM4UPzgA +AGCSclTw/PPP/7MSXavGC4o1a9Y8Vm5TT7ldF6N25c9tEa3XX3/9T5RI+uWcEhpxdOedd35Xud+X +436XXHLJlw4dOnRlfZ+4zY4dO342RgG7aaQ/kUHVRtbx48dX79q161jeNta5rP+dsX9n3rYZHRwN +yIi3LVu2fDTvt27dun9fQu7auG684KxHOONx6+cfy8jnEutVnsP9zfLXx3WbNm36VGyPHKk8evTo +ZSXkP1o939FpszlCV49qtjHajnzG8y5/fzLCuKzfj3f7u46OFsbycrvn4+QvE2J9c+Q1lxejgOX6 +kToeY1ndNOvnYlvffvvt31MuO5LrGfcpz+f8vuAs22ZrjC7G43av7zPtNOG+abhD1nukDssLLrjg +yzH1u9vOrynLe2euU7V9rivvsdfHOtQRHKOeZfuPPfeRkZE/u/nmmw/me668Xr8R1912221/r6zH +n1SP89qx94sfHAAAMDndh/Vn69Br1ZFXy0jNeOybltqKUI3gGO8+JTreXEdkBmcddU1cZAj1BmcJ +hz/OgKmViNgQUdg3pTaeW0ZjHaW1CJEIpVzPCKfy3N69fv3638mgrYOzhNNL+9YlrytxN7YvaQbe +ODE9Fpz15bGt9u/f/71tgNWx18XgU8Ouz3iczOuZgTssOPvuU27/ui7sf6C+T3m+f3+89aqjPMV9 +y3V3TyY4u9H8J/vWae/evd8XzyODs+d9OxbFfnAAAMAkZUwOC84YDSsfuEcjoATB9giw7oP72GXx +wT7jMb7vwupPIqwiMGOEKkdSY5QzPrhncNZhl6NuXbitrIOzLOO8fMwMxVxm3+3zucSBeCJGYhQw +R8rycfLot8P24cygjPtE7Bw9evRV8bgxSpnrUpxTB2d7oKU6OA8ePHhDBFX3nDfkyGjeJ75vR5Kb +oHvBlNoMqHLZjfFcYhkleP9d95ijl3Xrm8/x3Hj97rjjjptjm+Zzy21UBfTTOVJ75MiRyzMeY8pq +PH7Zhh/p3jenRGpfcEb8xXui2xbP5rTXNjjb9c59MHO98/q8fzzGzp07j3fr/IIptbkOufzyXv/h +HFHNkdm8LG+TwXnttdd+KN4v3fsn1+FFghMAAKYgRzgjBPv2fcswbA8gVD6Yn4gP/3Vw1repr6+n +0HYf3M/uW25cluHTBmSJh6uHjbqNF5x9o4PV6NwpwVlPwc1gzumtsZwYuWyPZhvB2U5RjfUvz+WS +dkptWcY7J7sfZo7mNhE4NDgjyHou63Nuhu4FF1zwlXq0tQ3OjMu+x2kDc1hw1tNZ6+X2BedE611d +/6J2e00mODMmI4DzNDuTuU35+lvBCQAAp6Eb/XrBQYPiNClx0KBuP82n6xHO+j6xD92ZBOcURjhf +1Y5w9kTHKfteRlh1R+A9ZYSzNVFwlufwplhGLrcd4czlxP2uu+66490o3g92UzTHgjOX045wxoGB +Yr/OvXv3vn2CUcdTntuw4Czr/n92635j3/lLu5h9pjzGbXH9sBHO2Q7OidY7ro99KusRztgfsx7h +jJHJ+D62T/cLjLF1KM/7AzkSnSOc9WVxP8EJAADTLCNp2GlRMh6HXd83WjnZ4Byyj9/39O3DOWw9 +MhQzLuvLYypnFYetc+rgHLIuF7TLbZcRoVx8LPbf7A6a9GxOhW334ex7rLjuiiuu6H1uuZye59bu +w3luG97tspp9YXtPgzOXwZnf96z3Kbdv9+Hs2/+zbx/OCfYrHQ1MwQkAADMgptZ2U0bro9CO7VvY +Rmk9GtoXnN1o5XN9o6J1cMZU3rLsn8tl902HrY9S2xfHGZxxfR2lee7L9siwbXCW5/2FNvbi+ZX7 +rcuR1PqARTEaVu7zu919zmmPrNptm5EuvF9wlNr64EC5D2S7jrH/ZLnPrRk93XP7VPvc+oIzt129 +L2i9bWMktgTup+p1qE8DM5ngbI8w234/XnBW+6X+QHsqlTwKbbPe76mOFvzDGct5lNrqtf9Ubr84 +MFHfUWpjn8x6+8cy8uBM3eslOAEAYKEbtm8oS0fG42ycO3M6efEAAEBwMo/Vpx/JEU/BCQAATGtw +5nk5bZOlJU+rEtNd8wA+ghMAAIAlzUYAAABAcAIAwGzbt2/f3htuuOG+4h8D4ztw4MCu+rygfogA +AMA44ryU1160d7B97S3AOLa+/MbByOoL/suyZa8+V3ACAMAkg/Puqz42eM+WfwGM49CVPz9Y89JX +CE4AABCcIDgBAEBwguAEAADBCYJTcAIAgOAEwQkAAIITBCcAAAhOEJyCEwAABCcITgAAEJwgOAEA +QHCC4BScAAAgOEFwAgCA4ATBCQAAghMEp+AEAADBCYITAAAEJwhOAAAQnCA4/RABAADBCYITAAAE +JwhOAAAQnCA4AQAAwQmCEwAABCcITgAAEJwgOAEAAMEJghMAAAQnCE4AABCcIDgBAADBCYITAAAE +JwhOAAAQnCA4AQAAwQmCEwAABCcITgAAEJwgOAEAAMEJghMAAAQnCE4AABCcIDgBAADBCYITAAAE +JwhOAAAQnCA4AQAAwQmCEwAABCcITgAAEJwgOAEAAMEJghMAAAQnCE4AABCcIDgBAEBwCk4QnAAA +IDhBcAIAgOAEwQkAAIJTcILgBAAAwQmCEwAABCcITgAAEJyCAgQnAAAIThCcAAAgOEFwAgCA4AQE +JwAACE4QnAAAIDhBcAIAgOAEBCcAAAhOEJwAACA4QXACAIDgBAQnAAAIThCcAAAgOEFwAgCA4AQE +JwAACE4QnAAAIDhBcAIAgOAEBCcAAAhOEJwAACA4QXACAIDgBAQnAAAIThCcAAAgOEFwAgCA4AQE +JwAACE4QnAAAIDhBcAIAgOAEBCcAAAhOEJwAACA4QXACAIDgBAQnAAAIThCcAAAgOEFwAgCA4AQE +JwAACE4QnAAAIDhBcAIAgOAEwTmzwXn48OHXHzt27OLZ/iFw5MiRy+Ox/UAEAEBwwgIPzgy8Oi7j ++/KPcbB58+bfnKl/7PF4bVju2LHjg/G44dZbb33bZKK4Np2BHNslTPb2x48fXx3rMJX7zAe53lPZ +dn2v3XSbq194AACCE5iG4IwwWrNmzZ9m4GVgZoDMdHDGsuMxMlxifer1uOmmm941UfTU694+h2n4 +YTS6vMncdteuXT9ar8P69et/L9fhdIJuNsV2nupr3b520/GLh/o1m433HwAgOIEZCs467vKDfcZn +jhbO9Af+CJ1YfobYVB8zb5/3icibyujoRLZu3fqxMNHtbr/99r3DtmMddBMF9EIKzva1m85fPGSE +TuaXDgAAghPmYXBGSLUjcSFCLafY9kVIXtc3spUjeX3X9U3bzcvifnF5TqeNdcrbtiNfGZp5XTsK +mSONbaiMt965zL5Rtr7bt9NmM9jisdvtWD+v+DMfox71zL/Xj5HPb9j03Fy39vp6m/WNrPbtH9sG +Z3u/iR5notd5vPvF7fMXBbHNct2GTU+u32PDHnu89QMAEJwwC8GZoXbw4MEbhgVNHSHDpq9mENSj +fHUExgf+HMFqRx/rka2MnvZ2GWq5Xvk4GW952/h7PJeMlzo8xlvvvmnFeV0bs+1zzFivL4/nUcdW +3/OqR5Az/PPyWF59WT5OLjOeY7u8+pcG+Xj1vrAZw+3rkMtsg7Net3rbxOPk88plZdjX4Zjq0eG+ +90Fst/ay3N59v/Bopy23v1jIZbW3G/YeBwAEJzADwRkf/ifaP7H9wJ/fR1TUkZAxmJfFh/uQl9fx +En+PiIzQaIMzLqvjJqdr5vcZVBljdbSNtw/nROtdT3eN20as9AVnvS6xnm3Y9gVexmg+r/gz1qEe +Qa6XF5fXgVpPz814q+Owfj65Tev7x3379tFtY21YcNah2cZbG5z5fT7H9pcLdQTm9fFnfXm+Rn3v +v3a71OuUr0H9uPX1dSgDAAhOmMURzmFTH9sP/DntNacqtpGSIRjfZ/yEel/RiIv68dp99/r2JcyQ +i2VmKEfUtGHUBthk1ztHUWOZ8fd6CmYdnDmKWa9bXNYe2bcOz5xi27cPZx3C9WNmIMayc9S2Dd+4 +fUZrbvc2HjNQ618u5HbOyGuju429WJdct3xebWDmiG47etsus92vNZbbN9I97P3X3n+8dcrl1uvl +P1cAQHDCHARne3Cdet+/+gN/Tj3NEawcQarDrh6tqmMloqlv1GsywVmHWUZfhlzfPpzt6O1E653R +WY8E5vrUy+nbTzO3VRvtGbHt6FxfcLb7yLYBnTIgcxvkaGCudxuc9WO126jdzsOCs163drl1cPaF +f8rbtyPVGYSTDc6++w9bp3o5ghMAEJwwB8FZj8RlNETM5AF72g/89UhaPZJX7+OZy66nvWaUZfzV +95lMcIZ2Wmh7VNu+kbWMjKmsd4ZiG0kZzfX01dxfNJaZQRiX9T1G/bzaAx61zzWDOMK27/yi9dTW +OuTnMjjryM+R2fbAPvm8cmp1HuW2XlY855yy265De//29RCcAIDghHkUnBEC7UFe6oPmjLcPXbtP +YH6wb/ffi0iqYyYfr93vc6LgzBBs98cbtg9n33TWYetd709YT2fti5W+7RXbqj3ITztS2h7oZ7zT +zvQdFKgv9IZdPxfBGd/3HdBnvG1QT/tt79u3DsO2S9+BjAQnACA4YY6DM6MzYi4++MeH9Tzya1wX +IZUHdcnb5qhUjubV10ek9S0nojOPkBrq/STz8jxITy6zPaVJO4JWX9dO4awfezLrnSOSeX29/Fxm +vaxc51xWvR3z9u2+qhlseb+4rt2+tXqdUq5XnmYln2se6TWvb78P+brUUVtv5/b7vnXL5bYHDaqn +ZNfvgVSf1iTum9fnQZVy+9XPadg6tNul3ee2fT/lek7mXKoAgOAEpjk4F4o6OJ1Xce5F+PUdxAcA +QHCC4Fxw8kA5RqrmzQ/pwURHOQYAEJwgOBeEnCqZ0zmZWzkt1usBAAhOEJwAAIDgBMEJAACCE+Z5 +cN5zzz0bb7jhhn8MTE78m/GfMAAsjeC89qK9g+1rbwHGsfXlNw5GVl/QH5yjH6KXLRvcB0zodWed +9dStt9560H/CALD47du3b2/5rHyfX7jPH2984xvf/4pXvOJ/v/766/8n22N+OXDgwK4TJ06s6A3O ++CA9ACb01rPP/qbgBACYGzfeeOPdy5cvf2bv3r376rhh/hGcIDgBABaM++6776KVK1d+I065t3r1 +6r/evXv3y2wXwQmCEwCAM7Zly5YHli9f/mye533nzp3v37Nnz0rbRnCC4AQA4LQdOnToqhKbT2Vs +hpUrVz554MCBDeXvy20jwQmCEwCA07Ju3brfKcH5XB2c4corrzxx+eWXr7KNBCcITgAApixGN7/t +277tf9m4cePJEp6PrFq16m/Xr1//O/F9OHz48CVGOQUnCE4AAM7IsWPHNoyMjHy1BOY5tofgBMEJ +AIDgFJxCAgQnAIDgRHCC4AQAEJy2ieAEwQkAgOAUnIDgBAAQnAhOEJwAAIITwQmCEwAAwSk4AcEJ +ACA4EZwgOAEABCeCEwQnAACCU3ACghMAQHAiOEFwAgAITgQnCE4AAASn4AQEJwCA4ERwguAEABCc +CE4QnAAACE7BCQhOAADBieAEwQkAIDgRnCA4AQAQnIITEJwAAIITwQmCEwBAcCI4QXACACA4BScg +OAEABCeCEwQnAIDgRHCC4AQAQHAKTkBwAgAITgQnCE4AAMGJ4ATBCQCA4BScgOAEABCcCE4QnAAA +ghPBCYITAADBKTgBwQkAIDgRnCA4AQAEJ4ITBCcAAIJTcAKCEwBAcCI4QXACAAhOBCcITgAABKfg +BAQnAIDgRHCC4AQAEJwIThCcAAAITsEJglNwAgAITgQnCE4AAMGJ4ATBCQCA4BScIDgFJwCA4ERw +guAEABCcCE4QnAAACE7BCYLTDxEAAMGJ4ATBCQAgOBGcIDgBABCcghMEJwAAghPBCYITAEBwIjhB +cAIAIDgFJwhOAAAEJ4ITBCcAgOBEcILgBABAcApOEJwAAAhOBCcITgAAwYngBMEJAIDgFJwgOAEA +EJwIThCcAACCE8EJghMAAMEpOEFwAgAgOBGcIDgBAAQnghMEJwAAglNwguAEAEBwIjhBcAIACE4E +JwhOgAkdOnTo+ptuuuld5YPYS20PQHAiOEFwMkW333773vhA3SeuG3a/I0eOXB63iT/n8D/Hi9t1 +Pnjw4A3Tsezjx4+vLss7MtnlxbqU9/DbpnMdZktZ7zumElX1tikfTJYvlNDLZZb37KbJ3mfz5s2f +Kc/x+XLfa+K5xraK13mmnjeA4ERwguBcVMoH6s+W/1wGfcp1vznsg3V8cI8P4nv27Hn3HIZSfPB/ +vl3v9evX/96ZBsHhw4dfH8vetGnTQxMtK8K8XY9du3b9WN4vg26+Rkr5ei7WOaJqCtvmuclsm6kE +b72sOvSm8b3+mXzPTna92+Asnu1e47P8/AAEJ4ITBCcTqEc4M5riA/lCGOGMUbZY5zVr1vxprPOO +HTs+mM+hBMLVZ7LsqYxwZrBdd911PxfbpKzPV+tYz+uLFYshOKd7hLNv+8zECOd0BKcRTkBwIjhB +cHL6H/xzlG55E3aj8ZlTWCMyY5Qr/h5/Vv9ZXTzetNy+6btxn7w+AiYvr0M2lxuPlY8bIZLBWcdd +jthmcEYc1cuNdYjL+qI7QiLXp3vMI/X6x/3ytnWIZjDVUzXz+lzHDPluNHR5+fNN+TwiYurlZcz3 +Tc+N9aq3Y3t9rnM3xXd05PDo0aOvjOty23XXv3RYcOa61etZx2UGZxte3XofaW/fXh/3y3WaaPvk +7fK553Oql1G/XvG659/b27TB2W2PI3Gb/Hu7bdrgzOeXz62+T65bO2W3e9+8qW+9AQQnghME55IP +zhxFzOmiOQJaT6ntAuP5YdNy169f/7t903YzDPum9pb3xIEMpVh2N1V27H65Du0IZz2lduvWrb/c +rld9fbteub7tlNqImGHTZrt1fz4vq4OiGr0blcvL++RzysvrAEvlOfxKz2hp7/TduL5+rZrHfb5d +j77gbNetft2rbXPKqORE613W8Ufa68t2etWQ7bOinVIbwdb33A8cOLCrXqeyzl9qH6dcd15fcOY6 +t/fJdegLznZKbXzft73z9vG+Kdd/Zdj1fu4AghPBCYJTcHaXR4Dk6FUbnBmMET/19Nz9+/d/bx2M +cV0JkY91QfKxiLPcBzLDsSzjR/P+MXKU98+IievjfvXjtKGTo1SxXvE4cdt6uTkKVcX0aBzkiGEb +nBmuuY4RphmnMYJVR2fGchuQ9Qheffu4PEdXM6ra7XjnnXd+V8ZhTt3ttuPzdfjl/XM963WK7TLe +fdrgzPXonmdupxVtcI633hGEMbJZTzvO1yEer53KXW2fsdCrl5/PPdexb53iNatf6/IevKVeZhuc +udx6SnbE8GSDs97eMZ06lxe3L8v82XwfxfX5Puoid15OsQYEp+0hOEFwMifBWV8+2eCsRwvb4MwR +zO5D+CDDMPeDzJHMvH97EKN2H876fvmBP+Oxb7nvq0Y4cxkZqm1wZszUo5f1dOIQYVWPqFZR+4J9 +FDOY6n1NY0pmbqd8Phl7GUntFOFh8ZijermN2lHQ7jabxwvOHD2Mx6yfQxucQ9b7s7neeXCn+jWJ +bZVTm4dsn7HQ6+7/XH3/YetUh1yOqua2Gxac9bYp9/+dOjAnG5y5vWPf0+qASivy+vx30b1mzznw +ECA4EZwgOAXnFIKz74ix9cFZ+kYi61HINjirfTxfOlFw1pe3t82gbIO03sczRtXq8OybUtsXnBF+ +MfrWhmcuKx9jssFZTfEcC7dqn9ZNOdJXXz8sONuprs1rcUpgDgvO+iBC4wXnROud17fBmfvLThSc +ffcfFpz1dNj2uQ8LznrbtIE52eDM78t6bJsoOHNfTlNqAcGJ4ATBKTgnGZwRchFrMWrZHkwop8zm +h+74sF0fuCdjtR6prA84dDojnDF9NGKunq7ZHsU2YzPXJe/bF5zDptTmY8TjjTNtd2w6aIZGX3B2 +U0+f7wuUZp/D3+umZ35sPgRnPWW2b73Hm1I7zvYZC732/rHcLurz9Zm3wblt27YH6im1uV26943g +BAQnghMEp+CcTHDmqF7usxlyBCsPKBSBlFNk6yCN4Os7qFB7AJ82OIedhzOiMGOvishBe7CiOvAm +OmhQ323zAEF961BHTD3Ntj1oUHv6ltznr2edT9lXsu/6uQrOidZ72PW5n2TP9nnBQYOGLT+nBc/X +4KxjuXl/3C04YfbFv9Hu36V/f4JTcILgZLbl6MtkLm9Pi5LTYnPkL49wGge7yeCMy6qD7ZxytNgM +075TquRpUdrzgvadhqU9TUjEbIRpXledVuXi+nm09+07LUp3Wo63tbetT5dSn1qlWYc76u3Vd9qP +VJ8WpR4prNcrp6vm49an6ai/z1OR1IGf65JTmtvv+9atPg1Kxl0esKdZ7yN96x3y3JrtqUEmu33q +5benL6lfr2bf3SP5y4d8/Pw+r6+jvz5dTd969J0Wpe/1aU8jk8sxwgnjK//WLstfSobul3pn/G8l +/h1u2LDh5PLly/OAZq+ZzX+D5WfD2ojd+rkNBoNpefx4brHsqZy3OLdz83N03gdnrveJEycmfdC1 +8vN263S9j8Z7baeyToITBCdT/43x63O6aX6gzqiMUc38e06H7Jt2ysJ5rXNEsj61CsB0fHAvP1Oe +bmcEXH755Z8rP3tGzmTZe/fufWuJzWdieZdccsmXSnBeO5s/v3bs2HF/efxne07/dduZhkr5f/Wu +eG67d+/+wX379p01UbCV/6v/pDmq+ug6RHCed955f3H77bdvn64YnvbQWrbsyVjn8jzfMJntFrFZ +ts23Lrvsss+X99HZZ/r4sbz45We9fcr/hb8Rr21Zp+tmKzoFJwjOpfgh4eIh0ylHg7IOzFp9EBgW +hvY0L14/YBp/obUtgzMiM35ZWT7Ij05J3759+y9MFFPTFWUzoUTJp+K5nH/++X8Wv6zL59U5oxHF +Es9vLNvq03fcccd3TxQ8JTb/OH6G1+sR27pYFcFZrntqOtZpvgRn/BIjtn3sxnCmr/uweC3vrR+I +3S/K+3f9bP2fKDhBcC5J9bTSnPJYHxionSYa39tuC09Oa62nrgJMd3AWZ1eh+GxEUYmGlXnbnFrZ +TpXMy+Oy+Hv8QjR+buV02gjXcv0Vffdp9+3MqarxGPn3GNnK28ffczplPTW1hMkL9hPN4Mzg7Y60 +/a027vJxhk0nzserl5+XtbfvW7d8zLIOF+RtynZ9Z6xT+bn+98s6Pp1Bl49RP9/cps3uElvb6bnt +OvRdn6OF9WNkRNbbvn6sNjjLbcbu1/d868epRyXr7TwsXOv7dbt+3B7bZ926df8+RoFz3XId2sfN +y4c9dt6/b/0EJwhOAGAGgzM+0Me+0CUUvxiheNVVV308gjPCpN4XM+R1XdidiLCLsMxQrUdKK2d3 +00u/XM/Aiem2JQCu7EJgWwRGWf6DOR22hM72GE2sHyPvGzFZlvdYLi/CJEYO6+CM+3Sne3pnjLjm +6GLcZteuXcdy2m+K2+dza6+P9Yrr2tHbCJp8vHbabI5wxrrdcccdu+vYqgJ4bCpzrFu97rlN4/IY +WS3fP1Xfp6zLP8iRxPXr1/+7drvXU4gjHmM94ngOebsYeS3P6WiGb7j++ut/MrdBG5w5nTVuU2+b +cv2NcX2OSnaXn9v9EuMH6uV3tx+bDtuF99jzysCslpOv93tivdoptRGasZ3r92gsI/YbjqjMdYrX +74ILLvhK3q57v0xqZFlwguAEAM4gOFt5kJ8MynZqaomIg12AnMjLIozyVEwZhnG/LVu2fPzYsWMj +OX20XVaOpmZw5m1KGI3G6BVXXDH2GBEJbVTVl0WgRmS0AdgXYHmbePw4cnvevjz3Dd1Rsr8V6xHx +0x2I7EjEXRucdZjGsuppsxHZ1bTZQS4v1vHVr371v6y3QVnOj3dB9al2m3bb71v5fOvtV5b3um59 +n8xt2x0xfnQqb47oZjzW61mvV3dguufrWBwWnO127+Lt7DY46+9jamy9neP6bj/iJ+vXPK6PmNyw +YcMX8rZx33LZrbG96+CMUc68/8jISDz3z+X65VTcZp1OWe/bbrvt701mqrDgBMEJAJxBcObIZHf6 +qVdFwNQHFYpIyiNN50hhF0ejMVg+U7y5/uCel2eU7d+//+b6fnGbbvkZY2dncHajU+dXyxoNsIzc +bqTv6QiU8vfR/fhKqHyhW4/bujA6ZR/OOnQySrsYHB1ZjeeWB/eJuIrrM/BylDSnYLbBmaOYeb8Y +8YyDJuXIY35fj+6W7bnxnnvu2di3D2euex3H3fZ7OrZfHIAp1nfbtm0fjbjK9eim3740XtcYra5G +DU+JxwzU8lr/cDyPGPkr9z2/m3act9mYEdsXnDmy2r2GvYEZ33cHb3omRkRjGzbb+boI+VjPej/N +GPEsy31VPI++fTjr4Oy7fx2xxYtynSJIc2pzWa8PdK/he+qp44ITBCcAMAPBmftwThSkKQ8Kk2EZ +QdrsQ3lKcGaklfD4ifpgMn3B2e4/mgGWoVjfrp1Cm4/Xfh+3yZHIdmSynQac4RgBt3PnzuM5YjZs +Sm27n2ZGTwRcfWyF+Hsdp2X53zZecOZ61JGbAV2/FnHwovJ459Ujts205lOCs5rqGst8OqIrt1Eb +mMOCsz6IUL3cNjjz9jkqW693HPQn16GexpvbbTLB2U2VjvvfV79nytfftsFZLyen+QpOEJwAwBwF +Zzfi9VQ3FfXNcT7bPKdlHrBlssGZo5L1skrM/UyEQzftc0aCM0cnu/1TT+ZtynPYHM+tnjKbI5AZ +et1I2fI6FCMM2+DMfScjSMvz/I6IoDiKavw9Rt9iNDCebz3q2AZnjGbGberpwHVw5v6b3fp+T31+ +0Qi/PMBOrkOca7nab3LOgjNHUSP2cmS2PrBPvV/q3r17vy9fpxxRz2VFXOZ96uDstum38v7d+yp+ +SfBMjGjGPpqCEwQnADAPgzN0o4AvOJ9lBuZkgzMu66a9vuCUXeX67+kOODPtwTlk/9QNzVFr2/V5 +Qxc7T9X7S2YYt8FZx3R9gKHu6LxP9Z3nNE+LsnLlym8MO2hQHZzd9vt3Q55Tht5Tfc9nLoOzmd46 +po6/vudVlv+6ev/M8Q4aVO7/b/u2S+6fKThBcAIAcyBiII4Au3Xr1o+N96E79h/ctm3bA810yCu6 +/QCPle8/2576JGImRvnqc1XmvoxxeSwjDoZT7xsZU1hjfWI/0nrabT5GHs021jsOJFTfrn28WOdY +Vr3O5bHfHlNPc7kRrrEfZO67Wj4XHYhl5OPkMqt9W9fF5bnsOIhNPreIzm5Zn43blsd5WT5GjLjF +5WU7/0q57y15nwjO88477y/iqL9xfaxfN9X3h+v1yPVtt1/K6K6fcyyrLOdH4rZ1lNe3z/OJ1s+j +rONHutdzdN/Y9vt63fJ1iwCM5xbL7TtKbbxe3Ws4tt55gKR8XrncWE79nol1jNc67hMjpHF53jaP +QpunUMnnHu+rGOXNZcRRbMsyPlU/Zuwn2p1LdbeDBoHgBABYjMG/YWRk5Kv1/psL/RcYOWra7ne5 +0AlOEJwAAIJzDmVsxujmtdde+6HJTFUVnCA4AQAQnBPK6a953tDF9FoJThCcAACCE8EJghMAAMEp +OEFwMutu3bfv7RdtuPRRYGLftuk1//bw4cMv9rMDJnbFtv/hk2u/ff1/vOjV6x9lfrhwwyv/eOXZ +q5686NvW/W+2x/xyzRu3/1IezVdwguBcVK7c8voTy65562DZ9/0iMIGVq0f+9siRI6/2swMmMUIT +54N8347BsvfvZD758e22wXzz3qsHL37Fy/7zslf/3WldBCcIzsUXnDfeO1j2ni8CEzh75KJvCk6Y +QnB+Zu9g2eduAcbzyzcOXrx2RHCC4BScIDgFJwhOEJwgOBGcIDhBcILgBMGJ4ATBCYITBKfgBMEp +OEFwCk4QnCA4QXAiOEFwguAEwQmCE8EJghMEJwhOwQmCU3CC4PSzAwQnCE4QnAhOEJwgOEFwguBE +cILgBMEJglNwguAUnCA4/fwAwQmCEwQnghMEJwhOEJwgOBGcIDhBcILgFJwgOAUnCE5AcILgBMGJ +4ATBCYITBCcITgQnCE4QnCA4BScITsEJghMQnCA4QXAiOEFwguAEwQmCE8EJghMEJwhOwQmCU3CC +4AQEJwhOEJwIThCcIDhBcILgRHCC4ATBCYJTcILgFJwgOAHBCYITBCeCEwQnCE4QnCA4EZwgOEFw +guAUnCA4BScITkBwguAEwYngBMEJghMEJwhOBCcIThCcIDiFBAhOwQmCExCcIDhBcCI4QXCC4ATB +CYITwQmCEwQnCE4xAYJTcILgBAQnCE4QnAhOEJwgOEFwguBEcILgBMEJghMQnIITBCcgOEFwguBE +cILgBMEJghMEJ4ITBCcIThCcgOAUnCA4AcEJghMEJ4ITBCcIThCcIDgRnCA4QXCC4AQEp+AEwQkI +ThCcIDgRnCA4QXCC4ATBieAEwQmCEwQnIDgFJwhOEJyCEwQnCE7BKThBcILgBMEJghPBCYIT5pNj +x45tKGG5XHCC4ATBieAEwQnT6vrrr/+JtWvX/tEdd9zx3YPBYLngBMEJghPBCYITpi04ly9f/myJ +y8Fll132+cOHD397jngKThCcIDiXiIMHD95QPgRsF5wgOGGmgjOsXLnyyTe84Q0/e/To0QvK908L +ThCcIDiXgJGRkb+oPwxs3Ljx5KWXXvpw/Ax75SUb/kBwwuSsOPvFz+S/JWC41atX/3X583nBCYIT +BOcScM011/yzvg8EJUS/dukVr/ltwQlGOGE6RjjDunXrfv/AgQM7yuVGOEFwguBc7GIq7ZVXXvkb +fbF59OjRbzOlFgQnTEdwxqhm+X/+jhMnTqywDycIThCci9Q999yz8eabb77rta997SfPOeecJ9at +W/fIG9/4xvefddZZT7WxaR9OEJxwpsG5atWq/7Zz586fOnbs2Evq6wQnCE4QnIvA8ePHz92/f/8t +11577YcuvPDCxyImr7766o/s27fvLeU//5G83caNG7/QxqbgBMEJZ+LAgQPXl/9rLu47F6fgBMEJ +gnPhTpPdsnv37nvj4D9xEKBNmzY9dOONN76nhOTmYfeJ69vYFJwgOGGmCE4QnCA4F4hjx46tjW0X +02RXr179+EUXXfTozp0774/TnUxlqm1oLxecIDhBcILgBMG59KYp7YlpshGXEZlbtmx5sJsmu3Y6 +H0dwguAEwQmCEwTnIhfTYWPaa5wfM/a1jD/j+5g+O5OPKzhBcILgBMEJgnPxTZMdiRHLGLmMEcw4 +4E+MaMbI5myuh+AEwQmCEwQnCM5FIPa5jH0vY5psnLIk9smMbTLd02QFJwhOEJwgOEFwLoFpsnFO +zDiKbBxNNo4qG0eXnelpsoITBCcIThCcIDgX4TTZOCdmnAczTkES02Tj73FZnC9zPq6z4ATBCYIT +BCcIznnq8OHD22PUct26dY/ENNkYzYxRzb5TkAhOEJwgOAHBCYJzqAjJCMrY/zICM0Iz9suM8FyI +z0dwguAEwQmCEwTnHImpsDElNo4gG1NkY6psTJPtzok5stA/EAhOEJwgOEFwguCc3WmyW2KabJwL +Mw72E9Nk45yYcRCgxfaBQHCC4ATBCYITBOcMitOSxLrFNNk4J2actiSmyc72OTEFJwhOEJwgOAUn +LILgjJiMabIRlxGZW7ZsebCbJrt2KX0gEJwgOEFwguAEwXmGYjpsTIvNabLxZ3w/n86JKThBcILg +BMEpOGEBBGcc0CdGLGPkMkYw44A/MaIZI5vz9ZyYghMEJwhOEJyCE+ZpcMapSWLfy5gmG6csiX0y +4zGX2jRZwQmCEwQnCE4QnNMwTTbOiRlHkY1psnFOzDi67FKfJis4QXCC4ATBCYLzNKbJxjkx4zyY +cT7MmCYbf4/LTJMVnCA4QXCC4ATBOeVpsjFqGaOXMU02RjNjVPOee+7Z6D90wQmCEwQnCE5g0sEZ +IRlBGftfRmBGaMZ+mRGe/gMXnCA4QXCC4AQmHZwxFTamxMYRZGOKbEyVjWmy3TkxR/ynLThBcILg +BMEJTDo446A+MU02z4kZ02TjnJhxECD/SQtOEJwgOEFwCk6YtMeL7Wed9a1XvepVvx/nxIzTlsQ0 +2Tgnpv+UBScIThCcIDgFJ0zJw8V7ii3F2uLVK1Y8E1NlnRNTcILgBMEJCE6YkseKDxV7inO7P+P7 +R6d40CAEJwhOEJwgOAUnS9wTxSeLg8XGYnM3ohkjm09O4aBBCE4QnCA4QXAKThg8UtzbTZMdKd5S +PFh8bYoHDUJwguAEwQkITpa4CMmPFLd002S3F/dX02SnQnAKThCcIDgBwckSnyb7UHFXN012Y/f3 +h4ZMkxWcghMEJyA4QXDCuNNk7+9GL0e60cyPTHKarOAUnCA4AcEJghNOmSb7YLf/5UgXmvd24TmT +jys4BScIThCcgOBkkXmymxIbR5Dd3E2TjSPLfrKbQjtb6yE4BScIThCcgOBkEXi0myab58SMabIf +WvZ358qcq3USnIITBCcITkBwsgA93k2TjZHLtcv+7rQlMU324Xm0joJTcILgBMEJCE4WiIe7abJb +usg82EXn4/N0fQWn4ATBCYITEJzMU49102Jzmuye7vtHF8j6C07BCYITBCcgOJknnugO7HOwO9DP +5m5E8+FlZ35OTMGJ4ATBCYITBCdLzCPdvpcxTTZOWfKWbprs1xbBcxOcghMEJwhOQHAyiyIkP7Ls +744iG9Nk45yY9y+gabKCU3CC4AQEJwhO5tE02YeKu7ppshu7vz+0QKfJCk7BCYITEJwgOJnjabL3 +d6OXI91o5kcWyTRZwSk4QXACghMEJ7M8TfbBbv/LkS407+3CcylvF8EpOEFwguAEBCdTFFNh85yY +m7tpsnFk2U92U2htI8EpOEFwguAEBCeT9mg3TbY9J+Zjto3gFJwgOAHBCYKTqXi8myYbI5drl/3d +aUvu7UY2bR/BKThBcAKCEwQnU/Lwsv9+Tsy1XWw+2MWn7SM4F4trrr3ul8qHggEwsRUrVz599OjR +V/rZARNbff7L/p/y7+Z5PztgYhdvvOSPLr/88nME5yL3WDcttp4mu1jPiSk4AQCWlvvuu++ikZGR +r+7evfs822OezxAQnIvDE8v+7sA+MXIZB/qJA/68pxvZXOznxBScAABLy7XXXvvB5cuXP7tr1673 +7tu37yzbRHAyAx7ppsnmOTHf0k2T/ZptIzgBABapGN1cuXLlN2L65urVq/969+7dL7NdBCenIfav +3DMyMurxLiQ/UtzSTZPd3k2TXernxBScAABLR45u5j6DRjkFJ6fhwVWrBptf/vLBww8/PGp9ic4L +ly8f3FWue8g0WcEJALAE1aObySin4OQ0RjUP3nrr4IknnhjkV/w9LsvRTttKcAIALDXt6KZRTsHJ +FHykGtUc9hXXxW1iBNQ2E5wAAEtF3+imUU7BySTEKUy2j4wM7rrjjlNGNYd9Ge0UnAAAS82hQ4e+ +ozTLT0a3XHvttb/4ohe96D/v3Lnzp+L7cPjw4deU+FxuWwlOKvefe+5gy/r1g0ceeWQw1S+jnYIT +AGApOnbs2IY4D2cJzHNsD8HJOKOa99599+DJJ58cnO6X0U7BCQAgOBGcjHryDEc1jXYKTgAAwSk4 +BScvEOfL3LJmzRmPahrtFJwAAIJTcApOxkY1733JSwbbN28ePPbYY4OZ/jLaKTgBAAQngnMJjWre +f999g9n8MtopOAEABCeCc5F6onjPLI5qjjfauTGC99xzvS6CEwBAcCI4F7qHi80XXDD40Ac/OJgP +X7G/aOw3GkfFfczrIzgBAAQngnNhjmoefNnLBnu2bx88/vjj8yI26684Km4cHddop+AEABCcCM4F +OKr54AMPzLvQNNopOAEABCeCcwGKA/LcMo9HNY12Ck4AAMGJ4FyA4rQjcfqRhx56aMGEptFOwQkA +IDgRnPN8VDNONxKnHYnTjyz0L6OdghMAQHAiOOfRqGacbmQxfRntFJwAAIITwTlHIsJuWESjmkY7 +BScAgOBEcM4DEV8RYSdPnly0oWm0U3ACAAhOBOcsj2pGdEV8RYQttS+jnYITAEBwIjhncFQzomsp +f+Vo5+bzzhs84n0hOAEABCeC8/RFVEVcLdVRzWFfjz322GD75s2De1/yksGT3ieCEwBAcCI4Jy8i +KmIqoiriylf/1/333TfYsmbNkh/tFJwAAIITwTnpUc2IqIgpX0Y7BScAgOBEcJ6xJ4xqGu0UnAAA +ghPBOd0ejn01L7jAqKbRTsEJACA4EZzTN6p58GUvG+zZvn3w+OOPK0ajnYITAEBwIjinb1TzwQce +UIhGOwUnAIDgRHCeuceNahrtFJwAAIITwTndHly1arD55S83qmm0U3ACAAhOBOf0jWruGRkZHLz1 +1sETTzyhAI12Ck4AAMGJ4Jy+Uc2HH35Y8c2j0c67XvrS0YM2CU4AAASn4FxwHjOqOa+/PvJP/sno +QZseFpwAAAhOwbmQ3H/uuYMt69cb1ZznX3HQpjh4UxzEaSGPdgpOAADByRIIzhjV3D4yMrj37rsH +Tz75pKJbIF9xEKeFPNopOAEABCeLPDhzVPORRx5RcEY7BScAgOBEcJ65OOLplhIpRjWNdgpOAADB +ieCcFnE+xzivYxzx9NFHH1VqRjsFJwCA4LRNBOc0jWquWTN6XkdfRjsFJwCA4BScgnNaRzXjfI6+ +jHYKTgAAwSk4BecZi1GuzSMjRjXnydfnP//58rIsG/Xe97538M1vfnPJjnYKTgAAwckCDc4Y1YrR +rRjl+trXvjajUfOrv/qrgxIOp1z24Q9/eNR0f8UyM9hq7eNP9BX3+cM//MMJbxfLjUg83a94nPrr +p3/6pwdf//rXR0PzHe94x4xso4Uy2ik4AQAEJwswOEdHNS+4YHR0aza+IppmKzhPJxqn+75nEpwT +bbfZGO18cNUqwQkAIDgFp+A8/VHNGNWara/JBOfJkycHl1122WiAxZ/xfY74hb4QjJHAuO1UonHY +44x331j3WIe8X4zY1iOcebsYlYxpsDmqWj+/r371q2P3z2WPNwKbI5z1c5+Nr3hfHCzrsmdkZPC4 +4AQAEJyCU3BORoxabSwRMVujmm1c9k1zzSCLcKwDL2MsoiuCcOfOnWPRlvEXXxF+EXiTDc7xHmei +4Ix1iNvFOtSjknVwxrrkc4rbRmDG7eMr/t439bZvhDO3Vyx7pvfhHPb18MMPDza//OVzOtopOAEA +BCcLIDgfLc5duXLwyCOPzEm8TDTC2bePZwRehlxGYdwuLssAjRHAYSOUfcE50eOMF5z1beK6iNf2 +uvh7G9VxXRupkwnO2dh3c6KveL/E++ZRwQkAIDgRnPP1HJtnGpwxchhhGYGZYZcjiON9zWRw9l3X +3i6/ciR1IQXnR37hF0bfL4+YUgsAIDhtD8E5n8+3OVFw5ghgxlqeGiSnk0ZsRnTmMvK+402n7QvO +iR7nTIMz1ieieNjyhk2pzaPSzoevPGLtXSMjo+8X+3ACAAhO20NwzuvRzsmcFiViLA+qE6OOue9j +fOVoZu67meE43nTavuCc6HGmEpx5v/agQRGc9ZTa/KoPVlQvO55TfJ+jt3M5wjnfzskpOAEABCcL +MDjncrRzsX0Nm5K7kL6chxMAAMEpOBfdvp0L/StPrTJfpsMuhlFNwQkAIDhZRMFptHNpfs3XUU3B +CQAgOFlkwWm0c2l9ffITn5i3o5qCEwBAcLJIg9No5+L+euKJJwYHb7118Jbzz5+3o5qCEwBAcLKI +g9No5+L8evjhhwebX/7ywYOrVi2I95/gBAAQnCzi4DTaubhGNfeMjAweX0DvPcEJACA4WeTBabTT +qKbgBAAQnLaH4DTa6WtRjGoKTgAAwckSDE6jnQvj6+TJkwt6VFNwAgAITpZwcBrtnJ9fTz755ODe +u+8e3LDARzUFJwCA4GSJB6fRzvn19cgjjwy2rF8/uP/ccxfNe0twAgAITpZ4cBrtnB+jmttHRgaP +LbL3leAEABCcCE6jnUY1BScAgOBEcBrtNKopOAEAEJyC02inryU6qik4gYXq9ttvf9NNN930riNH +jmyaww+HF5d1OHLnnXd+V/mAuNzrAghOwWm009ekviLeb7jwwkU/qik4z/iD5ruGieuH3bds57eF +Of6wvrde31if8dZ5Kg4ePHhDtw1eOpXbT+c6zIYSOpdHbMT6z9S2marjx4+vznWaiQA6fPjw62P5 ++XodPXr0lXPxGLt27fqR8vyeLwabNm16qNzmVXmfNFPboBaPWR7juViH8ucKPxsBwSk4jXb6Gvcr +Yj2iPeL9ySX23hGcU/9QnB94+xw6dOjqoT+k/vv9ls/ZD8oh6x4jNWe67M2bN392om2QcbR+/frf +bdchA6MLunfFtp6P74EuNp7vYmP5FLbN82XbXDNdwVtvn+59+dxU1mmyj9W9Vi9435T4+7GpPFau +d/v+6B7jdybzGOXr2bw8wzSed3u/srwvzWQICk5AcArOJRecRjvPbFQzYv2RJfieEZyn9Z/C2Ahn +iYjfjA+38edCGeHMD/XXXXfdz8X6rlmz5qv5HM40VCY7ile2wR2xHuWx/3TPnj3v3rFjxwe7GLu6 +Drq4bj5OVzyd4JzOEc6+7TNTI5x1CGbkZTzHZQcOHNg12ccb9rpO9jHiOVZxeVYdfu17qfvFxzUz +9f4RnIDgFJxLMjiNdhrVFJyzHh7vyg/Q9eXd1MDR+IwprBmZGaX1bdsprnW09k3fjdvX8RvLzsvj +A3m73LgsbpPTP6sP41fnusZldXDGiFMut9tX7vJh0V1PK83HrKM7RyvrabMZHlu3bv2V+jHj+lif +jI34MB/3jZHP3Gcurq+270sztvKx+6bn5u37rs/r4jEyCmNZGTgRx+3+gm1w1uuWf2+nhOay6+V0 +6/2mYVNI6+szJNvtE++9evvkuucyyut8/bCpprnsuKx9nLxvBl5MXa3Xrbx2v1y/hjkdtn5+ue3i +dRq23t3j9T7Gtm3bHsjHiNuWmPzZfP+W+98dj9UXfuVxPpOjyflcyuNvGzZdN6+Ly/J5xHZsfznQ +bcsj+V4TnIDgFJxLNiCMdhrVFJxzG5x5+fr163+vmua3vJ1S2zetNGOw+yD+gimGGYZ918coT35I +zumtuQ55v2EjnDmlNsKv73Hz+r7HzXhtp9TW+9vV02bracmxXvv37//eNuba5ed96m0al0do5XPo +W6ccTe2Zvvuq+vGa12rQfl8vsw3OvnVL5brzqm0zNrrXrfdXeh7jmoz6dqpojv71bJ9r2im13bTl +F0xT7Quzbvrp2O1yHft+MVDHV7V+K/pGL/P6WL8hr+s1zWOsGPYYXTyesj0iOjP82hHOekpt9x54 +rue1WVePVrbbod5WO3fu/Jm+95HgBASn4FzyjHYa1RSccxecGQnFj9aji22kxO3LB+6PxXXxZ0RZ +xlsbGRlnGVkZjhk7uc9b3j8DIkdZ+z40x4f1XG6sVyw7HzeXm9GRgZC3jwDNEcM6OOtwjXWMbVBP +m+1G4p6vAy9jtG+Es47UWJdYXj3qlbfttmPG3+j6RtjHNqi3Y9keB+rnk+uZ92/i6/m+EGuDMwMk +n2u+XtXrMRZk9XrHZc16r8gRxAypuH/EeWy/vpHCNjhz+Xn/Zp1u6dbpM/X7tPulxOh9hkVkPTI8 +leAcNsI5wWNsq0c/xxvhbN/T8R7J2O95D4zefu/evW+vXs/n8j1QT8uNKO3i/9n6vZz//gQnIDgF +J0Y7x74efOABo5qCc1aDs718MsEZIVTHW8+o1vI65vJDdH5IzpHMYQfwaUc46w/XOc0wpwL3Lbce +4YwP9fXy68fsDubyfDxOPW22nvabU2Hr5dVBV0dIbq92v8kMhdxOzbZakY+bU4Qzqtp4rA9Ok8us +g6Ue6ZtghHN5Pbqbj9MG50Tr3TfNNA8SNM72GQvOvvv3rNNocA7ZR3IsIuvXsB19HC9O6+Actt5T +eYw6QIftw1mP2rfvvZziW70Wd9fBWb8HcnS4Z/R4Rf24ghMQnIITo52Dxx9/fLBn+/bBwZe9zKim +4Jw3wTlstDGnxGawNVMI313HVx2c7T6VEwXnsFDMoIy46gvO/PBejzblaGe9nNwG7Yf+iNl4jDo8 +8zHzMc40OKujly7PyKoORtMbnH2h1MTF2GMPC8563drlTiY46/XuC8YuwJZPd3DWR86tn3s9FTXj +u3rPfKZ+fU83OKfyGOMFZx1+ebu8rOc90Buc+X392IITEJwITqOdE45qbr7ggsHDXnvBOc+CM0f2 +6tM71BEWozQxahMjnn2nCGlHKtsDDp3OCGdMUcz7ZXC2+4BGHOZBhPI5NFE14ZTauF08t2ra7u/2 +jXDmqFUsa1hw5tTTnFKbcnvVYVdPhZzr4KzXu/6lQa73sCm1Q7bPpjY42/vXU2ozACcKznrEsx5B +rE+TUh53c/18c9u2R4utb1Ov91QeY6ojnLlfaH0qleY9MKng7N7LuYwfN6UWEJyCU3Aa7TxlVPMJ +r7fgnIfBWY0kfiyPvFpHZUZYjB7mdfXRVdt9ICc6gE/POgz6znc4bLkZOxkyk3nMehS0PmhQ38GE +Ihjq83DW19cHDWqDc9hBjtp9JYddP1fB2a33c8PWq+/6fI3a6/oOGjRs+fWI80TBWY+c9r1ncl/Q +7sPSCw5ylAfhyeX3rfdUHqMvOIcdECjeTxmq47wHJhWccVl70KB8boITEJyCkyU42mlUU3DOljyl +Rjv6OOzy+rQoGZwxghgxl+f0zP0AM97yuhxpzKPF5of8+vQl9Qhn3ylK6suHnSakXv+8rj4dS71/ +Z3vfvsfM04G0t83TpbSnVhmyDi+tT/vR3rY+LUo7Utit15vyOeTj5vV9r1Xu61dPBc5zXPZ933dK +klxuniYkgzMPIlSt95uGrXf3+t7Rd0qT8bbPsOXXpyypt0t9ipA8pUv9WLGcfB1zO/adT7Q9JUye +YqS+bV6W610/Rn0Kl77HyHON1uuXj9lOK2/3B+15DxyJX2TkVN36+/r29TrUy8hQjX+P8/FcsYDg +RHAa7TSqKTiXuAzKmHoYH2KbKY9X59TD/BCdQdodXMgH3AWknm5ajyYCgOAUnEY7F8jXQydOGNUU +nAtKHZi1CNBuemLv1Nd2lIoF8J9TNeWzPTAOAAhOwWm0cx5/PfHEE4ODt946uKWss1FNwbnQ1NNK +uymPl+d17TTR+Ht9UCEWjpwWW09dBQDBKTiNds7z0c6HH354sPnlLx88uGqV101wAgAITgSn0c7p +G9XcMzIyeNzrJDgBAAQngtNop1FNwQkAgOAUnMy70U6jmoITAEBwIjiNdk771yOPPGJUU3ACAAhO +BKfRzun7evLJJwf33n33YLtRTcEJACA4EZxGO6dzVHPL+vWD+8891/YWnAAAghPBabRzekc1H7N9 +BScAgOBEcBrtnI7RTqOaghMAFqOjR49etm3btgc2b978m4cPH76ihMXyuVyfvXv3vrWsy2fuuOOO +7y4fwZYvlO1Yr/eJEydWzOd13bp16z8vPrZnz56Vfd8LTsHJLI52GtUUnACz4fbbb39T+bD62fjQ +n8rPvwPlw95Lz3TZN91005FYdgmLV0102+PHj68uj3tHrkusw1wHyGSVbXVxfOCPD85lnc+azH0O +HTp0fdznwIEDu2bqeW7ZsuWjsU779u07a7qWuWvXrnvb90sJnreX1/iVU3ke8XqX2z9dDMKNN954 +dK5jadOmTSeWL1/+XK5LeW3iNfp0+1zL633efHr/lfX+VKz37t27f3A6X+tx3u9ry/vgWL4PprJN +yte3utf8nL7vBafgZJZGOx999FGjmoITYFZE9JQPdM/nB/9UPsQ+VP5cMR3LLh/cv2OiGNmwYcMX +2/WID7Jxvwy68iH3x850nWbC4cOHt5X1erZb75WTuc+ePXve2cXN3WcaWt32+XRsn3pZVdCtmrYP +llUktsrrfO1ko7PbZs+cf/75fxb3K9+/fq5/wdAGZ37fPs9LLrnkS5dffvmq+fL+m83gjFHpsg2e +arfJVVdd9WDfKKXgFJzMw9HOuG77BRcY1RScALManNddd93PHTp06OoYlSzf54fsM/rwGiE0mZDI +YFuzZs2fxojfwYMHb4jRk/IB9t1x37x+OiJ4vgRnjPDF/aZjJDnjrds+Z9VxcOTIkcunM+QyOMv7 +5M54v8QI+fr1638nf1lQXr/zJ7Oc/fv331wi6ZkSbp+bTKjMZXBu3779F8pzvSaea6xzPM/y9+3z +ZQR+toKzG5UeDcR169b9+zvuuGN3/PveuXPnz8S/18lEuOAUnMzhaGf8PS6L6560nQQnwCwHZ/nQ +f3d8gO4+VD5bB2dM/9yxY8fP5rTCGEmrQymCZ+vWrb8c15XbfbBEzqa4vNzuR+KDaLl+c942PrTn +bXPabB1s9b58EawRTHXQRFSVWPneuE2Oet55553fVU9PjedQPnzfO2x6bkzdzXVor49Ii2XFY5TL +3xbLiGmpERyxPvnhOh63rOt5w4IzRxwjynI/xe5xVuQ2jceJqM5RySHrvaIO+Lw+fkEQ2zW2Tz06 +3G2fW7rtMzodNCOkmrb8mbg8l5HPvdwvpld/Jp5rTodub5PBWY9mxnLXrFnz5ViHsvw35/PJfQur +98x5GZvl9o/lOkd0lsdYF8uLaay5vdrHzmmc8R4pt/lobrsIw/Z9EPtgxmuZr1e8hnl5HeR5fbz/ +4759wRnfxzbs/m08lcEZ2z62cWzvbnt9pqzbJd2o/Np6+nH3/F9Wx1tun1y3+n3QTlmtR6679T7e +rncGZ7WdxtbliiuuOFHvKxmRGusUjxvPIff/rJfdrlf3S4LR1788zgVNHK6Nx4r71+/h7jW8onr/ +CE7ByVyMdob4+yO2i+AEmKPgjNHF+ICYEZDR0n0ofLadQlc+jP5KxFA3gvZsNQ13LF7bKbXN6Okp +02braIplR6i1MZpy+X2XRwiPt759y6vXY9j1fcqH6Z+PD+N9wRkjjn33KR/Ub43HySm1OSrZxcwz +PVMVP17FzjNDprGecnkGUz2lNu7fN225jsdunV7w3GPaa/W8nu6bPhtxFfetYu3T7XTUWE7ZViN9 +jxPLi/dH3+OX614Tj9VOcc3R0XaKZ6xD9/55wfYqj3Ew1q8LpN7pwW1wxnrH61Q/dnlN1nSv+ynL +yBAdtuy4Xz2a2j7P7v07dMpqt95P9S07gzNHPMvft0dgl22xLUOxJ/TG7hu/aKhud8o2i+WWP++K +Ed4Y8R02Kp2P3bN+5whOwckcjXa+9dxzByvjh1vxoeJR20VwAsxBcLYfENevX/+lHMGLEacY8Yvp +czHqV0+57T50PxdBl7ePkbh62RmcGWURhhEEMcqY02YjiGK0qQ7S7mAkL41Rubg81ilHGusQLrHz +47G8+KCft41AiNvG9Nxq9HRd3CZGa2M0LJ5P/D1vXz2fsfWM2+X33f6Gu+I+dSyOF5xbtmz5eKxH +bov4Pu7TBudtt9321pw23LPel2SMxTaIdShhszeuj4CL51xd9x3V9hkLzhhNq+8fzz1HxjJq6xAs +63NrPEZ+X9bpyu417A3OvG/EWoxw5/6ZMXIWj5WxGyOgcYCheK3jsXOd62XHbeL1zNuUwPn5LqRG +Iy1CM+6T07UzwK6//vqfiG0Xr3GMgMZt87JuGz3TxXNuj2dif8xYx3iuOeo63j6ccf/cFnVwRpTF +eyXer3GwptyusZ7xWuYIcNyui+Gx5xrXx/aJbZrrmc8xrssILN9viPdJXN9Nac31Hl326QRn2T4/ +2WyzZ/OyZpudHSOn8f14U3fjNjEyH8+xXvdy+zfEughOwckchufDxXviP6ZibfGW4sHia7aP4ASY +heDMfTjjQ2KJgN+Ny+pRv5weW4+C5shcPeU1lhNB0QZnFwejwVNPE4371+vTE55nDduHs2+/yXzM +eJycMlvtXzgaNjGtt5si/Nlyu9+L69t4rPeHjCmHdTi3gTnBCOfo9+1y2+DMxxiy3tfm84rIqKey +Vst+wT6cdXDmiGPsd5f3z/tF3NTBWR8xNu+XgTksODOyYt/O4l2xnBwZjOdSouirdczlY+UoZa5L +jlzGffK1ydtkALaPXY34nd1elssKVTyePeS5jrsPZ7s/bAZnux9qPnaOZoYcHYxY67bPMxHD9XTn +eh0ihHPb5XpHQHa/eDgl+tp9OKc4wnlOtd7fGmebnZPPIa4ftr9mjMDGts2p1NW6C07ByXzyePHJ +4mCxsdhc3FWU/0UGT9g+gnMJK//5jMSHYWBi5cPwlqkEZ05T7UYpfiQv60Zjnstprl04vuCgQrkf +Z9wvp6/WwVlPvS23Hd2XLS6L+ItRmm5Ebyyk6v1IM9a6WD1rvOCM0dh4zBz1jPVN3bTed8b653Or +R0TnMjgz2Mrntd71zuvrQIpprNWU3me67bOyLzj77p+jnmcanHkAoBwJzRHXWG6EWv1cct/fNji7 +o5+OLbu+T55y5XSCM0dLc1kZjfn4dfQNC87ch7P9tzNRcNYHForzTWYUxiyB9oBJsQ1jZLgdma23 +QwRbRl+z3sOC83vieUw1OMv63da3zcp2f2MuJ25T73Ma6xX7qMYy4hcN8dy7ke2T3boITsHJfBZH +rP1IcUsxUpSfXoN7i5O2jeBcYra8/jv+xao16/6/cy557RPA+FasXPVMjjROJjhzH84cVcr9OOsp +tLmPZjXF8Kw4+E0Eao6OVvG2op1S201ZHLT7e8aIT9wv1iEu70bDBhmYXYyM7SeaB/npC84I2Pq2 +9YGOYp0ySHP0rT7YzlwG53jrHR/s6+tju8S26tY9Y+2Zevu0+3DGAYFyemyOouboU0yfjRiYSnDm +MnLkMi7Lqa/1/pH1iG3IwGqDMy4ry/qT+rXJ++RBZ6YSnNddd93xfL71iF28p+O51ZEc19fP40yD +s33smDFQvs/R6g0x5TnXOZ5rbKO4PiKtPhJuvd7xPoh1GG+9Mzi7uB17/GZa8NDQ27Fjx/192yz2 +O873Q1nXL+TyYlpv7tva/XLhynpkuV43wSk4WWDi4EL3F+V/9cG5sY+J/T8F5xJx5ZbXn1h2472D +Ze/5IjCBs0cu+uaRI0dePdG/q24fxhfsw1kftbZ8cPxKfV0XO4MqlNqDn+zqO2hQPcqZIibi8vYx +4oNsfYTS+oA3PQcNOuUAJnGUzHadhuxvmVE751Nqc8Sy3Wewvr6EzM/U10fgZQjESFIbTO15OPuW +HyOquYzJBGfuLzjsQDv5GtRh1Fg1LDgjnPuWn489leCM920cmbV9vvV61mHYbc9faA58dFrBGY/d +t/9nfQTfdvvEFNo8pUy3r+Qp/07qx6jDsF7vDM72XJlV9D43XugNW+96+u44z210xLOO3YxSI5yC +E/t/IjgFJyzh4MxzZbZTOPs+XOd1EYj17bpzSr7gvnm7dj/Neln15fFBuZt2eXHfurb3G7acap22 +tdMC6+vysvrv7XX1NmpPBVPfpu/7nm20LZ9bBmcdfBOtd7MuF5/O9qmX354DtG/Z+ZrUp42p3ysT +neezfi592yOny/a9D9ppuNX746WTeT91z2ltvQ7t49XLbM+Nmvcddq7UvvfKsMce7z3ad/+J3gfj +rXff9m3PyRrTbHOa7lS3Wbt+7TLqdeveU2Pr1j7uBOshOAUn9v8UnAhOWOjByeyrRxpvvPHGu+vg +BASn4MT+n4ITwQmCk9OWU3TzlCe2CQhOwcmi3f/zlm7/z8dsG8EpOEFwMlsfpC/umxoKCE7ByaLb +//Ohbv/Pzd3+nwe7/T8fF5yCU3CC4AQQnAhOpnP/zwe76FzbReh7uih9UnAiOEFwAghOBCfTuf/n +h7ppt+d2+3/e103LFZwIThCcnN6H6Dh3aZyzlPmj/Fu5Yryj7SI4EZzM0v6f93Xhudj3/xScghME +JzPh4ovP+4utW8/5xrXXnvME88fZZ694uj6PJ4ITwYn9PwWn4AQE54Lzile8+PE///Nlg+efL//f +DJgvzj572UBwCk4EJ/b/FJyCU3CC4BScCE7BieDE/p8Le/9PwSk4QXAiOAUnghPByQLe//Mj83j/ +T8EpOEFwIjgFJ4ITwckC3v/zrm7q7cZuKu4n59H+n4JTcILgRHAKTmbevvJV4vKV7VGCBafghGnz +tW7/z7d0+39u6fb/fHgO9/8UnIITBCen6/Dhw9cWW/pOsyE4BSen2rJlywOrVq36bzt37vypEpkv +EZyCE2bco93+n3u66bc3FPfP8v6fglNwguDkdJX/P95ePiQ/e/XVV3/06NGjrxScgpPxg3P58uXP +lu0/WL169V/v3bv3bfv27TtLcApOmDUni3u7/T9HZmn/T8EpOJmCd/4r20Bw0hOc8QH6nHPOeWLX +rl0/evjw4RcLzvkdnPv379998ODBG5hdl1122b8qwflc/HtJr3rVq/7XEp3fV4LzzwSn4IRZ9cQs +7f8pOAUnk7Nqz48ORi66ZHDepdcMlt38vsGy7/tF5omVq0f+9rbbbnurD7Szb8eOHfeXD8mnfIAu +H5z/U/kA/eYSnH8tOOdncG7YsOGLGzduPMnsWr169dfLv5Hn638v4ZJLLvnd88477/8WnIIT5tRM +7f8pOAUnE/iHvzkY2bxjcOtb3jb4r//1vw4OHTo0OPvcFw+Wr1k/WLZuC/PA8rNWPbtu3brf94F2 +9l144YWP9X2ALh+sHz///HOeEJym1NI/pTZmBGzbtu2Bw4cPf/s999yz0ZRawQnzznTt/yk4BSfj +j2q+/JJLB7/9279dPjQ/P8ivJ554YvDm2w+UEL2uBOlnbStTak2p7UJz5cqVT+7cufNnyofoNabU +Ck5eGJwlLP+vm2666V1Hjx69IC+3D6fghAWh3f/zLd3+n18TnIKTMx7VHPb1r//1vx68Yv2lJUzj +tTppuwnOJR2cmzZt+ly8DnnEWsEpODlVhGXfEZ0Fp+CEBeeJbl/Pu7p9Pzd2f/9kd53gFJyMN6r5 +I72jmsO+jHYKzqUenGvXrv2jAwcO7Go/SAtOwcnkQ1RwCk5Y0L7WjXa+pRv93NKNhj4sOAUnUx7V +NNopODnlg/LaYdcJTsGJ4BScsIT3/7y/2/8zpkG94hWv+D927959b5y82w8TwbnURzXP5CtHO883 +2ik4EZyCE8EpOIG/G+G87rrrfn7nzp33X3TRRY/GUdNe+9rXfvLmm2++K46a5oeL4DSqOfUvo52C +E8EpOBGcghPomVJbfuiN7Nu37y1XX331R8oPv6+F+HtcFtf5YSM4jWoa7RScCE7BieAUnEICTis4 +WzHKGaOdMeoZo58xChqjoQcOHNjjB4/gNKpptFNwIjgFJ4JTcAKnHZyt2M8z9ve89NJLH479P+NP ++38KTqOaRjsFJ33B+eu/vmxw8iTzycqVglNwIjhhHgdnK0Y62/0/Y3n2/xScS3lU02in4OR9y7Zv +3/LxzZvX/sdNmy56lPnjda979b/Zs2fPSu9RwYnghAURnM0PzNH9P7ds2fJg7Pt54YUXPhb7f+7f +v/8W+38KzqU2qmm0U3ACCE7BCYJzBs/DefTo0c2x/+emTZseitHPdevWPRKjoQcPHrzBDy3BuVRG +NY12Ck4AwSk4QXDOwj/Sw4cPb4/9PTdu3Hhy5cqVT8b+nzfeeON77P8pOBf7qKbRTsEJIDgFJwjO +WXT8+PFzY//Pa6+99kOx/+fq1asft/+n4Fzso5pGOwUngOAUnCA45+aH7Vr7fwrOpTCqOanRzu83 +2ik4AcGJ4ATBOWOW+v6fgnPxj2oa7RScAIJTcILgnCeW2v6fgnNpjGoa7RScAIJTcILgnGfG2/8z +puYKTqOai+XLaKfgBAQnghME59z/oB7b/zPiM/b/jBiN/T8jTgWnUU2jnYITQHAiOEFwTovY/zOm +28b+nzH9Nvb/jJ8LMS1XcBrVNNopOAEEJ4ITBOe0idCMnwsRnhGgEaIRpBGmgnNxjmp+85vfHPzQ +D/3QYPny5eVtvmxw2WWXDf7pP/2ng+eff37GgvAP//APB5dffvngueeeM9opOAEEp+AElkpw1mKK +bUy1jSm3MfU2puDGVNz5tv+n4DyzUc03v/nNg3e84x2Dv/qrvzolBmc6Oo12Ck4AwSk4gSUcnD0/ +5NfG859v+38KztPfV/OrX/3q6MjmN77xjVMu/63f+q3Bd37nd44F54oVK8ZGIyNI4z75/cmTJ0cD +NUZHb7nllsFXvvKVsdtFzOYyfumXfmk0bOP7+rr4ezzWe9/73tHl7ty5cyx+Y/Q1bpejr+F0Qtho +p+AEBCeCEwTnAjNf9v9c0sF5hvtqtlFYX15H5bDg/PrXvz4amxmIv/Zrv9YblRmc5d9Pb3DG8j/3 +uc+Nfv/93//9gw9/+MOjf4/lxfd5uzOdhmu0U3ACghPBCYJzgZqr/T+XanBOxxFozzQ48+85+pij +nFMNzvZ2Mdo5E8FptFNwAoITwQmCcxGYzf0/l1xwTuMRaHNKbRtxk51SOyxYh02pnWxw5vexHjld +Nw5mlKOg9u0UnIDgtD0EJwhO6v8gTtn/86KLLno0YvTAgQN7Jrv/Z4yUHjx48IalHJwzcV7N3H/y +b/7mb04Jyi984Qtjt4ngjO/rI9rmlNq47g/+4A96Yzau+8u//MvR28XjTDU44++xj6gj2QpOAMEp +OEFwMmmHDx/eEtNtL7300odj+u3GjRtP7t69+97x9v+Mn1dx25tuuumdSy44Z/C8mhGRMW01p8bG +QXtCRGjumxkjjXl9jDLWo6L1KGR7UJ8PfOADo7eN0clPfOITQw8aNOzgQjGltp6yG+uVByVyJFvB +CQhOBCcITiYlRi5LTNwf+3+ec845T8T+nzfffPNd9f6fJUq/kOERo6Plz+VLIThnYlRzMhEa4dc3 +3XY2vyJE62m0b3zjG2fsdC1LZbRTcAKCE8EJgnOp/2cyEvt/Xn311R+J/T/Lfyxfe93rXvfr9YFp +QoyOliA9f9EG5wyOai6Ur3b09NChQ2NTf2fqa7GPdgpOQHAiOEFwUrnnnns2Rny2wRkiSC+94jW/ +vdiCcy5GNX0tjdFOwQkITgQnCE4aW7Zs+XhfbEaIrn/1Zb+3aILTqOa8+lqMo52CExCcCE4QnDRW +r1799QzMffv2vaU+tcqimlK7638cXPSq9YMvf/nLam+efP2jHzo2WH3xqwfL7vqXghNAcCI4QXAu +NnHalPHO3bno9uHc/88GazZcOfjJ971/Rg6O42tyX4899tjgyi3XDF6y48Bg2bv/Z1NqAQQnghME +51K0KA8a9O5/U0Ln7YPNV1294EY74wi3cQ7OM/2Kc3eGerlxCpX6spn6+sc/9YHBBRuvLPH/Sw4a +BCA4EZwgOAXnIj0tygyOdka4RcANi8a4Lv6cahi259U83a84LUsuJ5a/YsWKwS233DJ6qpaZOjXK +Yh3VFJyA4ERwguBEcM7qaOf3f//3D423CL247g/+4A8mFYYf/vCHx5YxE8EZI6Y5ahoRHPH5l3/5 +l0Y1BScgOBGcIDgRnPNxtDNi7h3veMfgsssuO2V5MZoY574s7/UXBGfEZD1dNm77Qz/0Q4P3vve9 +o7fNkdEMxWFTYGMZw0ZQ8z51cLZfEZz/4T/8B6OaghMQnAhOEJwIzvk42hkxFyOT3/md3zn4whe+ +MHZ5BOSv/uqvjl6fwRmBmBG6c+fO0T8jBH/rt35r9PKI1rjsK1/5ymgwxmV5u3qkNAIzlpu3j+vy +seO6WJe4X1xfP04do7HOcb/nnnvOqKbgBAQnghMEJ4JzPo52ZnB+/vOfHxtJjOiLWPybv/mbU4Iz +pt/GbSP4Mijzur4ptRGEMXIYXx/4wAfGrv+1X/u1U0YtT548ObqsiMf2unjMNjhjZPPQoUOjYXum +o5pXLKFRTcEJ4ztw4MD1mzdv/kz5c1f5EbHcNhGcCE4QnCzt4JyG0c4MzojL3CcyRjZjemxEXh2c +8fcccUwZfRPtwxnXx9TdXGZ924zICM52OX1TavO2RjUXXnDGh/niN9OOHTs+eOjQoavLh8Yz/nC/ +bdu2B7Zu3fqxsqwVk/iwevGuXbt+pKzDZ+M+d95553dNxzrMhrK9RqNoz5497z5x4sSKydyn3Pad +8VwPHz68bibXKUJturZjeX2Odet8Rb3MTZs2nYj3TjyX9vJ4Lfft23fW6T5mbKfly5c/e+ONNx7t +27bddvzMHXfc8d2zHaS5Pep/P3v37n37ZN8DZxh3a6+44ooT5fnffSbbV3AKTsEJglNwLsHRzjr+ +YhrtT//0T4+ONmZItsFZT7udykGD4vocqYzHqW+bR5/N4Ix1GC84z+RgREt5VHOug7MEwrby4fC5 +YtAqofIdZxoq5evZbnlnTRSb1W3HZNhkPO3fv/9752OEZhSVwHpooudahf6ny22fL9v52jN9TlW8 +joXgRKF2OrZs2fLRsszn6mV276Fn4vW66aab7mwvLz+7PlfWZeVMBWdEbb1OEWKxbUsM/thMh1j5 +eqrv386ZPueeUd43xnMqz+fW3Abl38S28ryfjscqVglOBCcITsG5VJ3GaGcdnLGPZkyDjdOOZNDV +wRnxl/t65rTa/MqpsHHbPNXKsOCMKbTxOJ/73OdGbxfLfP/73z92gKGIz3iMmOab+4y2+3Aa1VzY +wVk+wF5z8ODBG9avX/87EUIxcnKmIXT06NHLynO6fKLllFB5Z6xHBFusx+233753zZo1X4m/x33z ++m6dVsy3n3WnE5wR2WX7v346Aro87qcjuup4PX78+Op4fcvjvHS6nmcJ/pvL45wSkfnc4z101VVX +fTwjb+/evW+N25bX7uCZBO9Ug7N7Tz893dE3XnDGc4xZAeXPI/Gcu/A8e7oepyz3rlju7t27f7CO +6IjO8m/slfPllzCCU3CC4ERwLpDRzgjBCLu8XYw+1qOYse9lva9khmVOqc37xX6fub/lX/3VX43G +a9w3r48DC9WjmhGdeftPfOITp6xn3DYeI496206/neoIp1HNeTnCOfpBNqa1xmUxNTA/yJb3xB1b +t2795Zw2WL4/UH/ILYH4ppxaGCNLZbnndaN4o9N1MxIjgmJZ7bTZDMoSu18q931Z3jbEsiM+I4LP +P//8P4uoK7fZHCGbo57l/u+KZZYP36/K0N25c+fP9E3PjWXG4+X6ttd3z+UzEbs5xfe66677uVh2 +BGJME662wYq+4IzH79btlojn+Hs8Tj29NUcl68tiJDeXH48Zz7PezvF65fPK7Vwi5N4Mvth+cd+I +kojDXIcMtYjcuH39WpXLzsvll+/vzWm+9ePkbWL0MGKue7+sqoOvGuFbVY+Gluf3mnwO9fq300+7 +bfbpWN8It1j3WI94rergzOeVI5h1cMb22rBhw8ny/fM50lg//4jgfE92z+tldbjGZfFeyNcg7hvP +Pafttq9JBmd5jbfHZfHeqkY9z65DPZeZ74Ncp3w+MSU4Lyu3+efdFOVLuvf/l+v3f3mfXBnv/5hS +m9shtl98H6Og3Xvu0/FY9XJztLSsy0fzfd1OjxacghMQnILTkWwX/JdRzfkZnPFBtgTL78UH2y7q +zqtu84LprhmkEZCxjPKh+E+74Ho+p+O2U2rjg3Vc306bbafUlg/CP5/xmDHaTvftRgefnczl3fre +WcftsOm7XeD0TjNuxYf7uE8bnPU0057HuaTbFqNTajOk6pHCWsRFPEbfMmNEMcKkZ31XtSODESTD +1qlst/PrkdL2+u3bt/98jqxl/GRIRoDW61FfXgdojo4Om37a9/wi5LpRw2czKHO5+Th1cHb7Hj9d +LyMur8O0vi4CLp/7sOmxrbhPPqe8zyWXXPKleO1z+WV7/UKOru7YseP+vtc1Ryv7Ri/L17e699f2 +ZtQ033fb431eT6nNKbZDXt8N3Xv7rr7XoFz/2umITsEpOEFwIjgX6Xk7F8qXUc2FtQ9njO7Eh9AY +SSkfmn82Rn7iQ278Pae/xghffCCuprsuz5HJ7oPzWHDW4RqhltN3c9psjgrWQRoBGTEao2LxGBGi +ERUxTbReXsRvLC8uj5GkuO2uXbt+PG7bBfGzXSicFSNGMXIZ18XzaaYQr6iDM9azfL0tHyen/OZ9 +2ljsC864TWy7EmqPxX1yP8c2ODOU4jFj3eI5x3pk7GUMRtzFOnSjgKP7bW7YsOGLuew84FMbnDni +mPeP7dWuUz5GjJTGyFhspyqWVnWjoMfisuqxYtrsnTHKnJeX98ymeD51qObzyzCM1yHjJ+Kx+0VB +7gt6MLZZvJ75PK6//vqfyHWpR03r4IzRyJzKGxEY75+YctpN63463gPlee2Ox4ptFiOh5fV9c7f9 +n8qIj/vVI6WxPvE+aKfL9kVqHZtd5I/eJh4nnnc9Ih1hNlFwxvqX9/3xuE8sO167fP8PC85YVvee ++3L3+h6MZedyy7rcVr3Hnq3XWXAKTkBwCk6jnUY1BeeMBGd8kI0P4hmQ9chkxEGEZoxi1aOgEWjd +h+qxEZwIt9xvsA7OXG6EYD3FtudD68UZdBlwfftwZnC2+01mtMTlOQW4itiVOa03pwiXD+Vf7QvO +EjB351TEauRtZd8U2mHBWa9bG4B1cGa41eud2zmCoouFp+vRyHr79e3D2T5e3/3b2/Qtpx2pjGm/ +EUgRZnFU3rh/3D6DMS6P6cZxm4y53B4RsrkN61is79/ue9mO/LbXT2Yfzi7qns0pqfXrnqHXToUd +EoKnTKGt9+GsgzRCsfzYGxtRjFiu973M8JtMcNbLqW/THjSo7yBC9f3i33f5+1O5Dbv32O/Gtpuu +Aw8JTsEJghPBabTTqKbgnHAfzm4/tBeEYgRZNWI4NsKZy4oozVDM6bb1cvruF/eJ0bB4jG5UbHlG +TR2TGZx1rE4UnDFSGBGT8uBFGXoxgpcjtvGhey6Ds3vuOdp3bb3eEeB1+GUM5r6odSjG6N0kgnMs +Jtujzk4mOLv3x9MZLt1o8Kr6tjGy2S1nfY5et8EbyynR9yd5pN6JgjPWMd5fMepYH4hoWHDGCGdG +VI56xrJz2nW1fV/ajFZOOTjz+xyJLNH9YDyH+nH7Rj3r4KyjdFhwltv85OkGZzcd+aluuW+ot8F0 +HXhIcApOEJwITqOdRjUF57j7cNajgbFPZsRSN0X1+Xofzbx9TqmN0Z08Smc9vbYOzno/zRhpyhG8 +CICcSpsHvcnHyMDMWK2ntea+mm1w5lTUdpQzpwhn2MVj5TrkyOxcBWd83x4YJtc79y3tYua5OvRy +9DPDsdquK9vHa+9fP/cMw8kEZ4jppnnfOqZyPdp9HUPGZd4nRxjzdsNOo1I/j2YfzvXtPpzVvqpj +QRy//Ij9gfOy3N8yt28+1nQEZ3dQpadyv8nuPf9Ubo++/TwzStvtUgdne5uM5qkEZzyHsuw/7nuP +5X7CglNwAoJTcBrtNKopOKfVsPNf5pFg65HEvK6bFjk2pTb3r8zrI0zj6KLtlNoc0axvm0el7S4/ +ZT1if808cFG7nvXBgdrgjJGzjLl6eRmUuU9n+3zmOjgjlHKfyuaALmMjmrmvZl5322233RpB0o0I +PzPeQYNyu7QHzslpr8Om5vYFZz3NtT5dSRdGo5e300i75/flev0jfNqDIo0XnPX+shGOcbs2OOM+ +5TX9Qu5/mQcNGnbQop79MU87OOM2cYTZ3Od02OPGcyzv6ZF8v5bX9Qt1qJdw/3gsI4OzDtmJDho0 +XnB2r8GftO+xGPGMxxGcghMQnILTaKdRTcE5I6OcfVM4a905HUenpeZ96vNr5jkl23Nu5rLry/L8 +kH3n58zb9507sr3feMup16nveeV18Wd1vsqL2+vqWKqfR3ufYd/3baN8bvXU3nr987HaKZ/t9e3l +7Xp334+ep7Hndtv6zgHat+y+bZzPr73tsMv7HmPYMtvLI7byedSnV8n1z+vbxxv2HNv3e98y6/Wp +H7d9H7TfD7tfToEdb/pqXJ/PPZ9THYETvf+HvOd6t81E7zHBKTgBwSk4jXYa1RScLGD1CGw9mggL +meAUnCA4EZxGO41qCk7mw4e9bppqe9RWEJwIThCcCE6jnUY1BSdnpG8qJghOBCcITgSn0U6jmoIT +QHAKThCcCE6jnUY1BSfTMhq56eDBgzcwvxw+fHiLEWLBieAEwSk4BeciGu00qik4l6ILL3zJX27d +es43rr32nCeYP5YvXzbIU5ggOBGcIDgFJwt4tNOopuBcyl7xihc//ud/vqz8Gyv/3wyYL84+W3AK +TgQnCE7BKTgX/GinUU3BKTgFp+BEcApOEJyCU3Aa7ZzW0U6jmoITwSk4EZyCExCcgpNpH+00qik4 +EZyCE8EpOAHBKTiZ1tFOo5qCE8EpOBGcghMQnIKTaR/tNKopOBGcghPBKTgBwSk4mdbRTqOagpP3 +LTtw4MDf279//y1953UUnIKTU918883/8NChQ284ceLECsEpOEFwIjgZOtppVFNw8nfK/x9vLx+S +n924ceMXywfpbXV4Ck7Byam2bNnywPLly5+98sorTxw9enRd/nsRnIITBCeC02jn6GjnD7zr7sGG +SzcPzn3tjYNlt/7sYNn3/eLcK+smOAXnXAdnEREz2LZt20fLB+lXCk7ByfDgjH8rK1eufHLnzp0/ +VWLzJYJTcILgRHAyOtq58tu3D5a94vLBsnVb5o+Lrxzkh/0Fbc36094Gy89a9ey6det+f+PGjSfn +0qZNmx66/vrr3xefQxayffv23Xrw4MEbJmPHjh33l9fvufq1POecc57YtWvXj7785S/6fwXn/AzO +/fv3757sa8z0ueyyy/5VCc5T/r2sXr36r3fv3v2PSnD+ueAUnCA4EZwwU97+idMe5V25euRvb7vt +trfO9YfJ2JexfAa5b6EH5+bNm39zspF94YUXPlY+JD/f/gLhta997W+sWXPufxGc8zM4N2zY8MW5 +/gXNUlTi8uvtv5cY6bzqqqt+ZdeuXcf27dt3ls9AghMEJ4ITTKmlZ0pt+TD9+M6dO3/GlFpTapl4 +Su3IyMh/2r179z3l38sFto3gBMGJ4ATByZDgXLt27R/t3bv30LFjx15UXyc4BScvDM5LL730t28r +X+2RahGcIDgRnCA4aRw/fvzcYdcJTsHJqY4dOzZiOwhOEJwIThCcTAPBKThBcAKCU3CC4ERwCk5A +cILgFJyA4BScCE4QnCA4EZwgOAWn4BScIDhBcApOwQmCE8EpOAHBCYJTcAKCcwEF56//+rLByZPM +JytXCk4QnCA4BafgBMG5wN1wwzW/uHnz2v+4adNFjzJ/XHPNlZ/Zs2fPSu9REJwgOAWnmADBCYDg +BMGJ4ATBCYDgBASn4ATBCQCCEwTnkg7Ob985WLb9IDCBs85d/ZTgBEBwguBkkg4ePHhD/CwDJrZ7 +9+57jx079iI/OwAQnCA4AQBAcILgBAAABCcITgAAEJwgOAEAQHCC4AQAAAQnCE4AABCcIDgBAEBw +guAEAADBKThBcAIAgOAEwQkAAIITBCcAAAhOwQmCEwAABCcITgAAEJwgOAEAQHAKThCcAAAgOEFw +AgCA4ATBCQAAglNwguAEAADBCYITAAAEJwhOAAAQnEICBCcAAAhOEJwAACA4QXACAIDgFBMgOAEA +QHCC4AQAAMEJghMAAAQnIDgBAEBwguAEAADBCYITAAAEJyA4AQBAcILgBAAAwQmCEwAABCcgOAEA +QHCC4AQAAMEJghMAAAQnIDgBAEBwguAEAADBCYITAAAEJyA4AQBAcILgBAAAwQmCEwAABCcgOAEA +QHCC4AQAAMEJghMAAAQnIDgBAEBwguAEAADBCYITAAAEJyA4AQBAcILgBAAAwQmCEwAABCcgOAEA +QHCC4AQAAMEJghMAAAQnIDgBAEBwguAEAADBCYITAAAEJyA4AQBAcILgBAAAwQmCEwAABCcITsEJ +AACCEwQnAAAIThCcAAAgOEFwCk4AABCcIDgBAEBwguAEAADBCYLTDxEAABCcIDgBAEBwguAEAADB +CYLTDxIAABCcIDgBAEBwguAEAADBCYITAAAQnCA4AQBAcILgBAAAwQmCEwAAEJwgOAEAQHCC4AQA +AMEJghMAABCcIDgBAEBwguAEAADBCYITAAAQnCA4AQBAcILgBAAAwQmCEwAAEJwgOAEAQHCC4AQA +AMEJghMAABCcIDgBAEBwguAEAADBCYITAAAQnCA4AQBAcILgBAAAwQmCEwAABKfgBMEJAACCEwQn +AAAIThCcAAAgOAUnCE4AABCcIDgBAEBwguAEAADBKThBcAIAwAwG5w3lg/R9wIRed9ZZTwlOAACY +ZHAeO3ZsZDQ6gUmJfzN+iAAAwCSCEwAAAAQnAAAA897/D09QwmKIv9URAAAAAElFTkSuQmCC + +------=_NextPart_01CEBF8E.4008E8B0 +Content-Location: file:///C:/0E5B2E2E/BASIC_STREAM_INJECTION_files/oledata.mso +Content-Transfer-Encoding: base64 +Content-Type: application/x-mso + +0M8R4KGxGuEAAAAAAAAAAAAAAAAAAAAAPgADAP7/CQAGAAAAAAAAAAAAAAABAAAAAQAAAAAAAAAA +EAAA/v///wAAAAD+////AAAAAAAAAAD///////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////9 +/////v///wMAAAAEAAAABQAAAAYAAAAHAAAACAAAAAkAAAAKAAAACwAAAAwAAAANAAAADgAAAA8A +AAAQAAAAEQAAABIAAAATAAAAFAAAABUAAAAWAAAAFwAAABgAAAAZAAAAGgAAABsAAAAcAAAAHQAA +AB4AAAAfAAAAIAAAACEAAAAiAAAAIwAAACQAAAAlAAAAJgAAACcAAAAoAAAAKQAAACoAAAArAAAA +/v////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////1IA +bwBvAHQAIABFAG4AdAByAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAWAAUA//////////8BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIDTeuzIv84B +/v///wAAAAAAAAAAXwAxADQANAAyADIAMwA3ADIANgAzAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAABgAAgH///////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAACAAAAllMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///////////////wAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////// +////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHYA +AHic7Jl5XBPX3v/PmZlsbEkmC6hoQoKAFSVsilYJiwpakYDEtQgJBqQiQRa9rsEFcUMWUWvV4l7F +VtoqttYlWFu11Qq2ajcrCMqqZcImCGR+JwHv03uf53Vv+/z+el6ve/RDZs4s33PmnPP9vuc7VZX8 +miOfDH0C/qkEAhyYaQ5g/qEODspaeABgg/tmmqZfV9P/Kf+nSj8SMTiGAYO/ljFnIbGROEg2SLZI +dkj2SA5I3IEpAPhIJJIASYgkQhIjOSI5IQ1BGoo0DMkZaTjSCCQJkhTJBUmGJEdyRRqJ5IbkjuSB +NArpDaTRSJ5IY5DGInkhKZC8kXyQfJH8kPyRxiGNH+zLhMHf/5T/uUQDPfqXicZiKkhFv+lg1T+7 +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 +CZfAPDgG3mszoyAEovRv3+uRDQc4Ge1Rewl/gqcIxP1wI08JFWAu4osEI9vRVOQcasx/hIC3zkZ7 +DIW/kMs/mlF77mY+mUG/JK3ibubu4qZxM7kgn1vOfceddEN1D503gTeZpzab1zSquk16m/eEd5DX +MSBMV9QsCU4fkOAiGV7Fa1CiFJnZSzSMgCt/4casvFX8f8S7+Gn8TH5XPr+c/46/x3geCfXiMah/ +vemplguHszDzKcQRtMaUbcp5UcKqrayclt3z81HJq6X64S/OdFRsqoCA+Tird+T5tj47JoRaJIMS +sGNHe0auYE6NtTR72xzpI6Hr2x1OMQ1CkrkpYrPy9NIt899B+e8o+e/U0Yn7wN6301Fv9AccgS4T +h4kxFOShZWgdWigfImmlKQZjpEeyJBTbfjEr7xB2Ads1ArLynmK1WAs2iGngJrg9Djj4wFDMWz6e +hCu5alFuYEzZf7V3LVBNnNt6z2QSAioTUFHxkYiCosaARaXPhCIPrfJG4qONEVGwSpSHaK8Y6qnW +e1tFT3209/YUbGurp1awWm9PW0VPa330uEA9Pm6LDfhAIFUmIIT3nP3PBExctdq71lm9d63zrzVK +vtl7//vfs//XzP/v//PBe1lrSVdJg/XjeKqtiD2IdWl/w+yGiobrDTBu6caRO237vu48tHHkOdt3 +ZXC17Bt75+dNzczJP1Uryv3OVlTknmw72vzCmW13l9ccLno1Zmj6IP2KrG911ZLmabrRcaoZo6q1 +8ZkQyIJPNfZB57g1C+KpWkWVVtqd7Zcqzf0Llx9oxV5oi5ePrxfFFPlJ9gSE//VvKzc1NV81168Z +al91J+Bo48b2s61fdjb5rQgqBs2owohTjfeoY5KVLW+vbNl+plbBaX0VW/ov5FfAwiBYWAzzXvlD +QUXBjYLziR4XunKTFTgniYSMY9u4ZSQG4DbO9CNk1Spe+bGgYoHH3LXaSZsnF1gWB19bPCWuyK8h ++8SFbdUfftn6bEXghKamoxJ35qgbt7JF00gX+Q0//fQfbccHuh18/57/cNhp05xj/bVnLvGX/Bt4 +yg1o2QC2o+V5/9LYo81nY2b4wxdRFblFMTJD+Mmi2FDPczbYZ9t38OYAt4OftuFfbcbYrdubplPz +qT0HPXbeLOi3SPf9jLiRdw0d7KmQOxrqsJ9EQvmarl78nr2hD57XaVCc6HOuWf9yVED1CtN605et +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 +AAAAAAAAAAAAAAAA + +------=_NextPart_01CEBF8E.4008E8B0 +Content-Location: file:///C:/0E5B2E2E/BASIC_STREAM_INJECTION_files/filelist.xml +Content-Transfer-Encoding: quoted-printable +Content-Type: text/xml; charset="utf-8" + + + + + + + + + + +------=_NextPart_01CEBF8E.4008E8B0-- diff --git a/network/trans/WFPSampler/docs/ConditionsForCommandLine.mht b/network/trans/WFPSampler/docs/ConditionsForCommandLine.mht new file mode 100644 index 000000000..ca480def0 --- /dev/null +++ b/network/trans/WFPSampler/docs/ConditionsForCommandLine.mht @@ -0,0 +1,12205 @@ +MIME-Version: 1.0 +Content-Type: multipart/related; boundary="----=_NextPart_01CFE175.4E1A8DA0" + +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_01CFE175.4E1A8DA0 +Content-Location: file:///C:/2267B225/ConditionsForCommandLine.htm +Content-Transfer-Encoding: quoted-printable +Content-Type: text/html; charset="windows-1252" + + + + + + + + + +Conditions for the Command Line + + + + + + + + + + +
+ +
+ +

WFPSampler C= +onditions +for the Command Line
+
+

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

F= +WPM_CONDITION

+
+

C= +ommand + line
+  parameter

+
+

A= +cceptable + value(s)

+
+

E= +xample + usage

+

&= +lt;Condition> + <MatchType> <Value>

+
+

FWPM_CONDITION_INTERFACE_MAC_ADDRESS

+
+

-ima

+
+

MAC + Address

+
+

-ima + =3D=3D 01:02:03:04:05:06

+
+

FWPM_CONDITION_MAC_LOCAL_ADDRESS

+
+

-mla

+
+

MAC + Address

+
+

-mla + =3D=3D 01:02:03:04:05:06

+
+

FWPM_CONDITION_MAC_REMOTE_ADDRESS

+
+

-mra

+
+

MAC + Address

+
+

-mra + =3D=3D 01:02:03:04:05:06

+
+

 

+

FWPM_CONDITION_ETHER_TYPE

+
+

 

+

-et

+
+

UINT16 +

+
+

-et + =3D=3D 0x806

+
+

NDIS_ETH_TYPE + name

+
+

-et + =3D=3D NDIS_ETH_TYPE_ARP

+
+

Well-known + alias

+
+

-et + =3D=3D ARP

+
+

FWPM_CONDITION_VLAN_ID

+
+

-vlid

+
+

UINT16

+
+

-vlid + =3D=3D 0xFFFF

+
+

FWPM_CONDITION_VSWITCH_TENANT_NETWORK_ID

+
+

-vstnid

+
+

UINT32

+
+

-vstnid + =3D=3D 0xFFFFFFFF

+
+

FWPM_CONDITION_NDIS_PORT

+
+

-np

+
+

UINT32

+
+

-np + =3D=3D 0xFFFFFFFF

+
+

 

+

FWPM_CONDITION_NDIS_MEDIA_TYPE

+
+

 

+

-nmt

+
+

UINT32

+
+

-nmt + =3D=3D 0

+
+

NDIS_MEDIUM + name

+
+

-nmt + =3D=3D NdisMedium802_3

+
+

Well-known + alias

+
+

-nmt + =3D=3D Ethernet

+
+

 

+

FWPM_CONDITION_PHYSICAL_MEDIA_TYPE

+
+

 

+

-npmt

+
+

UINT32

+
+

-nmpt + =3D=3D 0x000E

+
+

NDIS_PHYSICAL_MEDIUM + name

+
+

-nmpt + =3D=3D NdisPhysicalMedium802_3

+
+

 

+
+

-nmpt + =3D=3D Ethernet

+
+

FWPM_CONDITION_L2_FLAGS

+
+

-l2f

+
+

UINT32

+
+

-l2f + =3D=3D 0xA

+
+

FWPM_CONDITION_L2_FLAG + name

+
+

-l2f + =3D=3D FWP_CONDITION_L2_IS_WIFI

+
+

 

+

FWPM_CONDITION_MAC_LOCAL_ADDRESS_TYPE

+
+

 

+

-mlat

+
+

UINT8

+
+

-mlat + =3D=3D 0

+
+

DL_ADDRESS_TYPE + name

+
+

-mlat + =3D=3D DlUnicast

+
+

Well-known + alias

+
+

-mlat + =3D=3D Unicast

+
+

 

+

FWPM_CONDITION_MAC_REMOTE_ADDRESS_TYPE

+
+

 

+

-mrat

+
+

UINT8

+
+

-mrat + =3D=3D 0

+
+

DL_ADDRESS_TYPE + name

+
+

-mrat + =3D=3D DlUnicast

+
+

Well-known + alias

+
+

-mrat + =3D=3D Unicast

+
+

FWPM_CONDITION_ALE_PACKAGE_ID

+
+

-apid

+
+

SID

+
+

-apid + =3D=3D + S-1-15-2-2551677095-2355568638-4209445997-2436930744-3692183382-387691378= +-1866284433

+
+

Well-known + alias

+
+

-apid + =3D=3D WinNullSid

+
+

FWPM_CONDITION_MAC_SOURCE_ADDRESS

+
+

-msa

+
+

MAC + Address

+
+

-msa + =3D=3D 01:02:03:04:05:06

+
+

FWPM_CONDITION_MAC_DESTINATION_ADDRESS

+
+

-mda

+
+

MAC + Address

+
+

-mda + =3D=3D 01:02:03:04:05:06

+
+

 

+

FWPM_CONDITION_MAC_SOURCE_ADDRESS_TYPE

+
+

 

+

-msat

+
+

UINT8

+
+

-msat + =3D=3D 0

+
+

DL_ADDRESS_TYPE + name

+
+

-msat + =3D=3D DlUnicast

+
+

Well-known + alias

+
+

-msat + =3D=3D Unicast

+
+

 

+

FWPM_CONDITION_MAC_DESTINATION_ADDRESS_TYPE

+
+

 

+

-mdat

+
+

UINT8

+
+

-mdat + =3D=3D 0

+
+

DL_ADDRESS_TYPE + name

+
+

-mdat + =3D=3D DlUnicast

+
+

Well-known + alias

+
+

-mdat + =3D=3D Unicast

+
+

FWPM_CONDITION_IP_SOURCE_PORT

+
+

-ipsp

+
+

UINT16

+
+

-ipsp + =3D=3D 0xFFFF

+
+

FWPM_CONDITION_VSWITCH_ICMP_TYPE

+
+

-vsicmpt

+
+

UINT8

+
+

-vsicmpt + =3D=3D 0xFF

+
+

FWPM_CONDITION_IP_DESTINATION_PORT

+
+

-ipdp

+
+

UINT16

+
+

-ipdp + =3D=3D 0xFFFF

+
+

FWPM_CONDITION_VSWITCH_ICMP_CODE

+
+

-vsicmpc

+
+

UINT8

+
+

-vsicmpc + =3D=3D 0xFF

+
+

FWPM_CONDITION_VSWITCH_ID

+
+

-vsid

+
+

GUID

+
+

-vsid + =3D=3D 12345678-90AB-CDEF-1234-567890ABCDEF

+
+

 

+

FWPM_CONDITION_VSWITCH_NETWORK_TYPE

+
+

 

+

-vsnt

+
+

UINT8

+
+

-vsnt + =3D=3D 3

+
+

FWP_VSWITCH_NETWORK_TYPE + name

+
+

-vsnt + =3D=3D FWP_VSWITCH_NETWORK_TYPE_EXTERNAL

+
+

Well-known + alias

+
+

-vsnt + =3D=3D External

+
+

FWPM_CONDITION_VSWITCH_SOURCE_INTERFACE_ID

+
+

-vssiid

+
+

GUID(s)

+
+

-vssiid + =3D=3D 12345678-90AB-CDEF-1234-567890ABCDEF--12345678-90AB-CDEF-1234-5678= +90ABCDEF

+
+

FWPM_CONDITION_VSWITCH_DESTINATION_INTERFACE_ID

+
+

-vsdiid

+
+

GUID(s)

+
+

-vsdiid + =3D=3D 12345678-90AB-CDEF-1234-567890ABCDEF--12345678-90AB-CDEF-1234-5678= +90ABCDEF

+
+

FWPM_CONDITION_VSWITCH_SOURCE_VM_ID

+
+

-vssvmid

+
+

GUID

+
+

-vssvmid + =3D=3D 12345678-90AB-CDEF-1234-567890ABCDEF

+
+

FWPM_CONDITION_VSWITCH_DESTINATION_VM_ID

+
+

-vsdvmid

+
+

GUID

+
+

-vsdvmid + =3D=3D 12345678-90AB-CDEF-1234-567890ABCDEF

+
+

 

+

FWPM_CONDITION_VSWITCH_SOURCE_INTERFACE_TYPE

+
+

 

+

-vssit

+
+

UINT32

+
+

-vssit + =3D=3D 0

+
+

NDIS_SWITCH_NIC_TYPE + name

+
+

-vssit + =3D=3D SwitchNicTypeExternal

+
+

Well-known + alias

+
+

-vssit + =3D=3D External

+
+

 

+

FWPM_CONDITION_VSWITCH_DESTINATION_INTERFACE_TYPE

+
+

 

+

-vsdit

+
+

UINT32

+
+

-vsdit + =3D=3D 0

+
+

NDIS_SWITCH_NIC_TYPE + name

+
+

-vsdit + =3D=3D SwitchNicTypeExternal

+
+

Well-known + alias

+
+

-vsdit + =3D=3D External

+
+

FWPM_CONDITION_ALE_ORIGINAL_APP_ID

+
+

-aoaid

+
+

String

+
+

-aoaid + =3D=3D IExplore.Exe

+
+

FWPM_CONDITION_IP_NEXTHOP_ADDRESS

+
+

-ipnha

+
+

IPv4 + Address

+
+

-ipnha + =3D=3D 1.2.3.4

+
+

IPv6 + Address

+
+

-ipnha + =3D=3D 1:2:3:4:5:6:7:8

+
+

FWPM_CONDITION_ + NEXTHOP_SUB_INTERFACE_INDEX

+
+

-nhsii

+
+

UINT32

+
+

-nhsii + =3D=3D 0xFFFFFFFF

+
+

FWPM_CONDITION_IP_NEXTHOP_INTERFACE

+
+

-ipnhi

+
+

UINT64

+
+

-ipnhi + =3D=3D 0xFFFFFFFFFFFFFFFF

+
+

 

+

FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE

+
+

 

+

-nhit

+
+

UINT32

+
+

-nhit + =3D=3D 6

+
+

IF_TYPE + name

+
+

-nhit + =3D=3D IF_TYPE_ETHERNET_CSMACD

+
+

Well-known + alias

+
+

-nhit + =3D=3D Ethernet

+
+

 

+

FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE

+
+

 

+

-nhtt

+
+

UINT32

+
+

-nhtt + =3D=3D 14

+
+

TUNNEL_TYPE + name

+
+

-nhtt + =3D=3D TUNNEL_TYPE_TEREDO

+
+

Well-known + alias

+
+

-nhtt + =3D=3D Teredo

+
+

FWPM_CONDITION_NEXTHOP_INTERFACE_INDEX

+
+

-nhii

+
+

UINT32

+
+

-nhii + =3D=3D 0xFFFFFFFF

+
+

 

+

FWPM_CONDITION_ORIGINAL_PROFILE_ID

+
+

 

+

-opid

+
+

UINT32

+
+

-opid + =3D=3D 1

+
+

NL_INTERFACE_NETWORK_CATEGORY_STATE + name

+
+

-opid + =3D=3D NlincPublic

+
+

Well-known + alias

+
+

-opid + =3D=3D Public

+
+

 

+

FWPM_CONDITION_CURRENT_PROFILE_ID

+
+

 

+

-cpid

+
+

UINT32

+
+

-cpid + =3D=3D 1

+
+

NL_INTERFACE_NETWORK_CATEGORY_STATE + name

+
+

-cpid + =3D=3D NlincPublic

+
+

Well-known + alias

+
+

-cpid + =3D=3D Public

+
+

 

+

FWPM_CONDITION_LOCAL_INTERFACE_PROFILE_ID

+
+

 

+

-lipid

+
+

UINT32

+
+

-lipid + =3D=3D 1

+
+

NL_INTERFACE_NETWORK_CATEGORY_STATE + name

+
+

-lipid + =3D=3D NlincPublic

+
+

Well-known + alias

+
+

-lipid + =3D=3D Public

+
+

 

+

FWPM_CONDITION_ARRIVAL_INTERFACE_PROFILE_ID

+
+

 

+

-aipid

+
+

UINT32

+
+

-aipid + =3D=3D 1

+
+

NL_INTERFACE_NETWORK_CATEGORY_STATE + name

+
+

-aipid + =3D=3D NlincPublic

+
+

Well-known + alias

+
+

-aipid + =3D=3D Public

+
+

 

+

FWPM_CONDITION_NEXTHOP_INTERFACE_PROFILE_ID

+
+

 

+

-nhipid

+
+

UINT32

+
+

-nhipid + =3D=3D 1

+
+

NL_INTERFACE_NETWORK_CATEGORY_STATE + name

+
+

-nhipid + =3D=3D NlincPublic

+
+

Well-known + alias

+
+

-nhipid + =3D=3D Public

+
+

 

+

FWPM_CONDITION_REAUTHORIZE_REASON

+
+

 

+

-rr

+
+

UINT32             

+
+

-rr + =3D=3D 3

+
+

FWP_CONDITION_REAUTHORIZE_REASON + name

+
+

-rr + =3D=3D FWP_CONDITION_REAUTHORIZE_REASON_POLICY_CHANGE

+
+

Well-known + alias

+
+

-rr + =3D=3D PolicyChange

+
+

FWPM_CONDITION_ORIGINAL_ICMP_TYPE

+
+

-oicmpt

+
+

UINT16

+
+

-oicmpt + =3D=3D 0xFF

+
+

FWPM_CONDITION_IP_PHYSICAL_ARRIVAL_INTERFACE

+
+

-ippai

+
+

UINT64

+
+

-ippai + =3D=3D 0xFFFFFFFFFFFFFFFF

+
+

FWPM_CONDITION_IP_PHYSICAL_NEXTHOP_INTERFACE

+
+

-ippnhi

+
+

UINT64

+
+

-ippnhi + =3D=3D 0xFFFFFFFFFFFFFFFF

+
+

FWPM_CONDITION_INTERFACE_QUARANTINE_EPOCH

+
+

-iqe

+
+

UINT64

+
+

-iqe + =3D=3D 0xFFFFFFFFFFFFFFFF

+
+

FWPM_CONDITION_IP_LOCAL_ADDRESS

+
+

-ipla

+
+

IPv4 + Address

+
+

-ipla + =3D=3D 1.2.3.4

+
+

IPv6 + Address

+
+

-ipla + =3D=3D 1:2:3:4:5:6:7:8

+
+

FWPM_CONDITION_IP_REMOTE_ADDRESS

+
+

-ipra

+
+

IPv4 + Address

+
+

-ipra + =3D=3D 1.2.3.4

+
+

IPv6 + Address

+
+

-ipra + =3D=3D 1:2:3:4:5:6:7:8

+
+

FWPM_CONDITION_IP_SOURCE_ADDRESS

+
+

-ipsa

+
+

IPv4 + Address

+
+

-ipsa + =3D=3D 1.2.3.4

+
+

IPv6 + Address

+
+

-ipsa + =3D=3D 1:2:3:4:5:6:7:8

+
+

FWPM_CONDITION_IP_DESTINATION_ADDRESS

+
+

-ipda

+
+

IPv4 + Address

+
+

-ipda + =3D=3D 1.2.3.4

+
+

IPv6 + Address

+
+

-ipda + =3D=3D 1:2:3:4:5:6:7:8

+
+

 

+

FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE

+
+

 

+

-iplat

+
+

UINT8

+
+

-iplat + =3D=3D 1

+
+

NL_ADDRESS_TYPE + name

+
+

-iplat + =3D=3D NlatUnicast

+
+

Well-known + alias

+
+

-iplat + =3D=3D Unicast

+
+

 

+

FWPM_CONDITION_IP_DESTINATION_ADDRESS_TYPE

+
+

 

+

-ipdat

+
+

UINT8

+
+

-ipdat + =3D=3D 1

+
+

NL_ADDRESS_TYPE + name

+
+

-ipdat + =3D=3D NlatUnicast

+
+

Well-known + alias

+
+

-ipdat + =3D=3D Unicast

+
+

FWPM_CONDITION_IP_LOCAL_INTERFACE

+
+

-ipli

+
+

UINT64

+
+

-ipli + =3D=3D 0xFFFFFFFFFFFFFFFF

+
+

FWPM_CONDITION_IP_INTERFACE

+
+

-ipi

+
+

UINT64

+
+

-ipi + =3D=3D 0xFFFFFFFFFFFFFFFF

+
+

FWPM_CONDITION_IP_ARRIVAL_INTERFACE

+
+

-ipai

+
+

UINT64

+
+

-ipai + =3D=3D 0xFFFFFFFFFFFFFFFF

+
+

 

+

FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE

+
+

 

+

-ait

+
+

UINT32

+
+

-ait + =3D=3D 6

+
+

IF_TYPE + name

+
+

-ait + =3D=3D IF_TYPE_ETHERNET_CSMACD

+
+

Well-known + alias

+
+

-ait + =3D=3D Ethernet

+
+

 

+

FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE

+
+

 

+

-att

+
+

UINT32

+
+

-att + =3D=3D 14

+
+

TUNNEL_TYPE + name

+
+

-att + =3D=3D TUNNEL_TYPE_TEREDO

+
+

Well-known + alias

+
+

-att + =3D=3D Teredo

+
+

FWPM_CONDITION_ARRIVAL_INTERFACE_INDEX

+
+

-aii

+
+

UINT32

+
+

-aii + =3D=3D 0xFFFFFFFF

+
+

 

+

FWPM_CONDITION_INTERFACE_TYPE

+
+

 

+

-it

+
+

UINT32

+
+

-it + =3D=3D 6

+
+

IF_TYPE + name

+
+

-it + =3D=3D IF_TYPE_ETHERNET_CSMACD

+
+

Well-known + alias

+
+

-it + =3D=3D Ethernet

+
+

 

+

FWPM_CONDITION_LOCAL_INTERFACE_TYPE

+
+

 

+

-lit

+
+

UINT32

+
+

-lit + =3D=3D 6

+
+

IF_TYPE + name

+
+

-lit + =3D=3D IF_TYPE_ETHERNET_CSMACD

+
+

Well-known + alias

+
+

-lit + =3D=3D Ethernet

+
+

 

+

FWPM_CONDITION_TUNNEL_TYPE

+
+

 

+

-tt

+
+

UINT32

+
+

-tt + =3D=3D 14

+
+

TUNNEL_TYPE + name

+
+

-tt + =3D=3D TUNNEL_TYPE_TEREDO

+
+

Well-known + alias

+
+

-tt + =3D=3D Teredo

+
+

 

+

FWPM_CONDITION_LOCAL_TUNNEL_TYPE

+
+

 

+

-ltt

+
+

UINT32

+
+

-ltt + =3D=3D 14

+
+

TUNNEL_TYPE + name

+
+

-ltt + =3D=3D TUNNEL_TYPE_TEREDO

+
+

Well-known + alias

+
+

-ltt + =3D=3D Teredo

+
+

FWPM_CONDITION_IP_FORWARD_INTERFACE

+
+

-ipfi

+
+

UINT64

+
+

-ipfi + =3D=3D 0xFFFFFFFFFFFFFFFF

+
+

FWPM_CONDITION_IP_PROTOCOL

+
+

-ipp

+
+

UINT8

+
+

-ipp + =3D=3D 6

+
+

Well-known + alias

+
+

-ipp + =3D=3D TCP

+
+

FWPM_CONDITION_IP_LOCAL_PORT

+
+

-iplp

+
+

UINT16

+
+

-iplp + =3D=3D 0xFFFF

+
+

FWPM_CONDITION_ICMP_TYPE

+
+

-icmpt

+
+

UINT8

+
+

-icmpt + =3D=3D 0xFF

+
+

FWPM_CONDITION_IP_REMOTE_PORT

+
+

-iprp

+
+

UINT16

+
+

-iprp + =3D=3D 0xFFFF

+
+

FWPM_CONDITION_ICMP_CODE

+
+

-icmpc

+
+

UINT8

+
+

-icmpc + =3D=3D 0xFF

+
+

 

+

FWPM_CONDITION_EMBEDDED_LOCAL_ADDRESS_TYPE

+
+

 

+

-elat

+
+

UINT8

+
+

-elat + =3D=3D 1

+
+

NL_ADDRESS_TYPE + name

+
+

-elat + =3D=3D NlatUnicast

+
+

Well-known + alias

+
+

-elat + =3D=3D Unicast

+
+

FWPM_CONDITION_EMBEDDED_REMOTE_ADDRESS

+
+

-era

+
+

IPv4 + Address

+
+

-era + =3D=3D 1.2.3.4

+
+

IPv6 + Address

+
+

-era + =3D=3D 1:2:3:4:5:6:7:8

+
+

FWPM_CONDITION_EMBEDDED_PROTOCOL

+
+

-ep

+
+

UINT8

+
+

-ep + =3D=3D 6

+
+

Well-known + alias

+
+

-ep + =3D=3D TCP

+
+

FWPM_CONDITION_EMBEDDED_LOCAL_PORT

+
+

-elp

+
+

UINT16

+
+

-elp + =3D=3D 0xFFFF

+
+

FWPM_CONDITION_EMBEDDED_REMOTE_PORT

+
+

-erp

+
+

UINT16

+
+

-erp + =3D=3D 0xFFFF

+
+

FWPM_CONDITION_FLAGS

+
+

-f

+
+

UINT32

+
+

-f + =3D=3D 3

+
+

FWP_CONDITION_FLAG + name

+
+

-f + =3D=3D FWP_CONDITION_FLAG_IS_IPSEC_SECURED

+
+

 

+

FWPM_CONDITION_DIRECTION

+
+

 

+

-d

+
+

UINT

+
+

-d + =3D=3D 0

+
+

FWP_DIRECTION + name

+
+

-d + =3D=3D FWP_DIRECTION_OUTBOUND

+
+

Well-known + alias

+
+

-d + =3D=3D Outbound

+
+

FWPM_CONDITION_ + INTERFACE_INDEX

+
+

-ii

+
+

UINT32

+
+

-ii + =3D=3D 0xFFFFFFFF

+
+

FWPM_CONDITION_LOCAL_INTERFACE_INDEX

+
+

-lii

+
+

UINT32

+
+

-lii + =3D=3D 0xFFFFFFFF

+
+

FWPM_CONDITION_SUB_INTERFACE_INDEX

+
+

-subii

+
+

UINT32

+
+

-subii + =3D=3D 0xFFFFFFFF

+
+

FWPM_CONDITION_ARRIVAL_SUB_INTERFACE_INDEX

+
+

-asii

+
+

UINT32

+
+

-asii + =3D=3D 0xFFFFFFFF

+
+

FWPM_CONDITION_SOURCE_INTERFACE_INDEX

+
+

-sii

+
+

UINT32

+
+

-sii + =3D=3D 0xFFFFFFFF

+
+

FWPM_CONDITION_SOURCE_SUB_INTERFACE_INDEX

+
+

-ssii

+
+

UINT32

+
+

-ssii + =3D=3D 0xFFFFFFFF

+
+

FWPM_CONDITION_DESTINATION_INTERFACE_INDEX

+
+

-dii

+
+

UINT32

+
+

-dii + =3D=3D 0xFFFFFFFF

+
+

FWPM_CONDITION_DESTINATION_SUB_INTERFACE_INDEX

+
+

-dsii

+
+

UINT32

+
+

-dsii + =3D=3D 0xFFFFFFFF

+
+

FWPM_CONDITION_ALE_APP_ID

+
+

-aaid

+
+

String

+
+

-aaid + =3D=3D IExplore.exe

+
+

FWPM_CONDITION_ALE_USER_ID

+
+

-auid

+
+

String

+
+

-auid + =3D=3D Domain\UserName

+
+

FWPM_CONDITION_REMOTE_USER_ID

+
+

-aruid

+
+

String

+
+

-aruid + =3D=3D Domain\UserName

+
+

FWPM_CONDITION_REMOTE_MACHINE_ID

+
+

-armid

+
+

String

+
+

-armid + =3D=3D MachineName

+
+

FWPM_CONDITION_PROMISCUOUS_MODE

+
+

-apm

+
+

UINT32

+
+

-apm + =3D=3D 1

+
+

FWPM_CONDITION_ALE_SIO_FIREWALL_SYSTEM_PORT

+
+

-asfsp

+
+

UINT32

+
+

-asfsp + =3D=3D 1

+
+

FWPM_CONDITION_ALE_SIO_FIREWALL_SOCKET_PROPERTY

+
+

-asfsp

+
+

UINT32

+
+

-asfsp + =3D=3D 1

+
+

FWPM_CONDITION_ALE_NAP_CONTEXT

+
+

-anc

+
+

UINT32

+
+

-anc + =3D=3D 0xFFFFFFFF

+
+

FWPM_CONDITION_REMOTE_USER_TOKEN

+
+

 

+
+

 

+
+

 

+
+

FWPM_CONDITION_RPC_IF_UUID

+
+

 

+
+

 

+
+

 

+
+

FWPM_CONDITION_IF_VERSION

+
+

-riv

+
+

UINT16

+
+

-riv + =3D=3D 0xFFFF

+
+

FWPM_CONDITION_RPC_IF_FLAG

+
+

-rif

+
+

UINT32

+
+

-rif + =3D=3D 1

+
+

Well-known + alias

+
+

-rif + =3D=3D RPC_FW_IF_FLAG_DCOM

+
+

FWPM_CONDITION_DCOM_APP_ID

+
+

 

+
+

 

+
+

 

+
+

FWPM_CONDITION_IMAGE_NAME

+
+

 

+
+

 

+
+

 

+
+

FWPM_CONDITION_RPC_PROTOCOL

+
+

-rp

+
+

UINT8

+
+

-rp + =3D=3D 7

+
+

Well-known + alias

+
+

-rp + =3D=3D NCACN_IP_TCP

+
+

FWPM_CONDITION_RPC_AUTH_TYPE

+
+

-rat

+
+

UINT8

+
+

-rat + =3D=3D 0

+
+

Well-known + alias

+
+

-rat + =3D=3D  RPC_C_AUTHN_NONE

+
+

FWPM_CONDITION_RPC_AUTH_LEVEL

+
+

-ral

+
+

UINT8

+
+

-ral + =3D=3D 0

+
+

Well-known + alias

+
+

-ral + =3D=3D  RPC_C_AUTHN_LEVEL_DEFAULT= +

+
+

FWPM_CONDITION_SEC_ENCRYPT_ALGORITHM

+
+

-sea

+
+

UINT32

+
+

-sea + =3D=3D 1

+
+

FWPM_CONDITION_SEC_KEY_SIZE

+
+

-sks

+
+

UINT32

+
+

-sks + =3D=3D 128

+
+

FWPM_CONDITION_IP_LOCAL_ADDRESS_V4

+
+

-iplav4

+
+

IPv4 + Address

+
+

-iplav4 + =3D=3D 1.2.3.4

+
+

FWPM_CONDITION_IP_LOCAL_ADDRESS_V6

+
+

-iplav6

+
+

IPv6 + Address

+
+

-iplav6 + =3D=3D 1:2:3:4:5:6:7:8

+
+

FWPM_CONDITION_PIPE

+
+

 

+
+

 

+
+

 

+
+

FWPM_CONDITION_IP_REMOTE_ADDRESS_V4

+
+

-iprav4

+
+

Ipv4 + Address

+
+

-Iprav4 + =3D=3D 1.2.3.4

+
+

FWPM_CONDITION_IP_REMOTE_ADDRESS_V6

+
+

-iprav6

+
+

IPv6 + Address

+
+

-iprav6 + =3D=3D 1:2:3:4:5:6:7:8

+
+

FWPM_CONDITION_PROCESS_WITH_RPC_IF_UUID

+
+

 

+
+

 

+
+

 

+
+

FWPM_CONDITION_RPC_EP_VALUE

+
+

 

+
+

 

+
+

 

+
+

FWPM_CONDITION_RPC_EP_FLAGS

+
+

-ref

+
+

UINT32

+
+

-ref + =3D=3D 1

+
+

Well-known + alias

+
+

-ref + =3D=3D RPC_FW_IF_FLAG_DCOM

+
+

FWPM_CONDITION_RPC_SERVER_NAME

+
+

 

+
+

 

+
+

 

+
+

FWPM_CONDITION_RPC_SERVER_PORT

+
+

-rsp

+
+

UINT16

+
+

-rsp + =3D=3D 1

+
+

FWPM_CONDITION_RPC_PROXY_AUTH_TYPE

+
+

 

+
+

 

+
+

 

+
+

FWPM_CONDITION_CLIENT_CERT_KEY_LENGTH

+
+

-cckl

+
+

UINT32

+
+

-cckl + =3D=3D 128

+
+

FWPM_CONDITION_CLIENT_CERT_OID

+
+

 

+
+

 

+
+

 

+
+

FWPM_CONDITION_NET_EVENT_TYPE

+
+

 

+
+

 

+
+

 

+
+

FWPM_CONDITION_KM_AUTH_NAP_CONTEXT

+
+

-kanc

+
+

UINT32

+
+

-kanc + =3D=3D 1

+
+

FWPM_CONDITION_PEER_NAME

+
+

 

+
+

 

+
+

 

+
+

FWPM_CONDITION_REMOTE_ID

+
+

 

+
+

 

+
+

 

+
+

FWPM_CONDITION_AUTHENTICATION_TYPE

+
+

-at

+
+

UINT32

+
+

-at + =3D=3D 1

+
+

FWPM_CONDITION_KM_TYPE

+
+

-kt

+
+

UINT32

+
+

-kt + =3D=3D 1

+
+

FWPM_CONDITION_KM_MODE

+
+

-km

+
+

UINT32

+
+

-km + =3D=3D 1

+
+

FWPM_CONDITION_IPSEC_POLICY_KEY

+
+

 

+
+

 

+
+

 

+
+

FWPM_CONDITION_QM_MODE

+
+

-qm

+
+

UINT32

+
+

-qm + =3D=3D 1

+
+

FWPM_CONDITION_IPSEC_SECURITY_REALM_ID

+
+

-isrid

+
+

String

+
+

-isrid + =3D=3D “MyIPsecSecurityRealm”

+
+

FWPM_CONDITION_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE

+
+

-asafv

+
+

String

+
+

-asafv + =3D=3D “”

+
+ +

 

+ +


+

+ +

See Also

+ +

Filtering +Conditions Available at Each Filtering Layer

+ +

 

+ +

 

+ +
+ + + + + +------=_NextPart_01CFE175.4E1A8DA0 +Content-Location: file:///C:/2267B225/ConditionsForCommandLine_files/themedata.thmx +Content-Transfer-Encoding: base64 +Content-Type: application/vnd.ms-officetheme + +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 +cmVsc1BLBQYAAAAABQAFAF0BAACtCgAAAAA= + +------=_NextPart_01CFE175.4E1A8DA0 +Content-Location: file:///C:/2267B225/ConditionsForCommandLine_files/colorschememapping.xml +Content-Transfer-Encoding: quoted-printable +Content-Type: text/xml + + + +------=_NextPart_01CFE175.4E1A8DA0 +Content-Location: file:///C:/2267B225/ConditionsForCommandLine_files/filelist.xml +Content-Transfer-Encoding: quoted-printable +Content-Type: text/xml; charset="utf-8" + + + + + + + +------=_NextPart_01CFE175.4E1A8DA0-- diff --git a/network/trans/WFPSampler/docs/FAST_PACKET_INJECTION.mht b/network/trans/WFPSampler/docs/FAST_PACKET_INJECTION.mht new file mode 100644 index 000000000..cbf914d3c --- /dev/null +++ b/network/trans/WFPSampler/docs/FAST_PACKET_INJECTION.mht @@ -0,0 +1,2169 @@ +MIME-Version: 1.0 +Content-Type: multipart/related; boundary="----=_NextPart_01CEBF8E.3A42A330" + +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_01CEBF8E.3A42A330 +Content-Location: file:///C:/0E5B2E2E/FAST_PACKET_INJECTION.htm +Content-Transfer-Encoding: quoted-printable +Content-Type: text/html; charset="windows-1252" + + + + + + + + + + + + +Fast Packet Injection + + + + + + + + + + +
+ +
+ +

FAST PACKET = +INJECTION

+ +
+ +

Overview

+ +

The Fast Packet Injection scenario will clone the pack= +et and +inject it back to the same layer.  = +No +modification is performed on the packet.

+ +

All filters added sit in WFPSampl= +er's +sublayer (which is weighted just below IPsec's sublayer), unless otherwise +specified using the -sl +<SUBLAYER> command line option.  +All filters are associated with WFPSampler's +provider.

+ +

This scenario is meant to display the performance impa= +ct of +injection.  There is no guarantee t= +hat +the injection will succeed for certain packets (loopback, IPsec, etc.).  All injection is performed synchronously +(inline) from within the ClassifyFastPacketInjection().  +Memory is only allocated in the case of outbound transport.

+ +

CompleteFastPacketInjection() will indicate the final status of the packet’s injection.  This function will also free any alloca= +ted +memory.

+ +

The following diagram shows how the code flows for this +callout:

+ +

= +
+Figure A. Code flow for Fast Packet Injection Scenario

+ +

Applicable Layers

+ +

v  +FWPM_LAYER_INBOUND_IPPACKET_V4

+ +

v  +FWPM_LAYER_INBOUND_IPPACKET_V6

+ +

v  +FWPM_LAYER_OUTBOUND_IPPACKET_V4

+ +

v  +FWPM_LAYER_OUTBOUND_IPPACKET_V6

+ +

v  +FWPM_LAYER_IPFORWARD_V4

+ +

v  +FWPM_LAYER_IPFORWARD_V6

+ +

v  +FWPM_LAYER_INBOUND_TRANSPORT_V4

+ +

v  +FWPM_LAYER_INBOUND_TRANSPORT_V6

+ +

v  +FWPM_LAYER_OUTBOUND_TRANSPORT_V4

+ +

v  +FWPM_LAYER_OUTBOUND_TRANSPORT_V6

+ +

v  +FWPM_LAYER_DATAGRAM_DATA_V4

+ +

v  +FWPM_LAYER_DATAGRAM_DATA_V6

+ +

v  +FWPM_LAYER_INBOUND_ICMP_ERROR_V4

+ +

v  +FWPM_LAYER_INBOUND_ICMP_ERROR_V6

+ +

v  +FWPM_LAYER_OUTBOUND_ICMP_ERROR_V4

+ +

v  +FWPM_LAYER_OUTBOUND_ICMP_ERROR_V6

+ +

v  +FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4

+ +

v  +FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6

+ +

v  +FWPM_LAYER_ALE_AUTH_CONNECT_V4

+ +

v  +FWPM_LAYER_ALE_AUTH_CONNECT_V6

+ +

v  +FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4

+ +

v  +FWPM_LAYER_ALE_FLOW_ESTABLISHED_V6

+ +

v  +FWPM_LAYER_STREAM_PACKET_V4                                     (Win7+= +)

+ +

v  +FWPM_LAYER_STREAM_PACKET_V6                                     (Win7+= +)

+ +

v  +FWPM_LAYER_INBOUND_MAC_FRAME_ETHERNET       (Win8+)

+ +

v  +FWPM_LAYER_OUTBOUND_MAC_FRAME_ETHERNET   (Win8+)

+ +

v  +FWPM_LAYER_INBOUND_MAC_FRAME_NATIVE            (Win8+)

+ +

v  +FWPM_LAYER_OUTBOUND_MAC_FRAME_NATIVE        (Win8+)

+ +

v  +FWPM_LAYER_INGRESS_VSWITCH_ETHERNET                  (Win8+)

+ +

v  +FWPM_LAYER_EGRESS_VSWITCH_ETHERNET                    (Win8+)

+ +

Command Line Usage

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

Option

+
+

Argument

+
+

Meaning

+
+

-s

+
+

FAST_PACKET_INJECTION

+
+

Implement the FAST_PACKET_INJECTION scenario

+
+

-l

+
+

Applicable Layer

+
+

Layer at which this filter will apply

+
+

-sl

+
+

Applicable subLayer

+
+

SubLayer to associate with the filter= +.  [default is + WFPSAMPLER_SUBLAYER].

+
+

-v

+
+

 

+
+

Make the objects associated with this scenario’s instance dynamic= +

+
+

-b

+
+

 

+
+

Make the objects associated with this scenario’s instance availab= +le + during boot-time

+
+

-r

+
+

 

+
+

Remove objects associated with this scenario instance

+
+

-?

+
+

 

+
+

Display help

+
+ +

 

+ +

WFPSampler.E= +xe -s +FAST_PACKET_INJECTION -? +provides help output

+ +

WFPSampler.E= +xe -s +FAST_PACKET_INJECTION -l FWPM_LAYER_INBOUND_IPPACKET_V4 -v“ adds a dynamic filter (-v) at FWPM_LAYER_INBOUND_IPPACKET_V4 (-l) which references the appropriate callout.  This filter will have no conditions, me= +aning +it will act on all traffic seen at this layer.

+ +

WFPSampler.E= +xe -s +FAST_PACKET_INJECTION -l FWPM_LAYER_INBOUND_IPPACKET_V4 -v -r“ removes (-r= +) +the dynamic filter (-v) at +FWPM_LAYER_INBOUND_IPPACKET_V4 (-l= +) +which references the appropriate callout.

+ +

WFPSampler.E= +xe -s +FAST_PACKET_INJECTION -l FWPM_LAYER_INBOUND_IPPACKET_V4 -ipla +1.0.0.1 -ipra 1.0.0.254= +“ adds +a persistent filter at FWPM_LAYER_INBOUND_IPPACKET_V4 (-l) which references the appropriate callout.  This filter will have 2 conditions; +FWPM_CONDITION_IP_LOCAL_ADDRESS (-= +ipla) equals 1.0.0.1, and +FWPM_CONDITION_IP_REMOTE_ADDRESS (= +-ipra) equals 1.0.0.254.

+ +

For a list of conditions applicable to each layer, ref= +er to Filtering +Conditions Available at Each Filtering Layer.

+ +

For a list of command line parameters for configuring = +each +condition, refer to Conditions +for Command Line.

+ +
+ + + + + +------=_NextPart_01CEBF8E.3A42A330 +Content-Location: file:///C:/0E5B2E2E/FAST_PACKET_INJECTION_files/themedata.thmx +Content-Transfer-Encoding: base64 +Content-Type: application/vnd.ms-officetheme + +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 +cmVsc1BLBQYAAAAABQAFAF0BAACtCgAAAAA= + +------=_NextPart_01CEBF8E.3A42A330 +Content-Location: file:///C:/0E5B2E2E/FAST_PACKET_INJECTION_files/colorschememapping.xml +Content-Transfer-Encoding: quoted-printable +Content-Type: text/xml + + + +------=_NextPart_01CEBF8E.3A42A330 +Content-Location: file:///C:/0E5B2E2E/FAST_PACKET_INJECTION_files/image001.emz +Content-Transfer-Encoding: base64 +Content-Type: image/x-emz + +H4sIAAAAAAAEC7VWW2yURRQ+/7+lrE2RVUrTldtWQArtw5oQUh9oZ1kvQUuCKOUBL+XWWLZIA4So +Tz/QGBJvTVoxiqVrGom2mjRq4gPGrOKDD2CLRDBBTLEJwaBJvUR9gfX7zuxstwS0ljq73/+dOXNm +5syZ8/8znoi0Ah5QCqR8kUrAlZqHRH6sEYndt+Z+WkUiIuvRWOQMcpyeJtIeEqnBQJ9e0zbcWSTx +i75gAMFQEgMwXLVnPJkLOQL4kcw5+tCUA22fAh4DaBs3vtrZeYP6BaZI/UWzzDfT8vI8I1IGXRjg +MhbgUWIk4+YRyWY5TwhenJCsV5mz3QwuBdi2KAeRYCXEXBmTOe4SoAKgPUvWkiwGu3GcDF+CRujj +AEKEmUUGAfqGsFHvx2W0crSUoCy+6zsdfZ0M06DAtwTquRLkZfrG2HUBBwHGLmzEN5CXA4wzSL7i +A6XkbG8d+xSCemcHMRs2Xoh9q3J6+iyGa1ngDypiGk/WxfO8faHbZs42yBWYFQEsi3LAEm4Y0ydh +UwvQFzKLY0Za57XqcU8XH/ZzMmzzMecezQdOAcxtjnNtvFl3fScTc8aG+04fGP/3MGkaYPyXGbv+ +CuhFMquU8nLMzFhRnwgbv7gwxtK/rI4m9J0FPklSEtIgq2WVrMNTZIXxg3thUGuNNK8pzlHbVtkk +u/FrkWZ5Fj6xtkfWgrdISrZBXi1Py3ZIWyC3yE7UJBmqk/JYnbz+uOXPD1j+4X3l4KdBW//zF2Wz +N1RPe/NHRDmoXqycydRaHlqjLGeesPX0DuVY9X7LH76oHKRfVR4+36NsDvcpp+UDywMfKzctPK4s +h7609UdOK2fMd8qxoyPKwcglyxt/V0Yy1CMouVIo3w6de1+5P5RZygHK1K0D2hBY7u1oNuteddRE +PqESZToyPYnYtiKSm2UXnpMrlzHeWnT9VjIPcoRzr3QPP5eiZEtwaSXTV7qKVymHc/pIjuWqbU8f +sBw/aVOo/I398/9CEnVcsXrH6S/uSbLr8/1nE2QXG+YbS0SfY/0yfpW+0heO/PamyzXm29KX2vmK +ae6Rh87YeZ1/339j685/9mGJ6FNkz5B4W/fqZzv/DjO0jwKjwEyA+3Ec4EScu6/vaOLlkXcSbON4 +RANQWLBdWtqhTGJ3/vs7sUmd4qK5luIcWOcGUMdYFbbdco3etbEP29xY7MfxnB5ifu0zsBX8jnQA +7QC/I9VmLD+n4pu+CONWAPzWaxpj/EHp8bq8Ho/f8Zv5hvOcqwVYHE/kG85+7juM+AYbUKefJ4Fu +bPoh4C2A/uK8jJroaNTZI56Bk9E8obOyCobMK87LeDN/DGDPTS9UAzkGMEbcJzZ2eofx6/E6vV7g +MGO1r8x4QQTNaoMz040LVyd09i2BXQVA+8Li1kP9LIC+Unb6MNbZiDr97EQDvFH0gulLPDqA32g0 +Ho3fQdn1Y6yuNx66TChujJWLDWNVYsbuWOl4m55F3V7KO4HT52Zj0RZPY7axNV9v/XG0Mz9SQAuS +4xR4GnRD0YHyoWhkXsddBOWB8sIYOBmmE153F4wPAjZHxDeQlwPMEdCU3K26vVNei0+kPMaR9am4 +WzHPa+kkiuPJvJfMuTjAfGth3BHz0/mYj4834+/iPBXv6DOYpxVg/JeZG9+tKi8MJnc90J/853vS +TtkhbTgZeCPaNtF7UtH6lXpPem2mvQ9tSNn7UfkxyxcuWf3ZK8qmuczeQ+5eqJzpt/cjSTRY/cOb +lE3jLuWgA+cw71WXX7C8+5Byusnej+TIu1pv2viRciZzzHLbZ8rByAnlWOPXtj7rvPKwXFQ2zT8r +p3t/tTznqjJegpu4J7k9Rirkz+23sU8bAL6TzBP88+c225qBfz+3J7VHeibzvMVZOu7cLUY9DCAX +/7dzey7GjgDM0cJzuwG6rQBjVApshxADXJ1xmg0slT60unKjXXHt4/lOVDk2Lz78Thf64hvbBrXc +CpRRQIkAlP8GnyqBLhAQAAA= + +------=_NextPart_01CEBF8E.3A42A330 +Content-Location: file:///C:/0E5B2E2E/FAST_PACKET_INJECTION_files/image002.png +Content-Transfer-Encoding: base64 +Content-Type: image/png + +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 +AEBwguAEAAAEJwhOAAAQnCA4AQBAcILgBAAABCcITgAAEJwgOAEAQHCC4AQAAAQnCE4AABCcIDgB +AEBwguAEAAAEJwhOAAAQnCA4AQBAcILgBAAABCcITgAAEJwgOAEAQHCC4AQAAMEpOEFwAgCA4ATB +CQAAghMEJwAACE7BCYITAAAEJwhOAAAQnCA4AQBAcApOEJwAACA4QXACAIDgBMEJAACCU3CC4AQA +AMEJghMAAAQnCE4AABCcQgIEJwAACE4QnAAAIDhBcAIAgOAEBCcAAAhOEJwAACA4QXACAIDgBAQn +AAAIThCcAAAgOEFwAgCA4AQEJwAACE4QnAAAIDhBcAIAgOAEBCcAAAhOEJwAACA4QXACAIDgBAQn +AAAIThCcAAAgOEFwAgCA4AQEJwAACE4QnAAAIDhBcAIAgOAEBCcAAAhOEJwAACA4QXACAIDgBAQn +AAAIThCcAAAgOEFwAgCA4AQEJwAACE4QnAAAIDhBcAIAgOAEBCcAAAhOEJwAACA4QXACAIDgBAQn +AAAIThCcAAAgOEFwAgCA4AQEJwAACE4QnAAAIDhBcAIAgOAEwSk4AQBAcILgBAAAwQmCEwAABCcI +TsEJAACCEwQnAAAIThCcAAAgOEFw+iUCAACCEwQnAAAIThCcAAAgOEFwAgAAghMEJwAACE4QnAAA +IDhBcAIAAIITBCcAAAhOEJwAACA4QXACAACCEwQnAAAIThCcAAAgOEFwAgAAghMEJwAACE4QnAAA +IDhBcAIAAIITBCcAAAhOEJwAACA4QXACAACCEwQnAAAIThCcAAAgOEFwAgAAghMEJwAACE4QnAAA +IDhBcAIAAIITBCcAAAhOEJwAACA4QXACAIDgFJwgOAEAQHCC4AQAAMEJghMAAASn4ATBCQAAghME +JwAACE4QnAAAIDgFJwhOAAAQnCA4AQBAcILgBAAAwSk4QXACAIDgBMEJAACCEwQnAAAITiEBghMA +AAQnCE4AABCcIDgBAEBwigkQnAAAIDhBcAIAgOAEwQkAAIITEJwAACA4QXACAIDgBMEJAACCExCc +AAAgOEFwAgCA4ATBCQAAghMQnAAAIDhBcAIAgOAEwQkAAIITEJwAACA4QXACAIDgBMEJAACCExCc +AAAgOEFwAgCA4ATBCQAAghMQnAAAIDhBcAIAgOAEwQkAAIITEJwAACA4QXACAIDgBMEJAACCExCc +AAAgOEFwAgCA4ATBCQAAghMQnAAAIDhBcAIAgOAEwQkAAIITEJwAACA4QXACAIDgBMEJAACCExCc +AAAgOEFwAgCA4ATBCQAAghMEp+AEAADBCYITAAAEJwhOAAAQnCA4BScAAAhOEJwAACA4QXACAIDg +BMHplwgAAAhOEJwAACA4QXACAIDgBMHpFwkAAAhOEJwAACA4QXACAIDgBMEJAAAIThCcAAAgOEFw +AgCA4ATBCQAACE4QnAAAIDhBcAIAgOAEwQkAAAhOEJwAACA4QXACAIDgBMEJAAAIThCcAAAgOEFw +AgCA4ATBCQAACE4QnAAAIDhBcAIAgOAEwQkAAAhOEJwAACA4QXACAIDgBMEJAAAIThCcAAAgOEFw +AgCA4ATBCQAAglNwguAEAADBCYITAAAEJwhOAAAQnIITBCcAAAhOEJwAACA4QXACAIDgFJwgOAEA +QHCC4AQAAMEJghMAAASn4ATBCQAAghMEJwAACE4QnAAAIDgFJwhOAAAQnCA4AQBAcILgBAAAwSkm +QHACAIDgBMEJAACCEwQnAAAITkBwAgCA4ATBCQAAghMEJwAACE5AcAIAgOAEwQkAAIITBCcAAAhO +QHACAIDgBMEJAACCEwQnAAAITkBwAgCA4ATBCQAAghMEJwAACE5AcAIAgOAEwQkAAIITBCcAAAhO +QHACAIDgBMEJAACCEwQnAAAITkBwAgCA4ATBCQAAghMEJwAACE5AcAIAgOAEwQkAAIITBCcAAAhO +QHACAIDgBMEJAACCEwQnAAAITkBwAgCA4ATBCQAAghMEJwAACE5AcAIAgOAEwQkAAIITBCcAAAhO +EJyCEwAABCcITgAAEJwgOAEAQHCC4BScAAAgOEFwAgCA4ATBCQAAghMEp+AEAADBCYITAAAEJwhO +AAAQnCA4/RIBAADBCYITAAAEJwhOAAAQnCA4AQAAwQmCEwAABCcITgAAEJwgOAEAAMEJghMAAAQn +CE4AABCcIDgBAADBCYITAAAEJwhOAAAQnCA4AQAAwQmCEwAABCcITgAAEJwgOAEAAMEJghMAAAQn +CE4AABCcIDgBAADBCYITAAAEJwhOAAAQnCA4AQAAwQmCEwAABCcITgAAEJwgOAEAAMEJghMAAAQn +CE4AABCcIDgBAEBwCk4QnAAAIDhBcAIAgOAEwQkAAIJTcILgBAAAwQmCEwAABCcITgAAEJyCEwQn +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 +nAAAABx6/w88/2BLTSaXCAAAAABJRU5ErkJggk== + +------=_NextPart_01CEBF8E.3A42A330 +Content-Location: file:///C:/0E5B2E2E/FAST_PACKET_INJECTION_files/oledata.mso +Content-Transfer-Encoding: base64 +Content-Type: application/x-mso + +0M8R4KGxGuEAAAAAAAAAAAAAAAAAAAAAPgADAP7/CQAGAAAAAAAAAAAAAAABAAAAAQAAAAAAAAAA +EAAA/v///wAAAAD+////AAAAAAAAAAD///////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////9 +/////v///wMAAAAEAAAABQAAAAYAAAAHAAAACAAAAAkAAAAKAAAACwAAAAwAAAANAAAADgAAAA8A +AAAQAAAAEQAAABIAAAATAAAAFAAAABUAAAAWAAAAFwAAABgAAAAZAAAAGgAAABsAAAAcAAAAHQAA +AB4AAAAfAAAAIAAAACEAAAAiAAAA/v////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////1IA +bwBvAHQAIABFAG4AdAByAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAWAAUA//////////8BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABDTr+bIv84B +/v///wAAAAAAAAAAXwAxADQANAAyADIAMwA3ADIANQAyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAABgAAgH///////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAACAAAApEEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///////////////wAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////// +////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGIA +AHic7Hp5WBNX3/Y5M5ONLXtAQRMIIFaUsClaNQlIEZUSkLi1CgmyKRAEUuvW4MLigiyK1VYLKlql +rdRabG3V0NrFp1rBVq1dFBRl1TIJu0DmOwF8nn59r+t52vf9672u9+jNzJxZzjlzzu933/dAfR23 +8ejZ8Q/An8pcgAMLxQL0P9TBMYwUDgDY2LGFoqjn1dT/lf9VZRiBGJvDaWNb65wzEJgILAQbBFsE +OwR7BAcE9ugSAFwEHgIfQYAgRBAhOCI4IYxDGI/gjOCCMAFhIoIYQYLgiuCGIEVwR/BA8ESYhOCF +MBnhBYQpCN4IU8f66DO2/b/yPyvRQIf+ZaO5CAXpaJsJNvw5FfzbIgK0f8a8NR8IxmMj9bWjp1/6 +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 +AAAAAAAAAAAAAAAAAAAAAAAAAAAA + +------=_NextPart_01CEBF8E.3A42A330 +Content-Location: file:///C:/0E5B2E2E/FAST_PACKET_INJECTION_files/filelist.xml +Content-Transfer-Encoding: quoted-printable +Content-Type: text/xml; charset="utf-8" + + + + + + + + + + +------=_NextPart_01CEBF8E.3A42A330-- diff --git a/network/trans/WFPSampler/docs/FAST_STREAM_INJECTION.mht b/network/trans/WFPSampler/docs/FAST_STREAM_INJECTION.mht new file mode 100644 index 000000000..c9b460eb6 --- /dev/null +++ b/network/trans/WFPSampler/docs/FAST_STREAM_INJECTION.mht @@ -0,0 +1,1960 @@ +MIME-Version: 1.0 +Content-Type: multipart/related; boundary="----=_NextPart_01CEBF8E.80A71D60" + +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_01CEBF8E.80A71D60 +Content-Location: file:///C:/0E5B2E2E/FAST_STREAM_INJECTION.htm +Content-Transfer-Encoding: quoted-printable +Content-Type: text/html; charset="windows-1252" + + + + + + + + + + + + +Fast Stream Injection + + + + + + + + + + +
+ +
+ +

FAST STREAM = +INJECTION

+ +
+ +

Overview

+ +

The Fast Stream Injection scenario will clone the data= + and +inject it back to the stream.  No +modification is performed on the data.

+ +

All filters added sit in WFPSampl= +er's +sublayer (which is weighted just below IPsec's sublayer), unless otherwise +specified using the -sl +<SUBLAYER> command line option.  +All filters are associated with WFPSampler's +provider.

+ +

This scenario is meant to display the performance impa= +ct of +stream injection.  All injection is +performed synchronously (inline) from within the ClassifyFastStreamIn= +jection().

+ +

CompleteFastStreamInjection() will indicate the final status of the data’s injection.  This function will also free any alloca= +ted +memory.

+ +

The following diagram shows how the code flows for this +callout:

+ +

= +
+Figure A. Code flow for Fast Stream Injection Scenario

+ +

Applicable Layers

+ +

v  +FWPM_LAYER_STREAM_V4

+ +

v  +FWPM_LAYER_STREAM_V6

+ +

Command Line Usage

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

Option

+
+

Argument

+
+

Meaning

+
+

-s

+
+

FAST_STREAM_INJECTION

+
+

Implement the FAST_STREAM_INJECTION scenario

+
+

-l

+
+

Applicable Layer

+
+

Layer at which this filter will apply

+
+

-sl

+
+

Applicable subLayer

+
+

SubLayer to associate with the filter= +.  [default is + WFPSAMPLER_SUBLAYER].

+
+

-v

+
+

 

+
+

Make the objects associated with this scenario’s instance dynamic= +

+
+

-b

+
+

 

+
+

Make the objects associated with this scenario’s instance availab= +le + during boot-time

+
+

-r

+
+

 

+
+

Remove objects associated with this scenario instance

+
+

-?

+
+

 

+
+

Display help

+
+ +

 

+ +

WFPSampler.E= +xe -s +FAST_STREAM_INJECTION -? +provides help output

+ +

WFPSampler.E= +xe -s +FAST_STREAM_INJECTION -l FWPM_LAYER_STREAM_V4 -v“ a= +dds +a dynamic filter (-v) at +FWPM_LAYER_STREAM_V4 (-l) which +references the appropriate callout.  This +filter will have no conditions, meaning it will act on all traffic seen at = +this +layer.

+ +

WFPSampler.E= +xe -s +FAST_STREAM_INJECTION -l FWPM_LAYER_STREAM_V4 -v -r“ +removes (-r) the dynamic +filter (-v) at FWPM_LAYER_STRE= +AM_V4 +(-l) which references the appr= +opriate +callout.

+ +

WFPSampler.E= +xe -s +FAST_STREAM_INJECTION -l FWPM_LAYER_STREAM_V4 -ipla +1.0.0.1 -ipra 1.0.0.254 –i= +prp +80“ adds a persistent filter at +FWPM_LAYER_STREAM_V4 (-l) which +references the appropriate callout.  This +filter will have 3 conditions; FWPM_CONDITION_IP_LOCAL_ADDRESS (-ipla= +) equals +1.0.0.1, FWPM_CONDITION_IP_REMOTE_ADDRESS (-ipra) equals 1.0.0.254, and +FWPM_CONDITION_IP_REMOTE_PORT (-iprp) equal 80.

+ +

For a list of conditions applicable to each layer, ref= +er to Filtering +Conditions Available at Each Filtering Layer.

+ +

For a list of command line parameters for configuring = +each +condition, refer to Conditions +for Command Line.

+ +
+ + + + + +------=_NextPart_01CEBF8E.80A71D60 +Content-Location: file:///C:/0E5B2E2E/FAST_STREAM_INJECTION_files/themedata.thmx +Content-Transfer-Encoding: base64 +Content-Type: application/vnd.ms-officetheme + +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 +cmVsc1BLBQYAAAAABQAFAF0BAACtCgAAAAA= + +------=_NextPart_01CEBF8E.80A71D60 +Content-Location: file:///C:/0E5B2E2E/FAST_STREAM_INJECTION_files/colorschememapping.xml +Content-Transfer-Encoding: quoted-printable +Content-Type: text/xml + + + +------=_NextPart_01CEBF8E.80A71D60 +Content-Location: file:///C:/0E5B2E2E/FAST_STREAM_INJECTION_files/image001.emz +Content-Transfer-Encoding: base64 +Content-Type: image/x-emz + +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 +Acp/A8yyqPEQEAAA + +------=_NextPart_01CEBF8E.80A71D60 +Content-Location: file:///C:/0E5B2E2E/FAST_STREAM_INJECTION_files/image002.png +Content-Transfer-Encoding: base64 +Content-Type: image/png + +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 +ghMAABCcIDgBAEBwguAEAADBCYITAAAQnCA4AQBAcILgBAAAwQmCEwAAEJwgOAEAQHCC4AQAAMEJ +ghMAABCcIDgBAEBwguAEAADBCYITAAAQnCA4AQBAcILgBAAAwQmCEwAAEJwgOAEAQHCC4AQAAMEJ +ghMAABCcIDgBAEBwguAEAADBCYITAAAEp+AEwQkAAIITBCcAAAhOEJwAACA4BScITgAAEJwgOAEA +QHCC4AQAAMEpOEFwAgCA4ATBCQAAghMEJwAACE7BCYITAAAEJwhOAAAQnCA4AQBAcAoJEJwAACA4 +QXACAIDgBMEJAACCExCcAAAgOEFwAgCA4ATBCQAAghMQnAAAIDhBcAIAgOAEwQkAAIITEJwAACA4 +QXACAIDgBMEJAACCExCcAAAgOEFwAgCA4ATBCQAAghMQnAAAIDhBcAIAgOAEwQkAAIITEJwAACA4 +QXACAIDgBMEJAACCExCcAAAgOEFwAgCA4ATBCQAAghMQnAAAIDhBcAIAgOAEwQkAAIITEJwAACA4 +QXACAIDgBMEJAACCExCcAAAgOEFwAgCA4ATBCQAAghMQnAAAIDhBcAIAgOAEwQkAAIITEJwAACA4 +QXACAIDgBMEJAACCEwSn4AQAAMEJghMAAAQnCE4AABCcIDgFJwAACE4QnAAAIDhBcAIAgOAEwemX +CAAACE4QnAAAIDhBcAIAgOAEwQkAAAhOEJwAACA4QXACAIDgBMEJAAAIThCcAAAgOEFwAgCA4ATB +CQAACE4QnAAAIDhBcAIAgOAEwQkAAAhOEJwAACA4QXACAIDgBMEJAAAIThCcAAAgOEFwAgCA4ATB +CQAACE4QnAAAIDhBcAIAgOAEwQkAAAhOEJwAACA4QXACAIDgBMEJAAAIThCcAAAgOEFwAgCA4ATB +CQAAglNwguAEAADBCYITAAAEJwhOAAAQnIITBCcAAAhOEJwAACA4QXACAIDgFJwgOAEAQHCC4AQA +AMEJghMAAASn4ATBCQAAghMEJwAACE4QnAAAIDiFBAhOAAAQnCA4AQBAcILgBAAAwSkmQHACAIDg +BMEJAACCEwQnAAAITkBwAgCA4ATBCQAAghMEJwAACE5AcAIAgOAEwQkAAIITBCcAAAhOQHACAIDg +BMEJAACCEwQnAAAITkBwAgCA4ATBCQAAghMEJwAACE5AcAIAgOAEwQkAAIITBCcAAAhOQHACAIDg +BMEJAACCEwQnAAAITkBwAgCA4ATBCQAAghMEJwAACE5AcAIAgOAEwQkAAIITBCcAAAhOQHACAIDg +BMEJAACCEwQnAAAITkBwAgCA4ATBCQAAghMEJwAACE5AcAIAgOAEwQkAAIITBCcAAAhOEJyCEwAA +BCcITgAAEJwgOAEAQHCC4BScAAAgOEFwAgCA4ATBCQAAghMEp18iAAAgOEFwAgCA4ATBCQAAghME +p18kAAAgOEFwAgCA4ATBCQAAghMEJwAAIDhBcAIAgOAEwQkAAIITBCcAACA4QXACAIDgBMEJAACC +EwQnAAAgOEFwAgCA4ATBCQAAghMEJwAAIDhBcAIAgOAEwQkAAIITBCcAACA4QXACAIDgBMEJAACC +EwQnAAAgOEFwAgCA4ATBCQAAghMEJwAAIDhBcAIAgOAEwQkAAIITBCcAAAhOwQmCEwAABCcITgAA +EJwgOAEAQHAKThCcAAAgOEFwAgCA4ATBCQAAglNwguAEAADBCYITAAAEJwhOAAAQnIITBCcAAAhO +EJwAACA4QXACAIDgFJwgOAEAQHCC4AQAAMEJghMAAASnmADBCQAAghMEJwAACE4QnAAAIDgBwQkA +AIITBCcAAAhOEJwAACA4AcEJAACCEwQnAAAIThCcAAAgOAHBCQAAghMEJwAACE4QnAAAIDgBwQkA +AIITBCcAAAhOEJwAACA4AcEJAACCEwQnAAAIThCcAAAgOAHBCQAAghMEJwAACE4QnAAAIDgBwQkA +AIITBCcAAAhOEJwAACA4AcEJAACCEwQnAAAIThCcAAAgOAHBCQAAghMEJwAACE4QnAAAIDgBwQkA +AIITBCcAAAhOEJwAACA4AcEJAACCEwQnAAAIThCcAAAgOEFwCk4AABCcIDgBAEBwguAEAADBCYJT +cAIAgOAEwQkAAIITBCcAAAhOEJyCEwAABCcITgAAEJwgOAEAQHCC4PRLBAAABCcITgAAEJwgOAEA +QHCC4AQAAAQnCE4AABCcIDgBAEBwguAEAAAEJwhOAAAQnCA4AQBAcILgBAAABCcITgAAEJwgOAEA +QHCC4AQAAAQnCE4AABCcIDgBAEBwguAEAAAEJwhOAAAQnCA4AQBAcILgBAAABCcITgAAEJwgOAEA +QHCC4AQAAAQnCE4AABCcIDgBAEBwguAEAAAEJwhOAAAQnCA4AQBAcILgBAAAwSk4QXACAIDgBMEJ +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 + +------=_NextPart_01CEBF8E.80A71D60 +Content-Location: file:///C:/0E5B2E2E/FAST_STREAM_INJECTION_files/oledata.mso +Content-Transfer-Encoding: base64 +Content-Type: application/x-mso + +0M8R4KGxGuEAAAAAAAAAAAAAAAAAAAAAPgADAP7/CQAGAAAAAAAAAAAAAAABAAAAAQAAAAAAAAAA +EAAA/v///wAAAAD+////AAAAAAAAAAD///////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////9 +/////v///wMAAAAEAAAABQAAAAYAAAAHAAAACAAAAAkAAAAKAAAACwAAAAwAAAANAAAADgAAAA8A +AAAQAAAAEQAAABIAAAATAAAAFAAAABUAAAAWAAAAFwAAABgAAAAZAAAAGgAAABsAAAAcAAAAHQAA +AB4AAAAfAAAAIAAAACEAAAAiAAAAIwAAACQAAAAlAAAA/v////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////1IA +bwBvAHQAIABFAG4AdAByAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAWAAUA//////////8BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBNFC3Jv84B +/v///wAAAAAAAAAAXwAxADQANAAyADIAMwA3ADMANQAzAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAABgAAgH///////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAACAAAACUcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///////////////wAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////// +////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHYA +AHic7HsLWFTV+vdae++5cZv7AILOAF5QUYaboikzg5r3QGS8JcKA3BQY5KKZ2uCFiylyUUxLA/NW +WKIZlqVCmWVpgqVpWYKSgGCxhzsCs/9rzx6KPOd/Hk/nfM/3nec7i17WZa+99lrvWu/7+70Lq64S +1h46PeQ+eCoFAByYKB5gD2qDFjEnAQCYpW6iKGqgmfpv+o9K/UhsLXs4EeUsJPSec5BwkfCQWCGx +RmJj6eePcj5zBIAQiQiJGIkEiRSJDIk9EgckjkiGIHFC4oxkKJJhSORIFEhckLgicUMyHMkIJCOR +jELijmQ0kjFIxiLxQDIOyXgknkiUSLyQeCPxQeKLxA/JBMs8J1ly0/9VDf+/nUKAHv2kob2YAZJQ +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 +AAAAAAAAAAAAAAAAAAAAAAAA + +------=_NextPart_01CEBF8E.80A71D60 +Content-Location: file:///C:/0E5B2E2E/FAST_STREAM_INJECTION_files/filelist.xml +Content-Transfer-Encoding: quoted-printable +Content-Type: text/xml; charset="utf-8" + + + + + + + + + + +------=_NextPart_01CEBF8E.80A71D60-- diff --git a/network/trans/WFPSampler/docs/FLOW_ASSOCIATION.mht b/network/trans/WFPSampler/docs/FLOW_ASSOCIATION.mht new file mode 100644 index 000000000..c62cce6e5 --- /dev/null +++ b/network/trans/WFPSampler/docs/FLOW_ASSOCIATION.mht @@ -0,0 +1,2199 @@ +MIME-Version: 1.0 +Content-Type: multipart/related; boundary="----=_NextPart_01CEC02D.5020C7F0" + +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_01CEC02D.5020C7F0 +Content-Location: file:///C:/563B2E2E/FLOW_ASSOCIATION.htm +Content-Transfer-Encoding: quoted-printable +Content-Type: text/html; charset="windows-1252" + + + + + + + + + + + + +Flow Association + + + + + + + + + + +
+ +
+ +

FLOW ASSOCIA= +TION

+ +
+ +

Overview

+ +

The Flow Association scenario will cause the classific= +ation +to associate context to the flow at the specified layers.  This context will then be available as = +the flowContext parameter of those c= +lassifyFns. +

+ +

All filters added sit in WFPSampl= +er’s +sublayer (which is weighted just below IPsec’s sublayer), unless otherwise +specified using the –sl +<SUBLAYER> command line option.  +All filters are associated with WFPSampler’s +provider.

+ +

The following diagram shows how the code flows for thi= +s callout:

+ +


+Figure A. Code flow for Flow Association Scenario

+ +

When traffic matches a filter at the specified layer, = +ClassifyFlowAssociation() is invoked by the Filtering +Engine.  This function verify we ha= +ve +write access, and invoke PerformFlowAssociation().

+ +

PerformFlowAssociation() will create the FLOW_CONTEXT and call Fw= +psFlowAssociateContext() +for the layer(s) that were indicatedfor the con= +text +to be associated with (-awl).<= +/p> + +

This context will be valid until the flow is terminate= +d or +until FwpsFlowRemoveContext= +() is called, after which  +Notify= +FlowDeleteNotification() will be invoked and will clean= + up +the FLOW_CONTEXT.

+ +

Applicable Layers

+ +

v  +FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4

+ +

v  +FWPM_LAYER_ALE_ FLOW_ESTABLISHED_V6

+ +

Command Line Usage

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

Option

+
+

Argument

+
+

Meaning

+
+

-s

+
+

FLOW_ASSOCIATION

+
+

Implement the FLOW_ASSOCIATION scenario

+
+

-l

+
+

Applicable Layer

+
+

Layer at which this filter will apply

+
+

-aws

+
+

Scenario

+
+

Applicable scenario

+
+

-awl

+
+

Applicable Layer …

+
+

Layer(s) at which to associate Flow

+
+

-sl

+
+

Applicable sublayer

+
+

SubLayer to associate with the filter= +.  [default is + WFPSAMPLER_SUBLAYER].

+
+

-v

+
+

 

+
+

Make the objects associated with this scenario’s instance dynamic= +

+
+

-b

+
+

 

+
+

Make the objects associated with this scenario’s instance availab= +le + during boot-time

+
+

-r

+
+

 

+
+

Remove objects associated with this scenario instance

+
+

-?

+
+

 

+
+

Display help

+
+ +

 

+ +

WFPSampler.E= +xe -s FLOW_ASSOCIATION +-? +provides help output

+ +

WFPSampler.E= +xe -s FLOW_ASSOCIATION +-l FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4 -aws BASI= +C_STREAM_INJECTION +-awl FWPM_LAYER_STREAM_V4 -v  adds a dynamic filter (-v) at FWPM_LAYER_ALE_FLOW_ESTABL= +ISHED_V4 +(-l) which references the +appropriate callout.  This filter w= +ill +have no conditions, meaning it will act on all flows seen at this layer.  The context will then be visible to all +classifies at FWPM_LAYER_STREAM_V4 (-awl) +using the BASIC_STREAM_INJECTION callout (-aws).  No= +te that +the scenario for BASIC_STREAM_INJECTION must already have been configured +before implementing this scenario.

+ +

WFPSampler.E= +xe -s FLOW_ASSOCIATION +-l FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4  -aws +BASIC_STREAM_INJECTION -awl FWPM_LAYER_STREAM_V4 -v -r  removes (-r) +the dynamic filter (-v) at +FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4 (-l) +which references the appropriate callout.

+ +

WFPSampler.E= +xe -s FLOW_ASSOCIATION +-l FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4 -ipla 1.0= +.0.1 -ipra 1.0.0.254 –aws BASIC= +_STREAM_INJECTION +–awl FWPM_LAYER_STREAM_V4“ adds a persistent +filter at FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4 (-l) which references the appropriate callout.  This filter will have 2 conditions; +FWPM_CONDITION_IP_LOCAL_ADDRESS (-= +ipla) equals 1.0.0.1, and +FWPM_CONDITION_IP_REMOTE_ADDRESS (= +-ipra) equals 1.0.0.254.  +The context will then be visible to all classifies at +FWPM_LAYER_STREAM_V4 (-awl) us= +ing +the BASIC_STREAM_INJECTION callout (-aws).  No= +te that +the scenario for BASIC_STREAM_INJECTION must already have been configured +before implementing this scenario.

+ +

WFPSampler.E= +xe -s FLOW_ASSOCIATION +-l FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4  -aaid C:\Traffic.exe -ipla= + 1.0.0.1 +-ipra 1.0.0.254 -ipp TCP –iprp 6000 –aws BASIC_STRE= +AM_INJECTION +–awl FWPM_LAYER_STEAM_V4 FWPM_LAYER_ALE_ENDPOINT_CLOSURE_V4“ adds a +persistent filter at FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4  (-l) +which references the appropriate callout.  +This filter will have 5 conditions; FWPM_CONDITION_ALE_APP_ID (-aaid= +) equals +C:\Traffic.exe, FWPM_CONDITION_IP_LOCAL_ADDRESS (-ipla) equals 1.0.0.1, +FWPM_CONDITION_IP_REMOTE_ADDRESS (= +-ipra) equals 1.0.0.254, FWPM_CONDITION_IP_PROTOCOL  (-ipp)= + equals +TCP, and FWPM_CONDITION_IP_REMOTE_PORT equals 6000.    The +context will then be visible to all classifies at FWPM_LAYER_STREAM_V4 and +FWPM_LAYER_ALE_ENDPOINT_CLOSURE_V4 (-awl) +using the BASIC_STREAM_INJECTION (and PEND_ENDPOINT_CLOSURE) callout (-aws)= +.  Note that the scenario for +BASIC_STREAM_INJECTION and PEND_ENDPOINT_CLOSURE must already have been +configured before implementing this scenario.

+ +

For a list of conditions applicable to each layer, ref= +er to Filtering +Conditions Available at Each Filtering Layer.

+ +

For a list of command line parameters for configuring = +each +condition, refer to Co= +nditions +for Command Line.

+ +

Notes

+ +

FlowDelete

+ +

The FlowDeleteNotificationFn is +only invoked when the flow terminates or FwpsFlowRemov= +eContext +is called.

+ +

Mixing Scenar= +ios

+ +

FLOW_ASSOCIATION is only useful when mixed with other +scenarios.  For example:

+ +

                WFPSampler.exe +-s BASIC_STREAM_INJECTION -l FWPM_LAYER_STREAM_V4 -ipr= +a +1.0.0.254 -iprp 6000 -v

+ +

                WFPSampler.exe +-s PEND_ENDPOINT_CLOSURE -l FWPM_LAYER_ALE_ENDPOINT_CLOSURE_V4 -aaid C:\Traffic.exe -ipra +1.0.0.254 -ipp TCP -iprp +6000 -pcd 5000 -v

+ +

                WFPSampler.exe +-s FLOW_ASSOCIATION -l FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4 -aaid +C:\Traffic.exe -ipra 1.0.0.254 -ippTCP +-iprp 6000 -aws +BASIC_STREAM_INJECTION -awl FWPM_LAYER_STREAM_V4 +FWPM_LAYER_ALE_ENDPOINT_CLOSURE_V4 -v

+ +

This will associate flows with both the BASIC_STREAM_I= +NJECTION +and PEND_ENDPOINT_CLOSURE scenarios. The endpoint associated with the speci= +fied +flow will remain open for the duration of the injection being performed at +STREAM.  Once everything has been +injected, the endpoint is allowed to close, and the flow contexts are clean= +ed +up.

+ +
+ + + + + +------=_NextPart_01CEC02D.5020C7F0 +Content-Location: file:///C:/563B2E2E/FLOW_ASSOCIATION_files/themedata.thmx +Content-Transfer-Encoding: base64 +Content-Type: application/vnd.ms-officetheme + +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 +cmVsc1BLBQYAAAAABQAFAF0BAACtCgAAAAA= + +------=_NextPart_01CEC02D.5020C7F0 +Content-Location: file:///C:/563B2E2E/FLOW_ASSOCIATION_files/colorschememapping.xml +Content-Transfer-Encoding: quoted-printable +Content-Type: text/xml + + + +------=_NextPart_01CEC02D.5020C7F0 +Content-Location: file:///C:/563B2E2E/FLOW_ASSOCIATION_files/image001.emz +Content-Transfer-Encoding: base64 +Content-Type: image/x-emz + +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 +4gNo/wVJl/pwsBQAAB== + +------=_NextPart_01CEC02D.5020C7F0 +Content-Location: file:///C:/563B2E2E/FLOW_ASSOCIATION_files/image002.png +Content-Transfer-Encoding: base64 +Content-Type: image/png + +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 +wQkAAIITBCcAAAhOQHACAIDgBMEJAACCEwQnAAAITkBwAgCA4ATBCQAAghMEJwAACE5AcAIAgOAE +wQkAAIITBCcAAAhOQHACAIDgBMEJAACCEwQnAAAITkBwAgCA4ATBCQAAghMEJwAACE4QnIITAAAE +JwhOAAAQnCA4AQBAcILgFJwAACA4QXACAIDgBMEJAACCEwSnFxEAABCcIDgBAEBwguAEAADBCYIT +AAAQnCA4AQBAcILgBAAAwQmCEwAAEJwgOAEAQHCC4AQAAMEJghMAABCcIDgBAEBwguAEAADBCYIT +AAAQnCA4AQBAcILgBAAAwQmCEwAAEJwgOAEAQHCC4AQAAMEJghMAABCcIDgBAEBwguAEAADBCYIT +AAAQnCA4AQBAcILgBAAAwQmCEwAAEJwgOAEAQHCC4AQAAMEJghMAAASn4ATBCQAAghMEJwAACE4Q +nAAAIDgFJwhOAAAQnCA4AQBAcILgBAAAwSk4QXACAIDgBMEJAACCEwQnAAAITsEJghMAAAQnCE4A +ABCcIDgBAEBwCgkQnAAAIDhBcAIAgOAEwQkAAIJTTIDgBAAAwQmCEwAABCcITgAAEJyA4AQAAMEJ +ghMAAAQnCE4AABCcgOAEAADBCYITAAAEJwhOAAAQnIDgBAAAwQmCEwAABCcITgAAEJyA4AQAAMEJ +ghMAAAQnCE4AABCcgOAEAADBCYITAAAEJwhOAAAQnIDgBAAAwQmCEwAABCcITgAAEJyA4AQAAMEJ +ghMAAAQnCE4AABCcgOAEAADBCYITAAAEJwhOAAAQnIDgBAAAwQmCEwAABCcITgAAEJyA4AQAAMEJ +ghMAAAQnCE4AABCcgOAEAADBCYITAAAEJwhOAAAQnCA4BScAAAhOEJwAACA4QXACAIDgBMEpOAEA +QHCC4AQAAMEJghMAAAQnCE4vIgAAIDhBcAIAgOAEwQkAAIITBKcXEgAAEJwgOAEAQHCC4AQAAMEJ +ghMAABCcIDgBAEBwguAEAADBCYITAAAQnCA4AQBAcILgBAAAwQmCEwAAEJwgOAEAQHCC4AQAAMEJ +ghMAABCcIDgBAEBwguAEAADBCYITAAAQnCA4AQBAcILgBAAAwQmCEwAAEJwgOAEAQHCC4AQAAMEJ +ghMAABCcIDgBAEBwguAEAADBCYITAAAEp+AEwQkAAIITBCcAAAhOEJwAACA4BScITgAAEJwgOAEA +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 + +------=_NextPart_01CEC02D.5020C7F0 +Content-Location: file:///C:/563B2E2E/FLOW_ASSOCIATION_files/oledata.mso +Content-Transfer-Encoding: base64 +Content-Type: application/x-mso + +0M8R4KGxGuEAAAAAAAAAAAAAAAAAAAAAPgADAP7/CQAGAAAAAAAAAAAAAAABAAAAAQAAAAAAAAAA +EAAA/v///wAAAAD+////AAAAAAAAAAD///////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////9 +/////v///wMAAAAEAAAABQAAAAYAAAAHAAAACAAAAAkAAAAKAAAACwAAAAwAAAANAAAADgAAAA8A +AAAQAAAAEQAAABIAAAATAAAAFAAAABUAAAAWAAAAFwAAABgAAAAZAAAAGgAAABsAAAAcAAAAHQAA +AB4AAAAfAAAAIAAAACEAAAAiAAAAIwAAACQAAAAlAAAAJgAAAP7///////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////1IA +bwBvAHQAIABFAG4AdAByAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAWAAUA//////////8BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMBBkPxnwM4B +/v///wAAAAAAAAAAXwAxADQANAAyADMAMAA1ADUANgA4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAABgAAgH///////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAACAAAAw0kAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///////////////wAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////// +////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHwA +AHic7HsJWBPX2v97JpONLSEhAQVNABdUlLC5axLQulQFkbi0IgREQIUgi0vVBhcEq4ioWO3VghWt +SytaS1cVWttq6wL21urtIiiV1ZYJIItK5n8mE67obfvZ5Xn+332eb/THWebMmXPeec/5/d4ZqCh3 +rDpwqvdteOoYDxww00Lg9ahDVlgOMQBhLZtpmu6upv/v+K86ujBE1mc4GqdcDOaZ8zEEGEIMGwxb +DDsMewwHa/sxOHXEkGBIMZwwZBhyDGcMF4xeGL0xXDHcMPpg9MVQYCgx3DE8MDwx+mH0xxiAMRDD +C2MQxmCMIRjeGEMxhmH4YKgwfDH8MPwxAjACMYZjjMAYiTHKOtax1tT8/9Xa//uOMDDgf2n4WUyE +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 +AAAAAAAAAAAAAAAAAAAAAAA= + +------=_NextPart_01CEC02D.5020C7F0 +Content-Location: file:///C:/563B2E2E/FLOW_ASSOCIATION_files/filelist.xml +Content-Transfer-Encoding: quoted-printable +Content-Type: text/xml; charset="utf-8" + + + + + + + + + + +------=_NextPart_01CEC02D.5020C7F0-- diff --git a/network/trans/WFPSampler/docs/MatchTypesForCommandLine.mht b/network/trans/WFPSampler/docs/MatchTypesForCommandLine.mht new file mode 100644 index 000000000..1629d53c3 --- /dev/null +++ b/network/trans/WFPSampler/docs/MatchTypesForCommandLine.mht @@ -0,0 +1,1458 @@ +MIME-Version: 1.0 +Content-Type: multipart/related; boundary="----=_NextPart_01CF0C87.D6016E50" + +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_01CF0C87.D6016E50 +Content-Location: file:///C:/2267B225/MatchTypesForCommandLine.htm +Content-Transfer-Encoding: quoted-printable +Content-Type: text/html; charset="windows-1252" + + + + + + + + + +Match Types for the Command Line + + + + + + + + + + + +
+ +
+ +

WFPSampler C= +ondition +Match Types
+
+

+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

FWP_MATCH_TYPE

+
+

Command + line
+  parameter

+
+

Example + Usage

+
+

Interpretation

+
+

Flags + Interpretation

+
+

FWP_MATCH_EQUAL

+
+

=3D=3D

+
+

-ipp =3D=3D TCP

+
+

Match all packets that are IP Proto= +col + TCP (6).

+

[Only TCP packets will classify]

+
+

All specified flags are present
+ [FWP_MATCH_FLAGS_ALL_SET]

+
+

FWPM_MATCH_NOT_EQUAL

+
+

!=3D

+
+

-ipp !=3D TCP

+
+

Match all packets that are not IP + Protocol TCP (6).

+

[Every packet will classify except = +TCP + packets]

+
+

None of the specified flags are pre= +sent
+ [FWP_MATCH_FLAGS_NONE_SET]

+
+

FWPM_MATCH_GREATER

+
+

^>

+
+

-vlid ^> 0xFF

+
+

Match all frames with a VLAN higher + than 255

+

[frames with VLAN IDs 256+ will + classify]

+
+

Any of the  specified flags are present
+ [FWP_MATCH_FLAGS_ANY_SET]

+
+

FWPM_MATCH_GREATER_OR_EQUAL

+
+

^>=3D

+
+

-vlid ^>=3D 0xFF

+
+

Match all frames with a VLAN of 255= + or + higher

+

[frames with VLAN IDs 255+ will + classify]

+
+

Any of the specified flags are pres= +ent
+ [FWP_MATCH_FLAGS_ANY_SET]

+
+

FWPM_MATCH_LESS

+
+

^<

+
+

-vlid ^< 0xFF

+
+

Match all frames with a VLAN lower = +than + 255

+

[frames with VLAN IDs 254 and less = +will + classify]

+
+

Any of the specified flags are pres= +ent
+ [FWP_MATCH_FLAGS_ANY_SET]

+
+

FWPM_MATCH_LESS_OR_EQUAL

+
+

^<=3D

+
+

-vlid ^<=3D 0xFF

+
+

Match all frames with a VLAN of 255= + or + lower

+

[frames with VLAN IDs 255 and less = +will + classify]

+
+

Any of the specified flags are pres= +ent
+ [FWP_MATCH_FLAGS_ANY_SET]

+
+ +
+ +

·         +Note the escape (^) for the special characte= +r on +the command line

+ +

See also

+ +

                FWP_MATCH_TYPE +enumeration

+ +
+ + + + + +------=_NextPart_01CF0C87.D6016E50 +Content-Location: file:///C:/2267B225/MatchTypesForCommandLine_files/item0006.xml +Content-Transfer-Encoding: quoted-printable +Content-Type: text/xml + + + +------=_NextPart_01CF0C87.D6016E50 +Content-Location: file:///C:/2267B225/MatchTypesForCommandLine_files/props007.xml +Content-Transfer-Encoding: quoted-printable +Content-Type: text/xml + + + +------=_NextPart_01CF0C87.D6016E50 +Content-Location: file:///C:/2267B225/MatchTypesForCommandLine_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_01CF0C87.D6016E50 +Content-Location: file:///C:/2267B225/MatchTypesForCommandLine_files/colorschememapping.xml +Content-Transfer-Encoding: quoted-printable +Content-Type: text/xml + + + +------=_NextPart_01CF0C87.D6016E50 +Content-Location: file:///C:/2267B225/MatchTypesForCommandLine_files/filelist.xml +Content-Transfer-Encoding: quoted-printable +Content-Type: text/xml; charset="utf-8" + + + + + + + + + +------=_NextPart_01CF0C87.D6016E50-- diff --git a/network/trans/WFPSampler/docs/PEND_AUTHORIZATION.mht b/network/trans/WFPSampler/docs/PEND_AUTHORIZATION.mht new file mode 100644 index 000000000..492fcd19f --- /dev/null +++ b/network/trans/WFPSampler/docs/PEND_AUTHORIZATION.mht @@ -0,0 +1,2699 @@ +MIME-Version: 1.0 +Content-Type: multipart/related; boundary="----=_NextPart_01CEC02D.7438E4B0" + +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_01CEC02D.7438E4B0 +Content-Location: file:///C:/F83B2E2E/PEND_AUTHORIZATION.htm +Content-Transfer-Encoding: quoted-printable +Content-Type: text/html; charset="windows-1252" + + + + + + + + + + + + +Pend Authorization + + + + + + + + + + +
+ +
+ +

PEND AUTHORI= +ZATION

+ +
+ +

Overview

+ +

The Pend Authorization scenario will cause the +classification to pend for a specified period of time.  In a real world scenario, this time cou= +ld be +used to perform additional process of the packet.  For this scenario though, the thread is= + just +put to sleep for the specified period.

+ +

For the layers that contain an NBL, that NBL will be +injected back in the final action is to permit the traffic.

+ +

All filters added sit in WFPSampl= +er’s +sublayer (which is weighted just below IPsec’s sublayer), unless otherwise +specified using the –sl +<SUBLAYER> command line option.  +All filters are associated with WFPSampler’s +provider.

+ +

The following diagram shows how the code flows for this +callout:

+ +


+Figure A. Code flow for Pend Authorization Scenario= +

+ +

When traffic matches a filter at the specified layer, = +ClassifyPendAuthorization() is invoked by the Filtering +Engine.  This function validates th= +at we +can perform the injection by looking at the pClassifyO= +ut +rights.  It will then create the +INJECTION_DATA which consists of the injectionHandle +and the injectionState.  If the injectionSt= +ate +indicates that we haven’t injected this packet before, then the triggerFn is called.= +   +At this point, the original packet will be blocked.

+ +

Because we are essentially telling the classifyFn +“Hold on, we need more time”, the injection will always be asynchronous. TriggerPendAuthoriz= +ationOutOfBand() is invoked.  This function creates the CLASSIFY_DATA= + which +consists of copies and references of the data that was passed into the classifyFn.  = +Based on +the queuing method, the appropriate queueFn is +invoked.  The only time a DPC can b= +e used +is if there is no delay.  This is d= +ue to +the fact that the delay introduced is done by using a function only availab= +le +at PASSIVE_LEVEL.  Introducing dela= +ys at +DISPATCH_LEVEL is rarely a good idea.

+ +

Regardless of which queueFn is +used, each will call the PerformPendAuthorization().

+ +

 PerformPendAuthoriza= +tion() will cause the thread to sleep = +for +the duration provided.  When it wak= +es up, +if the layer contains an NBL and the final action is to allow the traffic, = +then +the data offset of the original NBL is retreated to the beginning of the IP +Header.  A clone is created, and the +offset of the original is advanced back to the original offset.  Once the clone is ready, it is injected= + back +into the TCP/IP stack.

+ +

Upon successful injection, CompletePendAuthoriz= +ation() will be called by the TCP/IP +stack.  This function will show the +status of the injected packet.  +Additionally, any memory that was allocated from the functions above, will be freed and any references released.

+ +

Note that with long delays, it is possible to bugcheck the machine by holding on to the inbound NBL= + for +too long.  This normally happens wh= +en a +machine requests to drop into a power managed state, and processing of the = +NBL +exceeds the allotted time for allowing the state transition to begin.

+ +

Applicable Layers

+ +

v  +FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V4

+ +

v  +FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V6

+ +

v  +FWPM_LAYER_ALE_AUTH_LISTEN_V4

+ +

v  +FWPM_LAYER_ALE_AUTH_LISTEN_V6

+ +

v  +FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4

+ +

v  +FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6

+ +

v  +FWPM_LAYER_ALE_AUTH_CONNECT_V4

+ +

v  +FWPM_LAYER_ALE_AUTH_CONNECT_V6

+ +

Command Line Usage

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

Option

+
+

Argument

+
+

Meaning

+
+

-s

+
+

PEND_AUTHORIZATION

+
+

Implement the PEND_AUTHORIZATION scenario

+
+

-l

+
+

Applicable Layer

+
+

Layer at which this filter will apply

+
+

-pcd

+
+

Integer

+
+

How long of a pend completion delay to introduce (in ms)

+
+

-fab

+
+

 

+
+

Return FWP_ACTION_BLOCK after the delay.  If there is an NBL, no injection occu= +rs. + [default]

+
+

-fap

+
+

 

+
+

Return FWP_ACTION_PERMIT after the delay.  If there is an NBL it will be injecte= +d.

+
+

-sl

+
+

Applicable subLayer

+
+

SubLayer to associate with the filter= +.  [default is + WFPSAMPLER_SUBLAYER].

+
+

-v

+
+

 

+
+

Make the objects associated with this scenario’s instance dynamic= +

+
+

-b

+
+

 

+
+

Make the objects associated with this scenario’s instance availab= +le + during boot-time

+
+

-tdpc

+
+

 

+
+

Use threaded DPCs for asynchronous (out of band) queuing method +

+

-wi

+
+

 

+
+

Use work items for asynchronous (out of band) queuing method

+
+

-r

+
+

 

+
+

Remove objects associated with this scenario instance

+
+

-?

+
+

 

+
+

Display help

+
+ +

 

+ +

WFPSampler.E= +xe -s PEND_AUTHORIZATION +-? +provides help output

+ +

WFPSampler.E= +xe -s +PEND_AUTHORIZATION -l FWPM_LAYER_ALE_AUTH_CONNECT_V4 -v  adds a dynamic filter (-v) at FWPM_LAYER_ALE_AUTH_CONNEC= +T_V4 (-l) which references the appropri= +ate +callout.  This filter will have no = +conditions, +meaning it will act on all connections seen at this layer.  The traffic will, by default, be blocke= +d.

+ +

WFPSampler.E= +xe -s +PEND_AUTHORIZATION -l FWPM_LAYER_ALE_AUTH_CONNECT_V4 -v -r  removes (-r) the dynamic filter (-v) at FWPM_LAYER_ALE_AUTH_CONNEC= +T_V4 (-l) which references the appropri= +ate +callout.

+ +

WFPSampler.E= +xe -s +PEND_AUTHORIZATION -l FWPM_LAYER_ALE_AUTH_CONNECT_V4 -= +ipla +1.0.0.1 -ipra 1.0.0.254 -f= +ap“ adds a persistent filter at FWPM_LAYER_ALE_AUTH_CONN= +ECT_V4 +(-l) which references the appr= +opriate +callout.  This filter will have 2 +conditions; FWPM_CONDITION_IP_LOCAL_ADDRESS (-ipla) equals 1.0.0.1, and +FWPM_CONDITION_IP_REMOTE_ADDRESS (= +-ipra) equals 1.0.0.254. The traffic will be permi= +tted (-fap)= + and the +NBL injected.

+ +

WFPSampler.E= +xe -s +PEND_AUTHORIZATION -l FWPM_LAYER_ALE_AUTH_CONNECT_V4  -aaid +C:\Traffic.exe -ipla 1.0.0.1 -ipra +1.0.0.254 -ipp TCP -fap –pcd 5000“ adds a persistent filter at FWPM_LAYER_= +ALE_AUTH_CONNECT_V4  (-l) +which references the appropriate callout.  +This filter will have 4 conditions; FWPM_CONDITION_ALE_APP_ID (-aaid= +) equals +C:\Traffic.exe, FWPM_CONDITION_IP_LOCAL_ADDRESS (-ipla) equals 1.0.0.1, +FWPM_CONDITION_IP_REMOTE_ADDRESS (= +-ipra) equals 1.0.0.254, and FWPM_CONDITION_IP_PROTOCOL  (-ipp)= + equals +TCP.  The decision will be delayed = +for 5 +seconds (-pcd= +) +before being permitted (-fap).

+ +

For a list of conditions applicable to each layer, ref= +er to Filtering +Conditions Available at Each Filtering Layer.

+ +

For a list of command line parameters for configuring = +each +condition, refer to Co= +nditions +for Command Line.

+ +
+ + + + + +------=_NextPart_01CEC02D.7438E4B0 +Content-Location: file:///C:/F83B2E2E/PEND_AUTHORIZATION_files/themedata.thmx +Content-Transfer-Encoding: base64 +Content-Type: application/vnd.ms-officetheme + +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 +cmVsc1BLBQYAAAAABQAFAF0BAACtCgAAAAA= + +------=_NextPart_01CEC02D.7438E4B0 +Content-Location: file:///C:/F83B2E2E/PEND_AUTHORIZATION_files/colorschememapping.xml +Content-Transfer-Encoding: quoted-printable +Content-Type: text/xml + + + +------=_NextPart_01CEC02D.7438E4B0 +Content-Location: file:///C:/F83B2E2E/PEND_AUTHORIZATION_files/image001.emz +Content-Transfer-Encoding: base64 +Content-Type: image/x-emz + +H4sIAAAAAAAEC9VbDXSU1Zm+38wkGSLSgSQQIOAXm4YAQxkzoLhnCR8EqbSo0SJ4Vo87Ij+yxTqH +oodV0/1CY4+7osYWTwVcmlZsxZ+a7rotu0dwwN9tCQzFU8V1cSCWqqVLWlxWD26zz/PeuZNv0kSG +YeRkL3nmuf/fve9973t/vg9LKbUKsAA/sBk/zT540i78FaXWT1PKvuyKecxlNyj1N8gcMBnS3F6k +VCvKhpG2s09a6rsBFTnqU6hAhQEbQHWTLcdSVfCHAF8o8R9sQywN5r0FuAFg3ojjk3z6ue6sC5yA +Goo0uvFOUcY/zlGqHHFBgN24Gz+ljkqY5yjV08Pn+FWz2qP+ZFWn8y4Bsz6m1aShlDsT3rTr9bPe +CUAlwPx0PZrUF8CmHuNHW9xFiI8AlHEzsA+4CxVBbIhPlkRUx4TuoQT9yRJTtgRljR9ZXU/bZiOc +dm7Gz7ZRdhuAewHKLugonwP/NIByBqm9/IErfeOxBpbxgvEmH7w9Qcfys2xdOp5tVg77cpdvn6BZ +5MmwsiyrBckVGNsQOADQ1aSBLgwo079GnhkA20KmM0xJy3N1dNavkQ/LGT/yZmTOMboL2A9Qt1lP +X3kzbMrmI3PKhuPONlD+j+Ch6wHKf5Kj+1+JeKUSc4Qyfts5Wbd/TtDxFXtlrJ6ahJnWq19ok2pU +s9UCNV/NUdfgV6mLHZ8bwTNqADjRa3pHA42Y1Tepb+DfSrVc/a1qUsvU19VS1HC7WoOZdZtajZQ7 +kWcN+DakqSNjG9RIzO8Zdwi7w9s0X/RvOv4v39bheX/S4S1DZkn+8aOEna9OFk4E5wi731qs01d+ +Xaf/eJ1w+4YHhWMlm4UTq34o7L7zjObF/yqcOpYQdia9osNb92oOH9Tx30kJt+/7jeYv/UEYAz+L +4tDO6x+BKDM3ORb0040E6GfcNUAcQuQ4dvf0mGmNkFLPMxKuBFrdCMmtguSWiBx1/Jn+HkF9u/Gs +Hf7El1nWeejlgPWj3lriCyZSVdW0Ks3BdFIozW2P6Hj72xMkXyqZkpSRm9aN/wgKE0qnG95++yWN +zDByyvE5ZCObvvWa/B3XjpHpe3jLiUeNXlG3Jt7fOp7l6aeLv5USNvX8w0EdNu03+UKSC/r6ekot +vUNMdGa+UrQLgXeB4cBKYDfAectnP/nkj2Y/0PXEbKatBVjnAsDrMFziXETmof/SIHaY/ShOg2EK +l3FDPEx/aTqOeRkm6GcZsqmHYabRGRtzPsQ/D+EZQBignZjs9OpkqdO7ZiXiem5Pt5619mA21yE/ +9ZVyqUkD1Q1oVycgT2U6PwgDlhAybWE9xh9ERYsQjgDTkfAsYGFwKgDKoamse0xTWVN1yCbo7x5j +ykLlXONH1kG1Vk23KnyWj3jWohwZLsRaxfV8BkBnmCNDWfXnjHxYzviRNyPzKOT9DOCHvEdlZJ4t +b8rflM1H5nV4NvWHbaAOPofnPQGcbq3adPC+xvue3tnIdcdF/jUAXGbdqUZgodjDFWoF1pvVOaw6 +V8l6dBW0eg4sKlcodeNvZ6o3H21QUy9vEL51nbC75Hs6/OYLOnzFe5p/gHUI+Z09lcKJlqnC7idz +hdWR63X41dXC9tXfFk69+JCwM3GjsHp0q+byDuHYtp8LJ44+r8M3vaz5/X06/so3hO31b2sOvCec +uuOEDv9ROWyX+1SxcOTGUuHUhuGaPywXbjs+RnPUFu4+9gVhKMVZrGNGP6BCYlfLMVafB7gnsRG5 +BYy/jF1l2magGnED2dU2qYv7hrMa3yy7GESdBsXw004yTCbO1MayzHnpsqyPfsyvrHoRzMw9Y4fb +ENcK9LXDhdg700ZXAtxTczyUw/3/EeuL1hGLNuhs9sqFsj+L0Sy2sxNIQQ8iwLsA29s91B6fGmGP +NzpVCJtDHXMAypvnizD8NkAZBcBMnGK9Azt9xJpiHQXeoaxayh3LDSFZ8sD21MFPW4am5rcWohyd +6RvrKQNMnSYeOik2mu2cgkxojayPR8FsS3JE97imMnt8ckQM6B5nylFW/dWHIjmvj0Y2lFWpZ1/Q +1KFtb6evC2fZAuwLOprQrGxZmH6Y/keQ3gml6AJvhPLNR/+L4E+GI8OS4bbytksJ+iPDTNl89IXr +0gbgXkDriPI58E8DqCOggpxhO33zrY1+ogv9ItCjApxhIZrMfmAGGysu/33BLyHzw6iD9zNfycg8 +W96U/9nIvA71U+/Zdsp/IXA5YOYoZc88lD/HnHN0s1Xku8XqsjZbu6y9wkWZfZV3rnJjbupH8/Oa +q6iCT815rkaQ9xY8bBewlzKDDNnujtFNtjsyMr5jdKgqVUVusr1yK0OevvMfUTnP1xgyU3aF0NvB +sDYsQl8iwHLI8AUgCZTkKEsjVxTPSX5GR4wOfoSCxwDKcpKj9aYSYdXnHuWNrn9unF/zfmPQ8Wff +o1w6TO5RpAh+YIv6uUfxu22IbwXgMvtZ7iWuxv50GbASe9MVysY/te3hBrWvtEGte1nYPfSBsGOX +zGK8u3e05v+pFk48Pk3YWXKZsFp1heaz2t8ZOWEYsu4sTphJgni6NowVXaHuLA5gU9UMIZ4apu8s +2h7OvrN4/YLS/Zzr+qnYOxw4EaA/UK/vEtLNUSYf2093TeWJcbyzSN2g7zJMuVG3vNXI9P99/Liw +2RObegx/+aob5M6DeelMefMcc2exfGz199m+4N4lkr8t8UfdPl1MmTDlS+etZ6A7C+pNKxpyAPgX +4DCAv8zemmncL1CfFgBex7bQxRDZj65JGeqsAecF/RgGsdOgjD00e9kOxG0FOGcmO9qWIahymT8X +O343jrxLWcAzF/i8KzAT9N0hUtvva1A7NjW4q17TvPd3wk7SmiXx3yzX3DBBGNO/39PMCNRrbC3n +Nf10IwH6GWdsCHVlIRAH1kGeXcBTwB8A/GXkzbTjANu8APA6Le2enusQ6e0PzwmUayDN9NOZZxvZ +uohbA+QrWwdlpwFwGTvDts9SiWOXfqqklMpHVpTXT/AAyuqfgL6yYhplhb8BZdUk7cMPXF95zEBc +GOgrj1LPPhWvXKSvK3zbreV4O1OH/BxbPrMmDejHTHjTrtfPMZ8AVALMT2esi2kL440/iIoWIRwB +VqDwdiRyv7oHXIS4eK07Nl6bqEqGCfrdsaYsxtw1fmTNab2ah4wbgHsByqAQ50XqB2Xk3Wet8O2x +OgXbLcqR4cG4T10GWf8Mst4Hlj0X+tFX3gwbOecjc8qG+mP2CNQ/G6D8S/vVu1PQu4qcdM20C10Y +UKdOIZE6FQKKkC9e23FBvFZVJ8ME/R0XmHry6d+506mQr9NHnII+EejRIDz7UKc+hsypUyMyMs+W +N+V/NjLvq1OtGNe1aZ0qxJymnasEOKfRBTk7PWrttm7y7R50d0CbIOsEcDMa+hKY7U2MtqtCtXZB +5+0CLYa03ez/Dijm2wm93G3FfK8AOwfNHVAMQkFrZI15BRzgmNamsJ7YVao2BqSy1pUyJJs1z+gp +onJeY86FPvIsf5OvmDagZTCcOxdDQJw3GyFfvg+hPgYB/MkZPlQbs40s87Gzfed8bvro862AjGK+ +IYBPZOW950DTethmAsM74J7GtBtdO+19I3UNTxJdGwIOsOpa246j/6q2DbCz5HC2ulaJZwwFuJ5O +cgY+c2/fac0dPXc/ztyB7DN3Tt8uBNwOdH4rBeDZC9cicPpvFebiJLJc3jCtxu9SlFiNbxhuFv/t +8C9Lv59fpdTJ/5wpXynsn6+/Wjj0oOZtOzSvwbkFXz0kNnxOf1VwZ63+iuCy6cLupAWax9ys0y/D +WyR+JZG4W7j9LbxNQrh9Cd4mMf61R4RjRVs0N28TtrufE04t2S3s2J2aUweE2//xkLCKHhWOvfZ7 +4cQ9Hwk7n1iOPGdbiXDTmvM0R4YLqx9XCHcUjRUOvWALx9fWCtuNGEm0L/mDqcKRL03XfN9fCKcm +zhSGzuZ9TjPzCaqTdSfxWX9H8SAM66Zipe4/v//vKHAH8Cue+6hodB0D30lIPrafztxJNF2rv7Mw +5Vb95PK5TK9++c1GspEY7TudVmml+t5JmPKmPfneSXjrGehOIoJGDEdHHgOH/fpegu3KfEeBNJ4H +Od9o97zOnJEfQWQh5yLvP4IAmcjn3R7LsI6iNGPYFeGtl2HWjzXhz+KZz0DsKMLGFpsz/nOQi3kf +PtnR6zayfer9ycwni+d+97oTjRc7AXcNyvPeGS5zxrcROL0sF8t3WV/D111rYMNuxSy6Td6P8/Zx +mVK//p62Zbtqte16+m7N1U9rPnZQ27JksbYZPxsnrFrCwu7yRs2L/kqnt6zU6Z98Q7h97TrN6n4d +v3yDcOyxjZrHPi5sP9Ah7Ha9qPl6vBGHbUl1/1rY+eJhzYe0LWtv/C8JK+tjnW9KkdgaZ+p5mn8X +Em7fOlK4afEYYXWySjj27IU6nJlpEKbHPwIhzj2KvDLtB33qHdJQZL4Qc+C34HGYHzxfsLyZH0w7 +iQgbcQPNj/VIO9sxpZ5SH8lEPnPic+myZh5Qr40fOp2pm/Uznun00/XV/TbEtQJ973M+qzNQp2+X +NcW/a9Cdgfi+7QWM/1ToxotgBPGOM1SVrA+d8zNQ2L/DikJGYf9LwI5+z0A1aB+BeVGQPSfXDDxJ +RcE8A1JnEvXJsZFoqCpR7wDJ/3fnm07s26f4B9/5hroWAKhr5nyTDMfsZP25P9+E/T5fFDIK+4cA +5+Z8Q13j+Ya6Zs43iXrbjkRjdqK+DSjs+Yb2bS1QqPtazjuuO967Hcow6m+SuTqYztIXQcZXYj5P +9+ga5RyJqgqzFmDf5Bo/upXTHUUdMnINRrXy7QDXTAfQMu7/bmeq/2rYtfnWJWD6ee/wWZ+lOcfm +o/+XgPFEsWvd9aGKSLStPB4NVdBv+k45lKEP7BeyZtZKeHOSyTxkDAM2QDmUOr3f9LZH4rI37PSN +Ksi3O/FIO57S20Zve4No7yKkRQDeW48C89udZnARkAzHrWQ4WdJ2KUF/3PLKwPiRNed+b0Dme4FC +zbFpqIs65n0n0ulrRj+IUegXgR4Nwvtr2vcKtJ3f7vwdWMs8W96Uv5FzIebfubFxXbDZ3xKZDzYb +1wI508b9hjoDRKKRYUCwkDLOzca1QkbNsDetWN8xKv3YOOq1sTE18BOYagPu4yYgtRLgHPc60zfG +lwGmThMfTNsB2iTaQc5/2kHqSgDorneCkWiyJB51gvSbcoWwg0vRqOsA2oNJzsB3im80RufCYjo8 +R0eQv4ad8ZyjRyPQiFPxrfgKYBXOxDwnn/4MphKb9cn54jp9Uj7Vonn5q5ov+m9hp6xIn1QfGCOc +uG6isHugQcf//bU63v814fYPcfeDk29sxXodv1CflN1Xt0o4FX5a5/s+viVHvvZR+sSsnvmFhGMf +6JOzWvamhNXvj+j4az4QTjx4XHPJKZ3uOfViMD03dSMgFzPe1A366UYC9DPOjCfnw0LgXeB1BKAC +6mH8VIApbnMGZhrrpcwXAF5n7ohcROYxHpkz7/koz7Mo9Q/6mTm3Mo5hMnGm52IUyfQXz3C5Fnt1 +cLLTK6NJ8NcgnTLCKWeOUNpPfTz5xJVyr0NdrOyjjyxD/VuN+2j+n7pbc9HG2ZbWulE3aLY2CruL +fp7mXwk7Y5Ue9Xn6bto9dZEO//RyrRWTbxR2ht8uHDt1j45/7zvC7vVbhFOJx4Xboz/V/NTzwmr2 +S8Kxb/67Dn+8X3PL2zr+aJdw4vPva978oU7P0rxeLaSunKkOvo0yB6Fn+8APQQcPg706yLRDiKOc +B9LBu5F2pmNg9KsYZYsAhuk3+hhMxxVK96pQXwig/fPqHvu0FEA3ZR5uggz4f5ZNmPu0OxGYqJ5E +DuN6ZZ5tBUx6Nl+I4FCAc5bzwNsWn9M7D4YhrRygCwH0/x/VMFMIID4AAD== + +------=_NextPart_01CEC02D.7438E4B0 +Content-Location: file:///C:/F83B2E2E/PEND_AUTHORIZATION_files/image002.png +Content-Transfer-Encoding: base64 +Content-Type: image/png + +iVBORw0KGgoAAAANSUhEUgAAA5wAAAJ9CAYAAABZ3X1CAAAAAXNSR0ICQMB9xQAAAAlwSFlzAAAO +xAAADsQBlSsOGwAAABl0RVh0U29mdHdhcmUATWljcm9zb2Z0IE9mZmljZX/tNXEAAFCmSURBVHja +7d0LkF13feB5tdySbCLbkuz4AXrx0MMOWUsNRookLKejKCqN0dgoKo0QjRXBKEGAwiwwIoTEYUNA +hCSTqpCZQIBMEVKbCEMcJpXKzG5sQkjNbMhjtybhkWSnZmuBULszoSDZgG1Jd/+/zvl1/XV8bvdt +qbvVLX1U9Smr7+Oc/z23Zd2v/v9zetE73vGORQAAADDTHAQAAAAEJwAAAIITAAAAwekgAAAAIDgB +AAAQnAAAwBX9sL9o0beKXrHsanttO3fufPfQ0NC5/fv3/8ChQ4eum4Nj+c3mWF7ve0twAgDAnDlz +5szyHTt2nIkAiihZuXLlX+3bt+/UXITQlQ7O48ePj5TX/WSzn3GrV6/+w6NHj+45e/bs4tna76ZN +m36j7Pf8nj17frg+zvV42vfN1+CczW0/9NBDR8rxeGquwlxwAgDADMfmunXrnigf6i/U0RWOHDmy +vfx3aKEE59atW38pormEyfZerzfQuEvI/FB5ztPt1x7Kdr5r0O3MVHCW1/BvMvwvJeJOnTq1IY7b +xo0bHyuWLKTgzNdejseOjH3BCQAAC1g9s5mBefr06TtGRkbePzY29pKFFJwZcZcSnBloTYA/3hWD +sx2cse94zffcc8+v7Ny5871NfO2dzkzr8ePHt8YM6VwG5wwek19vB+eV5n8SAABweVE3vnzzoYce +enm/D/kxaxYBGoGUS24PHDjwcD6+hMLZuC+2sWrVqs+Xx1yI4Ilg3bVr13syaHfv3v1jGVf5nPL1 +95bnfC6eE0tZSzDdnZHbDs5YbtrMxo6PI8KsjG11PD4DrjVLOf68MtZX5Liacfx4ieqbu4IzYrs8 +9i/isRmDcVvOnuaS2/LatmXU5ozivn373pCzpTG28rwVeQwPHz78QL7OeOzatWs/3Q7OeEzM5pXj +eLDE/4H4/fbt23+ubHe4XzjmDGDsL453e3lw2f4bY/v5vPg6Hl8dv5W57fL8++rjG68z/hGiep/H +g7Acz++P/cTz45jVY2qO51P1GHJ2smzrn9Tbj++jg+VX3Jfbbr1/N5Tn/mBsL8adx2HQcZZNf1+8 +lzF737xn3zHdGWv/kwAAgBkIzvKhf1XX/RFb+Zi2jNSMx67HtMWH/iYQO58TEVL+u7QdnP3GESER +0dMvODPi2s+L6IzQmWxJbRnruhhriZY/z1htLTleH/dX4+x1hVazzLXzGNbBmUtKY7s529melWwH +Zx3MEcFTBWe/MUbEtZ/bfp3tKNywYcNvlf0uHTQ4+0Rl74EHHri1ef+mDM7Jxln28dyucaYMZMEJ +AABzoImaSYNzdHT0dHx4Lx/WP5yzgnlbM6s3nPEYs3HxmJhRzIiLGcxy2405axhf15GawVVHWYmK +F7ZCblnuM2dJY+y5zXx815LatWvXPp4zqbHfVvwt6wrOCJMTJ07cFdtsAuepeK05G5iznRlSOc78 +uhnrxKxp7iNnPYs728t2MzCbgF5aB2jMeObs3WTBGfuaakltNcZ/Gc9ronFJOU6/14znjTmejMC8 +LUMuYj3e93hM15hCOX4bymPHX0/5/fj3Vsxwnjx5clO8N83795+b74kdzffEM5bUtoOzjPN/zXHm +GPJ5eVt+vW3btn8Vx7t5D/N76QbBCQAAcyTj6/Dhw51XZc0wrC8gVAJipPz+qXZw5mPa99fbycCq +ltROxGH7tjo4+8xg9upA7QrOfrOP7eBsB1qabAa0HZz10t86+nIb9Wxm+xzOXBrbtZ96Zu5ygzOf +l4/L4OyKxpytbAdnXEyp/l5pP7crNkOE31133XW2Pfs4neAsj/+Hdji2H9O1na7nCU4AAJhlzWzd ++fZFg3bt2nWmRMm9eQ5mPcOZFxqK2y43OAed4cyfW1mfB9oRx7/RnEt6sIz1prgtl8PmDGe/oOwX +nLkkt57h7Ij2gYIzwjGORxzf9gxnzjD2i+PymFvquMtj1ByXZwRnszR5Wb9ZyHZw5mxjPcOZY2ov +iZ0sOPvFZrMk+ps5ruYY/F7zDwQXBWecI5rvXzsmy/P/c5yTWc9w5jjL18db4xScAABwJTWh91Sf +c/e2l2h40WT31+djXkpw9jsnsx1yTcQ9Yxx1KGaUds1g9jt3cqrgbJ1L2eu6KNFUwdn1sz7rcZS4 +ek5soz5/NWVM5bLafueT1hc9qsfbcQ5nZ3B2nXtZn2PZvIdTBmfHj3UZH0Nzcadv9onp8TAs79+7 +Wu/VM87hzK8HHKfgBACA+RCd7avQlg/3p3Imsbkq6KfyZ3WuWbPmM0ePHv2e9lVqMzgzYuvgzJnU +EgzH6nM4Y8ayRNQX8uqtee5kV8hV4zjfFZyx3zyvs463iKn6KrV1cDZLWSdmH6c4PuemE5z1NuuL +F8WYDxw48Kr8+ZJlDK9souoZP4Yll9rm66xfY8R5eR/21FeMjefk+ZnNhZ3GQ7VfcNbPi+N0yy23 +TBynuC+uGlwteb7k4IzjUC8bjrGXx36oDsOYHc2gjvevjGtZ11Vq47Yyzs/l9+Mk4xScAABwLeo6 +hxPmGwcBAAAEJwhOAADg4uDMi984JghOAAAABCcAAAAITgAAmCWHDh06cP/99z9S/AQwubGxsdGL +rsDrfyIAANBf+fXkttsP9Lbf8SAwia3fvre3Yvkt/33RouddLzgBAGDA4HzDPR/svXHLvwUmcfzu +n+2tuvE2wQkAAIITBCcAAAhOEJwAACA4QXAKTgAAEJwgOAEAQHCC4AQAAMEJglNwAgCA4ATBCQAA +ghMEJwAACE4QnIITAAAEJwhOAAAQnCA4AQBAcILgFJwAACA4QXACAIDgBMEJAACCEwSn/4kAAIDg +BMEJAACCEwQnAAAIThCcAACA4ATBCQAAghMEJwAACE4QnAAAgOAEwQkAAIITBCcAAAhOEJwAAIDg +BMEJAACCEwQnAAAIThCcAACA4ATBCQAAghMEJwAACE4QnAAAgOAEwQkAAIITBCcAAAhOEJwAAIDg +BMEJAACCEwQnAAAIThCcAACA4ATBCQAAghMEJwAACE4QnAAAgOAEwQkAAIITBCcAAAhOEJwAACA4 +BScITgAAEJwgOAEAQHCC4AQAAMEpOEFwAgCA4ATBCQAAghMEJwAACE5BAYITAAAEJwhOAAAQnCA4 +AQBAcAKCEwAABCcITgAAEJwgOAEAQHACghMAAAQnCE4AABCcIDgBAEBwAoITAAAEJwhOAAAQnCA4 +AQBAcAKCEwAABCcITgAAEJwgOAEAQHACghMAAAQnCE4AABCcIDgBAEBwAoITAAAEJwhOAAAQnCA4 +AQBAcAKCEwAABCcITgAAEJwgOAEAQHACghMAAAQnCE4AABCcIDgBAEBwAoITAAAEJwhOAAAQnCA4 +AQBAcAKCEwAABCcITgAAEJwgOAEAQHCC4LxywXny5MmNYa7/B3H69Ok7T5w48aIzZ84sn6//E4vx +zeWxif3FcfEXCACA4IQFFZwZeO2AKr96YS5D6siRIwdyv1u3bv3gVM+PKI1t1OYixPsdm3o8M3mM +Yl+bN2/+xEy/lhzvbMV9fm/5iw8AEJxwjQVnREZETMZTWLVq1V9meM52cHaFVO5z7dq1fzBIcO7c +ufO99fhThOtsj7vr2NTjuZQxZADWET6bwbl///7XxbbjvzMR4u24zO8v0QkACE64hoIzwiaiLuMo +wiC/zviY7eCMqIr91rEz3X1m0EQox+/jv7mN2VqC2i8445hmLOd/ZyIAZzM4jx07dn9sN/47A/9j +f8YxidcR27ccGAAQnHANBedDDz30ygyEeiltxE3OzHUFRC6R7LcMM2e52oHRtWw3Z/Pitvh9Pabc +fte26nNLMzi7orWeVeu31LW9j8nOzcz7+gVnLgeO/2Z01mPvOje1nhWM+3OGNP5bH4M6OCcbY/3+ +9Bt//j62nY/PceZ4+i1Rru/vF+H1fvLx7e+VfuOczvsBACA4YZ4GZ4ba6Ojo2yb5g3pRVNUzou1l +o13LcyMg477YR317LpWtQ6oOlpSzfTFrWYdKfVvuMwMtn1OHdH1bO0ZzvzGmena0np2sz9msZzDb +wRnbiNviWORrzmNQj6NfHPcbZ44xxtdvjF3HP9Qzl/mceia7PaaubWQAdi1dbi+/rmfM6+3l8Z5q +nIO8HwAAghPmeXBmdEx2bl07qrqWrmb41aEQv4/QqmdK43E5e5pxUwdnhEt7iW9ESN6WQZKzoJMF +UgZovY/2suF8Xe3Qre/PfbbHVT++Dr46jDJS61CaKjjj2NTHNY/LIGOsx9VeWpyzi+0gjGPZHlOE +ctxXby9nI3Pf9X15nNv7z3/IaAfnVOMc5LUCAAhOWCAznJNd2KYdVbn0NaIgPvzX99ezgBEb9VLS +Ok7qwO1aKtreZy5TzbBpL1Vtn8MZwVu/plyiGmPK2cKMnPbsYYZZPqd9LmveP9U46+12zbROtvx3 +snM4+42xnn2sl67Wr7PeV/3e9LtoUG6/nsGO52V85vO6LvjU9X3WniXtN85B3g8AAMEJ8zw4c/ln +15VgM0jqgKiXQrZnwPJ5eQGa9pLaCK766q1dS2r7hVzOHGa4tWcNu87h7AqenJmr1TN3dTi1I6w9 +pq5xdi03rgN8JoKz3xjr2cd+sdcvCLv2147NfA9zSW89Uzyd4BxknIO8HwAAghPmeXC2f95lzlrG +77tCK5ey1ktm20GaM1IZhhkNGbBxf85mDRqcdQB1/biRqYIzz6XM80RTffGcQYMzXnM8vh5Pvr56 +mWjKKMtlx7nduD2X0PYLzhzvIFFcR3kGaP3+dP0DQr/XmsernqmN7ec/UOT28xh0BWe8rvYS2tzO +VOMUnACA4ISrIDi7Qq49MznZuY7t8xjrUMrQymhpn/vXPu9zquBsX7SnXo45VXD2u9hNe5+TBU77 +okft157R1HUBpq6L/3TJQKuXKk9n1q/fGOsxDRKc/cbWdUGj9pja57q2g3OQcQpOAEBwwlUSnCFm +oyIMu85/zJm6+rF5W0RWXmAmw66+4Ezcn2GYP4sxLyZT/wiO+gIz+XM5u5b51lejbQfTVD9Hsj22 +ep/tMdSvs95m7idneOPxOc7cdtfPmszn5XHNZcd5rHNf7fMq8zFx+6BjjNviOV3vZdf7WW8nH5vP +r+W5u/W48nXUY4rH5fPrKxTnNgYZ56CvFQBAcMICCM6Fon21WgAAEJwgOC9bvSzW/0ABABCcIDhn +TC6pdA4fAACCEwQnAAAIThCcAAAgOEFwtoLzTW960/r777//J4DBxJ8ZfwkDwLURnNtuP9DbfseD +wCS2fvve3orlt3QH5/iH6EWLeo8AU/rO66578qGHHjrmL2EAuPodOnToQPms/Ih/cJ8/7rvvvnfe +dttt/8fu3bv/J8djfhkbGxs9e/bs4s7gjA/SPWBKr1i69O8EJwDAlbF37943DA0NPX3gwIFDddww +/whOEJwAAAvGI488cvvw8PDX48ccLl++/Kt79uy52XERnCA4AQC4bFu2bPnA0NDQuQjOsGvXrnfu +27dv2LERnCA4AQC4ZMePH7+nxOaTGZtheHj4m2NjY+vK74ccI8EJghMAgEuyevXq3y/Beb4OznD3 +3Xef3bhx4xLHSHCC4AQAYNpidvO5z33u/7J+/fonSnj+xyVLlvzD2rVrfz++DidOnFhjllNwguAE +AOCynD59et2KFSu+WAJzmeMhOEFwAgAgOAWnkADBCQAgOBGcIDgBAASnYyI4QXACACA4BScgOAEA +BCeCEwQnAIDgRHCC4AQAQHAKTkBwAgAITgQnCE4AAMGJ4ATBCQCA4BScgOAEABCcCE4QnAAAghPB +CYITAADBKTgBwQkAIDgRnCA4AQAEJ4ITBCcAAIJTcAKCEwBAcCI4QXACAAhOBCcITgAABKfgBAQn +AIDgRHCC4AQAEJwIThCcAAAITsEJCE4AAMGJ4ATBCQAgOBGcIDgBABCcghMQnAAAghPBCYITAEBw +IjhBcAIAIDgFJyA4AQAEJ4ITBCcAgOBEcILgBABAcApOQHACAAhOBCcITgAAwYngBMEJAIDgFJyA +4AQAEJwIThCcAACCE8EJghMAAMEpOAHBCQAgOBGcIDgBAAQnghMEJwAAglNwguAUnAAAghPBCYIT +AEBwIjhBcAIAIDgFJwhOwQkAIDgRnCA4AQAEJ4ITBCcAAIJTcILg9D8RAADBieAEwQkAIDgRnCA4 +AQAQnIITBCcAAIITwQmCEwBAcCI4QXACACA4BScITgAABCeCEwQnAIDgRHCC4AQAQHAKThCcAAAI +TgQnCE4AAMGJ4ATBCQCA4BScIDgBABCcCE4QnAAAghPBCYITAADBKThBcAIAIDgRnCA4AQAEJ4IT +BCcAAIJTcILgBABAcCI4QXACAAhOBCcITgAABKfgBMEJAIDgRHCC4AQAEJwIThCcAAAITsEJghMA +AMGJ4ATBCQAgOBGcIDgBABCcghMEJwAAglNwCk4QnAAAghPBCYITAEBwCk7BCYITAGBWnDp1asOJ +Eyde1Ov1hq6RwLyjvN6R8t8b5yI4c39nz55d7PtNcILgBADmZSBFFIaTJ09uLHE0I3G4Y8eOM0ND +Q+fK9nr79+8/NtdRlLGbytfPnqnX1s/WrVt/KV7znj17fvjQoUPX1cGZ45nJcWzatOk3yv7Ol319 +10wf3zLWDfH9MFv/WHD8+PGtc/GeCE4QnADAFbJq1aq/KB/4L0QUppUrV/5ViYG7LycEIq7K85+K +7W3cuPGxffv2vSECbC5Duuz7yfp15VjKfStma78ZgO3gHBsbW1f2/608vuW/Sy81oOuwnM3gLL++ +2Ry36y9nO2fOnFke/6hRx2XEZhn3tzZs2PBb5T1ZOh/+LAhOEJwAwEx/yG6ibM2aNZ8p8fKbES9V +FC251O1GYERwNrE5PNevqwTNSHktE8G7efPmT+Rr2759+8/N1pj6Befu3bt/rNz+9OrVq/+wCcS9 +0w3ErgCczeDcunXrvyk+WI7fksvZzv79+38w3otyTN6Yxz3+QSDGHv8QcSW+PwQnCE4AYA6D88iR +I9tj9ilDsQmbiZmnXHrbXnJbz17Vv8/ltBGyY2Nj2+plmfm4mK3Lcx3rUMx9xO/j/vYMWf2Yemz1 +turgzPMnS/j8UERfBGgdUf2Wutbnn3bto31s4r5+wblq1aq/DOU4f2+5/8l29MaMX/2aOl5zzAiO +v1cRl/nYOjjjsf3GmPvoep31+Is7cxltPj5Ctn7PupYo1/fXt8e2d+7c+e447tu2bftX5XvhJfV7 +2rVkt984Y4lvjmey90NwguAEAOZZcMY5luWD/otHRkbenzOBEZwRBlu2bKlvG58NLR/219SBes89 +93w4HxOxFYFRL2UtQbQ9wmJ0dPR0nteZMs6a8XwrZgHXrl37BxluMa6Ix7KPX6mfG+FYtvej9b7K +dr43gqQOztj3sWPH7l+3bt0Tsc3YTsReBGXels/P+2IsGXO7d+/+8a59xGOamDpXL0eOJcp1cN50 +003/JW6L7ZSvb86ltfWFhNqzl+04ru6feO1xe44xvq5fRz2Devjw4QcyVuvXWV7/ympfT0UQVq/z ++npMuQS23kYuhy3vwb9sv9+xrRhfzm62vhd2RDi2l9SWGP8nk42zvNZfj2NdjuMj9TYPHjz4fTMx +uys4QXACALMUnG3l88PL40N8hk9GzqpVq+ICOBciMCPM6hnRiK0Sip8pQfFQedzn89zQeF4Eann8 +a+ttlcd+OiMpg7SKsfHHHDhw4FUZnHl7Lkvt+jpDrDXDeZGxsbEX1rODMe56OXF5zT8QsZj3t/cR +v49IKtu5L/cRt+WxqSM6gvP666//ah2BsVQ1wqmOwqmCsx5LE9pvj+M/1RjrmdG4rXmd55oxvjHG +mMGZj4nYj+fWY4rZxbvuuutsLE3O1xlR2YxhPAQjHlvv6XMjdvM84TzO5bh9Rzs466DtGme9nz6v +9bKvAiw4QXACALMUnHkO544dO36mfPi/N5cyZixEgEb4xUxhFXJLMzibpbMrc7t1MOWMYdnWn8e2 +Mi7jtpwhzEDL4GwusjP+mIzHCJbcRwmbx+ulqxl/eUGernM4Yzb01KlTq2O79UWFYjlqBFAZ82u7 +Ii8DtHnOxOxkvsaYuWzPitbBuXjx4r+v91Pue2s8L2bvcmnvVMHZ9Zh6f33GeH0uZ824jOfkjGcT +e0vqGc56mW/X/iI8Iwwj8srvV+Vt9RLc/J6JmczmHy2ecQ5n+6JBZZzvynHmY3LGMx+TwVnuP169 +1hzjDYITBCcAME+DM8/h7Hd/RluKi8nUM5ztiwN1BWcVeKvaj2sHZ73cNOOxjq/q3MXtTexcdM5m +1zmcteb+zteWV9Stz4/MQO4KznpJcDs4i0P9ZlqbY3HLTARnfdGg+nHV/ROzqTnr2Q7OOkq79tcV +myGX1Mb2mosznculs4MGZ8Zkxzi7HrOjeq3/IDhBcAIACzQ4m5nEOP/wx2Lms33BmOkEZ85Kxsxe +XDymni0tn1cORkTMVXDGuam5r9h3zN7ma8sL0QwanBFgcWGkcgxftmrVqs/V53CWMP/XEUm5TDSj +NpeDVq/7m7mcN8YSy1fz3Mx2cOZ443lTBWfGYOz/6NGje+L15XmrOaM5SHDWM6cRhfkzW0NEYcws +x/dQcww6ZzgjHOM45cWN6pjsM87Hc5zNjLPgBMEJAFxNwVmfp9h1oZ/pBGe/bTXn4C1pB91sBmdo +LmD0dMes43hgThWcreWrncenirRb6n2Xz2dH6tdUn4vZdXGgJth/r32u6lTBOdkYy3u+Pt7zQYKz +Ps+zvmhQXNCnfUGj+uJAMabmfX9ysosGtZbHtrfz3OacW8EJghMAWEi2bt36SzHjlled7XpMROWu +XbveUx738ZyhO3z48IPxoT+u9Fq+/licH1nHSkRG3J7LU+t4rLcVFwUqsXFT3h8BFbe3f2xJzPjV ++4gAKo97tGzv7jwnMx6TS33bX/d7/XE+48jIyAfq15bbjCCN2/PrHF/Z5odymzG2uLJvPC5ea8wC +x36PHj36PSdPntz0/Oc//7dvuOGG/7cdvTm+fK0x4xrnW8Z24oJAca5p3F8fv2Zf8X59PI5b3B4z +g/VxqMeYxzD21byWR5tzWd+e57LmMYj3Ki72VF/tNQKvGd/4BZJyvCkvXBRLX/MYxG3ls+dYbC/G +lKEes5yx/xhX2feamBkt2/uN3MYg48zXGhcdyu3GGOvXKjhBcAIAXDPy53D2m2Vl/hCcIDgBAAQn +ghMEJwAAglNwguAEAEBwCk7BCYITAEBwIjhBcAIAc+qukf/hf77j+Wv/9Pbnrf0z5odb1z37z4eX +Lvnm7c9d/b87HvPLvfdt/8X66raCEwQnADDZDE38TM137Owteucu5pO3b3cM5ps3v7j3rNtu/m+L +nrfoesEJghMAGDQ4Hz3QW/TYg8Bkfnlv71l3rBCcIDgBAMEJghMEJwAgOEFwguAEAAQnCE7BCYIT +ABCcIDhBcAIAghMEJwhOAEBwguAUnCA4AQDBCYITBCcAIDhBcILgBAAEJwhOwQmCEwAQnCA4QXAC +AIITBCcITgBAcILgFJwgOAEAwQmCEwQnACA4QXCC4AQABCcITsEJghMAEJwgOEFwAgCCEwQnCE4A +QHCC4BScIDgBAMEJghMEJwAgOEFwguAEAAQnCE7BCYITABCcIDhBcAIAghMEJwhOAEBwguAUnCA4 +AQDBCYITBCcAIDhBcILgBAAEJwhOIQGCEwAQnCA4QXACAIITBCcITgBAcILgFBMgOAEAwQmCEwQn +ACA4QXCC4AQABCcITkBwAgCCEwQnCE4AQHCC4ATBCQAIThCcgOAEAAQnCE4QnACA4ATBCYITABCc +IDgBwQkACE4QnCA4AQDBCYITBCcAIDhBcAKCEwAEp+AEwQmCEwAQnCA4QXACAIITBCcgOAFAcApO +EJwgOAEAwQmCEwQnACA4QXACghMABKegAMEJghMAEJwgOEFwAgCCEwQnIDgBQHACghMEJwAgOEFw +guAEAAQnCE5AcAKA4AQEJwhOAEBwguAEwQkACE4QnIDgBADBCQhOEJwAgOAEwQmCEwAQnCA4AcEJ +AIITEJwgOAEAwQmCEwQnACA4QXACghMABCcgOEFwAgCCEwQnCE4AQHCC4ATBKTgBQHACghMEJwAg +OEFwguAEAAQnCE4QnIITAAQnIDhBcAIAghMEJwhOAEBwguAEwekvYQAQnIDgBMEJAAhOEJwgOAEA +wQmCEwSnv4gBQHACghMEJwAgOEFwguAEAAQnCE4QnACA4AQEJwhOAEBwguAEwQkACE4QnCA4AQDB +CYJTcILgBAAEJwhOEJwAgOAEwQmCEwAQnCA4BScITgBAcILgBMEJAAhOEJwgOAEAwQmCU3CC4AQA +BCcIThCcAIDgBMEJghMAEJwgOAUnCE4AQHCC4ATBCQAIThCcIDgBAMEJglNwguAEAAQnCE4QnACA +4ATBCYITABCcIDgFJwhOAEBwguAEwQkACE4QnCA4AYCrxPKVN//fJTovFD1gcneuX/MnGzduXCY4 +QXACACxIjzzyyO0rVqz44p49e25yPOY3wQmCEwBgQdm2bdt7h4aGzo2Ojr750KFD1zkmghMEJwAA +ly1mN4eHh78eyzeXL1/+1T179tzsuAhOEJwAAFy2nN3McwbNcgpOEJwAAFy2enYzmeUUnCA4AQC4 +bO3ZTbOcghMEJwAAl61rdtMsp+AEwQkAwGU7fvz4S0qz/Hh0y7Zt237+hhtu+G+7du36yfg6nDhx +4jtKfA45VoITBCcAAJfs9OnT6+LncJbAXOZ4CE4QnAAACE7BKSRAcAIACE4EJwhOAADB6ZgIThCc +AAAITsEJCE4AAMGJ4ATBCQAgOBGcIDgBABCcghMQnAAAghPBCYITAEBwIjhBcAIAIDgFJyA4AQAE +J4ITBCcAgOBEcILgBABAcApOQHACAAhOBCcITgAAwYngBMEJAIDgFJyA4AQAEJwIThCcAACCE8EJ +ghMAAMEpOAHBCQAgOBGcIDgBAAQnghMEJwAAglNwAoITAEBwIjhBcAIACE4EJwhOAAAEp+AEBCcA +gOBEcILgBAAQnAhOEJwAAAhOwQkITgAAwYngBMEJACA4EZwgOAEAEJyCExCcAACCE8EJghMAQHAi +OEFwAgAgOAUnIDgBAAQnghMEJwCA4ERwguAEAEBwCk5AcAIACE4EJwhOAADBieAEwQkAgOAUnCA4 +BScAgOBEcILgBAAQnAhOEJwAAAhOwQmCU3ACAAhOBCcITgAAwYngBMEJAIDgFJwgOP1PBABAcCI4 +QXACAAhOBCcITgAABKfgBMEJAIDgRHCC4AQAEJwIThCcAAAITsEJghMAAMGJ4ATBCQAgOBGcIDgB +ABCcghMEJwAAghPBCYITAEBwIjhBcAIAIDgFJwhOAAAEJ4ITBCcAgOBEcILghA5Hjhw5sH///teV +v3BvnO19nTlzZnnZ18ljx47d79jPT/Eelf8nHZ2r74l+Tpw4MRLfK+XXJu8LIDgRnCA454X4kNxP ++QD7on7PKx9qN8Zj4r9Xauw5htpcjKf8ulD0jh8//uJ+45mp8Ij3IPa3adOm3yz/HZqF4zdrgVKO +z+7ZirCIvIjwPN7xjwAzvZ/YR76Gyfaxdu3a38/viX379r0hvi/iuLa+L2c9Ajdv3vxojCPGMNPf +KwCCU3CC4OSy4qlL+eD6+slCtflw+/orNfatW7f+cte4d+zY8TOz+YG7X3CWD/wfr8Lj9dMdQwZr +HfqzGZwRRfkeXu62c4av3k4ej3Kc7p3pkC3bPd/13h8+fPjB6byWrnFPZx/N+3M+YzOitPm+fMaf +q9HR0R+dze9LwQkITgQnCM55J2dgdu7c+d74ULxq1aq/XCgznE3Q9Mp/P1G9hvEP+rM5o9QVnOUv +3zvj9hIbH4r/xnGc7of+rgBcKDOcVZwtrqNtpmc4m+N8Pr9X41jFPsrvv5hhd+rUqedczrins48M +0+b9WVyHX9wWz63/IaK4TnACghPBCYLzmpNhE/FWf1iND98ZnyF+H0sN8/ft2bjJluXmuY+12H7c +F9vM+8v7+8q8vd5u3BaPifvr4KwDrSsG66WXdSDXry1/3953Pi5uzzF07SNmyuL2hx9++LszMOqQ +a7Z/0bmYOa4YU4whnxehEreXqHl2HZzxdWyja4z18etawhnPa5aE3pmR2X4Py/0v63h/bszx19uP +secxz1DO9yIeF/fl9mLcrXG+rGucObbq/Rh/rfXzR0dHfyT2tXbt2s/UkRjbLUH4hRjDgQMHXlXv +P19Dng8b24z7+4170H3U71mGaeyrK/zKr3N1cE61XLd6v27MWdj2e1ofy/hezLEITkBwIjhBcC6Y +4Mzby4fvP8jZneYctYuW1OaH9H7LcsvzP921PDG2FXFRzx7V99VBU48hxtie4WzGnjNQz66jtFa+ +f8b6vbaUH/4jBvstO66Ds3p9izM+6yWUXTOVVSC8Po9ne/v5vK79l/tuqsZ4vuvYV8Fzvpl1nbiv +fg+r49kew735/H5LRNv35WtsL6mdapy5PLUJvQtdrzVjLt7DdlTl92AzyzyUj83918tf433qM+7F ++bwM13776POe3Vv/w0E9w1l/P4yMjHyg/dw6cCNQ6/er3n5sI2KzPne0dTwFJyA4EZwgOBdWcMYH +2fIh+4PlQ/PbcjawDs56pigekyFXR1N8Hc+L7eT2Igx37tz507nv+v4mBC+aiYoP7c32h7pisjnH +7p82M3YH6tmnHFcGZf3aYt/1/bmNHFtuoxnbRTOcreW0Q/l1vax2quCcaoYzg6g9xthWxno+r15a +nEs/M67imMb+cna1Ds6cLav3kaEXz49zY6v3J0Nncb+ZwnZw5uxghlh7nHUQdrzWB7sisj2LWx3j +xVMF5yTjHmgf9axi1wznZOdwxmPiOLa/L8sYN9czorndfI+b85MXx3uVr6U8/+31sRScgOBEcILg +XHDB2b59kOBswmSoX3DmTGMdk7nEsPrAPtTv4jbtGc72UtO8eEvORtXn4bVDOLebM1itWb+LZjPb +wVlHSHv8scR2kODs9zqnGmPOwDZxu7g945pj7DpXsWt/zczZp+v4ydvrpcl1cPbbfh2cObvZMc7f +z5m71gzn4j7vx3jMdV0cKB+bF42aKjgnGffA+2hHbv38DOuucG7GM5JXs62+L+9tZo3PNe/BzfHY +9rmizX7P13HpHE5AcCI4QXBetcHZdcXYrkjrWrbaFZwZkP3CqA7OfldZzfvr4KzPTex6be19DRKc +XcuB21fMvdzgrJ9XPy7vn4ng7BebeRGdnB29lODM2BskOOt4a48x/xGhfkwGcY6h32zooMGZy10H +2cdkwVmHXzsQczlsfD1ZcOY5nxGnghMQnAhOEJzXZHBWS0o/2L4YUMilrRmUEZLx4T3vz1jNmcr2 +BYcuNTibi8NcqK80mhdo6ffa+gVOxlZ7SW09w1jvo57VikCol8ZWy4Iv+hEque/cVl5MaLLgrM+h +zBnWeoz1ktipgjMDsQ70vFBRv2PQDrcI7LwoT3tJbT3O9nZinIMEZ3vZbc4gZrDFsWuf7xkzph1X +i+077unsY7oznHmxofpHqdTLYQcNzuY84c5tCE5AcCI4QXBelcEZQRJLSPOKthmVzRVNx4M076uv +XtvvwjyTzfwNEpz1jF0tX8sgwdk1trzIUARnxkTXRWwyUPJ80PZYcjv99lUv+50sOJvz+S50ncta +XzRoquDsuqBPBFD9Y0I6LK7+0eDCZBcNmmqcgwRn8w8JR7u204TgxMxs18/SrC7E02/ci6t9nJ9q +H13B2XVBoNx+hmqez9rvokBTBWfXRYPyYkuCExCcCE4QnPPxL48769m/qW6vf6RGXmU2PoxHvIX8 +YB8zZBmcEVh5f31RoIzD9o9NySjt+hEsGbKD/CzQ+tzD+rV0vbbcV9ePT8mZ2+rHtNyYY+76WZO5 +3xx3vMbqR1xsrH5Eycb2/pvzUW/Mx+QFbSYbY/7olq4fm5LnCrb/MaEeX46tllf7zXHkj+fI112/ +f/n89o9ZqX+sSTPOoznO9n2TvNZN7e/Lfj+mpeP7dOJHjMSYciazPe562XS1j5dNto/8Wab1ffW5 +mf1+TE2933hujjOPR/v9qn+sTr+x1z/yxv/TAMGJ4ATBedXI2c56SWwGZ8z8ZWDmVU7rJYY+HAOA +4ERwguCkr5zBbC8PjBnMmFVqnc/Ya19QxzEEAMGJ4ATByWR/+Uws6cxlgvX9zdLDeonhRscNAAQn +ghMEJwAAglNwguAEAEBwIjhBcAIACE4EJwhOAAAEp+AEwQkAgOAUnIITBCcAgOBEcILgBAAQnIJT +cILgBABAcApOwQmCE2DB2LVr13s2b978iVOnTj1nLvY3MjLyga1bt36wfLBd7PjPrH379r22vJcf +Hx0d/dFDhw5ddyXHcubMmeV33XXX2Xivy7iGF9Jx3Llz57vjOJ44cWJN+T4dmseReEcc43J83xDv +99jY2H1l3B87evTo95w9e3bxNLclOAUnCE6Aq1n5wHdn+bD4aMRfinAoIfjsWf3wsmjRuaJXPqy+ +pP5wXY+n3Dc6Ux+8y6+nY3/F8Gwcv/Lh+/XT/bA9iOPHj++O7c/ksQibNm36WP2eR6AdPnz4wfJ6 +bpxubA4NDZ1rjm1YciW/n0usjZQxPNWMZWkVxI+2X+tsvF+X+T36ZIy7RNz2Xq8368FZjsED8Q8x +cUx27NjxM+V77d5B9lseN1Le8yc3btz4WLFk//79P1S+fnrPnj0/PN1/cBCcghMEJ8BVrnxYPFk+ +7J2vgmFC+fC+eq6DM2Y+czwrV678q/Lf6X6AHQ/AiOZ6NnO2grMJnHMl4H5zumPtirdmhmtzHpMM +ur17956aqUBqxvx013u+du3az0znfY9wLeM7H+MrAfNPr3TEdQVnhlzb7t27f+xKz8heqeCM2dTW +PxT0mj+PL5zqHzYEp+AUnCA4AaYdnBEaMcNx7Nix+1etWvWF+PAZS+Zma2lfv+CM22MsW7du/VD5 +/YUSEHdNZwz9AnC2gjP3GaF7uduJZYnxmssx2ZavOZaINtu/cSbHm8cj9lW+flH5+/BoBkiExKDh +UL5XPtce83wNzvK9fqx8j7+4hNFbq9haeq0FZwZi7CsiMf7cHzly5GXxXpb/TrlvwSk4BScIToBp +B2cTaOOzUyX4fj8iog7OCJKYfcslt3UA5YxifJgvofjL8Zjy+LE6QGJpaN738MMPf3dXcMZj4vYD +Bw68qjz/lTGust231zOVsa/6XMxcchpLWk+ePLkpxx7bjtcUs26x/SqwXpLLCNtjjGiMGKlfZwmY +m/L+8vVbcwYytpH7zNcfs3vxwb29RDmXw546dWpDedyP5PZjGWPOZsa2Y6YwZxnj/gjm3F4sAc2x +TjXOmHXM9yNfaxzTnH1szXBOLIGN8eXtcR5hBm++7vZ2cnYzxxzfL3HfZM9pnnc2xhffB7lcuLzO +A83vXxKz3PEeZ7yU23bXSz/rGeBQtv+K/L6I9zRjqh2cEVPxvNhe/ZhYWtred57/WS87jdtjrPVr +ifMZ87W2xxbHszlX+eNdz41zH3Pbcf5jGdNTdXDGcarHkudK5rmTk4273nYzrol/uClh+efxZ6Qd +iPG+hebP+5F8fr6H+VjBKTgFJwhOgGkHZwZa+TD6xVxeFzMf8SE1PtRmxKXyuL8sH2Jvbj7Qn+ta +spiRlCHZ9Zg6OHM5bXw4jqiK57SX1bZnKsv4X5vB3Iz3ov000by43xLS8gH5oYzBfmMs41nRxO7H +6uNQ7zNnVJslsBctUd67d+94iLWfXxnuc99we0ltHYUdx3Jlc4ye6rr/4MGDD0XI9AvOsG7duidy +xjL+UaFrW/fcc8+HIyza90WAxPfEZM/pGl+8tvI+vq5jieeSjnNE87V+RxPqp7vu7wrONWvWjId8 +vj8x3gi0PvtYWkLtTNe2M6wizvos113ammmdELOs8T5GEGZgdnxPjgdnte2l9cxkO/Raz1/W5/aJ +5bLFt5qv13XNSmdQdoz9B+J1C07BKThBcAJcUnB2xeLJkyc3RlBFYMYMTSy/zFnEfEyGWiyDjVm1 +DNRmWezimCnJr+P+mC3NfdbB2Q7MEj+fauKnfkzf4IxZvtx2LhHOpa75vGYM9/YZ4/ktW7Z8uLW0 ++MKBAwcebsJ7PAozNONYhDo4Y39xWzw/j0vOFsZMWMzAxTGo74/XF8c5X295TQ/HY2Kf7eAs43t/ +BFMEXDXOz+fz4jEZOvmYeK35nAiCyYIzZy0jOHOZbYRF7quaKV5bbyf2HReaqp8Tr6v9nOb75ak8 +hzJeZ7z2+D6M58X7H7N98bwmusYfW7b78nhs/KNEPG779u0/10Tvkxmt9f1TncPZxPH4PyTkMW72 +vSf2HTPX7X23l+JmaEXIxphjlrYJvaXl++qX4jg2r/He5rg83Xx/L8n74z2J45T3X0pw1uNu3v9v +NeM+2D5m5fvz5ry/X+Q1M7Nn8s97Pj/3KzgFp+AEwQkw7eDMQGsCaqh9fwRnRFXMEOUsaC65rWYG +xz9sts6jXJxx147LOjhj6Wg901rt58KOHTt+NpfQThacsf9Bz+FsP65aSrqieu3j285Z0uocy4nX +0bW/mPnKeCzH9O56KWwcz1xqmjOaub2uczjbwZkRVI+z32MyJjMM89zMyYIznxvjjvFEFOUS33p2 +MMeYj8+vYxnooM+p9911caR6nDH22FbZ7h+Ux13I2cl2lA1yDmeEcf09Xu87g6mJqHPtCwvV+8vX +Wu7/3hxzLknNx+W46+NQPzeX+dbPmW5w1qHXxOBTrWP26dhXPi+Dc7JzRWO5bi4Hbj9fcApOwQmC +c0Erf/msiH+lBaZWPlxvmangrM/hrOWMYXtWL1QfrgcKzpgF6hecOQvaZ2nkRChWYXjzZMEZwXMp +wVnHZM56Tic4+8Vms/xyfDYqnl/PFLeDsz5O/WKyjtKc9bzc4GyWkGYULcnt5kxk/d5X7+NF45nO +c6YKzmb58FP1xY1SRmNGWS4nniw467irdQVnnBeacZfnRdbjqWcx69CKJb71zGvOjua4mxUDQ12x +2i84y9erBg3OZowT2+k6ZuX77vHY9+rVq/8wj1tGZszsxp+p3G41O/uU4BScghME51Vhy4te8m+X +rFr9/y1b88KvAZNbPLzk6cv9eZlTBWeeS1lfzCZVs46TBmf9o06a2y7U53A25wpOBGj9ITmXtWaE +ZShFuNXbyuCrz8WM2/LCQFMFZ7NscGKMMZOW284lsYMEZ4m28e3k+OI4xdLjZonm061zZS+a4cxY +i+c2+7+uHWJ1FPYb53SCM2fB6vNO41zP2FcESH3l2vrntFaRdFFwNs95uus5HedwThqcoRynv4jX +Vx/PkBfBad/fOn/2koMzLgaU42xvO5fz1q81ltVW7+lF53/WxyF//mf7/nrc7eBs73+q0MuLArWP +Wf4DSPv80dhejr1s57tGRkben0uMu/YrOAWn4ATBuaDdveVFZxftfWtv0Rs/BUxh6Yrb/66EzPNm +MzhDvdy15bpBgjNm9+qrx8by3PyxJxFbce5bbL9eOpvivLm4L8+1bMYycSGX5iq25+slrTnD2Oei +QcP9Zia7LtyTFxUaNDi7tpEXDcqgrH/mZb29josrPeOiQf3GmRcEGiQ4m39EeMZFZfL8yTrKmqvn +nmuf/9iOx3rGtetCPvVM4XSCM/4BIX/0SusCONuqiz1dFE+57PZygjNn/NoX34ntx0qc1qzwxGvN +GI33KWcx+11wqHyfPt6Mc/zYx/nDsa0Mzvb+Y9a4PpeyX+g1x+wvOr6XJ5bQNtt+quu1xfdLfdGg +mAm1pBbBCYJTcILgvCTNz3l80SA/RzI+iHYtkczbWz878hnbrB+XF9epf9/1sybz51DmUsScda0f +3/VzMNtj6jPGZzwvt90+lzU/yLfH2R5f/fxU76PeRtfrrm67s+vrQcY5yGttv5eTzZTn89vLQuvX +0x7DZM9pjy9nFOP29vmV7eNWHdO+70E8dpAxDrrvHG+/+yd7ft7XXtba3naEYD62Pq8yb8vXG7GX +27icY9Z+THsb7WMa+83fd3zPTzoOwSk4QXD6n4jghGs4OAGuFMEpOEFwIjhBcAIITsEpOEFwCk4Q +nACCE8EJghPBCYITEJyCU3CC4ERwguAEEJyCU3CC4BScIDiZFx+i40ecHDt27H7mj/Jn5a7pXkUV +wYngBMEpOEFw+n/HvHLnnTf9l61bl31927ZlX2P+WLp08VP5szkRnAhOEJyCExCcC9Jttz3rb/76 +rxf1Llwof9/0mC+WLl3UE5yCE8EJglNwCk4QnIITwSk4EZwgOBGcIDgRnIITwSk4AcEpOEFwIjgF +p+AUnAhOEJyCU3CC4BScCM6rxaHyq8Tls9tXCRacghMEJ4ITBCcdTpw4sa3Y0vVjNgSn4ORiW7Zs ++cCSJUv+fteuXT9ZIvPbBKfgBMGJ4ATBySTK3x+vKh+Sz734xS9+/6lTp54tOAUnkwfn0NDQuXL8 +e8uXL//qgQMHXnno0KHrBKfgBMGJ4OQfvfa3HQPBSUdwxgfoZcuWfW10dPRtJ06ceJbgnN/Befjw +4T3Hjh27n7m1YcOG3y7BeT7+vKTnPOc5/1uJzu8vwflXglNwguBEcF7Dlux7W2/F7Wt6N73g3t6i +B97RW/T9P888Mbx8xT8cPHjwFT7Qzr2dO3e+u3xIvugDdPng/F/LB+iXl+D8quCcn8G5bt26T61f +v/4J5tby5cu/Uv6MXKj/vIQ1a9Z8+qabbvq/BKfgBMGJ4LwW/fNP9FZs3tl76J+9sve3f/u3vePH +j/eWXv+s3tCqtb1Fq7cwDwxdt+Tc6tWr/5MPtHPv1ltv/XzXB+jywfpvVq5c9jXBaUkt3UtqY0XA +yMjIB06cOPH8N73pTestqRWcIDgRnNforOa3r3lB73d+53fKh+YLvfz1ta99rffyI2MlRHeUIP24 +Y2VJrSW1TWgODw9/c9euXe8pH6JXWVIrOHlmcJaw/D/379//ulOnTt2StzuHU3CC4ERwXuOzmv1+ +/e7v/m7vtrUvKGEa79UTjpvgvKaDc9OmTY/F+5BXrBWcgpOLRVh2XdFZcApOEJwIzmtqVvNHOmc1 ++/0y2yk4r/XgvOOOO/5kbGxstP1BWnAKTgYPUcEpOEFwIjjNak76y2yn4LxGPyjf0e8+wSk4EZyC +ExCcgpPWrObl/MrZzpVmOwUnglNwIjgFJyA4BadZzcuZ1TTbKTgRnIITwSk4AcEpOJm1WU2znYIT +wSk4EZyCExCcgpNZndU02yk4EZyCE8EpOAHBKTjNas7arKbZTsHJxcH5a7+2qPfEE8wnw8OCU3Ai +OEFwCk7BuaBnNc12Ck7esWj79i0f3rz5jj/dtOn2P2P++M7vfN5/2Ldv37DvUcGJ4ATBKTjFxAKe +1TTbKTgBBKfgBMEpOAWnWU2znYITQHAiOEFwCk4W5qym2U7BCSA4BScITgSnWU2znYITQHAiOEFw +Ck4W5qzmQLOdrzHbKTgBwYngBMGJ4DSrabZTcAIITsEJCE7BaVbTbKfgBBCcCE4QnIJTcF5Ts5pm +OwUnIDgFp+AEwYngNKtptlNwAghOwSk4QXAKTrOaZjsFJ4DgRHCC4ERwzvCs5t/93d/13vKWt/SG +hobKt/mi3oYNG3q/8Au/0Ltw4cKsBeEf//Ef9zZu3Ng7f/682U7BCSA4BScgOAXn1Tqr+fKXv7z3 +6le/uvflL3/5ohic7eg02yk4AQSn4AQEp+C8is/V/OIXvzg+s/n1r3/9ots/+clP9l760pdOBOfi +xYsnZiMjSOM5+fUTTzwxHqgxO/rggw/2vvCFL0w8LmI2t/GLv/iL42EbX9f3xe9jX29+85vHt7tr +166J+I3Z13hczr6GSwlhs52CExCcCE4QnAjOOT5Xsx2F9e11VPYLzq985SvjsZmB+NGPfrQzKjM4 +y5+fzuCM7T/22GPjX7/mNa/pve997xv/fWwvvs7HXe4yXLOdghMQnAhOEJwIzjm6Au3lBmf+Pmcf +c5ZzusHZflzMds5GcJrtFJyA4ERwguBEcM7RFWhzSW074gZdUtsvWPstqR00OPPrGEcu142LGeUs +qHM7BScgOB0PwQmCE8G5AH6uZp4/+Y1vfOOioHz88ccnHhPBGV/XV7TNJbVx32c/+9nOmI37vvSl +L40/LvYz3eCM38c5oq5kKzgBBKfgBMGJ4FyAP1czIjKWrebS2LhoT4gIzXMzY6Yx749ZxnpWtJ6F +bF/U513vetf4Y2N28iMf+Ujfiwb1u7hQLKmtl+zGuPKiRK5kKzgBwYngBMHJTPzlckcJjXe/6U1v +Wn8tBudszGoOEqERfl3LbefyV4RovYz2vvvum7Uf13KtzHYKTkBwIjhBcFKJ0Cx/sVyIGa7Vq1f/ +x3gPyl84K6764JzFWc2F8qs9e3r8+PGJpb+z9etqn+0UnIDgRHDCPPX9S5b8/Y4dO84cO3bsfubO +oUOH/lkGZxoeHv7mli1bfmVsbGxfCc6PXW3BeSVmNf26NmY7BScgOBGcME/dVyLn1ltv/fz69euf +YO7ErGYdm7Xly5f/zer1z//DqyY4zWrOq19X42yn4AQEJ4ITLKmlUi+prUNz7969bzxz5sz1V9WS +2tH/sXf7c9b2Pve5z6m9efLrX7zldG/5nc/rLfqhfyc4AQQnghME59UcnHVo5v1X3Tmch/91b9W6 +u3s//o53zsrFcfwa7NfnP//53t1b7u19286x3qLX/3tLagEEJ4ITBOfVGpwlNL/SDs2rNjjD6/9D +CZ1X9Tbf8+IFN9sZV7iNn8F5ub/iZ3eGervxI1Tq22br10/85Lt6t6y/u8T/L7poEIDgRHCC4Lya +RWR2heZVHZxzMNsZ4RYB1y8a477473TDsP1zNS/1V/xYltxObH/x4sW9Bx98cPxHtczWj0a5Wmc1 +BScgOBGcIDi5RFf7z+GcrdnO17zmNX3jLUIv7vvsZz87UBi+733vm9jGbARnzJjmrGlEcMTnl770 +JbOaghMQnAhOEJwIzvk42xkx9+pXv7q3YcOGi7YXs4nxsy/L9/ozgjNisl4uG499y1ve0nvzm988 +/ticGc1Q7LcENrbRbwY1n1MHZ/tXBOcf/dEfmdUUnIDgRHCC4ERwzsfZzoi5mJl86Utf2nv88ccn +bo+A/NVf/dXx+zM4IxAzQnft2jX+3wjBT37yk+O3R7TGbV/4whfGgzFuy8fVM6URmLHdfHzcl/uO ++2Is8by4v95PHaMx5nje+fPnzWoKTkBwIjhBcCI45+NsZwbnb/3Wb03MJEb0RSx+4xvfuCg4Y/lt +PDaCL4My7+taUhtBGDOH8etd73rXxP0f/ehHL5q1fOKJJ8a3FfHYvi/22Q7OmNk8fvz4eNhe7qzm +XdfQrKbg/EcnTpwY2bx586P79u17ffmwuni291e+V3fH/sbGxkbL/ob8/3rOw+SOcvw/Fu/32bNn +FzsmghPBCYITwTmHs50ZnBGXeU5kzGzG8tiIvDo44/c545gy+qY6hzPuj6W7uc36sRmREZzt7XQt +qc3HmtVc+ME5MjLygRIDn0g7d+58bwm0F89mmO3fv/+1ZfvnN23a9Jvt4MzxzGSM5v7KNt8w04Fb +tvnaMt6Pl4hePRvHasuWLe/funXrBw8dOnTdTGzv8OHDD0R8l/8+WMff6Ojo6Xgd7SjP2+P1Xer3 +RPwDQ3nu0xs3bnysHK/h9v1ln7ubf4B4w1wHaR6P+s9Aec0/WmLq5rnYf3lvfyne367jIjgRnCA4 +EZwzMttZx18so/2pn/qp8dnGDMl2cNbLbqdz0aC4P2cqYz/1Y/PqsxmcMYbJgvNyLkZ0Lc9qzrfg +bELgfPxs3bbZnA3sF5xHjhx5Wfn6XI7hUiIug7U8/7q5CM6YuYufT1yO17bLOV7lw/udsa2InTq6 +yq+nmuOxZCbGe+DAgVcMDQ2di/irIzb3c88993y46/bL2f9UwRnRnmPK+yPEmn94mNUQK9+DZ8u+ +O/8MlOOwaiZnee+6666z8f62ju+3mv0tuGgTnIITBCeCc4HMdtbBGedoxjLY+LEjGXR1cEb85bme +uaw2f+VS2Hhs/qiVfsEZS2hjP4899tj442Kb73znOycuMBTxGfuIZb55zmj7HE6zmldXcJZgekn5 ++kUx2xMB1TX7ONvBGbEY+27C8HyJo1dNN+IibJoP8MNzEZwRinHcLjfO6yirg+TUqVMbyvfJxpmK +/wifdkTmvqvYWpL7jtvbEToXwVl+PdmMZelcBOf27dt/7vjx4/fGP3qUr8ePRfn99pk67mXbI2W7 +T8VrLJbM1vsrOBGcIDgFp9nOzpnJCLt8XMw+1rOYce5lfa5khmUuqc3nxXmfeb7ll7/85fF4jefm +/XFhoXpWM6IzH/+Rj3zkonHGY2MfedXb9vLb6c5wmtVcEDOc40GxZ8+et7ZjMM6B3Lp16y/nktsy +7k35ATk+oMeSy/iwXsLuZPy+/ZgzZ84sj+3mfaOjo29r7yMeE1FS9vOhMq7NMdO5du3az9QzlbmN +8iH9OdWs4KOxJDG2k7ON8Xpi2zlbmMFZPmu9vfz+dbGNHTt2/Eyzn6F6hjVfZ2yznuVtXuej8Tp3 +7dr1nlzmWvbx1mbJ6fi2Ygz18sx8XLy+GEc8tr39iI5169Z9KsceURJLXssfnaHyOsa3Vwdfua/v +OHOJaP1+tF/rqlWrPhf7Ko+5O27L4MvgzNtzNrRs5+GcdY1gzfehWn56U46tWoJ718jIyPtjaXSz +RHsiOOP15nGKr9vBWc86xm31rGAsv81Z7Pbrqvcd71H8Pv6RIcaXxyWPV76e3Fd5TT+c+yjfd4+X +2y7UwRl/VnKbse/4x5B6Jjq2Uy+NLfu4rzlv9Q0xxvL+PlG/plzSXJ73G7G9jND4Ol5vvH9x/Lr2 +FcevjOVMjCX2efTo0e+5EufGCk7BCYITwXmV/tzOhfbLrObCCM4ItPJB+w8yeprZwKGIlq5lt/Gh +frL7V65c+VcZi3VM1ergzOW0OatZoujz8Zx6WW21fPUl8Zhm/BlKw61Zutz+dRmcfcY4HgglXt7T +tbRy79694+cVtqMs99leUtseQ84Y9nn+eNw1M6RPt/Z7Kvbbno0s4zzTZ5ynJhln+7WObyNDMqMr +tlEHZpw/GreX1/YdGcbVeNpLsFfWAZe3R1w179d4cJbXuqK8t38Rx6z8Pf7yeswZnNXs5sQ26jDt +2Pf4+CZbHtvxnHX1c+L4xPdLGVsE1IU1a9Z8JsaaEZ+znl3j6pqRLcfwh+I5+fpjdrN+bgZue0lt +9fVFyrE6GMcqZ0rb95f9HZup83wFp+AEBKfgvMZ/budC+WVWc+Gewzk6Ovr2DMEMupiRiTDauXPn +T8dzSrT8bDymDs54TAnHA/l1RGmzXPBcc07cQ8eOHbu/a9luLqfNwMyZ1noZ7FTBWX8dj8mlinVw +xhiaMZ5rjfHpvD9eZzkGP1LFy0TsRJjEjFK8jpzRrIMzxhDPj9mweH7MqkYoRFDHNmO2L+7PCI/I +K1/fVP5OOxrbj1ndGHvM3jbHfyI4c4lrfH3w4MGHYlvNOM91hW08Jl5rxlLOXMaMdb1UNvYRv49Z +tfbtTaiOx24GaD42jkH+w0AETx2vGVrxWjOo47aYsYv7Mza7ltQ27+NTuaw138eMunhuvPbmGJ+L +5bAx3tx3xGK8R3l/PGf37t0/9vDDD393xlrOXnZFah2bdUxGJMZ+62W3GbuTBWd5L1c0s8VPr169 ++g/je6W8l89untcZnOUY/0ocv/j+ao75r8SxiXNb4zWV1/Pj8R403zdPN+/T0rn8/4fgFJwgOBGc +ZjvNagrOgYMz4yCWfrbuP5ezhRELOQuasZjBmTOiTRg+mmHYxMa5Oi7b53BGXPXbTz1TOlVwNh/Y +BzqHs95WvIY6DqvQeLodnDmT2DHruq1efhvjKiH0s/nYOK4RB7kUNmfScnv9zuGsgzPHEPHU78JC +XeOMZbnNTOX4GJvly08173vc9nR7RrWJ06fjNeR4queszH2395cBVx+PjnNEO7cx2YxhHaEZrvE9 +Ektf20txMybzOfV220to63M4Y6ltBmnen9uICK3Pvaz3FUufJwvOeF6/czgnmeFc1ixnv+h5eX8e +gwz4K3HhIcEpOEFwIjjNdprVFJzTmeF8xnK8enaynq0KOQM3aHDW52O2gzNCrN/Vcuvluxl3MXs1 +VXDWM1RTBWez/4zi8THWs4nTCc6u2KwfF2OvZ0Dbwdkcp+GumOy6wmxrnAMFZ7PM+YkYT8xW1vfl +LGZEbWynnolsRepQPes5SHDm+Zix3fpCRJMFZ4ZpvZw3Iq/+XszZwssJzvy6tXx2ab3fvGptBHsu +C24HZz6mX3DGDOdMBGcssc3Z8nAlLjwkOAUnCE4Ep9lOs5qC87KCMzRLJnvlv3+Zs48hL9YyVXDW +s5cRU/V5ohmcubw0l7OmXGabS3zz6+q59XmhFwVn7CsuKlNfNKhfcDZjfDrPday3neE4SHDW26mP +VRzbjJb2Mcjt1eEYcZEXiqmDs2ucObuVM5GDBmdc7Kh1PuR4BGXUVjORa/M59fmjMcb6deTjpgrO ++hzOEnd7ItYmC86YWYz3MV5bPi9fe/W9eNdMBWfzPX/ROab5db7unJ2ulxvneNvvS4Zi8/4+2fH+ +Tis4d+7c+e58f+pZzvizM9cXDhKcghMEJ4LTbKdZTcF52cEZMyflA/YXOi668pJBgrOe9WudIzo+ +wxlXnI376qWzqTnXcGIGs/nQfq6+KFAVPeNBEed+5of99kWDJjsftD3GfH45Rjd3LR3tCs6ui/+0 +ZyfztmYm80K9vZx1nOyiQXGF2vaFc5qQ6zvOruCsl7nWQVaHb3u2NWb28pzC1gVtXt6+6mu/4KzO +0cxzINfG+9MOzrjibB1WeXXbvMJuvf/2+ZiXG5x5vmUuo+3ab3y/5jmxzfty0YWFcoa4XkLbXP32 +/IAXDeoMzngPus47rccvOBGcIDgFJ1fNbKdZzYUdnBkDEZVTPS4+dLeW1N7YfOi8s15iWz+2XuLX +flzsN34fH6Dz9/3GV2+r/fiun2OY+5/uGOv9tcfTtY2ufeXza7mPehv9Xnf79ba/HnCcI7HMdNDX +mktSp7q92scd/cbVPh71e1a/T/Xj8v72/rq2Ndn3Ytfj2/vNsVffGxd93e959fb7LV9tbytisf2a +4rbYRszsDvJ1v2NTvweTvVeCE8EJglNwsiBnO81qXj3BCSA4BScgOAWn2U6zmoITQHAiOEFwCk4x +cXXOdprVFJwAglNwguBEcJrtNKspOAEEJ4ITBKfgZH7PdprVFJzXmlOnTm06duzY/cwvJ06c2DLX +F8VBcApOEJyCU3Ca7TSrKTiZUbfe+m1f2rp12de3bVv2NeaPoaFF5a//RUt9jwpOBCcITsEpJhb4 +bKdZTcF5Lbvttmf9zV//9aLyZ6z8fdNjvli6VHAKTgQnCE7BKTgX/GynWU3BKTgFp+BEcApOEJyC +U3Ca7ZzR2U6zmoITwSk4EZyCExCcgpMZn+00qyk4EZyCE8EpOAHBKTiZ0dlOs5qCE8EpOBGcghMQ +nIKTGZ/tNKspOBGcghPBKTgBwSk4mdHZTrOagpN3LBobG/u+w4cPP9j1cx0Fp+DkYg888MA/P378 ++HedPXt2seAUnCA4EZz0ne00qyk4+Ufl749XlQ/J59avX/+p8kF6pA5PwSk4udiWLVs+MDQ0dO7u +u+8+e+rUqdX550VwCk4QnAhOs53js50/+Lo39Na9YHPv+hfu7S166Kd7i77/56+8MjbBKTivdHAW +ETG9kZGR95cP0s8WnIKT/sEZf1aGh4e/uWvXrp8ssfltglNwguBEcDI+2zn8/O29Rbdt7C1avWX+ +uPPuXn7YX9BWrb3kYzB03ZJzq1ev/k/r169/4kratGnTb+7evfsd8TlkITt06NBDx44du38QO3fu +fHd5/87X7+WyZcu+Njo6+rZv//Yb/h/BOT+D8/Dhw3sGfY+ZORs2bPjtEpwX/XlZvnz5V/fs2fMv +SnD+teAUnCA4EZwwW171kUue5R1evuIfDh48+Ior/WEyzmUsn0EeWejBuXnz5k8MGtm33nrr58uH +5Avtf0B44Qtf+OurVl3/3wXn/AzOdevWfepK/wPNtajE5Vfaf15ipvOee+750Ojo6OlDhw5d5zOQ +4ATBieAES2rpWFJbPkz/za5du95jSa0ltUy9pHbFihX/dc+ePW8qf15ucWwEJwhOBCcITvoE5x13 +3PEnBw4cOH769Okb6vsEp+DkmcH5ghe84HcOll/tK9UiOEFwIjhBcNJy5syZ6/vdJzgFJxc7ffr0 +CsdBcILgRHCC4GQGCE7BCYITEJyCEwQnglNwAoITBKfgBASn4ERwguAEwYngBMEpOAWn4ATBCYJT +cApOEJwITsEJCE4QnIITEJwLKDh/7dcW9Z54gvlkeFhwguAEwSk4BScIzgXu/vvv/fnNm+/4002b +bv8z5o9777370X379g37HgXBCYJTcIoJEJwACE4QnAhOEJwACE5AcApOEJwAIDhBcF7Twfn8Xb1F +248BU7ju+uVPCk4ABCcITgZ07Nix++P/ZcDU9uzZ89bTp0/f4P8dAAhOEJwAACA4QXACAACCEwQn +AAAIThCcAAAgOEFwAgAAghMEJwAACE4QnAAAIDhBcAIAgOAUnCA4AQBAcILgBAAAwQmCEwAABKfg +BMEJAACCEwQnAAAIThCcAAAgOAUnCE4AABCcIDgBAEBwguAEAADBKThBcAIAgOAEwQkAAIITBCcA +AAhOIQGCEwAABCcITgAAEJwgOAEAQHCKCRCcAAAgOEFwAgCA4ATBCQAAghMQnAAAIDhBcAIAgOAE +wQkAAIITEJwAACA4QXACAIDgBMEJAACCExCcAAAgOEFwAgCA4ATBCQAAghMQnAAAIDhBcAIAgOAE +wQkAAIITEJwAACA4QXACAIDgBMEJAACCExCcAAAgOEFwAgCA4ATBCQAAghMQnAAAIDhBcAIAgOAE +wQkAAIITEJwAACA4QXACAIDgBMEJAACCExCcAAAgOEFwAgCA4ATBCQAAghMQnAAAIDhBcAIAgOAE +wQkAAIITEJwAACA4QXACAIDgBMEJAACCEwSn4AQAAMEJghMAAAQnCE4AABCcIDgFJwAACE4QnAAA +IDhBcAIAgOAEwel/IgAAIDhBcAIAgOAEwQkAAIITBKf/kQAAgOAEwQkAAIITBCcAAAhOEJwAAIDg +BMEJAACCEwQnAAAIThCcAACA4ATBCQAAghMEJwAACE4QnAAAgOAEwQkAAIITBCcAAAhOEJwAAIDg +BMEJAACCEwQnAAAIThCcAACA4ATBCQAAghMEJwAACE4QnAAAgOAEwQkAAIITBCcAAAhOEJwAAIDg +BMEJAACCEwQnAAAIThCcAAAgOAUnCE4AABCcIDgBAEBwguAEAADBKThBcAIAgOAEwQkAAIITBCcA +AAhOwQmCEwAAZjE47y8fpB8BpvSd1133pOAEAIABg/P06dMrxqMTGEj8mfE/EQAAGCA4AQAAQHAC +AAAw7/3/EbbkHOGJ/IwAAAAASUVORK5CYIJ= + +------=_NextPart_01CEC02D.7438E4B0 +Content-Location: file:///C:/F83B2E2E/PEND_AUTHORIZATION_files/oledata.mso +Content-Transfer-Encoding: base64 +Content-Type: application/x-mso + +0M8R4KGxGuEAAAAAAAAAAAAAAAAAAAAAPgADAP7/CQAGAAAAAAAAAAAAAAABAAAAAQAAAAAAAAAA +EAAA/v///wAAAAD+////AAAAAAAAAAD///////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////9 +/////v///wMAAAAEAAAABQAAAAYAAAAHAAAACAAAAAkAAAAKAAAACwAAAAwAAAANAAAADgAAAA8A +AAAQAAAAEQAAABIAAAATAAAAFAAAABUAAAAWAAAAFwAAABgAAAAZAAAAGgAAABsAAAAcAAAAHQAA +AB4AAAAfAAAAIAAAACEAAAAiAAAAIwAAACQAAAAlAAAAJgAAACcAAAAoAAAAKQAAACoAAAArAAAA +LAAAAC0AAAAuAAAALwAAADAAAAAxAAAAMgAAADMAAAA0AAAA/v////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////1IA +bwBvAHQAIABFAG4AdAByAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAWAAUA//////////8BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIBeqCBowM4B +/v///wAAAAAAAAAAXwAxADQANAAyADMAMAA1ADYAMgAyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAABgAAgH///////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAACAAAAsGQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///////////////wAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////// +////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALoA +AHic7HwJXBNHG/fMbk6uXCScmgCKqCiBiFfVhMNWrAoq8WirEJRLERChl1rirVUKeLTSWsGKVm29 +arGXFbyq79sq2NO2VlA8EGxJEFFE2G9mJ6mRakuVr5/9fu/qn2eunZ199pnnmJ1NRbmkatNe9/Og +3TEM0KCNEQKeTRm0gD3EAFCWfBvDMNZi5n/Hv+poRehmeYaTEeUi4GfORxAgCBHsEOwRHBAcEZwQ +REQEgARBiiBDcEaQIygQXBBcEdwQ3BE8EDwRuiB0RVAiqBC8ELwRfCxjeAZRX4QeCH4IPRF6IfRG +8Efog9AXIQBBjRCIEISgQeiHEIzQH2EAwkCEQQiDEZ5AGIIwlJVtALQIOoQQhFCEMIRwhOEITyI8 +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 +AAAA + +------=_NextPart_01CEC02D.7438E4B0 +Content-Location: file:///C:/F83B2E2E/PEND_AUTHORIZATION_files/filelist.xml +Content-Transfer-Encoding: quoted-printable +Content-Type: text/xml; charset="utf-8" + + + + + + + + + + +------=_NextPart_01CEC02D.7438E4B0-- diff --git a/network/trans/WFPSampler/docs/PEND_ENDPOINT_CLOSURE.mht b/network/trans/WFPSampler/docs/PEND_ENDPOINT_CLOSURE.mht new file mode 100644 index 000000000..3f4627eb6 --- /dev/null +++ b/network/trans/WFPSampler/docs/PEND_ENDPOINT_CLOSURE.mht @@ -0,0 +1,2638 @@ +MIME-Version: 1.0 +Content-Type: multipart/related; boundary="----=_NextPart_01CEC02D.655CE090" + +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_01CEC02D.655CE090 +Content-Location: file:///C:/5D1ADE85/PEND_ENDPOINT_CLOSURE.htm +Content-Transfer-Encoding: quoted-printable +Content-Type: text/html; charset="windows-1252" + + + + + + + + + + + + +Pend Endpoint Closure + + + + + + + + + + +
+ +
+ +

PEND ENDPOIN= +T CLOSURE

+ +
+ +

Overview

+ +

The Pend Endpoint Closure scenario will cause the +classification to pend for a specified period of time.  In a real world scenario, this time cou= +ld be +used to finish injecting outstanding NBLs.

+ +

All filters added sit in WFPSampl= +er’s +sublayer (which is weighted just below IPsec’s sublayer), unless otherwise +specified using the –sl +<SUBLAYER> command line option.  +All filters are associated with WFPSampler’s +provider.

+ +

The following diagram shows how the code flows for thi= +s callout:

+ +

= +
+Figure A. Code flow for Pend Endpoint Closure Scenario

+ +

When traffic matches a filter at the specified layer, = +ClassifyPendEndpointClosure() is invoked by the Filtering +Engine.  This function will create = +the +PEND_DATA and call FwpsPendClassif= +y().  If the cl= +assify +has an appropriate flow associated with it (see FLOW_ASOCIATION.mht), then = +the classify will exit with the expectation that the <= +span +class=3DSpellE>FlowDeleteFn will be invoked when the flow goes away.= +

+ +

Otherwise, TriggerPendEndpointClosureOutOfBand() is invoked.  Based on the queuing method, the approp= +riate queueFn is invoked.  +The only time a DPC can be used is if there is no delay.  This is due to the fact that the delay +introduced is done by using a function only available at PASSIVE_LEVEL.  Introducing delays at DISPATCH_LEVEL is +rarely a good idea.

+ +

Regardless of which queueFn is +used, each will call the PerformPendEndpointClosure= +().

+ +

 PerformPendEndpointC= +losure() will cause the thread to sleep = +for +the duration provided.  When it wak= +es up, +the classify is completed using FwpsCompleteClassify, +and the PEND_DATA is destroyed.

+ +

Note that with long delays, it is possible to bugcheck the machine.  +This normally happens when a machine requests to drop into a power +managed state.

+ +

Applicable Layers

+ +

v  +FWPM_LAYER_ALE_ENDPOINT_CLOSURE_V4

+ +

v  +FWPM_LAYER_ALE_ENDPOINT_CLOSURE_V6

+ +

Command Line Usage

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

Option

+
+

Argument

+
+

Meaning

+
+

-s

+
+

PEND_ENDPOINT_CLOSURE

+
+

Implement the PEND_ENDPOINT_CLOSURE scenario

+
+

-l

+
+

Applicable Layer

+
+

Layer at which this filter will apply

+
+

-pcd

+
+

Integer

+
+

How long of a pend completion delay to introduce (in ms)

+
+

-sl

+
+

Applicable subLayer

+
+

SubLayer to associate with the filter= +.  [default is + WFPSAMPLER_SUBLAYER].

+
+

-v

+
+

 

+
+

Make the objects associated with this scenario’s instance dynamic= +

+
+

-b

+
+

 

+
+

Make the objects associated with this scenario’s instance availab= +le + during boot-time

+
+

-tdpc

+
+

 

+
+

Use threaded DPCs for asynchronous (out of band) queuing method +

+

-wi

+
+

 

+
+

Use work items for asynchronous (out of band) queuing method

+
+

-r

+
+

 

+
+

Remove objects associated with this scenario instance

+
+

-?

+
+

 

+
+

Display help

+
+ +

 

+ +

WFPSampler.E= +xe -s +PEND_ENDPOINT_CLOSURE -? +provides help output

+ +

WFPSampler.E= +xe -s +PEND_ENDPOINT_CLOSURE -l FWPM_LAYER_ALE_ENDPOINT_CLOSURE_V4 -v  adds a dyna= +mic +filter (-v) at +FWPM_LAYER_ALE_ENDPOINT_CLOSURE_V4 (-l) +which references the appropriate callout.  +This filter will have no conditions, meaning it will act on all clos= +ures +seen at this layer.

+ +

WFPSampler.E= +xe -s +PEND_ENDPOINT_CLOSURE -l FWPM_LAYER_ALE_ENDPOINT_CLOSURE_V4  -v -r  removes (-r) +the dynamic filter (-v) at +FWPM_LAYER_ALE_ENDPOINT_CLOSURE_V4 (-l) +which references the appropriate callout.

+ +

WFPSampler.E= +xe -s +PEND_ENDPOINT_CLOSURE -l FWPM_LAYER_ALE_ENDPOINT_CLOSURE_V4 -ipla 1.0.0.1 -ipra 1.0.0.= +254 -pcd 5000“ adds a persi= +stent +filter at FWPM_LAYER_ALE_ENDPOINT_CLOSURE_V4 (-l) which references the appropriate callout.  This filter will have 2 conditions; FWP= +M_CONDITION_IP_LOCAL_ADDRESS +(-ipla= +) +equals 1.0.0.1, and FWPM_CONDITION_IP_REMOTE_ADDRESS (-ipra) equals 1.0.0.254. The closur= +e will +be delayed (-= +pcd) +for 5 seconds.

+ +

WFPSampler.E= +xe -s +PEND_ENDPOINT_CLOSURE -l FWPM_LAYER_ALE_ENDPOINT_CLOSURE_V4  -aaid C:\Tr= +affic.exe +-ipla 1.0.0.1 -ipra +1.0.0.254 -ipp TCP -pcd +5000“ adds a persistent filter at FWPM_LAYER_ALE_ENDPOINT_CLOSURE_V4  (-l) +which references the appropriate callout.  +This filter will have 4 conditions; FWPM_CONDITION_ALE_APP_ID (-aaid= +) equals +C:\Traffic.exe, FWPM_CONDITION_IP_LOCAL_ADDRESS (-ipla) equals 1.0.0.1, +FWPM_CONDITION_IP_REMOTE_ADDRESS (= +-ipra) equals 1.0.0.254, and FWPM_CONDITION_IP_PROTOCOL  (-ipp)= + equals +TCP.  The classification will be de= +layed +(-pcd<= +/b>) for +5 seconds.

+ +

For a list of conditions applicable to each layer, ref= +er to Filtering +Conditions Available at Each Filtering Layer.

+ +

For a list of command line parameters for configuring = +each +condition, refer to Co= +nditions +for Command Line.

+ +

Notes

+ +

FlowDelete

+ +

Pending at FWPM_LAYER_ALE_ENDPOINT_CLOSURE_V{4/6} can prevent flows from going away, which causes= + the FlowDeleteFn not to get invoked.  To work around this, you can call FwpsFlowRemoveContext on the context of the flow you = +need to  have the <= +span +class=3DSpellE>FlowDeleteFn invoked.  +For a clear usage, look at ClassifyFunctions_Ba= +sicStreamInjectionCallouts, +and notice that if a flow was associated with both the STREAM and +ALE_ENDPOINT_CLOSURE layers, a check is performed after the injection to +determine if the flow is being terminated.  +If it is, FwpsFlowRemoveContext is calle= +d on +the flowContext for the ALE_ENDPOINT_CLOSURE, w= +hich +will cause invocation of NotifyFlo= +wDeleteNotification(), which will complete the pended classify.

+ +

Mixing Scenar= +ios

+ +

PEND_ENDPOINT_CLOSURE is most useful when mixed with o= +ther +scenarios.  For example:

+ +

                WFPSampler.exe +-s BASIC_STREAM_INJECTION –l FWPM_LAYER_STREAM_V4 -ipr= +a +1.0.0.254 -iprp 6000 -v

+ +

                WFPSampler.exe +-s PEND_ENDPOINT_CLOSURE -l FWPM_LAYER_ALE_ENDPOINT_CLOSURE_V4 -aaid C:\Traffic.exe -ipra +1.0.0.254 -ipp TCP -iprp +6000 -pcd 5000 –v

+ +

                WFPSampler.exe +-s FLOW_ASSOCIATION –l FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4 -aaid +C:\Traffic.exe -ipra 1.0.0.254 -ippTCP +-iprp 6000 -aws +BASIC_STREAM_INJECTION -awl FWPM_LAYER_STREAM_V4 +FWPM_LAYER_ALE_ENDPOINT_CLOSURE_V4 –v

+ +

This will cause the endpoint associated with the speci= +fied +flow to remain open for the duration of the injection being performed at +STREAM.  Once t= +he +everything has been injected, the endpoint is allowed to close, and = +the +flow contexts are cleaned up.

+ +
+ + + + + +------=_NextPart_01CEC02D.655CE090 +Content-Location: file:///C:/5D1ADE85/PEND_ENDPOINT_CLOSURE_files/themedata.thmx +Content-Transfer-Encoding: base64 +Content-Type: application/vnd.ms-officetheme + +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 +cmVsc1BLBQYAAAAABQAFAF0BAACtCgAAAAA= + +------=_NextPart_01CEC02D.655CE090 +Content-Location: file:///C:/5D1ADE85/PEND_ENDPOINT_CLOSURE_files/colorschememapping.xml +Content-Transfer-Encoding: quoted-printable +Content-Type: text/xml + + + +------=_NextPart_01CEC02D.655CE090 +Content-Location: file:///C:/5D1ADE85/PEND_ENDPOINT_CLOSURE_files/image001.emz +Content-Transfer-Encoding: base64 +Content-Type: image/x-emz + +H4sIAAAAAAAEC9VbC3RV1Zne596EhMjjriSERAKcYAwRLnpNAGOV5ECQJUPAyMLHculwoUDUSSEL +0xbHRw+WtrRazVLqA5x6W20XBVoyg1bqKFyfdZTAZdShTF02wgw6ik6qSKGI6ff9++ybkzuJxnBl +ZXby3W/vfz/O3v/+9/OeaymlGgELmAP8OqjUbQF4PNe4SKnyWqXsS+bOYqr4NKWuQ+IMk8DjVgjc +LKVmIm6QLz+jY3/OUE3vZCkUoMKADaC4iZZjqWL4Q0AgFP8jsqqoB6a9HrgWYNqIE5B0+rluzVgn +Qw1BHN0YJzPpH+0olQ9ZNsBq3IqPHEfFzXOU6uzkc4LqNrVLfWaVeGkXg1ke40o9oEVorXFdfpY7 +HigEmJ6uU5M6G2zKMX7Uxb0S8ggA9eLJSu0BbkFBmeCISmRFVOv4jiEE/YkskxcqdY0fSV1f3aYj +7Dk36WfdqLt1wFqAust2VMCBfzJAPYPUbn7A5ex7tJp5/KDcpIO3M9uxgsxb7slZZ+WwLbcE9ghu +E30yrCzLWt3Z2TkCfRtCsgyArtQDmtCrThciTRXAupDpDFPT8lwt7vZp9MN8xo+0SZ2zj24B9gK0 +bZaTqm+GTd7+6Jy6Yb+zDtT/Fjw0BlD/Exzd/kLIlYrPEEr6bedo+d4Z2U5gkF/HavOEaiZh3elQ +J1Wrpqs6NVvNUPPxqdRUJ+ByvFXpRGLX9I6StI1qkboJfzeoZepmVa+WquVqibpEPpvUCsiXq2aU +2Qj/TeqbaiVSqKsbpqkCu1qF86uFR1YLuxN/qMNTN+uw0+aFD3h8UthJ5NYwX/x75wi7qkZYHbpC +h19t1PFtrg47dwk72x4QVmWPav7pb4Sjf9wmbM/bKexue0nY+WyvcOyifZq3HdDyqe8Lq18dFY5W +f6bDCs9LOr8/F1IzXtk/9NMVAPRTNh9ogmLZtx2wbVDSPU0hXBYsvRYab4ReF0OXN2jxl/98o4xd +rTpeydG96pWwc+SikmPfkYBuyZxSSQd7crwkmrz8j01bKultpctTJr0Xv/F1XX70Vc3ZXiEhU5iX +7t1ocBBFUa8+tC2i6MgSKT8EPx1ldLHDZ0m9drymy00+H3EhwKSzB2VKuea55CXfkmk7OYap2gVA +B8B46vRV4BZAK+cP0694Y990xq0CWHYd4HfoLnFrIKSt92NMyLMzkJ/PYaUJhjmPUDbYx6l+hgmm +JzMv8/jDRg5xsu1DlXJnIVwFhAHOIxOdLvvMcbrWtHiTkrE/xdpq7cJoL0d62i51VOoBxfU6745H +mkIvPQjGHhcy8yHLMf5sFHQlwhFgCiK2AhY6agRAfdTndZxZn1dfErIJ+jvONHlhGK7xI+mAWsum +WCMCVoDYalGPDKdjLeN6XwXQGWbPUFc9OaMf5jN+pE3qvBL6/jUQhL5HJnXeXd/Uv8nbH52X49m0 +H9aBNvgynvc08EVr2fr9d9beuWVHLdeldUi/FoBLrktnIbBA5sYG1YDVZmUfV6XLsDo1q8tg2TMw +frmKKfvHF6s/PFytLt01TXjPuGrhp+qE3X+/W4cvfVKHp2GdQnp3tqqRdJ0FwvGbw8LOwlodnnmN +5vwVwvbJO4TbV9wtHDvwkOZrfiGsHtwkHM/5rbDd9Ixw+1MvCzvH2jQ37dfyj94Wji39QFidOCIc +3d6p43OzHNbP2TVEuOP2kHDsiZGaR4wSrg+VaF5QJtw6NCwMQzmFdc7YDMxK5l0b/VcE3AWUQ/hT +MP7h9LzLuA0A+7W3eXcd4tLU58n5NwNlYh5KYhD8nEMpI6f6jYz56Gd6pvWH/fIzfOnoz/TCpnwE +k2PTzNMtkK0BUufpdOy9S1FuIcA9OftGOTw/HLDOtQ5YnKNOZa/NMV7FMuEM92d+ugr5Wc82oB02 +EQH+C2B9O4bYY9pz7THGvtIxJ9HeHID65vkkDL8NUEfsV0ZOsv6EefyANck6BPyJulqd71huCNGS +BnNTOfyc61DV/q2VyEdn2sZy8gBTppFne3M46zkJiVAbWT8PgVmXRG7H6Po8e0wiNwp0jDb5qKue +ykOWPq+fRjfUVY5v31DfqufmtsBBnIXTsG9orUe1uuvCtMO0P4L4NhjFQfBDML7ZaH8m/IlwZFgi +3JLfciFBf2SYydsfe+G6tQ5YC2gbUQEH/skAbQSUljNwW2C29VCQOIh2EWhRGs7AUE1yPFaxsuL6 +v294FTp/G2VsQMFzkjrvrm/q/1R0Xo7yafesO/W/ALgUMGOUumca6p99zjG6wcoMXG8dtDZYz1q7 +hTOT+y7/WOUm3pSP6vdrrKIIPrXPYzWCtNfjYc8Cu6kz6JD1bi2qt92CyJjWolBxezG53vbrLQ9p +Usc/RH0er1Ekpu7SYbcDYW24Em2JAMugw51AAsjqoy6NXpG9T/ozNmJs8BgyHgaoywmOtptChFXK +Pcy+g9tqZ5f+T222E+x+D3PhMLmHkSz4wFzUwz1M0G2BfA0Al9zvck9xOfauSwHetjQoG3/qVz+p +VntyqtUdLwq7b70n7NhZNZS7u4s0/6VEOP6LycLO4kuEVeNczae01zN6Qjd0u9/42AwSyOla0Fd0 +6brfiNx3jwz9ewsbx3r3GVL+/cNzjnOMIyA7WGeMd2+Rcr/hVUfNnfNht/ymXFNOoXe/EVAV2KIp +lbvlyFhyxdCSheTWx++TepjwiUMfSHld5TBVcp+QzHf/cC1/cv67kp76ozPPNeUlztD3G6ZVWy54 +Z2xv9xu0mzVo2GvAE8DbAP7h9D6bcdwv0J7qRN71QZ3RRSHqwdYkD23WgOOCfu6B6aczY8zsZVsh +ewzgmJno6LkMQdWX8TPVCbpNSLuEGXxjgc+bi5HQjFv9FYyN3Vmtnllf7Ta+rHn3+8JOwqoR+e35 +mqvHC2P4i11IqT5/LgRmruW4pp+uAKCfMtM+9tUCoAm4A/o8CGwG/gzgH07rm3H/C7DOdSLv+tDa +7uy8GiJ/e3hOoF4zPKafzjzb6NaFrBnor24d5J0MwCXnGda9RsUPX/i5msIYQLovqyvq6zd4AHX1 +L0CqrhhHXeG/V13VI850Xqo+qhAXBlL1kePbp+IrG2lrQ2C7tQzf7pQjvWlHKfwE7GOaUIqffT4e +KARYRzpvG5DsG8pNvbJR0JUIR4AGZN6OSO5Xd4EzIWsqc0c1lcWLE2GCfneUyYs+d40fSfu0Xs1C +wnXAWoA6SMd5cTLKoo78+6yGwC6rTbDdoh4ZHoj71KXQ9W+h6z1g2XOhHan6ZtjouT86p25oP2aP +QPuzAeo/p0e7OwG7GyF29kW2ZuqFJvRqUycQSZsKAZlI11TWOrapTJUkwgT9rWNNOf1p3+mzqVCg +LUCcgD0RaNEAPPvQpo5D57Sp3KTOu+ub+j8Vnafa1Br06yrPptIxpml3hQDHNJogZ6eHreesRYHn +Btwd0HroOg58HRV9Acz6xovs4lCZndZxW6fV4M2bPd8BRQM7YJfPWdHAS8COAXMHFIVSUBtZY14C +Z7BPy9qxntjFqiwKtHdbV/IQbdY8Y6cQ9XmNOR32yLP8ogDe/sA920A4d14FBXHcPAT98vsS2mM2 +gH85w4fKorbRZX/m2dQx3zd7DAQaoKNoYDAQEF357zlQtU7WmUD39rqnMfVG077wvpG2hieJrQ0G +Z7DoMttuQvtVWQtgd9PDqdpaGM+wAa6nE5zez9zbd1gzi2buxZk7o/uZu0/vPmS4nFu4P4NL7oXZ +J31712EmTiPLgJX4W4oTST14hfq6+PXbEPr7/EYs1Nn6rYjHj2t+cb5+C2LSA5qPvaD5wQ+F4+cP +028jjDxbOP7pRZoPzxd2G64TVnObhdvvXC0c+w/9NoTKv1fH3/VPWj7sl8LR1f8sHC/4nbD9+vPC +7bP2CjsZb2p+4YBwbDW+ZeLbHCM/EY5u+VS4/fGgQ7mzfLBwbNRw4fpDuZp/XiisikYLt357nHDo +/HLhpvcmCdtPnC+cyLtAOPK7izWfnC7cvmGmMOzYbP/RO13+XITMnMa1jX66AoB+ytif9MOcut1T +fNXvYXTc2+s9xV95BkR1pE3O+J7fw9BmiTNiyj2FKRf3BVLOKu+e48veU3SVg5rAyZgGm/uHz7mn +kOeadPXZ3j2F16rPu6e4GI3KQUc8Cr4gqO8qdDv1uZlxPCOyz+pYKZ8z5+aHIUv3+OS9SDZATvUb +GfVD/yCAaf1hvzzHi8/0mOlNHlMWw/Rjzej2XJOO5ZtnwKvMXG3uAF6Gjp4GUs+8E5ze58ppmwbN +vO/qj2unOhnuWuS9FYBLznvjEOibXq/CLLdS/QPeIGvGXPcNjKoV8v06byiX4sL+Cj3HlYdkLlN5 +y/Tc9npM8yO7NdccE3Yy82ROib93jrCTcDTHrxJWFy0Wdu2bhWOffFc46twjHL9tneZPHtHyVRuF +7fe3Cbvuvwq3T3tJ2Llvv+Yz/ls4FjssrN46qvluPce1Hrdk7olenSUczwwJ21kFmp8fJezeWiIc +Oe9s4fa28cLOD/Qc55+v/P5c6JvzEruh0PODus1dpt8xLOTeqRCJuS6+A56E8cMzCfObeyfGHYVg +HCS9jZ8WxKWjn2m/tFFj036/kfnHCOP9YaYx9j7cK8eEmc74YfPJZ5g8phxE/Z+x0QLZGiB1bHxV +Z6i2wLPWpOCzA+4Mxe/rdsIWzoOdPA9GEN+RhooTFaHTfoYKB5+xKqGjcPAF4Jkez1ClqB+BMZKW +PWsY7caTVCWY+zzaTLwiMSpSGSqOVzhA4v/d+agN+/5JwYF3PqKtZQC0NXM+SoSjdqLi9J+PwsFA +oBI6CgcHA6fnfERb4/mItmbOR/EK245URu14RQuQ3vMR57dVAOe4r2peow4rg/UyVgfSWfx86Hge +xvMUn61Rz5FKNcKsl6frLH5e8HLMa7OtC8D0897iqz6Lc4zNRvu5f8YTZV7rqAiNiFS25DdVhkbQ +79dDHuzE7DOMHKI+3/uEkdgGaGs5Ttc7w7FIk+wdb7QWpuXdn6ZIDE/pWs/RtOTajr1D8juVGxGx +EHFTMd5eBGcCsYKOzFhB/ZDoWQT9HZmmrf2xhVkocx2wFkjXGJuMssoB/3cqN1ovoh3EQkU9MjwQ +77+vh86vQ92roPPfg7XOu+ub+j8VnVM3tFOYtbz74+KZzQD1P8Hp/Vyz583Ha3ce/skMnmvqkN4B +4JLnmmIE5uGE0ow3KPg7llnye5VvK97eNAI8w3TF34D7m0WSdgVOM2rtFH2WaRstZxX145s1X7te +n2Eu3+rxK1r+9EfCzsdD9Fli+UjNf3+ucLx8nrAz7AYt/+Htwu2r12h5jT7TxB67X8cv/plwdMVW +ze88JRyve07Y/tHvNf/dbmH3B68Jtze/qfnTQ8LO4g+FY498rHlkpzCGldxBiMZ8/lwIzJxR6PmZ +psDzU2b6GiYhZ5Mj4OXQ/RzwFAhpL7or9NmecddAxv6oA/zOnO25rnX1xZfrK35/7j8f0EazAYx/ +mSMZxzaZ83kG/Iw354ye8jMN85t0DLMcOtN+nE9czher0L5GgPY60dHPYroJ8JeCCxlIfWeotnLm +0Y3z5ExehbxhAC5pu2ciwLPaSlguz9zf6OPJTW08frHc2xUc0fZ79DJtp/Xf9/hnmiN7hNX3P9Dh +VUXaLs6NCMc3XiLs3H2tDt90k3B7ibbX2Dptr9GsDSK3//GXmj/S94zueU9K2Pm5ttdY1isSjs5/ +Qzh+z39qztJ2Gv2RtlP75HGd79+UI+3w2abfZvtjp+9CpwnY5x7wekw2b4O12rWdMu4tyKj73uzU +RVx/+oW2Y2yJ9skw7Q82JH7GUWbSGb9hY3/GZv1hpqGcLtU2H4TsLiDVNrMdFXAgnwxwXQIl32nd +vGlTn37XGUYeG2B+1gc9hvdyT6rNgv3w75c1baDsIVnfB9Dv+8D8rXInmPVuuTAxvPVrRMtQvEM8 +1OgQ4981fiTr076pHAmHAFzL6oAlAMxK5sYtEPI3xSa8GeEKBM5Rm5DCuJ7nZRObyuMg4PM4j3Iu +KgZCAPs74HTNRcMgywfoQgD9fwPJNTMiwD0AAD== + +------=_NextPart_01CEC02D.655CE090 +Content-Location: file:///C:/5D1ADE85/PEND_ENDPOINT_CLOSURE_files/image002.png +Content-Transfer-Encoding: base64 +Content-Type: image/png + +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 +VdMXxzqHsx3hnGEd5wVUew5n37mUXShM3RbHsYqI6ddu1HqaEc6l9ahbhlXfemc7wtltW45ALqu2 +7bMZbPUIZ2xPBG+ubzbBWS+/iqi+1346IjNy+0Y4c5tnCs56JLGOy7ytvThQfaxz/TEtdqZzOOtR +/Hq74+9QTvHN45WxmNuQ56N2U6LP5HpyHXVcmlILCE7BCQjOeRec3XlvvaN8fdM9cz3Dgi2/AqJv +XW1QDrtSbl9w5nlufSNxo0J6nODs+3A+Yh3nLlVwdlMyLyg462W229r3mDoO6zAMfedwZiCOCs5c +bjyvDapLGZzVlV+X1lNRc7SwDcXchgsNzh07dvxadfzOO4ezOoZ3zBSc9fPzcXlbhmt70aM2OOtz +PgUnIDgRnCA4F1Rw5jmaOYLZXkyomy44dZGZviu4tiOV7TIuNDgzgnM6bHsho9kEZ0xjnGlKbd9V +amcTnEPWMdaU2lxGTqnNKaR1UF1ocOa+ZVDW5yzm9lTre9yIYL1t9fTW3Lb4B4lcdgZtbleuK0Y+ +47nN+ZJDgzMv/JPrzNclj3kdffUIYv01KWXbttbHJY9De7XYOtK66clvqY7fefseo//1ucb52sx2 +hLM793RZ/VUq9bLHDc46vOM5ptQCglNwigkQnPM2OCMK4gqYsay8omk8NqfY5ohXqK+gOuyiORkH +FxqcM11IZ5zg7Nu26gI1M34P5zjBOcM6lg07T7SOyRzx7PtO0lEXDRoVnM1zpo9bd6GZz/V8P2Tv +93DOtG1d9Jzre436vseyPjbDgnOmCxX1jJYOhn1HZt9FjfIiPBmcPRfvGXlRny42t1Z//x4XnOM8 +t+/7UGcTnH0XDcp9E5yA4BScgOC8lP9juL5v9DG/yqP9Ts76a1HyarDxQThCK2QUxIVxMlrzvvy+ +zPq8xvprUdoRzr6vYOk+kL9ipu8Cbb8qpN6Xvn2r1nVVewxifbm89ms52q97yeN4Ievovmpjeh11 +HHbHM7/C5Kr2Ncxj0ve1KX1fi9LF7tJ6G+qv/8jHxe31123Uj8/pz+1y6n3utu2evq/uiGOa3+va +95Up9dfJ5NeQ9B3/No7ar2KpRziHrbv+Spaevx8nctvzq1Lqx+a66mOaz623oz5W9Xa0X/dSH/tR +z63X2329zolumvX0V6HUx7tdT/2VNPkPSO3XwAAITsEJCM4nTB2U+cE4g7P7rsbzzqWsp+0ZQZlZ +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 +wLz3/wNXaRDG12+ZzAAAAABJRU5ErkJggk== + +------=_NextPart_01CEC02D.655CE090 +Content-Location: file:///C:/5D1ADE85/PEND_ENDPOINT_CLOSURE_files/oledata.mso +Content-Transfer-Encoding: base64 +Content-Type: application/x-mso + +0M8R4KGxGuEAAAAAAAAAAAAAAAAAAAAAPgADAP7/CQAGAAAAAAAAAAAAAAABAAAAAQAAAAAAAAAA +EAAA/v///wAAAAD+////AAAAAAAAAAD///////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////9 +/////v///wMAAAAEAAAABQAAAAYAAAAHAAAACAAAAAkAAAAKAAAACwAAAAwAAAANAAAADgAAAA8A +AAAQAAAAEQAAABIAAAATAAAAFAAAABUAAAAWAAAAFwAAABgAAAAZAAAAGgAAABsAAAAcAAAAHQAA +AB4AAAAfAAAAIAAAACEAAAAiAAAAIwAAACQAAAAlAAAAJgAAACcAAAAoAAAAKQAAACoAAAArAAAA +LAAAAC0AAAAuAAAALwAAADAAAAAxAAAAMgAAADMAAAA0AAAANQAAAP7///////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////1IA +bwBvAHQAIABFAG4AdAByAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAWAAUA//////////8BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGDLzhFowM4B +/v///wAAAAAAAAAAXwAxADQANAAyADMAMAA1ADUAOQA3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAABgAAgH///////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAACAAAAhmYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///////////////wAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////// +////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALgA +AHic7FwJXBNH25/Z3FwJCQkgaAIoYkW5xFsSEK1YlYjEo60lQbkUCUVotWqJt9YD8Gi1tQWrtdYL +rcVeVqhHtbUK9q21p6BUTi0JIoJK9pvZSWrk1RaVr5/9/b7VP8/Mzuzs7DPPPMfsZMtKnSu2Huhy +CbQ7wgALmGkB4NqcgxYwhwgAypI30zRtPU3///GvOtoQfCxjOBlRDgIecx4CH0GAYIdgj+CA4Ijg +hCAkIgCcEcQIEgQXBCmCDMEVwQ3BHaELggeCJ0JXhG4IcgQFgheCt+X+UxDtgeCL0BPBD6EXwlMI +vRH8Efog9EUIQAhECEIIRghB6IcQitAfYQDCQIRBCIMRhiAMRRjGyDYASgQVQjhCBMJwhEiEEQgj +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 +AAA= + +------=_NextPart_01CEC02D.655CE090 +Content-Location: file:///C:/5D1ADE85/PEND_ENDPOINT_CLOSURE_files/filelist.xml +Content-Transfer-Encoding: quoted-printable +Content-Type: text/xml; charset="utf-8" + + + + + + + + + + +------=_NextPart_01CEC02D.655CE090-- diff --git a/network/trans/WFPSampler/docs/PROXY.mht b/network/trans/WFPSampler/docs/PROXY.mht new file mode 100644 index 000000000..0709a1caa --- /dev/null +++ b/network/trans/WFPSampler/docs/PROXY.mht @@ -0,0 +1,5441 @@ +MIME-Version: 1.0 +Content-Type: multipart/related; boundary="----=_NextPart_01CEBF8E.2E70F8E0" + +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_01CEBF8E.2E70F8E0 +Content-Location: file:///C:/CF2A4759/PROXY.htm +Content-Transfer-Encoding: quoted-printable +Content-Type: text/html; charset="windows-1252" + + + + + + + + + + + + +Proxy + + + + + + + + + + +
+ +
+ +

PROXY

+ +
+ +

Overview

+ +

The Proxy scenario is divided into two types:

+ +

·         +Proxy the socket which is achieved in +Win7+ by use of FWPM_LAYER_ALE_BIND_REDIRECT.

+ +

·         +Proxy the connection which is further +divided into two methods:

+ +

<= +span +style=3D'mso-list:Ignore'>o&nb= +sp;  +Proxy the connection using injection which is +supported on Vista+.  It should be = +noted +though that for Win7+ the redirect method should be used.

+ +

<= +span +style=3D'mso-list:Ignore'>o&nb= +sp;  +Proxy the connection using +FWPM_LAYER_ALE_CONNECT_REDIRECT.  T= +his +method is recommended for all proxying of conne= +ctions +post Vista.

+ +

All filters added sit in WFPSampl= +er's +sublayer (which is weighted just below IPsec's sublayer), unless otherwise +specified using the -sl +<SUBLAYER> command line option.  +All filters are associated with WFPSampler's +provider.

+ +

Proxy the Socket

+ +

The following diagram shows how the code flows for this +callout:

+ +


+Figure A. Code flow for Proxy (socket) by ALE Redirect Scenario<= +/o:p>

+ +

When traffic matches a filter at the specified layer, = +ClassifyProxyByALERedirect() is invoked by the Filtering +Engine.  This function will create = +the +REDIRECT_DATA which consists of the classifyHandle, +the redirectHandle, and the writableLayerData.  The appropriate tr= +iggerFn +is called.

+ +

If the operation method is synchronous (inline), TriggerProxyByALERedirectInline() is invoked.  This function creates the CLASSIFY_DATA, +which consists of the data that was passed into the cl= +assifyFn.  PerformProxySocketRedirection() is then invoked.

+ +

If the injection method is asynchronous (out of band),= + TriggerProxyByALERedirectOutOfBand() is invoked.  This function creates the CLASSIFY_DATA= + which +consists of copies and references of the data that was passed into the classifyFn.  = +Based on +the queuing method, the appropriate queueFn is +invoked.

+ +

Regardless of which queueFn is +used, each will call PerformProxySocketRedirection= +().

+ +

PerformProxySocketRedirection +() will modify the writable layer data with the values passed to the command +line.  The action is set to +FWP_ACTION_PERMIT and the layer data is applied.  From this point on, the socket will be = +bound +to the new local address and /or new local port that was specified on the +command line.

+ +

Applicable Layers

+ +

v  +FWPM_LAYER_ALE_BIND_REDIRECT_V4              (Win7+)

+ +

v  +FWPM_LAYER_ALE_BIND_REDIRECT_V6              (Win7+)

+ +

Proxy the Connection

+ +

By Injection

+ +

The following diagram shows how the code flows for this +callout:

+ +


+Figure B. Code flow for Proxy By Injection +Scenario

+ +

When traffic matches a filter at the specified layer, = +ClassifyProxyByInjection() is invoked by the Filtering +Engine.  This function validates th= +at we +can perform the injection by looking at the pClassifyO= +ut +rights.  It will then create the +INJECTION_DATA which consists of the injectionHandle +and the injectionState.  If the injectionSt= +ate +indicates that we haven’t injected this packet before, then the injection +method is determined (default is asynchronous), and the appropriate triggerFn is called.= +   +At this point, the original packet will be blocked.

+ +

If the injection method is synchronous (inline), TriggerProxyInjectionInline() is invoked.  This function creates the CLASSIFY_DATA, +which consists of the data that was passed into the cl= +assifyFn.  Depending on which layer the injection = +is +happening, the appropriate performFn is called.= +

+ +

If the injection method is asynchronous (out of band),= + TriggerProxyInjectionOutOfBand() is invoked.  This function creates the CLASSIFY_DATA= + which +consists of copies and references of the data that was passed into the classifyFn.  = +Based on +the queuing method, the appropriate queueFn is +invoked.

+ +

Regardless of which queueFn is +used, each will call the appropriate performFn = +based +on the layer the injection is happening.

+ +

Each of the performFns are +tailored to inject for their respective layers.  +Each will get the required data for its specific injectionFn.  Depending on the layer, the offsets are= + adjusted +on the original so the whole packet is available.  Once the offsets are adjusted, the orig= +inal +is cloned, and the offsets of the original are returned to the original +place.  The clone is modified with = +the +new address and ports that are specified, and the injection function is cal= +led.

+ +

Upon successful injection, CompleteProxyInjecti= +on() will be called by the TCP/IP +stack.  This function will show the +status of the injected packet.  +Additionally, any memory that was allocated from the functions above, +will be freed and any references released.

+ +

Applicable Layers

+ +

v  +FWPM_LAYER_INBOUND_TRANSPORT_V4         (Vista+)

+ +

v  +FWPM_LAYER_INBOUND_TRANSPORT _V6        (Vista+)

+ +

v  +FWPM_LAYER_OUTBOUND_TRANSPORT_V4     (Vista+)

+ +

v  +FWPM_LAYER_OUTBOUND_TRANSPORT _V6    (Vista+)

+ +

v  +FWPM_LAYER_DATAGRAM_DATA_V4                  (Vista+)

+ +

v  +FWPM_LAYER_DATAGRAM_DATA_V6                  (Vista+)

+ +

v  +FWPM_LAYER_STREAM_PACKET_V4                     (Win7+)

+ +

v  +FWPM_LAYER_STREAM_PACKET_V6                     (Win7+)

+ +

By ALE Redirect

+ +

The following diagram shows how the code flows for this +callout:

+ +

 
+Figure C. Code flow for Proxy (connection) by ALE Redirect Scenario

+ +

When traffic matches a filter at the specified layer, = +ClassifyProxyByALERedirect() is invoked by the Filtering +Engine.  This function will create = +the +REDIRECT_DATA which consists of the classifyHandle, +the redirectHandle, and the writableLayerData.  The appropriate tr= +iggerFn +is called.

+ +

If the operation method is synchronous (inline), TriggerProxyByALERedirectInline() is invoked.  This function creates the CLASSIFY_DATA, +which consists of the data that was passed into the cl= +assifyFn.  PerformProxyConnectRedirection() is then invoked.

+ +

If the injection method is asynchronous (out of band),= + TriggerProxyByALERedirectOutOfBand() is invoked.  This function creates the CLASSIFY_DATA= + which +consists of copies and references of the data that was passed into the classifyFn.  = +Based on +the queuing method, the appropriate queueFn is +invoked.

+ +

Regardless of which queueFn is +used, each will call PerformProxyConnectRedirection().

+ +

PerformProxyConnectRedirection +() will modify the writable layer data with the values passed to the command +line.  The action is set to +FWP_ACTION_PERMIT and the layer data is applied.  From this point on, the connection will= + be adjusted +to use the new remote address and /or new remote port that was specified on= + the +command line.

+ +

Applicable Layers

+ +

v  +FWPM_LAYER_ALE_CONNECT_REDIRECT_V4     (Win7+)

+ +

v  +FWPM_LAYER_ALE_CONNECT_REDIRECT_V6     (Win7+)

+ +

Command Line Usage

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

Option

+
+

Argument

+
+

Meaning

+
+

-s

+
+

PROXY

+
+

Implement the PROXY scenario

+
+

-l

+
+

Applicable Layer

+
+

Layer at which this filter will apply

+
+

-pla

+
+

IP Address

+
+

New local IP address for the socket (or connection if proxying the connection by injection).

+
+

-plp

+
+

Port

+
+

New local port for the socket (or connection if proxying + the connection by injection).

+
+

-pra

+
+

IP Address

+
+

New destination IP address for the connection.

+
+

-prp

+
+

Port

+
+

New destination port for the connection.

+
+

-plspid

+
+

Process ID of local Proxy Service

+
+

Proxy to the specified local proxy service.  This is only valid for proxying + the connection by ALE redirection.

+
+

-prs

+
+

 

+
+

Proxy to a remote proxy service.  + This is valid only for proxying the + connection by ALE redirection.

+
+

-sl

+
+

Applicable subLayer

+
+

SubLayer to associate with the filter= +.  [default is + WFPSAMPLER_SUBLAYER].

+
+

-v

+
+

 

+
+

Make the objects associated with this scenario’s instance dynamic= +

+
+

-b

+
+

 

+
+

Make the objects associated with this scenario’s instance availab= +le + during boot-time

+
+

-in

+
+

 

+
+

Perform the injection synchronously (inline)

+
+

-tdpc

+
+

 

+
+

Use threaded DPCs for asynchronous (out of band) queuing method +

+

-wi

+
+

 

+
+

Use work items for asynchronous (out of band) queuing method

+
+

-r

+
+

 

+
+

Remove objects associated with this scenario instance

+
+

-?

+
+

 

+
+

Display help

+
+ +

 

+ +

WFPSampler.E= +xe -s +PROXY -? provides +help output

+ +

WFPSampler.E= +xe -s +PROXY -l FWPM_LAYER_ALE_BIND_REDIRECT_V4 -aaid +C:\Traffic.exe -ipla 1.0.0.1 -pla +1.0.0.2 -plp 0x4444 -v” adds a dynamic filt= +er (-v) at FWPM_LAYER_ALE_BIND_REDIRE= +CT_V4 +(-l) which references the +appropriate callout.  This filter w= +ill +have 2 conditions; FWPM_CONDITION_ALE_APP_ID (-aaid) equals C:\Traffic, and +FWPM_CONDITION_IP_LOCAL_ADDRESS (-= +ipla) equals 1.0.0.1.  +When classified, it will modify the socket to be bound to local addr= +ess +(-pla<= +/b>) +1.0.0.2 and local port (-plp) 0x4444.  +This change endures for the lifetime of the socket.

+ +

WFPSampler.Ex= +e -s +PROXY -l FWPM_LAYER_ALE_BIND_REDIRECT_V4 -aaid +C:\Traffic.exe -ipla 1.0.0.1 -pla +1.0.0.2 -plp 0x4444 -v -r  removes (-r) +the dynamic filter (-v) at +FWPM_LAYER_ALE_BIND_REDIRECT_V4 (-= +l) +which references the appropriate callout.

+ +

WFPSampler.E= +xe -s +PROXY -l FWPM_LAYER_OUTBOUND_TRANSPORT_V4 -pra +127.0.0.1 -prp 0x4444“ = +adds +a persistent filter at FWPM_LAYER_OUTBOUND_TRANSPORT_V4 (-l) which references the appropriate callout.  This filter will have no conditions, me= +aning +it will act on all traffic seen at this layer.  +It will modify the destination address (-pra) +to the software loopback (127.= +0.0.1) +and the destination port (-prp) to 0x4444.  +For full end-to-end proxying to occur, a +second filter will need to be added in the inverse direction to proxy back = +to +the original endpoint.

+ +

WFPSampler.E= +xe -s +PROXY -l FWPM_LAYER_OUTBOUND_TRANSPORT_V4 -pra +127.0.0.1 -prp 0x4444 -r“ +removes (-r) the persis= +tent +filter at FWPM_LAYER_OUTBOUND_TRANSPORT_V4 (-l) which references the appropriate callout.

+ +

WFPSampler.E= +xe -s +PROXY -l FWPM_LAYER_ALE_CONNECT_REDIRECT_V4 -aaid +C:\Traffic.exe -ipla 1.0.0.1 -pra +127.0.0.1 -prp 0x4444 –pls= +pid +501 -v” adds a dynamic filter (-v) +at FWPM_LAYER_ALE_BIND_REDIRECT_V4 (-l) +which references the appropriate callout.  +This filter will have 2 conditions; FWPM_CONDITION_ALE_APP_ID (-aaid= +) equals +C:\Traffic, and FWPM_CONDITION_IP_LOCAL_ADDRESS (-ipla) equals 1.0.0.1.  When classified, it will modify the +connection’s TCB to use remote address (-pra) 127.0.0.1 and remote port (-prp) 0x4444.  The process that is listening for this +modified traffic is identified by processe ID (= +-plspid) 501. +This change endures for the lifetime of the connection.

+ +

WFPSampler.E= +xe -s +PROXY -l FWPM_LAYER_ALE_CONNECT_REDIRECT_V4 -aaid +C:\Traffic.exe -ipla 1.0.0.1 -pra +127.0.0.1 -prp 0x4444 –pls= +pid +501 -v -r  removes (-r) the dynamic filter (-v) at +FWPM_LAYER_ALE_CONNECT_REDIRECT_V4 (-l) +which references the appropriate callout.

+ +

WFPSampler.E= +xe -s +PROXY -l FWPM_LAYER_ALE_CONNECT_REDIRECT_V4 -aaid +C:\Traffic.exe -ipla 1.0.0.1 -pra +1.0.0.254 -prp 0x4444 –prs= +-v” +adds a dynamic filter (-v) at +FWPM_LAYER_ALE_BIND_REDIRECT_V4 (-= +l) +which references the appropriate callout.  +This filter will have 2 conditions; FWPM_CONDITION_ALE_APP_ID (-aaid= +) equals +C:\Traffic, and FWPM_CONDITION_IP_LOCAL_ADDRESS (-ipla) equals 1.0.0.1.  When classified, it will modify the +connection’s TCB to use remote address (-pra) 1.0.0.254 and remote port (-prp) 0x4444.  The process that is listening for this +modified traffic is on a remote server (-prs). This change endures for the lifetime of the +connection.

+ +

For a list of conditions applicable to each layer, ref= +er to Filtering +Conditions Available at Each Filtering Layer.

+ +

For a list of command line parameters for configuring = +each +condition, refer to Co= +nditions +for Command Line.

+ +
+ + + + + +------=_NextPart_01CEBF8E.2E70F8E0 +Content-Location: file:///C:/CF2A4759/PROXY_files/themedata.thmx +Content-Transfer-Encoding: base64 +Content-Type: application/vnd.ms-officetheme + +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 +cmVsc1BLBQYAAAAABQAFAF0BAACtCgAAAAA= + +------=_NextPart_01CEBF8E.2E70F8E0 +Content-Location: file:///C:/CF2A4759/PROXY_files/colorschememapping.xml +Content-Transfer-Encoding: quoted-printable +Content-Type: text/xml + + + +------=_NextPart_01CEBF8E.2E70F8E0 +Content-Location: file:///C:/CF2A4759/PROXY_files/image001.emz +Content-Transfer-Encoding: base64 +Content-Type: image/x-emz + +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 +sDoN6EMBzgdQ/j+wNnfJ/EkAAE== + +------=_NextPart_01CEBF8E.2E70F8E0 +Content-Location: file:///C:/CF2A4759/PROXY_files/image002.png +Content-Transfer-Encoding: base64 +Content-Type: image/png + +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 +325Hfk+V8d8E9ZXtBwmTrjtHg3Obi9svLUM6rsCQo8vFTINP5zHr7b4/1X7Q8MMxs61Z1l/EsvL2 +5vW6qQzpOkSbML8qlluO9sbP48oixetz6STb3JsqpHOZ8T6ID1syiIv3z6l237y5HbUev33FihV/ +2WzHl2I7IpJj2ni5vB07dvxEuZ4M6Xq9zbZOxPLdd9/96naE/ltlqOdzzdgW0iCkAYAhCukItzgG +NkK4iZLxqxvkCHLcFiPWxYjkxNTvDOnm9jXV1Orxn+cx0k0MHW2n805MGS+X34bNeEhn7BbL+la7 +jpvbSyWOjwrnaHYZ/Lkd9THSEWNx2b2chluOSGdstifXeqEM4ZAnLssYLo91ztHaXG6GcjyH2I+x +HfVx2TE9eNu2bUdz30dMN2G6qo7wet3tY14q92E8j/hzPp+MuxzNL57Xt8qR5eb+Mc38xXZ0dtkg +x0iX25v7LKeEx89i/8aym23+83abb8/1lI+Z6mRjeXvzXvz+erQ5tqN5jW9r34u/ksuJc6DUU87j +MTnq33WMdBnS7XTtkxnNeZ/mtfs/IqbjPu0HC9+KUM/XKEfbZ/NEaUIahDQAMA9DujxGuuP2l3KU +Mi+rGPbt2/c9EbvFiPRZJ/CqQzojOadIZ1BGQNUhncc11yGdo83F6OuP1FO587H19/2eW7mMdnte +aKfqLiuC69Ptdr0tj98uR+Kb57g2P0Qol9Hc/74yItugndiWOD46n3/EYbHui7vW3Y6sni6neucy +8/nEhwsZvLldXScNSzFboAjxviEdo8txTHF5rHE5ipsfWJTLjpN4lcudbkiX08PzPhGxze1fqN6L +O+NcKO2+/nCuJz5gyXPITBXSGcn1NO2u+5SxfiHOOC6kQUgDAAsopPP46AjpiJXyesIZhIOGdDuK +PB5cEeGxjDVr1hzPY6LnQ0iXU7Pj5zHC2vz+tC9Hn3O0O+O2PpY7l11feiuDNh4Xl2HM55+j0vH4 +mCo92bpjGTkS3+7DnTElu739rKndcXLNDPTYrvJ5xShvTLOvrxuewZsjvjFC23WysfKkZPGa5THb +5Uhxeb3pDM3Y5ngvxHH4OWI9nZDOKeIR0s0yvrd8LxbH8k88xzhBaRwTHutsP/D5Zvn8yw8BYj2x +H/I+sR3F/s8ZCq/Pqd1CGoQ0ACCk+4Z0WL169VN5nGjl4umEdDltulYE6pyGdMhY7bi29viJzHIU +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 +vV0RXbldGd7x/OP7PBlVfl9N687tOV1O7x4kpPsFbjkFudxv5Yh3xm3uowz7jPm8PUdqqw9S+oZ0 +7ov44KMcsc3ltO+diajMfZXRGdueH6R0jRb3m4pd7LOBQ/p81xHRO9mI9GTvkbhP7NMi6Mfvf889 +9/xQ8T47na9R+WFIsw2r2vfMqbw99nd+ICSkASGNkDY6PVRfTz/9dG/T6tW9Ry691PtCSF/wkK6P +nR0kpJvXZX+5jI6RxNF2OvPEY8uwzVDp2oauY6Tr6dJdj8v15ah1/CxjuGuaeBnYuYyMxlhujm5n +YHaNBmfA5DpztDenXuftOdpbTs0uRpPP+nBhpkaky5H8XHb7YcHpct9mqOU218dDT3WysSpaR4v3 +0enyvVG9P5Z0TaHOY727gjZ/Vr6+KUeL29dzypAuX8/zWMeWqY6R7nqP5OuQU+uL6fjvL0O6nKWQ +x23HSHjXc3OMNCCkEdJGp4dyFHrrihW9E94HQnoehnT1/YQcXct4rc8gXa63DOkUIdFvG7qOka51 +Pa4rgrpCurxkUflcQsZzLCOjP0dzu45Pzm0tf5bhFKPQ9VTkvK1LbvdMHiNdhnc9elyGeHmM8myG +dHFm69GukG5jtTNyyzOW5wcTxfP+bPnan2tIl9OuB1hH35Au15v3y5/l+68+kVkd0vl9uW4hDQhp +hDRDPzptFFpIL4SQLkdlM4LK6zlHGEakxQh1efboUF7Gqo6p+hjpmQjpcn3FtOWzjqHO+8T3ORI7 +SXifdWmryUaky1iup19Xo8GvGE0uR46LMD1Tx255wrOpztqdcVieWTr3VTltuHxN8oRX5TTtcur3 +dEM6p27n1Ol6W/tN7Y5l1vs6Pswon0v5/MvLYTXL3ljHf7z3yn3c9SHDOa5jWiPSedx1ecmsclr2 +oCHdvo9yGT9hajcgpBHSDMXotFFoIb2QQjqnKseU7jwrcxnLGVkx+pq3lWfJLo8jrcx4SE+2vhxV +Lqdul8dLl8cQl8uIDwi6RkTrE3WVjy+XW8ZnjkSWyyw/kMj92O862nks+GTXke46a3euN2O/38nE +8iRgXbe3I9rTCulyGnnXtnbdntOZ69ty26rR8bM0++7efpexKq7lfKbf+qe7jq6Q7ncisfZkbhsn +e48OGtLxs/pkY/nchDQgpBHSRqcX5ei0UWghPRfykkRlALfBdE/XNaXLy19lSMfIbIwW5nTnHKnN +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 + +------=_NextPart_01CEBF8E.2E70F8E0 +Content-Location: file:///C:/CF2A4759/PROXY_files/image003.emz +Content-Transfer-Encoding: base64 +Content-Type: image/x-emz + +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 +Dk7ti8fpsg36z0sAhgDA+P8BNL6uAVhQAAB= + +------=_NextPart_01CEBF8E.2E70F8E0 +Content-Location: file:///C:/CF2A4759/PROXY_files/image004.png +Content-Transfer-Encoding: base64 +Content-Type: image/png + +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 +GVNKc71led+stvuMKbX5fXt7mymjFzQjd/2mIb6uKzj73b98CL0jti2n1OaU19Yo6WnlOd8T29hv +GyKSqrjLbdrYbNPpKbXx+BJEX6lGHs+4f7VNJ/vshwV1cJYP3Nuq7RkOlwiuWH5Z3sXVSN/C5kP5 +Z9tTFmO5ZTsHIyQ6pjMuHGV7hpfbMYX39H6JD/utUcfO+/QLzojLjJ46gDv236ub/X3GeiLgmvfr +s/2m6vaZ4rqoPc03t6Fr+m/sj9jndTy219c1pTZ+Fn8u8j1R3pubItQzwOrnEYFWXt/OZUdYxeO6 +nkv52Y9mxHZtd7ltWbPdvzOW7X7DG97wz8rj/iKCsw7FWH97HfnYkdYdf+7Kbc8076nFI4y2vuA1 +LH/PvaZar+AUnDNrtPOtS5b0FsRfhMXHiifsF8EJMIXB2Y7G8iHtwuaD2HBwRhzGaEhMz2tCb/hx +EXTxgTtGLTPoYkQkHhujIPFBtwmiUzHqkKOjZXlP5n1jedX3F41nSm0EURM1K3KEsNnuvsEZQdhs +78nchgzKGNnI0b+4f2xz7IuuKbUZY+vWrftUPCajOr6P7W4HZ4xixTLLZ7Ofin0WI5GxzIy9jJ2I +/9jXuY0xuhMjYznaGOuKabrtYzhz9DEfH88ptiked/31138qoqAOqjvvvHN4P+T3OXU24ykiNdcb +9z1w4MDaWF+OeNbBGSN8+VrEyFCsO+O3PM83x2Pz+xgdbSLtjACO+zXb81xGXozi5nri9nhc/OdE +FYWLmuf93JVXXvnVWHcso3k/nRotOJt9+GzGXh6fmetqnv/JGAGNIMt15TTceB3K6/2JWHaM0sZr +E69r3CejOUOq2Tfb8rjP3KZYd/6nRj7fvG+zH2NU9fncj9U23hnLypgf7RjOuH8+9wy5ss0/Hdsc +683tjqm4ue5cdnn/PNrsn+9VoX1jjsw2o8DP9NnuO8ez3WMJzmafbaz22fC6y2vyw+11x7oiONtT +d2sxuhqzMuI51Y8vz/O9VUwLTsE5M8PzaPHe+MeqWF68pXi0eMr+EZwA5zE48xjO9vGAGV5NuA1U +H9Kfb47jO/1hK4Mrp+fGsuqgjZjM+zax9fxNN930kebD6HDsxfJaj+sMzgzMCIIMyQivXHe/4KxP +IhQfeJvRutc1H1pP1qOlcVuOVo4UnPl93icDsB2c9WhsTtetRiMX5PZENNXHTMavXcc6toOz6/F5 +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 ++E/BaVTTaKfgpCs4f+M35vWOHWM6WbBAcApOBCdM4+Bsi5HO9vGfsTzHfwrOuTyqabRTcPKBeZs2 +rfvU0NDy/7B27eVPMH285jWv+nfbt29f4D0qOBGcMCOCs/UX5vDxn+vWrXs0jv289NJLn4zjP3fv +3n274z8F51wb1TTaKTgBBKfgBME5idfhPHjw4FAc/7l27drHYvRzxYoVj8do6N69e2/xl5bgnCuj +mkY7BSeA4BScIDjPwx/S/fv3b4rjPVevXn1swYIFJ+L4z1tvvfW9jv8UnLN9VNNop+AEEJyCEwTn +eXT48OElcfznxo0bPxbHfy5duvRpx38Kztk+qmm0U3ACCE7BCYJzav6yXe74T8E5F0Y1xzTa+U6j +nYITEJwIThCck2auH/8pOGf/qKbRTsEJIDgFJwjOaWKuHf8pOOfGqKbRTsEJIDgFJwjOaWak4z9j +aq7gNKo5W76MdgpOQHAiOEFwTv1f1KeP/4z4jOM/I0bj+M+IU8FpVNNop+AEEJwIThCcEyKO/4zp +tnH8Z0y/jeM/4++FmJYrOI1qGu0UnACCE8EJgnPCRGjG3wsRnhGgEaIRpBGmgnN2jmp+97vf7f34 +j/94b2BgoLzN5/Wuvvrq3r/4F/+id+rUqUkLwj/6oz/qrVmzpvf8888b7RScAIJTcAJzJThrMcU2 +ptrGlNuYehtTcGMq7nQ7/lNwntuo5pvf/ObeO97xjt5f/dVfnRGDkx2dRjsFJ4DgFJzAHA7Ojr/k +l8fzn27HfwrOsz9W85vf/ObwyOZ3vvOdM37+W7/1W703vOENp4Nz/vz5p0cjI0jjMfn9sWPHhgM1 +Rkdvv/323je+8Y3T94uYzWX80i/90nDYxvf1bfH7WNf73ve+4eVu2bLldPzG6GvcL0dfw9mEsNFO +wQkITgQnCM4ZZroc/zmng/Mcj9VsR2H98zoq+wXnt7/97eHYzED89V//9c6ozOAsf346gzOW/8Uv +fnH4+3e+8529j3/848O/j+XF93m/c52Ga7RTcAKCE8EJgnOGmqrjP+dqcE7EGWjPNTjz9zn6mKOc +4w3O9v1itHMygtNop+AEBCeCEwTnLHA+j/+cc8E5gWegzSm17Ygb65TafsHab0rtWIMzv4/tyOm6 +cTKjHAV1bKfgBASn/SE4QXBS/wNxxvGfl19++RMRo3v27Nk+1uM/Y6R07969t8zl4JyM62rm8ZN/ +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 +MqfU5uPiuM883vKv/uqvhuM1Hpu3x4mF6lHNiM68/6c//ekztjPuG+vIs962p9+Od4TTqOa0HOEc +jqitW7f+RBV787dt2/ZAM7I2tH79+l9pprkOf1CMD6rxs5x+ec899/xg3pZTLnNKazOt8fMxchrr +aE93raeHlg/jB8YanPGznBZ80003/UJsZ2xDvd3197HOLVu2/Fz8PqYRltsvrJffnmIcYhvawRkf +mnOaZnluO3Oqbz0ynKOS9c/ieXZtb25DvU9zumrZzgdyRCpCL26LUcey7tNTRTMSInLzuVZTJS+s +wzV+FtOmczvqabF1cMboZqy3/J3w5rw9RinjZ+U1uidGV+ugigCObc1118vNsMxR0diGeA7581jH +5s2bfz5fl3qbY7llWW/N91TXfiv7+PR+jSmyEXMZnHG/HJUt93t1fJ/BV43oD/881hM/L7fvzW2P +kbr6ebWnn8ZziJ/F+z9GtWP98R8NdXA275fP5ghmOzjrUcd6/zTP7Y2t98zpUe6c/ltez2vL+3r4 +uNWYphvbl1Nn431ZTxvOdZX3yY/lOsr76svxszo4Ixjzz0q+nvVodyynLPtX8/Uvt+1atGjR/xvL +jddn1apVX66fU7n5jtiGeFw9khvfxyhoxG95njEK/vn2umL/5fOLdbanQSM4QXAKTibxup0z7cuo +5vQMzgip8qHz93OqZHxojQ+1OeKZH8wzROODaNd03HxcRmN8OIyoi1C8+OKL/zxiLUcwy4fnj+Qo +avlw+pVYT3yYHmtwNqNRJ6vtOtWsf357Sm1+397eiKr44NpadtuCdnCOdP849u8D1ZTanPKaU2Db +949YiG3suj3CLl6DjimQC9tTapv98VyfKdIXt0YZz5BRWQdn+VC/LX4fo33x4T/CLx4f0dvs19PT +VPO29nLzse3b8nF1iLZel4/mOst741i5zwteu4zJiM06HrvuEycRin2VIZnrjTiqAzOmtTYjrsMB +miOefaafLmtNwR0Wy4ygz+As8TeYwZv7uR2c1ejm6f1Th2nH63ldbN9I02M7HrOqfkz8eYz3c9m2 +GJU8tWLFij/I90nEao56dm1X14hsaZf353OKEesY3Wzvl+a9cMaU2ur79nvyzthXZV+uby+r+bPx +o3PlhEyCEwQngnNKr9s5U76Mas6cYzhjtCFDsJ5im9Num+lww2EUo2vxsxwZzdHSCIXyQfbJanru +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 ++/8BhZ6aslkcBiUAAAAASUVORK5CYIJ= + +------=_NextPart_01CEBF8E.2E70F8E0 +Content-Location: file:///C:/CF2A4759/PROXY_files/image005.emz +Content-Transfer-Encoding: base64 +Content-Type: image/x-emz + +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 + +------=_NextPart_01CEBF8E.2E70F8E0 +Content-Location: file:///C:/CF2A4759/PROXY_files/image006.png +Content-Transfer-Encoding: base64 +Content-Type: image/png + +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 +sOGzzX1eW78+sf9nez/FOuM1im3I1whASCOkjU4bhX7ggfEPJZ4fsveOkD7v+DnTxs8rHDx48La+ +/2C9/Lg5iaSIzH7bPpejfhGPXdu1evXqP5juvtq4ceNv5bJ27Njx7vLx1Wu3ZJBtyGVs3rz5n3fd +3gbW6Hm8l84K6fr72XgPRBzmBwTlz5v9/ftdz7GJ2p+cznPMddR/F9p1/N4g62i+XsqfZ3AX+6Z8 +j3xhNgNXSANCGiFtdNrXkI9CC+kZ+x/fxOhpE22/Hb/Mx38Xwoh0GZIRiLG9K1eujP+J95pQ/PW5 +Cvw2Vs402/Ll2K4777zzF3M7J/tgouu1iehpnsvHcnmDhnS9DfWIdAZ6BFX8/Fy3caqQnu0R6Xye +9YcMZeBmvJYfSuzfv3/7oNt0vuuImQPFfrmoDNo+75E3zfL+EtKAkEZIG502Cj2so9BCelZ+yX5X +BkP585guHbdFEEQU5RTgjLOu+3ZND++a8hz3z8eW05jracZd21CF5GgZPe2HAqP5uJgWXW57bk9O +DY//lh8a5LTbnE6d2xbrLJdZb3v+NyM1tysDKyI1p13Xz7GOzhxVjuNZ8/HlSPsgId1vhDmXV8Zh +uY3VPtqXr1U9vTxHa3Pf1tuT+zHWkc87tjunlufy4nXNY4i7pkeXt+cU6FhG+YFAPJd4XHv7eLjG +FOpyOTkS3344MVq+Frkfmud/Vx7LfC7r2LJly+O5jrhvE8m/UHzg855YblfQNuv5TLv/J0K6Wf+W +ftPG87b4WU4v73qN2udzON+3QhoQ0ghpo9ND/XX8+PGhHoUW0hcupNuQyKnJvYzUOmLbqbSd08Or +yJqQy8p1l7eVo8pd2xDLLUekt2/f/hP5/R133PFL8dh+2x5R0bU9Ea1lcGVo5kh3REqGURmhef94 +fD6XerQxp3a3I83j624C6IoM0vI+1f5cklFdThk+lxHpOpq7RqQzDLtes1hebnOE7SSHBbxianeO +WOdrkaOvsT+a5X6x433zpoz5ehp0jgDX64/H5M8zluuo7NqmrqDN/dZnHYeLdSzpt442ik9X0+vf +k0Hb8R75Qjma3zX9O1+fjPH2MWeq6fnjy9i2bdvPTzKFX0gDQhohbXR6eL5OnjzZe+Q97+ndPeSj +0EL6wod0BmtO5y5DOqMuIimW00bweAzHiFkuow6gPXv2/EAZlvXtGbZd29Dv+O7YhhyV63pcub4I +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 +wcYHGO19TrUfikwa0u3rPjEy234AkqOot5frzZHtOqTLZUTodj1mOiHdjgrnc39T/dwzNuP2qUK6 +HoGdRkhvjWgttuelWFY7q+Os2I7nHx/ytCdueylHisvtjmXFNaPrCO8X0rGM6YR0bmO/dcflwHKE +OUbD8/VvXu+bLrvssq/EcdOx3Pj7lq/hmjVrjsc+FNIIafqKqd7N/zF7721salzXeHvjI43n7B8h +PWQ2377tY5P8kgoUll1y2X9tfuFcuRhCuh5pjttytLeM2RylbUeJX6qDNab0VrG7tI7YHDHLdZXL +r48xjpOK1cdLl6PJIbc7R8fzfuVobn3Cs3JEOa8j3XWysTwmO0/E1Xx/NEfqymXESasiWJr/vjWn +h0cc5T4c5BjpMqTbk0O9VI7yltvaHif8Yu6/PJ47ltFG/cSocp7wqj5uuDwGPPZVTMFv3wd/ka97 +dSz5TV0hPchjphPSeVbwrucer0F9e7mPzzek77zzzkfL/d7sl98vZgKsaQ9Z+PPyZGu5njjhXk6n +7tru8tjuiOl21P50eYx083vR3n7PbbKQLk8m1rXuuE+zvn+d68uR6/h+2bJl/615brfktnWtV0gj +pBn4uOpPNh5urG093P7M2b6F9GJ33Zob/+3IDz7WG3nv54EpXLzi2q830fL6xRLSMbLW/AL9xfL4 +6gijPLY6pofm1O14XHW89EV1MOf9ikDuvIZzeXbndvs/ndsQU7zjZzmtu5zCnfLM2u2x1RflMa0d +61nVdSKw+jrSZUi3o6053XxFBH25ffUJvdr4PloenxvbPN0R6eJ5vVSflKo6E/Op8jrhGcMxmlgH +fL1d5faVZ6Vu3wev2Ie5fV0h3e8xGc7TCen2uR/peu65vva5vFQ8hw/nqPH5hHQ5RbtcdzmK23U9 +5jZER/NY5K4TidXBm8c5lyPSsf7mtXu6vE56vK/juU0W0sU+67vu9rm94oRil19++f8dy9q8efNH +yn0asW1EGiHNeXmuHZ1+eztavakdvXbiMiEtpEFIz8eQzusU5zGztfh5hER93GgZUuV05frneUxx +Bkl9TeQiPG+qp1aXI8mxnV3b1wb6xEnHyudUrrvf88nt7FpXvQ3l88zl1NuVyyufS25Pv+dS7qti +ua/q2o9d+7V+bvW2DvJa1q9NjIjWz3+y6ynX+zG3L9eZ07YHeUy/59j1/hn0uU+2j/O5du3TQf4O +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 ++/rOAAAAAElFTkSuQmCC + +------=_NextPart_01CEBF8E.2E70F8E0 +Content-Location: file:///C:/CF2A4759/PROXY_files/oledata.mso +Content-Transfer-Encoding: base64 +Content-Type: application/x-mso + +0M8R4KGxGuEAAAAAAAAAAAAAAAAAAAAAPgADAP7/CQAGAAAAAAAAAAAAAAACAAAAAQAAAAAAAAAA +EAAA/v///wAAAAD+////AAAAAAAAAABnAAAA//////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////9 +/////v///wMAAAAEAAAABQAAAAYAAAAHAAAACAAAAAkAAAAKAAAACwAAAAwAAAANAAAADgAAAA8A +AAAQAAAAEQAAABIAAAATAAAAFAAAABUAAAAWAAAAFwAAABgAAAAZAAAAGgAAABsAAAAcAAAAHQAA +AB4AAAAfAAAAIAAAACEAAAAiAAAAIwAAACQAAAAlAAAAJgAAACcAAAAoAAAAKQAAACoAAAArAAAA +LAAAAC0AAAAuAAAALwAAADAAAAAxAAAAMgAAAP7///80AAAANQAAADYAAAA3AAAAOAAAADkAAAA6 +AAAAOwAAADwAAAA9AAAAPgAAAD8AAABAAAAAQQAAAEIAAABDAAAARAAAAEUAAABGAAAARwAAAEgA +AABJAAAASgAAAEsAAABMAAAATQAAAE4AAABPAAAAUAAAAFEAAABSAAAAUwAAAFQAAABVAAAAVgAA +AFcAAABYAAAAWQAAAFoAAABbAAAAXAAAAF0AAABeAAAAXwAAAGAAAABhAAAAYgAAAGMAAABkAAAA +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 +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAIEAAACCAAAAgwAAAIQAAACFAAAAhgAAAIcAAACIAAAAiQAAAIoAAACLAAAAjAAA +AI0AAACOAAAAjwAAAJAAAACRAAAAkgAAAJMAAACUAAAAlQAAAJYAAACXAAAAmAAAAP7///////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +////////////AL4AAHic7H0JXFTV2/85984MMyzOMMMMoCgDKGKiDJtbygyguaSByqhoCgOyJQgq +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 +AAAAAAAAAAAAAAAAAAAAAAA= + +------=_NextPart_01CEBF8E.2E70F8E0 +Content-Location: file:///C:/CF2A4759/PROXY_files/filelist.xml +Content-Transfer-Encoding: quoted-printable +Content-Type: text/xml; charset="utf-8" + + + + + + + + + + + + + + +------=_NextPart_01CEBF8E.2E70F8E0-- diff --git a/network/trans/WFPSampler/docs/_WFPSampler_Overview.mht b/network/trans/WFPSampler/docs/_WFPSampler_Overview.mht new file mode 100644 index 000000000..9828310bb --- /dev/null +++ b/network/trans/WFPSampler/docs/_WFPSampler_Overview.mht @@ -0,0 +1,5894 @@ +MIME-Version: 1.0 +Content-Type: multipart/related; boundary="----=_NextPart_01CEBF89.9B5B26B0" + +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_01CEBF89.9B5B26B0 +Content-Location: file:///C:/515CB117/_WFPSampler_Overview.htm +Content-Transfer-Encoding: quoted-printable +Content-Type: text/html; charset="windows-1252" + + + + + + + + + + + + +WFPSampler Overview + + + + + + + + + + +
+ +
+ +

Windows Filt= +ering +Platform (WFP) Sampler

+ +
+ +

The WFPSampler is a fully functional firewall comprise= +d of +the following binaries:

+ +

v  +WFPSampler.exe

+ +

v  +WFPSamplerService.Exe

+ +

v  +WFPSamplerCalloutDriver= +.Sys

+ +

v  +WFPSamplerProxyService.Exe

+ +

WFPSampler.Exe

+ +

Usage:

+ +

This executable is the command-line interface used by = +the +user to define the policy. 

+ +

WFPSampler.e= +xe -?” +provides output of valid parameters to pass.

+ +

WFPSampler.e= +xe -s +<SCENARIO> -?” provides output of valid parameters pertaining to = +that +scenario.

+ +

Source:

+ +

The executable is located under exe and is comprised o= +f the +following files:

+ +

v  +Framework_RPCClientInterface.CPP

+ +

v  +Framework_RPCClientInte= +rface.H

+ +

v  +Framework_WFPSampler.CPP

+ +

v  +Framework_WFPSampler.H<= +/span>

+ +

v  +Framework_WFPSampler.rc= +

+ +

v  +HelperFunctions_CommandLine.CPP

+ +

v  +HelperFunctions_Command= +Line.H

+ +

v  +Scenarios_AppContainers.CPP

+ +

v  +Scenarios_AppContainers= +.H

+ +

v  +Scenarios_BasicAction.CPP

+ +

v  +Scenarios_BasicAction.H= +

+ +

v  +Scenarios_BasicPacketExamination.CPP

+ +

v  +Scenarios_BasicPacketEx= +amination.H

+ +

v  +Scenarios_BasicPacketInjection.CPP

+ +

v  +Scenarios_BasicPacketIn= +jection.H

+ +

v  +Scenarios_BasicPacketModification.CPP

+ +

v  +Scenarios_BasicPacketMo= +dification.H

+ +

v  +Scenarios_BasicStreamInjection.CPP

+ +

v  +Scenarios_BasicStreamIn= +jection.H

+ +

v  +Scenarios_FastPacketInjection.CPP

+ +

v  +Scenarios_FastPacketInj= +ection.H

+ +

v  +Scenarios_FastStreamInjection.CPP= +

+ +

v  +Scenarios_FastStreamInj= +ection.H

+ +

v  +Scenarios_FlowAssociation.CPP

+ +

v  +Scenarios_FlowAssociati= +on.H

+ +

v  +Scenarios_Include.H

+ +

v  +Scenarios_PendAuthorization.CPP

+ +

v  +Scenarios_PendAuthoriza= +tion.H

+ +

v  +Scenarios_PendEndpointClosure.CPP= +

+ +

v  +Scenarios_PendEndpointC= +losure.H

+ +

v  +Scenarios_Proxy.CPP

+ +

v  +Scenarios_Proxy.H

+ +

and links with lib\WFPSampl= +er.lib.

+ +

The Framework= +_* +files contain functions for creating and maintaining the executable such as= + wmain.  These= + are +relatively safe to ignore if you are only concerned with WFP functionality.= +

+ +

The HelperFun= +ctions_CommandLine.* +are files which assist with parsing of the command line parameters passed.<= +span +style=3D'mso-spacerun:yes'>  This has the building blocks for the +FWPM_FILTER and FWPM_PROVIDER_CONTEXT that the WFPSamplerService.Exe uses f= +or +defining policy.

+ +

The Scenarios= +_* +are files which gather and package all the data required by the +WFPSamplerService.Exe.  The package= +d data +is sent to the WFPSamplerService.Exe via local RPC interfaces exposed by the +service.

+ +

<= +![endif]>

+ +

Figure A.   Interaction between modules= +

+ +

WFPSamplerService.Exe

+ +

Usage:

+ +

This is the service which instructs BFE to add or remo= +ve +policies.

+ +

WFPSamplerSe= +rvice.Exe +-i“ installs the +service.  While installed, the serv= +ice is +under the control of the Service Control Manager (as WFPSampler).  The service is set to autostart +and depends on BFE.

+ +

WFPSamplerSe= +rvice.Exe +-u“ uninstalls the service.

+ +

Source:

+ +

The executable is located under svc and is comprised o= +f the +following files:

+ +

v  +Framework_ActionCenter.CPP *

+ +

v  +Framework_ActionCenter.= +H +*

+ +

v  +Framework_Include.H

+ +

v  +Framework_RPCServerInterface.CPP

+ +

v  +Framework_RPCServerInte= +rface.H

+ +

v  +Framework_WFPSamplerService.CPP

+ +

v  +Framework_WFPSamplerSer= +vice.H

+ +

v  +Framework_WFPSamplerSer= +vice.RC

+ +

v  +Framework_WFPSamplerService_Msg.MC

+ +

v  +Framework_WindowsFirewall.CPP

+ +

v  +Framework_WindowsFirewa= +ll.H

+ +

v  +Scenarios_AppContainers.CPP

+ +

v  +Scenarios_BasicAction.CPP

+ +

v  +Scenarios_BasicPacketExamination.CPP

+ +

v  +Scenarios_BasicPacketInjection.CPP

+ +

v  +Scenarios_BasicPacketModification.CPP

+ +

v  +Scenarios_BasicStreamInjection.CPP

+ +

v  +Scenarios_FastPacketInjection.CPP

+ +

v  +Scenarios_FastStreamInjection.CPP= +

+ +

v  +Scenarios_FlowAssociation.CPP

+ +

v  +Scenarios_PendAuthorization.CPP

+ +

v  +Scenarios_PendEndpointClosure.CPP= +

+ +

v  +Scenarios_Proxy.CPP

+ +

*Avai= +lable +only under Non Disclosure Agreement (NDA)<= +/o:p>

+ +

and links with lib\WFPSampl= +er.lib.

+ +

The Framework= +_* +files contain functions for creating and maintaining the service such as ServiceMain.  These +are relatively safe to ignore if you are only concerned with WFP functional= +ity +with the following exceptions:  Framework_WindowsFirewall.CPP +demonstrates how to properly disable Microsoft Windows Firewall, and if you +have a signed NDA agreement, Frame= +work_ActionCenter.CPP +demonstrates how to register with Microsoft Windows Action Center.

+ +

The Scenarios= +_* +files finish creating the filters and provider contexts required by each +scenario.  These are then added to = +the +system using Fwpm Management calls.

+ +

WFPSamplerCalloutDriver.Sys

+ +

Usage:

+ +

This is the driver which houses the various callout +functions.

+ +

Source:

+ +

This driver is located under sys and is comprised of t= +he +following files:

+ +

v  +ClassifyFunctions_BasicActionCallouts.CPP

+ +

v  +ClassifyFunctions_Basic= +ActionCallouts.H

+ +

v  +ClassifyFunctions_BasicPacketExaminationCall= +outs.CPP

+ +

v  +ClassifyFunctions_Basic= +PacketExaminationCallouts.H

+ +

v  +ClassifyFunctions_BasicPacketInjectionCallou= +ts.CPP

+ +

v  +ClassifyFunctions_Basic= +PacketInjectionCallouts.H

+ +

v  +ClassifyFunctions_BasicPacketModificationCal= +louts.CPP

+ +

v  +ClassifyFunctions_Basic= +PacketModificationCallouts.H

+ +

v  +ClassifyFunctions_BasicStreamInjectionCallou= +ts.CPP

+ +

v  +ClassifyFunctions_Basic= +StreamInjectionCallouts.H

+ +

v  +ClassifyFunctions_FastPacketInjectionCallout= +s.CPP

+ +

v  +ClassifyFunctions_FastP= +acketInjectionCallouts.H

+ +

v  +ClassifyFunctions_FastStreamInjectionCallout= +s.CPP

+ +

v  +ClassifyFunctions_FastS= +treamInjectionCallouts.H

+ +

v  +ClassifyFunctions_Inclu= +de.H

+ +

v  +ClassifyFunctions_PendAuthorizationCallouts.= +CPP

+ +

v  +ClassifyFunctions_PendA= +uthorizationCallouts.H

+ +

v  +ClassifyFunctions_PendEndpointClosureCallout= +s.CPP

+ +

v  +ClassifyFunctions_PendE= +ndpointClosureCallouts.H

+ +

v  +ClassifyFunctions_ProxyCallouts.CPP

+ +

v  +ClassifyFunctions_Proxy= +Callouts.H

+ +

v  +CompletionFunctions_BasicPacketInjectionCall= +outs.CPP

+ +

v  +CompletionFunctions_Bas= +icPacketInjectionCallouts.H

+ +

v  +CompletionFunctions_BasicPacketModificationC= +allouts.CPP

+ +

v  +CompletionFunctions_Bas= +icPacketModificationCallouts.H

+ +

v  +CompletionFunctions_BasicStreamInjectionCall= +outs.CPP

+ +

v  +CompletionFunctions_Bas= +icStreamInjectionCallouts.H

+ +

v  +CompletionFunctions_FastPacketInjectionCallo= +uts.CPP

+ +

v  +CompletionFunctions_Fas= +tPacketInjectionCallouts.H

+ +

v  +CompletionFunctions_FastStreamInjectionCallo= +uts.CPP

+ +

v  +CompletionFunctions_Fas= +tStreamInjectionCallouts.H

+ +

v  +CompletionFunctions_Inc= +lude.H

+ +

v  +CompletionFunctions_PendAuthorizationCallout= +s.CPP

+ +

v  +CompletionFunctions_Pen= +dAuthorizationCallouts.H

+ +

v  +CompletionFunctions_ProxyCallouts.CPP

+ +

v  +CompletionFunctions_Pro= +xyCallouts.H

+ +

v  +Framework_Events.CPP

+ +

v  +Framework_Events.H

+ +

v  +Framework_Include.H

+ +

v  +Framework_PowerStates.CPP

+ +

v  +Framework_PowerStates.H= +

+ +

v  +Framework_WFPSamplerCalloutDriver.CPP

+ +

v  +Framework_WFPSamplerCal= +loutDriver.H

+ +

v  +Framework_WFPSamplerCal= +loutDriver.rc

+ +

v  +HelperFunctions_ExposedCallouts.CPP

+ +

v  +HelperFunctions_Exposed= +Callouts.H

+ +

v  +NotifyFunctions_BasicCallouts.CPP

+ +

v  +NotifyFunctions_BasicCa= +llouts.H

+ +

v  +NotifyFunctions_FastCallouts.CPP<= +/p> + +

v  +NotifyFunctions_FastCal= +louts.H

+ +

v  +NotifyFunctions_FlowDelete.CPP

+ +

v  +NotifyFunctions_FlowDel= +ete.H

+ +

v  +NotifyFunctions_Include= +.H

+ +

v  +NotifyFunctions_PendCallouts.CPP

+ +

v  +NotifyFunctions_PendCal= +louts.H

+ +

v  +NotifyFunctions_ProxyCallouts.CPP

+ +

v  +NotifyFunctions_ProxyCa= +llouts.H

+ +

v  +SubscriptionFunctions_BFEState.CPP

+ +

v  +SubscriptionFunctions_B= +FEState.H

+ +

v  +SubscriptionFunctions_I= +nclude.H

+ +

v  +WFPSamplerCalloutDriver= +.InX

+ +

and links with SysLib\WFPSamplerSys.Lib.

+ +

The Framework= +_* files +contain functions for creating and maintaining a filter driver using the +Microsoft Windows Driver Foundation.  +These are relatively safe to ignore if you are only concerned with W= +FP +functionality.

+ +

HelperFunctio= +ns_ExposedCallouts.CPP +contains functions used to register and unregister the various callouts exp= +osed +by this driver.  While the methodol= +ogy of +creating the array of the various callouts is inconsequential,   it does demonstrate proper use of fill= +ing +out the FWPS_CALLOUT object and calling FwpsCalloutReg= +ister +and FwpsCalloutUnregisterByKey.

+ +

The ClassifyFunctions_*are files which contain the var= +ious +FWPS_CALLOUT_CLASSIFY_FN and the work they perform.  The classifyFns are +all laid out the same:

+ +

·         +Cla= +ssify* +functions are the initial classify.  This +function will see if any further processing is needed, and if so, which Tri= +gger +function to call (inline (synchronous) or out of band (asynchronous)).

+ +

·         +Tri= +gger* are +functions which packages the data of the initial classify and either send i= +t to +the appropriate Perform function or to the WorkItemRou= +tine, +DeferredProcedureCall, or = +TDeferredProcedureCall.

+ +

·         +*WorkItemRoutine are fu= +nctions +which finish packaging the necessary data, and queues a WorkItem +for asynchronous processing at PASSIVE_LEVEL.

+ +

·         +*DeferredProcedureCall are +functions which finish packaging the necessary data, and queuesa +DPC for asynchronous processing at PASSIVE_LEVEL.  This is the default out of band method.= +

+ +

·         +*Pe= +rform +are functions which do the actual processing desired by the callout.

+ +

<= +![endif]>

+ +

The CompletionFunctions_* are files which contain the va= +rious +FWPS_INJECT_COMPLETE.

+ +

The NotifyFunctions_* are files which contain the va= +rious +FWPS_CALLOUT_NOTIFY_FN and FWPS_CALLOUT_FLOW_DELETE_NOTIFY_FN.

+ +

The SubscriptionFunctions_* are files which contain the va= +rious +callbacks used by subscriptions.

+ +

WFPSamplerCalloutDriver.InX +is a file used in creation of the = +Inf +used to install the driver.

+ +

<= +![endif]>

+ +

Figure B.   Packet Traversal= +

+ +

WFPSamplerProxyService.Exe

+ +

Usage:

+ +

This is the service which listens for connections to p= +roxy.

+ +

WFPSamplerPr= +oxyService.Exe +-i  will install the service.  While installed, the service is under t= +he +control of the Service Control Manager (as WFPSamplerP= +roxy).  The service is set to autostart.

+ +

WFPSamplerPr= +oxyService.Exe +-u“ will uninstall the service.

+ +

Source:

+ +

The executable is located under P= +roxySvc +and is comprised of the following files:

+ +

v  +Framework_ProxySvc.CPP

+ +

v  +Framework_ProxySvc.H

+ +

v  +Framework_ProxySvc.RC

+ +

v  +Framework_ProxySvc_Msg.MC

+ +

v  +ThreadRoutines_Include.= +H

+ +

v  +ThreadRoutines_TCPListener.CPP

+ +

v  +ThreadRoutines_TCPListe= +ner.H

+ +

v  +ThreadRoutines_UDPListener.CPP

+ +

v  +ThreadRoutines_UDPListe= +ner.H

+ +

The Framework= +_* +files contain functions for creating and maintaining the service such as ServiceMain.  These +are relatively safe to ignore if you are only concerned with WFP functional= +ity.

+ +

The ThreadRoutines_*files contain functions for +maintaining the proxy.  These are +relatively safe to ignore if you are only concerned with WFP functionality.= +

+ +

WFPSampler.Lib

+ +

Usage:

+ +

This is a library of user mode helper functions used +throughout the project.

+ +

Source:

+ +

This library is located under Lib and is comprised of = +the +following files:

+ +

v  +HelperFunctions_FwpmCallout.CPP

+ +

v  +HelperFunctions_FwpmCal= +lout.H

+ +

v  +HelperFunctions_FwpmEngine.CPP

+ +

v  +HelperFunctions_FwpmEng= +ine.H

+ +

v  +HelperFunctions_FwpmFilter.CPP

+ +

v  +HelperFunctions_FwpmFil= +ter.H

+ +

v  +HelperFunctions_FwpmLayer.CPP

+ +

v  +HelperFunctions_FwpmLay= +er.H

+ +

v  +HelperFunctions_FwpmProvider.CPP

+ +

v  +HelperFunctions_FwpmPro= +vider.H

+ +

v  +HelperFunctions_FwpmProviderContext.CPP

+ +

v  +HelperFunctions_FwpmPro= +viderContext.H

+ +

v  +HelperFunctions_FwpmSubLayer.CPP

+ +

v  +HelperFunctions_FwpmSub= +Layer.H

+ +

v  +HelperFunctions_FwpmTransaction.CPP

+ +

v  +HelperFunctions_FwpmTra= +nsaction.H

+ +

v  +HelperFunctions_GUID.CPP

+ +

v  +HelperFunctions_GUID.H<= +/span>

+ +

v  +HelperFunctions_Include= +.H

+ +

v  +HelperFunctions_IPAddress.CPP

+ +

v  +HelperFunctions_IPAddre= +ss.H

+ +

v  +HelperFunctions_Log.CPP

+ +

v  +HelperFunctions_Log.H

+ +

v  +HelperFunctions_MACAddress.CPP

+ +

v  +HelperFunctions_MACAddr= +ess.H

+ +

v  +HelperFunctions_Macros.= +H

+ +

v  +HelperFunctions_Process.CPP

+ +

v  +HelperFunctions_Process= +.H

+ +

v  +HelperFunctions_Registry.CPP

+ +

v  +HelperFunctions_Registr= +y.H

+ +

v  +HelperFunctions_Service.CPP

+ +

v  +HelperFunctions_Service= +.H

+ +

v  +HelperFunctions_SID.CPP

+ +

v  +HelperFunctions_SID.H

+ +

v  +HelperFunctions_Strings.CPP

+ +

v  +HelperFunctions_Strings= +.H

+ +

v  +HelperFunctions_ThreadPools.CPP

+ +

v  +HelperFunctions_ThreadP= +ools.H

+ +

v  +HelperFunctions_ThreadsAndEvents.CPP

+ +

v  +HelperFunctions_Threads= +AndEvents.H

+ +

v  +HelperFunctions_WinSock.CPP

+ +

v  +HelperFunctions_WinSock= +.H

+ +

If you are interested only in the WFP functionality, t= +hen +most attention should be with the HelperFunctions_Fwpm= +* +and HelperFunctions_WinSock.CPP.

+ +

WFPSamplerSys.Lib

+ +

Usage:

+ +

This is a library of kernel mode helper functions used +throughout the project.

+ +

Source:

+ +

This library is located under Sys= +Lib +and is comprised of the following files:

+ +

v  +HelperFunctions_ClassifyData.CPP

+ +

v  +HelperFunctions_Classif= +yData.H

+ +

v  +HelperFunctions_DeferredProcedureCalls.CPP + +

v  +HelperFunctions_Deferre= +dProcedureCalls.H

+ +

v  +HelperFunctions_FlowContext.CPP + +

v  +HelperFunctions_FlowCon= +text.H

+ +

v  +HelperFunctions_FwpObjects.CPP

+ +

v  +HelperFunctions_FwpObje= +cts.H

+ +

v  +HelperFunctions_Headers.CPP

+ +

v  +HelperFunctions_Headers= +.H

+ +

v  +HelperFunctions_ICMPMes= +sages.H

+ +

v  +HelperFunctions_Include= +.H

+ +

v  +HelperFunctions_InjectionData.CPP

+ +

v  +HelperFunctions_Injecti= +onData.H

+ +

v  +HelperFunctions_Macros.= +H

+ +

v  +HelperFunctions_NDIS.CPP

+ +

v  +HelperFunctions_NDIS.H<= +/span>

+ +

v  +HelperFunctions_NetBuffer.CPP

+ +

v  +HelperFunctions_NetBuff= +er.H

+ +

v  +HelperFunctions_NotifyD= +ata.H

+ +

v  +HelperFunctions_PendData.CPP

+ +

v  +HelperFunctions_PendDat= +a.H

+ +

v  +HelperFunctions_RedirectData.CPP

+ +

v  +HelperFunctions_Redirec= +tData.H

+ +

v  +HelperFunctions_WorkItems.CPP

+ +

v  +HelperFunctions_WorkIte= +ms.H

+ +

If you are interested only in the WFP functionality, t= +hen +most attention should be with HelperFunctions_FwpObjects.CPP.

+ +

Miscellaneous

+ +

HCK

+ +

Usage:

+ +

This directory contains an answer and info file that c= +an be +used in conjunction with the Windows Hardware Certification Kit.  To validate that the WFPSampler is full= +y WHCK +compliant, you can install the WFPSampler on a WHCK client machine, and run= + the +WindowsFilteringPlatform_Tests.  When prompted by the test to populate t= +he +info file, copy these files to %WinDir%\System3= +2\WFPLogo.Answer and %WinDir%\System32\WFPLogo.Info.  Exit +out of the WFPLogo.Info that is currently displ= +ayed +(without saving). Press “OK” to have the test continue<= +/span>.

+ +

Source:

+ +

v  +WFPLogo_WFPSampler.Answ= +er

+ +

v  +WFPLogo_WFPSampler.Info= +

+ +

IDL

+ +

Usage:

+ +

This directory contains files used by the MIDL compile= +r to +create the RPC interface between WFPSampler.Exe and WFPSamplerService.Exe + +

Source:

+ +

v  +WFPSamplerRPC.ACF

+ +

v  +WFPSamplerRPC.IDL

+ +

Scripts

+ +

Usage:

+ +

This directory contains scripts used to install and +uninstall the WFPSampler.

+ +

Sources:

+ +

v  +WFPSamplerInstall.cmd

+ +

WFPSamplerIn= +stall.cmd“ will copy the necessary binaries to their appropriate +location, and install each component.

+ +

WFPSamplerIn= +stall.cmd +-r “ will uninstall each component and remov= +e the binaries +from the appropriate location.

+ +

 

+ +
+ + + + + +------=_NextPart_01CEBF89.9B5B26B0 +Content-Location: file:///C:/515CB117/_WFPSampler_Overview_files/themedata.thmx +Content-Transfer-Encoding: base64 +Content-Type: application/vnd.ms-officetheme + +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 +cmVsc1BLBQYAAAAABQAFAF0BAACtCgAAAAA= + +------=_NextPart_01CEBF89.9B5B26B0 +Content-Location: file:///C:/515CB117/_WFPSampler_Overview_files/colorschememapping.xml +Content-Transfer-Encoding: quoted-printable +Content-Type: text/xml + + + +------=_NextPart_01CEBF89.9B5B26B0 +Content-Location: file:///C:/515CB117/_WFPSampler_Overview_files/image001.emz +Content-Transfer-Encoding: base64 +Content-Type: image/x-emz + +H4sIAAAAAAAEC81Zf2xV1R3/vtfX19emaIcdaUaVy2yxLd1WtyLNIu1plQqmrC9SIFOjrwoo29CC +FEn26wJt7Ka4uj1SsLh0Y3EMo7ZIJlm25VqbZXGLMYNtmC1Z5/xDZxa7BBODLuzzOeee29fHe/B8 +bd1O+bzv93t+3e/53O/5nnsvIRH5GmDLBBTClhfKRboBZ/W6dpGQuNeKnEZjxHbwpVskMorKWFhE +hWY2qnfR+EZEMIHUAw6A6ZaH0LESehkQLvP+wmEJH+x7P3AnwL4NKqz7meu6LUtURErRxnKNKgz0 +q5UI3JUYAFdkHChR4tnrXEDhdcL482SZLIXOvvcAnI9tVT5E3FVQ/TKtc95qwL++a3XM425EfQPA +PsQ4UAMUAo7+c8NOKWEsOxYMBfOgq5viQytsv7iBzrnJURIYAMhRTElYQW8EyCeEvMoflJI/H222 +PlnJetsP6oWYChVwLP1lPX0WBR014xpkjX/oEQqF9qL5kyqUcl9y4+5ujGsCQr6E0DYla/V1jTHj +13LFcVZH34Bz23kCSh3AeRyZyTdtOzYfzsmNjRPyz0WcgyD/dcqsv4L14rVp4esvv/dWCxFT4Wgq +x/L4P1JiTAQ+yU3SKh2yVtrkNvyK3KDC7iHIx/RcouOXHJQAmxAFcVkv3bJderCTt8hOUSuvaZF3 +H2h2+1dqqYZu1lLWrtfSuz1h5PntRn55n5Yjf/iOls6iA8auTWoJilvMpfk7rS+EZbngmv39IIt8 +nXW3AT1wlv6+h70HEZRBVqIUSQRr7ob327APd+J3dmVw7IkoZ3DOLNUTaQNaTFsitr3hyKBu8vz+ +o0PGbu3eUXkBNyKS1t87beY7Mv5SK5ssE9nmf9MfTw5YeL+IsUIzP3WWs38189rrdfi29a9ABP+m +/X9dBP9E+ny/cZ03N+82y7OxHUJ7FzAAMMCuAHYBnwLYdvz42dYNG/7YyjbeJ/rSAaQW5kqWBCoz +xVkx6rnHuH5KgnW0w74NEezVBaCsHbYL0BfumeVqOm7qoFehnv5Ilv3DvaDQ2sguyFlci8VnRZYc +vmTEiixE/8vFbDqHXRjTC5DD3UA6h2yjz/QjG4dxtME/Xez8lo8kageAfPmgf2v0zNN8MGBWyx7k +A+fnRzQnkz89qqX32s/mgaMErvcwQI76gXSO2EaO6Fc2jrgO4/N0PKHqovhpQl09+6bFT4nKdM53 +Cs/5KvQnEH0Meb9M64xXe1+gulaPQd+IigaAfTqBcaAXKAQcmYo4Ei9ySgnqU5Fq1DPGkEKCeWD+ +n53pvVgH0Yl1Eb0I4Nmf6eSI94fFSu6MQlN10a/lKpV/9A0454B1wATAGOI86XzTtvPkw3kN5uT9 +og/MT+XYyDEglzN9x6qn2piTJjHuDIAS5KRPwMiUN9frU3o3zrl7ocmv3m7mae3VX6H3pdu8WEs1 +VWv2adic4vJjc4p7t99h+nV8xcjTPUae/5aWIzv7jP3Bd7WUK7+vZSIypOVkz1PGPjli7Bef1hKk +Zzzl88uZJ7F25rs7gD7gPgCUBucO27B2IUfZ8kEP2nLgL5bLORTBXIwdwuq837RZqgHGgM3JLvRd +QHqOqVOXPqNsPKBbljMq8btn54HvLlyvDyDf/UA632zbAvAeZOM7jra5PqMsH/RvDYAS7I/pM8q9 +Z0xzorpOael889fzwFECF38EIEcHgXSO2EaO5uKMGsQ85Dw9fmJKwgr1jQDfsSA+8jtaFcZU+OMZ +w6L4/loDgGHk8Nm8k3G+Js6JYmU++XsTxtPPBLAM2AzcCnB+J+qGgVmdkzWYJzVnM6YVQL75HlsP +3QHIcQSSjVvlFnDUKXdBUgdXe8tVyC1Ds+6D2KTPBLJh1ueE69BaAXAtqaUahvXpKl/nfrP1MUzK +Zwn6dhcAD+CHeabg9RPRnogTnYpMFvZEqNtxRRiXaT4MyfmZwvJBfkoyPCc1yQq8RS7gm32whny4 +wOsCZpheMzmy67Drb0BdE7ACeBRoAwoBtyBe4BY4kdEYQT1eYMeSA6uza4pvrbD9cvH3kRrcgEqA +665T2XN38j8b21785Zf0WX4Os71lZgxyFfnvkAdxYvM91cF7bRzvrA7ezx/AOcG37q1o0ef55Nkb +pePtVfLGwVVa/jvarOWClUYe2K6le2a/sccGjZ183tjv/97Y694xdt37WnotBS2cx71+oZGn8JxA ++294ToBUr3zB2HtuNLLiViOXbNQSpM3h+e6Bj9R72AobNAfnO9t4OcMblJSiXyrxswt1OXIqEfSN +AowTgnFFsM6CdoFvsz/7UbLOjoMaxKM96wdR1wd8fLm6F3no4KxzNddFnlmszCVXc1w1wFwFXtxN +kFVAAngI2AwMAeTTicaLgGLbP599WIN5eC1etx3oABRw6VydBEfDyJNJ7Kxkxlxt56Wf9J/AcuY9 +b8MbnbeHISNAIqqKneho8WShKqaeytVVaOfauTdsPdSc81c9OjtAtrxdL/uQjz6+vE1/9gGHgO8B +iB/xwk7MC0/F3GKCuhOza80nXhgjDSCsCrhc3u4bqGr74OtyE9/BPsS4KQAlyNvlMNrx5tiD76MO +3iS7ka+78eS3BfYWnbsdfG2NI48j8m3m/s0PTeY+5ZgM/O07jRwb1tI9cMzY179u5I5zWqq6Yp1p +1SuLtPQ+U2Vk1+e1dBesNbJxk5Zq2VYjn33Q1C/erWVizX5T/yO8wSGzj9zwuJYIm4wZfCHWaGOs +wtchZnyHtfcDlOrvghOQvJd7Ad7LAwDb7HdBtvFsJn8dQGpJzd+5css4iQHM1Xxf457N9N7Gfqn1 +3F/sa3M8cwiLXY/N4U2oo8/pObxEZfomNCyeXJdTzrDXoQ9WxzqC7xOsZx4Y90H/HRktdgT/w6P/ +qI/OyAl2HnTNOQ8k0XkAMDlTwgp6I5DvOwTH1vjj6bMo8jzu/w1DJ7CqOfgmxHvWxGugWMlo09c1 +1TN+LT8cZ3X0DThn58PAhA/Ok843bTs2nxxEbkoB+sB8dDM2RxNwuXzE/+Pp/+pinY/K0D8CoAT5 +aBGMNmSgh5B9HMy8DdnbPD9uQza6D3WrtaTFHvLFV/XXIXf5v8xXohUR/WbsbbjSyG9cbd6U37nW +2B82GLnffDXySpqN/cQtRp7aoKV79G4j65CD8PVJVfZqOdmyV0t52HxF8u5/VNsjf/e/Iv3gsOn3 +J/NV2ev8ibbVvc+YcXOYo14DV7wPxwAXOAmQTpuj2HYCIKfZctQetH0UvqPoz3iK+DKcIqnbekra +tr/tl2pbHd2COLb5ykXdLiA9X9Up8xxTgTbJ8H8gNrbQLeP3pRpJPPncJe9Eft/zunC9fuAY8Ahw +Eki9F2w74ddluxdxtNcALHZvWj6SqBsA8uWD/q0BUIK9xr3bhj22WhL/PKE58Z7+hZYjn3tpHjhK +4HpcwzFgCEjniG3kiH5l44jrMD6Hwv+L+KmVxNLfzgM3XNchgNw8CaRzw7Zc4qdWzjPsLoqfStSV +AenxQ543AxxEvOwj3a6V42ixJfNzlm1Nl59GRSnA56J2yFRfwsq0oVr/31g5FZQygPp/AdZcng6A +IwAA + +------=_NextPart_01CEBF89.9B5B26B0 +Content-Location: file:///C:/515CB117/_WFPSampler_Overview_files/image002.png +Content-Transfer-Encoding: base64 +Content-Type: image/png + +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 + +------=_NextPart_01CEBF89.9B5B26B0 +Content-Location: file:///C:/515CB117/_WFPSampler_Overview_files/image003.emz +Content-Transfer-Encoding: base64 +Content-Type: image/x-emz + +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 + +------=_NextPart_01CEBF89.9B5B26B0 +Content-Location: file:///C:/515CB117/_WFPSampler_Overview_files/image004.png +Content-Transfer-Encoding: base64 +Content-Type: image/png + +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 + +------=_NextPart_01CEBF89.9B5B26B0 +Content-Location: file:///C:/515CB117/_WFPSampler_Overview_files/image005.emz +Content-Transfer-Encoding: base64 +Content-Type: image/x-emz + +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 + +------=_NextPart_01CEBF89.9B5B26B0 +Content-Location: file:///C:/515CB117/_WFPSampler_Overview_files/image006.png +Content-Transfer-Encoding: base64 +Content-Type: image/png + +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 + +------=_NextPart_01CEBF89.9B5B26B0 +Content-Location: file:///C:/515CB117/_WFPSampler_Overview_files/oledata.mso +Content-Transfer-Encoding: base64 +Content-Type: application/x-mso + +0M8R4KGxGuEAAAAAAAAAAAAAAAAAAAAAPgADAP7/CQAGAAAAAAAAAAAAAAACAAAAAQAAAAAAAAAA +EAAA/v///wAAAAD+////AAAAAAAAAABYAAAA//////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////9 +/////v///wMAAAAEAAAABQAAAAYAAAAHAAAACAAAAAkAAAAKAAAACwAAAAwAAAANAAAADgAAAA8A +AAAQAAAAEQAAABIAAAATAAAAFAAAABUAAAAWAAAAFwAAABgAAAAZAAAAGgAAABsAAAAcAAAAHQAA +AB4AAAD+////IAAAACEAAAAiAAAAIwAAACQAAAAlAAAAJgAAACcAAAAoAAAAKQAAACoAAAArAAAA +LAAAAC0AAAAuAAAALwAAADAAAAAxAAAAMgAAADMAAAA0AAAANQAAADYAAAA3AAAAOAAAADkAAAA6 +AAAAOwAAADwAAAA9AAAAPgAAAD8AAABAAAAAQQAAAEIAAABDAAAARAAAAEUAAABGAAAARwAAAEgA +AABJAAAASgAAAEsAAABMAAAATQAAAE4AAABPAAAAUAAAAFEAAABSAAAAUwAAAFQAAABVAAAAVgAA +AFcAAAD+/////f///1oAAABbAAAAXAAAAF0AAABeAAAAXwAAAGAAAABhAAAAYgAAAGMAAABkAAAA +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 +moTRHiPpL2/NHwaKV3Qga/Cy/j9x6J8Ok8dPnN7RfZt92Mdd0Tb/txArY3M8YQUMGWDjH7IkA7Km +Ghhco2E10F6hORgNK+LBaFgRD0bDingwGlbEg9GwIh6MhhXxYDSsiAfAsDLiVw6QsCAP2QhpjZyw +On/+fD1lYD9se9RAe2UAALAnAtmyAVldD+mkQBhAEaxaRmxYQfprEABZlA7p7QIZuPpxkLACnc8n +Au1QCyCxRwEmYIXcH8FQCr52JI+hhCEYzAZd5VDEUMkAOvE9jQF0oQpIBHQCfj5QBAEsGJgYMGOA +kJ0MDApY7ighF5BqP7XBULcfdP4l6ERHxPmXzvlFBflFiSWZ+aC4hpxnzQc+9xMEcNFyApCzNkFb +qgMS01N1DRn8QOa6leYlg4xS0FcoLk0qyi8tycxLBaYkkJxzfl5KJkgyMUchM68sPxlqKeg4cU8E +X4EHarYckvnFDIgzNWHivonFJalFxWBxEG5gQJz/qcGA+9xS2LVzoLIjPsDTJd7H08/byTHYFawC +dHB5fBhI2NEnxDXIzzHE1c/R1zUYcV6pIwPi2jrkM0cHOwAA7xkyDAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIEAAACCAAAA/v////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +////////////////////////////////AOoAAHic7HwJXFRV+/9z7izMsDMDAwI6A7hgogybO8wA +bpjKoEwuRTAgIooOCuRbmowLuSSyqKmpYe5ZqaXUa6lQWvmmhfam9WuDJAVBYwaBxGXu/5x77+ho +qKjUxz7/9+qX55y7nHvO9z7neZ5zzp17qsKl6s33PH+Fu7YI4IGZFoPQah/iwGzOABSXN9M0bdlN +/2/7R203MXpyzzALSwEGeeY2GCIMMYYthh2GPYYDhiOGE6sC4IIhwZBiuGK4Ycgw3DE8MDpheGJ4 +YXhjdMbogiHHUGD4YPhi+GF0xeiG0R2jB4Y/V6/ZWPbCCMDojdEHIxBDiRGEEYwRghGKEYbRF6Mf +Rn+MARgDMQZhDMYIZ3QbQIWhxojEiMKIxhiCMRRjGMZwjBEYMRgjMZ7GGMXVZwyWsRgajDiMsRjj +MOIxtBjPYIzHmIAxEWMSxrMYz2EkYDyPkYiRhKHDSMZIwZiMkYoxBSMNYypGOsY0jOkYGRgzMGZi +6DEyMWZxdbLgYbexuCQ9ZONnMRSXmw2z4UV4mE2GNcZSFh/nXT0pZn85e3iY9bmGhRXDj5V9jXgk +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 + +------=_NextPart_01CEBF89.9B5B26B0 +Content-Location: file:///C:/515CB117/_WFPSampler_Overview_files/filelist.xml +Content-Transfer-Encoding: quoted-printable +Content-Type: text/xml; charset="utf-8" + + + + + + + + + + + + + + +------=_NextPart_01CEBF89.9B5B26B0-- diff --git a/network/trans/WFPSampler/exe/Framework_RPCClientInterface.cpp b/network/trans/WFPSampler/exe/Framework_RPCClientInterface.cpp new file mode 100644 index 000000000..41e949ce0 --- /dev/null +++ b/network/trans/WFPSampler/exe/Framework_RPCClientInterface.cpp @@ -0,0 +1,435 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// Framework_RPCClientInterface.cpp +// +// Abstract: +// This module contains functions which implement the RPC client interface. +// +// Naming Convention: +// +// +// +// i.e. +// +// +// { +// - Function is likely visible to other modules. +// Prv - Function is private to this module. +// } +// +// { +// RPC - Function pertains to Remote Procedule Call. +// RPCClientInterface - Function pertains to the RPC Client Interface. +// } +// +// { +// Initialize - Function prepares environment for use. +// Terminate - Function cleans up the environment. +// Troubleshoot - Function attempts to provide extra information to diagnose cause +// of failure. +// } +// +// { +// Error - Function acts on errors. +// } +// +// Private Functions: +// PrvRPCTroubleshootError(), +// +// Public Functions: +// RPCClientInterfaceInitialize(), +// RPCClientInterfaceTerminate(), +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "Framework_WFPSampler.h" /// . +#include "WFPSamplerRPC_c.c" /// $(OBJ_PATH)\..\idl\$(O) + +/// Module's Local Structures + +typedef struct RPC_DATA_ +{ + RPC_IF_HANDLE rpcClientInterfaceHandle; + RPC_BINDING_HANDLE bindingHandle; + UINT32 protocolSequence; + RPC_WSTR pProtocolSequence; + RPC_WSTR pEndpoint; + BOOLEAN isBound; +}RPC_DATA, *PRPC_DATA; + +/// Module's Local Variables + +RPC_DATA* pRPCData = 0; + +/// + +extern "C" +{ + /** + @framework_function="MIDL_user_free" + + Purpose: RPC stub routine to allocate memory.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA378651.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA378715.aspx
+ */ + _Must_inspect_result_ + _Ret_maybenull_ _Post_writable_byte_size_(size) + VOID* __RPC_USER MIDL_user_allocate(/* _In_ */ size_t size) + { + BYTE* pBuffer = 0; + + if(size) + { + HLPR_NEW_ARRAY(pBuffer, + BYTE, + size); + } + + return pBuffer; + } + + /** + @framework_function="MIDL_user_free" + + Purpose: RPC stub routine to free allocated memory.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA378651.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA378716.aspx
+ */ + VOID __RPC_USER MIDL_user_free(/* _Inout_ */ _Pre_maybenull_ _Post_invalid_ VOID* pBuffer) + { + HLPR_DELETE_ARRAY(pBuffer); + + return; + } +} + +/** + @private_function="PrvRPCTroubleshootError" + + Purpose: Function to retrieve extended error information about RPC's inner workings.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA378651.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA374351.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA375686.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA375668.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA375664.aspx
+*/ +VOID PrvRPCTroubleshootError() +{ + RPC_STATUS status = RPC_S_OK; + RPC_ERROR_ENUM_HANDLE enumHandle = {0}; + + status = RpcErrorStartEnumeration(&enumHandle); + if(status != RPC_S_OK) + { + if(status != RPC_S_ENTRY_NOT_FOUND) + HlprLogError(L"PrvRPCTroubleshootError : RpcErrorStartEnumeration() [status: %#x]", + status); + + HLPR_BAIL; + } + + for(; + status == RPC_S_OK; + ) + { + RPC_EXTENDED_ERROR_INFO errorInfo = {0}; + + errorInfo.Version = RPC_EEINFO_VERSION; + errorInfo.NumberOfParameters = MaxNumberOfEEInfoParams; + + status = RpcErrorGetNextRecord(&enumHandle, + TRUE, + &errorInfo); + if(status == RPC_S_ENTRY_NOT_FOUND) + { + HlprLogInfo(L"PrvRPCTroubleshootError : RpcErrorGetNextRecord() [status: %#x]", + status); + + HLPR_BAIL; + } + else if(status != RPC_S_OK) + { + HlprLogError(L"PrvRPCTroubleshootError : RpcErrorGetNextRecord() [status: %#x]", + status); + + HLPR_BAIL; + } + else + { + if(errorInfo.ComputerName) + { + HlprLogInfo(L" [ComputerName: %S]", + errorInfo.ComputerName); + + HeapFree(GetProcessHeap(), + 0, + errorInfo.ComputerName); + } + + HlprLogInfo(L" [ProcessID: %d]", + errorInfo.ProcessID); + + HlprLogInfo(L" [SystemTime: %02d/%02d/%04d %02d:%02d:%02d:%03d]", + errorInfo.u.SystemTime.wMonth, + errorInfo.u.SystemTime.wDay, + errorInfo.u.SystemTime.wYear, + errorInfo.u.SystemTime.wHour, + errorInfo.u.SystemTime.wMinute, + errorInfo.u.SystemTime.wSecond, + errorInfo.u.SystemTime.wMilliseconds); + + HlprLogInfo(L" [GeneratingComponent:%d]", + errorInfo.GeneratingComponent); + + HlprLogInfo(L" [Status: %#x]", + errorInfo.Status); + + HlprLogInfo(L" [DetectionLocation: %d]", + errorInfo.DetectionLocation); + + HlprLogInfo(L" [Flags: %#x]", + errorInfo.Flags); + + HlprLogInfo(L" [NumberOfParameters: %d]", + errorInfo.NumberOfParameters); + + for(UINT32 i = 0; + i < (UINT32)errorInfo.NumberOfParameters; + i++) + { + PWSTR pNewLine = L""; + + if(i == ((UINT32)errorInfo.NumberOfParameters - 1)) + pNewLine = L"\n"; + + switch(errorInfo.Parameters[i].ParameterType) + { + case eeptAnsiString: + { + HlprLogInfo(L" [AnsiString: %s]%s", + errorInfo.Parameters[i].u.AnsiString, + pNewLine); + + HeapFree(GetProcessHeap(), + 0, + errorInfo.Parameters[i].u.AnsiString); + + break; + } + case eeptUnicodeString: + { + HlprLogInfo(L" [UnicodeString: %S]%s", + errorInfo.Parameters[i].u.UnicodeString, + pNewLine); + + HeapFree(GetProcessHeap(), + 0, + errorInfo.Parameters[i].u.UnicodeString); + + break; + } + case eeptLongVal: + { + HlprLogInfo(L" [LongVal: %#x]%s", + errorInfo.Parameters[i].u.LVal, + pNewLine); + + break; + } + case eeptShortVal: + { + HlprLogInfo(L" [ShortVal: %#x]%s", + errorInfo.Parameters[i].u.SVal, + pNewLine); + + break; + } + case eeptPointerVal: + { + HlprLogInfo(L" [PointerVal: %#p]%s", + errorInfo.Parameters[i].u.PVal, + pNewLine); + + break; + } + case eeptNone: + { + HlprLogInfo(L" [Truncated]%s", + pNewLine); + + break; + } + default: + HlprLogInfo(L" [ParameterType Invalid: %d]%s", + errorInfo.Parameters[i].ParameterType, + pNewLine); + } + } + } + } + + HLPR_BAIL_LABEL: + + RpcErrorEndEnumeration(&enumHandle); + + return; +} + +/** + @framework_function="RPCClientInterfaceTerminate" + + Purpose: Teardown the RPC client interface by unbinding and freeing the handles.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA378651.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA375613.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA375588.aspx
+*/ +UINT32 RPCClientInterfaceTerminate() +{ + RPC_STATUS status = RPC_S_OK; + + if(pRPCData && + pRPCData->bindingHandle) + { + if(pRPCData->isBound) + { + status = RpcBindingUnbind(pRPCData->bindingHandle); + if(status != RPC_S_OK) + HlprLogError(L"RPCClientInterfaceTerminate : RpcBindingUnbind() [status: %#x]", + status); + else + pRPCData->isBound = FALSE; + } + + status = RpcBindingFree(&(pRPCData->bindingHandle)); + if(status != RPC_S_OK) + HlprLogError(L"RPCClientInterfaceTerminate : RpcBindingFree() [status: %#x]", + status); + } + + HLPR_DELETE(pRPCData); + + return status; +} + +/** + @framework_function="RPCClientInterfaceInitialize" + + Purpose: Initialize the RPC client interface by creating a fast binding handle.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA378651.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA375587.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA375583.aspx
+*/ +UINT32 RPCClientInterfaceInitialize() +{ + RPC_STATUS status = RPC_S_OK; + SID* pLocalSystemSID = 0; + SIZE_T sidSize = 0; + RPC_SECURITY_QOS_V4 securityQoS = {0}; + RPC_BINDING_HANDLE_TEMPLATE_V1 bindingHandleTemplate = {0}; + RPC_BINDING_HANDLE_SECURITY_V1 bindingHandleSecurity = {0}; + RPC_BINDING_HANDLE_OPTIONS_V1 bindingHandleOptions = {0}; + + HLPR_NEW(pRPCData, + RPC_DATA); + HLPR_BAIL_ON_ALLOC_FAILURE(pRPCData, + status); + + status = HlprSIDCreate(&pLocalSystemSID, + &sidSize, + 0, + WinLocalSystemSid); + HLPR_BAIL_ON_FAILURE(status); + + wfpSamplerBindingHandle = 0; + + pRPCData->rpcClientInterfaceHandle = IWFPSampler_v1_0_c_ifspec; /// MIDL generated Client Interface Handle + pRPCData->protocolSequence = RPC_PROTSEQ_LRPC; /// Use Local RPC + + securityQoS.Version = 4; /// Use RPC_SECURITY_QOS_V4 structure + securityQoS.Capabilities = RPC_C_QOS_CAPABILITIES_MUTUAL_AUTH; /// Request mutual authentication from the security provider + securityQoS.IdentityTracking = RPC_C_QOS_IDENTITY_STATIC; /// Security context is created only once + securityQoS.ImpersonationType = RPC_C_IMP_LEVEL_IDENTIFY; /// Allow server to get client's identity and allow it's impersonation + securityQoS.Sid = pLocalSystemSID; /// Security identifier used by Local RPC + securityQoS.EffectiveOnly = TRUE; /// only see enabled privileges + + bindingHandleTemplate.Version = 1; /// Use BINDING_HANDLE_TEMPLATE_V1 structure + bindingHandleTemplate.ProtocolSequence = pRPCData->protocolSequence; /// Use Local RPC + bindingHandleTemplate.StringEndpoint = (PWSTR)g_pEndpoint; /// String representation of our endpoint + + bindingHandleSecurity.Version = 1; /// Use BINDING_HANDLE_SECURITY_V1 structure + bindingHandleSecurity.AuthnLevel = RPC_C_AUTHN_LEVEL_PKT_PRIVACY; /// Authentication level which causes Local RPC to use a secure channel + bindingHandleSecurity.AuthnSvc = RPC_C_AUTHN_WINNT; /// Autherntication service to use + bindingHandleSecurity.SecurityQos = (RPC_SECURITY_QOS*)&securityQoS; /// Security Qos Settings to Use + + bindingHandleOptions.Version = 1; /// Use BINDING_HANDLE_OPTIONS_V1 structure + bindingHandleOptions.Flags = RPC_BHO_NONCAUSAL; /// Execute calls in any order + bindingHandleOptions.ComTimeout = RPC_C_BINDING_DEFAULT_TIMEOUT; /// Use default communication timeout value + + status = RpcBindingCreate(&bindingHandleTemplate, /// Core structure of the binding handle + &bindingHandleSecurity, /// Security options for the binding handle + &bindingHandleOptions, /// Additional options to set on the binding handle + &wfpSamplerBindingHandle); /// Created binding handle + if(status != RPC_S_OK) + { + HlprLogError(L"RPCClientInterfaceInitialize : RpcBindingCreate() [status: %#x]", + status); + + HLPR_BAIL; + } + + pRPCData->bindingHandle = wfpSamplerBindingHandle; + + status = RpcBindingBind(0, /// These RPC calls will be synchronous + pRPCData->bindingHandle, /// Binding handle that will be used to make the RPC call + pRPCData->rpcClientInterfaceHandle); /// Interface handle that will be used to make the RPC call + if(status != RPC_S_OK) + { + HlprLogError(L"RPCClientInterfaceInitialize : RpcBindingBind() [status: %#x]", + status); + + HLPR_BAIL; + } + + HLPR_BAIL_LABEL: + + if(status != RPC_S_OK) + { + HlprLogError(L"[status: %#x]", + status); + + PrvRPCTroubleshootError(); + + RPCClientInterfaceTerminate(); + } + + if(pLocalSystemSID) + HlprSIDDestroy(&pLocalSystemSID); + + return status; +} diff --git a/network/trans/WFPSampler/exe/Framework_RPCClientInterface.h b/network/trans/WFPSampler/exe/Framework_RPCClientInterface.h new file mode 100644 index 000000000..0b77ce436 --- /dev/null +++ b/network/trans/WFPSampler/exe/Framework_RPCClientInterface.h @@ -0,0 +1,28 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// Framework_RPCClientInterface.h +// +// Abstract: +// This module contains prototypes for functions which implement the RPC client interface. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef FRAMEWORK_RPC_CLIENT_INTERFACE_H +#define FRAMEWORK_RPC_CLIENT_INTERFACE_H + +UINT32 RPCClientInterfaceTerminate(); + +UINT32 RPCClientInterfaceInitialize(); + +#endif /// FRAMEWORK_RPC_CLIENT_INTERFACE_H diff --git a/network/trans/WFPSampler/exe/Framework_WFPSampler.cpp b/network/trans/WFPSampler/exe/Framework_WFPSampler.cpp new file mode 100644 index 000000000..9a0771f5a --- /dev/null +++ b/network/trans/WFPSampler/exe/Framework_WFPSampler.cpp @@ -0,0 +1,941 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// Framework_WFPSampler.cpp +// +// Abstract: +// This module contains functions which form the entry point to our client program +// WFPSampler.Exe. +// +// Naming Convention: +// +// +// +// i.e. +// +// +// { +// - Function is likely visible to other modules. +// Prv - Function is private to this module. +// } +// +// { +// FlowControl - Function pertains to how the program execution should behave. +// Scenario - Function pertains to scenarios. +// +// { +// Dispatch - Function selects and invokes necessary code paths for given data. +// Get - Function parses strings for requested value. +// Log - Function writes to the console. +// } +// +// { +// Usage - Function provides generic application usage information. +// } +// +// Private Functions: +// PrvFlowControlGet(), +// PrvLogUsage(), +// PrvScenarioDispatch(), +// PrvScenarioGet(), +// wmain(), + +// Public Functions: +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Add ADVANCED_PACKET_INJECTION, FLOW_ASSOCIATION, and +// PEND_ENDPOINT_CLOSURE scenarios +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "Framework_WFPSampler.h" /// . +#include "Framework_RPCClientInterface.h" /// . + +/// Module's Local Enumerations + +typedef enum WFPSAMPLER_FLOW_CONTROL_ +{ + FLOW_CONTROL_NORMAL = 0, + FLOW_CONTROL_HELP = 1, + FLOW_CONTROL_CLEAN = 2, +}WFPSAMPLER_FLOW_CONTROL; + +/// + +/** + @private_function="PrvFlowControlGet" + + Purpose: Parse the command line parameters for any flow control commands such as:
+ help (-?) (?) (-help)
+
+ Notes:
+
+ MSDN_Ref:
+*/ +WFPSAMPLER_FLOW_CONTROL PrvFlowControlGet(_In_reads_(stringCount) PCWSTR* ppCLPStrings, + _In_ UINT32 stringCount) +{ + ASSERT(ppCLPStrings); + ASSERT(stringCount); + + WFPSAMPLER_FLOW_CONTROL flowControl = FLOW_CONTROL_NORMAL; + + for(UINT32 stringIndex = 0; + stringIndex < stringCount; + stringIndex++) + { + if(HlprStringsAreEqual(ppCLPStrings[stringIndex], + L"-?") || + HlprStringsAreEqual(ppCLPStrings[stringIndex], + L"/?") || + HlprStringsAreEqual(ppCLPStrings[stringIndex], + L"?") || + HlprStringsAreEqual(ppCLPStrings[stringIndex], + L"-help") || + HlprStringsAreEqual(ppCLPStrings[stringIndex], + L"/help")) + { + flowControl = FLOW_CONTROL_HELP; + + break; + } + /// This is used for testing purposes only. Generally we should not be touching any other + /// provider's WFP objects, and is grounds for failure in the Hardware Certification Kit. + else if(HlprStringsAreEqual(ppCLPStrings[stringIndex], + L"-clean") || + HlprStringsAreEqual(ppCLPStrings[stringIndex], + L"/clean")) + { + flowControl = FLOW_CONTROL_CLEAN; + + break; + } + } + + return flowControl; +} + +/** + @private_function="PrvScenarioDispatcher" + + Purpose: Route the provider scenario to the appropriate Scenario implementation.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +UINT32 PrvScenarioDispatcher(_In_ WFPSAMPLER_SCENARIO scenario, + _In_reads_(stringCount) PCWSTR* ppCLPStrings, + _In_ UINT32 stringCount) +{ + ASSERT(scenario < SCENARIO_MAX); + ASSERT(ppCLPStrings); + ASSERT(stringCount); + + UINT32 status = NO_ERROR; + + switch(scenario) + { + + case SCENARIO_ADVANCED_PACKET_INJECTION: + { + status = AdvancedPacketInjectionScenarioExecute(ppCLPStrings, + stringCount); + + break; + } + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + case SCENARIO_APP_CONTAINER: + { + status = AppContainerScenarioExecute(ppCLPStrings, + stringCount); + + break; + } + +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) + + case SCENARIO_BASIC_ACTION_BLOCK: + { + status = BasicActionBlockScenarioExecute(ppCLPStrings, + stringCount); + + break; + } + case SCENARIO_BASIC_ACTION_CONTINUE: + { + status = BasicActionContinueScenarioExecute(ppCLPStrings, + stringCount); + + break; + } + case SCENARIO_BASIC_ACTION_PERMIT: + { + status = BasicActionPermitScenarioExecute(ppCLPStrings, + stringCount); + + break; + } + case SCENARIO_BASIC_ACTION_RANDOM: + { + status = BasicActionRandomScenarioExecute(ppCLPStrings, + stringCount); + + break; + } + case SCENARIO_BASIC_PACKET_EXAMINATION: + { + status = BasicPacketExaminationScenarioExecute(ppCLPStrings, + stringCount); + + break; + } + case SCENARIO_BASIC_PACKET_INJECTION: + { + status = BasicPacketInjectionScenarioExecute(ppCLPStrings, + stringCount); + + break; + } + case SCENARIO_BASIC_PACKET_MODIFICATION: + { + status = BasicPacketModificationScenarioExecute(ppCLPStrings, + stringCount); + + break; + } + case SCENARIO_BASIC_STREAM_INJECTION: + { + status = BasicStreamInjectionScenarioExecute(ppCLPStrings, + stringCount); + + break; + } + case SCENARIO_FAST_PACKET_INJECTION: + { + status = FastPacketInjectionScenarioExecute(ppCLPStrings, + stringCount); + + break; + } + case SCENARIO_FAST_STREAM_INJECTION: + { + status = FastStreamInjectionScenarioExecute(ppCLPStrings, + stringCount); + + break; + } + case SCENARIO_FLOW_ASSOCIATION: + { + status = FlowAssociationScenarioExecute(ppCLPStrings, + stringCount); + + break; + } + case SCENARIO_PEND_AUTHORIZATION: + { + status = PendAuthorizationScenarioExecute(ppCLPStrings, + stringCount); + + break; + } + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + case SCENARIO_PEND_ENDPOINT_CLOSURE: + { + status = PendEndpointClosureScenarioExecute(ppCLPStrings, + stringCount); + + break; + } + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + case SCENARIO_PROXY: + { + status = ProxyScenarioExecute(ppCLPStrings, + stringCount); + + break; + } + default: + { + status = ERROR_NOT_SUPPORTED; + + break; + } + } + + return status; +} + +/** + @private_function="PrvScenarioGet" + + Purpose: Parse the command line parameters for the scenario (-s ).
+
+ Notes:
+
+ MSDN_Ref: N/A
+*/ +WFPSAMPLER_SCENARIO PrvScenarioGet(_In_reads_(stringCount) PCWSTR* ppCLPStrings, + _In_ UINT32 stringCount, + _In_ WFPSAMPLER_FLOW_CONTROL flowControl) +{ + ASSERT(ppCLPStrings); + ASSERT(stringCount); + + WFPSAMPLER_SCENARIO scenario = SCENARIO_UNDEFINED; + + for(UINT32 stringIndex = 0; + (stringIndex + 1) < stringCount; + stringIndex++) + { + if(HlprStringsAreEqual(ppCLPStrings[stringIndex], + L"-s") || + HlprStringsAreEqual(ppCLPStrings[stringIndex], + L"/s")) + { + PCWSTR pScenarioString = ppCLPStrings[stringIndex + 1]; + + if(pScenarioString) + { + if(HlprStringsAreEqual(pScenarioString, + L"ADVANCED_PACKET_INJECTION")) + scenario = SCENARIO_ADVANCED_PACKET_INJECTION; + else if(HlprStringsAreEqual(pScenarioString, + L"APP_CONTAINER")) + scenario = SCENARIO_APP_CONTAINER; + else if(HlprStringsAreEqual(pScenarioString, + L"BASIC_ACTION_BLOCK")) + scenario = SCENARIO_BASIC_ACTION_BLOCK; + else if(HlprStringsAreEqual(pScenarioString, + L"BASIC_ACTION_CONTINUE")) + scenario = SCENARIO_BASIC_ACTION_CONTINUE; + else if(HlprStringsAreEqual(pScenarioString, + L"BASIC_ACTION_PERMIT")) + scenario = SCENARIO_BASIC_ACTION_PERMIT; + else if(HlprStringsAreEqual(pScenarioString, + L"BASIC_ACTION_RANDOM")) + scenario = SCENARIO_BASIC_ACTION_RANDOM; + else if(HlprStringsAreEqual(pScenarioString, + L"BASIC_PACKET_EXAMINATION")) + scenario = SCENARIO_BASIC_PACKET_EXAMINATION; + else if(HlprStringsAreEqual(pScenarioString, + L"BASIC_PACKET_INJECTION")) + scenario = SCENARIO_BASIC_PACKET_INJECTION; + else if(HlprStringsAreEqual(pScenarioString, + L"BASIC_PACKET_MODIFICATION")) + scenario = SCENARIO_BASIC_PACKET_MODIFICATION; + else if(HlprStringsAreEqual(pScenarioString, + L"BASIC_STREAM_INJECTION")) + scenario = SCENARIO_BASIC_STREAM_INJECTION; + else if(HlprStringsAreEqual(pScenarioString, + L"FAST_PACKET_INJECTION")) + scenario = SCENARIO_FAST_PACKET_INJECTION; + else if(HlprStringsAreEqual(pScenarioString, + L"FAST_STREAM_INJECTION")) + scenario = SCENARIO_FAST_STREAM_INJECTION; + else if(HlprStringsAreEqual(pScenarioString, + L"FLOW_ASSOCIATION")) + scenario = SCENARIO_FLOW_ASSOCIATION; + else if(HlprStringsAreEqual(pScenarioString, + L"PEND_AUTHORIZATION")) + scenario = SCENARIO_PEND_AUTHORIZATION; + else if(HlprStringsAreEqual(pScenarioString, + L"PEND_ENDPOINT_CLOSURE")) + scenario = SCENARIO_PEND_ENDPOINT_CLOSURE; + else if(HlprStringsAreEqual(pScenarioString, + L"PROXY")) + scenario = SCENARIO_PROXY; + } + + if(scenario != SCENARIO_UNDEFINED) + break; + } + } + + if(scenario == SCENARIO_UNDEFINED && + flowControl == FLOW_CONTROL_NORMAL) + HlprLogError(L"Invalid Scenario defined"); + + return scenario; +} + +/** + @private_function="PrvCleanPolicy" + + Purpose: Remove all WFP objects based on the passed in scope.
+ default - cleanup all WFPSampler objects except WFPSAMPLER_SUBLAYER and + WFPSAMPLER_PROVIDER.
+ firewall - cleanup all WFP objects from the non-usermode layers except those + that are built-in, the WFPSAMPLER_SUBLAYER, and the + WFPSAMPLER_PROVIDER.
+ all - cleanup all WFP objects except those that are built-in, the + WFPSAMPLER_SUBLAYER, and the WFPSAMPLER_PROVIDER.
+
+ Notes: All and firewall are meant for testing purposes only. Doing this in a commercial + product is grounds for failure in the Microsoft Hardware Certification Kit.
+
+ MSDN_Ref:
+*/ +UINT32 PrvCleanPolicy(_In_reads_(stringCount) PCWSTR* ppCLPStrings, + _In_ UINT32 stringCount) +{ + ASSERT(ppCLPStrings); + ASSERT(stringCount); + + UINT32 status = NO_ERROR; + BOOLEAN cleanFirewallOnly = FALSE; + BOOLEAN cleanWFPSamplersOnly = FALSE; + HANDLE engineHandle = 0; + HANDLE enumHandle = 0; + UINT32 numEntries = 0; + FWPM_CALLOUT** ppCallouts = 0; + FWPM_FILTER** ppFilters = 0; + FWPM_PROVIDER** ppProviders = 0; + FWPM_PROVIDER_CONTEXT** ppProviderContexts = 0; + FWPM_SUBLAYER** ppSubLayers = 0; + FWPM_FILTER_ENUM_TEMPLATE* pFilterEnumTemplate = 0; + FWPM_CALLOUT_ENUM_TEMPLATE* pCalloutEnumTemplate = 0; + FWPM_PROVIDER_CONTEXT_ENUM_TEMPLATE* pProviderContextEnumTemplate = 0; + + for(UINT32 stringIndex = 0; + stringIndex < stringCount; + stringIndex++) + { + if(HlprStringsAreEqual(ppCLPStrings[stringIndex], + L"-clean") || + HlprStringsAreEqual(ppCLPStrings[stringIndex], + L"/clean")) + { + PCWSTR pString = 0; + + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + { + if(HlprStringsAreEqual(pString, + L"all")) + break; + else if(HlprStringsAreEqual(pString, + L"firewall")) + cleanFirewallOnly = TRUE; + else + cleanWFPSamplersOnly = TRUE; + } + + break; + } + } + + status = HlprFwpmEngineOpen(&engineHandle); + HLPR_BAIL_ON_FAILURE(status); + + if(cleanWFPSamplersOnly) + { + HLPR_NEW(pFilterEnumTemplate, + FWPM_FILTER_ENUM_TEMPLATE); + HLPR_BAIL_ON_ALLOC_FAILURE(pFilterEnumTemplate, + status); + + HLPR_NEW(pCalloutEnumTemplate, + FWPM_CALLOUT_ENUM_TEMPLATE); + HLPR_BAIL_ON_ALLOC_FAILURE(pCalloutEnumTemplate, + status); + + HLPR_NEW(pProviderContextEnumTemplate, + FWPM_PROVIDER_CONTEXT_ENUM_TEMPLATE); + HLPR_BAIL_ON_ALLOC_FAILURE(pProviderContextEnumTemplate, + status); + + pFilterEnumTemplate->providerKey = (GUID*)&WFPSAMPLER_PROVIDER; + pFilterEnumTemplate->enumType = FWP_FILTER_ENUM_FULLY_CONTAINED; + pFilterEnumTemplate->flags = FWP_FILTER_ENUM_FLAG_INCLUDE_BOOTTIME | + FWP_FILTER_ENUM_FLAG_INCLUDE_DISABLED; + pFilterEnumTemplate->actionMask = 0xFFFFFFFF; + + pCalloutEnumTemplate->providerKey = (GUID*)&WFPSAMPLER_PROVIDER; + + pProviderContextEnumTemplate->providerKey = (GUID*)&WFPSAMPLER_PROVIDER; + pProviderContextEnumTemplate->providerContextType = FWPM_GENERAL_CONTEXT; + + /// enumerate and flush filters + for(UINT32 layerIndex = 0; + layerIndex < RTL_NUMBER_OF(ppLayerKeyArray); + layerIndex++) + { + pFilterEnumTemplate->layerKey = *(ppLayerKeyArray[layerIndex]); + + status = HlprFwpmFilterCreateEnumHandle(engineHandle, + pFilterEnumTemplate, + &enumHandle); + HLPR_BAIL_ON_FAILURE_2(status); + + status = HlprFwpmFilterEnum(engineHandle, + enumHandle, + 0xFFFFFFFF, + &ppFilters, + &numEntries); + if(status == NO_ERROR && + ppFilters && + numEntries) + { + for(UINT32 filterIndex = 0; + filterIndex < numEntries; + filterIndex++) + { + if(cleanFirewallOnly && + HlprFwpmLayerIsUserMode(&(ppFilters[filterIndex]->layerKey))) + continue; + + HlprFwpmFilterDeleteByKey(engineHandle, + &(ppFilters[filterIndex]->filterKey)); + } + + numEntries = 0; + } + + HLPR_BAIL_LABEL_2: + + HlprFwpmFilterDestroyEnumHandle(engineHandle, + &enumHandle); + } + } + else + { + status = HlprFwpmFilterCreateEnumHandle(engineHandle, + pFilterEnumTemplate, + &enumHandle); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmFilterEnum(engineHandle, + enumHandle, + 0xFFFFFFFF, + &ppFilters, + &numEntries); + if(status == NO_ERROR && + ppFilters && + numEntries) + { + for(UINT32 filterIndex = 0; + filterIndex < numEntries; + filterIndex++) + { + if(cleanFirewallOnly && + HlprFwpmLayerIsUserMode(&(ppFilters[filterIndex]->layerKey))) + continue; + + HlprFwpmFilterDeleteByKey(engineHandle, + &(ppFilters[filterIndex]->filterKey)); + } + + numEntries = 0; + } + + HlprFwpmFilterDestroyEnumHandle(engineHandle, + &enumHandle); + } + + /// Enumerate and flush all Callouts + status = HlprFwpmCalloutCreateEnumHandle(engineHandle, + pCalloutEnumTemplate, + &enumHandle); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmCalloutEnum(engineHandle, + enumHandle, + 0xFFFFFFFF, + &ppCallouts, + &numEntries); + if(status == NO_ERROR && + ppCallouts && + numEntries) + { + for(UINT32 calloutIndex = 0; + calloutIndex < numEntries; + calloutIndex++) + { + if(cleanFirewallOnly && + HlprFwpmLayerIsUserMode(&(ppCallouts[calloutIndex]->applicableLayer))) + continue; + + HlprFwpmCalloutDeleteByKey(engineHandle, + &(ppCallouts[calloutIndex]->calloutKey)); + } + + numEntries = 0; + } + + HlprFwpmCalloutDestroyEnumHandle(engineHandle, + &enumHandle); + + if(!cleanFirewallOnly) + { + /// Enumerate and flush all provider contexts + status = HlprFwpmProviderContextCreateEnumHandle(engineHandle, + pProviderContextEnumTemplate, + &enumHandle); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmProviderContextEnum(engineHandle, + enumHandle, + 0xFFFFFFFF, + &ppProviderContexts, + &numEntries); + if(status == NO_ERROR && + ppProviderContexts && + numEntries) + { + for(UINT32 providerContextIndex = 0; + providerContextIndex < numEntries; + providerContextIndex++) + { + HlprFwpmProviderContextDeleteByKey(engineHandle, + &(ppProviderContexts[providerContextIndex]->providerContextKey)); + } + + numEntries = 0; + } + + HlprFwpmProviderContextDestroyEnumHandle(engineHandle, + &enumHandle); + } + + if(!cleanWFPSamplersOnly) + { + /// Enumerate and flush all sublayers not owned by WFPSampler + status = HlprFwpmSubLayerCreateEnumHandle(engineHandle, + 0, + &enumHandle); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmSubLayerEnum(engineHandle, + enumHandle, + 0xFFFFFFFF, + &ppSubLayers, + &numEntries); + if(status == NO_ERROR && + ppSubLayers && + numEntries) + { + for(UINT32 subLayerIndex = 0; + subLayerIndex < numEntries; + subLayerIndex++) + { + if(!HlprGUIDsAreEqual(ppSubLayers[subLayerIndex]->providerKey, + &WFPSAMPLER_PROVIDER)) + HlprFwpmSubLayerDeleteByKey(engineHandle, + &(ppSubLayers[subLayerIndex]->subLayerKey)); + } + + numEntries = 0; + } + + HlprFwpmSubLayerDestroyEnumHandle(engineHandle, + &enumHandle); + + /// Enumerate and flush all providers not owned by WFPSampler + status = HlprFwpmProviderCreateEnumHandle(engineHandle, + 0, + &enumHandle); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmProviderEnum(engineHandle, + enumHandle, + 0xFFFFFFFF, + &ppProviders, + &numEntries); + if(status == NO_ERROR && + ppProviders && + numEntries) + { + for(UINT32 providerIndex = 0; + providerIndex < numEntries; + providerIndex++) + { + if(!HlprGUIDsAreEqual(&(ppProviders[providerIndex]->providerKey), + &WFPSAMPLER_PROVIDER)) + HlprFwpmProviderDeleteByKey(engineHandle, + &(ppProviders[providerIndex]->providerKey)); + } + + numEntries = 0; + } + + HlprFwpmProviderDestroyEnumHandle(engineHandle, + &enumHandle); + } + + HLPR_BAIL_LABEL: + + if(engineHandle) + HlprFwpmEngineClose(&engineHandle); + + HLPR_DELETE(pProviderContextEnumTemplate); + + HLPR_DELETE(pCalloutEnumTemplate); + + HLPR_DELETE(pFilterEnumTemplate); + + return status; +} + +/** + @private_function="PrvLogUsage" + + Purpose: Log usage information to the console.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +VOID PrvLogUsage(_In_ UINT32 scenario) +{ + wprintf(L"\n\t WFPSampler.Exe \n"); + wprintf(L"\n\t The syntax of this command is: \n\n"); + + if(scenario != SCENARIO_UNDEFINED && + scenario < SCENARIO_MAX) + { + switch(scenario) + { + case SCENARIO_ADVANCED_PACKET_INJECTION: + { + AdvancedPacketInjectionScenarioLogHelp(); + + break; + } + case SCENARIO_APP_CONTAINER: + { + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + AppContainerScenarioLogHelp(); + +#else + + wprintf(L"\n\t\t\t APP_CONTAINER scenario is not supported on this version of Windows \n"); + +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) + + break; + } + case SCENARIO_BASIC_ACTION_BLOCK: + case SCENARIO_BASIC_ACTION_CONTINUE: + case SCENARIO_BASIC_ACTION_PERMIT: + case SCENARIO_BASIC_ACTION_RANDOM: + { + BasicActionScenarioLogHelp(scenario); + + break; + } + case SCENARIO_BASIC_PACKET_EXAMINATION: + { + BasicPacketExaminationScenarioLogHelp(); + + break; + } + case SCENARIO_BASIC_PACKET_INJECTION: + { + BasicPacketInjectionScenarioLogHelp(); + + break; + } + case SCENARIO_BASIC_PACKET_MODIFICATION: + { + BasicPacketModificationScenarioLogHelp(); + + break; + } + case SCENARIO_BASIC_STREAM_INJECTION: + { + BasicStreamInjectionScenarioLogHelp(); + + break; + } + case SCENARIO_FAST_PACKET_INJECTION: + { + FastPacketInjectionScenarioLogHelp(); + + break; + } + case SCENARIO_FAST_STREAM_INJECTION: + { + FastStreamInjectionScenarioLogHelp(); + + break; + } + case SCENARIO_FLOW_ASSOCIATION: + { + FlowAssociationScenarioLogHelp(); + + break; + } + case SCENARIO_PEND_AUTHORIZATION: + { + PendAuthorizationScenarioLogHelp(); + + break; + } + case SCENARIO_PEND_ENDPOINT_CLOSURE: + { + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + PendEndpointClosureScenarioLogHelp(); + +#else + + wprintf(L"\n\t\t\t PEND_ENDPOINT_CLOSURE scenario is not supported on this version of Windows \n"); + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + break; + } + case SCENARIO_PROXY: + { + ProxyScenarioLogHelp(); + + break; + } + } + } + else + { + wprintf(L"\n\t\t -? \t Receive usage information."); + wprintf(L"\n\t\t -clean \t Remove groups of WFP objects."); + wprintf(L"\n\t\t \t default \t Removes all of WFPSampler's objects except its Provider and SubLayer. 3rd party policy is preserved. [Optional]"); + wprintf(L"\n\t\t \t firewall \t Removes all of WFP's kernel-mode objects except built-in and WFPSampler's Provider and SubLayer. IPsec Policy will be preserved. [Optional]"); + wprintf(L"\n\t\t \t all \t Removes all WFP objects except built-in and WFPSampler's Provider and SubLayer. No policy is preserved. [Optional]"); + wprintf(L"\n\t\t -s \t Specify one of the following scenarios."); + wprintf(L"\n\t\t\t\t ADVANCED_PACKET_INJECTION"); + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + wprintf(L"\n\t\t\t\t APP_CONTAINER"); + +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) + + wprintf(L"\n\t\t\t\t BASIC_ACTION_BLOCK"); + wprintf(L"\n\t\t\t\t BASIC_ACTION_CONTINUE"); + wprintf(L"\n\t\t\t\t BASIC_ACTION_PERMIT"); + wprintf(L"\n\t\t\t\t BASIC_ACTION_RANDOM"); + wprintf(L"\n\t\t\t\t BASIC_PACKET_EXAMINATION"); + wprintf(L"\n\t\t\t\t BASIC_PACKET_INJECTION"); + wprintf(L"\n\t\t\t\t BASIC_PACKET_MODIFICATION"); + wprintf(L"\n\t\t\t\t BASIC_STREAM_INJECTION"); + wprintf(L"\n\t\t\t\t FAST_PACKET_INJECTION"); + wprintf(L"\n\t\t\t\t FAST_STREAM_INJECTION"); + wprintf(L"\n\t\t\t\t FLOW_ASSOCIATION"); + wprintf(L"\n\t\t\t\t PEND_AUTHORIZATION"); + wprintf(L"\n\t\t\t\t PEND_ENDPOINT_CLOSURE"); + wprintf(L"\n\t\t\t\t PROXY"); + wprintf(L"\n"); + wprintf(L"\n\t Add -? for a scenario's context sensitive help"); + wprintf(L"\n"); + wprintf(L"\n\t\t i.e."); + wprintf(L"\n\t\t\t WFPSampler.exe -s BASIC_ACTION_BLOCK -?"); + wprintf(L"\n"); + } + + return; +} + +/** + @framework_function="wmain" + + Purpose: Entry point for WFPSampler.Exe. Accepts multiple parameters which are parsed and + dispatched to the rest of the program.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA299386.aspx
+*/ +int __cdecl wmain(_In_ const int argumentCount, + _In_reads_(argumentCount) PCWSTR pArguments[]) +{ + ASSERT(argumentCount); + ASSERT(pArguments); + + UINT32 status = NO_ERROR; + + /// First argument is the executable's name, WFPSampler.exe, + /// so start with next argument + if(argumentCount > 1) + { + PCWSTR* ppCommandLineParameterStrings = (PCWSTR*)&(pArguments[1]); + UINT32 stringCount = argumentCount - 1; + WFPSAMPLER_FLOW_CONTROL flowControl = PrvFlowControlGet(ppCommandLineParameterStrings, + stringCount); + WFPSAMPLER_SCENARIO scenario = PrvScenarioGet(ppCommandLineParameterStrings, + stringCount, + flowControl); + + if(flowControl == FLOW_CONTROL_CLEAN) + { + PrvCleanPolicy(ppCommandLineParameterStrings, + stringCount); + + if(scenario == SCENARIO_UNDEFINED) + HLPR_BAIL; + else + flowControl = FLOW_CONTROL_NORMAL; + } + + if(flowControl == FLOW_CONTROL_NORMAL) + { + if(scenario == SCENARIO_UNDEFINED || + scenario > SCENARIO_MAX) + { + status = ERROR_NOT_FOUND; + + HlprLogError(L"wmain : PrvScenarioGet() [status: %#x][scenario: %d]", + status, + scenario); + + HLPR_BAIL; + } + } + else if(flowControl == FLOW_CONTROL_HELP) + { + PrvLogUsage(scenario); + + HLPR_BAIL; + } + + status = HlprServiceStart(g_pServiceName); + HLPR_BAIL_ON_FAILURE(status); + + status = RPCClientInterfaceInitialize(); + HLPR_BAIL_ON_FAILURE(status); + + status = PrvScenarioDispatcher(scenario, + ppCommandLineParameterStrings, + stringCount); + HLPR_BAIL_ON_FAILURE(status); + } + else + { + status = ERROR_INVALID_PARAMETER; + + HlprLogError(L"wmain() [status: %#x]", + status); + } + + HLPR_BAIL_LABEL: + + RPCClientInterfaceTerminate(); + + return (int)status; +} diff --git a/network/trans/WFPSampler/exe/Framework_WFPSampler.h b/network/trans/WFPSampler/exe/Framework_WFPSampler.h new file mode 100644 index 000000000..76b74f048 --- /dev/null +++ b/network/trans/WFPSampler/exe/Framework_WFPSampler.h @@ -0,0 +1,44 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// Framework_WFPSampler.h +// +// Abstract: +// This module contains include headers for central area of exportation +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef FRAMEWORK_WFP_SAMPLER_H +#define FRAMEWORK_WFP_SAMPLER_H + +#include /// Include\UM +#include /// Include\UM +#include /// Inc\CRT +#include /// Include\UM +#include /// Include\UM +#include /// Include\Shared +#include /// Include\UM +#include /// Include\UM +#include /// Include\Shared +#include /// Include\Shared +#include /// Include\Shared + +#include "WFPSamplerRPC.h" /// $(OBJ_PATH)\..\idl\$(O) +#include "Identifiers.h" /// ..\inc +#include "WFPArrays.h" /// ..\inc +#include "ScenarioData.h" /// ..\inc +#include "HelperFunctions_Include.h" /// ..\lib +#include "HelperFunctions_CommandLine.h" /// . +#include "Scenarios_Include.h" /// . + +#endif /// FRAMEWORK_WFP_SAMPLER_H \ No newline at end of file diff --git a/network/trans/WFPSampler/exe/Framework_WFPSampler.rc b/network/trans/WFPSampler/exe/Framework_WFPSampler.rc new file mode 100644 index 000000000..0cd0598b7 --- /dev/null +++ b/network/trans/WFPSampler/exe/Framework_WFPSampler.rc @@ -0,0 +1,35 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// Framework_WFPSampler.rc +// +// Abstract: +// Internal resource file for WFPSampler (WFPSampler.exe) +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef FRAMEWORK_WFP_SAMPLER_RESOURCES +#define FRAMEWORK_WFP_SAMPLER_RESOURCES + +#define VER_FILETYPE VFT_APP +#define VER_FILESUBTYPE VFT2_UNKNOWN +#define VER_FILEDESCRIPTION_STR "WFP Sampler" +#define VER_INTERNALNAME_STR "WFPSampler.Exe" +#define VER_ORIGINALFILENAME_STR "WFPSampler.Exe" +#define VER_PRODUCT_VERSION_STR "1.0.0.1" + +#include /// Include\UM +#include /// Include\Shared +#include /// Include\Shared + +#endif /// FRAMEWORK_WFP_SAMPLER_RESOURCES diff --git a/network/trans/WFPSampler/exe/HelperFunctions_CommandLine.cpp b/network/trans/WFPSampler/exe/HelperFunctions_CommandLine.cpp new file mode 100644 index 000000000..0223cc85f --- /dev/null +++ b/network/trans/WFPSampler/exe/HelperFunctions_CommandLine.cpp @@ -0,0 +1,6790 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_CommandLine.cpp +// +// Abstract: +// This module contains functions which assist in parsing informatin from the command prompt. +// +// Naming Convention: +// +// +// +// i.e. +// +// +// { +// - Function is likely visible to other modules. +// Prv - Function is private to this module. +// } +// +// { +// Hlpr - Function is from HelperFunctions_* Modules. +// } +// +// { +// CommandLine - Function acts on the arguments passed to the command +// line. +// DataLinkAddressType - Function pertains to DL_ADDRESS_TYPE values. +// EtherType - Function pertains to the Ethernet types. +// FwpConditionFlag - Function pertains to FWP_CONDITION_FLAG_* values. +// FwpConditionL2Flag - Function pertains to FWP_CONDITION_L2* values. +// FwpConditionReauthorizeReason - Function pertains to FWP_CONDITION_L2* values. +// FwpDirection - Function pertains to FWP_DIRECTION values. +// InterfaceType - Function pertains to interface types. +// IPAddressType - Function pertains to NL_ADDRESS_TYPE values. +// NDISMediumType - Function pertains to NDIS_MEDIUM values. +// NDISPhysicalMediumType - Function pertains to NDIS_PHYSICAL_MEDIUM values. +// ProfileID - Function pertains to NLM_NETWORK_CATEGORY values. +// Protocol = Function pertains to IP protocol values. +// TunnelType - Function pertains to TUNNEL_TYPE values. +// VSwitchNetworkType - Function pertains to FWP_VSWITCH_NETWORK_TYPE values. +// VSwitchNICType - Function pertains to NDIS_SWITCH_NIC_TYPE values. +// } +// +// { +// Parse - Function pulls data into the required format from the provided +// data. +// } +// +// { +// +// } +// +// Private Functions: +// PrvHlprCommandLineStringToFwpMatchType(), +// PrvHlprDataLinkAddressTypeParse(), +// PrvHlprEtherTypeParse(), +// PrvHlprFwpConditionFlagParse(), +// PrvHlprFwpConditionL2FlagParse(), +// PrvHlprFwpConditionReauthorizeReasonParse(), +// PrvHlprFwpDirectionParse(), +// PrvHlprInterfaceTypeParse(), +// PrvHlprIPAddressTypeParse(), +// PrvHlprNDISMediumTypeParse(), +// PrvHlprNDISPhysicalMediumTypeParse(), +// PrvHlprProfileIDParse(), +// PrvHlprProtocolParse(), +// PrvHlprTunnelTypeParse(), +// PrvHlprVSwitchNetworkTypeParse(), +// PrvHlprVSwitchNICTypeParse(), +// +// Public Functions: +// HlprCommandLineParseForBootTime(), +// HlprCommandLineParseForCalloutUse(), +// HlprCommandLineParseForLayerKey(), +// HlprCommandLineParseForVolatility(), +// HlprCommandLineParseForFilterConditions(), +// HlprCommandLineParseForFilterInfo() +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Add support for specifying a different sublayer +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "HelperFunctions_Include.h" /// . +#include +#include +#include + +#if(NTDDI_VERSION >= NTDDI_WIN8) + +/** + @private_function="PrvHlprCreateAppContainerSecurityDescriptor" + + Purpose: Create a SECURITY_DESCRIPTOR for use with AppContainer filtering.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_Success_(return != 0) +PSECURITY_DESCRIPTOR PrvHlprCreateAppContainerSecurityDescriptor() +{ + PSECURITY_DESCRIPTOR pSelfRelativeSecurityDescriptor = 0; + UINT32 status = NO_ERROR; + SID_IDENTIFIER_AUTHORITY appPackageAuthority = SECURITY_APP_PACKAGE_AUTHORITY; + PSID pAppContainerSID = 0; + PSECURITY_DESCRIPTOR pAbsoluteSecurityDescriptor = 0; + PACL pACL = 0; + UINT32 aclLength = sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE) + (GetLengthSid(pAppContainerSID) - sizeof(UINT32)); + UINT32 securityDescriptorLength = sizeof(SECURITY_DESCRIPTOR); + + HLPR_NEW_CASTED_ARRAY(pAbsoluteSecurityDescriptor, + SECURITY_DESCRIPTOR, + BYTE, + securityDescriptorLength); + HLPR_BAIL_ON_ALLOC_FAILURE(pAbsoluteSecurityDescriptor, + status); + + HLPR_NEW_CASTED_ARRAY(pACL, + ACL, + BYTE, + aclLength); + HLPR_BAIL_ON_ALLOC_FAILURE(pACL, + status); + + if(AllocateAndInitializeSid(&appPackageAuthority, + SECURITY_BUILTIN_APP_PACKAGE_RID_COUNT, + SECURITY_APP_PACKAGE_BASE_RID, + SECURITY_BUILTIN_PACKAGE_ANY_PACKAGE, + 0, + 0, + 0, + 0, + 0, + 0, + &pAppContainerSID) == 0 || + pAppContainerSID == 0) + { + status = GetLastError(); + + HlprLogInfo(L"PrvHlprCreateAppContainerSecurityDescriptor : AllocateAndInitializeSid() [status: %#x][pAppContainerSID: %#p]", + status, + pAppContainerSID); + + HLPR_BAIL; + } + + if(InitializeAcl(pACL, + aclLength, + ACL_REVISION) == 0) + { + status = GetLastError(); + + HlprLogInfo(L"PrvHlprCreateAppContainerSecurityDescriptor : InitializeACL() [status: %#x]", + status); + + HLPR_BAIL; + } + + if(AddAccessAllowedAce(pACL, + ACL_REVISION, + READ_CONTROL | FWP_ACTRL_MATCH_FILTER, + pAppContainerSID) == 0) + { + status = GetLastError(); + + HlprLogInfo(L"PrvHlprCreateAppContainerSecurityDescriptor : AddAccessAllowedAce() [status: %#x]", + status); + + HLPR_BAIL; + } + + if(InitializeSecurityDescriptor(pAbsoluteSecurityDescriptor, + SECURITY_DESCRIPTOR_REVISION) == 0) + { + status = GetLastError(); + + HlprLogInfo(L"PrvHlprCreateAppContainerSecurityDescriptor : InitializeSecurityDescriptor() [status: %#x]", + status); + + HLPR_BAIL; + } + + if(SetSecurityDescriptorDacl(pAbsoluteSecurityDescriptor, + TRUE, + pACL, + FALSE) == 0) + { + status = GetLastError(); + + HlprLogInfo(L"PrvHlprCreateAppContainerSecurityDescriptor : SetSecurityDescriptorGroup() [status: %#x]", + status); + + HLPR_BAIL; + } + + if(MakeSelfRelativeSD(pAbsoluteSecurityDescriptor, + pSelfRelativeSecurityDescriptor, + (LPDWORD)&securityDescriptorLength) == 0) + { + status = GetLastError(); + + if(status != ERROR_INSUFFICIENT_BUFFER || + securityDescriptorLength == 0) + { + HlprLogInfo(L"PrvHlprCreateAppContainerSecurityDescriptor : MakeSelfRelativeSD() [status: %#x]", + status); + + HLPR_BAIL; + } + else + status = NO_ERROR; + } + + HLPR_NEW_CASTED_ARRAY(pSelfRelativeSecurityDescriptor, + SECURITY_DESCRIPTOR, + BYTE, + securityDescriptorLength); + HLPR_BAIL_ON_ALLOC_FAILURE(pSelfRelativeSecurityDescriptor, + status); + + if(MakeSelfRelativeSD(pAbsoluteSecurityDescriptor, + pSelfRelativeSecurityDescriptor, + (LPDWORD)&securityDescriptorLength) == 0) + { + status = GetLastError(); + + HlprLogInfo(L"PrvHlprCreateAppContainerSecurityDescriptor : MakeSelfRelativeSD() [status: %#x]", + status); + + HLPR_BAIL; + } + + HLPR_BAIL_LABEL: + + HLPR_DELETE_ARRAY(pACL); + + HLPR_DELETE_ARRAY(pAbsoluteSecurityDescriptor); + + if(status != NO_ERROR) + { + HLPR_DELETE_ARRAY(pSelfRelativeSecurityDescriptor); + } + + if(pAppContainerSID) + { + FreeSid(pAppContainerSID); + + pAppContainerSID = 0; + } + + return pSelfRelativeSecurityDescriptor; +} + +/** + @private_function="AppliesToAppContainers" + + Purpose: Determine if the codition should apply to app containers.
+
+ Notes: Applies additional logic to:
+ FWPM_CONDITION_ALE_USER_ID
+
+ MSDN_Ref:
+*/ +BOOLEAN AppliesToAppContainers(_In_reads_(stringCount) PCWSTR* ppCLPStrings, + _In_ UINT32 stringCount) +{ + BOOLEAN appliesToAppContainers = FALSE; + + for(UINT32 stringIndex = 0; + (stringIndex + 1) < stringCount; + stringIndex++) + { + if(HlprStringsAreEqual(L"-a2ac", + ppCLPStrings[stringIndex])) + { + appliesToAppContainers = TRUE; + + break; + } + } + + return appliesToAppContainers; +} + +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) + +/** + @private_function="PrvHlprSIDGet" + + Purpose: Lookup the SID for the provided user.
+
+ Notes: The caller is responsible for freeing any allocated memory using HLPR_DELETE_ARRAY.
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA379159.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA379151.aspx
+*/ +UINT32 PrvHlprSIDGet(_In_ PCWSTR pName, + _Inout_ PSID* ppSID) +{ + UINT32 status = NO_ERROR; + PSID pSID = 0; + UINT32 sidSize = 0; + size_t domainSize = 0; + PWSTR pDomainName = 0; + SID_NAME_USE sidType = SidTypeUser; + + /// First call will give us the necessary size + if(LookupAccountName(0, + pName, + 0, + (DWORD*)&sidSize, + 0, + (DWORD*)&domainSize, + &sidType) == 0) + { + status = GetLastError(); + + if(status != ERROR_INSUFFICIENT_BUFFER || + sidSize == 0) + { + HlprLogError(L"PrvHlprSIDGet: LookupAccountName [status: %#x]", + status); + + HLPR_BAIL; + } + } + + HLPR_NEW_CASTED_ARRAY(pSID, + SID, + BYTE, + sidSize); + HLPR_BAIL_ON_ALLOC_FAILURE(pSID, + status); + + if(domainSize) + { + HLPR_NEW_ARRAY(pDomainName, + WCHAR, + domainSize); + HLPR_BAIL_ON_ALLOC_FAILURE(pDomainName, + status); + } + + if(LookupAccountName(0, + pName, + pSID, + (DWORD*)&sidSize, + pDomainName, + (DWORD*)&domainSize, + &sidType) == 0) + { + status = GetLastError(); + + HlprLogError(L"PrvHlprSIDGet: LookupAccountName [status: %#x]", + status); + + HLPR_BAIL; + } + + if(!IsValidSid(pSID)) + { + status = ERROR_INVALID_SID; + + HlprLogError(L"PrvHlprSIDGet: IsValidSid() [status: %#x]", + status); + + HLPR_BAIL; + } + + *ppSID = pSID; + + status = NO_ERROR; + + HLPR_BAIL_LABEL: + + if(status != NO_ERROR) + { + HLPR_DELETE_ARRAY(pSID); + } + + HLPR_DELETE_ARRAY(pDomainName); + + return status; +} + +/** + @private_function="PrvHlprCommandLineStringToFwpMatchType" + + Purpose: Parse a string for a FWP_MATCH_TYPE value.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA364942.aspx
+*/ +FWP_MATCH_TYPE PrvHlprCommandLineStringToFwpMatchType(_In_ PCWSTR pMatchType) +{ + ASSERT(pMatchType); + + FWP_MATCH_TYPE matchType = FWP_MATCH_TYPE_MAX; + + if(HlprStringsAreEqual(pMatchType, + L"==") || + HlprStringsAreEqual(pMatchType, + L"=")) + matchType = FWP_MATCH_EQUAL; + else if(HlprStringsAreEqual(pMatchType, + L"!=")) + matchType = FWP_MATCH_NOT_EQUAL; + else if(HlprStringsAreEqual(pMatchType, + L">")) + matchType = FWP_MATCH_GREATER; + else if(HlprStringsAreEqual(pMatchType, + L"<")) + matchType = FWP_MATCH_LESS; + else if(HlprStringsAreEqual(pMatchType, + L">=")) + matchType = FWP_MATCH_GREATER_OR_EQUAL; + else if(HlprStringsAreEqual(pMatchType, + L"<=")) + matchType = FWP_MATCH_LESS_OR_EQUAL; + + return matchType; +} + +/** + @private_function="PrvHlprEtherTypeParse" + + Purpose: Parse a string for a well known frame type value.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +UINT32 PrvHlprEtherTypeParse(_In_ PCWSTR pEtherType) +{ + ASSERT(pEtherType); + + UINT32 etherType = 0; + + if(HlprStringsAreEqual(pEtherType, + L"IPv4") || + HlprStringsAreEqual(pEtherType, + L"NDIS_ETH_TYPE_IPV4")) + etherType = NDIS_ETH_TYPE_IPV4; /// 0x0800 + else if(HlprStringsAreEqual(pEtherType, + L"ARP") || + HlprStringsAreEqual(pEtherType, + L"NDIS_ETH_TYPE_ARP")) + etherType = NDIS_ETH_TYPE_ARP; /// 0x0806 + else if(HlprStringsAreEqual(pEtherType, + L"IPv6") || + HlprStringsAreEqual(pEtherType, + L"NDIS_ETH_TYPE_IPV6")) + etherType = NDIS_ETH_TYPE_IPV6; /// 0x86DD + else if(HlprStringsAreEqual(pEtherType, + L"NDIS_ETH_TYPE_802_1X")) + etherType = NDIS_ETH_TYPE_802_1X; /// 0x888E + else if(HlprStringsAreEqual(pEtherType, + L"NDIS_ETH_TYPE_802_1Q")) + etherType = NDIS_ETH_TYPE_802_1Q; /// 0x8100 + else if(HlprStringsAreEqual(pEtherType, + L"NDIS_ETH_TYPE_SLOW_PROTOCOL")) + etherType = NDIS_ETH_TYPE_SLOW_PROTOCOL; /// 0x8809 + + return etherType; +} + +/** + @private_function="PrvHlprNDISMediumTypeParse" + + Purpose: Parse a string for a NDIS_MEDIUM value.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF565910.aspx
+*/ +UINT32 PrvHlprNDISMediumTypeParse(_In_ PCWSTR pMediumType) +{ + ASSERT(pMediumType); + + UINT32 mediumType = NdisMediumMax; + + if(HlprStringsAreEqual(pMediumType, + L"Ethernet") || + HlprStringsAreEqual(pMediumType, + L"NdisMedium802_3")) + mediumType = NdisMedium802_3; /// 0x0000 + else if(HlprStringsAreEqual(pMediumType, + L"TokenRing") || + HlprStringsAreEqual(pMediumType, + L"NdisMedium802_5")) + mediumType = NdisMedium802_5; /// 0x0001 + else if(HlprStringsAreEqual(pMediumType, + L"FDDI") || + HlprStringsAreEqual(pMediumType, + L"NdisMediumFddi")) + mediumType = NdisMediumFddi; /// 0x0002 + else if(HlprStringsAreEqual(pMediumType, + L"WAN") || + HlprStringsAreEqual(pMediumType, + L"NdisMediumWan")) + mediumType = NdisMediumWan; /// 0x0003 + else if(HlprStringsAreEqual(pMediumType, + L"LocalTalk") || + HlprStringsAreEqual(pMediumType, + L"NdisMediumLocalTalk")) + mediumType = NdisMediumLocalTalk; /// 0x0004 + else if(HlprStringsAreEqual(pMediumType, + L"DIX") || + HlprStringsAreEqual(pMediumType, + L"NdisMediumDix")) + mediumType = NdisMediumDix; /// 0x0005 + else if(HlprStringsAreEqual(pMediumType, + L"ARCNet") || + HlprStringsAreEqual(pMediumType, + L"NdisMediumArcnetRaw")) + mediumType = NdisMediumArcnetRaw; /// 0x0006 + else if(HlprStringsAreEqual(pMediumType, + L"NdisMediumArcnet878_2")) + mediumType = NdisMediumArcnet878_2; /// 0x0007 + else if(HlprStringsAreEqual(pMediumType, + L"ATM") || + HlprStringsAreEqual(pMediumType, + L"NdisMediumAtm")) + mediumType = NdisMediumAtm; /// 0x0008 + else if(HlprStringsAreEqual(pMediumType, + L"WirelessWAN") || + HlprStringsAreEqual(pMediumType, + L"NdisMediumWirelessWan")) + mediumType = NdisMediumWirelessWan; /// 0x0009 + else if(HlprStringsAreEqual(pMediumType, + L"IRDA") || + HlprStringsAreEqual(pMediumType, + L"NdisMediumIrda")) + mediumType = NdisMediumIrda; /// 0x000A + else if(HlprStringsAreEqual(pMediumType, + L"BPC") || + HlprStringsAreEqual(pMediumType, + L"NdisMediumBpc")) + mediumType = NdisMediumBpc; /// 0x000B + else if(HlprStringsAreEqual(pMediumType, + L"NdisMediumCoWan")) + mediumType = NdisMediumCoWan; /// 0x000C + else if(HlprStringsAreEqual(pMediumType, + L"Firewire") || + HlprStringsAreEqual(pMediumType, + L"NdisMedium1394")) + mediumType = NdisMedium1394; /// 0x000D + else if(HlprStringsAreEqual(pMediumType, + L"InfiniBand") || + HlprStringsAreEqual(pMediumType, + L"NdisMediumInfiniBand")) + mediumType = NdisMediumInfiniBand; /// 0x000E + else if(HlprStringsAreEqual(pMediumType, + L"Tunnel") || + HlprStringsAreEqual(pMediumType, + L"NdisMediumTunnel")) + mediumType = NdisMediumTunnel; /// 0x000F + else if(HlprStringsAreEqual(pMediumType, + L"NdisMediumNative802_11")) + mediumType = NdisMediumNative802_11; /// 0x0010 + else if(HlprStringsAreEqual(pMediumType, + L"Loopback") || + HlprStringsAreEqual(pMediumType, + L"NdisMediumLoopback")) + mediumType = NdisMediumLoopback; /// 0x0011 + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + else if(HlprStringsAreEqual(pMediumType, + L"WiMax") || + HlprStringsAreEqual(pMediumType, + L"NdisMediumWiMAX")) + mediumType = NdisMediumWiMAX; /// 0x0012 + else if(HlprStringsAreEqual(pMediumType, + L"IP") || + HlprStringsAreEqual(pMediumType, + L"NdisMediumIP")) + mediumType = NdisMediumIP; /// 0x0013 + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + return mediumType; +} + +/** + @private_function="PrvHlprNDISMediumTypeParse" + + Purpose: Parse a string for a NDIS_PHYSICAL_MEDIUM value.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +UINT32 PrvHlprNDISPhysicalMediumTypeParse(_In_ PCWSTR pPhysicalMediumType) +{ + ASSERT(pPhysicalMediumType); + + UINT32 mediumType = NdisPhysicalMediumMax; + + if(HlprStringsAreEqual(pPhysicalMediumType, + L"Unspecified") || + HlprStringsAreEqual(pPhysicalMediumType, + L"NdisPhysicalMediumUnspecified")) + mediumType = NdisPhysicalMediumUnspecified; /// 0x0000 + else if(HlprStringsAreEqual(pPhysicalMediumType, + L"WirelessLan") || + HlprStringsAreEqual(pPhysicalMediumType, + L"NdisPhysicalMediumWirelessLan")) + mediumType = NdisPhysicalMediumWirelessLan; /// 0x0001 + else if(HlprStringsAreEqual(pPhysicalMediumType, + L"CableModem") || + HlprStringsAreEqual(pPhysicalMediumType, + L"NdisPhysicalMediumCableModem")) + mediumType = NdisPhysicalMediumCableModem; /// 0x0002 + else if(HlprStringsAreEqual(pPhysicalMediumType, + L"PhoneLine") || + HlprStringsAreEqual(pPhysicalMediumType, + L"NdisPhysicalMediumPhoneLine")) + mediumType = NdisPhysicalMediumPhoneLine; /// 0x0003 + else if(HlprStringsAreEqual(pPhysicalMediumType, + L"PowerLine") || + HlprStringsAreEqual(pPhysicalMediumType, + L"NdisPhysicalMediumPowerLine")) + mediumType = NdisPhysicalMediumPowerLine; /// 0x0004 + else if(HlprStringsAreEqual(pPhysicalMediumType, + L"DSL") || + HlprStringsAreEqual(pPhysicalMediumType, + L"NdisPhysicalMediumDSL")) + mediumType = NdisPhysicalMediumDSL; /// 0x0005 + else if(HlprStringsAreEqual(pPhysicalMediumType, + L"FibreChannel") || + HlprStringsAreEqual(pPhysicalMediumType, + L"NdisPhysicalMediumFibreChannel")) + mediumType = NdisPhysicalMediumFibreChannel; /// 0x0006 + else if(HlprStringsAreEqual(pPhysicalMediumType, + L"Firewire") || + HlprStringsAreEqual(pPhysicalMediumType, + L"NdisPhysicalMedium1394")) + mediumType = NdisPhysicalMedium1394; /// 0x0007 + else if(HlprStringsAreEqual(pPhysicalMediumType, + L"WirelessWan") || + HlprStringsAreEqual(pPhysicalMediumType, + L"NdisPhysicalMediumWirelessWan")) + mediumType = NdisPhysicalMediumWirelessWan; /// 0x0008 + else if(HlprStringsAreEqual(pPhysicalMediumType, + L"Native802_11") || + HlprStringsAreEqual(pPhysicalMediumType, + L"NdisPhysicalMediumNative802_11")) + mediumType = NdisPhysicalMediumNative802_11; /// 0x0009 + else if(HlprStringsAreEqual(pPhysicalMediumType, + L"Bluetooth") || + HlprStringsAreEqual(pPhysicalMediumType, + L"NdisPhysicalMediumBluetooth")) + mediumType = NdisPhysicalMediumBluetooth; /// 0x000A + else if(HlprStringsAreEqual(pPhysicalMediumType, + L"Infiniband") || + HlprStringsAreEqual(pPhysicalMediumType, + L"NdisPhysicalMediumInfiniband")) + mediumType = NdisPhysicalMediumInfiniband; /// 0x000B + else if(HlprStringsAreEqual(pPhysicalMediumType, + L"WiMax") || + HlprStringsAreEqual(pPhysicalMediumType, + L"NdisPhysicalMediumWiMax")) + mediumType = NdisPhysicalMediumWiMax; /// 0x000C + else if(HlprStringsAreEqual(pPhysicalMediumType, + L"UWB") || + HlprStringsAreEqual(pPhysicalMediumType, + L"NdisPhysicalMediumUWB")) + mediumType = NdisPhysicalMediumUWB; /// 0x000D + else if(HlprStringsAreEqual(pPhysicalMediumType, + L"Ethernet") || + HlprStringsAreEqual(pPhysicalMediumType, + L"NdisPhysicalMedium802_3")) + mediumType = NdisPhysicalMedium802_3; /// 0x000E + else if(HlprStringsAreEqual(pPhysicalMediumType, + L"TokenRing") || + HlprStringsAreEqual(pPhysicalMediumType, + L"NdisPhysicalMedium802_5")) + mediumType = NdisPhysicalMedium802_5; /// 0x000F + else if(HlprStringsAreEqual(pPhysicalMediumType, + L"Irda") || + HlprStringsAreEqual(pPhysicalMediumType, + L"NdisPhysicalMediumIrda")) + mediumType = NdisPhysicalMediumIrda; /// 0x0010 + else if(HlprStringsAreEqual(pPhysicalMediumType, + L"WiredWan") || + HlprStringsAreEqual(pPhysicalMediumType, + L"NdisPhysicalMediumWiredWan")) + mediumType = NdisPhysicalMediumWiredWAN; /// 0x0011 + else if(HlprStringsAreEqual(pPhysicalMediumType, + L"WiredCoWan") || + HlprStringsAreEqual(pPhysicalMediumType, + L"NdisPhysicalMediumWiredCoWan")) + mediumType = NdisPhysicalMediumWiredCoWan; /// 0x0012 + else if(HlprStringsAreEqual(pPhysicalMediumType, + L"NdisPhysicalMediumOther")) + mediumType = NdisPhysicalMediumOther; /// 0x0013 + + return mediumType; +} + +/** + @private_function="PrvHlprDataLinkAddressTypeParse" + + Purpose: Parse a string for an DL_ADDRESS_TYPE value.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/DD744934.aspx
+*/ +UINT8 PrvHlprDataLinkAddressTypeParse(_In_ PCWSTR pDLAddressType) +{ + ASSERT(pDLAddressType); + + UINT8 type = DlUnicast; + + if(HlprStringsAreEqual(pDLAddressType, + L"Unicast") || + HlprStringsAreEqual(pDLAddressType, + L"DlUnicast")) + type = DlUnicast; + else if(HlprStringsAreEqual(pDLAddressType, + L"Multicast") || + HlprStringsAreEqual(pDLAddressType, + L"DlMulticast")) + type = DlMulticast; + else if(HlprStringsAreEqual(pDLAddressType, + L"Broadcast") || + HlprStringsAreEqual(pDLAddressType, + L"DlBroadcast")) + type = DlBroadcast; + + return type; +} + +/** + @private_function="PrvHlprIPAddressTypeParse" + + Purpose: Parse a string for an NL_ADDRESS_TYPE value.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/FF568757.aspx
+*/ +UINT8 PrvHlprIPAddressTypeParse(_In_ PCWSTR pNLAddressType) +{ + ASSERT(pNLAddressType); + + UINT8 type = NlatUnspecified; + + if(HlprStringsAreEqual(pNLAddressType, + L"Unicast") || + HlprStringsAreEqual(pNLAddressType, + L"NlatUnicast")) + type = NlatUnicast; + else if(HlprStringsAreEqual(pNLAddressType, + L"Anycast") || + HlprStringsAreEqual(pNLAddressType, + L"NlatAnycast")) + type = NlatAnycast; + else if(HlprStringsAreEqual(pNLAddressType, + L"Multicast") || + HlprStringsAreEqual(pNLAddressType, + L"NlatMulticast")) + type = NlatMulticast; + else if(HlprStringsAreEqual(pNLAddressType, + L"Broadcast") || + HlprStringsAreEqual(pNLAddressType, + L"NlatBroadcast")) + type = NlatBroadcast; + + return type; +} + +/** + @private_function="PrvHlprProtocolParse" + + Purpose: Parse a string for a well known protocol value.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +UINT32 PrvHlprProtocolParse(_In_ PCWSTR pProtocol) +{ + ASSERT(pProtocol); + + UINT32 protocol = IPPROTO_MAX; + + if(HlprStringsAreEqual(L"TCP", + pProtocol)) + protocol = IPPROTO_TCP; + else if(HlprStringsAreEqual(L"UDP", + pProtocol)) + protocol = IPPROTO_UDP; + else if(HlprStringsAreEqual(L"ICMPV4", + pProtocol)) + protocol = IPPROTO_ICMP; + else if(HlprStringsAreEqual(L"ICMPV6", + pProtocol)) + protocol = IPPROTO_ICMPV6; + + return protocol; +} + +/** + @private_function="PrvHlprFwpDirectionParse" + + Purpose: Parse a string for a FWP_DIRECTION value.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/FF552433.aspx
+*/ +UINT32 PrvHlprFwpDirectionParse(_In_ PCWSTR pDirection) +{ + ASSERT(pDirection); + + UINT32 direction = FWP_DIRECTION_MAX; + + if(HlprStringsAreEqual(pDirection, + L"OUTBOUND") || + HlprStringsAreEqual(pDirection, + L"FWP_DIRECTION_OUTBOUND")) + direction = FWP_DIRECTION_OUTBOUND; + else if(HlprStringsAreEqual(pDirection, + L"INBOUND") || + HlprStringsAreEqual(pDirection, + L"FWP_DIRECTION_INBOUND")) + direction = FWP_DIRECTION_INBOUND; + + return direction; +} + +/** + @helper_function="PrvHlprFwpConditionFlagParse" + + Purpose: Parse a string for a FWP_CONDITION_FLAGS value.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA364002.aspx
+*/ +UINT32 PrvHlprFwpConditionFlagParse(_In_ PCWSTR pFlag) +{ + ASSERT(pFlag); + + UINT32 flag = 0; + + if(HlprStringsAreEqual(pFlag, + L"FWP_CONDITION_FLAG_IS_LOOPBACK")) + flag = FWP_CONDITION_FLAG_IS_LOOPBACK; + else if(HlprStringsAreEqual(pFlag, + L"FWP_CONDITION_FLAG_IS_IPSEC_SECURED")) + flag = FWP_CONDITION_FLAG_IS_IPSEC_SECURED; + else if(HlprStringsAreEqual(pFlag, + L"FWP_CONDITION_FLAG_IS_REAUTHORIZE")) + flag = FWP_CONDITION_FLAG_IS_REAUTHORIZE; + else if(HlprStringsAreEqual(pFlag, + L"FWP_CONDITION_FLAG_IS_WILDCARD_BIND")) + flag = FWP_CONDITION_FLAG_IS_WILDCARD_BIND; + else if(HlprStringsAreEqual(pFlag, + L"FWP_CONDITION_FLAG_IS_RAW_ENDPOINT")) + flag = FWP_CONDITION_FLAG_IS_RAW_ENDPOINT; + else if(HlprStringsAreEqual(pFlag, + L"FWP_CONDITION_FLAG_IS_FRAGMENT")) + flag = FWP_CONDITION_FLAG_IS_FRAGMENT; + else if(HlprStringsAreEqual(pFlag, + L"FWP_CONDITION_FLAG_IS_FRAGMENT_GROUP")) + flag = FWP_CONDITION_FLAG_IS_FRAGMENT_GROUP; + else if(HlprStringsAreEqual(pFlag, + L"FWP_CONDITION_FLAG_IS_IPSEC_NATT_RECLASSIFY")) + flag = FWP_CONDITION_FLAG_IS_IPSEC_NATT_RECLASSIFY; + else if(HlprStringsAreEqual(pFlag, + L"FWP_CONDITION_FLAG_REQUIRES_ALE_CLASSIFY")) + flag = FWP_CONDITION_FLAG_REQUIRES_ALE_CLASSIFY; + else if(HlprStringsAreEqual(pFlag, + L"FWP_CONDITION_FLAG_IS_IMPLICIT_BIND")) + flag = FWP_CONDITION_FLAG_IS_IMPLICIT_BIND; + +#if(NTDDI_VERSION >= NTDDI_VISTASP1) + + else if(HlprStringsAreEqual(pFlag, + L"FWP_CONDITION_FLAG_IS_REASSEMBLED")) + flag = FWP_CONDITION_FLAG_IS_REASSEMBLED; + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + else if(HlprStringsAreEqual(pFlag, + L"FWP_CONDITION_FLAG_IS_NAME_APP_SPECIFIED")) + flag = FWP_CONDITION_FLAG_IS_NAME_APP_SPECIFIED; + else if(HlprStringsAreEqual(pFlag, + L"FWP_CONDITION_FLAG_IS_PROMISCUOUS")) + flag = FWP_CONDITION_FLAG_IS_PROMISCUOUS; + else if(HlprStringsAreEqual(pFlag, + L"FWP_CONDITION_FLAG_IS_AUTH_FW")) + flag = FWP_CONDITION_FLAG_IS_AUTH_FW; + else if(HlprStringsAreEqual(pFlag, + L"FWP_CONDITION_FLAG_IS_RECLASSIFY")) + flag = FWP_CONDITION_FLAG_IS_RECLASSIFY; + else if(HlprStringsAreEqual(pFlag, + L"FWP_CONDITION_FLAG_IS_OUTBOUND_PASS_THRU")) + flag = FWP_CONDITION_FLAG_IS_OUTBOUND_PASS_THRU; + else if(HlprStringsAreEqual(pFlag, + L"FWP_CONDITION_FLAG_IS_INBOUND_PASS_THRU")) + flag = FWP_CONDITION_FLAG_IS_INBOUND_PASS_THRU; + else if(HlprStringsAreEqual(pFlag, + L"FWP_CONDITION_FLAG_IS_CONNECTION_REDIRECTED")) + flag = FWP_CONDITION_FLAG_IS_CONNECTION_REDIRECTED; + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + else if(HlprStringsAreEqual(pFlag, + L"FWP_CONDITION_FLAG_IS_PROXY_CONNECTION")) + flag = FWP_CONDITION_FLAG_IS_PROXY_CONNECTION; + else if(HlprStringsAreEqual(pFlag, + L"FWP_CONDITION_FLAG_IS_APPCONTAINER_LOOPBACK")) + flag = FWP_CONDITION_FLAG_IS_APPCONTAINER_LOOPBACK; + else if(HlprStringsAreEqual(pFlag, + L"FWP_CONDITION_FLAG_IS_NON_APPCONTAINER_LOOPBACK")) + flag = FWP_CONDITION_FLAG_IS_NON_APPCONTAINER_LOOPBACK; + +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) +#endif /// (NTDDI_VERSION >= NTDDI_VISTASP1) + + return flag; +} + +/** + @private_function="PrvHlprInterfaceTypeParse" + + Purpose: Parse a string for an interface type value.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/FF565767.aspx
+*/ +UINT32 PrvHlprInterfaceTypeParse(_In_ PCWSTR pInterfaceType) +{ + ASSERT(pInterfaceType); + + UINT32 type = MAX_IF_TYPE; + + if(HlprStringsAreEqual(pInterfaceType, + L"ETHERNET") || + HlprStringsAreEqual(pInterfaceType, + L"IF_TYPE_ETHERNET_CSMACD")) + type = IF_TYPE_ETHERNET_CSMACD; + else if(HlprStringsAreEqual(pInterfaceType, + L"TOKENRING") || + HlprStringsAreEqual(pInterfaceType, + L"IF_TYPE_ISO88025_TOKENRING")) + type = IF_TYPE_ISO88025_TOKENRING; + else if(HlprStringsAreEqual(pInterfaceType, + L"PPP") || + HlprStringsAreEqual(pInterfaceType, + L"IF_TYPE_PPP")) + type = IF_TYPE_PPP; + else if(HlprStringsAreEqual(pInterfaceType, + L"LOOPBACK") || + HlprStringsAreEqual(pInterfaceType, + L"IF_TYPE_SOFTWARE_LOOPBACK")) + type = IF_TYPE_SOFTWARE_LOOPBACK; + else if(HlprStringsAreEqual(pInterfaceType, + L"SLIP") || + HlprStringsAreEqual(pInterfaceType, + L"IF_TYPE_SLIP")) + type = IF_TYPE_SLIP; + else if(HlprStringsAreEqual(pInterfaceType, + L"WIRELESS") || + HlprStringsAreEqual(pInterfaceType, + L"IF_TYPE_IEEE80211")) + type = IF_TYPE_IEEE80211; + else if(HlprStringsAreEqual(pInterfaceType, + L"TUNNEL") || + HlprStringsAreEqual(pInterfaceType, + L"IF_TYPE_TUNNEL")) + type = IF_TYPE_TUNNEL; + + return type; +} + +/** + @private_function="PrvHlprTunnelTypeParse" + + Purpose: Parse a string for a TUNNEL_TYPE value.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/FF570962.aspx
+*/ +UINT32 PrvHlprTunnelTypeParse(_In_ PCWSTR pTunnelType) +{ + ASSERT(pTunnelType); + + UINT32 type = TUNNEL_TYPE_NONE; + + if(HlprStringsAreEqual(pTunnelType, + L"OTHER") || + HlprStringsAreEqual(pTunnelType, + L"TUNNEL_TYPE_OTHER")) + type = TUNNEL_TYPE_OTHER; + else if(HlprStringsAreEqual(pTunnelType, + L"6TO4") || + HlprStringsAreEqual(pTunnelType, + L"TUNNEL_TYPE_6TO4")) + type = TUNNEL_TYPE_6TO4; + else if(HlprStringsAreEqual(pTunnelType, + L"ISATAP") || + HlprStringsAreEqual(pTunnelType, + L"TUNNEL_TYPE_ISATAP")) + type = TUNNEL_TYPE_ISATAP; + else if(HlprStringsAreEqual(pTunnelType, + L"TEREDO") || + HlprStringsAreEqual(pTunnelType, + L"TUNNEL_TYPE_TEREDO")) + type = TUNNEL_TYPE_TEREDO; + else if(HlprStringsAreEqual(pTunnelType, + L"IPHTTPS") || + HlprStringsAreEqual(pTunnelType, + L"TUNNEL_TYPE_IPHTTPS")) + type = TUNNEL_TYPE_IPHTTPS; + + return type; +} + +/** + @private_function="PrvHlprProfileIDParse" + + Purpose: Parse a string for a NLM_NETWORK_CATEGORY value.
+
+ Notes: Function converts from NLM_NETWORK_CATEGORY to NL_INTERFACE_NETWORK_CATEGORY_STATE.
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA370800.aspx
+*/ +UINT32 PrvHlprProfileIDParse(_In_ PCWSTR pProfileID) +{ + ASSERT(pProfileID); + + UINT32 id = NlincCategoryStateMax; + + if(HlprStringsAreEqual(pProfileID, + L"UNKNOWN") || + HlprStringsAreEqual(pProfileID, + L"NlincCategoryUnknown")) + id = NlincCategoryUnknown; + else if(HlprStringsAreEqual(pProfileID, + L"PUBLIC") || + HlprStringsAreEqual(pProfileID, + L"NLM_NETWORK_CATEGORY_PUBLIC") || + HlprStringsAreEqual(pProfileID, + L"NlincPublic")) + id = NlincPublic; + else if(HlprStringsAreEqual(pProfileID, + L"PRIVATE") || + HlprStringsAreEqual(pProfileID, + L"NLM_NETWORK_CATEGORY_PRIVATE") || + HlprStringsAreEqual(pProfileID, + L"NlincPrivate")) + id = NlincPrivate; + else if(HlprStringsAreEqual(pProfileID, + L"DOMAIN") || + HlprStringsAreEqual(pProfileID, + L"NLM_NETWORK_CATEGORY_DOMAIN_AUTHENTICATED") || + HlprStringsAreEqual(pProfileID, + L"NlincDomainAuthenticated")) + id = NlincDomainAuthenticated; + + return id; +} + +/** + @private_function="PrvHlprRPCIFFlagParse" + + Purpose: Parse a string for a RPC_FW_IF_FLAG value.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +UINT32 PrvHlprRPCIFFlagParse(_In_ PCWSTR pFlag) +{ + ASSERT(pFlag); + + UINT32 flag = 0; + + if(HlprStringsAreEqual(pFlag, + L"RPC_FW_IF_FLAG_DCOM")) + flag = RPC_FW_IF_FLAG_DCOM; + + return flag; +} + +/** + @private_function="PrvHlprRPCProtocolParse" + + Purpose: Parse a string for a RPC protocol value.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.microsoft.com/En-US/Library/Windows/Desktop/AA374395.aspx
+*/ +UINT8 PrvHlprRPCProtocolParse(_In_ PCWSTR pProtocol) +{ + ASSERT(pProtocol); + + UINT8 protocol = 0; + + if(HlprStringsAreEqual(pProtocol, + L"NCACN_IP_TCP") || + HlprStringsAreEqual(pProtocol, + L"TCP")) + protocol = 0x07; /// NCACN_IP_TCP + else if(HlprStringsAreEqual(pProtocol, + L"NCACN_NP") || + HlprStringsAreEqual(pProtocol, + L"NamedPipes")) + protocol = 0x0F; /// NCACN_NP + else if(HlprStringsAreEqual(pProtocol, + L"NCALRPC") || + HlprStringsAreEqual(pProtocol, + L"LocalRPC")) + protocol = 0x10; /// NCALRPC + + return protocol; +} + +/** + @private_function="PrvHlprRPCAuthTypeParse" + + Purpose: Parse a string for a RPC Auth Type value.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.microsoft.com/En-US/Library/Windows/Desktop/AA373556.aspx
+*/ +UINT32 PrvHlprRPCAuthTypeParse(_In_ PCWSTR pType) +{ + ASSERT(pType); + + UINT32 type = RPC_C_AUTHN_DEFAULT; + + if(HlprStringsAreEqual(pType, + L"RPC_C_AUTHN_NONE") || + HlprStringsAreEqual(pType, + L"None")) + type = RPC_C_AUTHN_NONE; + else if(HlprStringsAreEqual(pType, + L"RPC_C_AUTHN_WINNT") || + HlprStringsAreEqual(pType, + L"WinNT")) + type = RPC_C_AUTHN_WINNT; + else if(HlprStringsAreEqual(pType, + L"RPC_C_AUTHN_DEFAULT") || + HlprStringsAreEqual(pType, + L"Default")) + type = RPC_C_AUTHN_DEFAULT; + + return type; +} + +/** + @private_function="PrvHlprRPCAuthLevelParse" + + Purpose: Parse a string for a RPC Auth Level value.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.microsoft.com/En-US/Library/Windows/Desktop/AA373553.aspx
+*/ +UINT8 PrvHlprRPCAuthLevelParse(_In_ PCWSTR pLevel) +{ + ASSERT(pLevel); + + UINT8 level = RPC_C_AUTHN_LEVEL_DEFAULT; + + if(HlprStringsAreEqual(pLevel, + L"RPC_C_AUTHN_LEVEL_DEFAULT") || + HlprStringsAreEqual(pLevel, + L"Default")) + level = RPC_C_AUTHN_LEVEL_DEFAULT; + else if(HlprStringsAreEqual(pLevel, + L"RPC_C_AUTHN_LEVEL_NONE") || + HlprStringsAreEqual(pLevel, + L"None")) + level = RPC_C_AUTHN_LEVEL_NONE; + else if(HlprStringsAreEqual(pLevel, + L"RPC_C_AUTHN_LEVEL_CONNECT") || + HlprStringsAreEqual(pLevel, + L"Connect")) + level = RPC_C_AUTHN_LEVEL_CONNECT; + else if(HlprStringsAreEqual(pLevel, + L"RPC_C_AUTHN_LEVEL_NONE") || + HlprStringsAreEqual(pLevel, + L"None")) + level = RPC_C_AUTHN_LEVEL_NONE; + else if(HlprStringsAreEqual(pLevel, + L"RPC_C_AUTHN_LEVEL_CALL") || + HlprStringsAreEqual(pLevel, + L"Call")) + level = RPC_C_AUTHN_LEVEL_CALL; + else if(HlprStringsAreEqual(pLevel, + L"RPC_C_AUTHN_LEVEL_PKT_INTEGRITY") || + HlprStringsAreEqual(pLevel, + L"Integrity")) + level = RPC_C_AUTHN_LEVEL_PKT_INTEGRITY; + else if(HlprStringsAreEqual(pLevel, + L"RPC_C_AUTHN_LEVEL_PKT_PRIVACY") || + HlprStringsAreEqual(pLevel, + L"Privacy")) + level = RPC_C_AUTHN_LEVEL_PKT_PRIVACY; + + return level; +} + +/** + @private_function="PrvHlprFwpmFilterConditionsSort" + + Purpose: Pare down the filterConditions to only those applicable to the intended layer, and + sort them so identical condition fields are contiguous.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF549939.aspx
+*/ +UINT32 PrvHlprFwpmFilterConditionsSort(_In_reads_(numFilterConditions) FWPM_FILTER_CONDITION* pFilterConditions, + _In_ UINT32 numFilterConditions, + _Inout_ FWPM_FILTER* pFilter) +{ + ASSERT(pFilterConditions); + ASSERT(numFilterConditions); + ASSERT(pFilter); + + UINT32 status = NO_ERROR; + GUID** ppValidFilterConditions = 0; + UINT16 validConditionCount = 0; + + status = HlprFwpmLayerGetFilterConditionArrayByKey(&(pFilter->layerKey), + &ppValidFilterConditions, + &validConditionCount); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmFilterConditionCreate(&(pFilter->filterCondition), + numFilterConditions); + HLPR_BAIL_ON_FAILURE(status); + + pFilter->numFilterConditions = 0; + + if(ppValidFilterConditions && + validConditionCount) + { + for(UINT32 validConditionIndex = 0; + validConditionIndex < validConditionCount; + validConditionIndex++) + { + for(UINT32 conditionIndex = 0; + conditionIndex < numFilterConditions; + conditionIndex++) + { + if(HlprGUIDsAreEqual(ppValidFilterConditions[validConditionIndex], + &(pFilterConditions[conditionIndex].fieldKey))) + { + RtlCopyMemory(&(pFilter->filterCondition[pFilter->numFilterConditions]), + &(pFilterConditions[conditionIndex]), + sizeof(FWPM_FILTER_CONDITION)); + + pFilter->numFilterConditions++; + } + } + } + } + + HLPR_BAIL_LABEL: + + if(status != NO_ERROR) + { + HlprFwpmFilterConditionDestroy(&(pFilter->filterCondition), + pFilter->numFilterConditions); + + pFilter->numFilterConditions = 0; + } + + return status; +} + +#if(NTDDI_VERSION >= NTDDI_WIN7) + +/** + @private_function="PrvHlprFwpConditionReauthorizeReasonParse" + + Purpose: Parse a string for a FWP_CONDITION_REAUTHORIZE_REASON value.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA364002.aspx
+*/ +UINT32 PrvHlprFwpConditionReauthorizeReasonParse(_In_ PCWSTR pFlag) +{ + ASSERT(pFlag); + + UINT32 flag = 0; + + if(HlprStringsAreEqual(pFlag, + L"POLICY_CHANGE") || + HlprStringsAreEqual(pFlag, + L"FWP_CONDITION_REAUTHORIZE_REASON_POLICY_CHANGE")) + flag = FWP_CONDITION_REAUTHORIZE_REASON_POLICY_CHANGE; + else if(HlprStringsAreEqual(pFlag, + L"NEW_ARRIVAL_INTERFACE") || + HlprStringsAreEqual(pFlag, + L"FWP_CONDITION_REAUTHORIZE_REASON_NEW_ARRIVAL_INTERFACE")) + flag = FWP_CONDITION_REAUTHORIZE_REASON_NEW_ARRIVAL_INTERFACE; + else if(HlprStringsAreEqual(pFlag, + L"NEW_NEXTHOP_INTERFACE") || + HlprStringsAreEqual(pFlag, + L"FWP_CONDITION_REAUTHORIZE_REASON_NEW_NEXTHOP_INTERFACE")) + flag = FWP_CONDITION_REAUTHORIZE_REASON_NEW_NEXTHOP_INTERFACE; + else if(HlprStringsAreEqual(pFlag, + L"PROFILE_CROSSING") || + HlprStringsAreEqual(pFlag, + L"FWP_CONDITION_REAUTHORIZE_REASON_PROFILE_CROSSING")) + flag = FWP_CONDITION_REAUTHORIZE_REASON_PROFILE_CROSSING; + else if(HlprStringsAreEqual(pFlag, + L"CLASSIFY_COMPLETION") || + HlprStringsAreEqual(pFlag, + L"FWP_CONDITION_REAUTHORIZE_REASON_CLASSIFY_COMPLETION")) + flag = FWP_CONDITION_REAUTHORIZE_REASON_CLASSIFY_COMPLETION; + else if(HlprStringsAreEqual(pFlag, + L"IPSEC_PROPERTIES_CHANGED") || + HlprStringsAreEqual(pFlag, + L"FWP_CONDITION_REAUTHORIZE_REASON_IPSEC_PROPERTIES_CHANGED")) + flag = FWP_CONDITION_REAUTHORIZE_REASON_IPSEC_PROPERTIES_CHANGED; + else if(HlprStringsAreEqual(pFlag, + L"MID_STREAM_INSPECTION") || + HlprStringsAreEqual(pFlag, + L"FWP_CONDITION_REAUTHORIZE_REASON_MID_STREAM_INSPECTION")) + flag = FWP_CONDITION_REAUTHORIZE_REASON_MID_STREAM_INSPECTION; + else if(HlprStringsAreEqual(pFlag, + L"SOCKET_PROPERTY_CHANGED") || + HlprStringsAreEqual(pFlag, + L"FWP_CONDITION_REAUTHORIZE_REASON_SOCKET_PROPERTY_CHANGED")) + flag = FWP_CONDITION_REAUTHORIZE_REASON_SOCKET_PROPERTY_CHANGED; + else if(HlprStringsAreEqual(pFlag, + L"NEW_INBOUND_MCAST_BCAST_PACKET") || + HlprStringsAreEqual(pFlag, + L"FWP_CONDITION_REAUTHORIZE_REASON_NEW_INBOUND_MCAST_BCAST_PACKET")) + flag = FWP_CONDITION_REAUTHORIZE_REASON_NEW_INBOUND_MCAST_BCAST_PACKET; + + return flag; +} + +#if(NTDDI_VERSION >= NTDDI_WIN8) + +/** + @private_function="PrvHlprFwpConditionL2FlagParse" + + Purpose: Parse a string for a FWP_CONDITION_L2_FLAGS value.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +UINT32 PrvHlprFwpConditionL2FlagParse(_In_ PCWSTR pL2Flag) +{ + ASSERT(pL2Flag); + + UINT32 l2Flag = 0; + + if(HlprStringsAreEqual(pL2Flag, + L"FWP_CONDITION_L2_IS_NATIVE_ETHERNET")) + l2Flag = FWP_CONDITION_L2_IS_NATIVE_ETHERNET; + else if(HlprStringsAreEqual(pL2Flag, + L"FWP_CONDITION_L2_IS_WIFI")) + l2Flag = FWP_CONDITION_L2_IS_WIFI; + else if(HlprStringsAreEqual(pL2Flag, + L"FWP_CONDITION_L2_IS_MOBILE_BROADBAND")) + l2Flag = FWP_CONDITION_L2_IS_MOBILE_BROADBAND; + else if(HlprStringsAreEqual(pL2Flag, + L"FWP_CONDITION_L2_IS_WIFI_DIRECT_DATA")) + l2Flag = FWP_CONDITION_L2_IS_WIFI_DIRECT_DATA; + + return l2Flag; +} + +/** + @private_function="PrvHlprVSwitchNetworkTypeParse" + + Purpose: Parse a string for an FWP_VSWITCH_NETWORK_TYPE value.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/HH447394.aspx
+*/ +UINT8 PrvHlprVSwitchNetworkTypeParse(_In_ PCWSTR pVSwitchNetworkType) +{ + ASSERT(pVSwitchNetworkType); + + UINT8 type = FWP_VSWITCH_NETWORK_TYPE_UNKNOWN; + + if(HlprStringsAreEqual(pVSwitchNetworkType, + L"Unknown") || + HlprStringsAreEqual(pVSwitchNetworkType, + L"FWP_VSWITCH_NETWORK_TYPE_UNKNOWN")) + type = FWP_VSWITCH_NETWORK_TYPE_UNKNOWN; + else if(HlprStringsAreEqual(pVSwitchNetworkType, + L"Private") || + HlprStringsAreEqual(pVSwitchNetworkType, + L"FWP_VSWITCH_NETWORK_TYPE_PRIVATE")) + type = FWP_VSWITCH_NETWORK_TYPE_PRIVATE; + else if(HlprStringsAreEqual(pVSwitchNetworkType, + L"Internal") || + HlprStringsAreEqual(pVSwitchNetworkType, + L"FWP_VSWITCH_NETWORK_TYPE_INTERNAL")) + type = FWP_VSWITCH_NETWORK_TYPE_INTERNAL; + else if(HlprStringsAreEqual(pVSwitchNetworkType, + L"External") || + HlprStringsAreEqual(pVSwitchNetworkType, + L"FWP_VSWITCH_NETWORK_TYPE_EXTERNAL")) + type = FWP_VSWITCH_NETWORK_TYPE_EXTERNAL; + + return type; +} + +/** + @private_function="PrvHlprVSwitchNICTypeParse" + + Purpose: Parse a string for an NDIS_SWITCH_NIC_TYPE value.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/HH598218.aspx
+*/ +UINT32 PrvHlprVSwitchNICTypeParse(_In_ PCWSTR pNICType) +{ + ASSERT(pNICType); + + UINT32 type = NdisSwitchNicTypeInternal + 1; + + if(HlprStringsAreEqual(pNICType, + L"External") || + HlprStringsAreEqual(pNICType, + L"NdisSwitchNicTypeExternal")) + type = NdisSwitchNicTypeExternal; + else if(HlprStringsAreEqual(pNICType, + L"Synthetic") || + HlprStringsAreEqual(pNICType, + L"NdisSwitchNicTypeSynthetic")) + type = NdisSwitchNicTypeSynthetic; + else if(HlprStringsAreEqual(pNICType, + L"Emulated") || + HlprStringsAreEqual(pNICType, + L"NdisSwitchNicTypeEmulated")) + type = NdisSwitchNicTypeEmulated; + else if(HlprStringsAreEqual(pNICType, + L"Internal") || + HlprStringsAreEqual(pNICType, + L"NdisSwitchNicTypeInternal")) + type = NdisSwitchNicTypeInternal; + + return type; +} + +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + +/** + @helper_function="HlprCommandLineParseForScenarioRemoval" + + Purpose: Parse the command line parameters for strings which detail:
+ -r - remove the objects associated with this scenario.
+ Notes:
+
+ MSDN_Ref:
+*/ +VOID HlprCommandLineParseForScenarioRemoval(_In_reads_(stringCount) PCWSTR* ppCLPStrings, + _In_ UINT32 stringCount, + _Inout_ BOOLEAN* pRemoveScenario) +{ + ASSERT(ppCLPStrings); + ASSERT(stringCount); + ASSERT(pRemoveScenario); + + *pRemoveScenario = FALSE; + + for(UINT32 stringIndex = 0; + stringIndex < stringCount; + stringIndex++) + { + if(HlprStringsAreEqual(L"-r", + ppCLPStrings[stringIndex])) + { + *pRemoveScenario = TRUE; + + break; + } + } + + return; +} + +/** + @helper_function="HlprCommandLineParseForScenarioRemoval" + + Purpose: Parse the command line parameters for strings which detail:
+ -b - mark the filter for boottime use and make the associated objects + persistent.
+ Notes:
+
+ MSDN_Ref:
+*/ +VOID HlprCommandLineParseForBootTime(_In_reads_(stringCount) PCWSTR* ppCLPStrings, + _In_ UINT32 stringCount, + _Inout_ FWPM_FILTER* pFilter) +{ + ASSERT(ppCLPStrings); + ASSERT(stringCount); + ASSERT(pFilter); + + for(UINT32 stringIndex = 0; + stringIndex < stringCount; + stringIndex++) + { + if(HlprStringsAreEqual(L"-b", + ppCLPStrings[stringIndex])) + { + if(pFilter->flags & FWPM_FILTER_FLAG_PERSISTENT) + pFilter->flags ^= FWPM_FILTER_FLAG_PERSISTENT; + + pFilter->flags |= FWPM_FILTER_FLAG_BOOTTIME; + + break; + } + } + + return; +} + +/** + @helper_function="HlprCommandLineParseForCalloutUse" + + Purpose: Parse the command line parameters for strings which detail:
+ -c - mark the callout to use the appropriate callout.
+
+ Notes: Applies only to the following scenarios:
+ BASIC_ACTION_BLOCK
+ BASIC_ACTION_CONTINUE
+ BASIC_ACTION_PERMIT
+
+ MSDN_Ref:
+*/ +VOID HlprCommandLineParseForCalloutUse(_In_reads_(stringCount) PCWSTR* ppCLPStrings, + _In_ UINT32 stringCount, + _Inout_ FWPM_FILTER* pFilter) +{ + ASSERT(ppCLPStrings); + ASSERT(stringCount); + ASSERT(pFilter); + + for(UINT32 stringIndex = 0; + stringIndex < stringCount; + stringIndex++) + { + if(HlprStringsAreEqual(L"-c", + ppCLPStrings[stringIndex])) + { + pFilter->action.type = FWP_ACTION_CALLOUT_UNKNOWN; + + break; + } + } + + return; +} + +/** + @helper_function="HlprCommandLineParseForVolatility" + + Purpose: Parse the command line parameters for strings which detail:
+ -v - unmark the filter and associated objects as persistent.
+
+ Notes: Should not be combined with -b (boottime)
+
+ MSDN_Ref:
+*/ +VOID HlprCommandLineParseForVolatility(_In_reads_(stringCount) PCWSTR* ppCLPStrings, + _In_ UINT32 stringCount, + _Inout_ FWPM_FILTER* pFilter) +{ + ASSERT(ppCLPStrings); + ASSERT(stringCount); + ASSERT(pFilter); + + for(UINT32 stringIndex = 0; + stringIndex < stringCount; + stringIndex++) + { + if(HlprStringsAreEqual(L"-v", + ppCLPStrings[stringIndex])) + { + if(pFilter->flags & FWPM_FILTER_FLAG_PERSISTENT) + pFilter->flags ^= FWPM_FILTER_FLAG_PERSISTENT; + + break; + } + } + + return; +} + +/** + @helper_function="HlprCommandLineParseForLayerKey" + + Purpose: Parse the command line parameters for strings which detail:
+ -l - specify at which layer the filter should be added.
+ Notes:
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 HlprCommandLineParseForLayerKey(_In_reads_(stringCount) PCWSTR* ppCLPStrings, + _In_ UINT32 stringCount, + _Inout_ FWPM_FILTER* pFilter) +{ + ASSERT(ppCLPStrings); + ASSERT(stringCount); + ASSERT(pFilter); + + UINT32 status = NO_ERROR; + + for(UINT32 stringIndex = 0; + (stringIndex + 1) < stringCount; + stringIndex++) + { + if(HlprStringsAreEqual(L"-l", + ppCLPStrings[stringIndex])) + { + PCWSTR pLayerString = ppCLPStrings[++stringIndex]; + + if(iswdigit((wint_t)pLayerString[0])) + { + UINT32 layerID = wcstol(pLayerString, + 0, + 0); + + RtlCopyMemory(&(pFilter->layerKey), + HlprFwpmLayerGetByID(layerID), + sizeof(GUID)); + } + else + { + const GUID* pLayerKey = HlprFwpmLayerGetByString(pLayerString); + + if(pLayerKey) + RtlCopyMemory(&(pFilter->layerKey), + pLayerKey, + sizeof(GUID)); + else + { + status = ERROR_INVALID_PARAMETER; + + HlprLogError(L"HlprCommandLineParseForLayerKey() [status: %#x][%s]", + status, + pLayerString); + } + } + + break; + } + } + + return status; +} + +/** + @helper_function="HlprCommandLineParseForSubLayerKey" + + Purpose: Parse the command line parameters for strings which detail:
+ -s - specify at which sublayer the filter should be added.
+ Notes:
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 HlprCommandLineParseForSubLayerKey(_In_reads_(stringCount) PCWSTR* ppCLPStrings, + _In_ UINT32 stringCount, + _Inout_ FWPM_FILTER* pFilter) +{ + ASSERT(ppCLPStrings); + ASSERT(stringCount); + ASSERT(pFilter); + + UINT32 status = NO_ERROR; + + pFilter->subLayerKey = WFPSAMPLER_SUBLAYER; + + for(UINT32 stringIndex = 0; + (stringIndex + 1) < stringCount; + stringIndex++) + { + if(HlprStringsAreEqual(L"-sl", + ppCLPStrings[stringIndex])) + { + PCWSTR pSubLayerString = ppCLPStrings[++stringIndex]; + + if(HlprStringsAreEqual(L"FWPM_SUBLAYER_UNIVERSAL", + pSubLayerString) || + HlprStringsAreEqual(L"UNIVERSAL", + pSubLayerString)) + pFilter->subLayerKey = FWPM_SUBLAYER_UNIVERSAL; + else if(HlprStringsAreEqual(L"FWPM_SUBLAYER_INSPECTION", + pSubLayerString) || + HlprStringsAreEqual(L"INSPECTION", + pSubLayerString)) + pFilter->subLayerKey = FWPM_SUBLAYER_INSPECTION; + } + } + + return status; +} + + +/** + @helper_function="HlprCommandLineParseForFilterConditions" + + Purpose: Parse the command line parameters for strings which the conditions, match type, and + value to use.
+
+ Notes: If no match type is specified, then MATCH_TYPE_EQUAL is used.
+
+ For a full list of parameters and expected values, refer to ..\docs\
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF549939.aspx
+*/ +_Success_(return == NO_ERROR) +UINT32 HlprCommandLineParseForFilterConditions(_In_reads_(stringCount) PCWSTR* ppCLPStrings, + _In_ UINT32 stringCount, + _Inout_ FWPM_FILTER* pFilter, + _In_ BOOLEAN forEnum) /* FALSE */ +{ + ASSERT(ppCLPStrings); + ASSERT(stringCount); + ASSERT(pFilter); + + UINT32 status = NO_ERROR; + FWPM_FILTER_CONDITION* pTempFilterConditions = 0; + const UINT32 MAX_CONDITIONS = 50; + UINT32 tempConditionIndex = 0; + + HLPR_NEW_ARRAY(pTempFilterConditions, + FWPM_FILTER_CONDITION, + MAX_CONDITIONS); + HLPR_BAIL_ON_ALLOC_FAILURE_WITH_LABEL(pTempFilterConditions, + status, + HLPR_BAIL_LABEL_2); + +#pragma warning(push) +#pragma warning(disable: 6385) /// careful validation of stringIndex (and advancement) against stringCount prevents read overrun + + for(UINT32 stringIndex = 0; + (stringIndex + 1) < stringCount; + stringIndex++) + { + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + /// FWPM_CONDITION_INTERFACE_MAC_ADDRESS + /// -ima + /// -ima == 01:02:03:04:05:06 + if(HlprStringsAreEqual(L"-ima", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_INTERFACE_MAC_ADDRESS; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_BYTE_ARRAY6_TYPE; + + HLPR_NEW(pTempFilterConditions[tempConditionIndex].conditionValue.byteArray6, + FWP_BYTE_ARRAY6); + HLPR_BAIL_ON_ALLOC_FAILURE(pTempFilterConditions[tempConditionIndex].conditionValue.byteArray6, + status); + + status = HlprEthernetMACAddressStringToValue(pString, + pTempFilterConditions[tempConditionIndex].conditionValue.byteArray6->byteArray6); + HLPR_BAIL_ON_FAILURE(status); + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_MAC_LOCAL_ADDRESS + /// -mla + /// -mla == 01:02:03:04:05:06 + if(HlprStringsAreEqual(L"-mla", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_MAC_LOCAL_ADDRESS; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_BYTE_ARRAY6_TYPE; + + HLPR_NEW(pTempFilterConditions[tempConditionIndex].conditionValue.byteArray6, + FWP_BYTE_ARRAY6); + HLPR_BAIL_ON_ALLOC_FAILURE(pTempFilterConditions[tempConditionIndex].conditionValue.byteArray6, + status); + + status = HlprEthernetMACAddressStringToValue(pString, + pTempFilterConditions[tempConditionIndex].conditionValue.byteArray6->byteArray6); + HLPR_BAIL_ON_FAILURE(status); + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_MAC_REMOTE_ADDRESS + /// -mra + /// -mra == 01:02:03:04:05:06 + if(HlprStringsAreEqual(L"-mra", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_MAC_REMOTE_ADDRESS; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_BYTE_ARRAY6_TYPE; + + HLPR_NEW(pTempFilterConditions[tempConditionIndex].conditionValue.byteArray6, + FWP_BYTE_ARRAY6); + HLPR_BAIL_ON_ALLOC_FAILURE(pTempFilterConditions[tempConditionIndex].conditionValue.byteArray6, + status); + + status = HlprEthernetMACAddressStringToValue(pString, + pTempFilterConditions[tempConditionIndex].conditionValue.byteArray6->byteArray6); + HLPR_BAIL_ON_FAILURE(status); + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_ETHER_TYPE + /// -et + /// -et == ARP + if(HlprStringsAreEqual(L"-et", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 etherType = 0; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_ETHER_TYPE; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT16; + + if(iswdigit((wint_t)pString[0])) + etherType = wcstol(pString, + 0, + 0); + else + etherType = PrvHlprEtherTypeParse(pString); + + if(etherType <= 0xFFFF) + pTempFilterConditions[tempConditionIndex].conditionValue.uint16 = (UINT16)etherType; + else + { + status = ERROR_INVALID_PARAMETER; + + HLPR_BAIL; + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_VLAN_ID + /// -vlid + /// -vlid == 100 + if(HlprStringsAreEqual(L"-vlid", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 vlanID = 0xFFFFFFFF; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_VLAN_ID; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT16; + + if(iswdigit((wint_t)pString[0])) + vlanID = wcstol(pString, + 0, + 0); + + if(vlanID <= 0xFFFF) + pTempFilterConditions[tempConditionIndex].conditionValue.uint16 = (UINT16)vlanID; + else + { + status = ERROR_INVALID_PARAMETER; + + HLPR_BAIL; + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_VSWITCH_TENANT_NETWORK_ID + /// -vstnid + /// -vstnid == 100 + if(HlprStringsAreEqual(L"-vstnid", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 networkID = 0; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_VSWITCH_TENANT_NETWORK_ID; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT32; + + if(iswdigit((wint_t)pString[0])) + { + networkID = wcstol(pString, + 0, + 0); + + pTempFilterConditions[tempConditionIndex].conditionValue.uint32 = networkID; + } + else + { + status = ERROR_INVALID_PARAMETER; + + HLPR_BAIL; + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_NDIS_PORT + /// -np + /// -np == 0 + if(HlprStringsAreEqual(L"-np", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 ndisPort = 0; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_NDIS_PORT; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT32; + + if(iswdigit((wint_t)pString[0])) + { + ndisPort = wcstol(pString, + 0, + 0); + + pTempFilterConditions[tempConditionIndex].conditionValue.uint32 = ndisPort; + } + else + { + status = ERROR_INVALID_PARAMETER; + + HLPR_BAIL; + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_NDIS_MEDIA_TYPE + /// -nmt + /// -nmt == NdisMedium802_3 + if(HlprStringsAreEqual(L"-nmt", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 ndisMediumType = NdisMediumMax; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_NDIS_MEDIA_TYPE; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT32; + + if(iswdigit((wint_t)pString[0])) + ndisMediumType = wcstol(pString, + 0, + 0); + else + ndisMediumType = PrvHlprNDISMediumTypeParse(pString); + + if(ndisMediumType < NdisMediumMax) + pTempFilterConditions[tempConditionIndex].conditionValue.uint32 = ndisMediumType; + else + { + status = ERROR_INVALID_PARAMETER; + + HLPR_BAIL; + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_NDIS_PHYSICAL_MEDIA_TYPE + /// -npmt + if(HlprStringsAreEqual(L"-npmt", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 ndisPhysicalMediumType = NdisPhysicalMediumMax; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_NDIS_PHYSICAL_MEDIA_TYPE; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT32; + + if(iswdigit((wint_t)pString[0])) + ndisPhysicalMediumType = wcstol(pString, + 0, + 0); + else + ndisPhysicalMediumType = PrvHlprNDISPhysicalMediumTypeParse(pString); + + if(ndisPhysicalMediumType < NdisPhysicalMediumMax) + pTempFilterConditions[tempConditionIndex].conditionValue.uint32 = ndisPhysicalMediumType; + else + { + status = ERROR_INVALID_PARAMETER; + + HLPR_BAIL; + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_L2_FLAGS + /// -l2f + /// -l2f == FWP_CONDITION_L2_IS_NATIVE_ETHERNET + if(HlprStringsAreEqual(L"-l2f", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 l2Flags = 0; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_L2_FLAGS; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT32; + + if(iswdigit((wint_t)pString[0])) + l2Flags = wcstol(pString, + 0, + 0); + else + l2Flags = PrvHlprFwpConditionL2FlagParse(pString); + + pTempFilterConditions[tempConditionIndex].conditionValue.uint32 = l2Flags; + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_EQUAL) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_FLAGS_ALL_SET; + else if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_NOT_EQUAL) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_FLAGS_NONE_SET; + else + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_FLAGS_ANY_SET; + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_MAC_LOCAL_ADDRESS_TYPE + /// -mlat + /// -mlat == DlUnicast + if(HlprStringsAreEqual(L"-mlat", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 macAddressType = 0xFFFFFFFF; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_MAC_LOCAL_ADDRESS_TYPE; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT8; + + if(iswdigit((wint_t)pString[0])) + macAddressType = wcstol(pString, + 0, + 0); + else + macAddressType = PrvHlprDataLinkAddressTypeParse(pString); + + if(macAddressType <= DlBroadcast) + pTempFilterConditions[tempConditionIndex].conditionValue.uint8 = (UINT8)macAddressType; + else + { + status = ERROR_INVALID_PARAMETER; + + HLPR_BAIL; + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_MAC_REMOTE_ADDRESS_TYPE + /// -mrat + /// -mrat == DlUnicast + if(HlprStringsAreEqual(L"-mrat", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 macAddressType = 0xFFFFFFFF; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_MAC_REMOTE_ADDRESS_TYPE; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT8; + + if(iswdigit((wint_t)pString[0])) + macAddressType = wcstol(pString, + 0, + 0); + else + macAddressType = PrvHlprDataLinkAddressTypeParse(pString); + + if(macAddressType <= DlBroadcast) + pTempFilterConditions[tempConditionIndex].conditionValue.uint8 = (UINT8)macAddressType; + else + { + status = ERROR_INVALID_PARAMETER; + + HLPR_BAIL; + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_ALE_PACKAGE_ID + /// -apid + /// -apid == WinNullSid + if(HlprStringsAreEqual(L"-apid", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 sidSize = 0; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_ALE_PACKAGE_ID; + + if(HlprStringsAreEqual(L"WinNullSid", + pString) || + HlprStringsAreEqual(L"NullSid", + pString) || + HlprStringsAreEqual(L"Default", + pString)) + { + status = HlprSIDGetWellKnown(WinNullSid, + &(pTempFilterConditions[tempConditionIndex].conditionValue.sid), + &sidSize); + HLPR_BAIL_ON_FAILURE(status); + } + else + { + PSID pSID = 0; + + if(ConvertStringSidToSid(pString, + &pSID) == 0) + { + status = GetLastError(); + + HlprLogInfo(L"FWPM_CONDITION_ALE_PACKAGE_ID: Invalid SID [%s]", + pString); + + HLPR_BAIL; + } + + if(IsValidSid(pSID)) + { + PSID pData = 0; + + sidSize = GetLengthSid(pSID); + + HLPR_NEW_CASTED_ARRAY(pData, + SID, + BYTE, + sidSize); + if(pData == 0) + { + LocalFree(pSID); + + status = (UINT32)STATUS_NO_MEMORY; + + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].conditionValue.sid = (SID*)pData; + + RtlCopyMemory(pTempFilterConditions[tempConditionIndex].conditionValue.sid, + pSID, + sidSize); + } + + LocalFree(pSID); + } + + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_SID; + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_MAC_SOURCE_ADDRESS + /// -msa + /// -msa == 01:02:03:04:05:06 + if(HlprStringsAreEqual(L"-msa", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_MAC_SOURCE_ADDRESS; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_BYTE_ARRAY6_TYPE; + + HLPR_NEW(pTempFilterConditions[tempConditionIndex].conditionValue.byteArray6, + FWP_BYTE_ARRAY6); + HLPR_BAIL_ON_ALLOC_FAILURE(pTempFilterConditions[tempConditionIndex].conditionValue.byteArray6, + status); + + status = HlprEthernetMACAddressStringToValue(pString, + pTempFilterConditions[tempConditionIndex].conditionValue.byteArray6->byteArray6); + HLPR_BAIL_ON_FAILURE(status); + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_MAC_DESTINATION_ADDRESS + /// -mda + /// -mda == 01:02:03:04:05:06 + if(HlprStringsAreEqual(L"-mda", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_MAC_DESTINATION_ADDRESS; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_BYTE_ARRAY6_TYPE; + + HLPR_NEW(pTempFilterConditions[tempConditionIndex].conditionValue.byteArray6, + FWP_BYTE_ARRAY6); + HLPR_BAIL_ON_ALLOC_FAILURE(pTempFilterConditions[tempConditionIndex].conditionValue.byteArray6, + status); + + status = HlprEthernetMACAddressStringToValue(pString, + pTempFilterConditions[tempConditionIndex].conditionValue.byteArray6->byteArray6); + HLPR_BAIL_ON_FAILURE(status); + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_MAC_SOURCE_ADDRESS_TYPE + /// -msat + /// -msat == DlUnicast + if(HlprStringsAreEqual(L"-msat", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 macAddressType = 0xFFFFFFFF; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_MAC_SOURCE_ADDRESS_TYPE; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT8; + + if(iswdigit((wint_t)pString[0])) + macAddressType = wcstol(pString, + 0, + 0); + else + macAddressType = PrvHlprDataLinkAddressTypeParse(pString); + + if(macAddressType <= DlBroadcast) + pTempFilterConditions[tempConditionIndex].conditionValue.uint8 = (UINT8)macAddressType; + else + { + status = ERROR_INVALID_PARAMETER; + + HLPR_BAIL; + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_MAC_DESTINATION_ADDRESS_TYPE + /// -mdat + /// -mdat == DlUnicast + if(HlprStringsAreEqual(L"-mdat", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 macAddressType = 0xFFFFFFFF; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_MAC_DESTINATION_ADDRESS_TYPE; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT8; + + if(iswdigit((wint_t)pString[0])) + macAddressType = wcstol(pString, + 0, + 0); + else + macAddressType = PrvHlprDataLinkAddressTypeParse(pString); + + if(macAddressType <= DlBroadcast) + pTempFilterConditions[tempConditionIndex].conditionValue.uint8 = (UINT8)macAddressType; + else + { + status = ERROR_INVALID_PARAMETER; + + HLPR_BAIL; + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_IP_SOURCE_PORT \ FWPM_CONDITION_VSWITCH_ICMP_TYPE + /// -ipsp \ -vsicmpt + /// -ipsp == 80 + if(HlprStringsAreEqual(L"-ipsp", + ppCLPStrings[stringIndex]) || + HlprStringsAreEqual(L"-vsicmpt", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 port = 0; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_IP_SOURCE_PORT; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT16; + + if(iswdigit((wint_t)pString[0])) + port = wcstol(pString, + 0, + 0); + + if(port <= 0xFFFF) + pTempFilterConditions[tempConditionIndex].conditionValue.uint16 = (UINT16)port; + else + { + status = ERROR_INVALID_PARAMETER; + + HLPR_BAIL; + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_IP_DESTINATION_PORT \ FWPM_CONDITION_VSWITCH_ICMP_CODE + /// -ipdp \ -vsicmpc + /// -ipdp == 80 + if(HlprStringsAreEqual(L"-ipdp", + ppCLPStrings[stringIndex]) || + HlprStringsAreEqual(L"-vsicmpc", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 port = 0; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_IP_DESTINATION_PORT; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT16; + + if(iswdigit((wint_t)pString[0])) + port = wcstol(pString, + 0, + 0); + + if(port <= 0xFFFF) + pTempFilterConditions[tempConditionIndex].conditionValue.uint16 = (UINT16)port; + else + { + status = ERROR_INVALID_PARAMETER; + + HLPR_BAIL; + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_VSWITCH_ID + /// -vsid + /// -vsid == 12345678-1234-1234-1234-123456789012 + if(HlprStringsAreEqual(L"-vsid", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + FWP_BYTE_BLOB* pByteBlob = 0; + size_t size = 0; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_VSWITCH_ID; + + HLPR_NEW(pByteBlob, + FWP_BYTE_BLOB); + HLPR_BAIL_ON_ALLOC_FAILURE(pByteBlob, + status); + + status = StringCbLength(pString, + STRSAFE_MAX_CCH * sizeof(WCHAR), + &size); + if(FAILED(status) || + size != (36 * sizeof(WCHAR))) /// size of a GUID without braces + { + HLPR_DELETE(pByteBlob); + + if(size != (36 * sizeof(WCHAR))) + status = ERROR_INVALID_PARAMETER; + + HLPR_BAIL; + } + + HLPR_NEW_ARRAY(pByteBlob->data, + BYTE, + size); + if(pByteBlob->data == 0) + { + HLPR_DELETE(pByteBlob); + + status = ERROR_OUTOFMEMORY; + + HLPR_BAIL; + } + + pByteBlob->size = (UINT32)size; + + RtlCopyMemory(pByteBlob->data, + pString, + pByteBlob->size); + + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_BYTE_BLOB_TYPE; + pTempFilterConditions[tempConditionIndex].conditionValue.byteBlob = pByteBlob; + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_VSWITCH_NETWORK_TYPE + /// -vsnt + /// -vsnt == 1 + if(HlprStringsAreEqual(L"-vsnt", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 networkType = 0; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_VSWITCH_NETWORK_TYPE; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT8; + + if(iswdigit((wint_t)pString[0])) + networkType = wcstol(pString, + 0, + 0); + else + networkType = PrvHlprVSwitchNetworkTypeParse(pString); + + if(networkType <= FWP_VSWITCH_NETWORK_TYPE_EXTERNAL) + pTempFilterConditions[tempConditionIndex].conditionValue.uint8 = (UINT8)networkType; + else + { + status = ERROR_INVALID_PARAMETER; + + HLPR_BAIL; + } + + tempConditionIndex++; + + continue; + } + + + /// FWPM_CONDITION_VSWITCH_SOURCE_INTERFACE_ID + /// -vssiid + /// -vssiid == 12345678-9ABC-DEF0-1234-56784ABCDEF0--12345678-9ABC-DEF0-1234-56784ABCDEF0 + if(HlprStringsAreEqual(L"-vssiid", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + FWP_BYTE_BLOB* pByteBlob = 0; + size_t size = 0; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_VSWITCH_SOURCE_INTERFACE_ID; + + HLPR_NEW(pByteBlob, + FWP_BYTE_BLOB); + HLPR_BAIL_ON_ALLOC_FAILURE(pByteBlob, + status); + + status = StringCbLength(pString, + STRSAFE_MAX_CCH * sizeof(WCHAR), + &size); + if(FAILED(status) || + size < (36 * sizeof(WCHAR)) || /// size of a GUID with braces + size > (74 * sizeof(WCHAR))) /// size of 2 GUIDs + 2 hyphen separators + { + HLPR_DELETE(pByteBlob); + + if(SUCCEEDED(status)) + status = ERROR_INVALID_PARAMETER; + + HLPR_BAIL; + } + + HLPR_NEW_ARRAY(pByteBlob->data, + BYTE, + size); + if(pByteBlob->data == 0) + { + HLPR_DELETE(pByteBlob); + + status = ERROR_OUTOFMEMORY; + + HLPR_BAIL; + } + + pByteBlob->size = (UINT32)size; + + RtlCopyMemory(pByteBlob->data, + pString, + pByteBlob->size); + + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_BYTE_BLOB_TYPE; + pTempFilterConditions[tempConditionIndex].conditionValue.byteBlob = pByteBlob; + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_VSWITCH_DESTINATION_INTERFACE_ID + /// -vsdiid + /// -vsdiid == 12345678-9ABC-DEF0-1234-56784ABCDEF0--12345678-9ABC-DEF0-1234-56784ABCDEF0 + if(HlprStringsAreEqual(L"-vsdiid", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + FWP_BYTE_BLOB* pByteBlob = 0; + size_t size = 0; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_VSWITCH_DESTINATION_INTERFACE_ID; + + HLPR_NEW(pByteBlob, + FWP_BYTE_BLOB); + HLPR_BAIL_ON_ALLOC_FAILURE(pByteBlob, + status); + + status = StringCbLength(pString, + STRSAFE_MAX_CCH * sizeof(WCHAR), + &size); + if(FAILED(status) || + size < (36 * sizeof(WCHAR)) || /// size of a GUID with braces + size > (74 * sizeof(WCHAR))) + { + HLPR_DELETE(pByteBlob); + + if(SUCCEEDED(status)) + status = ERROR_INVALID_PARAMETER; + + HLPR_BAIL; + } + + HLPR_NEW_ARRAY(pByteBlob->data, + BYTE, + size); + if(pByteBlob->data == 0) + { + HLPR_DELETE(pByteBlob); + + status = ERROR_OUTOFMEMORY; + + HLPR_BAIL; + } + + pByteBlob->size = (UINT32)size; + + RtlCopyMemory(pByteBlob->data, + pString, + pByteBlob->size); + + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_BYTE_BLOB_TYPE; + pTempFilterConditions[tempConditionIndex].conditionValue.byteBlob = pByteBlob; + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_VSWITCH_SOURCE_VM_ID + /// -vssvmid + /// -vssvmid == 12345678-1234-1234-1234-123456789012 + if(HlprStringsAreEqual(L"-vssvmid", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + FWP_BYTE_BLOB* pByteBlob = 0; + size_t size = 0; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_VSWITCH_SOURCE_VM_ID; + + HLPR_NEW(pByteBlob, + FWP_BYTE_BLOB); + HLPR_BAIL_ON_ALLOC_FAILURE(pByteBlob, + status); + + status = StringCbLength(pString, + STRSAFE_MAX_CCH * sizeof(WCHAR), + &size); + if(FAILED(status) || + size != (36 * sizeof(WCHAR))) /// size of a GUID without braces + { + HLPR_DELETE(pByteBlob); + + if(size != (36 * sizeof(WCHAR))) + status = ERROR_INVALID_PARAMETER; + + HLPR_BAIL; + } + + HLPR_NEW_ARRAY(pByteBlob->data, + BYTE, + size); + if(pByteBlob->data == 0) + { + HLPR_DELETE(pByteBlob); + + status = ERROR_OUTOFMEMORY; + + HLPR_BAIL; + } + + pByteBlob->size = (UINT32)size; + + RtlCopyMemory(pByteBlob->data, + pString, + pByteBlob->size); + + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_BYTE_BLOB_TYPE; + pTempFilterConditions[tempConditionIndex].conditionValue.byteBlob = pByteBlob; + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_VSWITCH_DESTINATION_VM_ID + /// -vsdvmid + /// -vsdvmid == 12345678-1234-1234-1234-123456789012 + if(HlprStringsAreEqual(L"-vsdvmid", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + FWP_BYTE_BLOB* pByteBlob = 0; + size_t size = 0; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_VSWITCH_DESTINATION_VM_ID; + + HLPR_NEW(pByteBlob, + FWP_BYTE_BLOB); + HLPR_BAIL_ON_ALLOC_FAILURE(pByteBlob, + status); + + status = StringCbLength(pString, + STRSAFE_MAX_CCH * sizeof(WCHAR), + &size); + if(FAILED(status) || + size != (36 * sizeof(WCHAR))) /// size of a GUID without braces + { + HLPR_DELETE(pByteBlob); + + if(size != (36 * sizeof(WCHAR))) + status = ERROR_INVALID_PARAMETER; + + HLPR_BAIL; + } + + HLPR_NEW_ARRAY(pByteBlob->data, + BYTE, + size); + if(pByteBlob->data == 0) + { + HLPR_DELETE(pByteBlob); + + status = ERROR_OUTOFMEMORY; + + HLPR_BAIL; + } + + pByteBlob->size = (UINT32)size; + + RtlCopyMemory(pByteBlob->data, + pString, + pByteBlob->size); + + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_BYTE_BLOB_TYPE; + pTempFilterConditions[tempConditionIndex].conditionValue.byteBlob = pByteBlob; + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_VSWITCH_SOURCE_INTERFACE_TYPE + /// -vssit + /// -vssit == 0 + if(HlprStringsAreEqual(L"-vssit", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 type = NdisSwitchNicTypeInternal + 1; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_VSWITCH_SOURCE_INTERFACE_TYPE; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT32; + + if(iswdigit((wint_t)pString[0])) + type = wcstol(pString, + 0, + 0); + else + type = PrvHlprVSwitchNICTypeParse(pString); + + if(type <= NdisSwitchNicTypeInternal) + pTempFilterConditions[tempConditionIndex].conditionValue.uint32 = type; + else + { + status = ERROR_INVALID_PARAMETER; + + HLPR_BAIL; + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_VSWITCH_DESTINATION_INTERFACE_TYPE + /// -vsdit + /// -vsdit == 0 + if(HlprStringsAreEqual(L"-vsdit", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 type = NdisSwitchNicTypeInternal + 1; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_VSWITCH_DESTINATION_INTERFACE_TYPE; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT32; + + if(iswdigit((wint_t)pString[0])) + type = wcstol(pString, + 0, + 0); + else + type = PrvHlprVSwitchNICTypeParse(pString); + + if(type <= NdisSwitchNicTypeInternal) + pTempFilterConditions[tempConditionIndex].conditionValue.uint32 = type; + else + { + status = ERROR_INVALID_PARAMETER; + + HLPR_BAIL; + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_ALE_ORIGINAL_APP_ID + /// -aoaid + /// -aoaid == IExplore.exe + if(HlprStringsAreEqual(L"-aoaid", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + FWP_BYTE_BLOB* pAppID = 0; + FWP_BYTE_BLOB* pByteBlob = 0; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_ALE_ORIGINAL_APP_ID; + + status = FwpmGetAppIdFromFileName(pString, + &pAppID); + if(status != NO_ERROR) + { + HLPR_DELETE(pByteBlob); + + HLPR_BAIL; + } + else + { + HLPR_NEW(pByteBlob, + FWP_BYTE_BLOB); + HLPR_BAIL_ON_ALLOC_FAILURE(pByteBlob, + status); + + HLPR_NEW_ARRAY(pByteBlob->data, + BYTE, + pAppID->size); + HLPR_BAIL_ON_ALLOC_FAILURE(pByteBlob->data, + status); + + RtlCopyMemory(pByteBlob->data, + pAppID->data, + pAppID->size); + + pByteBlob->size = pAppID->size; + + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_BYTE_BLOB_TYPE; + pTempFilterConditions[tempConditionIndex].conditionValue.byteBlob = pByteBlob; + + FwpmFreeMemory((VOID**)&pAppID); + + pAppID = 0; + } + + tempConditionIndex++; + + continue; + } + +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + /// FWPM_CONDITION_IP_NEXTHOP_ADDRESS + /// -ipnha + /// -ipnha == 1.2.3.4 + if(HlprStringsAreEqual(L"-ipnha", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_IP_NEXTHOP_ADDRESS; + + if(HlprIPAddressV6StringIsValidFormat(pString)) + { + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_BYTE_ARRAY16_TYPE; + + HLPR_NEW(pTempFilterConditions[tempConditionIndex].conditionValue.byteArray16, + FWP_BYTE_ARRAY16); + HLPR_BAIL_ON_ALLOC_FAILURE(pTempFilterConditions[tempConditionIndex].conditionValue.byteArray16, + status); + + status = HlprIPAddressV6StringToValue(pString, + pTempFilterConditions[tempConditionIndex].conditionValue.byteArray16->byteArray16); + HLPR_BAIL_ON_FAILURE(status); + } + else + { + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT32; + + status = HlprIPAddressV4StringToValue(pString, + &(pTempFilterConditions[tempConditionIndex].conditionValue.uint32)); + HLPR_BAIL_ON_FAILURE(status); + } + + tempConditionIndex++; + + continue; + } + + + /// FWPM_CONDITION_NEXTHOP_SUB_INTERFACE_INDEX + /// -nhsii + /// -nhsii == 0 + if(HlprStringsAreEqual(L"-nhsii", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 index = 0; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_NEXTHOP_SUB_INTERFACE_INDEX; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT32; + + if(iswdigit((wint_t)pString[0])) + { + index = wcstol(pString, + 0, + 0); + + pTempFilterConditions[tempConditionIndex].conditionValue.uint32 = index; + } + else + { + status = ERROR_INVALID_PARAMETER; + + HLPR_BAIL; + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_IP_NEXTHOP_INTERFACE + /// -ipnhi + /// -ipnhi == + if(HlprStringsAreEqual(L"-ipnhi", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT64 nexthopInterface = 0; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_IP_NEXTHOP_INTERFACE; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT64; + + if(iswdigit((wint_t)pString[0])) + nexthopInterface = _wcstoui64(pString, + 0, + 0); + + HLPR_NEW(pTempFilterConditions[tempConditionIndex].conditionValue.uint64, + UINT64); + HLPR_BAIL_ON_ALLOC_FAILURE(pTempFilterConditions[tempConditionIndex].conditionValue.uint64, + status); + + *(pTempFilterConditions[tempConditionIndex].conditionValue.uint64) = nexthopInterface; + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE + /// -nhit + /// -nhit == 0 + if(HlprStringsAreEqual(L"-nhit", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 type = MAX_IF_TYPE; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT32; + + if(iswdigit((wint_t)pString[0])) + type = wcstol(pString, + 0, + 0); + else + type = PrvHlprInterfaceTypeParse(pString); + + if(type < MAX_IF_TYPE) + pTempFilterConditions[tempConditionIndex].conditionValue.uint32 = type; + else + { + status = ERROR_INVALID_PARAMETER; + + HLPR_BAIL; + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE + /// -nhtt + /// -nhtt == 14 + if(HlprStringsAreEqual(L"-nhtt", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 type = 0; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT32; + + if(iswdigit((wint_t)pString[0])) + type = wcstol(pString, + 0, + 0); + else + type = PrvHlprTunnelTypeParse(pString); + + if(type <= TUNNEL_TYPE_IPHTTPS) + pTempFilterConditions[tempConditionIndex].conditionValue.uint32 = type; + else + { + status = ERROR_INVALID_PARAMETER; + + HLPR_BAIL; + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_NEXTHOP_INTERFACE_INDEX + /// -nhii + /// -nhii == 0 + if(HlprStringsAreEqual(L"-nhii", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 index = 0; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_NEXTHOP_INTERFACE_INDEX; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT32; + + if(iswdigit((wint_t)pString[0])) + { + index = wcstol(pString, + 0, + 0); + + pTempFilterConditions[tempConditionIndex].conditionValue.uint32 = index; + } + else + { + status = ERROR_INVALID_PARAMETER; + + HLPR_BAIL; + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_ORIGINAL_PROFILE_ID + /// -opid + /// -opid == 1 + if(HlprStringsAreEqual(L"-opid", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 profileID = NlincCategoryStateMax; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_ORIGINAL_PROFILE_ID; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT32; + + if(iswdigit((wint_t)pString[0])) + profileID = wcstol(pString, + 0, + 0); + else + profileID = PrvHlprProfileIDParse(pString); + + if(profileID < NlincCategoryStateMax) + pTempFilterConditions[tempConditionIndex].conditionValue.uint32 = profileID; + else + { + status = ERROR_INVALID_PARAMETER; + + HLPR_BAIL; + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_CURRENT_PROFILE_ID + /// -cpid + /// -cpid == 1 + if(HlprStringsAreEqual(L"-cpid", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 profileID = NlincCategoryStateMax; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_CURRENT_PROFILE_ID; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT32; + + if(iswdigit((wint_t)pString[0])) + profileID = wcstol(pString, + 0, + 0); + else + profileID = PrvHlprProfileIDParse(pString); + + if(profileID < NlincCategoryStateMax) + pTempFilterConditions[tempConditionIndex].conditionValue.uint32 = profileID; + else + { + status = ERROR_INVALID_PARAMETER; + + HLPR_BAIL; + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_LOCAL_INTERFACE_PROFILE_ID + /// -lipid + /// -lipid == 1 + if(HlprStringsAreEqual(L"-lipid", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 profileID = NlincCategoryStateMax; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_LOCAL_INTERFACE_PROFILE_ID; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT32; + + if(iswdigit((wint_t)pString[0])) + profileID = wcstol(pString, + 0, + 0); + else + profileID = PrvHlprProfileIDParse(pString); + + if(profileID < NlincCategoryStateMax) + pTempFilterConditions[tempConditionIndex].conditionValue.uint32 = profileID; + else + { + status = ERROR_INVALID_PARAMETER; + + HLPR_BAIL; + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_ARRIVAL_INTERFACE_PROFILE_ID + /// -aipid + /// -aipid == 1 + if(HlprStringsAreEqual(L"-aipid", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 profileID = NlincCategoryStateMax; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_ARRIVAL_INTERFACE_PROFILE_ID; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT32; + + if(iswdigit((wint_t)pString[0])) + profileID = wcstol(pString, + 0, + 0); + else + profileID = PrvHlprProfileIDParse(pString); + + if(profileID < NlincCategoryStateMax) + pTempFilterConditions[tempConditionIndex].conditionValue.uint32 = profileID; + else + { + status = ERROR_INVALID_PARAMETER; + + HLPR_BAIL; + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_NEXTHOP_INTERFACE_PROFILE_ID + /// -nhipid + /// -nhipid == 1 + if(HlprStringsAreEqual(L"-nhipid", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 profileID = NlincCategoryStateMax; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_NEXTHOP_INTERFACE_PROFILE_ID; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT32; + + if(iswdigit((wint_t)pString[0])) + profileID = wcstol(pString, + 0, + 0); + else + profileID = PrvHlprProfileIDParse(pString); + + if(profileID < NlincCategoryStateMax) + pTempFilterConditions[tempConditionIndex].conditionValue.uint32 = profileID; + else + { + status = ERROR_INVALID_PARAMETER; + + HLPR_BAIL; + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_REAUTHORIZE_REASON + /// -rr + /// -rr == FWP_CONDITION_REAUTHORIZE_REASON_CLASSIFY_COMPLETION + if(HlprStringsAreEqual(L"-rr", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 flags = 0; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_REAUTHORIZE_REASON; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT32; + + if(iswdigit((wint_t)pString[0])) + flags = wcstol(pString, + 0, + 0); + else + flags = PrvHlprFwpConditionReauthorizeReasonParse(pString); + + pTempFilterConditions[tempConditionIndex].conditionValue.uint32 = flags; + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_EQUAL) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_FLAGS_ALL_SET; + else if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_NOT_EQUAL) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_FLAGS_NONE_SET; + else + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_FLAGS_ANY_SET; + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_ORIGINAL_ICMP_TYPE + /// -oicmpt + /// -oicmpt == 0 + if(HlprStringsAreEqual(L"-oicmpt", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 port = 0; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_ORIGINAL_ICMP_TYPE; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT16; + + if(iswdigit((wint_t)pString[0])) + port = wcstol(pString, + 0, + 0); + + if(port <= 0xFFFF) + pTempFilterConditions[tempConditionIndex].conditionValue.uint16 = (UINT16)port; + else + { + status = ERROR_INVALID_PARAMETER; + + HLPR_BAIL; + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_IP_PHYSICAL_ARRIVAL_INTERFACE + /// -ippai + /// -ippai == + if(HlprStringsAreEqual(L"-ippai", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT64 arrivalInterface = 0; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_IP_PHYSICAL_ARRIVAL_INTERFACE; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT64; + + if(iswdigit((wint_t)pString[0])) + arrivalInterface = _wcstoui64(pString, + 0, + 0); + + HLPR_NEW(pTempFilterConditions[tempConditionIndex].conditionValue.uint64, + UINT64); + HLPR_BAIL_ON_ALLOC_FAILURE(pTempFilterConditions[tempConditionIndex].conditionValue.uint64, + status); + + *(pTempFilterConditions[tempConditionIndex].conditionValue.uint64) = arrivalInterface; + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_IP_PHYSICAL_NEXTHOP_INTERFACE + /// -ippnhi + /// -ippnhi == + if(HlprStringsAreEqual(L"-ippnhi", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT64 nexthopInterface = 0; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_IP_PHYSICAL_NEXTHOP_INTERFACE; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT64; + + if(iswdigit((wint_t)pString[0])) + nexthopInterface = _wcstoui64(pString, + 0, + 0); + + HLPR_NEW(pTempFilterConditions[tempConditionIndex].conditionValue.uint64, + UINT64); + HLPR_BAIL_ON_ALLOC_FAILURE(pTempFilterConditions[tempConditionIndex].conditionValue.uint64, + status); + + *(pTempFilterConditions[tempConditionIndex].conditionValue.uint64) = nexthopInterface; + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_INTERFACE_QUARANTINE_EPOCH + /// -iqe + /// -iqe == 68719476721 + if(HlprStringsAreEqual(L"-iqe", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT64 epoch = 0; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_INTERFACE_QUARANTINE_EPOCH; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT64; + + if(iswdigit((wint_t)pString[0])) + epoch = _wcstoui64(pString, + 0, + 0); + + HLPR_NEW(pTempFilterConditions[tempConditionIndex].conditionValue.uint64, + UINT64); + HLPR_BAIL_ON_ALLOC_FAILURE(pTempFilterConditions[tempConditionIndex].conditionValue.uint64, + status); + + *(pTempFilterConditions[tempConditionIndex].conditionValue.uint64) = epoch; + + tempConditionIndex++; + + continue; + } + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + + /// FWPM_CONDITION_IP_LOCAL_ADDRESS + /// -ipla + /// -ipla == 1.2.3.4 + if(HlprStringsAreEqual(L"-ipla", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_IP_LOCAL_ADDRESS; + + if(HlprIPAddressV6StringIsValidFormat(pString)) + { + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_BYTE_ARRAY16_TYPE; + + HLPR_NEW(pTempFilterConditions[tempConditionIndex].conditionValue.byteArray16, + FWP_BYTE_ARRAY16); + HLPR_BAIL_ON_ALLOC_FAILURE(pTempFilterConditions[tempConditionIndex].conditionValue.byteArray16, + status); + + status = HlprIPAddressV6StringToValue(pString, + pTempFilterConditions[tempConditionIndex].conditionValue.byteArray16->byteArray16); + HLPR_BAIL_ON_FAILURE(status); + } + else + { + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT32; + + status = HlprIPAddressV4StringToValue(pString, + &(pTempFilterConditions[tempConditionIndex].conditionValue.uint32)); + HLPR_BAIL_ON_FAILURE(status); + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_IP_REMOTE_ADDRESS + /// -ipra + /// -ipra == 1.2.3.4 + if(HlprStringsAreEqual(L"-ipra", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_IP_REMOTE_ADDRESS; + + if(HlprIPAddressV6StringIsValidFormat(pString)) + { + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_BYTE_ARRAY16_TYPE; + + HLPR_NEW(pTempFilterConditions[tempConditionIndex].conditionValue.byteArray16, + FWP_BYTE_ARRAY16); + HLPR_BAIL_ON_ALLOC_FAILURE(pTempFilterConditions[tempConditionIndex].conditionValue.byteArray16, + status); + + status = HlprIPAddressV6StringToValue(pString, + pTempFilterConditions[tempConditionIndex].conditionValue.byteArray16->byteArray16); + HLPR_BAIL_ON_FAILURE(status); + } + else + { + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT32; + + status = HlprIPAddressV4StringToValue(pString, + &(pTempFilterConditions[tempConditionIndex].conditionValue.uint32)); + HLPR_BAIL_ON_FAILURE(status); + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_IP_SOURCE_ADDRESS + /// -ipsa + /// -ipsa == 1.2.3.4 + if(HlprStringsAreEqual(L"-ipsa", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_IP_SOURCE_ADDRESS; + + if(HlprIPAddressV6StringIsValidFormat(pString)) + { + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_BYTE_ARRAY16_TYPE; + + HLPR_NEW(pTempFilterConditions[tempConditionIndex].conditionValue.byteArray16, + FWP_BYTE_ARRAY16); + HLPR_BAIL_ON_ALLOC_FAILURE(pTempFilterConditions[tempConditionIndex].conditionValue.byteArray16, + status); + + status = HlprIPAddressV6StringToValue(pString, + pTempFilterConditions[tempConditionIndex].conditionValue.byteArray16->byteArray16); + HLPR_BAIL_ON_FAILURE(status); + } + else + { + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT32; + + status = HlprIPAddressV4StringToValue(pString, + &(pTempFilterConditions[tempConditionIndex].conditionValue.uint32)); + HLPR_BAIL_ON_FAILURE(status); + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_IP_DESTINATION_ADDRESS + /// -ipda + /// -ipda == 1.2.3.4 + if(HlprStringsAreEqual(L"-ipda", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_IP_DESTINATION_ADDRESS; + + if(HlprIPAddressV6StringIsValidFormat(pString)) + { + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_BYTE_ARRAY16_TYPE; + + HLPR_NEW(pTempFilterConditions[tempConditionIndex].conditionValue.byteArray16, + FWP_BYTE_ARRAY16); + HLPR_BAIL_ON_ALLOC_FAILURE(pTempFilterConditions[tempConditionIndex].conditionValue.byteArray16, + status); + + status = HlprIPAddressV6StringToValue(pString, + pTempFilterConditions[tempConditionIndex].conditionValue.byteArray16->byteArray16); + HLPR_BAIL_ON_FAILURE(status); + } + else + { + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT32; + + status = HlprIPAddressV4StringToValue(pString, + &(pTempFilterConditions[tempConditionIndex].conditionValue.uint32)); + HLPR_BAIL_ON_FAILURE(status); + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE + /// -iplat + /// -iplat == NlatUnicast + if(HlprStringsAreEqual(L"-iplat", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 ipAddressType = IPPROTO_MAX; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT8; + + if(iswdigit((wint_t)pString[0])) + ipAddressType = wcstol(pString, + 0, + 0); + else + ipAddressType = PrvHlprIPAddressTypeParse(pString); + + if(ipAddressType < IPPROTO_MAX) + pTempFilterConditions[tempConditionIndex].conditionValue.uint8 = (UINT8)ipAddressType; + else + { + status = ERROR_INVALID_PARAMETER; + + HLPR_BAIL; + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_IP_DESTINATION_ADDRESS_TYPE + /// -ipdat + /// -ipdat == NlatUnicast + if(HlprStringsAreEqual(L"-ipdat", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 ipAddressType = 0; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_IP_DESTINATION_ADDRESS_TYPE; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT8; + + if(iswdigit((wint_t)pString[0])) + ipAddressType = wcstol(pString, + 0, + 0); + else + ipAddressType = PrvHlprIPAddressTypeParse(pString); + + if(ipAddressType < IPPROTO_MAX) + pTempFilterConditions[tempConditionIndex].conditionValue.uint8 = (UINT8)ipAddressType; + else + { + status = ERROR_INVALID_PARAMETER; + + HLPR_BAIL; + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_IP_LOCAL_INTERFACE \ FWPM_CONDITION_INTERFACE + /// -ipli \ -ipi + /// -ipli == + if(HlprStringsAreEqual(L"-ipli", + ppCLPStrings[stringIndex]) || + HlprStringsAreEqual(L"-ipi", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT64 localInterface = 0; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_IP_LOCAL_INTERFACE; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT64; + + if(iswdigit((wint_t)pString[0])) + localInterface = _wcstoui64(pString, + 0, + 0); + + HLPR_NEW(pTempFilterConditions[tempConditionIndex].conditionValue.uint64, + UINT64); + HLPR_BAIL_ON_ALLOC_FAILURE(pTempFilterConditions[tempConditionIndex].conditionValue.uint64, + status); + + *(pTempFilterConditions[tempConditionIndex].conditionValue.uint64) = localInterface; + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_IP_ARRIVAL_INTERFACE + /// -ipai + /// -ipai == + if(HlprStringsAreEqual(L"-ipai", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT64 arrivalInterface = 0; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_IP_ARRIVAL_INTERFACE; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT64; + + if(iswdigit((wint_t)pString[0])) + arrivalInterface = _wcstoui64(pString, + 0, + 0); + + HLPR_NEW(pTempFilterConditions[tempConditionIndex].conditionValue.uint64, + UINT64); + HLPR_BAIL_ON_ALLOC_FAILURE(pTempFilterConditions[tempConditionIndex].conditionValue.uint64, + status); + + *(pTempFilterConditions[tempConditionIndex].conditionValue.uint64) = arrivalInterface; + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE + /// -ait + /// -ait == 0 + if(HlprStringsAreEqual(L"-ait", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 type = MAX_IF_TYPE; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT32; + + if(iswdigit((wint_t)pString[0])) + type = wcstol(pString, + 0, + 0); + else + type = PrvHlprInterfaceTypeParse(pString); + + if(type < MAX_IF_TYPE) + pTempFilterConditions[tempConditionIndex].conditionValue.uint32 = type; + else + { + status = ERROR_INVALID_PARAMETER; + + HLPR_BAIL; + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE + /// -att + /// -att == 14 + if(HlprStringsAreEqual(L"-att", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 type = 0; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT32; + + if(iswdigit((wint_t)pString[0])) + type = wcstol(pString, + 0, + 0); + else + type = PrvHlprTunnelTypeParse(pString); + + if(type <= TUNNEL_TYPE_IPHTTPS) + pTempFilterConditions[tempConditionIndex].conditionValue.uint32 = type; + else + { + status = ERROR_INVALID_PARAMETER; + + HLPR_BAIL; + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_ARRIVAL_INTERFACE_INDEX + /// -aii + /// -aii == 0 + if(HlprStringsAreEqual(L"-aii", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 index = 0; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_ARRIVAL_INTERFACE_INDEX; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT32; + + if(iswdigit((wint_t)pString[0])) + { + index = wcstol(pString, + 0, + 0); + + pTempFilterConditions[tempConditionIndex].conditionValue.uint32 = index; + } + else + { + status = ERROR_INVALID_PARAMETER; + + HLPR_BAIL; + } + + tempConditionIndex++; + + continue; + } + + + /// FWPM_CONDITION_INTERFACE_TYPE \ FWPM_CONDITION_LOCAL_INTERFACE_TYPE + /// -it \ -lit + /// -it == 0 \ -lit == 0 + if(HlprStringsAreEqual(L"-it", + ppCLPStrings[stringIndex]) || + HlprStringsAreEqual(L"-lit", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 type = MAX_IF_TYPE; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_INTERFACE_TYPE; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT32; + + if(iswdigit((wint_t)pString[0])) + type = wcstol(pString, + 0, + 0); + else + type = PrvHlprInterfaceTypeParse(pString); + + if(type < MAX_IF_TYPE) + pTempFilterConditions[tempConditionIndex].conditionValue.uint32 = type; + else + { + status = ERROR_INVALID_PARAMETER; + + HLPR_BAIL; + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_TUNNEL_TYPE \ FWPM_CONDITION_LOCAL_TUNNEL_TYPE + /// -tt \ -ltt + /// -tt == 14 \ -ltt == 14 + if(HlprStringsAreEqual(L"-tt", + ppCLPStrings[stringIndex]) || + HlprStringsAreEqual(L"-ltt", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 type = 0; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_TUNNEL_TYPE; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT32; + + if(iswdigit((wint_t)pString[0])) + type = wcstol(pString, + 0, + 0); + else + type = PrvHlprTunnelTypeParse(pString); + + if(type <= TUNNEL_TYPE_IPHTTPS) + pTempFilterConditions[tempConditionIndex].conditionValue.uint32 = type; + else + { + status = ERROR_INVALID_PARAMETER; + + HLPR_BAIL; + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_IP_FORWARD_INTERFACE + /// -ipfi + /// -ipfi == + if(HlprStringsAreEqual(L"-ipfi", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT64 forwardInterface = 0; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_IP_FORWARD_INTERFACE; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT64; + + if(iswdigit((wint_t)pString[0])) + forwardInterface = _wcstoui64(pString, + 0, + 0); + + HLPR_NEW(pTempFilterConditions[tempConditionIndex].conditionValue.uint64, + UINT64); + HLPR_BAIL_ON_ALLOC_FAILURE(pTempFilterConditions[tempConditionIndex].conditionValue.uint64, + status); + + *(pTempFilterConditions[tempConditionIndex].conditionValue.uint64) = forwardInterface; + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_IP_PROTOCOL + /// -ipp + /// -ipp == TCP + if(HlprStringsAreEqual(L"-ipp", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 protocol = IPPROTO_MAX; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_IP_PROTOCOL; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT8; + + if(iswdigit((wint_t)pString[0])) + protocol = wcstol(pString, + 0, + 0); + else + protocol = PrvHlprProtocolParse(pString); + + if(protocol <= 0xFF) + pTempFilterConditions[tempConditionIndex].conditionValue.uint8 = (UINT8)protocol; + else + { + status = ERROR_INVALID_PARAMETER; + + HLPR_BAIL; + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_IP_LOCAL_PORT \ FWPM_CONDITION_ICMP_TYPE + /// -iplp \ -icmpt + /// -iplp == 80 + if(HlprStringsAreEqual(L"-iplp", + ppCLPStrings[stringIndex]) || + HlprStringsAreEqual(L"-icmpt", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 port = 0; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_IP_LOCAL_PORT; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT16; + + if(iswdigit((wint_t)pString[0])) + port = wcstol(pString, + 0, + 0); + + if(port <= 0xFFFF) + pTempFilterConditions[tempConditionIndex].conditionValue.uint16 = (UINT16)(port & 0xFFFF); + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_IP_REMOTE_PORT \ FWPM_CONDITION_ICMP_CODE + /// -iprp \ -icmpc + /// -iprp == 80 + if(HlprStringsAreEqual(L"-iprp", + ppCLPStrings[stringIndex]) || + HlprStringsAreEqual(L"-icmpc", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 port = 0; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_IP_REMOTE_PORT; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT16; + + if(iswdigit((wint_t)pString[0])) + port = wcstol(pString, + 0, + 0); + + if(port <= 0xFFFF) + pTempFilterConditions[tempConditionIndex].conditionValue.uint16 = (UINT16)port; + else + { + status = ERROR_INVALID_PARAMETER; + + HLPR_BAIL; + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_EMBEDDED_LOCAL_ADDRESS_TYPE + /// -elat + /// -elat == NlatUnicast + if(HlprStringsAreEqual(L"-elat", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 ipAddressType = 0; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_EMBEDDED_LOCAL_ADDRESS_TYPE; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT8; + + if(iswdigit((wint_t)pString[0])) + ipAddressType = wcstol(pString, + 0, + 0); + else + ipAddressType = PrvHlprIPAddressTypeParse(pString); + + pTempFilterConditions[tempConditionIndex].conditionValue.uint8 = (UINT8)ipAddressType; + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_EMBEDDED_REMOTE_ADDRESS + /// -era + /// -era == 1.2.3.4 + if(HlprStringsAreEqual(L"-era", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_EMBEDDED_REMOTE_ADDRESS; + + if(HlprIPAddressV6StringIsValidFormat(pString)) + { + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_BYTE_ARRAY16_TYPE; + + HLPR_NEW(pTempFilterConditions[tempConditionIndex].conditionValue.byteArray16, + FWP_BYTE_ARRAY16); + HLPR_BAIL_ON_ALLOC_FAILURE(pTempFilterConditions[tempConditionIndex].conditionValue.byteArray16, + status); + + status = HlprIPAddressV6StringToValue(pString, + pTempFilterConditions[tempConditionIndex].conditionValue.byteArray16->byteArray16); + HLPR_BAIL_ON_FAILURE(status); + } + else + { + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT32; + + status = HlprIPAddressV4StringToValue(pString, + &(pTempFilterConditions[tempConditionIndex].conditionValue.uint32)); + HLPR_BAIL_ON_FAILURE(status); + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_EMBEDDED_PROTOCOL + /// -ep + /// -ep == TCP + if(HlprStringsAreEqual(L"-ep", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 protocol = IPPROTO_MAX; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_EMBEDDED_PROTOCOL; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT8; + + if(iswdigit((wint_t)pString[0])) + protocol = wcstol(pString, + 0, + 0); + else + protocol = PrvHlprProtocolParse(pString); + + if(protocol <= 0xFF) + pTempFilterConditions[tempConditionIndex].conditionValue.uint8 = (UINT8)(protocol & 0xFF); + else + { + status = ERROR_INVALID_PARAMETER; + + HLPR_BAIL; + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_EMBEDDED_LOCAL_PORT + /// -elp + /// -elp == 80 + if(HlprStringsAreEqual(L"-elp", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 port = 0; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_EMBEDDED_LOCAL_PORT; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT16; + + if(iswdigit((wint_t)pString[0])) + port = wcstol(pString, + 0, + 0); + + if(port <= 0xFFFF) + pTempFilterConditions[tempConditionIndex].conditionValue.uint16 = (UINT16)port; + else + { + status = ERROR_INVALID_PARAMETER; + + HLPR_BAIL; + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_EMBEDDED_REMOTE_PORT + /// -erp + /// -erp == 80 + if(HlprStringsAreEqual(L"-erp", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 port = 0; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_EMBEDDED_REMOTE_PORT; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT16; + + if(iswdigit((wint_t)pString[0])) + port = wcstol(pString, + 0, + 0); + + if(port <= 0xFFFF) + pTempFilterConditions[tempConditionIndex].conditionValue.uint16 = (UINT16)port; + else + { + status = ERROR_INVALID_PARAMETER; + + HLPR_BAIL; + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_FLAGS + /// -f + /// -f == FWP_CONDITION_FLAG_IS_LOOPBACK + if(HlprStringsAreEqual(L"-f", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 flags = 0; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_FLAGS; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT32; + + if(iswdigit((wint_t)pString[0])) + flags = wcstol(pString, + 0, + 0); + else + flags = PrvHlprFwpConditionFlagParse(pString); + + pTempFilterConditions[tempConditionIndex].conditionValue.uint32 = flags; + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_EQUAL) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_FLAGS_ALL_SET; + else if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_NOT_EQUAL) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_FLAGS_NONE_SET; + else + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_FLAGS_ANY_SET; + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_DIRECTION + /// -d + /// -d == 0 + if(HlprStringsAreEqual(L"-d", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 direction = 0; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_DIRECTION; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT32; + + if(iswdigit((wint_t)pString[0])) + direction = wcstol(pString, + 0, + 0); + else + direction = PrvHlprFwpDirectionParse(pString); + + if(direction < FWP_DIRECTION_MAX) + pTempFilterConditions[tempConditionIndex].conditionValue.uint32 = direction; + else + { + status = ERROR_INVALID_PARAMETER; + + HLPR_BAIL; + } + + tempConditionIndex++; + + continue; + } + + + /// FWPM_CONDITION_INTERFACE_INDEX \ FWPM_CONDITION_LOCAL_INTERFACE_INDEX + /// -ii \ -lii + /// -ii == 0 \ -lii == 0 + if(HlprStringsAreEqual(L"-ii", + ppCLPStrings[stringIndex]) || + HlprStringsAreEqual(L"-lii", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 index = 0; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_INTERFACE_INDEX; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT32; + + if(iswdigit((wint_t)pString[0])) + { + index = wcstol(pString, + 0, + 0); + + pTempFilterConditions[tempConditionIndex].conditionValue.uint32 = index; + } + else + { + status = ERROR_INVALID_PARAMETER; + + HLPR_BAIL; + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_SUB_INTERFACE_INDEX \ FWPM_CONDITION_ARRIVAL_SUB_INTERFACE_INDEX + /// -sii \ -asii + /// -sii == 0 \ -asii == 0 + if(HlprStringsAreEqual(L"-sii", + ppCLPStrings[stringIndex]) || + HlprStringsAreEqual(L"-asii", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 index = 0; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_SUB_INTERFACE_INDEX; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT32; + + if(iswdigit((wint_t)pString[0])) + { + index = wcstol(pString, + 0, + 0); + + pTempFilterConditions[tempConditionIndex].conditionValue.uint32 = index; + } + else + { + status = ERROR_INVALID_PARAMETER; + + HLPR_BAIL; + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_SOURCE_INTERFACE_INDEX + /// -sii + /// -sii == 0 + if(HlprStringsAreEqual(L"-sii", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 index = 0; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_SOURCE_INTERFACE_INDEX; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT32; + + if(iswdigit((wint_t)pString[0])) + { + index = wcstol(pString, + 0, + 0); + + pTempFilterConditions[tempConditionIndex].conditionValue.uint32 = index; + } + else + { + status = ERROR_INVALID_PARAMETER; + + HLPR_BAIL; + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_SOURCE_SUB_INTERFACE_INDEX + /// -ssii + /// -ssii == 0 + if(HlprStringsAreEqual(L"-ssii", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 index = 0; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_SOURCE_SUB_INTERFACE_INDEX; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT32; + + if(iswdigit((wint_t)pString[0])) + { + index = wcstol(pString, + 0, + 0); + + pTempFilterConditions[tempConditionIndex].conditionValue.uint32 = index; + } + else + { + status = ERROR_INVALID_PARAMETER; + + HLPR_BAIL; + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_DESTINATION_INTERFACE_INDEX + /// -dii + /// -dii == 0 + if(HlprStringsAreEqual(L"-dii", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 index = 0; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_DESTINATION_INTERFACE_INDEX; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT32; + + if(iswdigit((wint_t)pString[0])) + { + index = wcstol(pString, + 0, + 0); + + pTempFilterConditions[tempConditionIndex].conditionValue.uint32 = index; + } + else + { + status = ERROR_INVALID_PARAMETER; + + HLPR_BAIL; + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_DESTINATION_SUB_INTERFACE_INDEX + /// -dsii + /// -dsii == 0 + if(HlprStringsAreEqual(L"-dsii", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 index = 0; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_DESTINATION_SUB_INTERFACE_INDEX; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT32; + + if(iswdigit((wint_t)pString[0])) + { + index = wcstol(pString, + 0, + 0); + + pTempFilterConditions[tempConditionIndex].conditionValue.uint32 = index; + } + else + { + status = ERROR_INVALID_PARAMETER; + + HLPR_BAIL; + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_ALE_APP_ID + /// -aaid + /// -aaid == IExplore.exe + if(HlprStringsAreEqual(L"-aaid", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + FWP_BYTE_BLOB* pAppID = 0; + FWP_BYTE_BLOB* pByteBlob = 0; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_ALE_APP_ID; + + status = FwpmGetAppIdFromFileName(pString, + &pAppID); + if(status != NO_ERROR) + { + HLPR_DELETE(pByteBlob); + + HLPR_BAIL; + } + else + { + HLPR_NEW(pByteBlob, + FWP_BYTE_BLOB); + HLPR_BAIL_ON_ALLOC_FAILURE(pByteBlob, + status); + + HLPR_NEW_ARRAY(pByteBlob->data, + BYTE, + pAppID->size); + HLPR_BAIL_ON_ALLOC_FAILURE(pByteBlob->data, + status); + + RtlCopyMemory(pByteBlob->data, + pAppID->data, + pAppID->size); + + pByteBlob->size = pAppID->size; + + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_BYTE_BLOB_TYPE; + pTempFilterConditions[tempConditionIndex].conditionValue.byteBlob = pByteBlob; + + FwpmFreeMemory((VOID**)&pAppID); + + pAppID = 0; + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_ALE_USER_ID + /// -auid + /// -auid == Domain\UserName + if(HlprStringsAreEqual(L"-auid", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_ALE_USER_ID; + + if(forEnum) + { + PSID pSID = 0; + + status = PrvHlprSIDGet(pString, + &pSID); + HLPR_BAIL_ON_FAILURE(status); + + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_SID; + pTempFilterConditions[tempConditionIndex].conditionValue.sid = (SID*)pSID; + } + else + { + UINT32 length = 0; + PSECURITY_DESCRIPTOR pAppContainerSecurityDescriptor = 0; + PSECURITY_DESCRIPTOR pSecurityDescriptor = 0; + EXPLICIT_ACCESS explicitAccess = {0}; + + BuildExplicitAccessWithName(&explicitAccess, + (LPWSTR)pString, + FWP_ACTRL_MATCH_FILTER, + GRANT_ACCESS, + 0); + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + if(AppliesToAppContainers(ppCLPStrings, + stringCount)) + pAppContainerSecurityDescriptor = PrvHlprCreateAppContainerSecurityDescriptor(); + +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) + + /// Returns the self-relative security descriptor + status = BuildSecurityDescriptor(0, + 0, + 1, + &explicitAccess, + 0, + 0, + pAppContainerSecurityDescriptor, + (PULONG)&length, + &pSecurityDescriptor); + + HLPR_DELETE_ARRAY(pAppContainerSecurityDescriptor) + + if(status != NO_ERROR) + { + if(status == ERROR_NONE_MAPPED) + HlprLogInfo(L"FWPM_CONDITION_ALE_USER_ID: UserName %s Not Found", + pString); + + status = ERROR_INVALID_PARAMETER; + + HLPR_BAIL; + } + + HLPR_NEW(pTempFilterConditions[tempConditionIndex].conditionValue.sd, + FWP_BYTE_BLOB); + if(pTempFilterConditions[tempConditionIndex].conditionValue.sd == 0) + { + LocalFree(pSecurityDescriptor); + + HLPR_BAIL; + } + + HLPR_NEW_ARRAY(pTempFilterConditions[tempConditionIndex].conditionValue.sd->data, + BYTE, + length); + if(pTempFilterConditions[tempConditionIndex].conditionValue.sd->data == 0) + { + LocalFree(pSecurityDescriptor); + + HLPR_DELETE(pTempFilterConditions[tempConditionIndex].conditionValue.sd); + + HLPR_BAIL; + } + + RtlCopyMemory(pTempFilterConditions[tempConditionIndex].conditionValue.sd->data, + pSecurityDescriptor, + length); + + LocalFree(pSecurityDescriptor); + + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_SECURITY_DESCRIPTOR_TYPE; + pTempFilterConditions[tempConditionIndex].conditionValue.sd->size = length; + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_ALE_REMOTE_USER_ID + /// -aruid + /// -aruid == Domain\UserName + if(HlprStringsAreEqual(L"-aruid", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 length = 0; + PSECURITY_DESCRIPTOR pSecurityDescriptor = 0; + EXPLICIT_ACCESS explicitAccess = {0}; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_ALE_REMOTE_USER_ID; + + if(forEnum) + { + PSID pSID = 0; + + status = PrvHlprSIDGet(pString, + &pSID); + HLPR_BAIL_ON_FAILURE(status); + + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_SID; + pTempFilterConditions[tempConditionIndex].conditionValue.sid = (SID*)pSID; + } + else + { + BuildExplicitAccessWithName(&explicitAccess, + (LPWSTR)pString, + FWP_ACTRL_MATCH_FILTER, + GRANT_ACCESS, + 0); + + /// Returns the self-relative security descriptor + status = BuildSecurityDescriptor(0, + 0, + 1, + &explicitAccess, + 0, + 0, + 0, + (PULONG)&length, + &pSecurityDescriptor); + if(status != NO_ERROR) + { + if(status == ERROR_NONE_MAPPED) + HlprLogInfo(L"FWPM_CONDITION_ALE_REMOTE_USER_ID: UserName %s Not Found", + pString); + + status = ERROR_INVALID_PARAMETER; + + HLPR_BAIL; + } + + HLPR_NEW(pTempFilterConditions[tempConditionIndex].conditionValue.sd, + FWP_BYTE_BLOB); + if(pTempFilterConditions[tempConditionIndex].conditionValue.sd == 0) + { + LocalFree(pSecurityDescriptor); + + HLPR_BAIL; + } + + HLPR_NEW_ARRAY(pTempFilterConditions[tempConditionIndex].conditionValue.sd->data, + BYTE, + length); + if(pTempFilterConditions[tempConditionIndex].conditionValue.sd->data == 0) + { + LocalFree(pSecurityDescriptor); + + HLPR_DELETE(pTempFilterConditions[tempConditionIndex].conditionValue.sd); + + HLPR_BAIL; + } + + RtlCopyMemory(pTempFilterConditions[tempConditionIndex].conditionValue.sd->data, + pSecurityDescriptor, + length); + + LocalFree(pSecurityDescriptor); + + pTempFilterConditions[tempConditionIndex].conditionValue.type = forEnum ? FWP_SID : FWP_SECURITY_DESCRIPTOR_TYPE; + pTempFilterConditions[tempConditionIndex].conditionValue.sd->size = length; + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_ALE_REMOTE_MACHINE_ID + /// -armid + /// -armid == MachineName + if(HlprStringsAreEqual(L"-armid", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 length = 0; + PSECURITY_DESCRIPTOR pSecurityDescriptor = 0; + EXPLICIT_ACCESS explicitAccess = {0}; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_ALE_REMOTE_MACHINE_ID; + + if(forEnum) + { + PSID pSID = 0; + + status = PrvHlprSIDGet(pString, + &pSID); + HLPR_BAIL_ON_FAILURE(status); + + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_SID; + pTempFilterConditions[tempConditionIndex].conditionValue.sid = (SID*)pSID; + } + else + { + BuildExplicitAccessWithName(&explicitAccess, + (LPWSTR)pString, + FWP_ACTRL_MATCH_FILTER, + GRANT_ACCESS, + 0); + + /// Returns the self-relative security descriptor + status = BuildSecurityDescriptor(0, + 0, + 1, + &explicitAccess, + 0, + 0, + 0, + (PULONG)&length, + &pSecurityDescriptor); + if(status != NO_ERROR) + { + if(status == ERROR_NONE_MAPPED) + HlprLogInfo(L"FWPM_CONDITION_ALE_REMOTE_MACHINE_ID: MachineName %s Not Found", + pString); + + status = ERROR_INVALID_PARAMETER; + + HLPR_BAIL; + } + + HLPR_NEW(pTempFilterConditions[tempConditionIndex].conditionValue.sd, + FWP_BYTE_BLOB); + if(pTempFilterConditions[tempConditionIndex].conditionValue.sd == 0) + { + LocalFree(pSecurityDescriptor); + + HLPR_BAIL; + } + + HLPR_NEW_ARRAY(pTempFilterConditions[tempConditionIndex].conditionValue.sd->data, + BYTE, + length); + if(pTempFilterConditions[tempConditionIndex].conditionValue.sd->data == 0) + { + LocalFree(pSecurityDescriptor); + + HLPR_DELETE(pTempFilterConditions[tempConditionIndex].conditionValue.sd); + + HLPR_BAIL; + } + + RtlCopyMemory(pTempFilterConditions[tempConditionIndex].conditionValue.sd->data, + pSecurityDescriptor, + length); + + LocalFree(pSecurityDescriptor); + + pTempFilterConditions[tempConditionIndex].conditionValue.type = forEnum ? FWP_SID : FWP_SECURITY_DESCRIPTOR_TYPE; + pTempFilterConditions[tempConditionIndex].conditionValue.sd->size = length; + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_ALE_PROMISCUOUS_MODE + /// -apm + /// -apm == 0 + if(HlprStringsAreEqual(L"-apm", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 context = 0; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_ALE_PROMISCUOUS_MODE; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT32; + + if(iswdigit((wint_t)pString[0])) + { + context = wcstol(pString, + 0, + 0); + + pTempFilterConditions[tempConditionIndex].conditionValue.uint32 = context; + } + else + { + status = ERROR_INVALID_PARAMETER; + + HLPR_BAIL; + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_ALE_SIO_FIREWALL_SYSTEM_PORT \ FWPM_CONDITION_ALE_SIO_FIREWALL_SOCKET_PROPERTY + /// -asfsp + /// -asfsp == 0 + if(HlprStringsAreEqual(L"-asfsp", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 context = 0; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_ALE_SIO_FIREWALL_SYSTEM_PORT; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT32; + + if(iswdigit((wint_t)pString[0])) + { + context = wcstol(pString, + 0, + 0); + + pTempFilterConditions[tempConditionIndex].conditionValue.uint32 = context; + } + else + { + status = ERROR_INVALID_PARAMETER; + + HLPR_BAIL; + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_ALE_NAP_CONTEXT + /// -anc + /// -anc == 0 + if(HlprStringsAreEqual(L"-anc", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 context = 0; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_ALE_NAP_CONTEXT; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT32; + + if(iswdigit((wint_t)pString[0])) + { + context = wcstol(pString, + 0, + 0); + + pTempFilterConditions[tempConditionIndex].conditionValue.uint32 = context; + } + else + { + status = ERROR_INVALID_PARAMETER; + + HLPR_BAIL; + } + + tempConditionIndex++; + + continue; + } + +/// These can be implemented later as they apply only to UM layers and none of these are demo'd yet + + /// FWPM_CONDITION_REMOTE_USER_TOKEN + /// FWPM_CONDITION_RPC_IF_UUID + + /// FWPM_CONDITION_RPC_IF_VERSION + /// -riv + /// -riv == 1 + if(HlprStringsAreEqual(L"-riv", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 version = 0; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_RPC_IF_VERSION; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT16; + + if(iswdigit((wint_t)pString[0])) + version = wcstol(pString, + 0, + 0); + else + HLPR_BAIL; + + if(version <= 0xFFFF) + pTempFilterConditions[tempConditionIndex].conditionValue.uint16 = (UINT16)version; + else + { + status = ERROR_INVALID_PARAMETER; + + HLPR_BAIL; + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_RPC_IF_FLAG + /// -rif + /// -rif == 1 + if(HlprStringsAreEqual(L"-rif", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 flag = 0; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_RPC_IF_FLAG; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT32; + + if(iswdigit((wint_t)pString[0])) + flag = wcstol(pString, + 0, + 0); + else + flag = PrvHlprRPCIFFlagParse(pString); + + pTempFilterConditions[tempConditionIndex].conditionValue.uint32 = flag; + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_DCOM_APP_ID + /// FWPM_CONDITION_IMAGE_NAME + + /// FWPM_CONDITION_RPC_PROTOCOL + /// -rp + /// -rp == NCACN_IP_TCP + if(HlprStringsAreEqual(L"-rp", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 protocol = 0; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_RPC_PROTOCOL; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT8; + + if(iswdigit((wint_t)pString[0])) + protocol = wcstol(pString, + 0, + 0); + else + protocol = PrvHlprRPCProtocolParse(pString); + + if(protocol <= 0xFF) + pTempFilterConditions[tempConditionIndex].conditionValue.uint8 = (UINT8)protocol; + else + { + status = ERROR_INVALID_PARAMETER; + + HLPR_BAIL; + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_RPC_AUTH_TYPE + /// -rat + /// -rat == WinNT + if(HlprStringsAreEqual(L"-rat", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 authType = 0; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_RPC_AUTH_TYPE; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT8; + + if(iswdigit((wint_t)pString[0])) + authType = wcstol(pString, + 0, + 0); + else + authType = PrvHlprRPCAuthTypeParse(pString); + + if(authType <= 0xFF) + pTempFilterConditions[tempConditionIndex].conditionValue.uint8 = (UINT8)authType; + else + { + status = ERROR_INVALID_PARAMETER; + + HLPR_BAIL; + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_RPC_AUTH_LEVEL + /// -ral + /// -ral == Default + if(HlprStringsAreEqual(L"-ral", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 authLevel = 0; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_RPC_AUTH_LEVEL; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT8; + + if(iswdigit((wint_t)pString[0])) + authLevel = wcstol(pString, + 0, + 0); + else + authLevel = PrvHlprRPCAuthLevelParse(pString); + + if(authLevel <= 0xFF) + pTempFilterConditions[tempConditionIndex].conditionValue.uint8 = (UINT8)authLevel; + else + { + status = ERROR_INVALID_PARAMETER; + + HLPR_BAIL; + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_SEC_ENCRYPT_ALGORITHM + /// -sea + /// -sea == 1 + if(HlprStringsAreEqual(L"-sea", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 algorithm = 0; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_SEC_ENCRYPT_ALGORITHM; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT32; + + if(iswdigit((wint_t)pString[0])) + { + algorithm = wcstol(pString, + 0, + 0); + + pTempFilterConditions[tempConditionIndex].conditionValue.uint32 = algorithm; + } + else + { + status = ERROR_INVALID_PARAMETER; + + HLPR_BAIL; + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_SEC_KEY_SIZE + /// -sks + /// -sks == 128 + if(HlprStringsAreEqual(L"-sks", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 keySize = 0; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_SEC_KEY_SIZE; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT32; + + if(iswdigit((wint_t)pString[0])) + { + keySize = wcstol(pString, + 0, + 0); + + pTempFilterConditions[tempConditionIndex].conditionValue.uint32 = keySize; + } + else + { + status = ERROR_INVALID_PARAMETER; + + HLPR_BAIL; + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_IP_LOCAL_ADDRESS_V4 + /// -iplav4 + /// -iplav4 == 1.2.3.4 + if(HlprStringsAreEqual(L"-iplav4", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_IP_LOCAL_ADDRESS_V4; + + if(HlprIPAddressV4StringIsValidFormat(pString)) + { + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT32; + + status = HlprIPAddressV4StringToValue(pString, + &(pTempFilterConditions[tempConditionIndex].conditionValue.uint32)); + HLPR_BAIL_ON_FAILURE(status); + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_IP_LOCAL_ADDRESS_V6 + /// -iplav6 + /// -iplav6 == 1:2:3:4:5:6:7:8 + if(HlprStringsAreEqual(L"-iplav6", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_IP_LOCAL_ADDRESS_V6; + + if(HlprIPAddressV6StringIsValidFormat(pString)) + { + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_BYTE_ARRAY16_TYPE; + + HLPR_NEW(pTempFilterConditions[tempConditionIndex].conditionValue.byteArray16, + FWP_BYTE_ARRAY16); + HLPR_BAIL_ON_ALLOC_FAILURE(pTempFilterConditions[tempConditionIndex].conditionValue.byteArray16, + status); + + status = HlprIPAddressV6StringToValue(pString, + pTempFilterConditions[tempConditionIndex].conditionValue.byteArray16->byteArray16); + HLPR_BAIL_ON_FAILURE(status); + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_PIPE + + /// FWPM_CONDITION_IP_REMOTE_ADDRESS_V4 + /// -iprav4 + /// -iprav4 == 127.0.0.1 + if(HlprStringsAreEqual(L"-iprav4", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_IP_REMOTE_ADDRESS_V4; + + if(HlprIPAddressV4StringIsValidFormat(pString)) + { + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT32; + + status = HlprIPAddressV4StringToValue(pString, + &(pTempFilterConditions[tempConditionIndex].conditionValue.uint32)); + HLPR_BAIL_ON_FAILURE(status); + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_IP_REMOTE_ADDRESS_V6 + /// -iprav6 + /// -iprav6 == 1:2:3:4:5:6:7:8 + if(HlprStringsAreEqual(L"-iprav6", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_IP_REMOTE_ADDRESS_V6; + + if(HlprIPAddressV6StringIsValidFormat(pString)) + { + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_BYTE_ARRAY16_TYPE; + + HLPR_NEW(pTempFilterConditions[tempConditionIndex].conditionValue.byteArray16, + FWP_BYTE_ARRAY16); + HLPR_BAIL_ON_ALLOC_FAILURE(pTempFilterConditions[tempConditionIndex].conditionValue.byteArray16, + status); + + status = HlprIPAddressV6StringToValue(pString, + pTempFilterConditions[tempConditionIndex].conditionValue.byteArray16->byteArray16); + HLPR_BAIL_ON_FAILURE(status); + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_PROCESS_WITH_RPC_IF_UUID + /// FWPM_CONDITION_RPC_EP_VALUE + + /// FWPM_CONDITION_RPC_EP_FLAGS + /// -ref + /// -ref == 1 + if(HlprStringsAreEqual(L"-ref", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 flag = 0; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_RPC_EP_FLAGS; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT32; + + if(iswdigit((wint_t)pString[0])) + flag = wcstol(pString, + 0, + 0); + else + flag = PrvHlprRPCIFFlagParse(pString); + + pTempFilterConditions[tempConditionIndex].conditionValue.uint32 = flag; + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_CLIENT_TOKEN + /// FWPM_CONDITION_RPC_SERVER_NAME + + /// FWPM_CONDITION_RPC_SERVER_PORT + /// -rsp + /// -rsp == 1 + if(HlprStringsAreEqual(L"-rsp", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 port = 0xFFFFFFFF; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_RPC_SERVER_PORT; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT16; + + if(iswdigit((wint_t)pString[0])) + port = wcstol(pString, + 0, + 0); + + if(port <= 0xFFFF) + pTempFilterConditions[tempConditionIndex].conditionValue.uint16 = (UINT16)port; + else + { + status = ERROR_INVALID_PARAMETER; + + HLPR_BAIL; + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_RPC_PROXY_AUTH_TYPE + + /// FWPM_CONDITION_CLIENT_CERT_KEY_LENGTH + /// -cckl + /// -cckl == 1 + if(HlprStringsAreEqual(L"-cckl", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 keyLength = 0; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_CLIENT_CERT_KEY_LENGTH; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT32; + + if(iswdigit((wint_t)pString[0])) + { + keyLength = wcstol(pString, + 0, + 0); + + pTempFilterConditions[tempConditionIndex].conditionValue.uint32 = keyLength; + } + else + { + status = ERROR_INVALID_PARAMETER; + + HLPR_BAIL; + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_CLIENT_CERT_OID + /// FWPM_CONDITION_NET_EVENT_TYPE + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + /// FWPM_CONDITION_KM_AUTH_NAP_CONTEXT + /// -kanc + /// -kanc == 1 + if(HlprStringsAreEqual(L"-kanc", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 context = 0; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_KM_AUTH_NAP_CONTEXT; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT32; + + if(iswdigit((wint_t)pString[0])) + { + context = wcstol(pString, + 0, + 0); + + pTempFilterConditions[tempConditionIndex].conditionValue.uint32 = context; + } + else + { + status = ERROR_INVALID_PARAMETER; + + HLPR_BAIL; + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_PEER_NAME + /// FWPM_CONDITION_REMOTE_ID + + /// FWPM_CONDITION_AUTHENTICATION_TYPE + /// -at + /// -at == 1 + if(HlprStringsAreEqual(L"-at", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 type = 0; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_AUTHENTICATION_TYPE; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT32; + + if(iswdigit((wint_t)pString[0])) + { + type = wcstol(pString, + 0, + 0); + + pTempFilterConditions[tempConditionIndex].conditionValue.uint32 = type; + } + else + { + status = ERROR_INVALID_PARAMETER; + + HLPR_BAIL; + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_KM_TYPE + /// -kt + /// -kt == 1 + if(HlprStringsAreEqual(L"-kt", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 type = 0; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_KM_TYPE; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT32; + + if(iswdigit((wint_t)pString[0])) + { + type = wcstol(pString, + 0, + 0); + + pTempFilterConditions[tempConditionIndex].conditionValue.uint32 = type; + } + else + { + status = ERROR_INVALID_PARAMETER; + + HLPR_BAIL; + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_KM_MODE + /// -km + /// -km == 1 + if(HlprStringsAreEqual(L"-km", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 mode = 0; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_KM_MODE; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT32; + + if(iswdigit((wint_t)pString[0])) + { + mode = wcstol(pString, + 0, + 0); + + pTempFilterConditions[tempConditionIndex].conditionValue.uint32 = mode; + } + else + { + status = ERROR_INVALID_PARAMETER; + + HLPR_BAIL; + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_IPSEC_POLICY_KEY + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + /// FWPM_CONDITION_QM_MODE + /// -qm + /// -qm == 1 + if(HlprStringsAreEqual(L"-qm", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + UINT32 mode = 0; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_QM_MODE; + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_UINT32; + + if(iswdigit((wint_t)pString[0])) + { + mode = wcstol(pString, + 0, + 0); + + pTempFilterConditions[tempConditionIndex].conditionValue.uint32 = mode; + } + else + { + status = ERROR_INVALID_PARAMETER; + + HLPR_BAIL; + } + + tempConditionIndex++; + + continue; + } + +#if(NTDDI_VERSION >= NTDDI_WINTHRESHOLD) + + /// FWPM_CONDITION_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE + /// -asfv + /// -asfv == "CN=" + if(HlprStringsAreEqual(L"-asafv", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + FWP_BYTE_BLOB* pByteBlob = 0; + size_t stringSize = 0; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_IPSEC_SECURITY_REALM_ID; + + status = StringCbLength(pString, + STRSAFE_MAX_CCH * sizeof(WCHAR), + &stringSize); + if(FAILED(status)) + HLPR_BAIL; + else + { + HLPR_NEW(pByteBlob, + FWP_BYTE_BLOB); + HLPR_BAIL_ON_ALLOC_FAILURE(pByteBlob, + status); + + HLPR_NEW_ARRAY(pByteBlob->data, + BYTE, + stringSize); + HLPR_BAIL_ON_ALLOC_FAILURE(pByteBlob->data, + status); + + RtlCopyMemory(pByteBlob->data, + pString, + stringSize); + + pByteBlob->size = (UINT32)stringSize; + + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_BYTE_BLOB_TYPE; + pTempFilterConditions[tempConditionIndex].conditionValue.byteBlob = pByteBlob; + } + + tempConditionIndex++; + + continue; + } + + /// FWPM_CONDITION_IPSEC_SECURITY_REALM_ID + /// -isrid + /// -isrid == MyRealm + if(HlprStringsAreEqual(L"-isrid", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + FWP_BYTE_BLOB* pByteBlob = 0; + size_t stringSize = 0; + + pTempFilterConditions[tempConditionIndex].matchType = PrvHlprCommandLineStringToFwpMatchType(pString); + + if(pTempFilterConditions[tempConditionIndex].matchType == FWP_MATCH_TYPE_MAX) + pTempFilterConditions[tempConditionIndex].matchType = FWP_MATCH_EQUAL; + else + { + if((stringIndex + 1) < stringCount) + pString = ppCLPStrings[++stringIndex]; + else + HLPR_BAIL; + } + + pTempFilterConditions[tempConditionIndex].fieldKey = FWPM_CONDITION_IPSEC_SECURITY_REALM_ID; + + status = StringCbLength(pString, + STRSAFE_MAX_CCH * sizeof(WCHAR), + &stringSize); + if(FAILED(status)) + HLPR_BAIL; + else + { + HLPR_NEW(pByteBlob, + FWP_BYTE_BLOB); + HLPR_BAIL_ON_ALLOC_FAILURE(pByteBlob, + status); + + HLPR_NEW_ARRAY(pByteBlob->data, + BYTE, + stringSize); + HLPR_BAIL_ON_ALLOC_FAILURE(pByteBlob->data, + status); + + RtlCopyMemory(pByteBlob->data, + pString, + stringSize); + + pByteBlob->size = (UINT32)stringSize; + + pTempFilterConditions[tempConditionIndex].conditionValue.type = FWP_BYTE_BLOB_TYPE; + pTempFilterConditions[tempConditionIndex].conditionValue.byteBlob = pByteBlob; + } + + tempConditionIndex++; + + continue; + } + +#endif /// (NTDDI_VERSION >= NTDDI_WINTHRESHOLD) +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + + HLPR_BAIL_LABEL: + + if(status != NO_ERROR) + HlprFwpmFilterConditionPurge(&(pTempFilterConditions[tempConditionIndex])); + } + +#pragma warning(pop) + + if(tempConditionIndex) + status = PrvHlprFwpmFilterConditionsSort(pTempFilterConditions, + tempConditionIndex, + pFilter); + + HLPR_BAIL_LABEL_2: + + HLPR_DELETE_ARRAY(pTempFilterConditions); + + return status; +} + +/** + @helper_function="HlprCommandLineParseForFilterInfo" + + Purpose: Parse the command line parameters for strings which detail:
+ -l - which layer to have the filter reside.
+ -b - mark the filter and corresponding objects for boot time.
+ -v - mark the filter and corresponding objects as non-persistent.
+ -c - mark the filter for using a callout.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/DD744934.aspx
+*/ +_Success_(return == NO_ERROR) +UINT32 HlprCommandLineParseForFilterInfo(_In_reads_(stringCount) PCWSTR* ppCLPStrings, + _In_ UINT32 stringCount, + _Inout_ FWPM_FILTER* pFilter, + _In_ BOOLEAN forEnum) /* FALSE */ +{ + ASSERT(ppCLPStrings); + ASSERT(stringCount); + ASSERT(pFilter); + + UINT32 status = NO_ERROR; + + pFilter->flags |= FWPM_FILTER_FLAG_PERSISTENT; + + /// -l + status = HlprCommandLineParseForLayerKey(ppCLPStrings, + stringCount, + pFilter); + HLPR_BAIL_ON_FAILURE(status); + + /// -s + status = HlprCommandLineParseForSubLayerKey(ppCLPStrings, + stringCount, + pFilter); + HLPR_BAIL_ON_FAILURE(status); + + /// + /// -ipla 1.0.0.1 + status = HlprCommandLineParseForFilterConditions(ppCLPStrings, + stringCount, + pFilter, + forEnum); + HLPR_BAIL_ON_FAILURE(status); + + /// -b + HlprCommandLineParseForBootTime(ppCLPStrings, + stringCount, + pFilter); + + if(!(pFilter->flags & FWPM_FILTER_FLAG_BOOTTIME)) + { + /// -v + HlprCommandLineParseForVolatility(ppCLPStrings, + stringCount, + pFilter); + } + + /// -c + HlprCommandLineParseForCalloutUse(ppCLPStrings, + stringCount, + pFilter); + + HLPR_BAIL_LABEL: + + return status; +} diff --git a/network/trans/WFPSampler/exe/HelperFunctions_CommandLine.h b/network/trans/WFPSampler/exe/HelperFunctions_CommandLine.h new file mode 100644 index 000000000..65531e341 --- /dev/null +++ b/network/trans/WFPSampler/exe/HelperFunctions_CommandLine.h @@ -0,0 +1,63 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_CommandLine.h +// +// Abstract: +// This module contains functions which assist in parsing information from the command prompt. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Add support for specifying a different sublayer +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef HELPERFUNCTIONS_COMMAND_LINE_H +#define HELPERFUNCTIONS_COMMAND_LINE_H + +VOID HlprCommandLineParseForScenarioRemoval(_In_reads_(stringCount) PCWSTR* ppCLPStrings, + _In_ UINT32 stringCount, + _Inout_ BOOLEAN* pRemoveScenario); + +VOID HlprCommandLineParseForBootTime(_In_reads_(stringCount) PCWSTR* ppCLPStrings, + _In_ UINT32 stringCount, + _Inout_ FWPM_FILTER* pFilter); + +VOID HlprCommandLineParseForCalloutUse(_In_reads_(stringCount) PCWSTR* ppCLPStrings, + _In_ UINT32 stringCount, + _Inout_ FWPM_FILTER* pFilter); + +VOID HlprCommandLineParseForVolatility(_In_reads_(stringCount) PCWSTR* ppCLPStrings, + _In_ UINT32 stringCount, + _Inout_ FWPM_FILTER* pFilter); + +_Success_(return == NO_ERROR) +UINT32 HlprCommandLineParseForLayerKey(_In_reads_(stringCount) PCWSTR* ppCLPStrings, + _In_ UINT32 stringCount, + _Inout_ FWPM_FILTER* pFilter); + +_Success_(return == NO_ERROR) +UINT32 HlprCommandLineParseForSubLayerKey(_In_reads_(stringCount) PCWSTR* ppCLPStrings, + _In_ UINT32 stringCount, + _Inout_ FWPM_FILTER* pFilter); + +_Success_(return == NO_ERROR) +UINT32 HlprCommandLineParseForFilterConditions(_In_reads_(stringCount) PCWSTR* ppCLPStrings, + _In_ UINT32 stringCount, + _Inout_ FWPM_FILTER* pFilter, + _In_ BOOLEAN forEnum = FALSE); + +_Success_(return == NO_ERROR) +UINT32 HlprCommandLineParseForFilterInfo(_In_reads_(stringCount) PCWSTR* ppCLPStrings, + _In_ UINT32 stringCount, + _Inout_ FWPM_FILTER* pFilter, + _In_ BOOLEAN forEnum = FALSE); + +#endif /// HELPERFUNCTIONS_COMMAND_LINE_H \ No newline at end of file diff --git a/network/trans/WFPSampler/exe/Scenarios_AdvancedPacketInjection.cpp b/network/trans/WFPSampler/exe/Scenarios_AdvancedPacketInjection.cpp new file mode 100644 index 000000000..5867cd352 --- /dev/null +++ b/network/trans/WFPSampler/exe/Scenarios_AdvancedPacketInjection.cpp @@ -0,0 +1,260 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// Scenarios_AdvancedPacketInjection.cpp +// +// Abstract: +// This module contains functions which prepares and sends data for the +// ADVANCED_PACKET_INJECTION scenario implementation. +// +// Naming Convention: +// +// +// +// i.e. +// +// +// { +// - Function is likely visible to other modules. +// Prv - Function is private to this module. +// } +// +// { +// AdvancedPacketInjectionScenario - Function pertains to Advanced Packet Injection +// Scenario. +// } +// +// { +// Execute - Function packages data and invokes RPC to the +// WFPSampler service +// Log - Function writes to the console. +// Parse - Function pulls data into the required format from +// the provided data. +// } +// +// { +// Help - Function provides context sensitive help for the +// scenario. +// InjectionData - Function acts on the +// PC_ADVANCED_PACKET_INJECTION_DATA. +// } +// +// Private Functions: +// PrvScenarioAdvancedPacketInjectionParseInjectionData(), +// +// Public Functions: +// AdvancedPacketInjectionScenarioExecute(), +// AdvancedPacketInjectionScenarioLogHelp(), +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// December 13, 2013 - 1.1 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "Framework_WFPSampler.h" /// . + +/** + @private_function="PrvAdvancedPacketInjectionScenarioParseInjectionData" + + Purpose: Parse the command line parameters for implementing packet injection such as:
+ Perform the injection inline (from within the classify) (-in)
+ Use threaded DPCs for out of band (asynchronous) (-tdpc)
+ Use work items for out of band (asynchronous) (-wi)
+ Allocate additional Bytes at teh end of teh payload (-ab)
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 PrvAdvancedPacketInjectionScenarioParseInjectionData(_In_reads_(stringCount) PCWSTR* ppCLPStrings, + _In_ const UINT32 stringCount, + _Inout_ PC_ADVANCED_PACKET_INJECTION_DATA* pPCAdvancedPacketInjectionData) +{ + ASSERT(ppCLPStrings); + ASSERT(stringCount); + ASSERT(pPCAdvancedPacketInjectionData); + + UINT32 status = NO_ERROR; + const UINT32 MAX_PARAMETERS = 5; + UINT32 found = 0; + + for(UINT32 stringIndex = 0; + stringIndex < stringCount && + found != MAX_PARAMETERS; + stringIndex++) + { + /// Inline Injection + if(HlprStringsAreEqual(L"-in", + ppCLPStrings[stringIndex])) + { + pPCAdvancedPacketInjectionData->performInline = TRUE; + + found++; + + continue; + } + + /// Threaded DPC + if(HlprStringsAreEqual(L"-tdpc", + ppCLPStrings[stringIndex])) + { + pPCAdvancedPacketInjectionData->useThreadedDPC = TRUE; + + found++; + + continue; + } + + /// Work Items + if(HlprStringsAreEqual(L"-wi", + ppCLPStrings[stringIndex])) + { + pPCAdvancedPacketInjectionData->useWorkItems = TRUE; + + found++; + + continue; + } + + if((stringIndex + 1) < stringCount) + { + /// Additional Bytes to Allocate + if(HlprStringsAreEqual(L"-ab", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[stringIndex + 1]; + + if(iswdigit((wint_t)pString[0])) + { + UINT32 additionalBytes = wcstol(pString, + 0, + 0); + + pPCAdvancedPacketInjectionData->additionalBytes = additionalBytes; + } + } + } + } + + return status; +} + +/** + @scenario_function="AdvancedPacketInjectionScenarioExecute" + + Purpose: Gather and package data neccessary to setup the ADVANCED_PACKET_INJECTION scenario, + then invoke RPC to implement the scenario in the WFPSampler service.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 AdvancedPacketInjectionScenarioExecute(_In_reads_(stringCount) PCWSTR* ppCLPStrings, + _In_ const UINT32 stringCount) +{ + ASSERT(ppCLPStrings); + ASSERT(stringCount); + + UINT32 status = NO_ERROR; + BOOLEAN removeScenario = FALSE; + PC_ADVANCED_PACKET_INJECTION_DATA* pPCAdvancedPacketInjectionData = 0; + FWPM_FILTER* pFilter = 0; + + status = HlprFwpmFilterCreate(&pFilter); + HLPR_BAIL_ON_FAILURE(status); + + pFilter->displayData.name = L"WFPSampler's Advanced Packet Injection Scenario Filter"; + + HlprCommandLineParseForScenarioRemoval(ppCLPStrings, + stringCount, + &removeScenario); + + status = HlprCommandLineParseForFilterInfo(ppCLPStrings, + stringCount, + pFilter, + removeScenario); + HLPR_BAIL_ON_FAILURE(status); + + if(!removeScenario) + { + HLPR_NEW(pPCAdvancedPacketInjectionData, + PC_ADVANCED_PACKET_INJECTION_DATA); + HLPR_BAIL_ON_ALLOC_FAILURE(pPCAdvancedPacketInjectionData, + status); + + status = PrvAdvancedPacketInjectionScenarioParseInjectionData(ppCLPStrings, + stringCount, + pPCAdvancedPacketInjectionData); + HLPR_BAIL_ON_FAILURE(status); + } + + status = RPCInvokeScenarioAdvancedPacketInjection(wfpSamplerBindingHandle, + SCENARIO_ADVANCED_PACKET_INJECTION, + removeScenario ? FWPM_CHANGE_DELETE : FWPM_CHANGE_ADD, + pFilter, + pPCAdvancedPacketInjectionData); + if(status != NO_ERROR) + HlprLogError(L"AdvancedPacketInjectionScenarioExecute : RpcInvokeScenarioAdvancedPacketInjection() [status: %#x]", + status); + else + HlprLogInfo(L"AdvancedPacketInjectionScenarioExecute : RpcInvokeScenarioAdvancedPacketInjection() [status: %#x]", + status); + + HLPR_BAIL_LABEL: + + if(pFilter) + HlprFwpmFilterDestroy(&pFilter); + + HLPR_DELETE(pPCAdvancedPacketInjectionData); + + return status; +} + +/** + @public_function="AdvancedPacketInjectionScenarioLogHelp" + + Purpose: Log usage information for the ADVANCED_PACKET_INJECTION scenario to the console.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +VOID AdvancedPacketInjectionScenarioLogHelp() +{ + wprintf(L"\n\t\t -s \t ADVANCED_PACKET_INJECTION"); + wprintf(L"\n\t\t -? \t Receive usage information."); + wprintf(L"\n\t\t -l \t Specify the layer to perform the filtering. [Required]"); + wprintf(L"\n\t\t -sl \t Specify the sublayer to perform the filtering. [Optional]"); + wprintf(L"\n\t\t -r \t Remove the scenario objects."); + wprintf(L"\n\t\t -v \t Make the filter volatile (non-persistent). [Optional]"); + wprintf(L"\n\t\t -b \t Makes the objects available during boot time. [Optional]"); + wprintf(L"\n\t\t -in \t Perform the injection inline if possible. [Optional]"); + wprintf(L"\n\t\t -tdpc \t Use threaded DPCs for out of band. [Optional]"); + wprintf(L"\n\t\t -wi \t Use work items for out of band. [Optional]"); + wprintf(L"\n\t\t -ab \t Add more bytes to teh payload. [Optional]"); + wprintf(L"\n\t\t -ipla \t Specify the IP_LOCAL_ADDRESS /"); + wprintf(L"\n\t\t \t IP_SOURCE_ADDRESS to filter. [Optional]"); + wprintf(L"\n\t\t -ipra \t Specify the IP_REMOTE_ADDRESS /"); + wprintf(L"\n\t\t \t IP_DESTINATION_ADDRESS to filter. [Optional]"); + wprintf(L"\n\t\t -ipp \t Specify the IP_PROTOCOL to filter. [Optional]"); + wprintf(L"\n\t\t -iplp \t Specify the IP_LOCAL_PORT to filter. [Optional]"); + wprintf(L"\n\t\t -icmpt \t Specify the ICMP_TYPE to filter. [Optional]"); + wprintf(L"\n\t\t -iprp \t Specify the IP_REMOTE_PORT to filter. [Optional]"); + wprintf(L"\n\t\t -icmpc \t Specify the ICMP_CODE to filter. [Optional]"); + wprintf(L"\n"); + wprintf(L"\n\t i.e."); + wprintf(L"\n\t\t WFPSampler.Exe -s ADVANCED_PACKET_INJECTION -l FWPM_LAYER_INBOUND_TRANSPORT_V4 -ipla 1.0.0.1 -ipra 1.0.0.254 -ipp TCP -ab 100 -v"); + wprintf(L"\n\t\t WFPSampler.Exe -s ADVANCED_PACKET_INJECTION -l FWPM_LAYER_INBOUND_TRANSPORT_V4 -ipla 1.0.0.1 -ipra 1.0.0.254 -ipp TCP -ab 100 -v -r"); + wprintf(L"\n"); + + return; +} diff --git a/network/trans/WFPSampler/exe/Scenarios_AdvancedPacketInjection.h b/network/trans/WFPSampler/exe/Scenarios_AdvancedPacketInjection.h new file mode 100644 index 000000000..809a9de70 --- /dev/null +++ b/network/trans/WFPSampler/exe/Scenarios_AdvancedPacketInjection.h @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// Scenarios_AdvancedPacketInjection.h +// +// Abstract: +// This module contains prototypes for functions which run the specified +// ADVANCED_PACKET_INJECTION scenario. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// December 13, 2013 - 1.1 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef SCENARIOS_ADVANCED_PACKET_INJECTION_H +#define SCENARIOS_ADVANCED_PACKET_INJECTION_H + +_Success_(return == NO_ERROR) +UINT32 AdvancedPacketInjectionScenarioExecute(_In_reads_(stringCount) PCWSTR* ppCLPStrings, + _In_ const UINT32 stringCount); + +VOID AdvancedPacketInjectionScenarioLogHelp(); + +#endif /// SCENARIOS_ADVANCED_PACKET_INJECTION_H \ No newline at end of file diff --git a/network/trans/WFPSampler/exe/Scenarios_AppContainers.cpp b/network/trans/WFPSampler/exe/Scenarios_AppContainers.cpp new file mode 100644 index 000000000..7022a87a7 --- /dev/null +++ b/network/trans/WFPSampler/exe/Scenarios_AppContainers.cpp @@ -0,0 +1,231 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// Scenarios_AppContainers.cpp +// +// Abstract: +// This module contains functions which prepares and sends data for the APPLICATION_CONTAINER +// scenario implementation. +// +// Naming Convention: +// +// +// +// i.e. +// +// +// { +// - Function is likely visible to other modules. +// Prv - Function is private to this module. +// } +// +// { +// AppContainerScenario - Function pertains to all of the Application Container Scenarios. +// } +// +// { +// Execute - Function packages data and invokes RPC to the WFPSampler +// service. +// Log - Function writes to the console. +// Parse - Function pulls data into the required format from the provided +// data. +// } +// +// { +// DataForModifiers - Function acts on scenario modifiers such as volatility and +// removal. +// Help - Function provides context sensitive help for the scenario. +// } +// +// Public Functions: +// AppContainerScenarioExecute(), +// AppContainerScenarioLogHelp +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "Framework_WFPSampler.h" /// . + +#if(NTDDI_VERSION >= NTDDI_WIN8) + +/** + @private_function="PrvAppContainerScenarioParseDataForModifiers" + + Purpose: Parse the command line parameters for any scenario modifiers such as:
+ trust Windows Service Hardening (-trustWSH)
+ add own filters for containers (-distrustWSH)
+ use a callout (-c)
+ make volatile / non persistent (-v)
+ mark objects available during boottime (-b)
+ remove scenario (-r)
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 PrvAppContainerScenarioParseDataForModifiers(_In_reads_(stringCount) PCWSTR* ppCLPStrings, + _In_ UINT32 stringCount, + _Inout_ BOOLEAN* pTrustWSH, + _Inout_ BOOLEAN* pRemoveScenario, + _Inout_ BOOLEAN* pIsPersistent, + _Inout_ BOOLEAN* pIsBootTime) +{ + ASSERT(ppCLPStrings); + ASSERT(stringCount); + ASSERT(pTrustWSH); + ASSERT(pRemoveScenario); + ASSERT(pIsPersistent); + ASSERT(pIsBootTime); + + UINT32 status = NO_ERROR; + + for(UINT32 stringIndex = 0; + stringIndex < stringCount; + stringIndex++) + { + /// Trust that Windows Service Hardening rules are enforcing AppContainer constraints + if(HlprStringsAreEqual(L"-trustWSH", + ppCLPStrings[stringIndex])) + { + *pTrustWSH = TRUE; + + continue; + } + + /// Distrust that Windows Service Hardening rules are enforcing AppContainer constraints + if(HlprStringsAreEqual(L"-distrustWSH", + ppCLPStrings[stringIndex])) + { + *pTrustWSH = FALSE; + + continue; + } + + /// Remove any filter with this setup + if(HlprStringsAreEqual(L"-r", + ppCLPStrings[stringIndex])) + { + *pRemoveScenario = TRUE; + + continue; + } + + /// Make the objects volatile + if(HlprStringsAreEqual(L"-v", + ppCLPStrings[stringIndex]) && + *pIsBootTime == FALSE) + { + *pIsPersistent = FALSE; + + continue; + } + + /// Mark the objects available during BootTime + if(HlprStringsAreEqual(L"-b", + ppCLPStrings[stringIndex])) + { + *pIsPersistent = TRUE; + + *pIsBootTime = TRUE; + + continue; + } + } + + return status; +} + +/** + @scenario_function="AppContainerScenarioExecute" + + Purpose: Gather and package data neccessary to setup the APPLICATION_CONTAINER scenario, + then invoke RPC to implement the scenario in the WFPSampler service.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 AppContainerScenarioExecute(_In_reads_(stringCount) PCWSTR* ppCLPStrings, + _In_ UINT32 stringCount) +{ + ASSERT(ppCLPStrings); + ASSERT(stringCount); + + UINT32 status = NO_ERROR; + BOOLEAN trustWSH = TRUE; + BOOLEAN removeScenario = FALSE; + BOOLEAN persistent = TRUE; + BOOLEAN bootTime = FALSE; + + status = PrvAppContainerScenarioParseDataForModifiers(ppCLPStrings, + stringCount, + &trustWSH, + &removeScenario, + &persistent, + &bootTime); + HLPR_BAIL_ON_FAILURE(status); + + status = RPCInvokeScenarioAppContainer(wfpSamplerBindingHandle, + SCENARIO_APP_CONTAINER, + removeScenario ? FWPM_CHANGE_DELETE : FWPM_CHANGE_ADD, + trustWSH, + persistent, + bootTime); + if(status != NO_ERROR) + HlprLogError(L"AppContainerScenarioExecute : RPCInvokeScenarioAppContainer() [status: %#x]", + status); + else + HlprLogInfo(L"AppContainerScenarioExecute : RPCInvokeScenarioAppContainer() [status: %#x]", + status); + + HLPR_BAIL_LABEL: + + return status; +} + +/** + @public_function="AppContainerScenarioLogHelp" + + Purpose: Log usage information to the console for the APP_CONTAINER scenario.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +VOID AppContainerScenarioLogHelp() +{ + PWSTR pScenario = L"APP_CONTAINER"; + + wprintf(L"\n\t\t -s \t %s ", + pScenario); + wprintf(L"\n\t\t -? \t Receive usage information."); + wprintf(L"\n\t\t -r \t Remove the scenario objects."); + wprintf(L"\n\t\t -v \t Make the objects volatile (non-persistent). [Optional]"); + wprintf(L"\n\t\t -b \t Makes the objects available during boot time. [Optional]"); + wprintf(L"\n\t\t -trustWSH \t Adds filters that inherently allows Windows Service Hardening"); + wprintf(L"\n\t\t \t to handle all App Container decisions. (Default / Recommended)"); + wprintf(L"\n\t\t -distrustWSH \t Enumerates current WSH filters and subscribes to receive future"); + wprintf(L"\n\t\t \t filters. Adds granular filters to allow contained apps."); + wprintf(L"\n"); + wprintf(L"\n\t i.e."); + wprintf(L"\n\t\t WFPSampler.Exe -s %s -trustWSH -v", + pScenario); + wprintf(L"\n\t\t WFPSampler.Exe -s %s -trustWSH -v -r", + pScenario); + wprintf(L"\n"); + + return; +} + +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) diff --git a/network/trans/WFPSampler/exe/Scenarios_AppContainers.h b/network/trans/WFPSampler/exe/Scenarios_AppContainers.h new file mode 100644 index 000000000..8e14799e5 --- /dev/null +++ b/network/trans/WFPSampler/exe/Scenarios_AppContainers.h @@ -0,0 +1,35 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// Scenarios_AppContainers.h +// +// Abstract: +// This module contains prototypes for functions which prepares and sends data for the +// APPLICATION_CONTAINER scenario implementation. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef SCENARIOS_APP_CONTAINER_H +#define SCENARIOS_APP_CONTAINER_H + +#if(NTDDI_VERSION >= NTDDI_WIN8) + +_Success_(return == NO_ERROR) +UINT32 AppContainerScenarioExecute(_In_reads_(stringCount) PCWSTR* ppCLPStrings, + _In_ UINT32 stringCount); + +VOID AppContainerScenarioLogHelp(); + +#endif // (NTDDI_VERSION >= NTDDI_WIN8) + +#endif /// SCENARIOS_APP_CONTAINER_H \ No newline at end of file diff --git a/network/trans/WFPSampler/exe/Scenarios_BasicAction.cpp b/network/trans/WFPSampler/exe/Scenarios_BasicAction.cpp new file mode 100644 index 000000000..35a3aa6e6 --- /dev/null +++ b/network/trans/WFPSampler/exe/Scenarios_BasicAction.cpp @@ -0,0 +1,487 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// Scenarios_BasicAction.cpp +// +// Abstract: +// This module contains functions which prepares and sends data for the BASIC_ACTION_* scenario +// implementation. +// +// Naming Convention: +// +// +// +// i.e. +// +// +// { +// - Function is likely visible to other modules. +// Prv - Function is private to this module. +// } +// +// { +// BasicActionBlockScenario - Function pertains to the Basic Action Block Scenario. +// BasicActionContinueScenario - Function pertains to the Basic Action Continue Scenario. +// BasicActionPermitScenario - Function pertains to the Basic Action Permit Scenario. +// BasicActionRandomScenario - Function pertains to the Basic Action Random Scenario. +// BasicActionScenario - Function pertains to all of the Basic Action Scenarios. +// } +// +// { +// Execute - Function packages data and invokes RPC to the WFPSampler +// service. +// Log - Function writes to the console. +// Parse - Function pulls data into the required format from the +// provided data. +// } +// +// { +// Help - Function provides context sensitive help for the +// scenario. +// RandomizedData - Function acts on the PC_BASIC_ACTION_DATA. +// } +// +// Private Functions: +// PrvBasicActionScenarioParseRandomizedData(), +// +// Public Functions: +// BasicActionBlockScenarioExecute(), +// BasicActionContinueScenarioExecute(), +// BasicActionPermitScenarioExecute(), +// BasicActionRandomScenarioExecute(), +// BasicActionScenarioLogHelp(), +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Add support for specifying a different sublayer +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "Framework_WFPSampler.h" /// . + +/** + @private_function="PrvBasicActionScenarioParseRandomizedData" + + Purpose: Parse the command line parameters for implementing randomization such as:
+ Random chance of returning block (-rab PERCENTAGE)
+ Random chance of returning continue (-rac PERCENTAGE)
+ Random chance of returning permit (-rap PERCENTAGE)
+
+ Notes: This is only used if using SCENARIO_BASIC_ACTION_RANDOM.
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 PrvBasicActionScenarioParseRandomizedData(_In_reads_(stringCount) PCWSTR* ppCLPStrings, + _In_ const UINT32 stringCount, + _Inout_ PC_BASIC_ACTION_DATA* pPCBasicActionData) +{ + ASSERT(ppCLPStrings); + ASSERT(stringCount); + ASSERT(pPCBasicActionData); + + UINT32 status = NO_ERROR; + + for(UINT32 stringIndex = 0; + (stringIndex + 1) < stringCount; + stringIndex++) + { + const UINT32 WHOLE_PERCENTAGE = 100; + + /// Random FWP_ACTION_BLOCK + if(HlprStringsAreEqual(L"-rab", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[stringIndex + 1]; + + if(iswdigit((wint_t)pString[0])) + { + UINT32 percentBlock = wcstol(pString, + 0, + 0); + if(percentBlock <= WHOLE_PERCENTAGE) + pPCBasicActionData->percentBlock = percentBlock & 0xFF; + } + + continue; + } + + /// Random FWP_ACTION_CONTINUE + if(HlprStringsAreEqual(L"-rac", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[stringIndex + 1]; + + if(iswdigit((wint_t)pString[0])) + { + UINT32 percentContinue = wcstol(pString, + 0, + 0); + if(percentContinue <= WHOLE_PERCENTAGE) + pPCBasicActionData->percentContinue = percentContinue & 0xFF; + } + + continue; + } + + /// FWP_ACTION_PERMIT + if(HlprStringsAreEqual(L"-rap", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[stringIndex + 1]; + + if(iswdigit((wint_t)pString[0])) + { + UINT32 percentPermit = wcstol(pString, + 0, + 0); + if(percentPermit <= WHOLE_PERCENTAGE) + pPCBasicActionData->percentPermit = percentPermit & 0xFF; + } + + continue; + } + + if(pPCBasicActionData->percentBlock && + pPCBasicActionData->percentContinue && + pPCBasicActionData->percentPermit) + break; + } + + /// Set the chance of each action for Randomization + if(pPCBasicActionData->percentBlock == 0 && + pPCBasicActionData->percentContinue == 0 && + pPCBasicActionData->percentPermit == 0) + { + pPCBasicActionData->percentBlock = 50; + pPCBasicActionData->percentContinue = 25; + pPCBasicActionData->percentPermit = 25; + } + else + { + if(pPCBasicActionData->percentBlock == 0) + pPCBasicActionData->percentBlock = 100 - (pPCBasicActionData->percentContinue + pPCBasicActionData->percentPermit); + + if(pPCBasicActionData->percentContinue == 0) + pPCBasicActionData->percentContinue = 100 - (pPCBasicActionData->percentBlock + pPCBasicActionData->percentPermit); + + if(pPCBasicActionData->percentPermit == 0) + pPCBasicActionData->percentPermit = 100 - (pPCBasicActionData->percentBlock + pPCBasicActionData->percentContinue); + } + + if((pPCBasicActionData->percentBlock + + pPCBasicActionData->percentContinue + + pPCBasicActionData->percentPermit) > 100) + { + pPCBasicActionData->percentBlock = 50; + pPCBasicActionData->percentContinue = 25; + pPCBasicActionData->percentPermit = 25; + } + + return status; +} + +/** + @scenario_function="BasicActionBlockScenarioExecute" + + Purpose: Gather and package data neccessary to setup the BASIC_ACTION_BLOCK scenario, then + invoke RPC to implement the scenario in the WFPSampler service.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 BasicActionBlockScenarioExecute(_In_reads_(stringCount) PCWSTR* ppCLPStrings, + _In_ UINT32 stringCount) +{ + ASSERT(ppCLPStrings); + ASSERT(stringCount); + + UINT32 status = NO_ERROR; + BOOLEAN removeScenario = FALSE; + FWPM_FILTER* pFilter = 0; + + status = HlprFwpmFilterCreate(&pFilter); + HLPR_BAIL_ON_FAILURE(status); + + pFilter->displayData.name = L"WFPSampler's Basic Action Block Scenario Filter"; + + HlprCommandLineParseForScenarioRemoval(ppCLPStrings, + stringCount, + &removeScenario); + + status = HlprCommandLineParseForFilterInfo(ppCLPStrings, + stringCount, + pFilter, + removeScenario); + HLPR_BAIL_ON_FAILURE(status); + + status = RPCInvokeScenarioBasicAction(wfpSamplerBindingHandle, + SCENARIO_BASIC_ACTION_BLOCK, + removeScenario ? FWPM_CHANGE_DELETE : FWPM_CHANGE_ADD, + pFilter, + 0); + if(status != NO_ERROR) + HlprLogError(L"BasicActionBlockScenarioExecute : RpcInvokeScenarioBasicAction() [status: %#x]", + status); + else + HlprLogInfo(L"BasicActionBlockScenarioExecute : RpcInvokeScenarioBasicAction() [status: %#x]", + status); + + HLPR_BAIL_LABEL: + + return status; +} + +/** + @scenario_function="BasicActionContinueScenarioExecute" + + Purpose: Gather and package data neccessary to setup the BASIC_ACTION_CONTINUE scenario, + then invoke RPC to implement the scenario in the WFPSampler service.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 BasicActionContinueScenarioExecute(_In_reads_(stringCount) PCWSTR* ppCLPStrings, + _In_ UINT32 stringCount) +{ + ASSERT(ppCLPStrings); + ASSERT(stringCount); + + UINT32 status = NO_ERROR; + BOOLEAN removeScenario = FALSE; + FWPM_FILTER* pFilter = 0; + + status = HlprFwpmFilterCreate(&pFilter); + HLPR_BAIL_ON_FAILURE(status); + + pFilter->displayData.name = L"WFPSampler's Basic Action Continue Scenario Filter"; + + HlprCommandLineParseForScenarioRemoval(ppCLPStrings, + stringCount, + &removeScenario); + + status = HlprCommandLineParseForFilterInfo(ppCLPStrings, + stringCount, + pFilter, + removeScenario); + HLPR_BAIL_ON_FAILURE(status); + + status = RPCInvokeScenarioBasicAction(wfpSamplerBindingHandle, + SCENARIO_BASIC_ACTION_CONTINUE, + removeScenario ? FWPM_CHANGE_DELETE : FWPM_CHANGE_ADD, + pFilter, + 0); + if(status != NO_ERROR) + HlprLogError(L"BasicActionContinueScenarioExecute : RpcInvokeScenarioBasicAction() [status: %#x]", + status); + else + HlprLogInfo(L"BasicActionContinueScenarioExecute : RpcInvokeScenarioBasicAction() [status: %#x]", + status); + + HLPR_BAIL_LABEL: + + if(pFilter) + HlprFwpmFilterDestroy(&pFilter); + + return status; +} + +/** + @scenario_function="BasicActionPermitScenarioExecute" + + Purpose: Gather and package data neccessary to setup the BASIC_ACTION_PERMIT scenario, then + invoke RPC to implement the scenario in the WFPSampler service.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 BasicActionPermitScenarioExecute(_In_reads_(stringCount) PCWSTR* ppCLPStrings, + _In_ UINT32 stringCount) +{ + ASSERT(ppCLPStrings); + ASSERT(stringCount); + + UINT32 status = NO_ERROR; + BOOLEAN removeScenario = FALSE; + FWPM_FILTER* pFilter = 0; + + status = HlprFwpmFilterCreate(&pFilter); + HLPR_BAIL_ON_FAILURE(status); + + pFilter->displayData.name = L"WFPSampler's Basic Action Permit Scenario Filter"; + + HlprCommandLineParseForScenarioRemoval(ppCLPStrings, + stringCount, + &removeScenario); + + status = HlprCommandLineParseForFilterInfo(ppCLPStrings, + stringCount, + pFilter, + removeScenario); + HLPR_BAIL_ON_FAILURE(status); + + status = RPCInvokeScenarioBasicAction(wfpSamplerBindingHandle, + SCENARIO_BASIC_ACTION_PERMIT, + removeScenario ? FWPM_CHANGE_DELETE : FWPM_CHANGE_ADD, + pFilter, + 0); + if(status != NO_ERROR) + HlprLogError(L"BasicActionPermitScenarioExecute : RpcInvokeScenarioBasicAction() [status: %#x]", + status); + else + HlprLogInfo(L"BasicActionPermitScenarioExecute : RpcInvokeScenarioBasicAction() [status: %#x]", + status); + + HLPR_BAIL_LABEL: + + if(pFilter) + HlprFwpmFilterDestroy(&pFilter); + + return status; +} + +/** + @scenario_function="BasicActionRandomScenarioExecute" + + Purpose: Gather and package data neccessary to setup the BASIC_ACTION_RANDOM scenario, then + invoke RPC to implement the scenario in the WFPSampler service.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 BasicActionRandomScenarioExecute(_In_reads_(stringCount) PCWSTR* ppCLPStrings, + _In_ UINT32 stringCount) +{ + UINT32 status = NO_ERROR; + BOOLEAN removeScenario = FALSE; + PC_BASIC_ACTION_DATA* pPCBasicActionData = 0; + FWPM_FILTER* pFilter = 0; + + status = HlprFwpmFilterCreate(&pFilter); + HLPR_BAIL_ON_FAILURE(status); + + pFilter->displayData.name = L"WFPSampler's Basic Action Random Scenario Filter"; + + HlprCommandLineParseForScenarioRemoval(ppCLPStrings, + stringCount, + &removeScenario); + + status = HlprCommandLineParseForFilterInfo(ppCLPStrings, + stringCount, + pFilter, + removeScenario); + HLPR_BAIL_ON_FAILURE(status); + + pFilter->action.type = FWP_ACTION_CALLOUT_UNKNOWN; + + if(!removeScenario) + { + HLPR_NEW(pPCBasicActionData, + PC_BASIC_ACTION_DATA); + HLPR_BAIL_ON_ALLOC_FAILURE(pPCBasicActionData, + status); + + status = PrvBasicActionScenarioParseRandomizedData(ppCLPStrings, + stringCount, + pPCBasicActionData); + HLPR_BAIL_ON_FAILURE(status); + } + + status = RPCInvokeScenarioBasicAction(wfpSamplerBindingHandle, + SCENARIO_BASIC_ACTION_RANDOM, + removeScenario ? FWPM_CHANGE_DELETE : FWPM_CHANGE_ADD, + pFilter, + pPCBasicActionData); + if(status != NO_ERROR) + HlprLogError(L"BasicActionRandomScenarioExecute : RpcInvokeScenarioBasicAction() [status: %#x]", + status); + else + HlprLogInfo(L"BasicActionRandomScenarioExecute : RpcInvokeScenarioBasicAction() [status: %#x]", + status); + + HLPR_BAIL_LABEL: + + if(pFilter) + HlprFwpmFilterDestroy(&pFilter); + + HLPR_DELETE(pPCBasicActionData); + + return status; +} + +/** + @public_function="BasicActionScenarioLogHelp" + + Purpose: Log usage information for the BASIC_ACTION* scenarios to the console.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +VOID BasicActionScenarioLogHelp(_In_ const UINT32 scenario) +{ + PWSTR pScenario = L"BASIC_ACTION_BLOCK"; + PWSTR pRandomInfo = L" "; + + if(scenario == SCENARIO_BASIC_ACTION_CONTINUE) + pScenario = L"BASIC_ACTION_CONTINUE"; + else if(scenario == SCENARIO_BASIC_ACTION_PERMIT) + pScenario = L"BASIC_ACTION_PERMIT"; + else if(scenario == SCENARIO_BASIC_ACTION_RANDOM) + pScenario = L"BASIC_ACTION_RANDOM"; + + wprintf(L"\n\t\t -s \t %s", + pScenario); + wprintf(L"\n\t\t -? \t Receive usage information."); + wprintf(L"\n\t\t -l \t Specify the layer to perform the filtering. [Required]"); + wprintf(L"\n\t\t -sl \t Specify the sublayer to perform the filtering. [Optional]"); + wprintf(L"\n\t\t -r \t Remove the scenario objects."); + wprintf(L"\n\t\t -c \t Use a callout to perform the action. [Optional]"); + wprintf(L"\n\t\t -v \t Make the filter volatile (non-persistent). [Optional]"); + wprintf(L"\n\t\t -b \t Makes the objects available during boot time. [Optional]"); + wprintf(L"\n\t\t -ipla \t Specify the IP_LOCAL_ADDRESS /"); + wprintf(L"\n\t\t \t IP_SOURCE_ADDRESS to filter. [Optional]"); + wprintf(L"\n\t\t -ipra \t Specify the IP_REMOTE_ADDRESS /"); + wprintf(L"\n\t\t \t IP_DESTINATION_ADDRESS to filter. [Optional]"); + wprintf(L"\n\t\t -ipp \t Specify the IP_PROTOCOL to filter. [Optional]"); + wprintf(L"\n\t\t -iplp \t Specify the IP_LOCAL_PORT to filter. [Optional]"); + wprintf(L"\n\t\t -icmpt \t Specify the ICMP_TYPE to filter. [Optional]"); + wprintf(L"\n\t\t -iprp \t Specify the IP_REMOTE_PORT to filter. [Optional]"); + wprintf(L"\n\t\t -icmpc \t Specify the ICMP_CODE to filter. [Optional]"); + + if(scenario == SCENARIO_BASIC_ACTION_RANDOM) + { + pRandomInfo = L"-rab 50 -rac 25 -rap 25 "; + + wprintf(L"\n\t\t -rab \t Percentage chance of returning FWP_ACTION_BLOCK. [Optional]"); + wprintf(L"\n\t\t -rac \t Percentage chance of returning FWP_ACTION_CONTINUE. [Optional]"); + wprintf(L"\n\t\t -rap \t Percentage chance of returning FWP_ACTION_PERMIT. [Optional]"); + } + + wprintf(L"\n"); + wprintf(L"\n\t i.e."); + wprintf(L"\n\t\t WFPSampler.Exe -s %s -l FWPM_LAYER_INBOUND_TRANSPORT_V4 -ipla 1.0.0.1 -ipra 1.0.0.254 -ipp TCP -c %s", + pScenario, + pRandomInfo); + wprintf(L"\n\t\t WFPSampler.Exe -s %s -l FWPM_LAYER_INBOUND_TRANSPORT_V4 -ipla 1.0.0.1 -ipra 1.0.0.254 -ipp TCP -c %s-r", + pScenario, + pRandomInfo); + wprintf(L"\n"); + + return; +} diff --git a/network/trans/WFPSampler/exe/Scenarios_BasicAction.h b/network/trans/WFPSampler/exe/Scenarios_BasicAction.h new file mode 100644 index 000000000..347dcdd94 --- /dev/null +++ b/network/trans/WFPSampler/exe/Scenarios_BasicAction.h @@ -0,0 +1,42 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// Scenarios_BasicAction.h +// +// Abstract: +// This module contains prototypes for functions which run the specified BASIC_ACTION scenario. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef SCENARIOS_BASIC_ACTION_H +#define SCENARIOS_BASIC_ACTION_H + +_Success_(return == NO_ERROR) +UINT32 BasicActionBlockScenarioExecute(_In_reads_(stringCount) PCWSTR* ppCLPStrings, + _In_ UINT32 stringCount); + +_Success_(return == NO_ERROR) +UINT32 BasicActionContinueScenarioExecute(_In_reads_(stringCount) PCWSTR* ppCLPStrings, + _In_ UINT32 stringCount); + +_Success_(return == NO_ERROR) +UINT32 BasicActionPermitScenarioExecute(_In_reads_(stringCount) PCWSTR* ppCLPStrings, + _In_ UINT32 stringCount); + +_Success_(return == NO_ERROR) +UINT32 BasicActionRandomScenarioExecute(_In_reads_(stringCount) PCWSTR* ppCLPStrings, + _In_ UINT32 stringCount); + +VOID BasicActionScenarioLogHelp(_In_ const UINT32 scenario); + +#endif /// SCENARIOS_BASIC_ACTION_H \ No newline at end of file diff --git a/network/trans/WFPSampler/exe/Scenarios_BasicPacketExamination.cpp b/network/trans/WFPSampler/exe/Scenarios_BasicPacketExamination.cpp new file mode 100644 index 000000000..f97ab002a --- /dev/null +++ b/network/trans/WFPSampler/exe/Scenarios_BasicPacketExamination.cpp @@ -0,0 +1,292 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// Scenarios_BasicPacketExamination.cpp +// +// Abstract: +// This module contains functions which prepares and sends data for the +// BASIC_PACKET_EXAMINATION scenario implementation. +// +// Naming Convention: +// +// +// +// i.e. +// +// +// { +// - Function is likely visible to other modules. +// Prv - Function is private to this module. +// } +// +// { +// BasicPacketExaminationScenario - Function pertains to the Basic Packet Examination. +// } +// +// { +// Execute - Function packages data and invokes RPC to the +// WFPSampler service. +// Log - Function writes to the console. +// } +// +// { +// Help - Function provides context sensitive help for the +// scenario. +// ExaminationFlags - Function acts on the . +// } +// + +// Private Functions: +// PrvScenarioBasicPacketExaminationParseExaminationFlags(), +// +// Public Functions: +// BasicPacketExaminationScenarioExecute(), +// BasicPacketExaminationScenarioLogHelp(), +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Add support for specifying a different sublayer and +// for examination options +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "Framework_WFPSampler.h" /// . + +/** + @private_function="PrvScenarioBasicPacketExaminationParseExaminationFlags" + + Purpose: Parse the command line parameters for implementing packet examination such as:
+ Log while under lock (-lul)
+ Log the FWPS_INCOMING_VALUES (-liv)
+ Log the FWPS_INCOMING_METADATA_VALUES (-limv)
+ Log the layerData pointer (-lld)
+ Log the classifyContext (-lcc)
+ Log the FWPS_FILTER (-lf)
+ Log the flowContext (-lfc)
+ Log the FWPS_CLASSIFY_OUT (-lco)
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 PrvScenarioBasicPacketExaminationParseExaminationFlags(_In_reads_(stringCount) PCWSTR* ppCLPStrings, + _In_ const UINT32 stringCount, + _Inout_ UINT64* pExaminationFlags) +{ + ASSERT(ppCLPStrings); + ASSERT(stringCount); + ASSERT(pExaminationFlags); + + UINT32 status = NO_ERROR; + const UINT32 MAX_PARAMETERS = 7; + UINT32 found = 0; + + *pExaminationFlags = 0; + + for(UINT32 stringIndex = 0; + stringIndex < stringCount && + found != MAX_PARAMETERS; + stringIndex++) + { + /// Log while under lock + if(HlprStringsAreEqual(L"-lul", + ppCLPStrings[stringIndex])) + { + (*pExaminationFlags) |= PCPEF_EXAMINE_UNDER_LOCK; + + found++; + + continue; + } + + /// Log FWPS_INCOMING_VALUES + if(HlprStringsAreEqual(L"-liv", + ppCLPStrings[stringIndex])) + { + (*pExaminationFlags) |= PCPEF_EXAMINE_INCOMING_VALUES; + + found++; + + continue; + } + + /// Log FWPS_INCOMING_METADATA_VALUES + if(HlprStringsAreEqual(L"-limv", + ppCLPStrings[stringIndex])) + { + (*pExaminationFlags) |= PCPEF_EXAMINE_INCOMING_METADATA_VALUES; + + found++; + + continue; + } + + /// Log layerData pointer + if(HlprStringsAreEqual(L"-lld", + ppCLPStrings[stringIndex])) + { + (*pExaminationFlags) |= PCPEF_EXAMINE_LAYER_DATA; + + found++; + + continue; + } + + /// Log classifyContext + if(HlprStringsAreEqual(L"-lcc", + ppCLPStrings[stringIndex])) + { + (*pExaminationFlags) |= PCPEF_EXAMINE_CLASSIFY_CONTEXT; + + found++; + + continue; + } + + /// Log FWPS_FILTER + if(HlprStringsAreEqual(L"-lf", + ppCLPStrings[stringIndex])) + { + (*pExaminationFlags) |= PCPEF_EXAMINE_FILTER; + + found++; + + continue; + } + + + /// Log flowContext + if(HlprStringsAreEqual(L"-lfc", + ppCLPStrings[stringIndex])) + { + (*pExaminationFlags) |= PCPEF_EXAMINE_FLOW_CONTEXT; + + found++; + + continue; + } + + /// Log FWPS_CLASSIFY_OUT + if(HlprStringsAreEqual(L"-lco", + ppCLPStrings[stringIndex])) + { + (*pExaminationFlags) |= PCPEF_EXAMINE_CLASSIFY_OUT; + + found++; + + continue; + } + } + + return status; +} + +/** + @scenario_function="BasicPacketExaminationScenarioExecute" + + Purpose: Gather and package data neccessary to setup the BASIC_PACKET_EXAMINATION scenario, + then invoke RPC to implement the scenario in the WFPSampler service.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 BasicPacketExaminationScenarioExecute(_In_reads_(stringCount) PCWSTR* ppCLPStrings, + _In_ UINT32 stringCount) +{ + ASSERT(ppCLPStrings); + ASSERT(stringCount); + + UINT32 status = NO_ERROR; + BOOLEAN removeScenario = FALSE; + FWPM_FILTER* pFilter = 0; + + status = HlprFwpmFilterCreate(&pFilter); + HLPR_BAIL_ON_FAILURE(status); + + pFilter->displayData.name = L"WFPSampler's Basic Packet Examination Scenario Filter"; + + HlprCommandLineParseForScenarioRemoval(ppCLPStrings, + stringCount, + &removeScenario); + + status = HlprCommandLineParseForFilterInfo(ppCLPStrings, + stringCount, + pFilter, + removeScenario); + HLPR_BAIL_ON_FAILURE(status); + + PrvScenarioBasicPacketExaminationParseExaminationFlags(ppCLPStrings, + stringCount, + &(pFilter->rawContext)); + + status = RPCInvokeScenarioBasicPacketExamination(wfpSamplerBindingHandle, + SCENARIO_BASIC_PACKET_EXAMINATION, + removeScenario ? FWPM_CHANGE_DELETE : FWPM_CHANGE_ADD, + pFilter); + if(status != NO_ERROR) + HlprLogError(L"BasicPacketExaminationScenarioExecute : RpcInvokeScenarioBasicPacketExamination() [status: %#x]", + status); + else + HlprLogInfo(L"BasicPacketExaminationScenarioExecute : RpcInvokeScenarioBasicPacketExamination() [status: %#x]", + status); + + HLPR_BAIL_LABEL: + + if(pFilter) + HlprFwpmFilterDestroy(&pFilter); + + return status; +} + +/** + @public_function="BasicPacketExaminationScenarioLogHelp" + + Purpose: Log usage information for the BASIC_PACKET_EXAMINATION scenario to the console.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +VOID BasicPacketExaminationScenarioLogHelp() +{ + wprintf(L"\n\t\t -s \t BASIC_PACKET_EXAMINATION"); + wprintf(L"\n\t\t -? \t Receive usage information."); + wprintf(L"\n\t\t -l \t Specify the layer to perform the filtering. [Required]"); + wprintf(L"\n\t\t -r \t Remove the scenario objects."); + wprintf(L"\n\t\t -v \t Make the filter volatile (non-persistent). [Optional]"); + wprintf(L"\n\t\t -b \t Makes the objects available during boot time. [Optional]"); + wprintf(L"\n\t\t -lul \t log while under lock"); + wprintf(L"\n\t\t -liv \t log the FWPS_INCOMING_VALUES"); + wprintf(L"\n\t\t -limv \t log the FWPS_INCOMING_METADATA_VALUES"); + wprintf(L"\n\t\t -lld \t log the layerData"); + wprintf(L"\n\t\t -lcc \t log the classifyContext"); + wprintf(L"\n\t\t -lf \t log the FWPS_FILTER"); + wprintf(L"\n\t\t -lfc \t log the flowContext"); + wprintf(L"\n\t\t -lco \t log the FWPS_CLASSIFY_OUT"); + wprintf(L"\n\t\t -ipla \t Specify the IP_LOCAL_ADDRESS /"); + wprintf(L"\n\t\t \t IP_SOURCE_ADDRESS to filter. [Optional]"); + wprintf(L"\n\t\t -ipra \t Specify the IP_REMOTE_ADDRESS /"); + wprintf(L"\n\t\t \t IP_DESTINATION_ADDRESS to filter. [Optional]"); + wprintf(L"\n\t\t -ipp \t Specify the IP_PROTOCOL to filter. [Optional]"); + wprintf(L"\n\t\t -iplp \t Specify the IP_LOCAL_PORT to filter. [Optional]"); + wprintf(L"\n\t\t -icmpt \t Specify the ICMP_TYPE to filter. [Optional]"); + wprintf(L"\n\t\t -iprp \t Specify the IP_REMOTE_PORT to filter. [Optional]"); + wprintf(L"\n\t\t -icmpc \t Specify the ICMP_CODE to filter. [Optional]"); + wprintf(L"\n"); + wprintf(L"\n\t i.e."); + wprintf(L"\n\t\t WFPSampler.Exe -s BASIC_PACKET_EXAMINATION -l FWPM_LAYER_INBOUND_TRANSPORT_V4 -ipla 1.0.0.1 -ipra 1.0.0.254 -ipp TCP -v"); + wprintf(L"\n\t\t WFPSampler.Exe -s BASIC_PACKET_EXAMINATION -l FWPM_LAYER_INBOUND_TRANSPORT_V4 -ipla 1.0.0.1 -ipra 1.0.0.254 -ipp TCP -v -r"); + wprintf(L"\n"); + + return; +} diff --git a/network/trans/WFPSampler/exe/Scenarios_BasicPacketExamination.h b/network/trans/WFPSampler/exe/Scenarios_BasicPacketExamination.h new file mode 100644 index 000000000..22f21a02c --- /dev/null +++ b/network/trans/WFPSampler/exe/Scenarios_BasicPacketExamination.h @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// Scenarios_BasicPacketExamination.h +// +// Abstract: +// This module contains prototypes for functions which run the specified +// BASIC_PACKET_EXAMINATION scenario. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef SCENARIOS_BASIC_PACKET_EXAMINATION_H +#define SCENARIOS_BASIC_PACKET_EXAMINATION_H + +_Success_(return == NO_ERROR) +UINT32 BasicPacketExaminationScenarioExecute(_In_reads_(stringCount) PCWSTR* ppCLPStrings, + _In_ const UINT32 stringCount); + +VOID BasicPacketExaminationScenarioLogHelp(); + +#endif /// SCENARIOS_BASIC_PACKET_EXAMINATION_H \ No newline at end of file diff --git a/network/trans/WFPSampler/exe/Scenarios_BasicPacketInjection.cpp b/network/trans/WFPSampler/exe/Scenarios_BasicPacketInjection.cpp new file mode 100644 index 000000000..f6046a832 --- /dev/null +++ b/network/trans/WFPSampler/exe/Scenarios_BasicPacketInjection.cpp @@ -0,0 +1,238 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// Scenarios_BasicPacketInjection.cpp +// +// Abstract: +// This module contains functions which prepares and sends data for the BASIC_PACKET_INJECTION +// scenario implementation. +// +// Naming Convention: +// +// +// +// i.e. +// +// +// { +// - Function is likely visible to other modules. +// Prv - Function is private to this module. +// } +// +// { +// BasicPacketInjectionScenario - Function pertains to Basic Packet Injection Scenario. +// } +// +// { +// Execute - Function packages data and invokes RPC to the +// WFPSampler service +// Log - Function writes to the console. +// Parse - Function pulls data into the required format from the +// provided data. +// } +// +// { +// Help - Function provides context sensitive help for the +// scenario. +// InjectionData - Function acts on the PC_BASIC_PACKET_INJECTION_DATA. +// } +// +// Private Functions: +// PrvScenarioBasicPacketInjectionParseInjectionData(), +// +// Public Functions: +// BasicPacketInjectionScenarioExecute(), +// BasicPacketInjectionScenarioLogHelp(), +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Add support for specifying a different sublayer +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "Framework_WFPSampler.h" /// . + +/** + @private_function="PrvBasicPacketInjectionScenarioParseInjectionData" + + Purpose: Parse the command line parameters for implementing packet injection such as:
+ Perform the injection inline (from within the classify) (-in)
+ Use threaded DPCs for out of band (asynchronous) (-tdpc)
+ Use work items for out of band (asynchronous) (-wi)
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 PrvBasicPacketInjectionScenarioParseInjectionData(_In_reads_(stringCount) PCWSTR* ppCLPStrings, + _In_ const UINT32 stringCount, + _Inout_ PC_BASIC_PACKET_INJECTION_DATA* pPCBasicPacketInjectionData) +{ + ASSERT(ppCLPStrings); + ASSERT(stringCount); + ASSERT(pPCBasicPacketInjectionData); + + UINT32 status = NO_ERROR; + const UINT32 MAX_PARAMETERS = 4; + UINT32 found = 0; + + for(UINT32 stringIndex = 0; + stringIndex < stringCount && + found != MAX_PARAMETERS; + stringIndex++) + { + /// Inline Injection + if(HlprStringsAreEqual(L"-in", + ppCLPStrings[stringIndex])) + { + pPCBasicPacketInjectionData->performInline = TRUE; + + found++; + + continue; + } + + /// Threaded DPC + if(HlprStringsAreEqual(L"-tdpc", + ppCLPStrings[stringIndex])) + { + pPCBasicPacketInjectionData->useThreadedDPC = TRUE; + + found++; + + continue; + } + + /// Work Items + if(HlprStringsAreEqual(L"-wi", + ppCLPStrings[stringIndex])) + { + pPCBasicPacketInjectionData->useWorkItems = TRUE; + + found++; + + continue; + } + } + + return status; +} + +/** + @scenario_function="BasicPacketInjectionScenarioExecute" + + Purpose: Gather and package data neccessary to setup the BASIC_PACKET_INJECTION scenario, + then invoke RPC to implement the scenario in the WFPSampler service.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 BasicPacketInjectionScenarioExecute(_In_reads_(stringCount) PCWSTR* ppCLPStrings, + _In_ const UINT32 stringCount) +{ + ASSERT(ppCLPStrings); + ASSERT(stringCount); + + UINT32 status = NO_ERROR; + BOOLEAN removeScenario = FALSE; + PC_BASIC_PACKET_INJECTION_DATA* pPCBasicPacketInjectionData = 0; + FWPM_FILTER* pFilter = 0; + + status = HlprFwpmFilterCreate(&pFilter); + HLPR_BAIL_ON_FAILURE(status); + + pFilter->displayData.name = L"WFPSampler's Basic Packet Injection Scenario Filter"; + + HlprCommandLineParseForScenarioRemoval(ppCLPStrings, + stringCount, + &removeScenario); + + status = HlprCommandLineParseForFilterInfo(ppCLPStrings, + stringCount, + pFilter, + removeScenario); + HLPR_BAIL_ON_FAILURE(status); + + if(!removeScenario) + { + HLPR_NEW(pPCBasicPacketInjectionData, + PC_BASIC_PACKET_INJECTION_DATA); + HLPR_BAIL_ON_ALLOC_FAILURE(pPCBasicPacketInjectionData, + status); + + status = PrvBasicPacketInjectionScenarioParseInjectionData(ppCLPStrings, + stringCount, + pPCBasicPacketInjectionData); + HLPR_BAIL_ON_FAILURE(status); + } + + status = RPCInvokeScenarioBasicPacketInjection(wfpSamplerBindingHandle, + SCENARIO_BASIC_PACKET_INJECTION, + removeScenario ? FWPM_CHANGE_DELETE : FWPM_CHANGE_ADD, + pFilter, + pPCBasicPacketInjectionData); + if(status != NO_ERROR) + HlprLogError(L"BasicPacketInjectionScenarioExecute : RpcInvokeScenarioBasicPacketInjection() [status: %#x]", + status); + else + HlprLogInfo(L"BasicPacketInjectionScenarioExecute : RpcInvokeScenarioBasicPacketInjection() [status: %#x]", + status); + + HLPR_BAIL_LABEL: + + if(pFilter) + HlprFwpmFilterDestroy(&pFilter); + + HLPR_DELETE(pPCBasicPacketInjectionData); + + return status; +} + +/** + @public_function="BasicPacketInjectionScenarioLogHelp" + + Purpose: Log usage information for the BASIC_PACKET_INJECTION scenario to the console.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +VOID BasicPacketInjectionScenarioLogHelp() +{ + wprintf(L"\n\t\t -s \t BASIC_PACKET_INJECTION"); + wprintf(L"\n\t\t -? \t Receive usage information."); + wprintf(L"\n\t\t -l \t Specify the layer to perform the filtering. [Required]"); + wprintf(L"\n\t\t -sl \t Specify the sublayer to perform the filtering. [Optional]"); + wprintf(L"\n\t\t -r \t Remove the scenario objects."); + wprintf(L"\n\t\t -v \t Make the filter volatile (non-persistent). [Optional]"); + wprintf(L"\n\t\t -b \t Makes the objects available during boot time. [Optional]"); + wprintf(L"\n\t\t -in \t Perform the injection inline if possible. [Optional]"); + wprintf(L"\n\t\t -tdpc \t Use threaded DPCs for out of band. [Optional]"); + wprintf(L"\n\t\t -wi \t Use work items for out of band. [Optional]"); + wprintf(L"\n\t\t -ipla \t Specify the IP_LOCAL_ADDRESS /"); + wprintf(L"\n\t\t \t IP_SOURCE_ADDRESS to filter. [Optional]"); + wprintf(L"\n\t\t -ipra \t Specify the IP_REMOTE_ADDRESS /"); + wprintf(L"\n\t\t \t IP_DESTINATION_ADDRESS to filter. [Optional]"); + wprintf(L"\n\t\t -ipp \t Specify the IP_PROTOCOL to filter. [Optional]"); + wprintf(L"\n\t\t -iplp \t Specify the IP_LOCAL_PORT to filter. [Optional]"); + wprintf(L"\n\t\t -icmpt \t Specify the ICMP_TYPE to filter. [Optional]"); + wprintf(L"\n\t\t -iprp \t Specify the IP_REMOTE_PORT to filter. [Optional]"); + wprintf(L"\n\t\t -icmpc \t Specify the ICMP_CODE to filter. [Optional]"); + wprintf(L"\n"); + wprintf(L"\n\t i.e."); + wprintf(L"\n\t\t WFPSampler.Exe -s BASIC_PACKET_INJECTION -l FWPM_LAYER_INBOUND_TRANSPORT_V4 -ipla 1.0.0.1 -ipra 1.0.0.254 -ipp TCP -v"); + wprintf(L"\n\t\t WFPSampler.Exe -s BASIC_PACKET_INJECTION -l FWPM_LAYER_INBOUND_TRANSPORT_V4 -ipla 1.0.0.1 -ipra 1.0.0.254 -ipp TCP -v -r"); + wprintf(L"\n"); + + return; +} diff --git a/network/trans/WFPSampler/exe/Scenarios_BasicPacketInjection.h b/network/trans/WFPSampler/exe/Scenarios_BasicPacketInjection.h new file mode 100644 index 000000000..aa291b262 --- /dev/null +++ b/network/trans/WFPSampler/exe/Scenarios_BasicPacketInjection.h @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// Scenarios_BasicPacketInjection.h +// +// Abstract: +// This module contains prototypes for functions which run the specified BASIC_PACKET_INJECTION +// scenario. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef SCENARIOS_BASIC_PACKET_INJECTION_H +#define SCENARIOS_BASIC_PACKET_INJECTION_H + +_Success_(return == NO_ERROR) +UINT32 BasicPacketInjectionScenarioExecute(_In_reads_(stringCount) PCWSTR* ppCLPStrings, + _In_ const UINT32 stringCount); + +VOID BasicPacketInjectionScenarioLogHelp(); + +#endif /// SCENARIOS_BASIC_PACKET_INJECTION_H \ No newline at end of file diff --git a/network/trans/WFPSampler/exe/Scenarios_BasicPacketModification.cpp b/network/trans/WFPSampler/exe/Scenarios_BasicPacketModification.cpp new file mode 100644 index 000000000..ccc36ca24 --- /dev/null +++ b/network/trans/WFPSampler/exe/Scenarios_BasicPacketModification.cpp @@ -0,0 +1,581 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// Scenarios_BasicPacketModification.cpp +// +// Abstract: +// This module contains functions which prepares and sends data for the +// BASIC_PACKET_MODIFICATION scenario implementation. +// +// Naming Convention: +// +// +// +// i.e. +// +// +// { +// - Function is likely visible to other modules. +// Prv - Function is private to this module. +// } +// +// { +// BasicPacketModificationScenario - Function pertains to Basic Packet Modification +// Scenario. +// } +// +// { +// Execute - Function packages data and invokes RPC to the +// WFPSampler service. +// Log - Function writes to the console. +// Parse - Function pulls data into the required format from +// the provided data. +// } +// +// { +// ModificationData - Function acts on modification data. +// Help - Function provides context sensitive help for the +// scenario. +// } +// +// Private Functions: +// PrvBasicPacketModificationScenarioParseModificationData(), +// +// +// Public Functions: +// BasicPacketModificationScenarioExecute(), +// BasicPacketModificationScenarioLogHelp(), +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Add support for specifying a different sublayer, +// modifying the interfaceIndex, and set the original +// traffic data in the providerContext. +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "Framework_WFPSampler.h" /// . + +/** + @private_function="PrvBasicPacketModificationScenarioParseModificationData" + + Purpose: Parse the command line parameters for modifying packets such as:
+ Perform the injection inline (from within the classify) (-in)
+ Use threaded DPCs for out of band (asynchronous) (-tdpc)
+ Use work items for out of band (asynchronous) (-wi)
+ Modify MAC Source Address (-mmsa MAC_ADDRESS)
+ Modify MAC Destination Address (-mmda MAC_ADDRESS)
+ Modify IP Source Address (-misa IP_ADDRESS)
+ Modify IP's Destination Address (-mida IP_ADDRESS)
+ Modify Transport's Source Port (-mtsp PORT)
+ Modify Transport's Destination Port (-mtdp PORT)
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 PrvBasicPacketModificationScenarioParseModificationData(_In_reads_(stringCount) PCWSTR* ppCLPStrings, + _In_ const UINT32 stringCount, + _In_ const GUID* pLayerKey, + _Inout_ PC_BASIC_PACKET_MODIFICATION_DATA* pPCBasicPacketModificationData) +{ + ASSERT(ppCLPStrings); + ASSERT(stringCount); + ASSERT(pPCBasicPacketModificationData); + + UINT32 status = NO_ERROR; + + if(HlprFwpmLayerIsIPv6(pLayerKey)) + { + pPCBasicPacketModificationData->ipData.ipVersion = IPV6; + pPCBasicPacketModificationData->originalIPData.ipVersion = IPV6; + } + else + { + pPCBasicPacketModificationData->ipData.ipVersion = IPV4; + pPCBasicPacketModificationData->originalIPData.ipVersion = IPV4; + } + + + pPCBasicPacketModificationData->transportData.protocol = IPPROTO_RAW; + pPCBasicPacketModificationData->originalTransportData.protocol = IPPROTO_RAW; + + for(UINT32 stringIndex = 0; + stringIndex < stringCount; + stringIndex++) + { + /// Inline Injection + if(HlprStringsAreEqual(L"-in", + ppCLPStrings[stringIndex])) + { + pPCBasicPacketModificationData->performInline = TRUE; + + continue; + } + + /// Threaded DPC + if(HlprStringsAreEqual(L"-tdpc", + ppCLPStrings[stringIndex])) + { + pPCBasicPacketModificationData->useThreadedDPC = TRUE; + + + continue; + } + + /// Work Items + if(HlprStringsAreEqual(L"-wi", + ppCLPStrings[stringIndex])) + { + pPCBasicPacketModificationData->useWorkItems = TRUE; + + continue; + } + + if((stringIndex + 1) < stringCount) + { + /// Original protocol + if(HlprStringsAreEqual(L"-ipp", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[stringIndex + 1]; + + if(iswdigit((wint_t)pString[0])) + { + UINT32 protocol = wcstol(pString, + 0, + 0); + + if(protocol <= 0xFF) + { + pPCBasicPacketModificationData->transportData.protocol = (UINT8)(protocol & 0xFF); + pPCBasicPacketModificationData->originalTransportData.protocol = (UINT8)(protocol & 0xFF); + } + } + else + { + UINT32 protocol = IPPROTO_MAX; + + if(HlprStringsAreEqual(L"TCP", + pString)) + protocol = IPPROTO_TCP; + else if(HlprStringsAreEqual(L"UDP", + pString)) + protocol = IPPROTO_UDP; + else if(HlprStringsAreEqual(L"ICMPV4", + pString)) + protocol = IPPROTO_ICMP; + else if(HlprStringsAreEqual(L"ICMPV6", + pString)) + protocol = IPPROTO_ICMPV6; + + pPCBasicPacketModificationData->transportData.protocol = (UINT8)(protocol & 0xFF); + pPCBasicPacketModificationData->originalTransportData.protocol = (UINT8)(protocol & 0xFF); + } + + continue; + } + + /// Original source port + if(HlprStringsAreEqual(L"-ipsp", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[stringIndex + 1]; + + if(iswdigit((wint_t)pString[0])) + { + UINT32 port = wcstol(pString, + 0, + 0); + + if(port <= 0xFFFF) + { + pPCBasicPacketModificationData->originalTransportData.source.port = (UINT16)(port & 0xFFFF); + pPCBasicPacketModificationData->originalTransportData.source.port = htons(pPCBasicPacketModificationData->originalTransportData.source.port); + } + } + + continue; + } + + /// Original destination port + if(HlprStringsAreEqual(L"-ipdp", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[stringIndex + 1]; + + if(iswdigit((wint_t)pString[0])) + { + UINT32 port = wcstol(pString, + 0, + 0); + + if(port <= 0xFFFF) + { + pPCBasicPacketModificationData->originalTransportData.destination.port = (UINT16)(port & 0xFFFF); + pPCBasicPacketModificationData->originalTransportData.destination.port = htons(pPCBasicPacketModificationData->originalTransportData.destination.port); + } + } + + continue; + } + + /// Modify MAC Source Address + if(HlprStringsAreEqual(L"-mmsa", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[stringIndex + 1]; + + if(HlprEthernetMACAddressStringIsValidFormat(pString)) + { + status = HlprEthernetMACAddressStringToValue(pString, + (BYTE*)pPCBasicPacketModificationData->macData.pSourceMACAddress); + HLPR_BAIL_ON_FAILURE(status); + } + else + { + status = ERROR_INVALID_DATA; + + HlprLogError(L"PrvBasicPacketModificationScenarioParseData() [status: %#x][source MAC Address: %s]", + status, + pString); + + HLPR_BAIL; + } + + pPCBasicPacketModificationData->flags |= PCPMDF_MODIFY_MAC_HEADER; + + pPCBasicPacketModificationData->macData.flags |= PCPMDF_MODIFY_MAC_HEADER_SOURCE_ADDRESS; + + continue; + } + + /// Modify MAC Source Address + if(HlprStringsAreEqual(L"-mmda", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[stringIndex + 1]; + + if(HlprEthernetMACAddressStringIsValidFormat(pString)) + { + status = HlprEthernetMACAddressStringToValue(pString, + (BYTE*)pPCBasicPacketModificationData->macData.pDestinationMACAddress); + HLPR_BAIL_ON_FAILURE(status); + } + else + { + status = ERROR_INVALID_DATA; + + HlprLogError(L"PrvBasicPacketModificationScenarioParseData() [status: %#x][destination MAC Address: %s]", + status, + pString); + + HLPR_BAIL; + } + + pPCBasicPacketModificationData->flags |= PCPMDF_MODIFY_MAC_HEADER; + + pPCBasicPacketModificationData->macData.flags |= PCPMDF_MODIFY_MAC_HEADER_DESTINATION_ADDRESS; + + continue; + } + + /// Modify IP Source Address + if(HlprStringsAreEqual(L"-misa", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[stringIndex + 1]; + + if(pPCBasicPacketModificationData->ipData.ipVersion == IPV6 && + HlprIPAddressV6StringIsValidFormat(pString)) + { + status = HlprIPAddressV6StringToValue(pString, + (BYTE*)pPCBasicPacketModificationData->ipData.sourceAddress.pIPv6); + HLPR_BAIL_ON_FAILURE(status); + } + else if(pPCBasicPacketModificationData->ipData.ipVersion == IPV4) + { + UINT32 sourceAddress = 0; + + status = HlprIPAddressV4StringToValue(pString, + &sourceAddress); + HLPR_BAIL_ON_FAILURE(status); + + sourceAddress = htonl(sourceAddress); + + RtlCopyMemory(pPCBasicPacketModificationData->ipData.sourceAddress.pIPv4, + &sourceAddress, + sizeof(UINT32)); + } + else + { + status = ERROR_INVALID_DATA; + + HlprLogError(L"PrvBasicPacketModificationScenarioParseModificationData() [status: %#x][ipVersion: %d][source ipAddress: %s]", + status, + pPCBasicPacketModificationData->ipData.ipVersion, + pString); + + HLPR_BAIL; + } + + pPCBasicPacketModificationData->flags |= PCPMDF_MODIFY_IP_HEADER; + + pPCBasicPacketModificationData->ipData.flags |= PCPMDF_MODIFY_IP_HEADER_SOURCE_ADDRESS; + + continue; + } + + /// Modify IP Destination Address + if(HlprStringsAreEqual(L"-mida", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[stringIndex + 1]; + + if(pPCBasicPacketModificationData->ipData.ipVersion == IPV6 && + HlprIPAddressV6StringIsValidFormat(pString)) + { + status = HlprIPAddressV6StringToValue(pString, + (BYTE*)pPCBasicPacketModificationData->ipData.destinationAddress.pIPv6); + HLPR_BAIL_ON_FAILURE(status); + } + else if(pPCBasicPacketModificationData->ipData.ipVersion == IPV4) + { + UINT32 destinationAddress = 0; + + status = HlprIPAddressV4StringToValue(pString, + &destinationAddress); + HLPR_BAIL_ON_FAILURE(status); + + destinationAddress = htonl(destinationAddress); + + RtlCopyMemory(pPCBasicPacketModificationData->ipData.destinationAddress.pIPv4, + &destinationAddress, + sizeof(UINT32)); + } + else + { + status = ERROR_INVALID_DATA; + + HlprLogError(L"PrvBasicPacketModificationScenarioParseModificationData() [status: %#x][ipVersion: %d][destination ipAddress: %s]", + status, + pPCBasicPacketModificationData->ipData.ipVersion, + pString); + + HLPR_BAIL; + } + + pPCBasicPacketModificationData->flags |= PCPMDF_MODIFY_IP_HEADER; + + pPCBasicPacketModificationData->ipData.flags |= PCPMDF_MODIFY_IP_HEADER_DESTINATION_ADDRESS; + + continue; + } + + /// Modify the Interface Index to inject to + if(HlprStringsAreEqual(L"-mii", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[stringIndex + 1]; + + if(iswdigit((wint_t)pString[0])) + { + UINT32 interfaceIndex = wcstol(pString, + 0, + 0); + + pPCBasicPacketModificationData->ipData.interfaceIndex = interfaceIndex; + + pPCBasicPacketModificationData->flags |= PCPMDF_MODIFY_IP_HEADER; + + pPCBasicPacketModificationData->ipData.flags |= PCPMDF_MODIFY_IP_HEADER_INTERFACE_INDEX; + } + + continue; + } + + /// Modify Transport Source Port + if(HlprStringsAreEqual(L"-mtsp", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[stringIndex + 1]; + + if(iswdigit((wint_t)pString[0])) + { + UINT32 localPort = wcstol(pString, + 0, + 0); + if(localPort <= 0xFFFF) + { + UINT16 port = localPort & 0xFFFF; + + pPCBasicPacketModificationData->transportData.source.port = htons(port); + } + + pPCBasicPacketModificationData->flags |= PCPMDF_MODIFY_TRANSPORT_HEADER; + + pPCBasicPacketModificationData->transportData.flags |= PCPMDF_MODIFY_TRANSPORT_HEADER_SOURCE_PORT; + } + + continue; + } + + /// Modify Transport Destination Port + if(HlprStringsAreEqual(L"-mtdp", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[stringIndex + 1]; + + if(iswdigit((wint_t)pString[0])) + { + UINT32 localPort = wcstol(pString, + 0, + 0); + if(localPort <= 0xFFFF) + { + UINT16 port = localPort & 0xFFFF; + + pPCBasicPacketModificationData->transportData.destination.port = htons(port); + } + + pPCBasicPacketModificationData->flags |= PCPMDF_MODIFY_TRANSPORT_HEADER; + + pPCBasicPacketModificationData->transportData.flags |= PCPMDF_MODIFY_TRANSPORT_HEADER_DESTINATION_PORT; + } + + continue; + } + } + } + + HLPR_BAIL_LABEL: + + return status; +} + +/** + @scenario_function="BasicPacketModificationScenarioExecute" + + Purpose: Gather and package data neccessary to setup the BASIC_PACKET_MODIFICATION scenario, + then invoke RPC to implement the scenario in the WFPSampler service.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 BasicPacketModificationScenarioExecute(_In_reads_(stringCount) PCWSTR* ppCLPStrings, + _In_ const UINT32 stringCount) +{ + ASSERT(ppCLPStrings); + ASSERT(stringCount); + + UINT32 status = NO_ERROR; + BOOLEAN removeScenario = FALSE; + PC_BASIC_PACKET_MODIFICATION_DATA* pPCBasicPacketModificationData = 0; + FWPM_FILTER* pFilter = 0; + + status = HlprFwpmFilterCreate(&pFilter); + HLPR_BAIL_ON_FAILURE(status); + + pFilter->displayData.name = L"WFPSampler's Basic Packet Modification Scenario Filter"; + + HlprCommandLineParseForScenarioRemoval(ppCLPStrings, + stringCount, + &removeScenario); + + status = HlprCommandLineParseForFilterInfo(ppCLPStrings, + stringCount, + pFilter, + removeScenario); + HLPR_BAIL_ON_FAILURE(status); + + if(!removeScenario) + { + HLPR_NEW(pPCBasicPacketModificationData, + PC_BASIC_PACKET_MODIFICATION_DATA); + HLPR_BAIL_ON_ALLOC_FAILURE(pPCBasicPacketModificationData, + status); + + status = PrvBasicPacketModificationScenarioParseModificationData(ppCLPStrings, + stringCount, + &(pFilter->layerKey), + pPCBasicPacketModificationData); + HLPR_BAIL_ON_FAILURE(status); + } + + status = RPCInvokeScenarioBasicPacketModification(wfpSamplerBindingHandle, + SCENARIO_BASIC_PACKET_MODIFICATION, + removeScenario ? FWPM_CHANGE_DELETE : FWPM_CHANGE_ADD, + pFilter, + pPCBasicPacketModificationData); + if(status != NO_ERROR) + HlprLogError(L"BasicPacketModificationScenarioExecute : RpcInvokeScenarioBasicPacketModification() [status: %#x]", + status); + else + HlprLogInfo(L"BasicPacketModificationScenarioExecute : RpcInvokeScenarioBasicPacketModification() [status: %#x]", + status); + + HLPR_BAIL_LABEL: + + if(pFilter) + HlprFwpmFilterDestroy(&pFilter); + + HLPR_DELETE(pPCBasicPacketModificationData); + + return status; +} + +/** + @public_function="BasicPacketModificationScenarioLogHelp" + + Purpose: Log usage information for the BASIC_PACKET_MODIFICATION scenario to the console.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +VOID BasicPacketModificationScenarioLogHelp() +{ + wprintf(L"\n\t\t -s \t BASIC_PACKET_MODIFICATION"); + wprintf(L"\n\t\t -? \t Receive usage information."); + wprintf(L"\n\t\t -l \t Specify the layer to perform the filtering. [Required]"); + wprintf(L"\n\t\t -sl \t Specify the sublayer to perform the filtering. [Optional]"); + wprintf(L"\n\t\t -r \t Remove the scenario objects."); + wprintf(L"\n\t\t -v \t Make the filter volatile (non-persistent). [Optional]"); + wprintf(L"\n\t\t -b \t Makes the objects available during boot time. [Optional]"); + wprintf(L"\n\t\t -in \t Perform the injection inline if possible. [Optional]"); + wprintf(L"\n\t\t -tdpc \t Use threaded DPCs for out of band. [Optional]"); + wprintf(L"\n\t\t -wi \t Use work items for out of band. [Optional]"); + wprintf(L"\n\t\t -ipla \t Specify the IP_LOCAL_ADDRESS /"); + wprintf(L"\n\t\t \t IP_SOURCE_ADDRESS to filter. [Optional]"); + wprintf(L"\n\t\t -ipra \t Specify the IP_REMOTE_ADDRESS /"); + wprintf(L"\n\t\t \t IP_DESTINATION_ADDRESS to filter. [Optional]"); + wprintf(L"\n\t\t -ipp \t Specify the IP_PROTOCOL to filter. [Optional]"); + wprintf(L"\n\t\t -iplp \t Specify the IP_LOCAL_PORT to filter. [Optional]"); + wprintf(L"\n\t\t -icmpt \t Specify the ICMP_TYPE to filter. [Optional]"); + wprintf(L"\n\t\t -iprp \t Specify the IP_REMOTE_PORT to filter. [Optional]"); + wprintf(L"\n\t\t -icmpc \t Specify the ICMP_CODE to filter. [Optional]"); + wprintf(L"\n\t\t -mmsa \t Specify the new source MAC address. [Optional]"); + wprintf(L"\n\t\t -mmda \t Specify the new destination MAC address. [Optional]"); + wprintf(L"\n\t\t -mii \t Specify the new interface index to inject to. [Optional]"); + wprintf(L"\n\t\t -misa \t Specify the new source IP address. [Optional]"); + wprintf(L"\n\t\t -mida \t Specify the new destination IP address. [Optional]"); + wprintf(L"\n\t\t -mtsp \t Specify the new source transport port / icmp code. [Optional]"); + wprintf(L"\n\t\t -mtdp \t Specify the new destination transport port / icmp type. [Optional]"); + wprintf(L"\n"); + wprintf(L"\n\t i.e."); + wprintf(L"\n\t\t WFPSampler.Exe -s BASIC_PACKET_MODIFICATION -l FWPM_LAYER_OUTBOUND_TRANSPORT_V4 -ipla 1.0.0.1 -ipra 1.0.0.254 -ipp UDP -mtdp 4000 -v"); + wprintf(L"\n\t\t WFPSampler.Exe -s BASIC_PACKET_MODIFICATION -l FWPM_LAYER_OUTBOUND_TRANSPORT_V4 -ipla 1.0.0.1 -ipra 1.0.0.254 -ipp UDP -mtdp 4000 -v -r"); + wprintf(L"\n"); + + return; +} diff --git a/network/trans/WFPSampler/exe/Scenarios_BasicPacketModification.h b/network/trans/WFPSampler/exe/Scenarios_BasicPacketModification.h new file mode 100644 index 000000000..c9472be22 --- /dev/null +++ b/network/trans/WFPSampler/exe/Scenarios_BasicPacketModification.h @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// Scenarios_BasicPacketModification.h +// +// Abstract: +// This module contains prototypes for functions which run the specified +// BASIC_PACKET_MODIFICATION scenario. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef SCENARIOS_BASIC_PACKET_MODIFICATION_H +#define SCENARIOS_BASIC_PACKET_MODIFICATION_H + +_Success_(return == NO_ERROR) +UINT32 BasicPacketModificationScenarioExecute(_In_reads_(stringCount) PCWSTR* ppCLPStrings, + _In_ const UINT32 stringCount); + +VOID BasicPacketModificationScenarioLogHelp(); + +#endif /// SCENARIOS_BASIC_PACKET_MODIFICATION_H \ No newline at end of file diff --git a/network/trans/WFPSampler/exe/Scenarios_BasicStreamInjection.cpp b/network/trans/WFPSampler/exe/Scenarios_BasicStreamInjection.cpp new file mode 100644 index 000000000..c92c87d71 --- /dev/null +++ b/network/trans/WFPSampler/exe/Scenarios_BasicStreamInjection.cpp @@ -0,0 +1,236 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// Scenarios_BasicStreamInjection.cpp +// +// Abstract: +// This module contains functions which prepares and sends data for the BASIC_STREAM_INJECTION +// scenario implementation. +// +// Naming Convention: +// +// +// +// i.e. +// +// +// { +// - Function is likely visible to other modules. +// Prv - Function is private to this module. +// } +// +// { +// BasicStreamInjectionScenario - Function pertains to the Basic Stream Injection +// Scenario. +// } +// +// { +// Execute - Function packages data and invokes RPC to the +// WFPSampler service. +// Log - Function writes to the console. +// Parse - Function pulls data into the required format from the +// provided data. +// } +// +// { +// Help - Function provides context sensitive help for the +// scenario. +// InjectionData - Function acts on the PC_BASIC_STREAM_INJECTION_DATA. +// } +// +// Private Functions: +// PrvScenarioBasicStreamInjectionParseInjectionData(), +// +// Public Functions: +// BasicStreamInjectionScenarioExecute(), +// BasicStreamInjectionScenarioLogHelp(), +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// June 15, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Add support for specifying a different sublayer +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "Framework_WFPSampler.h" /// . + +/** + @private_function="PrvBasicStreamInjectionScenarioParseInjectionData" + + Purpose: Parse the command line parameters for implementing stream injection such as:
+ Perform the injection inline (from within the classify) (-in)
+ Use threaded DPCs for out of band (asynchronous) (-tdpc)
+ Use work items for out of band (asynchronous) (-wi)
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 PrvBasicStreamInjectionScenarioParseInjectionData(_In_reads_(stringCount) PCWSTR* ppCLPStrings, + _In_ const UINT32 stringCount, + _Inout_ PC_BASIC_STREAM_INJECTION_DATA* pPCBasicStreamInjectionData) +{ + ASSERT(ppCLPStrings); + ASSERT(stringCount); + ASSERT(pPCBasicStreamInjectionData); + + UINT32 status = NO_ERROR; + const UINT32 MAX_PARAMETERS = 3; + UINT32 found = 0; + + for(UINT32 stringIndex = 0; + stringIndex < stringCount && + found != MAX_PARAMETERS; + stringIndex++) + { + /// Inline Injection + if(HlprStringsAreEqual(L"-in", + ppCLPStrings[stringIndex])) + { + pPCBasicStreamInjectionData->performInline = TRUE; + + found++; + + continue; + } + + /// Threaded DPC + if(HlprStringsAreEqual(L"-tdpc", + ppCLPStrings[stringIndex])) + { + pPCBasicStreamInjectionData->useThreadedDPC = TRUE; + + found++; + + continue; + } + + /// Work Items + if(HlprStringsAreEqual(L"-wi", + ppCLPStrings[stringIndex])) + { + pPCBasicStreamInjectionData->useWorkItems = TRUE; + + found++; + + continue; + } + } + + return status; +} + +/** + @scenario_function="BasicStreamInjectionScenarioExecute" + + Purpose: Gather and package data neccessary to setup the BASIC_STREAM_INJECTION scenario, + then invoke RPC to implement the scenario in the WFPSampler service.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 BasicStreamInjectionScenarioExecute(_In_reads_(stringCount) PCWSTR* ppCLPStrings, + _In_ const UINT32 stringCount) +{ + ASSERT(ppCLPStrings); + ASSERT(stringCount); + + UINT32 status = NO_ERROR; + BOOLEAN removeScenario = FALSE; + PC_BASIC_STREAM_INJECTION_DATA* pPCBasicStreamInjectionData = 0; + FWPM_FILTER* pFilter = 0; + + status = HlprFwpmFilterCreate(&pFilter); + HLPR_BAIL_ON_FAILURE(status); + + pFilter->displayData.name = L"WFPSampler's Basic Stream Injection Scenario Filter"; + + HlprCommandLineParseForScenarioRemoval(ppCLPStrings, + stringCount, + &removeScenario); + + status = HlprCommandLineParseForFilterInfo(ppCLPStrings, + stringCount, + pFilter, + removeScenario); + HLPR_BAIL_ON_FAILURE(status); + + if(!removeScenario) + { + HLPR_NEW(pPCBasicStreamInjectionData, + PC_BASIC_STREAM_INJECTION_DATA); + HLPR_BAIL_ON_ALLOC_FAILURE(pPCBasicStreamInjectionData, + status); + + status = PrvBasicStreamInjectionScenarioParseInjectionData(ppCLPStrings, + stringCount, + pPCBasicStreamInjectionData); + HLPR_BAIL_ON_FAILURE(status); + } + + status = RPCInvokeScenarioBasicStreamInjection(wfpSamplerBindingHandle, + SCENARIO_BASIC_STREAM_INJECTION, + removeScenario ? FWPM_CHANGE_DELETE : FWPM_CHANGE_ADD, + pFilter, + pPCBasicStreamInjectionData); + if(status != NO_ERROR) + HlprLogError(L"BasicStreamInjectionScenarioExecute : RpcInvokeScenarioBasicStreamInjection() [status: %#x]", + status); + else + HlprLogInfo(L"BasicStreamInjectionScenarioExecute : RpcInvokeScenarioBasicStreamInjection() [status: %#x]", + status); + + HLPR_BAIL_LABEL: + + if(pFilter) + HlprFwpmFilterDestroy(&pFilter); + + HLPR_DELETE(pPCBasicStreamInjectionData); + + return status; +} + +/** + @public_function="BasicStreamInjectionScenarioLogHelp" + + Purpose: Log usage information for the BASIC_STREAM_INJECTION scenario to the console.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +VOID BasicStreamInjectionScenarioLogHelp() +{ + wprintf(L"\n\t\t -s \t BASIC_STREAM_INJECTION"); + wprintf(L"\n\t\t -? \t Receive usage information."); + wprintf(L"\n\t\t -l \t Specify the layer to perform the filtering. [Required]"); + wprintf(L"\n\t\t -sl \t Specify the sublayer to perform the filtering. [Optional]"); + wprintf(L"\n\t\t -r \t Remove the scenario objects."); + wprintf(L"\n\t\t -v \t Make the filter volatile (non-persistent). [Optional]"); + wprintf(L"\n\t\t -b \t Makes the objects available during boot time. [Optional]"); + wprintf(L"\n\t\t -in \t Perform the injection inline if possible. [Optional]"); + wprintf(L"\n\t\t -tdpc \t Use threaded DPCs for out of band. [Optional]"); + wprintf(L"\n\t\t -wi \t Use work items for out of band. [Optional]"); + wprintf(L"\n\t\t -ipla \t Specify the IP_LOCAL_ADDRESS /"); + wprintf(L"\n\t\t \t IP_SOURCE_ADDRESS to filter. [Optional]"); + wprintf(L"\n\t\t -ipra \t Specify the IP_REMOTE_ADDRESS /"); + wprintf(L"\n\t\t \t IP_DESTINATION_ADDRESS to filter. [Optional]"); + wprintf(L"\n\t\t -iplp \t Specify the IP_LOCAL_PORT to filter. [Optional]"); + wprintf(L"\n\t\t -iprp \t Specify the IP_REMOTE_PORT to filter. [Optional]"); + wprintf(L"\n"); + wprintf(L"\n\t i.e."); + wprintf(L"\n\t\t WFPSampler.Exe -s BASIC_STREAM_INJECTION -l FWPM_LAYER_STREAM_V4 -ipla 1.0.0.1 -ipra 1.0.0.254 -v"); + wprintf(L"\n\t\t WFPSampler.Exe -s BASIC_STREAM_INJECTION -l FWPM_LAYER_STREAM_V4 -ipla 1.0.0.1 -ipra 1.0.0.254 -v -r"); + wprintf(L"\n"); + + return; +} diff --git a/network/trans/WFPSampler/exe/Scenarios_BasicStreamInjection.h b/network/trans/WFPSampler/exe/Scenarios_BasicStreamInjection.h new file mode 100644 index 000000000..412a98527 --- /dev/null +++ b/network/trans/WFPSampler/exe/Scenarios_BasicStreamInjection.h @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// Scenarios_BasicStreamInjection.h +// +// Abstract: +// This module contains prototypes for functions which run the specified BASIC_STREAM_INJECTION +// scenario. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// June 15, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef SCENARIOS_BASIC_STREAM_INJECTION_H +#define SCENARIOS_BASIC_STREAM_INJECTION_H + +_Success_(return == NO_ERROR) +UINT32 BasicStreamInjectionScenarioExecute(_In_reads_(stringCount) PCWSTR* ppCLPStrings, + _In_ const UINT32 stringCount); + +VOID BasicStreamInjectionScenarioLogHelp(); + +#endif /// SCENARIOS_BASIC_STREAM_INJECTION_H diff --git a/network/trans/WFPSampler/exe/Scenarios_FastPacketInjection.cpp b/network/trans/WFPSampler/exe/Scenarios_FastPacketInjection.cpp new file mode 100644 index 000000000..b506bbb90 --- /dev/null +++ b/network/trans/WFPSampler/exe/Scenarios_FastPacketInjection.cpp @@ -0,0 +1,149 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// Scenarios_FastPacketInjection.cpp +// +// Abstract: +// This module contains functions which prepares and sends data for the FAST_PACKET_INJECTION +// scenario implementation. +// +// Naming Convention: +// +// +// +// i.e. +// +// +// { +// - Function is likely visible to other modules. +// Prv - Function is private to this module. +// } +// +// { +// FastPacketInjectionScenario - Function pertains to Fast Packet Injection Scenario. +// } +// +// { +// Execute - Function packages data and invokes RPC to the +// WFPSampler service +// Log - Function writes to the console. +// Parse - Function pulls data into the required format from the +// provided data. +// } +// +// { +// Help - Function provides context sensitive help for the +// scenario. +// } +// +// Private Functions: +// +// Public Functions: +// FastPacketInjectionScenarioExecute(), +// FastPacketInjectionScenarioLogHelp(), +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Add support for specifying a different sublayer +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "Framework_WFPSampler.h" /// . + +/** + @scenario_function="FastPacketInjectionScenarioExecute" + + Purpose: Gather and package data neccessary to setup the FAST_PACKET_INJECTION scenario, + then invoke RPC to implement the scenario in the WFPSampler service.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 FastPacketInjectionScenarioExecute(_In_reads_(stringCount) PCWSTR* ppCLPStrings, + _In_ const UINT32 stringCount) +{ + ASSERT(ppCLPStrings); + ASSERT(stringCount); + + UINT32 status = NO_ERROR; + BOOLEAN removeScenario = FALSE; + FWPM_FILTER* pFilter = 0; + + status = HlprFwpmFilterCreate(&pFilter); + HLPR_BAIL_ON_FAILURE(status); + + pFilter->displayData.name = L"WFPSampler's Fast Packet Injection Scenario Filter"; + + HlprCommandLineParseForScenarioRemoval(ppCLPStrings, + stringCount, + &removeScenario); + + status = HlprCommandLineParseForFilterInfo(ppCLPStrings, + stringCount, + pFilter, + removeScenario); + HLPR_BAIL_ON_FAILURE(status); + + status = RPCInvokeScenarioFastPacketInjection(wfpSamplerBindingHandle, + SCENARIO_FAST_PACKET_INJECTION, + removeScenario ? FWPM_CHANGE_DELETE : FWPM_CHANGE_ADD, + pFilter); + if(status != NO_ERROR) + HlprLogError(L"FastPacketInjectionScenarioExecute : RpcInvokeScenarioFastPacketInjection() [status: %#x]", + status); + else + HlprLogInfo(L"FastPacketInjectionScenarioExecute : RpcInvokeScenarioFastPacketInjection() [status: %#x]", + status); + + HLPR_BAIL_LABEL: + + if(pFilter) + HlprFwpmFilterDestroy(&pFilter); + + return status; +} + +/** + @public_function="FastPacketInjectionScenarioLogHelp" + + Purpose: Log usage information for the FAST_PACKET_INJECTION scenario to the console.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +VOID FastPacketInjectionScenarioLogHelp() +{ + wprintf(L"\n\t\t -s \t FAST_PACKET_INJECTION"); + wprintf(L"\n\t\t -? \t Receive usage information."); + wprintf(L"\n\t\t -l \t Specify the layer to perform the filtering. [Required]"); + wprintf(L"\n\t\t -sl \t Specify the sublayer to perform the filtering. [Optional]"); + wprintf(L"\n\t\t -r \t Remove the scenario objects."); + wprintf(L"\n\t\t -v \t Make the filter volatile (non-persistent). [Optional]"); + wprintf(L"\n\t\t -b \t Makes the objects available during boot time. [Optional]"); + wprintf(L"\n\t\t -ipla \t Specify the IP_LOCAL_ADDRESS /"); + wprintf(L"\n\t\t \t IP_SOURCE_ADDRESS to filter. [Optional]"); + wprintf(L"\n\t\t -ipra \t Specify the IP_REMOTE_ADDRESS /"); + wprintf(L"\n\t\t \t IP_DESTINATION_ADDRESS to filter. [Optional]"); + wprintf(L"\n\t\t -ipp \t Specify the IP_PROTOCOL to filter. [Optional]"); + wprintf(L"\n\t\t -iplp \t Specify the IP_LOCAL_PORT to filter. [Optional]"); + wprintf(L"\n\t\t -icmpt \t Specify the ICMP_TYPE to filter. [Optional]"); + wprintf(L"\n\t\t -iprp \t Specify the IP_REMOTE_PORT to filter. [Optional]"); + wprintf(L"\n\t\t -icmpc \t Specify the ICMP_CODE to filter. [Optional]"); + wprintf(L"\n"); + wprintf(L"\n\t i.e."); + wprintf(L"\n\t\t WFPSampler.Exe -s FAST_PACKET_INJECTION -l FWPM_LAYER_INBOUND_TRANSPORT_V4 -ipla 1.0.0.1 -ipra 1.0.0.254 -ipp TCP -v"); + wprintf(L"\n\t\t WFPSampler.Exe -s FAST_PACKET_INJECTION -l FWPM_LAYER_INBOUND_TRANSPORT_V4 -ipla 1.0.0.1 -ipra 1.0.0.254 -ipp TCP -v -r"); + wprintf(L"\n"); + + return; +} diff --git a/network/trans/WFPSampler/exe/Scenarios_FastPacketInjection.h b/network/trans/WFPSampler/exe/Scenarios_FastPacketInjection.h new file mode 100644 index 000000000..c7bc2623a --- /dev/null +++ b/network/trans/WFPSampler/exe/Scenarios_FastPacketInjection.h @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// Scenarios_FastPacketInjection.h +// +// Abstract: +// This module contains prototypes for functions which run the specified FAST_PACKET_INJECTION +// scenario. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef SCENARIOS_FAST_PACKET_INJECTION_H +#define SCENARIOS_FAST_PACKET_INJECTION_H + +_Success_(return == NO_ERROR) +UINT32 FastPacketInjectionScenarioExecute(_In_reads_(stringCount) PCWSTR* ppCLPStrings, + _In_ const UINT32 stringCount); + +VOID FastPacketInjectionScenarioLogHelp(); + +#endif /// SCENARIOS_FAST_PACKET_INJECTION_H \ No newline at end of file diff --git a/network/trans/WFPSampler/exe/Scenarios_FastStreamInjection.cpp b/network/trans/WFPSampler/exe/Scenarios_FastStreamInjection.cpp new file mode 100644 index 000000000..9ddd883c3 --- /dev/null +++ b/network/trans/WFPSampler/exe/Scenarios_FastStreamInjection.cpp @@ -0,0 +1,157 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// Scenarios_FastStreamInjection.cpp +// +// Abstract: +// This module contains functions which prepares and sends data for the FAST_STREAM_INJECTION +// scenario implementation. +// +// Naming Convention: +// +// +// +// i.e. +// +// +// { +// - Function is likely visible to other modules. +// Prv - Function is private to this module. +// } +// +// { +// FastStreamInjectionScenario - Function pertains to Fast Stream Injection Scenario. +// } +// +// { +// Execute - Function packages data and invokes RPC to the +// WFPSampler service +// Log - Function writes to the console. +// Parse - Function pulls data into the required format from the +// provided data. +// } +// +// { +// Help - Function provides context sensitive help for the +// scenario. +// } +// +// Private Functions: +// +// Public Functions: +// FastStreamInjectionScenarioExecute(), +// FastStreamInjectionScenarioLogHelp(), +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Add support for specifying a different sublayer +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "Framework_WFPSampler.h" /// . + +/** + @scenario_function="FastStreamInjectionScenarioExecute" + + Purpose: Gather and package data neccessary to setup the FAST_STREAM_INJECTION scenario, + then invoke RPC to implement the scenario in the WFPSampler service.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 FastStreamInjectionScenarioExecute(_In_reads_(stringCount) PCWSTR* ppCLPStrings, + _In_ const UINT32 stringCount) +{ + ASSERT(ppCLPStrings); + ASSERT(stringCount); + + UINT32 status = NO_ERROR; + BOOLEAN removeScenario = FALSE; + FWPM_FILTER* pFilter = 0; + + status = HlprFwpmFilterCreate(&pFilter); + HLPR_BAIL_ON_FAILURE(status); + + pFilter->displayData.name = L"WFPSampler's Fast Stream Injection Scenario Filter"; + + HlprCommandLineParseForScenarioRemoval(ppCLPStrings, + stringCount, + &removeScenario); + + status = HlprCommandLineParseForFilterInfo(ppCLPStrings, + stringCount, + pFilter, + removeScenario); + HLPR_BAIL_ON_FAILURE(status); + + if(pFilter->layerKey == FWPM_LAYER_STREAM_V4 || + pFilter->layerKey == FWPM_LAYER_STREAM_V6) + { + status = RPCInvokeScenarioFastStreamInjection(wfpSamplerBindingHandle, + SCENARIO_FAST_STREAM_INJECTION, + removeScenario ? FWPM_CHANGE_DELETE : FWPM_CHANGE_ADD, + pFilter); + if(status != NO_ERROR) + HlprLogError(L"FastStreamInjectionScenarioExecute : RpcInvokeScenarioFastStreamInjection() [status: %#x]", + status); + else + HlprLogInfo(L"FastStreamInjectionScenarioExecute : RpcInvokeScenarioFastStreamInjection() [status: %#x]", + status); + } + else + { + status = (UINT32)FWP_E_INCOMPATIBLE_LAYER; + + HlprLogError(L"FastStreamInjectionScenarioExecute() [status: %#x]", + status); + } + + HLPR_BAIL_LABEL: + + if(pFilter) + HlprFwpmFilterDestroy(&pFilter); + + return status; +} + +/** + @public_function="FastStreamInjectionScenarioLogHelp" + + Purpose: Log usage information for the FAST_STREAM_INJECTION scenario to the console.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +VOID FastStreamInjectionScenarioLogHelp() +{ + wprintf(L"\n\t\t -s \t FAST_STREAM_INJECTION"); + wprintf(L"\n\t\t -? \t Receive usage information."); + wprintf(L"\n\t\t -l \t Specify the layer to perform the filtering. [Required]"); + wprintf(L"\n\t\t -sl \t Specify the sublayer to perform the filtering. [Optional]"); + wprintf(L"\n\t\t -r \t Remove the scenario objects."); + wprintf(L"\n\t\t -v \t Make the filter volatile (non-persistent). [Optional]"); + wprintf(L"\n\t\t -b \t Makes the objects available during boot time. [Optional]"); + wprintf(L"\n\t\t -ipla \t Specify the IP_LOCAL_ADDRESS /"); + wprintf(L"\n\t\t \t IP_SOURCE_ADDRESS to filter. [Optional]"); + wprintf(L"\n\t\t -ipra \t Specify the IP_REMOTE_ADDRESS /"); + wprintf(L"\n\t\t \t IP_DESTINATION_ADDRESS to filter. [Optional]"); + wprintf(L"\n\t\t -iplp \t Specify the IP_LOCAL_PORT to filter. [Optional]"); + wprintf(L"\n\t\t -iprp \t Specify the IP_REMOTE_PORT to filter. [Optional]"); + wprintf(L"\n"); + wprintf(L"\n\t i.e."); + wprintf(L"\n\t\t WFPSampler.Exe -s FAST_STREAM_INJECTION -l FWPM_LAYER_STREAM_V4 -ipla 1.0.0.1 -ipra 1.0.0.254 -iprp 80 -v"); + wprintf(L"\n\t\t WFPSampler.Exe -s FAST_STREAM_INJECTION -l FWPM_LAYER_STREAM_V4 -ipla 1.0.0.1 -ipra 1.0.0.254 -iprp 80 -v -r"); + wprintf(L"\n"); + + return; +} diff --git a/network/trans/WFPSampler/exe/Scenarios_FastStreamInjection.h b/network/trans/WFPSampler/exe/Scenarios_FastStreamInjection.h new file mode 100644 index 000000000..eb6599663 --- /dev/null +++ b/network/trans/WFPSampler/exe/Scenarios_FastStreamInjection.h @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// Scenarios_FastStreamInjection.h +// +// Abstract: +// This module contains prototypes for functions which run the specified FAST_STREAM_INJECTION +// scenario. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef SCENARIOS_FAST_STREAM_INJECTION_H +#define SCENARIOS_FAST_STREAM_INJECTION_H + +_Success_(return == NO_ERROR) +UINT32 FastStreamInjectionScenarioExecute(_In_reads_(stringCount) PCWSTR* ppCLPStrings, + _In_ const UINT32 stringCount); + +VOID FastStreamInjectionScenarioLogHelp(); + +#endif /// SCENARIOS_FAST_STREAM_INJECTION_H \ No newline at end of file diff --git a/network/trans/WFPSampler/exe/Scenarios_FlowAssociation.cpp b/network/trans/WFPSampler/exe/Scenarios_FlowAssociation.cpp new file mode 100644 index 000000000..341f071b2 --- /dev/null +++ b/network/trans/WFPSampler/exe/Scenarios_FlowAssociation.cpp @@ -0,0 +1,319 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// Scenarios_FlowAssociation.cpp +// +// Abstract: +// This module contains functions which prepares and sends data for the FLOW_ASSOCIATION +// scenario implementation. +// +// Naming Convention: +// +// +// +// i.e. +// +// +// { +// - Function is likely visible to other modules. +// Prv - Function is private to this module. +// } +// +// { +// FlowAssociationScenario - Function pertains to Flow Association Scenario. +// } +// +// { +// Execute - Function packages data and invokes RPC to the +// WFPSampler service +// Log - Function writes to the console. +// Parse - Function pulls data into the required format from the +// provided data. +// } +// +// { +// Help - Function provides context sensitive help for the +// scenario. +// } +// +// Private Functions: +// +// Public Functions: +// FlowAssociationScenarioExecute(), +// FlowAssociationScenarioLogHelp(), +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// December 13, 2013 - 1.1 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "Framework_WFPSampler.h" /// . + +/** + @private_function="PrvFlowAssociationScenarioParseFlowAsociationData" + + Purpose: Parse the command line parameters for implementing flow association such as:
+ Associate context with the scenario callout for the layer (-aws)
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 PrvFlowAssociationScenarioParseFlowAsociationData(_In_reads_(stringCount) PCWSTR* ppCLPStrings, + _In_ const UINT32 stringCount, + _Inout_ PC_FLOW_ASSOCIATION_DATA* pPCFlowAssociationData) +{ + ASSERT(ppCLPStrings); + ASSERT(stringCount); + ASSERT(pPCFlowAssociationData); + + UINT32 status = NO_ERROR; + HANDLE engineHandle = 0; + FWPM_CALLOUT* pCallout = 0; + GUID calloutKey = {0}; + + for(UINT32 stringIndex = 0; + stringIndex < stringCount; + stringIndex++) + { +#pragma warning(push) +#pragma warning(disable: 6385) /// careful validation of stringIndex (and advancement) against stringCount prevents read overrun + + if((stringIndex + 1) < stringCount) + { + /// Associate with callout + if(HlprStringsAreEqual(L"-aws", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[++stringIndex]; + + if(HlprStringsAreEqual(L"BASIC_PACKET_INJECTION", + pString)) + calloutKey = WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION; + else if(HlprStringsAreEqual(L"BASIC_PACKET_MODIFICATION", + pString)) + calloutKey = WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION; + else if(HlprStringsAreEqual(L"BASIC_STREAM_INJECTION", + pString)) + calloutKey = WFPSAMPLER_CALLOUT_BASIC_STREAM_INJECTION; + else if(HlprStringsAreEqual(L"PEND_ENDPOINT_CLOSURE", + pString)) + calloutKey = WFPSAMPLER_CALLOUT_PEND_ENDPOINT_CLOSURE; + + break; + } + } + +#pragma warning(pop) + + } + + if(HlprGUIDIsNull(&calloutKey)) + { + status = (UINT32)FWP_E_INCOMPATIBLE_LAYER; + + HlprLogError(L"PrvFlowAssociationScenarioParseFlowAsociationData() [status: %#x]", + status); + + HLPR_BAIL; + } + + status = HlprFwpmEngineOpen(&engineHandle); + HLPR_BAIL_ON_FAILURE(status); + + for(UINT32 stringIndex = 0; + stringIndex < stringCount; + stringIndex++) + { + if((stringIndex + 1) < stringCount) + { + /// Associate with layer + if(HlprStringsAreEqual(L"-awl", + ppCLPStrings[stringIndex])) + { + for(UINT32 index = 0; + index < PCFA_MAX_COUNT; + index++) + { + stringIndex++; + + if(stringIndex < stringCount) + { + PCWSTR pString = ppCLPStrings[stringIndex]; + UINT8 layerID = HlprFwpmLayerGetIDByString(pString); + + if(layerID == FWPS_BUILTIN_LAYER_MAX) + break; + else + { + GUID queryCalloutKey = calloutKey; + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + if(layerID == FWPS_LAYER_ALE_ENDPOINT_CLOSURE_V4 || + layerID == FWPS_LAYER_ALE_ENDPOINT_CLOSURE_V6) + queryCalloutKey = WFPSAMPLER_CALLOUT_PEND_ENDPOINT_CLOSURE; + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + queryCalloutKey.Data4[7] = layerID; + + status = FwpmCalloutGetByKey(engineHandle, + &queryCalloutKey, + &pCallout); + if(status != NO_ERROR) + { + if(status == FWP_E_CALLOUT_NOT_FOUND) + HlprLogError(L"Must add scenario filter and callout first"); + + HlprLogError(L"PrvFlowAssociationScenarioParseFlowAsociationData() [status: %#x]", + status); + + HLPR_BAIL; + } + + if(pCallout) + { + pPCFlowAssociationData->itemCount++; + pPCFlowAssociationData->pLayerIDs[index] = layerID; + pPCFlowAssociationData->pCalloutIDs[index] = pCallout->calloutId; + + FwpmFreeMemory((VOID**)&pCallout); + } + } + } + } + } + } + } + + if(pPCFlowAssociationData->itemCount == 0) + { + status = ERROR_INVALID_DATA; + + HlprLogError(L"PrvFlowAssociationScenarioParseFlowAsociationData() [status: %#x]", + status); + } + + HLPR_BAIL_LABEL: + + if(engineHandle) + HlprFwpmEngineClose(&engineHandle); + + return status; +} + +/** + @scenario_function="FlowAssociationScenarioExecute" + + Purpose: Gather and package data neccessary to setup the FLOW_ASSOCIATION scenario, + then invoke RPC to implement the scenario in the WFPSampler service.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 FlowAssociationScenarioExecute(_In_reads_(stringCount) PCWSTR* ppCLPStrings, + _In_ const UINT32 stringCount) +{ + ASSERT(ppCLPStrings); + ASSERT(stringCount); + + UINT32 status = NO_ERROR; + BOOLEAN removeScenario = FALSE; + FWPM_FILTER* pFilter = 0; + PC_FLOW_ASSOCIATION_DATA* pPCFlowAssociationData = 0; + + status = HlprFwpmFilterCreate(&pFilter); + HLPR_BAIL_ON_FAILURE(status); + + pFilter->displayData.name = L"WFPSampler's Flow Association Scenario Filter"; + + HlprCommandLineParseForScenarioRemoval(ppCLPStrings, + stringCount, + &removeScenario); + + status = HlprCommandLineParseForFilterInfo(ppCLPStrings, + stringCount, + pFilter, + removeScenario); + HLPR_BAIL_ON_FAILURE(status); + + if(!removeScenario) + { + HLPR_NEW(pPCFlowAssociationData, + PC_FLOW_ASSOCIATION_DATA); + HLPR_BAIL_ON_ALLOC_FAILURE(pPCFlowAssociationData, + status); + + status = PrvFlowAssociationScenarioParseFlowAsociationData(ppCLPStrings, + stringCount, + pPCFlowAssociationData); + HLPR_BAIL_ON_FAILURE(status); + } + + status = RPCInvokeScenarioFlowAssociation(wfpSamplerBindingHandle, + SCENARIO_FLOW_ASSOCIATION, + removeScenario ? FWPM_CHANGE_DELETE : FWPM_CHANGE_ADD, + pFilter, + pPCFlowAssociationData); + if(status != NO_ERROR) + HlprLogError(L"FlowAssociationScenarioExecute : RPCInvokeScenarioFlowAssociation() [status: %#x]", + status); + else + HlprLogInfo(L"FlowAssociationScenarioExecute : RPCInvokeScenarioFlowAssociation() [status: %#x]", + status); + + HLPR_BAIL_LABEL: + + if(pFilter) + HlprFwpmFilterDestroy(&pFilter); + + HLPR_DELETE(pPCFlowAssociationData); + + return status; +} + +/** + @public_function="FlowAssociationScenarioLogHelp" + + Purpose: Log usage information for the FLOW_ASSOCIATION scenario to the console.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +VOID FlowAssociationScenarioLogHelp() +{ + wprintf(L"\n\t\t -s \t FLOW_ASSOCIATION"); + wprintf(L"\n\t\t -? \t Receive usage information."); + wprintf(L"\n\t\t -l \t Specify the flow established layer from which the context will be associated. [Required]"); + wprintf(L"\n\t\t -sl \t Specify the sublayer to perform the filtering. [Optional]"); + wprintf(L"\n\t\t -r \t Remove the scenario objects."); + wprintf(L"\n\t\t -v \t Make the filter volatile (non-persistent). [Optional]"); + wprintf(L"\n\t\t -b \t Makes the objects available during boot time. [Optional]"); + wprintf(L"\n\t\t -ipla \t Specify the IP_LOCAL_ADDRESS /"); + wprintf(L"\n\t\t \t IP_SOURCE_ADDRESS to filter. [Optional]"); + wprintf(L"\n\t\t -ipra \t Specify the IP_REMOTE_ADDRESS /"); + wprintf(L"\n\t\t \t IP_DESTINATION_ADDRESS to filter. [Optional]"); + wprintf(L"\n\t\t -iplp \t Specify the IP_LOCAL_PORT to filter. [Optional]"); + wprintf(L"\n\t\t -iprp \t Specify the IP_REMOTE_PORT to filter. [Optional]"); + wprintf(L"\n\t\t -aws \t Specify the SCENARIO to associate the context with. [Required]"); + wprintf(L"\n\t\t -awl \t Specify the layers to associate context with. [Required]"); + wprintf(L"\n"); + wprintf(L"\n\t i.e."); + wprintf(L"\n\t\t WFPSampler.Exe -s FLOW_ASSOCIATION -l FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4 -ipla 1.0.0.1 -ipra 1.0.0.254 -iprp 80 -aws BASIC_STREAM_INJECTION -awl FWPM_LAYER_STREAM_V4 FWPM_LAYER_ALE_ENDPOINT_CLOSURE_V4 -v"); + wprintf(L"\n\t\t WFPSampler.Exe -s FLOW_ASSOCIATION -l FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4 -ipla 1.0.0.1 -ipra 1.0.0.254 -iprp 80 -aws BASIC_STREAM_INJECTION -awl FWPM_LAYER_STREAM_V4 FWPM_LAYER_ALE_ENDPOINT_CLOSURE_V4 -v -r"); + wprintf(L"\n"); + + return; +} diff --git a/network/trans/WFPSampler/exe/Scenarios_FlowAssociation.h b/network/trans/WFPSampler/exe/Scenarios_FlowAssociation.h new file mode 100644 index 000000000..1c108cc4d --- /dev/null +++ b/network/trans/WFPSampler/exe/Scenarios_FlowAssociation.h @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// Scenarios_FastStreamInjection.h +// +// Abstract: +// This module contains prototypes for functions which run the specified FLOW_ASSOCIATION +// scenario. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// December 13, 2013 - 1.1 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef SCENARIOS_FLOW_ASSOCIATION_H +#define SCENARIOS_FLOW_ASSOCIATION_H + +_Success_(return == NO_ERROR) +UINT32 FlowAssociationScenarioExecute(_In_reads_(stringCount) PCWSTR* ppCLPStrings, + _In_ const UINT32 stringCount); + +VOID FlowAssociationScenarioLogHelp(); + +#endif /// SCENARIOS_FLOW_ASSOCIATION_H diff --git a/network/trans/WFPSampler/exe/Scenarios_Include.h b/network/trans/WFPSampler/exe/Scenarios_Include.h new file mode 100644 index 000000000..9946207d4 --- /dev/null +++ b/network/trans/WFPSampler/exe/Scenarios_Include.h @@ -0,0 +1,46 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// Scenarios_Include.h +// +// Abstract: +// This module contains include headers for central exportation of the Scenarios_* modules. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Add ADVANCED_PACKET_INJECTION, FLOW_ASSOCIATION, and +// PEND_ENDPOINT_CLOSURE scenarios +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef SCENARIOS_INCLUDE_H +#define SCENARIOS_INCLUDE_H + +#include "Scenarios_AdvancedPacketInjection.h" /// . + +#if(NTDDI_VERSION >= NTDDI_WIN8) + +#include "Scenarios_AppContainers.h" /// . + +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) + +#include "Scenarios_BasicAction.h" /// . +#include "Scenarios_BasicPacketExamination.h" /// . +#include "Scenarios_BasicPacketInjection.h" /// . +#include "Scenarios_BasicPacketModification.h" /// . +#include "Scenarios_BasicStreamInjection.h" /// . +#include "Scenarios_FastPacketInjection.h" /// . +#include "Scenarios_FastStreamInjection.h" /// . +#include "Scenarios_FlowAssociation.h" /// . +#include "Scenarios_PendAuthorization.h" /// . +#include "Scenarios_PendEndpointClosure.h" /// . +#include "Scenarios_Proxy.h" /// . + +#endif /// SCENARIOS_INCLUDE_H \ No newline at end of file diff --git a/network/trans/WFPSampler/exe/Scenarios_PendAuthorization.cpp b/network/trans/WFPSampler/exe/Scenarios_PendAuthorization.cpp new file mode 100644 index 000000000..055c6acb7 --- /dev/null +++ b/network/trans/WFPSampler/exe/Scenarios_PendAuthorization.cpp @@ -0,0 +1,280 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// Scenarios_PendAuthorization.cpp +// +// Abstract: +// This module contains functions which prepares and sends data for the PEND_AUTHORIZATION +// scenario implementation. +// +// Naming Convention: +// +// +// +// i.e. +// +// +// { +// - Function is likely visible to other modules. +// Prv - Function is private to this module. +// } +// +// { +// PendAuthorizationScenario - Function pertains to all of the Pend Authorization +// Scenarios. +// } +// +// { +// Execute - Function packages data and invokes RPC to the WFPSampler +// service. +// Log - Function writes to the console. +// Parse - Function pulls data into the required format from the +// provided data. +// } +// +// { +// AuthorizationData - Function acts on the PC_PEND_AUTHORIZATION_DATA. +// Help - Function provides usage information. +// } +// +// Private Functions: +// PrvPendAuthorizationScenarioParseAuthorizationData(), +// +// Public Functions: +// PendAuthorizationScenarioExecute(), +// PendAuthorizationScenarioLogHelp(), +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Add support for specifying a different sublayer, change +// parameter, and limit scenario to only supported +// layers +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "Framework_WFPSampler.h" /// . + +/** + @private_function="PrvPendAuthorizationScenarioParseAuthorizationData" + + Purpose: Parse the command line parameters for pending authorization data such as:
+ delay before callout returns (-d DELAY_IN_MS)
+ final action - BLOCK (-fab)
+ final action - PERMIT (-fap)
+
+ Notes: If no final action is specified, the action will default to BLOCK.
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 PrvPendAuthorizationScenarioParseAuthorizationData(_In_reads_(stringCount) PCWSTR* ppCLPStrings, + _In_ const UINT32 stringCount, + _Inout_ PC_PEND_AUTHORIZATION_DATA* pPCPendAuthorizationData) +{ + ASSERT(ppCLPStrings); + ASSERT(stringCount); + ASSERT(pPCPendAuthorizationData); + + UINT32 status = NO_ERROR; + const UINT32 MAX_PARAMETERS = 5; + UINT32 found = 0; + + for(UINT32 stringIndex = 0; + stringIndex < stringCount && + found != MAX_PARAMETERS; + stringIndex++) + { + /// Final Action BLOCK + if(HlprStringsAreEqual(L"-fab", + ppCLPStrings[stringIndex])) + { + pPCPendAuthorizationData->finalAction = FWP_ACTION_BLOCK; + + found++; + + continue; + } + + /// Final Action PERMIT + if(HlprStringsAreEqual(L"-fap", + ppCLPStrings[stringIndex])) + { + pPCPendAuthorizationData->finalAction = FWP_ACTION_PERMIT; + + found++; + + continue; + } + + if((stringIndex + 1) < stringCount) + { + /// Delay (in MS) + if(HlprStringsAreEqual(L"-pcd", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[stringIndex + 1]; + + if(iswdigit((wint_t)pString[0])) + { + UINT32 delay = wcstol(pString, + 0, + 0); + + pPCPendAuthorizationData->delay = delay; + + found++; + } + + continue; + } + } + + /// Threaded DPC + if(HlprStringsAreEqual(L"-tdpc", + ppCLPStrings[stringIndex])) + { + pPCPendAuthorizationData->useThreadedDPC = TRUE; + + found++; + + continue; + } + + /// Work Items + if(HlprStringsAreEqual(L"-wi", + ppCLPStrings[stringIndex])) + { + pPCPendAuthorizationData->useWorkItems = TRUE; + + found++; + + continue; + } + } + + /// Default Final Action BLOCK + if(pPCPendAuthorizationData->finalAction == 0) + pPCPendAuthorizationData->finalAction = FWP_ACTION_BLOCK; + + return status; +} + +/** + @scenario_function="PendAuthorizationScenarioExecute" + + Purpose: Gather and package data neccessary to setup the PEND_AUTHORIZATION scenario, then + invoke RPC to implement the scenario in the WFPSampler service.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 PendAuthorizationScenarioExecute(_In_reads_(stringCount) PCWSTR* ppCLPStrings, + _In_ UINT32 stringCount) +{ + ASSERT(ppCLPStrings); + ASSERT(stringCount); + + UINT32 status = NO_ERROR; + BOOLEAN removeScenario = FALSE; + PC_PEND_AUTHORIZATION_DATA* pPCPendAuthorizationData = 0; + FWPM_FILTER* pFilter = 0; + + status = HlprFwpmFilterCreate(&pFilter); + HLPR_BAIL_ON_FAILURE(status); + + pFilter->displayData.name = L"WFPSampler's Pend Authorization Scenario Filter"; + + HlprCommandLineParseForScenarioRemoval(ppCLPStrings, + stringCount, + &removeScenario); + + status = HlprCommandLineParseForFilterInfo(ppCLPStrings, + stringCount, + pFilter, + removeScenario); + HLPR_BAIL_ON_FAILURE(status); + + if(!removeScenario) + { + HLPR_NEW(pPCPendAuthorizationData, + PC_PEND_AUTHORIZATION_DATA); + HLPR_BAIL_ON_ALLOC_FAILURE(pPCPendAuthorizationData, + status); + + status = PrvPendAuthorizationScenarioParseAuthorizationData(ppCLPStrings, + stringCount, + pPCPendAuthorizationData); + HLPR_BAIL_ON_FAILURE(status); + } + + status = RPCInvokeScenarioPendAuthorization(wfpSamplerBindingHandle, + SCENARIO_PEND_AUTHORIZATION, + removeScenario ? FWPM_CHANGE_DELETE : FWPM_CHANGE_ADD, + pFilter, + pPCPendAuthorizationData); + if(status != NO_ERROR) + HlprLogError(L"PendAuthorizationScenarioExecute : RpcInvokeScenarioPendAuthorization() [status: %#x]", + status); + else + HlprLogInfo(L"PendAuthorizationScenarioExecute : RpcInvokeScenarioPendAuthorization() [status: %#x]", + status); + + HLPR_BAIL_LABEL: + + if(pFilter) + HlprFwpmFilterDestroy(&pFilter); + + HLPR_DELETE(pPCPendAuthorizationData); + + return status; +} + +/** + @public_function="PendAuthorizationScenarioLogHelp" + + Purpose: Log usage information for the PEND_AUTHORIZATION scenario to the console.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +VOID PendAuthorizationScenarioLogHelp() +{ + wprintf(L"\n\t\t -s \t PEND_AUTHORIZATION"); + wprintf(L"\n\t\t -? \t Receive usage information."); + wprintf(L"\n\t\t -l \t Specify the layer to perform the filtering. [Required]"); + wprintf(L"\n\t\t -sl \t Specify the sublayer to perform the filtering. [Optional]"); + wprintf(L"\n\t\t -r \t Remove the scenario objects."); + wprintf(L"\n\t\t -v \t Make the filter volatile (non-persistent). [Optional]"); + wprintf(L"\n\t\t -b \t Makes the objects available during boot time. [Optional]"); + wprintf(L"\n\t\t -tdpc \t Use threaded DPCs for out of band. [Optional]"); + wprintf(L"\n\t\t -wi \t Use work items for out of band. [Optional]"); + wprintf(L"\n\t\t -pcd \t Introduce a delay (in ms) before the pend is completed. [Optional]"); + wprintf(L"\n\t\t -fab \t Specify the final action to be block. [Optional][Default]"); + wprintf(L"\n\t\t -fap \t Specify the final action to be permit. [Optional]"); + wprintf(L"\n\t\t -ipla \t Specify the IP_LOCAL_ADDRESS /"); + wprintf(L"\n\t\t \t IP_SOURCE_ADDRESS to filter. [Optional]"); + wprintf(L"\n\t\t -ipra \t Specify the IP_REMOTE_ADDRESS /"); + wprintf(L"\n\t\t \t IP_DESTINATION_ADDRESS to filter. [Optional]"); + wprintf(L"\n\t\t -ipp \t Specify the IP_PROTOCOL to filter. [Optional]"); + wprintf(L"\n\t\t -iplp \t Specify the IP_LOCAL_PORT to filter. [Optional]"); + wprintf(L"\n\t\t -icmpt \t Specify the ICMP_TYPE to filter. [Optional]"); + wprintf(L"\n\t\t -iprp \t Specify the IP_REMOTE_PORT to filter. [Optional]"); + wprintf(L"\n\t\t -icmpc \t Specify the ICMP_CODE to filter. [Optional]"); + wprintf(L"\n"); + wprintf(L"\n\t i.e."); + wprintf(L"\n\t\t WFPSampler.Exe -s PEND_AUTHORIZATION -l FWPM_LAYER_ALE_AUTH_CONNECT_V4 -ipla 1.0.0.1 -ipra 1.0.0.254 -ipp TCP -d 10000 -fap -v"); + wprintf(L"\n\t\t WFPSampler.Exe -s PEND_AUTHORIZATION -l FWPM_LAYER_ALE_AUTH_CONNECT_V4 -ipla 1.0.0.1 -ipra 1.0.0.254 -ipp TCP -d 10000 -fap -v -r"); + wprintf(L"\n"); + + return; +} diff --git a/network/trans/WFPSampler/exe/Scenarios_PendAuthorization.h b/network/trans/WFPSampler/exe/Scenarios_PendAuthorization.h new file mode 100644 index 000000000..67b10f887 --- /dev/null +++ b/network/trans/WFPSampler/exe/Scenarios_PendAuthorization.h @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// Scenarios_PendAuthorization.h +// +// Abstract: +// This module contains prototypes for functions which run the specified PEND_AUTHORIZATION +// scenario. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef SCENARIOS_PEND_AUTHORIZATION_H +#define SCENARIOS_PEND_AUTHORIZATION_H + +_Success_(return == NO_ERROR) +UINT32 PendAuthorizationScenarioExecute(_In_reads_(stringCount) PCWSTR* ppCLPStrings, + _In_ UINT32 stringCount); + +VOID PendAuthorizationScenarioLogHelp(); + +#endif /// SCENARIOS_PEND_AUTHORIZATION_H \ No newline at end of file diff --git a/network/trans/WFPSampler/exe/Scenarios_PendEndpointClosure.cpp b/network/trans/WFPSampler/exe/Scenarios_PendEndpointClosure.cpp new file mode 100644 index 000000000..98a1375c3 --- /dev/null +++ b/network/trans/WFPSampler/exe/Scenarios_PendEndpointClosure.cpp @@ -0,0 +1,265 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// Scenarios_PendEndpointClosure.cpp +// +// Abstract: +// This module contains functions which prepares and sends data for the PEND_ENDPOINT_CLOSURE +// scenario implementation. +// +// Naming Convention: +// +// +// +// i.e. +// +// +// { +// - Function is likely visible to other modules. +// Prv - Function is private to this module. +// } +// +// { +// PendEndpointClosureScenario - Function pertains to all of the Pend Endpoint Closure +// Scenarios. +// } +// +// { +// Execute - Function packages data and invokes RPC to the WFPSampler +// service. +// Log - Function writes to the console. +// Parse - Function pulls data into the required format from the +// provided data. +// } +// +// { +// ClosureData - Function acts on the PC_ENDPOINT_CLOSURE_DATA. +// Help - Function provides usage information. +// } +// +// Private Functions: +// PrvPendEndpointClosureScenarioParseClosureData(), +// +// Public Functions: +// PendEndpointClosureScenarioExecute(), +// PendEndpointClosureScenarioLogHelp(), +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// December 13, 2013 - 1.1 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "Framework_WFPSampler.h" /// . + +#if(NTDDI_VERSION >= NTDDI_WIN7) + +/** + @private_function="PrvPendEndpointClosureScenarioParseClosureData" + + Purpose: Parse the command line parameters for closure data such as:
+ delay before callout returns (-p DELAY_IN_MS)
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 PrvPendEndpointClosureScenarioParseClosureData(_In_reads_(stringCount) PCWSTR* ppCLPStrings, + _In_ const UINT32 stringCount, + _Inout_ PC_PEND_ENDPOINT_CLOSURE_DATA* pPCPendEndpointClosureData) +{ + ASSERT(ppCLPStrings); + ASSERT(stringCount); + ASSERT(pPCPendEndpointClosureData); + + UINT32 status = NO_ERROR; + const UINT32 MAX_PARAMETERS = 5; + UINT32 found = 0; + + for(UINT32 stringIndex = 0; + stringIndex < stringCount && + found != MAX_PARAMETERS; + stringIndex++) + { + if((stringIndex + 1) < stringCount) + { + /// Delay (in MS) + if(HlprStringsAreEqual(L"-pcd", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[stringIndex + 1]; + + if(iswdigit((wint_t)pString[0])) + { + UINT32 delay = wcstol(pString, + 0, + 0); + + pPCPendEndpointClosureData->delay = delay; + + found++; + } + + continue; + } + } + + /// Threaded DPC + if(HlprStringsAreEqual(L"-tdpc", + ppCLPStrings[stringIndex])) + { + pPCPendEndpointClosureData->useThreadedDPC = TRUE; + + found++; + + continue; + } + + /// Work Items + if(HlprStringsAreEqual(L"-wi", + ppCLPStrings[stringIndex])) + { + pPCPendEndpointClosureData->useWorkItems = TRUE; + + found++; + + continue; + } + } + + return status; +} + +/** + @scenario_function="PendEndpointClosureScenarioExecute" + + Purpose: Gather and package data neccessary to setup the PEND_ENDPOINT_CLOSURE scenario, + then invoke RPC to implement the scenario in the WFPSampler service.
+
+ Notes: This scenario is only supported on Win7+.
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 PendEndpointClosureScenarioExecute(_In_reads_(stringCount) PCWSTR* ppCLPStrings, + _In_ UINT32 stringCount) +{ + ASSERT(ppCLPStrings); + ASSERT(stringCount); + + UINT32 status = NO_ERROR; + BOOLEAN removeScenario = FALSE; + PC_PEND_ENDPOINT_CLOSURE_DATA* pPCPendEndpointClosureData = 0; + FWPM_FILTER* pFilter = 0; + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + status = HlprFwpmFilterCreate(&pFilter); + HLPR_BAIL_ON_FAILURE(status); + + pFilter->displayData.name = L"WFPSampler's Pend Endpoint Closure Scenario Filter"; + + HlprCommandLineParseForScenarioRemoval(ppCLPStrings, + stringCount, + &removeScenario); + + status = HlprCommandLineParseForFilterInfo(ppCLPStrings, + stringCount, + pFilter, + removeScenario); + HLPR_BAIL_ON_FAILURE(status); + + if(!removeScenario) + { + HLPR_NEW(pPCPendEndpointClosureData, + PC_PEND_ENDPOINT_CLOSURE_DATA); + HLPR_BAIL_ON_ALLOC_FAILURE(pPCPendEndpointClosureData, + status); + + status = PrvPendEndpointClosureScenarioParseClosureData(ppCLPStrings, + stringCount, + pPCPendEndpointClosureData); + HLPR_BAIL_ON_FAILURE(status); + } + + status = RPCInvokeScenarioPendEndpointClosure(wfpSamplerBindingHandle, + SCENARIO_PEND_ENDPOINT_CLOSURE, + removeScenario ? FWPM_CHANGE_DELETE : FWPM_CHANGE_ADD, + pFilter, + pPCPendEndpointClosureData); + if(status != NO_ERROR) + HlprLogError(L"PendEndpointClosureScenarioExecute : RpcInvokeScenarioPendEndpointClosure() [status: %#x]", + status); + else + HlprLogInfo(L"PendEndpointClosureScenarioExecute : RpcInvokeScenarioPendEndpointClosure() [status: %#x]", + status); + + HLPR_BAIL_LABEL: + + if(pFilter) + HlprFwpmFilterDestroy(&pFilter); + + HLPR_DELETE(pPCPendEndpointClosureData); + +#else + + UNREFERENCED_PARAMETER(ppCLPStrings); + UNREFERENCED_PARAMETER(stringCount); + + status = ERROR_NOT_SUPPORTED; + + HlprLogError(L"PendEndpointClosureScenarioExecute() [status: %#x]", + status); + +#endif // (NTDDI_VERSION >= NTDDI_WIN7) + + return status; +} + +/** + @public_function="PendEndpointClosureScenarioLogHelp" + + Purpose: Log usage information for the PEND_ENDPOINT_CLOSURE scenario to the console.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +VOID PendEndpointClosureScenarioLogHelp() +{ + wprintf(L"\n\t\t -s \t PEND_ENDPOINT_CLOSURE"); + wprintf(L"\n\t\t -? \t Receive usage information."); + wprintf(L"\n\t\t -l \t Specify the layer to perform the filtering. [Required]"); + wprintf(L"\n\t\t -sl \t Specify the sublayer to perform the filtering. [Optional]"); + wprintf(L"\n\t\t -r \t Remove the scenario objects."); + wprintf(L"\n\t\t -v \t Make the filter volatile (non-persistent). [Optional]"); + wprintf(L"\n\t\t -b \t Makes the objects available during boot time. [Optional]"); + wprintf(L"\n\t\t -tdpc \t Use threaded DPCs for out of band. [Optional]"); + wprintf(L"\n\t\t -wi \t Use work items for out of band. [Optional]"); + wprintf(L"\n\t\t -pcd \t Introduce a delay (in ms) before the pend is completed. [Optional]"); + wprintf(L"\n\t\t -ipla \t Specify the IP_LOCAL_ADDRESS /"); + wprintf(L"\n\t\t \t IP_SOURCE_ADDRESS to filter. [Optional]"); + wprintf(L"\n\t\t -ipra \t Specify the IP_REMOTE_ADDRESS /"); + wprintf(L"\n\t\t \t IP_DESTINATION_ADDRESS to filter. [Optional]"); + wprintf(L"\n\t\t -ipp \t Specify the IP_PROTOCOL to filter. [Optional]"); + wprintf(L"\n\t\t -iplp \t Specify the IP_LOCAL_PORT to filter. [Optional]"); + wprintf(L"\n\t\t -icmpt \t Specify the ICMP_TYPE to filter. [Optional]"); + wprintf(L"\n\t\t -iprp \t Specify the IP_REMOTE_PORT to filter. [Optional]"); + wprintf(L"\n\t\t -icmpc \t Specify the ICMP_CODE to filter. [Optional]"); + wprintf(L"\n"); + wprintf(L"\n\t i.e."); + wprintf(L"\n\t\t WFPSampler.Exe -s PEND_ENDPOINT_CLOSURE -l FWPM_LAYER_ALE_ENDPOINT_CLOSURE_V4 -ipla 1.0.0.1 -ipra 1.0.0.254 -ipp TCP -d 10000 -v"); + wprintf(L"\n\t\t WFPSampler.Exe -s PEND_ENDPOINT_CLOSURE -l FWPM_LAYER_ALE_ENDPOINT_CLOSURE_V4 -ipla 1.0.0.1 -ipra 1.0.0.254 -ipp TCP -d 10000 -v -r"); + wprintf(L"\n"); + + return; +} + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) diff --git a/network/trans/WFPSampler/exe/Scenarios_PendEndpointClosure.h b/network/trans/WFPSampler/exe/Scenarios_PendEndpointClosure.h new file mode 100644 index 000000000..5fd330959 --- /dev/null +++ b/network/trans/WFPSampler/exe/Scenarios_PendEndpointClosure.h @@ -0,0 +1,35 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// Scenarios_PendEndpointClosure.h +// +// Abstract: +// This module contains prototypes for functions which run the specified PEND_ENDPOINT_CLOSURE +// scenario. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// December 13, 2013 - 1.1 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef SCENARIOS_PEND_ENDPOINT_CLOSURE_H +#define SCENARIOS_PEND_ENDPOINT_CLOSURE_H + +#if(NTDDI_VERSION >= NTDDI_WIN7) + +_Success_(return == NO_ERROR) +UINT32 PendEndpointClosureScenarioExecute(_In_reads_(stringCount) PCWSTR* ppCLPStrings, + _In_ UINT32 stringCount); + +VOID PendEndpointClosureScenarioLogHelp(); + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + +#endif /// SCENARIOS_PEND_ENDPOINT_CLOSURE_H diff --git a/network/trans/WFPSampler/exe/Scenarios_Proxy.cpp b/network/trans/WFPSampler/exe/Scenarios_Proxy.cpp new file mode 100644 index 000000000..efd0ab8b3 --- /dev/null +++ b/network/trans/WFPSampler/exe/Scenarios_Proxy.cpp @@ -0,0 +1,663 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// Scenarios_Proxy.cpp +// +// Abstract: +// This module contains functions which prepares and sends data for the PROXY scenario +// implementation. +// +// Naming Convention: +// +// +// +// i.e. +// +// +// { +// - Function is likely visible to other modules. +// Prv - Function is private to this module. +// } +// +// { +// ProxyScenario - Function pertains to all of the Proxy Scenarios. +// } +// +// { +// Execute - Function packages data and invokes RPC to the WFPSampler service. +// Log - Function writes to the console. +// Parse - Function pulls data into the required format from the provided data. +// } +// +// { +// ProxyData - Function acts on the PC_PROXY_DATA. +// Help - Function provides usage information. +// } +// +// Private Functions: +// PrvProxyScenarioParseProxyData(), +// +// Public Functions: +// ProxyScenarioExecute(), +// ProxyScenarioLogHelp(), +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Add support for specifying a different sublayer, and +// set the original traffic data in the providerContext +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "Framework_WFPSampler.h" /// . + +/// WFPSamplerProxyService.Exe's ports +#define PROXY_PORT_V4 17476 +#define PROXY_PORT_V6 26214 + +/** + @private_function="PrvProxyScenarioParseProxyData" + + Purpose: Parse the command line parameters for proxying data such as:
+ Proxy to local address (-pla IP_ADDRESS)
+ Proxy to local port (-plp PORT)
+ Proxy to remote address (-pra IP_ADDRESS)
+ Proxy to remote port (-prp PORT)
+ Proxy to local service (-plspid PID)
+ Perform the injection inline (from within the classify) (-in)
+ Use threaded DPCs for out of band (asynchronous) (-tdpc)
+ Use work items for out of band (asynchronous) (-wi)
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 PrvProxyScenarioParseProxyData(_In_reads_(stringCount) PCWSTR* ppCLPStrings, + _In_ const UINT32 stringCount, + _Inout_ PC_PROXY_DATA* pPCProxyData) +{ + ASSERT(ppCLPStrings); + ASSERT(stringCount); + ASSERT(pPCProxyData); + + UINT32 status = NO_ERROR; + const UINT32 MAX_PARAMETERS = 9; + UINT32 found = 0; + + for(UINT32 stringIndex = 0; + stringIndex < stringCount && + found != MAX_PARAMETERS; + stringIndex++) + { + if((stringIndex + 1) < stringCount) + { + /// Original local address + if(HlprStringsAreEqual(L"-ipla", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[stringIndex + 1]; + + if(HlprIPAddressV6StringIsValidFormat(pString)) + { + status = HlprIPAddressV6StringToValue(pString, + pPCProxyData->originalLocalAddress.pIPv6); + HLPR_BAIL_ON_FAILURE(status); + } + else + { + UINT32 ipv4Address = 0; + + status = HlprIPAddressV4StringToValue(pString, + &ipv4Address); + HLPR_BAIL_ON_FAILURE(status); + + ipv4Address = htonl(ipv4Address); + + RtlCopyMemory(pPCProxyData->originalLocalAddress.pIPv4, + &ipv4Address, + sizeof(UINT32)); + } + + continue; + } + + /// Original remote address + if(HlprStringsAreEqual(L"-ipra", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[stringIndex + 1]; + + if(HlprIPAddressV6StringIsValidFormat(pString)) + { + status = HlprIPAddressV6StringToValue(pString, + pPCProxyData->originalRemoteAddress.pIPv6); + HLPR_BAIL_ON_FAILURE(status); + } + else + { + UINT32 ipv4Address = 0; + + status = HlprIPAddressV4StringToValue(pString, + &ipv4Address); + + ipv4Address = htonl(ipv4Address); + + RtlCopyMemory(pPCProxyData->originalRemoteAddress.pIPv4, + &ipv4Address, + sizeof(UINT32)); + } + + continue; + } + + /// Original protocol + if(HlprStringsAreEqual(L"-ipp", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[stringIndex + 1]; + + if(iswdigit((wint_t)pString[0])) + { + UINT32 protocol = wcstol(pString, + 0, + 0); + + if(protocol <= 0xFF) + pPCProxyData->ipProtocol = (UINT8)(protocol & 0xFF); + } + else + { + UINT32 protocol = IPPROTO_MAX; + + if(HlprStringsAreEqual(L"TCP", + pString)) + protocol = IPPROTO_TCP; + else if(HlprStringsAreEqual(L"UDP", + pString)) + protocol = IPPROTO_UDP; + else if(HlprStringsAreEqual(L"ICMPV4", + pString)) + protocol = IPPROTO_ICMP; + else if(HlprStringsAreEqual(L"ICMPV6", + pString)) + protocol = IPPROTO_ICMPV6; + + pPCProxyData->ipProtocol = (UINT8)(protocol & 0xFF); + } + + continue; + } + + /// Original local port + if(HlprStringsAreEqual(L"-iplp", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[stringIndex + 1]; + + if(iswdigit((wint_t)pString[0])) + { + UINT32 port = wcstol(pString, + 0, + 0); + + if(port <= 0xFFFF) + { + pPCProxyData->originalLocalPort = (UINT16)(port & 0xFFFF); + pPCProxyData->originalLocalPort = htons(pPCProxyData->originalLocalPort); + } + } + + continue; + } + + /// Original remote port + if(HlprStringsAreEqual(L"-iprp", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[stringIndex + 1]; + + if(iswdigit((wint_t)pString[0])) + { + UINT32 port = wcstol(pString, + 0, + 0); + + if(port <= 0xFFFF) + { + pPCProxyData->originalRemotePort = (UINT16)(port & 0xFFFF); + pPCProxyData->originalRemotePort = htons(pPCProxyData->originalRemotePort); + } + } + + continue; + } + + /// Proxy to Local Address + if(HlprStringsAreEqual(L"-pla", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[stringIndex + 1]; + + if(pPCProxyData->ipVersion == IPV6 && + HlprIPAddressV6StringIsValidFormat(pString)) + { + status = HlprIPAddressV6StringToValue(pString, + (BYTE*)pPCProxyData->proxyLocalAddress.pIPv6); + HLPR_BAIL_ON_FAILURE(status); + } + else if(pPCProxyData->ipVersion == IPV4) + { + UINT32 localAddress = 0; + + status = HlprIPAddressV4StringToValue(pString, + &localAddress); + HLPR_BAIL_ON_FAILURE(status); + + localAddress = htonl(localAddress); + + RtlCopyMemory(pPCProxyData->proxyLocalAddress.pIPv4, + &localAddress, + sizeof(UINT32)); + } + else + { + status = ERROR_INVALID_DATA; + + HlprLogError(L"PrvProxyScenarioParseRandomizedData() [status: %#x][ipVersion: %d][local ipAddress: %s]", + status, + pPCProxyData->ipVersion, + pString); + + HLPR_BAIL; + } + + pPCProxyData->flags |= PCPDF_PROXY_LOCAL_ADDRESS; + + found++; + + continue; + } + + /// Proxy to Local Port + if(HlprStringsAreEqual(L"-plp", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[stringIndex + 1]; + + if(iswdigit((wint_t)pString[0])) + { + UINT32 localPort = wcstol(pString, + 0, + 0); + if(localPort <= 0xFFFF) + { + UINT16 port = localPort & 0xFFFF; + + pPCProxyData->proxyLocalPort = htons(port); + } + + pPCProxyData->flags |= PCPDF_PROXY_LOCAL_PORT; + + found++; + } + + continue; + } + + /// Proxy to Remote Address + if(HlprStringsAreEqual(L"-pra", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[stringIndex + 1]; + + if(pPCProxyData->ipVersion == IPV6 && + HlprIPAddressV6StringIsValidFormat(pString)) + { + status = HlprIPAddressV6StringToValue(pString, + (BYTE*)pPCProxyData->proxyRemoteAddress.pIPv6); + HLPR_BAIL_ON_FAILURE(status); + } + else if(pPCProxyData->ipVersion == IPV4) + { + UINT32 remoteAddress = 0; + + status = HlprIPAddressV4StringToValue(pString, + &remoteAddress); + HLPR_BAIL_ON_FAILURE(status); + + remoteAddress = htonl(remoteAddress); + + RtlCopyMemory(pPCProxyData->proxyRemoteAddress.pIPv4, + &remoteAddress, + sizeof(UINT32)); + } + else + { + status = ERROR_INVALID_DATA; + + HlprLogError(L"PrvProxyScenarioParseRandomizedData() [status: %#x][ipVersion: %d][Remote ipAddress: %s]", + status, + pPCProxyData->ipVersion, + pString); + + HLPR_BAIL; + } + + pPCProxyData->flags |= PCPDF_PROXY_REMOTE_ADDRESS; + + found++; + + continue; + } + + /// Proxy to Remote Port + if(HlprStringsAreEqual(L"-prp", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[stringIndex + 1]; + + if(iswdigit((wint_t)pString[0])) + { + UINT32 remotePort = wcstol(pString, + 0, + 0); + if(remotePort <= 0xFFFF) + { + UINT16 port = remotePort & 0xFFFF; + + pPCProxyData->proxyRemotePort = htons(port); + } + + pPCProxyData->flags |= PCPDF_PROXY_REMOTE_PORT; + + found++; + } + + continue; + } + + /// Proxy to local service (process ID) + if(HlprStringsAreEqual(L"-plspid", + ppCLPStrings[stringIndex])) + { + PCWSTR pString = ppCLPStrings[stringIndex + 1]; + + if(iswdigit((wint_t)pString[0])) + { + UINT32 pid = wcstol(pString, + 0, + 0); + if(pid) + pPCProxyData->targetProcessID = pid; + + found++; + } + + continue; + } + } + + /// Proxy Inline + if(HlprStringsAreEqual(L"-in", + ppCLPStrings[stringIndex])) + { + pPCProxyData->performInline = TRUE; + + found++; + + continue; + } + + /// Proxy to a remote service + if(HlprStringsAreEqual(L"-prs", + ppCLPStrings[stringIndex])) + { + pPCProxyData->proxyToRemoteService = TRUE; + + found++; + + continue; + } + + /// Threaded DPC + if(HlprStringsAreEqual(L"-tdpc", + ppCLPStrings[stringIndex])) + { + pPCProxyData->useThreadedDPC = TRUE; + + found++; + + continue; + } + + /// Work Items + if(HlprStringsAreEqual(L"-wi", + ppCLPStrings[stringIndex])) + { + pPCProxyData->useWorkItems = TRUE; + + found++; + + continue; + } + } + + if(!(pPCProxyData->proxyToRemoteService)) + { +/* + INET_PORT_RANGE portRange = {0}; + + portRange.StartPort = pPCProxyData->ipVersion == IPV6 ? PROXY_PORT_V6 : PROXY_PORT_V4; + portRange.NumberOfPorts = 1; + + HlprWinSockQueryPortReservation(IPPROTO_TCP, + &portRange, + &pPCProxyData->tcpPortReservationToken); + + HlprWinSockQueryPortReservation(IPPROTO_UDP, + &portRange, + &pPCProxyData->udpPortReservationToken); +*/ + if(pPCProxyData->targetProcessID == 0) + HlprProcessGetID(L"WFPSamplerProxyService.Exe", + &(pPCProxyData->targetProcessID)); + } + + HLPR_BAIL_LABEL: + + return status; +} + +/** + @scenario_function="ProxyScenarioExecute" + + Purpose: Gather and package data neccessary to setup the PROXY scenario, then invoke RPC to + implement the scenario in the WFPSampler service.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 ProxyScenarioExecute(_In_reads_(stringCount) PCWSTR* ppCLPStrings, + _In_ UINT32 stringCount) +{ + ASSERT(ppCLPStrings); + ASSERT(stringCount); + + UINT32 status = NO_ERROR; + BOOLEAN removeScenario = FALSE; + PC_PROXY_DATA* pPCProxyData = 0; + FWPM_FILTER* pFilter = 0; + + status = HlprFwpmFilterCreate(&pFilter); + HLPR_BAIL_ON_FAILURE(status); + + pFilter->displayData.name = L"WFPSampler's Proxy Filter"; + + HlprCommandLineParseForScenarioRemoval(ppCLPStrings, + stringCount, + &removeScenario); + + status = HlprCommandLineParseForFilterInfo(ppCLPStrings, + stringCount, + pFilter, + removeScenario); + HLPR_BAIL_ON_FAILURE(status); + + if(pFilter->layerKey != FWPM_LAYER_OUTBOUND_TRANSPORT_V4 && + pFilter->layerKey != FWPM_LAYER_OUTBOUND_TRANSPORT_V6 + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + && + pFilter->layerKey != FWPM_LAYER_ALE_BIND_REDIRECT_V4 && + pFilter->layerKey != FWPM_LAYER_ALE_BIND_REDIRECT_V6 && + pFilter->layerKey != FWPM_LAYER_ALE_CONNECT_REDIRECT_V4 && + pFilter->layerKey != FWPM_LAYER_ALE_CONNECT_REDIRECT_V6 + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + ) + { + status = (UINT32)FWP_E_INCOMPATIBLE_LAYER; + + HLPR_BAIL; + } + + HLPR_NEW(pPCProxyData, + PC_PROXY_DATA); + HLPR_BAIL_ON_ALLOC_FAILURE(pPCProxyData, + status); + + if(HlprFwpmLayerIsIPv4(&(pFilter->layerKey))) + pPCProxyData->ipVersion = IPV4; + else + pPCProxyData->ipVersion = IPV6; + + status = PrvProxyScenarioParseProxyData(ppCLPStrings, + stringCount, + pPCProxyData); + HLPR_BAIL_ON_FAILURE(status); + + /// Proxying at this layer means we need to reverse the proxying on the inbound traffic. + /// This will be done at INBOUND_IPPACKET so as to support as many scenarios as possible (TCP, UDP, connected UDP etc.) + /// This will not work for IPsec protected traffic. + if(pFilter->layerKey == FWPM_LAYER_OUTBOUND_TRANSPORT_V4 && + pFilter->layerKey == FWPM_LAYER_OUTBOUND_TRANSPORT_V6) + { + BYTE pEmptyAddress[IPV6_ADDRESS_LENGTH] = {0}; + + if((pPCProxyData->flags & PCPDF_PROXY_LOCAL_ADDRESS && + RtlCompareMemory(pPCProxyData->originalLocalAddress.pIPv6, + pEmptyAddress, + IPV6_ADDRESS_LENGTH) == IPV6_ADDRESS_LENGTH) || + (pPCProxyData->flags & PCPDF_PROXY_REMOTE_ADDRESS && + RtlCompareMemory(pPCProxyData->originalLocalAddress.pIPv6, + pEmptyAddress, + IPV6_ADDRESS_LENGTH) == IPV6_ADDRESS_LENGTH) || + (pPCProxyData->flags & PCPDF_PROXY_LOCAL_PORT && + (!pPCProxyData->ipProtocol || + !pPCProxyData->originalLocalPort)) || + (pPCProxyData->flags & PCPDF_PROXY_REMOTE_PORT && + (!pPCProxyData->ipProtocol || + !pPCProxyData->originalRemotePort))) + { + status = ERROR_INVALID_DATA; + + HlprLogError(L"ProxyScenarioExecute : Must specify the original address and / or port being proxied [status: %#x]", + status); + + HLPR_BAIL; + } + } + + status = RPCInvokeScenarioProxy(wfpSamplerBindingHandle, + SCENARIO_PROXY, + removeScenario ? FWPM_CHANGE_DELETE : FWPM_CHANGE_ADD, + pFilter, + pPCProxyData); + if(status != NO_ERROR) + HlprLogError(L"ProxyScenarioExecute : RpcInvokeScenarioProxy() [status: %#x]", + status); + else + HlprLogInfo(L"ProxyScenarioExecute : RpcInvokeScenarioProxy() [status: %#x]", + status); + + HLPR_BAIL_LABEL: + + if(pFilter) + HlprFwpmFilterDestroy(&pFilter); + + HLPR_DELETE(pPCProxyData);; + + return status; +} + +/** + @public_function="ProxyScenarioLogHelp" + + Purpose: Log usage information for the PROXY scenario to the console.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +VOID ProxyScenarioLogHelp() +{ +#if(NTDDI_VERSION >= NTDDI_WIN7) + + PWSTR pLayer = L"FWPM_LAYER_ALE_CONNECT_REDIRECT_V4"; + +#else + + PWSTR pLayer = L"FWPM_LAYER_OUTBOUND_TRANSPORT_V4"; + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + wprintf(L"\n\t\t -s \t PROXY"); + wprintf(L"\n\t\t -? \t Receive usage information."); + wprintf(L"\n\t\t -l \t Specify the layer to perform the filtering. [Required]"); + wprintf(L"\n\t\t -sl \t Specify the sublayer to perform the filtering. [Optional]"); + wprintf(L"\n\t\t -r \t Remove the scenario objects."); + wprintf(L"\n\t\t -v \t Make the filter volatile (non-persistent). [Optional]"); + wprintf(L"\n\t\t -b \t Makes the objects available during boot time. [Optional]"); + wprintf(L"\n\t\t -in \t Perform the injection inline if possible. [Optional]"); + wprintf(L"\n\t\t -tdpc \t Use threaded DPCs for out of band. [Optional]"); + wprintf(L"\n\t\t -wi \t Use work items for out of band. [Optional]"); + wprintf(L"\n\t\t -ipla \t Specify the IP_LOCAL_ADDRESS /"); + wprintf(L"\n\t\t \t IP_SOURCE_ADDRESS to filter. [Optional]"); + wprintf(L"\n\t\t -ipra \t Specify the IP_REMOTE_ADDRESS /"); + wprintf(L"\n\t\t \t IP_DESTINATION_ADDRESS to filter. [Optional]"); + wprintf(L"\n\t\t -ipp \t Specify the IP_PROTOCOL to filter. [Optional]"); + wprintf(L"\n\t\t -iplp \t Specify the IP_LOCAL_PORT to filter. [Optional]"); + wprintf(L"\n\t\t -icmpt \t Specify the ICMP_TYPE to filter. [Optional]"); + wprintf(L"\n\t\t -iprp \t Specify the IP_REMOTE_PORT to filter. [Optional]"); + wprintf(L"\n\t\t -icmpc \t Specify the ICMP_CODE to filter. [Optional]"); + wprintf(L"\n\t\t -pla \t Specify the local address to proxy to. [Optional]"); + wprintf(L"\n\t\t -plp \t Specify the local port to proxy to. [Optional]"); + wprintf(L"\n\t\t -pra \t Specify the remote address to proxy to. [Optional]"); + wprintf(L"\n\t\t -prp \t Specify the remote port to proxy to. [Optional]"); + wprintf(L"\n\t\t -plspid\t Specify the local service PID to proxy to. [Optional] [Defaults to WFPSamplerProxyService.Exe's PID]"); + wprintf(L"\n\t\t -prs \t Specify the proxying will be to a remote service. [Optional]"); + wprintf(L"\n"); + wprintf(L"\n\t i.e."); + wprintf(L"\n\t\t WFPSampler.Exe -s PROXY -l %s -rp 80 -p TCP -pla 127.0.0.1 -plp 4080 -v", + pLayer); + wprintf(L"\n\t\t WFPSampler.Exe -s PROXY -l %s -rp 80 -p TCP -pla 127.0.0.1 -plp 4080 -v -r", + pLayer); + wprintf(L"\n"); + wprintf(L"\n\t\t Note: if the layer specified is FWPM_LAYER_OUTBOUND_TRANSPORT_V{4/6}:"); + wprintf(L"\n\t\t\tIf proxying the ports, then you must specify the protocol (-ipp) and the original port being modified (-iprp and / or -iplp)"); + wprintf(L"\n\t\t\tIf proxying the addresses, then you must specify the the original address (-ipra and / or -ipla)"); + wprintf(L"\n"); + + return; +} diff --git a/network/trans/WFPSampler/exe/Scenarios_Proxy.h b/network/trans/WFPSampler/exe/Scenarios_Proxy.h new file mode 100644 index 000000000..c846c0f94 --- /dev/null +++ b/network/trans/WFPSampler/exe/Scenarios_Proxy.h @@ -0,0 +1,30 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// Scenarios_Proxy.h +// +// Abstract: +// This module contains prototypes for functions which run the specified PROXY scenario. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef SCENARIOS_PROXY_H +#define SCENARIOS_PROXY_H + +_Success_(return == NO_ERROR) +UINT32 ProxyScenarioExecute(_In_reads_(stringCount) PCWSTR* ppCLPStrings, + _In_ UINT32 stringCount); + +VOID ProxyScenarioLogHelp(); + +#endif /// SCENARIOS_PROXY_H diff --git a/network/trans/WFPSampler/exe/WFPSampler.vcxproj b/network/trans/WFPSampler/exe/WFPSampler.vcxproj new file mode 100644 index 000000000..62c833eeb --- /dev/null +++ b/network/trans/WFPSampler/exe/WFPSampler.vcxproj @@ -0,0 +1,206 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {649E7C26-3C81-45CB-98E1-D40349D7B3F2} + $(MSBuildProjectName) + Debug + Win32 + {9E22E687-050F-4927-8518-52188494A972} + + + + 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) + + + + + + + + + + + + + + + + WFPSampler + + + WFPSampler + + + WFPSampler + + + WFPSampler + + + + Sync + true + Level4 + %(PreprocessorDefinitions);WIN32_LEAN_AND_MEAN;UNICODE;_UNICODE;UM_NDIS630 + %(AdditionalIncludeDirectories);..\inc;..\lib;$(SDK_INC_PATH);.\$(IntDir);.\..\lib\$(IntDir) + + + %(PreprocessorDefinitions);WIN32_LEAN_AND_MEAN;UNICODE;_UNICODE;UM_NDIS630 + %(AdditionalIncludeDirectories);..\inc;..\lib;$(SDK_INC_PATH);.\$(IntDir);.\..\lib\$(IntDir) + + + %(PreprocessorDefinitions);WIN32_LEAN_AND_MEAN;UNICODE;_UNICODE;UM_NDIS630 + %(AdditionalIncludeDirectories);..\inc;..\lib;$(SDK_INC_PATH);.\$(IntDir);.\..\lib\$(IntDir) + + + %(AdditionalOptions) /integritycheck + %(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;ws2_32.lib;.\..\lib\$(IntDir)\WFPSampler.lib + + + + + Sync + true + Level4 + %(PreprocessorDefinitions);WIN32_LEAN_AND_MEAN;UNICODE;_UNICODE;UM_NDIS630 + %(AdditionalIncludeDirectories);..\inc;..\lib;$(SDK_INC_PATH);.\$(IntDir);.\..\lib\$(IntDir) + + + %(PreprocessorDefinitions);WIN32_LEAN_AND_MEAN;UNICODE;_UNICODE;UM_NDIS630 + %(AdditionalIncludeDirectories);..\inc;..\lib;$(SDK_INC_PATH);.\$(IntDir);.\..\lib\$(IntDir) + + + %(PreprocessorDefinitions);WIN32_LEAN_AND_MEAN;UNICODE;_UNICODE;UM_NDIS630 + %(AdditionalIncludeDirectories);..\inc;..\lib;$(SDK_INC_PATH);.\$(IntDir);.\..\lib\$(IntDir) + + + %(AdditionalOptions) /integritycheck + %(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;ws2_32.lib;.\..\lib\$(IntDir)\WFPSampler.lib + + + + + Sync + true + Level4 + %(PreprocessorDefinitions);WIN32_LEAN_AND_MEAN;UNICODE;_UNICODE;UM_NDIS630 + %(AdditionalIncludeDirectories);..\inc;..\lib;$(SDK_INC_PATH);.\$(IntDir);.\..\lib\$(IntDir) + + + %(PreprocessorDefinitions);WIN32_LEAN_AND_MEAN;UNICODE;_UNICODE;UM_NDIS630 + %(AdditionalIncludeDirectories);..\inc;..\lib;$(SDK_INC_PATH);.\$(IntDir);.\..\lib\$(IntDir) + + + %(PreprocessorDefinitions);WIN32_LEAN_AND_MEAN;UNICODE;_UNICODE;UM_NDIS630 + %(AdditionalIncludeDirectories);..\inc;..\lib;$(SDK_INC_PATH);.\$(IntDir);.\..\lib\$(IntDir) + + + %(AdditionalOptions) /integritycheck + %(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;ws2_32.lib;.\..\lib\$(IntDir)\WFPSampler.lib + + + + + Sync + true + Level4 + %(PreprocessorDefinitions);WIN32_LEAN_AND_MEAN;UNICODE;_UNICODE;UM_NDIS630 + %(AdditionalIncludeDirectories);..\inc;..\lib;$(SDK_INC_PATH);.\$(IntDir);.\..\lib\$(IntDir) + + + %(PreprocessorDefinitions);WIN32_LEAN_AND_MEAN;UNICODE;_UNICODE;UM_NDIS630 + %(AdditionalIncludeDirectories);..\inc;..\lib;$(SDK_INC_PATH);.\$(IntDir);.\..\lib\$(IntDir) + + + %(PreprocessorDefinitions);WIN32_LEAN_AND_MEAN;UNICODE;_UNICODE;UM_NDIS630 + %(AdditionalIncludeDirectories);..\inc;..\lib;$(SDK_INC_PATH);.\$(IntDir);.\..\lib\$(IntDir) + + + %(AdditionalOptions) /integritycheck + %(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;ws2_32.lib;.\..\lib\$(IntDir)\WFPSampler.lib + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/network/trans/WFPSampler/exe/WFPSampler.vcxproj.Filters b/network/trans/WFPSampler/exe/WFPSampler.vcxproj.Filters new file mode 100644 index 000000000..88b520428 --- /dev/null +++ b/network/trans/WFPSampler/exe/WFPSampler.vcxproj.Filters @@ -0,0 +1,67 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {31FB934B-A010-4E7D-9A21-C196110E39E4} + + + h;hpp;hxx;hm;inl;inc;xsd + {7EA14BFC-1419-406C-86A0-9A7468E86FD8} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {DF54F0E0-F664-4EC6-BC4A-990C39A66A4B} + + + + + 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 + + + \ No newline at end of file diff --git a/network/trans/WFPSampler/idl/WFPSamplerRPC.ACF b/network/trans/WFPSampler/idl/WFPSamplerRPC.ACF new file mode 100644 index 000000000..51883f4f1 --- /dev/null +++ b/network/trans/WFPSampler/idl/WFPSamplerRPC.ACF @@ -0,0 +1,55 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// WFPSamplerRPC.acf +// +// Abstract: +// This module contains application configuration information for WFPSampler's RPC interface. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Add ADVANCED_PACKET_INJECTION, FLOW_ASSOCIATION, and +// PEND_ENDPOINT_CLOSURE scenarios +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +[ + type_strict_context_handle, + implicit_handle(handle_t wfpSamplerBindingHandle) +] + +interface IWFPSampler +{ + [comm_status, fault_status] RPCInvokeScenarioAdvancedPacketInjection(); + + [comm_status, fault_status] RPCInvokeScenarioAppContainer(); + + [comm_status, fault_status] RPCInvokeScenarioBasicAction(); + + [comm_status, fault_status] RPCInvokeScenarioBasicPacketExamination(); + + [comm_status, fault_status] RPCInvokeScenarioBasicPacketInjection(); + + [comm_status, fault_status] RPCInvokeScenarioBasicPacketModification(); + + [comm_status, fault_status] RPCInvokeScenarioBasicStreamInjection(); + + [comm_status, fault_status] RPCInvokeScenarioFastPacketInjection(); + + [comm_status, fault_status] RPCInvokeScenarioFastStreamInjection(); + + [comm_status, fault_status] RPCInvokeScenarioFlowAssociation(); + + [comm_status, fault_status] RPCInvokeScenarioPendAuthorization(); + + [comm_status, fault_status] RPCInvokeScenarioPendEndpointClosure(); + + [comm_status, fault_status] RPCInvokeScenarioProxy(); +} diff --git a/network/trans/WFPSampler/idl/WFPSamplerRPC.IDL b/network/trans/WFPSampler/idl/WFPSamplerRPC.IDL new file mode 100644 index 000000000..10d0a3fa1 --- /dev/null +++ b/network/trans/WFPSampler/idl/WFPSamplerRPC.IDL @@ -0,0 +1,475 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// WFPSamplerRPC.idl +// +// Abstract: +// This module contains interface declarations used in implementing WFPSampler's RPC interface. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Add ADVANCED_PACKET_INJECTION, FLOW_ASSOCIATION, and +// PEND_ENDPOINT_CLOSURE scenarios. Enhancements for +// BASIC_PACKET_EXAMINATION, BASIC_PACKET_MODIFICATION, +// and PROXY providerContexts. +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +import "wtypes.idl"; /// Include\Shared +import "fwpmtypes.idl"; /// Include\Shared + +[ + helpstring("WFP Sampler Service 1.0"), + uuid(53504657-6D61-6C70-6572-5F496E746572), + version(1.0), + endpoint("ncalrpc:[WFPSampler]"), + pointer_default(ref) +] + +interface IWFPSampler +{ + cpp_quote("#ifdef __midl") + + typedef enum + { + IPV4 = 4, + IPV6 = 6, + }IPVERSION; + + typedef enum + { + IPPROTO_HOPOPTS = 0, + IPPROTO_ICMP = 1, + IPPROTO_IGMP = 2, + IPPROTO_GGP = 3, + IPPROTO_IPV4 = 4, + IPPROTO_ST = 5, + IPPROTO_TCP = 6, + IPPROTO_CBT = 7, + IPPROTO_EGP = 8, + IPPROTO_IGP = 9, + IPPROTO_PUP = 12, + IPPROTO_UDP = 17, + IPPROTO_IDP = 22, + IPPROTO_RDP = 27, + IPPROTO_IPV6 = 41, + IPPROTO_ROUTING = 43, + IPPROTO_FRAGMENT = 44, + IPPROTO_ESP = 50, + IPPROTO_AH = 51, + IPPROTO_ICMPV6 = 58, + IPPROTO_NONE = 59, + IPPROTO_DSTOPTS = 60, + IPPROTO_ND = 77, + IPPROTO_ICLFXBM = 78, + IPPROTO_PIM = 103, + IPPROTO_PGM = 113, + IPPROTO_L2TP = 115, + IPPROTO_SCTP = 132, + IPPROTO_RAW = 255, + IPPROTO_MAX = 256, + }IPPROTO; + + cpp_quote("#endif") + + typedef enum WFPSAMPLER_SCENARIO_ + { + SCENARIO_UNDEFINED = 0, + SCENARIO_ADVANCED_PACKET_INJECTION = 1, + SCENARIO_APP_CONTAINER = 2, + SCENARIO_BASIC_ACTION_BLOCK = 3, + SCENARIO_BASIC_ACTION_CONTINUE = 4, + SCENARIO_BASIC_ACTION_PERMIT = 5, + SCENARIO_BASIC_ACTION_RANDOM = 6, + SCENARIO_BASIC_PACKET_EXAMINATION = 7, + SCENARIO_BASIC_PACKET_INJECTION = 8, + SCENARIO_BASIC_PACKET_MODIFICATION = 9, + SCENARIO_BASIC_STREAM_INJECTION = 10, + SCENARIO_FAST_PACKET_INJECTION = 11, + SCENARIO_FAST_STREAM_INJECTION = 12, + SCENARIO_FLOW_ASSOCIATION = 13, + SCENARIO_PEND_AUTHORIZATION = 14, + SCENARIO_PEND_ENDPOINT_CLOSURE = 15, + SCENARIO_PROXY = 16, + SCENARIO_MAX + }WFPSAMPLER_SCENARIO; + + typedef struct MAC_DATA_ + { + UINT32 flags; + + BYTE pReserved[4]; + + BYTE pSourceMACAddress[8]; + + BYTE pDestinationMACAddress[8]; + }MAC_DATA; + + typedef struct IP_DATA_ + { + UINT32 flags; + + BYTE pReserved[3]; + + UINT8 ipVersion; + + [switch_type(IPVERSION), switch_is(ipVersion)] + union + { + [case(IPV4)] + BYTE pIPv4[4]; /// Network Byte Order + [case(IPV6)] + BYTE pIPv6[16]; + }sourceAddress; + + [switch_type(IPVERSION), switch_is(ipVersion)] + union + { + [case(IPV4)] + BYTE pIPv4[4]; /// Network Byte Order + [case(IPV6)] + BYTE pIPv6[16]; + }destinationAddress; + + UINT32 interfaceIndex; + }IP_DATA; + + typedef struct TRANSPORT_DATA_ + { + UINT32 flags; + + BYTE pReserved[3]; + + UINT8 protocol; + + [switch_type(UINT8), switch_is(protocol)] + union + { + [case(IPPROTO_ICMP, + IPPROTO_ICMPV6)] + UINT8 type; + [case(IPPROTO_TCP, + IPPROTO_UDP)] + UINT16 port; /// Network Byte Order + [case(IPPROTO_RAW)] + UINT16 data; + }source; + + [switch_type(UINT8), switch_is(protocol)] + union + { + [case(IPPROTO_ICMP, + IPPROTO_ICMPV6)] + UINT8 code; + [case(IPPROTO_TCP, + IPPROTO_UDP)] + UINT16 port; /// Network Byte Order + [case(IPPROTO_RAW)] + UINT16 data; + }destination; + BYTE pPadding[4]; + } TRANSPORT_DATA; + + typedef struct PC_ADVANCED_PACKET_INJECTION_DATA_ + { + BOOLEAN performInline; /// Inline vs. Out of Band + + BOOLEAN useWorkItems; /// DPCs vs. WorkItems + + BOOLEAN useThreadedDPC; /// DPCs vs Threaded DPCs + + UINT32 additionalBytes; + + BYTE pReserved[1]; + } PC_ADVANCED_PACKET_INJECTION_DATA; + + typedef struct PC_BASIC_ACTION_DATA_ + { + UINT8 percentBlock; + + UINT8 percentPermit; + + UINT8 percentContinue; + + BYTE pReserved[5]; + } PC_BASIC_ACTION_DATA; + + typedef struct PC_BASIC_PACKET_INJECTION_DATA_ + { + BOOLEAN performInline; /// Inline vs. Out of Band + + BOOLEAN useWorkItems; /// DPCs vs. WorkItems + + BOOLEAN useThreadedDPC; /// DPCs vs Threaded DPCs + + BYTE pReserved[5]; + } PC_BASIC_PACKET_INJECTION_DATA; + + typedef struct PC_BASIC_PACKET_MODIFICATION_DATA_ + { + UINT32 flags; + + BOOLEAN performInline; /// Inline vs. Out of Band + + BOOLEAN useWorkItems; /// DPCs vs. WorkItems + + BOOLEAN useThreadedDPC; /// DPCs vs Threaded DPCs + + BYTE pReserved[1]; + + MAC_DATA macData; + + IP_DATA ipData; + + TRANSPORT_DATA transportData; + + MAC_DATA originalMACData; + + IP_DATA originalIPData; + + TRANSPORT_DATA originalTransportData; /// Used to uniquely identify traffic at the Network layers + }PC_BASIC_PACKET_MODIFICATION_DATA; + + typedef struct PC_BASIC_STREAM_INJECTION_DATA_ + { + BOOLEAN performInline; /// Inline vs. Out of Band + + BOOLEAN useWorkItems; /// DPCs vs. WorkItems + + BOOLEAN useThreadedDPC; /// DPCs vs Threaded DPCs + + BYTE pReserved[5]; + } PC_BASIC_STREAM_INJECTION_DATA; + + typedef struct PC_FLOW_ASSOCIATION_DATA_ + { + UINT16 itemCount; + + UINT16 pLayerIDs[7]; + + UINT32 pCalloutIDs[7]; + }PC_FLOW_ASSOCIATION_DATA; + + typedef struct PC_PEND_AUTHORIZATION_DATA_ + { + UINT32 flags; + + BOOLEAN useWorkItems; /// DPCs vs. WorkItems + + BOOLEAN useThreadedDPC; /// DPCs vs Threaded DPCs + + BYTE pReserved[2]; + + UINT32 finalAction; + + UINT32 delay; + + }PC_PEND_AUTHORIZATION_DATA; + + typedef struct PC_PEND_ENDPOINT_CLOSURE_DATA_ + { + UINT32 flags; + + BOOLEAN useWorkItems; /// DPCs vs. WorkItems + + BOOLEAN useThreadedDPC; /// DPCs vs Threaded DPCs + + BYTE pReserved[2]; + + UINT32 delay; + + }PC_PEND_ENDPOINT_CLOSURE_DATA; + + typedef struct PC_PROXY_DATA_ + { + UINT32 flags; + + BOOLEAN performInline; /// Inline vs. Out of Band + + BOOLEAN useWorkItems; /// DPCs vs. WorkItems + + BOOLEAN useThreadedDPC; /// DPCs vs Threaded DPCs + + BOOLEAN proxyToRemoteService; /// Local vs. Remote Service + + BYTE pReserved[6]; + + UINT8 ipVersion; + + UINT8 ipProtocol; + + [switch_type(IPVERSION), switch_is(ipVersion)] + union + { + [case(IPV4)] + BYTE pIPv4[4]; /// Network Byte Order + [case(IPV6)] + BYTE pIPv6[16]; + }proxyLocalAddress; + + [switch_type(IPVERSION), switch_is(ipVersion)] + union + { + [case(IPV4)] + BYTE pIPv4[4]; /// Network Byte Order + [case(IPV6)] + BYTE pIPv6[16]; + }proxyRemoteAddress; + + [switch_type(IPVERSION), switch_is(ipVersion)] + union + { + [case(IPV4)] + BYTE pIPv4[4]; /// Network Byte Order + [case(IPV6)] + BYTE pIPv6[16]; + }originalLocalAddress; + + [switch_type(IPVERSION), switch_is(ipVersion)] + union + { + [case(IPV4)] + BYTE pIPv4[4]; /// Network Byte Order + [case(IPV6)] + BYTE pIPv6[16]; + }originalRemoteAddress; + + UINT32 localScopeId; + + UINT32 remoteScopeId; + + UINT16 proxyLocalPort; /// Network Byte Order + + UINT16 proxyRemotePort; /// Network Byte Order + + UINT16 originalLocalPort; /// Network Byte Order + + UINT16 originalRemotePort; /// Network Byte Order + + UINT32 targetProcessID; + + UINT64 tcpPortReservationToken; + + UINT64 udpPortReservationToken; + } PC_PROXY_DATA; + + /// ProviderContext ProxyData Flags + cpp_quote("#define PCPDF_PROXY_LOCAL_ADDRESS 0x01") + cpp_quote("#define PCPDF_PROXY_LOCAL_PORT 0x02") + cpp_quote("#define PCPDF_PROXY_REMOTE_ADDRESS 0x04") + cpp_quote("#define PCPDF_PROXY_REMOTE_PORT 0x08") + + ///ProviderContext PacketExamination Flags + cpp_quote("#define PCPEF_EXAMINE_UNDER_LOCK 0x00000001") + cpp_quote("#define PCPEF_EXAMINE_INCOMING_VALUES 0x00000010") + cpp_quote("#define PCPEF_EXAMINE_INCOMING_METADATA_VALUES 0x00000100") + cpp_quote("#define PCPEF_EXAMINE_LAYER_DATA 0x00001000") + cpp_quote("#define PCPEF_EXAMINE_CLASSIFY_CONTEXT 0x00010000") + cpp_quote("#define PCPEF_EXAMINE_FILTER 0x00100000") + cpp_quote("#define PCPEF_EXAMINE_FLOW_CONTEXT 0x01000000") + cpp_quote("#define PCPEF_EXAMINE_CLASSIFY_OUT 0x10000000") + + ///ProviderContext PacketModificationData Flags + cpp_quote("#define PCPMDF_MODIFY_MAC_HEADER 0x10") + cpp_quote("#define PCPMDF_MODIFY_MAC_HEADER_SOURCE_ADDRESS 0x01") + cpp_quote("#define PCPMDF_MODIFY_MAC_HEADER_DESTINATION_ADDRESS 0x02") + + cpp_quote("#define PCPMDF_MODIFY_IP_HEADER 0x20") + cpp_quote("#define PCPMDF_MODIFY_IP_HEADER_SOURCE_ADDRESS 0x01") + cpp_quote("#define PCPMDF_MODIFY_IP_HEADER_DESTINATION_ADDRESS 0x02") + cpp_quote("#define PCPMDF_MODIFY_IP_HEADER_INTERFACE_INDEX 0x04") + + cpp_quote("#define PCPMDF_MODIFY_TRANSPORT_HEADER 0x40") + cpp_quote("#define PCPMDF_MODIFY_TRANSPORT_HEADER_SOURCE_PORT 0x01") + cpp_quote("#define PCPMDF_MODIFY_TRANSPORT_HEADER_DESTINATION_PORT 0x02") + + cpp_quote("#define PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_TYPE PCPMDF_MODIFY_TRANSPORT_HEADER_SOURCE_PORT") + cpp_quote("#define PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_CODE PCPMDF_MODIFY_TRANSPORT_HEADER_DESTINATION_PORT") + + cpp_quote("#define PCFA_MAX_COUNT 7") + + extern handle_t wfpSamplerBindingHandle; + + error_status_t RPCInvokeScenarioAdvancedPacketInjection([in] handle_t rpcBindingHandle, + [in] WFPSAMPLER_SCENARIO scenario, + [in] FWPM_CHANGE_TYPE changeType, + [in, ref] const FWPM_FILTER0* pFilter, + [in, unique] const PC_ADVANCED_PACKET_INJECTION_DATA* pPCAdvancedPacketInjectionData); + + error_status_t RPCInvokeScenarioAppContainer([in] handle_t rpcBindingHandle, + [in] WFPSAMPLER_SCENARIO scenario, + [in] FWPM_CHANGE_TYPE changeType, + [in] BOOLEAN trustWSH, + [in] BOOLEAN persistent, + [in] BOOLEAN bootTime); + + error_status_t RPCInvokeScenarioBasicAction([in] handle_t rpcBindingHandle, + [in] WFPSAMPLER_SCENARIO scenario, + [in] FWPM_CHANGE_TYPE changeType, + [in, ref] const FWPM_FILTER0* pFilter, + [in, unique] const PC_BASIC_ACTION_DATA* pPCBasicActionData); + + error_status_t RPCInvokeScenarioBasicPacketExamination([in] handle_t rpcBindingHandle, + [in] WFPSAMPLER_SCENARIO scenario, + [in] FWPM_CHANGE_TYPE changeType, + [in, ref] const FWPM_FILTER0* pFilter); + + error_status_t RPCInvokeScenarioBasicPacketInjection([in] handle_t rpcBindingHandle, + [in] WFPSAMPLER_SCENARIO scenario, + [in] FWPM_CHANGE_TYPE changeType, + [in, ref] const FWPM_FILTER0* pFilter, + [in, unique] const PC_BASIC_PACKET_INJECTION_DATA* pPCBasicPacketInjectionData); + + error_status_t RPCInvokeScenarioBasicPacketModification([in] handle_t rpcBindingHandle, + [in] WFPSAMPLER_SCENARIO scenario, + [in] FWPM_CHANGE_TYPE changeType, + [in, ref] const FWPM_FILTER0* pFilter, + [in, unique] const PC_BASIC_PACKET_MODIFICATION_DATA* pPCBasicPacketModificationData); + + error_status_t RPCInvokeScenarioBasicStreamInjection([in] handle_t rpcBindingHandle, + [in] WFPSAMPLER_SCENARIO scenario, + [in] FWPM_CHANGE_TYPE changeType, + [in, ref] const FWPM_FILTER0* pFilter, + [in, unique] const PC_BASIC_STREAM_INJECTION_DATA* pPCBasicStreamInjectionData); + + error_status_t RPCInvokeScenarioFastPacketInjection([in] handle_t rpcBindingHandle, + [in] WFPSAMPLER_SCENARIO scenario, + [in] FWPM_CHANGE_TYPE changeType, + [in, ref] const FWPM_FILTER0* pFilter); + + error_status_t RPCInvokeScenarioFastStreamInjection([in] handle_t rpcBindingHandle, + [in] WFPSAMPLER_SCENARIO scenario, + [in] FWPM_CHANGE_TYPE changeType, + [in, ref] const FWPM_FILTER0* pFilter); + + error_status_t RPCInvokeScenarioFlowAssociation([in] handle_t rpcBindingHandle, + [in] WFPSAMPLER_SCENARIO scenario, + [in] FWPM_CHANGE_TYPE changeType, + [in, ref] const FWPM_FILTER0* pFilter, + [in, unique] const PC_FLOW_ASSOCIATION_DATA* pPCFlowAssociationData); + + error_status_t RPCInvokeScenarioPendAuthorization([in] handle_t rpcBindingHandle, + [in] WFPSAMPLER_SCENARIO scenario, + [in] FWPM_CHANGE_TYPE changeType, + [in, ref] const FWPM_FILTER0* pFilter, + [in, unique] const PC_PEND_AUTHORIZATION_DATA* pPCPendAuthorizationData); + + error_status_t RPCInvokeScenarioPendEndpointClosure([in] handle_t rpcBindingHandle, + [in] WFPSAMPLER_SCENARIO scenario, + [in] FWPM_CHANGE_TYPE changeType, + [in, ref] const FWPM_FILTER0* pFilter, + [in, unique] const PC_PEND_ENDPOINT_CLOSURE_DATA* pPCPendEndpointClosureData); + + error_status_t RPCInvokeScenarioProxy([in] handle_t rpcBindingHandle, + [in] WFPSAMPLER_SCENARIO scenario, + [in] FWPM_CHANGE_TYPE changeType, + [in, ref] const FWPM_FILTER0* pFilter, + [in, ref] const PC_PROXY_DATA* pPCProxyData); +} diff --git a/network/trans/WFPSampler/inc/Identifiers.h b/network/trans/WFPSampler/inc/Identifiers.h new file mode 100644 index 000000000..161c19a0a --- /dev/null +++ b/network/trans/WFPSampler/inc/Identifiers.h @@ -0,0 +1,4082 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// Identifiers.h +// +// Abstract: +// This module contains global variables and definitions for the WFPSampler project +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Add ADVANCED_PACKET_INJECTION, FLOW_ASSOCIATION, and +// PEND_ENDPOINT_CLOSURE callouts. Expand callouts for +// BASIC_ACTION, and BASIC_PACKET_EXAMINATION. +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef WFP_SAMPLER_IDENTIFIERS_H +#define WFP_SAMPLER_IDENTIFIERS_H + +static PCWSTR g_pDeviceName = L"\\Device\\WFPSampler"; +static PCWSTR g_pCompanyName = L"Microsoft Corporation"; +static PCWSTR g_pBinaryDescription = L"WFPSampler version 1.0.0.1, Copyright (c) 2012 Microsoft Corporation. All Rights Reserved."; +static PCWSTR g_pProxyBinaryDescription = L"WFPSamplerProxyService version 1.0.0.1, Copyright (c) 2012 Microsoft Corporation. All Rights Reserved."; +static PCWSTR g_pServiceExe = L"%SystemRoot%\\System32\\WFPSamplerService.Exe"; +static PCWSTR g_pServiceName = L"WFPSampler"; +static PCWSTR g_pServiceDescription = L"Microsoft Windows Filtering Platform (WFP) Sample Policy Service"; +static PCWSTR g_pCalloutServiceName = L"WFPSamplerCallouts"; +static PCWSTR g_pProxyServiceExe = L"%SystemRoot%\\System32\\WFPSamplerProxyService.Exe"; +static PCWSTR g_pProxyServiceName = L"WFPSamplerProxy"; +static PCWSTR g_pProxyServiceDescription = L"Microsoft Windows Filtering Platform (WFP) Sample Proxy Service"; +static PCWSTR g_pRPCProtocolSequence = L"ncalrpc"; +static PCWSTR g_pEndpoint = L"ncalrpc:[WFPSampler]"; + +/* + Service Security Identifier +*/ + +static const SID wfpSamplerSID = +{ + SID_REVISION, + 1, + SECURITY_NT_AUTHORITY, + { SECURITY_LOCAL_SERVICE_RID } +}; + +/* + DEVICE_INTERFACE +*/ + +static const GUID WFPSAMPLER_INTERFACE = +{ + /* 53504657-6D61-6C70-6572-5F496E746572 */ + 0x53504657, + 0x6D61, + 0x6C70, + {0x65, 0x72, 0x5F, 0x49, 0x6E, 0x74, 0x65, 0x72} +}; + +/* + ETW \ WTT TRACING +*/ + +static const GUID WFPSAMPLER_WPP_TRACE = +{ + /* 53504657-6D61-6C70-6572-5F5750505472 */ + 0x53504657, + 0x6D61, + 0x6C70, + {0x65, 0x72, 0x5F, 0x57, 0x50, 0x50, 0x54, 0x72} +}; + +/* + FWPM_SESSION Keys +**/ + +static const GUID WFPSAMPLER_SESSION_KM = +{ + /* 53504657-6D61-5F70-5365-7373696F6E4B */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x53, 0x65, 0x73, 0x73, 0x69, 0x6F, 0x6E, 0x4B} +}; + +static const GUID WFPSAMPLER_SESSION_UM = +{ + /* 53504657-6D61-5F70-5365-7373696F6E55 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x53, 0x65, 0x73, 0x73, 0x69, 0x6F, 0x6E, 0x55} +}; + + +/* + FWPM_PROVIDER Key +**/ + +static const GUID WFPSAMPLER_PROVIDER = +{ + /* 53504657-6D61-5F70-5072-6F7669646572 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x50, 0x72, 0x6F, 0x76, 0x69, 0x64, 0x65, 0x72} +}; + +static const GUID WFPSAMPLERPROXYSERVICE_PROVIDER = +{ + /* 50504657-7653-6370-5072-6F7669646572 */ + 0x50504657, + 0x7653, + 0x6370, + {0x50, 0x72, 0x6F, 0x76, 0x69, 0x64, 0x65, 0x72} +}; + +/* + FWPM_SUBLAYER Key +**/ + +static const GUID WFPSAMPLER_SUBLAYER = +{ + /* 53504657-6D61-5F70-5375-624C61796572 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x53, 0x75, 0x62, 0x4C, 0x61, 0x79, 0x65, 0x72} +}; + +static const GUID WFPSAMPLERPROXYSERVICE_SUBLAYER = +{ + /* 50504657-7653-6370-5375-624C61796572 */ + 0x50504657, + 0x7653, + 0x6370, + {0x53, 0x75, 0x62, 0x4C, 0x61, 0x79, 0x65, 0x72} +}; + +/* + FWPM_CALLOUT Keys +**/ + +#ifndef ADVANCED_PACKET_INJECTION +#define ADVANCED_PACKET_INJECTION + +static const GUID WFPSAMPLER_CALLOUT_ADVANCED_PACKET_INJECTION = +{ + /* 53504657-6D61-5F70-4361-6C6C415049FF */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x41, 0x50, 0x49, 0xFF} +}; + +static const GUID WFPSAMPLER_CALLOUT_ADVANCED_PACKET_INJECTION_AT_INBOUND_IPPACKET_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C41504900 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x41, 0x50, 0x49, 0x00} +}; + +static const GUID WFPSAMPLER_CALLOUT_ADVANCED_PACKET_INJECTION_AT_INBOUND_IPPACKET_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C41504902 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x41, 0x50, 0x49, 0x02} +}; + +static const GUID WFPSAMPLER_CALLOUT_ADVANCED_PACKET_INJECTION_AT_OUTBOUND_IPPACKET_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C41504904 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x41, 0x50, 0x49, 0x04} +}; + +static const GUID WFPSAMPLER_CALLOUT_ADVANCED_PACKET_INJECTION_AT_OUTBOUND_IPPACKET_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C41504906 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x41, 0x50, 0x49, 0x06} +}; + +static const GUID WFPSAMPLER_CALLOUT_ADVANCED_PACKET_INJECTION_AT_IPFORWARD_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C41504908 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x41, 0x50, 0x49, 0x08} +}; + +static const GUID WFPSAMPLER_CALLOUT_ADVANCED_PACKET_INJECTION_AT_IPFORWARD_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C4150490A */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x41, 0x50, 0x49, 0x0A} +}; + +static const GUID WFPSAMPLER_CALLOUT_ADVANCED_PACKET_INJECTION_AT_INBOUND_TRANSPORT_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C4150490C */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x41, 0x50, 0x49, 0x0C} +}; + +static const GUID WFPSAMPLER_CALLOUT_ADVANCED_PACKET_INJECTION_AT_INBOUND_TRANSPORT_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C4150490E */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x41, 0x50, 0x49, 0x0E} +}; + +static const GUID WFPSAMPLER_CALLOUT_ADVANCED_PACKET_INJECTION_AT_OUTBOUND_TRANSPORT_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C41504910 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x41, 0x50, 0x49, 0x10} +}; + +static const GUID WFPSAMPLER_CALLOUT_ADVANCED_PACKET_INJECTION_AT_OUTBOUND_TRANSPORT_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C41504912 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x41, 0x50, 0x49, 0x12} +}; + +static const GUID WFPSAMPLER_CALLOUT_ADVANCED_PACKET_INJECTION_AT_DATAGRAM_DATA_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C41504918 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x41, 0x50, 0x49, 0x18} +}; + +static const GUID WFPSAMPLER_CALLOUT_ADVANCED_PACKET_INJECTION_AT_DATAGRAM_DATA_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C4150491A */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x41, 0x50, 0x49, 0x1A} +}; + +static const GUID WFPSAMPLER_CALLOUT_ADVANCED_PACKET_INJECTION_AT_INBOUND_ICMP_ERROR_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C4150491C */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x41, 0x50, 0x49, 0x1C} +}; + +static const GUID WFPSAMPLER_CALLOUT_ADVANCED_PACKET_INJECTION_AT_INBOUND_ICMP_ERROR_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C4150491E */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x41, 0x50, 0x49, 0x1E} +}; + +static const GUID WFPSAMPLER_CALLOUT_ADVANCED_PACKET_INJECTION_AT_OUTBOUND_ICMP_ERROR_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C41504920 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x41, 0x50, 0x49, 0x20} +}; + +static const GUID WFPSAMPLER_CALLOUT_ADVANCED_PACKET_INJECTION_AT_OUTBOUND_ICMP_ERROR_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C41504922 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x41, 0x50, 0x49, 0x22} +}; + +static const GUID WFPSAMPLER_CALLOUT_ADVANCED_PACKET_INJECTION_AT_ALE_AUTH_RECV_ACCEPT_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C4150492C */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x41, 0x50, 0x49, 0x2C} +}; + +static const GUID WFPSAMPLER_CALLOUT_ADVANCED_PACKET_INJECTION_AT_ALE_AUTH_RECV_ACCEPT_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C4150492E */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x41, 0x50, 0x49, 0x2E} +}; + +static const GUID WFPSAMPLER_CALLOUT_ADVANCED_PACKET_INJECTION_AT_ALE_AUTH_CONNECT_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C41504930 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x41, 0x50, 0x49, 0x30} +}; + +static const GUID WFPSAMPLER_CALLOUT_ADVANCED_PACKET_INJECTION_AT_ALE_AUTH_CONNECT_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C41504932 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x41, 0x50, 0x49, 0x32} +}; + +static const GUID WFPSAMPLER_CALLOUT_ADVANCED_PACKET_INJECTION_AT_ALE_FLOW_ESTABLISHED_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C41504934 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x41, 0x50, 0x49, 0x34} +}; + +static const GUID WFPSAMPLER_CALLOUT_ADVANCED_PACKET_INJECTION_AT_ALE_FLOW_ESTABLISHED_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C41504936 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x41, 0x50, 0x49, 0x36} +}; + +static const GUID WFPSAMPLER_CALLOUT_ADVANCED_PACKET_INJECTION_AT_INBOUND_MAC_FRAME_ETHERNET = +{ + /* 53504657-6D61-5F70-4361-6C6C41504938 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x41, 0x50, 0x49, 0x38} +}; + +static const GUID WFPSAMPLER_CALLOUT_ADVANCED_PACKET_INJECTION_AT_OUTBOUND_MAC_FRAME_ETHERNET = +{ + /* 53504657-6D61-5F70-4361-6C6C41504939 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x41, 0x50, 0x49, 0x39} +}; + +static const GUID WFPSAMPLER_CALLOUT_ADVANCED_PACKET_INJECTION_AT_INBOUND_MAC_FRAME_NATIVE = +{ + /* 53504657-6D61-5F70-4361-6C6C4150493A */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x41, 0x50, 0x49, 0x3A} +}; + +static const GUID WFPSAMPLER_CALLOUT_ADVANCED_PACKET_INJECTION_AT_OUTBOUND_MAC_FRAME_NATIVE = +{ + /* 53504657-6D61-5F70-4361-6C6C4150493B */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x41, 0x50, 0x49, 0x3B} +}; + +static const GUID WFPSAMPLER_CALLOUT_ADVANCED_PACKET_INJECTION_AT_STREAM_PACKET_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C41504946 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x41, 0x50, 0x49, 0x46} +}; + +static const GUID WFPSAMPLER_CALLOUT_ADVANCED_PACKET_INJECTION_AT_STREAM_PACKET_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C41504947 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x41, 0x50, 0x49, 0x47} +}; + +static const GUID WFPSAMPLER_CALLOUT_ADVANCED_PACKET_INJECTION_AT_INGRESS_VSWITCH_ETHERNET = +{ + /* 53504657-6D61-5F70-4361-6C6C41504948 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x41, 0x50, 0x49, 0x48} +}; + +static const GUID WFPSAMPLER_CALLOUT_ADVANCED_PACKET_INJECTION_AT_EGRESS_VSWITCH_ETHERNET = +{ + /* 53504657-6D61-5F70-4361-6C6C41504949 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x41, 0x50, 0x49, 0x49} +}; + +#endif /// ADVANCED_PACKET_INJECTION + +#ifndef BASIC_ACTION_BLOCK +#define BASIC_ACTION_BLOCK + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK = +{ + /* 53504657-6D61-5F70-4361-6C6C424142FF */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x42, 0xFF} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_INBOUND_IPPACKET_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42414200 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x42, 0x00} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_INBOUND_IPPACKET_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42414202 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x42, 0x02} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_OUTBOUND_IPPACKET_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42414204 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x42, 0x04} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_OUTBOUND_IPPACKET_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42414206 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x42, 0x06} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_IPFORWARD_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42414208 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x42, 0x08} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_IPFORWARD_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C4241420A */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x42, 0x0A} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_INBOUND_TRANSPORT_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C4241420C */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x42, 0x0C} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_INBOUND_TRANSPORT_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C4241420E */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x42, 0x0E} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_OUTBOUND_TRANSPORT_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42414210 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x42, 0x10} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_OUTBOUND_TRANSPORT_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42414212 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x42, 0x12} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_STREAM_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42414214 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x42, 0x14} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_STREAM_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42414216 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x42, 0x16} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_DATAGRAM_DATA_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42414218 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x42, 0x18} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_DATAGRAM_DATA_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C4241421A */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x42, 0x1A} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_INBOUND_ICMP_ERROR_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C4241421C */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x42, 0x1C} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_INBOUND_ICMP_ERROR_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C4241421E */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x42, 0x1E} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_OUTBOUND_ICMP_ERROR_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42414220 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x42, 0x20} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_OUTBOUND_ICMP_ERROR_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42414222 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x42, 0x22} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_ALE_RESOURCE_ASSIGNMENT_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42414224 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x42, 0x24} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_ALE_RESOURCE_ASSIGNMENT_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42414226 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x42, 0x26} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_ALE_AUTH_LISTEN_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42414228 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x42, 0x28} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_ALE_AUTH_LISTEN_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C4241422A */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x42, 0x2A} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_ALE_AUTH_RECV_ACCEPT_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C4241422C */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x42, 0x2C} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_ALE_AUTH_RECV_ACCEPT_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C4241422E */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x42, 0x2E} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_ALE_AUTH_CONNECT_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42414230 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x42, 0x30} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_ALE_AUTH_CONNECT_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42414232 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x42, 0x32} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_ALE_FLOW_ESTABLISHED_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42414234 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x42, 0x34} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_ALE_FLOW_ESTABLISHED_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42414236 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x42, 0x36} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_INBOUND_MAC_FRAME_ETHERNET = +{ + /* 53504657-6D61-5F70-4361-6C6C42414238 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x42, 0x38} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_OUTBOUND_MAC_FRAME_ETHERNET = +{ + /* 53504657-6D61-5F70-4361-6C6C42414239 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x42, 0x39} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_INBOUND_MAC_FRAME_NATIVE = +{ + /* 53504657-6D61-5F70-4361-6C6C4241423A */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x42, 0x3A} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_OUTBOUND_MAC_FRAME_NATIVE = +{ + /* 53504657-6D61-5F70-4361-6C6C4241423B */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x42, 0x3B} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_ALE_RESOURCE_RELEASE_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C4241423E */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x42, 0x3E} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_ALE_RESOURCE_RELEASE_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C4241423F */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x42, 0x3F} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_ALE_ENDPOINT_CLOSURE_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42414240 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x42, 0x40} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_ALE_ENDPOINT_CLOSURE_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42414241 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x42, 0x41} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_ALE_CONNECT_REDIRECT_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42414242 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x42, 0x42} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_ALE_CONNECT_REDIRECT_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42414243 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x42, 0x43} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_ALE_BIND_REDIRECT_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42414244 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x42, 0x44} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_ALE_BIND_REDIRECT_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42414245 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x42, 0x45} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_STREAM_PACKET_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42414246 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x42, 0x46} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_STREAM_PACKET_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42414247 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x42, 0x47} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_INGRESS_VSWITCH_ETHERNET = +{ + /* 53504657-6D61-5F70-4361-6C6C42414248 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x42, 0x48} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_EGRESS_VSWITCH_ETHERNET = +{ + /* 53504657-6D61-5F70-4361-6C6C42414249 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x42, 0x49} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_INGRESS_VSWITCH_TRANSPORT_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C4241424A */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x42, 0x4A} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_INGRESS_VSWITCH_TRANSPORT_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C4241424B */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x42, 0x4B} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_EGRESS_VSWITCH_TRANSPORT_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C4241424C */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x42, 0x4C} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_EGRESS_VSWITCH_TRANSPORT_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C4241424D */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x42, 0x4D} +}; + +#endif /// BASIC_ACTION_BLOCK + +#ifndef BASIC_ACTION_CONTINUE +#define BASIC_ACTION_CONTINUE + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE = +{ + /* 53504657-6D61-5F70-4361-6C6C424143FF */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x43, 0xFF} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_INBOUND_IPPACKET_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42414300 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x43, 0x00} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_INBOUND_IPPACKET_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42414302 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x43, 0x02} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_OUTBOUND_IPPACKET_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42414304 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x43, 0x04} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_OUTBOUND_IPPACKET_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42414306 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x43, 0x06} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_IPFORWARD_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42414308 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x43, 0x08} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_IPFORWARD_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C4241430A */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x43, 0x0A} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_INBOUND_TRANSPORT_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C4241430C */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x43, 0x0C} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_INBOUND_TRANSPORT_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C4241430E */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x43, 0x0E} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_OUTBOUND_TRANSPORT_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42414310 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x43, 0x10} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_OUTBOUND_TRANSPORT_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42414312 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x43, 0x12} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_STREAM_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42414314 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x43, 0x14} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_STREAM_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42414316 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x43, 0x16} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_DATAGRAM_DATA_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42414318 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x43, 0x18} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_DATAGRAM_DATA_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C4241431A */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x43, 0x1A} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_INBOUND_ICMP_ERROR_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C4241431C */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x43, 0x1C} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_INBOUND_ICMP_ERROR_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C4241431E */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x43, 0x1E} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_OUTBOUND_ICMP_ERROR_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42414320 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x43, 0x20} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_OUTBOUND_ICMP_ERROR_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42414322 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x43, 0x22} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_ALE_RESOURCE_ASSIGNMENT_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42414324 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x43, 0x24} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_ALE_RESOURCE_ASSIGNMENT_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42414326 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x43, 0x26} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_ALE_AUTH_LISTEN_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42414328 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x43, 0x28} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_ALE_AUTH_LISTEN_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C4241432A */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x43, 0x2A} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_ALE_AUTH_RECV_ACCEPT_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C4241432C */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x43, 0x2C} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_ALE_AUTH_RECV_ACCEPT_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C4241432E */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x43, 0x2E} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_ALE_AUTH_CONNECT_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42414330 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x43, 0x30} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_ALE_AUTH_CONNECT_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42414332 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x43, 0x32} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_ALE_FLOW_ESTABLISHED_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42414334 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x43, 0x34} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_ALE_FLOW_ESTABLISHED_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42414336 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x43, 0x36} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_INBOUND_MAC_FRAME_ETHERNET = +{ + /* 53504657-6D61-5F70-4361-6C6C42414338 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x43, 0x38} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_OUTBOUND_MAC_FRAME_ETHERNET = +{ + /* 53504657-6D61-5F70-4361-6C6C42414339 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x43, 0x39} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_INBOUND_MAC_FRAME_NATIVE = +{ + /* 53504657-6D61-5F70-4361-6C6C4241433A */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x43, 0x3A} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_OUTBOUND_MAC_FRAME_NATIVE = +{ + /* 53504657-6D61-5F70-4361-6C6C4241433B */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x43, 0x3B} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_ALE_RESOURCE_RELEASE_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C4241433E */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x43, 0x3E} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_ALE_RESOURCE_RELEASE_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C4241433F */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x43, 0x3F} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_ALE_ENDPOINT_CLOSURE_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42414340 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x43, 0x40} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_ALE_ENDPOINT_CLOSURE_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42414341 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x43, 0x41} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_ALE_CONNECT_REDIRECT_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42414342 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x43, 0x42} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_ALE_CONNECT_REDIRECT_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42414343 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x43, 0x43} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_ALE_BIND_REDIRECT_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42414344 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x43, 0x44} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_ALE_BIND_REDIRECT_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42414345 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x43, 0x45} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_STREAM_PACKET_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42414346 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x43, 0x46} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_STREAM_PACKET_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42414347 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x43, 0x47} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_INGRESS_VSWITCH_ETHERNET = +{ + /* 53504657-6D61-5F70-4361-6C6C42414348 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x43, 0x48} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_EGRESS_VSWITCH_ETHERNET = +{ + /* 53504657-6D61-5F70-4361-6C6C42414349 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x43, 0x49} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_INGRESS_VSWITCH_TRANSPORT_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C4241434A */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x43, 0x4A} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_INGRESS_VSWITCH_TRANSPORT_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C4241434B */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x43, 0x4B} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_EGRESS_VSWITCH_TRANSPORT_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C4241434C */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x43, 0x4C} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_EGRESS_VSWITCH_TRANSPORT_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C4241434D */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x43, 0x4D} +}; + +#endif /// BASIC_ACTION_CONTINUE + +#ifndef BASIC_ACTION_PERMIT +#define BASIC_ACTION_PERMIT + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT = +{ + /* 53504657-6D61-5F70-4361-6C6C424150FF */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x50, 0xFF} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_INBOUND_IPPACKET_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42415000 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x50, 0x00} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_INBOUND_IPPACKET_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42415002 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x50, 0x02} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_OUTBOUND_IPPACKET_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42415004 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x50, 0x04} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_OUTBOUND_IPPACKET_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42415006 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x50, 0x06} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_IPFORWARD_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42415008 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x50, 0x08} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_IPFORWARD_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C4241500A */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x50, 0x0A} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_INBOUND_TRANSPORT_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C4241500C */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x50, 0x0C} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_INBOUND_TRANSPORT_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C4241500E */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x50, 0x0E} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_OUTBOUND_TRANSPORT_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42415010 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x50, 0x10} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_OUTBOUND_TRANSPORT_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42415012 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x50, 0x12} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_STREAM_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42415014 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x50, 0x14} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_STREAM_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42415016 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x50, 0x16} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_DATAGRAM_DATA_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42415018 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x50, 0x18} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_DATAGRAM_DATA_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C4241501A */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x50, 0x1A} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_INBOUND_ICMP_ERROR_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C4241501C */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x50, 0x1C} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_INBOUND_ICMP_ERROR_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C4241501E */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x50, 0x1E} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_OUTBOUND_ICMP_ERROR_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42415020 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x50, 0x20} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_OUTBOUND_ICMP_ERROR_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42415022 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x50, 0x22} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_ALE_RESOURCE_ASSIGNMENT_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42415024 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x50, 0x24} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_ALE_RESOURCE_ASSIGNMENT_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42415026 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x50, 0x26} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_ALE_AUTH_LISTEN_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42415028 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x50, 0x28} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_ALE_AUTH_LISTEN_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C4241502A */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x50, 0x2A} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_ALE_AUTH_RECV_ACCEPT_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C4241502C */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x50, 0x2C} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_ALE_AUTH_RECV_ACCEPT_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C4241502E */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x50, 0x2E} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_ALE_AUTH_CONNECT_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42415030 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x50, 0x30} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_ALE_AUTH_CONNECT_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42415032 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x50, 0x32} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_ALE_FLOW_ESTABLISHED_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42415034 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x50, 0x34} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_ALE_FLOW_ESTABLISHED_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42415036 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x50, 0x36} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_INBOUND_MAC_FRAME_ETHERNET = +{ + /* 53504657-6D61-5F70-4361-6C6C42415038 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x50, 0x38} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_OUTBOUND_MAC_FRAME_ETHERNET = +{ + /* 53504657-6D61-5F70-4361-6C6C42415039 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x50, 0x39} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_INBOUND_MAC_FRAME_NATIVE = +{ + /* 53504657-6D61-5F70-4361-6C6C4241503A */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x50, 0x3A} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_OUTBOUND_MAC_FRAME_NATIVE = +{ + /* 53504657-6D61-5F70-4361-6C6C4241503B */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x50, 0x3B} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_ALE_RESOURCE_RELEASE_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C4241503E */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x50, 0x3E} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_ALE_RESOURCE_RELEASE_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C4241503F */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x50, 0x3F} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_ALE_ENDPOINT_CLOSURE_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42415040 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x50, 0x40} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_ALE_ENDPOINT_CLOSURE_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42415041 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x50, 0x41} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_ALE_CONNECT_REDIRECT_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42415042 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x50, 0x42} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_ALE_CONNECT_REDIRECT_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42415043 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x50, 0x43} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_ALE_BIND_REDIRECT_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42415044 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x50, 0x44} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_ALE_BIND_REDIRECT_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42415045 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x50, 0x45} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_STREAM_PACKET_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42415046 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x50, 0x46} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_STREAM_PACKET_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42415047 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x50, 0x47} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_INGRESS_VSWITCH_ETHERNET = +{ + /* 53504657-6D61-5F70-4361-6C6C42415048 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x50, 0x48} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_EGRESS_VSWITCH_ETHERNET = +{ + /* 53504657-6D61-5F70-4361-6C6C42415049 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x50, 0x49} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_INGRESS_VSWITCH_TRANSPORT_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C4241504A */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x50, 0x4A} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_INGRESS_VSWITCH_TRANSPORT_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C4241504B */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x50, 0x4B} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_EGRESS_VSWITCH_TRANSPORT_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C4241504C */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x50, 0x4C} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_EGRESS_VSWITCH_TRANSPORT_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C4241504D */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x50, 0x4D} +}; + +#endif /// BASIC_ACTION_PERMIT + +#ifndef BASIC_ACTION_RANDOM +#define BASIC_ACTION_RANDOM + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM = +{ + /* 53504657-6D61-5F70-4361-6C6C424152FF */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x52, 0xFF} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_INBOUND_IPPACKET_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42415200 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x52, 0x00} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_INBOUND_IPPACKET_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42415202 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x52, 0x02} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_OUTBOUND_IPPACKET_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42415204 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x52, 0x04} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_OUTBOUND_IPPACKET_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42415206 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x52, 0x06} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_IPFORWARD_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42415208 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x52, 0x08} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_IPFORWARD_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C4241520A */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x52, 0x0A} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_INBOUND_TRANSPORT_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C4241520C */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x52, 0x0C} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_INBOUND_TRANSPORT_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C4241520E */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x52, 0x0E} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_OUTBOUND_TRANSPORT_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42415210 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x52, 0x10} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_OUTBOUND_TRANSPORT_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42415212 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x52, 0x12} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_STREAM_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42415214 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x52, 0x14} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_STREAM_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42415216 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x52, 0x16} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_DATAGRAM_DATA_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42415218 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x52, 0x18} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_DATAGRAM_DATA_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C4241521A */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x52, 0x1A} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_INBOUND_ICMP_ERROR_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C4241521C */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x52, 0x1C} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_INBOUND_ICMP_ERROR_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C4241521E */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x52, 0x1E} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_OUTBOUND_ICMP_ERROR_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42415220 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x52, 0x20} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_OUTBOUND_ICMP_ERROR_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42415222 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x52, 0x22} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_ALE_RESOURCE_ASSIGNMENT_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42415224 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x52, 0x24} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_ALE_RESOURCE_ASSIGNMENT_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42415226 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x52, 0x26} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_ALE_AUTH_LISTEN_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42415228 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x52, 0x28} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_ALE_AUTH_LISTEN_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C4241522A */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x52, 0x2A} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_ALE_AUTH_RECV_ACCEPT_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C4241522C */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x52, 0x2C} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_ALE_AUTH_RECV_ACCEPT_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C4241522E */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x52, 0x2E} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_ALE_AUTH_CONNECT_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42415230 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x52, 0x30} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_ALE_AUTH_CONNECT_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42415232 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x52, 0x32} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_ALE_FLOW_ESTABLISHED_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42415234 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x52, 0x34} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_ALE_FLOW_ESTABLISHED_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42415236 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x52, 0x36} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_INBOUND_MAC_FRAME_ETHERNET = +{ + /* 53504657-6D61-5F70-4361-6C6C42415238 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x52, 0x38} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_OUTBOUND_MAC_FRAME_ETHERNET = +{ + /* 53504657-6D61-5F70-4361-6C6C42415239 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x52, 0x39} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_INBOUND_MAC_FRAME_NATIVE = +{ + /* 53504657-6D61-5F70-4361-6C6C4241523A */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x52, 0x3A} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_OUTBOUND_MAC_FRAME_NATIVE = +{ + /* 53504657-6D61-5F70-4361-6C6C4241523B */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x52, 0x3B} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_ALE_RESOURCE_RELEASE_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C4241523E */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x52, 0x3E} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_ALE_RESOURCE_RELEASE_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C4241523F */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x52, 0x3F} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_ALE_ENDPOINT_CLOSURE_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42415240 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x52, 0x40} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_ALE_ENDPOINT_CLOSURE_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42415241 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x52, 0x41} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_ALE_CONNECT_REDIRECT_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42415242 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x52, 0x42} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_ALE_CONNECT_REDIRECT_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42415243 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x52, 0x43} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_ALE_BIND_REDIRECT_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42415244 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x52, 0x44} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_ALE_BIND_REDIRECT_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42415245 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x52, 0x45} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_STREAM_PACKET_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42415246 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x52, 0x46} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_STREAM_PACKET_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42415247 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x52, 0x47} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_INGRESS_VSWITCH_ETHERNET = +{ + /* 53504657-6D61-5F70-4361-6C6C42415248 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x52, 0x48} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_EGRESS_VSWITCH_ETHERNET = +{ + /* 53504657-6D61-5F70-4361-6C6C42415249 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x52, 0x49} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_INGRESS_VSWITCH_TRANSPORT_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C4241524A */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x52, 0x4A} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_INGRESS_VSWITCH_TRANSPORT_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C4241524B */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x52, 0x4B} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_EGRESS_VSWITCH_TRANSPORT_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C4241524C */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x52, 0x4C} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_EGRESS_VSWITCH_TRANSPORT_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C4241524D */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x41, 0x52, 0x4D} +}; + +#endif /// BASIC_ACTION_RANDOM + +#ifndef BASIC_PACKET_EXAMINATION +#define BASIC_PACKET_EXAMINATION + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION = +{ + /* 53504657-6D61-5F70-4361-6C6C425045FF */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0xFF} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_INBOUND_IPPACKET_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42504500 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x00} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_INBOUND_IPPACKET_V4_DISCARD = +{ + /* 53504657-6D61-5F70-4361-6C6C42504501 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x01} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_INBOUND_IPPACKET_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42504502 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x02} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_INBOUND_IPPACKET_V6_DISCARD = +{ + /* 53504657-6D61-5F70-4361-6C6C42504503 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x03} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_OUTBOUND_IPPACKET_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42504504 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x04} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_OUTBOUND_IPPACKET_V4_DISCARD = +{ + /* 53504657-6D61-5F70-4361-6C6C42504505 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x05} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_OUTBOUND_IPPACKET_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42504506 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x06} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_OUTBOUND_IPPACKET_V6_DISCARD = +{ + /* 53504657-6D61-5F70-4361-6C6C42504507 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x07} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_IPFORWARD_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42504508 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x08} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_IPFORWARD_V4_DISCARD = +{ + /* 53504657-6D61-5F70-4361-6C6C42504509 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x09} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_IPFORWARD_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C4250450A */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x0A} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_IPFORWARD_V6_DISCARD = +{ + /* 53504657-6D61-5F70-4361-6C6C4250450B */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x0B} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_INBOUND_TRANSPORT_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C4250450C */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x0C} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_INBOUND_TRANSPORT_V4_DISCARD = +{ + /* 53504657-6D61-5F70-4361-6C6C4250450D */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x0D} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_INBOUND_TRANSPORT_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C4250450E */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x0E} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_INBOUND_TRANSPORT_V6_DISCARD = +{ + /* 53504657-6D61-5F70-4361-6C6C4250450F */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x0F} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_OUTBOUND_TRANSPORT_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42504510 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x10} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_OUTBOUND_TRANSPORT_V4_DISCARD = +{ + /* 53504657-6D61-5F70-4361-6C6C42504511 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x11} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_OUTBOUND_TRANSPORT_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42504512 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x12} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_OUTBOUND_TRANSPORT_V6_DISCARD = +{ + /* 53504657-6D61-5F70-4361-6C6C42504513 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x13} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_STREAM_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42504514 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x14} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_STREAM_V4_DISCARD = +{ + /* 53504657-6D61-5F70-4361-6C6C42504515 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x15} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_STREAM_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42504516 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x16} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_STREAM_V6_DISCARD = +{ + /* 53504657-6D61-5F70-4361-6C6C42504517 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x17} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_DATAGRAM_DATA_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42504518 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x18} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_DATAGRAM_DATA_V4_DISCARD = +{ + /* 53504657-6D61-5F70-4361-6C6C42504519 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x19} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_DATAGRAM_DATA_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C4250451A */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x1A} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_DATAGRAM_DATA_V6_DISCARD = +{ + /* 53504657-6D61-5F70-4361-6C6C4250451B */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x1B} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_INBOUND_ICMP_ERROR_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C4250451C */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x1C} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_INBOUND_ICMP_ERROR_V4_DISCARD = +{ + /* 53504657-6D61-5F70-4361-6C6C4250451D */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x1D} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_INBOUND_ICMP_ERROR_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C4250451E */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x1E} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_INBOUND_ICMP_ERROR_V6_DISCARD = +{ + /* 53504657-6D61-5F70-4361-6C6C4250451F */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x1F} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_OUTBOUND_ICMP_ERROR_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42504520 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x20} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_OUTBOUND_ICMP_ERROR_V4_DISCARD = +{ + /* 53504657-6D61-5F70-4361-6C6C42504521 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x21} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_OUTBOUND_ICMP_ERROR_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42504522 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x22} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_OUTBOUND_ICMP_ERROR_V6_DISCARD = +{ + /* 53504657-6D61-5F70-4361-6C6C42504523 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x23} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_ALE_RESOURCE_ASSIGNMENT_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42504524 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x24} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_ALE_RESOURCE_ASSIGNMENT_V4_DISCARD = +{ + /* 53504657-6D61-5F70-4361-6C6C42504525 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x25} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_ALE_RESOURCE_ASSIGNMENT_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42504526 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x26} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_ALE_RESOURCE_ASSIGNMENT_V6_DISCARD = +{ + /* 53504657-6D61-5F70-4361-6C6C42504527 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x27} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_ALE_AUTH_LISTEN_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42504528 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x28} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_ALE_AUTH_LISTEN_V4_DISCARD = +{ + /* 53504657-6D61-5F70-4361-6C6C42504529 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x29} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_ALE_AUTH_LISTEN_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C4250452A */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x2A} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_ALE_AUTH_LISTEN_V6_DISCARD = +{ + /* 53504657-6D61-5F70-4361-6C6C4250452B */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x2B} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_ALE_AUTH_RECV_ACCEPT_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C4250452C */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x2C} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_ALE_AUTH_RECV_ACCEPT_V4_DISCARD = +{ + /* 53504657-6D61-5F70-4361-6C6C4250452D */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x2D} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_ALE_AUTH_RECV_ACCEPT_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C4250452E */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x2E} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_ALE_AUTH_RECV_ACCEPT_V6_DISCARD = +{ + /* 53504657-6D61-5F70-4361-6C6C4250452F */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x2F} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_ALE_AUTH_CONNECT_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42504530 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x30} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_ALE_AUTH_CONNECT_V4_DISCARD = +{ + /* 53504657-6D61-5F70-4361-6C6C42504531 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x31} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_ALE_AUTH_CONNECT_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42504532 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x32} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_ALE_AUTH_CONNECT_V6_DISCARD = +{ + /* 53504657-6D61-5F70-4361-6C6C42504533 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x33} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_ALE_FLOW_ESTABLISHED_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42504534 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x34} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_ALE_FLOW_ESTABLISHED_V4_DISCARD = +{ + /* 53504657-6D61-5F70-4361-6C6C42504535 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x35} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_ALE_FLOW_ESTABLISHED_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42504536 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x36} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_ALE_FLOW_ESTABLISHED_V6_DISCARD = +{ + /* 53504657-6D61-5F70-4361-6C6C42504537 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x37} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_INBOUND_MAC_FRAME_ETHERNET = +{ + /* 53504657-6D61-5F70-4361-6C6C42504538 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x38} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_OUTBOUND_MAC_FRAME_ETHERNET = +{ + /* 53504657-6D61-5F70-4361-6C6C42504539 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x39} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_INBOUND_MAC_FRAME_NATIVE = +{ + /* 53504657-6D61-5F70-4361-6C6C4250453A */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x3A} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_OUTBOUND_MAC_FRAME_NATIVE = +{ + /* 53504657-6D61-5F70-4361-6C6C4250453B */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x3B} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_ALE_RESOURCE_RELEASE_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C4250453E */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x3E} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_ALE_RESOURCE_RELEASE_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C4250453F */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x3F} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_ALE_ENDPOINT_CLOSURE_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42504540 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x40} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_ALE_ENDPOINT_CLOSURE_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42504541 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x41} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_ALE_CONNECT_REDIRECT_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42504542 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x42} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_ALE_CONNECT_REDIRECT_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42504543 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x43} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_ALE_BIND_REDIRECT_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42504544 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x44} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_ALE_BIND_REDIRECT_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42504545 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x45} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_STREAM_PACKET_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42504546 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x46} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_STREAM_PACKET_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42504547 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x47} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_INGRESS_VSWITCH_ETHERNET = +{ + /* 53504657-6D61-5F70-4361-6C6C42504548 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x48} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_EGRESS_VSWITCH_ETHERNET = +{ + /* 53504657-6D61-5F70-4361-6C6C42504549 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x49} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_INGRESS_VSWITCH_TRANSPORT_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C4250454A */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x4A} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_INGRESS_VSWITCH_TRANSPORT_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C4250454B */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x4B} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_EGRESS_VSWITCH_TRANSPORT_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C4250454C */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x4C} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_EGRESS_VSWITCH_TRANSPORT_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C4250454D */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x45, 0x4D} +}; + +#endif /// BASIC_PACKET_EXAMINATION + +#ifndef BASIC_PACKET_INJECTION +#define BASIC_PACKET_INJECTION + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION = +{ + /* 53504657-6D61-5F70-4361-6C6C425049FF */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x49, 0xFF} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_INBOUND_IPPACKET_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42504900 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x49, 0x00} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_INBOUND_IPPACKET_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42504902 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x49, 0x02} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_OUTBOUND_IPPACKET_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42504904 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x49, 0x04} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_OUTBOUND_IPPACKET_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42504906 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x49, 0x06} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_IPFORWARD_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42504908 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x49, 0x08} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_IPFORWARD_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C4250490A */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x49, 0x0A} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_INBOUND_TRANSPORT_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C4250490C */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x49, 0x0C} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_INBOUND_TRANSPORT_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C4250490E */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x49, 0x0E} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_OUTBOUND_TRANSPORT_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42504910 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x49, 0x10} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_OUTBOUND_TRANSPORT_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42504912 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x49, 0x12} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_DATAGRAM_DATA_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42504918 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x49, 0x18} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_DATAGRAM_DATA_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C4250491A */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x49, 0x1A} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_INBOUND_ICMP_ERROR_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C4250491C */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x49, 0x1C} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_INBOUND_ICMP_ERROR_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C4250491E */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x49, 0x1E} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_OUTBOUND_ICMP_ERROR_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42504920 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x49, 0x20} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_OUTBOUND_ICMP_ERROR_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42504922 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x49, 0x22} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_ALE_AUTH_RECV_ACCEPT_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C4250492C */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x49, 0x2C} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_ALE_AUTH_RECV_ACCEPT_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C4250492E */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x49, 0x2E} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_ALE_AUTH_CONNECT_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42504930 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x49, 0x30} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_ALE_AUTH_CONNECT_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42504932 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x49, 0x32} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_ALE_FLOW_ESTABLISHED_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42504934 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x49, 0x34} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_ALE_FLOW_ESTABLISHED_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42504936 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x49, 0x36} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_INBOUND_MAC_FRAME_ETHERNET = +{ + /* 53504657-6D61-5F70-4361-6C6C42504938 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x49, 0x38} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_OUTBOUND_MAC_FRAME_ETHERNET = +{ + /* 53504657-6D61-5F70-4361-6C6C42504939 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x49, 0x39} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_INBOUND_MAC_FRAME_NATIVE = +{ + /* 53504657-6D61-5F70-4361-6C6C4250493A */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x49, 0x3A} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_OUTBOUND_MAC_FRAME_NATIVE = +{ + /* 53504657-6D61-5F70-4361-6C6C4250493B */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x49, 0x3B} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_STREAM_PACKET_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42504946 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x49, 0x46} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_STREAM_PACKET_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42504947 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x49, 0x47} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_INGRESS_VSWITCH_ETHERNET = +{ + /* 53504657-6D61-5F70-4361-6C6C42504948 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x49, 0x48} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_EGRESS_VSWITCH_ETHERNET = +{ + /* 53504657-6D61-5F70-4361-6C6C42504949 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x49, 0x49} +}; + +#endif /// BASIC_PACKET_INJECTION + +#ifndef BASIC_PACKET_MODIFICATION +#define BASIC_PACKET_MODIFICATION + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION = +{ + /* 53504657-6D61-5F70-4361-6C6C42504DFF */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x4D, 0xFF} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_INBOUND_IPPACKET_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42504D00 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x4D, 0x00} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_INBOUND_IPPACKET_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42504D02 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x4D, 0x02} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_OUTBOUND_IPPACKET_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42504D04 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x4D, 0x04} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_OUTBOUND_IPPACKET_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42504D06 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x4D, 0x06} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_IPFORWARD_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42504D08 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x4D, 0x08} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_IPFORWARD_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42504D0A */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x4D, 0x0A} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_INBOUND_TRANSPORT_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42504D0C */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x4D, 0x0C} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_INBOUND_TRANSPORT_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42504D0E */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x4D, 0x0E} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_OUTBOUND_TRANSPORT_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42504D10 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x4D, 0x10} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_OUTBOUND_TRANSPORT_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42504D12 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x4D, 0x12} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_DATAGRAM_DATA_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42504D18 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x4D, 0x18} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_DATAGRAM_DATA_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42504D1A */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x4D, 0x1A} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_INBOUND_ICMP_ERROR_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42504D1C */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x4D, 0x1C} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_INBOUND_ICMP_ERROR_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42504D1E */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x4D, 0x1E} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_OUTBOUND_ICMP_ERROR_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42504D20 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x4D, 0x20} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_OUTBOUND_ICMP_ERROR_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42504D22 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x4D, 0x22} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_ALE_AUTH_RECV_ACCEPT_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42504D2C */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x4D, 0x2C} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_ALE_AUTH_RECV_ACCEPT_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42504D2E */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x4D, 0x2E} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_ALE_AUTH_CONNECT_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42504D30 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x4D, 0x30} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_ALE_AUTH_CONNECT_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42504D32 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x4D, 0x32} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_ALE_FLOW_ESTABLISHED_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42504D34 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x4D, 0x34} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_ALE_FLOW_ESTABLISHED_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42504D36 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x4D, 0x36} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_INBOUND_MAC_FRAME_ETHERNET = +{ + /* 53504657-6D61-5F70-4361-6C6C42504D38 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x4D, 0x38} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_OUTBOUND_MAC_FRAME_ETHERNET = +{ + /* 53504657-6D61-5F70-4361-6C6C42504D39 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x4D, 0x39} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_INBOUND_MAC_FRAME_NATIVE = +{ + /* 53504657-6D61-5F70-4361-6C6C42504D3A */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x4D, 0x3A} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_OUTBOUND_MAC_FRAME_NATIVE = +{ + /* 53504657-6D61-5F70-4361-6C6C42504D3B */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x4D, 0x3B} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_STREAM_PACKET_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42504D46 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x4D, 0x46} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_STREAM_PACKET_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42504D47 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x4D, 0x47} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_INGRESS_VSWITCH_ETHERNET = +{ + /* 53504657-6D61-5F70-4361-6C6C42504D48 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x4D, 0x48} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_EGRESS_VSWITCH_ETHERNET = +{ + /* 53504657-6D61-5F70-4361-6C6C42504D49 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x50, 0x4D, 0x49} +}; + +#endif /// BASIC_PACKET_MODIFICATION + +#ifndef BASIC_STREAM_INJECTION +#define BASIC_STREAM_INJECTION + +static const GUID WFPSAMPLER_CALLOUT_BASIC_STREAM_INJECTION = +{ + /* 53504657-6D61-5F70-4361-6C6C425349FF */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x53, 0x49, 0xFF} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_STREAM_INJECTION_AT_STREAM_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C42534914 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x53, 0x49, 0x14} +}; + +static const GUID WFPSAMPLER_CALLOUT_BASIC_STREAM_INJECTION_AT_STREAM_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C42534916 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x42, 0x53, 0x49, 0x16} +}; + +#endif /// BASIC_STREAM_INJECTION + +#ifndef FAST_PACKET_INJECTION +#define FAST_PACKET_INJECTION + +static const GUID WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION = +{ + /* 53504657-6D61-5F70-4361-6C6C465049FF */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x46, 0x50, 0x49, 0xFF} +}; + +static const GUID WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_INBOUND_IPPACKET_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C46504900 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x46, 0x50, 0x49, 0x00} +}; + +static const GUID WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_INBOUND_IPPACKET_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C46504902 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x46, 0x50, 0x49, 0x02} +}; + +static const GUID WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_OUTBOUND_IPPACKET_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C46504904 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x46, 0x50, 0x49, 0x04} +}; + +static const GUID WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_OUTBOUND_IPPACKET_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C46504906 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x46, 0x50, 0x49, 0x06} +}; + +static const GUID WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_IPFORWARD_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C46504908 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x46, 0x50, 0x49, 0x08} +}; + +static const GUID WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_IPFORWARD_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C4650490A */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x46, 0x50, 0x49, 0x0A} +}; + +static const GUID WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_INBOUND_TRANSPORT_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C4650490C */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x46, 0x50, 0x49, 0x0C} +}; + +static const GUID WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_INBOUND_TRANSPORT_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C4650490E */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x46, 0x50, 0x49, 0x0E} +}; + +static const GUID WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_OUTBOUND_TRANSPORT_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C46504910 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x46, 0x50, 0x49, 0x10} +}; + +static const GUID WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_OUTBOUND_TRANSPORT_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C46504912 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x46, 0x50, 0x49, 0x12} +}; + +static const GUID WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_DATAGRAM_DATA_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C46504918 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x46, 0x50, 0x49, 0x18} +}; + +static const GUID WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_DATAGRAM_DATA_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C4650491A */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x46, 0x50, 0x49, 0x1A} +}; + +static const GUID WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_INBOUND_ICMP_ERROR_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C4650491C */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x46, 0x50, 0x49, 0x1C} +}; + +static const GUID WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_INBOUND_ICMP_ERROR_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C4650491E */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x46, 0x50, 0x49, 0x1E} +}; + +static const GUID WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_OUTBOUND_ICMP_ERROR_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C46504920 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x46, 0x50, 0x49, 0x20} +}; + +static const GUID WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_OUTBOUND_ICMP_ERROR_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C46504922 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x46, 0x50, 0x49, 0x22} +}; + +static const GUID WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_ALE_AUTH_RECV_ACCEPT_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C4650492C */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x46, 0x50, 0x49, 0x2C} +}; + +static const GUID WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_ALE_AUTH_RECV_ACCEPT_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C4650492E */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x46, 0x50, 0x49, 0x2E} +}; + +static const GUID WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_ALE_AUTH_CONNECT_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C46504930 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x46, 0x50, 0x49, 0x30} +}; + +static const GUID WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_ALE_AUTH_CONNECT_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C46504932 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x46, 0x50, 0x49, 0x32} +}; + +static const GUID WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_ALE_FLOW_ESTABLISHED_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C46504934 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x46, 0x50, 0x49, 0x34} +}; + +static const GUID WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_ALE_FLOW_ESTABLISHED_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C46504936 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x46, 0x50, 0x49, 0x36} +}; + +static const GUID WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_INBOUND_MAC_FRAME_ETHERNET = +{ + /* 53504657-6D61-5F70-4361-6C6C46504938 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x46, 0x50, 0x49, 0x38} +}; + +static const GUID WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_OUTBOUND_MAC_FRAME_ETHERNET = +{ + /* 53504657-6D61-5F70-4361-6C6C46504939 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x46, 0x50, 0x49, 0x39} +}; + +static const GUID WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_INBOUND_MAC_FRAME_NATIVE = +{ + /* 53504657-6D61-5F70-4361-6C6C4650493A */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x46, 0x50, 0x49, 0x3A} +}; + +static const GUID WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_OUTBOUND_MAC_FRAME_NATIVE = +{ + /* 53504657-6D61-5F70-4361-6C6C4650493B */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x46, 0x50, 0x49, 0x3B} +}; + +static const GUID WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_STREAM_PACKET_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C46504946 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x46, 0x50, 0x49, 0x46} +}; + +static const GUID WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_STREAM_PACKET_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C46504947 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x46, 0x50, 0x49, 0x47} +}; + +static const GUID WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_INGRESS_VSWITCH_ETHERNET = +{ + /* 53504657-6D61-5F70-4361-6C6C46504948 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x46, 0x50, 0x49, 0x48} +}; + +static const GUID WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_EGRESS_VSWITCH_ETHERNET = +{ + /* 53504657-6D61-5F70-4361-6C6C46504949 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x46, 0x50, 0x49, 0x49} +}; + +#endif /// FAST_PACKET_INJECTION + +#ifndef FAST_STREAM_INJECTION +#define FAST_STREAM_INJECTION + +static const GUID WFPSAMPLER_CALLOUT_FAST_STREAM_INJECTION = +{ + /* 53504657-6D61-5F70-4361-6C6C465349FF */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x46, 0x53, 0x49, 0xFF} +}; + +static const GUID WFPSAMPLER_CALLOUT_FAST_STREAM_INJECTION_AT_STREAM_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C46534914 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x46, 0x53, 0x49, 0x14} +}; + +static const GUID WFPSAMPLER_CALLOUT_FAST_STREAM_INJECTION_AT_STREAM_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C46534916 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x46, 0x53, 0x49, 0x16} +}; + +#endif /// FAST_STREAM_INJECTION + +#ifndef FLOW_ASSOCIATION +#define FLOW_ASSOCIATION + +static const GUID WFPSAMPLER_CALLOUT_FLOW_ASSOCIATION = +{ + /* 53504657-6D61-5F70-4361-6C6C464100FF */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x46, 0x41, 0x00, 0xFF} +}; + +static const GUID WFPSAMPLER_CALLOUT_FLOW_ASSOCIATION_AT_ALE_FLOW_ESTABLISHED_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C46410034 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x46, 0x41, 0x00, 0x34} +}; + +static const GUID WFPSAMPLER_CALLOUT_FLOW_ASSOCIATION_AT_ALE_FLOW_ESTABLISHED_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C46410036 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x46, 0x41, 0x00, 0x36} +}; + +#endif /// FLOW_ASSOCIATION + +#ifndef PEND_AUTHORIZATION +#define PEND_AUTHORIZATION + +static const GUID WFPSAMPLER_CALLOUT_PEND_AUTHORIZATION = +{ + /* 53504657-6D61-5F70-4361-6C6C504100FF */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x50, 0x41, 0x00, 0xFF} +}; + +static const GUID WFPSAMPLER_CALLOUT_PEND_AUTHORIZATION_AT_ALE_RESOURCE_ASSIGNMENT_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C50410024 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x50, 0x41, 0x00, 0x24} +}; + +static const GUID WFPSAMPLER_CALLOUT_PEND_AUTHORIZATION_AT_ALE_RESOURCE_ASSIGNMENT_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C50410026 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x50, 0x41, 0x00, 0x26} +}; + +static const GUID WFPSAMPLER_CALLOUT_PEND_AUTHORIZATION_AT_ALE_AUTH_LISTEN_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C50410028 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x50, 0x41, 0x00, 0x28} +}; + +static const GUID WFPSAMPLER_CALLOUT_PEND_AUTHORIZATION_AT_ALE_AUTH_LISTEN_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C5041002A */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x50, 0x41, 0x00, 0x2A} +}; + +static const GUID WFPSAMPLER_CALLOUT_PEND_AUTHORIZATION_AT_ALE_AUTH_RECV_ACCEPT_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C5041002C */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x50, 0x41, 0x00, 0x2C} +}; + +static const GUID WFPSAMPLER_CALLOUT_PEND_AUTHORIZATION_AT_ALE_AUTH_RECV_ACCEPT_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C5041002E */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x50, 0x41, 0x00, 0x2E} +}; + +static const GUID WFPSAMPLER_CALLOUT_PEND_AUTHORIZATION_AT_ALE_AUTH_CONNECT_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C50410030 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x50, 0x41, 0x00, 0x30} +}; + +static const GUID WFPSAMPLER_CALLOUT_PEND_AUTHORIZATION_AT_ALE_AUTH_CONNECT_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C50410032 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x50, 0x41, 0x00, 0x32} +}; + +#endif /// PEND_AUTHORIZATION + +#ifndef PEND_ENDPOINT_CLOSURE +#define PEND_ENDPOINT_CLOSURE + +static const GUID WFPSAMPLER_CALLOUT_PEND_ENDPOINT_CLOSURE = +{ + /* 53504657-6D61-5F70-4361-6C6C504543FF */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x50, 0x45, 0x43, 0xFF} +}; + +static const GUID WFPSAMPLER_CALLOUT_PEND_ENDPOINT_CLOSURE_AT_ALE_ENDPOINT_CLOSURE_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C50454340 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x50, 0x45, 0x43, 0x40} +}; + +static const GUID WFPSAMPLER_CALLOUT_PEND_ENDPOINT_CLOSURE_AT_ALE_ENDPOINT_CLOSURE_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C50454341 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x50, 0x45, 0x43, 0x41} +}; + +#endif /// PEND_ENDPOINT_CLOSURE + +#ifndef PROXY_BY_INJECTION +#define PROXY_BY_INJECTION + +static const GUID WFPSAMPLER_CALLOUT_PROXY_BY_INJECTION = +{ + /* 53504657-6D61-5F70-4361-6C6C504249FF */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x50, 0x42, 0x49, 0xFF} +}; + +static const GUID WFPSAMPLER_CALLOUT_PROXY_BY_INJECTION_AT_INBOUND_IPPACKET_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C50424900 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x50, 0x42, 0x49, 0x00} +}; + +static const GUID WFPSAMPLER_CALLOUT_PROXY_BY_INJECTION_AT_INBOUND_IPPACKET_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C50424902 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x50, 0x42, 0x49, 0x02} +}; + +static const GUID WFPSAMPLER_CALLOUT_PROXY_BY_INJECTION_AT_OUTBOUND_TRANSPORT_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C50424910 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x50, 0x42, 0x49, 0x10} +}; + +static const GUID WFPSAMPLER_CALLOUT_PROXY_BY_INJECTION_AT_OUTBOUND_TRANSPORT_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C50424912 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x50, 0x42, 0x49, 0x12} +}; + +#endif /// PROXY_BY_INJECTION + +#ifndef PROXY_BY_ALE_REDIRECT +#define PROXY_BY_ALE_REDIRECT + +static const GUID WFPSAMPLER_CALLOUT_PROXY_BY_ALE_REDIRECT = +{ + /* 53504657-6D61-5F70-4361-6C6C504241FF */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x50, 0x42, 0x41, 0xFF} +}; + +static const GUID WFPSAMPLER_CALLOUT_PROXY_BY_ALE_AT_CONNECT_REDIRECT_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C50424142 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x50, 0x42, 0x41, 0x42} +}; + +static const GUID WFPSAMPLER_CALLOUT_PROXY_BY_ALE_AT_CONNECT_REDIRECT_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C50424143 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x50, 0x42, 0x41, 0x43} +}; + +static const GUID WFPSAMPLER_CALLOUT_PROXY_BY_ALE_AT_BIND_REDIRECT_V4 = +{ + /* 53504657-6D61-5F70-4361-6C6C50424144 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x50, 0x42, 0x41, 0x44} +}; + +static const GUID WFPSAMPLER_CALLOUT_PROXY_BY_ALE_AT_BIND_REDIRECT_V6 = +{ + /* 53504657-6D61-5F70-4361-6C6C50424145 */ + 0x53504657, + 0x6D61, + 0x5F70, + {0x43, 0x61, 0x6C, 0x6C, 0x50, 0x42, 0x41, 0x45} +}; + +#endif /// PROXY_BY_ALE_REDIRECT + +#endif /// WFP_SAMPLER_IDENTIFIERS_H diff --git a/network/trans/WFPSampler/inc/ProviderContexts.h b/network/trans/WFPSampler/inc/ProviderContexts.h new file mode 100644 index 000000000..88b56eb1b --- /dev/null +++ b/network/trans/WFPSampler/inc/ProviderContexts.h @@ -0,0 +1,229 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// ProviderContexts.h +// +// Abstract: +// This module contains global definitions of FWPM_PROVIDER_CONTEXT Data for the WFPSampler +// project +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Add ADVANCED_PACKET_INJECTION, FLOW_ASSOCIATION, and +// PEND_ENDPOINT_CLOSURE scenarios. Enhancements for +// BASIC_PACKET_EXAMINATION, BASIC_PACKET_MODIFICATION, +// and PROXY providerContexts. +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef WFP_SAMPLER_PROVIDER_CONTEXT_H +#define WFP_SAMPLER_PROVIDER_CONTEXT_H + +/// ProviderContext ProxyData Flags +#define PCPDF_PROXY_LOCAL_ADDRESS 0x01 +#define PCPDF_PROXY_LOCAL_PORT 0x02 +#define PCPDF_PROXY_REMOTE_ADDRESS 0x04 +#define PCPDF_PROXY_REMOTE_PORT 0x08 + +/// ProviderContext PacketExamination Flags +#define PCPEF_EXAMINE_UNDER_LOCK 0x00000001 +#define PCPEF_EXAMINE_INCOMING_VALUES 0x00000010 +#define PCPEF_EXAMINE_INCOMING_METADATA_VALUES 0x00000100 +#define PCPEF_EXAMINE_LAYER_DATA 0x00001000 +#define PCPEF_EXAMINE_CLASSIFY_CONTEXT 0x00010000 +#define PCPEF_EXAMINE_FILTER 0x00100000 +#define PCPEF_EXAMINE_FLOW_CONTEXT 0x01000000 +#define PCPEF_EXAMINE_CLASSIFY_OUT 0x10000000 + +///ProviderContext PacketModificationData Flags +#define PCPMDF_MODIFY_MAC_HEADER 0x10 +#define PCPMDF_MODIFY_MAC_HEADER_SOURCE_ADDRESS 0x01 +#define PCPMDF_MODIFY_MAC_HEADER_DESTINATION_ADDRESS 0x02 + +#define PCPMDF_MODIFY_IP_HEADER 0x20 +#define PCPMDF_MODIFY_IP_HEADER_SOURCE_ADDRESS 0x01 +#define PCPMDF_MODIFY_IP_HEADER_DESTINATION_ADDRESS 0x02 +#define PCPMDF_MODIFY_IP_HEADER_INTERFACE_INDEX 0x04 + +#define PCPMDF_MODIFY_TRANSPORT_HEADER 0x40 +#define PCPMDF_MODIFY_TRANSPORT_HEADER_SOURCE_PORT 0x01 +#define PCPMDF_MODIFY_TRANSPORT_HEADER_DESTINATION_PORT 0x02 + +#define PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_TYPE PCPMDF_MODIFY_TRANSPORT_HEADER_SOURCE_PORT +#define PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_CODE PCPMDF_MODIFY_TRANSPORT_HEADER_DESTINATION_PORT + +#define PCFA_MAX_COUNT 7 + +typedef struct MAC_DATA_ +{ + UINT32 flags; + BYTE pReserved[4]; + BYTE pSourceMACAddress[8]; + BYTE pDestinationMACAddress[8]; +}MAC_DATA; + +typedef struct IP_DATA_ +{ + UINT32 flags; + BYTE pReserved[3]; + UINT8 ipVersion; + union + { + BYTE pIPv4[4]; /// Network Byte Order + BYTE pIPv6[16]; + BYTE pBytes[16]; + }sourceAddress; + union + { + BYTE pIPv4[4]; /// Network Byte Order + BYTE pIPv6[16]; + BYTE pBytes[16]; + }destinationAddress; + UINT32 interfaceIndex; +}IP_DATA; + +typedef struct TRANSPORT_DATA_ +{ + UINT32 flags; + BYTE pReserved[3]; + UINT8 protocol; + union + { + UINT8 icmpType; + UINT16 sourcePort; /// Network Byte Order + }; + union + { + UINT8 icmpCode; + UINT16 destinationPort; /// Network Byte Order + }; + BYTE pPadding[4]; +} TRANSPORT_DATA; + +typedef struct PC_ADVANCED_PACKET_INJECTION_DATA_ +{ + BOOLEAN performInline; /// Inline vs. Out of Band + BOOLEAN useWorkItems; /// Work Items vs. Deferred Procedure Calls + BOOLEAN useThreadedDPC; /// Threaded DPCs vs Deferred Procedure Calls + UINT32 additionalBytes; + BYTE pReserved[1]; +} PC_ADVANCED_PACKET_INJECTION_DATA, *PPC_ADVANCED_PACKET_INJECTION_DATA; + +typedef struct PC_BASIC_ACTION_DATA_ +{ + UINT8 percentBlock; + UINT8 percentPermit; + UINT8 percentContinue; + BYTE pReserved[5]; +} PC_BASIC_ACTION_DATA, *PPC_BASIC_ACTION_DATA; + +typedef struct PC_BASIC_PACKET_INJECTION_DATA_ +{ + BOOLEAN performInline; /// Inline vs. Out of Band + BOOLEAN useWorkItems; /// Work Items vs. Deferred Procedure Calls + BOOLEAN useThreadedDPC; /// Threaded DPCs vs Deferred Procedure Calls + BYTE pReserved[5]; +} PC_BASIC_PACKET_INJECTION_DATA, *PPC_BASIC_PACKET_INJECTION_DATA; + +typedef struct PC_BASIC_PACKET_MODIFICATION_DATA_ +{ + UINT32 flags; + BOOLEAN performInline; /// Inline vs. Out of Band + BOOLEAN useWorkItems; /// Work Items vs. Deferred Procedure Calls + BOOLEAN useThreadedDPC; /// Threaded DPCs vs Deferred Procedure Calls + BYTE pReserved[1]; + MAC_DATA macData; + IP_DATA ipData; + TRANSPORT_DATA transportData; + MAC_DATA originalMACData; + IP_DATA originalIPData; + TRANSPORT_DATA originalTransportData; /// Used to uniquely identify traffic at the Network layers +}PC_BASIC_PACKET_MODIFICATION_DATA; + +typedef struct PC_BASIC_STREAM_INJECTION_DATA_ +{ + BOOLEAN performInline; /// Inline vs. Out of Band + BOOLEAN useWorkItems; /// Work Items vs. Deferred Procedure Calls + BOOLEAN useThreadedDPC; /// Threaded DPCs vs Deferred Procedure Calls + BYTE pReserved[5]; +} PC_BASIC_STREAM_INJECTION_DATA, *PPC_BASIC_STREAM_INJECTION_DATA; + +typedef struct PC_FLOW_ASSOCIATION_DATA_ +{ + UINT16 itemCount; + UINT16 pLayerIDs[PCFA_MAX_COUNT]; + UINT32 pCalloutIDs[PCFA_MAX_COUNT]; +}PC_FLOW_ASSOCIATION_DATA,*PPC_FLOW_ASSOCIATION_DATA; + +typedef struct PC_PEND_AUTHORIZATION_DATA_ +{ + UINT32 flags; + BOOLEAN useWorkItems; /// Work Items vs. Deferred Procedure Calls + BOOLEAN useThreadedDPC; /// Threaded DPCs vs Deferred Procedure Calls + BYTE pReserved[2]; + UINT32 finalAction; + UINT32 delay; +}PC_PEND_AUTHORIZATION_DATA, *PPC_PEND_AUTHORIZATION_DATA; + +typedef struct PC_PEND_ENDPOINT_CLOSURE_DATA_ +{ + UINT32 flags; + BOOLEAN useWorkItems; /// Work Items vs. Deferred Procedure Calls + BOOLEAN useThreadedDPC; /// Threaded DPCs vs Deferred Procedure Calls + BYTE pReserved[2]; + UINT32 delay; +}PC_PEND_ENDPOINT_CLOSURE_DATA, *PPC_PEND_ENDPOINT_CLOSURE_DATA; + +typedef struct PC_PROXY_DATA_ +{ + UINT32 flags; + BOOLEAN performInline; /// Inline vs. Out of Band + BOOLEAN useWorkItems; /// Work Items vs. Deferred Procedure Calls + BOOLEAN useThreadedDPC; /// Threaded DPCs vs Deferred Procedure Calls + BOOLEAN proxyToRemoteService; /// Local vs. Remote Service + BYTE pReserved[6]; + UINT8 ipVersion; + UINT8 ipProtocol; + union + { + BYTE pIPv4[4]; /// Network Byte Order + BYTE pIPv6[16]; + BYTE pBytes[16]; + }proxyLocalAddress; + union + { + BYTE pIPv4[4]; /// Network Byte Order + BYTE pIPv6[16]; + BYTE pBytes[16]; + }proxyRemoteAddress; + union + { + BYTE pIPv4[4]; /// Network Byte Order + BYTE pIPv6[16]; + BYTE pBytes[16]; + }originalLocalAddress; + union + { + BYTE pIPv4[4]; /// Network Byte Order + BYTE pIPv6[16]; + BYTE pBytes[16]; + }originalRemoteAddress; + UINT32 localScopeId; + UINT32 remoteScopeId; + UINT16 proxyLocalPort; /// Network Byte Order + UINT16 proxyRemotePort; /// Network Byte Order + UINT16 originalLocalPort; /// Network Byte Order + UINT16 originalRemotePort; /// Network Byte Order + UINT32 targetProcessID; + UINT64 tcpPortReservationToken; + UINT64 udpPortReservationToken; +} PC_PROXY_DATA, *PPC_PROXY_DATA; + +#endif /// WFP_SAMPLER_PROVIDER_CONTEXT_H diff --git a/network/trans/WFPSampler/inc/ScenarioData.h b/network/trans/WFPSampler/inc/ScenarioData.h new file mode 100644 index 000000000..7201d2c4d --- /dev/null +++ b/network/trans/WFPSampler/inc/ScenarioData.h @@ -0,0 +1,76 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// ScenarioData.h +// +// Abstract: +// This module contains global definitions of Scenario specific data for the WFPSampler project +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef WFP_SAMPLER_SCENARIO_DATA_H +#define WFP_SAMPLER_SCENARIO_DATA_H + +#define IEEE_802_ADDRESS_LENGTH 6 +#define IEEE_802_ADDRESS_STRING_BUFFER 17 + +/// FRAME_DATA Flags +#define FDF_INTERFACE_MAC_ADDRESS_SET 0x00000001 +#define FDF_LOCAL_MAC_ADDRESS_SET 0x00000002 +#define FDF_REMOTE_MAC_ADDRESS_SET 0x00000004 +#define FDF_ETHER_TYPE_SET 0x00000008 +#define FDF_VLAN_ID_SET 0x00000010 +#define FDF_NDIS_PORT_SET 0x00000020 +#define FDF_NDIS_MEDIA_TYPE_SET 0x00000040 +#define FDF_NDIS_PHYSICAL_MEDIA_TYPE_SET 0x00000080 +#define FDF_L2_FLAGS_SET 0x00000100 +#define FDF_LOCAL_MAC_ADDRESS_TYPE_SET 0x00000200 +#define FDF_REMOTE_MAC_ADDRESS_TYPE_SET 0x00000400 +#define FDF_SOURCE_MAC_ADDRESS_SET 0x00000800 +#define FDF_DESTINATION_MAC_ADDRESS_SET 0x00001000 +#define FDF_SOURCE_MAC_ADDRESS_TYPE_SET 0x00002000 +#define FDF_DESTINATION_MAC_ADDRESS_TYPE_SET 0x00004000 + +/// VSWITCH_DATA Flags +#define VSDF_ID_SET 0x00000001 +#define VSDF_NETWORK_TYPE_SET 0x00000002 +#define VSDF_SOURCE_INTERFACE_ID_SET 0x00000004 +#define VSDF_DESTINATION_INTERFACE_ID_SET 0x00000008 +#define VSDF_SOURCE_INTERFACE_TYPE_SET 0x00000010 +#define VSDF_DESTINATION_INTERFACE_TYPE_SET 0x00000020 +#define VSDF_SOURCE_VM_ID_SET 0x00000040 +#define VSDF_DESTINATION_VM_ID_SET 0x00000080 + +/// PACKET_DATA Flags +#define PDF_VERSION_SET 0x00000001 +#define PDF_PROTOCOL_SET 0x00000002 +#define PDF_LOCAL_ADDRESS_SET 0x00000004 +#define PDF_LOCAL_PORT_SET 0x00000008 +#define PDF_REMOTE_ADDRESS_SET 0x00000010 +#define PDF_REMOTE_PORT_SET 0x00000020 +#define PDF_LOCAL_ADDRESS_TYPE_SET 0x00000040 +#define PDF_DESTINATION_ADDRESS_TYPE_SET 0x00000080 +#define PDF_DIRECTION_SET 0x00000100 +#define PDF_FLAGS_SET 0x00000200 +#define PDF_NEXTHOP_ADDRESS_SET 0x00000400 +#define PDF_ORIGINAL_ICMP_TYPE_SET 0x00000800 +#define PDF_EMBEDDED_LOCAL_ADDRESS_TYPE_SET 0x00001000 +#define PDF_EMBEDDED_REMOTE_ADDRESS_SET 0x00002000 +#define PDF_EMBEDDED_PROTOCOL_SET 0x00004000 +#define PDF_EMBEDDED_LOCAL_PORT_SET 0x00008000 +#define PDF_EMBEDDED_REMOTE_PORT_SET 0x00010000 + +#define PDF_ICMP_TYPE_SET PDF_LOCAL_PORT_SET +#define PDF_ICMP_CODE_SET PDF_REMOTE_PORT_SET + +#endif /// WFP_SAMPLER_SCENARIO_DATA_H \ No newline at end of file diff --git a/network/trans/WFPSampler/inc/WFPArrays.h b/network/trans/WFPSampler/inc/WFPArrays.h new file mode 100644 index 000000000..f4f8deb2a --- /dev/null +++ b/network/trans/WFPSampler/inc/WFPArrays.h @@ -0,0 +1,925 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// WFPArrays.h +// +// Abstract: +// This module contains global variables and definitions of WFP specific data for the +// WFPSampler project +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef WFP_SAMPLER_WFP_ARRAYS_H +#define WFP_SAMPLER_WFP_ARRAYS_H + +#if(NTDDI_VERSION >= NTDDI_VISTA) + +const GUID* const ppLayerKeyArray[] = {&FWPM_LAYER_INBOUND_IPPACKET_V4, /// 0 + &FWPM_LAYER_INBOUND_IPPACKET_V4_DISCARD, + &FWPM_LAYER_INBOUND_IPPACKET_V6, + &FWPM_LAYER_INBOUND_IPPACKET_V6_DISCARD, + &FWPM_LAYER_OUTBOUND_IPPACKET_V4, + &FWPM_LAYER_OUTBOUND_IPPACKET_V4_DISCARD, + &FWPM_LAYER_OUTBOUND_IPPACKET_V6, + &FWPM_LAYER_OUTBOUND_IPPACKET_V6_DISCARD, + &FWPM_LAYER_IPFORWARD_V4, + &FWPM_LAYER_IPFORWARD_V4_DISCARD, + &FWPM_LAYER_IPFORWARD_V6, /// 10 + &FWPM_LAYER_IPFORWARD_V6_DISCARD, + &FWPM_LAYER_INBOUND_TRANSPORT_V4, + &FWPM_LAYER_INBOUND_TRANSPORT_V4_DISCARD, + &FWPM_LAYER_INBOUND_TRANSPORT_V6, + &FWPM_LAYER_INBOUND_TRANSPORT_V6_DISCARD, + &FWPM_LAYER_OUTBOUND_TRANSPORT_V4, + &FWPM_LAYER_OUTBOUND_TRANSPORT_V4_DISCARD, + &FWPM_LAYER_OUTBOUND_TRANSPORT_V6, + &FWPM_LAYER_OUTBOUND_TRANSPORT_V6_DISCARD, + &FWPM_LAYER_STREAM_V4, /// 20 + &FWPM_LAYER_STREAM_V4_DISCARD, + &FWPM_LAYER_STREAM_V6, + &FWPM_LAYER_STREAM_V6_DISCARD, + &FWPM_LAYER_DATAGRAM_DATA_V4, + &FWPM_LAYER_DATAGRAM_DATA_V4_DISCARD, + &FWPM_LAYER_DATAGRAM_DATA_V6, + &FWPM_LAYER_DATAGRAM_DATA_V6_DISCARD, + &FWPM_LAYER_INBOUND_ICMP_ERROR_V4, + &FWPM_LAYER_INBOUND_ICMP_ERROR_V4_DISCARD, + &FWPM_LAYER_INBOUND_ICMP_ERROR_V6, /// 30 + &FWPM_LAYER_INBOUND_ICMP_ERROR_V6_DISCARD, + &FWPM_LAYER_OUTBOUND_ICMP_ERROR_V4, + &FWPM_LAYER_OUTBOUND_ICMP_ERROR_V4_DISCARD, + &FWPM_LAYER_OUTBOUND_ICMP_ERROR_V6, + &FWPM_LAYER_OUTBOUND_ICMP_ERROR_V6_DISCARD, + &FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V4, + &FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V4_DISCARD, + &FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V6, + &FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V6_DISCARD, + &FWPM_LAYER_ALE_AUTH_LISTEN_V4, /// 40 + &FWPM_LAYER_ALE_AUTH_LISTEN_V4_DISCARD, + &FWPM_LAYER_ALE_AUTH_LISTEN_V6, + &FWPM_LAYER_ALE_AUTH_LISTEN_V6_DISCARD, + &FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4, + &FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4_DISCARD, + &FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6, + &FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6_DISCARD, + &FWPM_LAYER_ALE_AUTH_CONNECT_V4, + &FWPM_LAYER_ALE_AUTH_CONNECT_V4_DISCARD, + &FWPM_LAYER_ALE_AUTH_CONNECT_V6, /// 50 + &FWPM_LAYER_ALE_AUTH_CONNECT_V6_DISCARD, + &FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4, + &FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4_DISCARD, + &FWPM_LAYER_ALE_FLOW_ESTABLISHED_V6, + &FWPM_LAYER_ALE_FLOW_ESTABLISHED_V6_DISCARD, +#if(NTDDI_VERSION >= NTDDI_WIN7) + + &FWPM_LAYER_NAME_RESOLUTION_CACHE_V4, /// 60 + &FWPM_LAYER_NAME_RESOLUTION_CACHE_V6, + &FWPM_LAYER_ALE_RESOURCE_RELEASE_V4, + &FWPM_LAYER_ALE_RESOURCE_RELEASE_V6, + &FWPM_LAYER_ALE_ENDPOINT_CLOSURE_V4, + &FWPM_LAYER_ALE_ENDPOINT_CLOSURE_V6, + &FWPM_LAYER_ALE_CONNECT_REDIRECT_V4, + &FWPM_LAYER_ALE_CONNECT_REDIRECT_V6, + &FWPM_LAYER_ALE_BIND_REDIRECT_V4, + &FWPM_LAYER_ALE_BIND_REDIRECT_V6, + &FWPM_LAYER_STREAM_PACKET_V4, /// 70 + &FWPM_LAYER_STREAM_PACKET_V6, + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + &FWPM_LAYER_INBOUND_MAC_FRAME_ETHERNET, /// 56 + &FWPM_LAYER_OUTBOUND_MAC_FRAME_ETHERNET, /// 57 + &FWPM_LAYER_INBOUND_MAC_FRAME_NATIVE, /// 58 + &FWPM_LAYER_OUTBOUND_MAC_FRAME_NATIVE, /// 59 + &FWPM_LAYER_INGRESS_VSWITCH_ETHERNET, /// 72 + &FWPM_LAYER_EGRESS_VSWITCH_ETHERNET, + &FWPM_LAYER_INGRESS_VSWITCH_TRANSPORT_V4, + &FWPM_LAYER_INGRESS_VSWITCH_TRANSPORT_V6, + &FWPM_LAYER_EGRESS_VSWITCH_TRANSPORT_V4, + &FWPM_LAYER_EGRESS_VSWITCH_TRANSPORT_V6, + +#if(NTDDI_VERSION >= NTDDI_WINBLUE) + + &FWPM_LAYER_INBOUND_TRANSPORT_FAST, /// 78 + &FWPM_LAYER_OUTBOUND_TRANSPORT_FAST, + &FWPM_LAYER_INBOUND_MAC_FRAME_NATIVE_FAST, /// 80 + &FWPM_LAYER_OUTBOUND_MAC_FRAME_NATIVE_FAST, + + +#endif /// (NTDDI_VERSION >= NTDDI_WINBLUE) +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + &FWPM_LAYER_IPSEC_KM_DEMUX_V4, + &FWPM_LAYER_IPSEC_KM_DEMUX_V6, + &FWPM_LAYER_IPSEC_V4, + &FWPM_LAYER_IPSEC_V6, + &FWPM_LAYER_IKEEXT_V4, + &FWPM_LAYER_IKEEXT_V6, + &FWPM_LAYER_RPC_UM, + &FWPM_LAYER_RPC_EPMAP, + &FWPM_LAYER_RPC_EP_ADD, /// 90 + &FWPM_LAYER_RPC_PROXY_CONN, + &FWPM_LAYER_RPC_PROXY_IF, + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + &FWPM_LAYER_KM_AUTHORIZATION, /// 93 + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + }; +const GUID* const ppUserModeLayerKeyArray[] = {&FWPM_LAYER_IPSEC_KM_DEMUX_V4, /// 0 + &FWPM_LAYER_IPSEC_KM_DEMUX_V6, + &FWPM_LAYER_IPSEC_V4, + &FWPM_LAYER_IPSEC_V6, + &FWPM_LAYER_IKEEXT_V4, + &FWPM_LAYER_IKEEXT_V6, + &FWPM_LAYER_RPC_UM, + &FWPM_LAYER_RPC_EPMAP, + &FWPM_LAYER_RPC_EP_ADD, + &FWPM_LAYER_RPC_PROXY_CONN, + &FWPM_LAYER_RPC_PROXY_IF, /// 10 +#if(NTDDI_VERSION >= NTDDI_WIN7) + + &FWPM_LAYER_KM_AUTHORIZATION, + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + }; + +const UINT32 TOTAL_LAYER_COUNT = RTL_NUMBER_OF(ppLayerKeyArray); +const UINT32 TOTAL_USER_MODE_LAYER_COUNT = RTL_NUMBER_OF(ppUserModeLayerKeyArray); +const UINT32 TOTAL_KERNEL_MODE_LAYER_COUNT = TOTAL_LAYER_COUNT - TOTAL_USER_MODE_LAYER_COUNT; + +const GUID* const ppConditionInboundIPPacket[] = {&FWPM_CONDITION_IP_LOCAL_ADDRESS, + &FWPM_CONDITION_IP_REMOTE_ADDRESS, + &FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE, + &FWPM_CONDITION_IP_LOCAL_INTERFACE, /// &FWPM_CONDITION_INTERFACE + &FWPM_CONDITION_INTERFACE_INDEX, /// &FWPM_CONDITION_LOCAL_INTERFACE_INDEX + &FWPM_CONDITION_SUB_INTERFACE_INDEX, /// &FWPM_CONDITION_ARRIVAL_SUB_INTERFACE_INDEX + &FWPM_CONDITION_FLAGS, + &FWPM_CONDITION_INTERFACE_TYPE, /// &FWPM_CONDITION_LOCAL_INTERFACE_TYPE + &FWPM_CONDITION_TUNNEL_TYPE, /// &FWPM_CONDITION_LOCAL_TUNNEL_TYPE + }; +const GUID* const ppConditionOutboundIPPacket[] = {&FWPM_CONDITION_IP_LOCAL_ADDRESS, + &FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE, + &FWPM_CONDITION_IP_REMOTE_ADDRESS, + &FWPM_CONDITION_IP_LOCAL_INTERFACE, /// &FWPM_CONDITION_INTERFACE + &FWPM_CONDITION_INTERFACE_INDEX, /// &FWPM_CONDITION_LOCAL_INTERFACE_INDEX + &FWPM_CONDITION_SUB_INTERFACE_INDEX, /// &FWPM_CONDITION_ARRIVAL_SUB_INTERFACE_INDEX + &FWPM_CONDITION_FLAGS, + &FWPM_CONDITION_INTERFACE_TYPE, /// &FWPM_CONDITION_LOCAL_INTERFACE_TYPE + &FWPM_CONDITION_TUNNEL_TYPE, /// &FWPM_CONDITION_LOCAL_TUNNEL_TYPE + }; +const GUID* const ppConditionIPForward[] = {&FWPM_CONDITION_IP_SOURCE_ADDRESS, + &FWPM_CONDITION_IP_DESTINATION_ADDRESS, + &FWPM_CONDITION_IP_DESTINATION_ADDRESS_TYPE, + &FWPM_CONDITION_IP_LOCAL_INTERFACE, /// &FWPM_CONDITION_INTERFACE + &FWPM_CONDITION_IP_FORWARD_INTERFACE, + &FWPM_CONDITION_SOURCE_INTERFACE_INDEX, + &FWPM_CONDITION_SOURCE_SUB_INTERFACE_INDEX, + &FWPM_CONDITION_DESTINATION_INTERFACE_INDEX, + &FWPM_CONDITION_DESTINATION_SUB_INTERFACE_INDEX, + &FWPM_CONDITION_FLAGS, + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + &FWPM_CONDITION_IP_PHYSICAL_ARRIVAL_INTERFACE, + &FWPM_CONDITION_ARRIVAL_INTERFACE_PROFILE_ID, + &FWPM_CONDITION_IP_PHYSICAL_NEXTHOP_INTERFACE, + &FWPM_CONDITION_NEXTHOP_INTERFACE_PROFILE_ID, + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + }; +const GUID* const ppConditionInboundTransport[] = {&FWPM_CONDITION_IP_PROTOCOL, + &FWPM_CONDITION_IP_LOCAL_ADDRESS, + &FWPM_CONDITION_IP_REMOTE_ADDRESS, + &FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE, + &FWPM_CONDITION_IP_LOCAL_PORT, /// &FWPM_CONDITION_ICMP_TYPE + &FWPM_CONDITION_IP_REMOTE_PORT, /// &FWPM_CONDITION_ICMP_CODE + &FWPM_CONDITION_IP_LOCAL_INTERFACE, /// &FWPM_CONDITION_INTERFACE + &FWPM_CONDITION_INTERFACE_INDEX, /// &FWPM_CONDITION_LOCAL_INTERFACE_INDEX + &FWPM_CONDITION_SUB_INTERFACE_INDEX, /// &FWPM_CONDITION_ARRIVAL_SUB_INTERFACE_INDEX + &FWPM_CONDITION_FLAGS, + &FWPM_CONDITION_INTERFACE_TYPE, /// &FWPM_CONDITION_LOCAL_INTERFACE_TYPE + &FWPM_CONDITION_TUNNEL_TYPE, /// &FWPM_CONDITION_LOCAL_TUNNEL_TYPE + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + &FWPM_CONDITION_CURRENT_PROFILE_ID, + +#if(NTDDI_VERSION >= NTDDI_WINTHRESHOLD) + + &FWPM_CONDITION_IPSEC_SECURITY_REALM_ID, + +#endif /// (NTDDI_VERSION >= NTDDI_WINTHRESHOLD) +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + }; +const GUID* const ppConditionOutboundTransport[] = {&FWPM_CONDITION_IP_PROTOCOL, + &FWPM_CONDITION_IP_LOCAL_ADDRESS, + &FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE, + &FWPM_CONDITION_IP_REMOTE_ADDRESS, + &FWPM_CONDITION_IP_LOCAL_PORT, /// &FWPM_CONDITION_ICMP_TYPE + &FWPM_CONDITION_IP_REMOTE_PORT, /// &FWPM_CONDITION_ICMP_CODE + &FWPM_CONDITION_IP_LOCAL_INTERFACE, /// &FWPM_CONDITION_INTERFACE + &FWPM_CONDITION_INTERFACE_INDEX, /// &FWPM_CONDITION_LOCAL_INTERFACE_INDEX + &FWPM_CONDITION_SUB_INTERFACE_INDEX, /// &FWPM_CONDITION_ARRIVAL_SUB_INTERFACE_INDEX + &FWPM_CONDITION_IP_DESTINATION_ADDRESS_TYPE, + &FWPM_CONDITION_FLAGS, + &FWPM_CONDITION_INTERFACE_TYPE, /// &FWPM_CONDITION_LOCAL_INTERFACE_TYPE + &FWPM_CONDITION_TUNNEL_TYPE, /// &FWPM_CONDITION_LOCAL_TUNNEL_TYPE + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + &FWPM_CONDITION_CURRENT_PROFILE_ID, + +#if(NTDDI_VERSION >= NTDDI_WINTHRESHOLD) + + &FWPM_CONDITION_IPSEC_SECURITY_REALM_ID, + +#endif /// (NTDDI_VERSION >= NTDDI_WINTHRESHOLD) +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + }; +const GUID* const ppConditionStream[] = {&FWPM_CONDITION_IP_LOCAL_ADDRESS, + &FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE, + &FWPM_CONDITION_IP_REMOTE_ADDRESS, + &FWPM_CONDITION_IP_LOCAL_PORT, + &FWPM_CONDITION_IP_REMOTE_PORT, + &FWPM_CONDITION_DIRECTION, + +#if(NTDDI_VERSION >= NTDDI_VISTASP1) + + &FWPM_CONDITION_FLAGS, + +#endif /// (NTDDI_VERSION >= NTDDI_VISTASP1) + + }; +const GUID* const ppConditionDatagramData[] = {&FWPM_CONDITION_IP_PROTOCOL, + &FWPM_CONDITION_IP_LOCAL_ADDRESS, + &FWPM_CONDITION_IP_REMOTE_ADDRESS, + &FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE, + &FWPM_CONDITION_IP_LOCAL_PORT, /// &FWPM_CONDITION_ICMP_TYPE + &FWPM_CONDITION_IP_REMOTE_PORT, /// &FWPM_CONDITION_ICMP_CODE + &FWPM_CONDITION_IP_LOCAL_INTERFACE, /// &FWPM_CONDITION_INTERFACE + &FWPM_CONDITION_INTERFACE_INDEX, /// &FWPM_CONDITION_LOCAL_INTERFACE_INDEX + &FWPM_CONDITION_SUB_INTERFACE_INDEX, /// &FWPM_CONDITION_ARRIVAL_SUB_INTERFACE_INDEX + &FWPM_CONDITION_DIRECTION, + &FWPM_CONDITION_FLAGS, + &FWPM_CONDITION_INTERFACE_TYPE, /// &FWPM_CONDITION_LOCAL_INTERFACE_TYPE + &FWPM_CONDITION_TUNNEL_TYPE, /// &FWPM_CONDITION_LOCAL_TUNNEL_TYPE + }; +const GUID* const ppConditionInboundICMPError[] = {&FWPM_CONDITION_EMBEDDED_PROTOCOL, + &FWPM_CONDITION_IP_LOCAL_ADDRESS, + &FWPM_CONDITION_IP_REMOTE_ADDRESS, + &FWPM_CONDITION_EMBEDDED_REMOTE_ADDRESS, + &FWPM_CONDITION_EMBEDDED_LOCAL_ADDRESS_TYPE, + &FWPM_CONDITION_EMBEDDED_LOCAL_PORT, + &FWPM_CONDITION_EMBEDDED_REMOTE_PORT, + &FWPM_CONDITION_IP_LOCAL_INTERFACE, /// &FWPM_CONDITION_INTERFACE + &FWPM_CONDITION_ICMP_TYPE, + &FWPM_CONDITION_ICMP_CODE, + &FWPM_CONDITION_INTERFACE_INDEX, /// &FWPM_CONDITION_LOCAL_INTERFACE_INDEX + &FWPM_CONDITION_SUB_INTERFACE_INDEX, /// &FWPM_CONDITION_ARRIVAL_SUB_INTERFACE_INDEX + &FWPM_CONDITION_INTERFACE_TYPE, /// &FWPM_CONDITION_LOCAL_INTERFACE_TYPE + &FWPM_CONDITION_TUNNEL_TYPE, /// &FWPM_CONDITION_LOCAL_TUNNEL_TYPE + +#if(NTDDI_VERSION >= NTDDI_VISTASP1) + + &FWPM_CONDITION_IP_ARRIVAL_INTERFACE, + &FWPM_CONDITION_ARRIVAL_INTERFACE_INDEX, + &FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE, + &FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE, + &FWPM_CONDITION_FLAGS, + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + &FWPM_CONDITION_ARRIVAL_INTERFACE_PROFILE_ID, + &FWPM_CONDITION_INTERFACE_QUARANTINE_EPOCH, + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) +#endif /// (NTDDI_VERSION >= NTDDI_VISTASP1) + + }; +const GUID* const ppConditionOutboundICMPError[] = {&FWPM_CONDITION_IP_LOCAL_ADDRESS, + &FWPM_CONDITION_IP_REMOTE_ADDRESS, + &FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE, + &FWPM_CONDITION_IP_LOCAL_INTERFACE, /// &FWPM_CONDITION_INTERFACE + &FWPM_CONDITION_ICMP_TYPE, + &FWPM_CONDITION_ICMP_CODE, + &FWPM_CONDITION_INTERFACE_INDEX, /// &FWPM_CONDITION_LOCAL_INTERFACE_INDEX + &FWPM_CONDITION_SUB_INTERFACE_INDEX, /// &FWPM_CONDITION_ARRIVAL_SUB_INTERFACE_INDEX + &FWPM_CONDITION_INTERFACE_TYPE, /// &FWPM_CONDITION_LOCAL_INTERFACE_TYPE + &FWPM_CONDITION_TUNNEL_TYPE, /// &FWPM_CONDITION_LOCAL_TUNNEL_TYPE + +#if(NTDDI_VERSION >= NTDDI_VISTASP1) + + &FWPM_CONDITION_FLAGS, + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + &FWPM_CONDITION_NEXTHOP_INTERFACE_PROFILE_ID, + &FWPM_CONDITION_INTERFACE_QUARANTINE_EPOCH, + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) +#endif /// (NTDDI_VERSION >= NTDDI_VISTASP1) + + }; +const GUID* const ppConditionALEResourceAssignment[] = {&FWPM_CONDITION_ALE_APP_ID, + &FWPM_CONDITION_ALE_USER_ID, + &FWPM_CONDITION_IP_LOCAL_ADDRESS, + &FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE, + &FWPM_CONDITION_IP_LOCAL_PORT, /// &FWPM_CONDITION_ICMP_TYPE + &FWPM_CONDITION_IP_PROTOCOL, + &FWPM_CONDITION_ALE_PROMISCUOUS_MODE, + &FWPM_CONDITION_IP_LOCAL_INTERFACE, /// &FWPM_CONDITION_INTERFACE + &FWPM_CONDITION_FLAGS, + &FWPM_CONDITION_INTERFACE_TYPE, /// &FWPM_CONDITION_LOCAL_INTERFACE_TYPE + &FWPM_CONDITION_TUNNEL_TYPE, /// &FWPM_CONDITION_LOCAL_TUNNEL_TYPE + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + &FWPM_CONDITION_LOCAL_INTERFACE_PROFILE_ID, + &FWPM_CONDITION_ALE_SIO_FIREWALL_SYSTEM_PORT, /// &FWPM_CONDITION_ALE_SIO_FIREWALL_SOCKET_PROPERTY + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + &FWPM_CONDITION_ALE_PACKAGE_ID, + +#if(NTDDI_VERSION >= NTDDI_WINTHRESHOLD) + + &FWPM_CONDITION_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE, + +#endif /// (NTDDI_VERSION >= NTDDI_WINTHRESHOLD) +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + }; +const GUID* const ppConditionALEAuthListen[] = {&FWPM_CONDITION_ALE_APP_ID, + &FWPM_CONDITION_ALE_USER_ID, + &FWPM_CONDITION_IP_LOCAL_ADDRESS, + &FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE, + &FWPM_CONDITION_IP_LOCAL_PORT, + &FWPM_CONDITION_IP_LOCAL_INTERFACE, /// &FWPM_CONDITION_INTERFACE + &FWPM_CONDITION_FLAGS, + &FWPM_CONDITION_INTERFACE_TYPE, /// &FWPM_CONDITION_LOCAL_INTERFACE_TYPE + &FWPM_CONDITION_TUNNEL_TYPE, /// &FWPM_CONDITION_LOCAL_TUNNEL_TYPE + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + &FWPM_CONDITION_LOCAL_INTERFACE_PROFILE_ID, + &FWPM_CONDITION_ALE_SIO_FIREWALL_SYSTEM_PORT, /// &FWPM_CONDITION_ALE_SIO_FIREWALL_SOCKET_PROPERTY + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + &FWPM_CONDITION_ALE_PACKAGE_ID, + +#if(NTDDI_VERSION >= NTDDI_WINTHRESHOLD) + + &FWPM_CONDITION_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE, + +#endif /// (NTDDI_VERSION >= NTDDI_WINTHRESHOLD) +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + }; +const GUID* const ppConditionALEAuthRecvAccept[] = {&FWPM_CONDITION_ALE_APP_ID, + &FWPM_CONDITION_ALE_USER_ID, + &FWPM_CONDITION_IP_LOCAL_ADDRESS, + &FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE, + &FWPM_CONDITION_IP_LOCAL_PORT, /// &FWPM_CONDITION_ICMP_TYPE + &FWPM_CONDITION_IP_PROTOCOL, + &FWPM_CONDITION_IP_REMOTE_ADDRESS, + &FWPM_CONDITION_IP_REMOTE_PORT, /// &FWPM_CONDITION_ICMP_CODE + &FWPM_CONDITION_ALE_REMOTE_USER_ID, + &FWPM_CONDITION_ALE_REMOTE_MACHINE_ID, + &FWPM_CONDITION_IP_LOCAL_INTERFACE, /// &FWPM_CONDITION_INTERFACE + &FWPM_CONDITION_FLAGS, + &FWPM_CONDITION_ALE_SIO_FIREWALL_SYSTEM_PORT, /// &FWPM_CONDITION_ALE_SIO_FIREWALL_SOCKET_PROPERTY + &FWPM_CONDITION_ALE_NAP_CONTEXT, + &FWPM_CONDITION_INTERFACE_TYPE, /// &FWPM_CONDITION_LOCAL_INTERFACE_TYPE + &FWPM_CONDITION_TUNNEL_TYPE, /// &FWPM_CONDITION_LOCAL_TUNNEL_TYPE + &FWPM_CONDITION_INTERFACE_INDEX, /// &FWPM_CONDITION_LOCAL_INTERFACE_INDEX + &FWPM_CONDITION_SUB_INTERFACE_INDEX, /// &FWPM_CONDITION_ARRIVAL_SUB_INTERFACE_INDEX + +#if(NTDDI_VERSION >= NTDDI_VISTASP1) + + &FWPM_CONDITION_IP_ARRIVAL_INTERFACE, + &FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE, + &FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE, + &FWPM_CONDITION_ARRIVAL_INTERFACE_INDEX, + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + &FWPM_CONDITION_NEXTHOP_SUB_INTERFACE_INDEX, + &FWPM_CONDITION_IP_NEXTHOP_INTERFACE, + &FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE, + &FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE, + &FWPM_CONDITION_NEXTHOP_INTERFACE_INDEX, + &FWPM_CONDITION_ORIGINAL_PROFILE_ID, + &FWPM_CONDITION_CURRENT_PROFILE_ID, + &FWPM_CONDITION_REAUTHORIZE_REASON, + &FWPM_CONDITION_ORIGINAL_ICMP_TYPE, + &FWPM_CONDITION_INTERFACE_QUARANTINE_EPOCH, + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + &FWPM_CONDITION_ALE_PACKAGE_ID, + +#if(NTDDI_VERSION >= NTDDI_WINTHRESHOLD) + + &FWPM_CONDITION_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE, + +#endif /// (NTDDI_VERSION >= NTDDI_WINTHRESHOLD) +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) +#endif /// (NTDDI_VERSION >= NTDDI_VISTASP1) + + }; +const GUID* const ppConditionALEAuthConnect[] = {&FWPM_CONDITION_ALE_APP_ID, + &FWPM_CONDITION_ALE_USER_ID, + &FWPM_CONDITION_IP_LOCAL_ADDRESS, + &FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE, + &FWPM_CONDITION_IP_LOCAL_PORT, /// &FWPM_CONDITION_ICMP_TYPE + &FWPM_CONDITION_IP_PROTOCOL, + &FWPM_CONDITION_IP_REMOTE_ADDRESS, /// &FWPM_CONDITION_ICMP_CODE + &FWPM_CONDITION_IP_REMOTE_PORT, /// &FWPM_CONDITION_ICMP_CODE + &FWPM_CONDITION_ALE_REMOTE_USER_ID, + &FWPM_CONDITION_ALE_REMOTE_MACHINE_ID, + &FWPM_CONDITION_IP_DESTINATION_ADDRESS_TYPE, + &FWPM_CONDITION_IP_LOCAL_INTERFACE, /// &FWPM_CONDITION_INTERFACE + &FWPM_CONDITION_FLAGS, + &FWPM_CONDITION_INTERFACE_TYPE, /// &FWPM_CONDITION_LOCAL_INTERFACE_TYPE + &FWPM_CONDITION_TUNNEL_TYPE, /// &FWPM_CONDITION_LOCAL_TUNNEL_TYPE + +#if(NTDDI_VERSION >= NTDDI_VISTASP1) + + &FWPM_CONDITION_INTERFACE_INDEX, /// &FWPM_CONDITION_LOCAL_INTERFACE_INDEX + &FWPM_CONDITION_SUB_INTERFACE_INDEX, /// &FWPM_CONDITION_ARRIVAL_SUB_INTERFACE_INDEX + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + &FWPM_CONDITION_IP_ARRIVAL_INTERFACE, + &FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE, + &FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE, + &FWPM_CONDITION_ARRIVAL_INTERFACE_INDEX, + &FWPM_CONDITION_NEXTHOP_SUB_INTERFACE_INDEX, + &FWPM_CONDITION_IP_NEXTHOP_INTERFACE, + &FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE, + &FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE, + &FWPM_CONDITION_NEXTHOP_INTERFACE_INDEX, + &FWPM_CONDITION_ORIGINAL_PROFILE_ID, + &FWPM_CONDITION_CURRENT_PROFILE_ID, + &FWPM_CONDITION_REAUTHORIZE_REASON, + &FWPM_CONDITION_PEER_NAME, + &FWPM_CONDITION_ORIGINAL_ICMP_TYPE, + &FWPM_CONDITION_INTERFACE_QUARANTINE_EPOCH, + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + &FWPM_CONDITION_ALE_ORIGINAL_APP_ID, + &FWPM_CONDITION_ALE_PACKAGE_ID, + +#if(NTDDI_VERSION >= NTDDI_WINTHRESHOLD) + + &FWPM_CONDITION_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE, + +#endif /// (NTDDI_VERSION >= NTDDI_WINTHRESHOLD) +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) +#endif /// (NTDDI_VERSION >= NTDDI_VISTASP1) + + }; +const GUID* const ppConditionALEFlowEstablished[] = {&FWPM_CONDITION_ALE_APP_ID, + &FWPM_CONDITION_ALE_USER_ID, + &FWPM_CONDITION_IP_LOCAL_ADDRESS, + &FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE, + &FWPM_CONDITION_IP_LOCAL_PORT, /// &FWPM_CONDITION_ICMP_TYPE + &FWPM_CONDITION_IP_PROTOCOL, + &FWPM_CONDITION_IP_REMOTE_ADDRESS, + &FWPM_CONDITION_IP_REMOTE_PORT, /// &FWPM_CONDITION_ICMP_CODE + &FWPM_CONDITION_ALE_REMOTE_USER_ID, + &FWPM_CONDITION_ALE_REMOTE_MACHINE_ID, + &FWPM_CONDITION_IP_DESTINATION_ADDRESS_TYPE, + &FWPM_CONDITION_IP_LOCAL_INTERFACE, /// &FWPM_CONDITION_INTERFACE + &FWPM_CONDITION_DIRECTION, + &FWPM_CONDITION_INTERFACE_TYPE, /// &FWPM_CONDITION_LOCAL_INTERFACE_TYPE + &FWPM_CONDITION_TUNNEL_TYPE, /// &FWPM_CONDITION_LOCAL_TUNNEL_TYPE + +#if(NTDDI_VERSION >= NTDDI_VISTASP1) + + &FWPM_CONDITION_FLAGS, + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + &FWPM_CONDITION_ALE_ORIGINAL_APP_ID, + &FWPM_CONDITION_ALE_PACKAGE_ID, + +#if(NTDDI_VERSION >= NTDDI_WINTHRESHOLD) + + &FWPM_CONDITION_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE, + +#endif /// (NTDDI_VERSION >= NTDDI_WINTHRESHOLD) +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) +#endif /// (NTDDI_VERSION >= NTDDI_VISTASP1) + + }; +const GUID* const ppConditionIPsecKMDemux[] = {&FWPM_CONDITION_IP_LOCAL_ADDRESS, + &FWPM_CONDITION_IP_REMOTE_ADDRESS, + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + &FWPM_CONDITION_QM_MODE, + &FWPM_CONDITION_IP_LOCAL_INTERFACE, + &FWPM_CONDITION_CURRENT_PROFILE_ID, + +#if(NTDDI_VERSION >= NTDDI_WINTHRESHOLD) + &FWPM_CONDITION_IPSEC_SECURITY_REALM_ID +#endif /// (NTDDI_VERSION >= NTDDI_WINTHRESHOLD) + +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) + + + }; +const GUID* const ppConditionIPsec[] = {&FWPM_CONDITION_IP_PROTOCOL, + &FWPM_CONDITION_IP_LOCAL_ADDRESS, + &FWPM_CONDITION_IP_REMOTE_ADDRESS, + &FWPM_CONDITION_IP_LOCAL_PORT, /// &FWPM_CONDITION_ICMP_TYPE + &FWPM_CONDITION_IP_REMOTE_PORT, /// &FWPM_CONDITION_ICMP_CODE + &FWPM_CONDITION_IP_LOCAL_INTERFACE, /// &FWPM_CONDITION_INTERFACE + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + &FWPM_CONDITION_CURRENT_PROFILE_ID, + +#if(NTDDI_VERSION >= NTDDI_WINTHRESHOLD) + &FWPM_CONDITION_IPSEC_SECURITY_REALM_ID +#endif /// (NTDDI_VERSION >= NTDDI_WINTHRESHOLD) + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + + }; +const GUID* const ppConditionIKEExt[] = {&FWPM_CONDITION_IP_LOCAL_ADDRESS, + &FWPM_CONDITION_IP_REMOTE_ADDRESS, + &FWPM_CONDITION_IP_LOCAL_INTERFACE, /// &FWPM_CONDITION_INTERFACE + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + &FWPM_CONDITION_CURRENT_PROFILE_ID, + +#if(NTDDI_VERSION >= NTDDI_WINTHRESHOLD) + &FWPM_CONDITION_IPSEC_SECURITY_REALM_ID +#endif /// (NTDDI_VERSION >= NTDDI_WINTHRESHOLD) + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + + }; +const GUID* const ppConditionRPCUM[] = {&FWPM_CONDITION_REMOTE_USER_TOKEN, + &FWPM_CONDITION_RPC_IF_UUID, + &FWPM_CONDITION_RPC_IF_VERSION, + &FWPM_CONDITION_RPC_IF_FLAG, + &FWPM_CONDITION_DCOM_APP_ID, + &FWPM_CONDITION_IMAGE_NAME, + &FWPM_CONDITION_RPC_PROTOCOL, + &FWPM_CONDITION_RPC_AUTH_TYPE, + &FWPM_CONDITION_RPC_AUTH_LEVEL, + &FWPM_CONDITION_SEC_ENCRYPT_ALGORITHM, + &FWPM_CONDITION_SEC_KEY_SIZE, + &FWPM_CONDITION_IP_LOCAL_ADDRESS_V4, + &FWPM_CONDITION_IP_LOCAL_ADDRESS_V6, + &FWPM_CONDITION_IP_LOCAL_PORT, /// &FWPM_CONDITION_ICMP_CODEE + &FWPM_CONDITION_PIPE, + &FWPM_CONDITION_IP_REMOTE_ADDRESS_V4, + &FWPM_CONDITION_IP_REMOTE_ADDRESS_V6, + }; +const GUID* const ppConditionRPCEPMap[] = {&FWPM_CONDITION_REMOTE_USER_TOKEN, + &FWPM_CONDITION_RPC_IF_UUID, + &FWPM_CONDITION_RPC_IF_VERSION, + &FWPM_CONDITION_RPC_PROTOCOL, + &FWPM_CONDITION_RPC_AUTH_TYPE, + &FWPM_CONDITION_RPC_AUTH_LEVEL, + &FWPM_CONDITION_SEC_ENCRYPT_ALGORITHM, + &FWPM_CONDITION_SEC_KEY_SIZE, + &FWPM_CONDITION_IP_LOCAL_ADDRESS_V4, + &FWPM_CONDITION_IP_LOCAL_ADDRESS_V6, + &FWPM_CONDITION_IP_LOCAL_PORT, /// &FWPM_CONDITION_ICMP_TYPE + &FWPM_CONDITION_PIPE, + &FWPM_CONDITION_IP_REMOTE_ADDRESS_V4, + &FWPM_CONDITION_IP_REMOTE_ADDRESS_V6, + }; +const GUID* const ppConditionRPCEPAdd[] = {&FWPM_CONDITION_PROCESS_WITH_RPC_IF_UUID, + &FWPM_CONDITION_RPC_PROTOCOL, + &FWPM_CONDITION_RPC_EP_VALUE, + &FWPM_CONDITION_RPC_EP_FLAGS, + }; +const GUID* const ppConditionRPCProxyConn[] = {&FWPM_CONDITION_CLIENT_TOKEN, + &FWPM_CONDITION_RPC_SERVER_NAME, + &FWPM_CONDITION_RPC_SERVER_PORT, + &FWPM_CONDITION_RPC_PROXY_AUTH_TYPE, + &FWPM_CONDITION_CLIENT_CERT_KEY_LENGTH, + &FWPM_CONDITION_CLIENT_CERT_OID, + }; +const GUID* const ppConditionRPCProxyIf[] = {&FWPM_CONDITION_CLIENT_TOKEN, + &FWPM_CONDITION_RPC_IF_UUID, + &FWPM_CONDITION_RPC_IF_VERSION, + &FWPM_CONDITION_RPC_SERVER_NAME, + &FWPM_CONDITION_RPC_SERVER_PORT, + &FWPM_CONDITION_RPC_PROXY_AUTH_TYPE, + &FWPM_CONDITION_CLIENT_CERT_KEY_LENGTH, + &FWPM_CONDITION_CLIENT_CERT_OID, + }; + +#if(NTDDI_VERSION >= NTDDI_WIN7) + +const GUID* const ppConditionNameResolutionCache[] = {&FWPM_CONDITION_ALE_USER_ID, + &FWPM_CONDITION_ALE_APP_ID, + &FWPM_CONDITION_IP_REMOTE_ADDRESS, + &FWPM_CONDITION_PEER_NAME, + }; +const GUID* const ppConditionALEResourceRelease[] = {&FWPM_CONDITION_ALE_APP_ID, + &FWPM_CONDITION_ALE_USER_ID, + &FWPM_CONDITION_IP_LOCAL_ADDRESS, + &FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE, + &FWPM_CONDITION_IP_LOCAL_PORT, /// &FWPM_CONDITION_ICMP_TYPE + &FWPM_CONDITION_IP_PROTOCOL, + &FWPM_CONDITION_IP_LOCAL_INTERFACE, /// &FWPM_CONDITION_INTERFACE + &FWPM_CONDITION_FLAGS, + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + &FWPM_CONDITION_ALE_PACKAGE_ID, + +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) + + }; +const GUID* const ppConditionALEEndpointClosure[] = {&FWPM_CONDITION_ALE_APP_ID, + &FWPM_CONDITION_ALE_USER_ID, + &FWPM_CONDITION_IP_LOCAL_ADDRESS, + &FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE, + &FWPM_CONDITION_IP_LOCAL_PORT, /// &FWPM_CONDITION_ICMP_TYPE + &FWPM_CONDITION_IP_PROTOCOL, + &FWPM_CONDITION_IP_REMOTE_ADDRESS, + &FWPM_CONDITION_IP_REMOTE_PORT, /// &FWPM_CONDITION_ICMP_CODE + &FWPM_CONDITION_IP_LOCAL_INTERFACE, /// &FWPM_CONDITION_INTERFACE + &FWPM_CONDITION_FLAGS, + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + &FWPM_CONDITION_ALE_PACKAGE_ID, + +#if(NTDDI_VERSION >= NTDDI_WINTHRESHOLD) + + &FWPM_CONDITION_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE, + +#endif /// (NTDDI_VERSION >= NTDDI_WINTHRESHOLD) +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) + + }; +const GUID* const ppConditionALEConnectRedirect[] = {&FWPM_CONDITION_ALE_APP_ID, + &FWPM_CONDITION_ALE_USER_ID, + &FWPM_CONDITION_IP_LOCAL_ADDRESS, + &FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE, + &FWPM_CONDITION_IP_LOCAL_PORT, /// &FWPM_CONDITION_ICMP_TYPE + &FWPM_CONDITION_IP_PROTOCOL, + &FWPM_CONDITION_IP_REMOTE_ADDRESS, + &FWPM_CONDITION_IP_DESTINATION_ADDRESS_TYPE, + &FWPM_CONDITION_IP_REMOTE_PORT, /// &FWPM_CONDITION_ICMP_CODE + &FWPM_CONDITION_FLAGS, + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + &FWPM_CONDITION_ALE_ORIGINAL_APP_ID, + &FWPM_CONDITION_ALE_PACKAGE_ID, + +#if(NTDDI_VERSION >= NTDDI_WINTHRESHOLD) + + &FWPM_CONDITION_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE, + +#endif /// (NTDDI_VERSION >= NTDDI_WINTHRESHOLD) +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) + + }; +const GUID* const ppConditionALEBindRedirect[] = {&FWPM_CONDITION_ALE_APP_ID, + &FWPM_CONDITION_ALE_USER_ID, + &FWPM_CONDITION_IP_LOCAL_ADDRESS, + &FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE, + &FWPM_CONDITION_IP_LOCAL_PORT, /// &FWPM_CONDITION_ICMP_TYPE + &FWPM_CONDITION_IP_PROTOCOL, + &FWPM_CONDITION_FLAGS, + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + &FWPM_CONDITION_ALE_PACKAGE_ID, + +#if(NTDDI_VERSION >= NTDDI_WINTHRESHOLD) + + &FWPM_CONDITION_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE, + +#endif /// (NTDDI_VERSION >= NTDDI_WINTHRESHOLD) +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) + + }; +const GUID* const ppConditionStreamPacket[] = {&FWPM_CONDITION_IP_LOCAL_ADDRESS, + &FWPM_CONDITION_IP_REMOTE_ADDRESS, + &FWPM_CONDITION_IP_LOCAL_PORT, + &FWPM_CONDITION_IP_REMOTE_PORT, /// &FWPM_CONDITION_ICMP_CODE + &FWPM_CONDITION_IP_LOCAL_INTERFACE, /// &FWPM_CONDITION_INTERFACE + &FWPM_CONDITION_INTERFACE_INDEX, /// &FWPM_CONDITION_LOCAL_INTERFACE_INDEX + &FWPM_CONDITION_SUB_INTERFACE_INDEX, /// &FWPM_CONDITION_ARRIVAL_SUB_INTERFACE_INDEX + &FWPM_CONDITION_DIRECTION, + &FWPM_CONDITION_FLAGS, + &FWPM_CONDITION_INTERFACE_TYPE, /// &FWPM_CONDITION_LOCAL_INTERFACE_TYPE + &FWPM_CONDITION_TUNNEL_TYPE, /// &FWPM_CONDITION_LOCAL_TUNNEL_TYPE + }; +const GUID* const ppConditionKMAuthorization[] = {&FWPM_CONDITION_REMOTE_ID, + &FWPM_CONDITION_AUTHENTICATION_TYPE, + &FWPM_CONDITION_KM_TYPE, + &FWPM_CONDITION_DIRECTION, + &FWPM_CONDITION_KM_MODE, + &FWPM_CONDITION_IPSEC_POLICY_KEY, + &FWPM_CONDITION_KM_AUTH_NAP_CONTEXT, + }; + +#if(NTDDI_VERSION >= NTDDI_WIN8) + +const GUID* const ppConditionInboundMACFrameEthernet[] = {&FWPM_CONDITION_INTERFACE_MAC_ADDRESS, + &FWPM_CONDITION_MAC_LOCAL_ADDRESS, + &FWPM_CONDITION_MAC_REMOTE_ADDRESS, + &FWPM_CONDITION_MAC_LOCAL_ADDRESS_TYPE, + &FWPM_CONDITION_MAC_REMOTE_ADDRESS_TYPE, + &FWPM_CONDITION_ETHER_TYPE, + &FWPM_CONDITION_VLAN_ID, + &FWPM_CONDITION_INTERFACE, + &FWPM_CONDITION_INTERFACE_INDEX, /// &FWPM_CONDITION_LOCAL_INTERFACE_INDEX + &FWPM_CONDITION_NDIS_PORT, + &FWPM_CONDITION_L2_FLAGS, + }; +const GUID* const ppConditionOutboundMACFrameEthernet[] = {&FWPM_CONDITION_INTERFACE_MAC_ADDRESS, + &FWPM_CONDITION_MAC_LOCAL_ADDRESS, + &FWPM_CONDITION_MAC_REMOTE_ADDRESS, + &FWPM_CONDITION_MAC_LOCAL_ADDRESS_TYPE, + &FWPM_CONDITION_MAC_REMOTE_ADDRESS_TYPE, + &FWPM_CONDITION_ETHER_TYPE, + &FWPM_CONDITION_VLAN_ID, + &FWPM_CONDITION_INTERFACE, + &FWPM_CONDITION_INTERFACE_INDEX, /// &FWPM_CONDITION_LOCAL_INTERFACE_INDEX + &FWPM_CONDITION_NDIS_PORT, + &FWPM_CONDITION_L2_FLAGS, + }; +const GUID* const ppConditionInboundMACFrameNative[] = {&FWPM_CONDITION_NDIS_MEDIA_TYPE, + &FWPM_CONDITION_NDIS_PHYSICAL_MEDIA_TYPE, + &FWPM_CONDITION_INTERFACE, + &FWPM_CONDITION_INTERFACE_TYPE, /// &FWPM_CONDITION_LOCAL_INTERFACE_TYPE + &FWPM_CONDITION_INTERFACE_INDEX, /// &FWPM_CONDITION_LOCAL_INTERFACE_INDEX + &FWPM_CONDITION_NDIS_PORT, + &FWPM_CONDITION_L2_FLAGS, + }; +const GUID* const ppConditionOutboundMACFrameNative[] = {&FWPM_CONDITION_NDIS_MEDIA_TYPE, + &FWPM_CONDITION_NDIS_PHYSICAL_MEDIA_TYPE, + &FWPM_CONDITION_INTERFACE, + &FWPM_CONDITION_INTERFACE_TYPE, /// &FWPM_CONDITION_LOCAL_INTERFACE_TYPE + &FWPM_CONDITION_INTERFACE_INDEX, /// &FWPM_CONDITION_LOCAL_INTERFACE_INDEX + &FWPM_CONDITION_NDIS_PORT, + &FWPM_CONDITION_L2_FLAGS, + }; +const GUID* const ppConditionIngressVSwitchEthernet[] = {&FWPM_CONDITION_MAC_SOURCE_ADDRESS, + &FWPM_CONDITION_MAC_SOURCE_ADDRESS_TYPE, + &FWPM_CONDITION_MAC_DESTINATION_ADDRESS, + &FWPM_CONDITION_MAC_DESTINATION_ADDRESS_TYPE, + &FWPM_CONDITION_ETHER_TYPE, + &FWPM_CONDITION_VLAN_ID, + &FWPM_CONDITION_VSWITCH_TENANT_NETWORK_ID, + &FWPM_CONDITION_VSWITCH_ID, + &FWPM_CONDITION_VSWITCH_NETWORK_TYPE, + &FWPM_CONDITION_VSWITCH_SOURCE_INTERFACE_ID, + &FWPM_CONDITION_VSWITCH_SOURCE_INTERFACE_TYPE, + &FWPM_CONDITION_VSWITCH_SOURCE_VM_ID, + &FWPM_CONDITION_L2_FLAGS, + }; +const GUID* const ppConditionEgressVSwitchEthernet[] = {&FWPM_CONDITION_MAC_SOURCE_ADDRESS, + &FWPM_CONDITION_MAC_SOURCE_ADDRESS_TYPE, + &FWPM_CONDITION_MAC_DESTINATION_ADDRESS, + &FWPM_CONDITION_MAC_DESTINATION_ADDRESS_TYPE, + &FWPM_CONDITION_ETHER_TYPE, + &FWPM_CONDITION_VLAN_ID, + &FWPM_CONDITION_VSWITCH_TENANT_NETWORK_ID, + &FWPM_CONDITION_VSWITCH_ID, + &FWPM_CONDITION_VSWITCH_NETWORK_TYPE, + &FWPM_CONDITION_VSWITCH_SOURCE_INTERFACE_ID, + &FWPM_CONDITION_VSWITCH_SOURCE_INTERFACE_TYPE, + &FWPM_CONDITION_VSWITCH_SOURCE_VM_ID, + &FWPM_CONDITION_VSWITCH_DESTINATION_INTERFACE_ID, + &FWPM_CONDITION_VSWITCH_DESTINATION_INTERFACE_TYPE, + &FWPM_CONDITION_VSWITCH_DESTINATION_VM_ID, + &FWPM_CONDITION_L2_FLAGS, + }; +const GUID* const ppConditionIngressVSwitchTransport[] = {&FWPM_CONDITION_IP_SOURCE_ADDRESS, + &FWPM_CONDITION_IP_DESTINATION_ADDRESS, + &FWPM_CONDITION_IP_PROTOCOL, + &FWPM_CONDITION_IP_SOURCE_PORT, /// &FWPM_CONDITION_VSWITCH_ICMP_TYPE + &FWPM_CONDITION_IP_DESTINATION_PORT, /// &FWPM_CONDITION_VSWITCH_ICMP_CODE + &FWPM_CONDITION_VLAN_ID, + &FWPM_CONDITION_VSWITCH_TENANT_NETWORK_ID, + &FWPM_CONDITION_VSWITCH_ID, + &FWPM_CONDITION_VSWITCH_NETWORK_TYPE, + &FWPM_CONDITION_VSWITCH_SOURCE_INTERFACE_ID, + &FWPM_CONDITION_VSWITCH_SOURCE_INTERFACE_TYPE, + &FWPM_CONDITION_VSWITCH_SOURCE_VM_ID, + &FWPM_CONDITION_L2_FLAGS + }; +const GUID* const ppConditionEgressVSwitchTransport[] = {&FWPM_CONDITION_IP_SOURCE_ADDRESS, + &FWPM_CONDITION_IP_DESTINATION_ADDRESS, + &FWPM_CONDITION_IP_PROTOCOL, + &FWPM_CONDITION_IP_SOURCE_PORT, /// &FWPM_CONDITION_VSWITCH_ICMP_TYPE + &FWPM_CONDITION_IP_DESTINATION_PORT, /// &FWPM_CONDITION_VSWITCH_ICMP_CODE + &FWPM_CONDITION_VLAN_ID, + &FWPM_CONDITION_VSWITCH_TENANT_NETWORK_ID, + &FWPM_CONDITION_VSWITCH_ID, + &FWPM_CONDITION_VSWITCH_NETWORK_TYPE, + &FWPM_CONDITION_VSWITCH_SOURCE_INTERFACE_ID, + &FWPM_CONDITION_VSWITCH_SOURCE_INTERFACE_TYPE, + &FWPM_CONDITION_VSWITCH_SOURCE_VM_ID, + &FWPM_CONDITION_VSWITCH_DESTINATION_INTERFACE_ID, + &FWPM_CONDITION_VSWITCH_DESTINATION_INTERFACE_TYPE, + &FWPM_CONDITION_VSWITCH_DESTINATION_VM_ID, + &FWPM_CONDITION_L2_FLAGS + }; + +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) +#endif /// (NTDDI_VERSION >= NTDDI_VISTA) + +#if(NTDDI_VERSION >= NTDDI_VISTA) + +const UINT16 INBOUND_IPPACKET_CONDITIONS_COUNT = RTL_NUMBER_OF(ppConditionInboundIPPacket); +const UINT16 OUTBOUND_IPPACKET_CONDITIONS_COUNT = RTL_NUMBER_OF(ppConditionOutboundIPPacket); +const UINT16 IPFORWARD_CONDITIONS_COUNT = RTL_NUMBER_OF(ppConditionIPForward); +const UINT16 INBOUND_TRANSPORT_CONDITIONS_COUNT = RTL_NUMBER_OF(ppConditionInboundTransport); +const UINT16 OUTBOUND_TRANSPORT_CONDITIONS_COUNT = RTL_NUMBER_OF(ppConditionOutboundTransport); +const UINT16 STREAM_CONDITIONS_COUNT = RTL_NUMBER_OF(ppConditionStream); +const UINT16 DATAGRAM_DATA_CONDITIONS_COUNT = RTL_NUMBER_OF(ppConditionDatagramData); +const UINT16 INBOUND_ICMP_ERROR_CONDITIONS_COUNT = RTL_NUMBER_OF(ppConditionInboundICMPError); +const UINT16 OUTBOUND_ICMP_ERROR_CONDITIONS_COUNT = RTL_NUMBER_OF(ppConditionOutboundICMPError); +const UINT16 ALE_RESOURCE_ASSIGNMENT_CONDITIONS_COUNT = RTL_NUMBER_OF(ppConditionALEResourceAssignment); +const UINT16 ALE_AUTH_LISTEN_CONDITIONS_COUNT = RTL_NUMBER_OF(ppConditionALEAuthListen); +const UINT16 ALE_AUTH_RECV_ACCEPT_CONDITIONS_COUNT = RTL_NUMBER_OF(ppConditionALEAuthRecvAccept); +const UINT16 ALE_AUTH_CONNECT_CONDITIONS_COUNT = RTL_NUMBER_OF(ppConditionALEAuthConnect); +const UINT16 ALE_FLOW_ESTABLISHED_CONDITIONS_COUNT = RTL_NUMBER_OF(ppConditionALEFlowEstablished); +const UINT16 IPSEC_KM_DEMUX_CONDITIONS_COUNT = RTL_NUMBER_OF(ppConditionIPsecKMDemux); +const UINT16 IPSEC_CONDITIONS_COUNT = RTL_NUMBER_OF(ppConditionIPsec); +const UINT16 IKEEXT_CONDITIONS_COUNT = RTL_NUMBER_OF(ppConditionIKEExt); +const UINT16 RPC_UM_CONDITIONS_COUNT = RTL_NUMBER_OF(ppConditionRPCUM); +const UINT16 RPC_EPMAP_CONDITIONS_COUNT = RTL_NUMBER_OF(ppConditionRPCEPMap); +const UINT16 RPC_EP_ADD_CONDITIONS_COUNT = RTL_NUMBER_OF(ppConditionRPCEPAdd); +const UINT16 RPC_PROXY_CONN_CONDITIONS_COUNT = RTL_NUMBER_OF(ppConditionRPCProxyConn); +const UINT16 RPC_PROXY_IF_CONDITIONS_COUNT = RTL_NUMBER_OF(ppConditionRPCProxyIf); + +#if(NTDDI_VERSION >= NTDDI_WIN7) + +const UINT16 NAME_RESOLUTION_CACHE_CONDITIONS_COUNT = RTL_NUMBER_OF(ppConditionNameResolutionCache); +const UINT16 ALE_RESOURCE_RELEASE_CONDITIONS_COUNT = RTL_NUMBER_OF(ppConditionALEResourceRelease); +const UINT16 ALE_ENDPOINT_CLOSURE_CONDITIONS_COUNT = RTL_NUMBER_OF(ppConditionALEEndpointClosure); +const UINT16 ALE_CONNECT_REDIRECT_CONDITIONS_COUNT = RTL_NUMBER_OF(ppConditionALEConnectRedirect); +const UINT16 ALE_BIND_REDIRECT_CONDITIONS_COUNT = RTL_NUMBER_OF(ppConditionALEBindRedirect); +const UINT16 STREAM_PACKET_CONDITIONS_COUNT = RTL_NUMBER_OF(ppConditionStreamPacket); +const UINT16 KM_AUTHORIZATION_CONDITIONS_COUNT = RTL_NUMBER_OF(ppConditionKMAuthorization); + +#if(NTDDI_VERSION >= NTDDI_WIN8) + +const UINT16 INBOUND_MAC_FRAME_ETHERNET_CONDITIONS_COUNT = RTL_NUMBER_OF(ppConditionInboundMACFrameEthernet); +const UINT16 OUTBOUND_MAC_FRAME_ETHERNET_CONDITIONS_COUNT = RTL_NUMBER_OF(ppConditionOutboundMACFrameEthernet); +const UINT16 INBOUND_MAC_FRAME_NATIVE_CONDITIONS_COUNT = RTL_NUMBER_OF(ppConditionInboundMACFrameNative); +const UINT16 OUTBOUND_MAC_FRAME_NATIVE_CONDITIONS_COUNT = RTL_NUMBER_OF(ppConditionOutboundMACFrameNative); +const UINT16 INGRESS_VSWITCH_ETHERNET_CONDITIONS_COUNT = RTL_NUMBER_OF(ppConditionIngressVSwitchEthernet); +const UINT16 EGRESS_VSWITCH_ETHERNET_CONDITIONS_COUNT = RTL_NUMBER_OF(ppConditionEgressVSwitchEthernet); +const UINT16 INGRESS_VSWITCH_TRANSPORT_CONDITIONS_COUNT = RTL_NUMBER_OF(ppConditionIngressVSwitchTransport); +const UINT16 EGRESS_VSWITCH_TRANSPORT_CONDITIONS_COUNT = RTL_NUMBER_OF(ppConditionEgressVSwitchTransport); + +#if(NTDDI_VERSION >= NTDDI_WINBLUE) + +const UINT16 INBOUND_TRANSPORT_FAST_CONDITIONS_COUNT = 0; +const UINT16 OUTBOUND_TRANSPORT_FAST_CONDITIONS_COUNT = 0; +const UINT16 INBOUND_MAC_FRAME_NATIVE_FAST_CONDITIONS_COUNT = 0; +const UINT16 OUTBOUND_MAC_FRAME_NATIVE_FAST_CONDITIONS_COUNT = 0; + +#endif /// (NTDDI_VERSION >= NTDDI_WINBLUE) +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) +#endif /// (NTDDI_VERSION >= NTDDI_VISTA) + +#endif /// WFP_SAMPLER_WFP_ARRAYS_H diff --git a/network/trans/WFPSampler/lib/HelperFunctions_FwpmCallout.cpp b/network/trans/WFPSampler/lib/HelperFunctions_FwpmCallout.cpp new file mode 100644 index 000000000..826e69e36 --- /dev/null +++ b/network/trans/WFPSampler/lib/HelperFunctions_FwpmCallout.cpp @@ -0,0 +1,387 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_FwpmCallout.cpp +// +// Abstract: +// This module contains functions which assist in actions pertaining to FWPM_CALLOUT objects. +// +// Naming Convention: +// +// +// +// i.e. +// +// +// { +// - Function is likely visible to other modules. +// } +// +// { +// Hlpr - Function is from HelperFunctions_* Modules. +// } +// +// { +// FwpmCallout - Function pertains to FWPM_CALLOUT objects. +// } +// +// { +// Add - Function adds and object to BFE. +// Create - Function allocates memory for an object. +// Delete - Function deletes an object from BFE. +// Destroy - Function frees memory for an object +// Enum - Function returns list of requested objects. +// Remove - Function deletes objects from BFE. +// } +// +// { +// All - Function acts on all FWPM_CALLOUT objects. +// ByKey - Function takes a runtime ID. +// EnumHandle - Function acts on the enumeration Handle. +// } +// +// Private Functions: +// +// Public Functions: +// HlprFwpmCalloutAdd(), +// HlprFwpmCalloutCreateEnumHandle(), +// HlprFwpmCalloutDeleteByKey(), +// HlprFwpmCalloutDestroyEnumHandle(), +// HlprFwpmCalloutEnum(), +// HlprFwpmCalloutRemoveAll(), +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "HelperFunctions_Include.h" /// . + +/** + @helper_function="HlprFwpmCalloutDeleteByKey" + + Purpose: Wrapper for the FwpmCalloutDeleteByKey API
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/AA364016.aspx
+*/ +_Success_(return == NO_ERROR) +UINT32 HlprFwpmCalloutDeleteByKey(_In_ const HANDLE engineHandle, + _In_ const GUID* pCalloutKey) +{ + UINT32 status = NO_ERROR; + + if(engineHandle && + pCalloutKey) + { + status = FwpmCalloutDeleteByKey(engineHandle, + pCalloutKey); + if(status != NO_ERROR) + { + if(status != FWP_E_IN_USE && + status != FWP_E_BUILTIN_OBJECT && + status != FWP_E_CALLOUT_NOT_FOUND) + HlprLogError(L"HlprFwpmCalloutDeleteByKey : FwpmCalloutDeleteByKey() [status: %#x]", + status); + else + { + HlprLogInfo(L"HlprFwpmCalloutDeleteByKey : FwpmCalloutDeleteByKey() [status: %#x]", + status); + + status = NO_ERROR; + } + } + } + else + { + status = ERROR_INVALID_PARAMETER; + + HlprLogError(L"HlprFwpmCalloutDeleteByKey() [status: %#x][engineHandle: %#p][pCalloutKey: %#p]", + status, + engineHandle, + pCalloutKey); + } + + return status; +} + +/** + @helper_function="HlprFwpmCalloutAdd" + + Purpose: Wrapper for the FwpmCalloutAdd API
+
+ Notes: Callout ID is written to pCallout->calloutId.
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/AA364010.aspx
+*/ +_Success_(return == NO_ERROR) +UINT32 HlprFwpmCalloutAdd(_In_ const HANDLE engineHandle, + _Inout_ FWPM_CALLOUT* pCallout) +{ + UINT32 status = NO_ERROR; + + if(engineHandle && + pCallout) + { + status = FwpmCalloutAdd(engineHandle, + pCallout, + 0, + &(pCallout->calloutId)); + if(status != NO_ERROR) + { + if(status == FWP_E_ALREADY_EXISTS) + { + HlprLogInfo(L"Callout Already Exists"); + + status = NO_ERROR; + } + else + HlprLogError(L"HlprFwpmCalloutAdd : FwpmCalloutAdd() [status: %#x]", + status); + } + } + else + { + status = ERROR_INVALID_PARAMETER; + + HlprLogError(L"HlprFwpmCalloutAdd() [status: %#x][engineHandle: %#p][pCallout: %#p]", + status, + engineHandle, + pCallout); + } + + return status; +} + +/** + @helper_function="HlprFwpmCalloutRemoveAll" + + Purpose: Remove all callouts associated with the specified provider.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 HlprFwpmCalloutRemoveAll(_In_opt_ HANDLE* pEngineHandle, + _In_ const GUID* pProviderKey) +{ + UINT32 status = NO_ERROR; + HANDLE engineHandle = 0; + BOOLEAN isLocal = FALSE; + + if(pEngineHandle && + *pEngineHandle) + engineHandle = *pEngineHandle; + else + { + status = HlprFwpmEngineOpen(&engineHandle); + HLPR_BAIL_ON_FAILURE(status); + + isLocal = TRUE; + } + + HANDLE enumHandle = 0; + FWPM_CALLOUT** ppCallouts = 0; + UINT32 calloutCount = 0; + FWPM_CALLOUT_ENUM_TEMPLATE enumTemplate; + + enumTemplate.providerKey = (GUID*)pProviderKey; + + status = HlprFwpmCalloutCreateEnumHandle(engineHandle, + &enumTemplate, + &enumHandle); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmCalloutEnum(engineHandle, + enumHandle, + 0xFFFFFFFF, + &ppCallouts, + &calloutCount); + if(ppCallouts && + calloutCount) + { + for(UINT32 calloutIndex = 0; + calloutIndex < calloutCount; + calloutIndex++) + { + HlprFwpmCalloutDeleteByKey(engineHandle, + &(ppCallouts[calloutIndex]->calloutKey)); + } + + FwpmFreeMemory((VOID**)&ppCallouts); + } + + HlprFwpmCalloutDestroyEnumHandle(engineHandle, + &enumHandle); + + HLPR_BAIL_LABEL: + + if(engineHandle && + isLocal) + HlprFwpmEngineClose(&engineHandle); + + return status; +} + +/** + @helper_function="HlprFwpmCalloutEnum" + + Purpose: Wrapper for the FwpmCalloutEnum API
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/AA364020.aspx
+*/ +_At_(*pppEntries, _Pre_ _Null_) +_When_(return != NO_ERROR, _At_(*pppEntries, _Post_ _Null_)) +_Success_(return == NO_ERROR) +UINT32 HlprFwpmCalloutEnum(_In_ const HANDLE engineHandle, + _In_ const HANDLE enumHandle, + _In_ const UINT32 numEntriesRequested, + _Outptr_result_buffer_maybenull_(*pNumEntriesReturned) FWPM_CALLOUT*** pppEntries, + _Out_ UINT32* pNumEntriesReturned) +{ + UINT32 status = NO_ERROR; + + if(engineHandle && + enumHandle && + numEntriesRequested && + pppEntries && + pNumEntriesReturned) + { + status = FwpmCalloutEnum(engineHandle, + enumHandle, + numEntriesRequested, + pppEntries, + pNumEntriesReturned); + if(status != NO_ERROR && + status != FWP_E_CALLOUT_NOT_FOUND && + status != FWP_E_NOT_FOUND) + HlprLogError(L"HlprFwpmCalloutEnum : FwpmCalloutEnum() [status: %#x]", + status); + } + else + { + if(pppEntries) + *pppEntries = 0; + + if(pNumEntriesReturned) + *pNumEntriesReturned = 0; + + status = ERROR_INVALID_PARAMETER; + + HlprLogError(L"HlprFwpmCalloutEnum() [status: %#x][engineHandle: %#p][enumHandle: %#p][numEntriesRequested: %d][pppEntries: %#p][pNumEntriesReturned: %#p]", + status, + engineHandle, + enumHandle, + numEntriesRequested, + pppEntries, + pNumEntriesReturned); + } + + return status; +} + +/** + @helper_function="HlprFwpmCalloutDestroyEnumHandle" + + Purpose: Wrapper for the FwpmCalloutDestroyEnumHandle API
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/AA364017.aspx
+*/ +_When_(return != NO_ERROR, _At_(*pEnumHandle, _Post_ _Notnull_)) +_When_(return == NO_ERROR, _At_(*pEnumHandle, _Post_ _Null_)) +_Success_(return == NO_ERROR) +UINT32 HlprFwpmCalloutDestroyEnumHandle(_In_ const HANDLE engineHandle, + _Inout_ HANDLE* pEnumHandle) +{ + UINT32 status = NO_ERROR; + + if(engineHandle && + pEnumHandle) + { + if(*pEnumHandle) + { + status = FwpmCalloutDestroyEnumHandle(engineHandle, + *pEnumHandle); + if(status != NO_ERROR) + { + HlprLogError(L"HlprFwpmCalloutDestroyEnumHandle : FwpmCalloutDestroyEnumHandle() [status: %#x]", + status); + + HLPR_BAIL; + } + + *pEnumHandle = 0; + } + } + else + { + status = ERROR_INVALID_PARAMETER; + + HlprLogError(L"HlprFwpmCalloutDestroyEnumHandle() [status: %#x][engineHandle: %#p][pEnumHandle: %#p]", + status, + engineHandle, + pEnumHandle); + } + + HLPR_BAIL_LABEL: + + return status; +} + +/** + @helper_function="HlprFwpmCalloutCreateEnumHandle" + + Purpose: Wrapper for the FwpmCalloutCreateEnumHandle API
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/AA364012.aspx
+*/ +_When_(return != NO_ERROR, _At_(*pEnumHandle, _Post_ _Null_)) +_When_(return == NO_ERROR, _At_(*pEnumHandle, _Post_ _Notnull_)) +_Success_(return == NO_ERROR) +UINT32 HlprFwpmCalloutCreateEnumHandle(_In_ const HANDLE engineHandle, + _In_opt_ const FWPM_CALLOUT_ENUM_TEMPLATE* pEnumTemplate, + _Out_ HANDLE* pEnumHandle) +{ + UINT32 status = NO_ERROR; + + if(engineHandle && + pEnumHandle) + { + status = FwpmCalloutCreateEnumHandle(engineHandle, + pEnumTemplate, + pEnumHandle); + if(status != NO_ERROR) + { + *pEnumHandle = 0; + + HlprLogError(L"HlprFwpmCalloutCreateEnumHandle : FwpmCalloutCreateEnumHandle() [status: %#x]", + status); + } + } + else + { + status = ERROR_INVALID_PARAMETER; + + HlprLogError(L"HlprFwpmCalloutCreateEnumHandle() [status: %#x][engineHandle: %#p][pEnumHandle: %#p]", + status, + engineHandle, + pEnumHandle); + } + + return status; +} diff --git a/network/trans/WFPSampler/lib/HelperFunctions_FwpmCallout.h b/network/trans/WFPSampler/lib/HelperFunctions_FwpmCallout.h new file mode 100644 index 000000000..c250a0629 --- /dev/null +++ b/network/trans/WFPSampler/lib/HelperFunctions_FwpmCallout.h @@ -0,0 +1,59 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_FwpmCallout.h +// +// Abstract: +// This module contains prototypes for functions which assist in actions pertaining to +// FWPM_CALLOUT objects. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef HELPERFUNCTIONS_FWPM_CALLOUT_H +#define HELPERFUNCTIONS_FWPM_CALLOUT_H + +_Success_(return == NO_ERROR) +UINT32 HlprFwpmCalloutDeleteByKey(_In_ const HANDLE engineHandle, + _In_ const GUID* pCalloutKey); + +_Success_(return == NO_ERROR) +UINT32 HlprFwpmCalloutAdd(_In_ const HANDLE engineHandle, + _Inout_ FWPM_CALLOUT* pCallout); + +_Success_(return == NO_ERROR) +UINT32 HlprFwpmCalloutRemoveAll(_In_opt_ HANDLE* pEngineHandle, + _In_ const GUID* pProviderKey); + +_At_(*pppEntries, _Pre_ _Null_) +_When_(return != NO_ERROR, _At_(*pppEntries, _Post_ _Null_)) +_Success_(return == NO_ERROR) +UINT32 HlprFwpmCalloutEnum(_In_ const HANDLE engineHandle, + _In_ const HANDLE enumHandle, + _In_ const UINT32 numEntriesRequested, + _Outptr_result_buffer_maybenull_(*pNumEntriesReturned) FWPM_CALLOUT*** pppEntries, + _Out_ UINT32* pNumEntriesReturned); + +_When_(return != NO_ERROR, _At_(*pEnumHandle, _Post_ _Notnull_)) +_When_(return == NO_ERROR, _At_(*pEnumHandle, _Post_ _Null_)) +_Success_(return == NO_ERROR) +UINT32 HlprFwpmCalloutDestroyEnumHandle(_In_ const HANDLE engineHandle, + _Inout_ HANDLE* pEnumHandle); + +_When_(return != NO_ERROR, _At_(*pEnumHandle, _Post_ _Null_)) +_When_(return == NO_ERROR, _At_(*pEnumHandle, _Post_ _Notnull_)) +_Success_(return == NO_ERROR) +UINT32 HlprFwpmCalloutCreateEnumHandle(_In_ const HANDLE engineHandle, + _In_opt_ const FWPM_CALLOUT_ENUM_TEMPLATE* pEnumTemplate, + _Out_ HANDLE* pEnumHandle); + +#endif /// HELPERFUNCTIONS_FWPM_CALLOUT_H \ No newline at end of file diff --git a/network/trans/WFPSampler/lib/HelperFunctions_FwpmEngine.cpp b/network/trans/WFPSampler/lib/HelperFunctions_FwpmEngine.cpp new file mode 100644 index 000000000..4ea15a8a8 --- /dev/null +++ b/network/trans/WFPSampler/lib/HelperFunctions_FwpmEngine.cpp @@ -0,0 +1,126 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_FwpmEngine.cpp +// +// Abstract: +// This module contains functions which assist in actions pertaining to BFE engine objects. +// +// Naming Convention: +// +// +// +// i.e. +// +// +// { +// - Function is likely visible to other modules. +// } +// +// { +// Hlpr - Function is from HelperFunctions_* Modules. +// } +// +// { +// FwpmEngine - Function pertains to BFE engine objects. +// } +// +// { +// Close - Function destroys a handle. +// Open - Function creates a handle. +// } +// +// { +// } +// +// Private Functions: +// +// Public Functions: +// HlprFwpmEngineClose(), +// HlprFwpmEngineOpen() +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "HelperFunctions_Include.h" /// . + +/** + @helper_function="HlprFwpmEngineOpen" + + Purpose: Wrapper for the FwpmEngineClose API.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/AA364034.aspx
+*/ +_When_(return == NO_ERROR, _At_(*pEngineHandle, _Post_ _Null_)) +_Success_(return == NO_ERROR) +UINT32 HlprFwpmEngineClose(_Inout_ HANDLE* pEngineHandle) +{ + ASSERT(pEngineHandle); + + UINT32 status = NO_ERROR; + + if(*pEngineHandle) + { + status = FwpmEngineClose(*pEngineHandle); + if(status != NO_ERROR) + { + HlprLogError(L"HlprFwpmEngineClose : FwpmEngineClose() [status: %#x]", + status); + + HLPR_BAIL; + } + + *pEngineHandle = 0; + } + + HLPR_BAIL_LABEL: + + return status; +} + +/** + @helper_function="HlprFwpmEngineOpen" + + Purpose: Wrapper for the FwpmEngineOpen API.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/AA364040.aspx
+*/ +_At_(*pEngineHandle, _Pre_ _Null_) +_When_(return != NO_ERROR, _At_(*pEngineHandle, _Post_ _Null_)) +_When_(return == NO_ERROR, _At_(*pEngineHandle, _Post_ _Notnull_)) +_Success_(return == NO_ERROR) +UINT32 HlprFwpmEngineOpen(_Out_ HANDLE* pEngineHandle, + _In_ const UINT32 sessionFlags) /* FWPM_SESSION_FLAG_NONDYNAMIC */ +{ + ASSERT(pEngineHandle); + + UINT32 status = NO_ERROR; + FWPM_SESSION session = {0}; + + session.displayData.name = L"WFPSampler's User Mode Session"; + session.flags = sessionFlags; + + status = FwpmEngineOpen(0, + RPC_C_AUTHN_WINNT, + 0, + &session, + pEngineHandle); + if(status != NO_ERROR) + HlprLogError(L"HlprFwpmEngineOpen : FwpmEngineOpen() [status: %#x]", + status); + + return status; +} diff --git a/network/trans/WFPSampler/lib/HelperFunctions_FwpmEngine.h b/network/trans/WFPSampler/lib/HelperFunctions_FwpmEngine.h new file mode 100644 index 000000000..d51dea4a5 --- /dev/null +++ b/network/trans/WFPSampler/lib/HelperFunctions_FwpmEngine.h @@ -0,0 +1,38 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_FwpmEngine.h +// +// Abstract: +// This module contains prototypes for functions which assist in actions pertaining to BFE +// engine objects. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef HELPERFUNCTIONS_FWPM_ENGINE_H +#define HELPERFUNCTIONS_FWPM_ENGINE_H + +#define FWPM_SESSION_FLAG_NONDYNAMIC 0 + +_When_(return == NO_ERROR, _At_(*pEngineHandle, _Post_ _Null_)) +_Success_(return == NO_ERROR) +UINT32 HlprFwpmEngineClose(_Inout_ HANDLE* pEngineHandle); + +_At_(*pEngineHandle, _Pre_ _Null_) +_When_(return != NO_ERROR, _At_(*pEngineHandle, _Post_ _Null_)) +_When_(return == NO_ERROR, _At_(*pEngineHandle, _Post_ _Notnull_)) +_Success_(return == NO_ERROR) +UINT32 HlprFwpmEngineOpen(_Out_ HANDLE* pEngineHandle, + _In_ const UINT32 sessionFlags = FWPM_SESSION_FLAG_NONDYNAMIC); + +#endif /// HELPERFUNCTIONS_FWPM_ENGINE_H \ No newline at end of file diff --git a/network/trans/WFPSampler/lib/HelperFunctions_FwpmFilter.cpp b/network/trans/WFPSampler/lib/HelperFunctions_FwpmFilter.cpp new file mode 100644 index 000000000..16d14fe56 --- /dev/null +++ b/network/trans/WFPSampler/lib/HelperFunctions_FwpmFilter.cpp @@ -0,0 +1,1651 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_FwpmFilter.cpp +// +// Abstract: +// This module contains functions which assist in actions pertaining to FWPM_FILTER objects. +// +// Naming Convention: +// +// +// +// i.e. +// +// +// { +// - Function is likely visible to other modules. +// Prv - Function is private to this module. +// } +// +// { +// Hlpr - Function is from HelperFunctions_* Modules. +// } +// +// { +// FwpByteBlob - Function pertains to FWP_BYTE_BLOB objects. +// FwpTokenInformation - Function pertains to FWP_TOKEN_INFORMATION objects. +// FwpV4AddrMask - Function pertains to FWP_V4_ADDR_MASK objects. +// FwpV6AddrMask - Function pertains to FWP_V6_ADDR_MASK objects. +// FwpValue - Function pertains to FWP_VALUE objects. +// FwpRange - Function pertains to FWP_RANGE objects. +// FwpmFilter - Function pertains to FWPM_FILTER objects. +// FwpmFilterCondition - Function pertains to FWPM_FILTER_CONDITION objects. +// } +// +// { +// Add - Function adds an object. +// Create - Function allocates memory for an object. +// Delete - Function deletes an object. +// Destroy - Function frees memory for an object. +// Enum - Function returns list of requested objects. +// IsValid - Function compares values. +// Purge - Function cleans up values. +// Remove - Function deletes objects. +// } +// +// { +// All - Functions acts on all of WFPSampler's filters. +// ByKey - Function acts off of the object's GUID. +// EnumHandle - Function acts on the enumeration handle. +// ForLayer - Function acts based on provided layer value. +// FromPacketData - Function acts off of the provided PACKET_DATA. +// } +// +// Private Functions: +// PrvFwpByteBlobDestroy(), +// PrvFwpByteBlobPurge(), +// PrvFwpRangeDestroy(), +// PrvFwpTokenInformationDestroy(), +// PrvFwpTokenInformationPurge(), +// PrvFwpV4AddrAndMaskDestroy(), +// PrvFwpV6AddrAndMaskDestroy(), +// PrvFwpValuePurge(), +// +// Public Functions: +// HlprFwpmFilterAdd(), +// HlprFwpmFilterConditionCreateFromFrameData(), +// HlprFwpmFilterConditionCreateFromPacketData(), +// HlprFwpmFilterConditionCreateFromScenarioData(), +// HlprFwpmFilterConditionDestroy(), +// HlprFwpmFilterConditionIsValidForLayer(), +// HlprFwpmFilterConditionPrune(), +// HlprFwpmFilterConditionPurge(), +// HlprFwpmFilterCreateEnumHandle(), +// HlprFwpmFilterDeleteByKey(), +// HlprFwpmFilterDestroyEnumHandle(), +// HlprFwpmFilterEnum(), +// HlprFwpmFilterPurge(), +// HlprFwpmFilterRemoveAll(), +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Add HlprFwpmFilterConditionPrune +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "HelperFunctions_Include.h" /// . + +/** + @private_function="PrvFwpByteBlobPurge" + + Purpose: Cleanup an FWP_BYTE_BLOB.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA364911.aspx
+*/ +inline VOID PrvFwpByteBlobPurge(_Inout_ FWP_BYTE_BLOB* pBlob) +{ + ASSERT(pBlob); + + HLPR_DELETE_ARRAY(pBlob->data); + + return; +} + +/** + @private_function="PrvFwpByteBlobDestroy" + + Purpose: Cleanup and free an FWP_BYTE_BLOB.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA364911.aspx
+*/ +_At_(*ppBlob, _Post_ _Null_) +VOID PrvFwpByteBlobDestroy(_Inout_ FWP_BYTE_BLOB** ppBlob) +{ + ASSERT(ppBlob); + + if(*ppBlob) + PrvFwpByteBlobPurge(*ppBlob); + + HLPR_DELETE(*ppBlob); + + return; +} + +/** + @private_function="PrvFwpTokenInformationPurge" + + Purpose: Cleanup an FWP_TOKEN_INFORMATION.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/BB485774.aspx
+*/ +inline VOID PrvFwpTokenInformationPurge(_Inout_ FWP_TOKEN_INFORMATION* pTokenInfo) +{ + ASSERT(pTokenInfo); + + for(UINT32 i = 0; + i < pTokenInfo->sidCount; + i++) + { + HLPR_DELETE_ARRAY(pTokenInfo->sids[i].Sid); + } + + HLPR_DELETE_ARRAY(pTokenInfo->sids); + + for(UINT32 i = 0; + i < pTokenInfo->restrictedSidCount; + i++) + { + HLPR_DELETE_ARRAY(pTokenInfo->restrictedSids[i].Sid); + } + + HLPR_DELETE_ARRAY(pTokenInfo->restrictedSids); + + return; +} + +/** + @private_function="PrvFwpTokenInformationDestroy" + + Purpose: Cleanup and free an FWP_TOKEN_INFORMATION.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/BB485774.aspx
+*/ +_At_(*ppTokenInfo, _Post_ _Null_) +VOID PrvFwpTokenInformationDestroy(_Inout_ FWP_TOKEN_INFORMATION** ppTokenInfo) +{ + ASSERT(ppTokenInfo); + + if(*ppTokenInfo) + PrvFwpTokenInformationPurge(*ppTokenInfo); + + HLPR_DELETE(*ppTokenInfo); + + return; +} + +/** + @private_function="PrvFwpV4AddrAndMaskDestroy" + + Purpose: Cleanup and free an FWP_V4_ADDR_AND_MASK.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA364950.aspx
+*/ +_At_(*ppAddrMask, _Post_ _Null_) +VOID PrvFwpV4AddrAndMaskDestroy(_Inout_ FWP_V4_ADDR_AND_MASK** ppAddrMask) +{ + ASSERT(ppAddrMask); + + HLPR_DELETE(*ppAddrMask); + + return; +} + +/** + @private_function="PrvFwpV6AddrAndMaskDestroy" + + Purpose: Cleanup and free an FWP_V6_ADDR_AND_MASK.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA364954.aspx
+*/ +_At_(*ppAddrMask, _Post_ _Null_) +VOID PrvFwpV6AddrAndMaskDestroy(_Inout_ FWP_V6_ADDR_AND_MASK** ppAddrMask) +{ + ASSERT(ppAddrMask); + + HLPR_DELETE(*ppAddrMask); + + return; +} + +/** + @private_function="PrvFwpValuePurge" + + Purpose: Cleanup an FWP_VALUE.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA364956.aspx
+*/ +VOID PrvFwpValuePurge(_Inout_ FWP_VALUE* pValue) +{ + ASSERT(pValue); + + switch(pValue->type) + { + case FWP_UINT64: + { + HLPR_DELETE(pValue->uint64); + + break; + } + case FWP_INT64: + { + HLPR_DELETE(pValue->int64); + + break; + } + case FWP_DOUBLE: + { + HLPR_DELETE(pValue->double64); + + break; + } + case FWP_BYTE_ARRAY16_TYPE: + { + HLPR_DELETE(pValue->byteArray16); + + break; + } + case FWP_BYTE_BLOB_TYPE: + { + PrvFwpByteBlobDestroy(&(pValue->byteBlob)); + + break; + } + case FWP_SID: + { + HLPR_DELETE_ARRAY(pValue->sid); + + break; + } + case FWP_SECURITY_DESCRIPTOR_TYPE: + { + PrvFwpByteBlobDestroy(&(pValue->sd)); + + break; + } + case FWP_TOKEN_INFORMATION_TYPE: + { + PrvFwpTokenInformationDestroy(&(pValue->tokenInformation)); + + break; + } + case FWP_TOKEN_ACCESS_INFORMATION_TYPE: + { + PrvFwpByteBlobDestroy(&(pValue->tokenAccessInformation)); + + break; + } + case FWP_UNICODE_STRING_TYPE: + { + HLPR_DELETE_ARRAY(pValue->unicodeString); + + break; + } + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + case FWP_BYTE_ARRAY6_TYPE: + { + HLPR_DELETE(pValue->byteArray6); + + break; + } + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + default: + { + pValue->uint64 = 0; + + break; + } + } + + pValue->type = FWP_EMPTY; + + return; +} + +/** + @private_function="PrvFwpRangeDestroy" + + Purpose: Cleanup and free an FWP_RANGE.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA814108.aspx
+*/ +_At_(*ppRange, _Post_ _Null_) +_Success_(*ppRange == 0) +VOID PrvFwpRangeDestroy(_Inout_ FWP_RANGE** ppRange) +{ + ASSERT(ppRange); + + if(*ppRange) + { + PrvFwpValuePurge(&((*ppRange)->valueLow)); + + PrvFwpValuePurge(&((*ppRange)->valueHigh)); + } + + HLPR_DELETE(*ppRange); + + return; +} +/** + @helper_function="HlprFwpmFilterConditionPrune" + + Purpose: Remove duplicate conditions from the supplied filterCondition array.
+
+ Notes: Due to a bug in WFP, filter enumeration cannot handle multiple consecutive + conditions.
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA364268.aspx
+*/ +VOID HlprFwpmFilterConditionPrune(_In_reads_(numFilterConditions) const FWPM_FILTER_CONDITION* pFilterConditions, + _Inout_ UINT32 numFilterConditions, + _Inout_updates_all_(*pNewNumFilterConditions) FWPM_FILTER_CONDITION** ppNewFilterConditions, + _Inout_ UINT32* pNewNumFilterConditions) +{ + UINT32 numConditions = numFilterConditions; + + if(numFilterConditions >= 2) + { + for(UINT32 index = 0; + (index + 1 ) < numFilterConditions; + index++) + { + if(pFilterConditions[index].fieldKey == pFilterConditions[index + 1].fieldKey) + numConditions--; + } + + if(numConditions != numFilterConditions) + { + UINT32 status = 0; + FWPM_FILTER_CONDITION* pNewFilterConditions = 0; + UINT32 newIndex = 0; + + HLPR_NEW_ARRAY(pNewFilterConditions, + FWPM_FILTER_CONDITION, + numConditions); + HLPR_BAIL_ON_ALLOC_FAILURE(pNewFilterConditions, + status); + + for(UINT32 index = 0; + index < numFilterConditions && + newIndex < numConditions; + index++) + { + // Always copy the first condition ... + if(index == 0) + { + RtlCopyMemory(&(pNewFilterConditions[newIndex]), + &(pFilterConditions[index]), + sizeof(FWPM_FILTER_CONDITION)); + + newIndex++; + } + else + { + /// ... then copy only if the current condition is not the same as the previous + /// condition. + if(pFilterConditions[index].fieldKey != pFilterConditions[index - 1].fieldKey) + { + RtlCopyMemory(&(pNewFilterConditions[newIndex]), + &(pFilterConditions[index]), + sizeof(FWPM_FILTER_CONDITION)); + + newIndex++; + } + } + } + + *ppNewFilterConditions = pNewFilterConditions; + + *pNewNumFilterConditions = numConditions; + } + } + + HLPR_BAIL_LABEL: + + return; +} + +/** + @helper_function="HlprFwpmFilterConditionPurge" + + Purpose: Cleanup an FWPM_FILTER_CONDITION.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA364268.aspx
+*/ +_Success_(pFilterCondition->conditionValue.type == FWP_EMPTY && + pFilterCondition->conditionValue.uint64 == 0) +VOID HlprFwpmFilterConditionPurge(_Inout_ FWPM_FILTER_CONDITION* pFilterCondition) +{ + ASSERT(pFilterCondition); + + FWP_CONDITION_VALUE* pValue = &(pFilterCondition->conditionValue); + + switch(pValue->type) + { + case FWP_UINT64: + { + HLPR_DELETE(pValue->uint64); + + break; + } + case FWP_INT64: + { + HLPR_DELETE(pValue->int64); + + break; + } + case FWP_DOUBLE: + { + HLPR_DELETE(pValue->double64); + + break; + } + case FWP_BYTE_ARRAY16_TYPE: + { + HLPR_DELETE(pValue->byteArray16); + + break; + } + case FWP_BYTE_BLOB_TYPE: + { + PrvFwpByteBlobDestroy(&(pValue->byteBlob)); + + break; + } + case FWP_SID: + { + HLPR_DELETE_ARRAY(pValue->sid); + + break; + } + case FWP_SECURITY_DESCRIPTOR_TYPE: + { + PrvFwpByteBlobDestroy(&(pValue->sd)); + + break; + } + case FWP_TOKEN_INFORMATION_TYPE: + { + PrvFwpTokenInformationDestroy(&(pValue->tokenInformation)); + + break; + } + case FWP_TOKEN_ACCESS_INFORMATION_TYPE: + { + PrvFwpByteBlobDestroy(&(pValue->tokenAccessInformation)); + + break; + } + case FWP_UNICODE_STRING_TYPE: + { + HLPR_DELETE_ARRAY(pValue->unicodeString); + + break; + } + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + case FWP_BYTE_ARRAY6_TYPE: + { + HLPR_DELETE(pValue->byteArray6); + + break; + } + +#endif // (NTDDI_VERSION >= NTDDI_WIN7) + + case FWP_SINGLE_DATA_TYPE_MAX: + { + break; + } + case FWP_V4_ADDR_MASK: + { + PrvFwpV4AddrAndMaskDestroy(&(pValue->v4AddrMask)); + + break; + } + case FWP_V6_ADDR_MASK: + { + PrvFwpV6AddrAndMaskDestroy(&(pValue->v6AddrMask)); + + break; + } + case FWP_RANGE_TYPE: + { + PrvFwpRangeDestroy(&(pValue->rangeValue)); + + break; + } + case FWP_DATA_TYPE_MAX: + { + break; + } + default: + { + pValue->uint64 = 0; + + break; + } + } + + pValue->type = FWP_EMPTY; + + return; +} + +/** + @helper_function="HlprFwpmFilterConditionDestroy" + + Purpose: Cleanup and free an (array of) FWPM_FILTER_CONDITION.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA364268.aspx
+*/ +_At_(*ppFilterConditions, _Post_ _Null_) +_Success_(*ppFilterConditions == 0) +VOID HlprFwpmFilterConditionDestroy(_Inout_updates_all_(numFilterConditions) FWPM_FILTER_CONDITION** ppFilterConditions, + _In_ const UINT32 numFilterConditions) /* 1 */ +{ + ASSERT(ppFilterConditions); + ASSERT(numFilterConditions); + + for(UINT32 conditionIndex = 0; + conditionIndex < numFilterConditions; + conditionIndex++) + { + HlprFwpmFilterConditionPurge(&((*ppFilterConditions)[conditionIndex])); + } + + if(numFilterConditions > 1) + { + HLPR_DELETE_ARRAY(*ppFilterConditions); + } + else + { + HLPR_DELETE(*ppFilterConditions); + } + + return; +} + +/** + @helper_function="HlprFwpmFilterConditionCreate" + + Purpose: Allocate an (array of) FWPM_FILTER_CONDITION.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA364268.aspx
+*/ +_Success_(return == NO_ERROR) +UINT32 HlprFwpmFilterConditionCreate(_Inout_updates_all_(numFilterConditions) FWPM_FILTER_CONDITION** ppFilterConditions, + _In_ const UINT32 numFilterConditions) /* 1 */ +{ + UINT32 status = NO_ERROR; + + if(ppFilterConditions && + numFilterConditions) + { + if(numFilterConditions > 1) + { + HLPR_NEW_ARRAY(*ppFilterConditions, + FWPM_FILTER_CONDITION, + numFilterConditions); + } + else + { + HLPR_NEW(*ppFilterConditions, + FWPM_FILTER_CONDITION); + } + } + else + { + status = ERROR_INVALID_PARAMETER; + + HlprLogError(L"HlprFwpmFilterConditionCreate() [status: %#x][pFilterConditions: %#p][numFilterConditions: %d]", + status, + ppFilterConditions, + numFilterConditions); + } + + return status; +} + +/** + @helper_function="HlprFwpmFilterConditionIsValidForLayer" + + Purpose: Determine if the provided filtering condition is available at the provided layer.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA363996.aspx
+*/ +BOOLEAN HlprFwpmFilterConditionIsValidForLayer(_In_ const GUID* pLayerKey, + _In_ const GUID* pConditionKey) +{ + ASSERT(pLayerKey); + ASSERT(pConditionKey); + + BOOLEAN isValid = FALSE; + + if(*pLayerKey == FWPM_LAYER_INBOUND_IPPACKET_V4 || + *pLayerKey == FWPM_LAYER_INBOUND_IPPACKET_V4_DISCARD || + *pLayerKey == FWPM_LAYER_INBOUND_IPPACKET_V6 || + *pLayerKey == FWPM_LAYER_INBOUND_IPPACKET_V6_DISCARD) + { + for(UINT32 conditionIndex = 0; + conditionIndex < INBOUND_IPPACKET_CONDITIONS_COUNT; + conditionIndex++) + { + if(HlprGUIDsAreEqual(pConditionKey, + ppConditionInboundIPPacket[conditionIndex])) + { + isValid = TRUE; + + break; + } + } + } + else if(*pLayerKey == FWPM_LAYER_OUTBOUND_IPPACKET_V4 || + *pLayerKey == FWPM_LAYER_OUTBOUND_IPPACKET_V4_DISCARD || + *pLayerKey == FWPM_LAYER_OUTBOUND_IPPACKET_V6 || + *pLayerKey == FWPM_LAYER_OUTBOUND_IPPACKET_V6_DISCARD) + { + for(UINT32 conditionIndex = 0; + conditionIndex < OUTBOUND_IPPACKET_CONDITIONS_COUNT; + conditionIndex++) + { + if(HlprGUIDsAreEqual(pConditionKey, + ppConditionOutboundIPPacket[conditionIndex])) + { + isValid = TRUE; + + break; + } + } + } + else if(*pLayerKey == FWPM_LAYER_IPFORWARD_V4 || + *pLayerKey == FWPM_LAYER_IPFORWARD_V4_DISCARD || + *pLayerKey == FWPM_LAYER_IPFORWARD_V6 || + *pLayerKey == FWPM_LAYER_IPFORWARD_V6_DISCARD) + { + for(UINT32 conditionIndex = 0; + conditionIndex < IPFORWARD_CONDITIONS_COUNT; + conditionIndex++) + { + if(HlprGUIDsAreEqual(pConditionKey, + ppConditionIPForward[conditionIndex])) + { + isValid = TRUE; + + break; + } + } + } + else if(*pLayerKey == FWPM_LAYER_INBOUND_TRANSPORT_V4 || + *pLayerKey == FWPM_LAYER_INBOUND_TRANSPORT_V4_DISCARD || + *pLayerKey == FWPM_LAYER_INBOUND_TRANSPORT_V6 || + *pLayerKey == FWPM_LAYER_INBOUND_TRANSPORT_V6_DISCARD) + { + for(UINT32 conditionIndex = 0; + conditionIndex < INBOUND_TRANSPORT_CONDITIONS_COUNT; + conditionIndex++) + { + if(HlprGUIDsAreEqual(pConditionKey, + ppConditionInboundTransport[conditionIndex])) + { + isValid = TRUE; + + break; + } + } + } + else if(*pLayerKey == FWPM_LAYER_OUTBOUND_TRANSPORT_V4 || + *pLayerKey == FWPM_LAYER_OUTBOUND_TRANSPORT_V4_DISCARD || + *pLayerKey == FWPM_LAYER_OUTBOUND_TRANSPORT_V6 || + *pLayerKey == FWPM_LAYER_OUTBOUND_TRANSPORT_V6_DISCARD) + { + for(UINT32 conditionIndex = 0; + conditionIndex < OUTBOUND_TRANSPORT_CONDITIONS_COUNT; + conditionIndex++) + { + if(HlprGUIDsAreEqual(pConditionKey, + ppConditionOutboundTransport[conditionIndex])) + { + isValid = TRUE; + + break; + } + } + } + else if(*pLayerKey == FWPM_LAYER_STREAM_V4 || + *pLayerKey == FWPM_LAYER_STREAM_V4_DISCARD || + *pLayerKey == FWPM_LAYER_STREAM_V6 || + *pLayerKey == FWPM_LAYER_STREAM_V6_DISCARD) + { + for(UINT32 conditionIndex = 0; + conditionIndex < STREAM_CONDITIONS_COUNT; + conditionIndex++) + { + if(HlprGUIDsAreEqual(pConditionKey, + ppConditionStream[conditionIndex])) + { + isValid = TRUE; + + break; + } + } + } + else if(*pLayerKey == FWPM_LAYER_DATAGRAM_DATA_V4 || + *pLayerKey == FWPM_LAYER_DATAGRAM_DATA_V4_DISCARD || + *pLayerKey == FWPM_LAYER_DATAGRAM_DATA_V6 || + *pLayerKey == FWPM_LAYER_DATAGRAM_DATA_V6_DISCARD) + { + for(UINT32 conditionIndex = 0; + conditionIndex < DATAGRAM_DATA_CONDITIONS_COUNT; + conditionIndex++) + { + if(HlprGUIDsAreEqual(pConditionKey, + ppConditionDatagramData[conditionIndex])) + { + isValid = TRUE; + + break; + } + } + } + else if(*pLayerKey == FWPM_LAYER_INBOUND_ICMP_ERROR_V4 || + *pLayerKey == FWPM_LAYER_INBOUND_ICMP_ERROR_V4_DISCARD || + *pLayerKey == FWPM_LAYER_INBOUND_ICMP_ERROR_V6 || + *pLayerKey == FWPM_LAYER_INBOUND_ICMP_ERROR_V6_DISCARD) + { + for(UINT32 conditionIndex = 0; + conditionIndex < INBOUND_ICMP_ERROR_CONDITIONS_COUNT; + conditionIndex++) + { + if(HlprGUIDsAreEqual(pConditionKey, + ppConditionInboundICMPError[conditionIndex])) + { + isValid = TRUE; + + break; + } + } + } + else if(*pLayerKey == FWPM_LAYER_OUTBOUND_ICMP_ERROR_V4 || + *pLayerKey == FWPM_LAYER_OUTBOUND_ICMP_ERROR_V4_DISCARD || + *pLayerKey == FWPM_LAYER_OUTBOUND_ICMP_ERROR_V6 || + *pLayerKey == FWPM_LAYER_OUTBOUND_ICMP_ERROR_V6_DISCARD) + { + for(UINT32 conditionIndex = 0; + conditionIndex < OUTBOUND_ICMP_ERROR_CONDITIONS_COUNT; + conditionIndex++) + { + if(HlprGUIDsAreEqual(pConditionKey, + ppConditionOutboundICMPError[conditionIndex])) + { + isValid = TRUE; + + break; + } + } + } + else if(*pLayerKey == FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V4 || + *pLayerKey == FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V4_DISCARD || + *pLayerKey == FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V6 || + *pLayerKey == FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V6_DISCARD) + { + for(UINT32 conditionIndex = 0; + conditionIndex < ALE_RESOURCE_ASSIGNMENT_CONDITIONS_COUNT; + conditionIndex++) + { + if(HlprGUIDsAreEqual(pConditionKey, + ppConditionALEResourceAssignment[conditionIndex])) + { + isValid = TRUE; + + break; + } + } + } + else if(*pLayerKey == FWPM_LAYER_ALE_AUTH_LISTEN_V4 || + *pLayerKey == FWPM_LAYER_ALE_AUTH_LISTEN_V4_DISCARD || + *pLayerKey == FWPM_LAYER_ALE_AUTH_LISTEN_V6 || + *pLayerKey == FWPM_LAYER_ALE_AUTH_LISTEN_V6_DISCARD) + { + for(UINT32 conditionIndex = 0; + conditionIndex < ALE_AUTH_LISTEN_CONDITIONS_COUNT; + conditionIndex++) + { + if(HlprGUIDsAreEqual(pConditionKey, + ppConditionALEAuthListen[conditionIndex])) + { + isValid = TRUE; + + break; + } + } + } + else if(*pLayerKey == FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 || + *pLayerKey == FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4_DISCARD || + *pLayerKey == FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 || + *pLayerKey == FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6_DISCARD) + { + for(UINT32 conditionIndex = 0; + conditionIndex < ALE_AUTH_RECV_ACCEPT_CONDITIONS_COUNT; + conditionIndex++) + { + if(HlprGUIDsAreEqual(pConditionKey, + ppConditionALEAuthRecvAccept[conditionIndex])) + { + isValid = TRUE; + + break; + } + } + } + else if(*pLayerKey == FWPM_LAYER_ALE_AUTH_CONNECT_V4 || + *pLayerKey == FWPM_LAYER_ALE_AUTH_CONNECT_V4_DISCARD || + *pLayerKey == FWPM_LAYER_ALE_AUTH_CONNECT_V6 || + *pLayerKey == FWPM_LAYER_ALE_AUTH_CONNECT_V6_DISCARD) + { + for(UINT32 conditionIndex = 0; + conditionIndex < ALE_AUTH_CONNECT_CONDITIONS_COUNT; + conditionIndex++) + { + if(HlprGUIDsAreEqual(pConditionKey, + ppConditionALEAuthConnect[conditionIndex])) + { + isValid = TRUE; + + break; + } + } + } + else if(*pLayerKey == FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4 || + *pLayerKey == FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4_DISCARD || + *pLayerKey == FWPM_LAYER_ALE_FLOW_ESTABLISHED_V6 || + *pLayerKey == FWPM_LAYER_ALE_FLOW_ESTABLISHED_V6_DISCARD) + { + for(UINT32 conditionIndex = 0; + conditionIndex < ALE_FLOW_ESTABLISHED_CONDITIONS_COUNT; + conditionIndex++) + { + if(HlprGUIDsAreEqual(pConditionKey, + ppConditionALEFlowEstablished[conditionIndex])) + { + isValid = TRUE; + + break; + } + } + } + else if(*pLayerKey == FWPM_LAYER_IPSEC_KM_DEMUX_V4 || + *pLayerKey == FWPM_LAYER_IPSEC_KM_DEMUX_V6) + { + for(UINT32 conditionIndex = 0; + conditionIndex < IPSEC_KM_DEMUX_CONDITIONS_COUNT; + conditionIndex++) + { + if(HlprGUIDsAreEqual(pConditionKey, + ppConditionIPsecKMDemux[conditionIndex])) + { + isValid = TRUE; + + break; + } + } + } + else if(*pLayerKey == FWPM_LAYER_IPSEC_V4 || + *pLayerKey == FWPM_LAYER_IPSEC_V6) + { + for(UINT32 conditionIndex = 0; + conditionIndex < IPSEC_CONDITIONS_COUNT; + conditionIndex++) + { + if(HlprGUIDsAreEqual(pConditionKey, + ppConditionIPsec[conditionIndex])) + { + isValid = TRUE; + + break; + } + } + } + else if(*pLayerKey == FWPM_LAYER_IKEEXT_V4 || + *pLayerKey == FWPM_LAYER_IKEEXT_V6) + { + for(UINT32 conditionIndex = 0; + conditionIndex < IKEEXT_CONDITIONS_COUNT; + conditionIndex++) + { + if(HlprGUIDsAreEqual(pConditionKey, + ppConditionIKEExt[conditionIndex])) + { + isValid = TRUE; + + break; + } + } + } + else if(*pLayerKey == FWPM_LAYER_RPC_UM) + { + for(UINT32 conditionIndex = 0; + conditionIndex < RPC_UM_CONDITIONS_COUNT; + conditionIndex++) + { + if(HlprGUIDsAreEqual(pConditionKey, + ppConditionRPCUM[conditionIndex])) + { + isValid = TRUE; + + break; + } + } + } + else if(*pLayerKey == FWPM_LAYER_RPC_EPMAP) + { + for(UINT32 conditionIndex = 0; + conditionIndex < RPC_EPMAP_CONDITIONS_COUNT; + conditionIndex++) + { + if(HlprGUIDsAreEqual(pConditionKey, + ppConditionRPCEPMap[conditionIndex])) + { + isValid = TRUE; + + break; + } + } + } + else if(*pLayerKey == FWPM_LAYER_RPC_EP_ADD) + { + for(UINT32 conditionIndex = 0; + conditionIndex < RPC_EP_ADD_CONDITIONS_COUNT; + conditionIndex++) + { + if(HlprGUIDsAreEqual(pConditionKey, + ppConditionRPCEPAdd[conditionIndex])) + { + isValid = TRUE; + + break; + } + } + } + else if(*pLayerKey == FWPM_LAYER_RPC_PROXY_CONN) + { + for(UINT32 conditionIndex = 0; + conditionIndex < RPC_PROXY_CONN_CONDITIONS_COUNT; + conditionIndex++) + { + if(HlprGUIDsAreEqual(pConditionKey, + ppConditionRPCProxyConn[conditionIndex])) + { + isValid = TRUE; + + break; + } + } + } + else if(*pLayerKey == FWPM_LAYER_RPC_PROXY_IF) + { + for(UINT32 conditionIndex = 0; + conditionIndex < RPC_PROXY_IF_CONDITIONS_COUNT; + conditionIndex++) + { + if(HlprGUIDsAreEqual(pConditionKey, + ppConditionRPCProxyIf[conditionIndex])) + { + isValid = TRUE; + + break; + } + } + } + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + else if(*pLayerKey == FWPM_LAYER_NAME_RESOLUTION_CACHE_V4 || + *pLayerKey == FWPM_LAYER_NAME_RESOLUTION_CACHE_V6) + { + for(UINT32 conditionIndex = 0; + conditionIndex < NAME_RESOLUTION_CACHE_CONDITIONS_COUNT; + conditionIndex++) + { + if(HlprGUIDsAreEqual(pConditionKey, + ppConditionNameResolutionCache[conditionIndex])) + { + isValid = TRUE; + + break; + } + } + } + else if(*pLayerKey == FWPM_LAYER_ALE_RESOURCE_RELEASE_V4 || + *pLayerKey == FWPM_LAYER_ALE_RESOURCE_RELEASE_V6) + { + for(UINT32 conditionIndex = 0; + conditionIndex < ALE_RESOURCE_RELEASE_CONDITIONS_COUNT; + conditionIndex++) + { + if(HlprGUIDsAreEqual(pConditionKey, + ppConditionALEResourceRelease[conditionIndex])) + { + isValid = TRUE; + + break; + } + } + } + else if(*pLayerKey == FWPM_LAYER_ALE_ENDPOINT_CLOSURE_V4 || + *pLayerKey == FWPM_LAYER_ALE_ENDPOINT_CLOSURE_V6) + { + for(UINT32 conditionIndex = 0; + conditionIndex < ALE_ENDPOINT_CLOSURE_CONDITIONS_COUNT; + conditionIndex++) + { + if(HlprGUIDsAreEqual(pConditionKey, + ppConditionALEEndpointClosure[conditionIndex])) + { + isValid = TRUE; + + break; + } + } + } + else if(*pLayerKey == FWPM_LAYER_ALE_CONNECT_REDIRECT_V4 || + *pLayerKey == FWPM_LAYER_ALE_CONNECT_REDIRECT_V6) + { + for(UINT32 conditionIndex = 0; + conditionIndex < ALE_CONNECT_REDIRECT_CONDITIONS_COUNT; + conditionIndex++) + { + if(HlprGUIDsAreEqual(pConditionKey, + ppConditionALEConnectRedirect[conditionIndex])) + { + isValid = TRUE; + + break; + } + } + } + else if(*pLayerKey == FWPM_LAYER_ALE_BIND_REDIRECT_V4 || + *pLayerKey == FWPM_LAYER_ALE_BIND_REDIRECT_V6) + { + for(UINT32 conditionIndex = 0; + conditionIndex < ALE_BIND_REDIRECT_CONDITIONS_COUNT; + conditionIndex++) + { + if(HlprGUIDsAreEqual(pConditionKey, + ppConditionALEBindRedirect[conditionIndex])) + { + isValid = TRUE; + + break; + } + } + } + else if(*pLayerKey == FWPM_LAYER_STREAM_PACKET_V4 || + *pLayerKey == FWPM_LAYER_STREAM_PACKET_V6) + { + for(UINT32 conditionIndex = 0; + conditionIndex < STREAM_PACKET_CONDITIONS_COUNT; + conditionIndex++) + { + if(HlprGUIDsAreEqual(pConditionKey, + ppConditionStreamPacket[conditionIndex])) + { + isValid = TRUE; + + break; + } + } + } + else if(*pLayerKey == FWPM_LAYER_KM_AUTHORIZATION) + { + for(UINT32 conditionIndex = 0; + conditionIndex < KM_AUTHORIZATION_CONDITIONS_COUNT; + conditionIndex++) + { + if(HlprGUIDsAreEqual(pConditionKey, + ppConditionKMAuthorization[conditionIndex])) + { + isValid = TRUE; + + break; + } + } + } + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + else if(*pLayerKey == FWPM_LAYER_INBOUND_MAC_FRAME_ETHERNET) + { + for(UINT32 conditionIndex = 0; + conditionIndex < INBOUND_MAC_FRAME_ETHERNET_CONDITIONS_COUNT; + conditionIndex++) + { + if(HlprGUIDsAreEqual(pConditionKey, + ppConditionInboundMACFrameEthernet[conditionIndex])) + { + isValid = TRUE; + + break; + } + } + } + else if(*pLayerKey == FWPM_LAYER_OUTBOUND_MAC_FRAME_ETHERNET) + { + for(UINT32 conditionIndex = 0; + conditionIndex < OUTBOUND_MAC_FRAME_ETHERNET_CONDITIONS_COUNT; + conditionIndex++) + { + if(HlprGUIDsAreEqual(pConditionKey, + ppConditionOutboundMACFrameEthernet[conditionIndex])) + { + isValid = TRUE; + + break; + } + } + } + else if(*pLayerKey == FWPM_LAYER_INBOUND_MAC_FRAME_NATIVE) + { + for(UINT32 conditionIndex = 0; + conditionIndex < INBOUND_MAC_FRAME_NATIVE_CONDITIONS_COUNT; + conditionIndex++) + { + if(HlprGUIDsAreEqual(pConditionKey, + ppConditionInboundMACFrameNative[conditionIndex])) + { + isValid = TRUE; + + break; + } + } + } + else if(*pLayerKey == FWPM_LAYER_OUTBOUND_MAC_FRAME_NATIVE) + { + for(UINT32 conditionIndex = 0; + conditionIndex < OUTBOUND_MAC_FRAME_NATIVE_CONDITIONS_COUNT; + conditionIndex++) + { + if(HlprGUIDsAreEqual(pConditionKey, + ppConditionOutboundMACFrameNative[conditionIndex])) + { + isValid = TRUE; + + break; + } + } + } + else if(*pLayerKey == FWPM_LAYER_INGRESS_VSWITCH_ETHERNET) + { + for(UINT32 conditionIndex = 0; + conditionIndex < INGRESS_VSWITCH_ETHERNET_CONDITIONS_COUNT; + conditionIndex++) + { + if(HlprGUIDsAreEqual(pConditionKey, + ppConditionIngressVSwitchEthernet[conditionIndex])) + { + isValid = TRUE; + + break; + } + } + } + else if(*pLayerKey == FWPM_LAYER_EGRESS_VSWITCH_ETHERNET) + { + for(UINT32 conditionIndex = 0; + conditionIndex < EGRESS_VSWITCH_ETHERNET_CONDITIONS_COUNT; + conditionIndex++) + { + if(HlprGUIDsAreEqual(pConditionKey, + ppConditionEgressVSwitchEthernet[conditionIndex])) + { + isValid = TRUE; + + break; + } + } + } + else if(*pLayerKey == FWPM_LAYER_INGRESS_VSWITCH_TRANSPORT_V4 || + *pLayerKey == FWPM_LAYER_INGRESS_VSWITCH_TRANSPORT_V6) + { + for(UINT32 conditionIndex = 0; + conditionIndex < INGRESS_VSWITCH_TRANSPORT_CONDITIONS_COUNT; + conditionIndex++) + { + if(HlprGUIDsAreEqual(pConditionKey, + ppConditionIngressVSwitchTransport[conditionIndex])) + { + isValid = TRUE; + + break; + } + } + } + else if(*pLayerKey == FWPM_LAYER_EGRESS_VSWITCH_TRANSPORT_V4 || + *pLayerKey == FWPM_LAYER_EGRESS_VSWITCH_TRANSPORT_V6) + { + for(UINT32 conditionIndex = 0; + conditionIndex < EGRESS_VSWITCH_TRANSPORT_CONDITIONS_COUNT; + conditionIndex++) + { + if(HlprGUIDsAreEqual(pConditionKey, + ppConditionEgressVSwitchTransport[conditionIndex])) + { + isValid = TRUE; + + break; + } + } + } +#if(NTDDI_VERSION >= NTDDI_WINBLUE) + + else if(*pLayerKey == FWPM_LAYER_INBOUND_TRANSPORT_FAST || + *pLayerKey == FWPM_LAYER_OUTBOUND_TRANSPORT_FAST || + *pLayerKey == FWPM_LAYER_INBOUND_MAC_FRAME_NATIVE_FAST || + *pLayerKey == FWPM_LAYER_OUTBOUND_MAC_FRAME_NATIVE_FAST) + isValid = FALSE; + +#endif /// (NTDDI_VERSION >= NTDDI_WINBLUE) +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + return isValid; +} + +/** + @helper_function="HlprFwpmFilterEnum" + + Purpose: Wrapper for the FwpmFilterEnum API.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA364058.aspx
+*/ +_At_(*pppEntries, _Pre_ _Null_) +_When_(return != NO_ERROR, _At_(*pppEntries, _Post_ _Null_)) +_Success_(return == NO_ERROR) +UINT32 HlprFwpmFilterEnum(_In_ const HANDLE engineHandle, + _In_ const HANDLE enumHandle, + _In_ const UINT32 numEntriesRequested, + _Outptr_result_buffer_maybenull_(*pNumEntriesReturned) FWPM_FILTER*** pppEntries, + _Out_ UINT32* pNumEntriesReturned) +{ + ASSERT(engineHandle); + ASSERT(enumHandle); + ASSERT(numEntriesRequested); + ASSERT(pppEntries); + ASSERT(pNumEntriesReturned); + + UINT32 status = NO_ERROR; + + status = FwpmFilterEnum(engineHandle, + enumHandle, + numEntriesRequested, + pppEntries, + pNumEntriesReturned); + if(status != NO_ERROR && + status != FWP_E_FILTER_NOT_FOUND && + status != FWP_E_NOT_FOUND) + { + *pppEntries = 0; + + *pNumEntriesReturned = 0; + + HlprLogError(L"HlprFwpmFilterEnum : FwpmFilterEnum() [status: %#x]", + status); + } + + return status; +} + + +/** + @helper_function="HlprFwpmFilterDestroyEnumHandle" + + Purpose: Wrapper for the FwpmFilterDestroyEnumHandle API.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA364055.aspx
+*/ +_When_(return != NO_ERROR, _At_(*pEnumHandle, _Post_ _Notnull_)) +_When_(return == NO_ERROR, _At_(*pEnumHandle, _Post_ _Null_)) +_Success_(return == NO_ERROR) +UINT32 HlprFwpmFilterDestroyEnumHandle(_In_ const HANDLE engineHandle, + _Inout_ HANDLE* pEnumHandle) +{ + ASSERT(engineHandle); + ASSERT(pEnumHandle); + + UINT32 status = NO_ERROR; + + if(*pEnumHandle) + { + status = FwpmFilterDestroyEnumHandle(engineHandle, + *pEnumHandle); + if(status != NO_ERROR) + { + HlprLogError(L"HlprFwpmFilterDestroyEnumHandle : FwpmFilterDestroyEnumHandle() [status: %#x]", + status); + + HLPR_BAIL; + } + + *pEnumHandle = 0; + } + + HLPR_BAIL_LABEL: + + return status; +} + +/** + @helper_function="HlprFwpmFilterCreateEnumHandle" + + Purpose: Wrapper for the FwpmFilterCreateEnumHandle API.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA364048.aspx
+*/ +_When_(return != NO_ERROR, _At_(*pEnumHandle, _Post_ _Null_)) +_When_(return == NO_ERROR, _At_(*pEnumHandle, _Post_ _Notnull_)) +_Success_(return == NO_ERROR) +UINT32 HlprFwpmFilterCreateEnumHandle(_In_ const HANDLE engineHandle, + _In_opt_ const FWPM_FILTER_ENUM_TEMPLATE* pEnumTemplate, + _Out_ HANDLE* pEnumHandle) +{ + ASSERT(engineHandle); + ASSERT(pEnumHandle); + + UINT32 status = NO_ERROR; + + status = FwpmFilterCreateEnumHandle(engineHandle, + pEnumTemplate, + pEnumHandle); + if(status != NO_ERROR) + { + *pEnumHandle = 0; + + HlprLogError(L"HlprFwpmFilterCreateEnumHandle : FwpmFilterCreateEnumHandle() [status: %#x]", + status); + } + + return status; +} + +/** + @helper_function="HlprFwpmFilterDeleteByKey" + + Purpose: Wrapper for the FwpmFilterDeleteByKey API.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA364053.aspx
+*/ +_Success_(return == NO_ERROR) +UINT32 HlprFwpmFilterDeleteByKey(_In_ const HANDLE engineHandle, + _In_ const GUID* pFilterKey) +{ + ASSERT(engineHandle); + ASSERT(pFilterKey); + + UINT32 status = NO_ERROR; + + status = FwpmFilterDeleteByKey(engineHandle, + pFilterKey); + if(status != NO_ERROR) + { + if(status != FWP_E_IN_USE && + status != FWP_E_BUILTIN_OBJECT && + status != FWP_E_FILTER_NOT_FOUND) + HlprLogError(L"HlprFwpmFilterDeleteByKey : FwpmFilterDeleteByKey() [status: %#x]", + status); + else + { + HlprLogInfo(L"HlprFwpmFilterDeleteByKey : FwpmFilterDeleteByKey() [status: %#x]", + status); + + status = NO_ERROR; + } + } + + return status; +} + +/** + @helper_function="HlprFwpmFilterAdd" + + Purpose: Wrapper for the FwpmFilterAdd API.
+
+ Notes: Filter ID is written to pFilter->filterId.
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA364046.aspx
+*/ +_Success_(return == NO_ERROR) +UINT32 HlprFwpmFilterAdd(_In_ const HANDLE engineHandle, + _Inout_ FWPM_FILTER* pFilter) +{ + ASSERT(engineHandle); + ASSERT(pFilter); + + UINT32 status = NO_ERROR; + + status = FwpmFilterAdd(engineHandle, + pFilter, + 0, + &(pFilter->filterId)); + if(status != NO_ERROR) + HlprLogError(L"HlprFwpmFilterAdd : FwpmFilterAdd() [status: %#x]", + status); + + return status; +} + +/** + @helper_function="HlprFwpmFilterRemoveAll" + + Purpose: Remove all filters associated with the specified provider.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 HlprFwpmFilterRemoveAll(_In_opt_ HANDLE* pEngineHandle, + _In_opt_ const GUID* pProviderKey) +{ + UINT32 status = NO_ERROR; + HANDLE engineHandle = 0; + BOOLEAN isLocal = FALSE; + + if(pEngineHandle && + *pEngineHandle) + engineHandle = *pEngineHandle; + else + { + status = HlprFwpmEngineOpen(&engineHandle); + HLPR_BAIL_ON_FAILURE(status); + + isLocal = TRUE; + } + + for(UINT32 layerIndex = 0; + layerIndex < TOTAL_LAYER_COUNT; + layerIndex++) + { + HANDLE enumHandle = 0; + FWPM_FILTER** ppFilters = 0; + UINT32 filterCount = 0; + FWPM_FILTER_ENUM_TEMPLATE enumTemplate = {0}; + + enumTemplate.providerKey = (GUID*)pProviderKey; + enumTemplate.layerKey = *(ppLayerKeyArray[layerIndex]); + enumTemplate.enumType = FWP_FILTER_ENUM_FULLY_CONTAINED; + enumTemplate.flags = FWP_FILTER_ENUM_FLAG_INCLUDE_BOOTTIME | + FWP_FILTER_ENUM_FLAG_INCLUDE_DISABLED; + enumTemplate.actionMask = 0xFFFFFFFF; + + status = HlprFwpmFilterCreateEnumHandle(engineHandle, + &enumTemplate, + &enumHandle); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmFilterEnum(engineHandle, + enumHandle, + 0xFFFFFFFF, + &ppFilters, + &filterCount); + if(ppFilters && + filterCount) + { + for(UINT32 filterIndex = 0; + filterIndex < filterCount; + filterIndex++) + { + HlprFwpmFilterDeleteByKey(engineHandle, + &(ppFilters[filterIndex]->filterKey)); + } + + FwpmFreeMemory((VOID**)&ppFilters); + } + + HlprFwpmFilterDestroyEnumHandle(engineHandle, + &enumHandle); + } + + HLPR_BAIL_LABEL: + + if(engineHandle && + isLocal) + HlprFwpmEngineClose(&engineHandle); + + return status; +} + +/** + @helper_function="HlprFwpmFilterPurge" + + Purpose: Cleanup an FWPM_FILTER.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA364265.aspx
+*/ +VOID HlprFwpmFilterPurge(_Inout_ FWPM_FILTER* pFilter) +{ + ASSERT(pFilter); + + if(pFilter->numFilterConditions) + HlprFwpmFilterConditionDestroy(&(pFilter->filterCondition), + pFilter->numFilterConditions); + + ZeroMemory(pFilter, + sizeof(FWPM_FILTER)); + + return; +} + +/** + @helper_function="HlprFwpmFilterDestroy" + + Purpose: Cleanup and free an (array of) FWPM_FILTER.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA364265.aspx
+*/ +_At_(*ppFilters, _Post_ _Null_) +_Success_(*ppFilters == 0) +VOID HlprFwpmFilterDestroy(_Inout_updates_all_(numFilters) FWPM_FILTER** ppFilters, + _In_ const UINT32 numFilters) /* 1 */ +{ + ASSERT(ppFilters); + ASSERT(numFilters); + + for(UINT32 filterIndex = 0; + filterIndex < numFilters; + filterIndex++) + { + HlprFwpmFilterPurge(&((*ppFilters)[filterIndex])); + } + + if(numFilters > 1) + { + HLPR_DELETE_ARRAY(*ppFilters); + } + else + { + HLPR_DELETE(*ppFilters); + } + + return; +} + +/** + @helper_function="HlprFwpmFilterCreate" + + Purpose: Allocate an (array of) FWPM_FILTER.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA364265.aspx
+*/ +_Success_(return == NO_ERROR) +UINT32 HlprFwpmFilterCreate(_Inout_updates_all_(numFilters) FWPM_FILTER** ppFilters, + _In_ const UINT32 numFilters) /* 1 */ +{ + ASSERT(ppFilters); + ASSERT(numFilters); + + UINT32 status = NO_ERROR; + + if(numFilters > 1) + { + HLPR_NEW_ARRAY(*ppFilters, + FWPM_FILTER, + numFilters); + } + else + { + HLPR_NEW(*ppFilters, + FWPM_FILTER); + } + + return status; +} diff --git a/network/trans/WFPSampler/lib/HelperFunctions_FwpmFilter.h b/network/trans/WFPSampler/lib/HelperFunctions_FwpmFilter.h new file mode 100644 index 000000000..ea3b234db --- /dev/null +++ b/network/trans/WFPSampler/lib/HelperFunctions_FwpmFilter.h @@ -0,0 +1,92 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_FwpmFilter.h +// +// Abstract: +// This module contains prototypes for functions which assist in actions pertaining to +// FWPM_FILTER objects. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Add HlprFwpmFilterConditionPrune +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef HELPERFUNCTIONS_FWPM_FILTER_H +#define HELPERFUNCTIONS_FWPM_FILTER_H + +VOID HlprFwpmFilterConditionPrune(_In_reads_(numFilterConditions) const FWPM_FILTER_CONDITION* pFilterConditions, + _Inout_ UINT32 numFilterConditions, + _Inout_updates_all_(*pNewNumFilterConditions) FWPM_FILTER_CONDITION** ppNewFilterConditions, + _Inout_ UINT32* pNewNumFilterConditions); + +_Success_(pFilterCondition->conditionValue.type == FWP_EMPTY && + pFilterCondition->conditionValue.uint64 == 0) +VOID HlprFwpmFilterConditionPurge(_Inout_ FWPM_FILTER_CONDITION* pFilterCondition); + +_At_(*ppFilterConditions, _Post_ _Null_) +_Success_(*ppFilterConditions == 0) +VOID HlprFwpmFilterConditionDestroy(_Inout_updates_all_(numFilterConditions) FWPM_FILTER_CONDITION** ppFilterConditions, + _In_ const UINT32 numFilterConditions = 1); + +_Success_(return == NO_ERROR) +UINT32 HlprFwpmFilterConditionCreate(_Inout_updates_all_(numFilterConditions) FWPM_FILTER_CONDITION** ppFilterConditions, + _In_ const UINT32 numFilterConditions = 1); + +BOOLEAN HlprFwpmFilterConditionIsValidForLayer(_In_ const GUID* pLayerKey, + _In_ const GUID* pConditionKey); + +_At_(*pppEntries, _Pre_ _Null_) +_When_(return != NO_ERROR, _At_(*pppEntries, _Post_ _Null_)) +_Success_(return == NO_ERROR) +UINT32 HlprFwpmFilterEnum(_In_ const HANDLE engineHandle, + _In_ const HANDLE enumHandle, + _In_ const UINT32 numEntriesRequested, + _Outptr_result_buffer_maybenull_(*pNumEntriesReturned) FWPM_FILTER*** pppEntries, + _Out_ UINT32* pNumEntriesReturned); + +_When_(return != NO_ERROR, _At_(*pEnumHandle, _Post_ _Notnull_)) +_When_(return == NO_ERROR, _At_(*pEnumHandle, _Post_ _Null_)) +_Success_(return == NO_ERROR) +UINT32 HlprFwpmFilterDestroyEnumHandle(_In_ const HANDLE engineHandle, + _Inout_ HANDLE* pEnumHandle); + +_When_(return != NO_ERROR, _At_(*pEnumHandle, _Post_ _Null_)) +_When_(return == NO_ERROR, _At_(*pEnumHandle, _Post_ _Notnull_)) +_Success_(return == NO_ERROR) +UINT32 HlprFwpmFilterCreateEnumHandle(_In_ const HANDLE engineHandle, + _In_opt_ const FWPM_FILTER_ENUM_TEMPLATE* pEnumTemplate, + _Out_ HANDLE* pEnumHandle); + +_Success_(return == NO_ERROR) +UINT32 HlprFwpmFilterDeleteByKey(_In_ const HANDLE engineHandle, + _In_ const GUID* pFilterKey); + +_Success_(return == NO_ERROR) +UINT32 HlprFwpmFilterAdd(_In_ const HANDLE engineHandle, + _Inout_ FWPM_FILTER* pFilter); + +_Success_(return == NO_ERROR) +UINT32 HlprFwpmFilterRemoveAll(_In_opt_ HANDLE* pEngineHandle, + _In_opt_ const GUID* pProviderKey); + +VOID HlprFwpmFilterPurge(_Inout_ FWPM_FILTER* pFilter); + +_At_(*ppFilters, _Post_ _Null_) +_Success_(*ppFilters == 0) +VOID HlprFwpmFilterDestroy(_Inout_updates_all_(numFilters) FWPM_FILTER** ppFilters, + _In_ const UINT32 numFilters = 1); + +_Success_(return == NO_ERROR) +UINT32 HlprFwpmFilterCreate(_Inout_updates_all_(numFilters) FWPM_FILTER** ppFilters, + _In_ const UINT32 numFilters = 1); + +#endif /// HELPERFUNCTIONS_FWPM_FILTER_H diff --git a/network/trans/WFPSampler/lib/HelperFunctions_FwpmLayer.cpp b/network/trans/WFPSampler/lib/HelperFunctions_FwpmLayer.cpp new file mode 100644 index 000000000..c67de75bb --- /dev/null +++ b/network/trans/WFPSampler/lib/HelperFunctions_FwpmLayer.cpp @@ -0,0 +1,1720 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_FwpmLayer.cpp +// +// Abstract: +// This module contains functions which assist in actions pertaining to FWPM_LAYER objects. +// +// Naming Convention: +// +// +// +// i.e. +// +// +// { +// - Function is likely visible to other modules. +// } +// +// { +// Hlpr - Function is from HelperFunctions_* Modules. +// } +// +// { +// FwpmLayer - Function pertains to FWPM_LAYER objects. +// } +// +// { +// Get - Function retrieves a value. +// Is - Function determines capabilities. +// } +// +// { +// ByID - Function takes a runtime ID. +// ByKey - Function takes a GUID. +// ByString - Function takes a null terminated wide character string and returns the +// LayerKey. +// ID - Function acts on the objects ID. +// IPv4 - Function acts on IP version 4 objects. +// IPv6 - Function acts on IP version 6 objects. +// } +// Private Functions: +// +// Public Functions: +// HlprFwpmLayerGetByID(), +// HlprFwpmLayerGetByString(), +// HlprFwpmLayerGetIDByKey(), +// HlprFwpmLayerGetIDByString(), +// HlprFwpmLayerIsIPv4(), +// HlprFwpmLayerIsIPv6(), +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Add basic support for WinBlue Fast layers +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "HelperFunctions_Include.h" /// . + +_At_(*pppFilterConditionArray, _Pre_ _Null_) +_When_(return != NO_ERROR, _At_(*pppFilterConditionArray, _Post_ _Null_)) +_When_(return == NO_ERROR, _At_(*pppFilterConditionArray, _Post_ _Maybenull_)) +_Success_(return == NO_ERROR) +UINT32 HlprFwpmLayerGetFilterConditionArrayByKey(_In_ const GUID* pLayerKey, + _Outptr_result_buffer_maybenull_(*pConditionArrayCount) GUID*** pppFilterConditionArray, + _Out_ UINT16* pConditionArrayCount) +{ + ASSERT(pLayerKey); + ASSERT(pppFilterConditionArray); + ASSERT(pConditionArrayCount); + + UINT32 status = NO_ERROR; + + if(HlprGUIDsAreEqual(&FWPM_LAYER_INBOUND_IPPACKET_V4, + pLayerKey) || + HlprGUIDsAreEqual(&FWPM_LAYER_INBOUND_IPPACKET_V4_DISCARD, + pLayerKey) || + HlprGUIDsAreEqual(&FWPM_LAYER_INBOUND_IPPACKET_V6, + pLayerKey) || + HlprGUIDsAreEqual(&FWPM_LAYER_INBOUND_IPPACKET_V6_DISCARD, + pLayerKey)) + { + *pppFilterConditionArray = (GUID**)ppConditionInboundIPPacket; + + *pConditionArrayCount = INBOUND_IPPACKET_CONDITIONS_COUNT; + } + else if(HlprGUIDsAreEqual(&FWPM_LAYER_OUTBOUND_IPPACKET_V4, + pLayerKey) || + HlprGUIDsAreEqual(&FWPM_LAYER_OUTBOUND_IPPACKET_V4_DISCARD, + pLayerKey) || + HlprGUIDsAreEqual(&FWPM_LAYER_OUTBOUND_IPPACKET_V6, + pLayerKey) || + HlprGUIDsAreEqual(&FWPM_LAYER_OUTBOUND_IPPACKET_V6_DISCARD, + pLayerKey)) + { + *pppFilterConditionArray = (GUID**)ppConditionOutboundIPPacket; + + *pConditionArrayCount = OUTBOUND_IPPACKET_CONDITIONS_COUNT; + } + else if(HlprGUIDsAreEqual(&FWPM_LAYER_IPFORWARD_V4, + pLayerKey) || + HlprGUIDsAreEqual(&FWPM_LAYER_IPFORWARD_V4_DISCARD, + pLayerKey) || + HlprGUIDsAreEqual(&FWPM_LAYER_IPFORWARD_V6, + pLayerKey) || + HlprGUIDsAreEqual(&FWPM_LAYER_IPFORWARD_V6_DISCARD, + pLayerKey)) + { + *pppFilterConditionArray = (GUID**)ppConditionIPForward; + + *pConditionArrayCount = IPFORWARD_CONDITIONS_COUNT; + } + else if(HlprGUIDsAreEqual(&FWPM_LAYER_INBOUND_TRANSPORT_V4, + pLayerKey) || + HlprGUIDsAreEqual(&FWPM_LAYER_INBOUND_TRANSPORT_V4_DISCARD, + pLayerKey) || + HlprGUIDsAreEqual(&FWPM_LAYER_INBOUND_TRANSPORT_V6, + pLayerKey) || + HlprGUIDsAreEqual(&FWPM_LAYER_INBOUND_TRANSPORT_V6_DISCARD, + pLayerKey)) + { + *pppFilterConditionArray = (GUID**)ppConditionInboundTransport; + + *pConditionArrayCount = INBOUND_TRANSPORT_CONDITIONS_COUNT; + } + else if(HlprGUIDsAreEqual(&FWPM_LAYER_OUTBOUND_TRANSPORT_V4, + pLayerKey) || + HlprGUIDsAreEqual(&FWPM_LAYER_OUTBOUND_TRANSPORT_V4_DISCARD, + pLayerKey) || + HlprGUIDsAreEqual(&FWPM_LAYER_OUTBOUND_TRANSPORT_V6, + pLayerKey) || + HlprGUIDsAreEqual(&FWPM_LAYER_OUTBOUND_TRANSPORT_V6_DISCARD, + pLayerKey)) + { + *pppFilterConditionArray = (GUID**)ppConditionOutboundTransport; + + *pConditionArrayCount = OUTBOUND_TRANSPORT_CONDITIONS_COUNT; + } + else if(HlprGUIDsAreEqual(&FWPM_LAYER_STREAM_V4, + pLayerKey) || + HlprGUIDsAreEqual(&FWPM_LAYER_STREAM_V4_DISCARD, + pLayerKey) || + HlprGUIDsAreEqual(&FWPM_LAYER_STREAM_V6, + pLayerKey) || + HlprGUIDsAreEqual(&FWPM_LAYER_STREAM_V6_DISCARD, + pLayerKey)) + { + *pppFilterConditionArray = (GUID**)ppConditionStream; + + *pConditionArrayCount = STREAM_CONDITIONS_COUNT; + } + else if(HlprGUIDsAreEqual(&FWPM_LAYER_DATAGRAM_DATA_V4, + pLayerKey) || + HlprGUIDsAreEqual(&FWPM_LAYER_DATAGRAM_DATA_V4_DISCARD, + pLayerKey) || + HlprGUIDsAreEqual(&FWPM_LAYER_DATAGRAM_DATA_V6, + pLayerKey) || + HlprGUIDsAreEqual(&FWPM_LAYER_DATAGRAM_DATA_V6_DISCARD, + pLayerKey)) + { + *pppFilterConditionArray = (GUID**)ppConditionDatagramData; + + *pConditionArrayCount = DATAGRAM_DATA_CONDITIONS_COUNT; + } + else if(HlprGUIDsAreEqual(&FWPM_LAYER_INBOUND_ICMP_ERROR_V4, + pLayerKey) || + HlprGUIDsAreEqual(&FWPM_LAYER_INBOUND_ICMP_ERROR_V4_DISCARD, + pLayerKey) || + HlprGUIDsAreEqual(&FWPM_LAYER_INBOUND_ICMP_ERROR_V6, + pLayerKey) || + HlprGUIDsAreEqual(&FWPM_LAYER_INBOUND_ICMP_ERROR_V6_DISCARD, + pLayerKey)) + { + *pppFilterConditionArray = (GUID**)ppConditionInboundICMPError; + + *pConditionArrayCount = INBOUND_ICMP_ERROR_CONDITIONS_COUNT; + } + else if(HlprGUIDsAreEqual(&FWPM_LAYER_OUTBOUND_ICMP_ERROR_V4, + pLayerKey) || + HlprGUIDsAreEqual(&FWPM_LAYER_OUTBOUND_ICMP_ERROR_V4_DISCARD, + pLayerKey) || + HlprGUIDsAreEqual(&FWPM_LAYER_OUTBOUND_ICMP_ERROR_V6, + pLayerKey) || + HlprGUIDsAreEqual(&FWPM_LAYER_OUTBOUND_ICMP_ERROR_V6_DISCARD, + pLayerKey)) + { + *pppFilterConditionArray = (GUID**)ppConditionOutboundICMPError; + + *pConditionArrayCount = OUTBOUND_ICMP_ERROR_CONDITIONS_COUNT; + } + else if(HlprGUIDsAreEqual(&FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V4, + pLayerKey) || + HlprGUIDsAreEqual(&FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V4_DISCARD, + pLayerKey) || + HlprGUIDsAreEqual(&FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V6, + pLayerKey) || + HlprGUIDsAreEqual(&FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V6_DISCARD, + pLayerKey)) + { + *pppFilterConditionArray = (GUID**)ppConditionALEResourceAssignment; + + *pConditionArrayCount = ALE_RESOURCE_ASSIGNMENT_CONDITIONS_COUNT; + } + else if(HlprGUIDsAreEqual(&FWPM_LAYER_ALE_AUTH_LISTEN_V4, + pLayerKey) || + HlprGUIDsAreEqual(&FWPM_LAYER_ALE_AUTH_LISTEN_V4_DISCARD, + pLayerKey) || + HlprGUIDsAreEqual(&FWPM_LAYER_ALE_AUTH_LISTEN_V6, + pLayerKey) || + HlprGUIDsAreEqual(&FWPM_LAYER_ALE_AUTH_LISTEN_V6_DISCARD, + pLayerKey)) + { + *pppFilterConditionArray = (GUID**)ppConditionALEAuthListen; + + *pConditionArrayCount = ALE_AUTH_LISTEN_CONDITIONS_COUNT; + } + else if(HlprGUIDsAreEqual(&FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4, + pLayerKey) || + HlprGUIDsAreEqual(&FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4_DISCARD, + pLayerKey) || + HlprGUIDsAreEqual(&FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6, + pLayerKey) || + HlprGUIDsAreEqual(&FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6_DISCARD, + pLayerKey)) + { + *pppFilterConditionArray = (GUID**)ppConditionALEAuthRecvAccept; + + *pConditionArrayCount = ALE_AUTH_RECV_ACCEPT_CONDITIONS_COUNT; + } + else if(HlprGUIDsAreEqual(&FWPM_LAYER_ALE_AUTH_CONNECT_V4, + pLayerKey) || + HlprGUIDsAreEqual(&FWPM_LAYER_ALE_AUTH_CONNECT_V4_DISCARD, + pLayerKey) || + HlprGUIDsAreEqual(&FWPM_LAYER_ALE_AUTH_CONNECT_V6, + pLayerKey) || + HlprGUIDsAreEqual(&FWPM_LAYER_ALE_AUTH_CONNECT_V6_DISCARD, + pLayerKey)) + { + *pppFilterConditionArray = (GUID**)ppConditionALEAuthConnect; + + *pConditionArrayCount = ALE_AUTH_CONNECT_CONDITIONS_COUNT; + } + else if(HlprGUIDsAreEqual(&FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4, + pLayerKey) || + HlprGUIDsAreEqual(&FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4_DISCARD, + pLayerKey) || + HlprGUIDsAreEqual(&FWPM_LAYER_ALE_FLOW_ESTABLISHED_V6, + pLayerKey) || + HlprGUIDsAreEqual(&FWPM_LAYER_ALE_FLOW_ESTABLISHED_V6_DISCARD, + pLayerKey)) + { + *pppFilterConditionArray = (GUID**)ppConditionALEFlowEstablished; + + *pConditionArrayCount = ALE_FLOW_ESTABLISHED_CONDITIONS_COUNT; + } + else if(HlprGUIDsAreEqual(&FWPM_LAYER_IPSEC_KM_DEMUX_V4, + pLayerKey) || + HlprGUIDsAreEqual(&FWPM_LAYER_IPSEC_KM_DEMUX_V6, + pLayerKey)) + { + *pppFilterConditionArray = (GUID**)ppConditionIPsecKMDemux; + + *pConditionArrayCount = IPSEC_KM_DEMUX_CONDITIONS_COUNT; + } + else if(HlprGUIDsAreEqual(&FWPM_LAYER_IPSEC_V4, + pLayerKey) || + HlprGUIDsAreEqual(&FWPM_LAYER_IPSEC_V6, + pLayerKey)) + { + *pppFilterConditionArray = (GUID**)ppConditionIPsec; + + *pConditionArrayCount = IPSEC_CONDITIONS_COUNT; + } + else if(HlprGUIDsAreEqual(&FWPM_LAYER_IKEEXT_V4, + pLayerKey) || + HlprGUIDsAreEqual(&FWPM_LAYER_IKEEXT_V6, + pLayerKey)) + { + *pppFilterConditionArray = (GUID**)ppConditionIKEExt; + + *pConditionArrayCount = IKEEXT_CONDITIONS_COUNT; + } + else if(HlprGUIDsAreEqual(&FWPM_LAYER_RPC_UM, + pLayerKey)) + { + *pppFilterConditionArray = (GUID**)ppConditionRPCUM; + + *pConditionArrayCount = RPC_UM_CONDITIONS_COUNT; + } + else if(HlprGUIDsAreEqual(&FWPM_LAYER_RPC_EPMAP, + pLayerKey)) + { + *pppFilterConditionArray = (GUID**)ppConditionRPCEPMap; + + *pConditionArrayCount = RPC_EPMAP_CONDITIONS_COUNT; + } + else if(HlprGUIDsAreEqual(&FWPM_LAYER_RPC_EP_ADD, + pLayerKey)) + { + *pppFilterConditionArray = (GUID**)ppConditionRPCEPAdd; + + *pConditionArrayCount = RPC_EP_ADD_CONDITIONS_COUNT; + } + else if(HlprGUIDsAreEqual(&FWPM_LAYER_RPC_UM, + pLayerKey)) + { + *pppFilterConditionArray = (GUID**)ppConditionRPCProxyConn; + + *pConditionArrayCount = RPC_PROXY_CONN_CONDITIONS_COUNT; + } + else if(HlprGUIDsAreEqual(&FWPM_LAYER_RPC_PROXY_IF, + pLayerKey)) + { + *pppFilterConditionArray = (GUID**)ppConditionRPCProxyIf; + + *pConditionArrayCount = RPC_PROXY_IF_CONDITIONS_COUNT; + } + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + else if(HlprGUIDsAreEqual(&FWPM_LAYER_NAME_RESOLUTION_CACHE_V4, + pLayerKey) || + HlprGUIDsAreEqual(&FWPM_LAYER_NAME_RESOLUTION_CACHE_V6, + pLayerKey)) + { + *pppFilterConditionArray = (GUID**)ppConditionNameResolutionCache; + + *pConditionArrayCount = NAME_RESOLUTION_CACHE_CONDITIONS_COUNT; + } + else if(HlprGUIDsAreEqual(&FWPM_LAYER_ALE_RESOURCE_RELEASE_V4, + pLayerKey) || + HlprGUIDsAreEqual(&FWPM_LAYER_ALE_RESOURCE_RELEASE_V6, + pLayerKey)) + { + *pppFilterConditionArray = (GUID**)ppConditionALEResourceRelease; + + *pConditionArrayCount = ALE_RESOURCE_RELEASE_CONDITIONS_COUNT; + } + else if(HlprGUIDsAreEqual(&FWPM_LAYER_ALE_ENDPOINT_CLOSURE_V4, + pLayerKey) || + HlprGUIDsAreEqual(&FWPM_LAYER_ALE_ENDPOINT_CLOSURE_V6, + pLayerKey)) + { + *pppFilterConditionArray = (GUID**)ppConditionALEEndpointClosure; + + *pConditionArrayCount = ALE_ENDPOINT_CLOSURE_CONDITIONS_COUNT; + } + else if(HlprGUIDsAreEqual(&FWPM_LAYER_ALE_CONNECT_REDIRECT_V4, + pLayerKey) || + HlprGUIDsAreEqual(&FWPM_LAYER_ALE_CONNECT_REDIRECT_V6, + pLayerKey)) + { + *pppFilterConditionArray = (GUID**)ppConditionALEConnectRedirect; + + *pConditionArrayCount = ALE_CONNECT_REDIRECT_CONDITIONS_COUNT; + } + else if(HlprGUIDsAreEqual(&FWPM_LAYER_ALE_BIND_REDIRECT_V4, + pLayerKey) || + HlprGUIDsAreEqual(&FWPM_LAYER_ALE_BIND_REDIRECT_V6, + pLayerKey)) + { + *pppFilterConditionArray = (GUID**)ppConditionALEBindRedirect; + + *pConditionArrayCount = ALE_BIND_REDIRECT_CONDITIONS_COUNT; + } + else if(HlprGUIDsAreEqual(&FWPM_LAYER_STREAM_PACKET_V4, + pLayerKey) || + HlprGUIDsAreEqual(&FWPM_LAYER_STREAM_PACKET_V6, + pLayerKey)) + { + *pppFilterConditionArray = (GUID**)ppConditionStreamPacket; + + *pConditionArrayCount = STREAM_PACKET_CONDITIONS_COUNT; + } + else if(HlprGUIDsAreEqual(&FWPM_LAYER_KM_AUTHORIZATION, + pLayerKey)) + { + *pppFilterConditionArray = (GUID**)ppConditionKMAuthorization; + + *pConditionArrayCount = KM_AUTHORIZATION_CONDITIONS_COUNT; + } + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + else if(HlprGUIDsAreEqual(&FWPM_LAYER_INBOUND_MAC_FRAME_ETHERNET, + pLayerKey)) + { + *pppFilterConditionArray = (GUID**)ppConditionInboundMACFrameEthernet; + + *pConditionArrayCount = INBOUND_MAC_FRAME_ETHERNET_CONDITIONS_COUNT; + } + else if(HlprGUIDsAreEqual(&FWPM_LAYER_OUTBOUND_MAC_FRAME_ETHERNET, + pLayerKey)) + { + *pppFilterConditionArray = (GUID**)ppConditionOutboundMACFrameEthernet; + + *pConditionArrayCount = OUTBOUND_MAC_FRAME_ETHERNET_CONDITIONS_COUNT; + } + else if(HlprGUIDsAreEqual(&FWPM_LAYER_INBOUND_MAC_FRAME_NATIVE, + pLayerKey)) + { + *pppFilterConditionArray = (GUID**)ppConditionInboundMACFrameNative; + + *pConditionArrayCount = INBOUND_MAC_FRAME_NATIVE_CONDITIONS_COUNT; + } + else if(HlprGUIDsAreEqual(&FWPM_LAYER_OUTBOUND_MAC_FRAME_NATIVE, + pLayerKey)) + { + *pppFilterConditionArray = (GUID**)ppConditionOutboundMACFrameNative; + + *pConditionArrayCount = OUTBOUND_MAC_FRAME_NATIVE_CONDITIONS_COUNT; + } + else if(HlprGUIDsAreEqual(&FWPM_LAYER_INGRESS_VSWITCH_ETHERNET, + pLayerKey)) + { + *pppFilterConditionArray = (GUID**)ppConditionIngressVSwitchEthernet; + + *pConditionArrayCount = INGRESS_VSWITCH_ETHERNET_CONDITIONS_COUNT; + } + else if(HlprGUIDsAreEqual(&FWPM_LAYER_EGRESS_VSWITCH_ETHERNET, + pLayerKey)) + { + *pppFilterConditionArray = (GUID**)ppConditionEgressVSwitchEthernet; + + *pConditionArrayCount = EGRESS_VSWITCH_ETHERNET_CONDITIONS_COUNT; + } + else if(HlprGUIDsAreEqual(&FWPM_LAYER_INGRESS_VSWITCH_TRANSPORT_V4, + pLayerKey) || + HlprGUIDsAreEqual(&FWPM_LAYER_INGRESS_VSWITCH_TRANSPORT_V6, + pLayerKey)) + { + *pppFilterConditionArray = (GUID**)ppConditionIngressVSwitchTransport; + + *pConditionArrayCount = INGRESS_VSWITCH_TRANSPORT_CONDITIONS_COUNT; + } + else if(HlprGUIDsAreEqual(&FWPM_LAYER_EGRESS_VSWITCH_TRANSPORT_V4, + pLayerKey) || + HlprGUIDsAreEqual(&FWPM_LAYER_EGRESS_VSWITCH_TRANSPORT_V6, + pLayerKey)) + { + *pppFilterConditionArray = (GUID**)ppConditionEgressVSwitchTransport; + + *pConditionArrayCount = EGRESS_VSWITCH_TRANSPORT_CONDITIONS_COUNT; + } + +#if(NTDDI_VERSION >= NTDDI_WINBLUE) + + else if(HlprGUIDsAreEqual(&FWPM_LAYER_INBOUND_TRANSPORT_FAST, + pLayerKey)) + { + *pppFilterConditionArray = 0; + + *pConditionArrayCount = 0; + } + else if(HlprGUIDsAreEqual(&FWPM_LAYER_OUTBOUND_TRANSPORT_FAST, + pLayerKey)) + { + *pppFilterConditionArray = 0; + + *pConditionArrayCount = 0; + } + else if(HlprGUIDsAreEqual(&FWPM_LAYER_INBOUND_MAC_FRAME_NATIVE_FAST, + pLayerKey)) + { + *pppFilterConditionArray = 0; + + *pConditionArrayCount = 0; + } + else if(HlprGUIDsAreEqual(&FWPM_LAYER_OUTBOUND_MAC_FRAME_NATIVE_FAST, + pLayerKey)) + { + *pppFilterConditionArray = 0; + + *pConditionArrayCount = 0; + } + +#endif // (NTDDI_VERSION >= NTDDI_WINBLUE) +#endif // (NTDDI_VERSION >= NTDDI_WIN8) +#endif // (NTDDI_VERSION >= NTDDI_WIN7) + + else + { + status = (UINT32)FWP_E_LAYER_NOT_FOUND; + + *pppFilterConditionArray = 0; + + *pConditionArrayCount = 0; + + HlprLogError(L"HlprFwpmLayerGetFilterConditionArrayByKey() [status: %#x]", + status); + + } + + return status; +} + +/** + @helper_function="HlprFwpmLayerGetIDByKey" + + Purpose: Return the runtime ID of the layer provided the layer's key.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA366492.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/FF570731.aspx
+*/ +_Success_(return < FWPS_BUILTIN_LAYER_MAX) +UINT8 HlprFwpmLayerGetIDByKey(_In_ const GUID* pLayerKey) +{ + ASSERT(pLayerKey); + + UINT8 layerID = FWPS_BUILTIN_LAYER_MAX; + + if(HlprGUIDsAreEqual(&FWPM_LAYER_INBOUND_IPPACKET_V4, + pLayerKey)) + layerID = FWPS_LAYER_INBOUND_IPPACKET_V4; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_INBOUND_IPPACKET_V4_DISCARD, + pLayerKey)) + layerID = FWPS_LAYER_INBOUND_IPPACKET_V4_DISCARD; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_INBOUND_IPPACKET_V6, + pLayerKey)) + layerID = FWPS_LAYER_INBOUND_IPPACKET_V6; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_INBOUND_IPPACKET_V6_DISCARD, + pLayerKey)) + layerID = FWPS_LAYER_INBOUND_IPPACKET_V6_DISCARD; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_OUTBOUND_IPPACKET_V4, + pLayerKey)) + layerID = FWPS_LAYER_OUTBOUND_IPPACKET_V4; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_OUTBOUND_IPPACKET_V4_DISCARD, + pLayerKey)) + layerID = FWPS_LAYER_OUTBOUND_IPPACKET_V4_DISCARD; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_OUTBOUND_IPPACKET_V6, + pLayerKey)) + layerID = FWPS_LAYER_OUTBOUND_IPPACKET_V6; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_OUTBOUND_IPPACKET_V6_DISCARD, + pLayerKey)) + layerID = FWPS_LAYER_OUTBOUND_IPPACKET_V6_DISCARD; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_IPFORWARD_V4, + pLayerKey)) + layerID = FWPS_LAYER_IPFORWARD_V4; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_IPFORWARD_V4_DISCARD, + pLayerKey)) + layerID = FWPS_LAYER_IPFORWARD_V4_DISCARD; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_IPFORWARD_V6, + pLayerKey)) + layerID = FWPS_LAYER_IPFORWARD_V6; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_IPFORWARD_V6_DISCARD, + pLayerKey)) + layerID = FWPS_LAYER_IPFORWARD_V6_DISCARD; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_INBOUND_TRANSPORT_V4, + pLayerKey)) + layerID = FWPS_LAYER_INBOUND_TRANSPORT_V4; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_INBOUND_TRANSPORT_V4_DISCARD, + pLayerKey)) + layerID = FWPS_LAYER_INBOUND_TRANSPORT_V4_DISCARD; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_INBOUND_TRANSPORT_V6, + pLayerKey)) + layerID = FWPS_LAYER_INBOUND_TRANSPORT_V6; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_INBOUND_TRANSPORT_V6_DISCARD, + pLayerKey)) + layerID = FWPS_LAYER_INBOUND_TRANSPORT_V6_DISCARD; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_OUTBOUND_TRANSPORT_V4, + pLayerKey)) + layerID = FWPS_LAYER_OUTBOUND_TRANSPORT_V4; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_OUTBOUND_TRANSPORT_V4_DISCARD, + pLayerKey)) + layerID = FWPS_LAYER_OUTBOUND_TRANSPORT_V4_DISCARD; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_OUTBOUND_TRANSPORT_V6, + pLayerKey)) + layerID = FWPS_LAYER_OUTBOUND_TRANSPORT_V6; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_OUTBOUND_TRANSPORT_V6_DISCARD, + pLayerKey)) + layerID = FWPS_LAYER_OUTBOUND_TRANSPORT_V6_DISCARD; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_STREAM_V4, + pLayerKey)) + layerID = FWPS_LAYER_STREAM_V4; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_STREAM_V4_DISCARD, + pLayerKey)) + layerID = FWPS_LAYER_STREAM_V4_DISCARD; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_STREAM_V6, + pLayerKey)) + layerID = FWPS_LAYER_STREAM_V6; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_STREAM_V6_DISCARD, + pLayerKey)) + layerID = FWPS_LAYER_STREAM_V6_DISCARD; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_DATAGRAM_DATA_V4, + pLayerKey)) + layerID = FWPS_LAYER_DATAGRAM_DATA_V4; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_DATAGRAM_DATA_V4_DISCARD, + pLayerKey)) + layerID = FWPS_LAYER_DATAGRAM_DATA_V4_DISCARD; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_DATAGRAM_DATA_V6, + pLayerKey)) + layerID = FWPS_LAYER_DATAGRAM_DATA_V6; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_DATAGRAM_DATA_V6_DISCARD, + pLayerKey)) + layerID = FWPS_LAYER_DATAGRAM_DATA_V6_DISCARD; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_INBOUND_ICMP_ERROR_V4, + pLayerKey)) + layerID = FWPS_LAYER_INBOUND_ICMP_ERROR_V4; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_INBOUND_ICMP_ERROR_V4_DISCARD, + pLayerKey)) + layerID = FWPS_LAYER_INBOUND_ICMP_ERROR_V4_DISCARD; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_INBOUND_ICMP_ERROR_V6, + pLayerKey)) + layerID = FWPS_LAYER_INBOUND_ICMP_ERROR_V6; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_INBOUND_ICMP_ERROR_V6_DISCARD, + pLayerKey)) + layerID = FWPS_LAYER_INBOUND_ICMP_ERROR_V6_DISCARD; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_OUTBOUND_ICMP_ERROR_V4, + pLayerKey)) + layerID = FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_OUTBOUND_ICMP_ERROR_V4_DISCARD, + pLayerKey)) + layerID = FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4_DISCARD; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_OUTBOUND_ICMP_ERROR_V6, + pLayerKey)) + layerID = FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_OUTBOUND_ICMP_ERROR_V6_DISCARD, + pLayerKey)) + layerID = FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6_DISCARD; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V4, + pLayerKey)) + layerID = FWPS_LAYER_ALE_RESOURCE_ASSIGNMENT_V4; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V4_DISCARD, + pLayerKey)) + layerID = FWPS_LAYER_ALE_RESOURCE_ASSIGNMENT_V4_DISCARD; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V6, + pLayerKey)) + layerID = FWPS_LAYER_ALE_RESOURCE_ASSIGNMENT_V6; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V6_DISCARD, + pLayerKey)) + layerID = FWPS_LAYER_ALE_RESOURCE_ASSIGNMENT_V6_DISCARD; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_ALE_AUTH_LISTEN_V4, + pLayerKey)) + layerID = FWPS_LAYER_ALE_AUTH_LISTEN_V4; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_ALE_AUTH_LISTEN_V4_DISCARD, + pLayerKey)) + layerID = FWPS_LAYER_ALE_AUTH_LISTEN_V4_DISCARD; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_ALE_AUTH_LISTEN_V6, + pLayerKey)) + layerID = FWPS_LAYER_ALE_AUTH_LISTEN_V6; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_ALE_AUTH_LISTEN_V6_DISCARD, + pLayerKey)) + layerID = FWPS_LAYER_ALE_AUTH_LISTEN_V6_DISCARD; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4, + pLayerKey)) + layerID = FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4_DISCARD, + pLayerKey)) + layerID = FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4_DISCARD; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6, + pLayerKey)) + layerID = FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6_DISCARD, + pLayerKey)) + layerID = FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6_DISCARD; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_ALE_AUTH_CONNECT_V4, + pLayerKey)) + layerID = FWPS_LAYER_ALE_AUTH_CONNECT_V4; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_ALE_AUTH_CONNECT_V4_DISCARD, + pLayerKey)) + layerID = FWPS_LAYER_ALE_AUTH_CONNECT_V4_DISCARD; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_ALE_AUTH_CONNECT_V6, + pLayerKey)) + layerID = FWPS_LAYER_ALE_AUTH_CONNECT_V6; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_ALE_AUTH_CONNECT_V6_DISCARD, + pLayerKey)) + layerID = FWPS_LAYER_ALE_AUTH_CONNECT_V6_DISCARD; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4, + pLayerKey)) + layerID = FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4_DISCARD, + pLayerKey)) + layerID = FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4_DISCARD; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_ALE_FLOW_ESTABLISHED_V6, + pLayerKey)) + layerID = FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_ALE_FLOW_ESTABLISHED_V6_DISCARD, + pLayerKey)) + layerID = FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6_DISCARD; + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + else if(HlprGUIDsAreEqual(&FWPM_LAYER_NAME_RESOLUTION_CACHE_V4, + pLayerKey)) + layerID = FWPS_LAYER_NAME_RESOLUTION_CACHE_V4; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_NAME_RESOLUTION_CACHE_V6, + pLayerKey)) + layerID = FWPS_LAYER_NAME_RESOLUTION_CACHE_V6; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_ALE_RESOURCE_RELEASE_V4, + pLayerKey)) + layerID = FWPS_LAYER_ALE_RESOURCE_RELEASE_V4; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_ALE_RESOURCE_RELEASE_V6, + pLayerKey)) + layerID = FWPS_LAYER_ALE_RESOURCE_RELEASE_V6; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_ALE_ENDPOINT_CLOSURE_V4, + pLayerKey)) + layerID = FWPS_LAYER_ALE_ENDPOINT_CLOSURE_V4; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_ALE_ENDPOINT_CLOSURE_V6, + pLayerKey)) + layerID = FWPS_LAYER_ALE_ENDPOINT_CLOSURE_V6; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_ALE_CONNECT_REDIRECT_V4, + pLayerKey)) + layerID = FWPS_LAYER_ALE_CONNECT_REDIRECT_V4; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_ALE_CONNECT_REDIRECT_V6, + pLayerKey)) + layerID = FWPS_LAYER_ALE_CONNECT_REDIRECT_V6; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_ALE_BIND_REDIRECT_V4, + pLayerKey)) + layerID = FWPS_LAYER_ALE_BIND_REDIRECT_V4; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_ALE_BIND_REDIRECT_V6, + pLayerKey)) + layerID = FWPS_LAYER_ALE_BIND_REDIRECT_V6; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_STREAM_PACKET_V4, + pLayerKey)) + layerID = FWPS_LAYER_STREAM_PACKET_V4; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_STREAM_PACKET_V6, + pLayerKey)) + layerID = FWPS_LAYER_STREAM_PACKET_V6; + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + else if(HlprGUIDsAreEqual(&FWPM_LAYER_INBOUND_MAC_FRAME_ETHERNET, + pLayerKey)) + layerID = FWPS_LAYER_INBOUND_MAC_FRAME_ETHERNET; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_OUTBOUND_MAC_FRAME_ETHERNET, + pLayerKey)) + layerID = FWPS_LAYER_OUTBOUND_MAC_FRAME_ETHERNET; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_INBOUND_MAC_FRAME_NATIVE, + pLayerKey)) + layerID = FWPS_LAYER_INBOUND_MAC_FRAME_NATIVE; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_OUTBOUND_MAC_FRAME_NATIVE, + pLayerKey)) + layerID = FWPS_LAYER_OUTBOUND_MAC_FRAME_NATIVE; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_INGRESS_VSWITCH_ETHERNET, + pLayerKey)) + layerID = FWPS_LAYER_INGRESS_VSWITCH_ETHERNET; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_EGRESS_VSWITCH_ETHERNET, + pLayerKey)) + layerID = FWPS_LAYER_EGRESS_VSWITCH_ETHERNET; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_INGRESS_VSWITCH_TRANSPORT_V4, + pLayerKey)) + layerID = FWPS_LAYER_INGRESS_VSWITCH_TRANSPORT_V4; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_INGRESS_VSWITCH_TRANSPORT_V6, + pLayerKey)) + layerID = FWPS_LAYER_INGRESS_VSWITCH_TRANSPORT_V6; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_EGRESS_VSWITCH_TRANSPORT_V4, + pLayerKey)) + layerID = FWPS_LAYER_EGRESS_VSWITCH_TRANSPORT_V4; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_EGRESS_VSWITCH_TRANSPORT_V6, + pLayerKey)) + layerID = FWPS_LAYER_EGRESS_VSWITCH_TRANSPORT_V6; + +#if(NTDDI_VERSION >= NTDDI_WINBLUE) + + else if(HlprGUIDsAreEqual(&FWPM_LAYER_INBOUND_TRANSPORT_FAST, + pLayerKey)) + layerID = FWPS_LAYER_INBOUND_TRANSPORT_FAST; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_OUTBOUND_TRANSPORT_FAST, + pLayerKey)) + layerID = FWPS_LAYER_OUTBOUND_TRANSPORT_FAST; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_INBOUND_MAC_FRAME_NATIVE_FAST, + pLayerKey)) + layerID = FWPS_LAYER_INBOUND_MAC_FRAME_NATIVE_FAST; + else if(HlprGUIDsAreEqual(&FWPM_LAYER_OUTBOUND_MAC_FRAME_NATIVE_FAST, + pLayerKey)) + layerID = FWPS_LAYER_OUTBOUND_MAC_FRAME_NATIVE_FAST; + +#endif // (NTDDI_VERSION >= NTDDI_WINBLUE) +#endif // (NTDDI_VERSION >= NTDDI_WIN8) +#endif // (NTDDI_VERSION >= NTDDI_WIN7) + + return layerID; +} + +/** + @helper_function="HlprFwpmLayerGetID" + + Purpose: Return the runtime ID of the layer provided a string representation of the layer's + name.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA366492.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/FF570731.aspx
+*/ +_Success_(return < FWPS_BUILTIN_LAYER_MAX) +UINT8 HlprFwpmLayerGetIDByString(_In_ PCWSTR pLayerString) +{ + ASSERT(pLayerString); + + UINT8 layerID = FWPS_BUILTIN_LAYER_MAX; + + if(HlprStringsAreEqual(L"FWPM_LAYER_INBOUND_IPPACKET_V4", + pLayerString)) + layerID = FWPS_LAYER_INBOUND_IPPACKET_V4; + else if(HlprStringsAreEqual(L"FWPM_LAYER_INBOUND_IPPACKET_V4_DISCARD", + pLayerString)) + layerID = FWPS_LAYER_INBOUND_IPPACKET_V4_DISCARD; + else if(HlprStringsAreEqual(L"FWPM_LAYER_INBOUND_IPPACKET_V6", + pLayerString)) + layerID = FWPS_LAYER_INBOUND_IPPACKET_V6; + else if(HlprStringsAreEqual(L"FWPM_LAYER_INBOUND_IPPACKET_V6_DISCARD", + pLayerString)) + layerID = FWPS_LAYER_INBOUND_IPPACKET_V6_DISCARD; + else if(HlprStringsAreEqual(L"FWPM_LAYER_OUTBOUND_IPPACKET_V4", + pLayerString)) + layerID = FWPS_LAYER_OUTBOUND_IPPACKET_V4; + else if(HlprStringsAreEqual(L"FWPM_LAYER_OUTBOUND_IPPACKET_V4_DISCARD", + pLayerString)) + layerID = FWPS_LAYER_OUTBOUND_IPPACKET_V4_DISCARD; + else if(HlprStringsAreEqual(L"FWPM_LAYER_OUTBOUND_IPPACKET_V6", + pLayerString)) + layerID = FWPS_LAYER_OUTBOUND_IPPACKET_V6; + else if(HlprStringsAreEqual(L"FWPM_LAYER_OUTBOUND_IPPACKET_V6_DISCARD", + pLayerString)) + layerID = FWPS_LAYER_OUTBOUND_IPPACKET_V6_DISCARD; + else if(HlprStringsAreEqual(L"FWPM_LAYER_IPFORWARD_V4", + pLayerString)) + layerID = FWPS_LAYER_IPFORWARD_V4; + else if(HlprStringsAreEqual(L"FWPM_LAYER_IPFORWARD_V4_DISCARD", + pLayerString)) + layerID = FWPS_LAYER_IPFORWARD_V4_DISCARD; + else if(HlprStringsAreEqual(L"FWPM_LAYER_IPFORWARD_V6", + pLayerString)) + layerID = FWPS_LAYER_IPFORWARD_V6; + else if(HlprStringsAreEqual(L"FWPM_LAYER_IPFORWARD_V6_DISCARD", + pLayerString)) + layerID = FWPS_LAYER_IPFORWARD_V6_DISCARD; + else if(HlprStringsAreEqual(L"FWPM_LAYER_INBOUND_TRANSPORT_V4", + pLayerString)) + layerID = FWPS_LAYER_INBOUND_TRANSPORT_V4; + else if(HlprStringsAreEqual(L"FWPM_LAYER_INBOUND_TRANSPORT_V4_DISCARD", + pLayerString)) + layerID = FWPS_LAYER_INBOUND_TRANSPORT_V4_DISCARD; + else if(HlprStringsAreEqual(L"FWPM_LAYER_INBOUND_TRANSPORT_V6", + pLayerString)) + layerID = FWPS_LAYER_INBOUND_TRANSPORT_V6; + else if(HlprStringsAreEqual(L"FWPM_LAYER_INBOUND_TRANSPORT_V6_DISCARD", + pLayerString)) + layerID = FWPS_LAYER_INBOUND_TRANSPORT_V6_DISCARD; + else if(HlprStringsAreEqual(L"FWPM_LAYER_OUTBOUND_TRANSPORT_V4", + pLayerString)) + layerID = FWPS_LAYER_OUTBOUND_TRANSPORT_V4; + else if(HlprStringsAreEqual(L"FWPM_LAYER_OUTBOUND_TRANSPORT_V4_DISCARD", + pLayerString)) + layerID = FWPS_LAYER_OUTBOUND_TRANSPORT_V4_DISCARD; + else if(HlprStringsAreEqual(L"FWPM_LAYER_OUTBOUND_TRANSPORT_V6", + pLayerString)) + layerID = FWPS_LAYER_OUTBOUND_TRANSPORT_V6; + else if(HlprStringsAreEqual(L"FWPM_LAYER_OUTBOUND_TRANSPORT_V6_DISCARD", + pLayerString)) + layerID = FWPS_LAYER_OUTBOUND_TRANSPORT_V6_DISCARD; + else if(HlprStringsAreEqual(L"FWPM_LAYER_STREAM_V4", + pLayerString)) + layerID = FWPS_LAYER_STREAM_V4; + else if(HlprStringsAreEqual(L"FWPM_LAYER_STREAM_V4_DISCARD", + pLayerString)) + layerID = FWPS_LAYER_STREAM_V4_DISCARD; + else if(HlprStringsAreEqual(L"FWPM_LAYER_STREAM_V6", + pLayerString)) + layerID = FWPS_LAYER_STREAM_V6; + else if(HlprStringsAreEqual(L"FWPM_LAYER_STREAM_V6_DISCARD", + pLayerString)) + layerID = FWPS_LAYER_STREAM_V6_DISCARD; + else if(HlprStringsAreEqual(L"FWPM_LAYER_DATAGRAM_DATA_V4", + pLayerString)) + layerID = FWPS_LAYER_DATAGRAM_DATA_V4; + else if(HlprStringsAreEqual(L"FWPM_LAYER_DATAGRAM_DATA_V4_DISCARD", + pLayerString)) + layerID = FWPS_LAYER_DATAGRAM_DATA_V4_DISCARD; + else if(HlprStringsAreEqual(L"FWPM_LAYER_DATAGRAM_DATA_V6", + pLayerString)) + layerID = FWPS_LAYER_DATAGRAM_DATA_V6; + else if(HlprStringsAreEqual(L"FWPM_LAYER_DATAGRAM_DATA_V6_DISCARD", + pLayerString)) + layerID = FWPS_LAYER_DATAGRAM_DATA_V6_DISCARD; + else if(HlprStringsAreEqual(L"FWPM_LAYER_INBOUND_ICMP_ERROR_V4", + pLayerString)) + layerID = FWPS_LAYER_INBOUND_ICMP_ERROR_V4; + else if(HlprStringsAreEqual(L"FWPM_LAYER_INBOUND_ICMP_ERROR_V4_DISCARD", + pLayerString)) + layerID = FWPS_LAYER_INBOUND_ICMP_ERROR_V4_DISCARD; + else if(HlprStringsAreEqual(L"FWPM_LAYER_INBOUND_ICMP_ERROR_V6", + pLayerString)) + layerID = FWPS_LAYER_INBOUND_ICMP_ERROR_V6; + else if(HlprStringsAreEqual(L"FWPM_LAYER_INBOUND_ICMP_ERROR_V6_DISCARD", + pLayerString)) + layerID = FWPS_LAYER_INBOUND_ICMP_ERROR_V6_DISCARD; + else if(HlprStringsAreEqual(L"FWPM_LAYER_OUTBOUND_ICMP_ERROR_V4", + pLayerString)) + layerID = FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4; + else if(HlprStringsAreEqual(L"FWPM_LAYER_OUTBOUND_ICMP_ERROR_V4_DISCARD", + pLayerString)) + layerID = FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4_DISCARD; + else if(HlprStringsAreEqual(L"FWPM_LAYER_OUTBOUND_ICMP_ERROR_V6", + pLayerString)) + layerID = FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6; + else if(HlprStringsAreEqual(L"FWPM_LAYER_OUTBOUND_ICMP_ERROR_V6_DISCARD", + pLayerString)) + layerID = FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6_DISCARD; + else if(HlprStringsAreEqual(L"FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V4", + pLayerString)) + layerID = FWPS_LAYER_ALE_RESOURCE_ASSIGNMENT_V4; + else if(HlprStringsAreEqual(L"FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V4_DISCARD", + pLayerString)) + layerID = FWPS_LAYER_ALE_RESOURCE_ASSIGNMENT_V4_DISCARD; + else if(HlprStringsAreEqual(L"FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V6", + pLayerString)) + layerID = FWPS_LAYER_ALE_RESOURCE_ASSIGNMENT_V6; + else if(HlprStringsAreEqual(L"FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V6_DISCARD", + pLayerString)) + layerID = FWPS_LAYER_ALE_RESOURCE_ASSIGNMENT_V6_DISCARD; + else if(HlprStringsAreEqual(L"FWPM_LAYER_ALE_AUTH_LISTEN_V4", + pLayerString)) + layerID = FWPS_LAYER_ALE_AUTH_LISTEN_V4; + else if(HlprStringsAreEqual(L"FWPM_LAYER_ALE_AUTH_LISTEN_V4_DISCARD", + pLayerString)) + layerID = FWPS_LAYER_ALE_AUTH_LISTEN_V4_DISCARD; + else if(HlprStringsAreEqual(L"FWPM_LAYER_ALE_AUTH_LISTEN_V6", + pLayerString)) + layerID = FWPS_LAYER_ALE_AUTH_LISTEN_V6; + else if(HlprStringsAreEqual(L"FWPM_LAYER_ALE_AUTH_LISTEN_V6_DISCARD", + pLayerString)) + layerID = FWPS_LAYER_ALE_AUTH_LISTEN_V6_DISCARD; + else if(HlprStringsAreEqual(L"FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4", + pLayerString)) + layerID = FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4; + else if(HlprStringsAreEqual(L"FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4_DISCARD", + pLayerString)) + layerID = FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4_DISCARD; + else if(HlprStringsAreEqual(L"FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6", + pLayerString)) + layerID = FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6; + else if(HlprStringsAreEqual(L"FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6_DISCARD", + pLayerString)) + layerID = FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6_DISCARD; + else if(HlprStringsAreEqual(L"FWPM_LAYER_ALE_AUTH_CONNECT_V4", + pLayerString)) + layerID = FWPS_LAYER_ALE_AUTH_CONNECT_V4; + else if(HlprStringsAreEqual(L"FWPM_LAYER_ALE_AUTH_CONNECT_V4_DISCARD", + pLayerString)) + layerID = FWPS_LAYER_ALE_AUTH_CONNECT_V4_DISCARD; + else if(HlprStringsAreEqual(L"FWPM_LAYER_ALE_AUTH_CONNECT_V6", + pLayerString)) + layerID = FWPS_LAYER_ALE_AUTH_CONNECT_V6; + else if(HlprStringsAreEqual(L"FWPM_LAYER_ALE_AUTH_CONNECT_V6_DISCARD", + pLayerString)) + layerID = FWPS_LAYER_ALE_AUTH_CONNECT_V6_DISCARD; + else if(HlprStringsAreEqual(L"FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4", + pLayerString)) + layerID = FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4; + else if(HlprStringsAreEqual(L"FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4_DISCARD", + pLayerString)) + layerID = FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4_DISCARD; + else if(HlprStringsAreEqual(L"FWPM_LAYER_ALE_FLOW_ESTABLISHED_V6", + pLayerString)) + layerID = FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6; + else if(HlprStringsAreEqual(L"FWPM_LAYER_ALE_FLOW_ESTABLISHED_V6_DISCARD", + pLayerString)) + layerID = FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6_DISCARD; + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + else if(HlprStringsAreEqual(L"FWPM_LAYER_INBOUND_MAC_FRAME_ETHERNET", + pLayerString)) + layerID = FWPS_LAYER_INBOUND_MAC_FRAME_ETHERNET; + else if(HlprStringsAreEqual(L"FWPM_LAYER_OUTBOUND_MAC_FRAME_ETHERNET", + pLayerString)) + layerID = FWPS_LAYER_OUTBOUND_MAC_FRAME_ETHERNET; + else if(HlprStringsAreEqual(L"FWPM_LAYER_NAME_RESOLUTION_CACHE_V4", + pLayerString)) + layerID = FWPS_LAYER_NAME_RESOLUTION_CACHE_V4; + else if(HlprStringsAreEqual(L"FWPM_LAYER_NAME_RESOLUTION_CACHE_V6", + pLayerString)) + layerID = FWPS_LAYER_NAME_RESOLUTION_CACHE_V6; + else if(HlprStringsAreEqual(L"FWPM_LAYER_ALE_RESOURCE_RELEASE_V4", + pLayerString)) + layerID = FWPS_LAYER_ALE_RESOURCE_RELEASE_V4; + else if(HlprStringsAreEqual(L"FWPM_LAYER_ALE_RESOURCE_RELEASE_V6", + pLayerString)) + layerID = FWPS_LAYER_ALE_RESOURCE_RELEASE_V6; + else if(HlprStringsAreEqual(L"FWPM_LAYER_ALE_ENDPOINT_CLOSURE_V4", + pLayerString)) + layerID = FWPS_LAYER_ALE_ENDPOINT_CLOSURE_V4; + else if(HlprStringsAreEqual(L"FWPM_LAYER_ALE_ENDPOINT_CLOSURE_V6", + pLayerString)) + layerID = FWPS_LAYER_ALE_ENDPOINT_CLOSURE_V6; + else if(HlprStringsAreEqual(L"FWPM_LAYER_ALE_CONNECT_REDIRECT_V4", + pLayerString)) + layerID = FWPS_LAYER_ALE_CONNECT_REDIRECT_V4; + else if(HlprStringsAreEqual(L"FWPM_LAYER_ALE_CONNECT_REDIRECT_V6", + pLayerString)) + layerID = FWPS_LAYER_ALE_CONNECT_REDIRECT_V6; + else if(HlprStringsAreEqual(L"FWPM_LAYER_ALE_BIND_REDIRECT_V4", + pLayerString)) + layerID = FWPS_LAYER_ALE_BIND_REDIRECT_V4; + else if(HlprStringsAreEqual(L"FWPM_LAYER_ALE_BIND_REDIRECT_V6", + pLayerString)) + layerID = FWPS_LAYER_ALE_BIND_REDIRECT_V6; + else if(HlprStringsAreEqual(L"FWPM_LAYER_STREAM_PACKET_V4", + pLayerString)) + layerID = FWPS_LAYER_STREAM_PACKET_V4; + else if(HlprStringsAreEqual(L"FWPM_LAYER_STREAM_PACKET_V6", + pLayerString)) + layerID = FWPS_LAYER_STREAM_PACKET_V6; + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + else if(HlprStringsAreEqual(L"FWPM_LAYER_INBOUND_MAC_FRAME_NATIVE", + pLayerString)) + layerID = FWPS_LAYER_INBOUND_MAC_FRAME_NATIVE; + else if(HlprStringsAreEqual(L"FWPM_LAYER_OUTBOUND_MAC_FRAME_NATIVE", + pLayerString)) + layerID = FWPS_LAYER_OUTBOUND_MAC_FRAME_NATIVE; + else if(HlprStringsAreEqual(L"FWPM_LAYER_INGRESS_VSWITCH_ETHERNET", + pLayerString)) + layerID = FWPS_LAYER_INGRESS_VSWITCH_ETHERNET; + else if(HlprStringsAreEqual(L"FWPM_LAYER_EGRESS_VSWITCH_ETHERNET", + pLayerString)) + layerID = FWPS_LAYER_EGRESS_VSWITCH_ETHERNET; + else if(HlprStringsAreEqual(L"FWPM_LAYER_INGRESS_VSWITCH_TRANSPORT_V4", + pLayerString)) + layerID = FWPS_LAYER_INGRESS_VSWITCH_TRANSPORT_V4; + else if(HlprStringsAreEqual(L"FWPM_LAYER_INGRESS_VSWITCH_TRANSPORT_V6", + pLayerString)) + layerID = FWPS_LAYER_INGRESS_VSWITCH_TRANSPORT_V6; + else if(HlprStringsAreEqual(L"FWPM_LAYER_EGRESS_VSWITCH_TRANSPORT_V4", + pLayerString)) + layerID = FWPS_LAYER_EGRESS_VSWITCH_TRANSPORT_V4; + else if(HlprStringsAreEqual(L"FWPM_LAYER_EGRESS_VSWITCH_TRANSPORT_V6", + pLayerString)) + layerID = FWPS_LAYER_EGRESS_VSWITCH_TRANSPORT_V6; + +#if(NTDDI_VERSION >= NTDDI_WINBLUE) + + else if(HlprStringsAreEqual(L"FWPM_LAYER_INBOUND_TRANSPORT_FAST", + pLayerString)) + layerID = FWPS_LAYER_INBOUND_TRANSPORT_FAST; + else if(HlprStringsAreEqual(L"FWPM_LAYER_OUTBOUND_TRANSPORT_FAST", + pLayerString)) + layerID = FWPS_LAYER_OUTBOUND_TRANSPORT_FAST; + else if(HlprStringsAreEqual(L"FWPM_LAYER_INBOUND_MAC_FRAME_NATIVE_FAST", + pLayerString)) + layerID = FWPS_LAYER_INBOUND_MAC_FRAME_NATIVE_FAST; + else if(HlprStringsAreEqual(L"FWPM_LAYER_OUTBOUND_MAC_FRAME_NATIVE_FAST", + pLayerString)) + layerID = FWPS_LAYER_OUTBOUND_MAC_FRAME_NATIVE_FAST; + +#endif // (NTDDI_VERSION >= NTDDI_WINBLUE) +#endif // (NTDDI_VERSION >= NTDDI_WIN8) +#endif // (NTDDI_VERSION >= NTDDI_WIN7) + + return layerID; +} + +/** + @helper_function="HlprFwpmLayerGetByID" + + Purpose: Return a pointer to the GUID of the layer represented by the provider layer's + runtime ID.
+
+ Notes: Calling function must check for NULL pointer.
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA366492.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/FF570731.aspx
+*/ +_Success_(return != 0) +const GUID* HlprFwpmLayerGetByID(_In_ const UINT32 layerID) +{ + const GUID* pLayerKey = 0; + + if(layerID == FWPS_LAYER_INBOUND_IPPACKET_V4) + pLayerKey = &FWPM_LAYER_INBOUND_IPPACKET_V4; + else if(layerID == FWPS_LAYER_INBOUND_IPPACKET_V4_DISCARD) + pLayerKey = &FWPM_LAYER_INBOUND_IPPACKET_V4_DISCARD; + else if(layerID == FWPS_LAYER_INBOUND_IPPACKET_V6) + pLayerKey = &FWPM_LAYER_INBOUND_IPPACKET_V6; + else if(layerID == FWPS_LAYER_INBOUND_IPPACKET_V6_DISCARD) + pLayerKey = &FWPM_LAYER_INBOUND_IPPACKET_V6_DISCARD; + else if(layerID == FWPS_LAYER_OUTBOUND_IPPACKET_V4) + pLayerKey = &FWPM_LAYER_OUTBOUND_IPPACKET_V4; + else if(layerID == FWPS_LAYER_OUTBOUND_IPPACKET_V4_DISCARD) + pLayerKey = &FWPM_LAYER_OUTBOUND_IPPACKET_V4_DISCARD; + else if(layerID == FWPS_LAYER_OUTBOUND_IPPACKET_V6) + pLayerKey = &FWPM_LAYER_OUTBOUND_IPPACKET_V6; + else if(layerID == FWPS_LAYER_OUTBOUND_IPPACKET_V6_DISCARD) + pLayerKey = &FWPM_LAYER_OUTBOUND_IPPACKET_V6_DISCARD; + else if(layerID == FWPS_LAYER_IPFORWARD_V4) + pLayerKey = &FWPM_LAYER_IPFORWARD_V4; + else if(layerID == FWPS_LAYER_IPFORWARD_V4_DISCARD) + pLayerKey = &FWPM_LAYER_IPFORWARD_V4_DISCARD; + else if(layerID == FWPS_LAYER_IPFORWARD_V6) + pLayerKey = &FWPM_LAYER_IPFORWARD_V6; + else if(layerID == FWPS_LAYER_IPFORWARD_V6_DISCARD) + pLayerKey = &FWPM_LAYER_IPFORWARD_V6_DISCARD; + else if(layerID == FWPS_LAYER_INBOUND_TRANSPORT_V4) + pLayerKey = &FWPM_LAYER_INBOUND_TRANSPORT_V4; + else if(layerID == FWPS_LAYER_INBOUND_TRANSPORT_V4_DISCARD) + pLayerKey = &FWPM_LAYER_INBOUND_TRANSPORT_V4_DISCARD; + else if(layerID == FWPS_LAYER_INBOUND_TRANSPORT_V6) + pLayerKey = &FWPM_LAYER_INBOUND_TRANSPORT_V6; + else if(layerID == FWPS_LAYER_INBOUND_TRANSPORT_V6_DISCARD) + pLayerKey = &FWPM_LAYER_INBOUND_TRANSPORT_V6_DISCARD; + else if(layerID == FWPS_LAYER_OUTBOUND_TRANSPORT_V4) + pLayerKey = &FWPM_LAYER_OUTBOUND_TRANSPORT_V4; + else if(layerID == FWPS_LAYER_OUTBOUND_TRANSPORT_V4_DISCARD) + pLayerKey = &FWPM_LAYER_OUTBOUND_TRANSPORT_V4_DISCARD; + else if(layerID == FWPS_LAYER_OUTBOUND_TRANSPORT_V6) + pLayerKey = &FWPM_LAYER_OUTBOUND_TRANSPORT_V6; + else if(layerID == FWPS_LAYER_OUTBOUND_TRANSPORT_V6_DISCARD) + pLayerKey = &FWPM_LAYER_OUTBOUND_TRANSPORT_V6_DISCARD; + else if(layerID == FWPS_LAYER_STREAM_V4) + pLayerKey = &FWPM_LAYER_STREAM_V4; + else if(layerID == FWPS_LAYER_STREAM_V4_DISCARD) + pLayerKey = &FWPM_LAYER_STREAM_V4_DISCARD; + else if(layerID == FWPS_LAYER_STREAM_V6) + pLayerKey = &FWPM_LAYER_STREAM_V6; + else if(layerID == FWPS_LAYER_STREAM_V6_DISCARD) + pLayerKey = &FWPM_LAYER_STREAM_V6_DISCARD; + else if(layerID == FWPS_LAYER_DATAGRAM_DATA_V4) + pLayerKey = &FWPM_LAYER_DATAGRAM_DATA_V4; + else if(layerID == FWPS_LAYER_DATAGRAM_DATA_V4_DISCARD) + pLayerKey = &FWPM_LAYER_DATAGRAM_DATA_V4_DISCARD; + else if(layerID == FWPS_LAYER_DATAGRAM_DATA_V6) + pLayerKey = &FWPM_LAYER_DATAGRAM_DATA_V6; + else if(layerID == FWPS_LAYER_DATAGRAM_DATA_V6_DISCARD) + pLayerKey = &FWPM_LAYER_DATAGRAM_DATA_V6_DISCARD; + else if(layerID == FWPS_LAYER_INBOUND_ICMP_ERROR_V4) + pLayerKey = &FWPM_LAYER_INBOUND_ICMP_ERROR_V4; + else if(layerID == FWPS_LAYER_INBOUND_ICMP_ERROR_V4_DISCARD) + pLayerKey = &FWPM_LAYER_INBOUND_ICMP_ERROR_V4_DISCARD; + else if(layerID == FWPS_LAYER_INBOUND_ICMP_ERROR_V6) + pLayerKey = &FWPM_LAYER_INBOUND_ICMP_ERROR_V6; + else if(layerID == FWPS_LAYER_INBOUND_ICMP_ERROR_V6_DISCARD) + pLayerKey = &FWPM_LAYER_INBOUND_ICMP_ERROR_V6_DISCARD; + else if(layerID == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4) + pLayerKey = &FWPM_LAYER_OUTBOUND_ICMP_ERROR_V4; + else if(layerID == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4_DISCARD) + pLayerKey = &FWPM_LAYER_OUTBOUND_ICMP_ERROR_V4_DISCARD; + else if(layerID == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6) + pLayerKey = &FWPM_LAYER_OUTBOUND_ICMP_ERROR_V6; + else if(layerID == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6_DISCARD) + pLayerKey = &FWPM_LAYER_OUTBOUND_ICMP_ERROR_V6_DISCARD; + else if(layerID == FWPS_LAYER_ALE_RESOURCE_ASSIGNMENT_V4) + pLayerKey = &FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V4; + else if(layerID == FWPS_LAYER_ALE_RESOURCE_ASSIGNMENT_V4_DISCARD) + pLayerKey = &FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V4_DISCARD; + else if(layerID == FWPS_LAYER_ALE_RESOURCE_ASSIGNMENT_V6) + pLayerKey = &FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V6; + else if(layerID == FWPS_LAYER_ALE_RESOURCE_ASSIGNMENT_V6_DISCARD) + pLayerKey = &FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V6_DISCARD; + else if(layerID == FWPS_LAYER_ALE_AUTH_LISTEN_V4) + pLayerKey = &FWPM_LAYER_ALE_AUTH_LISTEN_V4; + else if(layerID == FWPS_LAYER_ALE_AUTH_LISTEN_V4_DISCARD) + pLayerKey = &FWPM_LAYER_ALE_AUTH_LISTEN_V4_DISCARD; + else if(layerID == FWPS_LAYER_ALE_AUTH_LISTEN_V6) + pLayerKey = &FWPM_LAYER_ALE_AUTH_LISTEN_V6; + else if(layerID == FWPS_LAYER_ALE_AUTH_LISTEN_V6_DISCARD) + pLayerKey = &FWPM_LAYER_ALE_AUTH_LISTEN_V6_DISCARD; + else if(layerID == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4) + pLayerKey = &FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4; + else if(layerID == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4_DISCARD) + pLayerKey = &FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4_DISCARD; + else if(layerID == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6) + pLayerKey = &FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6; + else if(layerID == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6_DISCARD) + pLayerKey = &FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6_DISCARD; + else if(layerID == FWPS_LAYER_ALE_AUTH_CONNECT_V4) + pLayerKey = &FWPM_LAYER_ALE_AUTH_CONNECT_V4; + else if(layerID == FWPS_LAYER_ALE_AUTH_CONNECT_V4_DISCARD) + pLayerKey = &FWPM_LAYER_ALE_AUTH_CONNECT_V4_DISCARD; + else if(layerID == FWPS_LAYER_ALE_AUTH_CONNECT_V6) + pLayerKey = &FWPM_LAYER_ALE_AUTH_CONNECT_V6; + else if(layerID == FWPS_LAYER_ALE_AUTH_CONNECT_V6_DISCARD) + pLayerKey = &FWPM_LAYER_ALE_AUTH_CONNECT_V6_DISCARD; + else if(layerID == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4) + pLayerKey = &FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4; + else if(layerID == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4_DISCARD) + pLayerKey = &FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4_DISCARD; + else if(layerID == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6) + pLayerKey = &FWPM_LAYER_ALE_FLOW_ESTABLISHED_V6; + else if(layerID == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6_DISCARD) + pLayerKey = &FWPM_LAYER_ALE_FLOW_ESTABLISHED_V6_DISCARD; + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + else if(layerID == FWPS_LAYER_NAME_RESOLUTION_CACHE_V4) + pLayerKey = &FWPM_LAYER_NAME_RESOLUTION_CACHE_V4; + else if(layerID == FWPS_LAYER_NAME_RESOLUTION_CACHE_V6) + pLayerKey = &FWPM_LAYER_NAME_RESOLUTION_CACHE_V6; + else if(layerID == FWPS_LAYER_ALE_RESOURCE_RELEASE_V4) + pLayerKey = &FWPM_LAYER_ALE_RESOURCE_RELEASE_V4; + else if(layerID == FWPS_LAYER_ALE_RESOURCE_RELEASE_V6) + pLayerKey = &FWPM_LAYER_ALE_RESOURCE_RELEASE_V6; + else if(layerID == FWPS_LAYER_ALE_ENDPOINT_CLOSURE_V4) + pLayerKey = &FWPM_LAYER_ALE_ENDPOINT_CLOSURE_V4; + else if(layerID == FWPS_LAYER_ALE_ENDPOINT_CLOSURE_V6) + pLayerKey = &FWPM_LAYER_ALE_ENDPOINT_CLOSURE_V6; + else if(layerID == FWPS_LAYER_ALE_CONNECT_REDIRECT_V4) + pLayerKey = &FWPM_LAYER_ALE_CONNECT_REDIRECT_V4; + else if(layerID == FWPS_LAYER_ALE_CONNECT_REDIRECT_V6) + pLayerKey = &FWPM_LAYER_ALE_CONNECT_REDIRECT_V6; + else if(layerID == FWPS_LAYER_ALE_BIND_REDIRECT_V4) + pLayerKey = &FWPM_LAYER_ALE_BIND_REDIRECT_V4; + else if(layerID == FWPS_LAYER_ALE_BIND_REDIRECT_V6) + pLayerKey = &FWPM_LAYER_ALE_BIND_REDIRECT_V6; + else if(layerID == FWPS_LAYER_STREAM_PACKET_V4) + pLayerKey = &FWPM_LAYER_STREAM_PACKET_V4; + else if(layerID == FWPS_LAYER_STREAM_PACKET_V6) + pLayerKey = &FWPM_LAYER_STREAM_PACKET_V6; + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + else if(layerID == FWPS_LAYER_INBOUND_MAC_FRAME_ETHERNET) + pLayerKey = &FWPM_LAYER_INBOUND_MAC_FRAME_ETHERNET; + else if(layerID == FWPS_LAYER_OUTBOUND_MAC_FRAME_ETHERNET) + pLayerKey = &FWPM_LAYER_OUTBOUND_MAC_FRAME_ETHERNET; + else if(layerID == FWPS_LAYER_INBOUND_MAC_FRAME_NATIVE) + pLayerKey = &FWPM_LAYER_INBOUND_MAC_FRAME_NATIVE; + else if(layerID == FWPS_LAYER_OUTBOUND_MAC_FRAME_NATIVE) + pLayerKey = &FWPM_LAYER_OUTBOUND_MAC_FRAME_NATIVE; + else if(layerID == FWPS_LAYER_INGRESS_VSWITCH_ETHERNET) + pLayerKey = &FWPM_LAYER_INGRESS_VSWITCH_ETHERNET; + else if(layerID == FWPS_LAYER_EGRESS_VSWITCH_ETHERNET) + pLayerKey = &FWPM_LAYER_EGRESS_VSWITCH_ETHERNET; + else if(layerID == FWPS_LAYER_INGRESS_VSWITCH_TRANSPORT_V4) + pLayerKey = &FWPM_LAYER_INGRESS_VSWITCH_TRANSPORT_V4; + else if(layerID == FWPS_LAYER_INGRESS_VSWITCH_TRANSPORT_V6) + pLayerKey = &FWPM_LAYER_INGRESS_VSWITCH_TRANSPORT_V6; + else if(layerID == FWPS_LAYER_EGRESS_VSWITCH_TRANSPORT_V4) + pLayerKey = &FWPM_LAYER_EGRESS_VSWITCH_TRANSPORT_V4; + else if(layerID == FWPS_LAYER_EGRESS_VSWITCH_TRANSPORT_V6) + pLayerKey = &FWPM_LAYER_EGRESS_VSWITCH_TRANSPORT_V6; + +#if(NTDDI_VERSION >= NTDDI_WINBLUE) + + else if(layerID == FWPS_LAYER_INBOUND_TRANSPORT_FAST) + pLayerKey = &FWPM_LAYER_INBOUND_TRANSPORT_FAST; + else if(layerID == FWPS_LAYER_OUTBOUND_TRANSPORT_FAST) + pLayerKey = &FWPM_LAYER_OUTBOUND_TRANSPORT_FAST; + else if(layerID == FWPS_LAYER_INBOUND_MAC_FRAME_NATIVE_FAST) + pLayerKey = &FWPM_LAYER_INBOUND_MAC_FRAME_NATIVE_FAST; + else if(layerID == FWPS_LAYER_OUTBOUND_MAC_FRAME_NATIVE_FAST) + pLayerKey = &FWPM_LAYER_OUTBOUND_MAC_FRAME_NATIVE_FAST; + +#endif // (NTDDI_VERSION >= NTDDI_WINBLUE) +#endif // (NTDDI_VERSION >= NTDDI_WIN8) +#endif // (NTDDI_VERSION >= NTDDI_WIN7) + + return pLayerKey; +} + +/** + @helper_function="HlprFwpmLayerGetByString" + + Purpose: Return a pointer to the GUID of the layer represented by the provided string.
+
+ Notes: Calling function must check for NULL pointer.
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA366492.aspx
+*/ +_Success_(return != 0) +const GUID* HlprFwpmLayerGetByString(_In_ PCWSTR pLayerString) +{ + ASSERT(pLayerString); + + const GUID* pLayerKey = 0; + + if(HlprStringsAreEqual(L"FWPM_LAYER_INBOUND_IPPACKET_V4", + pLayerString)) + pLayerKey = &FWPM_LAYER_INBOUND_IPPACKET_V4; + else if(HlprStringsAreEqual(L"FWPM_LAYER_INBOUND_IPPACKET_V4_DISCARD", + pLayerString)) + pLayerKey = &FWPM_LAYER_INBOUND_IPPACKET_V4_DISCARD; + else if(HlprStringsAreEqual(L"FWPM_LAYER_INBOUND_IPPACKET_V6", + pLayerString)) + pLayerKey = &FWPM_LAYER_INBOUND_IPPACKET_V6; + else if(HlprStringsAreEqual(L"FWPM_LAYER_INBOUND_IPPACKET_V6_DISCARD", + pLayerString)) + pLayerKey = &FWPM_LAYER_INBOUND_IPPACKET_V6_DISCARD; + else if(HlprStringsAreEqual(L"FWPM_LAYER_OUTBOUND_IPPACKET_V4", + pLayerString)) + pLayerKey = &FWPM_LAYER_OUTBOUND_IPPACKET_V4; + else if(HlprStringsAreEqual(L"FWPM_LAYER_OUTBOUND_IPPACKET_V4_DISCARD", + pLayerString)) + pLayerKey = &FWPM_LAYER_OUTBOUND_IPPACKET_V4_DISCARD; + else if(HlprStringsAreEqual(L"FWPM_LAYER_OUTBOUND_IPPACKET_V6", + pLayerString)) + pLayerKey = &FWPM_LAYER_OUTBOUND_IPPACKET_V6; + else if(HlprStringsAreEqual(L"FWPM_LAYER_OUTBOUND_IPPACKET_V6_DISCARD", + pLayerString)) + pLayerKey = &FWPM_LAYER_OUTBOUND_IPPACKET_V6_DISCARD; + else if(HlprStringsAreEqual(L"FWPM_LAYER_IPFORWARD_V4", + pLayerString)) + pLayerKey = &FWPM_LAYER_IPFORWARD_V4; + else if(HlprStringsAreEqual(L"FWPM_LAYER_IPFORWARD_V4_DISCARD", + pLayerString)) + pLayerKey = &FWPM_LAYER_IPFORWARD_V4_DISCARD; + else if(HlprStringsAreEqual(L"FWPM_LAYER_IPFORWARD_V6", + pLayerString)) + pLayerKey = &FWPM_LAYER_IPFORWARD_V6; + else if(HlprStringsAreEqual(L"FWPM_LAYER_IPFORWARD_V6_DISCARD", + pLayerString)) + pLayerKey = &FWPM_LAYER_IPFORWARD_V6_DISCARD; + else if(HlprStringsAreEqual(L"FWPM_LAYER_INBOUND_TRANSPORT_V4", + pLayerString)) + pLayerKey = &FWPM_LAYER_INBOUND_TRANSPORT_V4; + else if(HlprStringsAreEqual(L"FWPM_LAYER_INBOUND_TRANSPORT_V4_DISCARD", + pLayerString)) + pLayerKey = &FWPM_LAYER_INBOUND_TRANSPORT_V4_DISCARD; + else if(HlprStringsAreEqual(L"FWPM_LAYER_INBOUND_TRANSPORT_V6", + pLayerString)) + pLayerKey = &FWPM_LAYER_INBOUND_TRANSPORT_V6; + else if(HlprStringsAreEqual(L"FWPM_LAYER_INBOUND_TRANSPORT_V6_DISCARD", + pLayerString)) + pLayerKey = &FWPM_LAYER_INBOUND_TRANSPORT_V6_DISCARD; + else if(HlprStringsAreEqual(L"FWPM_LAYER_OUTBOUND_TRANSPORT_V4", + pLayerString)) + pLayerKey = &FWPM_LAYER_OUTBOUND_TRANSPORT_V4; + else if(HlprStringsAreEqual(L"FWPM_LAYER_OUTBOUND_TRANSPORT_V4_DISCARD", + pLayerString)) + pLayerKey = &FWPM_LAYER_OUTBOUND_TRANSPORT_V4_DISCARD; + else if(HlprStringsAreEqual(L"FWPM_LAYER_OUTBOUND_TRANSPORT_V6", + pLayerString)) + pLayerKey = &FWPM_LAYER_OUTBOUND_TRANSPORT_V6; + else if(HlprStringsAreEqual(L"FWPM_LAYER_OUTBOUND_TRANSPORT_V6_DISCARD", + pLayerString)) + pLayerKey = &FWPM_LAYER_OUTBOUND_TRANSPORT_V6_DISCARD; + else if(HlprStringsAreEqual(L"FWPM_LAYER_STREAM_V4", + pLayerString)) + pLayerKey = &FWPM_LAYER_STREAM_V4; + else if(HlprStringsAreEqual(L"FWPM_LAYER_STREAM_V4_DISCARD", + pLayerString)) + pLayerKey = &FWPM_LAYER_STREAM_V4_DISCARD; + else if(HlprStringsAreEqual(L"FWPM_LAYER_STREAM_V6", + pLayerString)) + pLayerKey = &FWPM_LAYER_STREAM_V6; + else if(HlprStringsAreEqual(L"FWPM_LAYER_STREAM_V6_DISCARD", + pLayerString)) + pLayerKey = &FWPM_LAYER_STREAM_V6_DISCARD; + else if(HlprStringsAreEqual(L"FWPM_LAYER_DATAGRAM_DATA_V4", + pLayerString)) + pLayerKey = &FWPM_LAYER_DATAGRAM_DATA_V4; + else if(HlprStringsAreEqual(L"FWPM_LAYER_DATAGRAM_DATA_V4_DISCARD", + pLayerString)) + pLayerKey = &FWPM_LAYER_DATAGRAM_DATA_V4_DISCARD; + else if(HlprStringsAreEqual(L"FWPM_LAYER_DATAGRAM_DATA_V6", + pLayerString)) + pLayerKey = &FWPM_LAYER_DATAGRAM_DATA_V6; + else if(HlprStringsAreEqual(L"FWPM_LAYER_DATAGRAM_DATA_V6_DISCARD", + pLayerString)) + pLayerKey = &FWPM_LAYER_DATAGRAM_DATA_V6_DISCARD; + else if(HlprStringsAreEqual(L"FWPM_LAYER_INBOUND_ICMP_ERROR_V4", + pLayerString)) + pLayerKey = &FWPM_LAYER_INBOUND_ICMP_ERROR_V4; + else if(HlprStringsAreEqual(L"FWPM_LAYER_INBOUND_ICMP_ERROR_V4_DISCARD", + pLayerString)) + pLayerKey = &FWPM_LAYER_INBOUND_ICMP_ERROR_V4_DISCARD; + else if(HlprStringsAreEqual(L"FWPM_LAYER_INBOUND_ICMP_ERROR_V6", + pLayerString)) + pLayerKey = &FWPM_LAYER_INBOUND_ICMP_ERROR_V6; + else if(HlprStringsAreEqual(L"FWPM_LAYER_INBOUND_ICMP_ERROR_V6_DISCARD", + pLayerString)) + pLayerKey = &FWPM_LAYER_INBOUND_ICMP_ERROR_V6_DISCARD; + else if(HlprStringsAreEqual(L"FWPM_LAYER_OUTBOUND_ICMP_ERROR_V4", + pLayerString)) + pLayerKey = &FWPM_LAYER_OUTBOUND_ICMP_ERROR_V4; + else if(HlprStringsAreEqual(L"FWPM_LAYER_OUTBOUND_ICMP_ERROR_V4_DISCARD", + pLayerString)) + pLayerKey = &FWPM_LAYER_OUTBOUND_ICMP_ERROR_V4_DISCARD; + else if(HlprStringsAreEqual(L"FWPM_LAYER_OUTBOUND_ICMP_ERROR_V6", + pLayerString)) + pLayerKey = &FWPM_LAYER_OUTBOUND_ICMP_ERROR_V6; + else if(HlprStringsAreEqual(L"FWPM_LAYER_OUTBOUND_ICMP_ERROR_V6_DISCARD", + pLayerString)) + pLayerKey = &FWPM_LAYER_OUTBOUND_ICMP_ERROR_V6_DISCARD; + else if(HlprStringsAreEqual(L"FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V4", + pLayerString)) + pLayerKey = &FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V4; + else if(HlprStringsAreEqual(L"FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V4_DISCARD", + pLayerString)) + pLayerKey = &FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V4_DISCARD; + else if(HlprStringsAreEqual(L"FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V6", + pLayerString)) + pLayerKey = &FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V6; + else if(HlprStringsAreEqual(L"FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V6_DISCARD", + pLayerString)) + pLayerKey = &FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V6_DISCARD; + else if(HlprStringsAreEqual(L"FWPM_LAYER_ALE_AUTH_LISTEN_V4", + pLayerString)) + pLayerKey = &FWPM_LAYER_ALE_AUTH_LISTEN_V4; + else if(HlprStringsAreEqual(L"FWPM_LAYER_ALE_AUTH_LISTEN_V4_DISCARD", + pLayerString)) + pLayerKey = &FWPM_LAYER_ALE_AUTH_LISTEN_V4_DISCARD; + else if(HlprStringsAreEqual(L"FWPM_LAYER_ALE_AUTH_LISTEN_V6", + pLayerString)) + pLayerKey = &FWPM_LAYER_ALE_AUTH_LISTEN_V6; + else if(HlprStringsAreEqual(L"FWPM_LAYER_ALE_AUTH_LISTEN_V6_DISCARD", + pLayerString)) + pLayerKey = &FWPM_LAYER_ALE_AUTH_LISTEN_V6_DISCARD; + else if(HlprStringsAreEqual(L"FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4", + pLayerString)) + pLayerKey = &FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4; + else if(HlprStringsAreEqual(L"FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4_DISCARD", + pLayerString)) + pLayerKey = &FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4_DISCARD; + else if(HlprStringsAreEqual(L"FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6", + pLayerString)) + pLayerKey = &FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6; + else if(HlprStringsAreEqual(L"FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6_DISCARD", + pLayerString)) + pLayerKey = &FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6_DISCARD; + else if(HlprStringsAreEqual(L"FWPM_LAYER_ALE_AUTH_CONNECT_V4", + pLayerString)) + pLayerKey = &FWPM_LAYER_ALE_AUTH_CONNECT_V4; + else if(HlprStringsAreEqual(L"FWPM_LAYER_ALE_AUTH_CONNECT_V4_DISCARD", + pLayerString)) + pLayerKey = &FWPM_LAYER_ALE_AUTH_CONNECT_V4_DISCARD; + else if(HlprStringsAreEqual(L"FWPM_LAYER_ALE_AUTH_CONNECT_V6", + pLayerString)) + pLayerKey = &FWPM_LAYER_ALE_AUTH_CONNECT_V6; + else if(HlprStringsAreEqual(L"FWPM_LAYER_ALE_AUTH_CONNECT_V6_DISCARD", + pLayerString)) + pLayerKey = &FWPM_LAYER_ALE_AUTH_CONNECT_V6_DISCARD; + else if(HlprStringsAreEqual(L"FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4", + pLayerString)) + pLayerKey = &FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4; + else if(HlprStringsAreEqual(L"FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4_DISCARD", + pLayerString)) + pLayerKey = &FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4_DISCARD; + else if(HlprStringsAreEqual(L"FWPM_LAYER_ALE_FLOW_ESTABLISHED_V6", + pLayerString)) + pLayerKey = &FWPM_LAYER_ALE_FLOW_ESTABLISHED_V6; + else if(HlprStringsAreEqual(L"FWPM_LAYER_ALE_FLOW_ESTABLISHED_V6_DISCARD", + pLayerString)) + pLayerKey = &FWPM_LAYER_ALE_FLOW_ESTABLISHED_V6_DISCARD; + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + else if(HlprStringsAreEqual(L"FWPM_LAYER_NAME_RESOLUTION_CACHE_V4", + pLayerString)) + pLayerKey = &FWPM_LAYER_NAME_RESOLUTION_CACHE_V4; + else if(HlprStringsAreEqual(L"FWPM_LAYER_NAME_RESOLUTION_CACHE_V6", + pLayerString)) + pLayerKey = &FWPM_LAYER_NAME_RESOLUTION_CACHE_V6; + else if(HlprStringsAreEqual(L"FWPM_LAYER_ALE_RESOURCE_RELEASE_V4", + pLayerString)) + pLayerKey = &FWPM_LAYER_ALE_RESOURCE_RELEASE_V4; + else if(HlprStringsAreEqual(L"FWPM_LAYER_ALE_RESOURCE_RELEASE_V6", + pLayerString)) + pLayerKey = &FWPM_LAYER_ALE_RESOURCE_RELEASE_V6; + else if(HlprStringsAreEqual(L"FWPM_LAYER_ALE_ENDPOINT_CLOSURE_V4", + pLayerString)) + pLayerKey = &FWPM_LAYER_ALE_ENDPOINT_CLOSURE_V4; + else if(HlprStringsAreEqual(L"FWPM_LAYER_ALE_ENDPOINT_CLOSURE_V6", + pLayerString)) + pLayerKey = &FWPM_LAYER_ALE_ENDPOINT_CLOSURE_V6; + else if(HlprStringsAreEqual(L"FWPM_LAYER_ALE_CONNECT_REDIRECT_V4", + pLayerString)) + pLayerKey = &FWPM_LAYER_ALE_CONNECT_REDIRECT_V4; + else if(HlprStringsAreEqual(L"FWPM_LAYER_ALE_CONNECT_REDIRECT_V6", + pLayerString)) + pLayerKey = &FWPM_LAYER_ALE_CONNECT_REDIRECT_V6; + else if(HlprStringsAreEqual(L"FWPM_LAYER_ALE_BIND_REDIRECT_V4", + pLayerString)) + pLayerKey = &FWPM_LAYER_ALE_BIND_REDIRECT_V4; + else if(HlprStringsAreEqual(L"FWPM_LAYER_ALE_BIND_REDIRECT_V6", + pLayerString)) + pLayerKey = &FWPM_LAYER_ALE_BIND_REDIRECT_V6; + else if(HlprStringsAreEqual(L"FWPM_LAYER_STREAM_PACKET_V4", + pLayerString)) + pLayerKey = &FWPM_LAYER_STREAM_PACKET_V4; + else if(HlprStringsAreEqual(L"FWPM_LAYER_STREAM_PACKET_V6", + pLayerString)) + pLayerKey = &FWPM_LAYER_STREAM_PACKET_V6; + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + else if(HlprStringsAreEqual(L"FWPM_LAYER_INBOUND_MAC_FRAME_ETHERNET", + pLayerString)) + pLayerKey = &FWPM_LAYER_INBOUND_MAC_FRAME_ETHERNET; + else if(HlprStringsAreEqual(L"FWPM_LAYER_OUTBOUND_MAC_FRAME_ETHERNET", + pLayerString)) + pLayerKey = &FWPM_LAYER_OUTBOUND_MAC_FRAME_ETHERNET; + else if(HlprStringsAreEqual(L"FWPM_LAYER_INBOUND_MAC_FRAME_NATIVE", + pLayerString)) + pLayerKey = &FWPM_LAYER_INBOUND_MAC_FRAME_NATIVE; + else if(HlprStringsAreEqual(L"FWPM_LAYER_OUTBOUND_MAC_FRAME_NATIVE", + pLayerString)) + pLayerKey = &FWPM_LAYER_OUTBOUND_MAC_FRAME_NATIVE; + else if(HlprStringsAreEqual(L"FWPM_LAYER_INGRESS_VSWITCH_ETHERNET", + pLayerString)) + pLayerKey = &FWPM_LAYER_INGRESS_VSWITCH_ETHERNET; + else if(HlprStringsAreEqual(L"FWPM_LAYER_EGRESS_VSWITCH_ETHERNET", + pLayerString)) + pLayerKey = &FWPM_LAYER_EGRESS_VSWITCH_ETHERNET; + else if(HlprStringsAreEqual(L"FWPM_LAYER_INGRESS_VSWITCH_TRANSPORT_V4", + pLayerString)) + pLayerKey = &FWPM_LAYER_INGRESS_VSWITCH_TRANSPORT_V4; + else if(HlprStringsAreEqual(L"FWPM_LAYER_INGRESS_VSWITCH_TRANSPORT_V6", + pLayerString)) + pLayerKey = &FWPM_LAYER_INGRESS_VSWITCH_TRANSPORT_V6; + else if(HlprStringsAreEqual(L"FWPM_LAYER_EGRESS_VSWITCH_TRANSPORT_V4", + pLayerString)) + pLayerKey = &FWPM_LAYER_EGRESS_VSWITCH_TRANSPORT_V4; + else if(HlprStringsAreEqual(L"FWPM_LAYER_EGRESS_VSWITCH_TRANSPORT_V6", + pLayerString)) + pLayerKey = &FWPM_LAYER_EGRESS_VSWITCH_TRANSPORT_V6; + +#if(NTDDI_VERSION >= NTDDI_WINBLUE) + + else if(HlprStringsAreEqual(L"FWPM_LAYER_INBOUND_TRANSPORT_FAST", + pLayerString)) + pLayerKey = &FWPM_LAYER_INBOUND_TRANSPORT_FAST; + else if(HlprStringsAreEqual(L"FWPM_LAYER_OUTBOUND_TRANSPORT_FAST", + pLayerString)) + pLayerKey = &FWPM_LAYER_OUTBOUND_TRANSPORT_FAST; + else if(HlprStringsAreEqual(L"FWPM_LAYER_INBOUND_MAC_FRAME_NATIVE_FAST", + pLayerString)) + pLayerKey = &FWPM_LAYER_INBOUND_MAC_FRAME_NATIVE_FAST; + else if(HlprStringsAreEqual(L"FWPM_LAYER_OUTBOUND_MAC_FRAME_NATIVE_FAST", + pLayerString)) + pLayerKey = &FWPM_LAYER_OUTBOUND_MAC_FRAME_NATIVE_FAST; + +#endif // (NTDDI_VERSION >= NTDDI_WINBLUE) +#endif // (NTDDI_VERSION >= NTDDI_WIN8) +#endif // (NTDDI_VERSION >= NTDDI_WIN7) + + return pLayerKey; +} + +/** + @helper function="HlprFwpmLayerIsUserMode" + + Purpose: Determine if the layer is a user-mode layer.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +BOOLEAN HlprFwpmLayerIsUserMode(_In_ const GUID* pLayerKey) +{ + ASSERT(pLayerKey); + + BOOLEAN isUserMode = FALSE; + + for(UINT32 layerIndex = 0; + layerIndex < TOTAL_USER_MODE_LAYER_COUNT && + isUserMode == FALSE; + layerIndex++) + { + if(pLayerKey == ppUserModeLayerKeyArray[layerIndex]) + isUserMode = TRUE; + } + + return isUserMode; +} + +/** + @helper function="HlprFwpmLayerIsKernelMode" + + Purpose: Determine if the layer is a kernel-mode layer.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +BOOLEAN HlprFwpmLayerIsKernelMode(_In_ const GUID* pLayerKey) +{ + ASSERT(pLayerKey); + + return !(HlprFwpmLayerIsUserMode(pLayerKey)); +} + +/** + @helper function="HlprFwpmLayerIsIPv4" + + Purpose: Determine if the layer is an IPv4 layer.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +BOOLEAN HlprFwpmLayerIsIPv4(_In_ const GUID* pLayerKey) +{ + ASSERT(pLayerKey); + + BOOLEAN isIPv4 = FALSE; + + if(*pLayerKey == FWPM_LAYER_INBOUND_IPPACKET_V4 || + *pLayerKey == FWPM_LAYER_INBOUND_IPPACKET_V4_DISCARD || + *pLayerKey == FWPM_LAYER_OUTBOUND_IPPACKET_V4 || + *pLayerKey == FWPM_LAYER_OUTBOUND_IPPACKET_V4_DISCARD || + *pLayerKey == FWPM_LAYER_IPFORWARD_V4 || + *pLayerKey == FWPM_LAYER_IPFORWARD_V4_DISCARD || + *pLayerKey == FWPM_LAYER_INBOUND_TRANSPORT_V4 || + *pLayerKey == FWPM_LAYER_INBOUND_TRANSPORT_V4_DISCARD || + *pLayerKey == FWPM_LAYER_OUTBOUND_TRANSPORT_V4 || + *pLayerKey == FWPM_LAYER_OUTBOUND_TRANSPORT_V4_DISCARD || + *pLayerKey == FWPM_LAYER_STREAM_V4 || + *pLayerKey == FWPM_LAYER_STREAM_V4_DISCARD || + *pLayerKey == FWPM_LAYER_DATAGRAM_DATA_V4 || + *pLayerKey == FWPM_LAYER_DATAGRAM_DATA_V4_DISCARD || + *pLayerKey == FWPM_LAYER_INBOUND_ICMP_ERROR_V4 || + *pLayerKey == FWPM_LAYER_INBOUND_ICMP_ERROR_V4_DISCARD || + *pLayerKey == FWPM_LAYER_OUTBOUND_ICMP_ERROR_V4 || + *pLayerKey == FWPM_LAYER_OUTBOUND_ICMP_ERROR_V4_DISCARD || + *pLayerKey == FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V4 || + *pLayerKey == FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V4_DISCARD || + *pLayerKey == FWPM_LAYER_ALE_AUTH_LISTEN_V4 || + *pLayerKey == FWPM_LAYER_ALE_AUTH_LISTEN_V4_DISCARD || + *pLayerKey == FWPM_LAYER_ALE_AUTH_CONNECT_V4 || + *pLayerKey == FWPM_LAYER_ALE_AUTH_CONNECT_V4_DISCARD || + *pLayerKey == FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 || + *pLayerKey == FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4_DISCARD || + *pLayerKey == FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4 || + *pLayerKey == FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4_DISCARD || + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + *pLayerKey == FWPM_LAYER_NAME_RESOLUTION_CACHE_V4 || + *pLayerKey == FWPM_LAYER_ALE_RESOURCE_RELEASE_V4 || + *pLayerKey == FWPM_LAYER_ALE_ENDPOINT_CLOSURE_V4 || + *pLayerKey == FWPM_LAYER_ALE_CONNECT_REDIRECT_V4 || + *pLayerKey == FWPM_LAYER_ALE_BIND_REDIRECT_V4 || + *pLayerKey == FWPM_LAYER_STREAM_PACKET_V4 || + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + *pLayerKey == FWPM_LAYER_INGRESS_VSWITCH_TRANSPORT_V4 || + *pLayerKey == FWPM_LAYER_EGRESS_VSWITCH_TRANSPORT_V4 || + +#endif // (NTDDI_VERSION >= NTDDI_WIN8) +#endif // (NTDDI_VERSION >= NTDDI_WIN7) + + *pLayerKey == FWPM_LAYER_IPSEC_KM_DEMUX_V4 || + *pLayerKey == FWPM_LAYER_IPSEC_V4 || + *pLayerKey == FWPM_LAYER_IKEEXT_V4) + isIPv4 = TRUE; + + return isIPv4; +} + +/** + @helper function="HlprFwpmLayerIsIPv6" + + Purpose: Determine if the layer is an IPv6 layer.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +BOOLEAN HlprFwpmLayerIsIPv6(_In_ const GUID* pLayerKey) +{ + ASSERT(pLayerKey); + + BOOLEAN isIPv6 = FALSE; + + if(*pLayerKey == FWPM_LAYER_INBOUND_IPPACKET_V6 || + *pLayerKey == FWPM_LAYER_INBOUND_IPPACKET_V6_DISCARD || + *pLayerKey == FWPM_LAYER_OUTBOUND_IPPACKET_V6 || + *pLayerKey == FWPM_LAYER_OUTBOUND_IPPACKET_V6_DISCARD || + *pLayerKey == FWPM_LAYER_IPFORWARD_V6 || + *pLayerKey == FWPM_LAYER_IPFORWARD_V6_DISCARD || + *pLayerKey == FWPM_LAYER_INBOUND_TRANSPORT_V6 || + *pLayerKey == FWPM_LAYER_INBOUND_TRANSPORT_V6_DISCARD || + *pLayerKey == FWPM_LAYER_OUTBOUND_TRANSPORT_V6 || + *pLayerKey == FWPM_LAYER_OUTBOUND_TRANSPORT_V6_DISCARD || + *pLayerKey == FWPM_LAYER_STREAM_V6 || + *pLayerKey == FWPM_LAYER_STREAM_V6_DISCARD || + *pLayerKey == FWPM_LAYER_DATAGRAM_DATA_V6 || + *pLayerKey == FWPM_LAYER_DATAGRAM_DATA_V6_DISCARD || + *pLayerKey == FWPM_LAYER_INBOUND_ICMP_ERROR_V6 || + *pLayerKey == FWPM_LAYER_INBOUND_ICMP_ERROR_V6_DISCARD || + *pLayerKey == FWPM_LAYER_OUTBOUND_ICMP_ERROR_V6 || + *pLayerKey == FWPM_LAYER_OUTBOUND_ICMP_ERROR_V6_DISCARD || + *pLayerKey == FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V6 || + *pLayerKey == FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V6_DISCARD || + *pLayerKey == FWPM_LAYER_ALE_AUTH_LISTEN_V6 || + *pLayerKey == FWPM_LAYER_ALE_AUTH_LISTEN_V6_DISCARD || + *pLayerKey == FWPM_LAYER_ALE_AUTH_CONNECT_V6 || + *pLayerKey == FWPM_LAYER_ALE_AUTH_CONNECT_V6_DISCARD || + *pLayerKey == FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 || + *pLayerKey == FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6_DISCARD || + *pLayerKey == FWPM_LAYER_ALE_FLOW_ESTABLISHED_V6 || + *pLayerKey == FWPM_LAYER_ALE_FLOW_ESTABLISHED_V6_DISCARD || + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + *pLayerKey == FWPM_LAYER_NAME_RESOLUTION_CACHE_V6 || + *pLayerKey == FWPM_LAYER_ALE_RESOURCE_RELEASE_V6 || + *pLayerKey == FWPM_LAYER_ALE_ENDPOINT_CLOSURE_V6 || + *pLayerKey == FWPM_LAYER_ALE_CONNECT_REDIRECT_V6 || + *pLayerKey == FWPM_LAYER_ALE_BIND_REDIRECT_V6 || + *pLayerKey == FWPM_LAYER_STREAM_PACKET_V6 || + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + *pLayerKey == FWPM_LAYER_INGRESS_VSWITCH_TRANSPORT_V6 || + *pLayerKey == FWPM_LAYER_EGRESS_VSWITCH_TRANSPORT_V6 || + +#endif // (NTDDI_VERSION >= NTDDI_WIN8) +#endif // (NTDDI_VERSION >= NTDDI_WIN7) + + *pLayerKey == FWPM_LAYER_IPSEC_KM_DEMUX_V6 || + *pLayerKey == FWPM_LAYER_IPSEC_V6 || + *pLayerKey == FWPM_LAYER_IKEEXT_V6) + isIPv6 = TRUE; + + return isIPv6; +} diff --git a/network/trans/WFPSampler/lib/HelperFunctions_FwpmLayer.h b/network/trans/WFPSampler/lib/HelperFunctions_FwpmLayer.h new file mode 100644 index 000000000..61b64ccee --- /dev/null +++ b/network/trans/WFPSampler/lib/HelperFunctions_FwpmLayer.h @@ -0,0 +1,54 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_FwpmLayer.cpp +// +// Abstract: +// This module contains prototypes for functions which assist in actions pertaining to +// FWPM_LAYER objects. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Add basic support for WinBlue Fast layers +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef HELPERFUNCTIONS_FWPM_LAYER_H +#define HELPERFUNCTIONS_FWPM_LAYER_H + +_At_(*pppFilterConditionArray, _Pre_ _Null_) +_When_(return != NO_ERROR, _At_(*pppFilterConditionArray, _Post_ _Null_)) +_When_(return == NO_ERROR, _At_(*pppFilterConditionArray, _Post_ _Maybenull_)) +_Success_(return == NO_ERROR) +UINT32 HlprFwpmLayerGetFilterConditionArrayByKey(_In_ const GUID* pLayerKey, + _Outptr_result_buffer_maybenull_(*pConditionArrayCount) GUID*** pppFilterConditionArray, + _Out_ UINT16* pConditionArrayCount); + +_Success_(return < FWPS_BUILTIN_LAYER_MAX) +UINT8 HlprFwpmLayerGetIDByKey(_In_ const GUID* pLayerKey); + +_Success_(return < FWPS_BUILTIN_LAYER_MAX) +UINT8 HlprFwpmLayerGetIDByString(_In_ PCWSTR pLayerString); + +_Success_(return != 0) +const GUID* HlprFwpmLayerGetByString(_In_ PCWSTR pLayerString); + +_Success_(return != 0) +const GUID* HlprFwpmLayerGetByID(_In_ const UINT32 pLayerID); + +BOOLEAN HlprFwpmLayerIsUserMode(_In_ const GUID* pLayerKey); + +BOOLEAN HlprFwpmLayerIsKernelMode(_In_ const GUID* pLayerKey); + +BOOLEAN HlprFwpmLayerIsIPv4(_In_ const GUID* pLayerKey); + +BOOLEAN HlprFwpmLayerIsIPv6(_In_ const GUID* pLayerKey); + +#endif /// HELPERFUNCTIONS_FWPM_LAYER_H \ No newline at end of file diff --git a/network/trans/WFPSampler/lib/HelperFunctions_FwpmProvider.cpp b/network/trans/WFPSampler/lib/HelperFunctions_FwpmProvider.cpp new file mode 100644 index 000000000..3e7f07572 --- /dev/null +++ b/network/trans/WFPSampler/lib/HelperFunctions_FwpmProvider.cpp @@ -0,0 +1,365 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_FwpmProvider.cpp +// +// Abstract: +// This module contains functions which assist in actions pertaining to FWPM_PROVIDER objects. +// +// Naming Convention: +// +// +// +// i.e. +// +// +// { +// - Function is likely visible to other modules. +// } +// +// { +// Hlpr - Function is from HelperFunctions_* Modules. +// } +// +// { +// FwpmProvider - Function pertains to FWPM_PROVIDER objects. +// } +// +// { +// Add - Function adds an object. +// Remove - Function deletes objects. +// } +// +// { +// DefaultGlobal - Function acts on the programs constant global object. +// } +// Private Functions: +// +// Public Functions: +// HlprFwpmProviderAddDefaultGlobal(), +// HlprFwpmProviderRemoveDefaultGlobal(), +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "HelperFunctions_Include.h" /// . + +/** + @helper_function="HlprFwpmProviderEnum" + + Purpose: Wrapper for the FwpmProviderEnum API.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA364197.aspx
+*/ +_At_(*pppEntries, _Pre_ _Null_) +_When_(return != NO_ERROR, _At_(*pppEntries, _Post_ _Null_)) +_Success_(return == NO_ERROR) +UINT32 HlprFwpmProviderEnum(_In_ const HANDLE engineHandle, + _In_ const HANDLE enumHandle, + _In_ const UINT32 numEntriesRequested, + _Outptr_result_buffer_maybenull_(*pNumEntriesReturned) FWPM_PROVIDER*** pppEntries, + _Out_ UINT32* pNumEntriesReturned) +{ + UINT32 status = NO_ERROR; + + if(engineHandle && + enumHandle && + numEntriesRequested && + pppEntries && + pNumEntriesReturned) + { + status = FwpmProviderEnum(engineHandle, + enumHandle, + numEntriesRequested, + pppEntries, + pNumEntriesReturned); + if(status != NO_ERROR && + status != FWP_E_PROVIDER_NOT_FOUND && + status != FWP_E_NOT_FOUND) + HlprLogError(L"HlprFwpmProviderEnum : FwpmProviderEnum() [status: %#x]", + status); + } + else + { + if(pppEntries) + *pppEntries = 0; + + if(pNumEntriesReturned) + *pNumEntriesReturned = 0; + + status = ERROR_INVALID_PARAMETER; + + HlprLogError(L"HlprFwpmProviderEnum() [status: %#x][engineHandle: %#p][enumHandle: %#p][numEntriesRequested: %d][pppEntries: %#p][pNumEntriesReturned: %#p]", + status, + engineHandle, + enumHandle, + numEntriesRequested, + pppEntries, + pNumEntriesReturned); + } + + return status; +} + +/** + @helper_function="HlprFwpmProviderDestroyEnumHandle" + + Purpose: Wrapper for the FwpmProviderDestroyEnumHandle API.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA364196.aspx
+*/ +_When_(return != NO_ERROR, _At_(*pEnumHandle, _Post_ _Notnull_)) +_When_(return == NO_ERROR, _At_(*pEnumHandle, _Post_ _Null_)) +_Success_(return == NO_ERROR) +UINT32 HlprFwpmProviderDestroyEnumHandle(_In_ const HANDLE engineHandle, + _Inout_ HANDLE* pEnumHandle) +{ + UINT32 status = NO_ERROR; + + if(engineHandle && + pEnumHandle) + { + if(*pEnumHandle) + { + status = FwpmProviderDestroyEnumHandle(engineHandle, + *pEnumHandle); + if(status != NO_ERROR) + { + HlprLogError(L"HlprFwpmProviderDestroyEnumHandle : FwpmProviderDestroyEnumHandle() [status: %#x]", + status); + + HLPR_BAIL; + } + + *pEnumHandle = 0; + } + } + else + { + status = ERROR_INVALID_PARAMETER; + + HlprLogError(L"HlprFwpmProviderDestroyEnumHandle() [status: %#x][engineHandle: %#p][pEnumHandle: %#p]", + status, + engineHandle, + pEnumHandle); + } + + HLPR_BAIL_LABEL: + + return status; +} + +/** + @helper_function="HlprFwpmProviderCreateEnumHandle" + + Purpose: Wrapper for the FwpmProviderCreateEnumHandle API.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA364194.aspx
+*/ +_When_(return != NO_ERROR, _At_(*pEnumHandle, _Post_ _Null_)) +_When_(return == NO_ERROR, _At_(*pEnumHandle, _Post_ _Notnull_)) +_Success_(return == NO_ERROR) +UINT32 HlprFwpmProviderCreateEnumHandle(_In_ const HANDLE engineHandle, + _In_opt_ const FWPM_PROVIDER_ENUM_TEMPLATE* pEnumTemplate, + _Out_ HANDLE* pEnumHandle) +{ + UINT32 status = NO_ERROR; + + if(engineHandle && + pEnumHandle) + { + status = FwpmProviderCreateEnumHandle(engineHandle, + pEnumTemplate, + pEnumHandle); + if(status != NO_ERROR) + HlprLogError(L"HlprFwpmProviderCreateEnumHandle : FwpmProviderCreateEnumHandle() [status: %#x]", + status); + } + else + { + status = ERROR_INVALID_PARAMETER; + + HlprLogError(L"HlprFwpmProviderCreateEnumHandle() [status: %#x][engineHandle: %#p][pEnumHandle: %#p]", + status, + engineHandle, + pEnumHandle); + } + + return status; +} + +/** + @helper_function="HlprFwpmProviderDeleteByKey" + + Purpose: Wrapper for the FwpmProviderDeleteByKey API.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA364195.aspx
+*/ +_Success_(return == NO_ERROR) +UINT32 HlprFwpmProviderDeleteByKey(_In_ const HANDLE engineHandle, + _In_ const GUID* pProviderKey) +{ + UINT32 status = NO_ERROR; + + if(engineHandle) + { + if(pProviderKey) + { + status = FwpmProviderDeleteByKey(engineHandle, + pProviderKey); + if(status != NO_ERROR) + { + if(status != FWP_E_IN_USE && + status != FWP_E_BUILTIN_OBJECT && + status != FWP_E_PROVIDER_NOT_FOUND) + HlprLogError(L"HlprFwpmProviderDeleteByKey : FwpmProviderDeleteByKey() [status: %#x]", + status); + else + { + HlprLogInfo(L"HlprFwpmProviderDeleteByKey : FwpmProviderDeleteByKey() [status: %#x]", + status); + + status = NO_ERROR; + } + } + } + } + else + { + status = ERROR_INVALID_PARAMETER; + + HlprLogError(L"HlprFwpmProviderDeleteByKey() [status: %#x][engineHandle: %#p]", + status, + engineHandle); + } + + return status; +} + +/** + @helper_function="HlprFwpmProviderDelete" + + Purpose: Remove the provider that was associated with all of this program's WFP objects.
+
+ Notes: Function will fail if any of this program's WFP objects are still present and + associated with this provider.
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA364195.aspx
+*/ +_Success_(return == NO_ERROR) +UINT32 HlprFwpmProviderDelete(_In_opt_ HANDLE* pEngineHandle, + _In_ const GUID* pProviderKey) +{ + UINT32 status = NO_ERROR; + HANDLE engineHandle = 0; + BOOLEAN isLocal = FALSE; + + if(pEngineHandle && + *pEngineHandle) + engineHandle = *pEngineHandle; + else + { + status = HlprFwpmEngineOpen(&engineHandle); + HLPR_BAIL_ON_FAILURE(status); + + isLocal = TRUE; + } + + status = FwpmProviderDeleteByKey(engineHandle, + pProviderKey); + if(status != NO_ERROR) + { + if(status != FWP_E_PROVIDER_NOT_FOUND && + status != FWP_E_IN_USE) + HlprLogError(L"HlprFwpmProviderDelete : FwpmProviderDeleteByKey() [status: %#x]", + status); + else + { + HlprLogInfo(L"HlprFwpmProviderDelete : FwpmProviderDeleteByKey() [status: %#x]", + status); + + status = NO_ERROR; + } + } + + HLPR_BAIL_LABEL: + + if(isLocal && + engineHandle) + HlprFwpmEngineClose(&engineHandle); + + return status; +} + +/** + @helper_function="HlprFwpmProviderAdd" + + Purpose: Add a default provider to associate with all of this program's WFP objects.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA364180.aspx
+*/ +_Success_(return == NO_ERROR) +UINT32 HlprFwpmProviderAdd(_In_ HANDLE engineHandle, + _In_ const GUID* pProviderKey, + _In_ PCWSTR pCompanyName, + _In_ PCWSTR pBinaryDescription, + _In_ PCWSTR pServiceName, + _In_ UINT32 flags) +{ + UINT32 status = NO_ERROR; + + if(engineHandle) + { + FWPM_PROVIDER provider = {0}; + + provider.providerKey = *pProviderKey; + provider.displayData.name = (PWSTR)pCompanyName; + provider.displayData.description = (PWSTR)pBinaryDescription; + provider.flags = flags; + provider.serviceName = (PWSTR)pServiceName; + + status = FwpmProviderAdd(engineHandle, + &provider, + 0); + if(status != NO_ERROR) + { + if(status == FWP_E_ALREADY_EXISTS) + { + HlprLogInfo(L"Provider Already Exists"); + + status = NO_ERROR; + } + else + HlprLogError(L"HlprFwpmProviderAdd : FwpmProviderAdd() [status: %#x]", + status); + } + } + else + { + status = ERROR_INVALID_PARAMETER; + + HlprLogError(L"HlprFwpmProviderAdd() [status: %#x]", + status); + } + + return status; +} diff --git a/network/trans/WFPSampler/lib/HelperFunctions_FwpmProvider.h b/network/trans/WFPSampler/lib/HelperFunctions_FwpmProvider.h new file mode 100644 index 000000000..f83173dbc --- /dev/null +++ b/network/trans/WFPSampler/lib/HelperFunctions_FwpmProvider.h @@ -0,0 +1,63 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_FwpmProvider.h +// +// Abstract: +// This module contains prototypes for functions which assist in actions pertaining to +// FWPM_PROVIDER objects. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef HELPERFUNCTIONS_FWPM_PROVIDER_H +#define HELPERFUNCTIONS_FWPM_PROVIDER_H + +_At_(*pppEntries, _Pre_ _Null_) +_When_(return != NO_ERROR, _At_(*pppEntries, _Post_ _Null_)) +_Success_(return == NO_ERROR) +UINT32 HlprFwpmProviderEnum(_In_ const HANDLE engineHandle, + _In_ const HANDLE enumHandle, + _In_ const UINT32 numEntriesRequested, + _Outptr_result_buffer_maybenull_(*pNumEntriesReturned) FWPM_PROVIDER*** pppEntries, + _Out_ UINT32* pNumEntriesReturned); + +_When_(return != NO_ERROR, _At_(*pEnumHandle, _Post_ _Notnull_)) +_When_(return == NO_ERROR, _At_(*pEnumHandle, _Post_ _Null_)) +_Success_(return == NO_ERROR) +UINT32 HlprFwpmProviderDestroyEnumHandle(_In_ const HANDLE engineHandle, + _Inout_ HANDLE* pEnumHandle); + +_When_(return != NO_ERROR, _At_(*pEnumHandle, _Post_ _Null_)) +_When_(return == NO_ERROR, _At_(*pEnumHandle, _Post_ _Notnull_)) +_Success_(return == NO_ERROR) +UINT32 HlprFwpmProviderCreateEnumHandle(_In_ const HANDLE engineHandle, + _In_opt_ const FWPM_PROVIDER_ENUM_TEMPLATE* pEnumTemplate, + _Out_ HANDLE* pEnumHandle); + +_Success_(return == NO_ERROR) +UINT32 HlprFwpmProviderDeleteByKey(_In_ const HANDLE engineHandle, + _In_ const GUID* pProviderKey); + +_Success_(return == NO_ERROR) +UINT32 HlprFwpmProviderDelete(_In_opt_ HANDLE* pEngineHandle, + _In_ const GUID* pProviderKey); + +_Success_(return == NO_ERROR) +UINT32 HlprFwpmProviderAdd(_In_ HANDLE engineHandle, + _In_ const GUID* pProviderKey, + _In_ PCWSTR pCompanyName, + _In_ PCWSTR pBinaryDescription, + _In_ PCWSTR pServiceName, + _In_ UINT32 flags); + +#endif /// HELPERFUNCTIONS_FWPM_PROVIDER_H \ No newline at end of file diff --git a/network/trans/WFPSampler/lib/HelperFunctions_FwpmProviderContext.cpp b/network/trans/WFPSampler/lib/HelperFunctions_FwpmProviderContext.cpp new file mode 100644 index 000000000..74dcd5462 --- /dev/null +++ b/network/trans/WFPSampler/lib/HelperFunctions_FwpmProviderContext.cpp @@ -0,0 +1,340 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_FwpmFilter.cpp +// +// Abstract: +// This module contains functions which assist in actions pertaining to FWPM_FILTER objects. +// +// Naming Convention: +// +// +// +// i.e. +// +// +// { +// - Function is likely visible to other modules. +// Prv - Function is private to this module. +// } +// +// { +// Hlpr - Function is from HelperFunctions_* Modules. +// } +// +// { +// FwpmProviderContext - Function pertains to FWPM_PROVIDER_CONTEXT objects. +// } +// +// { +// Add - Function adds an object. +// Create - Function allocates memory for an object. +// Delete - Function deletes an object. +// Destroy - Function frees memory for an object. +// Enum - Function returns list of requested objects. +// Remove - Function deletes objects. +// } +// +// { +// All - Functions acts on all of WFPSampler's provider contexts. +// ByKey - Function acts off of the object's GUID. +// EnumHandle - Function acts on the enumeration handle. +// } +// +// Private Functions: +// +// Public Functions: +// HlprFwpmProviderContextAdd(), +// HlprFwpmProviderContextCreateEnumHandle(), +// HlprFwpmProviderContextDeleteByKey(), +// HlprFwpmProviderContextDestroyEnumHandle(), +// HlprFwpmProviderContextEnum(), +// HlprFwpmProviderContextRemoveAll(), +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "HelperFunctions_Include.h" /// . + +/** + @helper_function="HlprFwpmProviderContextDeleteByKey" + + Purpose: Wrapper for the FwpmProviderContextDeleteByKey API.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA364184.aspx
+*/ +_Success_(return == NO_ERROR) +UINT32 HlprFwpmProviderContextDeleteByKey(_In_ const HANDLE engineHandle, + _In_ const GUID* pProviderContextKey) +{ + ASSERT(engineHandle); + ASSERT(pProviderContextKey); + + UINT32 status = NO_ERROR; + + status = FwpmProviderContextDeleteByKey(engineHandle, + pProviderContextKey); + if(status != NO_ERROR) + { + if(status != FWP_E_IN_USE && + status != FWP_E_BUILTIN_OBJECT && + status != FWP_E_PROVIDER_CONTEXT_NOT_FOUND) + HlprLogError(L"HlprFwpmProviderContextDeleteByKey : FwpmProviderContextDeleteByKey() [status: %#x]", + status); + else + { + HlprLogInfo(L"HlprFwpmProviderContextDeleteByKey : FwpmProviderContextDeleteByKey() [status: %#x]", + status); + + status = NO_ERROR; + } + } + + return status; +} + +/** + @helper_function="HlprFwpmProviderContextAdd" + + Purpose: Wrapper for the FwpmProviderContextAdd API.
+
+ Notes: ProviderContext ID is written to pProviderContext->providerContextId.
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA364181.aspx
+*/ +_Success_(return == NO_ERROR) +UINT32 HlprFwpmProviderContextAdd(_In_ const HANDLE engineHandle, + _Inout_ FWPM_PROVIDER_CONTEXT* pProviderContext) +{ + ASSERT(engineHandle); + ASSERT(pProviderContext); + + UINT32 status = NO_ERROR; + + status = FwpmProviderContextAdd(engineHandle, + pProviderContext, + 0, + &(pProviderContext->providerContextId)); + if(status != NO_ERROR) + { + if(status == FWP_E_ALREADY_EXISTS) + { + HlprLogInfo(L"ProviderContext Already Exists"); + + status = NO_ERROR; + } + else + HlprLogError(L"HlprFwpmProviderContextAdd : FwpmProviderContextAdd() [status: %#x]", + status); + } + + return status; +} + +/** + @helper_function="HlprFwpmProviderContextEnum" + + Purpose: Wrapper for the FwpmProviderContextEnum API.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA364186.aspx
+*/ +_At_(*pppEntries, _Pre_ _Null_) +_When_(return != NO_ERROR, _At_(*pppEntries, _Post_ _Null_)) +_Success_(return == NO_ERROR) +UINT32 HlprFwpmProviderContextEnum(_In_ const HANDLE engineHandle, + _In_ const HANDLE enumHandle, + _In_ const UINT32 numEntriesRequested, + _Outptr_result_buffer_maybenull_(*pNumEntriesReturned) FWPM_PROVIDER_CONTEXT*** pppEntries, + _Out_ UINT32* pNumEntriesReturned) +{ + ASSERT(engineHandle); + ASSERT(enumHandle); + ASSERT(numEntriesRequested); + ASSERT(pppEntries); + ASSERT(pNumEntriesReturned); + + UINT32 status = NO_ERROR; + + status = FwpmProviderContextEnum(engineHandle, + enumHandle, + numEntriesRequested, + pppEntries, + pNumEntriesReturned); + if(status != NO_ERROR && + status != FWP_E_PROVIDER_CONTEXT_NOT_FOUND && + status != FWP_E_NOT_FOUND) + { + *pppEntries = 0; + + *pNumEntriesReturned = 0; + + HlprLogError(L"HlprFwpmProviderContextEnum : FwpmProviderContextEnum() [status: %#x]", + status); + } + + return status; +} + +/** + @helper_function="HlprFwpmProviderContextDestroyEnumHandle" + + Purpose: Wrapper for the FwpmProviderContextDestroyEnumHandle API.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/AA364185.aspx
+*/ +_When_(return != NO_ERROR, _At_(*pEnumHandle, _Post_ _Notnull_)) +_When_(return == NO_ERROR, _At_(*pEnumHandle, _Post_ _Null_)) +_Success_(return == NO_ERROR) +UINT32 HlprFwpmProviderContextDestroyEnumHandle(_In_ const HANDLE engineHandle, + _Inout_ HANDLE* pEnumHandle) +{ + UINT32 status = NO_ERROR; + + if(engineHandle && + pEnumHandle) + { + if(*pEnumHandle) + { + status = FwpmProviderContextDestroyEnumHandle(engineHandle, + *pEnumHandle); + if(status != NO_ERROR) + { + HlprLogError(L"HlprFwpmProviderContextDestroyEnumHandle : FwpmProviderContextDestroyEnumHandle() [status: %#x]", + status); + + HLPR_BAIL; + } + + *pEnumHandle = 0; + } + } + else + { + status = ERROR_INVALID_PARAMETER; + + HlprLogError(L"HlprFwpmProviderContextDestroyEnumHandle() [status: %#x][engineHandle: %#p][pEnumHandle: %#p]", + status, + engineHandle, + pEnumHandle); + } + + HLPR_BAIL_LABEL: + + return status; +} + +/** + @helper_function="HlprFwpmProviderContextCreateEnumHandle" + + Purpose: Wrapper for the FwpmProviderContextCreateEnumHandle API.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA364182.aspx
+*/ +_When_(return != NO_ERROR, _At_(*pEnumHandle, _Post_ _Null_)) +_When_(return == NO_ERROR, _At_(*pEnumHandle, _Post_ _Notnull_)) +_Success_(return == NO_ERROR) +UINT32 HlprFwpmProviderContextCreateEnumHandle(_In_ const HANDLE engineHandle, + _In_opt_ const FWPM_PROVIDER_CONTEXT_ENUM_TEMPLATE* pEnumTemplate, + _Out_ HANDLE* pEnumHandle) +{ + ASSERT(engineHandle); + ASSERT(pEnumHandle); + + UINT32 status = NO_ERROR; + + status = FwpmProviderContextCreateEnumHandle(engineHandle, + pEnumTemplate, + pEnumHandle); + if(status != NO_ERROR) + HlprLogError(L"HlprFwpmProviderContextCreateEnumHandle : FwpmProviderContextCreateEnumHandle() [status: %#x]", + status); + + return status; +} + +/** + @helper_function="HlprFwpmProviderContextRemoveAll" + + Purpose: Remove all providerContexts associated with the specified provider.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 HlprFwpmProviderContextRemoveAll(_In_opt_ HANDLE* pEngineHandle, + _In_opt_ const GUID* pProviderContextKey) +{ + UINT32 status = NO_ERROR; + HANDLE engineHandle = 0; + HANDLE enumHandle = 0; + BOOLEAN isLocal = FALSE; + FWPM_PROVIDER_CONTEXT** ppProviderContexts = 0; + UINT32 providerContextCount = 0; + FWPM_PROVIDER_CONTEXT_ENUM_TEMPLATE enumTemplate = {0}; + + if(pEngineHandle && + *pEngineHandle) + engineHandle = *pEngineHandle; + else + { + status = HlprFwpmEngineOpen(&engineHandle); + HLPR_BAIL_ON_FAILURE(status); + + isLocal = TRUE; + } + + enumTemplate.providerKey = (GUID*)pProviderContextKey; + + status = HlprFwpmProviderContextCreateEnumHandle(engineHandle, + &enumTemplate, + &enumHandle); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmProviderContextEnum(engineHandle, + enumHandle, + 0xFFFFFFFF, + &ppProviderContexts, + &providerContextCount); + if(ppProviderContexts && + providerContextCount) + { + for(UINT32 providerContextIndex = 0; + providerContextIndex < providerContextCount; + providerContextIndex++) + { + HlprFwpmProviderContextDeleteByKey(engineHandle, + &(ppProviderContexts[providerContextIndex]->providerContextKey)); + } + + FwpmFreeMemory((VOID**)&ppProviderContexts); + } + + HlprFwpmProviderContextDestroyEnumHandle(engineHandle, + &enumHandle); + + HLPR_BAIL_LABEL: + + if(engineHandle && + isLocal) + HlprFwpmEngineClose(&engineHandle); + + return status; +} diff --git a/network/trans/WFPSampler/lib/HelperFunctions_FwpmProviderContext.h b/network/trans/WFPSampler/lib/HelperFunctions_FwpmProviderContext.h new file mode 100644 index 000000000..ef3bc3165 --- /dev/null +++ b/network/trans/WFPSampler/lib/HelperFunctions_FwpmProviderContext.h @@ -0,0 +1,59 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_FwpmProviderContext.h +// +// Abstract: +// This module contains prototypes for functions which assist in actions pertaining to +// FWPM_PROVIDER_CONTEXT objects. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef HELPERFUNCTIONS_FWPM_PROVIDER_CONTEXT_H +#define HELPERFUNCTIONS_FWPM_PROVIDER_CONTEXT_H + +_Success_(return == NO_ERROR) +UINT32 HlprFwpmProviderContextDeleteByKey(_In_ const HANDLE engineHandle, + _In_ const GUID* pProviderContextKey); + +_Success_(return == NO_ERROR) +UINT32 HlprFwpmProviderContextAdd(_In_ const HANDLE engineHandle, + _Inout_ FWPM_PROVIDER_CONTEXT* pProviderContext); + +_At_(*pppEntries, _Pre_ _Null_) +_When_(return != NO_ERROR, _At_(*pppEntries, _Post_ _Null_)) +_Success_(return == NO_ERROR) +UINT32 HlprFwpmProviderContextEnum(_In_ const HANDLE engineHandle, + _In_ const HANDLE enumHandle, + _In_ const UINT32 numEntriesRequested, + _Outptr_result_buffer_maybenull_(*pNumEntriesReturned) FWPM_PROVIDER_CONTEXT*** pppEntries, + _Out_ UINT32* pNumEntriesReturned); + +_When_(return != NO_ERROR, _At_(*pEnumHandle, _Post_ _Notnull_)) +_When_(return == NO_ERROR, _At_(*pEnumHandle, _Post_ _Null_)) +_Success_(return == NO_ERROR) +UINT32 HlprFwpmProviderContextDestroyEnumHandle(_In_ const HANDLE engineHandle, + _Inout_ HANDLE* pEnumHandle); + +_When_(return != NO_ERROR, _At_(*pEnumHandle, _Post_ _Null_)) +_When_(return == NO_ERROR, _At_(*pEnumHandle, _Post_ _Notnull_)) +_Success_(return == NO_ERROR) +UINT32 HlprFwpmProviderContextCreateEnumHandle(_In_ const HANDLE engineHandle, + _In_opt_ const FWPM_PROVIDER_CONTEXT_ENUM_TEMPLATE* pEnumTemplate, + _Out_ HANDLE* pEnumHandle); + +_Success_(return == NO_ERROR) +UINT32 HlprFwpmProviderContextRemoveAll(_In_opt_ HANDLE* pEngineHandle, + _In_opt_ const GUID* pProviderContextKey); + +#endif /// HELPERFUNCTIONS_FWPM_PROVIDER_CONTEXT_H \ No newline at end of file diff --git a/network/trans/WFPSampler/lib/HelperFunctions_FwpmSubLayer.cpp b/network/trans/WFPSampler/lib/HelperFunctions_FwpmSubLayer.cpp new file mode 100644 index 000000000..bbedd10f5 --- /dev/null +++ b/network/trans/WFPSampler/lib/HelperFunctions_FwpmSubLayer.cpp @@ -0,0 +1,367 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_FwpmSubLayer.cpp +// +// Abstract: +// This module contains functions which assist in actions pertaining to FWPM_SUBLAYER objects. +// +// Naming Convention: +// +// +// +// i.e. +// +// +// { +// - Function is likely visible to other modules. +// } +// +// { +// Hlpr - Function is from HelperFunctions_* Modules. +// } +// +// { +// FwpmSubLayer - Function pertains to FWPM_SUBLAYER objects. +// } +// +// { +// Add - Function adds an object. +// Remove - Function deletes objects. +// } +// +// { +// DefaultGlobal - Function acts on the programs constant global object. +// } +// Private Functions: +// +// Public Functions: +// HlprFwpmSubLayerAddDefaultGlobal(), +// HlprFwpmSubLayerRemoveDefaultGlobal(), +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "HelperFunctions_Include.h" /// . + +/** + @helper_function="HlprFwpmSubLayerEnum" + + Purpose: Wrapper for the FwpmSubLayerEnum API.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/AA364211.aspx
+*/ +_At_(*pppEntries, _Pre_ _Null_) +_When_(return != NO_ERROR, _At_(*pppEntries, _Post_ _Null_)) +_Success_(return == NO_ERROR) +UINT32 HlprFwpmSubLayerEnum(_In_ const HANDLE engineHandle, + _In_ const HANDLE enumHandle, + _In_ const UINT32 numEntriesRequested, + _Outptr_result_buffer_maybenull_(*pNumEntriesReturned) FWPM_SUBLAYER*** pppEntries, + _Out_ UINT32* pNumEntriesReturned) +{ + UINT32 status = NO_ERROR; + + if(engineHandle && + enumHandle && + numEntriesRequested && + pppEntries && + pNumEntriesReturned) + { + status = FwpmSubLayerEnum(engineHandle, + enumHandle, + numEntriesRequested, + pppEntries, + pNumEntriesReturned); + if(status != NO_ERROR && + status != FWP_E_SUBLAYER_NOT_FOUND && + status != FWP_E_NOT_FOUND) + HlprLogError(L"HlprFwpmSubLayerEnum : FwpmSubLayerEnum() [status: %#x]", + status); + } + else + { + if(pppEntries) + *pppEntries = 0; + + if(pNumEntriesReturned) + *pNumEntriesReturned = 0; + + status = ERROR_INVALID_PARAMETER; + + HlprLogError(L"HlprFwpmSubLayerEnum() [status: %#x][engineHandle: %#p][enumHandle: %#p][numEntriesRequested: %d][pppEntries: %#p][pNumEntriesReturned: %#p]", + status, + engineHandle, + enumHandle, + numEntriesRequested, + pppEntries, + pNumEntriesReturned); + } + + return status; +} + +/** + @helper_function="HlprFwpmSubLayerDestroyEnumHandle" + + Purpose: Wrapper for the FwpmSubLayerDestroyEnumHandle API.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/AA364210.aspx
+*/ +_When_(return != NO_ERROR, _At_(*pEnumHandle, _Post_ _Notnull_)) +_When_(return == NO_ERROR, _At_(*pEnumHandle, _Post_ _Null_)) +_Success_(return == NO_ERROR) +UINT32 HlprFwpmSubLayerDestroyEnumHandle(_In_ const HANDLE engineHandle, + _Inout_ HANDLE* pEnumHandle) +{ + UINT32 status = NO_ERROR; + + if(engineHandle && + pEnumHandle) + { + if(*pEnumHandle) + { + status = FwpmSubLayerDestroyEnumHandle(engineHandle, + *pEnumHandle); + if(status != NO_ERROR) + { + HlprLogError(L"HlprFwpmSubLayerDestroyEnumHandle : FwpmSubLayerDestroyEnumHandle() [status: %#x]", + status); + + HLPR_BAIL; + } + + *pEnumHandle = 0; + } + } + else + { + status = ERROR_INVALID_PARAMETER; + + HlprLogError(L"HlprFwpmSubLayerDestroyEnumHandle() [status: %#x][engineHandle: %#p][pEnumHandle: %#p]", + status, + engineHandle, + pEnumHandle); + } + + HLPR_BAIL_LABEL: + + return status; +} + +/** + @helper_function="HlprFwpmSubLayerCreateEnumHandle" + + Purpose: Wrapper for the FwpmSubLayerCreateEnumHandle API.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/AA364208.aspx
+*/ +_When_(return != NO_ERROR, _At_(*pEnumHandle, _Post_ _Null_)) +_When_(return == NO_ERROR, _At_(*pEnumHandle, _Post_ _Notnull_)) +_Success_(return == NO_ERROR) +UINT32 HlprFwpmSubLayerCreateEnumHandle(_In_ const HANDLE engineHandle, + _In_opt_ const FWPM_SUBLAYER_ENUM_TEMPLATE* pEnumTemplate, + _Out_ HANDLE* pEnumHandle) +{ + UINT32 status = NO_ERROR; + + if(engineHandle && + pEnumHandle) + { + status = FwpmSubLayerCreateEnumHandle(engineHandle, + pEnumTemplate, + pEnumHandle); + if(status != NO_ERROR) + HlprLogError(L"HlprFwpmSubLayerCreateEnumHandle : FwpmSubLayerCreateEnumHandle() [status: %#x]", + status); + } + else + { + status = ERROR_INVALID_PARAMETER; + + HlprLogError(L"HlprFwpmSubLayerCreateEnumHandle() [status: %#x][engineHandle: %#p][pEnumHandle: %#p]", + status, + engineHandle, + pEnumHandle); + } + + return status; +} + +/** + @helper_function="HlprFwpmSubLayerDeleteByKey" + + Purpose: Wrapper for the FwpmSubLayerDeleteByKey API.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/AA364209.aspx
+*/ +_Success_(return == NO_ERROR) +UINT32 HlprFwpmSubLayerDeleteByKey(_In_ const HANDLE engineHandle, + _In_ const GUID* pSubLayerKey) +{ + UINT32 status = NO_ERROR; + + if(engineHandle) + { + if(pSubLayerKey) + { + status = FwpmSubLayerDeleteByKey(engineHandle, + pSubLayerKey); + if(status != NO_ERROR) + { + if(status != FWP_E_IN_USE && + status != FWP_E_BUILTIN_OBJECT && + status != FWP_E_SUBLAYER_NOT_FOUND) + HlprLogError(L"HlprFwpmSubLayerDeleteByKey : FwpmSubLayerDeleteByKey() [status: %#x]", + status); + else + { + HlprLogInfo(L"HlprFwpmSubLayerDeleteByKey : FwpmSubLayerDeleteByKey() [status: %#x]", + status); + + status = NO_ERROR; + } + } + } + } + else + { + status = ERROR_INVALID_PARAMETER; + + HlprLogError(L"HlprFwpmSubLayerDeleteByKey() [status: %#x][engineHandle: %#p]", + status, + engineHandle); + } + + return status; +} + +/** + @helper_function="HlprFwpmSubLayerAdd" + + Purpose: Add a sublayer to associate with all of this program's filters.
+
+ Notes: By default sublayer is weighted just below the weight of FWPM_SUBLAYER_UNIVERSAL + so IPsec decryption will occur prior to invocation of any of WFPSampler's callout + routines.
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/AA364207.aspx
+*/ +_Success_(return == NO_ERROR) +UINT32 HlprFwpmSubLayerAdd(_In_ HANDLE engineHandle, + _In_ const GUID* pSubLayerKey, + _In_ PCWSTR pSubLayerName, + _In_ const GUID* pProviderKey, + _In_ UINT16 weight, /* 0x7FFF */ + _In_ UINT32 flags) /* FWPM_SUBLAYER_FLAG_PERSISTENT */ +{ + UINT32 status = NO_ERROR; + + if(engineHandle) + { + FWPM_SUBLAYER subLayer = {0}; + + subLayer.subLayerKey = *pSubLayerKey; + subLayer.displayData.name = (PWSTR)pSubLayerName; + subLayer.flags = flags; + subLayer.providerKey = (GUID*)pProviderKey; + subLayer.weight = weight; + + status = FwpmSubLayerAdd(engineHandle, + &subLayer, + 0); + if(status != NO_ERROR) + { + if(status == FWP_E_ALREADY_EXISTS) + { + HlprLogInfo(L"SubLayer Already Exists"); + + status = NO_ERROR; + } + else + HlprLogError(L"HlprFwpmSubLayerAdd : FwpmSubLayerAdd() [status: %#x]", + status); + } + } + else + { + status = ERROR_INVALID_PARAMETER; + + HlprLogError(L"HlprFwpmSubLayerAdd() [status: %#x]", + status); + } + + return status; +} + +/** + @helper_function="HlprFwpmSubLayerDelete" + + Purpose: Remove the sublayer that was associated with all of WFPSampler's filters.
+
+ Notes: Function will fail if any of WFPSampler's filters are still present and associated + with this sublayer.
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/AA364209.aspx
+*/ +_Success_(return == NO_ERROR) +UINT32 HlprFwpmSubLayerDelete(_In_opt_ HANDLE* pEngineHandle, + _In_ const GUID* pSubLayerKey) +{ + UINT32 status = NO_ERROR; + HANDLE engineHandle = 0; + BOOLEAN isLocal = FALSE; + + if(pEngineHandle && + *pEngineHandle) + engineHandle = *pEngineHandle; + else + { + status = HlprFwpmEngineOpen(&engineHandle); + HLPR_BAIL_ON_FAILURE(status); + + isLocal = TRUE; + } + + status = FwpmSubLayerDeleteByKey(engineHandle, + pSubLayerKey); + if(status != NO_ERROR) + { + if(status != FWP_E_SUBLAYER_NOT_FOUND && + status != FWP_E_IN_USE) + HlprLogError(L"HlprFwpmSubLayerDelete : FwpmSubLayerDeleteByKey() [status: %#x]", + status); + else + { + HlprLogInfo(L"HlprFwpmSubLayerDelete : FwpmSubLayerDeleteByKey() [status: %#x]", + status); + + status = NO_ERROR; + } + } + + HLPR_BAIL_LABEL: + + if(engineHandle && + isLocal) + HlprFwpmEngineClose(&engineHandle); + + return status; +} diff --git a/network/trans/WFPSampler/lib/HelperFunctions_FwpmSubLayer.h b/network/trans/WFPSampler/lib/HelperFunctions_FwpmSubLayer.h new file mode 100644 index 000000000..bba25d683 --- /dev/null +++ b/network/trans/WFPSampler/lib/HelperFunctions_FwpmSubLayer.h @@ -0,0 +1,63 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_FwpmSubLayer.h +// +// Abstract: +// This module contains prototypes for functions which assist in actions pertaining to +// FWPM_SUBLAYER objects. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef HELPERFUNCTIONS_FWPM_SUBLAYER_H +#define HELPERFUNCTIONS_FWPM_SUBLAYER_H + +_At_(*pppEntries, _Pre_ _Null_) +_When_(return != NO_ERROR, _At_(*pppEntries, _Post_ _Null_)) +_Success_(return == NO_ERROR) +UINT32 HlprFwpmSubLayerEnum(_In_ const HANDLE engineHandle, + _In_ const HANDLE enumHandle, + _In_ const UINT32 numEntriesRequested, + _Outptr_result_buffer_maybenull_(*pNumEntriesReturned) FWPM_SUBLAYER*** pppEntries, + _Out_ UINT32* pNumEntriesReturned); + +_When_(return != NO_ERROR, _At_(*pEnumHandle, _Post_ _Notnull_)) +_When_(return == NO_ERROR, _At_(*pEnumHandle, _Post_ _Null_)) +_Success_(return == NO_ERROR) +UINT32 HlprFwpmSubLayerDestroyEnumHandle(_In_ const HANDLE engineHandle, + _Inout_ HANDLE* pEnumHandle); + +_When_(return != NO_ERROR, _At_(*pEnumHandle, _Post_ _Null_)) +_When_(return == NO_ERROR, _At_(*pEnumHandle, _Post_ _Notnull_)) +_Success_(return == NO_ERROR) +UINT32 HlprFwpmSubLayerCreateEnumHandle(_In_ const HANDLE engineHandle, + _In_opt_ const FWPM_SUBLAYER_ENUM_TEMPLATE* pEnumTemplate, + _Out_ HANDLE* pEnumHandle); + +_Success_(return == NO_ERROR) +UINT32 HlprFwpmSubLayerDeleteByKey(_In_ const HANDLE engineHandle, + _In_ const GUID* pSubLayerKey); + +_Success_(return == NO_ERROR) +UINT32 HlprFwpmSubLayerAdd(_In_ HANDLE engineHandle, + _In_ const GUID* pSubLayerKey, + _In_ PCWSTR pSubLayerName, + _In_ const GUID* pProviderKey, + _In_ UINT16 weight = 0x7FFF, + _In_ UINT32 flags = FWPM_SUBLAYER_FLAG_PERSISTENT); + +_Success_(return == NO_ERROR) +UINT32 HlprFwpmSubLayerDelete(_In_opt_ HANDLE* pEngineHandle, + _In_ const GUID* pSubLayerKey); + +#endif /// HELPERFUNCTIONS_FWPM_SUBLAYER_H \ No newline at end of file diff --git a/network/trans/WFPSampler/lib/HelperFunctions_FwpmTransaction.cpp b/network/trans/WFPSampler/lib/HelperFunctions_FwpmTransaction.cpp new file mode 100644 index 000000000..5225e9fcb --- /dev/null +++ b/network/trans/WFPSampler/lib/HelperFunctions_FwpmTransaction.cpp @@ -0,0 +1,162 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_FwpmTransaction.cpp +// +// Abstract: +// This module contains functions which assist in actions pertaining to BFE transactions. +// +// Naming Convention: +// +// +// +// i.e. +// +// +// { +// - Function is likely visible to other modules. +// } +// +// { +// Hlpr - Function is from HelperFunctions_* Modules. +// } +// +// { +// FwpmTransaction - Function pertains to BFE engine objects. +// } +// +// { +// Abort - Function ends a transaction without making changes. +// Begin - Function starts a transaction. +// Commit - Function ends a transaction successfully. +// } +// +// { +// } +// +// Private Functions: +// +// Public Functions: +// HlprFwpmTransactionAbort(), +// HlprFwpmTransactionBegin(), +// HlprFwpmTransactionCommit(), +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "HelperFunctions_Include.h" /// . + + +/** + @helper_function="HlprFwpmTransactionAbort" + + Purpose: Wrapper for the FwpmTransactionAbort API.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/AA364242.aspx
+*/ +_Success_(return == NO_ERROR) +UINT32 HlprFwpmTransactionAbort(_In_ HANDLE engineHandle) +{ + UINT32 status = NO_ERROR; + + if(engineHandle) + { + status = FwpmTransactionAbort(engineHandle); + if(status != NO_ERROR) + HlprLogError(L"HlprFwpmTransactionAbort() [status: %#x]", + status); + } + else + { + status = ERROR_INVALID_PARAMETER; + + HlprLogError(L"HlprFwpmTransactionAbort() [status: %#x][engineHandle: %#p]", + status, + engineHandle); + } + + return status; +} + +/** + @helper_function="HlprFwpmTransactionBegin" + + Purpose: Wrapper for the FwpmTransactionBegin API.
+
+ Notes: Only need to use if making multiple BFE calls.
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/AA364243.aspx
+*/ +_Success_(return == NO_ERROR) +UINT32 HlprFwpmTransactionBegin(_In_ HANDLE engineHandle, + _In_ UINT32 flags) /* 0 */ +{ + UINT32 status = NO_ERROR; + + if(engineHandle) + { + status = FwpmTransactionBegin(engineHandle, + flags); + if(status != NO_ERROR) + HlprLogError(L"HlprFwpmTransactionBegin() [status: %#x]", + status); + } + else + { + status = ERROR_INVALID_PARAMETER; + + HlprLogError(L"HlprFwpmTransactionBegin() [status: %#x][engineHandle: %#p]", + status, + engineHandle); + } + + return status; +} + +/** + @helper_function="HlprFwpmTransactionCommit" + + Purpose: Wrapper for the FwpmTransactionCommit API.
+
+ Notes: On failure, transaction gets aborted.
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/AA364245.aspx
+*/ +_Success_(return == NO_ERROR) +UINT32 HlprFwpmTransactionCommit(_In_ HANDLE engineHandle) +{ + UINT32 status = NO_ERROR; + + if(engineHandle) + { + status = FwpmTransactionCommit(engineHandle); + if(status != NO_ERROR) + { + HlprLogError(L"HlprFwpmTransactionCommit() [status: %#x]", + status); + + HlprFwpmTransactionAbort(engineHandle); + } + } + else + { + status = ERROR_INVALID_PARAMETER; + + HlprLogError(L"HlprFwpmTransactionCommit() [status: %#x][engineHandle: %#p]", + status, + engineHandle); + } + + return status; +} diff --git a/network/trans/WFPSampler/lib/HelperFunctions_FwpmTransaction.h b/network/trans/WFPSampler/lib/HelperFunctions_FwpmTransaction.h new file mode 100644 index 000000000..3789cd383 --- /dev/null +++ b/network/trans/WFPSampler/lib/HelperFunctions_FwpmTransaction.h @@ -0,0 +1,35 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_FwpmTransaction.h +// +// Abstract: +// This module contains prototypes for functions which assist in actions pertaining to BFE +// transactions. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef HELPERFUNCTIONS_FWPM_TRANSACTION_H +#define HELPERFUNCTIONS_FWPM_TRANSACTION_H + +_Success_(return == NO_ERROR) +UINT32 HlprFwpmTransactionAbort(_In_ HANDLE engineHandle); + +_Success_(return == NO_ERROR) +UINT32 HlprFwpmTransactionBegin(_In_ HANDLE engineHandle, + _In_ UINT32 flags = 0); + +_Success_(return == NO_ERROR) +UINT32 HlprFwpmTransactionCommit(_In_ HANDLE engineHandle); + +#endif /// HELPERFUNCTIONS_FWPM_TRANSACTION_H \ No newline at end of file diff --git a/network/trans/WFPSampler/lib/HelperFunctions_GUID.cpp b/network/trans/WFPSampler/lib/HelperFunctions_GUID.cpp new file mode 100644 index 000000000..eda8106ea --- /dev/null +++ b/network/trans/WFPSampler/lib/HelperFunctions_GUID.cpp @@ -0,0 +1,318 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_GUID.cpp +// +// Abstract: +// This module contains functions which assist in actions pertaining to GUIDs. +// +// Naming Convention: +// +// +// +// i.e. +// +// +// { +// - Function is likely visible to other modules +// } +// +// { +// Hlpr - Function is from HelperFunctions_* Modules. +// } +// +// { +// GUID - Function pertains to GUID objects. +// } +// +// { +// Are - Function compares values. +// Create - Function allocates and fills memory. +// Destroy - Function cleans up and frees memory. +// Is - Function compares values. +// Populate - Function fills memory with values. +// Purge - Function cleans up values. +// } +// +// { +// Equal - Function determines equality between values. +// Null - Function determines if value is Null or empty. +// String - Function acts on a null terminated wide character string. +// } +// +// Private Functions: +// +// Public Functions: +// HlprGUIDsAreEqual(), +// HlprGUIDCreate(), +// HlprGUIDCreateString(), +// HlprGUIDDestroy(), +// HlprGUIDDestroyString(), +// HlprGUIDIsNull() +// HlprGUIDPopulate(), +// HlprGUIDPurge(), +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "HelperFunctions_Include.h" /// . + +static const GUID NULL_GUID = {0}; + +/** + @helper_function="HlprGUIDPurge" + + Purpose: Cleanup a GUID.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +VOID HlprGUIDPurge(_Inout_ GUID* pGUID) +{ + if(pGUID) + ZeroMemory(pGUID, + sizeof(GUID)); + + return; +} + +/** + @helper_function="HlprGUIDDestroy" + + Purpose: Cleanup and free a GUID.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_At_(*ppGUID, _Post_ _Null_) +VOID HlprGUIDDestroy(_Inout_ GUID** ppGUID) +{ + if(ppGUID) + { + if(*ppGUID) + HlprGUIDPurge(*ppGUID); + + HLPR_DELETE(*ppGUID); + } + + return; +} + +/** + @helper_function="HlprGUIDPopulate" + + Purpose: Populate a GUID with a random value.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/AA379205.aspx
+*/ +_Success_(return == NO_ERROR) +UINT32 HlprGUIDPopulate(_Inout_ GUID* pGUID) +{ + UINT32 status = NO_ERROR; + + if(pGUID) + { + status = UuidCreate(pGUID); + + if(status != RPC_S_OK && // 0 + status != RPC_S_UUID_LOCAL_ONLY) // 1824 + { + // RPC_S_UUID_NO_ADDRESS // 1739 + HlprLogError(L"HlprGUIDPopulate : UuidCreate() [status: %#x]", + status); + } + else + status = NO_ERROR; + } + else + { + status = ERROR_INVALID_PARAMETER; + + HlprLogError(L"HlprGUIDPopulate() [status: %#x][pGUID: %#p]", + status, + pGUID); + } + + return status; +} + +/** + @helper_function="HlprGUIDCreate" + + Purpose: Allocate and populate a GUID with a random value.
+
+ Notes: The caller is responsible for freeing the allocated memory using + HlprGUIDDestroy().
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/AA379205.aspx
+*/ +_At_(*ppGUID, _Pre_ _Null_) +_When_(return != NO_ERROR, _At_(*ppGUID, _Post_ _Null_)) +_When_(return == NO_ERROR, _At_(*ppGUID, _Post_ _Notnull_)) +_Success_(return == NO_ERROR) +UINT32 HlprGUIDCreate(_Outptr_ GUID** ppGUID) +{ + UINT32 status = NO_ERROR; + + if(ppGUID) + { + HLPR_NEW(*ppGUID, + GUID); + HLPR_BAIL_ON_ALLOC_FAILURE(*ppGUID, + status); + + status = HlprGUIDPopulate(*ppGUID); + + HLPR_BAIL_LABEL: + + if(status != NO_ERROR) + { + HLPR_DELETE(*ppGUID); + } + } + else + { + status = ERROR_INVALID_PARAMETER; + + HlprLogError(L"HlprGUIDCreate() [status: %#x][ppGUID: %#p]", + status, + ppGUID); + } + + return status; +} + +/** + @helper_function="HlprGUIDDestroyString" + + Purpose: Cleanup and free a string representing a GUID.
+
+ Notes: Use if string was allocated by HlprGUIDCreateString().
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/AA378483.aspx
+*/ +_When_(return != NO_ERROR, _At_(*ppGUIDString, _Post_ _Notnull_)) +_When_(return == NO_ERROR, _At_(*ppGUIDString, _Post_ _Null_)) +_Success_(return == NO_ERROR) +UINT32 HlprGUIDDestroyString(_Inout_ PWSTR* ppGUIDString) +{ + UINT32 status = NO_ERROR; + + if(ppGUIDString && + *ppGUIDString) + { + status = RpcStringFree(ppGUIDString); + if(status != RPC_S_OK) + HlprLogError(L"HlprGUIDDestroyString : RpcStringFree() [status: %#x]", + status); + else + *ppGUIDString = 0; + } + + return status; +} + +/** + @helper_function="HlprGUIDCreateString" + + Purpose: Allocate and populate a string representing the provided GUID.
+
+ Notes: The caller is responsible for freeing the allocated memory using + HlprGUIDDestroyString().
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/AA379352.aspx
+*/ +_Success_(return != 0) +PWSTR HlprGUIDCreateString(_In_ const GUID* pGUID) +{ + PWSTR pGUIDString = 0; + + if(pGUID) + { + UINT32 status = NO_ERROR; + + status = UuidToString(pGUID, + &pGUIDString); + if(status != NO_ERROR) + { + HlprLogError(L"HlprGUIDCreateString : UuidToString() [status: %#x]", + status); + } + } + + return pGUIDString; +} + +/** + @helper_function="HlprGUIDsAreEqual" + + Purpose: Determine if two GUIDs are identical.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/AA379329.aspx
+*/ +BOOLEAN HlprGUIDsAreEqual(_In_ const GUID* pGUIDAlpha, + _In_ const GUID* pGUIDOmega) +{ + RPC_STATUS status = RPC_S_OK; + UINT32 areEqual = FALSE; + + if(pGUIDAlpha == 0 || + pGUIDOmega == 0) + { + if((pGUIDAlpha == 0 && + pGUIDOmega) || + (pGUIDAlpha && + pGUIDOmega == 0)) + + HLPR_BAIL; + } + + if(pGUIDAlpha == 0 && + pGUIDOmega == 0) + { + areEqual = TRUE; + + HLPR_BAIL; + } + + areEqual = UuidEqual((UUID*)pGUIDAlpha, + (UUID*)pGUIDOmega, + &status); + if(status != RPC_S_OK) + HlprLogError(L"HlprGUIDsAreEqual : UuidEqual() [status %#x]", + status); + + HLPR_BAIL_LABEL: + + return (BOOLEAN)areEqual; +} + +/** + @helper_function="HlprGUIDIsNull" + + Purpose: Determine if a GUID is a NULL GUID.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +BOOLEAN HlprGUIDIsNull(_In_ const GUID* pGUID) +{ + return HlprGUIDsAreEqual(pGUID, + (GUID*)&NULL_GUID);; +} diff --git a/network/trans/WFPSampler/lib/HelperFunctions_GUID.h b/network/trans/WFPSampler/lib/HelperFunctions_GUID.h new file mode 100644 index 000000000..bda5e257b --- /dev/null +++ b/network/trans/WFPSampler/lib/HelperFunctions_GUID.h @@ -0,0 +1,51 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_GUID.h +// +// Abstract: +// This module contains prototypes for functions which assist in actions pertaining to GUIDs. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef HELPERFUNCTIONS_GUID_H +#define HELPERFUNCTIONS_GUID_H + +VOID HlprGUIDPurge(_Inout_ GUID* pGUID); + +_At_(*ppGUID, _Post_ _Null_) +VOID HlprGUIDDestroy(_Inout_ GUID** ppGUID); + +_Success_(return == NO_ERROR) +UINT32 HlprGUIDPopulate(_Inout_ GUID* pGUID); + +_At_(*ppGUID, _Pre_ _Null_) +_When_(return != NO_ERROR, _At_(*ppGUID, _Post_ _Null_)) +_When_(return == NO_ERROR, _At_(*ppGUID, _Post_ _Notnull_)) +_Success_(return == NO_ERROR) +UINT32 HlprGUIDCreate(_Outptr_ GUID** ppGUID); + +_When_(return != NO_ERROR, _At_(*ppGUIDString, _Post_ _Notnull_)) +_When_(return == NO_ERROR, _At_(*ppGUIDString, _Post_ _Null_)) +_Success_(return == NO_ERROR) +UINT32 HlprGUIDDestroyString(_Inout_ PWSTR* ppGUIDString); + +_Success_(return != 0) +PWSTR HlprGUIDCreateString(_In_ const GUID* pGUID); + +BOOLEAN HlprGUIDsAreEqual(_In_ const GUID* pGUIDAlpha, + _In_ const GUID* pGUIDOmega); + +BOOLEAN HlprGUIDIsNull(_In_ const GUID* pGUID); + +#endif /// HELPERFUNCTIONS_GUID_H \ No newline at end of file diff --git a/network/trans/WFPSampler/lib/HelperFunctions_IPAddress.cpp b/network/trans/WFPSampler/lib/HelperFunctions_IPAddress.cpp new file mode 100644 index 000000000..456b244f3 --- /dev/null +++ b/network/trans/WFPSampler/lib/HelperFunctions_IPAddress.cpp @@ -0,0 +1,286 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_IPAddress.cpp +// +// Abstract: +// This module contains functions which assist in actions pertaining to IP Addresses. +// +// Naming Convention: +// +// +// +// i.e. +// +// +// { +// - Function is likely visible to other modules +// } +// +// { +// Hlpr - Function is from HelperFunctions_* Modules. +// } +// +// { +// IPAddressV4String - Function pertains to null terminated wide character string +// representaions of IPv4 Addresses +// IPAddressV6String - Function pertains to null terminated wide character string +// representaions of IPv6 Addresses +// } +// +// { +// To - Function converts from one type to another +// Is - Function compares values. +// } +// +// { +// ValidFormat - Function validates condition. +// Value - Function acts on a value. +// } +// +// Private Functions: +// +// Public Functions: +// HlprIPAddressV4StringIsValidFormat(), +// HlprIPAddressV4StringToValue(), +// HlprIPAddressV6StringIsValidFormat(), +// HlprIPAddressV6StringToValue(), +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "HelperFunctions_Include.h" /// . + +/** + @helper_function="HlprIPAddressV4StringIsValidFormat" + + Purpose: Determine if a string may be an IPv4 address by verifying the string:
+ is at least 7 characters
+ has at least 3 decimals('.')
+ is not more than 16 characters
+
+ Notes:
+
+ MSDN_Ref:
+*/ +BOOLEAN HlprIPAddressV4StringIsValidFormat(_In_ PCWSTR pIPAddress) +{ + ASSERT(pIPAddress); + + UINT32 status = NO_ERROR; + BOOLEAN isIPv4Address = FALSE; + size_t addressSize = 0; + + status = StringCchLength(pIPAddress, + STRSAFE_MAX_CCH, + &addressSize); + if(FAILED(status)) + { + HlprLogError(L"HlprIPAddressV4StringIsValidFormat : StringCchLength() [status: %#x]", + status); + + HLPR_BAIL; + } + + if(addressSize > 6 && /// minimum address size: 0.0.0.0 + addressSize < 16) /// maximum address size: 255.255.255.255 + { + UINT32 numPeriods = 0; + + for(UINT32 index = 0; + index < addressSize; + index++) + { + if(pIPAddress[index] == '.') + numPeriods++; + } + + if(numPeriods == 3) + isIPv4Address = TRUE; + } + + HLPR_BAIL_LABEL: + + return isIPv4Address; +} + +/** + @helper_function="HlprIPAddressV4StringToValue" + + Purpose: Convert a string representing an IPv4 Address to it's 4 BYTE value.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA814459.aspx
+*/ +_Success_(return == NO_ERROR) +UINT32 HlprIPAddressV4StringToValue(_In_ PCWSTR pIPv4AddressString, + _Inout_ UINT32* pIPv4Address) +{ + ASSERT(pIPv4AddressString); + ASSERT(pIPv4Address); + + UINT32 status = NO_ERROR; + +#pragma warning(push) +#pragma warning(disable: 24002) /// function is IPv4 specific, however there is a v6 counter-part +#pragma warning(disable: 24007) /// function is IPv4 specific, however there is a v6 counter-part + + if(HlprIPAddressV4StringIsValidFormat(pIPv4AddressString)) + { + UINT16 port = 0; + IN_ADDR v4Addr = {0}; + + status = RtlIpv4StringToAddressEx(pIPv4AddressString, + FALSE, + &v4Addr, + &port); + if(status != NO_ERROR) + { + HlprLogError(L"HlprIPAddressV4StringToValue : RtlIpv4StringToAddressEx() [status: %#x][pIPv4AddressString: %s]", + status, + pIPv4AddressString); + + HLPR_BAIL; + } + + CopyMemory(pIPv4Address, + &v4Addr, + IPV4_ADDRESS_LENGTH); + + *pIPv4Address = ntohl(*pIPv4Address); + } + else + { + status = ERROR_INVALID_PARAMETER; + + HlprLogError(L"HlprIPAddressV4StringToValue() [status: %#x][pIPv4AddressString: %#s]", + status, + pIPv4AddressString); + } + +#pragma warning(pop) + + HLPR_BAIL_LABEL: + + return status; +} + +/** + @helper_function="HlprIPAddressV6StringIsValidFormat" + + Purpose: Determine if a string may be an IPv6 address by verifying the string:
+ is at least 3 characters
+ has at least 2 colons (':')
+ is not more than 40 characters
+
+ Notes:
+
+ MSDN_Ref:
+*/ +BOOLEAN HlprIPAddressV6StringIsValidFormat(_In_ PCWSTR pIPAddress) +{ + ASSERT(pIPAddress); + + UINT32 status = NO_ERROR; + BOOLEAN isIPv6Address = FALSE; + size_t addressSize = 0; + + status = StringCchLength(pIPAddress, + STRSAFE_MAX_CCH, + &addressSize); + if(FAILED(status)) + { + HlprLogError(L"HlprIPAddressV6StringIsValidFormat : StringCchLength() [status: %#x]", + status); + + HLPR_BAIL; + } + + if(addressSize > 2 && /// minimum address size: ::1 + addressSize < 40) /// maximum address size: FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF + { + UINT32 numColons = 0; + + for(UINT32 index = 0; + index < addressSize; + index++) + { + if(pIPAddress[index] == ':') + numColons++; + } + + if(numColons > 1 && + numColons < 8) + isIPv6Address = TRUE; + } + + HLPR_BAIL_LABEL: + + return isIPv6Address; +} + +/** + @helper_function="HlprIPAddressV4StringToValue" + + Purpose: Convert a string representing an IPv6 Address to it's 16 BYTE value.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA814463.aspx
+*/ +_Success_(return == NO_ERROR) +UINT32 HlprIPAddressV6StringToValue(_In_ PCWSTR pIPv6AddressString, + _Inout_updates_all_(IPV6_ADDRESS_LENGTH) BYTE* pIPv6Address) +{ + ASSERT(pIPv6AddressString); + ASSERT(pIPv6Address); + + UINT32 status = NO_ERROR; + + if(HlprIPAddressV6StringIsValidFormat(pIPv6AddressString)) + { + UINT32 scopeId = 0; + UINT16 port = 0; + IN6_ADDR v6Addr = {0}; + + status = RtlIpv6StringToAddressEx(pIPv6AddressString, + &v6Addr, + (PULONG)&scopeId, + &port); + if(status != NO_ERROR) + { + HlprLogError(L"HlprIPAddressV6StringToValue : RtlIpv6StringToAddressEx() [status: %#x][pIPv6AddressString: %s]", + status, + pIPv6AddressString); + + HLPR_BAIL; + } + + CopyMemory(pIPv6Address, + &v6Addr, + IPV6_ADDRESS_LENGTH); + } + else + { + status = ERROR_INVALID_PARAMETER; + + HlprLogError(L"HlprIPAddressV6StringToValue() [status: %#x][pIPv6AddressString: %#p][pIPv6Address: %#p]", + status, + pIPv6AddressString, + pIPv6Address); + } + + HLPR_BAIL_LABEL: + + return status; +} diff --git a/network/trans/WFPSampler/lib/HelperFunctions_IPAddress.h b/network/trans/WFPSampler/lib/HelperFunctions_IPAddress.h new file mode 100644 index 000000000..9a20636f4 --- /dev/null +++ b/network/trans/WFPSampler/lib/HelperFunctions_IPAddress.h @@ -0,0 +1,43 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_IPAddress.h +// +// Abstract: +// This module contains prototypes for functions which assist in actions pertaining to IP +// Addresses. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef HELPERFUNCTIONS_IP_ADDRESS_H +#define HELPERFUNCTIONS_IP_ADDRESS_H + +#define IPV4 4 +#define IPV6 6 + +#define IPV4_ADDRESS_LENGTH 4 +#define IPV6_ADDRESS_LENGTH 16 + +BOOLEAN HlprIPAddressV4StringIsValidFormat(_In_ PCWSTR pIPAddress); + +_Success_(return == NO_ERROR) +UINT32 HlprIPAddressV4StringToValue(_In_ PCWSTR pIPv4AddressString, + _Inout_ UINT32* pIPv4Address); + +BOOLEAN HlprIPAddressV6StringIsValidFormat(_In_ PCWSTR pIPAddress); + +_Success_(return == NO_ERROR) +UINT32 HlprIPAddressV6StringToValue(_In_ PCWSTR pIPv6AddressString, + _Inout_updates_all_(IPV6_ADDRESS_LENGTH) BYTE* pIPv6Address); + +#endif /// HELPERFUNCTIONS_IP_ADDRESS_H \ No newline at end of file diff --git a/network/trans/WFPSampler/lib/HelperFunctions_Include.h b/network/trans/WFPSampler/lib/HelperFunctions_Include.h new file mode 100644 index 000000000..293d249a8 --- /dev/null +++ b/network/trans/WFPSampler/lib/HelperFunctions_Include.h @@ -0,0 +1,46 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_Include.h +// +// Abstract: +// This module contains include headers for central exportaion of the HelperFunction_* modules. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef HELPERFUNCTIONS_INCLUDE_H +#define HELPERFUNCTIONS_INCLUDE_H + +#include "HelperFunctions_Macros.h" /// . +#include "HelperFunctions_Log.h" /// . +#include "HelperFunctions_Strings.h" /// . +#include "HelperFunctions_GUID.h" /// . +#include "HelperFunctions_SID.h" /// . +#include "HelperFunctions_Service.h" /// . +#include "HelperFunctions_Process.h" /// . +#include "HelperFunctions_Registry.h" /// . +#include "HelperFunctions_ThreadPools.h" /// . +#include "HelperFunctions_ThreadsAndEvents.h" /// . +#include "HelperFunctions_MACAddress.h" /// . +#include "HelperFunctions_IPAddress.h" /// . +#include "HelperFunctions_WinSock.h" /// . +#include "HelperFunctions_FwpmEngine.h" /// . +#include "HelperFunctions_FwpmTransaction.h" /// . +#include "HelperFunctions_FwpmLayer.h" /// . +#include "HelperFunctions_FwpmProvider.h" /// . +#include "HelperFunctions_FwpmProviderContext.h" /// . +#include "HelperFunctions_FwpmSubLayer.h" /// . +#include "HelperFunctions_FwpmCallout.h" /// . +#include "HelperFunctions_FwpmFilter.h" /// . + +#endif /// HELPERFUNCTIONS_INCLUDE_H \ No newline at end of file diff --git a/network/trans/WFPSampler/lib/HelperFunctions_Log.cpp b/network/trans/WFPSampler/lib/HelperFunctions_Log.cpp new file mode 100644 index 000000000..79a183c11 --- /dev/null +++ b/network/trans/WFPSampler/lib/HelperFunctions_Log.cpp @@ -0,0 +1,213 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_Log.cpp +// +// Abstract: +// This module contains functions which assist in logging. +// +// Naming Convention: +// +// +// +// i.e. +// +// +// { +// - Function is likely visible to other modules +// } +// +// { +// Hlpr - Function is from HelperFunctions_* Modules. +// } +// +// { +// Log - Function writes data to the console +// } +// +// { +// Error - Function pertains to error occurrences +// Info - Function pertains to informational mesages +// } +// +// Private Functions: +// +// Public Functions: +// HlprLogError(), +// HlprLogInfo(), +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "HelperFunctions_Include.h" /// . + +#define MAX_CHARS 1024 + +/** + @helper_function="HlprLogError" + + Purpose: Log an error message to the console.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +VOID HlprLogError(_In_ PCWSTR pMessage, + ...) +{ + ASSERT(pMessage); + + UINT32 status = NO_ERROR; + PCWSTR pFormatMessage = L"ERROR: %s\n"; + PWSTR pActualMessage = 0; + size_t size = 0; + size_t actualSize = 11; /// Size of format Message and room for NULL Terminator '\0' + WCHAR pLogMessage[MAX_CHARS] = {0}; + + va_list argumentList; + + va_start(argumentList, + pMessage); + + status = StringCchVPrintf(pLogMessage, + MAX_CHARS, + pMessage, + argumentList); + if(status != NO_ERROR) + { + wprintf(L"HlprLogError : StringCchVPrintf() [status: %#x]", + status); + + HLPR_BAIL; + } + + status = StringCchLength(pLogMessage, + STRSAFE_MAX_CCH, + &size); + if(status != NO_ERROR) + { + wprintf(L"HlprLogError : StringCchLength() [status: %#x]", + status); + + HLPR_BAIL; + } + + actualSize += size; + + HLPR_NEW_ARRAY(pActualMessage, + WCHAR, + actualSize); + HLPR_BAIL_ON_ALLOC_FAILURE(pActualMessage, + status); + + status = StringCchPrintf(pActualMessage, + actualSize, + pFormatMessage, + pLogMessage); + if(status != NO_ERROR) + { + wprintf(L"HlprLogError : StringCchPrintf() [status: %#x]", + status); + + HLPR_BAIL; + } + + wprintf(pActualMessage); + + HLPR_BAIL_LABEL: + + va_end(argumentList); + + HLPR_DELETE_ARRAY(pActualMessage); + + return; +} + +/** + @helper_function="HlprLogInfo" + + Purpose: Log an information message to the console.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +VOID HlprLogInfo(_In_ PCWSTR pMessage, + ...) +{ + ASSERT(pMessage); + + UINT32 status = NO_ERROR; + PCWSTR pFormatMessage = L"INFO: %s\n"; + PWSTR pActualMessage = 0; + size_t size = 0; + size_t actualSize = 9; /// Size of format Message and room for NULL Terminator '\0' + WCHAR pLogMessage[MAX_CHARS] = {0}; + + va_list argumentList; + + va_start(argumentList, + pMessage); + + status = StringCchVPrintf(pLogMessage, + MAX_CHARS, + pMessage, + argumentList); + if(status != NO_ERROR) + { + HlprLogError(L"HlprLogInfo : StringCchVPrintf() [status: %#x]", + status); + + HLPR_BAIL; + } + + status = StringCchLength(pLogMessage, + STRSAFE_MAX_CCH, + &size); + if(status != NO_ERROR) + { + HlprLogError(L"HlprLogInfo : StringCchLength() [status: %#x]", + status); + + HLPR_BAIL; + } + + actualSize += size; + + HLPR_NEW_ARRAY(pActualMessage, + WCHAR, + actualSize); + HLPR_BAIL_ON_ALLOC_FAILURE(pActualMessage, + status); + + status = StringCchPrintf(pActualMessage, + actualSize, + pFormatMessage, + pLogMessage); + if(status != NO_ERROR) + { + HlprLogError(L"HlprLogInfo : StringCchPrintf() [status: %#x]", + status); + + HLPR_BAIL; + } + + wprintf(pActualMessage); + + HLPR_BAIL_LABEL: + + va_end(argumentList); + + HLPR_DELETE_ARRAY(pActualMessage); + + return; +} diff --git a/network/trans/WFPSampler/lib/HelperFunctions_Log.h b/network/trans/WFPSampler/lib/HelperFunctions_Log.h new file mode 100644 index 000000000..4829509e6 --- /dev/null +++ b/network/trans/WFPSampler/lib/HelperFunctions_Log.h @@ -0,0 +1,30 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_Log.h +// +// Abstract: +// This module contains prototypes for functions which assist in actions pertaining to logging. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef HELPERFUNCTIONS_LOG_H +#define HELPERFUNCTIONS_LOG_H + +VOID HlprLogError(_In_ PCWSTR pMessage, + ...); + +VOID HlprLogInfo(_In_ PCWSTR pMessage, + ...); + +#endif /// HELPERFUNCTIONS_LOG_H \ No newline at end of file diff --git a/network/trans/WFPSampler/lib/HelperFunctions_MACAddress.cpp b/network/trans/WFPSampler/lib/HelperFunctions_MACAddress.cpp new file mode 100644 index 000000000..d500b08e0 --- /dev/null +++ b/network/trans/WFPSampler/lib/HelperFunctions_MACAddress.cpp @@ -0,0 +1,237 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_MACAddress.cpp +// +// Abstract: +// This module contains functions which assist in actions pertaining to MAC Addresses. +// +// Naming Convention: +// +// +// +// i.e. +// +// +// { +// - Function is likely visible to other modules +// } +// +// { +// Hlpr - Function is from HelperFunctions_* Modules. +// } +// +// { +// EthernetMACAddressString - Function pertains to null terminated wide character string +// representaions of Ethernet MAC Addresses +// } +// +// { +// To - Function converts from one type to another +// Is - Function compares values. +// } +// +// { +// ValidFormat - Function validates condition. +// Value - Function acts on a value. +// } +// +// Private Functions: +// +// Public Functions: +// HlprEthernetMACAddressStringIsValidFormat(), +// HlprEthernetMACAddressStringToValue(), +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Add offset to check in +// HlprEthernetMACAddressStringToValue +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "HelperFunctions_Include.h" + +/** + @helper_function="HlprEthernetMACAddressStringIsValidFormat" + + Purpose: Determine if a string may be an Ethernet MAC address by verifying the string:
+ is at least 17 characters
+ has at least 5 colons(':') or hyphens('-')
+
+ Notes:
+
+ MSDN_Ref:
+*/ +BOOLEAN HlprEthernetMACAddressStringIsValidFormat(_In_ PCWSTR pEthernetMACAddressString) +{ + BOOLEAN isEthernetMACAddress = FALSE; + + if(pEthernetMACAddressString) + { + UINT32 status = NO_ERROR; + size_t stringLength = 0; + + status = StringCchLength(pEthernetMACAddressString, + STRSAFE_MAX_CCH, + &stringLength); + if(status != ERROR_SUCCESS) + { + HlprLogError(L"HlprEthernetMACAddressStringIsValidFormat : StringCchLength() [status: %#x]", + status); + + HLPR_BAIL; + } + else if(stringLength <= IEEE_802_ADDRESS_STRING_BUFFER) + { + if(wcschr(pEthernetMACAddressString, + L':')) + { + UINT32 numColons = 0; + + for(UINT32 index = 0; + index < stringLength; + index++) + { + if(pEthernetMACAddressString[index] == L':') + numColons++; + } + + if(numColons == 5) + isEthernetMACAddress = TRUE; + } + else + { + if(wcschr(pEthernetMACAddressString, + L'-')) + { + UINT32 numHyphens = 0; + + for(UINT32 index = 0; + index < stringLength; + index++) + { + if(pEthernetMACAddressString[index] == L'-') + numHyphens++; + } + + if(numHyphens == 5) + isEthernetMACAddress = TRUE; + } + } + } + } + + HLPR_BAIL_LABEL: + + if(!isEthernetMACAddress) + HlprLogError(L"HlprEthernetMACAddressStringIsValidFormat() [status: %#x][pEthernetMACAddressString: %s]", + ERROR_INVALID_DATA, + pEthernetMACAddressString); + + return isEthernetMACAddress; +} + +/** + @helper_function="HlprEthernetMACAddressStringToValue" + + Purpose: Convert a string representing an Ethernet MAC Address to it's 6 BYTE value.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 HlprEthernetMACAddressStringToValue(_In_ PCWSTR pEthernetMACAddressString, + _Inout_updates_all_(IEEE_802_ADDRESS_LENGTH) BYTE* pEthernetMACAddress) +{ + UINT32 status = NO_ERROR; + + if(pEthernetMACAddressString && + HlprEthernetMACAddressStringIsValidFormat(pEthernetMACAddressString)) + { + UINT32 offset = 0; + UINT32 bytePosition = 0x10; + + ZeroMemory(pEthernetMACAddress, + IEEE_802_ADDRESS_LENGTH); + +#pragma warning(push) +#pragma warning(disable: 26018) /// length validation occurs in HlprEthernetMACAddressStringIsValidFormat + + for(UINT32 i = 0; + i < IEEE_802_ADDRESS_STRING_BUFFER && /// does not include '\0' + offset < IEEE_802_ADDRESS_LENGTH; + i++) + { + WCHAR character = pEthernetMACAddressString[i]; + + /// Validate every 3 character is either a ':' or a '-' + if((i + 1) % 3 == 0) + { + if(character != L':' && + character != L'-') + { + status = ERROR_INVALID_DATA; + + HlprLogError(L"HlprEthernetMACAddressStringToValue() [status: %#x][pEthernetMACAddressString: %s]", + status, + pEthernetMACAddressString); + + HLPR_BAIL; + } + + continue; + } + + if(character >= L'0' && + character <= L'9') + pEthernetMACAddress[offset] = (BYTE)(pEthernetMACAddress[offset] + ((character - L'0') * bytePosition)); + else if(character >= L'a' && + character <= L'f') + pEthernetMACAddress[offset] = (BYTE)(pEthernetMACAddress[offset] + ((character + 10 - L'a') * bytePosition)); + else if(character >= L'A' && + character <= L'F') + pEthernetMACAddress[offset] = (BYTE)(pEthernetMACAddress[offset] + ((character + 10 - L'A') * bytePosition)); + else + { + status = ERROR_INVALID_DATA; + + HlprLogError(L"HlprEthernetMACAddressStringToValue() [status: %#x][pEthernetMACAddressString: %s]", + status, + pEthernetMACAddressString); + + HLPR_BAIL; + } + + if(bytePosition == 1) + { + offset++; + + bytePosition = 0x10; + } + else + bytePosition = 1; + } + +#pragma warning(pop) + + HLPR_BAIL_LABEL: + + if(status != ERROR_SUCCESS) + ZeroMemory(pEthernetMACAddress, + IEEE_802_ADDRESS_LENGTH); + } + else + HlprLogError(L"HlprEthernetMACAddressStringToValue() [status: %#x][pEthernetMACAddressString: %s]", + status, + pEthernetMACAddressString); + + return status; +} diff --git a/network/trans/WFPSampler/lib/HelperFunctions_MACAddress.h b/network/trans/WFPSampler/lib/HelperFunctions_MACAddress.h new file mode 100644 index 000000000..5aaa83418 --- /dev/null +++ b/network/trans/WFPSampler/lib/HelperFunctions_MACAddress.h @@ -0,0 +1,30 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_MACAddress.h +// +// Abstract: +// This module contains functions which assist in actions pertaining to MAC Addresses. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef HELPERFUNCTIONS_MAC_ADDRESS_H +#define HELPERFUNCTIONS_MAC_ADDRESS_H + +BOOLEAN HlprEthernetMACAddressStringIsValidFormat(_In_ PCWSTR pEthernetMACAddress); + +_Success_(return == NO_ERROR) +UINT32 HlprEthernetMACAddressStringToValue(_In_ PCWSTR pEthernetMACAddressString, + _Inout_updates_all_(IEEE_802_ADDRESS_LENGTH) BYTE* pEthernetMACAddress); + +#endif /// HELPERFUNCTIONS_MAC_ADDRESS_H \ No newline at end of file diff --git a/network/trans/WFPSampler/lib/HelperFunctions_Macros.h b/network/trans/WFPSampler/lib/HelperFunctions_Macros.h new file mode 100644 index 000000000..afa099393 --- /dev/null +++ b/network/trans/WFPSampler/lib/HelperFunctions_Macros.h @@ -0,0 +1,408 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_Macros.h +// +// Abstract: +// This module contains definitions for the various macros used throughout this program. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef HELPERFUNCTIONS_MACROS_H +#define HELPERFUNCTIONS_MACROS_H + +#include /// Include\UM +#include /// Include\UM +#include /// Inc\CRT +#include /// Include\UM +#include /// Include\UM +#include /// Include\UM +#include /// Include\UM +#include /// Include\Shared +#include /// Include\UM +#include /// Include\UM +#include /// Include\Shared +#include /// Include\Shared +#include /// Include\Shared +#include /// Include\Shared + +#include "WFPSamplerRPC.h" /// $(OBJ_PATH)\..\idl\$(O) +#include "Identifiers.h" /// ..\inc +#include "WFPArrays.h" /// ..\inc +#include "ScenarioData.h" /// ..\inc + +/// + +VOID HlprLogError(_In_ PCWSTR pMessage, + ...); + +/// + +/** + @macro="HLPR_CLOSE_HANDLE" + + Purpose: Close a standard handle and set to 0.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +#define HLPR_CLOSE_HANDLE(handle) \ + if(handle) \ + { \ + CloseHandle(handle); \ + handle = 0; \ + } + +/** + @macro="HLPR_CLOSE_SERVICE_HANDLE" + + Purpose: Close a service handle and set to 0.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +#define HLPR_CLOSE_SERVICE_HANDLE(handle) \ + if(handle) \ + { \ + CloseServiceHandle(handle); \ + handle = 0; \ + } + +/** + @macro="HLPR_DELETE" + + Purpose: Free memory allocated with new and set the pointer to 0.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +#define HLPR_DELETE(pPtr) \ + if(pPtr) \ + { \ + delete pPtr; \ + pPtr = 0; \ + } + +/** + @macro="HLPR_DELETE_ARRAY" + + Purpose: Free memory allocated with new[] and set the pointer to 0.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +#define HLPR_DELETE_ARRAY(pPtr) \ + if(pPtr) \ + { \ + delete[] pPtr; \ + pPtr = 0; \ + } + +/** + @macro="HLPR_NEW" + + Purpose: Allocate memory with new and zero out its contents.
+
+ Notes: Caller responsible for freeing allocated memory using macro HLPR_DELETE.
+
+ MSDN_Ref:
+*/ +#define HLPR_NEW(pPtr, object) \ + do \ + { \ + HLPR_DELETE(pPtr); \ + pPtr = new object; \ + if(pPtr) \ + SecureZeroMemory(pPtr, \ + sizeof(object)); \ + }while(pPtr == 0) + +/** + @macro="HLPR_NEW_ARRAY" + + Purpose: Allocate memory with new[] and zero out its contents.
+
+ Notes: Caller responsible for freeing allocated memory using macro HLPR_DELETE_ARRAY.
+
+ MSDN_Ref:
+*/ +#define HLPR_NEW_ARRAY(pPtr, object, count) \ + do \ + { \ + size_t SAFE_SIZE = 0; \ + HLPR_DELETE_ARRAY(pPtr); \ + if(SizeTMult(sizeof(object), \ + (size_t)count, \ + &SAFE_SIZE) == S_OK && \ + SAFE_SIZE >= (sizeof(object) * count)) \ + { \ + pPtr = new object[count]; \ + if(pPtr) \ + SecureZeroMemory(pPtr, \ + SAFE_SIZE); \ + } \ + else \ + { \ + HlprLogError(L"[count: %d][objectSize: %d]", \ + count, \ + sizeof(object)); \ + break; \ + } \ + }while(pPtr == 0) + +/** + @macro="HLPR_NEW_CASTED_ARRAY" + + Purpose: Allocate memory with new[] while casting the pointer, and zero out its contents.
+
+ Notes: Caller responsible for freeing allocated memory using macro HLPR_DELETE_ARRAY.
+
+ MSDN_Ref:
+*/ +#define HLPR_NEW_CASTED_ARRAY(pPtr, CAST_TYPE, object, count) \ + do \ + { \ + size_t SAFE_SIZE = 0; \ + HLPR_DELETE_ARRAY(pPtr); \ + if(SizeTMult(sizeof(object), \ + (size_t)count, \ + &SAFE_SIZE) == S_OK && \ + SAFE_SIZE >= (sizeof(object) * count)) \ + { \ + pPtr = (CAST_TYPE*)(new object[count]); \ + if(pPtr) \ + SecureZeroMemory(pPtr, \ + SAFE_SIZE); \ + } \ + else \ + { \ + HlprLogError(L"[count: %d][objectSize: %d]", \ + count, \ + sizeof(object)); \ + break; \ + } \ + }while(pPtr == 0) + +/** + @macro="HLPR_BAIL_LABEL" + + Purpose: Tag for the cleanup and exit portion of the function.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +#define HLPR_BAIL_LABEL \ + cleanup + +/* + @macro="HLPR_BAIL_LABEL_2" + + Purpose: Tag for the cleanup and exit portion of the function.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +#define HLPR_BAIL_LABEL_2 \ + cleanup_2 + +/** + @macro="HLPR_BAIL_ON_FAILURE_WITH_LABEL" + + Purpose: Jump in the code's execution to the provided tag if an error occurs.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +#define HLPR_BAIL_ON_FAILURE_WITH_LABEL(status, label) \ + if(status != NO_ERROR) \ + goto label + +/** + @macro="HLPR_BAIL_ON_FAILURE" + + Purpose: Jump in the code's execution path to the HLPR_BAIL_LABEL tag if an error occurs.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +#define HLPR_BAIL_ON_FAILURE(status) \ + HLPR_BAIL_ON_FAILURE_WITH_LABEL(status, \ + HLPR_BAIL_LABEL) + +/** + @macro="HLPR_BAIL_ON_FAILURE_2" + + Purpose: Jump in the code's execution path to the HLPR_BAIL_LABEL_2 tag if an error occurs.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +#define HLPR_BAIL_ON_FAILURE_2(status) \ + HLPR_BAIL_ON_FAILURE_WITH_LABEL(status, \ + HLPR_BAIL_LABEL_2) + +/** + @macro="HLPR_BAIL_ON_ALLOC_FAILURE_WITH_LABEL" + + Purpose: Jump in the code's execution path to the provided tag if memory allocation fails.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +#define HLPR_BAIL_ON_ALLOC_FAILURE_WITH_LABEL(pPtr, status, label) \ + if(pPtr == 0) \ + { \ + status = ERROR_OUTOFMEMORY; \ + HlprLogError(L"Allocation Failure [status: %#x]", status); \ + goto label; \ + } + +/** + @macro="HLPR_BAIL_ON_ALLOC_FAILURE" + + Purpose: Jump in the code's execution path to the HLPR_BAIL_LABEL tag if memory allocation + fails.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +#define HLPR_BAIL_ON_ALLOC_FAILURE(pPtr, status) \ + HLPR_BAIL_ON_ALLOC_FAILURE_WITH_LABEL(pPtr, \ + status, \ + HLPR_BAIL_LABEL) + +/** + @macro="HLPR_BAIL_ON_ALLOC_FAILURE_2" + + Purpose: Jump in the code's execution path to the HLPR_BAIL_LABEL_2 tag if memory allocation + fails.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +#define HLPR_BAIL_ON_ALLOC_FAILURE_2(pPtr, status) \ + HLPR_BAIL_ON_ALLOC_FAILURE_WITH_LABEL(pPtr, \ + status, \ + HLPR_BAIL_LABEL_2) + +/** + @macro="HLPR_BAIL_ON_NULL_POINTER" + + Purpose: Jump in the code's execution path to the HLPR_BAIL_LABEL tag if the pointer is + NULL.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +#define HLPR_BAIL_ON_NULL_POINTER(pPtr) \ + if(pPtr == 0) \ + HLPR_BAIL + +/** + @macro="HLPR_BAIL_ON_NULL_POINTER_2" + + Purpose: Jump in the code's execution path to the HLPR_BAIL_LABEL_2 tag if the pointer is + NULL.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +#define HLPR_BAIL_ON_NULL_POINTER_2(pPtr) \ + if(pPtr == 0) \ + HLPR_BAIL_2 + +/** + @macro="HLPR_BAIL_ON_NULL_POINTER_WITH_STATUS" + + Purpose: Jump in the code's execution path to the HLPR_BAIL_LABEL tag if the pointer is + NULL.
+
+ Notes: Status is set to ERROR_OUTOFMEMORY.
+
+ MSDN_Ref:
+*/ +#define HLPR_BAIL_ON_NULL_POINTER_WITH_STATUS(pPtr, status) \ + if(pPtr == 0) \ + { \ + status = ERROR_OUTOFMEMORY; \ + HLPR_BAIL; \ + } + +/** + @macro="HLPR_BAIL_ON_NULL_POINTER_WITH_STATUS_2" + + Purpose: Jump in the code's execution path to the HLPR_BAIL_LABEL_2 tag if the pointer is + NULL.
+
+ Notes: Status is set to ERROR_OUTOFMEMORY.
+
+ MSDN_Ref:
+*/ +#define HLPR_BAIL_ON_NULL_POINTER_WITH_STATUS_2(pPtr, status) \ + if(pPtr == 0) \ + { \ + status = ERROR_OUTOFMEMORY; \ + HLPR_BAIL_2; \ + } + +/** + @macro="HLPR_BAIL_WITH_LABEL" + + Purpose: Jump in the code's execution path to the provided tag.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +#define HLPR_BAIL_WITH_LABEL(label) \ + goto label + +/** + @macro="HLPR_BAIL" + + Purpose: Jump in the code's execution path to the HLPR_BAIL_LABEL tag.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +#define HLPR_BAIL \ + HLPR_BAIL_WITH_LABEL(HLPR_BAIL_LABEL) + +/** + @macro="HLPR_BAIL_2" + + Purpose: Jump in the code's execution path to the HLPR_BAIL_LABEL_2 tag.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +#define HLPR_BAIL_2 \ + HLPR_BAIL_WITH_LABEL(HLPR_BAIL_LABEL_2) + +#endif /// HELPERFUNCTIONS_MACROS_H \ No newline at end of file diff --git a/network/trans/WFPSampler/lib/HelperFunctions_Process.cpp b/network/trans/WFPSampler/lib/HelperFunctions_Process.cpp new file mode 100644 index 000000000..6124ab407 --- /dev/null +++ b/network/trans/WFPSampler/lib/HelperFunctions_Process.cpp @@ -0,0 +1,196 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_Process.cpp +// +// Abstract: +// This module contains functions which assist actions pertaining to processes. +// +// Naming Convention: +// +// +// +// i.e. +// +// +// { +// - Function is likely visible to other modules +// Prv - Function is private to this module. +// } +// +// { +// Hlpr - Function is from HelperFunctions_* Modules. +// } +// +// { +// Process - Function pertains to processes. +// } +// +// { +// Get - Function retrieves data. +// } +// +// { +// State - Funstion acts on the object's state. +// String - Function acts on a null terminated wide character string. +// } +// +// Private Functions: +// +// Public Functions: +// HlprProcessGetID(), +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "HelperFunctions_Include.h" /// . + +/** + @helper_function="PrvHlprServiceQueryState" + + Purpose: Return the state of the service.
+
+ Notes:
+
+ +*/ + +/** + @helper_function="HlprServiceStop" + + Purpose: Issue a stop control to the specified service.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/MS682489.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/MS684834.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/MS684836.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/MS684320.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/MS684218.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/MS684221.aspx
+*/ +_Success_(return == NO_ERROR) +UINT32 HlprProcessGetID(_In_ PCWSTR pProcessName, + _Inout_ UINT32* pPID) +{ + ASSERT(pProcessName); + ASSERT(pPID); + + UINT32 status = NO_ERROR; + HANDLE processSnapshotHandle = 0; + PROCESSENTRY32 processEntry = {0}; + + processEntry.dwSize = sizeof(PROCESSENTRY32); + + processSnapshotHandle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, + 0); + if(processSnapshotHandle == INVALID_HANDLE_VALUE) + { + status = GetLastError(); + + HlprLogError(L"HlprProcessGetID : CreateToolhelp32Snapshot() [status: %#x]", + status); + + HLPR_BAIL; + } + + if(!Process32First(processSnapshotHandle, + &processEntry)) + { + status = GetLastError(); + + HlprLogError(L"HlprProcessGetID : Process32First() [status: %#x]", + status); + + HLPR_BAIL; + } + + for(; + *pPID == 0; + ) + { + HANDLE processHandle = 0; + HANDLE moduleSnapshotHandle = 0; + MODULEENTRY32 moduleEntry = {0}; + + moduleEntry.dwSize = sizeof(MODULEENTRY32); + + processHandle = OpenProcess(PROCESS_QUERY_INFORMATION | + PROCESS_VM_READ, + FALSE, + processEntry.th32ProcessID); + if(processHandle == 0) + { + status = GetLastError(); + + /// Don't log errors for the Idle, CSRSS, & System processes + if(status != ERROR_ACCESS_DENIED && + status != ERROR_INVALID_PARAMETER) + HlprLogError(L"HlprProcessGetID : OpenProcess() [status: %#x]", + status); + + HLPR_BAIL_2; + } + + moduleSnapshotHandle = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, + processEntry.th32ProcessID); + if(moduleSnapshotHandle == INVALID_HANDLE_VALUE) + { + status = GetLastError(); + + HlprLogError(L"HlprProcessGetID : CreateToolhelp32Snapshot() [status: %#x]", + status); + + HLPR_BAIL_2; + } + + if(!Module32First(moduleSnapshotHandle, + &moduleEntry)) + { + status = GetLastError(); + + HlprLogError(L"HlprProcessGetID : Module32First() [status: %#x]", + status); + + HLPR_BAIL_2; + } + + for(; + *pPID == 0; + ) + { + if(HlprStringsAreEqual(pProcessName, + moduleEntry.szModule)) + *pPID = processEntry.th32ProcessID; + + if(!Module32Next(moduleSnapshotHandle, + &moduleEntry)) + break; + } + + HLPR_BAIL_LABEL_2: + + HLPR_CLOSE_HANDLE(processHandle); + + if(!Process32Next(processSnapshotHandle, + &processEntry)) + break; + } + + HLPR_BAIL_LABEL: + + HLPR_CLOSE_HANDLE(processSnapshotHandle); + + ASSERT(*pPID); + + return status; +} diff --git a/network/trans/WFPSampler/lib/HelperFunctions_Process.h b/network/trans/WFPSampler/lib/HelperFunctions_Process.h new file mode 100644 index 000000000..64bfb94e3 --- /dev/null +++ b/network/trans/WFPSampler/lib/HelperFunctions_Process.h @@ -0,0 +1,30 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_Process.h +// +// Abstract: +// This module contains prototypes for functions which assist in actions pertaining to +// processes. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef HELPERFUNCTIONS_PROCESS_H +#define HELPERFUNCTIONS_PROCESS_H + + +_Success_(return == NO_ERROR) +UINT32 HlprProcessGetID(_In_ PCWSTR pProcessName, + _Inout_ UINT32* pPID); + +#endif /// HELPERFUNCTIONS_PROCESS_H \ No newline at end of file diff --git a/network/trans/WFPSampler/lib/HelperFunctions_Registry.cpp b/network/trans/WFPSampler/lib/HelperFunctions_Registry.cpp new file mode 100644 index 000000000..c933587cb --- /dev/null +++ b/network/trans/WFPSampler/lib/HelperFunctions_Registry.cpp @@ -0,0 +1,232 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_Registry.cpp +// +// Abstract: +// This module contains functions which simplify registry operations. +// +// Private Functions: +// +// Public Functions: +// HlprRegistryDeleteKey(), +// HlprRegistryDeleteValue(), +// HlprRegistryGetValue(), +// HlprRegistrySetValue(), +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2012 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "HelperFunctions_Include.h" /// . + +/** + @helper_function="HlprRegistryDeleteValue" + + Purpose: Delete the Value from the specified Registry key.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-Us/Library/Windows/Desktop/MS724851.aspx
+*/ +_Success_(return == NO_ERROR) +UINT32 HlprRegistryDeleteValue(_In_ HKEY hKey, + _In_opt_ PWSTR pSubKey, + _In_ PWSTR pValueName) +{ + UINT32 status = NO_ERROR; + HKEY keyHandle = 0; + + if(pSubKey) + { + UINT32 dispositionValue = 0; + + status = RegCreateKeyEx(hKey, + pSubKey, + 0, + 0, + REG_OPTION_NON_VOLATILE, + KEY_WRITE, + 0, + &keyHandle, + (LPDWORD)&dispositionValue); + if(status != NO_ERROR) + { + HlprLogError(L"HlprRegistryDeleteValue : RegCreateKeyEx() [status: %#x][subKey: %s]", + status, + pSubKey); + + HLPR_BAIL; + } + } + else + keyHandle = hKey; + + status = RegDeleteValue(keyHandle, + pValueName); + if(status != NO_ERROR) + { + if(status != ERROR_FILE_NOT_FOUND) + { + HlprLogError(L"HlprRegistryDeleteValue : RegDeleteValue() [status: %#x][Value: %s%s%s]", + status, + pSubKey ? pSubKey : L"", + pSubKey ? L"\\" : L"", + pValueName); + + HLPR_BAIL; + } + + status = NO_ERROR; + } + + HLPR_BAIL_LABEL: + + return status; +} + + +/** + @helper_function="HlprRegistrySetValue" + + Purpose: Opens a handle to the key, creates the appropriate value, and closes the key.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-Us/Library/Windows/Desktop/MS724844.aspx
+ HTTP://MSDN.Microsoft.com/En-Us/Library/Windows/Desktop/MS724923.aspx
+*/ +_Success_(return == NO_ERROR) +UINT32 HlprRegistrySetValue(_In_ HKEY hKey, + _In_ PWSTR pSubKey, + _In_ UINT32 type, + _In_opt_ PWSTR pValueName, + _In_reads_bytes_(valueSize) BYTE* pValue, + _In_ UINT32 valueSize) +{ + UINT32 status = NO_ERROR; + HKEY keyHandle = 0; + UINT32 dispositionValue = 0; + + status = RegCreateKeyEx(hKey, + pSubKey, + 0, + 0, + REG_OPTION_NON_VOLATILE, + KEY_WRITE, + 0, + &keyHandle, + (LPDWORD)&dispositionValue); + if(status != NO_ERROR) + { + HlprLogError(L"HlprRegistrySetValue : RegCreateKeyEx() [status: %#x][subKey: %s]", + status, + pSubKey); + + HLPR_BAIL; + } + + status = RegSetValueEx(keyHandle, + pValueName, + 0, + type, + pValue, + valueSize); + if(status != NO_ERROR) + { + HlprLogError(L"HlprRegistrySetValue : RegSetValueEx() [status: %#x][valueName: %s]", + status, + pValueName ? pValueName : L"(Default)"); + + HLPR_BAIL; + } + + HLPR_BAIL_LABEL: + + RegCloseKey(keyHandle); + + return status; +}; + +/** + @helper_function="HlprRegistryGetValue" + + Purpose: Retrieves a value from the registry.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-Us/Library/Windows/Desktop/MS724868.aspx
+*/ +_Success_(return == NO_ERROR) +UINT32 HlprRegistryGetValue(_In_ HKEY hKey, + _In_ PWSTR pSubKey, + _In_opt_ PWSTR pValueName, + _Inout_ REGISTRY_VALUE* pRegValue) +{ + UINT32 status = NO_ERROR; + + status = RegGetValue(hKey, + pSubKey, + pValueName, + RRF_RT_ANY, + (LPDWORD)&(pRegValue->type), + (PVOID)pRegValue->pBuffer, + (LPDWORD)&(pRegValue->size)); + if(status != NO_ERROR) + { + HlprLogError(L"HlprRegistryGetValue : RegGetValue() [status: %#x][Value: %s\\%s]", + status, + pSubKey, + pValueName ? pValueName : L"(Default)"); + + HLPR_BAIL_ON_FAILURE(status); + } + + HLPR_BAIL_LABEL: + + return status; +} + +/** + @helper_function="HlprRegistryDeleteKey" + + Purpose: Delete the SubKey from the Registry.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-Us/Library/Windows/Desktop/MS724845.aspx
+*/ +_Success_(return == NO_ERROR) +UINT32 HlprRegistryDeleteKey(_In_ HKEY hKey, + _In_ PWSTR pSubKey) +{ + UINT32 status = NO_ERROR; + + status = RegDeleteKey(hKey, + pSubKey); + if(status != NO_ERROR) + { + if(status != ERROR_FILE_NOT_FOUND) + { + HlprLogError(L"HlprRegistryDeleteKey : RegDeleteKey() [status: %#x][subKey: %s]", + status, + pSubKey); + + HLPR_BAIL; + } + + status = NO_ERROR; + } + + HLPR_BAIL_LABEL: + + return status; +} diff --git a/network/trans/WFPSampler/lib/HelperFunctions_Registry.h b/network/trans/WFPSampler/lib/HelperFunctions_Registry.h new file mode 100644 index 000000000..d2ba636ac --- /dev/null +++ b/network/trans/WFPSampler/lib/HelperFunctions_Registry.h @@ -0,0 +1,59 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_Registry.h +// +// Abstract: +// This module contains prototypes for functions which simplif registry operations +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2012 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef HELPERFUNCTIONS_REGISTRY_H +#define HELPERFUNCTIONS_REGISTRY_H + +typedef struct REGISTRY_VALUE_ +{ + UINT32 type; + UINT32 size; + union + { + UINT32 dword; + UINT64 qword; + BYTE pBuffer[1024]; + }; +}REGISTRY_VALUE,*PREGISTRY_VALUE; + +_Success_(return == NO_ERROR) +UINT32 HlprRegistryDeleteValue(_In_ HKEY hKey, + _In_opt_ PWSTR pSubKey, + _In_ PWSTR pValueName); + +_Success_(return == NO_ERROR) +UINT32 HlprRegistrySetValue(_In_ HKEY hKey, + _In_ PWSTR pSubKey, + _In_ UINT32 type, + _In_opt_ PWSTR pValueName, + _In_reads_bytes_(valueSize) BYTE* pValue, + _In_ UINT32 valueSize); + +_Success_(return == NO_ERROR) +UINT32 HlprRegistryGetValue(_In_ HKEY hKey, + _In_ PWSTR pSubKey, + _In_opt_ PWSTR pValueName, + _Inout_ REGISTRY_VALUE* pRegValue); + +_Success_(return == NO_ERROR) +UINT32 HlprRegistryDeleteKey(_In_ HKEY hKey, + _In_ PWSTR pSubKey); + +#endif /// HELPERFUNCTIONS_REGISTRY_H \ No newline at end of file diff --git a/network/trans/WFPSampler/lib/HelperFunctions_SID.cpp b/network/trans/WFPSampler/lib/HelperFunctions_SID.cpp new file mode 100644 index 000000000..2a35da83e --- /dev/null +++ b/network/trans/WFPSampler/lib/HelperFunctions_SID.cpp @@ -0,0 +1,473 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_SID.cpp +// +// Abstract: +// This module contains functions which assist in actions pertaining to security identifiers +// (SIDs). +// +// Naming Convention: +// +// +// +// i.e. +// +// +// { +// - Function is likely visible to other modules +// Prv - Function is private to this module. +// } +// +// { +// Hlpr - Function is from HelperFunctions_* Modules. +// } +// +// { +// SID - Function pertains to SID objects. +// } +// +// { +// Create - Function allocates and fills memory. +// Destroy - Function cleans up and frees memory. +// Get - Function retrieves requested data. +// } +// +// { +// Current - Function acts on current object. +// ForCurrentUser - Function acts on behalf of current user. +// WellKnown - Function acts on Windows defined entities. +// } +// +// Private Functions: +// PrvHlprUserNameGetCurrent +// +// Public Functions: +// HlprSIDDestroy(), +// HlprSIDCreate(), +// HlprSIDGetForCurrentUser() +// HlprSIDGetWellKnown() +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "HelperFunctions_Include.h" /// . + +/** + @private_function="PrvHlprUserNameGetCurrent" + + Purpose: Allocate memory and return a string representation of the current user name.
+
+ Notes: The caller is responsible for freeing any allocated memory using + HLPR_DELETE_ARRAY()
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/MS724432.aspx
+*/ +_Success_(return != 0) +PWSTR PrvHlprUserNameGetCurrent() +{ + UINT32 status = ERROR_GEN_FAILURE; + PWSTR pUserName = 0; + + for(UINT32 nameLength = 0; + status != NO_ERROR; + ) + { + if(nameLength) + { + HLPR_NEW_ARRAY(pUserName, + WCHAR, + nameLength); + HLPR_BAIL_ON_ALLOC_FAILURE(pUserName, + status); + } + + if(!GetUserName(pUserName, + (DWORD*)&nameLength)) + { + status = GetLastError(); + + if(status != ERROR_INSUFFICIENT_BUFFER || + nameLength == 0) + { + HlprLogError(L"PrvHlprUserNameGetCurrent : GetUserName() [status: %#x]", + status); + + HLPR_BAIL; + } + + HLPR_DELETE_ARRAY(pUserName); + } + + if(pUserName && + nameLength) + { + pUserName[nameLength - 1] = '\0'; /// Ensure NULL Termination + + status = NO_ERROR; + + break; + } + } + + HLPR_BAIL_LABEL: + + if(status != NO_ERROR) + { + HLPR_DELETE_ARRAY(pUserName); + } + + return pUserName; +} + +/** + @helper_function="HlprSIDDestroy" + + Purpose: Free an allocated SID.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/aa379594.aspx
+*/ +_At_(*ppSID, _Pre_ _Maybenull_) +_At_(*ppSID, _Post_ _Null_) +_Success_(*ppSID == 0) +VOID HlprSIDDestroy(_Inout_ SID** ppSID) +{ + if(ppSID) + { + HLPR_DELETE_ARRAY(*ppSID); + } + + return; +} + +/** + @helper_function="HlprSIDCreate" + + Purpose: Allocate memory and populate with a SID for either the specified account name and + type (SID_NAME_USE), or a well-known SID based on the type (WELL_KNOWN_SID_TYPE).
+
+ Notes: The caller is responsible for freeing any allocated memory using HlprSIDDestroy().
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/AA379594.aspx
+*/ +_At_(*ppSID, _Pre_ _Null_) +_When_(return != NO_ERROR, _At_(*ppSID, _Post_ _Null_)) +_When_(return == NO_ERROR, _At_(*ppSID, _Post_ _Notnull_)) +_Success_(return == NO_ERROR) +UINT32 HlprSIDCreate(_Outptr_result_bytebuffer_(*pSIDSize) SID** ppSID, + _Out_ SIZE_T* pSIDSize, + _In_opt_ PCWSTR pAccountName, /* 0 */ + _In_ UINT32 type) /* WinNullSid */ +{ + UINT32 status = NO_ERROR; + + if(ppSID && + pSIDSize) + { + PWSTR pDomainName = 0; + SIZE_T accountSIDSize = 0; + SIZE_T domainNameSize = 0; + const UINT32 NUM_TRIES = 2; + + for(UINT32 index = 0; + index < NUM_TRIES; + index++) + { + BYTE* pByteBuffer = 0; + + if(pAccountName) + { + if(!LookupAccountName(0, + pAccountName, + *ppSID, + (DWORD*)&accountSIDSize, + pDomainName, + (DWORD*)&domainNameSize, + (PSID_NAME_USE)(&type))) + { + status = GetLastError(); + + if(status != ERROR_INSUFFICIENT_BUFFER || + accountSIDSize == 0 || + domainNameSize == 0) + { + HlprLogError(L"HlprSIDCreate : LookupAccountName [status: %#x][accountSIDSize: %d][domainNameSize: %d]", + status, + accountSIDSize, + domainNameSize); + + HLPR_BAIL; + } + else + status = NO_ERROR; + + HLPR_NEW_ARRAY(pDomainName, + WCHAR, + domainNameSize); + HLPR_BAIL_ON_ALLOC_FAILURE(pDomainName, + status); + } + else + break; + } + else + { + if(!CreateWellKnownSid((WELL_KNOWN_SID_TYPE)type, + 0, + *ppSID, + (DWORD*)&accountSIDSize)) + { + status = GetLastError(); + + if(status != ERROR_INSUFFICIENT_BUFFER || + accountSIDSize == 0) + { + HlprLogError(L"HlprSIDCreate : CreateWellKnownSid() [status: %#x][accountSIDSize: %d]", + status, + accountSIDSize); + + HLPR_BAIL; + } + else + status = NO_ERROR; + } + else + break; + } + + if(index) + { + HLPR_DELETE_ARRAY(pDomainName); + } + else + { + HLPR_NEW_ARRAY(pByteBuffer, + BYTE, + accountSIDSize); + HLPR_BAIL_ON_ALLOC_FAILURE(pByteBuffer, + status); + + *ppSID = (SID*)pByteBuffer; + + *pSIDSize = accountSIDSize; + } + } + + if(*ppSID == 0 || + *pSIDSize == 0 || + !IsValidSid(*ppSID)) + { + status = ERROR_INVALID_SID; + + HlprLogError(L"HlprSIDCreate : IsValidSid() [status: %#x]", + status); + + HLPR_BAIL; + } + + status = NO_ERROR; + + HLPR_BAIL_LABEL: + + if(status != NO_ERROR) + { +#pragma warning(push) +#pragma warning(disable: 26000) /// no possible overflow + + HlprSIDDestroy(ppSID); + +#pragma warning(pop) + + *pSIDSize = 0; + } + } + else + { + status = ERROR_INVALID_PARAMETER; + + HlprLogError(L"HlprSIDCreate() [status: %#x][ppSID: %#p][pSIDSize: %#p]", + status, + ppSID, + pSIDSize); + } + + return status; +} + +/** + @helper_function="HlprSIDGetForCurrentUser" + + Purpose: Allocate memory and populate with a SID for the current running user.
+
+ Notes: The caller is responsible for freeing any allocated memory using HlprSIDDestroy().
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/AA379594.aspx
+*/ +_At_(*ppSID, _Pre_ _Null_) +_When_(return != NO_ERROR, _At_(*ppSID, _Post_ _Null_)) +_When_(return == NO_ERROR, _At_(*ppSID, _Post_ _Notnull_)) +_Success_(return == NO_ERROR) +UINT32 HlprSIDGetForCurrentUser(_Outptr_result_bytebuffer_(*pSIDSize) SID** ppSID, + _Inout_ SIZE_T* pSIDSize) +{ + UINT32 status = NO_ERROR; + + if(ppSID && + pSIDSize) + { + PWSTR pUserName = 0; + + pUserName = PrvHlprUserNameGetCurrent(); + HLPR_BAIL_ON_NULL_POINTER_WITH_STATUS(pUserName, + status); + + status = HlprSIDCreate(ppSID, + pSIDSize, + pUserName, + SidTypeUser); + HLPR_BAIL_ON_FAILURE(status); + + HLPR_BAIL_LABEL: + + if(status != NO_ERROR) + { + HlprSIDDestroy(ppSID); + + *pSIDSize = 0; + } + + HLPR_DELETE_ARRAY(pUserName); + } + else + { + status = ERROR_INVALID_PARAMETER; + + HlprLogError(L"HlprSIDGetForCurrentUser() [status: %#x][ppSID: %#p][pSIDSize: %#p]", + status, + ppSID, + pSIDSize); + } + + return status; +} + +/** + @helper_function="HlprSIDGetWellKnown" + + Purpose: Allocate memory and populate with a SID of the Well Known type provided.
+
+ Notes: The caller is responsible for freeing any allocated memory using HlprSIDDestroy().
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/AA446585.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/AA379151.aspx
+*/ +_At_(*ppSID, _Pre_ _Null_) +_When_(return != NO_ERROR, _At_(*ppSID, _Post_ _Null_)) +_When_(return == NO_ERROR, _At_(*ppSID, _Post_ _Notnull_)) +_Success_(return == NO_ERROR) +UINT32 HlprSIDGetWellKnown(_In_ WELL_KNOWN_SID_TYPE sidType, + _Outptr_result_bytebuffer_(*pSIDSize) SID** ppSID, + _Inout_ UINT32* pSIDSize) +{ + UINT32 status = NO_ERROR; + + if(ppSID && + pSIDSize) + { + UINT32 sidSize = 0; + const UINT32 NUM_TRIES = 2; + + for(UINT32 index = 0; + index < NUM_TRIES; + index++) + { + BYTE* pBuffer = 0; + + if(!CreateWellKnownSid(sidType, + 0, + *ppSID, + (DWORD*)&sidSize)) + { + status = GetLastError(); + + if(status != ERROR_INSUFFICIENT_BUFFER || + sidSize == 0) + { + HlprLogError(L"HlprSIDGetWellKnown : CreateWellKnownSid() [status: %#x][sidSize: %d]", + status, + sidSize); + + HLPR_BAIL; + } + else + status = NO_ERROR; + + if(index == 0) + { + HLPR_NEW_ARRAY(pBuffer, + BYTE, + sidSize); + HLPR_BAIL_ON_ALLOC_FAILURE(pBuffer, + status); + + *ppSID = (SID*)pBuffer; + + *pSIDSize = sidSize; + } + } + else + break; + } + + if(*ppSID == 0 || + *pSIDSize == 0 || + !IsValidSid(*ppSID)) + { + status = ERROR_INVALID_SID; + + HlprLogError(L"HlprSIDGetWellKnown : IsValidSid() [status: %#x]", + status); + + HLPR_BAIL; + } + + status = NO_ERROR; + + HLPR_BAIL_LABEL: + + if(status != NO_ERROR) + { +#pragma warning(push) +#pragma warning(disable: 26000) /// no possible overflow + + HlprSIDDestroy(ppSID); + +#pragma warning(pop) + + *pSIDSize = 0; + } + } + else + { + status = ERROR_INVALID_PARAMETER; + + HlprLogError(L"HlprSIDGetWellKnown() [status: %#x][ppSID: %#p][pSIDSize: %#p]", + status, + ppSID, + pSIDSize); + } + + return status; +} + diff --git a/network/trans/WFPSampler/lib/HelperFunctions_SID.h b/network/trans/WFPSampler/lib/HelperFunctions_SID.h new file mode 100644 index 000000000..44f2f2cc4 --- /dev/null +++ b/network/trans/WFPSampler/lib/HelperFunctions_SID.h @@ -0,0 +1,54 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_SID.h +// +// Abstract: +// This module contains prototypes for functions which assist in actions pertaining to security +// identifiers (SIDs). +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef HELPERFUNCTIONS_SID_H +#define HELPERFUNCTIONS_SID_H + +_At_(*ppSID, _Pre_ _Maybenull_) +_At_(*ppSID, _Post_ _Null_) +_Success_(*ppSID == 0) +VOID HlprSIDDestroy(_Inout_ SID** ppSID); + +_At_(*ppSID, _Pre_ _Null_) +_When_(return != NO_ERROR, _At_(*ppSID, _Post_ _Null_)) +_When_(return == NO_ERROR, _At_(*ppSID, _Post_ _Notnull_)) +_Success_(return == NO_ERROR) +UINT32 HlprSIDCreate(_Outptr_result_bytebuffer_(*pSIDSize) SID** ppSID, + _Out_ SIZE_T* pSIDSize, + _In_opt_ PCWSTR pAccountName = 0, + _In_ UINT32 type = WinNullSid); + +_At_(*ppSID, _Pre_ _Null_) +_When_(return != NO_ERROR, _At_(*ppSID, _Post_ _Null_)) +_When_(return == NO_ERROR, _At_(*ppSID, _Post_ _Notnull_)) +_Success_(return == NO_ERROR) +UINT32 HlprSIDGetForCurrentUser(_Outptr_result_bytebuffer_(*pSIDSize) SID** ppSID, + _Inout_ SIZE_T* pSIDSize); + +_At_(*ppSID, _Pre_ _Null_) +_When_(return != NO_ERROR, _At_(*ppSID, _Post_ _Null_)) +_When_(return == NO_ERROR, _At_(*ppSID, _Post_ _Notnull_)) +_Success_(return == NO_ERROR) +UINT32 HlprSIDGetWellKnown(_In_ WELL_KNOWN_SID_TYPE sidType, + _Outptr_result_bytebuffer_(*pSIDSize) SID** ppSID, + _Inout_ UINT32* pSIDSize); + +#endif /// HELPERFUNCTIONS_SID_H \ No newline at end of file diff --git a/network/trans/WFPSampler/lib/HelperFunctions_Service.cpp b/network/trans/WFPSampler/lib/HelperFunctions_Service.cpp new file mode 100644 index 000000000..b04a5a590 --- /dev/null +++ b/network/trans/WFPSampler/lib/HelperFunctions_Service.cpp @@ -0,0 +1,508 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_Service.cpp +// +// Abstract: +// This module contains functions which assist actions pertaining to services. +// +// Naming Convention: +// +// +// +// i.e. +// +// +// { +// - Function is likely visible to other modules +// Prv - Function is private to this module. +// } +// +// { +// Hlpr - Function is from HelperFunctions_* Modules. +// } +// +// { +// Service - Function pertains to services. +// ServiceState - Function pertains to a service's state +// } +// +// { +// Control - Function sends a specified control to the object. +// Query - Function gets information about the object. +// Start - Function sends a stop control to the object. +// Stop - Function sends a stop control to the object. +// To - Function converts one value type to another. +// } +// +// { +// State - Funstion acts on the object's state. +// String - Function acts on a null terminated wide character string. +// } +// +// Private Functions: +// PrvHlprServiceControl(), +// PrvHlprServiceQueryState(), +// PrvHlprServiceStateToString(), +// +// Public Functions: +// HlprServiceStart(), +// HlprServiceStop() +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "HelperFunctions_Include.h" /// . + +/** + @helper_function="PrvHlprServiceQueryState" + + Purpose: Return the state of the service.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/MS685974.aspx
+*/ +_Success_(return == NO_ERROR) +UINT32 PrvHlprServiceQueryState(_In_ PCWSTR pServiceName, + _Inout_ UINT32* pServiceState, + _Inout_opt_ UINT32* pWait = 0) +{ + ASSERT(pServiceName); + ASSERT(pServiceState); + + UINT32 status = NO_ERROR; + SC_HANDLE scmHandle = 0; + SC_HANDLE svcHandle = 0; + UINT32 bytesRequired = 0; + SERVICE_STATUS_PROCESS srvStatus = {0}; + + scmHandle = OpenSCManager(0, + SERVICES_ACTIVE_DATABASE, + SC_MANAGER_CONNECT); + if(scmHandle == 0) + { + status = GetLastError(); + + HlprLogError(L"PrvHlprServiceQueryState: OpenSCManager() [status: %#x]", + status); + + HLPR_BAIL; + } + + svcHandle = OpenService(scmHandle, + pServiceName, + SERVICE_QUERY_STATUS); + if(svcHandle == 0) + { + status = GetLastError(); + + HlprLogError(L"PrvHlprServiceQueryState: OpenService() [status: %#x]", + status); + + HLPR_BAIL; + } + + if(!QueryServiceStatusEx(svcHandle, + SC_STATUS_PROCESS_INFO, + (BYTE*)&srvStatus, + sizeof(SERVICE_STATUS_PROCESS), + (DWORD*)&bytesRequired)) + { + status = GetLastError(); + + HlprLogError(L"PrvHlprServiceQueryState: QueryServiceStatus() [status: %#x]", + status); + + HLPR_BAIL; + } + else + { + status = NO_ERROR; + + *pServiceState = srvStatus.dwCurrentState; + + if(pWait) + *pWait = srvStatus.dwWaitHint; + } + + HLPR_BAIL_LABEL: + + if(status != NO_ERROR) + { + *pServiceState = 0; + + if(pWait) + *pWait = 0; + } + + HLPR_CLOSE_SERVICE_HANDLE(svcHandle); + + HLPR_CLOSE_SERVICE_HANDLE(scmHandle); + + return status; +} + +/** + @helper_function="PrvHlprServiceStateToString" + + Purpose: Return a string representation of the service state.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/MS685974.aspx
+*/ +_Success_(return != 0) +PCWSTR PrvHlprServiceStateToString(_In_ const UINT32 state) +{ + switch(state) + { + case SERVICE_STOPPED: + return L"SERVICE_STOPPED"; + case SERVICE_START_PENDING: + return L"SERVICE_START_PENDING"; + case SERVICE_STOP_PENDING: + return L"SERVICE_STOP_PENDING"; + case SERVICE_RUNNING: + return L"SERVICE_RUNNING"; + case SERVICE_CONTINUE_PENDING: + return L"SERVICE_CONTINUE_PENDING"; + case SERVICE_PAUSE_PENDING: + return L"SERVICE_PAUSE_PENDING"; + case SERVICE_PAUSED: + return L"SERVICE_PAUSED"; + } + + return L"Unknown"; +} + +/** + @helper_function="PrvHlprServiceControl" + + Purpose: Issue a control to the specified service.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/MS685974.aspx
+*/ +_Success_(return == NO_ERROR) +UINT32 PrvHlprServiceControl(_In_ PCWSTR pServiceName, + _In_ const HLPR_SERVICE_COMMAND command) +{ + ASSERT(pServiceName); + ASSERT(command < HLPR_SERVICE_COMMAND_MAX); + + UINT32 status = NO_ERROR; + UINT32 serviceState = SERVICE_STOPPED; + + status = PrvHlprServiceQueryState(pServiceName, + &serviceState); + + if(status == NO_ERROR && + ((command == HLPR_SERVICE_COMMAND_STOP && + serviceState != SERVICE_STOPPED) || + (command == HLPR_SERVICE_COMMAND_START && + serviceState != SERVICE_RUNNING))) + { + SC_HANDLE scmHandle = 0; + SC_HANDLE svcHandle = 0; + SERVICE_STATUS srvStatus = {0}; + + scmHandle = OpenSCManager(0, + SERVICES_ACTIVE_DATABASE, + SC_MANAGER_CONNECT); + if(scmHandle == 0) + { + status = GetLastError(); + + HlprLogError(L"PrvHlprServiceControl : OpenSCManager() [status: %#x]", + status); + + HLPR_BAIL; + } + + svcHandle = OpenService(scmHandle, + pServiceName, + SERVICE_START | SERVICE_STOP); + if(svcHandle == 0) + { + status = GetLastError(); + + HlprLogError(L"PrvHlprServiceControl : OpenService() [status: %#x]", + status); + + HLPR_BAIL; + } + + if(command == HLPR_SERVICE_COMMAND_START) + { + if(!StartService(svcHandle, + 0, + 0)) + { + status = GetLastError(); + + if(status != ERROR_SERVICE_ALREADY_RUNNING) + HlprLogError(L"HlprServiceStopStart: StartService() [status: %#x][Starting %s]", + status, + pServiceName); + } + } + else + { + if(!ControlService(svcHandle, + SERVICE_CONTROL_STOP, + &srvStatus)) + { + status = GetLastError(); + + if(status == NO_ERROR || + status == ERROR_INVALID_SERVICE_CONTROL || + status == ERROR_SERVICE_CANNOT_ACCEPT_CTRL || + status == ERROR_SERVICE_NOT_ACTIVE) + HlprLogError(L"PrvHlprServiceControl : ControlService() [status: %#x][Stopping %s][ServiceType: %#x][CurrentState: %s][ControlsAccepted: %#x][Win32ExitCode: %#x][ServiceSpecificExitCode: %#x][CheckPoint: %#x][WaitHint: %#x]", + status, + pServiceName, + srvStatus.dwServiceType, + PrvHlprServiceStateToString(srvStatus.dwCurrentState), + srvStatus.dwControlsAccepted, + srvStatus.dwWin32ExitCode, + srvStatus.dwServiceSpecificExitCode, + srvStatus.dwCheckPoint, + srvStatus.dwWaitHint); + else + HlprLogError(L"PrvHlprServiceControl : ControlService() [status: %#x][Stopping %s]", + status, + pServiceName); + + HLPR_BAIL; + } + } + + status = NO_ERROR; + + HLPR_BAIL_LABEL: + + HLPR_CLOSE_SERVICE_HANDLE(svcHandle); + + HLPR_CLOSE_SERVICE_HANDLE(scmHandle); + } + + return status; +} + +/** + @helper_function="HlprServiceNotificationRegister" + + Purpose: Register a notification function which is triggered by the service's transition in + state.
+
+ Notes: The caller is responsible for closing the service handle using + HLPR_CLOSE_SERVICE_HANDLE.
+
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/MS684276.aspx
+*/ +_When_(return != NO_ERROR, _At_(*pSCMHandle, _Post_ _Null_)) +_When_(return != NO_ERROR, _At_(*pSvcHandle, _Post_ _Null_)) +_When_(return == NO_ERROR, _At_(*pSCMHandle, _Post_ _Notnull_)) +_When_(return == NO_ERROR, _At_(*pSvcHandle, _Post_ _Notnull_)) +_Success_(return == NO_ERROR) +UINT32 HlprServiceNotificationStateChangeRegister(_In_ PCWSTR pServiceName, + _In_ SERVICE_NOTIFY* pSvcNotify, + _In_ UINT32 notifyMask, + _Out_ SC_HANDLE* pSCMHandle, + _Out_ SC_HANDLE* pSvcHandle) +{ + ASSERT(pServiceName); + ASSERT(pSvcNotify); + ASSERT(pSCMHandle); + ASSERT(pSvcHandle); + + UINT32 status = NO_ERROR; + + *pSCMHandle = 0; + + *pSvcHandle = 0; + + *pSCMHandle = OpenSCManager(0, + SERVICES_ACTIVE_DATABASE, + SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE); + if(*pSCMHandle == 0) + { + status = GetLastError(); + + HlprLogError(L"HlprServiceNotificationStateChangeRegister: OpenSCManager() [status: %#x]", + status); + + HLPR_BAIL; + } + + *pSvcHandle = OpenService(*pSCMHandle, + pServiceName, + SERVICE_QUERY_STATUS); + if(*pSvcHandle == 0) + { + status = GetLastError(); + + HlprLogError(L"HlprServiceNotificationStateChangeRegister: OpenService() [status: %#x]", + status); + + HLPR_BAIL; + } + + status = NotifyServiceStatusChange(*pSvcHandle, + notifyMask, + pSvcNotify); + if(status != NO_ERROR) + HlprLogError(L"HlprServiceNotificationStateChangeRegister: NotifyServiceStatusChange() [status: %#x]", + status); + + HLPR_BAIL_LABEL: + + if(status != NO_ERROR) + { + HLPR_CLOSE_SERVICE_HANDLE(*pSvcHandle); + + HLPR_CLOSE_SERVICE_HANDLE(*pSCMHandle); + } + + return status; +} + +/** + @helper_function="HlprServiceQueryState" + + Purpose: Return the state of the service.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_Success_(return != 0) +UINT32 HlprServiceQueryState(_In_ PCWSTR pServiceName) +{ + ASSERT(pServiceName); + + UINT32 serviceState = 0; + UINT32 wait = 0; + + PrvHlprServiceQueryState(pServiceName, + &serviceState, + &wait); + + return serviceState; +} + +/** + @helper_function="HlprServiceStart" + + Purpose: Issue a start control to the specified service.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 HlprServiceStart(_In_ PCWSTR pServiceName) +{ + ASSERT(pServiceName); + + UINT32 status = NO_ERROR; + const UINT64 MAX_WAIT_TIME = 5000; + UINT64 now = 0; + + status = PrvHlprServiceControl(pServiceName, + HLPR_SERVICE_COMMAND_START); + HLPR_BAIL_ON_FAILURE(status); + + for(UINT64 then = 0; + MAX_WAIT_TIME > now - then; + ) + { + UINT32 serviceState = SERVICE_STOPPED; + UINT32 wait = 0; + + if(then == 0) + then = GetTickCount64(); + + status = PrvHlprServiceQueryState(pServiceName, + &serviceState, + &wait); + HLPR_BAIL_ON_FAILURE(status); + + if(serviceState != SERVICE_RUNNING) + { + Sleep(wait); + + now = GetTickCount64(); + } + else + break; + } + + HLPR_BAIL_LABEL: + + return status; +} + +/** + @helper_function="HlprServiceStop" + + Purpose: Issue a stop control to the specified service.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 HlprServiceStop(_In_ PCWSTR pServiceName) +{ + ASSERT(pServiceName); + + UINT32 status = NO_ERROR; + const UINT64 MAX_WAIT_TIME = 5000; + UINT64 now = 0; + + status = PrvHlprServiceControl(pServiceName, + HLPR_SERVICE_COMMAND_STOP); + HLPR_BAIL_ON_FAILURE(status); + + for(UINT64 then = 0; + MAX_WAIT_TIME > now - then; + ) + { + UINT32 serviceState = SERVICE_RUNNING; + UINT32 wait = 0; + + if(then == 0) + then = GetTickCount64(); + + status = PrvHlprServiceQueryState(pServiceName, + &serviceState, + &wait); + HLPR_BAIL_ON_FAILURE(status); + + if(serviceState != SERVICE_STOPPED) + { + Sleep(wait); + + now = GetTickCount64(); + } + else + break; + } + + HLPR_BAIL_LABEL: + + return status; +} diff --git a/network/trans/WFPSampler/lib/HelperFunctions_Service.h b/network/trans/WFPSampler/lib/HelperFunctions_Service.h new file mode 100644 index 000000000..e6668ece9 --- /dev/null +++ b/network/trans/WFPSampler/lib/HelperFunctions_Service.h @@ -0,0 +1,53 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_Service.h +// +// Abstract: +// This module contains prototypes for functions which assist in actions pertaining to +// services. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef HELPERFUNCTIONS_SERVICE_H +#define HELPERFUNCTIONS_SERVICE_H + +typedef enum _HLPR_SERVICE_COMMAND_ +{ + HLPR_SERVICE_COMMAND_START, + HLPR_SERVICE_COMMAND_STOP, + HLPR_SERVICE_COMMAND_MAX +} HLPR_SERVICE_COMMAND; + +_When_(return != NO_ERROR, _At_(*pSCMHandle, _Post_ _Null_)) +_When_(return != NO_ERROR, _At_(*pSvcHandle, _Post_ _Null_)) +_When_(return == NO_ERROR, _At_(*pSCMHandle, _Post_ _Notnull_)) +_When_(return == NO_ERROR, _At_(*pSvcHandle, _Post_ _Notnull_)) +_Success_(return == NO_ERROR) +UINT32 HlprServiceNotificationStateChangeRegister(_In_ PCWSTR pServiceName, + _In_ SERVICE_NOTIFY* pSvcNotify, + _In_ UINT32 notifyMask, + _Out_ SC_HANDLE* pSCMHandle, + _Out_ SC_HANDLE* pSvcHandle); + +_Success_(return != 0) +UINT32 HlprServiceQueryState(_In_ PCWSTR pServiceName); + +_Success_(return == NO_ERROR) +UINT32 HlprServiceStart(_In_ PCWSTR pServiceName); + + +_Success_(return == NO_ERROR) +UINT32 HlprServiceStop(_In_ PCWSTR pServiceName); + +#endif /// HELPERFUNCTIONS_SERVICE_H \ No newline at end of file diff --git a/network/trans/WFPSampler/lib/HelperFunctions_Strings.cpp b/network/trans/WFPSampler/lib/HelperFunctions_Strings.cpp new file mode 100644 index 000000000..1d473c7d1 --- /dev/null +++ b/network/trans/WFPSampler/lib/HelperFunctions_Strings.cpp @@ -0,0 +1,196 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_Strings.cpp +// +// Abstract: +// This module contains functions which assist in actions pertaining to strings. +// +// Naming Convention: +// +// +// +// i.e. +// +// +// { +// - Function is likely visible to other modules +// } +// +// { +// Hlpr - Function is from HelperFunctions_* Modules. +// } +// +// { +// Strings - Function pertains to null terminated wide character strings. +// } +// +// { +// Are - Function compares values. +// } +// +// { +// Equal - Function determines equality between values. +// } +// +// Private Functions: +// +// Public Functions: +// HlprStringsAreEqual(), +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "HelperFunctions_Include.h" /// . + +/** + @helper_function="HlprStringsAreEqual" + + Purpose: Determine if two strings are identical.
+
+ Notes: Function is overloaded.
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/E0Z9K731.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/CHD90W8E.aspx
+*/ +BOOLEAN HlprStringsAreEqual(_In_reads_(stringSizeAlpha) PCWSTR pStringAlpha, + _In_ SIZE_T stringSizeAlpha, + _In_reads_(stringSizeOmega) PCWSTR pStringOmega, + _In_ SIZE_T stringSizeOmega, + _In_ BOOLEAN isCaseSensitive) /* FALSE */ +{ + BOOLEAN areEqual = FALSE; + + if(pStringAlpha && + pStringOmega) + { + if(stringSizeAlpha != stringSizeOmega) + HLPR_BAIL; + + if(pStringAlpha == pStringOmega) + areEqual = TRUE; + else + { + if(stringSizeAlpha != stringSizeOmega) + HLPR_BAIL; + + if(isCaseSensitive) + { + if(wcscmp(pStringAlpha, + pStringOmega)) + HLPR_BAIL; + } + else + { + if(_wcsnicmp(pStringAlpha, + pStringOmega, + stringSizeAlpha)) + HLPR_BAIL; + } + + areEqual = TRUE; + } + } + + HLPR_BAIL_LABEL: + + return areEqual; +} + +/** + @helper_function="HlprStringsAreEqual" + + Purpose: Determine if two strings are identical.
+
+ Notes: Function is overloaded.
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/MS647539.aspx
+*/ +BOOLEAN HlprStringsAreEqual(_In_opt_ PCWSTR pStringAlpha, + _In_opt_ PCWSTR pStringOmega, + _In_ BOOLEAN isCaseSensitive) /* FALSE */ +{ + BOOLEAN areEqual = FALSE; + + if(pStringAlpha && + pStringOmega) + { + UINT32 status = NO_ERROR; + size_t alphaSize = 0; + size_t omegaSize = 0; + + status = StringCchLength(pStringAlpha, + STRSAFE_MAX_CCH, + &alphaSize); + if(FAILED(status)) + { + HlprLogError(L"HlprStringsAreEqual : StringCchLength() [status = %#x]", + status); + + HLPR_BAIL; + } + + status = StringCchLength(pStringOmega, + STRSAFE_MAX_CCH, + &omegaSize); + if(FAILED(status)) + { + HlprLogError(L"HlprStringsAreEqual : StringCchLength() [status = %#x]", + status); + + HLPR_BAIL; + } + + areEqual = HlprStringsAreEqual(pStringAlpha, + alphaSize, + pStringOmega, + omegaSize, + isCaseSensitive); + } + + HLPR_BAIL_LABEL: + + return areEqual; +} + +/** + @helper_function="HlprStringsAreEqual" + + Purpose: Determine if two strings are identical.
+
+ Notes: Function is overloaded.
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA380518.aspx
+*/ +BOOLEAN HlprStringsAreEqual(_In_ const UNICODE_STRING* pUnicodeStringAlpha, + _In_ const UNICODE_STRING* pUnicodeStringOmega, + _In_ BOOLEAN isCaseSensitive) /* FALSE */ +{ + BOOLEAN areEqual = FALSE; + +#pragma warning(push) +#pragma warning(disable: 26018) /// constrined by UNICODE_STRING::Length + + if(pUnicodeStringAlpha && + pUnicodeStringAlpha->Length && + pUnicodeStringOmega && + pUnicodeStringOmega->Length) + areEqual = HlprStringsAreEqual(pUnicodeStringAlpha->Buffer, + pUnicodeStringAlpha->Length, + pUnicodeStringOmega->Buffer, + pUnicodeStringOmega->Length, + isCaseSensitive); + +#pragma warning(pop) + + return areEqual; +} diff --git a/network/trans/WFPSampler/lib/HelperFunctions_Strings.h b/network/trans/WFPSampler/lib/HelperFunctions_Strings.h new file mode 100644 index 000000000..be2162e48 --- /dev/null +++ b/network/trans/WFPSampler/lib/HelperFunctions_Strings.h @@ -0,0 +1,36 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_Strings.h +// +// Abstract: +// This module contains prototypes for functions which assist in actions pertaining to strings. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef HELPERFUNCTIONS_STRINGS_H +#define HELPERFUNCTIONS_STRINGS_H + +BOOLEAN HlprStringsAreEqual(_In_reads_(stringSizeAlpha) PCWSTR pStringAlpha, + _In_ SIZE_T stringSizeAlpha, + _In_reads_(stringSizeOmega) PCWSTR pStringOmega, + _In_ SIZE_T stringSizeOmega, + _In_ BOOLEAN isCaseSensitive = FALSE); +BOOLEAN HlprStringsAreEqual(_In_opt_ PCWSTR pStringAlpha, + _In_opt_ PCWSTR pStringOmega, + _In_ BOOLEAN isCaseSensitive = FALSE); +BOOLEAN HlprStringsAreEqual(_In_ const UNICODE_STRING* pUnicodeStringAlpha, + _In_ const UNICODE_STRING* pUnicodeStringOmega, + _In_ BOOLEAN isCaseSensitive = FALSE); + +#endif /// HELPERFUNCTIONS_STRINGS_H \ No newline at end of file diff --git a/network/trans/WFPSampler/lib/HelperFunctions_ThreadPools.cpp b/network/trans/WFPSampler/lib/HelperFunctions_ThreadPools.cpp new file mode 100644 index 000000000..eb2e35a07 --- /dev/null +++ b/network/trans/WFPSampler/lib/HelperFunctions_ThreadPools.cpp @@ -0,0 +1,179 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_ThreadPools.cpp +// +// Abstract: +// This module contains functions for assisting in operations pertaining to Thread Pools. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "HelperFunctions_Include.h" /// . + +/** + @helper_function="HlprThreadPoolDataPurge" + + Purpose: Cleanup a THREADPOOL_DATA object.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/MS682036.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/MS682033.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/MS682030.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/MS682576.aspx
+*/ +inline VOID HlprThreadPoolDataPurge(_Inout_ THREADPOOL_DATA* pThreadPoolData) +{ + ASSERT(pThreadPoolData); + + if(pThreadPoolData->pCleanupGroup) + { + CloseThreadpoolCleanupGroupMembers(pThreadPoolData->pCleanupGroup, + FALSE, + 0); + + CloseThreadpoolCleanupGroup(pThreadPoolData->pCleanupGroup); + } + + if(pThreadPoolData->pThreadPool) + CloseThreadpool(pThreadPoolData->pThreadPool); + + DestroyThreadpoolEnvironment(&(pThreadPoolData->callbackEnvironment)); + + ZeroMemory(pThreadPoolData, + sizeof(THREADPOOL_DATA)); + + return; +} + +/** + @helper_function="HlprThreadPoolDataDestroy" + + Purpose: Cleanup and free a THREADPOOL_DATA object.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_Success_(*ppThreadPoolData == 0) +inline VOID HlprThreadPoolDataDestroy(_Inout_ THREADPOOL_DATA** ppThreadPoolData) +{ + ASSERT(ppThreadPoolData); + + if(*ppThreadPoolData) + HlprThreadPoolDataPurge(*ppThreadPoolData); + + HLPR_DELETE(*ppThreadPoolData); + + return; +} + +/** + @helper_function="HlprThreadPoolDataPurge" + + Purpose: Cleanup a THREADPOOL_DATA object.
+
+ Notes: The caller is responsible for freeing any allocated memory using + HlprThreadPoolDataPurge().
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/MS683486.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/MS682456.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/MS682462.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/MS686261.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/MS686255.aspx
+ +*/ +_Success_(return == NO_ERROR) +UINT32 HlprThreadPoolDataPopulate(_Inout_ THREADPOOL_DATA* pThreadPoolData, + _In_ const PTP_CLEANUP_GROUP_CANCEL_CALLBACK pGroupCancelFn) +{ + ASSERT(pThreadPoolData); + + UINT32 status = NO_ERROR; + + InitializeThreadpoolEnvironment(&(pThreadPoolData->callbackEnvironment)); + + pThreadPoolData->pThreadPool = CreateThreadpool(0); + if(pThreadPoolData->pThreadPool == 0) + { + status = GetLastError(); + + HlprLogError(L"HlprThreadPoolDataPopulate : CreateThreadPool() [status: %#x]", + status); + + HLPR_BAIL; + } + + pThreadPoolData->pCleanupGroup = CreateThreadpoolCleanupGroup(); + if(pThreadPoolData->pCleanupGroup == 0) + { + status = GetLastError(); + + HlprLogError(L"HlprThreadPoolDataPopulate : CreateThreadPool() [status: %#x]", + status); + + HLPR_BAIL; + } + + SetThreadpoolCallbackPool(&(pThreadPoolData->callbackEnvironment), + pThreadPoolData->pThreadPool); + + SetThreadpoolCallbackCleanupGroup(&(pThreadPoolData->callbackEnvironment), + pThreadPoolData->pCleanupGroup, + pGroupCancelFn); + + HLPR_BAIL_LABEL: + + if(status != NO_ERROR) + HlprThreadPoolDataPurge(pThreadPoolData); + + return status; +} + +/** + @helper_function="HlprThreadPoolDataDestroy" + + Purpose: Allocate and populate a THREADPOOL_DATA object.
+
+ Notes: The caller is responsible for freeing any allocated memory using + HlprThreadPoolDataDestroy().
+
+ MSDN_Ref:
+*/ +_At_(*ppThreadPoolData, _Pre_ _Null_) +_When_(return != NO_ERROR, _At_(*ppThreadPoolData, _Post_ _Null_)) +_When_(return == NO_ERROR, _At_(*ppThreadPoolData, _Post_ _Notnull_)) +_Success_(return == NO_ERROR) +UINT32 HlprThreadPoolDataCreate(_Outptr_ THREADPOOL_DATA** ppThreadPoolData, + _In_ const PTP_CLEANUP_GROUP_CANCEL_CALLBACK pGroupCancelFn) +{ + ASSERT(ppThreadPoolData); + + UINT32 status = NO_ERROR; + + HLPR_NEW(*ppThreadPoolData, + THREADPOOL_DATA); + HLPR_BAIL_ON_ALLOC_FAILURE(*ppThreadPoolData, + status); + + status = HlprThreadPoolDataPopulate(*ppThreadPoolData, + pGroupCancelFn); + + HLPR_BAIL_LABEL: + + if(status != NO_ERROR) + HlprThreadPoolDataDestroy(ppThreadPoolData); + + + return status; +} diff --git a/network/trans/WFPSampler/lib/HelperFunctions_ThreadPools.h b/network/trans/WFPSampler/lib/HelperFunctions_ThreadPools.h new file mode 100644 index 000000000..1f1a6ad2d --- /dev/null +++ b/network/trans/WFPSampler/lib/HelperFunctions_ThreadPools.h @@ -0,0 +1,49 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_ThreadPools.h +// +// Abstract: +// This module contains prototypes of functions which assist in operations pertaining to +// Thread Pools. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef HELPERFUNCTIONS_THREAD_POOLS_H +#define HELPERFUNCTIONS_THREAD_POOLS_H + +typedef struct THREADPOOL_DATA_ +{ + TP_CALLBACK_ENVIRON callbackEnvironment; + TP_POOL* pThreadPool; + TP_CLEANUP_GROUP* pCleanupGroup; + +}THREADPOOL_DATA, *PTHREADPOOL_DATA; + +inline VOID HlprThreadPoolDataPurge(_Inout_ THREADPOOL_DATA* pThreadPoolData); + +_Success_(*ppThreadPoolData == 0) +inline VOID HlprThreadPoolDataDestroy(_Inout_ THREADPOOL_DATA** ppThreadPoolData); + +_Success_(return == NO_ERROR) +UINT32 HlprThreadPoolDataPopulate(_Inout_ THREADPOOL_DATA* pThreadPoolData, + _In_ const PTP_CLEANUP_GROUP_CANCEL_CALLBACK pGroupCancelFn); + +_At_(*ppThreadPoolData, _Pre_ _Null_) +_When_(return != NO_ERROR, _At_(*ppThreadPoolData, _Post_ _Null_)) +_When_(return == NO_ERROR, _At_(*ppThreadPoolData, _Post_ _Notnull_)) +_Success_(return == NO_ERROR) +UINT32 HlprThreadPoolDataCreate(_Outptr_ THREADPOOL_DATA** ppThreadPoolData, + _In_ const PTP_CLEANUP_GROUP_CANCEL_CALLBACK pGroupCancelFn); + +#endif /// HELPERFUNCTIONS_THREAD_POOLS_H diff --git a/network/trans/WFPSampler/lib/HelperFunctions_ThreadsAndEvents.cpp b/network/trans/WFPSampler/lib/HelperFunctions_ThreadsAndEvents.cpp new file mode 100644 index 000000000..89c7a5206 --- /dev/null +++ b/network/trans/WFPSampler/lib/HelperFunctions_ThreadsAndEvents.cpp @@ -0,0 +1,389 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_ThreadsAndEvents.cpp +// +// Abstract: +// This module contains functions which functions which simplify threads and eventing. +// +// Naming Convention: +// +// +// +// i.e. +// +// +// { +// - Function is likely visible to other modules +// } +// +// { +// Hlpr - Function is from HelperFunctions_* Modules. +// } +// +// { +// Event - Function pertains to events. +// Thread - Function pertains to threads. +// } +// +// { +// Cleanup - +// Set - +// Start - +// Stop - +// Wait - +// } +// +// { +// ForCompletion - +// ForEvent - Function determines equality between values. +// } +// +// Private Functions: +// +// Public Functions: +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "HelperFunctions_Include.h" /// . + +/** + @helper_function="HlprEventReset" + + Purpose: Sets the event to nonsignaled.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-Us/Library/Windows/Desktop/MS685081.aspx
+*/ +VOID HlprEventReset(_In_opt_ HANDLE event) +{ + if(event) + ResetEvent(event); + + return; +} + + +/** + @helper_function="HlprEventSet" + + Purpose: Signals an event.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-Us/Library/Windows/Desktop/MS686211.aspx
+*/ +VOID HlprEventSet(_In_opt_ HANDLE event) +{ + if(event) + SetEvent(event); + + return; +} + +/** + @helper_function="HlprThreadStart" + + Purpose: Generates a new thread.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-Us/Library/Windows/Desktop/MS682396.aspx
+ HTTP://MSDN.Microsoft.com/En-Us/Library/Windows/Desktop/MS682453.aspx
+*/ +_Success_(return == NO_ERROR) +UINT32 HlprThreadStart(_Inout_ THREAD_DATA* pThreadData, + _In_opt_ VOID* pData) /* 0 */ +{ + UINT32 status = NO_ERROR; + + if(pThreadData) + { + HlprThreadCleanup(pThreadData); + + if(pThreadData->threadStartRoutine) + { + pThreadData->threadStartEvent = CreateEvent(0, + TRUE, + FALSE, + 0); + + pThreadData->threadStopEvent = CreateEvent(0, + TRUE, + FALSE, + 0); + + pThreadData->threadContinueEvent = CreateEvent(0, + TRUE, + FALSE, + 0); + + pThreadData->thread = CreateThread(0, + 0, + (LPTHREAD_START_ROUTINE)pThreadData->threadStartRoutine, + pData ? pData : pThreadData, + 0, + (LPDWORD)&(pThreadData->threadId)); + + if(pThreadData->threadStartEvent && + pThreadData->threadStopEvent && + pThreadData->threadContinueEvent && + pThreadData->thread) + HlprThreadWaitForEvent(pThreadData->threadStartEvent, + pThreadData); + else + { + status = ERROR_GEN_FAILURE; + + HlprThreadCleanup(pThreadData); + + HlprLogError(L"HlprThreadStart() [status: %#x]", + status); + } + } + else + { + status = ERROR_INVALID_DATA; + + HlprLogError(L"HlprThreadStart() [status: %#x][pThreadData->threadStartRoutine: %#x]", + status, + pThreadData->threadStartRoutine); + } + } + else + { + status = ERROR_INVALID_PARAMETER; + + HlprLogError(L"HlprThreadStart() [status: %#x][pThread: %#x]", + status, + pThreadData); + } + + return status; +} + +/** + @helper_function="HlprThreadStop" + + Purpose: Signals a thread to stop and cleans it up.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-Us/Library/Windows/Desktop/MS682396.aspx

+*/ +_Success_(return == NO_ERROR) +UINT32 HlprThreadStop(_Inout_ THREAD_DATA* pThreadData) +{ + UINT32 status = NO_ERROR; + const UINT32 RETRY_ATTEMPTS = 5; + + if(pThreadData) + { + for(UINT32 i = 0; + status != WAIT_TIMEOUT && + i < RETRY_ATTEMPTS; + i++) + { + HlprEventSet(pThreadData->threadStopEvent); + + status = HlprThreadWaitForCompletion(pThreadData); + } + + HlprThreadCleanup(pThreadData); + } + + return status; +} + +/** + @helper_function="HlprThreadStop" + + Purpose: Signals a thread to stop and cleans it up, and frees any allocated memory.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_At_(*ppThreadData, _Pre_ _Notnull_) +_When_(return != NO_ERROR, _At_(*ppThreadData, _Post_ _Notnull_)) +_When_(return == NO_ERROR, _At_(*ppThreadData, _Post_ _Null_)) +_Success_(return == NO_ERROR && *ppThreadData == 0) +UINT32 HlprThreadStop(_Inout_ THREAD_DATA** ppThreadData) +{ + UINT32 status = NO_ERROR; + + if(ppThreadData) + { + status = HlprThreadStop(*ppThreadData); + if(status == NO_ERROR) + { + HLPR_DELETE(*ppThreadData) + } + } + + return status; +} + +/** + @helper_function="HlprThreadCleanup" + + Purpose: Cleans up a previously allocated thread.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-Us/Library/Windows/Desktop/MS683190.aspx
+ HTTP://MSDN.Microsoft.com/En-Us/Library/Windows/Desktop/MS686717.aspx
+*/ +VOID HlprThreadCleanup(_Inout_opt_ THREAD_DATA* pThreadData) +{ + if(pThreadData) + { + LPTHREAD_START_ROUTINE threadStartRoutine = pThreadData->threadStartRoutine; + UINT32 status = NO_ERROR; + + if(pThreadData->thread) + { + if(GetExitCodeThread(pThreadData->thread, + (DWORD*)&status)) + { + if(status == STILL_ACTIVE) + { + UINT64 timeAlpha = GetTickCount64(); + const UINT32 FOUR_MINUTES = 240000; + + for(UINT64 timeOmega = GetTickCount64(); + timeOmega - timeAlpha < FOUR_MINUTES; + timeOmega = GetTickCount64()) + { + HlprEventSet(pThreadData->threadStopEvent); + + status = HlprThreadWaitForCompletion(pThreadData); + if(status == NO_ERROR) + break; + } + + if(status != NO_ERROR) + { + HlprLogInfo(L"Possible Runaway Thread"); + +#pragma warning(push) +#pragma warning(disable: 6258) /// This is a last resort + + TerminateThread(pThreadData->thread, + ERROR_THREAD_WAS_SUSPENDED); + +#pragma warning(pop) + } + } + } + else + { + status = GetLastError(); + + HlprLogError(L"HlprThreadCleanup() [status:%#x]", + status); + } + } + + HLPR_CLOSE_HANDLE(pThreadData->threadStopEvent); + + HLPR_CLOSE_HANDLE(pThreadData->threadStartEvent); + + HLPR_CLOSE_HANDLE(pThreadData->threadContinueEvent); + + ZeroMemory(pThreadData, + sizeof(THREAD_DATA)); + + pThreadData->threadStartRoutine = threadStartRoutine; + } + + return; +} + +/** + @helper_function="HlprThreadWaitForCompletion" + + Purpose: Waits for a thread to complete.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 HlprThreadWaitForCompletion(_Inout_opt_ THREAD_DATA* pThreadData) +{ + UINT32 status = NO_ERROR; + + if(pThreadData) + { + HlprEventSet(pThreadData->threadStopEvent); + + status = HlprThreadWaitForEvent(pThreadData->thread, + pThreadData); + } + + return status; +} + +/** + @helper_function="HlprThreadWaitForEvent" + + Purpose: Waits for a particular event to be set within the thread.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 HlprThreadWaitForEvent(_In_ HANDLE eventHandle, + _In_ THREAD_DATA* pThreadData) +{ + UINT32 status = NO_ERROR; + const UINT32 RETRY_ATTEMPTS = 6; + WCHAR* pEventName = 0; + + if(eventHandle && + pThreadData) + { + if(eventHandle == pThreadData->threadContinueEvent) + pEventName = L"Continue Event"; + else if(eventHandle == pThreadData->threadStartEvent) + pEventName = L"Start Event"; + else if(eventHandle == pThreadData->threadStopEvent) + pEventName = L"Stop Event"; + else if(eventHandle == pThreadData->thread) + pEventName = L"Thread"; + + /// Try for 30 seconds before bailing + for(UINT32 i = 0; + WaitForSingleObject(eventHandle, + 5000) == WAIT_TIMEOUT; + i++) + { + HlprLogInfo(L"HlprThreadWaitForEvent() Waiting for %s ...", + pEventName); + + if(i == RETRY_ATTEMPTS - 1) + { + status = WAIT_TIMEOUT; + + HlprLogInfo(L"HlprThreadWaitForEvent() [status: %#x]"); + + break; + } + } + } + + return status; +} diff --git a/network/trans/WFPSampler/lib/HelperFunctions_ThreadsAndEvents.h b/network/trans/WFPSampler/lib/HelperFunctions_ThreadsAndEvents.h new file mode 100644 index 000000000..cca27328a --- /dev/null +++ b/network/trans/WFPSampler/lib/HelperFunctions_ThreadsAndEvents.h @@ -0,0 +1,63 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_ThreadsAndEvents.cpp +// +// Abstract: +// This module contains prototypes for functions which simplify threads and eventing. +// +// Exported Functions: +// +// Internal Functions: +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef HELPERFUNCTIONS_THREADS_AND_EVENTS_H +#define HELPERFUNCTIONS_THREADS_AND_EVENTS_H + +typedef struct THREAD_DATA_ +{ + LPTHREAD_START_ROUTINE threadStartRoutine; + UINT32 threadId; + HANDLE thread; + HANDLE threadStartEvent; + HANDLE threadStopEvent; + HANDLE threadContinueEvent; +}THREAD_DATA, *PTHREAD_DATA; + +VOID HlprEventReset(_In_opt_ HANDLE event); + +VOID HlprEventSet(_In_opt_ HANDLE event); + +_Success_(return == NO_ERROR) +UINT32 HlprThreadStart(_Inout_ THREAD_DATA* pThreadData, + _In_opt_ VOID* pData = 0); + +_Success_(return == NO_ERROR) +UINT32 HlprThreadStop(_Inout_ THREAD_DATA* pThreadData); +_At_(*ppThreadData, _Pre_ _Notnull_) +_When_(return != NO_ERROR, _At_(*ppThreadData, _Post_ _Notnull_)) +_When_(return == NO_ERROR, _At_(*ppThreadData, _Post_ _Null_)) +_Success_(return == NO_ERROR && *ppThreadData == 0) +UINT32 HlprThreadStop(_Inout_ THREAD_DATA** ppThreadData); + +VOID HlprThreadCleanup(_Inout_opt_ THREAD_DATA* pThreadData); + +_Success_(return == NO_ERROR) +UINT32 HlprThreadWaitForCompletion(_Inout_opt_ THREAD_DATA* pThreadData); + +_Success_(return == NO_ERROR) +UINT32 HlprThreadWaitForEvent(_In_ HANDLE eventHandle, + _In_ THREAD_DATA* pThreadData); + +#endif /// HELPERFUNCTIONS_THREADS_AND_EVENTS_H \ No newline at end of file diff --git a/network/trans/WFPSampler/lib/HelperFunctions_WinSock.cpp b/network/trans/WFPSampler/lib/HelperFunctions_WinSock.cpp new file mode 100644 index 000000000..2ff097377 --- /dev/null +++ b/network/trans/WFPSampler/lib/HelperFunctions_WinSock.cpp @@ -0,0 +1,999 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_WinSock.cpp +// +// Abstract: +// This module contains functions for assisting in operations pertaining to Windows Sockets. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "HelperFunctions_Include.h" /// . + +/** + @helper_function="HlprWinSockCleanup" + + Purpose: Terminates use of the Winsock DLL for the process's use.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/MS741549.aspx
+*/ +_Success_(return == NO_ERROR) +UINT32 HlprWinSockCleanup() +{ + UINT32 status = NO_ERROR; + + status = WSACleanup(); + if(status != NO_ERROR) + { + status = WSAGetLastError(); + + HlprLogError(L"HlprWinSockCleanup : WSACleanup() [status: %#x]", + status); + } + + return status; +} + +/** + @helper_function="HlprWinSockInitialize" + + Purpose: Initializes use of the Winsock DLL for the process's use.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/MS742213.aspx
+*/ +_Success_(return == NO_ERROR) +UINT32 HlprWinSockInitialize() +{ + UINT32 status = NO_ERROR; + UINT16 versionRequested = MAKEWORD(2, + 2); + WSADATA wsaData = {0}; + + status = WSAStartup(versionRequested, + &wsaData); + if(status != NO_ERROR) + HlprLogError(L"HlprWinSockInitialize : WSAStartup() [status: %#x]", + status); + + return status; +} + +/** + @helper_function="HlprWinSockDestroyWSAEvent" + + Purpose: Free a WSAEvent.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/MS741551.aspx
+*/ +_Success_(return == NO_ERROR) +UINT32 HlprWinSockDestroyWSAEvent(_Inout_ WSAEVENT* pWSAEvent) +{ + ASSERT(pWSAEvent); + + UINT32 status = NO_ERROR; + + if(!WSACloseEvent(*pWSAEvent)) + { + status = WSAGetLastError(); + + HlprLogError(L"HlprWinSockDestroyWSAEvent : WSACloseEvent() [status: %#x]", + status); + } + else + *pWSAEvent = WSA_INVALID_EVENT; + + return status; +} + +/** + @helper_function="HlprWinSockSetWSAEvent" + + Purpose: Set a WSAEvent to signaled.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/MS742208.aspx
+*/ +_Success_(return == NO_ERROR) +UINT32 HlprWinSockSetWSAEvent(_In_ WSAEVENT wsaEvent) +{ + UINT32 status = NO_ERROR; + + if(!WSASetEvent(wsaEvent)) + { + status = WSAGetLastError(); + + HlprLogError(L"HlprWinSockSetWSAEvent : WSASetEvent() [status: %#x]", + status); + } + + return status; +} + +/** + @helper_function="HlprWinSockCreateWSAEvent" + + Purpose: Create a WSAEvent.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/MS741561.aspx
+*/ +_Success_(return == NO_ERROR) +UINT32 HlprWinSockCreateWSAEvent(_Inout_ WSAEVENT* pWSAEvent) +{ + ASSERT(pWSAEvent); + + UINT32 status = NO_ERROR; + + *pWSAEvent = WSACreateEvent(); + if(*pWSAEvent == WSA_INVALID_EVENT) + { + status = WSAGetLastError(); + + HlprLogError(L"HlprWinSockCreateWSAEvent : WSACreateEvent() [status: %#x]", + status); + } + + return status; +} + +/** + @helper_function="HlprWinSockDestroySocket" + + Purpose: Destroy a socket gracefully.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/MS740481.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/MS737582.aspx
+*/ +_Success_(return == NO_ERROR) +UINT32 HlprWinSockDestroySocket(_Inout_ SOCKET* pSocket) +{ + ASSERT(pSocket); + + UINT32 status = NO_ERROR; + + if(*pSocket != INVALID_SOCKET) + { + status = shutdown(*pSocket, + SD_BOTH); + if(status != NO_ERROR) + { + status = WSAGetLastError(); + + HlprLogError(L"HlprWinSockDestroySocket : shutdown() [status: %#x]", + status); + + HLPR_BAIL; + } + + status = closesocket(*pSocket); + if(status != NO_ERROR) + { + status = WSAGetLastError(); + + HlprLogError(L"HlprWinSockDestroySocket : shutdown() [status: %#x]", + status); + + HLPR_BAIL; + } + + *pSocket = INVALID_SOCKET; + } + + HLPR_BAIL_LABEL: + + return status; +} + +/** + @helper_function="HlprWinSockCreateSocket" + + Purpose: Create a new socket.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/MS742212.aspx
+*/ +_Success_(return == NO_ERROR) +UINT32 HlprWinSockCreateSocket(_In_ SOCKADDR_STORAGE* pSockAddrStorage, + _In_ UINT8 protocol, + _Inout_ SOCKET* pSocket) +{ + ASSERT(pSockAddrStorage); + ASSERT(pSocket); + + UINT32 status = NO_ERROR; + UINT32 type = SOCK_RAW; + + if(protocol == IPPROTO_TCP) + type = SOCK_STREAM; + else if(protocol == IPPROTO_UDP) + type = SOCK_DGRAM; + + *pSocket = WSASocket(pSockAddrStorage->ss_family, + type, + protocol, + 0, + 0, + WSA_FLAG_OVERLAPPED); + if(*pSocket == INVALID_SOCKET) + { + status = WSAGetLastError(); + + HlprLogError(L"HlprWinSockCreateSocket : WSASocket() [status:%#x]", + status); + } + + return status; +} + +/** + @helper_function="HlprWinSockSetAbortiveDisconnect" + + Purpose: Set the linger socket option to FALSE.
+
+ Notes: This function needs to be called prior to HlprWinSockDestroySocket for stream + sockets that get a WSAECONNRESET.
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/MS740476.aspx
+*/ +_Success_(return == NO_ERROR) +UINT32 HlprWinSockSetAbortiveDisconnect(_In_ SOCKET abortedSocket) +{ + ASSERT(abortedSocket != INVALID_SOCKET); + + UINT32 status = NO_ERROR; + LINGER linger = {0}; + + linger.l_onoff = TRUE; + linger.l_linger = 0; + + status = setsockopt(abortedSocket, + SOL_SOCKET, + SO_LINGER, + (char*)&linger, + sizeof(LINGER)); + if(status != NO_ERROR) + { + status = WSAGetLastError(); + + HlprLogError(L"HlprWinSockSetAbortiveDisconnect : setsockopt() [status: %#x]", + status); + } + + return status; +} + +/** + @helper_function="HlprWinSockSetSocketReceiveTimeout" + + Purpose: Specify a timeout for blocking receive calls.
+
+ Notes: This function needs to be called prior to binding the socket.
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/MS740476.aspx
+*/ +_Success_(return == NO_ERROR) +UINT32 HlprWinSockSetSocketReceiveTimeout(_In_ SOCKET receivingSocket, + _In_ UINT32 timeout) /* 5000 */ +{ + ASSERT(receivingSocket != INVALID_SOCKET); + + UINT32 status = NO_ERROR; + + status = setsockopt(receivingSocket, + SOL_SOCKET, + SO_RCVTIMEO, + (char*)&timeout, + sizeof(UINT32)); + if(status != NO_ERROR) + { + status = WSAGetLastError(); + + HlprLogError(L"HlprWinSockSetSocketReceiveTimeout : setsockopt() [status: %#x]", + status); + } + + return status; +} + +/** + @helper_function="HlprWinSockSetSocketSendTimeout" + + Purpose: Specify a timeout for blocking send calls.
+
+ Notes: This function needs to be called prior to binding the socket.
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/MS740476.aspx
+*/ +_Success_(return == NO_ERROR) +UINT32 HlprWinSockSetSocketSendTimeout(_In_ SOCKET sendingSocket, + _In_ UINT32 timeout) /* 5000 */ +{ + ASSERT(sendingSocket != INVALID_SOCKET); + + UINT32 status = NO_ERROR; + + status = setsockopt(sendingSocket, + SOL_SOCKET, + SO_SNDTIMEO, + (char*)&timeout, + sizeof(UINT32)); + if(status != NO_ERROR) + { + status = WSAGetLastError(); + + HlprLogError(L"HlprWinSockSetSocketSendTimeout : setsockopt() [status: %#x]", + status); + } + + return status; +} + + +/** + @helper_function="HlprWinSockSetSocketNonBlocking" + + Purpose: Enable non-blocking mode on the socket.
+
+ Notes: This function needs to be called prior to binding the socket.
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/MS741621.aspx
+*/ +_Success_(return == NO_ERROR) +UINT32 HlprWinSockSetSocketNonBlocking(_In_ SOCKET unboundSocket) +{ + ASSERT(unboundSocket != INVALID_SOCKET); + + UINT32 status = NO_ERROR; + UINT32 isNonBlockingSocket = TRUE; + UINT32 bytesReturned = 0; + + status = WSAIoctl(unboundSocket, + FIONBIO, + &isNonBlockingSocket, + sizeof(UINT32), + 0, + 0, + (LPDWORD)&bytesReturned, + 0, + 0); + if(status != NO_ERROR) + { + status = WSAGetLastError(); + + HlprLogError(L"HlprWinSockSetSocketNonBlocking : WSAIoctl() [status: %#x]", + status); + } + + return status; +} + +/** + @helper_function="HlprWinSockAcquirePortReservationForSocket" + + Purpose: Request a runtime reservation for the provided ports.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/GG699720.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/MS741621.aspx
+*/ +_Success_(return == NO_ERROR) +UINT32 HlprWinSockAcquirePortReservationForSocket(_In_ SOCKET unboundSocket, + _In_ INET_PORT_RANGE* pPortRange, + _Inout_ UINT64* pReservationToken) +{ + ASSERT(unboundSocket != INVALID_SOCKET); + ASSERT(pPortRange); + ASSERT(pReservationToken); + + UINT32 status = NO_ERROR; + UINT32 bytesReturned = 0; + INET_PORT_RESERVATION_INSTANCE reservation = {0}; + + status = WSAIoctl(unboundSocket, + SIO_ACQUIRE_PORT_RESERVATION, + pPortRange, + sizeof(INET_PORT_RANGE), + &reservation, + sizeof(INET_PORT_RESERVATION_INSTANCE), + (LPDWORD)&bytesReturned, + 0, + 0); + if(status != NO_ERROR) + { + status = WSAGetLastError(); + + HlprLogError(L"HlprWinSockAssociatePortReservationWithSocket : WSAIoctl() [status: %#x]", + status); + } + else + *pReservationToken = reservation.Token.Token; + + return status; +} + +/** + @helper_function="HlprWinSockAssociatePortReservationWithSocket" + + Purpose: Associate the socket with the persistent port(s) that were reserved previously.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/GG699721.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/MS741621.aspx
+*/ +_Success_(return == NO_ERROR) +UINT32 HlprWinSockAssociatePortReservationWithSocket(_In_ SOCKET unboundSocket, + _In_ UINT64 reservationToken) +{ + ASSERT(unboundSocket != INVALID_SOCKET); + + UINT32 status = NO_ERROR; + UINT32 bytesReturned = 0; + + status = WSAIoctl(unboundSocket, + SIO_ASSOCIATE_PORT_RESERVATION, + &reservationToken, + sizeof(UINT64), + 0, + 0, + (LPDWORD)&bytesReturned, + 0, + 0); + if(status != NO_ERROR) + { + status = WSAGetLastError(); + + HlprLogError(L"HlprWinSockAssociatePortReservationWithSocket : WSAIoctl() [status: %#x]", + status); + } + + return status; +} + +/** + @private_function="HlprWinSockQueryPortReservation" + + Purpose: Find the token for the previously created port reservations.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/GG696072.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/GG696063.aspx
+*/ +_Success_(return == NO_ERROR) +UINT32 HlprWinSockQueryPortReservation(_In_ UINT8 ipProtocol, + _In_ INET_PORT_RANGE* pPortRange, + _Inout_ UINT64* pReservationToken) +{ + ASSERT(ipProtocol != IPPROTO_TCP && + ipProtocol != IPPROTO_UDP); + ASSERT(pPortRange); + ASSERT(pReservationToken); + + UINT32 status = NO_ERROR; + + if(ipProtocol == IPPROTO_TCP) + status = LookupPersistentTcpPortReservation(htons(pPortRange->StartPort), + pPortRange->NumberOfPorts, + pReservationToken); + else + status = LookupPersistentUdpPortReservation(htons(pPortRange->StartPort), + pPortRange->NumberOfPorts, + pReservationToken); + + if(status != NO_ERROR) + { + if(status == ERROR_NOT_FOUND) + HlprLogInfo(L"HlprWinSockQueryPortReservation : LookupPersistent%sPortReservation() [status: %#x]", + ipProtocol == IPPROTO_TCP ? L"Tcp" : L"Udp", + status); + else + HlprLogError(L"HlprWinSockQueryPortReservation : LookupPersistent%sPortReservation() [status: %#x]", + ipProtocol == IPPROTO_TCP ? L"Tcp" : L"Udp", + status); + + *pReservationToken = 0; + } + + return status; +} + +/** + @private_function="HlprWinSockDestroyPortReservation" + + Purpose: Removes the reservation of specific ports for the Proxy service's use.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/GG696070.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/GG696071.aspx
+*/ +_Success_(return == NO_ERROR) +UINT32 HlprWinSockDestroyPortReservation(_In_ UINT8 ipProtocol, + _In_ INET_PORT_RANGE* pPortRange, + _Inout_opt_ UINT64* pReservationToken) /* 0 */ +{ + ASSERT(ipProtocol != IPPROTO_TCP && + ipProtocol != IPPROTO_UDP); + ASSERT(pPortRange); + + UINT32 status = NO_ERROR; + + if(ipProtocol == IPPROTO_TCP) + status = DeletePersistentTcpPortReservation(htons(pPortRange->StartPort), + pPortRange->NumberOfPorts); + else + status = DeletePersistentUdpPortReservation(htons(pPortRange->StartPort), + pPortRange->NumberOfPorts); + + if(status == NO_ERROR) + { + if(pReservationToken) + *pReservationToken = 0; + } + else if(status == ERROR_NOT_FOUND) + { + status = NO_ERROR; + + if(pReservationToken) + *pReservationToken = 0; + } + else + HlprLogError(L"HlprWinSockDestroyPortReservation : DeletePersistent%sPortReservation() [status: %#x]", + ipProtocol == IPPROTO_TCP ? L"Tcp" : L"Udp", + status); + + return status; +} + +/** + @private_function="HlprWinSockCreatePortReservation" + + Purpose: Reserves specific ports for the Proxy service's use.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/GG696068.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/GG696069.aspx
+*/ +_Success_(return == NO_ERROR) +UINT32 HlprWinSockCreatePortReservation(_In_ UINT8 ipProtocol, + _In_ INET_PORT_RANGE* pPortRange, + _Inout_ UINT64* pReservationToken) +{ + ASSERT(ipProtocol != IPPROTO_TCP && + ipProtocol != IPPROTO_UDP); + ASSERT(pPortRange); + ASSERT(pReservationToken); + + UINT32 status = NO_ERROR; + + if(ipProtocol == IPPROTO_TCP) + status = CreatePersistentTcpPortReservation(htons(pPortRange->StartPort), + pPortRange->NumberOfPorts, + pReservationToken); + else + status = CreatePersistentUdpPortReservation(htons(pPortRange->StartPort), + pPortRange->NumberOfPorts, + pReservationToken); + + if(status != NO_ERROR) + { + HlprLogError(L"HlprWinSockCreatePortReservation : CreatePersistent%sPortReservation() [status: %#x]", + ipProtocol == IPPROTO_TCP ? L"Tcp" : L"Udp", + status); + + *pReservationToken = 0; + } + + return status; +} + +#if(NTDDI_VERSION >= NTDDI_WIN8) + +/** + @helper_function="HlprWinSockAssociateConnectionRedirectRecordsAndContextWithProxySocket" + + Purpose: Retrieve the original REDIRECT_RECORDS from the original socket and associate them + with the new (proxy) socket.
+
+ Notes: This function needs to be called prior to binding the proxied socket.
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/MS741621.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/HH802472.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/HH802473.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/HH802474.aspx
+ +*/ +_Success_(return == NO_ERROR) +UINT32 HlprWinSockAssociateConnectionRedirectRecordsAndContextWithProxySocket(_In_ SOCKET originalSocket, + _Inout_ SOCKET proxySocket, + _Inout_ SOCKADDR_STORAGE* pProxyPeerSockAddrStorage, + _Inout_opt_ SOCKADDR_STORAGE* pOriginSockAddrStorage) /* 0 */ +{ + ASSERT(originalSocket != INVALID_SOCKET); + ASSERT(proxySocket != INVALID_SOCKET); + ASSERT(pProxyPeerSockAddrStorage); + + UINT32 status = NO_ERROR; + const SIZE_T REDIRECT_RECORDS_SIZE = 8192; + const SIZE_T REDIRECT_CONTEXT_SIZE = sizeof(SOCKADDR_STORAGE) * 2; + BYTE* pRedirectRecords = 0; + BYTE* pRedirectContext = 0; + SIZE_T redirectRecordsSize = 0; + SIZE_T redirectContextSize = 0; + SIZE_T outputSize = 0; + + HLPR_NEW_ARRAY(pRedirectRecords, + BYTE, + REDIRECT_RECORDS_SIZE); + HLPR_BAIL_ON_ALLOC_FAILURE(pRedirectRecords, + status); + + HLPR_NEW_ARRAY(pRedirectContext, + BYTE, + REDIRECT_CONTEXT_SIZE); + HLPR_BAIL_ON_ALLOC_FAILURE(pRedirectContext, + status); + + /// Retrieve the REDIRECT_RECORDS from the client socket. + status = WSAIoctl(originalSocket, + SIO_QUERY_WFP_CONNECTION_REDIRECT_RECORDS, + 0, + 0, + pRedirectRecords, + REDIRECT_RECORDS_SIZE, + (LPDWORD)&redirectRecordsSize, + 0, + 0); + if(status != NO_ERROR) + { + status = WSAGetLastError(); + + HlprLogError(L"HlprWinSockAssociateConnectionRedirectRecordsAndContextWithProxySocket : WSAIoctl() [status: %#x]", + status); + + HLPR_BAIL; + } + + /// Retrieve the REDIRECT_CONTEXT from the client socket. + status = WSAIoctl(originalSocket, + SIO_QUERY_WFP_CONNECTION_REDIRECT_CONTEXT, + 0, + 0, + pRedirectContext, + REDIRECT_CONTEXT_SIZE, + (LPDWORD)&redirectContextSize, + 0, + 0); + if(status != NO_ERROR) + { + status = WSAGetLastError(); + + if(status != FWP_E_NOT_FOUND) + { + HlprLogError(L"HlprWinSockAssociateConnectionRedirectRecordsAndContextWithProxySocket : WSAIoctl() [status: %#x]", + status); + + HLPR_BAIL; + } + } + else + { + ASSERT(redirectContextSize == REDIRECT_CONTEXT_SIZE); + + /// The context passed in WFPSamplerCalloutDriver.sys is the SOCKADDR_STORAGE info of the + /// classified peer endpoint ... + RtlCopyMemory(pProxyPeerSockAddrStorage, + pRedirectContext, + sizeof(SOCKADDR_STORAGE)); + + /// ... and the SOCK_ADDR_STORAGE of the classified origin endpoint. + if(pOriginSockAddrStorage) + { + UINT32 contextOriginOffset = sizeof(SOCKADDR_STORAGE); + + RtlCopyMemory(pOriginSockAddrStorage, + &(pRedirectContext[contextOriginOffset]), + sizeof(SOCKADDR_STORAGE)); + } + } + + /// Attach the REDIRECT_RECORDS to the proxied socket + status = WSAIoctl(proxySocket, + SIO_SET_WFP_CONNECTION_REDIRECT_RECORDS, + pRedirectRecords, + (DWORD)redirectRecordsSize, + 0, + 0, + (LPDWORD)&outputSize, + 0, + 0); + if(status != NO_ERROR) + { + status = WSAGetLastError(); + + HlprLogError(L"HlprWinSockAssociateConnectionRedirectRecordsAndContextWithProxySocket : WSAIoctl() [status: %#x]", + status); + + HLPR_BAIL; + } + + HLPR_BAIL_LABEL: + + HLPR_DELETE_ARRAY(pRedirectContext); + + HLPR_DELETE_ARRAY(pRedirectRecords); + + return status; +} + +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) + +/** + @helper_function="HlprWinSockBindToSocket" + + Purpose: Bind a socket to a protocol, port, and network address.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/MS737550.aspx
+*/ +_Success_(return == NO_ERROR) +UINT32 HlprWinSockBindToSocket(_In_ SOCKET unboundSocket, + _In_ SOCKADDR_STORAGE* pSockAddrStorage) +{ + ASSERT(unboundSocket != INVALID_SOCKET); + ASSERT(pSockAddrStorage); + + UINT32 status = NO_ERROR; + + status = bind(unboundSocket, + (SOCKADDR*)pSockAddrStorage, + sizeof(SOCKADDR_STORAGE)); + if(status != NO_ERROR) + { + status = WSAGetLastError(); + + HlprLogError(L"HlprWinSockBindToSocket : bind() [status: %#x]", + status); + } + + return status; +} + +/** + @helper_function="HlprWinSockListenOnSocket" + + Purpose: Place the socket in a state in which it listens for incoming connections.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/MS739168.aspx
+*/ +_Success_(return == NO_ERROR) +UINT32 HlprWinSockListenOnSocket(_In_ SOCKET boundSocket) +{ + ASSERT(boundSocket != INVALID_SOCKET); + + UINT32 status = NO_ERROR; + + status = listen(boundSocket, + SOMAXCONN); + if(status != NO_ERROR) + { + status = WSAGetLastError(); + + HlprLogError(L"HlprWinSockListenOnSocket : listen() [status: %#x]", + status); + } + + return status; +} + +/** + @helper_function="HlprWinSockAcceptConnection" + + Purpose: Place the socket in a state in which it listens for incoming connections.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/MS741513.aspx
+*/ +_Success_(return == NO_ERROR) +UINT32 HlprWinSockAcceptConnection(_In_ SOCKET listeningSocket, + _Inout_ SOCKET* pConnectedSocket, + _Inout_ SOCKADDR_STORAGE* pPeerSockAddrStorage) +{ + ASSERT(listeningSocket != INVALID_SOCKET); + ASSERT(pConnectedSocket); + ASSERT(pPeerSockAddrStorage); + + UINT32 status = NO_ERROR; + UINT32 addressLength = sizeof(SOCKADDR_STORAGE); + + *pConnectedSocket = WSAAccept(listeningSocket, + (SOCKADDR*)pPeerSockAddrStorage, + (LPINT)&addressLength, + 0, + 0); + if(*pConnectedSocket == INVALID_SOCKET) + { + status = WSAGetLastError(); + + HlprLogError(L"HlprWinSockAcceptConnection : WSAAccept() [status: %#x]", + status); + } + + return status; +} + +/** + @helper_function="HlprWinSockConnectSocket" + + Purpose: Establish the TCP handshake with the remote peer.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/MS741559.aspx
+*/ +_Success_(return == NO_ERROR) +UINT32 HlprWinSockConnectSocket(_In_ SOCKET unconnectedSocket, + _In_ SOCKADDR_STORAGE* pPeerSockAddrStorage) +{ + ASSERT(unconnectedSocket != INVALID_SOCKET); + ASSERT(pPeerSockAddrStorage); + + UINT32 status = NO_ERROR; + + status = WSAConnect(unconnectedSocket, + (SOCKADDR*)pPeerSockAddrStorage, + sizeof(SOCKADDR_STORAGE), + 0, + 0, + 0, + 0); + if(status != NO_ERROR) + { + status = WSAGetLastError(); + + HlprLogError(L"HlprWinSockConnectSocket : WSAConnect() [status: %#x]", + status); + } + + return status; +} + +/** + @helper_function="HlprWinSockReceiveTCPData" + + Purpose: Receive TCP data from the peer.
+
+ Notes: For use with TCP only.
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/MS741688.aspx
+*/ +_Success_(return == NO_ERROR) +UINT32 HlprWinSockReceiveTCPData(_In_ SOCKET receivingSocket, + _Inout_updates_(numDataBuffers) WSABUF* pDataBuffers, + _Inout_ UINT32* pNumBytesReceived, + _In_opt_ LPWSAOVERLAPPED pOverlapped, /* 0 */ + _In_opt_ LPWSAOVERLAPPED_COMPLETION_ROUTINE pCompletionFn, /* 0 */ + _In_ UINT32 numDataBuffers, /* 1 */ + _In_ UINT32 msgFlags) /* 0 */ +{ + ASSERT(receivingSocket != INVALID_SOCKET); + ASSERT(pDataBuffers); + ASSERT(pDataBuffers->len); + ASSERT(pDataBuffers->buf); + ASSERT(pNumBytesReceived); + ASSERT(numDataBuffers); + ASSERT((pNumBytesReceived && + pOverlapped == 0 && + pCompletionFn == 0) || + (pNumBytesReceived && + pOverlapped && + pCompletionFn)); + + + UINT32 status = NO_ERROR; + UINT32 flags = msgFlags; + + status = WSARecv(receivingSocket, + pDataBuffers, + numDataBuffers, + (LPDWORD)pNumBytesReceived, + (LPDWORD)&flags, + pOverlapped, + pCompletionFn); + if(status != NO_ERROR) + { + status = WSAGetLastError(); + + if(status == WSA_IO_PENDING && + pOverlapped && + pCompletionFn) + status = NO_ERROR; + else if(status != WSAEDISCON) + HlprLogError(L"HlprWinSockReceiveData : WSARecv() [status: %#x]", + status); + } + + return status; +} + +/** + @helper_function="HlprWinSockSendTCPData" + + Purpose: Transmit TCP data to the peer.
+
+ Notes: For use with TCP only.
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/MS742203.aspx
+*/ +_Success_(return == NO_ERROR) +UINT32 HlprWinSockSendTCPData(_In_ SOCKET sendingSocket, + _In_reads_(numDataBuffers) WSABUF* pDataBuffers, + _Inout_opt_ UINT32* pNumBytesTransmitted, + _In_opt_ LPWSAOVERLAPPED pOverlapped, /* 0 */ + _In_opt_ LPWSAOVERLAPPED_COMPLETION_ROUTINE pCompletionFn, /* 0 */ + _In_ UINT32 numDataBuffers) /* 1 */ +{ + ASSERT(sendingSocket != INVALID_SOCKET); + ASSERT(pDataBuffers); + ASSERT(pDataBuffers->len); + ASSERT(pDataBuffers->buf); + ASSERT(numDataBuffers); + ASSERT((pNumBytesTransmitted && + pOverlapped == 0 && + pCompletionFn == 0) || + (pNumBytesTransmitted && + pOverlapped && + pCompletionFn)); + + UINT32 status = NO_ERROR; + + status = WSASend(sendingSocket, + pDataBuffers, + numDataBuffers, + (LPDWORD)pNumBytesTransmitted, + 0, + pOverlapped, + pCompletionFn); + if(status != NO_ERROR) + { + status = WSAGetLastError(); + + if(status == WSA_IO_PENDING && + pOverlapped && + pCompletionFn) + status = NO_ERROR; + else + HlprLogError(L"HlprWinSockSendTCPData : WSASend() [status: %#x]", + status); + } + + return status; +} diff --git a/network/trans/WFPSampler/lib/HelperFunctions_WinSock.h b/network/trans/WFPSampler/lib/HelperFunctions_WinSock.h new file mode 100644 index 000000000..36ccc78af --- /dev/null +++ b/network/trans/WFPSampler/lib/HelperFunctions_WinSock.h @@ -0,0 +1,129 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_WinSock.h +// +// Abstract: +// This module contains prototypes of functions which assist in operations pertaining to +// Windows Sockets. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef HELPERFUNCTIONS_WINSOCK_H +#define HELPERFUNCTIONS_WINSOCK_H + +_Success_(return == NO_ERROR) +UINT32 HlprWinSockCleanup(); + +_Success_(return == NO_ERROR) +UINT32 HlprWinSockInitialize(); + +_Success_(return == NO_ERROR) +UINT32 HlprWinSockDestroyWSAEvent(_Inout_ WSAEVENT* pWSAEvent); + +_Success_(return == NO_ERROR) +UINT32 HlprWinSockSetWSAEvent(_In_ WSAEVENT wsaEvent); + +_Success_(return == NO_ERROR) +UINT32 HlprWinSockCreateWSAEvent(_Inout_ WSAEVENT* pWSAEvent); + +_Success_(return == NO_ERROR) +UINT32 HlprWinSockDestroySocket(_Inout_ SOCKET* pSocket); + +_Success_(return == NO_ERROR) +UINT32 HlprWinSockCreateSocket(_In_ SOCKADDR_STORAGE* pSockAddr, + _In_ UINT8 protocol, + _Inout_ SOCKET* pSocket); + +_Success_(return == NO_ERROR) +UINT32 HlprWinSockSetAbortiveDisconnect(_In_ SOCKET abortedSocket); + +_Success_(return == NO_ERROR) +UINT32 HlprWinSockSetSocketReceiveTimeout(_In_ SOCKET receivingSocket, + _In_ UINT32 timeout = 5000); + +_Success_(return == NO_ERROR) +UINT32 HlprWinSockSetSocketSendTimeout(_In_ SOCKET sendingSocket, + _In_ UINT32 timeout = 5000); + +_Success_(return == NO_ERROR) +UINT32 HlprWinSockSetSocketNonBlocking(_In_ SOCKET unboundSocket); + +_Success_(return == NO_ERROR) +UINT32 HlprWinSockAcquirePortReservationForSocket(_In_ SOCKET unboundSocket, + _In_ INET_PORT_RANGE* pPortRange, + _Inout_ UINT64* pReservationToken); + +_Success_(return == NO_ERROR) +UINT32 HlprWinSockAssociatePortReservationWithSocket(_In_ SOCKET unboundSocket, + _In_ UINT64 reservationToken); + +_Success_(return == NO_ERROR) +UINT32 HlprWinSockQueryPortReservation(_In_ UINT8 ipProtocol, + _In_ INET_PORT_RANGE* pPortRange, + _Inout_ UINT64* pReservationToken); + +_Success_(return == NO_ERROR) +UINT32 HlprWinSockDestroyPortReservation(_In_ UINT8 ipProtocol, + _In_ INET_PORT_RANGE* pPortRange, + _Inout_opt_ UINT64* pReservationToken = 0); + +_Success_(return == NO_ERROR) +UINT32 HlprWinSockCreatePortReservation(_In_ UINT8 ipProtocol, + _In_ INET_PORT_RANGE* pPortRange, + _Inout_ UINT64* pReservationToken); + +#if(NTDDI_VERSION >= NTDDI_WIN8) + +_Success_(return == NO_ERROR) +UINT32 HlprWinSockAssociateConnectionRedirectRecordsAndContextWithProxySocket(_In_ SOCKET originalSocket, + _Inout_ SOCKET proxySocket, + _Inout_ SOCKADDR_STORAGE* pRemoteSockAddrStorage, + _Inout_opt_ SOCKADDR_STORAGE* pOriginSockAddrStorage = 0); + +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) + +_Success_(return == NO_ERROR) +UINT32 HlprWinSockBindToSocket(_In_ SOCKET unboundSocket, + _In_ SOCKADDR_STORAGE* pSockAddrStorage); + +_Success_(return == NO_ERROR) +UINT32 HlprWinSockListenOnSocket(_In_ SOCKET boundSocket); + +_Success_(return == NO_ERROR) +UINT32 HlprWinSockAcceptConnection(_In_ SOCKET listeningSocket, + _Inout_ SOCKET* pConnectedSocket, + _Inout_ SOCKADDR_STORAGE* pPeerSockAddrStorage); + +_Success_(return == NO_ERROR) +UINT32 HlprWinSockConnectSocket(_In_ SOCKET unconnectedSocket, + _In_ SOCKADDR_STORAGE* pPeerSockAddrStorage); + +_Success_(return == NO_ERROR) +UINT32 HlprWinSockReceiveTCPData(_In_ SOCKET receivingSocket, + _Inout_updates_(numDataBuffers) WSABUF* pDataBuffers, + _Inout_ UINT32* pNumBytesReceived, + _In_opt_ LPWSAOVERLAPPED pOverlapped = 0, + _In_opt_ LPWSAOVERLAPPED_COMPLETION_ROUTINE pCompletionFn = 0, + _In_ UINT32 numDataBuffers = 1, + _In_ UINT32 msgFlags = 0); + +_Success_(return == NO_ERROR) +UINT32 HlprWinSockSendTCPData(_In_ SOCKET sendingSocket, + _In_reads_(numDataBuffers) WSABUF* pDataBuffers, + _Inout_opt_ UINT32* pNumBytesTransmitted, + _In_opt_ LPWSAOVERLAPPED pOverlapped = 0, + _In_opt_ LPWSAOVERLAPPED_COMPLETION_ROUTINE pCompletionFn = 0, + _In_ UINT32 numDataBuffers = 1); + +#endif /// HELPERFUNCTIONS_WINSOCK_H diff --git a/network/trans/WFPSampler/lib/WFPSampler.vcxproj b/network/trans/WFPSampler/lib/WFPSampler.vcxproj new file mode 100644 index 000000000..461823042 --- /dev/null +++ b/network/trans/WFPSampler/lib/WFPSampler.vcxproj @@ -0,0 +1,213 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {9049BF3E-29C4-4934-90C3-5E68C79036B4} + $(MSBuildProjectName) + false + Debug + Win32 + {076E4E09-5294-44A9-AE1A-251F59D32A52} + + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + StaticLibrary + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + StaticLibrary + + + Windows10 + False + Desktop + + WindowsApplicationForDrivers10.0 + StaticLibrary + + + Windows10 + True + Desktop + + WindowsApplicationForDrivers10.0 + StaticLibrary + + + + $(IntDir) + + + + + + + + + + + + + + + + WFPSampler + + + WFPSampler + + + WFPSampler + + + WFPSampler + + + + Sync + true + Level4 + %(PreprocessorDefinitions);WIN32_LEAN_AND_MEAN;UNICODE;_UNICODE;UM_NDIS630 + %(AdditionalIncludeDirectories);..\inc;..\idl;$(SDK_INC_PATH);.\$(IntDir) + + + %(PreprocessorDefinitions);WIN32_LEAN_AND_MEAN;UNICODE;_UNICODE;UM_NDIS630 + %(AdditionalIncludeDirectories);..\inc;..\idl;$(SDK_INC_PATH);.\$(IntDir) + + + %(PreprocessorDefinitions);WIN32_LEAN_AND_MEAN;UNICODE;_UNICODE;UM_NDIS630 + %(AdditionalIncludeDirectories);..\inc;..\idl;$(SDK_INC_PATH);.\$(IntDir) + + + %(AdditionalOptions) /integritycheck + %(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;ws2_32.lib + + + + + Sync + true + Level4 + %(PreprocessorDefinitions);WIN32_LEAN_AND_MEAN;UNICODE;_UNICODE;UM_NDIS630 + %(AdditionalIncludeDirectories);..\inc;..\idl;$(SDK_INC_PATH);.\$(IntDir) + + + %(PreprocessorDefinitions);WIN32_LEAN_AND_MEAN;UNICODE;_UNICODE;UM_NDIS630 + %(AdditionalIncludeDirectories);..\inc;..\idl;$(SDK_INC_PATH);.\$(IntDir) + + + %(PreprocessorDefinitions);WIN32_LEAN_AND_MEAN;UNICODE;_UNICODE;UM_NDIS630 + %(AdditionalIncludeDirectories);..\inc;..\idl;$(SDK_INC_PATH);.\$(IntDir) + + + %(AdditionalOptions) /integritycheck + %(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;ws2_32.lib + + + + + Sync + true + Level4 + %(PreprocessorDefinitions);WIN32_LEAN_AND_MEAN;UNICODE;_UNICODE;UM_NDIS630 + %(AdditionalIncludeDirectories);..\inc;..\idl;$(SDK_INC_PATH);.\$(IntDir) + + + %(PreprocessorDefinitions);WIN32_LEAN_AND_MEAN;UNICODE;_UNICODE;UM_NDIS630 + %(AdditionalIncludeDirectories);..\inc;..\idl;$(SDK_INC_PATH);.\$(IntDir) + + + %(PreprocessorDefinitions);WIN32_LEAN_AND_MEAN;UNICODE;_UNICODE;UM_NDIS630 + %(AdditionalIncludeDirectories);..\inc;..\idl;$(SDK_INC_PATH);.\$(IntDir) + + + %(AdditionalOptions) /integritycheck + %(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;ws2_32.lib + + + + + Sync + true + Level4 + %(PreprocessorDefinitions);WIN32_LEAN_AND_MEAN;UNICODE;_UNICODE;UM_NDIS630 + %(AdditionalIncludeDirectories);..\inc;..\idl;$(SDK_INC_PATH);.\$(IntDir) + + + %(PreprocessorDefinitions);WIN32_LEAN_AND_MEAN;UNICODE;_UNICODE;UM_NDIS630 + %(AdditionalIncludeDirectories);..\inc;..\idl;$(SDK_INC_PATH);.\$(IntDir) + + + %(PreprocessorDefinitions);WIN32_LEAN_AND_MEAN;UNICODE;_UNICODE;UM_NDIS630 + %(AdditionalIncludeDirectories);..\inc;..\idl;$(SDK_INC_PATH);.\$(IntDir) + + + %(AdditionalOptions) /integritycheck + %(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;ws2_32.lib + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/network/trans/WFPSampler/lib/WFPSampler.vcxproj.Filters b/network/trans/WFPSampler/lib/WFPSampler.vcxproj.Filters new file mode 100644 index 000000000..b88ea5bda --- /dev/null +++ b/network/trans/WFPSampler/lib/WFPSampler.vcxproj.Filters @@ -0,0 +1,88 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {D42F364B-2CA4-4EA2-80D6-895BFE9C44D4} + + + h;hpp;hxx;hm;inl;inc;xsd + {9403B31A-C7BB-4094-8943-3E168F09182E} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {773B4FFA-2A89-474E-A9AB-43C84AA0B2D2} + + + + + 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 + + + \ No newline at end of file diff --git a/network/trans/WFPSampler/scripts/WFPSamplerInstall.cmd b/network/trans/WFPSampler/scripts/WFPSamplerInstall.cmd new file mode 100644 index 000000000..9e4300c65 --- /dev/null +++ b/network/trans/WFPSampler/scripts/WFPSamplerInstall.cmd @@ -0,0 +1,227 @@ +:: WFPSamplerInstall.cmd +:: Command line script to install the WFPSampler + +:SoF + + @ECHO OFF + SETLOCAL ENABLEEXTENSIONS + SETLOCAL ENABLEDELAYEDEXPANSION + +:Variables + + :: Help + IF /I "%1" EQU "" ( GOTO :Usage ) ELSE ( + IF /I "%1" EQU "?" ( GOTO :Usage ) ELSE ( + IF /I "%1" EQU "-?" ( GOTO :Usage ) ELSE ( + IF /I "%1" EQU "/?" ( GOTO :Usage )))) + + :: Removal + IF /I "%1" EQU "r" ( GOTO :Uninstall ) ELSE ( + IF /I "%1" EQU "-r" ( GOTO :Uninstall ) ELSE ( + IF /I "%1" EQU "/r" ( GOTO :Uninstall ))) + + :: Architecture + IF /I "%1" EQU "" ( GOTO :Usage ) ELSE ( + IF /I "%1" EQU "amd64" (SET WFP_ARCH=x64) ELSE ( + IF /I "%1" EQU "i386" (SET WFP_ARCH=x86) ELSE ( + IF /I "%1" EQU "x64" (SET WFP_ARCH=%1) ELSE ( + IF /I "%1" EQU "x86" (SET WFP_ARCH=%1) ELSE ( GOTO :BinPath ))))) + + :: OS + IF /I "%2" EQU "" ( GOTO :Usage ) ELSE ( + IF /I "%2" EQU "Win7" (SET WFP_OS=%2) ELSE ( + IF /I "%2" EQU "Win8" (SET WFP_OS=%2) ELSE ( + IF /I "%2" EQU "Win8.1" (SET WFP_OS=%2) ELSE ( GOTO :Usage )))) + + :: Optimized + IF /I "%3" EQU "" ( GOTO :Usage ) ELSE ( + IF /I "%3" EQU "Debug" (SET WFP_OPZ=%3) ELSE ( + IF /I "%3" EQU "Release" (SET WFP_OPZ=%3) ELSE ( GOTO :Usage ))) + +:BinPath + + IF "%PROCESSOR_ARCHITECTURE%" EQU "AMD64" (SET _PROCESSOR_ARCHITECTURE_=x64) ELSE ( + IF "%PROCESSOR_ARCHITECTURE%" EQU "I386" (SET _PROCESSOR_ARCHITECTURE_=x86) ELSE ( + SET _PROCESSOR_ARCHITECTURE_=%PROCESSOR_ARCHITECTURE%)) + + IF DEFINED WFP_OS ( + :: Use a path relative to where this script is located for finding the binaries + FOR /F %%A IN ('ECHO "%~dp0" ^| FIND /I "network\trans\WFPSampler\scripts\"') DO (SET SCRIPTS_DIR=TRUE) + + IF DEFINED SCRIPTS_DIR ( + :: Running from Network\Trans\WFPSampler\Scripts + SET WFPSAMPLER_EXE="%~dp0..\exe\%WFP_ARCH%\%WFP_OS%%WFP_OPZ%\WFPSampler.exe" + SET WFPSAMPLERSERVICE_EXE="%~dp0..\svc\%WFP_ARCH%\%WFP_OS%%WFP_OPZ%\WFPSamplerService.exe" + SET WFPSAMPLERCALLOUTDRIVER_SYS="%~dp0..\%WFP_ARCH%\%WFP_OS%%WFP_OPZ%\Package\WFPSamplerCalloutDriver.sys" + SET WFPSAMPLERCALLOUTDRIVER="%~dp0..\%WFP_ARCH%\%WFP_OS%%WFP_OPZ%\Package\WFPSamplerCalloutDriver.*" + SET SIGNTOOL_PATH="%~dp0..\..\..\..\..\bin\%_PROCESSOR_ARCHITECTURE_%" + ) ELSE ( + FOR /F %%A IN ('ECHO "%~dp0" ^| FIND /I "network\trans\WFPSampler\%WFP_ARCH%\%WFP_OS%%WFP_OPZ%\package"') DO (SET PACKAGE_DIR=TRUE) + + IF DEFINED PACKAGE_DIR ( + :: Running from Package Directory + SET WFPSAMPLER_EXE="%~dp0..\..\..\exe\%WFP_ARCH%\%WFP_OS%%WFP_OPZ%\WFPSampler.exe" + SET WFPSAMPLERSERVICE_EXE="%~dp0..\..\..\svc\%WFP_ARCH%\%WFP_OS%%WFP_OPZ%\WFPSamplerService.exe" + SET WFPSAMPLERCALLOUTDRIVER_SYS="%~dp0WFPSamplerCalloutDriver.sys" + SET WFPSAMPLERCALLOUTDRIVER="%~dp0WFPSamplerCalloutDriver.*" + SET SIGNTOOL_PATH="%~dp0..\..\..\..\..\..\..\bin\%_PROCESSOR_ARCHITECTURE_%" + ) ELSE ( + ECHO. + ECHO Don't know binary location. Please provide path. + ECHO Current directory: "%CD%" + ECHO Script directory: "%~dp0" + + GOTO :Usage + ) + ) + ) ELSE ( + :: Use the provided path for finding the binaries + SET WFPSAMPLER_EXE="%1\WFPSampler.exe" + SET WFPSAMPLERSERVICE_EXE="%1\WFPSamplerService.exe" + SET WFPSAMPLERCALLOUTDRIVER_SYS="%1\WFPSamplerCalloutDriver.sys" + SET WFPSAMPLERCALLOUTDRIVER="%1\WFPSamplerCalloutDriver.*" + SET SIGNTOOL_PATH= + ) + +:Install + + ECHO. + ECHO Installing WFPSampler + + IF DEFINED SIGNTOOL_PATH ( + ECHO. + ECHO Attempting to sign WFPSampler.Exe + %SIGNTOOL_PATH%\SignTool.exe Sign -A -V %WFPSAMPLER_EXE% + + ECHO. + ECHO Attempting to sign WFPSamplerService.Exe + %SIGNTOOL_PATH%\SignTool.exe Sign -A -V %WFPSAMPLERSERVICE_EXE% + + ECHO. + ECHO Attempting to sign WFPSamplerCaloutDriver.Sys + %SIGNTOOL_PATH%\SignTool.exe Sign -A -V %WFPSAMPLERCALLOUTDRIVER_SYS% + ) + + ECHO. + ECHO Copying WFPSamplerCalloutDriver Bins to %WinDir%\System32\Drivers\ + COPY /Y %WFPSAMPLERCALLOUTDRIVER% %WinDir%\System32\Drivers\ + + ECHO. + ECHO Copying WFPSampler application binaries to %WinDir%\System32\ + COPY /Y %WFPSAMPLER_EXE% %WinDir%\System32\ + COPY /Y %WFPSAMPLERSERVICE_EXE% %WinDir%\System32\ +goto :Cleanup + IF EXIST %WinDir%\System32\WFPSamplerService.Exe ( + ECHO. + ECHO Registering the WFPSampler Service + %WinDir%\System32\WFPSamplerService.Exe -i + Net Start WFPSampler + ) + + IF EXIST %WinDir%\System32\Drivers\WFPSamplerCalloutDriver.Inf ( + IF EXIST %WinDir%\System32\Drivers\WFPSamplerCalloutDriver.Sys ( + ECHO. + ECHO Registering the WFPSampler Callout Driver + RunDLL32.Exe syssetup,SetupInfObjectInstallAction DefaultInstall 131 %WinDir%\System32\Drivers\WFPSamplerCalloutDriver.Inf + Net Start WFPSamplerCallouts + ) + ) + +:Cleanup + + SET WFPSAMPLERCALLOUTDRIVER= + SET WFPSAMPLERCALLOUTDRIVER_SYS= + SET WFPSAMPLERSERVICE_EXE= + SET WFPSAMPLER_EXE= + + SET PACKAGE_DIR= + SET SCRIPTS_DIR= + + SET _PROCESSOR_ARCHITECTURE_= + + SET WFP_ARCH= + SET WFP_OPZ= + SET WFP_OS= + + GOTO :EoF + +:Uninstall + + ECHO. + ECHO Uninstalling WFPSampler + + IF EXIST %WinDir%\System32\WFPSampler.Exe ( + ECHO. + ECHO Removing policy + WFPSampler.exe -clean + ) + + ECHO. + ECHO Stopping the WFPSampler service + Net Stop WFPSampler + + ECHO. + ECHO Stopping the WFPSamplerCallouts service + Net Stop WFPSamplerCallouts + + IF EXIST %WinDir%\System32\WFPSamplerService.Exe ( + ECHO. + ECHO Unregistering the WFPSampler Service + %WinDir%\System32\WFPSamplerService.Exe -u + ) + + IF EXIST %WinDir%\System32\Drivers\WFPSamplerCalloutDriver.Inf ( + IF EXIST %WinDir%\System32\Drivers\WFPSamplerCalloutDriver.Sys ( + ECHO. + ECHO Unregistering the WFPSampler Callout Driver + RunDLL32.Exe SETUPAPI.DLL,InstallHinfSection DefaultUninstall 132 %WinDir%\System32\Drivers\WFPSamplerCalloutDriver.Inf + ) + ) + + ECHO. + ECHO Deleting WFPSampler application binaries from %WinDir%\System32\ + ERASE /F /Q %WinDir%\System32\WFPSampler.exe + ERASE /F /Q %WinDir%\System32\WFPSamplerService.exe + + ECHO. + ECHO Deleting WFPSamplerCalloutDriver binaries from %WinDir%\System32\Drivers\ + ERASE /F /Q %WinDir%\System32\Drivers\WFPSamplerCalloutDriver.cat + ERASE /F /Q %WinDir%\System32\Drivers\WFPSamplerCalloutDriver.inf + ERASE /F /Q %WinDir%\System32\Drivers\WFPSamplerCalloutDriver.sys + + GOTO :EoF + +:Usage + + ECHO. + ECHO WFPSamplerInstall.cmd [PATH ^| ARCH ^| -r] [OS] [OPTIMIZE] + ECHO. + ECHO. + ECHO ARCH Specify the architecure the binaries are for + ECHO x86 or x64 + ECHO. + ECHO OS Specify the OS the binaries are for + ECHO Win7, Win8, or Win8.1 + ECHO. + ECHO OPTIMIZE Specify the optimization + ECHO i.e. Debug + ECHO. + ECHO WFPSampler.cmd x86 Win7 Debug + ECHO. + ECHO. + ECHO WFPSamplerInstall.cmd [PATH] + ECHO. + ECHO PATH Copies binaries from specified path and installs the WFPSampler + ECHO i.e. C:\MyDir + ECHO. + ECHO WFPSampler.cmd C:\MyDir + ECHO. + ECHO. + ECHO WFPSamplerInstall.cmd [-r] + ECHO. + ECHO -r Uninstalls the WFPSampler and removes binaries + ECHO. + ECHO WFPSampler.cmd -r + ECHO. + +:EoF \ No newline at end of file diff --git a/network/trans/WFPSampler/svc/Framework_Include.h b/network/trans/WFPSampler/svc/Framework_Include.h new file mode 100644 index 000000000..625f2838f --- /dev/null +++ b/network/trans/WFPSampler/svc/Framework_Include.h @@ -0,0 +1,34 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// Framework_Include.h +// +// Abstract: +// This module contains include headers for central exportaion of the Framework_* modules. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef FRAMEWORK_INCLUDE_H +#define FRAMEWORK_INCLUDE_H + +#include "Framework_WFPSamplerService.h" /// . +#include "Framework_RPCServerInterface.h" /// . +#include "Framework_WindowsFirewall.h" /// . + +/* + The Action Center APIs are under Non Disclosure Agreement. If you wish to see the sample code + for this, you will need to contact your Microsoft representative and request the sample code. +*/ +///#include "Framework_ActionCenter.h" /// . + +#endif /// FRAMEWORK_INCLUDE_H \ No newline at end of file diff --git a/network/trans/WFPSampler/svc/Framework_RPCServerInterface.cpp b/network/trans/WFPSampler/svc/Framework_RPCServerInterface.cpp new file mode 100644 index 000000000..b1cce97e3 --- /dev/null +++ b/network/trans/WFPSampler/svc/Framework_RPCServerInterface.cpp @@ -0,0 +1,420 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// Framework_RPCServerInterface.cpp +// +// Abstract: +// This module contains functions which implement the RPC server interface. +// +// Author: +// Dusty Harper (DHarper) +// +// Naming Convention: +// +// +// +// i.e. +// +// +// { +// - Function is likely visible to other modules. +// } +// +// { +// RPCServerInterface - Function pertains to the RPC Server Interface. +// } +// +// { +// Initialize - Function prepares environment for use. +// SecurityCallback - Function enforces RPC security measures. +// Terminate - Function cleans up the environment. +// } +// +// { +// } +// +// Private Functions: +// +// Public Functions: +// RPCClientInterfaceInitialize(), +// RPCClientInterfaceSecurityCallback(), +// RPCClientInterfaceTerminate(), +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Enhance function declaration for IntelliSense +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "Framework_WFPSamplerService.h" /// . +#include "WFPSamplerRPC_s.c" /// $(OBJ_PATH)\..\idl\$(O) + +/// Module's Local Structures + +typedef struct RPC_DATA_ +{ + RPC_IF_HANDLE rpcInterfaceHandle; + RPC_BINDING_VECTOR* pBindingVector; + UINT32 protocolSequence; + RPC_WSTR pProtocolSequence; + PWSTR pEndpoint; + UINT32 interfaceFlags; + BOOLEAN isInterfaceRegistered; + BOOLEAN isEndpointRegistered; +}RPC_DATA, *PRPC_DATA; + +/// + +/// Module's Local Enumerations + +typedef enum RPC_INITIALIZATION_TASKS_ +{ + RPC_SELECT_PROTOCOL_SEQUENCE = 0, + RPC_REGISTER_INTERFACE = 1, + RPC_CREATE_BINDING_INFORMATION = 2, + RPC_REGISTER_ENDPOINT = 3, + RPC_INITIALIZATION_TASKS_MAX +}RPC_INITIALIZATION_TASKS; + +typedef enum RPC_TERMINATION_TASKS_ +{ + RPC_UNREGISTER_ENDPOINT = 0, + RPC_FREE_BINDING_INFORMATION = 1, + RPC_UNREGISTER_INTERFACE = 2, + RPC_TERMINATION_TASKS_MAX +}RPC_TERMINATION_TASKS; + +/// + +/// Module's Local Variables + +RPC_DATA* pRPCData = 0; + +/// + +/// Functions implemented in Framework_WFPSamplerSvc.cpp + +_Success_(return == NO_ERROR) +UINT32 ServiceStatusReportToSCM(_In_ UINT32 currentState, + _In_ UINT32 exitCode, + _In_ UINT32 waitHint); + +VOID ServiceEventLogError(_In_ PCWSTR pFunctionName, + _In_ UINT32 status); + +/// + +extern "C" +{ + /** + @framework_function="MIDL_user_free" + + Purpose: RPC stub routine to allocate memory.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA378651.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA378715.aspx
+ */ + _Must_inspect_result_ + _Ret_maybenull_ _Post_writable_byte_size_(size) + VOID* __RPC_USER MIDL_user_allocate(/* _In_ */ size_t size) + { + BYTE* pBuffer = 0; + + if(size) + { + HLPR_NEW_ARRAY(pBuffer, + BYTE, + size); + } + + return pBuffer; + } + + /** + @framework_function="MIDL_user_free" + + Purpose: RPC stub routine to free allocated memory.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA378651.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA378716.aspx
+ */ + VOID __RPC_USER MIDL_user_free(/* _Inout_ */ _Pre_maybenull_ _Post_invalid_ VOID* pBuffer) + { + HLPR_DELETE_ARRAY(pBuffer); + + return; + } +} + +/** + @framework_function="RPCInterfaceSecurityCallback" + + Purpose: Callback routine to enforce security measures.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA378651.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA378625.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA378434.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA373553.aspx
+*/ +_Success_(return == RPC_S_OK) +RPC_STATUS CALLBACK RPCServerInterfaceSecurityCallback(_In_ RPC_IF_HANDLE interfaceHandle, + _In_ VOID* pContext) +{ + UNREFERENCED_PARAMETER(interfaceHandle); + + ASSERT(interfaceHandle); + ASSERT(pContext); + + RPC_STATUS status = RPC_S_OK; + + RPC_CALL_ATTRIBUTES_V2 callAttributes = {0}; + + callAttributes.Version = 2; + callAttributes.Flags = RPC_QUERY_CLIENT_PID; + + status = RpcServerInqCallAttributes((RPC_BINDING_HANDLE)pContext, /// Binding handle used by the client when making the RPC call + &callAttributes); /// Client's call attributes + if(status != RPC_S_OK) + { + ServiceEventLogError(L"RPCServerInterfaceSecurityCallback : RpcServerinqCallAttributes()", + status); + + HLPR_BAIL; + } + + if(callAttributes.IsClientLocal == rcclRemote || /// make sure this is a local client + callAttributes.ProtocolSequence != pRPCData->protocolSequence || /// make sure this is the correct protocol sequence (in our case Local RPC) + !HlprGUIDsAreEqual(&(callAttributes.InterfaceUuid), + &WFPSAMPLER_INTERFACE) || /// make sure this is the correct interface + callAttributes.AuthenticationLevel < RPC_C_AUTHN_LEVEL_PKT_PRIVACY) /// make sure this is highest security + { + status = RPC_S_ACCESS_DENIED; + + ServiceEventLogError(L"RPCServerInterfaceSecurityCallback()", + status); + } + + HLPR_BAIL_LABEL: + + return status; +} + +/** + @framework_function="RPCServerInterfaceTerminate" + + Purpose: Remove the RPC server interface by unregistering the endpoint and interface and + freeing the bindings.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA378651.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA375651.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA375615.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA378445.aspx
+*/ +_Success_(return == RPC_S_OK) +RPC_STATUS RPCServerInterfaceTerminate() +{ + RPC_STATUS status = RPC_S_OK; + const UINT32 ITERATIONS = RPC_TERMINATION_TASKS_MAX; + + for(UINT32 index = 0; + index < ITERATIONS; + index++) + { + switch(index) + { + case RPC_UNREGISTER_ENDPOINT: + { + if(pRPCData->isEndpointRegistered) + { + status = RpcEpUnregister(pRPCData->rpcInterfaceHandle, /// Handle genereated by MIDL compiler for Interface + pRPCData->pBindingVector, /// Vector of binding Handles + 0); /// no object UUIDs + if(status != RPC_S_OK) + ServiceEventLogError(L"RPCServerInterfaceTerminate : RpcEpUnregister()", + status); + } + + break; + } + case RPC_FREE_BINDING_INFORMATION: + { + if(pRPCData->pBindingVector) + { + status = RpcBindingVectorFree(&(pRPCData->pBindingVector)); /// Vector of binding Handles to free. 0 on success + if(status != RPC_S_OK) + ServiceEventLogError(L"RPCServerInterfaceTerminate : RpcBindingVectorFree()", + status); + } + + break; + } + case RPC_UNREGISTER_INTERFACE: + { + if(pRPCData->isInterfaceRegistered) + { + status = RpcServerUnregisterIf(pRPCData->rpcInterfaceHandle, /// Handle genereated by MIDL compiler for Interface + (UUID*)&WFPSAMPLER_INTERFACE, /// GUID for our Interface + TRUE); /// Wait for any outstanding calls to finish + if(status != RPC_S_OK) + ServiceEventLogError(L"RPCServerInterfaceTerminate : RpcServerUnregisterIf()", + status); + } + + break; + } + default: + { + status = RPC_S_INVALID_ARG; + + break; + } + } + + ServiceStatusReportToSCM(SERVICE_STOP_PENDING, + status, + 0); + } + + HLPR_DELETE(pRPCData); + + return status; +} + +/** + @framework_function="RPCServerInterfaceInitialize" + + Purpose: Initialize the RPC server interface by creating the RPC bindings and registering + the interface and endpoint.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA378651.aspx
+ HTTP://MSDN.Microsoft.com/En-US/LibraryWindows/Desktop//AA378452.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA378441.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA378433.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA375637.aspx
+*/ +_Success_(return == RPC_S_OK) +RPC_STATUS RPCServerInterfaceInitialize() +{ + RPC_STATUS status = RPC_S_OK; + const UINT32 ITERATIONS = RPC_INITIALIZATION_TASKS_MAX; + + HLPR_NEW(pRPCData, + RPC_DATA); + HLPR_BAIL_ON_ALLOC_FAILURE(pRPCData, + status); + + pRPCData->rpcInterfaceHandle = IWFPSampler_v1_0_s_ifspec; /// MIDL generated Server Interface Handle + pRPCData->protocolSequence = RPC_PROTSEQ_LRPC; /// Use Local RPC + pRPCData->pProtocolSequence = (RPC_WSTR)g_pRPCProtocolSequence; /// Use Local RPC + pRPCData->pEndpoint = (PWSTR)g_pEndpoint; /// String representation of our endpoint + pRPCData->interfaceFlags = RPC_IF_ALLOW_LOCAL_ONLY | /// Reject all non-local RPC calls + RPC_IF_AUTOLISTEN | /// Interface will automatically listen + RPC_IF_ALLOW_SECURE_ONLY; /// Only allow authorized clients (valid account and credentials) + + for(UINT32 index = 0; + index < ITERATIONS; + index++) + { + switch(index) + { + case RPC_SELECT_PROTOCOL_SEQUENCE: + { + status = RpcServerUseProtseqEp(pRPCData->pProtocolSequence, /// protocolSequence to register. In our case this is Local RPC + RPC_C_PROTSEQ_MAX_REQS_DEFAULT, /// Backlog queue length. In our case this is ignored. + pRPCData->pEndpoint, /// Use our endpoint information + 0); /// We will implement security via a callback routine + if(status != RPC_S_OK) + { + ServiceEventLogError(L"RPCServerInterfaceInitialize : RpcServerUseProtseqEp()", + status); + + HLPR_BAIL; + } + + break; + } + case RPC_REGISTER_INTERFACE: + { + status = RpcServerRegisterIfEx(pRPCData->rpcInterfaceHandle, /// Handle genereated by MIDL compiler for Interface + 0, /// Use default entry point type + 0, /// Use default entry point vector + pRPCData->interfaceFlags, /// Registration flags + RPC_C_LISTEN_MAX_CALLS_DEFAULT, /// Max number of calls that can be accepted concurrently + RPCServerInterfaceSecurityCallback); /// Callback routine to implement our security + if(status != RPC_S_OK) + { + ServiceEventLogError(L"RPCServerInterfaceInitialize : RpcServerRegisterIf()", + status); + + HLPR_BAIL; + } + + break; + } + case RPC_CREATE_BINDING_INFORMATION: + { + status = RpcServerInqBindings(&(pRPCData->pBindingVector)); /// Get vector of binding handles + if(status != RPC_S_OK) + { + ServiceEventLogError(L"RPCServerInterfaceInitialize : RpcServerInqBindings()", + status); + + HLPR_BAIL; + } + + break; + } + case RPC_REGISTER_ENDPOINT: + { + status = RpcEpRegister(pRPCData->rpcInterfaceHandle, /// Handle genereated by MIDL compiler for Interface + pRPCData->pBindingVector, /// Vector of Binding Handles + 0, /// No object UUIDs + L"WFPSampler Endpoint"); /// Annotation / Comment + if(status != RPC_S_OK) + { + ServiceEventLogError(L"RPCServerInterfaceInitialize : RpcEpRegister()", + status); + + HLPR_BAIL; + } + + pRPCData->isEndpointRegistered = TRUE; + + break; + } + default: + { + status = RPC_S_INVALID_ARG; + + break; + } + } + + ServiceStatusReportToSCM(SERVICE_START_PENDING, + status, + 5000); + } + + HLPR_BAIL_LABEL: + + if(status != RPC_S_OK) + ServiceStatusReportToSCM(SERVICE_STOPPED, + status, + 0); + + return status; +} diff --git a/network/trans/WFPSampler/svc/Framework_RPCServerInterface.h b/network/trans/WFPSampler/svc/Framework_RPCServerInterface.h new file mode 100644 index 000000000..01e798ac3 --- /dev/null +++ b/network/trans/WFPSampler/svc/Framework_RPCServerInterface.h @@ -0,0 +1,30 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// Framework_RPCServerInterface.h +// +// Abstract: +// This module contains prototypes for functions which implement the RPC server interface. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef FRAMEWORK_RPC_SERVER_INTERFACE_H +#define FRAMEWORK_RPC_SERVER_INTERFACE_H + +_Success_(return == RPC_S_OK) +RPC_STATUS RPCServerInterfaceTerminate(); + +_Success_(return == RPC_S_OK) +RPC_STATUS RPCServerInterfaceInitialize(); + +#endif /// FRAMEWORK_RPC_SERVER_INTERFACE_H \ No newline at end of file diff --git a/network/trans/WFPSampler/svc/Framework_WFPSamplerService.cpp b/network/trans/WFPSampler/svc/Framework_WFPSamplerService.cpp new file mode 100644 index 000000000..df1bb66e7 --- /dev/null +++ b/network/trans/WFPSampler/svc/Framework_WFPSamplerService.cpp @@ -0,0 +1,841 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// Framework_WFPSamplerService.cpp +// +// Abstract: +// This module contains functions which form the entry point to the WFPSampler service +// (WFPSamplerService.exe). +// +// Naming Convention: +// +// +// +// i.e. +// +// +// { +// ServiceEvent +// ServiceStatus +// ServiceControl +// Service +// } +// +// { +// Log +// Report +// Handler +// Terminate +// Initialize +// Uninstall +// Install +// } +// +// { +// Error +// ToSCM +// } +// +// Exported Functions: +// +// Internal Functions: +// ServiceEventLogError(), +// ServiceStatusReportToSCM(), +// ServiceControlHandler(), +// ServiceTerminate(), +// ServiceInitialize(), +// ServiceUninstall(), +// ServiceInstall(), +// ServiceMain(), +// wmain() +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Change sublayer weight +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "Framework_Include.h" /// . + +/// Module's Local Structures + +typedef struct SERVICE_DATA_ +{ + SERVICE_STATUS serviceStatus; + SERVICE_STATUS_HANDLE serviceStatusHandle; + HANDLE serviceStopEventHandle; +}SERVICE_DATA, *PSERVICE_DATA; + +/// + +BOOLEAN g_isCOMInitialized = FALSE; +BOOLEAN g_takeFirewallCategory = TRUE; + +/// Module's Local Variables + +SERVICE_DATA* pServiceData = 0; + +/// + +/** + @private_function="PrvRegistryCreateEntries" + + Purpose: Adds registry keys that allow operation during Safe-Mode with Networking.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 PrvRegistryCreateEntries() +{ + UINT32 status = NO_ERROR; + PCWSTR pService = L"Service"; + size_t valueSize = (wcslen(pService) + 1) * sizeof(WCHAR); + + /// This Registry Setting allows functionality during Safe-Mode with Networking + status = HlprRegistrySetValue(HKEY_LOCAL_MACHINE, + L"SYSTEM\\CurrentControlSet\\Control\\SafeBoot\\Network\\WFPSampler", + REG_SZ, + 0, + (BYTE*)pService, + (UINT32)valueSize); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprRegistrySetValue(HKEY_LOCAL_MACHINE, + L"SYSTEM\\CurrentControlSet\\Control\\SafeBoot\\Network\\WFPSamplerCallouts", + REG_SZ, + 0, + (BYTE*)pService, + (UINT32)valueSize); + + HLPR_BAIL_LABEL: + + return status; +} + +/** + @private_function="PrvRegistryDestroyEntries" + + Purpose: Removes the registry keys that allow operation during Safe-Mode.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 PrvRegistryDestroyEntries() +{ + UINT32 status = NO_ERROR; + + status = HlprRegistryDeleteKey(HKEY_LOCAL_MACHINE, + L"SYSTEM\\CurrentControlSet\\Control\\SafeBoot\\Network\\WFPSampler"); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprRegistryDeleteKey(HKEY_LOCAL_MACHINE, + L"SYSTEM\\CurrentControlSet\\Control\\SafeBoot\\Network\\WFPSamplerCallouts"); + + HLPR_BAIL_LABEL: + + return status; +} + +/** + @framework_function="ServiceEventLogError" + + Purpose: Log errors in the eventlog.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/AA363678.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/AA363679.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/AA363642.aspx
+*/ +VOID ServiceEventLogError(_In_ PCWSTR pFunctionName, + _In_ UINT32 errorCode) +{ + UINT32 status = NO_ERROR; + HANDLE eventLogHandle = 0; + PCWSTR pFormatString = L"%s [status: %#x]"; + const size_t MAX_CHARS = 256; + WCHAR pMessage[MAX_CHARS]; + PCWSTR ppStrings[2]; + + ZeroMemory(pMessage, + sizeof(WCHAR) * MAX_CHARS); + + ZeroMemory(ppStrings, + sizeof(PCWSTR) * 2); + + status = StringCchPrintf(pMessage, + MAX_CHARS, + pFormatString, + pFunctionName, + errorCode); + if(status != NO_ERROR) + { + wprintf(L"HlprLogError : StringCchVPrintf() [status: %#x]", + status); + + HLPR_BAIL; + } + +#pragma warning(push) +#pragma warning(disable: 28735) /// Use ETW + + eventLogHandle = RegisterEventSource(0, + g_pServiceName); + HLPR_BAIL_ON_NULL_POINTER(eventLogHandle); + +#pragma warning(pop) + + ppStrings[0] = g_pServiceName; + ppStrings[1] = pMessage; + + ReportEvent(eventLogHandle, + EVENTLOG_ERROR_TYPE, + 0, + SVC_ERROR, + 0, + 2, + 0, + ppStrings, + 0); + + HLPR_BAIL_LABEL: + + if(eventLogHandle) + DeregisterEventSource(eventLogHandle); + + return; +} + +/** + @framework_function="ServiceStatusReportToSCM" + + Purpose: Inform the Service Control Manager of the WFPSampler service's current state.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/MS686241.aspx
+*/ +_Success_(return == NO_ERROR) +UINT32 ServiceStatusReportToSCM(_In_ UINT32 currentState, + _In_ UINT32 exitCode, + _In_ UINT32 waitHint) +{ + UINT32 status = NO_ERROR; + static UINT32 checkpoint = 1; + + pServiceData->serviceStatus.dwCurrentState = currentState; + pServiceData->serviceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP; + pServiceData->serviceStatus.dwWin32ExitCode = exitCode; + pServiceData->serviceStatus.dwWaitHint = waitHint; + pServiceData->serviceStatus.dwCheckPoint = checkpoint++; + + switch(currentState) + { + case SERVICE_START_PENDING: + { + pServiceData->serviceStatus.dwControlsAccepted = 0; + + break; + } + case SERVICE_RUNNING: + { + checkpoint = 1; + + pServiceData->serviceStatus.dwCheckPoint = 0; +/* + The Action Center APIs are under Non Disclosure Agreement. If you wish to see the sample code + for this, you will need to contact your Microsoft representative and request the sample code. +*/ +/// ActionCenterReportStatusEnabled(); + + break; + } + case SERVICE_STOPPED: + { + checkpoint = 1; + + pServiceData->serviceStatus.dwCheckPoint = 0; +/* + The Action Center APIs are under Non Disclosure Agreement. If you wish to see the sample code + for this, you will need to contact your Microsoft representative and request the sample code. +*/ +/// ActionCenterReportStatusDisabled(); + + break; + } + } + + if(!SetServiceStatus(pServiceData->serviceStatusHandle, + &(pServiceData->serviceStatus))) + { + status = GetLastError(); + + ServiceEventLogError(L"ServiceStatusReportToSCM : SetServiceStatus()", + status); + } + + return status; +} + +/** + @framework_function="ServiceControlHandler" + + Purpose: Take action on the indicated service control.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +VOID ServiceControlHandler(_In_ UINT32 serviceControl) +{ + switch(serviceControl) + { + case SERVICE_CONTROL_CONTINUE: + { + ServiceStatusReportToSCM(SERVICE_CONTINUE_PENDING, + NO_ERROR, + 0); + + break; + } + case SERVICE_CONTROL_PAUSE: + { + ServiceStatusReportToSCM(SERVICE_PAUSE_PENDING, + NO_ERROR, + 0); + + break; + } + case SERVICE_CONTROL_SHUTDOWN: + case SERVICE_CONTROL_STOP: + { + ServiceStatusReportToSCM(SERVICE_STOP_PENDING, + NO_ERROR, + 0); + + SetEvent(pServiceData->serviceStopEventHandle); + + break; + } + default: + break; + } + + return; +} + +/** + @framework_function="ServiceTerminate" + + Purpose: Cleanup and stop the WFPSampler service.
+
+ Notes: Cleanup entails relinquishing Windows Firewall categories, tearing down the RPC + server interface and stopping the service.
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/MS688715.aspx
+*/ +_Success_(return == NO_ERROR) +UINT32 ServiceTerminate() +{ + UINT32 status = NO_ERROR; + + if(g_takeFirewallCategory) + WindowsFirewallReleaseFirewallCategory(); +/* + The Action Center APIs are under Non Disclosure Agreement. If you wish to see the sample code + for this, you will need to contact your Microsoft representative and request the sample code. +*/ +/// ActionCenterUnregisterFirewall(); + + if(g_isCOMInitialized) + { + CoUninitialize(); + + g_isCOMInitialized = FALSE; + } + + RPCServerInterfaceTerminate(); + + HLPR_CLOSE_HANDLE(pServiceData->serviceStopEventHandle); + + ServiceStatusReportToSCM(SERVICE_STOPPED, + status, + 0); + + return status; +} + +/** + @framework_function="ServiceInitialize" + + Purpose: Create environment to successfully run and start the WFPSampler service.
+
+ Notes: Creation entails taking the firewall category from Windows Firewall, building the + RPC server interface, and starting the WFPSampler service.
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 ServiceInitialize() +{ + UINT32 status = NO_ERROR; + REGISTRY_VALUE regValue = {0}; + HANDLE engineHandle = 0; + FWPM_PROVIDER* pProvider = 0; + FWPM_SUBLAYER* pSubLayer = 0; + + ServiceStatusReportToSCM(SERVICE_START_PENDING, + status, + 2500); + + pServiceData->serviceStopEventHandle = CreateEvent(0, + TRUE, + FALSE, + L"WFPSampler_Stop_Event"); + if(pServiceData->serviceStopEventHandle == 0) + { + status = GetLastError(); + + HLPR_BAIL; + } + + regValue.type = REG_DWORD; + regValue.size = sizeof(UINT32); + + HlprRegistryGetValue(HKEY_LOCAL_MACHINE, + L"SYSTEM\\CurrentControlSet\\Services\\WFPSampler", + L"coexistWithWindowsFirewall", + ®Value); + if(regValue.dword) + g_takeFirewallCategory = FALSE; + + status = HlprFwpmEngineOpen(&engineHandle); + HLPR_BAIL_ON_FAILURE(status); + + /// Make sure WFPSampler's provider is available. If not, create it. + status = FwpmProviderGetByKey(engineHandle, + &WFPSAMPLER_PROVIDER, + &pProvider); + if(status == FWP_E_PROVIDER_NOT_FOUND) + { + status = HlprFwpmProviderAdd(engineHandle, + &WFPSAMPLER_PROVIDER, + g_pCompanyName, + g_pBinaryDescription, + g_pServiceName, + FWPM_PROVIDER_FLAG_PERSISTENT); + HLPR_BAIL_ON_FAILURE(status); + } + else + FwpmFreeMemory((VOID**)&pProvider); + + /// Make sure WFPSampler's subLayer is available. If not, create it. + status = FwpmSubLayerGetByKey(engineHandle, + &WFPSAMPLER_SUBLAYER, + &pSubLayer); + if(status == FWP_E_SUBLAYER_NOT_FOUND) + { + status = HlprFwpmSubLayerAdd(engineHandle, + &WFPSAMPLER_SUBLAYER, + L"WFP Sampler's default subLayer", + &WFPSAMPLER_PROVIDER, + 0x7FFE, + FWPM_SUBLAYER_FLAG_PERSISTENT); + HLPR_BAIL_ON_FAILURE(status); + } + else + FwpmFreeMemory((VOID**)&pSubLayer); + + status = RPCServerInterfaceInitialize(); + HLPR_BAIL_ON_FAILURE(status); + + if(g_takeFirewallCategory) + { + status = WindowsFirewallAcquireFirewallCategory(); + HLPR_BAIL_ON_FAILURE(status); + } +/* + The Action Center APIs are under Non Disclosure Agreement. If you wish to see the sample code + for this, you will need to contact your Microsoft representative and request the sample code. +*/ +/// status = ActionCenterRegisterFirewall(); +/// HLPR_BAIL_ON_FAILURE(status); + + ServiceStatusReportToSCM(SERVICE_RUNNING, + status, + 0); + + HLPR_BAIL_LABEL: + + if(engineHandle) + HlprFwpmEngineClose(&engineHandle); + + if(status != NO_ERROR) + ServiceStatusReportToSCM(SERVICE_STOPPED, + status, + 0); + + return status; +} + +/** + @framework_function="ServiceUninstall" + + Purpose: Remove the WFPSampler service from the Service Control Manager.
+
+ Notes: Uninstalling will remove all WFP objects associated to the WFPSampler service.
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/MS684323.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/MS684330.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/MS682108.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/MS682562.aspx
+*/ +VOID ServiceUninstall() +{ + UINT32 status = NO_ERROR; + SC_HANDLE scmHandle = 0; + SC_HANDLE svcHandle = 0; + HANDLE engineHandle = 0; + SERVICE_STATUS serviceStatus = {0}; + + HlprFwpmEngineOpen(&engineHandle); + if(engineHandle) + { + HlprFwpmFilterRemoveAll(&engineHandle, + &WFPSAMPLER_PROVIDER); + + HlprFwpmCalloutRemoveAll(&engineHandle, + &WFPSAMPLER_PROVIDER); + + HlprFwpmProviderContextRemoveAll(&engineHandle, + &WFPSAMPLER_PROVIDER); + + HlprFwpmSubLayerDelete(&engineHandle, + &WFPSAMPLER_SUBLAYER); + + HlprFwpmProviderDelete(&engineHandle, + &WFPSAMPLER_PROVIDER); + } + + scmHandle = OpenSCManager(0, /// local Machine + SERVICES_ACTIVE_DATABASE, + SC_MANAGER_ALL_ACCESS); + if(scmHandle == 0) + { + status = GetLastError(); + + HlprLogError(L"ServiceUninstall : OpenSCManager() [status: %#x]", + status); + + HLPR_BAIL; + } + + svcHandle = OpenService(scmHandle, + g_pServiceName, + SERVICE_ALL_ACCESS); + if(svcHandle == 0) + { + status = GetLastError(); + + HlprLogError(L"ServiceUninstall : OpenService() [status: %#x]", + status); + + HLPR_BAIL; + } + + if(!ControlService(svcHandle, + SERVICE_CONTROL_STOP, + &serviceStatus)) + { + status = GetLastError(); + + if(status != ERROR_SERVICE_NOT_ACTIVE && + status != ERROR_SHUTDOWN_IN_PROGRESS) + HlprLogError(L"ServiceUninstall : ControlService() [status: %#x]", + status); + } + + if(!DeleteService(svcHandle)) + { + status = GetLastError(); + + HlprLogError(L"ServiceUninstall : DeleteService() [status: %#x]", + status); + + HLPR_BAIL; + } + + status = PrvRegistryDestroyEntries(); + HLPR_BAIL_ON_FAILURE(status); + + HlprLogInfo(L"ServiceUninstall() [status: %#x]", + status); + + HLPR_BAIL_LABEL: + + if(engineHandle) + HlprFwpmEngineClose(&engineHandle); + + HLPR_CLOSE_SERVICE_HANDLE(svcHandle); + + HLPR_CLOSE_SERVICE_HANDLE(scmHandle); + + return; +} + +/** + @framework_function="ServiceInstall" + + Purpose: Add the WFPSampler service to the Service Control Manager and start it.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/MS683197.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/MS684323.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/MS682450.aspx
+*/ +_Success_(return == NO_ERROR) +UINT32 ServiceInstall() +{ + UINT32 status = NO_ERROR; + UINT32 size = 1024; + PWSTR pFileName = 0; + SC_HANDLE scmHandle = 0; + SC_HANDLE svcHandle = 0; + PCWSTR pWindowsFirewall = L"MpsSvc\0\0\0"; + HANDLE engineHandle = 0; + + HLPR_NEW_ARRAY(pFileName, + WCHAR, + size); + HLPR_BAIL_ON_ALLOC_FAILURE(pFileName, + status); + + if(!GetModuleFileName(0, /// this process + pFileName, + size)) + { + status = GetLastError(); + + HlprLogError(L"ServiceInstall : GetModuleFileName() [status: %#x]", + status); + + HLPR_BAIL; + } + + scmHandle = OpenSCManager(0, /// local Machine + SERVICES_ACTIVE_DATABASE, + SC_MANAGER_ALL_ACCESS); + if(scmHandle == 0) + { + status = GetLastError(); + + HlprLogError(L"ServiceInstall : OpenSCManager() [status: %#x]", + status); + + HLPR_BAIL; + } + + svcHandle = CreateService(scmHandle, + g_pServiceName, + g_pServiceDescription, + SERVICE_ALL_ACCESS, + SERVICE_WIN32_OWN_PROCESS, + SERVICE_AUTO_START, + SERVICE_ERROR_CRITICAL, + pFileName, + 0, /// No load ordering group + 0, /// No Tag identifier + pWindowsFirewall, /// Create dependency + 0, /// LocalSystem + 0); /// No password + if(svcHandle == 0) + { + status = GetLastError(); + + HlprLogError(L"ServiceInstall : CreateService() [status: %#x]", + status); + + HLPR_BAIL; + } + + /// Create keys so service can run in SafeMode with Networking + PrvRegistryCreateEntries(); + + status = HlprFwpmEngineOpen(&engineHandle); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmTransactionBegin(engineHandle); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmProviderAdd(engineHandle, + &WFPSAMPLER_PROVIDER, + g_pCompanyName, + g_pBinaryDescription, + g_pServiceName, + FWPM_PROVIDER_FLAG_PERSISTENT); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmSubLayerAdd(engineHandle, + &WFPSAMPLER_SUBLAYER, + L"WFP Sampler's default subLayer", + &WFPSAMPLER_PROVIDER, + 0x7FFF, + FWPM_SUBLAYER_FLAG_PERSISTENT); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmTransactionCommit(engineHandle); + HLPR_BAIL_ON_FAILURE(status); + + HlprLogInfo(L"ServiceInstall() [status: %#x]", + status); + + HLPR_BAIL_LABEL: + + if(engineHandle) + { + if(status != NO_ERROR) + HlprFwpmTransactionAbort(engineHandle); + + HlprFwpmEngineClose(&engineHandle); + } + + HLPR_CLOSE_SERVICE_HANDLE(svcHandle); + + HLPR_CLOSE_SERVICE_HANDLE(scmHandle); + + HLPR_DELETE_ARRAY(pFileName); + + if(status != NO_ERROR) + ServiceUninstall(); + + return status; +} + +/** + @framework_function="ServiceMain" + + Purpose: Entry point for the Service Control Manager to hook into the WFPSampler service.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/MS685138.aspx
+*/ +VOID ServiceMain(_In_ const UINT32 argumentCount, + _In_reads_(argumentCount) PCWSTR ppArguments[]) +{ + UNREFERENCED_PARAMETER(argumentCount); + UNREFERENCED_PARAMETER(ppArguments); + + UINT32 status = NO_ERROR; + + HLPR_NEW(pServiceData, + SERVICE_DATA); + HLPR_BAIL_ON_ALLOC_FAILURE(pServiceData, + status); + + pServiceData->serviceStatusHandle = RegisterServiceCtrlHandler(g_pServiceName, + (LPHANDLER_FUNCTION)ServiceControlHandler); + if(pServiceData->serviceStatusHandle == 0) + { + ServiceEventLogError(L"ServiceMain : RegisterServiceCtrlHandler()", + status); + + HLPR_BAIL; + } + + pServiceData->serviceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; + + status = ServiceInitialize(); + HLPR_BAIL_ON_FAILURE(status); + + for(; + ; + ) + { + status = WaitForSingleObject(pServiceData->serviceStopEventHandle, + INFINITE); + if(status == WAIT_OBJECT_0) + { + ServiceTerminate(); + + break; + } + } + + HLPR_BAIL_LABEL: + + HLPR_DELETE(pServiceData); + + return; +} + +/** + @framework_function="wmain" + + Purpose: Entry point for WFPSamplerService.Exe. Accepts several parameters which are + parsed and dealt with accordingly.
+ -i - installs the WFPSampler service.
+ -u - uninstalls the WFPSampler service and its associated objects.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +int __cdecl wmain(_In_ const int argumentCount, + _In_reads_(argumentCount) PCWSTR pArguments[]) +{ + UINT32 status = NO_ERROR; + SERVICE_TABLE_ENTRY pServiceTable[2] = {0}; + + /// First argument is the executable's name + /// WFPSamplerSvc.exe, so start with next argument + if(argumentCount > 1) + { + PCWSTR* ppCommandLineParameterStrings = (PCWSTR*)&(pArguments[1]); + UINT32 stringCount = argumentCount - 1; + + for(UINT32 index = 0; + index < stringCount; + index++) + { + if(HlprStringsAreEqual(L"-i", + ppCommandLineParameterStrings[index])) + { + status = ServiceInstall(); + HLPR_BAIL_ON_FAILURE(status); + + break; + } + else if(HlprStringsAreEqual(L"-u", + ppCommandLineParameterStrings[index])) + { + ServiceUninstall(); + + HLPR_BAIL; + } + } + } + + pServiceTable[0].lpServiceName = (LPWSTR)g_pServiceName; + pServiceTable[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION)ServiceMain; + + if(!StartServiceCtrlDispatcher(pServiceTable)) + status = GetLastError(); + + HLPR_BAIL_LABEL: + + return status; +} diff --git a/network/trans/WFPSampler/svc/Framework_WFPSamplerService.h b/network/trans/WFPSampler/svc/Framework_WFPSamplerService.h new file mode 100644 index 000000000..573fbd21d --- /dev/null +++ b/network/trans/WFPSampler/svc/Framework_WFPSamplerService.h @@ -0,0 +1,48 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// Framework_WFPSamplerService.h +// +// Abstract: +// This module contains include headers for central area of exportation +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef FRAMEWORK_WFP_SAMPLER_SERVICE_H +#define FRAMEWORK_WFP_SAMPLER_SERVICE_H + +#include /// Include\UM +#include /// Include\UM +#include /// Inc\CRT +#include /// Include\UM +#include /// Include\UM +#include /// Include\Shared +#include /// Include\UM +#include /// Include\UM +#include /// Include\Shared +#include /// Include\Shared +#include /// Include\Shared +#include +#include +#include + +#include "WFPSamplerRPC.h" /// $(OBJ_PATH)\..\idl\$(O) +#include "Framework_WFPSamplerService_Msg.h" /// $(OBJ_PATH)\$(O) +#include "Identifiers.h" /// ..\inc +#include "WFPArrays.h" /// ..\inc +#include "ScenarioData.h" /// ..\inc +#include "HelperFunctions_Include.h" /// ..\lib + +extern BOOLEAN g_isCOMInitialized; + +#endif /// FRAMEWORK_WFP_SAMPLER_SERVICE_H \ No newline at end of file diff --git a/network/trans/WFPSampler/svc/Framework_WFPSamplerService.rc b/network/trans/WFPSampler/svc/Framework_WFPSamplerService.rc new file mode 100644 index 000000000..51785e62f --- /dev/null +++ b/network/trans/WFPSampler/svc/Framework_WFPSamplerService.rc @@ -0,0 +1,38 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// Framework_WFPSamplerService.rc +// +// Abstract: +// Internal resource file for WFPSampler service (WFPSamplerService.exe) +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef FRAMEWORK_WFP_SAMPLER_SERVICE_RESOURCES +#define FRAMEWORK_WFP_SAMPLER_SERVICE_RESOURCES + +#define VER_FILETYPE VFT_APP +#define VER_FILESUBTYPE VFT2_UNKNOWN +#define VER_FILEDESCRIPTION_STR "WFP Sampler Test Service" +#define VER_INTERNALNAME_STR "WFPSamplerService.Exe" +#define VER_ORIGINALFILENAME_STR "WFPSamplerService.Exe" +#define VER_PRODUCT_VERSION_STR "1.0.0.1" + +#include /// Include\UM +#include /// Include\Shared +#include /// Include\Shared + +#include "Framework_WFPSamplerService_Msg.rc" /// $(OBJ_PATH)\$(O) +#include "Framework_WFPSamplerService.res" /// $(OBJ_PATH)\$(O) + +#endif /// FRAMEWORK_WFP_SAMPLER_SERVICE_RESOURCES diff --git a/network/trans/WFPSampler/svc/Framework_WFPSamplerService_Msg.mc b/network/trans/WFPSampler/svc/Framework_WFPSamplerService_Msg.mc new file mode 100644 index 000000000..49521b473 --- /dev/null +++ b/network/trans/WFPSampler/svc/Framework_WFPSamplerService_Msg.mc @@ -0,0 +1,72 @@ +;/////////////////////////////////////////////////////////////////////////////////////////////////// +;/// +;/// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +;/// +;/// Module Name: +;/// Framework_WFPSamplerService_Msg.mc +;/// +;/// Abstract: +;/// Message text for WFPSampler service (WFPSamplerService.exe) +;/// +;/// Author: +;/// Dusty Harper (DHarper) +;/// +;/// Revision History: +;/// +;/// [ Month ][Day] [Year] - [Revision]-[ Comments ] +;/// May 01, 2010 - 1.0 - Creation +;/// +;/// Notes: +;/// A .mc file is compiled by the MC tool to generate a .h file and +;/// a .rc (resource compiler script) file. +;/// +;/// Comments in .mc files start with a ";". +;/// Comment lines are generated directly in the .h file, without the leading ";" +;/// +;/// See mc.hlp for more help on .mc files and the MC tool. +;/// +;/////////////////////////////////////////////////////////////////////////////////////////////////// + +;#ifndef FRAMEWORK_WFP_SAMPLER_SERVICE_MESSAGES +;#define FRAMEWORK_WFP_SAMPLER_SERVICE_MESSAGES + +;/// HEADER SECTION + +SeverityNames = (Success = 0x0:STATUS_SEVERITY_SUCCESS + Informational = 0x1:STATUS_SEVERITY_INFORMATIONAL + Warning = 0x2:STATUS_SEVERITY_WARNING + Error = 0x3:STATUS_SEVERITY_ERROR + ) + +FacilityNames = (System = 0x0:FACILITY_SYSTEM + Runtime = 0x2:FACILITY_RUNTIME + Stubs = 0x3:FACILITY_STUBS + Io = 0x4:FACILITY_IO_ERROR_CODE + ) + +LanguageNames = (English = 0x409:MSG00409 + ) + +;/// CATEGORY EVENTS + +MessageIdTypedef = UINT16 + +MessageId = 0x1 +SymbolicName = NETWORK_CATEGORY +Language = English +Network Events +. + +;/// MESSAGE DEFINITIONS + +MessageIdTypedef = UINT32 + +MessageId = 0x1 +Severity = Error +Facility = Runtime +SymbolicName = SVC_ERROR +Language = English +An error has occurred (%2) +. + +;#endif /// FRAMEWORK_WFP_SAMPLER_SERVICE_MESSAGES \ No newline at end of file diff --git a/network/trans/WFPSampler/svc/Framework_WindowsFirewall.cpp b/network/trans/WFPSampler/svc/Framework_WindowsFirewall.cpp new file mode 100644 index 000000000..cade7d774 --- /dev/null +++ b/network/trans/WFPSampler/svc/Framework_WindowsFirewall.cpp @@ -0,0 +1,373 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// Framework_WindowsFirewall.cpp +// +// Abstract: +// This module contains functions for interoperating with the Microsoft Windows Firewall +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "Framework_WFPSamplerService.h" + +/// Module's Global variables +namespace +{ + IUnknown* pFirewallRegistrationHandle = 0; + THREAD_DATA* pWFNotifyThread = 0; + SC_HANDLE scmHandle = 0; + SC_HANDLE mpsSvcHandle = 0; +}; + +/// + +/// Functions implemented in Framework_WFPSamplerSvc.cpp + +_Success_(return == NO_ERROR) +UINT32 ServiceStatusReportToSCM(_In_ UINT32 currentState, + _In_ UINT32 exitCode, + _In_ UINT32 waitHint); + +VOID ServiceEventLogError(_In_ PCWSTR pFunctionName, + _In_ UINT32 errorCode); + +/// + +/// Forward Declarations + +_Success_(return == NO_ERROR) +UINT32 WindowsFirewallAcquireFirewallCategory(); + +/// + +/** + @framework_function="WindowsFirewallNotification" + + Purpose: Notification used to take firewall categories from Windows Firewall when MPSSvc + transitions to a running
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/MS685947.aspx
+*/ +VOID CALLBACK WindowsFirewallNotification(_In_ VOID* pContext) +{ + ASSERT(pContext); + + SERVICE_NOTIFY* pServiceNotify = (SERVICE_NOTIFY*)pContext; + THREAD_DATA* pThreadData = (THREAD_DATA*)pServiceNotify->pContext; + + if(pServiceNotify->dwNotificationStatus == NO_ERROR) + { + if(pServiceNotify->ServiceStatus.dwCurrentState == SERVICE_RUNNING) + { + WindowsFirewallAcquireFirewallCategory(); + + HlprThreadStop(&pThreadData); + } + } + else if(pServiceNotify->dwNotificationStatus == ERROR_SERVICE_MARKED_FOR_DELETE) + HlprThreadStop(&pThreadData); + + return; +} + +/** + @framework_function="WindowsFirewallNotifyThreadStartRoutine" + + Purpose: New thread which waits for a notification that MPSSvc has started.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/MS686736.aspx
+*/ +_Success_(return == NO_ERROR) +DWORD WindowsFirewallNotifyThreadStartRoutine(_In_ LPVOID lpThreadParameter) +{ + ASSERT(lpThreadParameter); + + UINT32 status = NO_ERROR; + THREAD_DATA* pThreadData = (THREAD_DATA*)lpThreadParameter; + SERVICE_NOTIFY svcNotify = {0}; + + HlprEventSet(pThreadData->threadStartEvent); + + svcNotify.dwVersion = SERVICE_NOTIFY_STATUS_CHANGE; + svcNotify.pfnNotifyCallback = WindowsFirewallNotification; + svcNotify.pContext = pWFNotifyThread; + + status = HlprServiceNotificationStateChangeRegister(L"MPSSvc", + &svcNotify, + SERVICE_NOTIFY_RUNNING, + &scmHandle, + &mpsSvcHandle); + HLPR_BAIL_ON_FAILURE(status); + + status = WaitForSingleObjectEx(mpsSvcHandle, + INFINITE, + TRUE); + if(status != WAIT_IO_COMPLETION) + { + if(status == WAIT_FAILED) + status = GetLastError(); + + HlprLogError(L"WindowsFirewallNotifyThreadStartRoutine : WaitForSingleObjectEx() [status: %#x]", + status); + } + + HLPR_BAIL_LABEL: + + HLPR_CLOSE_SERVICE_HANDLE(mpsSvcHandle); + + HLPR_CLOSE_SERVICE_HANDLE(scmHandle); + + return status; +} + +/** + @framework_function="WindowsFirewallReleaseFirewallCategory" + + Purpose: Relinquish ownership of the Firewall Category back to Windows Firewall and + unregister as a Firewall product.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/DD607247.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/DD607255.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/DD607259.aspx
+*/ +VOID WindowsFirewallReleaseFirewallCategory() +{ + if(pFirewallRegistrationHandle) + { + pFirewallRegistrationHandle->Release(); + + pFirewallRegistrationHandle = 0; + } + + HLPR_CLOSE_SERVICE_HANDLE(mpsSvcHandle); + + HLPR_CLOSE_SERVICE_HANDLE(scmHandle); + + HlprThreadStop(&pWFNotifyThread); + + return; +} + +/** + @framework_function="WindowsFirewallAcquireFirewallCategory" + + Purpose: Take ownership from Windows Firewall of the Firewall Category and register as a + Firewall product so as to be displayed in the Windows Firewall User Interface.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/DD607247.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/DD607255.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/DD607259.aspx
+*/ +_Success_(return == NO_ERROR) +UINT32 WindowsFirewallAcquireFirewallCategory() +{ + UINT32 status = NO_ERROR; + + if(HlprServiceQueryState(L"MPSSvc") == SERVICE_RUNNING) + { + BSTR pProductName = SysAllocString(g_pServiceName); + INetFwProduct* pNetFwProduct = 0; + INetFwProducts* pNetFwProducts = 0; + SAFEARRAY* pFirewallCategory = 0; + SAFEARRAY* pBootTimeCategory = 0; + SAFEARRAYBOUND boundary = {0}; + VARIANT firewallCategory = {0}; + VARIANT boottimeCategory = {0}; + + if(pProductName == 0) + { + status = ERROR_GEN_FAILURE; + + ServiceEventLogError(L"WindowsFirewallAcquireFirewallCategory : SysAllocString()", + status); + + HLPR_BAIL; + } + + boundary.lLbound = 0; + boundary.cElements = 1; + + pBootTimeCategory = SafeArrayCreate(VT_VARIANT, + 1, + &boundary); + if(pBootTimeCategory == 0) + { + status = ERROR_GEN_FAILURE; + + ServiceEventLogError(L"WindowsFirewallAcquireFirewallCategory : SafeArrayCreate()", + status); + + HLPR_BAIL; + } + + V_VT((VARIANT*)pBootTimeCategory->pvData) = VT_I4; + V_I4((VARIANT*)pBootTimeCategory->pvData) = NET_FW_RULE_CATEGORY_BOOT; + + VariantInit(&firewallCategory); + + V_VT(&boottimeCategory) = VT_ARRAY | VT_VARIANT; + V_ARRAY(&boottimeCategory) = pBootTimeCategory; + + pFirewallCategory = SafeArrayCreate(VT_VARIANT, + 1, + &boundary); + if(pFirewallCategory == 0) + { + status = ERROR_GEN_FAILURE; + + ServiceEventLogError(L"WindowsFirewallAcquireFirewallCategory : SafeArrayCreate()", + status); + + HLPR_BAIL; + } + + V_VT((VARIANT*)pFirewallCategory->pvData) = VT_I4; + V_I4((VARIANT*)pFirewallCategory->pvData) = NET_FW_RULE_CATEGORY_FIREWALL; + + VariantInit(&firewallCategory); + + V_VT(&firewallCategory) = VT_ARRAY | VT_VARIANT; + V_ARRAY(&firewallCategory) = pFirewallCategory; + + /// Initialize the COM library + if(!g_isCOMInitialized) + { + status = CoInitializeEx(0, + COINIT_MULTITHREADED); + if(FAILED(status)) + { + ServiceEventLogError(L"WindowsFirewallAcquireFirewallCategory : CoInitializeEx()", + status); + + HLPR_BAIL; + } + + g_isCOMInitialized = TRUE; + } + + /// Create an instance of the NetFwProduct class + status = CoCreateInstance(__uuidof(NetFwProduct), + 0, + CLSCTX_INPROC_SERVER, + __uuidof(INetFwProduct), + (LPVOID*)&pNetFwProduct); + if(FAILED(status)) + { + ServiceEventLogError(L"WindowsFirewallAcquireFirewallCategory : CoCreateInstance()", + status); + + HLPR_BAIL; + } + + /// Set the DisplayName + status = pNetFwProduct->put_DisplayName(pProductName); + if(FAILED(status)) + { + ServiceEventLogError(L"WindowsFirewallAcquireFirewallCategory : INetFwProduct::put_DisplayName()", + status); + + HLPR_BAIL; + } + + /// Take the category + status = pNetFwProduct->put_RuleCategories(boottimeCategory); + if(FAILED(status)) + { + ServiceEventLogError(L"WindowsFirewallAcquireFirewallCategory : INetFwProduct::put_RuleCategories()", + status); + + HLPR_BAIL; + } + + status = pNetFwProduct->put_RuleCategories(firewallCategory); + if(FAILED(status)) + { + ServiceEventLogError(L"WindowsFirewallAcquireFirewallCategory : INetFwProduct::put_RuleCategories()", + status); + + HLPR_BAIL; + } + + /// Create an instance of the NetFwProducts class + status = CoCreateInstance(__uuidof(NetFwProducts), + 0, + CLSCTX_INPROC_SERVER, + __uuidof(INetFwProducts), + (LPVOID*)&pNetFwProducts); + if(FAILED(status)) + { + ServiceEventLogError(L"WindowsFirewallAcquireFirewallCategory : CoCreateInstance()", + status); + + HLPR_BAIL; + } + + /// Register as a Firewall Product + if(pFirewallRegistrationHandle == 0) + { + status = pNetFwProducts->Register(pNetFwProduct, + &pFirewallRegistrationHandle); + if(FAILED(status)) + { + ServiceEventLogError(L"WindowsFirewallAcquireFirewallCategory : INetFwProducts::Register()", + status); + + HLPR_BAIL; + } + } + + status = NO_ERROR; + + HLPR_BAIL_LABEL: + + if(FAILED(status)) + WindowsFirewallReleaseFirewallCategory(); + + if(pNetFwProducts) + pNetFwProducts->Release(); + + if(pNetFwProduct) + pNetFwProduct->Release(); + + if(pProductName) + SysFreeString(pProductName); + } + else + { + HLPR_NEW(pWFNotifyThread, + THREAD_DATA); + if(pWFNotifyThread) + { + pWFNotifyThread->threadStartRoutine = (LPTHREAD_START_ROUTINE)WindowsFirewallNotifyThreadStartRoutine; + + status = HlprThreadStart(pWFNotifyThread); + } + else + status = ERROR_OUTOFMEMORY; + } + + if(HlprServiceQueryState(L"WFPSampler") == SERVICE_START_PENDING) + ServiceStatusReportToSCM(SERVICE_START_PENDING, + status, + 2500); + + return status; +} diff --git a/network/trans/WFPSampler/svc/Framework_WindowsFirewall.h b/network/trans/WFPSampler/svc/Framework_WindowsFirewall.h new file mode 100644 index 000000000..7472e650e --- /dev/null +++ b/network/trans/WFPSampler/svc/Framework_WindowsFirewall.h @@ -0,0 +1,30 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// Framework_WindowsFirewall.h +// +// Abstract: +// This module contains prototypes for functions for interoperating with the Microsoft Windows +// Firewall +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef FRAMEWORK_WINDOWS_FIREWALL_H +#define FRAMEWORK_WINDOWS_FIREWALL_H + +VOID WindowsFirewallReleaseFirewallCategory(); + +_Success_(return == NO_ERROR) +UINT32 WindowsFirewallAcquireFirewallCategory(); + +#endif /// FRAMEWORK_WINDOWS_FIREWALL_H \ No newline at end of file diff --git a/network/trans/WFPSampler/svc/Scenarios_AdvancedPacketInjection.cpp b/network/trans/WFPSampler/svc/Scenarios_AdvancedPacketInjection.cpp new file mode 100644 index 000000000..9cd69d1c3 --- /dev/null +++ b/network/trans/WFPSampler/svc/Scenarios_AdvancedPacketInjection.cpp @@ -0,0 +1,401 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// Scenarios_AdvancedPacketInjection.cpp +// +// Abstract: +// This module contains functions which implements the ADVANCED_PACKET_INJECTION scenarios. +// +// Naming Convention: +// +// +// +// +// i.e. +// +// +// { +// - Function is likely visible to other modules +// Prv - Function is private to this module. +// } +// +// { +// ScenarioAdvancedPacketInjection - Function pertains to all of the Advanced Packet +// Injection Scenarios +// RPC - Function is and RPC entry point. +// } +// +// { +// Add - Function adds objects +// Remove - Function removes objects +// Invoke - Function implements the scenario based on parameters +// passed from the commandline interface +// (WFPSampler.exe). +// } +// +// { +// FwpmObjects - Function acts on WFP objects. +// ScenarioAdvancedPacketInjection - Function pertains to all of the Advanced Packet +// Injection Scenarios +// } +// +// Private Functions: +// PrvScenarioAdvancedPacketInjectionAddFwpmObjects(), +// PrvScenarioAdvancedPacketInjectionDeleteFwpmObjects(), +// ScenarioAdvancedStreamInjectionAdd(), +// ScenarioAdvancedStreamInjectionRemove(), +// +// Public Functions: +// RPCInvokeScenarioAdvancedPacketInjection(), +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// December 13, 2013 - 1.1 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "Framework_WFPSamplerService.h" /// . + +/** + @private_function="PrvScenarioAdvancedPacketInjectionDeleteFwpmObjects" + + Purpose: Function that disables the SCENARIO_ADVANCED_PACKET_INJECTION scenarios.
+
+ Notes: Scenario removes the filters using specified filtering conditions at the specified + layer. Associated callouts are removed as well.
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 PrvScenarioAdvancedPacketInjectionDeleteFwpmObjects(_In_ const FWPM_FILTER* pFilter) +{ + ASSERT(pFilter); + + UINT32 status = NO_ERROR; + HANDLE engineHandle = 0; + HANDLE enumHandle = 0; + UINT32 entryCount = 0; + FWPM_FILTER** ppFilters = 0; + FWPM_FILTER_CONDITION* pFilterConditions = 0; + UINT32 numFilterConditions = 0; + FWPM_FILTER_ENUM_TEMPLATE filterEnumTemplate = {0}; + FWPM_PROVIDER_CONTEXT_ENUM_TEMPLATE providerContextEnumTemplate = {0}; + GUID calloutKey = {0}; + + HlprFwpmFilterConditionPrune(pFilter->filterCondition, + pFilter->numFilterConditions, + &pFilterConditions, + &numFilterConditions); + + calloutKey = WFPSAMPLER_CALLOUT_ADVANCED_PACKET_INJECTION; + calloutKey.Data4[7] = HlprFwpmLayerGetIDByKey(&(pFilter->layerKey)); /// Uniquely identifies the callout used + + providerContextEnumTemplate.providerContextType = FWPM_GENERAL_CONTEXT; + + filterEnumTemplate.providerKey = (GUID*)&WFPSAMPLER_PROVIDER; + filterEnumTemplate.layerKey = pFilter->layerKey; + filterEnumTemplate.enumType = FWP_FILTER_ENUM_FULLY_CONTAINED; + filterEnumTemplate.flags = FWP_FILTER_ENUM_FLAG_INCLUDE_BOOTTIME | + FWP_FILTER_ENUM_FLAG_INCLUDE_DISABLED; + filterEnumTemplate.numFilterConditions = pFilterConditions ? numFilterConditions : pFilter->numFilterConditions; + filterEnumTemplate.filterCondition = pFilterConditions ? pFilterConditions : pFilter->filterCondition; + filterEnumTemplate.providerContextTemplate = &providerContextEnumTemplate; + filterEnumTemplate.actionMask = FWP_ACTION_FLAG_CALLOUT; + filterEnumTemplate.calloutKey = &calloutKey; + + status = HlprFwpmEngineOpen(&engineHandle); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmFilterCreateEnumHandle(engineHandle, + &filterEnumTemplate, + &enumHandle); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmFilterEnum(engineHandle, + enumHandle, + 0xFFFFFFFF, + &ppFilters, + &entryCount); + HLPR_BAIL_ON_FAILURE(status); + + if(ppFilters) + { + for(UINT32 filterIndex = 0; + filterIndex < entryCount; + filterIndex++) + { + HlprFwpmFilterDeleteByKey(engineHandle, + &(ppFilters[filterIndex]->filterKey)); + + HlprFwpmCalloutDeleteByKey(engineHandle, + &(ppFilters[filterIndex]->action.calloutKey)); + + if(ppFilters[filterIndex]->flags & FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT) + HlprFwpmProviderContextDeleteByKey(engineHandle, + &(ppFilters[filterIndex]->providerContextKey)); + } + + FwpmFreeMemory((VOID**)&ppFilters); + } + + HLPR_BAIL_LABEL: + + if(engineHandle) + { + if(enumHandle) + HlprFwpmFilterDestroyEnumHandle(engineHandle, + &enumHandle); + + HlprFwpmEngineClose(&engineHandle); + } + + HLPR_DELETE_ARRAY(pFilterConditions); + + return status; +} + +/** + @private_function="PrvScenarioAdvancedPacketInjectionAddFwpmObjects" + + Purpose: Function that enables the SCENARIO_ADVANCED_PACKET_INJECTION scenarios.
+
+ Notes: Scenario adds a filter using specified filtering conditions to the specified layer. + This filter is associated with WFPSampler's default sublayer and provider. The + appropriate callout is then added and associated with the filter.
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 PrvScenarioAdvancedPacketInjectionAddFwpmObjects(_In_ const FWPM_FILTER* pFilter, + _In_ const PC_ADVANCED_PACKET_INJECTION_DATA* pPCAdvancedPacketInjectionData) +{ + ASSERT(pFilter); + ASSERT(pPCAdvancedPacketInjectionData); + + UINT32 status = NO_ERROR; + + if(pFilter->layerKey == FWPM_LAYER_INBOUND_IPPACKET_V4 || + pFilter->layerKey == FWPM_LAYER_INBOUND_IPPACKET_V6 || + pFilter->layerKey == FWPM_LAYER_OUTBOUND_IPPACKET_V4 || + pFilter->layerKey == FWPM_LAYER_OUTBOUND_IPPACKET_V6 || + pFilter->layerKey == FWPM_LAYER_IPFORWARD_V4 || + pFilter->layerKey == FWPM_LAYER_IPFORWARD_V6 || + pFilter->layerKey == FWPM_LAYER_INBOUND_TRANSPORT_V4 || + pFilter->layerKey == FWPM_LAYER_INBOUND_TRANSPORT_V6 || + pFilter->layerKey == FWPM_LAYER_OUTBOUND_TRANSPORT_V4 || + pFilter->layerKey == FWPM_LAYER_OUTBOUND_TRANSPORT_V6 || + pFilter->layerKey == FWPM_LAYER_DATAGRAM_DATA_V4 || + pFilter->layerKey == FWPM_LAYER_DATAGRAM_DATA_V6 || + pFilter->layerKey == FWPM_LAYER_INBOUND_ICMP_ERROR_V4 || + pFilter->layerKey == FWPM_LAYER_INBOUND_ICMP_ERROR_V6 || + pFilter->layerKey == FWPM_LAYER_OUTBOUND_ICMP_ERROR_V4 || + pFilter->layerKey == FWPM_LAYER_OUTBOUND_ICMP_ERROR_V6 || + pFilter->layerKey == FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 || + pFilter->layerKey == FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 || + pFilter->layerKey == FWPM_LAYER_ALE_AUTH_CONNECT_V4 || + pFilter->layerKey == FWPM_LAYER_ALE_AUTH_CONNECT_V6 || + pFilter->layerKey == FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4 || + pFilter->layerKey == FWPM_LAYER_ALE_FLOW_ESTABLISHED_V6 + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + || + pFilter->layerKey == FWPM_LAYER_STREAM_PACKET_V4 || + pFilter->layerKey == FWPM_LAYER_STREAM_PACKET_V6 + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + || + pFilter->layerKey == FWPM_LAYER_INBOUND_MAC_FRAME_ETHERNET || + pFilter->layerKey == FWPM_LAYER_OUTBOUND_MAC_FRAME_ETHERNET || + pFilter->layerKey == FWPM_LAYER_INBOUND_MAC_FRAME_NATIVE || + pFilter->layerKey == FWPM_LAYER_OUTBOUND_MAC_FRAME_NATIVE || + pFilter->layerKey == FWPM_LAYER_INGRESS_VSWITCH_ETHERNET || + pFilter->layerKey == FWPM_LAYER_EGRESS_VSWITCH_ETHERNET + +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + ) + { + HANDLE engineHandle = 0; + FWP_BYTE_BLOB byteBlob = {0}; + FWPM_PROVIDER_CONTEXT providerContext = {0}; + FWPM_CALLOUT callout = {0}; + FWPM_FILTER filter = {0}; + + RtlCopyMemory(&filter, + pFilter, + sizeof(FWPM_FILTER)); + + status = HlprGUIDPopulate(&(providerContext.providerContextKey)); + HLPR_BAIL_ON_FAILURE(status); + + providerContext.displayData.name = L"WFPSampler's Advanced Packet Injection ProviderContext"; + providerContext.providerKey = (GUID*)&WFPSAMPLER_PROVIDER; + providerContext.type = FWPM_GENERAL_CONTEXT; + providerContext.dataBuffer = &byteBlob; + providerContext.dataBuffer->size = sizeof(PC_ADVANCED_PACKET_INJECTION_DATA); + providerContext.dataBuffer->data = (UINT8*)pPCAdvancedPacketInjectionData; + + callout.calloutKey = WFPSAMPLER_CALLOUT_ADVANCED_PACKET_INJECTION; + callout.calloutKey.Data4[7] = HlprFwpmLayerGetIDByKey(&(filter.layerKey)); /// Uniquely identifies the callout used + callout.displayData.name = L"WFPSampler's Advanced Packet Injection Callout"; + callout.displayData.description = L"Causes callout invocation which blindly injects traffic back"; + callout.flags = FWPM_CALLOUT_FLAG_USES_PROVIDER_CONTEXT; + callout.providerKey = (GUID*)&WFPSAMPLER_PROVIDER; + callout.applicableLayer = filter.layerKey; + + status = HlprGUIDPopulate(&(filter.filterKey)); + HLPR_BAIL_ON_FAILURE(status); + + filter.flags |= FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT; + filter.providerKey = (GUID*)&WFPSAMPLER_PROVIDER; + filter.weight.type = FWP_UINT8; + filter.weight.uint8 = 0xF; + filter.action.type = FWP_ACTION_CALLOUT_TERMINATING; + filter.action.calloutKey = callout.calloutKey; + filter.providerContextKey = providerContext.providerContextKey; + + if(filter.flags & FWPM_FILTER_FLAG_BOOTTIME || + filter.flags & FWPM_FILTER_FLAG_PERSISTENT) + { + providerContext.flags |= FWPM_PROVIDER_CONTEXT_FLAG_PERSISTENT; + + callout.flags |= FWPM_CALLOUT_FLAG_PERSISTENT; + } + + status = HlprFwpmEngineOpen(&engineHandle); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmTransactionBegin(engineHandle); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmProviderContextAdd(engineHandle, + &providerContext); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmCalloutAdd(engineHandle, + &callout); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmFilterAdd(engineHandle, + &filter); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmTransactionCommit(engineHandle); + HLPR_BAIL_ON_FAILURE(status); + + HLPR_BAIL_LABEL: + + if(engineHandle) + { + if(status != NO_ERROR) + HlprFwpmTransactionAbort(engineHandle); + + HlprFwpmEngineClose(&engineHandle); + } + } + else + status = (UINT32)FWP_E_INCOMPATIBLE_LAYER; + + return status; +} + +/** + @scenario_function="ScenarioAdvancedPacketInjectionRemove" + + Purpose: Function that removes corresponding objects for a previously added + SCENARIO_ADVANCED_PACKET_INJECTION.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 ScenarioAdvancedPacketInjectionRemove(_In_ const FWPM_FILTER* pFilter) +{ + ASSERT(pFilter); + + return PrvScenarioAdvancedPacketInjectionDeleteFwpmObjects(pFilter); +} + +/** + @scenario_function="ScenarioAdvancedPacketInjectionAdd" + + Purpose: Scenario which will blindly reinject the classified traffic.
+
+ Notes: Adds a filter which references one of the + WFPSAMPLER_CALLOUT_ADVANCED_PACKET_INJECTION callouts for the provided layer.
+
+ No data modification is made to the traffic, and checks are in place to prevent + infinite reinjection of the traffic.
+
+ Ideal usage is to implement in the presence of a 3rd party firewall to see how they + coexist with another provider performing injection.
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 ScenarioAdvancedPacketInjectionAdd(_In_ const FWPM_FILTER* pFilter, + _In_ const PC_ADVANCED_PACKET_INJECTION_DATA* pPCAdvancedPacketInjectionData) +{ + + ASSERT(pFilter); + ASSERT(pPCAdvancedPacketInjectionData); + + return PrvScenarioAdvancedPacketInjectionAddFwpmObjects(pFilter, + pPCAdvancedPacketInjectionData); +} + +/** + @rpc_function="RPCInvokeScenarioAdvancedPacketInjection" + + Purpose: RPC exposed function used to dipatch the scenario routines for + SCENARIO_ADVANCED_PACKET_INJECTION.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +/* [fault_status][comm_status] */ +error_status_t RPCInvokeScenarioAdvancedPacketInjection(/* [in] */ handle_t rpcBindingHandle, + /* [in] */ WFPSAMPLER_SCENARIO scenario, + /* [in] */ FWPM_CHANGE_TYPE changeType, + /* [ref][in] */ __RPC__in const FWPM_FILTER0* pFilter, + /* [unique][in] */ __RPC__in_opt const PC_ADVANCED_PACKET_INJECTION_DATA* pPCAdvancedPacketInjectionData) +{ + UNREFERENCED_PARAMETER(rpcBindingHandle); + UNREFERENCED_PARAMETER(scenario); + + ASSERT(pFilter); + ASSERT(scenario == SCENARIO_ADVANCED_PACKET_INJECTION); + ASSERT(changeType < FWPM_CHANGE_TYPE_MAX); + + UINT32 status = NO_ERROR; + + if(changeType == FWPM_CHANGE_ADD) + { + if(pPCAdvancedPacketInjectionData) + status = ScenarioAdvancedPacketInjectionAdd(pFilter, + pPCAdvancedPacketInjectionData); + else + { + status = ERROR_INVALID_PARAMETER; + + HlprLogError(L"RpcInvokeScenarioAdvancedPacketInjection() [status: %#x][pPCAdvancedPacketInjectionData: %#x]", + status, + pPCAdvancedPacketInjectionData); + } + } + else + status = ScenarioAdvancedPacketInjectionRemove(pFilter); + + return status; +} diff --git a/network/trans/WFPSampler/svc/Scenarios_AppContainers.cpp b/network/trans/WFPSampler/svc/Scenarios_AppContainers.cpp new file mode 100644 index 000000000..e9ed9f4c6 --- /dev/null +++ b/network/trans/WFPSampler/svc/Scenarios_AppContainers.cpp @@ -0,0 +1,764 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// Scenarios_AppContainers.cpp +// +// Abstract: +// This module contains functions which implements the APPLICATION_CONTAINER scenarios. +// +// Naming Convention: +// +// +// +// +// i.e. +// +// +// { +// - Function is likely visible to other modules +// Prv - Function is private to this module. +// } +// +// { +// ScenarioAppContainer - Function pertains to all of the Application Container +// Scenarios +// RPC - Function is and RPC entry point. +// } +// +// { +// Add - Function adds objects +// Remove - Function removes objects +// Invoke - Function implements the scenario based on parameters +// passed from the commandline interface (WFPSampler.exe). +// } +// +// { +// FwpmObjects - Function acts on WFP objects. +// ScenarioAppContainer - Function pertains to all of the Application Container +// Scenarios +// } +// +// Public Functions: +// RPCInvokeScenarioAppContainer(). +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Enhance function declaration for IntelliSense +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "Framework_WFPSamplerService.h" /// . + +#if(NTDDI_VERSION >= NTDDI_WIN8) + +#include + +static BOOLEAN scenarioConfigured = FALSE; +static HANDLE registrationHandle = 0; + +/** + @private_function="PrvScenarioAppContainerDeleteFwpmObjects" + + Purpose:
+
+ Notes: Function is overloaded.
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 PrvScenarioAppContainerDeleteFwpmObjects(_In_ const SID* pPackageID, + _In_ const SID* pUserID) +{ + UNREFERENCED_PARAMETER(pUserID); + + ASSERT(pPackageID); + ASSERT(pUserID); + + UINT32 status = NO_ERROR; +/// UINT32 sidSize = 0; + HANDLE engineHandle = 0; + const GUID pLayerKeys[] = {FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4, + FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6, + FWPM_LAYER_ALE_AUTH_CONNECT_V4, + FWPM_LAYER_ALE_AUTH_CONNECT_V6}; + const UINT8 NUM_CONDITIONS = 1; /// 2 + const UINT8 NUM_OBJECTS = RTL_NUMBER_OF(pLayerKeys); + FWPM_FILTER_CONDITION pFilterConditions[NUM_CONDITIONS] = {0}; + + pFilterConditions[0].fieldKey = FWPM_CONDITION_ALE_PACKAGE_ID; + pFilterConditions[0].matchType = FWP_MATCH_EQUAL; + pFilterConditions[0].conditionValue.type = FWP_SID; + pFilterConditions[0].conditionValue.sid = (SID*)pPackageID; + +/// pFilterConditions[1].fieldKey = FWPM_CONDITION_ALE_USER_ID; +/// pFilterConditions[1].matchType = FWP_MATCH_EQUAL; +/// pFilterConditions[1].conditionValue.type = FWP_SECURITY_DESCRIPTOR_TYPE; +/// pFilterConditions[1].conditionValue.sd = ; + + status = HlprFwpmEngineOpen(&engineHandle); + HLPR_BAIL_ON_FAILURE(status); + + for(UINT32 objectIndex = 0; + objectIndex < NUM_OBJECTS; + objectIndex++) + { + UINT32 entryCount = 0; + FWPM_FILTER** ppFilters = 0; + HANDLE enumHandle = 0; + FWPM_FILTER_ENUM_TEMPLATE filterEnumTemplate = {0}; + + filterEnumTemplate.providerKey = (GUID*)&WFPSAMPLER_PROVIDER; + filterEnumTemplate.layerKey = pLayerKeys[objectIndex]; + filterEnumTemplate.enumType = FWP_FILTER_ENUM_FULLY_CONTAINED; + filterEnumTemplate.flags = FWP_FILTER_ENUM_FLAG_INCLUDE_BOOTTIME | + FWP_FILTER_ENUM_FLAG_INCLUDE_DISABLED; + filterEnumTemplate.numFilterConditions = NUM_CONDITIONS; + filterEnumTemplate.filterCondition = pFilterConditions; + filterEnumTemplate.actionMask = 0xFFFFFFFF; + + status = HlprFwpmFilterCreateEnumHandle(engineHandle, + &filterEnumTemplate, + &enumHandle); + HLPR_BAIL_ON_FAILURE_WITH_LABEL(status, + HLPR_BAIL_LABEL_2); + + status = HlprFwpmFilterEnum(engineHandle, + enumHandle, + 0xFFFFFFFF, + &ppFilters, + &entryCount); + HLPR_BAIL_ON_FAILURE_WITH_LABEL(status, + HLPR_BAIL_LABEL_2); + + if(ppFilters) + { + for(UINT32 filterIndex = 0; + filterIndex < entryCount; + filterIndex++) + { + HlprFwpmFilterDeleteByKey(engineHandle, + &(ppFilters[filterIndex]->filterKey)); + } + + FwpmFreeMemory((VOID**)&ppFilters); + } + + HLPR_BAIL_LABEL_2: + + if(enumHandle) + HlprFwpmFilterDestroyEnumHandle(engineHandle, + &enumHandle); + } + + HLPR_BAIL_LABEL: + + HlprFwpmEngineClose(&engineHandle); + + return status; +} + +/** + @private_function="PrvScenarioAppContainerDeleteFwpmObjects" + + Purpose:
+
+ Notes: Function is overloaded.
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 PrvScenarioAppContainerDeleteFwpmObjects() +{ + UINT32 status = NO_ERROR; + UINT32 sidSize = 0; + HANDLE engineHandle = 0; + const GUID pLayerKeys[] = {FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4, + FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6, + FWPM_LAYER_ALE_AUTH_CONNECT_V4, + FWPM_LAYER_ALE_AUTH_CONNECT_V6}; + const UINT32 NUM_OBJECTS = RTL_NUMBER_OF(pLayerKeys); + FWPM_FILTER_CONDITION filterCondition = {0}; + + /// Only App Containers have a valid (non-NULL) SID for the ALE_PACKAGE_ID + filterCondition.fieldKey = FWPM_CONDITION_ALE_PACKAGE_ID; + filterCondition.matchType = FWP_MATCH_NOT_EQUAL; + filterCondition.conditionValue.type = FWP_SID; + +#pragma warning(push) +#pragma warning(disable: 6388) /// filterCondition.conditionValue.sid guaranteed to be 0 due to ZeroMemory call + + status = HlprSIDGetWellKnown(WinNullSid, + &(filterCondition.conditionValue.sid), + &sidSize); + HLPR_BAIL_ON_FAILURE(status); + +#pragma warning(pop) + + status = HlprFwpmEngineOpen(&engineHandle); + HLPR_BAIL_ON_FAILURE(status); + + for(UINT32 objectIndex = 0; + objectIndex < NUM_OBJECTS; + objectIndex++) + { + HANDLE enumHandle = 0; + UINT32 entryCount = 0; + FWPM_FILTER** ppFilters = 0; + FWPM_FILTER_ENUM_TEMPLATE filterEnumTemplate = {0}; + + filterEnumTemplate.providerKey = (GUID*)&WFPSAMPLER_PROVIDER; + filterEnumTemplate.layerKey = pLayerKeys[objectIndex]; + filterEnumTemplate.enumType = FWP_FILTER_ENUM_FULLY_CONTAINED; + filterEnumTemplate.flags = FWP_FILTER_ENUM_FLAG_INCLUDE_BOOTTIME | + FWP_FILTER_ENUM_FLAG_INCLUDE_DISABLED; + filterEnumTemplate.numFilterConditions = 1; + filterEnumTemplate.filterCondition = &filterCondition; + filterEnumTemplate.actionMask = 0xFFFFFFFF; + + status = HlprFwpmFilterCreateEnumHandle(engineHandle, + &filterEnumTemplate, + &enumHandle); + HLPR_BAIL_ON_FAILURE_WITH_LABEL(status, + HLPR_BAIL_LABEL_2); + + status = HlprFwpmFilterEnum(engineHandle, + enumHandle, + 0xFFFFFFFF, + &ppFilters, + &entryCount); + HLPR_BAIL_ON_FAILURE_WITH_LABEL(status, + HLPR_BAIL_LABEL_2); + + if(ppFilters) + { + for(UINT32 filterIndex = 0; + filterIndex < entryCount; + filterIndex++) + { + HlprFwpmFilterDeleteByKey(engineHandle, + &(ppFilters[filterIndex]->filterKey)); + } + + FwpmFreeMemory((VOID**)&ppFilters); + } + + HLPR_BAIL_LABEL_2: + + if(enumHandle) + HlprFwpmFilterDestroyEnumHandle(engineHandle, + &enumHandle); + } + + HLPR_BAIL_LABEL: + + HlprSIDDestroy(&(filterCondition.conditionValue.sid)); + + HlprFwpmEngineClose(&engineHandle); + + return status; +} + +/** + @private_function="PrvScenarioAppContainerAddFwpmObjects" + + Purpose: Add filters for specific containers.
+
+ Notes: Function is overloaded.
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 PrvScenarioAppContainerAddFwpmObjects(_In_ const SID* pPackageID, + _In_ const SID* pUserID, + _In_opt_ PCWSTR pDisplayName, + _In_ BOOLEAN persistent = TRUE, + _In_ BOOLEAN bootTime = FALSE) +{ + UNREFERENCED_PARAMETER(pUserID); + + ASSERT(pPackageID); + ASSERT(pUserID); + + UINT32 status = NO_ERROR; +/// UINT32 sidSize = 0; + HANDLE engineHandle = 0; + const GUID pLayerKeys[] = {FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4, + FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6, + FWPM_LAYER_ALE_AUTH_CONNECT_V4, + FWPM_LAYER_ALE_AUTH_CONNECT_V6}; + const UINT8 NUM_CONDITIONS = 1; /// 2 + const UINT8 NUM_OBJECTS = RTL_NUMBER_OF(pLayerKeys); + FWPM_FILTER_CONDITION pFilterConditions[NUM_CONDITIONS] = {0}; + FWPM_FILTER pFilters[NUM_OBJECTS] = {0}; + +/// status = HlprSecurityDescriptorGetSelfRelativeForUser(); + + pFilterConditions[0].fieldKey = FWPM_CONDITION_ALE_PACKAGE_ID; + pFilterConditions[0].matchType = FWP_MATCH_EQUAL; + pFilterConditions[0].conditionValue.type = FWP_SID; + pFilterConditions[0].conditionValue.sid = (SID*)pPackageID; + +/// pFilterConditions[1].fieldKey = FWPM_CONDITION_ALE_USER_ID; +/// pFilterConditions[1].matchType = FWP_MATCH_EQUAL; +/// pFilterConditions[1].conditionValue.type = FWP_SECURITY_DESCRIPTOR_TYPE; +/// pFilterConditions[1].conditionValue.sd = ; + + for(UINT32 objectIndex = 0; + objectIndex < NUM_OBJECTS; + objectIndex++) + { + status = HlprGUIDPopulate(&(pFilters[objectIndex].filterKey)); + HLPR_BAIL_ON_FAILURE(status); + + pFilters[objectIndex].displayData.name = L"WFPSampler's AppContainer Scenario Filter"; + pFilters[objectIndex].displayData.description = (PWSTR)pDisplayName; + pFilters[objectIndex].flags |= FWPM_FILTER_FLAG_PERSISTENT; + pFilters[objectIndex].providerKey = (GUID*)&WFPSAMPLER_PROVIDER; + pFilters[objectIndex].layerKey = pLayerKeys[objectIndex]; + pFilters[objectIndex].subLayerKey = WFPSAMPLER_SUBLAYER; + pFilters[objectIndex].weight.type = FWP_UINT8; + pFilters[objectIndex].weight.uint8 = 0xF; + pFilters[objectIndex].numFilterConditions = NUM_CONDITIONS; + pFilters[objectIndex].filterCondition = pFilterConditions; + pFilters[objectIndex].action.type = FWP_ACTION_PERMIT; + + if(!persistent) + pFilters[objectIndex].flags ^= FWPM_FILTER_FLAG_PERSISTENT; + + if(bootTime) + { + if(pFilters[objectIndex].flags & FWPM_FILTER_FLAG_PERSISTENT) + pFilters[objectIndex].flags ^= FWPM_FILTER_FLAG_PERSISTENT; + + pFilters[objectIndex].flags |= FWPM_FILTER_FLAG_BOOTTIME; + } + } + + status = HlprFwpmEngineOpen(&engineHandle); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmTransactionBegin(engineHandle); + HLPR_BAIL_ON_FAILURE(status); + + for(UINT32 objectIndex = 0; + objectIndex < NUM_OBJECTS; + objectIndex++) + { + status = HlprFwpmFilterAdd(engineHandle, + &(pFilters[objectIndex])); + HLPR_BAIL_ON_FAILURE(status); + } + + status = HlprFwpmTransactionCommit(engineHandle); + HLPR_BAIL_ON_FAILURE(status); + + HLPR_BAIL_LABEL: + + if(engineHandle) + { + if(status != NO_ERROR) + HlprFwpmTransactionAbort(engineHandle); + + HlprFwpmEngineClose(&engineHandle); + } + + return status; +} + +/** + @private_function="PrvScenarioAppContainerAddFwpmObjects" + + Purpose: Function that enables the SCENARIO_APP_CONTAINER scenarios.
+
+ Notes: Function is overloaded.
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 PrvScenarioAppContainerAddFwpmObjects(_In_ BOOLEAN persistent = TRUE, + _In_ BOOLEAN bootTime = FALSE) +{ + UINT32 status = NO_ERROR; + UINT32 sidSize = 0; + HANDLE engineHandle = 0; + const GUID pLayerKeys[] = {FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4, + FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6, + FWPM_LAYER_ALE_AUTH_CONNECT_V4, + FWPM_LAYER_ALE_AUTH_CONNECT_V6}; + const UINT32 NUM_OBJECTS = RTL_NUMBER_OF(pLayerKeys); + FWPM_FILTER_CONDITION filterCondition = {0}; + FWPM_FILTER pFilters[NUM_OBJECTS] = {0}; + + /// Only App Containers have a valid (non-NULL) SID for the ALE_PACKAGE_ID + filterCondition.fieldKey = FWPM_CONDITION_ALE_PACKAGE_ID; + filterCondition.matchType = FWP_MATCH_NOT_EQUAL; + filterCondition.conditionValue.type = FWP_SID; + +#pragma warning(push) +#pragma warning(disable: 6388) /// filterCondition.conditionValue.sid guaranteed to be 0 due to ZeroMemory call + + status = HlprSIDGetWellKnown(WinNullSid, + &(filterCondition.conditionValue.sid), + &sidSize); + HLPR_BAIL_ON_FAILURE(status); + +#pragma warning(pop) + + for(UINT32 objectIndex = 0; + objectIndex < NUM_OBJECTS; + objectIndex++) + { + status = HlprGUIDPopulate(&(pFilters[objectIndex].filterKey)); + HLPR_BAIL_ON_FAILURE(status); + + pFilters[objectIndex].displayData.name = L"WFPSampler's AppContainer Scenario Filter"; + pFilters[objectIndex].displayData.description = L"Trust Windows Service Hardening to handle all App Containers"; + pFilters[objectIndex].flags |= FWPM_FILTER_FLAG_PERSISTENT; + pFilters[objectIndex].providerKey = (GUID*)&WFPSAMPLER_PROVIDER; + pFilters[objectIndex].layerKey = pLayerKeys[objectIndex]; + pFilters[objectIndex].subLayerKey = WFPSAMPLER_SUBLAYER; + pFilters[objectIndex].weight.type = FWP_UINT8; + pFilters[objectIndex].weight.uint8 = 0xF; + pFilters[objectIndex].numFilterConditions = 1; + pFilters[objectIndex].filterCondition = &filterCondition; + pFilters[objectIndex].action.type = FWP_ACTION_PERMIT; + + if(!persistent) + pFilters[objectIndex].flags ^= FWPM_FILTER_FLAG_PERSISTENT; + + if(bootTime) + { + if(pFilters[objectIndex].flags & FWPM_FILTER_FLAG_PERSISTENT) + pFilters[objectIndex].flags ^= FWPM_FILTER_FLAG_PERSISTENT; + + pFilters[objectIndex].flags |= FWPM_FILTER_FLAG_BOOTTIME; + } + } + + status = HlprFwpmEngineOpen(&engineHandle); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmTransactionBegin(engineHandle); + HLPR_BAIL_ON_FAILURE(status); + + for(UINT32 objectIndex = 0; + objectIndex < NUM_OBJECTS; + objectIndex++) + { + status = HlprFwpmFilterAdd(engineHandle, + &(pFilters[objectIndex])); + HLPR_BAIL_ON_FAILURE(status); + } + + status = HlprFwpmTransactionCommit(engineHandle); + HLPR_BAIL_ON_FAILURE(status); + + HLPR_BAIL_LABEL: + + HlprSIDDestroy(&(filterCondition.conditionValue.sid)); + + if(engineHandle) + { + if(status != NO_ERROR) + HlprFwpmTransactionAbort(engineHandle); + + HlprFwpmEngineClose(&engineHandle); + } + + return status; +} + +/** + @callback_function="PrvScenarioAppContainerActOnChange" + + Purpose: Callback function which is invoked when an application adds or removes it's + appContainer capabilities.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +VOID CALLBACK PrvScenarioAppContainerActOnChange(_In_opt_ VOID* pContext, + _In_ const INET_FIREWALL_AC_CHANGE* pChange) +{ + UNREFERENCED_PARAMETER(pContext); + + ASSERT(pChange); + + if(pChange->changeType == INET_FIREWALL_AC_CHANGE_CREATE) + { + PrvScenarioAppContainerAddFwpmObjects(pChange->appContainerSid, + pChange->userSid, + pChange->displayName); + } + else if(pChange->changeType == INET_FIREWALL_AC_CHANGE_DELETE) + { + PrvScenarioAppContainerDeleteFwpmObjects(pChange->appContainerSid, + pChange->userSid); + } + + return; +} + +/** + @private_function="PrvScenarioAppContainerEnumExisting" + + Purpose:
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/HH447479.aspx
+*/ +_Success_(return == NO_ERROR) +UINT32 PrvScenarioAppContainerEnumExisting() +{ + UINT32 status = NO_ERROR; + UINT32 numAppContainers = 0; + INET_FIREWALL_APP_CONTAINER* pAppContainers = 0; + + status = NetworkIsolationEnumAppContainers(NETISO_FLAG_FORCE_COMPUTE_BINARIES, + (DWORD*)&numAppContainers, + &pAppContainers); + + HLPR_BAIL_ON_FAILURE(status); + + for(UINT32 containerIndex = 0; + containerIndex < numAppContainers; + containerIndex++) + { + status = PrvScenarioAppContainerAddFwpmObjects(pAppContainers[containerIndex].appContainerSid, + pAppContainers[containerIndex].userSid, + pAppContainers[containerIndex].displayName); + HLPR_BAIL_ON_FAILURE(status); + } + + HLPR_BAIL_LABEL: + + return status; +} + +/** + @private_function="PrvScenarioAppContainerUnregister" + + Purpose:
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/HH447485.aspx
+*/ +_Success_(return == NO_ERROR) +UINT32 PrvScenarioAppContainerUnregister() +{ + UINT32 status = NO_ERROR; + + if(registrationHandle) + { + status = NetworkIsolationUnregisterForAppContainerChanges(registrationHandle); + if(status != NO_ERROR) + HlprLogError(L"PrvScenarioAppContainerUnregister : NetworkIsolationUnregisterForAppContainerChanges() [status: %#x]", + status); + else + registrationHandle = 0; + } + + return status; +} + +/** + @private_function="PrvScenarioAppContainerRegister" + + Purpose:
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/HH447482.aspx
+*/ +_Success_(return == NO_ERROR) +UINT32 PrvScenarioAppContainerRegister() +{ + UINT32 status = NO_ERROR; + + status = NetworkIsolationRegisterForAppContainerChanges(0, + PrvScenarioAppContainerActOnChange, + 0, + ®istrationHandle); + if(status != NO_ERROR) + HlprLogError(L"ScenarioAppContainerRegister : NetworkIsolationRegisterForAppContainerChanges() [status: %#x]", + status); + + return status; +} + +/** + @scenario_function="ScenarioAppContainerRemove" + + Purpose: Function that removes corresponding objects for a previously added + SCENARIO_APP_CONTAINER.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 ScenarioAppContainerRemove(_In_ BOOLEAN trustWSH) +{ + UINT32 status = NO_ERROR; + + if(trustWSH) + status = PrvScenarioAppContainerDeleteFwpmObjects(); + else + { + } + + return status; +} + +/** + @scenario_function="ScenarioAppContainerAdd" + + Purpose:
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 ScenarioAppContainerAdd(_In_ BOOLEAN trustWSH, + _In_ BOOLEAN persistent, + _In_ BOOLEAN bootTime) +{ + UINT32 status = NO_ERROR; + + if(trustWSH) + status = PrvScenarioAppContainerAddFwpmObjects(persistent, + bootTime); + else + { + status = PrvScenarioAppContainerRegister(); + HLPR_BAIL_ON_FAILURE(status); + + status = PrvScenarioAppContainerEnumExisting(); + HLPR_BAIL_ON_FAILURE(status); + } + + HLPR_BAIL_LABEL: + + if(status == NO_ERROR) + scenarioConfigured = TRUE; + + return status; +} + +/** + @rpc_function="RPCInvokeScenarioBasicAction" + + Purpose: RPC exposed function used to dipatch the scenario routines for + SCENARIO_APP_CONTAINER.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +/* [fault_status][comm_status] */ +error_status_t RPCInvokeScenarioAppContainer(/* [in] */ handle_t rpcBindingHandle, + /* [in] */ WFPSAMPLER_SCENARIO scenario, + /* [in] */ FWPM_CHANGE_TYPE changeType, + /* [in] */ BOOLEAN trustWSH, + /* [in] */ BOOLEAN persistent, + /* [in] */ BOOLEAN bootTime) +{ + UINT32 status = NO_ERROR; + + UNREFERENCED_PARAMETER(rpcBindingHandle); + UNREFERENCED_PARAMETER(scenario); + + if(scenario < SCENARIO_MAX && + changeType < FWPM_CHANGE_TYPE_MAX) + { + switch(scenario) + { + case SCENARIO_APP_CONTAINER: + { + if(changeType == FWPM_CHANGE_ADD) + { + if(!scenarioConfigured) + status = ScenarioAppContainerAdd(trustWSH, + persistent, + bootTime); + else + { + status = ERROR_ALREADY_EXISTS; + + HlprLogError(L"RPCInvokeScenarioAppContainer() [status: %#x]", + status); + } + } + else + status = ScenarioAppContainerRemove(trustWSH); + + break; + } + default: + { + status = ERROR_INVALID_PARAMETER; + + HlprLogError(L"RPCInvokeScenarioAppContainer() [status: %#x][scenario: %d]", + status, + scenario); + + break; + } + } + } + else + { + status = ERROR_INVALID_PARAMETER; + + HlprLogError(L"RPCInvokeScenarioAppContainer() [status: %#x][scenario: %#x][changeType: %#x]", + status, + scenario, + changeType); + } + + return status; +} + +#else + +/** + @rpc_function="RPCInvokeScenarioBasicAction" + + Purpose: RPC exposed function used to dipatch the scenario routines for + SCENARIO_APP_CONTAINER.
+
+ Notes: This particular function is only a stub for RPC on downlevel SKUs (Windows 7 and + below).
+
+ MSDN_Ref:
+*/ + /* [fault_status][comm_status] */ + error_status_t RPCInvokeScenarioAppContainer(/* [in] */ handle_t rpcBindingHandle, + /* [in] */ WFPSAMPLER_SCENARIO scenario, + /* [in] */ FWPM_CHANGE_TYPE changeType, + /* [in] */ BOOLEAN trustWSH, + /* [in] */ BOOLEAN persistent, + /* [in] */ BOOLEAN bootTime) +{ + UNREFERENCED_PARAMETER(rpcBindingHandle); + UNREFERENCED_PARAMETER(scenario); + UNREFERENCED_PARAMETER(changeType); + UNREFERENCED_PARAMETER(trustWSH); + UNREFERENCED_PARAMETER(persistent); + UNREFERENCED_PARAMETER(bootTime); + + return ERROR_NOT_SUPPORTED; +} + +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) diff --git a/network/trans/WFPSampler/svc/Scenarios_BasicAction.cpp b/network/trans/WFPSampler/svc/Scenarios_BasicAction.cpp new file mode 100644 index 000000000..2e8c4f95a --- /dev/null +++ b/network/trans/WFPSampler/svc/Scenarios_BasicAction.cpp @@ -0,0 +1,609 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// Scenarios_BasicAction.cpp +// +// Abstract: +// This module contains functions which implements the BASIC_ACTION_* scenarios. +// +// Naming Convention: +// +// +// +// +// i.e. +// +// +// { +// - Function is likely visible to other modules. +// Prv - Function is private to this module. +// } +// +// { +// ScenarioBasicActionBlock - Function pertains to the Basic Action Block Scenario. +// ScenarioBasicActionContinue - Function pertains to the Basic Action Continue Scenario. +// ScenarioBasicActionPermit - Function pertains to the Basic Action Permit Scenario. +// ScenarioBasicActionRandom - Function pertains to the Basic Action Random Scenario. +// RPC - Function is and RPC entry point. +// } +// +// { +// Add - Function adds objects +// Remove - Function removes objects +// Invoke - Function implements the scenario based on parameters +// passed from the commandline interface +// (WFPSampler.exe). +// } +// +// { +// FwpmObjects - Function acts on WFP objects. +// ScenarioBasicAction - Function pertains to all of the Basic Action Scenarios. +// } +// +// Private Functions: +// PrvScenarioBasicActionAddFwpmObjects(), +// PrvScenarioBasicActionDeleteFwpmObjects(), +// ScenarioBasicActionBlockRemove(), +// ScenarioBasicActionBlockAdd(), +// ScenarioBasicActionContinueRemove(), +// ScenarioBasicActionContinueAdd(), +// ScenarioBasicActionPermitRemove(), +// ScenarioBasicActionPermitAdd(), +// ScenarioBasicActionRandomRemove(), +// ScenarioBasicActionRandomAdd(), +// +// Public Functions: +// RPCInvokeScenarioBasicAction(). +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Prune filters for enumeration and handle WinBlue Fast +// layers. +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "Framework_WFPSamplerService.h" /// . + +/** + @private_function="PrvScenarioBasicActionDeleteFwpmObjects" + + Purpose: Function that disables the SCENARIO_BASIC_ACTION_* scenarios.
+
+ Notes: Scenario removes the filters using specified filtering conditions at the specified + layer. If no layer is provided, then a default layer is used. If associated, the + callout and provider contexts are removed as well.
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 PrvScenarioBasicActionDeleteFwpmObjects(_In_ const FWPM_FILTER* pFilter, + _In_ FWP_ACTION_TYPE actionType) +{ + ASSERT(pFilter); + + UINT32 status = NO_ERROR; + HANDLE engineHandle = 0; + HANDLE enumHandle = 0; + UINT32 entryCount = 0; + FWPM_FILTER** ppFilters = 0; + FWPM_FILTER_CONDITION* pFilterConditions = 0; + UINT32 numFilterConditions = 0; + FWPM_FILTER_ENUM_TEMPLATE filterEnumTemplate = {0}; + + HlprFwpmFilterConditionPrune(pFilter->filterCondition, + pFilter->numFilterConditions, + &pFilterConditions, + &numFilterConditions); + + filterEnumTemplate.providerKey = (GUID*)&WFPSAMPLER_PROVIDER; + filterEnumTemplate.layerKey = pFilter->layerKey; + filterEnumTemplate.enumType = FWP_FILTER_ENUM_FULLY_CONTAINED; + filterEnumTemplate.flags = FWP_FILTER_ENUM_FLAG_INCLUDE_BOOTTIME | + FWP_FILTER_ENUM_FLAG_INCLUDE_DISABLED; + filterEnumTemplate.numFilterConditions = pFilterConditions ? numFilterConditions : pFilter->numFilterConditions; + filterEnumTemplate.filterCondition = pFilterConditions ? pFilterConditions : pFilter->filterCondition; + filterEnumTemplate.actionMask = 0xFFFFFFFF; + + status = HlprFwpmEngineOpen(&engineHandle); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmFilterCreateEnumHandle(engineHandle, + &filterEnumTemplate, + &enumHandle); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmFilterEnum(engineHandle, + enumHandle, + 0xFFFFFFFF, + &ppFilters, + &entryCount); + HLPR_BAIL_ON_FAILURE(status); + + if(ppFilters) + { + for(UINT32 filterIndex = 0; + filterIndex < entryCount; + filterIndex++) + { + if(ppFilters[filterIndex]->action.type == actionType || + ppFilters[filterIndex]->action.type & FWP_ACTION_FLAG_CALLOUT) + { + HlprFwpmFilterDeleteByKey(engineHandle, + &(ppFilters[filterIndex]->filterKey)); + + if(ppFilters[filterIndex]->flags & FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT) + HlprFwpmProviderContextDeleteByKey(engineHandle, + &(ppFilters[filterIndex]->providerContextKey)); + + if(ppFilters[filterIndex]->action.type & FWP_ACTION_FLAG_CALLOUT) + HlprFwpmCalloutDeleteByKey(engineHandle, + &(ppFilters[filterIndex]->action.calloutKey)); + } + } + + FwpmFreeMemory((VOID**)&ppFilters); + } + + HLPR_BAIL_LABEL: + + if(engineHandle) + { + if(enumHandle) + HlprFwpmFilterDestroyEnumHandle(engineHandle, + &enumHandle); + + HlprFwpmEngineClose(&engineHandle); + } + + HLPR_DELETE_ARRAY(pFilterConditions); + + return status; +} + +/** + @private_function="PrvScenarioBasicActionAddFwpmObjects" + + Purpose: Function that enables the SCENARIO_BASIC_ACTION_* scenarios.
+
+ Notes: Scenario adds a filter using specified filtering conditions to the specified layer. + If no layer is provided, then a default layer is used. This filter is associated + with WFPSampler's default sublayer and provider. If required, the appropriate + callout and provider context is added and associated with the filter.
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 PrvScenarioBasicActionAddFwpmObjects(_In_ const FWPM_FILTER* pFilter, + _In_ FWP_ACTION_TYPE actionType = 0, + _In_opt_ const PC_BASIC_ACTION_DATA* pPCBasicActionData = 0) +{ + ASSERT(pFilter); + + UINT32 status = NO_ERROR; + HANDLE engineHandle = 0; + FWPM_CALLOUT* pCallout = 0; + FWPM_PROVIDER_CONTEXT* pProviderContext = 0; + FWP_BYTE_BLOB* pByteBlob = 0; + FWPM_FILTER filter = {0}; + + RtlCopyMemory(&filter, + pFilter, + sizeof(FWPM_FILTER)); + +#if(NTDDI_VERSION >= NTDDI_WINBLUE) + + /// It is not advised to use these layers as they are meant for Microsoft's internal use. + /// Trying to use static filtering will yield FWP_E_INCOMPATIBLE_LAYER when adding the filter. + if(pFilter->layerKey == FWPM_LAYER_INBOUND_TRANSPORT_FAST || + pFilter->layerKey == FWPM_LAYER_OUTBOUND_TRANSPORT_FAST || + pFilter->layerKey == FWPM_LAYER_INBOUND_MAC_FRAME_NATIVE_FAST || + pFilter->layerKey == FWPM_LAYER_OUTBOUND_MAC_FRAME_NATIVE_FAST) + { + /// Callouts are not publicly supported for these layers. + if(filter.action.type & FWP_ACTION_FLAG_CALLOUT) + { + status = (UINT32)FWP_E_INCOMPATIBLE_LAYER; + + HLPR_BAIL; + } + + filter.numFilterConditions = 0; + filter.filterCondition = 0; + } + +#endif /// (NTDDI_VERSION >= NTDDI_WINBLUE) + + status = HlprGUIDPopulate(&(filter.filterKey)); + HLPR_BAIL_ON_FAILURE(status); + + filter.providerKey = (GUID*)&WFPSAMPLER_PROVIDER; + filter.weight.type = FWP_UINT8; + filter.weight.uint8 = filter.numFilterConditions ? 0xF : 0x0; + + if(filter.action.type & FWP_ACTION_FLAG_CALLOUT) + { + FWP_ACTION_TYPE newActionType = FWP_ACTION_CALLOUT_TERMINATING; + + HLPR_NEW(pCallout, + FWPM_CALLOUT); + HLPR_BAIL_ON_ALLOC_FAILURE(pCallout, + status); + + if(actionType == FWP_ACTION_BLOCK) + { + pCallout->calloutKey = WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK; + pCallout->displayData.name = L"WFPSampler's Basic Block Callout"; + pCallout->displayData.description = L"Causes callout invocation which returns FWP_ACTION_BLOCK"; + + filter.flags |= FWPM_FILTER_FLAG_CLEAR_ACTION_RIGHT; + } + else if(actionType == FWP_ACTION_CONTINUE) + { + pCallout->calloutKey = WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE; + pCallout->displayData.name = L"WFPSampler's Basic Continue Callout"; + pCallout->displayData.description = L"Causes callout invocation which returns FWP_ACTION_CONTINUE"; + + newActionType = FWP_ACTION_CALLOUT_UNKNOWN; + } + else if(actionType == FWP_ACTION_PERMIT) + { + pCallout->calloutKey = WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT; + pCallout->displayData.name = L"WFPSampler's Basic Permit Callout"; + pCallout->displayData.description = L"Causes callout invocation which returns FWP_ACTION_PERMIT"; + + filter.flags |= FWPM_FILTER_FLAG_CLEAR_ACTION_RIGHT; + } + else + { + HLPR_NEW(pProviderContext, + FWPM_PROVIDER_CONTEXT); + HLPR_BAIL_ON_ALLOC_FAILURE(pProviderContext, + status); + + HLPR_NEW(pByteBlob, + FWP_BYTE_BLOB); + HLPR_BAIL_ON_ALLOC_FAILURE(pProviderContext, + status); + + status = HlprGUIDPopulate(&(pProviderContext->providerContextKey)); + HLPR_BAIL_ON_FAILURE(status); + + pProviderContext->displayData.name = L"WFPSampler's Basic Random Action Provider Context"; + pProviderContext->displayData.description = L"Testing Purposes Only!!!"; + pProviderContext->providerKey = (GUID*)&WFPSAMPLER_PROVIDER; + pProviderContext->type = FWPM_GENERAL_CONTEXT; + pProviderContext->dataBuffer = pByteBlob; + pProviderContext->dataBuffer->size = sizeof(PC_BASIC_ACTION_DATA); + pProviderContext->dataBuffer->data = (BYTE*)pPCBasicActionData; + + pCallout->calloutKey = WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM; + pCallout->displayData.name = L"WFPSampler's Basic Random Action Callout"; + pCallout->displayData.description = L"Testing Purposes Only!!! Causes callout invocation which randomly chooses an action to return."; + pCallout->flags |= FWPM_CALLOUT_FLAG_USES_PROVIDER_CONTEXT; + + filter.flags |= FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT; + filter.providerContextKey = pProviderContext->providerContextKey; + + newActionType = FWP_ACTION_CALLOUT_UNKNOWN; + } + + pCallout->calloutKey.Data4[7] = HlprFwpmLayerGetIDByKey(&(filter.layerKey)); /// Uniquely identifies the callout used + pCallout->providerKey = (GUID*)&WFPSAMPLER_PROVIDER; + pCallout->applicableLayer = filter.layerKey; + + if(filter.flags & FWPM_FILTER_FLAG_BOOTTIME || + filter.flags & FWPM_FILTER_FLAG_PERSISTENT) + { + if(pProviderContext) + pProviderContext->flags = FWPM_PROVIDER_CONTEXT_FLAG_PERSISTENT; + + pCallout->flags = FWPM_CALLOUT_FLAG_PERSISTENT; + } + + filter.action.type = newActionType; + filter.action.calloutKey = pCallout->calloutKey; + } + else + filter.action.type = actionType; + + status = HlprFwpmEngineOpen(&engineHandle); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmTransactionBegin(engineHandle); + HLPR_BAIL_ON_FAILURE(status); + + if(pCallout) + { + status = HlprFwpmCalloutAdd(engineHandle, + pCallout); + HLPR_BAIL_ON_FAILURE(status); + } + + if(pProviderContext) + { + status = HlprFwpmProviderContextAdd(engineHandle, + pProviderContext); + HLPR_BAIL_ON_FAILURE(status); + } + + status = HlprFwpmFilterAdd(engineHandle, + &filter); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmTransactionCommit(engineHandle); + HLPR_BAIL_ON_FAILURE(status); + + HLPR_BAIL_LABEL: + + if(engineHandle) + { + if(status != NO_ERROR) + HlprFwpmTransactionAbort(engineHandle); + + HlprFwpmEngineClose(&engineHandle); + } + + HLPR_DELETE(pByteBlob); + + HLPR_DELETE(pProviderContext); + + HLPR_DELETE(pCallout); + + return status; + +} + +/** + @scenario_function="ScenarioBasicActionBlockRemove" + + Purpose: Function that removes corresponding objects for a previously added + SCENARIO_BASIC_ACTION_BLOCK.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 ScenarioBasicActionBlockRemove(_In_ const FWPM_FILTER* pFilter) +{ + ASSERT(pFilter); + + return PrvScenarioBasicActionDeleteFwpmObjects(pFilter, + FWP_ACTION_BLOCK); +} + +/** + @scenario_function="ScenarioBasicActionBlockAdd" + + Purpose: Scenario which will return FWP_ACTION_BLOCK.
+
+ Notes: Adds a filter. By default this will be a static terminating filter. If specified, + this filter can be made to reference one of the + WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK callouts.
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 ScenarioBasicActionBlockAdd(_In_ const FWPM_FILTER* pFilter) +{ + ASSERT(pFilter); + + return PrvScenarioBasicActionAddFwpmObjects(pFilter, + FWP_ACTION_BLOCK, + 0); +} + +/** + @scenario_function="ScenarioBasicActionContinueRemove" + + Purpose: Function that removes corresponding objects for a previously added + SCENARIO_BASIC_ACTION_CONTINUE.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 ScenarioBasicActionContinueRemove(_In_ const FWPM_FILTER* pFilter) +{ + ASSERT(pFilter); + + return PrvScenarioBasicActionDeleteFwpmObjects(pFilter, + FWP_ACTION_CONTINUE); +} + +/** + @scenario_function="ScenarioBasicActionContinueAdd" + + Purpose: Scenario which will return FWP_ACTION_CONTINUE.
+
+ Notes: Adds a filter. By default this will be a static terminating filter. If specified, + this filter can be made to reference one of the + WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE callouts.
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 ScenarioBasicActionContinueAdd(_In_ const FWPM_FILTER* pFilter) +{ + ASSERT(pFilter); + + return PrvScenarioBasicActionAddFwpmObjects(pFilter, + FWP_ACTION_CONTINUE, + 0); +} + +/** + @scenario_function="ScenarioBasicActionPermitRemove" + + Purpose: Function that removes corresponding objects for a previously added + SCENARIO_BASIC_ACTION_PERMIT.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 ScenarioBasicActionPermitRemove(_In_ const FWPM_FILTER* pFilter) +{ + ASSERT(pFilter); + + return PrvScenarioBasicActionDeleteFwpmObjects(pFilter, + FWP_ACTION_PERMIT); +} + +/** + @scenario_function="ScenarioBasicActionPermitAdd" + + Purpose: Scenario which will return FWP_ACTION_PERMIT.
+
+ Notes: Adds a filter. By default this will be a static terminating filter. If specified, + this filter can be made to reference one of the + WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT callouts.
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 ScenarioBasicActionPermitAdd(_In_ const FWPM_FILTER* pFilter) +{ + ASSERT(pFilter); + + return PrvScenarioBasicActionAddFwpmObjects(pFilter, + FWP_ACTION_PERMIT, + 0); +} + +/** + @scenario_function="ScenarioBasicActionRandomRemove" + + Purpose: Function that removes corresponding objects for a previously added + SCENARIO_BASIC_ACTION_RANDOM.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 ScenarioBasicActionRandomRemove(_In_ const FWPM_FILTER* pFilter) +{ + ASSERT(pFilter); + + return PrvScenarioBasicActionDeleteFwpmObjects(pFilter, + FWP_ACTION_CALLOUT_UNKNOWN); +} + +/** + @scenario_function="ScenarioBasicActionRandomAdd" + + Purpose: Scenario which will randomly return FWP_ACTION_BLOCK, FWP_ACTION_CONTINUE, or + FWP_ACTION_PERMIT.
+
+ Notes: Adds a filter which references one of the WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM + callouts for the provided layer.
+
+ The randomness of the action is configurable via the percentage values for this + scenario.
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 ScenarioBasicActionRandomAdd(_In_ const FWPM_FILTER* pFilter, + _In_ const PC_BASIC_ACTION_DATA* pPCBasicActionData) +{ + ASSERT(pFilter); + ASSERT(pPCBasicActionData); + + return PrvScenarioBasicActionAddFwpmObjects(pFilter, + 0, + pPCBasicActionData); +} + +/** + @rpc_function="RPCInvokeScenarioBasicAction" + + Purpose: RPC exposed function used to dipatch the scenario routines for + SCENARIO_BASIC_ACTION_*.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +/* [fault_status][comm_status] */ +error_status_t RPCInvokeScenarioBasicAction(/* [in] */ handle_t rpcBindingHandle, + /* [in] */ WFPSAMPLER_SCENARIO scenario, + /* [in] */ FWPM_CHANGE_TYPE changeType, + /* [ref][in] */ __RPC__in const FWPM_FILTER0* pFilter, + /* [unique][in] */ __RPC__in_opt const PC_BASIC_ACTION_DATA* pPCBasicActionData) +{ + UINT32 status = NO_ERROR; + + UNREFERENCED_PARAMETER(rpcBindingHandle); + + ASSERT(scenario == SCENARIO_BASIC_ACTION_BLOCK || + scenario == SCENARIO_BASIC_ACTION_CONTINUE || + scenario == SCENARIO_BASIC_ACTION_PERMIT || + scenario == SCENARIO_BASIC_ACTION_RANDOM); + ASSERT(changeType < FWPM_CHANGE_TYPE_MAX); + ASSERT(pFilter); + + switch(scenario) + { + case SCENARIO_BASIC_ACTION_BLOCK: + { + if(changeType == FWPM_CHANGE_ADD) + status = ScenarioBasicActionBlockAdd(pFilter); + else + status = ScenarioBasicActionBlockRemove(pFilter); + + break; + } + case SCENARIO_BASIC_ACTION_CONTINUE: + { + if(changeType == FWPM_CHANGE_ADD) + status = ScenarioBasicActionContinueAdd(pFilter); + else + status = ScenarioBasicActionContinueRemove(pFilter); + + break; + } + case SCENARIO_BASIC_ACTION_PERMIT: + { + if(changeType == FWPM_CHANGE_ADD) + status = ScenarioBasicActionPermitAdd(pFilter); + else + status = ScenarioBasicActionPermitRemove(pFilter); + + break; + } + case SCENARIO_BASIC_ACTION_RANDOM: + { + if(changeType == FWPM_CHANGE_ADD) + { + if(pPCBasicActionData) + status = ScenarioBasicActionRandomAdd(pFilter, + pPCBasicActionData); + else + { + status = ERROR_INVALID_PARAMETER; + + HlprLogError(L"RpcInvokeScenarioBasicAction() [status: %#x][pPCBasicActionData: %#p]", + status, + pPCBasicActionData); + } + } + else + status = ScenarioBasicActionRandomRemove(pFilter); + + break; + } + } + + return status; +} diff --git a/network/trans/WFPSampler/svc/Scenarios_BasicPacketExamination.cpp b/network/trans/WFPSampler/svc/Scenarios_BasicPacketExamination.cpp new file mode 100644 index 000000000..a0d88dd26 --- /dev/null +++ b/network/trans/WFPSampler/svc/Scenarios_BasicPacketExamination.cpp @@ -0,0 +1,398 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// Scenarios_BasicPacketExamination.cpp +// +// Abstract: +// This module contains functions which implements the BASIC_PACKET_EXAMINATION scenarios. +// +// Naming Convention: +// +// +// +// +// i.e. +// +// +// { +// - Function is likely visible to other modules +// Prv - Function is private to this module. +// } +// +// { +// ScenarioBasicPacketExamination - Function pertains to all of the Basic Packet +// Examination Scenarios +// RPC - Function is and RPC entry point. +// } +// +// { +// Add - Function adds objects or scenarios +// Delete - Function deletes objects +// Invoke - Function implements the scenario. +// Remove - Function removes scenarios +// } +// +// { +// FwpmObjects - Function acts on WFP objects. +// ScenarioBasicPacketExamination - Function pertains to all of the Basic Packet +// Examination Scenarios +// } +// +// Private Functions: +// PrvScenarioBasicPacketExaminationAddFwpmObjects(), +// PrvScenarioBasicPacketExaminationDeleteFwpmObjects(), +// ScenarioBasicPacketExaminationAdd(), +// ScenarioBasicPacketExaminationRemove(), +// +// Public Functions: +// RPCInvokeScenarioBasicPacketExamination(). +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Prune filters for enumeration and limit scenario to +// only the supported layers +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "Framework_WFPSamplerService.h" /// . + +/** + @private_function="PrvScenarioBasicPacketExaminationDeleteFwpmObjects" + + Purpose: Function that disables the SCENARIO_BASIC_PACKET_EXAMINATION scenarios.
+
+ Notes: Scenario removes the filters using specified filtering conditions at the + specified layer. Associated callouts are removed as well.
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 PrvScenarioBasicPacketExaminationDeleteFwpmObjects(_In_ const FWPM_FILTER* pFilter) +{ + ASSERT(pFilter); + + UINT32 status = NO_ERROR; + HANDLE engineHandle = 0; + HANDLE enumHandle = 0; + UINT32 entryCount = 0; + FWPM_FILTER** ppFilters = 0; + FWPM_FILTER_CONDITION* pFilterConditions = 0; + UINT32 numFilterConditions = 0; + FWPM_FILTER_ENUM_TEMPLATE filterEnumTemplate = {0}; + GUID calloutKey = {0}; + + HlprFwpmFilterConditionPrune(pFilter->filterCondition, + pFilter->numFilterConditions, + &pFilterConditions, + &numFilterConditions); + + calloutKey = WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION; + calloutKey.Data4[7] = HlprFwpmLayerGetIDByKey(&(pFilter->layerKey)); /// Uniquely identifies the callout used + + filterEnumTemplate.providerKey = (GUID*)&WFPSAMPLER_PROVIDER; + filterEnumTemplate.layerKey = pFilter->layerKey; + filterEnumTemplate.enumType = FWP_FILTER_ENUM_FULLY_CONTAINED; + filterEnumTemplate.flags = FWP_FILTER_ENUM_FLAG_INCLUDE_BOOTTIME | + FWP_FILTER_ENUM_FLAG_INCLUDE_DISABLED; + filterEnumTemplate.numFilterConditions = pFilterConditions ? numFilterConditions : pFilter->numFilterConditions; + filterEnumTemplate.filterCondition = pFilterConditions ? pFilterConditions : pFilter->filterCondition; + filterEnumTemplate.actionMask = FWP_ACTION_FLAG_CALLOUT; + filterEnumTemplate.calloutKey = &calloutKey; + + status = HlprFwpmEngineOpen(&engineHandle); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmFilterCreateEnumHandle(engineHandle, + &filterEnumTemplate, + &enumHandle); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmFilterEnum(engineHandle, + enumHandle, + 0xFFFFFFFF, + &ppFilters, + &entryCount); + HLPR_BAIL_ON_FAILURE(status); + + if(ppFilters) + { + for(UINT32 filterIndex = 0; + filterIndex < entryCount; + filterIndex++) + { + HlprFwpmFilterDeleteByKey(engineHandle, + &(ppFilters[filterIndex]->filterKey)); + + HlprFwpmCalloutDeleteByKey(engineHandle, + &(ppFilters[filterIndex]->action.calloutKey)); + + if(ppFilters[filterIndex]->flags & FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT) + HlprFwpmProviderContextDeleteByKey(engineHandle, + &(ppFilters[filterIndex]->providerContextKey)); + } + + FwpmFreeMemory((VOID**)&ppFilters); + } + + HLPR_BAIL_LABEL: + + if(engineHandle) + { + if(enumHandle) + HlprFwpmFilterDestroyEnumHandle(engineHandle, + &enumHandle); + + HlprFwpmEngineClose(&engineHandle); + } + + HLPR_DELETE_ARRAY(pFilterConditions); + + return status; +} + +/** + @private_function="PrvScenarioBasicPacketExaminationAddFwpmObjects" + + Purpose: Function that enables the SCENARIO_BASIC_PACKET_EXAMINATION scenarios.
+
+ Notes: Scenario adds a filter using specified filtering conditions to the specified + layer. This filter is associated with WFPSampler's default provider and the + built-in inspection sublayer. The appropriate callout is then added and + associated with the filter.
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 PrvScenarioBasicPacketExaminationAddFwpmObjects(_In_ const FWPM_FILTER* pFilter) +{ + ASSERT(pFilter); + + UINT32 status = NO_ERROR; + + if(pFilter->layerKey == FWPM_LAYER_INBOUND_IPPACKET_V4 || + pFilter->layerKey == FWPM_LAYER_INBOUND_IPPACKET_V4_DISCARD || + pFilter->layerKey == FWPM_LAYER_INBOUND_IPPACKET_V6 || + pFilter->layerKey == FWPM_LAYER_INBOUND_IPPACKET_V6_DISCARD || + pFilter->layerKey == FWPM_LAYER_OUTBOUND_IPPACKET_V4 || + pFilter->layerKey == FWPM_LAYER_OUTBOUND_IPPACKET_V4_DISCARD || + pFilter->layerKey == FWPM_LAYER_OUTBOUND_IPPACKET_V6 || + pFilter->layerKey == FWPM_LAYER_OUTBOUND_IPPACKET_V6_DISCARD || + pFilter->layerKey == FWPM_LAYER_IPFORWARD_V4 || + pFilter->layerKey == FWPM_LAYER_IPFORWARD_V4_DISCARD || + pFilter->layerKey == FWPM_LAYER_IPFORWARD_V6 || + pFilter->layerKey == FWPM_LAYER_IPFORWARD_V6_DISCARD || + pFilter->layerKey == FWPM_LAYER_INBOUND_TRANSPORT_V4 || + pFilter->layerKey == FWPM_LAYER_INBOUND_TRANSPORT_V4_DISCARD || + pFilter->layerKey == FWPM_LAYER_INBOUND_TRANSPORT_V6 || + pFilter->layerKey == FWPM_LAYER_INBOUND_TRANSPORT_V6_DISCARD || + pFilter->layerKey == FWPM_LAYER_OUTBOUND_TRANSPORT_V4 || + pFilter->layerKey == FWPM_LAYER_OUTBOUND_TRANSPORT_V4_DISCARD || + pFilter->layerKey == FWPM_LAYER_OUTBOUND_TRANSPORT_V6 || + pFilter->layerKey == FWPM_LAYER_OUTBOUND_TRANSPORT_V6_DISCARD || + pFilter->layerKey == FWPM_LAYER_STREAM_V4 || + pFilter->layerKey == FWPM_LAYER_STREAM_V4_DISCARD || + pFilter->layerKey == FWPM_LAYER_STREAM_V6 || + pFilter->layerKey == FWPM_LAYER_STREAM_V6_DISCARD || + pFilter->layerKey == FWPM_LAYER_DATAGRAM_DATA_V4 || + pFilter->layerKey == FWPM_LAYER_DATAGRAM_DATA_V4_DISCARD || + pFilter->layerKey == FWPM_LAYER_DATAGRAM_DATA_V6 || + pFilter->layerKey == FWPM_LAYER_DATAGRAM_DATA_V6_DISCARD || + pFilter->layerKey == FWPM_LAYER_INBOUND_ICMP_ERROR_V4 || + pFilter->layerKey == FWPM_LAYER_INBOUND_ICMP_ERROR_V4_DISCARD || + pFilter->layerKey == FWPM_LAYER_INBOUND_ICMP_ERROR_V6 || + pFilter->layerKey == FWPM_LAYER_INBOUND_ICMP_ERROR_V6_DISCARD || + pFilter->layerKey == FWPM_LAYER_OUTBOUND_ICMP_ERROR_V4 || + pFilter->layerKey == FWPM_LAYER_OUTBOUND_ICMP_ERROR_V4_DISCARD || + pFilter->layerKey == FWPM_LAYER_OUTBOUND_ICMP_ERROR_V6 || + pFilter->layerKey == FWPM_LAYER_OUTBOUND_ICMP_ERROR_V6_DISCARD || + pFilter->layerKey == FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V4 || + pFilter->layerKey == FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V4_DISCARD || + pFilter->layerKey == FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V6 || + pFilter->layerKey == FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V6_DISCARD || + pFilter->layerKey == FWPM_LAYER_ALE_AUTH_LISTEN_V4 || + pFilter->layerKey == FWPM_LAYER_ALE_AUTH_LISTEN_V4_DISCARD || + pFilter->layerKey == FWPM_LAYER_ALE_AUTH_LISTEN_V6 || + pFilter->layerKey == FWPM_LAYER_ALE_AUTH_LISTEN_V6_DISCARD || + pFilter->layerKey == FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 || + pFilter->layerKey == FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4_DISCARD || + pFilter->layerKey == FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 || + pFilter->layerKey == FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6_DISCARD || + pFilter->layerKey == FWPM_LAYER_ALE_AUTH_CONNECT_V4 || + pFilter->layerKey == FWPM_LAYER_ALE_AUTH_CONNECT_V4_DISCARD || + pFilter->layerKey == FWPM_LAYER_ALE_AUTH_CONNECT_V6 || + pFilter->layerKey == FWPM_LAYER_ALE_AUTH_CONNECT_V6_DISCARD || + pFilter->layerKey == FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4 || + pFilter->layerKey == FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4_DISCARD || + pFilter->layerKey == FWPM_LAYER_ALE_FLOW_ESTABLISHED_V6 || + pFilter->layerKey == FWPM_LAYER_ALE_FLOW_ESTABLISHED_V6_DISCARD + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + || + pFilter->layerKey == FWPM_LAYER_ALE_RESOURCE_RELEASE_V4 || + pFilter->layerKey == FWPM_LAYER_ALE_RESOURCE_RELEASE_V6 || + pFilter->layerKey == FWPM_LAYER_ALE_ENDPOINT_CLOSURE_V4 || + pFilter->layerKey == FWPM_LAYER_ALE_ENDPOINT_CLOSURE_V6 || + pFilter->layerKey == FWPM_LAYER_ALE_CONNECT_REDIRECT_V4 || + pFilter->layerKey == FWPM_LAYER_ALE_CONNECT_REDIRECT_V6 || + pFilter->layerKey == FWPM_LAYER_ALE_BIND_REDIRECT_V4 || + pFilter->layerKey == FWPM_LAYER_ALE_BIND_REDIRECT_V6 || + pFilter->layerKey == FWPM_LAYER_STREAM_PACKET_V4 || + pFilter->layerKey == FWPM_LAYER_STREAM_PACKET_V6 + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + || + pFilter->layerKey == FWPM_LAYER_INBOUND_MAC_FRAME_ETHERNET || + pFilter->layerKey == FWPM_LAYER_OUTBOUND_MAC_FRAME_ETHERNET || + pFilter->layerKey == FWPM_LAYER_INBOUND_MAC_FRAME_NATIVE || + pFilter->layerKey == FWPM_LAYER_OUTBOUND_MAC_FRAME_NATIVE || + pFilter->layerKey == FWPM_LAYER_INGRESS_VSWITCH_ETHERNET || + pFilter->layerKey == FWPM_LAYER_EGRESS_VSWITCH_ETHERNET || + pFilter->layerKey == FWPM_LAYER_INGRESS_VSWITCH_TRANSPORT_V4 || + pFilter->layerKey == FWPM_LAYER_INGRESS_VSWITCH_TRANSPORT_V6 || + pFilter->layerKey == FWPM_LAYER_EGRESS_VSWITCH_TRANSPORT_V4 || + pFilter->layerKey == FWPM_LAYER_EGRESS_VSWITCH_TRANSPORT_V6 + +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + ) + { + HANDLE engineHandle = 0; + FWPM_CALLOUT callout = {0}; + FWPM_FILTER filter = {0}; + + RtlCopyMemory(&filter, + pFilter, + sizeof(FWPM_FILTER)); + + callout.calloutKey = WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION; + callout.calloutKey.Data4[7] = HlprFwpmLayerGetIDByKey(&(filter.layerKey)); /// Uniquely identifies the layer the callout will use + callout.displayData.name = L"WFPSampler's Basic Packet Examination Callout"; + callout.displayData.description = L"Causes callout invocation which examines traffic"; + callout.providerKey = (GUID*)&WFPSAMPLER_PROVIDER; + callout.applicableLayer = filter.layerKey; + + if(filter.flags & FWPM_FILTER_FLAG_BOOTTIME || + filter.flags & FWPM_FILTER_FLAG_PERSISTENT) + callout.flags |= FWPM_CALLOUT_FLAG_PERSISTENT; + + status = HlprGUIDPopulate(&(filter.filterKey)); + HLPR_BAIL_ON_FAILURE(status); + + filter.providerKey = (GUID*)&WFPSAMPLER_PROVIDER; + filter.subLayerKey = FWPM_SUBLAYER_INSPECTION; + filter.weight.type = FWP_UINT8; + filter.weight.uint8 = 0xF; + filter.action.type = FWP_ACTION_CALLOUT_INSPECTION; + filter.action.calloutKey = callout.calloutKey; + + status = HlprFwpmEngineOpen(&engineHandle); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmTransactionBegin(engineHandle); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmCalloutAdd(engineHandle, + &callout); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmFilterAdd(engineHandle, + &filter); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmTransactionCommit(engineHandle); + HLPR_BAIL_ON_FAILURE(status); + + HLPR_BAIL_LABEL: + + if(engineHandle) + { + if(status != NO_ERROR) + HlprFwpmTransactionAbort(engineHandle); + + HlprFwpmEngineClose(&engineHandle); + } + } + else + status = (UINT32)FWP_E_INCOMPATIBLE_LAYER; + + return status; +} + +/** + @scenario_function="ScenarioBasicPacketExaminationRemove" + + Purpose: Function that removes corresponding objects for a previously added + SCENARIO_BASIC_PACKET_EXAMINATION.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +UINT32 ScenarioBasicPacketExaminationRemove(_In_ const FWPM_FILTER* pFilter) +{ + ASSERT(pFilter); + + return PrvScenarioBasicPacketExaminationDeleteFwpmObjects(pFilter); +} + +/** + @scenario_function="ScenarioBasicPacketExaminationAdd" + + Purpose: Scenario which will blindly reinject the classified traffic.
+
+ Notes: Adds a filter which references one of the + WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION callouts for the provided layer.
+
+ No data modification is made to the traffic. No injection occurs.
+
+ MSDN_Ref:
+*/ +UINT32 ScenarioBasicPacketExaminationAdd(_In_ const FWPM_FILTER* pFilter) +{ + ASSERT(pFilter); + + return PrvScenarioBasicPacketExaminationAddFwpmObjects(pFilter); +} + +/** + @rpc_function="RPCInvokeScenarioBasicPacketExamination" + + Purpose: RPC exposed function used to dipatch the scenario routines for + SCENARIO_BASIC_PACKET_EXAMINATION.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +/* [fault_status][comm_status] */ +error_status_t RPCInvokeScenarioBasicPacketExamination(/* [in] */ handle_t rpcBindingHandle, + /* [in] */ WFPSAMPLER_SCENARIO scenario, + /* [in] */ FWPM_CHANGE_TYPE changeType, + /* [ref][in] */ __RPC__in const FWPM_FILTER0* pFilter) +{ + UNREFERENCED_PARAMETER(rpcBindingHandle); + UNREFERENCED_PARAMETER(scenario); + + ASSERT(pFilter); + ASSERT(scenario == SCENARIO_BASIC_PACKET_EXAMINATION); + ASSERT(changeType < FWPM_CHANGE_TYPE_MAX); + + UINT32 status = NO_ERROR; + + if(changeType == FWPM_CHANGE_ADD) + status = ScenarioBasicPacketExaminationAdd(pFilter); + else + status = ScenarioBasicPacketExaminationRemove(pFilter); + + return status; +} diff --git a/network/trans/WFPSampler/svc/Scenarios_BasicPacketInjection.cpp b/network/trans/WFPSampler/svc/Scenarios_BasicPacketInjection.cpp new file mode 100644 index 000000000..7a33beed7 --- /dev/null +++ b/network/trans/WFPSampler/svc/Scenarios_BasicPacketInjection.cpp @@ -0,0 +1,403 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// Scenarios_BasicPacketInjection.cpp +// +// Abstract: +// This module contains functions which implements the BASIC_PACKET_INJECTION scenarios. +// +// Naming Convention: +// +// +// +// +// i.e. +// +// +// { +// - Function is likely visible to other modules +// Prv - Function is private to this module. +// } +// +// { +// ScenarioBasicPacketInjection - Function pertains to all of the Basic Packet Injection +// Scenarios +// RPC - Function is and RPC entry point. +// } +// +// { +// Add - Function adds objects +// Remove - Function removes objects +// Invoke - Function implements the scenario based on parameters +// passed from the commandline interface +// (WFPSampler.exe). +// } +// +// { +// FwpmObjects - Function acts on WFP objects. +// ScenarioBasicPacketInjection - Function pertains to all of the Basic Packet Injection +// Scenarios +// } +// +// Private Functions: +// PrvScenarioBasicPacketInjectionAddFwpmObjects(), +// PrvScenarioBasicPacketInjectionDeleteFwpmObjects(), +// ScenarioBasicStreamInjectionAdd(), +// ScenarioBasicStreamInjectionRemove(), +// +// Public Functions: +// RPCInvokeScenarioBasicPacketInjection(), +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Prune filters for enumeration and limit scenario to +// only the supported layers +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "Framework_WFPSamplerService.h" /// . + +/** + @private_function="PrvScenarioBasicPacketInjectionDeleteFwpmObjects" + + Purpose: Function that disables the SCENARIO_BASIC_PACKET_INJECTION scenarios.
+
+ Notes: Scenario removes the filters using specified filtering conditions at the specified + layer. Associated callouts are removed as well.
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 PrvScenarioBasicPacketInjectionDeleteFwpmObjects(_In_ const FWPM_FILTER* pFilter) +{ + ASSERT(pFilter); + + UINT32 status = NO_ERROR; + HANDLE engineHandle = 0; + HANDLE enumHandle = 0; + UINT32 entryCount = 0; + FWPM_FILTER** ppFilters = 0; + FWPM_FILTER_CONDITION* pFilterConditions = 0; + UINT32 numFilterConditions = 0; + FWPM_FILTER_ENUM_TEMPLATE filterEnumTemplate = {0}; + FWPM_PROVIDER_CONTEXT_ENUM_TEMPLATE providerContextEnumTemplate = {0}; + GUID calloutKey = {0}; + + HlprFwpmFilterConditionPrune(pFilter->filterCondition, + pFilter->numFilterConditions, + &pFilterConditions, + &numFilterConditions); + + calloutKey = WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION; + calloutKey.Data4[7] = HlprFwpmLayerGetIDByKey(&(pFilter->layerKey)); /// Uniquely identifies the callout used + + providerContextEnumTemplate.providerContextType = FWPM_GENERAL_CONTEXT; + + filterEnumTemplate.providerKey = (GUID*)&WFPSAMPLER_PROVIDER; + filterEnumTemplate.layerKey = pFilter->layerKey; + filterEnumTemplate.enumType = FWP_FILTER_ENUM_FULLY_CONTAINED; + filterEnumTemplate.flags = FWP_FILTER_ENUM_FLAG_INCLUDE_BOOTTIME | + FWP_FILTER_ENUM_FLAG_INCLUDE_DISABLED; + filterEnumTemplate.numFilterConditions = pFilterConditions ? numFilterConditions : pFilter->numFilterConditions; + filterEnumTemplate.filterCondition = pFilterConditions ? pFilterConditions : pFilter->filterCondition; + filterEnumTemplate.providerContextTemplate = &providerContextEnumTemplate; + filterEnumTemplate.actionMask = FWP_ACTION_FLAG_CALLOUT; + filterEnumTemplate.calloutKey = &calloutKey; + + status = HlprFwpmEngineOpen(&engineHandle); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmFilterCreateEnumHandle(engineHandle, + &filterEnumTemplate, + &enumHandle); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmFilterEnum(engineHandle, + enumHandle, + 0xFFFFFFFF, + &ppFilters, + &entryCount); + HLPR_BAIL_ON_FAILURE(status); + + if(ppFilters) + { + for(UINT32 filterIndex = 0; + filterIndex < entryCount; + filterIndex++) + { + HlprFwpmFilterDeleteByKey(engineHandle, + &(ppFilters[filterIndex]->filterKey)); + + HlprFwpmCalloutDeleteByKey(engineHandle, + &(ppFilters[filterIndex]->action.calloutKey)); + + if(ppFilters[filterIndex]->flags & FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT) + HlprFwpmProviderContextDeleteByKey(engineHandle, + &(ppFilters[filterIndex]->providerContextKey)); + } + + FwpmFreeMemory((VOID**)&ppFilters); + } + + HLPR_BAIL_LABEL: + + if(engineHandle) + { + if(enumHandle) + HlprFwpmFilterDestroyEnumHandle(engineHandle, + &enumHandle); + + HlprFwpmEngineClose(&engineHandle); + } + + HLPR_DELETE_ARRAY(pFilterConditions); + + return status; +} + +/** + @private_function="PrvScenarioBasicPacketInjectionAddFwpmObjects" + + Purpose: Function that enables the SCENARIO_BASIC_PACKET_INJECTION scenarios.
+
+ Notes: Scenario adds a filter using specified filtering conditions to the specified layer. + This filter is associated with WFPSampler's default sublayer and provider. The + appropriate callout is then added and associated with the filter.
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 PrvScenarioBasicPacketInjectionAddFwpmObjects(_In_ const FWPM_FILTER* pFilter, + _In_ const PC_BASIC_PACKET_INJECTION_DATA* pPCBasicPacketInjectionData) +{ + ASSERT(pFilter); + ASSERT(pPCBasicPacketInjectionData); + + UINT32 status = NO_ERROR; + + if(pFilter->layerKey == FWPM_LAYER_INBOUND_IPPACKET_V4 || + pFilter->layerKey == FWPM_LAYER_INBOUND_IPPACKET_V6 || + pFilter->layerKey == FWPM_LAYER_OUTBOUND_IPPACKET_V4 || + pFilter->layerKey == FWPM_LAYER_OUTBOUND_IPPACKET_V6 || + pFilter->layerKey == FWPM_LAYER_IPFORWARD_V4 || + pFilter->layerKey == FWPM_LAYER_IPFORWARD_V6 || + pFilter->layerKey == FWPM_LAYER_INBOUND_TRANSPORT_V4 || + pFilter->layerKey == FWPM_LAYER_INBOUND_TRANSPORT_V6 || + pFilter->layerKey == FWPM_LAYER_OUTBOUND_TRANSPORT_V4 || + pFilter->layerKey == FWPM_LAYER_OUTBOUND_TRANSPORT_V6 || + pFilter->layerKey == FWPM_LAYER_DATAGRAM_DATA_V4 || + pFilter->layerKey == FWPM_LAYER_DATAGRAM_DATA_V6 || + pFilter->layerKey == FWPM_LAYER_INBOUND_ICMP_ERROR_V4 || + pFilter->layerKey == FWPM_LAYER_INBOUND_ICMP_ERROR_V6 || + pFilter->layerKey == FWPM_LAYER_OUTBOUND_ICMP_ERROR_V4 || + pFilter->layerKey == FWPM_LAYER_OUTBOUND_ICMP_ERROR_V6 || + pFilter->layerKey == FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 || + pFilter->layerKey == FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 || + pFilter->layerKey == FWPM_LAYER_ALE_AUTH_CONNECT_V4 || + pFilter->layerKey == FWPM_LAYER_ALE_AUTH_CONNECT_V6 || + pFilter->layerKey == FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4 || + pFilter->layerKey == FWPM_LAYER_ALE_FLOW_ESTABLISHED_V6 + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + || + pFilter->layerKey == FWPM_LAYER_STREAM_PACKET_V4 || + pFilter->layerKey == FWPM_LAYER_STREAM_PACKET_V6 + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + || + pFilter->layerKey == FWPM_LAYER_INBOUND_MAC_FRAME_ETHERNET || + pFilter->layerKey == FWPM_LAYER_OUTBOUND_MAC_FRAME_ETHERNET || + pFilter->layerKey == FWPM_LAYER_INBOUND_MAC_FRAME_NATIVE || + pFilter->layerKey == FWPM_LAYER_OUTBOUND_MAC_FRAME_NATIVE || + pFilter->layerKey == FWPM_LAYER_INGRESS_VSWITCH_ETHERNET || + pFilter->layerKey == FWPM_LAYER_EGRESS_VSWITCH_ETHERNET + +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + ) + { + HANDLE engineHandle = 0; + FWP_BYTE_BLOB byteBlob = {0}; + FWPM_PROVIDER_CONTEXT providerContext = {0}; + FWPM_CALLOUT callout = {0}; + FWPM_FILTER filter = {0}; + + RtlCopyMemory(&filter, + pFilter, + sizeof(FWPM_FILTER)); + + status = HlprGUIDPopulate(&(providerContext.providerContextKey)); + HLPR_BAIL_ON_FAILURE(status); + + providerContext.displayData.name = L"WFPSampler's Basic Packet Injection ProviderContext"; + providerContext.providerKey = (GUID*)&WFPSAMPLER_PROVIDER; + providerContext.type = FWPM_GENERAL_CONTEXT; + providerContext.dataBuffer = &byteBlob; + providerContext.dataBuffer->size = sizeof(PC_BASIC_PACKET_INJECTION_DATA); + providerContext.dataBuffer->data = (UINT8*)pPCBasicPacketInjectionData; + + callout.calloutKey = WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION; + callout.calloutKey.Data4[7] = HlprFwpmLayerGetIDByKey(&(filter.layerKey)); /// Uniquely identifies the callout used + callout.displayData.name = L"WFPSampler's Basic Packet Injection Callout"; + callout.displayData.description = L"Causes callout invocation which blindly injects traffic back"; + callout.flags = FWPM_CALLOUT_FLAG_USES_PROVIDER_CONTEXT; + callout.providerKey = (GUID*)&WFPSAMPLER_PROVIDER; + callout.applicableLayer = filter.layerKey; + + status = HlprGUIDPopulate(&(filter.filterKey)); + HLPR_BAIL_ON_FAILURE(status); + + filter.flags |= FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT; + filter.providerKey = (GUID*)&WFPSAMPLER_PROVIDER; + filter.weight.type = FWP_UINT8; + filter.weight.uint8 = 0xF; + filter.action.type = FWP_ACTION_CALLOUT_TERMINATING; + filter.action.calloutKey = callout.calloutKey; + filter.providerContextKey = providerContext.providerContextKey; + + if(filter.flags & FWPM_FILTER_FLAG_BOOTTIME || + filter.flags & FWPM_FILTER_FLAG_PERSISTENT) + { + providerContext.flags |= FWPM_PROVIDER_CONTEXT_FLAG_PERSISTENT; + + callout.flags |= FWPM_CALLOUT_FLAG_PERSISTENT; + } + + status = HlprFwpmEngineOpen(&engineHandle); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmTransactionBegin(engineHandle); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmProviderContextAdd(engineHandle, + &providerContext); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmCalloutAdd(engineHandle, + &callout); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmFilterAdd(engineHandle, + &filter); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmTransactionCommit(engineHandle); + HLPR_BAIL_ON_FAILURE(status); + + HLPR_BAIL_LABEL: + + if(engineHandle) + { + if(status != NO_ERROR) + HlprFwpmTransactionAbort(engineHandle); + + HlprFwpmEngineClose(&engineHandle); + } + } + else + status = (UINT32)FWP_E_INCOMPATIBLE_LAYER; + + return status; +} + +/** + @scenario_function="ScenarioBasicPacketInjectionRemove" + + Purpose: Function that removes corresponding objects for a previously added + SCENARIO_BASIC_PACKET_INJECTION.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 ScenarioBasicPacketInjectionRemove(_In_ const FWPM_FILTER* pFilter) +{ + ASSERT(pFilter); + + return PrvScenarioBasicPacketInjectionDeleteFwpmObjects(pFilter); +} + +/** + @scenario_function="ScenarioBasicPacketInjectionAdd" + + Purpose: Scenario which will blindly reinject the classified traffic.
+
+ Notes: Adds a filter which references one of the + WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION callouts for the provided layer.
+
+ No data modification is made to the traffic, and checks are in place to prevent + infinite reinjection of the traffic.
+
+ Ideal usage is to implement in the presence of a 3rd party firewall to see how they + coexist with another provider performing injection.
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 ScenarioBasicPacketInjectionAdd(_In_ const FWPM_FILTER* pFilter, + _In_ const PC_BASIC_PACKET_INJECTION_DATA* pPCBasicPacketInjectionData) +{ + + ASSERT(pFilter); + ASSERT(pPCBasicPacketInjectionData); + + return PrvScenarioBasicPacketInjectionAddFwpmObjects(pFilter, + pPCBasicPacketInjectionData); +} + +/** + @rpc_function="RPCInvokeScenarioBasicPacketInjection" + + Purpose: RPC exposed function used to dipatch the scenario routines for + SCENARIO_BASIC_PACKET_INJECTION.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +/* [fault_status][comm_status] */ +error_status_t RPCInvokeScenarioBasicPacketInjection(/* [in] */ handle_t rpcBindingHandle, + /* [in] */ WFPSAMPLER_SCENARIO scenario, + /* [in] */ FWPM_CHANGE_TYPE changeType, + /* [ref][in] */ __RPC__in const FWPM_FILTER0* pFilter, + /* [unique][in] */ __RPC__in_opt const PC_BASIC_PACKET_INJECTION_DATA* pPCBasicPacketInjectionData) +{ + UNREFERENCED_PARAMETER(rpcBindingHandle); + UNREFERENCED_PARAMETER(scenario); + + ASSERT(pFilter); + ASSERT(scenario == SCENARIO_BASIC_PACKET_INJECTION); + ASSERT(changeType < FWPM_CHANGE_TYPE_MAX); + + UINT32 status = NO_ERROR; + + if(changeType == FWPM_CHANGE_ADD) + { + if(pPCBasicPacketInjectionData) + status = ScenarioBasicPacketInjectionAdd(pFilter, + pPCBasicPacketInjectionData); + else + { + status = ERROR_INVALID_PARAMETER; + + HlprLogError(L"RpcInvokeScenarioBasicPacketInjection() [status: %#x][pPCBasicPacketInjectionData: %#x]", + status, + pPCBasicPacketInjectionData); + } + } + else + status = ScenarioBasicPacketInjectionRemove(pFilter); + + return status; +} diff --git a/network/trans/WFPSampler/svc/Scenarios_BasicPacketModification.cpp b/network/trans/WFPSampler/svc/Scenarios_BasicPacketModification.cpp new file mode 100644 index 000000000..a031eb7d4 --- /dev/null +++ b/network/trans/WFPSampler/svc/Scenarios_BasicPacketModification.cpp @@ -0,0 +1,400 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// Scenarios_BasicPacketModification.cpp +// +// Abstract: +// This module contains functions which implements the BASIC_PACKET_MODIFICATION scenarios. +// +// Naming Convention: +// +// +// +// +// i.e. +// +// +// { +// - Function is likely visible to other modules. +// Prv - Function is private to this module. +// } +// +// { +// ScenarioBasicPacketModification - Function pertains to all of the Basic Packet +// Modification Scenarios. +// RPC - Function is and RPC entry point. +// } +// +// { +// Add - Function adds objects. +// Remove - Function removes objects. +// Invoke - Function implements the scenario based on parameters +// passed from the commandline interface +// (WFPSampler.exe). +// } +// +// { +// FwpmObjects - Function acts on WFP objects. +// ScenarioBasicPacketModification - Function pertains to all of the Basic Packet +// Modification Scenarios. +// } +// +// Private Functions: +// PrvScenarioBasicPacketModificationAddFwpmObjects(), +// PrvScenarioBasicPacketModificationDeleteFwpmObjects(), +// ScenarioBasicPacketModificationAdd(), +// ScenarioBasicPacketModificationRemove(), +// +// Public Functions: +// RPCInvokeScenarioBasicPacketModification(), +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Prune filters for enumeration and limit scenario to +// only the supported layers +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "Framework_WFPSamplerService.h" /// . + +/** + @private_function="PrvBasicPacketModificationScenarioDeleteFwpmObjects" + + Purpose: Function that disables the SCENARIO_BASIC_PACKET_MODIFICATION scenarios.
+
+ Notes: Scenario removes the filters using specified filtering conditions at the specified + layer. Associated callouts and provider contexts are removed as well.
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 PrvBasicPacketModificationScenarioDeleteFwpmObjects(_In_ const FWPM_FILTER0* pFilter) +{ + ASSERT(pFilter); + + UINT32 status = NO_ERROR; + HANDLE engineHandle = 0; + HANDLE enumHandle = 0; + UINT32 entryCount = 0; + FWPM_FILTER** ppFilters = 0; + FWPM_FILTER_CONDITION* pFilterConditions = 0; + UINT32 numFilterConditions = 0; + FWPM_FILTER_ENUM_TEMPLATE filterEnumTemplate = {0}; + FWPM_PROVIDER_CONTEXT_ENUM_TEMPLATE providerContextEnumTemplate = {0}; + GUID calloutKey = {0}; + + HlprFwpmFilterConditionPrune(pFilter->filterCondition, + pFilter->numFilterConditions, + &pFilterConditions, + &numFilterConditions); + + calloutKey = WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION; + calloutKey.Data4[7] = HlprFwpmLayerGetIDByKey(&(pFilter->layerKey)); /// Uniquely identifies the callout used + + providerContextEnumTemplate.providerContextType = FWPM_GENERAL_CONTEXT; + + filterEnumTemplate.providerKey = (GUID*)&WFPSAMPLER_PROVIDER; + filterEnumTemplate.layerKey = pFilter->layerKey; + filterEnumTemplate.enumType = FWP_FILTER_ENUM_FULLY_CONTAINED; + filterEnumTemplate.flags = FWP_FILTER_ENUM_FLAG_INCLUDE_BOOTTIME | + FWP_FILTER_ENUM_FLAG_INCLUDE_DISABLED; + filterEnumTemplate.numFilterConditions = pFilterConditions ? numFilterConditions : pFilter->numFilterConditions; + filterEnumTemplate.filterCondition = pFilterConditions ? pFilterConditions : pFilter->filterCondition; + filterEnumTemplate.providerContextTemplate = &providerContextEnumTemplate; + filterEnumTemplate.actionMask = FWP_ACTION_FLAG_CALLOUT; + filterEnumTemplate.calloutKey = &calloutKey; + + status = HlprFwpmEngineOpen(&engineHandle); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmFilterCreateEnumHandle(engineHandle, + &filterEnumTemplate, + &enumHandle); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmFilterEnum(engineHandle, + enumHandle, + 0xFFFFFFFF, + &ppFilters, + &entryCount); + HLPR_BAIL_ON_FAILURE(status); + + if(ppFilters) + { + for(UINT32 filterIndex = 0; + filterIndex < entryCount; + filterIndex++) + { + HlprFwpmFilterDeleteByKey(engineHandle, + &(ppFilters[filterIndex]->filterKey)); + + HlprFwpmCalloutDeleteByKey(engineHandle, + &(ppFilters[filterIndex]->action.calloutKey)); + + if(ppFilters[filterIndex]->flags & FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT) + HlprFwpmProviderContextDeleteByKey(engineHandle, + &(ppFilters[filterIndex]->providerContextKey)); + } + + FwpmFreeMemory((VOID**)&ppFilters); + } + + HLPR_BAIL_LABEL: + + if(engineHandle) + { + if(enumHandle) + HlprFwpmFilterDestroyEnumHandle(engineHandle, + &enumHandle); + + HlprFwpmEngineClose(&engineHandle); + } + + HLPR_DELETE_ARRAY(pFilterConditions); + + return status; +} + +/** + @private_function="PrvBasicPacketModificationScenarioAddFwpmObjects" + + Purpose: Function that enables the SCENARIO_BASIC_PACKET_INJECTION scenarios.
+
+ Notes: Scenario adds a filter using specified filtering conditions to the + specified layer. This filter is associated with WFPSampler's default sublayer and + provider. The appropriate callout and provider contest is then added and + associated with the filter.
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 PrvBasicPacketModificationScenarioAddFwpmObjects(_In_ const FWPM_FILTER* pFilter, + _In_ const PC_BASIC_PACKET_MODIFICATION_DATA* pPCBasicPacketModificationData) +{ + ASSERT(pFilter); + ASSERT(pPCBasicPacketModificationData); + + UINT32 status = NO_ERROR; + + if(pFilter->layerKey == FWPM_LAYER_INBOUND_IPPACKET_V4 || + pFilter->layerKey == FWPM_LAYER_INBOUND_IPPACKET_V6 || + pFilter->layerKey == FWPM_LAYER_OUTBOUND_IPPACKET_V4 || + pFilter->layerKey == FWPM_LAYER_OUTBOUND_IPPACKET_V6 || + pFilter->layerKey == FWPM_LAYER_IPFORWARD_V4 || + pFilter->layerKey == FWPM_LAYER_IPFORWARD_V6 || + pFilter->layerKey == FWPM_LAYER_INBOUND_TRANSPORT_V4 || + pFilter->layerKey == FWPM_LAYER_INBOUND_TRANSPORT_V6 || + pFilter->layerKey == FWPM_LAYER_OUTBOUND_TRANSPORT_V4 || + pFilter->layerKey == FWPM_LAYER_OUTBOUND_TRANSPORT_V6 || + pFilter->layerKey == FWPM_LAYER_DATAGRAM_DATA_V4 || + pFilter->layerKey == FWPM_LAYER_DATAGRAM_DATA_V6 || + pFilter->layerKey == FWPM_LAYER_INBOUND_ICMP_ERROR_V4 || + pFilter->layerKey == FWPM_LAYER_INBOUND_ICMP_ERROR_V6 || + pFilter->layerKey == FWPM_LAYER_OUTBOUND_ICMP_ERROR_V4 || + pFilter->layerKey == FWPM_LAYER_OUTBOUND_ICMP_ERROR_V6 || + pFilter->layerKey == FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 || + pFilter->layerKey == FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 || + pFilter->layerKey == FWPM_LAYER_ALE_AUTH_CONNECT_V4 || + pFilter->layerKey == FWPM_LAYER_ALE_AUTH_CONNECT_V6 || + pFilter->layerKey == FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4 || + pFilter->layerKey == FWPM_LAYER_ALE_FLOW_ESTABLISHED_V6 + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + || + pFilter->layerKey == FWPM_LAYER_STREAM_PACKET_V4 || + pFilter->layerKey == FWPM_LAYER_STREAM_PACKET_V6 + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + || + pFilter->layerKey == FWPM_LAYER_INBOUND_MAC_FRAME_ETHERNET || + pFilter->layerKey == FWPM_LAYER_OUTBOUND_MAC_FRAME_ETHERNET || + pFilter->layerKey == FWPM_LAYER_INBOUND_MAC_FRAME_NATIVE || + pFilter->layerKey == FWPM_LAYER_OUTBOUND_MAC_FRAME_NATIVE || + pFilter->layerKey == FWPM_LAYER_INGRESS_VSWITCH_ETHERNET || + pFilter->layerKey == FWPM_LAYER_EGRESS_VSWITCH_ETHERNET + +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + ) + { + HANDLE engineHandle = 0; + FWP_BYTE_BLOB byteBlob = {0}; + FWPM_PROVIDER_CONTEXT providerContext = {0}; + FWPM_CALLOUT callout = {0}; + FWPM_FILTER filter = {0}; + + RtlCopyMemory(&filter, + pFilter, + sizeof(FWPM_FILTER)); + + status = HlprGUIDPopulate(&(providerContext.providerContextKey)); + HLPR_BAIL_ON_FAILURE(status); + + providerContext.displayData.name = L"WFPSampler's Basic Packet Modification Provider Context"; + providerContext.providerKey = (GUID*)&WFPSAMPLER_PROVIDER; + providerContext.type = FWPM_GENERAL_CONTEXT; + providerContext.dataBuffer = &byteBlob; + providerContext.dataBuffer->size = sizeof(PC_BASIC_PACKET_MODIFICATION_DATA); + providerContext.dataBuffer->data = (UINT8*)pPCBasicPacketModificationData; + + callout.calloutKey = WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION; + callout.calloutKey.Data4[7] = HlprFwpmLayerGetIDByKey(&(filter.layerKey)); /// Uniquely identifies the callout used + callout.displayData.name = L"WFPSampler's Basic Packet Modification Callout"; + callout.displayData.description = L"Causes callout invocation which modifies the headers and injects traffic back"; + callout.flags = FWPM_CALLOUT_FLAG_USES_PROVIDER_CONTEXT; + callout.providerKey = (GUID*)&WFPSAMPLER_PROVIDER; + callout.applicableLayer = filter.layerKey; + + status = HlprGUIDPopulate(&(filter.filterKey)); + HLPR_BAIL_ON_FAILURE(status); + + filter.flags |= FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT; + filter.providerKey = (GUID*)&WFPSAMPLER_PROVIDER; + filter.weight.type = FWP_UINT8; + filter.weight.uint8 = 0xF; + filter.action.type = FWP_ACTION_CALLOUT_UNKNOWN; + filter.action.calloutKey = callout.calloutKey; + filter.providerContextKey = providerContext.providerContextKey; + + if(filter.flags & FWPM_FILTER_FLAG_BOOTTIME || + filter.flags & FWPM_FILTER_FLAG_PERSISTENT) + { + providerContext.flags |= FWPM_PROVIDER_CONTEXT_FLAG_PERSISTENT; + + callout.flags |= FWPM_CALLOUT_FLAG_PERSISTENT; + } + + status = HlprFwpmEngineOpen(&engineHandle); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmTransactionBegin(engineHandle); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmProviderContextAdd(engineHandle, + &providerContext); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmCalloutAdd(engineHandle, + &callout); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmFilterAdd(engineHandle, + &filter); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmTransactionCommit(engineHandle); + HLPR_BAIL_ON_FAILURE(status); + + HLPR_BAIL_LABEL: + + if(engineHandle) + { + if(status != NO_ERROR) + HlprFwpmTransactionAbort(engineHandle); + + HlprFwpmEngineClose(&engineHandle); + } + } + else + status = (UINT32)FWP_E_INCOMPATIBLE_LAYER; + + return status; +} + +/** + @private_function="PrvBasicPacketModificationScenarioRemove" + + Purpose: Function that removes corresponding objects for a previously added + SCENARIO_BASIC_PACKET_MODIFICATION.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 PrvBasicPacketModificationScenarioRemove(_In_ const FWPM_FILTER* pFilter) +{ + ASSERT(pFilter); + + return PrvBasicPacketModificationScenarioDeleteFwpmObjects(pFilter); +} + +/** + @private_function="PrvBasicPacketModificationScenarioAdd" + + Purpose: Scenario which will inject modified traffic back into the stack.
+
+ Notes: Adds a filter which references one of the + WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION callouts for the provided layer.
+
+ Ideal usage is to implement in the presence of a 3rd party firewall to see how + they coexist with another provider performing modified packet injection.
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 PrvBasicPacketModificationScenarioAdd(_In_ const FWPM_FILTER* pFilter, + _In_ const PC_BASIC_PACKET_MODIFICATION_DATA* pPCBasicPacketModificationData) +{ + ASSERT(pFilter); + ASSERT(pPCBasicPacketModificationData); + + return PrvBasicPacketModificationScenarioAddFwpmObjects(pFilter, + pPCBasicPacketModificationData); +} + +/** + @rpc_function="RPCInvokeScenarioBasicPacketModification" + + Purpose: RPC exposed function used to dipatch the scenario routines for + SCENARIO_BASIC_PACKET_MODIFICATION.
+
+ Notes:
+
+ MSDN_Ref:
+*/ + /* [fault_status][comm_status] */ + error_status_t RPCInvokeScenarioBasicPacketModification(/* [in] */ handle_t rpcBindingHandle, + /* [in] */ WFPSAMPLER_SCENARIO scenario, + /* [in] */ FWPM_CHANGE_TYPE changeType, + /* [ref][in] */ __RPC__in const FWPM_FILTER0* pFilter, + /* [unique][in] */ __RPC__in_opt const PC_BASIC_PACKET_MODIFICATION_DATA* pPCBasicPacketModificationData) +{ + UNREFERENCED_PARAMETER(rpcBindingHandle); + UNREFERENCED_PARAMETER(scenario); + + ASSERT(pFilter); + ASSERT(scenario == SCENARIO_BASIC_PACKET_MODIFICATION); + ASSERT(changeType < FWPM_CHANGE_TYPE_MAX); + + UINT32 status = NO_ERROR; + + if(changeType == FWPM_CHANGE_ADD) + { + if(pPCBasicPacketModificationData) + status = PrvBasicPacketModificationScenarioAdd(pFilter, + pPCBasicPacketModificationData); + else + { + status = ERROR_INVALID_PARAMETER; + + HlprLogError(L"RPCInvokeScenarioBasicPacketModification() [status: %#x][pPCBasicPacketModificationData: %#x]", + status, + pPCBasicPacketModificationData); + } + } + else + status = PrvBasicPacketModificationScenarioRemove(pFilter); + + return status; +} diff --git a/network/trans/WFPSampler/svc/Scenarios_BasicStreamInjection.cpp b/network/trans/WFPSampler/svc/Scenarios_BasicStreamInjection.cpp new file mode 100644 index 000000000..e302e06af --- /dev/null +++ b/network/trans/WFPSampler/svc/Scenarios_BasicStreamInjection.cpp @@ -0,0 +1,361 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// Scenarios_BasicStreamInjection.cpp +// +// Abstract: +// This module contains functions which implements the BASIC_STREAM_INJECTION scenarios. +// +// Naming Convention: +// +// +// +// +// i.e. +// +// +// { +// - Function is likely visible to other modules +// Prv - Function is private to this module. +// } +// +// { +// ScenarioBasicStreamInjection - Function pertains to all of the Basic Stream Injection +// Scenarios +// RPC - Function is and RPC entry point. +// } +// +// { +// Add - Function adds objects +// Remove - Function removes objects +// Invoke - Function implements the scenario based on parameters +// passed from the commandline interface +// (WFPSampler.exe). +// } +// +// { +// FwpmObjects - Function acts on WFP objects. +// ScenarioBasicStreamInjection - Function pertains to all of the Basic Stream Injection +// Scenarios +// } +// +// Private Functions: +// PrvScenarioBasicStreamInjectionDeleteFwpmObjects(), +// PrvScenarioBasicStreamInjectionAddFwpmObjects(), +// ScenarioBasicStreamInjectionAdd(), +// ScenarioBasicStreamInjectionRemove(), +// +// Public Functions: +// RPCInvokeScenarioBasicStreamInjection(), +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Prune filters for enumeration and limit scenario to +// only the supported layers +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "Framework_WFPSamplerService.h" /// . + +/** + @private_function="PrvScenarioBasicStreamInjectionDeleteFwpmObjects" + + Purpose: Function that disables the SCENARIO_BASIC_STREAM_INJECTION scenarios.
+
+ Notes: Scenario removes the filters using specified filtering conditions at the specified + layer. Associated callouts and provider contexts are removed as well.
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 PrvScenarioBasicStreamInjectionDeleteFwpmObjects(_In_ const FWPM_FILTER* pFilter) +{ + ASSERT(pFilter); + + UINT32 status = NO_ERROR; + HANDLE engineHandle = 0; + HANDLE enumHandle = 0; + UINT32 entryCount = 0; + FWPM_FILTER** ppFilters = 0; + FWPM_FILTER_CONDITION* pFilterConditions = 0; + UINT32 numFilterConditions = 0; + FWPM_FILTER_ENUM_TEMPLATE filterEnumTemplate = {0}; + FWPM_PROVIDER_CONTEXT_ENUM_TEMPLATE providerContextEnumTemplate = {0}; + GUID calloutKey = {0}; + + HlprFwpmFilterConditionPrune(pFilter->filterCondition, + pFilter->numFilterConditions, + &pFilterConditions, + &numFilterConditions); + + calloutKey = WFPSAMPLER_CALLOUT_BASIC_STREAM_INJECTION; + calloutKey.Data4[7] = HlprFwpmLayerGetIDByKey(&(pFilter->layerKey)); /// Uniquely identifies the callout used + + providerContextEnumTemplate.providerContextType = FWPM_GENERAL_CONTEXT; + + filterEnumTemplate.providerKey = (GUID*)&WFPSAMPLER_PROVIDER; + filterEnumTemplate.layerKey = pFilter->layerKey; + filterEnumTemplate.enumType = FWP_FILTER_ENUM_FULLY_CONTAINED; + filterEnumTemplate.flags = FWP_FILTER_ENUM_FLAG_INCLUDE_BOOTTIME | + FWP_FILTER_ENUM_FLAG_INCLUDE_DISABLED; + filterEnumTemplate.numFilterConditions = pFilterConditions ? numFilterConditions : pFilter->numFilterConditions; + filterEnumTemplate.filterCondition = pFilterConditions ? pFilterConditions : pFilter->filterCondition; + filterEnumTemplate.providerContextTemplate = &providerContextEnumTemplate; + filterEnumTemplate.actionMask = FWP_ACTION_FLAG_CALLOUT; + filterEnumTemplate.calloutKey = &calloutKey; + + status = HlprFwpmEngineOpen(&engineHandle); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmFilterCreateEnumHandle(engineHandle, + &filterEnumTemplate, + &enumHandle); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmFilterEnum(engineHandle, + enumHandle, + 0xFFFFFFFF, + &ppFilters, + &entryCount); + HLPR_BAIL_ON_FAILURE(status); + + if(ppFilters) + { + for(UINT32 filterIndex = 0; + filterIndex < entryCount; + filterIndex++) + { + HlprFwpmFilterDeleteByKey(engineHandle, + &(ppFilters[filterIndex]->filterKey)); + + HlprFwpmCalloutDeleteByKey(engineHandle, + &(ppFilters[filterIndex]->action.calloutKey)); + + if(ppFilters[filterIndex]->flags & FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT) + HlprFwpmProviderContextDeleteByKey(engineHandle, + &(ppFilters[filterIndex]->providerContextKey)); + } + + FwpmFreeMemory((VOID**)&ppFilters); + } + + HLPR_BAIL_LABEL: + + if(engineHandle) + { + if(enumHandle) + HlprFwpmFilterDestroyEnumHandle(engineHandle, + &enumHandle); + + HlprFwpmEngineClose(&engineHandle); + } + + HLPR_DELETE_ARRAY(pFilterConditions); + + return status; +} + +/** + @private_function="PrvScenarioBasicPacketInjectionAddFwpmObjects" + + Purpose: Function that enables the SCENARIO_BASIC_STREAM_INJECTION scenarios.
+
+ Notes: Scenario adds a filter using specified filtering conditions to the specified layer. + This filter is associated with WFPSampler's default sublayer and provider. The + appropriate callout and provider context is then added and associated with the + filter.
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 PrvScenarioBasicStreamInjectionAddFwpmObjects(_In_ const FWPM_FILTER* pFilter, + _In_ const PC_BASIC_STREAM_INJECTION_DATA* pPCBasicStreamInjectionData) +{ + ASSERT(pFilter); + ASSERT(pPCBasicStreamInjectionData); + + UINT32 status = NO_ERROR; + + if(pFilter->layerKey == FWPM_LAYER_STREAM_V4 || + pFilter->layerKey == FWPM_LAYER_STREAM_V6) + { + HANDLE engineHandle = 0; + FWP_BYTE_BLOB byteBlob = {0}; + FWPM_PROVIDER_CONTEXT providerContext = {0}; + FWPM_CALLOUT callout = {0}; + FWPM_FILTER filter = {0}; + + RtlCopyMemory(&filter, + pFilter, + sizeof(FWPM_FILTER)); + + status = HlprGUIDPopulate(&(providerContext.providerContextKey)); + HLPR_BAIL_ON_FAILURE(status); + + providerContext.displayData.name = L"WFPSampler's Basic Stream Injection Provider Context"; + providerContext.providerKey = (GUID*)&WFPSAMPLER_PROVIDER; + providerContext.type = FWPM_GENERAL_CONTEXT; + providerContext.dataBuffer = &byteBlob; + providerContext.dataBuffer->size = sizeof(PC_BASIC_STREAM_INJECTION_DATA); + providerContext.dataBuffer->data = (UINT8*)pPCBasicStreamInjectionData; + + callout.calloutKey = WFPSAMPLER_CALLOUT_BASIC_STREAM_INJECTION; + callout.calloutKey.Data4[7] = HlprFwpmLayerGetIDByKey(&(filter.layerKey)); /// Uniquely identifies the callout used + callout.displayData.name = L"WFPSampler's Basic Stream Injection Callout"; + callout.displayData.description = L"Causes callout invocation which blindly injects stream data back"; + callout.flags = FWPM_CALLOUT_FLAG_USES_PROVIDER_CONTEXT; + callout.providerKey = (GUID*)&WFPSAMPLER_PROVIDER; + callout.applicableLayer = filter.layerKey; + + status = HlprGUIDPopulate(&(filter.filterKey)); + HLPR_BAIL_ON_FAILURE(status); + + filter.flags |= FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT; + filter.providerKey = (GUID*)&WFPSAMPLER_PROVIDER; + filter.weight.type = FWP_UINT8; + filter.weight.uint8 = 0xF; + filter.action.type = FWP_ACTION_CALLOUT_UNKNOWN; + filter.action.calloutKey = callout.calloutKey; + filter.providerContextKey = providerContext.providerContextKey; + + if(filter.flags & FWPM_FILTER_FLAG_BOOTTIME || + filter.flags & FWPM_FILTER_FLAG_PERSISTENT) + { + providerContext.flags |= FWPM_PROVIDER_CONTEXT_FLAG_PERSISTENT; + + callout.flags |= FWPM_CALLOUT_FLAG_PERSISTENT; + } + + status = HlprFwpmEngineOpen(&engineHandle); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmTransactionBegin(engineHandle); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmProviderContextAdd(engineHandle, + &providerContext); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmCalloutAdd(engineHandle, + &callout); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmFilterAdd(engineHandle, + &filter); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmTransactionCommit(engineHandle); + HLPR_BAIL_ON_FAILURE(status); + + HLPR_BAIL_LABEL: + + if(engineHandle) + { + if(status != NO_ERROR) + HlprFwpmTransactionAbort(engineHandle); + + HlprFwpmEngineClose(&engineHandle); + } + } + else + status = (UINT32)FWP_E_INCOMPATIBLE_LAYER; + + return status; +} + +/** + @scenario_function="ScenarioBasicStreamInjectionRemove" + + Purpose: Function that removes corresponding objects for a previously added + SCENARIO_BASIC_STREAM_INJECTION.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 ScenarioBasicStreamInjectionRemove(_In_ const FWPM_FILTER* pFilter) +{ + ASSERT(pFilter); + + return PrvScenarioBasicStreamInjectionDeleteFwpmObjects(pFilter); +} + +/** + @scenario_function="ScenarioBasicStreamInjectionAdd" + + Purpose: Scenario which will blindly reinject the classified stream data.
+
+ Notes: Adds a filter which references one of the + WFPSAMPLER_CALLOUT_BASIC_STREAM_INJECTION callouts for the provided layer.
+
+ No data modification is made to the data
+
+ Ideal usage is to implement in the presence of a 3rd party firewall to see how they + coexist with another provider performing stream injection.
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 ScenarioBasicStreamInjectionAdd(_In_ const FWPM_FILTER* pFilter, + _In_ const PC_BASIC_STREAM_INJECTION_DATA* pPCBasicStreamInjectionData) +{ + ASSERT(pFilter); + ASSERT(pPCBasicStreamInjectionData); + + return PrvScenarioBasicStreamInjectionAddFwpmObjects(pFilter, + pPCBasicStreamInjectionData); +} + +/** + @rpc_function="RPCInvokeScenarioBasicStreamInjection" + + Purpose: RPC exposed function used to dipatch the scenario routines for + SCENARIO_BASIC_STREAM_INJECTION.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +/* [fault_status][comm_status] */ +error_status_t RPCInvokeScenarioBasicStreamInjection(/* [in] */ handle_t rpcBindingHandle, + /* [in] */ WFPSAMPLER_SCENARIO scenario, + /* [in] */ FWPM_CHANGE_TYPE changeType, + /* [ref][in] */ __RPC__in const FWPM_FILTER0* pFilter, + /* [unique][in] */ __RPC__in_opt const PC_BASIC_STREAM_INJECTION_DATA* pPCBasicStreamInjectionData) +{ + UNREFERENCED_PARAMETER(rpcBindingHandle); + UNREFERENCED_PARAMETER(scenario); + + ASSERT(pFilter); + ASSERT(scenario == SCENARIO_BASIC_STREAM_INJECTION); + ASSERT(changeType < FWPM_CHANGE_TYPE_MAX); + + UINT32 status = NO_ERROR; + + if(changeType == FWPM_CHANGE_ADD) + { + if(pPCBasicStreamInjectionData) + status = ScenarioBasicStreamInjectionAdd(pFilter, + pPCBasicStreamInjectionData); + else + { + status = ERROR_INVALID_PARAMETER; + + HlprLogError(L"RpcInvokeScenarioBasicStreamInjection() [status: %#x][pPCBasicStreamInjectionData: %#x]", + status, + pPCBasicStreamInjectionData); + } + } + else + status = ScenarioBasicStreamInjectionRemove(pFilter); + + return status; +} diff --git a/network/trans/WFPSampler/svc/Scenarios_FastPacketInjection.cpp b/network/trans/WFPSampler/svc/Scenarios_FastPacketInjection.cpp new file mode 100644 index 000000000..cd2e18b7f --- /dev/null +++ b/network/trans/WFPSampler/svc/Scenarios_FastPacketInjection.cpp @@ -0,0 +1,353 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// Scenarios_FastPacketInjection.cpp +// +// Abstract: +// This module contains functions which implements the FAST_PACKET_INJECTION scenarios. +// +// Naming Convention: +// +// +// +// +// i.e. +// +// +// { +// - Function is likely visible to other modules +// Prv - Function is private to this module. +// } +// +// { +// ScenarioFastPacketInjection - Function pertains to all of the Fast Packet Injection +// Scenarios +// RPC - Function is and RPC entry point. +// } +// +// { +// Add - Function adds objects +// Remove - Function removes objects +// Invoke - Function implements the scenario based on parameters +// passed from the commandline interface +// (WFPSampler.exe). +// } +// +// { +// FwpmObjects - Function acts on WFP objects. +// ScenarioFastPacketInjection - Function pertains to all of the Fast Packet Injection +// Scenarios +// } +// +// Private Functions: +// PrvScenarioFastPacketInjectionAddFwpmObjects(), +// PrvScenarioFastPacketInjectionDeleteFwpmObjects(), +// ScenarioFastStreamInjectionAdd(), +// ScenarioFastStreamInjectionRemove(), +// +// Public Functions: +// RPCInvokeScenarioFastPacketInjection(), +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Prune filters for enumeration and limit scenario to +// only the supported layers +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "Framework_WFPSamplerService.h" /// . + +/** + @private_function="PrvScenarioFastPacketInjectionDeleteFwpmObjects" + + Purpose: Function that disables the SCENARIO_FAST_PACKET_INJECTION scenarios.
+
+ Notes: Scenario removes the filters using specified filtering conditions at the specified + layer. Associated callouts are removed as well.
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 PrvScenarioFastPacketInjectionDeleteFwpmObjects(_In_ const FWPM_FILTER* pFilter) +{ + ASSERT(pFilter); + + UINT32 status = NO_ERROR; + HANDLE engineHandle = 0; + HANDLE enumHandle = 0; + UINT32 entryCount = 0; + FWPM_FILTER** ppFilters = 0; + FWPM_FILTER_CONDITION* pFilterConditions = 0; + UINT32 numFilterConditions = 0; + FWPM_FILTER_ENUM_TEMPLATE filterEnumTemplate = {0}; + GUID calloutKey = {0}; + + HlprFwpmFilterConditionPrune(pFilter->filterCondition, + pFilter->numFilterConditions, + &pFilterConditions, + &numFilterConditions); + + calloutKey = WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION; + calloutKey.Data4[7] = HlprFwpmLayerGetIDByKey(&(pFilter->layerKey)); /// Uniquely identifies the callout used + + filterEnumTemplate.providerKey = (GUID*)&WFPSAMPLER_PROVIDER; + filterEnumTemplate.layerKey = pFilter->layerKey; + filterEnumTemplate.enumType = FWP_FILTER_ENUM_FULLY_CONTAINED; + filterEnumTemplate.flags = FWP_FILTER_ENUM_FLAG_INCLUDE_BOOTTIME | + FWP_FILTER_ENUM_FLAG_INCLUDE_DISABLED; + filterEnumTemplate.numFilterConditions = pFilterConditions ? numFilterConditions : pFilter->numFilterConditions; + filterEnumTemplate.filterCondition = pFilterConditions ? pFilterConditions : pFilter->filterCondition; + filterEnumTemplate.actionMask = FWP_ACTION_FLAG_CALLOUT; + filterEnumTemplate.calloutKey = &calloutKey; + + status = HlprFwpmEngineOpen(&engineHandle); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmFilterCreateEnumHandle(engineHandle, + &filterEnumTemplate, + &enumHandle); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmFilterEnum(engineHandle, + enumHandle, + 0xFFFFFFFF, + &ppFilters, + &entryCount); + HLPR_BAIL_ON_FAILURE(status); + + if(ppFilters) + { + for(UINT32 filterIndex = 0; + filterIndex < entryCount; + filterIndex++) + { + HlprFwpmFilterDeleteByKey(engineHandle, + &(ppFilters[filterIndex]->filterKey)); + + HlprFwpmCalloutDeleteByKey(engineHandle, + &(ppFilters[filterIndex]->action.calloutKey)); + } + + FwpmFreeMemory((VOID**)&ppFilters); + } + + HLPR_BAIL_LABEL: + + if(engineHandle) + { + if(enumHandle) + HlprFwpmFilterDestroyEnumHandle(engineHandle, + &enumHandle); + + HlprFwpmEngineClose(&engineHandle); + } + + HLPR_DELETE_ARRAY(pFilterConditions); + + return status; +} + +/** + @private_function="PrvScenarioFastPacketInjectionAddFwpmObjects" + + Purpose: Function that enables the SCENARIO_FAST_PACKET_INJECTION scenarios.
+
+ Notes: Scenario adds a filter using specified filtering conditions to the specified layer. + This filter is associated with WFPSampler's default sublayer and provider. The + appropriate callout is then added and associated with the filter.
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 PrvScenarioFastPacketInjectionAddFwpmObjects(_In_ const FWPM_FILTER* pFilter) +{ + ASSERT(pFilter); + + UINT32 status = NO_ERROR; + + if(pFilter->layerKey == FWPM_LAYER_INBOUND_IPPACKET_V4 || + pFilter->layerKey == FWPM_LAYER_INBOUND_IPPACKET_V6 || + pFilter->layerKey == FWPM_LAYER_OUTBOUND_IPPACKET_V4 || + pFilter->layerKey == FWPM_LAYER_OUTBOUND_IPPACKET_V6 || + pFilter->layerKey == FWPM_LAYER_IPFORWARD_V4 || + pFilter->layerKey == FWPM_LAYER_IPFORWARD_V6 || + pFilter->layerKey == FWPM_LAYER_INBOUND_TRANSPORT_V4 || + pFilter->layerKey == FWPM_LAYER_INBOUND_TRANSPORT_V6 || + pFilter->layerKey == FWPM_LAYER_OUTBOUND_TRANSPORT_V4 || + pFilter->layerKey == FWPM_LAYER_OUTBOUND_TRANSPORT_V6 || + pFilter->layerKey == FWPM_LAYER_DATAGRAM_DATA_V4 || + pFilter->layerKey == FWPM_LAYER_DATAGRAM_DATA_V6 || + pFilter->layerKey == FWPM_LAYER_INBOUND_ICMP_ERROR_V4 || + pFilter->layerKey == FWPM_LAYER_INBOUND_ICMP_ERROR_V6 || + pFilter->layerKey == FWPM_LAYER_OUTBOUND_ICMP_ERROR_V4 || + pFilter->layerKey == FWPM_LAYER_OUTBOUND_ICMP_ERROR_V6 || + pFilter->layerKey == FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 || + pFilter->layerKey == FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 || + pFilter->layerKey == FWPM_LAYER_ALE_AUTH_CONNECT_V4 || + pFilter->layerKey == FWPM_LAYER_ALE_AUTH_CONNECT_V6 || + pFilter->layerKey == FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4 || + pFilter->layerKey == FWPM_LAYER_ALE_FLOW_ESTABLISHED_V6 + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + || + pFilter->layerKey == FWPM_LAYER_STREAM_PACKET_V4 || + pFilter->layerKey == FWPM_LAYER_STREAM_PACKET_V6 + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + || + pFilter->layerKey == FWPM_LAYER_INBOUND_MAC_FRAME_ETHERNET || + pFilter->layerKey == FWPM_LAYER_OUTBOUND_MAC_FRAME_ETHERNET || + pFilter->layerKey == FWPM_LAYER_INBOUND_MAC_FRAME_NATIVE || + pFilter->layerKey == FWPM_LAYER_OUTBOUND_MAC_FRAME_NATIVE || + pFilter->layerKey == FWPM_LAYER_INGRESS_VSWITCH_ETHERNET || + pFilter->layerKey == FWPM_LAYER_EGRESS_VSWITCH_ETHERNET + +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + ) + { + HANDLE engineHandle = 0; + FWPM_CALLOUT callout = {0}; + FWPM_FILTER filter = {0}; + + RtlCopyMemory(&filter, + pFilter, + sizeof(FWPM_FILTER)); + + callout.calloutKey = WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION; + callout.calloutKey.Data4[7] = HlprFwpmLayerGetIDByKey(&(filter.layerKey)); /// Uniquely identifies the callout used + callout.displayData.name = L"WFPSampler's Fast Packet Injection Callout"; + callout.displayData.description = L"Causes callout invocation which blindly injects traffic back"; + callout.providerKey = (GUID*)&WFPSAMPLER_PROVIDER; + callout.applicableLayer = filter.layerKey; + + status = HlprGUIDPopulate(&(filter.filterKey)); + HLPR_BAIL_ON_FAILURE(status); + + filter.providerKey = (GUID*)&WFPSAMPLER_PROVIDER; + filter.weight.type = FWP_UINT8; + filter.weight.uint8 = 0xF; + filter.action.type = FWP_ACTION_CALLOUT_TERMINATING; + filter.action.calloutKey = callout.calloutKey; + + if(filter.flags & FWPM_FILTER_FLAG_BOOTTIME || + filter.flags & FWPM_FILTER_FLAG_PERSISTENT) + callout.flags = FWPM_CALLOUT_FLAG_PERSISTENT; + + status = HlprFwpmEngineOpen(&engineHandle); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmTransactionBegin(engineHandle); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmCalloutAdd(engineHandle, + &callout); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmFilterAdd(engineHandle, + &filter); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmTransactionCommit(engineHandle); + HLPR_BAIL_ON_FAILURE(status); + + HLPR_BAIL_LABEL: + + if(engineHandle) + { + if(status != NO_ERROR) + HlprFwpmTransactionAbort(engineHandle); + + HlprFwpmEngineClose(&engineHandle); + } + } + else + status = (UINT32)FWP_E_INCOMPATIBLE_LAYER; + + return status; +} + +/** + @scenario_function="ScenarioFastPacketInjectionRemove" + + Purpose: Function that removes corresponding objects for a previously added + SCENARIO_FAST_PACKET_INJECTION.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 ScenarioFastPacketInjectionRemove(_In_ const FWPM_FILTER* pFilter) +{ + ASSERT(pFilter); + + return PrvScenarioFastPacketInjectionDeleteFwpmObjects(pFilter); +} + +/** + @scenario_function="ScenarioFastPacketInjectionAdd" + + Purpose: Scenario which will blindly reinject the classified traffic.
+
+ Notes: Adds a filter which references one of the + WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION callouts for the provided layer.
+
+ No data modification is made to the traffic, and checks are in place to prevent + infinite reinjection of the traffic.
+
+ Ideal usage is to implement in the presence of a 3rd party firewall to see how they + coexist with another provider performing injection.
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 ScenarioFastPacketInjectionAdd(_In_ const FWPM_FILTER* pFilter) +{ + + ASSERT(pFilter); + + return PrvScenarioFastPacketInjectionAddFwpmObjects(pFilter); +} + +/** + @rpc_function="RPCInvokeScenarioFastPacketInjection" + + Purpose: RPC exposed function used to dipatch the scenario routines for + SCENARIO_FAST_PACKET_INJECTION.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +error_status_t RPCInvokeScenarioFastPacketInjection(/* [in] */ handle_t rpcBindingHandle, + /* [in] */ WFPSAMPLER_SCENARIO scenario, + /* [in] */ FWPM_CHANGE_TYPE changeType, + /* [ref][in] */ __RPC__in const FWPM_FILTER0* pFilter) +{ + UNREFERENCED_PARAMETER(rpcBindingHandle); + UNREFERENCED_PARAMETER(scenario); + + ASSERT(pFilter); + ASSERT(scenario == SCENARIO_FAST_PACKET_INJECTION); + ASSERT(changeType < FWPM_CHANGE_TYPE_MAX); + + UINT32 status = NO_ERROR; + + if(changeType == FWPM_CHANGE_ADD) + status = ScenarioFastPacketInjectionAdd(pFilter); + else + status = ScenarioFastPacketInjectionRemove(pFilter); + + return status; +} diff --git a/network/trans/WFPSampler/svc/Scenarios_FastStreamInjection.cpp b/network/trans/WFPSampler/svc/Scenarios_FastStreamInjection.cpp new file mode 100644 index 000000000..8013377b2 --- /dev/null +++ b/network/trans/WFPSampler/svc/Scenarios_FastStreamInjection.cpp @@ -0,0 +1,312 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// Scenarios_FastStreamInjection.cpp +// +// Abstract: +// This module contains functions which implements the FAST_STREAM_INJECTION scenarios. +// +// Naming Convention: +// +// +// +// +// i.e. +// +// +// { +// - Function is likely visible to other modules +// Prv - Function is private to this module. +// } +// +// { +// ScenarioFastStreamInjection - Function pertains to all of the Fast Stream Injection +// Scenarios +// RPC - Function is and RPC entry point. +// } +// +// { +// Add - Function adds objects +// Remove - Function removes objects +// Invoke - Function implements the scenario based on parameters +// passed from the commandline interface +// (WFPSampler.exe). +// } +// +// { +// FwpmObjects - Function acts on WFP objects. +// ScenarioFastStreamInjection - Function pertains to all of the Fast Stream Injection +// Scenarios +// } +// +// Private Functions: +// PrvScenarioFastStreamInjectionAddFwpmObjects(), +// PrvScenarioFastStreamInjectionDeleteFwpmObjects(), +// ScenarioFastStreamInjectionAdd(), +// ScenarioFastStreamInjectionRemove(), +// +// Public Functions: +// RPCInvokeScenarioFastStreamInjection(), +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Prune filters for enumeration and limit scenario to +// only the supported layers +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "Framework_WFPSamplerService.h" /// . + +/** + @private_function="PrvScenarioFastStreamInjectionDeleteFwpmObjects" + + Purpose: Function that disables the SCENARIO_FAST_STREAM_INJECTION scenarios.
+
+ Notes: Scenario removes the filters using specified filtering conditions at the specified + layer. Associated callouts are removed as well.
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 PrvScenarioFastStreamInjectionDeleteFwpmObjects(_In_ const FWPM_FILTER* pFilter) +{ + ASSERT(pFilter); + + UINT32 status = NO_ERROR; + HANDLE engineHandle = 0; + HANDLE enumHandle = 0; + UINT32 entryCount = 0; + FWPM_FILTER** ppFilters = 0; + FWPM_FILTER_CONDITION* pFilterConditions = 0; + UINT32 numFilterConditions = 0; + FWPM_FILTER_ENUM_TEMPLATE filterEnumTemplate = {0}; + GUID calloutKey = {0}; + + HlprFwpmFilterConditionPrune(pFilter->filterCondition, + pFilter->numFilterConditions, + &pFilterConditions, + &numFilterConditions); + + calloutKey = WFPSAMPLER_CALLOUT_FAST_STREAM_INJECTION; + calloutKey.Data4[7] = HlprFwpmLayerGetIDByKey(&(pFilter->layerKey)); /// Uniquely identifies the callout used + + filterEnumTemplate.providerKey = (GUID*)&WFPSAMPLER_PROVIDER; + filterEnumTemplate.layerKey = pFilter->layerKey; + filterEnumTemplate.enumType = FWP_FILTER_ENUM_FULLY_CONTAINED; + filterEnumTemplate.flags = FWP_FILTER_ENUM_FLAG_INCLUDE_BOOTTIME | + FWP_FILTER_ENUM_FLAG_INCLUDE_DISABLED; + filterEnumTemplate.numFilterConditions = pFilterConditions ? numFilterConditions : pFilter->numFilterConditions; + filterEnumTemplate.filterCondition = pFilterConditions ? pFilterConditions : pFilter->filterCondition; + filterEnumTemplate.actionMask = FWP_ACTION_FLAG_CALLOUT; + filterEnumTemplate.calloutKey = &calloutKey; + + status = HlprFwpmEngineOpen(&engineHandle); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmFilterCreateEnumHandle(engineHandle, + &filterEnumTemplate, + &enumHandle); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmFilterEnum(engineHandle, + enumHandle, + 0xFFFFFFFF, + &ppFilters, + &entryCount); + HLPR_BAIL_ON_FAILURE(status); + + if(ppFilters) + { + for(UINT32 filterIndex = 0; + filterIndex < entryCount; + filterIndex++) + { + HlprFwpmFilterDeleteByKey(engineHandle, + &(ppFilters[filterIndex]->filterKey)); + + HlprFwpmCalloutDeleteByKey(engineHandle, + &(ppFilters[filterIndex]->action.calloutKey)); + } + + FwpmFreeMemory((VOID**)&ppFilters); + } + + HLPR_BAIL_LABEL: + + if(engineHandle) + { + if(enumHandle) + HlprFwpmFilterDestroyEnumHandle(engineHandle, + &enumHandle); + + HlprFwpmEngineClose(&engineHandle); + } + + HLPR_DELETE_ARRAY(pFilterConditions); + + return status; +} + +/** + @private_function="PrvScenarioFastStreamInjectionAddFwpmObjects" + + Purpose: Function that enables the SCENARIO_FAST_STREAM_INJECTION scenarios.
+
+ Notes: Scenario adds a filter using specified filtering conditions to the specified layer. + This filter is associated with WFPSampler's default sublayer and provider. The + appropriate callout is then added and associated with the filter.
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 PrvScenarioFastStreamInjectionAddFwpmObjects(_In_ const FWPM_FILTER* pFilter) +{ + ASSERT(pFilter); + + UINT32 status = NO_ERROR; + + if(pFilter->layerKey == FWPM_LAYER_STREAM_V4 || + pFilter->layerKey == FWPM_LAYER_STREAM_V6) + { + HANDLE engineHandle = 0; + FWPM_CALLOUT callout = {0}; + FWPM_FILTER filter = {0}; + + RtlCopyMemory(&filter, + pFilter, + sizeof(FWPM_FILTER)); + + callout.calloutKey = WFPSAMPLER_CALLOUT_FAST_STREAM_INJECTION; + callout.calloutKey.Data4[7] = HlprFwpmLayerGetIDByKey(&(filter.layerKey)); /// Uniquely identifies the callout used + callout.displayData.name = L"WFPSampler's Fast Stream Injection Callout"; + callout.displayData.description = L"Causes callout invocation which blindly injects data back into the stream"; + callout.providerKey = (GUID*)&WFPSAMPLER_PROVIDER; + callout.applicableLayer = filter.layerKey; + + status = HlprGUIDPopulate(&(filter.filterKey)); + HLPR_BAIL_ON_FAILURE(status); + + filter.providerKey = (GUID*)&WFPSAMPLER_PROVIDER; + filter.weight.type = FWP_UINT8; + filter.weight.uint8 = 0xF; + filter.action.type = FWP_ACTION_CALLOUT_TERMINATING; + filter.action.calloutKey = callout.calloutKey; + + if(filter.flags & FWPM_FILTER_FLAG_BOOTTIME || + filter.flags & FWPM_FILTER_FLAG_PERSISTENT) + callout.flags = FWPM_CALLOUT_FLAG_PERSISTENT; + + status = HlprFwpmEngineOpen(&engineHandle); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmTransactionBegin(engineHandle); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmCalloutAdd(engineHandle, + &callout); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmFilterAdd(engineHandle, + &filter); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmTransactionCommit(engineHandle); + HLPR_BAIL_ON_FAILURE(status); + + HLPR_BAIL_LABEL: + + if(engineHandle) + { + if(status != NO_ERROR) + HlprFwpmTransactionAbort(engineHandle); + + HlprFwpmEngineClose(&engineHandle); + } + } + else + status = (UINT32) FWP_E_INCOMPATIBLE_LAYER; + + return status; +} + +/** + @scenario_function="ScenarioFastStreamInjectionRemove" + + Purpose: Function that removes corresponding objects for a previously added + SCENARIO_FAST_STREAM_INJECTION.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 ScenarioFastStreamInjectionRemove(_In_ const FWPM_FILTER* pFilter) +{ + ASSERT(pFilter); + + return PrvScenarioFastStreamInjectionDeleteFwpmObjects(pFilter); +} + +/** + @scenario_function="ScenarioFastStreamInjectionAdd" + + Purpose: Scenario which will blindly reinject the classified traffic.
+
+ Notes: Adds a filter which references one of the + WFPSAMPLER_CALLOUT_FAST_STREAM_INJECTION callouts for the provided layer.
+
+ No data modification is made to the traffic, and checks are in place to prevent + infinite reinjection of the traffic.
+
+ Ideal usage is to implement in the presence of a 3rd party firewall to see how they + coexist with another provider performing injection.
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 ScenarioFastStreamInjectionAdd(_In_ const FWPM_FILTER* pFilter) +{ + + ASSERT(pFilter); + + return PrvScenarioFastStreamInjectionAddFwpmObjects(pFilter); +} + +/** + @rpc_function="RPCInvokeScenarioFastStreamInjection" + + Purpose: RPC exposed function used to dipatch the scenario routines for + SCENARIO_FAST_STREAM_INJECTION.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +error_status_t RPCInvokeScenarioFastStreamInjection(/* [in] */ handle_t rpcBindingHandle, + /* [in] */ WFPSAMPLER_SCENARIO scenario, + /* [in] */ FWPM_CHANGE_TYPE changeType, + /* [ref][in] */ __RPC__in const FWPM_FILTER0* pFilter) +{ + UNREFERENCED_PARAMETER(rpcBindingHandle); + UNREFERENCED_PARAMETER(scenario); + + ASSERT(pFilter); + ASSERT(scenario == SCENARIO_FAST_STREAM_INJECTION); + ASSERT(changeType < FWPM_CHANGE_TYPE_MAX); + + UINT32 status = NO_ERROR; + + if(changeType == FWPM_CHANGE_ADD) + status = ScenarioFastStreamInjectionAdd(pFilter); + else + status = ScenarioFastStreamInjectionRemove(pFilter); + + return status; +} diff --git a/network/trans/WFPSampler/svc/Scenarios_FlowAssociation.cpp b/network/trans/WFPSampler/svc/Scenarios_FlowAssociation.cpp new file mode 100644 index 000000000..b767c2284 --- /dev/null +++ b/network/trans/WFPSampler/svc/Scenarios_FlowAssociation.cpp @@ -0,0 +1,347 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// Scenarios_FlowAssociation.cpp +// +// Abstract: +// This module contains functions which implements the FLOW_ASSOCIATION scenarios. +// +// Naming Convention: +// +// +// +// +// i.e. +// +// +// { +// - Function is likely visible to other modules +// Prv - Function is private to this module. +// } +// +// { +// ScenarioFlowAssociation - Function pertains to all of the Flow Association +// Scenarios +// RPC - Function is and RPC entry point. +// } +// +// { +// Add - Function adds objects +// Remove - Function removes objects +// Invoke - Function implements the scenario based on parameters +// passed from the commandline interface +// (WFPSampler.exe). +// } +// +// { +// FwpmObjects - Function acts on WFP objects. +// } +// +// Private Functions: +// PrvScenarioFlowAssociationAddFwpmObjects(), +// PrvScenarioFlowAssociationDeleteFwpmObjects(), +// ScenarioFlowAssociationAdd(), +// ScenarioFlowAssociationRemove(), +// +// Public Functions: +// RPCInvokeScenarioFlowAssociation(), +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// December 13, 2013 - 1.1 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "Framework_WFPSamplerService.h" /// . + +/** + @private_function="PrvScenarioFlowAssociationDeleteFwpmObjects" + + Purpose: Function that disables the SCENARIO_FLOW_ASSOCIATION scenarios.
+
+ Notes: Scenario removes the filters using specified filtering conditions at the specified + layer. Associated callouts and provider contexts are removed as well.
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 PrvScenarioFlowAssociationDeleteFwpmObjects(_In_ const FWPM_FILTER* pFilter) +{ + ASSERT(pFilter); + + UINT32 status = NO_ERROR; + HANDLE engineHandle = 0; + HANDLE enumHandle = 0; + UINT32 entryCount = 0; + FWPM_FILTER** ppFilters = 0; + FWPM_FILTER_CONDITION* pFilterConditions = 0; + UINT32 numFilterConditions = 0; + FWPM_FILTER_ENUM_TEMPLATE filterEnumTemplate = {0}; + FWPM_PROVIDER_CONTEXT_ENUM_TEMPLATE providerContextEnumTemplate = {0}; + GUID calloutKey = {0}; + + HlprFwpmFilterConditionPrune(pFilter->filterCondition, + pFilter->numFilterConditions, + &pFilterConditions, + &numFilterConditions); + + calloutKey = WFPSAMPLER_CALLOUT_FLOW_ASSOCIATION; + calloutKey.Data4[7] = HlprFwpmLayerGetIDByKey(&(pFilter->layerKey)); /// Uniquely identifies the callout used + + providerContextEnumTemplate.providerContextType = FWPM_GENERAL_CONTEXT; + + filterEnumTemplate.providerKey = (GUID*)&WFPSAMPLER_PROVIDER; + filterEnumTemplate.layerKey = pFilter->layerKey; + filterEnumTemplate.enumType = FWP_FILTER_ENUM_FULLY_CONTAINED; + filterEnumTemplate.flags = FWP_FILTER_ENUM_FLAG_INCLUDE_BOOTTIME | + FWP_FILTER_ENUM_FLAG_INCLUDE_DISABLED; + filterEnumTemplate.numFilterConditions = pFilterConditions ? numFilterConditions : pFilter->numFilterConditions; + filterEnumTemplate.filterCondition = pFilterConditions ? pFilterConditions : pFilter->filterCondition; + filterEnumTemplate.providerContextTemplate = &providerContextEnumTemplate; + filterEnumTemplate.actionMask = FWP_ACTION_FLAG_CALLOUT; + filterEnumTemplate.calloutKey = &calloutKey; + + status = HlprFwpmEngineOpen(&engineHandle); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmFilterCreateEnumHandle(engineHandle, + &filterEnumTemplate, + &enumHandle); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmFilterEnum(engineHandle, + enumHandle, + 0xFFFFFFFF, + &ppFilters, + &entryCount); + HLPR_BAIL_ON_FAILURE(status); + + if(ppFilters) + { + for(UINT32 filterIndex = 0; + filterIndex < entryCount; + filterIndex++) + { + HlprFwpmFilterDeleteByKey(engineHandle, + &(ppFilters[filterIndex]->filterKey)); + + HlprFwpmCalloutDeleteByKey(engineHandle, + &(ppFilters[filterIndex]->action.calloutKey)); + + if(ppFilters[filterIndex]->flags & FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT) + HlprFwpmProviderContextDeleteByKey(engineHandle, + &(ppFilters[filterIndex]->providerContextKey)); + } + + FwpmFreeMemory((VOID**)&ppFilters); + } + + HLPR_BAIL_LABEL: + + if(engineHandle) + { + if(enumHandle) + HlprFwpmFilterDestroyEnumHandle(engineHandle, + &enumHandle); + + HlprFwpmEngineClose(&engineHandle); + } + + HLPR_DELETE_ARRAY(pFilterConditions); + + return status; +} + +/** + @private_function="PrvScenarioFlowAssociationAddFwpmObjects" + + Purpose: Function that enables the SCENARIO_FLOW_ASSOCIATION scenarios.
+
+ Notes: Scenario adds a filter using specified filtering conditions to the specified layer. + This filter is associated with WFPSampler's default sublayer and provider. The + appropriate callout and provider context is then added and associated with the + filter.
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 PrvScenarioFlowAssociationAddFwpmObjects(_In_ const FWPM_FILTER* pFilter, + _In_ const PC_FLOW_ASSOCIATION_DATA* pPCFlowAssociationData) +{ + ASSERT(pFilter); + ASSERT(pPCFlowAssociationData); + + UINT32 status = NO_ERROR; + + if(pFilter->layerKey == FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4 || + pFilter->layerKey == FWPM_LAYER_ALE_FLOW_ESTABLISHED_V6) + { + HANDLE engineHandle = 0; + FWP_BYTE_BLOB byteBlob = {0}; + FWPM_PROVIDER_CONTEXT providerContext = {0}; + FWPM_CALLOUT callout = {0}; + FWPM_FILTER filter = {0}; + + RtlCopyMemory(&filter, + pFilter, + sizeof(FWPM_FILTER)); + + status = HlprGUIDPopulate(&(providerContext.providerContextKey)); + HLPR_BAIL_ON_FAILURE(status); + + providerContext.displayData.name = L"WFPSampler's Flow Association Provider Context"; + providerContext.providerKey = (GUID*)&WFPSAMPLER_PROVIDER; + providerContext.type = FWPM_GENERAL_CONTEXT; + providerContext.dataBuffer = &byteBlob; + providerContext.dataBuffer->size = sizeof(PC_FLOW_ASSOCIATION_DATA); + providerContext.dataBuffer->data = (UINT8*)pPCFlowAssociationData; + + callout.calloutKey = WFPSAMPLER_CALLOUT_FLOW_ASSOCIATION; + callout.calloutKey.Data4[7] = HlprFwpmLayerGetIDByKey(&(filter.layerKey)); /// Uniquely identifies the callout used + callout.displayData.name = L"WFPSampler's Flow Association Callout"; + callout.displayData.description = L"Associates context with the layers / callouts provided"; + callout.flags = FWPM_CALLOUT_FLAG_USES_PROVIDER_CONTEXT; + callout.providerKey = (GUID*)&WFPSAMPLER_PROVIDER; + callout.applicableLayer = filter.layerKey; + + status = HlprGUIDPopulate(&(filter.filterKey)); + HLPR_BAIL_ON_FAILURE(status); + + filter.flags |= FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT; + filter.providerKey = (GUID*)&WFPSAMPLER_PROVIDER; + filter.weight.type = FWP_UINT8; + filter.weight.uint8 = 0xF; + filter.action.type = FWP_ACTION_CALLOUT_UNKNOWN; + filter.action.calloutKey = callout.calloutKey; + filter.providerContextKey = providerContext.providerContextKey; + + if(filter.flags & FWPM_FILTER_FLAG_BOOTTIME || + filter.flags & FWPM_FILTER_FLAG_PERSISTENT) + { + providerContext.flags |= FWPM_PROVIDER_CONTEXT_FLAG_PERSISTENT; + + callout.flags |= FWPM_CALLOUT_FLAG_PERSISTENT; + } + + status = HlprFwpmEngineOpen(&engineHandle); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmTransactionBegin(engineHandle); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmProviderContextAdd(engineHandle, + &providerContext); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmCalloutAdd(engineHandle, + &callout); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmFilterAdd(engineHandle, + &filter); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmTransactionCommit(engineHandle); + HLPR_BAIL_ON_FAILURE(status); + + HLPR_BAIL_LABEL: + + if(engineHandle) + { + if(status != NO_ERROR) + HlprFwpmTransactionAbort(engineHandle); + + HlprFwpmEngineClose(&engineHandle); + } + } + else + status = (UINT32)FWP_E_INCOMPATIBLE_LAYER; + + return status; +} + +/** + @scenario_function="ScenarioFlowAssociationRemove" + + Purpose: Function that removes corresponding objects for a previously added + SCENARIO_FLOW_ASSOCIATION.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 ScenarioFlowAssociationRemove(_In_ const FWPM_FILTER* pFilter) +{ + ASSERT(pFilter); + + return PrvScenarioFlowAssociationDeleteFwpmObjects(pFilter); +} + +/** + @scenario_function="ScenarioFlowAssociationAdd" + + Purpose: Scenario which will associate context with the layers and callouts provided.
+
+ Notes: Adds a filter which references one of the + WFPSAMPLER_CALLOUT_FLOW_ASSOCIATION callouts for the provided layer.
+
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 ScenarioFlowAssociationAdd(_In_ const FWPM_FILTER* pFilter, + _In_ const PC_FLOW_ASSOCIATION_DATA* pPCFlowAssociationData) +{ + + ASSERT(pFilter); + ASSERT(pPCFlowAssociationData); + + return PrvScenarioFlowAssociationAddFwpmObjects(pFilter, + pPCFlowAssociationData); +} + +/** + @rpc_function="RPCInvokeScenarioFlowAssociation" + + Purpose: RPC exposed function used to dipatch the scenario routines for + SCENARIO_FLOW_ASSOCIATION.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +error_status_t RPCInvokeScenarioFlowAssociation(/* [in] */ handle_t rpcBindingHandle, + /* [in] */ WFPSAMPLER_SCENARIO scenario, + /* [in] */ FWPM_CHANGE_TYPE changeType, + /* [ref][in] */ __RPC__in const FWPM_FILTER0* pFilter, + /* [unique][in] */ __RPC__in_opt const PC_FLOW_ASSOCIATION_DATA* pPCFlowAssociationData) +{ + UNREFERENCED_PARAMETER(rpcBindingHandle); + UNREFERENCED_PARAMETER(scenario); + + ASSERT(pFilter); + ASSERT(scenario == SCENARIO_FLOW_ASSOCIATION); + ASSERT(changeType < FWPM_CHANGE_TYPE_MAX); + + UINT32 status = NO_ERROR; + + if(changeType == FWPM_CHANGE_ADD) + { + if(pPCFlowAssociationData) + status = ScenarioFlowAssociationAdd(pFilter, + pPCFlowAssociationData); + else + status = ERROR_INVALID_PARAMETER; + } + else + status = ScenarioFlowAssociationRemove(pFilter); + + return status; +} diff --git a/network/trans/WFPSampler/svc/Scenarios_PendAuthorization.cpp b/network/trans/WFPSampler/svc/Scenarios_PendAuthorization.cpp new file mode 100644 index 000000000..663039595 --- /dev/null +++ b/network/trans/WFPSampler/svc/Scenarios_PendAuthorization.cpp @@ -0,0 +1,362 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// Scenarios_PendAuthorization.cpp +// +// Abstract: +// This module contains functions which implements the PEND_AUTHORIZATION scenarios. +// +// Naming Convention: +// +// +// +// +// i.e. +// +// +// { +// - Function is likely visible to other modules. +// Prv - Function is private to this module. +// } +// +// { +// ScenarioPendAuthorization - Function pertains to all of the Pend Authorization +// Scenarios. +// RPC - Function is and RPC entry point. +// } +// +// { +// Add - Function adds objects. +// Remove - Function removes objects. +// Invoke - Function implements the scenario based on parameters +// passed from the commandline interface +// (WFPSampler.exe). +// } +// +// { +// FwpmObjects - Function acts on WFP objects. +// ScenarioPendAuthorization - Function pertains to all of the Pend Authorization +// Scenarios. +// } +// +// Private Functions: +// PrvScenarioPendAuthorizationAddFwpmObjects(), +// PrvScenarioPendAuthorizationDeleteFwpmObjects(), +// ScenarioPendAuthorizationAdd(), +// ScenarioPendAuthorizationRemove(), +// +// Public Functions: +// RPCInvokeScenarioPendAuthorization(), +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Prune filters for enumeration and limit scenario to +// only the supported layers +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "Framework_WFPSamplerService.h" /// . + +/** + @private_function="PrvScenarioPendAuthorizationDeleteFwpmObjects" + + Purpose: Function that disables the SCENARIO_PEND_AUTHORIZATION scenarios.
+
+ Notes: Scenario removes the filters using specified filtering conditions at the specified + layer. The associated callout and provider contexts are removed as well.
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 PrvScenarioPendAuthorizationDeleteFwpmObjects(_In_ const FWPM_FILTER* pFilter) +{ + ASSERT(pFilter); + + UINT32 status = NO_ERROR; + HANDLE engineHandle = 0; + HANDLE enumHandle = 0; + UINT32 entryCount = 0; + FWPM_FILTER** ppFilters = 0; + FWPM_FILTER_CONDITION* pFilterConditions = 0; + UINT32 numFilterConditions = 0; + FWPM_FILTER_ENUM_TEMPLATE filterEnumTemplate = {0}; + FWPM_PROVIDER_CONTEXT_ENUM_TEMPLATE providerContextEnumTemplate = {0}; + GUID calloutKey = {0}; + + HlprFwpmFilterConditionPrune(pFilter->filterCondition, + pFilter->numFilterConditions, + &pFilterConditions, + &numFilterConditions); + + calloutKey = WFPSAMPLER_CALLOUT_PEND_AUTHORIZATION; + calloutKey.Data4[7] = HlprFwpmLayerGetIDByKey(&(pFilter->layerKey)); /// Uniquely identifies the callout used + + providerContextEnumTemplate.providerContextType = FWPM_GENERAL_CONTEXT; + + filterEnumTemplate.providerKey = (GUID*)&WFPSAMPLER_PROVIDER; + filterEnumTemplate.layerKey = pFilter->layerKey; + filterEnumTemplate.enumType = FWP_FILTER_ENUM_FULLY_CONTAINED; + filterEnumTemplate.flags = FWP_FILTER_ENUM_FLAG_INCLUDE_BOOTTIME | + FWP_FILTER_ENUM_FLAG_INCLUDE_DISABLED; + filterEnumTemplate.numFilterConditions = pFilterConditions ? numFilterConditions : pFilter->numFilterConditions; + filterEnumTemplate.filterCondition = pFilterConditions ? pFilterConditions : pFilter->filterCondition; + filterEnumTemplate.providerContextTemplate = &providerContextEnumTemplate; + filterEnumTemplate.actionMask = FWP_ACTION_FLAG_CALLOUT; + filterEnumTemplate.calloutKey = &calloutKey; + + status = HlprFwpmEngineOpen(&engineHandle); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmFilterCreateEnumHandle(engineHandle, + &filterEnumTemplate, + &enumHandle); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmFilterEnum(engineHandle, + enumHandle, + 0xFFFFFFFF, + &ppFilters, + &entryCount); + HLPR_BAIL_ON_FAILURE(status); + + if(ppFilters) + { + for(UINT32 filterIndex = 0; + filterIndex < entryCount; + filterIndex++) + { + HlprFwpmFilterDeleteByKey(engineHandle, + &(ppFilters[filterIndex]->filterKey)); + + HlprFwpmCalloutDeleteByKey(engineHandle, + &(ppFilters[filterIndex]->action.calloutKey)); + + if(ppFilters[filterIndex]->flags & FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT) + HlprFwpmProviderContextDeleteByKey(engineHandle, + &(ppFilters[filterIndex]->providerContextKey)); + } + + FwpmFreeMemory((VOID**)&ppFilters); + } + + HLPR_BAIL_LABEL: + + if(engineHandle) + { + if(enumHandle) + HlprFwpmFilterDestroyEnumHandle(engineHandle, + &enumHandle); + + HlprFwpmEngineClose(&engineHandle); + } + + HLPR_DELETE_ARRAY(pFilterConditions); + + return status; +} + +/** + @private_function="PrvScenarioPendAuthorizationAddFwpmObjects" + + Purpose: Function that enables the SCENARIO_PEND_AUTHORIZATION scenarios.
+
+ Notes: Scenario adds a filter using specified filtering conditions to the specified layer. + This filter is associated with WFPSampler's default sublayer and provider. The + appropriate callout and provider context is added and associated with the filter.
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 PrvScenarioPendAuthorizationAddFwpmObjects(_In_ const FWPM_FILTER* pFilter, + _In_ const PC_PEND_AUTHORIZATION_DATA* pPCPendAuthorizationData) +{ + ASSERT(pFilter); + ASSERT(pPCPendAuthorizationData); + + UINT32 status = NO_ERROR; + + if(pFilter->layerKey == FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V4 || + pFilter->layerKey == FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V6 || + pFilter->layerKey == FWPM_LAYER_ALE_AUTH_LISTEN_V4 || + pFilter->layerKey == FWPM_LAYER_ALE_AUTH_LISTEN_V6 || + pFilter->layerKey == FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 || + pFilter->layerKey == FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 || + pFilter->layerKey == FWPM_LAYER_ALE_AUTH_CONNECT_V4 || + pFilter->layerKey == FWPM_LAYER_ALE_AUTH_CONNECT_V6) + { + HANDLE engineHandle = 0; + FWP_BYTE_BLOB byteBlob = {0}; + FWPM_PROVIDER_CONTEXT providerContext = {0}; + FWPM_CALLOUT callout = {0}; + FWPM_FILTER filter = {0}; + + RtlCopyMemory(&filter, + pFilter, + sizeof(FWPM_FILTER)); + + status = HlprGUIDPopulate(&(providerContext.providerContextKey)); + HLPR_BAIL_ON_FAILURE(status); + + providerContext.displayData.name = L"WFPSampler's Pend Authorization ProviderContext"; + providerContext.displayData.description = L"Instructs the driver what final action to take on the pended authorization"; + providerContext.providerKey = (GUID*)&WFPSAMPLER_PROVIDER; + providerContext.type = FWPM_GENERAL_CONTEXT; + providerContext.dataBuffer = &byteBlob; + providerContext.dataBuffer->size = sizeof(PC_PEND_AUTHORIZATION_DATA); + providerContext.dataBuffer->data = (UINT8*)pPCPendAuthorizationData; + + callout.calloutKey = WFPSAMPLER_CALLOUT_PEND_AUTHORIZATION; + callout.calloutKey.Data4[7] = HlprFwpmLayerGetIDByKey(&(filter.layerKey)); /// Uniquely identifies the callout used + callout.displayData.name = L"WFPSampler's Pend Authorization Callout"; + callout.displayData.description = L"Pends the authorization request and completes out of band"; + callout.flags = FWPM_CALLOUT_FLAG_USES_PROVIDER_CONTEXT; + callout.providerKey = (GUID*)&WFPSAMPLER_PROVIDER; + callout.applicableLayer = filter.layerKey; + + status = HlprGUIDPopulate(&(filter.filterKey)); + HLPR_BAIL_ON_FAILURE(status); + + filter.flags |= FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT; + filter.providerKey = (GUID*)&WFPSAMPLER_PROVIDER; + filter.weight.type = FWP_UINT8; + filter.weight.uint8 = 0xF; + filter.action.type = FWP_ACTION_CALLOUT_UNKNOWN; + filter.action.calloutKey = callout.calloutKey; + filter.providerContextKey = providerContext.providerContextKey; + + if(filter.flags & FWPM_FILTER_FLAG_BOOTTIME || + filter.flags & FWPM_FILTER_FLAG_PERSISTENT) + { + providerContext.flags |= FWPM_PROVIDER_CONTEXT_FLAG_PERSISTENT; + + callout.flags |= FWPM_CALLOUT_FLAG_PERSISTENT; + } + + status = HlprFwpmEngineOpen(&engineHandle); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmTransactionBegin(engineHandle); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmProviderContextAdd(engineHandle, + &providerContext); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmCalloutAdd(engineHandle, + &callout); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmFilterAdd(engineHandle, + &filter); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmTransactionCommit(engineHandle); + HLPR_BAIL_ON_FAILURE(status); + + HLPR_BAIL_LABEL: + + if(engineHandle) + { + if(status != NO_ERROR) + HlprFwpmTransactionAbort(engineHandle); + + HlprFwpmEngineClose(&engineHandle); + } + } + else + status = (UINT32)FWP_E_INCOMPATIBLE_LAYER; + + return status; +} + +/** + @scenario_function="ScenarioPendAuthorizationRemove" + + Purpose: Function that removes corresponding objects for a previously added + SCENARIO_PEND_AUTHORIZATION.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 ScenarioPendAuthorizationRemove(_In_ const FWPM_FILTER* pFilter) +{ + ASSERT(pFilter); + + return PrvScenarioPendAuthorizationDeleteFwpmObjects(pFilter); +} + +/** + @scenario_function="ScenarioPendAuthorizationAdd" + + Purpose: Scenario which will delay and either block or permit an authorization attempt.
+
+ Notes: Adds a filter which references one of the + WFPSAMPLER_CALLOUT_PEND_AUTHORIZATION callouts for the provided layer.
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 ScenarioPendAuthorizationAdd(_In_ const FWPM_FILTER* pFilter, + _In_ const PC_PEND_AUTHORIZATION_DATA* pPCPendAuthorizationData) +{ + ASSERT(pFilter); + ASSERT(pPCPendAuthorizationData); + + return PrvScenarioPendAuthorizationAddFwpmObjects(pFilter, + pPCPendAuthorizationData); +} + +/** + @rpc_function="RPCInvokeScenarioPendAuthorization" + + Purpose: RPC exposed function used to dipatch the scenario routines for + SCENARIO_PEND_AUTHORIZATION.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +/* [fault_status][comm_status] */ +error_status_t RPCInvokeScenarioPendAuthorization(/* [in] */ handle_t rpcBindingHandle, + /* [in] */ WFPSAMPLER_SCENARIO scenario, + /* [in] */ FWPM_CHANGE_TYPE changeType, + /* [ref][in] */ __RPC__in const FWPM_FILTER0* pFilter, + /* [unique][in] */ __RPC__in_opt const PC_PEND_AUTHORIZATION_DATA* pPCPendAuthorizationData) +{ + UNREFERENCED_PARAMETER(rpcBindingHandle); + UNREFERENCED_PARAMETER(scenario); + + ASSERT(pFilter); + ASSERT(scenario == SCENARIO_PEND_AUTHORIZATION); + ASSERT(changeType < FWPM_CHANGE_TYPE_MAX); + + UINT32 status = NO_ERROR; + + if(changeType == FWPM_CHANGE_ADD) + { + if(pPCPendAuthorizationData) + status = ScenarioPendAuthorizationAdd(pFilter, + pPCPendAuthorizationData); + else + { + status = ERROR_INVALID_PARAMETER; + + HlprLogError(L"RPCInvokeScenarioPendAuthorization() [status: %#x][pPCPendAuthorizationData: %#p]", + status, + pPCPendAuthorizationData); + } + } + else + status = ScenarioPendAuthorizationRemove(pFilter); + + return status; +} diff --git a/network/trans/WFPSampler/svc/Scenarios_PendEndpointClosure.cpp b/network/trans/WFPSampler/svc/Scenarios_PendEndpointClosure.cpp new file mode 100644 index 000000000..f7a1b9cb9 --- /dev/null +++ b/network/trans/WFPSampler/svc/Scenarios_PendEndpointClosure.cpp @@ -0,0 +1,379 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// Scenarios_PendEndpointClosure.cpp +// +// Abstract: +// This module contains functions which implements the PEND_ENDPOINT_CLOSURE scenarios. +// +// Naming Convention: +// +// +// +// +// i.e. +// +// +// { +// - Function is likely visible to other modules. +// Prv - Function is private to this module. +// } +// +// { +// ScenarioPendEndpointClosure - Function pertains to all of the Pend Endpoint Closure +// Scenarios. +// RPC - Function is and RPC entry point. +// } +// +// { +// Add - Function adds objects. +// Remove - Function removes objects. +// Invoke - Function implements the scenario based on parameters +// passed from the commandline interface +// (WFPSampler.exe). +// } +// +// { +// FwpmObjects - Function acts on WFP objects. +// ScenarioPendEndpointClosure - Function pertains to all of the Pend Endpoint Closure +// Scenarios. +// } +// +// Private Functions: +// PrvScenarioPendEndpointClosureAddFwpmObjects(), +// PrvScenarioPendEndpointClosureDeleteFwpmObjects(), +// ScenarioPendEndpointClosureAdd(), +// ScenarioPendEndpointClosureRemove(), +// +// Public Functions: +// RPCInvokeScenarioPendEndpointClosure(), +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// December 13, 2013 - 1.1 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "Framework_WFPSamplerService.h" /// . + +#if(NTDDI_VERSION >= NTDDI_WIN7) + +/** + @private_function="PrvScenarioPendEndpointClosureDeleteFwpmObjects" + + Purpose: Function that disables the SCENARIO_PEND_ENDPOINT_CLOSURE scenarios.
+
+ Notes: Scenario removes the filters using specified filtering conditions at the specified + layer. The associated callout and provider contexts are removed as well.
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 PrvScenarioPendEndpointClosureDeleteFwpmObjects(_In_ const FWPM_FILTER* pFilter) +{ + ASSERT(pFilter); + + UINT32 status = NO_ERROR; + HANDLE engineHandle = 0; + HANDLE enumHandle = 0; + UINT32 entryCount = 0; + FWPM_FILTER** ppFilters = 0; + FWPM_FILTER_CONDITION* pFilterConditions = 0; + UINT32 numFilterConditions = 0; + FWPM_FILTER_ENUM_TEMPLATE filterEnumTemplate = {0}; + FWPM_PROVIDER_CONTEXT_ENUM_TEMPLATE providerContextEnumTemplate = {0}; + GUID calloutKey = {0}; + + HlprFwpmFilterConditionPrune(pFilter->filterCondition, + pFilter->numFilterConditions, + &pFilterConditions, + &numFilterConditions); + + calloutKey = WFPSAMPLER_CALLOUT_PEND_ENDPOINT_CLOSURE; + calloutKey.Data4[7] = HlprFwpmLayerGetIDByKey(&(pFilter->layerKey)); /// Uniquely identifies the callout used + + providerContextEnumTemplate.providerContextType = FWPM_GENERAL_CONTEXT; + + filterEnumTemplate.providerKey = (GUID*)&WFPSAMPLER_PROVIDER; + filterEnumTemplate.layerKey = pFilter->layerKey; + filterEnumTemplate.enumType = FWP_FILTER_ENUM_FULLY_CONTAINED; + filterEnumTemplate.flags = FWP_FILTER_ENUM_FLAG_INCLUDE_BOOTTIME | + FWP_FILTER_ENUM_FLAG_INCLUDE_DISABLED; + filterEnumTemplate.numFilterConditions = pFilterConditions ? numFilterConditions : pFilter->numFilterConditions; + filterEnumTemplate.filterCondition = pFilterConditions ? pFilterConditions : pFilter->filterCondition; + filterEnumTemplate.providerContextTemplate = &providerContextEnumTemplate; + filterEnumTemplate.actionMask = FWP_ACTION_FLAG_CALLOUT; + filterEnumTemplate.calloutKey = &calloutKey; + + status = HlprFwpmEngineOpen(&engineHandle); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmFilterCreateEnumHandle(engineHandle, + &filterEnumTemplate, + &enumHandle); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmFilterEnum(engineHandle, + enumHandle, + 0xFFFFFFFF, + &ppFilters, + &entryCount); + HLPR_BAIL_ON_FAILURE(status); + + if(ppFilters) + { + for(UINT32 filterIndex = 0; + filterIndex < entryCount; + filterIndex++) + { + HlprFwpmFilterDeleteByKey(engineHandle, + &(ppFilters[filterIndex]->filterKey)); + + HlprFwpmCalloutDeleteByKey(engineHandle, + &(ppFilters[filterIndex]->action.calloutKey)); + + if(ppFilters[filterIndex]->flags & FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT) + HlprFwpmProviderContextDeleteByKey(engineHandle, + &(ppFilters[filterIndex]->providerContextKey)); + } + + FwpmFreeMemory((VOID**)&ppFilters); + } + + HLPR_BAIL_LABEL: + + if(engineHandle) + { + if(enumHandle) + HlprFwpmFilterDestroyEnumHandle(engineHandle, + &enumHandle); + + HlprFwpmEngineClose(&engineHandle); + } + + HLPR_DELETE_ARRAY(pFilterConditions); + + return status; +} + +/** + @private_function="PrvScenarioPendEndpointClosureAddFwpmObjects" + + Purpose: Function that enables the SCENARIO_PEND_ENDPOINT_CLOSURE scenarios.
+
+ Notes: Scenario adds a filter using specified filtering conditions to the specified layer. + This filter is associated with WFPSampler's default sublayer and provider. The + appropriate callout and provider context is added and associated with the filter.
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 PrvScenarioPendEndpointClosureAddFwpmObjects(_In_ const FWPM_FILTER* pFilter, + _In_ const PC_PEND_ENDPOINT_CLOSURE_DATA* pPCPendEndpointClosureData) +{ + ASSERT(pFilter); + ASSERT(pPCPendEndpointClosureData); + + UINT32 status = NO_ERROR; + HANDLE engineHandle = 0; + FWP_BYTE_BLOB byteBlob = {0}; + FWPM_PROVIDER_CONTEXT providerContext = {0}; + FWPM_CALLOUT callout = {0}; + FWPM_FILTER filter = {0}; + + RtlCopyMemory(&filter, + pFilter, + sizeof(FWPM_FILTER)); + + status = HlprGUIDPopulate(&(providerContext.providerContextKey)); + HLPR_BAIL_ON_FAILURE(status); + + providerContext.displayData.name = L"WFPSampler's Pend Endpoint Closure ProviderContext"; + providerContext.providerKey = (GUID*)&WFPSAMPLER_PROVIDER; + providerContext.type = FWPM_GENERAL_CONTEXT; + providerContext.dataBuffer = &byteBlob; + providerContext.dataBuffer->size = sizeof(PC_PEND_ENDPOINT_CLOSURE_DATA); + providerContext.dataBuffer->data = (UINT8*)pPCPendEndpointClosureData; + + callout.calloutKey = WFPSAMPLER_CALLOUT_PEND_ENDPOINT_CLOSURE; + callout.calloutKey.Data4[7] = HlprFwpmLayerGetIDByKey(&(filter.layerKey)); /// Uniquely identifies the callout used + callout.displayData.name = L"WFPSampler's Pend Endpoint Closure Callout"; + callout.displayData.description = L"Pends the endpoint's closing request and completes out of band"; + callout.flags = FWPM_CALLOUT_FLAG_USES_PROVIDER_CONTEXT; + callout.providerKey = (GUID*)&WFPSAMPLER_PROVIDER; + callout.applicableLayer = filter.layerKey; + + status = HlprGUIDPopulate(&(filter.filterKey)); + HLPR_BAIL_ON_FAILURE(status); + + filter.flags |= FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT; + filter.providerKey = (GUID*)&WFPSAMPLER_PROVIDER; + filter.weight.type = FWP_UINT8; + filter.weight.uint8 = 0xF; + filter.action.type = FWP_ACTION_CALLOUT_UNKNOWN; + filter.action.calloutKey = callout.calloutKey; + filter.providerContextKey = providerContext.providerContextKey; + + if(filter.flags & FWPM_FILTER_FLAG_BOOTTIME || + filter.flags & FWPM_FILTER_FLAG_PERSISTENT) + { + providerContext.flags |= FWPM_PROVIDER_CONTEXT_FLAG_PERSISTENT; + + callout.flags |= FWPM_CALLOUT_FLAG_PERSISTENT; + } + + status = HlprFwpmEngineOpen(&engineHandle); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmTransactionBegin(engineHandle); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmProviderContextAdd(engineHandle, + &providerContext); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmCalloutAdd(engineHandle, + &callout); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmFilterAdd(engineHandle, + &filter); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmTransactionCommit(engineHandle); + HLPR_BAIL_ON_FAILURE(status); + + HLPR_BAIL_LABEL: + + if(engineHandle) + { + if(status != NO_ERROR) + HlprFwpmTransactionAbort(engineHandle); + + HlprFwpmEngineClose(&engineHandle); + } + + return status; +} + +/** + @scenario_function="ScenarioPendEndpointClosureRemove" + + Purpose: Function that removes corresponding objects for a previously added + SCENARIO_PEND_ENDPOINT_CLOSURE.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 ScenarioPendEndpointClosureRemove(_In_ const FWPM_FILTER* pFilter) +{ + ASSERT(pFilter); + + return PrvScenarioPendEndpointClosureDeleteFwpmObjects(pFilter); +} + +/** + @scenario_function="ScenarioPendEndpointClosureAdd" + + Purpose: Scenario which will delay the endpoint from from closing.
+
+ Notes: Adds a filter which references one of the + WFPSAMPLER_CALLOUT_PEND_ENDPOINT_CLOSURE callouts for the provided layer.
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 ScenarioPendEndpointClosureAdd(_In_ const FWPM_FILTER* pFilter, + _In_ const PC_PEND_ENDPOINT_CLOSURE_DATA* pPCPendEndpointClosureData) +{ + ASSERT(pFilter); + ASSERT(pPCPendEndpointClosureData); + + return PrvScenarioPendEndpointClosureAddFwpmObjects(pFilter, + pPCPendEndpointClosureData); +} + +/** + @rpc_function="RPCInvokeScenarioPendEndpointClosure" + + Purpose: RPC exposed function used to dipatch the scenario routines for + SCENARIO_PEND_ENDPOINT_CLOSURE.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +/* [fault_status][comm_status] */ +error_status_t RPCInvokeScenarioPendEndpointClosure(/* [in] */ handle_t rpcBindingHandle, + /* [in] */ WFPSAMPLER_SCENARIO scenario, + /* [in] */ FWPM_CHANGE_TYPE changeType, + /* [ref][in] */ __RPC__in const FWPM_FILTER0* pFilter, + /* [unique][in] */ __RPC__in_opt const PC_PEND_ENDPOINT_CLOSURE_DATA* pPCPendEndpointClosureData) +{ + UNREFERENCED_PARAMETER(rpcBindingHandle); + UNREFERENCED_PARAMETER(scenario); + + ASSERT(pFilter); + ASSERT(scenario == SCENARIO_PEND_AUTHORIZATION); + ASSERT(changeType < FWPM_CHANGE_TYPE_MAX); + + UINT32 status = NO_ERROR; + + if(changeType == FWPM_CHANGE_ADD) + { + if(pPCPendEndpointClosureData) + status = ScenarioPendEndpointClosureAdd(pFilter, + pPCPendEndpointClosureData); + else + { + status = ERROR_INVALID_PARAMETER; + + HlprLogError(L"RPCInvokeScenarioPendEndpointClosure() [status: %#x][pPCPendEndpointClosureData: %#p]", + status, + pPCPendEndpointClosureData); + } + } + else + status = ScenarioPendEndpointClosureRemove(pFilter); + + return status; +} + +#else + +/** + @rpc_function="RPCInvokeScenarioBasicAction" + + Purpose: RPC exposed function used to dipatch the scenario routines for + SCENARIO_APP_CONTAINER.
+
+ Notes: This particular function is only a stub for RPC on downlevel SKUs (Windows 7 and + below).
+
+ MSDN_Ref:
+*/ + /* [fault_status][comm_status] */ + error_status_t RPCInvokeScenarioPendEndpointClosure(/* [in] */ handle_t rpcBindingHandle, + /* [in] */ WFPSAMPLER_SCENARIO scenario, + /* [in] */ FWPM_CHANGE_TYPE changeType, + /* [ref][in] */ __RPC__in const FWPM_FILTER0* pFilter, + /* [unique][in] */ __RPC__in_opt const PC_PEND_ENDPOINT_CLOSURE_DATA* pPCPendEndpointClosureData) +{ + UNREFERENCED_PARAMETER(rpcBindingHandle); + UNREFERENCED_PARAMETER(scenario); + UNREFERENCED_PARAMETER(changeType); + UNREFERENCED_PARAMETER(pFilter); + UNREFERENCED_PARAMETER(pPCPendEndpointClosureData); + + return ERROR_NOT_SUPPORTED; +} + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) diff --git a/network/trans/WFPSampler/svc/Scenarios_Proxy.cpp b/network/trans/WFPSampler/svc/Scenarios_Proxy.cpp new file mode 100644 index 000000000..d9be73235 --- /dev/null +++ b/network/trans/WFPSampler/svc/Scenarios_Proxy.cpp @@ -0,0 +1,702 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// Scenarios_Proxy.cpp +// +// Abstract: +// This module contains functions which implements the PROXY scenarios. +// +// Naming Convention: +// +// +// +// +// i.e. +// +// +// { +// - Function is likely visible to other modules. +// Prv - Function is private to this module. +// } +// +// { +// ScenarioProxy - Function pertains to all of the Proxy Scenarios. +// RPC - Function is and RPC entry point. +// } +// +// { +// Add - Function adds objects. +// Remove - Function removes objects. +// Invoke - Function implements the scenario based on parameters passed from the +// commandline interface (WFPSampler.exe). +// } +// +// { +// FwpmObjects - Function acts on WFP objects. +// ScenarioProxy - Function pertains to all of the Proxy Scenarios. +// } +// +// Private Functions: +// PrvScenarioProxyAddFwpmObjects(), +// PrvScenarioProxyDeleteFwpmObjects(), +// ScenarioProxyAdd(), +// ScenarioProxyRemove(), +// +// Public Functions: +// RPCInvokeScenarioProxy(), +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Prune filters for enumeration, fix legacy proxying by +// injection, and limit scenario to only the supported +// layers +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "Framework_WFPSamplerService.h" /// . + +/** + @private_function="PrvScenarioProxyDeleteFwpmObjects" + + Purpose: Function that disables the SCENARIO_PROXY scenarios.
+
+ Notes: Scenario removes the filters using specified filtering conditions at the specified + layer. The associated callout and provider contexts are removed as well.
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 PrvScenarioProxyDeleteFwpmObjects(_In_ const FWPM_FILTER* pFilter, + _In_ const PC_PROXY_DATA* pPCProxyData) +{ + ASSERT(pFilter); + + UINT32 status = NO_ERROR; + HANDLE engineHandle = 0; + HANDLE enumHandle = 0; + UINT32 entryCount = 0; + FWPM_FILTER** ppFilters = 0; + FWPM_FILTER_CONDITION* pFilterConditions = 0; + UINT32 numFilterConditions = 0; + FWPM_FILTER_ENUM_TEMPLATE filterEnumTemplate = {0}; + FWPM_PROVIDER_CONTEXT_ENUM_TEMPLATE providerContextEnumTemplate = {0}; + GUID calloutKey = {0}; + FWP_BYTE_ARRAY16 pIPv6Addresses[2] = {0}; + + HlprFwpmFilterConditionPrune(pFilter->filterCondition, + pFilter->numFilterConditions, + &pFilterConditions, + &numFilterConditions); + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + if(pFilter->layerKey == FWPM_LAYER_ALE_CONNECT_REDIRECT_V4 || + pFilter->layerKey == FWPM_LAYER_ALE_CONNECT_REDIRECT_V6 || + pFilter->layerKey == FWPM_LAYER_ALE_BIND_REDIRECT_V4 || + pFilter->layerKey == FWPM_LAYER_ALE_BIND_REDIRECT_V6) + calloutKey = WFPSAMPLER_CALLOUT_PROXY_BY_ALE_REDIRECT; + else + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + calloutKey = WFPSAMPLER_CALLOUT_PROXY_BY_INJECTION; + calloutKey.Data4[7] = HlprFwpmLayerGetIDByKey(&(pFilter->layerKey)); /// Uniquely identifies the callout used + + providerContextEnumTemplate.providerContextType = FWPM_GENERAL_CONTEXT; + + filterEnumTemplate.providerKey = (GUID*)&WFPSAMPLER_PROVIDER; + filterEnumTemplate.layerKey = pFilter->layerKey; + filterEnumTemplate.enumType = FWP_FILTER_ENUM_FULLY_CONTAINED; + filterEnumTemplate.flags = FWP_FILTER_ENUM_FLAG_INCLUDE_BOOTTIME | + FWP_FILTER_ENUM_FLAG_INCLUDE_DISABLED; + filterEnumTemplate.numFilterConditions = pFilter->numFilterConditions; + filterEnumTemplate.numFilterConditions = pFilterConditions ? numFilterConditions : pFilter->numFilterConditions; + filterEnumTemplate.filterCondition = pFilterConditions ? pFilterConditions : pFilter->filterCondition; + filterEnumTemplate.actionMask = FWP_ACTION_FLAG_CALLOUT; + filterEnumTemplate.calloutKey = &calloutKey; + + status = HlprFwpmEngineOpen(&engineHandle); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmFilterCreateEnumHandle(engineHandle, + &(filterEnumTemplate), + &enumHandle); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmFilterEnum(engineHandle, + enumHandle, + 0xFFFFFFFF, + &ppFilters, + &entryCount); + HLPR_BAIL_ON_FAILURE(status); + + if(ppFilters) + { + for(UINT32 filterIndex = 0; + filterIndex < entryCount; + filterIndex++) + { + HlprFwpmFilterDeleteByKey(engineHandle, + &(ppFilters[filterIndex]->filterKey)); + + HlprFwpmCalloutDeleteByKey(engineHandle, + &(ppFilters[filterIndex]->action.calloutKey)); + + if(pFilter->layerKey != FWPM_LAYER_OUTBOUND_TRANSPORT_V4 && + pFilter->layerKey != FWPM_LAYER_OUTBOUND_TRANSPORT_V6 && + ppFilters[filterIndex]->flags & FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT) + HlprFwpmProviderContextDeleteByKey(engineHandle, + &(ppFilters[filterIndex]->providerContextKey)); + } + + FwpmFreeMemory((VOID**)&ppFilters); + + ppFilters = 0; + } + + HLPR_DELETE_ARRAY(pFilterConditions); + + numFilterConditions = 0; + + if(pFilter->layerKey == FWPM_LAYER_OUTBOUND_TRANSPORT_V4 || + pFilter->layerKey == FWPM_LAYER_OUTBOUND_TRANSPORT_V6) + { + GUID layerKey = FWPM_LAYER_INBOUND_IPPACKET_V4; + + if(HlprFwpmLayerIsIPv6(&(pFilter->layerKey))) + layerKey = FWPM_LAYER_INBOUND_IPPACKET_V6; + + if(enumHandle) + HlprFwpmFilterDestroyEnumHandle(engineHandle, + &enumHandle); + + calloutKey.Data4[7] = HlprFwpmLayerGetIDByKey(&layerKey); /// Uniquely identifies the callout used + + filterEnumTemplate.layerKey = layerKey; + filterEnumTemplate.numFilterConditions = 0; + filterEnumTemplate.filterCondition = 0; + filterEnumTemplate.calloutKey = &calloutKey; + + for(UINT32 index = 0; + index < pFilter->numFilterConditions; + index++) + { + if(HlprFwpmFilterConditionIsValidForLayer(&layerKey, + &(pFilter->filterCondition[index].fieldKey))) + filterEnumTemplate.numFilterConditions++; + } + + if(filterEnumTemplate.numFilterConditions) + { + UINT32 conditionIndex = 0; + + HLPR_NEW_ARRAY(filterEnumTemplate.filterCondition, + FWPM_FILTER_CONDITION, + pFilter->numFilterConditions); + HLPR_BAIL_ON_ALLOC_FAILURE(filterEnumTemplate.filterCondition, + status); + + for(UINT32 index = 0; + index < pFilter->numFilterConditions; + index++) + { + if(conditionIndex < filterEnumTemplate.numFilterConditions && + HlprFwpmFilterConditionIsValidForLayer(&layerKey, + &(pFilter->filterCondition[index].fieldKey))) + { + RtlCopyMemory(&(filterEnumTemplate.filterCondition[conditionIndex]), + &(pFilter->filterCondition[index]), + sizeof(FWPM_FILTER_CONDITION)); + + if(pPCProxyData->flags & PCPDF_PROXY_LOCAL_ADDRESS && + filterEnumTemplate.filterCondition[conditionIndex].fieldKey == FWPM_CONDITION_IP_LOCAL_ADDRESS) + { + if(filterEnumTemplate.filterCondition[conditionIndex].conditionValue.type == FWP_UINT32) + { + UINT32 ipv4Address = 0; + + RtlCopyMemory(&ipv4Address, + pPCProxyData->proxyLocalAddress.pIPv4, + IPV4_ADDRESS_LENGTH); + + ipv4Address = ntohl(ipv4Address); + + filterEnumTemplate.filterCondition[conditionIndex].conditionValue.uint32 = ipv4Address; + } + else + { + filterEnumTemplate.filterCondition[conditionIndex].conditionValue.byteArray16 = &(pIPv6Addresses[0]); + + RtlCopyMemory(filterEnumTemplate.filterCondition[conditionIndex].conditionValue.byteArray16->byteArray16, + pPCProxyData->proxyLocalAddress.pIPv6, + IPV6_ADDRESS_LENGTH); + } + } + else if(pPCProxyData->flags & PCPDF_PROXY_REMOTE_ADDRESS && + filterEnumTemplate.filterCondition[conditionIndex].fieldKey == FWPM_CONDITION_IP_REMOTE_ADDRESS) + { + if(filterEnumTemplate.filterCondition[conditionIndex].conditionValue.type == FWP_UINT32) + { + UINT32 ipv4Address = 0; + + RtlCopyMemory(&ipv4Address, + pPCProxyData->proxyRemoteAddress.pIPv4, + IPV4_ADDRESS_LENGTH); + + ipv4Address = ntohl(ipv4Address); + + filterEnumTemplate.filterCondition[conditionIndex].conditionValue.uint32 = ipv4Address; + } + else + { + filterEnumTemplate.filterCondition[conditionIndex].conditionValue.byteArray16 = &(pIPv6Addresses[0]); + + RtlCopyMemory(filterEnumTemplate.filterCondition[conditionIndex].conditionValue.byteArray16->byteArray16, + pPCProxyData->proxyRemoteAddress.pIPv6, + IPV6_ADDRESS_LENGTH); + } + } + + conditionIndex++; + } + } + } + + HlprFwpmFilterConditionPrune(pFilter->filterCondition, + pFilter->numFilterConditions, + &pFilterConditions, + &numFilterConditions); + if(pFilterConditions) + { + HLPR_DELETE_ARRAY(filterEnumTemplate.filterCondition); + + filterEnumTemplate.numFilterConditions = numFilterConditions; + filterEnumTemplate.filterCondition = pFilterConditions; + } + + status = HlprFwpmFilterCreateEnumHandle(engineHandle, + &filterEnumTemplate, + &enumHandle); + HLPR_BAIL_ON_FAILURE_2(status); + + status = HlprFwpmFilterEnum(engineHandle, + enumHandle, + 0xFFFFFFFF, + &ppFilters, + &entryCount); + HLPR_BAIL_ON_FAILURE_2(status); + + if(ppFilters) + { + for(UINT32 filterIndex = 0; + filterIndex < entryCount; + filterIndex++) + { + HlprFwpmFilterDeleteByKey(engineHandle, + &(ppFilters[filterIndex]->filterKey)); + + HlprFwpmCalloutDeleteByKey(engineHandle, + &(ppFilters[filterIndex]->action.calloutKey)); + + if((ppFilters[filterIndex]->layerKey == FWPM_LAYER_INBOUND_IPPACKET_V4 || + ppFilters[filterIndex]->layerKey == FWPM_LAYER_INBOUND_IPPACKET_V6) && + ppFilters[filterIndex]->flags & FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT) + HlprFwpmProviderContextDeleteByKey(engineHandle, + &(ppFilters[filterIndex]->providerContextKey)); + } + + FwpmFreeMemory((VOID**)&ppFilters); + + ppFilters = 0; + } + + HLPR_BAIL_LABEL_2: + + HLPR_DELETE_ARRAY(filterEnumTemplate.filterCondition); + } + + HLPR_BAIL_LABEL: + + if(engineHandle) + { + if(enumHandle) + HlprFwpmFilterDestroyEnumHandle(engineHandle, + &enumHandle); + + HlprFwpmEngineClose(&engineHandle); + } + + HLPR_DELETE_ARRAY(pFilterConditions); + + return status; +} + +/** + @private_function="PrvScenarioProxyAddFwpmObjects" + + Purpose: Function that enables the SCENARIO_PROXY scenarios.
+
+ Notes: Scenario adds a filter using specified filtering conditions to the specified layer. + This filter is associated with WFPSampler's default sublayer and provider. The + appropriate callout and provider context are added and associated with the filter.
+
+ If proxying by injection, then both the OUTBOUND_TRANSPORT and INBOUND_IPPACKET + objects are added. This has the limitation of not working with IPsec, however + proxing using INBOUND_TRANSPORT has the limitation of not working with TCP and + connected UDP.
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 PrvScenarioProxyAddFwpmObjects(_In_ const FWPM_FILTER* pFilter, + _In_ const PC_PROXY_DATA* pPCProxyData) +{ + ASSERT(pFilter); + ASSERT(pPCProxyData); + + UINT32 status = NO_ERROR; + + if(pFilter->layerKey == FWPM_LAYER_OUTBOUND_TRANSPORT_V4 || + pFilter->layerKey == FWPM_LAYER_OUTBOUND_TRANSPORT_V6 + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + || + pFilter->layerKey == FWPM_LAYER_ALE_CONNECT_REDIRECT_V4 || + pFilter->layerKey == FWPM_LAYER_ALE_CONNECT_REDIRECT_V6 || + pFilter->layerKey == FWPM_LAYER_ALE_BIND_REDIRECT_V4 || + pFilter->layerKey == FWPM_LAYER_ALE_BIND_REDIRECT_V6 + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + ) + { + HANDLE engineHandle = 0; + BOOLEAN hasNetworkFilter = FALSE; + FWP_BYTE_BLOB byteBlob = {0}; + FWPM_PROVIDER_CONTEXT providerContext = {0}; + FWPM_CALLOUT outboundCallout = {0}; + FWPM_CALLOUT inboundCallout = {0}; + FWPM_FILTER outboundFilter = {0}; + FWPM_FILTER inboundFilter = {0}; + FWP_BYTE_ARRAY16 pIPv6Addresses[2] = {0}; + + RtlCopyMemory(&outboundFilter, + pFilter, + sizeof(FWPM_FILTER)); + + status = HlprGUIDPopulate(&(providerContext.providerContextKey)); + HLPR_BAIL_ON_FAILURE(status); + + providerContext.displayData.name = L"WFPSampler's Proxy ProviderContext"; + providerContext.displayData.description = L"Instructs the driver where to proxy the socket or connection"; + providerContext.providerKey = (GUID*)&WFPSAMPLER_PROVIDER; + providerContext.type = FWPM_GENERAL_CONTEXT; + providerContext.dataBuffer = &byteBlob; + providerContext.dataBuffer->size = sizeof(PC_PROXY_DATA); + providerContext.dataBuffer->data = (UINT8*)pPCProxyData; + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + if(pFilter->layerKey == FWPM_LAYER_ALE_CONNECT_REDIRECT_V4 || + pFilter->layerKey == FWPM_LAYER_ALE_CONNECT_REDIRECT_V6 || + pFilter->layerKey == FWPM_LAYER_ALE_BIND_REDIRECT_V4 || + pFilter->layerKey == FWPM_LAYER_ALE_BIND_REDIRECT_V6) + outboundCallout.calloutKey = WFPSAMPLER_CALLOUT_PROXY_BY_ALE_REDIRECT; + else + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + outboundCallout.calloutKey = WFPSAMPLER_CALLOUT_PROXY_BY_INJECTION; + outboundCallout.calloutKey.Data4[7] = HlprFwpmLayerGetIDByKey(&(outboundFilter.layerKey)); /// Uniquely identifies the callout used + outboundCallout.displayData.name = L"WFPSampler's Proxy Callout"; + outboundCallout.displayData.description = L"Proxies the socket or connection to the designated destination"; + outboundCallout.flags = FWPM_CALLOUT_FLAG_USES_PROVIDER_CONTEXT; + outboundCallout.providerKey = (GUID*)&WFPSAMPLER_PROVIDER; + outboundCallout.applicableLayer = outboundFilter.layerKey; + + status = HlprGUIDPopulate(&(outboundFilter.filterKey)); + HLPR_BAIL_ON_FAILURE(status); + + outboundFilter.flags |= FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT; + outboundFilter.providerKey = (GUID*)&WFPSAMPLER_PROVIDER; + outboundFilter.weight.type = FWP_UINT8; + outboundFilter.weight.uint8 = 0xF; + outboundFilter.action.type = FWP_ACTION_CALLOUT_UNKNOWN; + outboundFilter.action.calloutKey = outboundCallout.calloutKey; + outboundFilter.providerContextKey = providerContext.providerContextKey; + + if(outboundFilter.flags & FWPM_FILTER_FLAG_BOOTTIME || + outboundFilter.flags & FWPM_FILTER_FLAG_PERSISTENT) + { + providerContext.flags |= FWPM_PROVIDER_CONTEXT_FLAG_PERSISTENT; + + outboundCallout.flags |= FWPM_CALLOUT_FLAG_PERSISTENT; + } + + if(pFilter->layerKey == FWPM_LAYER_OUTBOUND_TRANSPORT_V4 || + pFilter->layerKey == FWPM_LAYER_OUTBOUND_TRANSPORT_V6) + { + GUID layerKey = FWPM_LAYER_INBOUND_IPPACKET_V4; + + if(HlprFwpmLayerIsIPv6(&(pFilter->layerKey))) + layerKey = FWPM_LAYER_INBOUND_IPPACKET_V6; + + RtlCopyMemory(&inboundCallout, + &outboundCallout, + sizeof(FWPM_CALLOUT)); + + inboundCallout.calloutKey.Data4[7] = HlprFwpmLayerGetIDByKey(&layerKey); /// Uniquely identifies the callout used + inboundCallout.applicableLayer = layerKey; + + RtlCopyMemory(&inboundFilter, + &outboundFilter, + sizeof(FWPM_FILTER)); + + inboundFilter.layerKey = layerKey; + inboundFilter.action.calloutKey = inboundCallout.calloutKey; + inboundFilter.numFilterConditions = 0; + inboundFilter.filterCondition = 0; + + hasNetworkFilter = TRUE; + + status = HlprGUIDPopulate(&(inboundFilter.filterKey)); + HLPR_BAIL_ON_FAILURE(status); + + for(UINT32 index = 0; + index < outboundFilter.numFilterConditions; + index++) + { + if(HlprFwpmFilterConditionIsValidForLayer(&layerKey, + &(outboundFilter.filterCondition[index].fieldKey))) + inboundFilter.numFilterConditions++; + } + + if(inboundFilter.numFilterConditions) + { + UINT32 conditionIndex = 0; + + HLPR_NEW_ARRAY(inboundFilter.filterCondition, + FWPM_FILTER_CONDITION, + inboundFilter.numFilterConditions); + HLPR_BAIL_ON_ALLOC_FAILURE(inboundFilter.filterCondition, + status); + + for(UINT32 index = 0; + index < outboundFilter.numFilterConditions; + index++) + { + if(conditionIndex < inboundFilter.numFilterConditions && + HlprFwpmFilterConditionIsValidForLayer(&layerKey, + &(outboundFilter.filterCondition[index].fieldKey))) + { + RtlCopyMemory(&(inboundFilter.filterCondition[conditionIndex]), + &(outboundFilter.filterCondition[index]), + sizeof(FWPM_FILTER_CONDITION)); + + if(pPCProxyData->flags & PCPDF_PROXY_LOCAL_ADDRESS && + inboundFilter.filterCondition[conditionIndex].fieldKey == FWPM_CONDITION_IP_LOCAL_ADDRESS) + { + if(inboundFilter.filterCondition[conditionIndex].conditionValue.type == FWP_UINT32) + { + UINT32 ipv4Address = 0; + + RtlCopyMemory(&ipv4Address, + pPCProxyData->proxyLocalAddress.pIPv4, + IPV4_ADDRESS_LENGTH); + + ipv4Address = ntohl(ipv4Address); + + inboundFilter.filterCondition[conditionIndex].conditionValue.uint32 = ipv4Address; + } + else + { + inboundFilter.filterCondition[conditionIndex].conditionValue.byteArray16 = &(pIPv6Addresses[0]); + + RtlCopyMemory(inboundFilter.filterCondition[conditionIndex].conditionValue.byteArray16->byteArray16, + pPCProxyData->proxyLocalAddress.pIPv6, + IPV6_ADDRESS_LENGTH); + } + } + else if(pPCProxyData->flags & PCPDF_PROXY_REMOTE_ADDRESS && + inboundFilter.filterCondition[conditionIndex].fieldKey == FWPM_CONDITION_IP_REMOTE_ADDRESS) + { + if(inboundFilter.filterCondition[conditionIndex].conditionValue.type == FWP_UINT32) + { + UINT32 ipv4Address = 0; + + RtlCopyMemory(&ipv4Address, + pPCProxyData->proxyRemoteAddress.pIPv4, + IPV4_ADDRESS_LENGTH); + + ipv4Address = ntohl(ipv4Address); + + inboundFilter.filterCondition[conditionIndex].conditionValue.uint32 = ipv4Address; + } + else + { + inboundFilter.filterCondition[conditionIndex].conditionValue.byteArray16 = &(pIPv6Addresses[0]); + + RtlCopyMemory(inboundFilter.filterCondition[conditionIndex].conditionValue.byteArray16->byteArray16, + pPCProxyData->proxyRemoteAddress.pIPv6, + IPV6_ADDRESS_LENGTH); + } + } + + conditionIndex++; + } + } + } + } + + status = HlprFwpmEngineOpen(&engineHandle); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmTransactionBegin(engineHandle); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmProviderContextAdd(engineHandle, + &providerContext); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmCalloutAdd(engineHandle, + &outboundCallout); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmFilterAdd(engineHandle, + &outboundFilter); + HLPR_BAIL_ON_FAILURE(status); + + if(pFilter->layerKey == FWPM_LAYER_OUTBOUND_TRANSPORT_V4 || + pFilter->layerKey == FWPM_LAYER_OUTBOUND_TRANSPORT_V6) + { + status = HlprFwpmCalloutAdd(engineHandle, + &inboundCallout); + HLPR_BAIL_ON_FAILURE(status); + + status = HlprFwpmFilterAdd(engineHandle, + &inboundFilter); + HLPR_BAIL_ON_FAILURE(status); + } + + status = HlprFwpmTransactionCommit(engineHandle); + HLPR_BAIL_ON_FAILURE(status); + + HLPR_BAIL_LABEL: + + if(engineHandle) + { + if(status != NO_ERROR) + HlprFwpmTransactionAbort(engineHandle); + + HlprFwpmEngineClose(&engineHandle); + } + + if(hasNetworkFilter) + { + HLPR_DELETE_ARRAY(inboundFilter.filterCondition); + } + } + else + status = (UINT32)FWP_E_INCOMPATIBLE_LAYER; + + return status; +} + +/** + @scenario_function="ScenarioProxyRemove" + + Purpose: Function that removes corresponding objects for a previously added SCENARIO_PROXY.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 ScenarioProxyRemove(_In_ const FWPM_FILTER* pFilter, + _In_ const PC_PROXY_DATA* pPCProxyData) +{ + ASSERT(pFilter); + ASSERT(pPCProxyData); + + return PrvScenarioProxyDeleteFwpmObjects(pFilter, + pPCProxyData); +} + +/** + @scenario_function="ScenarioProxyAdd" + + Purpose: Scenario which will proxy either a connection or a socket to a new endpoint.
+
+ Notes: Adds a filter which references one of the WFPSAMPLER_CALLOUT_PROXY callouts for + the provided layer.
+
+ MSDN_Ref:
+*/ +_Success_(return == NO_ERROR) +UINT32 ScenarioProxyAdd(_In_ const FWPM_FILTER* pFilter, + _In_ const PC_PROXY_DATA* pPCProxyData) +{ + ASSERT(pFilter); + ASSERT(pPCProxyData); + + return PrvScenarioProxyAddFwpmObjects(pFilter, + pPCProxyData); +} + +/** + @rpc_function="RPCInvokeScenarioProxy" + + Purpose: RPC exposed function used to dipatch the scenario routines for SCENARIO_PROXY.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +/* [fault_status][comm_status] */ +error_status_t RPCInvokeScenarioProxy(/* [in] */ handle_t rpcBindingHandle, + /* [in] */ WFPSAMPLER_SCENARIO scenario, + /* [in] */ FWPM_CHANGE_TYPE changeType, + /* [ref][in] */ __RPC__in const FWPM_FILTER0* pFilter, + /* [ref][in] */ __RPC__in const PC_PROXY_DATA* pPCProxyData) +{ + UNREFERENCED_PARAMETER(rpcBindingHandle); + UNREFERENCED_PARAMETER(scenario); + + ASSERT(pFilter); + ASSERT(pPCProxyData); + ASSERT(scenario == SCENARIO_PROXY); + ASSERT(changeType < FWPM_CHANGE_TYPE_MAX); + + UINT32 status = NO_ERROR; + + if(changeType == FWPM_CHANGE_ADD) + { + if(pPCProxyData) + status = ScenarioProxyAdd(pFilter, + pPCProxyData); + else + status = ERROR_INVALID_PARAMETER; + } + else + { + if(pPCProxyData) + status = ScenarioProxyRemove(pFilter, + pPCProxyData); + else + status = ERROR_INVALID_PARAMETER; + } + + if(status != NO_ERROR) + HlprLogError(L"RPCInvokeScenarioProxy() [status: %#x][pPCProxyData: %#x]", + status, + pPCProxyData); + + return status; +} diff --git a/network/trans/WFPSampler/svc/WFPSamplerService.vcxproj b/network/trans/WFPSampler/svc/WFPSamplerService.vcxproj new file mode 100644 index 000000000..a24d6d91c --- /dev/null +++ b/network/trans/WFPSampler/svc/WFPSamplerService.vcxproj @@ -0,0 +1,208 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {312373E3-9E58-48CC-A61C-73F3A3B0179B} + $(MSBuildProjectName) + Debug + Win32 + {BE41C1BA-3C21-458E-BDDD-7AB136137508} + + + + 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) + + + + + + + + + + + + + + + + WFPSamplerService + + + WFPSamplerService + + + WFPSamplerService + + + WFPSamplerService + + + + Sync + true + Level4 + %(PreprocessorDefinitions);WIN32_LEAN_AND_MEAN;UNICODE;_UNICODE + %(AdditionalIncludeDirectories);..\inc;..\lib;$(SDK_INC_PATH);.\$(IntDir);.\..\lib\$(IntDir) + + + %(PreprocessorDefinitions);WIN32_LEAN_AND_MEAN;UNICODE;_UNICODE + %(AdditionalIncludeDirectories);..\inc;..\lib;$(SDK_INC_PATH);.\$(IntDir);.\..\lib\$(IntDir) + + + %(PreprocessorDefinitions);WIN32_LEAN_AND_MEAN;UNICODE;_UNICODE + %(AdditionalIncludeDirectories);..\inc;..\lib;$(SDK_INC_PATH);.\$(IntDir);.\..\lib\$(IntDir) + + + %(AdditionalOptions) /integritycheck + %(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;rpcns4.lib;fwpuclnt.lib;ws2_32.lib;api-ms-win-net-isolation-l1-1-0.lib;.\..\lib\$(IntDir)\WFPSampler.lib + + + + + Sync + true + Level4 + %(PreprocessorDefinitions);WIN32_LEAN_AND_MEAN;UNICODE;_UNICODE + %(AdditionalIncludeDirectories);..\inc;..\lib;$(SDK_INC_PATH);.\$(IntDir);.\..\lib\$(IntDir) + + + %(PreprocessorDefinitions);WIN32_LEAN_AND_MEAN;UNICODE;_UNICODE + %(AdditionalIncludeDirectories);..\inc;..\lib;$(SDK_INC_PATH);.\$(IntDir);.\..\lib\$(IntDir) + + + %(PreprocessorDefinitions);WIN32_LEAN_AND_MEAN;UNICODE;_UNICODE + %(AdditionalIncludeDirectories);..\inc;..\lib;$(SDK_INC_PATH);.\$(IntDir);.\..\lib\$(IntDir) + + + %(AdditionalOptions) /integritycheck + %(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;rpcns4.lib;fwpuclnt.lib;ws2_32.lib;api-ms-win-net-isolation-l1-1-0.lib;.\..\lib\$(IntDir)\WFPSampler.lib + + + + + Sync + true + Level4 + %(PreprocessorDefinitions);WIN32_LEAN_AND_MEAN;UNICODE;_UNICODE + %(AdditionalIncludeDirectories);..\inc;..\lib;$(SDK_INC_PATH);.\$(IntDir);.\..\lib\$(IntDir) + + + %(PreprocessorDefinitions);WIN32_LEAN_AND_MEAN;UNICODE;_UNICODE + %(AdditionalIncludeDirectories);..\inc;..\lib;$(SDK_INC_PATH);.\$(IntDir);.\..\lib\$(IntDir) + + + %(PreprocessorDefinitions);WIN32_LEAN_AND_MEAN;UNICODE;_UNICODE + %(AdditionalIncludeDirectories);..\inc;..\lib;$(SDK_INC_PATH);.\$(IntDir);.\..\lib\$(IntDir) + + + %(AdditionalOptions) /integritycheck + %(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;rpcns4.lib;fwpuclnt.lib;ws2_32.lib;api-ms-win-net-isolation-l1-1-0.lib;.\..\lib\$(IntDir)\WFPSampler.lib + + + + + Sync + true + Level4 + %(PreprocessorDefinitions);WIN32_LEAN_AND_MEAN;UNICODE;_UNICODE + %(AdditionalIncludeDirectories);..\inc;..\lib;$(SDK_INC_PATH);.\$(IntDir);.\..\lib\$(IntDir) + + + %(PreprocessorDefinitions);WIN32_LEAN_AND_MEAN;UNICODE;_UNICODE + %(AdditionalIncludeDirectories);..\inc;..\lib;$(SDK_INC_PATH);.\$(IntDir);.\..\lib\$(IntDir) + + + %(PreprocessorDefinitions);WIN32_LEAN_AND_MEAN;UNICODE;_UNICODE + %(AdditionalIncludeDirectories);..\inc;..\lib;$(SDK_INC_PATH);.\$(IntDir);.\..\lib\$(IntDir) + + + %(AdditionalOptions) /integritycheck + %(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;rpcns4.lib;fwpuclnt.lib;ws2_32.lib;api-ms-win-net-isolation-l1-1-0.lib;.\..\lib\$(IntDir)\WFPSampler.lib + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/network/trans/WFPSampler/svc/WFPSamplerService.vcxproj.Filters b/network/trans/WFPSampler/svc/WFPSamplerService.vcxproj.Filters new file mode 100644 index 000000000..b51ac17a7 --- /dev/null +++ b/network/trans/WFPSampler/svc/WFPSamplerService.vcxproj.Filters @@ -0,0 +1,72 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {492F25BA-6855-45CC-8C4E-BD00CB4C34CF} + + + h;hpp;hxx;hm;inl;inc;xsd + {86FB0B19-58E5-4E59-8D0C-7D7AABB718A4} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {C8A6605B-93D4-4F38-AEE2-69EA9C851085} + + + + + 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/network/trans/WFPSampler/sys/ClassifyFunctions_AdvancedPacketInjectionCallouts.cpp b/network/trans/WFPSampler/sys/ClassifyFunctions_AdvancedPacketInjectionCallouts.cpp new file mode 100644 index 000000000..1df1f7b5c --- /dev/null +++ b/network/trans/WFPSampler/sys/ClassifyFunctions_AdvancedPacketInjectionCallouts.cpp @@ -0,0 +1,4250 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// ClassifyFunctions_AdvancedPacketInjectionCallouts.cpp +// +// Abstract: +// This module contains WFP Classify functions for injecting packets back into the data path +// using the allocate / block / inject method. +// +// Naming Convention: +// +// +// +// i.e. +// ClassifyAdvancedPacketInjection +// +// +// Classify - Function is an FWPS_CALLOUT_CLASSIFY_FN +// +// AdvancedPacketInjection - Function demonstrates the allocate / block / inject model. +// +// +// +// i.e. +// TriggerAdvancedPacketInjectionOutOfBand +// +// +// { +// - +// Trigger - Initiates the desired scenario. +// Perform - Executes the desired scenario. +// } +// +// AdvancedPacketInjection - Function demonstrates the allocate / block / inject model. +// +// DeferredProcedureCall - DPC routine for Out of Band injection which dispatches the +// proper Perform Function. +// WorkItemRoutine - WorkItem Routine for Out of Band Injection which dispatches +// the proper Perform Function. +// AtInboundMACFrame - Function operates on: +// FWPM_LAYER_INBOUND_MAC_FRAME_ETHERNET, and +// FWPM_LAYER_INBOUND_MAC_NATIVE. +// AtOutboundMACFrame - Function operates on: +// FWPM_LAYER_OUTBOUND_MAC_FRAME_ETHERNET, and +// FWPM_LAYER_OUTBOUND_MAC_NATIVE. +// AtEgressVSwitchEthernet - Function operates on: +// FWPM_LAYER_EGRESS_VSWITCH_ETHERNET. +// AtIngressVSwitchEthernet - Function operates on: +// FWPM_LAYER_INGRESS_VSWITCH_ETHERNET. +// AtInboundNetwork - Function operates on: +// FWPM_LAYER_INBOUND_IPPACKET_V{4/6} +// AtOutboundNetwork - Function operates on: +// FWPM_LAYER_OUTBOUND_IPPACKET_V{4/6} +// AtForward - Function operates on: +// FWPM_LAYER_IPFORWARD_V{4/6} +// AtInboundTransport - Function operates on: +// FWPM_LAYER_INBOUND_TRANSPORT_V{4/6}, +// FWPM_LAYER_INBOUND_ICMP_ERROR_V{4/6}, +// FWPM_LAYER_DATAGRAM_DATA_V{4/6}, +// FWPM_LAYER_STREAM_PACKET_V{4/6}, and +// FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V{4/6} +// FWPM_LAYER_ALE_FLOW_ESTABLISHED_V{4/6} +// AtOutboundTransport - Function operates on: +// FWPM_LAYER_OUTBOUND_TRANSPORT_V{4/6}, +// FWPM_LAYER_OUTBOUND_ICMP_ERROR_V{4/6}, +// FWPM_LAYER_DATAGRAM_DATA_V{4/6}, +// FWPM_LAYER_STREAM_PACKET_V{4/6}, and +// FWPM_LAYER_ALE_AUTH_CONNECT_V{4/6} +// FWPM_LAYER_ALE_FLOW_ESTABLISHED_V{4/6} +// +// Private Functions: +// AdvancedPacketInjectionDeferredProcedureCall(), +// AdvancedPacketInjectionWorkItemRoutine(), +// PerformAdvancedPacketInjectionAtEgressVSwitchEthernet(), +// PerformAdvancedPacketInjectionAtForward(), +// PerformAdvancedPacketInjectionAtInboundMACFrame(), +// PerformAdvancedPacketInjectionAtInboundNetwork(), +// PerformAdvancedPacketInjectionAtInboundTransport(), +// PerformAdvancedPacketInjectionAtIngressVSwitchEthernet(), +// PerformAdvancedPacketInjectionAtOutboundMACFrame(), +// PerformAdvancedPacketInjectionAtOutboundNetwork(), +// PerformAdvancedPacketInjectionAtOutboundTransport(), +// TriggerAdvancedPacketInjectionInline(), +// TriggerAdvancedPacketInjectionOutOfBand(), +// +// Public Functions: +// ClassifyAdvancedPacketInjection(), +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "Framework_WFPSamplerCalloutDriver.h" /// . +#include "ClassifyFunctions_AdvancedPacketInjectionCallouts.tmh" /// $(OBJ_PATH)\$(O)\ + +#if DBG + +INJECTION_COUNTERS g_apiTotalClassifies = {0}; +INJECTION_COUNTERS g_apiTotalBlockedAndAbsorbed = {0}; +INJECTION_COUNTERS g_apiTotalPermitted = {0}; +INJECTION_COUNTERS g_apiTotalSuccessfulInjectionCalls = {0}; +INJECTION_COUNTERS g_apiTotalFailedInjectionCalls = {0}; +INJECTION_COUNTERS g_apiOutstandingNewNBLs = {0}; + +/** + @function="PrvAdvancedPacketInjectionCountersIncrement" + + Purpose: Increment the appropriate counters based on the layerId and direction.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Desktop/MS683615.aspx
+*/ +VOID PrvAdvancedPacketInjectionCountersIncrement(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadataValues, + _Inout_ INJECTION_COUNTERS* pCounters) +{ + UINT32 direction = FWP_DIRECTION_MAX; + FWP_VALUE* pDirectionValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_DIRECTION); + + direction = KrnlHlprFwpsLayerGetDirection(pClassifyValues->layerId); + + if(pDirectionValue && + pDirectionValue->type == FWP_UINT32) + direction = (FWP_DIRECTION)pDirectionValue->uint32; + else + { + if(pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V4 || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V4_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V6 || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V6_DISCARD) + { + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadataValues, + FWPS_METADATA_FIELD_FORWARD_LAYER_INBOUND_PASS_THRU)) + direction = FWP_DIRECTION_INBOUND; + else if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadataValues, + FWPS_METADATA_FIELD_FORWARD_LAYER_OUTBOUND_PASS_THRU)) + direction = FWP_DIRECTION_OUTBOUND; + } + else + { + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadataValues, + FWPS_METADATA_FIELD_PACKET_DIRECTION)) + direction = pMetadataValues->packetDirection; + } + } + + switch(pClassifyValues->layerId) + { + case FWPS_LAYER_INBOUND_IPPACKET_V4: + { + InterlockedIncrement64((LONG64*)&(pCounters->inboundNetwork_IPv4)); + + break; + } + case FWPS_LAYER_INBOUND_IPPACKET_V6: + { + InterlockedIncrement64((LONG64*)&(pCounters->inboundNetwork_IPv6)); + + break; + } + case FWPS_LAYER_OUTBOUND_IPPACKET_V4: + { + InterlockedIncrement64((LONG64*)&(pCounters->outboundNetwork_IPv4)); + + break; + } + case FWPS_LAYER_OUTBOUND_IPPACKET_V6: + { + InterlockedIncrement64((LONG64*)&(pCounters->outboundNetwork_IPv6)); + + break; + } + case FWPS_LAYER_IPFORWARD_V4: + { + if(direction == FWP_DIRECTION_OUTBOUND) + InterlockedIncrement64((LONG64*)&(pCounters->inboundForward_IPv4)); + else + InterlockedIncrement64((LONG64*)&(pCounters->outboundForward_IPv4)); + + break; + } + case FWPS_LAYER_IPFORWARD_V6: + { + if(direction == FWP_DIRECTION_INBOUND) + InterlockedIncrement64((LONG64*)&(pCounters->inboundForward_IPv6)); + else + InterlockedIncrement64((LONG64*)&(pCounters->outboundForward_IPv6)); + + break; + } + case FWPS_LAYER_INBOUND_TRANSPORT_V4: + { + InterlockedIncrement64((LONG64*)&(pCounters->inboundTransport_IPv4)); + + break; + } + case FWPS_LAYER_INBOUND_TRANSPORT_V6: + { + InterlockedIncrement64((LONG64*)&(pCounters->inboundTransport_IPv6)); + + break; + } + case FWPS_LAYER_OUTBOUND_TRANSPORT_V4: + { + InterlockedIncrement64((LONG64*)&(pCounters->outboundTransport_IPv4)); + + break; + } + case FWPS_LAYER_OUTBOUND_TRANSPORT_V6: + { + InterlockedIncrement64((LONG64*)&(pCounters->outboundTransport_IPv6)); + + + break; + } + case FWPS_LAYER_DATAGRAM_DATA_V4: + { + if(direction == FWP_DIRECTION_INBOUND) + InterlockedIncrement64((LONG64*)&(pCounters->inboundTransport_IPv4)); + else + InterlockedIncrement64((LONG64*)&(pCounters->outboundTransport_IPv4)); + + break; + } + case FWPS_LAYER_DATAGRAM_DATA_V6: + { + if(direction == FWP_DIRECTION_INBOUND) + InterlockedIncrement64((LONG64*)&(pCounters->inboundTransport_IPv6)); + else + InterlockedIncrement64((LONG64*)&(pCounters->outboundTransport_IPv6)); + + break; + } + case FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4: + { + if(direction == FWP_DIRECTION_INBOUND) + InterlockedIncrement64((LONG64*)&(pCounters->inboundTransport_IPv4)); + else + InterlockedIncrement64((LONG64*)&(pCounters->outboundTransport_IPv4)); + + break; + } + case FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6: + { + if(direction == FWP_DIRECTION_INBOUND) + InterlockedIncrement64((LONG64*)&(pCounters->inboundTransport_IPv6)); + else + InterlockedIncrement64((LONG64*)&(pCounters->outboundTransport_IPv6)); + + break; + } + case FWPS_LAYER_ALE_AUTH_CONNECT_V4: + { + if(direction == FWP_DIRECTION_INBOUND) + InterlockedIncrement64((LONG64*)&(pCounters->inboundTransport_IPv4)); + else + InterlockedIncrement64((LONG64*)&(pCounters->outboundTransport_IPv4)); + + break; + } + case FWPS_LAYER_ALE_AUTH_CONNECT_V6: + { + if(direction == FWP_DIRECTION_INBOUND) + InterlockedIncrement64((LONG64*)&(pCounters->inboundTransport_IPv6)); + else + InterlockedIncrement64((LONG64*)&(pCounters->outboundTransport_IPv6)); + + break; + } + case FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4: + { + if(direction == FWP_DIRECTION_INBOUND) + InterlockedIncrement64((LONG64*)&(pCounters->inboundTransport_IPv4)); + else + InterlockedIncrement64((LONG64*)&(pCounters->outboundTransport_IPv4)); + + break; + } + case FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6: + { + if(direction == FWP_DIRECTION_INBOUND) + InterlockedIncrement64((LONG64*)&(pCounters->inboundTransport_IPv6)); + else + InterlockedIncrement64((LONG64*)&(pCounters->outboundTransport_IPv6)); + + break; + } + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + case FWPS_LAYER_STREAM_PACKET_V4: + { + if(direction == FWP_DIRECTION_INBOUND) + InterlockedIncrement64((LONG64*)&(pCounters->inboundTransport_IPv4)); + else + InterlockedIncrement64((LONG64*)&(pCounters->outboundTransport_IPv4)); + + break; + } + case FWPS_LAYER_STREAM_PACKET_V6: + { + if(direction == FWP_DIRECTION_INBOUND) + InterlockedIncrement64((LONG64*)&(pCounters->inboundTransport_IPv6)); + else + InterlockedIncrement64((LONG64*)&(pCounters->outboundTransport_IPv6)); + + break; + } + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + case FWPS_LAYER_INBOUND_MAC_FRAME_ETHERNET: + { + UINT16 etherType = pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_MAC_FRAME_ETHERNET_ETHER_TYPE].value.uint16; + + if(etherType == 0x86DD) + InterlockedIncrement64((LONG64*)&(pCounters->inboundMAC_IPv6)); + else if(etherType == 0x800) + InterlockedIncrement64((LONG64*)&(pCounters->inboundMAC_IPv4)); + else + InterlockedIncrement64((LONG64*)&(pCounters->inboundMAC_Unknown)); + + break; + } + case FWPS_LAYER_OUTBOUND_MAC_FRAME_ETHERNET: + { + UINT16 etherType = pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_MAC_FRAME_ETHERNET_ETHER_TYPE].value.uint16; + + if(etherType == 0x86DD) + InterlockedIncrement64((LONG64*)&(pCounters->outboundMAC_IPv6)); + else if(etherType == 0x800) + InterlockedIncrement64((LONG64*)&(pCounters->outboundMAC_IPv4)); + else + InterlockedIncrement64((LONG64*)&(pCounters->outboundMAC_Unknown)); + + break; + } + case FWPS_LAYER_INBOUND_MAC_FRAME_NATIVE: + { + InterlockedIncrement64((LONG64*)&(pCounters->inboundMAC_Unknown)); + + break; + } + case FWPS_LAYER_OUTBOUND_MAC_FRAME_NATIVE: + { + InterlockedIncrement64((LONG64*)&(pCounters->outboundMAC_Unknown)); + + break; + } + case FWPS_LAYER_INGRESS_VSWITCH_ETHERNET: + { + UINT16 etherType = pClassifyValues->incomingValue[FWPS_FIELD_INGRESS_VSWITCH_ETHERNET_ETHER_TYPE].value.uint16; + + if(etherType == 0x86DD) + InterlockedIncrement64((LONG64*)&(pCounters->ingressVSwitch_IPv6)); + else if(etherType == 0x800) + InterlockedIncrement64((LONG64*)&(pCounters->ingressVSwitch_IPv4)); + else + InterlockedIncrement64((LONG64*)&(pCounters->ingressVSwitch_Unknown)); + + break; + } + case FWPS_LAYER_EGRESS_VSWITCH_ETHERNET: + { + UINT16 etherType = pClassifyValues->incomingValue[FWPS_FIELD_EGRESS_VSWITCH_ETHERNET_ETHER_TYPE].value.uint16; + + if(etherType == 0x86DD) + InterlockedIncrement64((LONG64*)&(pCounters->egressVSwitch_IPv6)); + else if(etherType == 0x800) + InterlockedIncrement64((LONG64*)&(pCounters->egressVSwitch_IPv4)); + else + InterlockedIncrement64((LONG64*)&(pCounters->egressVSwitch_Unknown)); + + break; + } + +#endif // (NTDDI_VERSION >= NTDDI_WIN8) +#endif // (NTDDI_VERSION >= NTDDI_WIN7) + + } + + return; +} + +/** + @private_function="PrvAdvancedPacketInjectionCountersIncrementTotalActionResults" + + Purpose: Increment the appropriate counters based on the layerId, direction, and action.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +VOID PrvAdvancedPacketInjectionCountersIncrementTotalActionResults(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadataValues, + _In_ const FWPS_CLASSIFY_OUT* pClassifyOut) +{ + INJECTION_COUNTERS* pCounters = 0; + + if(pClassifyOut->actionType == FWP_ACTION_BLOCK) + { + NT_ASSERT(pClassifyOut->flags & FWPS_CLASSIFY_OUT_FLAG_ABSORB); + + pCounters = &g_apiTotalBlockedAndAbsorbed; + } + else + pCounters = &g_apiTotalPermitted; + + PrvAdvancedPacketInjectionCountersIncrement(pClassifyValues, + pMetadataValues, + pCounters); + + return; +} + +/** + @function="AdvancedPacketInjectionCountersIncrement" + + Purpose: Increment the appropriate counters based on the injection handle.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Desktop/MS683615.aspx
+*/ +VOID AdvancedPacketInjectionCountersIncrement(_In_ HANDLE injectionHandle, + _Inout_ INJECTION_COUNTERS* pCounters) +{ + if(injectionHandle == g_pIPv4InboundNetworkInjectionHandles[0] || + injectionHandle == g_pIPv4InboundNetworkInjectionHandles[1]) + InterlockedIncrement64((LONG64*)&(pCounters->inboundNetwork_IPv4)); + else if(injectionHandle == g_pIPv6InboundNetworkInjectionHandles[0] || + injectionHandle == g_pIPv6InboundNetworkInjectionHandles[1]) + InterlockedIncrement64((LONG64*)&(pCounters->inboundNetwork_IPv6)); + else if(injectionHandle == g_pIPv4OutboundNetworkInjectionHandles[0] || + injectionHandle == g_pIPv4OutboundNetworkInjectionHandles[1]) + InterlockedIncrement64((LONG64*)&(pCounters->outboundNetwork_IPv4)); + else if(injectionHandle == g_pIPv6OutboundNetworkInjectionHandles[0] || + injectionHandle == g_pIPv6OutboundNetworkInjectionHandles[1]) + InterlockedIncrement64((LONG64*)&(pCounters->outboundNetwork_IPv6)); + else if(injectionHandle == g_pIPv4InboundForwardInjectionHandles[0] || + injectionHandle == g_pIPv4InboundForwardInjectionHandles[1]) + InterlockedIncrement64((LONG64*)&(pCounters->inboundForward_IPv4)); + else if(injectionHandle == g_pIPv6InboundForwardInjectionHandles[0] || + injectionHandle == g_pIPv6InboundForwardInjectionHandles[1]) + InterlockedIncrement64((LONG64*)&(pCounters->inboundForward_IPv6)); + else if(injectionHandle == g_pIPv4OutboundForwardInjectionHandles[0] || + injectionHandle == g_pIPv4OutboundForwardInjectionHandles[1]) + InterlockedIncrement64((LONG64*)&(pCounters->outboundForward_IPv4)); + else if(injectionHandle == g_pIPv6OutboundForwardInjectionHandles[0] || + injectionHandle == g_pIPv6OutboundForwardInjectionHandles[1]) + InterlockedIncrement64((LONG64*)&(pCounters->outboundForward_IPv6)); + else if(injectionHandle == g_pIPv4InboundTransportInjectionHandles[0] || + injectionHandle == g_pIPv4InboundTransportInjectionHandles[1]) + InterlockedIncrement64((LONG64*)&(pCounters->inboundTransport_IPv4)); + else if(injectionHandle == g_pIPv6InboundTransportInjectionHandles[0] || + injectionHandle == g_pIPv6InboundTransportInjectionHandles[1]) + InterlockedIncrement64((LONG64*)&(pCounters->inboundTransport_IPv6)); + else if(injectionHandle == g_pIPv4OutboundTransportInjectionHandles[0] || + injectionHandle == g_pIPv4OutboundTransportInjectionHandles[1]) + InterlockedIncrement64((LONG64*)&(pCounters->outboundTransport_IPv4)); + else if(injectionHandle == g_pIPv6OutboundTransportInjectionHandles[0] || + injectionHandle == g_pIPv6OutboundTransportInjectionHandles[1]) + InterlockedIncrement64((LONG64*)&(pCounters->outboundTransport_IPv6)); + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + else if(injectionHandle == g_pIPv4InboundMACInjectionHandles[0] || + injectionHandle == g_pIPv4InboundMACInjectionHandles[1]) + InterlockedIncrement64((LONG64*)&(pCounters->inboundMAC_IPv4)); + else if(injectionHandle == g_pIPv6InboundMACInjectionHandles[0] || + injectionHandle == g_pIPv6InboundMACInjectionHandles[1]) + InterlockedIncrement64((LONG64*)&(pCounters->inboundMAC_IPv6)); + else if(injectionHandle == g_pInboundMACInjectionHandles[0] || + injectionHandle == g_pInboundMACInjectionHandles[1]) + InterlockedIncrement64((LONG64*)&(pCounters->inboundMAC_Unknown)); + else if(injectionHandle == g_pIPv4OutboundMACInjectionHandles[0] || + injectionHandle == g_pIPv4OutboundMACInjectionHandles[1]) + InterlockedIncrement64((LONG64*)&(pCounters->outboundMAC_IPv4)); + else if(injectionHandle == g_pIPv6OutboundMACInjectionHandles[0] || + injectionHandle == g_pIPv6OutboundMACInjectionHandles[1]) + InterlockedIncrement64((LONG64*)&(pCounters->outboundMAC_IPv6)); + else if(injectionHandle == g_pOutboundMACInjectionHandles[0] || + injectionHandle == g_pOutboundMACInjectionHandles[1]) + InterlockedIncrement64((LONG64*)&(pCounters->outboundMAC_Unknown)); + else if(injectionHandle == g_pIPv4IngressVSwitchEthernetInjectionHandles[0] || + injectionHandle == g_pIPv4IngressVSwitchEthernetInjectionHandles[1]) + InterlockedIncrement64((LONG64*)&(pCounters->ingressVSwitch_IPv4)); + else if(injectionHandle == g_pIPv6IngressVSwitchEthernetInjectionHandles[0] || + injectionHandle == g_pIPv6IngressVSwitchEthernetInjectionHandles[1]) + InterlockedIncrement64((LONG64*)&(pCounters->ingressVSwitch_IPv6)); + else if(injectionHandle == g_pIngressVSwitchEthernetInjectionHandles[0] || + injectionHandle == g_pIngressVSwitchEthernetInjectionHandles[1]) + InterlockedIncrement64((LONG64*)&(pCounters->ingressVSwitch_Unknown)); + else if(injectionHandle == g_pIPv4EgressVSwitchEthernetInjectionHandles[0] || + injectionHandle == g_pIPv4EgressVSwitchEthernetInjectionHandles[1]) + InterlockedIncrement64((LONG64*)&(pCounters->egressVSwitch_IPv4)); + else if(injectionHandle == g_pIPv6EgressVSwitchEthernetInjectionHandles[0] || + injectionHandle == g_pIPv6EgressVSwitchEthernetInjectionHandles[1]) + InterlockedIncrement64((LONG64*)&(pCounters->egressVSwitch_IPv6)); + else if(injectionHandle == g_pEgressVSwitchEthernetInjectionHandles[0] || + injectionHandle == g_pEgressVSwitchEthernetInjectionHandles[1]) + InterlockedIncrement64((LONG64*)&(pCounters->egressVSwitch_Unknown)); + +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) + + return; +} + +#endif /// DBG + +#if(NTDDI_VERSION >= NTDDI_WIN8) + +/** + @private_function="PerformAdvancedPacketInjectionAtInboundMACFrame" + + Purpose: Creates a new NET_BUFFER_LIST and injects it in to the stack from the incoming MAC + Layers using FwpsInjectMacReceiveAsync().
+
+ Notes: Applies to the following inbound layers:
+ FWPM_LAYER_INBOUND_MAC_FRAME_ETHERNET
+ FWPM_LAYER_INBOUND_MAC_FRAME_NATIVE
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/HH439588.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF546324.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +NTSTATUS PerformAdvancedPacketInjectionAtInboundMACFrame(_In_ CLASSIFY_DATA** ppClassifyData, + _In_ INJECTION_DATA** ppInjectionData, + _In_ BOOLEAN isInline = FALSE) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PerformAdvancedPacketInjectionAtInboundMACFrame()\n"); + +#endif /// DBG + + NT_ASSERT(ppClassifyData); + NT_ASSERT(ppInjectionData); + NT_ASSERT(*ppClassifyData); + NT_ASSERT(*ppInjectionData); + + NTSTATUS status = STATUS_SUCCESS; + FWPS_INCOMING_VALUES* pClassifyValues = (FWPS_INCOMING_VALUES*)(*ppClassifyData)->pClassifyValues; + FWPS_INCOMING_METADATA_VALUES* pMetadata = (FWPS_INCOMING_METADATA_VALUES*)(*ppClassifyData)->pMetadataValues; + PC_ADVANCED_PACKET_INJECTION_DATA* pData = (PC_ADVANCED_PACKET_INJECTION_DATA*)(*ppClassifyData)->pFilter->providerContext->dataBuffer->data; + IF_INDEX interfaceIndex = 0; + NDIS_PORT_NUMBER ndisPort = 0; + NET_BUFFER_LIST* pNetBufferList = 0; + UINT32 size = 0; + ADVANCED_PACKET_INJECTION_COMPLETION_DATA* pCompletionData = 0; + UINT32 bytesRetreated = 0; + FWP_VALUE* pInterfaceIndex = 0; + FWP_VALUE* pNDISPort = 0; + +#if DBG + + KIRQL irql = KeGetCurrentIrql(); + HANDLE injectionHandle = (*ppInjectionData)->injectionHandle; + +#endif /// DBG + +#pragma warning(push) +#pragma warning(disable: 6014) /// pCompletionData will be freed in completionFn using AdvancedPacketInjectionCompletionDataDestroy + + HLPR_NEW(pCompletionData, + ADVANCED_PACKET_INJECTION_COMPLETION_DATA, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pCompletionData, + status); + +#pragma warning(pop) + + KeInitializeSpinLock(&(pCompletionData->spinLock)); + + pCompletionData->performedInline = isInline; + pCompletionData->pClassifyData = *ppClassifyData; + pCompletionData->pInjectionData = *ppInjectionData; + + /// Responsibility for freeing this memory has been transferred to the pCompletionData + *ppClassifyData = 0; + + *ppInjectionData = 0; + + pInterfaceIndex = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_INTERFACE_INDEX); + if(pInterfaceIndex && + pInterfaceIndex->type == FWP_UINT32) + interfaceIndex = (IF_INDEX)pInterfaceIndex->uint32; + + pNDISPort = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_NDIS_PORT); + if(pNDISPort && + pNDISPort->type == FWP_UINT32) + ndisPort = (NDIS_PORT_NUMBER)pNDISPort->uint32; + + /// If NATIVE, initial offset is at the MAC Header ... + if(pClassifyValues->layerId != FWPS_LAYER_INBOUND_MAC_FRAME_NATIVE && + FWPS_IS_L2_METADATA_FIELD_PRESENT(pMetadata, + FWPS_L2_METADATA_FIELD_ETHERNET_MAC_HEADER_SIZE)) + bytesRetreated = pMetadata->ethernetMacHeaderSize; + + if(bytesRetreated) + { + /// ... otherwise the offset is at the IP Header, so retreat the size of the MAC Header ... + status = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket), + bytesRetreated, + 0, + 0); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformAdvancedPacketInjectionAtInboundMACFrame: NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + + HLPR_BAIL; + } + } + + /// ... create a new NET_BUFFER_LIST based on the original NET_BUFFER_LIST ... + pNetBufferList = KrnlHlprNBLCreateNew(g_pNDISPoolData->nblPoolHandle, + (NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket, + &(pCompletionData->pAllocatedBuffer), + &size, + &(pCompletionData->pAllocatedMDL), + pData->additionalBytes, + FALSE); + + if(bytesRetreated) + { + /// ... and advance the offset back to the original position. + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket), + bytesRetreated, + FALSE, + 0); + } + + if(!pNetBufferList) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformAdvancedPacketInjectionAtInboundMACFrame: KrnlHlprNBLCreateNew() [pNetBufferList: %#p]\n", + pNetBufferList); + + HLPR_BAIL; + } + +#if DBG + + AdvancedPacketInjectionCountersIncrement(injectionHandle, + &g_apiOutstandingNewNBLs); + +#endif /// DBG + + pCompletionData->refCount = KrnlHlprNBLGetRequiredRefCount(pNetBufferList, + TRUE); + + status = FwpsInjectMacReceiveAsync(pCompletionData->pInjectionData->injectionHandle, + pCompletionData->pInjectionData->injectionContext, + 0, + pClassifyValues->layerId, + interfaceIndex, + ndisPort, + pNetBufferList, + CompleteAdvancedPacketInjection, + pCompletionData); + + NT_ASSERT(irql == KeGetCurrentIrql()); + + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformAdvancedPacketInjectionAtInboundMACFrame: FwpsInjectMacReceiveAsync() [status: %#x]\n", + status); + +#if DBG + + AdvancedPacketInjectionCountersIncrement(injectionHandle, + &g_apiTotalFailedInjectionCalls); + +#endif /// DBG + + } + +#if DBG + + else + AdvancedPacketInjectionCountersIncrement(injectionHandle, + &g_apiTotalSuccessfulInjectionCalls); + +#endif /// DBG + + HLPR_BAIL_LABEL: + + NT_ASSERT(status == STATUS_SUCCESS); + + if(status != STATUS_SUCCESS) + { + if(pNetBufferList) + { + FwpsFreeNetBufferList(pNetBufferList); + + pNetBufferList = 0; + +#if DBG + + AdvancedPacketInjectionCountersDecrement(injectionHandle, + &g_apiOutstandingNewNBLs); + +#endif + + } + + if(pCompletionData) + AdvancedPacketInjectionCompletionDataDestroy(&pCompletionData, + TRUE); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PerformAdvancedPacketInjectionAtInboundMACFrame() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @private_function="PerformAdvancedPacketInjectionAtOutboundMACFrame" + + Purpose: Creates a new NET_BUFFER_LIST and injects it in to the stack from the outgoing MAC + Layers using FwpsInjectMacSendAsync().
+
+ Notes: Applies to the following outbound layers:
+ FWPM_LAYER_OUTBOUND_MAC_FRAME_ETHERNET
+ FWPM_LAYER_OUTBOUND_MAC_FRAME_NATIVE
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/HH439593.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF546324.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +NTSTATUS PerformAdvancedPacketInjectionAtOutboundMACFrame(_In_ CLASSIFY_DATA** ppClassifyData, + _In_ INJECTION_DATA** ppInjectionData, + _In_ BOOLEAN isInline = FALSE) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PerformAdvancedPacketInjectionAtOutboundMACFrame()\n"); + +#endif /// DBG + + NT_ASSERT(ppClassifyData); + NT_ASSERT(ppInjectionData); + NT_ASSERT(*ppClassifyData); + NT_ASSERT(*ppInjectionData); + + NTSTATUS status = STATUS_SUCCESS; + FWPS_INCOMING_VALUES* pClassifyValues = (FWPS_INCOMING_VALUES*)(*ppClassifyData)->pClassifyValues; + PC_ADVANCED_PACKET_INJECTION_DATA* pData = (PC_ADVANCED_PACKET_INJECTION_DATA*)(*ppClassifyData)->pFilter->providerContext->dataBuffer->data; + IF_INDEX interfaceIndex = 0; + NDIS_PORT_NUMBER ndisPort = 0; + NET_BUFFER_LIST* pNetBufferList = 0; + UINT32 size = 0; + ADVANCED_PACKET_INJECTION_COMPLETION_DATA* pCompletionData = 0; + FWP_VALUE* pInterfaceIndex = 0; + FWP_VALUE* pNDISPort = 0; + +#if DBG + + KIRQL irql = KeGetCurrentIrql(); + HANDLE injectionHandle = (*ppInjectionData)->injectionHandle; + +#endif /// DBG + +#pragma warning(push) +#pragma warning(disable: 6014) /// pCompletionData will be freed in completionFn using AdvancedPacketInjectionCompletionDataDestroy + + HLPR_NEW(pCompletionData, + ADVANCED_PACKET_INJECTION_COMPLETION_DATA, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pCompletionData, + status); + +#pragma warning(pop) + + KeInitializeSpinLock(&(pCompletionData->spinLock)); + + pCompletionData->performedInline = isInline; + pCompletionData->pClassifyData = *ppClassifyData; + pCompletionData->pInjectionData = *ppInjectionData; + + /// Responsibility for freeing this memory has been transferred to the pCompletionData + *ppClassifyData = 0; + + *ppInjectionData = 0; + + pInterfaceIndex = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_INTERFACE_INDEX); + if(pInterfaceIndex && + pInterfaceIndex->type == FWP_UINT32) + interfaceIndex = (IF_INDEX)pInterfaceIndex->uint32; + + pNDISPort = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_NDIS_PORT); + if(pNDISPort && + pNDISPort->type == FWP_UINT32) + ndisPort = (NDIS_PORT_NUMBER)pNDISPort->uint32; + + /// Initial offset is at the MAC Header, so just create a new NET_BUFFER_LIST based on the + /// original NET_BUFFER_LIST ... + pNetBufferList = KrnlHlprNBLCreateNew(g_pNDISPoolData->nblPoolHandle, + (NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket, + &(pCompletionData->pAllocatedBuffer), + &size, + &(pCompletionData->pAllocatedMDL), + pData->additionalBytes, + FALSE); + + if(!pNetBufferList) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformAdvancedPacketInjectionAtOutboundMACFrame: KrnlHlprNBLCreateNew() [pNetBufferList: %#p]\n", + pNetBufferList); + + HLPR_BAIL; + } + +#if DBG + + AdvancedPacketInjectionCountersIncrement(injectionHandle, + &g_apiOutstandingNewNBLs); + +#endif /// DBG + + pCompletionData->refCount = KrnlHlprNBLGetRequiredRefCount(pNetBufferList, + TRUE); + + status = FwpsInjectMacSendAsync(pCompletionData->pInjectionData->injectionHandle, + pCompletionData->pInjectionData->injectionContext, + 0, + pClassifyValues->layerId, + interfaceIndex, + ndisPort, + pNetBufferList, + CompleteAdvancedPacketInjection, + pCompletionData); + + NT_ASSERT(irql == KeGetCurrentIrql()); + + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformAdvancedPacketInjectionAtOutboundMACFrame: FwpsInjectMacSendAsync() [status: %#x]\n", + status); + +#if DBG + + AdvancedPacketInjectionCountersIncrement(injectionHandle, + &g_apiTotalFailedInjectionCalls); + +#endif /// DBG + + } + +#if DBG + + else + AdvancedPacketInjectionCountersIncrement(injectionHandle, + &g_apiTotalSuccessfulInjectionCalls); + +#endif /// DBG + + + HLPR_BAIL_LABEL: + + NT_ASSERT(status == STATUS_SUCCESS); + + if(status != STATUS_SUCCESS) + { + if(pNetBufferList) + { + FwpsFreeNetBufferList(pNetBufferList); + + pNetBufferList = 0; + +#if DBG + + AdvancedPacketInjectionCountersDecrement(injectionHandle, + &g_apiOutstandingNewNBLs); + +#endif + + } + + if(pCompletionData) + AdvancedPacketInjectionCompletionDataDestroy(&pCompletionData, + TRUE); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PerformAdvancedPacketInjectionAtOutboundMACFrame() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @private_function="PerformAdvancedPacketInjectionAtIngressVSwitchEthernet" + + Purpose: Creates a new NET_BUFFER_LIST and injects it in to the virtual switch's ingress + path from the ingress VSwitch Layers using + FwpsInjectvSwitchEthernetIngressAsync0().
+
+ Notes: Applies to the following ingress layers:
+ FWPM_LAYER_INGRESS_VSWITCH_ETHERNET
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/HH439669.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF546324.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +NTSTATUS PerformAdvancedPacketInjectionAtIngressVSwitchEthernet(_In_ CLASSIFY_DATA** ppClassifyData, + _In_ INJECTION_DATA** ppInjectionData, + _In_ BOOLEAN isInline = FALSE) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PerformAdvancedPacketInjectionAtIngressVSwitchEthernet()\n"); + +#endif /// DBG + + NT_ASSERT(ppClassifyData); + NT_ASSERT(ppInjectionData); + NT_ASSERT(*ppClassifyData); + NT_ASSERT(*ppInjectionData); + + NTSTATUS status = STATUS_SUCCESS; + FWPS_INCOMING_VALUES* pClassifyValues = (FWPS_INCOMING_VALUES*)(*ppClassifyData)->pClassifyValues; + FWPS_INCOMING_METADATA_VALUES* pMetadata = (FWPS_INCOMING_METADATA_VALUES*)(*ppClassifyData)->pMetadataValues; + PC_ADVANCED_PACKET_INJECTION_DATA* pData = (PC_ADVANCED_PACKET_INJECTION_DATA*)(*ppClassifyData)->pFilter->providerContext->dataBuffer->data; + FWP_VALUE* pVSwitchIDValue = 0; + FWP_BYTE_BLOB* pVSwitchID = 0; + NDIS_SWITCH_PORT_ID sourcePortID = 0; + NDIS_SWITCH_NIC_INDEX sourceNICIndex = 0; + NET_BUFFER_LIST* pNetBufferList = 0; + UINT32 size = 0; + ADVANCED_PACKET_INJECTION_COMPLETION_DATA* pCompletionData = 0; + +#if DBG + + KIRQL irql = KeGetCurrentIrql(); + HANDLE injectionHandle = (*ppInjectionData)->injectionHandle; + +#endif /// DBG + +#pragma warning(push) +#pragma warning(disable: 6014) /// pCompletionData will be freed in completionFn using AdvancedPacketInjectionCompletionDataDestroy + + HLPR_NEW(pCompletionData, + ADVANCED_PACKET_INJECTION_COMPLETION_DATA, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pCompletionData, + status); + +#pragma warning(pop) + + KeInitializeSpinLock(&(pCompletionData->spinLock)); + + pCompletionData->performedInline = isInline; + pCompletionData->pClassifyData = *ppClassifyData; + pCompletionData->pInjectionData = *ppInjectionData; + + /// Responsibility for freeing this memory has been transferred to the pCompletionData + *ppClassifyData = 0; + + *ppInjectionData = 0; + + if(FWPS_IS_L2_METADATA_FIELD_PRESENT(pMetadata, + FWPS_L2_METADATA_FIELD_VSWITCH_SOURCE_PORT_ID)) + sourcePortID = pMetadata->vSwitchSourcePortId; + + if(FWPS_IS_L2_METADATA_FIELD_PRESENT(pMetadata, + FWPS_L2_METADATA_FIELD_VSWITCH_SOURCE_NIC_INDEX)) + sourceNICIndex = (NDIS_SWITCH_NIC_INDEX)pMetadata->vSwitchSourceNicIndex; + + pVSwitchIDValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_VSWITCH_ID); + if(pVSwitchIDValue) + pVSwitchID = pVSwitchIDValue->byteBlob; + + if(pVSwitchID == 0) + { + status = STATUS_INVALID_MEMBER; + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformAdvancedPacketInjectionAtIngressVSwitchEthernet() [status: %#x][pVSwitchID: %#p]\n", + status, + pVSwitchID); + + HLPR_BAIL; + } + + /// Initial offset is at the MAC Header, so just create a new NET_BUFFER_LIST based on the + /// original NET_BUFFER_LIST ... + pNetBufferList = KrnlHlprNBLCreateNew(g_pNDISPoolData->nblPoolHandle, + (NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket, + &(pCompletionData->pAllocatedBuffer), + &size, + &(pCompletionData->pAllocatedMDL), + pData->additionalBytes, + FALSE); + + if(!pNetBufferList) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformAdvancedPacketInjectionAtIngressVSwitchEthernet: KrnlHlprNBLCreateNew() [pNetBufferList: %#p]\n", + pNetBufferList); + + HLPR_BAIL; + } + +#if DBG + + AdvancedPacketInjectionCountersIncrement(injectionHandle, + &g_apiOutstandingNewNBLs); + +#endif /// DBG + + pCompletionData->refCount = KrnlHlprNBLGetRequiredRefCount(pNetBufferList, + TRUE); + + status = FwpsInjectvSwitchEthernetIngressAsync(pCompletionData->pInjectionData->injectionHandle, + pCompletionData->pInjectionData->injectionContext, + 0, + 0, + pVSwitchID, + sourcePortID, + sourceNICIndex, + pNetBufferList, + CompleteAdvancedPacketInjection, + pCompletionData); + + NT_ASSERT(irql == KeGetCurrentIrql()); + + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformAdvancedPacketInjectionAtIngressVSwitchEthernet: FwpsInjectvSwitchEthernetIngressAsync() [status: %#x]\n", + status); + +#if DBG + + AdvancedPacketInjectionCountersIncrement(injectionHandle, + &g_apiTotalFailedInjectionCalls); + +#endif /// DBG + + } + +#if DBG + + else + AdvancedPacketInjectionCountersIncrement(injectionHandle, + &g_apiTotalSuccessfulInjectionCalls); + +#endif /// DBG + + HLPR_BAIL_LABEL: + + NT_ASSERT(status == STATUS_SUCCESS); + + if(status != STATUS_SUCCESS) + { + if(pNetBufferList) + { + FwpsFreeNetBufferList(pNetBufferList); + + pNetBufferList = 0; + +#if DBG + + AdvancedPacketInjectionCountersDecrement(injectionHandle, + &g_apiOutstandingNewNBLs); + +#endif + + } + + if(pCompletionData) + AdvancedPacketInjectionCompletionDataDestroy(&pCompletionData, + TRUE); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PerformAdvancedPacketInjectionAtIngressVSwitchEthernet() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @private_function="PerformAdvancedPacketInjectionAtEgressVSwitchEthernet" + + Purpose: Creates a new NET_BUFFER_LIST and injects it in to the virtual switch's ingress + path from the egress VSwitch Layers using + FwpsInjectvSwitchEthernetIngressAsync0().
+
+ Notes: Applies to the following egress layers:
+ FWPM_LAYER_EGRESS_VSWITCH_ETHERNET
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/HH439662.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF546324.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +NTSTATUS PerformAdvancedPacketInjectionAtEgressVSwitchEthernet(_In_ CLASSIFY_DATA** ppClassifyData, + _In_ INJECTION_DATA** ppInjectionData, + _In_ BOOLEAN isInline = FALSE) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PerformAdvancedPacketInjectionAtEgressVSwitchEthernet()\n"); + +#endif /// DBG + + NT_ASSERT(ppClassifyData); + NT_ASSERT(ppInjectionData); + NT_ASSERT(*ppClassifyData); + NT_ASSERT(*ppInjectionData); + + NTSTATUS status = STATUS_SUCCESS; + FWPS_INCOMING_VALUES* pClassifyValues = (FWPS_INCOMING_VALUES*)(*ppClassifyData)->pClassifyValues; + FWPS_INCOMING_METADATA_VALUES* pMetadata = (FWPS_INCOMING_METADATA_VALUES*)(*ppClassifyData)->pMetadataValues; + PC_ADVANCED_PACKET_INJECTION_DATA* pData = (PC_ADVANCED_PACKET_INJECTION_DATA*)(*ppClassifyData)->pFilter->providerContext->dataBuffer->data; + FWP_VALUE* pVSwitchIDValue = 0; + FWP_BYTE_BLOB* pVSwitchID = 0; + NDIS_SWITCH_PORT_ID sourcePortID = 0; + NDIS_SWITCH_NIC_INDEX sourceNICIndex = 0; + NET_BUFFER_LIST* pNetBufferList = 0; + UINT32 size = 0; + ADVANCED_PACKET_INJECTION_COMPLETION_DATA* pCompletionData = 0; + +#if DBG + + KIRQL irql = KeGetCurrentIrql(); + HANDLE injectionHandle = (*ppInjectionData)->injectionHandle; + +#endif /// DBG + +#pragma warning(push) +#pragma warning(disable: 6014) /// pCompletionData will be freed in completionFn using AdvancedPacketInjectionCompletionDataDestroy + + HLPR_NEW(pCompletionData, + ADVANCED_PACKET_INJECTION_COMPLETION_DATA, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pCompletionData, + status); + +#pragma warning(pop) + + KeInitializeSpinLock(&(pCompletionData->spinLock)); + + pCompletionData->performedInline = isInline; + pCompletionData->pClassifyData = *ppClassifyData; + pCompletionData->pInjectionData = *ppInjectionData; + + /// Responsibility for freeing this memory has been transferred to the pCompletionData + *ppClassifyData = 0; + + *ppInjectionData = 0; + + if(FWPS_IS_L2_METADATA_FIELD_PRESENT(pMetadata, + FWPS_L2_METADATA_FIELD_VSWITCH_SOURCE_PORT_ID)) + sourcePortID = pMetadata->vSwitchSourcePortId; + + if(FWPS_IS_L2_METADATA_FIELD_PRESENT(pMetadata, + FWPS_L2_METADATA_FIELD_VSWITCH_SOURCE_NIC_INDEX)) + sourceNICIndex = (NDIS_SWITCH_NIC_INDEX)pMetadata->vSwitchSourceNicIndex; + + pVSwitchIDValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_VSWITCH_ID); + if(pVSwitchIDValue) + pVSwitchID = pVSwitchIDValue->byteBlob; + + if(pVSwitchID == 0) + { + status = STATUS_INVALID_MEMBER; + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformAdvancedPacketInjectionAtEgressVSwitchEthernet() [status: %#x][pVSwitchID: %#p]\n", + status, + pVSwitchID); + + HLPR_BAIL; + } + + /// Initial offset is at the MAC Header, so just create a new NET_BUFFER_LIST based on the + /// original NET_BUFFER_LIST ... + pNetBufferList = KrnlHlprNBLCreateNew(g_pNDISPoolData->nblPoolHandle, + (NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket, + &(pCompletionData->pAllocatedBuffer), + &size, + &(pCompletionData->pAllocatedMDL), + pData->additionalBytes, + FALSE); + + if(!pNetBufferList) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformAdvancedPacketInjectionAtEgressVSwitchEthernet: KrnlHlprNBLCreateNew() [pNetBufferList: %#p]\n", + pNetBufferList); + + HLPR_BAIL; + } + +#if DBG + + AdvancedPacketInjectionCountersIncrement(injectionHandle, + &g_apiOutstandingNewNBLs); + +#endif /// DBG + + pCompletionData->refCount = KrnlHlprNBLGetRequiredRefCount(pNetBufferList, + TRUE); + + status = FwpsInjectvSwitchEthernetIngressAsync(pCompletionData->pInjectionData->injectionHandle, + pCompletionData->pInjectionData->injectionContext, + 0, + 0, + pVSwitchID, + sourcePortID, + sourceNICIndex, + pNetBufferList, + CompleteAdvancedPacketInjection, + pCompletionData); + + NT_ASSERT(irql == KeGetCurrentIrql()); + + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformAdvancedPacketInjectionAtEgressVSwitchEthernet: FwpsInjectvSwitchEthernetEgressAsync() [status: %#x]\n", + status); + +#if DBG + + AdvancedPacketInjectionCountersIncrement(injectionHandle, + &g_apiTotalFailedInjectionCalls); + +#endif /// DBG + + } + +#if DBG + + else + AdvancedPacketInjectionCountersIncrement(injectionHandle, + &g_apiTotalSuccessfulInjectionCalls); + +#endif /// DBG + + HLPR_BAIL_LABEL: + + NT_ASSERT(status == STATUS_SUCCESS); + + if(status != STATUS_SUCCESS) + { + if(pNetBufferList) + { + FwpsFreeNetBufferList(pNetBufferList); + + pNetBufferList = 0; + +#if DBG + + AdvancedPacketInjectionCountersDecrement(injectionHandle, + &g_apiOutstandingNewNBLs); + +#endif + + } + + if(pCompletionData) + AdvancedPacketInjectionCompletionDataDestroy(&pCompletionData, + TRUE); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PerformAdvancedPacketInjectionAtEgressVSwitchEthernet() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +#endif // (NTDDI_VERSION >= NTDDI_WIN8) + +/** + @private_function="PerformAdvancedPacketInjectionAtInboundNetwork" + + Purpose: Creates a new NET_BUFFER_LIST and injects it in to the stack's inbound path from + the incoming Network Layers using FwpsInjectNetworkReceiveAsync().
+
+ Notes: Applies to the following inbound layers:
+ FWPM_LAYER_INBOUND_IPPACKET_V{4/6}
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551183.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF546324.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +NTSTATUS PerformAdvancedPacketInjectionAtInboundNetwork(_In_ CLASSIFY_DATA** ppClassifyData, + _In_ INJECTION_DATA** ppInjectionData, + _In_ BOOLEAN isInline = FALSE) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PerformAdvancedPacketInjectionAtInboundNetwork()\n"); + +#endif /// DBG + + NT_ASSERT(ppClassifyData); + NT_ASSERT(ppInjectionData); + NT_ASSERT(*ppClassifyData); + NT_ASSERT(*ppInjectionData); + + NTSTATUS status = STATUS_SUCCESS; + FWPS_INCOMING_VALUES* pClassifyValues = (FWPS_INCOMING_VALUES*)(*ppClassifyData)->pClassifyValues; + FWPS_INCOMING_METADATA_VALUES* pMetadata = (FWPS_INCOMING_METADATA_VALUES*)(*ppClassifyData)->pMetadataValues; + PC_ADVANCED_PACKET_INJECTION_DATA* pData = (PC_ADVANCED_PACKET_INJECTION_DATA*)(*ppClassifyData)->pFilter->providerContext->dataBuffer->data; + COMPARTMENT_ID compartmentID = DEFAULT_COMPARTMENT_ID; + IF_INDEX interfaceIndex = 0; + IF_INDEX subInterfaceIndex = 0; + UINT32 flags = 0; + NET_BUFFER_LIST* pNetBufferList = 0; + UINT32 size = 0; + ADVANCED_PACKET_INJECTION_COMPLETION_DATA* pCompletionData = 0; + UINT32 ipHeaderSize = 0; + UINT32 bytesRetreated = 0; + FWP_VALUE* pInterfaceIndex = 0; + FWP_VALUE* pSubInterfaceIndex = 0; + FWP_VALUE* pFlags = 0; + NDIS_TCP_IP_CHECKSUM_PACKET_INFO checksumInfo = {0}; + +#if DBG + + KIRQL irql = KeGetCurrentIrql(); + HANDLE injectionHandle = (*ppInjectionData)->injectionHandle; + +#endif /// DBG + +#pragma warning(push) +#pragma warning(disable: 6014) /// pCompletionData will be freed in completionFn using AdvancedPacketInjectionCompletionDataDestroy + + HLPR_NEW(pCompletionData, + ADVANCED_PACKET_INJECTION_COMPLETION_DATA, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pCompletionData, + status); + +#pragma warning(pop) + + KeInitializeSpinLock(&(pCompletionData->spinLock)); + + pCompletionData->performedInline = isInline; + pCompletionData->pClassifyData = *ppClassifyData; + pCompletionData->pInjectionData = *ppInjectionData; + + /// Responsibility for freeing this memory has been transferred to the pCompletionData + *ppClassifyData = 0; + + *ppInjectionData = 0; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_COMPARTMENT_ID)) + compartmentID = (COMPARTMENT_ID)pMetadata->compartmentId; + + pInterfaceIndex = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_INTERFACE_INDEX); + if(pInterfaceIndex && + pInterfaceIndex->type == FWP_UINT32) + interfaceIndex = (IF_INDEX)pInterfaceIndex->uint32; + + pSubInterfaceIndex = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_SUB_INTERFACE_INDEX); + if(pSubInterfaceIndex && + pSubInterfaceIndex->type == FWP_UINT32) + subInterfaceIndex = (IF_INDEX)pSubInterfaceIndex->uint32; + + pFlags = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_FLAGS); + if(pFlags && + pFlags->type == FWP_UINT32) + flags = pFlags->uint32; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_IP_HEADER_SIZE)) + bytesRetreated = ipHeaderSize = pMetadata->ipHeaderSize; + + checksumInfo.Value = (ULONG)(ULONG_PTR)NET_BUFFER_LIST_INFO((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket, + TcpIpChecksumNetBufferListInfo); + + /// Initial offset is at the Transport Header, so retreat the size of the IP Header ... + status = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket), + bytesRetreated, + 0, + 0); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformAdvancedPacketInjectionAtInboundNetwork: NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + /// ... create a new NET_BUFFER_LIST based on the original NET_BUFFER_LIST ... + pNetBufferList = KrnlHlprNBLCreateNew(g_pNDISPoolData->nblPoolHandle, + (NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket, + &(pCompletionData->pAllocatedBuffer), + &size, + &(pCompletionData->pAllocatedMDL), + pData->additionalBytes, + FALSE); + + /// ... and advance the offset back to the original position. + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket), + bytesRetreated, + FALSE, + 0); + + if(!pNetBufferList) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformAdvancedPacketInjectionAtInboundNetwork: KrnlHlprNBLCreateNew() [pNetBufferList: %#p]\n", + pNetBufferList); + + HLPR_BAIL; + } + + /// Handle if this packet had the IP checksum offloaded or if it's loopback + if(checksumInfo.Receive.NdisPacketIpChecksumSucceeded || + flags & FWP_CONDITION_FLAG_IS_LOOPBACK) + { + /// Prevent TCP/IP Zone crossing and recalculate the checksums + if(flags & FWP_CONDITION_FLAG_IS_LOOPBACK) + { + FWP_VALUE* pLocalAddress = 0; + FWP_VALUE* pRemoteAddress = 0; + FWP_VALUE* pLoopbackAddress = 0; + + pLocalAddress = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_REMOTE_ADDRESS); + if(pLocalAddress && + ((pLocalAddress->type == FWP_UINT32 && + RtlCompareMemory(&(pLocalAddress->uint32), + IPV4_LOOPBACK_ADDRESS, + IPV4_ADDRESS_SIZE)) || + (pLocalAddress->type == FWP_BYTE_ARRAY16_TYPE && + RtlCompareMemory(pLocalAddress->byteArray16->byteArray16, + IPV6_LOOPBACK_ADDRESS, + IPV6_ADDRESS_SIZE)))) + pLoopbackAddress = pLocalAddress; + + if(!pLoopbackAddress) + { + pRemoteAddress = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_REMOTE_ADDRESS); + if(pRemoteAddress && + ((pRemoteAddress->type == FWP_UINT32 && + RtlCompareMemory(&(pRemoteAddress->uint32), + IPV4_LOOPBACK_ADDRESS, + IPV4_ADDRESS_SIZE)) || + (pRemoteAddress->type == FWP_BYTE_ARRAY16_TYPE && + RtlCompareMemory(pRemoteAddress->byteArray16->byteArray16, + IPV6_LOOPBACK_ADDRESS, + IPV6_ADDRESS_SIZE)))) + pLoopbackAddress = pRemoteAddress; + } + + if(pLoopbackAddress) + { + status = KrnlHlprIPHeaderModifyLoopbackToLocal(pMetadata, + pLoopbackAddress, + ipHeaderSize, + pNetBufferList, + (const WSACMSGHDR*)pCompletionData->pInjectionData->pControlData, + pCompletionData->pInjectionData->controlDataLength); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformAdvancedPacketInjectionAtInboundNetwork: KrnlHlprIPHeaderModifyLoopbackToLocal() [status: %#x]\n", + status); + + HLPR_BAIL; + } + } + } + else + { + /// Recalculate the checksum + if(pCompletionData->pInjectionData->addressFamily == AF_INET) + KrnlHlprIPHeaderCalculateV4Checksum(pNetBufferList, + ipHeaderSize); + } + } + +#if DBG + + AdvancedPacketInjectionCountersIncrement(injectionHandle, + &g_apiOutstandingNewNBLs); + +#endif /// DBG + + pCompletionData->refCount = KrnlHlprNBLGetRequiredRefCount(pNetBufferList); + + /// Handle if this packet is destined for the software loopback + if(flags & FWP_CONDITION_FLAG_IS_LOOPBACK) + { + FWP_VALUE* pLocalAddress = 0; + FWP_VALUE* pRemoteAddress = 0; + FWP_VALUE* pLoopbackAddress = 0; + + pLocalAddress = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_REMOTE_ADDRESS); + if(pLocalAddress && + ((pLocalAddress->type == FWP_UINT32 && + RtlCompareMemory(&(pLocalAddress->uint32), + IPV4_LOOPBACK_ADDRESS, + IPV4_ADDRESS_SIZE)) || + (pLocalAddress->type == FWP_BYTE_ARRAY16_TYPE && + RtlCompareMemory(pLocalAddress->byteArray16->byteArray16, + IPV6_LOOPBACK_ADDRESS, + IPV6_ADDRESS_SIZE)))) + pLoopbackAddress = pLocalAddress; + + if(!pLoopbackAddress) + { + pRemoteAddress = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_REMOTE_ADDRESS); + if(pRemoteAddress && + ((pRemoteAddress->type == FWP_UINT32 && + RtlCompareMemory(&(pRemoteAddress->uint32), + IPV4_LOOPBACK_ADDRESS, + IPV4_ADDRESS_SIZE)) || + (pRemoteAddress->type == FWP_BYTE_ARRAY16_TYPE && + RtlCompareMemory(pRemoteAddress->byteArray16->byteArray16, + IPV6_LOOPBACK_ADDRESS, + IPV6_ADDRESS_SIZE)))) + pLoopbackAddress = pRemoteAddress; + } + + if(pLoopbackAddress) + { + status = KrnlHlprIPHeaderModifyLoopbackToLocal(pMetadata, + pLoopbackAddress, + ipHeaderSize, + pNetBufferList, + (const WSACMSGHDR*)pCompletionData->pInjectionData->pControlData, + pCompletionData->pInjectionData->controlDataLength); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformAdvancedPacketInjectionAtInboundNetwork: KrnlHlprIPHeaderModifyLoopbackToLocal() [status: %#x]\n", + status); + + HLPR_BAIL; + } + } + } + + status = FwpsInjectNetworkReceiveAsync(pCompletionData->pInjectionData->injectionHandle, + pCompletionData->pInjectionData->injectionContext, + 0, + compartmentID, + interfaceIndex, + subInterfaceIndex, + pNetBufferList, + CompleteAdvancedPacketInjection, + pCompletionData); + + NT_ASSERT(irql == KeGetCurrentIrql()); + + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformAdvancedPacketInjectionAtInboundNetwork: FwpsInjectNetworkReceiveAsync() [status: %#x]\n", + status); + +#if DBG + + AdvancedPacketInjectionCountersIncrement(injectionHandle, + &g_apiTotalFailedInjectionCalls); + +#endif /// DBG + + } + +#if DBG + + else + AdvancedPacketInjectionCountersIncrement(injectionHandle, + &g_apiTotalSuccessfulInjectionCalls); + +#endif /// DBG + + HLPR_BAIL_LABEL: + + NT_ASSERT(status == STATUS_SUCCESS); + + if(status != STATUS_SUCCESS) + { + if(pNetBufferList) + { + FwpsFreeNetBufferList(pNetBufferList); + + pNetBufferList = 0; + +#if DBG + + AdvancedPacketInjectionCountersDecrement(injectionHandle, + &g_apiOutstandingNewNBLs); + +#endif + + } + + if(pCompletionData) + AdvancedPacketInjectionCompletionDataDestroy(&pCompletionData, + TRUE); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PerformAdvancedPacketInjectionAtInboundNetwork() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @private_function="PerformAdvancedPacketInjectionAtOutboundNetwork" + + Purpose: Creates a new NET_BUFFER_LIST and injects it in to the stack's outbound path + from the outgoing Network Layers using FwpsInjectNetworkSendAsync().
+
+ Notes: Applies to the following outbound layers:
+ FWPM_LAYER_OUTBOUND_IPPACKET_V{4/6}
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551185.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF546324.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +NTSTATUS PerformAdvancedPacketInjectionAtOutboundNetwork(_In_ CLASSIFY_DATA** ppClassifyData, + _In_ INJECTION_DATA** ppInjectionData, + _In_ BOOLEAN isInline = FALSE) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PerformAdvancedPacketInjectionAtOutboundNetwork()\n"); + +#endif /// DBG + + NT_ASSERT(ppClassifyData); + NT_ASSERT(ppInjectionData); + NT_ASSERT(*ppClassifyData); + NT_ASSERT(*ppInjectionData); + + NTSTATUS status = STATUS_SUCCESS; + FWPS_INCOMING_METADATA_VALUES* pMetadata = (FWPS_INCOMING_METADATA_VALUES*)(*ppClassifyData)->pMetadataValues; + PC_ADVANCED_PACKET_INJECTION_DATA* pData = (PC_ADVANCED_PACKET_INJECTION_DATA*)(*ppClassifyData)->pFilter->providerContext->dataBuffer->data; + COMPARTMENT_ID compartmentID = DEFAULT_COMPARTMENT_ID; + NET_BUFFER_LIST* pNetBufferList = 0; + UINT32 size = 0; + ADVANCED_PACKET_INJECTION_COMPLETION_DATA* pCompletionData = 0; + +#if DBG + + KIRQL irql = KeGetCurrentIrql(); + HANDLE injectionHandle = (*ppInjectionData)->injectionHandle; + +#endif /// DBG + +#pragma warning(push) +#pragma warning(disable: 6014) /// pCompletionData will be freed in completionFn using AdvancedPacketInjectionCompletionDataDestroy + + HLPR_NEW(pCompletionData, + ADVANCED_PACKET_INJECTION_COMPLETION_DATA, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pCompletionData, + status); + +#pragma warning(pop) + + KeInitializeSpinLock(&(pCompletionData->spinLock)); + + pCompletionData->performedInline = isInline; + pCompletionData->pClassifyData = *ppClassifyData; + pCompletionData->pInjectionData = *ppInjectionData; + + /// Responsibility for freeing this memory has been transferred to the pCompletionData + *ppClassifyData = 0; + + *ppInjectionData = 0; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_COMPARTMENT_ID)) + compartmentID = (COMPARTMENT_ID)pMetadata->compartmentId; + + /// Initial offset is at the IP Header, so just create a new NET_BUFFER_LIST based on the + /// original NET_BUFFER_LIST ... + pNetBufferList = KrnlHlprNBLCreateNew(g_pNDISPoolData->nblPoolHandle, + (NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket, + &(pCompletionData->pAllocatedBuffer), + &size, + &(pCompletionData->pAllocatedMDL), + pData->additionalBytes, + FALSE); + + if(!pNetBufferList) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformAdvancedPacketInjectionAtOutboundNetwork: KrnlHlprNBLCreateNew() [pNetBufferList: %#p]\n", + pNetBufferList); + + HLPR_BAIL; + } + +#if DBG + + AdvancedPacketInjectionCountersIncrement(injectionHandle, + &g_apiOutstandingNewNBLs); + +#endif /// DBG + + pCompletionData->refCount = KrnlHlprNBLGetRequiredRefCount(pNetBufferList); + + status = FwpsInjectNetworkSendAsync(pCompletionData->pInjectionData->injectionHandle, + pCompletionData->pInjectionData->injectionContext, + 0, + compartmentID, + pNetBufferList, + CompleteAdvancedPacketInjection, + pCompletionData); + + NT_ASSERT(irql == KeGetCurrentIrql()); + + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformAdvancedPacketInjectionAtOutboundNetwork: FwpsInjectNetworkSendAsync() [status: %#x]\n", + status); + +#if DBG + + AdvancedPacketInjectionCountersIncrement(injectionHandle, + &g_apiTotalFailedInjectionCalls); + +#endif /// DBG + + } + +#if DBG + + else + AdvancedPacketInjectionCountersIncrement(injectionHandle, + &g_apiTotalSuccessfulInjectionCalls); + +#endif /// DBG + + HLPR_BAIL_LABEL: + + NT_ASSERT(status == STATUS_SUCCESS); + + if(status != STATUS_SUCCESS) + { + if(pNetBufferList) + { + FwpsFreeNetBufferList(pNetBufferList); + + pNetBufferList = 0; + +#if DBG + + AdvancedPacketInjectionCountersDecrement(injectionHandle, + &g_apiOutstandingNewNBLs); + +#endif + + } + + if(pCompletionData) + AdvancedPacketInjectionCompletionDataDestroy(&pCompletionData, + TRUE); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PerformAdvancedPacketInjectionAtOutboundNetwork() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @private_function="PerformAdvancedPacketInjectionAtForward" + + Purpose: Creates a new NET_BUFFER_LIST and injects it in to the stack's forward path using + FwpsInjectForwardAsync().
+
+ Notes: Applies to the following forwarding layers:
+ FWPM_LAYER_IPFORWARD_V{4/6}
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551186.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF546324.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +NTSTATUS PerformAdvancedPacketInjectionAtForward(_In_ CLASSIFY_DATA** ppClassifyData, + _In_ INJECTION_DATA** ppInjectionData, + _In_ BOOLEAN isInline = FALSE) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PerformAdvancedPacketInjectionAtForward()\n"); + +#endif /// DBG + + NT_ASSERT(ppClassifyData); + NT_ASSERT(ppInjectionData); + NT_ASSERT(*ppClassifyData); + NT_ASSERT(*ppInjectionData); + + NTSTATUS status = STATUS_SUCCESS; + FWPS_INCOMING_VALUES* pClassifyValues = (FWPS_INCOMING_VALUES*)(*ppClassifyData)->pClassifyValues; + FWPS_INCOMING_METADATA_VALUES* pMetadata = (FWPS_INCOMING_METADATA_VALUES*)(*ppClassifyData)->pMetadataValues; + PC_ADVANCED_PACKET_INJECTION_DATA* pData = (PC_ADVANCED_PACKET_INJECTION_DATA*)(*ppClassifyData)->pFilter->providerContext->dataBuffer->data; + COMPARTMENT_ID compartmentID = DEFAULT_COMPARTMENT_ID; + IF_INDEX interfaceIndex = 0; + UINT32 flags = 0; + NET_BUFFER_LIST* pNetBufferList = 0; + UINT32 size = 0; + ADVANCED_PACKET_INJECTION_COMPLETION_DATA* pCompletionData = 0; + UINT32 ipHeaderSize = 0; + FWP_VALUE* pInterfaceIndex = 0; + FWP_VALUE* pFlags = 0; + BOOLEAN isWeakHostReceive = FALSE; + BOOLEAN isWeakHostSend = FALSE; + PSTR pInjectionFn = "FwpsInjectForwardAsync"; + NDIS_TCP_IP_CHECKSUM_PACKET_INFO checksumInfo = {0}; + +#if DBG + + KIRQL irql = KeGetCurrentIrql(); + HANDLE injectionHandle = (*ppInjectionData)->injectionHandle; + +#endif /// DBG + +#pragma warning(push) +#pragma warning(disable: 6014) /// pCompletionData will be freed in completionFn using AdvancedPacketInjectionCompletionDataDestroy + + HLPR_NEW(pCompletionData, + ADVANCED_PACKET_INJECTION_COMPLETION_DATA, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pCompletionData, + status); + +#pragma warning(pop) + + KeInitializeSpinLock(&(pCompletionData->spinLock)); + + pCompletionData->performedInline = isInline; + pCompletionData->pClassifyData = *ppClassifyData; + pCompletionData->pInjectionData = *ppInjectionData; + + /// Responsibility for freeing this memory has been transferred to the pCompletionData + *ppClassifyData = 0; + + *ppInjectionData = 0; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_COMPARTMENT_ID)) + compartmentID = (COMPARTMENT_ID)pMetadata->compartmentId; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_IP_HEADER_SIZE)) + ipHeaderSize = pMetadata->ipHeaderSize; + + pInterfaceIndex = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_DESTINATION_INTERFACE_INDEX); + if(pInterfaceIndex && + pInterfaceIndex->type == FWP_UINT32) + interfaceIndex = (IF_INDEX)pInterfaceIndex->uint32; + + pFlags = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_FLAGS); + if(pFlags && + pFlags->type == FWP_UINT32) + flags = pFlags->uint32; + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + /// Determine if this is a weakhost forward + if(flags & FWP_CONDITION_FLAG_IS_INBOUND_PASS_THRU) + isWeakHostReceive = TRUE; + + if(flags & FWP_CONDITION_FLAG_IS_OUTBOUND_PASS_THRU) + isWeakHostSend = TRUE; + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + /// Initial offset is at the IP Header, so just create a new NET_BUFFER_LIST based on the + /// original NET_BUFFER_LIST ... + pNetBufferList = KrnlHlprNBLCreateNew(g_pNDISPoolData->nblPoolHandle, + (NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket, + &(pCompletionData->pAllocatedBuffer), + &size, + &(pCompletionData->pAllocatedMDL), + pData->additionalBytes, + FALSE); + + if(!pNetBufferList) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformAdvancedPacketInjectionAtForward: KrnlHlprNBLCreateNew() [pNetBufferList: %#p]\n", + pNetBufferList); + + HLPR_BAIL; + } +#if DBG + + AdvancedPacketInjectionCountersIncrement(injectionHandle, + &g_apiOutstandingNewNBLs); + +#endif /// DBG + + checksumInfo.Value = (ULONG)(ULONG_PTR)NET_BUFFER_LIST_INFO((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket, + TcpIpChecksumNetBufferListInfo); + + /// Handle if this packet had the IP checksum offloaded or if it's loopback + if(checksumInfo.Receive.NdisPacketIpChecksumSucceeded || + flags & FWP_CONDITION_FLAG_IS_LOOPBACK) + { + /// Prevent TCP/IP Zone crossing and recalculate the checksums + if(flags & FWP_CONDITION_FLAG_IS_LOOPBACK) + { + FWP_VALUE* pLocalAddress = 0; + FWP_VALUE* pRemoteAddress = 0; + FWP_VALUE* pLoopbackAddress = 0; + + pLocalAddress = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_REMOTE_ADDRESS); + if(pLocalAddress && + ((pLocalAddress->type == FWP_UINT32 && + RtlCompareMemory(&(pLocalAddress->uint32), + IPV4_LOOPBACK_ADDRESS, + IPV4_ADDRESS_SIZE)) || + (pLocalAddress->type == FWP_BYTE_ARRAY16_TYPE && + RtlCompareMemory(pLocalAddress->byteArray16->byteArray16, + IPV6_LOOPBACK_ADDRESS, + IPV6_ADDRESS_SIZE)))) + pLoopbackAddress = pLocalAddress; + + if(!pLoopbackAddress) + { + pRemoteAddress = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_REMOTE_ADDRESS); + if(pRemoteAddress && + ((pRemoteAddress->type == FWP_UINT32 && + RtlCompareMemory(&(pRemoteAddress->uint32), + IPV4_LOOPBACK_ADDRESS, + IPV4_ADDRESS_SIZE)) || + (pRemoteAddress->type == FWP_BYTE_ARRAY16_TYPE && + RtlCompareMemory(pRemoteAddress->byteArray16->byteArray16, + IPV6_LOOPBACK_ADDRESS, + IPV6_ADDRESS_SIZE)))) + pLoopbackAddress = pRemoteAddress; + } + + if(pLoopbackAddress) + { + status = KrnlHlprIPHeaderModifyLoopbackToLocal(pMetadata, + pLoopbackAddress, + ipHeaderSize, + pNetBufferList, + (const WSACMSGHDR*)pCompletionData->pInjectionData->pControlData, + pCompletionData->pInjectionData->controlDataLength); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformAdvancedPacketInjectionAtForward: KrnlHlprIPHeaderModifyLoopbackToLocal() [status: %#x]\n", + status); + + HLPR_BAIL; + } + } + } + else + { + /// Recalculate the checksum + if(pCompletionData->pInjectionData->addressFamily == AF_INET) + KrnlHlprIPHeaderCalculateV4Checksum(pNetBufferList, + ipHeaderSize); + } + } + + pCompletionData->refCount = KrnlHlprNBLGetRequiredRefCount(pNetBufferList); + + /// If the Forwarded NBL is destined locally, inject using FwpsInjectNetworkReceiveAsync rather + /// than the traditional FwpsInjectForwardAsync otherwise STATUS_INVALID_PARAMETER will be + /// returned in the NBL.status and the injection fails. + if(isWeakHostReceive) + { + UINT32 index = WFPSAMPLER_INDEX; + IF_INDEX subInterfaceIndex = 0; + FWP_VALUE* pSubInterfaceIndex = 0; + + if(pCompletionData->pClassifyData->pFilter->subLayerWeight == FWPM_SUBLAYER_UNIVERSAL_WEIGHT) + index = UNIVERSAL_INDEX; + +#if DBG + + if(injectionHandle == g_pIPv4InboundForwardInjectionHandles[0] || + injectionHandle == g_pIPv4InboundForwardInjectionHandles[1]) + InterlockedDecrement64((LONG64*)&(g_apiOutstandingNewNBLs.inboundForward_IPv4)); + else if(injectionHandle == g_pIPv6InboundForwardInjectionHandles[0] || + injectionHandle == g_pIPv6InboundForwardInjectionHandles[1]) + InterlockedDecrement64((LONG64*)&(g_apiOutstandingNewNBLs.inboundForward_IPv6)); + +#endif /// DBG + + if(pCompletionData->pInjectionData->addressFamily == AF_INET) + pCompletionData->pInjectionData->injectionHandle = g_pIPv4InboundNetworkInjectionHandles[index]; + else + pCompletionData->pInjectionData->injectionHandle = g_pIPv6InboundNetworkInjectionHandles[index]; + + pSubInterfaceIndex = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_DESTINATION_SUB_INTERFACE_INDEX); + if(pSubInterfaceIndex && + pSubInterfaceIndex->type == FWP_UINT32) + subInterfaceIndex = (IF_INDEX)pSubInterfaceIndex->uint32; + +#if DBG + + AdvancedPacketInjectionCountersIncrement(injectionHandle, + &g_apiOutstandingNewNBLs); + +#endif /// DBG + + status = FwpsInjectNetworkReceiveAsync(pCompletionData->pInjectionData->injectionHandle, + pCompletionData->pInjectionData->injectionContext, + 0, + compartmentID, + interfaceIndex, + subInterfaceIndex, + pNetBufferList, + CompleteAdvancedPacketInjection, + pCompletionData); + } + /// If the Forwarded NBL is sourced locally, but another interface, inject using + /// FwpsInjectNetworkSendAsync rather than the traditional FwpsInjectForwardAsync otherwise + /// STATUS_INVALID_PARAMETER will be returned in the NBL.status and the injection fails + else if(isWeakHostSend) + { + UINT32 index = WFPSAMPLER_INDEX; + + if(pCompletionData->pClassifyData->pFilter->subLayerWeight == FWPM_SUBLAYER_UNIVERSAL_WEIGHT) + index = UNIVERSAL_INDEX; + +#if DBG + + if(injectionHandle == g_pIPv4OutboundForwardInjectionHandles[0] || + injectionHandle == g_pIPv4OutboundForwardInjectionHandles[1]) + InterlockedDecrement64((LONG64*)&(g_apiOutstandingNewNBLs.outboundForward_IPv4)); + else if(injectionHandle == g_pIPv6OutboundForwardInjectionHandles[0] || + injectionHandle == g_pIPv6OutboundForwardInjectionHandles[1]) + InterlockedDecrement64((LONG64*)&(g_apiOutstandingNewNBLs.outboundForward_IPv6)); + +#endif /// DBG + + if(pCompletionData->pInjectionData->addressFamily == AF_INET) + pCompletionData->pInjectionData->injectionHandle = g_pIPv4OutboundNetworkInjectionHandles[index]; + else + pCompletionData->pInjectionData->injectionHandle = g_pIPv6OutboundNetworkInjectionHandles[index]; + +#if DBG + + AdvancedPacketInjectionCountersIncrement(injectionHandle, + &g_apiOutstandingNewNBLs); + +#endif /// DBG + + status = FwpsInjectNetworkSendAsync(pCompletionData->pInjectionData->injectionHandle, + pCompletionData->pInjectionData->injectionContext, + 0, + compartmentID, + pNetBufferList, + CompleteAdvancedPacketInjection, + pCompletionData); + } + else + status = FwpsInjectForwardAsync(pCompletionData->pInjectionData->injectionHandle, + pCompletionData->pInjectionData->injectionContext, + 0, + pCompletionData->pInjectionData->addressFamily, + compartmentID, + interfaceIndex, + pNetBufferList, + CompleteAdvancedPacketInjection, + pCompletionData); + + NT_ASSERT(irql == KeGetCurrentIrql()); + + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformAdvancedPacketInjectionAtForward: %s() [status: %#x]\n", + pInjectionFn, + status); + +#if DBG + + AdvancedPacketInjectionCountersIncrement(injectionHandle, + &g_apiTotalFailedInjectionCalls); + +#endif /// DBG + + } + +#if DBG + + else + AdvancedPacketInjectionCountersIncrement(injectionHandle, + &g_apiTotalSuccessfulInjectionCalls); + +#endif /// DBG + + HLPR_BAIL_LABEL: + + NT_ASSERT(status == STATUS_SUCCESS); + + if(status != STATUS_SUCCESS) + { + if(pNetBufferList) + { + FwpsFreeNetBufferList(pNetBufferList); + + pNetBufferList = 0; + +#if DBG + + AdvancedPacketInjectionCountersDecrement(injectionHandle, + &g_apiOutstandingNewNBLs); + +#endif + + } + + if(pCompletionData) + AdvancedPacketInjectionCompletionDataDestroy(&pCompletionData, + TRUE); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PerformAdvancedPacketInjectionAtForward() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @private_function="PerformAdvancedPacketInjectionAtInboundTransport" + + Purpose: Creates a new NET_BUFFER_LIST and injects it in to the stack's inbound path from + the incoming Transport Layers using FwpsInjectTransportRecveiveAsync().
+
+ Notes: Applies to the following inbound layers:
+ FWPM_LAYER_INBOUND_TRANSPORT_V{4/6}
+ FWPM_LAYER_INBOUND_ICMP_ERROR_V{4/6}
+ FWPM_LAYER_DATAGRAM_DATA_V{4/6} (Inbound only)
+ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V{4/6} (Inbound only)
+ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V{4/6} (Inbound only)
+ FWPM_LAYER_ALE_AUTH_CONNECT_V{4/6} (Inbound, reauthorization only)
+ FWPM_LAYER_ALE_FLOW_ESTABLISHED_V{4/6} (Inbound, non-TCP only)
+ FWPM_LAYER_STREAM_PACKET_V{4/6} (Inbound only)
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551186.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF546324.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +NTSTATUS PerformAdvancedPacketInjectionAtInboundTransport(_In_ CLASSIFY_DATA** ppClassifyData, + _In_ INJECTION_DATA** ppInjectionData, + _In_ BOOLEAN isInline = FALSE) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PerformAdvancedPacketInjectionAtInboundTransport()\n"); + +#endif /// DBG + + NT_ASSERT(ppClassifyData); + NT_ASSERT(ppInjectionData); + NT_ASSERT(*ppClassifyData); + NT_ASSERT(*ppInjectionData); + + NTSTATUS status = STATUS_SUCCESS; + FWPS_INCOMING_VALUES* pClassifyValues = (FWPS_INCOMING_VALUES*)(*ppClassifyData)->pClassifyValues; + FWPS_INCOMING_METADATA_VALUES* pMetadata = (FWPS_INCOMING_METADATA_VALUES*)(*ppClassifyData)->pMetadataValues; + PC_ADVANCED_PACKET_INJECTION_DATA* pData = (PC_ADVANCED_PACKET_INJECTION_DATA*)(*ppClassifyData)->pFilter->providerContext->dataBuffer->data; + COMPARTMENT_ID compartmentID = DEFAULT_COMPARTMENT_ID; + IF_INDEX interfaceIndex = 0; + IF_INDEX subInterfaceIndex = 0; + UINT32 flags = 0; + NET_BUFFER_LIST* pNetBufferList = 0; + UINT32 size = 0; + ADVANCED_PACKET_INJECTION_COMPLETION_DATA* pCompletionData = 0; + UINT32 ipHeaderSize = 0; + UINT32 transportHeaderSize = 0; + UINT32 bytesRetreated = 0; + IPPROTO protocol = IPPROTO_MAX; + FWP_VALUE* pProtocol = 0; + FWP_VALUE* pInterfaceIndex = 0; + FWP_VALUE* pSubInterfaceIndex = 0; + FWP_VALUE* pFlags = 0; + FWPS_PACKET_LIST_INFORMATION* pPacketInformation = 0; + BOOLEAN bypassInjection = FALSE; + BYTE* pSourceAddress = 0; + BYTE* pDestinationAddress = 0; + NDIS_TCP_IP_CHECKSUM_PACKET_INFO checksumInfo = {0}; + +#if DBG + + KIRQL irql = KeGetCurrentIrql(); + HANDLE injectionHandle = (*ppInjectionData)->injectionHandle; + +#endif /// DBG + +#pragma warning(push) +#pragma warning(disable: 6014) /// pCompletionData will be freed in completionFn using AdvancedPacketInjectionCompletionDataDestroy + + HLPR_NEW(pCompletionData, + ADVANCED_PACKET_INJECTION_COMPLETION_DATA, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pCompletionData, + status); + +#pragma warning(pop) + + KeInitializeSpinLock(&(pCompletionData->spinLock)); + + pCompletionData->performedInline = isInline; + pCompletionData->pClassifyData = *ppClassifyData; + pCompletionData->pInjectionData = *ppInjectionData; + + /// Responsibility for freeing this memory has been transferred to the pCompletionData + *ppClassifyData = 0; + + *ppInjectionData = 0; + + HLPR_NEW(pPacketInformation, + FWPS_PACKET_LIST_INFORMATION, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pPacketInformation, + status); + pInterfaceIndex = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_INTERFACE_INDEX); + if(pInterfaceIndex && + pInterfaceIndex->type == FWP_UINT32) + interfaceIndex = (IF_INDEX)pInterfaceIndex->uint32; + + pSubInterfaceIndex = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_SUB_INTERFACE_INDEX); + if(pSubInterfaceIndex && + pSubInterfaceIndex->type == FWP_UINT32) + subInterfaceIndex = (IF_INDEX)pSubInterfaceIndex->uint32; + + pFlags = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_FLAGS); + if(pFlags && + pFlags->type == FWP_UINT32) + flags = pFlags->uint32; + + if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4) + protocol = IPPROTO_ICMP; + else if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6) + protocol = IPPROTO_ICMPV6; + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + else if(pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V6) + protocol = IPPROTO_TCP; + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + else + { + pProtocol = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_PROTOCOL); + HLPR_BAIL_ON_NULL_POINTER(pProtocol); + + protocol = (IPPROTO)pProtocol->uint8; + } + + if(pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4) + { + ipHeaderSize = IPV4_HEADER_MIN_SIZE; + + if(protocol == IPPROTO_ICMP) + transportHeaderSize = ICMP_HEADER_MIN_SIZE; + else if(protocol == IPPROTO_TCP) + transportHeaderSize = TCP_HEADER_MIN_SIZE; + else if(protocol == IPPROTO_UDP) + transportHeaderSize = UDP_HEADER_MIN_SIZE; + } + else if(pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6) + { + ipHeaderSize = IPV6_HEADER_MIN_SIZE; + + if(protocol == IPPROTO_ICMPV6) + transportHeaderSize = ICMP_HEADER_MIN_SIZE; + else if(protocol == IPPROTO_TCP) + transportHeaderSize = TCP_HEADER_MIN_SIZE; + else if(protocol == IPPROTO_UDP) + transportHeaderSize = UDP_HEADER_MIN_SIZE; + } + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_COMPARTMENT_ID)) + compartmentID = (COMPARTMENT_ID)pMetadata->compartmentId; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_IP_HEADER_SIZE) && + pMetadata->ipHeaderSize) + ipHeaderSize = pMetadata->ipHeaderSize; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE) && + pMetadata->transportHeaderSize) + transportHeaderSize = pMetadata->transportHeaderSize; + + bytesRetreated = ipHeaderSize; + + if(protocol != IPPROTO_ICMP && + protocol != IPPROTO_ICMPV6) + { + if(!isInline && + protocol != IPPROTO_TCP && + !(protocol == IPPROTO_UDP && + flags & FWP_CONDITION_FLAG_IS_RAW_ENDPOINT) && + (pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6)) + { + /// For asynchronous execution, the drop will cause the stack to continue processing on the + /// NBL for auditing purposes. This processing retreats the NBL Offset to the Transport header. + /// We need to take this into account because we only took a reference on the NBL. + } + else + bytesRetreated += transportHeaderSize; + } + else + { + if(pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4 || + pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V6) + { + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + bytesRetreated += pMetadata->transportHeaderSize; + } + } + + /// Query to see if IPsec has applied tunnel mode SA's to this NET_BUFFER_LIST ... + status = FwpsGetPacketListSecurityInformation((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket, + FWPS_PACKET_LIST_INFORMATION_QUERY_ALL_INBOUND, + pPacketInformation); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformAdvancedPacketInjectionAtInboundTransport: FwpsGetPacketListSecurityInformation() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + /// ... if it has, then bypass the injection until the NET_BUFFER_LIST has come out of the tunnel + if((pPacketInformation->ipsecInformation.inbound.isTunnelMode && + !(pPacketInformation->ipsecInformation.inbound.isDeTunneled)) || + pPacketInformation->ipsecInformation.inbound.isSecure) + { + bypassInjection = TRUE; + + HLPR_BAIL; + } + + /// Initial offset is at the data, so retreat the size of the IP Header and Transport Header ... + /// For ICMP, offset is at the ICMP Header, so retreat the size of the IP Header ... + status = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket), + bytesRetreated, + 0, + 0); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformAdvancedPacketInjectionAtInboundTransport: NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + /// ... create a new NET_BUFFER_LIST based on the original NET_BUFFER_LIST ... + pNetBufferList = KrnlHlprNBLCreateNew(g_pNDISPoolData->nblPoolHandle, + (NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket, + &(pCompletionData->pAllocatedBuffer), + &size, + &(pCompletionData->pAllocatedMDL), + pData->additionalBytes, + FALSE); + + /// ... and advance the offset back to the original position. + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket), + bytesRetreated, + FALSE, + 0); + + if(!pNetBufferList) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformAdvancedPacketInjectionAtInboundTransport: KrnlHlprNBLCreateNew() [pNetBufferList: %#p]\n", + pNetBufferList); + + HLPR_BAIL; + } + +#if DBG + + AdvancedPacketInjectionCountersIncrement(injectionHandle, + &g_apiOutstandingNewNBLs); + +#endif /// DBG + + checksumInfo.Value = (ULONG)(ULONG_PTR)NET_BUFFER_LIST_INFO((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket, + TcpIpChecksumNetBufferListInfo); + + /// Handle if the packet was IPsec secured + if(pCompletionData->pInjectionData->isIPsecSecured) + { + /// For performance reasons, IPsec leaves the original ESP / AH information in the IP Header ... + UINT32 headerIncludeSize = 0; + UINT64 endpointHandle = 0; + UINT32 ipv4Address = 0; + UINT32 addressSize = 0; + FWP_VALUE* pRemoteAddressValue = 0; + FWP_VALUE* pLocalAddressValue = 0; + FWP_VALUE* pProtocolValue = 0; + + pRemoteAddressValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_REMOTE_ADDRESS); + if(pRemoteAddressValue) + { + if(pRemoteAddressValue->type == FWP_BYTE_ARRAY16_TYPE) + addressSize = IPV6_ADDRESS_SIZE; + else + addressSize = IPV4_ADDRESS_SIZE; + + HLPR_NEW_ARRAY(pSourceAddress, + BYTE, + addressSize, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pSourceAddress, + status); + + if(pRemoteAddressValue->type == FWP_BYTE_ARRAY16_TYPE) + RtlCopyMemory(pSourceAddress, + pRemoteAddressValue->byteArray16->byteArray16, + addressSize); + else + { + ipv4Address = htonl(pRemoteAddressValue->uint32); + + RtlCopyMemory(pSourceAddress, + &ipv4Address, + addressSize); + } + } + + pLocalAddressValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_LOCAL_ADDRESS); + if(pLocalAddressValue) + { + if(pLocalAddressValue->type == FWP_BYTE_ARRAY16_TYPE) + addressSize = IPV6_ADDRESS_SIZE; + else + addressSize = IPV4_ADDRESS_SIZE; + + HLPR_NEW_ARRAY(pDestinationAddress, + BYTE, + addressSize, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pDestinationAddress, + status); + + if(pLocalAddressValue->type == FWP_BYTE_ARRAY16_TYPE) + RtlCopyMemory(pDestinationAddress, + pLocalAddressValue->byteArray16->byteArray16, + addressSize); + else + { + ipv4Address = htonl(pLocalAddressValue->uint32); + + RtlCopyMemory(pDestinationAddress, + &ipv4Address, + addressSize); + } + } + + pProtocolValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_PROTOCOL); + if(pProtocolValue && + pProtocolValue->type == FWP_UINT8) + protocol = (IPPROTO)pProtocolValue->uint8; + else + protocol = IPPROTO_MAX; + + NT_ASSERT(protocol != IPPROTO_MAX); + +#if (NTDDI_VERSION >= NTDDI_WIN6SP1) + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_INCLUDE_HEADER)) + headerIncludeSize = pMetadata->headerIncludeHeaderLength; + +#endif // (NTDDI_VERSION >= NTDDI_WIN6SP1) + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_ENDPOINT_HANDLE)) + endpointHandle = pMetadata->transportEndpointHandle; + + if(pSourceAddress == 0 || + pDestinationAddress == 0) + { + status = STATUS_INVALID_MEMBER; + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformAdvancedPacketModificationAtInboundTransport() [status: %#x][pSourceAddress: %#p][pDestinationAddress: %#p]\n", + status, + pSourceAddress, + pDestinationAddress); + + HLPR_BAIL; + } + + /// ... so we must re-construct the IPHeader with the appropriate information + status = FwpsConstructIpHeaderForTransportPacket(pNetBufferList, + headerIncludeSize, + pCompletionData->pInjectionData->addressFamily, + pSourceAddress, + pDestinationAddress, + protocol, + endpointHandle, + (const WSACMSGHDR*)pCompletionData->pInjectionData->pControlData, + pCompletionData->pInjectionData->controlDataLength, + 0, + 0, + interfaceIndex, + subInterfaceIndex); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformAdvancedPacketInjectionAtInboundTransport: FwpsConstructIpHeaderForTransportPacket() [status: %#x]\n", + status); + + HLPR_BAIL; + } + } + /// Handle if this packet had the IP or Transport checksums offloaded or if it's loopback + else if(checksumInfo.Receive.NdisPacketIpChecksumSucceeded || + checksumInfo.Receive.NdisPacketTcpChecksumSucceeded || + checksumInfo.Receive.NdisPacketUdpChecksumSucceeded || + flags & FWP_CONDITION_FLAG_IS_LOOPBACK) + { + /// Prevent TCP/IP Zone crossing and recalculate the checksums + if(flags & FWP_CONDITION_FLAG_IS_LOOPBACK) + { + FWP_VALUE* pLocalAddress = 0; + FWP_VALUE* pRemoteAddress = 0; + FWP_VALUE* pLoopbackAddress = 0; + + pLocalAddress = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_REMOTE_ADDRESS); + if(pLocalAddress && + ((pLocalAddress->type == FWP_UINT32 && + RtlCompareMemory(&(pLocalAddress->uint32), + IPV4_LOOPBACK_ADDRESS, + IPV4_ADDRESS_SIZE)) || + (pLocalAddress->type == FWP_BYTE_ARRAY16_TYPE && + RtlCompareMemory(pLocalAddress->byteArray16->byteArray16, + IPV6_LOOPBACK_ADDRESS, + IPV6_ADDRESS_SIZE)))) + pLoopbackAddress = pLocalAddress; + + if(!pLoopbackAddress) + { + pRemoteAddress = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_REMOTE_ADDRESS); + if(pRemoteAddress && + ((pRemoteAddress->type == FWP_UINT32 && + RtlCompareMemory(&(pRemoteAddress->uint32), + IPV4_LOOPBACK_ADDRESS, + IPV4_ADDRESS_SIZE)) || + (pRemoteAddress->type == FWP_BYTE_ARRAY16_TYPE && + RtlCompareMemory(pRemoteAddress->byteArray16->byteArray16, + IPV6_LOOPBACK_ADDRESS, + IPV6_ADDRESS_SIZE)))) + pLoopbackAddress = pRemoteAddress; + } + + if(pLoopbackAddress) + { + status = KrnlHlprIPHeaderModifyLoopbackToLocal(pMetadata, + pLoopbackAddress, + ipHeaderSize, + pNetBufferList, + (const WSACMSGHDR*)pCompletionData->pInjectionData->pControlData, + pCompletionData->pInjectionData->controlDataLength); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformAdvancedPacketInjectionAtInboundTransport: KrnlHlprIPHeaderModifyLoopbackToLocal() [status: %#x]\n", + status); + + HLPR_BAIL; + } + } + } + else + { + /// Recalculate the checksum + if(pCompletionData->pInjectionData->addressFamily == AF_INET) + KrnlHlprIPHeaderCalculateV4Checksum(pNetBufferList, + ipHeaderSize); + } + } + + pCompletionData->refCount = KrnlHlprNBLGetRequiredRefCount(pNetBufferList); + + status = FwpsInjectTransportReceiveAsync(pCompletionData->pInjectionData->injectionHandle, + pCompletionData->pInjectionData->injectionContext, + 0, + 0, + pCompletionData->pInjectionData->addressFamily, + compartmentID, + interfaceIndex, + subInterfaceIndex, + pNetBufferList, + CompleteAdvancedPacketInjection, + pCompletionData); + + NT_ASSERT(irql == KeGetCurrentIrql()); + + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformAdvancedPacketInjectionAtInboundTransport: FwpsInjectTransportReceiveAsync() [status: %#x]\n", + status); + +#if DBG + + AdvancedPacketInjectionCountersIncrement(injectionHandle, + &g_apiTotalFailedInjectionCalls); + +#endif /// DBG + + } + +#if DBG + + else + AdvancedPacketInjectionCountersIncrement(injectionHandle, + &g_apiTotalSuccessfulInjectionCalls); + +#endif /// DBG + + HLPR_BAIL_LABEL: + + NT_ASSERT(status == STATUS_SUCCESS); + + if(status != STATUS_SUCCESS || + bypassInjection) + { + if(pNetBufferList) + { + FwpsFreeNetBufferList(pNetBufferList); + + pNetBufferList = 0; + +#if DBG + + AdvancedPacketInjectionCountersDecrement(injectionHandle, + &g_apiOutstandingNewNBLs); + +#endif + + } + + if(pCompletionData) + AdvancedPacketInjectionCompletionDataDestroy(&pCompletionData, + TRUE); + } + + HLPR_DELETE_ARRAY(pSourceAddress, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + + HLPR_DELETE_ARRAY(pDestinationAddress, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + + HLPR_DELETE(pPacketInformation, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PerformAdvancedPacketInjectionAtInboundTransport() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @private_function="PerformAdvancedPacketInjectionAtOutboundTransport" + + Purpose: Creates a new NET_BUFFER_LIST and injects it in to the stack's outbound path from + the outgoing Transport Layers using FwpsInjectTransportSendAsync().
+
+ Notes: Applies to the following outbound layers:
+ FWPM_LAYER_OUTBOUND_TRANSPORT_V{4/6}
+ FWPM_LAYER_OUTBOUND_ICMP_ERROR_V{4/6}
+ FWPM_LAYER_DATAGRAM_DATA_V{4/6} (Outbound only)
+ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V{4/6} (Outbound only)
+ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V{4/6} (Outbound only)
+ FWPM_LAYER_ALE_AUTH_CONNECT_V{4/6} (Outbound reauthorization only)
+ FWPM_LAYER_ALE_FLOW_ESTABLISHED_V{4/6} (Outbound, non-TCP only)
+ FWPM_LAYER_STREAM_PACKET_V{4/6} (Outbound only)
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551188.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF546324.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +NTSTATUS PerformAdvancedPacketInjectionAtOutboundTransport(_In_ CLASSIFY_DATA** ppClassifyData, + _In_ INJECTION_DATA** ppInjectionData, + _In_ BOOLEAN isInline = FALSE) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PerformAdvancedPacketInjectionAtOutboundTransport()\n"); + +#endif /// DBG + + NT_ASSERT(ppClassifyData); + NT_ASSERT(ppInjectionData); + NT_ASSERT(*ppClassifyData); + NT_ASSERT(*ppInjectionData); + + NTSTATUS status = STATUS_SUCCESS; + FWPS_INCOMING_VALUES* pClassifyValues = (FWPS_INCOMING_VALUES*)(*ppClassifyData)->pClassifyValues; + FWPS_INCOMING_METADATA_VALUES* pMetadata = (FWPS_INCOMING_METADATA_VALUES*)(*ppClassifyData)->pMetadataValues; + PC_ADVANCED_PACKET_INJECTION_DATA* pData = (PC_ADVANCED_PACKET_INJECTION_DATA*)(*ppClassifyData)->pFilter->providerContext->dataBuffer->data; + UINT64 endpointHandle = 0; + FWPS_TRANSPORT_SEND_PARAMS* pSendParams = 0; + COMPARTMENT_ID compartmentID = DEFAULT_COMPARTMENT_ID; + NET_BUFFER_LIST* pNetBufferList = 0; + UINT32 size = 0; + ADVANCED_PACKET_INJECTION_COMPLETION_DATA* pCompletionData = 0; + BYTE* pRemoteAddress = 0; + FWP_VALUE* pAddressValue = 0; + +#if DBG + + KIRQL irql = KeGetCurrentIrql(); + HANDLE injectionHandle = (*ppInjectionData)->injectionHandle; + +#endif /// DBG + +#pragma warning(push) +#pragma warning(disable: 6014) /// pCompletionData & pSendParams will be freed in completionFn using AdvancedPacketInjectionCompletionDataDestroy + + HLPR_NEW(pCompletionData, + ADVANCED_PACKET_INJECTION_COMPLETION_DATA, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pCompletionData, + status); + + HLPR_NEW(pSendParams, + FWPS_TRANSPORT_SEND_PARAMS, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pSendParams, + status); + +#pragma warning(pop) + + KeInitializeSpinLock(&(pCompletionData->spinLock)); + + pCompletionData->performedInline = isInline; + pCompletionData->pClassifyData = *ppClassifyData; + pCompletionData->pInjectionData = *ppInjectionData; + pCompletionData->pSendParams = pSendParams; + + /// Responsibility for freeing this memory has been transferred to the pCompletionData + *ppClassifyData = 0; + + *ppInjectionData = 0; + + pSendParams = 0; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_ENDPOINT_HANDLE)) + endpointHandle = pMetadata->transportEndpointHandle; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_COMPARTMENT_ID)) + compartmentID = (COMPARTMENT_ID)pMetadata->compartmentId; + + pAddressValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_REMOTE_ADDRESS); + if(pAddressValue) + { + if(pCompletionData->pInjectionData->addressFamily == AF_INET) + { + UINT32 tempAddress = htonl(pAddressValue->uint32); + + HLPR_NEW_ARRAY(pRemoteAddress, + BYTE, + IPV4_ADDRESS_SIZE, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pRemoteAddress, + status); + + RtlCopyMemory(pRemoteAddress, + &tempAddress, + IPV4_ADDRESS_SIZE); + } + else + { +#pragma warning(push) +#pragma warning(disable: 6014) /// pRemoteAddress will be freed in completionFn using AdvancedPacketInjectionCompletionDataDestroy + + HLPR_NEW_ARRAY(pRemoteAddress, + BYTE, + IPV6_ADDRESS_SIZE, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pRemoteAddress, + status); + +#pragma warning(pop) + + RtlCopyMemory(pRemoteAddress, + pAddressValue->byteArray16->byteArray16, + IPV6_ADDRESS_SIZE); + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_REMOTE_SCOPE_ID)) + pCompletionData->pSendParams->remoteScopeId = pMetadata->remoteScopeId; + } + + pCompletionData->pSendParams->remoteAddress = pRemoteAddress; + } + + pCompletionData->pSendParams->controlData = (WSACMSGHDR*)pCompletionData->pInjectionData->pControlData; + pCompletionData->pSendParams->controlDataLength = pCompletionData->pInjectionData->controlDataLength; + + /// Initial offset is at Transport Header, so just create a new NET_BUFFER_LIST based on the + /// original NET_BUFFER_LIST ... + pNetBufferList = KrnlHlprNBLCreateNew(g_pNDISPoolData->nblPoolHandle, + (NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket, + &(pCompletionData->pAllocatedBuffer), + &size, + &(pCompletionData->pAllocatedMDL), + pData->additionalBytes, + FALSE); + + if(!pNetBufferList) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformAdvancedPacketInjectionAtOutboundTransport: KrnlHlprNBLCreateNew() [pNetBufferList: %#p]\n", + pNetBufferList); + + HLPR_BAIL; + } + +#if DBG + + AdvancedPacketInjectionCountersIncrement(injectionHandle, + &g_apiOutstandingNewNBLs); + +#endif /// DBG + + pCompletionData->refCount = KrnlHlprNBLGetRequiredRefCount(pNetBufferList); + + status = FwpsInjectTransportSendAsync(pCompletionData->pInjectionData->injectionHandle, + pCompletionData->pInjectionData->injectionContext, + endpointHandle, + 0, + pCompletionData->pSendParams, + pCompletionData->pInjectionData->addressFamily, + compartmentID, + pNetBufferList, + CompleteAdvancedPacketInjection, + pCompletionData); + + NT_ASSERT(irql == KeGetCurrentIrql()); + + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformAdvancedPacketInjectionAtOutboundTransport: FwpsInjectTransportSendAsync() [status: %#x]\n", + status); + +#if DBG + + AdvancedPacketInjectionCountersIncrement(injectionHandle, + &g_apiTotalFailedInjectionCalls); + +#endif /// DBG + + } + +#if DBG + + else + AdvancedPacketInjectionCountersIncrement(injectionHandle, + &g_apiTotalSuccessfulInjectionCalls); + +#endif /// DBG + + HLPR_BAIL_LABEL: + + NT_ASSERT(status == STATUS_SUCCESS); + + if(status != STATUS_SUCCESS) + { + if(pNetBufferList) + { + FwpsFreeNetBufferList(pNetBufferList); + + pNetBufferList = 0; + +#if DBG + + AdvancedPacketInjectionCountersDecrement(injectionHandle, + &g_apiOutstandingNewNBLs); + +#endif + + } + + if(pCompletionData) + AdvancedPacketInjectionCompletionDataDestroy(&pCompletionData, + TRUE); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PerformAdvancedPacketInjectionAtOutboundTransport() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @private_function="AdvancedPacketInjectionDeferredProcedureCall" + + Purpose: Invokes the appropriate private injection routine to perform the injection at + DISPATCH_LEVEL.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF542972.aspx
+*/ +_IRQL_requires_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Function_class_(KDEFERRED_ROUTINE) +VOID AdvancedPacketInjectionDeferredProcedureCall(_In_ KDPC* pDPC, + _In_opt_ PVOID pContext, + _In_opt_ PVOID pArg1, + _In_opt_ PVOID pArg2) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> AdvancedPacketInjectionDeferredProcedureCall()\n"); + +#endif /// DBG + + UNREFERENCED_PARAMETER(pDPC); + UNREFERENCED_PARAMETER(pContext); + UNREFERENCED_PARAMETER(pArg2); + + NT_ASSERT(pDPC); + NT_ASSERT(pArg1); + NT_ASSERT(((DPC_DATA*)pArg1)->pClassifyData); + NT_ASSERT(((DPC_DATA*)pArg1)->pInjectionData); + + DPC_DATA* pDPCData = (DPC_DATA*)pArg1; + + if(pDPCData) + { + NTSTATUS status = STATUS_SUCCESS; + FWPS_INCOMING_VALUES* pClassifyValues = (FWPS_INCOMING_VALUES*)pDPCData->pClassifyData->pClassifyValues; + + if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6) + status = PerformAdvancedPacketInjectionAtInboundNetwork(&(pDPCData->pClassifyData), + &(pDPCData->pInjectionData), + FALSE); + else if(pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V6) + status = PerformAdvancedPacketInjectionAtOutboundNetwork(&(pDPCData->pClassifyData), + &(pDPCData->pInjectionData), + FALSE); + else if(pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V4 || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V6) + status = PerformAdvancedPacketInjectionAtForward(&(pDPCData->pClassifyData), + &(pDPCData->pInjectionData), + FALSE); + else if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V6 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V6 || + (pDPCData->pInjectionData->direction == FWP_DIRECTION_INBOUND && + (pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4 || + pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4 || /// Policy Change Reauthorization + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V6 || /// Policy Change Reauthorization + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6))) + status = PerformAdvancedPacketInjectionAtInboundTransport(&(pDPCData->pClassifyData), + &(pDPCData->pInjectionData), + FALSE); + else if(pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6 || + (pDPCData->pInjectionData->direction == FWP_DIRECTION_OUTBOUND && + (pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4 || + pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 || /// Policy Change Reauthorization + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6 || /// Policy Change Reauthorization + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6))) + status = PerformAdvancedPacketInjectionAtOutboundTransport(&(pDPCData->pClassifyData), + &(pDPCData->pInjectionData), + FALSE); + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + else if(pDPCData->pInjectionData->direction == FWP_DIRECTION_INBOUND && + (pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V6)) + status = PerformAdvancedPacketInjectionAtInboundTransport(&(pDPCData->pClassifyData), + &(pDPCData->pInjectionData), + FALSE); + else if(pDPCData->pInjectionData->direction == FWP_DIRECTION_OUTBOUND && + (pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V6)) + status = PerformAdvancedPacketInjectionAtOutboundTransport(&(pDPCData->pClassifyData), + &(pDPCData->pInjectionData), + FALSE); + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + else if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_MAC_FRAME_ETHERNET || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_MAC_FRAME_NATIVE) + status = PerformAdvancedPacketInjectionAtInboundMACFrame(&(pDPCData->pClassifyData), + &(pDPCData->pInjectionData), + FALSE); + else if(pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_MAC_FRAME_ETHERNET || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_MAC_FRAME_NATIVE) + status = PerformAdvancedPacketInjectionAtOutboundMACFrame(&(pDPCData->pClassifyData), + &(pDPCData->pInjectionData), + FALSE); + else if(pClassifyValues->layerId == FWPS_LAYER_INGRESS_VSWITCH_ETHERNET) + status = PerformAdvancedPacketInjectionAtIngressVSwitchEthernet(&(pDPCData->pClassifyData), + &(pDPCData->pInjectionData), + FALSE); + else if(pClassifyValues->layerId == FWPS_LAYER_EGRESS_VSWITCH_ETHERNET) + status = PerformAdvancedPacketInjectionAtEgressVSwitchEthernet(&(pDPCData->pClassifyData), + &(pDPCData->pInjectionData), + FALSE); + +#endif // (NTDDI_VERSION >= NTDDI_WIN8) +#endif // (NTDDI_VERSION >= NTDDI_WIN7) + + else + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! AdvancedPacketInjectionDeferredProcedureCall() [status: %#x]\n", + (UINT32)STATUS_NOT_SUPPORTED); + + if(status != STATUS_SUCCESS) + { + if(pDPCData->pClassifyData) + KrnlHlprClassifyDataDestroyLocalCopy(&(pDPCData->pClassifyData)); + + if(pDPCData->pInjectionData) + KrnlHlprInjectionDataDestroy(&(pDPCData->pInjectionData)); + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! AdvancedPacketInjectionDeferredProcedureCall: PerformAdvancedPacketInjection() [status: %#x]\n", + status); + } + + KrnlHlprDPCDataDestroy(&pDPCData); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- AdvancedPacketInjectionDeferredProcedureCall()\n"); + +#endif /// DBG + + return; +} + +/** + @private_function="AdvancedPacketInjectionWorkItemRoutine" + + Purpose: Invokes the appropriate private injection routine to perform the injection at + PASSIVE_LEVEL.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF566380.aspx
+*/ +_IRQL_requires_(PASSIVE_LEVEL) +_IRQL_requires_same_ +_Function_class_(IO_WORKITEM_ROUTINE) +VOID AdvancedPacketInjectionWorkItemRoutine(_In_ PDEVICE_OBJECT pDeviceObject, + _Inout_opt_ PVOID pContext) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> AdvancedPacketInjectionWorkItemRoutine()\n"); + +#endif /// DBG + + UNREFERENCED_PARAMETER(pDeviceObject); + + NT_ASSERT(pContext); + NT_ASSERT(((WORKITEM_DATA*)pContext)->pClassifyData); + NT_ASSERT(((WORKITEM_DATA*)pContext)->pInjectionData); + + WORKITEM_DATA* pWorkItemData = (WORKITEM_DATA*)pContext; + + if(pWorkItemData) + { + NTSTATUS status = STATUS_SUCCESS; + FWPS_INCOMING_VALUES* pClassifyValues = (FWPS_INCOMING_VALUES*)pWorkItemData->pClassifyData->pClassifyValues; + + if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6) + status = PerformAdvancedPacketInjectionAtInboundNetwork(&(pWorkItemData->pClassifyData), + &(pWorkItemData->pInjectionData), + FALSE); + else if(pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V6) + status = PerformAdvancedPacketInjectionAtOutboundNetwork(&(pWorkItemData->pClassifyData), + &(pWorkItemData->pInjectionData), + FALSE); + else if(pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V4 || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V6) + status = PerformAdvancedPacketInjectionAtForward(&(pWorkItemData->pClassifyData), + &(pWorkItemData->pInjectionData), + FALSE); + else if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V6 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V6 || + (pWorkItemData->pInjectionData->direction == FWP_DIRECTION_INBOUND && + (pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4 || + pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4 || /// Policy Change Reauthorization + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V6 || /// Policy Change Reauthorization + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6))) + status = PerformAdvancedPacketInjectionAtInboundTransport(&(pWorkItemData->pClassifyData), + &(pWorkItemData->pInjectionData), + FALSE); + else if(pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6 || + (pWorkItemData->pInjectionData->direction == FWP_DIRECTION_OUTBOUND && + (pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4 || + pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 || /// Policy Change Reauthorization + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6 || /// Policy Change Reauthorization + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6))) + status = PerformAdvancedPacketInjectionAtOutboundTransport(&(pWorkItemData->pClassifyData), + &(pWorkItemData->pInjectionData), + FALSE); + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + else if(pWorkItemData->pInjectionData->direction == FWP_DIRECTION_INBOUND && + (pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V6)) + status = PerformAdvancedPacketInjectionAtInboundTransport(&(pWorkItemData->pClassifyData), + &(pWorkItemData->pInjectionData), + FALSE); + else if(pWorkItemData->pInjectionData->direction == FWP_DIRECTION_OUTBOUND && + (pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V6)) + status = PerformAdvancedPacketInjectionAtOutboundTransport(&(pWorkItemData->pClassifyData), + &(pWorkItemData->pInjectionData), + FALSE); + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + else if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_MAC_FRAME_ETHERNET || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_MAC_FRAME_NATIVE) + status = PerformAdvancedPacketInjectionAtInboundMACFrame(&(pWorkItemData->pClassifyData), + &(pWorkItemData->pInjectionData), + FALSE); + else if(pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_MAC_FRAME_ETHERNET || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_MAC_FRAME_NATIVE) + status = PerformAdvancedPacketInjectionAtOutboundMACFrame(&(pWorkItemData->pClassifyData), + &(pWorkItemData->pInjectionData), + FALSE); + else if(pClassifyValues->layerId == FWPS_LAYER_INGRESS_VSWITCH_ETHERNET) + status = PerformAdvancedPacketInjectionAtIngressVSwitchEthernet(&(pWorkItemData->pClassifyData), + &(pWorkItemData->pInjectionData), + FALSE); + else if(pClassifyValues->layerId == FWPS_LAYER_EGRESS_VSWITCH_ETHERNET) + status = PerformAdvancedPacketInjectionAtEgressVSwitchEthernet(&(pWorkItemData->pClassifyData), + &(pWorkItemData->pInjectionData), + FALSE); + +#endif // (NTDDI_VERSION >= NTDDI_WIN8) +#endif // (NTDDI_VERSION >= NTDDI_WIN7) + + else + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! AdvancedPacketInjectionWorkItemRoutine() [status: %#x]\n", + (UINT32)STATUS_NOT_SUPPORTED); + + if(status != STATUS_SUCCESS) + { + if(pWorkItemData->pClassifyData) + KrnlHlprClassifyDataDestroyLocalCopy(&(pWorkItemData->pClassifyData)); + + if(pWorkItemData->pInjectionData) + KrnlHlprInjectionDataDestroy(&(pWorkItemData->pInjectionData)); + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! AdvancedPacketInjectionWorkItemRoutine: PerformAdvancedPacketInjection() [status: %#x]\n", + status); + } + + KrnlHlprWorkItemDataDestroy(&pWorkItemData); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- AdvancedPacketInjectionWorkItemRoutine()\n"); + +#endif /// DBG + + return; +} + +/** + @private_function="TriggerAdvancedPacketInjectionInline" + + Purpose: Makes a reference to all the classification data structures and invokes the + appropriate private injection routine to perform the injection.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +NTSTATUS TriggerAdvancedPacketInjectionInline(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pNetBufferList, + _In_opt_ const VOID* pClassifyContext, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut, + _In_ INJECTION_DATA** ppInjectionData) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> TriggerAdvancedPacketInjectionInline()\n"); + +#endif /// DBG + + NT_ASSERT(pClassifyValues); + NT_ASSERT(pMetadata); + NT_ASSERT(pNetBufferList); + NT_ASSERT(pFilter); + NT_ASSERT(pClassifyOut); + NT_ASSERT(ppInjectionData); + NT_ASSERT(*ppInjectionData); + + + NTSTATUS status = STATUS_SUCCESS; + CLASSIFY_DATA* pClassifyData = 0; + +#pragma warning(push) +#pragma warning(disable: 6014) /// pClassifyData will be freed in completionFn using AdvancedPacketInjectionCompletionDataDestroy + + HLPR_NEW(pClassifyData, + CLASSIFY_DATA, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pClassifyData, + status); + +#pragma warning(pop) + + pClassifyData->pClassifyValues = pClassifyValues; + pClassifyData->pMetadataValues = pMetadata; + pClassifyData->pPacket = pNetBufferList; + pClassifyData->pClassifyContext = pClassifyContext; + pClassifyData->pFilter = pFilter; + pClassifyData->flowContext = flowContext; + pClassifyData->pClassifyOut = pClassifyOut; + + if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6) + status = PerformAdvancedPacketInjectionAtInboundNetwork(&pClassifyData, + ppInjectionData, + TRUE); + else if(pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V6) + status = PerformAdvancedPacketInjectionAtOutboundNetwork(&pClassifyData, + ppInjectionData, + TRUE); + else if(pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V4 || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V6) + status = PerformAdvancedPacketInjectionAtForward(&pClassifyData, + ppInjectionData, + TRUE); + else if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V6 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V6 || + ((*ppInjectionData)->direction == FWP_DIRECTION_INBOUND && + (pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4 || + pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4 || /// Policy Change Reauthorization + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V6 || /// Policy Change Reauthorization + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6))) + status = PerformAdvancedPacketInjectionAtInboundTransport(&pClassifyData, + ppInjectionData, + TRUE); + else if(pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6 || + ((*ppInjectionData)->direction == FWP_DIRECTION_OUTBOUND && + (pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4 || + pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 || /// Policy Change Reauthorization + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6 || /// Policy Change Reauthorization + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6))) + status = PerformAdvancedPacketInjectionAtOutboundTransport(&pClassifyData, + ppInjectionData, + TRUE); + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + else if((*ppInjectionData)->direction == FWP_DIRECTION_INBOUND && + (pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V6)) + status = PerformAdvancedPacketInjectionAtInboundTransport(&pClassifyData, + ppInjectionData, + TRUE); + else if((*ppInjectionData)->direction == FWP_DIRECTION_OUTBOUND && + (pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V6)) + status = PerformAdvancedPacketInjectionAtOutboundTransport(&pClassifyData, + ppInjectionData, + TRUE); + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + else if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_MAC_FRAME_ETHERNET || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_MAC_FRAME_NATIVE) + status = PerformAdvancedPacketInjectionAtInboundMACFrame(&pClassifyData, + ppInjectionData, + TRUE); + else if(pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_MAC_FRAME_ETHERNET || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_MAC_FRAME_NATIVE) + status = PerformAdvancedPacketInjectionAtOutboundMACFrame(&pClassifyData, + ppInjectionData, + TRUE); + else if(pClassifyValues->layerId == FWPS_LAYER_INGRESS_VSWITCH_ETHERNET) + status = PerformAdvancedPacketInjectionAtIngressVSwitchEthernet(&pClassifyData, + ppInjectionData, + TRUE); + else if(pClassifyValues->layerId == FWPS_LAYER_EGRESS_VSWITCH_ETHERNET) + status = PerformAdvancedPacketInjectionAtEgressVSwitchEthernet(&pClassifyData, + ppInjectionData, + TRUE); + +#endif // (NTDDI_VERSION >= NTDDI_WIN8) +#endif // (NTDDI_VERSION >= NTDDI_WIN7) + + else + { + status = STATUS_NOT_SUPPORTED; + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! TriggerAdvancedPacketInjectionInline() [status: %#x]\n", + status); + } + + HLPR_BAIL_LABEL: + + NT_ASSERT(status == STATUS_SUCCESS); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- TriggerAdvancedPacketInjectionInline() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @private_function="TriggerAdvancedPacketInjectionOutOfBand" + + Purpose: Creates a local copy of the classification data structures and queues a WorkItem + to perform the injection at PASSIVE_LEVEL.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF550679.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF566380.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +NTSTATUS TriggerAdvancedPacketInjectionOutOfBand(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pNetBufferList, + _In_opt_ const VOID* pClassifyContext, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _In_ FWPS_CLASSIFY_OUT* pClassifyOut, + _In_ INJECTION_DATA* pInjectionData, + _In_ PC_ADVANCED_PACKET_INJECTION_DATA* pPCData) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> TriggerAdvancedPacketInjectionOutOfBand()\n"); + +#endif /// DBG + + UNREFERENCED_PARAMETER(pClassifyOut); + + NT_ASSERT(pClassifyValues); + NT_ASSERT(pMetadata); + NT_ASSERT(pNetBufferList); + NT_ASSERT(pFilter); + NT_ASSERT(pClassifyOut); + NT_ASSERT(pInjectionData); + NT_ASSERT(pPCData); + + NTSTATUS status = STATUS_SUCCESS; + CLASSIFY_DATA* pClassifyData = 0; + +#pragma warning(push) +#pragma warning(disable: 6014) /// pClassifyData will be freed in completionFn using AdvancedPacketInjectionCompletionDataDestroy + + status = KrnlHlprClassifyDataCreateLocalCopy(&pClassifyData, + pClassifyValues, + pMetadata, + pNetBufferList, + pClassifyContext, + pFilter, + flowContext, + pClassifyOut); + HLPR_BAIL_ON_FAILURE(status); + +#pragma warning(pop) + + if(pPCData->useWorkItems) + status = KrnlHlprWorkItemQueue(g_pWDMDevice, + AdvancedPacketInjectionWorkItemRoutine, + pClassifyData, + pInjectionData, + 0); + else if(pPCData->useThreadedDPC) + status = KrnlHlprThreadedDPCQueue(AdvancedPacketInjectionDeferredProcedureCall, + pClassifyData, + pInjectionData, + 0); + else + status = KrnlHlprDPCQueue(AdvancedPacketInjectionDeferredProcedureCall, + pClassifyData, + pInjectionData, + 0); + + HLPR_BAIL_LABEL: + + NT_ASSERT(status == STATUS_SUCCESS); + + if(status != STATUS_SUCCESS) + { + if(pClassifyData) + KrnlHlprClassifyDataDestroyLocalCopy(&pClassifyData); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- TriggerAdvancedPacketInjectionOutOfBand() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +#if(NTDDI_VERSION >= NTDDI_WIN7) + +/** + @classify_function="ClassifyAdvancedPacketInjection" + + Purpose: Blocks the current NET_BUFFER_LIST and injects a new NBL back to the stack.
+
+ Notes: Applies to the following layers:
+ FWPS_LAYER_INBOUND_IPPACKET_V{4/6}
+ FWPS_LAYER_OUTBOUND_IPPACKET_V{4/6}
+ FWPS_LAYER_IPFORWARD_V{4/6}
+ FWPS_LAYER_INBOUND_TRANSPORT_V{4/6}
+ FWPS_LAYER_OUTBOUND_TRANSPORT_V{4/6}
+ FWPS_LAYER_DATAGRAM_DATA_V{4/6}
+ FWPS_LAYER_INBOUND_ICMP_ERROR_V{4/6}
+ FWPS_LAYER_OUTBOUND_ICMP_ERROR_V{4/6}
+ FWPS_LAYER_ALE_AUTH_CONNECT_V{4/6}
+ FWPS_LAYER_ALE_FLOW_ESTABLISHED_V{4/6}
+ FWPS_LAYER_STREAM_PACKET_V{4/6}
+ FWPS_LAYER_INBOUND_MAC_FRAME_ETHERNET
+ FWPS_LAYER_OUTBOUND_MAC_FRAME_ETHERNET
+ FWPS_LAYER_INBOUND_MAC_FRAME_NATIVE
+ FWPS_LAYER_OUTBOUND_MAC_FRAME_NATIVE
+ FWPS_LAYER_INGRESS_VSWITCH_ETHERNET
+ FWPS_LAYER_EGRESS_VSWITCH_ETHERNET
+
+ TCP @ FWPM_LAYER_ALE_AUTH_CONNECT_V{4/6} has no NBL
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF544893.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI ClassifyAdvancedPacketInjection(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pNetBufferList, + _In_opt_ const VOID* pClassifyContext, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut) +{ + NT_ASSERT(pClassifyValues); + NT_ASSERT(pMetadata); + NT_ASSERT(pFilter); + NT_ASSERT(pClassifyOut); + NT_ASSERT(pFilter->providerContext); + NT_ASSERT(pFilter->providerContext->type == FWPM_GENERAL_CONTEXT); + NT_ASSERT(pFilter->providerContext->dataBuffer); + NT_ASSERT(pFilter->providerContext->dataBuffer->size == sizeof(PC_ADVANCED_PACKET_INJECTION_DATA)); + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + NT_ASSERT(pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V6 || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V4 || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V6 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V6 || + pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4 || + pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V6 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_MAC_FRAME_ETHERNET || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_MAC_FRAME_ETHERNET || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_MAC_FRAME_NATIVE || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_MAC_FRAME_NATIVE || + pClassifyValues->layerId == FWPS_LAYER_INGRESS_VSWITCH_ETHERNET || + pClassifyValues->layerId == FWPS_LAYER_EGRESS_VSWITCH_ETHERNET); + +#else + + NT_ASSERT(pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V6 || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V4 || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V6 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V6 || + pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4 || + pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V6); + +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) + + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> ClassifyAdvancedPacketInjection() [Layer: %s][FilterID: %#I64x][Rights: %#x]", + KrnlHlprFwpsLayerIDToString(pClassifyValues->layerId), + pFilter->filterId, + pClassifyOut->rights); + +#if DBG + + PrvAdvancedPacketInjectionCountersIncrement(pClassifyValues, + pMetadata, + &g_apiTotalClassifies); + +#endif /// DBG + + if(pClassifyOut->rights & FWPS_RIGHT_ACTION_WRITE) + { + /// Packets are not available for TCP @ ALE_AUTH_CONNECT, so skip over as there is nothing to inject + if(pNetBufferList) + { + NTSTATUS status = STATUS_SUCCESS; + FWP_VALUE* pFlags = 0; + INJECTION_DATA* pInjectionData = 0; + + pClassifyOut->actionType = FWP_ACTION_CONTINUE; + + pFlags = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_FLAGS); + if(pFlags && + pFlags->type == FWP_UINT32) + { + /// For IPsec interop, if ALE classification is required, bypass the injection + if(pFlags->uint32 & FWP_CONDITION_FLAG_IS_IPSEC_SECURED && + FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_ALE_CLASSIFY_REQUIRED)) + HLPR_BAIL; + + /// Inject the individual fragments, but not the fragment grouping of those fragments + if(pFlags->uint32 & FWP_CONDITION_FLAG_IS_FRAGMENT_GROUP) + HLPR_BAIL; + } + +#pragma warning(push) +#pragma warning(disable: 6014) /// pInjectionData will be freed in completionFn using AdvancedPacketInjectionCompletionDataDestroy + + status = KrnlHlprInjectionDataCreate(&pInjectionData, + pClassifyValues, + pMetadata, + (NET_BUFFER_LIST*)pNetBufferList, + pFilter); + HLPR_BAIL_ON_FAILURE(status); + +#pragma warning(pop) + + if(pInjectionData->injectionState != FWPS_PACKET_INJECTED_BY_SELF && + pInjectionData->injectionState != FWPS_PACKET_PREVIOUSLY_INJECTED_BY_SELF) + { + BOOLEAN performOutOfBand = TRUE; + FWP_VALUE* pProtocolValue = 0; + PC_ADVANCED_PACKET_INJECTION_DATA* pData = (PC_ADVANCED_PACKET_INJECTION_DATA*)pFilter->providerContext->dataBuffer->data; + + pClassifyOut->actionType = FWP_ACTION_BLOCK; + pClassifyOut->flags |= FWPS_CLASSIFY_OUT_FLAG_ABSORB; + pClassifyOut->rights ^= FWPS_RIGHT_ACTION_WRITE; + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + if(pMetadata->l2Flags & FWPS_L2_INCOMING_FLAG_RECLASSIFY_MULTI_DESTINATION) + HLPR_BAIL; + +#endif + + if(pFlags && + pFlags->type == FWP_UINT32 && + pFlags->uint32 & FWP_CONDITION_FLAG_IS_IPSEC_SECURED) + pInjectionData->isIPsecSecured = TRUE; + + /// Override the default of performing Out of Band with the user's specified setting ... + if(pData->performInline) + performOutOfBand = FALSE; + + /// ... however, due to TCP's locking semantics, TCP can only be injected Out of Band at any transport layer or equivalent, ... + pProtocolValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_PROTOCOL); + if((pProtocolValue && + pProtocolValue->uint8 == IPPROTO_TCP && + pClassifyValues->layerId > FWPS_LAYER_IPFORWARD_V6_DISCARD) || + pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V6) + performOutOfBand = TRUE; + + /// ... and inbound injection of loopback traffic requires us to use Out of Band modification as well due to address lookups. + if(!performOutOfBand && + pFlags && + pFlags->type == FWP_UINT32 && + pFlags->uint32 & FWP_CONDITION_FLAG_IS_LOOPBACK && + pInjectionData->direction == FWP_DIRECTION_INBOUND) + { + FWP_VALUE* pLocalAddress = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_LOCAL_ADDRESS); + FWP_VALUE* pRemoteAddress = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_REMOTE_ADDRESS); + + if((pLocalAddress && + ((pLocalAddress->type == FWP_UINT32 && + RtlCompareMemory(&(pLocalAddress->uint32), + IPV4_LOOPBACK_ADDRESS, + IPV4_ADDRESS_SIZE)) || + (pLocalAddress->type == FWP_BYTE_ARRAY16_TYPE && + pLocalAddress->byteArray16 && + RtlCompareMemory(&(pLocalAddress->byteArray16->byteArray16), + IPV6_LOOPBACK_ADDRESS, + IPV6_ADDRESS_SIZE)))) || + (pRemoteAddress && + ((pRemoteAddress->type == FWP_UINT32 && + RtlCompareMemory(&(pRemoteAddress->uint32), + IPV4_LOOPBACK_ADDRESS, + IPV4_ADDRESS_SIZE)) || + (pRemoteAddress->type == FWP_BYTE_ARRAY16_TYPE && + pRemoteAddress->byteArray16 && + RtlCompareMemory(&(pRemoteAddress->byteArray16->byteArray16), + IPV6_LOOPBACK_ADDRESS, + IPV6_ADDRESS_SIZE))))) + performOutOfBand = TRUE; + } + + if(performOutOfBand) + status = TriggerAdvancedPacketInjectionOutOfBand(pClassifyValues, + pMetadata, + pNetBufferList, + pClassifyContext, + pFilter, + flowContext, + pClassifyOut, + pInjectionData, + pData); + else + status = TriggerAdvancedPacketInjectionInline(pClassifyValues, + pMetadata, + pNetBufferList, + pClassifyContext, + pFilter, + flowContext, + pClassifyOut, + &pInjectionData); + } + else + { + pClassifyOut->actionType = FWP_ACTION_PERMIT; + + KrnlHlprInjectionDataDestroy(&pInjectionData); + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " -- Injection previously performed.\n"); + } + + HLPR_BAIL_LABEL: + + NT_ASSERT(status == STATUS_SUCCESS); + + if(status != STATUS_SUCCESS) + { + KrnlHlprInjectionDataDestroy(&pInjectionData); + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! ClassifyAdvancedPacketInjection() [status: %#x]\n", + status); + } + } + else + pClassifyOut->actionType = FWP_ACTION_PERMIT; + } + +#if DBG + + PrvAdvancedPacketInjectionCountersIncrementTotalActionResults(pClassifyValues, + pMetadata, + pClassifyOut); + +#endif /// DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- ClassifyAdvancedPacketInjection() [Layer: %s][FilterID: %#I64x][Action: %#x][Rights: %#x][Absorb: %s]\n", + KrnlHlprFwpsLayerIDToString(pClassifyValues->layerId), + pFilter->filterId, + pClassifyOut->actionType, + pClassifyOut->rights, + (pClassifyOut->flags & FWPS_CLASSIFY_OUT_FLAG_ABSORB) ? "TRUE" : "FALSE"); + + return; +} + +#else + +/** + @classify_function="ClassifyAdvancedPacketInjection" + + Purpose: Blocks the current NET_BUFFER_LIST and injects a new NBL back to the stack.
+
+ Notes: Applies to the following layers:
+ FWPS_LAYER_INBOUND_IPPACKET_V{4/6}
+ FWPS_LAYER_OUTBOUND_IPPACKET_V{4/6}
+ FWPS_LAYER_IPFORWARD_V{4/6}
+ FWPS_LAYER_INBOUND_TRANSPORT_V{4/6}
+ FWPS_LAYER_OUTBOUND_TRANSPORT_V{4/6}
+ FWPS_LAYER_DATAGRAM_DATA_V{4/6}
+ FWPS_LAYER_INBOUND_ICMP_ERROR_V{4/6}
+ FWPS_LAYER_OUTBOUND_ICMP_ERROR_V{4/6}
+ FWPS_LAYER_ALE_AUTH_CONNECT_V{4/6}
+ FWPS_LAYER_ALE_FLOW_ESTABLISHED_V{4/6}
+
+ TCP @ FWPM_LAYER_ALE_AUTH_CONNECT_V{4/6} has no NBL
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF544890.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI ClassifyAdvancedPacketInjection(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pNetBufferList, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut) +{ + NT_ASSERT(pClassifyValues); + NT_ASSERT(pMetadata); + NT_ASSERT(pFilter); + NT_ASSERT(pClassifyOut); + NT_ASSERT(pFilter->providerContext); + NT_ASSERT(pFilter->providerContext->type == FWPM_GENERAL_CONTEXT); + NT_ASSERT(pFilter->providerContext->dataBuffer); + NT_ASSERT(pFilter->providerContext->dataBuffer->size == sizeof(PC_ADVANCED_PACKET_INJECTION_DATA)); + NT_ASSERT(pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V6 || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V4 || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V6 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V6 || + pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4 || + pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6); + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> ClassifyAdvancedPacketInjection() [Layer: %s][FilterID: %#I64x][Rights: %#x]", + KrnlHlprFwpsLayerIDToString(pClassifyValues->layerId), + pFilter->filterId, + pClassifyOut->rights); + +#if DBG + + PrvAdvancedPacketInjectionCountersIncrement(pClassifyValues, + pMetadata, + &g_apiTotalClassifies); + +#endif /// DBG + + if(pClassifyOut->rights & FWPS_RIGHT_ACTION_WRITE) + { + /// Packets are not available for TCP @ ALE_AUTH_CONNECT, so skip over as there is nothing to inject + if(pNetBufferList) + { + NTSTATUS status = STATUS_SUCCESS; + FWP_VALUE* pFlags = 0; + INJECTION_DATA* pInjectionData = 0; + + pClassifyOut->actionType = FWP_ACTION_CONTINUE; + + pFlags = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_FLAGS); + if(pFlags && + pFlags->type == FWP_UINT32) + { + /// For IPsec interop, if ALE classification is required, bypass the injection + if(pFlags->uint32 & FWP_CONDITION_FLAG_IS_IPSEC_SECURED && + +#if(NTDDI_VERSION >= NTDDI_WIN6SP1) + + FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_ALE_CLASSIFY_REQUIRED)) + +#else + + pFlags->uint32 & FWP_CONDITION_FLAG_REQUIRES_ALE_CLASSIFY) + +#endif // (NTDDI_VERSION >= NTDDI_WIN6SP1) + + HLPR_BAIL; + + /// Inject the individual fragments, but not the fragment grouping of those fragments + if(pFlags->uint32 & FWP_CONDITION_FLAG_IS_FRAGMENT_GROUP) + HLPR_BAIL; + } + +#pragma warning(push) +#pragma warning(disable: 6014) /// pInjectionData will be freed in completionFn using AdvancedPacketInjectionCompletionDataDestroy + + status = KrnlHlprInjectionDataCreate(&pInjectionData, + pClassifyValues, + pMetadata, + (NET_BUFFER_LIST*)pNetBufferList, + pFilter); + HLPR_BAIL_ON_FAILURE(status); + +#pragma warning(pop) + + if(pInjectionData->injectionState != FWPS_PACKET_INJECTED_BY_SELF && + pInjectionData->injectionState != FWPS_PACKET_PREVIOUSLY_INJECTED_BY_SELF) + { + BOOLEAN performOutOfBand = TRUE; + FWP_VALUE* pProtocolValue = 0; + PC_ADVANCED_PACKET_INJECTION_DATA* pData = (PC_ADVANCED_PACKET_INJECTION_DATA*)pFilter->providerContext->dataBuffer->data; + + pClassifyOut->actionType = FWP_ACTION_BLOCK; + pClassifyOut->flags |= FWPS_CLASSIFY_OUT_FLAG_ABSORB; + pClassifyOut->rights ^= FWPS_RIGHT_ACTION_WRITE; + + if(pFlags && + pFlags->type == FWP_UINT32 && + pFlags->uint32 & FWP_CONDITION_FLAG_IS_IPSEC_SECURED) + pInjectionData->isIPsecSecured = TRUE; + + /// Override the default of performing Out of Band with the user's specified setting ... + if(pData->performInline) + performOutOfBand = FALSE; + + /// ... however, due to TCP's locking semantics, TCP can only be injected Out of Band at any transport layer or equivalent, ... + pProtocolValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_PROTOCOL); + if(pProtocolValue && + pProtocolValue->uint8 == IPPROTO_TCP && + pClassifyValues->layerId > FWPS_LAYER_IPFORWARD_V6_DISCARD) + performOutOfBand = TRUE; + + /// ... and inbound injection of loopback traffic requires us to use Out of Band modification as well due to address lookups. + if(!performOutOfBand && + pFlags && + pFlags->type == FWP_UINT32 && + pFlags->uint32 & FWP_CONDITION_FLAG_IS_LOOPBACK && + pInjectionData->direction == FWP_DIRECTION_INBOUND) + { + FWP_VALUE* pLocalAddress = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_LOCAL_ADDRESS); + FWP_VALUE* pRemoteAddress = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_REMOTE_ADDRESS); + + if((pLocalAddress && + (pLocalAddress->type == FWP_UINT32 && + RtlCompareMemory(&(pLocalAddress->uint32), + IPV4_LOOPBACK_ADDRESS, + IPV4_ADDRESS_SIZE)) || + (pLocalAddress->type == FWP_BYTE_ARRAY16_TYPE && + RtlCompareMemory(&(pLocalAddress->byteArray16->byteArray16), + IPV6_LOOPBACK_ADDRESS, + IPV6_ADDRESS_SIZE))) || + (pRemoteAddress && + (pRemoteAddress->type == FWP_UINT32 && + RtlCompareMemory(&(pRemoteAddress->uint32), + IPV4_LOOPBACK_ADDRESS, + IPV4_ADDRESS_SIZE)) || + (pRemoteAddress->type == FWP_BYTE_ARRAY16_TYPE && + RtlCompareMemory(&(pRemoteAddress->byteArray16->byteArray16), + IPV6_LOOPBACK_ADDRESS, + IPV6_ADDRESS_SIZE)))) + performOutOfBand = TRUE; + } + + if(performOutOfBand) + status = TriggerAdvancedPacketInjectionOutOfBand(pClassifyValues, + pMetadata, + pNetBufferList, + 0, + pFilter, + flowContext, + pClassifyOut, + pInjectionData, + pData); + else + status = TriggerAdvancedPacketInjectionInline(pClassifyValues, + pMetadata, + pNetBufferList, + 0, + pFilter, + flowContext, + pClassifyOut, + &pInjectionData); + } + else + { + pClassifyOut->actionType = FWP_ACTION_PERMIT; + + KrnlHlprInjectionDataDestroy(&pInjectionData); + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " -- Injection previously performed.\n"); + } + + HLPR_BAIL_LABEL: + + NT_ASSERT(status == STATUS_SUCCESS); + + if(status != STATUS_SUCCESS) + { + KrnlHlprInjectionDataDestroy(&pInjectionData); + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! ClassifyAdvancedPacketInjection() [status: %#x]\n", + status); + } + } + else + pClassifyOut->actionType = FWP_ACTION_PERMIT; + } + +#if DBG + + PrvAdvancedPacketInjectionCountersIncrementTotalActionResults(pClassifyValues, + pMetadata, + pClassifyOut); + +#endif /// DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- ClassifyAdvancedPacketInjection() [Layer: %s][FilterID: %#I64x][Action: %#x][Rights: %#x][Absorb: %s]\n", + KrnlHlprFwpsLayerIDToString(pClassifyValues->layerId), + pFilter->filterId, + pClassifyOut->actionType, + pClassifyOut->rights, + (pClassifyOut->flags & FWPS_CLASSIFY_OUT_FLAG_ABSORB) ? "TRUE" : "FALSE"); + + return; +} + +#endif // (NTDDI_VERSION >= NTDDI_WIN7) diff --git a/network/trans/WFPSampler/sys/ClassifyFunctions_AdvancedPacketInjectionCallouts.h b/network/trans/WFPSampler/sys/ClassifyFunctions_AdvancedPacketInjectionCallouts.h new file mode 100644 index 000000000..88b9ad08c --- /dev/null +++ b/network/trans/WFPSampler/sys/ClassifyFunctions_AdvancedPacketInjectionCallouts.h @@ -0,0 +1,74 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// ClassifyFunctions_AdvancedPacketInjectionCallouts.h +// +// Abstract: +// This module contains prototypes for WFP Classify functions that inject packets back into the +// data path using the allocate / block / inject method. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// December 13, 2013 - 1.1 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef CLASSIFY_ADVANCED_PACKET_INJECTION_H +#define CLASSIFY_ADVANCED_PACKET_INJECTION_H + +#if DBG + +extern INJECTION_COUNTERS g_apiTotalClassifies; +extern INJECTION_COUNTERS g_apiTotalBlockedAndAbsorbed; +extern INJECTION_COUNTERS g_apiTotalPermitted; +extern INJECTION_COUNTERS g_apiTotalSuccessfulInjectionCalls; +extern INJECTION_COUNTERS g_apiTotalFailedInjectionCalls; +extern INJECTION_COUNTERS g_apiOutstandingNewNBLs; + +VOID AdvancedPacketInjectionCountersIncrement(_In_ HANDLE injectionHandle, + _Inout_ INJECTION_COUNTERS* pCounters); + +#endif /// DBG + +_IRQL_requires_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Function_class_(KDEFERRED_ROUTINE) +VOID AdvancedPacketInjectionDeferredProcedureCall(_In_ KDPC* pDPC, + _In_opt_ PVOID pContext, + _In_opt_ PVOID pDPCData, + _In_opt_ PVOID pArg2); + +#if(NTDDI_VERSION >= NTDDI_WIN7) + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI ClassifyAdvancedPacketInjection(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pLayerData, + _In_opt_ const VOID* pClassifyContext, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut); + +#else + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI ClassifyAdvancedPacketInjection(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pLayerData, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut); + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + +#endif /// CLASSIFY_ADVANCED_PACKET_INJECTION_H diff --git a/network/trans/WFPSampler/sys/ClassifyFunctions_BasicActionCallouts.cpp b/network/trans/WFPSampler/sys/ClassifyFunctions_BasicActionCallouts.cpp new file mode 100644 index 000000000..375dab933 --- /dev/null +++ b/network/trans/WFPSampler/sys/ClassifyFunctions_BasicActionCallouts.cpp @@ -0,0 +1,655 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// ClassifyFunctions_BasicActionCallouts.cpp +// +// Abstract: +// This module contains WFP Classify functions for returning the simple actions +// FWP_ACTION_BLOCK and FWP_ACTION_PERMIT. +// +// Naming Convention: +// +// +// +// i.e. +// +// ClassifyBasicActionBlock +// +// +// Classify - Function is an FWPS_CALLOUT_CLASSIFY_FN. +// +// BasicAction - Function demonstates use of the simple action types. +// +// { +// Block - Function returns FWP_ACTION_BLOCK, thus blocking the traffic. +// Continue - Function returns FWP_ACTION_CONTINUE thus allowing the traffic for another +// filter (if any) to make a final decision. +// Permit - Function returns FWP_ACTION_PERMIT, thus allowing the traffic. +// Random - Function randomly returns either FWP_ACTION_BLOCK or FWP_ACTION_PERMIT. +// } +// +// Private Functions: +// PerformBasicAction(), +// +// Public Functions: +// ClassifyBasicActionBlock(), +// ClassifyBasicActionContinue(), +// ClassifyBasicActionPermit(), +// ClassifyBasicActionRandom(), +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Enhance function declaration for IntelliSense and +// enhance traces +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "Framework_WFPSamplerCalloutDriver.h" /// . +#include "ClassifyFunctions_BasicActionCallouts.tmh" /// $(OBJ_PATH)\$(O)\ + +/** + @private_function="PerformBasicAction" + + Purpose: Sets the pClassifyOut->actionType to the basic action specified.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551229.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID PerformBasicAction(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pLayerData, + _In_opt_ const VOID* pClassifyContext, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut, + _In_ FWP_ACTION_TYPE basicAction) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PerformBasicAction()\n"); +#endif /// DBG + + + NT_ASSERT(pClassifyValues); + NT_ASSERT(pMetadata); + NT_ASSERT(pFilter); + NT_ASSERT(pClassifyOut); + + UNREFERENCED_PARAMETER(pMetadata); + UNREFERENCED_PARAMETER(pClassifyContext); + UNREFERENCED_PARAMETER(pFilter); + UNREFERENCED_PARAMETER(flowContext); + + if(pClassifyValues->layerId == FWPS_LAYER_STREAM_V4 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_V6) + { + FWPS_STREAM_CALLOUT_IO_PACKET0* pIOPacket = (FWPS_STREAM_CALLOUT_IO_PACKET*)pLayerData; + + if(pIOPacket) + { + pIOPacket->streamAction = FWPS_STREAM_ACTION_NONE; + pIOPacket->countBytesEnforced = pIOPacket->streamData->dataLength; + } + } + + if(pClassifyOut) + { + pClassifyOut->actionType = basicAction; + + /// Clear the right to mark as the definitive answer. + if(basicAction == FWP_ACTION_BLOCK || + (basicAction == FWP_ACTION_PERMIT && + pFilter->flags & FWPS_FILTER_FLAG_CLEAR_ACTION_RIGHT)) + pClassifyOut->rights ^= FWPS_RIGHT_ACTION_WRITE; + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PerformBasicAction()\n"); + +#endif /// DBG + + return; +} + +#if(NTDDI_VERSION >= NTDDI_WIN7) + +/** + @classify_function="ClassifyBasicActionBlock" + + Purpose: Sets the pClassifyOut->actionType to FWP_ACTION_BLOCK.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551229.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF544893.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI ClassifyBasicActionBlock(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pLayerData, + _In_opt_ const VOID* pClassifyContext, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut) +{ + NT_ASSERT(pClassifyValues); + NT_ASSERT(pMetadata); + NT_ASSERT(pFilter); + NT_ASSERT(pClassifyOut); + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> ClassifyBasicActionBlock() [Layer: %s][FilterID: %#I64x][Rights: %#x]", + KrnlHlprFwpsLayerIDToString(pClassifyValues->layerId), + pFilter->filterId, + pClassifyOut->rights); + + if(pClassifyOut->rights & FWPS_RIGHT_ACTION_WRITE) + { + PerformBasicAction(pClassifyValues, + pMetadata, + pLayerData, + pClassifyContext, + pFilter, + flowContext, + pClassifyOut, + FWP_ACTION_BLOCK); + } + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- ClassifyBasicActionBlock() [Layer: %s][FilterID: %#I64x][Action: %#x][Rights: %#x][Absorb: %s]\n", + KrnlHlprFwpsLayerIDToString(pClassifyValues->layerId), + pFilter->filterId, + pClassifyOut->actionType, + pClassifyOut->rights, + (pClassifyOut->flags & FWPS_CLASSIFY_OUT_FLAG_ABSORB) ? "TRUE" : "FALSE"); + + return; +} + +/** + @classify_function="ClassifyBasicActionContinue" + + Purpose: Sets the pClassifyOut->actionType to FWP_ACTION_CONTINUE.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551229.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF544893.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI ClassifyBasicActionContinue(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pLayerData, + _In_opt_ const VOID* pClassifyContext, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut) +{ + NT_ASSERT(pClassifyValues); + NT_ASSERT(pMetadata); + NT_ASSERT(pFilter); + NT_ASSERT(pClassifyOut); + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> ClassifyBasicActionContinue() [Layer: %s][FilterID: %#I64x][Rights: %#x]", + KrnlHlprFwpsLayerIDToString(pClassifyValues->layerId), + pFilter->filterId, + pClassifyOut->rights); + + if(pClassifyOut->rights & FWPS_RIGHT_ACTION_WRITE) + { + PerformBasicAction(pClassifyValues, + pMetadata, + pLayerData, + pClassifyContext, + pFilter, + flowContext, + pClassifyOut, + FWP_ACTION_CONTINUE); + } + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- ClassifyBasicActionContinue() [Layer: %s][FilterID: %#I64x][Action: %#x][Rights: %#x][Absorb: %s]\n", + KrnlHlprFwpsLayerIDToString(pClassifyValues->layerId), + pFilter->filterId, + pClassifyOut->actionType, + pClassifyOut->rights, + (pClassifyOut->flags & FWPS_CLASSIFY_OUT_FLAG_ABSORB) ? "TRUE" : "FALSE"); + + return; +} + +/** + @classify_function="ClassifyBasicActionPermit" + + Purpose: Sets the pClassifyOut->actionType to FWP_ACTION_PERMIT.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551229.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF544893.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI ClassifyBasicActionPermit(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pLayerData, + _In_opt_ const VOID* pClassifyContext, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut) +{ + NT_ASSERT(pClassifyValues); + NT_ASSERT(pMetadata); + NT_ASSERT(pFilter); + NT_ASSERT(pClassifyOut); + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> ClassifyBasicActionPermit() [Layer: %s][FilterID: %#I64x][Rights: %#x]", + KrnlHlprFwpsLayerIDToString(pClassifyValues->layerId), + pFilter->filterId, + pClassifyOut->rights); + + if(pClassifyOut->rights & FWPS_RIGHT_ACTION_WRITE) + { + PerformBasicAction(pClassifyValues, + pMetadata, + pLayerData, + pClassifyContext, + pFilter, + flowContext, + pClassifyOut, + FWP_ACTION_PERMIT); + } + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- ClassifyBasicActionPermit() [Layer: %s][FilterID: %#I64x][Action: %#x][Rights: %#x][Absorb: %s]\n", + KrnlHlprFwpsLayerIDToString(pClassifyValues->layerId), + pFilter->filterId, + pClassifyOut->actionType, + pClassifyOut->rights, + (pClassifyOut->flags & FWPS_CLASSIFY_OUT_FLAG_ABSORB) ? "TRUE" : "FALSE"); + + return; +} + +/** + @classify_function="ClassifyBasicActionRandom" + + Purpose: Sets the pClassifyOut->actionType to either FWP_ACTION_BLOCK or FWP_ACTION_PERMIT.
+
+ Notes: Specifying -rab at the command line will allow one to determine how randomly + FWP_ACTION_BLOCK is returned.
+ Specifying -rac at the command line will allow one to determine how randomly + FWP_ACTION_CONTINUE is returned.
+ Specifying -rap at the command line will allow one to determine how randomly + FWP_ACTION_PERMIT is returned.
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551229.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF544893.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI ClassifyBasicActionRandom(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pLayerData, + _In_opt_ const VOID* pClassifyContext, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut) +{ + NT_ASSERT(pClassifyValues); + NT_ASSERT(pMetadata); + NT_ASSERT(pFilter); + NT_ASSERT(pClassifyOut); + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> ClassifyBasicActionRandom() [Layer: %s][FilterID: %#I64x][Rights: %#x]", + KrnlHlprFwpsLayerIDToString(pClassifyValues->layerId), + pFilter->filterId, + pClassifyOut->rights); + + if(pClassifyOut->rights & FWPS_RIGHT_ACTION_WRITE) + { + FWPM_PROVIDER_CONTEXT* pProviderContext = pFilter->providerContext; + + if(pProviderContext && + pProviderContext->type == FWPM_GENERAL_CONTEXT && + pProviderContext->dataBuffer && + pProviderContext->dataBuffer->data && + pProviderContext->dataBuffer->size == sizeof(PC_BASIC_ACTION_DATA)) + { + PC_BASIC_ACTION_DATA* pPCBasicActionData = (PC_BASIC_ACTION_DATA*)(pProviderContext->dataBuffer->data); + UINT32 randomizer = 1; + FWP_ACTION_TYPE actionType = FWP_ACTION_CONTINUE; + UINT32 blockCap = pPCBasicActionData->percentBlock; + UINT32 permitCap = pPCBasicActionData->percentPermit + blockCap; + LARGE_INTEGER largeInteger; + + RtlZeroMemory(&largeInteger, + sizeof(LARGE_INTEGER)); + + KeQueryTickCount(&largeInteger); + + srand(largeInteger.u.LowPart); + + randomizer += (rand() % 100); + + if(randomizer <= blockCap) + actionType = FWP_ACTION_BLOCK; + else if(randomizer <= permitCap) + actionType = FWP_ACTION_PERMIT; + + PerformBasicAction(pClassifyValues, + pMetadata, + pLayerData, + pClassifyContext, + pFilter, + flowContext, + pClassifyOut, + actionType); + } + } + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- ClassifyBasicActionRandom() [Layer: %s][FilterID: %#I64x][Action: %#x][Rights: %#x][Absorb: %s]\n", + KrnlHlprFwpsLayerIDToString(pClassifyValues->layerId), + pFilter->filterId, + pClassifyOut->actionType, + pClassifyOut->rights, + (pClassifyOut->flags & FWPS_CLASSIFY_OUT_FLAG_ABSORB) ? "TRUE" : "FALSE"); + + return; +} + +#else + +/** + @classify_function="ClassifyBasicActionBlock" + + Purpose: Sets the pClassifyOut->actionType to FWP_ACTION_BLOCK.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551229.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF544890.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI ClassifyBasicActionBlock(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pLayerData, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut) +{ + NT_ASSERT(pClassifyValues); + NT_ASSERT(pMetadata); + NT_ASSERT(pFilter); + NT_ASSERT(pClassifyOut); + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> ClassifyBasicActionBlock() [Layer: %s][FilterID: %#I64x][Rights: %#x]", + KrnlHlprFwpsLayerIDToString(pClassifyValues->layerId), + pFilter->filterId, + pClassifyOut->rights); + + if(pClassifyOut->rights & FWPS_RIGHT_ACTION_WRITE) + { + PerformBasicAction(pClassifyValues, + pMetadata, + pLayerData, + 0, + pFilter, + flowContext, + pClassifyOut, + FWP_ACTION_BLOCK); + } + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- ClassifyBasicActionBlock() [Layer: %s][FilterID: %#I64x][Action: %#x][Rights: %#x][Absorb: %s]\n", + KrnlHlprFwpsLayerIDToString(pClassifyValues->layerId), + pFilter->filterId, + pClassifyOut->actionType, + pClassifyOut->rights, + (pClassifyOut->flags & FWPS_CLASSIFY_OUT_FLAG_ABSORB) ? "TRUE" : "FALSE"); + + return; +} + +/** + @classify_function="ClassifyBasicActionContinue" + + Purpose: Sets the pClassifyOut->actionType to FWP_ACTION_CONTINUE.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551229.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF544890.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI ClassifyBasicActionContinue(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pLayerData, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut) +{ + NT_ASSERT(pClassifyValues); + NT_ASSERT(pMetadata); + NT_ASSERT(pFilter); + NT_ASSERT(pClassifyOut); + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> ClassifyBasicActionContinue() [Layer: %s][FilterID: %#I64x][Rights: %#x]", + KrnlHlprFwpsLayerIDToString(pClassifyValues->layerId), + pFilter->filterId, + pClassifyOut->rights); + + if(pClassifyOut->rights & FWPS_RIGHT_ACTION_WRITE) + { + PerformBasicAction(pClassifyValues, + pMetadata, + pLayerData, + 0, + pFilter, + flowContext, + pClassifyOut, + FWP_ACTION_CONTINUE); + } + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- ClassifyBasicActionContinue() [Layer: %s][FilterID: %#I64x][Action: %#x][Rights: %#x][Absorb: %s]\n", + KrnlHlprFwpsLayerIDToString(pClassifyValues->layerId), + pFilter->filterId, + pClassifyOut->actionType, + pClassifyOut->rights, + (pClassifyOut->flags & FWPS_CLASSIFY_OUT_FLAG_ABSORB) ? "TRUE" : "FALSE"); + + return; +} + +/** + @classify_function="ClassifyBasicActionPermit" + + Purpose: Sets the pClassifyOut->actionType to FWP_ACTION_PERMIT.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551229.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF544890.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI ClassifyBasicActionPermit(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pLayerData, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut) +{ + NT_ASSERT(pClassifyValues); + NT_ASSERT(pMetadata); + NT_ASSERT(pFilter); + NT_ASSERT(pClassifyOut); + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> ClassifyBasicActionPermit() [Layer: %s][FilterID: %#I64x][Rights: %#x]", + KrnlHlprFwpsLayerIDToString(pClassifyValues->layerId), + pFilter->filterId, + pClassifyOut->rights); + + if(pClassifyOut->rights & FWPS_RIGHT_ACTION_WRITE) + { + PerformBasicAction(pClassifyValues, + pMetadata, + pLayerData, + 0, + pFilter, + flowContext, + pClassifyOut, + FWP_ACTION_PERMIT); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- ClassifyBasicActionPermit() [Layer: %s][FilterID: %#I64x][Action: %#x][Rights: %#x][Absorb: %s]\n", + KrnlHlprFwpsLayerIDToString(pClassifyValues->layerId), + pFilter->filterId, + pClassifyOut->actionType, + pClassifyOut->rights, + (pClassifyOut->flags & FWPS_CLASSIFY_OUT_FLAG_ABSORB) ? "TRUE" : "FALSE"); + +#endif /// DBG + + return; +} + +/** + @classify_function="ClassifyBasicActionRandom" + + Purpose: Sets the pClassifyOut->actionType to either FWP_ACTION_BLOCK or FWP_ACTION_PERMIT.
+
+ Notes: Specifying -rab at the command line will allow one to determine how randomly + FWP_ACTION_BLOCK is returned.
+ Specifying -rac at the command line will allow one to determine how randomly + FWP_ACTION_CONTINUE is returned.
+ Specifying -rap at the command line will allow one to determine how randomly + FWP_ACTION_PERMIT is returned.
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551229.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF544890.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI ClassifyBasicActionRandom(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pLayerData, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut) +{ + NT_ASSERT(pClassifyValues); + NT_ASSERT(pMetadata); + NT_ASSERT(pFilter); + NT_ASSERT(pClassifyOut); + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> ClassifyBasicActionRandom() [Layer: %s][FilterID: %#I64x][Rights: %#x]", + KrnlHlprFwpsLayerIDToString(pClassifyValues->layerId), + pFilter->filterId, + pClassifyOut->rights); + + if(pClassifyOut->rights & FWPS_RIGHT_ACTION_WRITE) + { + NT_ASSERT(pFilter->providerContext); + NT_ASSERT(pFilter->providerContext->type == FWPM_GENERAL_CONTEXT); + NT_ASSERT(pFilter->providerContext->dataBuffer); + NT_ASSERT(pFilter->providerContext->dataBuffer->data); + NT_ASSERT(pFilter->providerContext->dataBuffer->size == sizeof(PC_BASIC_ACTION_DATA)); + + FWPM_PROVIDER_CONTEXT* pProviderContext = pFilter->providerContext; + PC_BASIC_ACTION_DATA* pPCBasicActionData = (PC_BASIC_ACTION_DATA*)(pProviderContext->dataBuffer->data); + UINT32 randomizer = 1; + FWP_ACTION_TYPE actionType = FWP_ACTION_CONTINUE; + UINT32 blockCap = pPCBasicActionData->percentBlock; + UINT32 permitCap = pPCBasicActionData->percentPermit + blockCap; + LARGE_INTEGER largeInteger = {0}; + + KeQueryTickCount(&largeInteger); + + srand(largeInteger.u.LowPart); + + randomizer += (rand() % 100); + + if(randomizer <= blockCap) + actionType = FWP_ACTION_BLOCK; + else if(randomizer <= permitCap) + actionType = FWP_ACTION_PERMIT; + + PerformBasicAction(pClassifyValues, + pMetadata, + pLayerData, + 0, + pFilter, + flowContext, + pClassifyOut, + actionType); + } + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- ClassifyBasicActionRandom() [Layer: %s][FilterID: %#I64x][Action: %#x][Rights: %#x][Absorb: %s]\n", + KrnlHlprFwpsLayerIDToString(pClassifyValues->layerId), + pFilter->filterId, + pClassifyOut->actionType, + pClassifyOut->rights, + (pClassifyOut->flags & FWPS_CLASSIFY_OUT_FLAG_ABSORB) ? "TRUE" : "FALSE"); + + return; +} + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) diff --git a/network/trans/WFPSampler/sys/ClassifyFunctions_BasicActionCallouts.h b/network/trans/WFPSampler/sys/ClassifyFunctions_BasicActionCallouts.h new file mode 100644 index 000000000..19bf60779 --- /dev/null +++ b/network/trans/WFPSampler/sys/ClassifyFunctions_BasicActionCallouts.h @@ -0,0 +1,116 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// ClassifyFunctions_BasicActionCallouts.h +// +// Abstract: +// This module contains prototypes of WFP Classify functions for returning the simple actions +// FWP_ACTION_BLOCK, FWP_ACTION_CONTINUE, and FWP_ACTION_PERMIT. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Enhance function declaration for IntelliSense +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef CLASSIFY_BASIC_ACTION_H +#define CLASSIFY_BASIC_ACTION_H + +#if(NTDDI_VERSION >= NTDDI_WIN7) + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI ClassifyBasicActionBlock(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pLayerData, + _In_opt_ const VOID* pClassifyContext, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI ClassifyBasicActionContinue(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pLayerData, + _In_opt_ const VOID* pClassifyContext, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI ClassifyBasicActionPermit(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pLayerData, + _In_opt_ const VOID* pClassifyContext, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI ClassifyBasicActionRandom(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pLayerData, + _In_opt_ const VOID* pClassifyContext, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut); + +#else + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI ClassifyBasicActionBlock(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pLayerData, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI ClassifyBasicActionContinue(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pLayerData, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI ClassifyBasicActionPermit(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pLayerData, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI ClassifyBasicActionRandom(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pLayerData, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut); + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + +#endif /// CLASSIFY_BASIC_ACTION_H diff --git a/network/trans/WFPSampler/sys/ClassifyFunctions_BasicPacketExaminationCallouts.cpp b/network/trans/WFPSampler/sys/ClassifyFunctions_BasicPacketExaminationCallouts.cpp new file mode 100644 index 000000000..64c0b4017 --- /dev/null +++ b/network/trans/WFPSampler/sys/ClassifyFunctions_BasicPacketExaminationCallouts.cpp @@ -0,0 +1,5640 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// ClassifyFunctions_BasicPacketExaminationCallouts.cpp +// +// Abstract: +// This module contains WFP Classify functions for examining indicated NET_BUFFER_LISTS. +// +// Naming Convention: +// +// +// +// i.e. +// +// ClassifyBasicPacketExamination +// +// +// Classify - Function is an FWPS_CALLOUT_CLASSIFY_FN. +// +// BasicPacketExamination - Function demonstates examining classified packets. +// +// Private Functions: +// +// Public Functions: +// ClassifyBasicPacketExamination(), +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Enhance function declaration for IntelliSense, enhance +// traces, add support for discard layers, and support +// tracing all of the classifyFn parameters. +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "Framework_WFPSamplerCalloutDriver.h" /// . +#include "ClassifyFunctions_BasicPacketExaminationCallouts.tmh" /// $(OBJ_PATH)\$(O)\ + +#define MAX_STRING_SIZE 2048 + +/** + @private_function="LogFlags" + + Purpose: Logs each of the flags from the FWPM_CONDITION_FLAGS value.
+
+ Notes: Uses ETW Tracing for the logging.
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA364002.aspx
+*/ +VOID LogFlags(_In_ UINT32 flags) +{ + if(flags & FWP_CONDITION_FLAG_IS_LOOPBACK) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\t\t\t\tFWP_CONDITION_FLAG_IS_LOOPBACK"); + + if(flags & FWP_CONDITION_FLAG_IS_IPSEC_SECURED) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\t\t\t\tFWP_CONDITION_FLAG_IS_IPSEC_SECURED"); + + if(flags & FWP_CONDITION_FLAG_IS_REAUTHORIZE) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\t\t\t\tFWP_CONDITION_FLAG_IS_REAUTHORIZE"); + + if(flags & FWP_CONDITION_FLAG_IS_WILDCARD_BIND) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\t\t\t\tFWP_CONDITION_FLAG_IS_WILDCARD_BIND"); + + if(flags & FWP_CONDITION_FLAG_IS_RAW_ENDPOINT) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\t\t\t\tFWP_CONDITION_FLAG_IS_RAW_ENDPOINT"); + + if(flags & FWP_CONDITION_FLAG_IS_FRAGMENT) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\t\t\t\tFWP_CONDITION_FLAG_IS_FRAGMENT"); + + if(flags & FWP_CONDITION_FLAG_IS_FRAGMENT_GROUP) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\t\t\t\tFWP_CONDITION_FLAG_IS_FRAGMENT_GROUP"); + + if(flags & FWP_CONDITION_FLAG_IS_IPSEC_NATT_RECLASSIFY) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\t\t\t\tFWP_CONDITION_FLAG_IS_IPSEC_NATT_RECLASSIFY"); + + if(flags & FWP_CONDITION_FLAG_REQUIRES_ALE_CLASSIFY) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\t\t\t\tFWP_CONDITION_FLAG_REQUIRES_ALE_CLASSIFY"); + + if(flags & FWP_CONDITION_FLAG_IS_IMPLICIT_BIND) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\t\t\t\tFWP_CONDITION_FLAG_IS_IMPLICIT_BIND"); + + +#if(NTDDI_VERSION >= NTDDI_VISTASP1) + + if(flags & FWP_CONDITION_FLAG_IS_REASSEMBLED) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\t\t\t\tFWP_CONDITION_FLAG_IS_REASSEMBLED"); + + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + if(flags & FWP_CONDITION_FLAG_IS_NAME_APP_SPECIFIED) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\t\t\t\tFWP_CONDITION_FLAG_IS_NAME_APP_SPECIFIED"); + + if(flags & FWP_CONDITION_FLAG_IS_PROMISCUOUS) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\t\t\t\tFWP_CONDITION_FLAG_IS_PROMISCUOUS"); + + if(flags & FWP_CONDITION_FLAG_IS_AUTH_FW) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\t\t\t\tFWP_CONDITION_FLAG_IS_AUTH_FW"); + + if(flags & FWP_CONDITION_FLAG_IS_RECLASSIFY) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\t\t\t\tFWP_CONDITION_FLAG_IS_RECLASSIFY"); + + if(flags & FWP_CONDITION_FLAG_IS_OUTBOUND_PASS_THRU) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\t\t\t\tFWP_CONDITION_FLAG_IS_OUTBOUND_PASS_THRU"); + + if(flags & FWP_CONDITION_FLAG_IS_INBOUND_PASS_THRU) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\t\t\t\tFWP_CONDITION_FLAG_IS_INBOUND_PASS_THRU"); + + if(flags & FWP_CONDITION_FLAG_IS_CONNECTION_REDIRECTED) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\t\t\t\tFWP_CONDITION_FLAG_IS_CONNECTION_REDIRECTED"); + + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + if(flags & FWP_CONDITION_FLAG_IS_PROXY_CONNECTION) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\t\t\t\tFWP_CONDITION_FLAG_IS_PROXY_CONNECTION"); + + if(flags & FWP_CONDITION_FLAG_IS_APPCONTAINER_LOOPBACK) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\t\t\t\tFWP_CONDITION_FLAG_IS_APPCONTAINER_LOOPBACK"); + + if(flags & FWP_CONDITION_FLAG_IS_NON_APPCONTAINER_LOOPBACK) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\t\t\t\tFWP_CONDITION_FLAG_IS_NON_APPCONTAINER_LOOPBACK"); + + if(flags & FWP_CONDITION_FLAG_IS_RESERVED) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\t\t\t\tFWP_CONDITION_FLAG_IS_RESERVED"); + + if(flags & FWP_CONDITION_FLAG_IS_HONORING_POLICY_AUTHORIZE) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\t\t\t\tFWP_CONDITION_FLAG_IS_HONORING_POLICY_AUTHORIZE"); + +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) +#endif /// (NTDDI_VERSION >= NTDDI_VISTASP1) + + return; +} + +#if(NTDDI_VERSION >= NTDDI_WIN8) + +/** + @private_function="LogL2Flags" + + Purpose: Logs each of the flags from the FWPM_CONDITION_FLAGS value.
+
+ Notes: Uses ETW Tracing for the logging.
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA364002.aspx
+*/ +VOID LogL2Flags(_In_ UINT32 flags) +{ + + if(flags & FWP_CONDITION_L2_IS_NATIVE_ETHERNET) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\t\t\t\tFWP_CONDITION_L2_IS_NATIVE_ETHERNET"); + + if(flags & FWP_CONDITION_L2_IS_WIFI) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\t\t\t\tFWP_CONDITION_L2_IS_WIFI"); + + if(flags & FWP_CONDITION_L2_IS_MOBILE_BROADBAND) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\t\t\t\tFWP_CONDITION_L2_IS_MOBILE_BROADBAND"); + + if(flags & FWP_CONDITION_L2_IS_WIFI_DIRECT_DATA) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\t\t\t\tFWP_CONDITION_L2_IS_WIFI_DIRECT_DATA"); + + if(flags & FWP_CONDITION_L2_IS_VM2VM) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\t\t\t\tFWP_CONDITION_L2_IS_VM2VM"); + + if(flags & FWP_CONDITION_L2_IS_MALFORMED_PACKET) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\t\t\t\tFWP_CONDITION_L2_IS_MALFORMED_PACKET"); + + if(flags & FWP_CONDITION_L2_IS_IP_FRAGMENT_GROUP) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\t\t\t\tFWP_CONDITION_L2_IS_IP_FRAGMENT_GROUP"); + + if(flags & FWP_CONDITION_L2_IF_CONNECTOR_PRESENT) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\t\t\t\tFWP_CONDITION_L2_IF_CONNECTOR_PRESENT"); + return; +} + +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) + +/** + @private_function="LogValue" + + Purpose: Logs each of the FWPS_INCOMING_VALUE from the FWPS_INCOMING_VALUES in classifyFn.
+
+ Notes: Uses ETW Tracing for the logging.
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552401.aspx
+*/ +VOID LogValue(_In_ const FWP_VALUE* pValue, + _In_ BOOLEAN shouldFormat = FALSE) +{ + if(pValue) + { + NTSTATUS status = STATUS_SUCCESS; + PSTR pString = 0; + + HLPR_NEW_ARRAY(pString, + CHAR, + MAX_STRING_SIZE, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pString, + status); + + switch(pValue->type) + { + case FWP_EMPTY: + { + status = RtlStringCchPrintfA(pString, + MAX_STRING_SIZE, + "\t\t\t\ttype: FWP_EMPTY\n" + "\t\t\t\t\tuint64: %#p", + pValue->uint64); + + break; + } + case FWP_UINT8: + { + status = RtlStringCchPrintfA(pString, + MAX_STRING_SIZE, + "\t\t\t\ttype: FWP_UINT8\n" + "\t\t\t\t\tuint8: %#x", + pValue->uint8); + + break; + } + case FWP_UINT16: + { + status = RtlStringCchPrintfA(pString, + MAX_STRING_SIZE, + "\t\t\t\ttype: FWP_UINT16\n" + "\t\t\t\t\tuint16: %#x", + pValue->uint16); + + break; + } + case FWP_UINT32: + { + switch(shouldFormat) + { + case ADDRESS_VALUE: + { + BYTE pBytes[4] = {0}; + + RtlCopyMemory(pBytes, + &(pValue->uint32), + 4); + + status = RtlStringCchPrintfA(pString, + MAX_STRING_SIZE, + "\t\t\t\ttype: FWP_UINT32\n" + "\t\t\t\t\tuint32: %d.%d.%d.%d", + pBytes[3], + pBytes[2], + pBytes[1], + pBytes[0]); + + break; + } + case FLAGS_VALUE: + { + status = RtlStringCchPrintfA(pString, + MAX_STRING_SIZE, + "\t\t\t\ttype: FWP_UINT32\n" + "\t\t\t\t\tuint32: %#x", + pValue->uint32); + + LogFlags(pValue->uint32); + + break; + } + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + case L2_FLAGS_VALUE: + { + status = RtlStringCchPrintfA(pString, + MAX_STRING_SIZE, + "\t\t\t\ttype: FWP_UINT32\n" + "\t\t\t\t\tuint32: %#x", + pValue->uint32); + + LogL2Flags(pValue->uint32); + + break; + } + +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) + + default: + status = RtlStringCchPrintfA(pString, + MAX_STRING_SIZE, + "\t\t\t\ttype: FWP_UINT32\n" + "\t\t\t\t\tuint32: %#x", + pValue->uint32); + } + + break; + } + case FWP_UINT64: + { + status = RtlStringCchPrintfA(pString, + MAX_STRING_SIZE, + "\t\t\t\ttype: FWP_UINT64\n" + "\t\t\t\t\tuint64: %#I64x", + *(pValue->uint64)); + + break; + } + case FWP_INT8: + { + status = RtlStringCchPrintfA(pString, + MAX_STRING_SIZE, + "\t\t\t\ttype: FWP_INT8\n" + "\t\t\t\t\tint8: %#x", + pValue->int8); + + break; + } + case FWP_INT16: + { + status = RtlStringCchPrintfA(pString, + MAX_STRING_SIZE, + "\t\t\t\ttype: FWP_INT16\n" + "\t\t\t\t\tint16: %#x", + pValue->int16); + + break; + } + case FWP_INT32: + { + status = RtlStringCchPrintfA(pString, + MAX_STRING_SIZE, + "\t\t\t\ttype: FWP_INT32\n" + "\t\t\t\t\tint32: %#x", + pValue->int32); + + break; + } + case FWP_INT64: + { + status = RtlStringCchPrintfA(pString, + MAX_STRING_SIZE, + "\t\t\t\ttype: FWP_INT64\n" + "\t\t\t\t\tint64: %#I64x", + *(pValue->int64)); + + break; + } + case FWP_FLOAT: + { + /// Using uint32 in place of float as floats are unsafe in kernel. + /// This is safe as its part of a union. + status = RtlStringCchPrintfA(pString, + MAX_STRING_SIZE, + "\t\t\t\ttype: FWP_FLOAT\n" + "\t\t\t\t\tfloat32: %#x", + pValue->uint32); + + break; + } + case FWP_DOUBLE: + { + /// Using uint64 in place of double as doubles are unsafe in kernel. + /// This is safe as its part of a union. + status = RtlStringCchPrintfA(pString, + MAX_STRING_SIZE, + "\t\t\t\ttype: FWP_DOUBLE\n" + "\t\t\t\t\tdouble64: %#I64x", + *(pValue->uint64)); + + break; + } + case FWP_BYTE_ARRAY16_TYPE: + { + status = RtlStringCchPrintfA(pString, + MAX_STRING_SIZE, + "\t\t\t\ttype: FWP_BYTE_ARRAY16_TYPE\n" + "\t\t\t\t\tbyteArray16: %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x", + pValue->byteArray16->byteArray16[0], + pValue->byteArray16->byteArray16[1], + pValue->byteArray16->byteArray16[2], + pValue->byteArray16->byteArray16[3], + pValue->byteArray16->byteArray16[4], + pValue->byteArray16->byteArray16[5], + pValue->byteArray16->byteArray16[6], + pValue->byteArray16->byteArray16[7], + pValue->byteArray16->byteArray16[8], + pValue->byteArray16->byteArray16[9], + pValue->byteArray16->byteArray16[10], + pValue->byteArray16->byteArray16[11], + pValue->byteArray16->byteArray16[12], + pValue->byteArray16->byteArray16[13], + pValue->byteArray16->byteArray16[14], + pValue->byteArray16->byteArray16[15]); + + break; + } + case FWP_BYTE_BLOB_TYPE: + { + if(shouldFormat == UNICODE_STRING_VALUE && + KeGetCurrentIrql == PASSIVE_LEVEL) + status = RtlStringCchPrintfA(pString, + MAX_STRING_SIZE, + "\t\t\t\ttype: FWP_BYTE_BLOB_TYPE\n" + "\t\t\t\t\tbyteBlob:\n" + "\t\t\t\t\t\tsize: %d\n" + "\t\t\t\t\t\tdata: %S", + pValue->byteBlob->size, + (PWSTR)(pValue->byteBlob->data)); + else + status = RtlStringCchPrintfA(pString, + MAX_STRING_SIZE, + "\t\t\t\ttype: FWP_BYTE_BLOB_TYPE\n" + "\t\t\t\t\tbyteBlob:\n" + "\t\t\t\t\t\tsize: %d\n" + "\t\t\t\t\t\tdata: %#p", + pValue->byteBlob->size, + pValue->byteBlob->data); + + break; + } + case FWP_SID: + { + status = RtlStringCchPrintfA(pString, + MAX_STRING_SIZE, + "\t\t\t\ttype: FWP_SID\n" + "\t\t\t\t\tsid: %#p", + pValue->sid); + + break; + } + case FWP_SECURITY_DESCRIPTOR_TYPE: + { + status = RtlStringCchPrintfA(pString, + MAX_STRING_SIZE, + "\t\t\t\ttype: FWP_SECURITY_DESCRIPTOR_TYPE\n" + "\t\t\t\t\tsd:\n" + "\t\t\t\t\t\tsize: %d\n" + "\t\t\t\t\t\tdata: %#p", + pValue->sd->size, + pValue->sd->data); + break; + } + case FWP_TOKEN_INFORMATION_TYPE: + { + status = RtlStringCchPrintfA(pString, + MAX_STRING_SIZE, + "\t\t\t\ttype: FWP_TOKEN_INFORMATION_TYPE\n" + "\t\t\t\t\ttokenInformation: %#p", + pValue->tokenInformation); + + break; + } + case FWP_TOKEN_ACCESS_INFORMATION_TYPE: + { + status = RtlStringCchPrintfA(pString, + MAX_STRING_SIZE, + "\t\t\t\ttype: FWP_TOKEN_ACCESS_INFORMATION_TYPE\n" + "\t\t\t\t\ttokenAccessInformation:\n" + "\t\t\t\t\t\tsize: %d\n" + "\t\t\t\t\t\tdata: %#p", + pValue->tokenAccessInformation->size, + pValue->tokenAccessInformation->data); + break; + } + case FWP_UNICODE_STRING_TYPE: + { + status = RtlStringCchPrintfA(pString, + MAX_STRING_SIZE, + "\t\t\t\ttype: FWP_UNICODE_STRING_TYPE\n" + "\t\t\t\t\tunicodeString: %S", + pValue->unicodeString); + + break; + } + case FWP_BYTE_ARRAY6_TYPE: + { + status = RtlStringCchPrintfA(pString, + MAX_STRING_SIZE, + "\t\t\t\ttype: FWP_BYTE_ARRAY6_TYPE\n" + "\t\t\t\t\tbyteArray6: %02x:%02x:%02x:%02x:%02x:%02x", + pValue->byteArray6->byteArray6[0], + pValue->byteArray6->byteArray6[1], + pValue->byteArray6->byteArray6[2], + pValue->byteArray6->byteArray6[3], + pValue->byteArray6->byteArray6[4], + pValue->byteArray6->byteArray6[5]); + + break; + } + } + + if(status == STATUS_SUCCESS) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "%s", + pString); + + HLPR_BAIL_LABEL: + + HLPR_DELETE_ARRAY(pString, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + } + + return; +} + +/** + @private_function="LogValues" + + Purpose: Logs each of the FWPS_INCOMING_VALUE from the FWPS_INCOMING_VALUES in classifyFn.
+
+ Notes: Uses ETW Tracing for the logging.
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552401.aspx
+*/ +VOID LogValues(_In_reads_(numValues) const FWPS_INCOMING_VALUE* pValues, + _In_ UINT32 numValues, + _In_ UINT32 layerId) +{ + if(pValues) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\tincomingValue:\n"); + + for(UINT32 index = 0; + index < numValues; + index++) + { + BOOLEAN shouldFormat = FALSE; + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\tvalue: %d : %s\n", + index, + KrnlHlprFwpValueGetStringForFwpsIncomingValue(layerId, + index, + &shouldFormat)); + + LogValue(&(pValues[index].value), + shouldFormat); + } + } + + return; +} + +/** + @private_function="LogClassifyValues" + + Purpose: Logs the FWPS_INCOMING_VALUES from the classifyFn.
+
+ Notes: Uses ETW Tracing for the logging.
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552401.aspx
+*/ +VOID LogClassifyValues(_In_ const FWPS_INCOMING_VALUES* pClassifyValues) +{ + if(pClassifyValues) + { + NTSTATUS status = STATUS_SUCCESS; + PSTR pString = 0; + + HLPR_NEW_ARRAY(pString, + CHAR, + MAX_STRING_SIZE, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pString, + status); + + status = RtlStringCchPrintfA(pString, + MAX_STRING_SIZE, + "\tClassifyValues:\n" + "\t\tlayerId: %d : %s\n" + "\t\tvalueCount: %d", + pClassifyValues->layerId, + KrnlHlprFwpsLayerIDToString(pClassifyValues->layerId), + pClassifyValues->valueCount); + if(status == STATUS_SUCCESS) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "%s", + pString); + + LogValues(pClassifyValues->incomingValue, + pClassifyValues->valueCount, + pClassifyValues->layerId); + + HLPR_BAIL_LABEL: + + HLPR_DELETE_ARRAY(pString, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + } + + return; +} + +/** + @private_function="LogMetadata" + + Purpose: Logs the FWPS_INCOMING_METADATA_VALUES from the classifyFn.
+
+ Notes: Uses ETW Tracing for the logging.
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552397.aspx
+*/ +VOID LogMetadata(_In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata) +{ + if(pMetadata) + { + NTSTATUS status = STATUS_SUCCESS; + PSTR pString = 0; + + HLPR_NEW_ARRAY(pString, + CHAR, + MAX_STRING_SIZE, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pString, + status); + + status = RtlStringCchPrintfA(pString, + MAX_STRING_SIZE, + "\tMetadata:\n" + "\t\tCurrentMetadataValues: %#x", + pMetadata->currentMetadataValues); + if(status == STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "%s", + pString); + + RtlZeroMemory(pString, + MAX_STRING_SIZE); + } + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_DISCARD_REASON)) + { + status = RtlStringCchPrintfA(pString, + MAX_STRING_SIZE, + "\t\t\tdiscardMetadata:\n" + "\t\t\t\tdiscardModule: %d\n" + "\t\t\t\tdiscardReason: %d\n" + "\t\t\t\tfilterId: %I64d", + pMetadata->discardMetadata.discardModule, + pMetadata->discardMetadata.discardReason, + pMetadata->discardMetadata.filterId); + if(status == STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "%s", + pString); + + RtlZeroMemory(pString, + MAX_STRING_SIZE); + } + } + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_FLOW_HANDLE)) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\tflowHandle: %#I64x\n", + pMetadata->flowHandle); + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_IP_HEADER_SIZE)) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\tipHeaderSize: %d\n", + pMetadata->ipHeaderSize); + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_PROCESS_PATH)) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\tprocessPath: %s\n", + (PSTR)(pMetadata->processPath->data)); + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TOKEN)) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\ttoken: %#I64x\n", + pMetadata->token); + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_PROCESS_ID)) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\tprocessId: %#I64x\n", + pMetadata->processId); + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_SYSTEM_FLAGS)) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\tflags: %#x\n", + pMetadata->flags); + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_RESERVED)) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\treserved: %#I64x\n", + pMetadata->reserved); + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_SOURCE_INTERFACE_INDEX)) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\tsourceInterfaceIndex: %d\n", + pMetadata->sourceInterfaceIndex); + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_DESTINATION_INTERFACE_INDEX)) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\tdestinationInterfaceIndex: %d\n", + pMetadata->destinationInterfaceIndex); + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\ttransportHeaderSize: %d\n", + pMetadata->transportHeaderSize); + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_COMPARTMENT_ID)) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\tcompartmentId: %d\n", + pMetadata->compartmentId); + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_FRAGMENT_DATA)) + { + status = RtlStringCchPrintfA(pString, + MAX_STRING_SIZE, + "\t\t\tfragmentMetadata:\n" + "\t\t\t\tfragmentIdentification: %#x\n" + "\t\t\t\tfragmentOffset: %d\n" + "\t\t\t\tfragmentLength: %d", + pMetadata->fragmentMetadata.fragmentIdentification, + pMetadata->fragmentMetadata.fragmentOffset, + pMetadata->fragmentMetadata.fragmentLength); + if(status == STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "%s", + pString); + + RtlZeroMemory(pString, + MAX_STRING_SIZE); + } + } + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_PATH_MTU)) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\tpathMtu: %d\n", + pMetadata->pathMtu); + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_COMPLETION_HANDLE)) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\tcompletionHandle: %#I64x\n", + (UINT64)pMetadata->completionHandle); + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_ENDPOINT_HANDLE)) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\ttransportEndpointHandle: %#I64x\n", + pMetadata->transportEndpointHandle); + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_CONTROL_DATA)) + { + status = RtlStringCchPrintfA(pString, + MAX_STRING_SIZE, + "\t\t\tcontrolData:\n" + "\t\t\t\tcmsg_len: %I64d\n" + "\t\t\t\tcmsg_level: %d\n" + "\t\t\t\tcmsg_type: %d\n" + "\t\t\tcontrolDataLength: %d", + (UINT64)(pMetadata->controlData->cmsg_len), + pMetadata->controlData->cmsg_level, + pMetadata->controlData->cmsg_type, + pMetadata->controlDataLength); + if(status == STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "%s", + pString); + + RtlZeroMemory(pString, + MAX_STRING_SIZE); + } + } + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_REMOTE_SCOPE_ID)) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\tremoteScopeId: %#x\n", + pMetadata->remoteScopeId.Value); + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_PACKET_DIRECTION)) + { + PSTR pDirection = "FWP_DIRECTION_MAX"; + + if(pMetadata->packetDirection == FWP_DIRECTION_OUTBOUND) + pDirection = "FWP_DIRECTION_OUTBOUND"; + else if(pMetadata->packetDirection == FWP_DIRECTION_INBOUND) + pDirection = "FWP_DIRECTION_INBOUND"; + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\tpacketDirection: %s\n", + pDirection); + } + +#if(NTDDI_VERSION >= NTDDI_VISTASP1) + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_PACKET_SYSTEM_CRITICAL)) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\tpacketSystemCritical\n"); + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_FORWARD_LAYER_OUTBOUND_PASS_THRU)) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\tforwardLayerOutboundPassThru\n"); + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_FORWARD_LAYER_INBOUND_PASS_THRU)) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\tforwardLayerInboundPassThru\n"); + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_ALE_CLASSIFY_REQUIRED)) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\taleClassifyRequired\n"); + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_INCLUDE_HEADER)) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\theaderIncludeHeader: %#p\n", + pMetadata->headerIncludeHeader); + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\theaderIncludeHeaderLength: %d\n", + pMetadata->headerIncludeHeaderLength); + } + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_DESTINATION_PREFIX)) + { + PSTR pAddrFamily = "AF_UNSPEC"; + + if(pMetadata->destinationPrefix.Prefix.si_family == AF_INET) + pAddrFamily = "AF_INET"; + else if(pMetadata->destinationPrefix.Prefix.si_family == AF_INET6) + pAddrFamily = "AF_INET6"; + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\tdestinationPrefix:\n" + "\t\t\t\tPrefix:\n" + "\t\t\t\t\tIpv4:\n" + "\t\t\t\t\t\tsin_family: %d\n" + "\t\t\t\t\t\tsin_port: %d\n" + "\t\t\t\t\t\tsin_addr: %d.%d.%d.%d\n" + "\t\t\t\t\tIpv6:\n" + "\t\t\t\t\t\tsin6_family: %d\n" + "\t\t\t\t\t\tsin6_port: %d\n" + "\t\t\t\t\t\tsin6_flowinfo: %d\n" + "\t\t\t\t\t\tsin6_addr: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n" + "\t\t\t\t\tsi_family: %s\n" + "\t\t\t\tPrefixLength: %d\n", + pMetadata->destinationPrefix.Prefix.Ipv4.sin_family, + pMetadata->destinationPrefix.Prefix.Ipv4.sin_port, + pMetadata->destinationPrefix.Prefix.Ipv4.sin_addr.S_un.S_un_b.s_b1, + pMetadata->destinationPrefix.Prefix.Ipv4.sin_addr.S_un.S_un_b.s_b2, + pMetadata->destinationPrefix.Prefix.Ipv4.sin_addr.S_un.S_un_b.s_b3, + pMetadata->destinationPrefix.Prefix.Ipv4.sin_addr.S_un.S_un_b.s_b4, + pMetadata->destinationPrefix.Prefix.Ipv6.sin6_family, + pMetadata->destinationPrefix.Prefix.Ipv6.sin6_port, + pMetadata->destinationPrefix.Prefix.Ipv6.sin6_flowinfo, + pMetadata->destinationPrefix.Prefix.Ipv6.sin6_addr.u.Word[0], + pMetadata->destinationPrefix.Prefix.Ipv6.sin6_addr.u.Word[1], + pMetadata->destinationPrefix.Prefix.Ipv6.sin6_addr.u.Word[2], + pMetadata->destinationPrefix.Prefix.Ipv6.sin6_addr.u.Word[3], + pMetadata->destinationPrefix.Prefix.Ipv6.sin6_addr.u.Word[4], + pMetadata->destinationPrefix.Prefix.Ipv6.sin6_addr.u.Word[5], + pMetadata->destinationPrefix.Prefix.Ipv6.sin6_addr.u.Word[6], + pMetadata->destinationPrefix.Prefix.Ipv6.sin6_addr.u.Word[7], + pAddrFamily, + pMetadata->destinationPrefix.PrefixLength); + } + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_ETHER_FRAME_LENGTH)) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\tframeLength: %d\n", + pMetadata->frameLength); + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_PARENT_ENDPOINT_HANDLE)) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\tparentEndpointHandle: %#I64x\n", + pMetadata->parentEndpointHandle); + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_ICMP_ID_AND_SEQUENCE)) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\ticmpIdAndSequence: %#x\n", + pMetadata->icmpIdAndSequence); + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_LOCAL_REDIRECT_TARGET_PID)) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\tlocalRedirectTargetPID: %#x\n", + pMetadata->localRedirectTargetPID); + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_ORIGINAL_DESTINATION)) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\toriginalDestination: %#p\n", + pMetadata->originalDestination); + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_REDIRECT_RECORD_HANDLE)) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\tredirectRecords: %#p\n", + pMetadata->redirectRecords); + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_SUB_PROCESS_TAG)) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\tsubProcessTag: %#p\n", + pMetadata->subProcessTag); + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\tCurrentL2MetadataValues: %#x\n", + pMetadata->currentL2MetadataValues); + + if(FWPS_IS_L2_METADATA_FIELD_PRESENT(pMetadata, + FWPS_L2_METADATA_FIELD_ETHERNET_MAC_HEADER_SIZE)) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\tethernetMacHeaderSize: %d\n", + pMetadata->ethernetMacHeaderSize); + + if(FWPS_IS_L2_METADATA_FIELD_PRESENT(pMetadata, + FWPS_L2_METADATA_FIELD_WIFI_OPERATION_MODE)) + { + PSTR WIFIMode = "UNKNOWN"; + + if(pMetadata->wiFiOperationMode == DOT11_OPERATION_MODE_EXTENSIBLE_AP) + WIFIMode = "DOT11_OPERATION_MODE_EXTENSIBLE_AP"; + else if(pMetadata->wiFiOperationMode == DOT11_OPERATION_MODE_EXTENSIBLE_STATION) + WIFIMode = "DOT11_OPERATION_MODE_EXTENSIBLE_STATION"; + else if(pMetadata->wiFiOperationMode == DOT11_OPERATION_MODE_NETWORK_MONITOR) + WIFIMode = "DOT11_OPERATION_MODE_NETWORK_MONITOR"; + else if(pMetadata->wiFiOperationMode == DOT11_OPERATION_MODE_WFD_DEVICE) + WIFIMode = "DOT11_OPERATION_MODE_WFD_DEVICE"; + else if(pMetadata->wiFiOperationMode == DOT11_OPERATION_MODE_WFD_GROUP_OWNER) + WIFIMode = "DOT11_OPERATION_MODE_WFD_GROUP_OWNER"; + else if(pMetadata->wiFiOperationMode == DOT11_OPERATION_MODE_WFD_CLIENT) + WIFIMode = "DOT11_OPERATION_MODE_WFD_CLIENT"; + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\twiFiOperationMode: %s\n", + WIFIMode); + } + + if(FWPS_IS_L2_METADATA_FIELD_PRESENT(pMetadata, + FWPS_L2_METADATA_FIELD_VSWITCH_SOURCE_PORT_ID)) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\tvSwitchSourcePortId: %#x\n", + pMetadata->vSwitchSourcePortId); + + if(FWPS_IS_L2_METADATA_FIELD_PRESENT(pMetadata, + FWPS_L2_METADATA_FIELD_VSWITCH_SOURCE_NIC_INDEX)) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\tvSwitchSourceNicIndex: %#x\n", + pMetadata->vSwitchSourceNicIndex); + + if(FWPS_IS_L2_METADATA_FIELD_PRESENT(pMetadata, + FWPS_L2_METADATA_FIELD_VSWITCH_PACKET_CONTEXT)) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\tvSwitchPacketContext: %#p\n", + pMetadata->vSwitchPacketContext); + + if(FWPS_IS_L2_METADATA_FIELD_PRESENT(pMetadata, + FWPS_L2_METADATA_FIELD_VSWITCH_DESTINATION_PORT_ID)) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\tvSwitchDestinationPortId: %#x\n", + pMetadata->vSwitchDestinationPortId); + +#if(NTDDI_VERSION >= NTDDI_WINBLUE) + + if(FWPS_IS_L2_METADATA_FIELD_PRESENT(pMetadata, + FWPS_L2_METADATA_FIELD_RESERVED)) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\treserved1: %#I64x\n", + pMetadata->reserved1); + +#endif /// (NTDDI_VERSION >= NTDDI_WINBLUE) +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) +#endif /// (NTDDI_VERSION >= NTDDI_VISTASP1) + + HLPR_BAIL_LABEL: + + HLPR_DELETE_ARRAY(pString, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + } + + return; +} + +/** + @private_function="LogFilter" + + Purpose: Logs the FWPS_FILTER from the classifyFn.
+
+ Notes: Uses ETW Tracing for the logging.
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552387.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552389.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/HH439768.aspx
+*/ +VOID LogFilter(_In_ const FWPS_FILTER* pFilter) +{ + if(pFilter) + { + NTSTATUS status = STATUS_SUCCESS; + PSTR pType = "FWP_EMPTY"; + UINT64 data = 0; + PSTR pAction = "FWP_ACTION_CALLOUT_UNKNOWN"; + PSTR pString = 0; + + if(pFilter->weight.type == FWP_UINT64) + { + pType = "FWP_UINT64"; + + if(pFilter->weight.uint64) + data = *(pFilter->weight.uint64); + } + + if(pFilter->action.type == FWP_ACTION_CALLOUT_TERMINATING) + pAction = "FWP_ACTION_CALLOUT_TERMINATING"; + else if(pFilter->action.type == FWP_ACTION_CALLOUT_INSPECTION) + pAction = "FWP_ACTION_CALLOUT_INSPECTION"; + + HLPR_NEW_ARRAY(pString, + CHAR, + MAX_STRING_SIZE, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pString, + status); + + status = RtlStringCchPrintfA(pString, + MAX_STRING_SIZE, + "\tFilter:\n" + "\t\tfilterId: %#I64x\n" + "\t\tweight:\n" + "\t\t\ttype: %s\n" + "\t\t\tuint64: %#I64x\n" + "\t\tsubLayerWeight: %#x\n" + "\t\tflags: %#x\n" + "\t\tnumFilterConditions: %d:\n" + "\t\tfilterCondition: %#p\n" + "\t\taction:\n" + "\t\t\ttype: %s\n" + "\t\t\tcalloutId: %#x\n" + "\t\tcontext: %#I64d\n" + "\t\tproviderContext: %#p\n", + pFilter->filterId, + pType, + data, + pFilter->subLayerWeight, + pFilter->flags, + pFilter->numFilterConditions, + pFilter->filterCondition, + pAction, + pFilter->action.calloutId, + pFilter->context, + pFilter->providerContext); + if(status == STATUS_SUCCESS) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "%s", + pString); + + HLPR_BAIL_LABEL: + + HLPR_DELETE_ARRAY(pString, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + } + + return; +} + +/** + @private_function="LogClassifyOut" + + Purpose: Logs the FWPS_CLASSIFY_OUT from the classifyFn.
+
+ Notes: Uses ETW Tracing for the logging.
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551229.aspx
+*/ +VOID LogClassifyOut(_In_ const FWPS_CLASSIFY_OUT* pClassifyOut) +{ + if(pClassifyOut) + { + NTSTATUS status = STATUS_SUCCESS; + PSTR pAction = "FWP_ACTION_NONE"; + PSTR pString = 0; + + + if(pClassifyOut->actionType == FWP_ACTION_BLOCK) + pAction = "FWP_ACTION_BLOCK"; + else if(pClassifyOut->actionType == FWP_ACTION_CONTINUE) + pAction = "FWP_ACTION_CONTINUE"; + else if(pClassifyOut->actionType == FWP_ACTION_NONE_NO_MATCH) + pAction = "FWP_ACTION_NONE_NO_MATCH"; + else if(pClassifyOut->actionType == FWP_ACTION_PERMIT) + pAction = "FWP_ACTION_PERMIT"; + + HLPR_NEW_ARRAY(pString, + CHAR, + MAX_STRING_SIZE, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pString, + status); + + status = RtlStringCchPrintfA(pString, + MAX_STRING_SIZE, + "\tClassifyOut:\n" + "\t\tactionType: %s\n" + "\t\toutContext: %#I64x\n" + "\t\tfilterId: %I64d\n" + "\t\trights: %#x", + pAction, + pClassifyOut->outContext, + pClassifyOut->filterId, + pClassifyOut->rights); + if(status == STATUS_SUCCESS) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "%s", + pString); + + if(pClassifyOut->rights & FWPS_RIGHT_ACTION_WRITE) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\tFWPS_RIGHT_ACTION_WRITE"); + + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\tflags: %#x", + pClassifyOut->flags); + + if(pClassifyOut->flags & FWPS_CLASSIFY_OUT_FLAG_ABSORB) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\tFWPS_CLASSIFY_OUT_FLAG_ABSORB"); + + if(pClassifyOut->flags & FWPS_CLASSIFY_OUT_FLAG_BUFFER_LIMIT_REACHED) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\tFWPS_CLASSIFY_OUT_FLAG_BUFFER_LIMIT_REACHED"); + + if(pClassifyOut->flags & FWPS_CLASSIFY_OUT_FLAG_NO_MORE_DATA) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\t\tFWPS_CLASSIFY_OUT_FLAG_NO_MORE_DATA"); + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\treserved: %#x", + pClassifyOut->reserved); + + HLPR_BAIL_LABEL: + + HLPR_DELETE_ARRAY(pString, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + } + + return; +} + +/** + @private_function="LogEthernetIIHeader" + + Purpose: Logs the Ethernet II Header into a more easily readable format.
+
+ Notes: Uses ETW Tracing for the logging, which is not ideal in a real world scenario.
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/FF562859.aspx
+*/ +VOID LogEthernetIIHeader(_In_ ETHERNET_II_HEADER* pEthernetIIHeader, + _In_ UINT32 vlanID = 0) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> LogEthernetIIHeader()\n"); + +#endif /// DBG + + NT_ASSERT(pEthernetIIHeader); + + NTSTATUS status = STATUS_SUCCESS; + PSTR pString = 0; + + HLPR_NEW_ARRAY(pString, + CHAR, + MAX_STRING_SIZE, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pString, + status); + + if(vlanID) + status = RtlStringCchPrintfA(pString, + MAX_STRING_SIZE, + "\n" + "\t\t 0 1 2\n" + "\t\t 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3\n" + "\t\t+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n" + "\t\t| |\n" + "\t\t+ Destination MAC Address +\n" + "\t\t| |\n" + "\t\t| %02x:%02x:%02x:%02x:%02x:%02x |\n" + "\t\t+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n" + "\t\t| |\n" + "\t\t+ Source MAC Address +\n" + "\t\t| |\n" + "\t\t| %02x:%02x:%02x:%02x:%02x:%02x |\n" + "\t\t+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n" + "\t\t| VLAN ID ... >\n" + "\t\t| >\n" + "\t\t+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n" + "\t\t|VLAN ID (cont.)| Type |\n" + "\t\t| | |\n" + "\t\t+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n", + pEthernetIIHeader->pDestinationAddress[0], + pEthernetIIHeader->pDestinationAddress[1], + pEthernetIIHeader->pDestinationAddress[2], + pEthernetIIHeader->pDestinationAddress[3], + pEthernetIIHeader->pDestinationAddress[4], + pEthernetIIHeader->pDestinationAddress[5], + pEthernetIIHeader->pSourceAddress[0], + pEthernetIIHeader->pSourceAddress[1], + pEthernetIIHeader->pSourceAddress[2], + pEthernetIIHeader->pSourceAddress[3], + pEthernetIIHeader->pSourceAddress[4], + pEthernetIIHeader->pSourceAddress[5]); + else + status = RtlStringCchPrintfA(pString, + MAX_STRING_SIZE, + "\n" + "\t\t 0 1 2\n" + "\t\t 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3\n" + "\t\t+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n" + "\t\t| |\n" + "\t\t+ Destination MAC Address +\n" + "\t\t| |\n" + "\t\t| %02x:%02x:%02x:%02x:%02x:%02x |\n" + "\t\t+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n" + "\t\t| |\n" + "\t\t+ Source MAC Address +\n" + "\t\t| |\n" + "\t\t| %02x:%02x:%02x:%02x:%02x:%02x |\n" + "\t\t+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n" + "\t\t| Type | Data... |\n" + "\t\t| %04x | |\n" + "\t\t+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n", + pEthernetIIHeader->pDestinationAddress[0], + pEthernetIIHeader->pDestinationAddress[1], + pEthernetIIHeader->pDestinationAddress[2], + pEthernetIIHeader->pDestinationAddress[3], + pEthernetIIHeader->pDestinationAddress[4], + pEthernetIIHeader->pDestinationAddress[5], + pEthernetIIHeader->pSourceAddress[0], + pEthernetIIHeader->pSourceAddress[1], + pEthernetIIHeader->pSourceAddress[2], + pEthernetIIHeader->pSourceAddress[3], + pEthernetIIHeader->pSourceAddress[4], + pEthernetIIHeader->pSourceAddress[5], + ntohs(pEthernetIIHeader->type)); + + if(status == STATUS_SUCCESS) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\tEthernet II Header:%s", + pString); + + HLPR_BAIL_LABEL: + + HLPR_DELETE_ARRAY(pString, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- LogEthernetIIHeader [status: %#x]\n", + status); + +#else + + /// Used to get around preFast Warning 28931 + UNREFERENCED_PARAMETER(status); + +#endif /// DBG + + return; +} + +/** + @private_function="LogEthernetSNAPHeader" + + Purpose: Logs the MAC Ethernet SNAP Header into a more easily readable format.
+
+ Notes: Uses ETW Tracing for the logging, which is not ideal in a real world scenario.
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/FF562859.aspx
+*/ +VOID LogEthernetSNAPHeader(_In_ ETHERNET_SNAP_HEADER* pEthernetSNAPHeader) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> LogEthernetSNAPHeader()\n"); + +#endif /// DBG + + NT_ASSERT(pEthernetSNAPHeader); + + NTSTATUS status = STATUS_SUCCESS; + PSTR pString = 0; + + HLPR_NEW_ARRAY(pString, + CHAR, + MAX_STRING_SIZE, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pString, + status); + + status = RtlStringCchPrintfA(pString, + MAX_STRING_SIZE, + "\n" + "\t\t 0 1 2\n" + "\t\t 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3\n" + "\t\t+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n" + "\t\t| |\n" + "\t\t+ Destination MAC Address +\n" + "\t\t| |\n" + "\t\t| %02x:%02x:%02x:%02x:%02x:%02x |\n" + "\t\t+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n" + "\t\t| |\n" + "\t\t+ Source MAC Address +\n" + "\t\t| |\n" + "\t\t| %02x:%02x:%02x:%02x:%02x:%02x |\n" + "\t\t+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n" + "\t\t| Length | DSAP |\n" + "\t\t| %04x | %02x |\n" + "\t\t+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n" + "\t\t| SSAP | Control Byte | OUI ... >\n" + "\t\t| %02x | %02x | %02x |\n" + "\t\t+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n" + "\t\t< OUI (cont.) | Type ... >\n" + "\t\t< %02x%02x | %04x >\n" + "\t\t+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n" + "\t\t< Type (cont.) | Data ... |\n" + "\t\t< | |\n" + "\t\t+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n", + pEthernetSNAPHeader->pDestinationAddress[0], + pEthernetSNAPHeader->pDestinationAddress[1], + pEthernetSNAPHeader->pDestinationAddress[2], + pEthernetSNAPHeader->pDestinationAddress[3], + pEthernetSNAPHeader->pDestinationAddress[4], + pEthernetSNAPHeader->pDestinationAddress[5], + pEthernetSNAPHeader->pSourceAddress[0], + pEthernetSNAPHeader->pSourceAddress[1], + pEthernetSNAPHeader->pSourceAddress[2], + pEthernetSNAPHeader->pSourceAddress[3], + pEthernetSNAPHeader->pSourceAddress[4], + pEthernetSNAPHeader->pSourceAddress[5], + pEthernetSNAPHeader->length, + pEthernetSNAPHeader->destinationSAP, + pEthernetSNAPHeader->sourceSAP, + pEthernetSNAPHeader->controlByte, + pEthernetSNAPHeader->pOUI[0], + pEthernetSNAPHeader->pOUI[1], + pEthernetSNAPHeader->pOUI[2], + ntohs(pEthernetSNAPHeader->type)); + if(status == STATUS_SUCCESS) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\tEthernet SNAP Header:%s", + pString); + + HLPR_BAIL_LABEL: + + HLPR_DELETE_ARRAY(pString, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- LogEthernetSNAPHeader() [status: %#x]\n", + status); + +#else + + /// Used to get around preFast Warning 28931 + UNREFERENCED_PARAMETER(status); + +#endif /// DBG + + return; +} + +/** + @private_function="LogIPv4Header" + + Purpose: Logs the IPv4 Header into a more easily readable format.
+
+ Notes: Uses ETW Tracing for the logging, which is not ideal in a real world scenario.
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/FF562859.aspx
+*/ +VOID LogIPv4Header(_In_ IP_HEADER_V4* pIPv4Header) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> LogIPv4Header()\n"); + +#endif /// DBG + + NT_ASSERT(pIPv4Header); + + NTSTATUS status = STATUS_SUCCESS; + PSTR pString = 0; + + HLPR_NEW_ARRAY(pString, + CHAR, + MAX_STRING_SIZE, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pString, + status); + + status = RtlStringCchPrintfA(pString, + MAX_STRING_SIZE, + "\n" + "\t\t 0 1 2 3\n" + "\t\t 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1\n" + "\t\t+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n" + "\t\t|Version| IHL |Type of Service| Total Length |\n" + "\t\t| %01x | %01x | %02x | %04x |\n" + "\t\t+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n" + "\t\t| Identification |Flags| Fragment Offset |\n" + "\t\t| %04x | %01x | %04x |\n" + "\t\t+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n" + "\t\t| Time to Live | Protocol | Header Checksum |\n" + "\t\t| %02x | %02x | %04x |\n" + "\t\t+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n" + "\t\t| Source Address |\n" + "\t\t| %08x |\n" + "\t\t+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n" + "\t\t| Destination Address |\n" + "\t\t| %08x |\n" + "\t\t+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n" + "\t\t| Options | Padding |\n" + "\t\t+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n", + pIPv4Header->version, + pIPv4Header->headerLength, + pIPv4Header->typeOfService, + pIPv4Header->totalLength, + ntohs(pIPv4Header->identification), + pIPv4Header->flags, + pIPv4Header->fragmentOffset, + pIPv4Header->timeToLive, + pIPv4Header->protocol, + ntohs(pIPv4Header->checksum), + ntohl((*((UINT32*)pIPv4Header->pSourceAddress))), + ntohl((*((UINT32*)pIPv4Header->pDestinationAddress)))); + if(status == STATUS_SUCCESS) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\tIPv4 Header:%s", + pString); + + HLPR_BAIL_LABEL: + + HLPR_DELETE_ARRAY(pString, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- LogIPv4Header() [status: %#x]\n", + status); + +#else + + /// Used to get around preFast Warning 28931 + UNREFERENCED_PARAMETER(status); + +#endif /// DBG + + return; +} + +/** + @private_function="LogIPv6Header" + + Purpose: Logs the IPv6 Header into a more easily readable format.
+
+ Notes: Uses ETW Tracing for the logging, which is not ideal in a real world scenario.
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/FF562859.aspx
+*/ +VOID LogIPv6Header(_In_ IP_HEADER_V6* pIPv6Header) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> LogIPv6Header()\n"); + +#endif /// DBG + + NT_ASSERT(pIPv6Header); + + NTSTATUS status = STATUS_SUCCESS; + PSTR pString = 0; + UINT32 versionTrafficClassAndFlowLabel = 0; + + RtlCopyMemory(&versionTrafficClassAndFlowLabel, + pIPv6Header->pVersionTrafficClassAndFlowLabel, + 4); + + HLPR_NEW_ARRAY(pString, + CHAR, + MAX_STRING_SIZE, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pString, + status); + + status = RtlStringCchPrintfA(pString, + MAX_STRING_SIZE, + "\n" + "\t\t 0 1 2 3\n" + "\t\t 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1\n" + "\t\t+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n" + "\t\t|Version| Traffic Class | Flow Label |\n" + "\t\t| %01x | %02x | %05x |\n" + "\t\t+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n" + "\t\t| Payload Length | Next Header | Hop Limit |\n" + "\t\t| %04x | %02x | %02x |\n" + "\t\t+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n" + "\t\t| |\n" + "\t\t+ +\n" + "\t\t| |\n" + "\t\t+ Source Address +\n" + "\t\t| |\n" + "\t\t+ +\n" + "\t\t| %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x |\n" + "\t\t+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n" + "\t\t| |\n" + "\t\t+ +\n" + "\t\t| |\n" + "\t\t+ Destination Address +\n" + "\t\t| |\n" + "\t\t+ +\n" + "\t\t| %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x |\n" + "\t\t+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n", + pIPv6Header->version.value, + (ntohl(versionTrafficClassAndFlowLabel) & 0x0FF00000) >> 20, + ntohl(versionTrafficClassAndFlowLabel) & 0x000FFFFF, + ntohs(pIPv6Header->payloadLength), + pIPv6Header->nextHeader, + pIPv6Header->hopLimit, + pIPv6Header->pSourceAddress[0], + pIPv6Header->pSourceAddress[1], + pIPv6Header->pSourceAddress[2], + pIPv6Header->pSourceAddress[3], + pIPv6Header->pSourceAddress[4], + pIPv6Header->pSourceAddress[5], + pIPv6Header->pSourceAddress[6], + pIPv6Header->pSourceAddress[7], + pIPv6Header->pSourceAddress[8], + pIPv6Header->pSourceAddress[9], + pIPv6Header->pSourceAddress[10], + pIPv6Header->pSourceAddress[11], + pIPv6Header->pSourceAddress[12], + pIPv6Header->pSourceAddress[13], + pIPv6Header->pSourceAddress[14], + pIPv6Header->pSourceAddress[15], + pIPv6Header->pDestinationAddress[0], + pIPv6Header->pDestinationAddress[1], + pIPv6Header->pDestinationAddress[2], + pIPv6Header->pDestinationAddress[3], + pIPv6Header->pDestinationAddress[4], + pIPv6Header->pDestinationAddress[5], + pIPv6Header->pDestinationAddress[6], + pIPv6Header->pDestinationAddress[7], + pIPv6Header->pDestinationAddress[8], + pIPv6Header->pDestinationAddress[9], + pIPv6Header->pDestinationAddress[10], + pIPv6Header->pDestinationAddress[11], + pIPv6Header->pDestinationAddress[12], + pIPv6Header->pDestinationAddress[13], + pIPv6Header->pDestinationAddress[14], + pIPv6Header->pDestinationAddress[15]); + if(status == STATUS_SUCCESS) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\tIPv6 Header:%s", + pString); + + HLPR_BAIL_LABEL: + + HLPR_DELETE_ARRAY(pString, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- LogIPv6Header [status: %#x]\n", + status); +#else + + /// Used to get around preFast Warning 28931 + UNREFERENCED_PARAMETER(status); + +#endif /// DBG + + return; +} + +/** + @private_function="LogIPHeader" + + Purpose: Proxy IP Header logging to appropriate logging function base on IP version.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +VOID LogIPHeader(_In_ VOID* pIPHeader, + _In_ UINT8 ipVersion) +{ + NT_ASSERT(pIPHeader); + + if(ipVersion == IPV4) + LogIPv4Header((IP_HEADER_V4*)pIPHeader); + else + LogIPv6Header((IP_HEADER_V6*)pIPHeader); + + return; +} + +/** + @private_function="LogICMPv4Header" + + Purpose: Logs the ICMPv4 Header into a more easily readable format.
+
+ Notes: Uses ETW Tracing for the logging, which is not ideal in a real world scenario.
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/FF562859.aspx
+*/ +VOID LogICMPv4Header(_In_ ICMP_HEADER_V4* pICMPv4Header) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> LogICMPv4Header()\n"); + +#endif /// DBG + + NT_ASSERT(pICMPv4Header); + + NTSTATUS status = STATUS_SUCCESS; + PSTR pString = 0; + + HLPR_NEW_ARRAY(pString, + CHAR, + MAX_STRING_SIZE, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pString, + status); + + status = RtlStringCchPrintfA(pString, + MAX_STRING_SIZE, + "\n" + "\t\t 0 1 2 3\n" + "\t\t 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1\n" + "\t\t+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n" + "\t\t| Type | Code | Checksum |\n" + "\t\t| %02x | %02x | %04x |\n" + "\t\t+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n" + "\t\t| Variable (Dependent on Type / Code) |\n" + "\t\t+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n", + pICMPv4Header->type, + pICMPv4Header->code, + ntohs(pICMPv4Header->checksum)); + if(status == STATUS_SUCCESS) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\tICMPv4 Header:%s", + pString); + + HLPR_BAIL_LABEL: + + HLPR_DELETE_ARRAY(pString, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- LogICMPv4Header [status: %#x]\n", + status); + +#else + + /// Used to get around preFast Warning 28931 + UNREFERENCED_PARAMETER(status); + +#endif /// DBG + + return; +} + +/** + @private_function="LogICMPv6Header" + + Purpose: Logs the ICMPv6 Header into a more easily readable format.
+
+ Notes: Uses ETW Tracing for the logging, which is not ideal in a real world scenario.
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/FF562859.aspx
+*/ +VOID LogICMPv6Header(_In_ ICMP_HEADER_V6* pICMPv6Header) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> LogICMPv6Header()\n"); + +#endif /// DBG + + NT_ASSERT(pICMPv6Header); + + NTSTATUS status = STATUS_SUCCESS; + PSTR pString = 0; + + HLPR_NEW_ARRAY(pString, + CHAR, + MAX_STRING_SIZE, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pString, + status); + + status = RtlStringCchPrintfA(pString, + MAX_STRING_SIZE, + "\n" + "\t\t 0 1 2 3\n" + "\t\t 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1\n" + "\t\t+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n" + "\t\t| Type | Code | Checksum |\n" + "\t\t| %02x | %02x | %04x |\n" + "\t\t+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n" + "\t\t| Variable (Dependent on Type / Code) |\n" + "\t\t+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n", + pICMPv6Header->type, + pICMPv6Header->code, + ntohs(pICMPv6Header->checksum)); + if(status == STATUS_SUCCESS) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\tICMPv6 Header:%s", + pString); + + HLPR_BAIL_LABEL: + + HLPR_DELETE_ARRAY(pString, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- LogICMPv6Header() [status: %#x]\n", + status); + +#else + + /// Used to get around preFast Warning 28931 + UNREFERENCED_PARAMETER(status); + +#endif /// DBG + + return; +} + +/** + @private_function="LogTCPHeader" + + Purpose: Logs the TCP Header into a more easily readable format.
+
+ Notes: Uses ETW Tracing for the logging, which is not ideal in a real world scenario.
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/FF562859.aspx
+*/ +VOID LogTCPHeader(_In_ TCP_HEADER* pTCPHeader) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> LogTCPHeader()\n"); + +#endif /// DBG + + NT_ASSERT(pTCPHeader); + + NTSTATUS status = STATUS_SUCCESS; + PSTR pString = 0; + + HLPR_NEW_ARRAY(pString, + CHAR, + MAX_STRING_SIZE, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pString, + status); + + status = RtlStringCchPrintfA(pString, + MAX_STRING_SIZE, + "\n" + "\t\t 0 1 2 3\n" + "\t\t 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1\n" + "\t\t+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n" + "\t\t| Source Port | Destination Port |\n" + "\t\t| %04x | %04x |\n" + "\t\t+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n" + "\t\t| Sequence Number |\n" + "\t\t| %08x |\n" + "\t\t+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n" + "\t\t| Acknowledgment Number |\n" + "\t\t| %08x |\n" + "\t\t+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n" + "\t\t|Offset |Rsvd |N|C|E|U|A|P|R|S|F| Window |\n" + "\t\t| %01x | %01x |%01x|%01x|%01x|%01x|%01x|%01x|%01x|%01x|%01x| %04x |\n" + "\t\t+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n" + "\t\t| Checksum | Urgent Pointer |\n" + "\t\t| %04x | %04x |\n" + "\t\t+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n" + "\t\t| Options | Padding |\n" + "\t\t+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n", + ntohs(pTCPHeader->sourcePort), + ntohs(pTCPHeader->destinationPort), + ntohl(pTCPHeader->sequenceNumber), + ntohl(pTCPHeader->acknowledgementNumber), + pTCPHeader->dORNS.dataOffset, + pTCPHeader->dORNS.reserved, + pTCPHeader->dORNS.nonceSum, + pTCPHeader->CWR, + pTCPHeader->ECE, + pTCPHeader->URG, + pTCPHeader->ACK, + pTCPHeader->PSH, + pTCPHeader->RST, + pTCPHeader->SYN, + pTCPHeader->FIN, + ntohs(pTCPHeader->window), + ntohs(pTCPHeader->checksum), + ntohs(pTCPHeader->urgentPointer)); + if(status == STATUS_SUCCESS) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\tTCP Header:%s", + pString); + + HLPR_BAIL_LABEL: + + HLPR_DELETE_ARRAY(pString, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- LogTCPHeader() [status: %#x]\n", + status); + +#else + + /// Used to get around preFast Warning 28931 + UNREFERENCED_PARAMETER(status); + +#endif /// DBG + + return; +} + +/** + @private_function="LogUDPHeader" + + Purpose: Logs the UDP Header into a more easily readable format.
+
+ Notes: Uses ETW Tracing for the logging, which is not ideal in a real world scenario.
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/FF562859.aspx
+*/ +VOID LogUDPHeader(_In_ UDP_HEADER* pUDPHeader) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> LogUDPHeader()\n"); + +#endif /// DBG + + NT_ASSERT(pUDPHeader); + + NTSTATUS status = STATUS_SUCCESS; + PSTR pString = 0; + + HLPR_NEW_ARRAY(pString, + CHAR, + MAX_STRING_SIZE, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pString, + status); + + status = RtlStringCchPrintfA(pString, + MAX_STRING_SIZE, + "\n" + "\t\t 0 1 2 3\n" + "\t\t 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1\n" + "\t\t+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n" + "\t\t| Source Port | Destination Port |\n" + "\t\t| %04x | %04x |\n" + "\t\t+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n" + "\t\t| Length | Checksum |\n" + "\t\t| %04x | %04x |\n" + "\t\t+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n", + ntohs(pUDPHeader->sourcePort), + ntohs(pUDPHeader->destinationPort), + ntohs(pUDPHeader->length), + ntohs(pUDPHeader->checksum)); + if(status == STATUS_SUCCESS) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\tUDP Header:%s", + pString); + + HLPR_BAIL_LABEL: + + HLPR_DELETE_ARRAY(pString, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- LogUDPHeader() [status: %#x]\n", + status); + +#else + + /// Used to get around preFast Warning 28931 + UNREFERENCED_PARAMETER(status); + +#endif /// DBG + + return; +} + +/** + @private_function="LogTransportHeader" + + Purpose: Proxy Transport Header logging to appropriate logging function base on IP protocol.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +VOID LogTransportHeader(_In_ VOID* pTransportHeader, + _In_ UINT8 ipProtocol) +{ + NT_ASSERT(pTransportHeader); + + switch(ipProtocol) + { + case ICMPV4: + { + LogICMPv4Header((ICMP_HEADER_V4*)pTransportHeader); + + break; + } + case TCP: + { + LogTCPHeader((TCP_HEADER*)pTransportHeader); + + break; + } + case UDP: + { + LogUDPHeader((UDP_HEADER*)pTransportHeader); + + break; + } + case ICMPV6: + { + LogICMPv6Header((ICMP_HEADER_V6*)pTransportHeader); + + break; + } + } + + return; +} + +#if(NTDDI_VERSION >= NTDDI_WIN8) + +/** + @private_function="PerformBasicPacketExaminationAtInboundMACFrame" + + Purpose: Examines and logs the contents of the MAC Header and Ip and Transport Headers if + available.
+
+ Notes: Applies to the following framing layers:
+ FWPM_LAYER_INBOUND_MAC_FRAME_ETHERNET
+ FWPM_LAYER_INBOUND_MAC_FRAME_NATIVE
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/FF560703.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/FF564527.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/FF562631.aspx
+*/ +VOID PerformBasicPacketExaminationAtInboundMACFrame(_In_ CLASSIFY_DATA* pClassifyData) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PerformBasicPacketExaminationAtInboundMACFrame()\n"); + +#endif /// DBG + + NT_ASSERT(pClassifyData); + + NTSTATUS status = STATUS_SUCCESS; + FWPS_INCOMING_VALUES* pClassifyValues = (FWPS_INCOMING_VALUES*)pClassifyData->pClassifyValues; + FWPS_INCOMING_METADATA_VALUES* pMetadata = (FWPS_INCOMING_METADATA_VALUES*)pClassifyData->pMetadataValues; + UINT32 bytesRetreated = 0; + UINT32 bytesAdvanced = 0; + UINT32 macHeaderSize = 0; + UINT32 ipHeaderSize = 0; + UINT16 etherType = 0; + UINT8 ipProtocol = 0; + VOID* pHeader = 0; + BOOLEAN needToFreeMemory = FALSE; + + if(FWPS_IS_L2_METADATA_FIELD_PRESENT(pMetadata, + FWPS_L2_METADATA_FIELD_ETHERNET_MAC_HEADER_SIZE)) + macHeaderSize = pMetadata->ethernetMacHeaderSize; + + if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_MAC_FRAME_ETHERNET) + { + FWP_VALUE* pDestinationAddressValue = 0; + FWP_VALUE* pSourceAddressValue = 0; + FWP_VALUE* pEtherTypeValue = 0; + FWP_VALUE* pVLANIDValue = 0; + + pSourceAddressValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_MAC_REMOTE_ADDRESS); + HLPR_BAIL_ON_NULL_POINTER(pSourceAddressValue); + + pDestinationAddressValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_MAC_LOCAL_ADDRESS); + HLPR_BAIL_ON_NULL_POINTER(pDestinationAddressValue); + + pEtherTypeValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_ETHER_TYPE); + HLPR_BAIL_ON_NULL_POINTER(pEtherTypeValue); + + pVLANIDValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_VLAN_ID); + HLPR_BAIL_ON_NULL_POINTER(pVLANIDValue); + + /// Initial offset is at the IP Header, so retreat the size of the MAC Header ... + status = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pClassifyData->pPacket), + macHeaderSize, + 0, + 0); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketExaminationAtInboundMACFrame: NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + + HLPR_BAIL; + } + else + bytesRetreated = macHeaderSize; + + status = KrnlHlprMACHeaderGet((NET_BUFFER_LIST*)pClassifyData->pPacket, + &pHeader, + &needToFreeMemory, + macHeaderSize); + HLPR_BAIL_ON_FAILURE(status); + + if(macHeaderSize == sizeof(ETHERNET_SNAP_HEADER)) + { + ETHERNET_SNAP_HEADER* pEthernetSNAPHeader = (ETHERNET_SNAP_HEADER*)pHeader; + + NT_ASSERT(ntohs(pEthernetSNAPHeader->type) == pEtherTypeValue->uint16); + + LogEthernetSNAPHeader(pEthernetSNAPHeader); + } + else + { + ETHERNET_II_HEADER* pEthernetIIHeader = (ETHERNET_II_HEADER*)pHeader; + + NT_ASSERT(RtlCompareMemory(pEthernetIIHeader->pDestinationAddress, + pDestinationAddressValue->byteArray6->byteArray6, + ETHERNET_ADDRESS_SIZE) == ETHERNET_ADDRESS_SIZE); + NT_ASSERT(RtlCompareMemory(pEthernetIIHeader->pSourceAddress, + pSourceAddressValue->byteArray6->byteArray6, + ETHERNET_ADDRESS_SIZE) == ETHERNET_ADDRESS_SIZE); + NT_ASSERT(ntohs(pEthernetIIHeader->type) == pEtherTypeValue->uint16); + + LogEthernetIIHeader(pEthernetIIHeader); + } + + etherType = pEtherTypeValue->uint16; + } + else + { + FWP_VALUE* pNDISMediaType = 0; + + pNDISMediaType = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_NDIS_MEDIA_TYPE); + HLPR_BAIL_ON_NULL_POINTER(pNDISMediaType); + + /// Initial offset is at the MAC Header ... + status = KrnlHlprMACHeaderGet((NET_BUFFER_LIST*)pClassifyData->pPacket, + &pHeader, + &needToFreeMemory, + macHeaderSize); + HLPR_BAIL_ON_FAILURE(status); + + if(pNDISMediaType->uint32 == NdisMedium802_3) + { + ETHERNET_II_HEADER* pEthernetIIHeader = (ETHERNET_II_HEADER*)pHeader; + + etherType = ntohs(pEthernetIIHeader->type); + + if(etherType == NDIS_ETH_TYPE_IPV4 || + etherType == NDIS_ETH_TYPE_ARP || + etherType == NDIS_ETH_TYPE_IPV6 || + etherType == NDIS_ETH_TYPE_802_1X || + etherType == NDIS_ETH_TYPE_802_1Q || + etherType == NDIS_ETH_TYPE_SLOW_PROTOCOL) + { + LogEthernetIIHeader(pEthernetIIHeader); + + if(etherType == NDIS_ETH_TYPE_IPV4 || + etherType == NDIS_ETH_TYPE_IPV6) + macHeaderSize = sizeof(ETHERNET_II_HEADER); + } + else + { + ETHERNET_SNAP_HEADER* pEthernetSNAPHeader = (ETHERNET_SNAP_HEADER*)pHeader; + + LogEthernetSNAPHeader(pEthernetSNAPHeader); + + etherType = ntohs(pEthernetSNAPHeader->type); + + if(etherType == NDIS_ETH_TYPE_IPV4 || + etherType == NDIS_ETH_TYPE_IPV6) + macHeaderSize = sizeof(ETHERNET_SNAP_HEADER); + } + } + } + + if(macHeaderSize) + { + if(needToFreeMemory) + { + HLPR_DELETE_ARRAY(pHeader, + WFPSAMPLER_SYSLIB_TAG); + + needToFreeMemory = FALSE; + } + + if(bytesRetreated) + { + /// ... advance the offset back to the original position. + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pClassifyData->pPacket), + macHeaderSize, + FALSE, + 0); + + bytesRetreated -= macHeaderSize; + } + else + { + /// ... advance the offset to the IP Header. + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pClassifyData->pPacket), + macHeaderSize, + FALSE, + 0); + + bytesAdvanced += macHeaderSize; + } + + status = KrnlHlprIPHeaderGet((NET_BUFFER_LIST*)pClassifyData->pPacket, + &pHeader, + &needToFreeMemory); + HLPR_BAIL_ON_FAILURE(status); + + if(etherType == NDIS_ETH_TYPE_IPV4) + { + IP_HEADER_V4* pIPv4Header = (IP_HEADER_V4*)pHeader; + + ipProtocol = pIPv4Header->protocol; + + ipHeaderSize = pIPv4Header->headerLength * 4; + + LogIPv4Header(pIPv4Header); + } + else if(etherType == NDIS_ETH_TYPE_IPV6) + { + IP_HEADER_V6* pIPv6Header = (IP_HEADER_V6*)pHeader; + + ipProtocol = pIPv6Header->nextHeader; + + ipHeaderSize = sizeof(IP_HEADER_V6); + + LogIPv6Header(pIPv6Header); + } + else + HLPR_BAIL; + + if(needToFreeMemory) + { + HLPR_DELETE_ARRAY(pHeader, + WFPSAMPLER_SYSLIB_TAG); + + needToFreeMemory = FALSE; + } + + if(ipProtocol) + { + /// ... advance the offset to the Transport Header. + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pClassifyData->pPacket), + ipHeaderSize, + FALSE, + 0); + + bytesAdvanced += ipHeaderSize; + + status = KrnlHlprTransportHeaderGet((NET_BUFFER_LIST*)pClassifyData->pPacket, + &pHeader, + &needToFreeMemory); + HLPR_BAIL_ON_FAILURE(status); + + LogTransportHeader(pHeader, + ipProtocol); + } + } + + HLPR_BAIL_LABEL: + + if(needToFreeMemory) + { + HLPR_DELETE_ARRAY(pHeader, + WFPSAMPLER_SYSLIB_TAG); + + needToFreeMemory = FALSE; + } + + if(bytesRetreated) + { + /// ... advance the offset back to the original position. + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pClassifyData->pPacket), + bytesRetreated, + FALSE, + 0); + } + + if(bytesAdvanced) + { + /// ... retreat the offset back to the original position. + status = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pClassifyData->pPacket), + bytesAdvanced, + 0, + 0); + if(status != STATUS_SUCCESS) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketExaminationAtInboundMACFrame: NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PerformBasicPacketExaminationAtInboundMACFrame()\n"); + +#endif /// DBG + + return; +} + +/** + @private_function="PerformBasicPacketExaminationAtOutboundMACFrame" + + Purpose: Examines and logs the contents of the MAC Header and Ip and Transport Headers if + available.
+
+ Notes: Applies to the following framing layers:
+ FWPM_LAYER_OUTBOUND_MAC_FRAME_ETHERNET
+ FWPM_LAYER_OUTBOUND_MAC_FRAME_NATIVE
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/FF560703.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/FF564527.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/FF562631.aspx
+*/ +VOID PerformBasicPacketExaminationAtOutboundMACFrame(_In_ CLASSIFY_DATA* pClassifyData) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PerformBasicPacketExaminationAtOutboundMACFrame()\n"); + +#endif /// DBG + + NT_ASSERT(pClassifyData); + + NTSTATUS status = STATUS_SUCCESS; + FWPS_INCOMING_VALUES* pClassifyValues = (FWPS_INCOMING_VALUES*)pClassifyData->pClassifyValues; + FWPS_INCOMING_METADATA_VALUES* pMetadata = (FWPS_INCOMING_METADATA_VALUES*)pClassifyData->pMetadataValues; + UINT32 bytesAdvanced = 0; + UINT32 macHeaderSize = 0; + UINT32 ipHeaderSize = 0; + UINT16 etherType = 0; + UINT8 ipProtocol = 0; + VOID* pHeader = 0; + BOOLEAN needToFreeMemory = FALSE; + + if(FWPS_IS_L2_METADATA_FIELD_PRESENT(pMetadata, + FWPS_L2_METADATA_FIELD_ETHERNET_MAC_HEADER_SIZE)) + macHeaderSize = pMetadata->ethernetMacHeaderSize; + + /// Initial offset is at the MAC Header ... + status = KrnlHlprMACHeaderGet((NET_BUFFER_LIST*)pClassifyData->pPacket, + &pHeader, + &needToFreeMemory, + macHeaderSize); + HLPR_BAIL_ON_FAILURE(status); + + if(pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_MAC_FRAME_ETHERNET) + { + FWP_VALUE* pDestinationAddressValue = 0; + FWP_VALUE* pSourceAddressValue = 0; + FWP_VALUE* pEtherTypeValue = 0; + FWP_VALUE* pVLANIDValue = 0; + + pSourceAddressValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_MAC_LOCAL_ADDRESS); + HLPR_BAIL_ON_NULL_POINTER(pSourceAddressValue); + + pDestinationAddressValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_MAC_REMOTE_ADDRESS); + HLPR_BAIL_ON_NULL_POINTER(pDestinationAddressValue); + + pEtherTypeValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_ETHER_TYPE); + HLPR_BAIL_ON_NULL_POINTER(pEtherTypeValue); + + pVLANIDValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_VLAN_ID); + HLPR_BAIL_ON_NULL_POINTER(pVLANIDValue); + + if(macHeaderSize == sizeof(ETHERNET_SNAP_HEADER)) + { + ETHERNET_SNAP_HEADER* pEthernetSNAPHeader = (ETHERNET_SNAP_HEADER*)pHeader; + + NT_ASSERT(ntohs(pEthernetSNAPHeader->type) == pEtherTypeValue->uint16); + + LogEthernetSNAPHeader(pEthernetSNAPHeader); + } + else + { + ETHERNET_II_HEADER* pEthernetIIHeader = (ETHERNET_II_HEADER*)pHeader; + + NT_ASSERT(RtlCompareMemory(pEthernetIIHeader->pDestinationAddress, + pDestinationAddressValue->byteArray6->byteArray6, + ETHERNET_ADDRESS_SIZE) == ETHERNET_ADDRESS_SIZE); + NT_ASSERT(RtlCompareMemory(pEthernetIIHeader->pSourceAddress, + pSourceAddressValue->byteArray6->byteArray6, + ETHERNET_ADDRESS_SIZE) == ETHERNET_ADDRESS_SIZE); + NT_ASSERT(ntohs(pEthernetIIHeader->type) == pEtherTypeValue->uint16); + + LogEthernetIIHeader(pEthernetIIHeader, + pVLANIDValue->type == FWP_UINT32 ? pVLANIDValue->uint32: 0); + } + + etherType = pEtherTypeValue->uint16; + } + else + { + FWP_VALUE* pNDISMediaType = 0; + + pNDISMediaType = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_NDIS_MEDIA_TYPE); + HLPR_BAIL_ON_NULL_POINTER(pNDISMediaType); + + if(pNDISMediaType->uint32 == NdisMedium802_3) + { + ETHERNET_II_HEADER* pEthernetIIHeader = (ETHERNET_II_HEADER*)pHeader; + + etherType = ntohs(pEthernetIIHeader->type); + + if(etherType == NDIS_ETH_TYPE_IPV4 || + etherType == NDIS_ETH_TYPE_ARP || + etherType == NDIS_ETH_TYPE_IPV6 || + etherType == NDIS_ETH_TYPE_802_1X || + etherType == NDIS_ETH_TYPE_802_1Q || + etherType == NDIS_ETH_TYPE_SLOW_PROTOCOL) + { + LogEthernetIIHeader(pEthernetIIHeader); + + if(etherType == NDIS_ETH_TYPE_IPV4 || + etherType == NDIS_ETH_TYPE_IPV6) + macHeaderSize = sizeof(ETHERNET_II_HEADER); + } + else + { + ETHERNET_SNAP_HEADER* pEthernetSNAPHeader = (ETHERNET_SNAP_HEADER*)pHeader; + + LogEthernetSNAPHeader(pEthernetSNAPHeader); + + etherType = ntohs(pEthernetSNAPHeader->type); + + if(etherType == NDIS_ETH_TYPE_IPV4 || + etherType == NDIS_ETH_TYPE_IPV6) + macHeaderSize = sizeof(ETHERNET_SNAP_HEADER); + } + } + } + + if(macHeaderSize) + { + if(needToFreeMemory) + { + HLPR_DELETE_ARRAY(pHeader, + WFPSAMPLER_SYSLIB_TAG); + + needToFreeMemory = FALSE; + } + + /// ... advance the offset to the IP Header. + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pClassifyData->pPacket), + macHeaderSize, + FALSE, + 0); + + bytesAdvanced += macHeaderSize; + + status = KrnlHlprIPHeaderGet((NET_BUFFER_LIST*)pClassifyData->pPacket, + &pHeader, + &needToFreeMemory); + HLPR_BAIL_ON_FAILURE(status); + + if(etherType == NDIS_ETH_TYPE_IPV4) + { + IP_HEADER_V4* pIPv4Header = (IP_HEADER_V4*)pHeader; + + ipProtocol = pIPv4Header->protocol; + + ipHeaderSize = pIPv4Header->headerLength * 4; + + LogIPv4Header(pIPv4Header); + } + else if(etherType == NDIS_ETH_TYPE_IPV6) + { + IP_HEADER_V6* pIPv6Header = (IP_HEADER_V6*)pHeader; + + ipProtocol = pIPv6Header->nextHeader; + + ipHeaderSize = sizeof(IP_HEADER_V6); + + LogIPv6Header(pIPv6Header); + } + else + HLPR_BAIL; + + if(needToFreeMemory) + { + HLPR_DELETE_ARRAY(pHeader, + WFPSAMPLER_SYSLIB_TAG); + + needToFreeMemory = FALSE; + } + + if(ipProtocol) + { + /// ... advance the offset to the Transport Header. + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pClassifyData->pPacket), + ipHeaderSize, + FALSE, + 0); + + bytesAdvanced += ipHeaderSize; + + status = KrnlHlprTransportHeaderGet((NET_BUFFER_LIST*)pClassifyData->pPacket, + &pHeader, + &needToFreeMemory); + HLPR_BAIL_ON_FAILURE(status); + + LogTransportHeader(pHeader, + ipProtocol); + } + } + + HLPR_BAIL_LABEL: + + if(needToFreeMemory) + { + HLPR_DELETE_ARRAY(pHeader, + WFPSAMPLER_SYSLIB_TAG); + + needToFreeMemory = FALSE; + } + + if(bytesAdvanced) + { + /// ... retreat the offset back to the original position. + status = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pClassifyData->pPacket), + bytesAdvanced, + 0, + 0); + if(status != STATUS_SUCCESS) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketExaminationAtOutboundMACFrame: NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PerformBasicPacketExaminationAtOutboundMACFrame()\n"); + +#endif /// DBG + + return; +} + +/** + @private_function="PerformBasicPacketExaminationAtVSwitchTransport" + + Purpose: Examines and logs the contents of the IP Header and Transport Headers if available.
+
+ Notes: Applies to the following vSwitch layers:
+ FWPM_LAYER_INGRESS_VSWITCH_ETHERNET
+ FWPM_LAYER_EGRESS_VSWITCH_ETHERNET
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/FF560703.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/FF564527.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/FF562631.aspx
+*/ +VOID PerformBasicPacketExaminationAtVSwitchEthernet(_In_ CLASSIFY_DATA* pClassifyData) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PerformBasicPacketExaminationAtVSwitchEthernet()\n"); + +#endif /// DBG + + NT_ASSERT(pClassifyData); + + NTSTATUS status = STATUS_SUCCESS; + FWPS_INCOMING_VALUES* pClassifyValues = (FWPS_INCOMING_VALUES*)pClassifyData->pClassifyValues; + FWPS_INCOMING_METADATA_VALUES* pMetadata = (FWPS_INCOMING_METADATA_VALUES*)pClassifyData->pMetadataValues; + UINT32 bytesAdvanced = 0; + UINT32 macHeaderSize = 0; + UINT32 ipHeaderSize = 0; + UINT16 etherType = 0; + UINT8 ipProtocol = 0; + FWP_VALUE* pDestinationAddressValue = 0; + FWP_VALUE* pSourceAddressValue = 0; + FWP_VALUE* pEtherTypeValue = 0; + FWP_VALUE* pVLANIDValue = 0; + VOID* pHeader = 0; + BOOLEAN needToFreeMemory = FALSE; + + if(FWPS_IS_L2_METADATA_FIELD_PRESENT(pMetadata, + FWPS_L2_METADATA_FIELD_ETHERNET_MAC_HEADER_SIZE)) + macHeaderSize = pMetadata->ethernetMacHeaderSize; + + pSourceAddressValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_MAC_SOURCE_ADDRESS); + HLPR_BAIL_ON_NULL_POINTER(pSourceAddressValue); + + pDestinationAddressValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_MAC_DESTINATION_ADDRESS); + HLPR_BAIL_ON_NULL_POINTER(pDestinationAddressValue); + + pEtherTypeValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_ETHER_TYPE); + HLPR_BAIL_ON_NULL_POINTER(pEtherTypeValue); + + pVLANIDValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_VLAN_ID); + HLPR_BAIL_ON_NULL_POINTER(pVLANIDValue); + + /// Initial offset is at the MAC Header ... + status = KrnlHlprMACHeaderGet((NET_BUFFER_LIST*)pClassifyData->pPacket, + &pHeader, + &needToFreeMemory, + macHeaderSize); + HLPR_BAIL_ON_FAILURE(status); + + if(macHeaderSize == sizeof(ETHERNET_SNAP_HEADER)) + { + ETHERNET_SNAP_HEADER* pEthernetSNAPHeader = (ETHERNET_SNAP_HEADER*)pHeader; + + NT_ASSERT(ntohs(pEthernetSNAPHeader->type) == pEtherTypeValue->uint16); + + LogEthernetSNAPHeader(pEthernetSNAPHeader); + } + else + { + ETHERNET_II_HEADER* pEthernetIIHeader = (ETHERNET_II_HEADER*)pHeader; + + NT_ASSERT(RtlCompareMemory(pEthernetIIHeader->pDestinationAddress, + pDestinationAddressValue->byteArray6->byteArray6, + ETHERNET_ADDRESS_SIZE) == ETHERNET_ADDRESS_SIZE); + NT_ASSERT(RtlCompareMemory(pEthernetIIHeader->pSourceAddress, + pSourceAddressValue->byteArray6->byteArray6, + ETHERNET_ADDRESS_SIZE) == ETHERNET_ADDRESS_SIZE); + NT_ASSERT(ntohs(pEthernetIIHeader->type) == pEtherTypeValue->uint16); + + LogEthernetIIHeader(pEthernetIIHeader, + pVLANIDValue->type == FWP_UINT32 ? pVLANIDValue->uint32: 0); + } + + etherType = pEtherTypeValue->uint16; + + if(needToFreeMemory) + { + HLPR_DELETE_ARRAY(pHeader, + WFPSAMPLER_SYSLIB_TAG); + + needToFreeMemory = FALSE; + } + + /// ... advance the offset to the IP Header ... + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pClassifyData->pPacket), + macHeaderSize, + FALSE, + 0); + + bytesAdvanced += macHeaderSize; + + status = KrnlHlprIPHeaderGet((NET_BUFFER_LIST*)pClassifyData->pPacket, + &pHeader, + &needToFreeMemory); + HLPR_BAIL_ON_FAILURE(status); + + if(etherType == NDIS_ETH_TYPE_IPV4) + { + IP_HEADER_V4* pIPv4Header = (IP_HEADER_V4*)pHeader; + + ipProtocol = pIPv4Header->protocol; + + ipHeaderSize = pIPv4Header->headerLength * 4; + + LogIPv4Header(pIPv4Header); + } + else if(etherType == NDIS_ETH_TYPE_IPV6) + { + IP_HEADER_V6* pIPv6Header = (IP_HEADER_V6*)pHeader; + + ipProtocol = pIPv6Header->nextHeader; + + ipHeaderSize = sizeof(IP_HEADER_V6); + + LogIPv6Header(pIPv6Header); + } + else + HLPR_BAIL; + + if(needToFreeMemory) + { + HLPR_DELETE_ARRAY(pHeader, + WFPSAMPLER_SYSLIB_TAG); + + needToFreeMemory = FALSE; + } + + if(ipProtocol) + { + /// ... advance the offset to the Transport Header. + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pClassifyData->pPacket), + ipHeaderSize, + FALSE, + 0); + + bytesAdvanced += ipHeaderSize; + + status = KrnlHlprTransportHeaderGet((NET_BUFFER_LIST*)pClassifyData->pPacket, + &pHeader, + &needToFreeMemory); + HLPR_BAIL_ON_FAILURE(status); + + LogTransportHeader(pHeader, + ipProtocol); + } + + HLPR_BAIL_LABEL: + + if(needToFreeMemory) + { + HLPR_DELETE_ARRAY(pHeader, + WFPSAMPLER_SYSLIB_TAG); + + needToFreeMemory = FALSE; + } + + if(bytesAdvanced) + { + /// ... retreat the offset back to the original position. + status = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pClassifyData->pPacket), + bytesAdvanced, + 0, + 0); + if(status != STATUS_SUCCESS) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketExaminationAtVSwitchEthernet: NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PerformBasicPacketExaminationAtVSwitchEthernet()\n"); + +#endif /// DBG + + return; +} + +/** + @private_function="PerformBasicPacketExaminationAtVSwitchTransport" + + Purpose: Examines and logs the contents of the IP Header and Transport Headers if available.
+
+ Notes: Applies to the following vSwitch layers:
+ FWPM_LAYER_INGRESS_VSWITCH_TRANSPORT_V4
+ FWPM_LAYER_INGRESS_VSWITCH_TRANSPORT_V6
+ FWPM_LAYER_EGRESS_VSWITCH_TRANSPORT_V4
+ FWPM_LAYER_EGRESS_VSWITCH_TRANSPORT_V6
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/FF560703.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/FF564527.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/FF562631.aspx
+*/ +VOID PerformBasicPacketExaminationAtVSwitchTransport(_In_ CLASSIFY_DATA* pClassifyData) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PerformBasicPacketExaminationAtVSwitchTransport()\n"); + +#endif /// DBG + + NT_ASSERT(pClassifyData); + + NTSTATUS status = STATUS_SUCCESS; + FWPS_INCOMING_VALUES* pClassifyValues = (FWPS_INCOMING_VALUES*)pClassifyData->pClassifyValues; + FWPS_INCOMING_METADATA_VALUES* pMetadata = (FWPS_INCOMING_METADATA_VALUES*)pClassifyData->pMetadataValues; + UINT32 ipHeaderSize = 0; + UINT32 bytesAdvanced = 0; + FWP_VALUE* pProtocolValue = 0; + FWP_VALUE* pSourceAddressValue = 0; + FWP_VALUE* pDestinationAddressValue = 0; + FWP_VALUE* pSourcePortValue = 0; + FWP_VALUE* pDestinationPortValue = 0; + FWP_VALUE* pICMPTypeValue = 0; + FWP_VALUE* pICMPCodeValue = 0; + VOID* pHeader = 0; + BOOLEAN needToFreeMemory = FALSE; + + pProtocolValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_PROTOCOL); + HLPR_BAIL_ON_NULL_POINTER(pProtocolValue); + + pSourceAddressValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_SOURCE_ADDRESS); + HLPR_BAIL_ON_NULL_POINTER(pSourceAddressValue); + + pDestinationAddressValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_DESTINATION_ADDRESS); + HLPR_BAIL_ON_NULL_POINTER(pDestinationAddressValue); + + pSourcePortValue = pICMPTypeValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_SOURCE_PORT); + HLPR_BAIL_ON_NULL_POINTER(pSourcePortValue); + + pDestinationPortValue = pICMPCodeValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_DESTINATION_PORT); + HLPR_BAIL_ON_NULL_POINTER(pDestinationPortValue); + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_IP_HEADER_SIZE)) + ipHeaderSize = pMetadata->ipHeaderSize; + + /// Initial offset is at the IP Header ... + status = KrnlHlprIPHeaderGet((NET_BUFFER_LIST*)pClassifyData->pPacket, + &pHeader, + &needToFreeMemory, + ipHeaderSize); + HLPR_BAIL_ON_FAILURE(status); + + /// Validate that what is indicated in the classify is what is present in packet's IP Header + if(KrnlHlprFwpsLayerIsIPv4(pClassifyValues->layerId)) + { + IP_HEADER_V4* pIPv4Header = (IP_HEADER_V4*)pHeader; + + NT_ASSERT(pIPv4Header->version == IPV4); + NT_ASSERT(pIPv4Header->protocol == pProtocolValue->uint8); + NT_ASSERT(ntohl(*((UINT32*)pIPv4Header->pSourceAddress)) == pSourceAddressValue->uint32); + NT_ASSERT(ntohl(*((UINT32*)pIPv4Header->pDestinationAddress)) == pDestinationAddressValue->uint32); + + LogIPv4Header(pIPv4Header); + + if(ipHeaderSize == 0) + ipHeaderSize = IPV4_HEADER_MIN_SIZE; + } + else + { + IP_HEADER_V6* pIPv6Header = (IP_HEADER_V6*)pHeader; + + NT_ASSERT(pIPv6Header->version.value == IPV6); + NT_ASSERT(pIPv6Header->nextHeader == pProtocolValue->uint8); + NT_ASSERT(RtlCompareMemory(pIPv6Header->pSourceAddress, + pSourceAddressValue->byteArray16->byteArray16, + IPV6_ADDRESS_SIZE) == IPV6_ADDRESS_SIZE); + NT_ASSERT(RtlCompareMemory(pIPv6Header->pDestinationAddress, + pDestinationAddressValue->byteArray16->byteArray16, + IPV6_ADDRESS_SIZE) == IPV6_ADDRESS_SIZE); + + LogIPv6Header(pIPv6Header); + + if(ipHeaderSize == 0) + ipHeaderSize = IPV6_HEADER_MIN_SIZE; + } + + if(needToFreeMemory) + { + HLPR_DELETE_ARRAY(pHeader, + WFPSAMPLER_SYSLIB_TAG); + + needToFreeMemory = FALSE; + } + + /// ... advance the offset to the Transport Header. + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pClassifyData->pPacket), + ipHeaderSize, + FALSE, + 0); + + bytesAdvanced += ipHeaderSize; + + status = KrnlHlprTransportHeaderGet((NET_BUFFER_LIST*)pClassifyData->pPacket, + &pHeader, + &needToFreeMemory); + HLPR_BAIL_ON_FAILURE(status); + + switch(pProtocolValue->uint8) + { + case ICMPV4: + { + ICMP_HEADER_V4* pICMPv4Header = (ICMP_HEADER_V4*)pHeader; + + NT_ASSERT(pICMPv4Header->type == pICMPTypeValue->uint16); + NT_ASSERT(pICMPv4Header->code == pICMPCodeValue->uint16); + + LogICMPv4Header(pICMPv4Header); + + break; + } + case ICMPV6: + { + ICMP_HEADER_V6* pICMPv6Header = (ICMP_HEADER_V6*)pHeader; + + NT_ASSERT(pICMPv6Header->type == pICMPTypeValue->uint16); + NT_ASSERT(pICMPv6Header->code == pICMPCodeValue->uint16); + + LogICMPv6Header(pICMPv6Header); + + break; + } + case TCP: + { + TCP_HEADER* pTCPHeader = (TCP_HEADER*)pHeader; + + NT_ASSERT(ntohs(pTCPHeader->sourcePort) == pSourcePortValue->uint16 ); + NT_ASSERT(ntohs(pTCPHeader->destinationPort) == pDestinationPortValue->uint16); + + LogTCPHeader(pTCPHeader); + + break; + } + case UDP: + { + UDP_HEADER* pUDPHeader = (UDP_HEADER*)pHeader; + + NT_ASSERT(ntohs(pUDPHeader->sourcePort) == pSourcePortValue->uint16 ); + NT_ASSERT(ntohs(pUDPHeader->destinationPort) == pDestinationPortValue->uint16); + + LogUDPHeader(pUDPHeader); + + break; + } + } + + HLPR_BAIL_LABEL: + + if(needToFreeMemory) + { + HLPR_DELETE_ARRAY(pHeader, + WFPSAMPLER_SYSLIB_TAG); + + needToFreeMemory = FALSE; + } + + if(bytesAdvanced) + { + /// ... retreat the offset to the original position. + status = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pClassifyData->pPacket), + bytesAdvanced, + 0, + 0); + if(status != STATUS_SUCCESS) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketExaminationAtInboundMACFrame: NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PerformBasicPacketExaminationAtVSwitchTransport()\n"); + +#endif /// DBG + + return; +} + +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) + +/** + @private_function="PerformBasicPacketExaminationAtInboundNetwork" + + Purpose: Examines and logs the contents of the IP Header and Transport Header if available.
+
+ Notes: Applies to the following network layers:
+ FWPM_LAYER_INBOUND_IPPACKET_V{4/6}
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/FF560703.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/FF564527.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/FF562631.aspx
+*/ +VOID PerformBasicPacketExaminationAtInboundNetwork(_In_ CLASSIFY_DATA* pClassifyData) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PerformBasicPacketExaminationAtInboundNetwork()\n"); + +#endif /// DBG + + NT_ASSERT(pClassifyData); + + NTSTATUS status = STATUS_SUCCESS; + FWPS_INCOMING_VALUES* pClassifyValues = (FWPS_INCOMING_VALUES*)pClassifyData->pClassifyValues; + FWPS_INCOMING_METADATA_VALUES* pMetadata = (FWPS_INCOMING_METADATA_VALUES*)pClassifyData->pMetadataValues; + UINT32 ipHeaderSize = 0; + UINT32 bytesRetreated = 0; + FWP_VALUE* pSourceAddressValue = 0; + FWP_VALUE* pDestinationAddressValue = 0; + VOID* pHeader = 0; + BOOLEAN needToFreeMemory = FALSE; + UINT8 protocol = IPPROTO_RAW; + + pSourceAddressValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_REMOTE_ADDRESS); + HLPR_BAIL_ON_NULL_POINTER(pSourceAddressValue); + + pDestinationAddressValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_LOCAL_ADDRESS); + HLPR_BAIL_ON_NULL_POINTER(pDestinationAddressValue); + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_IP_HEADER_SIZE)) + ipHeaderSize = pMetadata->ipHeaderSize; + + /// Initial offset is at the Transport Header, so retreat the size of the IP Header ... + status = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pClassifyData->pPacket), + ipHeaderSize, + 0, + 0); + if(status != STATUS_SUCCESS) + { + bytesRetreated = 0; + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketExaminationAtInboundNetwork: NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + + HLPR_BAIL; + } + else + bytesRetreated = ipHeaderSize; + + status = KrnlHlprIPHeaderGet((NET_BUFFER_LIST*)pClassifyData->pPacket, + &pHeader, + &needToFreeMemory, + ipHeaderSize); + HLPR_BAIL_ON_FAILURE(status); + + /// Validate that what is indicated in the classify is what is present in packet's IP Header + if(KrnlHlprFwpsLayerIsIPv4(pClassifyValues->layerId)) + { + IP_HEADER_V4* pIPv4Header = (IP_HEADER_V4*)pHeader; + + NT_ASSERT(pIPv4Header->version == IPV4); + NT_ASSERT(((UINT32)(pIPv4Header->headerLength * 4)) == ipHeaderSize); + NT_ASSERT(ntohl(*((UINT32*)pIPv4Header->pSourceAddress)) == pSourceAddressValue->uint32); + NT_ASSERT(ntohl(*((UINT32*)pIPv4Header->pDestinationAddress)) == pDestinationAddressValue->uint32); + + LogIPv4Header(pIPv4Header); + + protocol = pIPv4Header->protocol; + } + else + { + IP_HEADER_V6* pIPv6Header = (IP_HEADER_V6*)pHeader; + + NT_ASSERT(sizeof(IP_HEADER_V6) == ipHeaderSize); + NT_ASSERT(pIPv6Header->version.value == IPV6); + NT_ASSERT(RtlCompareMemory(pIPv6Header->pSourceAddress, + pSourceAddressValue->byteArray16->byteArray16, + IPV6_ADDRESS_SIZE) == IPV6_ADDRESS_SIZE); + NT_ASSERT(RtlCompareMemory(pIPv6Header->pDestinationAddress, + pDestinationAddressValue->byteArray16->byteArray16, + IPV6_ADDRESS_SIZE) == IPV6_ADDRESS_SIZE); + + LogIPv6Header(pIPv6Header); + + protocol = pIPv6Header->nextHeader; + } + + if(needToFreeMemory) + { + HLPR_DELETE_ARRAY(pHeader, + WFPSAMPLER_SYSLIB_TAG); + + needToFreeMemory = FALSE; + } + + if(bytesRetreated) + { + /// ... and advance the offset back to the original position. + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pClassifyData->pPacket), + bytesRetreated, + FALSE, + 0); + + bytesRetreated -= ipHeaderSize; + } + + status = KrnlHlprTransportHeaderGet((NET_BUFFER_LIST*)pClassifyData->pPacket, + &pHeader, + &needToFreeMemory); + HLPR_BAIL_ON_FAILURE(status); + + LogTransportHeader(pHeader, + protocol); + + HLPR_BAIL_LABEL: + + if(needToFreeMemory) + { + HLPR_DELETE_ARRAY(pHeader, + WFPSAMPLER_SYSLIB_TAG); + + needToFreeMemory = FALSE; + } + + if(bytesRetreated) + { + /// ... and advance the offset back to the original position. + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pClassifyData->pPacket), + bytesRetreated, + FALSE, + 0); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PerformBasicPacketExaminationAtInboundNetwork()\n"); + +#endif /// DBG + + return; +} + +/** + @private_function="PerformBasicPacketExaminationAtOutboundNetwork" + + Purpose: Examines and logs the contents of the IP Header and Transport Header if available.
+
+ Notes: Applies to the following network layers:
+ FWPM_LAYER_OUTBOUND_IPPACKET_V{4/6}
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/FF560703.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/FF564527.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/FF562631.aspx
+*/ +VOID PerformBasicPacketExaminationAtOutboundNetwork(_In_ CLASSIFY_DATA* pClassifyData) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PerformBasicPacketExaminationAtOutboundNetwork()\n"); + +#endif /// DBG + + NT_ASSERT(pClassifyData); + + NTSTATUS status = STATUS_SUCCESS; + FWPS_INCOMING_VALUES* pClassifyValues = (FWPS_INCOMING_VALUES*)pClassifyData->pClassifyValues; + FWPS_INCOMING_METADATA_VALUES* pMetadata = (FWPS_INCOMING_METADATA_VALUES*)pClassifyData->pMetadataValues; + UINT32 ipHeaderSize = 0; + UINT32 bytesAdvanced = 0; + FWP_VALUE* pSourceAddressValue = 0; + FWP_VALUE* pDestinationAddressValue = 0; + VOID* pHeader = 0; + BOOLEAN needToFreeMemory = FALSE; + UINT8 protocol = IPPROTO_RAW; + + pSourceAddressValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_LOCAL_ADDRESS); + HLPR_BAIL_ON_NULL_POINTER(pSourceAddressValue); + + pDestinationAddressValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_REMOTE_ADDRESS); + HLPR_BAIL_ON_NULL_POINTER(pDestinationAddressValue); + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_IP_HEADER_SIZE)) + ipHeaderSize = pMetadata->ipHeaderSize; + + /// Initial offset is at the IP Header ... + status = KrnlHlprIPHeaderGet((NET_BUFFER_LIST*)pClassifyData->pPacket, + &pHeader, + &needToFreeMemory, + ipHeaderSize); + HLPR_BAIL_ON_FAILURE(status); + + /// Validate that what is indicated in the classify is what is present in packet's IP Header + if(KrnlHlprFwpsLayerIsIPv4(pClassifyValues->layerId)) + { + IP_HEADER_V4* pIPv4Header = (IP_HEADER_V4*)pHeader; + + NT_ASSERT(pIPv4Header->version == IPV4); + NT_ASSERT(((UINT32)(pIPv4Header->headerLength * 4)) == ipHeaderSize); + NT_ASSERT(ntohl(*((UINT32*)pIPv4Header->pSourceAddress)) == pSourceAddressValue->uint32); + NT_ASSERT(ntohl(*((UINT32*)pIPv4Header->pDestinationAddress)) == pDestinationAddressValue->uint32); + + LogIPv4Header(pIPv4Header); + + protocol = pIPv4Header->protocol; + } + else + { + IP_HEADER_V6* pIPv6Header = (IP_HEADER_V6*)pHeader; + + NT_ASSERT(sizeof(IP_HEADER_V6) == ipHeaderSize); + NT_ASSERT(pIPv6Header->version.value == IPV6); + NT_ASSERT(RtlCompareMemory(pIPv6Header->pSourceAddress, + pSourceAddressValue->byteArray16->byteArray16, + IPV6_ADDRESS_SIZE) == IPV6_ADDRESS_SIZE); + NT_ASSERT(RtlCompareMemory(pIPv6Header->pDestinationAddress, + pDestinationAddressValue->byteArray16->byteArray16, + IPV6_ADDRESS_SIZE) == IPV6_ADDRESS_SIZE); + + LogIPv6Header(pIPv6Header); + + protocol = pIPv6Header->nextHeader; + } + + if(needToFreeMemory) + { + HLPR_DELETE_ARRAY(pHeader, + WFPSAMPLER_SYSLIB_TAG); + + needToFreeMemory = FALSE; + } + + /// ... advance the offset to the Transport Header ... + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pClassifyData->pPacket), + ipHeaderSize, + FALSE, + 0); + + bytesAdvanced = ipHeaderSize; + + status = KrnlHlprTransportHeaderGet((NET_BUFFER_LIST*)pClassifyData->pPacket, + &pHeader, + &needToFreeMemory); + HLPR_BAIL_ON_FAILURE(status); + + LogTransportHeader(pHeader, + protocol); + + HLPR_BAIL_LABEL: + + if(needToFreeMemory) + { + HLPR_DELETE_ARRAY(pHeader, + WFPSAMPLER_SYSLIB_TAG); + + needToFreeMemory = FALSE; + } + + if(bytesAdvanced) + { + /// ... and retreat the offset back to the original position. + status = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pClassifyData->pPacket), + bytesAdvanced, + 0, + 0); + if(status != STATUS_SUCCESS) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketExaminationAtOutboundNetwork : NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PerformBasicPacketExaminationAtOutboundNetwork()\n"); + +#endif /// DBG + + return; +} + +/** + @private_function="PerformBasicPacketExaminationAtForward" + + Purpose: Examines and logs the contents of the IP Header and Transport Header if available.
+
+ Notes: Applies to the following forwarding layers:
+ FWPM_LAYER_IPFORWARD_V{4/6}
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/FF560703.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/FF564527.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/FF562631.aspx
+*/ +VOID PerformBasicPacketExaminationAtForward(_In_ CLASSIFY_DATA* pClassifyData) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PerformBasicPacketExaminationAtForward()\n"); + +#endif /// DBG + + NT_ASSERT(pClassifyData); + + NTSTATUS status = STATUS_SUCCESS; + FWPS_INCOMING_VALUES* pClassifyValues = (FWPS_INCOMING_VALUES*)pClassifyData->pClassifyValues; + FWPS_INCOMING_METADATA_VALUES* pMetadata = (FWPS_INCOMING_METADATA_VALUES*)pClassifyData->pMetadataValues; + UINT32 ipHeaderSize = 0; + UINT32 bytesAdvanced = 0; + FWP_VALUE* pSourceAddressValue = 0; + FWP_VALUE* pDestinationAddressValue = 0; + VOID* pHeader = 0; + BOOLEAN needToFreeMemory = FALSE; + UINT8 protocol = IPPROTO_RAW; + + pSourceAddressValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_SOURCE_ADDRESS); + HLPR_BAIL_ON_NULL_POINTER(pSourceAddressValue); + + pDestinationAddressValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_DESTINATION_ADDRESS); + HLPR_BAIL_ON_NULL_POINTER(pDestinationAddressValue); + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_IP_HEADER_SIZE)) + ipHeaderSize = pMetadata->ipHeaderSize; + + /// Initial offset is at the IP Header ... + status = KrnlHlprIPHeaderGet((NET_BUFFER_LIST*)pClassifyData->pPacket, + &pHeader, + &needToFreeMemory, + ipHeaderSize); + HLPR_BAIL_ON_FAILURE(status); + + /// Validate that what is indicated in the classify is what is present in packet's IP Header + if(KrnlHlprFwpsLayerIsIPv4(pClassifyValues->layerId)) + { + IP_HEADER_V4* pIPv4Header = (IP_HEADER_V4*)pHeader; + + NT_ASSERT(pIPv4Header->version == IPV4); + NT_ASSERT(ntohl(*((UINT32*)pIPv4Header->pSourceAddress)) == pSourceAddressValue->uint32); + NT_ASSERT(ntohl(*((UINT32*)pIPv4Header->pDestinationAddress)) == pDestinationAddressValue->uint32); + + LogIPv4Header(pIPv4Header); + + protocol = pIPv4Header->protocol; + } + else + { + IP_HEADER_V6* pIPv6Header = (IP_HEADER_V6*)pHeader; + + NT_ASSERT(pIPv6Header->version.value == IPV6); + NT_ASSERT(RtlCompareMemory(pIPv6Header->pSourceAddress, + pSourceAddressValue->byteArray16->byteArray16, + IPV6_ADDRESS_SIZE) == IPV6_ADDRESS_SIZE); + NT_ASSERT(RtlCompareMemory(pIPv6Header->pDestinationAddress, + pDestinationAddressValue->byteArray16->byteArray16, + IPV6_ADDRESS_SIZE) == IPV6_ADDRESS_SIZE); + + LogIPv6Header(pIPv6Header); + + protocol = pIPv6Header->nextHeader; + } + + if(needToFreeMemory) + { + HLPR_DELETE_ARRAY(pHeader, + WFPSAMPLER_SYSLIB_TAG); + + needToFreeMemory = FALSE; + } + + /// ... advance the offset to the Transport Header ... + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pClassifyData->pPacket), + ipHeaderSize, + FALSE, + 0); + + bytesAdvanced = ipHeaderSize; + + status = KrnlHlprTransportHeaderGet((NET_BUFFER_LIST*)pClassifyData->pPacket, + &pHeader, + &needToFreeMemory); + HLPR_BAIL_ON_FAILURE(status); + + LogTransportHeader(pHeader, + protocol); + + HLPR_BAIL_LABEL: + + if(needToFreeMemory) + { + HLPR_DELETE_ARRAY(pHeader, + WFPSAMPLER_SYSLIB_TAG); + + needToFreeMemory = FALSE; + } + + if(bytesAdvanced) + { + /// ... and retreat the offset back to the original position. + status = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pClassifyData->pPacket), + bytesAdvanced, + 0, + 0); + if(status != STATUS_SUCCESS) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketExaminationAtForward : NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PerformBasicPacketExaminationAtForward()\n"); + +#endif /// DBG + + return; +} + +/** + @private_function="PerformBasicPacketExaminationAtInboundTransport" + + Purpose: Examines and logs the contents of the IP Header and Transport Header if available.
+
+ Notes: Applies to the following transport layers:
+ FWPM_LAYER_INBOUND_TRANSPORT_V{4/6}
+ FWPM_LAYER_INBOUND_ICMP_ERROR_V{4/6}
+ FWPM_LAYER_DATAGRAM_DATA_V{4/6} (Inbound only)
+ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V{4/6} (Inbound only)
+ FWPM_LAYER_ALE_AUTH_CONNECT_V{4/6} (Inbound, reauthorization only)
+ FWPM_LAYER_ALE_FLOW_ESTABLISHED_V{4/6} (Inbound, non-TCP only)
+ FWPM_LAYER_STREAM_PACKET_V{4/6} (Inbound only)
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/FF560703.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/FF564527.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/FF562631.aspx
+*/ +VOID PerformBasicPacketExaminationAtInboundTransport(_In_ CLASSIFY_DATA* pClassifyData) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PerformBasicPacketExaminationAtInboundTransport()\n"); + +#endif /// DBG + + NT_ASSERT(pClassifyData); + + NTSTATUS status = STATUS_SUCCESS; + FWPS_INCOMING_VALUES* pClassifyValues = (FWPS_INCOMING_VALUES*)pClassifyData->pClassifyValues; + FWPS_INCOMING_METADATA_VALUES* pMetadata = (FWPS_INCOMING_METADATA_VALUES*)pClassifyData->pMetadataValues; + UINT32 ipHeaderSize = 0; + UINT32 transportHeaderSize = 0; + UINT32 bytesRetreated = 0; + UINT8 protocol = IPPROTO_RAW; + FWP_VALUE* pProtocolValue = 0; + FWP_VALUE* pSourceAddressValue = 0; + FWP_VALUE* pDestinationAddressValue = 0; + FWP_VALUE* pSourcePortValue = 0; + FWP_VALUE* pDestinationPortValue = 0; + FWP_VALUE* pICMPTypeValue = 0; + FWP_VALUE* pICMPCodeValue = 0; + VOID* pHeader = 0; + BOOLEAN needToFreeMemory = FALSE; + + if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V4) + protocol = ICMPV4; + else if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V6) + protocol = ICMPV6; + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + else if(pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V6) + protocol = TCP; + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + else + { + pProtocolValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_PROTOCOL); + HLPR_BAIL_ON_NULL_POINTER(pProtocolValue); + + protocol = pProtocolValue->uint8; + } + + pSourceAddressValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_REMOTE_ADDRESS); + HLPR_BAIL_ON_NULL_POINTER(pSourceAddressValue); + + pDestinationAddressValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_LOCAL_ADDRESS); + HLPR_BAIL_ON_NULL_POINTER(pDestinationAddressValue); + + pSourcePortValue = pICMPCodeValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_REMOTE_PORT); + HLPR_BAIL_ON_NULL_POINTER(pSourcePortValue); + + pDestinationPortValue = pICMPTypeValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_LOCAL_PORT); + HLPR_BAIL_ON_NULL_POINTER(pDestinationPortValue); + + if(pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4) + { + ipHeaderSize = IPV4_HEADER_MIN_SIZE; + + if(protocol == ICMPV4) + transportHeaderSize = ICMP_HEADER_MIN_SIZE; + else if(protocol == TCP) + transportHeaderSize = TCP_HEADER_MIN_SIZE; + else if(protocol == UDP) + transportHeaderSize = UDP_HEADER_MIN_SIZE; + } + else if(pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6) + { + ipHeaderSize = IPV6_HEADER_MIN_SIZE; + + if(protocol == ICMPV6) + transportHeaderSize = ICMP_HEADER_MIN_SIZE; + else if(protocol == TCP) + transportHeaderSize = TCP_HEADER_MIN_SIZE; + else if(protocol == UDP) + transportHeaderSize = UDP_HEADER_MIN_SIZE; + } + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_IP_HEADER_SIZE) && + pMetadata->ipHeaderSize) + ipHeaderSize = pMetadata->ipHeaderSize; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE) && + pMetadata->transportHeaderSize) + transportHeaderSize = pMetadata->transportHeaderSize; + + if((protocol == ICMPV4 || + protocol == ICMPV6) && + (pClassifyValues->layerId != FWPS_LAYER_INBOUND_ICMP_ERROR_V4 && + pClassifyValues->layerId != FWPS_LAYER_INBOUND_ICMP_ERROR_V6 && + pClassifyValues->layerId != FWPS_LAYER_DATAGRAM_DATA_V4 && + pClassifyValues->layerId != FWPS_LAYER_DATAGRAM_DATA_V6)) + { + /// Initial offset is at the ICMP Header, so retreat the size of the IP Header ... + status = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pClassifyData->pPacket), + ipHeaderSize, + 0, + 0); + } + else + { + /// Initial offset is at the Data, so retreat the size of the IP and Transport Headers ... + status = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pClassifyData->pPacket), + ipHeaderSize + transportHeaderSize, + 0, + 0); + } + + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketExaminationAtInboundTransport: NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + + HLPR_BAIL; + } + else + { + if((protocol == ICMPV4 || + protocol == ICMPV6) && + (pClassifyValues->layerId != FWPS_LAYER_INBOUND_ICMP_ERROR_V4 && + pClassifyValues->layerId != FWPS_LAYER_INBOUND_ICMP_ERROR_V6 && + pClassifyValues->layerId != FWPS_LAYER_DATAGRAM_DATA_V4 && + pClassifyValues->layerId != FWPS_LAYER_DATAGRAM_DATA_V6)) + bytesRetreated = ipHeaderSize; + else + bytesRetreated = ipHeaderSize + transportHeaderSize; + } + + status = KrnlHlprIPHeaderGet((NET_BUFFER_LIST*)pClassifyData->pPacket, + &pHeader, + &needToFreeMemory, + ipHeaderSize); + HLPR_BAIL_ON_FAILURE(status); + + /// Validate that what is indicated in the classify is what is present in packet's IP Header + if(KrnlHlprFwpsLayerIsIPv4(pClassifyValues->layerId)) + { + IP_HEADER_V4* pIPv4Header = (IP_HEADER_V4*)pHeader; + + NT_ASSERT(pIPv4Header->version == IPV4); + NT_ASSERT(((UINT32)(pIPv4Header->headerLength * 4)) == ipHeaderSize); + NT_ASSERT(pIPv4Header->protocol == protocol); + NT_ASSERT(ntohl(*((UINT32*)pIPv4Header->pSourceAddress)) == pSourceAddressValue->uint32); + NT_ASSERT(ntohl(*((UINT32*)pIPv4Header->pDestinationAddress)) == pDestinationAddressValue->uint32); + + if(pIPv4Header->version == IPV4 && + ipHeaderSize >= IPV4_HEADER_MIN_SIZE) + LogIPv4Header(pIPv4Header); + } + else + { + IP_HEADER_V6* pIPv6Header = (IP_HEADER_V6*)pHeader; + + NT_ASSERT(sizeof(IP_HEADER_V6) == ipHeaderSize); + NT_ASSERT(pIPv6Header->version.value == IPV6); + NT_ASSERT(pIPv6Header->nextHeader == protocol); + NT_ASSERT(RtlCompareMemory(pIPv6Header->pSourceAddress, + pSourceAddressValue->byteArray16->byteArray16, + IPV6_ADDRESS_SIZE) == IPV6_ADDRESS_SIZE); + NT_ASSERT(RtlCompareMemory(pIPv6Header->pDestinationAddress, + pDestinationAddressValue->byteArray16->byteArray16, + IPV6_ADDRESS_SIZE) == IPV6_ADDRESS_SIZE); + + if(pIPv6Header->version.value == IPV6 && + ipHeaderSize >= IPV6_HEADER_MIN_SIZE) + LogIPv6Header(pIPv6Header); + } + + if(needToFreeMemory) + { + HLPR_DELETE_ARRAY(pHeader, + WFPSAMPLER_SYSLIB_TAG); + + needToFreeMemory = FALSE; + } + + if(bytesRetreated) + { + /// and advance the offset to the Transport Header. + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pClassifyData->pPacket), + ipHeaderSize, + FALSE, + 0); + + bytesRetreated -= ipHeaderSize; + } + + status = KrnlHlprTransportHeaderGet((NET_BUFFER_LIST*)pClassifyData->pPacket, + &pHeader, + &needToFreeMemory, + transportHeaderSize); + HLPR_BAIL_ON_FAILURE(status); + + switch(protocol) + { + case ICMPV4: + { + ICMP_HEADER_V4* pICMPv4Header = (ICMP_HEADER_V4*)pHeader; + + NT_ASSERT(pICMPv4Header->type == pICMPTypeValue->uint16); + NT_ASSERT(pICMPv4Header->code == pICMPCodeValue->uint16); + + if(transportHeaderSize >= ICMP_HEADER_MIN_SIZE) + LogICMPv4Header(pICMPv4Header); + + break; + } + case ICMPV6: + { + ICMP_HEADER_V6* pICMPv6Header = (ICMP_HEADER_V6*)pHeader; + + NT_ASSERT(pICMPv6Header->type == pICMPTypeValue->uint16); + NT_ASSERT(pICMPv6Header->code == pICMPCodeValue->uint16); + + if(transportHeaderSize >= ICMP_HEADER_MIN_SIZE) + LogICMPv6Header(pICMPv6Header); + + break; + } + case TCP: + { + TCP_HEADER* pTCPHeader = (TCP_HEADER*)pHeader; + + NT_ASSERT(ntohs(pTCPHeader->sourcePort) == pSourcePortValue->uint16 ); + NT_ASSERT(ntohs(pTCPHeader->destinationPort) == pDestinationPortValue->uint16); + + if(transportHeaderSize >= TCP_HEADER_MIN_SIZE) + LogTCPHeader(pTCPHeader); + + break; + } + case UDP: + { + UDP_HEADER* pUDPHeader = (UDP_HEADER*)pHeader; + + NT_ASSERT(ntohs(pUDPHeader->sourcePort) == pSourcePortValue->uint16 ); + NT_ASSERT(ntohs(pUDPHeader->destinationPort) == pDestinationPortValue->uint16); + + if(transportHeaderSize >= UDP_HEADER_MIN_SIZE) + LogUDPHeader(pUDPHeader); + + break; + } + } + + HLPR_BAIL_LABEL: + + if(needToFreeMemory) + { + HLPR_DELETE_ARRAY(pHeader, + WFPSAMPLER_SYSLIB_TAG); + + needToFreeMemory = FALSE; + } + + if(bytesRetreated) + { + /// ... and advance the offset back to the original position. + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pClassifyData->pPacket), + bytesRetreated, + FALSE, + 0); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PerformBasicPacketExaminationAtInboundTransport()\n"); + +#endif /// DBG + + return; +} + +/** + @private_function="PerformBasicPacketExaminationAtOutboundTransport" + + Purpose: Examines and logs the contents of the IP Header and Transport Header if available.
+
+ Notes: Applies to the following transport layers:
+ FWPM_LAYER_OUTBOUND_TRANSPORT_V{4/6}
+ FWPM_LAYER_OUTBOUND_ICMP_ERROR_V{4/6}
+ FWPM_LAYER_DATAGRAM_DATA_V{4/6} (Outbound only)
+ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V{4/6} (Outbound only)
+ FWPM_LAYER_ALE_AUTH_CONNECT_V{4/6} (Outbound reauthorization only)
+ FWPM_LAYER_ALE_FLOW_ESTABLISHED_V{4/6} (Outbound, non-TCP only)
+ FWPM_LAYER_STREAM_PACKET_V{4/6} (Outbound only)
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/FF560703.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/FF564527.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/FF562631.aspx
+*/ +VOID PerformBasicPacketExaminationAtOutboundTransport(_In_ CLASSIFY_DATA* pClassifyData) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PerformBasicPacketExaminationAtOutboundTransport()\n"); + +#endif /// DBG + + NT_ASSERT(pClassifyData); + + NTSTATUS status = STATUS_SUCCESS; + FWPS_INCOMING_VALUES* pClassifyValues = (FWPS_INCOMING_VALUES*)pClassifyData->pClassifyValues; + UINT8 protocol = IPPROTO_RAW; + FWP_VALUE* pProtocolValue = 0; + FWP_VALUE* pSourcePortValue = 0; + FWP_VALUE* pDestinationPortValue = 0; + FWP_VALUE* pICMPTypeValue = 0; + FWP_VALUE* pICMPCodeValue = 0; + VOID* pHeader = 0; + BOOLEAN needToFreeMemory = FALSE; + + if(pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4) + protocol = ICMPV4; + else if(pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6) + protocol = ICMPV6; + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + else if(pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V6) + protocol = TCP; + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + else + { + pProtocolValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_PROTOCOL); + HLPR_BAIL_ON_NULL_POINTER(pProtocolValue); + + protocol = pProtocolValue->uint8; + } + + pSourcePortValue = pICMPTypeValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_LOCAL_PORT); + HLPR_BAIL_ON_NULL_POINTER(pSourcePortValue); + + pDestinationPortValue = pICMPCodeValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_REMOTE_PORT); + HLPR_BAIL_ON_NULL_POINTER(pDestinationPortValue); + + /// Initial offset is at the Transport Header ... + status = KrnlHlprTransportHeaderGet((NET_BUFFER_LIST*)pClassifyData->pPacket, + &pHeader, + &needToFreeMemory); + HLPR_BAIL_ON_FAILURE(status); + + switch(protocol) + { + case ICMPV4: + { + ICMP_HEADER_V4* pICMPv4Header = (ICMP_HEADER_V4*)pHeader; + + NT_ASSERT(pICMPv4Header->type == pICMPTypeValue->uint16); + NT_ASSERT(pICMPv4Header->code == pICMPCodeValue->uint16); + + LogICMPv4Header(pICMPv4Header); + + break; + } + case ICMPV6: + { + ICMP_HEADER_V6* pICMPv6Header = (ICMP_HEADER_V6*)pHeader; + + NT_ASSERT(pICMPv6Header->type == pICMPTypeValue->uint16); + NT_ASSERT(pICMPv6Header->code == pICMPCodeValue->uint16); + + LogICMPv6Header(pICMPv6Header); + + break; + } + case TCP: + { + TCP_HEADER* pTCPHeader = (TCP_HEADER*)pHeader; + + NT_ASSERT(ntohs(pTCPHeader->sourcePort) == pSourcePortValue->uint16); + NT_ASSERT(ntohs(pTCPHeader->destinationPort) == pDestinationPortValue->uint16); + + LogTCPHeader(pTCPHeader); + + break; + } + case UDP: + { + UDP_HEADER* pUDPHeader = (UDP_HEADER*)pHeader; + + NT_ASSERT(ntohs(pUDPHeader->sourcePort) == pSourcePortValue->uint16); + NT_ASSERT(ntohs(pUDPHeader->destinationPort) == pDestinationPortValue->uint16); + + LogUDPHeader(pUDPHeader); + + break; + } + } + + HLPR_BAIL_LABEL: + + if(needToFreeMemory) + { + HLPR_DELETE_ARRAY(pHeader, + WFPSAMPLER_SYSLIB_TAG); + + needToFreeMemory = FALSE; + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PerformBasicPacketExaminationAtOutboundTransport()\n"); + +#endif /// DBG + + return; +} + +/** + @private_function="PerformBasicPacketExaminationAtOutboundTransport" + + Purpose: Logs the Discard reason.
+
+ Notes: Applies to the following discard layers:
+ FWPM_LAYER_INBOUND_IPPACKET_V{4/6}_DISCARD
+ FWPM_LAYER_OUTBOUND_IPPACKET_V{4/6}_DISCARD
+ FWPM_LAYER_IPFORWARD_V{4/6}_DISCARD
+ FWPM_LAYER_INBOUND_TRANSPORT_V{4/6}_DISCARD
+ FWPM_LAYER_OUTBOUND_TRANSPORT_V{4/6}_DISCARD
+ FWPM_LAYER_STREAM_V{4/6}_DISCARD
+ FWPM_LAYER_DATAGRAM_DATA_V{4/6}
+ FWPM_LAYER_INBOUND_ICMP_ERROR_V{4/6}_DISCARD
+ FWPM_LAYER_OUTBOUND_ICMP_ERROR_V{4/6}_DISCARD
+ FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V{4/6}_DISCARD
+ FWPM_LAYER_ALE_AUTH_LISTEN_V{4/6}_DISCARD
+ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V{4/6}_DISCARD
+ FWPM_LAYER_ALE_AUTH_CONNECT_V{4/6}_DISCARD
+ FWPM_LAYER_ALE_FLOW_ESTABLISHED_V{4/6}_DISCARD
+
+ MSDN_Ref:
+*/ +VOID PerformBasicPacketExaminationAtDiscard(_In_ CLASSIFY_DATA* pClassifyData) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PerformBasicPacketExaminationAtDiscard()\n"); + +#endif /// DBG + + NT_ASSERT(pClassifyData); + NT_ASSERT(pClassifyData->pClassifyValues); + NT_ASSERT(pClassifyData->pMetadataValues); + NT_ASSERT(pClassifyData->pMetadataValues->currentMetadataValues & FWPS_METADATA_FIELD_DISCARD_REASON); + + NTSTATUS status = STATUS_SUCCESS; + UINT32 discardModule = FWPS_DISCARD_MODULE_MAX; + UINT32 discardReason = IpDiscardMax; + UINT64 filterID = 0; + PSTR pDiscardModule = "UNKNOWN"; + PSTR pDiscardReason = "UNKNOWN"; + PSTR pString = 0; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pClassifyData->pMetadataValues, + FWPS_METADATA_FIELD_DISCARD_REASON)) + { + discardModule = pClassifyData->pMetadataValues->discardMetadata.discardModule; + + discardReason = pClassifyData->pMetadataValues->discardMetadata.discardReason; + + filterID = pClassifyData->pMetadataValues->discardMetadata.filterId; + } + + switch(discardModule) + { + case FWPS_DISCARD_MODULE_NETWORK: + { + pDiscardModule = "NETWORK"; + + switch(discardReason) + { + case IpDiscardBadSourceAddress: + { + pDiscardReason = "Bad Source Address"; + + break; + } + case IpDiscardNotLocallyDestined: + { + pDiscardReason = "Not Locally Destined"; + + break; + } + case IpDiscardProtocolUnreachable: + { + pDiscardReason = "Unreachable Protocol"; + + break; + } + case IpDiscardPortUnreachable: + { + pDiscardReason = "UnreachablePort"; + + break; + } + case IpDiscardBadLength: + { + pDiscardReason = "Bad Length"; + + break; + } + case IpDiscardMalformedHeader: + { + pDiscardReason = "Malformed Header"; + + break; + } + case IpDiscardNoRoute: + { + pDiscardReason = "No Route"; + + break; + } + case IpDiscardBeyondScope: + { + pDiscardReason = "Beyond Scope"; + + break; + } + case IpDiscardInspectionDrop: + { + pDiscardReason = "Inspection Drop"; + + break; + } + case IpDiscardTooManyDecapsulations: + { + pDiscardReason = "Too Many Decapsulations"; + + break; + } + case IpDiscardAdministrativelyProhibited: + { + pDiscardReason = "Administratively Prohibited"; + + break; + } + case IpDiscardHopLimitExceeded: + { + pDiscardReason = "Hop Limit Exceeded"; + + break; + } + case IpDiscardAddressUnreachable: + { + pDiscardReason = "Unreachable Address"; + + break; + } + case IpDiscardRscPacket: + { + pDiscardReason = "RSC Packet"; + + break; + } + case IpDiscardArbitrationUnhandled: + { + pDiscardReason = "Unhandled Arbitration"; + + break; + } + case IpDiscardInspectionAbsorb: + { + pDiscardReason = "Inspection Absorb"; + + break; + } + case IpDiscardDontFragmentMtuExceeded: + { + pDiscardReason = "Don't Fragment / MTU Exceeded"; + + break; + } + case IpDiscardBufferLengthExceeded: + { + pDiscardReason = "Buffer Length Exceeded"; + + break; + } + case IpDiscardAddressResolutionTimeout: + { + pDiscardReason = "Address Resolution Timeout"; + + break; + } + case IpDiscardAddressResolutionFailure: + { + pDiscardReason = "Address Resolution Failure"; + + break; + } + case IpDiscardIpsecFailure: + { + pDiscardReason = "IPsec Failure"; + + break; + } + case IpDiscardExtensionHeadersFailure: + { + pDiscardReason = "Extension Headers Failure"; + + break; + } + case IpDiscardIPSNPIDrop: + { + pDiscardReason = "IPSNPI Drop"; + + break; + } + } + + break; + } + case FWPS_DISCARD_MODULE_TRANSPORT: + { + pDiscardModule = "TRANSPORT"; + + switch(discardReason) + { + case InetDiscardSourceUnspecified: + { + pDiscardReason = "Unspecified Source"; + + break; + } + case InetDiscardDestinationMulticast: + { + pDiscardReason = "Multicast Destination"; + + break; + } + case InetDiscardHeaderInvalid: + { + pDiscardReason = "Invalid Header"; + + break; + } + case InetDiscardChecksumInvalid: + { + pDiscardReason = "Invalid Checksum"; + + break; + } + case InetDiscardEndpointNotFound: + { + pDiscardReason = "Endpoint Not Found"; + + break; + } + case InetDiscardConnectedPath: + { + pDiscardReason = "Connected Path"; + + break; + } + case InetDiscardSessionState: + { + pDiscardReason = "Session State"; + + break; + } + case InetDiscardReceiveInspection: + { + pDiscardReason = "Receive Inspection"; + + break; + } + } + + break; + } + case FWPS_DISCARD_MODULE_GENERAL: + { + pDiscardModule = "GENERAL"; + + switch(discardReason) + { + case FWPS_DISCARD_FIREWALL_POLICY: + { + pDiscardReason = "Firewall Policy"; + + break; + } + case FWPS_DISCARD_IPSEC: + { + pDiscardReason = "IPsec Policy"; + + break; + } + } + + break; + } + } + + HLPR_NEW_ARRAY(pString, + CHAR, + MAX_STRING_SIZE, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pString, + status); + + status = RtlStringCchPrintfA(pString, + MAX_STRING_SIZE, + "\n" + "\t\tLayer:\t%s" + "\t\tModule:\t%s\n" + "\t\tReason:\t%s\n" + "\t\tFilter:\t%I64d\n", + KrnlHlprFwpsLayerIDToString(pClassifyData->pClassifyValues->layerId - 1), + pDiscardModule, + pDiscardReason, + filterID); + if(status == STATUS_SUCCESS) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\tDiscard:%s", + pString); + + HLPR_BAIL_LABEL: + + HLPR_DELETE_ARRAY(pString, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PerformBasicPacketExaminationAtDiscard()\n"); + +#endif /// DBG + + return; +} + +/** + @private_function="PerformBasicPacketExaminationAtOther" + + Purpose: Logs the classify.
+
+ Notes: Applies to the following discard layers:
+ FWPM_LAYER_STREAM_V{4/6}
+ FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V{4/6}
+ FWPM_LAYER_ALE_AUTH_LISTEN_V{4/6}
+ FWPM_LAYER_ALE_RESOURCE_RELEASE_V{4/6}
+ FWPM_LAYER_ALE_ENDPOINT_CLOSURE_V{4/6}
+ FWPM_LAYER_ALE_CONNECT_REDIRECT_V{4/6}
+ FWPM_LAYER_ALE_BIND_REDIRECT_V{4/6}
+ FWPM_LAYER_ALE_CONNECT_REDIRECT_V{4/6}
+
+ MSDN_Ref:
+*/ +VOID PerformBasicPacketExaminationAtOther(_In_ CLASSIFY_DATA* pClassifyData) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PerformBasicPacketExaminationAtOther()\n"); + +#endif /// DBG + + NT_ASSERT(pClassifyData); + NT_ASSERT(pClassifyData->pClassifyValues); + + NTSTATUS status = STATUS_SUCCESS; + UINT16 localPort = 0; + BYTE* pLocalAddress = 0; + PSTR pString = 0; + +#pragma warning(push) +#pragma warning(disable: 28193) /// value is checked before use + + FWP_VALUE* pLocalAddressValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyData->pClassifyValues, + &FWPM_CONDITION_IP_LOCAL_ADDRESS); + FWP_VALUE* pProtocolValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyData->pClassifyValues, + &FWPM_CONDITION_IP_PROTOCOL); + FWP_VALUE* pLocalPortValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyData->pClassifyValues, + &FWPM_CONDITION_IP_LOCAL_PORT); + +#pragma warning(pop) + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + UINT16 remotePort = 0; + BYTE* pRemoteAddress = 0; + +#pragma warning(push) +#pragma warning(disable: 28193) /// value is checked before use + + FWP_VALUE* pRemoteAddressValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyData->pClassifyValues, + &FWPM_CONDITION_IP_REMOTE_ADDRESS); + FWP_VALUE* pRemotePortValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyData->pClassifyValues, + &FWPM_CONDITION_IP_REMOTE_PORT); + +#pragma warning(pop) + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\tLayer:%s\n", + KrnlHlprFwpsLayerIDToString(pClassifyData->pClassifyValues->layerId)); + + HLPR_NEW_ARRAY(pString, + CHAR, + MAX_STRING_SIZE, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pString, + status); + + switch(pClassifyData->pClassifyValues->layerId) + { + case FWPS_LAYER_STREAM_V4: + case FWPS_LAYER_STREAM_V6: + { + FWPS_STREAM_CALLOUT_IO_PACKET0* pIOPacket = (FWPS_STREAM_CALLOUT_IO_PACKET*)(pClassifyData->pPacket); + + if(pIOPacket) + { + status = RtlStringCchPrintfA(pString, + MAX_STRING_SIZE, + "\n" + "\t\t\tStreamData:\n" + "\t\t\t\tFlags: %#x\n" + "\t\t\t\tDataLength: %I64d\n" + "\t\t\tBytesMissed: %I64d\n" + "\t\t\tBytesRequired: %d\n" + "\t\t\tBytesEnforced: %I64d\n" + "\t\t\tStreamAction: %#x\n", + pIOPacket->streamData ? pIOPacket->streamData->flags : 0, + (UINT64)(pIOPacket->streamData ? pIOPacket->streamData->dataLength : 0), + (UINT64)(pIOPacket->missedBytes), + pIOPacket->countBytesRequired, + (UINT64)(pIOPacket->countBytesEnforced), + pIOPacket->streamAction); + if(status == STATUS_SUCCESS) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\tStreamCalloutIOPacket: %s", + pString); + } + + break; + } + case FWPS_LAYER_ALE_RESOURCE_ASSIGNMENT_V4: + case FWPS_LAYER_ALE_RESOURCE_ASSIGNMENT_V6: + { + PSTR pProtocolString = 0; + + if(pProtocolValue && + pProtocolValue->type == FWP_UINT8) + { + if(pProtocolValue->uint8 == ICMPV4 || + pProtocolValue->uint8 == ICMPV6) + pProtocolString = "ICMP"; + else if(pProtocolValue->uint8 == TCP) + pProtocolString = "TCP"; + else if(pProtocolValue->uint8 == UDP) + pProtocolString = "UDP"; + else if(pProtocolValue->uint8 == IPPROTO_RAW) + pProtocolString = "Raw IP"; + } + + if(pLocalPortValue) + { + if(pLocalPortValue->type == FWP_UINT16) + localPort = pLocalPortValue->uint16; + else if(pLocalPortValue->type == FWP_UINT8) + localPort = pLocalPortValue->uint8; + } + + if(pLocalAddressValue) + { + if(pLocalAddressValue->type == FWP_UINT32) + { + pLocalAddress = (BYTE*)&(pLocalAddressValue->uint32); + + status = RtlStringCchPrintfA(pString, + MAX_STRING_SIZE, + "\n\t\t\t" + "%s %d.%d.%d.%d : %d\n", + pProtocolString, + pLocalAddress[3], + pLocalAddress[2], + pLocalAddress[1], + pLocalAddress[0], + localPort); + } + else if(pLocalAddressValue->type == FWP_BYTE_ARRAY16_TYPE) + { + pLocalAddress = (BYTE*)pLocalAddressValue->byteArray16; + + status = RtlStringCchPrintfA(pString, + MAX_STRING_SIZE, + "\n\t\t\t" + "%s %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x : %d", + pProtocolString, + pLocalAddress[0], + pLocalAddress[1], + pLocalAddress[2], + pLocalAddress[3], + pLocalAddress[4], + pLocalAddress[5], + pLocalAddress[6], + pLocalAddress[7], + pLocalAddress[8], + pLocalAddress[9], + pLocalAddress[10], + pLocalAddress[11], + pLocalAddress[12], + pLocalAddress[13], + pLocalAddress[14], + pLocalAddress[15], + localPort); + } + + if(status == STATUS_SUCCESS) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\tBinding: %s", + pString); + } + + break; + } + case FWPS_LAYER_ALE_AUTH_LISTEN_V4: + case FWPS_LAYER_ALE_AUTH_LISTEN_V6: + { + if(pLocalPortValue) + { + if(pLocalPortValue->type == FWP_UINT16) + localPort = pLocalPortValue->uint16; + else if(pLocalPortValue->type == FWP_UINT8) + localPort = pLocalPortValue->uint8; + } + + if(pLocalAddressValue) + { + if(pLocalAddressValue->type == FWP_UINT32) + { + pLocalAddress = (BYTE*)&(pLocalAddressValue->uint32); + + status = RtlStringCchPrintfA(pString, + MAX_STRING_SIZE, + "\n\t\t\t" + "%d.%d.%d.%d : %d\n", + pLocalAddress[3], + pLocalAddress[2], + pLocalAddress[1], + pLocalAddress[0], + localPort); + } + else if(pLocalAddressValue->type == FWP_BYTE_ARRAY16_TYPE) + { + pLocalAddress = (BYTE*)pLocalAddressValue->byteArray16; + + status = RtlStringCchPrintfA(pString, + MAX_STRING_SIZE, + "\n\t\t\t" + "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x : %d", + pLocalAddress[0], + pLocalAddress[1], + pLocalAddress[2], + pLocalAddress[3], + pLocalAddress[4], + pLocalAddress[5], + pLocalAddress[6], + pLocalAddress[7], + pLocalAddress[8], + pLocalAddress[9], + pLocalAddress[10], + pLocalAddress[11], + pLocalAddress[12], + pLocalAddress[13], + pLocalAddress[14], + pLocalAddress[15], + localPort); + } + + if(status == STATUS_SUCCESS) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\tListening: TCP %s", + pString); + } + + break; + } + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + case FWPS_LAYER_ALE_RESOURCE_RELEASE_V4: + case FWPS_LAYER_ALE_RESOURCE_RELEASE_V6: + { + PSTR pProtocolString = 0; + + if(pProtocolValue && + pProtocolValue->type == FWP_UINT8) + { + if(pProtocolValue->uint8 == ICMPV4 || + pProtocolValue->uint8 == ICMPV6) + pProtocolString = "ICMP"; + else if(pProtocolValue->uint8 == TCP) + pProtocolString = "TCP"; + else if(pProtocolValue->uint8 == UDP) + pProtocolString = "UDP"; + else if(pProtocolValue->uint8 == IPPROTO_RAW) + pProtocolString = "Raw IP"; + } + + if(pLocalPortValue) + { + if(pLocalPortValue->type == FWP_UINT16) + localPort = pLocalPortValue->uint16; + else if(pLocalPortValue->type == FWP_UINT8) + localPort = pLocalPortValue->uint8; + } + + if(pLocalAddressValue) + { + if(pLocalAddressValue->type == FWP_UINT32) + { + pLocalAddress = (BYTE*)&(pLocalAddressValue->uint32); + + status = RtlStringCchPrintfA(pString, + MAX_STRING_SIZE, + "\n\t\t\t" + "%s %d.%d.%d.%d : %d\n", + pProtocolString, + pLocalAddress[3], + pLocalAddress[2], + pLocalAddress[1], + pLocalAddress[0], + localPort); + } + else if(pLocalAddressValue->type == FWP_BYTE_ARRAY16_TYPE) + { + pLocalAddress = (BYTE*)pLocalAddressValue->byteArray16; + + status = RtlStringCchPrintfA(pString, + MAX_STRING_SIZE, + "\n\t\t\t" + "%s %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x : %d", + pProtocolString, + pLocalAddress[0], + pLocalAddress[1], + pLocalAddress[2], + pLocalAddress[3], + pLocalAddress[4], + pLocalAddress[5], + pLocalAddress[6], + pLocalAddress[7], + pLocalAddress[8], + pLocalAddress[9], + pLocalAddress[10], + pLocalAddress[11], + pLocalAddress[12], + pLocalAddress[13], + pLocalAddress[14], + pLocalAddress[15], + localPort); + } + + if(status == STATUS_SUCCESS) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\tReleasing: %s", + pString); + } + + break; + } + case FWPS_LAYER_ALE_ENDPOINT_CLOSURE_V4: + case FWPS_LAYER_ALE_ENDPOINT_CLOSURE_V6: + { + PSTR pProtocolString = 0; + + if(pProtocolValue && + pProtocolValue->type == FWP_UINT8) + { + if(pProtocolValue->uint8 == ICMPV4 || + pProtocolValue->uint8 == ICMPV6) + pProtocolString = "ICMP"; + else if(pProtocolValue->uint8 == TCP) + pProtocolString = "TCP"; + else if(pProtocolValue->uint8 == UDP) + pProtocolString = "UDP"; + else if(pProtocolValue->uint8 == IPPROTO_RAW) + pProtocolString = "Raw IP"; + } + + if(pLocalPortValue) + { + if(pLocalPortValue->type == FWP_UINT16) + localPort = pLocalPortValue->uint16; + else if(pLocalPortValue->type == FWP_UINT8) + localPort = pLocalPortValue->uint8; + } + + if(pLocalAddressValue) + { + if(pLocalAddressValue->type == FWP_UINT32) + { + pLocalAddress = (BYTE*)&(pLocalAddressValue->uint32); + + status = RtlStringCchPrintfA(pString, + MAX_STRING_SIZE, + "\n\t\t\t" + "%s %d.%d.%d.%d : %d\n", + pProtocolString, + pLocalAddress[3], + pLocalAddress[2], + pLocalAddress[1], + pLocalAddress[0], + localPort); + } + else if(pLocalAddressValue->type == FWP_BYTE_ARRAY16_TYPE) + { + pLocalAddress = (BYTE*)pLocalAddressValue->byteArray16; + + status = RtlStringCchPrintfA(pString, + MAX_STRING_SIZE, + "\n\t\t\t" + "%s %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x : %d", + pProtocolString, + pLocalAddress[0], + pLocalAddress[1], + pLocalAddress[2], + pLocalAddress[3], + pLocalAddress[4], + pLocalAddress[5], + pLocalAddress[6], + pLocalAddress[7], + pLocalAddress[8], + pLocalAddress[9], + pLocalAddress[10], + pLocalAddress[11], + pLocalAddress[12], + pLocalAddress[13], + pLocalAddress[14], + pLocalAddress[15], + localPort); + } + + if(status == STATUS_SUCCESS) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\tClosing: %s", + pString); + } + + break; + } + case FWPS_LAYER_ALE_CONNECT_REDIRECT_V4: + case FWPS_LAYER_ALE_CONNECT_REDIRECT_V6: + { + PSTR pProtocolString = 0; + + if(pProtocolValue && + pProtocolValue->type == FWP_UINT8) + { + if(pProtocolValue->uint8 == ICMPV4 || + pProtocolValue->uint8 == ICMPV6) + pProtocolString = "ICMP"; + else if(pProtocolValue->uint8 == TCP) + pProtocolString = "TCP"; + else if(pProtocolValue->uint8 == UDP) + pProtocolString = "UDP"; + else if(pProtocolValue->uint8 == IPPROTO_RAW) + pProtocolString = "Raw IP"; + } + + if(pLocalPortValue) + { + if(pLocalPortValue->type == FWP_UINT16) + localPort = pLocalPortValue->uint16; + else if(pLocalPortValue->type == FWP_UINT8) + localPort = pLocalPortValue->uint8; + } + + if(pRemotePortValue) + { + if(pRemotePortValue->type == FWP_UINT16) + remotePort = pRemotePortValue->uint16; + else if(pRemotePortValue->type == FWP_UINT8) + remotePort = pRemotePortValue->uint8; + } + + if(pLocalAddressValue && + pRemoteAddressValue) + { + if(pLocalAddressValue->type == FWP_UINT32 && + pRemoteAddressValue->type == FWP_UINT32) + { + pLocalAddress = (BYTE*)&(pLocalAddressValue->uint32); + + pRemoteAddress = (BYTE*)&(pRemoteAddressValue->uint32); + + status = RtlStringCchPrintfA(pString, + MAX_STRING_SIZE, + "\n\t\t\t" + "%s %d.%d.%d.%d : %d To %d.%d.%d.%d : %d\n", + pProtocolString, + pLocalAddress[3], + pLocalAddress[2], + pLocalAddress[1], + pLocalAddress[0], + localPort, + pRemoteAddress[3], + pRemoteAddress[2], + pRemoteAddress[1], + pRemoteAddress[0], + remotePort); + } + else if(pLocalAddressValue->type == FWP_BYTE_ARRAY16_TYPE && + pRemoteAddressValue->type == FWP_BYTE_ARRAY16_TYPE) + { + pLocalAddress = (BYTE*)pLocalAddressValue->byteArray16; + + pRemoteAddress = (BYTE*)pRemoteAddressValue->byteArray16; + + status = RtlStringCchPrintfA(pString, + MAX_STRING_SIZE, + "\n\t\t\t" + "%s %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x : %d To %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x : %d\n", + pProtocolString, + pLocalAddress[0], + pLocalAddress[1], + pLocalAddress[2], + pLocalAddress[3], + pLocalAddress[4], + pLocalAddress[5], + pLocalAddress[6], + pLocalAddress[7], + pLocalAddress[8], + pLocalAddress[9], + pLocalAddress[10], + pLocalAddress[11], + pLocalAddress[12], + pLocalAddress[13], + pLocalAddress[14], + pLocalAddress[15], + localPort, + pRemoteAddress[0], + pRemoteAddress[1], + pRemoteAddress[2], + pRemoteAddress[3], + pRemoteAddress[4], + pRemoteAddress[5], + pRemoteAddress[6], + pRemoteAddress[7], + pRemoteAddress[8], + pRemoteAddress[9], + pRemoteAddress[10], + pRemoteAddress[11], + pRemoteAddress[12], + pRemoteAddress[13], + pRemoteAddress[14], + pRemoteAddress[15], + remotePort); + } + + if(status == STATUS_SUCCESS) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\tInspecting: %s", + pString); + } + + break; + } + case FWPS_LAYER_ALE_BIND_REDIRECT_V4: + case FWPS_LAYER_ALE_BIND_REDIRECT_V6: + { + PSTR pProtocolString = 0; + + if(pProtocolValue && + pProtocolValue->type == FWP_UINT8) + { + if(pProtocolValue->uint8 == ICMPV4 || + pProtocolValue->uint8 == ICMPV6) + pProtocolString = "ICMP"; + else if(pProtocolValue->uint8 == TCP) + pProtocolString = "TCP"; + else if(pProtocolValue->uint8 == UDP) + pProtocolString = "UDP"; + else if(pProtocolValue->uint8 == IPPROTO_RAW) + pProtocolString = "Raw IP"; + } + + if(pLocalPortValue) + { + if(pLocalPortValue->type == FWP_UINT16) + localPort = pLocalPortValue->uint16; + else if(pLocalPortValue->type == FWP_UINT8) + localPort = pLocalPortValue->uint8; + } + + if(pLocalAddressValue) + { + if(pLocalAddressValue->type == FWP_UINT32) + { + pLocalAddress = (BYTE*)&(pLocalAddressValue->uint32); + + status = RtlStringCchPrintfA(pString, + MAX_STRING_SIZE, + "\n\t\t\t" + "%s %d.%d.%d.%d : %d\n", + pProtocolString, + pLocalAddress[3], + pLocalAddress[2], + pLocalAddress[1], + pLocalAddress[0], + localPort); + } + else if(pLocalAddressValue->type == FWP_BYTE_ARRAY16_TYPE) + { + pLocalAddress = (BYTE*)pLocalAddressValue->byteArray16; + + status = RtlStringCchPrintfA(pString, + MAX_STRING_SIZE, + "\n\t\t\t" + "%s %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x : %d", + pProtocolString, + pLocalAddress[0], + pLocalAddress[1], + pLocalAddress[2], + pLocalAddress[3], + pLocalAddress[4], + pLocalAddress[5], + pLocalAddress[6], + pLocalAddress[7], + pLocalAddress[8], + pLocalAddress[9], + pLocalAddress[10], + pLocalAddress[11], + pLocalAddress[12], + pLocalAddress[13], + pLocalAddress[14], + pLocalAddress[15], + localPort); + } + + if(status == STATUS_SUCCESS) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\t\tInspecting: %s", + pString); + } + + break; + } + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + } + + HLPR_BAIL_LABEL: + + HLPR_DELETE_ARRAY(pString, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PerformBasicPacketExaminationAtOther()\n"); + +#endif /// DBG + + return; +} + +#if(NTDDI_VERSION >= NTDDI_WIN7) + +/** + @classify_function="ClassifyBasicPacketExamination" + + Purpose: Examines the packet and returns FWP_ACTION_CONTINUE.
+
+ Notes: Applies to the following layers:
+ FWPS_LAYER_INBOUND_IPPACKET_V{4/6}
+ FWPS_LAYER_INBOUND_IPPACKET_V{4/6}_DISCARD
+ FWPS_LAYER_OUTBOUND_IPPACKET_V{4/6}
+ FWPS_LAYER_OUTBOUND_IPPACKET_V{4/6}_DISCARD
+ FWPS_LAYER_IPFORWARD_V{4/6}
+ FWPS_LAYER_IPFORWARD_V{4/6}_DISCARD
+ FWPS_LAYER_INBOUND_TRANSPORT_V{4/6}
+ FWPS_LAYER_INBOUND_TRANSPORT_V{4/6}_DISCARD
+ FWPS_LAYER_OUTBOUND_TRANSPORT_V{4/6}
+ FWPS_LAYER_OUTBOUND_TRANSPORT_V{4/6}_DISCARD
+ FWPS_LAYER_STREAM_V{4/6}
+ FWPS_LAYER_STREAM_V{4/6}_DISCARD
+ FWPS_LAYER_DATAGRAM_DATA_V{4/6}
+ FWPS_LAYER_DATAGRAM_DATA_V{4/6}_DISCARD
+ FWPS_LAYER_INBOUND_ICMP_ERROR_V{4/6}
+ FWPS_LAYER_INBOUND_ICMP_ERROR_V{4/6}_DISCARD
+ FWPS_LAYER_OUTBOUND_ICMP_ERROR_V{4/6}
+ FWPS_LAYER_OUTBOUND_ICMP_ERROR_V{4/6}_DISCARD
+ FWPS_LAYER_ALE_RESOURCE_ASSIGNMENT_V{4/6}
+ FWPS_LAYER_ALE_RESOURCE_ASSIGNMENT_V{4/6}_DISCARD
+ FWPS_LAYER_ALE_AUTH_LISTEN_V{4/6}
+ FWPS_LAYER_ALE_AUTH_LISTEN_V{4/6}_DISCARD
+ FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V{4/6}
+ FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V{4/6}_DISCARD
+ FWPS_LAYER_ALE_AUTH_CONNECT_V{4/6}
+ FWPS_LAYER_ALE_AUTH_CONNECT_V{4/6}_DISCARD
+ FWPS_LAYER_ALE_FLOW_ESTABLISHED_V{4/6}
+ FWPS_LAYER_ALE_FLOW_ESTABLISHED_V{4/6}_DISCARD
+ FWPS_LAYER_ALE_RESOURCE_RELEASE_V{4/6}
+ FWPS_LAYER_ALE_ENDPOINT_CLOSURE_V{4/6}
+ FWPS_LAYER_ALE_CONNECT_REDIRECT_V{4/6}
+ FWPS_LAYER_ALE_BIND_REDIRECT_V{4/6}
+ FWPS_LAYER_STREAM_PACKET_V{4/6}
+ FWPS_LAYER_INBOUND_MAC_FRAME_ETHERNET
+ FWPS_LAYER_OUTBOUND_MAC_FRAME_ETHERNET
+ FWPS_LAYER_INBOUND_MAC_FRAME_NATIVE
+ FWPS_LAYER_OUTBOUND_MAC_FRAME_NATIVE
+ FWPS_LAYER_INGRESS_VSWITCH_ETHERNET
+ FWPS_LAYER_EGRESS_VSWITCH_ETHERNET
+ FWPS_LAYER_INGRESS_VSWITCH_TRANSPORT_V{4/6}
+ FWPS_LAYER_EGRESS_VSWITCH_TRANSPORT_V{4/6}
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/FF544893.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI ClassifyBasicPacketExamination(_In_ const FWPS_INCOMING_VALUES0* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES0* pMetadata, + _Inout_opt_ VOID* pLayerData, + _In_opt_ const VOID* pClassifyContext, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut) +{ + NT_ASSERT(pClassifyValues); + NT_ASSERT(pMetadata); + NT_ASSERT(pFilter); + NT_ASSERT(pClassifyOut); + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + NT_ASSERT(pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V4_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V4_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V6_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V4 || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V4_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V6 || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V6_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V4_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V6 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V6_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V4_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V6_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_STREAM_V4 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_V4_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_STREAM_V6 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_V6_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4 || + pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6 || + pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V4_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V6 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V6_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_ALE_RESOURCE_ASSIGNMENT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_RESOURCE_ASSIGNMENT_V4_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_ALE_RESOURCE_ASSIGNMENT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_RESOURCE_ASSIGNMENT_V6_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_LISTEN_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_LISTEN_V4_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_LISTEN_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_LISTEN_V6_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V6_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_ALE_RESOURCE_RELEASE_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_RESOURCE_RELEASE_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_ENDPOINT_CLOSURE_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_ENDPOINT_CLOSURE_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_CONNECT_REDIRECT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_CONNECT_REDIRECT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_BIND_REDIRECT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_BIND_REDIRECT_V6 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V6 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_MAC_FRAME_ETHERNET || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_MAC_FRAME_ETHERNET || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_MAC_FRAME_NATIVE || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_MAC_FRAME_NATIVE || + pClassifyValues->layerId == FWPS_LAYER_INGRESS_VSWITCH_ETHERNET || + pClassifyValues->layerId == FWPS_LAYER_EGRESS_VSWITCH_ETHERNET || + pClassifyValues->layerId == FWPS_LAYER_INGRESS_VSWITCH_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_INGRESS_VSWITCH_TRANSPORT_V6 || + pClassifyValues->layerId == FWPS_LAYER_EGRESS_VSWITCH_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_EGRESS_VSWITCH_TRANSPORT_V6); + +#else + + NT_ASSERT(pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V4_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V4_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V6_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V4 || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V4_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V6 || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V6_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V4_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V6 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V6_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V4_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V6_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_STREAM_V4 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_V4_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_STREAM_V4 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_V6_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4 || + pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6 || + pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V4_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V6 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V6_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_ALE_RESOURCE_ASSIGNMENT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_RESOURCE_ASSIGNMENT_V4_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_ALE_RESOURCE_ASSIGNMENT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_RESOURCE_ASSIGNMENT_V6_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_LISTEN_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_LISTEN_V4_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_LISTEN_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_LISTEN_V6_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V6_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_ALE_RESOURCE_RELEASE_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_RESOURCE_RELEASE_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_ENDPOINT_CLOSURE_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_ENDPOINT_CLOSURE_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_CONNECT_REDIRECT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_CONNECT_REDIRECT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_BIND_REDIRECT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_BIND_REDIRECT_V6 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V6); + +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> ClassifyBasicPacketExamination() [Layer: %s][FilterID: %#I64x][Rights: %#x]", + KrnlHlprFwpsLayerIDToString(pClassifyValues->layerId), + pFilter->filterId, + pClassifyOut->rights); + + NTSTATUS status = STATUS_SUCCESS; + CLASSIFY_DATA* pClassifyData = 0; + FWP_DIRECTION direction = FWP_DIRECTION_MAX; + FWP_VALUE* pDirectionValue = 0; + KIRQL originalIRQL = PASSIVE_LEVEL; + + HLPR_NEW(pClassifyData, + CLASSIFY_DATA, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pClassifyData, + status); + + if(pFilter->context & PCPEF_EXAMINE_UNDER_LOCK) + KeAcquireSpinLock(&g_bpeSpinLock, + &originalIRQL); + + if(pFilter->context & PCPEF_EXAMINE_INCOMING_VALUES) + LogClassifyValues(pClassifyValues); + + if(pFilter->context & PCPEF_EXAMINE_INCOMING_METADATA_VALUES) + LogMetadata(pMetadata); + + if(pFilter->context & PCPEF_EXAMINE_LAYER_DATA) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\tLayerData: %#p", + pLayerData); + + if(pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6) + direction = FWP_DIRECTION_INBOUND; + else if(pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V6) + direction = FWP_DIRECTION_OUTBOUND; + + pDirectionValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_DIRECTION); + if(pDirectionValue) + direction = (FWP_DIRECTION)pDirectionValue->uint32; + else + { + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_PACKET_DIRECTION)) + direction = pMetadata->packetDirection; + } + + pClassifyData->pClassifyValues = pClassifyValues; + pClassifyData->pMetadataValues = pMetadata; + pClassifyData->pPacket = pLayerData; + pClassifyData->pClassifyContext = pClassifyContext; + pClassifyData->pFilter = pFilter; + pClassifyData->flowContext = flowContext; + pClassifyData->pClassifyOut = pClassifyOut; + + if(pClassifyData->pPacket) + { + if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6) + PerformBasicPacketExaminationAtInboundNetwork(pClassifyData); + else if(pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V6) + PerformBasicPacketExaminationAtOutboundNetwork(pClassifyData); + else if(pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V4 || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V6) + PerformBasicPacketExaminationAtForward(pClassifyData); + else if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V6 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V6 || + (direction == FWP_DIRECTION_INBOUND && + (pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4 || + pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4 || /// Policy Change Reauthorization + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V6 || /// Policy Change Reauthorization + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V6))) + PerformBasicPacketExaminationAtInboundTransport(pClassifyData); + else if(pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6 || + (direction == FWP_DIRECTION_OUTBOUND && + (pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4 || + pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 || /// Policy Change Reauthorization + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6 || /// Policy Change Reauthorization + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V6))) + PerformBasicPacketExaminationAtOutboundTransport(pClassifyData); + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + else if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_MAC_FRAME_ETHERNET || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_MAC_FRAME_NATIVE) + PerformBasicPacketExaminationAtInboundMACFrame(pClassifyData); + else if(pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_MAC_FRAME_ETHERNET || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_MAC_FRAME_NATIVE) + PerformBasicPacketExaminationAtOutboundMACFrame(pClassifyData); + else if(pClassifyValues->layerId == FWPS_LAYER_INGRESS_VSWITCH_ETHERNET || + pClassifyValues->layerId == FWPS_LAYER_EGRESS_VSWITCH_ETHERNET) + PerformBasicPacketExaminationAtVSwitchEthernet(pClassifyData); + else if(pClassifyValues->layerId == FWPS_LAYER_INGRESS_VSWITCH_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_INGRESS_VSWITCH_TRANSPORT_V6 || + pClassifyValues->layerId == FWPS_LAYER_EGRESS_VSWITCH_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_EGRESS_VSWITCH_TRANSPORT_V6) + PerformBasicPacketExaminationAtVSwitchTransport(pClassifyData); + +#endif // (NTDDI_VERSION >= NTDDI_WIN8) + + else if(KrnlHlprFwpsLayerIsDiscard(pClassifyValues->layerId)) + PerformBasicPacketExaminationAtDiscard(pClassifyData); + else + PerformBasicPacketExaminationAtOther(pClassifyData); + } + else + { + if(KrnlHlprFwpsLayerIsDiscard(pClassifyValues->layerId)) + PerformBasicPacketExaminationAtDiscard(pClassifyData); + else + PerformBasicPacketExaminationAtOther(pClassifyData); + } + + if(pFilter->context & PCPEF_EXAMINE_CLASSIFY_CONTEXT) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\tClassifyContext: %#p", + pClassifyContext); + + if(pFilter->context & PCPEF_EXAMINE_FILTER) + LogFilter(pFilter); + + if(pFilter->context & PCPEF_EXAMINE_FLOW_CONTEXT) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\tflowContext: %#I64x", + flowContext); + + if(pFilter->context & PCPEF_EXAMINE_CLASSIFY_OUT) + LogClassifyOut(pClassifyOut); + + if(pFilter->context & PCPEF_EXAMINE_UNDER_LOCK) + KeReleaseSpinLock(&g_bpeSpinLock, + originalIRQL); + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! ClassifyBasicPacketExamination: [status: %#x]\n", + status); + + HLPR_DELETE(pClassifyData, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + + if(pClassifyOut->rights & FWPS_RIGHT_ACTION_WRITE) + pClassifyOut->actionType = FWP_ACTION_CONTINUE; + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- ClassifyBasicPacketExamination() [Layer: %s][FilterID: %#I64x][Action: %#x][Rights: %#x][Absorb: %s]\n", + KrnlHlprFwpsLayerIDToString(pClassifyValues->layerId), + pFilter->filterId, + pClassifyOut->actionType, + pClassifyOut->rights, + (pClassifyOut->flags & FWPS_CLASSIFY_OUT_FLAG_ABSORB) ? "TRUE" : "FALSE"); + + return; +} + +#else + +/** + @classify_function="ClassifyBasicPacketExamination" + + Purpose: Examines the packet and returns FWP_ACTION_CONTINUE.
+
+ Notes: Applies to the following layers:
+ FWPS_LAYER_INBOUND_IPPACKET_V{4/6}
+ FWPS_LAYER_INBOUND_IPPACKET_V{4/6}_DISCARD
+ FWPS_LAYER_OUTBOUND_IPPACKET_V{4/6}
+ FWPS_LAYER_OUTBOUND_IPPACKET_V{4/6}_DISCARD
+ FWPS_LAYER_IPFORWARD_V{4/6}
+ FWPS_LAYER_IPFORWARD_V{4/6}_DISCARD
+ FWPS_LAYER_INBOUND_TRANSPORT_V{4/6}
+ FWPS_LAYER_INBOUND_TRANSPORT_V{4/6}_DISCARD
+ FWPS_LAYER_OUTBOUND_TRANSPORT_V{4/6}
+ FWPS_LAYER_OUTBOUND_TRANSPORT_V{4/6}_DISCARD
+ FWPS_LAYER_STREAM_V{4/6}
+ FWPS_LAYER_STREAM_V{4/6}_DISCARD
+ FWPS_LAYER_DATAGRAM_DATA_V{4/6}
+ FWPS_LAYER_DATAGRAM_DATA_V{4/6}_DISCARD
+ FWPS_LAYER_INBOUND_ICMP_ERROR_V{4/6}
+ FWPS_LAYER_INBOUND_ICMP_ERROR_V{4/6}_DISCARD
+ FWPS_LAYER_OUTBOUND_ICMP_ERROR_V{4/6}
+ FWPS_LAYER_OUTBOUND_ICMP_ERROR_V{4/6}_DISCARD
+ FWPS_LAYER_ALE_RESOURCE_ASSIGNMENT_V{4/6}
+ FWPS_LAYER_ALE_RESOURCE_ASSIGNMENT_V{4/6}_DISCARD
+ FWPS_LAYER_ALE_AUTH_LISTEN_V{4/6}
+ FWPS_LAYER_ALE_AUTH_LISTEN_V{4/6}_DISCARD
+ FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V{4/6}
+ FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V{4/6}_DISCARD
+ FWPS_LAYER_ALE_AUTH_CONNECT_V{4/6}
+ FWPS_LAYER_ALE_AUTH_CONNECT_V{4/6}_DISCARD
+ FWPS_LAYER_ALE_FLOW_ESTABLISHED_V{4/6}
+ FWPS_LAYER_ALE_FLOW_ESTABLISHED_V{4/6}_DISCARD
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/FF544890.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI ClassifyBasicPacketExamination(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pLayerData, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut) +{ + NT_ASSERT(pClassifyValues); + NT_ASSERT(pMetadata); + NT_ASSERT(pFilter); + NT_ASSERT(pClassifyOut); + NT_ASSERT(pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V4_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V4_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V6_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V4 || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V4_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V6 || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V6_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V4_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V6 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V6_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V4_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V6_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_STREAM_V4 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_V4_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_STREAM_V6 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_V6_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4 || + pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6 || + pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V4_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V6 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V6_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_ALE_RESOURCE_ASSIGNMENT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_RESOURCE_ASSIGNMENT_V4_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_ALE_RESOURCE_ASSIGNMENT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_RESOURCE_ASSIGNMENT_V6_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_LISTEN_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_LISTEN_V4_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_LISTEN_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_LISTEN_V6_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V6_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6_DISCARD); + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> ClassifyBasicPacketExamination() [Layer: %s][FilterID: %#I64x][Rights: %#x]", + KrnlHlprFwpsLayerIDToString(pClassifyValues->layerId), + pFilter->filterId, + pClassifyOut->rights); + + NTSTATUS status = STATUS_SUCCESS; + CLASSIFY_DATA* pClassifyData = 0; + FWP_DIRECTION direction = FWP_DIRECTION_MAX; + FWP_VALUE* pDirectionValue = 0; + KIRQL originalIRQL = PASSIVE_LEVEL; + + HLPR_NEW(pClassifyData, + CLASSIFY_DATA, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pClassifyData, + status); + + if(pFilter->context & PCPEF_EXAMINE_UNDER_LOCK) + KeAcquireSpinLock(&g_bpeSpinLock, + &originalIRQL); + + if(pFilter->context & PCPEF_EXAMINE_INCOMING_VALUES) + LogClassifyValues(pClassifyValues); + + if(pFilter->context & PCPEF_EXAMINE_INCOMING_METADATA_VALUES) + LogMetadata(pMetadata); + + if(pFilter->context & PCPEF_EXAMINE_LAYER_DATA) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\tLayerData: %#p", + pLayerData); + + if(pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6) + direction = FWP_DIRECTION_INBOUND; + else if(pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V6) + direction = FWP_DIRECTION_OUTBOUND; + + pDirectionValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_DIRECTION); + if(pDirectionValue) + direction = (FWP_DIRECTION)pDirectionValue->uint32; + else + { + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_PACKET_DIRECTION)) + direction = pMetadata->packetDirection; + } + + pClassifyData->pClassifyValues = pClassifyValues; + pClassifyData->pMetadataValues = pMetadata; + pClassifyData->pPacket = pLayerData; + pClassifyData->pFilter = pFilter; + pClassifyData->flowContext = flowContext; + pClassifyData->pClassifyOut = pClassifyOut; + + if(pClassifyData->pPacket) + { + if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6) + PerformBasicPacketExaminationAtInboundNetwork(pClassifyData); + else if(pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V6) + PerformBasicPacketExaminationAtOutboundNetwork(pClassifyData); + else if(pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V4 || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V6) + PerformBasicPacketExaminationAtForward(pClassifyData); + else if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V6 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V6 || + (direction == FWP_DIRECTION_INBOUND && + (pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4 || + pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4 || /// Policy Change Reauthorization + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V6 || /// Policy Change Reauthorization + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6))) + PerformBasicPacketExaminationAtInboundTransport(pClassifyData); + else if(pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6 || + (direction == FWP_DIRECTION_OUTBOUND && + (pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4 || + pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 || /// Policy Change Reauthorization + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6 || /// Policy Change Reauthorization + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6))) + PerformBasicPacketExaminationAtOutboundTransport(pClassifyData); + else if(KrnlHlprFwpsLayerIsDiscard(pClassifyValues->layerId)) + PerformBasicPacketExaminationAtDiscard(pClassifyData); + else + PerformBasicPacketExaminationAtOther(pClassifyData); + } + else + { + if(KrnlHlprFwpsLayerIsDiscard(pClassifyValues->layerId)) + PerformBasicPacketExaminationAtDiscard(pClassifyData); + else + PerformBasicPacketExaminationAtOther(pClassifyData); + } + + if(pFilter->context & PCPEF_EXAMINE_FILTER) + LogFilter(pFilter); + + if(pFilter->context & PCPEF_EXAMINE_FLOW_CONTEXT) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + "\tflowContext: %#I64x", + flowContext); + + if(pFilter->context & PCPEF_EXAMINE_CLASSIFY_OUT) + LogClassifyOut(pClassifyOut); + + if(pFilter->context & PCPEF_EXAMINE_UNDER_LOCK) + KeReleaseSpinLock(&g_bpeSpinLock, + originalIRQL); + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! ClassifyBasicPacketExamination: [status: %#x]\n", + status); + + HLPR_DELETE(pClassifyData, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + + if(pClassifyOut->rights & FWPS_RIGHT_ACTION_WRITE) + pClassifyOut->actionType = FWP_ACTION_CONTINUE; + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- ClassifyBasicPacketExamination() [Layer: %s][FilterID: %#I64x][Action: %#x][Rights: %#x][Absorb: %s]\n", + KrnlHlprFwpsLayerIDToString(pClassifyValues->layerId), + pFilter->filterId, + pClassifyOut->actionType, + pClassifyOut->rights, + (pClassifyOut->flags & FWPS_CLASSIFY_OUT_FLAG_ABSORB) ? "TRUE" : "FALSE"); + + return; +} + +#endif // (NTDDI_VERSION >= NTDDI_WIN7) diff --git a/network/trans/WFPSampler/sys/ClassifyFunctions_BasicPacketExaminationCallouts.h b/network/trans/WFPSampler/sys/ClassifyFunctions_BasicPacketExaminationCallouts.h new file mode 100644 index 000000000..32ce00cfe --- /dev/null +++ b/network/trans/WFPSampler/sys/ClassifyFunctions_BasicPacketExaminationCallouts.h @@ -0,0 +1,53 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// ClassifyFunctions_BasicPacketExaminationCallouts.h +// +// Abstract: +// This module contains prototypes of WFP Classify functions for examining indicated +// NET_BUFFER_LISTS. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Enhance function declaration for IntelliSense +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef CLASSIFY_BASIC_PACKET_EXAMINATION_H +#define CLASSIFY_BASIC_PACKET_EXAMINATION_H + +#if(NTDDI_VERSION >= NTDDI_WIN7) + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI ClassifyBasicPacketExamination(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pLayerData, + _In_opt_ const VOID* pClassifyContext, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut); + +#else + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI ClassifyBasicPacketExamination(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pLayerData, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut); + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + +#endif /// CLASSIFY_BASIC_PACKET_EXAMINATION_H diff --git a/network/trans/WFPSampler/sys/ClassifyFunctions_BasicPacketInjectionCallouts.cpp b/network/trans/WFPSampler/sys/ClassifyFunctions_BasicPacketInjectionCallouts.cpp new file mode 100644 index 000000000..2409a64da --- /dev/null +++ b/network/trans/WFPSampler/sys/ClassifyFunctions_BasicPacketInjectionCallouts.cpp @@ -0,0 +1,4231 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// ClassifyFunctions_BasicPacketInjectionCallouts.cpp +// +// Abstract: +// This module contains WFP Classify functions for injecting packets back into the data path +// using the clone / block / inject method. +// +// Naming Convention: +// +// +// +// i.e. +// ClassifyBasicPacketInjection +// +// +// Classify - Function is an FWPS_CALLOUT_CLASSIFY_FN +// +// BasicPacketInjection - Function demonstrates the clone / block / inject model. +// +// +// +// i.e. +// TriggerBasicPacketInjectionOutOfBand +// +// +// { +// - +// Trigger - Initiates the desired scenario. +// Perform - Executes the desired scenario. +// } +// +// BasicPacketInjection - Function demonstrates the clone / block / inject model. +// +// DeferredProcedureCall - DPC routine for Out of Band injection which dispatches the +// proper Perform Function. +// WorkItemRoutine - WorkItem Routine for Out of Band Injection which dispatches +// the proper Perform Function. +// AtInboundMACFrame - Function operates on: +// FWPM_LAYER_INBOUND_MAC_FRAME_ETHERNET, and +// FWPM_LAYER_INBOUND_MAC_NATIVE. +// AtOutboundMACFrame - Function operates on: +// FWPM_LAYER_OUTBOUND_MAC_FRAME_ETHERNET, and +// FWPM_LAYER_OUTBOUND_MAC_NATIVE. +// AtEgressVSwitchEthernet - Function operates on: +// FWPM_LAYER_EGRESS_VSWITCH_ETHERNET. +// AtIngressVSwitchEthernet - Function operates on: +// FWPM_LAYER_INGRESS_VSWITCH_ETHERNET. +// AtInboundNetwork - Function operates on: +// FWPM_LAYER_INBOUND_IPPACKET_V{4/6} +// AtOutboundNetwork - Function operates on: +// FWPM_LAYER_OUTBOUND_IPPACKET_V{4/6} +// AtForward - Function operates on: +// FWPM_LAYER_IPFORWARD_V{4/6} +// AtInboundTransport - Function operates on: +// FWPM_LAYER_INBOUND_TRANSPORT_V{4/6}, +// FWPM_LAYER_INBOUND_ICMP_ERROR_V{4/6}, +// FWPM_LAYER_DATAGRAM_DATA_V{4/6}, +// FWPM_LAYER_STREAM_PACKET_V{4/6}, and +// FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V{4/6} +// FWPM_LAYER_ALE_FLOW_ESTABLISHED_V{4/6} +// AtOutboundTransport - Function operates on: +// FWPM_LAYER_OUTBOUND_TRANSPORT_V{4/6}, +// FWPM_LAYER_OUTBOUND_ICMP_ERROR_V{4/6}, +// FWPM_LAYER_DATAGRAM_DATA_V{4/6}, +// FWPM_LAYER_STREAM_PACKET_V{4/6}, and +// FWPM_LAYER_ALE_AUTH_CONNECT_V{4/6} +// FWPM_LAYER_ALE_FLOW_ESTABLISHED_V{4/6} +// +// Private Functions: +// BasicPacketInjectionDeferredProcedureCall(), +// BasicPacketInjectionWorkItemRoutine(), +// PerformBasicPacketInjectionAtEgressVSwitchEthernet(), +// PerformBasicPacketInjectionAtForward(), +// PerformBasicPacketInjectionAtInboundMACFrame(), +// PerformBasicPacketInjectionAtInboundNetwork(), +// PerformBasicPacketInjectionAtInboundTransport(), +// PerformBasicPacketInjectionAtIngressVSwitchEthernet(), +// PerformBasicPacketInjectionAtOutboundMACFrame(), +// PerformBasicPacketInjectionAtOutboundNetwork(), +// PerformBasicPacketInjectionAtOutboundTransport(), +// TriggerBasicPacketInjectionInline(), +// TriggerBasicPacketInjectionOutOfBand(), +// +// Public Functions: +// ClassifyBasicPacketInjection(), +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Enhance function declaration for IntelliSense, enhance +// traces, fix weakhost injection, fix expected +// offsets, and add support for multiple injectors and +// controlData. +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "Framework_WFPSamplerCalloutDriver.h" /// . +#include "ClassifyFunctions_BasicPacketInjectionCallouts.tmh" /// $(OBJ_PATH)\$(O)\ + +#if DBG + +INJECTION_COUNTERS g_bpiTotalClassifies = {0}; +INJECTION_COUNTERS g_bpiTotalBlockedAndAbsorbed = {0}; +INJECTION_COUNTERS g_bpiTotalPermitted = {0}; +INJECTION_COUNTERS g_bpiTotalSuccessfulInjectionCalls = {0}; +INJECTION_COUNTERS g_bpiTotalFailedInjectionCalls = {0}; +INJECTION_COUNTERS g_bpiOutstandingNBLClones = {0}; + +/** + @function="PrvBasicPacketInjectionCountersIncrement" + + Purpose: Increment the appropriate counters based on the layerId and direction.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Desktop/MS683615.aspx
+*/ +VOID PrvBasicPacketInjectionCountersIncrement(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadataValues, + _Inout_ INJECTION_COUNTERS* pCounters) +{ + UINT32 direction = FWP_DIRECTION_MAX; + FWP_VALUE* pDirectionValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_DIRECTION); + + direction = KrnlHlprFwpsLayerGetDirection(pClassifyValues->layerId); + + if(pDirectionValue && + pDirectionValue->type == FWP_UINT32) + direction = (FWP_DIRECTION)pDirectionValue->uint32; + else + { + if(pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V4 || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V4_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V6 || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V6_DISCARD) + { + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadataValues, + FWPS_METADATA_FIELD_FORWARD_LAYER_INBOUND_PASS_THRU)) + direction = FWP_DIRECTION_INBOUND; + else if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadataValues, + FWPS_METADATA_FIELD_FORWARD_LAYER_OUTBOUND_PASS_THRU)) + direction = FWP_DIRECTION_OUTBOUND; + } + else + { + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadataValues, + FWPS_METADATA_FIELD_PACKET_DIRECTION)) + direction = pMetadataValues->packetDirection; + } + } + + switch(pClassifyValues->layerId) + { + case FWPS_LAYER_INBOUND_IPPACKET_V4: + { + InterlockedIncrement64((LONG64*)&(pCounters->inboundNetwork_IPv4)); + + break; + } + case FWPS_LAYER_INBOUND_IPPACKET_V6: + { + InterlockedIncrement64((LONG64*)&(pCounters->inboundNetwork_IPv6)); + + break; + } + case FWPS_LAYER_OUTBOUND_IPPACKET_V4: + { + InterlockedIncrement64((LONG64*)&(pCounters->outboundNetwork_IPv4)); + + break; + } + case FWPS_LAYER_OUTBOUND_IPPACKET_V6: + { + InterlockedIncrement64((LONG64*)&(pCounters->outboundNetwork_IPv6)); + + break; + } + case FWPS_LAYER_IPFORWARD_V4: + { + if(direction == FWP_DIRECTION_OUTBOUND) + InterlockedIncrement64((LONG64*)&(pCounters->inboundForward_IPv4)); + else + InterlockedIncrement64((LONG64*)&(pCounters->outboundForward_IPv4)); + + break; + } + case FWPS_LAYER_IPFORWARD_V6: + { + if(direction == FWP_DIRECTION_INBOUND) + InterlockedIncrement64((LONG64*)&(pCounters->inboundForward_IPv6)); + else + InterlockedIncrement64((LONG64*)&(pCounters->outboundForward_IPv6)); + + break; + } + case FWPS_LAYER_INBOUND_TRANSPORT_V4: + { + InterlockedIncrement64((LONG64*)&(pCounters->inboundTransport_IPv4)); + + break; + } + case FWPS_LAYER_INBOUND_TRANSPORT_V6: + { + InterlockedIncrement64((LONG64*)&(pCounters->inboundTransport_IPv6)); + + break; + } + case FWPS_LAYER_OUTBOUND_TRANSPORT_V4: + { + InterlockedIncrement64((LONG64*)&(pCounters->outboundTransport_IPv4)); + + break; + } + case FWPS_LAYER_OUTBOUND_TRANSPORT_V6: + { + InterlockedIncrement64((LONG64*)&(pCounters->outboundTransport_IPv6)); + + + break; + } + case FWPS_LAYER_DATAGRAM_DATA_V4: + { + if(direction == FWP_DIRECTION_INBOUND) + InterlockedIncrement64((LONG64*)&(pCounters->inboundTransport_IPv4)); + else + InterlockedIncrement64((LONG64*)&(pCounters->outboundTransport_IPv4)); + + break; + } + case FWPS_LAYER_DATAGRAM_DATA_V6: + { + if(direction == FWP_DIRECTION_INBOUND) + InterlockedIncrement64((LONG64*)&(pCounters->inboundTransport_IPv6)); + else + InterlockedIncrement64((LONG64*)&(pCounters->outboundTransport_IPv6)); + + break; + } + case FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4: + { + if(direction == FWP_DIRECTION_INBOUND) + InterlockedIncrement64((LONG64*)&(pCounters->inboundTransport_IPv4)); + else + InterlockedIncrement64((LONG64*)&(pCounters->outboundTransport_IPv4)); + + break; + } + case FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6: + { + if(direction == FWP_DIRECTION_INBOUND) + InterlockedIncrement64((LONG64*)&(pCounters->inboundTransport_IPv6)); + else + InterlockedIncrement64((LONG64*)&(pCounters->outboundTransport_IPv6)); + + break; + } + case FWPS_LAYER_ALE_AUTH_CONNECT_V4: + { + if(direction == FWP_DIRECTION_INBOUND) + InterlockedIncrement64((LONG64*)&(pCounters->inboundTransport_IPv4)); + else + InterlockedIncrement64((LONG64*)&(pCounters->outboundTransport_IPv4)); + + break; + } + case FWPS_LAYER_ALE_AUTH_CONNECT_V6: + { + if(direction == FWP_DIRECTION_INBOUND) + InterlockedIncrement64((LONG64*)&(pCounters->inboundTransport_IPv6)); + else + InterlockedIncrement64((LONG64*)&(pCounters->outboundTransport_IPv6)); + + break; + } + case FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4: + { + if(direction == FWP_DIRECTION_INBOUND) + InterlockedIncrement64((LONG64*)&(pCounters->inboundTransport_IPv4)); + else + InterlockedIncrement64((LONG64*)&(pCounters->outboundTransport_IPv4)); + + break; + } + case FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6: + { + if(direction == FWP_DIRECTION_INBOUND) + InterlockedIncrement64((LONG64*)&(pCounters->inboundTransport_IPv6)); + else + InterlockedIncrement64((LONG64*)&(pCounters->outboundTransport_IPv6)); + + break; + } + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + case FWPS_LAYER_STREAM_PACKET_V4: + { + if(direction == FWP_DIRECTION_INBOUND) + InterlockedIncrement64((LONG64*)&(pCounters->inboundTransport_IPv4)); + else + InterlockedIncrement64((LONG64*)&(pCounters->outboundTransport_IPv4)); + + break; + } + case FWPS_LAYER_STREAM_PACKET_V6: + { + if(direction == FWP_DIRECTION_INBOUND) + InterlockedIncrement64((LONG64*)&(pCounters->inboundTransport_IPv6)); + else + InterlockedIncrement64((LONG64*)&(pCounters->outboundTransport_IPv6)); + + break; + } + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + case FWPS_LAYER_INBOUND_MAC_FRAME_ETHERNET: + { + UINT16 etherType = pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_MAC_FRAME_ETHERNET_ETHER_TYPE].value.uint16; + + if(etherType == 0x86DD) + InterlockedIncrement64((LONG64*)&(pCounters->inboundMAC_IPv6)); + else if(etherType == 0x800) + InterlockedIncrement64((LONG64*)&(pCounters->inboundMAC_IPv4)); + else + InterlockedIncrement64((LONG64*)&(pCounters->inboundMAC_Unknown)); + + break; + } + case FWPS_LAYER_OUTBOUND_MAC_FRAME_ETHERNET: + { + UINT16 etherType = pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_MAC_FRAME_ETHERNET_ETHER_TYPE].value.uint16; + + if(etherType == 0x86DD) + InterlockedIncrement64((LONG64*)&(pCounters->outboundMAC_IPv6)); + else if(etherType == 0x800) + InterlockedIncrement64((LONG64*)&(pCounters->outboundMAC_IPv4)); + else + InterlockedIncrement64((LONG64*)&(pCounters->outboundMAC_Unknown)); + + break; + } + case FWPS_LAYER_INBOUND_MAC_FRAME_NATIVE: + { + InterlockedIncrement64((LONG64*)&(pCounters->inboundMAC_Unknown)); + + break; + } + case FWPS_LAYER_OUTBOUND_MAC_FRAME_NATIVE: + { + InterlockedIncrement64((LONG64*)&(pCounters->outboundMAC_Unknown)); + + break; + } + case FWPS_LAYER_INGRESS_VSWITCH_ETHERNET: + { + UINT16 etherType = pClassifyValues->incomingValue[FWPS_FIELD_INGRESS_VSWITCH_ETHERNET_ETHER_TYPE].value.uint16; + + if(etherType == 0x86DD) + InterlockedIncrement64((LONG64*)&(pCounters->ingressVSwitch_IPv6)); + else if(etherType == 0x800) + InterlockedIncrement64((LONG64*)&(pCounters->ingressVSwitch_IPv4)); + else + InterlockedIncrement64((LONG64*)&(pCounters->ingressVSwitch_Unknown)); + + break; + } + case FWPS_LAYER_EGRESS_VSWITCH_ETHERNET: + { + UINT16 etherType = pClassifyValues->incomingValue[FWPS_FIELD_EGRESS_VSWITCH_ETHERNET_ETHER_TYPE].value.uint16; + + if(etherType == 0x86DD) + InterlockedIncrement64((LONG64*)&(pCounters->egressVSwitch_IPv6)); + else if(etherType == 0x800) + InterlockedIncrement64((LONG64*)&(pCounters->egressVSwitch_IPv4)); + else + InterlockedIncrement64((LONG64*)&(pCounters->egressVSwitch_Unknown)); + + break; + } + +#endif // (NTDDI_VERSION >= NTDDI_WIN8) +#endif // (NTDDI_VERSION >= NTDDI_WIN7) + + } + + return; +} + +/** + @private_function="PrvBasicPacketInjectionCountersIncrementTotalActionResults" + + Purpose: Increment the appropriate counters based on the layerId, direction, and action.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +VOID PrvBasicPacketInjectionCountersIncrementTotalActionResults(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadataValues, + _In_ const FWPS_CLASSIFY_OUT* pClassifyOut) +{ + INJECTION_COUNTERS* pCounters = 0; + + if(pClassifyOut->actionType == FWP_ACTION_BLOCK) + { + NT_ASSERT(pClassifyOut->flags & FWPS_CLASSIFY_OUT_FLAG_ABSORB); + + pCounters = &g_bpiTotalBlockedAndAbsorbed; + } + else + pCounters = &g_bpiTotalPermitted; + + PrvBasicPacketInjectionCountersIncrement(pClassifyValues, + pMetadataValues, + pCounters); + + return; +} + +/** + @function="BasicPacketInjectionCountersIncrement" + + Purpose: Increment the appropriate counters based on the injection handle.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Desktop/MS683615.aspx
+*/ +VOID BasicPacketInjectionCountersIncrement(_In_ HANDLE injectionHandle, + _Inout_ INJECTION_COUNTERS* pCounters) +{ + if(injectionHandle == g_pIPv4InboundNetworkInjectionHandles[0] || + injectionHandle == g_pIPv4InboundNetworkInjectionHandles[1]) + InterlockedIncrement64((LONG64*)&(pCounters->inboundNetwork_IPv4)); + else if(injectionHandle == g_pIPv6InboundNetworkInjectionHandles[0] || + injectionHandle == g_pIPv6InboundNetworkInjectionHandles[1]) + InterlockedIncrement64((LONG64*)&(pCounters->inboundNetwork_IPv6)); + else if(injectionHandle == g_pIPv4OutboundNetworkInjectionHandles[0] || + injectionHandle == g_pIPv4OutboundNetworkInjectionHandles[1]) + InterlockedIncrement64((LONG64*)&(pCounters->outboundNetwork_IPv4)); + else if(injectionHandle == g_pIPv6OutboundNetworkInjectionHandles[0] || + injectionHandle == g_pIPv6OutboundNetworkInjectionHandles[1]) + InterlockedIncrement64((LONG64*)&(pCounters->outboundNetwork_IPv6)); + else if(injectionHandle == g_pIPv4InboundForwardInjectionHandles[0] || + injectionHandle == g_pIPv4InboundForwardInjectionHandles[1]) + InterlockedIncrement64((LONG64*)&(pCounters->inboundForward_IPv4)); + else if(injectionHandle == g_pIPv6InboundForwardInjectionHandles[0] || + injectionHandle == g_pIPv6InboundForwardInjectionHandles[1]) + InterlockedIncrement64((LONG64*)&(pCounters->inboundForward_IPv6)); + else if(injectionHandle == g_pIPv4OutboundForwardInjectionHandles[0] || + injectionHandle == g_pIPv4OutboundForwardInjectionHandles[1]) + InterlockedIncrement64((LONG64*)&(pCounters->outboundForward_IPv4)); + else if(injectionHandle == g_pIPv6OutboundForwardInjectionHandles[0] || + injectionHandle == g_pIPv6OutboundForwardInjectionHandles[1]) + InterlockedIncrement64((LONG64*)&(pCounters->outboundForward_IPv6)); + else if(injectionHandle == g_pIPv4InboundTransportInjectionHandles[0] || + injectionHandle == g_pIPv4InboundTransportInjectionHandles[1]) + InterlockedIncrement64((LONG64*)&(pCounters->inboundTransport_IPv4)); + else if(injectionHandle == g_pIPv6InboundTransportInjectionHandles[0] || + injectionHandle == g_pIPv6InboundTransportInjectionHandles[1]) + InterlockedIncrement64((LONG64*)&(pCounters->inboundTransport_IPv6)); + else if(injectionHandle == g_pIPv4OutboundTransportInjectionHandles[0] || + injectionHandle == g_pIPv4OutboundTransportInjectionHandles[1]) + InterlockedIncrement64((LONG64*)&(pCounters->outboundTransport_IPv4)); + else if(injectionHandle == g_pIPv6OutboundTransportInjectionHandles[0] || + injectionHandle == g_pIPv6OutboundTransportInjectionHandles[1]) + InterlockedIncrement64((LONG64*)&(pCounters->outboundTransport_IPv6)); + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + else if(injectionHandle == g_pIPv4InboundMACInjectionHandles[0] || + injectionHandle == g_pIPv4InboundMACInjectionHandles[1]) + InterlockedIncrement64((LONG64*)&(pCounters->inboundMAC_IPv4)); + else if(injectionHandle == g_pIPv6InboundMACInjectionHandles[0] || + injectionHandle == g_pIPv6InboundMACInjectionHandles[1]) + InterlockedIncrement64((LONG64*)&(pCounters->inboundMAC_IPv6)); + else if(injectionHandle == g_pInboundMACInjectionHandles[0] || + injectionHandle == g_pInboundMACInjectionHandles[1]) + InterlockedIncrement64((LONG64*)&(pCounters->inboundMAC_Unknown)); + else if(injectionHandle == g_pIPv4OutboundMACInjectionHandles[0] || + injectionHandle == g_pIPv4OutboundMACInjectionHandles[1]) + InterlockedIncrement64((LONG64*)&(pCounters->outboundMAC_IPv4)); + else if(injectionHandle == g_pIPv6OutboundMACInjectionHandles[0] || + injectionHandle == g_pIPv6OutboundMACInjectionHandles[1]) + InterlockedIncrement64((LONG64*)&(pCounters->outboundMAC_IPv6)); + else if(injectionHandle == g_pOutboundMACInjectionHandles[0] || + injectionHandle == g_pOutboundMACInjectionHandles[1]) + InterlockedIncrement64((LONG64*)&(pCounters->outboundMAC_Unknown)); + else if(injectionHandle == g_pIPv4IngressVSwitchEthernetInjectionHandles[0] || + injectionHandle == g_pIPv4IngressVSwitchEthernetInjectionHandles[1]) + InterlockedIncrement64((LONG64*)&(pCounters->ingressVSwitch_IPv4)); + else if(injectionHandle == g_pIPv6IngressVSwitchEthernetInjectionHandles[0] || + injectionHandle == g_pIPv6IngressVSwitchEthernetInjectionHandles[1]) + InterlockedIncrement64((LONG64*)&(pCounters->ingressVSwitch_IPv6)); + else if(injectionHandle == g_pIngressVSwitchEthernetInjectionHandles[0] || + injectionHandle == g_pIngressVSwitchEthernetInjectionHandles[1]) + InterlockedIncrement64((LONG64*)&(pCounters->ingressVSwitch_Unknown)); + else if(injectionHandle == g_pIPv4EgressVSwitchEthernetInjectionHandles[0] || + injectionHandle == g_pIPv4EgressVSwitchEthernetInjectionHandles[1]) + InterlockedIncrement64((LONG64*)&(pCounters->egressVSwitch_IPv4)); + else if(injectionHandle == g_pIPv6EgressVSwitchEthernetInjectionHandles[0] || + injectionHandle == g_pIPv6EgressVSwitchEthernetInjectionHandles[1]) + InterlockedIncrement64((LONG64*)&(pCounters->egressVSwitch_IPv6)); + else if(injectionHandle == g_pEgressVSwitchEthernetInjectionHandles[0] || + injectionHandle == g_pEgressVSwitchEthernetInjectionHandles[1]) + InterlockedIncrement64((LONG64*)&(pCounters->egressVSwitch_Unknown)); + +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) + + return; +} + +#endif /// DBG + +#if(NTDDI_VERSION >= NTDDI_WIN8) + +/** + @private_function="PerformBasicPacketInjectionAtInboundMACFrame" + + Purpose: Clones the NET_BUFFER_LIST and injects the clone back to the stack from the + incoming MAC Layers using FwpsInjectMacReceiveAsync().
+
+ Notes: Applies to the following inbound layers:
+ FWPM_LAYER_INBOUND_MAC_FRAME_ETHERNET
+ FWPM_LAYER_INBOUND_MAC_FRAME_NATIVE
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/HH439588.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF546324.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +NTSTATUS PerformBasicPacketInjectionAtInboundMACFrame(_In_ CLASSIFY_DATA** ppClassifyData, + _In_ INJECTION_DATA** ppInjectionData, + _In_ BOOLEAN isInline = FALSE) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PerformBasicPacketInjectionAtInboundMACFrame()\n"); + +#endif /// DBG + + NT_ASSERT(ppClassifyData); + NT_ASSERT(ppInjectionData); + NT_ASSERT(*ppClassifyData); + NT_ASSERT(*ppInjectionData); + + NTSTATUS status = STATUS_SUCCESS; + FWPS_INCOMING_VALUES* pClassifyValues = (FWPS_INCOMING_VALUES*)(*ppClassifyData)->pClassifyValues; + FWPS_INCOMING_METADATA_VALUES* pMetadata = (FWPS_INCOMING_METADATA_VALUES*)(*ppClassifyData)->pMetadataValues; + IF_INDEX interfaceIndex = 0; + NDIS_PORT_NUMBER ndisPort = 0; + NET_BUFFER_LIST* pNetBufferList = 0; + BASIC_PACKET_INJECTION_COMPLETION_DATA* pCompletionData = 0; + UINT32 bytesRetreated = 0; + FWP_VALUE* pInterfaceIndex = 0; + FWP_VALUE* pNDISPort = 0; + +#if DBG + + KIRQL irql = KeGetCurrentIrql(); + HANDLE injectionHandle = (*ppInjectionData)->injectionHandle; + +#endif /// DBG + +#pragma warning(push) +#pragma warning(disable: 6014) /// pCompletionData will be freed in completionFn using BasicPacketInjectionCompletionDataDestroy + + HLPR_NEW(pCompletionData, + BASIC_PACKET_INJECTION_COMPLETION_DATA, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pCompletionData, + status); + +#pragma warning(pop) + + KeInitializeSpinLock(&(pCompletionData->spinLock)); + + pCompletionData->performedInline = isInline; + pCompletionData->pClassifyData = *ppClassifyData; + pCompletionData->pInjectionData = *ppInjectionData; + + /// Responsibility for freeing this memory has been transferred to the pCompletionData + *ppClassifyData = 0; + + *ppInjectionData = 0; + + pInterfaceIndex = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_INTERFACE_INDEX); + if(pInterfaceIndex && + pInterfaceIndex->type == FWP_UINT32) + interfaceIndex = (IF_INDEX)pInterfaceIndex->uint32; + + pNDISPort = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_NDIS_PORT); + if(pNDISPort && + pNDISPort->type == FWP_UINT32) + ndisPort = (NDIS_PORT_NUMBER)pNDISPort->uint32; + + /// If NATIVE, initial offset is at the MAC Header ... + if(pClassifyValues->layerId != FWPS_LAYER_INBOUND_MAC_FRAME_NATIVE && + FWPS_IS_L2_METADATA_FIELD_PRESENT(pMetadata, + FWPS_L2_METADATA_FIELD_ETHERNET_MAC_HEADER_SIZE)) + bytesRetreated = pMetadata->ethernetMacHeaderSize; + + if(bytesRetreated) + { + /// ... otherwise the offset is at the IP Header, so retreat the size of the MAC Header ... + status = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket), + bytesRetreated, + 0, + 0); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketInjectionAtInboundMACFrame: NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + + HLPR_BAIL; + } + } + + /// ... clone the entire NET_BUFFER_LIST ... + status = FwpsAllocateCloneNetBufferList((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket, + g_pNDISPoolData->nblPoolHandle, + g_pNDISPoolData->nbPoolHandle, + 0, + &pNetBufferList); + + if(bytesRetreated) + { + /// ... and advance the offset back to the original position. + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket), + bytesRetreated, + FALSE, + 0); + } + + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketInjectionAtInboundMACFrame: FwpsAllocateCloneNetBufferList() [status: %#x]\n", + status); + + HLPR_BAIL; + } + +#if DBG + + BasicPacketInjectionCountersIncrement(injectionHandle, + &g_bpiOutstandingNBLClones); + +#endif /// DBG + + pCompletionData->refCount = KrnlHlprNBLGetRequiredRefCount(pNetBufferList, + TRUE); + + status = FwpsInjectMacReceiveAsync(pCompletionData->pInjectionData->injectionHandle, + pCompletionData->pInjectionData->injectionContext, + 0, + pClassifyValues->layerId, + interfaceIndex, + ndisPort, + pNetBufferList, + CompleteBasicPacketInjection, + pCompletionData); + + NT_ASSERT(irql == KeGetCurrentIrql()); + + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketInjectionAtInboundMACFrame: FwpsInjectMacReceiveAsync() [status: %#x]\n", + status); + +#if DBG + + BasicPacketInjectionCountersIncrement(injectionHandle, + &g_bpiTotalFailedInjectionCalls); + +#endif /// DBG + + } + +#if DBG + + else + BasicPacketInjectionCountersIncrement(injectionHandle, + &g_bpiTotalSuccessfulInjectionCalls); + +#endif /// DBG + + HLPR_BAIL_LABEL: + + NT_ASSERT(status == STATUS_SUCCESS); + + if(status != STATUS_SUCCESS) + { + if(pNetBufferList) + { + FwpsFreeCloneNetBufferList(pNetBufferList, + 0); + + pNetBufferList = 0; + +#if DBG + + BasicPacketInjectionCountersDecrement(injectionHandle, + &g_bpiOutstandingNBLClones); + +#endif + + } + + if(pCompletionData) + BasicPacketInjectionCompletionDataDestroy(&pCompletionData, + TRUE); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PerformBasicPacketInjectionAtInboundMACFrame() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @private_function="PerformBasicPacketInjectionAtOutboundMACFrame" + + Purpose: Clones the NET_BUFFER_LIST and injects the clone back to the stack from the + outgoing MAC Layers using FwpsInjectMacSendAsync().
+
+ Notes: Applies to the following outbound layers:
+ FWPM_LAYER_OUTBOUND_MAC_FRAME_ETHERNET
+ FWPM_LAYER_OUTBOUND_MAC_FRAME_NATIVE
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/HH439593.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF546324.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +NTSTATUS PerformBasicPacketInjectionAtOutboundMACFrame(_In_ CLASSIFY_DATA** ppClassifyData, + _In_ INJECTION_DATA** ppInjectionData, + _In_ BOOLEAN isInline = FALSE) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PerformBasicPacketInjectionAtOutboundMACFrame()\n"); + +#endif /// DBG + + NT_ASSERT(ppClassifyData); + NT_ASSERT(ppInjectionData); + NT_ASSERT(*ppClassifyData); + NT_ASSERT(*ppInjectionData); + + NTSTATUS status = STATUS_SUCCESS; + FWPS_INCOMING_VALUES* pClassifyValues = (FWPS_INCOMING_VALUES*)(*ppClassifyData)->pClassifyValues; + IF_INDEX interfaceIndex = 0; + NDIS_PORT_NUMBER ndisPort = 0; + NET_BUFFER_LIST* pNetBufferList = 0; + BASIC_PACKET_INJECTION_COMPLETION_DATA* pCompletionData = 0; + FWP_VALUE* pInterfaceIndex = 0; + FWP_VALUE* pNDISPort = 0; + +#if DBG + + KIRQL irql = KeGetCurrentIrql(); + HANDLE injectionHandle = (*ppInjectionData)->injectionHandle; + +#endif /// DBG + +#pragma warning(push) +#pragma warning(disable: 6014) /// pCompletionData will be freed in completionFn using BasicPacketInjectionCompletionDataDestroy + + HLPR_NEW(pCompletionData, + BASIC_PACKET_INJECTION_COMPLETION_DATA, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pCompletionData, + status); + +#pragma warning(pop) + + KeInitializeSpinLock(&(pCompletionData->spinLock)); + + pCompletionData->performedInline = isInline; + pCompletionData->pClassifyData = *ppClassifyData; + pCompletionData->pInjectionData = *ppInjectionData; + + /// Responsibility for freeing this memory has been transferred to the pCompletionData + *ppClassifyData = 0; + + *ppInjectionData = 0; + + pInterfaceIndex = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_INTERFACE_INDEX); + if(pInterfaceIndex && + pInterfaceIndex->type == FWP_UINT32) + interfaceIndex = (IF_INDEX)pInterfaceIndex->uint32; + + pNDISPort = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_NDIS_PORT); + if(pNDISPort && + pNDISPort->type == FWP_UINT32) + ndisPort = (NDIS_PORT_NUMBER)pNDISPort->uint32; + + /// Initial offset is at the MAC Header, so just clone the entire NET_BUFFER_LIST. + status = FwpsAllocateCloneNetBufferList((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket, + g_pNDISPoolData->nblPoolHandle, + g_pNDISPoolData->nbPoolHandle, + 0, + &pNetBufferList); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketInjectionAtOutboundMACFrame: FwpsAllocateCloneNetBufferList() [status: %#x]\n", + status); + + HLPR_BAIL; + } + +#if DBG + + BasicPacketInjectionCountersIncrement(injectionHandle, + &g_bpiOutstandingNBLClones); + +#endif /// DBG + + pCompletionData->refCount = KrnlHlprNBLGetRequiredRefCount(pNetBufferList, + TRUE); + + status = FwpsInjectMacSendAsync(pCompletionData->pInjectionData->injectionHandle, + pCompletionData->pInjectionData->injectionContext, + 0, + pClassifyValues->layerId, + interfaceIndex, + ndisPort, + pNetBufferList, + CompleteBasicPacketInjection, + pCompletionData); + + NT_ASSERT(irql == KeGetCurrentIrql()); + + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketInjectionAtOutboundMACFrame: FwpsInjectMacSendAsync() [status: %#x]\n", + status); + +#if DBG + + BasicPacketInjectionCountersIncrement(injectionHandle, + &g_bpiTotalFailedInjectionCalls); + +#endif /// DBG + + } + +#if DBG + + else + BasicPacketInjectionCountersIncrement(injectionHandle, + &g_bpiTotalSuccessfulInjectionCalls); + +#endif /// DBG + + + HLPR_BAIL_LABEL: + + NT_ASSERT(status == STATUS_SUCCESS); + + if(status != STATUS_SUCCESS) + { + if(pNetBufferList) + { + FwpsFreeCloneNetBufferList(pNetBufferList, + 0); + + pNetBufferList = 0; + +#if DBG + + BasicPacketInjectionCountersDecrement(injectionHandle, + &g_bpiOutstandingNBLClones); + +#endif + + } + + if(pCompletionData) + BasicPacketInjectionCompletionDataDestroy(&pCompletionData, + TRUE); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PerformBasicPacketInjectionAtOutboundMACFrame() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @private_function="PerformBasicPacketInjectionAtIngressVSwitchEthernet" + + Purpose: Clones the NET_BUFFER_LIST and injects the clone back to the virtual switch's + ingress path from the ingress VSwitch Layers using + FwpsInjectvSwitchEthernetIngressAsync0().
+
+ Notes: Applies to the following ingress layers:
+ FWPM_LAYER_INGRESS_VSWITCH_ETHERNET
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/HH439669.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF546324.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +NTSTATUS PerformBasicPacketInjectionAtIngressVSwitchEthernet(_In_ CLASSIFY_DATA** ppClassifyData, + _In_ INJECTION_DATA** ppInjectionData, + _In_ BOOLEAN isInline = FALSE) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PerformBasicPacketInjectionAtIngressVSwitchEthernet()\n"); + +#endif /// DBG + + NT_ASSERT(ppClassifyData); + NT_ASSERT(ppInjectionData); + NT_ASSERT(*ppClassifyData); + NT_ASSERT(*ppInjectionData); + + NTSTATUS status = STATUS_SUCCESS; + FWPS_INCOMING_VALUES* pClassifyValues = (FWPS_INCOMING_VALUES*)(*ppClassifyData)->pClassifyValues; + FWPS_INCOMING_METADATA_VALUES* pMetadata = (FWPS_INCOMING_METADATA_VALUES*)(*ppClassifyData)->pMetadataValues; + FWP_VALUE* pVSwitchIDValue = 0; + FWP_BYTE_BLOB* pVSwitchID = 0; + NDIS_SWITCH_PORT_ID sourcePortID = 0; + NDIS_SWITCH_NIC_INDEX sourceNICIndex = 0; + NET_BUFFER_LIST* pNetBufferList = 0; + BASIC_PACKET_INJECTION_COMPLETION_DATA* pCompletionData = 0; + +#if DBG + + KIRQL irql = KeGetCurrentIrql(); + HANDLE injectionHandle = (*ppInjectionData)->injectionHandle; + +#endif /// DBG + +#pragma warning(push) +#pragma warning(disable: 6014) /// pCompletionData will be freed in completionFn using BasicPacketInjectionCompletionDataDestroy + + HLPR_NEW(pCompletionData, + BASIC_PACKET_INJECTION_COMPLETION_DATA, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pCompletionData, + status); + +#pragma warning(pop) + + KeInitializeSpinLock(&(pCompletionData->spinLock)); + + pCompletionData->performedInline = isInline; + pCompletionData->pClassifyData = *ppClassifyData; + pCompletionData->pInjectionData = *ppInjectionData; + + /// Responsibility for freeing this memory has been transferred to the pCompletionData + *ppClassifyData = 0; + + *ppInjectionData = 0; + + if(FWPS_IS_L2_METADATA_FIELD_PRESENT(pMetadata, + FWPS_L2_METADATA_FIELD_VSWITCH_SOURCE_PORT_ID)) + sourcePortID = pMetadata->vSwitchSourcePortId; + + if(FWPS_IS_L2_METADATA_FIELD_PRESENT(pMetadata, + FWPS_L2_METADATA_FIELD_VSWITCH_SOURCE_NIC_INDEX)) + sourceNICIndex = (NDIS_SWITCH_NIC_INDEX)pMetadata->vSwitchSourceNicIndex; + + pVSwitchIDValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_VSWITCH_ID); + if(pVSwitchIDValue) + pVSwitchID = pVSwitchIDValue->byteBlob; + + if(pVSwitchID == 0) + { + status = STATUS_INVALID_MEMBER; + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketInjectionAtIngressVSwitchEthernet() [status: %#x][pVSwitchID: %#p]\n", + status, + pVSwitchID); + + HLPR_BAIL; + } + + /// Initial offset is at the MAC Header, so just clone the entire NET_BUFFER_LIST. + status = FwpsAllocateCloneNetBufferList((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket, + g_pNDISPoolData->nblPoolHandle, + g_pNDISPoolData->nbPoolHandle, + 0, + &pNetBufferList); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketInjectionAtIngressVSwitchEthernet: FwpsAllocateCloneNetBufferList() [status: %#x]\n", + status); + + HLPR_BAIL; + } + +#if DBG + + BasicPacketInjectionCountersIncrement(injectionHandle, + &g_bpiOutstandingNBLClones); + +#endif /// DBG + + pCompletionData->refCount = KrnlHlprNBLGetRequiredRefCount(pNetBufferList, + TRUE); + + status = FwpsInjectvSwitchEthernetIngressAsync(pCompletionData->pInjectionData->injectionHandle, + pCompletionData->pInjectionData->injectionContext, + 0, + 0, + pVSwitchID, + sourcePortID, + sourceNICIndex, + pNetBufferList, + CompleteBasicPacketInjection, + pCompletionData); + + NT_ASSERT(irql == KeGetCurrentIrql()); + + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketInjectionAtIngressVSwitchEthernet: FwpsInjectvSwitchEthernetIngressAsync() [status: %#x]\n", + status); + +#if DBG + + BasicPacketInjectionCountersIncrement(injectionHandle, + &g_bpiTotalFailedInjectionCalls); + +#endif /// DBG + + } + +#if DBG + + else + BasicPacketInjectionCountersIncrement(injectionHandle, + &g_bpiTotalSuccessfulInjectionCalls); + +#endif /// DBG + + HLPR_BAIL_LABEL: + + NT_ASSERT(status == STATUS_SUCCESS); + + if(status != STATUS_SUCCESS) + { + if(pNetBufferList) + { + FwpsFreeCloneNetBufferList(pNetBufferList, + 0); + + pNetBufferList = 0; + +#if DBG + + BasicPacketInjectionCountersDecrement(injectionHandle, + &g_bpiOutstandingNBLClones); + +#endif + + } + + if(pCompletionData) + BasicPacketInjectionCompletionDataDestroy(&pCompletionData, + TRUE); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PerformBasicPacketInjectionAtIngressVSwitchEthernet() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @private_function="PerformBasicPacketInjectionAtEgressVSwitchEthernet" + + Purpose: Clones the NET_BUFFER_LIST and injects the clone back to the virtual switch's + ingress path from the egress VSwitch Layers using + FwpsInjectvSwitchEthernetIngressAsync0().
+
+ Notes: Applies to the following egress layers:
+ FWPM_LAYER_EGRESS_VSWITCH_ETHERNET
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/HH439662.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF546324.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +NTSTATUS PerformBasicPacketInjectionAtEgressVSwitchEthernet(_In_ CLASSIFY_DATA** ppClassifyData, + _In_ INJECTION_DATA** ppInjectionData, + _In_ BOOLEAN isInline = FALSE) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PerformBasicPacketInjectionAtEgressVSwitchEthernet()\n"); + +#endif /// DBG + + NT_ASSERT(ppClassifyData); + NT_ASSERT(ppInjectionData); + NT_ASSERT(*ppClassifyData); + NT_ASSERT(*ppInjectionData); + + NTSTATUS status = STATUS_SUCCESS; + FWPS_INCOMING_VALUES* pClassifyValues = (FWPS_INCOMING_VALUES*)(*ppClassifyData)->pClassifyValues; + FWPS_INCOMING_METADATA_VALUES* pMetadata = (FWPS_INCOMING_METADATA_VALUES*)(*ppClassifyData)->pMetadataValues; + FWP_VALUE* pVSwitchIDValue = 0; + FWP_BYTE_BLOB* pVSwitchID = 0; + NDIS_SWITCH_PORT_ID sourcePortID = 0; + NDIS_SWITCH_NIC_INDEX sourceNICIndex = 0; + NET_BUFFER_LIST* pNetBufferList = 0; + BASIC_PACKET_INJECTION_COMPLETION_DATA* pCompletionData = 0; + +#if DBG + + KIRQL irql = KeGetCurrentIrql(); + HANDLE injectionHandle = (*ppInjectionData)->injectionHandle; + +#endif /// DBG + +#pragma warning(push) +#pragma warning(disable: 6014) /// pCompletionData will be freed in completionFn using BasicPacketInjectionCompletionDataDestroy + + HLPR_NEW(pCompletionData, + BASIC_PACKET_INJECTION_COMPLETION_DATA, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pCompletionData, + status); + +#pragma warning(pop) + + KeInitializeSpinLock(&(pCompletionData->spinLock)); + + pCompletionData->performedInline = isInline; + pCompletionData->pClassifyData = *ppClassifyData; + pCompletionData->pInjectionData = *ppInjectionData; + + /// Responsibility for freeing this memory has been transferred to the pCompletionData + *ppClassifyData = 0; + + *ppInjectionData = 0; + + if(FWPS_IS_L2_METADATA_FIELD_PRESENT(pMetadata, + FWPS_L2_METADATA_FIELD_VSWITCH_SOURCE_PORT_ID)) + sourcePortID = pMetadata->vSwitchSourcePortId; + + if(FWPS_IS_L2_METADATA_FIELD_PRESENT(pMetadata, + FWPS_L2_METADATA_FIELD_VSWITCH_SOURCE_NIC_INDEX)) + sourceNICIndex = (NDIS_SWITCH_NIC_INDEX)pMetadata->vSwitchSourceNicIndex; + + pVSwitchIDValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_VSWITCH_ID); + if(pVSwitchIDValue) + pVSwitchID = pVSwitchIDValue->byteBlob; + + if(pVSwitchID == 0) + { + status = STATUS_INVALID_MEMBER; + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketInjectionAtEgressVSwitchEthernet() [status: %#x][pVSwitchID: %#p]\n", + status, + pVSwitchID); + + HLPR_BAIL; + } + + /// Initial offset is at the MAC Header, so just clone the entire NET_BUFFER_LIST. + status = FwpsAllocateCloneNetBufferList((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket, + g_pNDISPoolData->nblPoolHandle, + g_pNDISPoolData->nbPoolHandle, + 0, + &pNetBufferList); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketInjectionAtEgressVSwitchEthernet: FwpsAllocateCloneNetBufferList() [status: %#x]\n", + status); + + HLPR_BAIL; + } + +#if DBG + + BasicPacketInjectionCountersIncrement(injectionHandle, + &g_bpiOutstandingNBLClones); + +#endif /// DBG + + pCompletionData->refCount = KrnlHlprNBLGetRequiredRefCount(pNetBufferList, + TRUE); + + status = FwpsInjectvSwitchEthernetIngressAsync(pCompletionData->pInjectionData->injectionHandle, + pCompletionData->pInjectionData->injectionContext, + 0, + 0, + pVSwitchID, + sourcePortID, + sourceNICIndex, + pNetBufferList, + CompleteBasicPacketInjection, + pCompletionData); + + NT_ASSERT(irql == KeGetCurrentIrql()); + + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketInjectionAtEgressVSwitchEthernet: FwpsInjectvSwitchEthernetEgressAsync() [status: %#x]\n", + status); + +#if DBG + + BasicPacketInjectionCountersIncrement(injectionHandle, + &g_bpiTotalFailedInjectionCalls); + +#endif /// DBG + + } + +#if DBG + + else + BasicPacketInjectionCountersIncrement(injectionHandle, + &g_bpiTotalSuccessfulInjectionCalls); + +#endif /// DBG + + HLPR_BAIL_LABEL: + + NT_ASSERT(status == STATUS_SUCCESS); + + if(status != STATUS_SUCCESS) + { + if(pNetBufferList) + { + FwpsFreeCloneNetBufferList(pNetBufferList, + 0); + + pNetBufferList = 0; + +#if DBG + + BasicPacketInjectionCountersDecrement(injectionHandle, + &g_bpiOutstandingNBLClones); + +#endif + + } + + if(pCompletionData) + BasicPacketInjectionCompletionDataDestroy(&pCompletionData, + TRUE); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PerformBasicPacketInjectionAtEgressVSwitchEthernet() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +#endif // (NTDDI_VERSION >= NTDDI_WIN8) + +/** + @private_function="PerformBasicPacketInjectionAtInboundNetwork" + + Purpose: Clones the NET_BUFFER_LIST and injects the clone back to the stack's inbound path + from the incoming Network Layers using FwpsInjectNetworkReceiveAsync().
+
+ Notes: Applies to the following inbound layers:
+ FWPM_LAYER_INBOUND_IPPACKET_V{4/6}
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551183.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF546324.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +NTSTATUS PerformBasicPacketInjectionAtInboundNetwork(_In_ CLASSIFY_DATA** ppClassifyData, + _In_ INJECTION_DATA** ppInjectionData, + _In_ BOOLEAN isInline = FALSE) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PerformBasicPacketInjectionAtInboundNetwork()\n"); + +#endif /// DBG + + NT_ASSERT(ppClassifyData); + NT_ASSERT(ppInjectionData); + NT_ASSERT(*ppClassifyData); + NT_ASSERT(*ppInjectionData); + + NTSTATUS status = STATUS_SUCCESS; + FWPS_INCOMING_VALUES* pClassifyValues = (FWPS_INCOMING_VALUES*)(*ppClassifyData)->pClassifyValues; + FWPS_INCOMING_METADATA_VALUES* pMetadata = (FWPS_INCOMING_METADATA_VALUES*)(*ppClassifyData)->pMetadataValues; + COMPARTMENT_ID compartmentID = DEFAULT_COMPARTMENT_ID; + IF_INDEX interfaceIndex = 0; + IF_INDEX subInterfaceIndex = 0; + UINT32 flags = 0; + NET_BUFFER_LIST* pNetBufferList = 0; + BASIC_PACKET_INJECTION_COMPLETION_DATA* pCompletionData = 0; + UINT32 ipHeaderSize = 0; + UINT32 bytesRetreated = 0; + FWP_VALUE* pInterfaceIndex = 0; + FWP_VALUE* pSubInterfaceIndex = 0; + FWP_VALUE* pFlags = 0; + NDIS_TCP_IP_CHECKSUM_PACKET_INFO checksumInfo = {0}; + +#if DBG + + KIRQL irql = KeGetCurrentIrql(); + HANDLE injectionHandle = (*ppInjectionData)->injectionHandle; + +#endif /// DBG + +#pragma warning(push) +#pragma warning(disable: 6014) /// pCompletionData will be freed in completionFn using BasicPacketInjectionCompletionDataDestroy + + HLPR_NEW(pCompletionData, + BASIC_PACKET_INJECTION_COMPLETION_DATA, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pCompletionData, + status); + +#pragma warning(pop) + + KeInitializeSpinLock(&(pCompletionData->spinLock)); + + pCompletionData->performedInline = isInline; + pCompletionData->pClassifyData = *ppClassifyData; + pCompletionData->pInjectionData = *ppInjectionData; + + /// Responsibility for freeing this memory has been transferred to the pCompletionData + *ppClassifyData = 0; + + *ppInjectionData = 0; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_COMPARTMENT_ID)) + compartmentID = (COMPARTMENT_ID)pMetadata->compartmentId; + + pInterfaceIndex = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_INTERFACE_INDEX); + if(pInterfaceIndex && + pInterfaceIndex->type == FWP_UINT32) + interfaceIndex = (IF_INDEX)pInterfaceIndex->uint32; + + pSubInterfaceIndex = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_SUB_INTERFACE_INDEX); + if(pSubInterfaceIndex && + pSubInterfaceIndex->type == FWP_UINT32) + subInterfaceIndex = (IF_INDEX)pSubInterfaceIndex->uint32; + + pFlags = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_FLAGS); + if(pFlags && + pFlags->type == FWP_UINT32) + flags = pFlags->uint32; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_IP_HEADER_SIZE)) + bytesRetreated = ipHeaderSize = pMetadata->ipHeaderSize; + + checksumInfo.Value = (ULONG)(ULONG_PTR)NET_BUFFER_LIST_INFO((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket, + TcpIpChecksumNetBufferListInfo); + + /// Initial offset is at the Transport Header, so retreat the size of the IP Header ... + status = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket), + bytesRetreated, + 0, + 0); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketInjectionAtInboundNetwork: NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + /// ... clone the entire NET_BUFFER_LIST ... + status = FwpsAllocateCloneNetBufferList((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket, + g_pNDISPoolData->nblPoolHandle, + g_pNDISPoolData->nbPoolHandle, + 0, + &pNetBufferList); + + /// ... and advance the offset back to the original position. + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket), + bytesRetreated, + FALSE, + 0); + + if(status != STATUS_SUCCESS || + !pNetBufferList) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketInjectionAtInboundNetwork: FwpsAllocateCloneNetBufferList() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + /// Handle if this packet had the IP checksum offloaded or if it's loopback + if(checksumInfo.Receive.NdisPacketIpChecksumSucceeded || + flags & FWP_CONDITION_FLAG_IS_LOOPBACK) + { + /// Prevent TCP/IP Zone crossing and recalculate the checksums + if(flags & FWP_CONDITION_FLAG_IS_LOOPBACK) + { + FWP_VALUE* pLocalAddress = 0; + FWP_VALUE* pRemoteAddress = 0; + FWP_VALUE* pLoopbackAddress = 0; + + pLocalAddress = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_REMOTE_ADDRESS); + if(pLocalAddress && + ((pLocalAddress->type == FWP_UINT32 && + RtlCompareMemory(&(pLocalAddress->uint32), + IPV4_LOOPBACK_ADDRESS, + IPV4_ADDRESS_SIZE)) || + (pLocalAddress->type == FWP_BYTE_ARRAY16_TYPE && + RtlCompareMemory(pLocalAddress->byteArray16->byteArray16, + IPV6_LOOPBACK_ADDRESS, + IPV6_ADDRESS_SIZE)))) + pLoopbackAddress = pLocalAddress; + + if(!pLoopbackAddress) + { + pRemoteAddress = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_REMOTE_ADDRESS); + if(pRemoteAddress && + ((pRemoteAddress->type == FWP_UINT32 && + RtlCompareMemory(&(pRemoteAddress->uint32), + IPV4_LOOPBACK_ADDRESS, + IPV4_ADDRESS_SIZE)) || + (pRemoteAddress->type == FWP_BYTE_ARRAY16_TYPE && + RtlCompareMemory(pRemoteAddress->byteArray16->byteArray16, + IPV6_LOOPBACK_ADDRESS, + IPV6_ADDRESS_SIZE)))) + pLoopbackAddress = pRemoteAddress; + } + + if(pLoopbackAddress) + { + status = KrnlHlprIPHeaderModifyLoopbackToLocal(pMetadata, + pLoopbackAddress, + ipHeaderSize, + pNetBufferList, + (const WSACMSGHDR*)pCompletionData->pInjectionData->pControlData, + pCompletionData->pInjectionData->controlDataLength); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketInjectionAtInboundNetwork: KrnlHlprIPHeaderModifyLoopbackToLocal() [status: %#x]\n", + status); + + HLPR_BAIL; + } + } + } + else + { + /// Recalculate the checksum + if(pCompletionData->pInjectionData->addressFamily == AF_INET) + KrnlHlprIPHeaderCalculateV4Checksum(pNetBufferList, + ipHeaderSize); + } + } + +#if DBG + + BasicPacketInjectionCountersIncrement(injectionHandle, + &g_bpiOutstandingNBLClones); + +#endif /// DBG + + pCompletionData->refCount = KrnlHlprNBLGetRequiredRefCount(pNetBufferList); + + /// Handle if this packet is destined for the software loopback + if(flags & FWP_CONDITION_FLAG_IS_LOOPBACK) + { + FWP_VALUE* pLocalAddress = 0; + FWP_VALUE* pRemoteAddress = 0; + FWP_VALUE* pLoopbackAddress = 0; + + pLocalAddress = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_REMOTE_ADDRESS); + if(pLocalAddress && + ((pLocalAddress->type == FWP_UINT32 && + RtlCompareMemory(&(pLocalAddress->uint32), + IPV4_LOOPBACK_ADDRESS, + IPV4_ADDRESS_SIZE)) || + (pLocalAddress->type == FWP_BYTE_ARRAY16_TYPE && + RtlCompareMemory(pLocalAddress->byteArray16->byteArray16, + IPV6_LOOPBACK_ADDRESS, + IPV6_ADDRESS_SIZE)))) + pLoopbackAddress = pLocalAddress; + + if(!pLoopbackAddress) + { + pRemoteAddress = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_REMOTE_ADDRESS); + if(pRemoteAddress && + ((pRemoteAddress->type == FWP_UINT32 && + RtlCompareMemory(&(pRemoteAddress->uint32), + IPV4_LOOPBACK_ADDRESS, + IPV4_ADDRESS_SIZE)) || + (pRemoteAddress->type == FWP_BYTE_ARRAY16_TYPE && + RtlCompareMemory(pRemoteAddress->byteArray16->byteArray16, + IPV6_LOOPBACK_ADDRESS, + IPV6_ADDRESS_SIZE)))) + pLoopbackAddress = pRemoteAddress; + } + + if(pLoopbackAddress) + { + status = KrnlHlprIPHeaderModifyLoopbackToLocal(pMetadata, + pLoopbackAddress, + ipHeaderSize, + pNetBufferList, + (const WSACMSGHDR*)pCompletionData->pInjectionData->pControlData, + pCompletionData->pInjectionData->controlDataLength); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketInjectionAtInboundNetwork: KrnlHlprIPHeaderModifyLoopbackToLocal() [status: %#x]\n", + status); + + HLPR_BAIL; + } + } + } + + status = FwpsInjectNetworkReceiveAsync(pCompletionData->pInjectionData->injectionHandle, + pCompletionData->pInjectionData->injectionContext, + 0, + compartmentID, + interfaceIndex, + subInterfaceIndex, + pNetBufferList, + CompleteBasicPacketInjection, + pCompletionData); + + NT_ASSERT(irql == KeGetCurrentIrql()); + + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketInjectionAtInboundNetwork: FwpsInjectNetworkReceiveAsync() [status: %#x]\n", + status); + +#if DBG + + BasicPacketInjectionCountersIncrement(injectionHandle, + &g_bpiTotalFailedInjectionCalls); + +#endif /// DBG + + } + +#if DBG + + else + BasicPacketInjectionCountersIncrement(injectionHandle, + &g_bpiTotalSuccessfulInjectionCalls); + +#endif /// DBG + + HLPR_BAIL_LABEL: + + NT_ASSERT(status == STATUS_SUCCESS); + + if(status != STATUS_SUCCESS) + { + if(pNetBufferList) + { + FwpsFreeCloneNetBufferList(pNetBufferList, + 0); + + pNetBufferList = 0; + +#if DBG + + BasicPacketInjectionCountersDecrement(injectionHandle, + &g_bpiOutstandingNBLClones); + +#endif + + } + + if(pCompletionData) + BasicPacketInjectionCompletionDataDestroy(&pCompletionData, + TRUE); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PerformBasicPacketInjectionAtInboundNetwork() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @private_function="PerformBasicPacketInjectionAtOutboundNetwork" + + Purpose: Clones the NET_BUFFER_LIST and injects the clone back to the stack's outbound path + from the outgoing Network Layers using FwpsInjectNetworkSendAsync().
+
+ Notes: Applies to the following outbound layers:
+ FWPM_LAYER_OUTBOUND_IPPACKET_V{4/6}
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551185.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF546324.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +NTSTATUS PerformBasicPacketInjectionAtOutboundNetwork(_In_ CLASSIFY_DATA** ppClassifyData, + _In_ INJECTION_DATA** ppInjectionData, + _In_ BOOLEAN isInline = FALSE) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PerformBasicPacketInjectionAtOutboundNetwork()\n"); + +#endif /// DBG + + NT_ASSERT(ppClassifyData); + NT_ASSERT(ppInjectionData); + NT_ASSERT(*ppClassifyData); + NT_ASSERT(*ppInjectionData); + + NTSTATUS status = STATUS_SUCCESS; + FWPS_INCOMING_METADATA_VALUES* pMetadata = (FWPS_INCOMING_METADATA_VALUES*)(*ppClassifyData)->pMetadataValues; + COMPARTMENT_ID compartmentID = DEFAULT_COMPARTMENT_ID; + NET_BUFFER_LIST* pNetBufferList = 0; + BASIC_PACKET_INJECTION_COMPLETION_DATA* pCompletionData = 0; + +#if DBG + + KIRQL irql = KeGetCurrentIrql(); + HANDLE injectionHandle = (*ppInjectionData)->injectionHandle; + +#endif /// DBG + +#pragma warning(push) +#pragma warning(disable: 6014) /// pCompletionData will be freed in completionFn using BasicPacketInjectionCompletionDataDestroy + + HLPR_NEW(pCompletionData, + BASIC_PACKET_INJECTION_COMPLETION_DATA, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pCompletionData, + status); + +#pragma warning(pop) + + KeInitializeSpinLock(&(pCompletionData->spinLock)); + + pCompletionData->performedInline = isInline; + pCompletionData->pClassifyData = *ppClassifyData; + pCompletionData->pInjectionData = *ppInjectionData; + + /// Responsibility for freeing this memory has been transferred to the pCompletionData + *ppClassifyData = 0; + + *ppInjectionData = 0; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_COMPARTMENT_ID)) + compartmentID = (COMPARTMENT_ID)pMetadata->compartmentId; + + /// Initial offset is at the IP Header, so just clone the entire NET_BUFFER_LIST. + status = FwpsAllocateCloneNetBufferList((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket, + g_pNDISPoolData->nblPoolHandle, + g_pNDISPoolData->nbPoolHandle, + 0, + &pNetBufferList); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketInjectionAtOutboundNetwork: FwpsAllocateCloneNetBufferList() [status: %#x]\n", + status); + + HLPR_BAIL; + } + +#if DBG + + BasicPacketInjectionCountersIncrement(injectionHandle, + &g_bpiOutstandingNBLClones); + +#endif /// DBG + + pCompletionData->refCount = KrnlHlprNBLGetRequiredRefCount(pNetBufferList); + + status = FwpsInjectNetworkSendAsync(pCompletionData->pInjectionData->injectionHandle, + pCompletionData->pInjectionData->injectionContext, + 0, + compartmentID, + pNetBufferList, + CompleteBasicPacketInjection, + pCompletionData); + + NT_ASSERT(irql == KeGetCurrentIrql()); + + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketInjectionAtOutboundNetwork: FwpsInjectNetworkSendAsync() [status: %#x]\n", + status); + +#if DBG + + BasicPacketInjectionCountersIncrement(injectionHandle, + &g_bpiTotalFailedInjectionCalls); + +#endif /// DBG + + } + +#if DBG + + else + BasicPacketInjectionCountersIncrement(injectionHandle, + &g_bpiTotalSuccessfulInjectionCalls); + +#endif /// DBG + + HLPR_BAIL_LABEL: + + NT_ASSERT(status == STATUS_SUCCESS); + + if(status != STATUS_SUCCESS) + { + if(pNetBufferList) + { + FwpsFreeCloneNetBufferList(pNetBufferList, + 0); + + pNetBufferList = 0; + +#if DBG + + BasicPacketInjectionCountersDecrement(injectionHandle, + &g_bpiOutstandingNBLClones); + +#endif + + } + + if(pCompletionData) + BasicPacketInjectionCompletionDataDestroy(&pCompletionData, + TRUE); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PerformBasicPacketInjectionAtOutboundNetwork() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @private_function="PerformBasicPacketInjectionAtForward" + + Purpose: Clones the NET_BUFFER_LIST and injects the clone back to the stack's forward path + using FwpsInjectForwardAsync().
+
+ Notes: Applies to the following forwarding layers:
+ FWPM_LAYER_IPFORWARD_V{4/6}
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551186.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF546324.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +NTSTATUS PerformBasicPacketInjectionAtForward(_In_ CLASSIFY_DATA** ppClassifyData, + _In_ INJECTION_DATA** ppInjectionData, + _In_ BOOLEAN isInline = FALSE) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PerformBasicPacketInjectionAtForward()\n"); + +#endif /// DBG + + NT_ASSERT(ppClassifyData); + NT_ASSERT(ppInjectionData); + NT_ASSERT(*ppClassifyData); + NT_ASSERT(*ppInjectionData); + + NTSTATUS status = STATUS_SUCCESS; + FWPS_INCOMING_VALUES* pClassifyValues = (FWPS_INCOMING_VALUES*)(*ppClassifyData)->pClassifyValues; + FWPS_INCOMING_METADATA_VALUES* pMetadata = (FWPS_INCOMING_METADATA_VALUES*)(*ppClassifyData)->pMetadataValues; + COMPARTMENT_ID compartmentID = DEFAULT_COMPARTMENT_ID; + IF_INDEX interfaceIndex = 0; + UINT32 flags = 0; + NET_BUFFER_LIST* pNetBufferList = 0; + BASIC_PACKET_INJECTION_COMPLETION_DATA* pCompletionData = 0; + UINT32 ipHeaderSize = 0; + FWP_VALUE* pInterfaceIndex = 0; + FWP_VALUE* pFlags = 0; + BOOLEAN isWeakHostReceive = FALSE; + BOOLEAN isWeakHostSend = FALSE; + PSTR pInjectionFn = "FwpsInjectForwardAsync"; + NDIS_TCP_IP_CHECKSUM_PACKET_INFO checksumInfo = {0}; + +#if DBG + + KIRQL irql = KeGetCurrentIrql(); + HANDLE injectionHandle = (*ppInjectionData)->injectionHandle; + +#endif /// DBG + +#pragma warning(push) +#pragma warning(disable: 6014) /// pCompletionData will be freed in completionFn using BasicPacketInjectionCompletionDataDestroy + + HLPR_NEW(pCompletionData, + BASIC_PACKET_INJECTION_COMPLETION_DATA, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pCompletionData, + status); + +#pragma warning(pop) + + KeInitializeSpinLock(&(pCompletionData->spinLock)); + + pCompletionData->performedInline = isInline; + pCompletionData->pClassifyData = *ppClassifyData; + pCompletionData->pInjectionData = *ppInjectionData; + + /// Responsibility for freeing this memory has been transferred to the pCompletionData + *ppClassifyData = 0; + + *ppInjectionData = 0; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_COMPARTMENT_ID)) + compartmentID = (COMPARTMENT_ID)pMetadata->compartmentId; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_IP_HEADER_SIZE)) + ipHeaderSize = pMetadata->ipHeaderSize; + + pInterfaceIndex = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_DESTINATION_INTERFACE_INDEX); + if(pInterfaceIndex && + pInterfaceIndex->type == FWP_UINT32) + interfaceIndex = (IF_INDEX)pInterfaceIndex->uint32; + + pFlags = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_FLAGS); + if(pFlags && + pFlags->type == FWP_UINT32) + flags = pFlags->uint32; + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + /// Determine if this is a weakhost forward + if(flags & FWP_CONDITION_FLAG_IS_INBOUND_PASS_THRU) + isWeakHostReceive = TRUE; + + if(flags & FWP_CONDITION_FLAG_IS_OUTBOUND_PASS_THRU) + isWeakHostSend = TRUE; + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + /// Initial offset is at the IP Header, so just clone the entire NET_BUFFER_LIST. + status = FwpsAllocateCloneNetBufferList((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket, + g_pNDISPoolData->nblPoolHandle, + g_pNDISPoolData->nbPoolHandle, + 0, + &pNetBufferList); + if(status != STATUS_SUCCESS || + !pNetBufferList) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketInjectionAtForward: FwpsAllocateCloneNetBufferList() [status: %#x]\n", + status); + + HLPR_BAIL; + } + +#if DBG + + BasicPacketInjectionCountersIncrement(injectionHandle, + &g_bpiOutstandingNBLClones); + +#endif /// DBG + + checksumInfo.Value = (ULONG)(ULONG_PTR)NET_BUFFER_LIST_INFO((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket, + TcpIpChecksumNetBufferListInfo); + + /// Handle if this packet had the IP checksum offloaded or if it's loopback + if(checksumInfo.Receive.NdisPacketIpChecksumSucceeded || + flags & FWP_CONDITION_FLAG_IS_LOOPBACK) + { + /// Prevent TCP/IP Zone crossing and recalculate the checksums + if(flags & FWP_CONDITION_FLAG_IS_LOOPBACK) + { + FWP_VALUE* pLocalAddress = 0; + FWP_VALUE* pRemoteAddress = 0; + FWP_VALUE* pLoopbackAddress = 0; + + pLocalAddress = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_REMOTE_ADDRESS); + if(pLocalAddress && + ((pLocalAddress->type == FWP_UINT32 && + RtlCompareMemory(&(pLocalAddress->uint32), + IPV4_LOOPBACK_ADDRESS, + IPV4_ADDRESS_SIZE)) || + (pLocalAddress->type == FWP_BYTE_ARRAY16_TYPE && + RtlCompareMemory(pLocalAddress->byteArray16->byteArray16, + IPV6_LOOPBACK_ADDRESS, + IPV6_ADDRESS_SIZE)))) + pLoopbackAddress = pLocalAddress; + + if(!pLoopbackAddress) + { + pRemoteAddress = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_REMOTE_ADDRESS); + if(pRemoteAddress && + ((pRemoteAddress->type == FWP_UINT32 && + RtlCompareMemory(&(pRemoteAddress->uint32), + IPV4_LOOPBACK_ADDRESS, + IPV4_ADDRESS_SIZE)) || + (pRemoteAddress->type == FWP_BYTE_ARRAY16_TYPE && + RtlCompareMemory(pRemoteAddress->byteArray16->byteArray16, + IPV6_LOOPBACK_ADDRESS, + IPV6_ADDRESS_SIZE)))) + pLoopbackAddress = pRemoteAddress; + } + + if(pLoopbackAddress) + { + status = KrnlHlprIPHeaderModifyLoopbackToLocal(pMetadata, + pLoopbackAddress, + ipHeaderSize, + pNetBufferList, + (const WSACMSGHDR*)pCompletionData->pInjectionData->pControlData, + pCompletionData->pInjectionData->controlDataLength); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketInjectionAtForward: KrnlHlprIPHeaderModifyLoopbackToLocal() [status: %#x]\n", + status); + + HLPR_BAIL; + } + } + } + else + { + /// Recalculate the checksum + if(pCompletionData->pInjectionData->addressFamily == AF_INET) + KrnlHlprIPHeaderCalculateV4Checksum(pNetBufferList, + ipHeaderSize); + } + } + + pCompletionData->refCount = KrnlHlprNBLGetRequiredRefCount(pNetBufferList); + + /// If the Forwarded NBL is destined locally, inject using FwpsInjectNetworkReceiveAsync rather + /// than the traditional FwpsInjectForwardAsync otherwise STATUS_INVALID_PARAMETER will be + /// returned in the NBL.status and the injection fails. + if(isWeakHostReceive) + { + UINT32 index = WFPSAMPLER_INDEX; + IF_INDEX subInterfaceIndex = 0; + FWP_VALUE* pSubInterfaceIndex = 0; + + if(pCompletionData->pClassifyData->pFilter->subLayerWeight == FWPM_SUBLAYER_UNIVERSAL_WEIGHT) + index = UNIVERSAL_INDEX; + +#if DBG + + if(injectionHandle == g_pIPv4InboundForwardInjectionHandles[0] || + injectionHandle == g_pIPv4InboundForwardInjectionHandles[1]) + InterlockedDecrement64((LONG64*)&(g_bpiOutstandingNBLClones.inboundForward_IPv4)); + else if(injectionHandle == g_pIPv6InboundForwardInjectionHandles[0] || + injectionHandle == g_pIPv6InboundForwardInjectionHandles[1]) + InterlockedDecrement64((LONG64*)&(g_bpiOutstandingNBLClones.inboundForward_IPv6)); + +#endif /// DBG + + if(pCompletionData->pInjectionData->addressFamily == AF_INET) + pCompletionData->pInjectionData->injectionHandle = g_pIPv4InboundNetworkInjectionHandles[index]; + else + pCompletionData->pInjectionData->injectionHandle = g_pIPv6InboundNetworkInjectionHandles[index]; + + pSubInterfaceIndex = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_DESTINATION_SUB_INTERFACE_INDEX); + if(pSubInterfaceIndex && + pSubInterfaceIndex->type == FWP_UINT32) + subInterfaceIndex = (IF_INDEX)pSubInterfaceIndex->uint32; + +#if DBG + + BasicPacketInjectionCountersIncrement(injectionHandle, + &g_bpiOutstandingNBLClones); + +#endif /// DBG + + status = FwpsInjectNetworkReceiveAsync(pCompletionData->pInjectionData->injectionHandle, + pCompletionData->pInjectionData->injectionContext, + 0, + compartmentID, + interfaceIndex, + subInterfaceIndex, + pNetBufferList, + CompleteBasicPacketInjection, + pCompletionData); + } + /// If the Forwarded NBL is sourced locally, but another interface, inject using + /// FwpsInjectNetworkSendAsync rather than the traditional FwpsInjectForwardAsync otherwise + /// STATUS_INVALID_PARAMETER will be returned in the NBL.status and the injection fails + else if(isWeakHostSend) + { + UINT32 index = WFPSAMPLER_INDEX; + + if(pCompletionData->pClassifyData->pFilter->subLayerWeight == FWPM_SUBLAYER_UNIVERSAL_WEIGHT) + index = UNIVERSAL_INDEX; + +#if DBG + + if(injectionHandle == g_pIPv4OutboundForwardInjectionHandles[0] || + injectionHandle == g_pIPv4OutboundForwardInjectionHandles[1]) + InterlockedDecrement64((LONG64*)&(g_bpiOutstandingNBLClones.outboundForward_IPv4)); + else if(injectionHandle == g_pIPv6OutboundForwardInjectionHandles[0] || + injectionHandle == g_pIPv6OutboundForwardInjectionHandles[1]) + InterlockedDecrement64((LONG64*)&(g_bpiOutstandingNBLClones.outboundForward_IPv6)); + +#endif /// DBG + + if(pCompletionData->pInjectionData->addressFamily == AF_INET) + pCompletionData->pInjectionData->injectionHandle = g_pIPv4OutboundNetworkInjectionHandles[index]; + else + pCompletionData->pInjectionData->injectionHandle = g_pIPv6OutboundNetworkInjectionHandles[index]; + +#if DBG + + BasicPacketInjectionCountersIncrement(injectionHandle, + &g_bpiOutstandingNBLClones); + +#endif /// DBG + + status = FwpsInjectNetworkSendAsync(pCompletionData->pInjectionData->injectionHandle, + pCompletionData->pInjectionData->injectionContext, + 0, + compartmentID, + pNetBufferList, + CompleteBasicPacketInjection, + pCompletionData); + } + else + status = FwpsInjectForwardAsync(pCompletionData->pInjectionData->injectionHandle, + pCompletionData->pInjectionData->injectionContext, + 0, + pCompletionData->pInjectionData->addressFamily, + compartmentID, + interfaceIndex, + pNetBufferList, + CompleteBasicPacketInjection, + pCompletionData); + + NT_ASSERT(irql == KeGetCurrentIrql()); + + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketInjectionAtForward: %s() [status: %#x]\n", + pInjectionFn, + status); + +#if DBG + + BasicPacketInjectionCountersIncrement(injectionHandle, + &g_bpiTotalFailedInjectionCalls); + +#endif /// DBG + + } + +#if DBG + + else + BasicPacketInjectionCountersIncrement(injectionHandle, + &g_bpiTotalSuccessfulInjectionCalls); + +#endif /// DBG + + HLPR_BAIL_LABEL: + + NT_ASSERT(status == STATUS_SUCCESS); + + if(status != STATUS_SUCCESS) + { + if(pNetBufferList) + { + FwpsFreeCloneNetBufferList(pNetBufferList, + 0); + + pNetBufferList = 0; + +#if DBG + + BasicPacketInjectionCountersDecrement(injectionHandle, + &g_bpiOutstandingNBLClones); + +#endif + + } + + if(pCompletionData) + BasicPacketInjectionCompletionDataDestroy(&pCompletionData, + TRUE); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PerformBasicPacketInjectionAtForward() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @private_function="PerformBasicPacketInjectionAtInboundTransport" + + Purpose: Clones the NET_BUFFER_LIST and injects the clone back to the stack's inbound path + from the incoming Transport Layers using FwpsInjectTransportRecveiveAsync().
+
+ Notes: Applies to the following inbound layers:
+ FWPM_LAYER_INBOUND_TRANSPORT_V{4/6}
+ FWPM_LAYER_INBOUND_ICMP_ERROR_V{4/6}
+ FWPM_LAYER_DATAGRAM_DATA_V{4/6} (Inbound only)
+ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V{4/6} (Inbound only)
+ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V{4/6} (Inbound only)
+ FWPM_LAYER_ALE_AUTH_CONNECT_V{4/6} (Inbound, reauthorization only)
+ FWPM_LAYER_ALE_FLOW_ESTABLISHED_V{4/6} (Inbound, non-TCP only)
+ FWPM_LAYER_STREAM_PACKET_V{4/6} (Inbound only)
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551186.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF546324.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +NTSTATUS PerformBasicPacketInjectionAtInboundTransport(_In_ CLASSIFY_DATA** ppClassifyData, + _In_ INJECTION_DATA** ppInjectionData, + _In_ BOOLEAN isInline = FALSE) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PerformBasicPacketInjectionAtInboundTransport()\n"); + +#endif /// DBG + + NT_ASSERT(ppClassifyData); + NT_ASSERT(ppInjectionData); + NT_ASSERT(*ppClassifyData); + NT_ASSERT(*ppInjectionData); + + NTSTATUS status = STATUS_SUCCESS; + FWPS_INCOMING_VALUES* pClassifyValues = (FWPS_INCOMING_VALUES*)(*ppClassifyData)->pClassifyValues; + FWPS_INCOMING_METADATA_VALUES* pMetadata = (FWPS_INCOMING_METADATA_VALUES*)(*ppClassifyData)->pMetadataValues; + COMPARTMENT_ID compartmentID = DEFAULT_COMPARTMENT_ID; + IF_INDEX interfaceIndex = 0; + IF_INDEX subInterfaceIndex = 0; + UINT32 flags = 0; + NET_BUFFER_LIST* pNetBufferList = 0; + BASIC_PACKET_INJECTION_COMPLETION_DATA* pCompletionData = 0; + UINT32 ipHeaderSize = 0; + UINT32 transportHeaderSize = 0; + UINT32 bytesRetreated = 0; + IPPROTO protocol = IPPROTO_MAX; + FWP_VALUE* pProtocol = 0; + FWP_VALUE* pInterfaceIndex = 0; + FWP_VALUE* pSubInterfaceIndex = 0; + FWP_VALUE* pFlags = 0; + FWPS_PACKET_LIST_INFORMATION* pPacketInformation = 0; + BOOLEAN bypassInjection = FALSE; + BYTE* pSourceAddress = 0; + BYTE* pDestinationAddress = 0; + NDIS_TCP_IP_CHECKSUM_PACKET_INFO checksumInfo = {0}; + +#if DBG + + KIRQL irql = KeGetCurrentIrql(); + HANDLE injectionHandle = (*ppInjectionData)->injectionHandle; + +#endif /// DBG + +#pragma warning(push) +#pragma warning(disable: 6014) /// pCompletionData will be freed in completionFn using BasicPacketInjectionCompletionDataDestroy + + HLPR_NEW(pCompletionData, + BASIC_PACKET_INJECTION_COMPLETION_DATA, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pCompletionData, + status); + +#pragma warning(pop) + + KeInitializeSpinLock(&(pCompletionData->spinLock)); + + pCompletionData->performedInline = isInline; + pCompletionData->pClassifyData = *ppClassifyData; + pCompletionData->pInjectionData = *ppInjectionData; + + /// Responsibility for freeing this memory has been transferred to the pCompletionData + *ppClassifyData = 0; + + *ppInjectionData = 0; + + HLPR_NEW(pPacketInformation, + FWPS_PACKET_LIST_INFORMATION, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pPacketInformation, + status); + pInterfaceIndex = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_INTERFACE_INDEX); + if(pInterfaceIndex && + pInterfaceIndex->type == FWP_UINT32) + interfaceIndex = (IF_INDEX)pInterfaceIndex->uint32; + + pSubInterfaceIndex = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_SUB_INTERFACE_INDEX); + if(pSubInterfaceIndex && + pSubInterfaceIndex->type == FWP_UINT32) + subInterfaceIndex = (IF_INDEX)pSubInterfaceIndex->uint32; + + pFlags = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_FLAGS); + if(pFlags && + pFlags->type == FWP_UINT32) + flags = pFlags->uint32; + + if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4) + protocol = IPPROTO_ICMP; + else if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6) + protocol = IPPROTO_ICMPV6; + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + else if(pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V6) + protocol = IPPROTO_TCP; + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + else + { + pProtocol = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_PROTOCOL); + HLPR_BAIL_ON_NULL_POINTER(pProtocol); + + protocol = (IPPROTO)pProtocol->uint8; + } + + if(pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4) + { + ipHeaderSize = IPV4_HEADER_MIN_SIZE; + + if(protocol == IPPROTO_ICMP) + transportHeaderSize = ICMP_HEADER_MIN_SIZE; + else if(protocol == IPPROTO_TCP) + transportHeaderSize = TCP_HEADER_MIN_SIZE; + else if(protocol == IPPROTO_UDP) + transportHeaderSize = UDP_HEADER_MIN_SIZE; + } + else if(pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6) + { + ipHeaderSize = IPV6_HEADER_MIN_SIZE; + + if(protocol == IPPROTO_ICMPV6) + transportHeaderSize = ICMP_HEADER_MIN_SIZE; + else if(protocol == IPPROTO_TCP) + transportHeaderSize = TCP_HEADER_MIN_SIZE; + else if(protocol == IPPROTO_UDP) + transportHeaderSize = UDP_HEADER_MIN_SIZE; + } + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_COMPARTMENT_ID)) + compartmentID = (COMPARTMENT_ID)pMetadata->compartmentId; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_IP_HEADER_SIZE) && + pMetadata->ipHeaderSize) + ipHeaderSize = pMetadata->ipHeaderSize; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE) && + pMetadata->transportHeaderSize) + transportHeaderSize = pMetadata->transportHeaderSize; + + bytesRetreated = ipHeaderSize; + + if(protocol != IPPROTO_ICMP && + protocol != IPPROTO_ICMPV6) + { + if(!isInline && + protocol != IPPROTO_TCP && + !(protocol == IPPROTO_UDP && + flags & FWP_CONDITION_FLAG_IS_RAW_ENDPOINT) && + (pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6)) + { + /// For asynchronous execution, the drop will cause the stack to continue processing on the + /// NBL for auditing purposes. This processing retreats the NBL Offset to the Transport header. + /// We need to take this into account because we only took a reference on the NBL. + } + else + bytesRetreated += transportHeaderSize; + } + else + { + if(pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4 || + pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V6) + { + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + bytesRetreated += pMetadata->transportHeaderSize; + } + } + + /// Query to see if IPsec has applied tunnel mode SA's to this NET_BUFFER_LIST ... + status = FwpsGetPacketListSecurityInformation((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket, + FWPS_PACKET_LIST_INFORMATION_QUERY_ALL_INBOUND, + pPacketInformation); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketInjectionAtInboundTransport: FwpsGetPacketListSecurityInformation() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + /// ... if it has, then bypass the injection until the NET_BUFFER_LIST has come out of the tunnel + if((pPacketInformation->ipsecInformation.inbound.isTunnelMode && + !(pPacketInformation->ipsecInformation.inbound.isDeTunneled)) || + pPacketInformation->ipsecInformation.inbound.isSecure) + { + bypassInjection = TRUE; + + HLPR_BAIL; + } + + /// Initial offset is at the data, so retreat the size of the IP Header and Transport Header ... + /// For ICMP, offset is at the ICMP Header, so retreat the size of the IP Header ... + status = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket), + bytesRetreated, + 0, + 0); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketInjectionAtInboundTransport: NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + /// ... clone the entire NET_BUFFER_LIST ... + status = FwpsAllocateCloneNetBufferList((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket, + g_pNDISPoolData->nblPoolHandle, + g_pNDISPoolData->nbPoolHandle, + 0, + &pNetBufferList); + + /// ... and advance the offset back to the original position. + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket), + bytesRetreated, + FALSE, + 0); + if(status != STATUS_SUCCESS || + !pNetBufferList) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketInjectionAtInboundTransport: FwpsAllocateCloneNetBufferList() [status: %#x]\n", + status); + + HLPR_BAIL; + } + +#if DBG + + BasicPacketInjectionCountersIncrement(injectionHandle, + &g_bpiOutstandingNBLClones); + +#endif /// DBG + + checksumInfo.Value = (ULONG)(ULONG_PTR)NET_BUFFER_LIST_INFO((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket, + TcpIpChecksumNetBufferListInfo); + + /// Handle if the packet was IPsec secured + if(pCompletionData->pInjectionData->isIPsecSecured) + { + /// For performance reasons, IPsec leaves the original ESP / AH information in the IP Header ... + UINT32 headerIncludeSize = 0; + UINT64 endpointHandle = 0; + UINT32 ipv4Address = 0; + UINT32 addressSize = 0; + FWP_VALUE* pRemoteAddressValue = 0; + FWP_VALUE* pLocalAddressValue = 0; + FWP_VALUE* pProtocolValue = 0; + + pRemoteAddressValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_REMOTE_ADDRESS); + if(pRemoteAddressValue) + { + if(pRemoteAddressValue->type == FWP_BYTE_ARRAY16_TYPE) + addressSize = IPV6_ADDRESS_SIZE; + else + addressSize = IPV4_ADDRESS_SIZE; + + HLPR_NEW_ARRAY(pSourceAddress, + BYTE, + addressSize, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pSourceAddress, + status); + + if(pRemoteAddressValue->type == FWP_BYTE_ARRAY16_TYPE) + RtlCopyMemory(pSourceAddress, + pRemoteAddressValue->byteArray16->byteArray16, + addressSize); + else + { + ipv4Address = htonl(pRemoteAddressValue->uint32); + + RtlCopyMemory(pSourceAddress, + &ipv4Address, + addressSize); + } + } + + pLocalAddressValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_LOCAL_ADDRESS); + if(pLocalAddressValue) + { + if(pLocalAddressValue->type == FWP_BYTE_ARRAY16_TYPE) + addressSize = IPV6_ADDRESS_SIZE; + else + addressSize = IPV4_ADDRESS_SIZE; + + HLPR_NEW_ARRAY(pDestinationAddress, + BYTE, + addressSize, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pDestinationAddress, + status); + + if(pLocalAddressValue->type == FWP_BYTE_ARRAY16_TYPE) + RtlCopyMemory(pDestinationAddress, + pLocalAddressValue->byteArray16->byteArray16, + addressSize); + else + { + ipv4Address = htonl(pLocalAddressValue->uint32); + + RtlCopyMemory(pDestinationAddress, + &ipv4Address, + addressSize); + } + } + + pProtocolValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_PROTOCOL); + if(pProtocolValue && + pProtocolValue->type == FWP_UINT8) + protocol = (IPPROTO)pProtocolValue->uint8; + else + protocol = IPPROTO_MAX; + + NT_ASSERT(protocol != IPPROTO_MAX); + +#if (NTDDI_VERSION >= NTDDI_WIN6SP1) + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_INCLUDE_HEADER)) + headerIncludeSize = pMetadata->headerIncludeHeaderLength; + +#endif // (NTDDI_VERSION >= NTDDI_WIN6SP1) + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_ENDPOINT_HANDLE)) + endpointHandle = pMetadata->transportEndpointHandle; + + if(pSourceAddress == 0 || + pDestinationAddress == 0) + { + status = STATUS_INVALID_MEMBER; + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtInboundTransport() [status: %#x][pSourceAddress: %#p][pDestinationAddress: %#p]\n", + status, + pSourceAddress, + pDestinationAddress); + + HLPR_BAIL; + } + + /// ... so we must re-construct the IPHeader with the appropriate information + status = FwpsConstructIpHeaderForTransportPacket(pNetBufferList, + headerIncludeSize, + pCompletionData->pInjectionData->addressFamily, + pSourceAddress, + pDestinationAddress, + protocol, + endpointHandle, + (const WSACMSGHDR*)pCompletionData->pInjectionData->pControlData, + pCompletionData->pInjectionData->controlDataLength, + 0, + 0, + interfaceIndex, + subInterfaceIndex); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketInjectionAtInboundTransport: FwpsConstructIpHeaderForTransportPacket() [status: %#x]\n", + status); + + HLPR_BAIL; + } + } + /// Handle if this packet had the IP or Transport checksums offloaded or if it's loopback + else if(checksumInfo.Receive.NdisPacketIpChecksumSucceeded || + checksumInfo.Receive.NdisPacketTcpChecksumSucceeded || + checksumInfo.Receive.NdisPacketUdpChecksumSucceeded || + flags & FWP_CONDITION_FLAG_IS_LOOPBACK) + { + /// Prevent TCP/IP Zone crossing and recalculate the checksums + if(flags & FWP_CONDITION_FLAG_IS_LOOPBACK) + { + FWP_VALUE* pLocalAddress = 0; + FWP_VALUE* pRemoteAddress = 0; + FWP_VALUE* pLoopbackAddress = 0; + + pLocalAddress = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_REMOTE_ADDRESS); + if(pLocalAddress && + ((pLocalAddress->type == FWP_UINT32 && + RtlCompareMemory(&(pLocalAddress->uint32), + IPV4_LOOPBACK_ADDRESS, + IPV4_ADDRESS_SIZE)) || + (pLocalAddress->type == FWP_BYTE_ARRAY16_TYPE && + RtlCompareMemory(pLocalAddress->byteArray16->byteArray16, + IPV6_LOOPBACK_ADDRESS, + IPV6_ADDRESS_SIZE)))) + pLoopbackAddress = pLocalAddress; + + if(!pLoopbackAddress) + { + pRemoteAddress = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_REMOTE_ADDRESS); + if(pRemoteAddress && + ((pRemoteAddress->type == FWP_UINT32 && + RtlCompareMemory(&(pRemoteAddress->uint32), + IPV4_LOOPBACK_ADDRESS, + IPV4_ADDRESS_SIZE)) || + (pRemoteAddress->type == FWP_BYTE_ARRAY16_TYPE && + RtlCompareMemory(pRemoteAddress->byteArray16->byteArray16, + IPV6_LOOPBACK_ADDRESS, + IPV6_ADDRESS_SIZE)))) + pLoopbackAddress = pRemoteAddress; + } + + if(pLoopbackAddress) + { + status = KrnlHlprIPHeaderModifyLoopbackToLocal(pMetadata, + pLoopbackAddress, + ipHeaderSize, + pNetBufferList, + (const WSACMSGHDR*)pCompletionData->pInjectionData->pControlData, + pCompletionData->pInjectionData->controlDataLength); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketInjectionAtInboundTransport: KrnlHlprIPHeaderModifyLoopbackToLocal() [status: %#x]\n", + status); + + HLPR_BAIL; + } + } + } + else + { + /// Recalculate the checksum + if(pCompletionData->pInjectionData->addressFamily == AF_INET) + KrnlHlprIPHeaderCalculateV4Checksum(pNetBufferList, + ipHeaderSize); + } + } + + pCompletionData->refCount = KrnlHlprNBLGetRequiredRefCount(pNetBufferList); + + status = FwpsInjectTransportReceiveAsync(pCompletionData->pInjectionData->injectionHandle, + pCompletionData->pInjectionData->injectionContext, + 0, + 0, + pCompletionData->pInjectionData->addressFamily, + compartmentID, + interfaceIndex, + subInterfaceIndex, + pNetBufferList, + CompleteBasicPacketInjection, + pCompletionData); + + NT_ASSERT(irql == KeGetCurrentIrql()); + + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketInjectionAtInboundTransport: FwpsInjectTransportReceiveAsync() [status: %#x]\n", + status); + +#if DBG + + BasicPacketInjectionCountersIncrement(injectionHandle, + &g_bpiTotalFailedInjectionCalls); + +#endif /// DBG + + } + +#if DBG + + else + BasicPacketInjectionCountersIncrement(injectionHandle, + &g_bpiTotalSuccessfulInjectionCalls); + +#endif /// DBG + + HLPR_BAIL_LABEL: + + NT_ASSERT(status == STATUS_SUCCESS); + + if(status != STATUS_SUCCESS || + bypassInjection) + { + if(pNetBufferList) + { + FwpsFreeCloneNetBufferList(pNetBufferList, + 0); + + pNetBufferList = 0; + +#if DBG + + BasicPacketInjectionCountersDecrement(injectionHandle, + &g_bpiOutstandingNBLClones); + +#endif + + } + + if(pCompletionData) + BasicPacketInjectionCompletionDataDestroy(&pCompletionData, + TRUE); + } + + HLPR_DELETE_ARRAY(pSourceAddress, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + + HLPR_DELETE_ARRAY(pDestinationAddress, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + + HLPR_DELETE(pPacketInformation, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PerformBasicPacketInjectionAtInboundTransport() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @private_function="PerformBasicPacketInjectionAtOutboundTransport" + + Purpose: Clones the NET_BUFFER_LIST and injects the clone back to the stack's outbound path + from the outgoing Transport Layers using FwpsInjectTransportSendAsync().
+
+ Notes: Applies to the following outbound layers:
+ FWPM_LAYER_OUTBOUND_TRANSPORT_V{4/6}
+ FWPM_LAYER_OUTBOUND_ICMP_ERROR_V{4/6}
+ FWPM_LAYER_DATAGRAM_DATA_V{4/6} (Outbound only)
+ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V{4/6} (Outbound only)
+ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V{4/6} (Outbound only)
+ FWPM_LAYER_ALE_AUTH_CONNECT_V{4/6} (Outbound reauthorization only)
+ FWPM_LAYER_ALE_FLOW_ESTABLISHED_V{4/6} (Outbound, non-TCP only)
+ FWPM_LAYER_STREAM_PACKET_V{4/6} (Outbound only)
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551188.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF546324.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +NTSTATUS PerformBasicPacketInjectionAtOutboundTransport(_In_ CLASSIFY_DATA** ppClassifyData, + _In_ INJECTION_DATA** ppInjectionData, + _In_ BOOLEAN isInline = FALSE) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PerformBasicPacketInjectionAtOutboundTransport()\n"); + +#endif /// DBG + + NT_ASSERT(ppClassifyData); + NT_ASSERT(ppInjectionData); + NT_ASSERT(*ppClassifyData); + NT_ASSERT(*ppInjectionData); + + NTSTATUS status = STATUS_SUCCESS; + FWPS_INCOMING_VALUES* pClassifyValues = (FWPS_INCOMING_VALUES*)(*ppClassifyData)->pClassifyValues; + FWPS_INCOMING_METADATA_VALUES* pMetadata = (FWPS_INCOMING_METADATA_VALUES*)(*ppClassifyData)->pMetadataValues; + UINT64 endpointHandle = 0; + FWPS_TRANSPORT_SEND_PARAMS* pSendParams = 0; + COMPARTMENT_ID compartmentID = DEFAULT_COMPARTMENT_ID; + NET_BUFFER_LIST* pNetBufferList = 0; + BASIC_PACKET_INJECTION_COMPLETION_DATA* pCompletionData = 0; + BYTE* pRemoteAddress = 0; + FWP_VALUE* pAddressValue = 0; + +#if DBG + + KIRQL irql = KeGetCurrentIrql(); + HANDLE injectionHandle = (*ppInjectionData)->injectionHandle; + +#endif /// DBG + +#pragma warning(push) +#pragma warning(disable: 6014) /// pCompletionData & pSendParams will be freed in completionFn using BasicPacketInjectionCompletionDataDestroy + + HLPR_NEW(pCompletionData, + BASIC_PACKET_INJECTION_COMPLETION_DATA, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pCompletionData, + status); + + HLPR_NEW(pSendParams, + FWPS_TRANSPORT_SEND_PARAMS, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pSendParams, + status); + +#pragma warning(pop) + + KeInitializeSpinLock(&(pCompletionData->spinLock)); + + pCompletionData->performedInline = isInline; + pCompletionData->pClassifyData = *ppClassifyData; + pCompletionData->pInjectionData = *ppInjectionData; + pCompletionData->pSendParams = pSendParams; + + /// Responsibility for freeing this memory has been transferred to the pCompletionData + *ppClassifyData = 0; + + *ppInjectionData = 0; + + pSendParams = 0; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_ENDPOINT_HANDLE)) + endpointHandle = pMetadata->transportEndpointHandle; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_COMPARTMENT_ID)) + compartmentID = (COMPARTMENT_ID)pMetadata->compartmentId; + + pAddressValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_REMOTE_ADDRESS); + if(pAddressValue) + { + if(pCompletionData->pInjectionData->addressFamily == AF_INET) + { + UINT32 tempAddress = htonl(pAddressValue->uint32); + + HLPR_NEW_ARRAY(pRemoteAddress, + BYTE, + IPV4_ADDRESS_SIZE, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pRemoteAddress, + status); + + RtlCopyMemory(pRemoteAddress, + &tempAddress, + IPV4_ADDRESS_SIZE); + } + else + { +#pragma warning(push) +#pragma warning(disable: 6014) /// pRemoteAddress will be freed in completionFn using BasicPacketInjectionCompletionDataDestroy + + HLPR_NEW_ARRAY(pRemoteAddress, + BYTE, + IPV6_ADDRESS_SIZE, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pRemoteAddress, + status); + +#pragma warning(pop) + + RtlCopyMemory(pRemoteAddress, + pAddressValue->byteArray16->byteArray16, + IPV6_ADDRESS_SIZE); + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_REMOTE_SCOPE_ID)) + pCompletionData->pSendParams->remoteScopeId = pMetadata->remoteScopeId; + } + + pCompletionData->pSendParams->remoteAddress = pRemoteAddress; + } + + pCompletionData->pSendParams->controlData = (WSACMSGHDR*)pCompletionData->pInjectionData->pControlData; + pCompletionData->pSendParams->controlDataLength = pCompletionData->pInjectionData->controlDataLength; + + /// Initial offset is at Transport Header, so just clone entire NET_BUFFER_LIST. + status = FwpsAllocateCloneNetBufferList((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket, + g_pNDISPoolData->nblPoolHandle, + g_pNDISPoolData->nbPoolHandle, + 0, + &pNetBufferList); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketInjectionAtOutboundTransport: FwpsAllocateCloneNetBufferList() [status: %#x]\n", + status); + + HLPR_BAIL; + } + +#if DBG + + BasicPacketInjectionCountersIncrement(injectionHandle, + &g_bpiOutstandingNBLClones); + +#endif /// DBG + + pCompletionData->refCount = KrnlHlprNBLGetRequiredRefCount(pNetBufferList); + + status = FwpsInjectTransportSendAsync(pCompletionData->pInjectionData->injectionHandle, + pCompletionData->pInjectionData->injectionContext, + endpointHandle, + 0, + pCompletionData->pSendParams, + pCompletionData->pInjectionData->addressFamily, + compartmentID, + pNetBufferList, + CompleteBasicPacketInjection, + pCompletionData); + + NT_ASSERT(irql == KeGetCurrentIrql()); + + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketInjectionAtOutboundTransport: FwpsInjectTransportSendAsync() [status: %#x]\n", + status); + +#if DBG + + BasicPacketInjectionCountersIncrement(injectionHandle, + &g_bpiTotalFailedInjectionCalls); + +#endif /// DBG + + } + +#if DBG + + else + BasicPacketInjectionCountersIncrement(injectionHandle, + &g_bpiTotalSuccessfulInjectionCalls); + +#endif /// DBG + + HLPR_BAIL_LABEL: + + NT_ASSERT(status == STATUS_SUCCESS); + + if(status != STATUS_SUCCESS) + { + if(pNetBufferList) + { + FwpsFreeCloneNetBufferList(pNetBufferList, + 0); + + pNetBufferList = 0; + +#if DBG + + BasicPacketInjectionCountersDecrement(injectionHandle, + &g_bpiOutstandingNBLClones); + +#endif + + } + + if(pCompletionData) + BasicPacketInjectionCompletionDataDestroy(&pCompletionData, + TRUE); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PerformBasicPacketInjectionAtOutboundTransport() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @private_function="BasicPacketInjectionDeferredProcedureCall" + + Purpose: Invokes the appropriate private injection routine to perform the injection at + DISPATCH_LEVEL.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF542972.aspx
+*/ +_IRQL_requires_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Function_class_(KDEFERRED_ROUTINE) +VOID BasicPacketInjectionDeferredProcedureCall(_In_ KDPC* pDPC, + _In_opt_ PVOID pContext, + _In_opt_ PVOID pArg1, + _In_opt_ PVOID pArg2) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> BasicPacketInjectionDeferredProcedureCall()\n"); + +#endif /// DBG + + UNREFERENCED_PARAMETER(pDPC); + UNREFERENCED_PARAMETER(pContext); + UNREFERENCED_PARAMETER(pArg2); + + NT_ASSERT(pDPC); + NT_ASSERT(pArg1); + NT_ASSERT(((DPC_DATA*)pArg1)->pClassifyData); + NT_ASSERT(((DPC_DATA*)pArg1)->pInjectionData); + + DPC_DATA* pDPCData = (DPC_DATA*)pArg1; + + if(pDPCData) + { + NTSTATUS status = STATUS_SUCCESS; + FWPS_INCOMING_VALUES* pClassifyValues = (FWPS_INCOMING_VALUES*)pDPCData->pClassifyData->pClassifyValues; + + if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6) + status = PerformBasicPacketInjectionAtInboundNetwork(&(pDPCData->pClassifyData), + &(pDPCData->pInjectionData), + FALSE); + else if(pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V6) + status = PerformBasicPacketInjectionAtOutboundNetwork(&(pDPCData->pClassifyData), + &(pDPCData->pInjectionData), + FALSE); + else if(pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V4 || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V6) + status = PerformBasicPacketInjectionAtForward(&(pDPCData->pClassifyData), + &(pDPCData->pInjectionData), + FALSE); + else if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V6 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V6 || + (pDPCData->pInjectionData->direction == FWP_DIRECTION_INBOUND && + (pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4 || + pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4 || /// Policy Change Reauthorization + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V6 || /// Policy Change Reauthorization + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6))) + status = PerformBasicPacketInjectionAtInboundTransport(&(pDPCData->pClassifyData), + &(pDPCData->pInjectionData), + FALSE); + else if(pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6 || + (pDPCData->pInjectionData->direction == FWP_DIRECTION_OUTBOUND && + (pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4 || + pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 || /// Policy Change Reauthorization + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6 || /// Policy Change Reauthorization + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6))) + status = PerformBasicPacketInjectionAtOutboundTransport(&(pDPCData->pClassifyData), + &(pDPCData->pInjectionData), + FALSE); + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + else if(pDPCData->pInjectionData->direction == FWP_DIRECTION_INBOUND && + (pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V6)) + status = PerformBasicPacketInjectionAtInboundTransport(&(pDPCData->pClassifyData), + &(pDPCData->pInjectionData), + FALSE); + else if(pDPCData->pInjectionData->direction == FWP_DIRECTION_OUTBOUND && + (pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V6)) + status = PerformBasicPacketInjectionAtOutboundTransport(&(pDPCData->pClassifyData), + &(pDPCData->pInjectionData), + FALSE); + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + else if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_MAC_FRAME_ETHERNET || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_MAC_FRAME_NATIVE) + status = PerformBasicPacketInjectionAtInboundMACFrame(&(pDPCData->pClassifyData), + &(pDPCData->pInjectionData), + FALSE); + else if(pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_MAC_FRAME_ETHERNET || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_MAC_FRAME_NATIVE) + status = PerformBasicPacketInjectionAtOutboundMACFrame(&(pDPCData->pClassifyData), + &(pDPCData->pInjectionData), + FALSE); + else if(pClassifyValues->layerId == FWPS_LAYER_INGRESS_VSWITCH_ETHERNET) + status = PerformBasicPacketInjectionAtIngressVSwitchEthernet(&(pDPCData->pClassifyData), + &(pDPCData->pInjectionData), + FALSE); + else if(pClassifyValues->layerId == FWPS_LAYER_EGRESS_VSWITCH_ETHERNET) + status = PerformBasicPacketInjectionAtEgressVSwitchEthernet(&(pDPCData->pClassifyData), + &(pDPCData->pInjectionData), + FALSE); + +#endif // (NTDDI_VERSION >= NTDDI_WIN8) +#endif // (NTDDI_VERSION >= NTDDI_WIN7) + + else + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! BasicPacketInjectionDeferredProcedureCall() [status: %#x]\n", + (UINT32)STATUS_NOT_SUPPORTED); + + if(status != STATUS_SUCCESS) + { + if(pDPCData->pClassifyData) + KrnlHlprClassifyDataDestroyLocalCopy(&(pDPCData->pClassifyData)); + + if(pDPCData->pInjectionData) + KrnlHlprInjectionDataDestroy(&(pDPCData->pInjectionData)); + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! BasicPacketInjectionDeferredProcedureCall: PerformBasicPacketInjection() [status: %#x]\n", + status); + } + + KrnlHlprDPCDataDestroy(&pDPCData); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- BasicPacketInjectionDeferredProcedureCall()\n"); + +#endif /// DBG + + return; +} + +/** + @private_function="BasicPacketInjectionWorkItemRoutine" + + Purpose: Invokes the appropriate private injection routine to perform the injection at + PASSIVE_LEVEL.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF566380.aspx
+*/ +_IRQL_requires_(PASSIVE_LEVEL) +_IRQL_requires_same_ +_Function_class_(IO_WORKITEM_ROUTINE) +VOID BasicPacketInjectionWorkItemRoutine(_In_ PDEVICE_OBJECT pDeviceObject, + _Inout_opt_ PVOID pContext) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> BasicPacketInjectionWorkItemRoutine()\n"); + +#endif /// DBG + + UNREFERENCED_PARAMETER(pDeviceObject); + + NT_ASSERT(pContext); + NT_ASSERT(((WORKITEM_DATA*)pContext)->pClassifyData); + NT_ASSERT(((WORKITEM_DATA*)pContext)->pInjectionData); + + WORKITEM_DATA* pWorkItemData = (WORKITEM_DATA*)pContext; + + if(pWorkItemData) + { + NTSTATUS status = STATUS_SUCCESS; + FWPS_INCOMING_VALUES* pClassifyValues = (FWPS_INCOMING_VALUES*)pWorkItemData->pClassifyData->pClassifyValues; + + if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6) + status = PerformBasicPacketInjectionAtInboundNetwork(&(pWorkItemData->pClassifyData), + &(pWorkItemData->pInjectionData), + FALSE); + else if(pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V6) + status = PerformBasicPacketInjectionAtOutboundNetwork(&(pWorkItemData->pClassifyData), + &(pWorkItemData->pInjectionData), + FALSE); + else if(pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V4 || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V6) + status = PerformBasicPacketInjectionAtForward(&(pWorkItemData->pClassifyData), + &(pWorkItemData->pInjectionData), + FALSE); + else if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V6 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V6 || + (pWorkItemData->pInjectionData->direction == FWP_DIRECTION_INBOUND && + (pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4 || + pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4 || /// Policy Change Reauthorization + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V6 || /// Policy Change Reauthorization + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6))) + status = PerformBasicPacketInjectionAtInboundTransport(&(pWorkItemData->pClassifyData), + &(pWorkItemData->pInjectionData), + FALSE); + else if(pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6 || + (pWorkItemData->pInjectionData->direction == FWP_DIRECTION_OUTBOUND && + (pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4 || + pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 || /// Policy Change Reauthorization + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6 || /// Policy Change Reauthorization + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6))) + status = PerformBasicPacketInjectionAtOutboundTransport(&(pWorkItemData->pClassifyData), + &(pWorkItemData->pInjectionData), + FALSE); + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + else if(pWorkItemData->pInjectionData->direction == FWP_DIRECTION_INBOUND && + (pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V6)) + status = PerformBasicPacketInjectionAtInboundTransport(&(pWorkItemData->pClassifyData), + &(pWorkItemData->pInjectionData), + FALSE); + else if(pWorkItemData->pInjectionData->direction == FWP_DIRECTION_OUTBOUND && + (pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V6)) + status = PerformBasicPacketInjectionAtOutboundTransport(&(pWorkItemData->pClassifyData), + &(pWorkItemData->pInjectionData), + FALSE); + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + else if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_MAC_FRAME_ETHERNET || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_MAC_FRAME_NATIVE) + status = PerformBasicPacketInjectionAtInboundMACFrame(&(pWorkItemData->pClassifyData), + &(pWorkItemData->pInjectionData), + FALSE); + else if(pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_MAC_FRAME_ETHERNET || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_MAC_FRAME_NATIVE) + status = PerformBasicPacketInjectionAtOutboundMACFrame(&(pWorkItemData->pClassifyData), + &(pWorkItemData->pInjectionData), + FALSE); + else if(pClassifyValues->layerId == FWPS_LAYER_INGRESS_VSWITCH_ETHERNET) + status = PerformBasicPacketInjectionAtIngressVSwitchEthernet(&(pWorkItemData->pClassifyData), + &(pWorkItemData->pInjectionData), + FALSE); + else if(pClassifyValues->layerId == FWPS_LAYER_EGRESS_VSWITCH_ETHERNET) + status = PerformBasicPacketInjectionAtEgressVSwitchEthernet(&(pWorkItemData->pClassifyData), + &(pWorkItemData->pInjectionData), + FALSE); + +#endif // (NTDDI_VERSION >= NTDDI_WIN8) +#endif // (NTDDI_VERSION >= NTDDI_WIN7) + + else + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! BasicPacketInjectionWorkItemRoutine() [status: %#x]\n", + (UINT32)STATUS_NOT_SUPPORTED); + + if(status != STATUS_SUCCESS) + { + if(pWorkItemData->pClassifyData) + KrnlHlprClassifyDataDestroyLocalCopy(&(pWorkItemData->pClassifyData)); + + if(pWorkItemData->pInjectionData) + KrnlHlprInjectionDataDestroy(&(pWorkItemData->pInjectionData)); + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! BasicPacketInjectionWorkItemRoutine: PerformBasicPacketInjection() [status: %#x]\n", + status); + } + + KrnlHlprWorkItemDataDestroy(&pWorkItemData); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- BasicPacketInjectionWorkItemRoutine()\n"); + +#endif /// DBG + + return; +} + +/** + @private_function="TriggerBasicPacketInjectionInline" + + Purpose: Makes a reference to all the classification data structures and invokes the + appropriate private injection routine to perform the injection.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +NTSTATUS TriggerBasicPacketInjectionInline(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pNetBufferList, + _In_opt_ const VOID* pClassifyContext, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut, + _In_ INJECTION_DATA** ppInjectionData) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> TriggerBasicPacketInjectionInline()\n"); + +#endif /// DBG + + NT_ASSERT(pClassifyValues); + NT_ASSERT(pMetadata); + NT_ASSERT(pNetBufferList); + NT_ASSERT(pFilter); + NT_ASSERT(pClassifyOut); + NT_ASSERT(ppInjectionData); + NT_ASSERT(*ppInjectionData); + + + NTSTATUS status = STATUS_SUCCESS; + CLASSIFY_DATA* pClassifyData = 0; + +#pragma warning(push) +#pragma warning(disable: 6014) /// pClassifyData will be freed in completionFn using BasicPacketInjectionCompletionDataDestroy + + HLPR_NEW(pClassifyData, + CLASSIFY_DATA, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pClassifyData, + status); + +#pragma warning(pop) + + pClassifyData->pClassifyValues = pClassifyValues; + pClassifyData->pMetadataValues = pMetadata; + pClassifyData->pPacket = pNetBufferList; + pClassifyData->pClassifyContext = pClassifyContext; + pClassifyData->pFilter = pFilter; + pClassifyData->flowContext = flowContext; + pClassifyData->pClassifyOut = pClassifyOut; + + if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6) + status = PerformBasicPacketInjectionAtInboundNetwork(&pClassifyData, + ppInjectionData, + TRUE); + else if(pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V6) + status = PerformBasicPacketInjectionAtOutboundNetwork(&pClassifyData, + ppInjectionData, + TRUE); + else if(pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V4 || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V6) + status = PerformBasicPacketInjectionAtForward(&pClassifyData, + ppInjectionData, + TRUE); + else if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V6 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V6 || + ((*ppInjectionData)->direction == FWP_DIRECTION_INBOUND && + (pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4 || + pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4 || /// Policy Change Reauthorization + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V6 || /// Policy Change Reauthorization + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6))) + status = PerformBasicPacketInjectionAtInboundTransport(&pClassifyData, + ppInjectionData, + TRUE); + else if(pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6 || + ((*ppInjectionData)->direction == FWP_DIRECTION_OUTBOUND && + (pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4 || + pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 || /// Policy Change Reauthorization + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6 || /// Policy Change Reauthorization + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6))) + status = PerformBasicPacketInjectionAtOutboundTransport(&pClassifyData, + ppInjectionData, + TRUE); + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + else if((*ppInjectionData)->direction == FWP_DIRECTION_INBOUND && + (pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V6)) + status = PerformBasicPacketInjectionAtInboundTransport(&pClassifyData, + ppInjectionData, + TRUE); + else if((*ppInjectionData)->direction == FWP_DIRECTION_OUTBOUND && + (pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V6)) + status = PerformBasicPacketInjectionAtOutboundTransport(&pClassifyData, + ppInjectionData, + TRUE); + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + else if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_MAC_FRAME_ETHERNET || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_MAC_FRAME_NATIVE) + status = PerformBasicPacketInjectionAtInboundMACFrame(&pClassifyData, + ppInjectionData, + TRUE); + else if(pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_MAC_FRAME_ETHERNET || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_MAC_FRAME_NATIVE) + status = PerformBasicPacketInjectionAtOutboundMACFrame(&pClassifyData, + ppInjectionData, + TRUE); + else if(pClassifyValues->layerId == FWPS_LAYER_INGRESS_VSWITCH_ETHERNET) + status = PerformBasicPacketInjectionAtIngressVSwitchEthernet(&pClassifyData, + ppInjectionData, + TRUE); + else if(pClassifyValues->layerId == FWPS_LAYER_EGRESS_VSWITCH_ETHERNET) + status = PerformBasicPacketInjectionAtEgressVSwitchEthernet(&pClassifyData, + ppInjectionData, + TRUE); + +#endif // (NTDDI_VERSION >= NTDDI_WIN8) +#endif // (NTDDI_VERSION >= NTDDI_WIN7) + + else + { + status = STATUS_NOT_SUPPORTED; + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! TriggerBasicPacketInjectionInline() [status: %#x]\n", + status); + } + + HLPR_BAIL_LABEL: + + NT_ASSERT(status == STATUS_SUCCESS); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- TriggerBasicPacketInjectionInline() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @private_function="TriggerBasicPacketInjectionOutOfBand" + + Purpose: Creates a local copy of the classification data structures and queues a WorkItem + to perform the injection at PASSIVE_LEVEL.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF550679.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF566380.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +NTSTATUS TriggerBasicPacketInjectionOutOfBand(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pNetBufferList, + _In_opt_ const VOID* pClassifyContext, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _In_ FWPS_CLASSIFY_OUT* pClassifyOut, + _In_ INJECTION_DATA* pInjectionData, + _In_ PC_BASIC_PACKET_INJECTION_DATA* pPCData) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> TriggerBasicPacketInjectionOutOfBand()\n"); + +#endif /// DBG + + UNREFERENCED_PARAMETER(pClassifyOut); + + NT_ASSERT(pClassifyValues); + NT_ASSERT(pMetadata); + NT_ASSERT(pNetBufferList); + NT_ASSERT(pFilter); + NT_ASSERT(pClassifyOut); + NT_ASSERT(pInjectionData); + NT_ASSERT(pPCData); + + NTSTATUS status = STATUS_SUCCESS; + CLASSIFY_DATA* pClassifyData = 0; + +#pragma warning(push) +#pragma warning(disable: 6014) /// pClassifyData will be freed in completionFn using BasicPacketInjectionCompletionDataDestroy + + status = KrnlHlprClassifyDataCreateLocalCopy(&pClassifyData, + pClassifyValues, + pMetadata, + pNetBufferList, + pClassifyContext, + pFilter, + flowContext, + pClassifyOut); + HLPR_BAIL_ON_FAILURE(status); + +#pragma warning(pop) + + if(pPCData->useWorkItems) + status = KrnlHlprWorkItemQueue(g_pWDMDevice, + BasicPacketInjectionWorkItemRoutine, + pClassifyData, + pInjectionData, + 0); + else if(pPCData->useThreadedDPC) + status = KrnlHlprThreadedDPCQueue(BasicPacketInjectionDeferredProcedureCall, + pClassifyData, + pInjectionData, + 0); + else + status = KrnlHlprDPCQueue(BasicPacketInjectionDeferredProcedureCall, + pClassifyData, + pInjectionData, + 0); + + HLPR_BAIL_LABEL: + + NT_ASSERT(status == STATUS_SUCCESS); + + if(status != STATUS_SUCCESS) + { + if(pClassifyData) + KrnlHlprClassifyDataDestroyLocalCopy(&pClassifyData); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- TriggerBasicPacketInjectionOutOfBand() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +#if(NTDDI_VERSION >= NTDDI_WIN7) + +/** + @classify_function="ClassifyBasicPacketInjection" + + Purpose: Blocks the current NET_BUFFER_LIST and injects a clone back to the stack without + modification.
+
+ Notes: Applies to the following layers:
+ FWPS_LAYER_INBOUND_IPPACKET_V{4/6}
+ FWPS_LAYER_OUTBOUND_IPPACKET_V{4/6}
+ FWPS_LAYER_IPFORWARD_V{4/6}
+ FWPS_LAYER_INBOUND_TRANSPORT_V{4/6}
+ FWPS_LAYER_OUTBOUND_TRANSPORT_V{4/6}
+ FWPS_LAYER_DATAGRAM_DATA_V{4/6}
+ FWPS_LAYER_INBOUND_ICMP_ERROR_V{4/6}
+ FWPS_LAYER_OUTBOUND_ICMP_ERROR_V{4/6}
+ FWPS_LAYER_ALE_AUTH_CONNECT_V{4/6}
+ FWPS_LAYER_ALE_FLOW_ESTABLISHED_V{4/6}
+ FWPS_LAYER_STREAM_PACKET_V{4/6}
+ FWPS_LAYER_INBOUND_MAC_FRAME_ETHERNET
+ FWPS_LAYER_OUTBOUND_MAC_FRAME_ETHERNET
+ FWPS_LAYER_INBOUND_MAC_FRAME_NATIVE
+ FWPS_LAYER_OUTBOUND_MAC_FRAME_NATIVE
+ FWPS_LAYER_INGRESS_VSWITCH_ETHERNET
+ FWPS_LAYER_EGRESS_VSWITCH_ETHERNET
+
+ TCP @ FWPM_LAYER_ALE_AUTH_CONNECT_V{4/6} has no NBL
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF544893.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI ClassifyBasicPacketInjection(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pNetBufferList, + _In_opt_ const VOID* pClassifyContext, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut) +{ + NT_ASSERT(pClassifyValues); + NT_ASSERT(pMetadata); + NT_ASSERT(pFilter); + NT_ASSERT(pClassifyOut); + NT_ASSERT(pFilter->providerContext); + NT_ASSERT(pFilter->providerContext->type == FWPM_GENERAL_CONTEXT); + NT_ASSERT(pFilter->providerContext->dataBuffer); + NT_ASSERT(pFilter->providerContext->dataBuffer->size == sizeof(PC_BASIC_PACKET_INJECTION_DATA)); + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + NT_ASSERT(pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V6 || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V4 || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V6 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V6 || + pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4 || + pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V6 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_MAC_FRAME_ETHERNET || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_MAC_FRAME_ETHERNET || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_MAC_FRAME_NATIVE || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_MAC_FRAME_NATIVE || + pClassifyValues->layerId == FWPS_LAYER_INGRESS_VSWITCH_ETHERNET || + pClassifyValues->layerId == FWPS_LAYER_EGRESS_VSWITCH_ETHERNET); + +#else + + NT_ASSERT(pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V6 || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V4 || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V6 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V6 || + pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4 || + pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V6); + +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) + + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> ClassifyBasicPacketInjection() [Layer: %s][FilterID: %#I64x][Rights: %#x]", + KrnlHlprFwpsLayerIDToString(pClassifyValues->layerId), + pFilter->filterId, + pClassifyOut->rights); + +#if DBG + + PrvBasicPacketInjectionCountersIncrement(pClassifyValues, + pMetadata, + &g_bpiTotalClassifies); + +#endif /// DBG + + if(pClassifyOut->rights & FWPS_RIGHT_ACTION_WRITE) + { + /// Packets are not available for TCP @ ALE_AUTH_CONNECT, so skip over as there is nothing to inject + if(pNetBufferList) + { + NTSTATUS status = STATUS_SUCCESS; + FWP_VALUE* pFlags = 0; + INJECTION_DATA* pInjectionData = 0; + + pClassifyOut->actionType = FWP_ACTION_CONTINUE; + + pFlags = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_FLAGS); + if(pFlags && + pFlags->type == FWP_UINT32) + { + /// For IPsec interop, if ALE classification is required, bypass the injection + if(pFlags->uint32 & FWP_CONDITION_FLAG_IS_IPSEC_SECURED && + FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_ALE_CLASSIFY_REQUIRED)) + HLPR_BAIL; + + /// Inject the individual fragments, but not the fragment grouping of those fragments + if(pFlags->uint32 & FWP_CONDITION_FLAG_IS_FRAGMENT_GROUP) + HLPR_BAIL; + } + +#pragma warning(push) +#pragma warning(disable: 6014) /// pInjectionData will be freed in completionFn using BasicPacketInjectionCompletionDataDestroy + + status = KrnlHlprInjectionDataCreate(&pInjectionData, + pClassifyValues, + pMetadata, + (NET_BUFFER_LIST*)pNetBufferList, + pFilter); + HLPR_BAIL_ON_FAILURE(status); + +#pragma warning(pop) + + if(pInjectionData->injectionState != FWPS_PACKET_INJECTED_BY_SELF && + pInjectionData->injectionState != FWPS_PACKET_PREVIOUSLY_INJECTED_BY_SELF) + { + BOOLEAN performOutOfBand = TRUE; + FWP_VALUE* pProtocolValue = 0; + PC_BASIC_PACKET_INJECTION_DATA* pData = (PC_BASIC_PACKET_INJECTION_DATA*)pFilter->providerContext->dataBuffer->data; + + pClassifyOut->actionType = FWP_ACTION_BLOCK; + pClassifyOut->flags |= FWPS_CLASSIFY_OUT_FLAG_ABSORB; + pClassifyOut->rights ^= FWPS_RIGHT_ACTION_WRITE; + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + /// This indicates that we have already acted on the 1st NBL of the distributed set. + /// We can ignore injecting the rest of the individual distributed NBLs because the + /// injected NBL will get redistributed. Flag is only set for FWPM_LAYER_EGRESS_VSWITCH. + if(pMetadata->l2Flags & FWPS_L2_INCOMING_FLAG_RECLASSIFY_MULTI_DESTINATION) + HLPR_BAIL; + +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) + + if(pFlags && + pFlags->type == FWP_UINT32 && + pFlags->uint32 & FWP_CONDITION_FLAG_IS_IPSEC_SECURED) + pInjectionData->isIPsecSecured = TRUE; + + /// Override the default of performing Out of Band with the user's specified setting ... + if(pData->performInline) + performOutOfBand = FALSE; + + /// ... however, due to TCP's locking semantics, TCP can only be injected Out of Band at any transport layer or equivalent, ... + pProtocolValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_PROTOCOL); + if((pProtocolValue && + pProtocolValue->uint8 == IPPROTO_TCP && + pClassifyValues->layerId > FWPS_LAYER_IPFORWARD_V6_DISCARD) || + pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V6) + performOutOfBand = TRUE; + + /// ... and inbound injection of loopback traffic requires us to use Out of Band modification as well due to address lookups. + if(!performOutOfBand && + pFlags && + pFlags->type == FWP_UINT32 && + pFlags->uint32 & FWP_CONDITION_FLAG_IS_LOOPBACK && + pInjectionData->direction == FWP_DIRECTION_INBOUND) + { + FWP_VALUE* pLocalAddress = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_LOCAL_ADDRESS); + FWP_VALUE* pRemoteAddress = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_REMOTE_ADDRESS); + + if((pLocalAddress && + ((pLocalAddress->type == FWP_UINT32 && + RtlCompareMemory(&(pLocalAddress->uint32), + IPV4_LOOPBACK_ADDRESS, + IPV4_ADDRESS_SIZE)) || + (pLocalAddress->type == FWP_BYTE_ARRAY16_TYPE && + pLocalAddress->byteArray16 && + RtlCompareMemory(&(pLocalAddress->byteArray16->byteArray16), + IPV6_LOOPBACK_ADDRESS, + IPV6_ADDRESS_SIZE)))) || + (pRemoteAddress && + ((pRemoteAddress->type == FWP_UINT32 && + RtlCompareMemory(&(pRemoteAddress->uint32), + IPV4_LOOPBACK_ADDRESS, + IPV4_ADDRESS_SIZE)) || + (pRemoteAddress->type == FWP_BYTE_ARRAY16_TYPE && + pRemoteAddress->byteArray16 && + RtlCompareMemory(&(pRemoteAddress->byteArray16->byteArray16), + IPV6_LOOPBACK_ADDRESS, + IPV6_ADDRESS_SIZE))))) + performOutOfBand = TRUE; + } + + if(performOutOfBand) + status = TriggerBasicPacketInjectionOutOfBand(pClassifyValues, + pMetadata, + pNetBufferList, + pClassifyContext, + pFilter, + flowContext, + pClassifyOut, + pInjectionData, + pData); + else + status = TriggerBasicPacketInjectionInline(pClassifyValues, + pMetadata, + pNetBufferList, + pClassifyContext, + pFilter, + flowContext, + pClassifyOut, + &pInjectionData); + } + else + { + pClassifyOut->actionType = FWP_ACTION_PERMIT; + + KrnlHlprInjectionDataDestroy(&pInjectionData); + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " -- Injection previously performed.\n"); + } + + HLPR_BAIL_LABEL: + + NT_ASSERT(status == STATUS_SUCCESS); + + if(status != STATUS_SUCCESS) + { + KrnlHlprInjectionDataDestroy(&pInjectionData); + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! ClassifyBasicPacketInjection() [status: %#x]\n", + status); + } + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + else if(pMetadata->l2Flags & FWPS_L2_INCOMING_FLAG_RECLASSIFY_MULTI_DESTINATION) + KrnlHlprInjectionDataDestroy(&pInjectionData); + +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) + + } + else + pClassifyOut->actionType = FWP_ACTION_PERMIT; + } + +#if DBG + + PrvBasicPacketInjectionCountersIncrementTotalActionResults(pClassifyValues, + pMetadata, + pClassifyOut); + +#endif /// DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- ClassifyBasicPacketInjection() [Layer: %s][FilterID: %#I64x][Action: %#x][Rights: %#x][Absorb: %s]\n", + KrnlHlprFwpsLayerIDToString(pClassifyValues->layerId), + pFilter->filterId, + pClassifyOut->actionType, + pClassifyOut->rights, + (pClassifyOut->flags & FWPS_CLASSIFY_OUT_FLAG_ABSORB) ? "TRUE" : "FALSE"); + + return; +} + +#else + +/** + @classify_function="ClassifyBasicPacketInjection" + + Purpose: Blocks the current NET_BUFFER_LIST and injects a clone back to the stack without + modification.
+
+ Notes: Applies to the following layers:
+ FWPS_LAYER_INBOUND_IPPACKET_V{4/6}
+ FWPS_LAYER_OUTBOUND_IPPACKET_V{4/6}
+ FWPS_LAYER_IPFORWARD_V{4/6}
+ FWPS_LAYER_INBOUND_TRANSPORT_V{4/6}
+ FWPS_LAYER_OUTBOUND_TRANSPORT_V{4/6}
+ FWPS_LAYER_DATAGRAM_DATA_V{4/6}
+ FWPS_LAYER_INBOUND_ICMP_ERROR_V{4/6}
+ FWPS_LAYER_OUTBOUND_ICMP_ERROR_V{4/6}
+ FWPS_LAYER_ALE_AUTH_CONNECT_V{4/6}
+ FWPS_LAYER_ALE_FLOW_ESTABLISHED_V{4/6}
+
+ TCP @ FWPM_LAYER_ALE_AUTH_CONNECT_V{4/6} has no NBL
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF544890.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI ClassifyBasicPacketInjection(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pNetBufferList, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut) +{ + NT_ASSERT(pClassifyValues); + NT_ASSERT(pMetadata); + NT_ASSERT(pFilter); + NT_ASSERT(pClassifyOut); + NT_ASSERT(pFilter->providerContext); + NT_ASSERT(pFilter->providerContext->type == FWPM_GENERAL_CONTEXT); + NT_ASSERT(pFilter->providerContext->dataBuffer); + NT_ASSERT(pFilter->providerContext->dataBuffer->size == sizeof(PC_BASIC_PACKET_INJECTION_DATA)); + NT_ASSERT(pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V6 || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V4 || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V6 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V6 || + pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4 || + pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6); + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> ClassifyBasicPacketInjection() [Layer: %s][FilterID: %#I64x][Rights: %#x]", + KrnlHlprFwpsLayerIDToString(pClassifyValues->layerId), + pFilter->filterId, + pClassifyOut->rights); + +#if DBG + + PrvBasicPacketInjectionCountersIncrement(pClassifyValues, + pMetadata, + &g_bpiTotalClassifies); + +#endif /// DBG + + if(pClassifyOut->rights & FWPS_RIGHT_ACTION_WRITE) + { + /// Packets are not available for TCP @ ALE_AUTH_CONNECT, so skip over as there is nothing to inject + if(pNetBufferList) + { + NTSTATUS status = STATUS_SUCCESS; + FWP_VALUE* pFlags = 0; + INJECTION_DATA* pInjectionData = 0; + + pClassifyOut->actionType = FWP_ACTION_CONTINUE; + + pFlags = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_FLAGS); + if(pFlags && + pFlags->type == FWP_UINT32) + { + /// For IPsec interop, if ALE classification is required, bypass the injection + if(pFlags->uint32 & FWP_CONDITION_FLAG_IS_IPSEC_SECURED && + +#if(NTDDI_VERSION >= NTDDI_WIN6SP1) + + FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_ALE_CLASSIFY_REQUIRED)) + +#else + + pFlags->uint32 & FWP_CONDITION_FLAG_REQUIRES_ALE_CLASSIFY) + +#endif // (NTDDI_VERSION >= NTDDI_WIN6SP1) + + HLPR_BAIL; + + /// Inject the individual fragments, but not the fragment grouping of those fragments + if(pFlags->uint32 & FWP_CONDITION_FLAG_IS_FRAGMENT_GROUP) + HLPR_BAIL; + } + +#pragma warning(push) +#pragma warning(disable: 6014) /// pInjectionData will be freed in completionFn using BasicPacketInjectionCompletionDataDestroy + + status = KrnlHlprInjectionDataCreate(&pInjectionData, + pClassifyValues, + pMetadata, + (NET_BUFFER_LIST*)pNetBufferList, + pFilter); + HLPR_BAIL_ON_FAILURE(status); + +#pragma warning(pop) + + if(pInjectionData->injectionState != FWPS_PACKET_INJECTED_BY_SELF && + pInjectionData->injectionState != FWPS_PACKET_PREVIOUSLY_INJECTED_BY_SELF) + { + BOOLEAN performOutOfBand = TRUE; + FWP_VALUE* pProtocolValue = 0; + PC_BASIC_PACKET_INJECTION_DATA* pData = (PC_BASIC_PACKET_INJECTION_DATA*)pFilter->providerContext->dataBuffer->data; + + pClassifyOut->actionType = FWP_ACTION_BLOCK; + pClassifyOut->flags |= FWPS_CLASSIFY_OUT_FLAG_ABSORB; + pClassifyOut->rights ^= FWPS_RIGHT_ACTION_WRITE; + + if(pFlags && + pFlags->type == FWP_UINT32 && + pFlags->uint32 & FWP_CONDITION_FLAG_IS_IPSEC_SECURED) + pInjectionData->isIPsecSecured = TRUE; + + /// Override the default of performing Out of Band with the user's specified setting ... + if(pData->performInline) + performOutOfBand = FALSE; + + /// ... however, due to TCP's locking semantics, TCP can only be injected Out of Band at any transport layer or equivalent, ... + pProtocolValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_PROTOCOL); + if(pProtocolValue && + pProtocolValue->uint8 == IPPROTO_TCP && + pClassifyValues->layerId > FWPS_LAYER_IPFORWARD_V6_DISCARD) + performOutOfBand = TRUE; + + /// ... and inbound injection of loopback traffic requires us to use Out of Band modification as well due to address lookups. + if(!performOutOfBand && + pFlags && + pFlags->type == FWP_UINT32 && + pFlags->uint32 & FWP_CONDITION_FLAG_IS_LOOPBACK && + pInjectionData->direction == FWP_DIRECTION_INBOUND) + { + FWP_VALUE* pLocalAddress = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_LOCAL_ADDRESS); + FWP_VALUE* pRemoteAddress = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_REMOTE_ADDRESS); + + if((pLocalAddress && + (pLocalAddress->type == FWP_UINT32 && + RtlCompareMemory(&(pLocalAddress->uint32), + IPV4_LOOPBACK_ADDRESS, + IPV4_ADDRESS_SIZE)) || + (pLocalAddress->type == FWP_BYTE_ARRAY16_TYPE && + RtlCompareMemory(&(pLocalAddress->byteArray16->byteArray16), + IPV6_LOOPBACK_ADDRESS, + IPV6_ADDRESS_SIZE))) || + (pRemoteAddress && + (pRemoteAddress->type == FWP_UINT32 && + RtlCompareMemory(&(pRemoteAddress->uint32), + IPV4_LOOPBACK_ADDRESS, + IPV4_ADDRESS_SIZE)) || + (pRemoteAddress->type == FWP_BYTE_ARRAY16_TYPE && + RtlCompareMemory(&(pRemoteAddress->byteArray16->byteArray16), + IPV6_LOOPBACK_ADDRESS, + IPV6_ADDRESS_SIZE)))) + performOutOfBand = TRUE; + } + + if(performOutOfBand) + status = TriggerBasicPacketInjectionOutOfBand(pClassifyValues, + pMetadata, + pNetBufferList, + 0, + pFilter, + flowContext, + pClassifyOut, + pInjectionData, + pData); + else + status = TriggerBasicPacketInjectionInline(pClassifyValues, + pMetadata, + pNetBufferList, + 0, + pFilter, + flowContext, + pClassifyOut, + &pInjectionData); + } + else + { + pClassifyOut->actionType = FWP_ACTION_PERMIT; + + KrnlHlprInjectionDataDestroy(&pInjectionData); + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " -- Injection previously performed.\n"); + } + + HLPR_BAIL_LABEL: + + NT_ASSERT(status == STATUS_SUCCESS); + + if(status != STATUS_SUCCESS) + { + KrnlHlprInjectionDataDestroy(&pInjectionData); + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! ClassifyBasicPacketInjection() [status: %#x]\n", + status); + } + } + else + pClassifyOut->actionType = FWP_ACTION_PERMIT; + } + +#if DBG + + PrvBasicPacketInjectionCountersIncrementTotalActionResults(pClassifyValues, + pMetadata, + pClassifyOut); + +#endif /// DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- ClassifyBasicPacketInjection() [Layer: %s][FilterID: %#I64x][Action: %#x][Rights: %#x][Absorb: %s]\n", + KrnlHlprFwpsLayerIDToString(pClassifyValues->layerId), + pFilter->filterId, + pClassifyOut->actionType, + pClassifyOut->rights, + (pClassifyOut->flags & FWPS_CLASSIFY_OUT_FLAG_ABSORB) ? "TRUE" : "FALSE"); + + return; +} + +#endif // (NTDDI_VERSION >= NTDDI_WIN7) diff --git a/network/trans/WFPSampler/sys/ClassifyFunctions_BasicPacketInjectionCallouts.h b/network/trans/WFPSampler/sys/ClassifyFunctions_BasicPacketInjectionCallouts.h new file mode 100644 index 000000000..103a0c07c --- /dev/null +++ b/network/trans/WFPSampler/sys/ClassifyFunctions_BasicPacketInjectionCallouts.h @@ -0,0 +1,75 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// ClassifyFunctions_BasicPacketInjectionCallouts.h +// +// Abstract: +// This module contains prototypes for WFP Classify functions that inject packets back into the +// data path using the clone / block / inject method. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Enhance function declaration for IntelliSense +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef CLASSIFY_BASIC_PACKET_INJECTION_H +#define CLASSIFY_BASIC_PACKET_INJECTION_H + +#if DBG + +extern INJECTION_COUNTERS g_bpiTotalClassifies; +extern INJECTION_COUNTERS g_bpiTotalBlockedAndAbsorbed; +extern INJECTION_COUNTERS g_bpiTotalPermitted; +extern INJECTION_COUNTERS g_bpiTotalSuccessfulInjectionCalls; +extern INJECTION_COUNTERS g_bpiTotalFailedInjectionCalls; +extern INJECTION_COUNTERS g_bpiOutstandingNBLClones; + +VOID BasicPacketInjectionCountersIncrement(_In_ HANDLE injectionHandle, + _Inout_ INJECTION_COUNTERS* pCounters); + +#endif /// DBG + +_IRQL_requires_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Function_class_(KDEFERRED_ROUTINE) +VOID BasicPacketInjectionDeferredProcedureCall(_In_ KDPC* pDPC, + _In_opt_ PVOID pContext, + _In_opt_ PVOID pDPCData, + _In_opt_ PVOID pArg2); + +#if(NTDDI_VERSION >= NTDDI_WIN7) + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI ClassifyBasicPacketInjection(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pLayerData, + _In_opt_ const VOID* pClassifyContext, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut); + +#else + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI ClassifyBasicPacketInjection(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pLayerData, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut); + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + +#endif /// CLASSIFY_BASIC_PACKET_INJECTION_H diff --git a/network/trans/WFPSampler/sys/ClassifyFunctions_BasicPacketModificationCallouts.cpp b/network/trans/WFPSampler/sys/ClassifyFunctions_BasicPacketModificationCallouts.cpp new file mode 100644 index 000000000..563672c68 --- /dev/null +++ b/network/trans/WFPSampler/sys/ClassifyFunctions_BasicPacketModificationCallouts.cpp @@ -0,0 +1,6604 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// ClassifyFunctions_BasicPacketModificationCallouts.cpp +// +// Abstract: +// This module contains WFP Classify functions for modifying and injecting packets back into +// the data path using the clone / block / inject method. +// +// Naming Convention: +// +// +// +// i.e. +// ClassifyBasicPacketModification +// +// +// Classify - Function is an FWPS_CALLOUT_CLASSIFY_FN +// +// BasicPacketModification - Function demonstrates the clone / block / modify / inject +// model. +// +// +// +// i.e. +// TriggerBasicPacketModificationOutOfBand +// +// +// { +// - +// Trigger - Initiates the desired scenario. +// Perform - Executes the desired scenario. +// } +// +// BasicPacketModification - Function demonstrates the clone / block / modify / inject +// model. +// +// DeferredProcedureCall - DPC routine for Out of Band injection which dispatches the +// proper Perform Function. +// WorkItemRoutine - WorkItem Routine for Out of Band Injection which dispatches +// the proper Perform Function. +// AtInboundMACFrame - Function operates on: +// FWPM_LAYER_INBOUND_MAC_FRAME_ETHERNET, and +// FWPM_LAYER_INBOUND_MAC_NATIVE. +// AtOutboundMACFrame - Function operates on: +// FWPM_LAYER_OUTBOUND_MAC_FRAME_ETHERNET, and +// FWPM_LAYER_OUTBOUND_MAC_NATIVE. +// AtEgressVSwitchEthernet - Function operates on: +// FWPM_LAYER_EGRESS_VSWITCH_ETHERNET. +// AtIngressVSwitchEthernet - Function operates on: +// FWPM_LAYER_INGRESS_VSWITCH_ETHERNET. +// AtInboundNetwork - Function operates on: +// FWPM_LAYER_INBOUND_IPPACKET_V{4/6} +// AtOutboundNetwork - Function operates on: +// FWPM_LAYER_OUTBOUND_IPPACKET_V{4/6} +// AtForward - Function operates on: +// FWPM_LAYER_IPFORWARD_V{4/6} +// AtInboundTransport - Function operates on: +// FWPM_LAYER_INBOUND_TRANSPORT_V{4/6}, +// FWPM_LAYER_INBOUND_ICMP_ERROR_V{4/6}, +// FWPM_LAYER_DATAGRAM_DATA_V{4/6}, +// FWPM_LAYER_STREAM_PACKET_V{4/6}, and +// FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V{4/6} +// FWPM_LAYER_ALE_FLOW_ESTABLISHED_V{4/6} +// AtOutboundTransport - Function operates on: +// FWPM_LAYER_OUTBOUND_TRANSPORT_V{4/6}, +// FWPM_LAYER_OUTBOUND_ICMP_ERROR_V{4/6}, +// FWPM_LAYER_DATAGRAM_DATA_V{4/6}, +// FWPM_LAYER_STREAM_PACKET_V{4/6}, and +// FWPM_LAYER_ALE_AUTH_CONNECT_V{4/6} +// FWPM_LAYER_ALE_FLOW_ESTABLISHED_V{4/6} +// +// Private Functions: +// BasicPacketModificationDeferredProcedureCall(), +// BasicPacketModificationWorkItemRoutine(), +// PerformBasicPacketModificationAtEgressVSwitchEthernet(), +// PerformBasicPacketModificationAtForward(), +// PerformBasicPacketModificationAtInboundMACFrame(), +// PerformBasicPacketModificationAtInboundNetwork(), +// PerformBasicPacketModificationAtInboundTransport(), +// PerformBasicPacketModificationAtIngressVSwitchEthernet(), +// PerformBasicPacketModificationAtOutboundMACFrame(), +// PerformBasicPacketModificationAtOutboundNetwork(), +// PerformBasicPacketModificationAtOutboundTransport(), +// TriggerBasicPacketModificationInline(), +// TriggerBasicPacketModificationOutOfBand(), +// +// Public Functions: +// ClassifyBasicPacketModification(), +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Enhance function declaration for IntelliSense, enhance +// traces, fix weakhost injection, fix expected +// offsets,fix copy / paste issues with modifying dst +// port, and add support for multiple injectors and +// controlData. +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "Framework_WFPSamplerCalloutDriver.h" /// . +#include "ClassifyFunctions_BasicPacketModificationCallouts.tmh" /// $(OBJ_PATH)\$(O)\ + +#if(NTDDI_VERSION >= NTDDI_WIN8) + +/** + @private_function="PerformBasicPacketModificationAtInboundMACFrame" + + Purpose: Clones the NET_BUFFER_LIST, modifies it with data from the associated context and + injects the clone back to the stack's inbound path from the incoming MAC Layers + using FwpsInjectMacReceiveAsync().
+
+ Notes: Applies to the following inbound layers:
+ FWPM_LAYER_INBOUND_MAC_FRAME_ETHERNET
+ FWPM_LAYER_INBOUND_MAC_FRAME_NATIVE
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/HH439588.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF546324.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +NTSTATUS PerformBasicPacketModificationAtInboundMACFrame(_In_ CLASSIFY_DATA** ppClassifyData, + _In_ INJECTION_DATA** ppInjectionData, + _In_ PC_BASIC_PACKET_MODIFICATION_DATA* pModificationData, + _In_ BOOLEAN isInline = FALSE) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PerformBasicPacketModificationAtInboundMACFrame()\n"); + +#endif /// DBG + + NT_ASSERT(ppClassifyData); + NT_ASSERT(ppInjectionData); + NT_ASSERT(pModificationData); + NT_ASSERT(*ppClassifyData); + NT_ASSERT(*ppInjectionData); + + NTSTATUS status = STATUS_SUCCESS; + FWPS_INCOMING_VALUES* pClassifyValues = (FWPS_INCOMING_VALUES*)(*ppClassifyData)->pClassifyValues; + FWPS_INCOMING_METADATA_VALUES* pMetadata = (FWPS_INCOMING_METADATA_VALUES*)(*ppClassifyData)->pMetadataValues; + IF_INDEX interfaceIndex = 0; + NDIS_PORT_NUMBER ndisPort = 0; + NET_BUFFER_LIST* pNetBufferList = 0; + BASIC_PACKET_MODIFICATION_COMPLETION_DATA* pCompletionData = 0; + UINT32 bytesRetreated = 0; + FWP_VALUE* pInterfaceIndex = 0; + FWP_VALUE* pNDISPort = 0; + +#if DBG + + KIRQL irql = KeGetCurrentIrql(); + +#endif /// DBG + +#pragma warning(push) +#pragma warning(disable: 6014) /// pCompletionData will be freed in completionFn using BasicPacketModificationCompletionDataDestroy + + HLPR_NEW(pCompletionData, + BASIC_PACKET_MODIFICATION_COMPLETION_DATA, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pCompletionData, + status); + +#pragma warning(pop) + + KeInitializeSpinLock(&(pCompletionData->spinLock)); + + pCompletionData->performedInline = isInline; + pCompletionData->pClassifyData = *ppClassifyData; + pCompletionData->pInjectionData = *ppInjectionData; + + /// Responsibility for freeing this memory has been transferred to the pCompletionData + *ppClassifyData = 0; + + *ppInjectionData = 0; + + pInterfaceIndex = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_INTERFACE_INDEX); + if(pInterfaceIndex && + pInterfaceIndex->type == FWP_UINT32) + interfaceIndex = (IF_INDEX)pInterfaceIndex->uint32; + + pNDISPort = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_NDIS_PORT); + if(pNDISPort && + pNDISPort->type == FWP_UINT32) + ndisPort = (NDIS_PORT_NUMBER)pNDISPort->uint32; + + /// If NATIVE, initial offset is at the MAC Header ... + if(pClassifyValues->layerId != FWPS_LAYER_INBOUND_MAC_FRAME_NATIVE && + FWPS_IS_L2_METADATA_FIELD_PRESENT(pMetadata, + FWPS_L2_METADATA_FIELD_ETHERNET_MAC_HEADER_SIZE)) + bytesRetreated = pMetadata->ethernetMacHeaderSize; + + if(bytesRetreated) + { + /// ... otherwise the offset is at the IP Header, so retreat the size of the MAC Header ... + status = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket), + bytesRetreated, + 0, + 0); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtInboundMACFrame: NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + + HLPR_BAIL; + } + } + + /// ... clone the entire NET_BUFFER_LIST ... + status = FwpsAllocateCloneNetBufferList((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket, + g_pNDISPoolData->nblPoolHandle, + g_pNDISPoolData->nbPoolHandle, + 0, + &pNetBufferList); + + if(bytesRetreated) + { + /// ... and advance the offset back to the original position. + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket), + bytesRetreated, + FALSE, + 0); + } + + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtInboundMACFrame: FwpsAllocateCloneNetBufferList() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + if(pModificationData->flags) + { + /// Various checks and balances must be performed to modify the IP and Transport headers at this modification point. + /// Parsing of the headers will need to occur, as well as spot checking to verify everything is as it should be. + /// Additionally, checksum routines will need to be written to recalculate checksums for some of the headers. + /// The following block of code is to get you started with modifying the headers with info not readily available + /// (i.e. header parsing has not occurred so there is no relevant classifiable data nor metadata present). +/* + if(pModificationData->flags & PCPMDF_MODIFY_TRANSPORT_HEADER) + { + UINT32 tmpStatus = STATUS_SUCCESS; + IPPROTO protocol = IPPROTO_MAX; + + /// The clone is at the Ethernet Header, so advance by the size of the Ethernet Header... + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList), + ethernetHeaderSize, + FALSE, + 0); + + protocol = KrnlHlprIPHeaderGetProtocolField(pNetBufferList, + pCompletionData->pInjectionData->addressFamily); + + /// No Transport Modification if IPsec encrypted + if(protocol != IPPROTO_ESP && + protocol != IPPROTO_AH) + { + /// ... advance by the size of the IP Header. + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList), + ipHeaderSize, + FALSE, + 0); + + switch(protocol) + { + case IPPROTO_ICMP: + { + UINT32 icmpHeaderSize = ICMP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + icmpHeaderSize = pMetadata->transportHeaderSize; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_TYPE) + { + FWP_VALUE0 icmpType; + + icmpType.type = FWP_UINT8; + icmpType.uint8 = (UINT8)ntohs(pModificationData->transportData.sourcePort); + + status = KrnlHlprICMPv4HeaderModifyType(&icmpType, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_CODE) + { + FWP_VALUE0 icmpCode; + + icmpCode.type = FWP_UINT8; + icmpCode.uint8 = (UINT8)ntohs(pModificationData->transportData.destinationPort); + + status = KrnlHlprICMPv4HeaderModifyCode(&icmpCode, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + break; + } + case IPPROTO_TCP: + { + UINT32 tcpHeaderSize = TCP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + tcpHeaderSize = pMetadata->transportHeaderSize; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_SOURCE_PORT) + { + FWP_VALUE0 srcPort; + + srcPort.type = FWP_UINT16; + srcPort.uint16 = pModificationData->transportData.sourcePort; + + status = KrnlHlprTCPHeaderModifySourcePort(&srcPort, + pNetBufferList, + tcpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_DESTINATION_PORT) + { + FWP_VALUE0 dstPort; + + dstPort.type = FWP_UINT16; + dstPort.uint16 = pModificationData->transportData.destinationPort; + + status = KrnlHlprTCPHeaderModifyDestinationPort(&dstPort, + pNetBufferList, + tcpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + break; + } + case IPPROTO_UDP: + { + UINT32 udpHeaderSize = UDP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + udpHeaderSize = pMetadata->transportHeaderSize; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_SOURCE_PORT) + { + FWP_VALUE0 srcPort; + + srcPort.type = FWP_UINT16; + srcPort.uint16 = pModificationData->transportData.sourcePort; + + status = KrnlHlprUDPHeaderModifySourcePort(&srcPort, + pNetBufferList, + udpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_DESTINATION_PORT) + { + FWP_VALUE0 dstPort; + + dstPort.type = FWP_UINT16; + dstPort.uint16 = pModificationData->transportData.destinationPort; + + status = KrnlHlprUDPHeaderModifyDestinationPort(&dstPort, + pNetBufferList, + udpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + break; + } + case IPPROTO_ICMPV6: + { + UINT32 icmpHeaderSize = ICMP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + icmpHeaderSize = pMetadata->transportHeaderSize; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_TYPE) + { + FWP_VALUE0 icmpType; + + icmpType.type = FWP_UINT8; + icmpType.uint8 = (UINT8)ntohs(pModificationData->transportData.sourcePort); + + status = KrnlHlprICMPv6HeaderModifyType(&icmpType, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_CODE) + { + FWP_VALUE0 icmpCode; + + icmpCode.type = FWP_UINT8; + icmpCode.uint8 = (UINT8)ntohs(pModificationData->transportData.destinationPort); + + status = KrnlHlprICMPv6HeaderModifyCode(&icmpCode, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + break; + } + } + + /// ToDo: Recalculate the Transport Checksum Here + + HLPR_BAIL_LABEL_2: + + /// return the data offset to the beginning of the IP Header + tmpStatus = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList), + ipHeaderSize, + 0, + 0); + if(tmpStatus != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtInboundMACFrame: NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + HLPR_BAIL_ON_FAILURE(status); + } + + /// return the data offset to the beginning of the Ethernet Header + tmpStatus = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList), + ipHeaderSize, + 0, + 0); + if(tmpStatus != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtInboundMACFrame: NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + + HLPR_BAIL; + } + } + + if(pModificationData->flags & PCPMDF_MODIFY_IP_HEADER) + { + /// The clone is at the Ethernet Header, so advance by the size of the Ethernet Header... + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList), + ethernetHeaderSize, + FALSE, + 0); + + if(pModificationData->ipData.flags & PCPMDF_MODIFY_IP_HEADER_SOURCE_ADDRESS) + { + FWP_VALUE value; + + RtlZeroMemory(&value, + sizeof(FWP_VALUE)); + + if(pCompletionData->pInjectionData->addressFamily == AF_INET) + { + value.type = FWP_UINT32; + + RtlCopyMemory(&(value.uint32), + pModificationData->ipData.sourceAddress.pIPv4, + IPV4_ADDRESS_SIZE); + } + else + { + HLPR_NEW(value.byteArray16, + FWP_BYTE_ARRAY16, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE_WITH_LABEL(value.byteArray16, + status, + HLPR_BAIL_LABEL_3); + + value.type = FWP_BYTE_ARRAY16_TYPE; + + RtlCopyMemory(value.byteArray16->byteArray16, + pModificationData->ipData.sourceAddress.pIPv6, + IPV6_ADDRESS_SIZE); + } + + status = KrnlHlprIPHeaderModifySourceAddress(&value, + pNetBufferList, + TRUE); + + KrnlHlprFwpValuePurgeLocalCopy(&value); + + HLPR_BAIL_ON_FAILURE_WITH_LABEL(status, + HLPR_BAIL_LABEL_3); + } + + if(pModificationData->ipData.flags & PCPMDF_MODIFY_IP_HEADER_DESTINATION_ADDRESS) + { + FWP_VALUE value; + + RtlZeroMemory(&value, + sizeof(FWP_VALUE)); + + if(pCompletionData->pInjectionData->addressFamily == AF_INET) + { + value.type = FWP_UINT32; + + RtlCopyMemory(&(value.uint32), + pModificationData->ipData.destinationAddress.pIPv4, + IPV4_ADDRESS_SIZE); + } + else + { + HLPR_NEW(value.byteArray16, + FWP_BYTE_ARRAY16, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE_WITH_LABEL(value.byteArray16, + status, + HLPR_BAIL_LABEL_3); + + value.type = FWP_BYTE_ARRAY16_TYPE; + + RtlCopyMemory(value.byteArray16->byteArray16, + pModificationData->ipData.destinationAddress.pIPv6, + IPV6_ADDRESS_SIZE); + } + + status = KrnlHlprIPHeaderModifyDestinationAddress(&value, + pNetBufferList, + TRUE); + + KrnlHlprFwpValuePurgeLocalCopy(&value); + + HLPR_BAIL_ON_FAILURE_WITH_LABEL(status, + HLPR_BAIL_LABEL_3); + } + + HLPR_BAIL_LABEL_3: + + /// return the data offset to the beginning of the Ethernet Header + tmpStatus = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList), + ipHeaderSize, + 0, + 0); + if(tmpStatus != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtInboundMACFrame: NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + + HLPR_BAIL; + } + } +*/ + if(pModificationData->flags & PCPMDF_MODIFY_MAC_HEADER) + { + if(pModificationData->macData.flags & PCPMDF_MODIFY_MAC_HEADER_SOURCE_ADDRESS) + { + FWP_VALUE value; + + RtlZeroMemory(&value, + sizeof(FWP_VALUE)); + + HLPR_NEW(value.byteArray6, + FWP_BYTE_ARRAY6, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(value.byteArray6, + status); + + value.type = FWP_BYTE_ARRAY6_TYPE; + + RtlCopyMemory(value.byteArray6->byteArray6, + pModificationData->macData.pSourceMACAddress, + ETHERNET_ADDRESS_SIZE); + + status = KrnlHlprMACHeaderModifySourceAddress(&value, + pNetBufferList); + + KrnlHlprFwpValuePurgeLocalCopy(&value); + + HLPR_BAIL_ON_FAILURE(status); + } + + if(pModificationData->macData.flags & PCPMDF_MODIFY_MAC_HEADER_DESTINATION_ADDRESS) + { + FWP_VALUE value; + + RtlZeroMemory(&value, + sizeof(FWP_VALUE)); + +#pragma warning(push) +#pragma warning(disable: 6014) /// value.byteArray6 will be freed in with call to KrnlHlprFwpValuePurgeLocalCopy + + HLPR_NEW(value.byteArray6, + FWP_BYTE_ARRAY6, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(value.byteArray6, + status); + +#pragma warning(pop) + + value.type = FWP_BYTE_ARRAY6_TYPE; + + RtlCopyMemory(value.byteArray6->byteArray6, + pModificationData->macData.pDestinationMACAddress, + ETHERNET_ADDRESS_SIZE); + + status = KrnlHlprMACHeaderModifyDestinationAddress(&value, + pNetBufferList); + + KrnlHlprFwpValuePurgeLocalCopy(&value); + + HLPR_BAIL_ON_FAILURE(status); + } + } + } + + pCompletionData->refCount = KrnlHlprNBLGetRequiredRefCount(pNetBufferList, + TRUE); + + status = FwpsInjectMacReceiveAsync(pCompletionData->pInjectionData->injectionHandle, + pCompletionData->pInjectionData->injectionContext, + 0, + pClassifyValues->layerId, + interfaceIndex, + ndisPort, + pNetBufferList, + CompleteBasicPacketModification, + pCompletionData); + + NT_ASSERT(irql == KeGetCurrentIrql()); + + if(status != STATUS_SUCCESS) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtInboundMACFrame: FwpsInjectMacReceiveAsync() [status: %#x]\n", + status); + + HLPR_BAIL_LABEL: + + NT_ASSERT(status == STATUS_SUCCESS); + + if(status != STATUS_SUCCESS) + { + if(pNetBufferList) + { + FwpsFreeCloneNetBufferList(pNetBufferList, + 0); + + pNetBufferList = 0; + } + + if(pCompletionData) + BasicPacketModificationCompletionDataDestroy(&pCompletionData, + TRUE); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PerformBasicPacketModificationAtInboundMACFrame() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @private_function="PerformBasicPacketInjectionAtOutboundMACFrame" + + Purpose: Clones the NET_BUFFER_LIST and injects the clone back to the stack from the + outgoing MAC Layers using FwpsInjectMacSendAsync().
+
+ Notes: Applies to the following inbound layers:
+ FWPM_LAYER_OUTBOUND_MAC_FRAME_ETHERNET
+ FWPM_LAYER_OUTBOUND_MAC_FRAME_NATIVE
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/HH439593.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF546324.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +NTSTATUS PerformBasicPacketModificationAtOutboundMACFrame(_In_ CLASSIFY_DATA** ppClassifyData, + _In_ INJECTION_DATA** ppInjectionData, + _In_ PC_BASIC_PACKET_MODIFICATION_DATA* pModificationData, + _In_ BOOLEAN isInline = FALSE) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PerformBasicPacketModificationAtOutboundMACFrame()\n"); + +#endif /// DBG + + NT_ASSERT(ppClassifyData); + NT_ASSERT(ppInjectionData); + NT_ASSERT(pModificationData); + NT_ASSERT(*ppClassifyData); + NT_ASSERT(*ppInjectionData); + + NTSTATUS status = STATUS_SUCCESS; + FWPS_INCOMING_VALUES* pClassifyValues = (FWPS_INCOMING_VALUES*)(*ppClassifyData)->pClassifyValues; + IF_INDEX interfaceIndex = 0; + NDIS_PORT_NUMBER ndisPort = 0; + NET_BUFFER_LIST* pNetBufferList = 0; + BASIC_PACKET_MODIFICATION_COMPLETION_DATA* pCompletionData = 0; + FWP_VALUE* pInterfaceIndex = 0; + FWP_VALUE* pNDISPort = 0; + +#if DBG + + KIRQL irql = KeGetCurrentIrql(); + +#endif /// DBG + +#pragma warning(push) +#pragma warning(disable: 6014) /// pCompletionData will be freed in completionFn using BasicPacketModificationCompletionDataDestroy + + HLPR_NEW(pCompletionData, + BASIC_PACKET_MODIFICATION_COMPLETION_DATA, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pCompletionData, + status); + +#pragma warning(pop) + + KeInitializeSpinLock(&(pCompletionData->spinLock)); + + pCompletionData->performedInline = isInline; + pCompletionData->pClassifyData = *ppClassifyData; + pCompletionData->pInjectionData = *ppInjectionData; + + /// Responsibility for freeing this memory has been transferred to the pCompletionData + *ppClassifyData = 0; + + *ppInjectionData = 0; + + pInterfaceIndex = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_INTERFACE_INDEX); + if(pInterfaceIndex && + pInterfaceIndex->type == FWP_UINT32) + interfaceIndex = (IF_INDEX)pInterfaceIndex->uint32; + + pNDISPort = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_NDIS_PORT); + if(pNDISPort && + pNDISPort->type == FWP_UINT32) + ndisPort = (NDIS_PORT_NUMBER)pNDISPort->uint32; + + /// Initial offset is at the MAC Header, so just clone the entire NET_BUFFER_LIST. + status = FwpsAllocateCloneNetBufferList((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket, + g_pNDISPoolData->nblPoolHandle, + g_pNDISPoolData->nbPoolHandle, + 0, + &pNetBufferList); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtOutboundMACFrame: FwpsAllocateCloneNetBufferList() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + pCompletionData->refCount = KrnlHlprNBLGetRequiredRefCount(pNetBufferList, + TRUE); + + if(pModificationData->flags) + { + /// Various checks and balances must be performed to modify the IP and Transport headers at this modification point. + /// Parsing of the headers will need to occur, as well as spot checking to verify everything is as it should be. + /// Additionally, checksum routines will need to be written to recalculate checksums for some of the headers. + /// The following block of code is to get you started with modifying the headers with info not readily available. + /// (i.e. header parsing has not occurred so there is no relevant classifiable data nor metadata present). +/* + if(pModificationData->flags & PCPMDF_MODIFY_TRANSPORT_HEADER) + { + UINT32 tmpStatus = STATUS_SUCCESS; + IPPROTO protocol = IPPROTO_MAX; + + /// The clone is at the Ethernet Header, so advance by the size of the Ethernet Header... + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList), + ethernetHeaderSize, + FALSE, + 0); + + protocol = KrnlHlprIPHeaderGetProtocolField(pNetBufferList, + pCompletionData->pInjectionData->addressFamily); + + /// No Transport Modification if IPsec encrypted + if(protocol != IPPROTO_ESP && + protocol != IPPROTO_AH) + { + /// ... advance by the size of the IP Header. + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList), + ipHeaderSize, + FALSE, + 0); + + switch(protocol) + { + case IPPROTO_ICMP: + { + UINT32 icmpHeaderSize = ICMP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + icmpHeaderSize = pMetadata->transportHeaderSize; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_TYPE) + { + FWP_VALUE0 icmpType; + + icmpType.type = FWP_UINT8; + icmpType.uint8 = (UINT8)ntohs(pModificationData->transportData.sourcePort); + + status = KrnlHlprICMPv4HeaderModifyType(&icmpType, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_CODE) + { + FWP_VALUE0 icmpCode; + + icmpCode.type = FWP_UINT8; + icmpCode.uint8 = (UINT8)ntohs(pModificationData->transportData.destinationPort); + + status = KrnlHlprICMPv4HeaderModifyCode(&icmpCode, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + break; + } + case IPPROTO_TCP: + { + UINT32 tcpHeaderSize = TCP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + tcpHeaderSize = pMetadata->transportHeaderSize; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_SOURCE_PORT) + { + FWP_VALUE0 srcPort; + + srcPort.type = FWP_UINT16; + srcPort.uint16 = pModificationData->transportData.sourcePort; + + status = KrnlHlprTCPHeaderModifySourcePort(&srcPort, + pNetBufferList, + tcpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_DESTINATION_PORT) + { + FWP_VALUE0 dstPort; + + dstPort.type = FWP_UINT16; + dstPort.uint16 = pModificationData->transportData.destinationPort; + + status = KrnlHlprTCPHeaderModifyDestinationPort(&dstPort, + pNetBufferList, + tcpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + break; + } + case IPPROTO_UDP: + { + UINT32 udpHeaderSize = UDP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + udpHeaderSize = pMetadata->transportHeaderSize; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_SOURCE_PORT) + { + FWP_VALUE0 srcPort; + + srcPort.type = FWP_UINT16; + srcPort.uint16 = pModificationData->transportData.sourcePort; + + status = KrnlHlprUDPHeaderModifySourcePort(&srcPort, + pNetBufferList, + udpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_DESTINATION_PORT) + { + FWP_VALUE0 dstPort; + + dstPort.type = FWP_UINT16; + dstPort.uint16 = pModificationData->transportData.destinationPort; + + status = KrnlHlprUDPHeaderModifyDestinationPort(&dstPort, + pNetBufferList, + udpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + break; + } + case IPPROTO_ICMPV6: + { + UINT32 icmpHeaderSize = ICMP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + icmpHeaderSize = pMetadata->transportHeaderSize; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_TYPE) + { + FWP_VALUE0 icmpType; + + icmpType.type = FWP_UINT8; + icmpType.uint8 = (UINT8)ntohs(pModificationData->transportData.sourcePort); + + status = KrnlHlprICMPv6HeaderModifyType(&icmpType, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_CODE) + { + FWP_VALUE0 icmpCode; + + icmpCode.type = FWP_UINT8; + icmpCode.uint8 = (UINT8)ntohs(pModificationData->transportData.destinationPort); + + status = KrnlHlprICMPv6HeaderModifyCode(&icmpCode, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + break; + } + } + + /// ToDo: Recalculate the Transport Checksum Here + + HLPR_BAIL_LABEL_2: + + /// return the data offset to the beginning of the IP Header + tmpStatus = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList), + ipHeaderSize, + 0, + 0); + if(tmpStatus != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtOutboundMacFrame: NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + HLPR_BAIL_ON_FAILURE(status); + } + + /// return the data offset to the beginning of the Ethernet Header + tmpStatus = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList), + ipHeaderSize, + 0, + 0); + if(tmpStatus != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtOutboundMACFrame: NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + + HLPR_BAIL; + } + } + + if(pModificationData->flags & PCPMDF_MODIFY_IP_HEADER) + { + /// The clone is at the Ethernet Header, so advance by the size of the Ethernet Header... + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList), + ethernetHeaderSize, + FALSE, + 0); + + if(pModificationData->ipData.flags & PCPMDF_MODIFY_IP_HEADER_SOURCE_ADDRESS) + { + FWP_VALUE value; + + RtlZeroMemory(&value, + sizeof(FWP_VALUE)); + + if(pCompletionData->pInjectionData->addressFamily == AF_INET) + { + value.type = FWP_UINT32; + + RtlCopyMemory(&(value.uint32), + pModificationData->ipData.sourceAddress.pIPv4, + IPV4_ADDRESS_SIZE); + } + else + { + HLPR_NEW(value.byteArray16, + FWP_BYTE_ARRAY16, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE_WITH_LABEL(value.byteArray16, + status, + HLPR_BAIL_LABEL_3); + + value.type = FWP_BYTE_ARRAY16_TYPE; + + RtlCopyMemory(value.byteArray16->byteArray16, + pModificationData->ipData.sourceAddress.pIPv6, + IPV6_ADDRESS_SIZE); + } + + status = KrnlHlprIPHeaderModifySourceAddress(&value, + pNetBufferList, + TRUE); + + KrnlHlprFwpValuePurgeLocalCopy(&value); + + HLPR_BAIL_ON_FAILURE_WITH_LABEL(status, + HLPR_BAIL_LABEL_3); + } + + if(pModificationData->ipData.flags & PCPMDF_MODIFY_IP_HEADER_DESTINATION_ADDRESS) + { + FWP_VALUE value; + + RtlZeroMemory(&value, + sizeof(FWP_VALUE)); + + if(pCompletionData->pInjectionData->addressFamily == AF_INET) + { + value.type = FWP_UINT32; + + RtlCopyMemory(&(value.uint32), + pModificationData->ipData.destinationAddress.pIPv4, + IPV4_ADDRESS_SIZE); + } + else + { + HLPR_NEW(value.byteArray16, + FWP_BYTE_ARRAY16, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE_WITH_LABEL(value.byteArray16, + status, + HLPR_BAIL_LABEL_3); + + value.type = FWP_BYTE_ARRAY16_TYPE; + + RtlCopyMemory(value.byteArray16->byteArray16, + pModificationData->ipData.destinationAddress.pIPv6, + IPV6_ADDRESS_SIZE); + } + + status = KrnlHlprIPHeaderModifyDestinationAddress(&value, + pNetBufferList, + TRUE); + + KrnlHlprFwpValuePurgeLocalCopy(&value); + + HLPR_BAIL_ON_FAILURE_WITH_LABEL(status, + HLPR_BAIL_LABEL_3); + } + + HLPR_BAIL_LABEL_3: + + /// return the data offset to the beginning of the Ethernet Header + tmpStatus = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList), + ipHeaderSize, + 0, + 0); + if(tmpStatus != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtOutboundMACFrame: NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + + HLPR_BAIL; + } + } +*/ + if(pModificationData->flags & PCPMDF_MODIFY_MAC_HEADER) + { + if(pModificationData->macData.flags & PCPMDF_MODIFY_MAC_HEADER_SOURCE_ADDRESS) + { + FWP_VALUE value; + + RtlZeroMemory(&value, + sizeof(FWP_VALUE)); + + HLPR_NEW(value.byteArray6, + FWP_BYTE_ARRAY6, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(value.byteArray6, + status); + + value.type = FWP_BYTE_ARRAY6_TYPE; + + RtlCopyMemory(value.byteArray6->byteArray6, + pModificationData->macData.pSourceMACAddress, + ETHERNET_ADDRESS_SIZE); + + status = KrnlHlprMACHeaderModifySourceAddress(&value, + pNetBufferList); + + KrnlHlprFwpValuePurgeLocalCopy(&value); + + HLPR_BAIL_ON_FAILURE(status); + } + + if(pModificationData->macData.flags & PCPMDF_MODIFY_MAC_HEADER_DESTINATION_ADDRESS) + { + FWP_VALUE value; + + RtlZeroMemory(&value, + sizeof(FWP_VALUE)); + +#pragma warning(push) +#pragma warning(disable: 6014) /// value.byteArray6 will be freed in with call to KrnlHlprFwpValuePurgeLocalCopy + + HLPR_NEW(value.byteArray6, + FWP_BYTE_ARRAY6, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(value.byteArray6, + status); + +#pragma warning(pop) + + value.type = FWP_BYTE_ARRAY6_TYPE; + + RtlCopyMemory(value.byteArray6->byteArray6, + pModificationData->macData.pDestinationMACAddress, + ETHERNET_ADDRESS_SIZE); + + status = KrnlHlprMACHeaderModifyDestinationAddress(&value, + pNetBufferList); + + KrnlHlprFwpValuePurgeLocalCopy(&value); + + HLPR_BAIL_ON_FAILURE(status); + } + } + } + + status = FwpsInjectMacSendAsync(pCompletionData->pInjectionData->injectionHandle, + pCompletionData->pInjectionData->injectionContext, + 0, + pClassifyValues->layerId, + interfaceIndex, + ndisPort, + pNetBufferList, + CompleteBasicPacketModification, + pCompletionData); + + NT_ASSERT(irql == KeGetCurrentIrql()); + + if(status != STATUS_SUCCESS) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtOutboundMACFrame: FwpsInjectMacSendAsync() [status: %#x]\n", + status); + + HLPR_BAIL_LABEL: + + NT_ASSERT(status == STATUS_SUCCESS); + + if(status != STATUS_SUCCESS) + { + if(pNetBufferList) + { + FwpsFreeCloneNetBufferList(pNetBufferList, + 0); + + pNetBufferList = 0; + } + + if(pCompletionData) + BasicPacketModificationCompletionDataDestroy(&pCompletionData, + TRUE); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PerformBasicPacketModificationAtOutboundMACFrame() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @private_function="PerformBasicPacketModificationAtIngressVSwitchEthernet" + + Purpose: Clones the NET_BUFFER_LIST and injects the clone back to the virtual switch's + ingress path from the ingress VSwitch Layers using + FwpsInjectvSwitchEthernetIngressAsync0().
+
+ Notes: Applies to the following ingress layers:
+ FWPM_LAYER_INGRESS_VSWITCH_ETHERNET
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/HH439669.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF546324.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +NTSTATUS PerformBasicPacketModificationAtIngressVSwitchEthernet(_In_ CLASSIFY_DATA** ppClassifyData, + _In_ INJECTION_DATA** ppInjectionData, + _In_ PC_BASIC_PACKET_MODIFICATION_DATA* pModificationData, + _In_ BOOLEAN isInline = FALSE) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PerformBasicPacketModificationAtIngressVSwitchEthernet()\n"); + +#endif /// DBG + + NT_ASSERT(ppClassifyData); + NT_ASSERT(ppInjectionData); + NT_ASSERT(pModificationData); + NT_ASSERT(*ppClassifyData); + NT_ASSERT(*ppInjectionData); + + NTSTATUS status = STATUS_SUCCESS; + FWPS_INCOMING_VALUES* pClassifyValues = (FWPS_INCOMING_VALUES*)(*ppClassifyData)->pClassifyValues; + FWPS_INCOMING_METADATA_VALUES* pMetadata = (FWPS_INCOMING_METADATA_VALUES*)(*ppClassifyData)->pMetadataValues; + FWP_VALUE* pVSwitchIDValue = 0; + FWP_BYTE_BLOB* pVSwitchID = 0; + NDIS_SWITCH_PORT_ID sourcePortID = 0; + NDIS_SWITCH_NIC_INDEX sourceNICIndex = 0; + NET_BUFFER_LIST* pNetBufferList = 0; + BASIC_PACKET_MODIFICATION_COMPLETION_DATA* pCompletionData = 0; + +#if DBG + + KIRQL irql = KeGetCurrentIrql(); + +#endif /// DBG + +#pragma warning(push) +#pragma warning(disable: 6014) /// pCompletionData will be freed in completionFn using BasicPacketModificationCompletionDataDestroy + + HLPR_NEW(pCompletionData, + BASIC_PACKET_MODIFICATION_COMPLETION_DATA, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pCompletionData, + status); + +#pragma warning(pop) + + KeInitializeSpinLock(&(pCompletionData->spinLock)); + + pCompletionData->performedInline = isInline; + pCompletionData->pClassifyData = *ppClassifyData; + pCompletionData->pInjectionData = *ppInjectionData; + + /// Responsibility for freeing this memory has been transferred to the pCompletionData + *ppClassifyData = 0; + + *ppInjectionData = 0; + + if(FWPS_IS_L2_METADATA_FIELD_PRESENT(pMetadata, + FWPS_L2_METADATA_FIELD_VSWITCH_SOURCE_PORT_ID)) + sourcePortID = pMetadata->vSwitchSourcePortId; + + if(FWPS_IS_L2_METADATA_FIELD_PRESENT(pMetadata, + FWPS_L2_METADATA_FIELD_VSWITCH_SOURCE_NIC_INDEX)) + sourceNICIndex = (NDIS_SWITCH_NIC_INDEX)pMetadata->vSwitchSourceNicIndex; + + pVSwitchIDValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_VSWITCH_ID); + if(pVSwitchIDValue) + pVSwitchID = pVSwitchIDValue->byteBlob; + + if(pVSwitchID == 0) + { + status = STATUS_INVALID_MEMBER; + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtIngressVSwitchEthernet() [status: %#x][pVSwitchID: %#p]\n", + status, + pVSwitchID); + + HLPR_BAIL; + } + + /// Initial offset is at the MAC Header, so just clone the entire NET_BUFFER_LIST. + status = FwpsAllocateCloneNetBufferList((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket, + g_pNDISPoolData->nblPoolHandle, + g_pNDISPoolData->nbPoolHandle, + 0, + &pNetBufferList); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtIngressVSwitchEthernet: FwpsAllocateCloneNetBufferList() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + pCompletionData->refCount = KrnlHlprNBLGetRequiredRefCount(pNetBufferList, + TRUE); + + if(pModificationData->flags) + { + /// Various checks and balances must be performed to modify the IP and Transport headers at this modification point. + /// Parsing of the headers will need to occur, as well as spot checking to verify everything is as it should be. + /// Additionally, checksum routines will need to be written to recalculate checksums for some of the headers. + /// The following block of code is to get you started with modifying the headers with info not readily available. + /// (i.e. header parsing has not occurred so there is no relevant classifiable data nor metadata present). +/* + if(pModificationData->flags & PCPMDF_MODIFY_TRANSPORT_HEADER) + { + UINT32 tmpStatus = STATUS_SUCCESS; + IPPROTO protocol = IPPROTO_MAX; + + /// The clone is at the Ethernet Header, so advance by the size of the Ethernet Header... + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList), + ethernetHeaderSize, + FALSE, + 0); + + protocol = KrnlHlprIPHeaderGetProtocolField(pNetBufferList, + pCompletionData->pInjectionData->addressFamily); + + /// No Transport Modification if IPsec encrypted + if(protocol != IPPROTO_ESP && + protocol != IPPROTO_AH) + { + /// ... advance by the size of the IP Header. + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList), + ipHeaderSize, + FALSE, + 0); + + switch(protocol) + { + case IPPROTO_ICMP: + { + UINT32 icmpHeaderSize = ICMP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + icmpHeaderSize = pMetadata->transportHeaderSize; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_TYPE) + { + FWP_VALUE0 icmpType; + + icmpType.type = FWP_UINT8; + icmpType.uint8 = (UINT8)ntohs(pModificationData->transportData.sourcePort); + + status = KrnlHlprICMPv4HeaderModifyType(&icmpType, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_CODE) + { + FWP_VALUE0 icmpCode; + + icmpCode.type = FWP_UINT8; + icmpCode.uint8 = (UINT8)ntohs(pModificationData->transportData.destinationPort); + + status = KrnlHlprICMPv4HeaderModifyCode(&icmpCode, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + break; + } + case IPPROTO_TCP: + { + UINT32 tcpHeaderSize = TCP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + tcpHeaderSize = pMetadata->transportHeaderSize; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_SOURCE_PORT) + { + FWP_VALUE0 srcPort; + + srcPort.type = FWP_UINT16; + srcPort.uint16 = pModificationData->transportData.sourcePort; + + status = KrnlHlprTCPHeaderModifySourcePort(&srcPort, + pNetBufferList, + tcpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_DESTINATION_PORT) + { + FWP_VALUE0 dstPort; + + dstPort.type = FWP_UINT16; + dstPort.uint16 = pModificationData->transportData.destinationPort; + + status = KrnlHlprTCPHeaderModifyDestinationPort(&dstPort, + pNetBufferList, + tcpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + break; + } + case IPPROTO_UDP: + { + UINT32 udpHeaderSize = UDP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + udpHeaderSize = pMetadata->transportHeaderSize; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_SOURCE_PORT) + { + FWP_VALUE0 srcPort; + + srcPort.type = FWP_UINT16; + srcPort.uint16 = pModificationData->transportData.sourcePort; + + status = KrnlHlprUDPHeaderModifySourcePort(&srcPort, + pNetBufferList, + udpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_DESTINATION_PORT) + { + FWP_VALUE0 dstPort; + + dstPort.type = FWP_UINT16; + dstPort.uint16 = pModificationData->transportData.destinationPort; + + status = KrnlHlprUDPHeaderModifyDestinationPort(&dstPort, + pNetBufferList, + udpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + break; + } + case IPPROTO_ICMPV6: + { + UINT32 icmpHeaderSize = ICMP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + icmpHeaderSize = pMetadata->transportHeaderSize; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_TYPE) + { + FWP_VALUE0 icmpType; + + icmpType.type = FWP_UINT8; + icmpType.uint8 = (UINT8)ntohs(pModificationData->transportData.sourcePort); + + status = KrnlHlprICMPv6HeaderModifyType(&icmpType, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_CODE) + { + FWP_VALUE0 icmpCode; + + icmpCode.type = FWP_UINT8; + icmpCode.uint8 = (UINT8)ntohs(pModificationData->transportData.destinationPort); + + status = KrnlHlprICMPv6HeaderModifyCode(&icmpCode, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + break; + } + } + + /// ToDo: Recalculate the Transport Checksum Here + + HLPR_BAIL_LABEL_2: + + /// return the data offset to the beginning of the IP Header + tmpStatus = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList), + ipHeaderSize, + 0, + 0); + if(tmpStatus != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtIngressVSwitchEthernet: NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + HLPR_BAIL_ON_FAILURE(status); + } + + /// return the data offset to the beginning of the Ethernet Header + tmpStatus = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList), + ipHeaderSize, + 0, + 0); + if(tmpStatus != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtIngressVSwitchEthernet: NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + + HLPR_BAIL; + } + } + + if(pModificationData->flags & PCPMDF_MODIFY_IP_HEADER) + { + /// The clone is at the Ethernet Header, so advance by the size of the Ethernet Header... + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList), + ethernetHeaderSize, + FALSE, + 0); + + if(pModificationData->ipData.flags & PCPMDF_MODIFY_IP_HEADER_SOURCE_ADDRESS) + { + FWP_VALUE value; + + RtlZeroMemory(&value, + sizeof(FWP_VALUE)); + + if(pCompletionData->pInjectionData->addressFamily == AF_INET) + { + value.type = FWP_UINT32; + + RtlCopyMemory(&(value.uint32), + pModificationData->ipData.sourceAddress.pIPv4, + IPV4_ADDRESS_SIZE); + } + else + { + HLPR_NEW(value.byteArray16, + FWP_BYTE_ARRAY16, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE_WITH_LABEL(value.byteArray16, + status, + HLPR_BAIL_LABEL_3); + + value.type = FWP_BYTE_ARRAY16_TYPE; + + RtlCopyMemory(value.byteArray16->byteArray16, + pModificationData->ipData.sourceAddress.pIPv6, + IPV6_ADDRESS_SIZE); + } + + status = KrnlHlprIPHeaderModifySourceAddress(&value, + pNetBufferList, + TRUE); + + KrnlHlprFwpValuePurgeLocalCopy(&value); + + HLPR_BAIL_ON_FAILURE_WITH_LABEL(status, + HLPR_BAIL_LABEL_3); + } + + if(pModificationData->ipData.flags & PCPMDF_MODIFY_IP_HEADER_DESTINATION_ADDRESS) + { + FWP_VALUE value; + + RtlZeroMemory(&value, + sizeof(FWP_VALUE)); + + if(pCompletionData->pInjectionData->addressFamily == AF_INET) + { + value.type = FWP_UINT32; + + RtlCopyMemory(&(value.uint32), + pModificationData->ipData.destinationAddress.pIPv4, + IPV4_ADDRESS_SIZE); + } + else + { + HLPR_NEW(value.byteArray16, + FWP_BYTE_ARRAY16, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE_WITH_LABEL(value.byteArray16, + status, + HLPR_BAIL_LABEL_3); + + value.type = FWP_BYTE_ARRAY16_TYPE; + + RtlCopyMemory(value.byteArray16->byteArray16, + pModificationData->ipData.destinationAddress.pIPv6, + IPV6_ADDRESS_SIZE); + } + + status = KrnlHlprIPHeaderModifyDestinationAddress(&value, + pNetBufferList, + TRUE); + + KrnlHlprFwpValuePurgeLocalCopy(&value); + + HLPR_BAIL_ON_FAILURE_WITH_LABEL(status, + HLPR_BAIL_LABEL_3); + } + + HLPR_BAIL_LABEL_3: + + /// return the data offset to the beginning of the Ethernet Header + tmpStatus = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList), + ipHeaderSize, + 0, + 0); + if(tmpStatus != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtIngressVSwitchEthernet: NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + + HLPR_BAIL; + } + } +*/ + if(pModificationData->flags & PCPMDF_MODIFY_MAC_HEADER) + { + if(pModificationData->macData.flags & PCPMDF_MODIFY_MAC_HEADER_SOURCE_ADDRESS) + { + FWP_VALUE value; + + RtlZeroMemory(&value, + sizeof(FWP_VALUE)); + + HLPR_NEW(value.byteArray6, + FWP_BYTE_ARRAY6, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(value.byteArray6, + status); + + value.type = FWP_BYTE_ARRAY6_TYPE; + + RtlCopyMemory(value.byteArray6->byteArray6, + pModificationData->macData.pSourceMACAddress, + ETHERNET_ADDRESS_SIZE); + + status = KrnlHlprMACHeaderModifySourceAddress(&value, + pNetBufferList); + + KrnlHlprFwpValuePurgeLocalCopy(&value); + + HLPR_BAIL_ON_FAILURE(status); + } + + if(pModificationData->macData.flags & PCPMDF_MODIFY_MAC_HEADER_DESTINATION_ADDRESS) + { + FWP_VALUE value; + + RtlZeroMemory(&value, + sizeof(FWP_VALUE)); + +#pragma warning(push) +#pragma warning(disable: 6014) /// value.byteArray6 will be freed in with call to KrnlHlprFwpValuePurgeLocalCopy + + HLPR_NEW(value.byteArray6, + FWP_BYTE_ARRAY6, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(value.byteArray6, + status); + +#pragma warning(pop) + + value.type = FWP_BYTE_ARRAY6_TYPE; + + RtlCopyMemory(value.byteArray6->byteArray6, + pModificationData->macData.pDestinationMACAddress, + ETHERNET_ADDRESS_SIZE); + + status = KrnlHlprMACHeaderModifyDestinationAddress(&value, + pNetBufferList); + + KrnlHlprFwpValuePurgeLocalCopy(&value); + + HLPR_BAIL_ON_FAILURE(status); + } + } + } + + + status = FwpsInjectvSwitchEthernetIngressAsync(pCompletionData->pInjectionData->injectionHandle, + pCompletionData->pInjectionData->injectionContext, + 0, + 0, + pVSwitchID, + sourcePortID, + sourceNICIndex, + pNetBufferList, + CompleteBasicPacketModification, + pCompletionData); + + NT_ASSERT(irql == KeGetCurrentIrql()); + + if(status != STATUS_SUCCESS) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtIngressVSwitchEthernet: FwpsInjectvSwitchEthernetIngressAsync() [status: %#x]\n", + status); + + HLPR_BAIL_LABEL: + + NT_ASSERT(status == STATUS_SUCCESS); + + if(status != STATUS_SUCCESS) + { + if(pNetBufferList) + { + FwpsFreeCloneNetBufferList(pNetBufferList, + 0); + + pNetBufferList = 0; + } + + if(pCompletionData) + BasicPacketModificationCompletionDataDestroy(&pCompletionData, + TRUE); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PerformBasicPacketModificationAtIngressVSwitchEthernet() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @private_function="PerformBasicPacketModificationAtEgressVSwitchEthernet" + + Purpose: Clones the NET_BUFFER_LIST and injects the clone back to the virtual switch's + ingress path from the egress VSwitch Layers using + FwpsInjectvSwitchEthernetIngressAsync0().
+
+ Notes: Applies to the following egress layers:
+ FWPM_LAYER_EGRESS_VSWITCH_ETHERNET
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/HH439669.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF546324.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +NTSTATUS PerformBasicPacketModificationAtEgressVSwitchEthernet(_In_ CLASSIFY_DATA** ppClassifyData, + _In_ INJECTION_DATA** ppInjectionData, + _In_ PC_BASIC_PACKET_MODIFICATION_DATA* pModificationData, + _In_ BOOLEAN isInline = FALSE) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PerformBasicPacketModificationAtEgressVSwitchEthernet()\n"); + +#endif /// DBG + + NT_ASSERT(ppClassifyData); + NT_ASSERT(ppInjectionData); + NT_ASSERT(pModificationData); + NT_ASSERT(*ppClassifyData); + NT_ASSERT(*ppInjectionData); + + NTSTATUS status = STATUS_SUCCESS; + FWPS_INCOMING_VALUES* pClassifyValues = (FWPS_INCOMING_VALUES*)(*ppClassifyData)->pClassifyValues; + FWPS_INCOMING_METADATA_VALUES* pMetadata = (FWPS_INCOMING_METADATA_VALUES*)(*ppClassifyData)->pMetadataValues; + FWP_VALUE* pVSwitchIDValue = 0; + FWP_BYTE_BLOB* pVSwitchID = 0; + NDIS_SWITCH_PORT_ID sourcePortID = 0; + NDIS_SWITCH_NIC_INDEX sourceNICIndex = 0; + NET_BUFFER_LIST* pNetBufferList = 0; + BASIC_PACKET_MODIFICATION_COMPLETION_DATA* pCompletionData = 0; + +#if DBG + + KIRQL irql = KeGetCurrentIrql(); + +#endif /// DBG + +#pragma warning(push) +#pragma warning(disable: 6014) /// pCompletionData will be freed in completionFn using BasicPacketModificationCompletionDataDestroy + + HLPR_NEW(pCompletionData, + BASIC_PACKET_MODIFICATION_COMPLETION_DATA, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pCompletionData, + status); + +#pragma warning(pop) + + KeInitializeSpinLock(&(pCompletionData->spinLock)); + + pCompletionData->performedInline = isInline; + pCompletionData->pClassifyData = *ppClassifyData; + pCompletionData->pInjectionData = *ppInjectionData; + + /// Responsibility for freeing this memory has been transferred to the pCompletionData + *ppClassifyData = 0; + + *ppInjectionData = 0; + + if(FWPS_IS_L2_METADATA_FIELD_PRESENT(pMetadata, + FWPS_L2_METADATA_FIELD_VSWITCH_SOURCE_PORT_ID)) + sourcePortID = pMetadata->vSwitchSourcePortId; + + if(FWPS_IS_L2_METADATA_FIELD_PRESENT(pMetadata, + FWPS_L2_METADATA_FIELD_VSWITCH_SOURCE_NIC_INDEX)) + sourceNICIndex = (NDIS_SWITCH_NIC_INDEX)pMetadata->vSwitchSourceNicIndex; + + pVSwitchIDValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_VSWITCH_ID); + if(pVSwitchIDValue) + pVSwitchID = pVSwitchIDValue->byteBlob; + + if(pVSwitchID == 0) + { + status = STATUS_INVALID_MEMBER; + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtEgressVSwitchEthernet() [status: %#x][pVSwitchID: %#p]\n", + status, + pVSwitchID); + + HLPR_BAIL; + } + + /// Initial offset is at the MAC Header, so just clone the entire NET_BUFFER_LIST. + status = FwpsAllocateCloneNetBufferList((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket, + g_pNDISPoolData->nblPoolHandle, + g_pNDISPoolData->nbPoolHandle, + 0, + &pNetBufferList); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtEgressVSwitchEthernet: FwpsAllocateCloneNetBufferList() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + pCompletionData->refCount = KrnlHlprNBLGetRequiredRefCount(pNetBufferList, + TRUE); + + if(pModificationData->flags) + { + /// Various checks and balances must be performed to modify the IP and Transport headers at this modification point. + /// Parsing of the headers will need to occur, as well as spot checking to verify everything is as it should be. + /// Additionally, checksum routines will need to be written to recalculate checksums for some of the headers. + /// The following block of code is to get you started with modifying the headers with info not readily available. + /// (i.e. header parsing has not occurred so there is no relevant classifiable data nor metadata present). +/* + if(pModificationData->flags & PCPMDF_MODIFY_TRANSPORT_HEADER) + { + UINT32 tmpStatus = STATUS_SUCCESS; + IPPROTO protocol = IPPROTO_MAX; + + /// The clone is at the Ethernet Header, so advance by the size of the Ethernet Header... + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList), + ethernetHeaderSize, + FALSE, + 0); + + protocol = KrnlHlprIPHeaderGetProtocolField(pNetBufferList, + pCompletionData->pInjectionData->addressFamily); + + /// No Transport Modification if IPsec encrypted + if(protocol != IPPROTO_ESP && + protocol != IPPROTO_AH) + { + /// ... advance by the size of the IP Header. + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList), + ipHeaderSize, + FALSE, + 0); + + switch(protocol) + { + case IPPROTO_ICMP: + { + UINT32 icmpHeaderSize = ICMP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + icmpHeaderSize = pMetadata->transportHeaderSize; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_TYPE) + { + FWP_VALUE0 icmpType; + + icmpType.type = FWP_UINT8; + icmpType.uint8 = (UINT8)ntohs(pModificationData->transportData.sourcePort); + + status = KrnlHlprICMPv4HeaderModifyType(&icmpType, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_CODE) + { + FWP_VALUE0 icmpCode; + + icmpCode.type = FWP_UINT8; + icmpCode.uint8 = (UINT8)ntohs(pModificationData->transportData.destinationPort); + + status = KrnlHlprICMPv4HeaderModifyCode(&icmpCode, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + break; + } + case IPPROTO_TCP: + { + UINT32 tcpHeaderSize = TCP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + tcpHeaderSize = pMetadata->transportHeaderSize; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_SOURCE_PORT) + { + FWP_VALUE0 srcPort; + + srcPort.type = FWP_UINT16; + srcPort.uint16 = pModificationData->transportData.sourcePort; + + status = KrnlHlprTCPHeaderModifySourcePort(&srcPort, + pNetBufferList, + tcpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_DESTINATION_PORT) + { + FWP_VALUE0 dstPort; + + dstPort.type = FWP_UINT16; + dstPort.uint16 = pModificationData->transportData.destinationPort; + + status = KrnlHlprTCPHeaderModifyDestinationPort(&dstPort, + pNetBufferList, + tcpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + break; + } + case IPPROTO_UDP: + { + UINT32 udpHeaderSize = UDP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + udpHeaderSize = pMetadata->transportHeaderSize; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_SOURCE_PORT) + { + FWP_VALUE0 srcPort; + + srcPort.type = FWP_UINT16; + srcPort.uint16 = pModificationData->transportData.sourcePort; + + status = KrnlHlprUDPHeaderModifySourcePort(&srcPort, + pNetBufferList, + udpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_DESTINATION_PORT) + { + FWP_VALUE0 dstPort; + + dstPort.type = FWP_UINT16; + dstPort.uint16 = pModificationData->transportData.destinationPort; + + status = KrnlHlprUDPHeaderModifyDestinationPort(&dstPort, + pNetBufferList, + udpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + break; + } + case IPPROTO_ICMPV6: + { + UINT32 icmpHeaderSize = ICMP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + icmpHeaderSize = pMetadata->transportHeaderSize; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_TYPE) + { + FWP_VALUE0 icmpType; + + icmpType.type = FWP_UINT8; + icmpType.uint8 = (UINT8)ntohs(pModificationData->transportData.sourcePort); + + status = KrnlHlprICMPv6HeaderModifyType(&icmpType, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_CODE) + { + FWP_VALUE0 icmpCode; + + icmpCode.type = FWP_UINT8; + icmpCode.uint8 = (UINT8)ntohs(pModificationData->transportData.destinationPort); + + status = KrnlHlprICMPv6HeaderModifyCode(&icmpCode, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + break; + } + } + + /// ToDo: Recalculate the Transport Checksum Here + + HLPR_BAIL_LABEL_2: + + /// return the data offset to the beginning of the IP Header + tmpStatus = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList), + ipHeaderSize, + 0, + 0); + if(tmpStatus != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtEgressVSwitchEthernet: NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + HLPR_BAIL_ON_FAILURE(status); + } + + /// return the data offset to the beginning of the Ethernet Header + tmpStatus = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList), + ipHeaderSize, + 0, + 0); + if(tmpStatus != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtEgressVSwitchEthernet: NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + + HLPR_BAIL; + } + } + + if(pModificationData->flags & PCPMDF_MODIFY_IP_HEADER) + { + /// The clone is at the Ethernet Header, so advance by the size of the Ethernet Header... + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList), + ethernetHeaderSize, + FALSE, + 0); + + if(pModificationData->ipData.flags & PCPMDF_MODIFY_IP_HEADER_SOURCE_ADDRESS) + { + FWP_VALUE value; + + RtlZeroMemory(&value, + sizeof(FWP_VALUE)); + + if(pCompletionData->pInjectionData->addressFamily == AF_INET) + { + value.type = FWP_UINT32; + + RtlCopyMemory(&(value.uint32), + pModificationData->ipData.sourceAddress.pIPv4, + IPV4_ADDRESS_SIZE); + } + else + { + HLPR_NEW(value.byteArray16, + FWP_BYTE_ARRAY16, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE_WITH_LABEL(value.byteArray16, + status, + HLPR_BAIL_LABEL_3); + + value.type = FWP_BYTE_ARRAY16_TYPE; + + RtlCopyMemory(value.byteArray16->byteArray16, + pModificationData->ipData.sourceAddress.pIPv6, + IPV6_ADDRESS_SIZE); + } + + status = KrnlHlprIPHeaderModifySourceAddress(&value, + pNetBufferList, + TRUE); + + KrnlHlprFwpValuePurgeLocalCopy(&value); + + HLPR_BAIL_ON_FAILURE_WITH_LABEL(status, + HLPR_BAIL_LABEL_3); + } + + if(pModificationData->ipData.flags & PCPMDF_MODIFY_IP_HEADER_DESTINATION_ADDRESS) + { + FWP_VALUE value; + + RtlZeroMemory(&value, + sizeof(FWP_VALUE)); + + if(pCompletionData->pInjectionData->addressFamily == AF_INET) + { + value.type = FWP_UINT32; + + RtlCopyMemory(&(value.uint32), + pModificationData->ipData.destinationAddress.pIPv4, + IPV4_ADDRESS_SIZE); + } + else + { + HLPR_NEW(value.byteArray16, + FWP_BYTE_ARRAY16, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE_WITH_LABEL(value.byteArray16, + status, + HLPR_BAIL_LABEL_3); + + value.type = FWP_BYTE_ARRAY16_TYPE; + + RtlCopyMemory(value.byteArray16->byteArray16, + pModificationData->ipData.destinationAddress.pIPv6, + IPV6_ADDRESS_SIZE); + } + + status = KrnlHlprIPHeaderModifyDestinationAddress(&value, + pNetBufferList, + TRUE); + + KrnlHlprFwpValuePurgeLocalCopy(&value); + + HLPR_BAIL_ON_FAILURE_WITH_LABEL(status, + HLPR_BAIL_LABEL_3); + } + + HLPR_BAIL_LABEL_3: + + /// return the data offset to the beginning of the Ethernet Header + tmpStatus = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList), + ipHeaderSize, + 0, + 0); + if(tmpStatus != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtEgressVSwitchEthernet: NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + + HLPR_BAIL; + } + } +*/ + if(pModificationData->flags & PCPMDF_MODIFY_MAC_HEADER) + { + if(pModificationData->macData.flags & PCPMDF_MODIFY_MAC_HEADER_SOURCE_ADDRESS) + { + FWP_VALUE value; + + RtlZeroMemory(&value, + sizeof(FWP_VALUE)); + + HLPR_NEW(value.byteArray6, + FWP_BYTE_ARRAY6, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(value.byteArray6, + status); + + value.type = FWP_BYTE_ARRAY6_TYPE; + + RtlCopyMemory(value.byteArray6->byteArray6, + pModificationData->macData.pSourceMACAddress, + ETHERNET_ADDRESS_SIZE); + + status = KrnlHlprMACHeaderModifySourceAddress(&value, + pNetBufferList); + + KrnlHlprFwpValuePurgeLocalCopy(&value); + + HLPR_BAIL_ON_FAILURE(status); + } + + if(pModificationData->macData.flags & PCPMDF_MODIFY_MAC_HEADER_DESTINATION_ADDRESS) + { + FWP_VALUE value; + + RtlZeroMemory(&value, + sizeof(FWP_VALUE)); + +#pragma warning(push) +#pragma warning(disable: 6014) /// value.byteArray6 will be freed in with call to KrnlHlprFwpValuePurgeLocalCopy + + HLPR_NEW(value.byteArray6, + FWP_BYTE_ARRAY6, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(value.byteArray6, + status); + +#pragma warning(pop) + + value.type = FWP_BYTE_ARRAY6_TYPE; + + RtlCopyMemory(value.byteArray6->byteArray6, + pModificationData->macData.pDestinationMACAddress, + ETHERNET_ADDRESS_SIZE); + + status = KrnlHlprMACHeaderModifyDestinationAddress(&value, + pNetBufferList); + + KrnlHlprFwpValuePurgeLocalCopy(&value); + + HLPR_BAIL_ON_FAILURE(status); + } + } + } + + status = FwpsInjectvSwitchEthernetIngressAsync(pCompletionData->pInjectionData->injectionHandle, + pCompletionData->pInjectionData->injectionContext, + 0, + 0, + pVSwitchID, + sourcePortID, + sourceNICIndex, + pNetBufferList, + CompleteBasicPacketModification, + pCompletionData); + + NT_ASSERT(irql == KeGetCurrentIrql()); + + if(status != STATUS_SUCCESS) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtEgressVSwitchEthernet: FwpsInjectvSwitchEthernetIngressAsync() [status: %#x]\n", + status); + + HLPR_BAIL_LABEL: + + NT_ASSERT(status == STATUS_SUCCESS); + + if(status != STATUS_SUCCESS) + { + if(pNetBufferList) + { + FwpsFreeCloneNetBufferList(pNetBufferList, + 0); + + pNetBufferList = 0; + } + + if(pCompletionData) + BasicPacketModificationCompletionDataDestroy(&pCompletionData, + TRUE); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PerformBasicPacketModificationAtEgressVSwitchEthernet() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + + +#endif // (NTDDI_VERSION >= NTDDI_WIN8) + +/** + @private_function="PerformBasicPacketModificationAtInboundNetwork" + + Purpose: Clones the NET_BUFFER_LIST and injects the clone back to the stack from the + incoming Network Layers using FwpsInjectNetworkReceiveAsync().
+
+ Notes: Applies to the following inbound layers:
+ FWPM_LAYER_INBOUND_IPPACKET_V{4/6}
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551183.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF546324.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +NTSTATUS PerformBasicPacketModificationAtInboundNetwork(_In_ CLASSIFY_DATA** ppClassifyData, + _In_ INJECTION_DATA** ppInjectionData, + _In_ PC_BASIC_PACKET_MODIFICATION_DATA* pModificationData, + _In_ BOOLEAN isInline = FALSE) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PerformBasicPacketModificationAtInboundNetwork()\n"); + +#endif /// DBG + + NT_ASSERT(ppClassifyData); + NT_ASSERT(ppInjectionData); + NT_ASSERT(pModificationData); + NT_ASSERT(*ppClassifyData); + NT_ASSERT(*ppInjectionData); + + NTSTATUS status = STATUS_SUCCESS; + FWPS_INCOMING_VALUES* pClassifyValues = (FWPS_INCOMING_VALUES*)(*ppClassifyData)->pClassifyValues; + FWPS_INCOMING_METADATA_VALUES* pMetadata = (FWPS_INCOMING_METADATA_VALUES*)(*ppClassifyData)->pMetadataValues; + COMPARTMENT_ID compartmentID = DEFAULT_COMPARTMENT_ID; + IF_INDEX interfaceIndex = 0; + IF_INDEX subInterfaceIndex = 0; + UINT32 flags = 0; + NET_BUFFER_LIST* pNetBufferList = 0; + BASIC_PACKET_MODIFICATION_COMPLETION_DATA* pCompletionData = 0; + UINT32 ipHeaderSize = 0; + UINT32 bytesRetreated = 0; + UINT64 endpointHandle = 0; + IPPROTO protocol = IPPROTO_MAX; + FWP_VALUE* pInterfaceIndex = 0; + FWP_VALUE* pSubInterfaceIndex = 0; + FWP_VALUE* pFlags = 0; + BYTE* pSourceAddress = 0; + BYTE* pDestinationAddress = 0; + NDIS_TCP_IP_CHECKSUM_PACKET_INFO checksumInfo = {0}; + +#if DBG + + KIRQL irql = KeGetCurrentIrql(); + +#endif /// DBG + +#pragma warning(push) +#pragma warning(disable: 6014) /// pCompletionData will be freed in completionFn using BasicPacketModificationCompletionDataDestroy + + HLPR_NEW(pCompletionData, + BASIC_PACKET_MODIFICATION_COMPLETION_DATA, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pCompletionData, + status); + +#pragma warning(pop) + + KeInitializeSpinLock(&(pCompletionData->spinLock)); + + pCompletionData->performedInline = isInline; + pCompletionData->pClassifyData = *ppClassifyData; + pCompletionData->pInjectionData = *ppInjectionData; + + /// Responsibility for freeing this memory has been transferred to the pCompletionData + *ppClassifyData = 0; + + *ppInjectionData = 0; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_COMPARTMENT_ID)) + compartmentID = (COMPARTMENT_ID)pMetadata->compartmentId; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_ENDPOINT_HANDLE)) + endpointHandle = pMetadata->transportEndpointHandle; + + pInterfaceIndex = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_INTERFACE_INDEX); + if(pInterfaceIndex && + pInterfaceIndex->type == FWP_UINT32) + interfaceIndex = (IF_INDEX)pInterfaceIndex->uint32; + + pSubInterfaceIndex = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_SUB_INTERFACE_INDEX); + if(pSubInterfaceIndex && + pSubInterfaceIndex->type == FWP_UINT32) + subInterfaceIndex = (IF_INDEX)pSubInterfaceIndex->uint32; + + pFlags = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_FLAGS); + if(pFlags && + pFlags->type == FWP_UINT32) + flags = pFlags->uint32; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_IP_HEADER_SIZE)) + bytesRetreated = ipHeaderSize = pMetadata->ipHeaderSize; + + checksumInfo.Value = (ULONG)(ULONG_PTR)NET_BUFFER_LIST_INFO((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket, + TcpIpChecksumNetBufferListInfo); + + /// Initial offset is at the Transport Header, so retreat the size of the IP Header ... + status = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket), + bytesRetreated, + 0, + 0); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtInboundNetwork: NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + /// ... clone the entire NET_BUFFER_LIST ... + status = FwpsAllocateCloneNetBufferList((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket, + g_pNDISPoolData->nblPoolHandle, + g_pNDISPoolData->nbPoolHandle, + 0, + &pNetBufferList); + + /// ... and advance the offset back to the original position. + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket), + bytesRetreated, + FALSE, + 0); + + if(status != STATUS_SUCCESS || + !pNetBufferList) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtInboundNetwork: FwpsAllocateCloneNetBufferList() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + /// Handle if this packet had the IP checksum offloaded or if it's loopback + if(checksumInfo.Receive.NdisPacketIpChecksumSucceeded || + flags & FWP_CONDITION_FLAG_IS_LOOPBACK) + { + /// Prevent TCP/IP Zone crossing and recalculate the checksums + if(flags & FWP_CONDITION_FLAG_IS_LOOPBACK) + { + FWP_VALUE* pLocalAddress = 0; + FWP_VALUE* pRemoteAddress = 0; + FWP_VALUE* pLoopbackAddress = 0; + + pLocalAddress = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_REMOTE_ADDRESS); + if(pLocalAddress && + ((pLocalAddress->type == FWP_UINT32 && + RtlCompareMemory(&(pLocalAddress->uint32), + IPV4_LOOPBACK_ADDRESS, + IPV4_ADDRESS_SIZE)) || + (pLocalAddress->type == FWP_BYTE_ARRAY16_TYPE && + RtlCompareMemory(pLocalAddress->byteArray16->byteArray16, + IPV6_LOOPBACK_ADDRESS, + IPV6_ADDRESS_SIZE)))) + pLoopbackAddress = pLocalAddress; + + if(!pLoopbackAddress) + { + pRemoteAddress = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_REMOTE_ADDRESS); + if(pRemoteAddress && + ((pRemoteAddress->type == FWP_UINT32 && + RtlCompareMemory(&(pRemoteAddress->uint32), + IPV4_LOOPBACK_ADDRESS, + IPV4_ADDRESS_SIZE)) || + (pRemoteAddress->type == FWP_BYTE_ARRAY16_TYPE && + RtlCompareMemory(pRemoteAddress->byteArray16->byteArray16, + IPV6_LOOPBACK_ADDRESS, + IPV6_ADDRESS_SIZE)))) + pLoopbackAddress = pRemoteAddress; + } + + if(pLoopbackAddress) + { + status = KrnlHlprIPHeaderModifyLoopbackToLocal(pMetadata, + pLoopbackAddress, + ipHeaderSize, + pNetBufferList, + (const WSACMSGHDR*)pCompletionData->pInjectionData->pControlData, + pCompletionData->pInjectionData->controlDataLength); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtInboundNetwork: KrnlHlprIPHeaderModifyLoopbackToLocal() [status: %#x]\n", + status); + + HLPR_BAIL; + } + } + } + else + { + /// Recalculate the checksum + if(pCompletionData->pInjectionData->addressFamily == AF_INET) + KrnlHlprIPHeaderCalculateV4Checksum(pNetBufferList, + ipHeaderSize); + } + } + + pCompletionData->refCount = KrnlHlprNBLGetRequiredRefCount(pNetBufferList); + + protocol = KrnlHlprIPHeaderGetProtocolField(pNetBufferList, + pCompletionData->pInjectionData->addressFamily); + + if(pModificationData->flags) + { + if(pModificationData->flags & PCPMDF_MODIFY_TRANSPORT_HEADER) + { + NTSTATUS tmpStatus = STATUS_SUCCESS; + + /// The clone is at the IP Header, so advance by the size of the IP Header. + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList), + ipHeaderSize, + FALSE, + 0); + + switch(protocol) + { + case IPPROTO_ICMP: + { + UINT32 icmpHeaderSize = ICMP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + icmpHeaderSize = pMetadata->transportHeaderSize; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_TYPE) + { + FWP_VALUE0 icmpType; + + icmpType.type = FWP_UINT8; + icmpType.uint8 = (UINT8)ntohs(pModificationData->transportData.sourcePort); + + status = KrnlHlprICMPv4HeaderModifyType(&icmpType, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_CODE) + { + FWP_VALUE0 icmpCode; + + icmpCode.type = FWP_UINT8; + icmpCode.uint8 = (UINT8)ntohs(pModificationData->transportData.destinationPort); + + status = KrnlHlprICMPv4HeaderModifyCode(&icmpCode, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + break; + } + case IPPROTO_TCP: + { + UINT32 tcpHeaderSize = TCP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + tcpHeaderSize = pMetadata->transportHeaderSize; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_SOURCE_PORT) + { + FWP_VALUE0 srcPort; + + srcPort.type = FWP_UINT16; + srcPort.uint16 = pModificationData->transportData.sourcePort; + + status = KrnlHlprTCPHeaderModifySourcePort(&srcPort, + pNetBufferList, + tcpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_DESTINATION_PORT) + { + FWP_VALUE0 dstPort; + + dstPort.type = FWP_UINT16; + dstPort.uint16 = pModificationData->transportData.destinationPort; + + status = KrnlHlprTCPHeaderModifyDestinationPort(&dstPort, + pNetBufferList, + tcpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + break; + } + case IPPROTO_UDP: + { + UINT32 udpHeaderSize = UDP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + udpHeaderSize = pMetadata->transportHeaderSize; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_SOURCE_PORT) + { + FWP_VALUE0 srcPort; + + srcPort.type = FWP_UINT16; + srcPort.uint16 = pModificationData->transportData.sourcePort; + + status = KrnlHlprUDPHeaderModifySourcePort(&srcPort, + pNetBufferList, + udpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_DESTINATION_PORT) + { + FWP_VALUE0 dstPort; + + dstPort.type = FWP_UINT16; + dstPort.uint16 = pModificationData->transportData.destinationPort; + + status = KrnlHlprUDPHeaderModifyDestinationPort(&dstPort, + pNetBufferList, + udpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + break; + } + case IPPROTO_ICMPV6: + { + UINT32 icmpHeaderSize = ICMP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + icmpHeaderSize = pMetadata->transportHeaderSize; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_TYPE) + { + FWP_VALUE0 icmpType; + + icmpType.type = FWP_UINT8; + icmpType.uint8 = (UINT8)ntohs(pModificationData->transportData.sourcePort); + + status = KrnlHlprICMPv6HeaderModifyType(&icmpType, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_CODE) + { + FWP_VALUE0 icmpCode; + + icmpCode.type = FWP_UINT8; + icmpCode.uint8 = (UINT8)ntohs(pModificationData->transportData.destinationPort); + + status = KrnlHlprICMPv6HeaderModifyCode(&icmpCode, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + break; + } + } + + HLPR_BAIL_LABEL_2: + + /// return the data offset to the beginning of the IP Header + tmpStatus = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList), + ipHeaderSize, + 0, + 0); + if(tmpStatus != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtInboundNetwork: NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + HLPR_BAIL_ON_FAILURE(status); + } + + if(pModificationData->flags & PCPMDF_MODIFY_IP_HEADER) + { + if(pModificationData->ipData.flags & PCPMDF_MODIFY_IP_HEADER_INTERFACE_INDEX) + interfaceIndex = pModificationData->ipData.interfaceIndex; + + if(pModificationData->ipData.flags & PCPMDF_MODIFY_IP_HEADER_SOURCE_ADDRESS) + { + FWP_VALUE value; + + RtlZeroMemory(&value, + sizeof(FWP_VALUE)); + + if(pCompletionData->pInjectionData->addressFamily == AF_INET) + { + value.type = FWP_UINT32; + + RtlCopyMemory(&(value.uint32), + pModificationData->ipData.sourceAddress.pIPv4, + IPV4_ADDRESS_SIZE); + } + else + { + HLPR_NEW(value.byteArray16, + FWP_BYTE_ARRAY16, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(value.byteArray16, + status); + + value.type = FWP_BYTE_ARRAY16_TYPE; + + RtlCopyMemory(value.byteArray16->byteArray16, + pModificationData->ipData.sourceAddress.pIPv6, + IPV6_ADDRESS_SIZE); + } + + status = KrnlHlprIPHeaderModifySourceAddress(&value, + pNetBufferList, + TRUE); + + KrnlHlprFwpValuePurgeLocalCopy(&value); + + HLPR_BAIL_ON_FAILURE(status); + } + + if(pModificationData->ipData.flags & PCPMDF_MODIFY_IP_HEADER_DESTINATION_ADDRESS) + { + FWP_VALUE value; + + RtlZeroMemory(&value, + sizeof(FWP_VALUE)); + + if(pCompletionData->pInjectionData->addressFamily == AF_INET) + { + value.type = FWP_UINT32; + + RtlCopyMemory(&(value.uint32), + pModificationData->ipData.destinationAddress.pIPv4, + IPV4_ADDRESS_SIZE); + } + else + { +#pragma warning(push) +#pragma warning(disable: 6014) /// value.byteArray16 will be freed in with call to KrnlHlprFwpValuePurgeLocalCopy + + HLPR_NEW(value.byteArray16, + FWP_BYTE_ARRAY16, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(value.byteArray16, + status); + +#pragma warning(pop) + + value.type = FWP_BYTE_ARRAY16_TYPE; + + RtlCopyMemory(value.byteArray16->byteArray16, + pModificationData->ipData.destinationAddress.pIPv6, + IPV6_ADDRESS_SIZE); + } + + status = KrnlHlprIPHeaderModifyDestinationAddress(&value, + pNetBufferList, + TRUE); + + KrnlHlprFwpValuePurgeLocalCopy(&value); + + HLPR_BAIL_ON_FAILURE(status); + } + } + } + + /// Handle if this packet is destined for the software loopback + if(flags & FWP_CONDITION_FLAG_IS_LOOPBACK) + { + FWP_VALUE* pLocalAddress = 0; + FWP_VALUE* pRemoteAddress = 0; + FWP_VALUE* pLoopbackAddress = 0; + + pLocalAddress = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_REMOTE_ADDRESS); + if(pLocalAddress && + ((pLocalAddress->type == FWP_UINT32 && + RtlCompareMemory(&(pLocalAddress->uint32), + IPV4_LOOPBACK_ADDRESS, + IPV4_ADDRESS_SIZE)) || + (pLocalAddress->type == FWP_BYTE_ARRAY16_TYPE && + RtlCompareMemory(pLocalAddress->byteArray16->byteArray16, + IPV6_LOOPBACK_ADDRESS, + IPV6_ADDRESS_SIZE)))) + pLoopbackAddress = pLocalAddress; + + if(!pLoopbackAddress) + { + pRemoteAddress = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_REMOTE_ADDRESS); + if(pRemoteAddress && + ((pRemoteAddress->type == FWP_UINT32 && + RtlCompareMemory(&(pRemoteAddress->uint32), + IPV4_LOOPBACK_ADDRESS, + IPV4_ADDRESS_SIZE)) || + (pRemoteAddress->type == FWP_BYTE_ARRAY16_TYPE && + RtlCompareMemory(pRemoteAddress->byteArray16->byteArray16, + IPV6_LOOPBACK_ADDRESS, + IPV6_ADDRESS_SIZE)))) + pLoopbackAddress = pRemoteAddress; + } + + if(pLoopbackAddress) + { + status = KrnlHlprIPHeaderModifyLoopbackToLocal(pMetadata, + pLoopbackAddress, + ipHeaderSize, + pNetBufferList, + (const WSACMSGHDR*)pCompletionData->pInjectionData->pControlData, + pCompletionData->pInjectionData->controlDataLength); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtInboundNetwork: KrnlHlprIPHeaderModifyLoopbackToLocal() [status: %#x]\n", + status); + + HLPR_BAIL; + } + } + } + + pSourceAddress = KrnlHlprIPHeaderGetSourceAddressField(pNetBufferList, + pCompletionData->pInjectionData->addressFamily); + + pDestinationAddress = KrnlHlprIPHeaderGetDestinationAddressField(pNetBufferList, + pCompletionData->pInjectionData->addressFamily); + + status = FwpsConstructIpHeaderForTransportPacket(pNetBufferList, + ipHeaderSize, + pCompletionData->pInjectionData->addressFamily, + (UCHAR*)pSourceAddress, + (UCHAR*)pDestinationAddress, + protocol, + endpointHandle, + (const WSACMSGHDR*)pCompletionData->pInjectionData->pControlData, + pCompletionData->pInjectionData->controlDataLength, + 0, + 0, + interfaceIndex, + subInterfaceIndex); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtInboundNetwork: NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + status = FwpsInjectNetworkReceiveAsync(pCompletionData->pInjectionData->injectionHandle, + pCompletionData->pInjectionData->injectionContext, + 0, + compartmentID, + interfaceIndex, + subInterfaceIndex, + pNetBufferList, + CompleteBasicPacketModification, + pCompletionData); + + NT_ASSERT(irql == KeGetCurrentIrql()); + + if(status != STATUS_SUCCESS) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtInboundNetwork: FwpsInjectNetworkReceiveAsync() [status: %#x]\n", + status); + + HLPR_BAIL_LABEL: + + NT_ASSERT(status == STATUS_SUCCESS); + + if(status != STATUS_SUCCESS) + { + if(pNetBufferList) + { + FwpsFreeCloneNetBufferList(pNetBufferList, + 0); + + pNetBufferList = 0; + } + + if(pCompletionData) + BasicPacketModificationCompletionDataDestroy(&pCompletionData, + TRUE); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PerformBasicPacketModificationAtInboundNetwork() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @private_function="PerformBasicPacketModificationAtOutboundNetwork" + + Purpose: Clones the NET_BUFFER_LIST and injects the clone back to the stack from the + outgoing Network Layers using FwpsInjectNetworkSendAsync().
+
+ Notes: Applies to the following inbound layers:
+ FWPM_LAYER_OUTBOUND_IPPACKET_V{4/6}
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551185.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF546324.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +NTSTATUS PerformBasicPacketModificationAtOutboundNetwork(_In_ CLASSIFY_DATA** ppClassifyData, + _In_ INJECTION_DATA** ppInjectionData, + _In_ PC_BASIC_PACKET_MODIFICATION_DATA* pModificationData, + _In_ BOOLEAN isInline = FALSE) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PerformBasicPacketModificationAtOutboundNetwork()\n"); + +#endif /// DBG + + NT_ASSERT(ppClassifyData); + NT_ASSERT(ppInjectionData); + NT_ASSERT(pModificationData); + NT_ASSERT(*ppClassifyData); + NT_ASSERT(*ppInjectionData); + + NTSTATUS status = STATUS_SUCCESS; + FWPS_INCOMING_METADATA_VALUES* pMetadata = (FWPS_INCOMING_METADATA_VALUES*)(*ppClassifyData)->pMetadataValues; + COMPARTMENT_ID compartmentID = DEFAULT_COMPARTMENT_ID; + NET_BUFFER_LIST* pNetBufferList = 0; + BASIC_PACKET_MODIFICATION_COMPLETION_DATA* pCompletionData = 0; + +#if DBG + + KIRQL irql = KeGetCurrentIrql(); + +#endif /// DBG + +#pragma warning(push) +#pragma warning(disable: 6014) /// pCompletionData will be freed in completionFn using BasicPacketModificationCompletionDataDestroy + + HLPR_NEW(pCompletionData, + BASIC_PACKET_MODIFICATION_COMPLETION_DATA, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pCompletionData, + status); + +#pragma warning(pop) + + KeInitializeSpinLock(&(pCompletionData->spinLock)); + + pCompletionData->performedInline = isInline; + pCompletionData->pClassifyData = *ppClassifyData; + pCompletionData->pInjectionData = *ppInjectionData; + + /// Responsibility for freeing this memory has been transferred to the pCompletionData + *ppClassifyData = 0; + + *ppInjectionData = 0; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_COMPARTMENT_ID)) + compartmentID = (COMPARTMENT_ID)pMetadata->compartmentId; + + /// Initial offset is at the IP Header, so just clone the entire NET_BUFFER_LIST. + status = FwpsAllocateCloneNetBufferList((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket, + g_pNDISPoolData->nblPoolHandle, + g_pNDISPoolData->nbPoolHandle, + 0, + &pNetBufferList); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtOutboundNetwork: FwpsAllocateCloneNetBufferList() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + pCompletionData->refCount = KrnlHlprNBLGetRequiredRefCount(pNetBufferList); + + if(pModificationData->flags) + { + /// Various checks and balances must be performed to modify the Transport header at this modification point. + /// Parsing of the headers will need to occur, as well as spot checking to verify everything is as it should be. + /// Additionally, checksum routines will need to be written to recalculate checksums for some of the headers. + /// The following block of code is to get you started with modifying the headers with info not readily available. + /// (i.e. header parsing has not occurred so there is no relevant classifiable data nor metadata present). +/* + if(pModificationData->flags & PCPMDF_MODIFY_TRANSPORT_HEADER) + { + UINT32 tmpStatus = STATUS_SUCCESS; + IPPROTO protocol = IPPROTO_MAX; + + protocol = KrnlHlprIPHeaderGetProtocolField(pNetBufferList, + pCompletionData->pInjectionData->addressFamily); + + /// The clone is at the IP Header, so advance by the size of the IP Header. + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList), + ipHeaderSize, + FALSE, + 0); + + switch(protocol) + { + case IPPROTO_ICMP: + { + UINT32 icmpHeaderSize = ICMP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + icmpHeaderSize = pMetadata->transportHeaderSize; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_TYPE) + { + FWP_VALUE0 icmpType; + + icmpType.type = FWP_UINT8; + icmpType.uint8 = (UINT8)ntohs(pModificationData->transportData.sourcePort); + + status = KrnlHlprICMPv4HeaderModifyType(&icmpType, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_CODE) + { + FWP_VALUE0 icmpCode; + + icmpCode.type = FWP_UINT8; + icmpCode.uint8 = (UINT8)ntohs(pModificationData->transportData.destinationPort); + + status = KrnlHlprICMPv4HeaderModifyCode(&icmpCode, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + break; + } + case IPPROTO_TCP: + { + UINT32 tcpHeaderSize = TCP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + tcpHeaderSize = pMetadata->transportHeaderSize; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_SOURCE_PORT) + { + FWP_VALUE0 srcPort; + + srcPort.type = FWP_UINT16; + srcPort.uint16 = pModificationData->transportData.sourcePort; + + status = KrnlHlprTCPHeaderModifySourcePort(&srcPort, + pNetBufferList, + tcpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_DESTINATION_PORT) + { + FWP_VALUE0 dstPort; + + dstPort.type = FWP_UINT16; + dstPort.uint16 = pModificationData->transportData.destinationPort; + + status = KrnlHlprTCPHeaderModifyDestinationPort(&dstPort, + pNetBufferList, + tcpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + break; + } + case IPPROTO_UDP: + { + UINT32 udpHeaderSize = UDP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + udpHeaderSize = pMetadata->transportHeaderSize; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_SOURCE_PORT) + { + FWP_VALUE0 srcPort; + + srcPort.type = FWP_UINT16; + srcPort.uint16 = pModificationData->transportData.sourcePort; + + status = KrnlHlprUDPHeaderModifySourcePort(&srcPort, + pNetBufferList, + udpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_DESTINATION_PORT) + { + FWP_VALUE0 dstPort; + + dstPort.type = FWP_UINT16; + dstPort.uint16 = pModificationData->transportData.destinationPort; + + status = KrnlHlprUDPHeaderModifyDestinationPort(&dstPort, + pNetBufferList, + udpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + break; + } + case IPPROTO_ICMPV6: + { + UINT32 icmpHeaderSize = ICMP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + icmpHeaderSize = pMetadata->transportHeaderSize; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_TYPE) + { + FWP_VALUE0 icmpType; + + icmpType.type = FWP_UINT8; + icmpType.uint8 = (UINT8)ntohs(pModificationData->transportData.sourcePort); + + status = KrnlHlprICMPv6HeaderModifyType(&icmpType, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_CODE) + { + FWP_VALUE0 icmpCode; + + icmpCode.type = FWP_UINT8; + icmpCode.uint8 = (UINT8)ntohs(pModificationData->transportData.destinationPort); + + status = KrnlHlprICMPv6HeaderModifyCode(&icmpCode, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + break; + } + } + + /// ToDo: Recalculate the Transport Checksum Here + + HLPR_BAIL_LABEL_2: + + /// return the data offset to the beginning of the IP Header + tmpStatus = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList), + ipHeaderSize, + 0, + 0); + if(tmpStatus != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtOutboundNetwork: NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + HLPR_BAIL_ON_FAILURE(status); + } +*/ + if(pModificationData->flags & PCPMDF_MODIFY_IP_HEADER) + { + if(pModificationData->ipData.flags & PCPMDF_MODIFY_IP_HEADER_SOURCE_ADDRESS) + { + FWP_VALUE value; + + RtlZeroMemory(&value, + sizeof(FWP_VALUE)); + + if(pCompletionData->pInjectionData->addressFamily == AF_INET) + { + value.type = FWP_UINT32; + + RtlCopyMemory(&(value.uint32), + pModificationData->ipData.sourceAddress.pIPv4, + IPV4_ADDRESS_SIZE); + } + else + { + HLPR_NEW(value.byteArray16, + FWP_BYTE_ARRAY16, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(value.byteArray16, + status); + + value.type = FWP_BYTE_ARRAY16_TYPE; + + RtlCopyMemory(value.byteArray16->byteArray16, + pModificationData->ipData.sourceAddress.pIPv6, + IPV6_ADDRESS_SIZE); + } + + status = KrnlHlprIPHeaderModifySourceAddress(&value, + pNetBufferList, + TRUE); + + KrnlHlprFwpValuePurgeLocalCopy(&value); + + HLPR_BAIL_ON_FAILURE(status); + } + + if(pModificationData->ipData.flags & PCPMDF_MODIFY_IP_HEADER_DESTINATION_ADDRESS) + { + FWP_VALUE value; + + RtlZeroMemory(&value, + sizeof(FWP_VALUE)); + + if(pCompletionData->pInjectionData->addressFamily == AF_INET) + { + value.type = FWP_UINT32; + + RtlCopyMemory(&(value.uint32), + pModificationData->ipData.destinationAddress.pIPv4, + IPV4_ADDRESS_SIZE); + } + else + { +#pragma warning(push) +#pragma warning(disable: 6014) /// value.byteArray16 will be freed in with call to KrnlHlprFwpValuePurgeLocalCopy + + HLPR_NEW(value.byteArray16, + FWP_BYTE_ARRAY16, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(value.byteArray16, + status); + +#pragma warning(pop) + + value.type = FWP_BYTE_ARRAY16_TYPE; + + RtlCopyMemory(value.byteArray16->byteArray16, + pModificationData->ipData.destinationAddress.pIPv6, + IPV6_ADDRESS_SIZE); + } + + status = KrnlHlprIPHeaderModifyDestinationAddress(&value, + pNetBufferList, + TRUE); + + KrnlHlprFwpValuePurgeLocalCopy(&value); + + HLPR_BAIL_ON_FAILURE(status); + } + } + } + + status = FwpsInjectNetworkSendAsync(pCompletionData->pInjectionData->injectionHandle, + pCompletionData->pInjectionData->injectionContext, + 0, + compartmentID, + pNetBufferList, + CompleteBasicPacketModification, + pCompletionData); + + NT_ASSERT(irql == KeGetCurrentIrql()); + + if(status != STATUS_SUCCESS) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtOutboundNetwork: FwpsInjectNetworkSendAsync() [status: %#x]\n", + status); + + HLPR_BAIL_LABEL: + + NT_ASSERT(status == STATUS_SUCCESS); + + if(status != STATUS_SUCCESS) + { + if(pNetBufferList) + { + FwpsFreeCloneNetBufferList(pNetBufferList, + 0); + + pNetBufferList = 0; + } + + if(pCompletionData) + BasicPacketModificationCompletionDataDestroy(&pCompletionData, + TRUE); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PerformBasicPacketModificationAtOutboundNetwork() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @private_function="PerformBasicPacketModificationAtForward" + + Purpose: Clones the NET_BUFFER_LIST, modifies it with data from the associated context and + injects the clone back to the stack's forward path using FwpsInjectForwardAsync().
+
+ Notes: Applies to the following forwarding layers:
+ FWPM_LAYER_IPFORWARD_V{4/6}
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551186.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF546324.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +NTSTATUS PerformBasicPacketModificationAtForward(_In_ CLASSIFY_DATA** ppClassifyData, + _In_ INJECTION_DATA** ppInjectionData, + _In_ PC_BASIC_PACKET_MODIFICATION_DATA* pModificationData, + _In_ BOOLEAN isInline = FALSE) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PerformBasicPacketModificationAtForward()\n"); + +#endif /// DBG + + NT_ASSERT(ppClassifyData); + NT_ASSERT(ppInjectionData); + NT_ASSERT(pModificationData); + NT_ASSERT(*ppClassifyData); + NT_ASSERT(*ppInjectionData); + + NTSTATUS status = STATUS_SUCCESS; + FWPS_INCOMING_VALUES* pClassifyValues = (FWPS_INCOMING_VALUES*)(*ppClassifyData)->pClassifyValues; + FWPS_INCOMING_METADATA_VALUES* pMetadata = (FWPS_INCOMING_METADATA_VALUES*)(*ppClassifyData)->pMetadataValues; + COMPARTMENT_ID compartmentID = DEFAULT_COMPARTMENT_ID; + IF_INDEX interfaceIndex = 0; + UINT32 flags = 0; + NET_BUFFER_LIST* pNetBufferList = 0; + BASIC_PACKET_MODIFICATION_COMPLETION_DATA* pCompletionData = 0; + UINT32 ipHeaderSize = 0; + FWP_VALUE* pInterfaceIndex = 0; + FWP_VALUE* pFlags = 0; + BOOLEAN isWeakHostReceive = FALSE; + BOOLEAN isWeakHostSend = FALSE; + PSTR pInjectionFn = "FwpsInjectForwardAsync"; + NDIS_TCP_IP_CHECKSUM_PACKET_INFO checksumInfo = {0}; + +#if DBG + + KIRQL irql = KeGetCurrentIrql(); + +#endif /// DBG + +#pragma warning(push) +#pragma warning(disable: 6014) /// pCompletionData will be freed in completionFn using BasicPacketModificationCompletionDataDestroy + + HLPR_NEW(pCompletionData, + BASIC_PACKET_MODIFICATION_COMPLETION_DATA, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pCompletionData, + status); + +#pragma warning(pop) + + KeInitializeSpinLock(&(pCompletionData->spinLock)); + + pCompletionData->performedInline = isInline; + pCompletionData->pClassifyData = *ppClassifyData; + pCompletionData->pInjectionData = *ppInjectionData; + + /// Responsibility for freeing this memory has been transferred to the pCompletionData + *ppClassifyData = 0; + + *ppInjectionData = 0; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_COMPARTMENT_ID)) + compartmentID = (COMPARTMENT_ID)pMetadata->compartmentId; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_IP_HEADER_SIZE)) + ipHeaderSize = pMetadata->ipHeaderSize; + + pInterfaceIndex = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_DESTINATION_INTERFACE_INDEX); + if(pInterfaceIndex && + pInterfaceIndex->type == FWP_UINT32) + interfaceIndex = (IF_INDEX)pInterfaceIndex->uint32; + + pFlags = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_FLAGS); + if(pFlags && + pFlags->type == FWP_UINT32) + flags = pFlags->uint32; + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + /// Determine if this is a weakhost forward + if(flags & FWP_CONDITION_FLAG_IS_INBOUND_PASS_THRU) + isWeakHostReceive = TRUE; + + if(flags & FWP_CONDITION_FLAG_IS_OUTBOUND_PASS_THRU) + isWeakHostSend = TRUE; + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + /// Initial offset is at the IP Header, so just clone the entire NET_BUFFER_LIST. + status = FwpsAllocateCloneNetBufferList((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket, + g_pNDISPoolData->nblPoolHandle, + g_pNDISPoolData->nbPoolHandle, + 0, + &pNetBufferList); + if(status != STATUS_SUCCESS || + !pNetBufferList) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtForward: FwpsAllocateCloneNetBufferList() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + checksumInfo.Value = (ULONG)(ULONG_PTR)NET_BUFFER_LIST_INFO((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket, + TcpIpChecksumNetBufferListInfo); + + /// Handle if this packet had the IP checksum offloaded or if it's loopback + if(checksumInfo.Receive.NdisPacketIpChecksumSucceeded || + flags & FWP_CONDITION_FLAG_IS_LOOPBACK) + { + /// Prevent TCP/IP Zone crossing and recalculate the checksums + if(flags & FWP_CONDITION_FLAG_IS_LOOPBACK) + { + FWP_VALUE* pLocalAddress = 0; + FWP_VALUE* pRemoteAddress = 0; + FWP_VALUE* pLoopbackAddress = 0; + + pLocalAddress = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_REMOTE_ADDRESS); + if(pLocalAddress && + ((pLocalAddress->type == FWP_UINT32 && + RtlCompareMemory(&(pLocalAddress->uint32), + IPV4_LOOPBACK_ADDRESS, + IPV4_ADDRESS_SIZE)) || + (pLocalAddress->type == FWP_BYTE_ARRAY16_TYPE && + RtlCompareMemory(pLocalAddress->byteArray16->byteArray16, + IPV6_LOOPBACK_ADDRESS, + IPV6_ADDRESS_SIZE)))) + pLoopbackAddress = pLocalAddress; + + if(!pLoopbackAddress) + { + pRemoteAddress = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_REMOTE_ADDRESS); + if(pRemoteAddress && + ((pRemoteAddress->type == FWP_UINT32 && + RtlCompareMemory(&(pRemoteAddress->uint32), + IPV4_LOOPBACK_ADDRESS, + IPV4_ADDRESS_SIZE)) || + (pRemoteAddress->type == FWP_BYTE_ARRAY16_TYPE && + RtlCompareMemory(pRemoteAddress->byteArray16->byteArray16, + IPV6_LOOPBACK_ADDRESS, + IPV6_ADDRESS_SIZE)))) + pLoopbackAddress = pRemoteAddress; + } + + if(pLoopbackAddress) + { + status = KrnlHlprIPHeaderModifyLoopbackToLocal(pMetadata, + pLoopbackAddress, + ipHeaderSize, + pNetBufferList, + (const WSACMSGHDR*)pCompletionData->pInjectionData->pControlData, + pCompletionData->pInjectionData->controlDataLength); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtForward: KrnlHlprIPHeaderModifyLoopbackToLocal() [status: %#x]\n", + status); + + HLPR_BAIL; + } + } + } + else + { + /// Recalculate the checksum + if(pCompletionData->pInjectionData->addressFamily == AF_INET) + KrnlHlprIPHeaderCalculateV4Checksum(pNetBufferList, + ipHeaderSize); + } + } + + pCompletionData->refCount = KrnlHlprNBLGetRequiredRefCount(pNetBufferList); + + if(pModificationData->flags) + { + /// Various checks and balances must be performed to modify the Transport header at this modification point. + /// Parsing of the headers will need to occur, as well as spot checking to verify everything is as it should be. + /// Additionally, checksum routines will need to be written to recalculate checksums for some of the headers. + /// The following block of code is to get you started with modifying the headers with info not readily available. + /// (i.e. header parsing has not occurred so there is no relevant classifiable data nor metadata present). +/* + if(pModificationData->flags & PCPMDF_MODIFY_TRANSPORT_HEADER) + { + UINT32 tmpStatus = STATUS_SUCCESS; + IPPROTO protocol = IPPROTO_MAX; + + protocol = KrnlHlprIPHeaderGetProtocolField(pNetBufferList, + pCompletionData->pInjectionData->addressFamily); + + /// The clone is at the IP Header, so advance by the size of the IP Header. + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList), + ipHeaderSize, + FALSE, + 0); + + switch(protocol) + { + case IPPROTO_ICMP: + { + UINT32 icmpHeaderSize = ICMP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + icmpHeaderSize = pMetadata->transportHeaderSize; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_TYPE) + { + FWP_VALUE0 icmpType; + + icmpType.type = FWP_UINT8; + icmpType.uint8 = (UINT8)ntohs(pModificationData->transportData.sourcePort); + + status = KrnlHlprICMPv4HeaderModifyType(&icmpType, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_CODE) + { + FWP_VALUE0 icmpCode; + + icmpCode.type = FWP_UINT8; + icmpCode.uint8 = (UINT8)ntohs(pModificationData->transportData.destinationPort); + + status = KrnlHlprICMPv4HeaderModifyCode(&icmpCode, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + break; + } + case IPPROTO_TCP: + { + UINT32 tcpHeaderSize = TCP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + tcpHeaderSize = pMetadata->transportHeaderSize; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_SOURCE_PORT) + { + FWP_VALUE0 srcPort; + + srcPort.type = FWP_UINT16; + srcPort.uint16 = pModificationData->transportData.sourcePort; + + status = KrnlHlprTCPHeaderModifySourcePort(&srcPort, + pNetBufferList, + tcpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_DESTINATION_PORT) + { + FWP_VALUE0 dstPort; + + dstPort.type = FWP_UINT16; + dstPort.uint16 = pModificationData->transportData.destinationPort; + + status = KrnlHlprTCPHeaderModifyDestinationPort(&dstPort, + pNetBufferList, + tcpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + break; + } + case IPPROTO_UDP: + { + UINT32 udpHeaderSize = UDP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + udpHeaderSize = pMetadata->transportHeaderSize; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_SOURCE_PORT) + { + FWP_VALUE0 srcPort; + + srcPort.type = FWP_UINT16; + srcPort.uint16 = pModificationData->transportData.sourcePort; + + status = KrnlHlprUDPHeaderModifySourcePort(&srcPort, + pNetBufferList, + udpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_DESTINATION_PORT) + { + FWP_VALUE0 dstPort; + + dstPort.type = FWP_UINT16; + dstPort.uint16 = pModificationData->transportData.destinationPort; + + status = KrnlHlprUDPHeaderModifyDestinationPort(&dstPort, + pNetBufferList, + udpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + break; + } + case IPPROTO_ICMPV6: + { + UINT32 icmpHeaderSize = ICMP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + icmpHeaderSize = pMetadata->transportHeaderSize; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_TYPE) + { + FWP_VALUE0 icmpType; + + icmpType.type = FWP_UINT8; + icmpType.uint8 = (UINT8)ntohs(pModificationData->transportData.sourcePort); + + status = KrnlHlprICMPv6HeaderModifyType(&icmpType, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_CODE) + { + FWP_VALUE0 icmpCode; + + icmpCode.type = FWP_UINT8; + icmpCode.uint8 = (UINT8)ntohs(pModificationData->transportData.destinationPort); + + status = KrnlHlprICMPv6HeaderModifyCode(&icmpCode, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + break; + } + } + + /// ToDo: Recalculate the Transport Checksum Here + + HLPR_BAIL_LABEL_2: + + /// return the data offset to the beginning of the IP Header + tmpStatus = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList), + ipHeaderSize, + 0, + 0); + if(tmpStatus != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtForward: NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + HLPR_BAIL_ON_FAILURE(status); + } +*/ + if(pModificationData->flags & PCPMDF_MODIFY_IP_HEADER) + { + if(pModificationData->ipData.flags & PCPMDF_MODIFY_IP_HEADER_INTERFACE_INDEX) + interfaceIndex = pModificationData->ipData.interfaceIndex; + + if(pModificationData->ipData.flags & PCPMDF_MODIFY_IP_HEADER_SOURCE_ADDRESS) + { + FWP_VALUE value; + + RtlZeroMemory(&value, + sizeof(FWP_VALUE)); + + if(pCompletionData->pInjectionData->addressFamily == AF_INET) + { + value.type = FWP_UINT32; + + RtlCopyMemory(&(value.uint32), + pModificationData->ipData.sourceAddress.pIPv4, + IPV4_ADDRESS_SIZE); + } + else + { + HLPR_NEW(value.byteArray16, + FWP_BYTE_ARRAY16, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(value.byteArray16, + status); + + value.type = FWP_BYTE_ARRAY16_TYPE; + + RtlCopyMemory(value.byteArray16->byteArray16, + pModificationData->ipData.sourceAddress.pIPv6, + IPV6_ADDRESS_SIZE); + } + + status = KrnlHlprIPHeaderModifySourceAddress(&value, + pNetBufferList, + TRUE); + + KrnlHlprFwpValuePurgeLocalCopy(&value); + + HLPR_BAIL_ON_FAILURE(status); + } + + if(pModificationData->ipData.flags & PCPMDF_MODIFY_IP_HEADER_DESTINATION_ADDRESS) + { + FWP_VALUE value; + + RtlZeroMemory(&value, + sizeof(FWP_VALUE)); + + if(pCompletionData->pInjectionData->addressFamily == AF_INET) + { + value.type = FWP_UINT32; + + RtlCopyMemory(&(value.uint32), + pModificationData->ipData.destinationAddress.pIPv4, + IPV4_ADDRESS_SIZE); + } + else + { +#pragma warning(push) +#pragma warning(disable: 6014) /// value.byteArray16 will be freed in with call to KrnlHlprFwpValuePurgeLocalCopy + + HLPR_NEW(value.byteArray16, + FWP_BYTE_ARRAY16, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(value.byteArray16, + status); + +#pragma warning(pop) + + value.type = FWP_BYTE_ARRAY16_TYPE; + + RtlCopyMemory(value.byteArray16->byteArray16, + pModificationData->ipData.destinationAddress.pIPv6, + IPV6_ADDRESS_SIZE); + } + + status = KrnlHlprIPHeaderModifyDestinationAddress(&value, + pNetBufferList, + TRUE); + + KrnlHlprFwpValuePurgeLocalCopy(&value); + + HLPR_BAIL_ON_FAILURE(status); + } + } + } + + /// If the Forwarded NBL is destined locally, inject using FwpsInjectNetworkReceiveAsync rather + /// than the traditional FwpsInjectForwardAsync otherwise STATUS_INVALID_PARAMETER will be + /// returned in the NBL.status and the injection fails. + if(isWeakHostReceive) + { + UINT32 index = WFPSAMPLER_INDEX; + IF_INDEX subInterfaceIndex = 0; + FWP_VALUE* pSubInterfaceIndex = 0; + + if(pCompletionData->pClassifyData->pFilter->subLayerWeight == FWPM_SUBLAYER_UNIVERSAL_WEIGHT) + index = UNIVERSAL_INDEX; + + if(pCompletionData->pInjectionData->addressFamily == AF_INET) + pCompletionData->pInjectionData->injectionHandle = g_pIPv4InboundNetworkInjectionHandles[index]; + else + pCompletionData->pInjectionData->injectionHandle = g_pIPv6InboundNetworkInjectionHandles[index]; + + pSubInterfaceIndex = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_DESTINATION_SUB_INTERFACE_INDEX); + if(pSubInterfaceIndex && + pSubInterfaceIndex->type == FWP_UINT32) + subInterfaceIndex = (IF_INDEX)pSubInterfaceIndex->uint32; + + status = FwpsInjectNetworkReceiveAsync(pCompletionData->pInjectionData->injectionHandle, + pCompletionData->pInjectionData->injectionContext, + 0, + compartmentID, + interfaceIndex, + subInterfaceIndex, + pNetBufferList, + CompleteBasicPacketInjection, + pCompletionData); + } + /// If the Forwarded NBL is sourced locally, but another interface, inject using + /// FwpsInjectNetworkSendAsync rather than the traditional FwpsInjectForwardAsync otherwise + /// STATUS_INVALID_PARAMETER will be returned in the NBL.status and the injection fails + else if(isWeakHostSend) + { + UINT32 index = WFPSAMPLER_INDEX; + + if(pCompletionData->pClassifyData->pFilter->subLayerWeight == FWPM_SUBLAYER_UNIVERSAL_WEIGHT) + index = UNIVERSAL_INDEX; + + if(pCompletionData->pInjectionData->addressFamily == AF_INET) + pCompletionData->pInjectionData->injectionHandle = g_pIPv4OutboundNetworkInjectionHandles[index]; + else + pCompletionData->pInjectionData->injectionHandle = g_pIPv6OutboundNetworkInjectionHandles[index]; + + status = FwpsInjectNetworkSendAsync(pCompletionData->pInjectionData->injectionHandle, + pCompletionData->pInjectionData->injectionContext, + 0, + compartmentID, + pNetBufferList, + CompleteBasicPacketInjection, + pCompletionData); + } + else + status = FwpsInjectForwardAsync(pCompletionData->pInjectionData->injectionHandle, + pCompletionData->pInjectionData->injectionContext, + 0, + pCompletionData->pInjectionData->addressFamily, + compartmentID, + interfaceIndex, + pNetBufferList, + CompleteBasicPacketModification, + pCompletionData); + + NT_ASSERT(irql == KeGetCurrentIrql()); + + if(status != STATUS_SUCCESS) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtForward: %s() [status: %#x]\n", + pInjectionFn, + status); + + HLPR_BAIL_LABEL: + + NT_ASSERT(status == STATUS_SUCCESS); + + if(status != STATUS_SUCCESS) + { + if(pNetBufferList) + { + FwpsFreeCloneNetBufferList(pNetBufferList, + 0); + + pNetBufferList = 0; + } + + if(pCompletionData) + BasicPacketModificationCompletionDataDestroy(&pCompletionData, + TRUE); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PerformBasicPacketModificationAtForward() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @private_function="PerformBasicPacketModificationAtInboundTransport" + + Purpose: Clones the NET_BUFFER_LIST, modifies it with data from the associated context, and + injects the clone back to the stack's inbound path from the incoming Transport + Layers using FwpsInjectTransportRecveiveAsync().
+
+ Notes: Applies to the following inbound layers:
+ FWPM_LAYER_INBOUND_TRANSPORT_V{4/6}
+ FWPM_LAYER_INBOUND_ICMP_ERROR_V{4/6}
+ FWPM_LAYER_DATAGRAM_DATA_V{4/6} (Inbound only)
+ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V{4/6} (Inbound only)
+ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V{4/6} (Inbound only)
+ FWPM_LAYER_ALE_AUTH_CONNECT_V{4/6} (Inbound, reauthorization only)
+ FWPM_LAYER_ALE_FLOW_ESTABLISHED_V{4/6} (Inbound, non-TCP only)
+ FWPM_LAYER_STREAM_PACKET_V{4/6} (Inbound only)
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551186.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF546324.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +NTSTATUS PerformBasicPacketModificationAtInboundTransport(_In_ CLASSIFY_DATA** ppClassifyData, + _In_ INJECTION_DATA** ppInjectionData, + _In_ PC_BASIC_PACKET_MODIFICATION_DATA* pModificationData, + _In_ BOOLEAN isInline = FALSE) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PerformBasicPacketModificationAtInboundTransport()\n"); + +#endif /// DBG + + NT_ASSERT(ppClassifyData); + NT_ASSERT(ppInjectionData); + NT_ASSERT(pModificationData); + NT_ASSERT(*ppClassifyData); + NT_ASSERT(*ppInjectionData); + + NTSTATUS status = STATUS_SUCCESS; + FWPS_INCOMING_VALUES* pClassifyValues = (FWPS_INCOMING_VALUES*)(*ppClassifyData)->pClassifyValues; + FWPS_INCOMING_METADATA_VALUES* pMetadata = (FWPS_INCOMING_METADATA_VALUES*)(*ppClassifyData)->pMetadataValues; + COMPARTMENT_ID compartmentID = DEFAULT_COMPARTMENT_ID; + IF_INDEX interfaceIndex = 0; + IF_INDEX subInterfaceIndex = 0; + UINT32 flags = 0; + NET_BUFFER_LIST* pNetBufferList = 0; + BASIC_PACKET_MODIFICATION_COMPLETION_DATA* pCompletionData = 0; + UINT32 ipHeaderSize = 0; + UINT32 transportHeaderSize = 0; + UINT32 bytesRetreated = 0; + IPPROTO protocol = IPPROTO_MAX; + FWP_VALUE* pProtocol = 0; + FWP_VALUE* pInterfaceIndex = 0; + FWP_VALUE* pSubInterfaceIndex = 0; + FWP_VALUE* pFlags = 0; + FWPS_PACKET_LIST_INFORMATION* pPacketInformation = 0; + BOOLEAN bypassInjection = FALSE; + UINT64 endpointHandle = 0; + BYTE* pSourceAddress = 0; + BYTE* pDestinationAddress = 0; + NDIS_TCP_IP_CHECKSUM_PACKET_INFO checksumInfo = {0}; + +#if DBG + + KIRQL irql = KeGetCurrentIrql(); + +#endif /// DBG + +#pragma warning(push) +#pragma warning(disable: 6014) /// pCompletionData will be freed in completionFn using BasicPacketModificationCompletionDataDestroy + + HLPR_NEW(pCompletionData, + BASIC_PACKET_MODIFICATION_COMPLETION_DATA, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pCompletionData, + status); + +#pragma warning(pop) + + KeInitializeSpinLock(&(pCompletionData->spinLock)); + + pCompletionData->performedInline = isInline; + pCompletionData->pClassifyData = *ppClassifyData; + pCompletionData->pInjectionData = *ppInjectionData; + + /// Responsibility for freeing this memory has been transferred to the pCompletionData + *ppClassifyData = 0; + + *ppInjectionData = 0; + + HLPR_NEW(pPacketInformation, + FWPS_PACKET_LIST_INFORMATION, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pPacketInformation, + status); + pInterfaceIndex = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_INTERFACE_INDEX); + if(pInterfaceIndex && + pInterfaceIndex->type == FWP_UINT32) + interfaceIndex = (IF_INDEX)pInterfaceIndex->uint32; + + pSubInterfaceIndex = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_SUB_INTERFACE_INDEX); + if(pSubInterfaceIndex && + pSubInterfaceIndex->type == FWP_UINT32) + subInterfaceIndex = (IF_INDEX)pSubInterfaceIndex->uint32; + + pFlags = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_FLAGS); + if(pFlags && + pFlags->type == FWP_UINT32) + flags = pFlags->uint32; + + if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4) + protocol = IPPROTO_ICMP; + else if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6) + protocol = IPPROTO_ICMPV6; + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + else if(pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V6) + protocol = IPPROTO_TCP; + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + else + { + pProtocol = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_PROTOCOL); + HLPR_BAIL_ON_NULL_POINTER(pProtocol); + + protocol = (IPPROTO)pProtocol->uint8; + } + + if(pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4) + { + ipHeaderSize = IPV4_HEADER_MIN_SIZE; + + if(protocol == IPPROTO_ICMP) + transportHeaderSize = ICMP_HEADER_MIN_SIZE; + else if(protocol == IPPROTO_TCP) + transportHeaderSize = TCP_HEADER_MIN_SIZE; + else if(protocol == IPPROTO_UDP) + transportHeaderSize = UDP_HEADER_MIN_SIZE; + } + else if(pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6) + { + ipHeaderSize = IPV6_HEADER_MIN_SIZE; + + if(protocol == IPPROTO_ICMPV6) + transportHeaderSize = ICMP_HEADER_MIN_SIZE; + else if(protocol == IPPROTO_TCP) + transportHeaderSize = TCP_HEADER_MIN_SIZE; + else if(protocol == IPPROTO_UDP) + transportHeaderSize = UDP_HEADER_MIN_SIZE; + } + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_COMPARTMENT_ID)) + compartmentID = (COMPARTMENT_ID)pMetadata->compartmentId; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_IP_HEADER_SIZE) && + pMetadata->ipHeaderSize) + ipHeaderSize = pMetadata->ipHeaderSize; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE) && + pMetadata->transportHeaderSize) + transportHeaderSize = pMetadata->transportHeaderSize; + + bytesRetreated = ipHeaderSize; + + if(protocol != IPPROTO_ICMP && + protocol != IPPROTO_ICMPV6) + { + if(!isInline && + protocol != IPPROTO_TCP && + !(protocol == IPPROTO_UDP && + flags & FWP_CONDITION_FLAG_IS_RAW_ENDPOINT) && + (pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6)) + { + /// For asynchronous execution, the drop will cause the stack to continue processing on the + /// NBL for auditing purposes. This processing retreats the NBL Offset to the Transport header. + /// We need to take this into account because we only took a reference on the NBL. + } + else + { + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + bytesRetreated += transportHeaderSize; + } + } + else + { + if(pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4 || + pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V6) + { + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + bytesRetreated += transportHeaderSize; + } + } + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_ENDPOINT_HANDLE)) + endpointHandle = pMetadata->transportEndpointHandle; + + /// Query to see if IPsec has applied tunnel mode SA's to this NET_BUFFER_LIST ... + status = FwpsGetPacketListSecurityInformation((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket, + FWPS_PACKET_LIST_INFORMATION_QUERY_ALL_INBOUND, + pPacketInformation); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtInboundTransport: FwpsGetPacketListSecurityInformation() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + /// ... if it has, then bypass the injection until the NET_BUFFER_LIST has come out of the tunnel + if((pPacketInformation->ipsecInformation.inbound.isTunnelMode && + !(pPacketInformation->ipsecInformation.inbound.isDeTunneled)) || + pPacketInformation->ipsecInformation.inbound.isSecure) + { + bypassInjection = TRUE; + + HLPR_BAIL; + } + + /// Initial offset is at the data, so retreat the size of the IP Header and Transport Header ... + /// except for ICMP, offset is at the ICMP Header, so retreat the size of the IP Header ... + status = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket), + bytesRetreated, + 0, + 0); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtInboundTransport: NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + /// ... clone the entire NET_BUFFER_LIST ... + status = FwpsAllocateCloneNetBufferList((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket, + g_pNDISPoolData->nblPoolHandle, + g_pNDISPoolData->nbPoolHandle, + 0, + &pNetBufferList); + + /// ... and advance the offset back to the original position. + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket), + bytesRetreated, + FALSE, + 0); + if(status != STATUS_SUCCESS || + !pNetBufferList) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtInboundTransport: FwpsAllocateCloneNetBufferList() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + checksumInfo.Value = (ULONG)(ULONG_PTR)NET_BUFFER_LIST_INFO((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket, + TcpIpChecksumNetBufferListInfo); + + /// Handle if the packet was IPsec secured + if(pCompletionData->pInjectionData->isIPsecSecured) + { + /// For performance reasons, IPsec leaves the original ESP / AH information in the IP Header ... + UINT32 headerIncludeSize = 0; + UINT32 ipv4Address = 0; + UINT32 addressSize = 0; + FWP_VALUE* pRemoteAddressValue = 0; + FWP_VALUE* pLocalAddressValue = 0; + FWP_VALUE* pProtocolValue = 0; + + pRemoteAddressValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_REMOTE_ADDRESS); + if(pRemoteAddressValue) + { + if(pRemoteAddressValue->type == FWP_BYTE_ARRAY16_TYPE) + addressSize = IPV6_ADDRESS_SIZE; + else + addressSize = IPV4_ADDRESS_SIZE; + + HLPR_NEW_ARRAY(pSourceAddress, + BYTE, + addressSize, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pSourceAddress, + status); + + if(pRemoteAddressValue->type == FWP_BYTE_ARRAY16_TYPE) + RtlCopyMemory(pSourceAddress, + pRemoteAddressValue->byteArray16->byteArray16, + addressSize); + else + { + ipv4Address = htonl(pRemoteAddressValue->uint32); + + RtlCopyMemory(pSourceAddress, + &ipv4Address, + addressSize); + } + } + + pLocalAddressValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_LOCAL_ADDRESS); + if(pLocalAddressValue) + { + if(pLocalAddressValue->type == FWP_BYTE_ARRAY16_TYPE) + addressSize = IPV6_ADDRESS_SIZE; + else + addressSize = IPV4_ADDRESS_SIZE; + + HLPR_NEW_ARRAY(pDestinationAddress, + BYTE, + addressSize, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pDestinationAddress, + status); + + if(pLocalAddressValue->type == FWP_BYTE_ARRAY16_TYPE) + RtlCopyMemory(pDestinationAddress, + pLocalAddressValue->byteArray16->byteArray16, + addressSize); + else + { + ipv4Address = htonl(pLocalAddressValue->uint32); + + RtlCopyMemory(pDestinationAddress, + &ipv4Address, + addressSize); + } + } + + pProtocolValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_PROTOCOL); + if(pProtocolValue && + pProtocolValue->type == FWP_UINT8) + protocol = (IPPROTO)pProtocolValue->uint8; + else + protocol = IPPROTO_MAX; + + NT_ASSERT(protocol != IPPROTO_MAX); + +#if (NTDDI_VERSION >= NTDDI_WIN6SP1) + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_INCLUDE_HEADER)) + headerIncludeSize = pMetadata->headerIncludeHeaderLength; + +#endif // (NTDDI_VERSION >= NTDDI_WIN6SP1) + + if(pSourceAddress == 0 || + pDestinationAddress == 0) + { + status = STATUS_INVALID_MEMBER; + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtInboundTransport() [status: %#x][pSourceAddress: %#p][pDestinationAddress: %#p]\n", + status, + pSourceAddress, + pDestinationAddress); + + HLPR_BAIL; + } + + /// ... so we must re-construct the IPHeader with the appropriate information + /// for checksum offload, this will recalculate the checksums + status = FwpsConstructIpHeaderForTransportPacket(pNetBufferList, + headerIncludeSize, + pCompletionData->pInjectionData->addressFamily, + pSourceAddress, + pDestinationAddress, + protocol, + endpointHandle, + (const WSACMSGHDR*)pCompletionData->pInjectionData->pControlData, + pCompletionData->pInjectionData->controlDataLength, + 0, + 0, + interfaceIndex, + subInterfaceIndex); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketInjectionAtInboundTransport: FwpsConstructIpHeaderForTransportPacket() [status: %#x]\n", + status); + + HLPR_BAIL; + } + } + /// Handle if this packet had the IP or Transport checksums offloaded or if it's loopback + else if(checksumInfo.Receive.NdisPacketIpChecksumSucceeded || + checksumInfo.Receive.NdisPacketTcpChecksumSucceeded || + checksumInfo.Receive.NdisPacketUdpChecksumSucceeded || + flags & FWP_CONDITION_FLAG_IS_LOOPBACK) + { + /// Prevent TCP/IP Zone crossing and recalculate the checksums + if(flags & FWP_CONDITION_FLAG_IS_LOOPBACK) + { + FWP_VALUE* pLocalAddress = 0; + FWP_VALUE* pRemoteAddress = 0; + FWP_VALUE* pLoopbackAddress = 0; + + pLocalAddress = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_REMOTE_ADDRESS); + if(pLocalAddress && + ((pLocalAddress->type == FWP_UINT32 && + RtlCompareMemory(&(pLocalAddress->uint32), + IPV4_LOOPBACK_ADDRESS, + IPV4_ADDRESS_SIZE)) || + (pLocalAddress->type == FWP_BYTE_ARRAY16_TYPE && + RtlCompareMemory(pLocalAddress->byteArray16->byteArray16, + IPV6_LOOPBACK_ADDRESS, + IPV6_ADDRESS_SIZE)))) + pLoopbackAddress = pLocalAddress; + + if(!pLoopbackAddress) + { + pRemoteAddress = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_REMOTE_ADDRESS); + if(pRemoteAddress && + ((pRemoteAddress->type == FWP_UINT32 && + RtlCompareMemory(&(pRemoteAddress->uint32), + IPV4_LOOPBACK_ADDRESS, + IPV4_ADDRESS_SIZE)) || + (pRemoteAddress->type == FWP_BYTE_ARRAY16_TYPE && + RtlCompareMemory(pRemoteAddress->byteArray16->byteArray16, + IPV6_LOOPBACK_ADDRESS, + IPV6_ADDRESS_SIZE)))) + pLoopbackAddress = pRemoteAddress; + } + + if(pLoopbackAddress) + { + status = KrnlHlprIPHeaderModifyLoopbackToLocal(pMetadata, + pLoopbackAddress, + ipHeaderSize, + pNetBufferList, + (const WSACMSGHDR*)pCompletionData->pInjectionData->pControlData, + pCompletionData->pInjectionData->controlDataLength); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtInboundTransport: KrnlHlprIPHeaderModifyLoopbackToLocal() [status: %#x]\n", + status); + + HLPR_BAIL; + } + } + } + else + { + /// Recalculate the checksum + if(pCompletionData->pInjectionData->addressFamily == AF_INET) + KrnlHlprIPHeaderCalculateV4Checksum(pNetBufferList, + ipHeaderSize); + } + } + + pCompletionData->refCount = KrnlHlprNBLGetRequiredRefCount(pNetBufferList); + + if(pModificationData->flags) + { + FWP_VALUE* pLocalAddressValue = 0; + FWP_VALUE* pRemoteAddressValue = 0; + BYTE pIPDestinationAddress[16] = {0}; + BYTE pIPSourceAddress[16] = {0}; + + pRemoteAddressValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_REMOTE_ADDRESS); + HLPR_BAIL_ON_NULL_POINTER_WITH_STATUS(pRemoteAddressValue, + status); + + pLocalAddressValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_LOCAL_ADDRESS); + HLPR_BAIL_ON_NULL_POINTER_WITH_STATUS(pLocalAddressValue, + status); + + if(pCompletionData->pInjectionData->addressFamily == AF_INET6) + { + RtlCopyMemory(pIPSourceAddress, + pRemoteAddressValue->byteArray16->byteArray16, + IPV6_ADDRESS_SIZE); + + RtlCopyMemory(pIPDestinationAddress, + pLocalAddressValue->byteArray16->byteArray16, + IPV6_ADDRESS_SIZE); + } + else + { + UINT32 sourceAddress = htonl(pRemoteAddressValue->uint32); + UINT32 destinationAddress = htonl(pLocalAddressValue->uint32); + + RtlCopyMemory(pIPSourceAddress, + &sourceAddress, + IPV4_ADDRESS_SIZE); + + RtlCopyMemory(pIPDestinationAddress, + &destinationAddress, + IPV4_ADDRESS_SIZE); + } + + if(pModificationData->flags & PCPMDF_MODIFY_TRANSPORT_HEADER) + { + UINT32 tmpStatus = STATUS_SUCCESS; + FWP_VALUE* pProtocolValue = 0; + + /// The clone is at the IP Header, so advance by the size of the IP Header. + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList), + ipHeaderSize, + FALSE, + 0); + + pProtocolValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_PROTOCOL); + if(pProtocolValue && + pProtocolValue->type == FWP_UINT8) + protocol = (IPPROTO)pProtocolValue->uint8; + else + protocol = IPPROTO_MAX; + + NT_ASSERT(protocol != IPPROTO_MAX); + + if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V4) + protocol = IPPROTO_ICMP; + else if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V6) + protocol = IPPROTO_ICMPV6; + + switch(protocol) + { + case IPPROTO_ICMP: + { + UINT32 icmpHeaderSize = transportHeaderSize ? transportHeaderSize : ICMP_HEADER_MIN_SIZE; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_TYPE) + { + FWP_VALUE0 icmpType; + + icmpType.type = FWP_UINT8; + icmpType.uint8 = (UINT8)ntohs(pModificationData->transportData.sourcePort); + + status = KrnlHlprICMPv4HeaderModifyType(&icmpType, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_CODE) + { + FWP_VALUE0 icmpCode; + + icmpCode.type = FWP_UINT8; + icmpCode.uint8 = (UINT8)ntohs(pModificationData->transportData.destinationPort); + + status = KrnlHlprICMPv4HeaderModifyCode(&icmpCode, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + break; + } + case IPPROTO_TCP: + { + UINT32 tcpHeaderSize = transportHeaderSize ? transportHeaderSize : TCP_HEADER_MIN_SIZE; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_SOURCE_PORT) + { + FWP_VALUE0 srcPort; + + srcPort.type = FWP_UINT16; + srcPort.uint16 = pModificationData->transportData.sourcePort; + + status = KrnlHlprTCPHeaderModifySourcePort(&srcPort, + pNetBufferList, + tcpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_DESTINATION_PORT) + { + FWP_VALUE0 dstPort; + + dstPort.type = FWP_UINT16; + dstPort.uint16 = pModificationData->transportData.destinationPort; + + status = KrnlHlprTCPHeaderModifyDestinationPort(&dstPort, + pNetBufferList, + tcpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + break; + } + case IPPROTO_UDP: + { + UINT32 udpHeaderSize = transportHeaderSize ? transportHeaderSize : UDP_HEADER_MIN_SIZE; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_SOURCE_PORT) + { + FWP_VALUE0 srcPort; + + srcPort.type = FWP_UINT16; + srcPort.uint16 = pModificationData->transportData.sourcePort; + + status = KrnlHlprUDPHeaderModifySourcePort(&srcPort, + pNetBufferList, + udpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_DESTINATION_PORT) + { + FWP_VALUE0 dstPort; + + dstPort.type = FWP_UINT16; + dstPort.uint16 = pModificationData->transportData.destinationPort; + + status = KrnlHlprUDPHeaderModifyDestinationPort(&dstPort, + pNetBufferList, + udpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + break; + } + case IPPROTO_ICMPV6: + { + UINT32 icmpHeaderSize = transportHeaderSize ? transportHeaderSize : ICMP_HEADER_MIN_SIZE; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_TYPE) + { + FWP_VALUE0 icmpType; + + icmpType.type = FWP_UINT8; + icmpType.uint8 = (UINT8)ntohs(pModificationData->transportData.sourcePort); + + status = KrnlHlprICMPv6HeaderModifyType(&icmpType, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_CODE) + { + FWP_VALUE0 icmpCode; + + icmpCode.type = FWP_UINT8; + icmpCode.uint8 = (UINT8)ntohs(pModificationData->transportData.destinationPort); + + status = KrnlHlprICMPv6HeaderModifyCode(&icmpCode, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + break; + } + } + + HLPR_BAIL_LABEL_2: + + /// return the data offset to the beginning of the IP Header + tmpStatus = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList), + ipHeaderSize, + 0, + 0); + if(tmpStatus != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtInboundTransport: NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + HLPR_BAIL_ON_FAILURE(status); + } + + if(pModificationData->flags & PCPMDF_MODIFY_IP_HEADER) + { + if(pModificationData->ipData.flags & PCPMDF_MODIFY_IP_HEADER_INTERFACE_INDEX) + interfaceIndex = pModificationData->ipData.interfaceIndex; + + if(pModificationData->ipData.flags & PCPMDF_MODIFY_IP_HEADER_SOURCE_ADDRESS) + { + FWP_VALUE value; + + RtlZeroMemory(&value, + sizeof(FWP_VALUE)); + + if(pCompletionData->pInjectionData->addressFamily == AF_INET) + { + value.type = FWP_UINT32; + + RtlCopyMemory(&(value.uint32), + pModificationData->ipData.sourceAddress.pIPv4, + IPV4_ADDRESS_SIZE); + + RtlCopyMemory(pIPSourceAddress, + pModificationData->ipData.sourceAddress.pIPv4, + IPV4_ADDRESS_SIZE); + } + else + { + HLPR_NEW(value.byteArray16, + FWP_BYTE_ARRAY16, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(value.byteArray16, + status); + + value.type = FWP_BYTE_ARRAY16_TYPE; + + RtlCopyMemory(value.byteArray16->byteArray16, + pModificationData->ipData.sourceAddress.pIPv6, + IPV6_ADDRESS_SIZE); + + RtlCopyMemory(pIPSourceAddress, + pModificationData->ipData.sourceAddress.pIPv6, + IPV6_ADDRESS_SIZE); + } + + status = KrnlHlprIPHeaderModifySourceAddress(&value, + pNetBufferList, + FALSE); + + KrnlHlprFwpValuePurgeLocalCopy(&value); + + HLPR_BAIL_ON_FAILURE(status); + } + + if(pModificationData->ipData.flags & PCPMDF_MODIFY_IP_HEADER_DESTINATION_ADDRESS) + { + FWP_VALUE value; + + RtlZeroMemory(&value, + sizeof(FWP_VALUE)); + + if(pCompletionData->pInjectionData->addressFamily == AF_INET) + { + value.type = FWP_UINT32; + + RtlCopyMemory(&(value.uint32), + pModificationData->ipData.destinationAddress.pIPv4, + IPV4_ADDRESS_SIZE); + + RtlCopyMemory(pIPDestinationAddress, + pModificationData->ipData.destinationAddress.pIPv4, + IPV4_ADDRESS_SIZE); + } + else + { +#pragma warning(push) +#pragma warning(disable: 6014) /// value.byteArray16 will be freed in with call to KrnlHlprFwpValuePurgeLocalCopy + + HLPR_NEW(value.byteArray16, + FWP_BYTE_ARRAY16, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(value.byteArray16, + status); + +#pragma warning(pop) + + value.type = FWP_BYTE_ARRAY16_TYPE; + + RtlCopyMemory(value.byteArray16->byteArray16, + pModificationData->ipData.destinationAddress.pIPv6, + IPV6_ADDRESS_SIZE); + + RtlCopyMemory(pIPDestinationAddress, + pModificationData->ipData.destinationAddress.pIPv6, + IPV6_ADDRESS_SIZE); + } + + status = KrnlHlprIPHeaderModifyDestinationAddress(&value, + pNetBufferList, + FALSE); + + KrnlHlprFwpValuePurgeLocalCopy(&value); + + HLPR_BAIL_ON_FAILURE(status); + + } + } + + status = FwpsConstructIpHeaderForTransportPacket(pNetBufferList, + ipHeaderSize, + pCompletionData->pInjectionData->addressFamily, + (UCHAR*)pIPSourceAddress, + (UCHAR*)pIPDestinationAddress, + protocol, + endpointHandle, + (const WSACMSGHDR*)pCompletionData->pInjectionData->pControlData, + pCompletionData->pInjectionData->controlDataLength, + 0, + 0, + interfaceIndex, + subInterfaceIndex); + HLPR_BAIL_ON_FAILURE(status); + } + + status = FwpsInjectTransportReceiveAsync(pCompletionData->pInjectionData->injectionHandle, + pCompletionData->pInjectionData->injectionContext, + 0, + 0, + pCompletionData->pInjectionData->addressFamily, + compartmentID, + interfaceIndex, + subInterfaceIndex, + pNetBufferList, + CompleteBasicPacketModification, + pCompletionData); + + NT_ASSERT(irql == KeGetCurrentIrql()); + + if(status != STATUS_SUCCESS) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketInjectionAtInboundTransport: FwpsInjectTransportReceiveAsync() [status: %#x]\n", + status); + + HLPR_BAIL_LABEL: + + NT_ASSERT(status == STATUS_SUCCESS); + + if(status != STATUS_SUCCESS || + bypassInjection) + { + if(pNetBufferList) + { + FwpsFreeCloneNetBufferList(pNetBufferList, + 0); + + pNetBufferList = 0; + } + + if(pCompletionData) + BasicPacketModificationCompletionDataDestroy(&pCompletionData, + TRUE); + } + + HLPR_DELETE_ARRAY(pSourceAddress, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + + HLPR_DELETE_ARRAY(pDestinationAddress, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + + HLPR_DELETE(pPacketInformation, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PerformBasicPacketModificationAtInboundTransport() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @private_function="PerformBasicPacketModificationAtOutboundTransport" + + Purpose: Clones the NET_BUFFER_LIST, modifies it with data from the associated context, and + injects the clone back to the stack's outbound path from the outgoing Transport + Layers using FwpsInjectTransportSendAsync().
+
+ Notes: Applies to the following outbound layers:
+ FWPM_LAYER_OUTBOUND_TRANSPORT_V{4/6}
+ FWPM_LAYER_OUTBOUND_ICMP_ERROR_V{4/6}
+ FWPM_LAYER_DATAGRAM_DATA_V{4/6} (Outbound only)
+ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V{4/6} (Outbound only)
+ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V{4/6} (Outbound only)
+ FWPM_LAYER_ALE_AUTH_CONNECT_V{4/6} (Outbound, reauthorization only)
+ FWPM_LAYER_ALE_FLOW_ESTABLISHED_V{4/6} (Outbound, non-TCP only)
+ FWPM_LAYER_STREAM_PACKET_V{4/6} (Outbound only)
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551188.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF546324.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +NTSTATUS PerformBasicPacketModificationAtOutboundTransport(_In_ CLASSIFY_DATA** ppClassifyData, + _In_ INJECTION_DATA** ppInjectionData, + _In_ PC_BASIC_PACKET_MODIFICATION_DATA* pModificationData, + _In_ BOOLEAN isInline = FALSE) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PerformBasicPacketModificationAtOutboundTransport()\n"); + +#endif /// DBG + + NT_ASSERT(ppClassifyData); + NT_ASSERT(ppInjectionData); + NT_ASSERT(pModificationData); + NT_ASSERT(*ppClassifyData); + NT_ASSERT(*ppInjectionData); + + NTSTATUS status = STATUS_SUCCESS; + FWPS_INCOMING_VALUES* pClassifyValues = (FWPS_INCOMING_VALUES*)(*ppClassifyData)->pClassifyValues; + FWPS_INCOMING_METADATA_VALUES* pMetadata = (FWPS_INCOMING_METADATA_VALUES*)(*ppClassifyData)->pMetadataValues; + UINT64 endpointHandle = 0; + FWPS_TRANSPORT_SEND_PARAMS* pSendParams = 0; + COMPARTMENT_ID compartmentID = DEFAULT_COMPARTMENT_ID; + NET_BUFFER_LIST* pNetBufferList = 0; + BASIC_PACKET_MODIFICATION_COMPLETION_DATA* pCompletionData = 0; + BYTE* pRemoteAddress = 0; + FWP_VALUE* pAddressValue = 0; + +#if DBG + + KIRQL irql = KeGetCurrentIrql(); + +#endif /// DBG + +#pragma warning(push) +#pragma warning(disable: 6014) /// pCompletionData & pSendParams will be freed in completionFn using BasicPacketModificationCompletionDataDestroy + + HLPR_NEW(pCompletionData, + BASIC_PACKET_MODIFICATION_COMPLETION_DATA, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pCompletionData, + status); + + HLPR_NEW(pSendParams, + FWPS_TRANSPORT_SEND_PARAMS, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pSendParams, + status); + +#pragma warning(pop) + + KeInitializeSpinLock(&(pCompletionData->spinLock)); + + pCompletionData->performedInline = isInline; + pCompletionData->pClassifyData = *ppClassifyData; + pCompletionData->pInjectionData = *ppInjectionData; + pCompletionData->pSendParams = pSendParams; + + /// Responsibility for freeing this memory has been transferred to the pCompletionData + *ppClassifyData = 0; + + *ppInjectionData = 0; + + pSendParams = 0; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_ENDPOINT_HANDLE)) + endpointHandle = pMetadata->transportEndpointHandle; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_COMPARTMENT_ID)) + compartmentID = (COMPARTMENT_ID)pMetadata->compartmentId; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_CONTROL_DATA)) + { + pSendParams->controlData = pMetadata->controlData; + pSendParams->controlDataLength = pMetadata->controlDataLength; + } + + pAddressValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_REMOTE_ADDRESS); + if(pAddressValue) + { + if(pCompletionData->pInjectionData->addressFamily == AF_INET) + { + UINT32 tempAddress = htonl(pAddressValue->uint32); + + HLPR_NEW_ARRAY(pRemoteAddress, + BYTE, + IPV4_ADDRESS_SIZE, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pRemoteAddress, + status); + + RtlCopyMemory(pRemoteAddress, + &tempAddress, + IPV4_ADDRESS_SIZE); + } + else + { +#pragma warning(push) +#pragma warning(disable: 6014) /// pRemoteAddress will be freed in completionFn using BasicPacketModificationCompletionDataDestroy + + HLPR_NEW_ARRAY(pRemoteAddress, + BYTE, + IPV6_ADDRESS_SIZE, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pRemoteAddress, + status); + +#pragma warning(pop) + + RtlCopyMemory(pRemoteAddress, + pAddressValue->byteArray16->byteArray16, + IPV6_ADDRESS_SIZE); + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_REMOTE_SCOPE_ID)) + pCompletionData->pSendParams->remoteScopeId = pMetadata->remoteScopeId; + } + + pCompletionData->pSendParams->remoteAddress = pRemoteAddress; + } + + pCompletionData->pSendParams->controlData = (WSACMSGHDR*)pCompletionData->pInjectionData->pControlData; + pCompletionData->pSendParams->controlDataLength = pCompletionData->pInjectionData->controlDataLength; + + /// Initial offset is at Transport Header (no IP Header yet), so just clone entire NET_BUFFER_LIST. + status = FwpsAllocateCloneNetBufferList((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket, + g_pNDISPoolData->nblPoolHandle, + g_pNDISPoolData->nbPoolHandle, + 0, + &pNetBufferList); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtOutboundTransport: FwpsAllocateCloneNetBufferList() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + pCompletionData->refCount = KrnlHlprNBLGetRequiredRefCount(pNetBufferList); + + if(pModificationData->flags) + { + if(pModificationData->flags & PCPMDF_MODIFY_TRANSPORT_HEADER) + { + FWP_VALUE* pProtocolValue = 0; + IPPROTO protocol = IPPROTO_MAX; + + pProtocolValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_PROTOCOL); + if(pProtocolValue && + pProtocolValue->type == FWP_UINT8) + protocol = (IPPROTO)pProtocolValue->uint8; + + if(pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4) + protocol = IPPROTO_ICMP; + else if(pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6) + protocol = IPPROTO_ICMPV6; + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + else if(pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V6) + protocol = IPPROTO_TCP; + +#endif + + switch(protocol) + { + case IPPROTO_ICMP: + { + UINT32 icmpHeaderSize = ICMP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + icmpHeaderSize = pMetadata->transportHeaderSize; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_TYPE) + { + FWP_VALUE0 icmpType; + + icmpType.type = FWP_UINT8; + icmpType.uint8 = (UINT8)ntohs(pModificationData->transportData.sourcePort); + + status = KrnlHlprICMPv4HeaderModifyType(&icmpType, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_CODE) + { + FWP_VALUE0 icmpCode; + + icmpCode.type = FWP_UINT8; + icmpCode.uint8 = (UINT8)ntohs(pModificationData->transportData.destinationPort); + + status = KrnlHlprICMPv4HeaderModifyCode(&icmpCode, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE(status); + } + + break; + } + case IPPROTO_TCP: + { + UINT32 tcpHeaderSize = TCP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + tcpHeaderSize = pMetadata->transportHeaderSize; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_SOURCE_PORT) + { + FWP_VALUE0 srcPort; + + srcPort.type = FWP_UINT16; + srcPort.uint16 = pModificationData->transportData.sourcePort; + + status = KrnlHlprTCPHeaderModifySourcePort(&srcPort, + pNetBufferList, + tcpHeaderSize); + HLPR_BAIL_ON_FAILURE(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_DESTINATION_PORT) + { + FWP_VALUE0 dstPort; + + dstPort.type = FWP_UINT16; + dstPort.uint16 = pModificationData->transportData.destinationPort; + + status = KrnlHlprTCPHeaderModifyDestinationPort(&dstPort, + pNetBufferList, + tcpHeaderSize); + HLPR_BAIL_ON_FAILURE(status); + } + + break; + } + case IPPROTO_UDP: + { + UINT32 udpHeaderSize = UDP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + udpHeaderSize = pMetadata->transportHeaderSize; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_SOURCE_PORT) + { + FWP_VALUE0 srcPort; + + srcPort.type = FWP_UINT16; + srcPort.uint16 = pModificationData->transportData.sourcePort; + + status = KrnlHlprUDPHeaderModifySourcePort(&srcPort, + pNetBufferList, + udpHeaderSize); + HLPR_BAIL_ON_FAILURE(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_DESTINATION_PORT) + { + FWP_VALUE0 dstPort; + + dstPort.type = FWP_UINT16; + dstPort.uint16 = pModificationData->transportData.destinationPort; + + status = KrnlHlprUDPHeaderModifyDestinationPort(&dstPort, + pNetBufferList, + udpHeaderSize); + HLPR_BAIL_ON_FAILURE(status); + } + + break; + } + case IPPROTO_ICMPV6: + { + UINT32 icmpHeaderSize = ICMP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + icmpHeaderSize = pMetadata->transportHeaderSize; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_TYPE) + { + FWP_VALUE0 icmpType; + + icmpType.type = FWP_UINT8; + icmpType.uint8 = (UINT8)ntohs(pModificationData->transportData.sourcePort); + + status = KrnlHlprICMPv6HeaderModifyType(&icmpType, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_CODE) + { + FWP_VALUE0 icmpCode; + + icmpCode.type = FWP_UINT8; + icmpCode.uint8 = (UINT8)ntohs(pModificationData->transportData.destinationPort); + + status = KrnlHlprICMPv6HeaderModifyCode(&icmpCode, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE(status); + } + + break; + } + } + } + + /// As there is no IP Header yet, we can only modify the destination IP address via the FWPS_TRANSPORT_SEND_PARAMS + if(pModificationData->flags & PCPMDF_MODIFY_IP_HEADER) + { + if(pModificationData->ipData.flags & PCPMDF_MODIFY_IP_HEADER_DESTINATION_ADDRESS) + { + if(pCompletionData->pInjectionData->addressFamily == AF_INET) + RtlCopyMemory(pCompletionData->pSendParams->remoteAddress, + pModificationData->ipData.destinationAddress.pIPv4, + IPV4_ADDRESS_SIZE); + else + RtlCopyMemory(pCompletionData->pSendParams->remoteAddress, + pModificationData->ipData.destinationAddress.pIPv6, + IPV6_ADDRESS_SIZE); + } + } + } + + status = FwpsInjectTransportSendAsync(pCompletionData->pInjectionData->injectionHandle, + pCompletionData->pInjectionData->injectionContext, + endpointHandle, + 0, + pCompletionData->pSendParams, + pCompletionData->pInjectionData->addressFamily, + compartmentID, + pNetBufferList, + CompleteBasicPacketModification, + pCompletionData); + + NT_ASSERT(irql == KeGetCurrentIrql()); + + if(status != STATUS_SUCCESS) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtOutboundTransport: FwpsInjectTransportSendAsync() [status: %#x]\n", + status); + + HLPR_BAIL_LABEL: + + NT_ASSERT(status == STATUS_SUCCESS); + + if(status != STATUS_SUCCESS) + { + if(pNetBufferList) + { + FwpsFreeCloneNetBufferList(pNetBufferList, + 0); + + pNetBufferList = 0; + } + + if(pCompletionData) + BasicPacketModificationCompletionDataDestroy(&pCompletionData, + TRUE); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PerformBasicPacketModificationAtOutboundTransport() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @private_function="BasicPacketModificationDeferredProcedureCall" + + Purpose: Invokes the appropriate private injection routine to perform the injection at + DISPATCH_LEVEL.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF542972.aspx
+*/ +_IRQL_requires_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Function_class_(KDEFERRED_ROUTINE) +VOID BasicPacketModificationDeferredProcedureCall(_In_ KDPC* pDPC, + _In_opt_ PVOID pContext, + _In_opt_ PVOID pArg1, + _In_opt_ PVOID pArg2) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> BasicPacketModificationDeferredProcedureCall()\n"); + +#endif /// DBG + + UNREFERENCED_PARAMETER(pDPC); + UNREFERENCED_PARAMETER(pContext); + UNREFERENCED_PARAMETER(pArg2); + + NT_ASSERT(pDPC); + NT_ASSERT(pArg1); + NT_ASSERT(((DPC_DATA*)pArg1)->pClassifyData); + NT_ASSERT(((DPC_DATA*)pArg1)->pInjectionData); + + DPC_DATA* pDPCData = (DPC_DATA*)pArg1; + + if(pDPCData) + { + NTSTATUS status = STATUS_SUCCESS; + FWPS_INCOMING_VALUES* pClassifyValues = (FWPS_INCOMING_VALUES*)pDPCData->pClassifyData->pClassifyValues; + FWPS_FILTER* pFilter = (FWPS_FILTER*)pDPCData->pClassifyData->pFilter; + PC_BASIC_PACKET_MODIFICATION_DATA* pModificationData = (PC_BASIC_PACKET_MODIFICATION_DATA*)pFilter->providerContext->dataBuffer->data; + + if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6) + status = PerformBasicPacketModificationAtInboundNetwork(&(pDPCData->pClassifyData), + &(pDPCData->pInjectionData), + pModificationData, + FALSE); + else if(pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V6) + status = PerformBasicPacketModificationAtOutboundNetwork(&(pDPCData->pClassifyData), + &(pDPCData->pInjectionData), + pModificationData, + FALSE); + else if(pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V4 || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V6) + status = PerformBasicPacketModificationAtForward(&(pDPCData->pClassifyData), + &(pDPCData->pInjectionData), + pModificationData, + FALSE); + else if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V6 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V6 || + (pDPCData->pInjectionData->direction == FWP_DIRECTION_INBOUND && + (pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4 || + pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4 || /// Policy Change Reauthorization + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V6 || /// Policy Change Reauthorization + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6))) + status = PerformBasicPacketModificationAtInboundTransport(&(pDPCData->pClassifyData), + &(pDPCData->pInjectionData), + pModificationData, + FALSE); + else if(pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6 || + (pDPCData->pInjectionData->direction == FWP_DIRECTION_OUTBOUND && + (pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4 || + pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 || /// Policy Change Reauthorization + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6 || /// Policy Change Reauthorization + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6))) + status = PerformBasicPacketModificationAtOutboundTransport(&(pDPCData->pClassifyData), + &(pDPCData->pInjectionData), + pModificationData, + FALSE); + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + else if(pDPCData->pInjectionData->direction == FWP_DIRECTION_INBOUND && + (pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V6)) + status = PerformBasicPacketModificationAtInboundTransport(&(pDPCData->pClassifyData), + &(pDPCData->pInjectionData), + pModificationData, + FALSE); + else if(pDPCData->pInjectionData->direction == FWP_DIRECTION_OUTBOUND && + (pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V6)) + status = PerformBasicPacketModificationAtOutboundTransport(&(pDPCData->pClassifyData), + &(pDPCData->pInjectionData), + pModificationData, + FALSE); + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + else if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_MAC_FRAME_ETHERNET || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_MAC_FRAME_NATIVE) + status = PerformBasicPacketModificationAtInboundMACFrame(&(pDPCData->pClassifyData), + &(pDPCData->pInjectionData), + pModificationData, + FALSE); + else if(pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_MAC_FRAME_ETHERNET || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_MAC_FRAME_NATIVE) + status = PerformBasicPacketModificationAtOutboundMACFrame(&(pDPCData->pClassifyData), + &(pDPCData->pInjectionData), + pModificationData, + FALSE); + else if(pClassifyValues->layerId == FWPS_LAYER_INGRESS_VSWITCH_ETHERNET) + status = PerformBasicPacketModificationAtIngressVSwitchEthernet(&(pDPCData->pClassifyData), + &(pDPCData->pInjectionData), + pModificationData, + FALSE); + else if(pClassifyValues->layerId == FWPS_LAYER_EGRESS_VSWITCH_ETHERNET) + status = PerformBasicPacketModificationAtEgressVSwitchEthernet(&(pDPCData->pClassifyData), + &(pDPCData->pInjectionData), + pModificationData, + FALSE); + +#endif // (NTDDI_VERSION >= NTDDI_WIN8) +#endif // (NTDDI_VERSION >= NTDDI_WIN7) + + else + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! BasicPacketModificationDeferredProcedureCall() [status: %#x]\n", + (UINT32)STATUS_NOT_SUPPORTED); + + if(status != STATUS_SUCCESS) + { + if(pDPCData->pClassifyData) + KrnlHlprClassifyDataDestroyLocalCopy(&(pDPCData->pClassifyData)); + + if(pDPCData->pInjectionData) + KrnlHlprInjectionDataDestroy(&(pDPCData->pInjectionData)); + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! BasicPacketModificationDeferredProcedureCall: PerformBasicPacketModification() [status: %#x]\n", + status); + } + + KrnlHlprDPCDataDestroy(&pDPCData); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- BasicPacketModificationDeferredProcedureCall()\n"); + +#endif /// DBG + + return; +} + +/** + @private_function="BasicPacketModificationWorkItemRoutine" + + Purpose: Invokes the appropriate private routine to perform the modification and injection + at PASSIVE_LEVEL.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF566380.aspx
+*/ +_IRQL_requires_(PASSIVE_LEVEL) +_IRQL_requires_same_ +_Function_class_(IO_WORKITEM_ROUTINE) +VOID BasicPacketModificationWorkItemRoutine(_In_ PDEVICE_OBJECT pDeviceObject, + _Inout_opt_ PVOID pContext) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> BasicPacketModificationWorkItemRoutine()\n"); + +#endif /// DBG + + UNREFERENCED_PARAMETER(pDeviceObject); + + NT_ASSERT(pContext); + NT_ASSERT(((WORKITEM_DATA*)pContext)->pClassifyData); + NT_ASSERT(((WORKITEM_DATA*)pContext)->pInjectionData); + + WORKITEM_DATA* pWorkItemData = (WORKITEM_DATA*)pContext; + + if(pWorkItemData) + { + NTSTATUS status = STATUS_SUCCESS; + FWPS_INCOMING_VALUES* pClassifyValues = (FWPS_INCOMING_VALUES*)pWorkItemData->pClassifyData->pClassifyValues; + FWPS_FILTER* pFilter = (FWPS_FILTER*)pWorkItemData->pClassifyData->pFilter; + PC_BASIC_PACKET_MODIFICATION_DATA* pModificationData = (PC_BASIC_PACKET_MODIFICATION_DATA*)pFilter->providerContext->dataBuffer->data; + + if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6) + status = PerformBasicPacketModificationAtInboundNetwork(&(pWorkItemData->pClassifyData), + &(pWorkItemData->pInjectionData), + pModificationData, + FALSE); + else if(pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V6) + status = PerformBasicPacketModificationAtOutboundNetwork(&(pWorkItemData->pClassifyData), + &(pWorkItemData->pInjectionData), + pModificationData, + FALSE); + else if(pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V4 || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V6) + status = PerformBasicPacketModificationAtForward(&(pWorkItemData->pClassifyData), + &(pWorkItemData->pInjectionData), + pModificationData, + FALSE); + else if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V6 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V6 || + (pWorkItemData->pInjectionData->direction == FWP_DIRECTION_INBOUND && + (pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4 || + pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4 || /// Policy Change Reauthorization + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V6 || /// Policy Change Reauthorization + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6))) + status = PerformBasicPacketModificationAtInboundTransport(&(pWorkItemData->pClassifyData), + &(pWorkItemData->pInjectionData), + pModificationData, + FALSE); + else if(pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6 || + (pWorkItemData->pInjectionData->direction == FWP_DIRECTION_OUTBOUND && + (pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4 || + pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 || /// Policy Change Reauthorization + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6 || /// Policy Change Reauthorization + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6))) + status = PerformBasicPacketModificationAtOutboundTransport(&(pWorkItemData->pClassifyData), + &(pWorkItemData->pInjectionData), + pModificationData, + FALSE); + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + else if(pWorkItemData->pInjectionData->direction == FWP_DIRECTION_INBOUND && + (pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V6)) + status = PerformBasicPacketModificationAtInboundTransport(&(pWorkItemData->pClassifyData), + &(pWorkItemData->pInjectionData), + pModificationData, + FALSE); + else if(pWorkItemData->pInjectionData->direction == FWP_DIRECTION_OUTBOUND && + (pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V6)) + status = PerformBasicPacketModificationAtOutboundTransport(&(pWorkItemData->pClassifyData), + &(pWorkItemData->pInjectionData), + pModificationData, + FALSE); + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + else if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_MAC_FRAME_ETHERNET || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_MAC_FRAME_NATIVE) + status = PerformBasicPacketModificationAtInboundMACFrame(&(pWorkItemData->pClassifyData), + &(pWorkItemData->pInjectionData), + pModificationData, + FALSE); + else if(pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_MAC_FRAME_ETHERNET || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_MAC_FRAME_NATIVE) + status = PerformBasicPacketModificationAtOutboundMACFrame(&(pWorkItemData->pClassifyData), + &(pWorkItemData->pInjectionData), + pModificationData, + FALSE); + else if(pClassifyValues->layerId == FWPS_LAYER_INGRESS_VSWITCH_ETHERNET) + status = PerformBasicPacketModificationAtIngressVSwitchEthernet(&(pWorkItemData->pClassifyData), + &(pWorkItemData->pInjectionData), + pModificationData, + FALSE); + else if(pClassifyValues->layerId == FWPS_LAYER_EGRESS_VSWITCH_ETHERNET) + status = PerformBasicPacketModificationAtEgressVSwitchEthernet(&(pWorkItemData->pClassifyData), + &(pWorkItemData->pInjectionData), + pModificationData, + FALSE); + +#endif // (NTDDI_VERSION >= NTDDI_WIN8) +#endif // (NTDDI_VERSION >= NTDDI_WIN7) + + else + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! BasicPacketModificationWorkItemRoutine() [status: %#x]\n", + (UINT32)STATUS_NOT_SUPPORTED); + + if(status != STATUS_SUCCESS) + { + if(pWorkItemData->pClassifyData) + KrnlHlprClassifyDataDestroyLocalCopy(&(pWorkItemData->pClassifyData)); + + if(pWorkItemData->pInjectionData) + KrnlHlprInjectionDataDestroy(&(pWorkItemData->pInjectionData)); + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! BasicPacketModificationWorkItemRoutine: PerformBasicPacketModification() [status: %#x]\n", + status); + } + + KrnlHlprWorkItemDataDestroy(&pWorkItemData); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- BasicPacketModificationWorkItemRoutine()\n"); + +#endif /// DBG + + return; +} + +/** + @private_function="TriggerBasicPacketModificationInline" + + Purpose: Makes a reference to all the classification data structures and invokes the + appropriate private routine to perform the modification and injection.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +NTSTATUS TriggerBasicPacketModificationInline(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pNetBufferList, + _In_opt_ const VOID* pClassifyContext, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut, + _In_ INJECTION_DATA** ppInjectionData) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> TriggerBasicPacketModificationInline()\n"); + +#endif /// DBG + + NT_ASSERT(pClassifyValues); + NT_ASSERT(pMetadata); + NT_ASSERT(pNetBufferList); + NT_ASSERT(pFilter); + NT_ASSERT(pClassifyOut); + NT_ASSERT(ppInjectionData); + NT_ASSERT(*ppInjectionData); + + NTSTATUS status = STATUS_SUCCESS; + CLASSIFY_DATA* pClassifyData = 0; + PC_BASIC_PACKET_MODIFICATION_DATA* pModificationData = (PC_BASIC_PACKET_MODIFICATION_DATA*)pFilter->providerContext->dataBuffer->data; + +#pragma warning(push) +#pragma warning(disable: 6014) /// pClassifyData will be freed in completionFn using BasicPacketModificationCompletionDataDestroy + + HLPR_NEW(pClassifyData, + CLASSIFY_DATA, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pClassifyData, + status); + +#pragma warning(pop) + + pClassifyData->pClassifyValues = pClassifyValues; + pClassifyData->pMetadataValues = pMetadata; + pClassifyData->pPacket = pNetBufferList; + pClassifyData->pClassifyContext = pClassifyContext; + pClassifyData->pFilter = pFilter; + pClassifyData->flowContext = flowContext; + pClassifyData->pClassifyOut = pClassifyOut; + + if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6) + status = PerformBasicPacketModificationAtInboundNetwork(&pClassifyData, + ppInjectionData, + pModificationData, + TRUE); + else if(pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V6) + status = PerformBasicPacketModificationAtOutboundNetwork(&pClassifyData, + ppInjectionData, + pModificationData, + TRUE); + else if(pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V4 || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V6) + status = PerformBasicPacketModificationAtForward(&pClassifyData, + ppInjectionData, + pModificationData, + TRUE); + else if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V6 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V6 || + ((*ppInjectionData)->direction == FWP_DIRECTION_INBOUND && + (pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4 || + pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4 || /// Policy Change Reauthorization + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V6 || /// Policy Change Reauthorization + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6))) + status = PerformBasicPacketModificationAtInboundTransport(&pClassifyData, + ppInjectionData, + pModificationData, + TRUE); + else if(pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6 || + ((*ppInjectionData)->direction == FWP_DIRECTION_OUTBOUND && + (pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4 || + pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 || /// Policy Change Reauthorization + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6 || /// Policy Change Reauthorization + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6))) + status = PerformBasicPacketModificationAtOutboundTransport(&pClassifyData, + ppInjectionData, + pModificationData, + TRUE); + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + else if((*ppInjectionData)->direction == FWP_DIRECTION_INBOUND && + (pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V6)) + status = PerformBasicPacketModificationAtInboundTransport(&pClassifyData, + ppInjectionData, + pModificationData, + TRUE); + else if((*ppInjectionData)->direction == FWP_DIRECTION_OUTBOUND && + (pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V6)) + status = PerformBasicPacketModificationAtOutboundTransport(&pClassifyData, + ppInjectionData, + pModificationData, + TRUE); + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + else if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_MAC_FRAME_ETHERNET || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_MAC_FRAME_NATIVE) + status = PerformBasicPacketModificationAtInboundMACFrame(&pClassifyData, + ppInjectionData, + pModificationData, + TRUE); + else if(pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_MAC_FRAME_ETHERNET || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_MAC_FRAME_NATIVE) + status = PerformBasicPacketModificationAtOutboundMACFrame(&pClassifyData, + ppInjectionData, + pModificationData, + TRUE); + else if(pClassifyValues->layerId == FWPS_LAYER_INGRESS_VSWITCH_ETHERNET) + status = PerformBasicPacketModificationAtIngressVSwitchEthernet(&pClassifyData, + ppInjectionData, + pModificationData, + TRUE); + else if(pClassifyValues->layerId == FWPS_LAYER_EGRESS_VSWITCH_ETHERNET) + status = PerformBasicPacketModificationAtEgressVSwitchEthernet(&pClassifyData, + ppInjectionData, + pModificationData, + TRUE); + +#endif // (NTDDI_VERSION >= NTDDI_WIN8) +#endif // (NTDDI_VERSION >= NTDDI_WIN7) + + else + { + status = STATUS_NOT_SUPPORTED; + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! TriggerBasicPacketModificationInline() [status: %#x]\n", + status); + } + + HLPR_BAIL_LABEL: + + NT_ASSERT(status == STATUS_SUCCESS); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- TriggerBasicPacketModificationInline() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @private_function="TriggerBasicPacketModificationOutOfBand" + + Purpose: Creates a local copy of the classification data structures and queues a WorkItem + to perform the modification and injection at PASSIVE_LEVEL.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF550679.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF566380.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +NTSTATUS TriggerBasicPacketModificationOutOfBand(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pNetBufferList, + _In_opt_ const VOID* pClassifyContext, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _In_ FWPS_CLASSIFY_OUT* pClassifyOut, + _In_ INJECTION_DATA* pInjectionData, + _In_ PC_BASIC_PACKET_MODIFICATION_DATA* pPCData) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> TriggerBasicPacketModificationOutOfBand()\n"); + +#endif /// DBG + + NT_ASSERT(pClassifyValues); + NT_ASSERT(pMetadata); + NT_ASSERT(pNetBufferList); + NT_ASSERT(pFilter); + NT_ASSERT(pClassifyOut); + NT_ASSERT(pInjectionData); + NT_ASSERT(pPCData); + + NTSTATUS status = STATUS_SUCCESS; + CLASSIFY_DATA* pClassifyData = 0; + +#pragma warning(push) +#pragma warning(disable: 6014) /// pClassifyData will be freed in completionFn using BasicPacketModificationCompletionDataDestroy + + status = KrnlHlprClassifyDataCreateLocalCopy(&pClassifyData, + pClassifyValues, + pMetadata, + pNetBufferList, + pClassifyContext, + pFilter, + flowContext, + pClassifyOut); + HLPR_BAIL_ON_FAILURE(status); + +#pragma warning(pop) + + if(pPCData->useWorkItems) + status = KrnlHlprWorkItemQueue(g_pWDMDevice, + BasicPacketModificationWorkItemRoutine, + pClassifyData, + pInjectionData, + 0); + else if(pPCData->useThreadedDPC) + status = KrnlHlprThreadedDPCQueue(BasicPacketModificationDeferredProcedureCall, + pClassifyData, + pInjectionData, + 0); + else + status = KrnlHlprDPCQueue(BasicPacketModificationDeferredProcedureCall, + pClassifyData, + pInjectionData, + 0); + + HLPR_BAIL_LABEL: + + NT_ASSERT(status == STATUS_SUCCESS); + + if(status != STATUS_SUCCESS) + { + if(pClassifyData) + KrnlHlprClassifyDataDestroyLocalCopy(&pClassifyData); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- TriggerBasicPacketModificationOutOfBand() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +#if(NTDDI_VERSION >= NTDDI_WIN7) + +/** + @classify_function="ClassifyBasicPacketModification" + + Purpose: Blocks the current NET_BUFFER_LIST, modifies a clone of the NBL with the specified + data and injects the clone back to the stack's data path.
+
+ Notes: Applies to the following layers:
+ FWPS_LAYER_INBOUND_IPPACKET_V{4/6}
+ FWPS_LAYER_OUTBOUND_IPPACKET_V{4/6}
+ FWPS_LAYER_IPFORWARD_V{4/6}
+ FWPS_LAYER_INBOUND_TRANSPORT_V{4/6}
+ FWPS_LAYER_OUTBOUND_TRANSPORT_V{4/6}
+ FWPS_LAYER_DATAGRAM_DATA_V{4/6}
+ FWPS_LAYER_INBOUND_ICMP_ERROR_V{4/6}
+ FWPS_LAYER_OUTBOUND_ICMP_ERROR_V{4/6}
+ FWPS_LAYER_ALE_AUTH_CONNECT_V{4/6}
+ FWPS_LAYER_ALE_FLOW_ESTABLISHED_V{4/6}
+ FWPS_LAYER_STREAM_PACKET_V{4/6}
+ FWPS_LAYER_INBOUND_MAC_FRAME_ETHERNET
+ FWPS_LAYER_OUTBOUND_MAC_FRAME_ETHERNET
+ FWPS_LAYER_INBOUND_MAC_FRAME_NATIVE
+ FWPS_LAYER_OUTBOUND_MAC_FRAME_NATIVE
+ FWPS_LAYER_INGRESS_VSWITCH_ETHERNET
+ FWPS_LAYER_EGRESS_VSWITCH_ETHERNET
+
+ TCP @ FWPM_LAYER_ALE_AUTH_CONNECT_V{4/6} has no NBL
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF544893.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI ClassifyBasicPacketModification(_In_ const FWPS_INCOMING_VALUES0* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pNetBufferList, + _In_opt_ const VOID* pClassifyContext, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut) +{ + NT_ASSERT(pClassifyValues); + NT_ASSERT(pMetadata); + NT_ASSERT(pFilter); + NT_ASSERT(pClassifyOut); + NT_ASSERT(pFilter->providerContext); + NT_ASSERT(pFilter->providerContext->type == FWPM_GENERAL_CONTEXT); + NT_ASSERT(pFilter->providerContext->dataBuffer); + NT_ASSERT(pFilter->providerContext->dataBuffer->size == sizeof(PC_BASIC_PACKET_MODIFICATION_DATA)); + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + NT_ASSERT(pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V6 || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V4 || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V6 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V6 || + pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4 || + pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V6 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_MAC_FRAME_ETHERNET || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_MAC_FRAME_ETHERNET || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_MAC_FRAME_NATIVE || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_MAC_FRAME_NATIVE || + pClassifyValues->layerId == FWPS_LAYER_INGRESS_VSWITCH_ETHERNET || + pClassifyValues->layerId == FWPS_LAYER_EGRESS_VSWITCH_ETHERNET); + +#else + + NT_ASSERT(pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V6 || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V4 || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V6 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V6 || + pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4 || + pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V6); + +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) + + NT_ASSERT(pFilter->providerContext); + NT_ASSERT(pFilter->providerContext->type == FWPM_GENERAL_CONTEXT); + NT_ASSERT(pFilter->providerContext->dataBuffer); + NT_ASSERT(pFilter->providerContext->dataBuffer->size == sizeof(PC_BASIC_PACKET_MODIFICATION_DATA)); + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> ClassifyBasicPacketModification() [Layer: %s][FilterID: %#I64x][Rights: %#x]", + KrnlHlprFwpsLayerIDToString(pClassifyValues->layerId), + pFilter->filterId, + pClassifyOut->rights); + + if(pClassifyOut->rights & FWPS_RIGHT_ACTION_WRITE) + { + /// Packets are not available for TCP @ ALE_AUTH_CONNECT, so skip over as there is nothing to inject + if(pNetBufferList) + { + PC_BASIC_PACKET_MODIFICATION_DATA* pData = (PC_BASIC_PACKET_MODIFICATION_DATA*)pFilter->providerContext->dataBuffer->data; + + if(pData) + { + NTSTATUS status = STATUS_SUCCESS; + FWP_VALUE* pFlags = 0; + INJECTION_DATA* pInjectionData = 0; + + pClassifyOut->actionType = FWP_ACTION_CONTINUE; + + pFlags = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_FLAGS); + if(pFlags && + pFlags->type == FWP_UINT32) + { + /// For IPsec interop, if ALE classification is required, bypass the injection + if(pFlags->uint32 & FWP_CONDITION_FLAG_IS_IPSEC_SECURED && + FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_ALE_CLASSIFY_REQUIRED)) + HLPR_BAIL; + + /// Inject the individual fragments, but not the fragment grouping of those fragments + if(pFlags->uint32 & FWP_CONDITION_FLAG_IS_FRAGMENT_GROUP) + HLPR_BAIL; + } + + if(pData->flags & PCPMDF_MODIFY_TRANSPORT_HEADER && + (pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V6 || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V4 || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V6)) + { + UINT32 bytesRetreated = 0; + UINT8 version = 0; + IPPROTO protocol = IPPROTO_MAX; + + if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6) + { + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_IP_HEADER_SIZE)) + bytesRetreated = pMetadata->ipHeaderSize; + } + + if(bytesRetreated) + { + /// Initial offset is at the Transport Header for INBOUND_IPPACKET, so retreat the size of the IP Header ... + status = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pNetBufferList), + bytesRetreated, + 0, + 0); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! ClassifyBasicPacketModification: NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + + HLPR_BAIL; + } + } + + version = KrnlHlprIPHeaderGetVersionField((NET_BUFFER_LIST*)pNetBufferList); + + protocol = KrnlHlprIPHeaderGetProtocolField((NET_BUFFER_LIST*)pNetBufferList, + version == IPV4 ? AF_INET : AF_INET6); + + if(bytesRetreated) + { + /// ... and advance the offset back to the original position. + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pNetBufferList), + bytesRetreated, + FALSE, + 0); + } + + /// Exit if this isn't the protocol we are looking for + if(protocol != pData->originalTransportData.protocol) + HLPR_BAIL; + else + { + UINT16 sourcePort = 0; + UINT16 destinationPort = 0; + UINT32 bytesAdvanced = 0; + + if(pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V6 || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V4 || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V6) + { + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_IP_HEADER_SIZE)) + bytesAdvanced = pMetadata->ipHeaderSize; + } + + if(bytesAdvanced) + { + /// Initial offset is at the IP Header for OUTBOUND_IPPACKET and IPFORWARD, so + /// advance the size of the IP Header ... + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pNetBufferList), + bytesAdvanced, + FALSE, + 0); + } + + sourcePort = KrnlHlprTransportHeaderGetSourcePortField((NET_BUFFER_LIST*)pNetBufferList, + protocol); + destinationPort = KrnlHlprTransportHeaderGetDestinationPortField((NET_BUFFER_LIST*)pNetBufferList, + protocol); + /// Exit if the ports are not what we are looking for + if(pData->originalTransportData.sourcePort && + sourcePort != pData->originalTransportData.sourcePort) + HLPR_BAIL; + + if(pData->originalTransportData.destinationPort && + destinationPort != pData->originalTransportData.destinationPort) + HLPR_BAIL; + + if(bytesAdvanced) + { + /// ... and retreat the offset back to the original position. + status = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pNetBufferList), + bytesAdvanced, + 0, + 0); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! ClassifyBasicPacketModification: NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + + HLPR_BAIL; + } + } + } + } + +#pragma warning(push) +#pragma warning(disable: 6014) /// pInjectionData will be freed in completionFn using BasicPacketModificationCompletionDataDestroy + + status = KrnlHlprInjectionDataCreate(&pInjectionData, + pClassifyValues, + pMetadata, + (NET_BUFFER_LIST*)pNetBufferList, + pFilter); + HLPR_BAIL_ON_FAILURE(status); + +#pragma warning(pop) + + if(pInjectionData->injectionState != FWPS_PACKET_INJECTED_BY_SELF && + pInjectionData->injectionState != FWPS_PACKET_PREVIOUSLY_INJECTED_BY_SELF) + { + BOOLEAN performOutOfBand = TRUE; + FWP_VALUE* pProtocolValue = 0; + + pClassifyOut->actionType = FWP_ACTION_BLOCK; + pClassifyOut->flags |= FWPS_CLASSIFY_OUT_FLAG_ABSORB; + pClassifyOut->rights ^= FWPS_RIGHT_ACTION_WRITE; + + if(pFlags && + pFlags->type == FWP_UINT32 && + pFlags->uint32 & FWP_CONDITION_FLAG_IS_IPSEC_SECURED) + pInjectionData->isIPsecSecured = TRUE; + + /// Override the default of performing Out of Band with the user's specified setting ... + if(pData->performInline) + performOutOfBand = FALSE; + + /// ... however, due to TCP's locking semantics, TCP can only be injected Out of Band at any transport layer or equivalent, ... + pProtocolValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_PROTOCOL); + if((pProtocolValue && + pProtocolValue->uint8 == IPPROTO_TCP && + pClassifyValues->layerId > FWPS_LAYER_IPFORWARD_V6_DISCARD) || + pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V6) + performOutOfBand = TRUE; + + /// ... and inbound injection of loopback traffic requires us to use Out of Band modification as well due to address lookups. + if(!performOutOfBand && + pInjectionData->direction == FWP_DIRECTION_INBOUND) + { + FWP_VALUE* pRemoteAddress = 0; + + pRemoteAddress = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_REMOTE_ADDRESS); + if(pRemoteAddress && + ((pRemoteAddress->type == FWP_UINT32 && + RtlCompareMemory(&(pRemoteAddress->uint32), + IPV4_LOOPBACK_ADDRESS, + IPV4_ADDRESS_SIZE)) || + (pRemoteAddress->type == FWP_BYTE_ARRAY16_TYPE && + RtlCompareMemory(&(pRemoteAddress->byteArray16->byteArray16), + IPV6_LOOPBACK_ADDRESS, + IPV6_ADDRESS_SIZE)))) + performOutOfBand = TRUE; + } + + if(performOutOfBand) + status = TriggerBasicPacketModificationOutOfBand(pClassifyValues, + pMetadata, + pNetBufferList, + pClassifyContext, + pFilter, + flowContext, + pClassifyOut, + pInjectionData, + pData); + else + status = TriggerBasicPacketModificationInline(pClassifyValues, + pMetadata, + pNetBufferList, + pClassifyContext, + pFilter, + flowContext, + pClassifyOut, + &pInjectionData); + } + else + { + pClassifyOut->actionType = FWP_ACTION_PERMIT; + + KrnlHlprInjectionDataDestroy(&pInjectionData); + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " -- Injection previously performed.\n"); + } + + HLPR_BAIL_LABEL: + + NT_ASSERT(status == STATUS_SUCCESS); + + if(status != STATUS_SUCCESS) + { + KrnlHlprInjectionDataDestroy(&pInjectionData); + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! ClassifyBasicPacketModification() [status: %#x]\n", + status); + } + } + } + else + pClassifyOut->actionType = FWP_ACTION_PERMIT; + } + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- ClassifyBasicPacketModification() [Layer: %s][FilterID: %#I64x][Action: %#x][Rights: %#x][Absorb: %s]\n", + KrnlHlprFwpsLayerIDToString(pClassifyValues->layerId), + pFilter->filterId, + pClassifyOut->actionType, + pClassifyOut->rights, + (pClassifyOut->flags & FWPS_CLASSIFY_OUT_FLAG_ABSORB) ? "TRUE" : "FALSE"); + + return; +} + +#else + +/** + @classify_function="ClassifyBasicPacketModification" + + Purpose: Blocks the current NET_BUFFER_LIST, modifies a clone of the NBL with the specified + data and injects the clone back to the stack's data path.
+
+ Notes: Applies to the following layers:
+ FWPS_LAYER_INBOUND_IPPACKET_V{4/6}
+ FWPS_LAYER_OUTBOUND_IPPACKET_V{4/6}
+ FWPS_LAYER_IPFORWARD_V{4/6}
+ FWPS_LAYER_INBOUND_TRANSPORT_V{4/6}
+ FWPS_LAYER_OUTBOUND_TRANSPORT_V{4/6}
+ FWPS_LAYER_DATAGRAM_DATA_V{4/6}
+ FWPS_LAYER_INBOUND_ICMP_ERROR_V{4/6}
+ FWPS_LAYER_OUTBOUND_ICMP_ERROR_V{4/6}
+ FWPS_LAYER_ALE_AUTH_CONNECT_V{4/6}
+ FWPS_LAYER_ALE_FLOW_ESTABLISHED_V{4/6}
+
+ TCP @ FWPM_LAYER_ALE_AUTH_CONNECT_V{4/6} has no NBL
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF544890.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI ClassifyBasicPacketModification(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pNetBufferList, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut) +{ + NT_ASSERT(pClassifyValues); + NT_ASSERT(pMetadata); + NT_ASSERT(pFilter); + NT_ASSERT(pClassifyOut); + NT_ASSERT(pFilter->providerContext); + NT_ASSERT(pFilter->providerContext->type == FWPM_GENERAL_CONTEXT); + NT_ASSERT(pFilter->providerContext->dataBuffer); + NT_ASSERT(pFilter->providerContext->dataBuffer->size == sizeof(PC_BASIC_PACKET_MODIFICATION_DATA)); + NT_ASSERT(pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V6 || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V4 || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V6 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V6 || + pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4 || + pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6); + NT_ASSERT(pFilter->providerContext); + NT_ASSERT(pFilter->providerContext->type == FWPM_GENERAL_CONTEXT); + NT_ASSERT(pFilter->providerContext->dataBuffer); + NT_ASSERT(pFilter->providerContext->dataBuffer->size == sizeof(PC_BASIC_PACKET_MODIFICATION_DATA)); + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> ClassifyBasicPacketModification() [Layer: %s][FilterID: %#I64x][Rights: %#x]", + KrnlHlprFwpsLayerIDToString(pClassifyValues->layerId), + pFilter->filterId, + pClassifyOut->rights); + + + if(pClassifyOut->rights & FWPS_RIGHT_ACTION_WRITE) + { + /// Packets are not available for TCP @ ALE_AUTH_CONNECT, so skip over as there is nothing to inject + if(pNetBufferList) + { + NTSTATUS status = STATUS_SUCCESS; + FWP_VALUE* pFlags = 0; + PC_BASIC_PACKET_MODIFICATION_DATA* pData = (PC_BASIC_PACKET_MODIFICATION_DATA*)pFilter->providerContext->dataBuffer->data; + INJECTION_DATA* pInjectionData = 0; + + pClassifyOut->actionType = FWP_ACTION_CONTINUE; + + pFlags = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_FLAGS); + if(pFlags && + pFlags->type == FWP_UINT32) + { + /// For IPsec interop, if ALE classification is required, bypass the injection + if(pFlags->uint32 & FWP_CONDITION_FLAG_IS_IPSEC_SECURED && + +#if(NTDDI_VERSION >= NTDDI_WIN6SP1) + + FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_ALE_CLASSIFY_REQUIRED)) +#else + + pFlags->uint32 & FWP_CONDITION_FLAG_REQUIRES_ALE_CLASSIFY) + +#endif // (NTDDI_VERSION >= NTDDI_WIN6SP1) + + HLPR_BAIL; + + /// Inject the individual fragments, but not the fragment grouping of those fragments + if(pFlags->uint32 & FWP_CONDITION_FLAG_IS_FRAGMENT_GROUP) + HLPR_BAIL; + } + + if(pData && + pData->flags & PCPMDF_MODIFY_TRANSPORT_HEADER && + (pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6 || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V4 || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V6)) + { + UINT32 bytesRetreated = 0; + UINT8 version = 0; + IPPROTO protocol = IPPROTO_MAX; + + if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6) + { + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_IP_HEADER_SIZE)) + bytesRetreated = pMetadata->ipHeaderSize; + } + + if(bytesRetreated) + { + /// Initial offset is at the Transport Header for INBOUND_IPPACKET, so retreat the size of the IP Header ... + status = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pNetBufferList), + bytesRetreated, + 0, + 0); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! ClassifyBasicPacketModification: NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + + HLPR_BAIL; + } + } + + version = KrnlHlprIPHeaderGetVersionField((NET_BUFFER_LIST*)pNetBufferList); + + protocol = KrnlHlprIPHeaderGetProtocolField((NET_BUFFER_LIST*)pNetBufferList, + version == IPV4 ? AF_INET : AF_INET6); + + if(bytesRetreated) + { + /// ... and advance the offset back to the original position. + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pNetBufferList), + bytesRetreated, + FALSE, + 0); + } + + /// Exit if this isn't the protocol we are looking for + if(protocol != pData->originalTransportData.protocol) + HLPR_BAIL; + else + { + UINT16 sourcePort = KrnlHlprTransportHeaderGetSourcePortField((NET_BUFFER_LIST*)pNetBufferList, + protocol); + UINT16 destinationPort = KrnlHlprTransportHeaderGetDestinationPortField((NET_BUFFER_LIST*)pNetBufferList, + protocol); + /// Exit if the ports are not what we are looking for + if((pData->originalTransportData.sourcePort && + sourcePort != pData->originalTransportData.sourcePort) || + (pData->originalTransportData.destinationPort && + destinationPort != pData->originalTransportData.destinationPort)) + HLPR_BAIL; + } + } + +#pragma warning(push) +#pragma warning(disable: 6014) /// pInjectionData will be freed in completionFn using BasicPacketModificationCompletionDataDestroy + + status = KrnlHlprInjectionDataCreate(&pInjectionData, + pClassifyValues, + pMetadata, + (NET_BUFFER_LIST*)pNetBufferList, + pFilter); + HLPR_BAIL_ON_FAILURE(status); + +#pragma warning(pop) + + if(pInjectionData->injectionState != FWPS_PACKET_INJECTED_BY_SELF && + pInjectionData->injectionState != FWPS_PACKET_PREVIOUSLY_INJECTED_BY_SELF) + { + BOOLEAN performOutOfBand = TRUE; + FWP_VALUE* pProtocolValue = 0; + PC_BASIC_PACKET_MODIFICATION_DATA* pData = (PC_BASIC_PACKET_MODIFICATION_DATA*)pFilter->providerContext->dataBuffer->data; + + pClassifyOut->actionType = FWP_ACTION_BLOCK; + pClassifyOut->flags |= FWPS_CLASSIFY_OUT_FLAG_ABSORB; + pClassifyOut->rights ^= FWPS_RIGHT_ACTION_WRITE; + + if(pFlags && + pFlags->type == FWP_UINT32 && + pFlags->uint32 & FWP_CONDITION_FLAG_IS_IPSEC_SECURED) + pInjectionData->isIPsecSecured = TRUE; + + /// Override the default of performing Out of Band with the user's specified setting ... + if(pData->performInline) + performOutOfBand = FALSE; + + /// Due to TCP's locking semantics, TCP can only be injected Out of Band at any transport layer or equivalent + pProtocolValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_PROTOCOL); + if(pProtocolValue && + pProtocolValue->uint8 == IPPROTO_TCP && + pClassifyValues->layerId > FWPS_LAYER_IPFORWARD_V6_DISCARD) + performOutOfBand = TRUE; + + if(performOutOfBand) + status = TriggerBasicPacketModificationOutOfBand(pClassifyValues, + pMetadata, + pNetBufferList, + 0, + pFilter, + flowContext, + pClassifyOut, + pInjectionData, + pData); + else + status = TriggerBasicPacketModificationInline(pClassifyValues, + pMetadata, + pNetBufferList, + 0, + pFilter, + flowContext, + pClassifyOut, + &pInjectionData); + } + else + { + pClassifyOut->actionType = FWP_ACTION_PERMIT; + + KrnlHlprInjectionDataDestroy(&pInjectionData); + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " -- Injection previously performed.\n"); + } + + HLPR_BAIL_LABEL: + + NT_ASSERT(status == STATUS_SUCCESS); + + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! ClassifyBasicPacketModification() [status: %#x]\n", + status); + + KrnlHlprInjectionDataDestroy(&pInjectionData); + } + } + else + pClassifyOut->actionType = FWP_ACTION_PERMIT; + } + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- ClassifyBasicPacketModification() [Layer: %s][FilterID: %#I64x][Action: %#x][Rights: %#x][Absorb: %s]\n", + KrnlHlprFwpsLayerIDToString(pClassifyValues->layerId), + pFilter->filterId, + pClassifyOut->actionType, + pClassifyOut->rights, + (pClassifyOut->flags & FWPS_CLASSIFY_OUT_FLAG_ABSORB) ? "TRUE" : "FALSE"); + + return; +} + +#endif // (NTDDI_VERSION >= NTDDI_WIN7) diff --git a/network/trans/WFPSampler/sys/ClassifyFunctions_BasicPacketModificationCallouts.h b/network/trans/WFPSampler/sys/ClassifyFunctions_BasicPacketModificationCallouts.h new file mode 100644 index 000000000..9a0a6bc6e --- /dev/null +++ b/network/trans/WFPSampler/sys/ClassifyFunctions_BasicPacketModificationCallouts.h @@ -0,0 +1,62 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// ClassifyFunctions_BasicPacketModificationCallouts.h +// +// Abstract: +// This module contains prototypes for WFP Classify functions for modifying and injecting +// packets back into the data path using the clone / block / inject method. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Enhance function declaration for IntelliSense +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef CLASSIFY_BASIC_PACKET_MODIFICATION_H +#define CLASSIFY_BASIC_PACKET_MODIFICATION_H + + +_IRQL_requires_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Function_class_(KDEFERRED_ROUTINE) +VOID BasicPacketModificationDeferredProcedureCall(_In_ KDPC* pDPC, + _In_opt_ PVOID pContext, + _In_opt_ PVOID pArg1, + _In_opt_ PVOID pArg2); + +#if(NTDDI_VERSION >= NTDDI_WIN7) + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI ClassifyBasicPacketModification(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pLayerData, + _In_opt_ const VOID* pClassifyContext, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut); + +#else + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI ClassifyBasicPacketModification(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pLayerData, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut); + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + +#endif /// CLASSIFY_BASIC_PACKET_MODIFICATION_H diff --git a/network/trans/WFPSampler/sys/ClassifyFunctions_BasicStreamInjectionCallouts.cpp b/network/trans/WFPSampler/sys/ClassifyFunctions_BasicStreamInjectionCallouts.cpp new file mode 100644 index 000000000..493fdacef --- /dev/null +++ b/network/trans/WFPSampler/sys/ClassifyFunctions_BasicStreamInjectionCallouts.cpp @@ -0,0 +1,947 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// ClassifyFunctions_BasicStreamInjectionCallouts.cpp +// +// Abstract: +// This module contains WFP Classify functions for injecting data back into TCP's stream using +// the clone / block / inject method. +// +// Naming Convention: +// +// +// +// i.e. +// +// ClassifyBasicStreamInjection +// +// +// Classify - Function is an FWPS_CALLOUT_CLASSIFY_FN +// +// BasicStreamInjection - Function demonstates use clone / block / inject model for +// Stream +// +// +// +// i.e. +// +// TriggerBasicStreamInjectionOutOfBand +// +// +// { +// - +// Trigger - Initiates the desired scenario +// Perform - Executes the desired scenario +// } +// +// BasicStreamInjection - Function demonstates use clone / block / inject model +// +// WorkItemRoutine - WorkItem Routine for Out of Band Injection. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Enhance function declaration for IntelliSense, enhance +// traces, fix serialization, and add support for +// multiple injectors and flowContext +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "Framework_WFPSamplerCalloutDriver.h" /// . +#include "ClassifyFunctions_BasicStreamInjectionCallouts.tmh" /// $(OBJ_PATH)\$(O)\ + +/** + @private_function="PerformBasicPacketInjectionAtOutboundTransport" + + Purpose: Clones the stream data and injects the clone back to the stream using + FwpsStreamInjectAsync().
+
+ Notes: Applies to the following layers:
+ FWPM_LAYER_STREAM_V{4/6}
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/FF551188.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/FF546324.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +NTSTATUS PerformBasicStreamInjection(_In_ CLASSIFY_DATA** ppClassifyData, + _In_ INJECTION_DATA** ppInjectionData, + _In_ BOOLEAN isInline = FALSE) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PerformBasicStreamInjection()\n"); + +#endif /// DBG + + NT_ASSERT(ppClassifyData); + NT_ASSERT(ppInjectionData); + NT_ASSERT(*ppClassifyData); + NT_ASSERT(*ppInjectionData); + + NTSTATUS status = STATUS_SUCCESS; + FWPS_INCOMING_VALUES* pClassifyValues = (FWPS_INCOMING_VALUES*)(*ppClassifyData)->pClassifyValues; + FWPS_INCOMING_METADATA_VALUES* pMetadata = (FWPS_INCOMING_METADATA_VALUES*)(*ppClassifyData)->pMetadataValues; + FWPS_FILTER* pFilter = (FWPS_FILTER*)(*ppClassifyData)->pFilter; + FWPS_STREAM_CALLOUT_IO_PACKET* pStreamCalloutIOPacket = (FWPS_STREAM_CALLOUT_IO_PACKET*)(*ppClassifyData)->pPacket; + UINT64 flowID = 0; + UINT32 streamFlags = pStreamCalloutIOPacket->streamData->flags; + NET_BUFFER_LIST* pNetBufferListChain = 0; + SIZE_T dataLength = pStreamCalloutIOPacket->streamData->dataLength; + BASIC_STREAM_INJECTION_COMPLETION_DATA* pCompletionData = 0; + KIRQL irql = KeGetCurrentIrql(); + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + FLOW_CONTEXT* pFlowContext = (FLOW_CONTEXT*)(*ppClassifyData)->flowContext; + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + +#pragma warning(push) +#pragma warning(disable: 6014) /// pCompletionData will be freed in completionFn using BasicStreamInjectionCompletionDataDestroy + + HLPR_NEW(pCompletionData, + BASIC_STREAM_INJECTION_COMPLETION_DATA, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pCompletionData, + status); + +#pragma warning(pop) + + KeInitializeSpinLock(&(pCompletionData->spinLock)); + + pCompletionData->performedInline = isInline; + pCompletionData->pClassifyData = *ppClassifyData; + pCompletionData->pInjectionData = *ppInjectionData; + + /// Responsibility for freeing this memory has been transferred to the pCompletionData + *ppClassifyData = 0; + + *ppInjectionData = 0; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_FLOW_HANDLE)) + flowID = pMetadata->flowHandle; + + if(!(streamFlags & FWPS_STREAM_FLAG_RECEIVE) && + !(streamFlags & FWPS_STREAM_FLAG_SEND)) + streamFlags |= pCompletionData->pInjectionData->direction ? FWPS_STREAM_FLAG_RECEIVE : FWPS_STREAM_FLAG_SEND; + + pStreamCalloutIOPacket->countBytesRequired = 0; + pStreamCalloutIOPacket->countBytesEnforced = pStreamCalloutIOPacket->streamData->dataLength; + pStreamCalloutIOPacket->streamAction = FWPS_STREAM_ACTION_NONE; + + status = FwpsCloneStreamData(pStreamCalloutIOPacket->streamData, + g_pNDISPoolData->nblPoolHandle, + g_pNDISPoolData->nbPoolHandle, + 0, + &pNetBufferListChain); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicStreamInjection : FwpsCloneStreamData() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + pCompletionData->refCount = KrnlHlprNBLGetRequiredRefCount(pNetBufferListChain, + TRUE); + + status = FwpsStreamInjectAsync(pCompletionData->pInjectionData->injectionHandle, + pCompletionData->pInjectionData->injectionContext, + 0, + flowID, + pFilter->action.calloutId, + pClassifyValues->layerId, + streamFlags, + pNetBufferListChain, + dataLength, + CompleteBasicStreamInjection, + pCompletionData); + + NT_ASSERT(irql == KeGetCurrentIrql()); + + if(status != STATUS_SUCCESS) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicStreamInjection : FwpsStreamInjectAsync() [status: %#x]\n", + status); + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + /// If the enpoint has been pended, this will cause the FlowDeleteFn to get invoked on the + /// PEND_ENDPOINT_CLOSURE callout, which will unpend the closure, and free its flow context. + if(streamFlags & FWPS_STREAM_FLAG_RECEIVE_DISCONNECT || /// FIN + streamFlags & FWPS_STREAM_FLAG_RECEIVE_ABORT || /// RST + streamFlags & FWPS_STREAM_FLAG_SEND_ABORT) /// RST + { + if(pFlowContext && + pFlowContext->aecLayerID) + { + NTSTATUS tempStatus = STATUS_SUCCESS; + + tempStatus = FwpsFlowRemoveContext(pFlowContext->flowID, + pFlowContext->aecLayerID, + pFlowContext->aecCalloutID); + if(status != STATUS_SUCCESS) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicStreamInjection : FwpsFlowRemoveContext() [status: %#x]\n", + tempStatus); + } + } + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS) + { + if(pNetBufferListChain) + { + FwpsDiscardClonedStreamData(pNetBufferListChain, + 0, + irql == DISPATCH_LEVEL ? TRUE : FALSE); + + pNetBufferListChain = 0; + } + + if(pCompletionData) + BasicStreamInjectionCompletionDataDestroy(&pCompletionData, + TRUE); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PerformBasicStreamInjection() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @private_function="BasicStreamInjectionDeferredProcedureCall" + + Purpose: Invokes the appropriate private injection routine to perform the injection at + DISPATCH_LEVEL.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF542972.aspx
+*/ +_IRQL_requires_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Function_class_(KDEFERRED_ROUTINE) +VOID BasicStreamInjectionDeferredProcedureCall(_In_ KDPC* pDPC, + _In_opt_ PVOID pContext, + _In_opt_ PVOID pArg1, + _In_opt_ PVOID pArg2) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> BasicStreamInjectionDeferredProcedureCall()\n"); + +#endif /// DBG + + UNREFERENCED_PARAMETER(pDPC); + UNREFERENCED_PARAMETER(pContext); + UNREFERENCED_PARAMETER(pArg2); + + NT_ASSERT(pDPC); + NT_ASSERT(pArg1); + + DPC_DATA* pDPCData = (DPC_DATA*)pArg1; + + if(pDPCData) + { + NTSTATUS status = STATUS_SUCCESS; + KSPIN_LOCK* pSpinLock = &(g_bsiSerializationList.spinLock); + LIST_ENTRY* pListHead = &(g_bsiSerializationList.listHead); + LIST_ENTRY* pListItem = 0; + INT64* pNumEntries = &(g_bsiSerializationList.numEntries); + + /// Seriailze the Stream injection to prevent data corruption + if(!IsListEmpty(pListHead)) + { + pListItem = ExInterlockedRemoveHeadList(pListHead, + pSpinLock); + + InterlockedDecrement64(pNumEntries); + } + + if(pListItem) + { + BASIC_STREAM_INJECTION_LIST_ITEM* pBSIListItem = (BASIC_STREAM_INJECTION_LIST_ITEM*)pListItem; + + status = PerformBasicStreamInjection(&(pBSIListItem->pClassifyData), + &(pBSIListItem->pInjectionData), + FALSE); + if(status != STATUS_SUCCESS) + { + if(pBSIListItem->pClassifyData) + KrnlHlprClassifyDataDestroyLocalCopy(&(pBSIListItem->pClassifyData)); + + if(pBSIListItem->pInjectionData) + KrnlHlprInjectionDataDestroy(&(pBSIListItem->pInjectionData)); + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! BasicStreamInjectionDeferredProcedureCall() [status: %#x]\n", + status); + } + + HLPR_DELETE(pBSIListItem, + WFPSAMPLER_SYSLIB_TAG); + } + + KrnlHlprDPCDataDestroy(&pDPCData); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- BasicStreamInjectionDeferredProcedureCall()\n"); + +#endif /// DBG + + return; +} + +/** + @private_function="BasicStreamInjectionWorkItemRoutine" + + Purpose: Invokes the private stream injection routine to perform the injection at + PASSIVE_LEVEL.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/FF566380.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(PASSIVE_LEVEL) +_IRQL_requires_same_ +_Function_class_(IO_WORKITEM_ROUTINE) +VOID BasicStreamInjectionWorkItemRoutine(_In_ PDEVICE_OBJECT pDeviceObject, + _Inout_opt_ PVOID pContext) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> BasicStreamInjectionWorkItemRoutine()\n"); + +#endif /// DBG + + UNREFERENCED_PARAMETER(pDeviceObject); + + NT_ASSERT(pContext); + NT_ASSERT(((WORKITEM_DATA*)pContext)->pClassifyData); + NT_ASSERT(((WORKITEM_DATA*)pContext)->pInjectionData); + + NTSTATUS status = STATUS_SUCCESS; + WORKITEM_DATA* pWorkItemData = (WORKITEM_DATA*)pContext; + KSPIN_LOCK* pSpinLock = &(g_bsiSerializationList.spinLock); + WDFWAITLOCK* pWaitLock = &(g_bsiSerializationList.waitLock); + LIST_ENTRY* pOriginalListHead = &(g_bsiSerializationList.listHead); + INT64* pNumEntries = &(g_bsiSerializationList.numEntries); + KIRQL irql = PASSIVE_LEVEL; + LIST_ENTRY* pListItem = 0; + LIST_ENTRY listHead = {0}; + + if(pWorkItemData->pClassifyData->flowContext) + { + FLOW_CONTEXT* pFlowContext = (FLOW_CONTEXT*)pWorkItemData->pClassifyData->flowContext; + + if(pFlowContext->contextType == CONTEXT_TYPE_STREAM && + pFlowContext->pStreamContext) + { + pSpinLock = &(pFlowContext->pStreamContext->serializationList.spinLock); + + pOriginalListHead = &(pFlowContext->pStreamContext->serializationList.listHead); + + pNumEntries = &(pFlowContext->pStreamContext->serializationList.numEntries); + } + } + + /// Seriailze the Stream injection to prevent data corruption + WdfWaitLockAcquire(*pWaitLock, + 0); + + /// Flush the queue + KeAcquireSpinLock(pSpinLock, + &irql); + + if(!IsListEmpty(pOriginalListHead)) + { + LIST_ENTRY* pListEntry = 0; + + pListEntry = pOriginalListHead->Flink; + + RemoveEntryList(pOriginalListHead); + + InitializeListHead(pOriginalListHead); + + InitializeListHead(&listHead); + + AppendTailList(&listHead, + pListEntry); + } + + *pNumEntries = 0; + + KeReleaseSpinLock(pSpinLock, + irql); + + /// Inject all items from the queue until empty + for(; + !IsListEmpty(&listHead); + ) + { + pListItem = ExInterlockedRemoveHeadList(&listHead, + pSpinLock); + + if(pListItem) + { + BASIC_STREAM_INJECTION_LIST_ITEM* pBSIListItem = (BASIC_STREAM_INJECTION_LIST_ITEM*)pListItem; + + status = PerformBasicStreamInjection(&(pBSIListItem->pClassifyData), + &(pBSIListItem->pInjectionData), + FALSE); + if(status != STATUS_SUCCESS) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! BasicStreamInjectionWorkItemRoutine() [status: %#x]\n", + status); + + HLPR_DELETE(pBSIListItem, + WFPSAMPLER_SYSLIB_TAG); + } + } + + WdfWaitLockRelease(*pWaitLock); + + KrnlHlprWorkItemDataDestroy(&pWorkItemData); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- BasicStreamInjectionWorkItemRoutine()\n"); + +#endif /// DBG + + return; +} + +/** + @private_function="TriggerBasicPacketInjectionInline" + + Purpose: Makes a reference to all the classification data structures and invokes the + private stream injection routine to perform the injection.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +NTSTATUS TriggerBasicStreamInjectionInline(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pStreamCalloutIOPacket, + _In_opt_ const VOID* pClassifyContext, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut, + _In_ INJECTION_DATA** ppInjectionData) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> TriggerBasicStreamInjectionInline()\n"); + +#endif /// DBG + + NT_ASSERT(pClassifyValues); + NT_ASSERT(pMetadata); + NT_ASSERT(pStreamCalloutIOPacket); + NT_ASSERT(pFilter); + NT_ASSERT(pClassifyOut); + NT_ASSERT(ppInjectionData); + NT_ASSERT(*ppInjectionData); + + NTSTATUS status = STATUS_SUCCESS; + CLASSIFY_DATA* pClassifyData = 0; + +#pragma warning(push) +#pragma warning(disable: 6014) /// pClassifyData will be freed in completionFn using BasicStreamInjectionCompletionDataDestroy + + HLPR_NEW(pClassifyData, + CLASSIFY_DATA, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pClassifyData, + status); + +#pragma warning(pop) + + pClassifyData->pClassifyValues = pClassifyValues; + pClassifyData->pMetadataValues = pMetadata; + pClassifyData->pPacket = pStreamCalloutIOPacket; + pClassifyData->pClassifyContext = pClassifyContext; + pClassifyData->pFilter = pFilter; + pClassifyData->flowContext = flowContext; + pClassifyData->pClassifyOut = pClassifyOut; + + /// Do not touch the rights in case other callouts use them (which they shouldn't). + pClassifyOut->actionType = FWP_ACTION_BLOCK; + + status = PerformBasicStreamInjection(&pClassifyData, + ppInjectionData, + TRUE); + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS) + { + if(pClassifyData) + KrnlHlprClassifyDataDestroyLocalCopy(&pClassifyData); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- TriggerBasicStreamInjectionInline() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @private_function="TriggerBasicStreamInjectionOutOfBand" + + Purpose: Creates a local copy of the classification data structures and queues a WorkItem + to perform the injection at PASSIVE_LEVEL.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/FF550679.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/FF566380.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +NTSTATUS TriggerBasicStreamInjectionOutOfBand(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pStreamCalloutIOPacket, + _In_opt_ const VOID* pClassifyContext, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _In_ FWPS_CLASSIFY_OUT* pClassifyOut, + _Inout_ INJECTION_DATA* pInjectionData, + _In_ PC_BASIC_STREAM_INJECTION_DATA* pPCData) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> TriggerBasicStreamInjectionOutOfBand()\n"); + +#endif /// DBG + + NT_ASSERT(pClassifyValues); + NT_ASSERT(pMetadata); + NT_ASSERT(pStreamCalloutIOPacket); + NT_ASSERT(pFilter); + NT_ASSERT(pClassifyOut); + NT_ASSERT(pInjectionData); + NT_ASSERT(pPCData); + + NTSTATUS status = STATUS_SUCCESS; + CLASSIFY_DATA* pClassifyData = 0; + BASIC_STREAM_INJECTION_LIST_ITEM* pBSIListItem = 0; + UINT32 processorNumber = KeGetCurrentProcessorNumber(); + KSPIN_LOCK* pSpinLock = &(g_bsiSerializationList.spinLock); + KIRQL irql = PASSIVE_LEVEL; + LIST_ENTRY* pListHead = &(g_bsiSerializationList.listHead); + INT64* pNumEntries = &(g_bsiSerializationList.numEntries); + LIST_ENTRY* pLastEntry = 0; + +#pragma warning(push) +#pragma warning(disable: 6014) /// pClassifyData will be freed in completionFn using BasicStreamInjectionCompletionDataDestroy + + status = KrnlHlprClassifyDataCreateLocalCopy(&pClassifyData, + pClassifyValues, + pMetadata, + pStreamCalloutIOPacket, + pClassifyContext, + pFilter, + flowContext, + pClassifyOut); + HLPR_BAIL_ON_FAILURE(status); + +#pragma warning(pop) + + if(pClassifyData->flowContext) + { + FLOW_CONTEXT* pFlowContext = (FLOW_CONTEXT*)pClassifyData->flowContext; + + if(pFlowContext->contextType == CONTEXT_TYPE_STREAM && + pFlowContext->pStreamContext) + { + pFlowContext->pStreamContext->filterID = pFilter->filterId; + + processorNumber = pFlowContext->pStreamContext->processorID; + + if(pPCData->useWorkItems) + { + pSpinLock = &(pFlowContext->pStreamContext->serializationList.spinLock); + + pListHead = &(pFlowContext->pStreamContext->serializationList.listHead); + + pNumEntries = &(pFlowContext->pStreamContext->serializationList.numEntries); + } + } + } + + /// Seriailze the Stream injection to prevent data corruption + HLPR_NEW(pBSIListItem, + BASIC_STREAM_INJECTION_LIST_ITEM, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pBSIListItem, + status); + + pBSIListItem->pClassifyData = pClassifyData; + pBSIListItem->pInjectionData = pInjectionData; + + pLastEntry = ExInterlockedInsertTailList(pListHead, + &(pBSIListItem->entry), + pSpinLock); + + InterlockedIncrement64(pNumEntries); + + if(pPCData->useWorkItems) + { + /// List was empty, so start a new thread + if(!pLastEntry) + status = KrnlHlprWorkItemQueue(g_pWDMDevice, + BasicStreamInjectionWorkItemRoutine, + pClassifyData, + pInjectionData, + (VOID*)pClassifyData->flowContext); + } + else if(pPCData->useThreadedDPC) + status = KrnlHlprThreadedDPCQueue(BasicStreamInjectionDeferredProcedureCall, + pClassifyData, + pInjectionData, + 0, + processorNumber, + TRUE); + else + status = KrnlHlprDPCQueue(BasicStreamInjectionDeferredProcedureCall, + pClassifyData, + pInjectionData, + 0, + processorNumber, + TRUE); + + HLPR_BAIL_ON_FAILURE(status); + + /// Do not touch the rights in case other callouts use them (which they shouldn't). + pClassifyOut->actionType = FWP_ACTION_BLOCK; + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS) + { + if(pBSIListItem) + { + KeAcquireSpinLock(pSpinLock, + &irql); + + RemoveEntryList(&(pBSIListItem->entry)); + + KeReleaseSpinLock(pSpinLock, + irql); + + + InterlockedDecrement64(pNumEntries); + + HLPR_DELETE(pBSIListItem, + WFPSAMPLER_SYSLIB_TAG); + } + + if(pClassifyData) + KrnlHlprClassifyDataDestroyLocalCopy(&pClassifyData); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- ClassifyBasicStreamInjection() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + + +#if(NTDDI_VERSION >= NTDDI_WIN7) + +/** + @classify_function="ClassifyBasicStreamInjection" + + Purpose: Blocks the current data and blindly injects a clone of the data back to the stream + without modification.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/FF544893.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI ClassifyBasicStreamInjection(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pStreamCalloutIOPacket, + _In_opt_ const VOID* pClassifyContext, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut) +{ + NT_ASSERT(pClassifyValues); + NT_ASSERT(pMetadata); + NT_ASSERT(pStreamCalloutIOPacket); + NT_ASSERT(pFilter); + NT_ASSERT(pClassifyOut); + NT_ASSERT(pFilter->providerContext); + NT_ASSERT(pFilter->providerContext->type == FWPM_GENERAL_CONTEXT); + NT_ASSERT(pFilter->providerContext->dataBuffer); + NT_ASSERT(pFilter->providerContext->dataBuffer->size == sizeof(PC_BASIC_STREAM_INJECTION_DATA)); + NT_ASSERT(pClassifyValues->layerId == FWPS_LAYER_STREAM_V4 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_V6); + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> ClassifyBasicStreamInjection() [Layer: %s][FilterID: %#I64x][FlowContext: %#I64x][Rights: %#x][BytesIndicated: %I64d]", + KrnlHlprFwpsLayerIDToString(pClassifyValues->layerId), + pFilter->filterId, + flowContext, + pClassifyOut->rights, + ((FWPS_STREAM_CALLOUT_IO_PACKET*)pStreamCalloutIOPacket)->streamData->dataLength); + + /// Stream has no concept of Veto. Due to its "waterfall" nature where a block will + /// remove the data and others will not be able to see it. + /// This means that if we got classified, regardless of the rights, we can do whatever we need to. + + NTSTATUS status = STATUS_SUCCESS; + INJECTION_DATA* pInjectionData = 0; + BOOLEAN performOutOfBand = TRUE; + PC_BASIC_STREAM_INJECTION_DATA* pData = (PC_BASIC_STREAM_INJECTION_DATA*)pFilter->providerContext->dataBuffer->data; + + pClassifyOut->actionType = FWP_ACTION_CONTINUE; + +#pragma warning(push) +#pragma warning(disable: 6014) /// pInjectionData will be freed in completionFn using BasicStreamInjectionCompletionDataDestroy + + status = KrnlHlprInjectionDataCreate(&pInjectionData, + pClassifyValues, + pMetadata, + 0, + pFilter); + HLPR_BAIL_ON_FAILURE(status); + +#pragma warning(pop) + + /// Override the default of performing Out of Band with the user's specified setting ... + performOutOfBand = !(pData->performInline); + + if(performOutOfBand) + status = TriggerBasicStreamInjectionOutOfBand(pClassifyValues, + pMetadata, + pStreamCalloutIOPacket, + pClassifyContext, + pFilter, + flowContext, + pClassifyOut, + pInjectionData, + pData); + else + status = TriggerBasicStreamInjectionInline(pClassifyValues, + pMetadata, + pStreamCalloutIOPacket, + pClassifyContext, + pFilter, + flowContext, + pClassifyOut, + &pInjectionData); + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! ClassifyBasicStreamInjection() [status: %#x]\n", + status); + + if(pInjectionData) + KrnlHlprInjectionDataDestroy(&pInjectionData); + } + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- ClassifyBasicStreamInjection() [Layer: %s][FilterID: %#I64x][StreamAction: %#x][BytesEnforced: %I64d/%I64d][Action: %#x][Rights: %#x][Absorb: %s]\n", + KrnlHlprFwpsLayerIDToString(pClassifyValues->layerId), + pFilter->filterId, + ((FWPS_STREAM_CALLOUT_IO_PACKET*)pStreamCalloutIOPacket)->streamAction, + ((FWPS_STREAM_CALLOUT_IO_PACKET*)pStreamCalloutIOPacket)->countBytesEnforced, + ((FWPS_STREAM_CALLOUT_IO_PACKET*)pStreamCalloutIOPacket)->streamData->dataLength, + pClassifyOut->actionType, + pClassifyOut->rights, + (pClassifyOut->flags & FWPS_CLASSIFY_OUT_FLAG_ABSORB) ? "TRUE" : "FALSE"); + + return; +} + +#else + +/** + @classify_function="ClassifyBasicStreamInjection" + + Purpose: Blocks the current data and blindly injects a clone of the data back to the stream + without modification.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/FF544890.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI ClassifyBasicStreamInjection(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pStreamCalloutIOPacket, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut) +{ + NT_ASSERT(pClassifyValues); + NT_ASSERT(pMetadata); + NT_ASSERT(pStreamCalloutIOPacket); + NT_ASSERT(pFilter); + NT_ASSERT(pClassifyOut); + NT_ASSERT(pFilter->providerContext); + NT_ASSERT(pFilter->providerContext->type == FWPM_GENERAL_CONTEXT); + NT_ASSERT(pFilter->providerContext->dataBuffer); + NT_ASSERT(pFilter->providerContext->dataBuffer->size == sizeof(PC_BASIC_STREAM_INJECTION_DATA)); + NT_ASSERT(pClassifyValues->layerId == FWPS_LAYER_STREAM_V4 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_V6); + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> ClassifyBasicStreamInjection() [Layer: %s][FilterID: %#I64x][FlowContext: %#I64x][Rights: %#x][BytesIndicated: %I64d]", + KrnlHlprFwpsLayerIDToString(pClassifyValues->layerId), + pFilter->filterId, + flowContext, + pClassifyOut->rights, + ((FWPS_STREAM_CALLOUT_IO_PACKET*)pStreamCalloutIOPacket)->streamData->dataLength); + + /// Stream has no concept of Veto. Due to its "waterfall" nature where a block will + /// remove the data and others will not be able to see it. + /// This means that if we got classified, regardless of the rights, we can do whatever we need to. + + NTSTATUS status = STATUS_SUCCESS; + INJECTION_DATA* pInjectionData = 0; + BOOLEAN performOutOfBand = TRUE; + PC_BASIC_STREAM_INJECTION_DATA* pData = (PC_BASIC_STREAM_INJECTION_DATA*)pFilter->providerContext->dataBuffer->data; + + pClassifyOut->actionType = FWP_ACTION_CONTINUE; + + status = KrnlHlprInjectionDataCreate(&pInjectionData, + pClassifyValues, + pMetadata, + 0, + pFilter); + HLPR_BAIL_ON_FAILURE(status); + + /// Override the default of performing Out of Band with the user's specified setting ... + performOutOfBand = !(pData->performInline); + + if(performOutOfBand) + status = TriggerBasicStreamInjectionOutOfBand(pClassifyValues, + pMetadata, + pStreamCalloutIOPacket, + 0, + pFilter, + flowContext, + pClassifyOut, + pInjectionData, + pData); + else + status = TriggerBasicStreamInjectionInline(pClassifyValues, + pMetadata, + pStreamCalloutIOPacket, + 0, + pFilter, + flowContext, + pClassifyOut, + &pInjectionData); + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! ClassifyBasicStreamInjection() [status: %#x]\n", + status); + + if(pInjectionData) + KrnlHlprInjectionDataDestroy(&pInjectionData); + } + + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- ClassifyBasicStreamInjection() [Layer: %s][FilterID: %#I64x][StreamAction: %#x][BytesEnforced: %I64d/%I64d][Action: %#x][Rights: %#x][Absorb: %s]\n", + KrnlHlprFwpsLayerIDToString(pClassifyValues->layerId), + pFilter->filterId, + ((FWPS_STREAM_CALLOUT_IO_PACKET*)pStreamCalloutIOPacket)->streamAction, + ((FWPS_STREAM_CALLOUT_IO_PACKET*)pStreamCalloutIOPacket)->countBytesEnforced, + ((FWPS_STREAM_CALLOUT_IO_PACKET*)pStreamCalloutIOPacket)->streamData->dataLength, + pClassifyOut->actionType, + pClassifyOut->rights, + (pClassifyOut->flags & FWPS_CLASSIFY_OUT_FLAG_ABSORB) ? "TRUE" : "FALSE"); + + return; +} + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) diff --git a/network/trans/WFPSampler/sys/ClassifyFunctions_BasicStreamInjectionCallouts.h b/network/trans/WFPSampler/sys/ClassifyFunctions_BasicStreamInjectionCallouts.h new file mode 100644 index 000000000..06e2f8633 --- /dev/null +++ b/network/trans/WFPSampler/sys/ClassifyFunctions_BasicStreamInjectionCallouts.h @@ -0,0 +1,99 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// ClassifyFunctions_BasicStreamInjectionCallouts.cpp +// +// Abstract: +// This module contains prototypes for WFP Classify functions that inject data back into TCP's +// stream using the clone / block / inject method. +// +// Naming Convention: +// +// +// +// i.e. +// +// ClassifyBasicStreamInjection +// +// +// Classify - Function is an FWPS_CALLOUT_CLASSIFY_FN +// +// BasicStreamInjection - Function demonstates use clone / block / inject model +// +// +// +// i.e. +// +// TriggerBasicStreamInjectionOutOfBand +// +// +// { +// - +// Trigger - Initiates the desired scenario +// Perform - Executes the desired scenario +// } +// +// BasicStreamInjection - Function demonstates use clone / block / inject model +// +// WorkItemRoutine - WorkItem Routine for Out of Band Injection. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Enhance function declaration for IntelliSense +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef CLASSIFY_BASIC_STREAM_INJECTION_H +#define CLASSIFY_BASIC_STREAM_INJECTION_H + +typedef struct BASIC_STREAM_INJECTION_LIST_ITEM_ +{ + LIST_ENTRY entry; + CLASSIFY_DATA* pClassifyData; + INJECTION_DATA* pInjectionData; +}BASIC_STREAM_INJECTION_LIST_ITEM, *PBASIC_STREAM_INJECTION_LIST_ITEM; + + +_IRQL_requires_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Function_class_(KDEFERRED_ROUTINE) +VOID BasicStreamInjectionDeferredProcedureCall(_In_ KDPC* pDPC, + _In_opt_ PVOID pContext, + _In_opt_ PVOID pArg1, + _In_opt_ PVOID pArg2); + +#if(NTDDI_VERSION >= NTDDI_WIN7) + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI ClassifyBasicStreamInjection(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pStreamCalloutIOPacket, + _In_opt_ const VOID* pClassifyContext, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut); + +#else + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI ClassifyBasicStreamInjection(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pStreamCalloutIOPacket, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut); + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + +#endif /// CLASSIFY_BASIC_STREAM_INJECTION_H diff --git a/network/trans/WFPSampler/sys/ClassifyFunctions_FastPacketInjectionCallouts.cpp b/network/trans/WFPSampler/sys/ClassifyFunctions_FastPacketInjectionCallouts.cpp new file mode 100644 index 000000000..b7bf9fc9b --- /dev/null +++ b/network/trans/WFPSampler/sys/ClassifyFunctions_FastPacketInjectionCallouts.cpp @@ -0,0 +1,1875 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// ClassifyFunctions_FastPacketInjectionCallouts.cpp +// +// Abstract: +// This module contains WFP Classify functions that inject packets back into the data path +// using the clone / block / inject method. This method is inline only, no modification, +// and uses as little validation and error checking as possible. Certain scenarios will +// definitely fail injection, such as injection to loopback, or IPsec encrypted packets. +// These functions are meant for test performance purposes only. +// +// Naming Convention: +// +// +// +// i.e. +// ClassifyFastPacketInjection +// +// +// Classify - Function is an FWPS_CALLOUT_CLASSIFY_FN +// +// FastPacketInjection - Function demonstrates the clone / block / inject model in the +// fastest form available (inline, no validation, etc.). +// +// +// Private Functions: +// +// Public Functions: +// ClassifyFastPacketInjection(), +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Enhance function declaration for IntelliSense, enhance +// traces, fix weakhost injection, fix expected +// offsets,and add support for multiple injectors and +// controlData. +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "Framework_WFPSamplerCalloutDriver.h" /// . +#include "ClassifyFunctions_FastPacketInjectionCallouts.tmh" /// $(OBJ_PATH)\$(O)\ + +#if(NTDDI_VERSION >= NTDDI_WIN7) + +/** + @classify_function="ClassifyFastPacketInjection" + + Purpose: Blocks the current NET_BUFFER_LIST and injects a clone back to the stack without + modification.
+
+ Notes: Applies to the following layers:
+ FWPS_LAYER_INBOUND_IPPACKET_V{4/6}
+ FWPS_LAYER_OUTBOUND_IPPACKET_V{4/6}
+ FWPS_LAYER_IPFORWARD_V{4/6}
+ FWPS_LAYER_INBOUND_TRANSPORT_V{4/6}
+ FWPS_LAYER_OUTBOUND_TRANSPORT_V{4/6}
+ FWPS_LAYER_DATAGRAM_DATA_V{4/6}
+ FWPS_LAYER_INBOUND_ICMP_ERROR_V{4/6}
+ FWPS_LAYER_OUTBOUND_ICMP_ERROR_V{4/6}
+ FWPS_LAYER_ALE_AUTH_CONNECT_V{4/6}
+ FWPS_LAYER_ALE_FLOW_ESTABLISHED_V{4/6}
+ FWPS_LAYER_STREAM_PACKET_V{4/6}
+ FWPS_LAYER_INBOUND_MAC_FRAME_ETHERNET
+ FWPS_LAYER_OUTBOUND_MAC_FRAME_ETHERNET
+ FWPS_LAYER_INBOUND_MAC_FRAME_NATIVE
+ FWPS_LAYER_OUTBOUND_MAC_FRAME_NATIVE
+ FWPS_LAYER_INGRESS_VSWITCH_ETHERNET
+ FWPS_LAYER_EGRESS_VSWITCH_ETHERNET
+
+ Intended for test performance purposes only.
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF544893.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551202.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551134.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551183.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551185.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551177.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551186.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551188.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/HH439588.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/HH439593.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/HH439669.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551170.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI ClassifyFastPacketInjection(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pNetBufferList, + _In_opt_ const VOID* pClassifyContext, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut) +{ + UNREFERENCED_PARAMETER(pFilter); + UNREFERENCED_PARAMETER(pClassifyContext); + UNREFERENCED_PARAMETER(flowContext); + + if(pClassifyOut->rights & FWPS_RIGHT_ACTION_WRITE) + { + NTSTATUS status = STATUS_SUCCESS; + UINT32 ipHeaderSize = 0; + UINT32 transportHeaderSize = 0; + COMPARTMENT_ID compartmentID = DEFAULT_COMPARTMENT_ID; + UINT64 endpointHandle = 0; + ADDRESS_FAMILY addressFamily = AF_UNSPEC; + UINT8 protocol = 0; + FWP_DIRECTION direction = FWP_DIRECTION_MAX; + UINT32 flags = 0; + BOOLEAN isALEClassifyRequired = FALSE; + VOID* pRemoteAddress = 0; + IF_INDEX interfaceIndex = 0; + IF_INDEX subInterfaceIndex = 0; + HANDLE injectionHandle = 0; + FWPS_PACKET_INJECTION_STATE injectionState = FWPS_PACKET_INJECTION_STATE_MAX; + HANDLE injectionContext = 0; + UINT32 bytesRetreated = 0; + NET_BUFFER_LIST* pClonedNetBufferList = 0; + UINT32 index = WFPSAMPLER_INDEX; + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + UINT32 ethernetHeaderSize = 0; + NDIS_PORT_NUMBER ndisPort = 0; + NDIS_SWITCH_PORT_ID sourcePortID = 0; + NDIS_SWITCH_NIC_INDEX sourceNICIndex = 0; + FWP_BYTE_BLOB* pVSwitchID = 0; + + if(FWPS_IS_L2_METADATA_FIELD_PRESENT(pMetadata, + FWPS_L2_METADATA_FIELD_ETHERNET_MAC_HEADER_SIZE)) + ethernetHeaderSize = pMetadata->ethernetMacHeaderSize; + + if(FWPS_IS_L2_METADATA_FIELD_PRESENT(pMetadata, + FWPS_L2_METADATA_FIELD_VSWITCH_SOURCE_PORT_ID)) + sourcePortID = pMetadata->vSwitchSourcePortId; + + if(FWPS_IS_L2_METADATA_FIELD_PRESENT(pMetadata, + FWPS_L2_METADATA_FIELD_VSWITCH_SOURCE_NIC_INDEX)) + sourceNICIndex = (NDIS_SWITCH_NIC_INDEX)pMetadata->vSwitchSourceNicIndex; + +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_IP_HEADER_SIZE)) + ipHeaderSize = pMetadata->ipHeaderSize; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + transportHeaderSize = pMetadata->transportHeaderSize; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_COMPARTMENT_ID)) + compartmentID = (COMPARTMENT_ID)pMetadata->compartmentId; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_ENDPOINT_HANDLE)) + endpointHandle = pMetadata->transportEndpointHandle; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_PACKET_DIRECTION)) + direction = pMetadata->packetDirection; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_ALE_CLASSIFY_REQUIRED)) + isALEClassifyRequired = TRUE; + + if(pFilter->subLayerWeight == FWPM_SUBLAYER_UNIVERSAL_WEIGHT) + index = UNIVERSAL_INDEX; + + switch(pClassifyValues->layerId) + { + case FWPS_LAYER_INBOUND_IPPACKET_V4: + { + addressFamily = AF_INET; + + interfaceIndex = pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_IPPACKET_V4_INTERFACE_INDEX].value.uint32; + + subInterfaceIndex = pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_IPPACKET_V4_SUB_INTERFACE_INDEX].value.uint32; + + flags = pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_IPPACKET_V4_FLAGS].value.uint32; + + injectionHandle = g_pIPv4InboundNetworkInjectionHandles[index]; + + bytesRetreated = ipHeaderSize; + + break; + } + case FWPS_LAYER_INBOUND_IPPACKET_V6: + { + addressFamily = AF_INET6; + + interfaceIndex = pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_IPPACKET_V6_INTERFACE_INDEX].value.uint32; + + subInterfaceIndex = pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_IPPACKET_V6_SUB_INTERFACE_INDEX].value.uint32; + + flags = pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_IPPACKET_V6_FLAGS].value.uint32; + + injectionHandle = g_pIPv6InboundNetworkInjectionHandles[index]; + + bytesRetreated = ipHeaderSize; + + break; + } + case FWPS_LAYER_OUTBOUND_IPPACKET_V4: + { + addressFamily = AF_INET; + + flags = pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_IPPACKET_V4_FLAGS].value.uint32; + + injectionHandle = g_pIPv4OutboundNetworkInjectionHandles[index]; + + break; + } + case FWPS_LAYER_OUTBOUND_IPPACKET_V6: + { + addressFamily = AF_INET6; + + flags = pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_IPPACKET_V6_FLAGS].value.uint32; + + injectionHandle = g_pIPv6OutboundNetworkInjectionHandles[index]; + + break; + } + case FWPS_LAYER_IPFORWARD_V4: + { + addressFamily = AF_INET; + + interfaceIndex = pClassifyValues->incomingValue[FWPS_FIELD_IPFORWARD_V4_DESTINATION_INTERFACE_INDEX].value.uint32; + + flags = pClassifyValues->incomingValue[FWPS_FIELD_IPFORWARD_V4_FLAGS].value.uint32; + + if(direction == FWP_DIRECTION_OUTBOUND) + injectionHandle = g_pIPv4OutboundForwardInjectionHandles[index]; + else + injectionHandle = g_pIPv4InboundForwardInjectionHandles[index]; + + /// Determine if this is a weakhost forward and adjust accordingly + if(flags & FWP_CONDITION_FLAG_IS_INBOUND_PASS_THRU) + injectionHandle = g_pIPv4InboundNetworkInjectionHandles[index]; + else if(flags & FWP_CONDITION_FLAG_IS_OUTBOUND_PASS_THRU) + injectionHandle = g_pIPv4OutboundNetworkInjectionHandles[index]; + + break; + } + case FWPS_LAYER_IPFORWARD_V6: + { + addressFamily = AF_INET6; + + interfaceIndex = pClassifyValues->incomingValue[FWPS_FIELD_IPFORWARD_V6_DESTINATION_INTERFACE_INDEX].value.uint32; + + flags = pClassifyValues->incomingValue[FWPS_FIELD_IPFORWARD_V6_FLAGS].value.uint32; + + if(direction == FWP_DIRECTION_OUTBOUND) + injectionHandle = g_pIPv6OutboundForwardInjectionHandles[index]; + else + injectionHandle = g_pIPv6InboundForwardInjectionHandles[index]; + + /// Determine if this is a weakhost forward and adjust accordingly + if(flags & FWP_CONDITION_FLAG_IS_INBOUND_PASS_THRU) + injectionHandle = g_pIPv6InboundNetworkInjectionHandles[index]; + else if(flags & FWP_CONDITION_FLAG_IS_OUTBOUND_PASS_THRU) + injectionHandle = g_pIPv6OutboundNetworkInjectionHandles[index]; + + break; + } + case FWPS_LAYER_INBOUND_TRANSPORT_V4: + { + addressFamily = AF_INET; + + protocol = pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_TRANSPORT_V4_IP_PROTOCOL].value.uint8; + + interfaceIndex = pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_TRANSPORT_V4_INTERFACE_INDEX].value.uint32; + + subInterfaceIndex = pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_TRANSPORT_V4_SUB_INTERFACE_INDEX].value.uint32; + + flags = pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_TRANSPORT_V4_FLAGS].value.uint32; + + injectionHandle = g_pIPv4InboundTransportInjectionHandles[index]; + + if(protocol == ICMPV4) + bytesRetreated = ipHeaderSize; + else + bytesRetreated = ipHeaderSize + transportHeaderSize; + + break; + } + case FWPS_LAYER_INBOUND_TRANSPORT_V6: + { + addressFamily = AF_INET6; + + protocol = pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_TRANSPORT_V6_IP_PROTOCOL].value.uint8; + + interfaceIndex = pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_TRANSPORT_V6_INTERFACE_INDEX].value.uint32; + + subInterfaceIndex = pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_TRANSPORT_V6_SUB_INTERFACE_INDEX].value.uint32; + + flags = pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_TRANSPORT_V6_FLAGS].value.uint32; + + injectionHandle = g_pIPv6InboundTransportInjectionHandles[index]; + + if(protocol == ICMPV6) + bytesRetreated = ipHeaderSize; + else + bytesRetreated = ipHeaderSize + transportHeaderSize; + + break; + } + case FWPS_LAYER_OUTBOUND_TRANSPORT_V4: + { + addressFamily = AF_INET; + + protocol = pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_TRANSPORT_V4_IP_PROTOCOL].value.uint8; + + pRemoteAddress = &(pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_TRANSPORT_V4_IP_REMOTE_ADDRESS].value.uint32); + + flags = pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_TRANSPORT_V4_FLAGS].value.uint32; + + injectionHandle = g_pIPv4OutboundTransportInjectionHandles[index]; + + break; + } + case FWPS_LAYER_OUTBOUND_TRANSPORT_V6: + { + addressFamily = AF_INET6; + + protocol = pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_TRANSPORT_V6_IP_PROTOCOL].value.uint8; + + pRemoteAddress = pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_TRANSPORT_V6_IP_REMOTE_ADDRESS].value.byteArray16->byteArray16; + + flags = pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_TRANSPORT_V6_FLAGS].value.uint32; + + injectionHandle = g_pIPv6OutboundTransportInjectionHandles[index]; + + break; + } + case FWPS_LAYER_DATAGRAM_DATA_V4: + { + addressFamily = AF_INET; + + protocol = pClassifyValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V4_IP_PROTOCOL].value.uint8; + + direction = (FWP_DIRECTION)pClassifyValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V4_DIRECTION].value.uint32; + + flags = pClassifyValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V4_FLAGS].value.uint32; + + if(direction == FWP_DIRECTION_INBOUND) + { + interfaceIndex = pClassifyValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V4_INTERFACE_INDEX].value.uint32; + + subInterfaceIndex = pClassifyValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V4_SUB_INTERFACE_INDEX].value.uint32; + + injectionHandle = g_pIPv4InboundTransportInjectionHandles[index]; + + bytesRetreated = ipHeaderSize + transportHeaderSize; + } + else + { + pRemoteAddress = &(pClassifyValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V4_IP_REMOTE_ADDRESS].value.uint32); + + injectionHandle = g_pIPv4OutboundTransportInjectionHandles[index]; + } + + break; + } + case FWPS_LAYER_DATAGRAM_DATA_V6: + { + addressFamily = AF_INET6; + + protocol = pClassifyValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V6_IP_PROTOCOL].value.uint8; + + direction = (FWP_DIRECTION)pClassifyValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V6_DIRECTION].value.uint32; + + flags = pClassifyValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V6_FLAGS].value.uint32; + + if(direction == FWP_DIRECTION_INBOUND) + { + interfaceIndex = pClassifyValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V6_INTERFACE_INDEX].value.uint32; + + subInterfaceIndex = pClassifyValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V6_SUB_INTERFACE_INDEX].value.uint32; + + injectionHandle = g_pIPv6InboundTransportInjectionHandles[index]; + + bytesRetreated = ipHeaderSize + transportHeaderSize; + } + else + { + pRemoteAddress = pClassifyValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V6_IP_REMOTE_ADDRESS].value.byteArray16->byteArray16; + + injectionHandle = g_pIPv6OutboundTransportInjectionHandles[index]; + } + + break; + } + case FWPS_LAYER_INBOUND_ICMP_ERROR_V4: + { + addressFamily = AF_INET; + + protocol = ICMPV4; + + interfaceIndex = pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_ICMP_ERROR_V4_INTERFACE_INDEX].value.uint32; + + subInterfaceIndex = pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_ICMP_ERROR_V4_SUB_INTERFACE_INDEX].value.uint32; + + flags = pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_ICMP_ERROR_V4_FLAGS].value.uint32; + + injectionHandle = g_pIPv4InboundTransportInjectionHandles[index]; + + bytesRetreated = ipHeaderSize + transportHeaderSize; + + break; + } + case FWPS_LAYER_INBOUND_ICMP_ERROR_V6: + { + addressFamily = AF_INET6; + + protocol = ICMPV6; + + interfaceIndex = pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_ICMP_ERROR_V6_INTERFACE_INDEX].value.uint32; + + subInterfaceIndex = pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_ICMP_ERROR_V6_SUB_INTERFACE_INDEX].value.uint32; + + flags = pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_ICMP_ERROR_V6_FLAGS].value.uint32; + + injectionHandle = g_pIPv6InboundTransportInjectionHandles[index]; + + bytesRetreated = ipHeaderSize + transportHeaderSize; + + break; + } + case FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4: + { + addressFamily = AF_INET; + + protocol = ICMPV4; + + pRemoteAddress = &(pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_ICMP_ERROR_V4_IP_REMOTE_ADDRESS].value.uint32); + + flags = pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_ICMP_ERROR_V4_FLAGS].value.uint32; + + injectionHandle = g_pIPv4OutboundTransportInjectionHandles[index]; + + bytesRetreated = ipHeaderSize; + + break; + } + case FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6: + { + addressFamily = AF_INET6; + + protocol = ICMPV6; + + pRemoteAddress = pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_ICMP_ERROR_V6_IP_REMOTE_ADDRESS].value.byteArray16->byteArray16; + + flags = pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_ICMP_ERROR_V6_FLAGS].value.uint32; + + injectionHandle = g_pIPv6OutboundTransportInjectionHandles[index]; + + bytesRetreated = ipHeaderSize; + + break; + } + case FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4: + { + addressFamily = AF_INET; + + protocol = pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_IP_PROTOCOL].value.uint8; + + flags = pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_FLAGS].value.uint32; + + if(direction == FWP_DIRECTION_OUTBOUND) + { + pRemoteAddress = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_IP_REMOTE_ADDRESS].value.uint32); + + injectionHandle = g_pIPv4OutboundTransportInjectionHandles[index]; + } + else + { + interfaceIndex = pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_INTERFACE_INDEX].value.uint32; + + subInterfaceIndex = pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_SUB_INTERFACE_INDEX].value.uint32; + + injectionHandle = g_pIPv4InboundTransportInjectionHandles[index]; + + if(protocol == ICMPV4) + bytesRetreated = ipHeaderSize; + else + bytesRetreated = ipHeaderSize + transportHeaderSize; + } + + break; + } + case FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6: + { + addressFamily = AF_INET6; + + protocol = pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V6_IP_PROTOCOL].value.uint8; + + flags = pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V6_FLAGS].value.uint32; + + if(direction == FWP_DIRECTION_OUTBOUND) + { + pRemoteAddress = pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V6_IP_REMOTE_ADDRESS].value.byteArray16->byteArray16; + + injectionHandle = g_pIPv6OutboundTransportInjectionHandles[index]; + } + else + { + interfaceIndex = pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V6_INTERFACE_INDEX].value.uint32; + + subInterfaceIndex = pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V6_SUB_INTERFACE_INDEX].value.uint32; + + injectionHandle = g_pIPv6InboundTransportInjectionHandles[index]; + + if(protocol == ICMPV6) + bytesRetreated = ipHeaderSize; + else + bytesRetreated = ipHeaderSize + transportHeaderSize; + } + + break; + } + case FWPS_LAYER_ALE_AUTH_CONNECT_V4: + { + addressFamily = AF_INET; + + protocol = pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_PROTOCOL].value.uint8; + + flags = pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_FLAGS].value.uint32; + + if(direction == FWP_DIRECTION_INBOUND) + { + bytesRetreated = ipHeaderSize + transportHeaderSize; + + interfaceIndex = pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_INTERFACE_INDEX].value.uint32; + + subInterfaceIndex = pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_SUB_INTERFACE_INDEX].value.uint32; + + injectionHandle = g_pIPv4InboundTransportInjectionHandles[index]; + } + else + { + pRemoteAddress = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_REMOTE_ADDRESS].value.uint32); + + injectionHandle = g_pIPv4OutboundTransportInjectionHandles[index]; + } + + break; + } + case FWPS_LAYER_ALE_AUTH_CONNECT_V6: + { + addressFamily = AF_INET6; + + protocol = pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V6_IP_PROTOCOL].value.uint8; + + flags = pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V6_FLAGS].value.uint32; + + if(direction == FWP_DIRECTION_INBOUND) + { + bytesRetreated = ipHeaderSize + transportHeaderSize; + + interfaceIndex = pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V6_INTERFACE_INDEX].value.uint32; + + subInterfaceIndex = pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V6_SUB_INTERFACE_INDEX].value.uint32; + + injectionHandle = g_pIPv6InboundTransportInjectionHandles[index]; + } + else + { + pRemoteAddress = pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V6_IP_REMOTE_ADDRESS].value.byteArray16->byteArray16; + + injectionHandle = g_pIPv6OutboundTransportInjectionHandles[index]; + } + + break; + } + case FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4: + { + addressFamily = AF_INET; + + protocol = pClassifyValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_PROTOCOL].value.uint8; + + direction = (FWP_DIRECTION)pClassifyValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_DIRECTION].value.uint8; + + flags = pClassifyValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_FLAGS].value.uint32; + + if(direction == FWP_DIRECTION_INBOUND) + { + injectionHandle = g_pIPv4InboundTransportInjectionHandles[index]; + + if(ipHeaderSize == 0) + ipHeaderSize = IPV4_HEADER_MIN_SIZE; + + if(transportHeaderSize == 0) + { + if(protocol == ICMPV4) + transportHeaderSize = ICMP_HEADER_MIN_SIZE; + else if(protocol == TCP) + transportHeaderSize = TCP_HEADER_MIN_SIZE; + else if(protocol == UDP) + transportHeaderSize = UDP_HEADER_MIN_SIZE; + } + + if(protocol == ICMPV4) + bytesRetreated = ipHeaderSize; + else + bytesRetreated = ipHeaderSize + transportHeaderSize; + } + else + { + pRemoteAddress = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V6_IP_REMOTE_ADDRESS].value.uint32); + + injectionHandle = g_pIPv4OutboundTransportInjectionHandles[index]; + + bytesRetreated = ipHeaderSize; + } + + break; + } + case FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6: + { + addressFamily = AF_INET6; + + protocol = pClassifyValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V6_IP_PROTOCOL].value.uint8; + + direction = (FWP_DIRECTION)pClassifyValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V6_DIRECTION].value.uint8; + + flags = pClassifyValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V6_FLAGS].value.uint32; + + if(direction == FWP_DIRECTION_INBOUND) + { + injectionHandle = g_pIPv6InboundTransportInjectionHandles[index]; + + if(ipHeaderSize == 0) + ipHeaderSize = IPV6_HEADER_MIN_SIZE; + + if(transportHeaderSize == 0) + { + if(protocol == ICMPV6) + transportHeaderSize = ICMP_HEADER_MIN_SIZE; + else if(protocol == TCP) + transportHeaderSize = TCP_HEADER_MIN_SIZE; + else if(protocol == UDP) + transportHeaderSize = UDP_HEADER_MIN_SIZE; + } + + if(protocol == ICMPV6) + bytesRetreated = ipHeaderSize; + else + bytesRetreated = ipHeaderSize + transportHeaderSize; + } + else + { + pRemoteAddress = pClassifyValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V6_IP_REMOTE_ADDRESS].value.byteArray16->byteArray16; + + injectionHandle = g_pIPv6OutboundTransportInjectionHandles[index]; + + bytesRetreated = ipHeaderSize; + } + + break; + } + case FWPS_LAYER_STREAM_PACKET_V4: + { + addressFamily = AF_INET; + + protocol = TCP; + + direction = (FWP_DIRECTION)pClassifyValues->incomingValue[FWPS_FIELD_STREAM_PACKET_V4_DIRECTION].value.uint8; + + flags = pClassifyValues->incomingValue[FWPS_FIELD_STREAM_PACKET_V4_FLAGS].value.uint32; + + if(direction == FWP_DIRECTION_INBOUND) + { + interfaceIndex = pClassifyValues->incomingValue[FWPS_FIELD_STREAM_PACKET_V4_INTERFACE_INDEX].value.uint32; + + subInterfaceIndex = pClassifyValues->incomingValue[FWPS_FIELD_STREAM_PACKET_V4_SUB_INTERFACE_INDEX].value.uint32; + + injectionHandle = g_pIPv4InboundTransportInjectionHandles[index]; + + bytesRetreated = ipHeaderSize + transportHeaderSize; + } + else + { + pRemoteAddress = &(pClassifyValues->incomingValue[FWPS_FIELD_STREAM_PACKET_V4_IP_REMOTE_ADDRESS].value.uint32); + + injectionHandle = g_pIPv4OutboundTransportInjectionHandles[index]; + + bytesRetreated = ipHeaderSize; + } + + break; + } + case FWPS_LAYER_STREAM_PACKET_V6: + { + addressFamily = AF_INET6; + + protocol = TCP; + + direction = (FWP_DIRECTION)pClassifyValues->incomingValue[FWPS_FIELD_STREAM_PACKET_V6_DIRECTION].value.uint8; + + flags = pClassifyValues->incomingValue[FWPS_FIELD_STREAM_PACKET_V6_FLAGS].value.uint32; + + if(direction == FWP_DIRECTION_INBOUND) + { + interfaceIndex = pClassifyValues->incomingValue[FWPS_FIELD_STREAM_PACKET_V6_INTERFACE_INDEX].value.uint32; + + subInterfaceIndex = pClassifyValues->incomingValue[FWPS_FIELD_STREAM_PACKET_V6_SUB_INTERFACE_INDEX].value.uint32; + + injectionHandle = g_pIPv6InboundTransportInjectionHandles[index]; + + bytesRetreated = ipHeaderSize + transportHeaderSize; + } + else + { + pRemoteAddress = pClassifyValues->incomingValue[FWPS_FIELD_STREAM_PACKET_V6_IP_REMOTE_ADDRESS].value.byteArray16->byteArray16; + + injectionHandle = g_pIPv6OutboundTransportInjectionHandles[index]; + + bytesRetreated = ipHeaderSize; + } + + break; + } + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + case FWPS_LAYER_INBOUND_MAC_FRAME_ETHERNET: + { + UINT16 etherType = pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_MAC_FRAME_ETHERNET_ETHER_TYPE].value.uint16; + + interfaceIndex = pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_MAC_FRAME_ETHERNET_INTERFACE_INDEX].value.uint32; + + ndisPort = pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_MAC_FRAME_ETHERNET_NDIS_PORT].value.uint32; + + if(etherType == 0x86DD) + { + addressFamily = AF_INET6; + + injectionHandle = g_pIPv6InboundMACInjectionHandles[index]; + } + else if(etherType == 0x800) + { + addressFamily = AF_INET; + + injectionHandle = g_pIPv4InboundMACInjectionHandles[index]; + } + else + injectionHandle = g_pInboundMACInjectionHandles[index]; + + bytesRetreated = ethernetHeaderSize; + + break; + } + case FWPS_LAYER_OUTBOUND_MAC_FRAME_ETHERNET: + { + UINT16 etherType = pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_MAC_FRAME_ETHERNET_ETHER_TYPE].value.uint16; + + interfaceIndex = pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_MAC_FRAME_ETHERNET_INTERFACE_INDEX].value.uint32; + + ndisPort = pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_MAC_FRAME_ETHERNET_NDIS_PORT].value.uint32; + + if(etherType == 0x86DD) + { + addressFamily = AF_INET6; + + injectionHandle = g_pIPv6OutboundMACInjectionHandles[index]; + } + else if(etherType == 0x800) + { + addressFamily = AF_INET; + + injectionHandle = g_pIPv4OutboundMACInjectionHandles[index]; + } + else + injectionHandle = g_pOutboundMACInjectionHandles[index]; + + break; + } + case FWPS_LAYER_INBOUND_MAC_FRAME_NATIVE: + { + interfaceIndex = pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_MAC_FRAME_NATIVE_INTERFACE_INDEX].value.uint32; + + ndisPort = pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_MAC_FRAME_NATIVE_NDIS_PORT].value.uint32; + + injectionHandle = g_pInboundMACInjectionHandles[index]; + + break; + } + case FWPS_LAYER_OUTBOUND_MAC_FRAME_NATIVE: + { + interfaceIndex = pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_MAC_FRAME_NATIVE_INTERFACE_INDEX].value.uint32; + + ndisPort = pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_MAC_FRAME_NATIVE_NDIS_PORT].value.uint32; + + injectionHandle = g_pOutboundMACInjectionHandles[index]; + + break; + } + case FWPS_LAYER_INGRESS_VSWITCH_ETHERNET: + { + UINT16 etherType = pClassifyValues->incomingValue[FWPS_FIELD_INGRESS_VSWITCH_ETHERNET_ETHER_TYPE].value.uint16; + + pVSwitchID = pClassifyValues->incomingValue[FWPS_FIELD_INGRESS_VSWITCH_ETHERNET_VSWITCH_ID].value.byteBlob; + + if(etherType == 0x86DD) + { + addressFamily = AF_INET6; + + injectionHandle = g_pIPv6IngressVSwitchEthernetInjectionHandles[index]; + } + else if(etherType == 0x800) + { + addressFamily = AF_INET; + + injectionHandle = g_pIPv4IngressVSwitchEthernetInjectionHandles[index]; + } + else + injectionHandle = g_pIngressVSwitchEthernetInjectionHandles[index]; + + break; + } + case FWPS_LAYER_EGRESS_VSWITCH_ETHERNET: + { + UINT16 etherType = pClassifyValues->incomingValue[FWPS_FIELD_EGRESS_VSWITCH_ETHERNET_ETHER_TYPE].value.uint16; + + pVSwitchID = pClassifyValues->incomingValue[FWPS_FIELD_EGRESS_VSWITCH_ETHERNET_VSWITCH_ID].value.byteBlob; + + if(etherType == 0x86DD) + { + addressFamily = AF_INET6; + + injectionHandle = g_pIPv6EgressVSwitchEthernetInjectionHandles[index]; + } + else if(etherType == 0x800) + { + addressFamily = AF_INET; + + injectionHandle = g_pIPv4EgressVSwitchEthernetInjectionHandles[index]; + } + else + injectionHandle = g_pEgressVSwitchEthernetInjectionHandles[index]; + + break; + } + +#endif // (NTDDI_VERSION >= NTDDI_WIN8) + + } + + pClassifyOut->actionType = FWP_ACTION_PERMIT; + + if(!((flags & FWP_CONDITION_FLAG_IS_LOOPBACK || + (flags & FWP_CONDITION_FLAG_IS_IPSEC_SECURED && + (flags & FWP_CONDITION_FLAG_REQUIRES_ALE_CLASSIFY || + isALEClassifyRequired))))) + { + injectionState = FwpsQueryPacketInjectionState(injectionHandle, + (NET_BUFFER_LIST*)pNetBufferList, + &injectionContext); + if(injectionState != FWPS_PACKET_INJECTED_BY_SELF && + injectionState != FWPS_PACKET_PREVIOUSLY_INJECTED_BY_SELF) + { + /// Due to TCP's locking semantics, TCP can only be injected Out of Band at any + /// transport layer or equivalent. + if(protocol == TCP && + (injectionHandle == g_pIPv4InboundTransportInjectionHandles[index] || + injectionHandle == g_pIPv6InboundTransportInjectionHandles[index] || + injectionHandle == g_pIPv4OutboundTransportInjectionHandles[index] || + injectionHandle == g_pIPv6OutboundTransportInjectionHandles[index])) + HLPR_BAIL; + + pClassifyOut->actionType = FWP_ACTION_BLOCK; + pClassifyOut->flags |= FWPS_CLASSIFY_OUT_FLAG_ABSORB; + pClassifyOut->rights ^= FWPS_RIGHT_ACTION_WRITE; + + if(bytesRetreated) + { + status = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pNetBufferList), + bytesRetreated, + 0, + 0); + HLPR_BAIL_ON_FAILURE(status); + } + + status = FwpsAllocateCloneNetBufferList((NET_BUFFER_LIST*)pNetBufferList, + g_pNDISPoolData->nblPoolHandle, + g_pNDISPoolData->nbPoolHandle, + 0, + &pClonedNetBufferList); + + if(bytesRetreated) + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pNetBufferList), + bytesRetreated, + FALSE, + 0); + + HLPR_BAIL_ON_FAILURE(status); + + if(injectionHandle == g_pIPv4InboundNetworkInjectionHandles[index] || + injectionHandle == g_pIPv6InboundNetworkInjectionHandles[index]) + status = FwpsInjectNetworkReceiveAsync(injectionHandle, + injectionContext, + 0, + compartmentID, + interfaceIndex, + subInterfaceIndex, + pClonedNetBufferList, + CompleteFastPacketInjection, + 0); + else if(injectionHandle == g_pIPv4OutboundNetworkInjectionHandles[index] || + injectionHandle == g_pIPv6OutboundNetworkInjectionHandles[index]) + status = FwpsInjectNetworkSendAsync(injectionHandle, + injectionContext, + 0, + compartmentID, + pClonedNetBufferList, + CompleteFastPacketInjection, + 0); + else if(injectionHandle == g_pIPv4InboundForwardInjectionHandles[index] || + injectionHandle == g_pIPv6InboundForwardInjectionHandles[index] || + injectionHandle == g_pIPv4OutboundForwardInjectionHandles[index] || + injectionHandle == g_pIPv6OutboundForwardInjectionHandles[index]) + status = FwpsInjectForwardAsync(injectionHandle, + injectionContext, + 0, + addressFamily, + compartmentID, + interfaceIndex, + pClonedNetBufferList, + CompleteFastPacketInjection, + 0); + else if(injectionHandle == g_pIPv4InboundTransportInjectionHandles[index] || + injectionHandle == g_pIPv6InboundTransportInjectionHandles[index]) + status = FwpsInjectTransportReceiveAsync(injectionHandle, + injectionContext, + 0, + 0, + addressFamily, + compartmentID, + interfaceIndex, + subInterfaceIndex, + pClonedNetBufferList, + CompleteFastPacketInjection, + 0); + else if(injectionHandle == g_pIPv4OutboundTransportInjectionHandles[index] || + injectionHandle == g_pIPv6OutboundTransportInjectionHandles[index]) + { + FWPS_TRANSPORT_SEND_PARAMS* pSendParams = 0; + UINT32 addressSize = addressFamily == AF_INET ? IPV4_ADDRESS_SIZE : IPV6_ADDRESS_SIZE; + +#pragma warning(push) +#pragma warning(disable: 6014) /// pSendParams will be freed in completionFn using FastPacketInjectionCompletionDataDestroy + + HLPR_NEW(pSendParams, + FWPS_TRANSPORT_SEND_PARAMS, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE_2(pSendParams, + status); + + HLPR_NEW_ARRAY(pSendParams->remoteAddress, + BYTE, + addressSize, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE_2(pSendParams->remoteAddress, + status); + +#pragma warning(pop) + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_CONTROL_DATA)) + { + pSendParams->controlData = pMetadata->controlData; + pSendParams->controlDataLength = pMetadata->controlDataLength; + } + + if(addressFamily == AF_INET) + { + UINT32 remoteAddress = *((UINT32*)pRemoteAddress); + + remoteAddress = ntohl(remoteAddress); + + RtlCopyMemory(pSendParams->remoteAddress, + &remoteAddress, + addressSize); + } + else + { + RtlCopyMemory(pSendParams->remoteAddress, + pRemoteAddress, + addressSize); + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_REMOTE_SCOPE_ID)) + pSendParams->remoteScopeId = pMetadata->remoteScopeId; + } + + status = FwpsInjectTransportSendAsync(injectionHandle, + injectionContext, + endpointHandle, + 0, + pSendParams, + addressFamily, + compartmentID, + pClonedNetBufferList, + CompleteFastPacketInjection, + pSendParams); + + HLPR_BAIL_LABEL_2: + + if(status != STATUS_SUCCESS) + { +#pragma warning(push) +#pragma warning(disable: 28924) /// pSendParams could be NULL if the ALLOC fails + + if(pSendParams) + { + HLPR_DELETE_ARRAY(pSendParams->remoteAddress, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + } + + HLPR_DELETE(pSendParams, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + +#pragma warning(pop) + } + } + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + else if(injectionHandle == g_pIPv4InboundMACInjectionHandles[index] || + injectionHandle == g_pIPv6InboundMACInjectionHandles[index] || + injectionHandle == g_pInboundMACInjectionHandles[index]) + status = FwpsInjectMacReceiveAsync(injectionHandle, + injectionContext, + 0, + pClassifyValues->layerId, + interfaceIndex, + ndisPort, + pClonedNetBufferList, + CompleteFastPacketInjection, + 0); + else if(injectionHandle == g_pIPv4OutboundMACInjectionHandles[index] || + injectionHandle == g_pIPv6OutboundMACInjectionHandles[index] || + injectionHandle == g_pOutboundMACInjectionHandles[index]) + status = FwpsInjectMacSendAsync(injectionHandle, + injectionContext, + 0, + pClassifyValues->layerId, + interfaceIndex, + ndisPort, + pClonedNetBufferList, + CompleteFastPacketInjection, + 0); + else if(injectionHandle == g_pIPv4IngressVSwitchEthernetInjectionHandles[index] || + injectionHandle == g_pIPv6IngressVSwitchEthernetInjectionHandles[index] || + injectionHandle == g_pIngressVSwitchEthernetInjectionHandles[index] || + injectionHandle == g_pIPv4EgressVSwitchEthernetInjectionHandles[index] || + injectionHandle == g_pIPv6EgressVSwitchEthernetInjectionHandles[index] || + injectionHandle == g_pEgressVSwitchEthernetInjectionHandles[index]) + status = FwpsInjectvSwitchEthernetIngressAsync(injectionHandle, + injectionContext, + 0, + 0, + pVSwitchID, + sourcePortID, + sourceNICIndex, + pClonedNetBufferList, + CompleteFastPacketInjection, + 0); + +#endif ///(NTDDI_VERSION >= NTDDI_WIN8) + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS) + { + if(pClonedNetBufferList) + FwpsFreeCloneNetBufferList(pClonedNetBufferList, + 0); + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! ClassifyFastPacketInjection : FwpsInjectAsync() [status: %#x]\n", + status); + } + } + } + } + + return; +} + +#else + +/** + @classify_function="ClassifyFastPacketInjection" + + Purpose: Blocks the current NET_BUFFER_LIST and injects a clone back to the stack without + modification.
+
+ Notes: Applies to the following layers:
+ FWPS_LAYER_INBOUND_IPPACKET_V{4/6}
+ FWPS_LAYER_OUTBOUND_IPPACKET_V{4/6}
+ FWPS_LAYER_IPFORWARD_V{4/6}
+ FWPS_LAYER_INBOUND_TRANSPORT_V{4/6}
+ FWPS_LAYER_OUTBOUND_TRANSPORT_V{4/6}
+ FWPS_LAYER_DATAGRAM_DATA_V{4/6}
+ FWPS_LAYER_INBOUND_ICMP_ERROR_V{4/6}
+ FWPS_LAYER_OUTBOUND_ICMP_ERROR_V{4/6}
+ FWPS_LAYER_ALE_AUTH_CONNECT_V{4/6}
+ FWPS_LAYER_ALE_FLOW_ESTABLISHED_V{4/6}
+
+ Intended for test performance purposes only.
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF544890.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551202.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551134.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551183.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551185.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551177.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551186.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551188.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551170.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI ClassifyFastPacketInjection(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES0* pMetadata, + _Inout_opt_ VOID* pNetBufferList, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut) +{ + UNREFERENCED_PARAMETER(pFilter); + UNREFERENCED_PARAMETER(flowContext); + + if(pClassifyOut->rights & FWPS_RIGHT_ACTION_WRITE) + { + NTSTATUS status = STATUS_SUCCESS; + UINT32 ipHeaderSize = 0; + UINT32 transportHeaderSize = 0; + COMPARTMENT_ID compartmentID = DEFAULT_COMPARTMENT_ID; + UINT64 endpointHandle = 0; + ADDRESS_FAMILY addressFamily = AF_UNSPEC; + UINT8 protocol = 0; + FWP_DIRECTION direction = FWP_DIRECTION_MAX; + UINT32 flags = 0; + BOOLEAN isALEClassifyRequired = FALSE; + VOID* pRemoteAddress = 0; + IF_INDEX interfaceIndex = 0; + IF_INDEX subInterfaceIndex = 0; + HANDLE injectionHandle = 0; + FWPS_PACKET_INJECTION_STATE injectionState = FWPS_PACKET_INJECTION_STATE_MAX; + HANDLE injectionContext = 0; + UINT32 bytesRetreated = 0; + NET_BUFFER_LIST* pClonedNetBufferList = 0; + UINT32 index = WFPSAMPLER_INDEX; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_IP_HEADER_SIZE)) + ipHeaderSize = pMetadata->ipHeaderSize; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + transportHeaderSize = pMetadata->transportHeaderSize; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_COMPARTMENT_ID)) + compartmentID = (COMPARTMENT_ID)pMetadata->compartmentId; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_ENDPOINT_HANDLE)) + endpointHandle = pMetadata->transportEndpointHandle; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_PACKET_DIRECTION)) + direction = pMetadata->packetDirection; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_ALE_CLASSIFY_REQUIRED)) + isALEClassifyRequired = TRUE; + + if(pFilter->subLayerWeight == FWPM_SUBLAYER_UNIVERSAL_WEIGHT) + index = UNIVERSAL_INDEX; + + switch(pClassifyValues->layerId) + { + case FWPS_LAYER_INBOUND_IPPACKET_V4: + { + addressFamily = AF_INET; + + interfaceIndex = pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_IPPACKET_V4_INTERFACE_INDEX].value.uint32; + + subInterfaceIndex = pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_IPPACKET_V4_SUB_INTERFACE_INDEX].value.uint32; + + flags = pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_IPPACKET_V4_FLAGS].value.uint32; + + injectionHandle = g_pIPv4InboundNetworkInjectionHandles[index]; + + bytesRetreated = ipHeaderSize; + + break; + } + case FWPS_LAYER_INBOUND_IPPACKET_V6: + { + addressFamily = AF_INET6; + + interfaceIndex = pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_IPPACKET_V6_INTERFACE_INDEX].value.uint32; + + subInterfaceIndex = pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_IPPACKET_V6_SUB_INTERFACE_INDEX].value.uint32; + + flags = pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_IPPACKET_V6_FLAGS].value.uint32; + + injectionHandle = g_pIPv6InboundNetworkInjectionHandles[index]; + + bytesRetreated = ipHeaderSize; + + break; + } + case FWPS_LAYER_OUTBOUND_IPPACKET_V4: + { + addressFamily = AF_INET; + + flags = pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_IPPACKET_V4_FLAGS].value.uint32; + + injectionHandle = g_pIPv4OutboundNetworkInjectionHandles[index]; + + break; + } + case FWPS_LAYER_OUTBOUND_IPPACKET_V6: + { + addressFamily = AF_INET6; + + flags = pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_IPPACKET_V6_FLAGS].value.uint32; + + injectionHandle = g_pIPv6OutboundNetworkInjectionHandles[index]; + + break; + } + case FWPS_LAYER_IPFORWARD_V4: + { + addressFamily = AF_INET; + + interfaceIndex = pClassifyValues->incomingValue[FWPS_FIELD_IPFORWARD_V4_DESTINATION_INTERFACE_INDEX].value.uint32; + + flags = pClassifyValues->incomingValue[FWPS_FIELD_IPFORWARD_V4_FLAGS].value.uint32; + + if(direction == FWP_DIRECTION_OUTBOUND) + injectionHandle = g_pIPv4OutboundForwardInjectionHandles[index]; + else + injectionHandle = g_pIPv4InboundForwardInjectionHandles[index]; + + break; + } + case FWPS_LAYER_IPFORWARD_V6: + { + addressFamily = AF_INET6; + + interfaceIndex = pClassifyValues->incomingValue[FWPS_FIELD_IPFORWARD_V6_DESTINATION_INTERFACE_INDEX].value.uint32; + + flags = pClassifyValues->incomingValue[FWPS_FIELD_IPFORWARD_V6_FLAGS].value.uint32; + + if(direction == FWP_DIRECTION_OUTBOUND) + injectionHandle = g_pIPv6OutboundForwardInjectionHandles[index]; + else + injectionHandle = g_pIPv6InboundForwardInjectionHandles[index]; + + break; + } + case FWPS_LAYER_INBOUND_TRANSPORT_V4: + { + addressFamily = AF_INET; + + protocol = pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_TRANSPORT_V4_IP_PROTOCOL].value.uint8; + + interfaceIndex = pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_TRANSPORT_V4_INTERFACE_INDEX].value.uint32; + + subInterfaceIndex = pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_TRANSPORT_V4_SUB_INTERFACE_INDEX].value.uint32; + + flags = pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_TRANSPORT_V4_FLAGS].value.uint32; + + injectionHandle = g_pIPv4InboundTransportInjectionHandles[index]; + + if(protocol == ICMPV4) + bytesRetreated = ipHeaderSize; + else + bytesRetreated = ipHeaderSize + transportHeaderSize; + + break; + } + case FWPS_LAYER_INBOUND_TRANSPORT_V6: + { + addressFamily = AF_INET6; + + protocol = pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_TRANSPORT_V6_IP_PROTOCOL].value.uint8; + + interfaceIndex = pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_TRANSPORT_V6_INTERFACE_INDEX].value.uint32; + + subInterfaceIndex = pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_TRANSPORT_V6_SUB_INTERFACE_INDEX].value.uint32; + + flags = pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_TRANSPORT_V6_FLAGS].value.uint32; + + injectionHandle = g_pIPv6InboundTransportInjectionHandles[index]; + + if(protocol == ICMPV6) + bytesRetreated = ipHeaderSize; + else + bytesRetreated = ipHeaderSize + transportHeaderSize; + + break; + } + case FWPS_LAYER_OUTBOUND_TRANSPORT_V4: + { + addressFamily = AF_INET; + + protocol = pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_TRANSPORT_V4_IP_PROTOCOL].value.uint8; + + pRemoteAddress = &(pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_TRANSPORT_V4_IP_REMOTE_ADDRESS].value.uint32); + + flags = pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_TRANSPORT_V4_FLAGS].value.uint32; + + injectionHandle = g_pIPv4OutboundTransportInjectionHandles[index]; + + break; + } + case FWPS_LAYER_OUTBOUND_TRANSPORT_V6: + { + addressFamily = AF_INET6; + + protocol = pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_TRANSPORT_V6_IP_PROTOCOL].value.uint8; + + pRemoteAddress = pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_TRANSPORT_V6_IP_REMOTE_ADDRESS].value.byteArray16->byteArray16; + + flags = pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_TRANSPORT_V6_FLAGS].value.uint32; + + injectionHandle = g_pIPv6OutboundTransportInjectionHandles[index]; + + break; + } + case FWPS_LAYER_DATAGRAM_DATA_V4: + { + addressFamily = AF_INET; + + protocol = pClassifyValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V4_IP_PROTOCOL].value.uint8; + + direction = (FWP_DIRECTION)pClassifyValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V4_DIRECTION].value.uint32; + + flags = pClassifyValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V4_FLAGS].value.uint32; + + if(direction == FWP_DIRECTION_INBOUND) + { + interfaceIndex = pClassifyValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V4_INTERFACE_INDEX].value.uint32; + + subInterfaceIndex = pClassifyValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V4_SUB_INTERFACE_INDEX].value.uint32; + + injectionHandle = g_pIPv4InboundTransportInjectionHandles[index]; + + bytesRetreated = ipHeaderSize + transportHeaderSize; + } + else + { + pRemoteAddress = &(pClassifyValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V4_IP_REMOTE_ADDRESS].value.uint32); + + injectionHandle = g_pIPv4OutboundTransportInjectionHandles[index]; + } + + break; + } + case FWPS_LAYER_DATAGRAM_DATA_V6: + { + addressFamily = AF_INET6; + + protocol = pClassifyValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V6_IP_PROTOCOL].value.uint8; + + direction = (FWP_DIRECTION)pClassifyValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V6_DIRECTION].value.uint32; + + flags = pClassifyValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V6_FLAGS].value.uint32; + + if(direction == FWP_DIRECTION_INBOUND) + { + interfaceIndex = pClassifyValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V6_INTERFACE_INDEX].value.uint32; + + subInterfaceIndex = pClassifyValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V6_SUB_INTERFACE_INDEX].value.uint32; + + injectionHandle = g_pIPv6InboundTransportInjectionHandles[index]; + + bytesRetreated = ipHeaderSize + transportHeaderSize; + } + else + { + pRemoteAddress = pClassifyValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V6_IP_REMOTE_ADDRESS].value.byteArray16->byteArray16; + + injectionHandle = g_pIPv6OutboundTransportInjectionHandles[index]; + } + + break; + } + case FWPS_LAYER_INBOUND_ICMP_ERROR_V4: + { + addressFamily = AF_INET; + + protocol = ICMPV4; + + interfaceIndex = pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_ICMP_ERROR_V4_INTERFACE_INDEX].value.uint32; + + subInterfaceIndex = pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_ICMP_ERROR_V4_SUB_INTERFACE_INDEX].value.uint32; + + flags = pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_ICMP_ERROR_V4_FLAGS].value.uint32; + + injectionHandle = g_pIPv4InboundTransportInjectionHandles[index]; + + bytesRetreated = ipHeaderSize + transportHeaderSize; + + break; + } + case FWPS_LAYER_INBOUND_ICMP_ERROR_V6: + { + addressFamily = AF_INET6; + + protocol = ICMPV6; + + interfaceIndex = pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_ICMP_ERROR_V6_INTERFACE_INDEX].value.uint32; + + subInterfaceIndex = pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_ICMP_ERROR_V6_SUB_INTERFACE_INDEX].value.uint32; + + flags = pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_ICMP_ERROR_V6_FLAGS].value.uint32; + + injectionHandle = g_pIPv6InboundTransportInjectionHandles[index]; + + bytesRetreated = ipHeaderSize + transportHeaderSize; + + break; + } + case FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4: + { + addressFamily = AF_INET; + + protocol = ICMPV4; + + pRemoteAddress = &(pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_ICMP_ERROR_V4_IP_REMOTE_ADDRESS].value.uint32); + + flags = pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_ICMP_ERROR_V4_FLAGS].value.uint32; + + injectionHandle = g_pIPv4OutboundTransportInjectionHandles[index]; + + bytesRetreated = ipHeaderSize; + + break; + } + case FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6: + { + addressFamily = AF_INET6; + + protocol = ICMPV6; + + pRemoteAddress = pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_ICMP_ERROR_V6_IP_REMOTE_ADDRESS].value.byteArray16->byteArray16; + + flags = pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_ICMP_ERROR_V6_FLAGS].value.uint32; + + injectionHandle = g_pIPv6OutboundTransportInjectionHandles[index]; + + bytesRetreated = ipHeaderSize; + + break; + } + case FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4: + { + addressFamily = AF_INET; + + protocol = pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_IP_PROTOCOL].value.uint8; + + flags = pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_FLAGS].value.uint32; + + if(direction == FWP_DIRECTION_OUTBOUND) + { + pRemoteAddress = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_IP_REMOTE_ADDRESS].value.uint32); + + injectionHandle = g_pIPv4OutboundTransportInjectionHandles[index]; + } + else + { + interfaceIndex = pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_INTERFACE_INDEX].value.uint32; + + subInterfaceIndex = pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_SUB_INTERFACE_INDEX].value.uint32; + + injectionHandle = g_pIPv4InboundTransportInjectionHandles[index]; + + if(protocol == ICMPV4) + bytesRetreated = ipHeaderSize; + else + bytesRetreated = ipHeaderSize + transportHeaderSize; + } + + break; + } + case FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6: + { + addressFamily = AF_INET6; + + protocol = pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V6_IP_PROTOCOL].value.uint8; + + flags = pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V6_FLAGS].value.uint32; + + if(direction == FWP_DIRECTION_OUTBOUND) + { + pRemoteAddress = pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V6_IP_REMOTE_ADDRESS].value.byteArray16->byteArray16; + + injectionHandle = g_pIPv6OutboundTransportInjectionHandles[index]; + } + else + { + interfaceIndex = pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V6_INTERFACE_INDEX].value.uint32; + + subInterfaceIndex = pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V6_SUB_INTERFACE_INDEX].value.uint32; + + injectionHandle = g_pIPv6InboundTransportInjectionHandles[index]; + + if(protocol == ICMPV6) + bytesRetreated = ipHeaderSize; + else + bytesRetreated = ipHeaderSize + transportHeaderSize; + } + + break; + } + case FWPS_LAYER_ALE_AUTH_CONNECT_V4: + { + addressFamily = AF_INET; + + protocol = pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_PROTOCOL].value.uint8; + + flags = pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_FLAGS].value.uint32; + + bytesRetreated = ipHeaderSize + transportHeaderSize; + + if(direction == FWP_DIRECTION_INBOUND) + { + interfaceIndex = pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_INTERFACE_INDEX].value.uint32; + + subInterfaceIndex = pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_SUB_INTERFACE_INDEX].value.uint32; + + injectionHandle = g_pIPv4InboundTransportInjectionHandles[index]; + } + else + { + pRemoteAddress = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_REMOTE_ADDRESS].value.uint32); + + injectionHandle = g_pIPv4OutboundTransportInjectionHandles[index]; + } + + break; + } + case FWPS_LAYER_ALE_AUTH_CONNECT_V6: + { + addressFamily = AF_INET6; + + protocol = pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V6_IP_PROTOCOL].value.uint8; + + flags = pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V6_FLAGS].value.uint32; + + bytesRetreated = ipHeaderSize + transportHeaderSize; + + if(direction == FWP_DIRECTION_INBOUND) + { + interfaceIndex = pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V6_INTERFACE_INDEX].value.uint32; + + subInterfaceIndex = pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V6_SUB_INTERFACE_INDEX].value.uint32; + + injectionHandle = g_pIPv6InboundTransportInjectionHandles[index]; + } + else + { + pRemoteAddress = pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V6_IP_REMOTE_ADDRESS].value.byteArray16->byteArray16; + + injectionHandle = g_pIPv6OutboundTransportInjectionHandles[index]; + } + + break; + } + case FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4: + { + addressFamily = AF_INET; + + protocol = pClassifyValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_PROTOCOL].value.uint8; + + direction = (FWP_DIRECTION)pClassifyValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_DIRECTION].value.uint8; + + flags = pClassifyValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_FLAGS].value.uint32; + + if(direction == FWP_DIRECTION_INBOUND) + { + injectionHandle = g_pIPv4InboundTransportInjectionHandles[index]; + + if(ipHeaderSize == 0) + ipHeaderSize = IPV4_HEADER_MIN_SIZE; + + if(transportHeaderSize == 0) + { + if(protocol == ICMPV4) + transportHeaderSize = ICMP_HEADER_MIN_SIZE; + else if(protocol == TCP) + transportHeaderSize = TCP_HEADER_MIN_SIZE; + else if(protocol == UDP) + transportHeaderSize = UDP_HEADER_MIN_SIZE; + } + + if(protocol == ICMPV4) + bytesRetreated = ipHeaderSize; + else + bytesRetreated = ipHeaderSize + transportHeaderSize; + } + else + { + pRemoteAddress = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V6_IP_REMOTE_ADDRESS].value.uint32); + + injectionHandle = g_pIPv4OutboundTransportInjectionHandles[index]; + + bytesRetreated = ipHeaderSize; + } + + break; + } + case FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6: + { + addressFamily = AF_INET6; + + protocol = pClassifyValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V6_IP_PROTOCOL].value.uint8; + + direction = (FWP_DIRECTION)pClassifyValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V6_DIRECTION].value.uint8; + + flags = pClassifyValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V6_FLAGS].value.uint32; + + if(direction == FWP_DIRECTION_INBOUND) + { + injectionHandle = g_pIPv6InboundTransportInjectionHandles[index]; + + if(ipHeaderSize == 0) + ipHeaderSize = IPV6_HEADER_MIN_SIZE; + + if(transportHeaderSize == 0) + { + if(protocol == ICMPV6) + transportHeaderSize = ICMP_HEADER_MIN_SIZE; + else if(protocol == TCP) + transportHeaderSize = TCP_HEADER_MIN_SIZE; + else if(protocol == UDP) + transportHeaderSize = UDP_HEADER_MIN_SIZE; + } + + if(protocol == ICMPV6) + bytesRetreated = ipHeaderSize; + else + bytesRetreated = ipHeaderSize + transportHeaderSize; + } + else + { + pRemoteAddress = pClassifyValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V6_IP_REMOTE_ADDRESS].value.byteArray16->byteArray16; + + injectionHandle = g_pIPv6OutboundTransportInjectionHandles[index]; + + bytesRetreated = ipHeaderSize; + } + + break; + } + } + + pClassifyOut->actionType = FWP_ACTION_PERMIT; + + if(!((flags & FWP_CONDITION_FLAG_IS_LOOPBACK || + (flags & FWP_CONDITION_FLAG_IS_IPSEC_SECURED && + (flags & FWP_CONDITION_FLAG_REQUIRES_ALE_CLASSIFY || + isALEClassifyRequired))))) + { + injectionState = FwpsQueryPacketInjectionState(injectionHandle, + (NET_BUFFER_LIST*)pNetBufferList, + &injectionContext); + if(injectionState != FWPS_PACKET_INJECTED_BY_SELF && + injectionState != FWPS_PACKET_PREVIOUSLY_INJECTED_BY_SELF) + { + /// Due to TCP's locking semantics, TCP can only be injected Out of Band at any + /// transport layer or equivalent. + if(protocol == TCP && + (injectionHandle == g_pIPv4InboundTransportInjectionHandles[index] || + injectionHandle == g_pIPv6InboundTransportInjectionHandles[index] || + injectionHandle == g_pIPv4OutboundTransportInjectionHandles[index] || + injectionHandle == g_pIPv6OutboundTransportInjectionHandles[index])) + HLPR_BAIL; + + pClassifyOut->actionType = FWP_ACTION_BLOCK; + pClassifyOut->flags |= FWPS_CLASSIFY_OUT_FLAG_ABSORB; + pClassifyOut->rights ^= FWPS_RIGHT_ACTION_WRITE; + + if(bytesRetreated) + { + status = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pNetBufferList), + bytesRetreated, + 0, + 0); + HLPR_BAIL_ON_FAILURE(status); + } + + status = FwpsAllocateCloneNetBufferList((NET_BUFFER_LIST*)pNetBufferList, + g_pNDISPoolData->nblPoolHandle, + g_pNDISPoolData->nbPoolHandle, + 0, + &pClonedNetBufferList); + + if(bytesRetreated) + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pNetBufferList), + bytesRetreated, + FALSE, + 0); + + HLPR_BAIL_ON_FAILURE(status); + + if(injectionHandle == g_pIPv4InboundNetworkInjectionHandles[index] || + injectionHandle == g_pIPv6InboundNetworkInjectionHandles[index]) + status = FwpsInjectNetworkReceiveAsync(injectionHandle, + injectionContext, + 0, + compartmentID, + interfaceIndex, + subInterfaceIndex, + pClonedNetBufferList, + CompleteFastPacketInjection, + 0); + else if(injectionHandle == g_pIPv4OutboundNetworkInjectionHandles[index] || + injectionHandle == g_pIPv6OutboundNetworkInjectionHandles[index]) + status = FwpsInjectNetworkSendAsync(injectionHandle, + injectionContext, + 0, + compartmentID, + pClonedNetBufferList, + CompleteFastPacketInjection, + 0); + else if(injectionHandle == g_pIPv4InboundForwardInjectionHandles[index] || + injectionHandle == g_pIPv6InboundForwardInjectionHandles[index] || + injectionHandle == g_pIPv4OutboundForwardInjectionHandles[index] || + injectionHandle == g_pIPv6OutboundForwardInjectionHandles[index]) + status = FwpsInjectForwardAsync(injectionHandle, + injectionContext, + 0, + addressFamily, + compartmentID, + interfaceIndex, + pClonedNetBufferList, + CompleteFastPacketInjection, + 0); + else if(injectionHandle == g_pIPv4InboundTransportInjectionHandles[index] || + injectionHandle == g_pIPv6InboundTransportInjectionHandles[index]) + status = FwpsInjectTransportReceiveAsync(injectionHandle, + injectionContext, + 0, + 0, + addressFamily, + compartmentID, + interfaceIndex, + subInterfaceIndex, + pClonedNetBufferList, + CompleteFastPacketInjection, + 0); + else if(injectionHandle == g_pIPv4OutboundTransportInjectionHandles[index] || + injectionHandle == g_pIPv6OutboundTransportInjectionHandles[index]) + { + FWPS_TRANSPORT_SEND_PARAMS* pSendParams = 0; + UINT32 addressSize = addressFamily == AF_INET ? IPV4_ADDRESS_SIZE : IPV6_ADDRESS_SIZE; + +#pragma warning(push) +#pragma warning(disable: 6014) /// pSendParams will be freed in completionFn using FastPacketInjectionCompletionDataDestroy + + HLPR_NEW(pSendParams, + FWPS_TRANSPORT_SEND_PARAMS, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE_2(pSendParams, + status); + + HLPR_NEW_ARRAY(pSendParams->remoteAddress, + BYTE, + addressSize, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE_2(pSendParams->remoteAddress, + status); + +#pragma warning(pop) + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_CONTROL_DATA)) + { + pSendParams->controlData = pMetadata->controlData; + pSendParams->controlDataLength = pMetadata->controlDataLength; + } + + if(addressFamily == AF_INET) + { + UINT32 remoteAddress = *((UINT32*)pRemoteAddress); + + remoteAddress = ntohl(remoteAddress); + + RtlCopyMemory(pSendParams->remoteAddress, + &remoteAddress, + addressSize); + } + else + { + RtlCopyMemory(pSendParams->remoteAddress, + pRemoteAddress, + addressSize); + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_REMOTE_SCOPE_ID)) + pSendParams->remoteScopeId = pMetadata->remoteScopeId; + } + + status = FwpsInjectTransportSendAsync(injectionHandle, + injectionContext, + endpointHandle, + 0, + pSendParams, + addressFamily, + compartmentID, + pClonedNetBufferList, + CompleteFastPacketInjection, + pSendParams); + + HLPR_BAIL_LABEL_2: + + if(status != STATUS_SUCCESS) + { +#pragma warning(push) +#pragma warning(disable: 28924) /// pSendParams could be NULL if the ALLOC fails + + if(pSendParams) + { + HLPR_DELETE_ARRAY(pSendParams->remoteAddress, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + } + + HLPR_DELETE(pSendParams, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + +#pragma warning(pop) + } + } + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS) + { + if(pClonedNetBufferList) + FwpsFreeCloneNetBufferList(pClonedNetBufferList, + 0); + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! ClassifyFastPacketInjection : FwpsInjectAsync() [status: %#x]\n", + status); + } + } + } + } + + return;} + +#endif // (NTDDI_VERSION >= NTDDI_WIN7) diff --git a/network/trans/WFPSampler/sys/ClassifyFunctions_FastPacketInjectionCallouts.h b/network/trans/WFPSampler/sys/ClassifyFunctions_FastPacketInjectionCallouts.h new file mode 100644 index 000000000..5a7582119 --- /dev/null +++ b/network/trans/WFPSampler/sys/ClassifyFunctions_FastPacketInjectionCallouts.h @@ -0,0 +1,56 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// ClassifyFunctions_FastPacketInjectionCallouts.h +// +// Abstract: +// This module contains prototypes for WFP Classify functions that inject packets back into the +// data path using the clone / block / inject method. This method is inline only, no +// modification, and uses as little validation and error checking as possible. Certain +// scenarios will definitely fail injection, such as injection to loopback, or IPsec +// encrypted packets. These functions are meant for test performance purposes only. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Enhance function declaration for IntelliSense +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef CLASSIFY_FAST_PACKET_INJECTION_H +#define CLASSIFY_FAST_PACKET_INJECTION_H + +#if(NTDDI_VERSION >= NTDDI_WIN7) + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI ClassifyFastPacketInjection(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pLayerData, + _In_opt_ const VOID* pClassifyContext, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut); + +#else + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI ClassifyFastPacketInjection(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pLayerData, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut); + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + +#endif /// CLASSIFY_FAST_PACKET_INJECTION_H diff --git a/network/trans/WFPSampler/sys/ClassifyFunctions_FastStreamInjectionCallouts.cpp b/network/trans/WFPSampler/sys/ClassifyFunctions_FastStreamInjectionCallouts.cpp new file mode 100644 index 000000000..07822e88d --- /dev/null +++ b/network/trans/WFPSampler/sys/ClassifyFunctions_FastStreamInjectionCallouts.cpp @@ -0,0 +1,295 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// ClassifyFunctions_FastStreamInjectionCallouts.cpp +// +// Abstract: +// This module contains WFP Classify functions that inject data back into the stream using +// the clone / block / inject method. This method is inline only, no modification, +// and uses as little validation and error checking as possible. These functions are meant +// for test performance purposes only. +// +// Naming Convention: +// +// +// +// i.e. +// ClassifyFastStreamInjection +// +// +// Classify - Function is an FWPS_CALLOUT_CLASSIFY_FN +// +// FastStreamInjection - Function demonstrates the clone / block / inject model in the +// fastest form available (inline, no validation, etc.). +// +// Private Functions: +// +// Public Functions: +// ClassifyFastStreamInjection(), +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Enhance function declaration for IntelliSense, enhance +// traces, and add support for multiple injectors. +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "Framework_WFPSamplerCalloutDriver.h" /// . +#include "ClassifyFunctions_FastStreamInjectionCallouts.tmh" /// $(OBJ_PATH)\$(O)\ + +#if(NTDDI_VERSION >= NTDDI_WIN7) + +/** + @classify_function="ClassifyFastStreamInjection" + + Purpose: Blocks the current stream data and injects a clone back to the stack without + modification.
+
+ Notes: Applies to the following layers:
+ FWPS_LAYER_STREAM_V{4/6}
+
+ Intended for test performance purposes only.
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF544893.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551149.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551213.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551161.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI ClassifyFastStreamInjection(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pStreamCalloutIOPacket, + _In_opt_ const VOID* pClassifyContext, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut) +{ + UNREFERENCED_PARAMETER(pClassifyContext); + UNREFERENCED_PARAMETER(flowContext); + + /// Stream has no concept of Veto. Due to its "waterfall" nature where a block will + /// remove the data and others will not be able to see it. + /// This means that if we got classified, regardless of the rights, we can do whatever we need to. + + if(pStreamCalloutIOPacket) + { + NTSTATUS status = STATUS_SUCCESS; + FWPS_STREAM_CALLOUT_IO_PACKET* pStreamPacket = (FWPS_STREAM_CALLOUT_IO_PACKET*)pStreamCalloutIOPacket; + FWP_DIRECTION direction = (FWP_DIRECTION)pClassifyValues->incomingValue[FWPS_FIELD_STREAM_V4_DIRECTION].value.uint32; + HANDLE injectionHandle = 0; + UINT64 flowID = 0; + UINT32 streamFlags = pStreamPacket->streamData->flags; + NET_BUFFER_LIST* pNetBufferListChain = 0; + UINT32 index = WFPSAMPLER_INDEX; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_FLOW_HANDLE)) + flowID = pMetadata->flowHandle; + + if(pFilter->subLayerWeight == FWPM_SUBLAYER_UNIVERSAL_WEIGHT) + index = UNIVERSAL_INDEX; + + if(pClassifyValues->layerId == FWPS_LAYER_STREAM_V4) + { + if(direction == FWP_DIRECTION_OUTBOUND) + injectionHandle = g_pIPv4OutboundStreamInjectionHandles[index]; + else + injectionHandle = g_pIPv4InboundStreamInjectionHandles[index]; + } + else if(pClassifyValues->layerId == FWPS_LAYER_STREAM_V6) + { + if(direction == FWP_DIRECTION_OUTBOUND) + injectionHandle = g_pIPv6OutboundStreamInjectionHandles[index]; + else + injectionHandle = g_pIPv6InboundStreamInjectionHandles[index]; + } + else + HLPR_BAIL; + + if(!(streamFlags & FWPS_STREAM_FLAG_RECEIVE) && + !(streamFlags & FWPS_STREAM_FLAG_SEND)) + streamFlags |= direction ? FWPS_STREAM_FLAG_RECEIVE : FWPS_STREAM_FLAG_SEND; + + pStreamPacket->countBytesRequired = 0; + pStreamPacket->countBytesEnforced = pStreamPacket->streamData->dataLength; + pStreamPacket->streamAction = FWPS_STREAM_ACTION_NONE; + + status = FwpsCloneStreamData(pStreamPacket->streamData, + g_pNDISPoolData->nblPoolHandle, + g_pNDISPoolData->nbPoolHandle, + 0, + &pNetBufferListChain); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicStreamInjection : FwpsCloneStreamData() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + /// Do not touch the rights in case other callouts use them (which they shouldn't). + pClassifyOut->actionType = FWP_ACTION_BLOCK; + + status = FwpsStreamInjectAsync(injectionHandle, + 0, + 0, + flowID, + pFilter->action.calloutId, + pClassifyValues->layerId, + streamFlags, + pNetBufferListChain, + pStreamPacket->streamData->dataLength, + CompleteFastStreamInjection, + 0); + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS && + pNetBufferListChain) + { + KIRQL irql = KeGetCurrentIrql(); + + FwpsDiscardClonedStreamData(pNetBufferListChain, + 0, + irql == DISPATCH_LEVEL ? TRUE : FALSE); + } + } + + return; +} + +#else + +/** + @classify_function="ClassifyFastStreamInjection" + + Purpose: Blocks the current stream data and injects a clone back to the stack without + modification.
+
+ Notes: Applies to the following layers:
+ FWPS_LAYER_STREAM_V{4/6}
+
+ Intended for test performance purposes only.
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF544890.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551149.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551213.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551161.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI ClassifyFastStreamInjection(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES0* pMetadata, + _Inout_opt_ VOID* pStreamCalloutIOPacket, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut) +{ + UNREFERENCED_PARAMETER(flowContext); + + /// Stream has no concept of Veto. Due to its "waterfall" nature where a block will + /// remove the data and others will not be able to see it. + /// This means that if we got classified, regardless of the rights, we can do whatever we need to. + + if(pStreamCalloutIOPacket) + { + NTSTATUS status = STATUS_SUCCESS; + FWPS_STREAM_CALLOUT_IO_PACKET* pStreamPacket = (FWPS_STREAM_CALLOUT_IO_PACKET*)pStreamCalloutIOPacket; + FWP_DIRECTION direction = (FWP_DIRECTION)pClassifyValues->incomingValue[FWPS_FIELD_STREAM_V4_DIRECTION].value.uint32; + HANDLE injectionHandle = 0; + UINT64 flowID = 0; + UINT32 streamFlags = pStreamPacket->streamData->flags; + NET_BUFFER_LIST* pNetBufferListChain = 0; + UINT32 index = WFPSAMPLER_INDEX; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_FLOW_HANDLE)) + flowID = pMetadata->flowHandle; + + if(pFilter->subLayerWeight == FWPM_SUBLAYER_UNIVERSAL_WEIGHT) + index = UNIVERSAL_INDEX; + + if(pClassifyValues->layerId == FWPS_LAYER_STREAM_V4) + { + if(direction == FWP_DIRECTION_OUTBOUND) + injectionHandle = g_pIPv4OutboundStreamInjectionHandles[index]; + else + injectionHandle = g_pIPv4InboundStreamInjectionHandles[index]; + } + else if(pClassifyValues->layerId == FWPS_LAYER_STREAM_V6) + { + if(direction == FWP_DIRECTION_OUTBOUND) + injectionHandle = g_pIPv6OutboundStreamInjectionHandles[index]; + else + injectionHandle = g_pIPv6InboundStreamInjectionHandles[index]; + } + else + HLPR_BAIL; + + if(!(streamFlags & FWPS_STREAM_FLAG_RECEIVE) && + !(streamFlags & FWPS_STREAM_FLAG_SEND)) + streamFlags |= direction ? FWPS_STREAM_FLAG_RECEIVE : FWPS_STREAM_FLAG_SEND; + + pStreamPacket->countBytesRequired = 0; + pStreamPacket->countBytesEnforced = pStreamPacket->streamData->dataLength; + pStreamPacket->streamAction = FWPS_STREAM_ACTION_NONE; + + status = FwpsCloneStreamData(pStreamPacket->streamData, + g_pNDISPoolData->nblPoolHandle, + g_pNDISPoolData->nbPoolHandle, + 0, + &pNetBufferListChain); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicStreamInjection : FwpsCloneStreamData() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + /// Do not touch the rights in case other callouts use them (which they shouldn't). + pClassifyOut->actionType = FWP_ACTION_BLOCK; + + status = FwpsStreamInjectAsync(injectionHandle, + 0, + 0, + flowID, + pFilter->action.calloutId, + pClassifyValues->layerId, + streamFlags, + pNetBufferListChain, + pStreamPacket->streamData->dataLength, + CompleteFastStreamInjection, + 0); + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS && + pNetBufferListChain) + { + KIRQL irql = KeGetCurrentIrql(); + + FwpsDiscardClonedStreamData(pNetBufferListChain, + 0, + irql == DISPATCH_LEVEL ? TRUE : FALSE); + } + } + + return; +} + +#endif // (NTDDI_VERSION >= NTDDI_WIN7) diff --git a/network/trans/WFPSampler/sys/ClassifyFunctions_FastStreamInjectionCallouts.h b/network/trans/WFPSampler/sys/ClassifyFunctions_FastStreamInjectionCallouts.h new file mode 100644 index 000000000..9db087dfb --- /dev/null +++ b/network/trans/WFPSampler/sys/ClassifyFunctions_FastStreamInjectionCallouts.h @@ -0,0 +1,55 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// ClassifyFunctions_FastStreamInjectionCallouts.h +// +// Abstract: +// This module contains prototypes for WFP Classify functions that inject data back into the +// stream using the clone / block / inject method. This method is inline only, no +// modification, and uses as little validation and error checking as possible. These +// functions are meant for test performance purposes only. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Enhance function declaration for IntelliSense +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef CLASSIFY_FAST_STREAM_INJECTION_H +#define CLASSIFY_FAST_STREAM_INJECTION_H + +#if(NTDDI_VERSION >= NTDDI_WIN7) + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI ClassifyFastStreamInjection(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pLayerData, + _In_opt_ const VOID* pClassifyContext, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut); + +#else + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI ClassifyFastStreamInjection(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pLayerData, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut); + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + +#endif /// CLASSIFY_FAST_STREAM_INJECTION_H diff --git a/network/trans/WFPSampler/sys/ClassifyFunctions_FlowAssociationCallouts.cpp b/network/trans/WFPSampler/sys/ClassifyFunctions_FlowAssociationCallouts.cpp new file mode 100644 index 000000000..179a7ac09 --- /dev/null +++ b/network/trans/WFPSampler/sys/ClassifyFunctions_FlowAssociationCallouts.cpp @@ -0,0 +1,238 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// ClassifyFunctions_FlowAssocationCallouts.cpp +// +// Abstract: +// This module contains WFP Classify functions for associating contexts with flows. +// +// Naming Convention: +// +// +// +// i.e. +// +// ClassifyFlowAssociation +// +// +// Classify - Function is an FWPS_CALLOUT_CLASSIFY_FN. +// Perform - Executes the desired scenario. +// +// FlowAssociation - Function demonstates associating contexts with flows. +// +// Private Functions: +// PerformFlowAssociation(), +// +// Public Functions: +// ClassifyFlowAssociation(), +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// December 13, 2013 - 1.1 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "Framework_WFPSamplerCalloutDriver.h" /// . +#include "ClassifyFunctions_FlowAssociationCallouts.tmh" /// $(OBJ_PATH)\$(O)\ + +NTSTATUS PerformFlowAssociation(_In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _In_ const PC_FLOW_ASSOCIATION_DATA* pFlowAssociationData) +{ + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PerformFlowAssociation()\n"); + +#endif /// DBG + + NT_ASSERT(pMetadata); + NT_ASSERT(pFlowAssociationData); + + NTSTATUS status = STATUS_SUCCESS; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_FLOW_HANDLE)) + { + for(UINT32 index = 0; + index < pFlowAssociationData->itemCount; + index++) + { + FLOW_CONTEXT* pFlowContext = 0; + +#pragma warning(push) +#pragma warning(disable: 6014) /// pNotifyData is expected to be cleaned up by NotifyFlowDeleteNotification + + status = KrnlHlprFlowContextCreate(&pFlowContext, + pMetadata->flowHandle, + pFlowAssociationData->pLayerIDs[index], + pFlowAssociationData->pCalloutIDs[index], + CONTEXT_TYPE_DEFAULT, + pFlowAssociationData); + HLPR_BAIL_ON_FAILURE(status); + +#pragma warning(pop) + + } + } + + HLPR_BAIL_LABEL: + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PerformFlowAssociation() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +#if(NTDDI_VERSION >= NTDDI_WIN7) + +/** + @classify_function="ClassifyFlowAssociation" + + Purpose: Associates a context with a flow which is available for each of the layers + specified in the provider context
+
+ Notes: Applies to the following layers:
+ FWPS_LAYER_ALE_FLOW_ESTABLISHED_V{4/6}
+
+ MSDN_Ref:
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI ClassifyFlowAssociation(_In_ const FWPS_INCOMING_VALUES0* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pNetBufferList, + _In_opt_ const VOID* pClassifyContext, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT0* pClassifyOut) +{ + NT_ASSERT(pClassifyValues); + NT_ASSERT(pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6); + NT_ASSERT(pMetadata); + NT_ASSERT(pFilter); + NT_ASSERT(pFilter->providerContext); + NT_ASSERT(pFilter->providerContext->type == FWPM_GENERAL_CONTEXT); + NT_ASSERT(pFilter->providerContext->dataBuffer); + NT_ASSERT(pFilter->providerContext->dataBuffer); + NT_ASSERT(pFilter->providerContext->dataBuffer->size == sizeof(PC_FLOW_ASSOCIATION_DATA)); + NT_ASSERT(pFilter->providerContext->dataBuffer->data); + NT_ASSERT(pClassifyOut); + + UNREFERENCED_PARAMETER(pNetBufferList); + UNREFERENCED_PARAMETER(pClassifyContext); + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> ClassifyFlowAssociation() [Layer: %s][FilterID: %#I64x][Rights: %#x]", + KrnlHlprFwpsLayerIDToString(pClassifyValues->layerId), + pFilter->filterId, + pClassifyOut->rights); + + if(pClassifyOut->rights & FWPS_RIGHT_ACTION_WRITE) + { + pClassifyOut->actionType = FWP_ACTION_CONTINUE; + + /// ensure we only associate the context once + if(flowContext == 0 && + !(pClassifyValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_FLAGS].value.uint32 & FWP_CONDITION_FLAG_IS_REAUTHORIZE)) + PerformFlowAssociation(pMetadata, + (PC_FLOW_ASSOCIATION_DATA*)pFilter->providerContext->dataBuffer->data); + } + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- ClassifyFlowAssociation() [Layer: %s][FilterID: %#I64x][Action: %#x][Rights: %#x][Absorb: %s]\n", + KrnlHlprFwpsLayerIDToString(pClassifyValues->layerId), + pFilter->filterId, + pClassifyOut->actionType, + pClassifyOut->rights, + (pClassifyOut->flags & FWPS_CLASSIFY_OUT_FLAG_ABSORB) ? "TRUE" : "FALSE"); + + return; +} + +#else + +/** + @classify_function="ClassifyFlowAssociation" + + Purpose: Associates a context with a flow which is available for each of the layers + specified in the provider context
+
+ Notes: Applies to the following layers:
+ FWPS_LAYER_ALE_FLOW_ESTABLISHED_V{4/6}
+
+ MSDN_Ref:
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI ClassifyFlowAssociation(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pNetBufferList, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut) +{ + NT_ASSERT(pClassifyValues); + NT_ASSERT(pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6); + NT_ASSERT(pMetadata); + NT_ASSERT(pFilter); + NT_ASSERT(pFilter->providerContext); + NT_ASSERT(pFilter->providerContext->type == FWPM_GENERAL_CONTEXT); + NT_ASSERT(pFilter->providerContext->dataBuffer); + NT_ASSERT(pFilter->providerContext->dataBuffer); + NT_ASSERT(pFilter->providerContext->dataBuffer->size == sizeof(PC_FLOW_ASSOCIATION_DATA)); + NT_ASSERT(pFilter->providerContext->dataBuffer->data); + NT_ASSERT(pClassifyOut); + + UNREFERENCED_PARAMETER(pNetBufferList); + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> ClassifyFlowAssociation() [Layer: %s][FilterID: %#I64x][Rights: %#x]", + KrnlHlprFwpsLayerIDToString(pClassifyValues->layerId), + pFilter->filterId, + pClassifyOut->rights); + + if(pClassifyOut->rights & FWPS_RIGHT_ACTION_WRITE) + { + pClassifyOut->actionType = FWP_ACTION_CONTINUE; + + /// ensure we only associate the context once + if(flowContext == 0 && + !(pClassifyValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_FLAGS].value.uint32 & FWP_CONDITION_FLAG_IS_REAUTHORIZE)) + PerformFlowAssociation(pMetadata, + (PC_FLOW_ASSOCIATION_DATA*)pFilter->providerContext->dataBuffer->data); + } + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- ClassifyFlowAssociation() [Layer: %s][FilterID: %#I64x][Action: %#x][Rights: %#x][Absorb: %s]\n", + KrnlHlprFwpsLayerIDToString(pClassifyValues->layerId), + pFilter->filterId, + pClassifyOut->actionType, + pClassifyOut->rights, + (pClassifyOut->flags & FWPS_CLASSIFY_OUT_FLAG_ABSORB) ? "TRUE" : "FALSE"); + + return; +} + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) diff --git a/network/trans/WFPSampler/sys/ClassifyFunctions_FlowAssociationCallouts.h b/network/trans/WFPSampler/sys/ClassifyFunctions_FlowAssociationCallouts.h new file mode 100644 index 000000000..c9e6a5396 --- /dev/null +++ b/network/trans/WFPSampler/sys/ClassifyFunctions_FlowAssociationCallouts.h @@ -0,0 +1,51 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// ClassifyFunctions_FlowAssociationCallouts.h +// +// Abstract: +// This module contains prototypes of WFP Classify functions for associating context to flows. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// December 13, 2013 - 1.1 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef CLASSIFY_FLOW_ASSOCIATION_H +#define CLASSIFY_FLOW_ASSOCIATION_H + +#if(NTDDI_VERSION >= NTDDI_WIN7) + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI ClassifyFlowAssociation(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pLayerData, + _In_opt_ const VOID* pClassifyContext, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut); + +#else + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI ClassifyFlowAssociation(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pLayerData, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut); + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + +#endif /// CLASSIFY_FLOW_ASSOCIATION_H diff --git a/network/trans/WFPSampler/sys/ClassifyFunctions_Include.h b/network/trans/WFPSampler/sys/ClassifyFunctions_Include.h new file mode 100644 index 000000000..41b201b38 --- /dev/null +++ b/network/trans/WFPSampler/sys/ClassifyFunctions_Include.h @@ -0,0 +1,41 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// ClassifyFunctions_Include.h +// +// Abstract: +// This module contains a central repository of headers which contain prototypes for all of the +// classify functions. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Add ClassifyFunctions_AdvancedPacketInjection.h, +// ClassifyFunctions_FlowAssociation.h, and +// ClassifyFunctions_EndpointClosure.h +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef CLASSIFY_INCLUDE_H +#define CLASSIFY_INCLUDE_H + +#include "ClassifyFunctions_AdvancedPacketInjectionCallouts.h" /// . +#include "ClassifyFunctions_BasicActionCallouts.h" /// . +#include "ClassifyFunctions_BasicPacketExaminationCallouts.h" /// . +#include "ClassifyFunctions_BasicPacketInjectionCallouts.h" /// . +#include "ClassifyFunctions_BasicPacketModificationCallouts.h" /// . +#include "ClassifyFunctions_BasicStreamInjectionCallouts.h" /// . +#include "ClassifyFunctions_FastPacketInjectionCallouts.h" /// . +#include "ClassifyFunctions_FastStreamInjectionCallouts.h" /// . +#include "ClassifyFunctions_FlowAssociationCallouts.h" /// . +#include "ClassifyFunctions_PendAuthorizationCallouts.h" /// . +#include "ClassifyFunctions_PendEndpointClosureCallouts.h" /// . +#include "ClassifyFunctions_ProxyCallouts.h" /// . + +#endif /// CLASSIFY_INCLUDE_H \ No newline at end of file diff --git a/network/trans/WFPSampler/sys/ClassifyFunctions_PendAuthorizationCallouts.cpp b/network/trans/WFPSampler/sys/ClassifyFunctions_PendAuthorizationCallouts.cpp new file mode 100644 index 000000000..f4f23ebaf --- /dev/null +++ b/network/trans/WFPSampler/sys/ClassifyFunctions_PendAuthorizationCallouts.cpp @@ -0,0 +1,1015 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// ClassifyFunctions_PendAuthorizationCallouts.cpp +// +// Abstract: +// This module contains WFP Classify functions for pending and completing authorizations. +// +// Naming Convention: +// +// +// +// i.e. +// +// ClassifyPendAuthorization +// +// +// Classify - Function is an FWPS_CALLOUT_CLASSIFY_FN. +// Perform - Function executes the desired scenario. +// Prv - Function is a private helper to this module. +// Trigger - +// +// PendAuthorization - Function demonstates pending authorization requests. +// +// Author: +// Dusty Harper (DHarper) +// +// Private Functions: +// PendAuthorizationWorkItemRoutine(), +// PerformPendAuthorization(), +// PrvCloneAuthorizedNBLAndInject() +// TriggerPendAuthorizationOutOfBand(), +// +// Public Functions: +// PendAuthorizationDeferredProcedureCall(), +// ClassifyPendAuthorization(), +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Enhance function declaration for IntelliSense, enhance +// traces, and add support for controlData. +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "Framework_WFPSamplerCalloutDriver.h" /// . +#include "ClassifyFunctions_PendAuthorizationCallouts.tmh" /// $(OBJ_PATH)\$(O)\ + +/** + @private_function="PrvCloneAuthorizedNBLAndInject" + + Purpose:
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS PrvCloneAuthorizedNBLAndInject(_Inout_ CLASSIFY_DATA** ppClassifyData, + _Inout_ INJECTION_DATA** ppInjectionData) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PrvCloneAuthorizedNBLAndInject()\n"); + +#endif /// DBG + + NT_ASSERT(ppClassifyData); + NT_ASSERT(ppInjectionData); + NT_ASSERT(*ppClassifyData); + NT_ASSERT(*ppInjectionData); + + NTSTATUS status = STATUS_SUCCESS; + FWPS_INCOMING_VALUES* pClassifyValues = (FWPS_INCOMING_VALUES*)(*ppClassifyData)->pClassifyValues; + FWPS_INCOMING_METADATA_VALUES* pMetadata = (FWPS_INCOMING_METADATA_VALUES*)(*ppClassifyData)->pMetadataValues; + UINT32 bytesRetreated = 0; + COMPARTMENT_ID compartmentID = DEFAULT_COMPARTMENT_ID; + BOOLEAN isInbound = FALSE; + NET_BUFFER_LIST* pNetBufferList = 0; + PEND_AUTHORIZATION_COMPLETION_DATA* pCompletionData = 0; + FWPS_TRANSPORT_SEND_PARAMS* pSendParams = 0; + BYTE* pRemoteAddress = 0; + +#pragma warning(push) +#pragma warning(disable: 6014) /// pCompletionData will be freed in completionFn using PendAuthorizationCompletionDataDestroy + + HLPR_NEW(pCompletionData, + PEND_AUTHORIZATION_COMPLETION_DATA, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pCompletionData, + status); + + HLPR_NEW(pSendParams, + FWPS_TRANSPORT_SEND_PARAMS, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pSendParams, + status); + +#pragma warning(pop) + + KeInitializeSpinLock(&(pCompletionData->spinLock)); + + pCompletionData->performedInline = FALSE; + pCompletionData->pClassifyData = *ppClassifyData; + pCompletionData->pInjectionData = *ppInjectionData; + pCompletionData->pSendParams = pSendParams; + + /// Responsibility for freeing this memory has been transferred to the pCompletionData + *ppClassifyData = 0; + + *ppInjectionData = 0; + + pSendParams = 0; + + if(pCompletionData->pInjectionData->direction == FWP_DIRECTION_INBOUND) + isInbound = TRUE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_COMPARTMENT_ID)) + compartmentID = (COMPARTMENT_ID)pMetadata->compartmentId; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_IP_HEADER_SIZE)) + bytesRetreated = pMetadata->ipHeaderSize; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + bytesRetreated += pMetadata->transportHeaderSize; + + if(pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6) + { + if(isInbound) /// NBL offset is at the start of the transport header ... + { + if(bytesRetreated) + { + /// so retreat (size of IP Header) to clone the whole NBL + status = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket), + bytesRetreated, + 0, + 0); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PrvCloneAuthorizedNBLAndInject: NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + + HLPR_BAIL; + } + } + } + } + else if(pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V6) + { + /// NBL offset is at the Transport Header, and no IP Header is present yet + bytesRetreated = 0; + + isInbound = FALSE; + } + else + { + status = STATUS_FWP_INCOMPATIBLE_LAYER; + + HLPR_BAIL; + } + + status = FwpsAllocateCloneNetBufferList((NET_BUFFER_LIST*)(pCompletionData->pClassifyData->pPacket), + g_pNDISPoolData->nblPoolHandle, + g_pNDISPoolData->nbPoolHandle, + 0, + &pNetBufferList); + + if(bytesRetreated) + { + /// Advance the NBL offset so we are back at the expected position in the NET_BUFFER_LIST + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket), + bytesRetreated, + FALSE, + 0); + } + + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PrvCloneAuthorizedNBLAndInject : FwpsAllocateCloneNetBufferList() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + if(isInbound) + { + FWP_VALUE* pInterfaceIndex = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_INTERFACE_INDEX); + FWP_VALUE* pSubInterfaceIndex = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_SUB_INTERFACE_INDEX); + IF_INDEX interfaceIndex = 0; + IF_INDEX subInterfaceIndex = 0; + + if(pInterfaceIndex && + pInterfaceIndex->type == FWP_UINT32) + interfaceIndex = (IF_INDEX)pInterfaceIndex->uint32; + + if(pSubInterfaceIndex && + pSubInterfaceIndex->type == FWP_UINT32) + subInterfaceIndex = (IF_INDEX)pSubInterfaceIndex->uint32; + + status = FwpsInjectTransportReceiveAsync(pCompletionData->pInjectionData->injectionHandle, + pCompletionData->pInjectionData->injectionContext, + 0, + 0, + pCompletionData->pInjectionData->addressFamily, + compartmentID, + interfaceIndex, + subInterfaceIndex, + pNetBufferList, + CompletePendAuthorization, + pCompletionData); + } + else + { + UINT64 endpointHandle = 0; + FWP_VALUE* pAddressValue = 0; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_ENDPOINT_HANDLE)) + endpointHandle = pMetadata->transportEndpointHandle; + + pAddressValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_REMOTE_ADDRESS); + if(pAddressValue) + { + if(pCompletionData->pInjectionData->addressFamily == AF_INET) + { + UINT32 tempAddress = htonl(pAddressValue->uint32); + +#pragma warning(push) +#pragma warning(disable: 6014) /// pRemoteAddress will be freed in completionFn using PendAuthorizationCompletionDataDestroy + + HLPR_NEW_ARRAY(pRemoteAddress, + BYTE, + IPV4_ADDRESS_SIZE, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pRemoteAddress, + status); + +#pragma warning(pop) + + RtlCopyMemory(pRemoteAddress, + &tempAddress, + IPV4_ADDRESS_SIZE); + } + else + { + +#pragma warning(push) +#pragma warning(disable: 6014) /// pRemoteAddress will be freed in completionFn using PendAuthorizationCompletionDataDestroy + + HLPR_NEW_ARRAY(pRemoteAddress, + BYTE, + IPV6_ADDRESS_SIZE, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pRemoteAddress, + status); + +#pragma warning(pop) + + RtlCopyMemory(pRemoteAddress, + pAddressValue->byteArray16->byteArray16, + IPV6_ADDRESS_SIZE); + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_REMOTE_SCOPE_ID)) + pCompletionData->pSendParams->remoteScopeId = pMetadata->remoteScopeId; + } + + pCompletionData->pSendParams->remoteAddress = pRemoteAddress; + } + + pCompletionData->pSendParams->controlData = (WSACMSGHDR*)pCompletionData->pInjectionData->pControlData; + pCompletionData->pSendParams->controlDataLength = pCompletionData->pInjectionData->controlDataLength; + + pCompletionData->refCount = KrnlHlprNBLGetRequiredRefCount(pNetBufferList); + + status = FwpsInjectTransportSendAsync(pCompletionData->pInjectionData->injectionHandle, + pCompletionData->pInjectionData->injectionContext, + endpointHandle, + 0, + pCompletionData->pSendParams, + pCompletionData->pInjectionData->addressFamily, + compartmentID, + pNetBufferList, + CompletePendAuthorization, + pCompletionData); + } + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS) + { + if(pNetBufferList) + { + FwpsFreeCloneNetBufferList(pNetBufferList, + 0); + + pNetBufferList = 0; + } + + if(pCompletionData) + PendAuthorizationCompletionDataDestroy(&pCompletionData, + TRUE); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PrvCloneAuthorizedNBLAndInject() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +#pragma warning(push) +#pragma warning(disable: 6101) /// *ppClassifyData and *ppPendData are freed and set to 0 on successful completion + +/** + @private_function="PerformPendAuthorization" + + Purpose:
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS PerformPendAuthorization(_Inout_ CLASSIFY_DATA** ppClassifyData, + _Inout_ PEND_DATA** ppPendData, + _Inout_ INJECTION_DATA** ppInjectionData) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PerformPendAuthorization()\n"); + +#endif /// DBG + + NT_ASSERT(ppClassifyData); + NT_ASSERT(ppPendData); + NT_ASSERT(ppInjectionData); + NT_ASSERT(*ppClassifyData); + NT_ASSERT(*ppPendData); + + NTSTATUS status = STATUS_SUCCESS; + NET_BUFFER_LIST* pNBL = (*ppPendData)->pNBL; + UINT32 finalAction = (*ppPendData)->pPendAuthorizationData->finalAction; + UINT32 delay = (*ppPendData)->pPendAuthorizationData->delay; + BOOLEAN reInjected = FALSE; + + if(delay && + KeGetCurrentIrql() < DISPATCH_LEVEL) + { + +#pragma warning(push) +#pragma warning(disable: 28118) /// IRQL check has already been performed + + KrnlHlprWorkItemSleep(delay); + +#pragma warning(pop) + + } + +#pragma warning(push) +#pragma warning(disable: 6001) /// *ppPendData initialized prior to call to this function + + /// Completes the Pend + KrnlHlprPendDataDestroy(ppPendData); + +#pragma warning(pop) + + if(pNBL && + finalAction == FWP_ACTION_PERMIT) + { + status = PrvCloneAuthorizedNBLAndInject(ppClassifyData, + ppInjectionData); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformPendAuthorization : PrvCloneAuthorizedNBLAndInject() [status: %#x]\n", + status); + } + else + reInjected = TRUE; + } + +#pragma warning(push) +#pragma warning(disable: 6001) /// *ppClassifyData initialized prior to call to this function + + if(!reInjected) + KrnlHlprClassifyDataDestroyLocalCopy(ppClassifyData); + +#pragma warning(pop) + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PerformPendAuthorization() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +}; + +#pragma warning(pop) + +/** + @private_function="PendAuthorizationDeferredProcedureCall" + + Purpose: Invokes the appropriate private injection routine to perform the injection at + DISPATCH_LEVEL.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF542972.aspx
+*/ +_IRQL_requires_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Function_class_(KDEFERRED_ROUTINE) +VOID PendAuthorizationDeferredProcedureCall(_In_ KDPC* pDPC, + _In_opt_ PVOID pContext, + _In_opt_ PVOID pArg1, + _In_opt_ PVOID pArg2) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PendAuthorizationDeferredProcedureCall()\n"); + +#endif /// DBG + + UNREFERENCED_PARAMETER(pDPC); + UNREFERENCED_PARAMETER(pContext); + UNREFERENCED_PARAMETER(pArg2); + + NT_ASSERT(pDPC); + NT_ASSERT(pArg1); + NT_ASSERT(((DPC_DATA*)pArg1)->pClassifyData); + NT_ASSERT(((DPC_DATA*)pArg1)->pInjectionData); + + DPC_DATA* pDPCData = (DPC_DATA*)pArg1; + + if(pDPCData) + { + NTSTATUS status = STATUS_SUCCESS; + + status = PerformPendAuthorization(&(pDPCData->pClassifyData), + &(pDPCData->pPendData), + (INJECTION_DATA**)&(pDPCData->pContext)); + if(status != STATUS_SUCCESS) + { + if(pDPCData->pClassifyData) + KrnlHlprClassifyDataDestroyLocalCopy(&(pDPCData->pClassifyData)); + + if(pDPCData->pInjectionData) + KrnlHlprInjectionDataDestroy(&(pDPCData->pInjectionData)); + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PendAuthorizationDeferredProcedureCall() [status: %#x]\n", + status); + } + + KrnlHlprDPCDataDestroy(&pDPCData); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PendAuthorizationDeferredProcedureCall()\n"); + +#endif /// DBG + + return; +} + +/** + @private_function="PendAuthorizationWorkItemRoutine" + + Purpose:
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_IRQL_requires_(PASSIVE_LEVEL) +_IRQL_requires_same_ +_Function_class_(IO_WORKITEM_ROUTINE) +VOID PendAuthorizationWorkItemRoutine(_In_ PDEVICE_OBJECT pDeviceObject, + _In_opt_ PVOID pContext) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PendAuthorizationWorkItemRoutine()\n"); + +#endif /// DBG + + UNREFERENCED_PARAMETER(pDeviceObject); + + NT_ASSERT(pContext); + NT_ASSERT(((WORKITEM_DATA*)pContext)->pClassifyData); + NT_ASSERT(((WORKITEM_DATA*)pContext)->pInjectionData); + + WORKITEM_DATA* pWorkItemData = (WORKITEM_DATA*)pContext; + + if(pWorkItemData) + { + NTSTATUS status = STATUS_SUCCESS; + + status = PerformPendAuthorization(&(pWorkItemData->pClassifyData), + &(pWorkItemData->pPendData), + (INJECTION_DATA**)&(pWorkItemData->pContext)); + if(status != STATUS_SUCCESS) + { + if(pWorkItemData->pClassifyData) + KrnlHlprClassifyDataDestroyLocalCopy(&(pWorkItemData->pClassifyData)); + + if(pWorkItemData->pInjectionData) + KrnlHlprInjectionDataDestroy(&(pWorkItemData->pInjectionData)); + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PendAuthorizationWorkItemRoutine() [status: %#x]\n", + status); + } + + KrnlHlprWorkItemDataDestroy(&pWorkItemData); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PendAuthorizationWorkItemRoutine()\n"); + +#endif /// DBG + + return; +} + +/** + @private_function="TriggerPendAuthorizationOutOfBand" + + Purpose:
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS TriggerPendAuthorizationOutOfBand(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pLayerData, + _In_opt_ const VOID* pClassifyContext, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _In_ FWPS_CLASSIFY_OUT* pClassifyOut, + _Inout_opt_ INJECTION_DATA* pInjectionData, + _Inout_ PEND_DATA* pPendData, + _In_ PC_PEND_AUTHORIZATION_DATA* pPCData) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> TriggerPendAuthorizationOutOfBand()\n"); + +#endif /// DBG + + NT_ASSERT(pClassifyValues); + NT_ASSERT(pMetadata); + NT_ASSERT(pFilter); + NT_ASSERT(pClassifyOut); + NT_ASSERT(pPendData); + NT_ASSERT(pPCData); + + NTSTATUS status = STATUS_SUCCESS; + CLASSIFY_DATA* pClassifyData = 0; + +#pragma warning(push) +#pragma warning(disable: 6014) /// pInjectionData will be freed in completionFn using PendAuthorizationCompletionDataDestroy + + status = KrnlHlprClassifyDataCreateLocalCopy(&pClassifyData, + pClassifyValues, + pMetadata, + pLayerData, + pClassifyContext, + pFilter, + flowContext, + pClassifyOut); + HLPR_BAIL_ON_FAILURE(status); + +#pragma warning(pop) + + if(pPCData->useWorkItems || + pPCData->delay) + { + /// introducing the delay requires PASSIVE_LEVEL, so force use of a Work Item + status = KrnlHlprWorkItemQueue(g_pWDMDevice, + PendAuthorizationWorkItemRoutine, + pClassifyData, + pPendData, + (VOID*)pInjectionData); + } + else + { + if(pPCData->useThreadedDPC) + status = KrnlHlprThreadedDPCQueue(PendAuthorizationDeferredProcedureCall, + pClassifyData, + pPendData, + pInjectionData); + else + status = KrnlHlprDPCQueue(PendAuthorizationDeferredProcedureCall, + pClassifyData, + pPendData, + pInjectionData); + } + + HLPR_BAIL_ON_FAILURE(status); + + pClassifyOut->actionType = FWP_ACTION_BLOCK; + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS) + { + if(pClassifyData) + KrnlHlprClassifyDataDestroyLocalCopy(&pClassifyData); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- TriggerPendAuthorizationOutOfBand() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +#if(NTDDI_VERSION >= NTDDI_WIN7) + +/** + @classify_function="ClassifyPendAuthorization" + + Purpose: Classify Function which will pend an authorization request, and send the request + for further processing to a worker thread. On a reauthorization the final decided + action is returned as the actionType.
+
+ Notes: Applies to the following layers:
+ FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V{4/6}
+ FWPM_LAYER_ALE_AUTH_LISTEN_V{4/6}
+ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V{4/6}
+ FWPM_LAYER_ALE_AUTH_CONNECT_V{4/6}
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551229.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF544893.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI ClassifyPendAuthorization(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pNetBufferList, + _In_opt_ const VOID* pClassifyContext, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut) +{ + NT_ASSERT(pClassifyValues); + NT_ASSERT(pMetadata); + NT_ASSERT(pFilter); + NT_ASSERT(pClassifyOut); + NT_ASSERT(pClassifyValues->layerId == FWPS_LAYER_ALE_RESOURCE_ASSIGNMENT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_RESOURCE_ASSIGNMENT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_LISTEN_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_LISTEN_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V6); + NT_ASSERT(pFilter->providerContext); + NT_ASSERT(pFilter->providerContext->type == FWPM_GENERAL_CONTEXT); + NT_ASSERT(pFilter->providerContext->dataBuffer); + NT_ASSERT(pFilter->providerContext->dataBuffer->size == sizeof(PC_PEND_AUTHORIZATION_DATA)); + NT_ASSERT(pFilter->providerContext->dataBuffer->data); + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> ClassifyPendAuthorization() [Layer: %s][FilterID: %#I64x][Rights: %#x][Reauth: %s]", + KrnlHlprFwpsLayerIDToString(pClassifyValues->layerId), + pFilter->filterId, + pClassifyOut->rights, + KrnlHlprFwpsIncomingValueConditionFlagsAreSet(pClassifyValues, + FWP_CONDITION_FLAG_IS_REAUTHORIZE) ? "TRUE" : "FALSE"); + + PC_PEND_AUTHORIZATION_DATA* pPendAuthorizationData = (PC_PEND_AUTHORIZATION_DATA*)pFilter->providerContext->dataBuffer->data; + BOOLEAN isReauth = KrnlHlprFwpsIncomingValueConditionFlagsAreSet(pClassifyValues, + FWP_CONDITION_FLAG_IS_REAUTHORIZE); + + /// RESOURCE_ASSIGNTMENT & AUTH_LISTEN will Reauthorize after the completion of the pend. + /// AUTH_CONNECT's completeOperation will trigger a Reauthorization + if(isReauth) + { + if(pClassifyOut->rights & FWPS_RIGHT_ACTION_WRITE) + pClassifyOut->actionType = pPendAuthorizationData->finalAction; + } + else + { + BOOLEAN actionSet = FALSE; + + if(pClassifyOut->rights & FWPS_RIGHT_ACTION_WRITE) + { + NTSTATUS status = STATUS_SUCCESS; + INJECTION_DATA* pInjectionData = 0; + PEND_DATA* pPendData = 0; + +#pragma warning(push) +#pragma warning(disable: 6014) /// pInjectionData will be freed in completionFn using PendAuthorizationCompletionDataDestroy + + if(pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V6) + { + status = KrnlHlprInjectionDataCreate(&pInjectionData, + pClassifyValues, + pMetadata, + (NET_BUFFER_LIST*)pNetBufferList, + pFilter); + HLPR_BAIL_ON_FAILURE(status); + } + +#pragma warning(pop) + + /// AUTH_RECV_ACCEPT's injection will be indicative of PERMIT + if(pInjectionData && + pInjectionData->injectionState == FWPS_PACKET_INJECTED_BY_SELF) + { + isReauth = TRUE; + + pClassifyOut->actionType = pPendAuthorizationData->finalAction; + + actionSet = TRUE; + } + else + { +#pragma warning(push) +#pragma warning(disable: 6014) /// pPendData will be freed in completionFn using PendAuthorizationCompletionDataDestroy + + status = KrnlHlprPendDataCreate(&pPendData, + pClassifyValues, + pMetadata, + (NET_BUFFER_LIST*)pNetBufferList, + pFilter); + HLPR_BAIL_ON_FAILURE(status); + +#pragma warning(pop) + + status = TriggerPendAuthorizationOutOfBand(pClassifyValues, + pMetadata, + pNetBufferList, + pClassifyContext, + pFilter, + flowContext, + pClassifyOut, + pInjectionData, + pPendData, + pPendAuthorizationData); + } + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! ClassifyPendAuthorization() [status: %#x]\n", + status); + + if(pInjectionData) + KrnlHlprInjectionDataDestroy(&pInjectionData); + + if(pPendData) + KrnlHlprPendDataDestroy(&pPendData); + } + + if(!actionSet) + { + pClassifyOut->actionType = FWP_ACTION_BLOCK; + pClassifyOut->flags |= FWPS_CLASSIFY_OUT_FLAG_ABSORB; + } + } + } + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- ClassifyPendAuthorization() [Layer: %s][FilterID: %#I64x][Action: %#x][Rights: %#x][Absorb: %s][ReAuth: %s]\n", + KrnlHlprFwpsLayerIDToString(pClassifyValues->layerId), + pFilter->filterId, + pClassifyOut->actionType, + pClassifyOut->rights, + (pClassifyOut->flags & FWPS_CLASSIFY_OUT_FLAG_ABSORB) ? "TRUE" : "FALSE", + isReauth ? "TRUE" : "FALSE"); + + return; +} + +#else + +/** + @classify_function="ClassifyPendAuthorization" + + Purpose: Classify Function which will pend an authorization request, and send the request + for further processing to a worker thread. On a reauthorization the final decided + action is returned as the actionType.
+
+ Notes: Applies to the following layers:
+ FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V{4/6}
+ FWPM_LAYER_ALE_AUTH_LISTEN_V{4/6}
+ FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V{4/6}
+ FWPM_LAYER_ALE_AUTH_CONNECT_V{4/6}
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551229.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF544890.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI ClassifyPendAuthorization(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pNetBufferList, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut) +{ + NT_ASSERT(pClassifyValues); + NT_ASSERT(pMetadata); + NT_ASSERT(pFilter); + NT_ASSERT(pClassifyOut); + NT_ASSERT(pClassifyValues->layerId == FWPS_LAYER_ALE_RESOURCE_ASSIGNMENT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_RESOURCE_ASSIGNMENT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_LISTEN_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_LISTEN_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V6); + NT_ASSERT(pFilter->providerContext); + NT_ASSERT(pFilter->providerContext->type == FWPM_GENERAL_CONTEXT); + NT_ASSERT(pFilter->providerContext->dataBuffer); + NT_ASSERT(pFilter->providerContext->dataBuffer->size == sizeof(PC_PEND_AUTHORIZATION_DATA)); + NT_ASSERT(pFilter->providerContext->dataBuffer->data); + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> ClassifyPendAuthorization() [Layer: %s][FilterID: %#I64x][Rights: %#x][ReAuth: %s]", + KrnlHlprFwpsLayerIDToString(pClassifyValues->layerId), + pFilter->filterId, + pClassifyOut->rights, + KrnlHlprFwpsIncomingValueConditionFlagsAreSet(pClassifyValues, + FWP_CONDITION_FLAG_IS_REAUTHORIZE) ? "TRUE" : "FALSE"); + + PC_PEND_AUTHORIZATION_DATA* pPendAuthorizationData = (PC_PEND_AUTHORIZATION_DATA*)pFilter->providerContext->dataBuffer->data; + BOOLEAN isReauth = KrnlHlprFwpsIncomingValueConditionFlagsAreSet(pClassifyValues, + FWP_CONDITION_FLAG_IS_REAUTHORIZE); + + /// RESOURCE_ASSIGNTMENT & AUTH_LISTEN will Reauthorize after the completion of the pend. + /// AUTH_CONNECT's completeOperation will trigger a Reauthorization + if(isReauth) + { + if(pClassifyOut->rights & FWPS_RIGHT_ACTION_WRITE) + pClassifyOut->actionType = pPendAuthorizationData->finalAction; + } + else + { + BOOLEAN actionSet = FALSE; + + if(pClassifyOut->rights & FWPS_RIGHT_ACTION_WRITE) + { + NTSTATUS status = STATUS_SUCCESS; + INJECTION_DATA* pInjectionData = 0; + PEND_DATA* pPendData = 0; + +#pragma warning(push) +#pragma warning(disable: 6014) /// pInjectionData will be freed in completionFn using PendAuthorizationCompletionDataDestroy + + if(pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V6) + { + status = KrnlHlprInjectionDataCreate(&pInjectionData, + pClassifyValues, + pMetadata, + (NET_BUFFER_LIST*)pNetBufferList, + pFilter); + HLPR_BAIL_ON_FAILURE(status); + } + +#pragma warning(pop) + + /// AUTH_RECV_ACCEPT's injection will be indicative of ReAuth + if(pInjectionData && + pInjectionData->injectionState == FWPS_PACKET_INJECTED_BY_SELF) + { + isReauth = TRUE; + + pClassifyOut->actionType = pPendAuthorizationData->finalAction; + + actionSet = TRUE; + } + else + { +#pragma warning(push) +#pragma warning(disable: 6014) /// pInjectionData will be freed in completionFn using PendAuthorizationCompletionDataDestroy + + status = KrnlHlprPendDataCreate(&pPendData, + pClassifyValues, + pMetadata, + (NET_BUFFER_LIST*)pNetBufferList, + pFilter); + HLPR_BAIL_ON_FAILURE(status); + +#pragma warning(pop) + + status = TriggerPendAuthorizationOutOfBand(pClassifyValues, + pMetadata, + pNetBufferList, + 0, + pFilter, + flowContext, + pClassifyOut, + pInjectionData, + pPendData, + pPendAuthorizationData); + } + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! ClassifyPendAuthorization() [status: %#x]\n", + status); + + if(pInjectionData) + KrnlHlprInjectionDataDestroy(&pInjectionData); + + if(pPendData) + KrnlHlprPendDataDestroy(&pPendData); + } + + if(!actionSet) + { + pClassifyOut->actionType = FWP_ACTION_BLOCK; + pClassifyOut->flags |= FWPS_CLASSIFY_OUT_FLAG_ABSORB; + } + } + } + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- ClassifyPendAuthorization() [Layer: %s][FilterID: %#I64x][Action: %#x][Rights: %#x][Absorb: %s][ReAuth: %s]\n", + KrnlHlprFwpsLayerIDToString(pClassifyValues->layerId), + pFilter->filterId, + pClassifyOut->actionType, + pClassifyOut->rights, + (pClassifyOut->flags & FWPS_CLASSIFY_OUT_FLAG_ABSORB) ? "TRUE" : "FALSE", + isReauth ? "TRUE" : "FALSE"); + + return; +} + +#endif // (NTDDI_VERSION >= NTDDI_WIN7) diff --git a/network/trans/WFPSampler/sys/ClassifyFunctions_PendAuthorizationCallouts.h b/network/trans/WFPSampler/sys/ClassifyFunctions_PendAuthorizationCallouts.h new file mode 100644 index 000000000..05f0f2c3c --- /dev/null +++ b/network/trans/WFPSampler/sys/ClassifyFunctions_PendAuthorizationCallouts.h @@ -0,0 +1,61 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// ClassifyFunctions_PendAuthorizationCallouts.h +// +// Abstract: +// This module contains prototypes of WFP Classify functions for pending and completing +// authorizations +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Enhance function declaration for IntelliSense +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef CLASSIFY_PEND_AUTHORIZATION_H +#define CLASSIFY_PEND_AUTHORIZATION_H + +_IRQL_requires_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Function_class_(KDEFERRED_ROUTINE) +VOID PendAuthorizationDeferredProcedureCall(_In_ KDPC* pDPC, + _In_opt_ PVOID pContext, + _In_opt_ PVOID pArg1, + _In_opt_ PVOID pArg2); + +#if(NTDDI_VERSION >= NTDDI_WIN7) + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI ClassifyPendAuthorization(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pLayerData, + _In_opt_ const VOID* pClassifyContext, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut); + +#else + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI ClassifyPendAuthorization(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pLayerData, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut); + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + +#endif /// CLASSIFY_PEND_AUTHORIZATION_H diff --git a/network/trans/WFPSampler/sys/ClassifyFunctions_PendEndpointClosureCallouts.cpp b/network/trans/WFPSampler/sys/ClassifyFunctions_PendEndpointClosureCallouts.cpp new file mode 100644 index 000000000..315dde6c1 --- /dev/null +++ b/network/trans/WFPSampler/sys/ClassifyFunctions_PendEndpointClosureCallouts.cpp @@ -0,0 +1,410 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// ClassifyFunctions_PendEndpointClosureCallouts.cpp +// +// Abstract: +// This module contains WFP Classify functions for pending and completing endpoint closures. +// +// Naming Convention: +// +// +// +// i.e. +// +// ClassifyPendEndpointClosure +// +// +// Classify - Function is an FWPS_CALLOUT_CLASSIFY_FN. +// Perform - Function executes the desired scenario. +// Trigger - Function queues a worker thread for later execution of +// the the scenario. +// +// PendEndpointClosure - Function demonstates pending endpoint closure requests. +// +// Author: +// Dusty Harper (DHarper) +// +// Private Functions: +// PendEndpointClosureWorkItemRoutine(), +// PerformPendEndpointClosure(), +// TriggerPendEndpointClosureOutOfBand(), +// +// Public Functions: +// PendEndpointClosureDeferredProcedureCall(), +// ClassifyPendEndpointClosure(), +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// December 13, 2013 - 1.1 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "Framework_WFPSamplerCalloutDriver.h" /// . +#include "ClassifyFunctions_PendEndpointClosureCallouts.tmh" /// $(OBJ_PATH)\$(O)\ + +#if(NTDDI_VERSION >= NTDDI_WIN7) + +#pragma warning(push) +#pragma warning(disable: 6101) /// *ppPendData are freed and set to 0 on successful completion + +/** + @private_function="PerformPendEndpointClosure" + + Purpose: Waits for the specified delay, then completes the pended classify, and frees the + memory.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS PerformPendEndpointClosure(_Inout_ PEND_DATA** ppPendData) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PerformPendEndpointClosure()\n"); + +#endif /// DBG + + NT_ASSERT(*ppPendData); + NT_ASSERT((*ppPendData)->pPendEndpointClosureData); + + NTSTATUS status = STATUS_SUCCESS; + UINT32 delay = (*ppPendData)->pPendEndpointClosureData->delay; + + if(delay && + KeGetCurrentIrql() < DISPATCH_LEVEL) + { + +#pragma warning(push) +#pragma warning(disable: 28118) /// IRQL check has already been performed + + KrnlHlprWorkItemSleep(delay); + +#pragma warning(pop) + + } + +#pragma warning(push) +#pragma warning(disable: 6001) /// *ppPendData initialized prior to call to this function + + /// Completes the Pend + KrnlHlprPendDataDestroy(ppPendData); + +#pragma warning(pop) + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PerformPendEndpointClosure() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +}; + +#pragma warning(pop) + +/** + @private_function="PendEndpointClosureDeferredProcedureCall" + + Purpose: Invokes the appropriate private routine to complete the pended classify.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF542972.aspx
+*/ +_IRQL_requires_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Function_class_(KDEFERRED_ROUTINE) +VOID PendEndpointClosureDeferredProcedureCall(_In_ KDPC* pDPC, + _In_opt_ PVOID pContext, + _In_opt_ PVOID pArg1, + _In_opt_ PVOID pArg2) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PendEndpointClosureDeferredProcedureCall()\n"); + +#endif /// DBG + + UNREFERENCED_PARAMETER(pDPC); + UNREFERENCED_PARAMETER(pContext); + UNREFERENCED_PARAMETER(pArg2); + + NT_ASSERT(pDPC); + NT_ASSERT(pArg1); + NT_ASSERT(((DPC_DATA*)pArg1)->pPendData); + + DPC_DATA* pDPCData = (DPC_DATA*)pArg1; + + if(pDPCData) + { + NTSTATUS status = STATUS_SUCCESS; + + status = PerformPendEndpointClosure(&(pDPCData->pPendData)); + if(status != STATUS_SUCCESS) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PendEndpointClosureDeferredProcedureCall() [status: %#x]\n", + status); + + KrnlHlprDPCDataDestroy(&pDPCData); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PendEndpointClosureDeferredProcedureCall()\n"); + +#endif /// DBG + + return; +} + +/** + @private_function="PendEndpointClosureWorkItemRoutine" + + Purpose: Invokes the appropriate private routine to complete the pended classify at + PASSIVE_LEVEL.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF566380.aspx
+*/ +_IRQL_requires_(PASSIVE_LEVEL) +_IRQL_requires_same_ +_Function_class_(IO_WORKITEM_ROUTINE) +VOID PendEndpointClosureWorkItemRoutine(_In_ PDEVICE_OBJECT pDeviceObject, + _In_opt_ PVOID pContext) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PendEndpointClosureWorkItemRoutine()\n"); + +#endif /// DBG + + UNREFERENCED_PARAMETER(pDeviceObject); + + NT_ASSERT(pContext); + NT_ASSERT(((WORKITEM_DATA*)pContext)->pPendData); + + WORKITEM_DATA* pWorkItemData = (WORKITEM_DATA*)pContext; + + if(pWorkItemData) + { + NTSTATUS status = STATUS_SUCCESS; + + status = PerformPendEndpointClosure(&(pWorkItemData->pPendData)); + if(status != STATUS_SUCCESS) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PendEndpointClosureWorkItemRoutine() [status: %#x]\n", + status); + + KrnlHlprWorkItemDataDestroy(&pWorkItemData); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PendEndpointClosureWorkItemRoutine()\n"); + +#endif /// DBG + + return; +} + +/** + @private_function="TriggerPendEndpointClosure" + + Purpose:
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS TriggerPendEndpointClosure(_Inout_ PEND_DATA* pPendData, + _In_ PC_PEND_ENDPOINT_CLOSURE_DATA* pPCData) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> TriggerPendEndpointClosure()\n"); + +#endif /// DBG + + NT_ASSERT(pPendData); + NT_ASSERT(pPCData); + + NTSTATUS status = STATUS_SUCCESS; + + if(pPCData->useWorkItems || + pPCData->delay) + { + /// introducing the delay requires PASSIVE_LEVEL, so force use of a Work Item + status = KrnlHlprWorkItemQueue(g_pWDMDevice, + PendEndpointClosureWorkItemRoutine, + pPendData); + } + else + { + if(pPCData->useThreadedDPC) + status = KrnlHlprThreadedDPCQueue(PendEndpointClosureDeferredProcedureCall, + pPendData); + else + status = KrnlHlprDPCQueue(PendEndpointClosureDeferredProcedureCall, + pPendData); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- TriggerPendEndpointClosure() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @classify_function="ClassifyPendEndpointClosure" + + Purpose: Classify Function which will pend an endpoint closure request. If there is + appropriate flowContext, te request will be pended, and then exit, requiring a call + to the FlowDeleteFn to perform the pend completion. Otherwise a worker thread is + queued, and a will wait for the specified milliseconds before the pend is + completed.
+
+ Notes: Applies to the following layers:
+ FWPM_LAYER_ALE_ENDPOINT_CLOSURE_V{4/6}
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551229.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF544893.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI ClassifyPendEndpointClosure(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pLayerData, + _In_opt_ const VOID* pClassifyContext, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut) +{ + NT_ASSERT(pClassifyValues); + NT_ASSERT(pMetadata); + NT_ASSERT(pFilter); + NT_ASSERT(pClassifyOut); + NT_ASSERT(pClassifyValues->layerId == FWPS_LAYER_ALE_ENDPOINT_CLOSURE_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_ENDPOINT_CLOSURE_V6); + NT_ASSERT(pFilter->providerContext); + NT_ASSERT(pFilter->providerContext->type == FWPM_GENERAL_CONTEXT); + NT_ASSERT(pFilter->providerContext->dataBuffer); + NT_ASSERT(pFilter->providerContext->dataBuffer->size == sizeof(PC_PEND_ENDPOINT_CLOSURE_DATA)); + NT_ASSERT(pFilter->providerContext->dataBuffer->data); + + UNREFERENCED_PARAMETER(pLayerData); + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> ClassifyPendEndpointClosure() [Layer: %s][FilterID: %#I64x][FlowContext: %#I64x][Rights: %#x]", + KrnlHlprFwpsLayerIDToString(pClassifyValues->layerId), + pFilter->filterId, + flowContext, + pClassifyOut->rights); + + NTSTATUS status = STATUS_SUCCESS; + PEND_DATA* pPendData = 0; + +#pragma warning(push) +#pragma warning(disable: 6014) /// pPendData will be freed in PerformPendEndpointClosure + + status = KrnlHlprPendDataCreate(&pPendData, + pClassifyValues, + pMetadata, + 0, + pFilter, + (VOID*)pClassifyContext, + pClassifyOut); + HLPR_BAIL_ON_FAILURE(status); + +#pragma warning(pop) + + if(flowContext) + { + NT_ASSERT(flowContext); + NT_ASSERT(((FLOW_CONTEXT*)flowContext)->contextType == CONTEXT_TYPE_ALE_ENDPOINT_CLOSURE); + NT_ASSERT(((FLOW_CONTEXT*)flowContext)->pALEEndpointClosureContext); + + FLOW_CONTEXT* pFlowContext = (FLOW_CONTEXT*)flowContext; + KIRQL irql = PASSIVE_LEVEL; + + KeAcquireSpinLock(&(pFlowContext->pALEEndpointClosureContext->spinLock), + &irql); + + /// NotifyFlowDeleteNotification will free this memory + pFlowContext->pALEEndpointClosureContext->pPendData = pPendData; + pFlowContext->pALEEndpointClosureContext->filterID = pFilter->filterId; + + KeReleaseSpinLock(&(pFlowContext->pALEEndpointClosureContext->spinLock), + irql); + } + else + { + PC_PEND_ENDPOINT_CLOSURE_DATA* pPendEndpointClosureData = (PC_PEND_ENDPOINT_CLOSURE_DATA*)pFilter->providerContext->dataBuffer->data; + + status = TriggerPendEndpointClosure(pPendData, + pPendEndpointClosureData); + } + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! ClassifyPendEndpointClosure() [status: %#x]\n", + status); + + KrnlHlprPendDataDestroy(&pPendData); + } + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- ClassifyPendEndpointClosure() [Layer: %s][FilterID: %#I64x][Action: %#x][Rights: %#x][Absorb: %s]\n", + KrnlHlprFwpsLayerIDToString(pClassifyValues->layerId), + pFilter->filterId, + pClassifyOut->actionType, + pClassifyOut->rights, + (pClassifyOut->flags & FWPS_CLASSIFY_OUT_FLAG_ABSORB) ? "TRUE" : "FALSE"); + + return; +} + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) diff --git a/network/trans/WFPSampler/sys/ClassifyFunctions_PendEndpointClosureCallouts.h b/network/trans/WFPSampler/sys/ClassifyFunctions_PendEndpointClosureCallouts.h new file mode 100644 index 000000000..931b3e65a --- /dev/null +++ b/network/trans/WFPSampler/sys/ClassifyFunctions_PendEndpointClosureCallouts.h @@ -0,0 +1,48 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// ClassifyFunctions_PendEndpointClosureCallouts.h +// +// Abstract: +// This module contains prototypes of WFP Classify functions for pending and completing +// endpoint closures +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// December 13, 2013 - 1.1 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef CLASSIFY_PEND_ENDPOINT_CLOSURE_H +#define CLASSIFY_PEND_ENDPOINT_CLOSURE_H + +#if(NTDDI_VERSION >= NTDDI_WIN7) + +_IRQL_requires_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Function_class_(KDEFERRED_ROUTINE) +VOID PendEndpointClosureDeferredProcedureCall(_In_ KDPC* pDPC, + _In_opt_ PVOID pContext, + _In_opt_ PVOID pArg1, + _In_opt_ PVOID pArg2); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI ClassifyPendEndpointClosure(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pLayerData, + _In_opt_ const VOID* pClassifyContext, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut); + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + +#endif /// CLASSIFY_PEND_ENDPOINT_CLOSURE_H diff --git a/network/trans/WFPSampler/sys/ClassifyFunctions_ProxyCallouts.cpp b/network/trans/WFPSampler/sys/ClassifyFunctions_ProxyCallouts.cpp new file mode 100644 index 000000000..d6b2b4d4d --- /dev/null +++ b/network/trans/WFPSampler/sys/ClassifyFunctions_ProxyCallouts.cpp @@ -0,0 +1,2424 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// ClassifyFunctions_ProxyCallouts.cpp +// +// Abstract: +// This module contains WFP Classify functions for proxying connections and sockets. +// +// Naming Convention: +// +// +// +// i.e. +// +// ClassifyProxyByALERedirect +// +// +// Classify - Function is an FWPS_CALLOUT_CLASSIFY_FN +// Perform - Function executes the desired scenario. +// Prv - Function is a private helper to this module. +// Trigger - +// +// ProxyByALERedirect - Function demonstates use of +// FWPM_LAYER_ALE_CONNECT_REDIRECT_V{4/6} and +// FWPM_LAYER_ALE_BIND_REDIRECT_V{4/6} for proxying. +// (For use in Win7+) +// +// Private Functions: +// PerformProxyInjectionAtInboundNetwork(), +// PerformProxyInjectionAtOutboundTransport(), +// PerformProxySocketRedirection(), +// PerformProxyConnectRedirection(), +// ProxyByALERedirectWorkItemRoutine(), +// ProxyUsingInjectionMethodWorkItemRoutine(), +// TriggerProxyByALERedirectInline(), +// TriggerProxyByALERedirectOutOfBand(), +// TriggerProxyInjectionInline(), +// TriggerProxyInjectionOutOfBand(), +// +// Public Functions: +// ClassifyProxyByALERedirect(), +// ClassifyProxyByInjection(), +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Enhance function declaration for IntelliSense, fix +// proxying by injection to use INBOUND_IPPACKET, fix +// proxying by ALE to a local service, and add support +// for multiple redirectors. +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "Framework_WFPSamplerCalloutDriver.h" /// . +#include "ClassifyFunctions_ProxyCallouts.tmh" /// $(OBJ_PATH)\$(O)\ + +/** + @private_function="PerformProxyInjectionAtInboundNetwork" + + Purpose:
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS PerformProxyInjectionAtInboundNetwork(_In_ CLASSIFY_DATA** ppClassifyData, + _In_ INJECTION_DATA** ppInjectionData, + _In_ PC_PROXY_DATA* pProxyData, + _In_ BOOLEAN isInline = FALSE) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PerformProxyInjectionAtInboundNetwork()\n"); + +#endif /// DBG + + NT_ASSERT(ppClassifyData); + NT_ASSERT(ppInjectionData); + NT_ASSERT(pProxyData); + NT_ASSERT(*ppClassifyData); + NT_ASSERT(*ppInjectionData); + + NTSTATUS status = STATUS_SUCCESS; + FWPS_INCOMING_VALUES* pClassifyValues = (FWPS_INCOMING_VALUES*)(*ppClassifyData)->pClassifyValues; + FWPS_INCOMING_METADATA_VALUES* pMetadata = (FWPS_INCOMING_METADATA_VALUES*)(*ppClassifyData)->pMetadataValues; + COMPARTMENT_ID compartmentID = DEFAULT_COMPARTMENT_ID; + IF_INDEX interfaceIndex = 0; + IF_INDEX subInterfaceIndex = 0; + UINT32 flags = 0; + NET_BUFFER_LIST* pNetBufferList = 0; + PROXY_COMPLETION_DATA* pCompletionData = 0; + UINT32 ipHeaderSize = 0; + UINT32 bytesRetreated = 0; + UINT64 endpointHandle = 0; + IPPROTO protocol = IPPROTO_MAX; + FWP_VALUE* pInterfaceIndex = 0; + FWP_VALUE* pSubInterfaceIndex = 0; + FWP_VALUE* pFlags = 0; + BYTE* pSourceAddress = 0; + BYTE* pDestinationAddress = 0; + NDIS_TCP_IP_CHECKSUM_PACKET_INFO checksumInfo = {0}; + +#if DBG + + KIRQL irql = KeGetCurrentIrql(); + +#endif /// DBG + +#pragma warning(push) +#pragma warning(disable: 6014) /// pCompletionData will be freed in completionFn using BasicPacketModificationCompletionDataDestroy + + HLPR_NEW(pCompletionData, + PROXY_COMPLETION_DATA, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pCompletionData, + status); + +#pragma warning(pop) + + pCompletionData->performedInline = isInline; + pCompletionData->pClassifyData = *ppClassifyData; + pCompletionData->pInjectionData = *ppInjectionData; + + /// Responsibility for freeing this memory has been transferred to the pCompletionData + *ppClassifyData = 0; + + *ppInjectionData = 0; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_COMPARTMENT_ID)) + compartmentID = (COMPARTMENT_ID)pMetadata->compartmentId; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_ENDPOINT_HANDLE)) + endpointHandle = pMetadata->transportEndpointHandle; + + pInterfaceIndex = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_INTERFACE_INDEX); + if(pInterfaceIndex && + pInterfaceIndex->type == FWP_UINT32) + interfaceIndex = (IF_INDEX)pInterfaceIndex->uint32; + + pSubInterfaceIndex = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_SUB_INTERFACE_INDEX); + if(pSubInterfaceIndex && + pSubInterfaceIndex->type == FWP_UINT32) + subInterfaceIndex = (IF_INDEX)pSubInterfaceIndex->uint32; + + pFlags = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_FLAGS); + if(pFlags && + pFlags->type == FWP_UINT32) + flags = pFlags->uint32; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_IP_HEADER_SIZE)) + bytesRetreated = ipHeaderSize = pMetadata->ipHeaderSize; + + checksumInfo.Value = (ULONG)(ULONG_PTR)NET_BUFFER_LIST_INFO((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket, + TcpIpChecksumNetBufferListInfo); + + /// Initial offset is at the Transport Header, so retreat the size of the IP Header ... + status = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket), + bytesRetreated, + 0, + 0); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformProxyInjectionAtInboundNetwork : NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + /// ... clone the entire NET_BUFFER_LIST ... + status = FwpsAllocateCloneNetBufferList((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket, + g_pNDISPoolData->nblPoolHandle, + g_pNDISPoolData->nbPoolHandle, + 0, + &pNetBufferList); + + /// ... and advance the offset back to the original position. + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket), + bytesRetreated, + FALSE, + 0); + + if(status != STATUS_SUCCESS || + !pNetBufferList) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformProxyInjectionAtInboundNetwork : FwpsAllocateCloneNetBufferList() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + /// Handle if this packet had the IP checksum offloaded or if it's loopback + if(checksumInfo.Receive.NdisPacketIpChecksumSucceeded || + flags & FWP_CONDITION_FLAG_IS_LOOPBACK) + { + /// Prevent TCP/IP Zone crossing and recalculate the checksums + if(flags & FWP_CONDITION_FLAG_IS_LOOPBACK) + { + FWP_VALUE* pLocalAddress = 0; + FWP_VALUE* pRemoteAddress = 0; + FWP_VALUE* pLoopbackAddress = 0; + + pLocalAddress = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_REMOTE_ADDRESS); + if(pLocalAddress && + ((pLocalAddress->type == FWP_UINT32 && + RtlCompareMemory(&(pLocalAddress->uint32), + IPV4_LOOPBACK_ADDRESS, + IPV4_ADDRESS_SIZE)) || + (pLocalAddress->type == FWP_BYTE_ARRAY16_TYPE && + RtlCompareMemory(pLocalAddress->byteArray16->byteArray16, + IPV6_LOOPBACK_ADDRESS, + IPV6_ADDRESS_SIZE)))) + pLoopbackAddress = pLocalAddress; + + if(!pLoopbackAddress) + { + pRemoteAddress = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_REMOTE_ADDRESS); + if(pRemoteAddress && + ((pRemoteAddress->type == FWP_UINT32 && + RtlCompareMemory(&(pRemoteAddress->uint32), + IPV4_LOOPBACK_ADDRESS, + IPV4_ADDRESS_SIZE)) || + (pRemoteAddress->type == FWP_BYTE_ARRAY16_TYPE && + RtlCompareMemory(pRemoteAddress->byteArray16->byteArray16, + IPV6_LOOPBACK_ADDRESS, + IPV6_ADDRESS_SIZE)))) + pLoopbackAddress = pRemoteAddress; + } + + if(pLoopbackAddress) + { + status = KrnlHlprIPHeaderModifyLoopbackToLocal(pMetadata, + pLoopbackAddress, + ipHeaderSize, + pNetBufferList, + (const WSACMSGHDR*)pCompletionData->pInjectionData->pControlData, + pCompletionData->pInjectionData->controlDataLength); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformProxyInjectionAtInboundNetwork: KrnlHlprIPHeaderModifyLoopbackToLocal() [status: %#x]\n", + status); + + HLPR_BAIL; + } + } + } + else + { + /// Recalculate the checksum + if(pCompletionData->pInjectionData->addressFamily == AF_INET) + KrnlHlprIPHeaderCalculateV4Checksum(pNetBufferList, + ipHeaderSize); + } + } + + protocol = KrnlHlprIPHeaderGetProtocolField(pNetBufferList, + pCompletionData->pInjectionData->addressFamily); + + if(pProxyData->flags & PCPDF_PROXY_LOCAL_PORT || + pProxyData->flags & PCPDF_PROXY_REMOTE_PORT) + { + NTSTATUS tmpStatus = STATUS_SUCCESS; + + protocol = KrnlHlprIPHeaderGetProtocolField(pNetBufferList, + pCompletionData->pInjectionData->addressFamily); + + /// The clone is at the IP Header, so advance by the size of the IP Header. + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList), + ipHeaderSize, + FALSE, + 0); + + if(pProxyData->flags & PCPDF_PROXY_LOCAL_PORT) + { + FWP_VALUE localPort; + + RtlZeroMemory(&localPort, + sizeof(FWP_VALUE)); + + localPort.type = FWP_UINT16; + localPort.uint16 = pProxyData->originalLocalPort; + + switch(protocol) + { + case TCP: + { + UINT32 tcpHeaderSize = TCP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + tcpHeaderSize = pMetadata->transportHeaderSize; + + status = KrnlHlprTCPHeaderModifyDestinationPort(&localPort, + pNetBufferList, + tcpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + + break; + } + case UDP: + { + UINT32 udpHeaderSize = UDP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + udpHeaderSize = pMetadata->transportHeaderSize; + + status = KrnlHlprUDPHeaderModifyDestinationPort(&localPort, + pNetBufferList, + udpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + + break; + } + } + } + + if(pProxyData->flags & PCPDF_PROXY_REMOTE_PORT) + { + FWP_VALUE remotePort; + + RtlZeroMemory(&remotePort, + sizeof(FWP_VALUE)); + + remotePort.type = FWP_UINT16; + remotePort.uint16 = pProxyData->originalRemotePort; + + switch(protocol) + { + case TCP: + { + UINT32 tcpHeaderSize = TCP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + tcpHeaderSize = pMetadata->transportHeaderSize; + + status = KrnlHlprTCPHeaderModifySourcePort(&remotePort, + pNetBufferList, + tcpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + + break; + } + case UDP: + { + UINT32 udpHeaderSize = UDP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + udpHeaderSize = pMetadata->transportHeaderSize; + + status = KrnlHlprUDPHeaderModifySourcePort(&remotePort, + pNetBufferList, + udpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + + break; + } + } + + } + + HLPR_BAIL_LABEL_2: + + /// return the data offset to the beginning of the IP Header + tmpStatus = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList), + ipHeaderSize, + 0, + 0); + if(tmpStatus != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformProxyInjectionAtInboundNetwork : NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + HLPR_BAIL_ON_FAILURE(status); + } + + if(pProxyData->flags & PCPDF_PROXY_LOCAL_ADDRESS) + { + FWP_VALUE localAddress; + + RtlZeroMemory(&localAddress, + sizeof(FWP_VALUE)); + + if(pCompletionData->pInjectionData->addressFamily == AF_INET) + { + localAddress.type = FWP_UINT32; + + RtlCopyMemory(&(localAddress.uint32), + pProxyData->originalLocalAddress.pIPv4, + IPV4_ADDRESS_SIZE); + } + else + { + HLPR_NEW(localAddress.byteArray16, + FWP_BYTE_ARRAY16, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(localAddress.byteArray16, + status); + + localAddress.type = FWP_BYTE_ARRAY16_TYPE; + + RtlCopyMemory(localAddress.byteArray16->byteArray16, + pProxyData->originalLocalAddress.pIPv6, + IPV6_ADDRESS_SIZE); + } + + status = KrnlHlprIPHeaderModifyDestinationAddress(&localAddress, + pNetBufferList, + TRUE); + + KrnlHlprFwpValuePurgeLocalCopy(&localAddress); + + HLPR_BAIL_ON_FAILURE(status); + } + + if(pProxyData->flags & PCPDF_PROXY_REMOTE_ADDRESS) + { + FWP_VALUE remoteAddress; + + RtlZeroMemory(&remoteAddress, + sizeof(FWP_VALUE)); + + if(pCompletionData->pInjectionData->addressFamily == AF_INET) + { + remoteAddress.type = FWP_UINT32; + + RtlCopyMemory(&(remoteAddress.uint32), + pProxyData->originalRemoteAddress.pIPv4, + IPV4_ADDRESS_SIZE); + } + else + { + HLPR_NEW(remoteAddress.byteArray16, + FWP_BYTE_ARRAY16, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(remoteAddress.byteArray16, + status); + + remoteAddress.type = FWP_BYTE_ARRAY16_TYPE; + + RtlCopyMemory(remoteAddress.byteArray16->byteArray16, + pProxyData->originalRemoteAddress.pIPv6, + IPV6_ADDRESS_SIZE); + } + + status = KrnlHlprIPHeaderModifySourceAddress(&remoteAddress, + pNetBufferList, + TRUE); + + KrnlHlprFwpValuePurgeLocalCopy(&remoteAddress); + + HLPR_BAIL_ON_FAILURE(status); + } + + pSourceAddress = KrnlHlprIPHeaderGetSourceAddressField(pNetBufferList, + pCompletionData->pInjectionData->addressFamily); + + pDestinationAddress = KrnlHlprIPHeaderGetDestinationAddressField(pNetBufferList, + pCompletionData->pInjectionData->addressFamily); + + status = FwpsConstructIpHeaderForTransportPacket(pNetBufferList, + ipHeaderSize, + pCompletionData->pInjectionData->addressFamily, + (UCHAR*)pSourceAddress, + (UCHAR*)pDestinationAddress, + protocol, + endpointHandle, + (const WSACMSGHDR*)pCompletionData->pInjectionData->pControlData, + pCompletionData->pInjectionData->controlDataLength, + 0, + 0, + interfaceIndex, + subInterfaceIndex); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformProxyInjectionAtInboundNetwork : NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + status = FwpsInjectNetworkReceiveAsync(pCompletionData->pInjectionData->injectionHandle, + pCompletionData->pInjectionData->injectionContext, + 0, + compartmentID, + interfaceIndex, + subInterfaceIndex, + pNetBufferList, + CompleteBasicPacketModification, + pCompletionData); + + NT_ASSERT(irql == KeGetCurrentIrql()); + + if(status != STATUS_SUCCESS) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformProxyInjectionAtInboundNetwork : FwpsInjectNetworkReceiveAsync() [status: %#x]\n", + status); + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS) + { + if(pNetBufferList) + { + FwpsFreeCloneNetBufferList(pNetBufferList, + 0); + + pNetBufferList = 0; + } + + if(pCompletionData) + ProxyCompletionDataDestroy(&pCompletionData); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PerformProxyInjectionAtInboundNetwork() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @private_function="PerformProxyInjectionAtOutboundTransport" + + Purpose:
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS PerformProxyInjectionAtOutboundTransport(_In_ CLASSIFY_DATA** ppClassifyData, + _In_ INJECTION_DATA** ppInjectionData, + _In_ PC_PROXY_DATA* pProxyData, + _In_ BOOLEAN isInline = FALSE) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PerformProxyInjectionAtOutboundTransport()\n"); + +#endif /// DBG + + NT_ASSERT(ppClassifyData); + NT_ASSERT(ppInjectionData); + NT_ASSERT(pProxyData); + NT_ASSERT(*ppClassifyData); + NT_ASSERT(*ppInjectionData); + + NTSTATUS status = STATUS_SUCCESS; + FWPS_INCOMING_VALUES* pClassifyValues = (FWPS_INCOMING_VALUES*)(*ppClassifyData)->pClassifyValues; + FWPS_INCOMING_METADATA_VALUES* pMetadata = (FWPS_INCOMING_METADATA_VALUES*)(*ppClassifyData)->pMetadataValues; + UINT64 endpointHandle = 0; + FWPS_TRANSPORT_SEND_PARAMS* pSendParams = 0; + COMPARTMENT_ID compartmentID = DEFAULT_COMPARTMENT_ID; + NET_BUFFER_LIST* pNetBufferList = 0; + PROXY_COMPLETION_DATA* pCompletionData = 0; + IPPROTO protocol = IPPROTO_MAX; + BYTE* pRemoteAddress = 0; + FWP_VALUE* pAddressValue = 0; + FWP_VALUE* pProtocolValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_PROTOCOL); + + HLPR_BAIL_ON_NULL_POINTER_WITH_STATUS(pProtocolValue, + status); + +#pragma warning(push) +#pragma warning(disable: 6014) /// pCompletionData & pSendParams will be freed in completionFn using BasicPacketInjectionCompletionDataDestroy + + HLPR_NEW(pCompletionData, + PROXY_COMPLETION_DATA, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pCompletionData, + status); + + HLPR_NEW(pSendParams, + FWPS_TRANSPORT_SEND_PARAMS, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pSendParams, + status); + +#pragma warning(pop) + + pCompletionData->performedInline = isInline; + pCompletionData->pClassifyData = *ppClassifyData; + pCompletionData->pInjectionData = *ppInjectionData; + pCompletionData->pProxyData = pProxyData; + pCompletionData->pSendParams = pSendParams; + + /// Responsibility for freeing this memory has been transferred to the pCompletionData + *ppClassifyData = 0; + + *ppInjectionData = 0; + + pSendParams = 0; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_ENDPOINT_HANDLE)) + endpointHandle = pMetadata->transportEndpointHandle; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_COMPARTMENT_ID)) + compartmentID = (COMPARTMENT_ID)pMetadata->compartmentId; + + protocol = (IPPROTO)pProtocolValue->uint8; + + pAddressValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_REMOTE_ADDRESS); + if(pAddressValue) + { + if(pCompletionData->pInjectionData->addressFamily == AF_INET) + { + UINT32 tempAddress = htonl(pAddressValue->uint32); + + HLPR_NEW_ARRAY(pRemoteAddress, + BYTE, + IPV4_ADDRESS_SIZE, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pRemoteAddress, + status); + + RtlCopyMemory(pRemoteAddress, + &tempAddress, + IPV4_ADDRESS_SIZE); + } + else + { +#pragma warning(push) +#pragma warning(disable: 6014) /// pRemoteAddress will be freed in completionFn using BasicPacketModificationCompletionDataDestroy + + HLPR_NEW_ARRAY(pRemoteAddress, + BYTE, + IPV6_ADDRESS_SIZE, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pRemoteAddress, + status); + +#pragma warning(pop) + + RtlCopyMemory(pRemoteAddress, + pAddressValue->byteArray16->byteArray16, + IPV6_ADDRESS_SIZE); + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_REMOTE_SCOPE_ID)) + pCompletionData->pSendParams->remoteScopeId = pMetadata->remoteScopeId; + } + + pCompletionData->pSendParams->remoteAddress = pRemoteAddress; + } + + pCompletionData->pSendParams->controlData = (WSACMSGHDR*)pCompletionData->pInjectionData->pControlData; + pCompletionData->pSendParams->controlDataLength = pCompletionData->pInjectionData->controlDataLength; + + /// Initial offset is at Transport Header (no IP Header yet), so just clone entire NET_BUFFER_LIST. + status = FwpsAllocateCloneNetBufferList((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket, + g_pNDISPoolData->nblPoolHandle, + g_pNDISPoolData->nbPoolHandle, + 0, + &pNetBufferList); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformProxyInjectionAtOutboundTransport : FwpsAllocateCloneNetBufferList() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + if(pProxyData->flags & PCPDF_PROXY_LOCAL_PORT) + { + FWP_VALUE localPort; + + RtlZeroMemory(&localPort, + sizeof(FWP_VALUE)); + + localPort.type = FWP_UINT16; + localPort.uint16 = pProxyData->proxyLocalPort; + + switch(protocol) + { + case IPPROTO_TCP: + { + UINT32 tcpHeaderSize = TCP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + tcpHeaderSize = pMetadata->transportHeaderSize; + + status = KrnlHlprTCPHeaderModifySourcePort(&localPort, + pNetBufferList, + tcpHeaderSize); + + break; + } + case IPPROTO_UDP: + { + UINT32 udpHeaderSize = UDP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + udpHeaderSize = pMetadata->transportHeaderSize; + + status = KrnlHlprUDPHeaderModifySourcePort(&localPort, + pNetBufferList, + udpHeaderSize); + + break; + } + } + + HLPR_BAIL_ON_FAILURE(status); + } + + if(pProxyData->flags & PCPDF_PROXY_REMOTE_PORT) + { + FWP_VALUE remotePort; + + RtlZeroMemory(&remotePort, + sizeof(FWP_VALUE)); + + remotePort.type = FWP_UINT16; + remotePort.uint16 = pProxyData->proxyRemotePort; + + switch(protocol) + { + case IPPROTO_TCP: + { + UINT32 tcpHeaderSize = TCP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + tcpHeaderSize = pMetadata->transportHeaderSize; + + status = KrnlHlprTCPHeaderModifyDestinationPort(&remotePort, + pNetBufferList, + tcpHeaderSize); + + break; + } + case IPPROTO_UDP: + { + UINT32 udpHeaderSize = UDP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + udpHeaderSize = pMetadata->transportHeaderSize; + + status = KrnlHlprUDPHeaderModifyDestinationPort(&remotePort, + pNetBufferList, + udpHeaderSize); + + break; + } + } + + HLPR_BAIL_ON_FAILURE(status); + } + + /// As there is no IP Header yet, we can only modify the destination IP address via the FWPS_TRANSPORT_SEND_PARAMS + if(pProxyData->flags & PCPDF_PROXY_REMOTE_ADDRESS) + { + if(pCompletionData->pInjectionData->addressFamily == AF_INET) + RtlCopyMemory(pCompletionData->pSendParams->remoteAddress, + pProxyData->proxyRemoteAddress.pIPv4, + IPV4_ADDRESS_SIZE); + else + { + RtlCopyMemory(pCompletionData->pSendParams->remoteAddress, + pProxyData->proxyRemoteAddress.pIPv6, + IPV6_ADDRESS_SIZE); + + pCompletionData->pSendParams->remoteScopeId.Value = pProxyData->remoteScopeId; + } + } + + status = FwpsInjectTransportSendAsync(pCompletionData->pInjectionData->injectionHandle, + pCompletionData->pInjectionData->injectionContext, + endpointHandle, + 0, + pCompletionData->pSendParams, + pCompletionData->pInjectionData->addressFamily, + compartmentID, + pNetBufferList, + CompleteProxyInjection, + pCompletionData); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformProxyInjectionAtOutboundTransport : FwpsInjectTransportSendAsync() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS) + { + if(pNetBufferList) + { + FwpsFreeCloneNetBufferList(pNetBufferList, + 0); + + pNetBufferList = 0; + } + + if(pCompletionData) + ProxyCompletionDataDestroy(&pCompletionData); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PerformProxyInjectionAtOutboundTransport() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @private_function="ProxyUsingInjectionMethodDeferredProcedureCall" + + Purpose: Invokes the appropriate private injection routine to perform the injection at + DISPATCH_LEVEL.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF542972.aspx
+*/ +_IRQL_requires_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Function_class_(KDEFERRED_ROUTINE) +VOID ProxyUsingInjectionMethodDeferredProcedureCall(_In_ KDPC* pDPC, + _In_opt_ PVOID pContext, + _In_opt_ PVOID pArg1, + _In_opt_ PVOID pArg2) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> BasicPacketInjectionDeferredProcedureCall()\n"); + +#endif /// DBG + + UNREFERENCED_PARAMETER(pDPC); + UNREFERENCED_PARAMETER(pContext); + UNREFERENCED_PARAMETER(pArg2); + + NT_ASSERT(pDPC); + NT_ASSERT(pArg1); + NT_ASSERT(((DPC_DATA*)pArg1)->pClassifyData); + NT_ASSERT(((DPC_DATA*)pArg1)->pInjectionData); + + DPC_DATA* pDPCData = (DPC_DATA*)pArg1; + + if(pDPCData) + { + NTSTATUS status = STATUS_SUCCESS; + FWPS_INCOMING_VALUES* pClassifyValues = 0; + + pClassifyValues = (FWPS_INCOMING_VALUES*)pDPCData->pClassifyData->pClassifyValues; + + if(pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V6) + status = PerformProxyInjectionAtOutboundTransport(&(pDPCData->pClassifyData), + &(pDPCData->pInjectionData), + (PC_PROXY_DATA*)pDPCData->pContext); + else if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6) + status = PerformProxyInjectionAtInboundNetwork(&(pDPCData->pClassifyData), + &(pDPCData->pInjectionData), + (PC_PROXY_DATA*)pDPCData->pContext); + else + status = STATUS_FWP_INCOMPATIBLE_LAYER; + + + if(status != STATUS_SUCCESS) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! ProxyUsingInjectionMethodDeferredProcedureCall() [status: %#x]\n", + status); + + KrnlHlprDPCDataDestroy(&pDPCData); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- ProxyUsingInjectionMethodDeferredProcedureCall()\n"); + +#endif /// DBG + + return; +} + +/** + @private_function="ProxyUsingInjectionMethodWorkItemRoutine" + + Purpose:
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_IRQL_requires_(PASSIVE_LEVEL) +_IRQL_requires_same_ +_Function_class_(IO_WORKITEM_ROUTINE) +VOID ProxyUsingInjectionMethodWorkItemRoutine(_In_ PDEVICE_OBJECT pDeviceObject, + _In_opt_ PVOID pContext) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> ProxyUsingInjectionMethodWorkItemRoutine()\n"); + +#endif /// DBG + + UNREFERENCED_PARAMETER(pDeviceObject); + + NT_ASSERT(pContext); + NT_ASSERT(((WORKITEM_DATA*)pContext)->pClassifyData); + NT_ASSERT(((WORKITEM_DATA*)pContext)->pInjectionData); + + WORKITEM_DATA* pWorkItemData = (WORKITEM_DATA*)pContext; + + if(pWorkItemData) + { + NTSTATUS status = STATUS_SUCCESS; + FWPS_INCOMING_VALUES* pClassifyValues = 0; + + pClassifyValues = (FWPS_INCOMING_VALUES*)pWorkItemData->pClassifyData->pClassifyValues; + + if(pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V6) + status = PerformProxyInjectionAtOutboundTransport(&(pWorkItemData->pClassifyData), + &(pWorkItemData->pInjectionData), + (PC_PROXY_DATA*)pWorkItemData->pInjectionData->pContext); + else if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6) + status = PerformProxyInjectionAtInboundNetwork(&(pWorkItemData->pClassifyData), + &(pWorkItemData->pInjectionData), + (PC_PROXY_DATA*)pWorkItemData->pContext); + else + status = STATUS_FWP_INCOMPATIBLE_LAYER; + + + if(status != STATUS_SUCCESS) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! ProxyUsingInjectionMethodWorkItemRoutine : PerformProxyInjectionAt*() [status: %#x]\n", + status); + + KrnlHlprWorkItemDataDestroy(&pWorkItemData); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- ProxyUsingInjectionMethodWorkItemRoutine()\n"); + +#endif /// DBG + + return; +} + +/** + @private_function="TriggerProxyInjectionInline" + + Purpose:
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS TriggerProxyInjectionInline(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_ VOID* pNetBufferList, + _In_opt_ const VOID* pClassifyContext, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut, + _In_ INJECTION_DATA** ppInjectionData, + _In_ PC_PROXY_DATA* pProxyData) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> TriggerProxyInjectionInline()\n"); + +#endif /// DBG + + NT_ASSERT(pClassifyValues); + NT_ASSERT(pMetadata); + NT_ASSERT(pNetBufferList); + NT_ASSERT(pFilter); + NT_ASSERT(pClassifyOut); + NT_ASSERT(ppInjectionData); + NT_ASSERT(*ppInjectionData); + NT_ASSERT(pProxyData); + NT_ASSERT(pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V6); + + NTSTATUS status = STATUS_SUCCESS; + CLASSIFY_DATA* pClassifyData = 0; + +#pragma warning(push) +#pragma warning(disable: 6014) /// pClassifyData will be freed in completionFn using ProxyCompletionDataDestroy + + HLPR_NEW(pClassifyData, + CLASSIFY_DATA, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pClassifyData, + status); + +#pragma warning(pop) + + pClassifyData->pClassifyValues = pClassifyValues; + pClassifyData->pMetadataValues = pMetadata; + pClassifyData->pPacket = pNetBufferList; + pClassifyData->pClassifyContext = pClassifyContext; + pClassifyData->pFilter = pFilter; + pClassifyData->flowContext = flowContext; + pClassifyData->pClassifyOut = pClassifyOut; + + if(pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V6) + status = PerformProxyInjectionAtOutboundTransport(&pClassifyData, + ppInjectionData, + pProxyData, + TRUE); + else if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6) + status = PerformProxyInjectionAtInboundNetwork(&pClassifyData, + ppInjectionData, + pProxyData, + TRUE); + else + status = STATUS_FWP_INCOMPATIBLE_LAYER; + + HLPR_BAIL_LABEL: + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- TriggerProxyInjectionInline() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @private_function="TriggerProxyInjectionOutOfBand" + + Purpose:
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS TriggerProxyInjectionOutOfBand(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_ VOID* pNetBufferList, + _In_opt_ const VOID* pClassifyContext, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _In_ FWPS_CLASSIFY_OUT* pClassifyOut, + _In_ INJECTION_DATA* pInjectionData, + _In_ PC_PROXY_DATA* pPCData) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> TriggerProxyInjectionOutOfBand()\n"); + +#endif /// DBG + + NT_ASSERT(pClassifyValues); + NT_ASSERT(pMetadata); + NT_ASSERT(pNetBufferList); + NT_ASSERT(pFilter); + NT_ASSERT(pClassifyOut); + NT_ASSERT(pInjectionData); + NT_ASSERT(pPCData); + NT_ASSERT(pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V6); + + NTSTATUS status = STATUS_SUCCESS; + CLASSIFY_DATA* pClassifyData = 0; + +#pragma warning(push) +#pragma warning(disable: 6014) /// pClassifyData will be freed in completionFn using ProxyInjectionCompletionDataDestroy + + status = KrnlHlprClassifyDataCreateLocalCopy(&pClassifyData, + pClassifyValues, + pMetadata, + pNetBufferList, + pClassifyContext, + pFilter, + flowContext, + pClassifyOut); + HLPR_BAIL_ON_FAILURE(status); + +#pragma warning(pop) + + if(pPCData->useWorkItems) + status = KrnlHlprWorkItemQueue(g_pWDMDevice, + ProxyUsingInjectionMethodWorkItemRoutine, + pClassifyData, + pInjectionData, + (VOID*)pPCData); + else if(pPCData->useThreadedDPC) + status = KrnlHlprThreadedDPCQueue(ProxyUsingInjectionMethodDeferredProcedureCall, + pClassifyData, + pInjectionData, + (VOID*)pPCData); + else + status = KrnlHlprDPCQueue(ProxyUsingInjectionMethodDeferredProcedureCall, + pClassifyData, + pInjectionData, + (VOID*)pPCData); + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS) + { + if(pClassifyData) + KrnlHlprClassifyDataDestroyLocalCopy(&pClassifyData); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- TriggerProxyInjectionOutOfBand() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +#if(NTDDI_VERSION >= NTDDI_WIN7) + +#pragma warning(push) +#pragma warning(disable: 6101) /// *ppClassifyData and *ppRedirectData are freed and set to 0 on successful completion + +/** + @private_function="PerformProxySocketRedirection" + + Purpose:
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_At_(*ppRedirectData, _Pre_ _Notnull_) +///_At_(*ppRedirectData, _Post_ _Null_) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS PerformProxySocketRedirection(_In_ CLASSIFY_DATA** ppClassifyData, + _Inout_ REDIRECT_DATA** ppRedirectData) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PerformProxySocketRedirection()\n"); + +#endif /// DBG + + NT_ASSERT(ppClassifyData); + NT_ASSERT(ppRedirectData); + NT_ASSERT((*ppRedirectData)->pWritableLayerData); + NT_ASSERT((*ppRedirectData)->pProxyData); + + NTSTATUS status = STATUS_SUCCESS; + FWPS_BIND_REQUEST* pBindRequest = (FWPS_BIND_REQUEST*)(*ppRedirectData)->pWritableLayerData; + FWPS_INCOMING_VALUES* pClassifyValues = (FWPS_INCOMING_VALUES*)(*ppClassifyData)->pClassifyValues; + FWP_VALUE* pProtocolValue = 0; + UINT8 ipProtocol = 0; + + pProtocolValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_PROTOCOL); + if(pProtocolValue) + ipProtocol = pProtocolValue->uint8; + + if((*ppRedirectData)->pProxyData->flags & PCPDF_PROXY_LOCAL_ADDRESS) + INETADDR_SET_ADDRESS((PSOCKADDR)&(pBindRequest->localAddressAndPort), + (*ppRedirectData)->pProxyData->proxyLocalAddress.pBytes); + + if((*ppRedirectData)->pProxyData->flags & PCPDF_PROXY_LOCAL_PORT) + INETADDR_SET_PORT((PSOCKADDR)&(pBindRequest->localAddressAndPort), + (*ppRedirectData)->pProxyData->proxyLocalPort); + + if(ipProtocol == IPPROTO_TCP) + pBindRequest->portReservationToken = (*ppRedirectData)->pProxyData->tcpPortReservationToken; + else if(ipProtocol == IPPROTO_UDP) + pBindRequest->portReservationToken = (*ppRedirectData)->pProxyData->udpPortReservationToken; + + (*ppRedirectData)->pClassifyOut->actionType = FWP_ACTION_PERMIT; + (*ppRedirectData)->pClassifyOut->rights ^= FWPS_RIGHT_ACTION_WRITE; + +#pragma warning(push) +#pragma warning(disable: 6001) /// *ppRedirectData has already been initialized in previous call to KrnlHlprRedirectDataCreate + + /// This will apply the modified data and cleanup the classify handle + KrnlHlprRedirectDataDestroy(ppRedirectData); + +#pragma warning(pop) + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PerformProxySocketRedirection() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @private_function="PerformProxyConnectRedirection" + + Purpose:
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_At_(*ppRedirectData, _Pre_ _Notnull_) +///_At_(*ppRedirectData, _Post_ _Null_) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS PerformProxyConnectRedirection(_In_ CLASSIFY_DATA** ppClassifyData, + _Inout_ REDIRECT_DATA** ppRedirectData) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PerformProxyConnectRedirection()\n"); + +#endif /// DBG + + NT_ASSERT(ppClassifyData); + NT_ASSERT(ppRedirectData); + NT_ASSERT(*ppClassifyData); + NT_ASSERT(*ppRedirectData); + + NTSTATUS status = STATUS_SUCCESS; + FWPS_CONNECT_REQUEST* pConnectRequest = (FWPS_CONNECT_REQUEST*)(*ppRedirectData)->pWritableLayerData; + UINT32 actionType = FWP_ACTION_PERMIT; + FWPS_INCOMING_VALUES* pClassifyValues = (FWPS_INCOMING_VALUES*)(*ppClassifyData)->pClassifyValues; + FWP_VALUE* pProtocolValue = 0; + UINT8 ipProtocol = 0; + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + SOCKADDR_STORAGE* pSockAddrStorage = 0; + + /// Set redirectHandle only if proxying locally + if((*ppRedirectData)->redirectHandle && + !((*ppRedirectData)->pProxyData->proxyToRemoteService)) + pConnectRequest->localRedirectHandle = (*ppRedirectData)->redirectHandle; + + HLPR_NEW_ARRAY(pSockAddrStorage, + SOCKADDR_STORAGE, + 2, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pSockAddrStorage, + status); + + /// Pass original remote destination values to query them in user mode + RtlCopyMemory(&(pSockAddrStorage[0]), + &(pConnectRequest->remoteAddressAndPort), + sizeof(SOCKADDR_STORAGE)); + + RtlCopyMemory(&(pSockAddrStorage[1]), + &(pConnectRequest->localAddressAndPort), + sizeof(SOCKADDR_STORAGE)); + + /// WFP will take ownership of this memory and free it when the flow / redirection terminates + pConnectRequest->localRedirectContext = pSockAddrStorage; + pConnectRequest->localRedirectContextSize = sizeof(SOCKADDR_STORAGE) * 2; + +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) + + pProtocolValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_PROTOCOL); + if(pProtocolValue) + ipProtocol = pProtocolValue->uint8; + + /// For non-TCP, this setting will not be enforced being that local redirection of this tuple is only + /// available during bind time. and ideally redirection should be performed using ALE_BIND_REDIRECT instead. + if((*ppRedirectData)->pProxyData->flags & PCPDF_PROXY_LOCAL_ADDRESS) + INETADDR_SET_ADDRESS((PSOCKADDR)&(pConnectRequest->localAddressAndPort), + (*ppRedirectData)->pProxyData->proxyLocalAddress.pBytes); + + /// For non-TCP, this setting will not be enforced being that local redirection of this tuple is only + /// available during bind time. and ideally redirection should be performed using ALE_BIND_REDIRECT instead. + if((*ppRedirectData)->pProxyData->flags & PCPDF_PROXY_LOCAL_PORT) + INETADDR_SET_PORT((PSOCKADDR)&(pConnectRequest->localAddressAndPort), + (*ppRedirectData)->pProxyData->proxyLocalPort); + + if((*ppRedirectData)->pProxyData->flags & PCPDF_PROXY_REMOTE_ADDRESS) + { + if((*ppRedirectData)->pProxyData->proxyToRemoteService) + INETADDR_SET_ADDRESS((PSOCKADDR)&(pConnectRequest->remoteAddressAndPort), + (*ppRedirectData)->pProxyData->proxyRemoteAddress.pBytes); + else + { + /// Ensure we don't need to worry about crossing any of the TCP/IP stack's zones + if(INETADDR_ISANY((PSOCKADDR)&(pConnectRequest->localAddressAndPort))) + INETADDR_SETLOOPBACK((PSOCKADDR)&(pConnectRequest->remoteAddressAndPort)); + else + INETADDR_SET_ADDRESS((PSOCKADDR)&(pConnectRequest->remoteAddressAndPort), + INETADDR_ADDRESS((PSOCKADDR)&(pConnectRequest->localAddressAndPort))); + } + } + + if((*ppRedirectData)->pProxyData->flags & PCPDF_PROXY_REMOTE_PORT) + INETADDR_SET_PORT((PSOCKADDR)&(pConnectRequest->remoteAddressAndPort), + (*ppRedirectData)->pProxyData->proxyRemotePort); + + if(ipProtocol == IPPROTO_TCP) + pConnectRequest->portReservationToken = (*ppRedirectData)->pProxyData->tcpPortReservationToken; + else if(ipProtocol == IPPROTO_UDP) + pConnectRequest->portReservationToken = (*ppRedirectData)->pProxyData->udpPortReservationToken; + + if((*ppRedirectData)->pProxyData->targetProcessID) + pConnectRequest->localRedirectTargetPID = (*ppRedirectData)->pProxyData->targetProcessID; + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS) + { + actionType = FWP_ACTION_BLOCK; + + HLPR_DELETE_ARRAY(pSockAddrStorage, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + } + +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) + +#pragma warning(push) +#pragma warning(disable: 6001) /// *ppRedirectData has already been initialized in previous call to KrnlHlprRedirectDataCreate + + (*ppRedirectData)->pClassifyOut->actionType = actionType; + (*ppRedirectData)->pClassifyOut->rights ^= FWPS_RIGHT_ACTION_WRITE; + + + /// This will apply the modified data and cleanup the classify handle + KrnlHlprRedirectDataDestroy(ppRedirectData); + +#pragma warning(pop) + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PerformProxyConnectRedirection() [status:%#x]\n", + status); + +#endif /// DBG + + return status; +} + +#pragma warning(pop) + +/** + @private_function="ProxyByALERedirectDeferredProcedureCall" + + Purpose: Invokes the appropriate private redirection routine to at DISPATCH_LEVEL.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF542972.aspx
+*/ +_IRQL_requires_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Function_class_(KDEFERRED_ROUTINE) +VOID ProxyByALERedirectDeferredProcedureCall(_In_ KDPC* pDPC, + _In_opt_ PVOID pContext, + _In_opt_ PVOID pArg1, + _In_opt_ PVOID pArg2) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> ProxyByALERedirectDeferredProcedureCall()\n"); + +#endif /// DBG + + UNREFERENCED_PARAMETER(pDPC); + UNREFERENCED_PARAMETER(pContext); + UNREFERENCED_PARAMETER(pArg2); + + NT_ASSERT(pDPC); + NT_ASSERT(pArg1); + NT_ASSERT(((DPC_DATA*)pArg1)->pClassifyData); + NT_ASSERT(((DPC_DATA*)pArg1)->pInjectionData); + + DPC_DATA* pDPCData = (DPC_DATA*)pArg1; + + if(pDPCData) + { + NTSTATUS status = STATUS_SUCCESS; + FWPS_INCOMING_VALUES* pClassifyValues = (FWPS_INCOMING_VALUES*)pDPCData->pClassifyData->pClassifyValues; + + if(pClassifyValues->layerId == FWPS_LAYER_ALE_CONNECT_REDIRECT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_CONNECT_REDIRECT_V6) + status = PerformProxyConnectRedirection(&(pDPCData->pClassifyData), + &(pDPCData->pRedirectData)); + else if(pClassifyValues->layerId == FWPS_LAYER_ALE_BIND_REDIRECT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_BIND_REDIRECT_V6) + status = PerformProxySocketRedirection(&(pDPCData->pClassifyData), + &(pDPCData->pRedirectData)); + + if(status != STATUS_SUCCESS) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! ProxyByALERedirectDeferredProcedureCall() [status: %#x]\n", + status); + + KrnlHlprDPCDataDestroy(&pDPCData); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- ProxyByALERedirectDeferredProcedureCall()\n"); + +#endif /// DBG + + return; +} + +/** + @private_function="ProxyByALERedirectWorkItemRoutine" + + Purpose:
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_IRQL_requires_(PASSIVE_LEVEL) +_IRQL_requires_same_ +_Function_class_(IO_WORKITEM_ROUTINE) +VOID ProxyByALERedirectWorkItemRoutine(_In_ PDEVICE_OBJECT pDeviceObject, + _In_opt_ PVOID pContext) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> ProxyByALERedirectWorkItemRoutine()\n"); + +#endif /// DBG + + UNREFERENCED_PARAMETER(pDeviceObject); + + NT_ASSERT(pContext); + NT_ASSERT((WORKITEM_DATA*)pContext); + NT_ASSERT(((WORKITEM_DATA*)pContext)->pClassifyData); + NT_ASSERT(((WORKITEM_DATA*)pContext)->pRedirectData); + + WORKITEM_DATA* pWorkItemData = (WORKITEM_DATA*)pContext; + + if(pWorkItemData) + { + NTSTATUS status = STATUS_SUCCESS; + FWPS_INCOMING_VALUES* pClassifyValues = (FWPS_INCOMING_VALUES*)pWorkItemData->pClassifyData->pClassifyValues; + + if(pClassifyValues->layerId == FWPS_LAYER_ALE_CONNECT_REDIRECT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_CONNECT_REDIRECT_V6) + status = PerformProxyConnectRedirection(&(pWorkItemData->pClassifyData), + &(pWorkItemData->pRedirectData)); + else if(pClassifyValues->layerId == FWPS_LAYER_ALE_BIND_REDIRECT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_BIND_REDIRECT_V6) + status = PerformProxySocketRedirection(&(pWorkItemData->pClassifyData), + &(pWorkItemData->pRedirectData)); + + if(status != STATUS_SUCCESS) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! ProxyByALERedirectWorkItemRoutine() [status: %#x]\n", + status); + + KrnlHlprWorkItemDataDestroy(&pWorkItemData); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- ProxyUsingInjectionMethodWorkItemRoutine()\n"); + +#endif /// DBG + + return; +} + +/** + @private_function="TriggerProxyByALERedirectInline" + + Purpose:
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS TriggerProxyByALERedirectInline(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_ VOID* pLayerData, + _In_opt_ const VOID* pClassifyContext, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut, + _Inout_ REDIRECT_DATA** ppRedirectData) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> TriggerProxyByALERedirectInline()\n"); + +#endif /// DBG + + NT_ASSERT(pClassifyValues); + NT_ASSERT(pMetadata); + NT_ASSERT(pLayerData); + NT_ASSERT(pFilter); + NT_ASSERT(pClassifyOut); + NT_ASSERT(ppRedirectData); + NT_ASSERT(*ppRedirectData); + + NTSTATUS status = STATUS_SUCCESS; + CLASSIFY_DATA* pClassifyData = 0; + + HLPR_NEW(pClassifyData, + CLASSIFY_DATA, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pClassifyData, + status); + + pClassifyData->pClassifyValues = pClassifyValues; + pClassifyData->pMetadataValues = pMetadata; + pClassifyData->pPacket = pLayerData; + pClassifyData->pClassifyContext = pClassifyContext; + pClassifyData->pFilter = pFilter; + pClassifyData->flowContext = flowContext; + pClassifyData->pClassifyOut = pClassifyOut; + + (*ppRedirectData)->pClassifyOut = pClassifyOut; + + if(pClassifyValues->layerId == FWPS_LAYER_ALE_CONNECT_REDIRECT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_CONNECT_REDIRECT_V6) + status = PerformProxyConnectRedirection(&pClassifyData, + ppRedirectData); + else if(pClassifyValues->layerId == FWPS_LAYER_ALE_BIND_REDIRECT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_BIND_REDIRECT_V6) + status = PerformProxySocketRedirection(&pClassifyData, + ppRedirectData); + + HLPR_BAIL_LABEL: + + HLPR_DELETE(pClassifyData, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- TriggerProxyByALERedirectInline() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @private_function="TriggerProxyByALERedirectOutOfBand" + + Purpose:
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS TriggerProxyByALERedirectOutOfBand(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_ VOID* pLayerData, + _In_opt_ const VOID* pClassifyContext, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _In_ FWPS_CLASSIFY_OUT* pClassifyOut, + _Inout_ REDIRECT_DATA* pRedirectData) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> TriggerProxyByALERedirectOutOfBand()\n"); + +#endif /// DBG + + NT_ASSERT(pClassifyValues); + NT_ASSERT(pMetadata); + NT_ASSERT(pLayerData); + NT_ASSERT(pFilter); + NT_ASSERT(pClassifyOut); + NT_ASSERT(pRedirectData); + + NTSTATUS status = STATUS_SUCCESS; + CLASSIFY_DATA* pClassifyData = 0; + +#pragma warning(push) +#pragma warning(disable: 6014) /// pClassifyData will be freed in completionFn using ProxyInjectionCompletionDataDestroy + + status = KrnlHlprClassifyDataCreateLocalCopy(&pClassifyData, + pClassifyValues, + pMetadata, + pLayerData, + pClassifyContext, + pFilter, + flowContext, + pClassifyOut); + HLPR_BAIL_ON_FAILURE(status); + +#pragma warning(pop) + + status = FwpsPendClassify(pRedirectData->classifyHandle, + pFilter->filterId, + 0, + pClassifyOut); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! TriggerProxyByALERedirectOutOfBand : FwpsPendClassify() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + pClassifyOut->actionType = FWP_ACTION_BLOCK; + pClassifyOut->rights ^= FWPS_RIGHT_ACTION_WRITE; + + pRedirectData->isPended = TRUE; + pRedirectData->pClassifyOut = pClassifyData->pClassifyOut; + + if(pRedirectData->pProxyData->useWorkItems) + status = KrnlHlprWorkItemQueue(g_pWDMDevice, + ProxyByALERedirectWorkItemRoutine, + pClassifyData, + pRedirectData, + 0); + else if(pRedirectData->pProxyData->useThreadedDPC) + status = KrnlHlprThreadedDPCQueue(ProxyByALERedirectDeferredProcedureCall, + pClassifyData, + pRedirectData, + 0); + else + status = KrnlHlprDPCQueue(ProxyByALERedirectDeferredProcedureCall, + pClassifyData, + pRedirectData, + 0); + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS) + { + if(pClassifyData) + KrnlHlprClassifyDataDestroyLocalCopy(&pClassifyData); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- TriggerProxyByALERedirectOutOfBand() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @classify_function="ClassifyProxyByALERedirect" + + Purpose:
+
+ Notes: Applies to the following layers:
+ FWPM_LAYER_ALE_REDIRECT_BIND_V4
+ FWPM_LAYER_ALE_REDIRECT_BIND_V6
+ FWPM_LAYER_ALE_REDIRECT_CONNECT_V4
+ FWPM_LAYER_ALE_REDIRECT_CONNECT_V6
+
+ Microsoft recommends using FWPM_LAYER_STREAM_V{4/6} rather than proxying network + data to a local service. Doing so will make for a better ecosystem, however if you + feel you must proxy, then it is advised to use + FWPM_LAYER_ALE_REDIRECT_CONNECT_V{4/6}, and have the proxy service call the + REDIRECT_RECORD IOCTLs so multiple proxies can coexst without losing data on the + origin of the data.
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551229.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF544893.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF571005.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/HH439677.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI ClassifyProxyByALERedirect(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pLayerData, + _In_opt_ const VOID* pClassifyContext, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut) +{ + NT_ASSERT(pClassifyValues); + NT_ASSERT(pMetadata); + NT_ASSERT(pLayerData); + NT_ASSERT(pClassifyContext); + NT_ASSERT(pFilter); + NT_ASSERT(pClassifyOut); + NT_ASSERT(pClassifyValues->layerId == FWPS_LAYER_ALE_CONNECT_REDIRECT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_CONNECT_REDIRECT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_BIND_REDIRECT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_BIND_REDIRECT_V6); + NT_ASSERT(pFilter->providerContext); + NT_ASSERT(pFilter->providerContext->type == FWPM_GENERAL_CONTEXT); + NT_ASSERT(pFilter->providerContext->dataBuffer); + NT_ASSERT(pFilter->providerContext->dataBuffer->size == sizeof(PC_PROXY_DATA)); + NT_ASSERT(pFilter->providerContext->dataBuffer->data); + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> ClassifyProxyByALERedirect() [Layer: %s][FilterID: %#I64x][Rights: %#x]", + KrnlHlprFwpsLayerIDToString(pClassifyValues->layerId), + pFilter->filterId, + pClassifyOut->rights); + + if(pClassifyOut->rights & FWPS_RIGHT_ACTION_WRITE) + { + NTSTATUS status = STATUS_SUCCESS; + UINT32 conditionIndex = FWPS_FIELD_ALE_CONNECT_REDIRECT_V4_FLAGS; + BOOLEAN redirectSocket = FALSE; + REDIRECT_DATA* pRedirectData = 0; + UINT32 index = WFPSAMPLER_INDEX; + + if(pFilter->subLayerWeight == FWPM_SUBLAYER_UNIVERSAL_WEIGHT) + index = UNIVERSAL_INDEX; + + if(pClassifyValues->layerId == FWPS_LAYER_ALE_BIND_REDIRECT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_BIND_REDIRECT_V6) + { + conditionIndex = FWPS_FIELD_ALE_BIND_REDIRECT_V4_FLAGS; + + redirectSocket = TRUE; + } + + /// Prevent infinite redirection + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + if(!redirectSocket) + { + FWPS_CONNECTION_REDIRECT_STATE redirectionState = FWPS_CONNECTION_NOT_REDIRECTED; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_REDIRECT_RECORD_HANDLE)) + { + VOID* pRedirectContext = 0; + + redirectionState = FwpsQueryConnectionRedirectState(pMetadata->redirectRecords, + *(g_WFPSamplerDeviceData.ppRedirectionHandles[index]), + &pRedirectContext); + } + + switch(redirectionState) + { + /// Go ahead and continue with our redirection + case FWPS_CONNECTION_NOT_REDIRECTED: + case FWPS_CONNECTION_REDIRECTED_BY_OTHER: + { + break; + } + /// We've already seen this, so let it through + case FWPS_CONNECTION_REDIRECTED_BY_SELF: + { + pClassifyOut->actionType = FWP_ACTION_PERMIT; + + HLPR_BAIL; + } + /// Must not perform redirection. In this case we are letting the last redirection action win. + case FWPS_CONNECTION_PREVIOUSLY_REDIRECTED_BY_SELF: + { + HLPR_BAIL; + } + } + } + +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) + +#pragma warning(push) +#pragma warning(disable: 6014) /// pRedirectData will be freed in completionFn using ProxyInjectionCompletionDataDestroy + + status = KrnlHlprRedirectDataCreate(&pRedirectData, + pClassifyContext, + pFilter, + pClassifyOut, + *(g_WFPSamplerDeviceData.ppRedirectionHandles[index])); + HLPR_BAIL_ON_FAILURE(status); + +#pragma warning(pop) + + /// FWP_CONDITION_FLAG_IS_REAUTHORIZE will be set if: + /// 1) a callout with a higher priority completed its pended classification and the current + /// callout is seeing the connection for the first time + /// 2) FWPS_CLASSIFY_FLAG_REAUTHORIZE_IF_MODIFIED_BY_OTHERS was set when calling FwpsApplyModifiedLayerData + /// and another callout has further redirected the initial connection. + if(pClassifyValues->incomingValue[conditionIndex].value.uint32 & FWP_CONDITION_FLAG_IS_REAUTHORIZE) + { + if(redirectSocket) + { + FWPS_BIND_REQUEST* pBindRequest = ((FWPS_BIND_REQUEST*)(pRedirectData->pWritableLayerData))->previousVersion; + + /// ReAuth for our proxying, so let it go, + /// and set the rights to write back as it was removed by acquiring the layer data + if(pBindRequest->modifierFilterId == pFilter->filterId) + { + pClassifyOut->actionType = FWP_ACTION_PERMIT; + pClassifyOut->rights |= FWPS_RIGHT_ACTION_WRITE; + + KrnlHlprRedirectDataDestroy(&pRedirectData); + + HLPR_BAIL; + } + } + else + { + FWPS_CONNECT_REQUEST* pConnectRequest = ((FWPS_CONNECT_REQUEST*)(pRedirectData->pWritableLayerData))->previousVersion; + + /// ReAuth for our proxying, so let it go, + /// and set the rights to write back as it was removed by acquiring the layer data + if(pConnectRequest->modifierFilterId == pFilter->filterId) + { + pClassifyOut->actionType = FWP_ACTION_PERMIT; + pClassifyOut->rights |= FWPS_RIGHT_ACTION_WRITE; + + KrnlHlprRedirectDataDestroy(&pRedirectData); + + HLPR_BAIL; + } + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + else + { + /// No more redirection if someone else has done it locally. We could catch the new + /// connection from their proxy if we really must act on it. + /// Set the rights to write back as it was removed when acquiring the layer data + if(pConnectRequest->localRedirectHandle) + { + pClassifyOut->actionType = FWP_ACTION_PERMIT; + pClassifyOut->rights |= FWPS_RIGHT_ACTION_WRITE; + + KrnlHlprRedirectDataDestroy(&pRedirectData); + + HLPR_BAIL; + } + } + +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) + + } + } + + if(redirectSocket) + { + UINT32 timesRedirected = 0; + + for(FWPS_BIND_REQUEST* pBindRequest = ((FWPS_BIND_REQUEST*)(pRedirectData->pWritableLayerData))->previousVersion; + pBindRequest; + pBindRequest = pBindRequest->previousVersion) + { + if(pBindRequest->modifierFilterId == pFilter->filterId) + timesRedirected++; + + /// Don't redirect the same socket more than 3 times + if(timesRedirected > 3) + { + status = STATUS_TOO_MANY_COMMANDS; + + HLPR_BAIL; + } + } + } + else + { + UINT32 timesRedirected = 0; + + for(FWPS_CONNECT_REQUEST* pConnectRequest = ((FWPS_CONNECT_REQUEST*)(pRedirectData->pWritableLayerData))->previousVersion; + pConnectRequest; + pConnectRequest = pConnectRequest->previousVersion) + { + if(pConnectRequest->modifierFilterId == pFilter->filterId) + timesRedirected++; + + /// Don't redirect the same connection more than 3 times + if(timesRedirected > 3) + { + status = STATUS_TOO_MANY_COMMANDS; + + HLPR_BAIL; + } + } + } + + if(pRedirectData->pProxyData->performInline) + status = TriggerProxyByALERedirectInline(pClassifyValues, + pMetadata, + pLayerData, + pClassifyContext, + pFilter, + flowContext, + pClassifyOut, + &pRedirectData); + else + status = TriggerProxyByALERedirectOutOfBand(pClassifyValues, + pMetadata, + pLayerData, + pClassifyContext, + pFilter, + flowContext, + pClassifyOut, + pRedirectData); + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! ClassifyProxyByALERedirect() [status: %#x]\n", + status); + + if(pRedirectData) + KrnlHlprRedirectDataDestroy(&pRedirectData); + + pClassifyOut->actionType = FWP_ACTION_BLOCK; + pClassifyOut->rights |= FWPS_RIGHT_ACTION_WRITE; + } + } + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- ClassifyProxyByALERedirect() [Layer: %s][FilterID: %#I64x][Action: %#x][Rights: %#x][Absorb: %s]\n", + KrnlHlprFwpsLayerIDToString(pClassifyValues->layerId), + pFilter->filterId, + pClassifyOut->actionType, + pClassifyOut->rights, + (pClassifyOut->flags & FWPS_CLASSIFY_OUT_FLAG_ABSORB) ? "TRUE" : "FALSE"); + + return; +} + +/** + @classify_function="ClassifyProxyByInjection" + + Purpose:
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI ClassifyProxyByInjection(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pNetBufferList, + _In_opt_ const VOID* pClassifyContext, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut) +{ + NT_ASSERT(pClassifyValues); + NT_ASSERT(pMetadata); + NT_ASSERT(pNetBufferList); + NT_ASSERT(pFilter); + NT_ASSERT(pClassifyOut); + NT_ASSERT(pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V6); + NT_ASSERT(pFilter->providerContext); + NT_ASSERT(pFilter->providerContext->type == FWPM_GENERAL_CONTEXT); + NT_ASSERT(pFilter->providerContext->dataBuffer); + NT_ASSERT(pFilter->providerContext->dataBuffer->data); + NT_ASSERT(pFilter->providerContext->dataBuffer->size == sizeof(PC_PROXY_DATA)); + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> ClassifyProxyByInjection() [Layer: %s][FilterID: %#I64x][Rights: %#x]", + KrnlHlprFwpsLayerIDToString(pClassifyValues->layerId), + pFilter->filterId, + pClassifyOut->rights); + + if(pClassifyOut->rights & FWPS_RIGHT_ACTION_WRITE) + { + NTSTATUS status = STATUS_SUCCESS; + INJECTION_DATA* pInjectionData = 0; + + pClassifyOut->actionType = FWP_ACTION_CONTINUE; + + if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6) + { + /// Validate this is the correct traffic + PC_PROXY_DATA* pProxyData = (PPC_PROXY_DATA)pFilter->providerContext->dataBuffer->data; + UINT32 ipHeaderSize = 0; + UINT8 version = IPV4; + IPPROTO protocol = IPPROTO_MAX; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_IP_HEADER_SIZE)) + ipHeaderSize = pMetadata->ipHeaderSize; + + /// Initial offset is at the Transport Header, so retreat the size of the IP Header ... + status = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pNetBufferList), + ipHeaderSize, + 0, + 0); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! ClassifyProxyByInjection: NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + version = KrnlHlprIPHeaderGetVersionField((NET_BUFFER_LIST*)pNetBufferList); + + protocol = KrnlHlprIPHeaderGetProtocolField((NET_BUFFER_LIST*)pNetBufferList, + version == IPV4 ? AF_INET : AF_INET6); + + /// ... and advance the offset back to the original position. + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pNetBufferList), + ipHeaderSize, + FALSE, + 0); + + if(protocol == pProxyData->ipProtocol) + { + UINT16 destinationPort = KrnlHlprTransportHeaderGetDestinationPortField((NET_BUFFER_LIST*)pNetBufferList, + protocol); + UINT16 sourcePort = KrnlHlprTransportHeaderGetSourcePortField((NET_BUFFER_LIST*)pNetBufferList, + protocol); + + if(pProxyData->proxyLocalPort && + pProxyData->proxyLocalPort != destinationPort) + HLPR_BAIL; + + if(pProxyData->proxyRemotePort && + pProxyData->proxyRemotePort != sourcePort) + HLPR_BAIL; + } + else + HLPR_BAIL; + } + +#pragma warning(push) +#pragma warning(disable: 6014) /// pInjectionData will be freed in completionFn using ProxyInjectionCompletionDataDestroy + + status = KrnlHlprInjectionDataCreate(&pInjectionData, + pClassifyValues, + pMetadata, + (NET_BUFFER_LIST*)pNetBufferList, + pFilter); + HLPR_BAIL_ON_FAILURE(status); + +#pragma warning(pop) + + if(pInjectionData->injectionState != FWPS_PACKET_INJECTED_BY_SELF && + pInjectionData->injectionState != FWPS_PACKET_PREVIOUSLY_INJECTED_BY_SELF) + { + PC_PROXY_DATA* pProxyData = (PPC_PROXY_DATA)pFilter->providerContext->dataBuffer->data; + + pClassifyOut->actionType = FWP_ACTION_BLOCK; + pClassifyOut->flags |= FWPS_CLASSIFY_OUT_FLAG_ABSORB; + + if(pProxyData->performInline) + status = TriggerProxyInjectionInline(pClassifyValues, + pMetadata, + pNetBufferList, + pClassifyContext, + pFilter, + flowContext, + pClassifyOut, + &pInjectionData, + pProxyData); + else + status = TriggerProxyInjectionOutOfBand(pClassifyValues, + pMetadata, + pNetBufferList, + pClassifyContext, + pFilter, + flowContext, + pClassifyOut, + pInjectionData, + pProxyData); + } + else + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " -- ClassifyProxyByInjection() Injection previously performed.\n"); + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! ClassifyProxyByInjection() [status: %#x]\n", + status); + + if(pInjectionData) + KrnlHlprInjectionDataDestroy(&pInjectionData); + } + } + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- ClassifyProxyByInjection() [Layer: %s][FilterID: %#I64x][Action: %#x][Rights: %#x][Absorb: %s]\n", + KrnlHlprFwpsLayerIDToString(pClassifyValues->layerId), + pFilter->filterId, + pClassifyOut->actionType, + pClassifyOut->rights, + (pClassifyOut->flags & FWPS_CLASSIFY_OUT_FLAG_ABSORB) ? "TRUE" : "FALSE"); + + return; +} + +#else + +/** + @classify_function="ClassifyProxyByALE" + + Purpose: Stub function for downlevel OS's trying to use uplevel functionality.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551229.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF544890.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI ClassifyProxyByALERedirect(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pLayerData, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut) +{ + NT_ASSERT(pClassifyValues); + NT_ASSERT(pMetadata); + NT_ASSERT(pLayerData); + NT_ASSERT(pFilter); + NT_ASSERT(pClassifyOut); + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> ClassifyProxyByALERedirect() [Layer: %s][FilterID: %#I64x][Rights: %#x]", + KrnlHlprFwpsLayerIDToString(pClassifyValues->layerId), + pFilter->filterId, + pClassifyOut->rights); + + UNREFERENCED_PARAMETER(pMetadata); + UNREFERENCED_PARAMETER(pLayerData); + UNREFERENCED_PARAMETER(pFilter); + UNREFERENCED_PARAMETER(flowContext); + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! ClassifyProxyByALERedirect() : Method not supported prior to Windows 7 [status: %#x]\n", + (UINT32)STATUS_UNSUCCESSFUL); + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- ClassifyProxyByALERedirect() [Layer: %s][FilterID: %#I64x][Action: %#x][Rights: %#x][Absorb: %s]\n", + KrnlHlprFwpsLayerIDToString(pClassifyValues->layerId), + pFilter->filterId, + pClassifyOut->actionType, + pClassifyOut->rights, + (pClassifyOut->flags & FWPS_CLASSIFY_OUT_FLAG_ABSORB) ? "TRUE" : "FALSE"); + + return; +}; + +/** + @classify_function="ClassifyProxyByInjection" + + Purpose:
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI ClassifyProxyByInjection(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pNetBufferList, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut) +{ + NT_ASSERT(pClassifyValues); + NT_ASSERT(pMetadata); + NT_ASSERT(pNetBufferList); + NT_ASSERT(pFilter); + NT_ASSERT(pClassifyOut); + NT_ASSERT(pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V6); + NT_ASSERT(pFilter->providerContext); + NT_ASSERT(pFilter->providerContext->type == FWPM_GENERAL_CONTEXT); + NT_ASSERT(pFilter->providerContext->dataBuffer); + NT_ASSERT(pFilter->providerContext->dataBuffer->data); + NT_ASSERT(pFilter->providerContext->dataBuffer->size == sizeof(PC_PROXY_DATA)); + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> ClassifyProxyByInjection() [Layer: %s][FilterID: %#I64x][Rights: %#x]", + KrnlHlprFwpsLayerIDToString(pClassifyValues->layerId), + pFilter->filterId, + pClassifyOut->rights); + + if(pNetBufferList) + { + NTSTATUS status = STATUS_SUCCESS; + INJECTION_DATA* pInjectionData = 0; + + pClassifyOut->actionType = FWP_ACTION_CONTINUE; + +#pragma warning(push) +#pragma warning(disable: 6014) /// pInjectionData will be freed in completionFn using ProxyInjectionCompletionDataDestroy + + status = KrnlHlprInjectionDataCreate(&pInjectionData, + pClassifyValues, + pMetadata, + (NET_BUFFER_LIST*)pNetBufferList, + pFilter); + HLPR_BAIL_ON_FAILURE(status); + +#pragma warning(pop) + + if(pInjectionData->injectionState != FWPS_PACKET_INJECTED_BY_SELF && + pInjectionData->injectionState != FWPS_PACKET_PREVIOUSLY_INJECTED_BY_SELF) + { + PC_PROXY_DATA* pProxyData = (PPC_PROXY_DATA)pFilter->providerContext->dataBuffer->data; + + pClassifyOut->actionType = FWP_ACTION_BLOCK; + pClassifyOut->flags |= FWPS_CLASSIFY_OUT_FLAG_ABSORB; + + if(pProxyData->performInline) + status = TriggerProxyInjectionInline(pClassifyValues, + pMetadata, + pNetBufferList, + 0, + pFilter, + flowContext, + pClassifyOut, + &pInjectionData, + pProxyData); + else + status = TriggerProxyInjectionOutOfBand(pClassifyValues, + pMetadata, + pNetBufferList, + 0, + pFilter, + flowContext, + pClassifyOut, + pInjectionData, + pProxyData); + } + else + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " -- ClassifyProxyByInjection() Injection previously performed.\n"); + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! ClassifyProxyByInjection() [status: %#x]\n", + status); + + if(pInjectionData) + KrnlHlprInjectionDataDestroy(&pInjectionData); + } + } + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- ClassifyProxyByInjection() [Layer: %s][FilterID: %#I64x][Action: %#x][Rights: %#x][Absorb: %s]\n", + KrnlHlprFwpsLayerIDToString(pClassifyValues->layerId), + pFilter->filterId, + pClassifyOut->actionType, + pClassifyOut->rights, + (pClassifyOut->flags & FWPS_CLASSIFY_OUT_FLAG_ABSORB) ? "TRUE" : "FALSE"); + + return; +} + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) diff --git a/network/trans/WFPSampler/sys/ClassifyFunctions_ProxyCallouts.h b/network/trans/WFPSampler/sys/ClassifyFunctions_ProxyCallouts.h new file mode 100644 index 000000000..378b7eb48 --- /dev/null +++ b/network/trans/WFPSampler/sys/ClassifyFunctions_ProxyCallouts.h @@ -0,0 +1,91 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// ClassifyFunctions_ProxyCallouts.cpp +// +// Abstract: +// This module contains prototypes for WFP Classify functions for proxying connections and +// sockets. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Enhance function declaration for IntelliSense +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef CLASSIFY_PROXY_H +#define CLASSIFY_PROXY_H + +_IRQL_requires_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Function_class_(KDEFERRED_ROUTINE) +VOID ProxyUsingInjectionMethodDeferredProcedureCall(_In_ KDPC* pDPC, + _In_opt_ PVOID pContext, + _In_opt_ PVOID pArg1, + _In_opt_ PVOID pArg2); + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + +_IRQL_requires_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Function_class_(KDEFERRED_ROUTINE) +VOID ProxyByALERedirectDeferredProcedureCall(_In_ KDPC* pDPC, + _In_opt_ PVOID pContext, + _In_opt_ PVOID pArg1, + _In_opt_ PVOID pArg2); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI ClassifyProxyByALERedirect(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pNetBufferList, + _In_opt_ const VOID* pClassifyContext, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI ClassifyProxyByInjection(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pNetBufferList, + _In_opt_ const VOID* pClassifyContext, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut); + +#else + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI ClassifyProxyByALERedirect(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pNetBufferList, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI ClassifyProxyByInjection(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pNetBufferList, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut); + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + +#endif /// CLASSIFY_PROXY_H diff --git a/network/trans/WFPSampler/sys/CompletionFunctions_AdvancedPacketInjectionCallouts.cpp b/network/trans/WFPSampler/sys/CompletionFunctions_AdvancedPacketInjectionCallouts.cpp new file mode 100644 index 000000000..7348cb8d4 --- /dev/null +++ b/network/trans/WFPSampler/sys/CompletionFunctions_AdvancedPacketInjectionCallouts.cpp @@ -0,0 +1,312 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// CompletionFunctions_AdvancedPacketInjectionCallouts.cpp +// +// Abstract: +// This module contains WFP Completion functions for injecting packets back into the data path +// using the allocate / block / inject method. +// +// Naming Convention: +// +// +// +// i.e. +// CompleteAdvancedPacketInjection +// +// +// Complete - Function is an FWPS_INJECT_COMPLETE function. +// +// AdvancedPacketInjection - Function demonstrates the allocate / block / +// inject model. +// +// +// +// i.e. +// AdvancedPacketInjectionCompletionDataDestroy +// +// +// { +// AdvancedPacketInjectionCompletionData - pertains to +// ADVANCED_PACKET_INJECTION_COMPLETION_DATA. +// } +// +// Destroy - Cleans up and frees all memory in the object. +// +// Private Functions: +// AdvancedPacketInjectionCompletionDataDestroy(), +// +// Public Functions: +// CompleteAdvancedPacketInjection(), +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// December 13, 2013 - 1.1 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "Framework_WFPSamplerCalloutDriver.h" /// . +#include "CompletionFunctions_AdvancedPacketInjectionCallouts.tmh" /// $(OBJ_PATH)\$(O)\ + +#if DBG + +INJECTION_COUNTERS g_apiTotalCompletions = {0}; + +/** + @function="AdvancedPacketInjectionCountersDecrement" + + Purpose: Decrement the appropriate counters based on the injection handle.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Desktop/MS683581.aspx
+*/ +VOID AdvancedPacketInjectionCountersDecrement(_In_ HANDLE injectionHandle, + _Inout_ INJECTION_COUNTERS* pCounters) +{ + if(injectionHandle == g_pIPv4InboundNetworkInjectionHandles[0] || + injectionHandle == g_pIPv4InboundNetworkInjectionHandles[1]) + InterlockedDecrement64((LONG64*)&(pCounters->inboundNetwork_IPv4)); + else if(injectionHandle == g_pIPv6InboundNetworkInjectionHandles[0] || + injectionHandle == g_pIPv6InboundNetworkInjectionHandles[1]) + InterlockedDecrement64((LONG64*)&(pCounters->inboundNetwork_IPv6)); + else if(injectionHandle == g_pIPv4OutboundNetworkInjectionHandles[0] || + injectionHandle == g_pIPv4OutboundNetworkInjectionHandles[1]) + InterlockedDecrement64((LONG64*)&(pCounters->outboundNetwork_IPv4)); + else if(injectionHandle == g_pIPv6OutboundNetworkInjectionHandles[0] || + injectionHandle == g_pIPv6OutboundNetworkInjectionHandles[1]) + InterlockedDecrement64((LONG64*)&(pCounters->outboundNetwork_IPv6)); + else if(injectionHandle == g_pIPv4InboundForwardInjectionHandles[0] || + injectionHandle == g_pIPv4InboundForwardInjectionHandles[1]) + InterlockedDecrement64((LONG64*)&(pCounters->inboundForward_IPv4)); + else if(injectionHandle == g_pIPv6InboundForwardInjectionHandles[0] || + injectionHandle == g_pIPv6InboundForwardInjectionHandles[1]) + InterlockedDecrement64((LONG64*)&(pCounters->inboundForward_IPv6)); + else if(injectionHandle == g_pIPv4OutboundForwardInjectionHandles[0] || + injectionHandle == g_pIPv4OutboundForwardInjectionHandles[1]) + InterlockedDecrement64((LONG64*)&(pCounters->outboundForward_IPv4)); + else if(injectionHandle == g_pIPv6OutboundForwardInjectionHandles[0] || + injectionHandle == g_pIPv6OutboundForwardInjectionHandles[1]) + InterlockedDecrement64((LONG64*)&(pCounters->outboundForward_IPv6)); + else if(injectionHandle == g_pIPv4InboundTransportInjectionHandles[0] || + injectionHandle == g_pIPv4InboundTransportInjectionHandles[1]) + InterlockedDecrement64((LONG64*)&(pCounters->inboundTransport_IPv4)); + else if(injectionHandle == g_pIPv6InboundTransportInjectionHandles[0] || + injectionHandle == g_pIPv6InboundTransportInjectionHandles[1]) + InterlockedDecrement64((LONG64*)&(pCounters->inboundTransport_IPv6)); + else if(injectionHandle == g_pIPv4OutboundTransportInjectionHandles[0] || + injectionHandle == g_pIPv4OutboundTransportInjectionHandles[1]) + InterlockedDecrement64((LONG64*)&(pCounters->outboundTransport_IPv4)); + else if(injectionHandle == g_pIPv6OutboundTransportInjectionHandles[0] || + injectionHandle == g_pIPv6OutboundTransportInjectionHandles[1]) + InterlockedDecrement64((LONG64*)&(pCounters->outboundTransport_IPv6)); + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + else if(injectionHandle == g_pIPv4InboundMACInjectionHandles[0] || + injectionHandle == g_pIPv4InboundMACInjectionHandles[1]) + InterlockedDecrement64((LONG64*)&(pCounters->inboundMAC_IPv4)); + else if(injectionHandle == g_pIPv6InboundMACInjectionHandles[0] || + injectionHandle == g_pIPv6InboundMACInjectionHandles[1]) + InterlockedDecrement64((LONG64*)&(pCounters->inboundMAC_IPv6)); + else if(injectionHandle == g_pInboundMACInjectionHandles[0] || + injectionHandle == g_pInboundMACInjectionHandles[1]) + InterlockedDecrement64((LONG64*)&(pCounters->inboundMAC_Unknown)); + else if(injectionHandle == g_pIPv4OutboundMACInjectionHandles[0] || + injectionHandle == g_pIPv4OutboundMACInjectionHandles[1]) + InterlockedDecrement64((LONG64*)&(pCounters->outboundMAC_IPv4)); + else if(injectionHandle == g_pIPv6OutboundMACInjectionHandles[0] || + injectionHandle == g_pIPv6OutboundMACInjectionHandles[1]) + InterlockedDecrement64((LONG64*)&(pCounters->outboundMAC_IPv6)); + else if(injectionHandle == g_pOutboundMACInjectionHandles[0] || + injectionHandle == g_pOutboundMACInjectionHandles[1]) + InterlockedDecrement64((LONG64*)&(pCounters->outboundMAC_Unknown)); + else if(injectionHandle == g_pIPv4IngressVSwitchEthernetInjectionHandles[0] || + injectionHandle == g_pIPv4IngressVSwitchEthernetInjectionHandles[1]) + InterlockedDecrement64((LONG64*)&(pCounters->ingressVSwitch_IPv4)); + else if(injectionHandle == g_pIPv6IngressVSwitchEthernetInjectionHandles[0] || + injectionHandle == g_pIPv6IngressVSwitchEthernetInjectionHandles[1]) + InterlockedDecrement64((LONG64*)&(pCounters->ingressVSwitch_IPv6)); + else if(injectionHandle == g_pIngressVSwitchEthernetInjectionHandles[0] || + injectionHandle == g_pIngressVSwitchEthernetInjectionHandles[1]) + InterlockedDecrement64((LONG64*)&(pCounters->ingressVSwitch_Unknown)); + else if(injectionHandle == g_pIPv4EgressVSwitchEthernetInjectionHandles[0] || + injectionHandle == g_pIPv4EgressVSwitchEthernetInjectionHandles[1]) + InterlockedDecrement64((LONG64*)&(pCounters->egressVSwitch_IPv4)); + else if(injectionHandle == g_pIPv6EgressVSwitchEthernetInjectionHandles[0] || + injectionHandle == g_pIPv6EgressVSwitchEthernetInjectionHandles[1]) + InterlockedDecrement64((LONG64*)&(pCounters->egressVSwitch_IPv6)); + else if(injectionHandle == g_pEgressVSwitchEthernetInjectionHandles[0] || + injectionHandle == g_pEgressVSwitchEthernetInjectionHandles[1]) + InterlockedDecrement64((LONG64*)&(pCounters->egressVSwitch_Unknown)); + +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) + + return; +} + +#endif /// DBG + +/** + @private_function="AdvancedPacketInjectionCompletionDataDestroy" + + Purpose:
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_At_(*ppCompletionData, _Pre_ _Notnull_) +_At_(*ppCompletionData, _Post_ _Null_ __drv_freesMem(Pool)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(*ppCompletionData == 0) +VOID AdvancedPacketInjectionCompletionDataDestroy(_Inout_ ADVANCED_PACKET_INJECTION_COMPLETION_DATA** ppCompletionData, + _In_ BOOLEAN override) /* FALSE */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> AdvancedPacketInjectionCompletionDataDestroy()\n"); + +#endif /// DBG + + NT_ASSERT(ppCompletionData); + NT_ASSERT(*ppCompletionData); + + ADVANCED_PACKET_INJECTION_COMPLETION_DATA* pCompletionData = *ppCompletionData; + KIRQL originalIRQL = PASSIVE_LEVEL; + INT32 refCount = -1; + + refCount = InterlockedDecrement((LONG*)&(pCompletionData->refCount)); + if(refCount == 0 || + override) + { + KeAcquireSpinLock(&(pCompletionData->spinLock), + &originalIRQL); + + pCompletionData->refCount = -1; + + if(pCompletionData->pClassifyData) + { + if(!(pCompletionData->performedInline)) + KrnlHlprClassifyDataDestroyLocalCopy(&(pCompletionData->pClassifyData)); + else + { + HLPR_DELETE(pCompletionData->pClassifyData, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + } + } + + if(pCompletionData->pInjectionData) + KrnlHlprInjectionDataDestroy(&(pCompletionData->pInjectionData)); + + if(pCompletionData->pSendParams) + { + HLPR_DELETE_ARRAY(pCompletionData->pSendParams->remoteAddress, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + + HLPR_DELETE(pCompletionData->pSendParams, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + } + + KrnlHlprNBLDestroyNew(0, + &(pCompletionData->pAllocatedMDL), + &(pCompletionData->pAllocatedBuffer)); + + KeReleaseSpinLock(&(pCompletionData->spinLock), + originalIRQL); + + HLPR_DELETE(*ppCompletionData, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- AdvancedPacketInjectionCompletionDataDestroy()\n"); + +#endif /// DBG + + return; +} + +/** + @completion_function="CompleteAdvancedPacketInjection" + + Purpose: Cleanup injection objects and memory.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-Us/Library/Windows/Hardware/FF545018.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI CompleteAdvancedPacketInjection(_In_ VOID* pContext, + _Inout_ NET_BUFFER_LIST* pNetBufferList, + _In_ BOOLEAN dispatchLevel) +{ + NT_ASSERT(pContext); + NT_ASSERT(((ADVANCED_PACKET_INJECTION_COMPLETION_DATA*)pContext)->pInjectionData); + NT_ASSERT(((ADVANCED_PACKET_INJECTION_COMPLETION_DATA*)pContext)->pClassifyData); + NT_ASSERT(((ADVANCED_PACKET_INJECTION_COMPLETION_DATA*)pContext)->pClassifyData->pClassifyValues); + NT_ASSERT(((ADVANCED_PACKET_INJECTION_COMPLETION_DATA*)pContext)->pClassifyData->pClassifyOut); + NT_ASSERT(((ADVANCED_PACKET_INJECTION_COMPLETION_DATA*)pContext)->pClassifyData->pFilter); + NT_ASSERT(((ADVANCED_PACKET_INJECTION_COMPLETION_DATA*)pContext)->pAllocatedMDL); + NT_ASSERT(((ADVANCED_PACKET_INJECTION_COMPLETION_DATA*)pContext)->pAllocatedBuffer); + NT_ASSERT(pNetBufferList); + NT_ASSERT(NT_SUCCESS(pNetBufferList->Status)); + + UNREFERENCED_PARAMETER(dispatchLevel); + + NTSTATUS status = pNetBufferList->Status; + ADVANCED_PACKET_INJECTION_COMPLETION_DATA* pCompletionData = (ADVANCED_PACKET_INJECTION_COMPLETION_DATA*)pContext; + UINT32 layerID = pCompletionData->pClassifyData->pClassifyValues->layerId; + UINT64 filterID = pCompletionData->pClassifyData->pFilter->filterId; + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> CompleteAdvancedPacketInjection() [Layer: %s][FilterID: %#I64x][NBL->Status: %#x]", + KrnlHlprFwpsLayerIDToString(layerID), + filterID, + status); + + if(status != STATUS_SUCCESS) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! CompleteAdvancedPacketInjection() [status: %#x]\n", + status); + + FwpsFreeNetBufferList(pNetBufferList); + +#if DBG + + AdvancedPacketInjectionCountersIncrement(pCompletionData->pInjectionData->injectionHandle, + &g_apiTotalCompletions); + + AdvancedPacketInjectionCountersDecrement(pCompletionData->pInjectionData->injectionHandle, + &g_apiOutstandingNewNBLs); + +#endif // DBG + + AdvancedPacketInjectionCompletionDataDestroy(&pCompletionData); + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- CompleteAdvancedPacketInjection() [Layer: %s][FilterID: %#I64x][NBL->Status: %#x]", + KrnlHlprFwpsLayerIDToString(layerID), + filterID, + status); + + return; +} diff --git a/network/trans/WFPSampler/sys/CompletionFunctions_AdvancedPacketInjectionCallouts.h b/network/trans/WFPSampler/sys/CompletionFunctions_AdvancedPacketInjectionCallouts.h new file mode 100644 index 000000000..12822d1dc --- /dev/null +++ b/network/trans/WFPSampler/sys/CompletionFunctions_AdvancedPacketInjectionCallouts.h @@ -0,0 +1,62 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// CompletionFunctions_AdvancedPacketInjectionCallouts.cpp +// +// Abstract: +// This module contains prototypes for WFP Completion functions for injecting packets back into +// the data path using the allocate / block / inject method. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// December 13, 2013 - 1.1 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef COMPLETION_ADVANCED_PACKET_INJECTION_H +#define COMPLETION_ADVANCED_PACKET_INJECTION_H + +typedef struct ADVANCED_PACKET_INJECTION_COMPLETION_DATA_ +{ + KSPIN_LOCK spinLock; + INT32 refCount; + BOOLEAN performedInline; + CLASSIFY_DATA* pClassifyData; + INJECTION_DATA* pInjectionData; + FWPS_TRANSPORT_SEND_PARAMS* pSendParams; + BYTE* pAllocatedBuffer; + PMDL pAllocatedMDL; +}ADVANCED_PACKET_INJECTION_COMPLETION_DATA, *PADVANCED_PACKET_INJECTION_COMPLETION_DATA; + +#if DBG + +extern INJECTION_COUNTERS g_apiTotalCompletions; + +VOID AdvancedPacketInjectionCountersDecrement(_In_ HANDLE injectionHandle, + _Inout_ INJECTION_COUNTERS* pCounters); + +#endif /// DBG + +_At_(*ppCompletionData, _Pre_ _Notnull_) +_At_(*ppCompletionData, _Post_ _Null_ __drv_freesMem(Pool)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(*ppCompletionData == 0) +VOID AdvancedPacketInjectionCompletionDataDestroy(_Inout_ ADVANCED_PACKET_INJECTION_COMPLETION_DATA** ppCompletionData, + _In_ BOOLEAN override = FALSE); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI CompleteAdvancedPacketInjection(_In_ VOID* pContext, + _Inout_ NET_BUFFER_LIST* pNetBufferList, + _In_ BOOLEAN dispatchLevel); + +#endif /// COMPLETION_ADVANCED_PACKET_INJECTION_H diff --git a/network/trans/WFPSampler/sys/CompletionFunctions_BasicPacketInjectionCallouts.cpp b/network/trans/WFPSampler/sys/CompletionFunctions_BasicPacketInjectionCallouts.cpp new file mode 100644 index 000000000..3b8032fab --- /dev/null +++ b/network/trans/WFPSampler/sys/CompletionFunctions_BasicPacketInjectionCallouts.cpp @@ -0,0 +1,308 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// CompletionFunctions_BasicPacketInjectionCallouts.cpp +// +// Abstract: +// This module contains WFP Completion functions for injecting packets back into the data path +// using the clone / block / inject method. +// +// Naming Convention: +// +// +// +// i.e. +// CompleteBasicPacketInjection +// +// +// Complete - Function is an FWPS_INJECT_COMPLETE function. +// +// BasicPacketInjection - Function demonstrates the clone / block / inject +// model. +// +// +// +// i.e. +// BasicPacketInjectionCompletionDataDestroy +// +// +// { +// BasicPacketInjectionCompletionData - pertains to BASIC_PACKET_INJECTION_COMPLETION_DATA. +// } +// +// Destroy - Cleans up and frees all memory in the object. +// +// Private Functions: +// BasicPacketInjectionCompletionDataDestroy(), +// +// Public Functions: +// CompleteBasicPacketInjection(), +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Enhance function declaration for IntelliSense, add +// support for multiple injectors, and enhance traces. +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "Framework_WFPSamplerCalloutDriver.h" /// . +#include "CompletionFunctions_BasicPacketInjectionCallouts.tmh" /// $(OBJ_PATH)\$(O)\ + +#if DBG + +INJECTION_COUNTERS g_bpiTotalCompletions = {0}; + +/** + @function="BasicPacketInjectionCountersDecrement" + + Purpose: Deccrement the appropriate counters based on the injection handle.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Desktop/MS683581.aspx
+*/ +VOID BasicPacketInjectionCountersDecrement(_In_ HANDLE injectionHandle, + _Inout_ INJECTION_COUNTERS* pCounters) +{ + if(injectionHandle == g_pIPv4InboundNetworkInjectionHandles[0] || + injectionHandle == g_pIPv4InboundNetworkInjectionHandles[1]) + InterlockedDecrement64((LONG64*)&(pCounters->inboundNetwork_IPv4)); + else if(injectionHandle == g_pIPv6InboundNetworkInjectionHandles[0] || + injectionHandle == g_pIPv6InboundNetworkInjectionHandles[1]) + InterlockedDecrement64((LONG64*)&(pCounters->inboundNetwork_IPv6)); + else if(injectionHandle == g_pIPv4OutboundNetworkInjectionHandles[0] || + injectionHandle == g_pIPv4OutboundNetworkInjectionHandles[1]) + InterlockedDecrement64((LONG64*)&(pCounters->outboundNetwork_IPv4)); + else if(injectionHandle == g_pIPv6OutboundNetworkInjectionHandles[0] || + injectionHandle == g_pIPv6OutboundNetworkInjectionHandles[1]) + InterlockedDecrement64((LONG64*)&(pCounters->outboundNetwork_IPv6)); + else if(injectionHandle == g_pIPv4InboundForwardInjectionHandles[0] || + injectionHandle == g_pIPv4InboundForwardInjectionHandles[1]) + InterlockedDecrement64((LONG64*)&(pCounters->inboundForward_IPv4)); + else if(injectionHandle == g_pIPv6InboundForwardInjectionHandles[0] || + injectionHandle == g_pIPv6InboundForwardInjectionHandles[1]) + InterlockedDecrement64((LONG64*)&(pCounters->inboundForward_IPv6)); + else if(injectionHandle == g_pIPv4OutboundForwardInjectionHandles[0] || + injectionHandle == g_pIPv4OutboundForwardInjectionHandles[1]) + InterlockedDecrement64((LONG64*)&(pCounters->outboundForward_IPv4)); + else if(injectionHandle == g_pIPv6OutboundForwardInjectionHandles[0] || + injectionHandle == g_pIPv6OutboundForwardInjectionHandles[1]) + InterlockedDecrement64((LONG64*)&(pCounters->outboundForward_IPv6)); + else if(injectionHandle == g_pIPv4InboundTransportInjectionHandles[0] || + injectionHandle == g_pIPv4InboundTransportInjectionHandles[1]) + InterlockedDecrement64((LONG64*)&(pCounters->inboundTransport_IPv4)); + else if(injectionHandle == g_pIPv6InboundTransportInjectionHandles[0] || + injectionHandle == g_pIPv6InboundTransportInjectionHandles[1]) + InterlockedDecrement64((LONG64*)&(pCounters->inboundTransport_IPv6)); + else if(injectionHandle == g_pIPv4OutboundTransportInjectionHandles[0] || + injectionHandle == g_pIPv4OutboundTransportInjectionHandles[1]) + InterlockedDecrement64((LONG64*)&(pCounters->outboundTransport_IPv4)); + else if(injectionHandle == g_pIPv6OutboundTransportInjectionHandles[0] || + injectionHandle == g_pIPv6OutboundTransportInjectionHandles[1]) + InterlockedDecrement64((LONG64*)&(pCounters->outboundTransport_IPv6)); + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + else if(injectionHandle == g_pIPv4InboundMACInjectionHandles[0] || + injectionHandle == g_pIPv4InboundMACInjectionHandles[1]) + InterlockedDecrement64((LONG64*)&(pCounters->inboundMAC_IPv4)); + else if(injectionHandle == g_pIPv6InboundMACInjectionHandles[0] || + injectionHandle == g_pIPv6InboundMACInjectionHandles[1]) + InterlockedDecrement64((LONG64*)&(pCounters->inboundMAC_IPv6)); + else if(injectionHandle == g_pInboundMACInjectionHandles[0] || + injectionHandle == g_pInboundMACInjectionHandles[1]) + InterlockedDecrement64((LONG64*)&(pCounters->inboundMAC_Unknown)); + else if(injectionHandle == g_pIPv4OutboundMACInjectionHandles[0] || + injectionHandle == g_pIPv4OutboundMACInjectionHandles[1]) + InterlockedDecrement64((LONG64*)&(pCounters->outboundMAC_IPv4)); + else if(injectionHandle == g_pIPv6OutboundMACInjectionHandles[0] || + injectionHandle == g_pIPv6OutboundMACInjectionHandles[1]) + InterlockedDecrement64((LONG64*)&(pCounters->outboundMAC_IPv6)); + else if(injectionHandle == g_pOutboundMACInjectionHandles[0] || + injectionHandle == g_pOutboundMACInjectionHandles[1]) + InterlockedDecrement64((LONG64*)&(pCounters->outboundMAC_Unknown)); + else if(injectionHandle == g_pIPv4IngressVSwitchEthernetInjectionHandles[0] || + injectionHandle == g_pIPv4IngressVSwitchEthernetInjectionHandles[1]) + InterlockedDecrement64((LONG64*)&(pCounters->ingressVSwitch_IPv4)); + else if(injectionHandle == g_pIPv6IngressVSwitchEthernetInjectionHandles[0] || + injectionHandle == g_pIPv6IngressVSwitchEthernetInjectionHandles[1]) + InterlockedDecrement64((LONG64*)&(pCounters->ingressVSwitch_IPv6)); + else if(injectionHandle == g_pIngressVSwitchEthernetInjectionHandles[0] || + injectionHandle == g_pIngressVSwitchEthernetInjectionHandles[1]) + InterlockedDecrement64((LONG64*)&(pCounters->ingressVSwitch_Unknown)); + else if(injectionHandle == g_pIPv4EgressVSwitchEthernetInjectionHandles[0] || + injectionHandle == g_pIPv4EgressVSwitchEthernetInjectionHandles[1]) + InterlockedDecrement64((LONG64*)&(pCounters->egressVSwitch_IPv4)); + else if(injectionHandle == g_pIPv6EgressVSwitchEthernetInjectionHandles[0] || + injectionHandle == g_pIPv6EgressVSwitchEthernetInjectionHandles[1]) + InterlockedDecrement64((LONG64*)&(pCounters->egressVSwitch_IPv6)); + else if(injectionHandle == g_pEgressVSwitchEthernetInjectionHandles[0] || + injectionHandle == g_pEgressVSwitchEthernetInjectionHandles[1]) + InterlockedDecrement64((LONG64*)&(pCounters->egressVSwitch_Unknown)); + +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) + + return; +} + +#endif /// DBG + +/** + @private_function="BasicPacketInjectionCompletionDataDestroy" + + Purpose:
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_At_(*ppCompletionData, _Pre_ _Notnull_) +_At_(*ppCompletionData, _Post_ _Null_ __drv_freesMem(Pool)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(*ppCompletionData == 0) +VOID BasicPacketInjectionCompletionDataDestroy(_Inout_ BASIC_PACKET_INJECTION_COMPLETION_DATA** ppCompletionData, + _In_ BOOLEAN override) /* FALSE */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> BasicPacketInjectionCompletionDataDestroy()\n"); + +#endif /// DBG + + NT_ASSERT(ppCompletionData); + NT_ASSERT(*ppCompletionData); + + BASIC_PACKET_INJECTION_COMPLETION_DATA* pCompletionData = *ppCompletionData; + KIRQL originalIRQL = PASSIVE_LEVEL; + INT32 refCount = -1; + + refCount = InterlockedDecrement((LONG*)&(pCompletionData->refCount)); + if(refCount == 0 || + override) + { + KeAcquireSpinLock(&(pCompletionData->spinLock), + &originalIRQL); + + pCompletionData->refCount = -1; + + if(pCompletionData->pClassifyData) + { + if(!(pCompletionData->performedInline)) + KrnlHlprClassifyDataDestroyLocalCopy(&(pCompletionData->pClassifyData)); + else + { + HLPR_DELETE(pCompletionData->pClassifyData, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + } + } + + if(pCompletionData->pInjectionData) + KrnlHlprInjectionDataDestroy(&(pCompletionData->pInjectionData)); + + if(pCompletionData->pSendParams) + { + HLPR_DELETE_ARRAY(pCompletionData->pSendParams->remoteAddress, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + + HLPR_DELETE(pCompletionData->pSendParams, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + } + + KeReleaseSpinLock(&(pCompletionData->spinLock), + originalIRQL); + + HLPR_DELETE(*ppCompletionData, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- BasicPacketInjectionCompletionDataDestroy()\n"); + +#endif /// DBG + + return; +} + +/** + @completion_function="CompleteBasicPacketInjection" + + Purpose: Cleanup injection objects and memory.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-Us/Library/Windows/Hardware/FF545018.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI CompleteBasicPacketInjection(_In_ VOID* pContext, + _Inout_ NET_BUFFER_LIST* pNetBufferList, + _In_ BOOLEAN dispatchLevel) +{ + NT_ASSERT(pContext); + NT_ASSERT(((BASIC_PACKET_INJECTION_COMPLETION_DATA*)pContext)->pInjectionData); + NT_ASSERT(((BASIC_PACKET_INJECTION_COMPLETION_DATA*)pContext)->pClassifyData); + NT_ASSERT(((BASIC_PACKET_INJECTION_COMPLETION_DATA*)pContext)->pClassifyData->pClassifyValues); + NT_ASSERT(((BASIC_PACKET_INJECTION_COMPLETION_DATA*)pContext)->pClassifyData->pClassifyOut); + NT_ASSERT(((BASIC_PACKET_INJECTION_COMPLETION_DATA*)pContext)->pClassifyData->pFilter); + NT_ASSERT(pNetBufferList); + NT_ASSERT(NT_SUCCESS(pNetBufferList->Status)); + + UNREFERENCED_PARAMETER(dispatchLevel); + + NTSTATUS status = pNetBufferList->Status; + BASIC_PACKET_INJECTION_COMPLETION_DATA* pCompletionData = (BASIC_PACKET_INJECTION_COMPLETION_DATA*)pContext; + UINT32 layerID = pCompletionData->pClassifyData->pClassifyValues->layerId; + UINT64 filterID = pCompletionData->pClassifyData->pFilter->filterId; + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> CompleteBasicPacketInjection() [Layer: %s][FilterID: %#I64x][NBL->Status: %#x]", + KrnlHlprFwpsLayerIDToString(layerID), + filterID, + status); + + if(status != STATUS_SUCCESS) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! CompleteBasicPacketInjection() [status: %#x]\n", + status); + + FwpsFreeCloneNetBufferList(pNetBufferList, + 0); + +#if DBG + + BasicPacketInjectionCountersIncrement(pCompletionData->pInjectionData->injectionHandle, + &g_bpiTotalCompletions); + + BasicPacketInjectionCountersDecrement(pCompletionData->pInjectionData->injectionHandle, + &g_bpiOutstandingNBLClones); + +#endif // DBG + + BasicPacketInjectionCompletionDataDestroy(&pCompletionData); + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- CompleteBasicPacketInjection() [Layer: %s][FilterID: %#I64x][NBL->Status: %#x]", + KrnlHlprFwpsLayerIDToString(layerID), + filterID, + status); + + return; +} diff --git a/network/trans/WFPSampler/sys/CompletionFunctions_BasicPacketInjectionCallouts.h b/network/trans/WFPSampler/sys/CompletionFunctions_BasicPacketInjectionCallouts.h new file mode 100644 index 000000000..77694a568 --- /dev/null +++ b/network/trans/WFPSampler/sys/CompletionFunctions_BasicPacketInjectionCallouts.h @@ -0,0 +1,61 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// CompletionFunctions_BasicPacketInjectionCallouts.cpp +// +// Abstract: +// This module contains prototypes for WFP Completion functions for injecting packets back into +// the data path using the clone / block / inject method. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Enhance function declaration for IntelliSense +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef COMPLETION_BASIC_PACKET_INJECTION_H +#define COMPLETION_BASIC_PACKET_INJECTION_H + +typedef struct BASIC_PACKET_INJECTION_COMPLETION_DATA_ +{ + KSPIN_LOCK spinLock; + INT32 refCount; + BOOLEAN performedInline; + CLASSIFY_DATA* pClassifyData; + INJECTION_DATA* pInjectionData; + FWPS_TRANSPORT_SEND_PARAMS* pSendParams; +}BASIC_PACKET_INJECTION_COMPLETION_DATA, *PBASIC_PACKET_INJECTION_COMPLETION_DATA; + +#if DBG + +extern INJECTION_COUNTERS g_bpiTotalCompletions; + +VOID BasicPacketInjectionCountersDecrement(_In_ HANDLE injectionHandle, + _Inout_ INJECTION_COUNTERS* pCounters); + +#endif /// DBG + +_At_(*ppCompletionData, _Pre_ _Notnull_) +_At_(*ppCompletionData, _Post_ _Null_ __drv_freesMem(Pool)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(*ppCompletionData == 0) +VOID BasicPacketInjectionCompletionDataDestroy(_Inout_ BASIC_PACKET_INJECTION_COMPLETION_DATA** ppCompletionData, + _In_ BOOLEAN override = FALSE); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI CompleteBasicPacketInjection(_In_ VOID* pContext, + _Inout_ NET_BUFFER_LIST* pNetBufferList, + _In_ BOOLEAN dispatchLevel); + +#endif /// COMPLETION_BASIC_PACKET_INJECTION_H diff --git a/network/trans/WFPSampler/sys/CompletionFunctions_BasicPacketModificationCallouts.cpp b/network/trans/WFPSampler/sys/CompletionFunctions_BasicPacketModificationCallouts.cpp new file mode 100644 index 000000000..ef51c44b1 --- /dev/null +++ b/network/trans/WFPSampler/sys/CompletionFunctions_BasicPacketModificationCallouts.cpp @@ -0,0 +1,200 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// CompletionFunctions_BasicPacketInjectionCallouts.cpp +// +// Abstract: +// This module contains WFP Completion functions for injecting packets back into the data path +// using the clone / block / inject method for modified NBLs. +// +// Naming Convention: +// +// +// +// i.e. +// CompleteBasicPacketModification +// +// +// Complete - Function is an FWPS_INJECT_COMPLETE function. +// +// BasicPacketModification - Function demonstrates the clone / block / inject +// model for modified NBLs. +// +// +// +// i.e. +// BasicPacketModificationCompletionDataDestroy +// +// +// { +// BasicPacketModificationCompletionData - pertains to +// BASIC_PACKET_MODIFICATION_COMPLETION_DATA. +// } +// +// Destroy - Cleans up and frees all memory in the object. +// +// Private Functions: +// BasicPacketModificationCompletionDataDestroy(), +// +// Public Functions: +// CompleteBasicPacketModification(), +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Enhance function declaration for IntelliSense and +// enhance traces. +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "Framework_WFPSamplerCalloutDriver.h" /// . +#include "CompletionFunctions_BasicPacketModificationCallouts.tmh" /// $(OBJ_PATH)\$(O)\ + +/** + @private_function="BasicPacketModificationCompletionDataDestroy" + + Purpose:
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_At_(*ppCompletionData, _Pre_ _Notnull_) +_At_(*ppCompletionData, _Post_ _Null_ __drv_freesMem(Pool)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(*ppCompletionData == 0) +VOID BasicPacketModificationCompletionDataDestroy(_Inout_ BASIC_PACKET_MODIFICATION_COMPLETION_DATA** ppCompletionData, + _In_ BOOLEAN override) /* FALSE */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> BasicPacketModificationCompletionDataDestroy()\n"); + +#endif /// DBG + + NT_ASSERT(ppCompletionData); + NT_ASSERT(*ppCompletionData); + + BASIC_PACKET_MODIFICATION_COMPLETION_DATA* pCompletionData = *ppCompletionData; + KIRQL originalIRQL = PASSIVE_LEVEL; + INT32 refCount = -1; + + refCount = InterlockedDecrement((LONG*)&(pCompletionData->refCount)); + if(refCount == 0 || + override) + { + KeAcquireSpinLock(&(pCompletionData->spinLock), + &originalIRQL); + + pCompletionData->refCount = -1; + + if(pCompletionData->pClassifyData) + { + if(!(pCompletionData->performedInline)) + KrnlHlprClassifyDataDestroyLocalCopy(&(pCompletionData->pClassifyData)); + else + { + HLPR_DELETE(pCompletionData->pClassifyData, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + } + } + + if(pCompletionData->pInjectionData) + KrnlHlprInjectionDataDestroy(&(pCompletionData->pInjectionData)); + + if(pCompletionData->pSendParams) + { + HLPR_DELETE_ARRAY(pCompletionData->pSendParams->remoteAddress, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + + HLPR_DELETE(pCompletionData->pSendParams, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + } + + KeReleaseSpinLock(&(pCompletionData->spinLock), + originalIRQL); + + HLPR_DELETE(*ppCompletionData, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- BasicPacketModificationCompletionDataDestroy()\n"); + +#endif /// DBG + + return; +} + +/** + @completion_function="CompleteBasicPacketModification" + + Purpose:
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-Us/Library/Windows/Hardware/FF545018.aspx
+ HTTP://MSDN.Microsoft.com/En-Us/Library/Windows/Hardware/FF551170.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI CompleteBasicPacketModification(_In_ VOID* pContext, + _Inout_ NET_BUFFER_LIST* pNetBufferList, + _In_ BOOLEAN dispatchLevel) +{ + NT_ASSERT(pContext); + NT_ASSERT(((BASIC_PACKET_MODIFICATION_COMPLETION_DATA*)pContext)->pClassifyData); + NT_ASSERT(((BASIC_PACKET_MODIFICATION_COMPLETION_DATA*)pContext)->pClassifyData->pClassifyValues); + NT_ASSERT(((BASIC_PACKET_MODIFICATION_COMPLETION_DATA*)pContext)->pClassifyData->pClassifyOut); + NT_ASSERT(((BASIC_PACKET_MODIFICATION_COMPLETION_DATA*)pContext)->pClassifyData->pFilter); + NT_ASSERT(pNetBufferList); + NT_ASSERT(NT_SUCCESS(pNetBufferList->Status)); + + UNREFERENCED_PARAMETER(dispatchLevel); + + NTSTATUS status = pNetBufferList->Status; + BASIC_PACKET_MODIFICATION_COMPLETION_DATA* pCompletionData = (BASIC_PACKET_MODIFICATION_COMPLETION_DATA*)pContext; + UINT32 layerID = pCompletionData->pClassifyData->pClassifyValues->layerId; + UINT64 filterID = pCompletionData->pClassifyData->pFilter->filterId; + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> CompleteBasicPacketModification() [Layer: %s][FilterID: %#I64x][NBL->Status: %#x]", + KrnlHlprFwpsLayerIDToString(layerID), + filterID, + status); + + BasicPacketModificationCompletionDataDestroy(&pCompletionData); + + if(status != STATUS_SUCCESS) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! CompleteBasicPacketModification() [status: %#x]\n", + pNetBufferList->Status); + + FwpsFreeCloneNetBufferList(pNetBufferList, + 0); + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- CompleteBasicPacketModification() [Layer: %s][FilterID: %#I64x][NBL->Status: %#x]", + KrnlHlprFwpsLayerIDToString(layerID), + filterID, + status); + + return; +} diff --git a/network/trans/WFPSampler/sys/CompletionFunctions_BasicPacketModificationCallouts.h b/network/trans/WFPSampler/sys/CompletionFunctions_BasicPacketModificationCallouts.h new file mode 100644 index 000000000..4d497a168 --- /dev/null +++ b/network/trans/WFPSampler/sys/CompletionFunctions_BasicPacketModificationCallouts.h @@ -0,0 +1,52 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// CompletionFunctions_BasicPacketModificationCallouts.cpp +// +// Abstract: +// This module contains prototypes for WFP Completion functions for injecting modified packets +// back into the data path using the clone / block / inject method. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Enhance function declaration for IntelliSense +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef COMPLETION_BASIC_PACKET_MODIFICATION_H +#define COMPLETION_BASIC_PACKET_MODIFICATION_H + +typedef struct BASIC_PACKET_MODIFICATION_COMPLETION_DATA_ +{ + KSPIN_LOCK spinLock; + INT32 refCount; + BOOLEAN performedInline; + CLASSIFY_DATA* pClassifyData; + INJECTION_DATA* pInjectionData; + FWPS_TRANSPORT_SEND_PARAMS* pSendParams; +}BASIC_PACKET_MODIFICATION_COMPLETION_DATA, *PBASIC_PACKET_MODIFICATION_COMPLETION_DATA; + +_At_(*ppCompletionData, _Pre_ _Notnull_) +_At_(*ppCompletionData, _Post_ _Null_ __drv_freesMem(Pool)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(*ppCompletionData == 0) +VOID BasicPacketModificationCompletionDataDestroy(_Inout_ BASIC_PACKET_MODIFICATION_COMPLETION_DATA** ppCompletionData, + _In_ BOOLEAN override = FALSE); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI CompleteBasicPacketModification(_In_ VOID* pContext, + _Inout_ NET_BUFFER_LIST* pNetBufferList, + _In_ BOOLEAN dispatchLevel); + +#endif /// COMPLETION_BASIC_PACKET_MODIFICATION_H \ No newline at end of file diff --git a/network/trans/WFPSampler/sys/CompletionFunctions_BasicStreamInjectionCallouts.cpp b/network/trans/WFPSampler/sys/CompletionFunctions_BasicStreamInjectionCallouts.cpp new file mode 100644 index 000000000..34dcc81d0 --- /dev/null +++ b/network/trans/WFPSampler/sys/CompletionFunctions_BasicStreamInjectionCallouts.cpp @@ -0,0 +1,164 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// CompletionFunctions_BasicStreamInjectionCallouts.cpp +// +// Abstract: +// This module contains WFP Completion functions for injecting packets back into the stream +// using the clone / block / inject method. +// +// Naming Convention: +// +// +// +// i.e. +// CompleteBasicStreamInjection +// +// +// Complete - Function is an FWPS_INJECT_COMPLETE function. +// +// BasicStreamInjection - Function demonstrates the clone / block / inject +// model. +// +// +// +// i.e. +// BasicPacketInjectionsCompletionDataDestroy +// +// +// { +// BasicStreamInjectionCompletionData - pertains to BASIC_STREAM_INJECTION_COMPLETION_DATA. +// } +// +// Destroy - Cleans up and frees all memory in the object. +// +// Private Functions: +// BasicStreamInjectionCompletionDataDestroy(), +// +// Public Functions: +// CompleteBasicStreamInjection(), +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Enhance function declaration for IntelliSense +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "Framework_WFPSamplerCalloutDriver.h" /// . +#include "CompletionFunctions_BasicStreamInjectionCallouts.tmh" /// $(OBJ_PATH)\$(O)\ + +_At_(*ppCompletionData, _Pre_ _Notnull_) +_At_(*ppCompletionData, _Post_ _Null_ __drv_freesMem(Pool)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(*ppCompletionData == 0) +VOID BasicStreamInjectionCompletionDataDestroy(_Inout_ BASIC_STREAM_INJECTION_COMPLETION_DATA** ppCompletionData, + _In_ BOOLEAN override) /* FALSE */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> BasicStreamInjectionCompletionDataDestroy()\n"); + +#endif /// DBG + + NT_ASSERT(ppCompletionData); + NT_ASSERT(*ppCompletionData); + + BASIC_STREAM_INJECTION_COMPLETION_DATA* pCompletionData = *ppCompletionData; + KIRQL originalIRQL = PASSIVE_LEVEL; + + KeAcquireSpinLock(&(pCompletionData->spinLock), + &originalIRQL); + + pCompletionData->refCount--; + + if(pCompletionData->pClassifyData) + { + if(!(pCompletionData->performedInline)) + KrnlHlprClassifyDataDestroyLocalCopy(&(pCompletionData->pClassifyData)); + else + { + HLPR_DELETE(pCompletionData->pClassifyData, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + } + } + + if(pCompletionData->pInjectionData) + KrnlHlprInjectionDataDestroy(&(pCompletionData->pInjectionData)); + + KeReleaseSpinLock(&(pCompletionData->spinLock), + originalIRQL); + + /// Stream indicated each individual NBL from a chain, so wait until the last NBL is indicated + /// before removing the completionData. + if(pCompletionData->refCount == 0 || + override) + { + HLPR_DELETE(*ppCompletionData, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- BasicStreamInjectionCompletionDataDestroy()\n"); + +#endif /// DBG + + return; +} + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI CompleteBasicStreamInjection(_In_ VOID* pContext, + _Inout_ NET_BUFFER_LIST* pNetBufferList, + _In_ BOOLEAN dispatchLevel) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> CompleteBasicStreamInjection()\n"); + +#endif /// DBG + + UNREFERENCED_PARAMETER(dispatchLevel); + + NT_ASSERT(pContext); + NT_ASSERT(pNetBufferList); + NT_ASSERT(NT_SUCCESS(pNetBufferList->Status) || + pNetBufferList->Status == STATUS_CONNECTION_ABORTED); + + if(pNetBufferList->Status != STATUS_SUCCESS) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! CompleteBasicStreamInjection() [status: %#x]\n", + pNetBufferList->Status); + + FwpsFreeCloneNetBufferList(pNetBufferList, + 0); + + BasicStreamInjectionCompletionDataDestroy((BASIC_STREAM_INJECTION_COMPLETION_DATA**)&pContext); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- CompleteBasicStreamInjection()\n"); + +#endif /// DBG + + return; +} diff --git a/network/trans/WFPSampler/sys/CompletionFunctions_BasicStreamInjectionCallouts.h b/network/trans/WFPSampler/sys/CompletionFunctions_BasicStreamInjectionCallouts.h new file mode 100644 index 000000000..bcc9432ec --- /dev/null +++ b/network/trans/WFPSampler/sys/CompletionFunctions_BasicStreamInjectionCallouts.h @@ -0,0 +1,50 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// CompletionFunctions_BasicStreamInjectionCallouts.h +// +// Abstract: +// This module contains WFP Completion functions for injecting packets back into the stream +// using the clone / block / inject method. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Enhance function declaration for IntelliSense +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef COMPLETION_BASIC_STREAM_INJECTION_H +#define COMPLETION_BASIC_STREAM_INJECTION_H + +typedef struct BASIC_STREAM_INJECTION_COMPLETION_DATA_ +{ + KSPIN_LOCK spinLock; + UINT32 refCount; + BOOLEAN performedInline; + CLASSIFY_DATA* pClassifyData; + INJECTION_DATA* pInjectionData; +}BASIC_STREAM_INJECTION_COMPLETION_DATA, *PBASIC_STREAM_INJECTION_COMPLETION_DATA; + +_At_(*ppCompletionData, _Pre_ _Notnull_) +_At_(*ppCompletionData, _Post_ _Null_ __drv_freesMem(Pool)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(*ppCompletionData == 0) +VOID BasicStreamInjectionCompletionDataDestroy(_Inout_ BASIC_STREAM_INJECTION_COMPLETION_DATA** ppCompletionData, + _In_ BOOLEAN override = FALSE); +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI CompleteBasicStreamInjection(_In_ VOID* pContext, + _Inout_ NET_BUFFER_LIST* pNetBufferList, + _In_ BOOLEAN dispatchLevel); + +#endif /// COMPLETION_BASIC_STREAM_INJECTION_H \ No newline at end of file diff --git a/network/trans/WFPSampler/sys/CompletionFunctions_FastPacketInjectionCallouts.cpp b/network/trans/WFPSampler/sys/CompletionFunctions_FastPacketInjectionCallouts.cpp new file mode 100644 index 000000000..0a2dd0876 --- /dev/null +++ b/network/trans/WFPSampler/sys/CompletionFunctions_FastPacketInjectionCallouts.cpp @@ -0,0 +1,79 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// CompletionFunctions_FastPacketInjectionCallouts.cpp +// +// Abstract: +// This module contains WFP Completion functions for packets injected back into the data path +// using the clone / block / inject method. +// +// Naming Convention: +// +// +// +// i.e. +// CompleteFastPacketInjection +// +// +// Complete - Function is an FWPS_INJECT_COMPLETE function. +// +// FastPacketInjection - Function demonstrates the clone / block / inject model in the +// fastest form available (inline, no validation, etc.). +// +// Private Functions: +// +// Public Functions: +// CompleteFastPacketInjection(), +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Enhance function declaration for IntelliSense +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "Framework_WFPSamplerCalloutDriver.h" /// . + +/** + @completion_function="CompleteFastPacketInjection" + + Purpose:
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-Us/Library/Windows/Hardware/FF545018.aspx
+ HTTP://MSDN.Microsoft.com/En-Us/Library/Windows/Hardware/FF551170.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI CompleteFastPacketInjection(_In_ VOID* pContext, + _Inout_ NET_BUFFER_LIST* pNetBufferList, + _In_ BOOLEAN dispatchLevel) +{ + UNREFERENCED_PARAMETER(dispatchLevel); + + NT_ASSERT(NT_SUCCESS(pNetBufferList->Status)); + + FwpsFreeCloneNetBufferList(pNetBufferList, + 0); + + if(pContext) + { + FWPS_TRANSPORT_SEND_PARAMS* pSendParams = (FWPS_TRANSPORT_SEND_PARAMS*)pContext; + + HLPR_DELETE_ARRAY(pSendParams->remoteAddress, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + + HLPR_DELETE(pSendParams, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + } + + return; +} diff --git a/network/trans/WFPSampler/sys/CompletionFunctions_FastPacketInjectionCallouts.h b/network/trans/WFPSampler/sys/CompletionFunctions_FastPacketInjectionCallouts.h new file mode 100644 index 000000000..a4470c694 --- /dev/null +++ b/network/trans/WFPSampler/sys/CompletionFunctions_FastPacketInjectionCallouts.h @@ -0,0 +1,33 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// CompletionFunctions_FastPacketInjectionCallouts.cpp +// +// Abstract: +// This module contains prototypes for WFP Completion functions for packets injected back into +// the data path using the clone / block / inject method. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Enhance function declaration for IntelliSense +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef COMPLETION_FAST_PACKET_INJECTION_H +#define COMPLETION_FAST_PACKET_INJECTION_H + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI CompleteFastPacketInjection(_In_ VOID* pContext, + _Inout_ NET_BUFFER_LIST* pNetBufferList, + _In_ BOOLEAN dispatchLevel); + +#endif /// COMPLETION_FAST_PACKET_INJECTION_H diff --git a/network/trans/WFPSampler/sys/CompletionFunctions_FastStreamInjectionCallouts.cpp b/network/trans/WFPSampler/sys/CompletionFunctions_FastStreamInjectionCallouts.cpp new file mode 100644 index 000000000..8dec0b9a2 --- /dev/null +++ b/network/trans/WFPSampler/sys/CompletionFunctions_FastStreamInjectionCallouts.cpp @@ -0,0 +1,69 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// CompletionFunctions_FastStreamInjectionCallouts.cpp +// +// Abstract: +// This module contains WFP Completion functions for data injected back into the stream using +// the clone / block / inject method. +// +// Naming Convention: +// +// +// +// i.e. +// CompleteFastStreamInjection +// +// +// Complete - Function is an FWPS_INJECT_COMPLETE function. +// +// FastStreamInjection - Function demonstrates the clone / block / inject model in the +// fastest form available (inline, no validation, etc.). +// +// Private Functions: +// +// Public Functions: +// CompleteFastStreamInjection(), +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Enhance function declaration for IntelliSense +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "Framework_WFPSamplerCalloutDriver.h" /// . + +/** + @completion_function="CompleteFastStreamInjection" + + Purpose:
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-Us/Library/Windows/Hardware/FF545018.aspx
+ HTTP://MSDN.Microsoft.com/En-Us/Library/Windows/Hardware/FF551170.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI CompleteFastStreamInjection(_In_ VOID* pContext, + _Inout_ NET_BUFFER_LIST* pNetBufferList, + _In_ BOOLEAN dispatchLevel) +{ + UNREFERENCED_PARAMETER(pContext); + UNREFERENCED_PARAMETER(dispatchLevel); + + NT_ASSERT(NT_SUCCESS(pNetBufferList->Status)); + + FwpsFreeCloneNetBufferList(pNetBufferList, + 0); + + return; +} diff --git a/network/trans/WFPSampler/sys/CompletionFunctions_FastStreamInjectionCallouts.h b/network/trans/WFPSampler/sys/CompletionFunctions_FastStreamInjectionCallouts.h new file mode 100644 index 000000000..0da036710 --- /dev/null +++ b/network/trans/WFPSampler/sys/CompletionFunctions_FastStreamInjectionCallouts.h @@ -0,0 +1,33 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// CompletionFunctions_FastStreamInjectionCallouts.cpp +// +// Abstract: +// This module contains prototypes for WFP Completion functions for data injected back into +// the stream path using the clone / block / inject method. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Enhance function declaration for IntelliSense +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef COMPLETION_FAST_STREAM_INJECTION_H +#define COMPLETION_FAST_STREAM_INJECTION_H + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI CompleteFastStreamInjection(_In_ VOID* pContext, + _Inout_ NET_BUFFER_LIST* pNetBufferList, + _In_ BOOLEAN dispatchLevel); + +#endif /// COMPLETION_FAST_STREAM_INJECTION_H diff --git a/network/trans/WFPSampler/sys/CompletionFunctions_Include.h b/network/trans/WFPSampler/sys/CompletionFunctions_Include.h new file mode 100644 index 000000000..a9979447f --- /dev/null +++ b/network/trans/WFPSampler/sys/CompletionFunctions_Include.h @@ -0,0 +1,35 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// CompletionFunctions_Include.h +// +// Abstract: +// This module contains a central repository of headers which contain prototypes for all of the +// completion functions. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Add CompletionFunctions_AdvancedPacketInjectionCallouts.h +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef COMPLETION_INCLUDE_H +#define COMPLETION_INCLUDE_H + +#include "CompletionFunctions_AdvancedPacketInjectionCallouts.h" /// . +#include "CompletionFunctions_BasicPacketInjectionCallouts.h" /// . +#include "CompletionFunctions_BasicPacketModificationCallouts.h" /// . +#include "CompletionFunctions_BasicStreamInjectionCallouts.h" /// . +#include "CompletionFunctions_FastPacketInjectionCallouts.h" /// . +#include "CompletionFunctions_FastStreamInjectionCallouts.h" /// . +#include "CompletionFunctions_PendAuthorizationCallouts.h" /// . +#include "CompletionFunctions_ProxyCallouts.h" /// . + +#endif /// COMPLETION_INCLUDE_H \ No newline at end of file diff --git a/network/trans/WFPSampler/sys/CompletionFunctions_PendAuthorizationCallouts.cpp b/network/trans/WFPSampler/sys/CompletionFunctions_PendAuthorizationCallouts.cpp new file mode 100644 index 000000000..e48a9ef81 --- /dev/null +++ b/network/trans/WFPSampler/sys/CompletionFunctions_PendAuthorizationCallouts.cpp @@ -0,0 +1,196 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// CompletionFunctions_PendAuthorizationCallouts.cpp +// +// Abstract: +// This module contains WFP Completion functions for pended authorizations that inject data +// back into the data path. +// +// Naming Convention: +// +// +// +// i.e. +// CompleteBasicPacketInjection +// +// +// Complete - Function is an FWPS_INJECT_COMPLETE function. +// +// PendAuthorization - Function demonstrates the clone / block / inject +// model for pended items. +// +// +// +// i.e. +// PendAuthorizationCompletionDataDestroy +// +// +// { +// PendAuthorizationCompletionData - pertains to PEND_AUTHORIZATION_COMPLETION_DATA. +// } +// +// Destroy - Cleans up and frees all memory in the object. +// +// Private Functions: +// PendAuthorizationCompletionDataDestroy(), +// +// Public Functions: +// CompletePendAuthorization(), +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Enhance function declaration for IntelliSense and +// enhance traces. +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "Framework_WFPSamplerCalloutDriver.h" /// . +#include "CompletionFunctions_PendAuthorizationCallouts.tmh" /// $(OBJ_PATH)\$(O)\ + +/** + @private_function="PendAuthorizationCompletionDataDestroy" + + Purpose:
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_At_(*ppCompletionData, _Pre_ _Notnull_) +_At_(*ppCompletionData, _Post_ _Null_ __drv_freesMem(Pool)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(*ppCompletionData == 0) +VOID PendAuthorizationCompletionDataDestroy(_Inout_ PEND_AUTHORIZATION_COMPLETION_DATA** ppCompletionData, + _In_ BOOLEAN override) /* FALSE */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PendAuthorizationCompletionDataDestroy()\n"); + +#endif /// DBG + + NT_ASSERT(ppCompletionData); + NT_ASSERT(*ppCompletionData); + + PEND_AUTHORIZATION_COMPLETION_DATA* pCompletionData = *ppCompletionData; + KIRQL originalIRQL = PASSIVE_LEVEL; + + KeAcquireSpinLock(&(pCompletionData->spinLock), + &originalIRQL); + + pCompletionData->refCount--; + + if(pCompletionData->pClassifyData) + { + if(!(pCompletionData->performedInline)) + KrnlHlprClassifyDataDestroyLocalCopy(&(pCompletionData->pClassifyData)); + else + { + HLPR_DELETE(pCompletionData->pClassifyData, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + } + } + + if(pCompletionData->pInjectionData) + KrnlHlprInjectionDataDestroy(&(pCompletionData->pInjectionData)); + + if(pCompletionData->pSendParams) + { + HLPR_DELETE_ARRAY(pCompletionData->pSendParams->remoteAddress, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + + HLPR_DELETE(pCompletionData->pSendParams, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + } + + KeReleaseSpinLock(&(pCompletionData->spinLock), + originalIRQL); + + if(pCompletionData->refCount == 0 || + override) + { + HLPR_DELETE(*ppCompletionData, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PendAuthorizationCompletionDataDestroy()\n"); + +#endif /// DBG + + return; +} + +/** + @completion_function="CompletePendAuthorization" + + Purpose:
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-Us/Library/Windows/Hardware/FF545018.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI CompletePendAuthorization(_In_ VOID* pContext, + _Inout_ NET_BUFFER_LIST* pNetBufferList, + _In_ BOOLEAN dispatchLevel) +{ + NT_ASSERT(pContext); + NT_ASSERT(((PEND_AUTHORIZATION_COMPLETION_DATA*)pContext)->pClassifyData); + NT_ASSERT(((PEND_AUTHORIZATION_COMPLETION_DATA*)pContext)->pClassifyData->pClassifyValues); + NT_ASSERT(((PEND_AUTHORIZATION_COMPLETION_DATA*)pContext)->pClassifyData->pClassifyOut); + NT_ASSERT(((PEND_AUTHORIZATION_COMPLETION_DATA*)pContext)->pClassifyData->pFilter); + NT_ASSERT(pNetBufferList); + NT_ASSERT(NT_SUCCESS(pNetBufferList->Status)); + + UNREFERENCED_PARAMETER(dispatchLevel); + + NTSTATUS status = pNetBufferList->Status; + PEND_AUTHORIZATION_COMPLETION_DATA* pCompletionData = (PEND_AUTHORIZATION_COMPLETION_DATA*)pContext; + UINT32 layerID = pCompletionData->pClassifyData->pClassifyValues->layerId; + UINT64 filterID = pCompletionData->pClassifyData->pFilter->filterId; + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> CompletePendAuthorization() [Layer: %s][FilterID: %#I64x][NBL->Status: %#x]", + KrnlHlprFwpsLayerIDToString(layerID), + filterID, + status); + + if(status != STATUS_SUCCESS) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! CompletePendAuthorization() [status: %#x]\n", + status); + + FwpsFreeCloneNetBufferList(pNetBufferList, + 0); + + PendAuthorizationCompletionDataDestroy(&pCompletionData); + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- CompletePendAuthorization() [Layer: %s][FilterID: %#I64x][NBL->Status: %#x]", + KrnlHlprFwpsLayerIDToString(layerID), + filterID, + status); + + return; +} diff --git a/network/trans/WFPSampler/sys/CompletionFunctions_PendAuthorizationCallouts.h b/network/trans/WFPSampler/sys/CompletionFunctions_PendAuthorizationCallouts.h new file mode 100644 index 000000000..3f02e2a11 --- /dev/null +++ b/network/trans/WFPSampler/sys/CompletionFunctions_PendAuthorizationCallouts.h @@ -0,0 +1,52 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// CompletionFunctions_PendAuthorizationCallouts.h +// +// Abstract: +// This module contains prototypes for WFP Completion functions for pending authorizations that +// inject data back into the data path. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Enhance function declaration for IntelliSense +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef COMPLETION_PEND_AUTHORIZATION_H +#define COMPLETION_PEND_AUTHORIZATION_H + +typedef struct PEND_AUTHORIZATION_COMPLETION_DATA_ +{ + KSPIN_LOCK spinLock; + UINT32 refCount; + BOOLEAN performedInline; + CLASSIFY_DATA* pClassifyData; + INJECTION_DATA* pInjectionData; + FWPS_TRANSPORT_SEND_PARAMS* pSendParams; +}PEND_AUTHORIZATION_COMPLETION_DATA, *PPEND_AUTHORIZATION_COMPLETION_DATA; + +_At_(*ppCompletionData, _Pre_ _Notnull_) +_At_(*ppCompletionData, _Post_ _Null_ __drv_freesMem(Pool)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(*ppCompletionData == 0) +VOID PendAuthorizationCompletionDataDestroy(_Inout_ PEND_AUTHORIZATION_COMPLETION_DATA** ppCompletionData, + _In_ BOOLEAN override = FALSE); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI CompletePendAuthorization(_In_ VOID* pContext, + _Inout_ NET_BUFFER_LIST* pNetBufferList, + _In_ BOOLEAN dispatchLevel); + +#endif /// COMPLETION_PEND_AUTHORIZATION_H \ No newline at end of file diff --git a/network/trans/WFPSampler/sys/CompletionFunctions_ProxyCallouts.cpp b/network/trans/WFPSampler/sys/CompletionFunctions_ProxyCallouts.cpp new file mode 100644 index 000000000..ec47380ee --- /dev/null +++ b/network/trans/WFPSampler/sys/CompletionFunctions_ProxyCallouts.cpp @@ -0,0 +1,183 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// CompletionFunctions_ProxyCallouts.cpp +// +// Abstract: +// This module contains WFP Completion functions for proxied connections using the injection +// clone / drop / modify /inject model. +// +// Naming Convention: +// +// +// +// i.e. +// CompleteProxyInjection +// +// +// Complete - Function is an FWPS_INJECT_COMPLETE function. +// +// ProxyInjection - Function demonstrates the clone / block / modify / inject +// model for proxying. +// +// +// +// i.e. +// ProxyCompletionDataDestroy +// +// +// { +// ProxyCompletionData - pertains to PROXY_COMPLETION_DATA. +// } +// +// Destroy - Cleans up and frees all memory in the object. +// +// Private Functions: +// ProxyInjectionCompletionDataDestroy(), +// +// Public Functions: +// CompleteProxyInjection(), +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Enhance function declaration for IntelliSense and +// enhance traces. +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "Framework_WFPSamplerCalloutDriver.h" /// . +#include "CompletionFunctions_ProxyCallouts.tmh" /// $(OBJ_PATH)\$(O)\ + +/** + @private_function="ProxyCompletionDataDestroy" + + Purpose:
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_At_(*ppCompletionData, _Pre_ _Notnull_) +_At_(*ppCompletionData, _Post_ _Null_ __drv_freesMem(Pool)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(*ppCompletionData == 0) +VOID ProxyCompletionDataDestroy(_Inout_ PROXY_COMPLETION_DATA** ppCompletionData) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> ProxyCompletionDataDestroy()\n"); + +#endif /// DBG + + NT_ASSERT(ppCompletionData); + NT_ASSERT(*ppCompletionData); + + PROXY_COMPLETION_DATA* pCompletionData = *ppCompletionData; + + + if(pCompletionData->pClassifyData) + { + if(!pCompletionData->performedInline) + KrnlHlprClassifyDataDestroyLocalCopy(&(pCompletionData->pClassifyData)); + else + { + HLPR_DELETE(pCompletionData->pClassifyData, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + } + } + + if(pCompletionData->pInjectionData) + KrnlHlprInjectionDataDestroy(&(pCompletionData->pInjectionData)); + + if(pCompletionData->pSendParams) + { + HLPR_DELETE_ARRAY(pCompletionData->pSendParams->remoteAddress, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + + HLPR_DELETE(pCompletionData->pSendParams, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + } + + HLPR_DELETE(pCompletionData, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- ProxyCompletionDataDestroy()\n"); + +#endif /// DBG + + return; +} + +/** + @completion_function="CompleteProxyInjection" + + Purpose:
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-Us/Library/Windows/Hardware/FF545018.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI CompleteProxyInjection(_In_ VOID* pContext, + _Inout_ NET_BUFFER_LIST* pNetBufferList, + _In_ BOOLEAN dispatchLevel) +{ + NT_ASSERT(pContext); + NT_ASSERT(((PROXY_COMPLETION_DATA*)pContext)->pClassifyData); + NT_ASSERT(((PROXY_COMPLETION_DATA*)pContext)->pClassifyData->pClassifyValues); + NT_ASSERT(((PROXY_COMPLETION_DATA*)pContext)->pClassifyData->pClassifyOut); + NT_ASSERT(((PROXY_COMPLETION_DATA*)pContext)->pClassifyData->pFilter); + NT_ASSERT(pNetBufferList); + NT_ASSERT(NT_SUCCESS(pNetBufferList->Status)); + + UNREFERENCED_PARAMETER(dispatchLevel); + + NTSTATUS status = pNetBufferList->Status; + PROXY_COMPLETION_DATA* pCompletionData = (PROXY_COMPLETION_DATA*)pContext; + UINT32 layerID = pCompletionData->pClassifyData->pClassifyValues->layerId; + UINT64 filterID = pCompletionData->pClassifyData->pFilter->filterId; + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> CompleteProxyInjection() [Layer: %s][FilterID: %#I64x][NBL->Status: %#x]", + KrnlHlprFwpsLayerIDToString(layerID), + filterID, + status); + + if(status != STATUS_SUCCESS) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! CompleteProxyInjection() [status: %#x]\n", + status); + + FwpsFreeCloneNetBufferList(pNetBufferList, + 0); + + ProxyCompletionDataDestroy(&pCompletionData); + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- CompleteProxyInjection() [Layer: %s][FilterID: %#I64x][NBL->Status: %#x]", + KrnlHlprFwpsLayerIDToString(layerID), + filterID, + status); + + return; +} diff --git a/network/trans/WFPSampler/sys/CompletionFunctions_ProxyCallouts.h b/network/trans/WFPSampler/sys/CompletionFunctions_ProxyCallouts.h new file mode 100644 index 000000000..a3d158cb2 --- /dev/null +++ b/network/trans/WFPSampler/sys/CompletionFunctions_ProxyCallouts.h @@ -0,0 +1,54 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// CompletionFunctions_ProxyCallouts.cpp +// +// Abstract: +// This module contains prototypes of WFP Completion functions for proxied connections using +// the injection clone / drop / modify /inject model. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Enhance function declaration for IntelliSense +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef COMPLETION_PROXY_CONNECTION_H +#define COMPLETION_PROXY_CONNECTION_H + +typedef struct PROXY_COMPLETION_DATA_ +{ + BOOLEAN performedInline; + CLASSIFY_DATA* pClassifyData; + union + { + INJECTION_DATA* pInjectionData; + REDIRECT_DATA* pRedirectData; + }; + PC_PROXY_DATA* pProxyData; + FWPS_TRANSPORT_SEND_PARAMS* pSendParams; +}PROXY_COMPLETION_DATA, *PPROXY_COMPLETION_DATA; + +_At_(*ppCompletionData, _Pre_ _Notnull_) +_At_(*ppCompletionData, _Post_ _Null_ __drv_freesMem(Pool)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(*ppCompletionData == 0) +VOID ProxyCompletionDataDestroy(_Inout_ PROXY_COMPLETION_DATA** ppCompletionData); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI CompleteProxyInjection(_In_ VOID* pContext, + _Inout_ NET_BUFFER_LIST* pNetBufferList, + _In_ BOOLEAN dispatchLevel); + +#endif /// COMPLETION_PROXY_CONNECTION_H \ No newline at end of file diff --git a/network/trans/WFPSampler/sys/Framework_Events.cpp b/network/trans/WFPSampler/sys/Framework_Events.cpp new file mode 100644 index 000000000..cd2290f6f --- /dev/null +++ b/network/trans/WFPSampler/sys/Framework_Events.cpp @@ -0,0 +1,300 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// Framework_Events.cpp +// +// Abstract: +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Add support for multiple injectors and redirectors +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "Framework_WFPSamplerCalloutDriver.h" /// . +#include "Framework_Include.h" /// . +#include "Framework_Events.tmh" /// $(OBJ_PATH)\$(O)\ + +/** + @framework_function="EventDriverUnload" + + Purpose: Callback function responding to Driver Unload Events.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF541694.aspx
+*/ +_IRQL_requires_(PASSIVE_LEVEL) +_IRQL_requires_same_ +_Function_class_(EVT_WDF_DRIVER_UNLOAD) +VOID EventDriverUnload(_In_ WDFDRIVER wdfDriver) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> EventDriverUnload()\n"); + +#endif /// DBG + + UNREFERENCED_PARAMETER(wdfDriver); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- EventDriverUnload()\n"); + +#endif /// DBG + + WPP_CLEANUP(wdfDriver); + + return; +} + +/** + @framework_function="EventCleanupDriverObject" + + Purpose: Callback function responding to Driver Cleanup Events.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF540840.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(PASSIVE_LEVEL) +_IRQL_requires_same_ +_Function_class_(EVT_WDF_OBJECT_CONTEXT_CLEANUP) +VOID EventCleanupDriverObject(_In_ WDFOBJECT driverObject) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> EventCleanupDriverObject()\n"); + + +#endif /// DBG + + UNREFERENCED_PARAMETER(driverObject); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- EventCleanupDriverObject()\n"); + +#endif /// DBG + + return; +} + +/** + @framework_function="EventCleanupDeviceObject" + + Purpose: Callback function responding to Object Cleanup Events.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF540840.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(PASSIVE_LEVEL) +_IRQL_requires_same_ +_Function_class_(EVT_WDF_OBJECT_CONTEXT_CLEANUP) +VOID EventCleanupDeviceObject(_In_ WDFOBJECT deviceObject) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> EventCleanupDeviceObject()\n"); + +#endif /// DBG + + UNREFERENCED_PARAMETER(deviceObject); + + KrnlHlprExposedCalloutsUnregister(); + + for(UINT32 index = 0; + index <= UNIVERSAL_INDEX; + index++) + { + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + if(g_WFPSamplerDeviceData.ppRedirectionHandles[index]) + KrnlHlprFwpsRedirectHandleDestroy(g_WFPSamplerDeviceData.ppRedirectionHandles[index]); + +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) + + if(g_WFPSamplerDeviceData.ppOutboundInjectionHandles[index]) + KrnlHlprInjectionHandleDataDestroy(&(g_WFPSamplerDeviceData.ppOutboundInjectionHandles[index])); + + if(g_WFPSamplerDeviceData.ppInboundInjectionHandles[index]) + KrnlHlprInjectionHandleDataDestroy(&(g_WFPSamplerDeviceData.ppInboundInjectionHandles[index])); + + if(g_WFPSamplerDeviceData.ppIPv6OutboundInjectionHandles[index]) + KrnlHlprInjectionHandleDataDestroy(&(g_WFPSamplerDeviceData.ppIPv6OutboundInjectionHandles[index])); + + if(g_WFPSamplerDeviceData.ppIPv6InboundInjectionHandles[index]) + KrnlHlprInjectionHandleDataDestroy(&(g_WFPSamplerDeviceData.ppIPv6InboundInjectionHandles[index])); + + if(g_WFPSamplerDeviceData.ppIPv4OutboundInjectionHandles[index]) + KrnlHlprInjectionHandleDataDestroy(&(g_WFPSamplerDeviceData.ppIPv4OutboundInjectionHandles[index])); + + if(g_WFPSamplerDeviceData.ppIPv4InboundInjectionHandles[index]) + KrnlHlprInjectionHandleDataDestroy(&(g_WFPSamplerDeviceData.ppIPv4InboundInjectionHandles[index])); + } + + if(g_WFPSamplerDeviceData.pEngineHandle) + KrnlHlprFwpmSessionDestroyEngineHandle(&(g_WFPSamplerDeviceData.pEngineHandle)); + + FwpmBfeStateUnsubscribeChanges(g_bfeSubscriptionHandle); + + UnregisterPowerStateChangeCallback(&g_deviceExtension); + + if(g_pNDISPoolData) + KrnlHlprNDISPoolDataDestroy(&g_pNDISPoolData); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- EventCleanupDeviceObject()\n"); + +#endif /// DBG + + return; +} + +/** + @framework_function="EventIODeviceControl" + + Purpose: Callback function responding to IO Control Events.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF541758.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Function_class_(EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL) +VOID EventIODeviceControl(_In_ WDFQUEUE wdfQueue, + _In_ WDFREQUEST wdfRequest, + _In_ size_t outputBufferLength, + _In_ size_t inputBufferLength, + _In_ ULONG ioControlCode) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> EventIODeviceControl()\n"); + +#endif /// DBG + + UNREFERENCED_PARAMETER(wdfQueue); + UNREFERENCED_PARAMETER(wdfRequest); + UNREFERENCED_PARAMETER(outputBufferLength); + UNREFERENCED_PARAMETER(inputBufferLength); + UNREFERENCED_PARAMETER(ioControlCode); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- EventIODeviceControl()\n"); + +#endif /// DBG + + return; +} + +/** + @framework_function="EventIORead" + + Purpose: Callback function responding to IO Read Events.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF541776.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Function_class_(EVT_WDF_IO_QUEUE_IO_READ) +VOID EventIORead(_In_ WDFQUEUE wdfQueue, + _In_ WDFREQUEST wdfRequest, + _In_ size_t length) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> EventIORead()\n"); + +#endif /// DBG + + UNREFERENCED_PARAMETER(wdfQueue); + UNREFERENCED_PARAMETER(wdfRequest); + UNREFERENCED_PARAMETER(length); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- EventIORead()\n"); + +#endif /// DBG + + return; +} + +/** + @framework_function="EventIOWrite" + + Purpose: Callback function responding to IO Write Events.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF541813.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Function_class_(EVT_WDF_IO_QUEUE_IO_WRITE) +VOID EventIOWrite(_In_ WDFQUEUE wdfQueue, + _In_ WDFREQUEST wdfRequest, + _In_ size_t length) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> EventIOWrite()\n"); + +#endif /// DBG + + UNREFERENCED_PARAMETER(wdfQueue); + UNREFERENCED_PARAMETER(wdfRequest); + UNREFERENCED_PARAMETER(length); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- EventIOWrite()\n"); + +#endif /// DBG + + return; +} diff --git a/network/trans/WFPSampler/sys/Framework_Events.h b/network/trans/WFPSampler/sys/Framework_Events.h new file mode 100644 index 000000000..2858656d9 --- /dev/null +++ b/network/trans/WFPSampler/sys/Framework_Events.h @@ -0,0 +1,66 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// Framework_Events.h +// +// Abstract: +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef FRAMEWORK_EVENTS_H +#define FRAMEWORK_EVENTS_H + +_IRQL_requires_(PASSIVE_LEVEL) +_IRQL_requires_same_ +_Function_class_(EVT_WDF_DRIVER_UNLOAD) +VOID EventDriverUnload(_In_ WDFDRIVER wdfDriver); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(PASSIVE_LEVEL) +_IRQL_requires_same_ +_Function_class_(EVT_WDF_OBJECT_CONTEXT_CLEANUP) +VOID EventCleanupDriverObject(_In_ WDFOBJECT driverObject); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(PASSIVE_LEVEL) +_IRQL_requires_same_ +_Function_class_(EVT_WDF_OBJECT_CONTEXT_CLEANUP) +VOID EventCleanupDeviceObject(_In_ WDFOBJECT deviceObject); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Function_class_(EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL) +VOID EventIODeviceControl(_In_ WDFQUEUE wdfQueue, + _In_ WDFREQUEST wdfRequest, + _In_ size_t outputBufferLength, + _In_ size_t inputBufferLength, + _In_ ULONG ioControlCode); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Function_class_(EVT_WDF_IO_QUEUE_IO_READ) +VOID EventIORead(_In_ WDFQUEUE wdfQueue, + _In_ WDFREQUEST wdfRequest, + _In_ size_t length); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Function_class_(EVT_WDF_IO_QUEUE_IO_WRITE) +VOID EventIOWrite(_In_ WDFQUEUE wdfQueue, + _In_ WDFREQUEST wdfRequest, + _In_ size_t length); + +#endif /// FRAMEWORK_EVENTS_H \ No newline at end of file diff --git a/network/trans/WFPSampler/sys/Framework_Include.h b/network/trans/WFPSampler/sys/Framework_Include.h new file mode 100644 index 000000000..4a696b148 --- /dev/null +++ b/network/trans/WFPSampler/sys/Framework_Include.h @@ -0,0 +1,28 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// Framework_Include.h +// +// Abstract: +// This module contains a central repository of headers which contain prototypes for all of the +// driver's framework functions. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef FRAMEWORK_INCLUDE_H +#define FRAMEWORK_INCLUDE_H + +#include "Framework_Events.h" /// . +#include "Framework_PowerStates.h" /// . + +#endif /// FRAMEWORK_INCLUDE_H \ No newline at end of file diff --git a/network/trans/WFPSampler/sys/Framework_PowerStates.cpp b/network/trans/WFPSampler/sys/Framework_PowerStates.cpp new file mode 100644 index 000000000..ed67b11f8 --- /dev/null +++ b/network/trans/WFPSampler/sys/Framework_PowerStates.cpp @@ -0,0 +1,210 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// Framework_PowerStates.cpp +// +// Abstract: +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "Framework_WFPSamplerCalloutDriver.h" /// . +#include "Framework_PowerStates.tmh" /// $(OBJ_PATH)\$(O)\ + +/** + @framework_function="PowerStateCallback" + + Purpose: Callback which handles various power state transitions for the callout driver.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_IRQL_requires_same_ +_Function_class_(CALLBACK_FUNCTION) +VOID PowerStateCallback(_In_ VOID* pCallbackContext, + _In_ VOID* pPowerStateEvent, + _In_ VOID* pEventSpecifics) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PowerStateCallback()\n"); + +#endif /// DBG + + UNREFERENCED_PARAMETER(pCallbackContext); + + if(pPowerStateEvent == (VOID*)PO_CB_SYSTEM_STATE_LOCK) + { + NTSTATUS status = STATUS_SUCCESS; + + if(pEventSpecifics) + { + /// entering the ON state (S0), so return operation to normal + if(g_calloutsRegistered == FALSE) + { + status = KrnlHlprExposedCalloutsRegister(); + HLPR_BAIL_ON_FAILURE(status); + + g_calloutsRegistered = TRUE; + } + } + else + { + /// leaving the ON state (S0) to sleep (S1/S2/S3) or hibernate (S4) + /// Unregister the callouts so no more injection will take place. By default, the filters + /// invoking the callouts will return block. + if(g_calloutsRegistered == TRUE) + { + status = KrnlHlprExposedCalloutsUnregister(); + HLPR_BAIL_ON_FAILURE(status); + + g_calloutsRegistered = FALSE; + } + } + } + + HLPR_BAIL_LABEL: + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PowerStateCallback()\n"); + +#endif /// DBG + + return; +} + +/** + @framework_function="RegisterPowerStateChangeCallback" + + Purpose: Unregister a callback that handled notifications of power state changes.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/FF545649.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/FF547724.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(APC_LEVEL) +_IRQL_requires_same_ +VOID UnregisterPowerStateChangeCallback(_Inout_ DEVICE_EXTENSION* pDeviceExtension) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> UnregisterPowerStateChangeCallback()\n"); + +#endif /// DBG + + NT_ASSERT(pDeviceExtension); + + if(pDeviceExtension->pRegistration) + { + ExUnregisterCallback(pDeviceExtension->pRegistration); + + pDeviceExtension->pRegistration = 0; + } + + if(pDeviceExtension->pCallbackObject) + { + ObDereferenceObject(pDeviceExtension->pCallbackObject); + + pDeviceExtension->pCallbackObject = 0; + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- UnregisterPowerStateChangeCallback()\n"); + +#endif /// DBG + + return; +} + +/** + @framework_function="RegisterPowerStateChangeCallback" + + Purpose: Create and register a callback to handle notifications of power state changes.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/FF544560.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/FF545534.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(APC_LEVEL) +_IRQL_requires_same_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS RegisterPowerStateChangeCallback(_Inout_ DEVICE_EXTENSION* pDeviceExtension) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> RegisterPowerStateChangeCallback()\n"); + +#endif /// DBG + + NT_ASSERT(pDeviceExtension); + + NTSTATUS status = STATUS_SUCCESS; + OBJECT_ATTRIBUTES objectAttributes = {0}; + UNICODE_STRING unicodeString = {0}; + + RtlInitUnicodeString(&unicodeString, + L"\\Callback\\PowerState"); + + InitializeObjectAttributes(&objectAttributes, + &unicodeString, + OBJ_CASE_INSENSITIVE, + 0, + 0); + + status = ExCreateCallback(&(pDeviceExtension->pCallbackObject), + &objectAttributes, + FALSE, /// Do not create as the system should already have done this + TRUE); /// Allow multiple callbacks + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! RegisterPowerStateChangeCallback : ExCreateCallback() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + pDeviceExtension->pRegistration = ExRegisterCallback(pDeviceExtension->pCallbackObject, + PowerStateCallback, + pDeviceExtension); + + HLPR_BAIL_LABEL: + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- RegisterPowerStateChangeCallback() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} diff --git a/network/trans/WFPSampler/sys/Framework_PowerStates.h b/network/trans/WFPSampler/sys/Framework_PowerStates.h new file mode 100644 index 000000000..10ddac58d --- /dev/null +++ b/network/trans/WFPSampler/sys/Framework_PowerStates.h @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// Framework_PowerStates.h +// +// Abstract: +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef FRAMEWORK_POWER_STATES_H +#define FRAMEWORK_POWER_STATES_H + +VOID UnregisterPowerStateChangeCallback(_Inout_ DEVICE_EXTENSION* pDeviceExtension); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(APC_LEVEL) +_IRQL_requires_same_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS RegisterPowerStateChangeCallback(_Inout_ DEVICE_EXTENSION* pDeviceExtension); + +#endif /// FRAMEWORK_POWER_STATES_H \ No newline at end of file diff --git a/network/trans/WFPSampler/sys/Framework_WFPSamplerCalloutDriver.cpp b/network/trans/WFPSampler/sys/Framework_WFPSamplerCalloutDriver.cpp new file mode 100644 index 000000000..88824779e --- /dev/null +++ b/network/trans/WFPSampler/sys/Framework_WFPSamplerCalloutDriver.cpp @@ -0,0 +1,533 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// Framework_WFPSamplerCalloutDriver.cpp +// +// Abstract: +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Add support for multiple injectors and redirectors, and +// add support for serializing asynchronous +// FWPM_LAYER_STREAM injections +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "Framework_WFPSamplerCalloutDriver.h" /// . +#include "Framework_Include.h" /// . +#include "Framework_WFPSamplerCalloutDriver.tmh" /// $(OBJ_PATH)\$(O)\ + +extern "C" +{ + _IRQL_requires_(PASSIVE_LEVEL) + _IRQL_requires_same_ + _Function_class_(DRIVER_INITIALIZE) + DRIVER_INITIALIZE DriverEntry; +}; + +PDEVICE_OBJECT g_pWDMDevice = 0; +NDIS_POOL_DATA* g_pNDISPoolData = 0; +BOOLEAN g_calloutsRegistered = FALSE; +HANDLE g_bfeSubscriptionHandle = 0; +SERIALIZATION_LIST g_bsiSerializationList = {0}; +WFPSAMPLER_DEVICE_DATA g_WFPSamplerDeviceData = {0}; +DEVICE_EXTENSION g_deviceExtension = {0}; +KSPIN_LOCK g_bpeSpinLock; +WDFDRIVER g_WDFDriver; +WDFDEVICE g_WDFDevice; + +/** + @private_function="PrvBasicStreamInjectionSerializationListInitialization" + + Purpose: Initialize Stream Injection's global serailization list.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552160.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551171.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF547799.aspx
+*/ +_Success_(return == STATUS_SUCCESS) +NTSTATUS PrvBasicStreamInjectionSerializationListInitialization() +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PrvBasicStreamInjectionSerializationListInitialization()\n"); + +#endif /// DBG + + NTSTATUS status = STATUS_SUCCESS; + + KeInitializeSpinLock(&(g_bsiSerializationList.spinLock)); + + status = WdfWaitLockCreate(WDF_NO_OBJECT_ATTRIBUTES, + &(g_bsiSerializationList.waitLock)); + HLPR_BAIL_ON_FAILURE(status); + + InitializeListHead(&(g_bsiSerializationList.listHead)); + + g_bsiSerializationList.numEntries = 0; + + HLPR_BAIL_LABEL: + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PrvBasicStreamInjectionSerializationListInitialization() [status: %#x]", + status); + +#endif /// DBG + + return status; +} + +/** + @private_function="PrvFwpmBfeStateSubscribeChanges" + + Purpose: Subscribe to notifications of changes to the BFE service's state.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF550062.aspx
+*/ +_Success_(return == STATUS_SUCCESS) +NTSTATUS PrvFwpmBfeStateSubscribeChanges() +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PrvFwpmBfeStateSubscribeChanges()\n"); + +#endif /// DBG + + NTSTATUS status = STATUS_SUCCESS; + + status = FwpmBfeStateSubscribeChanges(g_pWDMDevice, + SubscriptionBFEStateChangeCallback, + &g_WFPSamplerDeviceData, + &g_bfeSubscriptionHandle); + if(status != STATUS_SUCCESS) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PrvFwpmBfeStateSubscribeChanges : FwpmBfeStateSubscribeChanges() [status: %#x]\n", + status); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PrvFwpmBfeStateSubscribeChanges() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @private_function="PrvWFPSamplerDeviceDataPopulate" + + Purpose: Register the global WFP objects such as:
+ Callouts
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_IRQL_requires_(PASSIVE_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS PrvWFPSamplerFwpObjectsAddGlobal(_In_ WFPSAMPLER_DEVICE_DATA* pDeviceData) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PrvWFPSamplerFwpObjectsAddGlobal()\n"); + +#endif /// DBG + + UNREFERENCED_PARAMETER(pDeviceData); + + NT_ASSERT(pDeviceData); + + NTSTATUS status = STATUS_SUCCESS; + + if(g_calloutsRegistered == FALSE) + { + status = KrnlHlprExposedCalloutsRegister(); + HLPR_BAIL_ON_FAILURE(status); + + g_calloutsRegistered = TRUE; + } + + HLPR_BAIL_LABEL: + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PrvWFPSamplerFwpObjectsAddGlobal() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @private_function="PrvWFPSamplerDeviceDataPopulate" + + Purpose: Populate the deviceData with the BFE engineHandle and various injection handles.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF550061.aspx
+*/ +_IRQL_requires_(PASSIVE_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS PrvWFPSamplerDeviceDataPopulate(_Inout_ WFPSAMPLER_DEVICE_DATA* pDeviceData) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PrvWFPSamplerDeviceDataPopulate()\n"); + +#endif /// DBG + + NT_ASSERT(pDeviceData); + + NTSTATUS status = STATUS_SUCCESS; + + if(FwpmBfeStateGet() == FWPM_SERVICE_RUNNING) + { + status = KrnlHlprFwpmSessionCreateEngineHandle(&(pDeviceData->pEngineHandle)); + HLPR_BAIL_ON_FAILURE(status); + } + + /// There are injection handles for each of the supported sublayers that allow injection. This + /// allows us to do multiple injectors in the layer provided that the callouts are in different + /// sublayers. + for(UINT32 index = 0; + index < MAX_INDEX; + index++) + { + +#pragma warning(push) +#pragma warning(disable: 6388) /// all injection Handles will be 0 initially + + status = KrnlHlprInjectionHandleDataCreate(&(pDeviceData->ppIPv4InboundInjectionHandles[index]), + AF_INET, + FWP_DIRECTION_INBOUND, + index); + HLPR_BAIL_ON_FAILURE(status); + + status = KrnlHlprInjectionHandleDataCreate(&(pDeviceData->ppIPv4OutboundInjectionHandles[index]), + AF_INET, + FWP_DIRECTION_OUTBOUND, + index); + HLPR_BAIL_ON_FAILURE(status); + + status = KrnlHlprInjectionHandleDataCreate(&(pDeviceData->ppIPv6InboundInjectionHandles[index]), + AF_INET6, + FWP_DIRECTION_INBOUND, + index); + HLPR_BAIL_ON_FAILURE(status); + + status = KrnlHlprInjectionHandleDataCreate(&(pDeviceData->ppIPv6OutboundInjectionHandles[index]), + AF_INET6, + FWP_DIRECTION_OUTBOUND, + index); + HLPR_BAIL_ON_FAILURE(status); + + status = KrnlHlprInjectionHandleDataCreate(&(pDeviceData->ppInboundInjectionHandles[index]), + AF_UNSPEC, + FWP_DIRECTION_INBOUND, + index); + HLPR_BAIL_ON_FAILURE(status); + + status = KrnlHlprInjectionHandleDataCreate(&(pDeviceData->ppOutboundInjectionHandles[index]), + AF_UNSPEC, + FWP_DIRECTION_OUTBOUND, + index); + HLPR_BAIL_ON_FAILURE(status); + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + pDeviceData->ppRedirectionHandles[index] = &g_pRedirectionHandles[index]; + + status = KrnlHlprFwpsRedirectHandleCreate(pDeviceData->ppRedirectionHandles[index]); + HLPR_BAIL_ON_FAILURE(status); + +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) + +#pragma warning(pop) + + } + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS) + { + for(UINT32 index = 0; + index < MAX_INDEX; + index++) + { + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + if(pDeviceData->ppRedirectionHandles[index]) + KrnlHlprFwpsRedirectHandleDestroy(pDeviceData->ppRedirectionHandles[index]); + +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) + + + if(pDeviceData->ppOutboundInjectionHandles[index]) + KrnlHlprInjectionHandleDataDestroy(&(pDeviceData->ppOutboundInjectionHandles[index])); + + if(pDeviceData->ppInboundInjectionHandles[index]) + KrnlHlprInjectionHandleDataDestroy(&(pDeviceData->ppInboundInjectionHandles[index])); + + if(pDeviceData->ppIPv6OutboundInjectionHandles[index]) + KrnlHlprInjectionHandleDataDestroy(&(pDeviceData->ppIPv6OutboundInjectionHandles[index])); + + if(pDeviceData->ppIPv6InboundInjectionHandles[index]) + KrnlHlprInjectionHandleDataDestroy(&(pDeviceData->ppIPv6InboundInjectionHandles[index])); + + if(pDeviceData->ppIPv4OutboundInjectionHandles[index]) + KrnlHlprInjectionHandleDataDestroy(&(pDeviceData->ppIPv4OutboundInjectionHandles[index])); + + if(pDeviceData->ppIPv4InboundInjectionHandles[index]) + KrnlHlprInjectionHandleDataDestroy(&(pDeviceData->ppIPv4InboundInjectionHandles[index])); + } + + if(pDeviceData->pEngineHandle && + *(pDeviceData->pEngineHandle)) + KrnlHlprFwpmSessionReleaseHandle(pDeviceData->pEngineHandle); + + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PrvWFPSamplerDeviceDataPopulate()\n"); + +#endif /// DBG + + return status; +} + +/** + @private_function="PrvDriverDeviceAdd" + + Purpose: Add the device and configure initial WFP callout driver settings.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF545841.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF546090.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF545926.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF546942.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF545854.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF546050.aspx
+*/ +_IRQL_requires_(PASSIVE_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS PrvDriverDeviceAdd(_In_ WDFDRIVER* pWDFDriver) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PrvDriverDeviceAdd()\n"); + +#endif /// DBG + + NT_ASSERT(pWDFDriver); + + NTSTATUS status = STATUS_SUCCESS; + PWDFDEVICE_INIT pWDFDeviceInit = 0; + WDF_OBJECT_ATTRIBUTES attributes = {0}; + + WDF_OBJECT_ATTRIBUTES_INIT(&attributes); + + attributes.EvtCleanupCallback = EventCleanupDeviceObject; + + pWDFDeviceInit = WdfControlDeviceInitAllocate(*pWDFDriver, + &SDDL_DEVOBJ_KERNEL_ONLY); + if(pWDFDeviceInit == 0) + { + status = STATUS_UNSUCCESSFUL; + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PrvDriverDeviceAdd : WdfControlDeviceInitAllocate() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + WdfDeviceInitSetDeviceType(pWDFDeviceInit, + FILE_DEVICE_NETWORK); + + status = WdfDeviceCreate(&pWDFDeviceInit, + &attributes, + &g_WDFDevice); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PrvDriverDeviceAdd : WdfDeviceCreate() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + g_pWDMDevice = WdfDeviceWdmGetDeviceObject(g_WDFDevice); + HLPR_BAIL_ON_NULL_POINTER_WITH_STATUS(g_pWDMDevice, + status); + + status = RegisterPowerStateChangeCallback(&g_deviceExtension); + HLPR_BAIL_ON_FAILURE(status); + +#pragma warning(push) +#pragma warning(disable: 6388) /// g_pNDISPoolData will be 0 + + status = KrnlHlprNDISPoolDataCreate(&g_pNDISPoolData); + HLPR_BAIL_ON_FAILURE(status); + +#pragma warning(pop) + + PrvFwpmBfeStateSubscribeChanges(); + + status = PrvWFPSamplerDeviceDataPopulate(&g_WFPSamplerDeviceData); + HLPR_BAIL_ON_FAILURE(status); + + KeInitializeSpinLock(&g_bpeSpinLock); + + status = PrvBasicStreamInjectionSerializationListInitialization(); + HLPR_BAIL_ON_FAILURE(status); + + + status = PrvWFPSamplerFwpObjectsAddGlobal(&g_WFPSamplerDeviceData); + HLPR_BAIL_ON_FAILURE(status); + + WdfControlFinishInitializing(g_WDFDevice); + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS) + { + if(pWDFDeviceInit) + WdfDeviceInitFree(pWDFDeviceInit); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PrvDriverDeviceAdd() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @framework_function="DriverEntry" + + Purpose: Entry point for the driver.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF540807.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF547175.aspx
+*/ +_IRQL_requires_same_ +_Function_class_(DRIVER_INITIALIZE) +NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT pDriverObject, + _In_ PUNICODE_STRING pRegistryPath) +{ + WPP_INIT_TRACING(pDriverObject, + pRegistryPath); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> DriverEntry()\n"); + +#endif /// DBG + + NT_ASSERT(pDriverObject); + NT_ASSERT(pRegistryPath); + + NTSTATUS status = STATUS_SUCCESS; + WDF_DRIVER_CONFIG driverConfig; + WDF_OBJECT_ATTRIBUTES attributes; + + WDF_DRIVER_CONFIG_INIT(&driverConfig, + WDF_NO_EVENT_CALLBACK); + + ExInitializeDriverRuntime(DrvRtPoolNxOptIn); + + driverConfig.DriverInitFlags |= WdfDriverInitNonPnpDriver; + driverConfig.EvtDriverUnload = EventDriverUnload; + + WDF_OBJECT_ATTRIBUTES_INIT(&attributes); + + attributes.EvtCleanupCallback = EventCleanupDriverObject; + + status = WdfDriverCreate(pDriverObject, + pRegistryPath, + &attributes, + &driverConfig, + &g_WDFDriver); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! DriverEntry : WdfDriverCreate() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + status = PrvDriverDeviceAdd(&g_WDFDriver); + HLPR_BAIL_ON_FAILURE(status); + + HLPR_BAIL_LABEL: + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- DriverEntry() [status: %#x]\n", + status); + +#endif /// DBG + + if(status != STATUS_SUCCESS) + WPP_CLEANUP(pDriverObject); + + return status; +} diff --git a/network/trans/WFPSampler/sys/Framework_WFPSamplerCalloutDriver.h b/network/trans/WFPSampler/sys/Framework_WFPSamplerCalloutDriver.h new file mode 100644 index 000000000..52b03284f --- /dev/null +++ b/network/trans/WFPSampler/sys/Framework_WFPSamplerCalloutDriver.h @@ -0,0 +1,95 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// Framework_WFPSamplerCalloutDriver.h +// +// Abstract: +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Add support for multiple injectors and redirectors, and +// add structure for serializing asynchronous +// FWPM_LAYER_STREAM injections +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef FRAMEWORK_WFP_SAMPLER_CALLOUT_DRIVER_H +#define FRAMEWORK_WFP_SAMPLER_CALLOUT_DRIVER_H + +extern "C" +{ +#pragma warning(push) +#pragma warning(disable: 4201) /// NAMELESS_STRUCT_UNION +#pragma warning(disable: 4324) /// STRUCTURE_PADDED + + #include /// IfsKit\Inc + #include /// Inc + #include /// Inc\WDF\KMDF\1.9 + #include /// Inc + #include /// Inc + #include /// Inc + #include /// Inc + #include /// Inc + #include /// Inc + #include /// SDK\Inc\CRT + +#pragma warning(pop) +} + +#include "Identifiers.h" /// ..\Inc +#include "ProviderContexts.h" /// ..\Inc +#include "HelperFunctions_Include.h" /// ..\SysLib +#include "HelperFunctions_ExposedCallouts.h" /// . +#include "ClassifyFunctions_Include.h" /// . +#include "CompletionFunctions_Include.h" /// . +#include "NotifyFunctions_Include.h" /// . +#include "SubscriptionFunctions_Include.h" /// . + +#define WFPSAMPLER_CALLOUT_DRIVER_TAG (UINT32)'DCSW' + +#define WPP_COMPID_LEVEL_LOGGER(COMPID,LEVEL) (WPP_CONTROL(WPP_BIT_Error).Logger), +#define WPP_COMPID_LEVEL_ENABLED(COMPID,LEVEL) (WPP_CONTROL(WPP_BIT_Error).Level >= LEVEL) + +typedef struct WFPSAMPLER_DEVICE_DATA_ +{ + HANDLE* pEngineHandle; + INJECTION_HANDLE_DATA* ppIPv4InboundInjectionHandles[2]; + INJECTION_HANDLE_DATA* ppIPv4OutboundInjectionHandles[2]; + INJECTION_HANDLE_DATA* ppIPv6InboundInjectionHandles[2]; + INJECTION_HANDLE_DATA* ppIPv6OutboundInjectionHandles[2]; + INJECTION_HANDLE_DATA* ppInboundInjectionHandles[2]; + INJECTION_HANDLE_DATA* ppOutboundInjectionHandles[2]; + HANDLE* ppRedirectionHandles[2]; +}WFPSAMPLER_DEVICE_DATA, *PWFPSAMPLER_DEVICE_DATA; + +typedef struct DEVICE_EXTENSION_ +{ + VOID* pRegistration; + PCALLBACK_OBJECT pCallbackObject; +}DEVICE_EXTENSION, *PDEVICE_EXTENSION; + +extern PDEVICE_OBJECT g_pWDMDevice; + +extern WDFDRIVER g_WDFDriver; +extern WDFDEVICE g_WDFDevice; + +extern HANDLE g_bfeSubscriptionHandle; + +extern WFPSAMPLER_DEVICE_DATA g_WFPSamplerDeviceData; + +extern DEVICE_EXTENSION g_deviceExtension; + +extern BOOLEAN g_calloutsRegistered; + +extern SERIALIZATION_LIST g_bsiSerializationList; + +extern KSPIN_LOCK g_bpeSpinLock; + +#endif /// FRAMEWORK_WFP_SAMPLER_CALLOUT_DRIVER_H diff --git a/network/trans/WFPSampler/sys/Framework_WFPSamplerCalloutDriver.rc b/network/trans/WFPSampler/sys/Framework_WFPSamplerCalloutDriver.rc new file mode 100644 index 000000000..b98d5537c --- /dev/null +++ b/network/trans/WFPSampler/sys/Framework_WFPSamplerCalloutDriver.rc @@ -0,0 +1,28 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// WFPSampler.rc +// +// Abstract: +// Internal resource file for WFPSamplerCalloutDriver. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_NETWORK +#define VER_FILEDESCRIPTION_STR "Microsoft WFP Sampler Test Driver" +#define VER_INTERNALNAME_STR "WFPSampler.sys" +#define VER_ORIGINALFILENAME_STR "WFPSampler.sys" diff --git a/network/trans/WFPSampler/sys/HelperFunctions_ExposedCallouts.cpp b/network/trans/WFPSampler/sys/HelperFunctions_ExposedCallouts.cpp new file mode 100644 index 000000000..4567b5bd6 --- /dev/null +++ b/network/trans/WFPSampler/sys/HelperFunctions_ExposedCallouts.cpp @@ -0,0 +1,1265 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_ExposedCallouts.cpp +// +// Abstract: +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Add support for ADVANCED_PACKET_INJECTION, +// FLOW_ASSOCIATION, and PEND_ENDPOINT_CLOSURE callouts +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "Framework_WFPSamplerCalloutDriver.h" /// . +#include "HelperFunctions_ExposedCallouts.tmh" /// $(OBJ_PATH)\$(O)\ + +FWPS_CALLOUT* ppRegisteredCallouts[EXPOSED_CALLOUT_COUNT] = {0}; + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID PrvExposedCalloutsUndefine() +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PrvExposedCalloutsUndefine()\n"); + +#endif /// DBG + + for(UINT32 calloutIndex = 0; + calloutIndex < EXPOSED_CALLOUT_COUNT; + calloutIndex++) + { + HLPR_DELETE(ppRegisteredCallouts[calloutIndex], + WFPSAMPLER_CALLOUT_DRIVER_TAG); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PrvExposedCalloutsUndefine()\n"); + +#endif /// DBG + + return; +} + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS PrvExposedCalloutsDefine() +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PrvExposedCalloutsDefine()\n"); + +#endif /// DBG + + NTSTATUS status = 0; + UINT32 flags = 0; + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + flags = FWP_CALLOUT_FLAG_ENABLE_COMMIT_ADD_NOTIFY; + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + /// Register all ADVANCED_PACKET_INJECTION Callouts + for(UINT32 apiIndex = 0; + apiIndex < ADVANCED_PACKET_INJECTION_COUNT; + apiIndex++) + { + HLPR_NEW(ppRegisteredCallouts[BASE_INDEX_API + apiIndex], + FWPS_CALLOUT, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(ppRegisteredCallouts[BASE_INDEX_API + apiIndex], + status); + + ppRegisteredCallouts[BASE_INDEX_API + apiIndex]->calloutKey = *(ppAdvancedPacketInjection[apiIndex]); + ppRegisteredCallouts[BASE_INDEX_API + apiIndex]->flags = flags; + ppRegisteredCallouts[BASE_INDEX_API + apiIndex]->classifyFn = ClassifyAdvancedPacketInjection; + ppRegisteredCallouts[BASE_INDEX_API + apiIndex]->notifyFn = NotifyAdvancedNotification; + ppRegisteredCallouts[BASE_INDEX_API + apiIndex]->flowDeleteFn = 0; + } + + /// Register all BASIC_ACTION_BLOCK Callouts + for(UINT32 babIndex = 0; + babIndex < BASIC_ACTION_BLOCK_COUNT; + babIndex++) + { + HLPR_NEW(ppRegisteredCallouts[BASE_INDEX_BAB + babIndex], + FWPS_CALLOUT, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(ppRegisteredCallouts[BASE_INDEX_BAB + babIndex], + status); + + ppRegisteredCallouts[BASE_INDEX_BAB + babIndex]->calloutKey = *(ppBasicActionBlock[babIndex]); + ppRegisteredCallouts[BASE_INDEX_BAB + babIndex]->flags = flags; + ppRegisteredCallouts[BASE_INDEX_BAB + babIndex]->classifyFn = ClassifyBasicActionBlock; + ppRegisteredCallouts[BASE_INDEX_BAB + babIndex]->notifyFn = NotifyBasicNotification; + ppRegisteredCallouts[BASE_INDEX_BAB + babIndex]->flowDeleteFn = 0; + } + + /// Register all BASIC_ACTION_CONTINUE Callouts + for(UINT32 bacIndex = 0; + bacIndex < BASIC_ACTION_CONTINUE_COUNT; + bacIndex++) + { + HLPR_NEW(ppRegisteredCallouts[BASE_INDEX_BAC + bacIndex], + FWPS_CALLOUT, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(ppRegisteredCallouts[BASE_INDEX_BAC + bacIndex], + status); + + ppRegisteredCallouts[BASE_INDEX_BAC + bacIndex]->calloutKey = *(ppBasicActionContinue[bacIndex]); + ppRegisteredCallouts[BASE_INDEX_BAC + bacIndex]->flags = flags; + ppRegisteredCallouts[BASE_INDEX_BAC + bacIndex]->classifyFn = ClassifyBasicActionContinue; + ppRegisteredCallouts[BASE_INDEX_BAC + bacIndex]->notifyFn = NotifyBasicNotification; + ppRegisteredCallouts[BASE_INDEX_BAC + bacIndex]->flowDeleteFn = 0; + } + + /// Register all BASIC_ACTION_PERMIT Callouts + for(UINT32 bapIndex = 0; + bapIndex < BASIC_ACTION_PERMIT_COUNT; + bapIndex++) + { + HLPR_NEW(ppRegisteredCallouts[BASE_INDEX_BAP + bapIndex], + FWPS_CALLOUT, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(ppRegisteredCallouts[BASE_INDEX_BAP + bapIndex], + status); + + ppRegisteredCallouts[BASE_INDEX_BAP + bapIndex]->calloutKey = *(ppBasicActionPermit[bapIndex]); + ppRegisteredCallouts[BASE_INDEX_BAP + bapIndex]->flags = flags; + ppRegisteredCallouts[BASE_INDEX_BAP + bapIndex]->classifyFn = ClassifyBasicActionPermit; + ppRegisteredCallouts[BASE_INDEX_BAP + bapIndex]->notifyFn = NotifyBasicNotification; + ppRegisteredCallouts[BASE_INDEX_BAP + bapIndex]->flowDeleteFn = 0; + } + + /// Register all BASIC_ACTION_RANDOM Callouts + for(UINT32 barIndex = 0; + barIndex < BASIC_ACTION_RANDOM_COUNT; + barIndex++) + { + HLPR_NEW(ppRegisteredCallouts[BASE_INDEX_BAR + barIndex], + FWPS_CALLOUT, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(ppRegisteredCallouts[BASE_INDEX_BAR + barIndex], + status); + + ppRegisteredCallouts[BASE_INDEX_BAR + barIndex]->calloutKey = *(ppBasicActionRandom[barIndex]); + ppRegisteredCallouts[BASE_INDEX_BAR + barIndex]->flags = flags; + ppRegisteredCallouts[BASE_INDEX_BAR + barIndex]->classifyFn = ClassifyBasicActionRandom; + ppRegisteredCallouts[BASE_INDEX_BAR + barIndex]->notifyFn = NotifyBasicNotification; + ppRegisteredCallouts[BASE_INDEX_BAR + barIndex]->flowDeleteFn = 0; + } + + /// Register all BASIC_PACKET_EXAMINATION Callouts + for(UINT32 bpeIndex = 0; + bpeIndex < BASIC_PACKET_EXAMINATION_COUNT; + bpeIndex++) + { + HLPR_NEW(ppRegisteredCallouts[BASE_INDEX_BPE + bpeIndex], + FWPS_CALLOUT, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(ppRegisteredCallouts[BASE_INDEX_BPE +bpeIndex], + status); + + ppRegisteredCallouts[BASE_INDEX_BPE + bpeIndex]->calloutKey = *(ppBasicPacketExamination[bpeIndex]); + ppRegisteredCallouts[BASE_INDEX_BPE + bpeIndex]->flags = flags; + ppRegisteredCallouts[BASE_INDEX_BPE + bpeIndex]->classifyFn = ClassifyBasicPacketExamination; + ppRegisteredCallouts[BASE_INDEX_BPE + bpeIndex]->notifyFn = NotifyBasicNotification; + ppRegisteredCallouts[BASE_INDEX_BPE + bpeIndex]->flowDeleteFn = 0; + } + + /// Register all BASIC_PACKET_INJECTION Callouts + for(UINT32 bpiIndex = 0; + bpiIndex < BASIC_PACKET_INJECTION_COUNT; + bpiIndex++) + { + HLPR_NEW(ppRegisteredCallouts[BASE_INDEX_BPI + bpiIndex], + FWPS_CALLOUT, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(ppRegisteredCallouts[BASE_INDEX_BPI + bpiIndex], + status); + + ppRegisteredCallouts[BASE_INDEX_BPI + bpiIndex]->calloutKey = *(ppBasicPacketInjection[bpiIndex]); + ppRegisteredCallouts[BASE_INDEX_BPI + bpiIndex]->flags = flags; + ppRegisteredCallouts[BASE_INDEX_BPI + bpiIndex]->classifyFn = ClassifyBasicPacketInjection; + ppRegisteredCallouts[BASE_INDEX_BPI + bpiIndex]->notifyFn = NotifyBasicNotification; + ppRegisteredCallouts[BASE_INDEX_BPI + bpiIndex]->flowDeleteFn = 0; + } + + /// Register all BASIC_PACKET_MODIFICATION Callouts + for(UINT32 bpmIndex = 0; + bpmIndex < BASIC_PACKET_MODIFICATION_COUNT; + bpmIndex++) + { + HLPR_NEW(ppRegisteredCallouts[BASE_INDEX_BPM + bpmIndex], + FWPS_CALLOUT, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(ppRegisteredCallouts[BASE_INDEX_BPM + bpmIndex], + status); + + ppRegisteredCallouts[BASE_INDEX_BPM + bpmIndex]->calloutKey = *(ppBasicPacketModification[bpmIndex]); + ppRegisteredCallouts[BASE_INDEX_BPM + bpmIndex]->flags = flags; + ppRegisteredCallouts[BASE_INDEX_BPM + bpmIndex]->classifyFn = ClassifyBasicPacketModification; + ppRegisteredCallouts[BASE_INDEX_BPM + bpmIndex]->notifyFn = NotifyBasicNotification; + ppRegisteredCallouts[BASE_INDEX_BPM + bpmIndex]->flowDeleteFn = 0; + } + + /// Register all BASIC_STREAM_INJECTION Callouts + for(UINT32 bsiIndex = 0; + bsiIndex < BASIC_STREAM_INJECTION_COUNT; + bsiIndex++) + { + UINT32 calloutFlags = flags; + + HLPR_NEW(ppRegisteredCallouts[BASE_INDEX_BSI + bsiIndex], + FWPS_CALLOUT, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(ppRegisteredCallouts[BASE_INDEX_BSI + bsiIndex], + status); + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + calloutFlags |= FWP_CALLOUT_FLAG_ALLOW_MID_STREAM_INSPECTION; + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + ppRegisteredCallouts[BASE_INDEX_BSI + bsiIndex]->calloutKey = *(ppBasicStreamInjection[bsiIndex]); + ppRegisteredCallouts[BASE_INDEX_BSI + bsiIndex]->flags = calloutFlags; + ppRegisteredCallouts[BASE_INDEX_BSI + bsiIndex]->classifyFn = ClassifyBasicStreamInjection; + ppRegisteredCallouts[BASE_INDEX_BSI + bsiIndex]->notifyFn = NotifyBasicNotification; + ppRegisteredCallouts[BASE_INDEX_BSI + bsiIndex]->flowDeleteFn = NotifyFlowDeleteNotification; + } + + /// Register all FAST_PACKET_INJECTION Callouts + for(UINT32 fpiIndex = 0; + fpiIndex < FAST_PACKET_INJECTION_COUNT; + fpiIndex++) + { + HLPR_NEW(ppRegisteredCallouts[BASE_INDEX_FPI + fpiIndex], + FWPS_CALLOUT, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(ppRegisteredCallouts[BASE_INDEX_FPI + fpiIndex], + status); + + ppRegisteredCallouts[BASE_INDEX_FPI + fpiIndex]->calloutKey = *(ppFastPacketInjection[fpiIndex]); + ppRegisteredCallouts[BASE_INDEX_FPI + fpiIndex]->flags = flags; + ppRegisteredCallouts[BASE_INDEX_FPI + fpiIndex]->classifyFn = ClassifyFastPacketInjection; + ppRegisteredCallouts[BASE_INDEX_FPI + fpiIndex]->notifyFn = NotifyFastNotification; + ppRegisteredCallouts[BASE_INDEX_FPI + fpiIndex]->flowDeleteFn = 0; + } + + /// Register all FAST_STREAM_INJECTION Callouts + for(UINT32 fsiIndex = 0; + fsiIndex < FAST_STREAM_INJECTION_COUNT; + fsiIndex++) + { + UINT32 calloutFlags = flags; + + HLPR_NEW(ppRegisteredCallouts[BASE_INDEX_FSI + fsiIndex], + FWPS_CALLOUT, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(ppRegisteredCallouts[BASE_INDEX_FSI + fsiIndex], + status); + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + calloutFlags |= FWP_CALLOUT_FLAG_ALLOW_MID_STREAM_INSPECTION; + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + ppRegisteredCallouts[BASE_INDEX_FSI + fsiIndex]->calloutKey = *(ppFastStreamInjection[fsiIndex]); + ppRegisteredCallouts[BASE_INDEX_FSI + fsiIndex]->flags = calloutFlags; + ppRegisteredCallouts[BASE_INDEX_FSI + fsiIndex]->classifyFn = ClassifyFastStreamInjection; + ppRegisteredCallouts[BASE_INDEX_FSI + fsiIndex]->notifyFn = NotifyFastNotification; + ppRegisteredCallouts[BASE_INDEX_FSI + fsiIndex]->flowDeleteFn = NotifyFlowDeleteNotification; + } + + /// Register all FLOW_ASSOCIATION Callouts + for(UINT32 faIndex = 0; + faIndex < FLOW_ASSOCIATION_COUNT; + faIndex++) + { + HLPR_NEW(ppRegisteredCallouts[BASE_INDEX_FA + faIndex], + FWPS_CALLOUT, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(ppRegisteredCallouts[BASE_INDEX_FA + faIndex], + status); + + ppRegisteredCallouts[BASE_INDEX_FA + faIndex]->calloutKey = *(ppFlowAssociation[faIndex]); + ppRegisteredCallouts[BASE_INDEX_FA + faIndex]->flags = flags; + ppRegisteredCallouts[BASE_INDEX_FA + faIndex]->classifyFn = ClassifyFlowAssociation; + ppRegisteredCallouts[BASE_INDEX_FA + faIndex]->notifyFn = NotifyBasicNotification; + ppRegisteredCallouts[BASE_INDEX_FA + faIndex]->flowDeleteFn = 0; + } + + /// Register all PEND_AUTHORIZATION Callouts + for(UINT32 paIndex = 0; + paIndex < PEND_AUTHORIZATION_COUNT; + paIndex++) + { + HLPR_NEW(ppRegisteredCallouts[BASE_INDEX_PA + paIndex], + FWPS_CALLOUT, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(ppRegisteredCallouts[BASE_INDEX_PA + paIndex], + status); + + ppRegisteredCallouts[BASE_INDEX_PA + paIndex]->calloutKey = *(ppPendAuthorization[paIndex]); + ppRegisteredCallouts[BASE_INDEX_PA + paIndex]->flags = flags; + ppRegisteredCallouts[BASE_INDEX_PA + paIndex]->classifyFn = ClassifyPendAuthorization; + ppRegisteredCallouts[BASE_INDEX_PA + paIndex]->notifyFn = NotifyPendNotification; + ppRegisteredCallouts[BASE_INDEX_PA + paIndex]->flowDeleteFn = 0; + } + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + /// Register all PEND_ENDPOINT_CLOSURE Callouts + for(UINT32 pecIndex = 0; + pecIndex < PEND_ENDPOINT_CLOSURE_COUNT; + pecIndex++) + { + HLPR_NEW(ppRegisteredCallouts[BASE_INDEX_PEC + pecIndex], + FWPS_CALLOUT, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(ppRegisteredCallouts[BASE_INDEX_PEC + pecIndex], + status); + + ppRegisteredCallouts[BASE_INDEX_PEC + pecIndex]->calloutKey = *(ppPendEndpointClosure[pecIndex]); + ppRegisteredCallouts[BASE_INDEX_PEC + pecIndex]->flags = flags; + ppRegisteredCallouts[BASE_INDEX_PEC + pecIndex]->classifyFn = ClassifyPendEndpointClosure; + ppRegisteredCallouts[BASE_INDEX_PEC + pecIndex]->notifyFn = NotifyPendNotification; + ppRegisteredCallouts[BASE_INDEX_PEC + pecIndex]->flowDeleteFn = NotifyFlowDeleteNotification; + } + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + /// Register all PROXY_BY_INJECTION Callouts + for(UINT32 pbiIndex = 0; + pbiIndex < PROXY_BY_INJECTION_COUNT; + pbiIndex++) + { + HLPR_NEW(ppRegisteredCallouts[BASE_INDEX_PBI + pbiIndex], + FWPS_CALLOUT, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(ppRegisteredCallouts[BASE_INDEX_PBI + pbiIndex], + status); + + ppRegisteredCallouts[BASE_INDEX_PBI + pbiIndex]->calloutKey = *(ppProxyByInjection[pbiIndex]); + ppRegisteredCallouts[BASE_INDEX_PBI + pbiIndex]->flags = flags; + ppRegisteredCallouts[BASE_INDEX_PBI + pbiIndex]->classifyFn = ClassifyProxyByInjection; + ppRegisteredCallouts[BASE_INDEX_PBI + pbiIndex]->notifyFn = NotifyBasicNotification; + ppRegisteredCallouts[BASE_INDEX_PBI + pbiIndex]->flowDeleteFn = 0; + } + + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + /// Register all PROXY_BY_ALE Callouts + for(UINT32 pbaIndex = 0; + pbaIndex < PROXY_BY_ALE_REDIRECT_COUNT; + pbaIndex++) + { + HLPR_NEW(ppRegisteredCallouts[BASE_INDEX_PBA + pbaIndex], + FWPS_CALLOUT, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(ppRegisteredCallouts[BASE_INDEX_PBA + pbaIndex], + status); + + ppRegisteredCallouts[BASE_INDEX_PBA + pbaIndex]->calloutKey = *(ppProxyByALERedirect[pbaIndex]); + ppRegisteredCallouts[BASE_INDEX_PBA + pbaIndex]->flags = flags; + ppRegisteredCallouts[BASE_INDEX_PBA + pbaIndex]->classifyFn = ClassifyProxyByALERedirect; + ppRegisteredCallouts[BASE_INDEX_PBA + pbaIndex]->notifyFn = NotifyProxyByALERedirectNotification; + ppRegisteredCallouts[BASE_INDEX_PBA + pbaIndex]->flowDeleteFn = 0; + } + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS) + PrvExposedCalloutsUndefine(); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PrvExposedCalloutsDefine() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +_IRQL_requires_(PASSIVE_LEVEL) +_IRQL_requires_same_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprExposedCalloutsUnregister() +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprExposedCalloutsUnregister()\n"); + +#endif /// DBG + + NTSTATUS status = STATUS_SUCCESS; + + for(UINT32 calloutIndex = 0; + calloutIndex < EXPOSED_CALLOUT_COUNT; + calloutIndex++) + { + if(ppRegisteredCallouts[calloutIndex] && + ppRegisteredCallouts[calloutIndex]->classifyFn) + { + NTSTATUS errCode = STATUS_SUCCESS; + + errCode = FwpsCalloutUnregisterByKey(&(ppRegisteredCallouts[calloutIndex]->calloutKey)); + if(errCode != STATUS_SUCCESS) + { + if(status == STATUS_SUCCESS) + status = STATUS_UNSUCCESSFUL; + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! KrnlHlprExposedCalloutsUnregister() [status: %#x]\n", + errCode); + } + } + } + + PrvExposedCalloutsUndefine(); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprExposedCalloutsUnregister() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +_IRQL_requires_(PASSIVE_LEVEL) +_IRQL_requires_same_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprExposedCalloutsRegister() +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprExposedCalloutsRegister()\n"); + +#endif /// DBG + + NTSTATUS status = STATUS_SUCCESS; + + status = PrvExposedCalloutsDefine(); + HLPR_BAIL_ON_FAILURE(status); + + for(UINT32 calloutIndex = 0; + calloutIndex < EXPOSED_CALLOUT_COUNT; + calloutIndex++) + { + if(ppRegisteredCallouts[calloutIndex] && + ppRegisteredCallouts[calloutIndex]->classifyFn) + { + status = FwpsCalloutRegister(g_pWDMDevice, + ppRegisteredCallouts[calloutIndex], + 0); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! KrnlHlprExposedCalloutsRegister : FwpsCalloutRegister() [status: %#x][Index: %#x]\n", + status, + calloutIndex); + + HLPR_BAIL; + } + } + } + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS) + KrnlHlprExposedCalloutsUnregister(); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprExposedCalloutsRegister() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(return != 0) +PSTR KrnlHlprExposedCalloutToString(_In_ const GUID* pCalloutKey) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprExposedCalloutToString()\n"); + +#endif /// DBG + + NT_ASSERT(pCalloutKey); + + PSTR pCalloutString = 0; + const UINT32 NUM_MASKED_BYTES = 15; + + if(RtlCompareMemory(&WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK, + pCalloutKey, + NUM_MASKED_BYTES)) + { + if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_INBOUND_IPPACKET_V4) + pCalloutString = "BASIC_ACTION_BLOCK_AT_INBOUND_IPPACKET_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_INBOUND_IPPACKET_V6) + pCalloutString = "BASIC_ACTION_BLOCK_AT_INBOUND_IPPACKET_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_OUTBOUND_IPPACKET_V4) + pCalloutString = "BASIC_ACTION_BLOCK_AT_OUTBOUND_IPPACKET_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_OUTBOUND_IPPACKET_V6) + pCalloutString = "BASIC_ACTION_BLOCK_AT_OUTBOUND_IPPACKET_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_IPFORWARD_V4) + pCalloutString = "BASIC_ACTION_BLOCK_AT_IPFORWARD_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_IPFORWARD_V6) + pCalloutString = "BASIC_ACTION_BLOCK_AT_IPFORWARD_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_INBOUND_TRANSPORT_V4) + pCalloutString = "BASIC_ACTION_BLOCK_AT_INBOUND_TRANSPORT_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_INBOUND_TRANSPORT_V6) + pCalloutString = "BASIC_ACTION_BLOCK_AT_INBOUND_TRANSPORT_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_OUTBOUND_TRANSPORT_V4) + pCalloutString = "BASIC_ACTION_BLOCK_AT_OUTBOUND_TRANSPORT_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_OUTBOUND_TRANSPORT_V6) + pCalloutString = "BASIC_ACTION_BLOCK_AT_OUTBOUND_TRANSPORT_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_STREAM_V4) + pCalloutString = "BASIC_ACTION_BLOCK_AT_STREAM_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_STREAM_V6) + pCalloutString = "BASIC_ACTION_BLOCK_AT_STREAM_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_DATAGRAM_DATA_V4) + pCalloutString = "BASIC_ACTION_BLOCK_AT_DATAGRAM_DATA_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_DATAGRAM_DATA_V6) + pCalloutString = "BASIC_ACTION_BLOCK_AT_DATAGRAM_DATA_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_INBOUND_ICMP_ERROR_V4) + pCalloutString = "BASIC_ACTION_BLOCK_AT_INBOUND_ICMP_ERROR_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_INBOUND_ICMP_ERROR_V6) + pCalloutString = "BASIC_ACTION_BLOCK_AT_INBOUND_ICMP_ERROR_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_OUTBOUND_ICMP_ERROR_V4) + pCalloutString = "BASIC_ACTION_BLOCK_AT_OUTBOUND_ICMP_ERROR_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_OUTBOUND_ICMP_ERROR_V6) + pCalloutString = "BASIC_ACTION_BLOCK_AT_OUTBOUND_ICMP_ERROR_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_ALE_RESOURCE_ASSIGNMENT_V4) + pCalloutString = "BASIC_ACTION_BLOCK_AT_ALE_RESOURCE_ASSIGNMENT_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_ALE_RESOURCE_ASSIGNMENT_V6) + pCalloutString = "BASIC_ACTION_BLOCK_AT_ALE_RESOURCE_ASSIGNMENT_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_ALE_AUTH_LISTEN_V4) + pCalloutString = "BASIC_ACTION_BLOCK_AT_ALE_AUTH_LISTEN_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_ALE_AUTH_LISTEN_V6) + pCalloutString = "BASIC_ACTION_BLOCK_AT_ALE_AUTH_LISTEN_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_ALE_AUTH_RECV_ACCEPT_V4) + pCalloutString = "BASIC_ACTION_BLOCK_AT_ALE_AUTH_RECV_ACCEPT_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_ALE_AUTH_RECV_ACCEPT_V6) + pCalloutString = "BASIC_ACTION_BLOCK_AT_ALE_AUTH_RECV_ACCEPT_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_ALE_AUTH_CONNECT_V4) + pCalloutString = "BASIC_ACTION_BLOCK_AT_ALE_AUTH_CONNECT_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_ALE_AUTH_CONNECT_V6) + pCalloutString = "BASIC_ACTION_BLOCK_AT_ALE_AUTH_CONNECT_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_ALE_FLOW_ESTABLISHED_V4) + pCalloutString = "BASIC_ACTION_BLOCK_AT_ALE_FLOW_ESTABLISHED_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_ALE_FLOW_ESTABLISHED_V6) + pCalloutString = "BASIC_ACTION_BLOCK_AT_ALE_FLOW_ESTABLISHED_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_INBOUND_MAC_FRAME_ETHERNET) + pCalloutString = "BASIC_ACTION_BLOCK_AT_INBOUND_MAC_FRAME_ETHERNET"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_OUTBOUND_MAC_FRAME_ETHERNET) + pCalloutString = "BASIC_ACTION_BLOCK_AT_OUTBOUND_MAC_FRAME_ETHERNET"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_ALE_RESOURCE_RELEASE_V4) + pCalloutString = "BASIC_ACTION_BLOCK_AT_ALE_RESOURCE_RELEASE_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_ALE_RESOURCE_RELEASE_V6) + pCalloutString = "BASIC_ACTION_BLOCK_AT_ALE_RESOURCE_RELEASE_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_ALE_ENDPOINT_CLOSURE_V4) + pCalloutString = "BASIC_ACTION_BLOCK_AT_ALE_ENDPOINT_CLOSURE_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_ALE_ENDPOINT_CLOSURE_V6) + pCalloutString = "BASIC_ACTION_BLOCK_AT_ALE_ENDPOINT_CLOSURE_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_STREAM_PACKET_V4) + pCalloutString = "BASIC_ACTION_BLOCK_AT_STREAM_PACKET_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_STREAM_PACKET_V6) + pCalloutString = "BASIC_ACTION_BLOCK_AT_STREAM_PACKET_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_INBOUND_MAC_FRAME_NATIVE) + pCalloutString = "BASIC_ACTION_BLOCK_AT_INBOUND_MAC_FRAME_NATIVE"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_OUTBOUND_MAC_FRAME_NATIVE) + pCalloutString = "BASIC_ACTION_BLOCK_AT_OUTBOUND_MAC_FRAME_NATIVE"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_INGRESS_VSWITCH_ETHERNET) + pCalloutString = "BASIC_ACTION_BLOCK_AT_INGRESS_VSWITCH_ETHERNET"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_EGRESS_VSWITCH_ETHERNET) + pCalloutString = "BASIC_ACTION_BLOCK_AT_EGRESS_VSWITCH_ETHERNET"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_INGRESS_VSWITCH_TRANSPORT_V4) + pCalloutString = "BASIC_ACTION_BLOCK_AT_INGRESS_VSWITCH_TRANSPORT_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_INGRESS_VSWITCH_TRANSPORT_V6) + pCalloutString = "BASIC_ACTION_BLOCK_AT_INGRESS_VSWITCH_TRANSPORT_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_EGRESS_VSWITCH_TRANSPORT_V4) + pCalloutString = "BASIC_ACTION_BLOCK_AT_EGRESS_VSWITCH_TRANSPORT_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_EGRESS_VSWITCH_TRANSPORT_V6) + pCalloutString = "BASIC_ACTION_BLOCK_AT_EGRESS_VSWITCH_TRANSPORT_V6"; + } + else if(RtlCompareMemory(&WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE, + pCalloutKey, + NUM_MASKED_BYTES)) + { + if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_INBOUND_IPPACKET_V4) + pCalloutString = "BASIC_ACTION_CONTINUE_AT_INBOUND_IPPACKET_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_INBOUND_IPPACKET_V6) + pCalloutString = "BASIC_ACTION_CONTINUE_AT_INBOUND_IPPACKET_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_OUTBOUND_IPPACKET_V4) + pCalloutString = "BASIC_ACTION_CONTINUE_AT_OUTBOUND_IPPACKET_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_OUTBOUND_IPPACKET_V6) + pCalloutString = "BASIC_ACTION_CONTINUE_AT_OUTBOUND_IPPACKET_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_IPFORWARD_V4) + pCalloutString = "BASIC_ACTION_CONTINUE_AT_IPFORWARD_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_IPFORWARD_V6) + pCalloutString = "BASIC_ACTION_CONTINUE_AT_IPFORWARD_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_INBOUND_TRANSPORT_V4) + pCalloutString = "BASIC_ACTION_CONTINUE_AT_INBOUND_TRANSPORT_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_INBOUND_TRANSPORT_V6) + pCalloutString = "BASIC_ACTION_CONTINUE_AT_INBOUND_TRANSPORT_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_OUTBOUND_TRANSPORT_V4) + pCalloutString = "BASIC_ACTION_CONTINUE_AT_OUTBOUND_TRANSPORT_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_OUTBOUND_TRANSPORT_V6) + pCalloutString = "BASIC_ACTION_CONTINUE_AT_OUTBOUND_TRANSPORT_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_STREAM_V4) + pCalloutString = "BASIC_ACTION_CONTINUE_AT_STREAM_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_STREAM_V6) + pCalloutString = "BASIC_ACTION_CONTINUE_AT_STREAM_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_DATAGRAM_DATA_V4) + pCalloutString = "BASIC_ACTION_CONTINUE_AT_DATAGRAM_DATA_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_DATAGRAM_DATA_V6) + pCalloutString = "BASIC_ACTION_CONTINUE_AT_DATAGRAM_DATA_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_INBOUND_ICMP_ERROR_V4) + pCalloutString = "BASIC_ACTION_CONTINUE_AT_INBOUND_ICMP_ERROR_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_INBOUND_ICMP_ERROR_V6) + pCalloutString = "BASIC_ACTION_CONTINUE_AT_INBOUND_ICMP_ERROR_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_OUTBOUND_ICMP_ERROR_V4) + pCalloutString = "BASIC_ACTION_CONTINUE_AT_OUTBOUND_ICMP_ERROR_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_OUTBOUND_ICMP_ERROR_V6) + pCalloutString = "BASIC_ACTION_CONTINUE_AT_OUTBOUND_ICMP_ERROR_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_ALE_RESOURCE_ASSIGNMENT_V4) + pCalloutString = "BASIC_ACTION_CONTINUE_AT_ALE_RESOURCE_ASSIGNMENT_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_ALE_RESOURCE_ASSIGNMENT_V6) + pCalloutString = "BASIC_ACTION_CONTINUE_AT_ALE_RESOURCE_ASSIGNMENT_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_ALE_AUTH_LISTEN_V4) + pCalloutString = "BASIC_ACTION_CONTINUE_AT_ALE_AUTH_LISTEN_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_ALE_AUTH_LISTEN_V6) + pCalloutString = "BASIC_ACTION_CONTINUE_AT_ALE_AUTH_LISTEN_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_ALE_AUTH_RECV_ACCEPT_V4) + pCalloutString = "BASIC_ACTION_CONTINUE_AT_ALE_AUTH_RECV_ACCEPT_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_ALE_AUTH_RECV_ACCEPT_V6) + pCalloutString = "BASIC_ACTION_CONTINUE_AT_ALE_AUTH_RECV_ACCEPT_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_ALE_AUTH_CONNECT_V4) + pCalloutString = "BASIC_ACTION_CONTINUE_AT_ALE_AUTH_CONNECT_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_ALE_AUTH_CONNECT_V6) + pCalloutString = "BASIC_ACTION_CONTINUE_AT_ALE_AUTH_CONNECT_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_ALE_FLOW_ESTABLISHED_V4) + pCalloutString = "BASIC_ACTION_CONTINUE_AT_ALE_FLOW_ESTABLISHED_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_ALE_FLOW_ESTABLISHED_V6) + pCalloutString = "BASIC_ACTION_CONTINUE_AT_ALE_FLOW_ESTABLISHED_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_INBOUND_MAC_FRAME_ETHERNET) + pCalloutString = "BASIC_ACTION_CONTINUE_AT_INBOUND_MAC_FRAME_ETHERNET"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_OUTBOUND_MAC_FRAME_ETHERNET) + pCalloutString = "BASIC_ACTION_CONTINUE_AT_OUTBOUND_MAC_FRAME_ETHERNET"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_ALE_RESOURCE_RELEASE_V4) + pCalloutString = "BASIC_ACTION_CONTINUE_AT_ALE_RESOURCE_RELEASE_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_ALE_RESOURCE_RELEASE_V6) + pCalloutString = "BASIC_ACTION_CONTINUE_AT_ALE_RESOURCE_RELEASE_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_ALE_ENDPOINT_CLOSURE_V4) + pCalloutString = "BASIC_ACTION_CONTINUE_AT_ALE_ENDPOINT_CLOSURE_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_ALE_ENDPOINT_CLOSURE_V6) + pCalloutString = "BASIC_ACTION_CONTINUE_AT_ALE_ENDPOINT_CLOSURE_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_STREAM_PACKET_V4) + pCalloutString = "BASIC_ACTION_CONTINUE_AT_STREAM_PACKET_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_STREAM_PACKET_V6) + pCalloutString = "BASIC_ACTION_CONTINUE_AT_STREAM_PACKET_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_INBOUND_MAC_FRAME_NATIVE) + pCalloutString = "BASIC_ACTION_CONTINUE_AT_INBOUND_MAC_FRAME_NATIVE"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_OUTBOUND_MAC_FRAME_NATIVE) + pCalloutString = "BASIC_ACTION_CONTINUE_AT_OUTBOUND_MAC_FRAME_NATIVE"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_INGRESS_VSWITCH_ETHERNET) + pCalloutString = "BASIC_ACTION_CONTINUE_AT_INGRESS_VSWITCH_ETHERNET"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_EGRESS_VSWITCH_ETHERNET) + pCalloutString = "BASIC_ACTION_CONTINUE_AT_EGRESS_VSWITCH_ETHERNET"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_INGRESS_VSWITCH_TRANSPORT_V4) + pCalloutString = "BASIC_ACTION_CONTINUE_AT_INGRESS_VSWITCH_TRANSPORT_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_INGRESS_VSWITCH_TRANSPORT_V6) + pCalloutString = "BASIC_ACTION_CONTINUE_AT_INGRESS_VSWITCH_TRANSPORT_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_EGRESS_VSWITCH_TRANSPORT_V4) + pCalloutString = "BASIC_ACTION_CONTINUE_AT_EGRESS_VSWITCH_TRANSPORT_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_EGRESS_VSWITCH_TRANSPORT_V6) + pCalloutString = "BASIC_ACTION_CONTINUE_AT_EGRESS_VSWITCH_TRANSPORT_V6"; + } + else if(RtlCompareMemory(&WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT, + pCalloutKey, + NUM_MASKED_BYTES)) + { + if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_INBOUND_IPPACKET_V4) + pCalloutString = "BASIC_ACTION_PERMIT_AT_INBOUND_IPPACKET_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_INBOUND_IPPACKET_V6) + pCalloutString = "BASIC_ACTION_PERMIT_AT_INBOUND_IPPACKET_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_OUTBOUND_IPPACKET_V4) + pCalloutString = "BASIC_ACTION_PERMIT_AT_OUTBOUND_IPPACKET_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_OUTBOUND_IPPACKET_V6) + pCalloutString = "BASIC_ACTION_PERMIT_AT_OUTBOUND_IPPACKET_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_IPFORWARD_V4) + pCalloutString = "BASIC_ACTION_PERMIT_AT_IPFORWARD_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_IPFORWARD_V6) + pCalloutString = "BASIC_ACTION_PERMIT_AT_IPFORWARD_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_INBOUND_TRANSPORT_V4) + pCalloutString = "BASIC_ACTION_PERMIT_AT_INBOUND_TRANSPORT_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_INBOUND_TRANSPORT_V6) + pCalloutString = "BASIC_ACTION_PERMIT_AT_INBOUND_TRANSPORT_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_OUTBOUND_TRANSPORT_V4) + pCalloutString = "BASIC_ACTION_PERMIT_AT_OUTBOUND_TRANSPORT_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_OUTBOUND_TRANSPORT_V6) + pCalloutString = "BASIC_ACTION_PERMIT_AT_OUTBOUND_TRANSPORT_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_STREAM_V4) + pCalloutString = "BASIC_ACTION_PERMIT_AT_STREAM_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_STREAM_V6) + pCalloutString = "BASIC_ACTION_PERMIT_AT_STREAM_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_DATAGRAM_DATA_V4) + pCalloutString = "BASIC_ACTION_PERMIT_AT_DATAGRAM_DATA_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_DATAGRAM_DATA_V6) + pCalloutString = "BASIC_ACTION_PERMIT_AT_DATAGRAM_DATA_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_INBOUND_ICMP_ERROR_V4) + pCalloutString = "BASIC_ACTION_PERMIT_AT_INBOUND_ICMP_ERROR_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_INBOUND_ICMP_ERROR_V6) + pCalloutString = "BASIC_ACTION_PERMIT_AT_INBOUND_ICMP_ERROR_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_OUTBOUND_ICMP_ERROR_V4) + pCalloutString = "BASIC_ACTION_PERMIT_AT_OUTBOUND_ICMP_ERROR_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_OUTBOUND_ICMP_ERROR_V6) + pCalloutString = "BASIC_ACTION_PERMIT_AT_OUTBOUND_ICMP_ERROR_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_ALE_RESOURCE_ASSIGNMENT_V4) + pCalloutString = "BASIC_ACTION_PERMIT_AT_ALE_RESOURCE_ASSIGNMENT_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_ALE_RESOURCE_ASSIGNMENT_V6) + pCalloutString = "BASIC_ACTION_PERMIT_AT_ALE_RESOURCE_ASSIGNMENT_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_ALE_AUTH_LISTEN_V4) + pCalloutString = "BASIC_ACTION_PERMIT_AT_ALE_AUTH_LISTEN_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_ALE_AUTH_LISTEN_V6) + pCalloutString = "BASIC_ACTION_PERMIT_AT_ALE_AUTH_LISTEN_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_ALE_AUTH_RECV_ACCEPT_V4) + pCalloutString = "BASIC_ACTION_PERMIT_AT_ALE_AUTH_RECV_ACCEPT_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_ALE_AUTH_RECV_ACCEPT_V6) + pCalloutString = "BASIC_ACTION_PERMIT_AT_ALE_AUTH_RECV_ACCEPT_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_ALE_AUTH_CONNECT_V4) + pCalloutString = "BASIC_ACTION_PERMIT_AT_ALE_AUTH_CONNECT_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_ALE_AUTH_CONNECT_V6) + pCalloutString = "BASIC_ACTION_PERMIT_AT_ALE_AUTH_CONNECT_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_ALE_FLOW_ESTABLISHED_V4) + pCalloutString = "BASIC_ACTION_PERMIT_AT_ALE_FLOW_ESTABLISHED_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_ALE_FLOW_ESTABLISHED_V6) + pCalloutString = "BASIC_ACTION_PERMIT_AT_ALE_FLOW_ESTABLISHED_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_INBOUND_MAC_FRAME_ETHERNET) + pCalloutString = "BASIC_ACTION_PERMIT_AT_INBOUND_MAC_FRAME_ETHERNET"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_OUTBOUND_MAC_FRAME_ETHERNET) + pCalloutString = "BASIC_ACTION_PERMIT_AT_OUTBOUND_MAC_FRAME_ETHERNET"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_ALE_RESOURCE_RELEASE_V4) + pCalloutString = "BASIC_ACTION_PERMIT_AT_ALE_RESOURCE_RELEASE_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_ALE_RESOURCE_RELEASE_V6) + pCalloutString = "BASIC_ACTION_PERMIT_AT_ALE_RESOURCE_RELEASE_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_ALE_ENDPOINT_CLOSURE_V4) + pCalloutString = "BASIC_ACTION_PERMIT_AT_ALE_ENDPOINT_CLOSURE_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_ALE_ENDPOINT_CLOSURE_V6) + pCalloutString = "BASIC_ACTION_PERMIT_AT_ALE_ENDPOINT_CLOSURE_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_STREAM_PACKET_V4) + pCalloutString = "BASIC_ACTION_PERMIT_AT_STREAM_PACKET_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_STREAM_PACKET_V6) + pCalloutString = "BASIC_ACTION_PERMIT_AT_STREAM_PACKET_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_INBOUND_MAC_FRAME_NATIVE) + pCalloutString = "BASIC_ACTION_PERMIT_AT_INBOUND_MAC_FRAME_NATIVE"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_OUTBOUND_MAC_FRAME_NATIVE) + pCalloutString = "BASIC_ACTION_PERMIT_AT_OUTBOUND_MAC_FRAME_NATIVE"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_INGRESS_VSWITCH_ETHERNET) + pCalloutString = "BASIC_ACTION_PERMIT_AT_INGRESS_VSWITCH_ETHERNET"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_EGRESS_VSWITCH_ETHERNET) + pCalloutString = "BASIC_ACTION_PERMIT_AT_EGRESS_VSWITCH_ETHERNET"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_INGRESS_VSWITCH_TRANSPORT_V4) + pCalloutString = "BASIC_ACTION_PERMIT_AT_INGRESS_VSWITCH_TRANSPORT_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_INGRESS_VSWITCH_TRANSPORT_V6) + pCalloutString = "BASIC_ACTION_PERMIT_AT_INGRESS_VSWITCH_TRANSPORT_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_EGRESS_VSWITCH_TRANSPORT_V4) + pCalloutString = "BASIC_ACTION_PERMIT_AT_EGRESS_VSWITCH_TRANSPORT_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_EGRESS_VSWITCH_TRANSPORT_V6) + pCalloutString = "BASIC_ACTION_PERMIT_AT_EGRESS_VSWITCH_TRANSPORT_V6"; + } + else if(RtlCompareMemory(&WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM, + pCalloutKey, + NUM_MASKED_BYTES)) + { + if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_INBOUND_IPPACKET_V4) + pCalloutString = "BASIC_ACTION_RANDOM_AT_INBOUND_IPPACKET_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_INBOUND_IPPACKET_V6) + pCalloutString = "BASIC_ACTION_RANDOM_AT_INBOUND_IPPACKET_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_OUTBOUND_IPPACKET_V4) + pCalloutString = "BASIC_ACTION_RANDOM_AT_OUTBOUND_IPPACKET_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_OUTBOUND_IPPACKET_V6) + pCalloutString = "BASIC_ACTION_RANDOM_AT_OUTBOUND_IPPACKET_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_IPFORWARD_V4) + pCalloutString = "BASIC_ACTION_RANDOM_AT_IPFORWARD_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_IPFORWARD_V6) + pCalloutString = "BASIC_ACTION_RANDOM_AT_IPFORWARD_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_INBOUND_TRANSPORT_V4) + pCalloutString = "BASIC_ACTION_RANDOM_AT_INBOUND_TRANSPORT_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_INBOUND_TRANSPORT_V6) + pCalloutString = "BASIC_ACTION_RANDOM_AT_INBOUND_TRANSPORT_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_OUTBOUND_TRANSPORT_V4) + pCalloutString = "BASIC_ACTION_RANDOM_AT_OUTBOUND_TRANSPORT_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_OUTBOUND_TRANSPORT_V6) + pCalloutString = "BASIC_ACTION_RANDOM_AT_OUTBOUND_TRANSPORT_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_STREAM_V4) + pCalloutString = "BASIC_ACTION_RANDOM_AT_STREAM_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_STREAM_V6) + pCalloutString = "BASIC_ACTION_RANDOM_AT_STREAM_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_DATAGRAM_DATA_V4) + pCalloutString = "BASIC_ACTION_RANDOM_AT_DATAGRAM_DATA_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_DATAGRAM_DATA_V6) + pCalloutString = "BASIC_ACTION_RANDOM_AT_DATAGRAM_DATA_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_INBOUND_ICMP_ERROR_V4) + pCalloutString = "BASIC_ACTION_RANDOM_AT_INBOUND_ICMP_ERROR_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_INBOUND_ICMP_ERROR_V6) + pCalloutString = "BASIC_ACTION_RANDOM_AT_INBOUND_ICMP_ERROR_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_OUTBOUND_ICMP_ERROR_V4) + pCalloutString = "BASIC_ACTION_RANDOM_AT_OUTBOUND_ICMP_ERROR_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_OUTBOUND_ICMP_ERROR_V6) + pCalloutString = "BASIC_ACTION_RANDOM_AT_OUTBOUND_ICMP_ERROR_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_ALE_RESOURCE_ASSIGNMENT_V4) + pCalloutString = "BASIC_ACTION_RANDOM_AT_ALE_RESOURCE_ASSIGNMENT_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_ALE_RESOURCE_ASSIGNMENT_V6) + pCalloutString = "BASIC_ACTION_RANDOM_AT_ALE_RESOURCE_ASSIGNMENT_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_ALE_AUTH_LISTEN_V4) + pCalloutString = "BASIC_ACTION_RANDOM_AT_ALE_AUTH_LISTEN_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_ALE_AUTH_LISTEN_V6) + pCalloutString = "BASIC_ACTION_RANDOM_AT_ALE_AUTH_LISTEN_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_ALE_AUTH_RECV_ACCEPT_V4) + pCalloutString = "BASIC_ACTION_RANDOM_AT_ALE_AUTH_RECV_ACCEPT_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_ALE_AUTH_RECV_ACCEPT_V6) + pCalloutString = "BASIC_ACTION_RANDOM_AT_ALE_AUTH_RECV_ACCEPT_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_ALE_AUTH_CONNECT_V4) + pCalloutString = "BASIC_ACTION_RANDOM_AT_ALE_AUTH_CONNECT_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_ALE_AUTH_CONNECT_V6) + pCalloutString = "BASIC_ACTION_RANDOM_AT_ALE_AUTH_CONNECT_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_ALE_FLOW_ESTABLISHED_V4) + pCalloutString = "BASIC_ACTION_RANDOM_AT_ALE_FLOW_ESTABLISHED_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_ALE_FLOW_ESTABLISHED_V6) + pCalloutString = "BASIC_ACTION_RANDOM_AT_ALE_FLOW_ESTABLISHED_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_INBOUND_MAC_FRAME_ETHERNET) + pCalloutString = "BASIC_ACTION_RANDOM_AT_INBOUND_MAC_FRAME_ETHERNET"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_OUTBOUND_MAC_FRAME_ETHERNET) + pCalloutString = "BASIC_ACTION_RANDOM_AT_OUTBOUND_MAC_FRAME_ETHERNET"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_ALE_RESOURCE_RELEASE_V4) + pCalloutString = "BASIC_ACTION_RANDOM_AT_ALE_RESOURCE_RELEASE_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_ALE_RESOURCE_RELEASE_V6) + pCalloutString = "BASIC_ACTION_RANDOM_AT_ALE_RESOURCE_RELEASE_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_ALE_ENDPOINT_CLOSURE_V4) + pCalloutString = "BASIC_ACTION_RANDOM_AT_ALE_ENDPOINT_CLOSURE_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_ALE_ENDPOINT_CLOSURE_V6) + pCalloutString = "BASIC_ACTION_RANDOM_AT_ALE_ENDPOINT_CLOSURE_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_STREAM_PACKET_V4) + pCalloutString = "BASIC_ACTION_RANDOM_AT_STREAM_PACKET_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_STREAM_PACKET_V6) + pCalloutString = "BASIC_ACTION_RANDOM_AT_STREAM_PACKET_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_INBOUND_MAC_FRAME_NATIVE) + pCalloutString = "BASIC_ACTION_RANDOM_AT_INBOUND_MAC_FRAME_NATIVE"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_OUTBOUND_MAC_FRAME_NATIVE) + pCalloutString = "BASIC_ACTION_RANDOM_AT_OUTBOUND_MAC_FRAME_NATIVE"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_INGRESS_VSWITCH_ETHERNET) + pCalloutString = "BASIC_ACTION_RANDOM_AT_INGRESS_VSWITCH_ETHERNET"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_EGRESS_VSWITCH_ETHERNET) + pCalloutString = "BASIC_ACTION_RANDOM_AT_EGRESS_VSWITCH_ETHERNET"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_INGRESS_VSWITCH_TRANSPORT_V4) + pCalloutString = "BASIC_ACTION_RANDOM_AT_INGRESS_VSWITCH_TRANSPORT_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_INGRESS_VSWITCH_TRANSPORT_V6) + pCalloutString = "BASIC_ACTION_RANDOM_AT_INGRESS_VSWITCH_TRANSPORT_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_EGRESS_VSWITCH_TRANSPORT_V4) + pCalloutString = "BASIC_ACTION_RANDOM_AT_EGRESS_VSWITCH_TRANSPORT_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_EGRESS_VSWITCH_TRANSPORT_V6) + pCalloutString = "BASIC_ACTION_RANDOM_AT_EGRESS_VSWITCH_TRANSPORT_V6"; + } + else if(RtlCompareMemory(&WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION, + pCalloutKey, + NUM_MASKED_BYTES)) + { + if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_INBOUND_IPPACKET_V4) + pCalloutString = "BASIC_PACKET_EXAMINATION_AT_INBOUND_IPPACKET_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_INBOUND_IPPACKET_V6) + pCalloutString = "BASIC_PACKET_EXAMINATION_AT_INBOUND_IPPACKET_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_OUTBOUND_IPPACKET_V4) + pCalloutString = "BASIC_PACKET_EXAMINATION_AT_OUTBOUND_IPPACKET_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_OUTBOUND_IPPACKET_V6) + pCalloutString = "BASIC_PACKET_EXAMINATION_AT_OUTBOUND_IPPACKET_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_IPFORWARD_V4) + pCalloutString = "BASIC_PACKET_EXAMINATION_AT_IPFORWARD_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_IPFORWARD_V6) + pCalloutString = "BASIC_PACKET_EXAMINATION_AT_IPFORWARD_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_INBOUND_TRANSPORT_V4) + pCalloutString = "BASIC_PACKET_EXAMINATION_AT_INBOUND_TRANSPORT_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_INBOUND_TRANSPORT_V6) + pCalloutString = "BASIC_PACKET_EXAMINATION_AT_INBOUND_TRANSPORT_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_OUTBOUND_TRANSPORT_V4) + pCalloutString = "BASIC_PACKET_EXAMINATION_AT_OUTBOUND_TRANSPORT_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_OUTBOUND_TRANSPORT_V6) + pCalloutString = "BASIC_PACKET_EXAMINATION_AT_OUTBOUND_TRANSPORT_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_DATAGRAM_DATA_V4) + pCalloutString = "BASIC_PACKET_EXAMINATION_AT_DATAGRAM_DATA_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_DATAGRAM_DATA_V6) + pCalloutString = "BASIC_PACKET_EXAMINATION_AT_DATAGRAM_DATA_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_INBOUND_ICMP_ERROR_V4) + pCalloutString = "BASIC_PACKET_EXAMINATION_AT_INBOUND_ICMP_ERROR_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_INBOUND_ICMP_ERROR_V6) + pCalloutString = "BASIC_PACKET_EXAMINATION_AT_INBOUND_ICMP_ERROR_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_OUTBOUND_ICMP_ERROR_V4) + pCalloutString = "BASIC_PACKET_EXAMINATION_AT_OUTBOUND_ICMP_ERROR_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_OUTBOUND_ICMP_ERROR_V6) + pCalloutString = "BASIC_PACKET_EXAMINATION_AT_OUTBOUND_ICMP_ERROR_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_ALE_AUTH_RECV_ACCEPT_V4) + pCalloutString = "BASIC_PACKET_EXAMINATION_AT_ALE_AUTH_RECV_ACCEPT_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_ALE_AUTH_RECV_ACCEPT_V6) + pCalloutString = "BASIC_PACKET_EXAMINATION_AT_ALE_AUTH_RECV_ACCEPT_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_ALE_AUTH_CONNECT_V4) + pCalloutString = "BASIC_PACKET_EXAMINATION_AT_ALE_AUTH_CONNECT_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_ALE_AUTH_CONNECT_V6) + pCalloutString = "BASIC_PACKET_EXAMINATION_AT_ALE_AUTH_CONNECT_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_ALE_FLOW_ESTABLISHED_V4) + pCalloutString = "BASIC_PACKET_EXAMINATION_AT_ALE_FLOW_ESTABLISHED_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_ALE_FLOW_ESTABLISHED_V6) + pCalloutString = "BASIC_PACKET_EXAMINATION_AT_ALE_FLOW_ESTABLISHED_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_STREAM_PACKET_V4) + pCalloutString = "BASIC_PACKET_EXAMINATION_AT_STREAM_PACKET_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_STREAM_PACKET_V6) + pCalloutString = "BASIC_PACKET_EXAMINATION_AT_STREAM_PACKET_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_INBOUND_MAC_FRAME_ETHERNET) + pCalloutString = "BASIC_PACKET_EXAMINATION_AT_INBOUND_MAC_FRAME_ETHERNET"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_OUTBOUND_MAC_FRAME_ETHERNET) + pCalloutString = "BASIC_PACKET_EXAMINATION_AT_OUTBOUND_MAC_FRAME_ETHERNET"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_INBOUND_MAC_FRAME_NATIVE) + pCalloutString = "BASIC_PACKET_EXAMINATION_AT_INBOUND_MAC_FRAME_NATIVE"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_OUTBOUND_MAC_FRAME_NATIVE) + pCalloutString = "BASIC_PACKET_EXAMINATION_AT_OUTBOUND_MAC_FRAME_NATIVE"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_INGRESS_VSWITCH_ETHERNET) + pCalloutString = "BASIC_PACKET_EXAMINATION_AT_INGRESS_VSWITCH_ETHERNET"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_EGRESS_VSWITCH_ETHERNET) + pCalloutString = "BASIC_PACKET_EXAMINATION_AT_EGRESS_VSWITCH_ETHERNET"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_INGRESS_VSWITCH_TRANSPORT_V4) + pCalloutString = "BASIC_PACKET_EXAMINATION_AT_INGRESS_VSWITCH_TRANSPORT_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_INGRESS_VSWITCH_TRANSPORT_V6) + pCalloutString = "BASIC_PACKET_EXAMINATION_AT_INGRESS_VSWITCH_TRANSPORT_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_EGRESS_VSWITCH_TRANSPORT_V4) + pCalloutString = "BASIC_PACKET_EXAMINATION_AT_EGRESS_VSWITCH_TRANSPORT_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_EGRESS_VSWITCH_TRANSPORT_V6) + pCalloutString = "BASIC_PACKET_EXAMINATION_AT_EGRESS_VSWITCH_TRANSPORT_V6"; + } + else if(RtlCompareMemory(&WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION, + pCalloutKey, + NUM_MASKED_BYTES)) + { + if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_INBOUND_IPPACKET_V4) + pCalloutString = "BASIC_PACKET_INJECTION_AT_INBOUND_IPPACKET_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_INBOUND_IPPACKET_V6) + pCalloutString = "BASIC_PACKET_INJECTION_AT_INBOUND_IPPACKET_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_OUTBOUND_IPPACKET_V4) + pCalloutString = "BASIC_PACKET_INJECTION_AT_OUTBOUND_IPPACKET_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_OUTBOUND_IPPACKET_V6) + pCalloutString = "BASIC_PACKET_INJECTION_AT_OUTBOUND_IPPACKET_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_IPFORWARD_V4) + pCalloutString = "BASIC_PACKET_INJECTION_AT_IPFORWARD_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_IPFORWARD_V6) + pCalloutString = "BASIC_PACKET_INJECTION_AT_IPFORWARD_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_INBOUND_TRANSPORT_V4) + pCalloutString = "BASIC_PACKET_INJECTION_AT_INBOUND_TRANSPORT_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_INBOUND_TRANSPORT_V6) + pCalloutString = "BASIC_PACKET_INJECTION_AT_INBOUND_TRANSPORT_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_OUTBOUND_TRANSPORT_V4) + pCalloutString = "BASIC_PACKET_INJECTION_AT_OUTBOUND_TRANSPORT_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_OUTBOUND_TRANSPORT_V6) + pCalloutString = "BASIC_PACKET_INJECTION_AT_OUTBOUND_TRANSPORT_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_DATAGRAM_DATA_V4) + pCalloutString = "BASIC_PACKET_INJECTION_AT_DATAGRAM_DATA_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_DATAGRAM_DATA_V6) + pCalloutString = "BASIC_PACKET_INJECTION_AT_DATAGRAM_DATA_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_INBOUND_ICMP_ERROR_V4) + pCalloutString = "BASIC_PACKET_INJECTION_AT_INBOUND_ICMP_ERROR_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_INBOUND_ICMP_ERROR_V6) + pCalloutString = "BASIC_PACKET_INJECTION_AT_INBOUND_ICMP_ERROR_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_OUTBOUND_ICMP_ERROR_V4) + pCalloutString = "BASIC_PACKET_INJECTION_AT_OUTBOUND_ICMP_ERROR_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_OUTBOUND_ICMP_ERROR_V6) + pCalloutString = "BASIC_PACKET_INJECTION_AT_OUTBOUND_ICMP_ERROR_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_ALE_AUTH_RECV_ACCEPT_V4) + pCalloutString = "BASIC_PACKET_INJECTION_AT_ALE_AUTH_RECV_ACCEPT_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_ALE_AUTH_RECV_ACCEPT_V6) + pCalloutString = "BASIC_PACKET_INJECTION_AT_ALE_AUTH_RECV_ACCEPT_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_ALE_AUTH_CONNECT_V4) + pCalloutString = "BASIC_PACKET_INJECTION_AT_ALE_AUTH_CONNECT_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_ALE_AUTH_CONNECT_V6) + pCalloutString = "BASIC_PACKET_INJECTION_AT_ALE_AUTH_CONNECT_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_ALE_FLOW_ESTABLISHED_V4) + pCalloutString = "BASIC_PACKET_INJECTION_AT_ALE_FLOW_ESTABLISHED_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_ALE_FLOW_ESTABLISHED_V6) + pCalloutString = "BASIC_PACKET_INJECTION_AT_ALE_FLOW_ESTABLISHED_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_STREAM_PACKET_V4) + pCalloutString = "BASIC_PACKET_INJECTION_AT_STREAM_PACKET_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_STREAM_PACKET_V6) + pCalloutString = "BASIC_PACKET_INJECTION_AT_STREAM_PACKET_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_INBOUND_MAC_FRAME_ETHERNET) + pCalloutString = "BASIC_PACKET_INJECTION_AT_INBOUND_MAC_FRAME_ETHERNET"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_OUTBOUND_MAC_FRAME_ETHERNET) + pCalloutString = "BASIC_PACKET_INJECTION_AT_OUTBOUND_MAC_FRAME_ETHERNET"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_INBOUND_MAC_FRAME_NATIVE) + pCalloutString = "BASIC_PACKET_INJECTION_AT_INBOUND_MAC_FRAME_NATIVE"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_OUTBOUND_MAC_FRAME_NATIVE) + pCalloutString = "BASIC_PACKET_INJECTION_AT_OUTBOUND_MAC_FRAME_NATIVE"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_INGRESS_VSWITCH_ETHERNET) + pCalloutString = "BASIC_PACKET_INJECTION_AT_INGRESS_VSWITCH_ETHERNET"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_EGRESS_VSWITCH_ETHERNET) + pCalloutString = "BASIC_PACKET_INJECTION_AT_EGRESS_VSWITCH_ETHERNET"; + } + else if(RtlCompareMemory(&WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION, + pCalloutKey, + NUM_MASKED_BYTES)) + { + if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_INBOUND_IPPACKET_V4) + pCalloutString = "BASIC_PACKET_MODIFICATION_AT_INBOUND_IPPACKET_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_INBOUND_IPPACKET_V6) + pCalloutString = "BASIC_PACKET_MODIFICATION_AT_INBOUND_IPPACKET_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_OUTBOUND_IPPACKET_V4) + pCalloutString = "BASIC_PACKET_MODIFICATION_AT_OUTBOUND_IPPACKET_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_OUTBOUND_IPPACKET_V6) + pCalloutString = "BASIC_PACKET_MODIFICATION_AT_OUTBOUND_IPPACKET_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_IPFORWARD_V4) + pCalloutString = "BASIC_PACKET_MODIFICATION_AT_IPFORWARD_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_IPFORWARD_V6) + pCalloutString = "BASIC_PACKET_MODIFICATION_AT_IPFORWARD_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_INBOUND_TRANSPORT_V4) + pCalloutString = "BASIC_PACKET_MODIFICATION_AT_INBOUND_TRANSPORT_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_INBOUND_TRANSPORT_V6) + pCalloutString = "BASIC_PACKET_MODIFICATION_AT_INBOUND_TRANSPORT_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_OUTBOUND_TRANSPORT_V4) + pCalloutString = "BASIC_PACKET_MODIFICATION_AT_OUTBOUND_TRANSPORT_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_OUTBOUND_TRANSPORT_V6) + pCalloutString = "BASIC_PACKET_MODIFICATION_AT_OUTBOUND_TRANSPORT_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_DATAGRAM_DATA_V4) + pCalloutString = "BASIC_PACKET_MODIFICATION_AT_DATAGRAM_DATA_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_DATAGRAM_DATA_V6) + pCalloutString = "BASIC_PACKET_MODIFICATION_AT_DATAGRAM_DATA_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_INBOUND_ICMP_ERROR_V4) + pCalloutString = "BASIC_PACKET_MODIFICATION_AT_INBOUND_ICMP_ERROR_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_INBOUND_ICMP_ERROR_V6) + pCalloutString = "BASIC_PACKET_MODIFICATION_AT_INBOUND_ICMP_ERROR_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_OUTBOUND_ICMP_ERROR_V4) + pCalloutString = "BASIC_PACKET_MODIFICATION_AT_OUTBOUND_ICMP_ERROR_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_OUTBOUND_ICMP_ERROR_V6) + pCalloutString = "BASIC_PACKET_MODIFICATION_AT_OUTBOUND_ICMP_ERROR_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_ALE_AUTH_RECV_ACCEPT_V4) + pCalloutString = "BASIC_PACKET_MODIFICATION_AT_ALE_AUTH_RECV_ACCEPT_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_ALE_AUTH_RECV_ACCEPT_V6) + pCalloutString = "BASIC_PACKET_MODIFICATION_AT_ALE_AUTH_RECV_ACCEPT_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_ALE_AUTH_CONNECT_V4) + pCalloutString = "BASIC_PACKET_MODIFICATION_AT_ALE_AUTH_CONNECT_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_ALE_AUTH_CONNECT_V6) + pCalloutString = "BASIC_PACKET_MODIFICATION_AT_ALE_AUTH_CONNECT_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_ALE_FLOW_ESTABLISHED_V4) + pCalloutString = "BASIC_PACKET_MODIFICATION_AT_ALE_FLOW_ESTABLISHED_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_ALE_FLOW_ESTABLISHED_V6) + pCalloutString = "BASIC_PACKET_MODIFICATION_AT_ALE_FLOW_ESTABLISHED_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_STREAM_PACKET_V4) + pCalloutString = "BASIC_PACKET_MODIFICATION_AT_STREAM_PACKET_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_STREAM_PACKET_V6) + pCalloutString = "BASIC_PACKET_MODIFICATION_AT_STREAM_PACKET_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_INBOUND_MAC_FRAME_ETHERNET) + pCalloutString = "BASIC_PACKET_MODIFICATION_AT_INBOUND_MAC_FRAME_ETHERNET"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_OUTBOUND_MAC_FRAME_ETHERNET) + pCalloutString = "BASIC_PACKET_MODIFICATION_AT_OUTBOUND_MAC_FRAME_ETHERNET"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_INBOUND_MAC_FRAME_NATIVE) + pCalloutString = "BASIC_PACKET_MODIFICATION_AT_INBOUND_MAC_FRAME_NATIVE"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_INBOUND_MAC_FRAME_NATIVE) + pCalloutString = "BASIC_PACKET_MODIFICATION_AT_OUTBOUND_MAC_FRAME_NATIVE"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_INGRESS_VSWITCH_ETHERNET) + pCalloutString = "BASIC_PACKET_MODIFICATION_AT_INGRESS_VSWITCH_ETHERNET"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_EGRESS_VSWITCH_ETHERNET) + pCalloutString = "BASIC_PACKET_MODIFICATION_AT_EGRESS_VSWITCH_ETHERNET"; + } + else if(RtlCompareMemory(&WFPSAMPLER_CALLOUT_BASIC_STREAM_INJECTION, + pCalloutKey, + NUM_MASKED_BYTES)) + { + if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_STREAM_INJECTION_AT_STREAM_V4) + pCalloutString = "BASIC_STREAM_INJECTION_AT_STREAM_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_BASIC_STREAM_INJECTION_AT_STREAM_V6) + pCalloutString = "BASIC_STREAM_INJECTION_AT_STREAM_V6"; + } + else if(RtlCompareMemory(&WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION, + pCalloutKey, + NUM_MASKED_BYTES)) + { + if(pCalloutKey == &WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_INBOUND_IPPACKET_V4) + pCalloutString = "FAST_PACKET_INJECTION_AT_INBOUND_IPPACKET_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_INBOUND_IPPACKET_V6) + pCalloutString = "FAST_PACKET_INJECTION_AT_INBOUND_IPPACKET_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_OUTBOUND_IPPACKET_V4) + pCalloutString = "FAST_PACKET_INJECTION_AT_OUTBOUND_IPPACKET_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_OUTBOUND_IPPACKET_V6) + pCalloutString = "FAST_PACKET_INJECTION_AT_OUTBOUND_IPPACKET_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_IPFORWARD_V4) + pCalloutString = "FAST_PACKET_INJECTION_AT_IPFORWARD_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_IPFORWARD_V6) + pCalloutString = "FAST_PACKET_INJECTION_AT_IPFORWARD_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_INBOUND_TRANSPORT_V4) + pCalloutString = "FAST_PACKET_INJECTION_AT_INBOUND_TRANSPORT_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_INBOUND_TRANSPORT_V6) + pCalloutString = "FAST_PACKET_INJECTION_AT_INBOUND_TRANSPORT_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_OUTBOUND_TRANSPORT_V4) + pCalloutString = "FAST_PACKET_INJECTION_AT_OUTBOUND_TRANSPORT_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_OUTBOUND_TRANSPORT_V6) + pCalloutString = "FAST_PACKET_INJECTION_AT_OUTBOUND_TRANSPORT_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_DATAGRAM_DATA_V4) + pCalloutString = "FAST_PACKET_INJECTION_AT_DATAGRAM_DATA_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_DATAGRAM_DATA_V6) + pCalloutString = "FAST_PACKET_INJECTION_AT_DATAGRAM_DATA_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_INBOUND_ICMP_ERROR_V4) + pCalloutString = "FAST_PACKET_INJECTION_AT_INBOUND_ICMP_ERROR_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_INBOUND_ICMP_ERROR_V6) + pCalloutString = "FAST_PACKET_INJECTION_AT_INBOUND_ICMP_ERROR_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_OUTBOUND_ICMP_ERROR_V4) + pCalloutString = "FAST_PACKET_INJECTION_AT_OUTBOUND_ICMP_ERROR_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_OUTBOUND_ICMP_ERROR_V6) + pCalloutString = "FAST_PACKET_INJECTION_AT_OUTBOUND_ICMP_ERROR_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_ALE_AUTH_RECV_ACCEPT_V4) + pCalloutString = "FAST_PACKET_INJECTION_AT_ALE_AUTH_RECV_ACCEPT_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_ALE_AUTH_RECV_ACCEPT_V6) + pCalloutString = "FAST_PACKET_INJECTION_AT_ALE_AUTH_RECV_ACCEPT_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_ALE_AUTH_CONNECT_V4) + pCalloutString = "FAST_PACKET_INJECTION_AT_ALE_AUTH_CONNECT_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_ALE_AUTH_CONNECT_V6) + pCalloutString = "FAST_PACKET_INJECTION_AT_ALE_AUTH_CONNECT_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_ALE_FLOW_ESTABLISHED_V4) + pCalloutString = "FAST_PACKET_INJECTION_AT_ALE_FLOW_ESTABLISHED_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_ALE_FLOW_ESTABLISHED_V6) + pCalloutString = "FAST_PACKET_INJECTION_AT_ALE_FLOW_ESTABLISHED_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_STREAM_PACKET_V4) + pCalloutString = "FAST_PACKET_INJECTION_AT_STREAM_PACKET_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_STREAM_PACKET_V6) + pCalloutString = "FAST_PACKET_INJECTION_AT_STREAM_PACKET_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_INBOUND_MAC_FRAME_ETHERNET) + pCalloutString = "FAST_PACKET_INJECTION_AT_INBOUND_MAC_FRAME_ETHERNET"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_OUTBOUND_MAC_FRAME_ETHERNET) + pCalloutString = "FAST_PACKET_INJECTION_AT_OUTBOUND_MAC_FRAME_ETHERNET"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_INBOUND_MAC_FRAME_NATIVE) + pCalloutString = "FAST_PACKET_INJECTION_AT_INBOUND_MAC_FRAME_NATIVE"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_OUTBOUND_MAC_FRAME_NATIVE) + pCalloutString = "FAST_PACKET_INJECTION_AT_OUTBOUND_MAC_FRAME_NATIVE"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_INGRESS_VSWITCH_ETHERNET) + pCalloutString = "FAST_PACKET_INJECTION_AT_INGRESS_VSWITCH_ETHERNET"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_EGRESS_VSWITCH_ETHERNET) + pCalloutString = "FAST_PACKET_INJECTION_AT_EGRESS_VSWITCH_ETHERNET"; + } + else if(RtlCompareMemory(&WFPSAMPLER_CALLOUT_FAST_STREAM_INJECTION, + pCalloutKey, + NUM_MASKED_BYTES)) + { + if(pCalloutKey == &WFPSAMPLER_CALLOUT_FAST_STREAM_INJECTION_AT_STREAM_V4) + pCalloutString = "FAST_STREAM_INJECTION_AT_STREAM_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_FAST_STREAM_INJECTION_AT_STREAM_V6) + pCalloutString = "FAST_STREAM_INJECTION_AT_STREAM_V6"; + } + else if(RtlCompareMemory(&WFPSAMPLER_CALLOUT_PEND_AUTHORIZATION, + pCalloutKey, + NUM_MASKED_BYTES)) + { + if(pCalloutKey == &WFPSAMPLER_CALLOUT_PEND_AUTHORIZATION_AT_ALE_RESOURCE_ASSIGNMENT_V4) + pCalloutString = "PEND_AUTHORIZATION_AT_ALE_RESOURCE_ASSIGNMENT_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_PEND_AUTHORIZATION_AT_ALE_RESOURCE_ASSIGNMENT_V6) + pCalloutString = "PEND_AUTHORIZATION_AT_ALE_RESOURCE_ASSIGNMENT_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_PEND_AUTHORIZATION_AT_ALE_AUTH_LISTEN_V4) + pCalloutString = "PEND_AUTHORIZATION_AT_ALE_AUTH_LISTEN_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_PEND_AUTHORIZATION_AT_ALE_AUTH_LISTEN_V6) + pCalloutString = "PEND_AUTHORIZATION_AT_ALE_AUTH_LISTEN_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_PEND_AUTHORIZATION_AT_ALE_AUTH_RECV_ACCEPT_V4) + pCalloutString = "PEND_AUTHORIZATION_AT_ALE_AUTH_RECV_ACCEPT_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_PEND_AUTHORIZATION_AT_ALE_AUTH_RECV_ACCEPT_V6) + pCalloutString = "PEND_AUTHORIZATION_AT_ALE_AUTH_RECV_ACCEPT_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_PEND_AUTHORIZATION_AT_ALE_AUTH_CONNECT_V4) + pCalloutString = "PEND_AUTHORIZATION_AT_ALE_AUTH_CONNECT_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_PEND_AUTHORIZATION_AT_ALE_AUTH_CONNECT_V6) + pCalloutString = "PEND_AUTHORIZATION_AT_ALE_AUTH_CONNECT_V6"; + } + else if(RtlCompareMemory(&WFPSAMPLER_CALLOUT_PEND_ENDPOINT_CLOSURE, + pCalloutKey, + NUM_MASKED_BYTES)) + { + if(pCalloutKey == &WFPSAMPLER_CALLOUT_PEND_ENDPOINT_CLOSURE_AT_ALE_ENDPOINT_CLOSURE_V4) + pCalloutString = "PEND_ENDPOINT_CLOSURE_AT_ALE_RESOURCE_ASSIGNMENT_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_PEND_ENDPOINT_CLOSURE_AT_ALE_ENDPOINT_CLOSURE_V6) + pCalloutString = "PEND_ENDPOINT_CLOSURE_AT_ALE_ENDPOINT_CLOSURE_V6"; + } + else if(RtlCompareMemory(&WFPSAMPLER_CALLOUT_PROXY_BY_ALE_REDIRECT, + pCalloutKey, + NUM_MASKED_BYTES)) + { + if(pCalloutKey == &WFPSAMPLER_CALLOUT_PROXY_BY_ALE_AT_CONNECT_REDIRECT_V4) + pCalloutString = "PROXY_BY_ALE_AT_CONNECT_REDIRECT_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_PROXY_BY_ALE_AT_CONNECT_REDIRECT_V6) + pCalloutString = "PROXY_BY_ALE_AT_CONNECT_REDIRECT_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_PROXY_BY_ALE_AT_BIND_REDIRECT_V4) + pCalloutString = "PROXY_BY_ALE_AT_BIND_REDIRECT_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_PROXY_BY_ALE_AT_BIND_REDIRECT_V6) + pCalloutString = "PROXY_BY_ALE_AT_BIND_REDIRECT_V6"; + } + else if(RtlCompareMemory(&WFPSAMPLER_CALLOUT_PROXY_BY_INJECTION, + pCalloutKey, + NUM_MASKED_BYTES)) + { + if(pCalloutKey == &WFPSAMPLER_CALLOUT_PROXY_BY_INJECTION_AT_INBOUND_IPPACKET_V4) + pCalloutString = "PROXY_BY_INJECTION_AT_INBOUND_IPPACKET_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_PROXY_BY_INJECTION_AT_INBOUND_IPPACKET_V6) + pCalloutString = "PROXY_BY_INJECTION_AT_INBOUND_IPPACKET_V6"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_PROXY_BY_INJECTION_AT_OUTBOUND_TRANSPORT_V4) + pCalloutString = "PROXY_BY_INJECTION_AT_OUTBOUND_TRANSPORT_V4"; + else if(pCalloutKey == &WFPSAMPLER_CALLOUT_PROXY_BY_INJECTION_AT_OUTBOUND_TRANSPORT_V6) + pCalloutString = "PROXY_BY_INJECTION_AT_INBOUND_TRANSPORT_V6"; + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprExposedCalloutToString()\n"); + +#endif /// DBG + + return pCalloutString; +} diff --git a/network/trans/WFPSampler/sys/HelperFunctions_ExposedCallouts.h b/network/trans/WFPSampler/sys/HelperFunctions_ExposedCallouts.h new file mode 100644 index 000000000..eb2e4f03e --- /dev/null +++ b/network/trans/WFPSampler/sys/HelperFunctions_ExposedCallouts.h @@ -0,0 +1,650 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_ExposedCallouts.h +// +// Abstract: +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Add support for ADVANCED_PACKET_INJECTION, +// FLOW_ASSOCIATION, and PEND_ENDPOINT_CLOSURE callouts +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#if DBG + +typedef struct INJECTION_COUNTERS_ +{ + INT64 inboundNetwork_IPv4; + INT64 inboundNetwork_IPv6; + INT64 outboundNetwork_IPv4; + INT64 outboundNetwork_IPv6; + INT64 inboundForward_IPv4; + INT64 inboundForward_IPv6; + INT64 outboundForward_IPv4; + INT64 outboundForward_IPv6; + INT64 inboundTransport_IPv4; + INT64 inboundTransport_IPv6; + INT64 outboundTransport_IPv4; + INT64 outboundTransport_IPv6; + INT64 inboundStream_IPv4; + INT64 inboundStream_IPv6; + INT64 outboundStream_IPv4; + INT64 outboundStream_IPv6; + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + INT64 inboundMAC_IPv4; + INT64 inboundMAC_IPv6; + INT64 inboundMAC_Unknown; + INT64 outboundMAC_IPv4; + INT64 outboundMAC_IPv6; + INT64 outboundMAC_Unknown; + INT64 ingressVSwitch_IPv4; + INT64 ingressVSwitch_IPv6; + INT64 ingressVSwitch_Unknown; + INT64 egressVSwitch_IPv4; + INT64 egressVSwitch_IPv6; + INT64 egressVSwitch_Unknown; + +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) + +}INJECTION_COUNTERS, *PINJECTION_COUNTERS; + +#endif /// DBG + +static const GUID* const ppAdvancedPacketInjection[] = {&WFPSAMPLER_CALLOUT_ADVANCED_PACKET_INJECTION_AT_INBOUND_IPPACKET_V4, + &WFPSAMPLER_CALLOUT_ADVANCED_PACKET_INJECTION_AT_INBOUND_IPPACKET_V6, + &WFPSAMPLER_CALLOUT_ADVANCED_PACKET_INJECTION_AT_OUTBOUND_IPPACKET_V4, + &WFPSAMPLER_CALLOUT_ADVANCED_PACKET_INJECTION_AT_OUTBOUND_IPPACKET_V6, + &WFPSAMPLER_CALLOUT_ADVANCED_PACKET_INJECTION_AT_IPFORWARD_V4, + &WFPSAMPLER_CALLOUT_ADVANCED_PACKET_INJECTION_AT_IPFORWARD_V6, + &WFPSAMPLER_CALLOUT_ADVANCED_PACKET_INJECTION_AT_INBOUND_TRANSPORT_V4, + &WFPSAMPLER_CALLOUT_ADVANCED_PACKET_INJECTION_AT_INBOUND_TRANSPORT_V6, + &WFPSAMPLER_CALLOUT_ADVANCED_PACKET_INJECTION_AT_OUTBOUND_TRANSPORT_V4, + &WFPSAMPLER_CALLOUT_ADVANCED_PACKET_INJECTION_AT_OUTBOUND_TRANSPORT_V6, + &WFPSAMPLER_CALLOUT_ADVANCED_PACKET_INJECTION_AT_DATAGRAM_DATA_V4, + &WFPSAMPLER_CALLOUT_ADVANCED_PACKET_INJECTION_AT_DATAGRAM_DATA_V6, + &WFPSAMPLER_CALLOUT_ADVANCED_PACKET_INJECTION_AT_INBOUND_ICMP_ERROR_V4, + &WFPSAMPLER_CALLOUT_ADVANCED_PACKET_INJECTION_AT_INBOUND_ICMP_ERROR_V6, + &WFPSAMPLER_CALLOUT_ADVANCED_PACKET_INJECTION_AT_OUTBOUND_ICMP_ERROR_V4, + &WFPSAMPLER_CALLOUT_ADVANCED_PACKET_INJECTION_AT_OUTBOUND_ICMP_ERROR_V6, + &WFPSAMPLER_CALLOUT_ADVANCED_PACKET_INJECTION_AT_ALE_AUTH_RECV_ACCEPT_V4, + &WFPSAMPLER_CALLOUT_ADVANCED_PACKET_INJECTION_AT_ALE_AUTH_RECV_ACCEPT_V6, + &WFPSAMPLER_CALLOUT_ADVANCED_PACKET_INJECTION_AT_ALE_AUTH_CONNECT_V4, + &WFPSAMPLER_CALLOUT_ADVANCED_PACKET_INJECTION_AT_ALE_AUTH_CONNECT_V6, + &WFPSAMPLER_CALLOUT_ADVANCED_PACKET_INJECTION_AT_ALE_FLOW_ESTABLISHED_V4, + &WFPSAMPLER_CALLOUT_ADVANCED_PACKET_INJECTION_AT_ALE_FLOW_ESTABLISHED_V6, + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + &WFPSAMPLER_CALLOUT_ADVANCED_PACKET_INJECTION_AT_STREAM_PACKET_V4, + &WFPSAMPLER_CALLOUT_ADVANCED_PACKET_INJECTION_AT_STREAM_PACKET_V6, + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + &WFPSAMPLER_CALLOUT_ADVANCED_PACKET_INJECTION_AT_INBOUND_MAC_FRAME_ETHERNET, + &WFPSAMPLER_CALLOUT_ADVANCED_PACKET_INJECTION_AT_OUTBOUND_MAC_FRAME_ETHERNET, + &WFPSAMPLER_CALLOUT_ADVANCED_PACKET_INJECTION_AT_INBOUND_MAC_FRAME_NATIVE, + &WFPSAMPLER_CALLOUT_ADVANCED_PACKET_INJECTION_AT_OUTBOUND_MAC_FRAME_NATIVE, + &WFPSAMPLER_CALLOUT_ADVANCED_PACKET_INJECTION_AT_INGRESS_VSWITCH_ETHERNET, + &WFPSAMPLER_CALLOUT_ADVANCED_PACKET_INJECTION_AT_EGRESS_VSWITCH_ETHERNET, + +#endif // (NTDDI_VERSION >= NTDDI_WIN8) +#endif // (NTDDI_VERSION >= NTDDI_WIN7) + + }; +static const GUID* const ppBasicActionBlock[] = {&WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_INBOUND_IPPACKET_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_INBOUND_IPPACKET_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_OUTBOUND_IPPACKET_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_OUTBOUND_IPPACKET_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_IPFORWARD_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_IPFORWARD_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_INBOUND_TRANSPORT_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_INBOUND_TRANSPORT_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_OUTBOUND_TRANSPORT_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_OUTBOUND_TRANSPORT_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_STREAM_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_STREAM_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_DATAGRAM_DATA_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_DATAGRAM_DATA_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_INBOUND_ICMP_ERROR_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_INBOUND_ICMP_ERROR_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_OUTBOUND_ICMP_ERROR_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_OUTBOUND_ICMP_ERROR_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_ALE_RESOURCE_ASSIGNMENT_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_ALE_RESOURCE_ASSIGNMENT_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_ALE_AUTH_LISTEN_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_ALE_AUTH_LISTEN_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_ALE_AUTH_RECV_ACCEPT_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_ALE_AUTH_RECV_ACCEPT_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_ALE_AUTH_CONNECT_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_ALE_AUTH_CONNECT_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_ALE_FLOW_ESTABLISHED_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_ALE_FLOW_ESTABLISHED_V6, + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_ALE_RESOURCE_RELEASE_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_ALE_RESOURCE_RELEASE_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_ALE_ENDPOINT_CLOSURE_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_ALE_ENDPOINT_CLOSURE_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_ALE_CONNECT_REDIRECT_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_ALE_CONNECT_REDIRECT_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_ALE_BIND_REDIRECT_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_ALE_BIND_REDIRECT_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_STREAM_PACKET_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_STREAM_PACKET_V6, + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_INBOUND_MAC_FRAME_ETHERNET, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_OUTBOUND_MAC_FRAME_ETHERNET, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_INBOUND_MAC_FRAME_NATIVE, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_OUTBOUND_MAC_FRAME_NATIVE, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_INGRESS_VSWITCH_ETHERNET, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_EGRESS_VSWITCH_ETHERNET, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_INGRESS_VSWITCH_TRANSPORT_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_INGRESS_VSWITCH_TRANSPORT_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_EGRESS_VSWITCH_TRANSPORT_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_BLOCK_AT_EGRESS_VSWITCH_TRANSPORT_V6, + +#endif // (NTDDI_VERSION >= NTDDI_WIN8) +#endif // (NTDDI_VERSION >= NTDDI_WIN7) + + }; +static const GUID* const ppBasicActionContinue[] = {&WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_INBOUND_IPPACKET_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_INBOUND_IPPACKET_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_OUTBOUND_IPPACKET_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_OUTBOUND_IPPACKET_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_IPFORWARD_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_IPFORWARD_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_INBOUND_TRANSPORT_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_INBOUND_TRANSPORT_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_OUTBOUND_TRANSPORT_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_OUTBOUND_TRANSPORT_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_STREAM_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_STREAM_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_DATAGRAM_DATA_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_DATAGRAM_DATA_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_INBOUND_ICMP_ERROR_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_INBOUND_ICMP_ERROR_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_OUTBOUND_ICMP_ERROR_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_OUTBOUND_ICMP_ERROR_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_ALE_RESOURCE_ASSIGNMENT_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_ALE_RESOURCE_ASSIGNMENT_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_ALE_AUTH_LISTEN_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_ALE_AUTH_LISTEN_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_ALE_AUTH_RECV_ACCEPT_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_ALE_AUTH_RECV_ACCEPT_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_ALE_AUTH_CONNECT_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_ALE_AUTH_CONNECT_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_ALE_FLOW_ESTABLISHED_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_ALE_FLOW_ESTABLISHED_V6, + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_ALE_RESOURCE_RELEASE_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_ALE_RESOURCE_RELEASE_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_ALE_ENDPOINT_CLOSURE_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_ALE_ENDPOINT_CLOSURE_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_ALE_CONNECT_REDIRECT_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_ALE_CONNECT_REDIRECT_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_ALE_BIND_REDIRECT_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_ALE_BIND_REDIRECT_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_STREAM_PACKET_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_STREAM_PACKET_V6, + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_INBOUND_MAC_FRAME_ETHERNET, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_OUTBOUND_MAC_FRAME_ETHERNET, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_INBOUND_MAC_FRAME_NATIVE, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_OUTBOUND_MAC_FRAME_NATIVE, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_INGRESS_VSWITCH_ETHERNET, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_EGRESS_VSWITCH_ETHERNET, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_INGRESS_VSWITCH_TRANSPORT_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_INGRESS_VSWITCH_TRANSPORT_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_EGRESS_VSWITCH_TRANSPORT_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_CONTINUE_AT_EGRESS_VSWITCH_TRANSPORT_V6, + +#endif // (NTDDI_VERSION >= NTDDI_WIN8) +#endif // (NTDDI_VERSION >= NTDDI_WIN7) + + }; +static const GUID* const ppBasicActionPermit[] = {&WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_INBOUND_IPPACKET_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_INBOUND_IPPACKET_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_OUTBOUND_IPPACKET_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_OUTBOUND_IPPACKET_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_IPFORWARD_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_IPFORWARD_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_INBOUND_TRANSPORT_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_INBOUND_TRANSPORT_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_OUTBOUND_TRANSPORT_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_OUTBOUND_TRANSPORT_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_STREAM_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_STREAM_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_DATAGRAM_DATA_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_DATAGRAM_DATA_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_INBOUND_ICMP_ERROR_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_INBOUND_ICMP_ERROR_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_OUTBOUND_ICMP_ERROR_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_OUTBOUND_ICMP_ERROR_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_ALE_RESOURCE_ASSIGNMENT_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_ALE_RESOURCE_ASSIGNMENT_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_ALE_AUTH_LISTEN_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_ALE_AUTH_LISTEN_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_ALE_AUTH_RECV_ACCEPT_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_ALE_AUTH_RECV_ACCEPT_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_ALE_AUTH_CONNECT_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_ALE_AUTH_CONNECT_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_ALE_FLOW_ESTABLISHED_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_ALE_FLOW_ESTABLISHED_V6, + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_ALE_RESOURCE_RELEASE_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_ALE_RESOURCE_RELEASE_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_ALE_ENDPOINT_CLOSURE_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_ALE_ENDPOINT_CLOSURE_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_ALE_CONNECT_REDIRECT_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_ALE_CONNECT_REDIRECT_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_ALE_BIND_REDIRECT_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_ALE_BIND_REDIRECT_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_STREAM_PACKET_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_STREAM_PACKET_V6, + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_INBOUND_MAC_FRAME_ETHERNET, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_OUTBOUND_MAC_FRAME_ETHERNET, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_INBOUND_MAC_FRAME_NATIVE, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_OUTBOUND_MAC_FRAME_NATIVE, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_INGRESS_VSWITCH_ETHERNET, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_EGRESS_VSWITCH_ETHERNET, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_INGRESS_VSWITCH_TRANSPORT_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_INGRESS_VSWITCH_TRANSPORT_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_EGRESS_VSWITCH_TRANSPORT_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_PERMIT_AT_EGRESS_VSWITCH_TRANSPORT_V6, + +#endif // (NTDDI_VERSION >= NTDDI_WIN8) +#endif // (NTDDI_VERSION >= NTDDI_WIN7) + + }; +static const GUID* const ppBasicActionRandom[] = {&WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_INBOUND_IPPACKET_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_INBOUND_IPPACKET_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_OUTBOUND_IPPACKET_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_OUTBOUND_IPPACKET_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_IPFORWARD_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_IPFORWARD_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_INBOUND_TRANSPORT_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_INBOUND_TRANSPORT_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_OUTBOUND_TRANSPORT_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_OUTBOUND_TRANSPORT_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_STREAM_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_STREAM_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_DATAGRAM_DATA_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_DATAGRAM_DATA_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_INBOUND_ICMP_ERROR_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_INBOUND_ICMP_ERROR_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_OUTBOUND_ICMP_ERROR_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_OUTBOUND_ICMP_ERROR_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_ALE_RESOURCE_ASSIGNMENT_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_ALE_RESOURCE_ASSIGNMENT_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_ALE_AUTH_LISTEN_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_ALE_AUTH_LISTEN_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_ALE_AUTH_RECV_ACCEPT_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_ALE_AUTH_RECV_ACCEPT_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_ALE_AUTH_CONNECT_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_ALE_AUTH_CONNECT_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_ALE_FLOW_ESTABLISHED_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_ALE_FLOW_ESTABLISHED_V6, + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_ALE_RESOURCE_RELEASE_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_ALE_RESOURCE_RELEASE_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_ALE_ENDPOINT_CLOSURE_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_ALE_ENDPOINT_CLOSURE_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_ALE_CONNECT_REDIRECT_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_ALE_CONNECT_REDIRECT_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_ALE_BIND_REDIRECT_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_ALE_BIND_REDIRECT_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_STREAM_PACKET_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_STREAM_PACKET_V6, + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_INBOUND_MAC_FRAME_ETHERNET, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_OUTBOUND_MAC_FRAME_ETHERNET, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_INBOUND_MAC_FRAME_NATIVE, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_OUTBOUND_MAC_FRAME_NATIVE, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_INGRESS_VSWITCH_ETHERNET, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_EGRESS_VSWITCH_ETHERNET, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_INGRESS_VSWITCH_TRANSPORT_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_INGRESS_VSWITCH_TRANSPORT_V6, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_EGRESS_VSWITCH_TRANSPORT_V4, + &WFPSAMPLER_CALLOUT_BASIC_ACTION_RANDOM_AT_EGRESS_VSWITCH_TRANSPORT_V6, + +#endif // (NTDDI_VERSION >= NTDDI_WIN8) +#endif // (NTDDI_VERSION >= NTDDI_WIN7) + + }; +static const GUID* const ppBasicPacketExamination[] = {&WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_INBOUND_IPPACKET_V4, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_INBOUND_IPPACKET_V4_DISCARD, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_INBOUND_IPPACKET_V6, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_INBOUND_IPPACKET_V6_DISCARD, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_OUTBOUND_IPPACKET_V4, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_OUTBOUND_IPPACKET_V4_DISCARD, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_OUTBOUND_IPPACKET_V6, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_OUTBOUND_IPPACKET_V6_DISCARD, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_IPFORWARD_V4, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_IPFORWARD_V4_DISCARD, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_IPFORWARD_V6, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_IPFORWARD_V6_DISCARD, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_INBOUND_TRANSPORT_V4, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_INBOUND_TRANSPORT_V4_DISCARD, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_INBOUND_TRANSPORT_V6, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_INBOUND_TRANSPORT_V6_DISCARD, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_OUTBOUND_TRANSPORT_V4, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_OUTBOUND_TRANSPORT_V4_DISCARD, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_OUTBOUND_TRANSPORT_V6, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_OUTBOUND_TRANSPORT_V6_DISCARD, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_STREAM_V4, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_STREAM_V4_DISCARD, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_STREAM_V6, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_STREAM_V6_DISCARD, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_DATAGRAM_DATA_V4, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_DATAGRAM_DATA_V4_DISCARD, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_DATAGRAM_DATA_V6, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_DATAGRAM_DATA_V6_DISCARD, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_INBOUND_ICMP_ERROR_V4, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_INBOUND_ICMP_ERROR_V4_DISCARD, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_INBOUND_ICMP_ERROR_V6, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_INBOUND_ICMP_ERROR_V6_DISCARD, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_OUTBOUND_ICMP_ERROR_V4, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_OUTBOUND_ICMP_ERROR_V4_DISCARD, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_OUTBOUND_ICMP_ERROR_V6, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_OUTBOUND_ICMP_ERROR_V6_DISCARD, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_ALE_RESOURCE_ASSIGNMENT_V4, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_ALE_RESOURCE_ASSIGNMENT_V4_DISCARD, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_ALE_RESOURCE_ASSIGNMENT_V6, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_ALE_RESOURCE_ASSIGNMENT_V6_DISCARD, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_ALE_AUTH_LISTEN_V4, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_ALE_AUTH_LISTEN_V4_DISCARD, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_ALE_AUTH_LISTEN_V6, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_ALE_AUTH_LISTEN_V6_DISCARD, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_ALE_AUTH_RECV_ACCEPT_V4, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_ALE_AUTH_RECV_ACCEPT_V4_DISCARD, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_ALE_AUTH_RECV_ACCEPT_V6, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_ALE_AUTH_RECV_ACCEPT_V6_DISCARD, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_ALE_AUTH_CONNECT_V4, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_ALE_AUTH_CONNECT_V4_DISCARD, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_ALE_AUTH_CONNECT_V6, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_ALE_AUTH_CONNECT_V6_DISCARD, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_ALE_FLOW_ESTABLISHED_V4, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_ALE_FLOW_ESTABLISHED_V4_DISCARD, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_ALE_FLOW_ESTABLISHED_V6, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_ALE_FLOW_ESTABLISHED_V6_DISCARD, + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_ALE_RESOURCE_RELEASE_V4, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_ALE_RESOURCE_RELEASE_V6, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_ALE_ENDPOINT_CLOSURE_V4, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_ALE_ENDPOINT_CLOSURE_V6, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_ALE_CONNECT_REDIRECT_V4, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_ALE_CONNECT_REDIRECT_V6, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_ALE_BIND_REDIRECT_V4, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_ALE_BIND_REDIRECT_V6, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_STREAM_PACKET_V4, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_STREAM_PACKET_V6, + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_INBOUND_MAC_FRAME_ETHERNET, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_OUTBOUND_MAC_FRAME_ETHERNET, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_INBOUND_MAC_FRAME_NATIVE, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_OUTBOUND_MAC_FRAME_NATIVE, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_INGRESS_VSWITCH_ETHERNET, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_EGRESS_VSWITCH_ETHERNET, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_INGRESS_VSWITCH_TRANSPORT_V4, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_INGRESS_VSWITCH_TRANSPORT_V6, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_EGRESS_VSWITCH_TRANSPORT_V4, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_EXAMINATION_AT_EGRESS_VSWITCH_TRANSPORT_V6, + +#endif // (NTDDI_VERSION >= NTDDI_WIN8) +#endif // (NTDDI_VERSION >= NTDDI_WIN7) + + }; +static const GUID* const ppBasicPacketInjection[] = {&WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_INBOUND_IPPACKET_V4, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_INBOUND_IPPACKET_V6, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_OUTBOUND_IPPACKET_V4, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_OUTBOUND_IPPACKET_V6, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_IPFORWARD_V4, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_IPFORWARD_V6, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_INBOUND_TRANSPORT_V4, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_INBOUND_TRANSPORT_V6, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_OUTBOUND_TRANSPORT_V4, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_OUTBOUND_TRANSPORT_V6, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_DATAGRAM_DATA_V4, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_DATAGRAM_DATA_V6, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_INBOUND_ICMP_ERROR_V4, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_INBOUND_ICMP_ERROR_V6, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_OUTBOUND_ICMP_ERROR_V4, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_OUTBOUND_ICMP_ERROR_V6, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_ALE_AUTH_RECV_ACCEPT_V4, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_ALE_AUTH_RECV_ACCEPT_V6, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_ALE_AUTH_CONNECT_V4, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_ALE_AUTH_CONNECT_V6, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_ALE_FLOW_ESTABLISHED_V4, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_ALE_FLOW_ESTABLISHED_V6, + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + &WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_STREAM_PACKET_V4, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_STREAM_PACKET_V6, + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + &WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_INBOUND_MAC_FRAME_ETHERNET, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_OUTBOUND_MAC_FRAME_ETHERNET, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_INBOUND_MAC_FRAME_NATIVE, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_OUTBOUND_MAC_FRAME_NATIVE, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_INGRESS_VSWITCH_ETHERNET, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_INJECTION_AT_EGRESS_VSWITCH_ETHERNET, + +#endif // (NTDDI_VERSION >= NTDDI_WIN8) +#endif // (NTDDI_VERSION >= NTDDI_WIN7) + + }; +static const GUID* const ppBasicPacketModification[] = {&WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_INBOUND_IPPACKET_V4, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_INBOUND_IPPACKET_V6, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_OUTBOUND_IPPACKET_V4, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_OUTBOUND_IPPACKET_V6, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_IPFORWARD_V4, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_IPFORWARD_V6, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_INBOUND_TRANSPORT_V4, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_INBOUND_TRANSPORT_V6, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_OUTBOUND_TRANSPORT_V4, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_OUTBOUND_TRANSPORT_V6, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_DATAGRAM_DATA_V4, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_DATAGRAM_DATA_V6, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_INBOUND_ICMP_ERROR_V4, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_INBOUND_ICMP_ERROR_V6, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_OUTBOUND_ICMP_ERROR_V4, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_OUTBOUND_ICMP_ERROR_V6, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_ALE_AUTH_RECV_ACCEPT_V4, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_ALE_AUTH_RECV_ACCEPT_V6, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_ALE_AUTH_CONNECT_V4, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_ALE_AUTH_CONNECT_V6, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_ALE_FLOW_ESTABLISHED_V4, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_ALE_FLOW_ESTABLISHED_V6, + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + &WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_STREAM_PACKET_V4, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_STREAM_PACKET_V6, + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + &WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_INBOUND_MAC_FRAME_ETHERNET, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_OUTBOUND_MAC_FRAME_ETHERNET, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_INBOUND_MAC_FRAME_NATIVE, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_OUTBOUND_MAC_FRAME_NATIVE, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_INGRESS_VSWITCH_ETHERNET, + &WFPSAMPLER_CALLOUT_BASIC_PACKET_MODIFICATION_AT_EGRESS_VSWITCH_ETHERNET, + +#endif // (NTDDI_VERSION >= NTDDI_WIN8) +#endif // (NTDDI_VERSION >= NTDDI_WIN7) + + }; +static const GUID* const ppBasicStreamInjection[] = {&WFPSAMPLER_CALLOUT_BASIC_STREAM_INJECTION_AT_STREAM_V4, + &WFPSAMPLER_CALLOUT_BASIC_STREAM_INJECTION_AT_STREAM_V6, + }; +static const GUID* const ppFastPacketInjection[] = {&WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_INBOUND_IPPACKET_V4, + &WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_INBOUND_IPPACKET_V6, + &WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_OUTBOUND_IPPACKET_V4, + &WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_OUTBOUND_IPPACKET_V6, + &WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_IPFORWARD_V4, + &WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_IPFORWARD_V6, + &WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_INBOUND_TRANSPORT_V4, + &WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_INBOUND_TRANSPORT_V6, + &WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_OUTBOUND_TRANSPORT_V4, + &WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_OUTBOUND_TRANSPORT_V6, + &WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_DATAGRAM_DATA_V4, + &WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_DATAGRAM_DATA_V6, + &WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_INBOUND_ICMP_ERROR_V4, + &WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_INBOUND_ICMP_ERROR_V6, + &WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_OUTBOUND_ICMP_ERROR_V4, + &WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_OUTBOUND_ICMP_ERROR_V6, + &WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_ALE_AUTH_RECV_ACCEPT_V4, + &WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_ALE_AUTH_RECV_ACCEPT_V6, + &WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_ALE_AUTH_CONNECT_V4, + &WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_ALE_AUTH_CONNECT_V6, + &WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_ALE_FLOW_ESTABLISHED_V4, + &WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_ALE_FLOW_ESTABLISHED_V6, + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + &WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_STREAM_PACKET_V4, + &WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_STREAM_PACKET_V6, + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + &WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_INBOUND_MAC_FRAME_ETHERNET, + &WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_OUTBOUND_MAC_FRAME_ETHERNET, + &WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_INBOUND_MAC_FRAME_NATIVE, + &WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_OUTBOUND_MAC_FRAME_NATIVE, + &WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_INGRESS_VSWITCH_ETHERNET, + &WFPSAMPLER_CALLOUT_FAST_PACKET_INJECTION_AT_EGRESS_VSWITCH_ETHERNET, + +#endif // (NTDDI_VERSION >= NTDDI_WIN8) +#endif // (NTDDI_VERSION >= NTDDI_WIN7) + + }; +static const GUID* const ppFastStreamInjection[] = {&WFPSAMPLER_CALLOUT_FAST_STREAM_INJECTION_AT_STREAM_V4, + &WFPSAMPLER_CALLOUT_FAST_STREAM_INJECTION_AT_STREAM_V6, + }; +static const GUID* const ppFlowAssociation[] = {&WFPSAMPLER_CALLOUT_FLOW_ASSOCIATION_AT_ALE_FLOW_ESTABLISHED_V4, + &WFPSAMPLER_CALLOUT_FLOW_ASSOCIATION_AT_ALE_FLOW_ESTABLISHED_V6, + }; +static const GUID* const ppPendAuthorization[] = {&WFPSAMPLER_CALLOUT_PEND_AUTHORIZATION_AT_ALE_RESOURCE_ASSIGNMENT_V4, + &WFPSAMPLER_CALLOUT_PEND_AUTHORIZATION_AT_ALE_RESOURCE_ASSIGNMENT_V6, + &WFPSAMPLER_CALLOUT_PEND_AUTHORIZATION_AT_ALE_AUTH_LISTEN_V4, + &WFPSAMPLER_CALLOUT_PEND_AUTHORIZATION_AT_ALE_AUTH_LISTEN_V6, + &WFPSAMPLER_CALLOUT_PEND_AUTHORIZATION_AT_ALE_AUTH_RECV_ACCEPT_V4, + &WFPSAMPLER_CALLOUT_PEND_AUTHORIZATION_AT_ALE_AUTH_RECV_ACCEPT_V6, + &WFPSAMPLER_CALLOUT_PEND_AUTHORIZATION_AT_ALE_AUTH_CONNECT_V4, + &WFPSAMPLER_CALLOUT_PEND_AUTHORIZATION_AT_ALE_AUTH_CONNECT_V6, + }; +static const GUID* const ppPendEndpointClosure[] = {&WFPSAMPLER_CALLOUT_PEND_ENDPOINT_CLOSURE_AT_ALE_ENDPOINT_CLOSURE_V4, + &WFPSAMPLER_CALLOUT_PEND_ENDPOINT_CLOSURE_AT_ALE_ENDPOINT_CLOSURE_V6, + }; +static const GUID* const ppProxyByALERedirect[] = {&WFPSAMPLER_CALLOUT_PROXY_BY_ALE_AT_CONNECT_REDIRECT_V4, + &WFPSAMPLER_CALLOUT_PROXY_BY_ALE_AT_CONNECT_REDIRECT_V6, + &WFPSAMPLER_CALLOUT_PROXY_BY_ALE_AT_BIND_REDIRECT_V4, + &WFPSAMPLER_CALLOUT_PROXY_BY_ALE_AT_BIND_REDIRECT_V6, + }; +static const GUID* const ppProxyByInjection[] = {&WFPSAMPLER_CALLOUT_PROXY_BY_INJECTION_AT_INBOUND_IPPACKET_V4, + &WFPSAMPLER_CALLOUT_PROXY_BY_INJECTION_AT_INBOUND_IPPACKET_V6, + &WFPSAMPLER_CALLOUT_PROXY_BY_INJECTION_AT_OUTBOUND_TRANSPORT_V4, + &WFPSAMPLER_CALLOUT_PROXY_BY_INJECTION_AT_OUTBOUND_TRANSPORT_V6, + }; + + +const UINT32 ADVANCED_PACKET_INJECTION_COUNT = RTL_NUMBER_OF(ppAdvancedPacketInjection); +const UINT32 BASIC_ACTION_BLOCK_COUNT = RTL_NUMBER_OF(ppBasicActionBlock); +const UINT32 BASIC_ACTION_CONTINUE_COUNT = RTL_NUMBER_OF(ppBasicActionContinue); +const UINT32 BASIC_ACTION_PERMIT_COUNT = RTL_NUMBER_OF(ppBasicActionPermit); +const UINT32 BASIC_ACTION_RANDOM_COUNT = RTL_NUMBER_OF(ppBasicActionRandom); +const UINT32 BASIC_PACKET_EXAMINATION_COUNT = RTL_NUMBER_OF(ppBasicPacketExamination); +const UINT32 BASIC_PACKET_INJECTION_COUNT = RTL_NUMBER_OF(ppBasicPacketInjection); +const UINT32 BASIC_PACKET_MODIFICATION_COUNT = RTL_NUMBER_OF(ppBasicPacketModification); +const UINT32 BASIC_STREAM_INJECTION_COUNT = RTL_NUMBER_OF(ppBasicStreamInjection); +const UINT32 FAST_PACKET_INJECTION_COUNT = RTL_NUMBER_OF(ppFastPacketInjection); +const UINT32 FAST_STREAM_INJECTION_COUNT = RTL_NUMBER_OF(ppFastStreamInjection); +const UINT32 FLOW_ASSOCIATION_COUNT = RTL_NUMBER_OF(ppFlowAssociation); +const UINT32 PEND_AUTHORIZATION_COUNT = RTL_NUMBER_OF(ppPendAuthorization); +const UINT32 PEND_ENDPOINT_CLOSURE_COUNT = RTL_NUMBER_OF(ppPendEndpointClosure); +const UINT32 PROXY_BY_ALE_REDIRECT_COUNT = RTL_NUMBER_OF(ppProxyByALERedirect); +const UINT32 PROXY_BY_INJECTION_COUNT = RTL_NUMBER_OF(ppProxyByInjection); + +const UINT32 EXPOSED_CALLOUT_COUNT = ADVANCED_PACKET_INJECTION_COUNT + + BASIC_ACTION_BLOCK_COUNT + + BASIC_ACTION_CONTINUE_COUNT + + BASIC_ACTION_PERMIT_COUNT + + BASIC_ACTION_RANDOM_COUNT + + BASIC_PACKET_EXAMINATION_COUNT + + BASIC_PACKET_INJECTION_COUNT + + BASIC_PACKET_MODIFICATION_COUNT + + BASIC_STREAM_INJECTION_COUNT + + FAST_PACKET_INJECTION_COUNT + + FAST_STREAM_INJECTION_COUNT + + FLOW_ASSOCIATION_COUNT + + PEND_AUTHORIZATION_COUNT + + PEND_ENDPOINT_CLOSURE_COUNT + + PROXY_BY_ALE_REDIRECT_COUNT + + PROXY_BY_INJECTION_COUNT; + +/// Indices of where each group of callouts begins in an uber array (created in HelperFunctions_ExposedCallouts.cpp) +const UINT32 BASE_INDEX_API = 0; +const UINT32 BASE_INDEX_BAB = BASE_INDEX_API + ADVANCED_PACKET_INJECTION_COUNT; +const UINT32 BASE_INDEX_BAC = BASE_INDEX_BAB + BASIC_ACTION_BLOCK_COUNT; +const UINT32 BASE_INDEX_BAP = BASE_INDEX_BAC + BASIC_ACTION_CONTINUE_COUNT; +const UINT32 BASE_INDEX_BAR = BASE_INDEX_BAP + BASIC_ACTION_PERMIT_COUNT; +const UINT32 BASE_INDEX_BPE = BASE_INDEX_BAR + BASIC_ACTION_RANDOM_COUNT; +const UINT32 BASE_INDEX_BPI = BASE_INDEX_BPE + BASIC_PACKET_EXAMINATION_COUNT; +const UINT32 BASE_INDEX_BPM = BASE_INDEX_BPI + BASIC_PACKET_INJECTION_COUNT; +const UINT32 BASE_INDEX_BSI = BASE_INDEX_BPM + BASIC_PACKET_MODIFICATION_COUNT; +const UINT32 BASE_INDEX_FPI = BASE_INDEX_BSI + BASIC_STREAM_INJECTION_COUNT; +const UINT32 BASE_INDEX_FSI = BASE_INDEX_FPI + FAST_PACKET_INJECTION_COUNT; +const UINT32 BASE_INDEX_FA = BASE_INDEX_FSI + FAST_STREAM_INJECTION_COUNT; +const UINT32 BASE_INDEX_PA = BASE_INDEX_FA + FLOW_ASSOCIATION_COUNT; +const UINT32 BASE_INDEX_PEC = BASE_INDEX_PA + PEND_AUTHORIZATION_COUNT; +const UINT32 BASE_INDEX_PBA = BASE_INDEX_PEC + PEND_ENDPOINT_CLOSURE_COUNT; +const UINT32 BASE_INDEX_PBI = BASE_INDEX_PBA + PROXY_BY_ALE_REDIRECT_COUNT; + +_IRQL_requires_(PASSIVE_LEVEL) +_IRQL_requires_same_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprExposedCalloutsUnregister(); + +_IRQL_requires_(PASSIVE_LEVEL) +_IRQL_requires_same_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprExposedCalloutsRegister(); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(return != 0) +PSTR KrnlHlprExposedCalloutToString(_In_ const GUID* pCalloutKey); diff --git a/network/trans/WFPSampler/sys/NotifyFunctions_AdvancedCallouts.cpp b/network/trans/WFPSampler/sys/NotifyFunctions_AdvancedCallouts.cpp new file mode 100644 index 000000000..18c938245 --- /dev/null +++ b/network/trans/WFPSampler/sys/NotifyFunctions_AdvancedCallouts.cpp @@ -0,0 +1,223 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// NotifyFunctions_AdvancedCallouts.cpp +// +// Abstract: +// This module contains WFP Notify functions for the simpler callouts. +// +// Naming Convention: +// +// +// +// i.e. +// +// NotifyAdvancedNotification +// +// +// Notify - Function is an FWPS_CALLOUT_NOTIFY_FN +// +// AdvancedNotification - Function demonstates use of the simple notifications +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// December 13, 2013 - 1.1 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "Framework_WFPSamplerCalloutDriver.h" /// . +#include "NotifyFunctions_AdvancedCallouts.tmh" /// $(OBJ_PATH)\$(O)\ + +/** + @private_function="PrvAdvancedNotificationWorkItemRoutine" + + Purpose: Traces the appropriate notification event.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_IRQL_requires_(PASSIVE_LEVEL) +_IRQL_requires_same_ +_Function_class_(IO_WORKITEM_ROUTINE) +VOID PrvAdvancedNotificationWorkItemRoutine(_In_ PDEVICE_OBJECT pDeviceObject, + _Inout_opt_ PVOID pContext) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PrvAdvancedNotificationWorkItemRoutine()\n"); + +#endif /// DBG + + UNREFERENCED_PARAMETER(pDeviceObject); + + NT_ASSERT(pContext); + NT_ASSERT(((WORKITEM_DATA*)pContext)->pNotifyData); + + + WORKITEM_DATA* pWorkItemData = (WORKITEM_DATA*)pContext; + + if(pWorkItemData) + { + NTSTATUS status = STATUS_SUCCESS; + FWPM_CALLOUT* pCallout = 0; + PWSTR pCalloutName = L""; + + status = FwpmCalloutGetById(g_EngineHandle, + pWorkItemData->pNotifyData->calloutID, + &pCallout); + if(status != STATUS_SUCCESS) + { + if(status != STATUS_FWP_CALLOUT_NOT_FOUND) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! [FilterID: %#I64x][CalloutID: %#x] PrvAdvancedNotificationWorkItemRoutine : FwpmCalloutGetById() [status: %#x]\n", + pWorkItemData->pNotifyData->filterID, + pWorkItemData->pNotifyData->calloutID, + status); + } + else + pCalloutName = pCallout->displayData.name; + + switch(pWorkItemData->pNotifyData->notificationType) + { + case FWPS_CALLOUT_NOTIFY_ADD_FILTER: + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " -- [FilterID: %#I64x][CalloutID: %#x] A filter referencing %S callout was added\n", + pWorkItemData->pNotifyData->filterID, + pWorkItemData->pNotifyData->calloutID, + pCalloutName ? pCalloutName : L""); + break; + } + case FWPS_CALLOUT_NOTIFY_DELETE_FILTER: + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " -- [FilterID: %#I64x][CalloutID: %#x] A filter referencing %S callout was deleted\n", + pWorkItemData->pNotifyData->filterID, + pWorkItemData->pNotifyData->calloutID, + pCalloutName ? pCalloutName : L""); + + break; + } + case FWPS_CALLOUT_NOTIFY_ADD_FILTER_POST_COMMIT: + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " -- [FilterID: %#I64x][CalloutID: %#x] A filter referencing %S callout was committed\n", + pWorkItemData->pNotifyData->filterID, + pWorkItemData->pNotifyData->calloutID, + pCalloutName ? pCalloutName : L""); + break; + } + default: + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " -- [FilterID: %#I64x][CalloutID: %#x] Invalid Notification Type. Please Contact WFP@Microsoft.com\n", + pWorkItemData->pNotifyData->filterID, + pWorkItemData->pNotifyData->calloutID); + + break; + } + } + + FwpmFreeMemory((VOID**)&pCallout); + + HLPR_DELETE(pWorkItemData->pNotifyData, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + + KrnlHlprWorkItemDataDestroy(&pWorkItemData); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PrvAdvancedNotificationWorkItemRoutine()\n"); + +#endif /// DBG + + return; +} + +/** + @notify_function="NotifyAdvancedNotification" + + Purpose: Traces the notification event.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/FF568803.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/FF568804.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS NTAPI NotifyAdvancedNotification(_In_ FWPS_CALLOUT_NOTIFY_TYPE notificationType, + _In_ const GUID* pFilterKey, + _Inout_ FWPS_FILTER* pFilter) +{ + NT_ASSERT(pFilter); + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> NotifyAdvancedNotification() [FilterID: %#I64x][CalloutID: %#x]\n", + pFilter->filterId, + pFilter->action.calloutId); + + NTSTATUS status = STATUS_SUCCESS; + NOTIFY_DATA* pNotifyData = 0; + +#pragma warning(push) +#pragma warning(disable: 6014) /// pNotifyData is expected to be cleaned up by caller using PrvAdvancedNotificationWorkItemRoutine + + HLPR_NEW(pNotifyData, + NOTIFY_DATA, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pNotifyData, + status); + +#pragma warning(pop) + + pNotifyData->notificationType = notificationType; + pNotifyData->calloutID = pFilter ? pFilter->action.calloutId : 0; + pNotifyData->filterID = pFilter ? pFilter->filterId : 0; + pNotifyData->pFilterKey = pFilterKey; + + status = KrnlHlprWorkItemQueue(g_pWDMDevice, + PrvAdvancedNotificationWorkItemRoutine, + pNotifyData); + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! NotifyAdvancedNotification() [status: %#x]\n", + status); + + HLPR_DELETE(pNotifyData, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + } + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- NotifyAdvancedNotification() [FilterID: %#I64x][CalloutID: %#x]\n", + pFilter->filterId, + pFilter->action.calloutId); + + return status; +} diff --git a/network/trans/WFPSampler/sys/NotifyFunctions_AdvancedCallouts.h b/network/trans/WFPSampler/sys/NotifyFunctions_AdvancedCallouts.h new file mode 100644 index 000000000..ae26242ab --- /dev/null +++ b/network/trans/WFPSampler/sys/NotifyFunctions_AdvancedCallouts.h @@ -0,0 +1,45 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// NotifyFunctions_AdvancedCallouts.h +// +// Abstract: +// This module contains prototypes of WFP Notify functions for the simpler callouts. +// +// Naming Convention: +// +// +// +// i.e. +// +// NotifyAdvancedNotification +// +// +// Notify - Function is an FWPS_CALLOUT_NOTIFY_FN +// +// AdvancedNotification - Function demonstates use of the simple notifications +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// December 13, 2013 - 1.1 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef NOTIFY_ADVANCED_NOTIFICATION_H +#define NOTIFY_ADVANCED_NOTIFICATION_H + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS NTAPI NotifyAdvancedNotification(_In_ FWPS_CALLOUT_NOTIFY_TYPE notificationType, + _In_ const GUID* pFilterKey, + _Inout_ FWPS_FILTER* pFilter); + +#endif /// NOTIFY_ADVANCED_NOTIFICATION_H diff --git a/network/trans/WFPSampler/sys/NotifyFunctions_BasicCallouts.cpp b/network/trans/WFPSampler/sys/NotifyFunctions_BasicCallouts.cpp new file mode 100644 index 000000000..6b86daaf3 --- /dev/null +++ b/network/trans/WFPSampler/sys/NotifyFunctions_BasicCallouts.cpp @@ -0,0 +1,225 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// NotifyFunctions_BasicCallouts.cpp +// +// Abstract: +// This module contains WFP Notify functions for the simpler callouts. +// +// Naming Convention: +// +// +// +// i.e. +// +// NotifyBasicNotification +// +// +// Notify - Function is an FWPS_CALLOUT_NOTIFY_FN +// +// BasicNotification - Function demonstates use of the simple notifications +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Enhance function declaration for IntelliSense and +// improve traces +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "Framework_WFPSamplerCalloutDriver.h" /// . +#include "NotifyFunctions_BasicCallouts.tmh" /// $(OBJ_PATH)\$(O)\ + +/** + @private_function="PrvBasicNotificationWorkItemRoutine" + + Purpose: Traces the appropriate notification event.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_IRQL_requires_(PASSIVE_LEVEL) +_IRQL_requires_same_ +_Function_class_(IO_WORKITEM_ROUTINE) +VOID PrvBasicNotificationWorkItemRoutine(_In_ PDEVICE_OBJECT pDeviceObject, + _Inout_opt_ PVOID pContext) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PrvBasicNotificationWorkItemRoutine()\n"); + +#endif /// DBG + + UNREFERENCED_PARAMETER(pDeviceObject); + + NT_ASSERT(pContext); + NT_ASSERT(((WORKITEM_DATA*)pContext)->pNotifyData); + + + WORKITEM_DATA* pWorkItemData = (WORKITEM_DATA*)pContext; + + if(pWorkItemData) + { + NTSTATUS status = STATUS_SUCCESS; + FWPM_CALLOUT* pCallout = 0; + PWSTR pCalloutName = L""; + + status = FwpmCalloutGetById(g_EngineHandle, + pWorkItemData->pNotifyData->calloutID, + &pCallout); + if(status != STATUS_SUCCESS) + { + if(status != STATUS_FWP_CALLOUT_NOT_FOUND) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! [FilterID: %#I64x][CalloutID: %#x] PrvBasicNotificationWorkItemRoutine : FwpmCalloutGetById() [status: %#x]\n", + pWorkItemData->pNotifyData->filterID, + pWorkItemData->pNotifyData->calloutID, + status); + } + else + pCalloutName = pCallout->displayData.name; + + switch(pWorkItemData->pNotifyData->notificationType) + { + case FWPS_CALLOUT_NOTIFY_ADD_FILTER: + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " -- [FilterID: %#I64x][CalloutID: %#x] A filter referencing %S callout was added\n", + pWorkItemData->pNotifyData->filterID, + pWorkItemData->pNotifyData->calloutID, + pCalloutName ? pCalloutName : L""); + break; + } + case FWPS_CALLOUT_NOTIFY_DELETE_FILTER: + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " -- [FilterID: %#I64x][CalloutID: %#x] A filter referencing %S callout was deleted\n", + pWorkItemData->pNotifyData->filterID, + pWorkItemData->pNotifyData->calloutID, + pCalloutName ? pCalloutName : L""); + + break; + } + case FWPS_CALLOUT_NOTIFY_ADD_FILTER_POST_COMMIT: + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " -- [FilterID: %#I64x][CalloutID: %#x] A filter referencing %S callout was committed\n", + pWorkItemData->pNotifyData->filterID, + pWorkItemData->pNotifyData->calloutID, + pCalloutName ? pCalloutName : L""); + break; + } + default: + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " -- [FilterID: %#I64x][CalloutID: %#x] Invalid Notification Type. Please Contact WFP@Microsoft.com\n", + pWorkItemData->pNotifyData->filterID, + pWorkItemData->pNotifyData->calloutID); + + break; + } + } + + FwpmFreeMemory((VOID**)&pCallout); + + HLPR_DELETE(pWorkItemData->pNotifyData, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + + KrnlHlprWorkItemDataDestroy(&pWorkItemData); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PrvBasicNotificationWorkItemRoutine()\n"); + +#endif /// DBG + + return; +} + +/** + @notify_function="NotifyBasicNotification" + + Purpose: Traces the notification event.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/FF568803.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/FF568804.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS NTAPI NotifyBasicNotification(_In_ FWPS_CALLOUT_NOTIFY_TYPE notificationType, + _In_ const GUID* pFilterKey, + _Inout_ FWPS_FILTER* pFilter) +{ + NT_ASSERT(pFilter); + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> NotifyBasicNotification() [FilterID: %#I64x][CalloutID: %#x]\n", + pFilter->filterId, + pFilter->action.calloutId); + + NTSTATUS status = STATUS_SUCCESS; + NOTIFY_DATA* pNotifyData = 0; + +#pragma warning(push) +#pragma warning(disable: 6014) /// pNotifyData is expected to be cleaned up by caller using PrvBasicNotificationWorkItemRoutine + + HLPR_NEW(pNotifyData, + NOTIFY_DATA, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pNotifyData, + status); + +#pragma warning(pop) + + pNotifyData->notificationType = notificationType; + pNotifyData->calloutID = pFilter ? pFilter->action.calloutId : 0; + pNotifyData->filterID = pFilter ? pFilter->filterId : 0; + pNotifyData->pFilterKey = pFilterKey; + + status = KrnlHlprWorkItemQueue(g_pWDMDevice, + PrvBasicNotificationWorkItemRoutine, + pNotifyData); + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! NotifyBasicNotification() [status: %#x]\n", + status); + + HLPR_DELETE(pNotifyData, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + } + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- NotifyBasicNotification() [FilterID: %#I64x][CalloutID: %#x]\n", + pFilter->filterId, + pFilter->action.calloutId); + + return status; +} diff --git a/network/trans/WFPSampler/sys/NotifyFunctions_BasicCallouts.h b/network/trans/WFPSampler/sys/NotifyFunctions_BasicCallouts.h new file mode 100644 index 000000000..58b11fa2d --- /dev/null +++ b/network/trans/WFPSampler/sys/NotifyFunctions_BasicCallouts.h @@ -0,0 +1,46 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// NotifyFunctions_BasicCallouts.h +// +// Abstract: +// This module contains prototypes of WFP Notify functions for the simpler callouts. +// +// Naming Convention: +// +// +// +// i.e. +// +// NotifyBasicNotification +// +// +// Notify - Function is an FWPS_CALLOUT_NOTIFY_FN +// +// BasicNotification - Function demonstates use of the simple notifications +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Enhance function declaration for IntelliSense +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef NOTIFY_BASIC_NOTIFICATION_H +#define NOTIFY_BASIC_NOTIFICATION_H + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS NTAPI NotifyBasicNotification(_In_ FWPS_CALLOUT_NOTIFY_TYPE notificationType, + _In_ const GUID* pFilterKey, + _Inout_ FWPS_FILTER* pFilter); + +#endif /// NOTIFY_BASIC_NOTIFICATION_H \ No newline at end of file diff --git a/network/trans/WFPSampler/sys/NotifyFunctions_FastCallouts.cpp b/network/trans/WFPSampler/sys/NotifyFunctions_FastCallouts.cpp new file mode 100644 index 000000000..92011dfc3 --- /dev/null +++ b/network/trans/WFPSampler/sys/NotifyFunctions_FastCallouts.cpp @@ -0,0 +1,227 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// NotifyFunctions_FastCallouts.cpp +// +// Abstract: +// This module contains WFP Notify functions for the performance callouts. +// +// Naming Convention: +// +// +// +// i.e. +// +// NotifyBasicNotification +// +// +// Notify - Function is an FWPS_CALLOUT_NOTIFY_FN +// +// FastNotification - Function demonstates use of the performance notifications +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Enhance function declaration for IntelliSense and +// improve traces +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "Framework_WFPSamplerCalloutDriver.h" /// . +#include "NotifyFunctions_FastCallouts.tmh" /// $(OBJ_PATH)\$(O)\ + +/** + @private_function="PrvFastNotificationWorkItemRoutine" + + Purpose: Traces the appropriate notification event.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_IRQL_requires_(PASSIVE_LEVEL) +_IRQL_requires_same_ +_Function_class_(IO_WORKITEM_ROUTINE) +VOID PrvFastNotificationWorkItemRoutine(_In_ PDEVICE_OBJECT pDeviceObject, + _Inout_opt_ PVOID pContext) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PrvFastNotificationWorkItemRoutine()\n"); + +#endif /// DBG + + UNREFERENCED_PARAMETER(pDeviceObject); + + NT_ASSERT(pContext); + NT_ASSERT(((WORKITEM_DATA*)pContext)->pNotifyData); + + WORKITEM_DATA* pWorkItemData = (WORKITEM_DATA*)pContext; + + if(pWorkItemData) + { + NTSTATUS status = STATUS_SUCCESS; + FWPM_CALLOUT* pCallout = 0; + PWSTR pCalloutName = L""; + + status = FwpmCalloutGetById(g_EngineHandle, + pWorkItemData->pNotifyData->calloutID, + &pCallout); + if(status != STATUS_SUCCESS) + { + if(status != STATUS_FWP_CALLOUT_NOT_FOUND) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! [FilterID: %#I64x][CalloutID: %#x] PrvFastNotificationWorkItemRoutine : FwpmCalloutGetById() [status: %#x]\n", + pWorkItemData->pNotifyData->filterID, + pWorkItemData->pNotifyData->calloutID, + status); + } + else + pCalloutName = pCallout->displayData.name; + + switch(pWorkItemData->pNotifyData->notificationType) + { + case FWPS_CALLOUT_NOTIFY_ADD_FILTER: + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " -- [FilterID: %#I64x][CalloutID: %#x] A filter referencing %S callout was added\n", + pWorkItemData->pNotifyData->filterID, + pWorkItemData->pNotifyData->calloutID, + pCalloutName ? pCalloutName : L""); + + break; + } + case FWPS_CALLOUT_NOTIFY_DELETE_FILTER: + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " -- [FilterID: %#I64x][CalloutID: %#x] A filter referencing %S callout was deleted\n", + pWorkItemData->pNotifyData->filterID, + pWorkItemData->pNotifyData->calloutID, + pCalloutName ? pCalloutName : L""); + + break; + } + case FWPS_CALLOUT_NOTIFY_ADD_FILTER_POST_COMMIT: + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " -- [FilterID: %#I64x][CalloutID: %#x] A filter referencing %S callout was committed\n", + pWorkItemData->pNotifyData->filterID, + pWorkItemData->pNotifyData->calloutID, + pCalloutName ? pCalloutName : L""); + + break; + } + default: + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " -- [FilterID: %#I64x][CalloutID: %#x] Invalid Notification Type. Please Contact WFP@Microsoft.com\n", + pWorkItemData->pNotifyData->filterID, + pWorkItemData->pNotifyData->calloutID); + + break; + } + } + + FwpmFreeMemory((VOID**)&pCallout); + + HLPR_DELETE(pWorkItemData->pNotifyData, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + + KrnlHlprWorkItemDataDestroy(&pWorkItemData); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PrvFastNotificationWorkItemRoutine()\n"); + +#endif /// DBG + + return; +} + +/** + @notify_function="NotifyFastNotification" + + Purpose: Traces the notification event.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/FF568803.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/FF568804.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS NTAPI NotifyFastNotification(_In_ FWPS_CALLOUT_NOTIFY_TYPE notificationType, + _In_ const GUID* pFilterKey, + _Inout_ FWPS_FILTER* pFilter) +{ + NT_ASSERT(pFilter); + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> NotifyFastNotification() [FilterID: %#I64x][CalloutID: %#x]\n", + pFilter->filterId, + pFilter->action.calloutId); + + + NTSTATUS status = STATUS_SUCCESS; + NOTIFY_DATA* pNotifyData = 0; + +#pragma warning(push) +#pragma warning(disable: 6014) /// pNotifyData is expected to be cleaned up by caller using PrvBasicNotificationWorkItemRoutine + + HLPR_NEW(pNotifyData, + NOTIFY_DATA, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pNotifyData, + status); + +#pragma warning(pop) + + pNotifyData->notificationType = notificationType; + pNotifyData->calloutID = pFilter ? pFilter->action.calloutId : 0; + pNotifyData->filterID = pFilter ? pFilter->filterId : 0; + pNotifyData->pFilterKey = pFilterKey; + + status = KrnlHlprWorkItemQueue(g_pWDMDevice, + PrvFastNotificationWorkItemRoutine, + pNotifyData); + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! NotifyFastNotification() [status: %#x]\n", + status); + + HLPR_DELETE(pNotifyData, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + } + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- NotifyFastNotification() [FilterID: %#I64x][CalloutID: %#x]\n", + pFilter->filterId, + pFilter->action.calloutId); + + return status; +} diff --git a/network/trans/WFPSampler/sys/NotifyFunctions_FastCallouts.h b/network/trans/WFPSampler/sys/NotifyFunctions_FastCallouts.h new file mode 100644 index 000000000..96c816b43 --- /dev/null +++ b/network/trans/WFPSampler/sys/NotifyFunctions_FastCallouts.h @@ -0,0 +1,46 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// NotifyFunctions_FastCallouts.h +// +// Abstract: +// This module contains prototypes of WFP Notify functions for the performance callouts. +// +// Naming Convention: +// +// +// +// i.e. +// +// NotifyFastNotification +// +// +// Notify - Function is an FWPS_CALLOUT_NOTIFY_FN +// +// FastNotification - Function demonstates use of the performance notifications +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Enhance function declaration for IntelliSense +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef NOTIFY_FAST_NOTIFICATION_H +#define NOTIFY_FAST_NOTIFICATION_H + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS NTAPI NotifyFastNotification(_In_ FWPS_CALLOUT_NOTIFY_TYPE notificationType, + _In_ const GUID* pFilterKey, + _Inout_ FWPS_FILTER* pFilter); + +#endif /// NOTIFY_FAST_NOTIFICATION_H \ No newline at end of file diff --git a/network/trans/WFPSampler/sys/NotifyFunctions_FlowDelete.cpp b/network/trans/WFPSampler/sys/NotifyFunctions_FlowDelete.cpp new file mode 100644 index 000000000..bf1506cec --- /dev/null +++ b/network/trans/WFPSampler/sys/NotifyFunctions_FlowDelete.cpp @@ -0,0 +1,95 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// NotifyFunctions_FlowDelete.cpp +// +// Abstract: +// This module contains WFP Flow Delete Notification functions. +// +// Naming Convention: +// +// +// +// i.e. +// +// FlowDeleteNotification +// +// +// FlowDelete - Function is an FWPS_CALLOUT_FLOW_DELETE_NOTIFY_FN +// +// Notification - Function demonstates use of the simple notifications +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// December 13, 2013 - 1.1 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "Framework_WFPSamplerCalloutDriver.h" /// . +#include "NotifyFunctions_FlowDelete.tmh" /// $(OBJ_PATH)\$(O)\ + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI NotifyFlowDeleteNotification(_In_ UINT16 layerID, + _In_ UINT32 calloutID, + _In_ UINT64 flowContext) +{ + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> NotifyFlowDeleteNotification() [Layer: %s][CalloutID: %#I64x][FlowContext: %#I64x]", + KrnlHlprFwpsLayerIDToString(layerID), + calloutID, + flowContext); + + UNREFERENCED_PARAMETER(calloutID); + + if(flowContext) + { + FLOW_CONTEXT* pFlowContext = (FLOW_CONTEXT*)flowContext; + + if(layerID == FWPS_LAYER_STREAM_V4 || + layerID == FWPS_LAYER_STREAM_V6) + { + + + + } + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + else if(layerID == FWPS_LAYER_ALE_ENDPOINT_CLOSURE_V4 || + layerID == FWPS_LAYER_ALE_ENDPOINT_CLOSURE_V6) + { + PEND_DATA* pPendData = (PEND_DATA*)pFlowContext->pALEEndpointClosureContext->pPendData; + KIRQL irql = PASSIVE_LEVEL; + + KeAcquireSpinLock(&(pFlowContext->pALEEndpointClosureContext->spinLock), + &irql); + + KrnlHlprPendDataDestroy(&pPendData); + + KeReleaseSpinLock(&(pFlowContext->pALEEndpointClosureContext->spinLock), + irql); + } + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + KrnlHlprFlowContextDestroy(&pFlowContext); + } + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- NotifyFlowDeleteNotification() [Layer: %s][CalloutID: %#I64x][FlowContext: %#I64x]", + KrnlHlprFwpsLayerIDToString(layerID), + calloutID, + flowContext); + + return; +} diff --git a/network/trans/WFPSampler/sys/NotifyFunctions_FlowDelete.h b/network/trans/WFPSampler/sys/NotifyFunctions_FlowDelete.h new file mode 100644 index 000000000..faba5b104 --- /dev/null +++ b/network/trans/WFPSampler/sys/NotifyFunctions_FlowDelete.h @@ -0,0 +1,44 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// NotifyFunctions_FlowDelete.h +// +// Abstract: +// This module contains prototypes of WFP Notify functions for the simpler callouts. +// +// Naming Convention: +// +// +// +// i.e. +// +// NotifyFlowDeleteNotification +// +// +// NotifyFlowDelete - Function is an FWPS_CALLOUT_NOTIFY_FN +// +// Notification - Function demonstates use of the simple notifications +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// December 13, 2013 - 1.1 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef NOTIFY_FLOW_DELETE_NOTIFICATION_H +#define NOTIFY_FLOW_DELETE_NOTIFICATION_H + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI NotifyFlowDeleteNotification(_In_ UINT16 layerID, + _In_ UINT32 calloutID, + _In_ UINT64 flowContext); + +#endif /// NOTIFY_FLOW_DELETE_NOTIFICATION_H diff --git a/network/trans/WFPSampler/sys/NotifyFunctions_Include.h b/network/trans/WFPSampler/sys/NotifyFunctions_Include.h new file mode 100644 index 000000000..48c17b343 --- /dev/null +++ b/network/trans/WFPSampler/sys/NotifyFunctions_Include.h @@ -0,0 +1,36 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// NotifyFunctions_Include.h +// +// Abstract: +// This module contains a central repository of headers which contain prototypes for all of the +// notification functions. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Add NotifyFunctions_AdvancedCallouts.h, +// NotifyFunctions_FlowDelete.h, and replace +// NotifyFunctions_PendAuthorizationCallouts.h, with +// NotifyFunctions_PendCallouts.h +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef NOTIFY_INCLUDE_H +#define NOTIFY_INCLUDE_H + +#include "NotifyFunctions_AdvancedCallouts.h" /// . +#include "NotifyFunctions_BasicCallouts.h" /// . +#include "NotifyFunctions_FastCallouts.h" /// . +#include "NotifyFunctions_FlowDelete.h" /// . +#include "NotifyFunctions_PendCallouts.h" /// . +#include "NotifyFunctions_ProxyCallouts.h" /// . + +#endif /// NOTIFY_INCLUDE_H \ No newline at end of file diff --git a/network/trans/WFPSampler/sys/NotifyFunctions_PendCallouts.cpp b/network/trans/WFPSampler/sys/NotifyFunctions_PendCallouts.cpp new file mode 100644 index 000000000..e8b1fd637 --- /dev/null +++ b/network/trans/WFPSampler/sys/NotifyFunctions_PendCallouts.cpp @@ -0,0 +1,257 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// NotifyFunctions_PendCallouts.h +// +// Abstract: +// This module contains WFP Notify functions for the pend callouts. +// +// Naming Convention: +// +// +// +// i.e. +// +// NotifyPendNotification +// +// +// Notify - Function is an FWPS_CALLOUT_NOTIFY_FN +// +// PendNotification - Function demonstates use of notifications for callouts +// pending authorization or endpoint closure requests. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// December 13, 2013 - 1.1 - Creation +// (replaces NotifyFuncitons_PendAuthorizationCallouts.cpp) +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "Framework_WFPSamplerCalloutDriver.h" /// . +#include "NotifyFunctions_PendCallouts.tmh" /// $(OBJ_PATH)\$(O)\ + +/** + @private_function="PrvPendNotificationWorkItemRoutine" + + Purpose: Traces the appropriate notification event.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_IRQL_requires_(PASSIVE_LEVEL) +_IRQL_requires_same_ +_Function_class_(IO_WORKITEM_ROUTINE) +VOID PrvPendNotificationWorkItemRoutine(_In_ PDEVICE_OBJECT pDeviceObject, + _Inout_opt_ PVOID pContext) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PrvPendNotificationWorkItemRoutine()\n"); + +#endif /// DBG + + UNREFERENCED_PARAMETER(pDeviceObject); + + NT_ASSERT(pContext); + NT_ASSERT(((WORKITEM_DATA*)pContext)->pNotifyData); + + WORKITEM_DATA* pWorkItemData = (WORKITEM_DATA*)pContext; + + if(pWorkItemData) + { + NTSTATUS status = STATUS_SUCCESS; + FWPM_CALLOUT* pCallout = 0; + PWSTR pCalloutName = L""; + + status = FwpmCalloutGetById(g_EngineHandle, + pWorkItemData->pNotifyData->calloutID, + &pCallout); + if(status != STATUS_SUCCESS) + { + if(status != STATUS_FWP_CALLOUT_NOT_FOUND) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! [FilterID: %#I64x][CalloutID: %#x] PrvPendNotificationWorkItemRoutine : FwpmCalloutGetById() [status: %#x]\n", + pWorkItemData->pNotifyData->filterID, + pWorkItemData->pNotifyData->calloutID, + status); + } + else + { + pCalloutName = pCallout->displayData.name; + + if(pCallout->applicableLayer != FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V4 && + pCallout->applicableLayer != FWPM_LAYER_ALE_RESOURCE_ASSIGNMENT_V6 && + pCallout->applicableLayer != FWPM_LAYER_ALE_AUTH_LISTEN_V4 && + pCallout->applicableLayer != FWPM_LAYER_ALE_AUTH_LISTEN_V6 && + pCallout->applicableLayer != FWPM_LAYER_ALE_AUTH_CONNECT_V4 && + pCallout->applicableLayer != FWPM_LAYER_ALE_AUTH_CONNECT_V6 && + pCallout->applicableLayer != FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 && + pCallout->applicableLayer != FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + && + pCallout->applicableLayer != FWPM_LAYER_ALE_ENDPOINT_CLOSURE_V4 && + pCallout->applicableLayer != FWPM_LAYER_ALE_ENDPOINT_CLOSURE_V6 + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + ) + { + status = STATUS_FWP_INCOMPATIBLE_LAYER; + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! [FilterID: %#I64x][CalloutID: %#x] PrvPendNotificationWorkItemRoutine() [status: %#x]\n", + pWorkItemData->pNotifyData->filterID, + pWorkItemData->pNotifyData->calloutID, + status); + } + } + + switch(pWorkItemData->pNotifyData->notificationType) + { + case FWPS_CALLOUT_NOTIFY_ADD_FILTER: + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " -- [FilterID: %#I64x][CalloutID: %#x] A filter referencing %S callout was added\n", + pWorkItemData->pNotifyData->filterID, + pWorkItemData->pNotifyData->calloutID, + pCalloutName ? pCalloutName : L""); + + break; + } + case FWPS_CALLOUT_NOTIFY_DELETE_FILTER: + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " -- [FilterID: %#I64x][CalloutID: %#x] A filter referencing %S callout was deleted\n", + pWorkItemData->pNotifyData->filterID, + pWorkItemData->pNotifyData->calloutID, + pCalloutName ? pCalloutName : L""); + + break; + } + case FWPS_CALLOUT_NOTIFY_ADD_FILTER_POST_COMMIT: + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " -- [FilterID: %#I64x][CalloutID: %#x] A filter referencing %S callout was committed\n", + pWorkItemData->pNotifyData->filterID, + pWorkItemData->pNotifyData->calloutID, + pCalloutName ? pCalloutName : L""); + + break; + } + default: + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " -- [FilterID: %#I64x][CalloutID: %#x] Invalid Notification Type. Please Contact WFP@Microsoft.com\n", + pWorkItemData->pNotifyData->filterID, + pWorkItemData->pNotifyData->calloutID); + + break; + } + } + + FwpmFreeMemory((VOID**)&pCallout); + + HLPR_DELETE(pWorkItemData->pNotifyData, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + + KrnlHlprWorkItemDataDestroy(&pWorkItemData); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PrvPendNotificationWorkItemRoutine()\n"); + +#endif /// DBG + + return; +} + +/** + @notify_function="NotifyPendNotification" + + Purpose: Traces the notification event.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/FF568803.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/FF568804.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS NTAPI NotifyPendNotification(_In_ FWPS_CALLOUT_NOTIFY_TYPE notificationType, + _In_ const GUID* pFilterKey, + _Inout_ FWPS_FILTER* pFilter) +{ + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> NotifyPendNotification()[FilterID: %#I64x][CalloutID: %#x]\n", + pFilter->filterId, + pFilter->action.calloutId); + + NT_ASSERT(pFilter); + + NTSTATUS status = STATUS_SUCCESS; + NOTIFY_DATA* pNotifyData = 0; + +#pragma warning(push) +#pragma warning(disable: 6014) /// pNotifyData is expected to be cleaned up by caller using PrvPendNotificationWorkItemRoutine + + HLPR_NEW(pNotifyData, + NOTIFY_DATA, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pNotifyData, + status); + +#pragma warning(pop) + + pNotifyData->notificationType = notificationType; + pNotifyData->calloutID = pFilter ? pFilter->action.calloutId : 0; + pNotifyData->filterID = pFilter ? pFilter->filterId : 0; + pNotifyData->pFilterKey = pFilterKey; + + status = KrnlHlprWorkItemQueue(g_pWDMDevice, + PrvPendNotificationWorkItemRoutine, + pNotifyData); + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! NotifyPendNotification() [status: %#x]\n", + status); + + HLPR_DELETE(pNotifyData, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + } + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- NotifyPendNotification() [FilterID: %#I64x][CalloutID: %#x]\n", + pFilter->filterId, + pFilter->action.calloutId); + + return status; +} diff --git a/network/trans/WFPSampler/sys/NotifyFunctions_PendCallouts.h b/network/trans/WFPSampler/sys/NotifyFunctions_PendCallouts.h new file mode 100644 index 000000000..6b2554caa --- /dev/null +++ b/network/trans/WFPSampler/sys/NotifyFunctions_PendCallouts.h @@ -0,0 +1,47 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// NotifyFunctions_PendCallouts.h +// +// Abstract: +// This module contains prototypes of WFP Notify functions for the pend callouts. +// +// Naming Convention: +// +// +// +// i.e. +// +// NotifyPendNotification +// +// +// Notify - Function is located in sys\NotifyFunctions\ +// +// PendNotification - Function demonstates use of notifications for callouts +// pending authorization or endpoint closure requests. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// December 13, 2013 - 1.1 - Creation +// (replaces NotifyFuncitons_PendAuthorizationCallouts.h) +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef NOTIFY_PEND_NOTIFICATION_H +#define NOTIFY_PEND_NOTIFICATION_H + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS NTAPI NotifyPendNotification(_In_ FWPS_CALLOUT_NOTIFY_TYPE notificationType, + _In_ const GUID* pFilterKey, + _Inout_ FWPS_FILTER* pFilter); + +#endif /// NOTIFY_PEND_NOTIFICATION_H \ No newline at end of file diff --git a/network/trans/WFPSampler/sys/NotifyFunctions_ProxyCallouts.cpp b/network/trans/WFPSampler/sys/NotifyFunctions_ProxyCallouts.cpp new file mode 100644 index 000000000..5102b78da --- /dev/null +++ b/network/trans/WFPSampler/sys/NotifyFunctions_ProxyCallouts.cpp @@ -0,0 +1,248 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// NotifyFunctions_ProxyCallouts.h +// +// Abstract: +// This module contains WFP Notify functions for the proxy callouts using WFP's REDIRECT layers. +// +// Naming Convention: +// +// +// +// i.e. +// +// NotifyProxyByALERedirectNotification +// +// +// Notify - Function is an FWPS_CALLOUT_NOTIFY_FN +// +// ProxyByALERedirectNotification - Function demonstates use of notifications for +// callouts using WFP's REDIRECT layers +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Enhance function declaration for IntelliSense and +// improve traces +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "Framework_WFPSamplerCalloutDriver.h" /// . +#include "NotifyFunctions_ProxyCallouts.tmh" /// $(OBJ_PATH)\$(O)\ + +#if(NTDDI_VERSION >= NTDDI_WIN7) + +/** + @private_function="PrvProxyByALERedirectNotificationWorkItemRoutine" + + Purpose: Traces the appropriate notification event.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_IRQL_requires_(PASSIVE_LEVEL) +_IRQL_requires_same_ +_Function_class_(IO_WORKITEM_ROUTINE) +VOID PrvProxyByALERedirectNotificationWorkItemRoutine(_In_ PDEVICE_OBJECT pDeviceObject, + _Inout_opt_ PVOID pContext) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PrvProxyByALERedirectNotificationWorkItemRoutine()\n"); + +#endif /// DBG + + UNREFERENCED_PARAMETER(pDeviceObject); + + NT_ASSERT(pContext); + NT_ASSERT(((WORKITEM_DATA*)pContext)->pNotifyData); + + WORKITEM_DATA* pWorkItemData = (WORKITEM_DATA*)pContext; + + if(pWorkItemData) + { + NTSTATUS status = STATUS_SUCCESS; + FWPM_CALLOUT* pCallout = 0; + PWSTR pCalloutName = L""; + + status = FwpmCalloutGetById(g_EngineHandle, + pWorkItemData->pNotifyData->calloutID, + &pCallout); + if(status != STATUS_SUCCESS) + { + if(status != STATUS_FWP_CALLOUT_NOT_FOUND) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! [FilterID: %#I64x][CalloutID: %#x] PrvProxyByALERedirectNotificationWorkItemRoutine : FwpmCalloutGetById() [status: %#x]\n", + pWorkItemData->pNotifyData->filterID, + pWorkItemData->pNotifyData->calloutID, + status); + } + else + { + pCalloutName = pCallout->displayData.name; + + if(pCallout->applicableLayer != FWPM_LAYER_ALE_CONNECT_REDIRECT_V4 && + pCallout->applicableLayer != FWPM_LAYER_ALE_CONNECT_REDIRECT_V6 && + pCallout->applicableLayer != FWPM_LAYER_ALE_BIND_REDIRECT_V4 && + pCallout->applicableLayer != FWPM_LAYER_ALE_BIND_REDIRECT_V6) + { + status = STATUS_FWP_INCOMPATIBLE_LAYER; + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! [FilterID: %#I64x][CalloutID: %#x] PrvProxyByALERedirectNotificationWorkItemRoutine() [status: %#x]\n", + pWorkItemData->pNotifyData->filterID, + pWorkItemData->pNotifyData->calloutID, + status); + } + } + + switch(pWorkItemData->pNotifyData->notificationType) + { + case FWPS_CALLOUT_NOTIFY_ADD_FILTER: + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " -- [FilterID: %#I64x][CalloutID: %#x] A filter referencing %S callout was added\n", + pWorkItemData->pNotifyData->filterID, + pWorkItemData->pNotifyData->calloutID, + pCalloutName ? pCalloutName : L""); + + break; + } + case FWPS_CALLOUT_NOTIFY_DELETE_FILTER: + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " -- [FilterID: %#I64x][CalloutID: %#x] A filter referencing %S callout was deleted\n", + pWorkItemData->pNotifyData->filterID, + pWorkItemData->pNotifyData->calloutID, + pCalloutName ? pCalloutName : L""); + + break; + } + case FWPS_CALLOUT_NOTIFY_ADD_FILTER_POST_COMMIT: + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " -- [FilterID: %#I64x][CalloutID: %#x] A filter referencing %S callout was committed\n", + pWorkItemData->pNotifyData->filterID, + pWorkItemData->pNotifyData->calloutID, + pCalloutName ? pCalloutName : L""); + + break; + } + default: + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " -- [FilterID: %#I64x][CalloutID: %#x] Invalid Notification Type. Please Contact WFP@Microsoft.com\n", + pWorkItemData->pNotifyData->filterID, + pWorkItemData->pNotifyData->calloutID); + + break; + } + } + + FwpmFreeMemory((VOID**)&pCallout); + + HLPR_DELETE(pWorkItemData->pNotifyData, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + + KrnlHlprWorkItemDataDestroy(&pWorkItemData); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PrvProxyByALERedirectNotificationWorkItemRoutine()\n"); + +#endif /// DBG + + return; +} + +/** + @notify_function="NotifyProxyByALERedirectNotification" + + Purpose: Traces the notification event.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/FF568803.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/FF568804.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS NTAPI NotifyProxyByALERedirectNotification(_In_ FWPS_CALLOUT_NOTIFY_TYPE notificationType, + _In_ const GUID* pFilterKey, + _Inout_ FWPS_FILTER* pFilter) +{ + NT_ASSERT(pFilter); + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> NotifyProxyByALERedirectNotification() [FilterID: %#I64x][CalloutID: %#x]\n", + pFilter->filterId, + pFilter->action.calloutId); + + NTSTATUS status = STATUS_SUCCESS; + NOTIFY_DATA* pNotifyData = 0; + +#pragma warning(push) +#pragma warning(disable: 6014) /// pNotifyData is expected to be cleaned up by caller using PrvProxyByALERedirectNotificationWorkItemRoutine + + HLPR_NEW(pNotifyData, + NOTIFY_DATA, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pNotifyData, + status); + +#pragma warning(pop) + + pNotifyData->notificationType = notificationType; + pNotifyData->calloutID = pFilter ? pFilter->action.calloutId : 0; + pNotifyData->filterID = pFilter ? pFilter->filterId : 0; + pNotifyData->pFilterKey = pFilterKey; + + status = KrnlHlprWorkItemQueue(g_pWDMDevice, + PrvProxyByALERedirectNotificationWorkItemRoutine, + pNotifyData); + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! NotifyProxyByALERedirectNotification() [status: %#x]\n", + status); + + HLPR_DELETE(pNotifyData, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + } + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- NotifyProxyByALERedirectNotification() [FilterID: %#I64x][CalloutID: %#x]\n", + pFilter->filterId, + pFilter->action.calloutId); + + return status; +} + +#endif // (NTDDI_VERSION >= NTDDI_WIN7) diff --git a/network/trans/WFPSampler/sys/NotifyFunctions_ProxyCallouts.h b/network/trans/WFPSampler/sys/NotifyFunctions_ProxyCallouts.h new file mode 100644 index 000000000..b410eacc3 --- /dev/null +++ b/network/trans/WFPSampler/sys/NotifyFunctions_ProxyCallouts.h @@ -0,0 +1,52 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// NotifyFunctions_ProxyCallouts.h +// +// Abstract: +// This module contains prototypes of WFP Notify functions for the proxy callouts using WFP's +// REDIRECT layers. +// +// Naming Convention: +// +// +// +// i.e. +// +// NotifyProxyByALERedirectNotification +// +// +// Notify - Function is an FWPS_CALLOUT_NOTIFY_FN +// +// ProxyByALERedirectNotification - Function demonstates use of notifications for +// callouts using WFP's REDIRECT layers +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Enhance function declaration for IntelliSense +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef NOTIFY_PROXY_H +#define NOTIFY_PROXY_H + +#if(NTDDI_VERSION >= NTDDI_WIN7) + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS NTAPI NotifyProxyByALERedirectNotification(_In_ FWPS_CALLOUT_NOTIFY_TYPE notificationType, + _In_ const GUID* pFilterKey, + _Inout_ FWPS_FILTER* pFilter); + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + +#endif /// NOTIFY_PROXY_H \ No newline at end of file diff --git a/network/trans/WFPSampler/sys/SubscriptionFunctions_BFEState.cpp b/network/trans/WFPSampler/sys/SubscriptionFunctions_BFEState.cpp new file mode 100644 index 000000000..8c1416277 --- /dev/null +++ b/network/trans/WFPSampler/sys/SubscriptionFunctions_BFEState.cpp @@ -0,0 +1,99 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// SubscriptionFunctions_BFEState.cpp +// +// Abstract: +// This module contains WFP callback functions for changes in the BFE state. +// +// Naming Convention: +// +// +// +// i.e. +// +// SubscriptionBFEStateChangeCallback +// +// +// Subscription - Function pertains to a subscription. +// +// BFEStateChangeCallback - Function demonstates use of the BFE service state change +// callback. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Enhance function declaration for IntelliSense +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "Framework_WFPSamplerCalloutDriver.h" /// . +#include "SubscriptionFunctions_BFEState.tmh" /// $(OBJ_PATH)\$(O)\ + +/** + @notify_function="SubscriptionBFEStateChangeCallback" + + Purpose: Callback, invoked on BFE service state change, which will get or release a handle + to the engine.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF550062.aspx
+*/ +_IRQL_requires_(PASSIVE_LEVEL) +_IRQL_requires_same_ +VOID CALLBACK SubscriptionBFEStateChangeCallback(_Inout_ VOID* pContext, + _In_ FWPM_SERVICE_STATE bfeState) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> SubscriptionBFEStateChangeCallback()\n"); + +#endif /// DBG + + NT_ASSERT(pContext); + + NTSTATUS status = STATUS_SUCCESS; + WFPSAMPLER_DEVICE_DATA* pDeviceData = (WFPSAMPLER_DEVICE_DATA*)pContext; + + switch(bfeState) + { + case FWPM_SERVICE_RUNNING: + { + if(pDeviceData->pEngineHandle == 0) + { + status = KrnlHlprFwpmSessionCreateEngineHandle(&(pDeviceData->pEngineHandle)); + HLPR_BAIL_ON_FAILURE(status); + } + + break; + } + case FWPM_SERVICE_STOP_PENDING: + { + KrnlHlprFwpmSessionDestroyEngineHandle(&(pDeviceData->pEngineHandle)); + + break; + } + } + + HLPR_BAIL_LABEL: + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- SubscriptionBFEStateChangeCallback() [status: %#x]\n", + status); + +#endif /// DBG + + return; +} diff --git a/network/trans/WFPSampler/sys/SubscriptionFunctions_BFEState.h b/network/trans/WFPSampler/sys/SubscriptionFunctions_BFEState.h new file mode 100644 index 000000000..c32d63b47 --- /dev/null +++ b/network/trans/WFPSampler/sys/SubscriptionFunctions_BFEState.h @@ -0,0 +1,30 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// SubscriptionFunctions_BFEState.h +// +// Abstract: +// This module contains protptyes for WFP callback functions for changes in the BFE state. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Enhance function declaration for IntelliSense +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef SUBSCRIBE_BFE_STATE_H +#define SUBSCRIBE_BFE_STATE_H + +_IRQL_requires_(PASSIVE_LEVEL) +_IRQL_requires_same_ +VOID CALLBACK SubscriptionBFEStateChangeCallback(_Inout_ VOID* pContext, + _In_ FWPM_SERVICE_STATE bfeState); + +#endif /// SUBSCRIBE_BFE_STATE_H \ No newline at end of file diff --git a/network/trans/WFPSampler/sys/SubscriptionFunctions_Include.h b/network/trans/WFPSampler/sys/SubscriptionFunctions_Include.h new file mode 100644 index 000000000..93de27985 --- /dev/null +++ b/network/trans/WFPSampler/sys/SubscriptionFunctions_Include.h @@ -0,0 +1,27 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// SubscriptionFunctions_Include.h +// +// Abstract: +// This module contains a central repository of headers which contain prototypes for all of the +// subscription functions. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef SUBSCRIPTION_INCLUDE_H +#define SUBSCRIPTION_INCLUDE_H + +#include "SubscriptionFunctions_BFEState.h" /// . + +#endif /// SUBSCRIPTION_INCLUDE_H \ No newline at end of file diff --git a/network/trans/WFPSampler/sys/WFPSamplerCalloutDriver.InX b/network/trans/WFPSampler/sys/WFPSamplerCalloutDriver.InX new file mode 100644 index 000000000..4c5777bee --- /dev/null +++ b/network/trans/WFPSampler/sys/WFPSamplerCalloutDriver.InX @@ -0,0 +1,93 @@ +;/////////////////////////////////////////////////////////////////////////////////////////////////// +;// +;// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +;// +;// Module Name: +;// WFPSamplerCalloutDriver.Inf +;// +;// Abstract: +;// INF file for installing the WFPSampler callout driver. +;// +;// Author: +;// Dusty Harper (DHarper) +;// +;// Revision History: +;// +;// [ Month ][Day] [Year] - [Revision]-[ Comments ] +;// April 01, 2010 - 0.0.0.1 - Creation +;// +;/////////////////////////////////////////////////////////////////////////////////////////////////// + +[Version] + Signature = "$Windows NT$" + Class = WFPCALLOUTS + ClassGuid = {57465043-616C-6C6F-7574-5F636C617373} + Provider = %ProviderString% + CatalogFile = WFPSamplerCalloutDriver.Cat + DriverVer = + +[SourceDisksNames.$ARCH$] + 1 = %WFPSamplerCalloutDriverDiskId%,,,"" + +[SourceDisksFiles.$ARCH$] + WFPSamplerCalloutDriver.sys = 1,, + +[ClassInstall32.nt$ARCH$] + AddReg = WFPCalloutsClassReg + +[DestinationDirs] + DefaultDestDir = 12 ;/// %WinDir%\System32\Drivers + WFPSamplerCalloutDriver.DriverFiles = 12 ;/// %WinDir%\System32\Drivers + +[DefaultInstall.nt$ARCH$] + OptionDesc = %WFPSamplerCalloutDriverDescription% + CopyFiles = WFPSamplerCalloutDriver.DriverFiles + +[DefaultInstall.nt$ARCH$.Services] + AddService = %WFPSamplerCalloutDriverServiceName%,,WFPSamplerCalloutDriver.Service + +[DefaultUninstall.nt$ARCH$] + DelFiles = WFPSamplerCalloutDriver.DriverFiles + DelReg = WFPCalloutsClassReg + +[DefaultUninstall.nt$ARCH$.Services] + DelService = %WFPSamplerCalloutDriverServiceName%,0x200 ;/// SPSVCINST_STOPSERVICE + +[WFPCalloutClassReg] + HKR,,,0,%WFPCalloutClassName% + HKR,,Icon,,-5 + HKR,,SilentInstall,,1 + HKR,,DeviceCharacteristics,0x10001,0x100 ;/// FLG_ADDREG_BINVALUETYPE | FLG_ADDREG_TYPE_MULTI_SZ, FILE_DEVICE_SECURE_OPEN + HKR,,Security,,"D:P(A;;GA;;;SY)(A;;GA;;;BA)(A;;GA;;;LS)" ;/// Permit Generic All access to SYstem, Built-in Admin, and Local System. + +[WFPSamplerCalloutDriver.DriverFiles] + WFPSamplerCalloutDriver.sys,,,0x00000040 ;/// COPYFLG_OVERWRITE_OLDER_ONLY + +[WFPSamplerCalloutDriver.NT.CoInstallers] + AddReg = WFPSamplerCalloutDriver.CoInstaller.AddReg + CopyFiles = WFPSamplerCalloutDriver.CoInstaller.CopyFiles + +[WFPSamplerCalloutDriver.CoInstaller.AddReg] + HKR,,CoInstallers32,0x00010000, "WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll,WdfCoInstaller" + +[WFPSamplerCalloutDriver.CoInstaller.CopyFiles] + WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll + +[WFPSamplerCalloutDriver.Service] + DisplayName = %WFPSamplerCalloutDriverServiceName% + Description = %WFPSamplerCalloutDriverServiceDescription% + ServiceType = 1 ;/// SERVICE_KERNEL_DRIVER + StartType = 0 ;/// SERVICE_BOOT_START + ErrorControl = 1 ;/// SERVICE_ERROR_NORMAL + ServiceBinary = %12%\WFPSamplerCalloutDriver.sys ;/// %WinDir%\System32\Drivers\WFPSamplerCalloutDriver.sys + LoadOrderGroup = NDIS ;/// Load immediately after TCPIP.sys + Dependencies = TCPIP + +[Strings] + ProviderString = "TODO-Set-Provider" + WFPCalloutClassName = "WFP Callout" + WFPSamplerCalloutDriverDeviceDescription = "WFP Sampler Callout Device" + WFPSamplerCalloutDriverDescription = "WFP Sampler Callout Driver" + WFPSamplerCalloutDriverServiceName = "WFPSamplerCallouts" + WFPSamplerCalloutDriverServiceDescription = "WFP Sampler Service - Callouts" + WFPSamplerCalloutDriverDiskId = "WFPSampler Installation Disk" diff --git a/network/trans/WFPSampler/sys/WFPSamplerCalloutDriver.vcxproj b/network/trans/WFPSampler/sys/WFPSamplerCalloutDriver.vcxproj new file mode 100644 index 000000000..4c508f668 --- /dev/null +++ b/network/trans/WFPSampler/sys/WFPSamplerCalloutDriver.vcxproj @@ -0,0 +1,239 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {6F3FAA0B-ABE5-47D5-9110-DB54E8FF0EE3} + $(MSBuildProjectName) + 1 + 11 + Debug + Win32 + {3E644B07-F227-4361-93F6-32B4E32551C0} + + + + 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 + .\$(IntDir) + true + WFPSamplerCallouts + 53504657-6D61-6C70-6572-5F496E746572 + DbgPrintEx(COMPID,LEVEL,MSG,...) + WPP_INIT_TRACING + + + * + true + $(InfArch) + true + .\$(IntDir)\WFPSamplerCalloutDriver.inf + + + true + true + .\$(IntDir) + true + WFPSamplerCallouts + 53504657-6D61-6C70-6572-5F496E746572 + DbgPrintEx(COMPID,LEVEL,MSG,...) + WPP_INIT_TRACING + + + + WFPSamplerCalloutDriver + + + WFPSamplerCalloutDriver + + + WFPSamplerCalloutDriver + + + WFPSamplerCalloutDriver + + + + true + Level4 + %(PreprocessorDefinitions);WIN32_LEAN_AND_MEAN;UNICODE;_UNICODE;NDIS630 + %(AdditionalIncludeDirectories);..\inc;..\syslib;$(DDK_INC_PATH) + + + %(PreprocessorDefinitions);WIN32_LEAN_AND_MEAN;UNICODE;_UNICODE;NDIS630 + %(AdditionalIncludeDirectories);..\inc;..\syslib;$(DDK_INC_PATH) + + + %(PreprocessorDefinitions);WIN32_LEAN_AND_MEAN;UNICODE;_UNICODE;NDIS630 + %(AdditionalIncludeDirectories);..\inc;..\syslib;$(DDK_INC_PATH) + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\NTOSKrnl.lib;$(DDK_LIB_PATH)\FwpKClnt.lib;$(DDK_LIB_PATH)\NetIO.lib;$(DDK_LIB_PATH)\NDIS.lib;$(DDK_LIB_PATH)\WDMSec.lib;$(SDK_LIB_PATH)\UUID.lib;.\..\syslib\$(IntDir)\WFPSampler.lib + + + + + true + Level4 + %(PreprocessorDefinitions);WIN32_LEAN_AND_MEAN;UNICODE;_UNICODE;NDIS630 + %(AdditionalIncludeDirectories);..\inc;..\syslib;$(DDK_INC_PATH) + + + %(PreprocessorDefinitions);WIN32_LEAN_AND_MEAN;UNICODE;_UNICODE;NDIS630 + %(AdditionalIncludeDirectories);..\inc;..\syslib;$(DDK_INC_PATH) + + + %(PreprocessorDefinitions);WIN32_LEAN_AND_MEAN;UNICODE;_UNICODE;NDIS630 + %(AdditionalIncludeDirectories);..\inc;..\syslib;$(DDK_INC_PATH) + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\NTOSKrnl.lib;$(DDK_LIB_PATH)\FwpKClnt.lib;$(DDK_LIB_PATH)\NetIO.lib;$(DDK_LIB_PATH)\NDIS.lib;$(DDK_LIB_PATH)\WDMSec.lib;$(SDK_LIB_PATH)\UUID.lib;.\..\syslib\$(IntDir)\WFPSampler.lib + + + + + true + Level4 + %(PreprocessorDefinitions);WIN32_LEAN_AND_MEAN;UNICODE;_UNICODE;NDIS630 + %(AdditionalIncludeDirectories);..\inc;..\syslib;$(DDK_INC_PATH) + + + %(PreprocessorDefinitions);WIN32_LEAN_AND_MEAN;UNICODE;_UNICODE;NDIS630 + %(AdditionalIncludeDirectories);..\inc;..\syslib;$(DDK_INC_PATH) + + + %(PreprocessorDefinitions);WIN32_LEAN_AND_MEAN;UNICODE;_UNICODE;NDIS630 + %(AdditionalIncludeDirectories);..\inc;..\syslib;$(DDK_INC_PATH) + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\NTOSKrnl.lib;$(DDK_LIB_PATH)\FwpKClnt.lib;$(DDK_LIB_PATH)\NetIO.lib;$(DDK_LIB_PATH)\NDIS.lib;$(DDK_LIB_PATH)\WDMSec.lib;$(SDK_LIB_PATH)\UUID.lib;.\..\syslib\$(IntDir)\WFPSampler.lib + + + + + true + Level4 + %(PreprocessorDefinitions);WIN32_LEAN_AND_MEAN;UNICODE;_UNICODE;NDIS630 + %(AdditionalIncludeDirectories);..\inc;..\syslib;$(DDK_INC_PATH) + + + %(PreprocessorDefinitions);WIN32_LEAN_AND_MEAN;UNICODE;_UNICODE;NDIS630 + %(AdditionalIncludeDirectories);..\inc;..\syslib;$(DDK_INC_PATH) + + + %(PreprocessorDefinitions);WIN32_LEAN_AND_MEAN;UNICODE;_UNICODE;NDIS630 + %(AdditionalIncludeDirectories);..\inc;..\syslib;$(DDK_INC_PATH) + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\NTOSKrnl.lib;$(DDK_LIB_PATH)\FwpKClnt.lib;$(DDK_LIB_PATH)\NetIO.lib;$(DDK_LIB_PATH)\NDIS.lib;$(DDK_LIB_PATH)\WDMSec.lib;$(SDK_LIB_PATH)\UUID.lib;.\..\syslib\$(IntDir)\WFPSampler.lib + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/network/trans/WFPSampler/sys/WFPSamplerCalloutDriver.vcxproj.Filters b/network/trans/WFPSampler/sys/WFPSamplerCalloutDriver.vcxproj.Filters new file mode 100644 index 000000000..9f9e53a02 --- /dev/null +++ b/network/trans/WFPSampler/sys/WFPSamplerCalloutDriver.vcxproj.Filters @@ -0,0 +1,126 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {C90430E1-22EE-4B6F-B26C-B62F8B7F7501} + + + h;hpp;hxx;hm;inl;inc;xsd + {DABF0058-9631-4C98-9FFD-1F764BC6A528} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {4AB31C3E-00AC-4265-9B26-F2B386FC6580} + + + inf;inv;inx;mof;mc; + {FA8CA4F6-420C-4B63-9CB1-9669BFFF827E} + + + + + 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 + + + Source Files + + + Source Files + + + + + Driver Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/network/trans/WFPSampler/syslib/HelperFunctions_ClassifyData.cpp b/network/trans/WFPSampler/syslib/HelperFunctions_ClassifyData.cpp new file mode 100644 index 000000000..fa2d41033 --- /dev/null +++ b/network/trans/WFPSampler/syslib/HelperFunctions_ClassifyData.cpp @@ -0,0 +1,493 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_ClassifyData.cpp +// +// Abstract: +// This module contains kernel helper functions that assist with CLASSIFY_DATA. +// +// Naming Convention: +// +// +// +// i.e. +// +// KrnlHlprClassifyDataCreateLocalCopy +// +// +// KrnlHlpr - Function is located in syslib\ and applies to kernel mode. +// +// ClassifyData - Function pertains to CLASSIFY_DATA objects. +// +// { +// Acquire - Function will take a reference on an object +// Create - Function allocates and fills memory. +// Destroy - Function cleans up and frees memory. +// Release - Function releases the reference on the object +// } +// +// LocalCopy - Function performs a deep copy of the parameters or takes references +// on complex structures. +// +// Private Functions: +// +// Public Functions: +// KrnlHlprAcquireDataCreateLocalCopy(), +// KrnlHlprClassifyDataCreateLocalCopy(), +// KrnlHlprClassifyDataDestroyLocalCopy(), +// KrnlHlprClassifyDataReleaseLocalCopy(), +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "HelperFunctions_Include.h" /// . +#include "HelperFunctions_ClassifyData.tmh" /// $(OBJ_PATH)\$(O)\ + +INT64 g_OutstandingNBLReferences = 0; + +/** + @kernel_helper_function="KrnlHlprClassifyDataReleaseLocalCopy" + + Purpose: Release reference on NBLs and cleanup a local copy of a CLASSIFY_DATA.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551159.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551208.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprClassifyDataReleaseLocalCopy(_Inout_ CLASSIFY_DATA* pClassifyData) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprClassifyDataReleaseLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(pClassifyData); + + KrnlHlprFwpsClassifyOutDestroyLocalCopy((FWPS_CLASSIFY_OUT**)&(pClassifyData->pClassifyOut)); + + pClassifyData->flowContext = 0; + + KrnlHlprFwpsFilterDestroyLocalCopy((FWPS_FILTER**)&(pClassifyData->pFilter)); + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + if(pClassifyData->classifyContextHandle) + FwpsReleaseClassifyHandle(pClassifyData->classifyContextHandle); + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + if(pClassifyData->pPacket) + { + if(pClassifyData->pClassifyValues) + { + if(pClassifyData->pClassifyValues->layerId == FWPS_LAYER_STREAM_V4 || + pClassifyData->pClassifyValues->layerId == FWPS_LAYER_STREAM_V4_DISCARD || + pClassifyData->pClassifyValues->layerId == FWPS_LAYER_STREAM_V6 || + pClassifyData->pClassifyValues->layerId == FWPS_LAYER_STREAM_V6_DISCARD) + KrnlHlprFwpsStreamCalloutIOPacketDestroyLocalCopy((FWPS_STREAM_CALLOUT_IO_PACKET**)&(pClassifyData->pPacket)); + else if(pClassifyData->chainedNBL) + { + BOOLEAN isDispatch = (KeGetCurrentIrql() == DISPATCH_LEVEL) ? TRUE : FALSE; + UINT32 numChainedNBLs = pClassifyData->numChainedNBLs; + + for(NET_BUFFER_LIST* pCurrentNBL = (NET_BUFFER_LIST*)pClassifyData->pPacket; + pCurrentNBL && + numChainedNBLs; + numChainedNBLs--) + { + NET_BUFFER_LIST* pNextNBL = NET_BUFFER_LIST_NEXT_NBL(pCurrentNBL); + + FwpsDereferenceNetBufferList(pCurrentNBL, + isDispatch); + + pCurrentNBL = pNextNBL; + +#if DBG + + InterlockedDecrement64((LONG64*)&(g_OutstandingNBLReferences)); + +#endif /// DBG + + } + } + else + { + BOOLEAN isDispatch = (KeGetCurrentIrql() == DISPATCH_LEVEL) ? TRUE : FALSE; + + FwpsDereferenceNetBufferList((NET_BUFFER_LIST*)pClassifyData->pPacket, + isDispatch); + +#if DBG + + InterlockedDecrement64((LONG64*)&(g_OutstandingNBLReferences)); + +#endif /// DBG + + } + } + + pClassifyData->pPacket = 0; + } + + KrnlHlprFwpsIncomingMetadataValuesDestroyLocalCopy((FWPS_INCOMING_METADATA_VALUES**)&(pClassifyData->pMetadataValues)); + + if(pClassifyData->pClassifyValues) + KrnlHlprFwpsIncomingValuesDestroyLocalCopy((FWPS_INCOMING_VALUES**)&(pClassifyData->pClassifyValues)); + + RtlZeroMemory(pClassifyData, + sizeof(CLASSIFY_DATA)); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprClassifyDataReleaseLocalCopy()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprClassifyDataDestroyLocalCopy" + + Purpose: Release reference on packet and cleanup and free a local copy of CLASSIFY_DATA.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_At_(*ppClassifyData, _Pre_ _Notnull_) +_At_(*ppClassifyData, _Post_ _Null_ __drv_freesMem(Pool)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(*ppClassifyData == 0) +inline VOID KrnlHlprClassifyDataDestroyLocalCopy(_Inout_ CLASSIFY_DATA** ppClassifyData) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprClassifyDataDestroyLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(ppClassifyData); + + if(*ppClassifyData) + { + KrnlHlprClassifyDataReleaseLocalCopy(*ppClassifyData); + + HLPR_DELETE(*ppClassifyData, + WFPSAMPLER_SYSLIB_TAG); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprClassifyDataDestroyLocalCopy()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprClassifyDataAcquireLocalCopy" + + Purpose: Ppopulate a CLASSIFY_DATA with a local copy of data obtained from a + callout's classification. This local copy requiires taking a reference + on pPacket.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF550085.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551206.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprClassifyDataAcquireLocalCopy(_Inout_ CLASSIFY_DATA* pClassifyData, + _In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _In_opt_ VOID* pPacket, + _In_opt_ const VOID* pClassifyContext, + _In_ const FWPS_FILTER* pFilter, + _In_ const UINT64 flowContext, + _In_ FWPS_CLASSIFY_OUT* pClassifyOut) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprClassifyDataAcquireLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(pClassifyData); + NT_ASSERT(pClassifyValues); + NT_ASSERT(pMetadata); + NT_ASSERT(pFilter); + NT_ASSERT(pClassifyOut); + + NTSTATUS status = STATUS_SUCCESS; + + pClassifyData->pClassifyValues = KrnlHlprFwpsIncomingValuesCreateLocalCopy(pClassifyValues); + HLPR_BAIL_ON_NULL_POINTER_WITH_STATUS(pClassifyData->pClassifyValues, + status); + + pClassifyData->pMetadataValues = KrnlHlprFwpsIncomingMetadataValuesCreateLocalCopy(pMetadata); + HLPR_BAIL_ON_NULL_POINTER_WITH_STATUS(pClassifyData->pMetadataValues, + status); + + if(pPacket) + { + if(pClassifyValues->layerId == FWPS_LAYER_STREAM_V4 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_V4_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_STREAM_V6 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_V6_DISCARD) + { + pClassifyData->pPacket = KrnlHlprFwpsStreamCalloutIOPacketCreateLocalCopy((FWPS_STREAM_CALLOUT_IO_PACKET*)pPacket); + HLPR_BAIL_ON_NULL_POINTER_WITH_STATUS(pClassifyData->pPacket, + status); + } + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + /// LayerData at the FWPM_LAYER_ALE_{BIND/CONNECT}_REDIRECT_V{4/6} is obtained via KrnlHlprRedirectDataCreate() + else if(pClassifyValues->layerId == FWPS_LAYER_ALE_CONNECT_REDIRECT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_CONNECT_REDIRECT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_BIND_REDIRECT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_BIND_REDIRECT_V6) + { + pClassifyData->pPacket = 0; + } + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + else + { + if(NET_BUFFER_LIST_NEXT_NBL((NET_BUFFER_LIST*)pPacket)) + { + pClassifyData->chainedNBL = TRUE; + pClassifyData->numChainedNBLs = 1; + } + + if(pClassifyData->chainedNBL && + ( + /// The IPPACKET and IPFORWARD Layers allow for Fragment Grouping if the option is enabled + pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6 || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V4 || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V6 + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + /// The NDIS layers allow for batched NBLs provided the callout was registered with FWP_CALLOUT_FLAG_ALLOW_L2_BATCH_CLASSIFY set + || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_MAC_FRAME_ETHERNET || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_MAC_FRAME_ETHERNET || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_MAC_FRAME_NATIVE || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_MAC_FRAME_NATIVE || + pClassifyValues->layerId == FWPS_LAYER_INGRESS_VSWITCH_ETHERNET || + pClassifyValues->layerId == FWPS_LAYER_EGRESS_VSWITCH_ETHERNET + +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) + + )) + { + for(NET_BUFFER_LIST* pCurrentNBL = (NET_BUFFER_LIST*)pPacket; + pCurrentNBL; + pClassifyData->numChainedNBLs++) + { + NET_BUFFER_LIST* pNextNBL = NET_BUFFER_LIST_NEXT_NBL(pCurrentNBL); + + FwpsReferenceNetBufferList(pCurrentNBL, + TRUE); + + pCurrentNBL = pNextNBL; + +#if DBG + + InterlockedIncrement64((LONG64*)&(g_OutstandingNBLReferences)); + +#endif /// DBG + + } + + pClassifyData->pPacket = pPacket; + } + else + { + /// Otherwise we expect to receive a single NBL + NT_ASSERT(NET_BUFFER_LIST_NEXT_NBL((NET_BUFFER_LIST*)pPacket) == 0); + + FwpsReferenceNetBufferList((NET_BUFFER_LIST*)pPacket, + TRUE); + + pClassifyData->pPacket = pPacket; + +#if DBG + + InterlockedIncrement64((LONG64*)&(g_OutstandingNBLReferences)); + +#endif /// DBG + + } + } + } + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + if(pClassifyContext) + { + /// ClassifyHandle for these layers is obtained in REDIRECT_DATA + if(pClassifyValues->layerId != FWPS_LAYER_ALE_CONNECT_REDIRECT_V4 && + pClassifyValues->layerId != FWPS_LAYER_ALE_CONNECT_REDIRECT_V6 && + pClassifyValues->layerId != FWPS_LAYER_ALE_BIND_REDIRECT_V4 && + pClassifyValues->layerId != FWPS_LAYER_ALE_BIND_REDIRECT_V6) + { + status = FwpsAcquireClassifyHandle((VOID*)pClassifyContext, + 0, + &(pClassifyData->classifyContextHandle)); + HLPR_BAIL_ON_FAILURE(status); + } + } +#else + + UNREFERENCED_PARAMETER(pClassifyContext); + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + if(pFilter) + { + pClassifyData->pFilter = KrnlHlprFwpsFilterCreateLocalCopy(pFilter); + HLPR_BAIL_ON_NULL_POINTER_WITH_STATUS(pClassifyData->pFilter, + status); + } + + pClassifyData->flowContext = flowContext; + + if(pClassifyOut) + { + pClassifyData->pClassifyOut = KrnlHlprFwpsClassifyOutCreateLocalCopy(pClassifyOut); + HLPR_BAIL_ON_NULL_POINTER_WITH_STATUS(pClassifyData->pClassifyOut, + status); + } + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS) + KrnlHlprClassifyDataReleaseLocalCopy(pClassifyData); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprClassifyDataAcquireLocalCopy() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @kernel_helper_function="KrnlHlprClassifyDataCreateLocalCopy" + + Purpose: Allocate and populate a CLASSIFY_DATA with a local copy of data obtained from a + callout's classifyFn. This local copy requiires taking a reference on pPacket.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_At_(*ppClassifyData, _Pre_ _Null_) +_When_(return != STATUS_SUCCESS, _At_(*ppClassifyData, _Post_ _Null_)) +_When_(return == STATUS_SUCCESS, _At_(*ppClassifyData, _Post_ _Notnull_ __drv_allocatesMem(Pool))) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprClassifyDataCreateLocalCopy(_Outptr_ CLASSIFY_DATA** ppClassifyData, + _In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _In_opt_ VOID* pPacket, + _In_opt_ const VOID* pClassifyContext, + _In_ const FWPS_FILTER* pFilter, + _In_ const UINT64 flowContext, + _In_ FWPS_CLASSIFY_OUT* pClassifyOut) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprClassifyDataCreateLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(ppClassifyData); + NT_ASSERT(pClassifyValues); + NT_ASSERT(pMetadata); + NT_ASSERT(pFilter); + NT_ASSERT(pClassifyOut); + + NTSTATUS status = STATUS_SUCCESS; + + HLPR_NEW(*ppClassifyData, + CLASSIFY_DATA, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(*ppClassifyData, + status); + + status = KrnlHlprClassifyDataAcquireLocalCopy(*ppClassifyData, + pClassifyValues, + pMetadata, + pPacket, + pClassifyContext, + pFilter, + flowContext, + pClassifyOut); + + HLPR_BAIL_LABEL: + +#pragma warning(push) +#pragma warning(disable: 6001) /// *ppClassifyData initialized with call to HLPR_NEW & KrnlHlprClassifyDataAcquireLocalCopy + + if(status != STATUS_SUCCESS && + *ppClassifyData) + KrnlHlprClassifyDataDestroyLocalCopy(ppClassifyData); + +#pragma warning(pop) + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprClassifyDataCreateLocalCopy() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} diff --git a/network/trans/WFPSampler/syslib/HelperFunctions_ClassifyData.h b/network/trans/WFPSampler/syslib/HelperFunctions_ClassifyData.h new file mode 100644 index 000000000..6aa6e8388 --- /dev/null +++ b/network/trans/WFPSampler/syslib/HelperFunctions_ClassifyData.h @@ -0,0 +1,87 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_ClassifyData.h +// +// Abstract: +// This module contains prototypes for kernel helper functions that assist with CLASSIFY_DATA. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef HELPERFUNCTIONS_CLASSIFY_DATA_H +#define HELPERFUNCTIONS_CLASSIFY_DATA_H + +#if DBG + +extern INT64 g_OutstandingNBLReferences; + +#endif /// DBG + +typedef struct CLASSIFY_DATA_ +{ + const FWPS_INCOMING_VALUES* pClassifyValues; + const FWPS_INCOMING_METADATA_VALUES* pMetadataValues; + VOID* pPacket; /// NET_BUFFER_LIST | FWPS_STREAM_CALLOUT_IO_PACKET + const VOID* pClassifyContext; + const FWPS_FILTER* pFilter; + UINT64 flowContext; + FWPS_CLASSIFY_OUT* pClassifyOut; + UINT64 classifyContextHandle; + BOOLEAN chainedNBL; + UINT32 numChainedNBLs; +}CLASSIFY_DATA, *PCLASSIFY_DATA; + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprClassifyDataReleaseLocalCopy(_Inout_ CLASSIFY_DATA* pClassifyData); + +_At_(*ppClassifyData, _Pre_ _Notnull_) +_At_(*ppClassifyData, _Post_ _Null_ __drv_freesMem(Pool)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(*ppClassifyData == 0) +inline VOID KrnlHlprClassifyDataDestroyLocalCopy(_Inout_ CLASSIFY_DATA** ppClassifyData); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprClassifyDataAcquireLocalCopy(_Inout_ CLASSIFY_DATA* pClassifyData, + _In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _In_opt_ VOID* pPacket, + _In_opt_ const VOID* pClassifyContext, + _In_ const FWPS_FILTER* pFilter, + _In_ const UINT64 flowContext, + _In_ FWPS_CLASSIFY_OUT* pClassifyOut); + +_At_(*ppClassifyData, _Pre_ _Null_) +_When_(return != STATUS_SUCCESS, _At_(*ppClassifyData, _Post_ _Null_)) +_When_(return == STATUS_SUCCESS, _At_(*ppClassifyData, _Post_ _Notnull_ __drv_allocatesMem(Pool))) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprClassifyDataCreateLocalCopy(_Outptr_ CLASSIFY_DATA** ppClassifyData, + _In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _In_opt_ VOID* pPacket, + _In_opt_ const VOID* pClassifyContext, + _In_ const FWPS_FILTER* pFilter, + _In_ const UINT64 flowContext, + _In_ FWPS_CLASSIFY_OUT* pClassifyOut); + +#endif /// HELPERFUNCTIONS_CLASSIFY_DATA_H diff --git a/network/trans/WFPSampler/syslib/HelperFunctions_DeferredProcedureCalls.cpp b/network/trans/WFPSampler/syslib/HelperFunctions_DeferredProcedureCalls.cpp new file mode 100644 index 000000000..071c43f41 --- /dev/null +++ b/network/trans/WFPSampler/syslib/HelperFunctions_DeferredProcedureCalls.cpp @@ -0,0 +1,1630 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_DeferredProcedureCalls.cpp +// +// Abstract: +// This module contains kernel helper functions that assist with DPC and threaded DPC routines. +// +// Naming Convention: +// +// +// +// i.e. +// +// KrnlHlprDPCQueue +// +// +// KrnlHlpr - Function is located in syslib\ and applies to kernel mode. +// +// DPC - Function pertains to deferred procedure calls. +// DPCData - Function pertains to DPC_DATA objects. +// +// { +// Create - Function allocates and fills memory. +// Destroy - Function cleans up and frees memory. +// Populate - Function fills memory with values +// Purge - Function cleans up values +// Queue - Function will take a reference on an object +// Sleep - Function performs no operations for a set period. +// } +// +// Private Functions: +// +// Public Functions: +// KrnlHlprDPCDataCreate(), +// KrnlHlprDPCDataDestroy(), +// KrnlHlprDPCDataPopulate(), +// KrnlHlprDPCDataPurge(), +// KrnlHlprDPCQueue(), +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Add support for serializing asynchronous +// FWPM_LAYER_STREAM injections +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "HelperFunctions_Include.h" /// . +#include "HelperFunctions_DeferredProcedureCalls.tmh" /// $(OBJ_PATH)\$(O)\ + +#ifndef DPC____ +#define DPC____ + +/** + @kernel_helper_function="KrnlHlprDPCDataPurge" + + Purpose: Cleanup a DPC_DATA object.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprDPCDataPurge(_Inout_ DPC_DATA* pDPCData) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprDPCDataPurge()\n"); + +#endif /// DBG + + NT_ASSERT(pDPCData); + + RtlZeroMemory(pDPCData, + sizeof(DPC_DATA)); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprDPCDataPurge()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprDPCDataDestroy" + + Purpose: Cleanup and free a DPC_DATA object.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_At_(*ppDPCData, _Pre_ _Notnull_) +_At_(*ppDPCData, _Post_ _Null_ __drv_freesMem(Pool)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(*ppDPCData == 0) +inline VOID KrnlHlprDPCDataDestroy(_Inout_ DPC_DATA** ppDPCData) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprDPCDataDestroy()\n"); + +#endif /// DBG + + NT_ASSERT(ppDPCData); + + if(*ppDPCData) + { + KrnlHlprDPCDataPurge(*ppDPCData); + + HLPR_DELETE(*ppDPCData, + WFPSAMPLER_SYSLIB_TAG); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprDPCDataDestroy()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprDPCDataPopulate" + + Purpose: Populates a DPC_DATA object with the classifyData, injectionData, and context + supplied.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprDPCDataPopulate(_Inout_ DPC_DATA* pDPCData, + _In_ CLASSIFY_DATA* pClassifyData, + _In_opt_ INJECTION_DATA* pInjectionData, /* 0 */ + _In_opt_ VOID* pContext) /* 0 */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprDPCDataPopulate()\n"); + +#endif /// DBG + + NT_ASSERT(pDPCData); + NT_ASSERT(pClassifyData); + + pDPCData->pClassifyData = pClassifyData; + pDPCData->pInjectionData = pInjectionData; + pDPCData->pContext = pContext; + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprDPCDataPopulate()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprDPCDataPopulate" + + Purpose: Populates a DPC_DATA object with the classifyData, pendData, and context supplied.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprDPCDataPopulate(_Inout_ DPC_DATA* pDPCData, + _In_ CLASSIFY_DATA* pClassifyData, + _In_ PEND_DATA* pPendData, + _In_opt_ VOID* pContext) /* 0 */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprDPCDataPopulate()\n"); + +#endif /// DBG + + NT_ASSERT(pDPCData); + NT_ASSERT(pClassifyData); + NT_ASSERT(pPendData); + + pDPCData->pClassifyData = pClassifyData; + pDPCData->pPendData = pPendData; + pDPCData->pContext = pContext; + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprDPCDataPopulate()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprDPCDataPopulate" + + Purpose: Populates a DPC_DATA object with the classifyData, redirectData, and context + supplied.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprDPCDataPopulate(_Inout_ DPC_DATA* pDPCData, + _In_ CLASSIFY_DATA* pClassifyData, + _In_ REDIRECT_DATA* pRedirectData, + _In_opt_ VOID* pContext) /* 0 */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprDPCDataPopulate()\n"); + +#endif /// DBG + + NT_ASSERT(pDPCData); + NT_ASSERT(pClassifyData); + NT_ASSERT(pRedirectData); + + pDPCData->pClassifyData = pClassifyData; + pDPCData->pRedirectData = pRedirectData; + pDPCData->pContext = pContext; + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprDPCDataPopulate()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprDPCDataPopulate" + + Purpose: Populates a DPC_DATA object with the notifyData and context supplied.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprDPCDataPopulate(_Inout_ DPC_DATA* pDPCData, + _In_ NOTIFY_DATA* pNotifyData, + _In_opt_ VOID* pContext) /* 0 */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprDPCDataPopulate()\n"); + +#endif /// DBG + + NT_ASSERT(pDPCData); + NT_ASSERT(pNotifyData); + + pDPCData->pNotifyData = pNotifyData; + pDPCData->pContext = pContext; + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprDPCDataPopulate()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprDPCDataPopulate" + + Purpose: Populates a DPC_DATA object with the pendData, and context supplied.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprDPCDataPopulate(_Inout_ DPC_DATA* pDPCData, + _In_ PEND_DATA* pPendData, + _In_opt_ VOID* pContext) /* 0 */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprDPCDataPopulate()\n"); + +#endif /// DBG + + NT_ASSERT(pDPCData); + NT_ASSERT(pPendData); + + pDPCData->pPendData = pPendData; + pDPCData->pContext = pContext; + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprDPCDataPopulate()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprDPCDataCreate" + + Purpose: Allocates and populates a DPC_DATA object with the KDPC, classifyData, + injectionData, and context supplied.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_At_(*ppDPCData, _Pre_ _Null_) +_When_(return != STATUS_SUCCESS, _At_(*ppDPCData, _Post_ _Null_)) +_When_(return == STATUS_SUCCESS, _At_(*ppDPCData, _Post_ _Notnull_ __drv_allocatesMem(Pool))) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprDPCDataCreate(_Outptr_ DPC_DATA** ppDPCData, + _In_ CLASSIFY_DATA* pClassifyData, + _In_opt_ INJECTION_DATA* pInjectionData, /* 0 */ + _In_opt_ VOID* pContext) /* 0 */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprDPCDataCreate()\n"); + +#endif /// DBG + + NT_ASSERT(ppDPCData); + NT_ASSERT(pClassifyData); + NT_ASSERT(pInjectionData); + + NTSTATUS status = STATUS_SUCCESS; + + HLPR_NEW(*ppDPCData, + DPC_DATA, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(*ppDPCData, + status); + + KrnlHlprDPCDataPopulate(*ppDPCData, + pClassifyData, + pInjectionData, + pContext); + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS) + KrnlHlprDPCDataDestroy(ppDPCData); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprDPCDataCreate() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @kernel_helper_function="KrnlHlprDPCDataCreate" + + Purpose: Allocates and populates a DPC_DATA object with the KDPC, classifyData, pendData, + and context supplied.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_At_(*ppDPCData, _Pre_ _Null_) +_When_(return != STATUS_SUCCESS, _At_(*ppDPCData, _Post_ _Null_)) +_When_(return == STATUS_SUCCESS, _At_(*ppDPCData, _Post_ _Notnull_ __drv_allocatesMem(Pool))) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprDPCDataCreate(_Outptr_ DPC_DATA** ppDPCData, + _In_ CLASSIFY_DATA* pClassifyData, + _In_ PEND_DATA* pPendData, + _In_opt_ VOID* pContext) /* 0 */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprDPCDataCreate()\n"); + +#endif /// DBG + + NT_ASSERT(ppDPCData); + NT_ASSERT(pClassifyData); + NT_ASSERT(pPendData); + + NTSTATUS status = STATUS_SUCCESS; + + HLPR_NEW(*ppDPCData, + DPC_DATA, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(*ppDPCData, + status); + + KrnlHlprDPCDataPopulate(*ppDPCData, + pClassifyData, + pPendData, + pContext); + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS) + KrnlHlprDPCDataDestroy(ppDPCData); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprDPCDataCreate() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @kernel_helper_function="KrnlHlprDPCDataCreate" + + Purpose: Allocates and populates a DPC_DATA object with the KDPC, classifyData, + redirectData, and context supplied.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_At_(*ppDPCData, _Pre_ _Null_) +_When_(return != STATUS_SUCCESS, _At_(*ppDPCData, _Post_ _Null_)) +_When_(return == STATUS_SUCCESS, _At_(*ppDPCData, _Post_ _Notnull_ __drv_allocatesMem(Pool))) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprDPCDataCreate(_Outptr_ DPC_DATA** ppDPCData, + _In_ CLASSIFY_DATA* pClassifyData, + _In_ REDIRECT_DATA* pRedirectData, + _In_opt_ VOID* pContext) /* 0 */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprDPCDataCreate()\n"); + +#endif /// DBG + + NT_ASSERT(ppDPCData); + NT_ASSERT(pClassifyData); + NT_ASSERT(pRedirectData); + + NTSTATUS status = STATUS_SUCCESS; + + HLPR_NEW(*ppDPCData, + DPC_DATA, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(*ppDPCData, + status); + + KrnlHlprDPCDataPopulate(*ppDPCData, + pClassifyData, + pRedirectData, + pContext); + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS) + KrnlHlprDPCDataDestroy(ppDPCData); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprDPCDataCreate() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @kernel_helper_function="KrnlHlprDPCDataCreate" + + Purpose: Allocates and populates a DPC_DATA object with the notifyData, KDPC, and context + supplied.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_At_(*ppDPCData, _Pre_ _Null_) +_When_(return != STATUS_SUCCESS, _At_(*ppDPCData, _Post_ _Null_)) +_When_(return == STATUS_SUCCESS, _At_(*ppDPCData, _Post_ _Notnull_ __drv_allocatesMem(Pool))) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprDPCDataCreate(_Outptr_ DPC_DATA** ppDPCData, + _In_ NOTIFY_DATA* pNotifyData, + _In_opt_ VOID* pContext) /* 0 */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprDPCDataCreate()\n"); + +#endif /// DBG + + NT_ASSERT(ppDPCData); + NT_ASSERT(pNotifyData); + + NTSTATUS status = STATUS_SUCCESS; + + HLPR_NEW(*ppDPCData, + DPC_DATA, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(*ppDPCData, + status); + + KrnlHlprDPCDataPopulate(*ppDPCData, + pNotifyData, + pContext); + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS) + KrnlHlprDPCDataDestroy(ppDPCData); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprDPCDataCreate() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @kernel_helper_function="KrnlHlprDPCDataCreate" + + Purpose: Allocates and populates a DPC_DATA object with the KDPC, pendData, and context + supplied.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_At_(*ppDPCData, _Pre_ _Null_) +_When_(return != STATUS_SUCCESS, _At_(*ppDPCData, _Post_ _Null_)) +_When_(return == STATUS_SUCCESS, _At_(*ppDPCData, _Post_ _Notnull_ __drv_allocatesMem(Pool))) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprDPCDataCreate(_Outptr_ DPC_DATA** ppDPCData, + _In_ PEND_DATA* pPendData, + _In_opt_ VOID* pContext) /* 0 */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprDPCDataCreate()\n"); + +#endif /// DBG + + NT_ASSERT(ppDPCData); + NT_ASSERT(pPendData); + + NTSTATUS status = STATUS_SUCCESS; + + HLPR_NEW(*ppDPCData, + DPC_DATA, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(*ppDPCData, + status); + + KrnlHlprDPCDataPopulate(*ppDPCData, + pPendData, + pContext); + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS) + KrnlHlprDPCDataDestroy(ppDPCData); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprDPCDataCreate() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @kernel_helper_function="KrnlHlprDPCQueue" + + Purpose: Queue a DPC for later execution at DISPATCH_LEVEL.
+
+ Notes: Data is expected to be obtained by other means (i.e. LIST_ENTRY)
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552130.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552185.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprDPCQueue(_In_ KDEFERRED_ROUTINE* pDPCFn, + _In_ UINT32 processorNumber, /* 0 */ + _In_ BOOLEAN forceProcessor) /* FALSE */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprDPCQueue()\n"); + +#endif /// DBG + + NT_ASSERT(pDPCFn); + + NTSTATUS status = STATUS_SUCCESS; + DPC_DATA* pDPCData = 0; + + HLPR_NEW(pDPCData, + DPC_DATA, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pDPCData, + status); + + KeInitializeDpc(&(pDPCData->kdpc), + pDPCFn, + 0); + + if(forceProcessor) + KeSetTargetProcessorDpc(&(pDPCData->kdpc), + (CCHAR)processorNumber); + + KeInsertQueueDpc(&(pDPCData->kdpc), + pDPCData, + 0); + + HLPR_BAIL_LABEL: + +#pragma warning(push) +#pragma warning(disable: 6001) /// pDPCData initialized with call to HLPR_NEW + + if(status != STATUS_SUCCESS && + pDPCData) + KrnlHlprDPCDataDestroy(&pDPCData); + +#pragma warning(pop) + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprDPCQueue() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + + +/** + @kernel_helper_function="KrnlHlprDPCQueue" + + Purpose: Queue a DPC for later execution at DISPATCH_LEVEL.
+
+ Notes: INJECTION_DATA specific.
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552130.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552185.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprDPCQueue(_In_ KDEFERRED_ROUTINE* pDPCFn, + _In_ CLASSIFY_DATA* pClassifyData, + _In_opt_ INJECTION_DATA* pInjectionData, /* 0 */ + _In_opt_ VOID* pContext, /* 0 */ + _In_ UINT32 processorNumber, /* 0 */ + _In_ BOOLEAN forceProcessor) /* FALSE */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprDPCQueue()\n"); + +#endif /// DBG + + NT_ASSERT(pDPCFn); + NT_ASSERT(pClassifyData); + + NTSTATUS status = STATUS_SUCCESS; + DPC_DATA* pDPCData = 0; + + status = KrnlHlprDPCDataCreate(&pDPCData, + pClassifyData, + pInjectionData, + pContext); + HLPR_BAIL_ON_FAILURE(status); + + KeInitializeDpc(&(pDPCData->kdpc), + pDPCFn, + 0); + + if(forceProcessor) + KeSetTargetProcessorDpc(&(pDPCData->kdpc), + (CCHAR)processorNumber); + + KeInsertQueueDpc(&(pDPCData->kdpc), + pDPCData, + 0); + + HLPR_BAIL_LABEL: + +#pragma warning(push) +#pragma warning(disable: 6001) /// pDPCData initialized with call to KrnlHlprDPCDataCreate + + if(status != STATUS_SUCCESS && + pDPCData) + KrnlHlprDPCDataDestroy(&pDPCData); + +#pragma warning(pop) + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprDPCQueue() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @kernel_helper_function="KrnlHlprDPCQueue" + + Purpose: Queue a DPC for later execution at DISPATCH_LEVEL.
+
+ Notes: PEND_DATA specific.
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552130.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552185.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprDPCQueue(_In_ KDEFERRED_ROUTINE* pDPCFn, + _In_ CLASSIFY_DATA* pClassifyData, + _In_ PEND_DATA* pPendData, + _In_opt_ VOID* pContext, /* 0 */ + _In_ UINT32 processorNumber, /* 0 */ + _In_ BOOLEAN forceProcessor) /* FALSE */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprDPCQueue()\n"); + +#endif /// DBG + + NT_ASSERT(pDPCFn); + NT_ASSERT(pClassifyData); + NT_ASSERT(pPendData); + + NTSTATUS status = STATUS_SUCCESS; + DPC_DATA* pDPCData = 0; + + status = KrnlHlprDPCDataCreate(&pDPCData, + pClassifyData, + pPendData, + pContext); + HLPR_BAIL_ON_FAILURE(status); + + KeInitializeDpc(&(pDPCData->kdpc), + pDPCFn, + 0); + + if(forceProcessor) + KeSetTargetProcessorDpc(&(pDPCData->kdpc), + (CCHAR)processorNumber); + + KeInsertQueueDpc(&(pDPCData->kdpc), + pDPCData, + 0); + + HLPR_BAIL_LABEL: + +#pragma warning(push) +#pragma warning(disable: 6001) /// pDPCData initialized with call to KrnlHlprDPCDataCreate + + if(status != STATUS_SUCCESS && + pDPCData) + KrnlHlprDPCDataDestroy(&pDPCData); + +#pragma warning(pop) + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprDPCQueue() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @kernel_helper_function="KrnlHlprDPCQueue" + + Purpose: Queue a DPC for later execution at DISPATCH_LEVEL.
+
+ Notes: REDIRECT_DATA specific.
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552130.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552185.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprDPCQueue(_In_ KDEFERRED_ROUTINE* pDPCFn, + _In_ CLASSIFY_DATA* pClassifyData, + _In_ REDIRECT_DATA* pRedirectData, + _In_opt_ VOID* pContext, /* 0 */ + _In_ UINT32 processorNumber, /* 0 */ + _In_ BOOLEAN forceProcessor) /* FALSE */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprDPCQueue()\n"); + +#endif /// DBG + + NT_ASSERT(pDPCFn); + NT_ASSERT(pClassifyData); + NT_ASSERT(pRedirectData); + + NTSTATUS status = STATUS_SUCCESS; + DPC_DATA* pDPCData = 0; + + status = KrnlHlprDPCDataCreate(&pDPCData, + pClassifyData, + pRedirectData, + pContext); + HLPR_BAIL_ON_FAILURE(status); + + KeInitializeDpc(&(pDPCData->kdpc), + pDPCFn, + 0); + + if(forceProcessor) + KeSetTargetProcessorDpc(&(pDPCData->kdpc), + (CCHAR)processorNumber); + + KeInsertQueueDpc(&(pDPCData->kdpc), + pDPCData, + 0); + + HLPR_BAIL_LABEL: + +#pragma warning(push) +#pragma warning(disable: 6001) /// pDPCData initialized with call to KrnlHlprDPCDataCreate + + if(status != STATUS_SUCCESS && + pDPCData) + KrnlHlprDPCDataDestroy(&pDPCData); + +#pragma warning(pop) + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprDPCQueue() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @kernel_helper_function="KrnlHlprDPCQueue" + + Purpose: Queue a DPC for later execution at DISPATCH_LEVEL.
+
+ Notes: NOTIFY_DATA specific.
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552130.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552185.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprDPCQueue(_In_ KDEFERRED_ROUTINE* pDPCFn, + _In_ NOTIFY_DATA* pNotifyData, + _In_opt_ VOID* pContext, /* 0 */ + _In_ UINT32 processorNumber, /* 0 */ + _In_ BOOLEAN forceProcessor) /* FALSE */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprDPCQueue()\n"); + +#endif /// DBG + + NT_ASSERT(pDPCFn); + NT_ASSERT(pNotifyData); + + NTSTATUS status = STATUS_SUCCESS; + DPC_DATA* pDPCData = 0; + + status = KrnlHlprDPCDataCreate(&pDPCData, + pNotifyData, + pContext); + HLPR_BAIL_ON_FAILURE(status); + + KeInitializeDpc(&(pDPCData->kdpc), + pDPCFn, + 0); + + if(forceProcessor) + KeSetTargetProcessorDpc(&(pDPCData->kdpc), + (CCHAR)processorNumber); + + KeInsertQueueDpc(&(pDPCData->kdpc), + pDPCData, + 0); + + HLPR_BAIL_LABEL: + +#pragma warning(push) +#pragma warning(disable: 6001) /// pDPCData initialized with call to KrnlHlprDPCDataCreate + + if(status != STATUS_SUCCESS && + pDPCData) + KrnlHlprDPCDataDestroy(&pDPCData); + +#pragma warning(pop) + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprDPCQueue() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @kernel_helper_function="KrnlHlprDPCQueue" + + Purpose: Queue a DPC for later execution at DISPATCH_LEVEL.
+
+ Notes: PEND_DATA specific.
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552130.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552185.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprDPCQueue(_In_ KDEFERRED_ROUTINE* pDPCFn, + _In_ PEND_DATA* pPendData, + _In_opt_ VOID* pContext, /* 0 */ + _In_ UINT32 processorNumber, /* 0 */ + _In_ BOOLEAN forceProcessor) /* FALSE */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprDPCQueue()\n"); + +#endif /// DBG + + NT_ASSERT(pDPCFn); + NT_ASSERT(pPendData); + + NTSTATUS status = STATUS_SUCCESS; + DPC_DATA* pDPCData = 0; + + status = KrnlHlprDPCDataCreate(&pDPCData, + pPendData, + pContext); + HLPR_BAIL_ON_FAILURE(status); + + KeInitializeDpc(&(pDPCData->kdpc), + pDPCFn, + 0); + + if(forceProcessor) + KeSetTargetProcessorDpc(&(pDPCData->kdpc), + (CCHAR)processorNumber); + + KeInsertQueueDpc(&(pDPCData->kdpc), + pDPCData, + 0); + + HLPR_BAIL_LABEL: + +#pragma warning(push) +#pragma warning(disable: 6001) /// pDPCData initialized with call to KrnlHlprDPCDataCreate + + if(status != STATUS_SUCCESS && + pDPCData) + KrnlHlprDPCDataDestroy(&pDPCData); + +#pragma warning(pop) + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprDPCQueue() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +#endif /// DPC____ + +#ifndef THREADED_DPC____ +#define THREADED_DPC____ + +/** + @kernel_helper_function="KrnlHlprThreadedDPCQueue" + + Purpose: Queue a threaded DPC for later execution at either PASSIVE_LEVEL or DISPATCH_LEVEL.
+
+ Notes: Data is expected to be obtained by other means (i.e. LIST_ENTRY)
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552166.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552185.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprThreadedDPCQueue(_In_ KDEFERRED_ROUTINE* pDPCFn, + _In_ UINT32 processorNumber, /* 0 */ + _In_ BOOLEAN forceProcessor) /* FALSE */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprThreadedDPCQueue()\n"); + +#endif /// DBG + + NT_ASSERT(pDPCFn); + + NTSTATUS status = STATUS_SUCCESS; + DPC_DATA* pDPCData = 0; + +#pragma warning(push) +#pragma warning(disable: 6014) /// pDPCData will be freed by caller + + HLPR_NEW(pDPCData, + DPC_DATA, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pDPCData, + status); + +#pragma warning(pop) + + KeInitializeThreadedDpc(&(pDPCData->kdpc), + pDPCFn, + 0); + + if(forceProcessor) + KeSetTargetProcessorDpc(&(pDPCData->kdpc), + (CCHAR)processorNumber); + + KeInsertQueueDpc(&(pDPCData->kdpc), + pDPCData, + 0); + + HLPR_BAIL_LABEL: + +#pragma warning(push) +#pragma warning(disable: 6001) /// pDPCData initialized with call to HLPR_NEW + + if(status != STATUS_SUCCESS && + pDPCData) + KrnlHlprDPCDataDestroy(&pDPCData); + +#pragma warning(pop) + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprThreadedDPCQueue() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + + +/** + @kernel_helper_function="KrnlHlprThreadedDPCQueue" + + Purpose: Queue a threaded DPC for later execution at either PASSIVE_LEVEL or DISPATCH_LEVEL.
+
+ Notes: INJECTION_DATA specific.
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552166.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552185.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprThreadedDPCQueue(_In_ KDEFERRED_ROUTINE* pDPCFn, + _In_ CLASSIFY_DATA* pClassifyData, + _In_opt_ INJECTION_DATA* pInjectionData, /* 0 */ + _In_opt_ VOID* pContext, /* 0 */ + _In_ UINT32 processorNumber, /* 0 */ + _In_ BOOLEAN forceProcessor) /* FALSE */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprThreadedDPCQueue()\n"); + +#endif /// DBG + + NT_ASSERT(pDPCFn); + NT_ASSERT(pClassifyData); + + NTSTATUS status = STATUS_SUCCESS; + DPC_DATA* pDPCData = 0; + +#pragma warning(push) +#pragma warning(disable: 6014) /// pDPCData will be freed by caller + + status = KrnlHlprDPCDataCreate(&pDPCData, + pClassifyData, + pInjectionData, + pContext); + HLPR_BAIL_ON_FAILURE(status); + +#pragma warning(pop) + + KeInitializeThreadedDpc(&(pDPCData->kdpc), + pDPCFn, + 0); + + if(forceProcessor) + KeSetTargetProcessorDpc(&(pDPCData->kdpc), + (CCHAR)processorNumber); + + KeInsertQueueDpc(&(pDPCData->kdpc), + pDPCData, + 0); + + HLPR_BAIL_LABEL: + +#pragma warning(push) +#pragma warning(disable: 6001) /// pDPCData initialized with call to KrnlHlprDPCDataCreate + + if(status != STATUS_SUCCESS && + pDPCData) + KrnlHlprDPCDataDestroy(&pDPCData); + +#pragma warning(pop) + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprThreadedDPCQueue() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @kernel_helper_function="KrnlHlprThreadedDPCQueue" + + Purpose: Queue a threaded DPC for later execution at either PASSIVE_LEVEL or DISPATCH_LEVEL.
+
+ Notes: PEND_DATA specific.
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552166.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552185.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprThreadedDPCQueue(_In_ KDEFERRED_ROUTINE* pDPCFn, + _In_ CLASSIFY_DATA* pClassifyData, + _In_ PEND_DATA* pPendData, + _In_opt_ VOID* pContext, /* 0 */ + _In_ UINT32 processorNumber, /* 0 */ + _In_ BOOLEAN forceProcessor) /* FALSE */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprThreadedDPCQueue()\n"); + +#endif /// DBG + + NT_ASSERT(pDPCFn); + NT_ASSERT(pClassifyData); + NT_ASSERT(pPendData); + + NTSTATUS status = STATUS_SUCCESS; + DPC_DATA* pDPCData = 0; + +#pragma warning(push) +#pragma warning(disable: 6014) /// pDPCData will be freed by caller + + status = KrnlHlprDPCDataCreate(&pDPCData, + pClassifyData, + pPendData, + pContext); + HLPR_BAIL_ON_FAILURE(status); + +#pragma warning(pop) + + KeInitializeThreadedDpc(&(pDPCData->kdpc), + pDPCFn, + 0); + + if(forceProcessor) + KeSetTargetProcessorDpc(&(pDPCData->kdpc), + (CCHAR)processorNumber); + + KeInsertQueueDpc(&(pDPCData->kdpc), + pDPCData, + 0); + + HLPR_BAIL_LABEL: + +#pragma warning(push) +#pragma warning(disable: 6001) /// pDPCData initialized with call to KrnlHlprDPCDataCreate + + if(status != STATUS_SUCCESS && + pDPCData) + KrnlHlprDPCDataDestroy(&pDPCData); + +#pragma warning(pop) + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprThreadedDPCQueue() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @kernel_helper_function="KrnlHlprThreadedDPCQueue" + + Purpose: Queue a threaded DPC for later execution at either PASSIVE_LEVEL or DISPATCH_LEVEL.
+
+ Notes: REDIRECT_DATA specific.
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552166.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552185.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprThreadedDPCQueue(_In_ KDEFERRED_ROUTINE* pDPCFn, + _In_ CLASSIFY_DATA* pClassifyData, + _In_ REDIRECT_DATA* pRedirectData, + _In_opt_ VOID* pContext, /* 0 */ + _In_ UINT32 processorNumber, /* 0 */ + _In_ BOOLEAN forceProcessor) /* FALSE */ + +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprThreadedDPCQueue()\n"); + +#endif /// DBG + + NT_ASSERT(pDPCFn); + NT_ASSERT(pClassifyData); + NT_ASSERT(pRedirectData); + + NTSTATUS status = STATUS_SUCCESS; + DPC_DATA* pDPCData = 0; + +#pragma warning(push) +#pragma warning(disable: 6014) /// pDPCData will be freed by caller + + status = KrnlHlprDPCDataCreate(&pDPCData, + pClassifyData, + pRedirectData, + pContext); + HLPR_BAIL_ON_FAILURE(status); + +#pragma warning(pop) + + KeInitializeThreadedDpc(&(pDPCData->kdpc), + pDPCFn, + 0); + + if(forceProcessor) + KeSetTargetProcessorDpc(&(pDPCData->kdpc), + (CCHAR)processorNumber); + + KeInsertQueueDpc(&(pDPCData->kdpc), + pDPCData, + 0); + + HLPR_BAIL_LABEL: + +#pragma warning(push) +#pragma warning(disable: 6001) /// pDPCData initialized with call to KrnlHlprDPCDataCreate + + if(status != STATUS_SUCCESS && + pDPCData) + KrnlHlprDPCDataDestroy(&pDPCData); + +#pragma warning(pop) + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprThreadedDPCQueue() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @kernel_helper_function="KrnlHlprThreadedDPCQueue" + + Purpose: Queue a threaded DPC for later execution at either PASSIVE_LEVEL or DISPATCH_LEVEL.
+
+ Notes: NOTIFY_DATA specific.
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552166.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552185.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprThreadedDPCQueue(_In_ KDEFERRED_ROUTINE* pDPCFn, + _In_ NOTIFY_DATA* pNotifyData, + _In_opt_ VOID* pContext, /* 0 */ + _In_ UINT32 processorNumber, /* 0 */ + _In_ BOOLEAN forceProcessor) /* FALSE */ + +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprThreadedDPCQueue()\n"); + +#endif /// DBG + + NT_ASSERT(pDPCFn); + NT_ASSERT(pNotifyData); + + NTSTATUS status = STATUS_SUCCESS; + DPC_DATA* pDPCData = 0; + +#pragma warning(push) +#pragma warning(disable: 6014) /// pDPCData will be freed by caller + + status = KrnlHlprDPCDataCreate(&pDPCData, + pNotifyData, + pContext); + HLPR_BAIL_ON_FAILURE(status); + +#pragma warning(pop) + + KeInitializeThreadedDpc(&(pDPCData->kdpc), + pDPCFn, + 0); + + if(forceProcessor) + KeSetTargetProcessorDpc(&(pDPCData->kdpc), + (CCHAR)processorNumber); + + KeInsertQueueDpc(&(pDPCData->kdpc), + pDPCData, + 0); + + HLPR_BAIL_LABEL: + +#pragma warning(push) +#pragma warning(disable: 6001) /// pDPCData initialized with call to KrnlHlprDPCDataCreate + + if(status != STATUS_SUCCESS && + pDPCData) + KrnlHlprDPCDataDestroy(&pDPCData); + +#pragma warning(pop) + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprThreadedDPCQueue() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @kernel_helper_function="KrnlHlprThreadedDPCQueue" + + Purpose: Queue a threaded DPC for later execution at either PASSIVE_LEVEL or DISPATCH_LEVEL.
+
+ Notes: PEND_DATA specific.
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552166.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552185.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprThreadedDPCQueue(_In_ KDEFERRED_ROUTINE* pDPCFn, + _In_ PEND_DATA* pPendData, + _In_opt_ VOID* pContext, /* 0 */ + _In_ UINT32 processorNumber, /* 0 */ + _In_ BOOLEAN forceProcessor) /* FALSE */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprThreadedDPCQueue()\n"); + +#endif /// DBG + + NT_ASSERT(pDPCFn); + NT_ASSERT(pPendData); + + NTSTATUS status = STATUS_SUCCESS; + DPC_DATA* pDPCData = 0; + +#pragma warning(push) +#pragma warning(disable: 6014) /// pDPCData will be freed by caller + + status = KrnlHlprDPCDataCreate(&pDPCData, + pPendData, + pContext); + HLPR_BAIL_ON_FAILURE(status); + +#pragma warning(pop) + + KeInitializeThreadedDpc(&(pDPCData->kdpc), + pDPCFn, + 0); + + if(forceProcessor) + KeSetTargetProcessorDpc(&(pDPCData->kdpc), + (CCHAR)processorNumber); + + KeInsertQueueDpc(&(pDPCData->kdpc), + pDPCData, + 0); + + HLPR_BAIL_LABEL: + +#pragma warning(push) +#pragma warning(disable: 6001) /// pDPCData initialized with call to KrnlHlprDPCDataCreate + + if(status != STATUS_SUCCESS && + pDPCData) + KrnlHlprDPCDataDestroy(&pDPCData); + +#pragma warning(pop) + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprThreadedDPCQueue() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +#endif /// THREADED_DPC____ diff --git a/network/trans/WFPSampler/syslib/HelperFunctions_DeferredProcedureCalls.h b/network/trans/WFPSampler/syslib/HelperFunctions_DeferredProcedureCalls.h new file mode 100644 index 000000000..4de525e9e --- /dev/null +++ b/network/trans/WFPSampler/syslib/HelperFunctions_DeferredProcedureCalls.h @@ -0,0 +1,275 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_DeferredProcedureCalls.h +// +// Abstract: +// This module contains prototypes for kernel helper functions that assist with DPC and +// threaded DPC routines. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Add support for serializing asynchronous +// FWPM_LAYER_STREAM injections +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef HELPERFUNCTIONS_DEFERRED_PROCEDURE_CALLS_H +#define HELPERFUNCTIONS_DEFERRED_PROCEDURE_CALLS_H + +typedef struct DPC_DATA_ +{ + KDPC kdpc; + union + { + CLASSIFY_DATA* pClassifyData; + NOTIFY_DATA* pNotifyData; + }; + union + { + INJECTION_DATA* pInjectionData; + REDIRECT_DATA* pRedirectData; + PEND_DATA* pPendData; + }; + VOID* pContext; +}DPC_DATA, *PDPC_DATA; + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprDPCDataPurge(_Inout_ DPC_DATA* pDPCData); + +_At_(*ppDPCData, _Pre_ _Notnull_) +_At_(*ppDPCData, _Post_ _Null_ __drv_freesMem(Pool)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(*ppDPCData == 0) +inline VOID KrnlHlprDPCDataDestroy(_Inout_ DPC_DATA** ppDPCData); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprDPCDataPopulate(_Inout_ DPC_DATA* pDPCData, + _In_ CLASSIFY_DATA* pClassifyData, + _In_opt_ INJECTION_DATA* pInjectionData = 0, + _In_opt_ VOID* pContext = 0); +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprDPCDataPopulate(_Inout_ DPC_DATA* pDPCData, + _In_ CLASSIFY_DATA* pClassifyData, + _In_ PEND_DATA* pPendData, + _In_opt_ VOID* pContext = 0); +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprDPCDataPopulate(_Inout_ DPC_DATA* pDPCData, + _In_ CLASSIFY_DATA* pClassifyData, + _In_ REDIRECT_DATA* pRedirectData, + _In_opt_ VOID** pContext = 0); +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprDPCDataPopulate(_Inout_ DPC_DATA* pDPCData, + _In_ NOTIFY_DATA* pNotifyData, + _In_opt_ VOID* pContext = 0); +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprDPCDataPopulate(_Inout_ DPC_DATA* pDPCData, + _In_ PEND_DATA* pPendData, + _In_opt_ VOID* pContext = 0); + +_At_(*ppDPCData, _Pre_ _Null_) +_When_(return != STATUS_SUCCESS, _At_(*ppDPCData, _Post_ _Null_)) +_When_(return == STATUS_SUCCESS, _At_(*ppDPCData, _Post_ _Notnull_ __drv_allocatesMem(Pool))) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprDPCDataCreate(_Outptr_ DPC_DATA** ppDPCData, + _In_ CLASSIFY_DATA* pClassifyData, + _In_opt_ INJECTION_DATA* pInjectionData = 0, + _In_opt_ VOID* pContext = 0); +_At_(*ppDPCData, _Pre_ _Null_) +_When_(return != STATUS_SUCCESS, _At_(*ppDPCData, _Post_ _Null_)) +_When_(return == STATUS_SUCCESS, _At_(*ppDPCData, _Post_ _Notnull_ __drv_allocatesMem(Pool))) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprDPCDataCreate(_Outptr_ DPC_DATA** ppDPCData, + _In_ CLASSIFY_DATA* pClassifyData, + _In_ PEND_DATA* pPendData, + _In_opt_ VOID* pContext = 0); +_At_(*ppDPCData, _Pre_ _Null_) +_When_(return != STATUS_SUCCESS, _At_(*ppDPCData, _Post_ _Null_)) +_When_(return == STATUS_SUCCESS, _At_(*ppDPCData, _Post_ _Notnull_ __drv_allocatesMem(Pool))) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprDPCDataCreate(_Outptr_ DPC_DATA** ppDPCData, + _In_ CLASSIFY_DATA* pClassifyData, + _In_ REDIRECT_DATA* pRedirectData, + _In_opt_ VOID* pContext = 0); +_At_(*ppDPCData, _Pre_ _Null_) +_When_(return != STATUS_SUCCESS, _At_(*ppDPCData, _Post_ _Null_)) +_When_(return == STATUS_SUCCESS, _At_(*ppDPCData, _Post_ _Notnull_ __drv_allocatesMem(Pool))) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprDPCDataCreate(_Outptr_ DPC_DATA** ppDPCData, + _In_ NOTIFY_DATA* pNotifyData, + _In_opt_ VOID* pContext = 0); + +_At_(*ppDPCData, _Pre_ _Null_) +_When_(return != STATUS_SUCCESS, _At_(*ppDPCData, _Post_ _Null_)) +_When_(return == STATUS_SUCCESS, _At_(*ppDPCData, _Post_ _Notnull_ __drv_allocatesMem(Pool))) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprDPCDataCreate(_Outptr_ DPC_DATA** ppDPCData, + _In_ PEND_DATA* pPendData, + _In_opt_ VOID* pContext = 0); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprDPCQueue(_In_ KDEFERRED_ROUTINE* pDPCFn, + _In_ UINT32 processorNumber = 0, + _In_ BOOLEAN forceProcessor = FALSE); +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprDPCQueue(_In_ KDEFERRED_ROUTINE* pDPCFn, + _In_ CLASSIFY_DATA* pClassifyData, + _In_opt_ INJECTION_DATA* pInjectionData = 0, + _In_opt_ VOID* pContext = 0, + _In_ UINT32 processorNumber = 0, + _In_ BOOLEAN forceProcessor = FALSE); +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprDPCQueue(_In_ KDEFERRED_ROUTINE* pDPCFn, + _In_ CLASSIFY_DATA* pClassifyData, + _In_ PEND_DATA* pPendData, + _In_opt_ VOID* pContext = 0, + _In_ UINT32 processorNumber = 0, + _In_ BOOLEAN forceProcessor = FALSE); +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprDPCQueue(_In_ KDEFERRED_ROUTINE* pDPCFn, + _In_ CLASSIFY_DATA* pClassifyData, + _In_ REDIRECT_DATA* pRedirectData, + _In_opt_ VOID* pContext = 0, + _In_ UINT32 processorNumber = 0, + _In_ BOOLEAN forceProcessor = FALSE); +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprDPCQueue(_In_ KDEFERRED_ROUTINE* pDPCFn, + _In_ NOTIFY_DATA* pNotifyData, + _In_opt_ VOID* pContext = 0, + _In_ UINT32 processorNumber = 0, + _In_ BOOLEAN forceProcessor = FALSE); +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprDPCQueue(_In_ KDEFERRED_ROUTINE* pDPCFn, + _In_ PEND_DATA* pPendData, + _In_opt_ VOID* pContext = 0, + _In_ UINT32 processorNumber = 0, + _In_ BOOLEAN forceProcessor = FALSE); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprThreadedDPCQueue(_In_ KDEFERRED_ROUTINE* pDPCFn, + _In_ UINT32 processorNumber = 0, + _In_ BOOLEAN forceProcessor = FALSE); +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprThreadedDPCQueue(_In_ KDEFERRED_ROUTINE* pDPCFn, + _In_ CLASSIFY_DATA* pClassifyData, + _In_opt_ INJECTION_DATA* pInjectionData = 0, + _In_opt_ VOID* pContext = 0, + _In_ UINT32 processorNumber = 0, + _In_ BOOLEAN forceProcessor = FALSE); +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprThreadedDPCQueue(_In_ KDEFERRED_ROUTINE* pDPCFn, + _In_ CLASSIFY_DATA* pClassifyData, + _In_ PEND_DATA* pPendData, + _In_opt_ VOID* pContext = 0, + _In_ UINT32 processorNumber = 0, + _In_ BOOLEAN forceProcessor = FALSE); +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprThreadedDPCQueue(_In_ KDEFERRED_ROUTINE* pDPCFn, + _In_ CLASSIFY_DATA* pClassifyData, + _In_ REDIRECT_DATA* pRedirectData, + _In_opt_ VOID* pContext = 0, + _In_ UINT32 processorNumber = 0, + _In_ BOOLEAN forceProcessor = FALSE); +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprThreadedDPCQueue(_In_ KDEFERRED_ROUTINE* pDPCFn, + _In_ NOTIFY_DATA* pNotifyData, + _In_opt_ VOID* pContext = 0, + _In_ UINT32 processorNumber = 0, + _In_ BOOLEAN forceProcessor = FALSE); +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprThreadedDPCQueue(_In_ KDEFERRED_ROUTINE* pDPCFn, + _In_ PEND_DATA* pPendData, + _In_opt_ VOID* pContext = 0, + _In_ UINT32 processorNumber = 0, + _In_ BOOLEAN forceProcessor = FALSE); + +#endif /// HELPERFUNCTIONS_DEFERRED_PROCEDURE_CALLS_H diff --git a/network/trans/WFPSampler/syslib/HelperFunctions_FlowContext.cpp b/network/trans/WFPSampler/syslib/HelperFunctions_FlowContext.cpp new file mode 100644 index 000000000..b6d561d68 --- /dev/null +++ b/network/trans/WFPSampler/syslib/HelperFunctions_FlowContext.cpp @@ -0,0 +1,385 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_FlowContext.cpp +// +// Abstract: +// This module contains kernel helper functions that assist with FLOW_CONTEXT. +// +// Naming Convention: +// +// +// +// i.e. +// +// KrnlHlprFlowContextCreate +// +// +// KrnlHlpr - Function is located in syslib\ and applies to kernel mode. +// +// FlowContext - Function pertains to FLOW_CONTEXT objects. +// +// { +// Create - Function allocates and fills memory. +// Destroy - Function cleans up and frees memory. +// } +// +// +// Private Functions: +// +// Public Functions: +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// December 13, 2013 - 1.1 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "HelperFunctions_Include.h" /// . +#include "HelperFunctions_FlowContext.tmh" /// $(OBJ_PATH)\$(O)\ + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(return == STATUS_SUCCESS) +inline NTSTATUS KrnlHlprFlowContextPurge(_Inout_ FLOW_CONTEXT* pFlowContext) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFlowContextPurge()\n"); + +#endif /// DBG + + NT_ASSERT(pFlowContext); + + NTSTATUS status = STATUS_SUCCESS; + + status = FwpsFlowRemoveContext(pFlowContext->flowID, + pFlowContext->layerID, + pFlowContext->calloutID); + if(status != STATUS_SUCCESS) + { + if(status != STATUS_UNSUCCESSFUL) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! KrnlHlprFlowContextPurge : FwpsFlowRemoveContext() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + /// no context currently associated with the flow, so it should be safe to delete + } + + HLPR_DELETE(pFlowContext->pContext, + WFPSAMPLER_SYSLIB_TAG); + + RtlZeroMemory(pFlowContext, + sizeof(FLOW_CONTEXT)); + + HLPR_BAIL_LABEL: + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFlowContextPurge()\n"); + +#endif /// DBG + + return status; +} + +_At_(*ppFlowContext, _Pre_ _Notnull_) +_At_(*ppFlowContext, _Post_ _Null_ __drv_freesMem(Pool)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(return == STATUS_SUCCESS && *ppFlowContext == 0) +inline NTSTATUS KrnlHlprFlowContextDestroy(_Inout_ FLOW_CONTEXT** ppFlowContext) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFlowContextDestroy()\n"); + +#endif /// DBG + + NT_ASSERT(ppFlowContext); + + NTSTATUS status = STATUS_SUCCESS; + + if(*ppFlowContext) + { + status = KrnlHlprFlowContextPurge(*ppFlowContext); + HLPR_BAIL_ON_FAILURE(status); + + HLPR_DELETE(*ppFlowContext, + WFPSAMPLER_SYSLIB_TAG); + } + + HLPR_BAIL_LABEL: + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFlowContextDestroy()\n"); + +#endif /// DBG + + return status; +} + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprFlowContextPopulate(_Inout_ FLOW_CONTEXT* pFlowContext, + _In_ UINT64 flowHandle, + _In_ UINT16 layerID, + _In_ UINT32 calloutID, + _In_ UINT8 contextType, /* CONTEXT_TYPE_DEFAULT */ + _In_opt_ const VOID* pProviderContext) /* 0 */ + +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFlowContextPopulate()\n"); + +#endif /// DBG + + NT_ASSERT(pFlowContext); + + NTSTATUS status = STATUS_SUCCESS; + + pFlowContext->flowID = flowHandle; + pFlowContext->layerID = layerID; + pFlowContext->calloutID = calloutID; + pFlowContext->contextType = contextType; + + if(pFlowContext->contextType == CONTEXT_TYPE_DEFAULT || + pFlowContext->contextType >= CONTEXT_TYPE_MAX) + { + if(layerID == FWPS_LAYER_STREAM_V4 || + layerID == FWPS_LAYER_STREAM_V6) + pFlowContext->contextType = CONTEXT_TYPE_STREAM; + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + else if(layerID == FWPS_LAYER_ALE_ENDPOINT_CLOSURE_V4 || + layerID == FWPS_LAYER_ALE_ENDPOINT_CLOSURE_V6) + pFlowContext->contextType = CONTEXT_TYPE_ALE_ENDPOINT_CLOSURE; + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + else + { + status = STATUS_INVALID_PARAMETER; + + HLPR_BAIL; + } + } + + switch(pFlowContext->contextType) + { + case CONTEXT_TYPE_STREAM: + { + HLPR_NEW(pFlowContext->pStreamContext, + STREAM_CONTEXT, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pFlowContext->pStreamContext, + status); + + KeInitializeSpinLock(&(pFlowContext->pStreamContext->serializationList.spinLock)); + + status = WdfWaitLockCreate(WDF_NO_OBJECT_ATTRIBUTES, + &(pFlowContext->pStreamContext->serializationList.waitLock)); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! KrnlHlprFlowContextCreate : WdfWaitLockCreate() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + InitializeListHead(&(pFlowContext->pStreamContext->serializationList.listHead)); + + pFlowContext->pStreamContext->processorID = KeGetCurrentProcessorNumber(); + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + if(pProviderContext) + { + PC_FLOW_ASSOCIATION_DATA* pFlowAssociationData = (PC_FLOW_ASSOCIATION_DATA*)pProviderContext; + + for(UINT32 aecIndex = 0; + aecIndex < pFlowAssociationData->itemCount; + aecIndex++) + { + if(pFlowAssociationData->pLayerIDs[aecIndex] == FWPS_LAYER_ALE_ENDPOINT_CLOSURE_V4 || + pFlowAssociationData->pLayerIDs[aecIndex] == FWPS_LAYER_ALE_ENDPOINT_CLOSURE_V6) + { + pFlowContext->aecLayerID = pFlowAssociationData->pLayerIDs[aecIndex]; + pFlowContext->aecCalloutID = pFlowAssociationData->pCalloutIDs[aecIndex]; + + break; + } + } + } + +#else + + UNREFERENCED_PARAMETER(pProviderContext); + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + break; + } + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + case CONTEXT_TYPE_ALE_ENDPOINT_CLOSURE: + { + HLPR_NEW(pFlowContext->pALEEndpointClosureContext, + ALE_ENDPOINT_CLOSURE_CONTEXT, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pFlowContext->pALEEndpointClosureContext, + status); + + KeInitializeSpinLock(&(pFlowContext->pALEEndpointClosureContext->spinLock)); + + if(pProviderContext) + { + PC_FLOW_ASSOCIATION_DATA* pFlowAssociationData = (PC_FLOW_ASSOCIATION_DATA*)pProviderContext; + + for(UINT32 injectionIndex = 0; + injectionIndex < pFlowAssociationData->itemCount; + injectionIndex++) + { + if(pFlowAssociationData->pLayerIDs[injectionIndex] == FWPS_LAYER_STREAM_V4 || + pFlowAssociationData->pLayerIDs[injectionIndex] == FWPS_LAYER_STREAM_V6) + { + pFlowContext->injectionLayerID = pFlowAssociationData->pLayerIDs[injectionIndex]; + pFlowContext->injectionCalloutID = pFlowAssociationData->pCalloutIDs[injectionIndex]; + + break; + } + } + } + + break; + } + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + } + + status = FwpsFlowAssociateContext(pFlowContext->flowID, + pFlowContext->layerID, + pFlowContext->calloutID, + (UINT64)pFlowContext); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! KrnlHlprFlowContextCreate : FwpsFlowAssociateContext() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS) + KrnlHlprFlowContextPurge(pFlowContext); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFlowContextPopulate() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +_At_(*ppFlowContext, _Pre_ _Null_) +_When_(return != STATUS_SUCCESS, _At_(*ppFlowContext, _Post_ _Null_)) +_When_(return == STATUS_SUCCESS, _At_(*ppFlowContext, _Post_ _Notnull_ __drv_allocatesMem(Pool))) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprFlowContextCreate(_Outptr_ FLOW_CONTEXT** ppFlowContext, + _In_ UINT64 flowHandle, + _In_ UINT16 layerID, + _In_ UINT32 calloutID, + _In_ UINT8 contextType, /* CONTEXT_TYPE_DEFAULT */ + _In_opt_ const VOID* pProviderContext) /* 0 */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFlowContextCreate()\n"); + +#endif /// DBG + + NT_ASSERT(ppFlowContext); + + NTSTATUS status = STATUS_SUCCESS; + + HLPR_NEW(*ppFlowContext, + FLOW_CONTEXT, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(*ppFlowContext, + status); + + status = KrnlHlprFlowContextPopulate(*ppFlowContext, + flowHandle, + layerID, + calloutID, + contextType, + pProviderContext); + + HLPR_BAIL_LABEL: + +#pragma warning(push) +#pragma warning(disable: 6001) /// *ppFlowContext initialized with call to HLPR_NEW & KrnlHlprFlowContextPopulate + + if(status != STATUS_SUCCESS && + *ppFlowContext) + KrnlHlprFlowContextDestroy(ppFlowContext); + +#pragma warning(pop) + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFlowContextCreate() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} diff --git a/network/trans/WFPSampler/syslib/HelperFunctions_FlowContext.h b/network/trans/WFPSampler/syslib/HelperFunctions_FlowContext.h new file mode 100644 index 000000000..72689de52 --- /dev/null +++ b/network/trans/WFPSampler/syslib/HelperFunctions_FlowContext.h @@ -0,0 +1,145 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_FlowContext.h +// +// Abstract: +// This module contains prototypes for kernel helper functions that assist with FLOW_CONTEXT. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// December 13, 2013 - 1.1 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef HELPERFUNCTIONS_FLOW_CONTEXT_H +#define HELPERFUNCTIONS_FLOW_CONTEXT_H + +typedef struct SERIALIZATION_LIST_ +{ + KSPIN_LOCK spinLock; + WDFWAITLOCK waitLock; + LIST_ENTRY listHead; + INT64 numEntries; +}SERIALIZATION_LIST, *PSERIALIZATION_LIST; + +typedef enum CONTEXT_TYPE_ +{ + CONTEXT_TYPE_DEFAULT = 0, + CONTEXT_TYPE_STREAM, + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + CONTEXT_TYPE_ALE_ENDPOINT_CLOSURE, + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + CONTEXT_TYPE_MAX +}CONTEXT_TYPE; + +typedef struct STREAM_CONTEXT_ +{ + UINT64 filterID; + UINT32 processorID; + BYTE pReserved[1]; + SERIALIZATION_LIST serializationList; +}STREAM_CONTEXT, *PSTREAM_CONTEXT; + +#if(NTDDI_VERSION >= NTDDI_WIN7) + +typedef struct ALE_ENDPOINT_CLOSURE_CONTEXT_ +{ + UINT64 filterID; + KSPIN_LOCK spinLock; + BYTE pReserved[3]; + VOID* pPendData; + +}ALE_ENDPOINT_CLOSURE_CONTEXT, *PALE_ENDPOINT_CLOSURE_CONTEXT; + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + +typedef struct FLOW_CONTEXT_ +{ + UINT64 flowID; + UINT32 calloutID; + UINT16 layerID; + UINT16 contextType; + union + { + VOID* pContext; + STREAM_CONTEXT* pStreamContext; + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + ALE_ENDPOINT_CLOSURE_CONTEXT* pALEEndpointClosureContext; + + + }; + union + { + UINT32 aecCalloutID; + UINT32 injectionCalloutID; + }; + union + { + UINT16 aecLayerID; + UINT16 injectionLayerID; + }; + BYTE pReserved[2]; + +#else + + }; + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + +}FLOW_CONTEXT, *PFLOW_CONTEXT; + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(return == STATUS_SUCCESS) +inline NTSTATUS KrnlHlprFlowContextPurge(_Inout_ FLOW_CONTEXT* pFlowContext); + +_At_(*ppFlowContext, _Pre_ _Notnull_) +_At_(*ppFlowContext, _Post_ _Null_ __drv_freesMem(Pool)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(return == STATUS_SUCCESS && *ppFlowContext == 0) +inline NTSTATUS KrnlHlprFlowContextDestroy(_Inout_ FLOW_CONTEXT** ppFlowContext); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprFlowContextPopulate(_Inout_ FLOW_CONTEXT* pFlowContext, + _In_ UINT64 flowHandle, + _In_ UINT16 layerID, + _In_ UINT32 calloutID, + _In_ UINT8 contextType = CONTEXT_TYPE_DEFAULT, + _In_opt_ const VOID* pProviderContext = 0); + +_At_(*ppFlowContext, _Pre_ _Null_) +_When_(return != STATUS_SUCCESS, _At_(*ppFlowContext, _Post_ _Null_)) +_When_(return == STATUS_SUCCESS, _At_(*ppFlowContext, _Post_ _Notnull_ __drv_allocatesMem(Pool))) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprFlowContextCreate(_Outptr_ FLOW_CONTEXT** ppFlowContext, + _In_ UINT64 flowHandle, + _In_ UINT16 layerID, + _In_ UINT32 calloutID, + _In_ UINT8 contextType = CONTEXT_TYPE_DEFAULT, + _In_opt_ const VOID* pProviderContext = 0); + +#endif /// HELPERFUNCTIONS_FLOW_CONTEXT_H diff --git a/network/trans/WFPSampler/syslib/HelperFunctions_FwpObjects.cpp b/network/trans/WFPSampler/syslib/HelperFunctions_FwpObjects.cpp new file mode 100644 index 000000000..cc9a0646b --- /dev/null +++ b/network/trans/WFPSampler/syslib/HelperFunctions_FwpObjects.cpp @@ -0,0 +1,9068 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions\FwpObjects.c +// +// Abstract: +// This module contains kernel helper functions that allocate, populate, purge, and free WFP +// structures in memory. +// +// Naming Convention: +// +// +// +// i.e. +// +// KrnlHlprFwpValueCreateLocalCopy +// +// +// KrnlHlpr - Function is located in syslib\ and applies to kernel +// mode. +// +// { +// FwpByteBlob - Function pertains to FWP_BYTE_BLOB objects. +// FwpConditionValue - Function pertains to FWP_CONDITION_VALUE objects. +// FwpmClassifyOption - Function pertains to FWPM_CLASSIFY_OPTION objects. +// FwpmClassifyOptions - Function pertains to FWPM_CLASSIFY_OPTIONS objects. +// FwpmDisplayData - Function pertains to FWPM_DISPLAY_DATA objects. +// FwpmProviderContext - Function pertains to FWPM_PROVIDER_CONTEXT objects. +// FwpRange - Function pertains to FWP_RANGE objects. +// FwpsClassifyOut - Function pertains to FWPS_CLASSIFY_OUT objects. +// FwpsFilter - Function pertains to FWPS_FILTER objects. +// FwpsFilterCondition - Function pertains to FWPS_FILTER_CONDITION objects. +// FwpsIncomingMetadataValues - Function pertains to FWPS_INCOMING_METADATA +// objects. +// FwpsIncomingValues - Function pertains to FWPS_INCOMING_VALUES objects. +// FwpsStreamCalloutIOPacket - Function pertains to FWPS_STREAM_DATA objects. +// FwpsStreamData - Function pertains to FWPS_STREAM_DATA objects. +// FwpTokenInformation - Function pertains to FWP_TOKEN_INFORMATION objects. +// FwpV4AddrAndMask - Function pertains to FWP_V4_ADDR_AND_MASK objects. +// FwpV6AddrAndMask - Function pertains to FWP_V6_ADDR_AND_MASK objects. +// FwpValue - Function pertains to FWP_VALUE objects. +// IPsecDoSPOptions - Function pertains to IPSEC_DOSP_OPTIONS objects. +// } +// +// { +// Get - Function returns a pointer or value +// Create - Function allocates and fills memory +// Populate - Function fills memory with values +// Acquire - Function will take a reference on an object +// Release - Function releases the reference on the object +// Purge - Function cleans up values +// Destroy - Function cleans up and frees memory +// } +// +// LocalCopy - Function acts on the LocalCopy (as opposed to the original) +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Add support for multiple injectors, add +// KrnlHlprFwpValueGetStringForFwpsIncomingValue, +// KrnlHlprFwpsRedirectHandleCreate, +// KrnlHlprFwpsRedirecthandleDestroy, +// KrnlHlprFwpsLayerIsDiscard, +// KrnlHlprFwpsLayerIDToString functions, add missing +// conditions, and fix invalid conditions. +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "HelperFunctions_Include.h" /// . +#include "HelperFunctions_FwpObjects.tmh" /// $(OBJ_PATH)\$(O)\ + +HANDLE g_EngineHandle = 0; +HANDLE g_pIPv4InboundMACInjectionHandles[2] = {0}; +HANDLE g_pIPv4IngressVSwitchEthernetInjectionHandles[2] = {0}; +HANDLE g_pIPv4InboundForwardInjectionHandles[2] = {0}; +HANDLE g_pIPv4InboundNetworkInjectionHandles[2] = {0}; +HANDLE g_pIPv4InboundTransportInjectionHandles[2] = {0}; +HANDLE g_pIPv4InboundStreamInjectionHandles[2] = {0}; +HANDLE g_pIPv4OutboundMACInjectionHandles[2] = {0}; +HANDLE g_pIPv4EgressVSwitchEthernetInjectionHandles[2] = {0}; +HANDLE g_pIPv4OutboundForwardInjectionHandles[2] = {0}; +HANDLE g_pIPv4OutboundNetworkInjectionHandles[2] = {0}; +HANDLE g_pIPv4OutboundTransportInjectionHandles[2] = {0}; +HANDLE g_pIPv4OutboundStreamInjectionHandles[2] = {0}; +HANDLE g_pIPv6InboundMACInjectionHandles[2] = {0}; +HANDLE g_pIPv6IngressVSwitchEthernetInjectionHandles[2] = {0}; +HANDLE g_pIPv6InboundForwardInjectionHandles[2] = {0}; +HANDLE g_pIPv6InboundNetworkInjectionHandles[2] = {0}; +HANDLE g_pIPv6InboundTransportInjectionHandles[2] = {0}; +HANDLE g_pIPv6InboundStreamInjectionHandles[2] = {0}; +HANDLE g_pIPv6OutboundForwardInjectionHandles[2] = {0}; +HANDLE g_pIPv6OutboundNetworkInjectionHandles[2] = {0}; +HANDLE g_pIPv6OutboundTransportInjectionHandles[2] = {0}; +HANDLE g_pIPv6OutboundStreamInjectionHandles[2] = {0}; +HANDLE g_pIPv6OutboundMACInjectionHandles[2] = {0}; +HANDLE g_pIPv6EgressVSwitchEthernetInjectionHandles[2] = {0}; +HANDLE g_pInboundForwardInjectionHandles[2] = {0}; +HANDLE g_pInboundNetworkInjectionHandles[2] = {0}; +HANDLE g_pInboundTransportInjectionHandles[2] = {0}; +HANDLE g_pInboundStreamInjectionHandles[2] = {0}; +HANDLE g_pInboundMACInjectionHandles[2] = {0}; +HANDLE g_pIngressVSwitchEthernetInjectionHandles[2] = {0}; +HANDLE g_pOutboundForwardInjectionHandles[2] = {0}; +HANDLE g_pOutboundNetworkInjectionHandles[2] = {0}; +HANDLE g_pOutboundTransportInjectionHandles[2] = {0}; +HANDLE g_pOutboundStreamInjectionHandles[2] = {0}; +HANDLE g_pOutboundMACInjectionHandles[2] = {0}; +HANDLE g_pEgressVSwitchEthernetInjectionHandles[2] = {0}; +HANDLE g_pRedirectionHandles[2] = {0}; + +#ifndef FWP_BYTE_BLOB____ +#define FWP_BYTE_BLOB____ + +#pragma warning(disable: 24002) /// Code is IPv4 & IPv6 specific + +/** + @kernel_helper_function="KrnlHlprFwpByteBlobPurgeLocalCopy" + + Purpose: Cleanup a local copy of an FWP_BYTE_BLOB.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552427.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprFwpByteBlobPurgeLocalCopy(_Inout_ FWP_BYTE_BLOB* pBlob) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpByteBlobPurgeLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(pBlob); + + HLPR_DELETE_ARRAY(pBlob->data, + WFPSAMPLER_SYSLIB_TAG); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpByteBlobPurgeLocalCopy()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprFwpByteBlobDestroyLocalCopy" + + Purpose: Cleanup and free a local copy of an FWP_BYTE_BLOB.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552427.aspx
+*/ +_At_(*ppBlob, _Pre_ _Notnull_) +_At_(*ppBlob, _Post_ _Null_ __drv_freesMem(Pool)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(*ppBlob == 0) +inline VOID KrnlHlprFwpByteBlobDestroyLocalCopy(_Inout_ FWP_BYTE_BLOB** ppBlob) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpByteBlobDestroyLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(ppBlob); + + if(*ppBlob) + KrnlHlprFwpByteBlobPurgeLocalCopy(*ppBlob); + + HLPR_DELETE(*ppBlob, + WFPSAMPLER_SYSLIB_TAG); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpByteBlobDestroyLocalCopy()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprFwpByteBlobPopulateLocalCopy" + + Purpose: Populate a local copy of an FWP_BYTE_BLOB.
+
+ Notes: The caller is responsible for freeing any allocated memory using + KrnlHlprFwpByteBlobPurgeLocalCopy().
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552427.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprFwpByteBlobPopulateLocalCopy(_In_ const FWP_BYTE_BLOB* pOriginalBlob, + _Inout_ FWP_BYTE_BLOB* pBlob) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpByteBlobPopulateLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(pOriginalBlob); + NT_ASSERT(pBlob); + + NTSTATUS status = STATUS_SUCCESS; + + pBlob->size = pOriginalBlob->size; + + if(pBlob->size) + { + HLPR_NEW_ARRAY(pBlob->data, + BYTE, + pBlob->size, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pBlob->data, + status); + + RtlCopyMemory(pBlob->data, + pOriginalBlob->data, + pBlob->size); + } + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS) + KrnlHlprFwpByteBlobPurgeLocalCopy(pBlob); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpByteBlobPopulateLocalCopy() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @kernel_helper_function="KrnlHlprFwpByteBlobCreateLocalCopy" + + Purpose: Allocate and populate a local copy of an FWP_BYTE_BLOB.
+
+ Notes: The caller is responsible for freeing the allocated memory using + KrnlHlprFwpByteBlobDestroyLocalCopy().
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552427.aspx
+*/ +__drv_allocatesMem(Pool) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Must_inspect_result_ +_Success_(return != 0) +FWP_BYTE_BLOB* KrnlHlprFwpByteBlobCreateLocalCopy(_In_ const FWP_BYTE_BLOB* pOriginalBlob) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpByteBlobCreateLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(pOriginalBlob); + + NTSTATUS status = STATUS_SUCCESS; + FWP_BYTE_BLOB* pByteBlob = 0; + +#pragma warning(push) +#pragma warning(disable: 6014) /// pByteBlob is expected to be cleaned up by caller using KrnlHlprFwpByteBlobDestroyLocalCopy + + HLPR_NEW(pByteBlob, + FWP_BYTE_BLOB, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pByteBlob, + status); + +#pragma warning(pop) + + status = KrnlHlprFwpByteBlobPopulateLocalCopy(pOriginalBlob, + pByteBlob); + HLPR_BAIL_ON_FAILURE(status); + + HLPR_BAIL_LABEL: + +#pragma warning(push) +#pragma warning(disable: 6001) /// pByteBlob initialized with call to HLPR_NEW & KrnlHlprFwpByteBlobPopulateLocalCopy + + if(status != STATUS_SUCCESS && + pByteBlob) + KrnlHlprFwpByteBlobDestroyLocalCopy(&pByteBlob); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpByteBlobCreateLocalCopy() [pByteBlob: %#p]\n", + pByteBlob); + +#endif /// DBG + + return pByteBlob; + +#pragma warning(pop) +} + +#endif /// FWP_BYTE_BLOB____ + +#ifndef FWPM_DISPLAY_DATA____ +#define FWPM_DISPLAY_DATA____ + +/** + @kernel_helper_function="KrnlHlprFwpmDisplayDataPurgeLocalCopy" + + Purpose: Cleanup a local copy of an FWPM_DISPLAY_DATA.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA364261.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprFwpmDisplayDataPurgeLocalCopy(_Inout_ FWPM_DISPLAY_DATA* pData) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpmDisplayDataPurgeLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(pData); + + HLPR_DELETE_ARRAY(pData->name, + WFPSAMPLER_SYSLIB_TAG); + + HLPR_DELETE_ARRAY(pData->description, + WFPSAMPLER_SYSLIB_TAG); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpmDisplayDataPurgeLocalCopy()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprFwpmDisplayDataDestroyLocalCopy" + + Purpose: Cleanup and free a local copy of an FWPM_DISPLAY_DATA.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA364261.aspx
+*/ +_At_(*ppDisplayData, _Pre_ _Notnull_) +_At_(*ppDisplayData, _Post_ _Null_ __drv_freesMem(Pool)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(*ppDisplayData == 0) +inline VOID KrnlHlprFwpmDisplayDataDestroyLocalCopy(_Inout_ FWPM_DISPLAY_DATA** ppDisplayData) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpmDisplayDataDestroyLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(ppDisplayData); + + if(*ppDisplayData) + KrnlHlprFwpmDisplayDataPurgeLocalCopy(*ppDisplayData); + + HLPR_DELETE(*ppDisplayData, + WFPSAMPLER_SYSLIB_TAG); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpmDisplayDataDestroyLocalCopy()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprFwpmDisplayDataPopulateLocalCopy" + + Purpose: Populate a local copy of an FWPM_DISPLAY_DATA.
+
+ Notes: The caller is responsible for freeing any allocated memory using + KrnlHlprFwpmDisplayDataPurgeLocalCopy().
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA364261.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprFwpmDisplayDataPopulateLocalCopy(_In_ const FWPM_DISPLAY_DATA* pOriginalData, + _Inout_ FWPM_DISPLAY_DATA* pData) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpmDisplayDataPopulateLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(pOriginalData); + NT_ASSERT(pData); + + NTSTATUS status = STATUS_SUCCESS; + KIRQL irql = KeGetCurrentIrql(); + size_t nameSize = 0; + size_t descriptionSize = 0; + + if(irql == PASSIVE_LEVEL) + { + status = RtlStringCchLengthW(pOriginalData->name, + NTSTRSAFE_MAX_CCH, + &nameSize); + HLPR_BAIL_ON_FAILURE(status); + + if(pOriginalData->description) + { + status = RtlStringCchLengthW(pOriginalData->description, + NTSTRSAFE_MAX_CCH, + &descriptionSize); + HLPR_BAIL_ON_FAILURE(status); + } + } + else + { + nameSize = wcslen(pOriginalData->name); + + if(pOriginalData->description) + descriptionSize = wcslen(pOriginalData->description); + } + + /// name is required for the FWPM_DISPLAY_DATA to be valid + if(nameSize) + { + HLPR_NEW_ARRAY(pData->name, + WCHAR, + nameSize + 1, /// add space for '\0' + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pData->name, + status); + + RtlCopyMemory(pData->name, + pOriginalData->name, + nameSize); + + pData->name[nameSize] = L'\0'; + } + else + { + status = STATUS_INVALID_BUFFER_SIZE; + + HLPR_BAIL; + } + + /// description is not required for the FWPM_DISPLAY_DATA to be valid + if(descriptionSize) + { + HLPR_NEW_ARRAY(pData->description, + WCHAR, + descriptionSize + 1, /// add space for '\0' + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pData->description, + status); + + RtlCopyMemory(pData->description, + pOriginalData->description, + nameSize); + + pData->description[descriptionSize] = L'\0'; + } + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS) + { + HLPR_DELETE_ARRAY(pData->name, + WFPSAMPLER_SYSLIB_TAG); + + HLPR_DELETE_ARRAY(pData->description, + WFPSAMPLER_SYSLIB_TAG); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpmDisplayDataPopulateLocalCopy() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @kernel_helper_function="KrnlHlprFwpmDisplayDataCreateLocalCopy" + + Purpose: ALlocate and populate a local copy of an FWPM_DISPLAY_DATA.
+
+ Notes: The caller is responsible for freeing any allocated memory using + KrnlHlprFwpmDisplayDataDestroyLocalCopy().
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA364261.aspx
+*/ +__drv_allocatesMem(Pool) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Must_inspect_result_ +_Success_(return != 0) +FWPM_DISPLAY_DATA* KrnlHlprFwpmDisplayDataCreateLocalCopy(_In_ const FWPM_DISPLAY_DATA* pOriginalDisplayData) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpmDisplayDataCreateLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(pOriginalDisplayData); + + NTSTATUS status = STATUS_SUCCESS; + FWPM_DISPLAY_DATA* pDisplayData = 0; + +#pragma warning(push) +#pragma warning(disable: 6014) /// pDisplayData is expected to be cleaned up by caller using KrnlHlprFwpmDisplayDataDestroyLocalCopy + + HLPR_NEW(pDisplayData, + FWPM_DISPLAY_DATA, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pDisplayData, + status); + +#pragma warning(pop) + + status = KrnlHlprFwpmDisplayDataPopulateLocalCopy(pOriginalDisplayData, + pDisplayData); + + HLPR_BAIL_LABEL: + +#pragma warning(push) +#pragma warning(disable: 6001) /// pDisplayData initialized with call to HLPR_NEW & KrnlHlprFwpmDisplayDataPopulateLocalCopy + + if(status != STATUS_SUCCESS && + pDisplayData) + KrnlHlprFwpmDisplayDataDestroyLocalCopy(&pDisplayData); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpmDisplayDataCreateLocalCopy() [pDisplayData: %#p]\n", + pDisplayData); + +#endif /// DBG + + return pDisplayData; + +#pragma warning(pop) +} + +#endif /// FWP_DISPLAY_DATA____ + +#ifndef FWPM_CLASSIFY_OPTION____ +#define FWPM_CLASSIFY_OPTION____ + +/** + @kernel_helper_function="KrnlHlprFwpmClassifyOptionPurgeLocalCopy" + + Purpose: Cleanup a local copy of an FWPM_CLASSIFY_OPTION.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA364259.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprFwpmClassifyOptionPurgeLocalCopy(_Inout_ FWPM_CLASSIFY_OPTION* pOption) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpmClassifyOptionPurgeLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(pOption); + + KrnlHlprFwpValuePurgeLocalCopy(&(pOption->value)); + + pOption->type = FWP_CLASSIFY_OPTION_MAX; + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpmClassifyOptionPurgeLocalCopy()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprFwpmClassifyOptionDestroyLocalCopy" + + Purpose: Cleanup and free a local copy of an FWPM_CLASSIFY_OPTION.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA364259.aspx
+*/ +_At_(*ppOption, _Pre_ _Notnull_) +_At_(*ppOption, _Post_ _Null_ __drv_freesMem(Pool)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(*ppOption == 0) +inline VOID KrnlHlprFwpmClassifyOptionDestroyLocalCopy(_Inout_ FWPM_CLASSIFY_OPTION** ppOption) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpmClassifyOptionDestroyLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(ppOption); + + if(*ppOption) + KrnlHlprFwpmClassifyOptionPurgeLocalCopy(*ppOption); + + HLPR_DELETE(*ppOption, + WFPSAMPLER_SYSLIB_TAG); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpmClassifyOptionDestroyLocalCopy()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprFwpmClassifyOptionPopulateLocalCopy" + + Purpose: Populate a local copy of an FWPM_CLASSIFY_OPTION.
+
+ Notes: The caller is responsible for freeing any allocated memory using + KrnlHlprFwpmClassifyOptionPurgeLocalCopy().
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA364259.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprFwpmClassifyOptionPopulateLocalCopy(_In_ const FWPM_CLASSIFY_OPTION* pOriginalOption, + _Inout_ FWPM_CLASSIFY_OPTION* pOption) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpmClassifyOptionPopulateLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(pOriginalOption); + NT_ASSERT(pOption); + + NTSTATUS status = STATUS_SUCCESS; + + status = KrnlHlprFwpValuePopulateLocalCopy(&(pOriginalOption->value), + &(pOption->value)); + HLPR_BAIL_ON_FAILURE(status); + + pOption->type = pOriginalOption->type; + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS) + { + KrnlHlprFwpValuePurgeLocalCopy(&(pOption->value)); + + pOption->type = FWP_CLASSIFY_OPTION_MAX; + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpmClassifyOptionPopulateLocalCopy() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @kernel_helper_function="KrnlHlprFwpmClassifyOptionCreateLocalCopy" + + Purpose: Allocate and populate a local copy of an FWPM_CLASSIFY_OPTION.
+
+ Notes: The caller is responsible for freeing any allocated memory using + KrnlHlprFwpmClassifyOptionDestroyLocalCopy().
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA364259.aspx
+*/ +__drv_allocatesMem(Pool) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Must_inspect_result_ +_Success_(return != 0) +FWPM_CLASSIFY_OPTION* KrnlHlprFwpmClassifyOptionCreateLocalCopy(_In_ const FWPM_CLASSIFY_OPTION* pOriginalOption) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpmClassifyOptionCreateLocalCopy()\n"); + +#endif /// DBG + + FWPM_CLASSIFY_OPTION* pOption = 0; + + if(pOriginalOption) + { + NTSTATUS status = STATUS_SUCCESS; + +#pragma warning(push) +#pragma warning(disable: 6014) /// pOption is expected to be cleaned up by caller using KrnlHlprFwpmClassifyOptionDestroyLocalCopy + + HLPR_NEW(pOption, + FWPM_CLASSIFY_OPTION, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pOption, + status); + +#pragma warning(pop) + + status = KrnlHlprFwpmClassifyOptionPopulateLocalCopy(pOriginalOption, + pOption); + + HLPR_BAIL_LABEL: + +#pragma warning(push) +#pragma warning(disable: 6001) /// pOption initialized with call to HLPR_NEW & KrnlHlprFwpmClassifyOptionPopulateLocalCopy + + if(status != STATUS_SUCCESS) + KrnlHlprFwpmClassifyOptionDestroyLocalCopy(&pOption); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpmClassifyOptionCreateLocalCopy() [pOption: %#p]\n", + pOption); + +#endif /// DBG + + return pOption; + +#pragma warning(pop) +} + +#endif /// FWPM_CLASSIFY_OPTION____ + +#ifndef FWPM_CLASSIFY_OPTIONS____ +#define FWPM_CLASSIFY_OPTIONS____ + +/** + @kernel_helper_function="KrnlHlprFwpmClassifyOptionsPurgeLocalCopy" + + Purpose: Cleanup a local copy of an FWPM_CLASSIFY_OPTIONS.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA364260.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprFwpmClassifyOptionsPurgeLocalCopy(_Inout_ FWPM_CLASSIFY_OPTIONS* pOptions) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpmClassifyOptionsPurgeLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(pOptions); + + for(UINT32 optionIndex = 0; + optionIndex < pOptions->numOptions; + optionIndex++) + { + KrnlHlprFwpmClassifyOptionPurgeLocalCopy(&(pOptions->options[optionIndex])); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpmClassifyOptionsPurgeLocalCopy()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprFwpmClassifyOptionsDestroyLocalCopy" + + Purpose: Cleanup and free a local copy of an FWPM_CLASSIFY_OPTIONS.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA364260.aspx
+*/ +_At_(*ppOptions, _Pre_ _Notnull_) +_At_(*ppOptions, _Post_ _Null_ __drv_freesMem(Pool)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(*ppOptions == 0) +inline VOID KrnlHlprFwpmClassifyOptionsDestroyLocalCopy(_Inout_ FWPM_CLASSIFY_OPTIONS** ppOptions) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpmClassifyOptionsDestroyLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(ppOptions); + + if(*ppOptions) + KrnlHlprFwpmClassifyOptionsPurgeLocalCopy(*ppOptions); + + HLPR_DELETE(*ppOptions, + WFPSAMPLER_SYSLIB_TAG); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpmClassifyOptionsDestroyLocalCopy()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprFwpmClassifyOptionsPopulateLocalCopy" + + Purpose: Populate a local copy of an FWPM_CLASSIFY_OPTIONS.
+
+ Notes: The caller is responsible for freeing the allocated memory using + KrnlHlprFwpmClassifyOptionsPurgeLocalCopy().
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA364260.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprFwpmClassifyOptionsPopulateLocalCopy(_In_ const FWPM_CLASSIFY_OPTIONS* pOriginalOptions, + _Inout_ FWPM_CLASSIFY_OPTIONS* pOptions) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpmClassifyOptionsPopulateLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(pOriginalOptions); + NT_ASSERT(pOptions); + + NTSTATUS status = STATUS_SUCCESS; + + if(pOriginalOptions->numOptions) + { + HLPR_NEW_ARRAY(pOptions->options, + FWPM_CLASSIFY_OPTION, + pOriginalOptions->numOptions, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pOptions->options, + status); + + pOptions->numOptions = pOriginalOptions->numOptions; + + for(UINT32 optionIndex = 0; + optionIndex < pOptions->numOptions; + optionIndex++) + { + status = KrnlHlprFwpmClassifyOptionPopulateLocalCopy(&(pOriginalOptions->options[optionIndex]), + &(pOptions->options[optionIndex])); + HLPR_BAIL_ON_FAILURE(status); + } + } + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS) + KrnlHlprFwpmClassifyOptionsPurgeLocalCopy(pOptions); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpmClassifyOptionsPopulateLocalCopy() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @kernel_helper_function="KrnlHlprFwpmClassifyOptionsCreateLocalCopy" + + Purpose: Allocate and populate a local copy of an FWPM_CLASSIFY_OPTIONS.
+
+ Notes: The caller is responsible for freeing the allocated memory using + KrnlHlprFwpmClassifyOptionsDestroyLocalCopy().
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA364260.aspx
+*/ +__drv_allocatesMem(Pool) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Must_inspect_result_ +_Success_(return != 0) +FWPM_CLASSIFY_OPTIONS* KrnlHlprFwpmClassifyOptionsCreateLocalCopy(_In_ const FWPM_CLASSIFY_OPTIONS* pOriginalOptions) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpmClassifyOptionsCreateLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(pOriginalOptions); + + NTSTATUS status = STATUS_SUCCESS; + FWPM_CLASSIFY_OPTIONS* pOptions = 0; + +#pragma warning(push) +#pragma warning(disable: 6014) /// pOptions is expected to be cleaned up by caller using KrnlHlprFwpmClassifyOptionsDestroyLocalCopy + + HLPR_NEW(pOptions, + FWPM_CLASSIFY_OPTIONS, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pOptions, + status); + +#pragma warning(pop) + + status = KrnlHlprFwpmClassifyOptionsPopulateLocalCopy(pOriginalOptions, + pOptions); + + HLPR_BAIL_LABEL: + +#pragma warning(push) +#pragma warning(disable: 6001) /// pOptions initialized with call to HLPR_NEW & KrnlHlprFwpmClassifyOptionsPopulateLocalCopy + + if(status != STATUS_SUCCESS && + pOptions) + KrnlHlprFwpmClassifyOptionsDestroyLocalCopy(&pOptions); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpmClassifyOptionsCreateLocalCopy() [pOptions: %#p]\n", + pOptions); + +#endif /// DBG + + return pOptions; + +#pragma warning(pop) +} + +#endif /// FWPM_CLASSIFY_OPTIONS____ + +#ifndef IPSEC_DOSP_OPTIONS____ +#define IPSEC_DOSP_OPTIONS____ + +#if(NTDDI_VERSION >= NTDDI_WIN7) + +/** + @kernel_helper_function="KrnlHlprIPsecDoSPOptionsPurgeLocalCopy" + + Purpose: Cleanup a local copy of an IPSEC_DOSP_OPTIONS.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/DD744994.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprIPsecDoSPOptionsPurgeLocalCopy(_Inout_ IPSEC_DOSP_OPTIONS* pOptions) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprIPsecDoSPOptionsPurgeLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(pOptions); + + HLPR_DELETE_ARRAY(pOptions->publicIFLuids, + WFPSAMPLER_SYSLIB_TAG); + + HLPR_DELETE_ARRAY(pOptions->internalIFLuids, + WFPSAMPLER_SYSLIB_TAG); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprIPsecDoSPOptionsPurgeLocalCopy()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprIPsecDoSPOptionsDestroyLocalCopy" + + Purpose: Cleanup and free a local copy of an IPSEC_DOSP_OPTIONS.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/DD744994.aspx
+*/ +_At_(*ppOptions, _Pre_ _Notnull_) +_At_(*ppOptions, _Post_ _Null_ __drv_freesMem(Pool)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(*ppOptions == 0) +inline VOID KrnlHlprIPsecDoSPOptionsDestroyLocalCopy(_Inout_ IPSEC_DOSP_OPTIONS** ppOptions) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprIPsecDoSPOptionsDestroyLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(ppOptions); + + if(*ppOptions) + KrnlHlprIPsecDoSPOptionsPurgeLocalCopy(*ppOptions); + + HLPR_DELETE(*ppOptions, + WFPSAMPLER_SYSLIB_TAG); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprIPsecDoSPOptionsDestroyLocalCopy()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprIPsecDoSPOptionsPopulateLocalCopy" + + Purpose: Allocate and populate a local copy of an IPSEC_DOSP_OPTIONS.
+
+ Notes: The caller is responsible for freeing any allocated memory using + KrnlHlprIPsecDoSPOptionsDestroyLocalCopy().
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/DD744994.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprIPsecDoSPOptionsPopulateLocalCopy(_In_ const IPSEC_DOSP_OPTIONS* pOriginalOptions, + _Inout_ IPSEC_DOSP_OPTIONS* pOptions) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprIPsecDoSPOptionsPopulateLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(pOriginalOptions); + NT_ASSERT(pOptions); + + NTSTATUS status = STATUS_SUCCESS; + + RtlCopyMemory(pOptions, + pOriginalOptions, + sizeof(IPSEC_DOSP_OPTIONS)); + + pOptions->publicIFLuids = 0; + pOptions->internalIFLuids = 0; + + if(pOptions->numPublicIFLuids) + { + HLPR_NEW_ARRAY(pOptions->publicIFLuids, + UINT64, + pOptions->numPublicIFLuids, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pOptions->publicIFLuids, + status); + } + + if(pOptions->numInternalIFLuids) + { + HLPR_NEW_ARRAY(pOptions->internalIFLuids, + UINT64, + pOptions->numInternalIFLuids, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pOptions->internalIFLuids, + status); + } + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS) + KrnlHlprIPsecDoSPOptionsPurgeLocalCopy(pOptions); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprIPsecDoSPOptionsPopulateLocalCopy() [status: %#x]\n", + status); + +#endif /// DBG + + return status; + +} + +/** + @kernel_helper_function="KrnlHlprIPsecDoSPOptionsCreateLocalCopy" + + Purpose: Allocate and populate a local copy of an IPSEC_DOSP_OPTIONS.
+
+ Notes: The caller is responsible for freeing any allocated memory using + KrnlHlprIPsecDoSPOptionsDestroyLocalCopy().
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/DD744994.aspx
+*/ +__drv_allocatesMem(Pool) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Must_inspect_result_ +_Success_(return != 0) +IPSEC_DOSP_OPTIONS* KrnlHlprIPsecDoSPOptionsCreateLocalCopy(_In_ const IPSEC_DOSP_OPTIONS* pOriginalOptions) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprIPsecDoSPOptionsCreateLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(pOriginalOptions); + + NTSTATUS status = STATUS_SUCCESS; + IPSEC_DOSP_OPTIONS* pOptions = 0; + +#pragma warning(push) +#pragma warning(disable: 6014) /// pOptions is expected to be cleaned up by caller using KrnlHlprIPsecDoSPOptionsPopulateLocalCopy + + HLPR_NEW(pOptions, + IPSEC_DOSP_OPTIONS, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pOptions, + status); + +#pragma warning(pop) + + status = KrnlHlprIPsecDoSPOptionsPopulateLocalCopy(pOriginalOptions, + pOptions); + + HLPR_BAIL_LABEL: + +#pragma warning(push) +#pragma warning(disable: 6001) /// pOptions initialized with call to HLPR_NEW & KrnlHlprIPsecDoSPOptionsPopulateLocalCopy + + if(status != STATUS_SUCCESS && + pOptions) + KrnlHlprIPsecDoSPOptionsDestroyLocalCopy(&pOptions); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprIPsecDoSPOptionsCreateLocalCopy() [pOptions: %#p]\n", + pOptions); + +#endif /// DBG + + return pOptions; + +#pragma warning(pop) +} + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + +#endif /// IPSEC_DOSP_OPTIONS____ + +#ifndef FWP_TOKEN_INFORMATION____ +#define FWP_TOKEN_INFORMATION____ + +/** + @kernel_helper_function="KrnlHlprFwpTokenInformationPurgeLocalCopy" + + Purpose: Cleanup a local copy of an FWP_TOKEN_INFORMATION.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552440.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprFwpTokenInformationPurgeLocalCopy(_Inout_ FWP_TOKEN_INFORMATION* pTokenInfo) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpTokenInformationPurgeLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(pTokenInfo); + + for(UINT32 i = 0; + i < pTokenInfo->sidCount; + i++) + { + HLPR_DELETE_ARRAY(pTokenInfo->sids[i].Sid, + WFPSAMPLER_SYSLIB_TAG); + } + + HLPR_DELETE_ARRAY(pTokenInfo->sids, + WFPSAMPLER_SYSLIB_TAG); + + for(UINT32 i = 0; + i < pTokenInfo->restrictedSidCount; + i++) + { + HLPR_DELETE_ARRAY(pTokenInfo->restrictedSids[i].Sid, + WFPSAMPLER_SYSLIB_TAG); + } + + HLPR_DELETE_ARRAY(pTokenInfo->restrictedSids, + WFPSAMPLER_SYSLIB_TAG); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpTokenInformationPurgeLocalCopy()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprFwpTokenInformationDestroyLocalCopy" + + Purpose: Cleanup and free a local copy of an FWP_TOKEN_INFORMATION.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552440.aspx
+*/ +_At_(*ppTokenInfo, _Pre_ _Notnull_) +_At_(*ppTokenInfo, _Post_ _Null_ __drv_freesMem(Pool)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(*ppTokenInfo == 0) +inline VOID KrnlHlprFwpTokenInformationDestroyLocalCopy(_Inout_ FWP_TOKEN_INFORMATION** ppTokenInfo) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpTokenInformationDestroyLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(ppTokenInfo); + + if(*ppTokenInfo) + KrnlHlprFwpTokenInformationPurgeLocalCopy(*ppTokenInfo); + + HLPR_DELETE(*ppTokenInfo, + WFPSAMPLER_SYSLIB_TAG); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpTokenInformationDestroyLocalCopy()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprFwpTokenInformationPopulateLocalCopy" + + Purpose: Populate a local copy of an FWP_TOKEN_INFORMATION.
+
+ Notes: The caller is responsible for freeing the allocated memory using + KrnlHlprFwpTokenInformationPurgeLocalCopy().
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552440.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprFwpTokenInformationPopulateLocalCopy(_In_ const FWP_TOKEN_INFORMATION* pOriginalTokenInfo, + _Inout_ FWP_TOKEN_INFORMATION* pTokenInfo) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpTokenInformationPopulateLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(pOriginalTokenInfo); + NT_ASSERT(pTokenInfo); + + NTSTATUS status = STATUS_SUCCESS; + KIRQL irql = KeGetCurrentIrql(); + + HLPR_NEW_ARRAY(pTokenInfo->sids, + SID_AND_ATTRIBUTES, + pOriginalTokenInfo->sidCount, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pTokenInfo->sids, + status); + + pTokenInfo->sidCount = pOriginalTokenInfo->sidCount; + + for(UINT32 i = 0; + i < pTokenInfo->sidCount; + i++) + { + UINT32 sidSize = 0; + + if(irql < DISPATCH_LEVEL) + { + if(RtlValidSid(pOriginalTokenInfo->sids[i].Sid)) + { + sidSize = RtlLengthSid(pOriginalTokenInfo->sids[i].Sid); + if(sidSize) + { + HLPR_NEW_ARRAY(pTokenInfo->sids[i].Sid, + BYTE, + sidSize, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pTokenInfo->sids[i].Sid, + status); + + status = RtlCopySid(sidSize, + pTokenInfo->sids[i].Sid, + pOriginalTokenInfo->sids[i].Sid); + HLPR_BAIL_ON_FAILURE(status); + } + else + { + status = STATUS_INVALID_BUFFER_SIZE; + + HLPR_BAIL; + } + } + else + { + status = STATUS_INVALID_SID; + + HLPR_BAIL; + } + } + else + { + sidSize = SID_LENGTH(pOriginalTokenInfo->sids[i].Sid); + if(sidSize) + { + HLPR_NEW_ARRAY(pTokenInfo->sids[i].Sid, + BYTE, + sidSize, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pTokenInfo->sids[i].Sid, + status); + + RtlCopyMemory(pTokenInfo->sids[i].Sid, + pOriginalTokenInfo->sids[i].Sid, + sidSize); + } + else + { + status = STATUS_INVALID_BUFFER_SIZE; + + HLPR_BAIL; + } + } + + pTokenInfo->sids[i].Attributes = pTokenInfo->sids[i].Attributes; + } + + HLPR_NEW_ARRAY(pTokenInfo->restrictedSids, + SID_AND_ATTRIBUTES, + pOriginalTokenInfo->restrictedSidCount, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pTokenInfo->restrictedSids, + status); + + pTokenInfo->restrictedSidCount = pOriginalTokenInfo->restrictedSidCount; + + for(UINT32 i = 0; + i < pTokenInfo->restrictedSidCount; + i++) + { + UINT32 sidSize = 0; + + if(irql < DISPATCH_LEVEL) + { +#pragma warning(push) +#pragma warning(disable: 28118) /// IRQL check has already been performed + + if(RtlValidSid(pOriginalTokenInfo->restrictedSids[i].Sid)) + { + +#pragma warning(pop) + + sidSize = RtlLengthSid(pOriginalTokenInfo->restrictedSids[i].Sid); + if(sidSize) + { + HLPR_NEW_ARRAY(pTokenInfo->restrictedSids[i].Sid, + BYTE, + sidSize, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pTokenInfo->restrictedSids[i].Sid, + status); + + status = RtlCopySid(sidSize, + pTokenInfo->restrictedSids[i].Sid, + pOriginalTokenInfo->restrictedSids[i].Sid); + HLPR_BAIL_ON_FAILURE(status); + } + else + { + status = STATUS_INVALID_BUFFER_SIZE; + + HLPR_BAIL; + } + } + else + { + status = STATUS_INVALID_SID; + + HLPR_BAIL; + } + } + else + { + sidSize = SID_LENGTH(pOriginalTokenInfo->restrictedSids[i].Sid); + if(sidSize) + { + HLPR_NEW_ARRAY(pTokenInfo->restrictedSids[i].Sid, + BYTE, + sidSize, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pTokenInfo->restrictedSids[i].Sid, + status); + + RtlCopyMemory(pTokenInfo->restrictedSids[i].Sid, + pOriginalTokenInfo->restrictedSids[i].Sid, + sidSize); + } + else + { + status = STATUS_INVALID_BUFFER_SIZE; + + HLPR_BAIL; + } + } + + pTokenInfo->restrictedSids[i].Attributes = pTokenInfo->restrictedSids[i].Attributes; + } + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS) + KrnlHlprFwpTokenInformationDestroyLocalCopy(&pTokenInfo); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpTokenInformationPopulateLocalCopy() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @kernel_helper_function="KrnlHlprFwpTokenInformationCreateLocalCopy" + + Purpose: Allocate and populate a local copy of an FWP_TOKEN_INFORMATION.
+
+ Notes: The caller is responsible for freeing the allocated memory using + KrnlHlprFwpTokenInformationDestroyLocalCopy().
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552440.aspx
+*/ +__drv_allocatesMem(Pool) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Must_inspect_result_ +_Success_(return != 0) +FWP_TOKEN_INFORMATION* KrnlHlprFwpTokenInformationCreateLocalCopy(_In_ const FWP_TOKEN_INFORMATION* pOriginalTokenInfo) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpTokenInformationCreateLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(pOriginalTokenInfo); + + NTSTATUS status = STATUS_SUCCESS; + FWP_TOKEN_INFORMATION* pTokenInfo = 0; + +#pragma warning(push) +#pragma warning(disable: 6014) /// pTokenInfo is expected to be cleaned up by caller using KrnlHlprFwpTokenInformationDestroyLocalCopy + + HLPR_NEW(pTokenInfo, + FWP_TOKEN_INFORMATION, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pTokenInfo, + status); + +#pragma warning(pop) + + status = KrnlHlprFwpTokenInformationPopulateLocalCopy(pOriginalTokenInfo, + pTokenInfo); + + HLPR_BAIL_LABEL: + +#pragma warning(push) +#pragma warning(disable: 6001) /// pTokenInfo initialized with call to HLPR_NEW & KrnlHlprFwpTokenInformationPopulateLocalCopy + + if(status != STATUS_SUCCESS && + pTokenInfo) + KrnlHlprFwpTokenInformationDestroyLocalCopy(&pTokenInfo); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpTokenInformationCreateLocalCopy() [pTokenInfo: %#p]\n", + pTokenInfo); + +#endif /// DBG + + return pTokenInfo; + +#pragma warning(pop) +} + +#endif /// FWP_TOKEN_INFORMATION____ + +#ifndef FWP_VALUE____ +#define FWP_VALUE____ + +/** + @kernel_helper_function="KrnlHlprFwpValueGetStringForFwpsIncomingValue" + + Purpose: Return a string representing the condition field of the FWPS_INCOMING_VALUE.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552450.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windwos/Hardware/FF549939.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(return != 0) +PSTR KrnlHlprFwpValueGetStringForFwpsIncomingValue(_In_ UINT32 layerID, + _In_ UINT32 fieldID, + _Out_opt_ BOOLEAN* pFormat) /* 0 */ +{ + PSTR pString = 0; + + if(pFormat) + *pFormat = FALSE; + + switch(layerID) + { + case FWPS_LAYER_INBOUND_IPPACKET_V4: + case FWPS_LAYER_INBOUND_IPPACKET_V4_DISCARD: + case FWPS_LAYER_INBOUND_IPPACKET_V6: + case FWPS_LAYER_INBOUND_IPPACKET_V6_DISCARD: + { + if(fieldID == FWPS_FIELD_INBOUND_IPPACKET_V4_IP_LOCAL_ADDRESS) + { + pString = "FWPM_CONDITION_IP_LOCAL_ADDRESS"; + + if(pFormat) + *pFormat = ADDRESS_VALUE; + } + else if(fieldID == FWPS_FIELD_INBOUND_IPPACKET_V4_IP_REMOTE_ADDRESS) + { + pString = "FWPM_CONDITION_IP_REMOTE_ADDRESS"; + + if(pFormat) + *pFormat = ADDRESS_VALUE; + } + else if(fieldID == FWPS_FIELD_INBOUND_IPPACKET_V4_IP_LOCAL_ADDRESS_TYPE) + pString = "FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE"; + else if(fieldID == FWPS_FIELD_INBOUND_IPPACKET_V4_IP_LOCAL_INTERFACE) + pString = "FWPM_CONDITION_IP_LOCAL_INTERFACE"; + else if(fieldID == FWPS_FIELD_INBOUND_IPPACKET_V4_INTERFACE_INDEX) + pString = "FWPM_CONDITION_INTERFACE_INDEX"; + else if(fieldID == FWPS_FIELD_INBOUND_IPPACKET_V4_SUB_INTERFACE_INDEX) + pString = "FWPM_CONDITION_SUB_INTERFACE_INDEX"; + else if(fieldID == FWPS_FIELD_INBOUND_IPPACKET_V4_FLAGS) + { + pString = "FWPM_CONDITION_FLAGS"; + + if(pFormat) + *pFormat = FLAGS_VALUE; + } + else if(fieldID == FWPS_FIELD_INBOUND_IPPACKET_V4_INTERFACE_TYPE) + pString = "FWPM_CONDITION_INTERFACE_TYPE"; + else if(fieldID == FWPS_FIELD_INBOUND_IPPACKET_V4_TUNNEL_TYPE) + pString = "FWPM_CONDITION_TUNNEL_TYPE"; + + break; + } + case FWPS_LAYER_OUTBOUND_IPPACKET_V4: + case FWPS_LAYER_OUTBOUND_IPPACKET_V4_DISCARD: + case FWPS_LAYER_OUTBOUND_IPPACKET_V6: + case FWPS_LAYER_OUTBOUND_IPPACKET_V6_DISCARD: + { + if(fieldID == FWPS_FIELD_OUTBOUND_IPPACKET_V4_IP_LOCAL_ADDRESS) + { + pString = "FWPM_CONDITION_IP_LOCAL_ADDRESS"; + + if(pFormat) + *pFormat = ADDRESS_VALUE; + } + else if(fieldID == FWPS_FIELD_OUTBOUND_IPPACKET_V4_IP_LOCAL_ADDRESS_TYPE) + pString = "FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE"; + else if(fieldID == FWPS_FIELD_OUTBOUND_IPPACKET_V4_IP_REMOTE_ADDRESS) + { + pString = "FWPM_CONDITION_IP_REMOTE_ADDRESS"; + + if(pFormat) + *pFormat = ADDRESS_VALUE; + } + else if(fieldID == FWPS_FIELD_OUTBOUND_IPPACKET_V4_IP_LOCAL_INTERFACE) + pString = "FWPM_CONDITION_IP_LOCAL_INTERFACE"; + else if(fieldID == FWPS_FIELD_OUTBOUND_IPPACKET_V4_INTERFACE_INDEX) + pString = "FWPM_CONDITION_INTERFACE_INDEX"; + else if(fieldID == FWPS_FIELD_OUTBOUND_IPPACKET_V4_SUB_INTERFACE_INDEX) + pString = "FWPM_CONDITION_SUB_INTERFACE_INDEX"; + else if(fieldID == FWPS_FIELD_OUTBOUND_IPPACKET_V4_FLAGS) + { + pString = "FWPM_CONDITION_FLAGS"; + + if(pFormat) + *pFormat = FLAGS_VALUE; + } + else if(fieldID == FWPS_FIELD_OUTBOUND_IPPACKET_V4_INTERFACE_TYPE) + pString = "FWPM_CONDITION_INTERFACE_TYPE"; + else if(fieldID == FWPS_FIELD_OUTBOUND_IPPACKET_V4_TUNNEL_TYPE) + pString = "FWPM_CONDITION_TUNNEL_TYPE"; + + break; + } + case FWPS_LAYER_IPFORWARD_V4: + case FWPS_LAYER_IPFORWARD_V4_DISCARD: + case FWPS_LAYER_IPFORWARD_V6: + case FWPS_LAYER_IPFORWARD_V6_DISCARD: + { + if(fieldID == FWPS_FIELD_IPFORWARD_V4_IP_SOURCE_ADDRESS) + { + pString = "FWPM_CONDITION_IP_SOURCE_ADDRESS"; + + if(pFormat) + *pFormat = ADDRESS_VALUE; + } + else if(fieldID == FWPS_FIELD_IPFORWARD_V4_IP_DESTINATION_ADDRESS) + { + pString = "FWPM_CONDITION_IP_DESTINATION_ADDRESS"; + + if(pFormat) + *pFormat = ADDRESS_VALUE; + } + else if(fieldID == FWPS_FIELD_IPFORWARD_V4_IP_DESTINATION_ADDRESS_TYPE) + pString = "FWPM_CONDITION_IP_DESTINATION_ADDRESS_TYPE"; + else if(fieldID == FWPS_FIELD_IPFORWARD_V4_IP_LOCAL_INTERFACE) + pString = "FWPM_CONDITION_IP_LOCAL_INTERFACE"; + else if(fieldID == FWPS_FIELD_IPFORWARD_V4_IP_FORWARD_INTERFACE) + pString = "FWPM_CONDITION_IP_FORWARD_INTERFACE"; + else if(fieldID == FWPS_FIELD_IPFORWARD_V4_SOURCE_INTERFACE_INDEX) + pString = "FWPM_CONDITION_SOURCE_INTERFACE_INDEX"; + else if(fieldID == FWPS_FIELD_IPFORWARD_V4_SOURCE_SUB_INTERFACE_INDEX) + pString = "FWPM_CONDITION_SOURCE_SUB_INTERFACE_INDEX"; + else if(fieldID == FWPS_FIELD_IPFORWARD_V4_DESTINATION_INTERFACE_INDEX) + pString = "FWPM_CONDITION_DESTINATION_INTERFACE_INDEX"; + else if(fieldID == FWPS_FIELD_IPFORWARD_V4_DESTINATION_SUB_INTERFACE_INDEX) + pString = "FWPM_CONDITION_DESTINATION_SUB_INTERFACE_INDEX"; + else if(fieldID == FWPS_FIELD_IPFORWARD_V4_FLAGS) + { + pString = "FWPM_CONDITION_FLAGS"; + + if(pFormat) + *pFormat = FLAGS_VALUE; + } + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + else if(fieldID == FWPS_FIELD_IPFORWARD_V4_IP_PHYSICAL_ARRIVAL_INTERFACE) + pString = "FWPM_CONDITION_IP_PHYSICAL_ARRIVAL_INTERFACE"; + else if(fieldID == FWPS_FIELD_IPFORWARD_V4_ARRIVAL_INTERFACE_PROFILE_ID) + pString = "FWPM_CONDITION_ARRIVAL_INTERFACE_PROFILE_ID"; + else if(fieldID == FWPS_FIELD_IPFORWARD_V4_IP_PHYSICAL_NEXTHOP_INTERFACE) + pString = "FWPM_CONDITION_IP_PHYSICAL_NEXTHOP_INTERFACE"; + else if(fieldID == FWPS_FIELD_IPFORWARD_V4_NEXTHOP_INTERFACE_PROFILE_ID) + pString = "FWPM_CONDITION_NEXTHOP_INTERFACE_PROFILE_ID"; + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + break; + } + case FWPS_LAYER_INBOUND_TRANSPORT_V4: + case FWPS_LAYER_INBOUND_TRANSPORT_V4_DISCARD: + case FWPS_LAYER_INBOUND_TRANSPORT_V6: + case FWPS_LAYER_INBOUND_TRANSPORT_V6_DISCARD: + { + if(fieldID == FWPS_FIELD_INBOUND_TRANSPORT_V4_IP_PROTOCOL) + pString = "FWPM_CONDITION_IP_PROTOCOL"; + else if(fieldID == FWPS_FIELD_INBOUND_TRANSPORT_V4_IP_LOCAL_ADDRESS) + { + pString = "FWPM_CONDITION_IP_LOCAL_ADDRESS"; + + if(pFormat) + *pFormat = ADDRESS_VALUE; + } + else if(fieldID == FWPS_FIELD_INBOUND_TRANSPORT_V4_IP_REMOTE_ADDRESS) + { + pString = "FWPM_CONDITION_IP_REMOTE_ADDRESS"; + + if(pFormat) + *pFormat = ADDRESS_VALUE; + } + else if(fieldID == FWPS_FIELD_INBOUND_TRANSPORT_V4_IP_LOCAL_ADDRESS_TYPE) + pString = "FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE"; + else if(fieldID == FWPS_FIELD_INBOUND_TRANSPORT_V4_IP_LOCAL_PORT) + pString = "FWPM_CONDITION_IP_LOCAL_PORT"; + else if(fieldID == FWPS_FIELD_INBOUND_TRANSPORT_V4_IP_REMOTE_PORT) + pString = "FWPM_CONDITION_IP_REMOTE_PORT"; + else if(fieldID == FWPS_FIELD_INBOUND_TRANSPORT_V4_IP_LOCAL_INTERFACE) + pString = "FWPM_CONDITION_IP_LOCAL_INTERFACE"; + else if(fieldID == FWPS_FIELD_INBOUND_TRANSPORT_V4_INTERFACE_INDEX) + pString = "FWPM_CONDITION_INTERFACE_INDEX"; + else if(fieldID == FWPS_FIELD_INBOUND_TRANSPORT_V4_SUB_INTERFACE_INDEX) + pString = "FWPM_CONDITION_SUB_INTERFACE_INDEX"; + else if(fieldID == FWPS_FIELD_INBOUND_TRANSPORT_V4_FLAGS) + { + pString = "FWPM_CONDITION_FLAGS"; + + if(pFormat) + *pFormat = FLAGS_VALUE; + } + else if(fieldID == FWPS_FIELD_INBOUND_TRANSPORT_V4_INTERFACE_TYPE) + pString = "FWPM_CONDITION_INTERFACE_TYPE"; + else if(fieldID == FWPS_FIELD_INBOUND_TRANSPORT_V4_TUNNEL_TYPE) + pString = "FWPM_CONDITION_TUNNEL_TYPE"; + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + else if(fieldID == FWPS_FIELD_INBOUND_TRANSPORT_V4_PROFILE_ID) + pString = "FWPM_CONDITION_PROFILE_ID"; + +#if(NTDDI_VERSION >= NTDDI_WINTHRESHOLD) + + else if(fieldID == FWPS_FIELD_INBOUND_TRANSPORT_V4_IPSEC_SECURITY_REALM_ID) + pString = "FWPM_CONDITION_IPSEC_SECURITY_REALM_ID"; + +#endif /// (NTDDI_VERSION >= NTDDI_WINTHRESHOLD) + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + break; + } + case FWPS_LAYER_OUTBOUND_TRANSPORT_V4: + case FWPS_LAYER_OUTBOUND_TRANSPORT_V4_DISCARD: + case FWPS_LAYER_OUTBOUND_TRANSPORT_V6: + case FWPS_LAYER_OUTBOUND_TRANSPORT_V6_DISCARD: + { + if(fieldID == FWPS_FIELD_OUTBOUND_TRANSPORT_V4_IP_PROTOCOL) + pString = "FWPM_CONDITION_IP_PROTOCOL"; + else if(fieldID == FWPS_FIELD_OUTBOUND_TRANSPORT_V4_IP_LOCAL_ADDRESS) + { + pString = "FWPM_CONDITION_IP_LOCAL_ADDRESS"; + + if(pFormat) + *pFormat = ADDRESS_VALUE; + } + else if(fieldID == FWPS_FIELD_OUTBOUND_TRANSPORT_V4_IP_LOCAL_ADDRESS_TYPE) + pString = "FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE"; + else if(fieldID == FWPS_FIELD_OUTBOUND_TRANSPORT_V4_IP_REMOTE_ADDRESS) + { + pString = "FWPM_CONDITION_IP_REMOTE_ADDRESS"; + + if(pFormat) + *pFormat = ADDRESS_VALUE; + } + else if(fieldID == FWPS_FIELD_OUTBOUND_TRANSPORT_V4_IP_LOCAL_PORT) + pString = "FWPM_CONDITION_IP_LOCAL_PORT"; + else if(fieldID == FWPS_FIELD_OUTBOUND_TRANSPORT_V4_IP_REMOTE_PORT) + pString = "FWPM_CONDITION_IP_REMOTE_PORT"; + else if(fieldID == FWPS_FIELD_OUTBOUND_TRANSPORT_V4_IP_LOCAL_INTERFACE) + pString = "FWPM_CONDITION_IP_LOCAL_INTERFACE"; + else if(fieldID == FWPS_FIELD_OUTBOUND_TRANSPORT_V4_INTERFACE_INDEX) + pString = "FWPM_CONDITION_INTERFACE_INDEX"; + else if(fieldID == FWPS_FIELD_OUTBOUND_TRANSPORT_V4_SUB_INTERFACE_INDEX) + pString = "FWPM_CONDITION_SUB_INTERFACE_INDEX"; + else if(fieldID == FWPS_FIELD_OUTBOUND_TRANSPORT_V4_IP_DESTINATION_ADDRESS_TYPE) + pString = "FWPM_CONDITION_IP_DESTINATION_ADDRESS_TYPE"; + else if(fieldID == FWPS_FIELD_OUTBOUND_TRANSPORT_V4_FLAGS) + { + pString = "FWPM_CONDITION_FLAGS"; + + if(pFormat) + *pFormat = FLAGS_VALUE; + } + else if(fieldID == FWPS_FIELD_OUTBOUND_TRANSPORT_V4_INTERFACE_TYPE) + pString = "FWPM_CONDITION_INTERFACE_TYPE"; + else if(fieldID == FWPS_FIELD_OUTBOUND_TRANSPORT_V4_TUNNEL_TYPE) + pString = "FWPM_CONDITION_TUNNEL_TYPE"; + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + else if(fieldID == FWPS_FIELD_OUTBOUND_TRANSPORT_V4_PROFILE_ID) + pString = "FWPM_CONDITION_PROFILE_ID"; + +#if(NTDDI_VERSION >= NTDDI_WINTHRESHOLD) + + else if(fieldID == FWPS_FIELD_OUTBOUND_TRANSPORT_V4_IPSEC_SECURITY_REALM_ID) + pString = "FWPM_CONDITION_IPSEC_SECURITY_REALM_ID"; + +#endif /// (NTDDI_VERSION >= NTDDI_WINTHRESHOLD) +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + break; + } + case FWPS_LAYER_STREAM_V4: + case FWPS_LAYER_STREAM_V4_DISCARD: + case FWPS_LAYER_STREAM_V6: + case FWPS_LAYER_STREAM_V6_DISCARD: + { + if(fieldID == FWPS_FIELD_STREAM_V4_IP_LOCAL_ADDRESS) + { + pString = "FWPM_CONDITION_IP_LOCAL_ADDRESS"; + + if(pFormat) + *pFormat = ADDRESS_VALUE; + } + else if(fieldID == FWPS_FIELD_STREAM_V4_IP_LOCAL_ADDRESS_TYPE) + pString = "FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE"; + else if(fieldID == FWPS_FIELD_STREAM_V4_IP_REMOTE_ADDRESS) + { + pString = "FWPM_CONDITION_IP_REMOTE_ADDRESS"; + + if(pFormat) + *pFormat = ADDRESS_VALUE; + } + else if(fieldID == FWPS_FIELD_STREAM_V4_IP_LOCAL_PORT) + pString = "FWPM_CONDITION_IP_LOCAL_PORT"; + else if(fieldID == FWPS_FIELD_STREAM_V4_IP_REMOTE_PORT) + pString = "FWPM_CONDITION_IP_REMOTE_PORT"; + else if(fieldID == FWPS_FIELD_STREAM_V4_DIRECTION) + pString = "FWPM_CONDITION_DIRECTION"; + +#if(NTDDI_VERSION >= NTDDI_VISTASP1) + + else if(fieldID == FWPS_FIELD_STREAM_V4_FLAGS) + { + pString = "FWPM_CONDITION_FLAGS"; + + if(pFormat) + *pFormat = FLAGS_VALUE; + } + +#endif /// (NTDDI_VERSION >= NTDDI_VISTASP1) + + break; + } + case FWPS_LAYER_DATAGRAM_DATA_V4: + case FWPS_LAYER_DATAGRAM_DATA_V4_DISCARD: + case FWPS_LAYER_DATAGRAM_DATA_V6: + case FWPS_LAYER_DATAGRAM_DATA_V6_DISCARD: + { + if(fieldID == FWPS_FIELD_DATAGRAM_DATA_V4_IP_PROTOCOL) + pString = "FWPM_CONDITION_IP_PROTOCOL"; + else if(fieldID == FWPS_FIELD_DATAGRAM_DATA_V4_IP_LOCAL_ADDRESS) + { + pString = "FWPM_CONDITION_IP_LOCAL_ADDRESS"; + + if(pFormat) + *pFormat = ADDRESS_VALUE; + } + else if(fieldID == FWPS_FIELD_DATAGRAM_DATA_V4_IP_REMOTE_ADDRESS) + { + pString = "FWPM_CONDITION_IP_REMOTE_ADDRESS"; + + if(pFormat) + *pFormat = ADDRESS_VALUE; + } + else if(fieldID == FWPS_FIELD_DATAGRAM_DATA_V4_IP_LOCAL_ADDRESS_TYPE) + pString = "FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE"; + else if(fieldID == FWPS_FIELD_DATAGRAM_DATA_V4_IP_LOCAL_PORT) + pString = "FWPM_CONDITION_IP_LOCAL_PORT"; + else if(fieldID == FWPS_FIELD_DATAGRAM_DATA_V4_IP_REMOTE_PORT) + pString = "FWPM_CONDITION_IP_REMOTE_PORT"; + else if(fieldID == FWPS_FIELD_DATAGRAM_DATA_V4_IP_LOCAL_INTERFACE) + pString = "FWPM_CONDITION_IP_LOCAL_INTERFACE"; + else if(fieldID == FWPS_FIELD_DATAGRAM_DATA_V4_INTERFACE_INDEX) + pString = "FWPM_CONDITION_INTERFACE_INDEX"; + else if(fieldID == FWPS_FIELD_DATAGRAM_DATA_V4_SUB_INTERFACE_INDEX) + pString = "FWPM_CONDITION_SUB_INTERFACE_INDEX"; + else if(fieldID == FWPS_FIELD_DATAGRAM_DATA_V4_DIRECTION) + pString = "FWPM_CONDITION_DIRECTION"; + else if(fieldID == FWPS_FIELD_DATAGRAM_DATA_V4_FLAGS) + { + pString = "FWPM_CONDITION_FLAGS"; + + if(pFormat) + *pFormat = FLAGS_VALUE; + } + else if(fieldID == FWPS_FIELD_DATAGRAM_DATA_V4_INTERFACE_TYPE) + pString = "FWPM_CONDITION_INTERFACE_TYPE"; + else if(fieldID == FWPS_FIELD_DATAGRAM_DATA_V4_TUNNEL_TYPE) + pString = "FWPM_CONDITION_TUNNEL_TYPE"; + + break; + } + case FWPS_LAYER_INBOUND_ICMP_ERROR_V4: + case FWPS_LAYER_INBOUND_ICMP_ERROR_V4_DISCARD: + case FWPS_LAYER_INBOUND_ICMP_ERROR_V6: + case FWPS_LAYER_INBOUND_ICMP_ERROR_V6_DISCARD: + { + if(fieldID == FWPS_FIELD_INBOUND_ICMP_ERROR_V4_EMBEDDED_PROTOCOL) + pString = "FWPM_CONDITION_EMBEDDED_PROTOCOL"; + else if(fieldID == FWPS_FIELD_INBOUND_ICMP_ERROR_V4_IP_LOCAL_ADDRESS) + { + pString = "FWPM_CONDITION_IP_LOCAL_ADDRESS"; + + if(pFormat) + *pFormat = ADDRESS_VALUE; + } + else if(fieldID == FWPS_FIELD_INBOUND_ICMP_ERROR_V4_IP_REMOTE_ADDRESS) + { + pString = "FWPM_CONDITION_IP_REMOTE_ADDRESS"; + + if(pFormat) + *pFormat = ADDRESS_VALUE; + } + else if(fieldID == FWPS_FIELD_INBOUND_ICMP_ERROR_V4_EMBEDDED_REMOTE_ADDRESS) + pString = "FWPM_CONDITION_EMBEDDED_REMOTE_ADDRESS"; + else if(fieldID == FWPS_FIELD_INBOUND_ICMP_ERROR_V4_EMBEDDED_LOCAL_ADDRESS_TYPE) + pString = "FWPM_CONDITION_EMBEDDED_LOCAL_ADDRESS_TYPE"; + else if(fieldID == FWPS_FIELD_INBOUND_ICMP_ERROR_V4_EMBEDDED_LOCAL_PORT) + pString = "FWPM_CONDITION_EMBEDDED_LOCAL_PORT"; + else if(fieldID == FWPS_FIELD_INBOUND_ICMP_ERROR_V4_EMBEDDED_REMOTE_PORT) + pString = "FWPM_CONDITION_EMBEDDED_REMOTE_PORT"; + else if(fieldID == FWPS_FIELD_INBOUND_ICMP_ERROR_V4_IP_LOCAL_INTERFACE) + pString = "FWPM_CONDITION_IP_LOCAL_INTERFACE"; + else if(fieldID == FWPS_FIELD_INBOUND_ICMP_ERROR_V4_ICMP_TYPE) + pString = "FWPM_CONDITION_ICMP_TYPE"; + else if(fieldID == FWPS_FIELD_INBOUND_ICMP_ERROR_V4_ICMP_CODE) + pString = "FWPM_CONDITION_ICMP_CODE"; + else if(fieldID == FWPS_FIELD_INBOUND_ICMP_ERROR_V4_INTERFACE_INDEX) + pString = "FWPM_CONDITION_INTERFACE_INDEX"; + else if(fieldID == FWPS_FIELD_INBOUND_ICMP_ERROR_V4_SUB_INTERFACE_INDEX) + pString = "FWPM_CONDITION_SUB_INTERFACE_INDEX"; + else if(fieldID == FWPS_FIELD_INBOUND_ICMP_ERROR_V4_INTERFACE_TYPE) + pString = "FWPM_CONDITION_INTERFACE_TYPE"; + else if(fieldID == FWPS_FIELD_INBOUND_ICMP_ERROR_V4_TUNNEL_TYPE) + pString = "FWPM_CONDITION_TUNNEL_TYPE"; + +#if(NTDDI_VERSION >= NTDDI_VISTASP1) + + else if(fieldID == FWPS_FIELD_INBOUND_ICMP_ERROR_V4_IP_ARRIVAL_INTERFACE) + pString = "FWPM_CONDITION_IP_ARRIVAL_INTERFACE"; + else if(fieldID == FWPS_FIELD_INBOUND_ICMP_ERROR_V4_ARRIVAL_INTERFACE_INDEX) + pString = "FWPM_CONDITION_ARRIVAL_INTERFACE_INDEX"; + else if(fieldID == FWPS_FIELD_INBOUND_ICMP_ERROR_V4_ARRIVAL_INTERFACE_TYPE) + pString = "FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE"; + else if(fieldID == FWPS_FIELD_INBOUND_ICMP_ERROR_V4_ARRIVAL_TUNNEL_TYPE) + pString = "FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE"; + else if(fieldID == FWPS_FIELD_INBOUND_ICMP_ERROR_V4_FLAGS) + { + pString = "FWPM_CONDITION_FLAGS"; + + if(pFormat) + *pFormat = FLAGS_VALUE; + } + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + else if(fieldID == FWPS_FIELD_INBOUND_ICMP_ERROR_V4_ARRIVAL_INTERFACE_PROFILE_ID) + pString = "FWPM_CONDITION_ARRIVAL_INTERFACE_PROFILE_ID"; + else if(fieldID == FWPS_FIELD_INBOUND_ICMP_ERROR_V4_INTERFACE_QUARANTINE_EPOCH) + pString = "FWPM_CONDITION_INTERFACE_QUARANTINE_EPOCH"; + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) +#endif /// (NTDDI_VERSION >= NTDDI_VISTASP1) + + break; + } + case FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4: + case FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4_DISCARD: + case FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6: + case FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6_DISCARD: + { + if(fieldID == FWPS_FIELD_OUTBOUND_ICMP_ERROR_V4_IP_LOCAL_ADDRESS) + { + pString = "FWPM_CONDITION_IP_LOCAL_ADDRESS"; + + if(pFormat) + *pFormat = ADDRESS_VALUE; + } + else if(fieldID == FWPS_FIELD_OUTBOUND_ICMP_ERROR_V4_IP_REMOTE_ADDRESS) + { + pString = "FWPM_CONDITION_IP_REMOTE_ADDRESS"; + + if(pFormat) + *pFormat = ADDRESS_VALUE; + } + else if(fieldID == FWPS_FIELD_OUTBOUND_ICMP_ERROR_V4_IP_LOCAL_ADDRESS_TYPE) + pString = "FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE"; + else if(fieldID == FWPS_FIELD_OUTBOUND_ICMP_ERROR_V4_IP_LOCAL_INTERFACE) + pString = "FWPM_CONDITION_IP_LOCAL_INTERFACE"; + else if(fieldID == FWPS_FIELD_OUTBOUND_ICMP_ERROR_V4_ICMP_TYPE) + pString = "FWPM_CONDITION_ICMP_TYPE"; + else if(fieldID == FWPS_FIELD_OUTBOUND_ICMP_ERROR_V4_ICMP_CODE) + pString = "FWPM_CONDITION_ICMP_CODE"; + else if(fieldID == FWPS_FIELD_OUTBOUND_ICMP_ERROR_V4_INTERFACE_INDEX) + pString = "FWPM_CONDITION_INTERFACE_INDEX"; + else if(fieldID == FWPS_FIELD_OUTBOUND_ICMP_ERROR_V4_SUB_INTERFACE_INDEX) + pString = "FWPM_CONDITION_SUB_INTERFACE_INDEX"; + else if(fieldID == FWPS_FIELD_OUTBOUND_ICMP_ERROR_V4_INTERFACE_TYPE) + pString = "FWPM_CONDITION_INTERFACE_TYPE"; + else if(fieldID == FWPS_FIELD_OUTBOUND_ICMP_ERROR_V4_TUNNEL_TYPE) + pString = "FWPM_CONDITION_TUNNEL_TYPE"; + +#if(NTDDI_VERSION >= NTDDI_VISTASP1) + + else if(fieldID == FWPS_FIELD_OUTBOUND_ICMP_ERROR_V4_FLAGS) + { + pString = "FWPM_CONDITION_FLAGS"; + + if(pFormat) + *pFormat = FLAGS_VALUE; + } + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + else if(fieldID == FWPS_FIELD_OUTBOUND_ICMP_ERROR_V4_NEXTHOP_INTERFACE_PROFILE_ID) + pString = "FWPM_CONDITION_NEXTHOP_INTERFACE_PROFILE_ID"; + else if(fieldID == FWPS_FIELD_OUTBOUND_ICMP_ERROR_V4_INTERFACE_QUARANTINE_EPOCH) + pString = "FWPM_CONDITION_INTERFACE_QUARANTINE_EPOCH"; + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) +#endif /// (NTDDI_VERSION >= NTDDI_VISTASP1) + + break; + } + case FWPS_LAYER_ALE_RESOURCE_ASSIGNMENT_V4: + case FWPS_LAYER_ALE_RESOURCE_ASSIGNMENT_V4_DISCARD: + case FWPS_LAYER_ALE_RESOURCE_ASSIGNMENT_V6: + case FWPS_LAYER_ALE_RESOURCE_ASSIGNMENT_V6_DISCARD: + { + if(fieldID == FWPS_FIELD_ALE_RESOURCE_ASSIGNMENT_V4_ALE_APP_ID) + { + pString = "FWPM_CONDITION_ALE_APP_ID"; + + if(pFormat) + *pFormat = UNICODE_STRING_VALUE; + } + else if(fieldID == FWPS_FIELD_ALE_RESOURCE_ASSIGNMENT_V4_ALE_USER_ID) + pString = "FWPM_CONDITION_ALE_USER_ID"; + else if(fieldID == FWPS_FIELD_ALE_RESOURCE_ASSIGNMENT_V4_IP_LOCAL_ADDRESS) + { + pString = "FWPM_CONDITION_IP_LOCAL_ADDRESS"; + + if(pFormat) + *pFormat = ADDRESS_VALUE; + } + else if(fieldID == FWPS_FIELD_ALE_RESOURCE_ASSIGNMENT_V4_IP_LOCAL_ADDRESS_TYPE) + pString = "FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE"; + else if(fieldID == FWPS_FIELD_ALE_RESOURCE_ASSIGNMENT_V4_IP_LOCAL_PORT) + pString = "FWPM_CONDITION_IP_LOCAL_PORT"; + else if(fieldID == FWPS_FIELD_ALE_RESOURCE_ASSIGNMENT_V4_IP_PROTOCOL) + pString = "FWPM_CONDITION_IP_PROTOCOL"; + else if(fieldID == FWPS_FIELD_ALE_RESOURCE_ASSIGNMENT_V4_ALE_PROMISCUOUS_MODE) + pString = "FWPM_CONDITION_ALE_PROMISCUOUS_MODE"; + else if(fieldID == FWPS_FIELD_ALE_RESOURCE_ASSIGNMENT_V4_IP_LOCAL_INTERFACE) + pString = "FWPM_CONDITION_IP_LOCAL_INTERFACE"; + else if(fieldID == FWPS_FIELD_ALE_RESOURCE_ASSIGNMENT_V4_FLAGS) + { + pString = "FWPM_CONDITION_FLAGS"; + + if(pFormat) + *pFormat = FLAGS_VALUE; + } + else if(fieldID == FWPS_FIELD_ALE_RESOURCE_ASSIGNMENT_V4_INTERFACE_TYPE) + pString = "FWPM_CONDITION_INTERFACE_TYPE"; + else if(fieldID == FWPS_FIELD_ALE_RESOURCE_ASSIGNMENT_V4_TUNNEL_TYPE) + pString = "FWPM_CONDITION_TUNNEL_TYPE"; + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + else if(fieldID == FWPS_FIELD_ALE_RESOURCE_ASSIGNMENT_V4_LOCAL_INTERFACE_PROFILE_ID) + pString = "FWPM_CONDITION_LOCAL_INTERFACE_PROFILE_ID"; + else if(fieldID == FWPS_FIELD_ALE_RESOURCE_ASSIGNMENT_V4_SIO_FIREWALL_SOCKET_PROPERTY) + pString = "FWPM_CONDITION_SIO_FIREWALL_SOCKET_PROPERTY"; + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + else if(fieldID == FWPS_FIELD_ALE_RESOURCE_ASSIGNMENT_V4_ALE_PACKAGE_ID) + pString = "FWPM_CONDITION_ALE_PACKAGE_ID"; + +#if(NTDDI_VERSION >= NTDDI_WINTHRESHOLD) + + else if(fieldID == FWPS_FIELD_ALE_RESOURCE_ASSIGNMENT_V4_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE) + pString = "FWPM_CONDITION_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE"; + +#endif /// (NTDDI_VERSION >= NTDDI_WINTHRESHOLD) +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + break; + } + case FWPS_LAYER_ALE_AUTH_LISTEN_V4: + case FWPS_LAYER_ALE_AUTH_LISTEN_V4_DISCARD: + case FWPS_LAYER_ALE_AUTH_LISTEN_V6: + case FWPS_LAYER_ALE_AUTH_LISTEN_V6_DISCARD: + { + if(fieldID == FWPS_FIELD_ALE_AUTH_LISTEN_V4_ALE_APP_ID) + { + pString = "FWPM_CONDITION_ALE_APP_ID"; + + if(pFormat) + *pFormat = UNICODE_STRING_VALUE; + } + else if(fieldID == FWPS_FIELD_ALE_AUTH_LISTEN_V4_ALE_USER_ID) + pString = "FWPM_CONDITION_ALE_USER_ID"; + else if(fieldID == FWPS_FIELD_ALE_AUTH_LISTEN_V4_IP_LOCAL_ADDRESS) + { + pString = "FWPM_CONDITION_IP_LOCAL_ADDRESS"; + + if(pFormat) + *pFormat = ADDRESS_VALUE; + } + else if(fieldID == FWPS_FIELD_ALE_AUTH_LISTEN_V4_IP_LOCAL_ADDRESS_TYPE) + pString = "FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE"; + else if(fieldID == FWPS_FIELD_ALE_AUTH_LISTEN_V4_IP_LOCAL_PORT) + pString = "FWPM_CONDITION_IP_LOCAL_PORT"; + else if(fieldID == FWPS_FIELD_ALE_AUTH_LISTEN_V4_IP_LOCAL_INTERFACE) + pString = "FWPM_CONDITION_IP_LOCAL_INTERFACE"; + else if(fieldID == FWPS_FIELD_ALE_AUTH_LISTEN_V4_FLAGS) + { + pString = "FWPM_CONDITION_FLAGS"; + + if(pFormat) + *pFormat = FLAGS_VALUE; + } + else if(fieldID == FWPS_FIELD_ALE_AUTH_LISTEN_V4_INTERFACE_TYPE) + pString = "FWPM_CONDITION_INTERFACE_TYPE"; + else if(fieldID == FWPS_FIELD_ALE_AUTH_LISTEN_V4_TUNNEL_TYPE) + pString = "FWPM_CONDITION_TUNNEL_TYPE"; + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + else if(fieldID == FWPS_FIELD_ALE_AUTH_LISTEN_V4_LOCAL_INTERFACE_PROFILE_ID) + pString = "FWPM_CONDITION_LOCAL_INTERFACE_PROFILE_ID"; + else if(fieldID == FWPS_FIELD_ALE_AUTH_LISTEN_V4_SIO_FIREWALL_SOCKET_PROPERTY) + pString = "FWPM_CONDITION_SIO_FIREWALL_SOCKET_PROPERTY"; + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + else if(fieldID == FWPS_FIELD_ALE_AUTH_LISTEN_V4_ALE_PACKAGE_ID) + pString = "FWPM_CONDITION_ALE_PACKAGE_ID"; + +#if(NTDDI_VERSION >= NTDDI_WINTHRESHOLD) + + else if(fieldID == FWPS_FIELD_ALE_AUTH_LISTEN_V4_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE) + pString = "FWPM_CONDITION_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE"; + +#endif /// (NTDDI_VERSION >= NTDDI_WINTHRESHOLD) +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + break; + } + case FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4: + case FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4_DISCARD: + case FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6: + case FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6_DISCARD: + { + if(fieldID == FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_ALE_APP_ID) + { + pString = "FWPM_CONDITION_ALE_APP_ID"; + + if(pFormat) + *pFormat = UNICODE_STRING_VALUE; + } + else if(fieldID == FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_ALE_USER_ID) + pString = "FWPM_CONDITION_ALE_USER_ID"; + else if(fieldID == FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_IP_LOCAL_ADDRESS) + { + pString = "FWPM_CONDITION_IP_LOCAL_ADDRESS"; + + if(pFormat) + *pFormat = ADDRESS_VALUE; + } + else if(fieldID == FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_IP_LOCAL_ADDRESS_TYPE) + pString = "FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE"; + else if(fieldID == FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_IP_LOCAL_PORT) + pString = "FWPM_CONDITION_IP_LOCAL_PORT"; + else if(fieldID == FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_IP_PROTOCOL) + pString = "FWPM_CONDITION_IP_PROTOCOL"; + else if(fieldID == FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_IP_REMOTE_ADDRESS) + { + pString = "FWPM_CONDITION_IP_REMOTE_ADDRESS"; + + if(pFormat) + *pFormat = ADDRESS_VALUE; + } + else if(fieldID == FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_IP_REMOTE_PORT) + pString = "FWPM_CONDITION_IP_REMOTE_PORT"; + else if(fieldID == FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_ALE_REMOTE_USER_ID) + pString = "FWPM_CONDITION_ALE_REMOTE_USER_ID"; + else if(fieldID == FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_ALE_REMOTE_MACHINE_ID) + pString = "FWPM_CONDITION_ALE_REMOTE_MACHINE_ID"; + else if(fieldID == FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_IP_LOCAL_INTERFACE) + pString = "FWPM_CONDITION_IP_LOCAL_INTERFACE"; + else if(fieldID == FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_FLAGS) + { + pString = "FWPM_CONDITION_FLAGS"; + + if(pFormat) + *pFormat = FLAGS_VALUE; + } + else if(fieldID == FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_SIO_FIREWALL_SYSTEM_PORT) + pString = "FWPM_CONDITION_SIO_FIREWALL_SYSTEM_PORT"; + else if(fieldID == FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_NAP_CONTEXT) + pString = "FWPM_CONDITION_NAP_CONTEXT"; + else if(fieldID == FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_INTERFACE_TYPE) + pString = "FWPM_CONDITION_INTERFACE_TYPE"; + else if(fieldID == FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_TUNNEL_TYPE) + pString = "FWPM_CONDITION_TUNNEL_TYPE"; + else if(fieldID == FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_INTERFACE_INDEX) + pString = "FWPM_CONDITION_INTERFACE_INDEX"; + else if(fieldID == FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_SUB_INTERFACE_INDEX) + pString = "FWPM_CONDITION_SUB_INTERFACE_INDEX"; + +#if(NTDDI_VERSION >= NTDDI_VISTASP1) + + else if(fieldID == FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_IP_ARRIVAL_INTERFACE) + pString = "FWPM_CONDITION_IP_ARRIVAL_INTERFACE"; + else if(fieldID == FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_ARRIVAL_INTERFACE_TYPE) + pString = "FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE"; + else if(fieldID == FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_ARRIVAL_TUNNEL_TYPE) + pString = "FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE"; + else if(fieldID == FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_ARRIVAL_INTERFACE_INDEX) + pString = "FWPM_CONDITION_ARRIVAL_INTERFACE_INDEX"; + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + else if(fieldID == FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_NEXTHOP_SUB_INTERFACE_INDEX) + pString = "FWPM_CONDITION_NEXTHOP_SUB_INTERFACE_INDEX"; + else if(fieldID == FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_IP_NEXTHOP_INTERFACE) + pString = "FWPM_CONDITION_IP_NEXTHOP_INTERFACE"; + else if(fieldID == FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_NEXTHOP_INTERFACE_TYPE) + pString = "FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE"; + else if(fieldID == FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_NEXTHOP_TUNNEL_TYPE) + pString = "FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE"; + else if(fieldID == FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_NEXTHOP_INTERFACE_INDEX) + pString = "FWPM_CONDITION_NEXTHOP_INTERFACE_INDEX"; + else if(fieldID == FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_ORIGINAL_PROFILE_ID) + pString = "FWPM_CONDITION_ORIGINAL_PROFILE_ID"; + else if(fieldID == FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_CURRENT_PROFILE_ID) + pString = "FWPM_CONDITION_CURRENT_PROFILE_ID"; + else if(fieldID == FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_REAUTHORIZE_REASON) + pString = "FWPM_CONDITION_REAUTHORIZE_REASON"; + else if(fieldID == FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_ORIGINAL_ICMP_TYPE) + pString = "FWPM_CONDITION_ORIGINAL_ICMP_TYPE"; + else if(fieldID == FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_INTERFACE_QUARANTINE_EPOCH) + pString = "FWPM_CONDITION_INTERFACE_QUARANTINE_EPOCH"; + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + else if(fieldID == FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_ALE_PACKAGE_ID) + pString = "FWPM_CONDITION_ALE_PACKAGE_ID"; + +#if(NTDDI_VERSION >= NTDDI_WINTHRESHOLD) + + else if(fieldID == FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE) + pString = "FWPM_CONDITION_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE"; + +#endif /// (NTDDI_VERSION >= NTDDI_WINTHRESHOLD) +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) +#endif /// (NTDDI_VERSION >= NTDDI_VISTASP1) + + break; + } + case FWPS_LAYER_ALE_AUTH_CONNECT_V4: + case FWPS_LAYER_ALE_AUTH_CONNECT_V4_DISCARD: + case FWPS_LAYER_ALE_AUTH_CONNECT_V6: + case FWPS_LAYER_ALE_AUTH_CONNECT_V6_DISCARD: + { + if(fieldID == FWPS_FIELD_ALE_AUTH_CONNECT_V4_ALE_APP_ID) + { + pString = "FWPM_CONDITION_ALE_APP_ID"; + + if(pFormat) + *pFormat = UNICODE_STRING_VALUE; + } + else if(fieldID == FWPS_FIELD_ALE_AUTH_CONNECT_V4_ALE_USER_ID) + pString = "FWPM_CONDITION_ALE_USER_ID"; + else if(fieldID == FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_LOCAL_ADDRESS) + { + pString = "FWPM_CONDITION_IP_LOCAL_ADDRESS"; + + if(pFormat) + *pFormat = ADDRESS_VALUE; + } + else if(fieldID == FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_LOCAL_ADDRESS_TYPE) + pString = "FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE"; + else if(fieldID == FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_LOCAL_PORT) + pString = "FWPM_CONDITION_IP_LOCAL_PORT"; + else if(fieldID == FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_PROTOCOL) + pString = "FWPM_CONDITION_IP_PROTOCOL"; + else if(fieldID == FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_REMOTE_ADDRESS) + { + pString = "FWPM_CONDITION_IP_REMOTE_ADDRESS"; + + if(pFormat) + *pFormat = ADDRESS_VALUE; + } + else if(fieldID == FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_REMOTE_PORT) + pString = "FWPM_CONDITION_IP_REMOTE_PORT"; + else if(fieldID == FWPS_FIELD_ALE_AUTH_CONNECT_V4_ALE_REMOTE_USER_ID) + pString = "FWPM_CONDITION_ALE_REMOTE_USER_ID"; + else if(fieldID == FWPS_FIELD_ALE_AUTH_CONNECT_V4_ALE_REMOTE_MACHINE_ID) + pString = "FWPM_CONDITION_ALE_REMOTE_MACHINE_ID"; + else if(fieldID == FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_DESTINATION_ADDRESS_TYPE) + pString = "FWPM_CONDITION_IP_DESTINATION_ADDRESS_TYPE"; + else if(fieldID == FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_LOCAL_INTERFACE) + pString = "FWPM_CONDITION_IP_LOCAL_INTERFACE"; + else if(fieldID == FWPS_FIELD_ALE_AUTH_CONNECT_V4_FLAGS) + { + pString = "FWPM_CONDITION_FLAGS"; + + if(pFormat) + *pFormat = FLAGS_VALUE; + } + else if(fieldID == FWPS_FIELD_ALE_AUTH_CONNECT_V4_INTERFACE_TYPE) + pString = "FWPM_CONDITION_INTERFACE_TYPE"; + else if(fieldID == FWPS_FIELD_ALE_AUTH_CONNECT_V4_TUNNEL_TYPE) + pString = "FWPM_CONDITION_TUNNEL_TYPE"; + +#if(NTDDI_VERSION >= NTDDI_VISTASP1) + + else if(fieldID == FWPS_FIELD_ALE_AUTH_CONNECT_V4_INTERFACE_INDEX) + pString = "FWPM_CONDITION_INTERFACE_INDEX"; + else if(fieldID == FWPS_FIELD_ALE_AUTH_CONNECT_V4_SUB_INTERFACE_INDEX) + pString = "FWPM_CONDITION_SUB_INTERFACE_INDEX"; + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + else if(fieldID == FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_ARRIVAL_INTERFACE) + pString = "FWPM_CONDITION_IP_ARRIVAL_INTERFACE"; + else if(fieldID == FWPS_FIELD_ALE_AUTH_CONNECT_V4_ARRIVAL_INTERFACE_TYPE) + pString = "FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE"; + else if(fieldID == FWPS_FIELD_ALE_AUTH_CONNECT_V4_ARRIVAL_TUNNEL_TYPE) + pString = "FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE"; + else if(fieldID == FWPS_FIELD_ALE_AUTH_CONNECT_V4_ARRIVAL_INTERFACE_INDEX) + pString = "FWPM_CONDITION_ARRIVAL_INTERFACE_INDEX"; + else if(fieldID == FWPS_FIELD_ALE_AUTH_CONNECT_V4_NEXTHOP_SUB_INTERFACE_INDEX) + pString = "FWPM_CONDITION_NEXTHOP_SUB_INTERFACE_INDEX"; + else if(fieldID == FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_NEXTHOP_INTERFACE) + pString = "FWPM_CONDITION_IP_NEXTHOP_INTERFACE"; + else if(fieldID == FWPS_FIELD_ALE_AUTH_CONNECT_V4_NEXTHOP_INTERFACE_TYPE) + pString = "FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE"; + else if(fieldID == FWPS_FIELD_ALE_AUTH_CONNECT_V4_NEXTHOP_TUNNEL_TYPE) + pString = "FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE"; + else if(fieldID == FWPS_FIELD_ALE_AUTH_CONNECT_V4_NEXTHOP_INTERFACE_INDEX) + pString = "FWPM_CONDITION_NEXTHOP_INTERFACE_INDEX"; + else if(fieldID == FWPS_FIELD_ALE_AUTH_CONNECT_V4_ORIGINAL_PROFILE_ID) + pString = "FWPM_CONDITION_ORIGINAL_PROFILE_ID"; + else if(fieldID == FWPS_FIELD_ALE_AUTH_CONNECT_V4_CURRENT_PROFILE_ID) + pString = "FWPM_CONDITION_CURRENT_PROFILE_ID"; + else if(fieldID == FWPS_FIELD_ALE_AUTH_CONNECT_V4_REAUTHORIZE_REASON) + pString = "FWPM_CONDITION_REAUTHORIZE_REASON"; + else if(fieldID == FWPS_FIELD_ALE_AUTH_CONNECT_V4_PEER_NAME) + pString = "FWPM_CONDITION_PEER_NAME"; + else if(fieldID == FWPS_FIELD_ALE_AUTH_CONNECT_V4_ORIGINAL_ICMP_TYPE) + pString = "FWPM_CONDITION_ORIGINAL_ICMP_TYPE"; + else if(fieldID == FWPS_FIELD_ALE_AUTH_CONNECT_V4_INTERFACE_QUARANTINE_EPOCH) + pString = "FWPM_CONDITION_INTERFACE_QUARANTINE_EPOCH"; + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + else if(fieldID == FWPS_FIELD_ALE_AUTH_CONNECT_V4_ALE_ORIGINAL_APP_ID) + { + pString = "FWPM_CONDITION_ALE_ORIGINAL_APP_ID"; + + if(pFormat) + *pFormat = UNICODE_STRING_VALUE; + } + else if(fieldID == FWPS_FIELD_ALE_AUTH_CONNECT_V4_ALE_PACKAGE_ID) + pString = "FWPM_CONDITION_ALE_PACKAGE_ID"; + +#if(NTDDI_VERSION >= NTDDI_WINTHRESHOLD) + + else if(fieldID == FWPS_FIELD_ALE_AUTH_CONNECT_V4_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE) + pString = "FWPM_CONDITION_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE"; + +#endif /// (NTDDI_VERSION >= NTDDI_WINTHRESHOLD) +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) +#endif /// (NTDDI_VERSION >= NTDDI_VISTASP1) + + break; + } + case FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4: + case FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4_DISCARD: + case FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6: + case FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6_DISCARD: + { + if(fieldID == FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_ALE_APP_ID) + { + pString = "FWPM_CONDITION_ALE_APP_ID"; + + if(pFormat) + *pFormat = UNICODE_STRING_VALUE; + } + else if(fieldID == FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_ALE_USER_ID) + pString = "FWPM_CONDITION_ALE_USER_ID"; + else if(fieldID == FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_LOCAL_ADDRESS) + { + pString = "FWPM_CONDITION_IP_LOCAL_ADDRESS"; + + if(pFormat) + *pFormat = ADDRESS_VALUE; + } + else if(fieldID == FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_LOCAL_ADDRESS_TYPE) + pString = "FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE"; + else if(fieldID == FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_LOCAL_PORT) + pString = "FWPM_CONDITION_IP_LOCAL_PORT"; + else if(fieldID == FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_PROTOCOL) + pString = "FWPM_CONDITION_IP_PROTOCOL"; + else if(fieldID == FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_REMOTE_ADDRESS) + { + pString = "FWPM_CONDITION_IP_REMOTE_ADDRESS"; + + if(pFormat) + *pFormat = ADDRESS_VALUE; + } + else if(fieldID == FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_REMOTE_PORT) + pString = "FWPM_CONDITION_IP_REMOTE_PORT"; + else if(fieldID == FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_ALE_REMOTE_USER_ID) + pString = "FWPM_CONDITION_ALE_REMOTE_USER_ID"; + else if(fieldID == FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_ALE_REMOTE_MACHINE_ID) + pString = "FWPM_CONDITION_ALE_REMOTE_MACHINE_ID"; + else if(fieldID == FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_DESTINATION_ADDRESS_TYPE) + pString = "FWPM_CONDITION_IP_DESTINATION_ADDRESS_TYPE"; + else if(fieldID == FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_LOCAL_INTERFACE) + pString = "FWPM_CONDITION_IP_LOCAL_INTERFACE"; + else if(fieldID == FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_DIRECTION) + pString = "FWPM_CONDITION_DIRECTION"; + else if(fieldID == FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_INTERFACE_TYPE) + pString = "FWPM_CONDITION_INTERFACE_TYPE"; + else if(fieldID == FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_TUNNEL_TYPE) + pString = "FWPM_CONDITION_TUNNEL_TYPE"; + +#if(NTDDI_VERSION >= NTDDI_VISTASP1) + + else if(fieldID == FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_FLAGS) + { + pString = "FWPM_CONDITION_FLAGS"; + + if(pFormat) + *pFormat = FLAGS_VALUE; + } + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + else if(fieldID == FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_ALE_ORIGINAL_APP_ID) + { + pString = "FWPM_CONDITION_ALE_ORIGINAL_APP_ID"; + + if(pFormat) + *pFormat = UNICODE_STRING_VALUE; + } + else if(fieldID == FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_ALE_PACKAGE_ID) + pString = "FWPM_CONDITION_ALE_PACKAGE_ID"; + +#if(NTDDI_VERSION >= NTDDI_WINTHRESHOLD) + + else if(fieldID == FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE) + pString = "FWPM_CONDITION_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE"; + +#endif /// (NTDDI_VERSION >= NTDDI_WINTHRESHOLD) +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) +#endif /// (NTDDI_VERSION >= NTDDI_VISTASP1) + + break; + } + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + case FWPS_LAYER_NAME_RESOLUTION_CACHE_V4: + case FWPS_LAYER_NAME_RESOLUTION_CACHE_V6: + { + if(fieldID == FWPS_FIELD_NAME_RESOLUTION_CACHE_V4_ALE_USER_ID) + pString = "FWPM_CONDITION_ALE_USER_ID"; + else if(fieldID == FWPS_FIELD_NAME_RESOLUTION_CACHE_V4_ALE_APP_ID) + { + pString = "FWPM_CONDITION_ALE_APP_ID"; + + if(pFormat) + *pFormat = UNICODE_STRING_VALUE; + } + else if(fieldID == FWPS_FIELD_NAME_RESOLUTION_CACHE_V4_IP_REMOTE_ADDRESS) + { + pString = "FWPM_CONDITION_IP_REMOTE_ADDRESS"; + + if(pFormat) + *pFormat = ADDRESS_VALUE; + } + else if(fieldID == FWPS_FIELD_NAME_RESOLUTION_CACHE_V4_PEER_NAME) + pString = "FWPM_CONDITION_PEER_NAME"; + + break; + } + case FWPS_LAYER_ALE_RESOURCE_RELEASE_V4: + case FWPS_LAYER_ALE_RESOURCE_RELEASE_V6: + { + if(fieldID == FWPS_FIELD_ALE_RESOURCE_RELEASE_V4_ALE_APP_ID) + { + pString = "FWPM_CONDITION_ALE_APP_ID"; + + if(pFormat) + *pFormat = UNICODE_STRING_VALUE; + } + else if(fieldID == FWPS_FIELD_ALE_RESOURCE_RELEASE_V4_ALE_USER_ID) + pString = "FWPM_CONDITION_ALE_USER_ID"; + else if(fieldID == FWPS_FIELD_ALE_RESOURCE_RELEASE_V4_IP_LOCAL_ADDRESS) + { + pString = "FWPM_CONDITION_IP_LOCAL_ADDRESS"; + + if(pFormat) + *pFormat = ADDRESS_VALUE; + } + else if(fieldID == FWPS_FIELD_ALE_RESOURCE_RELEASE_V4_IP_LOCAL_ADDRESS_TYPE) + pString = "FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE"; + else if(fieldID == FWPS_FIELD_ALE_RESOURCE_RELEASE_V4_IP_LOCAL_PORT) + pString = "FWPM_CONDITION_IP_LOCAL_PORT"; + else if(fieldID == FWPS_FIELD_ALE_RESOURCE_RELEASE_V4_IP_PROTOCOL) + pString = "FWPM_CONDITION_IP_PROTOCOL"; + else if(fieldID == FWPS_FIELD_ALE_RESOURCE_RELEASE_V4_IP_LOCAL_INTERFACE) + pString = "FWPM_CONDITION_IP_LOCAL_INTERFACE"; + else if(fieldID == FWPS_FIELD_ALE_RESOURCE_RELEASE_V4_FLAGS) + { + pString = "FWPM_CONDITION_FLAGS"; + + if(pFormat) + *pFormat = FLAGS_VALUE; + } + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + else if(fieldID == FWPS_FIELD_ALE_RESOURCE_RELEASE_V4_ALE_PACKAGE_ID) + pString = "FWPM_CONDITION_ALE_PACKAGE_ID"; + +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) + + break; + } + case FWPS_LAYER_ALE_ENDPOINT_CLOSURE_V4: + case FWPS_LAYER_ALE_ENDPOINT_CLOSURE_V6: + { + if(fieldID == FWPS_FIELD_ALE_ENDPOINT_CLOSURE_V4_ALE_APP_ID) + { + pString = "FWPM_CONDITION_ALE_APP_ID"; + + if(pFormat) + *pFormat = UNICODE_STRING_VALUE; + } + else if(fieldID == FWPS_FIELD_ALE_ENDPOINT_CLOSURE_V4_ALE_USER_ID) + pString = "FWPM_CONDITION_ALE_USER_ID"; + else if(fieldID == FWPS_FIELD_ALE_ENDPOINT_CLOSURE_V4_IP_LOCAL_ADDRESS) + { + pString = "FWPM_CONDITION_IP_LOCAL_ADDRESS"; + + if(pFormat) + *pFormat = ADDRESS_VALUE; + } + else if(fieldID == FWPS_FIELD_ALE_ENDPOINT_CLOSURE_V4_IP_LOCAL_ADDRESS_TYPE) + pString = "FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE"; + else if(fieldID == FWPS_FIELD_ALE_ENDPOINT_CLOSURE_V4_IP_LOCAL_PORT) + pString = "FWPM_CONDITION_IP_LOCAL_PORT"; + else if(fieldID == FWPS_FIELD_ALE_ENDPOINT_CLOSURE_V4_IP_PROTOCOL) + pString = "FWPM_CONDITION_IP_PROTOCOL"; + else if(fieldID == FWPS_FIELD_ALE_ENDPOINT_CLOSURE_V4_IP_REMOTE_ADDRESS) + { + pString = "FWPM_CONDITION_IP_REMOTE_ADDRESS"; + + if(pFormat) + *pFormat = ADDRESS_VALUE; + } + else if(fieldID == FWPS_FIELD_ALE_ENDPOINT_CLOSURE_V4_IP_REMOTE_PORT) + pString = "FWPM_CONDITION_IP_REMOTE_PORT"; + else if(fieldID == FWPS_FIELD_ALE_ENDPOINT_CLOSURE_V4_IP_LOCAL_INTERFACE) + pString = "FWPM_CONDITION_IP_LOCAL_INTERFACE"; + else if(fieldID == FWPS_FIELD_ALE_ENDPOINT_CLOSURE_V4_FLAGS) + { + pString = "FWPM_CONDITION_FLAGS"; + + if(pFormat) + *pFormat = FLAGS_VALUE; + } + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + else if(fieldID == FWPS_FIELD_ALE_ENDPOINT_CLOSURE_V4_ALE_PACKAGE_ID) + pString = "FWPM_CONDITION_ALE_PACKAGE_ID"; + +#if(NTDDI_VERSION >= NTDDI_WINTHRESHOLD) + + else if(fieldID == FWPS_FIELD_ALE_ENDPOINT_CLOSURE_V4_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE) + pString = "FWPM_CONDITION_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE"; + +#endif /// (NTDDI_VERSION >= NTDDI_WINTHRESHOLD) +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) + + break; + } + case FWPS_LAYER_ALE_CONNECT_REDIRECT_V4: + case FWPS_LAYER_ALE_CONNECT_REDIRECT_V6: + { + if(fieldID == FWPS_FIELD_ALE_CONNECT_REDIRECT_V4_ALE_APP_ID) + { + pString = "FWPM_CONDITION_ALE_APP_ID"; + + if(pFormat) + *pFormat = UNICODE_STRING_VALUE; + } + else if(fieldID == FWPS_FIELD_ALE_CONNECT_REDIRECT_V4_ALE_USER_ID) + pString = "FWPM_CONDITION_ALE_USER_ID"; + else if(fieldID == FWPS_FIELD_ALE_CONNECT_REDIRECT_V4_IP_LOCAL_ADDRESS) + { + pString = "FWPM_CONDITION_IP_LOCAL_ADDRESS"; + + if(pFormat) + *pFormat = ADDRESS_VALUE; + } + else if(fieldID == FWPS_FIELD_ALE_CONNECT_REDIRECT_V4_IP_LOCAL_ADDRESS_TYPE) + pString = "FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE"; + else if(fieldID == FWPS_FIELD_ALE_CONNECT_REDIRECT_V4_IP_LOCAL_PORT) + pString = "FWPM_CONDITION_IP_LOCAL_PORT"; + else if(fieldID == FWPS_FIELD_ALE_CONNECT_REDIRECT_V4_IP_PROTOCOL) + pString = "FWPM_CONDITION_IP_PROTOCOL"; + else if(fieldID == FWPS_FIELD_ALE_CONNECT_REDIRECT_V4_IP_REMOTE_ADDRESS) + { + pString = "FWPM_CONDITION_IP_REMOTE_ADDRESS"; + + if(pFormat) + *pFormat = ADDRESS_VALUE; + } + else if(fieldID == FWPS_FIELD_ALE_CONNECT_REDIRECT_V4_IP_DESTINATION_ADDRESS_TYPE) + pString = "FWPM_CONDITION_IP_DESTINATION_ADDRESS_TYPE"; + else if(fieldID == FWPS_FIELD_ALE_CONNECT_REDIRECT_V4_IP_REMOTE_PORT) + pString = "FWPM_CONDITION_IP_REMOTE_PORT"; + else if(fieldID == FWPS_FIELD_ALE_CONNECT_REDIRECT_V4_FLAGS) + { + pString = "FWPM_CONDITION_FLAGS"; + + if(pFormat) + *pFormat = FLAGS_VALUE; + } + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + else if(fieldID == FWPS_FIELD_ALE_CONNECT_REDIRECT_V4_ALE_ORIGINAL_APP_ID) + { + pString = "FWPM_CONDITION_ALE_ORIGINAL_APP_ID"; + + if(pFormat) + *pFormat = UNICODE_STRING_VALUE; + } + else if(fieldID == FWPS_FIELD_ALE_CONNECT_REDIRECT_V4_ALE_PACKAGE_ID) + pString = "FWPM_CONDITION_ALE_PACKAGE_ID"; + +#if(NTDDI_VERSION >= NTDDI_WINTHRESHOLD) + + else if(fieldID == FWPS_FIELD_ALE_CONNECT_REDIRECT_V4_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE) + pString = "FWPM_CONDITION_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE"; + +#endif /// (NTDDI_VERSION >= NTDDI_WINTHRESHOLD) +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) + + break; + } + case FWPS_LAYER_ALE_BIND_REDIRECT_V4: + case FWPS_LAYER_ALE_BIND_REDIRECT_V6: + { + if(fieldID == FWPS_FIELD_ALE_BIND_REDIRECT_V4_ALE_APP_ID) + { + pString = "FWPM_CONDITION_ALE_APP_ID"; + + if(pFormat) + *pFormat = UNICODE_STRING_VALUE; + } + else if(fieldID == FWPS_FIELD_ALE_BIND_REDIRECT_V4_ALE_USER_ID) + pString = "FWPM_CONDITION_ALE_USER_ID"; + else if(fieldID == FWPS_FIELD_ALE_BIND_REDIRECT_V4_IP_LOCAL_ADDRESS) + { + pString = "FWPM_CONDITION_IP_LOCAL_ADDRESS"; + + if(pFormat) + *pFormat = ADDRESS_VALUE; + } + else if(fieldID == FWPS_FIELD_ALE_BIND_REDIRECT_V4_IP_LOCAL_ADDRESS_TYPE) + pString = "FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE"; + else if(fieldID == FWPS_FIELD_ALE_BIND_REDIRECT_V4_IP_LOCAL_PORT) + pString = "FWPM_CONDITION_IP_LOCAL_PORT"; + else if(fieldID == FWPS_FIELD_ALE_BIND_REDIRECT_V4_IP_PROTOCOL) + pString = "FWPM_CONDITION_IP_PROTOCOL"; + else if(fieldID == FWPS_FIELD_ALE_BIND_REDIRECT_V4_FLAGS) + { + pString = "FWPM_CONDITION_FLAGS"; + + if(pFormat) + *pFormat = FLAGS_VALUE; + } + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + else if(fieldID == FWPS_FIELD_ALE_BIND_REDIRECT_V4_ALE_PACKAGE_ID) + pString = "FWPM_CONDITION_ALE_PACKAGE_ID"; + +#if(NTDDI_VERSION >= NTDDI_WINTHRESHOLD) + + else if(fieldID == FWPS_FIELD_ALE_BIND_REDIRECT_V4_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE) + pString = "FWPM_CONDITION_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE"; + +#endif /// (NTDDI_VERSION >= NTDDI_WINTHRESHOLD) +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) + + break; + } + case FWPS_LAYER_STREAM_PACKET_V4: + case FWPS_LAYER_STREAM_PACKET_V6: + { + if(fieldID == FWPS_FIELD_STREAM_PACKET_V4_IP_LOCAL_ADDRESS) + { + pString = "FWPM_CONDITION_IP_LOCAL_ADDRESS"; + + if(pFormat) + *pFormat = ADDRESS_VALUE; + } + else if(fieldID == FWPS_FIELD_STREAM_PACKET_V4_IP_REMOTE_ADDRESS) + { + pString = "FWPM_CONDITION_IP_REMOTE_ADDRESS"; + + if(pFormat) + *pFormat = ADDRESS_VALUE; + } + else if(fieldID == FWPS_FIELD_STREAM_PACKET_V4_IP_LOCAL_PORT) + pString = "FWPM_CONDITION_IP_LOCAL_PORT"; + else if(fieldID == FWPS_FIELD_STREAM_PACKET_V4_IP_REMOTE_PORT) + pString = "FWPM_CONDITION_IP_REMOTE_PORT"; + else if(fieldID == FWPS_FIELD_STREAM_PACKET_V4_IP_LOCAL_INTERFACE) + pString = "FWPM_CONDITION_IP_LOCAL_INTERFACE"; + else if(fieldID == FWPS_FIELD_STREAM_PACKET_V4_INTERFACE_INDEX) + pString = "FWPM_CONDITION_INTERFACE_INDEX"; + else if(fieldID == FWPS_FIELD_STREAM_PACKET_V4_SUB_INTERFACE_INDEX) + pString = "FWPM_CONDITION_SUB_INTERFACE_INDEX"; + else if(fieldID == FWPS_FIELD_STREAM_PACKET_V4_DIRECTION) + pString = "FWPM_CONDITION_DIRECTION"; + else if(fieldID == FWPS_FIELD_STREAM_PACKET_V4_FLAGS) + { + pString = "FWPM_CONDITION_FLAGS"; + + if(pFormat) + *pFormat = FLAGS_VALUE; + } + else if(fieldID == FWPS_FIELD_STREAM_PACKET_V4_INTERFACE_TYPE) + pString = "FWPM_CONDITION_INTERFACE_TYPE"; + else if(fieldID == FWPS_FIELD_STREAM_PACKET_V4_TUNNEL_TYPE) + pString = "FWPM_CONDITION_TUNNEL_TYPE"; + + break; + } + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + case FWPS_LAYER_INBOUND_MAC_FRAME_ETHERNET: + { + if(fieldID == FWPS_FIELD_INBOUND_MAC_FRAME_ETHERNET_INTERFACE_MAC_ADDRESS) + pString = "FWPM_CONDITION_INTERFACE_MAC_ADDRESS"; + else if(fieldID == FWPS_FIELD_INBOUND_MAC_FRAME_ETHERNET_MAC_LOCAL_ADDRESS) + pString = "FWPM_CONDITION_MAC_LOCAL_ADDRESS"; + else if(fieldID == FWPS_FIELD_INBOUND_MAC_FRAME_ETHERNET_MAC_REMOTE_ADDRESS) + pString = "FWPM_CONDITION_MAC_REMOTE_ADDRESS"; + else if(fieldID == FWPS_FIELD_INBOUND_MAC_FRAME_ETHERNET_MAC_LOCAL_ADDRESS_TYPE) + pString = "FWPM_CONDITION_MAC_LOCAL_ADDRESS_TYPE"; + else if(fieldID == FWPS_FIELD_INBOUND_MAC_FRAME_ETHERNET_MAC_REMOTE_ADDRESS_TYPE) + pString = "FWPM_CONDITION_MAC_REMOTE_ADDRESS_TYPE"; + else if(fieldID == FWPS_FIELD_INBOUND_MAC_FRAME_ETHERNET_ETHER_TYPE) + pString = "FWPM_CONDITION_ETHER_TYPE"; + else if(fieldID == FWPS_FIELD_INBOUND_MAC_FRAME_ETHERNET_VLAN_ID) + pString = "FWPM_CONDITION_VLAN_ID"; + else if(fieldID == FWPS_FIELD_INBOUND_MAC_FRAME_ETHERNET_INTERFACE) + pString = "FWPM_CONDITION_INTERFACE"; + else if(fieldID == FWPS_FIELD_INBOUND_MAC_FRAME_ETHERNET_INTERFACE_INDEX) + pString = "FWPM_CONDITION_INTERFACE_INDEX"; + else if(fieldID == FWPS_FIELD_INBOUND_MAC_FRAME_ETHERNET_NDIS_PORT) + pString = "FWPM_CONDITION_NDIS_PORT"; + else if(fieldID == FWPS_FIELD_INBOUND_MAC_FRAME_ETHERNET_L2_FLAGS) + { + pString = "FWPM_CONDITION_L2_FLAGS"; + + if(pFormat) + *pFormat = L2_FLAGS_VALUE; + } + + break; + } + case FWPS_LAYER_OUTBOUND_MAC_FRAME_ETHERNET: + { + if(fieldID == FWPS_FIELD_OUTBOUND_MAC_FRAME_ETHERNET_INTERFACE_MAC_ADDRESS) + pString = "FWPM_CONDITION_INTERFACE_MAC_ADDRESS"; + else if(fieldID == FWPS_FIELD_OUTBOUND_MAC_FRAME_ETHERNET_MAC_LOCAL_ADDRESS) + pString = "FWPM_CONDITION_MAC_LOCAL_ADDRESS"; + else if(fieldID == FWPS_FIELD_OUTBOUND_MAC_FRAME_ETHERNET_MAC_REMOTE_ADDRESS) + pString = "FWPM_CONDITION_MAC_REMOTE_ADDRESS"; + else if(fieldID == FWPS_FIELD_OUTBOUND_MAC_FRAME_ETHERNET_MAC_LOCAL_ADDRESS_TYPE) + pString = "FWPM_CONDITION_MAC_LOCAL_ADDRESS_TYPE"; + else if(fieldID == FWPS_FIELD_OUTBOUND_MAC_FRAME_ETHERNET_MAC_REMOTE_ADDRESS_TYPE) + pString = "FWPM_CONDITION_MAC_REMOTE_ADDRESS_TYPE"; + else if(fieldID == FWPS_FIELD_OUTBOUND_MAC_FRAME_ETHERNET_ETHER_TYPE) + pString = "FWPM_CONDITION_ETHER_TYPE"; + else if(fieldID == FWPS_FIELD_OUTBOUND_MAC_FRAME_ETHERNET_VLAN_ID) + pString = "FWPM_CONDITION_VLAN_ID"; + else if(fieldID == FWPS_FIELD_OUTBOUND_MAC_FRAME_ETHERNET_INTERFACE) + pString = "FWPM_CONDITION_INTERFACE"; + else if(fieldID == FWPS_FIELD_OUTBOUND_MAC_FRAME_ETHERNET_INTERFACE_INDEX) + pString = "FWPM_CONDITION_INTERFACE_INDEX"; + else if(fieldID == FWPS_FIELD_OUTBOUND_MAC_FRAME_ETHERNET_NDIS_PORT) + pString = "FWPM_CONDITION_NDIS_PORT"; + else if(fieldID == FWPS_FIELD_OUTBOUND_MAC_FRAME_ETHERNET_L2_FLAGS) + { + pString = "FWPM_CONDITION_L2_FLAGS"; + + if(pFormat) + *pFormat = L2_FLAGS_VALUE; + } + + break; + } + case FWPS_LAYER_INBOUND_MAC_FRAME_NATIVE: + { + if(fieldID == FWPS_FIELD_INBOUND_MAC_FRAME_NATIVE_NDIS_MEDIA_TYPE) + pString = "FWPM_CONDITION_NDIS_MEDIA_TYPE"; + else if(fieldID == FWPS_FIELD_INBOUND_MAC_FRAME_NATIVE_NDIS_PHYSICAL_MEDIA_TYPE) + pString = "FWPM_CONDITION_NDIS_PHYSICAL_MEDIA_TYPE"; + else if(fieldID == FWPS_FIELD_INBOUND_MAC_FRAME_NATIVE_INTERFACE) + pString = "FWPM_CONDITION_INTERFACE"; + else if(fieldID == FWPS_FIELD_INBOUND_MAC_FRAME_NATIVE_INTERFACE_TYPE) + pString = "FWPM_CONDITION_INTERFACE_TYPE"; + else if(fieldID == FWPS_FIELD_INBOUND_MAC_FRAME_NATIVE_INTERFACE_INDEX) + pString = "FWPM_CONDITION_INTERFACE_INDEX"; + else if(fieldID == FWPS_FIELD_INBOUND_MAC_FRAME_NATIVE_NDIS_PORT) + pString = "FWPM_CONDITION_NDIS_PORT"; + else if(fieldID == FWPS_FIELD_INBOUND_MAC_FRAME_NATIVE_L2_FLAGS) + { + pString = "FWPM_CONDITION_L2_FLAGS"; + + if(pFormat) + *pFormat = L2_FLAGS_VALUE; + } + + break; + } + case FWPS_LAYER_OUTBOUND_MAC_FRAME_NATIVE: + { + if(fieldID == FWPS_FIELD_OUTBOUND_MAC_FRAME_NATIVE_NDIS_MEDIA_TYPE) + pString = "FWPM_CONDITION_NDIS_MEDIA_TYPE"; + else if(fieldID == FWPS_FIELD_OUTBOUND_MAC_FRAME_NATIVE_NDIS_PHYSICAL_MEDIA_TYPE) + pString = "FWPM_CONDITION_NDIS_PHYSICAL_MEDIA_TYPE"; + else if(fieldID == FWPS_FIELD_OUTBOUND_MAC_FRAME_NATIVE_INTERFACE) + pString = "FWPM_CONDITION_INTERFACE"; + else if(fieldID == FWPS_FIELD_OUTBOUND_MAC_FRAME_NATIVE_INTERFACE_TYPE) + pString = "FWPM_CONDITION_INTERFACE_TYPE"; + else if(fieldID == FWPS_FIELD_OUTBOUND_MAC_FRAME_NATIVE_INTERFACE_INDEX) + pString = "FWPM_CONDITION_INTERFACE_INDEX"; + else if(fieldID == FWPS_FIELD_OUTBOUND_MAC_FRAME_NATIVE_NDIS_PORT) + pString = "FWPM_CONDITION_NDIS_PORT"; + else if(fieldID == FWPS_FIELD_OUTBOUND_MAC_FRAME_NATIVE_L2_FLAGS) + { + pString = "FWPM_CONDITION_L2_FLAGS"; + + if(pFormat) + *pFormat = L2_FLAGS_VALUE; + } + + break; + } + case FWPS_LAYER_INGRESS_VSWITCH_ETHERNET: + { + if(fieldID == FWPS_FIELD_INGRESS_VSWITCH_ETHERNET_MAC_SOURCE_ADDRESS) + pString = "FWPS_FIELD_INGRESS_VSWITCH_ETHERNET_MAC_SOURCE_ADDRESS"; + else if(fieldID == FWPS_FIELD_INGRESS_VSWITCH_ETHERNET_MAC_SOURCE_ADDRESS_TYPE) + pString = "FWPS_FIELD_INGRESS_VSWITCH_ETHERNET_MAC_SOURCE_ADDRESS_TYPE"; + else if(fieldID == FWPS_FIELD_INGRESS_VSWITCH_ETHERNET_MAC_DESTINATION_ADDRESS) + pString = "FWPS_FIELD_INGRESS_VSWITCH_ETHERNET_MAC_DESTINATION_ADDRESS"; + else if(fieldID == FWPS_FIELD_INGRESS_VSWITCH_ETHERNET_MAC_DESTINATION_ADDRESS_TYPE) + pString = "FWPS_FIELD_INGRESS_VSWITCH_ETHERNET_MAC_DESTINATION_ADDRESS_TYPE"; + else if(fieldID == FWPS_FIELD_INGRESS_VSWITCH_ETHERNET_ETHER_TYPE) + pString = "FWPS_FIELD_INGRESS_VSWITCH_ETHERNET_ETHER_TYPE"; + else if(fieldID == FWPS_FIELD_INGRESS_VSWITCH_ETHERNET_VLAN_ID) + pString = "FWPS_FIELD_INGRESS_VSWITCH_ETHERNET_VLAN_ID"; + else if(fieldID == FWPS_FIELD_INGRESS_VSWITCH_ETHERNET_VSWITCH_TENANT_NETWORK_ID) + pString = "FWPS_FIELD_INGRESS_VSWITCH_ETHERNET_VSWITCH_TENANT_NETWORK_ID"; + else if(fieldID == FWPS_FIELD_INGRESS_VSWITCH_ETHERNET_VSWITCH_ID) + pString = "FWPS_FIELD_INGRESS_VSWITCH_ETHERNET_VSWITCH_ID"; + else if(fieldID == FWPS_FIELD_INGRESS_VSWITCH_ETHERNET_VSWITCH_NETWORK_TYPE) + pString = "FWPS_FIELD_INGRESS_VSWITCH_ETHERNET_VSWITCH_NETWORK_TYPE"; + else if(fieldID == FWPS_FIELD_INGRESS_VSWITCH_ETHERNET_VSWITCH_SOURCE_INTERFACE_ID) + pString = "FWPS_FIELD_INGRESS_VSWITCH_ETHERNET_VSWITCH_SOURCE_INTERFACE_ID"; + else if(fieldID == FWPS_FIELD_INGRESS_VSWITCH_ETHERNET_VSWITCH_SOURCE_INTERFACE_TYPE) + pString = "FWPS_FIELD_INGRESS_VSWITCH_ETHERNET_VSWITCH_SOURCE_INTERFACE_TYPE"; + else if(fieldID == FWPS_FIELD_INGRESS_VSWITCH_ETHERNET_VSWITCH_SOURCE_VM_ID) + pString = "FWPS_FIELD_INGRESS_VSWITCH_ETHERNET_VSWITCH_SOURCE_VM_ID"; + else if(fieldID == FWPS_FIELD_INGRESS_VSWITCH_ETHERNET_L2_FLAGS) + pString = "FWPS_FIELD_INGRESS_VSWITCH_ETHERNET_L2_FLAGS"; + + break; + } + case FWPS_LAYER_EGRESS_VSWITCH_ETHERNET: + { + if(fieldID == FWPS_FIELD_EGRESS_VSWITCH_ETHERNET_MAC_SOURCE_ADDRESS) + pString = "FWPS_FIELD_EGRESS_VSWITCH_ETHERNET_MAC_SOURCE_ADDRESS"; + else if(fieldID == FWPS_FIELD_EGRESS_VSWITCH_ETHERNET_MAC_SOURCE_ADDRESS_TYPE) + pString = "FWPS_FIELD_EGRESS_VSWITCH_ETHERNET_MAC_SOURCE_ADDRESS_TYPE"; + else if(fieldID == FWPS_FIELD_EGRESS_VSWITCH_ETHERNET_MAC_DESTINATION_ADDRESS) + pString = "FWPS_FIELD_EGRESS_VSWITCH_ETHERNET_MAC_DESTINATION_ADDRESS"; + else if(fieldID == FWPS_FIELD_EGRESS_VSWITCH_ETHERNET_MAC_DESTINATION_ADDRESS_TYPE) + pString = "FWPS_FIELD_EGRESS_VSWITCH_ETHERNET_MAC_DESTINATION_ADDRESS_TYPE"; + else if(fieldID == FWPS_FIELD_EGRESS_VSWITCH_ETHERNET_ETHER_TYPE) + pString = "FWPS_FIELD_EGRESS_VSWITCH_ETHERNET_ETHER_TYPE"; + else if(fieldID == FWPS_FIELD_EGRESS_VSWITCH_ETHERNET_VLAN_ID) + pString = "FWPS_FIELD_EGRESS_VSWITCH_ETHERNET_VLAN_ID"; + else if(fieldID == FWPS_FIELD_EGRESS_VSWITCH_ETHERNET_VSWITCH_TENANT_NETWORK_ID) + pString = "FWPS_FIELD_EGRESS_VSWITCH_ETHERNET_VSWITCH_TENANT_NETWORK_ID"; + else if(fieldID == FWPS_FIELD_EGRESS_VSWITCH_ETHERNET_VSWITCH_ID) + pString = "FWPS_FIELD_EGRESS_VSWITCH_ETHERNET_VSWITCH_ID"; + else if(fieldID == FWPS_FIELD_EGRESS_VSWITCH_ETHERNET_VSWITCH_NETWORK_TYPE) + pString = "FWPS_FIELD_EGRESS_VSWITCH_ETHERNET_VSWITCH_NETWORK_TYPE"; + else if(fieldID == FWPS_FIELD_EGRESS_VSWITCH_ETHERNET_VSWITCH_SOURCE_INTERFACE_ID) + pString = "FWPS_FIELD_EGRESS_VSWITCH_ETHERNET_VSWITCH_SOURCE_INTERFACE_ID"; + else if(fieldID == FWPS_FIELD_EGRESS_VSWITCH_ETHERNET_VSWITCH_SOURCE_INTERFACE_TYPE) + pString = "FWPS_FIELD_EGRESS_VSWITCH_ETHERNET_VSWITCH_SOURCE_INTERFACE_TYPE"; + else if(fieldID == FWPS_FIELD_EGRESS_VSWITCH_ETHERNET_VSWITCH_SOURCE_VM_ID) + pString = "FWPS_FIELD_EGRESS_VSWITCH_ETHERNET_VSWITCH_SOURCE_VM_ID"; + else if(fieldID == FWPS_FIELD_EGRESS_VSWITCH_ETHERNET_VSWITCH_DESTINATION_INTERFACE_ID) + pString = "FWPS_FIELD_EGRESS_VSWITCH_ETHERNET_VSWITCH_DESTINATION_INTERFACE_ID"; + else if(fieldID == FWPS_FIELD_EGRESS_VSWITCH_ETHERNET_VSWITCH_DESTINATION_INTERFACE_TYPE) + pString = "FWPS_FIELD_EGRESS_VSWITCH_ETHERNET_VSWITCH_DESTINATION_INTERFACE_TYPE"; + else if(fieldID == FWPS_FIELD_EGRESS_VSWITCH_ETHERNET_VSWITCH_DESTINATION_VM_ID) + pString = "FWPS_FIELD_EGRESS_VSWITCH_ETHERNET_VSWITCH_DESTINATION_VM_ID"; + else if(fieldID == FWPS_FIELD_EGRESS_VSWITCH_ETHERNET_L2_FLAGS) + pString = "FWPS_FIELD_EGRESS_VSWITCH_ETHERNET_L2_FLAGS"; + + break; + } + case FWPS_LAYER_INGRESS_VSWITCH_TRANSPORT_V4: + case FWPS_LAYER_INGRESS_VSWITCH_TRANSPORT_V6: + { + if(fieldID == FWPS_FIELD_INGRESS_VSWITCH_TRANSPORT_V4_IP_SOURCE_ADDRESS) + { + pString = "FWPM_CONDITION_IP_SOURCE_ADDRESS"; + + if(pFormat) + *pFormat = ADDRESS_VALUE; + } + else if(fieldID == FWPS_FIELD_INGRESS_VSWITCH_TRANSPORT_V4_IP_DESTINATION_ADDRESS) + { + pString = "FWPM_CONDITION_IP_DESTINATION_ADDRESS"; + + if(pFormat) + *pFormat = ADDRESS_VALUE; + } + else if(fieldID == FWPS_FIELD_INGRESS_VSWITCH_TRANSPORT_V4_IP_PROTOCOL) + pString = "FWPM_CONDITION_IP_PROTOCOL"; + else if(fieldID == FWPS_FIELD_INGRESS_VSWITCH_TRANSPORT_V4_IP_SOURCE_PORT) + pString = "FWPM_CONDITION_IP_SOURCE_PORT"; + else if(fieldID == FWPS_FIELD_INGRESS_VSWITCH_TRANSPORT_V4_IP_DESTINATION_PORT) + pString = "FWPM_CONDITION_IP_DESTINATION_PORT"; + else if(fieldID == FWPS_FIELD_INGRESS_VSWITCH_TRANSPORT_V4_VLAN_ID) + pString = "FWPM_CONDITION_VLAN_ID"; + else if(fieldID == FWPS_FIELD_INGRESS_VSWITCH_TRANSPORT_V4_VSWITCH_TENANT_NETWORK_ID) + pString = "FWPM_CONDITION_VSWITCH_TENANT_NETWORK_ID"; + else if(fieldID == FWPS_FIELD_INGRESS_VSWITCH_TRANSPORT_V4_VSWITCH_ID) + pString = "FWPM_CONDITION_VSWITCH_ID"; + else if(fieldID == FWPS_FIELD_INGRESS_VSWITCH_TRANSPORT_V4_VSWITCH_NETWORK_TYPE) + pString = "FWPM_CONDITION_VSWITCH_NETWORK_TYPE"; + else if(fieldID == FWPS_FIELD_INGRESS_VSWITCH_TRANSPORT_V4_VSWITCH_SOURCE_INTERFACE_ID) + pString = "FWPM_CONDITION_VSWITCH_SOURCE_INTERFACE_ID"; + else if(fieldID == FWPS_FIELD_INGRESS_VSWITCH_TRANSPORT_V4_VSWITCH_SOURCE_INTERFACE_TYPE) + pString = "FWPM_CONDITION_VSWITCH_SOURCE_INTERFACE_TYPE"; + else if(fieldID == FWPS_FIELD_INGRESS_VSWITCH_TRANSPORT_V4_VSWITCH_SOURCE_VM_ID) + pString = "FWPM_CONDITION_VSWITCH_SOURCE_VM_ID"; + else if(fieldID == FWPS_FIELD_INGRESS_VSWITCH_TRANSPORT_V4_L2_FLAGS) + { + pString = "FWPM_CONDITION_L2_FLAGS"; + + if(pFormat) + *pFormat = L2_FLAGS_VALUE; + } + + break; + } + case FWPS_LAYER_EGRESS_VSWITCH_TRANSPORT_V4: + case FWPS_LAYER_EGRESS_VSWITCH_TRANSPORT_V6: + { + if(fieldID == FWPS_FIELD_EGRESS_VSWITCH_TRANSPORT_V4_IP_SOURCE_ADDRESS) + { + pString = "FWPM_CONDITION_IP_SOURCE_ADDRESS"; + + if(pFormat) + *pFormat = ADDRESS_VALUE; + } + else if(fieldID == FWPS_FIELD_EGRESS_VSWITCH_TRANSPORT_V4_IP_DESTINATION_ADDRESS) + { + pString = "FWPM_CONDITION_IP_DESTINATION_ADDRESS"; + + if(pFormat) + *pFormat = ADDRESS_VALUE; + } + else if(fieldID == FWPS_FIELD_EGRESS_VSWITCH_TRANSPORT_V4_IP_PROTOCOL) + pString = "FWPM_CONDITION_IP_PROTOCOL"; + else if(fieldID == FWPS_FIELD_EGRESS_VSWITCH_TRANSPORT_V4_IP_SOURCE_PORT) + pString = "FWPM_CONDITION_IP_SOURCE_PORT"; + else if(fieldID == FWPS_FIELD_EGRESS_VSWITCH_TRANSPORT_V4_IP_DESTINATION_PORT) + pString = "FWPM_CONDITION_IP_DESTINATION_PORT"; + else if(fieldID == FWPS_FIELD_EGRESS_VSWITCH_TRANSPORT_V4_VLAN_ID) + pString = "FWPM_CONDITION_VLAN_ID"; + else if(fieldID == FWPS_FIELD_EGRESS_VSWITCH_TRANSPORT_V4_VSWITCH_TENANT_NETWORK_ID) + pString = "FWPM_CONDITION_VSWITCH_TENANT_NETWORK_ID"; + else if(fieldID == FWPS_FIELD_EGRESS_VSWITCH_TRANSPORT_V4_VSWITCH_ID) + pString = "FWPM_CONDITION_VSWITCH_ID"; + else if(fieldID == FWPS_FIELD_EGRESS_VSWITCH_TRANSPORT_V4_VSWITCH_NETWORK_TYPE) + pString = "FWPM_CONDITION_VSWITCH_NETWORK_TYPE"; + else if(fieldID == FWPS_FIELD_EGRESS_VSWITCH_TRANSPORT_V4_VSWITCH_SOURCE_INTERFACE_ID) + pString = "FWPM_CONDITION_VSWITCH_SOURCE_INTERFACE_ID"; + else if(fieldID == FWPS_FIELD_EGRESS_VSWITCH_TRANSPORT_V4_VSWITCH_SOURCE_INTERFACE_TYPE) + pString = "FWPM_CONDITION_VSWITCH_SOURCE_INTERFACE_TYPE"; + else if(fieldID == FWPS_FIELD_EGRESS_VSWITCH_TRANSPORT_V4_VSWITCH_SOURCE_VM_ID) + pString = "FWPM_CONDITION_VSWITCH_SOURCE_VM_ID"; + else if(fieldID == FWPS_FIELD_EGRESS_VSWITCH_TRANSPORT_V4_VSWITCH_DESTINATION_INTERFACE_ID) + pString = "FWPM_CONDITION_VSWITCH_DESTINATION_INTERFACE_ID"; + else if(fieldID == FWPS_FIELD_EGRESS_VSWITCH_TRANSPORT_V4_VSWITCH_DESTINATION_INTERFACE_TYPE) + pString = "FWPM_CONDITION_VSWITCH_DESTINATION_INTERFACE_TYPE"; + else if(fieldID == FWPS_FIELD_EGRESS_VSWITCH_TRANSPORT_V4_VSWITCH_DESTINATION_VM_ID) + pString = "FWPM_CONDITION_VSWITCH_DESTINATION_VM_ID"; + else if(fieldID == FWPS_FIELD_EGRESS_VSWITCH_TRANSPORT_V4_L2_FLAGS) + { + pString = "FWPM_CONDITION_L2_FLAGS"; + + if(pFormat) + *pFormat = L2_FLAGS_VALUE; + } + + break; + } + +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + } + + return pString; +} + + +/** + @kernel_helper_function="KrnlHlprFwpValueGetFromFwpsIncomingValues" + + Purpose: Retrieve a pointer to the FWP_VALUE for a given condition in the + FWPS_INCOMING_VALUES.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552450.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windwos/Hardware/FF549939.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Must_inspect_result_ +_Success_(return != 0) +FWP_VALUE* KrnlHlprFwpValueGetFromFwpsIncomingValues(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const GUID* pConditionKey) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpValueGetFromFwpsIncomingValues()\n"); + +#endif /// DBG + + NT_ASSERT(pClassifyValues); + NT_ASSERT(pConditionKey); + + FWP_VALUE* pValue = 0; + + switch(pClassifyValues->layerId) + { + case FWPS_LAYER_INBOUND_IPPACKET_V4: + case FWPS_LAYER_INBOUND_IPPACKET_V4_DISCARD: + case FWPS_LAYER_INBOUND_IPPACKET_V6: + case FWPS_LAYER_INBOUND_IPPACKET_V6_DISCARD: + { + if(pConditionKey == &FWPM_CONDITION_IP_LOCAL_ADDRESS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_IPPACKET_V4_IP_LOCAL_ADDRESS].value); + else if(pConditionKey == &FWPM_CONDITION_IP_REMOTE_ADDRESS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_IPPACKET_V4_IP_REMOTE_ADDRESS].value); + else if(pConditionKey == &FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_IPPACKET_V4_IP_LOCAL_ADDRESS_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_IP_LOCAL_INTERFACE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_IPPACKET_V4_IP_LOCAL_INTERFACE].value); + else if(pConditionKey == &FWPM_CONDITION_INTERFACE_INDEX) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_IPPACKET_V4_INTERFACE_INDEX].value); + else if(pConditionKey == &FWPM_CONDITION_SUB_INTERFACE_INDEX) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_IPPACKET_V4_SUB_INTERFACE_INDEX].value); + else if(pConditionKey == &FWPM_CONDITION_FLAGS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_IPPACKET_V4_FLAGS].value); + else if(pConditionKey == &FWPM_CONDITION_INTERFACE_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_IPPACKET_V4_INTERFACE_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_TUNNEL_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_IPPACKET_V4_TUNNEL_TYPE].value); + + break; + } + case FWPS_LAYER_OUTBOUND_IPPACKET_V4: + case FWPS_LAYER_OUTBOUND_IPPACKET_V4_DISCARD: + case FWPS_LAYER_OUTBOUND_IPPACKET_V6: + case FWPS_LAYER_OUTBOUND_IPPACKET_V6_DISCARD: + { + if(pConditionKey == &FWPM_CONDITION_IP_LOCAL_ADDRESS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_IPPACKET_V4_IP_LOCAL_ADDRESS].value); + else if(pConditionKey == &FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_IPPACKET_V4_IP_LOCAL_ADDRESS_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_IP_REMOTE_ADDRESS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_IPPACKET_V4_IP_REMOTE_ADDRESS].value); + else if(pConditionKey == &FWPM_CONDITION_IP_LOCAL_INTERFACE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_IPPACKET_V4_IP_LOCAL_INTERFACE].value); + else if(pConditionKey == &FWPM_CONDITION_INTERFACE_INDEX) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_IPPACKET_V4_INTERFACE_INDEX].value); + else if(pConditionKey == &FWPM_CONDITION_SUB_INTERFACE_INDEX) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_IPPACKET_V4_SUB_INTERFACE_INDEX].value); + else if(pConditionKey == &FWPM_CONDITION_FLAGS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_IPPACKET_V4_FLAGS].value); + else if(pConditionKey == &FWPM_CONDITION_INTERFACE_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_IPPACKET_V4_INTERFACE_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_TUNNEL_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_IPPACKET_V4_TUNNEL_TYPE].value); + + break; + } + case FWPS_LAYER_IPFORWARD_V4: + case FWPS_LAYER_IPFORWARD_V4_DISCARD: + case FWPS_LAYER_IPFORWARD_V6: + case FWPS_LAYER_IPFORWARD_V6_DISCARD: + { + if(pConditionKey == &FWPM_CONDITION_IP_SOURCE_ADDRESS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_IPFORWARD_V4_IP_SOURCE_ADDRESS].value); + else if(pConditionKey == &FWPM_CONDITION_IP_DESTINATION_ADDRESS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_IPFORWARD_V4_IP_DESTINATION_ADDRESS].value); + else if(pConditionKey == &FWPM_CONDITION_IP_DESTINATION_ADDRESS_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_IPFORWARD_V4_IP_DESTINATION_ADDRESS_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_IP_LOCAL_INTERFACE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_IPFORWARD_V4_IP_LOCAL_INTERFACE].value); + else if(pConditionKey == &FWPM_CONDITION_IP_FORWARD_INTERFACE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_IPFORWARD_V4_IP_FORWARD_INTERFACE].value); + else if(pConditionKey == &FWPM_CONDITION_SOURCE_INTERFACE_INDEX) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_IPFORWARD_V4_SOURCE_INTERFACE_INDEX].value); + else if(pConditionKey == &FWPM_CONDITION_SOURCE_SUB_INTERFACE_INDEX) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_IPFORWARD_V4_SOURCE_SUB_INTERFACE_INDEX].value); + else if(pConditionKey == &FWPM_CONDITION_DESTINATION_INTERFACE_INDEX) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_IPFORWARD_V4_DESTINATION_INTERFACE_INDEX].value); + else if(pConditionKey == &FWPM_CONDITION_DESTINATION_SUB_INTERFACE_INDEX) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_IPFORWARD_V4_DESTINATION_SUB_INTERFACE_INDEX].value); + else if(pConditionKey == &FWPM_CONDITION_FLAGS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_IPFORWARD_V4_FLAGS].value); + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + else if(pConditionKey == &FWPM_CONDITION_IP_PHYSICAL_ARRIVAL_INTERFACE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_IPFORWARD_V4_IP_PHYSICAL_ARRIVAL_INTERFACE].value); + else if(pConditionKey == &FWPM_CONDITION_ARRIVAL_INTERFACE_PROFILE_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_IPFORWARD_V4_ARRIVAL_INTERFACE_PROFILE_ID].value); + else if(pConditionKey == &FWPM_CONDITION_IP_PHYSICAL_NEXTHOP_INTERFACE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_IPFORWARD_V4_IP_PHYSICAL_NEXTHOP_INTERFACE].value); + else if(pConditionKey == &FWPM_CONDITION_NEXTHOP_INTERFACE_PROFILE_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_IPFORWARD_V4_NEXTHOP_INTERFACE_PROFILE_ID].value); + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + break; + } + case FWPS_LAYER_INBOUND_TRANSPORT_V4: + case FWPS_LAYER_INBOUND_TRANSPORT_V4_DISCARD: + case FWPS_LAYER_INBOUND_TRANSPORT_V6: + case FWPS_LAYER_INBOUND_TRANSPORT_V6_DISCARD: + { + if(pConditionKey == &FWPM_CONDITION_IP_PROTOCOL) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_TRANSPORT_V4_IP_PROTOCOL].value); + else if(pConditionKey == &FWPM_CONDITION_IP_LOCAL_ADDRESS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_TRANSPORT_V4_IP_LOCAL_ADDRESS].value); + else if(pConditionKey == &FWPM_CONDITION_IP_REMOTE_ADDRESS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_TRANSPORT_V4_IP_REMOTE_ADDRESS].value); + else if(pConditionKey == &FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_TRANSPORT_V4_IP_LOCAL_ADDRESS_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_IP_LOCAL_PORT) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_TRANSPORT_V4_IP_LOCAL_PORT].value); + else if(pConditionKey == &FWPM_CONDITION_IP_REMOTE_PORT) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_TRANSPORT_V4_IP_REMOTE_PORT].value); + else if(pConditionKey == &FWPM_CONDITION_IP_LOCAL_INTERFACE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_TRANSPORT_V4_IP_LOCAL_INTERFACE].value); + else if(pConditionKey == &FWPM_CONDITION_INTERFACE_INDEX) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_TRANSPORT_V4_INTERFACE_INDEX].value); + else if(pConditionKey == &FWPM_CONDITION_SUB_INTERFACE_INDEX) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_TRANSPORT_V4_SUB_INTERFACE_INDEX].value); + else if(pConditionKey == &FWPM_CONDITION_FLAGS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_TRANSPORT_V4_FLAGS].value); + else if(pConditionKey == &FWPM_CONDITION_INTERFACE_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_TRANSPORT_V4_INTERFACE_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_TUNNEL_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_TRANSPORT_V4_TUNNEL_TYPE].value); + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + else if(pConditionKey == &FWPM_CONDITION_CURRENT_PROFILE_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_TRANSPORT_V4_PROFILE_ID].value); + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + break; + } + case FWPS_LAYER_OUTBOUND_TRANSPORT_V4: + case FWPS_LAYER_OUTBOUND_TRANSPORT_V4_DISCARD: + case FWPS_LAYER_OUTBOUND_TRANSPORT_V6: + case FWPS_LAYER_OUTBOUND_TRANSPORT_V6_DISCARD: + { + if(pConditionKey == &FWPM_CONDITION_IP_PROTOCOL) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_TRANSPORT_V4_IP_PROTOCOL].value); + else if(pConditionKey == &FWPM_CONDITION_IP_LOCAL_ADDRESS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_TRANSPORT_V4_IP_LOCAL_ADDRESS].value); + else if(pConditionKey == &FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_TRANSPORT_V4_IP_LOCAL_ADDRESS_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_IP_REMOTE_ADDRESS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_TRANSPORT_V4_IP_REMOTE_ADDRESS].value); + else if(pConditionKey == &FWPM_CONDITION_IP_LOCAL_PORT) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_TRANSPORT_V4_IP_LOCAL_PORT].value); + else if(pConditionKey == &FWPM_CONDITION_IP_REMOTE_PORT) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_TRANSPORT_V4_IP_REMOTE_PORT].value); + else if(pConditionKey == &FWPM_CONDITION_IP_LOCAL_INTERFACE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_TRANSPORT_V4_IP_LOCAL_INTERFACE].value); + else if(pConditionKey == &FWPM_CONDITION_INTERFACE_INDEX) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_TRANSPORT_V4_INTERFACE_INDEX].value); + else if(pConditionKey == &FWPM_CONDITION_SUB_INTERFACE_INDEX) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_TRANSPORT_V4_SUB_INTERFACE_INDEX].value); + else if(pConditionKey == &FWPM_CONDITION_IP_DESTINATION_ADDRESS_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_TRANSPORT_V4_IP_DESTINATION_ADDRESS_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_FLAGS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_TRANSPORT_V4_FLAGS].value); + else if(pConditionKey == &FWPM_CONDITION_INTERFACE_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_TRANSPORT_V4_INTERFACE_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_TUNNEL_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_TRANSPORT_V4_TUNNEL_TYPE].value); + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + else if(pConditionKey == &FWPM_CONDITION_CURRENT_PROFILE_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_TRANSPORT_V4_PROFILE_ID].value); + +#if(NTDDI_VERSION >= NTDDI_WINTHRESHOLD) + + else if(pConditionKey == &FWPM_CONDITION_IPSEC_SECURITY_REALM_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_TRANSPORT_V4_IPSEC_SECURITY_REALM_ID].value); + +#endif /// (NTDDI_VERSION >= NTDDI_WINTHRESHOLD) +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + break; + } + case FWPS_LAYER_STREAM_V4: + case FWPS_LAYER_STREAM_V4_DISCARD: + case FWPS_LAYER_STREAM_V6: + case FWPS_LAYER_STREAM_V6_DISCARD: + { + if(pConditionKey == &FWPM_CONDITION_IP_LOCAL_ADDRESS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_STREAM_V4_IP_LOCAL_ADDRESS].value); + else if(pConditionKey == &FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_STREAM_V4_IP_LOCAL_ADDRESS_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_IP_REMOTE_ADDRESS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_STREAM_V4_IP_REMOTE_ADDRESS].value); + else if(pConditionKey == &FWPM_CONDITION_IP_LOCAL_PORT) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_STREAM_V4_IP_LOCAL_PORT].value); + else if(pConditionKey == &FWPM_CONDITION_IP_REMOTE_PORT) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_STREAM_V4_IP_REMOTE_PORT].value); + else if(pConditionKey == &FWPM_CONDITION_DIRECTION) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_STREAM_V4_DIRECTION].value); + +#if(NTDDI_VERSION >= NTDDI_VISTASP1) + + else if(pConditionKey == &FWPM_CONDITION_FLAGS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_STREAM_V4_FLAGS].value); + +#endif /// (NTDDI_VERSION >= NTDDI_VISTASP1) + + break; + } + case FWPS_LAYER_DATAGRAM_DATA_V4: + case FWPS_LAYER_DATAGRAM_DATA_V4_DISCARD: + case FWPS_LAYER_DATAGRAM_DATA_V6: + case FWPS_LAYER_DATAGRAM_DATA_V6_DISCARD: + { + if(pConditionKey == &FWPM_CONDITION_IP_PROTOCOL) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V4_IP_PROTOCOL].value); + else if(pConditionKey == &FWPM_CONDITION_IP_LOCAL_ADDRESS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V4_IP_LOCAL_ADDRESS].value); + else if(pConditionKey == &FWPM_CONDITION_IP_REMOTE_ADDRESS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V4_IP_REMOTE_ADDRESS].value); + else if(pConditionKey == &FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V4_IP_LOCAL_ADDRESS_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_IP_LOCAL_PORT) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V4_IP_LOCAL_PORT].value); + else if(pConditionKey == &FWPM_CONDITION_IP_REMOTE_PORT) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V4_IP_REMOTE_PORT].value); + else if(pConditionKey == &FWPM_CONDITION_IP_LOCAL_INTERFACE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V4_IP_LOCAL_INTERFACE].value); + else if(pConditionKey == &FWPM_CONDITION_INTERFACE_INDEX) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V4_INTERFACE_INDEX].value); + else if(pConditionKey == &FWPM_CONDITION_SUB_INTERFACE_INDEX) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V4_SUB_INTERFACE_INDEX].value); + else if(pConditionKey == &FWPM_CONDITION_DIRECTION) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V4_DIRECTION].value); + else if(pConditionKey == &FWPM_CONDITION_FLAGS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V4_FLAGS].value); + else if(pConditionKey == &FWPM_CONDITION_INTERFACE_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V4_INTERFACE_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_TUNNEL_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V4_TUNNEL_TYPE].value); + + break; + } + case FWPS_LAYER_INBOUND_ICMP_ERROR_V4: + case FWPS_LAYER_INBOUND_ICMP_ERROR_V4_DISCARD: + case FWPS_LAYER_INBOUND_ICMP_ERROR_V6: + case FWPS_LAYER_INBOUND_ICMP_ERROR_V6_DISCARD: + { + if(pConditionKey == &FWPM_CONDITION_EMBEDDED_PROTOCOL) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_ICMP_ERROR_V4_EMBEDDED_PROTOCOL].value); + else if(pConditionKey == &FWPM_CONDITION_IP_LOCAL_ADDRESS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_ICMP_ERROR_V4_IP_LOCAL_ADDRESS].value); + else if(pConditionKey == &FWPM_CONDITION_IP_REMOTE_ADDRESS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_ICMP_ERROR_V4_IP_REMOTE_ADDRESS].value); + else if(pConditionKey == &FWPM_CONDITION_EMBEDDED_REMOTE_ADDRESS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_ICMP_ERROR_V4_EMBEDDED_REMOTE_ADDRESS].value); + else if(pConditionKey == &FWPM_CONDITION_EMBEDDED_LOCAL_ADDRESS_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_ICMP_ERROR_V4_EMBEDDED_LOCAL_ADDRESS_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_EMBEDDED_LOCAL_PORT) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_ICMP_ERROR_V4_EMBEDDED_LOCAL_PORT].value); + else if(pConditionKey == &FWPM_CONDITION_EMBEDDED_REMOTE_PORT) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_ICMP_ERROR_V4_EMBEDDED_REMOTE_PORT].value); + else if(pConditionKey == &FWPM_CONDITION_IP_LOCAL_INTERFACE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_ICMP_ERROR_V4_IP_LOCAL_INTERFACE].value); + else if(pConditionKey == &FWPM_CONDITION_ICMP_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_ICMP_ERROR_V4_ICMP_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_ICMP_CODE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_ICMP_ERROR_V4_ICMP_CODE].value); + else if(pConditionKey == &FWPM_CONDITION_INTERFACE_INDEX) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_ICMP_ERROR_V4_INTERFACE_INDEX].value); + else if(pConditionKey == &FWPM_CONDITION_SUB_INTERFACE_INDEX) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_ICMP_ERROR_V4_SUB_INTERFACE_INDEX].value); + else if(pConditionKey == &FWPM_CONDITION_INTERFACE_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_ICMP_ERROR_V4_INTERFACE_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_TUNNEL_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_ICMP_ERROR_V4_TUNNEL_TYPE].value); + +#if(NTDDI_VERSION >= NTDDI_VISTASP1) + + else if(pConditionKey == &FWPM_CONDITION_IP_ARRIVAL_INTERFACE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_ICMP_ERROR_V4_IP_ARRIVAL_INTERFACE].value); + else if(pConditionKey == &FWPM_CONDITION_ARRIVAL_INTERFACE_INDEX) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_ICMP_ERROR_V4_ARRIVAL_INTERFACE_INDEX].value); + else if(pConditionKey == &FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_ICMP_ERROR_V4_ARRIVAL_INTERFACE_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_ICMP_ERROR_V4_ARRIVAL_TUNNEL_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_FLAGS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_ICMP_ERROR_V4_FLAGS].value); + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + else if(pConditionKey == &FWPM_CONDITION_ARRIVAL_INTERFACE_PROFILE_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_ICMP_ERROR_V4_ARRIVAL_INTERFACE_PROFILE_ID].value); + else if(pConditionKey == &FWPM_CONDITION_INTERFACE_QUARANTINE_EPOCH) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_ICMP_ERROR_V4_INTERFACE_QUARANTINE_EPOCH].value); + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) +#endif /// (NTDDI_VERSION >= NTDDI_VISTASP1) + + break; + } + case FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4: + case FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4_DISCARD: + case FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6: + case FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6_DISCARD: + { + if(pConditionKey == &FWPM_CONDITION_IP_LOCAL_ADDRESS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_ICMP_ERROR_V4_IP_LOCAL_ADDRESS].value); + else if(pConditionKey == &FWPM_CONDITION_IP_REMOTE_ADDRESS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_ICMP_ERROR_V4_IP_REMOTE_ADDRESS].value); + else if(pConditionKey == &FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_ICMP_ERROR_V4_IP_LOCAL_ADDRESS_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_IP_LOCAL_INTERFACE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_ICMP_ERROR_V4_IP_LOCAL_INTERFACE].value); + else if(pConditionKey == &FWPM_CONDITION_ICMP_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_ICMP_ERROR_V4_ICMP_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_ICMP_CODE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_ICMP_ERROR_V4_ICMP_CODE].value); + else if(pConditionKey == &FWPM_CONDITION_INTERFACE_INDEX) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_ICMP_ERROR_V4_INTERFACE_INDEX].value); + else if(pConditionKey == &FWPM_CONDITION_SUB_INTERFACE_INDEX) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_ICMP_ERROR_V4_SUB_INTERFACE_INDEX].value); + else if(pConditionKey == &FWPM_CONDITION_INTERFACE_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_ICMP_ERROR_V4_INTERFACE_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_TUNNEL_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_ICMP_ERROR_V4_TUNNEL_TYPE].value); + +#if(NTDDI_VERSION >= NTDDI_VISTASP1) + + else if(pConditionKey == &FWPM_CONDITION_FLAGS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_ICMP_ERROR_V4_FLAGS].value); + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + else if(pConditionKey == &FWPM_CONDITION_NEXTHOP_INTERFACE_PROFILE_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_ICMP_ERROR_V4_NEXTHOP_INTERFACE_PROFILE_ID].value); + else if(pConditionKey == &FWPM_CONDITION_INTERFACE_QUARANTINE_EPOCH) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_ICMP_ERROR_V4_INTERFACE_QUARANTINE_EPOCH].value); + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) +#endif /// (NTDDI_VERSION >= NTDDI_VISTASP1) + + break; + } + case FWPS_LAYER_ALE_RESOURCE_ASSIGNMENT_V4: + case FWPS_LAYER_ALE_RESOURCE_ASSIGNMENT_V4_DISCARD: + case FWPS_LAYER_ALE_RESOURCE_ASSIGNMENT_V6: + case FWPS_LAYER_ALE_RESOURCE_ASSIGNMENT_V6_DISCARD: + { + if(pConditionKey == &FWPM_CONDITION_ALE_APP_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_RESOURCE_ASSIGNMENT_V4_ALE_APP_ID].value); + else if(pConditionKey == &FWPM_CONDITION_ALE_USER_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_RESOURCE_ASSIGNMENT_V4_ALE_USER_ID].value); + else if(pConditionKey == &FWPM_CONDITION_IP_LOCAL_ADDRESS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_RESOURCE_ASSIGNMENT_V4_IP_LOCAL_ADDRESS].value); + else if(pConditionKey == &FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_RESOURCE_ASSIGNMENT_V4_IP_LOCAL_ADDRESS_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_IP_LOCAL_PORT) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_RESOURCE_ASSIGNMENT_V4_IP_LOCAL_PORT].value); + else if(pConditionKey == &FWPM_CONDITION_IP_PROTOCOL) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_RESOURCE_ASSIGNMENT_V4_IP_PROTOCOL].value); + else if(pConditionKey == &FWPM_CONDITION_ALE_PROMISCUOUS_MODE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_RESOURCE_ASSIGNMENT_V4_ALE_PROMISCUOUS_MODE].value); + else if(pConditionKey == &FWPM_CONDITION_IP_LOCAL_INTERFACE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_RESOURCE_ASSIGNMENT_V4_IP_LOCAL_INTERFACE].value); + else if(pConditionKey == &FWPM_CONDITION_FLAGS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_RESOURCE_ASSIGNMENT_V4_FLAGS].value); + else if(pConditionKey == &FWPM_CONDITION_INTERFACE_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_RESOURCE_ASSIGNMENT_V4_INTERFACE_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_TUNNEL_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_RESOURCE_ASSIGNMENT_V4_TUNNEL_TYPE].value); + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + else if(pConditionKey == &FWPM_CONDITION_LOCAL_INTERFACE_PROFILE_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_RESOURCE_ASSIGNMENT_V4_LOCAL_INTERFACE_PROFILE_ID].value); + else if(pConditionKey == &FWPM_CONDITION_ALE_SIO_FIREWALL_SOCKET_PROPERTY) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_RESOURCE_ASSIGNMENT_V4_SIO_FIREWALL_SOCKET_PROPERTY].value); + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + else if(pConditionKey == &FWPM_CONDITION_ALE_PACKAGE_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_RESOURCE_ASSIGNMENT_V4_ALE_PACKAGE_ID].value); + +#if(NTDDI_VERSION >= NTDDI_WINTHRESHOLD) + + else if(pConditionKey == &FWPM_CONDITION_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_RESOURCE_ASSIGNMENT_V4_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE].value); + +#endif /// (NTDDI_VERSION >= NTDDI_WINTHRESHOLD) +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + break; + } + case FWPS_LAYER_ALE_AUTH_LISTEN_V4: + case FWPS_LAYER_ALE_AUTH_LISTEN_V4_DISCARD: + case FWPS_LAYER_ALE_AUTH_LISTEN_V6: + case FWPS_LAYER_ALE_AUTH_LISTEN_V6_DISCARD: + { + if(pConditionKey == &FWPM_CONDITION_ALE_APP_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_LISTEN_V4_ALE_APP_ID].value); + else if(pConditionKey == &FWPM_CONDITION_ALE_USER_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_LISTEN_V4_ALE_USER_ID].value); + else if(pConditionKey == &FWPM_CONDITION_IP_LOCAL_ADDRESS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_LISTEN_V4_IP_LOCAL_ADDRESS].value); + else if(pConditionKey == &FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_LISTEN_V4_IP_LOCAL_ADDRESS_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_IP_LOCAL_PORT) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_LISTEN_V4_IP_LOCAL_PORT].value); + else if(pConditionKey == &FWPM_CONDITION_IP_LOCAL_INTERFACE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_LISTEN_V4_IP_LOCAL_INTERFACE].value); + else if(pConditionKey == &FWPM_CONDITION_FLAGS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_LISTEN_V4_FLAGS].value); + else if(pConditionKey == &FWPM_CONDITION_INTERFACE_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_LISTEN_V4_INTERFACE_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_TUNNEL_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_LISTEN_V4_TUNNEL_TYPE].value); + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + else if(pConditionKey == &FWPM_CONDITION_LOCAL_INTERFACE_PROFILE_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_LISTEN_V4_LOCAL_INTERFACE_PROFILE_ID].value); + else if(pConditionKey == &FWPM_CONDITION_ALE_SIO_FIREWALL_SOCKET_PROPERTY) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_LISTEN_V4_SIO_FIREWALL_SOCKET_PROPERTY].value); + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + else if(pConditionKey == &FWPM_CONDITION_ALE_PACKAGE_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_LISTEN_V4_ALE_PACKAGE_ID].value); + +#if(NTDDI_VERSION >= NTDDI_WINTHRESHOLD) + + else if(pConditionKey == &FWPM_CONDITION_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_LISTEN_V4_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE].value); + +#endif /// (NTDDI_VERSION >= NTDDI_WINTHRESHOLD) +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + break; + } + case FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4: + case FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4_DISCARD: + case FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6: + case FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6_DISCARD: + { + if(pConditionKey == &FWPM_CONDITION_ALE_APP_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_ALE_APP_ID].value); + else if(pConditionKey == &FWPM_CONDITION_ALE_USER_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_ALE_USER_ID].value); + else if(pConditionKey == &FWPM_CONDITION_IP_LOCAL_ADDRESS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_IP_LOCAL_ADDRESS].value); + else if(pConditionKey == &FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_IP_LOCAL_ADDRESS_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_IP_LOCAL_PORT) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_IP_LOCAL_PORT].value); + else if(pConditionKey == &FWPM_CONDITION_IP_PROTOCOL) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_IP_PROTOCOL].value); + else if(pConditionKey == &FWPM_CONDITION_IP_REMOTE_ADDRESS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_IP_REMOTE_ADDRESS].value); + else if(pConditionKey == &FWPM_CONDITION_IP_REMOTE_PORT) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_IP_REMOTE_PORT].value); + else if(pConditionKey == &FWPM_CONDITION_ALE_REMOTE_USER_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_ALE_REMOTE_USER_ID].value); + else if(pConditionKey == &FWPM_CONDITION_ALE_REMOTE_MACHINE_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_ALE_REMOTE_MACHINE_ID].value); + else if(pConditionKey == &FWPM_CONDITION_IP_LOCAL_INTERFACE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_IP_LOCAL_INTERFACE].value); + else if(pConditionKey == &FWPM_CONDITION_FLAGS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_FLAGS].value); + else if(pConditionKey == &FWPM_CONDITION_ALE_SIO_FIREWALL_SYSTEM_PORT) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_SIO_FIREWALL_SYSTEM_PORT].value); + else if(pConditionKey == &FWPM_CONDITION_ALE_NAP_CONTEXT) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_NAP_CONTEXT].value); + else if(pConditionKey == &FWPM_CONDITION_INTERFACE_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_INTERFACE_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_TUNNEL_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_TUNNEL_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_INTERFACE_INDEX) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_INTERFACE_INDEX].value); + else if(pConditionKey == &FWPM_CONDITION_SUB_INTERFACE_INDEX) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_SUB_INTERFACE_INDEX].value); + +#if(NTDDI_VERSION >= NTDDI_VISTASP1) + + else if(pConditionKey == &FWPM_CONDITION_IP_ARRIVAL_INTERFACE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_IP_ARRIVAL_INTERFACE].value); + else if(pConditionKey == &FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_ARRIVAL_INTERFACE_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_ARRIVAL_TUNNEL_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_ARRIVAL_INTERFACE_INDEX) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_ARRIVAL_INTERFACE_INDEX].value); + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + else if(pConditionKey == &FWPM_CONDITION_NEXTHOP_SUB_INTERFACE_INDEX) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_NEXTHOP_SUB_INTERFACE_INDEX].value); + else if(pConditionKey == &FWPM_CONDITION_IP_NEXTHOP_INTERFACE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_IP_NEXTHOP_INTERFACE].value); + else if(pConditionKey == &FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_NEXTHOP_INTERFACE_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_NEXTHOP_TUNNEL_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_NEXTHOP_INTERFACE_INDEX) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_NEXTHOP_INTERFACE_INDEX].value); + else if(pConditionKey == &FWPM_CONDITION_ORIGINAL_PROFILE_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_ORIGINAL_PROFILE_ID].value); + else if(pConditionKey == &FWPM_CONDITION_CURRENT_PROFILE_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_CURRENT_PROFILE_ID].value); + else if(pConditionKey == &FWPM_CONDITION_REAUTHORIZE_REASON) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_REAUTHORIZE_REASON].value); + else if(pConditionKey == &FWPM_CONDITION_ORIGINAL_ICMP_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_ORIGINAL_ICMP_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_INTERFACE_QUARANTINE_EPOCH) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_INTERFACE_QUARANTINE_EPOCH].value); + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + else if(pConditionKey == &FWPM_CONDITION_ALE_PACKAGE_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_ALE_PACKAGE_ID].value); + +#if(NTDDI_VERSION >= NTDDI_WINTHRESHOLD) + + else if(pConditionKey == &FWPM_CONDITION_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE].value); + +#endif /// (NTDDI_VERSION >= NTDDI_WINTHRESHOLD) +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) +#endif /// (NTDDI_VERSION >= NTDDI_VISTASP1) + + break; + } + case FWPS_LAYER_ALE_AUTH_CONNECT_V4: + case FWPS_LAYER_ALE_AUTH_CONNECT_V4_DISCARD: + case FWPS_LAYER_ALE_AUTH_CONNECT_V6: + case FWPS_LAYER_ALE_AUTH_CONNECT_V6_DISCARD: + { + if(pConditionKey == &FWPM_CONDITION_ALE_APP_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_ALE_APP_ID].value); + else if(pConditionKey == &FWPM_CONDITION_ALE_USER_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_ALE_USER_ID].value); + else if(pConditionKey == &FWPM_CONDITION_IP_LOCAL_ADDRESS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_LOCAL_ADDRESS].value); + else if(pConditionKey == &FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_LOCAL_ADDRESS_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_IP_LOCAL_PORT) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_LOCAL_PORT].value); + else if(pConditionKey == &FWPM_CONDITION_IP_PROTOCOL) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_PROTOCOL].value); + else if(pConditionKey == &FWPM_CONDITION_IP_REMOTE_ADDRESS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_REMOTE_ADDRESS].value); + else if(pConditionKey == &FWPM_CONDITION_IP_REMOTE_PORT) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_REMOTE_PORT].value); + else if(pConditionKey == &FWPM_CONDITION_ALE_REMOTE_USER_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_ALE_REMOTE_USER_ID].value); + else if(pConditionKey == &FWPM_CONDITION_ALE_REMOTE_MACHINE_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_ALE_REMOTE_MACHINE_ID].value); + else if(pConditionKey == &FWPM_CONDITION_IP_DESTINATION_ADDRESS_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_DESTINATION_ADDRESS_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_IP_LOCAL_INTERFACE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_LOCAL_INTERFACE].value); + else if(pConditionKey == &FWPM_CONDITION_FLAGS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_FLAGS].value); + else if(pConditionKey == &FWPM_CONDITION_INTERFACE_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_INTERFACE_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_TUNNEL_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_TUNNEL_TYPE].value); + +#if(NTDDI_VERSION >= NTDDI_VISTASP1) + + else if(pConditionKey == &FWPM_CONDITION_INTERFACE_INDEX) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_INTERFACE_INDEX].value); + else if(pConditionKey == &FWPM_CONDITION_SUB_INTERFACE_INDEX) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_SUB_INTERFACE_INDEX].value); + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + else if(pConditionKey == &FWPM_CONDITION_IP_ARRIVAL_INTERFACE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_ARRIVAL_INTERFACE].value); + else if(pConditionKey == &FWPM_CONDITION_ARRIVAL_INTERFACE_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_ARRIVAL_INTERFACE_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_ARRIVAL_TUNNEL_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_ARRIVAL_TUNNEL_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_ARRIVAL_INTERFACE_INDEX) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_ARRIVAL_INTERFACE_INDEX].value); + else if(pConditionKey == &FWPM_CONDITION_NEXTHOP_SUB_INTERFACE_INDEX) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_NEXTHOP_SUB_INTERFACE_INDEX].value); + else if(pConditionKey == &FWPM_CONDITION_IP_NEXTHOP_INTERFACE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_NEXTHOP_INTERFACE].value); + else if(pConditionKey == &FWPM_CONDITION_NEXTHOP_INTERFACE_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_NEXTHOP_INTERFACE_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_NEXTHOP_TUNNEL_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_NEXTHOP_TUNNEL_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_NEXTHOP_INTERFACE_INDEX) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_NEXTHOP_INTERFACE_INDEX].value); + else if(pConditionKey == &FWPM_CONDITION_ORIGINAL_PROFILE_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_ORIGINAL_PROFILE_ID].value); + else if(pConditionKey == &FWPM_CONDITION_CURRENT_PROFILE_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_CURRENT_PROFILE_ID].value); + else if(pConditionKey == &FWPM_CONDITION_REAUTHORIZE_REASON) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_REAUTHORIZE_REASON].value); + else if(pConditionKey == &FWPM_CONDITION_PEER_NAME) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_PEER_NAME].value); + else if(pConditionKey == &FWPM_CONDITION_ORIGINAL_ICMP_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_ORIGINAL_ICMP_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_INTERFACE_QUARANTINE_EPOCH) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_INTERFACE_QUARANTINE_EPOCH].value); + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + else if(pConditionKey == &FWPM_CONDITION_ALE_ORIGINAL_APP_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_ALE_ORIGINAL_APP_ID].value); + else if(pConditionKey == &FWPM_CONDITION_ALE_PACKAGE_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_ALE_PACKAGE_ID].value); + +#if(NTDDI_VERSION >= NTDDI_WINTHRESHOLD) + + else if(pConditionKey == &FWPM_CONDITION_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE].value); + +#endif /// (NTDDI_VERSION >= NTDDI_WINTHRESHOLD) +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) +#endif /// (NTDDI_VERSION >= NTDDI_VISTASP1) + + break; + } + case FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4: + case FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4_DISCARD: + case FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6: + case FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6_DISCARD: + { + if(pConditionKey == &FWPM_CONDITION_ALE_APP_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_ALE_APP_ID].value); + else if(pConditionKey == &FWPM_CONDITION_ALE_USER_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_ALE_USER_ID].value); + else if(pConditionKey == &FWPM_CONDITION_IP_LOCAL_ADDRESS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_LOCAL_ADDRESS].value); + else if(pConditionKey == &FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_LOCAL_ADDRESS_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_IP_LOCAL_PORT) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_LOCAL_PORT].value); + else if(pConditionKey == &FWPM_CONDITION_IP_PROTOCOL) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_PROTOCOL].value); + else if(pConditionKey == &FWPM_CONDITION_IP_REMOTE_ADDRESS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_REMOTE_ADDRESS].value); + else if(pConditionKey == &FWPM_CONDITION_IP_REMOTE_PORT) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_REMOTE_PORT].value); + else if(pConditionKey == &FWPM_CONDITION_ALE_REMOTE_USER_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_ALE_REMOTE_USER_ID].value); + else if(pConditionKey == &FWPM_CONDITION_ALE_REMOTE_MACHINE_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_ALE_REMOTE_MACHINE_ID].value); + else if(pConditionKey == &FWPM_CONDITION_IP_DESTINATION_ADDRESS_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_DESTINATION_ADDRESS_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_IP_LOCAL_INTERFACE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_LOCAL_INTERFACE].value); + else if(pConditionKey == &FWPM_CONDITION_DIRECTION) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_DIRECTION].value); + else if(pConditionKey == &FWPM_CONDITION_INTERFACE_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_INTERFACE_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_TUNNEL_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_TUNNEL_TYPE].value); + +#if(NTDDI_VERSION >= NTDDI_VISTASP1) + + else if(pConditionKey == &FWPM_CONDITION_FLAGS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_FLAGS].value); + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + else if(pConditionKey == &FWPM_CONDITION_ALE_ORIGINAL_APP_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_ALE_ORIGINAL_APP_ID].value); + else if(pConditionKey == &FWPM_CONDITION_ALE_PACKAGE_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_ALE_PACKAGE_ID].value); + +#if(NTDDI_VERSION >= NTDDI_WINTHRESHOLD) + + else if(pConditionKey == &FWPM_CONDITION_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE].value); + +#endif /// (NTDDI_VERSION >= NTDDI_WINTHRESHOLD) +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) +#endif /// (NTDDI_VERSION >= NTDDI_VISTASP1) + + break; + } + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + case FWPS_LAYER_NAME_RESOLUTION_CACHE_V4: + case FWPS_LAYER_NAME_RESOLUTION_CACHE_V6: + { + if(pConditionKey == &FWPM_CONDITION_ALE_USER_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_NAME_RESOLUTION_CACHE_V4_ALE_USER_ID].value); + else if(pConditionKey == &FWPM_CONDITION_ALE_APP_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_NAME_RESOLUTION_CACHE_V4_ALE_APP_ID].value); + else if(pConditionKey == &FWPM_CONDITION_IP_REMOTE_ADDRESS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_NAME_RESOLUTION_CACHE_V4_IP_REMOTE_ADDRESS].value); + else if(pConditionKey == &FWPM_CONDITION_PEER_NAME) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_NAME_RESOLUTION_CACHE_V4_PEER_NAME].value); + + break; + } + case FWPS_LAYER_ALE_RESOURCE_RELEASE_V4: + case FWPS_LAYER_ALE_RESOURCE_RELEASE_V6: + { + if(pConditionKey == &FWPM_CONDITION_ALE_APP_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_RESOURCE_RELEASE_V4_ALE_APP_ID].value); + else if(pConditionKey == &FWPM_CONDITION_ALE_USER_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_RESOURCE_RELEASE_V4_ALE_USER_ID].value); + else if(pConditionKey == &FWPM_CONDITION_IP_LOCAL_ADDRESS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_RESOURCE_RELEASE_V4_IP_LOCAL_ADDRESS].value); + else if(pConditionKey == &FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_RESOURCE_RELEASE_V4_IP_LOCAL_ADDRESS_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_IP_LOCAL_PORT) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_RESOURCE_RELEASE_V4_IP_LOCAL_PORT].value); + else if(pConditionKey == &FWPM_CONDITION_IP_PROTOCOL) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_RESOURCE_RELEASE_V4_IP_PROTOCOL].value); + else if(pConditionKey == &FWPM_CONDITION_IP_LOCAL_INTERFACE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_RESOURCE_RELEASE_V4_IP_LOCAL_INTERFACE].value); + else if(pConditionKey == &FWPM_CONDITION_FLAGS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_RESOURCE_RELEASE_V4_FLAGS].value); + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + else if(pConditionKey == &FWPM_CONDITION_ALE_PACKAGE_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_RESOURCE_RELEASE_V4_ALE_PACKAGE_ID].value); + +#if(NTDDI_VERSION >= NTDDI_WINTHRESHOLD) + + else if(pConditionKey == &FWPM_CONDITION_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_RESOURCE_RELEASE_V4_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE].value); + +#endif /// (NTDDI_VERSION >= NTDDI_WINTHRESHOLD) +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) + + break; + } + case FWPS_LAYER_ALE_ENDPOINT_CLOSURE_V4: + case FWPS_LAYER_ALE_ENDPOINT_CLOSURE_V6: + { + if(pConditionKey == &FWPM_CONDITION_ALE_APP_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_ENDPOINT_CLOSURE_V4_ALE_APP_ID].value); + else if(pConditionKey == &FWPM_CONDITION_ALE_USER_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_ENDPOINT_CLOSURE_V4_ALE_USER_ID].value); + else if(pConditionKey == &FWPM_CONDITION_IP_LOCAL_ADDRESS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_ENDPOINT_CLOSURE_V4_IP_LOCAL_ADDRESS].value); + else if(pConditionKey == &FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_ENDPOINT_CLOSURE_V4_IP_LOCAL_ADDRESS_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_IP_LOCAL_PORT) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_ENDPOINT_CLOSURE_V4_IP_LOCAL_PORT].value); + else if(pConditionKey == &FWPM_CONDITION_IP_PROTOCOL) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_ENDPOINT_CLOSURE_V4_IP_PROTOCOL].value); + else if(pConditionKey == &FWPM_CONDITION_IP_REMOTE_ADDRESS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_ENDPOINT_CLOSURE_V4_IP_REMOTE_ADDRESS].value); + else if(pConditionKey == &FWPM_CONDITION_IP_REMOTE_PORT) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_ENDPOINT_CLOSURE_V4_IP_REMOTE_PORT].value); + else if(pConditionKey == &FWPM_CONDITION_IP_LOCAL_INTERFACE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_ENDPOINT_CLOSURE_V4_IP_LOCAL_INTERFACE].value); + else if(pConditionKey == &FWPM_CONDITION_FLAGS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_ENDPOINT_CLOSURE_V4_FLAGS].value); + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + else if(pConditionKey == &FWPM_CONDITION_ALE_PACKAGE_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_ENDPOINT_CLOSURE_V4_ALE_PACKAGE_ID].value); + +#if(NTDDI_VERSION >= NTDDI_WINTHRESHOLD) + + else if(pConditionKey == &FWPM_CONDITION_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_ENDPOINT_CLOSURE_V4_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE].value); + +#endif /// (NTDDI_VERSION >= NTDDI_WINTHRESHOLD) +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) + + break; + } + case FWPS_LAYER_ALE_CONNECT_REDIRECT_V4: + case FWPS_LAYER_ALE_CONNECT_REDIRECT_V6: + { + if(pConditionKey == &FWPM_CONDITION_ALE_APP_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_CONNECT_REDIRECT_V4_ALE_APP_ID].value); + else if(pConditionKey == &FWPM_CONDITION_ALE_USER_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_CONNECT_REDIRECT_V4_ALE_USER_ID].value); + else if(pConditionKey == &FWPM_CONDITION_IP_LOCAL_ADDRESS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_CONNECT_REDIRECT_V4_IP_LOCAL_ADDRESS].value); + else if(pConditionKey == &FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_CONNECT_REDIRECT_V4_IP_LOCAL_ADDRESS_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_IP_LOCAL_PORT) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_CONNECT_REDIRECT_V4_IP_LOCAL_PORT].value); + else if(pConditionKey == &FWPM_CONDITION_IP_PROTOCOL) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_CONNECT_REDIRECT_V4_IP_PROTOCOL].value); + else if(pConditionKey == &FWPM_CONDITION_IP_REMOTE_ADDRESS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_CONNECT_REDIRECT_V4_IP_REMOTE_ADDRESS].value); + else if(pConditionKey == &FWPM_CONDITION_IP_DESTINATION_ADDRESS_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_CONNECT_REDIRECT_V4_IP_DESTINATION_ADDRESS_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_IP_REMOTE_PORT) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_CONNECT_REDIRECT_V4_IP_REMOTE_PORT].value); + else if(pConditionKey == &FWPM_CONDITION_FLAGS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_CONNECT_REDIRECT_V4_FLAGS].value); + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + else if(pConditionKey == &FWPM_CONDITION_ALE_ORIGINAL_APP_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_CONNECT_REDIRECT_V4_ALE_ORIGINAL_APP_ID].value); + else if(pConditionKey == &FWPM_CONDITION_ALE_PACKAGE_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_CONNECT_REDIRECT_V4_ALE_PACKAGE_ID].value); + +#if(NTDDI_VERSION >= NTDDI_WINTHRESHOLD) + + else if(pConditionKey == &FWPM_CONDITION_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_CONNECT_REDIRECT_V4_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE].value); + +#endif /// (NTDDI_VERSION >= NTDDI_WINTHRESHOLD) +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) + + break; + } + case FWPS_LAYER_ALE_BIND_REDIRECT_V4: + case FWPS_LAYER_ALE_BIND_REDIRECT_V6: + { + if(pConditionKey == &FWPM_CONDITION_ALE_APP_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_BIND_REDIRECT_V4_ALE_APP_ID].value); + else if(pConditionKey == &FWPM_CONDITION_ALE_USER_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_BIND_REDIRECT_V4_ALE_USER_ID].value); + else if(pConditionKey == &FWPM_CONDITION_IP_LOCAL_ADDRESS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_BIND_REDIRECT_V4_IP_LOCAL_ADDRESS].value); + else if(pConditionKey == &FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_BIND_REDIRECT_V4_IP_LOCAL_ADDRESS_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_IP_LOCAL_PORT) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_BIND_REDIRECT_V4_IP_LOCAL_PORT].value); + else if(pConditionKey == &FWPM_CONDITION_IP_PROTOCOL) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_BIND_REDIRECT_V4_IP_PROTOCOL].value); + else if(pConditionKey == &FWPM_CONDITION_FLAGS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_BIND_REDIRECT_V4_FLAGS].value); + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + else if(pConditionKey == &FWPM_CONDITION_ALE_PACKAGE_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_BIND_REDIRECT_V4_ALE_PACKAGE_ID].value); + +#if(NTDDI_VERSION >= NTDDI_WINTHRESHOLD) + + else if(pConditionKey == &FWPM_CONDITION_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_ALE_BIND_REDIRECT_V4_ALE_SECURITY_ATTRIBUTE_FQBN_VALUE].value); + +#endif /// (NTDDI_VERSION >= NTDDI_WINTHRESHOLD) +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) + + break; + } + case FWPS_LAYER_STREAM_PACKET_V4: + case FWPS_LAYER_STREAM_PACKET_V6: + { + if(pConditionKey == &FWPM_CONDITION_IP_LOCAL_ADDRESS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_STREAM_PACKET_V4_IP_LOCAL_ADDRESS].value); + else if(pConditionKey == &FWPM_CONDITION_IP_REMOTE_ADDRESS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_STREAM_PACKET_V4_IP_REMOTE_ADDRESS].value); + else if(pConditionKey == &FWPM_CONDITION_IP_LOCAL_PORT) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_STREAM_PACKET_V4_IP_LOCAL_PORT].value); + else if(pConditionKey == &FWPM_CONDITION_IP_REMOTE_PORT) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_STREAM_PACKET_V4_IP_REMOTE_PORT].value); + else if(pConditionKey == &FWPM_CONDITION_IP_LOCAL_INTERFACE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_STREAM_PACKET_V4_IP_LOCAL_INTERFACE].value); + else if(pConditionKey == &FWPM_CONDITION_INTERFACE_INDEX) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_STREAM_PACKET_V4_INTERFACE_INDEX].value); + else if(pConditionKey == &FWPM_CONDITION_SUB_INTERFACE_INDEX) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_STREAM_PACKET_V4_SUB_INTERFACE_INDEX].value); + else if(pConditionKey == &FWPM_CONDITION_DIRECTION) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_STREAM_PACKET_V4_DIRECTION].value); + else if(pConditionKey == &FWPM_CONDITION_FLAGS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_STREAM_PACKET_V4_FLAGS].value); + else if(pConditionKey == &FWPM_CONDITION_INTERFACE_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_STREAM_PACKET_V4_INTERFACE_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_TUNNEL_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_STREAM_PACKET_V4_TUNNEL_TYPE].value); + + break; + } + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + case FWPS_LAYER_INBOUND_MAC_FRAME_ETHERNET: + { + if(pConditionKey == &FWPM_CONDITION_INTERFACE_MAC_ADDRESS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_MAC_FRAME_ETHERNET_INTERFACE_MAC_ADDRESS].value); + else if(pConditionKey == &FWPM_CONDITION_MAC_LOCAL_ADDRESS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_MAC_FRAME_ETHERNET_MAC_LOCAL_ADDRESS].value); + else if(pConditionKey == &FWPM_CONDITION_MAC_REMOTE_ADDRESS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_MAC_FRAME_ETHERNET_MAC_REMOTE_ADDRESS].value); + else if(pConditionKey == &FWPM_CONDITION_MAC_LOCAL_ADDRESS_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_MAC_FRAME_ETHERNET_MAC_LOCAL_ADDRESS_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_MAC_REMOTE_ADDRESS_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_MAC_FRAME_ETHERNET_MAC_REMOTE_ADDRESS_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_ETHER_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_MAC_FRAME_ETHERNET_ETHER_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_VLAN_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_MAC_FRAME_ETHERNET_VLAN_ID].value); + else if(pConditionKey == &FWPM_CONDITION_INTERFACE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_MAC_FRAME_ETHERNET_INTERFACE].value); + else if(pConditionKey == &FWPM_CONDITION_INTERFACE_INDEX) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_MAC_FRAME_ETHERNET_INTERFACE_INDEX].value); + else if(pConditionKey == &FWPM_CONDITION_NDIS_PORT) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_MAC_FRAME_ETHERNET_NDIS_PORT].value); + else if(pConditionKey == &FWPM_CONDITION_L2_FLAGS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_MAC_FRAME_ETHERNET_L2_FLAGS].value); + + break; + } + case FWPS_LAYER_OUTBOUND_MAC_FRAME_ETHERNET: + { + if(pConditionKey == &FWPM_CONDITION_INTERFACE_MAC_ADDRESS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_MAC_FRAME_ETHERNET_INTERFACE_MAC_ADDRESS].value); + else if(pConditionKey == &FWPM_CONDITION_MAC_LOCAL_ADDRESS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_MAC_FRAME_ETHERNET_MAC_LOCAL_ADDRESS].value); + else if(pConditionKey == &FWPM_CONDITION_MAC_REMOTE_ADDRESS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_MAC_FRAME_ETHERNET_MAC_REMOTE_ADDRESS].value); + else if(pConditionKey == &FWPM_CONDITION_MAC_LOCAL_ADDRESS_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_MAC_FRAME_ETHERNET_MAC_LOCAL_ADDRESS_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_MAC_REMOTE_ADDRESS_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_MAC_FRAME_ETHERNET_MAC_REMOTE_ADDRESS_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_ETHER_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_MAC_FRAME_ETHERNET_ETHER_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_VLAN_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_MAC_FRAME_ETHERNET_VLAN_ID].value); + else if(pConditionKey == &FWPM_CONDITION_INTERFACE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_MAC_FRAME_ETHERNET_INTERFACE].value); + else if(pConditionKey == &FWPM_CONDITION_INTERFACE_INDEX) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_MAC_FRAME_ETHERNET_INTERFACE_INDEX].value); + else if(pConditionKey == &FWPM_CONDITION_NDIS_PORT) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_MAC_FRAME_ETHERNET_NDIS_PORT].value); + else if(pConditionKey == &FWPM_CONDITION_L2_FLAGS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_MAC_FRAME_ETHERNET_L2_FLAGS].value); + + break; + } + case FWPS_LAYER_INBOUND_MAC_FRAME_NATIVE: + { + if(pConditionKey == &FWPM_CONDITION_NDIS_MEDIA_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_MAC_FRAME_NATIVE_NDIS_MEDIA_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_NDIS_PHYSICAL_MEDIA_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_MAC_FRAME_NATIVE_NDIS_PHYSICAL_MEDIA_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_INTERFACE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_MAC_FRAME_NATIVE_INTERFACE].value); + else if(pConditionKey == &FWPM_CONDITION_INTERFACE_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_MAC_FRAME_NATIVE_INTERFACE_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_INTERFACE_INDEX) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_MAC_FRAME_NATIVE_INTERFACE_INDEX].value); + else if(pConditionKey == &FWPM_CONDITION_NDIS_PORT) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_MAC_FRAME_NATIVE_NDIS_PORT].value); + else if(pConditionKey == &FWPM_CONDITION_L2_FLAGS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_MAC_FRAME_NATIVE_L2_FLAGS].value); + + break; + } + case FWPS_LAYER_OUTBOUND_MAC_FRAME_NATIVE: + { + if(pConditionKey == &FWPM_CONDITION_NDIS_MEDIA_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_MAC_FRAME_NATIVE_NDIS_MEDIA_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_NDIS_PHYSICAL_MEDIA_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_MAC_FRAME_NATIVE_NDIS_PHYSICAL_MEDIA_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_INTERFACE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_MAC_FRAME_NATIVE_INTERFACE].value); + else if(pConditionKey == &FWPM_CONDITION_INTERFACE_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_MAC_FRAME_NATIVE_INTERFACE_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_INTERFACE_INDEX) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_MAC_FRAME_NATIVE_INTERFACE_INDEX].value); + else if(pConditionKey == &FWPM_CONDITION_NDIS_PORT) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_MAC_FRAME_NATIVE_NDIS_PORT].value); + else if(pConditionKey == &FWPM_CONDITION_L2_FLAGS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_MAC_FRAME_NATIVE_L2_FLAGS].value); + + break; + } + case FWPS_LAYER_INGRESS_VSWITCH_ETHERNET: + { + if(pConditionKey == &FWPM_CONDITION_MAC_SOURCE_ADDRESS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INGRESS_VSWITCH_ETHERNET_MAC_SOURCE_ADDRESS].value); + else if(pConditionKey == &FWPM_CONDITION_MAC_SOURCE_ADDRESS_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INGRESS_VSWITCH_ETHERNET_MAC_SOURCE_ADDRESS_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_MAC_DESTINATION_ADDRESS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INGRESS_VSWITCH_ETHERNET_MAC_DESTINATION_ADDRESS].value); + else if(pConditionKey == &FWPM_CONDITION_MAC_DESTINATION_ADDRESS_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INGRESS_VSWITCH_ETHERNET_MAC_DESTINATION_ADDRESS_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_ETHER_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INGRESS_VSWITCH_ETHERNET_ETHER_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_VLAN_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INGRESS_VSWITCH_ETHERNET_VLAN_ID].value); + else if(pConditionKey == &FWPM_CONDITION_VSWITCH_TENANT_NETWORK_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INGRESS_VSWITCH_ETHERNET_VSWITCH_TENANT_NETWORK_ID].value); + else if(pConditionKey == &FWPM_CONDITION_VSWITCH_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INGRESS_VSWITCH_ETHERNET_VSWITCH_ID].value); + else if(pConditionKey == &FWPM_CONDITION_VSWITCH_NETWORK_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INGRESS_VSWITCH_ETHERNET_VSWITCH_NETWORK_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_VSWITCH_SOURCE_INTERFACE_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INGRESS_VSWITCH_ETHERNET_VSWITCH_SOURCE_INTERFACE_ID].value); + else if(pConditionKey == &FWPM_CONDITION_VSWITCH_SOURCE_INTERFACE_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INGRESS_VSWITCH_ETHERNET_VSWITCH_SOURCE_INTERFACE_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_VSWITCH_SOURCE_VM_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INGRESS_VSWITCH_ETHERNET_VSWITCH_SOURCE_VM_ID].value); + else if(pConditionKey == &FWPM_CONDITION_L2_FLAGS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INGRESS_VSWITCH_ETHERNET_L2_FLAGS].value); + + break; + } + case FWPS_LAYER_EGRESS_VSWITCH_ETHERNET: + { + if(pConditionKey == &FWPM_CONDITION_MAC_SOURCE_ADDRESS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_EGRESS_VSWITCH_ETHERNET_MAC_SOURCE_ADDRESS].value); + else if(pConditionKey == &FWPM_CONDITION_MAC_SOURCE_ADDRESS_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_EGRESS_VSWITCH_ETHERNET_MAC_SOURCE_ADDRESS_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_MAC_DESTINATION_ADDRESS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_EGRESS_VSWITCH_ETHERNET_MAC_DESTINATION_ADDRESS].value); + else if(pConditionKey == &FWPM_CONDITION_MAC_DESTINATION_ADDRESS_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_EGRESS_VSWITCH_ETHERNET_MAC_DESTINATION_ADDRESS_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_ETHER_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_EGRESS_VSWITCH_ETHERNET_ETHER_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_VLAN_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_EGRESS_VSWITCH_ETHERNET_VLAN_ID].value); + else if(pConditionKey == &FWPM_CONDITION_VSWITCH_TENANT_NETWORK_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_EGRESS_VSWITCH_ETHERNET_VSWITCH_TENANT_NETWORK_ID].value); + else if(pConditionKey == &FWPM_CONDITION_VSWITCH_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_EGRESS_VSWITCH_ETHERNET_VSWITCH_ID].value); + else if(pConditionKey == &FWPM_CONDITION_VSWITCH_NETWORK_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_EGRESS_VSWITCH_ETHERNET_VSWITCH_NETWORK_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_VSWITCH_SOURCE_INTERFACE_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_EGRESS_VSWITCH_ETHERNET_VSWITCH_SOURCE_INTERFACE_ID].value); + else if(pConditionKey == &FWPM_CONDITION_VSWITCH_SOURCE_INTERFACE_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_EGRESS_VSWITCH_ETHERNET_VSWITCH_SOURCE_INTERFACE_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_VSWITCH_SOURCE_VM_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_EGRESS_VSWITCH_ETHERNET_VSWITCH_SOURCE_VM_ID].value); + else if(pConditionKey == &FWPM_CONDITION_VSWITCH_DESTINATION_INTERFACE_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_EGRESS_VSWITCH_ETHERNET_VSWITCH_DESTINATION_INTERFACE_ID].value); + else if(pConditionKey == &FWPM_CONDITION_VSWITCH_DESTINATION_INTERFACE_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_EGRESS_VSWITCH_ETHERNET_VSWITCH_DESTINATION_INTERFACE_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_VSWITCH_DESTINATION_VM_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_EGRESS_VSWITCH_ETHERNET_VSWITCH_DESTINATION_VM_ID].value); + else if(pConditionKey == &FWPM_CONDITION_L2_FLAGS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_EGRESS_VSWITCH_ETHERNET_L2_FLAGS].value); + + break; + } + case FWPS_LAYER_INGRESS_VSWITCH_TRANSPORT_V4: + case FWPS_LAYER_INGRESS_VSWITCH_TRANSPORT_V6: + { + if(pConditionKey == &FWPM_CONDITION_IP_SOURCE_ADDRESS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INGRESS_VSWITCH_TRANSPORT_V4_IP_SOURCE_ADDRESS].value); + else if(pConditionKey == &FWPM_CONDITION_IP_DESTINATION_ADDRESS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INGRESS_VSWITCH_TRANSPORT_V4_IP_DESTINATION_ADDRESS].value); + else if(pConditionKey == &FWPM_CONDITION_IP_PROTOCOL) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INGRESS_VSWITCH_TRANSPORT_V4_IP_PROTOCOL].value); + else if(pConditionKey == &FWPM_CONDITION_IP_SOURCE_PORT) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INGRESS_VSWITCH_TRANSPORT_V4_IP_SOURCE_PORT].value); + else if(pConditionKey == &FWPM_CONDITION_IP_DESTINATION_PORT) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INGRESS_VSWITCH_TRANSPORT_V4_IP_DESTINATION_PORT].value); + else if(pConditionKey == &FWPM_CONDITION_VLAN_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INGRESS_VSWITCH_TRANSPORT_V4_VLAN_ID].value); + else if(pConditionKey == &FWPM_CONDITION_VSWITCH_TENANT_NETWORK_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INGRESS_VSWITCH_TRANSPORT_V4_VSWITCH_TENANT_NETWORK_ID].value); + else if(pConditionKey == &FWPM_CONDITION_VSWITCH_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INGRESS_VSWITCH_TRANSPORT_V4_VSWITCH_ID].value); + else if(pConditionKey == &FWPM_CONDITION_VSWITCH_NETWORK_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INGRESS_VSWITCH_TRANSPORT_V4_VSWITCH_NETWORK_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_VSWITCH_SOURCE_INTERFACE_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INGRESS_VSWITCH_TRANSPORT_V4_VSWITCH_SOURCE_INTERFACE_ID].value); + else if(pConditionKey == &FWPM_CONDITION_VSWITCH_SOURCE_INTERFACE_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INGRESS_VSWITCH_TRANSPORT_V4_VSWITCH_SOURCE_INTERFACE_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_VSWITCH_SOURCE_VM_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INGRESS_VSWITCH_TRANSPORT_V4_VSWITCH_SOURCE_VM_ID].value); + else if(pConditionKey == &FWPM_CONDITION_L2_FLAGS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INGRESS_VSWITCH_TRANSPORT_V4_L2_FLAGS].value); + + break; + } + case FWPS_LAYER_EGRESS_VSWITCH_TRANSPORT_V4: + case FWPS_LAYER_EGRESS_VSWITCH_TRANSPORT_V6: + { + if(pConditionKey == &FWPM_CONDITION_IP_SOURCE_ADDRESS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INGRESS_VSWITCH_TRANSPORT_V4_IP_SOURCE_ADDRESS].value); + else if(pConditionKey == &FWPM_CONDITION_IP_DESTINATION_ADDRESS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INGRESS_VSWITCH_TRANSPORT_V4_IP_DESTINATION_ADDRESS].value); + else if(pConditionKey == &FWPM_CONDITION_IP_PROTOCOL) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INGRESS_VSWITCH_TRANSPORT_V4_IP_PROTOCOL].value); + else if(pConditionKey == &FWPM_CONDITION_IP_SOURCE_PORT) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INGRESS_VSWITCH_TRANSPORT_V4_IP_SOURCE_PORT].value); + else if(pConditionKey == &FWPM_CONDITION_IP_DESTINATION_PORT) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_INGRESS_VSWITCH_TRANSPORT_V4_IP_DESTINATION_PORT].value); + else if(pConditionKey == &FWPM_CONDITION_VLAN_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_EGRESS_VSWITCH_TRANSPORT_V4_VLAN_ID].value); + else if(pConditionKey == &FWPM_CONDITION_VSWITCH_TENANT_NETWORK_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_EGRESS_VSWITCH_TRANSPORT_V4_VSWITCH_TENANT_NETWORK_ID].value); + else if(pConditionKey == &FWPM_CONDITION_VSWITCH_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_EGRESS_VSWITCH_TRANSPORT_V4_VSWITCH_ID].value); + else if(pConditionKey == &FWPM_CONDITION_VSWITCH_NETWORK_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_EGRESS_VSWITCH_TRANSPORT_V4_VSWITCH_NETWORK_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_VSWITCH_SOURCE_INTERFACE_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_EGRESS_VSWITCH_TRANSPORT_V4_VSWITCH_SOURCE_INTERFACE_ID].value); + else if(pConditionKey == &FWPM_CONDITION_VSWITCH_SOURCE_INTERFACE_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_EGRESS_VSWITCH_TRANSPORT_V4_VSWITCH_SOURCE_INTERFACE_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_VSWITCH_SOURCE_VM_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_EGRESS_VSWITCH_TRANSPORT_V4_VSWITCH_SOURCE_VM_ID].value); + else if(pConditionKey == &FWPM_CONDITION_VSWITCH_DESTINATION_INTERFACE_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_EGRESS_VSWITCH_TRANSPORT_V4_VSWITCH_DESTINATION_INTERFACE_ID].value); + else if(pConditionKey == &FWPM_CONDITION_VSWITCH_DESTINATION_INTERFACE_TYPE) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_EGRESS_VSWITCH_TRANSPORT_V4_VSWITCH_DESTINATION_INTERFACE_TYPE].value); + else if(pConditionKey == &FWPM_CONDITION_VSWITCH_DESTINATION_VM_ID) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_EGRESS_VSWITCH_TRANSPORT_V4_VSWITCH_DESTINATION_VM_ID].value); + else if(pConditionKey == &FWPM_CONDITION_L2_FLAGS) + pValue = &(pClassifyValues->incomingValue[FWPS_FIELD_EGRESS_VSWITCH_TRANSPORT_V4_L2_FLAGS].value); + + break; + } + +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpValueGetFromFwpsIncomingValues() [pValue: %#p]\n", + pValue); + +#endif /// DBG + + return pValue; +} + +/** + @kernel_helper_function="KrnlHlprFwpValuePurgeLocalCopy" + + Purpose: Cleanup a local copy of an FWP_VALUE.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552450.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprFwpValuePurgeLocalCopy(_Inout_ FWP_VALUE* pValue) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpValuePurgeLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(pValue); + + switch(pValue->type) + { + case FWP_UINT64: + { + HLPR_DELETE(pValue->uint64, + WFPSAMPLER_SYSLIB_TAG); + + break; + } + case FWP_INT64: + { + HLPR_DELETE(pValue->int64, + WFPSAMPLER_SYSLIB_TAG); + + break; + } + case FWP_DOUBLE: + { + HLPR_DELETE(pValue->double64, + WFPSAMPLER_SYSLIB_TAG); + + break; + } + case FWP_BYTE_ARRAY16_TYPE: + { + HLPR_DELETE(pValue->byteArray16, + WFPSAMPLER_SYSLIB_TAG); + + break; + } + case FWP_BYTE_BLOB_TYPE: + { + KrnlHlprFwpByteBlobDestroyLocalCopy(&(pValue->byteBlob)); + + break; + } + case FWP_SID: + { + HLPR_DELETE_ARRAY(pValue->sid, + WFPSAMPLER_SYSLIB_TAG); + + break; + } + case FWP_SECURITY_DESCRIPTOR_TYPE: + { + KrnlHlprFwpByteBlobDestroyLocalCopy(&(pValue->sd)); + + break; + } + case FWP_TOKEN_INFORMATION_TYPE: + { + KrnlHlprFwpTokenInformationDestroyLocalCopy(&(pValue->tokenInformation)); + + break; + } + case FWP_TOKEN_ACCESS_INFORMATION_TYPE: + { + KrnlHlprFwpByteBlobDestroyLocalCopy(&(pValue->tokenAccessInformation)); + + break; + } + case FWP_UNICODE_STRING_TYPE: + { + HLPR_DELETE_ARRAY(pValue->unicodeString, + WFPSAMPLER_SYSLIB_TAG); + + break; + } + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + case FWP_BYTE_ARRAY6_TYPE: + { + HLPR_DELETE(pValue->byteArray6, + WFPSAMPLER_SYSLIB_TAG); + + break; + } + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + default: + { + pValue->uint64 = 0; + + break; + } + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpValuePurgeLocalCopy()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprFwpValueDestroyLocalCopy" + + Purpose: Cleanup and free a local copy of an FWP_VALUE.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552450.aspx
+*/ +_At_(*ppValue, _Pre_ _Notnull_) +_At_(*ppValue, _Post_ _Null_ __drv_freesMem(Pool)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(*ppValue == 0) +inline VOID KrnlHlprFwpValueDestroyLocalCopy(_Inout_ FWP_VALUE** ppValue) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpValueDestroyLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(ppValue); + + if(*ppValue) + KrnlHlprFwpValuePurgeLocalCopy(*ppValue); + + HLPR_DELETE(*ppValue, + WFPSAMPLER_SYSLIB_TAG); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpValueDestroyLocalCopy()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprFwpValuePopulateLocalCopy" + + Purpose: Populate a local copy of an FWP_VALUE.
+
+ Notes: The caller is responsible for freeing any allocated memory using + KrnlHlprFwpValuePurgeLocalCopy().
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552450.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprFwpValuePopulateLocalCopy(_In_ const FWP_VALUE* pOriginalValue, + _Inout_ FWP_VALUE* pValue) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpValuePopulateLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(pOriginalValue); + NT_ASSERT(pValue); + + NTSTATUS status = STATUS_SUCCESS; + KIRQL irql = KeGetCurrentIrql(); + + pValue->type = pOriginalValue->type; + + switch(pValue->type) + { + case FWP_UINT64: + { + HLPR_NEW(pValue->uint64, + UINT64, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pValue->uint64, + status); + + RtlCopyMemory(pValue->uint64, + pOriginalValue->uint64, + sizeof(UINT64)); + + break; + } + case FWP_INT64: + { + HLPR_NEW(pValue->int64, + INT64, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pValue->int64, + status); + + RtlCopyMemory(pValue->int64, + pOriginalValue->int64, + sizeof(INT64)); + + break; + } + case FWP_DOUBLE: + { + HLPR_NEW(pValue->double64, + double, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pValue->double64, + status); + + RtlCopyMemory(pValue->double64, + pOriginalValue->double64, + sizeof(double)); + + break; + } + case FWP_BYTE_ARRAY16_TYPE: + { + HLPR_NEW(pValue->byteArray16, + FWP_BYTE_ARRAY16, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pValue->byteArray16, + status); + + RtlCopyMemory(pValue->byteArray16, + pOriginalValue->byteArray16, + sizeof(FWP_BYTE_ARRAY16)); + + break; + } + case FWP_BYTE_BLOB_TYPE: + { + pValue->byteBlob = KrnlHlprFwpByteBlobCreateLocalCopy(pOriginalValue->byteBlob); + HLPR_BAIL_ON_ALLOC_FAILURE(pValue->byteBlob, + status); + + break; + } + case FWP_SID: + { + HLPR_DELETE_ARRAY(pValue->sid, + WFPSAMPLER_SYSLIB_TAG); + + break; + } + case FWP_SECURITY_DESCRIPTOR_TYPE: + { + pValue->sd = KrnlHlprFwpByteBlobCreateLocalCopy(pOriginalValue->sd); + HLPR_BAIL_ON_ALLOC_FAILURE(pValue->sd, + status); + break; + } + case FWP_TOKEN_INFORMATION_TYPE: + { + pValue->tokenInformation = KrnlHlprFwpTokenInformationCreateLocalCopy(pOriginalValue->tokenInformation); + HLPR_BAIL_ON_ALLOC_FAILURE(pValue->tokenInformation, + status); + + break; + } + case FWP_TOKEN_ACCESS_INFORMATION_TYPE: + { + pValue->tokenAccessInformation = KrnlHlprFwpByteBlobCreateLocalCopy(pOriginalValue->tokenAccessInformation); + HLPR_BAIL_ON_ALLOC_FAILURE(pValue->tokenAccessInformation, + status); + + break; + } + case FWP_UNICODE_STRING_TYPE: + { + size_t stringSize = 0; + + if(irql == PASSIVE_LEVEL) + { + status = RtlStringCchLengthW(pOriginalValue->unicodeString, + NTSTRSAFE_MAX_CCH, + &stringSize); + HLPR_BAIL_ON_FAILURE(status); + } + else + stringSize = wcslen(pOriginalValue->unicodeString); + + if(stringSize) + { + HLPR_NEW_ARRAY(pValue->unicodeString, + WCHAR, + stringSize + 1, /// add space for '\0' + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pValue->unicodeString, + status); + +#pragma warning(push) +#pragma warning(disable: 26018) /// buffers are contrained by stringSize + + RtlCopyMemory(pValue->unicodeString, + pOriginalValue->unicodeString, + stringSize * sizeof(WCHAR)); + + pValue->unicodeString[stringSize] = L'\0'; + +#pragma warning(pop) + } + else + { + status = STATUS_INVALID_BUFFER_SIZE; + + HLPR_BAIL; + } + + break; + } + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + case FWP_BYTE_ARRAY6_TYPE: + { + HLPR_NEW(pValue->byteArray6, + FWP_BYTE_ARRAY6, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pValue->byteArray6, + status); + + RtlCopyMemory(pValue->byteArray6, + pOriginalValue->byteArray6, + sizeof(FWP_BYTE_ARRAY6)); + + break; + } + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + default: + { + /// this is a straight value copy so it's safe to use the largest static space + pValue->uint64 = pOriginalValue->uint64; + + break; + } + } + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS) + KrnlHlprFwpValuePurgeLocalCopy(pValue); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpValuePopulateLocalCopy() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @kernel_helper_function="KrnlHlprFwpValueCreateLocalCopy" + + Purpose: Allocate and populate a local copy of an FWP_VALUE.
+
+ Notes: The caller is responsible for freeing the allocated memory using + KrnlHlprFwpValueDestroyLocalCopy().
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552450.aspx
+*/ +__drv_allocatesMem(Pool) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Must_inspect_result_ +_Success_(return != 0) +FWP_VALUE* KrnlHlprFwpValueCreateLocalCopy(_In_ const FWP_VALUE* pOriginalValue) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpValueCreateLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(pOriginalValue); + + NTSTATUS status = STATUS_SUCCESS; + FWP_VALUE* pValue = 0; + +#pragma warning(push) +#pragma warning(disable: 6014) /// pValue is expected to be cleaned up by caller using KrnlHlprFwpValueDestroyLocalCopy + + HLPR_NEW(pValue, + FWP_VALUE, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pValue, + status); + +#pragma warning(pop) + + status = KrnlHlprFwpValuePopulateLocalCopy(pOriginalValue, + pValue); + + HLPR_BAIL_LABEL: + +#pragma warning(push) +#pragma warning(disable: 6001) /// pValue initialized with call to HLPR_NEW & KrnlHlprFwpValuePopulateLocalCopy + + if(status != STATUS_SUCCESS && + pValue) + KrnlHlprFwpValueDestroyLocalCopy(&pValue); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpValueCreateLocalCopy() [pValue: %#p]\n", + pValue); + +#endif /// DBG + + return pValue; + +#pragma warning(pop) +} + +#endif /// FWP_VALUE____ + +#ifndef FWP_V4_ADDR_AND_MASK____ +#define FWP_V4_ADDR_AND_MASK____ + +/** + @kernel_helper_function="KrnlHlprFwpV4AddrAndMaskPurgeLocalCopy" + + Purpose: Cleanup a local copy of an FWP_V4_ADDR_AND_MASK.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552441.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprFwpV4AddrAndMaskPurgeLocalCopy(_Inout_ FWP_V4_ADDR_AND_MASK* pAddrMask) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpV4AddrAndMaskPurgeLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(pAddrMask); + + RtlZeroMemory(pAddrMask, + sizeof(FWP_V4_ADDR_AND_MASK)); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpV4AddrAndMaskPurgeLocalCopy()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprFwpV4AddrAndMaskDestroyLocalCopy" + + Purpose: Cleanup and free a local copy of an FWP_V4_ADDR_AND_MASK.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552441.aspx
+*/ +_At_(*ppAddrMask, _Pre_ _Notnull_) +_At_(*ppAddrMask, _Post_ _Null_ __drv_freesMem(Pool)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(*ppAddrMask == 0) +inline VOID KrnlHlprFwpV4AddrAndMaskDestroyLocalCopy(_Inout_ FWP_V4_ADDR_AND_MASK** ppAddrMask) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpV4AddrAndMaskDestroyLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(ppAddrMask); + + if(*ppAddrMask) + KrnlHlprFwpV4AddrAndMaskPurgeLocalCopy(*ppAddrMask); + + HLPR_DELETE(*ppAddrMask, + WFPSAMPLER_SYSLIB_TAG); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpV4AddrAndMaskDestroyLocalCopy()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprFwpV4AddrAndMaskPopulateLocalCopy" + + Purpose: Populate a local copy of an FWP_V4_ADDR_AND_MASK.
+
+ Notes: The caller is responsible for freeing the allocated memory using + KrnlHlprFwpV4AddrMaskPurgeLocalCopy().
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552441.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprFwpV4AddrAndMaskPopulateLocalCopy(_In_ const FWP_V4_ADDR_AND_MASK* pOriginalAddrMask, + _Inout_ FWP_V4_ADDR_AND_MASK* pAddrMask) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpV4AddrAndMaskPopulateLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(pOriginalAddrMask); + NT_ASSERT(pAddrMask); + + NTSTATUS status = STATUS_SUCCESS; + + pAddrMask->addr = pOriginalAddrMask->addr; + pAddrMask->mask = pOriginalAddrMask->mask; + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpV4AddrAndMaskPopulateLocalCopy() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @kernel_helper_function="KrnlHlprFwpV4AddrAndMaskCreateLocalCopy" + + Purpose: Allocate and populate a local copy of an FWP_V4_ADDR_AND_MASK.
+
+ Notes: The caller is responsible for freeing the allocated memory using + KrnlHlprFwpV4AddrMaskDestroyLocalCopy().
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552441.aspx
+*/ +__drv_allocatesMem(Pool) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Must_inspect_result_ +_Success_(return != 0) +FWP_V4_ADDR_AND_MASK* KrnlHlprFwpV4AddrAndMaskCreateLocalCopy(_In_ const FWP_V4_ADDR_AND_MASK* pOriginalAddrMask) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpV4AddrAndMaskCreateLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(pOriginalAddrMask); + + NTSTATUS status = STATUS_SUCCESS; + FWP_V4_ADDR_AND_MASK* pAddrMask = 0; + +#pragma warning(push) +#pragma warning(disable: 6014) /// pAddrMask is expected to be cleaned up by caller using KrnlHlprFwpV4AddrAndMaskDestroyLocalCopy + + HLPR_NEW(pAddrMask, + FWP_V4_ADDR_AND_MASK, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pAddrMask, + status); + +#pragma warning(pop) + + status = KrnlHlprFwpV4AddrAndMaskPopulateLocalCopy(pOriginalAddrMask, + pAddrMask); + + HLPR_BAIL_LABEL: + +#pragma warning(push) +#pragma warning(disable: 6001) /// pAddrMask initialized with call to HLPR_NEW & KrnlHlprFwpV4AddrAndMaskPopulateLocalCopy + + if(status != STATUS_SUCCESS && + pAddrMask) + KrnlHlprFwpV4AddrAndMaskDestroyLocalCopy(&pAddrMask); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpV4AddrAndMaskCreateLocalCopy() [pAddrMask: %#p]\n", + pAddrMask); + +#endif /// DBG + + return pAddrMask; + +#pragma warning(pop) +} + +#endif /// FWP_V4_ADDR_AND_MASK____ + +#ifndef FWP_V6_ADDR_AND_MASK____ +#define FWP_V6_ADDR_AND_MASK____ + +/** + @kernel_helper_function="KrnlHlprFwpV6AddrAndMaskPurgeLocalCopy" + + Purpose: Cleanup a local copy of an FWP_V6_ADDR_AND_MASK.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552446.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprFwpV6AddrAndMaskPurgeLocalCopy(_Inout_ FWP_V6_ADDR_AND_MASK* pAddrMask) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpV6AddrAndMaskPurgeLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(pAddrMask); + + RtlZeroMemory(pAddrMask, + sizeof(FWP_V6_ADDR_AND_MASK)); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpV6AddrAndMaskPurgeLocalCopy()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprFwpV6AddrAndMaskDestroyLocalCopy" + + Purpose: Cleanup and free a local copy of an FWP_V6_ADDR_AND_MASK.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552446.aspx
+*/ +_At_(*ppAddrMask, _Pre_ _Notnull_) +_At_(*ppAddrMask, _Post_ _Null_ __drv_freesMem(Pool)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(*ppAddrMask == 0) +inline VOID KrnlHlprFwpV6AddrAndMaskDestroyLocalCopy(_Inout_ FWP_V6_ADDR_AND_MASK** ppAddrMask) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpV6AddrAndMaskDestroyLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(ppAddrMask); + + if(*ppAddrMask) + KrnlHlprFwpV6AddrAndMaskPurgeLocalCopy(*ppAddrMask); + + HLPR_DELETE(*ppAddrMask, + WFPSAMPLER_SYSLIB_TAG); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpV6AddrAndMaskDestroyLocalCopy()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprFwpV6AddrAndMaskPopulateLocalCopy" + + Purpose: Populate a local copy of an FWP_V6_ADDR_AND_MASK.
+
+ Notes: The caller is responsible for freeing the allocated memory using + KrnlHlprFwpV6AddrMaskPurgeLocalCopy().
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552446.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprFwpV6AddrAndMaskPopulateLocalCopy(_In_ const FWP_V6_ADDR_AND_MASK* pOriginalAddrMask, + _Inout_ FWP_V6_ADDR_AND_MASK* pAddrMask) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpV6AddrAndMaskPopulateLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(pOriginalAddrMask); + NT_ASSERT(pAddrMask); + + NTSTATUS status = STATUS_SUCCESS; + + RtlCopyMemory(pAddrMask->addr, + pOriginalAddrMask->addr, + FWP_V6_ADDR_SIZE); + + pAddrMask->prefixLength = pOriginalAddrMask->prefixLength; + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpV6AddrAndMaskPopulateLocalCopy() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @kernel_helper_function="KrnlHlprFwpV6AddrAndMaskCreateLocalCopy" + + Purpose: Allocate and populate a local copy of an FWP_V6_ADDR_AND_MASK.
+
+ Notes: The caller is responsible for freeing the allocated memory using + KrnlHlprFwpV6AddrMaskDestroyLocalCopy().
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552446.aspx
+*/ +__drv_allocatesMem(Pool) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Must_inspect_result_ +_Success_(return != 0) +FWP_V6_ADDR_AND_MASK* KrnlHlprFwpV6AddrAndMaskCreateLocalCopy(_In_ const FWP_V6_ADDR_AND_MASK* pOriginalAddrMask) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpV6AddrAndMaskCreateLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(pOriginalAddrMask); + + NTSTATUS status = STATUS_SUCCESS; + FWP_V6_ADDR_AND_MASK* pAddrMask = 0; + +#pragma warning(push) +#pragma warning(disable: 6014) /// pAddrMask is expected to be cleaned up by caller using KrnlHlprFwpV6AddrAndMaskDestroyLocalCopy + + HLPR_NEW(pAddrMask, + FWP_V6_ADDR_AND_MASK, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pAddrMask, + status); + +#pragma warning(pop) + + status = KrnlHlprFwpV6AddrAndMaskPopulateLocalCopy(pOriginalAddrMask, + pAddrMask); + + HLPR_BAIL_LABEL: + +#pragma warning(push) +#pragma warning(disable: 6001) /// pAddrMask initialized with call to HLPR_NEW & KrnlHlprFwpV6AddrAndMaskPopulateLocalCopy + + if(status != STATUS_SUCCESS && + pAddrMask) + KrnlHlprFwpV6AddrAndMaskDestroyLocalCopy(&pAddrMask); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpV6AddrAndMaskCreateLocalCopy() [pAddrMask: %#p]\n", + pAddrMask); + +#endif /// DBG + + return pAddrMask; + +#pragma warning(pop) +} + +#endif /// FWP_V6_ADDR_AND_MASK____ + +#ifndef FWP_RANGE____ +#define FWP_RANGE____ + +/** + @kernel_helper_function="KrnlHlprFwpRangePurgeLocalCopy" + + Purpose: Cleanup a local copy of an FWP_RANGE.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552438.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprFwpRangePurgeLocalCopy(_Inout_ FWP_RANGE* pRange) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpRangePurgeLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(pRange); + + KrnlHlprFwpValuePurgeLocalCopy(&(pRange->valueLow)); + + KrnlHlprFwpValuePurgeLocalCopy(&(pRange->valueHigh)); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpRangePurgeLocalCopy()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprFwpRangeDestroyLocalCopy" + + Purpose: Cleanup and free a local copy of an FWP_RANGE.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552438.aspx
+*/ +_At_(*ppRange, _Pre_ _Notnull_) +_At_(*ppRange, _Post_ _Null_ __drv_freesMem(Pool)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(*ppRange == 0) +inline VOID KrnlHlprFwpRangeDestroyLocalCopy(_Inout_ FWP_RANGE** ppRange) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpRangeDestroyLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(ppRange); + + if(*ppRange) + KrnlHlprFwpRangePurgeLocalCopy(*ppRange); + + HLPR_DELETE(*ppRange, + WFPSAMPLER_SYSLIB_TAG); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpRangeDestroyLocalCopy()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprFwpRangePopulateLocalCopy" + + Purpose: Populate a local copy of an FWP_RANGE.
+
+ Notes: The caller is responsible for freeing the allocated memory using + KrnlHlprFwpRangePurgeLocalCopy().
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552438.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprFwpRangePopulateLocalCopy(_In_ const FWP_RANGE* pOriginalRange, + _Inout_ FWP_RANGE* pRange) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpRangePopulateLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(pOriginalRange); + NT_ASSERT(pRange); + + NTSTATUS status = STATUS_SUCCESS; + + status = KrnlHlprFwpValuePopulateLocalCopy(&(pOriginalRange->valueLow), + &(pRange->valueLow)); + HLPR_BAIL_ON_FAILURE(status); + + status = KrnlHlprFwpValuePopulateLocalCopy(&(pOriginalRange->valueHigh), + &(pRange->valueHigh)); + HLPR_BAIL_ON_FAILURE(status); + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS) + KrnlHlprFwpRangePurgeLocalCopy(pRange); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpRangePopulateLocalCopy() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @kernel_helper_function="KrnlHlprFwpRangeCreateLocalCopy" + + Purpose: Allocate and populate a local copy of an FWP_RANGE.
+
+ Notes: The caller is responsible for freeing the allocated memory using + KrnlHlprFwpRangeDestroyLocalCopy().
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552438.aspx
+*/ +__drv_allocatesMem(Pool) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Must_inspect_result_ +_Success_(return != 0) +FWP_RANGE* KrnlHlprFwpRangeCreateLocalCopy(_In_ const FWP_RANGE* pOriginalRange) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpRangeCreateLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(pOriginalRange); + + NTSTATUS status = STATUS_SUCCESS; + FWP_RANGE* pRange = 0; + +#pragma warning(push) +#pragma warning(disable: 6014) /// pRange is expected to be cleaned up by caller using KrnlHlprFwpRangeDestroyLocalCopy + + HLPR_NEW(pRange, + FWP_RANGE, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pRange, + status); + +#pragma warning(pop) + + status = KrnlHlprFwpValuePopulateLocalCopy(&(pOriginalRange->valueLow), + &(pRange->valueLow)); + HLPR_BAIL_ON_FAILURE(status); + + status = KrnlHlprFwpValuePopulateLocalCopy(&(pOriginalRange->valueHigh), + &(pRange->valueHigh)); + HLPR_BAIL_ON_FAILURE(status); + + HLPR_BAIL_LABEL: + +#pragma warning(push) +#pragma warning(disable: 6001) /// pRange initialized with call to HLPR_NEW & KrnlHlprFwpValuePopulateLocalCopy + + if(status != STATUS_SUCCESS && + pRange) + KrnlHlprFwpRangeDestroyLocalCopy(&pRange); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpRangeCreateLocalCopy() [pRange: %#p]\n", + pRange); + +#endif /// DBG + + return pRange; + +#pragma warning(pop) +} + +#endif /// FWP_RANGE____ + +#ifndef FWP_CONDITION_VALUE____ +#define FWP_CONDITION_VALUE____ + +/** + @kernel_helper_function="KrnlHlprFwpConditionValuePurgeLocalCopy" + + Purpose: Cleanup a local copy of an FWP_CONDITION_VALUE.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552430.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprFwpConditionValuePurgeLocalCopy(_Inout_ FWP_CONDITION_VALUE* pValue) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpConditionValuePurgeLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(pValue); + + switch(pValue->type) + { + case FWP_UINT64: + { + HLPR_DELETE(pValue->uint64, + WFPSAMPLER_SYSLIB_TAG); + + break; + } + case FWP_INT64: + { + HLPR_DELETE(pValue->int64, + WFPSAMPLER_SYSLIB_TAG); + + break; + } + case FWP_DOUBLE: + { + HLPR_DELETE(pValue->double64, + WFPSAMPLER_SYSLIB_TAG); + + break; + } + case FWP_BYTE_ARRAY16_TYPE: + { + HLPR_DELETE(pValue->byteArray16, + WFPSAMPLER_SYSLIB_TAG); + + break; + } + case FWP_BYTE_BLOB_TYPE: + { + KrnlHlprFwpByteBlobDestroyLocalCopy(&(pValue->byteBlob)); + + break; + } + case FWP_SID: + { + HLPR_DELETE_ARRAY(pValue->sid, + WFPSAMPLER_SYSLIB_TAG); + + break; + } + case FWP_SECURITY_DESCRIPTOR_TYPE: + { + KrnlHlprFwpByteBlobDestroyLocalCopy(&(pValue->sd)); + + break; + } + case FWP_TOKEN_INFORMATION_TYPE: + { + KrnlHlprFwpTokenInformationDestroyLocalCopy(&(pValue->tokenInformation)); + + break; + } + case FWP_TOKEN_ACCESS_INFORMATION_TYPE: + { + KrnlHlprFwpByteBlobDestroyLocalCopy(&(pValue->tokenAccessInformation)); + + break; + } + case FWP_UNICODE_STRING_TYPE: + { + HLPR_DELETE_ARRAY(pValue->unicodeString, + WFPSAMPLER_SYSLIB_TAG); + + break; + } + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + case FWP_BYTE_ARRAY6_TYPE: + { + HLPR_DELETE(pValue->byteArray6, + WFPSAMPLER_SYSLIB_TAG); + + break; + } + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + case FWP_SINGLE_DATA_TYPE_MAX: + { + break; + } + case FWP_V4_ADDR_MASK: + { + KrnlHlprFwpV4AddrAndMaskDestroyLocalCopy(&(pValue->v4AddrMask)); + + break; + } + case FWP_V6_ADDR_MASK: + { + KrnlHlprFwpV6AddrAndMaskDestroyLocalCopy(&(pValue->v6AddrMask)); + + break; + } + case FWP_RANGE_TYPE: + { + KrnlHlprFwpRangeDestroyLocalCopy(&(pValue->rangeValue)); + + break; + } + case FWP_DATA_TYPE_MAX: + { + break; + } + default: + { + pValue->uint64 = 0; + + break; + } + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpConditionValuePurgeLocalCopy()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprFwpConditionValueDestroyLocalCopy" + + Purpose: Cleanup and free a local copy of an FWP_CONDITION_VALUE.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552430.aspx
+*/ +_At_(*ppValue, _Pre_ _Notnull_) +_At_(*ppValue, _Post_ _Null_ __drv_freesMem(Pool)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(*ppValue == 0) +inline VOID KrnlHlprFwpConditionValueDestroyLocalCopy(_Inout_ FWP_CONDITION_VALUE** ppValue) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpConditionValueDestroyLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(ppValue); + + if(*ppValue) + KrnlHlprFwpConditionValuePurgeLocalCopy(*ppValue); + + HLPR_DELETE(*ppValue, + WFPSAMPLER_SYSLIB_TAG); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpConditionValueDestroyLocalCopy()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprFwpConditionValuePopulateLocalCopy" + + Purpose: Populate a local copy of an FWP_CONDITION_VALUE.
+
+ Notes: The caller is responsible for freeing any allocated memory using + KrnlHlprFwpConditionValuePurgeLocalCopy().
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552430.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprFwpConditionValuePopulateLocalCopy(_In_ const FWP_CONDITION_VALUE* pOriginalValue, + _Inout_ FWP_CONDITION_VALUE* pValue) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpConditionValuePopulateLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(pOriginalValue); + NT_ASSERT(pValue); + + NTSTATUS status = STATUS_SUCCESS; + + KIRQL irql = KeGetCurrentIrql(); + + pValue->type = pOriginalValue->type; + + switch(pValue->type) + { + case FWP_UINT64: + { + HLPR_NEW(pValue->uint64, + UINT64, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pValue->uint64, + status); + + RtlCopyMemory(pValue->uint64, + pOriginalValue->uint64, + sizeof(UINT64)); + + break; + } + case FWP_INT64: + { + HLPR_NEW(pValue->int64, + INT64, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pValue->int64, + status); + + RtlCopyMemory(pValue->int64, + pOriginalValue->int64, + sizeof(INT64)); + + break; + } + case FWP_DOUBLE: + { + HLPR_NEW(pValue->double64, + double, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pValue->double64, + status); + + RtlCopyMemory(pValue->double64, + pOriginalValue->double64, + sizeof(double)); + + break; + } + case FWP_BYTE_ARRAY16_TYPE: + { + HLPR_NEW(pValue->byteArray16, + FWP_BYTE_ARRAY16, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pValue->byteArray16, + status); + + RtlCopyMemory(pValue->byteArray16, + pOriginalValue->byteArray16, + sizeof(FWP_BYTE_ARRAY16)); + + break; + } + case FWP_BYTE_BLOB_TYPE: + { + pValue->byteBlob = KrnlHlprFwpByteBlobCreateLocalCopy(pOriginalValue->byteBlob); + HLPR_BAIL_ON_ALLOC_FAILURE(pValue->byteBlob, + status); + + break; + } + case FWP_SID: + { + HLPR_DELETE_ARRAY(pValue->sid, + WFPSAMPLER_SYSLIB_TAG); + + break; + } + case FWP_SECURITY_DESCRIPTOR_TYPE: + { + pValue->sd = KrnlHlprFwpByteBlobCreateLocalCopy(pOriginalValue->sd); + HLPR_BAIL_ON_ALLOC_FAILURE(pValue->sd, + status); + break; + } + case FWP_TOKEN_INFORMATION_TYPE: + { + pValue->tokenInformation = KrnlHlprFwpTokenInformationCreateLocalCopy(pOriginalValue->tokenInformation); + HLPR_BAIL_ON_ALLOC_FAILURE(pValue->tokenInformation, + status); + + break; + } + case FWP_TOKEN_ACCESS_INFORMATION_TYPE: + { + pValue->tokenAccessInformation = KrnlHlprFwpByteBlobCreateLocalCopy(pOriginalValue->tokenAccessInformation); + HLPR_BAIL_ON_ALLOC_FAILURE(pValue->tokenAccessInformation, + status); + + break; + } + case FWP_UNICODE_STRING_TYPE: + { + size_t stringSize = 0; + + if(irql == PASSIVE_LEVEL) + { + status = RtlStringCchLengthW(pOriginalValue->unicodeString, + NTSTRSAFE_MAX_CCH, + &stringSize); + HLPR_BAIL_ON_FAILURE(status); + } + else + stringSize = wcslen(pOriginalValue->unicodeString); + + if(stringSize) + { + HLPR_NEW_ARRAY(pValue->unicodeString, + WCHAR, + stringSize + 1, /// add space for '\0' + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pValue->unicodeString, + status); + +#pragma warning(push) +#pragma warning(disable: 26018) /// buffers are contrained by stringSize + + RtlCopyMemory(pValue->unicodeString, + pOriginalValue->unicodeString, + stringSize * sizeof(WCHAR)); + + pValue->unicodeString[stringSize] = L'\0'; + +#pragma warning(pop) + } + else + { + status = STATUS_INVALID_BUFFER_SIZE; + + HLPR_BAIL; + } + + break; + } + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + case FWP_BYTE_ARRAY6_TYPE: + { + HLPR_NEW(pValue->byteArray6, + FWP_BYTE_ARRAY6, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pValue->byteArray6, + status); + + RtlCopyMemory(pValue->byteArray6, + pOriginalValue->byteArray6, + sizeof(FWP_BYTE_ARRAY6)); + + break; + } + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + case FWP_SINGLE_DATA_TYPE_MAX: + { + status = STATUS_INVALID_LABEL; + + break; + } + case FWP_V4_ADDR_MASK: + { + pValue->v4AddrMask = KrnlHlprFwpV4AddrAndMaskCreateLocalCopy(pOriginalValue->v4AddrMask); + HLPR_BAIL_ON_ALLOC_FAILURE(pValue->v4AddrMask, + status); + + break; + } + case FWP_V6_ADDR_MASK: + { + pValue->v6AddrMask = KrnlHlprFwpV6AddrAndMaskCreateLocalCopy(pOriginalValue->v6AddrMask); + HLPR_BAIL_ON_ALLOC_FAILURE(pValue->v6AddrMask, + status); + + break; + } + case FWP_RANGE_TYPE: + { + pValue->rangeValue = KrnlHlprFwpRangeCreateLocalCopy(pOriginalValue->rangeValue); + HLPR_BAIL_ON_ALLOC_FAILURE(pValue->rangeValue, + status); + + break; + } + case FWP_DATA_TYPE_MAX: + { + status = STATUS_INVALID_LABEL; + + break; + } + default: + { + /// this is a straight value copy so it's safe to use the largest static space + pValue->uint64 = pOriginalValue->uint64; + + break; + } + } + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS) + KrnlHlprFwpConditionValuePurgeLocalCopy(pValue); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpConditionValuePopulateLocalCopy() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @kernel_helper_function="KrnlHlprFwpConditionValueCreateLocalCopy" + + Purpose: Allocate ans populate a local copy of an FWP_CONDITION_VALUE.
+
+ Notes: The caller is responsible for freeing any allocated memory using + KrnlHlprFwpConditionValueDestroyLocalCopy().
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552430.aspx
+*/ +__drv_allocatesMem(Pool) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Must_inspect_result_ +_Success_(return != 0) +FWP_CONDITION_VALUE* KrnlHlprFwpConditionValueCreateLocalCopy(_In_ const FWP_CONDITION_VALUE* pOriginalValue) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpConditionValueCreateLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(pOriginalValue); + + NTSTATUS status = STATUS_SUCCESS; + FWP_CONDITION_VALUE* pConditionValue = 0; + +#pragma warning(push) +#pragma warning(disable: 6014) /// pConditionValue is expected to be cleaned up by caller using KrnlHlprFwpConditionValueDestroyLocalCopy + + HLPR_NEW(pConditionValue, + FWP_CONDITION_VALUE, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pConditionValue, + status); + +#pragma warning(push) + + status = KrnlHlprFwpConditionValuePopulateLocalCopy(pOriginalValue, + pConditionValue); + + HLPR_BAIL_LABEL: + +#pragma warning(push) +#pragma warning(disable: 6001) /// pConditionValue initialized with call to HLPR_NEW & KrnlHlprFwpConditionValuePopulateLocalCopy + + if(status != STATUS_SUCCESS && + pConditionValue) + KrnlHlprFwpConditionValueDestroyLocalCopy(&pConditionValue); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpConditionValueCreateLocalCopy() [pConditionValue: %#p]\n", + pConditionValue); + +#endif /// DBG + + return pConditionValue; + +#pragma warning(pop) +} + +#endif /// FWP_CONDITION_VALUE____ + +#ifndef FWPS_INCOMING_VALUES____ +#define FWPS_INCOMING_VALUES____ + +/** + @kernel_helper_function="KrnlHlprFwpsIncomingValueConditionFlagsAreSet" + + Purpose: Determine if the FWPS_INCOMING_VALUES has the FLAGS condition set with the given + flags.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552401.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +BOOLEAN KrnlHlprFwpsIncomingValueConditionFlagsAreSet(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ UINT32 flags) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpsIncomingValueConditionFlagsAreSet()\n"); + +#endif /// DBG + + NT_ASSERT(pClassifyValues); + + BOOLEAN flagsAreSet = FALSE; + FWP_VALUE* pFlagsValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_FLAGS); + + if(pFlagsValue && + pFlagsValue->uint32 & flags) + flagsAreSet = TRUE; + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpsIncomingValueConditionFlagsAreSet()\n"); + +#endif /// DBG + + return flagsAreSet; +} + +/** + @kernel_helper_function="KrnlHlprFwpsIncomingValuesPurgeLocalCopy" + + Purpose: Cleanup a local copy of an FWPS_INCOMING_VALUES.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552401.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprFwpsIncomingValuesPurgeLocalCopy(_Inout_ FWPS_INCOMING_VALUES* pValues) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpsIncomingValuesPurgeLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(pValues); + + for(UINT32 valueIndex = 0; + valueIndex < pValues->valueCount; + valueIndex++) + { + KrnlHlprFwpValuePurgeLocalCopy(&(pValues->incomingValue[valueIndex].value)); + } + + HLPR_DELETE_ARRAY(pValues->incomingValue, + WFPSAMPLER_SYSLIB_TAG); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpsIncomingValuesPurgeLocalCopy()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprFwpsIncomingValuesDestroyLocalCopy" + + Purpose: Cleanup and free a local copy of an FWPS_INCOMING_VALUES.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552401.aspx
+*/ +_At_(*ppValues, _Pre_ _Notnull_) +_At_(*ppValues, _Post_ _Null_ __drv_freesMem(Pool)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(*ppValues == 0) +inline VOID KrnlHlprFwpsIncomingValuesDestroyLocalCopy(_Inout_ FWPS_INCOMING_VALUES** ppValues) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpsIncomingValuesDestroyLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(ppValues); + + if(*ppValues) + KrnlHlprFwpsIncomingValuesPurgeLocalCopy(*ppValues); + + HLPR_DELETE(*ppValues, + WFPSAMPLER_SYSLIB_TAG); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpsIncomingValuesDestroyLocalCopy()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprFwpsIncomingValuesPopulateLocalCopy" + + Purpose: Populate a local copy of an FWPS_INCOMING_VALUES.
+
+ Notes: The caller is responsible for freeing the allocated memory using + KrnlHlprFwpsIncomingValuesPurgeLocalCopy().
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552401.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprFwpsIncomingValuesPopulateLocalCopy(_In_ const FWPS_INCOMING_VALUES* pOriginalValues, + _Inout_ FWPS_INCOMING_VALUES* pValues) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpsIncomingValuesPopulateLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(pOriginalValues); + NT_ASSERT(pValues); + + NTSTATUS status = STATUS_SUCCESS; + + HLPR_NEW_ARRAY(pValues->incomingValue, + FWPS_INCOMING_VALUE, + pOriginalValues->valueCount, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pValues->incomingValue, + status); + + for(UINT32 valueIndex = 0; + valueIndex < pOriginalValues->valueCount; + valueIndex++) + { + status = KrnlHlprFwpValuePopulateLocalCopy(&(pOriginalValues->incomingValue[valueIndex].value), + &(pValues->incomingValue[valueIndex].value)); + HLPR_BAIL_ON_FAILURE(status); + } + + pValues->valueCount = pOriginalValues->valueCount; + pValues->layerId = pOriginalValues->layerId; + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS) + KrnlHlprFwpsIncomingValuesPurgeLocalCopy(pValues); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpsIncomingValuesPopulateLocalCopy() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @kernel_helper_function="KrnlHlprFwpsIncomingValuesCreateLocalCopy" + + Purpose: Allocate and populate a local copy of an FWPS_INCOMING_VALUES.
+
+ Notes: The caller is responsible for freeing the allocated memory using + KrnlHlprFwpsIncomingValuesDestroyLocalCopy().
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552401.aspx
+*/ +__drv_allocatesMem(Pool) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Must_inspect_result_ +_Success_(return != 0) +FWPS_INCOMING_VALUES* KrnlHlprFwpsIncomingValuesCreateLocalCopy(_In_ const FWPS_INCOMING_VALUES* pOriginalValues) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpsIncomingValuesCreateLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(pOriginalValues); + + NTSTATUS status = STATUS_SUCCESS; + FWPS_INCOMING_VALUES* pIncomingValues = 0; + +#pragma warning(push) +#pragma warning(disable: 6014) /// pIncomingValues is expected to be cleaned up by caller using KrnlHlprFwpsIncomingValuesDestroyLocalCopy + + HLPR_NEW(pIncomingValues, + FWPS_INCOMING_VALUES, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pIncomingValues, + status); + +#pragma warning(pop) + + status = KrnlHlprFwpsIncomingValuesPopulateLocalCopy(pOriginalValues, + pIncomingValues); + + HLPR_BAIL_LABEL: + +#pragma warning(push) +#pragma warning(disable: 6001) /// pIncomingValues initialized with call to HLPR_NEW & KrnlHlprFwpsIncomingValuesPopulateLocalCopy + + if(status != STATUS_SUCCESS && + pIncomingValues) + KrnlHlprFwpsIncomingValuesDestroyLocalCopy(&pIncomingValues); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpsIncomingValuesCreateLocalCopy() [pIncomingValues: %#p]\n", + pIncomingValues); + +#endif /// DBG + + return pIncomingValues; + +#pragma warning(pop) +} + +#endif /// FWPS_INCOMING_VALUES____ + +#ifndef FWPS_INCOMING_METADATA_VALUES____ +#define FWPS_INCOMING_METADATA_VALUES____ + +/** + @kernel_helper_function="KrnlHlprFwpsIncomingMetadataValuesPurgeLocalCopy + + Purpose: Cleanup a local copy of an FWPS_INCOMING_METADATA_VALUES.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552397.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprFwpsIncomingMetadataValuesPurgeLocalCopy(_Inout_ FWPS_INCOMING_METADATA_VALUES* pMetadata) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpsIncomingMetadataValuesPurgeLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(pMetadata); + + KrnlHlprFwpByteBlobDestroyLocalCopy(&(pMetadata->processPath)); + + HLPR_DELETE_ARRAY(pMetadata->controlData, + WFPSAMPLER_SYSLIB_TAG); + +#if(NTDDI_VERSION >= NTDDI_WIN6SP1) + + HLPR_DELETE_ARRAY(pMetadata->headerIncludeHeader, + WFPSAMPLER_SYSLIB_TAG); + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + HLPR_DELETE_ARRAY(pMetadata->originalDestination, + WFPSAMPLER_SYSLIB_TAG); + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + if(pMetadata->vSwitchPacketContext) + FwpsDereferencevSwitchPacketContext(pMetadata->vSwitchPacketContext); + +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) +#endif /// (NTDDI_VERSION >= NTDDI_WIN6SP1) + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpsIncomingMetadataValuesPurgeLocalCopy()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprFwpsIncomingMetadataValuesDestroyLocalCopy + + Purpose: Cleanup and free a local copy of an FWPS_INCOMING_METADATA_VALUES.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552397.aspx
+*/ +_At_(*ppMetadata, _Pre_ _Notnull_) +_At_(*ppMetadata, _Post_ _Null_ __drv_freesMem(Pool)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(*ppMetadata == 0) +inline VOID KrnlHlprFwpsIncomingMetadataValuesDestroyLocalCopy(_Inout_ FWPS_INCOMING_METADATA_VALUES** ppMetadata) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpsIncomingMetadataValuesDestroyLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(ppMetadata); + + if(*ppMetadata) + KrnlHlprFwpsIncomingMetadataValuesPurgeLocalCopy(*ppMetadata); + + HLPR_DELETE(*ppMetadata, + WFPSAMPLER_SYSLIB_TAG); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpsIncomingMetadataValuesDestroyLocalCopy()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprFwpsIncomingMetadataValuesPopulateLocalCopy + + Purpose: Populate a local copy of the FWPS_INCOMING_METADATA_VALUES.
+
+ Notes: The caller is responsible for freeing any allocated memory using + KrnlHlprFwpsIncomingMetadataValuesPurgeLocalCopy().
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552397.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprFwpsIncomingMetadataValuesPopulateLocalCopy(_In_ const FWPS_INCOMING_METADATA_VALUES* pOriginalMetadata, + _Inout_ FWPS_INCOMING_METADATA_VALUES* pMetadata) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpsIncomingMetadataValuesPopulateLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(pOriginalMetadata); + + NTSTATUS status = STATUS_SUCCESS; + + pMetadata->currentMetadataValues = pOriginalMetadata->currentMetadataValues; + pMetadata->flags = pOriginalMetadata->flags; + + /// Used internally by the filter engine. Callout drivers should ignore this member. + if(FWPS_IS_METADATA_FIELD_PRESENT(pOriginalMetadata, + FWPS_METADATA_FIELD_RESERVED)) + pMetadata->reserved = pOriginalMetadata->reserved; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pOriginalMetadata, + FWPS_METADATA_FIELD_DISCARD_REASON)) + { + pMetadata->discardMetadata.discardModule = pOriginalMetadata->discardMetadata.discardModule; + pMetadata->discardMetadata.discardReason = pOriginalMetadata->discardMetadata.discardReason; + pMetadata->discardMetadata.filterId = pOriginalMetadata->discardMetadata.filterId; + } + + /// Not guaranteed to be available outside of the classifyFn. + /// Use of an invalid handle will fail gracefully. + if(FWPS_IS_METADATA_FIELD_PRESENT(pOriginalMetadata, + FWPS_METADATA_FIELD_FLOW_HANDLE)) + pMetadata->flowHandle = pOriginalMetadata->flowHandle; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pOriginalMetadata, + FWPS_METADATA_FIELD_IP_HEADER_SIZE)) + pMetadata->ipHeaderSize = pOriginalMetadata->ipHeaderSize; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pOriginalMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + pMetadata->transportHeaderSize = pOriginalMetadata->transportHeaderSize; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pOriginalMetadata, + FWPS_METADATA_FIELD_PROCESS_PATH)) + { + pMetadata->processPath = KrnlHlprFwpByteBlobCreateLocalCopy(pOriginalMetadata->processPath); + HLPR_BAIL_ON_ALLOC_FAILURE(pMetadata->processPath, + status); + } + + if(FWPS_IS_METADATA_FIELD_PRESENT(pOriginalMetadata, + FWPS_METADATA_FIELD_TOKEN)) + pMetadata->token = pOriginalMetadata->token; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pOriginalMetadata, + FWPS_METADATA_FIELD_PROCESS_ID)) + pMetadata->processId = pOriginalMetadata->processId; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pOriginalMetadata, + FWPS_METADATA_FIELD_SOURCE_INTERFACE_INDEX)) + pMetadata->sourceInterfaceIndex = pOriginalMetadata->sourceInterfaceIndex; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pOriginalMetadata, + FWPS_METADATA_FIELD_DESTINATION_INTERFACE_INDEX)) + pMetadata->destinationInterfaceIndex = pOriginalMetadata->destinationInterfaceIndex; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pOriginalMetadata, + FWPS_METADATA_FIELD_COMPARTMENT_ID)) + pMetadata->compartmentId = pOriginalMetadata->compartmentId; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pOriginalMetadata, + FWPS_METADATA_FIELD_FRAGMENT_DATA)) + { + pMetadata->fragmentMetadata.fragmentIdentification = pOriginalMetadata->fragmentMetadata.fragmentIdentification; + pMetadata->fragmentMetadata.fragmentOffset = pOriginalMetadata->fragmentMetadata.fragmentOffset; + pMetadata->fragmentMetadata.fragmentLength = pOriginalMetadata->fragmentMetadata.fragmentLength; + } + + if(FWPS_IS_METADATA_FIELD_PRESENT(pOriginalMetadata, + FWPS_METADATA_FIELD_PATH_MTU)) + pMetadata->pathMtu = pOriginalMetadata->pathMtu; + + /// Not guaranteed to be available outside of the classifyFn. + /// Use of an invalid handle will fail gracefully. + if(FWPS_IS_METADATA_FIELD_PRESENT(pOriginalMetadata, + FWPS_METADATA_FIELD_COMPLETION_HANDLE)) + pMetadata->completionHandle = pOriginalMetadata->completionHandle; + + /// Not guaranteed to be available outside of the classifyFn. + /// Use of an invalid handle will fail gracefully. + if(FWPS_IS_METADATA_FIELD_PRESENT(pOriginalMetadata, + FWPS_METADATA_FIELD_TRANSPORT_ENDPOINT_HANDLE)) + pMetadata->transportEndpointHandle = pOriginalMetadata->transportEndpointHandle; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pOriginalMetadata, + FWPS_METADATA_FIELD_REMOTE_SCOPE_ID)) + pMetadata->remoteScopeId = pOriginalMetadata->remoteScopeId; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pOriginalMetadata, + FWPS_METADATA_FIELD_TRANSPORT_CONTROL_DATA)) + { + BYTE* pControlData = 0; + + HLPR_NEW_ARRAY(pControlData, + BYTE, + pOriginalMetadata->controlDataLength, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pControlData, + status); + + RtlCopyMemory(pControlData, + pOriginalMetadata->controlData, + pOriginalMetadata->controlDataLength); + + pMetadata->controlData = (WSACMSGHDR*)pControlData; + pMetadata->controlDataLength = pOriginalMetadata->controlDataLength; + } + + if(FWPS_IS_METADATA_FIELD_PRESENT(pOriginalMetadata, + FWPS_METADATA_FIELD_PACKET_DIRECTION)) + pMetadata->packetDirection= pOriginalMetadata->packetDirection; + +#if(NTDDI_VERSION >= NTDDI_WIN6SP1) + + if(FWPS_IS_METADATA_FIELD_PRESENT(pOriginalMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_INCLUDE_HEADER)) + { + HLPR_NEW_ARRAY(pMetadata->headerIncludeHeader, + BYTE, + pOriginalMetadata->headerIncludeHeaderLength, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pMetadata->headerIncludeHeader, + status); + + RtlCopyMemory(pMetadata->headerIncludeHeader, + pOriginalMetadata->headerIncludeHeader, + pOriginalMetadata->headerIncludeHeaderLength); + + pMetadata->headerIncludeHeaderLength = pOriginalMetadata->headerIncludeHeaderLength; + } + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + if(FWPS_IS_METADATA_FIELD_PRESENT(pOriginalMetadata, + FWPS_METADATA_FIELD_DESTINATION_PREFIX)) + pMetadata->destinationPrefix = pOriginalMetadata->destinationPrefix; + + /// Not guaranteed to be available outside of the classifyFn. + /// Use of an invalid handle will fail gracefully. + if(FWPS_IS_METADATA_FIELD_PRESENT(pOriginalMetadata, + FWPS_METADATA_FIELD_PARENT_ENDPOINT_HANDLE)) + pMetadata->parentEndpointHandle = pOriginalMetadata->parentEndpointHandle; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pOriginalMetadata, + FWPS_METADATA_FIELD_ICMP_ID_AND_SEQUENCE)) + pMetadata->icmpIdAndSequence = pOriginalMetadata->icmpIdAndSequence; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pOriginalMetadata, + FWPS_METADATA_FIELD_LOCAL_REDIRECT_TARGET_PID)) + pMetadata->localRedirectTargetPID = pOriginalMetadata->localRedirectTargetPID; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pOriginalMetadata, + FWPS_METADATA_FIELD_ORIGINAL_DESTINATION)) + { + BYTE* pDestination = 0; + SIZE_T sockAddrSize = (pOriginalMetadata->originalDestination->sa_family == AF_INET) ? + sizeof(SOCKADDR_IN) : + sizeof(SOCKADDR_IN6); + + HLPR_NEW_ARRAY(pDestination, + BYTE, + sockAddrSize, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pDestination, + status); + + RtlCopyMemory(pMetadata->originalDestination, + pOriginalMetadata->originalDestination, + sockAddrSize); + + pMetadata->originalDestination = (SOCKADDR*)pDestination; + } + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + if(FWPS_IS_METADATA_FIELD_PRESENT(pOriginalMetadata, + FWPS_METADATA_FIELD_REDIRECT_RECORD_HANDLE)) + pMetadata->redirectRecords = pOriginalMetadata->redirectRecords; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pOriginalMetadata, + FWPS_METADATA_FIELD_SUB_PROCESS_TAG)) + pMetadata->subProcessTag = pOriginalMetadata->subProcessTag; + + pMetadata->currentL2MetadataValues = pOriginalMetadata->currentL2MetadataValues; + pMetadata->l2Flags = pOriginalMetadata->l2Flags; + + if(FWPS_IS_L2_METADATA_FIELD_PRESENT(pOriginalMetadata, + FWPS_L2_METADATA_FIELD_ETHERNET_MAC_HEADER_SIZE)) + pMetadata->ethernetMacHeaderSize = pOriginalMetadata->ethernetMacHeaderSize; + + if(FWPS_IS_L2_METADATA_FIELD_PRESENT(pOriginalMetadata, + FWPS_L2_METADATA_FIELD_WIFI_OPERATION_MODE)) + pMetadata->wiFiOperationMode = pOriginalMetadata->wiFiOperationMode; + +#if(NDIS_SUPPORT_NDIS630) + + if(FWPS_IS_L2_METADATA_FIELD_PRESENT(pOriginalMetadata, + FWPS_L2_METADATA_FIELD_VSWITCH_SOURCE_PORT_ID)) + pMetadata->vSwitchSourcePortId = pOriginalMetadata->vSwitchSourcePortId; + + if(FWPS_IS_L2_METADATA_FIELD_PRESENT(pOriginalMetadata, + FWPS_L2_METADATA_FIELD_VSWITCH_SOURCE_NIC_INDEX)) + pMetadata->vSwitchSourceNicIndex = pOriginalMetadata->vSwitchSourceNicIndex; + + if(FWPS_IS_L2_METADATA_FIELD_PRESENT(pOriginalMetadata, + FWPS_L2_METADATA_FIELD_VSWITCH_DESTINATION_PORT_ID)) + pMetadata->vSwitchDestinationPortId = pOriginalMetadata->vSwitchDestinationPortId; + +#endif /// (NDIS_SUPPORT_NDIS630) + + /// Technically only need this if allocating and injecting new NBLs + if(FWPS_IS_L2_METADATA_FIELD_PRESENT(pOriginalMetadata, + FWPS_L2_METADATA_FIELD_VSWITCH_PACKET_CONTEXT)) + { + FwpsReferencevSwitchPacketContext(pOriginalMetadata->vSwitchPacketContext); + + pMetadata->vSwitchPacketContext = pOriginalMetadata->vSwitchPacketContext; + } + +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) +#endif /// (NTDDI_VERSION >= NTDDI_WIN6SP1) + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS) + KrnlHlprFwpsIncomingMetadataValuesPurgeLocalCopy(pMetadata); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpsIncomingMetadataValuesPopulateLocalCopy() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @kernel_helper_function="KrnlHlprFwpsIncomingMetadataValuesCreateLocalCopy + + Purpose: Allocate and populate a local copy of the FWPS_INCOMING_METADATA_VALUES.
+
+ Notes: The caller is responsible for freeing the allocated memory using + KrnlHlprFwpsIncomingMetadataValuesDestroyLocalCopy().
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552397.aspx
+*/ +__drv_allocatesMem(Pool) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Must_inspect_result_ +_Success_(return != 0) +FWPS_INCOMING_METADATA_VALUES* KrnlHlprFwpsIncomingMetadataValuesCreateLocalCopy(_In_ const FWPS_INCOMING_METADATA_VALUES* pOriginalMetadata) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpsIncomingMetadataValuesCreateLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(pOriginalMetadata); + + NTSTATUS status = STATUS_SUCCESS; + FWPS_INCOMING_METADATA_VALUES* pMetadata = 0; + +#pragma warning(push) +#pragma warning(disable: 6014) /// pMetadata is expected to be cleaned up by caller using KrnlHlprFwpsIncomingMetadataValuesDestroyLocalCopy + + HLPR_NEW(pMetadata, + FWPS_INCOMING_METADATA_VALUES, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pMetadata, + status); + +#pragma warning(pop) + + status = KrnlHlprFwpsIncomingMetadataValuesPopulateLocalCopy(pOriginalMetadata, + pMetadata); + + HLPR_BAIL_LABEL: + +#pragma warning(push) +#pragma warning(disable: 6001) /// pMetadata initialized with call to HLPR_NEW & KrnlHlprFwpsIncomingMetadataValuesPopulateLocalCopy + + if(status != STATUS_SUCCESS && + pMetadata) + KrnlHlprFwpsIncomingMetadataValuesDestroyLocalCopy(&pMetadata); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpsIncomingMetadataValuesCreateLocalCopy() [pMetadata: %#p]\n", + pMetadata); + +#endif /// DBG + + return pMetadata; + +#pragma warning(pop) +} + +#endif /// FWPS_INCOMING_METADATA_VALUES____ + +#ifndef FWPS_CLASSIFY_OUT____ +#define FWPS_CLASSIFY_OUT____ + +/** + @kernel_helper_function="KrnlHlprFwpsClassifyOutPurgeLocalCopy" + + Purpose: Cleanup a local copy of an FWPS_CLASSIFY_OUT.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551229.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprFwpsClassifyOutPurgeLocalCopy(_Inout_ FWPS_CLASSIFY_OUT* pOriginalClassifyOut) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpsClassifyOutPurgeLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(pOriginalClassifyOut); + + RtlZeroMemory(pOriginalClassifyOut, + sizeof(FWPS_CLASSIFY_OUT)); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpsClassifyOutPurgeLocalCopy()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprFwpsClassifyOutDestroyLocalCopy" + + Purpose: Cleanup and free a local copy of an FWPS_CLASSIFY_OUT.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551229.aspx
+*/ +_At_(*ppClassifyOut, _Pre_ _Notnull_) +_At_(*ppClassifyOut, _Post_ _Null_ __drv_freesMem(Pool)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(*ppClassifyOut == 0) +VOID KrnlHlprFwpsClassifyOutDestroyLocalCopy(_Inout_ FWPS_CLASSIFY_OUT** ppClassifyOut) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpsClassifyOutDestroyLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(ppClassifyOut); + + if(*ppClassifyOut) + KrnlHlprFwpsClassifyOutPurgeLocalCopy(*ppClassifyOut); + + HLPR_DELETE(*ppClassifyOut, + WFPSAMPLER_SYSLIB_TAG); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpsClassifyOutDestroyLocalCopy()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprFwpsClassifyOutPopulateLocalCopy" + + Purpose: Populate a local copy of the FWPS_CLASSIFY_OUT.
+
+ Notes: The caller is responsible for freeing the allocated memory using + KrnlHlprFwpsClassifyOutPurgeLocalCopy().
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551229.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprFwpsClassifyOutPopulateLocalCopy(_In_ const FWPS_CLASSIFY_OUT* pOriginalClassifyOut, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpsClassifyOutPopulateLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(pOriginalClassifyOut); + NT_ASSERT(pClassifyOut); + + RtlCopyMemory(pClassifyOut, + pOriginalClassifyOut, + sizeof(FWPS_CLASSIFY_OUT)); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpsClassifyOutPopulateLocalCopy()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprFwpsClassifyOutCreateLocalCopy" + + Purpose: Allocate and populate a local copy of the FWPS_CLASSIFY_OUT.
+
+ Notes: The caller is responsible for freeing the allocated memory using + KrnlHlprFwpsClassifyOutDestroyLocalCopy().
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551229.aspx
+*/ +__drv_allocatesMem(Pool) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Must_inspect_result_ +_Success_(return != 0) +FWPS_CLASSIFY_OUT* KrnlHlprFwpsClassifyOutCreateLocalCopy(_In_ const FWPS_CLASSIFY_OUT* pOriginalClassifyOut) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpsClassifyOutCreateLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(pOriginalClassifyOut); + + NTSTATUS status = 0; + FWPS_CLASSIFY_OUT* pClassifyOut = 0; + + HLPR_NEW(pClassifyOut, + FWPS_CLASSIFY_OUT, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pClassifyOut, + status); + + KrnlHlprFwpsClassifyOutPopulateLocalCopy(pOriginalClassifyOut, + pClassifyOut); + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! KrnlHlprFwpsClassifyOutCreateLocalCopy() [status: %#x]\n", + status); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpsClassifyOutCreateLocalCopy() [pClassifyOut: %#p]\n", + pClassifyOut); + +#endif /// DBG + + return pClassifyOut; +} + +#endif /// FWPS_CLASSIFY_OUT____ + +#ifndef FWPS_STREAM_DATA____ +#define FWPS_STREAM_DATA____ + +/** + @kernel_helper_function="KrnlHlprFwpsStreamDataPurgeLocalCopy" + + Purpose: Cleanup a local copy of an FWPS_STREAM_DATA.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552419.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprFwpsStreamDataPurgeLocalCopy(_Inout_ FWPS_STREAM_DATA* pStreamData) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpsStreamDataPurgeLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(pStreamData); + + BOOLEAN isDispatch = (KeGetCurrentIrql() == DISPATCH_LEVEL) ? TRUE : FALSE; + + for(NET_BUFFER_LIST* pNBL = pStreamData->netBufferListChain; + pNBL; + ) + { + NET_BUFFER_LIST* pNBLNext = NET_BUFFER_LIST_NEXT_NBL(pNBL); + + FwpsDereferenceNetBufferList(pNBL, + isDispatch); + + pNBL = pNBLNext; + } + + pStreamData->netBufferListChain = 0; + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpsStreamDataPurgeLocalCopy()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprFwpsStreamDataDestroyLocalCopy" + + Purpose: Cleanup and free a local copy of an FWPS_STREAM_DATA.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552419.aspx
+*/ +_At_(*ppStreamData, _Pre_ _Notnull_) +_At_(*ppStreamData, _Post_ _Null_ __drv_freesMem(Pool)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(*ppStreamData == 0) +inline VOID KrnlHlprFwpsStreamDataDestroyLocalCopy(_Inout_ FWPS_STREAM_DATA** ppStreamData) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpsStreamDataDestroyLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(ppStreamData); + + if(*ppStreamData) + KrnlHlprFwpsStreamDataPurgeLocalCopy(*ppStreamData); + + HLPR_DELETE(*ppStreamData, + WFPSAMPLER_SYSLIB_TAG); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpsStreamDataDestroyLocalCopy()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprFwpsStreamDataPopulateLocalCopy + + Purpose: Populate a local copy of the FWPS_STREAM_DATA.
+
+ Notes: The caller is responsible for freeing the allocated memory using + KrnlHlprFwpsStreamDataPurgeLocalCopy().
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552419.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprFwpsStreamDataPopulateLocalCopy(_In_ const FWPS_STREAM_DATA* pOriginalStreamData, + _Inout_ FWPS_STREAM_DATA* pStreamData) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpsStreamDataPopulateLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(pOriginalStreamData); + NT_ASSERT(pStreamData); + + NTSTATUS status = STATUS_SUCCESS; + + for(NET_BUFFER_LIST* pNBL = pOriginalStreamData->netBufferListChain; + pNBL; + pNBL = NET_BUFFER_LIST_NEXT_NBL(pNBL)) + { + FwpsReferenceNetBufferList(pNBL, + TRUE); + } + + pStreamData->flags = pOriginalStreamData->flags; + pStreamData->dataOffset = pOriginalStreamData->dataOffset; + pStreamData->dataLength = pOriginalStreamData->dataLength; + pStreamData->netBufferListChain = pOriginalStreamData->netBufferListChain; + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpsStreamDataPopulateLocalCopy() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @kernel_helper_function="KrnlHlprFwpsStreamDataCreateLocalCopy + + Purpose: Allocate and populate a local copy of the FWPS_STREAM_DATA.
+
+ Notes: The caller is responsible for freeing the allocated memory using + KrnlHlprFwpsStreamDataDestroyLocalCopy().
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552419.aspx
+*/ +__drv_allocatesMem(Pool) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Must_inspect_result_ +_Success_(return != 0) +FWPS_STREAM_DATA* KrnlHlprFwpsStreamDataCreateLocalCopy(_In_ const FWPS_STREAM_DATA* pOriginalStreamData) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpsStreamDataCreateLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(pOriginalStreamData); + + NTSTATUS status = STATUS_SUCCESS; + FWPS_STREAM_DATA* pStreamData = 0; + +#pragma warning(push) +#pragma warning(disable: 6014) /// pStreamData is expected to be cleaned up by caller using KrnlHlprFwpsStreamDataDestroyLocalCopy + + HLPR_NEW(pStreamData, + FWPS_STREAM_DATA, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pStreamData, + status); + +#pragma warning(pop) + + status = KrnlHlprFwpsStreamDataPopulateLocalCopy(pOriginalStreamData, + pStreamData); + + HLPR_BAIL_LABEL: + +#pragma warning(push) +#pragma warning(disable: 6001) /// pStreamData initialized with call to HLPR_NEW & KrnlHlprFwpsStreamDataPopulateLocalCopy + + if(status != STATUS_SUCCESS && + pStreamData) + KrnlHlprFwpsStreamDataDestroyLocalCopy(&pStreamData); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpsStreamDataCreateLocalCopy() [pStreamData: %#p]\n", + pStreamData); + +#endif /// DBG + + return pStreamData; + +#pragma warning(pop) +} + +#endif /// FWPS_STREAM_DATA____ + +#ifndef FWPS_STREAM_CALLOUT_IO_PACKET____ +#define FWPS_STREAM_CALLOUT_IO_PACKET____ + +/** + @kernel_helper_function="KrnlHlprFwpsStreamCalloutIOPacketPurgeLocalCopy" + + Purpose: Cleanup a local copy of an FWPS_STREAM_CALLOUT_IO_PACKET.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552417.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprFwpsStreamCalloutIOPacketPurgeLocalCopy(_Inout_ FWPS_STREAM_CALLOUT_IO_PACKET* pIOPacket) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpsStreamCalloutIOPacketPurgeLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(pIOPacket); + + KrnlHlprFwpsStreamDataDestroyLocalCopy(&(pIOPacket->streamData)); + + RtlZeroMemory(pIOPacket, + sizeof(FWPS_STREAM_CALLOUT_IO_PACKET)); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpsStreamCalloutIOPacketPurgeLocalCopy()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprFwpsStreamCalloutIOPacketDestroyLocalCopy" + + Purpose: Cleanup and free a local copy of an FWPS_STREAM_CALLOUT_IO_PACKET.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552417.aspx
+*/ +_At_(*ppIOPacket, _Pre_ _Notnull_) +_At_(*ppIOPacket, _Post_ _Null_ __drv_freesMem(Pool)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(*ppIOPacket == 0) +inline VOID KrnlHlprFwpsStreamCalloutIOPacketDestroyLocalCopy(_Inout_ FWPS_STREAM_CALLOUT_IO_PACKET** ppIOPacket) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpsStreamCalloutIOPacketDestroyLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(ppIOPacket); + + if(*ppIOPacket) + KrnlHlprFwpsStreamCalloutIOPacketPurgeLocalCopy(*ppIOPacket); + + HLPR_DELETE(*ppIOPacket, + WFPSAMPLER_SYSLIB_TAG); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpsStreamCalloutIOPacketDestroyLocalCopy()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprFwpsStreamCalloutIOPacketPopulateLocalCopy + + Purpose: Populate a local copy of the FWPS_STREAM_CALLOUT_IO_PACKET.
+
+ Notes: The caller is responsible for freeing the allocated memory using + KrnlHlprFwpsStreamCalloutIOPacketPurgeLocalCopy().
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552417.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprFwpsStreamCalloutIOPacketPopulateLocalCopy(_In_ const FWPS_STREAM_CALLOUT_IO_PACKET* pOriginalIOPacket, + _Inout_ FWPS_STREAM_CALLOUT_IO_PACKET* pIOPacket) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpsStreamCalloutIOPacketPopulateLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(pOriginalIOPacket); + NT_ASSERT(pIOPacket); + + NTSTATUS status = STATUS_SUCCESS; + + pIOPacket->streamData = KrnlHlprFwpsStreamDataCreateLocalCopy(pOriginalIOPacket->streamData); + HLPR_BAIL_ON_NULL_POINTER_WITH_STATUS(pIOPacket->streamData, + status); + + pIOPacket->missedBytes = pOriginalIOPacket->missedBytes; + pIOPacket->countBytesRequired = pOriginalIOPacket->countBytesRequired; + pIOPacket->countBytesEnforced = pOriginalIOPacket->countBytesEnforced; + pIOPacket->streamAction = pOriginalIOPacket->streamAction; + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS) + KrnlHlprFwpsStreamCalloutIOPacketPurgeLocalCopy(pIOPacket); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpsStreamCalloutIOPacketPopulateLocalCopy() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @kernel_helper_function="KrnlHlprFwpsStreamCalloutIOPacketCreateLocalCopy + + Purpose: Allocate and populate a local copy of the FWPS_STREAM_CALLOUT_IO_PACKET.
+
+ Notes: The caller is responsible for freeing the allocated memory using + KrnlHlprFwpsStreamCalloutIOPacketDestroyLocalCopy().
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552417.aspx
+*/ +__drv_allocatesMem(Pool) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Must_inspect_result_ +_Success_(return != 0) +FWPS_STREAM_CALLOUT_IO_PACKET* KrnlHlprFwpsStreamCalloutIOPacketCreateLocalCopy(_In_ const FWPS_STREAM_CALLOUT_IO_PACKET* pOriginalIOPacket) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpsStreamCalloutIOPacketCreateLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(pOriginalIOPacket); + + NTSTATUS status = STATUS_SUCCESS; + FWPS_STREAM_CALLOUT_IO_PACKET* pIOPacket = 0; + +#pragma warning(push) +#pragma warning(disable: 6014) /// pIOPacket is expected to be cleaned up by caller using KrnlHlprFwpsStreamCalloutIOPacketDestroyLocalCopy + + HLPR_NEW(pIOPacket, + FWPS_STREAM_CALLOUT_IO_PACKET, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pIOPacket, + status); + +#pragma warning(pop) + + status = KrnlHlprFwpsStreamCalloutIOPacketPopulateLocalCopy(pOriginalIOPacket, + pIOPacket); + + HLPR_BAIL_LABEL: + +#pragma warning(push) +#pragma warning(disable: 6001) /// pIOPacket initialized with call to HLPR_NEW & KrnlHlprFwpsStreamCalloutIOPacketPopulateLocalCopy + + if(status != STATUS_SUCCESS) + KrnlHlprFwpsStreamCalloutIOPacketDestroyLocalCopy(&pIOPacket); + +#pragma warning(push) + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpsStreamCalloutIOPacketCreateLocalCopy() [pIOPacket: %#p]\n", + pIOPacket); + +#endif /// DBG + + return pIOPacket; +} + +#endif /// FWPS_STREAM_CALLOUT_IO_PACKET____ + +#ifndef FWPM_PROVIDER_CONTEXT____ +#define FWPM_PROVIDER_CONTEXT____ + +/** + @kernel_helper_function="KrnlHlprFwpmProviderContextPurgeLocalCopy" + + Purpose: Cleanup a local copy of an FWPM_PROVIDER_CONTEXT.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/HH447383.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/DD744952.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA364289.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprFwpmProviderContextPurgeLocalCopy(_Inout_ FWPM_PROVIDER_CONTEXT* pContext) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpmProviderContextPurgeLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(pContext); + + switch(pContext->type) + { + case FWPM_IPSEC_KEYING_CONTEXT: + { + if(pContext->keyingPolicy) + { + HLPR_DELETE_ARRAY(pContext->keyingPolicy->keyModKeys, + WFPSAMPLER_SYSLIB_TAG); + } + + HLPR_DELETE(pContext->keyingPolicy, + WFPSAMPLER_SYSLIB_TAG); + + break; + } + case FWPM_IPSEC_IKE_QM_TRANSPORT_CONTEXT: + { + break; + } + case FWPM_IPSEC_IKE_QM_TUNNEL_CONTEXT: + { + break; + } + case FWPM_IPSEC_AUTHIP_QM_TRANSPORT_CONTEXT: + { + break; + } + case FWPM_IPSEC_AUTHIP_QM_TUNNEL_CONTEXT: + { + break; + } + case FWPM_IPSEC_IKE_MM_CONTEXT: + { + break; + } + case FWPM_IPSEC_AUTHIP_MM_CONTEXT: + { + break; + } + case FWPM_CLASSIFY_OPTIONS_CONTEXT: + { + KrnlHlprFwpmClassifyOptionsDestroyLocalCopy(&(pContext->classifyOptions)); + + break; + } + case FWPM_GENERAL_CONTEXT: + { + KrnlHlprFwpByteBlobDestroyLocalCopy(&(pContext->dataBuffer)); + + break; + } + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + case FWPM_IPSEC_IKEV2_QM_TUNNEL_CONTEXT: + { + break; + } + case FWPM_IPSEC_IKEV2_MM_CONTEXT: + { + break; + } + case FWPM_IPSEC_DOSP_CONTEXT: + { + KrnlHlprIPsecDoSPOptionsDestroyLocalCopy(&(pContext->idpOptions)); + + break; + } + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + } + + KrnlHlprFwpByteBlobPurgeLocalCopy(&(pContext->providerData)); + + HLPR_DELETE(pContext->providerKey, + WFPSAMPLER_SYSLIB_TAG); + + KrnlHlprFwpmDisplayDataPurgeLocalCopy(&(pContext->displayData)); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpmProviderContextPurgeLocalCopy()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprFwpmProviderContextDestroyLocalCopy" + + Purpose: Cleanup and free a local copy of an FWPM_PROVIDER_CONTEXT.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/HH447383.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/DD744952.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA364289.aspx
+*/ +_At_(*ppContext, _Pre_ _Notnull_) +_At_(*ppContext, _Post_ _Null_ __drv_freesMem(Pool)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(*ppContext == 0) +inline VOID KrnlHlprFwpmProviderContextDestroyLocalCopy(_Inout_ FWPM_PROVIDER_CONTEXT** ppContext) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpmProviderContextDestroyLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(ppContext); + + if(*ppContext) + KrnlHlprFwpmProviderContextPurgeLocalCopy(*ppContext); + + HLPR_DELETE(*ppContext, + WFPSAMPLER_SYSLIB_TAG); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpmProviderContextDestroyLocalCopy()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprFwpmProviderContextPopulateLocalCopy + + Purpose: Populate a local copy of the FWPM_PROVIDER_CONTEXT.
+
+ Notes: The caller is responsible for freeing the allocated memory using + KrnlHlprFwpmProviderContextPurgeLocalCopy().
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/HH447383.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/DD744952.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA364289.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprFwpmProviderContextPopulateLocalCopy(_In_ const FWPM_PROVIDER_CONTEXT* pOriginalContext, + _Inout_ FWPM_PROVIDER_CONTEXT* pProviderContext) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpmProviderContextPopulateLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(pOriginalContext); + NT_ASSERT(pProviderContext); + + NTSTATUS status = STATUS_SUCCESS; + + pProviderContext->providerContextKey = pOriginalContext->providerContextKey; + pProviderContext->flags = pOriginalContext->flags; + pProviderContext->type = pOriginalContext->type; + pProviderContext->providerContextId = pOriginalContext->providerContextId; + + switch(pProviderContext->type) + { + case FWPM_IPSEC_KEYING_CONTEXT: + { + if(pOriginalContext->keyingPolicy) + { + HLPR_NEW(pProviderContext->keyingPolicy, + IPSEC_KEYING_POLICY, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pProviderContext->keyingPolicy, + status); + + if(pOriginalContext->keyingPolicy->numKeyMods) + { + pProviderContext->keyingPolicy->numKeyMods = pOriginalContext->keyingPolicy->numKeyMods; + + HLPR_NEW_ARRAY(pProviderContext->keyingPolicy->keyModKeys, + GUID, + pProviderContext->keyingPolicy->numKeyMods, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pProviderContext->keyingPolicy->keyModKeys, + status); + + RtlCopyMemory(pProviderContext->keyingPolicy->keyModKeys, + pOriginalContext->keyingPolicy->keyModKeys, + pProviderContext->keyingPolicy->numKeyMods * sizeof(GUID)); + } + } + + break; + } + case FWPM_IPSEC_IKE_QM_TRANSPORT_CONTEXT: + { + break; + } + case FWPM_IPSEC_IKE_QM_TUNNEL_CONTEXT: + { + break; + } + case FWPM_IPSEC_AUTHIP_QM_TRANSPORT_CONTEXT: + { + break; + } + case FWPM_IPSEC_AUTHIP_QM_TUNNEL_CONTEXT: + { + break; + } + case FWPM_IPSEC_IKE_MM_CONTEXT: + { + break; + } + case FWPM_IPSEC_AUTHIP_MM_CONTEXT: + { + break; + } + case FWPM_CLASSIFY_OPTIONS_CONTEXT: + { + break; + } + case FWPM_GENERAL_CONTEXT: + { + pProviderContext->dataBuffer = KrnlHlprFwpByteBlobCreateLocalCopy(pOriginalContext->dataBuffer); + HLPR_BAIL_ON_NULL_POINTER_WITH_STATUS(pProviderContext->dataBuffer, + status); + + break; + } + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + case FWPM_IPSEC_IKEV2_QM_TUNNEL_CONTEXT: + { + break; + } + case FWPM_IPSEC_IKEV2_MM_CONTEXT: + { + break; + } + case FWPM_IPSEC_DOSP_CONTEXT: + { + pProviderContext->idpOptions = KrnlHlprIPsecDoSPOptionsCreateLocalCopy(pOriginalContext->idpOptions); + HLPR_BAIL_ON_NULL_POINTER_WITH_STATUS(pProviderContext->idpOptions, + status); + break; + } + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + } + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS) + KrnlHlprFwpmProviderContextPurgeLocalCopy(pProviderContext); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpmProviderContextPopulateLocalCopy() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @kernel_helper_function="KrnlHlprFwpmProviderContextCreateLocalCopy + + Purpose: Allocate and populate a local copy of the FWPM_PROVIDER_CONTEXT.
+
+ Notes: The caller is responsible for freeing the allocated memory using + KrnlHlprFwpmProviderContextDestroyLocalCopy().
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/HH447383.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/DD744952.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA364289.aspx
+*/ +__drv_allocatesMem(Pool) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Must_inspect_result_ +_Success_(return != 0) +FWPM_PROVIDER_CONTEXT* KrnlHlprFwpmProviderContextCreateLocalCopy(_In_ const FWPM_PROVIDER_CONTEXT* pOriginalContext) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpmProviderContextCreateLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(pOriginalContext); + + NTSTATUS status = STATUS_SUCCESS; + FWPM_PROVIDER_CONTEXT* pProviderContext = 0; + +#pragma warning(push) +#pragma warning(disable: 6014) /// pProviderContext is expected to be cleaned up by caller using KrnlHlprFwpmProviderContextDestroyLocalCopy + + HLPR_NEW(pProviderContext, + FWPM_PROVIDER_CONTEXT, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pProviderContext, + status); + +#pragma warning(pop) + + status = KrnlHlprFwpmProviderContextPopulateLocalCopy(pOriginalContext, + pProviderContext); + + HLPR_BAIL_LABEL: + +#pragma warning(push) +#pragma warning(disable: 6001) /// pProviderContext initialized with call to HLPR_NEW & KrnlHlprFwpmProviderContextPopulateLocalCopy + + if(status != STATUS_SUCCESS) + KrnlHlprFwpmProviderContextDestroyLocalCopy(&pProviderContext); + +#pragma warning(pop) + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpmProviderContextCreateLocalCopy() [pProviderContext: %#p]\n", + pProviderContext); + +#endif /// DBG + + return pProviderContext; +} + +#endif /// FWPS_PROVIDER_CONTEXT____ + +#ifndef FWPS_FILTER_CONDITION____ +#define FWPS_FILTER_CONDITION____ + +/** + @kernel_helper_function="KrnlHlprFwpsFilterConditionPurgeLocalCopy" + + Purpose: Cleanup a local copy of an FWPS_FILTER_CONDITION.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552391.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprFwpsFilterConditionPurgeLocalCopy(_Inout_ FWPS_FILTER_CONDITION* pCondition) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpsFilterConditionPurgeLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(pCondition); + + KrnlHlprFwpConditionValuePurgeLocalCopy(&(pCondition->conditionValue)); + + RtlZeroMemory(pCondition, + sizeof(FWPS_FILTER_CONDITION)); + + pCondition->matchType = FWP_MATCH_TYPE_MAX; + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpsFilterConditionPurgeLocalCopy()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprFwpsFilterConditionDestroyLocalCopy" + + Purpose: Cleanup and free a local copy of an FWPS_FILTER_CONDITION.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552391.aspx
+*/ +_At_(*ppConditions, _Pre_ _Notnull_) +_At_(*ppConditions, _Post_ _Null_ __drv_freesMem(Pool)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(*ppConditions == 0) +inline VOID KrnlHlprFwpsFilterConditionDestroyLocalCopy(_Inout_ FWPS_FILTER_CONDITION** ppConditions, + _In_ UINT32 numConditions) /* 1 */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpsFilterConditionDestroyLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(ppConditions); + + if(*ppConditions) + { + for(UINT32 conditionIndex = 0; + conditionIndex < numConditions; + conditionIndex++) + { + KrnlHlprFwpsFilterConditionPurgeLocalCopy(&((*ppConditions)[conditionIndex])); + } + } + + HLPR_DELETE_ARRAY(*ppConditions, + WFPSAMPLER_SYSLIB_TAG); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpsFilterConditionDestroyLocalCopy()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprFwpsFilterConditionPopulateLocalCopy" + + Purpose: Populate a local copy of an FWPS_FILTER_CONDITION.
+
+ Notes: The caller is responsible for freeing any allocated memory using + KrnlHlprFwpsFilterConditionPurgeLocalCopy().
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552391.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprFwpsFilterConditionPopulateLocalCopy(_In_ const FWPS_FILTER_CONDITION* pOriginalCondition, + _Inout_ FWPS_FILTER_CONDITION* pCondition) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpsFilterConditionPopulateLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(pOriginalCondition); + NT_ASSERT(pCondition); + + NTSTATUS status = STATUS_SUCCESS; + + pCondition->fieldId = pOriginalCondition->fieldId; + pCondition->reserved = pOriginalCondition->reserved; + pCondition->matchType = pOriginalCondition->matchType; + + status = KrnlHlprFwpConditionValuePopulateLocalCopy(&(pOriginalCondition->conditionValue), + &(pCondition->conditionValue)); + + if(status != STATUS_SUCCESS) + KrnlHlprFwpsFilterConditionPurgeLocalCopy(pCondition); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpsFilterConditionPopulateLocalCopy() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @kernel_helper_function="KrnlHlprFwpsFilterConditionCreateLocalCopy" + + Purpose: Allocate and populate a local copy of an FWPS_FILTER_CONDITION.
+
+ Notes: The caller is responsible for freeing any allocated memory using + KrnlHlprFwpsFilterConditionDestroyLocalCopy().
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552391.aspx
+*/ +__drv_allocatesMem(Pool) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Must_inspect_result_ +_Success_(return != 0) +FWPS_FILTER_CONDITION* KrnlHlprFwpsFilterConditionCreateLocalCopy(_In_reads_(numConditions) const FWPS_FILTER_CONDITION* pOriginalConditions, + _In_ UINT32 numConditions) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpsFilterConditionCreateLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(pOriginalConditions); + NT_ASSERT(numConditions); + + NTSTATUS status = STATUS_SUCCESS; + FWPS_FILTER_CONDITION* pConditions = 0; + +#pragma warning(push) +#pragma warning(disable: 6014) /// pConditions is expected to be cleaned up by caller using KrnlHlprFwpsFilterConditionDestroyLocalCopy + + HLPR_NEW_ARRAY(pConditions, + FWPS_FILTER_CONDITION, + numConditions, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pConditions, + status); + +#pragma warning(pop) + + for(UINT32 conditionIndex = 0; + conditionIndex < numConditions; + conditionIndex++) + { + status = KrnlHlprFwpsFilterConditionPopulateLocalCopy(&(pOriginalConditions[conditionIndex]), + &(pConditions[conditionIndex])); + } + + HLPR_BAIL_LABEL: + +#pragma warning(push) +#pragma warning(disable: 6001) /// pConditions initialized with call to HLPR_NEW_ARRAY & KrnlHlprFwpsFilterConditionPopulateLocalCopy + + if(status != STATUS_SUCCESS && + pConditions) + KrnlHlprFwpsFilterConditionDestroyLocalCopy(&pConditions); + +#pragma warning(pop) + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpsFilterConditionCreateLocalCopy() [pConditions: %#p]\n", + pConditions); + +#endif /// DBG + + return pConditions; +} + +#endif /// FWPS_FILTER_CONDITION____ + +#ifndef FWPS_FILTER____ +#define FWPS_FILTER____ + +/** + @kernel_helper_function="KrnlHlprFwpsFilterPurgeLocalCopy" + + Purpose: Cleanup a local copy of an FWPS_FILTER.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/EE220716.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprFwpsFilterPurgeLocalCopy(_Inout_ FWPS_FILTER* pFilter) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpsFilterPurgeLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(pFilter); + + KrnlHlprFwpmProviderContextDestroyLocalCopy(&(pFilter->providerContext)); + + KrnlHlprFwpsFilterConditionDestroyLocalCopy(&(pFilter->filterCondition), + pFilter->numFilterConditions); + + KrnlHlprFwpValuePurgeLocalCopy(&(pFilter->weight)); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpsFilterPurgeLocalCopy()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprFwpsFilterDestroyLocalCopy" + + Purpose: Cleanup and free a local copy of an FWPS_FILTER.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/HH439768.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552389.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552387.aspx
+*/ +_At_(*ppFilter, _Pre_ _Notnull_) +_At_(*ppFilter, _Post_ _Null_ __drv_freesMem(Pool)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(*ppFilter == 0) +inline VOID KrnlHlprFwpsFilterDestroyLocalCopy(_Inout_ FWPS_FILTER** ppFilter) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpsFilterDestroyLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(ppFilter); + + if(*ppFilter) + KrnlHlprFwpsFilterPurgeLocalCopy(*ppFilter); + + HLPR_DELETE(*ppFilter, + WFPSAMPLER_SYSLIB_TAG); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpsFilterDestroyLocalCopy()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprFwpsFilterPopulateLocalCopy + + Purpose: Populate a local copy of the FWPS_FILTER.
+
+ Notes: The caller is responsible for freeing the allocated memory using + KrnlHlprFwpsFilterPurgeLocalCopy().
+
+ + MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/HH439768.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552389.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552387.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprFwpsFilterPopulateLocalCopy(_In_ const FWPS_FILTER* pOriginalFilter, + _Inout_ FWPS_FILTER* pFilter) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpsFilterPopulateLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(pOriginalFilter); + NT_ASSERT(pFilter); + + NTSTATUS status = STATUS_SUCCESS; + + pFilter->filterId = pOriginalFilter->filterId; + pFilter->subLayerWeight = pOriginalFilter->subLayerWeight; + pFilter->flags = pOriginalFilter->flags; + pFilter->numFilterConditions = pOriginalFilter->numFilterConditions; + pFilter->action.type = pOriginalFilter->action.type; + pFilter->action.calloutId = pOriginalFilter->action.calloutId; + pFilter->context = pOriginalFilter->context; + + status = KrnlHlprFwpValuePopulateLocalCopy(&(pOriginalFilter->weight), + &(pFilter->weight)); + HLPR_BAIL_ON_FAILURE(status); + + if(pFilter->numFilterConditions) + { + pFilter->filterCondition = KrnlHlprFwpsFilterConditionCreateLocalCopy(pOriginalFilter->filterCondition, + pFilter->numFilterConditions); + HLPR_BAIL_ON_NULL_POINTER_WITH_STATUS(pFilter->filterCondition, + status); + } + + if(pOriginalFilter->providerContext) + { + pFilter->providerContext = KrnlHlprFwpmProviderContextCreateLocalCopy(pOriginalFilter->providerContext); + HLPR_BAIL_ON_ALLOC_FAILURE(pFilter->providerContext, + status); + } + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS) + KrnlHlprFwpsFilterPurgeLocalCopy(pFilter); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpsFilterPopulateLocalCopy() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @kernel_helper_function="KrnlHlprFwpsFilterCreateLocalCopy + + Purpose: Allocate and populate a local copy of the FWPS_FILTER.
+
+ Notes: The caller is responsible for freeing the allocated memory using + KrnlHlprFwpsFilterDestroyLocalCopy().
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/HH439768.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552389.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF552387.aspx
+*/ +__drv_allocatesMem(Pool) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Must_inspect_result_ +_Success_(return != 0) +FWPS_FILTER* KrnlHlprFwpsFilterCreateLocalCopy(_In_ const FWPS_FILTER* pOriginalFilter) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpsFilterCreateLocalCopy()\n"); + +#endif /// DBG + + NT_ASSERT(pOriginalFilter); + + NTSTATUS status = STATUS_SUCCESS; + FWPS_FILTER* pFilter = 0; + +#pragma warning(push) +#pragma warning(disable: 6014) /// pFilter is expected to be cleaned up by caller using KrnlHlprFwpsFilterDestroyLocalCopy + + HLPR_NEW(pFilter, + FWPS_FILTER, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pFilter, + status); + +#pragma warning(pop) + + status = KrnlHlprFwpsFilterPopulateLocalCopy(pOriginalFilter, + pFilter); + + HLPR_BAIL_LABEL: + +#pragma warning(push) +#pragma warning(disable: 6001) /// pFilter initialized with call to HLPR_NEW & KrnlHlprFwpsFilterPopulateLocalCopy + + if(status != STATUS_SUCCESS) + KrnlHlprFwpsFilterDestroyLocalCopy(&pFilter); + +#pragma warning(pop) + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpsFilterCreateLocalCopy() [pFilter: %p]\n", + pFilter); + +#endif /// DBG + + return pFilter; +} + +#endif /// FWPS_FILTER____ + +#ifndef FWPM_SESSION____ +#define FWPM_SESSION____ + +/** + @kernel_helper_function="KrnlHlprFwpmSessionReleaseHandle + + Purpose: Close an open handle to BFE.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF550072.aspx
+*/ +_IRQL_requires_(PASSIVE_LEVEL) +_IRQL_requires_same_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprFwpmSessionReleaseHandle(_Inout_ HANDLE* pEngineHandle) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpmSessionReleaseHandle()\n"); + +#endif /// DBG + + NT_ASSERT(pEngineHandle); + + NTSTATUS status = STATUS_SUCCESS; + + if(*pEngineHandle) + { + status = FwpmEngineClose(*pEngineHandle); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! KrnlHlprFwpmSessionReleaseHandle : FwpmEngineClose() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + *pEngineHandle = 0; + } + + HLPR_BAIL_LABEL: + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpmSessionReleaseHandle() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @kernel_helper_function="KrnlHlprFwpmSessionDestroyEngineHandle + + Purpose: Close an open handle to BFE and set the handle to 0.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_At_(*ppEngineHandle, _Pre_ _Notnull_) +_At_(*ppEngineHandle, _Post_ _Null_) +_IRQL_requires_(PASSIVE_LEVEL) +_IRQL_requires_same_ +_Success_(*ppEngineHandle == 0) +VOID KrnlHlprFwpmSessionDestroyEngineHandle(_Inout_ HANDLE** ppEngineHandle) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpmSessionDestroyEngineHandle()\n"); + +#endif /// DBG + + NT_ASSERT(ppEngineHandle); + + if(*ppEngineHandle) + KrnlHlprFwpmSessionReleaseHandle(*ppEngineHandle); + + *ppEngineHandle = 0; + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpmSessionDestroyEngineHandle()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprFwpmSessionAcquireHandle + + Purpose: Open a handle to BFE.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF550083.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF550075.aspx
+*/ +_IRQL_requires_(PASSIVE_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprFwpmSessionAcquireHandle(_Inout_ HANDLE* pEngineHandle, + _In_ const GUID* pSessionKey) /* WFPSAMPLER_SESSION_KM */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpmSessionAcquireHandle()\n"); + +#endif /// DBG + + NT_ASSERT(pEngineHandle); + NT_ASSERT(pSessionKey); + + NTSTATUS status = STATUS_SUCCESS; + FWPM_SESSION session = {0}; + + session.sessionKey = *pSessionKey; + session.displayData.name = L"Microsoft Corporation - WFPSampler's Session"; + session.displayData.description = L"Session created by WFPSampler"; + + status = FwpmEngineOpen(0, + RPC_C_AUTHN_WINNT, + 0, + &session, + pEngineHandle); + if(status != STATUS_SUCCESS || + *pEngineHandle == 0) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! KrnlHlprFwpmSessionAcquireHandle : FwpmEngineOpen() [status: %#x][*pEngineHandle: %#p]\n", + status, + *pEngineHandle); + + *pEngineHandle = 0; + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpmSessionAcquireHandle() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @kernel_helper_function="KrnlHlprFwpmSessionCreateEngineHandle + + Purpose: Open a handle to BFE.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_When_(return != STATUS_SUCCESS, _At_(*ppEngineHandle, _Post_ _Null_)) +_When_(return == STATUS_SUCCESS, _At_(*ppEngineHandle, _Post_ _Notnull_)) +_IRQL_requires_(PASSIVE_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprFwpmSessionCreateEngineHandle(_Outptr_ HANDLE** ppEngineHandle, + _In_ const GUID* pSessionKey) /* WFPSAMPLER_SESSION_KM */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpmSessionCreateEngineHandle()\n"); + +#endif /// DBG + + NT_ASSERT(ppEngineHandle); + NT_ASSERT(pSessionKey); + + NTSTATUS status = STATUS_SUCCESS; + + *ppEngineHandle = 0; + + status = KrnlHlprFwpmSessionAcquireHandle(&g_EngineHandle, + pSessionKey); + HLPR_BAIL_ON_FAILURE(status); + + *ppEngineHandle = &g_EngineHandle; + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS) + KrnlHlprFwpmSessionReleaseHandle(&g_EngineHandle); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpmSessionCreateEngineHandle() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +#endif /// FWPS_SESSION____ + +#ifndef FwpsInjection____ +#define FwpsInjection____ + +/** + @kernel_helper_function="KrnlHlprFwpsInjectionReleaseHandle + + Purpose: Destroy an opened injection handle and set it to 0.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551181.aspx
+*/ +_IRQL_requires_(PASSIVE_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprFwpsInjectionReleaseHandle(_In_ HANDLE* pInjectionHandle) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpsInjectionReleaseHandle()\n"); + +#endif /// DBG + + NT_ASSERT(pInjectionHandle); + + NTSTATUS status = STATUS_SUCCESS; + + status = FwpsInjectionHandleDestroy(*pInjectionHandle); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! KrnlHlprFwpsInjectionReleaseHandle : FwpsInjectionHandleDestroy() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + *pInjectionHandle = 0; + + HLPR_BAIL_LABEL: + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpsInjectionReleaseHandle() {status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @kernel_helper_function="KrnlHlprFwpsInjectionAcquireHandle + + Purpose: Open injection handle for use with the various injection APIs.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551180.aspx
+*/ +_IRQL_requires_(PASSIVE_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprFwpsInjectionAcquireHandle(_Inout_ HANDLE* pInjectionHandle, + _In_ ADDRESS_FAMILY addressFamily, /* AF_UNSPEC */ + _In_ UINT32 injectionType) /* 0 */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpsInjectionAcquireHandle()\n"); + +#endif /// DBG + + NT_ASSERT(pInjectionHandle); + + NTSTATUS status = STATUS_SUCCESS; + + status = FwpsInjectionHandleCreate(addressFamily, + injectionType, + pInjectionHandle); + if(status != STATUS_SUCCESS) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! KrnlHlprFwpsInjectionAcquireHandle : FwpsInjectionHandleCreate() [status: %#x]\n", + status); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpsInjectionAcquireHandle() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +#endif /// FwpsInjection____ + +#if(NTDDI_VERSION >= NTDDI_WIN8) + +#ifndef FwpsRedirection____ +#define FwpsRedirection____ + +/** + @kernel_helper_function="KrnlHlprFwpsRedirectHandleDestroy + + Purpose: Destroy an opened redirection handle and set it to 0.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/HH439684.aspx
+*/ +_IRQL_requires_(PASSIVE_LEVEL) +_IRQL_requires_same_ +VOID KrnlHlprFwpsRedirectHandleDestroy(_In_ HANDLE* pRedirectionHandle) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpsRedirectHandleDestroy()\n"); + +#endif /// DBG + + NT_ASSERT(pRedirectionHandle); + + FwpsRedirectHandleDestroy(*pRedirectionHandle); + + *pRedirectionHandle = 0; + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpsRedirectHandleDestroy()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprFwpsRedirectHandleCreate + + Purpose: Open redirection handle for use with the various redirection APIs.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/HH439681.aspx
+*/ +_IRQL_requires_(PASSIVE_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprFwpsRedirectHandleCreate(_Inout_ HANDLE* pRedirectionHandle) /* 0 */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprFwpsRedirectHandleCreate()\n"); + +#endif /// DBG + + NT_ASSERT(pRedirectionHandle); + + NTSTATUS status = STATUS_SUCCESS; + + status = FwpsRedirectHandleCreate(&WFPSAMPLER_PROVIDER, + 0, + pRedirectionHandle); + if(status != STATUS_SUCCESS) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! KrnlHlprFwpsRedirectHandleCreate : FwpsRedirectHandleCreate() [status: %#x]\n", + status); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprFwpsRedirectHandleCreate() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +#endif /// FwpsRedirection____ + +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) + +#ifndef FwpsLayer____ +#define FwpsLayer____ + +/** + @helper function="KrnlHlprFwpsLayerGetDirection" + + Purpose: Determine the direction of the NBL based on the layer.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +FWP_DIRECTION KrnlHlprFwpsLayerGetDirection(_In_ UINT32 layerID) +{ + FWP_DIRECTION direction = FWP_DIRECTION_MAX; + + if(layerID == FWPS_LAYER_INBOUND_IPPACKET_V4 || + layerID == FWPS_LAYER_INBOUND_IPPACKET_V4_DISCARD || + layerID == FWPS_LAYER_INBOUND_IPPACKET_V6 || + layerID == FWPS_LAYER_INBOUND_IPPACKET_V6_DISCARD || + layerID == FWPS_LAYER_INBOUND_TRANSPORT_V4 || + layerID == FWPS_LAYER_INBOUND_TRANSPORT_V4_DISCARD || + layerID == FWPS_LAYER_INBOUND_TRANSPORT_V6 || + layerID == FWPS_LAYER_INBOUND_TRANSPORT_V6_DISCARD || + layerID == FWPS_LAYER_INBOUND_ICMP_ERROR_V4 || + layerID == FWPS_LAYER_INBOUND_ICMP_ERROR_V4_DISCARD || + layerID == FWPS_LAYER_INBOUND_ICMP_ERROR_V6 || + layerID == FWPS_LAYER_INBOUND_ICMP_ERROR_V6_DISCARD || + layerID == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 || + layerID == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4_DISCARD || + layerID == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6 || + layerID == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6_DISCARD + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + || + layerID == FWPS_LAYER_INBOUND_MAC_FRAME_ETHERNET || + layerID == FWPS_LAYER_INBOUND_MAC_FRAME_NATIVE + +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) + + ) + direction = FWP_DIRECTION_INBOUND; + else if(layerID == FWPS_LAYER_OUTBOUND_IPPACKET_V4 || + layerID == FWPS_LAYER_OUTBOUND_IPPACKET_V4_DISCARD || + layerID == FWPS_LAYER_OUTBOUND_IPPACKET_V6 || + layerID == FWPS_LAYER_OUTBOUND_IPPACKET_V6_DISCARD || + layerID == FWPS_LAYER_OUTBOUND_TRANSPORT_V4 || + layerID == FWPS_LAYER_OUTBOUND_TRANSPORT_V4_DISCARD || + layerID == FWPS_LAYER_OUTBOUND_TRANSPORT_V6 || + layerID == FWPS_LAYER_OUTBOUND_TRANSPORT_V6_DISCARD || + layerID == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4 || + layerID == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4_DISCARD || + layerID == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6 || + layerID == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6_DISCARD || + layerID == FWPS_LAYER_ALE_AUTH_CONNECT_V4 || + layerID == FWPS_LAYER_ALE_AUTH_CONNECT_V4_DISCARD || + layerID == FWPS_LAYER_ALE_AUTH_CONNECT_V6 || + layerID == FWPS_LAYER_ALE_AUTH_CONNECT_V6_DISCARD + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + || + layerID == FWPS_LAYER_OUTBOUND_MAC_FRAME_ETHERNET || + layerID == FWPS_LAYER_OUTBOUND_MAC_FRAME_NATIVE + +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) + + ) + direction = FWP_DIRECTION_OUTBOUND; + + return direction; +} + +/** + @helper function="KrnlHlprFwpsLayerIsIPv4" + + Purpose: Determine if the layer is an IPv4 layer.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +BOOLEAN KrnlHlprFwpsLayerIsIPv4(_In_ UINT32 layerID) +{ + BOOLEAN isIPv4 = FALSE; + + if(layerID == FWPS_LAYER_INBOUND_IPPACKET_V4 || + layerID == FWPS_LAYER_INBOUND_IPPACKET_V4_DISCARD || + layerID == FWPS_LAYER_OUTBOUND_IPPACKET_V4 || + layerID == FWPS_LAYER_OUTBOUND_IPPACKET_V4_DISCARD || + layerID == FWPS_LAYER_IPFORWARD_V4 || + layerID == FWPS_LAYER_IPFORWARD_V4_DISCARD || + layerID == FWPS_LAYER_INBOUND_TRANSPORT_V4 || + layerID == FWPS_LAYER_INBOUND_TRANSPORT_V4_DISCARD || + layerID == FWPS_LAYER_OUTBOUND_TRANSPORT_V4 || + layerID == FWPS_LAYER_OUTBOUND_TRANSPORT_V4_DISCARD || + layerID == FWPS_LAYER_STREAM_V4 || + layerID == FWPS_LAYER_STREAM_V4_DISCARD || + layerID == FWPS_LAYER_DATAGRAM_DATA_V4 || + layerID == FWPS_LAYER_DATAGRAM_DATA_V4_DISCARD || + layerID == FWPS_LAYER_INBOUND_ICMP_ERROR_V4 || + layerID == FWPS_LAYER_INBOUND_ICMP_ERROR_V4_DISCARD || + layerID == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4 || + layerID == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4_DISCARD || + layerID == FWPS_LAYER_ALE_RESOURCE_ASSIGNMENT_V4 || + layerID == FWPS_LAYER_ALE_RESOURCE_ASSIGNMENT_V4_DISCARD || + layerID == FWPS_LAYER_ALE_AUTH_LISTEN_V4 || + layerID == FWPS_LAYER_ALE_AUTH_LISTEN_V4_DISCARD || + layerID == FWPS_LAYER_ALE_AUTH_CONNECT_V4 || + layerID == FWPS_LAYER_ALE_AUTH_CONNECT_V4_DISCARD || + layerID == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 || + layerID == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4_DISCARD || + layerID == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4 || + layerID == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4_DISCARD || + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + layerID == FWPS_LAYER_NAME_RESOLUTION_CACHE_V4 || + layerID == FWPS_LAYER_ALE_RESOURCE_RELEASE_V4 || + layerID == FWPS_LAYER_ALE_ENDPOINT_CLOSURE_V4 || + layerID == FWPS_LAYER_ALE_CONNECT_REDIRECT_V4 || + layerID == FWPS_LAYER_ALE_BIND_REDIRECT_V4 || + layerID == FWPS_LAYER_STREAM_PACKET_V4 || + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + layerID == FWPS_LAYER_INGRESS_VSWITCH_TRANSPORT_V4 || + layerID == FWPS_LAYER_EGRESS_VSWITCH_TRANSPORT_V4 || + +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + layerID == FWPS_LAYER_IPSEC_KM_DEMUX_V4 || + layerID == FWPS_LAYER_IPSEC_V4 || + layerID == FWPS_LAYER_IKEEXT_V4) + isIPv4 = TRUE; + + return isIPv4; +} + +/** + @helper function="KrnlHlprFwpsLayerIsIPv6" + + Purpose: Determine if the layer is an IPv6 layer.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +BOOLEAN KrnlHlprFwpsLayerIsIPv6(_In_ UINT32 layerID) +{ + BOOLEAN isIPv6 = FALSE; + + if(layerID == FWPS_LAYER_INBOUND_IPPACKET_V6 || + layerID == FWPS_LAYER_INBOUND_IPPACKET_V6_DISCARD || + layerID == FWPS_LAYER_OUTBOUND_IPPACKET_V6 || + layerID == FWPS_LAYER_OUTBOUND_IPPACKET_V6_DISCARD || + layerID == FWPS_LAYER_IPFORWARD_V6 || + layerID == FWPS_LAYER_IPFORWARD_V6_DISCARD || + layerID == FWPS_LAYER_INBOUND_TRANSPORT_V6 || + layerID == FWPS_LAYER_INBOUND_TRANSPORT_V6_DISCARD || + layerID == FWPS_LAYER_OUTBOUND_TRANSPORT_V6 || + layerID == FWPS_LAYER_OUTBOUND_TRANSPORT_V6_DISCARD || + layerID == FWPS_LAYER_STREAM_V6 || + layerID == FWPS_LAYER_STREAM_V6_DISCARD || + layerID == FWPS_LAYER_DATAGRAM_DATA_V6 || + layerID == FWPS_LAYER_DATAGRAM_DATA_V6_DISCARD || + layerID == FWPS_LAYER_INBOUND_ICMP_ERROR_V6 || + layerID == FWPS_LAYER_INBOUND_ICMP_ERROR_V6_DISCARD || + layerID == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6 || + layerID == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6_DISCARD || + layerID == FWPS_LAYER_ALE_RESOURCE_ASSIGNMENT_V6 || + layerID == FWPS_LAYER_ALE_RESOURCE_ASSIGNMENT_V6_DISCARD || + layerID == FWPS_LAYER_ALE_AUTH_LISTEN_V6 || + layerID == FWPS_LAYER_ALE_AUTH_LISTEN_V6_DISCARD || + layerID == FWPS_LAYER_ALE_AUTH_CONNECT_V6 || + layerID == FWPS_LAYER_ALE_AUTH_CONNECT_V6_DISCARD || + layerID == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6 || + layerID == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6_DISCARD || + layerID == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6 || + layerID == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6_DISCARD || + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + layerID == FWPS_LAYER_NAME_RESOLUTION_CACHE_V6 || + layerID == FWPS_LAYER_ALE_RESOURCE_RELEASE_V6 || + layerID == FWPS_LAYER_ALE_ENDPOINT_CLOSURE_V6 || + layerID == FWPS_LAYER_ALE_CONNECT_REDIRECT_V6 || + layerID == FWPS_LAYER_ALE_BIND_REDIRECT_V6 || + layerID == FWPS_LAYER_STREAM_PACKET_V6 || + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + layerID == FWPS_LAYER_INGRESS_VSWITCH_TRANSPORT_V6 || + layerID == FWPS_LAYER_EGRESS_VSWITCH_TRANSPORT_V6 || + +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + layerID == FWPS_LAYER_IPSEC_KM_DEMUX_V6 || + layerID == FWPS_LAYER_IPSEC_V6 || + layerID == FWPS_LAYER_IKEEXT_V6) + isIPv6 = TRUE; + + return isIPv6; +} + +/** + @helper function="KrnlHlprFwpsLayerIsDiscard" + + Purpose: Determine if the layer is a discard layer.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +BOOLEAN KrnlHlprFwpsLayerIsDiscard(_In_ UINT32 layerID) +{ + BOOLEAN isDiscard = FALSE; + + if(layerID == FWPS_LAYER_INBOUND_IPPACKET_V4_DISCARD || + layerID == FWPS_LAYER_INBOUND_IPPACKET_V6_DISCARD || + layerID == FWPS_LAYER_OUTBOUND_IPPACKET_V4_DISCARD || + layerID == FWPS_LAYER_OUTBOUND_IPPACKET_V6_DISCARD || + layerID == FWPS_LAYER_IPFORWARD_V4_DISCARD || + layerID == FWPS_LAYER_IPFORWARD_V6_DISCARD || + layerID == FWPS_LAYER_INBOUND_TRANSPORT_V4_DISCARD || + layerID == FWPS_LAYER_INBOUND_TRANSPORT_V6_DISCARD || + layerID == FWPS_LAYER_OUTBOUND_TRANSPORT_V4_DISCARD || + layerID == FWPS_LAYER_OUTBOUND_TRANSPORT_V6_DISCARD || + layerID == FWPS_LAYER_STREAM_V4_DISCARD || + layerID == FWPS_LAYER_STREAM_V6_DISCARD || + layerID == FWPS_LAYER_DATAGRAM_DATA_V4_DISCARD || + layerID == FWPS_LAYER_DATAGRAM_DATA_V6_DISCARD || + layerID == FWPS_LAYER_INBOUND_ICMP_ERROR_V4_DISCARD || + layerID == FWPS_LAYER_INBOUND_ICMP_ERROR_V6_DISCARD || + layerID == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4_DISCARD || + layerID == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6_DISCARD || + layerID == FWPS_LAYER_ALE_RESOURCE_ASSIGNMENT_V4_DISCARD || + layerID == FWPS_LAYER_ALE_RESOURCE_ASSIGNMENT_V6_DISCARD || + layerID == FWPS_LAYER_ALE_AUTH_LISTEN_V4_DISCARD || + layerID == FWPS_LAYER_ALE_AUTH_LISTEN_V6_DISCARD || + layerID == FWPS_LAYER_ALE_AUTH_CONNECT_V4_DISCARD || + layerID == FWPS_LAYER_ALE_AUTH_CONNECT_V6_DISCARD || + layerID == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4_DISCARD || + layerID == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6_DISCARD || + layerID == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4_DISCARD || + layerID == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6_DISCARD) + isDiscard = TRUE; + + return isDiscard; +} + +/** + @helper function="KrnlHlprFwpsLayerIDToString" + + Purpose: Return a string representation of the supplied layer.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(return != 0) +PSTR KrnlHlprFwpsLayerIDToString(_In_ UINT32 layerID) +{ + PSTR pLayerString = 0; + + if(layerID == FWPS_LAYER_INBOUND_IPPACKET_V4) + pLayerString = "FWPS_LAYER_INBOUND_IPPACKET_V4"; + else if(layerID == FWPS_LAYER_INBOUND_IPPACKET_V4_DISCARD) + pLayerString = "FWPS_LAYER_INBOUND_IPPACKET_V4_DISCARD"; + else if(layerID == FWPS_LAYER_INBOUND_IPPACKET_V6) + pLayerString = "FWPS_LAYER_INBOUND_IPPACKET_V6"; + else if(layerID == FWPS_LAYER_INBOUND_IPPACKET_V6_DISCARD) + pLayerString = "FWPS_LAYER_INBOUND_IPPACKET_V6_DISCARD"; + else if(layerID == FWPS_LAYER_OUTBOUND_IPPACKET_V4) + pLayerString = "FWPS_LAYER_OUTBOUND_IPPACKET_V4"; + else if(layerID == FWPS_LAYER_OUTBOUND_IPPACKET_V4_DISCARD) + pLayerString = "FWPS_LAYER_OUTBOUND_IPPACKET_V4_DISCARD"; + else if(layerID == FWPS_LAYER_OUTBOUND_IPPACKET_V6) + pLayerString = "FWPS_LAYER_OUTBOUND_IPPACKET_V6"; + else if(layerID == FWPS_LAYER_OUTBOUND_IPPACKET_V6_DISCARD) + pLayerString = "FWPS_LAYER_OUTBOUND_IPPACKET_V6_DISCARD"; + else if(layerID == FWPS_LAYER_IPFORWARD_V4) + pLayerString = "FWPS_LAYER_IPFORWARD_V4"; + else if(layerID == FWPS_LAYER_IPFORWARD_V4_DISCARD) + pLayerString = "FWPS_LAYER_IPFORWARD_V4_DISCARD"; + else if(layerID == FWPS_LAYER_IPFORWARD_V6) + pLayerString = "FWPS_LAYER_IPFORWARD_V6"; + else if(layerID == FWPS_LAYER_IPFORWARD_V6_DISCARD) + pLayerString = "FWPS_LAYER_IPFORWARD_V6_DISCARD"; + else if(layerID == FWPS_LAYER_INBOUND_TRANSPORT_V4) + pLayerString = "FWPS_LAYER_INBOUND_TRANSPORT_V4"; + else if(layerID == FWPS_LAYER_INBOUND_TRANSPORT_V4_DISCARD) + pLayerString = "FWPS_LAYER_INBOUND_TRANSPORT_V4_DISCARD"; + else if(layerID == FWPS_LAYER_INBOUND_TRANSPORT_V6) + pLayerString = "FWPS_LAYER_INBOUND_TRANSPORT_V6"; + else if(layerID == FWPS_LAYER_INBOUND_TRANSPORT_V6_DISCARD) + pLayerString = "FWPS_LAYER_INBOUND_TRANSPORT_V6_DISCARD"; + else if(layerID == FWPS_LAYER_OUTBOUND_TRANSPORT_V4) + pLayerString = "FWPS_LAYER_OUTBOUND_TRANSPORT_V4"; + else if(layerID == FWPS_LAYER_OUTBOUND_TRANSPORT_V4_DISCARD) + pLayerString = "FWPS_LAYER_OUTBOUND_TRANSPORT_V4_DISCARD"; + else if(layerID == FWPS_LAYER_OUTBOUND_TRANSPORT_V6) + pLayerString = "FWPS_LAYER_OUTBOUND_TRANSPORT_V6"; + else if(layerID == FWPS_LAYER_OUTBOUND_TRANSPORT_V6_DISCARD) + pLayerString = "FWPS_LAYER_OUTBOUND_TRANSPORT_V6_DISCARD"; + else if(layerID == FWPS_LAYER_STREAM_V4) + pLayerString = "FWPS_LAYER_STREAM_V4"; + else if(layerID == FWPS_LAYER_STREAM_V4_DISCARD) + pLayerString = "FWPS_LAYER_STREAM_V4_DISCARD"; + else if(layerID == FWPS_LAYER_STREAM_V6) + pLayerString = "FWPS_LAYER_STREAM_V6"; + else if(layerID == FWPS_LAYER_STREAM_V6_DISCARD) + pLayerString = "FWPS_LAYER_STREAM_V6_DISCARD"; + else if(layerID == FWPS_LAYER_DATAGRAM_DATA_V4) + pLayerString = "FWPS_LAYER_DATAGRAM_DATA_V4"; + else if(layerID == FWPS_LAYER_DATAGRAM_DATA_V4_DISCARD) + pLayerString = "FWPS_LAYER_DATAGRAM_DATA_V4_DISCARD"; + else if(layerID == FWPS_LAYER_DATAGRAM_DATA_V6) + pLayerString = "FWPS_LAYER_DATAGRAM_DATA_V6"; + else if(layerID == FWPS_LAYER_DATAGRAM_DATA_V6_DISCARD) + pLayerString = "FWPS_LAYER_DATAGRAM_DATA_V6_DISCARD"; + else if(layerID == FWPS_LAYER_INBOUND_ICMP_ERROR_V4) + pLayerString = "FWPS_LAYER_INBOUND_ICMP_ERROR_V4"; + else if(layerID == FWPS_LAYER_INBOUND_ICMP_ERROR_V4_DISCARD) + pLayerString = "FWPS_LAYER_INBOUND_ICMP_ERROR_V4_DISCARD"; + else if(layerID == FWPS_LAYER_INBOUND_ICMP_ERROR_V6) + pLayerString = "FWPS_LAYER_INBOUND_ICMP_ERROR_V6"; + else if(layerID == FWPS_LAYER_INBOUND_ICMP_ERROR_V6_DISCARD) + pLayerString = "FWPS_LAYER_INBOUND_ICMP_ERROR_V6_DISCARD"; + else if(layerID == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4) + pLayerString = "FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4"; + else if(layerID == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4_DISCARD) + pLayerString = "FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4_DISCARD"; + else if(layerID == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6) + pLayerString = "FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6"; + else if(layerID == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6_DISCARD) + pLayerString = "FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6_DISCARD"; + else if(layerID == FWPS_LAYER_ALE_RESOURCE_ASSIGNMENT_V4) + pLayerString = "FWPS_LAYER_ALE_RESOURCE_ASSIGNMENT_V4"; + else if(layerID == FWPS_LAYER_ALE_RESOURCE_ASSIGNMENT_V4_DISCARD) + pLayerString = "FWPS_LAYER_ALE_RESOURCE_ASSIGNMENT_V4_DISCARD"; + else if(layerID == FWPS_LAYER_ALE_RESOURCE_ASSIGNMENT_V6) + pLayerString = "FWPS_LAYER_ALE_RESOURCE_ASSIGNMENT_V6"; + else if(layerID == FWPS_LAYER_ALE_RESOURCE_ASSIGNMENT_V6_DISCARD) + pLayerString = "FWPS_LAYER_ALE_RESOURCE_ASSIGNMENT_V6_DISCARD"; + else if(layerID == FWPS_LAYER_ALE_AUTH_LISTEN_V4) + pLayerString = "FWPS_LAYER_ALE_AUTH_LISTEN_V4"; + else if(layerID == FWPS_LAYER_ALE_AUTH_LISTEN_V4_DISCARD) + pLayerString = "FWPS_LAYER_ALE_AUTH_LISTEN_V4_DISCARD"; + else if(layerID == FWPS_LAYER_ALE_AUTH_LISTEN_V6) + pLayerString = "FWPS_LAYER_ALE_AUTH_LISTEN_V6"; + else if(layerID == FWPS_LAYER_ALE_AUTH_LISTEN_V6_DISCARD) + pLayerString = "FWPS_LAYER_ALE_AUTH_LISTEN_V6_DISCARD"; + else if(layerID == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4) + pLayerString = "FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4"; + else if(layerID == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4_DISCARD) + pLayerString = "FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4_DISCARD"; + else if(layerID == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6) + pLayerString = "FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6"; + else if(layerID == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6_DISCARD) + pLayerString = "FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6_DISCARD"; + else if(layerID == FWPS_LAYER_ALE_AUTH_CONNECT_V4) + pLayerString = "FWPS_LAYER_ALE_AUTH_CONNECT_V4"; + else if(layerID == FWPS_LAYER_ALE_AUTH_CONNECT_V4_DISCARD) + pLayerString = "FWPS_LAYER_ALE_AUTH_CONNECT_V4_DISCARD"; + else if(layerID == FWPS_LAYER_ALE_AUTH_CONNECT_V6) + pLayerString = "FWPS_LAYER_ALE_AUTH_CONNECT_V6"; + else if(layerID == FWPS_LAYER_ALE_AUTH_CONNECT_V6_DISCARD) + pLayerString = "FWPS_LAYER_ALE_AUTH_CONNECT_V6_DISCARD"; + else if(layerID == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4) + pLayerString = "FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4"; + else if(layerID == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4_DISCARD) + pLayerString = "FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4_DISCARD"; + else if(layerID == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6) + pLayerString = "FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6"; + else if(layerID == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6_DISCARD) + pLayerString = "FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6_DISCARD"; + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + else if(layerID == FWPS_LAYER_NAME_RESOLUTION_CACHE_V4) + pLayerString = "FWPS_LAYER_NAME_RESOLUTION_CACHE_V4"; + else if(layerID == FWPS_LAYER_NAME_RESOLUTION_CACHE_V6) + pLayerString = "FWPS_LAYER_NAME_RESOLUTION_CACHE_V6"; + else if(layerID == FWPS_LAYER_ALE_RESOURCE_RELEASE_V4) + pLayerString = "FWPS_LAYER_ALE_RESOURCE_RELEASE_V4"; + else if(layerID == FWPS_LAYER_ALE_RESOURCE_RELEASE_V6) + pLayerString = "FWPS_LAYER_ALE_RESOURCE_RELEASE_V6"; + else if(layerID == FWPS_LAYER_ALE_ENDPOINT_CLOSURE_V4) + pLayerString = "FWPS_LAYER_ALE_ENDPOINT_CLOSURE_V4"; + else if(layerID == FWPS_LAYER_ALE_ENDPOINT_CLOSURE_V6) + pLayerString = "FWPS_LAYER_ALE_ENDPOINT_CLOSURE_V6"; + else if(layerID == FWPS_LAYER_ALE_CONNECT_REDIRECT_V4) + pLayerString = "FWPS_LAYER_ALE_CONNECT_REDIRECT_V4"; + else if(layerID == FWPS_LAYER_ALE_CONNECT_REDIRECT_V6) + pLayerString = "FWPS_LAYER_ALE_CONNECT_REDIRECT_V6"; + else if(layerID == FWPS_LAYER_ALE_BIND_REDIRECT_V4) + pLayerString = "FWPS_LAYER_ALE_BIND_REDIRECT_V4"; + else if(layerID == FWPS_LAYER_ALE_BIND_REDIRECT_V6) + pLayerString = "FWPS_LAYER_ALE_BIND_REDIRECT_V6"; + else if(layerID == FWPS_LAYER_STREAM_PACKET_V4) + pLayerString = "FWPS_LAYER_STREAM_PACKET_V4"; + else if(layerID == FWPS_LAYER_STREAM_PACKET_V6) + pLayerString = "FWPS_LAYER_STREAM_PACKET_V6"; + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + else if(layerID == FWPS_LAYER_INBOUND_MAC_FRAME_ETHERNET) + pLayerString = "FWPS_LAYER_INBOUND_MAC_FRAME_ETHERNET"; + else if(layerID == FWPS_LAYER_OUTBOUND_MAC_FRAME_ETHERNET) + pLayerString = "FWPS_LAYER_OUTBOUND_MAC_FRAME_ETHERNET"; + else if(layerID == FWPS_LAYER_INBOUND_MAC_FRAME_NATIVE) + pLayerString = "FWPS_LAYER_INBOUND_MAC_FRAME_NATIVE"; + else if(layerID == FWPS_LAYER_OUTBOUND_MAC_FRAME_NATIVE) + pLayerString = "FWPS_LAYER_OUTBOUND_MAC_FRAME_NATIVE"; + else if(layerID == FWPS_LAYER_INGRESS_VSWITCH_ETHERNET) + pLayerString = "FWPS_LAYER_INGRESS_VSWITCH_ETHERNET"; + else if(layerID == FWPS_LAYER_EGRESS_VSWITCH_ETHERNET) + pLayerString = "FWPS_LAYER_EGRESS_VSWITCH_ETHERNET"; + else if(layerID == FWPS_LAYER_INGRESS_VSWITCH_TRANSPORT_V4) + pLayerString = "FWPS_LAYER_INGRESS_VSWITCH_TRANSPORT_V4"; + else if(layerID == FWPS_LAYER_INGRESS_VSWITCH_TRANSPORT_V6) + pLayerString = "FWPS_LAYER_INGRESS_VSWITCH_TRANSPORT_V6"; + else if(layerID == FWPS_LAYER_EGRESS_VSWITCH_TRANSPORT_V4) + pLayerString = "FWPS_LAYER_EGRESS_VSWITCH_TRANSPORT_V4"; + else if(layerID == FWPS_LAYER_EGRESS_VSWITCH_TRANSPORT_V6) + pLayerString = "FWPS_LAYER_EGRESS_VSWITCH_TRANSPORT_V6"; + +#if(NTDDI_VERSION >= NTDDI_WINBLUE) + + else if(layerID == FWPS_LAYER_INBOUND_TRANSPORT_FAST) + pLayerString = "FWPS_LAYER_INBOUND_TRANSPORT_FAST"; + else if(layerID == FWPS_LAYER_OUTBOUND_TRANSPORT_FAST) + pLayerString = "FWPS_LAYER_OUTBOUND_TRANSPORT_FAST"; + else if(layerID == FWPS_LAYER_INBOUND_MAC_FRAME_NATIVE_FAST) + pLayerString = "FWPS_LAYER_INBOUND_MAC_FRAME_NATIVE_FAST"; + else if(layerID == FWPS_LAYER_OUTBOUND_MAC_FRAME_NATIVE_FAST) + pLayerString = "FWPS_LAYER_OUTBOUND_MAC_FRAME_NATIVE_FAST"; + +#endif /// (NTDDI_VERSION >= NTDDI_WINBLUE) +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + return pLayerString; +} + +#endif /// FwpsLayer____ diff --git a/network/trans/WFPSampler/syslib/HelperFunctions_FwpObjects.h b/network/trans/WFPSampler/syslib/HelperFunctions_FwpObjects.h new file mode 100644 index 000000000..39f69f542 --- /dev/null +++ b/network/trans/WFPSampler/syslib/HelperFunctions_FwpObjects.h @@ -0,0 +1,737 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_FwpObjects.h +// +// Abstract: +// This module contains prototypes for kernel helper functions that allocate, populate, purge, +// and free structures in memory. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// March 11, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Add support for multiple injectors and add +// KrnlHlprFwpValueGetStringForFwpsIncomingValue, +// KrnlHlprFwpsRedirectHandleCreate, +// KrnlHlprFwpsRedirecthandleDestroy, +// KrnlHlprFwpsLayerIsDiscard, +// KrnlHlprFwpsLayerIDToString functions. +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef HELPERFUNCTIONS_FWP_OBJECTS_H +#define HELPERFUNCTIONS_FWP_OBJECTS_H + +#define ADDRESS_VALUE 1 +#define FLAGS_VALUE 2 +#define L2_FLAGS_VALUE 3 +#define UNICODE_STRING_VALUE 4 + +extern HANDLE g_EngineHandle; +extern HANDLE g_pIPv4InboundMACInjectionHandles[2]; +extern HANDLE g_pIPv4IngressVSwitchEthernetInjectionHandles[2]; +extern HANDLE g_pIPv4InboundForwardInjectionHandles[2]; +extern HANDLE g_pIPv4InboundNetworkInjectionHandles[2]; +extern HANDLE g_pIPv4InboundTransportInjectionHandles[2]; +extern HANDLE g_pIPv4InboundStreamInjectionHandles[2]; +extern HANDLE g_pIPv4OutboundMACInjectionHandles[2]; +extern HANDLE g_pIPv4EgressVSwitchEthernetInjectionHandles[2]; +extern HANDLE g_pIPv4OutboundForwardInjectionHandles[2]; +extern HANDLE g_pIPv4OutboundNetworkInjectionHandles[2]; +extern HANDLE g_pIPv4OutboundTransportInjectionHandles[2]; +extern HANDLE g_pIPv4OutboundStreamInjectionHandles[2]; +extern HANDLE g_pIPv6InboundMACInjectionHandles[2]; +extern HANDLE g_pIPv6IngressVSwitchEthernetInjectionHandles[2]; +extern HANDLE g_pIPv6InboundForwardInjectionHandles[2]; +extern HANDLE g_pIPv6InboundNetworkInjectionHandles[2]; +extern HANDLE g_pIPv6InboundTransportInjectionHandles[2]; +extern HANDLE g_pIPv6InboundStreamInjectionHandles[2]; +extern HANDLE g_pIPv6OutboundMACInjectionHandles[2]; +extern HANDLE g_pIPv6EgressVSwitchEthernetInjectionHandles[2]; +extern HANDLE g_pIPv6OutboundForwardInjectionHandles[2]; +extern HANDLE g_pIPv6OutboundNetworkInjectionHandles[2]; +extern HANDLE g_pIPv6OutboundTransportInjectionHandles[2]; +extern HANDLE g_pIPv6OutboundStreamInjectionHandles[2]; +extern HANDLE g_pInboundMACInjectionHandles[2]; +extern HANDLE g_pIngressVSwitchEthernetInjectionHandles[2]; +extern HANDLE g_pInboundForwardInjectionHandles[2]; +extern HANDLE g_pInboundNetworkInjectionHandles[2]; +extern HANDLE g_pInboundTransportInjectionHandles[2]; +extern HANDLE g_pInboundStreamInjectionHandles[2]; +extern HANDLE g_pOutboundMACInjectionHandles[2]; +extern HANDLE g_pEgressVSwitchEthernetInjectionHandles[2]; +extern HANDLE g_pOutboundForwardInjectionHandles[2]; +extern HANDLE g_pOutboundNetworkInjectionHandles[2]; +extern HANDLE g_pOutboundTransportInjectionHandles[2]; +extern HANDLE g_pOutboundStreamInjectionHandles[2]; +extern HANDLE g_pRedirectionHandles[2]; + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprFwpByteBlobPurgeLocalCopy(_Inout_ FWP_BYTE_BLOB* pBlob); + +_At_(*ppBlob, _Pre_ _Notnull_) +_At_(*ppBlob, _Post_ _Null_ __drv_freesMem(Pool)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(*ppBlob == 0) +inline VOID KrnlHlprFwpByteBlobDestroyLocalCopy(_Inout_ FWP_BYTE_BLOB** ppBlob); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprFwpByteBlobPopulateLocalCopy(_In_ const FWP_BYTE_BLOB* pOriginalBlob, + _Inout_ FWP_BYTE_BLOB* pBlob); + +__drv_allocatesMem(Pool) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Must_inspect_result_ +_Success_(return != 0) +FWP_BYTE_BLOB* KrnlHlprFwpByteBlobCreateLocalCopy(_In_ const FWP_BYTE_BLOB* pOriginalBlob); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprFwpmDisplayDataPurgeLocalCopy(_Inout_ FWPM_DISPLAY_DATA* pData); + +_At_(*ppDisplayData, _Pre_ _Notnull_) +_At_(*ppDisplayData, _Post_ _Null_ __drv_freesMem(Pool)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(*ppDisplayData == 0) +inline VOID KrnlHlprFwpmDisplayDataDestroyLocalCopy(_Inout_ FWPM_DISPLAY_DATA** ppDisplayData); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprFwpmDisplayDataPopulateLocalCopy(_In_ const FWPM_DISPLAY_DATA* pOriginalData, + _Inout_ FWPM_DISPLAY_DATA* pData); + +__drv_allocatesMem(Pool) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Must_inspect_result_ + _Success_(return != 0) + +FWPM_DISPLAY_DATA* KrnlHlprFwpmDisplayDataCreateLocalCopy(_In_ const FWPM_DISPLAY_DATA* pOriginalDisplayData); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprFwpmClassifyOptionPurgeLocalCopy(_Inout_ FWPM_CLASSIFY_OPTION* pOption); + +_At_(*ppOption, _Pre_ _Notnull_) +_At_(*ppOption, _Post_ _Null_ __drv_freesMem(Pool)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(*ppOption == 0) +inline VOID KrnlHlprFwpmClassifyOptionDestroyLocalCopy(_Inout_ FWPM_CLASSIFY_OPTION** ppOption); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprFwpmClassifyOptionPopulateLocalCopy(_In_ const FWPM_CLASSIFY_OPTION* pOriginalOption, + _Inout_ FWPM_CLASSIFY_OPTION* pOption); + +__drv_allocatesMem(Pool) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Must_inspect_result_ +_Success_(return != 0) +FWPM_CLASSIFY_OPTION* KrnlHlprFwpmClassifyOptionCreateLocalCopy(_In_ const FWPM_CLASSIFY_OPTION* pOption); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprFwpmClassifyOptionsPurgeLocalCopy(_Inout_ FWPM_CLASSIFY_OPTIONS* pOptions); + +_At_(*ppOptions, _Pre_ _Notnull_) +_At_(*ppOptions, _Post_ _Null_ __drv_freesMem(Pool)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(*ppOptions == 0) +inline VOID KrnlHlprFwpmClassifyOptionsDestroyLocalCopy(_Inout_ FWPM_CLASSIFY_OPTIONS** ppOptions); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprFwpmClassifyOptionsPopulateLocalCopy(_In_ const FWPM_CLASSIFY_OPTIONS* pOriginalOptions, + _Inout_ FWPM_CLASSIFY_OPTIONS* pOptions); + +__drv_allocatesMem(Pool) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Must_inspect_result_ +_Success_(return != 0) +FWPM_CLASSIFY_OPTIONS* KrnlHlprFwpmClassifyOptionsCreateLocalCopy(_In_ const FWPM_CLASSIFY_OPTIONS* pOriginalOptions); + +#if(NTDDI_VERSION >= NTDDI_WIN7) + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprIPsecDoSPOptionsPurgeLocalCopy(_Inout_ IPSEC_DOSP_OPTIONS* pOptions); + +_At_(*ppOptions, _Pre_ _Notnull_) +_At_(*ppOptions, _Post_ _Null_ __drv_freesMem(Pool)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(*ppOptions == 0) +inline VOID KrnlHlprIPsecDoSPOptionsDestroyLocalCopy(_Inout_ IPSEC_DOSP_OPTIONS** ppOptions); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprIPsecDoSPOptionsPopulateLocalCopy(_In_ const IPSEC_DOSP_OPTIONS* pOriginalOptions, + _Inout_ IPSEC_DOSP_OPTIONS* pOptions); + +__drv_allocatesMem(Pool) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Must_inspect_result_ +_Success_(return != 0) +IPSEC_DOSP_OPTIONS* KrnlHlprIPsecDoSPOptionsCreateLocalCopy(_In_ const IPSEC_DOSP_OPTIONS* pOriginalOptions); + +#endif // (NTDDI_VERSION >= NTDDI_WIN7) + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprFwpTokenInformationPurgeLocalCopy(_Inout_ FWP_TOKEN_INFORMATION* pTokenInfo); + +_At_(*ppTokenInfo, _Pre_ _Notnull_) +_At_(*ppTokenInfo, _Post_ _Null_ __drv_freesMem(Pool)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(*ppTokenInfo == 0) +inline VOID KrnlHlprFwpTokenInformationDestroyLocalCopy(_Inout_ FWP_TOKEN_INFORMATION** ppTokenInfo); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprFwpTokenInformationPopulateLocalCopy(_In_ const FWP_TOKEN_INFORMATION* pOriginalTokenInfo, + _Inout_ FWP_TOKEN_INFORMATION* pTokenInfo); + +__drv_allocatesMem(Pool) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Must_inspect_result_ +_Success_(return != 0) +FWP_TOKEN_INFORMATION* KrnlHlprFwpTokenInformationCreateLocalCopy(_In_ const FWP_TOKEN_INFORMATION* pOriginalTokenInfo); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(return != 0) +PSTR KrnlHlprFwpValueGetStringForFwpsIncomingValue(_In_ UINT32 layerID, + _In_ UINT32 fieldID, + _Out_opt_ BOOLEAN* pFormat = 0); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Must_inspect_result_ +_Success_(return != 0) +FWP_VALUE* KrnlHlprFwpValueGetFromFwpsIncomingValues(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const GUID* pConditionKey); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprFwpValuePurgeLocalCopy(_Inout_ FWP_VALUE* pValue); + +_At_(*ppValue, _Pre_ _Notnull_) +_At_(*ppValue, _Post_ _Null_ __drv_freesMem(Pool)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(*ppValue == 0) +inline VOID KrnlHlprFwpValueDestroyLocalCopy(_Inout_ FWP_VALUE** ppValue); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprFwpValuePopulateLocalCopy(_In_ const FWP_VALUE* pOriginalValue, + _Inout_ FWP_VALUE* pValue); + +__drv_allocatesMem(Pool) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Must_inspect_result_ +_Success_(return != 0) +FWP_VALUE* KrnlHlprFwpValueCreateLocalCopy(_In_ const FWP_VALUE* pOriginalValue); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprFwpV4AddrAndMaskPurgeLocalCopy(_Inout_ FWP_V4_ADDR_AND_MASK* pAddrMask); + +_At_(*ppAddrMask, _Pre_ _Notnull_) +_At_(*ppAddrMask, _Post_ _Null_ __drv_freesMem(Pool)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(*ppAddrMask == 0) +inline VOID KrnlHlprFwpV4AddrAndMaskDestroyLocalCopy(_Inout_ FWP_V4_ADDR_AND_MASK** ppAddrMask); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprFwpV4AddrAndMaskPopulateLocalCopy(_In_ const FWP_V4_ADDR_AND_MASK* pOriginalAddrMask, + _Inout_ FWP_V4_ADDR_AND_MASK* pAddrMask); + +__drv_allocatesMem(Pool) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Must_inspect_result_ +_Success_(return != 0) +FWP_V4_ADDR_AND_MASK* KrnlHlprFwpV4AddrAndMaskCreateLocalCopy(_In_ const FWP_V4_ADDR_AND_MASK* pOriginalAddrMask); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprFwpV6AddrAndMaskPurgeLocalCopy(_Inout_ FWP_V6_ADDR_AND_MASK* pAddrMask); + +_At_(*ppAddrMask, _Pre_ _Notnull_) +_At_(*ppAddrMask, _Post_ _Null_ __drv_freesMem(Pool)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(*ppAddrMask == 0) +inline VOID KrnlHlprFwpV6AddrAndMaskDestroyLocalCopy(_Inout_ FWP_V6_ADDR_AND_MASK** ppAddrMask); + +__drv_allocatesMem(Pool) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +_Success_(return != 0) +NTSTATUS KrnlHlprFwpV6AddrAndMaskCreateLocalCopy(_In_ const FWP_V6_ADDR_AND_MASK* pOriginalAddrMask, + _Inout_ FWP_V6_ADDR_AND_MASK* pAddrMask); + +__drv_allocatesMem(Pool) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Must_inspect_result_ +_Success_(return != 0) +FWP_V6_ADDR_AND_MASK* KrnlHlprFwpV6AddrAndMaskCreateLocalCopy(_In_ const FWP_V6_ADDR_AND_MASK* pOriginalAddrMask); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprFwpRangePurgeLocalCopy(_Inout_ FWP_RANGE* pRange); + +_At_(*ppRange, _Pre_ _Notnull_) +_At_(*ppRange, _Post_ _Null_ __drv_freesMem(Pool)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(*ppRange == 0) +inline VOID KrnlHlprFwpRangeDestroyLocalCopy(_Inout_ FWP_RANGE** ppRange); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprFwpRangePopulateLocalCopy(_In_ const FWP_RANGE* pOriginalRange, + _Inout_ FWP_RANGE* pRange); + +__drv_allocatesMem(Pool) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Must_inspect_result_ +_Success_(return != 0) +FWP_RANGE* KrnlHlprFwpRangeCreateLocalCopy(_In_ const FWP_RANGE* pOriginalRange); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprFwpConditionValuePurgeLocalCopy(_Inout_ FWP_CONDITION_VALUE* pValue); + +_At_(*ppValue, _Pre_ _Notnull_) +_At_(*ppValue, _Post_ _Null_ __drv_freesMem(Pool)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(*ppValue == 0) +inline VOID KrnlHlprFwpConditionValueDestroyLocalCopy(_Inout_ FWP_CONDITION_VALUE** ppValue); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprFwpConditionValuePopulateLocalCopy(_In_ const FWP_CONDITION_VALUE* pOriginalValue, + _Inout_ FWP_CONDITION_VALUE* pValue); + +__drv_allocatesMem(Pool) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Must_inspect_result_ +_Success_(return != 0) +FWP_CONDITION_VALUE* KrnlHlprFwpConditionValueCreateLocalCopy(_In_ const FWP_CONDITION_VALUE* pOriginalValue); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +BOOLEAN KrnlHlprFwpsIncomingValueConditionFlagsAreSet(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ UINT32 flags); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprFwpsIncomingValuesPurgeLocalCopy(_Inout_ FWPS_INCOMING_VALUES* pOriginalValues); + +_At_(*ppValues, _Pre_ _Notnull_) +_At_(*ppValues, _Post_ _Null_ __drv_freesMem(Pool)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(*ppValues == 0) +inline VOID KrnlHlprFwpsIncomingValuesDestroyLocalCopy(_Inout_ FWPS_INCOMING_VALUES** ppValues); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprFwpsIncomingValuesPopulateLocalCopy(_In_ const FWPS_INCOMING_VALUES* pOriginalValues, + _Inout_ FWPS_INCOMING_VALUES* pValues); + +__drv_allocatesMem(Pool) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Must_inspect_result_ +_Success_(return != 0) +FWPS_INCOMING_VALUES* KrnlHlprFwpsIncomingValuesCreateLocalCopy(_In_ const FWPS_INCOMING_VALUES* pOriginalValues); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprFwpsIncomingMetadataValuesPurgeLocalCopy(_Inout_ FWPS_INCOMING_METADATA_VALUES* pMetadata); + +_At_(*ppMetadata, _Pre_ _Notnull_) +_At_(*ppMetadata, _Post_ _Null_ __drv_freesMem(Pool)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(*ppMetadata == 0) +inline VOID KrnlHlprFwpsIncomingMetadataValuesDestroyLocalCopy(_Inout_ FWPS_INCOMING_METADATA_VALUES** ppMetadata); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprFwpsIncomingMetadataValuesPopulateLocalCopy(_In_ const FWPS_INCOMING_METADATA_VALUES* pOriginalMetadata, + _Inout_ FWPS_INCOMING_METADATA_VALUES* pMetadata); + + +__drv_allocatesMem(Pool) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Must_inspect_result_ +_Success_(return != 0) +FWPS_INCOMING_METADATA_VALUES* KrnlHlprFwpsIncomingMetadataValuesCreateLocalCopy(_In_ const FWPS_INCOMING_METADATA_VALUES* pOriginalMetadata); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprFwpsClassifyOutPurgeLocalCopy(_Inout_ FWPS_CLASSIFY_OUT* pOriginalClassifyOut); + +_At_(*ppClassifyOut, _Pre_ _Notnull_) +_At_(*ppClassifyOut, _Post_ _Null_ __drv_freesMem(Pool)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(*ppClassifyOut == 0) +VOID KrnlHlprFwpsClassifyOutDestroyLocalCopy(_Inout_ FWPS_CLASSIFY_OUT** ppClassifyOut); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprFwpsIncomingValuesPopulateLocalCopy(_In_ const FWPS_CLASSIFY_OUT* pOriginalClassifyOut, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut); + +__drv_allocatesMem(Pool) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Must_inspect_result_ +_Success_(return != 0) +FWPS_CLASSIFY_OUT* KrnlHlprFwpsClassifyOutCreateLocalCopy(_In_ const FWPS_CLASSIFY_OUT* pOriginalClassifyOut); + + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprFwpsStreamDataPurgeLocalCopy(_Inout_ FWPS_STREAM_DATA* pStreamData); + +_At_(*ppStreamData, _Pre_ _Notnull_) +_At_(*ppStreamData, _Post_ _Null_ __drv_freesMem(Pool)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(*ppStreamData == 0) +inline VOID KrnlHlprFwpsStreamDataDestroyLocalCopy(_Inout_ FWPS_STREAM_DATA** ppStreamData); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprFwpsStreamDataPopulateLocalCopy(_In_ const FWPS_STREAM_DATA* pOriginalStreamData, + _Inout_ FWPS_STREAM_DATA* pStreamData); + +__drv_allocatesMem(Pool) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Must_inspect_result_ +_Success_(return != 0) +FWPS_STREAM_DATA* KrnlHlprFwpsStreamDataCreateLocalCopy(_In_ const FWPS_STREAM_DATA* pOriginalStreamData); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprFwpsStreamCalloutIOPacketPurgeLocalCopy(_Inout_ FWPS_STREAM_CALLOUT_IO_PACKET* pIOPacket); + +_At_(*ppIOPacket, _Pre_ _Notnull_) +_At_(*ppIOPacket, _Post_ _Null_ __drv_freesMem(Pool)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(*ppIOPacket == 0) +inline VOID KrnlHlprFwpsStreamCalloutIOPacketDestroyLocalCopy(_Inout_ FWPS_STREAM_CALLOUT_IO_PACKET** ppIOPacket); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprFwpsStreamCalloutIOPacketPopulateLocalCopy(_In_ const FWPS_STREAM_CALLOUT_IO_PACKET* pOriginalIOPacket, + _Inout_ FWPS_STREAM_CALLOUT_IO_PACKET* pIOPacket); + +__drv_allocatesMem(Pool) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Must_inspect_result_ +_Success_(return != 0) +FWPS_STREAM_CALLOUT_IO_PACKET* KrnlHlprFwpsStreamCalloutIOPacketCreateLocalCopy(_In_ const FWPS_STREAM_CALLOUT_IO_PACKET* pOriginalIOPacket); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprFwpmProviderContextPurgeLocalCopy(_Inout_ FWPM_PROVIDER_CONTEXT* pContext); + +_At_(*ppContext, _Pre_ _Notnull_) +_At_(*ppContext, _Post_ _Null_ __drv_freesMem(Pool)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(*ppContext == 0) +inline VOID KrnlHlprFwpmProviderContextDestroyLocalCopy(_Inout_ FWPM_PROVIDER_CONTEXT** ppContext); + +__drv_allocatesMem(Pool) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprFwpmProviderContextCreateLocalCopy(_In_ const FWPM_PROVIDER_CONTEXT* pOriginalContext, + _Inout_ FWPM_PROVIDER_CONTEXT* pContext); + +__drv_allocatesMem(Pool) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Must_inspect_result_ +_Success_(return != 0) +FWPM_PROVIDER_CONTEXT* KrnlHlprFwpmProviderContextCreateLocalCopy(_In_ const FWPM_PROVIDER_CONTEXT* pOriginalContext); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprFwpsFilterConditionPurgeLocalCopy(_Inout_ FWPS_FILTER_CONDITION* pCondition); + +_At_(*ppConditions, _Pre_ _Notnull_) +_At_(*ppConditions, _Post_ _Null_ __drv_freesMem(Pool)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(*ppConditions == 0) +inline VOID KrnlHlprFwpsFilterConditionDestroyLocalCopy(_Inout_ FWPS_FILTER_CONDITION** ppConditions, + _In_ UINT32 numConditions = 1); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprFwpsFilterConditionPopulateLocalCopy(_In_ const FWPS_FILTER_CONDITION* pOriginalCondition, + _Inout_ FWPS_FILTER_CONDITION* pCondition); + +__drv_allocatesMem(Pool) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Must_inspect_result_ +_Success_(return != 0) +FWPS_FILTER_CONDITION* KrnlHlprFwpsFilterConditionCreateLocalCopy(_In_reads_(numConditions) const FWPS_FILTER_CONDITION* pOriginalConditions, + _In_ UINT32 numConditions = 1); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprFwpsFilterPurgeLocalCopy(_Inout_ FWPS_FILTER* pFilter); + +_At_(*ppFilter, _Pre_ _Notnull_) +_At_(*ppFilter, _Post_ _Null_ __drv_freesMem(Pool)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(*ppFilter == 0) +inline VOID KrnlHlprFwpsFilterDestroyLocalCopy(_Inout_ FWPS_FILTER** ppFilter); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprFwpsFilterPopulateLocalCopy(_In_ const FWPS_FILTER* pOriginalFilter, + _Inout_ FWPS_FILTER* pFilter); + +__drv_allocatesMem(Pool) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Must_inspect_result_ +_Success_(return != 0) +FWPS_FILTER* KrnlHlprFwpsFilterCreateLocalCopy(_In_ const FWPS_FILTER* pOriginalFilter); + +_IRQL_requires_(PASSIVE_LEVEL) +_IRQL_requires_same_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprFwpmSessionReleaseHandle(_Inout_ HANDLE* pEngineHandle); + +_At_(*ppEngineHandle, _Pre_ _Notnull_) +_At_(*ppEngineHandle, _Post_ _Null_) +_IRQL_requires_(PASSIVE_LEVEL) +_IRQL_requires_same_ +_Success_(*ppEngineHandle == 0) +VOID KrnlHlprFwpmSessionDestroyEngineHandle(_Inout_ HANDLE** ppEngineHandle); + +_IRQL_requires_(PASSIVE_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprFwpmSessionAcquireHandle(_Inout_ HANDLE* pEngineHandle, + _In_ const GUID* pSessionKey = &WFPSAMPLER_SESSION_KM); + +_When_(return != STATUS_SUCCESS, _At_(*ppEngineHandle, _Post_ _Null_)) +_When_(return == STATUS_SUCCESS, _At_(*ppEngineHandle, _Post_ _Notnull_)) +_IRQL_requires_(PASSIVE_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprFwpmSessionCreateEngineHandle(_Outptr_ HANDLE** ppEngineHandle, + _In_ const GUID* pSessionKey = &WFPSAMPLER_SESSION_KM); + +_IRQL_requires_(PASSIVE_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprFwpsInjectionReleaseHandle(_In_ HANDLE* pInjectionHandle); + +_IRQL_requires_(PASSIVE_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprFwpsInjectionAcquireHandle(_Inout_ HANDLE* pInjectionHandle, + _In_ ADDRESS_FAMILY addressFamily = AF_UNSPEC, + _In_ UINT32 injectionType = 0); + +_IRQL_requires_(PASSIVE_LEVEL) +_IRQL_requires_same_ +VOID KrnlHlprFwpsRedirectHandleDestroy(_In_ HANDLE* pRedirectionHandle); + +_IRQL_requires_(PASSIVE_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprFwpsRedirectHandleCreate(_Inout_ HANDLE* pRedirectionHandle = 0); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +FWP_DIRECTION KrnlHlprFwpsLayerGetDirection(_In_ UINT32 layerID); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +BOOLEAN KrnlHlprFwpsLayerIsIPv4(_In_ UINT32 layerID); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +BOOLEAN KrnlHlprFwpsLayerIsIPv6(_In_ UINT32 layerID); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +BOOLEAN KrnlHlprFwpsLayerIsDiscard(_In_ UINT32 layerID); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(return != 0) +PSTR KrnlHlprFwpsLayerIDToString(_In_ UINT32 layerID); + +#endif /// HELPERFUNCTIONS_FWP_OBJECTS_H diff --git a/network/trans/WFPSampler/syslib/HelperFunctions_Headers.cpp b/network/trans/WFPSampler/syslib/HelperFunctions_Headers.cpp new file mode 100644 index 000000000..3a2a7785a --- /dev/null +++ b/network/trans/WFPSampler/syslib/HelperFunctions_Headers.cpp @@ -0,0 +1,3899 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_Headers.cpp +// +// Abstract: +// This module contains kernel helper functions that assist with IP and Transport header +// operations. +// +// Naming Convention: +// +// +// +// i.e. +// +// KrnlHlprIPHeaderCalculateV4Checksum +// +// +// KrnlHlpr - Function is located in syslib\ and applies to kernel mode. +// +// { +// IPHeader - Function pertains to the network's IP_HEADER. +// ICMPv4Header - Function pertains to the transport's ICMPV4_HEADER. +// ICMPv6Header - Function pertains to the transport's ICMPV6_HEADER. +// TCPHeader - Function pertains to the transport's TCP_HEADER. +// UDPHeader - Function pertains to the transport's UDP_HEADER. +// } +// +// { +// Calculate - Function performs a computation on the header. +// Modify - Function changes the field to the provided value. +// } +// +// { +// Code - Function acts on the ICMP Code Field. +// Destination Address - Function acts on the IP Destination Address Field. +// Destination Port - Function acts on the TCP / UDP Destination Port Field. +// Source Address - Function acts on the IP Source Address Field. +// Source Port - Function acts on the TCP / UDP Source Port Field. +// Type - Function acts on the ICMP Type Field. +// V4Checksum - Function pertains to IPv4 packets and its Checksum Field. +// } +// +// Private Functions: +// +// Public Functions: +// KrnlHlprICMPv4HeaderModifyCode(), +// KrnlHlprICMPv4HeaderModifyType(), +// KrnlHlprICMPv6HeaderModifyCode(), +// KrnlHlprICMPv6HeaderModifyType(), +// KrnlHlprIPHeaderCalculateV4Checksum(), +// KrnlHlprIPHeaderDestroy(), +// KrnlHlprIPHeaderGet(), +// KrnlHlprIPHeaderGetProtocolField(), +// KrnlHlprIPHeaderModifyDestinationAddress(), +// KrnlHlprIPHeaderModifyLoopbackToLocal(), +// KrnlHlprIPHeaderModifySourceAddress(), +// KrnlHlprMACHeaderDestroy(), +// KrnlHlprMACHeaderGet(), +// KrnlHlprMACHeaderModifyDestinationAddress(), +// KrnlHlprMACHeaderModifySourceAddress(), +// KrnlHlprTCPHeaderModifyDestinationPort(), +// KrnlHlprTCPHeaderModifySourcePort(), +// KrnlHlprUDPHeaderModifyDestinationPort(), +// KrnlHlprUDPHeaderModifySourcePort(), +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Enhance annotations, add +// KrnlHlprIPHeaderGetDestinationAddressField, +// KrnlHlprIPHeaderGetSourceAddressField, +// KrnlHlprIPHeaderGetVersionField, +// KrnlHlprTransportHeaderGetSourcePortField, +// KrnlHlprTransportHeaderGetDestinationPortField, add +// support for controlData, and fix various bugs. +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "HelperFunctions_Include.h" /// . +#include "HelperFunctions_Headers.tmh" /// $(OBJ_PATH)\$(O)\ + +/** + @private_kernel_helper_function="PrvKrnlHlprCopyBufferToMDL" + + Purpose: Copy a flat buffer into an MDL chain.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +NTSTATUS PrvKrnlHlprCopyBufferToMDL(_In_reads_(bytesToCopy) const BYTE* pBuffer, + _In_ PMDL pMDL, + _In_ SIZE_T mdlOffset, + _In_ SIZE_T bytesToCopy, + _Out_ SIZE_T* pBytesCopied) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PrvKrnlHlprCopyBufferToMDL()\n"); + +#endif /// DBG + + NTSTATUS status = STATUS_SUCCESS; + SIZE_T mdlByteCount = 0; + SIZE_T remainingBytesToCopy = bytesToCopy; + SIZE_T copySize = 0; + + *pBytesCopied = 0; + + if(MmGetMdlByteCount(pMDL) >= mdlOffset + bytesToCopy) + { + BYTE* pSystemAddress = 0; + + pSystemAddress = (BYTE*)MmGetSystemAddressForMdlSafe(pMDL, + LowPagePriority); + if(pSystemAddress) + { + RtlCopyMemory(pSystemAddress + mdlOffset, + pBuffer, + bytesToCopy); + + remainingBytesToCopy = 0; + + HLPR_BAIL; + } + } + + /// Skip over the offset in the MDL chain + for(mdlByteCount = MmGetMdlByteCount(pMDL); + pMDL && + mdlOffset >= mdlByteCount; + mdlByteCount = MmGetMdlByteCount(pMDL)) + { + mdlOffset -= mdlByteCount; + + pMDL = pMDL->Next; + } + + /// Copy data while there are MDLs to walk and data to copy + for(; + pMDL && + remainingBytesToCopy > 0; + pMDL = pMDL->Next) + { + BYTE* pSystemAddress = 0; + + mdlByteCount = MmGetMdlByteCount(pMDL); + if(mdlByteCount) + { + ASSERT(mdlOffset < mdlByteCount); + + mdlByteCount -= mdlOffset; + + copySize = min(remainingBytesToCopy, + mdlByteCount); + + pSystemAddress = (BYTE*)MmGetSystemAddressForMdlSafe(pMDL, + LowPagePriority); + if(pSystemAddress) + { + RtlCopyMemory(pSystemAddress + mdlOffset, + pBuffer, + copySize); + + pBuffer += copySize; + + remainingBytesToCopy -= copySize; + + mdlOffset = 0; + } + else + { + status = STATUS_INSUFFICIENT_RESOURCES; + + HLPR_BAIL; + } + } + } + + HLPR_BAIL_LABEL: + + *pBytesCopied = bytesToCopy - remainingBytesToCopy; + + ASSERT(*pBytesCopied <= bytesToCopy); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PrvKrnlHlprCopyBufferToMDL() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +#ifndef MAC_HEADER____ +#define MAC_HEADER____ + +/** + @kernel_helper_function="KrnlHlprMACHeaderDestroy" + + Purpose: Frees the allocated memory indicated in KrnlMACHeaderGet().
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_At_(*ppMACHeader, _Pre_ _Notnull_) +_At_(*ppMACHeader, _Post_ _Null_) +_Success_(*ppMACHeader == 0) +inline VOID KrnlHlprMACHeaderDestroy(_Inout_ VOID** ppMACHeader) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprMACHeaderDestroy()\n"); + +#endif /// DBG + + NT_ASSERT(ppMACHeader); + + HLPR_DELETE_ARRAY(*ppMACHeader, + WFPSAMPLER_SYSLIB_TAG); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprMACHeaderDestroy()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprMACHeaderGet" + + Purpose: Retrieve a pointer to the MAC Header from the NET_BUFFER_LIST.
+
+ Notes: Assumes the NBL is at the start of the MAC Header.
+
+ Function is overloaded.
+
+ If needToFreeMemory is TRUE, caller should call KrnlHlprMACHeaderDestroy() when + finished with the header.
+
+ MSDN_Ref:
+*/ +_When_(return != STATUS_SUCCESS, _At_(*ppMACHeader, _Post_ _Null_)) +_When_(return == STATUS_SUCCESS, _At_(*ppMACHeader, _Post_ _Notnull_)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprMACHeaderGet(_In_ NET_BUFFER_LIST* pNetBufferList, + _Outptr_ VOID** ppMACHeader, + _Inout_ BOOLEAN* pNeedToFreeMemory, + _In_ UINT32 macHeaderSize) /* 0 */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprMACHeaderGet()\n"); + +#endif /// DBG + + NT_ASSERT(pNetBufferList); + NT_ASSERT(ppMACHeader); + NT_ASSERT(pNeedToFreeMemory); + + NTSTATUS status = STATUS_SUCCESS; + BYTE* pBuffer = 0; + NET_BUFFER* pNetBuffer = NET_BUFFER_LIST_FIRST_NB(pNetBufferList); + UINT32 bytesNeeded = macHeaderSize ? macHeaderSize : NET_BUFFER_DATA_LENGTH(pNetBuffer); + PVOID pContiguousData = 0; + +#pragma warning(push) +#pragma warning(disable: 6014) /// pBuffer is expected to be cleaned up by caller using KrnlHlprMACHeaderDestroy if *pNeedToFreeMemory is TRUE + + HLPR_NEW_ARRAY(pBuffer, + BYTE, + bytesNeeded, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pBuffer, + status); + +#pragma warning(pop) + + *pNeedToFreeMemory = TRUE; + + pContiguousData = NdisGetDataBuffer(pNetBuffer, + bytesNeeded, + pBuffer, + 1, + 0); + if(!pContiguousData) + { + status = STATUS_UNSUCCESSFUL; + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! KrnlHlprMACHeaderGet : NdisGetDataBuffer() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + if(pBuffer != pContiguousData) + { + HLPR_DELETE_ARRAY(pBuffer, + WFPSAMPLER_SYSLIB_TAG); + + *pNeedToFreeMemory = FALSE; + } + + *ppMACHeader = pContiguousData; + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS && + *pNeedToFreeMemory && + pBuffer) + { + KrnlHlprMACHeaderDestroy((VOID**)&pBuffer); + + *pNeedToFreeMemory = FALSE; + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprMACHeaderGet() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @kernel_helper_function="KrnlHlprMACHeaderModifySourceAddress" + + Purpose: Set the Source Address field in the MAC Header to the provided value.
+
+ Notes: The NetBufferList parameter is expected to be offset to the start of the MAC + Header.
+
+ Assumes the Header is and Ethernet Header.
+
+ MSDN_Ref:
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprMACHeaderModifySourceAddress(_In_ const FWP_VALUE* pValue, + _Inout_ NET_BUFFER_LIST* pNetBufferList) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprMACHeaderModifySourceAddress()\n"); + +#endif /// DBG + + NT_ASSERT(pValue); + NT_ASSERT(pNetBufferList); + + NTSTATUS status = STATUS_SUCCESS; + VOID* pMACHeader = 0; + BOOLEAN needToFree = FALSE; + ETHERNET_II_HEADER* pEthernetHeader = 0; + + status = KrnlHlprMACHeaderGet(pNetBufferList, + &pMACHeader, + &needToFree); + HLPR_BAIL_ON_FAILURE(status); + + pEthernetHeader = (ETHERNET_II_HEADER*)pMACHeader; + + RtlCopyMemory(pEthernetHeader->pSourceAddress, + pValue->byteArray6->byteArray6, + ETHERNET_ADDRESS_SIZE); + + HLPR_BAIL_LABEL: + + if(needToFree) + { + /// Copy the contents of the allocated buffer to the NBL's discontiguous buffer + if(status == STATUS_SUCCESS) + { + NET_BUFFER* pFirstNetBuffer = NET_BUFFER_LIST_FIRST_NB(pNetBufferList); + PMDL pCurrentMDL = NET_BUFFER_CURRENT_MDL(pFirstNetBuffer); + SIZE_T currentMDLOffset = NET_BUFFER_CURRENT_MDL_OFFSET(pFirstNetBuffer); + SIZE_T headerSize = sizeof(ETHERNET_II_HEADER); + SIZE_T bytesCopied = 0; + + status = PrvKrnlHlprCopyBufferToMDL((BYTE*)pEthernetHeader, + pCurrentMDL, + currentMDLOffset, + headerSize, + &bytesCopied); + if(status == STATUS_SUCCESS && + bytesCopied != headerSize) + status = STATUS_INSUFFICIENT_RESOURCES; + } + + KrnlHlprMACHeaderDestroy(&pMACHeader); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprMACHeaderModifySourceAddress() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @kernel_helper_function="KrnlHlprMACHeaderModifyDestinationAddress" + + Purpose: Set the Destination Address field in the MAC Header to the provided value.
+
+ Notes: The NetBufferList parameter is expected to be offset to the start of the MAC + Header.
+
+ Assumes the Header is and Ethernet Header.
+
+ MSDN_Ref:
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprMACHeaderModifyDestinationAddress(_In_ const FWP_VALUE* pValue, + _Inout_ NET_BUFFER_LIST* pNetBufferList) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprMACHeaderModifyDestinationAddress()\n"); + +#endif /// DBG + + NT_ASSERT(pValue); + NT_ASSERT(pNetBufferList); + + NTSTATUS status = STATUS_SUCCESS; + VOID* pMACHeader = 0; + BOOLEAN needToFree = FALSE; + ETHERNET_II_HEADER* pEthernetHeader = 0; + + status = KrnlHlprMACHeaderGet(pNetBufferList, + &pMACHeader, + &needToFree); + HLPR_BAIL_ON_FAILURE(status); + + pEthernetHeader = (ETHERNET_II_HEADER*)pMACHeader; + + RtlCopyMemory(pEthernetHeader->pDestinationAddress, + pValue->byteArray6->byteArray6, + ETHERNET_ADDRESS_SIZE); + + HLPR_BAIL_LABEL: + + if(needToFree) + { + /// Copy the contents of the allocated buffer to the NBL's discontiguous buffer + if(status == STATUS_SUCCESS) + { + NET_BUFFER* pFirstNetBuffer = NET_BUFFER_LIST_FIRST_NB(pNetBufferList); + PMDL pCurrentMDL = NET_BUFFER_CURRENT_MDL(pFirstNetBuffer); + SIZE_T currentMDLOffset = NET_BUFFER_CURRENT_MDL_OFFSET(pFirstNetBuffer); + SIZE_T headerSize = sizeof(ETHERNET_II_HEADER); + SIZE_T bytesCopied = 0; + + status = PrvKrnlHlprCopyBufferToMDL((BYTE*)pEthernetHeader, + pCurrentMDL, + currentMDLOffset, + headerSize, + &bytesCopied); + if(status == STATUS_SUCCESS && + bytesCopied != headerSize) + status = STATUS_INSUFFICIENT_RESOURCES; + } + + KrnlHlprMACHeaderDestroy(&pMACHeader); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprMACHeaderModifyDestinationAddress() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +#endif /// MAC_HEADER____ + +#ifndef IP_HEADER____ +#define IP_HEADER____ + +/** + @kernel_helper_function="KrnlHlprIPHeaderDestroy" + + Purpose: Frees the allocated memory indicated in KrnlIPHeaderGet().
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_At_(*ppIPHeader, _Pre_ _Notnull_) +_At_(*ppIPHeader, _Post_ _Null_) +_Success_(*ppIPHeader == 0) +inline VOID KrnlHlprIPHeaderDestroy(_Inout_ VOID** ppIPHeader) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprIPHeaderDestroy()\n"); + +#endif /// DBG + + NT_ASSERT(ppIPHeader); + + HLPR_DELETE_ARRAY(*ppIPHeader, + WFPSAMPLER_SYSLIB_TAG); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprIPHeaderDestroy()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprIPHeaderGet" + + Purpose: Retrieve a pointer to the IP Header from the NET_BUFFER_LIST.
+
+ Notes: Function is overloaded .
+
+ If needToFreeMemory is TRUE, caller should call KrnlHlprIPHeaderDestroy() when + finished with the header.
+
+ MSDN_Ref:
+*/ +_When_(return != STATUS_SUCCESS, _At_(*ppIPHeader, _Post_ _Null_)) +_When_(return == STATUS_SUCCESS, _At_(*ppIPHeader, _Post_ _Notnull_)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprIPHeaderGet(_In_ NET_BUFFER_LIST* pNetBufferList, + _In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Outptr_result_buffer_(*pIPHeaderSize) VOID** ppIPHeader, + _Inout_ BOOLEAN* pNeedToFreeMemory, + _Inout_opt_ FWP_DIRECTION* pDirection, /* 0 */ + _Inout_opt_ UINT32* pIPHeaderSize) /* 0 */ +{ + NT_ASSERT(pNetBufferList); + NT_ASSERT(pClassifyValues); + NT_ASSERT(pMetadata); + NT_ASSERT(ppIPHeader); + NT_ASSERT(pNeedToFreeMemory); + + NTSTATUS status = STATUS_SUCCESS; + UINT32 bytesRetreated = 0; + UINT32 bytesAdvanced = 0; + UINT32 ipHeaderSize = 0; + UINT32 transportHeaderSize = 0; + FWP_DIRECTION direction = FWP_DIRECTION_MAX; + BOOLEAN ipHeaderAvailable = TRUE; + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + UINT32 ethernetHeaderSize = 0; + + if(FWPS_IS_L2_METADATA_FIELD_PRESENT(pMetadata, + FWPS_L2_METADATA_FIELD_ETHERNET_MAC_HEADER_SIZE)) + ethernetHeaderSize = pMetadata->ethernetMacHeaderSize; + +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_IP_HEADER_SIZE)) + ipHeaderSize = pMetadata->ipHeaderSize; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + transportHeaderSize = pMetadata->transportHeaderSize; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_PACKET_DIRECTION)) + direction = pMetadata->packetDirection; + + switch(pClassifyValues->layerId) + { + case FWPS_LAYER_INBOUND_IPPACKET_V4: + case FWPS_LAYER_INBOUND_IPPACKET_V6: + { + direction = FWP_DIRECTION_INBOUND; + + bytesRetreated = ipHeaderSize; + + break; + } + case FWPS_LAYER_INBOUND_IPPACKET_V4_DISCARD: + case FWPS_LAYER_INBOUND_IPPACKET_V6_DISCARD: + { + direction = FWP_DIRECTION_INBOUND; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_DISCARD_REASON)) + { + if(pMetadata->discardMetadata.discardModule == FWPS_DISCARD_MODULE_GENERAL && + pMetadata->discardMetadata.discardReason == FWPS_DISCARD_FIREWALL_POLICY) + bytesRetreated = ipHeaderSize; + } + + break; + } + case FWPS_LAYER_OUTBOUND_IPPACKET_V4: + case FWPS_LAYER_OUTBOUND_IPPACKET_V4_DISCARD: + case FWPS_LAYER_OUTBOUND_IPPACKET_V6: + case FWPS_LAYER_OUTBOUND_IPPACKET_V6_DISCARD: + { + direction = FWP_DIRECTION_OUTBOUND; + + /// At the IP Header + + break; + } + case FWPS_LAYER_IPFORWARD_V4: + case FWPS_LAYER_IPFORWARD_V4_DISCARD: + case FWPS_LAYER_IPFORWARD_V6: + case FWPS_LAYER_IPFORWARD_V6_DISCARD: + { + /// At the IP Header + + break; + } + case FWPS_LAYER_INBOUND_TRANSPORT_V4: + case FWPS_LAYER_INBOUND_TRANSPORT_V4_DISCARD: + case FWPS_LAYER_INBOUND_TRANSPORT_V6: + case FWPS_LAYER_INBOUND_TRANSPORT_V6_DISCARD: + { + direction = FWP_DIRECTION_INBOUND; + + if(pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_TRANSPORT_V4_IP_PROTOCOL].value.uint8 == IPPROTO_ICMP || + pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_TRANSPORT_V4_IP_PROTOCOL].value.uint8 == IPPROTO_ICMPV6) + bytesRetreated = ipHeaderSize; + else + bytesRetreated = ipHeaderSize + transportHeaderSize; + + break; + } + case FWPS_LAYER_OUTBOUND_TRANSPORT_V4: + case FWPS_LAYER_OUTBOUND_TRANSPORT_V4_DISCARD: + case FWPS_LAYER_OUTBOUND_TRANSPORT_V6: + case FWPS_LAYER_OUTBOUND_TRANSPORT_V6_DISCARD: + { + direction = FWP_DIRECTION_OUTBOUND; + + ipHeaderAvailable = FALSE; + + break; + } + case FWPS_LAYER_STREAM_V4: + case FWPS_LAYER_STREAM_V4_DISCARD: + case FWPS_LAYER_STREAM_V6: + case FWPS_LAYER_STREAM_V6_DISCARD: + { + ipHeaderAvailable = FALSE; + + break; + } + case FWPS_LAYER_DATAGRAM_DATA_V4: + case FWPS_LAYER_DATAGRAM_DATA_V4_DISCARD: + case FWPS_LAYER_DATAGRAM_DATA_V6: + case FWPS_LAYER_DATAGRAM_DATA_V6_DISCARD: + { + direction = (FWP_DIRECTION)pClassifyValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V4_DIRECTION].value.uint32; + + if(direction == FWP_DIRECTION_OUTBOUND) + bytesRetreated = ipHeaderSize; + else + { + if(pClassifyValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V4_IP_PROTOCOL].value.uint8 == IPPROTO_ICMP || + pClassifyValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V4_IP_PROTOCOL].value.uint8 == IPPROTO_ICMPV6) + bytesRetreated = ipHeaderSize; + else + bytesRetreated = ipHeaderSize + transportHeaderSize; + } + + break; + } + case FWPS_LAYER_INBOUND_ICMP_ERROR_V4: + case FWPS_LAYER_INBOUND_ICMP_ERROR_V4_DISCARD: + case FWPS_LAYER_INBOUND_ICMP_ERROR_V6: + case FWPS_LAYER_INBOUND_ICMP_ERROR_V6_DISCARD: + { + direction = FWP_DIRECTION_INBOUND; + + bytesRetreated = ipHeaderSize + transportHeaderSize; + + break; + } + case FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4: + case FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4_DISCARD: + case FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6: + case FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6_DISCARD: + { + direction = FWP_DIRECTION_OUTBOUND; + + bytesRetreated = ipHeaderSize; + + break; + } + case FWPS_LAYER_ALE_RESOURCE_ASSIGNMENT_V4: + case FWPS_LAYER_ALE_RESOURCE_ASSIGNMENT_V4_DISCARD: + case FWPS_LAYER_ALE_RESOURCE_ASSIGNMENT_V6: + case FWPS_LAYER_ALE_RESOURCE_ASSIGNMENT_V6_DISCARD: + { + ipHeaderAvailable = FALSE; + + break; + } + case FWPS_LAYER_ALE_AUTH_LISTEN_V4: + case FWPS_LAYER_ALE_AUTH_LISTEN_V4_DISCARD: + case FWPS_LAYER_ALE_AUTH_LISTEN_V6: + case FWPS_LAYER_ALE_AUTH_LISTEN_V6_DISCARD: + { + ipHeaderAvailable = FALSE; + + break; + } + case FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4: + case FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4_DISCARD: + case FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6: + case FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6_DISCARD: + { + if(direction == FWP_DIRECTION_OUTBOUND) + { + ipHeaderAvailable = FALSE; + } + else + { + if(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_IP_PROTOCOL].value.uint8 == IPPROTO_ICMP || + pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_IP_PROTOCOL].value.uint8 == IPPROTO_ICMPV6) + bytesRetreated = ipHeaderSize; + else + bytesRetreated = ipHeaderSize + transportHeaderSize; + } + + break; + } + case FWPS_LAYER_ALE_AUTH_CONNECT_V4: + case FWPS_LAYER_ALE_AUTH_CONNECT_V4_DISCARD: + case FWPS_LAYER_ALE_AUTH_CONNECT_V6: + case FWPS_LAYER_ALE_AUTH_CONNECT_V6_DISCARD: + { + if(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_PROTOCOL].value.uint8 == IPPROTO_TCP) + ipHeaderAvailable = FALSE; + else if(direction == FWP_DIRECTION_INBOUND) + ipHeaderAvailable = FALSE; + else + ipHeaderAvailable = FALSE; + + break; + } + case FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4: + case FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4_DISCARD: + case FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6: + case FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6_DISCARD: + { + direction = (FWP_DIRECTION)pClassifyValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_DIRECTION].value.uint32; + + if(direction == FWP_DIRECTION_OUTBOUND) + bytesRetreated = ipHeaderSize; + else + { + if(pClassifyValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_PROTOCOL].value.uint8 == IPPROTO_ICMP || + pClassifyValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V6_IP_PROTOCOL].value.uint8 == IPPROTO_ICMPV6) + bytesRetreated = ipHeaderSize; + else + bytesRetreated = ipHeaderSize + transportHeaderSize; + } + + break; + } + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + case FWPS_LAYER_NAME_RESOLUTION_CACHE_V4: + case FWPS_LAYER_NAME_RESOLUTION_CACHE_V6: + { + ipHeaderAvailable = FALSE; + + break; + } + case FWPS_LAYER_ALE_RESOURCE_RELEASE_V4: + case FWPS_LAYER_ALE_RESOURCE_RELEASE_V6: + { + ipHeaderAvailable = FALSE; + + break; + } + case FWPS_LAYER_ALE_ENDPOINT_CLOSURE_V4: + case FWPS_LAYER_ALE_ENDPOINT_CLOSURE_V6: + { + ipHeaderAvailable = FALSE; + + break; + } + case FWPS_LAYER_ALE_CONNECT_REDIRECT_V4: + case FWPS_LAYER_ALE_CONNECT_REDIRECT_V6: + { + ipHeaderAvailable = FALSE; + + break; + } + case FWPS_LAYER_ALE_BIND_REDIRECT_V4: + case FWPS_LAYER_ALE_BIND_REDIRECT_V6: + { + ipHeaderAvailable = FALSE; + + break; + } + case FWPS_LAYER_STREAM_PACKET_V4: + case FWPS_LAYER_STREAM_PACKET_V6: + { + direction = (FWP_DIRECTION)pClassifyValues->incomingValue[FWPS_FIELD_STREAM_PACKET_V4_DIRECTION].value.uint32; + + if(direction == FWP_DIRECTION_OUTBOUND) + bytesRetreated = ipHeaderSize; + else + bytesRetreated = ipHeaderSize + transportHeaderSize; + + break; + } + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + case FWPS_LAYER_INBOUND_MAC_FRAME_ETHERNET: + { + UINT16 etherType = pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_MAC_FRAME_ETHERNET_ETHER_TYPE].value.uint16; + + if(etherType != 0x86DD && + etherType != 0x0800) + ipHeaderAvailable = FALSE; + + break; + } + case FWPS_LAYER_OUTBOUND_MAC_FRAME_ETHERNET: + { + UINT16 etherType = pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_MAC_FRAME_ETHERNET_ETHER_TYPE].value.uint16; + + if(etherType == 0x86DD || + etherType == 0x0800) + bytesAdvanced = ethernetHeaderSize; + else + ipHeaderAvailable = FALSE; + + break; + } + case FWPS_LAYER_INBOUND_MAC_FRAME_NATIVE: + case FWPS_LAYER_OUTBOUND_MAC_FRAME_NATIVE: + { + ipHeaderAvailable = FALSE; + + break; + } + case FWPS_LAYER_INGRESS_VSWITCH_ETHERNET: + { + UINT16 etherType = pClassifyValues->incomingValue[FWPS_FIELD_INGRESS_VSWITCH_ETHERNET_ETHER_TYPE].value.uint16; + + if(etherType == 0x86DD || + etherType == 0x0800) + bytesAdvanced = ethernetHeaderSize; + else + ipHeaderAvailable = FALSE; + + break; + } + case FWPS_LAYER_EGRESS_VSWITCH_ETHERNET: + { + UINT16 etherType = pClassifyValues->incomingValue[FWPS_FIELD_EGRESS_VSWITCH_ETHERNET_ETHER_TYPE].value.uint16; + + if(etherType == 0x86DD || + etherType == 0x0800) + bytesAdvanced = ethernetHeaderSize; + else + ipHeaderAvailable = FALSE; + + break; + } + case FWPS_LAYER_INGRESS_VSWITCH_TRANSPORT_V4: + case FWPS_LAYER_INGRESS_VSWITCH_TRANSPORT_V6: + { + /// At the IP Header + + break; + } + case FWPS_LAYER_EGRESS_VSWITCH_TRANSPORT_V4: + case FWPS_LAYER_EGRESS_VSWITCH_TRANSPORT_V6: + { + /// At the IP Header + + break; + } + +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + } + + + if(ipHeaderAvailable) + { + BYTE* pBuffer = 0; + NET_BUFFER* pNetBuffer = NET_BUFFER_LIST_FIRST_NB(pNetBufferList); + UINT32 bytesNeeded = ipHeaderSize ? ipHeaderSize : NET_BUFFER_DATA_LENGTH(pNetBuffer); + PVOID pContiguousData = 0; + +#pragma warning(push) +#pragma warning(disable: 6014) /// pBuffer is expected to be cleaned up by caller using KrnlHlprIPHeaderDestroy if *pNeedToFreeMemory is TRUE + + HLPR_NEW_ARRAY(pBuffer, + BYTE, + bytesNeeded, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pBuffer, + status); + +#pragma warning(pop) + + *pNeedToFreeMemory = TRUE; + + if(bytesAdvanced) + NdisAdvanceNetBufferDataStart(pNetBuffer, + bytesAdvanced, + 0, + 0); + else if(bytesRetreated) + { + status = NdisRetreatNetBufferDataStart(pNetBuffer, + bytesRetreated, + 0, + 0); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! KrnlHlprIPHeaderGet : NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + + HLPR_BAIL; + } + } + + pContiguousData = NdisGetDataBuffer(pNetBuffer, + bytesNeeded, + pBuffer, + 1, + 0); + + /// Return to the original offset + if(bytesRetreated) + NdisAdvanceNetBufferDataStart(pNetBuffer, + bytesRetreated, + 0, + 0); + else if(bytesAdvanced) + { + status = NdisRetreatNetBufferDataStart(pNetBuffer, + bytesAdvanced, + 0, + 0); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! KrnlHlprIPHeaderGet : NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + + HLPR_BAIL; + } + } + + if(!pContiguousData) + { + status = STATUS_UNSUCCESSFUL; + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! KrnlHlprIPHeaderGet : NdisGetDataBuffer() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + if(pBuffer != pContiguousData) + { + HLPR_DELETE_ARRAY(pBuffer, + WFPSAMPLER_SYSLIB_TAG); + + *pNeedToFreeMemory = FALSE; + } + + *ppIPHeader = pContiguousData; + + if(pDirection) + *pDirection = direction; + + if(pIPHeaderSize) + *pIPHeaderSize = ipHeaderSize; + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS && + *pNeedToFreeMemory && + pBuffer) + { + KrnlHlprIPHeaderDestroy((VOID**)&pBuffer); + + *pNeedToFreeMemory = FALSE; + } + } + else + status = STATUS_NO_MATCH; + + return status; +} + + +/** + @kernel_helper_function="KrnlHlprIPHeaderGet" + + Purpose: Retrieve a pointer to the IP Header from the NET_BUFFER_LIST.
+
+ Notes: Assumes the NBL is at the start of the IP Header.
+
+ Function is overloaded.
+
+ If needToFreeMemory is TRUE, caller should call KrnlHlprIPHeaderDestroy() when + finished with the header.
+
+ MSDN_Ref:
+*/ +_When_(return != STATUS_SUCCESS, _At_(*ppIPHeader, _Post_ _Null_)) +_When_(return == STATUS_SUCCESS, _At_(*ppIPHeader, _Post_ _Notnull_)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprIPHeaderGet(_In_ NET_BUFFER_LIST* pNetBufferList, + _Outptr_result_buffer_(ipHeaderSize) VOID** ppIPHeader, + _Inout_ BOOLEAN* pNeedToFreeMemory, + _In_ UINT32 ipHeaderSize) /* 0 */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprIPHeaderGet()\n"); + +#endif /// DBG + + NT_ASSERT(pNetBufferList); + NT_ASSERT(ppIPHeader); + NT_ASSERT(pNeedToFreeMemory); + + NTSTATUS status = STATUS_SUCCESS; + BYTE* pBuffer = 0; + NET_BUFFER* pNetBuffer = NET_BUFFER_LIST_FIRST_NB(pNetBufferList); + UINT32 bytesNeeded = ipHeaderSize ? ipHeaderSize : NET_BUFFER_DATA_LENGTH(pNetBuffer); + PVOID pContiguousData = 0; + +#pragma warning(push) +#pragma warning(disable: 6014) /// pBuffer is expected to be cleaned up by caller using KrnlHlprIPHeaderDestroy if *pNeedToFreeMemory is TRUE + + HLPR_NEW_ARRAY(pBuffer, + BYTE, + bytesNeeded, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pBuffer, + status); + +#pragma warning(pop) + + *pNeedToFreeMemory = TRUE; + + pContiguousData = NdisGetDataBuffer(pNetBuffer, + bytesNeeded, + pBuffer, + 1, + 0); + if(!pContiguousData) + { + status = STATUS_UNSUCCESSFUL; + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! KrnlHlprIPHeaderGet : NdisGetDataBuffer() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + if(pBuffer != pContiguousData) + { + HLPR_DELETE_ARRAY(pBuffer, + WFPSAMPLER_SYSLIB_TAG); + + *pNeedToFreeMemory = FALSE; + } + + *ppIPHeader = pContiguousData; + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS && + *pNeedToFreeMemory && + pBuffer) + { + KrnlHlprIPHeaderDestroy((VOID**)&pBuffer); + + *pNeedToFreeMemory = FALSE; + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprIPHeaderGet() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @kernel_helper_function="KrnlHlprIPHeaderGetDestinationAddressField" + + Purpose: Retrieve the source address from the IP header.
+
+ Notes: Assumes the NBL is at the start of the IP Header.
+
+ MSDN_Ref:
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(return != 0) +BYTE* KrnlHlprIPHeaderGetDestinationAddressField(_In_ NET_BUFFER_LIST* pNetBufferList, + _In_ ADDRESS_FAMILY addressFamily) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprIPHeaderGetDestinationAddressField()\n"); + +#endif /// DBG + + NT_ASSERT(pNetBufferList); + + NTSTATUS status = STATUS_SUCCESS; + VOID* pIPHeader = 0; + BOOLEAN needToFree = FALSE; + BYTE* pDestinationAddress = 0; + + status = KrnlHlprIPHeaderGet(pNetBufferList, + &pIPHeader, + &needToFree); + HLPR_BAIL_ON_FAILURE(status); + + if(addressFamily == AF_INET6) + { + IP_HEADER_V6* pIPv6Header = (IP_HEADER_V6*)pIPHeader; + + pDestinationAddress = pIPv6Header->pDestinationAddress; + } + else + { + IP_HEADER_V4* pIPv4Header = (IP_HEADER_V4*)pIPHeader; + + pDestinationAddress = pIPv4Header->pDestinationAddress; + + } + + if(needToFree) + { + /// Copy the contents of the allocated buffer to the NBL's discontiguous buffer + if(status == STATUS_SUCCESS) + { + NET_BUFFER* pFirstNetBuffer = NET_BUFFER_LIST_FIRST_NB(pNetBufferList); + PMDL pCurrentMDL = NET_BUFFER_CURRENT_MDL(pFirstNetBuffer); + SIZE_T currentMDLOffset = NET_BUFFER_CURRENT_MDL_OFFSET(pFirstNetBuffer); + SIZE_T headerSize = (addressFamily == AF_INET) ? IPV4_HEADER_MIN_SIZE : IPV6_HEADER_MIN_SIZE; + SIZE_T bytesCopied = 0; + + status = PrvKrnlHlprCopyBufferToMDL((BYTE*)pIPHeader, + pCurrentMDL, + currentMDLOffset, + headerSize, + &bytesCopied); + if(status == STATUS_SUCCESS && + bytesCopied != headerSize) + status = STATUS_INSUFFICIENT_RESOURCES; + } + + KrnlHlprIPHeaderDestroy(&pIPHeader); + } + + HLPR_BAIL_LABEL: + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprIPHeaderGetDestinationAddressField()\n"); + +#endif /// DBG + + return pDestinationAddress; +} + +/** + @kernel_helper_function="KrnlHlprIPHeaderGetSourceAddressField" + + Purpose: Retrieve the source address from the IP header.
+
+ Notes: Assumes the NBL is at the start of the IP Header.
+
+ MSDN_Ref:
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(return != 0) +BYTE* KrnlHlprIPHeaderGetSourceAddressField(_In_ NET_BUFFER_LIST* pNetBufferList, + _In_ ADDRESS_FAMILY addressFamily) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprIPHeaderGetSourceAddressField()\n"); + +#endif /// DBG + + NT_ASSERT(pNetBufferList); + + NTSTATUS status = STATUS_SUCCESS; + VOID* pIPHeader = 0; + BOOLEAN needToFree = FALSE; + BYTE* pSourceAddress = 0; + + status = KrnlHlprIPHeaderGet(pNetBufferList, + &pIPHeader, + &needToFree); + HLPR_BAIL_ON_FAILURE(status); + + if(addressFamily == AF_INET6) + { + IP_HEADER_V6* pIPv6Header = (IP_HEADER_V6*)pIPHeader; + + pSourceAddress = pIPv6Header->pSourceAddress; + } + else + { + IP_HEADER_V4* pIPv4Header = (IP_HEADER_V4*)pIPHeader; + + pSourceAddress = pIPv4Header->pSourceAddress; + + } + + if(needToFree) + { + /// Copy the contents of the allocated buffer to the NBL's discontiguous buffer + if(status == STATUS_SUCCESS) + { + NET_BUFFER* pFirstNetBuffer = NET_BUFFER_LIST_FIRST_NB(pNetBufferList); + PMDL pCurrentMDL = NET_BUFFER_CURRENT_MDL(pFirstNetBuffer); + SIZE_T currentMDLOffset = NET_BUFFER_CURRENT_MDL_OFFSET(pFirstNetBuffer); + SIZE_T headerSize = (addressFamily == AF_INET) ? IPV4_HEADER_MIN_SIZE : IPV6_HEADER_MIN_SIZE; + SIZE_T bytesCopied = 0; + + status = PrvKrnlHlprCopyBufferToMDL((BYTE*)pIPHeader, + pCurrentMDL, + currentMDLOffset, + headerSize, + &bytesCopied); + if(status == STATUS_SUCCESS && + bytesCopied != headerSize) + status = STATUS_INSUFFICIENT_RESOURCES; + } + + KrnlHlprIPHeaderDestroy(&pIPHeader); + } + + HLPR_BAIL_LABEL: + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprIPHeaderGetSourceAddressField()\n"); + +#endif /// DBG + + return pSourceAddress; +} + +/** + @kernel_helper_function="KrnlHlprIPHeaderGetProtocolField" + + Purpose: Retrieve the protocol from the IP header.
+
+ Notes: Assumes the NBL is at the start of the IP Header.
+
+ MSDN_Ref:
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +IPPROTO KrnlHlprIPHeaderGetProtocolField(_In_ NET_BUFFER_LIST* pNetBufferList, + _In_ ADDRESS_FAMILY addressFamily) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprIPHeaderGetProtocolField()\n"); + +#endif /// DBG + + NT_ASSERT(pNetBufferList); + + NTSTATUS status = STATUS_SUCCESS; + VOID* pIPHeader = 0; + BOOLEAN needToFree = FALSE; + IPPROTO protocol = IPPROTO_MAX; + + status = KrnlHlprIPHeaderGet(pNetBufferList, + &pIPHeader, + &needToFree); + HLPR_BAIL_ON_FAILURE(status); + + if(addressFamily == AF_INET6) + { + IP_HEADER_V6* pIPv6Header = (IP_HEADER_V6*)pIPHeader; + + protocol = (IPPROTO)pIPv6Header->nextHeader; + } + else + { + IP_HEADER_V4* pIPv4Header = (IP_HEADER_V4*)pIPHeader; + + protocol = (IPPROTO)pIPv4Header->protocol; + } + + if(needToFree) + { + /// Copy the contents of the allocated buffer to the NBL's discontiguous buffer + if(status == STATUS_SUCCESS) + { + NET_BUFFER* pFirstNetBuffer = NET_BUFFER_LIST_FIRST_NB(pNetBufferList); + PMDL pCurrentMDL = NET_BUFFER_CURRENT_MDL(pFirstNetBuffer); + SIZE_T currentMDLOffset = NET_BUFFER_CURRENT_MDL_OFFSET(pFirstNetBuffer); + SIZE_T headerSize = (addressFamily == AF_INET) ? IPV4_HEADER_MIN_SIZE : IPV6_HEADER_MIN_SIZE; + SIZE_T bytesCopied = 0; + + status = PrvKrnlHlprCopyBufferToMDL((BYTE*)pIPHeader, + pCurrentMDL, + currentMDLOffset, + headerSize, + &bytesCopied); + if(status == STATUS_SUCCESS && + bytesCopied != headerSize) + status = STATUS_INSUFFICIENT_RESOURCES; + } + + KrnlHlprIPHeaderDestroy(&pIPHeader); + } + + HLPR_BAIL_LABEL: + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprIPHeaderGetProtocolField()\n"); + +#endif /// DBG + + return protocol; +} + +/** + @kernel_helper_function="KrnlHlprIPHeaderGetVersionField" + + Purpose: Retrieve the version from the IP header.
+
+ Notes: Assumes the NBL is at the start of the IP Header.
+
+ MSDN_Ref:
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +UINT8 KrnlHlprIPHeaderGetVersionField(_In_ NET_BUFFER_LIST* pNetBufferList) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprIPHeaderGetVersionField()\n"); + +#endif /// DBG + + NT_ASSERT(pNetBufferList); + + NTSTATUS status = STATUS_SUCCESS; + BYTE* pIPHeader = 0; + BOOLEAN needToFree = FALSE; + UINT8 version = 0; + + status = KrnlHlprIPHeaderGet(pNetBufferList, + (VOID**)&pIPHeader, + &needToFree); + HLPR_BAIL_ON_FAILURE(status); + + version = pIPHeader[0] >> 4; + + if(needToFree) + { + /// Copy the contents of the allocated buffer to the NBL's discontiguous buffer + if(status == STATUS_SUCCESS) + { + NET_BUFFER* pFirstNetBuffer = NET_BUFFER_LIST_FIRST_NB(pNetBufferList); + PMDL pCurrentMDL = NET_BUFFER_CURRENT_MDL(pFirstNetBuffer); + SIZE_T currentMDLOffset = NET_BUFFER_CURRENT_MDL_OFFSET(pFirstNetBuffer); + SIZE_T headerSize = (version == IPV4) ? IPV4_HEADER_MIN_SIZE : IPV6_HEADER_MIN_SIZE; + SIZE_T bytesCopied = 0; + + status = PrvKrnlHlprCopyBufferToMDL(pIPHeader, + pCurrentMDL, + currentMDLOffset, + headerSize, + &bytesCopied); + if(status == STATUS_SUCCESS && + bytesCopied != headerSize) + status = STATUS_INSUFFICIENT_RESOURCES; + } + + KrnlHlprIPHeaderDestroy((VOID**)&pIPHeader); + } + + HLPR_BAIL_LABEL: + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprIPHeaderGetVersionField()\n"); + +#endif /// DBG + + return version; +} + +/** + @kernel_helper_function="KrnlHlprIPHeaderCalculateV4Checksum" + + Purpose: Calculate the Checksum for the IPv4 Header.
+
+ Notes: Assumes the NBL is at the start of the IPv4 Header.
+
+ MSDN_Ref:
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID KrnlHlprIPHeaderCalculateV4Checksum(_Inout_ NET_BUFFER_LIST* pNetBufferList, + _In_ UINT32 ipHeaderSize) /* IPV4_HEADER_MIN_SIZE */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprIPHeaderCalculateV4Checksum()\n"); + +#endif /// DBG + + NT_ASSERT(pNetBufferList); + + NTSTATUS status = STATUS_SUCCESS; + IP_HEADER_V4* pIPv4Header = 0; + BOOLEAN needToFree = FALSE; + + status = KrnlHlprIPHeaderGet(pNetBufferList, + (VOID**)&pIPv4Header, + &needToFree, + ipHeaderSize); + if(status == STATUS_SUCCESS && + ipHeaderSize >= IPV4_HEADER_MIN_SIZE) + { + UINT32 sum = 0; + UINT32 words = ipHeaderSize / 2; + UINT16 UNALIGNED* pStart = (UINT16*)pIPv4Header; + + pIPv4Header->checksum = 0; + + for(UINT8 i = 0; + i < words; + i++) + { + sum += pStart[i]; + } + + sum = (sum & 0x0000ffff) + (sum >> 16); + sum += (sum >> 16); + + pIPv4Header->checksum = (UINT16)~sum; + + if(needToFree) + { + /// Copy the contents of the allocated buffer to the NBL's discontiguous buffer + if(status == STATUS_SUCCESS) + { + NET_BUFFER* pFirstNetBuffer = NET_BUFFER_LIST_FIRST_NB(pNetBufferList); + PMDL pCurrentMDL = NET_BUFFER_CURRENT_MDL(pFirstNetBuffer); + SIZE_T currentMDLOffset = NET_BUFFER_CURRENT_MDL_OFFSET(pFirstNetBuffer); + SIZE_T headerSize = ipHeaderSize ? ipHeaderSize : IPV4_HEADER_MIN_SIZE; + SIZE_T bytesCopied = 0; + + status = PrvKrnlHlprCopyBufferToMDL((BYTE*)pIPv4Header, + pCurrentMDL, + currentMDLOffset, + headerSize, + &bytesCopied); + if(status == STATUS_SUCCESS && + bytesCopied != headerSize) + status = STATUS_INSUFFICIENT_RESOURCES; + } + + KrnlHlprIPHeaderDestroy((VOID**)&pIPv4Header); + } + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprIPHeaderCalculateV4Checksum()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprIPHeaderModifySourceAddress" + + Purpose: Set the Source Address field in the IP Header to the provided value.
+
+ Notes: The NetBufferList parameter is expected to be offset to the start of the IP Header.
+
+ Values should be in Network Byte Order.
+
+ Function is IP version agnostic.
+
+ MSDN_Ref:
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprIPHeaderModifySourceAddress(_In_ const FWP_VALUE* pValue, + _Inout_ NET_BUFFER_LIST* pNetBufferList, + _In_ BOOLEAN recalculateChecksum, /* TRUE */ + _In_ BOOLEAN convertByteOrder) /* FALSE */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprIPHeaderModifySourceAddress()\n"); + +#endif /// DBG + + NT_ASSERT(pValue); + NT_ASSERT(pNetBufferList); + + NTSTATUS status = STATUS_SUCCESS; + VOID* pIPHeader = 0; + BOOLEAN needToFree = FALSE; + + status = KrnlHlprIPHeaderGet(pNetBufferList, + &pIPHeader, + &needToFree); + HLPR_BAIL_ON_FAILURE(status); + + switch(pValue->type) + { + case FWP_UINT32: + { + IP_HEADER_V4* pIPv4Header = (IP_HEADER_V4*)pIPHeader; + UINT32 sourceAddress = convertByteOrder ? htonl(pValue->uint32) : pValue->uint32; + + RtlCopyMemory(pIPv4Header->pSourceAddress, + &sourceAddress, + IPV4_ADDRESS_SIZE); + + break; + } + case FWP_BYTE_ARRAY16_TYPE: + { + IP_HEADER_V6* pIPv6Header = (IP_HEADER_V6*)pIPHeader; + + RtlCopyMemory(pIPv6Header->pSourceAddress, + &(pValue->byteArray16->byteArray16), + IPV6_ADDRESS_SIZE); + + break; + } + } + + HLPR_BAIL_LABEL: + + if(needToFree) + { + /// Copy the contents of the allocated buffer to the NBL's discontiguous buffer + if(status == STATUS_SUCCESS) + { + NET_BUFFER* pFirstNetBuffer = NET_BUFFER_LIST_FIRST_NB(pNetBufferList); + PMDL pCurrentMDL = NET_BUFFER_CURRENT_MDL(pFirstNetBuffer); + SIZE_T currentMDLOffset = NET_BUFFER_CURRENT_MDL_OFFSET(pFirstNetBuffer); + SIZE_T headerSize = (pValue->type == FWP_UINT32) ? IPV4_HEADER_MIN_SIZE : IPV6_HEADER_MIN_SIZE; + SIZE_T bytesCopied = 0; + + status = PrvKrnlHlprCopyBufferToMDL((BYTE*)pIPHeader, + pCurrentMDL, + currentMDLOffset, + headerSize, + &bytesCopied); + if(status == STATUS_SUCCESS && + bytesCopied != headerSize) + status = STATUS_INSUFFICIENT_RESOURCES; + } + + KrnlHlprIPHeaderDestroy(&pIPHeader); + } + + if(recalculateChecksum && + pValue->type == FWP_UINT32) + KrnlHlprIPHeaderCalculateV4Checksum(pNetBufferList); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprIPHeaderModifySourceAddress() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @kernel_helper_function="KrnlHlprIPHeaderModifyDestinationAddress" + + Purpose: Set the Destination Address field in the IP Header to the provided value.
+
+ Notes: The NetBufferList parameter is expected to be offset to the start of the IP Header.
+
+ Values should be in Network Byte Order.
+
+ Function is IP version agnostic.
+
+ MSDN_Ref:
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprIPHeaderModifyDestinationAddress(_In_ const FWP_VALUE* pValue, + _Inout_ NET_BUFFER_LIST* pNetBufferList, + _In_ const BOOLEAN recalculateChecksum, /* TRUE */ + _In_ BOOLEAN convertByteOrder) /* FALSE */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprIPHeaderModifyDestinationAddress()\n"); + +#endif /// DBG + + NT_ASSERT(pValue); + NT_ASSERT(pNetBufferList); + + NTSTATUS status = STATUS_SUCCESS; + VOID* pIPHeader = 0; + BOOLEAN needToFree = FALSE; + + status = KrnlHlprIPHeaderGet(pNetBufferList, + &pIPHeader, + &needToFree); + HLPR_BAIL_ON_FAILURE(status); + + switch(pValue->type) + { + case FWP_UINT32: + { + IP_HEADER_V4* pIPv4Header = (IP_HEADER_V4*)pIPHeader; + UINT32 destinationAddress = convertByteOrder ? htonl(pValue->uint32) : pValue->uint32; + + RtlCopyMemory(pIPv4Header->pDestinationAddress, + &destinationAddress, + IPV4_ADDRESS_SIZE); + + break; + } + case FWP_BYTE_ARRAY16_TYPE: + { + IP_HEADER_V6* pIPv6Header = (IP_HEADER_V6*)pIPHeader; + + RtlCopyMemory(pIPv6Header->pDestinationAddress, + &(pValue->byteArray16->byteArray16), + IPV6_ADDRESS_SIZE); + + break; + } + } + + HLPR_BAIL_LABEL: + + if(needToFree) + { + /// Copy the contents of the allocated buffer to the NBL's discontiguous buffer + if(status == STATUS_SUCCESS) + { + NET_BUFFER* pFirstNetBuffer = NET_BUFFER_LIST_FIRST_NB(pNetBufferList); + PMDL pCurrentMDL = NET_BUFFER_CURRENT_MDL(pFirstNetBuffer); + SIZE_T currentMDLOffset = NET_BUFFER_CURRENT_MDL_OFFSET(pFirstNetBuffer); + SIZE_T headerSize = (pValue->type == FWP_UINT32) ? IPV4_HEADER_MIN_SIZE : IPV6_HEADER_MIN_SIZE; + SIZE_T bytesCopied = 0; + + status = PrvKrnlHlprCopyBufferToMDL((BYTE*)pIPHeader, + pCurrentMDL, + currentMDLOffset, + headerSize, + &bytesCopied); + if(status == STATUS_SUCCESS && + bytesCopied != headerSize) + status = STATUS_INSUFFICIENT_RESOURCES; + } + + KrnlHlprIPHeaderDestroy(&pIPHeader); + } + + if(recalculateChecksum && + pValue->type == FWP_UINT32) + KrnlHlprIPHeaderCalculateV4Checksum(pNetBufferList); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprIPHeaderModifyDestinationAddress() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @kernel_helper_function="KrnlHlprIPHeaderModifyLoopbackToLocal" + + Purpose: Modifies the source address and destination address from software loopback to an + actual local IP address (i.e. 127.0.0.1 to 157.59.10.233).
+
+ Notes: The NetBufferList parameter is expected to be offset to the start of the IP Header.
+
+ The source address is modified to pass TCP/IP's source IP address validation, and + the destination address is modified to pass TCP/IP's zone crossing restrictions.
+
+ For some protocols, the need to capture and modify a response packet's addresses + back to the loopback addresses will exist (i.e. ICMP Echo Requests)
+
+ MSDN_Ref:
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprIPHeaderModifyLoopbackToLocal(_In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _In_ const FWP_VALUE* pLoopbackAddress, + _In_ const UINT32 ipHeaderSize, + _Inout_ NET_BUFFER_LIST* pNetBufferList, + _In_reads_opt_(controlDataSize) const WSACMSGHDR* pControlData, /* 0 */ + _In_ UINT32 controlDataSize) /* 0 */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprIPHeaderModifyLoopbackToLocal()\n"); + +#endif /// DBG + + NT_ASSERT(pMetadata); + NT_ASSERT(pLoopbackAddress); + NT_ASSERT(pNetBufferList); + + NTSTATUS status = STATUS_SUCCESS; + ADDRESS_FAMILY addressFamily = pLoopbackAddress->type == FWP_BYTE_ARRAY16_TYPE ? AF_INET6 : AF_INET; + VOID* pIPHeader = 0; + BOOLEAN needToFreeMemory = FALSE; + UINT8* pLocalAddress = 0; + IPPROTO nextProtocol = IPPROTO_MAX; + + status = KrnlHlprIPHeaderGet(pNetBufferList, + &pIPHeader, + &needToFreeMemory, + ipHeaderSize); + HLPR_BAIL_ON_FAILURE(status); + + if(addressFamily == AF_INET) + { + IP_HEADER_V4* pIPv4Header = (IP_HEADER_V4*)pIPHeader; + + nextProtocol = (IPPROTO)pIPv4Header->protocol; + + /// Only modify if the addresses are different + if(RtlCompareMemory(pIPv4Header->pSourceAddress, + pIPv4Header->pDestinationAddress, + IPV4_ADDRESS_SIZE) != IPV4_ADDRESS_SIZE) + { + HLPR_NEW_ARRAY(pLocalAddress, + UINT8, + IPV4_ADDRESS_SIZE, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pLocalAddress, + status); + + if(RtlCompareMemory(pIPv4Header->pSourceAddress, + IPV4_LOOPBACK_ADDRESS, + IPV4_ADDRESS_SIZE) == IPV4_ADDRESS_SIZE) + RtlCopyMemory(pLocalAddress, + pIPv4Header->pDestinationAddress, + IPV4_ADDRESS_SIZE); + else + RtlCopyMemory(pLocalAddress, + pIPv4Header->pSourceAddress, + IPV4_ADDRESS_SIZE); + } + } + else + { + IP_HEADER_V6* pIPv6Header = (IP_HEADER_V6*)pIPHeader; + + nextProtocol = (IPPROTO)pIPv6Header->nextHeader; + + /// Only modify if the addresses are different + if(RtlCompareMemory(pIPv6Header->pSourceAddress, + pIPv6Header->pDestinationAddress, + IPV6_ADDRESS_SIZE) != IPV6_ADDRESS_SIZE) + { + HLPR_NEW_ARRAY(pLocalAddress, + UINT8, + IPV6_ADDRESS_SIZE, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pLocalAddress, + status); + + if(RtlCompareMemory(pIPv6Header->pSourceAddress, + IPV6_LOOPBACK_ADDRESS, + IPV6_ADDRESS_SIZE) == IPV6_ADDRESS_SIZE) + RtlCopyMemory(pLocalAddress, + pIPv6Header->pDestinationAddress, + IPV6_ADDRESS_SIZE); + else + RtlCopyMemory(pLocalAddress, + pIPv6Header->pSourceAddress, + IPV6_ADDRESS_SIZE); + } + } + + if(pLocalAddress) + { + UINT64 endpointHandle = 0; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_ENDPOINT_HANDLE)) + endpointHandle = pMetadata->transportEndpointHandle; + + /// Rebuild the IP Header (recalculating the IP and transport checksums) + status = FwpsConstructIpHeaderForTransportPacket(pNetBufferList, + ipHeaderSize, + addressFamily, + pLocalAddress, + pLocalAddress, + nextProtocol, + endpointHandle, + pControlData, + controlDataSize, + 0, + 0, + 0, + 0); + if(status != STATUS_SUCCESS) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! KrnlHlprIPHeaderModifyLoopbackToLocal : FwpsConstructIpHeaderForTransportPacket() [status: %#x]\n", + status); + } + + HLPR_BAIL_LABEL: + + HLPR_DELETE_ARRAY(pLocalAddress, + WFPSAMPLER_SYSLIB_TAG); + + if(needToFreeMemory) + KrnlHlprIPHeaderDestroy(&pIPHeader); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprIPHeaderModifyLoopbackToLocal() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +#endif /// IP_HEADER____ + +#ifndef TRANSPORT_HEADERS____ +#define TRANSPORT_HEADERS____ + +/** + @kernel_helper_function="KrnlHlprTransportHeaderDestroy" + + Purpose: Frees the allocated memory indicated in KrnlTransportHeaderGet().
+
+ Notes: For use with generic and specific transport header functions.
+
+ MSDN_Ref:
+*/ +_At_(*ppTransportHeader, _Pre_ _Notnull_) +_At_(*ppTransportHeader, _Post_ _Null_) +_Success_(*ppTransportHeader == 0) +inline VOID KrnlHlprTransportHeaderDestroy(_Inout_ VOID** ppTransportHeader) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprTransportHeaderDestroy()\n"); + +#endif /// DBG + + NT_ASSERT(ppTransportHeader); + + HLPR_DELETE_ARRAY(*ppTransportHeader, + WFPSAMPLER_SYSLIB_TAG); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprTransportHeaderDestroy()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprTransportHeaderGet" + + Purpose: Retrieve a pointer to the Transport Header from the NET_BUFFER_LIST.
+
+ Notes: Function is overloaded. .
+
+ If needToFreeMemory is TRUE, caller should call KrnlHlprTransportHeaderDestroy() + when finished with the header.
+
+ MSDN_Ref:
+*/ +_When_(return != STATUS_SUCCESS, _At_(*ppTransportHeader, _Post_ _Null_)) +_When_(return == STATUS_SUCCESS, _At_(*ppTransportHeader, _Post_ _Notnull_)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprTransportHeaderGet(_In_ NET_BUFFER_LIST* pNetBufferList, + _In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Outptr_result_buffer_(*pTransportHeaderSize) VOID** ppTransportHeader, + _Inout_ BOOLEAN* pNeedToFreeMemory, + _Inout_opt_ IPPROTO* pProtocol, /* 0 */ + _Inout_opt_ FWP_DIRECTION* pDirection, /* 0 */ + _Inout_opt_ UINT32* pTransportHeaderSize) /* 0 */ +{ + NT_ASSERT(pNetBufferList); + NT_ASSERT(pClassifyValues); + NT_ASSERT(pMetadata); + NT_ASSERT(ppTransportHeader); + NT_ASSERT(pNeedToFreeMemory); + + NTSTATUS status = STATUS_SUCCESS; + UINT32 bytesRetreated = 0; + UINT32 bytesAdvanced = 0; + UINT32 ipHeaderSize = 0; + UINT32 transportHeaderSize = 0; + FWP_DIRECTION direction = FWP_DIRECTION_MAX; + IPPROTO protocol = IPPROTO_MAX; + BOOLEAN transportHeaderAvailable = TRUE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_IP_HEADER_SIZE)) + ipHeaderSize = pMetadata->ipHeaderSize; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + transportHeaderSize = pMetadata->transportHeaderSize; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_PACKET_DIRECTION)) + direction = pMetadata->packetDirection; + + switch(pClassifyValues->layerId) + { + case FWPS_LAYER_INBOUND_IPPACKET_V4: + case FWPS_LAYER_INBOUND_IPPACKET_V6: + { + /// At the Transport Header + + break; + } + case FWPS_LAYER_INBOUND_IPPACKET_V4_DISCARD: + case FWPS_LAYER_INBOUND_IPPACKET_V6_DISCARD: + { + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_DISCARD_REASON)) + { + if(pMetadata->discardMetadata.discardModule == FWPS_DISCARD_MODULE_GENERAL && + pMetadata->discardMetadata.discardReason == FWPS_DISCARD_FIREWALL_POLICY) + { + /// At the Transport Header + } + } + + break; + } + case FWPS_LAYER_OUTBOUND_IPPACKET_V4: + case FWPS_LAYER_OUTBOUND_IPPACKET_V4_DISCARD: + case FWPS_LAYER_OUTBOUND_IPPACKET_V6: + case FWPS_LAYER_OUTBOUND_IPPACKET_V6_DISCARD: + { + bytesAdvanced = ipHeaderSize; + + break; + } + case FWPS_LAYER_IPFORWARD_V4: + case FWPS_LAYER_IPFORWARD_V4_DISCARD: + case FWPS_LAYER_IPFORWARD_V6: + case FWPS_LAYER_IPFORWARD_V6_DISCARD: + { + bytesAdvanced = ipHeaderSize; + + break; + } + case FWPS_LAYER_INBOUND_TRANSPORT_V4: + case FWPS_LAYER_INBOUND_TRANSPORT_V4_DISCARD: + case FWPS_LAYER_INBOUND_TRANSPORT_V6: + case FWPS_LAYER_INBOUND_TRANSPORT_V6_DISCARD: + { + protocol = (IPPROTO)(pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_TRANSPORT_V4_IP_PROTOCOL].value.uint8); + + if(protocol == IPPROTO_ICMP || + protocol == IPPROTO_ICMPV6) + { + /// At the Transport Header + } + else + bytesRetreated = transportHeaderSize; + + break; + } + case FWPS_LAYER_OUTBOUND_TRANSPORT_V4: + case FWPS_LAYER_OUTBOUND_TRANSPORT_V4_DISCARD: + case FWPS_LAYER_OUTBOUND_TRANSPORT_V6: + case FWPS_LAYER_OUTBOUND_TRANSPORT_V6_DISCARD: + { + protocol = (IPPROTO)(pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_TRANSPORT_V4_IP_PROTOCOL].value.uint8); + + /// At the Transport Header + + break; + } + case FWPS_LAYER_STREAM_V4: + case FWPS_LAYER_STREAM_V4_DISCARD: + case FWPS_LAYER_STREAM_V6: + case FWPS_LAYER_STREAM_V6_DISCARD: + { + transportHeaderAvailable = FALSE; + + break; + } + case FWPS_LAYER_DATAGRAM_DATA_V4: + case FWPS_LAYER_DATAGRAM_DATA_V4_DISCARD: + case FWPS_LAYER_DATAGRAM_DATA_V6: + case FWPS_LAYER_DATAGRAM_DATA_V6_DISCARD: + { + protocol = (IPPROTO)(pClassifyValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V4_IP_PROTOCOL].value.uint8); + + direction = (FWP_DIRECTION)pClassifyValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V4_DIRECTION].value.uint32; + + if(direction == FWP_DIRECTION_OUTBOUND) + { + /// At the Transport Header + } + else + { + if(protocol == IPPROTO_ICMP || + protocol == IPPROTO_ICMPV6) + { + /// At the Transport Header + } + else + bytesRetreated = transportHeaderSize; + } + + break; + } + case FWPS_LAYER_INBOUND_ICMP_ERROR_V4: + case FWPS_LAYER_INBOUND_ICMP_ERROR_V4_DISCARD: + case FWPS_LAYER_INBOUND_ICMP_ERROR_V6: + case FWPS_LAYER_INBOUND_ICMP_ERROR_V6_DISCARD: + { + direction = FWP_DIRECTION_INBOUND; + + bytesRetreated = transportHeaderSize; + + break; + } + case FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4: + case FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4_DISCARD: + case FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6: + case FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6_DISCARD: + { + direction = FWP_DIRECTION_OUTBOUND; + + /// At the Transport Header + + break; + } + case FWPS_LAYER_ALE_RESOURCE_ASSIGNMENT_V4: + case FWPS_LAYER_ALE_RESOURCE_ASSIGNMENT_V4_DISCARD: + case FWPS_LAYER_ALE_RESOURCE_ASSIGNMENT_V6: + case FWPS_LAYER_ALE_RESOURCE_ASSIGNMENT_V6_DISCARD: + { + transportHeaderAvailable = FALSE; + + break; + } + case FWPS_LAYER_ALE_AUTH_LISTEN_V4: + case FWPS_LAYER_ALE_AUTH_LISTEN_V4_DISCARD: + case FWPS_LAYER_ALE_AUTH_LISTEN_V6: + case FWPS_LAYER_ALE_AUTH_LISTEN_V6_DISCARD: + { + transportHeaderAvailable = FALSE; + + break; + } + case FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4: + case FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4_DISCARD: + case FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6: + case FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6_DISCARD: + { + protocol = (IPPROTO)(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_RECV_ACCEPT_V4_IP_PROTOCOL].value.uint8); + + if(direction == FWP_DIRECTION_OUTBOUND) + { + /// At the Transport Header + } + else + { + if(protocol == IPPROTO_ICMP || + protocol == IPPROTO_ICMPV6) + { + /// At the Transport Header + } + else + bytesRetreated = transportHeaderSize; + } + + break; + } + case FWPS_LAYER_ALE_AUTH_CONNECT_V4: + case FWPS_LAYER_ALE_AUTH_CONNECT_V4_DISCARD: + case FWPS_LAYER_ALE_AUTH_CONNECT_V6: + case FWPS_LAYER_ALE_AUTH_CONNECT_V6_DISCARD: + { + protocol = (IPPROTO)(pClassifyValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_PROTOCOL].value.uint8); + + if(protocol == IPPROTO_TCP) + transportHeaderAvailable = FALSE; + if(direction == FWP_DIRECTION_INBOUND) + { + /// At the Transport Header + } + else + { + /// At the Transport Header + } + + break; + } + case FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4: + case FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4_DISCARD: + case FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6: + case FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6_DISCARD: + { + direction = (FWP_DIRECTION)pClassifyValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_DIRECTION].value.uint32; + + protocol = (IPPROTO)(pClassifyValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_PROTOCOL].value.uint8); + + if(direction == FWP_DIRECTION_OUTBOUND) + { + /// At the Transport Header + } + else + { + if(protocol == IPPROTO_ICMP || + protocol == IPPROTO_ICMPV6) + { + /// At the Transport Header + } + else + bytesRetreated = transportHeaderSize; + } + + break; + } + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + case FWPS_LAYER_NAME_RESOLUTION_CACHE_V4: + case FWPS_LAYER_NAME_RESOLUTION_CACHE_V6: + { + transportHeaderAvailable = FALSE; + + break; + } + case FWPS_LAYER_ALE_RESOURCE_RELEASE_V4: + case FWPS_LAYER_ALE_RESOURCE_RELEASE_V6: + { + transportHeaderAvailable = FALSE; + + break; + } + case FWPS_LAYER_ALE_ENDPOINT_CLOSURE_V4: + case FWPS_LAYER_ALE_ENDPOINT_CLOSURE_V6: + { + transportHeaderAvailable = FALSE; + + break; + } + case FWPS_LAYER_ALE_CONNECT_REDIRECT_V4: + case FWPS_LAYER_ALE_CONNECT_REDIRECT_V6: + { + transportHeaderAvailable = FALSE; + + break; + } + case FWPS_LAYER_ALE_BIND_REDIRECT_V4: + case FWPS_LAYER_ALE_BIND_REDIRECT_V6: + { + transportHeaderAvailable = FALSE; + + break; + } + case FWPS_LAYER_STREAM_PACKET_V4: + case FWPS_LAYER_STREAM_PACKET_V6: + { + direction = (FWP_DIRECTION)pClassifyValues->incomingValue[FWPS_FIELD_STREAM_PACKET_V4_DIRECTION].value.uint32; + + protocol = IPPROTO_TCP; + + if(direction == FWP_DIRECTION_OUTBOUND) + { + /// At the Transport Header + } + else + bytesRetreated = transportHeaderSize; + + break; + } + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + case FWPS_LAYER_INBOUND_MAC_FRAME_ETHERNET: + case FWPS_LAYER_OUTBOUND_MAC_FRAME_ETHERNET: + case FWPS_LAYER_INBOUND_MAC_FRAME_NATIVE: + case FWPS_LAYER_OUTBOUND_MAC_FRAME_NATIVE: + case FWPS_LAYER_INGRESS_VSWITCH_ETHERNET: + case FWPS_LAYER_EGRESS_VSWITCH_ETHERNET: + { + transportHeaderAvailable = FALSE; + + break; + } + case FWPS_LAYER_INGRESS_VSWITCH_TRANSPORT_V4: + case FWPS_LAYER_INGRESS_VSWITCH_TRANSPORT_V6: + { + bytesAdvanced = ipHeaderSize; + + break; + } + case FWPS_LAYER_EGRESS_VSWITCH_TRANSPORT_V4: + case FWPS_LAYER_EGRESS_VSWITCH_TRANSPORT_V6: + { + bytesAdvanced = ipHeaderSize; + + break; + } + +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + } + + if(transportHeaderAvailable) + { + BYTE* pBuffer = 0; + NET_BUFFER* pNetBuffer = NET_BUFFER_LIST_FIRST_NB(pNetBufferList); + UINT32 bytesNeeded = transportHeaderSize ? transportHeaderSize : NET_BUFFER_DATA_LENGTH(pNetBuffer); + PVOID pContiguousData = 0; + +#pragma warning(push) +#pragma warning(disable: 6014) /// pBuffer is expected to be cleaned up by caller using KrnlHlprTransportHeaderDestroy if *pNeedToFreeMemory is TRUE + + HLPR_NEW_ARRAY(pBuffer, + BYTE, + bytesNeeded, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pBuffer, + status); + +#pragma warning(pop) + + *pNeedToFreeMemory = TRUE; + + if(bytesAdvanced) + NdisAdvanceNetBufferDataStart(pNetBuffer, + bytesAdvanced, + 0, + 0); + else if(bytesRetreated) + { + status = NdisRetreatNetBufferDataStart(pNetBuffer, + bytesRetreated, + 0, + 0); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! KrnlHlpTransportHeaderGet : NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + + HLPR_BAIL; + } + } + + pContiguousData = NdisGetDataBuffer(pNetBuffer, + bytesNeeded, + pBuffer, + 1, + 0); + + /// Return to the original offset + if(bytesRetreated) + NdisAdvanceNetBufferDataStart(pNetBuffer, + bytesRetreated, + 0, + 0); + else if(bytesAdvanced) + { + status = NdisRetreatNetBufferDataStart(pNetBuffer, + bytesAdvanced, + 0, + 0); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! KrnlHlprTransportHeaderGet : NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + + HLPR_BAIL; + } + } + + if(!pContiguousData) + { + status = STATUS_UNSUCCESSFUL; + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! KrnlHlprTransportHeaderGet : NdisGetDataBuffer() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + if(pBuffer != pContiguousData) + { + HLPR_DELETE_ARRAY(pBuffer, + WFPSAMPLER_SYSLIB_TAG); + + *pNeedToFreeMemory = FALSE; + } + + *ppTransportHeader = pContiguousData; + + if(pProtocol) + *pProtocol = protocol; + + if(pDirection) + *pDirection = direction; + + if(pTransportHeaderSize) + *pTransportHeaderSize = transportHeaderSize; + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS && + *pNeedToFreeMemory && + pBuffer) + { + KrnlHlprTransportHeaderDestroy((VOID**)&pBuffer); + + *pNeedToFreeMemory = FALSE; + } + } + else + status = STATUS_NO_MATCH; + + return status; +} + + +/** + @kernel_helper_function="KrnlHlprTransportHeaderGet" + + Purpose: Retrieve a pointer to the Transport Header from the NET_BUFFER_LIST.
+
+ Notes: Assumes the NBL is at the start of the Transport Header.
+
+ Function is overloaded.
+
+ If needToFreeMemory is TRUE, caller should call KrnlHlprTransportHeaderDestroy() + when finished with the header.
+
+ MSDN_Ref:
+*/ +_When_(return != STATUS_SUCCESS, _At_(*ppTransportHeader, _Post_ _Null_)) +_When_(return == STATUS_SUCCESS, _At_(*ppTransportHeader, _Post_ _Notnull_)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprTransportHeaderGet(_In_ NET_BUFFER_LIST* pNetBufferList, + _Outptr_result_buffer_(transportHeaderSize) VOID** ppTransportHeader, + _Inout_ BOOLEAN* pNeedToFreeMemory, + _In_ UINT32 transportHeaderSize) /* 0 */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprTransportHeaderGet()\n"); + +#endif /// DBG + + NT_ASSERT(pNetBufferList); + NT_ASSERT(ppTransportHeader); + NT_ASSERT(pNeedToFreeMemory); + + NTSTATUS status = STATUS_SUCCESS; + BYTE* pBuffer = 0; + NET_BUFFER* pNetBuffer = NET_BUFFER_LIST_FIRST_NB(pNetBufferList); + const UINT32 BUFFER_SIZE = transportHeaderSize ? transportHeaderSize : NET_BUFFER_DATA_LENGTH(pNetBuffer); + UINT32 bytesNeeded = transportHeaderSize ? transportHeaderSize : NET_BUFFER_DATA_LENGTH(pNetBuffer); + PVOID pContiguousData = 0; + +#pragma warning(push) +#pragma warning(disable: 6014) /// pBuffer is expected to be cleaned up by caller using KrnlHlprTransportHeaderDestroy if *pNeedToFreeMemory is TRUE + + HLPR_NEW_ARRAY(pBuffer, + BYTE, + BUFFER_SIZE, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pBuffer, + status); + +#pragma warning(pop) + + *pNeedToFreeMemory = TRUE; + + pContiguousData = NdisGetDataBuffer(pNetBuffer, + bytesNeeded, + pBuffer, + 1, + 0); + if(!pContiguousData) + { + status = STATUS_UNSUCCESSFUL; + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! KrnlHlprTransportHeaderGet : NdisGetDataBuffer() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + if(pBuffer != pContiguousData) + { + HLPR_DELETE_ARRAY(pBuffer, + WFPSAMPLER_SYSLIB_TAG); + + *pNeedToFreeMemory = FALSE; + } + + *ppTransportHeader = pContiguousData; + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS && + *pNeedToFreeMemory && + pBuffer) + { + KrnlHlprTransportHeaderDestroy((VOID**)&pBuffer); + + *pNeedToFreeMemory = FALSE; + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprTransportHeaderGet() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @kernel_helper_function="KrnlHlprTransportHeaderGetSourcePortField" + + Purpose: Retrieve the source port from the Transport header.
+
+ Notes: Assumes the NBL is at the start of the Transport Header.
+
+ MSDN_Ref:
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +UINT16 KrnlHlprTransportHeaderGetSourcePortField(_In_ NET_BUFFER_LIST* pNetBufferList, + _In_ IPPROTO protocol) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprTransportHeaderGetSourcePortField()\n"); + +#endif /// DBG + + NT_ASSERT(pNetBufferList); + + NTSTATUS status = STATUS_SUCCESS; + BYTE* pTransportHeader = 0; + BOOLEAN needToFree = FALSE; + UINT16 port = 0; + + status = KrnlHlprTransportHeaderGet(pNetBufferList, + (VOID**)&pTransportHeader, + &needToFree); + HLPR_BAIL_ON_FAILURE(status); + + switch(protocol) + { + case TCP: + { + TCP_HEADER* pTCPHeader = (TCP_HEADER*)pTransportHeader; + + port = pTCPHeader->sourcePort; + + break; + } + case UDP: + { + UDP_HEADER* pUDPHeader = (UDP_HEADER*)pTransportHeader; + + port = pUDPHeader->sourcePort; + + break; + } + } + + if(needToFree) + { + /// Copy the contents of the allocated buffer to the NBL's discontiguous buffer + if(status == STATUS_SUCCESS) + { + NET_BUFFER* pFirstNetBuffer = NET_BUFFER_LIST_FIRST_NB(pNetBufferList); + PMDL pCurrentMDL = NET_BUFFER_CURRENT_MDL(pFirstNetBuffer); + SIZE_T currentMDLOffset = NET_BUFFER_CURRENT_MDL_OFFSET(pFirstNetBuffer); + SIZE_T headerSize = 0; + SIZE_T bytesCopied = 0; + + switch(protocol) + { + case TCP: + { + headerSize = TCP_HEADER_MIN_SIZE; + + break; + } + case UDP: + { + headerSize = UDP_HEADER_MIN_SIZE; + + break; + } + } + + status = PrvKrnlHlprCopyBufferToMDL(pTransportHeader, + pCurrentMDL, + currentMDLOffset, + headerSize, + &bytesCopied); + if(status == STATUS_SUCCESS && + bytesCopied != headerSize) + status = STATUS_INSUFFICIENT_RESOURCES; + } + + KrnlHlprTransportHeaderDestroy((VOID**)&pTransportHeader); + } + + HLPR_BAIL_LABEL: + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprTransportHeaderGetSourcePortField()\n"); + +#endif /// DBG + + return port; +} + +/** + @kernel_helper_function="KrnlHlprTransportHeaderGetDestinationPortField" + + Purpose: Retrieve the destination port from the Transport header.
+
+ Notes: Assumes the NBL is at the start of the Transport Header.
+
+ MSDN_Ref:
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +UINT16 KrnlHlprTransportHeaderGetDestinationPortField(_In_ NET_BUFFER_LIST* pNetBufferList, + _In_ IPPROTO protocol) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprTransportHeaderGetDestinationPortField()\n"); + +#endif /// DBG + + NT_ASSERT(pNetBufferList); + + NTSTATUS status = STATUS_SUCCESS; + BYTE* pTransportHeader = 0; + BOOLEAN needToFree = FALSE; + UINT16 port = 0; + + status = KrnlHlprTransportHeaderGet(pNetBufferList, + (VOID**)&pTransportHeader, + &needToFree); + HLPR_BAIL_ON_FAILURE(status); + + switch(protocol) + { + case TCP: + { + TCP_HEADER* pTCPHeader = (TCP_HEADER*)pTransportHeader; + + port = pTCPHeader->destinationPort; + + break; + } + case UDP: + { + UDP_HEADER* pUDPHeader = (UDP_HEADER*)pTransportHeader; + + port = pUDPHeader->destinationPort; + + break; + } + } + + if(needToFree) + { + /// Copy the contents of the allocated buffer to the NBL's discontiguous buffer + if(status == STATUS_SUCCESS) + { + NET_BUFFER* pFirstNetBuffer = NET_BUFFER_LIST_FIRST_NB(pNetBufferList); + PMDL pCurrentMDL = NET_BUFFER_CURRENT_MDL(pFirstNetBuffer); + SIZE_T currentMDLOffset = NET_BUFFER_CURRENT_MDL_OFFSET(pFirstNetBuffer); + SIZE_T headerSize = 0; + SIZE_T bytesCopied = 0; + + switch(protocol) + { + case TCP: + { + headerSize = TCP_HEADER_MIN_SIZE; + + break; + } + case UDP: + { + headerSize = UDP_HEADER_MIN_SIZE; + + break; + } + } + + status = PrvKrnlHlprCopyBufferToMDL(pTransportHeader, + pCurrentMDL, + currentMDLOffset, + headerSize, + &bytesCopied); + if(status == STATUS_SUCCESS && + bytesCopied != headerSize) + status = STATUS_INSUFFICIENT_RESOURCES; + } + + KrnlHlprTransportHeaderDestroy((VOID**)&pTransportHeader); + } + + HLPR_BAIL_LABEL: + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprTransportHeaderGetDestinationPortField()\n"); + +#endif /// DBG + + return port; +} + +#endif /// TRANSPORT_HEADERS____ + +#ifndef ICMPV4_HEADER____ +#define ICMPV4_HEADER____ + +/** + @kernel_helper_function="KrnlHlprICMPv4HeaderGet" + + Purpose: Retrieve a pointer to the ICMPv4 Header from the NET_BUFFER_LIST.
+
+ Notes: Assumes the NBL is at the start of the ICMPv4 Header.
+
+ MSDN_Ref:
+*/ +_When_(return != STATUS_SUCCESS, _At_(*ppICMPv4Header, _Post_ _Null_)) +_When_(return == STATUS_SUCCESS, _At_(*ppICMPv4Header, _Post_ _Notnull_)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprICMPv4HeaderGet(_In_ NET_BUFFER_LIST* pNetBufferList, + _Outptr_result_buffer_(icmpHeaderSize) VOID** ppICMPv4Header, + _Inout_ BOOLEAN* pNeedToFreeMemory, + _In_ UINT32 icmpHeaderSize) /* 0 */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprICMPv4HeaderGet()\n"); + +#endif /// DBG + + NTSTATUS status = STATUS_SUCCESS; + BYTE* pBuffer = 0; + NET_BUFFER* pNetBuffer = NET_BUFFER_LIST_FIRST_NB(pNetBufferList); + const UINT32 BUFFER_SIZE = icmpHeaderSize ? icmpHeaderSize : NET_BUFFER_DATA_LENGTH(pNetBuffer); + UINT32 bytesNeeded = icmpHeaderSize ? icmpHeaderSize : NET_BUFFER_DATA_LENGTH(pNetBuffer); + PVOID pContiguousData = 0; + +#pragma warning(push) +#pragma warning(disable: 6014) /// pBuffer is expected to be cleaned up by caller using KrnlHlprTransportHeaderDestroy if *pNeedToFreeMemory is TRUE + + HLPR_NEW_ARRAY(pBuffer, + BYTE, + BUFFER_SIZE, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pBuffer, + status); + +#pragma warning(pop) + + *pNeedToFreeMemory = TRUE; + + pContiguousData = NdisGetDataBuffer(pNetBuffer, + bytesNeeded, + pBuffer, + 1, + 0); + if(!pContiguousData) + { + status = STATUS_UNSUCCESSFUL; + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! KrnlHlprICMPv4HeaderGet : NdisGetDataBuffer() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + if(pBuffer != pContiguousData) + { + HLPR_DELETE_ARRAY(pBuffer, + WFPSAMPLER_SYSLIB_TAG); + + *pNeedToFreeMemory = FALSE; + } + + *ppICMPv4Header = pContiguousData; + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS && + *pNeedToFreeMemory && + pBuffer) + { + KrnlHlprTransportHeaderDestroy((VOID**)&pBuffer); + + *pNeedToFreeMemory = FALSE; + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprICMPv4HeaderGet() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @kernel_helper_function="KrnlHlprICMPv4HeaderModifyType" + + Purpose: Set the ICMP Type field in the ICMPv4 Header to the provided value.
+
+ Notes: The NetBufferList parameter is expected to be offset to the start of the ICMPv4 + Header.
+
+ MSDN_Ref:
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprICMPv4HeaderModifyType(_In_ const FWP_VALUE* pValue, + _Inout_ NET_BUFFER_LIST* pNetBufferList, + _In_ UINT32 icmpHeaderSize) /* 0 */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprICMPv4HeaderModifyType()\n"); + +#endif /// DBG + + NT_ASSERT(pValue); + NT_ASSERT(pNetBufferList); + + NTSTATUS status = STATUS_SUCCESS; + ICMP_HEADER_V4* pICMPHeader = 0; + BOOLEAN needToFree = FALSE; + + status = KrnlHlprICMPv4HeaderGet(pNetBufferList, + (VOID**)&pICMPHeader, + &needToFree, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE(status); + + pICMPHeader->type = pValue->uint8; + + HLPR_BAIL_LABEL: + + if(needToFree) + { + /// Copy the contents of the allocated buffer to the NBL's discontiguous buffer + if(status == STATUS_SUCCESS) + { + NET_BUFFER* pFirstNetBuffer = NET_BUFFER_LIST_FIRST_NB(pNetBufferList); + PMDL pCurrentMDL = NET_BUFFER_CURRENT_MDL(pFirstNetBuffer); + SIZE_T currentMDLOffset = NET_BUFFER_CURRENT_MDL_OFFSET(pFirstNetBuffer); + SIZE_T headerSize = icmpHeaderSize ? icmpHeaderSize : ICMP_HEADER_MIN_SIZE; + SIZE_T bytesCopied = 0; + + status = PrvKrnlHlprCopyBufferToMDL((BYTE*)pICMPHeader, + pCurrentMDL, + currentMDLOffset, + headerSize, + &bytesCopied); + if(status == STATUS_SUCCESS && + bytesCopied != headerSize) + status = STATUS_INSUFFICIENT_RESOURCES; + } + + KrnlHlprTransportHeaderDestroy((VOID**)&pICMPHeader); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprICMPv4HeaderModifyType() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @kernel_helper_function="KrnlHlprICMPv4HeaderModifyCode" + + Purpose: Set the ICMP Code field in the ICMPv4 Header to the provided value.
+
+ Notes: The NetBufferList parameter is expected to be offset to the start of the ICMPv4 + Header.
+
+ MSDN_Ref:
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprICMPv4HeaderModifyCode(_In_ const FWP_VALUE* pValue, + _Inout_ NET_BUFFER_LIST* pNetBufferList, + _In_ UINT32 icmpHeaderSize) /* 0 */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprICMPv4HeaderModifyCode()\n"); + +#endif /// DBG + + NT_ASSERT(pValue); + NT_ASSERT(pNetBufferList); + + NTSTATUS status = STATUS_SUCCESS; + ICMP_HEADER_V4* pICMPHeader = 0; + BOOLEAN needToFree = FALSE; + + status = KrnlHlprICMPv4HeaderGet(pNetBufferList, + (VOID**)&pICMPHeader, + &needToFree, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE(status); + + pICMPHeader->code = pValue->uint8; + + HLPR_BAIL_LABEL: + + if(needToFree) + { + /// Copy the contents of the allocated buffer to the NBL's discontiguous buffer + if(status == STATUS_SUCCESS) + { + NET_BUFFER* pFirstNetBuffer = NET_BUFFER_LIST_FIRST_NB(pNetBufferList); + PMDL pCurrentMDL = NET_BUFFER_CURRENT_MDL(pFirstNetBuffer); + SIZE_T currentMDLOffset = NET_BUFFER_CURRENT_MDL_OFFSET(pFirstNetBuffer); + SIZE_T headerSize = icmpHeaderSize ? icmpHeaderSize : ICMP_HEADER_MIN_SIZE; + SIZE_T bytesCopied = 0; + + status = PrvKrnlHlprCopyBufferToMDL((BYTE*)pICMPHeader, + pCurrentMDL, + currentMDLOffset, + headerSize, + &bytesCopied); + if(status == STATUS_SUCCESS && + bytesCopied != headerSize) + status = STATUS_INSUFFICIENT_RESOURCES; + } + + KrnlHlprTransportHeaderDestroy((VOID**)&pICMPHeader); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprICMPv4HeaderModifyCode() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +#endif /// ICMPV4_HEADER____ + +#ifndef ICMPV6_HEADER____ +#define ICMPV6_HEADER____ + +/** + @kernel_helper_function="KrnlHlprICMPv6HeaderGet" + + Purpose: Retrieve a pointer to the ICMPv6 Header from the NET_BUFFER_LIST.
+
+ Notes: Assumes the NBL is at the start of the ICMPv6 Header.
+
+ MSDN_Ref:
+*/ +_When_(return != STATUS_SUCCESS, _At_(*ppICMPv6Header, _Post_ _Null_)) +_When_(return == STATUS_SUCCESS, _At_(*ppICMPv6Header, _Post_ _Notnull_)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprICMPv6HeaderGet(_In_ NET_BUFFER_LIST* pNetBufferList, + _Outptr_result_buffer_(icmpHeaderSize) VOID** ppICMPv6Header, + _Inout_ BOOLEAN* pNeedToFreeMemory, + _In_ UINT32 icmpHeaderSize) /* 0 */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprICMPv6HeaderGet()\n"); + +#endif /// DBG + + NTSTATUS status = STATUS_SUCCESS; + BYTE* pBuffer = 0; + NET_BUFFER* pNetBuffer = NET_BUFFER_LIST_FIRST_NB(pNetBufferList); + const UINT32 BUFFER_SIZE = icmpHeaderSize ? icmpHeaderSize : NET_BUFFER_DATA_LENGTH(pNetBuffer); + UINT32 bytesNeeded = icmpHeaderSize ? icmpHeaderSize : NET_BUFFER_DATA_LENGTH(pNetBuffer); + PVOID pContiguousData = 0; + +#pragma warning(push) +#pragma warning(disable: 6014) /// pBuffer is expected to be cleaned up by caller using KrnlHlprTransportHeaderDestroy if *pNeedToFreeMemory is TRUE + + HLPR_NEW_ARRAY(pBuffer, + BYTE, + BUFFER_SIZE, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pBuffer, + status); + +#pragma warning(pop) + + *pNeedToFreeMemory = TRUE; + + pContiguousData = NdisGetDataBuffer(pNetBuffer, + bytesNeeded, + pBuffer, + 1, + 0); + if(!pContiguousData) + { + status = STATUS_UNSUCCESSFUL; + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! KrnlHlprICMPv6HeaderGet : NdisGetDataBuffer() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + if(pBuffer != pContiguousData) + { + HLPR_DELETE_ARRAY(pBuffer, + WFPSAMPLER_SYSLIB_TAG); + + *pNeedToFreeMemory = FALSE; + } + + *ppICMPv6Header = pContiguousData; + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS && + *pNeedToFreeMemory && + pBuffer) + { + KrnlHlprTransportHeaderDestroy((VOID**)&pBuffer); + + *pNeedToFreeMemory = FALSE; + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprICMPv4HeaderGet() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @kernel_helper_function="KrnlHlprICMPv6HeaderModifyType" + + Purpose: Set the ICMP Type field in the ICMPv6 Header to the provided value.
+
+ Notes: The NetBufferList parameter is expected to be offset to the start of the ICMPv6 + Header.
+
+ MSDN_Ref:
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprICMPv6HeaderModifyType(_In_ const FWP_VALUE* pValue, + _Inout_ NET_BUFFER_LIST* pNetBufferList, + _In_ UINT32 icmpHeaderSize) /* 0 */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprICMPv6HeaderModifyType()\n"); + +#endif /// DBG + + NT_ASSERT(pValue); + NT_ASSERT(pNetBufferList); + + NTSTATUS status = STATUS_SUCCESS; + ICMP_HEADER_V6* pICMPHeader = 0; + BOOLEAN needToFree = FALSE; + + status = KrnlHlprICMPv4HeaderGet(pNetBufferList, + (VOID**)&pICMPHeader, + &needToFree, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE(status); + + pICMPHeader->type = pValue->uint8; + + HLPR_BAIL_LABEL: + + if(needToFree) + { + /// Copy the contents of the allocated buffer to the NBL's discontiguous buffer + if(status == STATUS_SUCCESS) + { + NET_BUFFER* pFirstNetBuffer = NET_BUFFER_LIST_FIRST_NB(pNetBufferList); + PMDL pCurrentMDL = NET_BUFFER_CURRENT_MDL(pFirstNetBuffer); + SIZE_T currentMDLOffset = NET_BUFFER_CURRENT_MDL_OFFSET(pFirstNetBuffer); + SIZE_T headerSize = icmpHeaderSize ? icmpHeaderSize : ICMP_HEADER_MIN_SIZE; + SIZE_T bytesCopied = 0; + + status = PrvKrnlHlprCopyBufferToMDL((BYTE*)pICMPHeader, + pCurrentMDL, + currentMDLOffset, + headerSize, + &bytesCopied); + if(status == STATUS_SUCCESS && + bytesCopied != headerSize) + status = STATUS_INSUFFICIENT_RESOURCES; + } + + KrnlHlprTransportHeaderDestroy((VOID**)&pICMPHeader); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprICMPv6HeaderModifyType() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @kernel_helper_function="KrnlHlprICMPv6HeaderModifyCode" + + Purpose: Set the ICMP Code field in the ICMPv6 Header to the provided value.
+
+ Notes: The NetBufferList parameter is expected to be offset to the start of the ICMPv6 + Header.
+
+ MSDN_Ref:
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprICMPv6HeaderModifyCode(_In_ const FWP_VALUE* pValue, + _Inout_ NET_BUFFER_LIST* pNetBufferList, + _In_ UINT32 icmpHeaderSize) /* 0 */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprICMPv6HeaderModifyCode()\n"); + +#endif /// DBG + + NT_ASSERT(pValue); + NT_ASSERT(pNetBufferList); + + NTSTATUS status = STATUS_SUCCESS; + ICMP_HEADER_V6* pICMPHeader = 0; + BOOLEAN needToFree = FALSE; + + status = KrnlHlprICMPv4HeaderGet(pNetBufferList, + (VOID**)&pICMPHeader, + &needToFree, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE(status); + + pICMPHeader->code = pValue->uint8; + + HLPR_BAIL_LABEL: + + if(needToFree) + { + /// Copy the contents of the allocated buffer to the NBL's discontiguous buffer + if(status == STATUS_SUCCESS) + { + NET_BUFFER* pFirstNetBuffer = NET_BUFFER_LIST_FIRST_NB(pNetBufferList); + PMDL pCurrentMDL = NET_BUFFER_CURRENT_MDL(pFirstNetBuffer); + SIZE_T currentMDLOffset = NET_BUFFER_CURRENT_MDL_OFFSET(pFirstNetBuffer); + SIZE_T headerSize = icmpHeaderSize ? icmpHeaderSize : ICMP_HEADER_MIN_SIZE; + SIZE_T bytesCopied = 0; + + status = PrvKrnlHlprCopyBufferToMDL((BYTE*)pICMPHeader, + pCurrentMDL, + currentMDLOffset, + headerSize, + &bytesCopied); + if(status == STATUS_SUCCESS && + bytesCopied != headerSize) + status = STATUS_INSUFFICIENT_RESOURCES; + } + + KrnlHlprTransportHeaderDestroy((VOID**)&pICMPHeader); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprICMPv6HeaderModifyCode() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +#endif /// ICMPV6_HEADER____ + +#ifndef TCP_HEADER____ +#define TCP_HEADER____ + +/** + @kernel_helper_function="KrnlHlprTCPHeaderGet" + + Purpose: Retrieve a pointer to the TCP Header from the NET_BUFFER_LIST.
+
+ Notes: Assumes the NBL is at the start of the TCP Header.
+
+ MSDN_Ref:
+*/ +_When_(return != STATUS_SUCCESS, _At_(*ppTCPHeader, _Post_ _Null_)) +_When_(return == STATUS_SUCCESS, _At_(*ppTCPHeader, _Post_ _Notnull_)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprTCPHeaderGet(_In_ NET_BUFFER_LIST* pNetBufferList, + _Outptr_result_buffer_(tcpHeaderSize) VOID** ppTCPHeader, + _Inout_ BOOLEAN* pNeedToFreeMemory, + _In_ UINT32 tcpHeaderSize) /* 0 */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprTCPHeaderGet()\n"); + +#endif /// DBG + + NTSTATUS status = STATUS_SUCCESS; + BYTE* pBuffer = 0; + NET_BUFFER* pNetBuffer = NET_BUFFER_LIST_FIRST_NB(pNetBufferList); + const UINT32 BUFFER_SIZE = tcpHeaderSize ? tcpHeaderSize : NET_BUFFER_DATA_LENGTH(pNetBuffer); + UINT32 bytesNeeded = tcpHeaderSize ? tcpHeaderSize : NET_BUFFER_DATA_LENGTH(pNetBuffer); + PVOID pContiguousData = 0; + +#pragma warning(push) +#pragma warning(disable: 6014) /// pBuffer is expected to be cleaned up by caller using KrnlHlprTransportHeaderDestroy if *pNeedToFreeMemory is TRUE + + HLPR_NEW_ARRAY(pBuffer, + BYTE, + BUFFER_SIZE, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pBuffer, + status); + +#pragma warning(pop) + + *pNeedToFreeMemory = TRUE; + + pContiguousData = NdisGetDataBuffer(pNetBuffer, + bytesNeeded, + pBuffer, + 1, + 0); + if(!pContiguousData) + { + status = STATUS_UNSUCCESSFUL; + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! KrnlHlprTCPHeaderGet : NdisGetDataBuffer() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + if(pBuffer != pContiguousData) + { + HLPR_DELETE_ARRAY(pBuffer, + WFPSAMPLER_SYSLIB_TAG); + + *pNeedToFreeMemory = FALSE; + } + + *ppTCPHeader = pContiguousData; + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS && + *pNeedToFreeMemory && + pBuffer) + { + KrnlHlprTransportHeaderDestroy((VOID**)&pBuffer); + + *pNeedToFreeMemory = FALSE; + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprTCPHeaderGet() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @kernel_helper_function="KrnlHlprTCPHeaderModifySourcePort" + + Purpose: Set the Source Address field in the TCP Header to the provided value.
+
+ Notes: The NetBufferList parameter is expected to be offset to the start of the TCP + Header.
+
+ Values should be in Network Byte Order.
+
+ MSDN_Ref:
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprTCPHeaderModifySourcePort(_In_ const FWP_VALUE* pValue, + _Inout_ NET_BUFFER_LIST* pNetBufferList, + _In_ UINT32 tcpHeaderSize, /* 0 */ + _In_ BOOLEAN convertByteOrder) /* FALSE */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprTCPHeaderModifySourcePort()\n"); + +#endif /// DBG + + NT_ASSERT(pValue); + NT_ASSERT(pNetBufferList); + + NTSTATUS status = STATUS_SUCCESS; + TCP_HEADER* pTCPHeader = 0; + BOOLEAN needToFree = FALSE; + UINT16 port = convertByteOrder ? htons(pValue->uint16) : pValue->uint16; + + status = KrnlHlprTCPHeaderGet(pNetBufferList, + (VOID**)&pTCPHeader, + &needToFree, + tcpHeaderSize); + HLPR_BAIL_ON_FAILURE(status); + + pTCPHeader->sourcePort = port; + + HLPR_BAIL_LABEL: + + if(needToFree) + { + /// Copy the contents of the allocated buffer to the NBL's discontiguous buffer + if(status == STATUS_SUCCESS) + { + NET_BUFFER* pFirstNetBuffer = NET_BUFFER_LIST_FIRST_NB(pNetBufferList); + PMDL pCurrentMDL = NET_BUFFER_CURRENT_MDL(pFirstNetBuffer); + SIZE_T currentMDLOffset = NET_BUFFER_CURRENT_MDL_OFFSET(pFirstNetBuffer); + SIZE_T headerSize = tcpHeaderSize ? tcpHeaderSize : TCP_HEADER_MIN_SIZE; + SIZE_T bytesCopied = 0; + + status = PrvKrnlHlprCopyBufferToMDL((BYTE*)pTCPHeader, + pCurrentMDL, + currentMDLOffset, + headerSize, + &bytesCopied); + if(status == STATUS_SUCCESS && + bytesCopied != headerSize) + status = STATUS_INSUFFICIENT_RESOURCES; + } + + KrnlHlprTransportHeaderDestroy((VOID**)&pTCPHeader); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprTCPHeaderModifySourcePort() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @kernel_helper_function="KrnlHlprTCPHeaderModifyDestinationPort" + + Purpose: Set the Destination Address field in the TCP Header to the provided value.
+
+ Notes: The NetBufferList parameter is expected to be offset to the start of the TCP + Header.
+
+ Values should be in Network Byte Order.
+
+ MSDN_Ref:
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprTCPHeaderModifyDestinationPort(_In_ const FWP_VALUE* pValue, + _Inout_ NET_BUFFER_LIST* pNetBufferList, + _In_ UINT32 tcpHeaderSize, /* 0 */ + _In_ BOOLEAN convertByteOrder) /* FALSE */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprTCPHeaderModifyDestinationPort()\n"); + +#endif /// DBG + + NT_ASSERT(pValue); + NT_ASSERT(pNetBufferList); + + NTSTATUS status = STATUS_SUCCESS; + TCP_HEADER* pTCPHeader = 0; + BOOLEAN needToFree = FALSE; + UINT16 port = convertByteOrder ? htons(pValue->uint16) : pValue->uint16; + + status = KrnlHlprTCPHeaderGet(pNetBufferList, + (VOID**)&pTCPHeader, + &needToFree, + tcpHeaderSize); + HLPR_BAIL_ON_FAILURE(status); + + pTCPHeader->destinationPort = port; + + HLPR_BAIL_LABEL: + + if(needToFree) + { + /// Copy the contents of the allocated buffer to the NBL's discontiguous buffer + if(status == STATUS_SUCCESS) + { + NET_BUFFER* pFirstNetBuffer = NET_BUFFER_LIST_FIRST_NB(pNetBufferList); + PMDL pCurrentMDL = NET_BUFFER_CURRENT_MDL(pFirstNetBuffer); + SIZE_T currentMDLOffset = NET_BUFFER_CURRENT_MDL_OFFSET(pFirstNetBuffer); + SIZE_T headerSize = tcpHeaderSize ? tcpHeaderSize : TCP_HEADER_MIN_SIZE; + SIZE_T bytesCopied = 0; + + status = PrvKrnlHlprCopyBufferToMDL((BYTE*)pTCPHeader, + pCurrentMDL, + currentMDLOffset, + headerSize, + &bytesCopied); + if(status == STATUS_SUCCESS && + bytesCopied != headerSize) + status = STATUS_INSUFFICIENT_RESOURCES; + } + + KrnlHlprTransportHeaderDestroy((VOID**)&pTCPHeader); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprTCPHeaderModifyDestinationPort() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +#endif /// TCP_HEADER____ + +#ifndef UDP_HEADER____ +#define UDP_HEADER____ + +/** + @kernel_helper_function="KrnlHlprUDPHeaderGet" + + Purpose: Retrieve a pointer to the UDP Header from the NET_BUFFER_LIST.
+
+ Notes: Assumes the NBL is at the start of the UDP Header.
+
+ MSDN_Ref:
+*/ +_When_(return != STATUS_SUCCESS, _At_(*ppUDPHeader, _Post_ _Null_)) +_When_(return == STATUS_SUCCESS, _At_(*ppUDPHeader, _Post_ _Notnull_)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprUDPHeaderGet(_In_ NET_BUFFER_LIST* pNetBufferList, + _Outptr_result_buffer_(udpHeaderSize) VOID** ppUDPHeader, + _Inout_ BOOLEAN* pNeedToFreeMemory, + _In_ UINT32 udpHeaderSize) /* 0 */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprUDPHeaderGet()\n"); + +#endif /// DBG + + NTSTATUS status = STATUS_SUCCESS; + BYTE* pBuffer = 0; + NET_BUFFER* pNetBuffer = NET_BUFFER_LIST_FIRST_NB(pNetBufferList); + const UINT32 BUFFER_SIZE = udpHeaderSize ? udpHeaderSize : NET_BUFFER_DATA_LENGTH(pNetBuffer); + UINT32 bytesNeeded = udpHeaderSize ? udpHeaderSize : NET_BUFFER_DATA_LENGTH(pNetBuffer); + PVOID pContiguousData = 0; + +#pragma warning(push) +#pragma warning(disable: 6014) /// pBuffer is expected to be cleaned up by caller using KrnlHlprTransportHeaderDestroy if *pNeedToFreeMemory is TRUE + + HLPR_NEW_ARRAY(pBuffer, + BYTE, + BUFFER_SIZE, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pBuffer, + status); + +#pragma warning(pop) + + *pNeedToFreeMemory = TRUE; + + pContiguousData = NdisGetDataBuffer(pNetBuffer, + bytesNeeded, + pBuffer, + 1, + 0); + if(!pContiguousData) + { + status = STATUS_UNSUCCESSFUL; + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! KrnlHlprUDPHeaderGet : NdisGetDataBuffer() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + if(pBuffer != pContiguousData) + { + HLPR_DELETE_ARRAY(pBuffer, + WFPSAMPLER_SYSLIB_TAG); + + *pNeedToFreeMemory = FALSE; + } + + *ppUDPHeader = pContiguousData; + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS && + *pNeedToFreeMemory && + pBuffer) + { + KrnlHlprTransportHeaderDestroy((VOID**)&pBuffer); + + *pNeedToFreeMemory = FALSE; + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprUDPHeaderGet() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @kernel_helper_function="KrnlHlprUDPHeaderModifySourcePort" + + Purpose: Set the Source Address field in the UDP Header to the provided value.
+
+ Notes: The NetBufferList parameter is expected to be offset to the start of the UDP + Header.
+
+ Values should be in Network Byte Order.
+
+ MSDN_Ref:
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprUDPHeaderModifySourcePort(_In_ const FWP_VALUE* pValue, + _Inout_ NET_BUFFER_LIST* pNetBufferList, + _In_ UINT32 udpHeaderSize, /* 0 */ + _In_ BOOLEAN convertByteOrder) /* FALSE */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprUDPHeaderModifySourcePort()\n"); + +#endif /// DBG + + NT_ASSERT(pValue); + NT_ASSERT(pNetBufferList); + + NTSTATUS status = STATUS_SUCCESS; + UDP_HEADER* pUDPHeader = 0; + BOOLEAN needToFree = FALSE; + UINT16 port = convertByteOrder ? htons(pValue->uint16) : pValue->uint16; + + status = KrnlHlprUDPHeaderGet(pNetBufferList, + (VOID**)&pUDPHeader, + &needToFree, + udpHeaderSize); + HLPR_BAIL_ON_FAILURE(status); + + pUDPHeader->sourcePort = port; + + HLPR_BAIL_LABEL: + + if(needToFree) + { + /// Copy the contents of the allocated buffer to the NBL's discontiguous buffer + if(status == STATUS_SUCCESS) + { + NET_BUFFER* pFirstNetBuffer = NET_BUFFER_LIST_FIRST_NB(pNetBufferList); + PMDL pCurrentMDL = NET_BUFFER_CURRENT_MDL(pFirstNetBuffer); + SIZE_T currentMDLOffset = NET_BUFFER_CURRENT_MDL_OFFSET(pFirstNetBuffer); + SIZE_T headerSize = udpHeaderSize ? udpHeaderSize : UDP_HEADER_MIN_SIZE; + SIZE_T bytesCopied = 0; + + status = PrvKrnlHlprCopyBufferToMDL((BYTE*)pUDPHeader, + pCurrentMDL, + currentMDLOffset, + headerSize, + &bytesCopied); + if(status == STATUS_SUCCESS && + bytesCopied != headerSize) + status = STATUS_INSUFFICIENT_RESOURCES; + } + + KrnlHlprTransportHeaderDestroy((VOID**)&pUDPHeader); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprUDPHeaderModifySourcePort() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @kernel_helper_function="KrnlHlprUDPHeaderModifyDestinationPort" + + Purpose: Set the Destination Address field in the UDP Header to the provided value.
+
+ Notes: The NetBufferList parameter is expected to be offset to the start of the UDP + Header.
+
+ Values should be in Network Byte Order.
+
+ MSDN_Ref:
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprUDPHeaderModifyDestinationPort(_In_ const FWP_VALUE* pValue, + _Inout_ NET_BUFFER_LIST* pNetBufferList, + _In_ UINT32 udpHeaderSize, /* 0 */ + _In_ BOOLEAN convertByteOrder) /* FALSE */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprUDPHeaderModifyDestinationPort()\n"); + +#endif /// DBG + + NT_ASSERT(pValue); + NT_ASSERT(pNetBufferList); + + NTSTATUS status = STATUS_SUCCESS; + UDP_HEADER* pUDPHeader = 0; + BOOLEAN needToFree = FALSE; + UINT16 port = convertByteOrder ? htons(pValue->uint16) : pValue->uint16; + + status = KrnlHlprUDPHeaderGet(pNetBufferList, + (VOID**)&pUDPHeader, + &needToFree, + udpHeaderSize); + HLPR_BAIL_ON_FAILURE(status); + + pUDPHeader->destinationPort = port; + + HLPR_BAIL_LABEL: + + if(needToFree) + { + /// Copy the contents of the allocated buffer to the NBL's discontiguous buffer + if(status == STATUS_SUCCESS) + { + NET_BUFFER* pFirstNetBuffer = NET_BUFFER_LIST_FIRST_NB(pNetBufferList); + PMDL pCurrentMDL = NET_BUFFER_CURRENT_MDL(pFirstNetBuffer); + SIZE_T currentMDLOffset = NET_BUFFER_CURRENT_MDL_OFFSET(pFirstNetBuffer); + SIZE_T headerSize = udpHeaderSize ? udpHeaderSize : UDP_HEADER_MIN_SIZE; + SIZE_T bytesCopied = 0; + + status = PrvKrnlHlprCopyBufferToMDL((BYTE*)pUDPHeader, + pCurrentMDL, + currentMDLOffset, + headerSize, + &bytesCopied); + if(status == STATUS_SUCCESS && + bytesCopied != headerSize) + status = STATUS_INSUFFICIENT_RESOURCES; + } + + KrnlHlprTransportHeaderDestroy((VOID**)&pUDPHeader); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprUDPHeaderModifyDestinationPort() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +#endif /// UDP_HEADER____ diff --git a/network/trans/WFPSampler/syslib/HelperFunctions_Headers.h b/network/trans/WFPSampler/syslib/HelperFunctions_Headers.h new file mode 100644 index 000000000..04ecf96c2 --- /dev/null +++ b/network/trans/WFPSampler/syslib/HelperFunctions_Headers.h @@ -0,0 +1,678 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_Headers.h +// +// Abstract: +// This module contains definitions and prototypes of kernel helper functions that assist with +// MAC, IP, and Transport header operations. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Enhance annotations and add +// KrnlHlprIPHeaderGetDestinationAddressField, +// KrnlHlprIPHeaderGetSourceAddressField, +// KrnlHlprIPHeaderGetVersionField, +// KrnlHlprTransportHeaderGetSourcePortField, +// KrnlHlprTransportHeaderGetDestinationPortField +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef HELPERFUNCTIONS_HEADERS_H +#define HELPERFUNCTIONS_HEADERS_H + +#pragma warning(push) +#pragma warning(disable: 4201) /// NAMELESS_STRUCT_UNION + +#define ETHERNET_ADDRESS_SIZE 6 + +#define IPV4_ADDRESS_SIZE 4 +#define IPV6_ADDRESS_SIZE 16 + +#define IPV4 4 +#define IPV6 6 + +#define IPV4_HEADER_MIN_SIZE 20 +#define IPV6_HEADER_MIN_SIZE 40 +#define ICMP_HEADER_MIN_SIZE 8 +#define TCP_HEADER_MIN_SIZE 20 +#define UDP_HEADER_MIN_SIZE 8 + +#define ICMPV4 1 +#define TCP 6 +#define UDP 17 +#define ICMPV6 58 + +static const UINT8 IPV4_LOOPBACK_ADDRESS[] = {0x01, + 0x00, + 0x00, + 0x7F}; /// 127.0.0.1 ( in network byte order) +static const UINT8 IPV6_LOOPBACK_ADDRESS[] = {0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x01}; /// ::1 + + + +/* + RFC 894 - A Standard for the Transmission of
+ IP Datagrams over Ethernet Networks
+
+ 0 1 2
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | |
+ + Destination MAC Address +
+ | |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | |
+ + Source MAC Address +
+ | |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Type | Data... |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ RFC_REF: http://www.faqs.org/rfcs/rfc894.html
+*/ +typedef struct _ETHERNET_II_HEADER_ +{ + BYTE pDestinationAddress[6]; + BYTE pSourceAddress[6]; + UINT16 type; +}ETHERNET_II_HEADER; + +/* + RFC 1042 - A Standard for the Transmission of
+ IP Datagrams over IEEE 802 Networks
+
+ 0 1 2
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | |
+ + Destination MAC Address +
+ | |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | |
+ + Source MAC Address +
+ | |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Length | DSAP |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | SSAP | Control Byte | OUI ... >
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ < OUI (cont.) | Type ... >
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ < Type (cont.) | Data ... |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ RFC_REF: http://www.faqs.org/rfcs/rfc1042.html
+*/ +typedef struct _ETHERNET_SNAP_HEADER_ +{ + BYTE pDestinationAddress[6]; + BYTE pSourceAddress[6]; + UINT16 length; + UINT8 destinationSAP; /// Destination Subnetwork Access Protocol + UINT8 sourceSAP; /// Source Subnetwork Access Protocol + UINT8 controlByte; + UINT8 pOUI[3]; /// Organizationally Unique Identifier + UINT16 type; +}ETHERNET_SNAP_HEADER; + +/* + RFC 791 - Internet Protocol
+
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ |Version| IHL |Type of Service| Total Length |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Identification |Flags| Fragment Offset |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Time to Live | Protocol | Header Checksum |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Source Address |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Destination Address |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Options | Padding |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ RFC_REF: http://www.faqs.org/rfcs/rfc791.html
+*/ +typedef struct _IP_HEADER_V4_ +{ + union + { + UINT8 versionAndHeaderLength; + struct + { + UINT8 headerLength : 4; + UINT8 version : 4; + }; + }; + union + { + UINT8 typeOfService; + UINT8 differentiatedServicesCodePoint; + struct + { + UINT8 explicitCongestionNotification : 2; + UINT8 typeOfService : 6; + }; + }; + UINT16 totalLength; + UINT16 identification; + union + { + UINT16 flagsAndFragmentOffset; + struct + { + UINT16 fragmentOffset : 13; + UINT16 flags : 3; + }; + }; + UINT8 timeToLive; + UINT8 protocol; + UINT16 checksum; + BYTE pSourceAddress[sizeof(UINT32)]; + BYTE pDestinationAddress[sizeof(UINT32)]; +}IP_HEADER_V4, *PIP_HEADER_V4; + +/* + RFC 2460 - Internet Protocol, Version 6 (IPv6) Specification
+
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ |Version| Traffic Class | Flow Label |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Payload Length | Next Header | Hop Limit |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | |
+ + +
+ | |
+ + Source Address +
+ | |
+ + +
+ | |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | |
+ + +
+ | |
+ + Destination Address +
+ | |
+ + +
+ | |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ RFC_REF: http://www.faqs.org/rfcs/rfc2460.html
+*/ +typedef struct _IP_HEADER_V6_ +{ + union + { + UINT8 pVersionTrafficClassAndFlowLabel[4]; + struct + { + UINT8 r1 : 4; + UINT8 value : 4; + UINT8 r2; + UINT8 r3; + UINT8 r4; + }version; + }; + UINT16 payloadLength; + UINT8 nextHeader; + UINT8 hopLimit; + BYTE pSourceAddress[16]; + BYTE pDestinationAddress[16]; +}IP_HEADER_V6, *PIP_HEADER_V6; + +/* + RFC 792 - Internet Control Message Protocol
+
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Type | Code | Checksum |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Variable (Dependent on Type / Code) |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+*/ +typedef struct _ICMP_HEADER_V4_ +{ + UINT8 type; + UINT8 code; + UINT16 checksum; +/* + union + { + ECHO_MESSAGE echo; + DESTINATION_UNREACHABLE_MESSAGE destinationUnreachable; + SOURCE_QUENCH_MESSAGE sourceQuench; + REDIRECT_MESSAGE redirect; + TIME_EXCEEDED_MESSAGE timeExceeded; + PARAMETER_PROBLEM_MESSAGE parameterProblem; + TIMESTAMP_MESSAGE timestamp; + INFORMATION_MESSAGE information; + }; +*/ +}ICMP_HEADER_V4, *PICMP_HEADER_V4; + +/* + RFC 2463 - Internet Control Message Protocol for IPv6
+
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Type | Code | Checksum |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Variable (Dependent on Type / Code) |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+*/ +typedef struct _ICMP_HEADER_V6_ +{ + UINT8 type; + UINT8 code; + UINT16 checksum; +/* union + { + ECHO_MESSAGE echo; + DESTINATION_UNREACHABLE_MESSAGE destinationUnreachable; + SOURCE_QUENCH_MESSAGE sourceQuench; + REDIRECT_MESSAGE redirect; + TIME_EXCEEDED_MESSAGE timeExceeded; + PARAMETER_PROBLEM_MESSAGE parameterProblem; + TIMESTAMP_MESSAGE timestamp; + INFORMATION_MESSAGE information; + };*/ +}ICMP_HEADER_V6, *PICMP_HEADER_V6; + +/* + RFC 793 - Transmission Control Protocol
+
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Source Port | Destination Port |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Sequence Number |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Acknowledgment Number |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ |Offset |Rsvd |N|C|E|U|A|P|R|S|F| Window |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Checksum | Urgent Pointer |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Options | Padding |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ RFC_REF: http://www.faqs.org/rfcs/rfc793.html
+*/ +typedef struct _TCP_HEADER_ +{ + UINT16 sourcePort; + UINT16 destinationPort; + UINT32 sequenceNumber; + UINT32 acknowledgementNumber; + union + { + UINT8 dataOffsetReservedAndNS; + struct + { + UINT8 nonceSum : 1; + UINT8 reserved : 3; + UINT8 dataOffset : 4; + }dORNS; + }; + union + { + UINT8 controlBits; + struct + { + UINT8 FIN : 1; + UINT8 SYN : 1; + UINT8 RST : 1; + UINT8 PSH : 1; + UINT8 ACK : 1; + UINT8 URG : 1; + UINT8 ECE : 1; + UINT8 CWR : 1; + }; + }; + UINT16 window; + UINT16 checksum; + UINT16 urgentPointer; +}TCP_HEADER, *PTCP_HEADER; + +/* + RFC 768 - User Datagram Protocol
+
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Source Port | Destination Port |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Length | Checksum |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ RFC_REF: http://www.faqs.org/rfcs/rfc768.html
+*/ +typedef struct _UDP_HEADER_ +{ + UINT16 sourcePort; + UINT16 destinationPort; + UINT16 length; + UINT16 checksum; +}UDP_HEADER, *PUDP_HEADER; + +#pragma warning(pop) /// NAMELESS_STRUCT_UNION + +_At_(*ppMACHeader, _Pre_ _Notnull_) +_At_(*ppMACHeader, _Post_ _Null_) +_Success_(*ppMACHeader == 0) +inline VOID KrnlHlprMACHeaderDestroy(_Inout_ VOID** ppMACHeader); + +_When_(return != STATUS_SUCCESS, _At_(*ppMACHeader, _Post_ _Null_)) +_When_(return == STATUS_SUCCESS, _At_(*ppMACHeader, _Post_ _Notnull_)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprMACHeaderGet(_In_ NET_BUFFER_LIST* pNetBufferList, + _Outptr_ VOID** ppMACHeader, + _Inout_ BOOLEAN* pNeedToFreeMemory, + _In_ UINT32 macHeaderSize = 0); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprMACHeaderModifySourceAddress(_In_ const FWP_VALUE* pValue, + _Inout_ NET_BUFFER_LIST* pNetBufferList); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprMACHeaderModifyDestinationAddress(_In_ const FWP_VALUE* pValue, + _Inout_ NET_BUFFER_LIST* pNetBufferList); + +_At_(*ppIPHeader, _Pre_ _Notnull_) +_At_(*ppIPHeader, _Post_ _Null_) +_Success_(*ppIPHeader == 0) +inline VOID KrnlHlprIPHeaderDestroy(_Inout_ VOID** ppIPHeader); + +_When_(return != STATUS_SUCCESS, _At_(*ppIPHeader, _Post_ _Null_)) +_When_(return == STATUS_SUCCESS, _At_(*ppIPHeader, _Post_ _Notnull_)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprIPHeaderGet(_In_ NET_BUFFER_LIST* pNetBufferList, + _In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Outptr_result_buffer_(*pIPHeaderSize) VOID** ppIPHeader, + _Inout_ BOOLEAN* pNeedToFreeMemory, + _Inout_opt_ FWP_DIRECTION* pDirection = 0, + _Inout_opt_ UINT32* pIPHeaderSize = 0); +_When_(return != STATUS_SUCCESS, _At_(*ppIPHeader, _Post_ _Null_)) +_When_(return == STATUS_SUCCESS, _At_(*ppIPHeader, _Post_ _Notnull_)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprIPHeaderGet(_In_ NET_BUFFER_LIST* pNetBufferList, + _Outptr_result_buffer_(ipHeaderSize) VOID** ppIPHeader, + _Inout_ BOOLEAN* pNeedToFreeMemory, + _In_ UINT32 ipHeaderSize = 0); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(return != 0) +BYTE* KrnlHlprIPHeaderGetDestinationAddressField(_In_ NET_BUFFER_LIST* pNetBufferList, + _In_ ADDRESS_FAMILY addressFamily); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(return != 0) +BYTE* KrnlHlprIPHeaderGetSourceAddressField(_In_ NET_BUFFER_LIST* pNetBufferList, + _In_ ADDRESS_FAMILY addressFamily); + + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +IPPROTO KrnlHlprIPHeaderGetProtocolField(_In_ NET_BUFFER_LIST* pNetBufferList, + _In_ ADDRESS_FAMILY addressFamily); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +UINT8 KrnlHlprIPHeaderGetVersionField(_In_ NET_BUFFER_LIST* pNetBufferList); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID KrnlHlprIPHeaderCalculateV4Checksum(_Inout_ NET_BUFFER_LIST* pNetBufferList, + _In_ UINT32 ipHeaderSize = IPV4_HEADER_MIN_SIZE); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprIPHeaderModifySourceAddress(_In_ const FWP_VALUE* pValue, + _Inout_ NET_BUFFER_LIST* pNetBufferList, + _In_ const BOOLEAN recalculateChecksum = TRUE, + _In_ BOOLEAN convertByteOrder = FALSE); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprIPHeaderModifyDestinationAddress(_In_ const FWP_VALUE* pValue, + _Inout_ NET_BUFFER_LIST* pNetBufferList, + _In_ const BOOLEAN recalculateChecksum = TRUE, + _In_ BOOLEAN convertByteOrder = FALSE); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprIPHeaderModifyLoopbackToLocal(_In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _In_ const FWP_VALUE* pRemoteAddress, + _In_ const UINT32 ipHeaderSize, + _Inout_ NET_BUFFER_LIST* pNetBufferList, + _In_reads_opt_(controlDataSize) const WSACMSGHDR* pControlData = 0, + _In_ UINT32 controlDataSize = 0); + +_At_(*ppTransportHeader, _Pre_ _Notnull_) +_At_(*ppTransportHeader, _Post_ _Null_) +_Success_(*ppTransportHeader == 0) +inline VOID KrnlHlprTransportHeaderDestroy(_Inout_ VOID** ppTransportHeader); + +_When_(return != STATUS_SUCCESS, _At_(*ppTransportHeader, _Post_ _Null_)) +_When_(return == STATUS_SUCCESS, _At_(*ppTransportHeader, _Post_ _Notnull_)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprTransportHeaderGet(_In_ NET_BUFFER_LIST* pNetBufferList, + _In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Outptr_result_buffer_(*pTransportHeaderSize) VOID** ppTransportHeader, + _Inout_ BOOLEAN* pNeedToFreeMemory, + _Inout_opt_ IPPROTO* pProtocol = 0, + _Inout_opt_ FWP_DIRECTION* pDirection = 0, + _Inout_opt_ UINT32* pTransportHeaderSize = 0); +_When_(return != STATUS_SUCCESS, _At_(*ppTransportHeader, _Post_ _Null_)) +_When_(return == STATUS_SUCCESS, _At_(*ppTransportHeader, _Post_ _Notnull_)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprTransportHeaderGet(_In_ NET_BUFFER_LIST* pNetBufferList, + _Outptr_result_buffer_(transportHeaderSize) VOID** ppTransportHeader, + _Inout_ BOOLEAN* pNeedToFreeMemory, + _In_ UINT32 transportHeaderSize = 0); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +UINT16 KrnlHlprTransportHeaderGetSourcePortField(_In_ NET_BUFFER_LIST* pNetBufferList, + _In_ IPPROTO protocol); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +UINT16 KrnlHlprTransportHeaderGetDestinationPortField(_In_ NET_BUFFER_LIST* pNetBufferList, + _In_ IPPROTO protocol); + +_When_(return != STATUS_SUCCESS, _At_(*ppICMPv4Header, _Post_ _Null_)) +_When_(return == STATUS_SUCCESS, _At_(*ppICMPv4Header, _Post_ _Notnull_)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprICMPv4HeaderGet(_In_ NET_BUFFER_LIST* pNetBufferList, + _Outptr_result_buffer_(icmpHeaderSize) VOID** ppICMPv4Header, + _Inout_ BOOLEAN* pNeedToFreeMemory, + _In_ UINT32 icmpHeaderSize = 0); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprICMPv4HeaderModifyType(_In_ const FWP_VALUE* pValue, + _Inout_ NET_BUFFER_LIST* pNetBufferList, + _In_ UINT32 icmpHeaderSize = 0); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprICMPv4HeaderModifyCode(_In_ const FWP_VALUE* pValue, + _Inout_ NET_BUFFER_LIST* pNetBufferList, + _In_ UINT32 icmpHeaderSize = 0); + +_When_(return != STATUS_SUCCESS, _At_(*ppICMPv4Header, _Post_ _Null_)) +_When_(return == STATUS_SUCCESS, _At_(*ppICMPv4Header, _Post_ _Notnull_)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprICMPv6HeaderGet(_In_ NET_BUFFER_LIST* pNetBufferList, + _Outptr_result_buffer_(icmpHeaderSize) VOID** ppICMPv4Header, + _Inout_ BOOLEAN* pNeedToFreeMemory, + _In_ UINT32 icmpHeaderSize = 0); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprICMPv6HeaderModifyType(_In_ const FWP_VALUE* pValue, + _Inout_ NET_BUFFER_LIST* pNetBufferList, + _In_ UINT32 icmpHeaderSize = 0); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprICMPv6HeaderModifyCode(_In_ const FWP_VALUE* pValue, + _Inout_ NET_BUFFER_LIST* pNetBufferList, + _In_ UINT32 icmpHeaderSize = 0); + +_When_(return != STATUS_SUCCESS, _At_(*ppTCPHeader, _Post_ _Null_)) +_When_(return == STATUS_SUCCESS, _At_(*ppTCPHeader, _Post_ _Notnull_)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprTCPHeaderGet(_In_ NET_BUFFER_LIST* pNetBufferList, + _Outptr_result_buffer_(tcpHeaderSize) VOID** ppTCPHeader, + _Inout_ BOOLEAN* pNeedToFreeMemory, + _In_ UINT32 tcpHeaderSize = 0); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprTCPHeaderModifySourcePort(_In_ const FWP_VALUE* pValue, + _Inout_ NET_BUFFER_LIST* pNetBufferList, + _In_ UINT32 tcpHeaderSize = 0, + _In_ BOOLEAN convertByteOrder = FALSE); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprTCPHeaderModifyDestinationPort(_In_ const FWP_VALUE* pValue, + _Inout_ NET_BUFFER_LIST* pNetBufferList, + _In_ UINT32 tcpHeaderSize = 0, + _In_ BOOLEAN convertByteOrder = FALSE); + +_When_(return != STATUS_SUCCESS, _At_(*ppUDPHeader, _Post_ _Null_)) +_When_(return == STATUS_SUCCESS, _At_(*ppUDPHeader, _Post_ _Notnull_)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprUDPHeaderGet(_In_ NET_BUFFER_LIST* pNetBufferList, + _Outptr_result_buffer_(udpHeaderSize) VOID** ppUDPHeader, + _Inout_ BOOLEAN* pNeedToFreeMemory, + _In_ UINT32 udpHeaderSize = 0); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprUDPHeaderModifySourcePort(_In_ const FWP_VALUE* pValue, + _Inout_ NET_BUFFER_LIST* pNetBufferList, + _In_ UINT32 udpHeaderSize = 0, + _In_ BOOLEAN convertByteOrder = FALSE); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprUDPHeaderModifyDestinationPort(_In_ const FWP_VALUE* pValue, + _Inout_ NET_BUFFER_LIST* pNetBufferList, + _In_ UINT32 udpheaderSize = 0, + _In_ BOOLEAN convertByteOrder = FALSE); + +#endif /// HELPERFUNCTIONS_HEADERS_H diff --git a/network/trans/WFPSampler/syslib/HelperFunctions_ICMPMessages.h b/network/trans/WFPSampler/syslib/HelperFunctions_ICMPMessages.h new file mode 100644 index 000000000..d8a5437b5 --- /dev/null +++ b/network/trans/WFPSampler/syslib/HelperFunctions_ICMPMessages.h @@ -0,0 +1,152 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_ICMPMessages.h +// +// Abstract: +// This module contains prototypes for kernel helper functions that assist with ICMP Message +// packets. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef HELPERFUNCTIONS_ICMP_MESSAGES_H +#define HELPERFUNCTIONS_ICMP_MESSAGES_H + +/* + ICMP V4 Echo Message
+
+ ICMP Type 0 = Echo Request
+ 8 = Echo Reply
+
+ ICMP Code 0
+*/ +typedef struct _ECHO_MESSAGE_ +{ + UINT16 identifier; + UINT16 sequence; + BYTE pStartOfData[1]; +}ECHO_MESSAGE, *PECHO_MESSAGE; + +/* + ICMP V4 Destination Unreachable Message
+
+ ICMP Type 3
+
+ ICMP Code 0 = Network Unreachable
+ 1 = Host Unreachable
+ 2 = Protocol Unreachable
+ 3 = Port Unreachable
+ 4 = Fragmentation needed but IP Header's Don't Fragment Flag is set
+ 5 = Source Route Failed
+ +*/ +typedef struct _DESTINATION_UNREACHABLE_MESSAGE_ +{ + UINT32 unused; + IPV4_HEADER originalIPHeader; + BYTE pOriginalData[64]; +}DESTINATION_UNREACHABLE_MESSAGE, *PDESTINATION_UNREACHABLE_MESSAGE; + +/* + ICMP V4 Source Quench Message
+
+ ICMP Type 4
+
+ ICMP Code 0
+*/ +typedef struct _SOURCE_QUENCH_MESSAGE_ +{ + UINT32 unused; + IPV4_HEADER originalIPHeader; + BYTE pOriginalData[64]; +}SOURCE_QUENCH_MESSAGE, *PSOURCE_QUENCH_MESSAGE; + +/* + ICMP V4 Redirect Message
+
+ ICMP Type 5
+
+ ICMP Code 0 = Redrect datagrams for the Network
+ 1 = Redrect datagrams for the Host
+ 2 = Redrect datagrams for the Type of Service and Network
+ 3 = Redrect datagrams for the Type of Service and Host
+*/ +typedef struct _REDIRECT_MESSAGE_ +{ + UINT32 gatewayInternetAddress; + IPV4_HEADER originalIPHeader; + BYTE pOriginalData[64]; +}REDIRECT_MESSAGE, *PREDIRECT_MESSAGE; + +/* + ICMP V4 Time Exceeded Message
+
+ ICMP Type 11
+
+ ICMP Code 0 = Time To Live exceeded in transit
+ 1 = Fragment reassembly time exceeded
+*/ +typedef struct _TIME_EXCEEDED_MESSAGE_ +{ + UINT32 unused; + IPV4_HEADER originalIPHeader; + BYTE pOriginalData[64]; +}TIME_EXCEEDED_MESSAGE, *PTIME_EXCEEDED_MESSAGE; + +/* + ICMP V4 Parameter Problem Message
+
+ ICMP Type 12
+
+ ICMP Code 0 = Pointer indicates the error
+*/ +typedef struct _PARAMETER_PROBLEM_MESSAGE_ +{ + UINT8 pointer; + BYTE pUnused[3]; + IPV4_HEADER originalIPHeader; + BYTE pOriginalData[64]; +}PARAMETER_PROBLEM_MESSAGE, *PPARAMETER_PROBLEM_MESSAGE; + +/* + ICMP V4 Timestamp Message
+
+ ICMP Type 13 = Timestamp Request
+ 14 = Timestamp Reply
+
+ ICMP Code 0
+*/ +typedef struct _TIMESTAMP_MESSAGE_ +{ + UINT16 identifier; + UINT16 sequence; + UINT32 originateTimestamp; + UINT32 receiveTimestamp; + UINT32 transmitTimestamp; +}TIMESTAMP_MESSAGE, *PTIMESTAMP_MESSAGE; + +/* + ICMP V4 Information Message
+
+ ICMP Type 15 = Information Request
+ 16 = Information Reply
+
+ ICMP Code 0
+*/ +typedef struct _INFORMATION_MESSAGE_ +{ + UINT16 identifier; + UINT16 sequence; +}INFORMATION_MESSAGE, *PINFORMATION_MESSAGE; + +#endif /// HELPERFUNCTIONS_ICMP_MESSAGES_H \ No newline at end of file diff --git a/network/trans/WFPSampler/syslib/HelperFunctions_Include.h b/network/trans/WFPSampler/syslib/HelperFunctions_Include.h new file mode 100644 index 000000000..c00ad17db --- /dev/null +++ b/network/trans/WFPSampler/syslib/HelperFunctions_Include.h @@ -0,0 +1,41 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions\Include.h +// +// Abstract: +// This module contains a central repository of headers which contain prototypes for all kernel +// helper functions. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Add HelperFunctions_FlowContext.h +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef HELPERFUNCTIONS_INCLUDE_H +#define HELPERFUNCTIONS_INCLUDE_H + +#include "HelperFunctions_Macros.h" /// . +#include "HelperFunctions_NDIS.h" /// . +#include "HelperFunctions_ICMPMessages.h" /// . +#include "HelperFunctions_Headers.h" /// . +#include "HelperFunctions_FwpObjects.h" /// . +#include "HelperFunctions_FlowContext.h" /// . +#include "HelperFunctions_ClassifyData.h" /// . +#include "HelperFunctions_NotifyData.h" /// . +#include "HelperFunctions_InjectionData.h" /// . +#include "HelperFunctions_NetBuffer.h" /// . +#include "HelperFunctions_PendData.h" /// . +#include "HelperFunctions_RedirectData.h" /// . +#include "HelperFunctions_DeferredProcedureCalls.h" ///. +#include "HelperFunctions_WorkItems.h" /// . + +#endif /// HELPERFUNCTIONS_INCLUDE_H \ No newline at end of file diff --git a/network/trans/WFPSampler/syslib/HelperFunctions_InjectionData.cpp b/network/trans/WFPSampler/syslib/HelperFunctions_InjectionData.cpp new file mode 100644 index 000000000..663f7c607 --- /dev/null +++ b/network/trans/WFPSampler/syslib/HelperFunctions_InjectionData.cpp @@ -0,0 +1,988 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_InjectionData.cpp +// +// Abstract: +// This module contains kernel helper functions that assist with INJECTION_DATA and +// INJECTION_HANDLE_DATA. +// +// Naming Convention: +// +// +// +// i.e. +// +// KrnlHlprInjectionDataCreate +// +// +// KrnlHlpr - Function is located in syslib\ and applies to kernel mode. +// +// InjectionData - Function pertains to INJECTION_DATA objects. +// InjectionHandleData - Function pertains to INJECTION_HANDLE_DATA objects. +// +// { +// Create - Function allocates and fills memory. +// Destroy - Function cleans up and frees memory. +// Populate - Function fills memory with values. +// Purge - Function cleans up values. +// } +// +// Private Functions: +// +// Public Functions: +// KrnlHlprInjectionDataCreate(), +// KrnlHlprInjectionDataDestroy(), +// KrnlHlprInjectionDataPopulate(), +// KrnlHlprInjectionDataPurge(), +// KrnlHlprInjectionHandleDataCreate(), +// KrnlHlprInjectionHandleDataDestroy(), +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Enhance annotations, add multiple injector support, add +// missing layers for injection handles, and add +// support for controlData. +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "HelperFunctions_Include.h" /// . +#include "HelperFunctions_InjectionData.tmh" /// $(OBJ_PATH)\$(O)\ + +#ifndef INJECTION_DATA____ +#define INJECTION_DATA____ + +/** + @kernel_helper_function="KrnlHlprInjectionDataPurge" + + Purpose: Cleanup a INJECTION_DATA object.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprInjectionDataPurge(_Inout_ INJECTION_DATA* pInjectionData) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprInjectionDataPurge()\n"); + +#endif /// DBG + + NT_ASSERT(pInjectionData); + + if(pInjectionData->pControlData) + { + HLPR_DELETE_ARRAY(pInjectionData->pControlData, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + } + + RtlZeroMemory(pInjectionData, + sizeof(INJECTION_DATA)); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprInjectionDataPurge()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprInjectionDataDestroy" + + Purpose: Cleanup and free a INJECTION_DATA object.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_At_(*ppInjectionData, _Pre_ _Notnull_) +_At_(*ppInjectionData, _Post_ _Null_ __drv_freesMem(Pool)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(*ppInjectionData == 0) +inline VOID KrnlHlprInjectionDataDestroy(_Inout_ INJECTION_DATA** ppInjectionData) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprInjectionDataDestroy()\n"); + +#endif /// DBG + + NT_ASSERT(ppInjectionData); + + if(*ppInjectionData) + { + KrnlHlprInjectionDataPurge(*ppInjectionData); + + HLPR_DELETE(*ppInjectionData, + WFPSAMPLER_SYSLIB_TAG); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprInjectionDataDestroy()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprInjectionDataPopulate" + + Purpose: Populates a INJECTION_DATA object with the data based off values obtained in the + classifyFn.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/FF551202.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprInjectionDataPopulate(_Inout_ INJECTION_DATA* pInjectionData, + _In_ const FWPS_INCOMING_VALUES0* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES0* pMetadataValues, + _In_opt_ const NET_BUFFER_LIST* pNetBufferList, + _In_ const FWPS_FILTER* pFilter) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprInjectionDataPopulate()\n"); + +#endif /// DBG + + NT_ASSERT(pInjectionData); + NT_ASSERT(pClassifyValues); + NT_ASSERT(pMetadataValues); + NT_ASSERT(pFilter); + + NTSTATUS status = STATUS_SUCCESS; + UINT32 index = WFPSAMPLER_INDEX; + FWP_VALUE* pDirectionValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_DIRECTION); + + pInjectionData->direction = KrnlHlprFwpsLayerGetDirection(pClassifyValues->layerId); + + if(pFilter->subLayerWeight == FWPM_SUBLAYER_UNIVERSAL_WEIGHT) + index = UNIVERSAL_INDEX; + + if(pDirectionValue && + pDirectionValue->type == FWP_UINT32) + pInjectionData->direction = (FWP_DIRECTION)pDirectionValue->uint32; + else + { + if(pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V4 || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V4_DISCARD || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V6 || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V6_DISCARD) + { + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadataValues, + FWPS_METADATA_FIELD_FORWARD_LAYER_INBOUND_PASS_THRU)) + pInjectionData->direction = FWP_DIRECTION_INBOUND; + else if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadataValues, + FWPS_METADATA_FIELD_FORWARD_LAYER_OUTBOUND_PASS_THRU)) + pInjectionData->direction = FWP_DIRECTION_OUTBOUND; + } + else + { + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadataValues, + FWPS_METADATA_FIELD_PACKET_DIRECTION)) + pInjectionData->direction = pMetadataValues->packetDirection; + } + } + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadataValues, + FWPS_METADATA_FIELD_TRANSPORT_CONTROL_DATA)) + { + if(pMetadataValues->controlData && + pMetadataValues->controlDataLength) + { + HLPR_NEW_ARRAY(pInjectionData->pControlData, + BYTE, + pMetadataValues->controlDataLength, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pInjectionData->pControlData, + status); + + RtlCopyMemory(pInjectionData->pControlData, + pMetadataValues->controlData, + pMetadataValues->controlDataLength); + + pInjectionData->controlDataLength = pMetadataValues->controlDataLength; + } + } + + switch(pClassifyValues->layerId) + { + case FWPS_LAYER_INBOUND_IPPACKET_V4: + { + pInjectionData->addressFamily = AF_INET; + pInjectionData->injectionHandle = g_pIPv4InboundNetworkInjectionHandles[index]; + + break; + } + case FWPS_LAYER_INBOUND_IPPACKET_V6: + { + pInjectionData->addressFamily = AF_INET6; + pInjectionData->injectionHandle = g_pIPv6InboundNetworkInjectionHandles[index]; + + break; + } + case FWPS_LAYER_OUTBOUND_IPPACKET_V4: + { + pInjectionData->addressFamily = AF_INET; + pInjectionData->injectionHandle = g_pIPv4OutboundNetworkInjectionHandles[index]; + + break; + } + case FWPS_LAYER_OUTBOUND_IPPACKET_V6: + { + pInjectionData->addressFamily = AF_INET6; + pInjectionData->injectionHandle = g_pIPv6OutboundNetworkInjectionHandles[index]; + + break; + } + case FWPS_LAYER_IPFORWARD_V4: + { + pInjectionData->addressFamily = AF_INET; + + if(pInjectionData->direction == FWP_DIRECTION_OUTBOUND) + pInjectionData->injectionHandle = g_pIPv4OutboundForwardInjectionHandles[index]; + else + pInjectionData->injectionHandle = g_pIPv4InboundForwardInjectionHandles[index]; + + break; + } + case FWPS_LAYER_IPFORWARD_V6: + { + pInjectionData->addressFamily = AF_INET6; + + if(pInjectionData->direction == FWP_DIRECTION_OUTBOUND) + pInjectionData->injectionHandle = g_pIPv6OutboundForwardInjectionHandles[index]; + else + pInjectionData->injectionHandle = g_pIPv6InboundForwardInjectionHandles[index]; + + break; + } + case FWPS_LAYER_INBOUND_TRANSPORT_V4: + { + pInjectionData->addressFamily = AF_INET; + pInjectionData->injectionHandle = g_pIPv4InboundTransportInjectionHandles[index]; + + break; + } + case FWPS_LAYER_INBOUND_TRANSPORT_V6: + { + pInjectionData->addressFamily = AF_INET6; + pInjectionData->injectionHandle = g_pIPv6InboundTransportInjectionHandles[index]; + + break; + } + case FWPS_LAYER_OUTBOUND_TRANSPORT_V4: + { + pInjectionData->addressFamily = AF_INET; + pInjectionData->injectionHandle = g_pIPv4OutboundTransportInjectionHandles[index]; + + break; + } + case FWPS_LAYER_OUTBOUND_TRANSPORT_V6: + { + pInjectionData->addressFamily = AF_INET6; + pInjectionData->injectionHandle = g_pIPv6OutboundTransportInjectionHandles[index]; + + break; + } + case FWPS_LAYER_STREAM_V4: + { + pInjectionData->addressFamily = AF_INET; + + if(pInjectionData->direction == FWP_DIRECTION_OUTBOUND) + pInjectionData->injectionHandle = g_pIPv4OutboundStreamInjectionHandles[index]; + else + pInjectionData->injectionHandle = g_pIPv4InboundStreamInjectionHandles[index]; + + break; + } + case FWPS_LAYER_STREAM_V6: + { + pInjectionData->addressFamily = AF_INET6; + + if(pInjectionData->direction == FWP_DIRECTION_OUTBOUND) + pInjectionData->injectionHandle = g_pIPv6OutboundStreamInjectionHandles[index]; + else + pInjectionData->injectionHandle = g_pIPv6InboundStreamInjectionHandles[index]; + + break; + } + case FWPS_LAYER_DATAGRAM_DATA_V4: + { + pInjectionData->addressFamily = AF_INET; + + if(pInjectionData->direction == FWP_DIRECTION_OUTBOUND) + pInjectionData->injectionHandle = g_pIPv4OutboundTransportInjectionHandles[index]; + else + pInjectionData->injectionHandle = g_pIPv4InboundTransportInjectionHandles[index]; + + break; + } + case FWPS_LAYER_DATAGRAM_DATA_V6: + { + pInjectionData->addressFamily = AF_INET6; + + if(pInjectionData->direction == FWP_DIRECTION_OUTBOUND) + pInjectionData->injectionHandle = g_pIPv6OutboundTransportInjectionHandles[index]; + else + pInjectionData->injectionHandle = g_pIPv6InboundTransportInjectionHandles[index]; + + break; + } + case FWPS_LAYER_INBOUND_ICMP_ERROR_V4: + { + pInjectionData->addressFamily = AF_INET; + pInjectionData->injectionHandle = g_pIPv4InboundTransportInjectionHandles[index]; + + break; + } + case FWPS_LAYER_INBOUND_ICMP_ERROR_V6: + { + pInjectionData->addressFamily = AF_INET6; + pInjectionData->injectionHandle = g_pIPv6InboundTransportInjectionHandles[index]; + + break; + } + case FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4: + { + pInjectionData->addressFamily = AF_INET; + pInjectionData->injectionHandle = g_pIPv4OutboundTransportInjectionHandles[index]; + + break; + } + case FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6: + { + pInjectionData->addressFamily = AF_INET6; + pInjectionData->injectionHandle = g_pIPv6OutboundTransportInjectionHandles[index]; + + break; + } + case FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4: + { + pInjectionData->addressFamily = AF_INET; + + if(pInjectionData->direction == FWP_DIRECTION_OUTBOUND) + pInjectionData->injectionHandle = g_pIPv4OutboundTransportInjectionHandles[index]; + else + pInjectionData->injectionHandle = g_pIPv4InboundTransportInjectionHandles[index]; + + break; + } + case FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6: + { + pInjectionData->addressFamily = AF_INET6; + + if(pInjectionData->direction == FWP_DIRECTION_OUTBOUND) + pInjectionData->injectionHandle = g_pIPv6OutboundTransportInjectionHandles[index]; + else + pInjectionData->injectionHandle = g_pIPv6InboundTransportInjectionHandles[index]; + + break; + } + case FWPS_LAYER_ALE_AUTH_CONNECT_V4: + { + pInjectionData->addressFamily = AF_INET; + + if(pInjectionData->direction == FWP_DIRECTION_INBOUND) + pInjectionData->injectionHandle = g_pIPv4InboundTransportInjectionHandles[index]; + else + pInjectionData->injectionHandle = g_pIPv4OutboundTransportInjectionHandles[index]; + + break; + } + case FWPS_LAYER_ALE_AUTH_CONNECT_V6: + { + pInjectionData->addressFamily = AF_INET6; + + if(pInjectionData->direction == FWP_DIRECTION_INBOUND) + pInjectionData->injectionHandle = g_pIPv6InboundTransportInjectionHandles[index]; + else + pInjectionData->injectionHandle = g_pIPv6OutboundTransportInjectionHandles[index]; + + break; + } + case FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4: + { + pInjectionData->addressFamily = AF_INET; + + if(pInjectionData->direction == FWP_DIRECTION_OUTBOUND) + pInjectionData->injectionHandle = g_pIPv4OutboundTransportInjectionHandles[index]; + else + pInjectionData->injectionHandle = g_pIPv4InboundTransportInjectionHandles[index]; + + break; + } + case FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6: + { + pInjectionData->addressFamily = AF_INET6; + + if(pInjectionData->direction == FWP_DIRECTION_OUTBOUND) + pInjectionData->injectionHandle = g_pIPv6OutboundTransportInjectionHandles[index]; + else + pInjectionData->injectionHandle = g_pIPv6InboundTransportInjectionHandles[index]; + + break; + } + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + case FWPS_LAYER_STREAM_PACKET_V4: + { + pInjectionData->addressFamily = AF_INET; + + if(pInjectionData->direction == FWP_DIRECTION_OUTBOUND) + pInjectionData->injectionHandle = g_pIPv4OutboundTransportInjectionHandles[index]; + else + pInjectionData->injectionHandle = g_pIPv4InboundTransportInjectionHandles[index]; + + break; + } + case FWPS_LAYER_STREAM_PACKET_V6: + { + pInjectionData->addressFamily = AF_INET6; + + if(pInjectionData->direction == FWP_DIRECTION_OUTBOUND) + pInjectionData->injectionHandle = g_pIPv6OutboundTransportInjectionHandles[index]; + else + pInjectionData->injectionHandle = g_pIPv6InboundTransportInjectionHandles[index]; + + break; + } + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + case FWPS_LAYER_INBOUND_MAC_FRAME_ETHERNET: + { + UINT16 etherType = pClassifyValues->incomingValue[FWPS_FIELD_INBOUND_MAC_FRAME_ETHERNET_ETHER_TYPE].value.uint16; + + if(etherType == 0x86DD) + { + pInjectionData->addressFamily = AF_INET6; + pInjectionData->injectionHandle = g_pIPv6InboundMACInjectionHandles[index]; + } + else if(etherType == 0x800) + { + pInjectionData->addressFamily = AF_INET; + pInjectionData->injectionHandle = g_pIPv4InboundMACInjectionHandles[index]; + } + else + { + pInjectionData->addressFamily = AF_UNSPEC; + pInjectionData->injectionHandle = g_pInboundMACInjectionHandles[index]; + } + + break; + } + case FWPS_LAYER_OUTBOUND_MAC_FRAME_ETHERNET: + { + UINT16 etherType = pClassifyValues->incomingValue[FWPS_FIELD_OUTBOUND_MAC_FRAME_ETHERNET_ETHER_TYPE].value.uint16; + + if(etherType == 0x86DD) + { + pInjectionData->addressFamily = AF_INET6; + pInjectionData->injectionHandle = g_pIPv6OutboundMACInjectionHandles[index]; + } + else if(etherType == 0x800) + { + pInjectionData->addressFamily = AF_INET; + pInjectionData->injectionHandle = g_pIPv4OutboundMACInjectionHandles[index]; + } + else + { + pInjectionData->addressFamily = AF_UNSPEC; + pInjectionData->injectionHandle = g_pOutboundMACInjectionHandles[index]; + } + + break; + } + case FWPS_LAYER_INBOUND_MAC_FRAME_NATIVE: + { + pInjectionData->addressFamily = AF_UNSPEC; + pInjectionData->injectionHandle = g_pInboundMACInjectionHandles[index]; + + break; + } + case FWPS_LAYER_OUTBOUND_MAC_FRAME_NATIVE: + { + pInjectionData->addressFamily = AF_UNSPEC; + pInjectionData->injectionHandle = g_pOutboundMACInjectionHandles[index]; + + break; + } + case FWPS_LAYER_INGRESS_VSWITCH_ETHERNET: + { + UINT16 etherType = pClassifyValues->incomingValue[FWPS_FIELD_INGRESS_VSWITCH_ETHERNET_ETHER_TYPE].value.uint16; + + if(etherType == 0x86DD) + { + pInjectionData->addressFamily = AF_INET6; + pInjectionData->injectionHandle = g_pIPv6IngressVSwitchEthernetInjectionHandles[index]; + } + else if(etherType == 0x800) + { + pInjectionData->addressFamily = AF_INET; + pInjectionData->injectionHandle = g_pIPv4IngressVSwitchEthernetInjectionHandles[index]; + } + else + { + pInjectionData->addressFamily = AF_UNSPEC; + pInjectionData->injectionHandle = g_pIngressVSwitchEthernetInjectionHandles[index]; + } + + break; + } + case FWPS_LAYER_EGRESS_VSWITCH_ETHERNET: + { + UINT16 etherType = pClassifyValues->incomingValue[FWPS_FIELD_EGRESS_VSWITCH_ETHERNET_ETHER_TYPE].value.uint16; + + if(etherType == 0x86DD) + { + pInjectionData->addressFamily = AF_INET6; + pInjectionData->injectionHandle = g_pIPv6EgressVSwitchEthernetInjectionHandles[index]; + } + else if(etherType == 0x800) + { + pInjectionData->addressFamily = AF_INET; + pInjectionData->injectionHandle = g_pIPv4EgressVSwitchEthernetInjectionHandles[index]; + } + else + { + pInjectionData->addressFamily = AF_UNSPEC; + pInjectionData->injectionHandle = g_pEgressVSwitchEthernetInjectionHandles[index]; + } + + break; + } + +#endif // (NTDDI_VERSION >= NTDDI_WIN8) +#endif // (NTDDI_VERSION >= NTDDI_WIN7) + + default: + status = STATUS_NOT_SUPPORTED; + } + + if(pInjectionData->injectionHandle && + pNetBufferList && + pClassifyValues->layerId != FWPS_LAYER_STREAM_V4 && + pClassifyValues->layerId != FWPS_LAYER_STREAM_V6) + pInjectionData->injectionState = FwpsQueryPacketInjectionState(pInjectionData->injectionHandle, + pNetBufferList, + &(pInjectionData->injectionContext)); + + HLPR_BAIL_LABEL: + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprInjectionDataPopulate() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @kernel_helper_function="KrnlHlprInjectionDataCreate" + + Purpose: Allocates and populates a INJECTION_DATA object with data based on values obtained + in the classifyFn.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_At_(*ppInjectionData, _Pre_ _Null_) +_When_(return != STATUS_SUCCESS, _At_(*ppInjectionData, _Post_ _Null_)) +_When_(return == STATUS_SUCCESS, _At_(*ppInjectionData, _Post_ _Notnull_ __drv_allocatesMem(Pool))) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprInjectionDataCreate(_Outptr_ INJECTION_DATA** ppInjectionData, + _In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadataValues, + _In_opt_ const NET_BUFFER_LIST* pNetBufferList, + _In_ const FWPS_FILTER* pFilter) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprInjectionDataCreate()\n"); + +#endif /// DBG + + NT_ASSERT(ppInjectionData); + NT_ASSERT(pClassifyValues); + NT_ASSERT(pMetadataValues); + NT_ASSERT(pFilter); + + NTSTATUS status = STATUS_SUCCESS; + + HLPR_NEW(*ppInjectionData, + INJECTION_DATA, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(*ppInjectionData, + status); + + status = KrnlHlprInjectionDataPopulate(*ppInjectionData, + pClassifyValues, + pMetadataValues, + pNetBufferList, + pFilter); + + HLPR_BAIL_LABEL: + +#pragma warning(push) +#pragma warning(disable: 6001) /// *ppInjectionData initialized with call to HLPR_NEW & KrnlHlprInjectionDataPopulate + + if(status != STATUS_SUCCESS && + *ppInjectionData) + KrnlHlprInjectionDataDestroy(ppInjectionData); + +#pragma warning(pop) + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprInjectionDataCreate() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +#endif /// INJECTION_DATA____ + +#ifndef INJECTION_HANDLE_DATA____ +#define INJECTION_HANDLE_DATA____ + +/** + @kernel_helper_function="KrnlHlprInjectionHandleDataDestroy" + + Purpose: Cleanup and free a INJECTION_HANDLE_DATA object.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_At_(*ppInjectionHandleData, _Pre_ _Notnull_) +_At_(*ppInjectionHandleData, _Post_ _Null_ __drv_freesMem(Pool)) +_IRQL_requires_(PASSIVE_LEVEL) +_IRQL_requires_same_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprInjectionHandleDataDestroy(_Inout_ INJECTION_HANDLE_DATA** ppInjectionHandleData) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprInjectionHandleDataDestroy()\n"); + +#endif /// DBG + + NT_ASSERT(ppInjectionHandleData); + + NTSTATUS status = STATUS_SUCCESS; + + if(*ppInjectionHandleData) + { + if((*ppInjectionHandleData)->pMACHandle) + { + status = KrnlHlprFwpsInjectionReleaseHandle((*ppInjectionHandleData)->pMACHandle); + HLPR_BAIL_ON_FAILURE(status); + } + + if((*ppInjectionHandleData)->pVSwitchEthernetHandle) + { + status = KrnlHlprFwpsInjectionReleaseHandle((*ppInjectionHandleData)->pVSwitchEthernetHandle); + HLPR_BAIL_ON_FAILURE(status); + } + + if((*ppInjectionHandleData)->pForwardHandle) + { + status = KrnlHlprFwpsInjectionReleaseHandle((*ppInjectionHandleData)->pForwardHandle); + HLPR_BAIL_ON_FAILURE(status); + } + + if((*ppInjectionHandleData)->pNetworkHandle) + { + status = KrnlHlprFwpsInjectionReleaseHandle((*ppInjectionHandleData)->pNetworkHandle); + HLPR_BAIL_ON_FAILURE(status); + } + + if((*ppInjectionHandleData)->pTransportHandle) + { + status = KrnlHlprFwpsInjectionReleaseHandle((*ppInjectionHandleData)->pTransportHandle); + HLPR_BAIL_ON_FAILURE(status); + } + + if((*ppInjectionHandleData)->pStreamHandle) + { + status = KrnlHlprFwpsInjectionReleaseHandle((*ppInjectionHandleData)->pStreamHandle); + HLPR_BAIL_ON_FAILURE(status); + } + + HLPR_DELETE(*ppInjectionHandleData, + WFPSAMPLER_SYSLIB_TAG); + } + + HLPR_BAIL_LABEL: + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprInjectionHandleDataDestroy() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @kernel_helper_function="KrnlHlprInjectionHandleDataCreate" + + Purpose: Allocates and populates a INJECTION_HANDLE_DATA object with the various injection + handles created when the driver loads.
+
+ Notes: The index is related to which sublayer the handle operates on. For + FWPM_SUBLAYER_UNIVERSAL, the index is 1, otherwise it's 0.
+
+ MSDN_Ref:
+*/ +_At_(*ppInjectionHandleData, _Pre_ _Null_) +_When_(return != STATUS_SUCCESS, _At_(*ppInjectionHandleData, _Post_ _Null_)) +_When_(return == STATUS_SUCCESS, _At_(*ppInjectionHandleData, _Post_ _Notnull_ __drv_allocatesMem(Pool))) +_IRQL_requires_(PASSIVE_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprInjectionHandleDataCreate(_Outptr_ INJECTION_HANDLE_DATA** ppInjectionHandleData, + _In_ ADDRESS_FAMILY addressFamily, /* AF_INET */ + _In_ BOOLEAN isInbound, /* TRUE */ + _In_ UINT32 index) /* WFPSAMPLER_INDEX */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprInjectionHandleDataCreate()\n"); + +#endif /// DBG + + NT_ASSERT(ppInjectionHandleData); + + NTSTATUS status = STATUS_SUCCESS; + + HLPR_NEW(*ppInjectionHandleData, + INJECTION_HANDLE_DATA, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(*ppInjectionHandleData, + status); + + if(addressFamily == AF_INET) + { + if(isInbound) + { + (*ppInjectionHandleData)->pForwardHandle = &(g_pIPv4InboundForwardInjectionHandles[index]); + (*ppInjectionHandleData)->pNetworkHandle = &(g_pIPv4InboundNetworkInjectionHandles[index]); + (*ppInjectionHandleData)->pTransportHandle = &(g_pIPv4InboundTransportInjectionHandles[index]); + (*ppInjectionHandleData)->pStreamHandle = &(g_pIPv4InboundStreamInjectionHandles[index]); + (*ppInjectionHandleData)->pMACHandle = &(g_pIPv4InboundMACInjectionHandles[index]); + (*ppInjectionHandleData)->pVSwitchEthernetHandle = &(g_pIPv4IngressVSwitchEthernetInjectionHandles[index]); + } + else + { + (*ppInjectionHandleData)->pForwardHandle = &(g_pIPv4OutboundForwardInjectionHandles[index]); + (*ppInjectionHandleData)->pNetworkHandle = &(g_pIPv4OutboundNetworkInjectionHandles[index]); + (*ppInjectionHandleData)->pTransportHandle = &(g_pIPv4OutboundTransportInjectionHandles[index]); + (*ppInjectionHandleData)->pStreamHandle = &(g_pIPv4OutboundStreamInjectionHandles[index]); + (*ppInjectionHandleData)->pMACHandle = &(g_pIPv4OutboundMACInjectionHandles[index]); + (*ppInjectionHandleData)->pVSwitchEthernetHandle = &(g_pIPv4EgressVSwitchEthernetInjectionHandles[index]); + } + } + else if(addressFamily == AF_INET6) + { + if(isInbound) + { + (*ppInjectionHandleData)->pForwardHandle = &(g_pIPv6InboundForwardInjectionHandles[index]); + (*ppInjectionHandleData)->pNetworkHandle = &(g_pIPv6InboundNetworkInjectionHandles[index]); + (*ppInjectionHandleData)->pTransportHandle = &(g_pIPv6InboundTransportInjectionHandles[index]); + (*ppInjectionHandleData)->pStreamHandle = &(g_pIPv6InboundStreamInjectionHandles[index]); + (*ppInjectionHandleData)->pMACHandle = &(g_pIPv6InboundMACInjectionHandles[index]); + (*ppInjectionHandleData)->pVSwitchEthernetHandle = &(g_pIPv6IngressVSwitchEthernetInjectionHandles[index]); + } + else + { + (*ppInjectionHandleData)->pForwardHandle = &(g_pIPv6OutboundForwardInjectionHandles[index]); + (*ppInjectionHandleData)->pNetworkHandle = &(g_pIPv6OutboundNetworkInjectionHandles[index]); + (*ppInjectionHandleData)->pTransportHandle = &(g_pIPv6OutboundTransportInjectionHandles[index]); + (*ppInjectionHandleData)->pStreamHandle = &(g_pIPv6OutboundStreamInjectionHandles[index]); + (*ppInjectionHandleData)->pMACHandle = &(g_pIPv6OutboundMACInjectionHandles[index]); + (*ppInjectionHandleData)->pVSwitchEthernetHandle = &(g_pIPv6EgressVSwitchEthernetInjectionHandles[index]); + } + } + else + { + if(isInbound) + { + (*ppInjectionHandleData)->pForwardHandle = &(g_pInboundForwardInjectionHandles[index]); + (*ppInjectionHandleData)->pNetworkHandle = &(g_pInboundNetworkInjectionHandles[index]); + (*ppInjectionHandleData)->pTransportHandle = &(g_pInboundTransportInjectionHandles[index]); + (*ppInjectionHandleData)->pStreamHandle = &(g_pInboundStreamInjectionHandles[index]); + (*ppInjectionHandleData)->pMACHandle = &(g_pInboundMACInjectionHandles[index]); + (*ppInjectionHandleData)->pVSwitchEthernetHandle = &(g_pIngressVSwitchEthernetInjectionHandles[index]); + } + else + { + (*ppInjectionHandleData)->pForwardHandle = &(g_pOutboundForwardInjectionHandles[index]); + (*ppInjectionHandleData)->pNetworkHandle = &(g_pOutboundNetworkInjectionHandles[index]); + (*ppInjectionHandleData)->pTransportHandle = &(g_pOutboundTransportInjectionHandles[index]); + (*ppInjectionHandleData)->pStreamHandle = &(g_pOutboundStreamInjectionHandles[index]); + (*ppInjectionHandleData)->pMACHandle = &(g_pOutboundMACInjectionHandles[index]); + (*ppInjectionHandleData)->pVSwitchEthernetHandle = &(g_pEgressVSwitchEthernetInjectionHandles[index]); + } + } + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + status = KrnlHlprFwpsInjectionAcquireHandle((*ppInjectionHandleData)->pMACHandle, + addressFamily, + FWPS_INJECTION_TYPE_L2); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! KrnlHlprInjectionHandleDataCreate : KrnlHlprFwpsInjectionAcquireHandle() [type: FWPS_INJECTION_TYPE_L2][status: %#x]\n", + status); + + HLPR_BAIL; + } + + status = KrnlHlprFwpsInjectionAcquireHandle((*ppInjectionHandleData)->pVSwitchEthernetHandle, + addressFamily, + FWPS_INJECTION_TYPE_L2); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! KrnlHlprInjectionHandleDataCreate : KrnlHlprFwpsInjectionAcquireHandle() [type: FWPS_INJECTION_TYPE_L2][status: %#x]\n", + status); + + HLPR_BAIL; + } + +#endif // (NTDDI_VERSION >= NTDDI_WIN8) + + status = KrnlHlprFwpsInjectionAcquireHandle((*ppInjectionHandleData)->pForwardHandle, + addressFamily, + FWPS_INJECTION_TYPE_FORWARD); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! KrnlHlprInjectionHandleDataCreate : KrnlHlprFwpsInjectionAcquireHandle() [type: FWPS_INJECTION_TYPE_FORWARD][status: %#x]\n", + status); + + HLPR_BAIL; + } + + status = KrnlHlprFwpsInjectionAcquireHandle((*ppInjectionHandleData)->pNetworkHandle, + addressFamily ? addressFamily : AF_INET, + FWPS_INJECTION_TYPE_NETWORK); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! KrnlHlprInjectionHandleDataCreate : KrnlHlprFwpsInjectionAcquireHandle() [type: FWPS_INJECTION_TYPE_NETWORK][status: %#x]\n", + status); + + HLPR_BAIL; + } + + status = KrnlHlprFwpsInjectionAcquireHandle((*ppInjectionHandleData)->pTransportHandle, + addressFamily, + FWPS_INJECTION_TYPE_TRANSPORT); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! KrnlHlprInjectionHandleDataCreate : KrnlHlprFwpsInjectionAcquireHandle() [type: FWPS_INJECTION_TYPE_TRANSPORT][status: %#x]\n", + status); + + HLPR_BAIL; + } + + status = KrnlHlprFwpsInjectionAcquireHandle((*ppInjectionHandleData)->pStreamHandle, + addressFamily, + FWPS_INJECTION_TYPE_STREAM); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! KrnlHlprInjectionHandleDataCreate : KrnlHlprFwpsInjectionAcquireHandle() [type: FWPS_INJECTION_TYPE_STREAM][status: %#x]\n", + status); + + HLPR_BAIL; + } + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS && + *ppInjectionHandleData) + KrnlHlprInjectionHandleDataDestroy(ppInjectionHandleData); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprInjectionHandleDataCreate() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +#endif /// INJECTION_HANDLE_DATA____ diff --git a/network/trans/WFPSampler/syslib/HelperFunctions_InjectionData.h b/network/trans/WFPSampler/syslib/HelperFunctions_InjectionData.h new file mode 100644 index 000000000..d3d5d7953 --- /dev/null +++ b/network/trans/WFPSampler/syslib/HelperFunctions_InjectionData.h @@ -0,0 +1,105 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_InjectionData.h +// +// Abstract: +// This module contains prototypes for kernel helper functions that assist with INJECTION_DATA. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Enhance annotations, add multiple injector support, and +// add support for controlData. +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef HELPERFUNCTIONS_INJECTION_DATA_H +#define HELPERFUNCTIONS_INJECTION_DATA_H + +typedef struct INJECTION_DATA_ +{ + ADDRESS_FAMILY addressFamily; + FWP_DIRECTION direction; + BOOLEAN isIPsecSecured; + HANDLE injectionHandle; + HANDLE injectionContext; + FWPS_PACKET_INJECTION_STATE injectionState; + VOID* pContext; + BYTE* pControlData; + UINT32 controlDataLength; +}INJECTION_DATA, *PINJECTION_DATA; + +typedef struct INJECTION_HANDLE_DATA_ +{ + HANDLE* pMACHandle; + HANDLE* pVSwitchEthernetHandle; + HANDLE* pForwardHandle; + HANDLE* pNetworkHandle; + HANDLE* pTransportHandle; + HANDLE* pStreamHandle; +}INJECTION_HANDLE_DATA, *PINJECTION_HANDLE_DATA; + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprInjectionDataPurge(_Inout_ INJECTION_DATA* pInjectionData); + +_At_(*ppInjectionData, _Pre_ _Notnull_) +_At_(*ppInjectionData, _Post_ _Null_ __drv_freesMem(Pool)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(*ppInjectionData == 0) +inline VOID KrnlHlprInjectionDataDestroy(_Inout_ INJECTION_DATA** ppInjectionData); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprInjectionDataPopulate(_Inout_ INJECTION_DATA* pInjectionData, + _In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES0* pMetadataValues, + _In_opt_ const NET_BUFFER_LIST* pNetBufferList); + +_At_(*ppInjectionData, _Pre_ _Null_) +_When_(return != STATUS_SUCCESS, _At_(*ppInjectionData, _Post_ _Null_)) +_When_(return == STATUS_SUCCESS, _At_(*ppInjectionData, _Post_ _Notnull_ __drv_allocatesMem(Pool))) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprInjectionDataCreate(_Outptr_ INJECTION_DATA** ppInjectionData, + _In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES0* pMetadataValues, + _In_opt_ const NET_BUFFER_LIST* pNetBufferList, + _In_ const FWPS_FILTER* pFilter); + +_At_(*ppInjectionHandleData, _Pre_ _Notnull_) +_At_(*ppInjectionHandleData, _Post_ _Null_ __drv_freesMem(Pool)) +_IRQL_requires_(PASSIVE_LEVEL) +_IRQL_requires_same_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprInjectionHandleDataDestroy(_Inout_ INJECTION_HANDLE_DATA** ppInjectionHandleData); + +_At_(*ppInjectionHandleData, _Pre_ _Null_) +_When_(return != STATUS_SUCCESS, _At_(*ppInjectionHandleData, _Post_ _Null_)) +_When_(return == STATUS_SUCCESS, _At_(*ppInjectionHandleData, _Post_ _Notnull_ __drv_allocatesMem(Pool))) +_IRQL_requires_(PASSIVE_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprInjectionHandleDataCreate(_Outptr_ INJECTION_HANDLE_DATA** ppInjectionHandleData, + _In_ ADDRESS_FAMILY addressFamily = AF_INET, + _In_ BOOLEAN isInbound = TRUE, + _In_ UINT32 index = WFPSAMPLER_INDEX); + +#endif /// HELPERFUNCTIONS_INJECTION_DATA_H \ No newline at end of file diff --git a/network/trans/WFPSampler/syslib/HelperFunctions_Macros.h b/network/trans/WFPSampler/syslib/HelperFunctions_Macros.h new file mode 100644 index 000000000..7f2184152 --- /dev/null +++ b/network/trans/WFPSampler/syslib/HelperFunctions_Macros.h @@ -0,0 +1,454 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_Macros.h +// +// Abstract: +// This module contains definitions for the various macros used throughout this driver. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Add support for multiple injectors and WPP tracing. +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef HELPERFUNCTIONS_MACROS_H +#define HELPERFUNCTIONS_MACROS_H + +extern "C" +{ + #pragma warning(push) + #pragma warning(disable: 4201) /// NAMELESS_STRUCT_UNION + #pragma warning(disable: 4324) /// STRUCTURE_PADDED + + #include /// IfsKit\Inc + #include /// Inc + #include /// Inc\WDF\KMDF\1.9 + #include /// Inc + #include /// Inc + #include /// Inc + #include /// Inc + #include /// Inc + #include /// Inc + #include /// SDK\Inc\CRT + + #pragma warning(pop) +} + +#include "Identifiers.h" /// ..\Inc +#include "ProviderContexts.h" /// ..\Inc + +/// Macros + +#define WFPSAMPLER_SYSLIB_TAG (UINT32)'LSSW' +#define WFPSAMPLER_NDIS_POOL_TAG (UINT32)'PNSW' +#define WFPSAMPLER_CALLOUT_DRIVER_TAG (UINT32)'DCSW' + +#define WFPSAMPLER_INDEX 0 +#define UNIVERSAL_INDEX 1 +#define MAX_INDEX 2 + +#define FWPM_SUBLAYER_UNIVERSAL_WEIGHT 0x8000 + +#define WPP_COMPID_LEVEL_LOGGER(COMPID,LEVEL) (WPP_CONTROL(WPP_BIT_Error).Logger), +#define WPP_COMPID_LEVEL_ENABLED(COMPID,LEVEL) (WPP_CONTROL(WPP_BIT_Error).Level >= LEVEL) + +#define SID_LENGTH(pSID) \ + (8 + (4 * ((SID*)pSID)->SubAuthorityCount)) + +/** + @macro="htonl" + + Purpose: Convert ULONG in Host Byte Order to Network Byte Order.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +#define htonl(l) \ + ((((l) & 0xFF000000) >> 24) | \ + (((l) & 0x00FF0000) >> 8) | \ + (((l) & 0x0000FF00) << 8) | \ + (((l) & 0x000000FF) << 24)) + +/** + @macro="htons" + + Purpose: Convert USHORT in Host Byte Order to Network Byte Order.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +#define htons(s) \ + ((((s) >> 8) & 0x00FF) | \ + (((s) << 8) & 0xFF00)) + +/** + @macro="ntohl" + + Purpose: Convert ULONG in Network Byte Order to Host Byte Order.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +#define ntohl(l) \ + ((((l) >> 24) & 0x000000FFL) | \ + (((l) >> 8) & 0x0000FF00L) | \ + (((l) << 8) & 0x00FF0000L) | \ + (((l) << 24) & 0xFF000000L)) + +/** + @macro="ntohs" + + Purpose: Convert USHORT in Network Byte Order to Host Byte Order.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +#define ntohs(s) \ + ((USHORT)((((s) & 0x00ff) << 8) | \ + (((s) & 0xff00) >> 8))) + + +/** + @macro="HLPR_CLOSE_HANDLE" + + Purpose: Close a standard handle and set to 0.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +#define HLPR_CLOSE_HANDLE(handle)\ + if(handle) \ + { \ + CloseHandle(handle); \ + handle = 0; \ + } + +/** + @macro="HLPR_REG_CLOSE_KEY" + + Purpose: Close a registry handle and set to 0.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +#define HLPR_REG_CLOSE_KEY(keyHandle)\ + if(keyHandle) \ + { \ + RegCloseKey(keyHandle); \ + keyHandle = 0; \ + } + +/** + @macro="HLPR_DELETE" + + Purpose: Free memory allocated with ExAllocatePoolWithTag and set the pointer to 0.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +#define HLPR_DELETE(pPtr, tag) \ + if(pPtr) \ + { \ + ExFreePoolWithTag((VOID*)pPtr, \ + tag); \ + pPtr = 0; \ + } + +/** + @macro="HLPR_DELETE_ARRAY" + + Purpose: Free memory allocated with ExAllocatePoolWithTag and set the pointer to 0.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +#define HLPR_DELETE_ARRAY(pPtr, tag) \ + HLPR_DELETE(pPtr, tag) + +/** + @macro="HLPR_NEW" + + Purpose: Allocate memory from NonPaged Pool with ExAllocatePoolWithTag and initialize it's + contents with 0's.
+
+ Notes: Caller responsible for freeing allocated memory using macro HLPR_DELETE.
+
+ MSDN_Ref:
+*/ +#define HLPR_NEW(pPtr, object, tag) \ + for(; \ + pPtr == 0; \ + ) \ + { \ + pPtr = (object*)ExAllocatePoolWithTag(NonPagedPoolNx, \ + sizeof(object), \ + tag); \ + if(pPtr) \ + RtlSecureZeroMemory(pPtr, \ + sizeof(object)); \ + } + +/** + @macro="HLPR_NEW_ARRAY" + + Purpose: Allocate memory from NonPaged Pool with ExAllocatePoolWithTag and initialize it's + contents with 0's.
+
+ Notes: Caller responsible for freeing allocated memory using macro HLPR_DELETE_ARRAY.
+
+ MSDN_Ref:
+*/ +#define HLPR_NEW_ARRAY(pPtr, object, count, tag) \ + for(; \ + pPtr == 0; \ + ) \ + { \ + size_t SAFE_SIZE = 0; \ + if(RtlSizeTMult(sizeof(object), \ + (size_t)count, \ + &SAFE_SIZE) == STATUS_SUCCESS && \ + SAFE_SIZE >= (sizeof(object) * count)) \ + { \ + pPtr = (object*)ExAllocatePoolWithTag(NonPagedPoolNx, \ + SAFE_SIZE, \ + tag); \ + if(pPtr) \ + RtlZeroMemory(pPtr, \ + SAFE_SIZE); \ + } \ + else \ + break; \ + } + +/** + @macro="HLPR_NEW_CASTED_ARRAY" + + Purpose: Allocate memory from NonPaged Pool with ExAllocatePoolWithTag and initialize it's + contents with 0's.
+
+ Notes: Caller responsible for freeing allocated memory using macro HLPR_DELETE_ARRAY.
+
+ MSDN_Ref:
+*/ +#define HLPR_NEW_CASTED_ARRAY(pPtr, CAST_TYPE, object, count, tag) \ + for(; \ + pPtr == 0; \ + ) \ + { \ + size_t SAFE_SIZE = 0; \ + if(RtlSizeTMult(sizeof(object), \ + (size_t)count, \ + &SAFE_SIZE) == STATUS_SUCCESS && \ + SAFE_SIZE >= (sizeof(object) * count)) \ + { \ + pPtr = (CAST_TYPE*)ExAllocatePoolWithTag(NonPagedPoolNx, \ + SAFE_SIZE, \ + tag); \ + if(pPtr) \ + RtlZeroMemory(pPtr, \ + SAFE_SIZE); \ + } \ + else \ + break; \ + } + +/** + @macro="HLPR_NEW" + + Purpose: Allocate memory from NonPaged Pool with ExAllocatePoolWithTag and leave it's + contents as is.
+
+ Notes: Caller responsible for freeing allocated memory using macro HLPR_DELETE.
+
+ MSDN_Ref:
+*/ +#define HLPR_NEW_POPULATED(pPtr, object, tag) \ + for(; \ + pPtr == 0; \ + ) \ + { \ + pPtr = (object*)ExAllocatePoolWithTag(NonPagedPoolNx, \ + sizeof(object), \ + tag); \ + } + +/** + @macro="HLPR_BAIL_LABEL" + + Purpose: Tag for the cleanup and exit portion of the function.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +#define HLPR_BAIL_LABEL \ + cleanup + +/** + @macro="HLPR_BAIL_LABEL" + + Purpose: Tag for the cleanup and exit portion of the function.
+
+ Notes: Used when there can be more than 1 jump in code.
+
+ MSDN_Ref:
+*/ +#define HLPR_BAIL_LABEL_2 \ + cleanup_2 + +/** + @macro="HLPR_BAIL_ON_FAILURE_WITH_LABEL" + + Purpose: Jump in the code's execution to the provided label if an error occurs.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +#define HLPR_BAIL_ON_FAILURE_WITH_LABEL(status, label) \ + if(status != STATUS_SUCCESS) \ + goto label + + +#define HLPR_BAIL_ON_ALLOC_FAILURE_WITH_LABEL(pPtr, status, label) \ + if(pPtr == 0) \ + { \ + status = (UINT32)STATUS_NO_MEMORY; \ + goto label; \ + } + +/** + @macro="HLPR_BAIL_ON_FAILURE" + + Purpose: Jump in the code's execution path to the HLPR_BAIL_LABEL label if an error occurs.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +#define HLPR_BAIL_ON_FAILURE(status) \ + HLPR_BAIL_ON_FAILURE_WITH_LABEL(status, HLPR_BAIL_LABEL) + +/** + @macro="HLPR_BAIL_ON_FAILURE_2" + + Purpose: Jump in the code's execution path to the HLPR_BAIL_LABEL_2 label if an error + occurs.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +#define HLPR_BAIL_ON_FAILURE_2(status) \ + HLPR_BAIL_ON_FAILURE_WITH_LABEL(status, HLPR_BAIL_LABEL_2) + +/** + @macro="HLPR_BAIL_ON_ALLOC_FAILURE" + + Purpose: Jump in the code's execution path to the HLPR_BAIL_LABEL label if memory allocation + fails.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +#define HLPR_BAIL_ON_ALLOC_FAILURE(pPtr, status) \ + HLPR_BAIL_ON_ALLOC_FAILURE_WITH_LABEL(pPtr, status, HLPR_BAIL_LABEL) + +/** + @macro="HLPR_BAIL_ON_ALLOC_FAILURE_2" + + Purpose: Jump in the code's execution path to the HLPR_BAIL_LABEL_2 label if memory + allocation fails.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +#define HLPR_BAIL_ON_ALLOC_FAILURE_2(pPtr, status) \ + HLPR_BAIL_ON_ALLOC_FAILURE_WITH_LABEL(pPtr, status, HLPR_BAIL_LABEL_2) + +/** + @macro="HLPR_BAIL_WITH_LABEL" + + Purpose: Jump in the code's execution path to the provided label.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +#define HLPR_BAIL_WITH_LABEL(label) \ + goto label + +/** + @macro="HLPR_BAIL" + + Purpose: Jump in the code's execution path to the HLPR_BAIL_LABEL label.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +#define HLPR_BAIL \ + HLPR_BAIL_WITH_LABEL(HLPR_BAIL_LABEL) + +/** + @macro="HLPR_BAIL_2" + + Purpose: Jump in the code's execution path to the HLPR_BAIL_LABEL_2 label.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +#define HLPR_BAIL_2 \ + HLPR_BAIL_WITH_LABEL(HLPR_BAIL_LABEL_2) + +/** + @macro="HLPR_BAIL_ON_NULL_POINTER" + + Purpose: Jump in the code's execution path to the HLPR_BAIL_LABEL label if the pointer is + NULL.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +#define HLPR_BAIL_ON_NULL_POINTER(pPtr) \ + if(pPtr == 0) \ + HLPR_BAIL + +/** + @macro="HLPR_BAIL_ON_NULL_POINTER_WITH_STATUS" + + Purpose: Jump in the code's execution path to the HLPR_BAIL_LABEL label if the pointer is + NULL.
+
+ Notes: Status is set to STATUS_INVALID_ADDRESS.
+
+ MSDN_Ref:
+*/ +#define HLPR_BAIL_ON_NULL_POINTER_WITH_STATUS(pPtr, status) \ + if(pPtr == 0) \ + { \ + status = STATUS_INVALID_ADDRESS; \ + HLPR_BAIL; \ + } + +#endif /// HELPERFUNCTIONS_MACROS_H diff --git a/network/trans/WFPSampler/syslib/HelperFunctions_NDIS.cpp b/network/trans/WFPSampler/syslib/HelperFunctions_NDIS.cpp new file mode 100644 index 000000000..b56c63c60 --- /dev/null +++ b/network/trans/WFPSampler/syslib/HelperFunctions_NDIS.cpp @@ -0,0 +1,326 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_NDIS.cpp +// +// Abstract: +// This module contains kernel helper functions that assist with NDIS operations. +// +// Naming Convention: +// +// +// +// i.e. +// +// KrnlHlprNDISPoolDataDestroy +// +// +// KrnlHlpr - Function is located in syslib\ and applies to kernel mode. +// +// NDISPoolData - Function pertains to NDIS_POOL_DATA. +// +// { +// Create - Function allocates and fills memory. +// Destroy - Function cleans up and frees memory. +// Populate - Function fills memory with values. +// Purge - Function cleans up values. +// } +// +// Private Functions: +// +// Public Functions: +// KrnlHlprPendDataDataCreate(), +// KrnlHlprPendDataDataDestroy(), +// KrnlHlprPendDataDataPopulate(), +// KrnlHlprPendDataDataPurge(), +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "HelperFunctions_Include.h" /// . +#include "HelperFunctions_NDIS.tmh" /// $(OBJ_PATH)\$(O)\ + +/** + @kernel_helper_function="KrnlHlprNDISPoolDataDestroy" + + Purpose: Cleanup a NDIS_POOL_DATA object.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/FF562592.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/FF562590.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/FF561850.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprNDISPoolDataPurge(_Inout_ NDIS_POOL_DATA* pNDISPoolData) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprNDISPoolDataPurge()\n"); + +#endif /// DBG + + NT_ASSERT(pNDISPoolData); + + if(pNDISPoolData->ndisHandle) + { + if(pNDISPoolData->nbPoolHandle) + { + NdisFreeNetBufferPool(pNDISPoolData->nbPoolHandle); + + pNDISPoolData->nbPoolHandle = 0; + } + + if(pNDISPoolData->nblPoolHandle) + { + NdisFreeNetBufferListPool(pNDISPoolData->nblPoolHandle); + + pNDISPoolData->nblPoolHandle = 0; + } + + NdisFreeGenericObject((PNDIS_GENERIC_OBJECT)(pNDISPoolData->ndisHandle)); + + pNDISPoolData->ndisHandle = 0; + } + + RtlZeroMemory(pNDISPoolData, + sizeof(NDIS_POOL_DATA)); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprNDISPoolDataPurge()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprNDISPoolDataDestroy" + + Purpose: Cleanup a NDIS_POOL_DATA object.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_At_(*ppNDISPoolData, _Pre_ _Notnull_) +_At_(*ppNDISPoolData, _Post_ _Null_ __drv_freesMem(Pool)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(*ppNDISPoolData == 0) +inline VOID KrnlHlprNDISPoolDataDestroy(_Inout_ NDIS_POOL_DATA** ppNDISPoolData) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprNDISPoolDataDestroy()\n"); + +#endif /// DBG + + NT_ASSERT(ppNDISPoolData); + + if(*ppNDISPoolData) + { + KrnlHlprNDISPoolDataPurge(*ppNDISPoolData); + + HLPR_DELETE(*ppNDISPoolData, + WFPSAMPLER_SYSLIB_TAG); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprNDISPoolDataDestroy()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprPendDataCreate" + + Purpose: Populates a NDIS_POOL_DATA object with the various NDIS Pools.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/FF561603.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/FF561611.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/FF561613.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprNDISPoolDataPopulate(_Inout_ NDIS_POOL_DATA* pNDISPoolData, + _In_opt_ UINT32 memoryTag) /* WFPSAMPLER_NDIS_POOL_TAG */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprNDISPoolDataPopulate()\n"); + +#endif /// DBG + + NT_ASSERT(pNDISPoolData); + + NTSTATUS status = STATUS_SUCCESS; + NET_BUFFER_LIST_POOL_PARAMETERS nblPoolParameters = {0}; + NET_BUFFER_POOL_PARAMETERS nbPoolParameters = {0}; + + pNDISPoolData->ndisHandle = NdisAllocateGenericObject(0, + memoryTag, + 0); + if(pNDISPoolData->ndisHandle == 0) + { + status = STATUS_INVALID_HANDLE; + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! KrnlHlprNDISPoolDataPopulate : NdisAllocateGenericObject() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + nblPoolParameters.Header.Type = NDIS_OBJECT_TYPE_DEFAULT; + nblPoolParameters.Header.Revision = NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1; + nblPoolParameters.Header.Size = NDIS_SIZEOF_NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1; + nblPoolParameters.fAllocateNetBuffer = TRUE; + nblPoolParameters.DataSize = 0; + nblPoolParameters.PoolTag = memoryTag; + + pNDISPoolData->nblPoolHandle = NdisAllocateNetBufferListPool(pNDISPoolData->ndisHandle, + &nblPoolParameters); + if(pNDISPoolData->nblPoolHandle == 0) + { + status = STATUS_INVALID_HANDLE; + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! KrnlHlprNDISPoolDataPopulate : NdisAllocateNetBufferListPool() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + nbPoolParameters.Header.Type = NDIS_OBJECT_TYPE_DEFAULT; + nbPoolParameters.Header.Revision = NET_BUFFER_POOL_PARAMETERS_REVISION_1; + nbPoolParameters.Header.Size = NDIS_SIZEOF_NET_BUFFER_POOL_PARAMETERS_REVISION_1; + nbPoolParameters.PoolTag = memoryTag; + nbPoolParameters.DataSize = 0; + + pNDISPoolData->nbPoolHandle = NdisAllocateNetBufferPool(pNDISPoolData->ndisHandle, + &nbPoolParameters); + if(pNDISPoolData->nbPoolHandle == 0) + { + status = STATUS_INVALID_HANDLE; + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! KrnlHlprNDISPoolDataPopulate : NdisAllocateNetBufferPool() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS) + KrnlHlprNDISPoolDataPurge(pNDISPoolData); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprNDISPoolDataPopulate()\n"); + +#endif /// DBG + + return status; +} + +/** + @kernel_helper_function="KrnlHlprPendDataCreate" + + Purpose: Allocates and populates a NDIS_POOL_DATA object with the various NDIS Pools.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_At_(*ppNDISPoolData, _Pre_ _Null_) +_When_(return != STATUS_SUCCESS, _At_(*ppNDISPoolData, _Post_ _Null_)) +_When_(return == STATUS_SUCCESS, _At_(*ppNDISPoolData, _Post_ _Notnull_ __drv_allocatesMem(Pool))) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprNDISPoolDataCreate(_Outptr_ NDIS_POOL_DATA** ppNDISPoolData, + _In_opt_ UINT32 memoryTag) /* WFPSAMPLER_NDIS_POOL_TAG */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprNDISPoolDataCreate()\n"); + +#endif /// DBG + + NT_ASSERT(ppNDISPoolData); + + NTSTATUS status = STATUS_SUCCESS; + + HLPR_NEW(*ppNDISPoolData, + NDIS_POOL_DATA, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(*ppNDISPoolData, + status); + + status = KrnlHlprNDISPoolDataPopulate(*ppNDISPoolData, + memoryTag); + + HLPR_BAIL_LABEL: + +#pragma warning(push) +#pragma warning(disable: 6001) /// *ppNDISPoolData initialized with calls to HLPR_NEW & KrnlHlprNDISPoolDataPopulate + + if(status != STATUS_SUCCESS && + *ppNDISPoolData) + KrnlHlprNDISPoolDataDestroy(ppNDISPoolData); + +#pragma warning(pop) + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprNDISPoolDataCreate() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} diff --git a/network/trans/WFPSampler/syslib/HelperFunctions_NDIS.h b/network/trans/WFPSampler/syslib/HelperFunctions_NDIS.h new file mode 100644 index 000000000..6bd6f10e6 --- /dev/null +++ b/network/trans/WFPSampler/syslib/HelperFunctions_NDIS.h @@ -0,0 +1,65 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_NDIS.h +// +// Abstract: +// This module contains prototypes for kernel helper functions that assist with NDIS operations. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef HELPERFUNCTIONS_NDIS_H +#define HELPERFUNCTIONS_NDIS_H + +typedef struct NDIS_POOL_DATA_ +{ + HANDLE ndisHandle; /// NDIS_HANDLE + HANDLE nblPoolHandle; /// NDIS_HANDLE + HANDLE nbPoolHandle; /// NDIS_HANDLE +}NDIS_POOL_DATA, *PNDIS_POOL_DATA; + +extern NDIS_POOL_DATA* g_pNDISPoolData; + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID KrnlHlprNDISPoolDataPurge(_Inout_ NDIS_POOL_DATA* pNDISPoolData); + +_At_(*ppNDISPoolData, _Pre_ _Notnull_) +_At_(*ppNDISPoolData, _Post_ _Null_ __drv_freesMem(Pool)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(*ppNDISPoolData == 0) +VOID KrnlHlprNDISPoolDataDestroy(_Inout_ NDIS_POOL_DATA** ppNDISPoolData); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprNDISPoolDataPopulate(_Inout_ NDIS_POOL_DATA* pNDISPoolData, + _In_opt_ UINT32 memoryTag = WFPSAMPLER_NDIS_POOL_TAG); + +_At_(*ppNDISPoolData, _Pre_ _Null_) +_When_(return != STATUS_SUCCESS, _At_(*ppNDISPoolData, _Post_ _Null_)) +_When_(return == STATUS_SUCCESS, _At_(*ppNDISPoolData, _Post_ _Notnull_ __drv_allocatesMem(Pool))) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprNDISPoolDataCreate(_Outptr_ NDIS_POOL_DATA** ppNDISPoolData, + _In_opt_ UINT32 memoryTag = WFPSAMPLER_NDIS_POOL_TAG); + +#endif /// HELPERFUNCTIONS_NDIS_H \ No newline at end of file diff --git a/network/trans/WFPSampler/syslib/HelperFunctions_NetBuffer.cpp b/network/trans/WFPSampler/syslib/HelperFunctions_NetBuffer.cpp new file mode 100644 index 000000000..2d6bd3c99 --- /dev/null +++ b/network/trans/WFPSampler/syslib/HelperFunctions_NetBuffer.cpp @@ -0,0 +1,437 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_NetBuffer.cpp +// +// Abstract: +// This module contains kernel helper functions that assist with NET_BUFFER and NET_BUFFER_LIST. +// +// Naming Convention: +// +// +// +// i.e. +// +// KrnlHlprNBLGetRequiredRefCount +// +// +// KrnlHlpr - Function is located in syslib\ and applies to kernel mode. +// +// NBL - Function pertains to NET_BUFFER_LIST objects. +// +// { +// Get - Function retrieves data. +// } +// +// { +// RequiredRefCount - Function returns a refCount for the NBL / NBL chain. +// } +// +// Private Functions: +// +// Public Functions: +// KrnlHlprNBLGetRequiredRefCount(), +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Add KrnlHlprNBLCreateFromBuffer, +// KrnlHlprNBLCopyToBuffer, +// KrnlHlprNBLDestroyNew, +// KrnlHlprNBLCreateNew +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "HelperFunctions_Include.h" /// . +#include "HelperFunctions_NetBuffer.tmh" /// $(OBJ_PATH)\$(O)\ + + +/** + @kernel_helper_function="KrnlHlprNBLCreateFromBuffer" + + Purpose: Creates a new NBL from the data contained in the supplied buffer.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(return != 0) +NET_BUFFER_LIST* KrnlHlprNBLCreateFromBuffer(_In_ NDIS_HANDLE nblPoolHandle, + _In_reads_(bufferSize) BYTE* pBuffer, + _In_ UINT32 bufferSize, + _Outptr_opt_result_maybenull_ PMDL* ppMDL) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprNBLCreateFromBuffer()\n"); + +#endif /// DBG + + NTSTATUS status = STATUS_SUCCESS; + NET_BUFFER_LIST* pNBL = 0; + PMDL pMDL = 0; + + pMDL = IoAllocateMdl(pBuffer, + bufferSize, + FALSE, + FALSE, + 0); + if(!pMDL) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! KrnlHlprNBLCreateFromBuffer : IoAllocateMdl() [pMDL: %#p]\n", + pMDL); + + HLPR_BAIL; + } + + MmBuildMdlForNonPagedPool(pMDL); + + status = FwpsAllocateNetBufferAndNetBufferList(nblPoolHandle, + 0, + 0, + pMDL, + 0, + bufferSize, + &pNBL); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! KrnlHlprNBLCreateFromBuffer : FwpsAllocateNetBufferAndNetBufferList() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS) + { + IoFreeMdl(pMDL); + + pMDL = 0; + } + + if(ppMDL) + *ppMDL = pMDL; + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprNBLCreateFromBuffer() [pNBL: %#p]\n", + pNBL); + +#endif /// DBG + + return pNBL; +} + +/** + @kernel_helper_function="KrnlHlprNBLCopyToBuffer" + + Purpose: Copies the NBL to a buffer.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(return != 0) +BYTE* KrnlHlprNBLCopyToBuffer(_In_opt_ NET_BUFFER_LIST* pTemplateNBL, + _Out_ UINT32* pSize, + _In_ UINT32 additionalSpace) /* 0 */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprNBLCopyToBuffer()\n"); + +#endif /// DBG + + NTSTATUS status = STATUS_SUCCESS; + BYTE* pBuffer = 0; + UINT32 numBytes = additionalSpace; + + *pSize = 0; + + if(pTemplateNBL) + { + for(NET_BUFFER* pNB = NET_BUFFER_LIST_FIRST_NB(pTemplateNBL); + pNB; + pNB = NET_BUFFER_NEXT_NB(pNB)) + { + numBytes += NET_BUFFER_DATA_LENGTH(pNB); + } + } + + if(numBytes) + { + HLPR_NEW_ARRAY(pBuffer, + BYTE, + numBytes, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pBuffer, + status); + + if(pTemplateNBL) + { + NET_BUFFER* pNB = NET_BUFFER_LIST_FIRST_NB(pTemplateNBL); + + for(UINT32 bytesCopied = 0; + bytesCopied < numBytes && + pNB; + pNB = NET_BUFFER_NEXT_NB(pNB)) + { + BYTE* pContiguousBuffer = 0; + BYTE* pAllocatedBuffer = 0; + UINT32 bytesNeeded = NET_BUFFER_DATA_LENGTH(pNB); + + if(bytesNeeded) + { + HLPR_NEW_ARRAY(pAllocatedBuffer, + BYTE, + bytesNeeded, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pAllocatedBuffer, + status); + + pContiguousBuffer = (BYTE*)NdisGetDataBuffer(pNB, + bytesNeeded, + pAllocatedBuffer, + 1, + 0); + + RtlCopyMemory(&(pBuffer[bytesCopied]), + pContiguousBuffer ? pContiguousBuffer : pAllocatedBuffer, + bytesNeeded); + + bytesCopied += bytesNeeded; + + HLPR_DELETE_ARRAY(pAllocatedBuffer, + WFPSAMPLER_SYSLIB_TAG); + } + } + } + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS) + { + HLPR_DELETE_ARRAY(pBuffer, + WFPSAMPLER_SYSLIB_TAG); + } + else + *pSize = numBytes; + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprNBLCopyToBuffer() [pBuffer: %#p]\n", + pBuffer); + +#endif /// DBG + + return pBuffer; +} + +/** + @kernel_helper_function="KrnlHlprNBLDestroyNew" + + Purpose: Destroys a new NBL.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_Success_(*ppNetBufferList == 0 && *ppMDL == 0 && *ppAllocatedBuffer == 0) +VOID KrnlHlprNBLDestroyNew(_Inout_opt_ NET_BUFFER_LIST** ppNetBufferList, + _Inout_opt_ PMDL* ppMDL, + _Inout_opt_ BYTE** ppAllocatedBuffer) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprNBLDestroyNew()\n"); + +#endif /// DBG + + if(ppNetBufferList) + { + FwpsFreeNetBufferList(*ppNetBufferList); + + *ppNetBufferList = 0; + } + + if(ppMDL) + { + IoFreeMdl(*ppMDL); + + *ppMDL = 0; + } + + if(ppAllocatedBuffer) + { + HLPR_DELETE_ARRAY(*ppAllocatedBuffer, + WFPSAMPLER_SYSLIB_TAG); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprNBLDestroyNew()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprNBLCreateNew" + + Purpose: Creates a new NBL.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(return != 0) +NET_BUFFER_LIST* KrnlHlprNBLCreateNew(_In_ NDIS_HANDLE nblPoolHandle, + _In_opt_ NET_BUFFER_LIST* pTemplateNBL, + _Outptr_opt_result_buffer_maybenull_(*pSize) BYTE** ppAllocatedBuffer, + _Out_ UINT32* pSize, + _Outptr_opt_result_maybenull_ PMDL* ppMDL, + _In_ UINT32 additionalSpace, /* 0 */ + _In_ BOOLEAN isOutbound) /* FALSE */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprNBLCreateNew()\n"); + +#endif /// DBG + + NET_BUFFER_LIST* pNBL = 0; + UINT32 size = 0; + BYTE* pBuffer = KrnlHlprNBLCopyToBuffer(pTemplateNBL, + &size, + additionalSpace); + + if(pBuffer && + size) + { + pNBL = KrnlHlprNBLCreateFromBuffer(nblPoolHandle, + pBuffer, + size, + ppMDL); + if(pNBL && + pTemplateNBL) + { + if(isOutbound) + NdisCopySendNetBufferListInfo(pNBL, + pTemplateNBL); + else + NdisCopyReceiveNetBufferListInfo(pNBL, + pTemplateNBL); + } + else + { + HLPR_DELETE_ARRAY(pBuffer, + WFPSAMPLER_SYSLIB_TAG); + + size = 0; + } + } + + if(ppAllocatedBuffer) + *ppAllocatedBuffer = pBuffer; + + *pSize = size; + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprNBLCreateNew() [pNBL: %#p]\n", + pNBL); + +#endif /// DBG + + return pNBL; +} + + +/** + @kernel_helper_function="KrnlHlprNBLGetRequiredRefCount" + + Purpose: Return a count of how many NBLs are within the NBL chain.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +UINT32 KrnlHlprNBLGetRequiredRefCount(_In_ const NET_BUFFER_LIST* pNBL, + _In_ BOOLEAN isChained) /* FALSE */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprNBLGetRequiredRefCount()\n"); + +#endif /// DBG + + NT_ASSERT(pNBL); + + UINT32 requiredRefCount = 0; + + if(isChained) + { + for(NET_BUFFER_LIST* pCurrentNBL = (NET_BUFFER_LIST*)pNBL; + pCurrentNBL; + pCurrentNBL = NET_BUFFER_LIST_NEXT_NBL(pCurrentNBL)) + { + requiredRefCount++; + } + } + else + requiredRefCount = 1; + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprNBLGetRequiredRefCount() [refCount: %#d]\n", + requiredRefCount); + +#endif /// DBG + + return requiredRefCount; +} diff --git a/network/trans/WFPSampler/syslib/HelperFunctions_NetBuffer.h b/network/trans/WFPSampler/syslib/HelperFunctions_NetBuffer.h new file mode 100644 index 000000000..69f378982 --- /dev/null +++ b/network/trans/WFPSampler/syslib/HelperFunctions_NetBuffer.h @@ -0,0 +1,70 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_NetBuffer.h +// +// Abstract: +// This module contains prototypes for kernel helper functions that assist with NET_BUFFER and +// NET_BUFFER_LIST. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Add KrnlHlprNBLCreateFromBuffer, +// KrnlHlprNBLCopyToBuffer, +// KrnlHlprNBLDestroyNew, +// KrnlHlprNBLCreateNew +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef HELPERFUNCTIONS_NET_BUFFER_H +#define HELPERFUNCTIONS_NET_BUFFER_H + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(return != 0) +NET_BUFFER_LIST* KrnlHlprNBLCreateFromBuffer(_In_ NDIS_HANDLE nblPoolHandle, + _In_reads_(bufferSize) BYTE* pBuffer, + _In_ UINT32 bufferSize, + _Outptr_opt_result_maybenull_ PMDL* ppMDL); + + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(return != 0) +BYTE* KrnlHlprNBLCopyToBuffer(_In_opt_ NET_BUFFER_LIST* pTemplateNBL, + _Out_ UINT32* pSize, + _In_ UINT32 additionalSpace = 0); + +_Success_(*ppNetBufferList == 0 && *ppMDL == 0 && *ppAllocatedBuffer == 0) +VOID KrnlHlprNBLDestroyNew(_Inout_opt_ NET_BUFFER_LIST** ppNetBufferList, + _Inout_opt_ PMDL* ppMDL, + _Inout_opt_ BYTE** ppAllocatedBuffer); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(return != 0) +NET_BUFFER_LIST* KrnlHlprNBLCreateNew(_In_ NDIS_HANDLE nblPoolHandle, + _In_opt_ NET_BUFFER_LIST* pTemplateNBL, + _Outptr_opt_result_buffer_maybenull_(*pSize) BYTE** ppAllocatedBuffer, + _Out_ UINT32* pSize, + _Outptr_opt_result_maybenull_ PMDL* ppMDL, + _In_ UINT32 additionalSpace = 0, + _In_ BOOLEAN isOutbound = FALSE); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +UINT32 KrnlHlprNBLGetRequiredRefCount(_In_ const NET_BUFFER_LIST* pNBL, + _In_ BOOLEAN isChained = FALSE); + +#endif /// HELPERFUNCTIONS_NET_BUFFER_H diff --git a/network/trans/WFPSampler/syslib/HelperFunctions_NotifyData.h b/network/trans/WFPSampler/syslib/HelperFunctions_NotifyData.h new file mode 100644 index 000000000..7075f9411 --- /dev/null +++ b/network/trans/WFPSampler/syslib/HelperFunctions_NotifyData.h @@ -0,0 +1,33 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_NotifyData.h +// +// Abstract: +// This module contains prototypes for kernel helper functions that assist with NOTIFY_DATA. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Add filterID +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef HELPERFUNCTIONS_NOTIFY_DATA_H +#define HELPERFUNCTIONS_NOTIFY_DATA_H + +typedef struct NOTIFY_DATA_ +{ + UINT32 notificationType; + UINT32 calloutID; + UINT64 filterID; + const GUID* pFilterKey; +}NOTIFY_DATA, *PNOTIFY_DATA; + +#endif /// HELPERFUNCTIONS_NOTIFY_DATA_H \ No newline at end of file diff --git a/network/trans/WFPSampler/syslib/HelperFunctions_PendData.cpp b/network/trans/WFPSampler/syslib/HelperFunctions_PendData.cpp new file mode 100644 index 000000000..1ad4338fa --- /dev/null +++ b/network/trans/WFPSampler/syslib/HelperFunctions_PendData.cpp @@ -0,0 +1,411 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_PendData.cpp +// +// Abstract: +// This module contains kernel helper functions that assist with PEND_DATA. +// +// Naming Convention: +// +// +// +// i.e. +// +// KrnlHlprWorkItemQueue +// +// +// KrnlHlpr - Function is located in syslib\ and applies to kernel mode. +// +// PendData - Function pertains to PEND_DATA objects. +// +// { +// Create - Function allocates and fills memory. +// Destroy - Function cleans up and frees memory. +// Populate - Function fills memory with values +// Purge - Function cleans up values +// } +// +// Private Functions: +// +// Public Functions: +// KrnlHlprPendDataDataCreate(), +// KrnlHlprPendDataDataDestroy(), +// KrnlHlprPendDataDataPopulate(), +// KrnlHlprPendDataDataPurge(), +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Add support for pending at +// FWPM_LAYER_ALE_ENDPOINT_CLOSURE +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "HelperFunctions_Include.h" /// . +#include "HelperFunctions_PendData.tmh" /// $(OBJ_PATH)\$(O)\ + +/** + @kernel_helper_function="KrnlHlprPendDataPurge" + + Purpose: Cleanup a PEND_DATA object.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/FF551199.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprPendDataPurge(_Inout_ PEND_DATA* pPendData) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprPendDataPurge()\n"); + +#endif /// DBG + + NT_ASSERT(pPendData); + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + if(pPendData->layerID == FWPS_LAYER_ALE_ENDPOINT_CLOSURE_V4 || + pPendData->layerID == FWPS_LAYER_ALE_ENDPOINT_CLOSURE_V6) + { + if(pPendData->classifyHandle && + pPendData->isPended) + { + FwpsCompleteClassify(pPendData->classifyHandle, + 0, + &(pPendData->classifyOut)); + + + FwpsReleaseClassifyHandle(pPendData->classifyHandle); + + pPendData->classifyHandle = 0; + pPendData->pPCPendData = 0; + pPendData->isPended = FALSE; + } + } + else + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + { + if(pPendData->completionContext && + pPendData->isPended) + { + FwpsCompleteOperation(pPendData->completionContext, + pPendData->pNBL); + + pPendData->completionContext = 0; + pPendData->pNBL = 0; + pPendData->pPendAuthorizationData = 0; + pPendData->isPended = FALSE; + } + } + + RtlZeroMemory(pPendData, + sizeof(PEND_DATA)); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprPendDataPurge()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprPendDataDestroy" + + Purpose: Cleanup and free a PEND_DATA object.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_At_(ppPendData, _Pre_ _Notnull_) +_At_(*ppPendData, _Post_ _Null_ __drv_freesMem(Pool)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(*ppPendData == 0) +inline VOID KrnlHlprPendDataDestroy(_Inout_ PEND_DATA** ppPendData) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprPendDataDestroy()\n"); + +#endif /// DBG + + NT_ASSERT(ppPendData); + + if(*ppPendData) + { + KrnlHlprPendDataPurge(*ppPendData); + + HLPR_DELETE(*ppPendData, + WFPSAMPLER_SYSLIB_TAG); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprPendDataDestroy()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprPendDataPopulate" + + Purpose: Populates a PEND_DATA object with the completionContext.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/ff551199.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprPendDataPopulate(_Inout_ PEND_DATA* pPendData, + _In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _In_opt_ NET_BUFFER_LIST* pNBL, + _In_ const FWPS_FILTER* pFilter, + _In_opt_ VOID* pClassifyContext, /* 0 */ + _In_opt_ FWPS_CLASSIFY_OUT* pClassifyOut) /* 0 */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprPendDataPopulate()\n"); + +#endif /// DBG + + NT_ASSERT(pPendData); + NT_ASSERT(pClassifyValues); + NT_ASSERT(pMetadata); + NT_ASSERT(pFilter); + + NTSTATUS status = STATUS_SUCCESS; + + pPendData->layerID = pClassifyValues->layerId; + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + if(pPendData->layerID == FWPS_LAYER_ALE_ENDPOINT_CLOSURE_V4 || + pPendData->layerID == FWPS_LAYER_ALE_ENDPOINT_CLOSURE_V6) + { + NT_ASSERT(pClassifyContext); + NT_ASSERT(pClassifyOut); + + if(pClassifyContext && + pClassifyOut) + { + status = FwpsAcquireClassifyHandle(pClassifyContext, + 0, + &(pPendData->classifyHandle)); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! KrnlHlprPendDataPopulate : FwpsAcquireClassifyHandle() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + status = FwpsPendClassify(pPendData->classifyHandle, + pFilter->filterId, + 0, + pClassifyOut); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! KrnlHlprPendDataPopulate : FwpsPendClassify() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + RtlCopyMemory(&(pPendData->classifyOut), + pClassifyOut, + sizeof(FWPS_CLASSIFY_OUT)); + + pPendData->pPCPendData = pFilter->providerContext->dataBuffer->data; + pPendData->isPended = TRUE; + } + else + { + status = STATUS_INVALID_PARAMETER; + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! KrnlHlprPendDataPopulate : [status: %#x][pClassifyContext: %#p][pClassifyOut: %#p]\n", + status, + pClassifyContext, + pClassifyOut); + + HLPR_BAIL; + } + } + else + +#else + + UNREFERENCED_PARAMETER(pClassifyContext); + UNREFERENCED_PARAMETER(pClassifyOut); + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + { + + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_COMPLETION_HANDLE)) + { + status = FwpsPendOperation(pMetadata->completionHandle, + &(pPendData->completionContext)); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! KrnlHlprPendDataPopulate : FwpsPendOperation() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + pPendData->pNBL = pNBL; + pPendData->pPCPendData = pFilter->providerContext->dataBuffer->data; + pPendData->isPended = TRUE; + } + else + { + status = STATUS_INVALID_HANDLE; + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! KrnlHlprPendDataPopulate() [status: %#x]\n", + status); + } + } + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS) + KrnlHlprPendDataPurge(pPendData); + + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprPendDataPopulate() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @kernel_helper_function="KrnlHlprPendDataCreate" + + Purpose: Allocates and populates a PEND_DATA object with the completionContext.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_At_(*ppPendData, _Pre_ _Null_) +_When_(return != STATUS_SUCCESS, _At_(*ppPendData, _Post_ _Null_)) +_When_(return == STATUS_SUCCESS, _At_(*ppPendData, _Post_ _Notnull_ __drv_allocatesMem(Pool))) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprPendDataCreate(_Outptr_ PEND_DATA** ppPendData, + _In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _In_opt_ NET_BUFFER_LIST* pNBL, + _In_ const FWPS_FILTER* pFilter, + _In_opt_ VOID* pClassifyContext, /* 0 */ + _In_opt_ FWPS_CLASSIFY_OUT* pClassifyOut) /* 0 */ + + +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprPendDataCreate()\n"); + +#endif /// DBG + + NT_ASSERT(ppPendData); + NT_ASSERT(pClassifyValues); + NT_ASSERT(pMetadata); + NT_ASSERT(pFilter); + + NTSTATUS status = STATUS_SUCCESS; + + HLPR_NEW(*ppPendData, + PEND_DATA, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(*ppPendData, + status); + + status = KrnlHlprPendDataPopulate(*ppPendData, + pClassifyValues, + pMetadata, + pNBL, + pFilter, + pClassifyContext, + pClassifyOut); + + HLPR_BAIL_LABEL: + +#pragma warning(push) +#pragma warning(disable: 6001) /// *ppPendData initialized with calls to HLPR_NEW & KrnlHlprPendDataPopulate + + if(status != STATUS_SUCCESS && + *ppPendData) + KrnlHlprPendDataDestroy(ppPendData); + +#pragma warning(pop) + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprPendDataCreate() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} diff --git a/network/trans/WFPSampler/syslib/HelperFunctions_PendData.h b/network/trans/WFPSampler/syslib/HelperFunctions_PendData.h new file mode 100644 index 000000000..17174e326 --- /dev/null +++ b/network/trans/WFPSampler/syslib/HelperFunctions_PendData.h @@ -0,0 +1,85 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_PendData.h +// +// Abstract: +// This module contains prototypes for kernel helper functions that assist with PEND_DATA. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Add support for pending at +// FWPM_LAYER_ALE_ENDPOINT_CLOSURE +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef HELPERFUNCTIONS_PEND_DATA_H +#define HELPERFUNCTIONS_PEND_DATA_H + +typedef struct PEND_DATA_ +{ + HANDLE completionContext; + UINT64 classifyHandle; + NET_BUFFER_LIST* pNBL; + UINT16 layerID; + BOOLEAN isPended; + BYTE pReserved[1]; + union + { + PC_PEND_AUTHORIZATION_DATA* pPendAuthorizationData; + PC_PEND_ENDPOINT_CLOSURE_DATA* pPendEndpointClosureData; + VOID* pPCPendData; + }; + FWPS_CLASSIFY_OUT classifyOut; +}PEND_DATA, *PPEND_DATA; + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprPendDataPurge(_Inout_ PEND_DATA* pPendData); + +_At_(ppPendData, _Pre_ _Notnull_) +_At_(*ppPendData, _Post_ _Null_ __drv_freesMem(Pool)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(*ppPendData == 0) +inline VOID KrnlHlprPendDataDestroy(_Inout_ PEND_DATA** ppPendData); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprPendDataPopulate(_Inout_ PEND_DATA* pPendData, + _In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _In_opt_ NET_BUFFER_LIST* pNBL, + _In_ const FWPS_FILTER* pFilter, + _In_opt_ VOID* pClassifyContext = 0, + _In_opt_ FWPS_CLASSIFY_OUT* pClassifyOut = 0); + +_At_(*ppPendData, _Pre_ _Null_) +_When_(return != STATUS_SUCCESS, _At_(*ppPendData, _Post_ _Null_)) +_When_(return == STATUS_SUCCESS, _At_(*ppPendData, _Post_ _Notnull_ __drv_allocatesMem(Pool))) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprPendDataCreate(_Outptr_ PEND_DATA** ppPendData, + _In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _In_opt_ NET_BUFFER_LIST* pNBL, + _In_ const FWPS_FILTER* pFilter, + _In_opt_ VOID* pClassifyContext = 0, + _In_opt_ FWPS_CLASSIFY_OUT* pClassifyOut = 0); + +#endif /// HELPERFUNCTIONS_PEND_DATA_H \ No newline at end of file diff --git a/network/trans/WFPSampler/syslib/HelperFunctions_RedirectData.cpp b/network/trans/WFPSampler/syslib/HelperFunctions_RedirectData.cpp new file mode 100644 index 000000000..2671e2769 --- /dev/null +++ b/network/trans/WFPSampler/syslib/HelperFunctions_RedirectData.cpp @@ -0,0 +1,340 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_RedirectData.cpp +// +// Abstract: +// This module contains kernel helper functions that assist with REDIRECT_DATA. +// +// Naming Convention: +// +// +// +// i.e. +// +// KrnlHlprWorkItemQueue +// +// +// KrnlHlpr - Function is located in syslib\ and applies to kernel mode. +// +// RedirectData - Function pertains to REDIRECT_DATA objects. +// +// { +// Create - Function allocates and fills memory. +// Destroy - Function cleans up and frees memory. +// Populate - Function fills memory with values +// Purge - Function cleans up values +// } +// +// Private Functions: +// +// Public Functions: +// KrnlHlprRedirectDataDataCreate(), +// KrnlHlprRedirectDataDataDestroy(), +// KrnlHlprRedirectDataDataPopulate(), +// KrnlHlprRedirectDataDataPurge(), +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Enhance support for ALE redirection. +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "HelperFunctions_Include.h" /// . +#include "HelperFunctions_RedirectData.tmh" /// $(OBJ_PATH)\$(O)\ + +#if(NTDDI_VERSION >= NTDDI_WIN7) + +/** + @kernel_helper_function="KrnlHlprRedirectDataPurge" + + Purpose: Cleanup a REDIRECT_DATA object.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/FF551137.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/FF551150.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/FF551208.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprRedirectDataPurge(_Inout_ REDIRECT_DATA* pRedirectData) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprRedirectDataPurge()\n"); + +#endif /// DBG + + NT_ASSERT(pRedirectData); + + if(pRedirectData->pWritableLayerData) + { + FwpsApplyModifiedLayerData(pRedirectData->classifyHandle, + pRedirectData->pWritableLayerData, + FWPS_CLASSIFY_FLAG_REAUTHORIZE_IF_MODIFIED_BY_OTHERS); + + pRedirectData->pWritableLayerData = 0; + } + + if(pRedirectData->classifyHandle) + { + if(pRedirectData->isPended) + { + FwpsCompleteClassify(pRedirectData->classifyHandle, + 0, + pRedirectData->pClassifyOut); + + pRedirectData->isPended = FALSE; + } + + FwpsReleaseClassifyHandle(pRedirectData->classifyHandle); + + pRedirectData->classifyHandle = 0; + } + + RtlZeroMemory(pRedirectData, + sizeof(REDIRECT_DATA)); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprRedirectDataPurge()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprRedirectDataDestroy" + + Purpose: Cleanup and free a REDIRECT_DATA object.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_At_(*ppRedirectData, _Pre_ _Notnull_) +_At_(*ppRedirectData, _Post_ _Null_ __drv_freesMem(Pool)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(*ppRedirectData == 0) +inline VOID KrnlHlprRedirectDataDestroy(_Inout_ REDIRECT_DATA** ppRedirectData) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprRedirectDataDestroy()\n"); + +#endif /// DBG + + NT_ASSERT(ppRedirectData); + + if(*ppRedirectData) + { + KrnlHlprRedirectDataPurge(*ppRedirectData); + + HLPR_DELETE(*ppRedirectData, + WFPSAMPLER_SYSLIB_TAG); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprRedirectDataDestroy()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprRedirectDataPopulate" + + Purpose: Populates a REDIRECT_DATA object with the classifyHandle and the writable layer + data pointer.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/FF550085.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/FF550087.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprRedirectDataPopulate(_Inout_ REDIRECT_DATA* pRedirectData, + _In_ const VOID* pClassifyContext, + _In_ const FWPS_FILTER* pFilter, + _In_ FWPS_CLASSIFY_OUT* pClassifyOut, + _In_opt_ HANDLE redirectHandle) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprRedirectDataPopulate()\n"); + +#endif /// DBG + + NT_ASSERT(pRedirectData); + NT_ASSERT(pClassifyContext); + NT_ASSERT(pFilter); + NT_ASSERT(pClassifyOut); + NT_ASSERT(pFilter->providerContext); + NT_ASSERT(pFilter->providerContext->type == FWPM_GENERAL_CONTEXT); + NT_ASSERT(pFilter->providerContext->dataBuffer); + NT_ASSERT(pFilter->providerContext->dataBuffer->size == sizeof(PC_PROXY_DATA)); + NT_ASSERT(pFilter->providerContext->dataBuffer->data); + + NTSTATUS status = STATUS_SUCCESS; + + status = FwpsAcquireClassifyHandle((void*)pClassifyContext, + 0, + &(pRedirectData->classifyHandle)); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! KrnlHlprRedirectDataPopulate : FwpsAcquireClassifyHandle() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + status = FwpsAcquireWritableLayerDataPointer(pRedirectData->classifyHandle, + pFilter->filterId, + 0, + &(pRedirectData->pWritableLayerData), + pClassifyOut); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! KrnlHlprRedirectDataPopulate : FwpsAcquireWritableLayerDataPointer() [status: %#x]\n", + status); + + HLPR_BAIL; + } + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + pRedirectData->redirectHandle = redirectHandle; + +#else + + UNREFERENCED_PARAMETER(redirectHandle); + +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) + + + pRedirectData->pProxyData = (PC_PROXY_DATA*)pFilter->providerContext->dataBuffer->data; + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS) + KrnlHlprRedirectDataPurge(pRedirectData); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprRedirectDataPopulate() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @kernel_helper_function="KrnlHlprRedirectDataCreate" + + Purpose: Allocates and populates a REDIRECT_DATA object with the classifyHandle and the + writable layer data pointer.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_At_(*ppRedirectData, _Pre_ _Null_) +_When_(return != STATUS_SUCCESS, _At_(*ppRedirectData, _Post_ _Null_)) +_When_(return == STATUS_SUCCESS, _At_(*ppRedirectData, _Post_ _Notnull_ __drv_allocatesMem(Pool))) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprRedirectDataCreate(_Outptr_ REDIRECT_DATA** ppRedirectData, + _In_ const VOID* pClassifyContext, + _In_ const FWPS_FILTER* pFilter, + _In_ FWPS_CLASSIFY_OUT* pClassifyOut, + _In_opt_ HANDLE redirectHandle) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprRedirectDataCreate()\n"); + +#endif /// DBG + + NT_ASSERT(ppRedirectData); + NT_ASSERT(pClassifyContext); + NT_ASSERT(pFilter); + NT_ASSERT(pClassifyOut); + + NTSTATUS status = STATUS_SUCCESS; + + HLPR_NEW(*ppRedirectData, + REDIRECT_DATA, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(*ppRedirectData, + status); + + status = KrnlHlprRedirectDataPopulate(*ppRedirectData, + pClassifyContext, + pFilter, + pClassifyOut, + redirectHandle); + + HLPR_BAIL_LABEL: + +#pragma warning(push) +#pragma warning(disable: 6001) /// *ppRedirectData initialized with call to HLPR_NEW & KrnlHlprRedirectDataPopulate + + if(status != STATUS_SUCCESS && + *ppRedirectData) + KrnlHlprRedirectDataDestroy(ppRedirectData); + +#pragma warning(pop) + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprRedirectDataCreate() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +#endif // (NTDDI_VERSION >= NTDDI_WIN7) diff --git a/network/trans/WFPSampler/syslib/HelperFunctions_RedirectData.h b/network/trans/WFPSampler/syslib/HelperFunctions_RedirectData.h new file mode 100644 index 000000000..b6b77a351 --- /dev/null +++ b/network/trans/WFPSampler/syslib/HelperFunctions_RedirectData.h @@ -0,0 +1,90 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_RedirectData.h +// +// Abstract: +// This module contains prototypes for kernel helper functions that assist with REDIRECT_DATA. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Enhance support for ALE redirection. +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef HELPERFUNCTIONS_REDIRECT_DATA_H +#define HELPERFUNCTIONS_REDIRECT_DATA_H + +typedef struct REDIRECTION_HANDLE_DATA_ +{ + HANDLE* pConnectRedirectionHandle; + HANDLE* pBindRedirectionHandle; +}REDIRECTION_HANDLE_DATA,*PREDIRECTION_HANDLE_DATA; + +typedef struct REDIRECT_DATA_ +{ + UINT64 classifyHandle; + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + HANDLE redirectHandle; + +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) + + VOID* pWritableLayerData; /// FWPS_BIND_REQUEST or FWPS_CONNECT_REQUEST + PC_PROXY_DATA* pProxyData; + FWPS_CLASSIFY_OUT* pClassifyOut; + BOOLEAN isPended; + BYTE pReserved[7]; +}REDIRECT_DATA, *PREDIRECT_DATA; + +#if(NTDDI_VERSION >= NTDDI_WIN7) + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID KrnlHlprRedirectDataPurge(_Inout_ REDIRECT_DATA* pRedirectData); + +_At_(*ppRedirectData, _Pre_ _Notnull_) +_At_(*ppRedirectData, _Post_ _Null_ __drv_freesMem(Pool)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(*ppRedirectData == 0) +VOID KrnlHlprRedirectDataDestroy(_Inout_ REDIRECT_DATA** ppRedirectData); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprRedirectDataPopulate(_Inout_ REDIRECT_DATA* pRedirectData, + _In_ const VOID* pClassifyContext, + _In_ const FWPS_FILTER* pFilter, + _In_ FWPS_CLASSIFY_OUT* pClassifyOut, + _In_opt_ HANDLE redirectHandle); + +_At_(*ppRedirectData, _Pre_ _Null_) +_When_(return != STATUS_SUCCESS, _At_(*ppRedirectData, _Post_ _Null_)) +_When_(return == STATUS_SUCCESS, _At_(*ppRedirectData, _Post_ _Notnull_ __drv_allocatesMem(Pool))) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprRedirectDataCreate(_Outptr_ REDIRECT_DATA** ppRedirectData, + _In_ const VOID* pClassifyContext, + _In_ const FWPS_FILTER* pFilter, + _In_ FWPS_CLASSIFY_OUT* pClassifyOut, + _In_opt_ HANDLE redirectHandle); + +#endif // (NTDDI_VERSION >= NTDDI_WIN7) + +#endif /// HELPERFUNCTIONS_REDIRECT_DATA_H \ No newline at end of file diff --git a/network/trans/WFPSampler/syslib/HelperFunctions_WorkItems.cpp b/network/trans/WFPSampler/syslib/HelperFunctions_WorkItems.cpp new file mode 100644 index 000000000..4c47f3b33 --- /dev/null +++ b/network/trans/WFPSampler/syslib/HelperFunctions_WorkItems.cpp @@ -0,0 +1,1242 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_WorkItems.cpp +// +// Abstract: +// This module contains kernel helper functions that assist with IO_WORKITEM routines. +// +// Naming Convention: +// +// +// +// i.e. +// +// KrnlHlprWorkItemQueue +// +// +// KrnlHlpr - Function is located in syslib\ and applies to kernel mode. +// +// WorkItem - Function pertains to work item routines. +// WorkItemData - Function pertains to WORKITEM_DATA objects. +// +// { +// Create - Function allocates and fills memory. +// Destroy - Function cleans up and frees memory. +// Populate - Function fills memory with values +// Purge - Function cleans up values +// Queue - Function will take a reference on an object +// Sleep - Function performs no operations for a set period. +// } +// +// Private Functions: +// +// Public Functions: +// KrnlHlprWorkItemDataCreate(), +// KrnlHlprWorkItemDataDestroy(), +// KrnlHlprWorkItemDataPopulate(), +// KrnlHlprWorkItemDataPurge(), +// KrnlHlprWorkItemQueue(), +// KrnlHlprWorkItemSleep(), +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Add support for pending at +// FWPM_LAYER_ALE_ENDPOINT_CLOSURE. +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "HelperFunctions_Include.h" /// . +#include "HelperFunctions_WorkItems.tmh" /// $(OBJ_PATH)\$(O)\ + +/** + @kernel_helper_function="KrnlHlprWorkItemDataPurge" + + Purpose: Cleanup a WORKITEM_DATA object.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/FF549133.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprWorkItemDataPurge(_Inout_ WORKITEM_DATA* pWorkItemData) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprWorkItemDataPurge()\n"); + +#endif /// DBG + + NT_ASSERT(pWorkItemData); + + if(pWorkItemData->pIOWorkItem) + IoFreeWorkItem(pWorkItemData->pIOWorkItem); + + RtlZeroMemory(pWorkItemData, + sizeof(WORKITEM_DATA)); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprWorkItemDataPurge()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprWorkItemDataDestroy" + + Purpose: Cleanup and free a WORKITEM_DATA object.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_At_(*ppWorkItemData, _Pre_ _Notnull_) +_At_(*ppWorkItemData, _Post_ _Null_ __drv_freesMem(Pool)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(*ppWorkItemData == 0) +inline VOID KrnlHlprWorkItemDataDestroy(_Inout_ WORKITEM_DATA** ppWorkItemData) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprWorkItemDataDestroy()\n"); + +#endif /// DBG + + NT_ASSERT(ppWorkItemData); + + if(*ppWorkItemData) + { + KrnlHlprWorkItemDataPurge(*ppWorkItemData); + + HLPR_DELETE(*ppWorkItemData, + WFPSAMPLER_SYSLIB_TAG); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprWorkItemDataDestroy()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprWorkItemDataPopulate" + + Purpose: Populates a WORKITEM_DATA object with the classifyData, injectionData, + PIOWorkItem, and context supplied.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprWorkItemDataPopulate(_Inout_ WORKITEM_DATA* pWorkItemData, + _In_ CLASSIFY_DATA* pClassifyData, + _In_opt_ INJECTION_DATA* pInjectionData, /* 0 */ + _In_opt_ PIO_WORKITEM pIOWorkItem, /* 0 */ + _In_opt_ VOID* pContext) /* 0 */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprWorkItemDataPopulate()\n"); + +#endif /// DBG + + NT_ASSERT(pWorkItemData); + NT_ASSERT(pClassifyData); + + pWorkItemData->pIOWorkItem = pIOWorkItem; + pWorkItemData->pClassifyData = pClassifyData; + pWorkItemData->pInjectionData = pInjectionData; + pWorkItemData->pContext = pContext; + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprWorkItemDataPopulate()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprWorkItemDataPopulate" + + Purpose: Populates a WORKITEM_DATA object with the classifyData, pendData, PIOWorkItem, and + context supplied.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprWorkItemDataPopulate(_Inout_ WORKITEM_DATA* pWorkItemData, + _In_ CLASSIFY_DATA* pClassifyData, + _In_ PEND_DATA* pPendData, + _In_opt_ PIO_WORKITEM pIOWorkItem, /* 0 */ + _In_opt_ VOID* pContext) /* 0 */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprWorkItemDataPopulate()\n"); + +#endif /// DBG + + NT_ASSERT(pWorkItemData); + NT_ASSERT(pClassifyData); + NT_ASSERT(pPendData); + + pWorkItemData->pIOWorkItem = pIOWorkItem; + pWorkItemData->pClassifyData = pClassifyData; + pWorkItemData->pPendData = pPendData; + pWorkItemData->pContext = pContext; + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprWorkItemDataPopulate()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprWorkItemDataPopulate" + + Purpose: Populates a WORKITEM_DATA object with the classifyData, redirectData, PIOWorkItem, + and context supplied.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprWorkItemDataPopulate(_Inout_ WORKITEM_DATA* pWorkItemData, + _In_ CLASSIFY_DATA* pClassifyData, + _In_ REDIRECT_DATA* pRedirectData, + _In_opt_ PIO_WORKITEM pIOWorkItem, /* 0 */ + _In_opt_ VOID* pContext) /* 0 */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprWorkItemDataPopulate()\n"); + +#endif /// DBG + + NT_ASSERT(pWorkItemData); + NT_ASSERT(pClassifyData); + NT_ASSERT(pRedirectData); + + pWorkItemData->pIOWorkItem = pIOWorkItem; + pWorkItemData->pClassifyData = pClassifyData; + pWorkItemData->pRedirectData = pRedirectData; + pWorkItemData->pContext = pContext; + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprWorkItemDataPopulate()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprWorkItemDataPopulate" + + Purpose: Populates a WORKITEM_DATA object with the notifyData, PIOWorkItem, and context + supplied.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprWorkItemDataPopulate(_Inout_ WORKITEM_DATA* pWorkItemData, + _In_ NOTIFY_DATA* pNotifyData, + _In_opt_ PIO_WORKITEM pIOWorkItem, /* 0 */ + _In_opt_ VOID* pContext) /* 0 */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprWorkItemDataPopulate()\n"); + +#endif /// DBG + + NT_ASSERT(pWorkItemData); + NT_ASSERT(pNotifyData); + + pWorkItemData->pIOWorkItem = pIOWorkItem; + pWorkItemData->pNotifyData = pNotifyData; + pWorkItemData->pContext = pContext; + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprWorkItemDataPopulate()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprWorkItemDataPopulate" + + Purpose: Populates a WORKITEM_DATA object with the pendData, PIOWorkItem, and context + supplied.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprWorkItemDataPopulate(_Inout_ WORKITEM_DATA* pWorkItemData, + _In_ PEND_DATA* pPendData, + _In_opt_ PIO_WORKITEM pIOWorkItem, /* 0 */ + _In_opt_ VOID* pContext) /* 0 */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprWorkItemDataPopulate()\n"); + +#endif /// DBG + + NT_ASSERT(pWorkItemData); + NT_ASSERT(pPendData); + + pWorkItemData->pIOWorkItem = pIOWorkItem; + pWorkItemData->pPendData = pPendData; + pWorkItemData->pContext = pContext; + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprWorkItemDataPopulate()\n"); + +#endif /// DBG + + return; +} + +/** + @kernel_helper_function="KrnlHlprWorkItemDataCreate" + + Purpose: Allocates and populates a WORKITEM_DATA object with the classifyData, + injectionData, PIOWorkItem, and context supplied.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_At_(*ppWorkItemData, _Pre_ _Null_) +_When_(return != STATUS_SUCCESS, _At_(*ppWorkItemData, _Post_ _Null_)) +_When_(return == STATUS_SUCCESS, _At_(*ppWorkItemData, _Post_ _Notnull_ __drv_allocatesMem(Pool))) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprWorkItemDataCreate(_Outptr_ WORKITEM_DATA** ppWorkItemData, + _In_ CLASSIFY_DATA* pClassifyData, + _In_opt_ INJECTION_DATA* pInjectionData, /* 0 */ + _In_opt_ PIO_WORKITEM pIOWorkItem, /* 0 */ + _In_opt_ VOID* pContext) /* 0 */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprWorkItemDataCreate()\n"); + +#endif /// DBG + + NT_ASSERT(ppWorkItemData); + NT_ASSERT(pClassifyData); + NT_ASSERT(pInjectionData); + + NTSTATUS status = STATUS_SUCCESS; + + HLPR_NEW(*ppWorkItemData, + WORKITEM_DATA, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(*ppWorkItemData, + status); + + KrnlHlprWorkItemDataPopulate(*ppWorkItemData, + pClassifyData, + pInjectionData, + pIOWorkItem, + pContext); + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS) + KrnlHlprWorkItemDataDestroy(ppWorkItemData); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprWorkItemDataCreate() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @kernel_helper_function="KrnlHlprWorkItemDataCreate" + + Purpose: Allocates and populates a WORKITEM_DATA object with the classifyData, pendData, + PIOWorkItem, and context supplied.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_At_(*ppWorkItemData, _Pre_ _Null_) +_When_(return != STATUS_SUCCESS, _At_(*ppWorkItemData, _Post_ _Null_)) +_When_(return == STATUS_SUCCESS, _At_(*ppWorkItemData, _Post_ _Notnull_ __drv_allocatesMem(Pool))) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprWorkItemDataCreate(_Outptr_ WORKITEM_DATA** ppWorkItemData, + _In_ CLASSIFY_DATA* pClassifyData, + _In_ PEND_DATA* pPendData, + _In_opt_ PIO_WORKITEM pIOWorkItem, /* 0 */ + _In_opt_ VOID* pContext) /* 0 */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprWorkItemDataCreate()\n"); + +#endif /// DBG + + NT_ASSERT(ppWorkItemData); + NT_ASSERT(pClassifyData); + NT_ASSERT(pPendData); + + NTSTATUS status = STATUS_SUCCESS; + + HLPR_NEW(*ppWorkItemData, + WORKITEM_DATA, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(*ppWorkItemData, + status); + + KrnlHlprWorkItemDataPopulate(*ppWorkItemData, + pClassifyData, + pPendData, + pIOWorkItem, + pContext); + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS) + KrnlHlprWorkItemDataDestroy(ppWorkItemData); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprWorkItemDataCreate() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @kernel_helper_function="KrnlHlprWorkItemDataCreate" + + Purpose: Allocates and populates a WORKITEM_DATA object with the classifyData, redirectData, + PIOWorkItem, and context supplied.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_At_(*ppWorkItemData, _Pre_ _Null_) +_When_(return != STATUS_SUCCESS, _At_(*ppWorkItemData, _Post_ _Null_)) +_When_(return == STATUS_SUCCESS, _At_(*ppWorkItemData, _Post_ _Notnull_ __drv_allocatesMem(Pool))) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprWorkItemDataCreate(_Outptr_ WORKITEM_DATA** ppWorkItemData, + _In_ CLASSIFY_DATA* pClassifyData, + _In_ REDIRECT_DATA* pRedirectData, + _In_opt_ PIO_WORKITEM pIOWorkItem, /* 0 */ + _In_opt_ VOID* pContext) /* 0 */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprWorkItemDataCreate()\n"); + +#endif /// DBG + + NT_ASSERT(ppWorkItemData); + NT_ASSERT(pClassifyData); + NT_ASSERT(pRedirectData); + + NTSTATUS status = STATUS_SUCCESS; + + HLPR_NEW(*ppWorkItemData, + WORKITEM_DATA, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(*ppWorkItemData, + status); + + KrnlHlprWorkItemDataPopulate(*ppWorkItemData, + pClassifyData, + pRedirectData, + pIOWorkItem, + pContext); + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS) + KrnlHlprWorkItemDataDestroy(ppWorkItemData); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprWorkItemDataCreate() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @kernel_helper_function="KrnlHlprWorkItemDataCreate" + + Purpose: Allocates and populates a WORKITEM_DATA object with the notifyData, PIOWorkItem, + and context supplied.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_At_(*ppWorkItemData, _Pre_ _Null_) +_When_(return != STATUS_SUCCESS, _At_(*ppWorkItemData, _Post_ _Null_)) +_When_(return == STATUS_SUCCESS, _At_(*ppWorkItemData, _Post_ _Notnull_ __drv_allocatesMem(Pool))) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprWorkItemDataCreate(_Outptr_ WORKITEM_DATA** ppWorkItemData, + _In_ NOTIFY_DATA* pNotifyData, + _In_opt_ PIO_WORKITEM pIOWorkItem, /* 0 */ + _In_opt_ VOID* pContext) /* 0 */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprWorkItemDataCreate()\n"); + +#endif /// DBG + + NT_ASSERT(ppWorkItemData); + NT_ASSERT(pNotifyData); + + NTSTATUS status = STATUS_SUCCESS; + + HLPR_NEW(*ppWorkItemData, + WORKITEM_DATA, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(*ppWorkItemData, + status); + + KrnlHlprWorkItemDataPopulate(*ppWorkItemData, + pNotifyData, + pIOWorkItem, + pContext); + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS) + KrnlHlprWorkItemDataDestroy(ppWorkItemData); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprWorkItemDataCreate() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @kernel_helper_function="KrnlHlprWorkItemDataCreate" + + Purpose: Allocates and populates a WORKITEM_DATA object with the pendData, PIOWorkItem, and + context supplied.
+
+ Notes:
+
+ MSDN_Ref:
+*/ +_At_(*ppWorkItemData, _Pre_ _Null_) +_When_(return != STATUS_SUCCESS, _At_(*ppWorkItemData, _Post_ _Null_)) +_When_(return == STATUS_SUCCESS, _At_(*ppWorkItemData, _Post_ _Notnull_ __drv_allocatesMem(Pool))) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprWorkItemDataCreate(_Outptr_ WORKITEM_DATA** ppWorkItemData, + _In_ PEND_DATA* pPendData, + _In_opt_ PIO_WORKITEM pIOWorkItem, /* 0 */ + _In_opt_ VOID* pContext) /* 0 */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprWorkItemDataCreate()\n"); + +#endif /// DBG + + NT_ASSERT(ppWorkItemData); + NT_ASSERT(pPendData); + + NTSTATUS status = STATUS_SUCCESS; + + HLPR_NEW(*ppWorkItemData, + WORKITEM_DATA, + WFPSAMPLER_SYSLIB_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(*ppWorkItemData, + status); + + KrnlHlprWorkItemDataPopulate(*ppWorkItemData, + pPendData, + pIOWorkItem, + pContext); + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS) + KrnlHlprWorkItemDataDestroy(ppWorkItemData); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprWorkItemDataCreate() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @kernel_helper_function="KrnlHlprWorkItemQueue" + + Purpose: Queue a workitem for later execution at PASSIVE_LEVEL.
+
+ Notes: Data is expected to be obtained by other means (i.e. LIST_ENTRY)
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/FF548276.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/FF549466.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprWorkItemQueue(_In_ PDEVICE_OBJECT pWDMDevice, + _In_ IO_WORKITEM_ROUTINE* pWorkItemFn) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprWorkItemQueue()\n"); + +#endif /// DBG + + NT_ASSERT(pWDMDevice); + NT_ASSERT(pWorkItemFn); + + NTSTATUS status = STATUS_SUCCESS; + PIO_WORKITEM pIOWorkItem = 0; + +#pragma warning(push) +#pragma warning(disable: 6014) /// pIOWorkItem is cleaned up in pWorkItemFn + + pIOWorkItem = IoAllocateWorkItem(pWDMDevice); + if(pIOWorkItem == 0) + { + status = STATUS_UNSUCCESSFUL; + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! KrnlHlprWorkItemQueue : IoAllocateWorkItem() [status: %#x]\n", + status); + + HLPR_BAIL; + } + +#pragma warning(pop) + + IoQueueWorkItem(pIOWorkItem, + pWorkItemFn, + DelayedWorkQueue, + (PVOID)pIOWorkItem); + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS && + pIOWorkItem) + IoFreeWorkItem(pIOWorkItem); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprWorkItemQueue() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + + +/** + @kernel_helper_function="KrnlHlprWorkItemQueue" + + Purpose: Queue a workitem for later execution at PASSIVE_LEVEL.
+
+ Notes: INJECTION_DATA specific.
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/FF548276.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/FF549466.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprWorkItemQueue(_In_ PDEVICE_OBJECT pWDMDevice, + _In_ IO_WORKITEM_ROUTINE* pWorkItemFn, + _In_ CLASSIFY_DATA* pClassifyData, + _In_opt_ INJECTION_DATA* pInjectionData, /* 0 */ + _In_opt_ VOID* pContext) /* 0 */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprWorkItemQueue()\n"); + +#endif /// DBG + + NT_ASSERT(pWDMDevice); + NT_ASSERT(pWorkItemFn); + NT_ASSERT(pClassifyData); + + NTSTATUS status = STATUS_SUCCESS; + PIO_WORKITEM pIOWorkItem = 0; + WORKITEM_DATA* pWorkItemData = 0; + +#pragma warning(push) +#pragma warning(disable: 6014) /// pIOWorkItem is cleaned up in KrnlHlprWorkItemDataDestroy + + pIOWorkItem = IoAllocateWorkItem(pWDMDevice); + if(pIOWorkItem == 0) + { + status = STATUS_UNSUCCESSFUL; + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! KrnlHlprWorkItemQueue : IoAllocateWorkItem() [status: %#x]\n", + status); + + HLPR_BAIL; + } + +#pragma warning(pop) + + status = KrnlHlprWorkItemDataCreate(&pWorkItemData, + pClassifyData, + pInjectionData, + pIOWorkItem, + pContext); + HLPR_BAIL_ON_FAILURE(status); + + IoQueueWorkItem(pWorkItemData->pIOWorkItem, + pWorkItemFn, + DelayedWorkQueue, + (PVOID)pWorkItemData); + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS && + pWorkItemData) + KrnlHlprWorkItemDataDestroy(&pWorkItemData); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprWorkItemQueue() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @kernel_helper_function="KrnlHlprWorkItemQueue" + + Purpose: Queue a workitem for later execution at PASSIVE_LEVEL.
+
+ Notes: PEND_DATA specific.
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/FF548276.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/FF549466.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprWorkItemQueue(_In_ PDEVICE_OBJECT pWDMDevice, + _In_ IO_WORKITEM_ROUTINE* pWorkItemFn, + _In_ CLASSIFY_DATA* pClassifyData, + _In_ PEND_DATA* pPendData, + _In_opt_ VOID* pContext) /* 0 */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprWorkItemQueue()\n"); + +#endif /// DBG + + NT_ASSERT(pWDMDevice); + NT_ASSERT(pWorkItemFn); + NT_ASSERT(pClassifyData); + NT_ASSERT(pPendData); + + NTSTATUS status = STATUS_SUCCESS; + PIO_WORKITEM pIOWorkItem = 0; + WORKITEM_DATA* pWorkItemData = 0; + +#pragma warning(push) +#pragma warning(disable: 6014) /// pIOWorkItem is cleaned up in KrnlHlprWorkItemDataDestroy + + pIOWorkItem = IoAllocateWorkItem(pWDMDevice); + if(pIOWorkItem == 0) + { + status = STATUS_UNSUCCESSFUL; + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! KrnlHlprWorkItemQueue : IoAllocateWorkItem() [status: %#x]\n", + status); + + HLPR_BAIL; + } + +#pragma warning(pop) + + status = KrnlHlprWorkItemDataCreate(&pWorkItemData, + pClassifyData, + pPendData, + pIOWorkItem, + pContext); + HLPR_BAIL_ON_FAILURE(status); + + IoQueueWorkItem(pWorkItemData->pIOWorkItem, + pWorkItemFn, + DelayedWorkQueue, + (PVOID)pWorkItemData); + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS && + pWorkItemData) + KrnlHlprWorkItemDataDestroy(&pWorkItemData); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprWorkItemQueue() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @kernel_helper_function="KrnlHlprWorkItemQueue" + + Purpose: Queue a workitem for later execution at PASSIVE_LEVEL.
+
+ Notes: REDIRECT_DATA specific.
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/FF548276.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/FF549466.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprWorkItemQueue(_In_ PDEVICE_OBJECT pWDMDevice, + _In_ IO_WORKITEM_ROUTINE* pWorkItemFn, + _In_ CLASSIFY_DATA* pClassifyData, + _In_ REDIRECT_DATA* pRedirectData, + _In_opt_ VOID* pContext) /* 0 */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprWorkItemQueue()\n"); + +#endif /// DBG + + NT_ASSERT(pWDMDevice); + NT_ASSERT(pWorkItemFn); + NT_ASSERT(pClassifyData); + NT_ASSERT(pRedirectData); + + NTSTATUS status = STATUS_SUCCESS; + PIO_WORKITEM pIOWorkItem = 0; + WORKITEM_DATA* pWorkItemData = 0; + +#pragma warning(push) +#pragma warning(disable: 6014) /// pIOWorkItem is cleaned up in KrnlHlprWorkItemDataDestroy + + pIOWorkItem = IoAllocateWorkItem(pWDMDevice); + if(pIOWorkItem == 0) + { + status = STATUS_UNSUCCESSFUL; + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! KrnlHlprWorkItemQueue : IoAllocateWorkItem() [status: %#x]\n", + status); + + HLPR_BAIL; + } + +#pragma warning(pop) + + status = KrnlHlprWorkItemDataCreate(&pWorkItemData, + pClassifyData, + pRedirectData, + pIOWorkItem, + pContext); + HLPR_BAIL_ON_FAILURE(status); + + IoQueueWorkItem(pWorkItemData->pIOWorkItem, + pWorkItemFn, + DelayedWorkQueue, + (PVOID)pWorkItemData); + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS && + pWorkItemData) + KrnlHlprWorkItemDataDestroy(&pWorkItemData); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprWorkItemQueue() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @kernel_helper_function="KrnlHlprWorkItemQueue" + + Purpose: Queue a workitem for later execution at PASSIVE_LEVEL.
+
+ Notes: NOTIFY_DATA specific.
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/FF548276.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/FF549466.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprWorkItemQueue(_In_ PDEVICE_OBJECT pWDMDevice, + _In_ IO_WORKITEM_ROUTINE* pWorkItemFn, + _In_ NOTIFY_DATA* pNotifyData, + _In_opt_ VOID* pContext) /* 0 */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprWorkItemQueue()\n"); + +#endif /// DBG + + NT_ASSERT(pWDMDevice); + NT_ASSERT(pWorkItemFn); + NT_ASSERT(pNotifyData); + + NTSTATUS status = STATUS_SUCCESS; + PIO_WORKITEM pIOWorkItem = 0; + WORKITEM_DATA* pWorkItemData = 0; + +#pragma warning(push) +#pragma warning(disable: 6014) /// pIOWorkItem is cleaned up in KrnlHlprWorkItemDataDestroy + + pIOWorkItem = IoAllocateWorkItem(pWDMDevice); + if(pIOWorkItem == 0) + { + status = STATUS_UNSUCCESSFUL; + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! KrnlHlprWorkItemQueue : IoAllocateWorkItem() [status: %#x]\n", + status); + + HLPR_BAIL; + } + +#pragma warning(pop) + + status = KrnlHlprWorkItemDataCreate(&pWorkItemData, + pNotifyData, + pIOWorkItem, + pContext); + HLPR_BAIL_ON_FAILURE(status); + + IoQueueWorkItem(pWorkItemData->pIOWorkItem, + pWorkItemFn, + DelayedWorkQueue, + (PVOID)pWorkItemData); + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS && + pWorkItemData) + KrnlHlprWorkItemDataDestroy(&pWorkItemData); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprWorkItemQueue() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @kernel_helper_function="KrnlHlprWorkItemQueue" + + Purpose: Queue a workitem for later execution at PASSIVE_LEVEL.
+
+ Notes: PEND_DATA specific.
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/FF548276.aspx
+ HTTP://MSDN.Microsoft.com/En-US/Library/FF549466.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprWorkItemQueue(_In_ PDEVICE_OBJECT pWDMDevice, + _In_ IO_WORKITEM_ROUTINE* pWorkItemFn, + _In_ PEND_DATA* pPendData, + _In_opt_ VOID* pContext) /* 0 */ +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprWorkItemQueue()\n"); + +#endif /// DBG + + NT_ASSERT(pWDMDevice); + NT_ASSERT(pWorkItemFn); + NT_ASSERT(pPendData); + + NTSTATUS status = STATUS_SUCCESS; + PIO_WORKITEM pIOWorkItem = 0; + WORKITEM_DATA* pWorkItemData = 0; + +#pragma warning(push) +#pragma warning(disable: 6014) /// pIOWorkItem is cleaned up in KrnlHlprWorkItemDataDestroy + + pIOWorkItem = IoAllocateWorkItem(pWDMDevice); + if(pIOWorkItem == 0) + { + status = STATUS_UNSUCCESSFUL; + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! KrnlHlprWorkItemQueue : IoAllocateWorkItem() [status: %#x]\n", + status); + + HLPR_BAIL; + } + +#pragma warning(pop) + + status = KrnlHlprWorkItemDataCreate(&pWorkItemData, + pPendData, + pIOWorkItem, + pContext); + HLPR_BAIL_ON_FAILURE(status); + + IoQueueWorkItem(pWorkItemData->pIOWorkItem, + pWorkItemFn, + DelayedWorkQueue, + (PVOID)pWorkItemData); + + HLPR_BAIL_LABEL: + + if(status != STATUS_SUCCESS && + pWorkItemData) + KrnlHlprWorkItemDataDestroy(&pWorkItemData); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprWorkItemQueue() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @kernel_helper_function="KrnlHlprWorkItemSleep" + + Purpose: Delay the thread for the specified time.
+
+ Notes:
+
+ MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/FF551986.aspx
+*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(APC_LEVEL) +_IRQL_requires_same_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprWorkItemSleep(_In_ UINT32 numMS) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> KrnlHlprWorkItemSleep()\n"); + +#endif /// DBG + + NT_ASSERT(numMS); + + NTSTATUS status = STATUS_SUCCESS; + INT64 interval = numMS * -10000i64; /// (numMS[milli] * (-1[relative] * 1000[milli to micro] * 1000[micro to nano]) / 100[ns] + + status = KeDelayExecutionThread(KernelMode, + FALSE, + (PLARGE_INTEGER)&interval); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- KrnlHlprWorkItemSleep() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} diff --git a/network/trans/WFPSampler/syslib/HelperFunctions_WorkItems.h b/network/trans/WFPSampler/syslib/HelperFunctions_WorkItems.h new file mode 100644 index 000000000..cbbc77bc3 --- /dev/null +++ b/network/trans/WFPSampler/syslib/HelperFunctions_WorkItems.h @@ -0,0 +1,223 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// HelperFunctions_WorkItems.h +// +// Abstract: +// This module contains prototypes for kernel helper functions that assist with IO_WORKITEM +// routines. +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Add support for pending at +// FWPM_LAYER_ALE_ENDPOINT_CLOSURE. +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef HELPERFUNCTIONS_WORKITEMS_H +#define HELPERFUNCTIONS_WORKITEMS_H + +typedef struct WORKITEM_DATA_ +{ + PIO_WORKITEM pIOWorkItem; + union + { + CLASSIFY_DATA* pClassifyData; + NOTIFY_DATA* pNotifyData; + }; + union + { + INJECTION_DATA* pInjectionData; + REDIRECT_DATA* pRedirectData; + PEND_DATA* pPendData; + }; + VOID* pContext; +}WORKITEM_DATA, *PWORKITEM_DATA; + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprWorkItemDataPurge(_Inout_ WORKITEM_DATA* pWorkItemData); + +_At_(*ppWorkItemData, _Pre_ _Notnull_) +_At_(*ppWorkItemData, _Post_ _Null_ __drv_freesMem(Pool)) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Success_(*ppWorkItemData == 0) +inline VOID KrnlHlprWorkItemDataDestroy(_Inout_ WORKITEM_DATA** ppWorkItemData); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprWorkItemDataPopulate(_Inout_ WORKITEM_DATA* pWorkItemData, + _In_ CLASSIFY_DATA* pClassifyData, + _In_opt_ INJECTION_DATA* pInjectionData = 0, + _In_opt_ PIO_WORKITEM pIOWorkItem = 0, + _In_opt_ VOID* pContext = 0); +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprWorkItemDataPopulate(_Inout_ WORKITEM_DATA* pWorkItemData, + _In_ CLASSIFY_DATA* pClassifyData, + _In_ PEND_DATA* pPendData, + _In_opt_ PIO_WORKITEM pIOWorkItem = 0, + _In_opt_ VOID* pContext = 0); +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprWorkItemDataPopulate(_Inout_ WORKITEM_DATA* pWorkItemData, + _In_ CLASSIFY_DATA* pClassifyData, + _In_ REDIRECT_DATA* pRedirectData, + _In_opt_ PIO_WORKITEM pIOWorkItem = 0, + _In_opt_ VOID** pContext = 0); +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprWorkItemDataPopulate(_Inout_ WORKITEM_DATA* pWorkItemData, + _In_ NOTIFY_DATA* pNotifyData, + _In_opt_ PIO_WORKITEM pIOWorkItem = 0, + _In_opt_ VOID* pContext = 0); +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +inline VOID KrnlHlprWorkItemDataPopulate(_Inout_ WORKITEM_DATA* pWorkItemData, + _In_ PEND_DATA* pPendData, + _In_opt_ PIO_WORKITEM pIOWorkItem = 0, + _In_opt_ VOID* pContext = 0); + +_At_(*ppWorkItemData, _Pre_ _Null_) +_When_(return != STATUS_SUCCESS, _At_(*ppWorkItemData, _Post_ _Null_)) +_When_(return == STATUS_SUCCESS, _At_(*ppWorkItemData, _Post_ _Notnull_ __drv_allocatesMem(Pool))) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprWorkItemDataCreate(_Outptr_ WORKITEM_DATA** ppWorkItemData, + _In_ CLASSIFY_DATA* pClassifyData, + _In_opt_ INJECTION_DATA* pInjectionData = 0, + _In_opt_ PIO_WORKITEM pIOWorkItem = 0, + _In_opt_ VOID* pContext = 0); +_At_(*ppWorkItemData, _Pre_ _Null_) +_When_(return != STATUS_SUCCESS, _At_(*ppWorkItemData, _Post_ _Null_)) +_When_(return == STATUS_SUCCESS, _At_(*ppWorkItemData, _Post_ _Notnull_ __drv_allocatesMem(Pool))) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprWorkItemDataCreate(_Outptr_ WORKITEM_DATA** ppWorkItemData, + _In_ CLASSIFY_DATA* pClassifyData, + _In_ PEND_DATA* pPendData, + _In_opt_ PIO_WORKITEM pIOWorkItem = 0, + _In_opt_ VOID* pContext = 0); +_At_(*ppWorkItemData, _Pre_ _Null_) +_When_(return != STATUS_SUCCESS, _At_(*ppWorkItemData, _Post_ _Null_)) +_When_(return == STATUS_SUCCESS, _At_(*ppWorkItemData, _Post_ _Notnull_ __drv_allocatesMem(Pool))) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprWorkItemDataCreate(_Outptr_ WORKITEM_DATA** ppWorkItemData, + _In_ CLASSIFY_DATA* pClassifyData, + _In_ REDIRECT_DATA* pRedirectData, + _In_opt_ PIO_WORKITEM pIOWorkItem = 0, + _In_opt_ VOID* pContext = 0); +_At_(*ppWorkItemData, _Pre_ _Null_) +_When_(return != STATUS_SUCCESS, _At_(*ppWorkItemData, _Post_ _Null_)) +_When_(return == STATUS_SUCCESS, _At_(*ppWorkItemData, _Post_ _Notnull_ __drv_allocatesMem(Pool))) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprWorkItemDataCreate(_Outptr_ WORKITEM_DATA** ppWorkItemData, + _In_ NOTIFY_DATA* pNotifyData, + _In_opt_ PIO_WORKITEM pIOWorkItem = 0, + _In_opt_ VOID* pContext = 0); + +_At_(*ppWorkItemData, _Pre_ _Null_) +_When_(return != STATUS_SUCCESS, _At_(*ppWorkItemData, _Post_ _Null_)) +_When_(return == STATUS_SUCCESS, _At_(*ppWorkItemData, _Post_ _Notnull_ __drv_allocatesMem(Pool))) +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprWorkItemDataCreate(_Outptr_ WORKITEM_DATA** ppWorkItemData, + _In_ PEND_DATA* pPendData, + _In_opt_ PIO_WORKITEM pIOWorkItem = 0, + _In_opt_ VOID* pContext = 0); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprWorkItemQueue(_In_ PDEVICE_OBJECT pWDMDevice, + _In_ IO_WORKITEM_ROUTINE* pWorkItemFn); +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprWorkItemQueue(_In_ PDEVICE_OBJECT pWDMDevice, + _In_ IO_WORKITEM_ROUTINE* pWorkItemFn, + _In_ CLASSIFY_DATA* pClassifyData, + _In_opt_ INJECTION_DATA* pInjectionData = 0, + _In_opt_ VOID* pContext = 0); +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprWorkItemQueue(_In_ PDEVICE_OBJECT pWDMDevice, + _In_ IO_WORKITEM_ROUTINE* pWorkItemFn, + _In_ CLASSIFY_DATA* pClassifyData, + _In_ PEND_DATA* pPendData, + _In_opt_ VOID* pContext = 0); +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprWorkItemQueue(_In_ PDEVICE_OBJECT pWDMDevice, + _In_ IO_WORKITEM_ROUTINE* pWorkItemFn, + _In_ CLASSIFY_DATA* pClassifyData, + _In_ REDIRECT_DATA* pRedirectData, + _In_opt_ VOID* pContext = 0); +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprWorkItemQueue(_In_ PDEVICE_OBJECT pWDMDevice, + _In_ IO_WORKITEM_ROUTINE* pWorkItemFn, + _In_ NOTIFY_DATA* pNotifyData, + _In_opt_ VOID* pContext = 0); +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprWorkItemQueue(_In_ PDEVICE_OBJECT pWDMDevice, + _In_ IO_WORKITEM_ROUTINE* pWorkItemFn, + _In_ PEND_DATA* pPendData, + _In_opt_ VOID* pContext = 0); + +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(APC_LEVEL) +_IRQL_requires_same_ +_Success_(return == STATUS_SUCCESS) +NTSTATUS KrnlHlprWorkItemSleep(_In_ UINT32 numMS); + +#endif /// HELPERFUNCTIONS_WORKITEMS_H diff --git a/network/trans/WFPSampler/syslib/WFPSampler.vcxproj b/network/trans/WFPSampler/syslib/WFPSampler.vcxproj new file mode 100644 index 000000000..1718f5d9e --- /dev/null +++ b/network/trans/WFPSampler/syslib/WFPSampler.vcxproj @@ -0,0 +1,201 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {512C5A72-2415-45CA-A1B2-851C97781F9E} + $(MSBuildProjectName) + 1 + 11 + false + Debug + Win32 + {86BC8178-D674-4761-AA8D-A4D4FAA95D25} + + + + 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 + .\$(IntDir) + true + WFPSamplerCallouts + 53504657-6D61-6C70-6572-5F496E746572 + DbgPrintEx(COMPID,LEVEL,MSG,...) + WPP_INIT_TRACING + + + + WFPSampler + + + WFPSampler + + + WFPSampler + + + WFPSampler + + + + true + Level4 + %(PreprocessorDefinitions);WIN32_LEAN_AND_MEAN;UNICODE;_UNICODE;NDIS630 + %(AdditionalIncludeDirectories);..\inc;$(DDK_INC_PATH);$(IFSKIT_INC_PATH) + + + + + %(PreprocessorDefinitions);WIN32_LEAN_AND_MEAN;UNICODE;_UNICODE;NDIS630 + %(AdditionalIncludeDirectories);..\inc;$(DDK_INC_PATH);$(IFSKIT_INC_PATH) + + + %(PreprocessorDefinitions);WIN32_LEAN_AND_MEAN;UNICODE;_UNICODE;NDIS630 + %(AdditionalIncludeDirectories);..\inc;$(DDK_INC_PATH);$(IFSKIT_INC_PATH) + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\NTOSKrnl.lib;$(DDK_LIB_PATH)\FwpKClnt.lib;$(DDK_LIB_PATH)\NetIO.lib;$(DDK_LIB_PATH)\NDIS.lib;$(SDK_LIB_PATH)\UUID.lib + + + + + true + Level4 + %(PreprocessorDefinitions);WIN32_LEAN_AND_MEAN;UNICODE;_UNICODE;NDIS630 + %(AdditionalIncludeDirectories);..\inc;$(DDK_INC_PATH);$(IFSKIT_INC_PATH) + + + + + %(PreprocessorDefinitions);WIN32_LEAN_AND_MEAN;UNICODE;_UNICODE;NDIS630 + %(AdditionalIncludeDirectories);..\inc;$(DDK_INC_PATH);$(IFSKIT_INC_PATH) + + + %(PreprocessorDefinitions);WIN32_LEAN_AND_MEAN;UNICODE;_UNICODE;NDIS630 + %(AdditionalIncludeDirectories);..\inc;$(DDK_INC_PATH);$(IFSKIT_INC_PATH) + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\NTOSKrnl.lib;$(DDK_LIB_PATH)\FwpKClnt.lib;$(DDK_LIB_PATH)\NetIO.lib;$(DDK_LIB_PATH)\NDIS.lib;$(SDK_LIB_PATH)\UUID.lib + + + + + true + Level4 + %(PreprocessorDefinitions);WIN32_LEAN_AND_MEAN;UNICODE;_UNICODE;NDIS630 + %(AdditionalIncludeDirectories);..\inc;$(DDK_INC_PATH);$(IFSKIT_INC_PATH) + + + + + %(PreprocessorDefinitions);WIN32_LEAN_AND_MEAN;UNICODE;_UNICODE;NDIS630 + %(AdditionalIncludeDirectories);..\inc;$(DDK_INC_PATH);$(IFSKIT_INC_PATH) + + + %(PreprocessorDefinitions);WIN32_LEAN_AND_MEAN;UNICODE;_UNICODE;NDIS630 + %(AdditionalIncludeDirectories);..\inc;$(DDK_INC_PATH);$(IFSKIT_INC_PATH) + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\NTOSKrnl.lib;$(DDK_LIB_PATH)\FwpKClnt.lib;$(DDK_LIB_PATH)\NetIO.lib;$(DDK_LIB_PATH)\NDIS.lib;$(SDK_LIB_PATH)\UUID.lib + + + + + true + Level4 + %(PreprocessorDefinitions);WIN32_LEAN_AND_MEAN;UNICODE;_UNICODE;NDIS630 + %(AdditionalIncludeDirectories);..\inc;$(DDK_INC_PATH);$(IFSKIT_INC_PATH) + + + + + %(PreprocessorDefinitions);WIN32_LEAN_AND_MEAN;UNICODE;_UNICODE;NDIS630 + %(AdditionalIncludeDirectories);..\inc;$(DDK_INC_PATH);$(IFSKIT_INC_PATH) + + + %(PreprocessorDefinitions);WIN32_LEAN_AND_MEAN;UNICODE;_UNICODE;NDIS630 + %(AdditionalIncludeDirectories);..\inc;$(DDK_INC_PATH);$(IFSKIT_INC_PATH) + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\NTOSKrnl.lib;$(DDK_LIB_PATH)\FwpKClnt.lib;$(DDK_LIB_PATH)\NetIO.lib;$(DDK_LIB_PATH)\NDIS.lib;$(SDK_LIB_PATH)\UUID.lib + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/network/trans/WFPSampler/syslib/WFPSampler.vcxproj.Filters b/network/trans/WFPSampler/syslib/WFPSampler.vcxproj.Filters new file mode 100644 index 000000000..e8a37dd25 --- /dev/null +++ b/network/trans/WFPSampler/syslib/WFPSampler.vcxproj.Filters @@ -0,0 +1,56 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {0C5D633B-BA32-475A-A409-5938564ACC13} + + + h;hpp;hxx;hm;inl;inc;xsd + {D731A15D-8E93-4919-8EAA-1622C9379D8B} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {67EEF6D2-CB2C-4297-808D-6F916E6FF66A} + + + inf;inv;inx;mof;mc; + {BB02E02B-7BD9-4147-B016-07EF55164047} + + + + + Source Files + + + Source Files + + + Source Files + + + 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/trans/ddproxy/ddproxy.sln b/network/trans/ddproxy/ddproxy.sln index b420611ed..0b35cc649 100644 --- a/network/trans/ddproxy/ddproxy.sln +++ b/network/trans/ddproxy/ddproxy.sln @@ -3,7 +3,7 @@ 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", "{CBE3E4FB-B5D5-46F1-B97E-B8400FC372C8}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ddproxy", "sys\ddproxy.vcxproj", "{88969E59-3140-4A06-8E52-7E9483F6E42C}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {CBE3E4FB-B5D5-46F1-B97E-B8400FC372C8}.Debug|Win32.ActiveCfg = Debug|Win32 - {CBE3E4FB-B5D5-46F1-B97E-B8400FC372C8}.Debug|Win32.Build.0 = Debug|Win32 - {CBE3E4FB-B5D5-46F1-B97E-B8400FC372C8}.Release|Win32.ActiveCfg = Release|Win32 - {CBE3E4FB-B5D5-46F1-B97E-B8400FC372C8}.Release|Win32.Build.0 = Release|Win32 - {CBE3E4FB-B5D5-46F1-B97E-B8400FC372C8}.Debug|x64.ActiveCfg = Debug|x64 - {CBE3E4FB-B5D5-46F1-B97E-B8400FC372C8}.Debug|x64.Build.0 = Debug|x64 - {CBE3E4FB-B5D5-46F1-B97E-B8400FC372C8}.Release|x64.ActiveCfg = Release|x64 - {CBE3E4FB-B5D5-46F1-B97E-B8400FC372C8}.Release|x64.Build.0 = Release|x64 + {88969E59-3140-4A06-8E52-7E9483F6E42C}.Debug|Win32.ActiveCfg = Debug|Win32 + {88969E59-3140-4A06-8E52-7E9483F6E42C}.Debug|Win32.Build.0 = Debug|Win32 + {88969E59-3140-4A06-8E52-7E9483F6E42C}.Release|Win32.ActiveCfg = Release|Win32 + {88969E59-3140-4A06-8E52-7E9483F6E42C}.Release|Win32.Build.0 = Release|Win32 + {88969E59-3140-4A06-8E52-7E9483F6E42C}.Debug|x64.ActiveCfg = Debug|x64 + {88969E59-3140-4A06-8E52-7E9483F6E42C}.Debug|x64.Build.0 = Debug|x64 + {88969E59-3140-4A06-8E52-7E9483F6E42C}.Release|x64.ActiveCfg = Release|x64 + {88969E59-3140-4A06-8E52-7E9483F6E42C}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/network/trans/ddproxy/sys/ddproxy.inf b/network/trans/ddproxy/sys/ddproxy.inf index dd5049670..33be4df4e 100644 --- a/network/trans/ddproxy/sys/ddproxy.inf +++ b/network/trans/ddproxy/sys/ddproxy.inf @@ -9,7 +9,7 @@ Signature = "$Windows NT$" Class = WFPCALLOUTS ClassGuid = {57465043-616C-6C6F-7574-5F636C617373} - Provider = %Contoso% + Provider = %ProviderString% CatalogFile = DDProxy.cat DriverVer = 11/24/2014,14.24.55.836 @@ -57,7 +57,7 @@ HKR,"Parameters",,, [Strings] - Contoso = "Contoso Ltd." + ProviderString = "TODO-Set-Provider" 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 index 516c68a3a..09c0e9bc4 100644 --- a/network/trans/ddproxy/sys/ddproxy.vcxproj +++ b/network/trans/ddproxy/sys/ddproxy.vcxproj @@ -19,12 +19,12 @@ - {CBE3E4FB-B5D5-46F1-B97E-B8400FC372C8} + {88969E59-3140-4A06-8E52-7E9483F6E42C} $(MSBuildProjectName) 1 Debug Win32 - {4805FA90-5926-46C2-845B-2FC0F749B960} + {DA73B837-D1BB-4D0D-908B-717988311EE3} @@ -171,7 +171,6 @@ - diff --git a/network/trans/ddproxy/sys/ddproxy.vcxproj.Filters b/network/trans/ddproxy/sys/ddproxy.vcxproj.Filters index 6d7db8de8..031dba36b 100644 --- a/network/trans/ddproxy/sys/ddproxy.vcxproj.Filters +++ b/network/trans/ddproxy/sys/ddproxy.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {58F05CCD-742F-44E4-8DCB-61983C2AAC0C} + {8D3EB44B-EF28-4AEB-BB5B-C48CB78F4C77} h;hpp;hxx;hm;inl;inc;xsd - {6FA03C9B-94CF-45F5-ADA3-F23B54700362} + {E9F8ED7B-6F16-4D51-ADE0-E86D3E1BD5CE} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {6D410DAF-C0BC-40CF-8D06-F53EA77C7354} + {4EEA7F36-617A-4A62-BE16-0D2EBF088BEF} inf;inv;inx;mof;mc; - {6F97EAF5-00A6-4E35-9A51-AF4244397FF6} + {B59BFA52-A599-4C9B-9360-ABB5C95276BC} diff --git a/network/trans/inspect/inspect.sln b/network/trans/inspect/inspect.sln index eb1f632d3..e5e2cc7ee 100644 --- a/network/trans/inspect/inspect.sln +++ b/network/trans/inspect/inspect.sln @@ -3,7 +3,7 @@ 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", "{F850B54F-985A-485A-A99A-E787184F31D8}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "inspect", "sys\inspect.vcxproj", "{72A988A3-0603-4315-A76D-AADCC057DB3F}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {F850B54F-985A-485A-A99A-E787184F31D8}.Debug|Win32.ActiveCfg = Debug|Win32 - {F850B54F-985A-485A-A99A-E787184F31D8}.Debug|Win32.Build.0 = Debug|Win32 - {F850B54F-985A-485A-A99A-E787184F31D8}.Release|Win32.ActiveCfg = Release|Win32 - {F850B54F-985A-485A-A99A-E787184F31D8}.Release|Win32.Build.0 = Release|Win32 - {F850B54F-985A-485A-A99A-E787184F31D8}.Debug|x64.ActiveCfg = Debug|x64 - {F850B54F-985A-485A-A99A-E787184F31D8}.Debug|x64.Build.0 = Debug|x64 - {F850B54F-985A-485A-A99A-E787184F31D8}.Release|x64.ActiveCfg = Release|x64 - {F850B54F-985A-485A-A99A-E787184F31D8}.Release|x64.Build.0 = Release|x64 + {72A988A3-0603-4315-A76D-AADCC057DB3F}.Debug|Win32.ActiveCfg = Debug|Win32 + {72A988A3-0603-4315-A76D-AADCC057DB3F}.Debug|Win32.Build.0 = Debug|Win32 + {72A988A3-0603-4315-A76D-AADCC057DB3F}.Release|Win32.ActiveCfg = Release|Win32 + {72A988A3-0603-4315-A76D-AADCC057DB3F}.Release|Win32.Build.0 = Release|Win32 + {72A988A3-0603-4315-A76D-AADCC057DB3F}.Debug|x64.ActiveCfg = Debug|x64 + {72A988A3-0603-4315-A76D-AADCC057DB3F}.Debug|x64.Build.0 = Debug|x64 + {72A988A3-0603-4315-A76D-AADCC057DB3F}.Release|x64.ActiveCfg = Release|x64 + {72A988A3-0603-4315-A76D-AADCC057DB3F}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/network/trans/inspect/sys/inspect.inf b/network/trans/inspect/sys/inspect.inf index 125f96014..ae01d9757 100644 --- a/network/trans/inspect/sys/inspect.inf +++ b/network/trans/inspect/sys/inspect.inf @@ -9,7 +9,7 @@ Signature = "$Windows NT$" Class = WFPCALLOUTS ClassGuid = {57465043-616C-6C6F-7574-5F636C617373} - Provider = %Contoso% + Provider = %ProviderString% CatalogFile = Inspect.cat DriverVer = 11/24/2014,14.24.55.836 @@ -57,7 +57,7 @@ HKR,"Parameters",,, [Strings] - Contoso = "Contoso Ltd." - InspectDisk = "Traffic Inspect Installation Disk" + ProviderString = "TODO-Set-Provider" + 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 index cf5d67cb7..7c7ef16bf 100644 --- a/network/trans/inspect/sys/inspect.vcxproj +++ b/network/trans/inspect/sys/inspect.vcxproj @@ -19,12 +19,12 @@ - {F850B54F-985A-485A-A99A-E787184F31D8} + {72A988A3-0603-4315-A76D-AADCC057DB3F} $(MSBuildProjectName) 1 Debug Win32 - {1BE47893-DEA2-451D-B25B-B9485B05A13E} + {9F9DA1EB-580C-4DA1-A053-63D6E3EE2D2F} @@ -172,7 +172,6 @@ - diff --git a/network/trans/inspect/sys/inspect.vcxproj.Filters b/network/trans/inspect/sys/inspect.vcxproj.Filters index b8b1fd4bf..602cb4afc 100644 --- a/network/trans/inspect/sys/inspect.vcxproj.Filters +++ b/network/trans/inspect/sys/inspect.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {090AD1B0-632B-470E-A4F5-19B122F18E4A} + {8A1B2EA9-BFC6-4FD8-B2F2-0216B285279F} h;hpp;hxx;hm;inl;inc;xsd - {1F438D75-BA91-40B8-A34B-21A50AB1DB2D} + {A6A4AE15-BDDC-4797-B4FA-CE4EB8CF8E04} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {C99C7FAB-3055-4C61-98EC-42F2C637694C} + {BC4CCBE2-11CC-4557-8986-1D28735C2F00} inf;inv;inx;mof;mc; - {6EEC81C3-277F-4A14-831C-A84E74534082} + {B4358050-C623-4EE4-9397-7BA2FFC69B7E} diff --git a/network/trans/msnmntr/exe/monitor.vcxproj b/network/trans/msnmntr/exe/monitor.vcxproj index 471359840..ced8f9d31 100644 --- a/network/trans/msnmntr/exe/monitor.vcxproj +++ b/network/trans/msnmntr/exe/monitor.vcxproj @@ -19,11 +19,11 @@ - {FCC67889-AD0A-4C2F-A826-7D2AD714FC6A} + {73B16E02-2FA9-45E9-BAC6-D2371C627EE8} $(MSBuildProjectName) Debug Win32 - {BC6143C7-DF6C-4466-8AF0-FBE869102EEC} + {7817CD59-C9C2-4DE2-90D9-EF9E3F2489A8} @@ -237,7 +237,6 @@ - diff --git a/network/trans/msnmntr/exe/monitor.vcxproj.Filters b/network/trans/msnmntr/exe/monitor.vcxproj.Filters index 88c9cd7fe..64c456bec 100644 --- a/network/trans/msnmntr/exe/monitor.vcxproj.Filters +++ b/network/trans/msnmntr/exe/monitor.vcxproj.Filters @@ -3,15 +3,15 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {E70DDE2C-E112-4C1D-AFC4-39EA907AE848} + {C70F02F2-1801-4CE3-9B14-C3AF71C4A36B} h;hpp;hxx;hm;inl;inc;xsd - {16A331F5-433A-4195-BA31-CB3D082B7F8A} + {3CE61C1C-1EDB-4A29-82C7-5BDEC0568397} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {ECFC1BCD-5BF5-4402-B1D2-3C2CDE9E5291} + {0A993A34-46CF-4CCB-B3AE-1714A7538940} diff --git a/network/trans/msnmntr/msnmntr.sln b/network/trans/msnmntr/msnmntr.sln index 5a24d0b50..9c496dca7 100644 --- a/network/trans/msnmntr/msnmntr.sln +++ b/network/trans/msnmntr/msnmntr.sln @@ -3,13 +3,13 @@ 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", "{14AC7312-9D93-4A70-896D-97D9FDB48125}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Exe", "Exe", "{1F0A7D31-AD24-4250-A45D-1C6ED72D3AA9}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sys", "Sys", "{04F57F95-9237-4AE9-908B-41F30AA8BB1E}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sys", "Sys", "{E66178FA-3CEE-447F-ACEE-15052142FE60}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "monitor", "exe\monitor.vcxproj", "{FCC67889-AD0A-4C2F-A826-7D2AD714FC6A}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "monitor", "exe\monitor.vcxproj", "{73B16E02-2FA9-45E9-BAC6-D2371C627EE8}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "msnmntr", "sys\msnmntr.vcxproj", "{FB90ED51-1865-4ADC-B3A7-827EFE7302EA}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "msnmntr", "sys\msnmntr.vcxproj", "{8D90BF13-8184-4C0E-A938-99B22509F363}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -19,28 +19,28 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {FCC67889-AD0A-4C2F-A826-7D2AD714FC6A}.Debug|Win32.ActiveCfg = Debug|Win32 - {FCC67889-AD0A-4C2F-A826-7D2AD714FC6A}.Debug|Win32.Build.0 = Debug|Win32 - {FCC67889-AD0A-4C2F-A826-7D2AD714FC6A}.Release|Win32.ActiveCfg = Release|Win32 - {FCC67889-AD0A-4C2F-A826-7D2AD714FC6A}.Release|Win32.Build.0 = Release|Win32 - {FCC67889-AD0A-4C2F-A826-7D2AD714FC6A}.Debug|x64.ActiveCfg = Debug|x64 - {FCC67889-AD0A-4C2F-A826-7D2AD714FC6A}.Debug|x64.Build.0 = Debug|x64 - {FCC67889-AD0A-4C2F-A826-7D2AD714FC6A}.Release|x64.ActiveCfg = Release|x64 - {FCC67889-AD0A-4C2F-A826-7D2AD714FC6A}.Release|x64.Build.0 = Release|x64 - {FB90ED51-1865-4ADC-B3A7-827EFE7302EA}.Debug|Win32.ActiveCfg = Debug|Win32 - {FB90ED51-1865-4ADC-B3A7-827EFE7302EA}.Debug|Win32.Build.0 = Debug|Win32 - {FB90ED51-1865-4ADC-B3A7-827EFE7302EA}.Release|Win32.ActiveCfg = Release|Win32 - {FB90ED51-1865-4ADC-B3A7-827EFE7302EA}.Release|Win32.Build.0 = Release|Win32 - {FB90ED51-1865-4ADC-B3A7-827EFE7302EA}.Debug|x64.ActiveCfg = Debug|x64 - {FB90ED51-1865-4ADC-B3A7-827EFE7302EA}.Debug|x64.Build.0 = Debug|x64 - {FB90ED51-1865-4ADC-B3A7-827EFE7302EA}.Release|x64.ActiveCfg = Release|x64 - {FB90ED51-1865-4ADC-B3A7-827EFE7302EA}.Release|x64.Build.0 = Release|x64 + {73B16E02-2FA9-45E9-BAC6-D2371C627EE8}.Debug|Win32.ActiveCfg = Debug|Win32 + {73B16E02-2FA9-45E9-BAC6-D2371C627EE8}.Debug|Win32.Build.0 = Debug|Win32 + {73B16E02-2FA9-45E9-BAC6-D2371C627EE8}.Release|Win32.ActiveCfg = Release|Win32 + {73B16E02-2FA9-45E9-BAC6-D2371C627EE8}.Release|Win32.Build.0 = Release|Win32 + {73B16E02-2FA9-45E9-BAC6-D2371C627EE8}.Debug|x64.ActiveCfg = Debug|x64 + {73B16E02-2FA9-45E9-BAC6-D2371C627EE8}.Debug|x64.Build.0 = Debug|x64 + {73B16E02-2FA9-45E9-BAC6-D2371C627EE8}.Release|x64.ActiveCfg = Release|x64 + {73B16E02-2FA9-45E9-BAC6-D2371C627EE8}.Release|x64.Build.0 = Release|x64 + {8D90BF13-8184-4C0E-A938-99B22509F363}.Debug|Win32.ActiveCfg = Debug|Win32 + {8D90BF13-8184-4C0E-A938-99B22509F363}.Debug|Win32.Build.0 = Debug|Win32 + {8D90BF13-8184-4C0E-A938-99B22509F363}.Release|Win32.ActiveCfg = Release|Win32 + {8D90BF13-8184-4C0E-A938-99B22509F363}.Release|Win32.Build.0 = Release|Win32 + {8D90BF13-8184-4C0E-A938-99B22509F363}.Debug|x64.ActiveCfg = Debug|x64 + {8D90BF13-8184-4C0E-A938-99B22509F363}.Debug|x64.Build.0 = Debug|x64 + {8D90BF13-8184-4C0E-A938-99B22509F363}.Release|x64.ActiveCfg = Release|x64 + {8D90BF13-8184-4C0E-A938-99B22509F363}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {FCC67889-AD0A-4C2F-A826-7D2AD714FC6A} = {14AC7312-9D93-4A70-896D-97D9FDB48125} - {FB90ED51-1865-4ADC-B3A7-827EFE7302EA} = {04F57F95-9237-4AE9-908B-41F30AA8BB1E} + {73B16E02-2FA9-45E9-BAC6-D2371C627EE8} = {1F0A7D31-AD24-4250-A45D-1C6ED72D3AA9} + {8D90BF13-8184-4C0E-A938-99B22509F363} = {E66178FA-3CEE-447F-ACEE-15052142FE60} EndGlobalSection EndGlobal diff --git a/network/trans/msnmntr/sys/msnmntr.inf b/network/trans/msnmntr/sys/msnmntr.inf index 30d012b1a..17ebcb7c6 100644 --- a/network/trans/msnmntr/sys/msnmntr.inf +++ b/network/trans/msnmntr/sys/msnmntr.inf @@ -9,7 +9,7 @@ Signature = "$Windows NT$" Class = WFPCALLOUTS ClassGuid = {57465043-616C-6C6F-7574-5F636C617373} - Provider = %Contoso% + Provider = %ProviderString% CatalogFile = MSNMntr.cat DriverVer = 11/24/2014,14.24.55.836 @@ -48,7 +48,7 @@ ServiceBinary = %12%\MSNMntr.sys ; %WinDir%\System32\Drivers\MSNMntr.sys [Strings] - Contoso = "Contoso Ltd." + ProviderString = "TODO-Set-Provider" 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 index fd2ee8a8d..3e5e6b631 100644 --- a/network/trans/msnmntr/sys/msnmntr.vcxproj +++ b/network/trans/msnmntr/sys/msnmntr.vcxproj @@ -19,12 +19,12 @@ - {FB90ED51-1865-4ADC-B3A7-827EFE7302EA} + {8D90BF13-8184-4C0E-A938-99B22509F363} $(MSBuildProjectName) 1 Debug Win32 - {37A322AB-C708-4912-A65E-32CF743F1620} + {CBB4D2BA-9991-40A9-BDA7-A164E72B50A4} @@ -180,7 +180,6 @@ - diff --git a/network/trans/msnmntr/sys/msnmntr.vcxproj.Filters b/network/trans/msnmntr/sys/msnmntr.vcxproj.Filters index 7c24c2320..c367ced72 100644 --- a/network/trans/msnmntr/sys/msnmntr.vcxproj.Filters +++ b/network/trans/msnmntr/sys/msnmntr.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {C234D0E2-4C06-4AD3-911B-E5B20C7AA72D} + {21B35188-8ABA-4678-8D98-453C58E58904} h;hpp;hxx;hm;inl;inc;xsd - {65D341DD-B365-45C9-9EDE-816F5EE76E72} + {203B4798-2BF9-4998-8E54-C0474AD49CB7} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {023B8D22-079E-43B6-8C96-623C9ED0B1F1} + {B3C8566B-2D21-4AA7-BC03-4EE7DED7DD43} inf;inv;inx;mof;mc; - {D2F25005-E168-4EEF-8F96-23805D06AAAE} + {6DB69CEC-0955-4458-8D01-665D15B180CA} diff --git a/network/trans/stmedit/stmedit.sln b/network/trans/stmedit/stmedit.sln index b626c402a..e99fea000 100644 --- a/network/trans/stmedit/stmedit.sln +++ b/network/trans/stmedit/stmedit.sln @@ -3,7 +3,7 @@ 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", "{047B99BF-F490-4938-BFDA-3697680EE8AB}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "stmedit", "sys\stmedit.vcxproj", "{00B26024-D8C5-40FD-A6C7-BA15FD324FD0}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {047B99BF-F490-4938-BFDA-3697680EE8AB}.Debug|Win32.ActiveCfg = Debug|Win32 - {047B99BF-F490-4938-BFDA-3697680EE8AB}.Debug|Win32.Build.0 = Debug|Win32 - {047B99BF-F490-4938-BFDA-3697680EE8AB}.Release|Win32.ActiveCfg = Release|Win32 - {047B99BF-F490-4938-BFDA-3697680EE8AB}.Release|Win32.Build.0 = Release|Win32 - {047B99BF-F490-4938-BFDA-3697680EE8AB}.Debug|x64.ActiveCfg = Debug|x64 - {047B99BF-F490-4938-BFDA-3697680EE8AB}.Debug|x64.Build.0 = Debug|x64 - {047B99BF-F490-4938-BFDA-3697680EE8AB}.Release|x64.ActiveCfg = Release|x64 - {047B99BF-F490-4938-BFDA-3697680EE8AB}.Release|x64.Build.0 = Release|x64 + {00B26024-D8C5-40FD-A6C7-BA15FD324FD0}.Debug|Win32.ActiveCfg = Debug|Win32 + {00B26024-D8C5-40FD-A6C7-BA15FD324FD0}.Debug|Win32.Build.0 = Debug|Win32 + {00B26024-D8C5-40FD-A6C7-BA15FD324FD0}.Release|Win32.ActiveCfg = Release|Win32 + {00B26024-D8C5-40FD-A6C7-BA15FD324FD0}.Release|Win32.Build.0 = Release|Win32 + {00B26024-D8C5-40FD-A6C7-BA15FD324FD0}.Debug|x64.ActiveCfg = Debug|x64 + {00B26024-D8C5-40FD-A6C7-BA15FD324FD0}.Debug|x64.Build.0 = Debug|x64 + {00B26024-D8C5-40FD-A6C7-BA15FD324FD0}.Release|x64.ActiveCfg = Release|x64 + {00B26024-D8C5-40FD-A6C7-BA15FD324FD0}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/network/trans/stmedit/sys/stmedit.inf b/network/trans/stmedit/sys/stmedit.inf index 1e19c7872..b9ee15eab 100644 --- a/network/trans/stmedit/sys/stmedit.inf +++ b/network/trans/stmedit/sys/stmedit.inf @@ -9,7 +9,7 @@ Signature = "$Windows NT$" Class = WFPCALLOUTS ClassGuid = {57465043-616C-6C6F-7574-5F636C617373} - Provider = %Contoso% + Provider = %ProviderString% CatalogFile = StmEdit.cat DriverVer = 11/24/2014,14.24.55.836 @@ -60,7 +60,7 @@ HKR,"Parameters",,, [Strings] - Contoso = "Contoso Ltd." + ProviderString = "TODO-Set-Provider" 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 index 0a3e35b0f..6194a5108 100644 --- a/network/trans/stmedit/sys/stmedit.vcxproj +++ b/network/trans/stmedit/sys/stmedit.vcxproj @@ -19,12 +19,12 @@ - {047B99BF-F490-4938-BFDA-3697680EE8AB} + {00B26024-D8C5-40FD-A6C7-BA15FD324FD0} $(MSBuildProjectName) 1 Debug Win32 - {E10325C3-F867-4F5D-BD53-F06AA0782EB0} + {4DEE9F36-71EC-4057-8531-96C8B4624E91} @@ -172,7 +172,6 @@ - diff --git a/network/trans/stmedit/sys/stmedit.vcxproj.Filters b/network/trans/stmedit/sys/stmedit.vcxproj.Filters index 474bda82e..671a946af 100644 --- a/network/trans/stmedit/sys/stmedit.vcxproj.Filters +++ b/network/trans/stmedit/sys/stmedit.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {3D06BA82-AC80-46D1-B13E-F01845AF9A9C} + {4EB0EC22-2845-4C9E-B03A-227073F198E7} h;hpp;hxx;hm;inl;inc;xsd - {2D8AF9DE-2497-4917-9CCF-7C147BB503A4} + {3953E48D-12FA-4B3E-ABA3-BEE2AFF7C284} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {B9C19587-10DE-473E-AF48-2BDC5E84B3D7} + {D5A362D5-FA75-4E10-9D64-22B6CCB57052} inf;inv;inx;mof;mc; - {02D242A6-B147-4BA5-BCAA-8086413C2C7E} + {9B77DAC7-BF0A-4BAD-B6E0-C663693F98BB} diff --git a/network/wlan/ihvfrm/arm/rc4utils.dll b/network/wlan/ihvfrm/arm/rc4utils.dll new file mode 100644 index 0000000000000000000000000000000000000000..9cc3e82fd86427e0e8ad7626083594e9270f4e21 GIT binary patch literal 36688 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 literal 0 HcmV?d00001 diff --git a/network/wlan/ihvfrm/arm/rc4utils.lib b/network/wlan/ihvfrm/arm/rc4utils.lib new file mode 100644 index 0000000000000000000000000000000000000000..3c1aad8b35abb990959b2b2aee103f7ad5ed395a GIT binary patch literal 2688 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 literal 0 HcmV?d00001 diff --git a/network/wlan/ihvfrm/arm64/rc4utils.lib b/network/wlan/ihvfrm/arm64/rc4utils.lib new file mode 100644 index 0000000000000000000000000000000000000000..10972b9be8f607db5b3a717f539025d0b917ddb4 GIT binary patch literal 2696 zcmcImJ5L)y5dN@v7>ghjNSVk;B!Pw)V;d7i(E&~b0)utf^>OiCf|VmC_EAu!qsmV} zlqo2vT&3a{q)eG2T_mJ*X~~T52H%}+unAddZ+9NMGqdx}?oGbXtEJtS(PuG#n@i3w zWHO8K_?({~cgguo!kz6z0o((^N5DWG7`y-?t4<%D2DnEjoE|wNKK0=f?wYoi%WtU# zE&FPFMP17mwp2|mC5v|1szK8>iyIrQ8u%nCjzVFzxVf%nmkLYBT2@Xkf1su1y?sq6 zb;a57b=5T2&BL5-o7J*zxmum1!q4l~8ZXQ1rT3;CSQHS-nygu>9`4(!Sv#<7($V)g z9QNrt4Uw^**DP%j_nf)KX67Q4x}z{q9{?@}fmLEm4+9gF(=(c%(410>ehgs%5e%Xa zArBS4I&<;9UF;$YX28q$MJ$5A`Q8pMel*;i2fs$p=T@oDbH6y?e`n+hq-PDw;(Xpz zeaXelRrMLCVf^}7FOK$6ZBeec%^K#LgLhNg4h1aJ2iSd!omrg4lV8#&Hm6sEv4odW!L}A!7b%h)9Si>c{tUz{*B8^XPvW#lp8yQ zAWky-I!!A$ig9;fQ*X(oK~mGObnigDg0C z?zzfRa#UQUTxH&-K#?j-3ZM193*nBeclYPp&DlO85AF_=Nr7aH{~x$gjZ?nG9yH?Y zZ@w$;{{Y{N=sAPw4pwH!N*~=M^3b$>wf@O+YQepr1Mxj3%lv|h<>33`#c1#GrUbA7 jt53*kIAGQHZu!i5iEip7m2MyF6j|-}SPeXfUNiU$-Qj2! literal 0 HcmV?d00001 diff --git a/network/wlan/ihvsample/ihvsample.vcxproj b/network/wlan/ihvsample/ihvsample.vcxproj index 8222ef43c..33e1e2ea6 100644 --- a/network/wlan/ihvsample/ihvsample.vcxproj +++ b/network/wlan/ihvsample/ihvsample.vcxproj @@ -19,11 +19,11 @@ - {098D97D4-8A9D-4AEA-8BBB-B5330EC55613} + {54DAA211-4F96-4AE7-B3D7-82ED403E0A22} $(MSBuildProjectName) Debug Win32 - {DD05EC52-7706-4D38-9EB6-596FBE935277} + {64311202-0CFD-4B80-9DD6-A268916B494D} @@ -114,6 +114,7 @@ %(PreprocessorDefinitions);RPC_NO_WINDOWS_H;UNICODE;_UNICODE;NO_STRICT;WIN32 + %(AdditionalOptions) /EHsc %(PreprocessorDefinitions);RPC_NO_WINDOWS_H;UNICODE;_UNICODE;NO_STRICT;WIN32 @@ -125,6 +126,7 @@ %(PreprocessorDefinitions);RPC_NO_WINDOWS_H;UNICODE;_UNICODE;NO_STRICT;WIN32 + %(AdditionalOptions) /EHsc %(PreprocessorDefinitions);RPC_NO_WINDOWS_H;UNICODE;_UNICODE;NO_STRICT;WIN32 @@ -136,6 +138,7 @@ %(PreprocessorDefinitions);RPC_NO_WINDOWS_H;UNICODE;_UNICODE;NO_STRICT;WIN32 + %(AdditionalOptions) /EHsc %(PreprocessorDefinitions);RPC_NO_WINDOWS_H;UNICODE;_UNICODE;NO_STRICT;WIN32 @@ -147,6 +150,7 @@ %(PreprocessorDefinitions);RPC_NO_WINDOWS_H;UNICODE;_UNICODE;NO_STRICT;WIN32 + %(AdditionalOptions) /EHsc %(PreprocessorDefinitions);RPC_NO_WINDOWS_H;UNICODE;_UNICODE;NO_STRICT;WIN32 @@ -240,7 +244,6 @@ - diff --git a/network/wlan/ihvsample/ihvsample.vcxproj.Filters b/network/wlan/ihvsample/ihvsample.vcxproj.Filters index 41235fc25..0a6dcbd1f 100644 --- a/network/wlan/ihvsample/ihvsample.vcxproj.Filters +++ b/network/wlan/ihvsample/ihvsample.vcxproj.Filters @@ -3,15 +3,15 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {2BFA3B7C-5457-42F0-AD10-BCFC2B55D74E} + {423168DD-A613-44E0-90D5-54ABB1A48236} h;hpp;hxx;hm;inl;inc;xsd - {3558D843-4C29-406E-87A5-E654CFF05FC4} + {95A2EEA6-FBCB-458E-80CD-26B1D9621DF9} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {87382B19-B7AA-4E7F-AB03-0AE307E2059E} + {EC753E12-4DCA-4153-AC12-532ABEF0099B} diff --git a/network/wlan/ihvsampleui/IHVSampleUI.vcxproj b/network/wlan/ihvsampleui/IHVSampleUI.vcxproj index cccce0637..838313b6b 100644 --- a/network/wlan/ihvsampleui/IHVSampleUI.vcxproj +++ b/network/wlan/ihvsampleui/IHVSampleUI.vcxproj @@ -19,11 +19,11 @@ - {5576312E-999E-4699-8F35-62D86C7E24B7} + {B29932FE-6321-4A6E-BE73-14BA0C441320} $(MSBuildProjectName) Debug Win32 - {CC668046-DE22-495D-BF12-86B0754ADBA4} + {A1585A58-9A12-4B2C-8398-4DB568BC725B} @@ -244,7 +244,6 @@ - diff --git a/network/wlan/ihvsampleui/IHVSampleUI.vcxproj.Filters b/network/wlan/ihvsampleui/IHVSampleUI.vcxproj.Filters index cd3890887..ad52e7ad3 100644 --- a/network/wlan/ihvsampleui/IHVSampleUI.vcxproj.Filters +++ b/network/wlan/ihvsampleui/IHVSampleUI.vcxproj.Filters @@ -3,15 +3,15 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {7A398CC0-086B-4682-9345-45CFA3130E1A} + {B2315A09-C8B4-4DA8-B678-86A98EA247B2} h;hpp;hxx;hm;inl;inc;xsd - {FC7DD856-4C33-419F-B10E-5BF283171077} + {7E8AF6EA-0142-462C-9B0B-EC7A38BBC338} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {260AC1C3-C7B5-451F-B4BB-FA1C77706860} + {A0337FFF-CA93-4B57-9DED-A958D1BC66AF} diff --git a/network/wlan/wlan.sln b/network/wlan/wlan.sln index 45081799a..44e716901 100644 --- a/network/wlan/wlan.sln +++ b/network/wlan/wlan.sln @@ -3,13 +3,13 @@ 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", "{4F0C336F-C969-4455-9DC3-5AC3E4321343}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Ihvsample", "Ihvsample", "{E3B8A38D-ED8A-4F9A-A336-412949303DD2}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Ihvsampleui", "Ihvsampleui", "{A185AFB8-6C0D-42D1-AF6A-7790F0CFA38C}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Ihvsampleui", "Ihvsampleui", "{22DD9E40-A7FD-4E55-BA06-26493C870F2E}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ihvsample", "ihvsample\ihvsample.vcxproj", "{098D97D4-8A9D-4AEA-8BBB-B5330EC55613}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ihvsample", "ihvsample\ihvsample.vcxproj", "{54DAA211-4F96-4AE7-B3D7-82ED403E0A22}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "IHVSampleUI", "ihvsampleui\IHVSampleUI.vcxproj", "{5576312E-999E-4699-8F35-62D86C7E24B7}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "IHVSampleUI", "ihvsampleui\IHVSampleUI.vcxproj", "{B29932FE-6321-4A6E-BE73-14BA0C441320}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -19,28 +19,28 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {098D97D4-8A9D-4AEA-8BBB-B5330EC55613}.Debug|Win32.ActiveCfg = Debug|Win32 - {098D97D4-8A9D-4AEA-8BBB-B5330EC55613}.Debug|Win32.Build.0 = Debug|Win32 - {098D97D4-8A9D-4AEA-8BBB-B5330EC55613}.Release|Win32.ActiveCfg = Release|Win32 - {098D97D4-8A9D-4AEA-8BBB-B5330EC55613}.Release|Win32.Build.0 = Release|Win32 - {098D97D4-8A9D-4AEA-8BBB-B5330EC55613}.Debug|x64.ActiveCfg = Debug|x64 - {098D97D4-8A9D-4AEA-8BBB-B5330EC55613}.Debug|x64.Build.0 = Debug|x64 - {098D97D4-8A9D-4AEA-8BBB-B5330EC55613}.Release|x64.ActiveCfg = Release|x64 - {098D97D4-8A9D-4AEA-8BBB-B5330EC55613}.Release|x64.Build.0 = Release|x64 - {5576312E-999E-4699-8F35-62D86C7E24B7}.Debug|Win32.ActiveCfg = Debug|Win32 - {5576312E-999E-4699-8F35-62D86C7E24B7}.Debug|Win32.Build.0 = Debug|Win32 - {5576312E-999E-4699-8F35-62D86C7E24B7}.Release|Win32.ActiveCfg = Release|Win32 - {5576312E-999E-4699-8F35-62D86C7E24B7}.Release|Win32.Build.0 = Release|Win32 - {5576312E-999E-4699-8F35-62D86C7E24B7}.Debug|x64.ActiveCfg = Debug|x64 - {5576312E-999E-4699-8F35-62D86C7E24B7}.Debug|x64.Build.0 = Debug|x64 - {5576312E-999E-4699-8F35-62D86C7E24B7}.Release|x64.ActiveCfg = Release|x64 - {5576312E-999E-4699-8F35-62D86C7E24B7}.Release|x64.Build.0 = Release|x64 + {54DAA211-4F96-4AE7-B3D7-82ED403E0A22}.Debug|Win32.ActiveCfg = Debug|Win32 + {54DAA211-4F96-4AE7-B3D7-82ED403E0A22}.Debug|Win32.Build.0 = Debug|Win32 + {54DAA211-4F96-4AE7-B3D7-82ED403E0A22}.Release|Win32.ActiveCfg = Release|Win32 + {54DAA211-4F96-4AE7-B3D7-82ED403E0A22}.Release|Win32.Build.0 = Release|Win32 + {54DAA211-4F96-4AE7-B3D7-82ED403E0A22}.Debug|x64.ActiveCfg = Debug|x64 + {54DAA211-4F96-4AE7-B3D7-82ED403E0A22}.Debug|x64.Build.0 = Debug|x64 + {54DAA211-4F96-4AE7-B3D7-82ED403E0A22}.Release|x64.ActiveCfg = Release|x64 + {54DAA211-4F96-4AE7-B3D7-82ED403E0A22}.Release|x64.Build.0 = Release|x64 + {B29932FE-6321-4A6E-BE73-14BA0C441320}.Debug|Win32.ActiveCfg = Debug|Win32 + {B29932FE-6321-4A6E-BE73-14BA0C441320}.Debug|Win32.Build.0 = Debug|Win32 + {B29932FE-6321-4A6E-BE73-14BA0C441320}.Release|Win32.ActiveCfg = Release|Win32 + {B29932FE-6321-4A6E-BE73-14BA0C441320}.Release|Win32.Build.0 = Release|Win32 + {B29932FE-6321-4A6E-BE73-14BA0C441320}.Debug|x64.ActiveCfg = Debug|x64 + {B29932FE-6321-4A6E-BE73-14BA0C441320}.Debug|x64.Build.0 = Debug|x64 + {B29932FE-6321-4A6E-BE73-14BA0C441320}.Release|x64.ActiveCfg = Release|x64 + {B29932FE-6321-4A6E-BE73-14BA0C441320}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {098D97D4-8A9D-4AEA-8BBB-B5330EC55613} = {4F0C336F-C969-4455-9DC3-5AC3E4321343} - {5576312E-999E-4699-8F35-62D86C7E24B7} = {A185AFB8-6C0D-42D1-AF6A-7790F0CFA38C} + {54DAA211-4F96-4AE7-B3D7-82ED403E0A22} = {E3B8A38D-ED8A-4F9A-A336-412949303DD2} + {B29932FE-6321-4A6E-BE73-14BA0C441320} = {22DD9E40-A7FD-4E55-BA06-26493C870F2E} EndGlobalSection EndGlobal diff --git a/network/wsk/echosrv/echosrv.sln b/network/wsk/echosrv/echosrv.sln index c21b2bf1a..e602262f8 100644 --- a/network/wsk/echosrv/echosrv.sln +++ b/network/wsk/echosrv/echosrv.sln @@ -3,7 +3,7 @@ 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", "{8E78BC0A-849D-4702-9E81-6F042E9390D6}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "echosrv", "echosrv.vcxproj", "{A2F1EB51-D648-4F09-8849-CB661530C63E}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {8E78BC0A-849D-4702-9E81-6F042E9390D6}.Debug|Win32.ActiveCfg = Debug|Win32 - {8E78BC0A-849D-4702-9E81-6F042E9390D6}.Debug|Win32.Build.0 = Debug|Win32 - {8E78BC0A-849D-4702-9E81-6F042E9390D6}.Release|Win32.ActiveCfg = Release|Win32 - {8E78BC0A-849D-4702-9E81-6F042E9390D6}.Release|Win32.Build.0 = Release|Win32 - {8E78BC0A-849D-4702-9E81-6F042E9390D6}.Debug|x64.ActiveCfg = Debug|x64 - {8E78BC0A-849D-4702-9E81-6F042E9390D6}.Debug|x64.Build.0 = Debug|x64 - {8E78BC0A-849D-4702-9E81-6F042E9390D6}.Release|x64.ActiveCfg = Release|x64 - {8E78BC0A-849D-4702-9E81-6F042E9390D6}.Release|x64.Build.0 = Release|x64 + {A2F1EB51-D648-4F09-8849-CB661530C63E}.Debug|Win32.ActiveCfg = Debug|Win32 + {A2F1EB51-D648-4F09-8849-CB661530C63E}.Debug|Win32.Build.0 = Debug|Win32 + {A2F1EB51-D648-4F09-8849-CB661530C63E}.Release|Win32.ActiveCfg = Release|Win32 + {A2F1EB51-D648-4F09-8849-CB661530C63E}.Release|Win32.Build.0 = Release|Win32 + {A2F1EB51-D648-4F09-8849-CB661530C63E}.Debug|x64.ActiveCfg = Debug|x64 + {A2F1EB51-D648-4F09-8849-CB661530C63E}.Debug|x64.Build.0 = Debug|x64 + {A2F1EB51-D648-4F09-8849-CB661530C63E}.Release|x64.ActiveCfg = Release|x64 + {A2F1EB51-D648-4F09-8849-CB661530C63E}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/network/wsk/echosrv/echosrv.vcxproj b/network/wsk/echosrv/echosrv.vcxproj index 7ccd0d41a..14e1ff784 100644 --- a/network/wsk/echosrv/echosrv.vcxproj +++ b/network/wsk/echosrv/echosrv.vcxproj @@ -19,11 +19,11 @@ - {8E78BC0A-849D-4702-9E81-6F042E9390D6} + {A2F1EB51-D648-4F09-8849-CB661530C63E} $(MSBuildProjectName) Debug Win32 - {0B7202D2-759C-425C-B5C5-FAE073649589} + {7393C8D1-88C5-429A-ADF3-B1623AAA3FEB} @@ -174,7 +174,6 @@ - diff --git a/network/wsk/echosrv/echosrv.vcxproj.Filters b/network/wsk/echosrv/echosrv.vcxproj.Filters index 22d8f67fd..9a412d056 100644 --- a/network/wsk/echosrv/echosrv.vcxproj.Filters +++ b/network/wsk/echosrv/echosrv.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {F13DC512-BF50-42A6-A954-1C591BDB2EF6} + {BEC2CE37-5BB9-4DAA-8B3F-093DE03E3FA2} h;hpp;hxx;hm;inl;inc;xsd - {239F589A-028C-461F-B097-D81EBE166FDF} + {1C7002F0-8985-492C-A5BF-79D9F1B71C8D} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {724E389F-B1ED-4B03-A345-5153DC92B75C} + {98C536C3-3B12-405F-BE3B-236A3CD4AFC1} inf;inv;inx;mof;mc; - {B9E8C901-FCC9-4990-990F-3D7CBBA234FC} + {ED1495C5-51D7-47A4-9175-75EAC4DEDD3E} diff --git a/nfc/NfcSimulator.sln b/nfc/NfcSimulator.sln new file mode 100644 index 000000000..06bb33165 --- /dev/null +++ b/nfc/NfcSimulator.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}") = "NfcDriverSim", "Simulator\Src\NfcDriverSim.vcxproj", "{E60D786C-D146-4B1A-835D-D62B883D7F00}" +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 + {E60D786C-D146-4B1A-835D-D62B883D7F00}.Debug|Win32.ActiveCfg = Debug|Win32 + {E60D786C-D146-4B1A-835D-D62B883D7F00}.Debug|Win32.Build.0 = Debug|Win32 + {E60D786C-D146-4B1A-835D-D62B883D7F00}.Release|Win32.ActiveCfg = Release|Win32 + {E60D786C-D146-4B1A-835D-D62B883D7F00}.Release|Win32.Build.0 = Release|Win32 + {E60D786C-D146-4B1A-835D-D62B883D7F00}.Debug|x64.ActiveCfg = Debug|x64 + {E60D786C-D146-4B1A-835D-D62B883D7F00}.Debug|x64.Build.0 = Debug|x64 + {E60D786C-D146-4B1A-835D-D62B883D7F00}.Release|x64.ActiveCfg = Release|x64 + {E60D786C-D146-4B1A-835D-D62B883D7F00}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/nfc/Simulator/Inc/NfcSimulatorDDI.h b/nfc/Simulator/Inc/NfcSimulatorDDI.h new file mode 100644 index 000000000..0fc742dfb --- /dev/null +++ b/nfc/Simulator/Inc/NfcSimulatorDDI.h @@ -0,0 +1,34 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved + +Module Name: + + NFCSimulatorDDI.h + +Abstract: + + This header contains definitions for the NFC simulator driver + +Environment: + + User Mode + +--*/ +#pragma once + +// {A965F47F-23E8-4897-90D1-A6C8A1BB4C59} +const GUID GUID_DEVINTERFACE_NFCSIM = + {0xa965f47f, 0x23e8, 0x4897, {0x90, 0xd1, 0xa6, 0xc8, 0xa1, 0xbb, 0x4c, 0x59}}; + +// {4D535369-6DD8-4448-4E46-4345452D4944} +const GUID GUID_DH_SECURE_ELEMENT = + {0x4D535369, 0x6DD8, 0x4448, {0x4E, 0x46, 0x43, 0x45, 0x45, 0x2D, 0x49, 0x44}}; + +#define IOCTL_NFCSIM_BEGIN_PROXIMITY CTL_CODE(FILE_DEVICE_UNKNOWN, 0x1000, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_NFCSIM_TRIGGER_SEEVENT CTL_CODE(FILE_DEVICE_UNKNOWN, 0x1001, METHOD_BUFFERED, FILE_ANY_ACCESS) + +struct BEGIN_PROXIMITY_ARGS +{ + WCHAR szName[MAX_PATH]; // Name or IP address +}; diff --git a/nfc/Simulator/README.md b/nfc/Simulator/README.md new file mode 100644 index 000000000..dfdeaa7c5 --- /dev/null +++ b/nfc/Simulator/README.md @@ -0,0 +1,14 @@ +NFC Simulator Driver Sample +=========================== +This sample demonstrates how to use User-Mode Driver Framework (UMDF) to write a Near-Field Communication (NFC) universal driver along with best practices. + +This sample uses a TCP/IPv6 network connection and a static configuration between two machines to allow simulation of near-field interaction. + +Installing the driver +--------------------- +To install the NfcSimulator driver: +Copy the driver binary, NfcSimulator.inf to a directory on your test machine. Change to the directory containing the inf and binaries Next run devcon.exe as follows: + + devcon.exe install NfcSimulator.inf root\NfcSimulator + +Create a Windows Firewall rule to allow the NfcSimulator to receive proximity simulation requests over the network. For example, run WF.msc, and create a new inbound rule that opens port 9299. \ No newline at end of file diff --git a/nfc/Simulator/Src/Connection.cpp b/nfc/Simulator/Src/Connection.cpp new file mode 100644 index 000000000..0b1d76bbd --- /dev/null +++ b/nfc/Simulator/Src/Connection.cpp @@ -0,0 +1,271 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +--*/ + +#include "Internal.h" +#include "Connection.tmh" + +/* 9C7D2C68-5AD8-4A14-BE20-F8741D60D100 */ +const GUID MAGIC_PACKET_P2P = + {0x9C7D2C68, 0x5AD8, 0x4A14, {0xBE, 0x20, 0xF8, 0x74, 0x1D, 0x60, 0xD1, 0x00}}; + +/* 9C7D2C68-5AD8-4A14-BE20-F8741D60D101 */ +const GUID MAGIC_PACKET_TAG = + {0x9C7D2C68, 0x5AD8, 0x4A14, {0xBE, 0x20, 0xF8, 0x74, 0x1D, 0x60, 0xD1, 0x01}}; + +/* 9C7D2C68-5AD8-4A14-BE20-F8741D60D102 */ +const GUID MAGIC_PACKET_HCE = + {0x9C7D2C68, 0x5AD8, 0x4A14, {0xBE, 0x20, 0xF8, 0x74, 0x1D, 0x60, 0xD1, 0x02}}; + +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 cbBytesRead = 0; + OVERLAPPED Overlapped = {}; + + if (!ReadFile((HANDLE)Socket, pBuffer, cbBuffer, &cbBytesRead, &Overlapped)) { + if (GetLastError() == ERROR_IO_PENDING) { + if (!GetOverlappedResult((HANDLE)Socket, &Overlapped, &cbBytesRead, TRUE)) { + hr = HRESULT_FROM_WIN32(GetLastError()); + } + } + else { + hr = HRESULT_FROM_WIN32(GetLastError()); + } + } + return hr; +} + +BOOL CConnection::Create(_In_ IConnectionCallback* pCallback, _Outptr_result_maybenull_ CConnection** ppConnection) +{ + NT_ASSERT(ppConnection != nullptr); + + *ppConnection = new CConnection(pCallback); + + if (*ppConnection != nullptr) { + return TRUE; + } + + return FALSE; +} + +void CConnection::Terminate() +{ + MethodEntry("void"); + + STATE PriorState = (STATE)(InterlockedExchange((long*)&_State, (long)TERMINATED)); + + if (PriorState != TERMINATED) { + shutdown(_Socket, SD_SEND); + + if (_ThreadpoolThreadId != GetCurrentThreadId()) { + WaitForThreadpoolWorkCallbacks(_ThreadpoolWork, false); + } + + SOCKET Socket = (SOCKET)InterlockedExchangePointer((PVOID*)&_Socket, (PVOID)INVALID_SOCKET); + + if (Socket != INVALID_SOCKET) { + closesocket(Socket); + } + } + + MethodReturnVoid(); +} + +HRESULT CConnection::InitializeAsClient(_In_ BEGIN_PROXIMITY_ARGS* pArgs) +{ + MethodEntry("pArgs->szName = '%S'", pArgs->szName); + + HRESULT hr = S_OK; + + pArgs->szName[MAX_PATH-1] = L'\0'; + + _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, + nullptr)) { + hr = HRESULT_FROM_WIN32(WSAGetLastError()); + TraceErrorHR(hr, L"WSAConnectByName returned failure"); + } + } + + if (SUCCEEDED(hr)) { + if (setsockopt(_Socket, SOL_SOCKET, SO_UPDATE_CONNECT_CONTEXT, nullptr, 0) == SOCKET_ERROR) { + hr = HRESULT_FROM_WIN32(WSAGetLastError()); + } + } + + if (SUCCEEDED(hr)) { + if (send(_Socket, (char*)&MAGIC_PACKET_P2P, sizeof(MAGIC_PACKET_P2P), 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_P2P, sizeof(MAGIC_PACKET_P2P)) != 0) { + hr = E_FAIL; + } + } + } + + if (SUCCEEDED(hr)) { + hr = FinalizeEstablish(INVALID_SOCKET); + } + } + + if (FAILED(hr)) { + if (_Socket != INVALID_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_P2P, sizeof(MAGIC_PACKET_P2P)) == 0) { + _ConnectionType = CONNECTION_TYPE_P2P; + } + else if (memcmp(pMagicPacket, &MAGIC_PACKET_TAG, sizeof(MAGIC_PACKET_TAG)) == 0) { + _ConnectionType = CONNECTION_TYPE_TAG; + } + else if (memcmp(pMagicPacket, &MAGIC_PACKET_HCE, sizeof(MAGIC_PACKET_HCE)) == 0) { + _ConnectionType = CONNECTION_TYPE_HCE; + } + else { + hr = E_FAIL; + } + + if (SUCCEEDED(hr)) { + if (send(Socket, (char*)pMagicPacket, sizeof(GUID), 0) == SOCKET_ERROR) { + hr = HRESULT_FROM_WIN32(WSAGetLastError()); + } + } + + if (SUCCEEDED(hr)) { + hr = FinalizeEstablish(Socket); + } + + if (FAILED(hr)) { + 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) { + hr = HRESULT_FROM_WIN32(ERROR_ALREADY_INITIALIZED); + } + + if (SUCCEEDED(hr)) { + _ThreadpoolWork = CreateThreadpoolWork(s_ReceiveThreadProc, this, nullptr); + + if (_ThreadpoolWork == nullptr) { + hr = HRESULT_FROM_WIN32(GetLastError()); + } + else { + if (Socket != INVALID_SOCKET) { + _Socket = Socket; + } + + SubmitThreadpoolWork(_ThreadpoolWork); + _pCallback->ConnectionEstablished(this); + } + } + + MethodReturnHR(hr); +} + +BOOL CConnection::ReceiveThreadProc() +{ + MethodEntry("void"); + + MESSAGE* pMessage = new MESSAGE(); + + if (pMessage == nullptr) { + Terminate(); + } + else + { + while (_Socket != INVALID_SOCKET) { + if (recv(_Socket, (char*)pMessage, sizeof(*pMessage), MSG_WAITALL) == sizeof(*pMessage)) { + _pCallback->HandleReceivedMessage(_ConnectionType, pMessage); + } + else { + Terminate(); + break; + } + } + delete pMessage; + } + + BOOL fConnectionDeleted = _pCallback->ConnectionTerminated(this); + + MethodReturnBool(fConnectionDeleted); +} + +HRESULT CConnection::TransmitMessage(_In_ MESSAGE* pMessage) +{ + MethodEntry("..."); + + 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(); + } + } + + MethodReturnHR(hr); +} diff --git a/nfc/Simulator/Src/Connection.h b/nfc/Simulator/Src/Connection.h new file mode 100644 index 000000000..ea851b58b --- /dev/null +++ b/nfc/Simulator/Src/Connection.h @@ -0,0 +1,121 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Abstract: + + Defines a simple NFP Provider implementation using the network. + +--*/ +#pragma once + +#include "SocketListener.h" + +class CConnection; + +struct MESSAGE; + +enum CONNECTION_TYPE +{ + CONNECTION_TYPE_P2P = 0, // Peer to peer connection + CONNECTION_TYPE_TAG, // Tag supports NDEF read/write access + CONNECTION_TYPE_HCE, // HCE connection +}; + +class IConnectionCallback +{ +public: + virtual void HandleReceivedMessage(_In_ CONNECTION_TYPE ConnType, _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(nullptr), + _fInboundConnection(false), + _ConnectionType(CONNECTION_TYPE_P2P) + {} + +public: + virtual ~CConnection() + { + Terminate(); + + if (_ThreadpoolWork != nullptr) { + // Don't wait for threadpool callbacks when this thread is actually the threadpool callback + if (_ThreadpoolThreadId != GetCurrentThreadId()) { + WaitForThreadpoolWorkCallbacks(_ThreadpoolWork, false); + } + CloseThreadpoolWork(_ThreadpoolWork); + _ThreadpoolWork = nullptr; + } + } + + static BOOL Create(_In_ IConnectionCallback* pCallback, _Outptr_result_maybenull_ 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); + } + + CONNECTION_TYPE GetConnectionType() + { + return _ConnectionType; + } + +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; + CONNECTION_TYPE _ConnectionType; +}; diff --git a/nfc/Simulator/Src/Constants.h b/nfc/Simulator/Src/Constants.h new file mode 100644 index 000000000..f6be14c9d --- /dev/null +++ b/nfc/Simulator/Src/Constants.h @@ -0,0 +1,102 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved + +Module Name: + + Constants.h + +Abstract: + + This header contains definitions common for NFC drivers + +--*/ + +#pragma once + +// +// Message Types +// +#define WINDOWS_PROTOCOL L"Windows" +#define WINDOWS_PROTOCOL_CHARS (ARRAYSIZE(WINDOWS_PROTOCOL) - 1) + +#define WINDOWSURI_TYPE L"Uri" +#define WINDOWSURI_TYPE_CHARS (ARRAYSIZE(WINDOWSURI_TYPE) - 1) + +#define WINDOWSURI_PROTOCOL WINDOWS_PROTOCOL WINDOWSURI_TYPE +#define WINDOWSURI_PROTOCOL_CHARS (ARRAYSIZE(WINDOWSURI_PROTOCOL) - 1) + +#define WINDOWSMIME_TYPE L"Mime" +#define WINDOWSMIME_TYPE_CHARS (ARRAYSIZE(WINDOWSMIME_TYPE) - 1) + +#define WINDOWSMIME_PROTOCOL WINDOWS_PROTOCOL WINDOWSMIME_TYPE +#define WINDOWSMIME_PROTOCOL_CHARS (ARRAYSIZE(WINDOWSMIME_PROTOCOL) - 1) + +#define NDEF_PROTOCOL L"NDEF" +#define NDEF_PROTOCOL_CHARS (ARRAYSIZE(NDEF_PROTOCOL) - 1) + +#define NDEF_UNKNOWN_TYPE L"Unknown" +#define NDEF_UNKNOWN_TYPE_CHARS (ARRAYSIZE(NDEF_UNKNOWN_TYPE) - 1) + +#define NDEF_EMPTY_TYPE L"Empty" +#define NDEF_EMPTY_TYPE_CHARS (ARRAYSIZE(NDEF_EMPTY_TYPE) - 1) + +#define NDEF_EXT_TYPE L"ext." +#define NDEF_EXT_TYPE_CHARS (ARRAYSIZE(NDEF_EXT_TYPE) - 1) + +#define NDEF_MIME_TYPE L"MIME." +#define NDEF_MIME_TYPE_CHARS (ARRAYSIZE(NDEF_MIME_TYPE) - 1) + +#define NDEF_URI_TYPE L"URI." +#define NDEF_URI_TYPE_CHARS (ARRAYSIZE(NDEF_URI_TYPE) - 1) + +#define NDEF_WKT_TYPE L"wkt." +#define NDEF_WKT_TYPE_CHARS (ARRAYSIZE(NDEF_WKT_TYPE) - 1) + +#define WRITETAG_TYPE L"WriteTag" +#define WRITETAG_TYPE_CHARS (ARRAYSIZE(WRITETAG_TYPE) - 1) + +#define DEVICE_ARRIVED L"DeviceArrived" +#define DEVICE_DEPARTED L"DeviceDeparted" +#define WRITEABLETAG_PROTOCOL L"WriteableTag" +#define PAIRING_BLUETOOTH_PROTOCOL L"Pairing:Bluetooth" +#define PAIRING_UPNP_PROTOCOL L"Pairing:UPnP" +#define LAUNCHAPP_WRITETAG_PROTOCOL L"LaunchApp:WriteTag" + +// +// Namespaces +// +#define NFP_NAMESPACE L"Nfp" +#define NFP_NAMESPACE_CHARS (ARRAYSIZE(NFP_NAMESPACE) - 1) + +#define PUBS_NAMESPACE L"Pubs\\" +#define PUBS_NAMESPACE_CHARS (ARRAYSIZE(PUBS_NAMESPACE) - 1) + +#define SUBS_NAMESPACE L"Subs\\" +#define SUBS_NAMESPACE_CHARS (ARRAYSIZE(SUBS_NAMESPACE) - 1) + +#define SE_NAMESPACE L"SE" +#define SE_NAMESPACE_CHARS (ARRAYSIZE(SE_NAMESPACE) - 1) + +#define SEMANAGE_NAMESPACE L"SEManage" +#define SEMANAGE_NAMESPACE_CHARS (ARRAYSIZE(SEMANAGE_NAMESPACE) - 1) + +#define SEEVENTS_NAMESPACE L"SEEvents" +#define SEEVENTS_NAMESPACE_CHARS (ARRAYSIZE(SEEVENTS_NAMESPACE) - 1) + +#define SMARTCARD_READER_NAMESPACE L"SCReader" +#define SMARTCARD_READER_NAMESPACE_CHARS (ARRAYSIZE(SMARTCARD_READER_NAMESPACE) - 1) + +#define RM_NAMESPACE L"RadioManage" +#define RM_NAMESPACE_CHARS (ARRAYSIZE(RM_NAMESPACE) - 1) + +#define MAX_MESSAGE_QUEUE_SIZE 50 + +// +// Macros +// +#define IsStringPrefixed(_STRING_, _PREFIX_) \ + (CompareStringOrdinal(_STRING_, (_PREFIX_ ## _CHARS), _PREFIX_, (_PREFIX_ ## _CHARS), TRUE) == CSTR_EQUAL) + +#define IsStringEqual(_STRING1_, _STRING2_) \ + (CompareStringOrdinal(_STRING1_, -1, _STRING2_, -1, TRUE) == CSTR_EQUAL) \ No newline at end of file diff --git a/nfc/Simulator/Src/Device.cpp b/nfc/Simulator/Src/Device.cpp new file mode 100644 index 000000000..cfbc57c07 --- /dev/null +++ b/nfc/Simulator/Src/Device.cpp @@ -0,0 +1,245 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved. + +Module Name: + + Device.cpp + +Abstract: + + This module contains the implementation of the UMDF + driver's device callback object. + +Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + +#include "Internal.h" +#include "Device.tmh" + +NTSTATUS +CDevice::OnDeviceAdd( + _In_ WDFDRIVER Driver, + _Inout_ PWDFDEVICE_INIT DeviceInit + ) +{ + FunctionEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + WDFDEVICE Device; + WDF_OBJECT_ATTRIBUTES DeviceAttributes; + WDF_OBJECT_ATTRIBUTES FileAttributes; + WDF_FILEOBJECT_CONFIG FileConfig; + WDF_PNPPOWER_EVENT_CALLBACKS PowerCallbacks; + CDevice* pDevice = nullptr; + + UNREFERENCED_PARAMETER(Driver); + + WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&PowerCallbacks); + + PowerCallbacks.EvtDeviceD0Entry = CDevice::OnD0Entry; + PowerCallbacks.EvtDeviceD0Exit = CDevice::OnD0Exit; + + WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &PowerCallbacks); + + WDF_FILEOBJECT_CONFIG_INIT( + &FileConfig, + CDevice::OnFileCreate, + CDevice::OnFileClose, + WDF_NO_EVENT_CALLBACK); + + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&FileAttributes, CFileObject); + FileAttributes.EvtDestroyCallback = CFileObject::OnDestroy; + + WdfDeviceInitSetFileObjectConfig(DeviceInit, &FileConfig, &FileAttributes); + + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&DeviceAttributes, CDevice); + + DeviceAttributes.EvtCleanupCallback = CDevice::OnCleanup; + DeviceAttributes.SynchronizationScope = WdfSynchronizationScopeNone; + DeviceAttributes.ExecutionLevel = WdfExecutionLevelPassive; + + Status = WdfDeviceCreate(&DeviceInit, &DeviceAttributes, &Device); + + if (!NT_SUCCESS(Status)) { + TraceInfo("WdfDeviceCreate failed with Status %!STATUS!", Status); + goto Exit; + } + + // Construct device object on the preallocated buffer using placement 'new' + pDevice = new (GetDeviceObject(Device)) CDevice(Device); + + if (pDevice == nullptr) { + Status = STATUS_INSUFFICIENT_RESOURCES; + goto Exit; + } + + Status = pDevice->Initialize(); + +Exit: + FunctionReturn(Status, "Status = %!STATUS!", Status); +} + +NTSTATUS CDevice::Initialize() +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + WDF_IO_QUEUE_CONFIG QueueConfig; + WDF_OBJECT_ATTRIBUTES QueueAttributes; + WDFQUEUE Queue = nullptr; + + WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&QueueConfig, WdfIoQueueDispatchParallel); + + QueueConfig.PowerManaged = WdfFalse; + QueueConfig.EvtIoDeviceControl = CQueue::OnIoDeviceControl; + + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&QueueAttributes, CQueue); + + Status = WdfIoQueueCreate( + m_Device, + &QueueConfig, + &QueueAttributes, + &Queue); + + if (!NT_SUCCESS(Status)) { + TraceInfo("WdfIoQueueCreate failed with Status %!STATUS!", Status); + goto Exit; + } + + // Construct a queue object on the preallocated buffer using placement new operation + m_pQueue = new (GetQueueObject(Queue)) CQueue(Queue); + + if (m_pQueue == nullptr) { + Status = STATUS_INSUFFICIENT_RESOURCES; + goto Exit; + } + + Status = m_pQueue->Initialize(); + +Exit: + MethodReturn(Status, "Status = %!STATUS!", Status); +} + +VOID CDevice::OnCleanup(_In_ WDFOBJECT Object) +{ + FunctionEntry("..."); + + CDevice *pDevice = GetDeviceObject(Object); + + NT_ASSERT(pDevice != nullptr); + + if (pDevice->m_Device == Object) { + // Device object constructed using placement 'new' so explicitly invoke destructor + pDevice->Deinitialize(); + pDevice->~CDevice(); + } + + FunctionReturnVoid(); +} + +NTSTATUS CDevice::Deinitialize() +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + + if (m_pQueue != nullptr) { + // Queue object constructed using placement new so explicitly invoke destructor + m_pQueue->Deinitialize(); + m_pQueue->~CQueue(); + + m_pQueue = nullptr; + } + + MethodReturn(Status, "Status = %!STATUS!", Status); +} + +NTSTATUS +CDevice::OnD0Entry( + _In_ WDFDEVICE Device, + _In_ WDF_POWER_DEVICE_STATE PreviousState + ) +{ + FunctionEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + CDevice *pDevice = GetDeviceObject(Device); + + NT_ASSERT(pDevice != nullptr); + Status = pDevice->OnD0Entry(PreviousState); + + FunctionReturn(Status, "Status = %!STATUS!", Status); +} + +NTSTATUS +CDevice::OnD0Entry( + _In_ WDF_POWER_DEVICE_STATE PreviousState + ) +{ + MethodEntry("PreviousState = %d", PreviousState); + NTSTATUS Status = STATUS_SUCCESS; + MethodReturn(Status, "Status = %!STATUS!", Status); +} + +NTSTATUS +CDevice::OnD0Exit( + _In_ WDFDEVICE Device, + _In_ WDF_POWER_DEVICE_STATE TargetState + ) +{ + FunctionEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + CDevice *pDevice = GetDeviceObject(Device); + + NT_ASSERT(pDevice != nullptr); + Status = pDevice->OnD0Exit(TargetState); + + FunctionReturn(Status, "Status = %!STATUS!", Status); +} + +NTSTATUS +CDevice::OnD0Exit( + _In_ WDF_POWER_DEVICE_STATE TargetState + ) +{ + MethodEntry("TargetState = %d", TargetState); + NTSTATUS Status = STATUS_SUCCESS; + MethodReturn(Status, "Status = %!STATUS!", Status); +} + +VOID +CDevice::OnFileCreate( + _In_ WDFDEVICE Device, + _In_ WDFREQUEST Request, + _In_ WDFFILEOBJECT FileObject + ) +{ + FunctionEntry("..."); + + CDevice *pDevice = GetDeviceObject(Device); + + NT_ASSERT(pDevice != nullptr); + pDevice->GetQueue()->OnFileCreate(Device, Request, FileObject); + + FunctionReturnVoid(); +} + +VOID +CDevice::OnFileClose( + _In_ WDFFILEOBJECT FileObject + ) +{ + FunctionEntry("..."); + + CDevice *pDevice = GetDeviceObject(WdfFileObjectGetDevice(FileObject)); + + NT_ASSERT(pDevice != nullptr); + pDevice->GetQueue()->OnFileClose(FileObject); + + FunctionReturnVoid(); +} diff --git a/nfc/Simulator/Src/Device.h b/nfc/Simulator/Src/Device.h new file mode 100644 index 000000000..41c32cf39 --- /dev/null +++ b/nfc/Simulator/Src/Device.h @@ -0,0 +1,55 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved + +Module Name: + + Device.h + +Abstract: + + This module contains the type definitions for the + driver's device callback class. + +Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ +#pragma once + +class CQueue; + +class CDevice +{ +public: + CDevice(WDFDEVICE Device) : m_Device(Device), m_pQueue(nullptr) + {} + +public: + static EVT_WDF_DRIVER_DEVICE_ADD OnDeviceAdd; + static EVT_WDF_OBJECT_CONTEXT_CLEANUP OnCleanup; + static EVT_WDF_DEVICE_D0_ENTRY OnD0Entry; + static EVT_WDF_DEVICE_D0_EXIT OnD0Exit; + static EVT_WDF_DEVICE_FILE_CREATE OnFileCreate; + static EVT_WDF_FILE_CLOSE OnFileClose; + +public: + NTSTATUS Initialize(); + NTSTATUS Deinitialize(); + + NTSTATUS OnD0Entry(_In_ WDF_POWER_DEVICE_STATE PreviousState); + NTSTATUS OnD0Exit(_In_ WDF_POWER_DEVICE_STATE TargetState); + +public: + CQueue* GetQueue() + { + return m_pQueue; + } + +private: + WDFDEVICE m_Device; + CQueue* m_pQueue; +}; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(CDevice, GetDeviceObject); \ No newline at end of file diff --git a/nfc/Simulator/Src/Driver.cpp b/nfc/Simulator/Src/Driver.cpp new file mode 100644 index 000000000..5082e3b4b --- /dev/null +++ b/nfc/Simulator/Src/Driver.cpp @@ -0,0 +1,70 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved. + +Module Name: + + Driver.cpp + +Abstract: + + This module contains the implementation of the UMDF driver callback object. + +Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + +#include "Internal.h" +#include "Driver.tmh" + +DECLARE_TRACING_TLS; + +NTSTATUS +DriverEntry( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ) +{ + NTSTATUS Status = STATUS_SUCCESS; + WDF_DRIVER_CONFIG Config; + + WPP_INIT_TRACING(DriverObject, RegistryPath); + TracingTlsInitialize(); + + FunctionEntry("..."); + + WDF_DRIVER_CONFIG_INIT(&Config, CDevice::OnDeviceAdd); + Config.EvtDriverUnload = OnDriverUnload; + + Status = WdfDriverCreate( + DriverObject, + RegistryPath, + WDF_NO_OBJECT_ATTRIBUTES, + &Config, + WDF_NO_HANDLE); + + if (!NT_SUCCESS(Status)) { + TraceInfo("WdfDriverCreate failed with Status %!STATUS!", Status); + goto Exit; + } + +Exit: + if (!NT_SUCCESS(Status)) { + TracingTlsFree(); + WPP_CLEANUP(DriverObject); + } + FunctionReturn(Status, "Status = %!STATUS!", Status); +} + +VOID +OnDriverUnload( + _In_ WDFDRIVER Driver + ) +{ + UNREFERENCED_PARAMETER(Driver); + + TracingTlsFree(); + WPP_CLEANUP(WdfDriverWdmGetDriverObject(Driver)); +} diff --git a/nfc/Simulator/Src/Driver.h b/nfc/Simulator/Src/Driver.h new file mode 100644 index 000000000..2ad65992e --- /dev/null +++ b/nfc/Simulator/Src/Driver.h @@ -0,0 +1,27 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved + +Module Name: + + Driver.h + +Abstract: + + This module contains the type definitions for the UMDF + driver callback class. + +Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + +#pragma once + +WDF_EXTERN_C_START + +DRIVER_INITIALIZE DriverEntry; +EVT_WDF_DRIVER_UNLOAD OnDriverUnload; + +WDF_EXTERN_C_END \ No newline at end of file diff --git a/nfc/Simulator/Src/FileContext.cpp b/nfc/Simulator/Src/FileContext.cpp new file mode 100644 index 000000000..6e609832a --- /dev/null +++ b/nfc/Simulator/Src/FileContext.cpp @@ -0,0 +1,640 @@ +/*++ + +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: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + +#include "Internal.h" +#include "FileContext.tmh" + +CFileObject::CFileObject( + WDFFILEOBJECT FileObject + ) + : m_FileObject(FileObject), + m_Role(ROLE_UNDEFINED), + m_pszType(nullptr), + m_fEnabled(TRUE), + m_dwQueueSize(0), + m_cCompleteReady(0), + m_pConnection(nullptr), + m_Request(nullptr), + m_SecureElementEventType(ExternalReaderArrival) +{ + NT_ASSERT(m_FileObject != nullptr); + + InitializeListHead(&m_Queue); + InitializeListHead(&m_ListEntry); + InitializeCriticalSection(&m_RoleLock); + + RtlZeroMemory(&m_SecureElementId, sizeof(GUID)); +} + +CFileObject::~CFileObject() +{ + // + // The object is bound to the file handle and so this happens after close and the + // framework guarentees all requests on the file handle are cancelled prior to close + // + EnterCriticalSection(&m_RoleLock); + NT_ASSERT(m_Request == nullptr); + LeaveCriticalSection(&m_RoleLock); + + PurgeQueue(); + + SAFE_DELETEARRAY(m_pszType); + SAFE_DELETE(m_pConnection); + + DeleteCriticalSection(&m_RoleLock); + + m_FileObject = nullptr; +} + +VOID CFileObject::OnDestroy(_In_ WDFOBJECT FileObject) +{ + FunctionEntry("..."); + + CFileObject *pFileObject = GetFileObject(FileObject); + + NT_ASSERT(pFileObject != nullptr); + + if (pFileObject->m_FileObject == FileObject) { + // File object constructed using placement 'new' so explicitly invoke destructor + pFileObject->~CFileObject(); + } + + FunctionReturnVoid(); +} + +NTSTATUS CFileObject::Enable() +{ + MethodEntry("void"); + + NTSTATUS Status = STATUS_SUCCESS; + + EnterCriticalSection(&m_RoleLock); + + NT_ASSERT(IsPublication() || IsSubscription()); + + if (m_fEnabled) { + Status = STATUS_INVALID_DEVICE_STATE; + goto Exit; + } + + m_fEnabled = TRUE; + +Exit: + LeaveCriticalSection(&m_RoleLock); + MethodReturn(Status, "Status = %!STATUS!", Status); +} + +NTSTATUS CFileObject::Disable() +{ + MethodEntry("void"); + + NTSTATUS Status = STATUS_SUCCESS; + + EnterCriticalSection(&m_RoleLock); + + NT_ASSERT(IsPublication() || IsSubscription()); + + if (!m_fEnabled) { + Status = STATUS_INVALID_DEVICE_STATE; + goto Exit; + } + + m_fEnabled = FALSE; + CompleteRequest(STATUS_CANCELLED, 0, true); + PurgeQueue(); + +Exit: + LeaveCriticalSection(&m_RoleLock); + MethodReturn(Status, "Status = %!STATUS!", Status); +} + +NTSTATUS CFileObject::SetType(_In_ PCWSTR pszType) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + size_t cchType = wcslen(pszType) + 1; + + EnterCriticalSection(&m_RoleLock); + + NT_ASSERT(m_pszType == nullptr); + + NT_ASSERT(IsPublication() || IsSubscription()); + + if ((cchType > MinCchType) && (cchType < MaxCchType)) { + m_pszType = new WCHAR[cchType]; + + if (m_pszType != nullptr) { + StringCchCopy(m_pszType, cchType, pszType); + } + else { + Status = STATUS_INSUFFICIENT_RESOURCES; + } + } + else { + Status = STATUS_INVALID_PARAMETER; + } + + LeaveCriticalSection(&m_RoleLock); + MethodReturn(Status, "Status = %!STATUS!", Status); +} + +NTSTATUS CFileObject::GetNextSubscribedMessage(_In_ WDFREQUEST Request) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + CPayload* pPayload = nullptr; + + EnterCriticalSection(&m_RoleLock); + + NT_ASSERT(IsSubscription()); + + if (m_Request != nullptr) { + Status = STATUS_INVALID_DEVICE_STATE; + goto Exit; + } + + if (!m_fEnabled) { + Status = STATUS_CANCELLED; + goto Exit; + } + + Status = WdfRequestMarkCancelableEx(Request, CFileObject::OnRequestCancel); + + if (NT_SUCCESS(Status)) { + m_Request = Request; + + if (!IsListEmpty(&m_Queue)) { + pPayload = CPayload::FromListEntry(m_Queue.Flink); + + if (CompleteRequest(pPayload->GetSize(), pPayload->GetPayload())) { + m_dwQueueSize--; + RemoveHeadList(&m_Queue); + delete pPayload; + } + } + } + +Exit: + LeaveCriticalSection(&m_RoleLock); + MethodReturn(Status, "Status = %!STATUS!", Status); +} + +NTSTATUS CFileObject::SetPayload(_In_ DWORD cbPayload, _In_reads_bytes_(cbPayload) PBYTE pbPayload) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + + EnterCriticalSection(&m_RoleLock); + + NT_ASSERT(IsPublication()); + + if (m_Payload.GetPayload() != nullptr) { + Status = STATUS_INVALID_DEVICE_STATE; + goto Exit; + } + + Status = m_Payload.Initialize(cbPayload, pbPayload); + +Exit: + LeaveCriticalSection(&m_RoleLock); + MethodReturn(Status, "Status = %!STATUS!", Status); +} + +NTSTATUS CFileObject::GetNextTransmittedMessage(_In_ WDFREQUEST Request) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + + EnterCriticalSection(&m_RoleLock); + + NT_ASSERT(IsPublication()); + + if (m_Request != nullptr) { + Status = STATUS_INVALID_DEVICE_STATE; + goto Exit; + } + + if (!m_fEnabled) { + Status = STATUS_CANCELLED; + goto Exit; + } + + Status = WdfRequestMarkCancelableEx(Request, CFileObject::OnRequestCancel); + + if (NT_SUCCESS(Status)) { + m_Request = Request; + + if (m_cCompleteReady > 0) { + if (CompleteRequest(STATUS_SUCCESS, 0, true)) { + m_cCompleteReady--; + } + } + } + +Exit: + LeaveCriticalSection(&m_RoleLock); + MethodReturn(Status, "Status = %!STATUS!", Status); +} + +NTSTATUS CFileObject::GetNextSecureElementPayload(_In_ WDFREQUEST Request) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + CPayload* pPayload = nullptr; + + EnterCriticalSection(&m_RoleLock); + + NT_ASSERT(IsSecureElementEvent() || IsSecureElementManager()); + + if (m_Request != nullptr) { + Status = STATUS_INVALID_DEVICE_STATE; + goto Exit; + } + + Status = WdfRequestMarkCancelableEx(Request, CFileObject::OnRequestCancel); + + if (NT_SUCCESS(Status)) { + m_Request = Request; + + if (!IsListEmpty(&m_Queue)) { + pPayload = CPayload::FromListEntry(m_Queue.Flink); + + if (CompleteRequest(pPayload->GetSize(), pPayload->GetPayload())) { + m_dwQueueSize--; + RemoveHeadList(&m_Queue); + delete pPayload; + } + } + } + +Exit: + LeaveCriticalSection(&m_RoleLock); + MethodReturn(Status, "Status = %!STATUS!", Status); +} + +NTSTATUS CFileObject::SubscribeForEvent(_In_ GUID& SecureElementId, _In_ SECURE_ELEMENT_EVENT_TYPE SecureElementEventType) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + + EnterCriticalSection(&m_RoleLock); + + NT_ASSERT(IsSecureElementEvent()); + + m_SecureElementId = SecureElementId; + m_SecureElementEventType = SecureElementEventType; + + LeaveCriticalSection(&m_RoleLock); + MethodReturn(Status, "Status = %!STATUS!", Status); +} + +NTSTATUS +CFileObject::BeginProximity( + _In_ BEGIN_PROXIMITY_ARGS *pArgs, + _In_ IConnectionCallback* pCallback + ) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + + NT_ASSERT(IsRoleSimulation()); + + if (CConnection::Create(pCallback, &m_pConnection)) { + if (SUCCEEDED(m_pConnection->InitializeAsClient(pArgs))) { + Status = STATUS_SUCCESS; + } + else { + Status = STATUS_INTERNAL_ERROR; + } + } + else { + Status = STATUS_INSUFFICIENT_RESOURCES; + } + + MethodReturn(Status, "Status = %!STATUS!", Status); +} + +void CFileObject::HandleArrivalEvent() +{ + MethodEntry("void"); + + DWORD dwFlags = 1; // payload for arrival event + + EnterCriticalSection(&m_RoleLock); + + NT_ASSERT(IsArrivedSubscription()); + + if (m_fEnabled) { + HandleReceivedMessage(sizeof(DWORD), (PBYTE) &dwFlags); + } + + LeaveCriticalSection(&m_RoleLock); + MethodReturnVoid(); +} + +void CFileObject::HandleRemovalEvent() +{ + MethodEntry("void"); + + DWORD dwFlags = 0; // payload for removal event + + EnterCriticalSection(&m_RoleLock); + + NT_ASSERT(IsDepartedSubscription()); + + if (m_fEnabled) { + HandleReceivedMessage(sizeof(DWORD), (PBYTE) &dwFlags); + } + + LeaveCriticalSection(&m_RoleLock); + MethodReturnVoid(); +} + +void CFileObject::HandleMessageTransmitted(void) +{ + MethodEntry("m_fEnabled=%!bool!", m_fEnabled); + + EnterCriticalSection(&m_RoleLock); + + NT_ASSERT(IsPublication()); + + if (m_fEnabled) { + if (!CompleteRequest(STATUS_SUCCESS, 0, true)) { + m_cCompleteReady++; + } + } + + LeaveCriticalSection(&m_RoleLock); + MethodReturnVoid(); +} + +void +CFileObject::HandleReceivedMessage( + _In_ PCWSTR pszType, + _In_ DWORD cbPayload, + _In_reads_bytes_(cbPayload) PBYTE pbPayload + ) +{ + MethodEntry("Enabled=%!bool!", m_fEnabled); + + EnterCriticalSection(&m_RoleLock); + + NT_ASSERT(IsNormalSubscription()); + + if (m_fEnabled) { + 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)) { + + CHAR szMimeType[MaxCchMimeType + 1] = {}; + BYTE* pbNewPayload = new BYTE[cbPayload + MaxCchMimeType]; + + if (pbNewPayload != nullptr) { + if (SUCCEEDED(StringCchPrintfA(szMimeType, _countof(szMimeType), "%S", pszType + WINDOWSMIME_PROTOCOL_CHARS + 1))) { + RtlCopyMemory(pbNewPayload, szMimeType, MaxCchMimeType); + RtlCopyMemory(pbNewPayload + MaxCchMimeType, pbPayload, cbPayload); + + HandleReceivedMessage(cbPayload + MaxCchMimeType, pbNewPayload); + } + + SAFE_DELETEARRAY(pbNewPayload); + } + } + else if (CompareStringOrdinal(pszType, -1, m_pszType, -1, FALSE) == CSTR_EQUAL) { + if (CompareStringOrdinal(m_pszType, -1, WINDOWSURI_PROTOCOL, -1, FALSE) == CSTR_EQUAL) { + // WindowsUri must be returned as NULL terminated UTF16 + DWORD cbNewPayload = cbPayload + sizeof(WCHAR); + BYTE* pbNewPayload = new BYTE[cbNewPayload]; + + if (pbNewPayload != nullptr) { + RtlCopyMemory(pbNewPayload, pbPayload, cbPayload); + RtlZeroMemory(pbNewPayload + cbPayload, sizeof(WCHAR)); + + HandleReceivedMessage(cbNewPayload, pbNewPayload); + + SAFE_DELETEARRAY(pbNewPayload); + } + } + else { + HandleReceivedMessage(cbPayload, pbPayload); + } + } + } + + LeaveCriticalSection(&m_RoleLock); + MethodReturnVoid(); +} + +void CFileObject::HandleReceiveHcePacket( + _In_ USHORT uConnectionId, + _In_ DWORD cbPayload, + _In_reads_bytes_(cbPayload) PBYTE pbPayload + ) +{ + MethodEntry("..."); + + size_t cbUsedBufferSize = 0; + DWORD cbNewPayload = cbPayload + 2 * sizeof(USHORT); + + EnterCriticalSection(&m_RoleLock); + + NT_ASSERT(IsSecureElementManager()); + + BYTE* pbNewPayload = new BYTE[cbNewPayload]; + + if (pbNewPayload != nullptr) { + RtlCopyMemory(pbNewPayload + cbUsedBufferSize, &uConnectionId, sizeof(USHORT)); + cbUsedBufferSize += sizeof(USHORT); + USHORT cbSize = (USHORT)cbPayload; + + RtlCopyMemory(pbNewPayload + cbUsedBufferSize, &cbSize, sizeof(USHORT)); + cbUsedBufferSize += sizeof(USHORT); + RtlCopyMemory(pbNewPayload + cbUsedBufferSize, pbPayload, cbPayload); + + HandleReceivedMessage(cbNewPayload, pbNewPayload); + + SAFE_DELETEARRAY(pbNewPayload); + } + LeaveCriticalSection(&m_RoleLock); + MethodReturnVoid(); +} + +void CFileObject::HandleSecureElementEvent(SECURE_ELEMENT_EVENT_INFO* pInfo) +{ + MethodEntry("..."); + + EnterCriticalSection(&m_RoleLock); + + NT_ASSERT(IsSecureElementEvent()); + + if ((IsEqualGUID(m_SecureElementId, pInfo->guidSecureElementId) || IsEqualGUID(m_SecureElementId, GUID_NULL)) && + (m_SecureElementEventType == pInfo->eEventType)) { + BYTE* pbPayload = (BYTE*)pInfo; + DWORD cbPayload = SECURE_ELEMENT_EVENT_INFO_HEADER + pInfo->cbEventData; + + HandleReceivedMessage(cbPayload, pbPayload); + } + + LeaveCriticalSection(&m_RoleLock); + MethodReturnVoid(); +} + +VOID CFileObject::HandleReceivedMessage( + _In_ DWORD cbPayload, + _In_reads_bytes_(cbPayload) PBYTE pbPayload + ) +{ + bool fDelivered = false; + + if (m_Request != nullptr) { + fDelivered = CompleteRequest(cbPayload, pbPayload); + } + + if ((!fDelivered) && (m_dwQueueSize < MAX_MESSAGE_QUEUE_SIZE)) { + CPayload* pPayload = new CPayload(); + + if (pPayload != nullptr) { + if (NT_SUCCESS(pPayload->Initialize(cbPayload, pbPayload))) { + InsertTailList(&m_Queue, pPayload->GetListEntry()); + m_dwQueueSize++; + } + else { + delete pPayload; + } + } + } +} + +VOID CFileObject::OnRequestCancel(_In_ WDFREQUEST Request) +{ + FunctionEntry("..."); + + CFileObject *pFileObject = GetFileObject(WdfRequestGetFileObject(Request)); + + NT_ASSERT(pFileObject != nullptr); + pFileObject->Cancel(); + + FunctionReturnVoid(); +} + +void CFileObject::Cancel() +{ + MethodEntry("..."); + + EnterCriticalSection(&m_RoleLock); + CompleteRequest(STATUS_CANCELLED, 0, false); + LeaveCriticalSection(&m_RoleLock); + + MethodReturnVoid(); +} + +bool +CFileObject::CompleteRequest( + _In_ DWORD cbPayload, + _In_reads_bytes_opt_(cbPayload) PBYTE pbPayload + ) +{ + MethodEntry("cbPayload = %d", cbPayload); + + NTSTATUS Status = STATUS_SUCCESS; + bool fDelivered = false; + WDFMEMORY OutputMemory; + size_t cbUsedBufferSize = 0; + size_t cbMaxBufferSize = 0; + + NT_ASSERT(m_Request != nullptr); + + Status = WdfRequestRetrieveOutputMemory(m_Request, &OutputMemory); + + if (NT_SUCCESS(Status) && OutputMemory != nullptr) { + // Set the first 4 bytes as the size of the payload as a hint for future subscriptions. + Status = WdfMemoryCopyFromBuffer(OutputMemory, cbUsedBufferSize, &cbPayload, sizeof(DWORD)); + cbUsedBufferSize += sizeof(DWORD); + + if (NT_SUCCESS(Status)) { + if (pbPayload != nullptr) { + WdfMemoryGetBuffer(OutputMemory, &cbMaxBufferSize); + + if (cbMaxBufferSize < (cbPayload + cbUsedBufferSize)) { + // We are unable to copy the payload into the output memory, + // Returning this signals to the client to send a bigger buffer + Status = STATUS_BUFFER_OVERFLOW; + } + else { + Status = WdfMemoryCopyFromBuffer(OutputMemory, cbUsedBufferSize, pbPayload, cbPayload); + } + } + + if (NT_SUCCESS(Status)) { + fDelivered = true; + cbUsedBufferSize += cbPayload; + } + } + + if (!CompleteRequest(Status, cbUsedBufferSize, true)) { + fDelivered = false; + } + } + + MethodReturnBool(fDelivered); +} + + +bool +CFileObject::CompleteRequest( + _In_ NTSTATUS CompletionStatus, + _In_ size_t cbSize, + _In_ bool fIsCancelable + ) +{ + MethodEntry("CompletionStatus = %!STATUS!, cbSize = %d, fIsCancelable = %!bool!", + CompletionStatus, (DWORD)cbSize, fIsCancelable); + + NTSTATUS Status = STATUS_SUCCESS; + bool fCompleted = false; + + if (m_Request != nullptr) { + if (fIsCancelable) { + Status = WdfRequestUnmarkCancelable(m_Request); + } + + if (NT_SUCCESS(Status)) { + WdfRequestCompleteWithInformation(m_Request, CompletionStatus, cbSize); + m_Request = nullptr; + fCompleted = true; + } + } + + MethodReturnBool(fCompleted); +} + +void CFileObject::PurgeQueue() +{ + m_dwQueueSize = 0; + + while (!IsListEmpty(&m_Queue)) { + delete CPayload::FromListEntry(RemoveHeadList(&m_Queue)); + } +} \ No newline at end of file diff --git a/nfc/Simulator/Src/FileContext.h b/nfc/Simulator/Src/FileContext.h new file mode 100644 index 000000000..d6470423d --- /dev/null +++ b/nfc/Simulator/Src/FileContext.h @@ -0,0 +1,268 @@ +/*++ + +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: + + Windows User-Mode Driver Framework (WUDF) + +Revision History: + +--*/ +#pragma once + +class CPayload +{ +public: + CPayload() + { + m_pbPayload = nullptr; + m_cbPayload = 0; + InitializeListHead(&m_ListEntry); + } + ~CPayload() + { + SAFE_DELETEARRAY(m_pbPayload); + } + NTSTATUS Initialize( + _In_ DWORD cbPayload, + _In_reads_bytes_(cbPayload) PBYTE pbPayload + ) + { + NTSTATUS Status = STATUS_SUCCESS; + + m_pbPayload = new BYTE[cbPayload]; + + if (m_pbPayload != nullptr) { + m_cbPayload = cbPayload; + RtlCopyMemory(m_pbPayload, pbPayload, cbPayload); + } + else { + Status = STATUS_INSUFFICIENT_RESOURCES; + } + return Status; + } + PBYTE GetPayload() + { + return m_pbPayload; + } + DWORD GetSize() + { + return m_cbPayload; + } + PLIST_ENTRY GetListEntry() + { + return &m_ListEntry; + } + static CPayload* FromListEntry(PLIST_ENTRY pEntry) + { + return (CPayload*) CONTAINING_RECORD(pEntry, CPayload, m_ListEntry); + } + +private: + PBYTE m_pbPayload; + DWORD m_cbPayload; + LIST_ENTRY m_ListEntry; +}; + +class CFileObject +{ +public: + CFileObject(WDFFILEOBJECT FileObject); + ~CFileObject(); + +public: + static EVT_WDF_OBJECT_CONTEXT_DESTROY OnDestroy; + static EVT_WDF_REQUEST_CANCEL OnRequestCancel; + +public: + NTSTATUS Enable(); + NTSTATUS Disable(); + NTSTATUS SetType(_In_ PCWSTR pszType); + NTSTATUS GetNextSubscribedMessage(_In_ WDFREQUEST Request); + NTSTATUS SetPayload(_In_ DWORD cbPayload, _In_reads_bytes_(cbPayload) PBYTE pbPayload); + NTSTATUS GetNextTransmittedMessage(_In_ WDFREQUEST Request); + NTSTATUS GetNextSecureElementPayload(_In_ WDFREQUEST Request); + NTSTATUS SubscribeForEvent(_In_ GUID& SecureElementId, _In_ SECURE_ELEMENT_EVENT_TYPE SecureElementEventType); + NTSTATUS BeginProximity(_In_ BEGIN_PROXIMITY_ARGS *pArgs, _In_ IConnectionCallback* pCallback); + + void HandleArrivalEvent(); + void HandleRemovalEvent(); + void HandleReceivedMessage(_In_ PCWSTR pszType, _In_ DWORD cbPayload, _In_reads_bytes_(cbPayload) PBYTE pbPayload); + void HandleMessageTransmitted(); + void HandleReceiveHcePacket(_In_ USHORT uConnectionId, _In_ DWORD cbPayload, _In_reads_bytes_(cbPayload) PBYTE pbPayload); + void HandleSecureElementEvent(SECURE_ELEMENT_EVENT_INFO* pInfo); + +public: + 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; + } + void SetRoleSmartCardReader() + { + m_Role = ROLE_SMARTCARDREADER; + } + void SetRoleSecureElementEvent() + { + m_Role = ROLE_SECUREELEMENTEVENT; + } + void SetRoleSecureElementManager() + { + m_Role = ROLE_SECUREELEMENTMANAGER; + } + void SetRoleSimulation() + { + m_Role = ROLE_SIMULATION; + } + void SetRoleRadioManager() + { + m_Role = ROLE_RADIOMANAGER; + } + +public: + 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) || (m_Role == ROLE_ARRIVEDSUBSCRIPTION) || (m_Role == ROLE_DEPARTEDSUBSCRIPTION); + } + BOOL IsPublication() + { + return (m_Role == ROLE_PUBLICATION); + } + BOOL IsSmartCardReader() + { + return (m_Role == ROLE_SMARTCARDREADER); + } + BOOL IsSecureElementEvent() + { + return (m_Role == ROLE_SECUREELEMENTEVENT); + } + BOOL IsSecureElementManager() + { + return (m_Role == ROLE_SECUREELEMENTMANAGER); + } + BOOL IsRoleSimulation() + { + return (m_Role == ROLE_SIMULATION); + } + BOOL IsRoleRadioManager() + { + return (m_Role == ROLE_RADIOMANAGER); + } + BOOL IsRoleUndefined() + { + return (m_Role == ROLE_UNDEFINED); + } + PCWSTR GetType() + { + return m_pszType; + } + DWORD GetSize() + { + return m_Payload.GetSize(); + } + PBYTE GetPayload() + { + return m_Payload.GetPayload(); + } + BOOL IsEnabled() + { + return m_fEnabled; + } + GUID& GetSecureElementId() + { + return m_SecureElementId; + } + SECURE_ELEMENT_EVENT_TYPE GetSecureElementEventType() + { + return m_SecureElementEventType; + } + PLIST_ENTRY GetListEntry() + { + return &m_ListEntry; + } + static CFileObject* FromListEntry(PLIST_ENTRY pEntry) + { + return (CFileObject*) CONTAINING_RECORD(pEntry, CFileObject, m_ListEntry); + } + +private: + void HandleReceivedMessage(_In_ DWORD cbPayload, _In_reads_bytes_(cbPayload) PBYTE pbPayload); + bool CompleteRequest(_In_ DWORD cbPayload, _In_reads_bytes_opt_(cbPayload) PBYTE pbPayload); + bool CompleteRequest(_In_ NTSTATUS CompletionStatus, _In_ size_t cbSize, _In_ bool fIsCancelable); + void PurgeQueue(); + void Cancel(); + +private: + enum ROLE + { + ROLE_UNDEFINED, + ROLE_SUBSCRIPTION, + ROLE_ARRIVEDSUBSCRIPTION, + ROLE_DEPARTEDSUBSCRIPTION, + ROLE_PUBLICATION, + ROLE_SMARTCARDREADER, + ROLE_SECUREELEMENTEVENT, + ROLE_SECUREELEMENTMANAGER, + ROLE_SIMULATION, + ROLE_RADIOMANAGER + }; + + WDFFILEOBJECT m_FileObject; // The Fx File object this CFileObject is associated with + ROLE m_Role; + PWSTR m_pszType; + BOOL m_fEnabled; + DWORD m_dwQueueSize; + LIST_ENTRY m_Queue; // Unique to ROLE_SUBSCRIPTION and ROLE_SECUREELEMENTEVENT + CPayload m_Payload; // Unique to ROLE_PUBLICATION + size_t m_cCompleteReady; // Unique to ROLE_PUBLICATION + CConnection* m_pConnection; // Unique to ROLE_SIMULATION + WDFREQUEST m_Request; // Pended "Get Next" Request + CRITICAL_SECTION m_RoleLock; + LIST_ENTRY m_ListEntry; + GUID m_SecureElementId; // Unique to ROLE_SECUREELEMENTEVENT + SECURE_ELEMENT_EVENT_TYPE m_SecureElementEventType; // Unique to ROLE_SECUREELEMENTEVENT +}; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(CFileObject, GetFileObject); \ No newline at end of file diff --git a/nfc/Simulator/Src/Internal.h b/nfc/Simulator/Src/Internal.h new file mode 100644 index 000000000..ad6426288 --- /dev/null +++ b/nfc/Simulator/Src/Internal.h @@ -0,0 +1,84 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved + +Module Name: + + Internal.h + +Abstract: + + This module contains the local type definitions for the UMDF driver. + +Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + +#pragma once + +// +// Include the type specific headers. +// +#include +#include +#include +#include +#include +#pragma warning( disable: 4201 ) // nonstandard extension used : nameless struct/union +#include +#include +#include +#define INIT_GUID +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// +// Define the tracing GUID for this driver +// +#define TRACE_CONTROL_GUID (12579E92, 1B46, 40A6, 9CFC, C718A677830B) + +#ifndef SAFE_DELETEARRAY +#define SAFE_DELETEARRAY(x) if ((x) != nullptr) { delete [] (x); (x) = nullptr; } +#endif + +#ifndef SAFE_DELETE +#define SAFE_DELETE(x) if ((x) != nullptr) { delete (x); (x) = nullptr; } +#endif + +#ifndef SAFE_CLOSEHANDLE +#define SAFE_CLOSEHANDLE(x) if ((x) != nullptr) { CloseHandle(x); (x) = nullptr; } +#endif + +#ifndef SAFE_FREELIBRARY +#define SAFE_FREELIBRARY(x) if ((x) != nullptr) { FreeLibrary(x); (x) = nullptr; } +#endif + +#include "nfcsimulatorddi.h" +#include "constants.h" +#include "linklist.h" +#include "wppdefs.h" +#include "connection.h" +#include "filecontext.h" +#include "secureelement.h" +#include "driver.h" +#include "device.h" +#include "smartcard.h" +#include "smartcardreader.h" +#include "routingtable.h" +#include "queue.h" + diff --git a/nfc/Simulator/Src/Internalsrc.cpp b/nfc/Simulator/Src/Internalsrc.cpp new file mode 100644 index 000000000..1e8fe6f23 --- /dev/null +++ b/nfc/Simulator/Src/Internalsrc.cpp @@ -0,0 +1 @@ +#include "Internal.h" \ No newline at end of file diff --git a/nfc/Simulator/Src/LinkList.h b/nfc/Simulator/Src/LinkList.h new file mode 100644 index 000000000..682d74209 --- /dev/null +++ b/nfc/Simulator/Src/LinkList.h @@ -0,0 +1,121 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +#pragma once + +// +// Calculate the address of the base of the structure given its type, and an +// address of a field within the structure. +// +#ifndef CONTAINING_RECORD +#define CONTAINING_RECORD(address, type, field) \ + ((type *)((PCHAR)(address) - (ULONG_PTR)(&((type *)0)->field))) +#endif + + +#ifndef InitializeListHead + +FORCEINLINE +VOID +InitializeListHead( + _Out_ PLIST_ENTRY ListHead + ) +{ + ListHead->Flink = ListHead->Blink = ListHead; + return; +} + +_Must_inspect_result_ +BOOLEAN +CFORCEINLINE +IsListEmpty( + _In_ const LIST_ENTRY * ListHead + ) +{ + return (BOOLEAN)(ListHead->Flink == ListHead); +} + +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 +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 +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; + return; +} + +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; + return; +} + +#endif // InitializeListHead diff --git a/nfc/Simulator/Src/NfcDriverSim.vcxproj b/nfc/Simulator/Src/NfcDriverSim.vcxproj new file mode 100644 index 000000000..70b3e49c4 --- /dev/null +++ b/nfc/Simulator/Src/NfcDriverSim.vcxproj @@ -0,0 +1,371 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {E60D786C-D146-4B1A-835D-D62B883D7F00} + $(MSBuildProjectName) + 2 + Debug + Win32 + {2082F9B4-EC5F-4180-9398-6493BA69FB07} + + + + 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 + ;%(AdditionalIncludeDirectories) + Internal.h + Use + $(IntDir)\Internal.h.pch + + + true + true + WppDefs.h + ;%(AdditionalIncludeDirectories) + Internal.h + Use + $(IntDir)\Internal.h.pch + + + true + true + WppDefs.h + ;%(AdditionalIncludeDirectories) + Internal.h + Use + $(IntDir)\Internal.h.pch + + + true + true + WppDefs.h + ;%(AdditionalIncludeDirectories) + Internal.h + Use + $(IntDir)\Internal.h.pch + + + true + true + WppDefs.h + ;%(AdditionalIncludeDirectories) + Internal.h + Use + $(IntDir)\Internal.h.pch + + + true + true + WppDefs.h + ;%(AdditionalIncludeDirectories) + Internal.h + Use + $(IntDir)\Internal.h.pch + + + true + true + WppDefs.h + ;%(AdditionalIncludeDirectories) + Internal.h + Use + $(IntDir)\Internal.h.pch + + + true + true + WppDefs.h + ;%(AdditionalIncludeDirectories) + Internal.h + Use + $(IntDir)\Internal.h.pch + + + true + true + WppDefs.h + ;%(AdditionalIncludeDirectories) + Internal.h + Use + $(IntDir)\Internal.h.pch + + + true + true + WppDefs.h + ;%(AdditionalIncludeDirectories) + Internal.h + Use + $(IntDir)\Internal.h.pch + + + $(InfArch) + true + true + .\$(IntDir)\NfcSimulator.inf + + + true + true + WppDefs.h + + + + NfcDriverSim + + + NfcDriverSim + + + NfcDriverSim + + + NfcDriverSim + + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + 0x2000000 + + + + + 0x2000000 + + + + + 0x2000000 + + + + + 0x2000000 + + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE;WPP_MACRO_USE_KM_VERSION_FOR_UM=1 + true + Level4 + %(AdditionalIncludeDirectories);.\$(IntDir);..\inc;$(DDK_INC_PATH) + + + %(PreprocessorDefinitions);UNICODE;_UNICODE;WPP_MACRO_USE_KM_VERSION_FOR_UM=1 + %(AdditionalIncludeDirectories);.\$(IntDir);..\inc;$(DDK_INC_PATH) + + + %(PreprocessorDefinitions);UNICODE;_UNICODE;WPP_MACRO_USE_KM_VERSION_FOR_UM=1 + %(AdditionalIncludeDirectories);.\$(IntDir);..\inc;$(DDK_INC_PATH) + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib + + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE;WPP_MACRO_USE_KM_VERSION_FOR_UM=1 + true + Level4 + %(AdditionalIncludeDirectories);.\$(IntDir);..\inc;$(DDK_INC_PATH) + + + %(PreprocessorDefinitions);UNICODE;_UNICODE;WPP_MACRO_USE_KM_VERSION_FOR_UM=1 + %(AdditionalIncludeDirectories);.\$(IntDir);..\inc;$(DDK_INC_PATH) + + + %(PreprocessorDefinitions);UNICODE;_UNICODE;WPP_MACRO_USE_KM_VERSION_FOR_UM=1 + %(AdditionalIncludeDirectories);.\$(IntDir);..\inc;$(DDK_INC_PATH) + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib + + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE;WPP_MACRO_USE_KM_VERSION_FOR_UM=1 + true + Level4 + %(AdditionalIncludeDirectories);.\$(IntDir);..\inc;$(DDK_INC_PATH) + + + %(PreprocessorDefinitions);UNICODE;_UNICODE;WPP_MACRO_USE_KM_VERSION_FOR_UM=1 + %(AdditionalIncludeDirectories);.\$(IntDir);..\inc;$(DDK_INC_PATH) + + + %(PreprocessorDefinitions);UNICODE;_UNICODE;WPP_MACRO_USE_KM_VERSION_FOR_UM=1 + %(AdditionalIncludeDirectories);.\$(IntDir);..\inc;$(DDK_INC_PATH) + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib + + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE;WPP_MACRO_USE_KM_VERSION_FOR_UM=1 + true + Level4 + %(AdditionalIncludeDirectories);.\$(IntDir);..\inc;$(DDK_INC_PATH) + + + %(PreprocessorDefinitions);UNICODE;_UNICODE;WPP_MACRO_USE_KM_VERSION_FOR_UM=1 + %(AdditionalIncludeDirectories);.\$(IntDir);..\inc;$(DDK_INC_PATH) + + + %(PreprocessorDefinitions);UNICODE;_UNICODE;WPP_MACRO_USE_KM_VERSION_FOR_UM=1 + %(AdditionalIncludeDirectories);.\$(IntDir);..\inc;$(DDK_INC_PATH) + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib + + + + + NfcSimulator.def + + + + + + + + + NfcSimulator.def + + + + + + + + + NfcSimulator.def + + + + + + + + + NfcSimulator.def + + + + + + + + + ;%(AdditionalIncludeDirectories) + Internal.h + Create + $(IntDir)\Internal.h.pch + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/nfc/Simulator/Src/NfcDriverSim.vcxproj.Filters b/nfc/Simulator/Src/NfcDriverSim.vcxproj.Filters new file mode 100644 index 000000000..bc53424ed --- /dev/null +++ b/nfc/Simulator/Src/NfcDriverSim.vcxproj.Filters @@ -0,0 +1,69 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {F02AF408-2502-49D2-B3D0-F9C353878FA6} + + + h;hpp;hxx;hm;inl;inc;xsd + {18A112F1-3B09-4196-8A5A-4D653CB64F5C} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {85A45C49-C7DD-4CD3-85D3-AFB43AFF2962} + + + inf;inv;inx;mof;mc; + {7D686BE0-2BB7-4EB9-B71C-9A7698008D64} + + + + + 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 + + + + + Resource Files + + + \ No newline at end of file diff --git a/nfc/Simulator/Src/NfcSimulator.def b/nfc/Simulator/Src/NfcSimulator.def new file mode 100644 index 000000000..c765118c3 --- /dev/null +++ b/nfc/Simulator/Src/NfcSimulator.def @@ -0,0 +1,6 @@ +; +; Copyright (c) Microsoft Corporation. All rights reserved. +; +LIBRARY "NfcDriverSim.dll" + +EXPORTS diff --git a/nfc/Simulator/Src/NfcSimulator.inx b/nfc/Simulator/Src/NfcSimulator.inx new file mode 100644 index 0000000000000000000000000000000000000000..465c3d5b720af4d7e89b9d432cee4dcb26ef1914 GIT binary patch literal 4110 zcmb`K+in_H6o%JzrM?5Qg46?Gg8|32=7NBUtu}UL?BoJRhyZp`8xsLeYejkWw%@<^ zY|jk#5FACLIqtRB;a|u3@81L4wx8^0j_>U|JBY7so7o4uv$;*}+WxSm73>Ea*^k^` z@omJ)%odziZcX%acIZ0f%ao@Zt3X|_mi@(1Nby>@w+Z_veErCE3~gHeEB24=mVFaw zPvH5Ur(a?kan*5x^pW+D_TJG-vNWjh<%YF1$CkZA-qPte;=ao37;as7Ua{9py>h$? zj+b^0ogLzQ4c!t{yl!1<^S*eXMF9(hpX{4wbSYaI%(vY)>Daf|jz&C$+grCHyRLbY zRkv`MSY%uF%-)!E?bs<#J%_?MU$@+zHaZT`WdZH)?3vj=POm9E?p7#X!gY#{k$tj; zHEq{+>^ave_LO;Ua^L5v&R4CM?FFkf+vBOrmyUaDu-ddkN55mAoeVGeqvI~yBP+(* zB;R^yBy@yk(e3KEcaN6GBXSyTbKM868uuzPOI#~F`?DHbC6wf$8T`8Nl@Fse5i)YP z3VrW^4)oG)#Qhkp5|7Jf?~xX0jj=nSEFhy`XZYgO&h0W+-X;3_qlEr>aXWz1jANOR zF}T+!(e`UOyhU4aIl}rmw*2kxvUrTe-Wo-XbjY_R=yBk7L}2ixqmZ$vaBq>%DP_+D zsO75sd#>i!S!uYMzt75^tNDl2 z{|-{r4cgqrufh2_?l|8^Nz@yvL*Kqm=0|R} z=T)d4X-z_A`-sl2MP$hZUg#2=s+vjc37@K~$)yxc#|A6hv$6wj{WW=(-BE%27&$&m zE|>=OwyI^G?Io&J-r4}Wk{NEb5=qm?rJb^C>=4Ya7U|tbL zk>&fs*(BTUu&YRv)VN-zqC|fL3sx5?TfWXq*{Jvp$?uTsWoPH^h#nt$$ZKld86G%h zvO9@;|2iACQdR^WVS-A>knjGXyZWhJk(p# zZz{Xmu;jkF!}}v|nhbN! +#include + +#define VER_FILETYPE VFT_DLL +#define VER_FILESUBTYPE VFT_UNKNOWN +#define VER_FILEDESCRIPTION_STR "NFC Simulator Driver" +#define VER_INTERNALNAME_STR "NfcDriverSim.dll" +#define VER_ORIGINALFILENAME_STR "NfcDriverSim.dll" + +#include "common.ver" diff --git a/nfc/Simulator/Src/Queue.cpp b/nfc/Simulator/Src/Queue.cpp new file mode 100644 index 000000000..c72dfba9a --- /dev/null +++ b/nfc/Simulator/Src/Queue.cpp @@ -0,0 +1,2420 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + queue.cpp + +Abstract: + + This file implements the I/O queue interface and performs the ioctl operations. + +Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + +#include "Internal.h" +#include "Queue.tmh" + +CQueue::CQueue(WDFQUEUE Queue) : + m_Queue(Queue), + m_SmartCardReader(WdfIoQueueGetDevice(Queue)), + m_SocketListener(this), + m_pSEManager(nullptr), + m_pHCEConnection(nullptr), + m_NfpRadioState(TRUE), + m_NfpRadioOffPolicyOverride(FALSE), + m_NfpRadioOffSystemOverride(FALSE), + m_SERadioState(TRUE), + m_NfpInterfaceCreated(FALSE), + m_ScInterfaceCreated(FALSE), + m_SEInterfaceCreated(FALSE), + m_RoutingTable(this), + m_HCEConnectionId(0) +{ + NT_ASSERT(m_Queue != nullptr); + + InitializeListHead(&m_SubsList); + InitializeListHead(&m_ArrivalSubsList); + InitializeListHead(&m_DepartureSubsList); + InitializeListHead(&m_PubsList); + InitializeListHead(&m_ConnectionList); + InitializeListHead(&m_SecureElementList); + InitializeListHead(&m_SEEventsList); + + InitializeCriticalSection(&m_SubsLock); + InitializeCriticalSection(&m_PubsLock); + InitializeCriticalSection(&m_ConnectionLock); + InitializeCriticalSection(&m_SEManagerLock); + InitializeCriticalSection(&m_SEEventsLock); + InitializeCriticalSection(&m_RadioLock); + InitializeCriticalSection(&m_pHCEConnectionLock); +} + +CQueue::~CQueue() +{ + DeleteCriticalSection(&m_SubsLock); + DeleteCriticalSection(&m_PubsLock); + DeleteCriticalSection(&m_ConnectionLock); + DeleteCriticalSection(&m_SEEventsLock); + DeleteCriticalSection(&m_RadioLock); + DeleteCriticalSection(&m_pHCEConnectionLock); + + m_Queue = nullptr; +} + +NTSTATUS CQueue::Initialize() +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + WSADATA WsaData = {}; + + DECLARE_CONST_UNICODE_STRING(RMNamespace, RM_NAMESPACE); + DECLARE_CONST_UNICODE_STRING(SCNamespace, SMARTCARD_READER_NAMESPACE); + DECLARE_CONST_UNICODE_STRING(SimulatorNamespace, SIM_NAMESPACE); + + ReadSettingsFromRegistry(); + + if (WSAStartup(MAKEWORD(2,2), &WsaData) != 0) { + Status = STATUS_INTERNAL_ERROR; + goto Exit; + } + + + if (!NT_SUCCESS(m_SmartCardReader.Initialize())) { + Status = STATUS_INTERNAL_ERROR; + goto Exit; + } + + if (FAILED(m_SocketListener.Bind())) { + Status = STATUS_INTERNAL_ERROR; + goto Exit; + } + + if (FAILED(m_SocketListener.EnableAccepting())) { + Status = STATUS_INTERNAL_ERROR; + goto Exit; + } + + EnumerateSecureElements(); + m_RoutingTable.Initialize(WdfIoQueueGetDevice(m_Queue)); + + // Register for NFP Radio Manager interface + Status = WdfDeviceCreateDeviceInterface( + WdfIoQueueGetDevice(m_Queue), + (LPGUID) &GUID_NFC_RADIO_MEDIA_DEVICE_INTERFACE, + &RMNamespace); + + if (!NT_SUCCESS(Status)) { + TraceInfo("WdfDeviceCreateDeviceInterface failed with %!STATUS!", Status); + goto Exit; + } + + if (IsNfpRadioEnabled()) { + // Register for Proximity interface + Status = WdfDeviceCreateDeviceInterface( + WdfIoQueueGetDevice(m_Queue), + (LPGUID) &GUID_DEVINTERFACE_NFP, + nullptr); + + if (!NT_SUCCESS(Status)) { + TraceInfo("WdfDeviceCreateDeviceInterface failed with Status %!STATUS!", Status); + goto Exit; + } + + m_NfpInterfaceCreated = TRUE; + + // Register for Smart Card interface + Status = WdfDeviceCreateDeviceInterface( + WdfIoQueueGetDevice(m_Queue), + (LPGUID) &GUID_DEVINTERFACE_SMARTCARD_READER, + &SCNamespace); + + if (!NT_SUCCESS(Status)) { + TraceInfo("WdfDeviceCreateDeviceInterface failed with Status %!STATUS!", Status); + goto Exit; + } + + m_ScInterfaceCreated = TRUE; + } + + if (IsSERadioEnabled()) { + // Register for Secure Element interface + Status = WdfDeviceCreateDeviceInterface( + WdfIoQueueGetDevice(m_Queue), + (LPGUID) &GUID_DEVINTERFACE_NFCSE, + nullptr); + + if (!NT_SUCCESS(Status)) { + TraceInfo("WdfDeviceCreateDeviceInterface failed with Status %!STATUS!", Status); + goto Exit; + } + + m_SEInterfaceCreated = TRUE; + } + + // Register for Simulator interface + Status = WdfDeviceCreateDeviceInterface( + WdfIoQueueGetDevice(m_Queue), + (LPGUID) &GUID_DEVINTERFACE_NFCSIM, + &SimulatorNamespace); + + if (!NT_SUCCESS(Status)) { + TraceInfo("WdfDeviceCreateDeviceInterface failed with %!STATUS!", Status); + goto Exit; + } + +Exit: + MethodReturn(Status, "Status = %!STATUS!", Status); +} + +NTSTATUS CQueue::Deinitialize() +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + + while (!IsListEmpty(&m_SecureElementList)) { + delete CSecureElement::FromListEntry(RemoveHeadList(&m_SecureElementList)); + } + + + m_SocketListener.StopAccepting(); + m_SmartCardReader.Deinitialize(); + + while (!IsListEmpty(&m_ConnectionList)) { + delete CConnection::FromListEntry(RemoveHeadList(&m_ConnectionList)); + } + + WSACleanup(); + + MethodReturn(Status, "Status = %!STATUS!", Status); +} + +BOOLEAN CQueue::IsNfpRadioEnabled() +{ + BOOLEAN NfpRadioState; + + EnterCriticalSection(&m_RadioLock); + NfpRadioState = m_NfpRadioState; + LeaveCriticalSection(&m_RadioLock); + + return NfpRadioState; +} + +BOOLEAN CQueue::IsSERadioEnabled() +{ + BOOLEAN SERadioState; + + EnterCriticalSection(&m_RadioLock); + SERadioState = m_SERadioState; + LeaveCriticalSection(&m_RadioLock); + + return SERadioState; +} + +NTSTATUS CQueue::DetectRole(CFileObject *pFileObject, PCWSTR pszFileName) +{ + NTSTATUS Status = STATUS_SUCCESS; + PCWSTR pszProtocol = nullptr; + PCWSTR pszType = nullptr; + + MethodEntry("pszFileName = %S", pszFileName); + + if (IsStringPrefixed(pszFileName, PUBS_NAMESPACE)) { + if (IsNfpRadioEnabled()) { + pFileObject->SetRolePublication(); + pszProtocol = pszFileName + PUBS_NAMESPACE_CHARS; + } + else { + Status = STATUS_INVALID_DEVICE_STATE; + } + } + else if (IsStringPrefixed(pszFileName, SUBS_NAMESPACE)) { + if (IsNfpRadioEnabled()) { + pFileObject->SetRoleSubcription(); + pszProtocol = pszFileName + SUBS_NAMESPACE_CHARS; + } + else { + Status = STATUS_INVALID_DEVICE_STATE; + } + } + else if (IsStringEqual(pszFileName, SEEVENTS_NAMESPACE)) { + if (IsSERadioEnabled()) { + pFileObject->SetRoleSecureElementEvent(); + } + else { + Status = STATUS_INVALID_DEVICE_STATE; + } + } + else if (IsStringEqual(pszFileName, SEMANAGE_NAMESPACE)) { + if (IsSERadioEnabled()) { + pFileObject->SetRoleSecureElementManager(); + } + else { + Status = STATUS_INVALID_DEVICE_STATE; + } + } + else if (IsStringEqual(pszFileName, SMARTCARD_READER_NAMESPACE)) { + if (IsNfpRadioEnabled()) { + pFileObject->SetRoleSmartCardReader(); + } + else { + Status = STATUS_INVALID_DEVICE_STATE; + } + } + else if (IsStringEqual(pszFileName, SIM_NAMESPACE)) { + pFileObject->SetRoleSimulation(); + } + else if (IsStringEqual(pszFileName, RM_NAMESPACE)) { + pFileObject->SetRoleRadioManager(); + } + else { + Status = STATUS_OBJECT_PATH_NOT_FOUND; + } + + if (NT_SUCCESS(Status) && pszProtocol != nullptr) { + Status = STATUS_OBJECT_PATH_NOT_FOUND; + + if (IsStringPrefixed(pszProtocol, WINDOWS_PROTOCOL)) { + pszType = pszProtocol + WINDOWS_PROTOCOL_CHARS; + + if (*pszType == L'.') { + Status = pFileObject->SetType(pszProtocol); + } + else if (*pszType == L':') { + pszType = pszType + 1; + + if (pFileObject->IsPublication() && IsStringEqual(pszType, WRITETAG_TYPE)) { + Status = pFileObject->SetType(pszProtocol); + } + } + else if (IsStringPrefixed(pszType, WINDOWSURI_TYPE)) { + pszType = pszType + WINDOWSURI_TYPE_CHARS; + + if (*pszType == L'\0') { + Status = pFileObject->SetType(pszProtocol); + } + else if (*pszType == L':') { + pszType = pszType + 1; + + if (pFileObject->IsPublication() && IsStringEqual(pszType, WRITETAG_TYPE)) { + Status = pFileObject->SetType(pszProtocol); + } + } + } + else if (IsStringPrefixed(pszType, WINDOWSMIME_TYPE)) { + pszType = pszType + WINDOWSMIME_TYPE_CHARS; + + if (*pszType == L'\0') { + if (pFileObject->IsNormalSubscription()) { + Status = pFileObject->SetType(pszProtocol); + } + } + else if (*pszType == L':') { + pszType = pszType + 1; + + if (pFileObject->IsPublication() && IsStringEqual(pszType, WRITETAG_TYPE)) { + Status = pFileObject->SetType(pszProtocol); + } + } + else if (*pszType == L'.') { + Status = pFileObject->SetType(pszProtocol); + } + } + } + else if (IsStringPrefixed(pszProtocol, NDEF_PROTOCOL)) { + pszType = pszProtocol + NDEF_PROTOCOL_CHARS; + + if (*pszType == L'\0') { + Status = pFileObject->SetType(pszProtocol); + } + else if (*pszType == L':') { + pszType = pszType + 1; + + if (pFileObject->IsPublication()) { + if (IsStringEqual(pszType, WRITETAG_TYPE)) { + Status = pFileObject->SetType(pszProtocol); + } + } + else { + if (IsStringPrefixed(pszType, NDEF_EXT_TYPE) || + IsStringPrefixed(pszType, NDEF_MIME_TYPE) || + IsStringPrefixed(pszType, NDEF_URI_TYPE) || + IsStringPrefixed(pszType, NDEF_WKT_TYPE) || + IsStringPrefixed(pszType, NDEF_UNKNOWN_TYPE)) { + Status = pFileObject->SetType(pszProtocol); + } + } + } + } + else if (pFileObject->IsPublication()) { + if (IsStringEqual(pszProtocol, LAUNCHAPP_WRITETAG_PROTOCOL)) { + Status = pFileObject->SetType(pszProtocol); + } + } + else if (pFileObject->IsNormalSubscription()) { + if (IsStringEqual(pszProtocol, DEVICE_ARRIVED)) { + if (pFileObject->SetRoleArrivedSubcription()) { + Status = STATUS_SUCCESS; + } + } + else if (IsStringEqual(pszProtocol, DEVICE_DEPARTED)) { + if (pFileObject->SetRoleDepartedSubcription()) { + Status = STATUS_SUCCESS; + } + } + else if (IsStringEqual(pszProtocol, PAIRING_BLUETOOTH_PROTOCOL) || + IsStringEqual(pszProtocol, PAIRING_UPNP_PROTOCOL) || + IsStringEqual(pszProtocol, WRITEABLETAG_PROTOCOL)) { + Status = pFileObject->SetType(pszProtocol); + } + } + } + + MethodReturn(Status, "Status = %!STATUS!", Status); +} + +void +CQueue::OnFileCreate( + _In_ WDFDEVICE Device, + _In_ WDFREQUEST Request, + _In_ WDFFILEOBJECT FileObject + ) +{ + MethodEntry("Request = %p, FileObject = %p", Request, FileObject); + + NTSTATUS Status = STATUS_SUCCESS; + PUNICODE_STRING FileName = nullptr; + PCWSTR pszFileName = nullptr; + CFileObject *pFileObject = nullptr; + + UNREFERENCED_PARAMETER(Device); + + // Construct file object on the preallocated buffer using placement 'new' + pFileObject = new (GetFileObject(FileObject)) CFileObject(FileObject); + + if (pFileObject == nullptr) { + Status = STATUS_INSUFFICIENT_RESOURCES; + goto Exit; + } + + FileName = WdfFileObjectGetFileName(FileObject); + + if ((FileName != nullptr) && (FileName->Length > 0)) { + NT_ASSERT(FileName->Buffer != nullptr); + + if ((FileName->Length / sizeof(WCHAR)) > MaxCchType) { + Status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + pszFileName = FileName->Buffer; + pszFileName = (pszFileName[0] == L'\\') ? pszFileName+1 : pszFileName; + + Status = DetectRole(pFileObject, pszFileName); + + if (!NT_SUCCESS(Status)) { + goto Exit; + } + + TraceInfo("pszFileName = %S pFileObject = %p", pszFileName, pFileObject); + } + + if (pFileObject->IsNormalSubscription()) { + EnterCriticalSection(&m_SubsLock); + InsertHeadList(&m_SubsList, pFileObject->GetListEntry()); + LeaveCriticalSection(&m_SubsLock); + } + else if (pFileObject->IsArrivedSubscription()) { + EnterCriticalSection(&m_SubsLock); + InsertHeadList(&m_ArrivalSubsList, pFileObject->GetListEntry()); + LeaveCriticalSection(&m_SubsLock); + } + else if (pFileObject->IsDepartedSubscription()) { + EnterCriticalSection(&m_SubsLock); + InsertHeadList(&m_DepartureSubsList, pFileObject->GetListEntry()); + LeaveCriticalSection(&m_SubsLock); + } + else if (pFileObject->IsSmartCardReader()) { + Status = m_SmartCardReader.AddClient(pFileObject); + } + else if (pFileObject->IsSecureElementManager()) { + EnterCriticalSection(&m_SEManagerLock); + Status = InterlockedCompareExchangePointer((void**)&m_pSEManager, pFileObject, nullptr) == nullptr ? STATUS_SUCCESS : STATUS_ACCESS_DENIED; + LeaveCriticalSection(&m_SEManagerLock); + } + +Exit: + TraceInfo("%!FUNC! Completing Request with Status %!STATUS!", Status); + WdfRequestComplete(Request, Status); + + MethodReturnVoid(); +} + +void CQueue::OnFileClose(_In_ WDFFILEOBJECT FileObject) +{ + MethodEntry("..."); + + CFileObject* pFileObject = GetFileObject(FileObject); + CRITICAL_SECTION *pLock = nullptr; + LIST_ENTRY* pHead = nullptr; + + NT_ASSERT(pFileObject != nullptr); + + if (pFileObject->IsNormalSubscription()) { + pLock = &m_SubsLock; + pHead = &m_SubsList; + } + else if (pFileObject->IsArrivedSubscription()) { + pLock = &m_SubsLock; + pHead = &m_ArrivalSubsList; + } + else if (pFileObject->IsDepartedSubscription()) { + pLock = &m_SubsLock; + pHead = &m_DepartureSubsList; + } + else if (pFileObject->IsPublication()) { + pLock = &m_PubsLock; + pHead = &m_PubsList; + } + else if (pFileObject->IsSmartCardReader()) { + m_SmartCardReader.RemoveClient(pFileObject); + } + else if (pFileObject->IsSecureElementEvent()) { + pLock = &m_SEEventsLock; + pHead = &m_SEEventsList; + } + else if (pFileObject->IsSecureElementManager()) { + EnterCriticalSection(&m_SEManagerLock); + InterlockedCompareExchangePointer((void**)&m_pSEManager, nullptr, pFileObject); + LeaveCriticalSection(&m_SEManagerLock); + + for (LIST_ENTRY* pEntry = m_SecureElementList.Flink; + pEntry != &m_SecureElementList; + pEntry = pEntry->Flink) { + CSecureElement::FromListEntry(pEntry)->SetEmulationMode(EmulationOff); + } + } + + if (pHead != nullptr) { + EnterCriticalSection(pLock); + + for (LIST_ENTRY* pEntry = pHead->Flink; + pEntry != pHead; + pEntry = pEntry->Flink) { + if (pEntry == pFileObject->GetListEntry()) { + RemoveEntryList(pEntry); + break; + } + } + LeaveCriticalSection(pLock); + } + + MethodReturnVoid(); +} + +VOID +CQueue::OnIoDeviceControl( + _In_ WDFQUEUE Queue, + _In_ WDFREQUEST Request, + _In_ size_t OutputBufferLength, + _In_ size_t InputBufferLength, + _In_ ULONG IoControlCode + ) +{ + FunctionEntry("..."); + + CQueue *pQueue = GetQueueObject(Queue); + + NT_ASSERT(pQueue != nullptr); + pQueue->OnIoDeviceControl(Request, IoControlCode, InputBufferLength, OutputBufferLength); + + FunctionReturnVoid(); +} + +void +CQueue::OnIoDeviceControl( + _In_ WDFREQUEST Request, + _In_ ULONG IoControlCode, + _In_ size_t InputBufferLength, + _In_ size_t OutputBufferLength + ) +{ + MethodEntry("Request = %p, IoControlCode = 0x%x InputBufferLength=%d OutputBufferLength=%d", + Request, IoControlCode, (DWORD)InputBufferLength, (DWORD)OutputBufferLength); + + NTSTATUS Status = STATUS_INVALID_DEVICE_STATE; + + switch (IoControlCode) + { + case IOCTL_NFP_GET_MAX_MESSAGE_BYTES: + case IOCTL_NFP_GET_KILO_BYTES_PER_SECOND: + case IOCTL_NFP_DISABLE: + case IOCTL_NFP_ENABLE: + case IOCTL_NFP_SET_PAYLOAD: + case IOCTL_NFP_GET_NEXT_SUBSCRIBED_MESSAGE: + case IOCTL_NFP_GET_NEXT_TRANSMITTED_MESSAGE: + case IOCTL_NFCSIM_BEGIN_PROXIMITY: + { + const DISPATCH_ENTRY DispatchTable[] = + { + { IOCTL_NFP_ENABLE, 0, 0, &CQueue::OnNfpEnable }, + { IOCTL_NFP_DISABLE, 0, 0, &CQueue::OnNfpDisable }, + { IOCTL_NFP_GET_NEXT_SUBSCRIBED_MESSAGE, 0, sizeof(DWORD), &CQueue::OnNfpGetNextSubscribedMessage }, + { IOCTL_NFP_SET_PAYLOAD, MinCbPayload, 0, &CQueue::OnNfpSetPayload }, + { IOCTL_NFP_GET_NEXT_TRANSMITTED_MESSAGE, 0, 0, &CQueue::OnNfpGetNextTransmittedMessage }, + { IOCTL_NFP_GET_MAX_MESSAGE_BYTES, 0, sizeof(DWORD), &CQueue::OnNfpGetMaxMessageBytes }, + { IOCTL_NFP_GET_KILO_BYTES_PER_SECOND, 0, sizeof(DWORD), &CQueue::OnNfpGetTransmissionRateKbps }, + { IOCTL_NFCSIM_BEGIN_PROXIMITY, sizeof(BEGIN_PROXIMITY_ARGS), 0, &CQueue::OnNfpBeginProximity }, + }; + + if (IsNfpRadioEnabled()) { + Status = DispatchMessage(DispatchTable, _countof(DispatchTable), Request, IoControlCode, InputBufferLength, OutputBufferLength); + } + } + break; + + case IOCTL_NFCSE_ENUM_ENDPOINTS: + case IOCTL_NFCSE_SUBSCRIBE_FOR_EVENT: + case IOCTL_NFCSE_GET_NEXT_EVENT: + case IOCTL_NFCSE_SET_CARD_EMULATION_MODE: + case IOCTL_NFCSIM_TRIGGER_SEEVENT: + case IOCTL_NFCSE_GET_NFCC_CAPABILITIES: + case IOCTL_NFCSE_GET_ROUTING_TABLE: + case IOCTL_NFCSE_SET_ROUTING_TABLE: + case IOCTL_NFCSE_HCE_REMOTE_RECV: + case IOCTL_NFCSE_HCE_REMOTE_SEND: + { + const DISPATCH_ENTRY DispatchTable[] = + { + { IOCTL_NFCSE_SET_CARD_EMULATION_MODE, sizeof(SECURE_ELEMENT_SET_CARD_EMULATION_MODE_INFO), 0, &CQueue::OnSESetCardEmulationMode }, + { IOCTL_NFCSE_GET_NEXT_EVENT, 0, sizeof(DWORD), &CQueue::OnSEGetNextEvent }, + { IOCTL_NFCSE_SUBSCRIBE_FOR_EVENT, sizeof(SECURE_ELEMENT_EVENT_SUBSCRIPTION_INFO), 0, &CQueue::OnSESubscribeForEvent }, + { IOCTL_NFCSE_ENUM_ENDPOINTS, 0, sizeof(DWORD), &CQueue::OnSEEnumEndpoints }, + { IOCTL_NFCSE_GET_NFCC_CAPABILITIES, 0, sizeof(SECURE_ELEMENT_NFCC_CAPABILITIES), &CQueue::OnSEGetNfccCapabilities }, + { IOCTL_NFCSE_GET_ROUTING_TABLE, 0, sizeof(DWORD), &CQueue::OnSEGetRoutingTable }, + { IOCTL_NFCSE_SET_ROUTING_TABLE, sizeof(SECURE_ELEMENT_ROUTING_TABLE), 0, &CQueue::OnSESetRoutingTable }, + { IOCTL_NFCSE_HCE_REMOTE_RECV, 0, sizeof(DWORD), &CQueue::OnSEHCERemoteRecv }, + { IOCTL_NFCSE_HCE_REMOTE_SEND, 2 * sizeof(USHORT) + 2, 0, &CQueue::OnSEHCERemoteSend }, + { IOCTL_NFCSIM_TRIGGER_SEEVENT, SECURE_ELEMENT_EVENT_INFO_HEADER, 0, &CQueue::OnSETriggerEvent }, + }; + + if (IsSERadioEnabled()) { + Status = DispatchMessage(DispatchTable, _countof(DispatchTable), Request, IoControlCode, InputBufferLength, OutputBufferLength); + } + } + break; + + case IOCTL_SMARTCARD_EJECT: + case IOCTL_SMARTCARD_GET_LAST_ERROR: + case IOCTL_SMARTCARD_SET_ATTRIBUTE: + case IOCTL_SMARTCARD_SWALLOW: + case IOCTL_SMARTCARD_GET_ATTRIBUTE: + case IOCTL_SMARTCARD_GET_STATE: + case IOCTL_SMARTCARD_IS_ABSENT: + case IOCTL_SMARTCARD_IS_PRESENT: + case IOCTL_SMARTCARD_POWER: + case IOCTL_SMARTCARD_SET_PROTOCOL: + case IOCTL_SMARTCARD_TRANSMIT: + { + const DISPATCH_ENTRY DispatchTable[] = + { + { IOCTL_SMARTCARD_GET_ATTRIBUTE, sizeof(DWORD), 0, &CQueue::OnSCGetAttribute }, + { IOCTL_SMARTCARD_SET_ATTRIBUTE, sizeof(DWORD), 0, &CQueue::OnSCSetAttribute }, + { IOCTL_SMARTCARD_GET_STATE, 0, sizeof(DWORD), &CQueue::OnSCGetState }, + { IOCTL_SMARTCARD_POWER, sizeof(DWORD), 0, &CQueue::OnSCPower }, + { IOCTL_SMARTCARD_SET_PROTOCOL, sizeof(DWORD), sizeof(DWORD), &CQueue::OnSCSetProtocol }, + { IOCTL_SMARTCARD_IS_ABSENT, 0, 0, &CQueue::OnSCIsAbsent }, + { IOCTL_SMARTCARD_IS_PRESENT, 0, 0, &CQueue::OnSCIsPresent }, + { IOCTL_SMARTCARD_TRANSMIT, sizeof(SCARD_IO_REQUEST)+1, sizeof(SCARD_IO_REQUEST)+2, &CQueue::OnSCTransmit }, + { IOCTL_SMARTCARD_GET_LAST_ERROR, 0, 0, &CQueue::OnSCGetLastError }, + }; + + if (IsNfpRadioEnabled()) { + Status = DispatchMessage(DispatchTable, _countof(DispatchTable), Request, IoControlCode, InputBufferLength, OutputBufferLength); + } + } + break; + + case IOCTL_NFCRM_SET_RADIO_STATE: + case IOCTL_NFCRM_QUERY_RADIO_STATE: + case IOCTL_NFCSERM_SET_RADIO_STATE: + case IOCTL_NFCSERM_QUERY_RADIO_STATE: + { + const DISPATCH_ENTRY DispatchTable[] = + { + { IOCTL_NFCRM_SET_RADIO_STATE, sizeof(NFCRM_SET_RADIO_STATE), 0, &CQueue::OnNfpSetRadioState }, + { IOCTL_NFCRM_QUERY_RADIO_STATE, 0, sizeof(NFCRM_RADIO_STATE), &CQueue::OnNfpQueryRadioState }, + }; + + Status = DispatchMessage(DispatchTable, _countof(DispatchTable), Request, IoControlCode, InputBufferLength, OutputBufferLength); + } + break; + + default: + { + Status = STATUS_INVALID_DEVICE_STATE; + break; + } + } + + if (Status != STATUS_PENDING) { + TraceInfo("%!FUNC! Completing Request with Status %!STATUS!", Status); + WdfRequestComplete(Request, Status); + } + + MethodReturnVoid(); +} + +BOOL CQueue::ValidateMessage(_In_ CFileObject *pFileObject, _In_ ULONG IoControlCode) +{ + switch (IoControlCode) + { + case IOCTL_NFP_GET_MAX_MESSAGE_BYTES: + case IOCTL_NFP_GET_KILO_BYTES_PER_SECOND: + { + return TRUE; + } + + case IOCTL_NFP_DISABLE: + case IOCTL_NFP_ENABLE: + { + return (pFileObject->IsPublication() || pFileObject->IsSubscription()); + } + + case IOCTL_NFP_SET_PAYLOAD: + case IOCTL_NFP_GET_NEXT_TRANSMITTED_MESSAGE: + { + return pFileObject->IsPublication(); + } + + case IOCTL_NFP_GET_NEXT_SUBSCRIBED_MESSAGE: + { + return pFileObject->IsSubscription(); + } + + case IOCTL_NFCSE_GET_NFCC_CAPABILITIES: + case IOCTL_NFCSE_ENUM_ENDPOINTS: + { + return TRUE; + } + + case IOCTL_NFCSE_GET_NEXT_EVENT: + case IOCTL_NFCSE_SUBSCRIBE_FOR_EVENT: + { + return pFileObject->IsSecureElementEvent(); + } + + case IOCTL_NFCSE_GET_ROUTING_TABLE: + case IOCTL_NFCSE_SET_ROUTING_TABLE: + case IOCTL_NFCSE_HCE_REMOTE_SEND: + case IOCTL_NFCSE_HCE_REMOTE_RECV: + case IOCTL_NFCSE_SET_CARD_EMULATION_MODE: + { + return pFileObject->IsSecureElementManager(); + } + + case IOCTL_SMARTCARD_EJECT: + case IOCTL_SMARTCARD_GET_LAST_ERROR: + case IOCTL_SMARTCARD_SET_ATTRIBUTE: + case IOCTL_SMARTCARD_SWALLOW: + case IOCTL_SMARTCARD_GET_ATTRIBUTE: + case IOCTL_SMARTCARD_GET_STATE: + case IOCTL_SMARTCARD_IS_ABSENT: + case IOCTL_SMARTCARD_IS_PRESENT: + case IOCTL_SMARTCARD_POWER: + case IOCTL_SMARTCARD_SET_PROTOCOL: + case IOCTL_SMARTCARD_TRANSMIT: + { + return pFileObject->IsSmartCardReader(); + } + + case IOCTL_NFCRM_SET_RADIO_STATE: + case IOCTL_NFCRM_QUERY_RADIO_STATE: + { + return pFileObject->IsRoleRadioManager(); + } + + case IOCTL_NFCSIM_BEGIN_PROXIMITY: + case IOCTL_NFCSIM_TRIGGER_SEEVENT: + { + return pFileObject->IsRoleSimulation(); + } + } + + return FALSE; +} + +NTSTATUS +CQueue::DispatchMessage( + _In_reads_(TableEntries) const DISPATCH_ENTRY rgDispatchTable[], + _In_ DWORD TableEntries, + _In_ WDFREQUEST Request, + _In_ ULONG IoControlCode, + _In_ size_t InputBufferLength, + _In_ size_t OutputBufferLength + ) +{ + NTSTATUS Status = STATUS_NOT_SUPPORTED; + PVOID InputBuffer = nullptr; + PVOID OutputBuffer = nullptr; + CFileObject *pFileObject = GetFileObject(WdfRequestGetFileObject(Request)); + + for (DWORD TableEntry = 0; TableEntry < TableEntries; TableEntry++) { + if (rgDispatchTable[TableEntry].IoControlCode != IoControlCode) + continue; + + if (InputBufferLength >= rgDispatchTable[TableEntry].MinInputBufferLength && + OutputBufferLength >= rgDispatchTable[TableEntry].MinOutputBufferLength) { + if (InputBufferLength != 0) { + Status = WdfRequestRetrieveInputBuffer(Request, 0, &InputBuffer, nullptr); + NT_ASSERT(NT_SUCCESS(Status)); + } + + if (OutputBufferLength != 0) { + Status = WdfRequestRetrieveOutputBuffer(Request, 0, &OutputBuffer, nullptr); + NT_ASSERT(NT_SUCCESS(Status)); + } + + if (ValidateMessage(pFileObject, IoControlCode)) { + Status = (this->*rgDispatchTable[TableEntry].DispatchHandler)( + pFileObject, + Request, + InputBuffer, + InputBufferLength, + OutputBuffer, + OutputBufferLength); + } + else { + Status = STATUS_INVALID_DEVICE_STATE; + } + } + else { + Status = STATUS_INVALID_PARAMETER; + } + break; + } + + return Status; +} + +NTSTATUS +CQueue::OnNfpEnable( + _In_ CFileObject *pFileObject, + _In_ WDFREQUEST Request, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ size_t InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ size_t OutputBufferLength + ) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + + UNREFERENCED_PARAMETER(Request); + UNREFERENCED_PARAMETER(InputBuffer); + UNREFERENCED_PARAMETER(OutputBuffer); + UNREFERENCED_PARAMETER(OutputBufferLength); + + if (InputBufferLength != 0) { + Status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + Status = pFileObject->Enable(); + +Exit: + MethodReturn(Status, "Status = %!STATUS!", Status); +} + +NTSTATUS +CQueue::OnNfpDisable( + _In_ CFileObject *pFileObject, + _In_ WDFREQUEST Request, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ size_t InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ size_t OutputBufferLength + ) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + + UNREFERENCED_PARAMETER(Request); + UNREFERENCED_PARAMETER(InputBuffer); + UNREFERENCED_PARAMETER(OutputBuffer); + UNREFERENCED_PARAMETER(OutputBufferLength); + + if (InputBufferLength != 0) { + Status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + Status = pFileObject->Disable(); + +Exit: + MethodReturn(Status, "Status = %!STATUS!", Status); +} + +NTSTATUS +CQueue::OnNfpSetPayload( + _In_ CFileObject *pFileObject, + _In_ WDFREQUEST Request, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ size_t InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ size_t OutputBufferLength + ) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + + UNREFERENCED_PARAMETER(Request); + UNREFERENCED_PARAMETER(OutputBuffer); + + if (OutputBufferLength != 0) { + Status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + if (InputBufferLength > MaxCbPayload) { + Status = STATUS_INVALID_BUFFER_SIZE; + goto Exit; + } + + Status = pFileObject->SetPayload((DWORD)InputBufferLength, (PBYTE)InputBuffer); + + if (NT_SUCCESS(Status)) { + MESSAGE* pMessage = new MESSAGE(); + + if (pMessage != nullptr) { + pMessage->Initialize(pFileObject->GetType(), pFileObject->GetSize(), pFileObject->GetPayload()); + + EnterCriticalSection(&m_ConnectionLock); + + for (LIST_ENTRY* pEntry = m_ConnectionList.Flink; + pEntry != &m_ConnectionList; + pEntry = pEntry->Flink) { + CConnection* pConnection = CConnection::FromListEntry(pEntry); + + if (SUCCEEDED(pConnection->TransmitMessage(pMessage))) { + pFileObject->HandleMessageTransmitted(); + } + } + LeaveCriticalSection(&m_ConnectionLock); + + EnterCriticalSection(&m_PubsLock); + InsertHeadList(&m_PubsList, pFileObject->GetListEntry()); + LeaveCriticalSection(&m_PubsLock); + + delete pMessage; + } + else { + Status = STATUS_INSUFFICIENT_RESOURCES; + } + } + +Exit: + MethodReturn(Status, "Status = %!STATUS!", Status); +} + +NTSTATUS +CQueue::OnNfpGetNextSubscribedMessage( + _In_ CFileObject *pFileObject, + _In_ WDFREQUEST Request, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ size_t InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ size_t OutputBufferLength + ) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + BOOL fCompleteRequest = TRUE; + + UNREFERENCED_PARAMETER(InputBuffer); + UNREFERENCED_PARAMETER(OutputBuffer); + UNREFERENCED_PARAMETER(OutputBufferLength); + + if (InputBufferLength != 0) { + Status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + Status = pFileObject->GetNextSubscribedMessage(Request); + fCompleteRequest = !NT_SUCCESS(Status); + +Exit: + MethodReturn(fCompleteRequest ? Status : STATUS_PENDING, "Status = %!STATUS!", Status); +} + +NTSTATUS +CQueue::OnNfpGetNextTransmittedMessage( + _In_ CFileObject *pFileObject, + _In_ WDFREQUEST Request, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ size_t InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ size_t OutputBufferLength + ) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + BOOL fCompleteRequest = TRUE; + + UNREFERENCED_PARAMETER(InputBuffer); + UNREFERENCED_PARAMETER(OutputBuffer); + + if (InputBufferLength != 0) { + Status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + if (OutputBufferLength != 0) { + Status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + Status = pFileObject->GetNextTransmittedMessage(Request); + fCompleteRequest = !NT_SUCCESS(Status); + +Exit: + MethodReturn(fCompleteRequest ? Status : STATUS_PENDING, "Status = %!STATUS!", Status); +} + +NTSTATUS +CQueue::OnNfpGetMaxMessageBytes( + _In_ CFileObject* pFileObject, + _In_ WDFREQUEST Request, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ size_t InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ size_t OutputBufferLength + ) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + BOOL fCompleteRequest = TRUE; + DWORD *pcbMaxPayload = (DWORD*)OutputBuffer; + + UNREFERENCED_PARAMETER(pFileObject); + UNREFERENCED_PARAMETER(InputBuffer); + UNREFERENCED_PARAMETER(OutputBufferLength); + + // + // Since the output buffer has already been validated + // to contain 4 bytes, we know we can safely make this assumption + // + _Analysis_assume_(sizeof(DWORD) <= OutputBufferLength); + + if (InputBufferLength != 0) { + Status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + *pcbMaxPayload = MaxCbPayload; + WdfRequestCompleteWithInformation(Request, Status, sizeof(DWORD)); + fCompleteRequest = FALSE; + +Exit: + MethodReturn(fCompleteRequest ? Status : STATUS_PENDING, "Status = %!STATUS!", Status); +} + +NTSTATUS +CQueue::OnNfpGetTransmissionRateKbps( + _In_ CFileObject* pFileObject, + _In_ WDFREQUEST Request, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ size_t InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ size_t OutputBufferLength + ) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + BOOL fCompleteRequest = TRUE; + DWORD *pdwKilobytesPerSecond = (DWORD*)OutputBuffer; + + UNREFERENCED_PARAMETER(pFileObject); + UNREFERENCED_PARAMETER(InputBuffer); + UNREFERENCED_PARAMETER(OutputBufferLength); + + // + // Since the output buffer has already been validated + // to contain 4 bytes, we know we can safely make this assumption + // + _Analysis_assume_(sizeof(DWORD) <= OutputBufferLength); + + if (InputBufferLength != 0) { + Status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + *pdwKilobytesPerSecond = KilobytesPerSecond; + WdfRequestCompleteWithInformation(Request, Status, sizeof(DWORD)); + fCompleteRequest = FALSE; + +Exit: + MethodReturn(fCompleteRequest ? Status : STATUS_PENDING, "Status = %!STATUS!", Status); +} + +NTSTATUS +CQueue::OnNfpBeginProximity( + _In_ CFileObject *pFileObject, + _In_ WDFREQUEST Request, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ size_t InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ size_t OutputBufferLength + ) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + BEGIN_PROXIMITY_ARGS *pArgs = (BEGIN_PROXIMITY_ARGS*)InputBuffer; + + UNREFERENCED_PARAMETER(Request); + UNREFERENCED_PARAMETER(InputBufferLength); + UNREFERENCED_PARAMETER(OutputBuffer); + + // + // Since the input buffer has already been validated + // for input buffer length so we know we can safely make this assumption + // + _Analysis_assume_(sizeof(BEGIN_PROXIMITY_ARGS) <= InputBufferLength); + + if (OutputBufferLength != 0) { + Status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + Status = pFileObject->BeginProximity(pArgs, this); + +Exit: + MethodReturn(Status, "Status = %!STATUS!", Status); +} + +NTSTATUS +CQueue::OnNfpSetRadioState( + _In_ CFileObject *pFileObject, + _In_ WDFREQUEST Request, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ size_t InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ size_t OutputBufferLength + ) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + PNFCRM_SET_RADIO_STATE pRadioState = (PNFCRM_SET_RADIO_STATE)InputBuffer; + BOOLEAN NfpRadioState; + DECLARE_CONST_UNICODE_STRING(SCNamespace, SMARTCARD_READER_NAMESPACE); + + UNREFERENCED_PARAMETER(pFileObject); + UNREFERENCED_PARAMETER(Request); + UNREFERENCED_PARAMETER(InputBufferLength); + UNREFERENCED_PARAMETER(OutputBuffer); + + _Analysis_assume_(sizeof(NFCRM_SET_RADIO_STATE) <= InputBufferLength); + + EnterCriticalSection(&m_RadioLock); + + if (OutputBufferLength != 0) { + Status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + if (pRadioState->SystemStateUpdate) { + m_NfpRadioOffSystemOverride = !pRadioState->MediaRadioOn; + } + else { + // + // Since the request is modifying the radio state independent of the + // system state, set the system state override to FALSE + // + m_NfpRadioOffSystemOverride = FALSE; + m_NfpRadioOffPolicyOverride = !pRadioState->MediaRadioOn; + } + + NfpRadioState = !m_NfpRadioOffSystemOverride && !m_NfpRadioOffPolicyOverride; + + if (NfpRadioState == m_NfpRadioState) { + Status = STATUS_INVALID_DEVICE_STATE; + goto Exit; + } + + if (NfpRadioState) { + if (!m_NfpInterfaceCreated) { + // Register for Proximity interface + Status = WdfDeviceCreateDeviceInterface( + WdfIoQueueGetDevice(m_Queue), + (LPGUID) &GUID_DEVINTERFACE_NFP, + nullptr); + + if (!NT_SUCCESS(Status)) { + TraceInfo("WdfDeviceCreateDeviceInterface failed with Status %!STATUS!", Status); + goto Exit; + } + + m_NfpInterfaceCreated = TRUE; + } + + WdfDeviceSetDeviceInterfaceState( + WdfIoQueueGetDevice(m_Queue), + (LPGUID) &GUID_DEVINTERFACE_NFP, + nullptr, + TRUE); + + if (!m_ScInterfaceCreated) { + // Register for Smart Card interface + Status = WdfDeviceCreateDeviceInterface( + WdfIoQueueGetDevice(m_Queue), + (LPGUID) &GUID_DEVINTERFACE_SMARTCARD_READER, + &SCNamespace); + + if (!NT_SUCCESS(Status)) { + TraceInfo("WdfDeviceCreateDeviceInterface failed with Status %!STATUS!", Status); + goto Exit; + } + + m_ScInterfaceCreated = TRUE; + } + + WdfDeviceSetDeviceInterfaceState( + WdfIoQueueGetDevice(m_Queue), + (LPGUID) &GUID_DEVINTERFACE_SMARTCARD_READER, + &SCNamespace, + TRUE); + } + else { + if (m_NfpInterfaceCreated) { + WdfDeviceSetDeviceInterfaceState( + WdfIoQueueGetDevice(m_Queue), + (LPGUID) &GUID_DEVINTERFACE_NFP, + nullptr, + FALSE); + } + + if (m_ScInterfaceCreated) { + WdfDeviceSetDeviceInterfaceState( + WdfIoQueueGetDevice(m_Queue), + (LPGUID) &GUID_DEVINTERFACE_SMARTCARD_READER, + &SCNamespace, + FALSE); + } + } + + m_NfpRadioState = NfpRadioState; + +Exit: + WriteSettingsToRegistry(); + LeaveCriticalSection(&m_RadioLock); + MethodReturn(Status, "Status = %!STATUS!", Status); +} + +NTSTATUS +CQueue::OnNfpQueryRadioState( + _In_ CFileObject *pFileObject, + _In_ WDFREQUEST Request, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ size_t InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ size_t OutputBufferLength + ) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + BOOL fCompleteRequest = TRUE; + PNFCRM_RADIO_STATE pRadioState = (PNFCRM_RADIO_STATE)OutputBuffer; + + UNREFERENCED_PARAMETER(pFileObject); + UNREFERENCED_PARAMETER(InputBuffer); + UNREFERENCED_PARAMETER(OutputBufferLength); + + _Analysis_assume_(sizeof(NFCRM_RADIO_STATE) <= OutputBufferLength); + + if (InputBufferLength != 0) { + Status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + pRadioState->MediaRadioOn = IsNfpRadioEnabled(); + WdfRequestCompleteWithInformation(Request, Status, sizeof(NFCRM_RADIO_STATE)); + fCompleteRequest = FALSE; + +Exit: + MethodReturn(fCompleteRequest ? Status : STATUS_PENDING, "Status = %!STATUS!", Status); +} + +NTSTATUS +CQueue::OnSEEnumEndpoints( + _In_ CFileObject* pFileObject, + _In_ WDFREQUEST Request, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ size_t InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ size_t OutputBufferLength + ) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + BOOL fCompleteRequest = TRUE; + SECURE_ELEMENT_ENDPOINT_INFO rgEndpointInfo[MaxSecureElements] = {}; + SECURE_ELEMENT_ENDPOINT_LIST *pEndpointList = (SECURE_ELEMENT_ENDPOINT_LIST*)OutputBuffer; + DWORD cbOutputBuffer = 0; + + UNREFERENCED_PARAMETER(pFileObject); + UNREFERENCED_PARAMETER(InputBuffer); + + // + // Since the output buffer has already been validated + // for output buffer length so we know we can safely make this assumption + // + _Analysis_assume_(sizeof(DWORD) <= OutputBufferLength); + + if (InputBufferLength != 0) { + Status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + pEndpointList->NumberOfEndpoints = 0; + + for (LIST_ENTRY* pEntry = m_SecureElementList.Flink; + pEntry != &m_SecureElementList && pEndpointList->NumberOfEndpoints < _countof(rgEndpointInfo); + pEntry = pEntry->Flink) { + SECURE_ELEMENT_ENDPOINT_INFO Info; + + Info.guidSecureElementId = CSecureElement::FromListEntry(pEntry)->GetIdentifier(); + Info.eSecureElementType = CSecureElement::FromListEntry(pEntry)->GetType(); + + rgEndpointInfo[pEndpointList->NumberOfEndpoints++] = Info; + } + + cbOutputBuffer = sizeof(DWORD); + + if ((cbOutputBuffer + (pEndpointList->NumberOfEndpoints * sizeof(SECURE_ELEMENT_ENDPOINT_INFO))) <= OutputBufferLength) { + RtlCopyMemory(pEndpointList->EndpointList, rgEndpointInfo, pEndpointList->NumberOfEndpoints * sizeof(SECURE_ELEMENT_ENDPOINT_INFO)); + cbOutputBuffer += (pEndpointList->NumberOfEndpoints * sizeof(SECURE_ELEMENT_ENDPOINT_INFO)); + } + else { + // Returning this signals to the client to send a bigger buffer + Status = STATUS_BUFFER_OVERFLOW; + } + + WdfRequestCompleteWithInformation(Request, Status, cbOutputBuffer); + fCompleteRequest = FALSE; + +Exit: + MethodReturn(fCompleteRequest ? Status : STATUS_PENDING, "Status = %!STATUS!", Status); +} + +NTSTATUS +CQueue::OnSESubscribeForEvent( + _In_ CFileObject *pFileObject, + _In_ WDFREQUEST Request, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ size_t InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ size_t OutputBufferLength + ) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + SECURE_ELEMENT_EVENT_SUBSCRIPTION_INFO *pInfo = (SECURE_ELEMENT_EVENT_SUBSCRIPTION_INFO*)InputBuffer; + CSecureElement *pSecureElement = nullptr; + + UNREFERENCED_PARAMETER(Request); + UNREFERENCED_PARAMETER(InputBufferLength); + UNREFERENCED_PARAMETER(OutputBuffer); + + // + // Since the input buffer has already been validated + // for input buffer length so we know we can safely make this assumption + // + _Analysis_assume_(sizeof(SECURE_ELEMENT_EVENT_SUBSCRIPTION_INFO) <= InputBufferLength); + + if (OutputBufferLength != 0) { + Status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + if (IsEqualGUID(pInfo->guidSecureElementId, GUID_NULL) || + NT_SUCCESS(Status = GetSecureElementObject(pInfo->guidSecureElementId, &pSecureElement))) { + Status = pFileObject->SubscribeForEvent(pInfo->guidSecureElementId, pInfo->eEventType); + + if (NT_SUCCESS(Status)) { + EnterCriticalSection(&m_SEEventsLock); + InsertHeadList(&m_SEEventsList, pFileObject->GetListEntry()); + LeaveCriticalSection(&m_SEEventsLock); + } + } + +Exit: + MethodReturn(Status, "Status = %!STATUS!", Status); +} + +NTSTATUS +CQueue::OnSEGetNextEvent( + _In_ CFileObject *pFileObject, + _In_ WDFREQUEST Request, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ size_t InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ size_t OutputBufferLength + ) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + BOOL fCompleteRequest = TRUE; + + UNREFERENCED_PARAMETER(InputBuffer); + UNREFERENCED_PARAMETER(OutputBuffer); + UNREFERENCED_PARAMETER(OutputBufferLength); + + if (InputBufferLength != 0) { + Status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + Status = pFileObject->GetNextSecureElementPayload(Request); + fCompleteRequest = !NT_SUCCESS(Status); + +Exit: + MethodReturn(fCompleteRequest ? Status : STATUS_PENDING, "Status = %!STATUS!", Status); +} + +NTSTATUS +CQueue::OnSESetCardEmulationMode( + _In_ CFileObject* pFileObject, + _In_ WDFREQUEST Request, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ size_t InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ size_t OutputBufferLength + ) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + SECURE_ELEMENT_SET_CARD_EMULATION_MODE_INFO *pMode = (SECURE_ELEMENT_SET_CARD_EMULATION_MODE_INFO*)InputBuffer; + CSecureElement *pSecureElement = nullptr; + + UNREFERENCED_PARAMETER(pFileObject); + UNREFERENCED_PARAMETER(Request); + UNREFERENCED_PARAMETER(InputBufferLength); + UNREFERENCED_PARAMETER(OutputBuffer); + + // + // Since the input buffer has already been validated + // for input buffer length so we know we can safely make this assumption + // + _Analysis_assume_(sizeof(SECURE_ELEMENT_SET_CARD_EMULATION_MODE_INFO) <= InputBufferLength); + + if (OutputBufferLength != 0) { + Status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + Status = GetSecureElementObject(pMode->guidSecureElementId, &pSecureElement); + + if (NT_SUCCESS(Status)) { + Status = pSecureElement->SetEmulationMode(pMode->eMode); + } + +Exit: + MethodReturn(Status, "Status = %!STATUS!", Status); +} + +NTSTATUS +CQueue::OnSETriggerEvent( + _In_ CFileObject* pFileObject, + _In_ WDFREQUEST Request, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ size_t InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ size_t OutputBufferLength + ) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + SECURE_ELEMENT_EVENT_INFO *pInfo = (SECURE_ELEMENT_EVENT_INFO*)InputBuffer; + CSecureElement *pSecureElement = nullptr; + + UNREFERENCED_PARAMETER(pFileObject); + UNREFERENCED_PARAMETER(Request); + UNREFERENCED_PARAMETER(InputBufferLength); + UNREFERENCED_PARAMETER(OutputBuffer); + + // + // Since the input buffer has already been validated + // for input buffer length so we know we can safely make this assumption + // + _Analysis_assume_(SECURE_ELEMENT_EVENT_INFO_HEADER <= InputBufferLength); + + if (OutputBufferLength != 0) { + Status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + if (pInfo->guidSecureElementId == GUID_NULL) { + for (LIST_ENTRY* pEntry = m_SecureElementList.Flink; + pEntry != &m_SecureElementList; + pEntry = pEntry->Flink) { + pInfo->guidSecureElementId = CSecureElement::FromListEntry(pEntry)->GetIdentifier(); + HandleSecureElementEvent(pInfo); + } + } + else { + Status = GetSecureElementObject(pInfo->guidSecureElementId, &pSecureElement); + + if (NT_SUCCESS(Status)) { + HandleSecureElementEvent(pInfo); + } + } + +Exit: + MethodReturn(Status, "Status = %!STATUS!", Status); +} + +NTSTATUS +CQueue::OnSEGetNfccCapabilities( + _In_ CFileObject *pFileObject, + _In_ WDFREQUEST Request, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ size_t InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ size_t OutputBufferLength + ) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + PSECURE_ELEMENT_NFCC_CAPABILITIES pCapabilities = (PSECURE_ELEMENT_NFCC_CAPABILITIES)OutputBuffer; + BOOL fCompleteRequest = TRUE; + + UNREFERENCED_PARAMETER(pFileObject); + UNREFERENCED_PARAMETER(InputBuffer); + UNREFERENCED_PARAMETER(OutputBufferLength); + + // + // Since the input buffer has already been validated + // for input buffer length so we know we can safely make this assumption + // + _Analysis_assume_(sizeof(SECURE_ELEMENT_NFCC_CAPABILITIES) <= OutputBufferLength); + + if (InputBufferLength != 0) { + Status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + pCapabilities->cbMaxRoutingTableSize = m_RoutingTable.MaxRoutingTableSize(); + pCapabilities->IsAidRoutingSupported = m_RoutingTable.IsAidRoutingSupported(); + pCapabilities->IsProtocolRoutingSupported = m_RoutingTable.IsProtocolRoutingSupported(); + pCapabilities->IsTechRoutingSupported = m_RoutingTable.IsTechRoutingSupported(); + + WdfRequestCompleteWithInformation(Request, Status, sizeof(SECURE_ELEMENT_NFCC_CAPABILITIES)); + fCompleteRequest = FALSE; + +Exit: + MethodReturn(fCompleteRequest ? Status : STATUS_PENDING, "Status = %!STATUS!", Status); +} + +NTSTATUS +CQueue::OnSESetRoutingTable( + _In_ CFileObject *pFileObject, + _In_ WDFREQUEST Request, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ size_t InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ size_t OutputBufferLength + ) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + PSECURE_ELEMENT_ROUTING_TABLE pRoutingTable = (PSECURE_ELEMENT_ROUTING_TABLE)InputBuffer; + + UNREFERENCED_PARAMETER(pFileObject); + UNREFERENCED_PARAMETER(Request); + UNREFERENCED_PARAMETER(OutputBuffer); + UNREFERENCED_PARAMETER(OutputBufferLength); + + // + // Since the input buffer has already been validated + // for input buffer length so we know we can safely make this assumption + // + _Analysis_assume_(sizeof(SECURE_ELEMENT_ROUTING_TABLE) <= InputBufferLength); + + if (pRoutingTable->NumberOfEntries == 0) { + Status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + if ((sizeof(SECURE_ELEMENT_ROUTING_TABLE) + (pRoutingTable->NumberOfEntries - 1) * sizeof(SECURE_ELEMENT_ROUTING_TABLE_ENTRY)) < InputBufferLength) { + Status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + Status = m_RoutingTable.SetRoutingTable(pRoutingTable); + +Exit: + MethodReturn(Status, "Status = %!STATUS!", Status); +} + +NTSTATUS +CQueue::OnSEGetRoutingTable( + _In_ CFileObject *pFileObject, + _In_ WDFREQUEST Request, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ size_t InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ size_t OutputBufferLength + ) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + BOOL fCompleteRequest = TRUE; + DWORD cbOutputBuffer = 0; + + UNREFERENCED_PARAMETER(pFileObject); + UNREFERENCED_PARAMETER(Request); + UNREFERENCED_PARAMETER(InputBuffer); + + // + // Since the output buffer has already been validated + // for output buffer length so we know we can safely make this assumption + // + _Analysis_assume_(sizeof(DWORD) <= OutputBufferLength); + + if (InputBufferLength != 0) { + Status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + Status = m_RoutingTable.GetRoutingTable(OutputBuffer, OutputBufferLength, &cbOutputBuffer); + WdfRequestCompleteWithInformation(Request, Status, cbOutputBuffer); + fCompleteRequest = FALSE; + +Exit: + MethodReturn(fCompleteRequest ? Status : STATUS_PENDING, "Status = %!STATUS!", Status); +} + +NTSTATUS +CQueue::OnSEHCERemoteRecv( + _In_ CFileObject *pFileObject, + _In_ WDFREQUEST Request, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ size_t InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ size_t OutputBufferLength + ) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + GUID guidSecureElementId; + BOOL fCompleteRequest = TRUE; + + UNREFERENCED_PARAMETER(InputBuffer); + UNREFERENCED_PARAMETER(OutputBuffer); + UNREFERENCED_PARAMETER(OutputBufferLength); + + if (InputBufferLength != 0) { + Status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + if (!NT_SUCCESS(GetSecureElementId(DeviceHost, &guidSecureElementId))) { + Status = STATUS_NOT_SUPPORTED; + goto Exit; + } + + Status = pFileObject->GetNextSecureElementPayload(Request); + fCompleteRequest = !NT_SUCCESS(Status); + +Exit: + MethodReturn(fCompleteRequest ? Status : STATUS_PENDING, "Status = %!STATUS!", Status); +} + +NTSTATUS +CQueue::OnSEHCERemoteSend( + _In_ CFileObject *pFileObject, + _In_ WDFREQUEST Request, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ size_t InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ size_t OutputBufferLength + ) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + GUID guidSecureElementId; + MESSAGE* pMessage = nullptr; + PSECURE_ELEMENT_HCE_DATA_PACKET pDataPacket = (PSECURE_ELEMENT_HCE_DATA_PACKET)InputBuffer; + + UNREFERENCED_PARAMETER(pFileObject); + UNREFERENCED_PARAMETER(Request); + UNREFERENCED_PARAMETER(InputBufferLength); + UNREFERENCED_PARAMETER(OutputBuffer); + + // + // Since the input buffer has already been validated + // for input buffer length so we know we can safely make this assumption + // + _Analysis_assume_(2 * sizeof(USHORT) + 2 <= InputBufferLength); + + EnterCriticalSection(&m_pHCEConnectionLock); + + if (OutputBufferLength != 0 || + pDataPacket->bConnectionId != m_HCEConnectionId) { + Status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + if (pDataPacket->cbPayload > MaxCbPayload) { + Status = STATUS_INVALID_BUFFER_SIZE; + goto Exit; + } + + if (!NT_SUCCESS(GetSecureElementId(DeviceHost, &guidSecureElementId))) { + Status = STATUS_NOT_SUPPORTED; + goto Exit; + } + + if (m_pHCEConnection == nullptr) { + Status = STATUS_INVALID_DEVICE_STATE; + goto Exit; + } + + pMessage = new MESSAGE(); + + if (pMessage == nullptr) { + Status = STATUS_INSUFFICIENT_RESOURCES; + goto Exit; + } + + pMessage->Initialize(HCE_MESSAGE_TYPE_TRANSMIT, pDataPacket->cbPayload, pDataPacket->pbPayload); + + if (FAILED(m_pHCEConnection->TransmitMessage(pMessage))) { + Status = STATUS_DATA_ERROR; + goto Exit; + } + +Exit: + SAFE_DELETE(pMessage); + LeaveCriticalSection(&m_pHCEConnectionLock); + MethodReturn(Status, "Status = %!STATUS!", Status); +} + +NTSTATUS +CQueue::OnSCGetLastError( + _In_ CFileObject* pFileObject, + _In_ WDFREQUEST Request, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ size_t InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ size_t OutputBufferLength + ) +{ + MethodEntry("..."); + + DWORD *pdwError = (DWORD*)OutputBuffer; + + UNREFERENCED_PARAMETER(pFileObject); + UNREFERENCED_PARAMETER(InputBuffer); + UNREFERENCED_PARAMETER(InputBufferLength); + UNREFERENCED_PARAMETER(OutputBufferLength); + + _Analysis_assume_(sizeof(DWORD) <= OutputBufferLength); + + *pdwError = STATUS_SUCCESS; + WdfRequestCompleteWithInformation(Request, STATUS_SUCCESS, sizeof(DWORD)); + + MethodReturn(STATUS_PENDING, "Status = %!STATUS!", STATUS_SUCCESS); +} + +NTSTATUS +CQueue::OnSCGetAttribute( + _In_ CFileObject* pFileObject, + _In_ WDFREQUEST Request, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ size_t InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ size_t OutputBufferLength + ) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + BOOL fCompleteRequest = TRUE; + DWORD *pdwAttributeId = (DWORD*)InputBuffer; + DWORD cbOutputBuffer = (DWORD)OutputBufferLength; + + UNREFERENCED_PARAMETER(pFileObject); + UNREFERENCED_PARAMETER(InputBufferLength); + + _Analysis_assume_(sizeof(DWORD) <= InputBufferLength); + _Analysis_assume_(sizeof(DWORD) <= OutputBufferLength); + + Status = m_SmartCardReader.GetAttribute(*pdwAttributeId, (PBYTE)OutputBuffer, &cbOutputBuffer); + + if (NT_SUCCESS(Status)) { + WdfRequestCompleteWithInformation(Request, Status, cbOutputBuffer); + fCompleteRequest = FALSE; + } + + MethodReturn(fCompleteRequest ? Status : STATUS_PENDING, "Status = %!STATUS!", Status); +} + +NTSTATUS +CQueue::OnSCSetAttribute( + _In_ CFileObject* pFileObject, + _In_ WDFREQUEST Request, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ size_t InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ size_t OutputBufferLength + ) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + DWORD *pdwAttributeId = (DWORD*)InputBuffer; + + UNREFERENCED_PARAMETER(pFileObject); + UNREFERENCED_PARAMETER(Request); + UNREFERENCED_PARAMETER(InputBufferLength); + UNREFERENCED_PARAMETER(OutputBuffer); + UNREFERENCED_PARAMETER(OutputBufferLength); + + _Analysis_assume_(sizeof(DWORD) <= InputBufferLength); + + Status = m_SmartCardReader.SetAttribute(*pdwAttributeId); + + MethodReturn(Status, "Status = %!STATUS!", Status); +} + +NTSTATUS +CQueue::OnSCGetState( + _In_ CFileObject* pFileObject, + _In_ WDFREQUEST Request, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ size_t InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ size_t OutputBufferLength + ) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + DWORD *pdwState = (DWORD*)OutputBuffer; + + UNREFERENCED_PARAMETER(pFileObject); + UNREFERENCED_PARAMETER(InputBuffer); + UNREFERENCED_PARAMETER(InputBufferLength); + UNREFERENCED_PARAMETER(OutputBufferLength); + + _Analysis_assume_(sizeof(DWORD) <= OutputBufferLength); + + *pdwState = m_SmartCardReader.GetState(); + WdfRequestCompleteWithInformation(Request, Status, sizeof(DWORD)); + + MethodReturn(STATUS_PENDING, "Status = %!STATUS!", Status); +} + +NTSTATUS +CQueue::OnSCIsAbsent( + _In_ CFileObject* pFileObject, + _In_ WDFREQUEST Request, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ size_t InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ size_t OutputBufferLength + ) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + + UNREFERENCED_PARAMETER(pFileObject); + UNREFERENCED_PARAMETER(InputBuffer); + UNREFERENCED_PARAMETER(OutputBuffer); + + if (InputBufferLength != 0) { + Status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + if (OutputBufferLength != 0) { + Status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + Status = m_SmartCardReader.IsAbsent(Request); + +Exit: + MethodReturn(Status, "Status = %!STATUS!", Status); +} + +NTSTATUS +CQueue::OnSCIsPresent( + _In_ CFileObject* pFileObject, + _In_ WDFREQUEST Request, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ size_t InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ size_t OutputBufferLength + ) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + + UNREFERENCED_PARAMETER(pFileObject); + UNREFERENCED_PARAMETER(InputBuffer); + UNREFERENCED_PARAMETER(OutputBuffer); + + if (InputBufferLength != 0) { + Status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + if (OutputBufferLength != 0) { + Status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + Status = m_SmartCardReader.IsPresent(Request); + +Exit: + MethodReturn(Status, "Status = %!STATUS!", Status); +} + +NTSTATUS +CQueue::OnSCPower( + _In_ CFileObject* pFileObject, + _In_ WDFREQUEST Request, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ size_t InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ size_t OutputBufferLength + ) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + DWORD *pdwPower = (DWORD*)InputBuffer; + + UNREFERENCED_PARAMETER(pFileObject); + UNREFERENCED_PARAMETER(Request); + UNREFERENCED_PARAMETER(InputBufferLength); + UNREFERENCED_PARAMETER(OutputBuffer); + UNREFERENCED_PARAMETER(OutputBufferLength); + + _Analysis_assume_(sizeof(DWORD) <= InputBufferLength); + + Status = m_SmartCardReader.SetPower(*pdwPower); + + MethodReturn(Status, "Status = %!STATUS!", Status); +} + +NTSTATUS +CQueue::OnSCSetProtocol( + _In_ CFileObject* pFileObject, + _In_ WDFREQUEST Request, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ size_t InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ size_t OutputBufferLength + ) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + BOOL fCompleteRequest = TRUE; + DWORD *pdwProtocol = (DWORD*)InputBuffer; + DWORD *pdwSelectedProtocol = (DWORD*)OutputBuffer; + + UNREFERENCED_PARAMETER(pFileObject); + UNREFERENCED_PARAMETER(InputBufferLength); + UNREFERENCED_PARAMETER(OutputBufferLength); + + _Analysis_assume_(sizeof(DWORD) <= InputBufferLength); + _Analysis_assume_(sizeof(DWORD) <= OutputBufferLength); + + Status = m_SmartCardReader.SetProtocol(*pdwProtocol, pdwSelectedProtocol); + + if (NT_SUCCESS(Status)) { + WdfRequestCompleteWithInformation(Request, Status, sizeof(DWORD)); + fCompleteRequest = FALSE; + } + + MethodReturn(fCompleteRequest ? Status : STATUS_PENDING, "Status = %!STATUS!", Status); +} + +NTSTATUS +CQueue::OnSCTransmit( + _In_ CFileObject* pFileObject, + _In_ WDFREQUEST Request, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ size_t InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ size_t OutputBufferLength + ) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + BOOL fCompleteRequest = TRUE; + size_t BytesTransferred = 0; + + UNREFERENCED_PARAMETER(pFileObject); + + // + // Since the input and buffer buffer length has already been validated + // for input buffer length we need to have atleast one byte to transfer (discarding the header) + // for output buffer length we should atleast be able to store status codes SW1+SW2 + // + _Analysis_assume_(sizeof(SCARD_IO_REQUEST)+1 <= InputBufferLength); + _Analysis_assume_(sizeof(SCARD_IO_REQUEST)+2 <= OutputBufferLength); + + Status = m_SmartCardReader.Transmit((PBYTE)InputBuffer, InputBufferLength, (PBYTE)OutputBuffer, OutputBufferLength, &BytesTransferred); + + if (NT_SUCCESS(Status)) { + WdfRequestCompleteWithInformation(Request, STATUS_SUCCESS, BytesTransferred); + fCompleteRequest = FALSE; + } + + MethodReturn(fCompleteRequest ? Status : STATUS_PENDING, "Status = %!STATUS!", Status); +} + +void CQueue::ValidateAccept(_In_ SOCKET Socket, _In_ GUID* pMagicPacket) +{ + MethodEntry("..."); + + CConnection* pConnection; + + if (CConnection::Create(this, &pConnection)) { + // 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 CQueue::ConnectionEstablished(_In_ CConnection* pConnection) +{ + MethodEntry("..."); + + if (pConnection->GetConnectionType() == CONNECTION_TYPE_P2P) { + EnterCriticalSection(&m_ConnectionLock); + + if (IsListEmpty(&m_ConnectionList)) { + HandleArrivalEvent(); + } + + InsertHeadList(&m_ConnectionList, pConnection->GetListEntry()); + LeaveCriticalSection(&m_ConnectionLock); + + MESSAGE* pMessage = new MESSAGE(); + + if (pMessage != nullptr) { + EnterCriticalSection(&m_PubsLock); + + for (LIST_ENTRY* pEntry = m_PubsList.Flink; + pEntry != &m_PubsList; + pEntry = pEntry->Flink) { + CFileObject* pPub = CFileObject::FromListEntry(pEntry); + + if (pPub->IsEnabled()) { + pMessage->Initialize(pPub->GetType(), pPub->GetSize(), pPub->GetPayload()); + + if (SUCCEEDED(pConnection->TransmitMessage(pMessage))) { + pPub->HandleMessageTransmitted(); + } + } + } + LeaveCriticalSection(&m_PubsLock); + + delete pMessage; + } + } + else if (pConnection->GetConnectionType() == CONNECTION_TYPE_TAG) { + m_SmartCardReader.CardArrived(pConnection); + } + else if (pConnection->GetConnectionType() == CONNECTION_TYPE_HCE) { + SECURE_ELEMENT_EVENT_INFO_AND_PAYLOAD(sizeof(SECURE_ELEMENT_HCE_ACTIVATION_PAYLOAD)) EventInfo; + + if (NT_SUCCESS(GetSecureElementId(DeviceHost, &EventInfo.Info.guidSecureElementId))) { + EnterCriticalSection(&m_pHCEConnectionLock); + + if (m_pHCEConnection == nullptr) { + PSECURE_ELEMENT_HCE_ACTIVATION_PAYLOAD pbEventPayload = NULL; + + m_pHCEConnection = pConnection; + m_HCEConnectionId++; + + EventInfo.Info.eEventType = HceActivated; + EventInfo.Info.cbEventData = sizeof(SECURE_ELEMENT_HCE_ACTIVATION_PAYLOAD); + + pbEventPayload = (PSECURE_ELEMENT_HCE_ACTIVATION_PAYLOAD)EventInfo.Info.pbEventData; + + pbEventPayload->bConnectionId = m_HCEConnectionId; + pbEventPayload->eRfTechType = NFC_RF_TECHNOLOGY_A; + pbEventPayload->eRfProtocolType = PROTOCOL_ISO_DEP; + + HandleSecureElementEvent(&EventInfo.Info); + } + + LeaveCriticalSection(&m_pHCEConnectionLock); + } + } + + MethodReturnVoid(); +} + +BOOL CQueue::ConnectionTerminated(_In_ CConnection* pConnection) +{ + MethodEntry("pConnection = 0x%p", pConnection); + + BOOL fConnectionDeleted = FALSE; + + if (pConnection->GetConnectionType() == CONNECTION_TYPE_P2P) { + LIST_ENTRY* pRemoveListEntry = pConnection->GetListEntry(); + + EnterCriticalSection(&m_ConnectionLock); + + for (LIST_ENTRY* pEntry = m_ConnectionList.Flink; + pEntry != &m_ConnectionList; + pEntry = pEntry->Flink) { + if (pEntry == pRemoveListEntry) { + RemoveEntryList(pEntry); + break; + } + } + + if (IsListEmpty(&m_ConnectionList)) { + HandleRemovalEvent(); + } + LeaveCriticalSection(&m_ConnectionLock); + + if (pConnection->IsInboundConnection()) { + delete pConnection; + fConnectionDeleted = TRUE; + } + } + else if (pConnection->GetConnectionType() == CONNECTION_TYPE_TAG) { + fConnectionDeleted = m_SmartCardReader.CardRemoved(pConnection); + } + else if (pConnection->GetConnectionType() == CONNECTION_TYPE_HCE) { + SECURE_ELEMENT_EVENT_INFO_AND_PAYLOAD(sizeof(USHORT)) EventInfo; + + if (NT_SUCCESS(GetSecureElementId(DeviceHost, &EventInfo.Info.guidSecureElementId))) { + EnterCriticalSection(&m_pHCEConnectionLock); + + if (m_pHCEConnection != nullptr) { + m_pHCEConnection = nullptr; + + EventInfo.Info.eEventType = HceDeactivated; + EventInfo.Info.cbEventData = sizeof(USHORT); + + *((USHORT*)EventInfo.Info.pbEventData) = m_HCEConnectionId; + + HandleSecureElementEvent(&EventInfo.Info); + } + LeaveCriticalSection(&m_pHCEConnectionLock); + } + } + + MethodReturnBool(fConnectionDeleted); +} + +void CQueue::HandleArrivalEvent() +{ + MethodEntry("void"); + + EnterCriticalSection(&m_SubsLock); + + for (LIST_ENTRY* pEntry = m_ArrivalSubsList.Flink; + pEntry != &m_ArrivalSubsList; + pEntry = pEntry->Flink) { + CFileObject::FromListEntry(pEntry)->HandleArrivalEvent(); + } + LeaveCriticalSection(&m_SubsLock); + + MethodReturnVoid(); +} + +void CQueue::HandleRemovalEvent() +{ + MethodEntry("void"); + + EnterCriticalSection(&m_SubsLock); + + for (LIST_ENTRY* pEntry = m_DepartureSubsList.Flink; + pEntry != &m_DepartureSubsList; + pEntry = pEntry->Flink) { + CFileObject::FromListEntry(pEntry)->HandleRemovalEvent(); + } + LeaveCriticalSection(&m_SubsLock); + + MethodReturnVoid(); +} + +void CQueue::HandleReceivedMessage(_In_ CONNECTION_TYPE ConnType, _In_ MESSAGE* pMessage) +{ + MethodEntry("pMessage->m_szType = '%S'", pMessage->m_szType); + + if (ConnType == CONNECTION_TYPE_P2P) { + if ((pMessage->m_cbPayload > 0) && (pMessage->m_cbPayload <= MaxCbPayload)) { + EnterCriticalSection(&m_SubsLock); + + for (LIST_ENTRY* pEntry = m_SubsList.Flink; pEntry != &m_SubsList; pEntry = pEntry->Flink) { + CFileObject* pSub = CFileObject::FromListEntry(pEntry); + pSub->HandleReceivedMessage(pMessage->m_szType, pMessage->m_cbPayload, pMessage->m_Payload); + } + LeaveCriticalSection(&m_SubsLock); + } + } + else if (ConnType == CONNECTION_TYPE_TAG) { + m_SmartCardReader.MessageReceived(pMessage); + } + else if (ConnType == CONNECTION_TYPE_HCE) { + EnterCriticalSection(&m_SEManagerLock); + + if (m_pSEManager != nullptr) { + m_pSEManager->HandleReceiveHcePacket((USHORT)m_HCEConnectionId, pMessage->m_cbPayload, pMessage->m_Payload); + } + LeaveCriticalSection(&m_SEManagerLock); + } + + MethodReturnVoid(); +} + +void CQueue::HandleSecureElementEvent(SECURE_ELEMENT_EVENT_INFO *pInfo) +{ + MethodEntry("..."); + + CSecureElement *pSecureElement = nullptr; + + if (NT_SUCCESS(GetSecureElementObject(pInfo->guidSecureElementId, &pSecureElement))) { + if (pSecureElement->GetEmulationMode() != EmulationOff) { + EnterCriticalSection(&m_SEEventsLock); + + for (LIST_ENTRY* pEntry = m_SEEventsList.Flink; + pEntry != &m_SEEventsList; + pEntry = pEntry->Flink) { + CFileObject::FromListEntry(pEntry)->HandleSecureElementEvent(pInfo); + } + LeaveCriticalSection(&m_SEEventsLock); + } + } + + MethodReturnVoid(); +} + +void CQueue::EnumerateSecureElements() +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + WDFKEY KeyHandle = nullptr; + WDFCOLLECTION Collection = nullptr; + GUID SecureElementId; + UNICODE_STRING GuidString; + CSecureElement *pSecureElement = nullptr; + + DECLARE_CONST_UNICODE_STRING(SEEndpointsKey, SE_ENDPOINTS_KEY); + + Status = WdfDeviceOpenRegistryKey( + WdfIoQueueGetDevice(m_Queue), + PLUGPLAY_REGKEY_DEVICE, + KEY_READ, + WDF_NO_OBJECT_ATTRIBUTES, + &KeyHandle); + + if (NT_SUCCESS(Status) && (KeyHandle != nullptr)) { + Status = WdfCollectionCreate(WDF_NO_OBJECT_ATTRIBUTES, &Collection); + + if (NT_SUCCESS(Status) && (Collection != nullptr)) { + Status = WdfRegistryQueryMultiString( + KeyHandle, + &SEEndpointsKey, + WDF_NO_OBJECT_ATTRIBUTES, + Collection); + + if (NT_SUCCESS(Status)) { + ULONG Count = WdfCollectionGetCount(Collection); + + for (ULONG Index = 0; Index < Count; Index++) { + WdfStringGetUnicodeString((WDFSTRING) WdfCollectionGetItem(Collection, Index), &GuidString); + + if (SUCCEEDED(CLSIDFromString(GuidString.Buffer, &SecureElementId)) && + (pSecureElement = new CSecureElement(SecureElementId, IsEqualGUID(SecureElementId, GUID_DH_SECURE_ELEMENT) ? DeviceHost : External)) != nullptr) { + TraceInfo("Secure Element Id=%S Type=%d", GuidString.Buffer, pSecureElement->GetType()); + InsertHeadList(&m_SecureElementList, pSecureElement->GetListEntry()); + } + } + } + + WdfObjectDelete(Collection); + } + + WdfRegistryClose(KeyHandle); + } + + MethodReturnVoid(); +} + +void CQueue::ReadSettingsFromRegistry() +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + WDFKEY KeyHandle = nullptr; + ULONG NfpRadioOffPolicyOverride = 0, NfpRadioOffSystemOverride = 0; + + DECLARE_CONST_UNICODE_STRING(NfpRadioTurnedOffKey, NFP_RADIO_TURNED_OFF_KEY); + DECLARE_CONST_UNICODE_STRING(NfpRadioFlightModeKey, NFP_RADIO_FLIGHT_MODE_KEY); + + Status = WdfDeviceOpenRegistryKey( + WdfIoQueueGetDevice(m_Queue), + PLUGPLAY_REGKEY_DEVICE | WDF_REGKEY_DEVICE_SUBKEY, + KEY_READ, + WDF_NO_OBJECT_ATTRIBUTES, + &KeyHandle); + + if (NT_SUCCESS(Status) && (KeyHandle != nullptr)) { + Status = WdfRegistryQueryULong( + KeyHandle, + &NfpRadioTurnedOffKey, + &NfpRadioOffPolicyOverride); + + if (NT_SUCCESS(Status)) { + TraceInfo("%S = %d", NFP_RADIO_TURNED_OFF_KEY, NfpRadioOffPolicyOverride); + m_NfpRadioOffPolicyOverride = (NfpRadioOffPolicyOverride != 0); + } + + Status = WdfRegistryQueryULong( + KeyHandle, + &NfpRadioFlightModeKey, + &NfpRadioOffSystemOverride); + + if (NT_SUCCESS(Status)) { + TraceInfo("%S = %d", NFP_RADIO_FLIGHT_MODE_KEY, NfpRadioOffSystemOverride); + m_NfpRadioOffSystemOverride = (NfpRadioOffSystemOverride != 0); + } + + m_NfpRadioState = !m_NfpRadioOffPolicyOverride && !m_NfpRadioOffSystemOverride; + + WdfRegistryClose(KeyHandle); + } + + MethodReturnVoid(); +} + +void CQueue::WriteSettingsToRegistry() +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + WDFKEY KeyHandle = nullptr; + + DECLARE_CONST_UNICODE_STRING(NfpRadioTurnedOffKey, NFP_RADIO_TURNED_OFF_KEY); + DECLARE_CONST_UNICODE_STRING(NfpRadioFlightModeKey, NFP_RADIO_FLIGHT_MODE_KEY); + + Status = WdfDeviceOpenRegistryKey( + WdfIoQueueGetDevice(m_Queue), + PLUGPLAY_REGKEY_DEVICE | WDF_REGKEY_DEVICE_SUBKEY, + GENERIC_ALL & ~(GENERIC_WRITE | KEY_CREATE_SUB_KEY | WRITE_DAC), + WDF_NO_OBJECT_ATTRIBUTES, + &KeyHandle); + + if (NT_SUCCESS(Status) && (KeyHandle != nullptr)) { + Status = WdfRegistryAssignULong( + KeyHandle, + &NfpRadioTurnedOffKey, + m_NfpRadioOffPolicyOverride); + + if (NT_SUCCESS(Status)) { + TraceInfo("%S = %d successfully persisted", NFP_RADIO_TURNED_OFF_KEY, m_NfpRadioOffPolicyOverride); + } + + Status = WdfRegistryAssignULong( + KeyHandle, + &NfpRadioFlightModeKey, + m_NfpRadioOffSystemOverride); + + if (NT_SUCCESS(Status)) { + TraceInfo("%S = %d successfully persisted", NFP_RADIO_FLIGHT_MODE_KEY, m_NfpRadioOffSystemOverride); + } + + WdfRegistryClose(KeyHandle); + } + + MethodReturnVoid(); +} + +NTSTATUS CQueue::GetSecureElementObject(GUID &SecureElementId, CSecureElement **ppSecureElement) +{ + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + for (LIST_ENTRY* pEntry = m_SecureElementList.Flink; + pEntry != &m_SecureElementList; + pEntry = pEntry->Flink) { + if (SecureElementId == CSecureElement::FromListEntry(pEntry)->GetIdentifier()) { + *ppSecureElement = CSecureElement::FromListEntry(pEntry); + Status = STATUS_SUCCESS; + break; + } + } + + return Status; +} + +NTSTATUS CQueue::GetSecureElementId(SECURE_ELEMENT_TYPE eType, GUID *pSecureElementId) +{ + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + *pSecureElementId = GUID_NULL; + + for (LIST_ENTRY* pEntry = m_SecureElementList.Flink; + (pEntry != &m_SecureElementList); + pEntry = pEntry->Flink) { + if (CSecureElement::FromListEntry(pEntry)->GetType() == eType) { + *pSecureElementId = CSecureElement::FromListEntry(pEntry)->GetIdentifier(); + Status = STATUS_SUCCESS; + break; + } + } + + return Status; +} + diff --git a/nfc/Simulator/Src/Queue.h b/nfc/Simulator/Src/Queue.h new file mode 100644 index 000000000..40ac6982a --- /dev/null +++ b/nfc/Simulator/Src/Queue.h @@ -0,0 +1,217 @@ +/*++ + +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) + +Revision History: + +--*/ + +#pragma once + +static const int KilobytesPerSecond = 20; +static const int MinCbPayload = 1; +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 nullptr 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 nullptr terminator. +static const int MinCchType = 2; +static const int MaxCchMimeType = 256; +static const int MaxSecureElements = 16; + +#define SIM_NAMESPACE L"Simulator" +#define SIM_NAMESPACE_CHARS ARRAYSIZE(SIM_NAMESPACE) - 1 + +#define NFP_RADIO_TURNED_OFF_KEY L"NfpRadioTurnedOff" +#define NFP_RADIO_FLIGHT_MODE_KEY L"NfpRadioFlightMode" + +#define SE_ENDPOINTS_KEY L"SEEndpoints" +#define HCE_MESSAGE_TYPE_TRANSMIT L"HCETransmit" + +struct MESSAGE +{ + MESSAGE() : m_cbPayload(0) + { + RtlZeroMemory(m_szType, sizeof(m_szType)); + RtlZeroMemory(m_Payload, sizeof(m_Payload)); + } + + void Initialize( + _In_ PCWSTR szType, + _In_ DWORD cbPayload, + _In_reads_bytes_(cbPayload) PBYTE pbPayload + ) + { + RtlZeroMemory(m_szType, sizeof(m_szType)); + RtlZeroMemory(m_Payload, sizeof(m_Payload)); + + m_cbPayload = cbPayload; + StringCchCopy(m_szType, _countof(m_szType), szType); + RtlCopyMemory(m_Payload, pbPayload, cbPayload); + } + + wchar_t m_szType[MaxCchTypeNetwork]; + DWORD m_cbPayload; + BYTE m_Payload[MaxCbPayload]; +}; + +class CQueue + : public IValidateAccept, + public IConnectionCallback +{ +public: + CQueue(WDFQUEUE Queue); + ~CQueue(); + +public: + static EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL OnIoDeviceControl; + + NTSTATUS Initialize(); + NTSTATUS Deinitialize(); + + BOOLEAN IsNfpRadioEnabled(); + BOOLEAN IsSERadioEnabled(); + + void OnFileCreate(_In_ WDFDEVICE Device, _In_ WDFREQUEST Request, _In_ WDFFILEOBJECT FileObject); + void OnFileClose(_In_ WDFFILEOBJECT FileObject); + + void OnIoDeviceControl(_In_ WDFREQUEST Request, _In_ ULONG IoControlCode, _In_ size_t InputBufferLength, _In_ size_t OutputBufferLength); + +public: + //IValidateAccept + void ValidateAccept(_In_ SOCKET Socket, _In_ GUID* pMagicPacket); + + //IConnectionCallback + virtual void HandleReceivedMessage(_In_ CONNECTION_TYPE ConnType, _In_ MESSAGE* pMessageData); + virtual void ConnectionEstablished(_In_ CConnection* pConnection); + virtual BOOL ConnectionTerminated(_In_ CConnection* pConnection); + +public: + CSmartCardReader* GetSmartCardReader() { return &m_SmartCardReader; } + NTSTATUS GetSecureElementId(SECURE_ELEMENT_TYPE eType, GUID *pSecureElementId); + +private: + NTSTATUS DetectRole(CFileObject *pFileObject, PCWSTR pszFileName); + void HandleArrivalEvent(); + void HandleRemovalEvent(); + void HandleSecureElementEvent(SECURE_ELEMENT_EVENT_INFO *pInfo); + void EnumerateSecureElements(); + void ReadSettingsFromRegistry(); + void WriteSettingsToRegistry(); + NTSTATUS GetSecureElementObject(GUID &SecureElementId, CSecureElement **ppSecureElement); + +private: + typedef NTSTATUS (CQueue::EVT_DISPATCH_HANDLER)( + _In_ CFileObject *pFileObject, + _In_ WDFREQUEST Request, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ size_t InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ size_t OutputBufferLength); + + typedef NTSTATUS (CQueue::*PFN_DISPATCH_HANDLER)( + _In_ CFileObject *pFileObject, + _In_ WDFREQUEST Request, + _In_reads_bytes_opt_(InputBufferLength) PVOID InputBuffer, + _In_ size_t InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ size_t OutputBufferLength); + +private: + // Proximity DDI + EVT_DISPATCH_HANDLER OnNfpEnable; + EVT_DISPATCH_HANDLER OnNfpDisable; + EVT_DISPATCH_HANDLER OnNfpSetPayload; + EVT_DISPATCH_HANDLER OnNfpGetNextSubscribedMessage; + EVT_DISPATCH_HANDLER OnNfpGetNextTransmittedMessage; + EVT_DISPATCH_HANDLER OnNfpGetMaxMessageBytes; + EVT_DISPATCH_HANDLER OnNfpGetTransmissionRateKbps; + EVT_DISPATCH_HANDLER OnNfpBeginProximity; + EVT_DISPATCH_HANDLER OnNfpSetRadioState; + EVT_DISPATCH_HANDLER OnNfpQueryRadioState; + + // Secure Element DDI + EVT_DISPATCH_HANDLER OnSEEnumEndpoints; + EVT_DISPATCH_HANDLER OnSESubscribeForEvent; + EVT_DISPATCH_HANDLER OnSEGetNextEvent; + EVT_DISPATCH_HANDLER OnSESetCardEmulationMode; + EVT_DISPATCH_HANDLER OnSETriggerEvent; + EVT_DISPATCH_HANDLER OnSEGetNfccCapabilities; + EVT_DISPATCH_HANDLER OnSESetRoutingTable; + EVT_DISPATCH_HANDLER OnSEGetRoutingTable; + EVT_DISPATCH_HANDLER OnSEHCERemoteRecv; + EVT_DISPATCH_HANDLER OnSEHCERemoteSend; + + // Smart Card DDI + EVT_DISPATCH_HANDLER OnSCGetAttribute; + EVT_DISPATCH_HANDLER OnSCSetAttribute; + EVT_DISPATCH_HANDLER OnSCGetState; + EVT_DISPATCH_HANDLER OnSCIsAbsent; + EVT_DISPATCH_HANDLER OnSCIsPresent; + EVT_DISPATCH_HANDLER OnSCPower; + EVT_DISPATCH_HANDLER OnSCSetProtocol; + EVT_DISPATCH_HANDLER OnSCTransmit; + EVT_DISPATCH_HANDLER OnSCGetLastError; + +private: + typedef struct _DISPATCH_ENTRY + { + ULONG IoControlCode; + size_t MinInputBufferLength; + size_t MinOutputBufferLength; + PFN_DISPATCH_HANDLER DispatchHandler; + } + DISPATCH_ENTRY, *PDISPATCH_ENTRY; + + NTSTATUS DispatchMessage( + _In_reads_(TableEntries) const DISPATCH_ENTRY rgDispatchTable[], + _In_ DWORD TableEntries, + _In_ WDFREQUEST Request, + _In_ ULONG IoControlCode, + _In_ size_t InputBufferLength, + _In_ size_t OutputBufferLength); + + BOOL ValidateMessage(_In_ CFileObject *pFileObject, _In_ ULONG IoControlCode); + + WDFQUEUE m_Queue; + LIST_ENTRY m_SubsList; + LIST_ENTRY m_ArrivalSubsList; + LIST_ENTRY m_DepartureSubsList; + CRITICAL_SECTION m_SubsLock; + LIST_ENTRY m_PubsList; + CRITICAL_SECTION m_PubsLock; + LIST_ENTRY m_ConnectionList; + CRITICAL_SECTION m_ConnectionLock; + CSocketListener m_SocketListener; + LIST_ENTRY m_SecureElementList; + LIST_ENTRY m_SEEventsList; + CRITICAL_SECTION m_SEEventsLock; + CFileObject* m_pSEManager; + CRITICAL_SECTION m_SEManagerLock; + CSmartCardReader m_SmartCardReader; + CRITICAL_SECTION m_pHCEConnectionLock; + CConnection* m_pHCEConnection; + USHORT m_HCEConnectionId; + CRITICAL_SECTION m_RadioLock; + BOOLEAN m_NfpRadioState; + BOOLEAN m_NfpRadioOffPolicyOverride; + BOOLEAN m_NfpRadioOffSystemOverride; + BOOLEAN m_SERadioState; + BOOLEAN m_NfpInterfaceCreated; + BOOLEAN m_ScInterfaceCreated; + BOOLEAN m_SEInterfaceCreated; + CRoutingTable m_RoutingTable; +}; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(CQueue, GetQueueObject); + diff --git a/nfc/Simulator/Src/RoutingTable.cpp b/nfc/Simulator/Src/RoutingTable.cpp new file mode 100644 index 000000000..5a15796ad --- /dev/null +++ b/nfc/Simulator/Src/RoutingTable.cpp @@ -0,0 +1,388 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + routingtable.cpp + +Abstract: + + Implements the NFC routing table class + +Environment: + + User-mode only. + +--*/ + +#include "Internal.h" +#include "RoutingTable.tmh" + +#define MAX_ROUTING_TABLE_SIZE_KEY L"MaxRoutingTableSize" +#define AID_ROUTING_SUPPORTED_KEY L"IsAidRoutingSupported" +#define PROTOCOL_ROUTING_SUPPORTED_KEY L"IsProtRoutingSupported" +#define TECH_ROUTING_SUPPORTED_KEY L"IsTechRoutingSupported" + +#define DEFAULT_MAX_ROUTING_TABLE_SIZE 255 + +CRoutingTable::CRoutingTable(_In_ CQueue *pQueue) + : m_pQueue(pQueue), + m_pRoutingTable(nullptr), + m_RoutingTableMaxSize(0), + m_IsAidRoutingSupported(TRUE), + m_IsTechRoutingSupported(TRUE), + m_IsProtocolRoutingSupported(TRUE), + m_cbMaxRoutingTableSize(DEFAULT_MAX_ROUTING_TABLE_SIZE) +{ + InitializeCriticalSection(&m_RoutingTableLock); +} + +CRoutingTable::~CRoutingTable() +{ + SAFE_DELETEARRAY(m_pRoutingTable); + m_RoutingTableMaxSize = 0; + DeleteCriticalSection(&m_RoutingTableLock); +} + +VOID CRoutingTable::Initialize(WDFDEVICE Device) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + WDFKEY KeyHandle = nullptr; + ULONG uValue = 0; + + DECLARE_CONST_UNICODE_STRING(MaxRoutingTableSizeKey, MAX_ROUTING_TABLE_SIZE_KEY); + DECLARE_CONST_UNICODE_STRING(AidRoutingSupportedKey, AID_ROUTING_SUPPORTED_KEY); + DECLARE_CONST_UNICODE_STRING(ProtocolRoutingSupportedKey, PROTOCOL_ROUTING_SUPPORTED_KEY); + DECLARE_CONST_UNICODE_STRING(TechRoutingSupportedKey, TECH_ROUTING_SUPPORTED_KEY); + + Status = WdfDeviceOpenRegistryKey( + Device, + PLUGPLAY_REGKEY_DEVICE | WDF_REGKEY_DEVICE_SUBKEY, + KEY_READ, + WDF_NO_OBJECT_ATTRIBUTES, + &KeyHandle); + + if (NT_SUCCESS(Status) && (KeyHandle != nullptr)) { + Status = WdfRegistryQueryULong( + KeyHandle, + &MaxRoutingTableSizeKey, + &uValue); + + if (NT_SUCCESS(Status) && uValue <= ((USHORT)0xFFFF)) { // MAX_USHORT + TraceInfo("MaxRoutingTableSize=%d", uValue); + m_cbMaxRoutingTableSize = (USHORT)uValue; + } + + Status = WdfRegistryQueryULong( + KeyHandle, + &AidRoutingSupportedKey, + &uValue); + + if (NT_SUCCESS(Status)) { + TraceInfo("IsAidRoutingSupported=%d", uValue); + m_IsAidRoutingSupported = (uValue != 0); + } + + Status = WdfRegistryQueryULong( + KeyHandle, + &ProtocolRoutingSupportedKey, + &uValue); + + if (NT_SUCCESS(Status)) { + TraceInfo("IsProtocolRoutingSupported=%d", uValue); + m_IsProtocolRoutingSupported = (uValue != 0); + } + + Status = WdfRegistryQueryULong( + KeyHandle, + &TechRoutingSupportedKey, + &uValue); + + if (NT_SUCCESS(Status)) { + TraceInfo("IsTechRoutingSupported=%d", uValue); + m_IsTechRoutingSupported = (uValue != 0); + } + + WdfRegistryClose(KeyHandle); + } + + Status = SetDefaultRoutingTable(); + + MethodReturnVoid(); +} + +NTSTATUS CRoutingTable::ValidateRoutingTable(_In_ PSECURE_ELEMENT_ROUTING_TABLE pRoutingTable) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + DWORD cbTableSize = NCI_PROTO_ROUTING_ENTRY_SIZE; // Implicit NFC-DEP route + + for (DWORD nIndex = 0; (nIndex < pRoutingTable->NumberOfEntries) && NT_SUCCESS(Status); nIndex++) { + switch (pRoutingTable->TableEntries[nIndex].eRoutingType) { + case RoutingTypeAid: + Status = ValidateAidRoute(&pRoutingTable->TableEntries[nIndex]); + cbTableSize += NCI_AID_ROUTING_ENTRY_SIZE(pRoutingTable->TableEntries[nIndex].AidRoutingInfo.cbAid); + break; + + case RoutingTypeProtocol: + Status = ValidateProtocolRoute(&pRoutingTable->TableEntries[nIndex]); + cbTableSize += NCI_PROTO_ROUTING_ENTRY_SIZE; + break; + + case RoutingTypeTech: + Status = ValidateTechRoute(&pRoutingTable->TableEntries[nIndex]); + cbTableSize += NCI_TECH_ROUTING_ENTRY_SIZE; + break; + + default: + Status = STATUS_INVALID_PARAMETER; + break; + } + + if (NT_SUCCESS(Status)) { + Status = ValidateRouteUnique(pRoutingTable, nIndex); + } + } + + if (NT_SUCCESS(Status) && cbTableSize > MaxRoutingTableSize()) { + Status = STATUS_INVALID_BUFFER_SIZE; + } + + MethodReturn(Status, "Status = %!STATUS!", Status); +} + +NTSTATUS CRoutingTable::ValidateAidRoute(_In_ PSECURE_ELEMENT_ROUTING_TABLE_ENTRY pRoutingEntry) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + + NT_ASSERT(pRoutingEntry->eRoutingType == RoutingTypeAid); + + if (!IsAidRoutingSupported()) { + Status = STATUS_NOT_SUPPORTED; + goto Exit; + } + + if ((pRoutingEntry->AidRoutingInfo.cbAid < ISO_7816_MINIMUM_AID_LENGTH) || + (pRoutingEntry->AidRoutingInfo.cbAid > ISO_7816_MAXIMUM_AID_LENGTH)) { + Status = STATUS_INVALID_PARAMETER; + goto Exit; + } + +Exit: + MethodReturn(Status, "Status = %!STATUS!", Status); +} + +NTSTATUS CRoutingTable::ValidateProtocolRoute(_In_ PSECURE_ELEMENT_ROUTING_TABLE_ENTRY pRoutingEntry) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + + NT_ASSERT(pRoutingEntry->eRoutingType == RoutingTypeProtocol); + + if (!IsProtocolRoutingSupported()) { + Status = STATUS_NOT_SUPPORTED; + goto Exit; + } + + if (pRoutingEntry->ProtoRoutingInfo.eRfProtocolType != PROTOCOL_T1T && + pRoutingEntry->ProtoRoutingInfo.eRfProtocolType != PROTOCOL_T2T && + pRoutingEntry->ProtoRoutingInfo.eRfProtocolType != PROTOCOL_T3T && + pRoutingEntry->ProtoRoutingInfo.eRfProtocolType != PROTOCOL_ISO_DEP) { + Status = STATUS_INVALID_PARAMETER; + goto Exit; + } + +Exit: + MethodReturn(Status, "Status = %!STATUS!", Status); +} + +NTSTATUS CRoutingTable::ValidateTechRoute(_In_ PSECURE_ELEMENT_ROUTING_TABLE_ENTRY pRoutingEntry) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + + NT_ASSERT(pRoutingEntry->eRoutingType == RoutingTypeTech); + + if (!IsTechRoutingSupported()) { + Status = STATUS_NOT_SUPPORTED; + goto Exit; + } + + if (pRoutingEntry->TechRoutingInfo.eRfTechType != NFC_RF_TECHNOLOGY_A && + pRoutingEntry->TechRoutingInfo.eRfTechType != NFC_RF_TECHNOLOGY_B && + pRoutingEntry->TechRoutingInfo.eRfTechType != NFC_RF_TECHNOLOGY_F) { + Status = STATUS_INVALID_PARAMETER; + goto Exit; + } + +Exit: + MethodReturn(Status, "Status = %!STATUS!", Status); +} + +NTSTATUS CRoutingTable::ValidateRouteUnique(_In_ PSECURE_ELEMENT_ROUTING_TABLE pRoutingTable, _In_ DWORD EndIndex) +{ + _Analysis_assume_(EndIndex < pRoutingTable->NumberOfEntries); + + for (DWORD nIndex = 0; (nIndex < EndIndex); nIndex++) { + if (pRoutingTable->TableEntries[nIndex].eRoutingType != + pRoutingTable->TableEntries[EndIndex].eRoutingType) { + continue; + } + + switch (pRoutingTable->TableEntries[nIndex].eRoutingType) { + case RoutingTypeAid: + if ((pRoutingTable->TableEntries[nIndex].AidRoutingInfo.cbAid == + pRoutingTable->TableEntries[EndIndex].AidRoutingInfo.cbAid) && + (memcmp(pRoutingTable->TableEntries[nIndex].AidRoutingInfo.pbAid, + pRoutingTable->TableEntries[EndIndex].AidRoutingInfo.pbAid, + pRoutingTable->TableEntries[nIndex].AidRoutingInfo.cbAid) == 0)) { + return STATUS_INVALID_PARAMETER; + } + break; + + case RoutingTypeProtocol: + if (pRoutingTable->TableEntries[nIndex].ProtoRoutingInfo.eRfProtocolType == + pRoutingTable->TableEntries[EndIndex].ProtoRoutingInfo.eRfProtocolType) { + return STATUS_INVALID_PARAMETER; + } + break; + + case RoutingTypeTech: + if (pRoutingTable->TableEntries[nIndex].TechRoutingInfo.eRfTechType == + pRoutingTable->TableEntries[EndIndex].TechRoutingInfo.eRfTechType) { + return STATUS_INVALID_PARAMETER; + } + break; + } + } + + return STATUS_SUCCESS; +} + +NTSTATUS CRoutingTable::GetRoutingTable(_Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, size_t OutputBufferLength, DWORD* pcbOutputBuffer) +{ + NTSTATUS Status = STATUS_SUCCESS; + PSECURE_ELEMENT_ROUTING_TABLE pRoutingTable = (PSECURE_ELEMENT_ROUTING_TABLE)OutputBuffer; + + _Analysis_assume_(sizeof(DWORD) <= OutputBufferLength); + + EnterCriticalSection(&m_RoutingTableLock); + + pRoutingTable->NumberOfEntries = m_pRoutingTable->NumberOfEntries; + *pcbOutputBuffer = sizeof(m_pRoutingTable->NumberOfEntries); + + if ((*pcbOutputBuffer + m_pRoutingTable->NumberOfEntries * sizeof(SECURE_ELEMENT_ROUTING_TABLE_ENTRY)) > OutputBufferLength) { + Status = STATUS_BUFFER_OVERFLOW; + goto Exit; + } + + RtlCopyMemory(pRoutingTable->TableEntries, m_pRoutingTable->TableEntries, m_pRoutingTable->NumberOfEntries * sizeof(SECURE_ELEMENT_ROUTING_TABLE_ENTRY)); + *pcbOutputBuffer += m_pRoutingTable->NumberOfEntries * sizeof(SECURE_ELEMENT_ROUTING_TABLE_ENTRY); + +Exit: + LeaveCriticalSection(&m_RoutingTableLock); + return Status; +} + +NTSTATUS CRoutingTable::SetDefaultRoutingTable() +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + GUID SecureElementId = GUID_NULL; + + Status = m_pQueue->GetSecureElementId(External, &SecureElementId); + + if (NT_SUCCESS(Status)) + { + if (IsTechRoutingSupported()) { + SECURE_ELEMENT_ROUTING_TABLE_AND_ENTRIES(3) RoutingTable; + + RoutingTable.Table.NumberOfEntries = 3; + RoutingTable.Table.TableEntries[0].eRoutingType = RoutingTypeTech; + RoutingTable.Table.TableEntries[0].TechRoutingInfo.eRfTechType = NFC_RF_TECHNOLOGY_A; + RoutingTable.Table.TableEntries[0].TechRoutingInfo.guidSecureElementId = SecureElementId; + RoutingTable.Table.TableEntries[1].eRoutingType = RoutingTypeTech; + RoutingTable.Table.TableEntries[1].TechRoutingInfo.eRfTechType = NFC_RF_TECHNOLOGY_B; + RoutingTable.Table.TableEntries[1].TechRoutingInfo.guidSecureElementId = SecureElementId; + RoutingTable.Table.TableEntries[2].eRoutingType = RoutingTypeTech; + RoutingTable.Table.TableEntries[2].TechRoutingInfo.eRfTechType = NFC_RF_TECHNOLOGY_F; + RoutingTable.Table.TableEntries[2].TechRoutingInfo.guidSecureElementId = SecureElementId; + + Status = SetRoutingTable(&RoutingTable.Table); + } + else if (IsProtocolRoutingSupported()) { + SECURE_ELEMENT_ROUTING_TABLE RoutingTable; + + RoutingTable.NumberOfEntries = 1; + RoutingTable.TableEntries[0].eRoutingType = RoutingTypeProtocol; + RoutingTable.TableEntries[0].TechRoutingInfo.eRfTechType = PROTOCOL_ISO_DEP; + RoutingTable.TableEntries[0].TechRoutingInfo.guidSecureElementId = SecureElementId; + + Status = SetRoutingTable(&RoutingTable); + } + else { + Status = STATUS_NOT_SUPPORTED; + TraceInfo("Neither Technology and Procotol routing is supported"); + } + } + + MethodReturn(Status, "Status = %!STATUS!", Status); +} + +NTSTATUS CRoutingTable::ResizeRoutingTable(DWORD NumberOfEntries) +{ + NTSTATUS Status = STATUS_SUCCESS; + + DWORD cbRoutingTable = sizeof(SECURE_ELEMENT_ROUTING_TABLE) + + sizeof(SECURE_ELEMENT_ROUTING_TABLE_ENTRY) * (NumberOfEntries-1); + + if (m_RoutingTableMaxSize >= NumberOfEntries) { + Status = STATUS_SUCCESS; + goto Exit; + } + + SAFE_DELETEARRAY(m_pRoutingTable); + m_RoutingTableMaxSize = 0; + + m_pRoutingTable = (PSECURE_ELEMENT_ROUTING_TABLE) new BYTE[cbRoutingTable]; + + if (m_pRoutingTable == nullptr) { + Status = STATUS_INSUFFICIENT_RESOURCES; + goto Exit; + } + + m_pRoutingTable->NumberOfEntries = 0; + m_RoutingTableMaxSize = NumberOfEntries; + +Exit: + return Status; +} + +NTSTATUS CRoutingTable::SetRoutingTable(_In_ PSECURE_ELEMENT_ROUTING_TABLE pRoutingTable) +{ + NTSTATUS Status = ValidateRoutingTable(pRoutingTable); + + if (NT_SUCCESS(Status)) { + EnterCriticalSection(&m_RoutingTableLock); + + Status = ResizeRoutingTable(pRoutingTable->NumberOfEntries); + + if (NT_SUCCESS(Status)) { + DWORD cbSize = sizeof(SECURE_ELEMENT_ROUTING_TABLE) + (pRoutingTable->NumberOfEntries - 1) * sizeof(SECURE_ELEMENT_ROUTING_TABLE_ENTRY); + RtlCopyMemory(m_pRoutingTable, pRoutingTable, cbSize); + } + + LeaveCriticalSection(&m_RoutingTableLock); + } + + return Status; +} diff --git a/nfc/Simulator/Src/RoutingTable.h b/nfc/Simulator/Src/RoutingTable.h new file mode 100644 index 000000000..2f234d6da --- /dev/null +++ b/nfc/Simulator/Src/RoutingTable.h @@ -0,0 +1,55 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + RoutingTable.h + +Abstract: + + This header file defines the listen mode routing table + +Environment: + + User Mode + +--*/ +#pragma once + +class CRoutingTable +{ +public: + CRoutingTable(_In_ CQueue *pQueue); + ~CRoutingTable(); + + VOID Initialize(_In_ WDFDEVICE Device); + NTSTATUS SetRoutingTable(_In_ PSECURE_ELEMENT_ROUTING_TABLE pRoutingTable); + NTSTATUS GetRoutingTable(_Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, size_t OutputBufferLength, DWORD* pcbOutputBuffer); + + FORCEINLINE BOOLEAN IsAidRoutingSupported() const { return m_IsAidRoutingSupported; } + FORCEINLINE BOOLEAN IsTechRoutingSupported() const { return m_IsTechRoutingSupported; } + FORCEINLINE BOOLEAN IsProtocolRoutingSupported() const { return m_IsProtocolRoutingSupported; } + FORCEINLINE USHORT MaxRoutingTableSize() const { return m_cbMaxRoutingTableSize; } + +private: + NTSTATUS ValidateRoutingTable(_In_ PSECURE_ELEMENT_ROUTING_TABLE pRoutingTable); + NTSTATUS ValidateAidRoute(_In_ PSECURE_ELEMENT_ROUTING_TABLE_ENTRY pRoutingEntry); + NTSTATUS ValidateProtocolRoute(_In_ PSECURE_ELEMENT_ROUTING_TABLE_ENTRY pRoutingEntry); + NTSTATUS ValidateTechRoute(_In_ PSECURE_ELEMENT_ROUTING_TABLE_ENTRY pRoutingEntry); + NTSTATUS ValidateRouteUnique(_In_ PSECURE_ELEMENT_ROUTING_TABLE pRoutingTable, _In_ DWORD EndIndex); + + NTSTATUS SetDefaultRoutingTable(); + NTSTATUS ResizeRoutingTable(DWORD NumberOfEntries); + +private: + CQueue* m_pQueue; + PSECURE_ELEMENT_ROUTING_TABLE + m_pRoutingTable; + CRITICAL_SECTION m_RoutingTableLock; + DWORD m_RoutingTableMaxSize; + BOOLEAN m_IsAidRoutingSupported; + BOOLEAN m_IsTechRoutingSupported; + BOOLEAN m_IsProtocolRoutingSupported; + USHORT m_cbMaxRoutingTableSize; +}; diff --git a/nfc/Simulator/Src/SecureElement.cpp b/nfc/Simulator/Src/SecureElement.cpp new file mode 100644 index 000000000..9273c9484 --- /dev/null +++ b/nfc/Simulator/Src/SecureElement.cpp @@ -0,0 +1,52 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + secureelement.cpp + +Abstract: + + Implements the secure element class + +Environment: + + User-mode only. + +--*/ + +#include "Internal.h" +#include "SecureElement.tmh" + +CSecureElement::CSecureElement( + const GUID& SecureElementId, + SECURE_ELEMENT_TYPE SecureElementType + ) + : m_SecureElementId(SecureElementId), + m_SecureElementType(SecureElementType), + m_EmulationMode(EmulationOff) +{ + InitializeListHead(&m_ListEntry); +} + +CSecureElement::~CSecureElement() +{ +} + +NTSTATUS CSecureElement::SetEmulationMode(SECURE_ELEMENT_CARD_EMULATION_MODE EmulationMode) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + + if (m_EmulationMode == EmulationMode) { + Status = STATUS_INVALID_DEVICE_STATE; + goto Exit; + } + + m_EmulationMode = EmulationMode; + +Exit: + MethodReturn(Status, "Status = %!STATUS!", Status); +} \ No newline at end of file diff --git a/nfc/Simulator/Src/SecureElement.h b/nfc/Simulator/Src/SecureElement.h new file mode 100644 index 000000000..e0e325b00 --- /dev/null +++ b/nfc/Simulator/Src/SecureElement.h @@ -0,0 +1,56 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + secureelement.h + +Abstract: + + This header file defines the secure element object + +Environment: + + User Mode + +--*/ +#pragma once + +class CSecureElement +{ +public: + CSecureElement(const GUID& SecureElementId, SECURE_ELEMENT_TYPE SecureElementType); + ~CSecureElement(); + +public: + NTSTATUS SetEmulationMode(SECURE_ELEMENT_CARD_EMULATION_MODE Mode); + +public: + GUID GetIdentifier() + { + return m_SecureElementId; + } + SECURE_ELEMENT_TYPE GetType() + { + return m_SecureElementType; + } + SECURE_ELEMENT_CARD_EMULATION_MODE GetEmulationMode() + { + return m_EmulationMode; + } + PLIST_ENTRY GetListEntry() + { + return &m_ListEntry; + } + static CSecureElement* FromListEntry(PLIST_ENTRY pEntry) + { + return (CSecureElement*) CONTAINING_RECORD(pEntry, CSecureElement, m_ListEntry); + } + +private: + GUID m_SecureElementId; // Secure Element GUID identifier + SECURE_ELEMENT_TYPE m_SecureElementType; // Secure Element type + SECURE_ELEMENT_CARD_EMULATION_MODE m_EmulationMode; // Emulation mode of the Secure Element + LIST_ENTRY m_ListEntry; // Secure Element list entry +}; diff --git a/nfc/Simulator/Src/SmartCard.cpp b/nfc/Simulator/Src/SmartCard.cpp new file mode 100644 index 000000000..eba0fd946 --- /dev/null +++ b/nfc/Simulator/Src/SmartCard.cpp @@ -0,0 +1,312 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + smartcard.cpp + +Abstract: + + This file implements the NFC smart card class. + +Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + +#include "Internal.h" +#include "SmartCard.tmh" + +CSmartCardRequest::CSmartCardRequest(_In_ CSmartCard *pSmartCard) + : m_pSmartCard(pSmartCard), + m_cbPayload(0), + m_hCompletionEvent(CreateEvent(nullptr, FALSE, FALSE, nullptr)) +{ + RtlZeroMemory(m_Payload, sizeof(m_Payload)); + InitializeListHead(&m_ListEntry); + m_dwSequenceNum = m_pSmartCard->AddRequest(this); +} + +CSmartCardRequest::~CSmartCardRequest() +{ + m_pSmartCard->RemoveRequest(this); + SAFE_CLOSEHANDLE(m_hCompletionEvent); +} + +VOID +CSmartCardRequest::CompleteRequest( + _In_ DWORD cbPayload, + _In_reads_bytes_(cbPayload) PBYTE pbPayload + ) +{ + RtlZeroMemory(m_Payload, sizeof(m_Payload)); + + if (cbPayload <= sizeof(m_Payload)) { + RtlCopyMemory(m_Payload, pbPayload, cbPayload); + m_cbPayload = cbPayload; + } + + SetEvent(m_hCompletionEvent); +} + +NTSTATUS CSmartCardRequest::WaitForCompletion() +{ + return (WaitForSingleObject(m_hCompletionEvent, ResponseTimeoutInMs) == WAIT_OBJECT_0) ? STATUS_SUCCESS : STATUS_IO_TIMEOUT; +} + +CSmartCard::CSmartCard(_In_ WDFDEVICE Device, _In_ CConnection* pConnection) + : m_Device(Device), + m_pConnection(pConnection), + m_dwSequenceNum(0) +{ + InitializeListHead(&m_RequestList); + InitializeCriticalSection(&m_ConnectionLock); + InitializeCriticalSection(&m_RequestListLock); +} + +CSmartCard::~CSmartCard() +{ + EnterCriticalSection(&m_RequestListLock); + + while (!IsListEmpty(&m_RequestList)) { + delete CSmartCardRequest::FromListEntry(RemoveHeadList(&m_RequestList)); + } + LeaveCriticalSection(&m_RequestListLock); + + DeleteCriticalSection(&m_ConnectionLock); + DeleteCriticalSection(&m_RequestListLock); + + SAFE_DELETE(m_pConnection); +} + +NTSTATUS +CSmartCard::SendCommand( + _In_reads_bytes_(InputBufferLength) PVOID InputBuffer, + _In_ size_t InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ size_t OutputBufferLength, + _Out_ size_t* BytesTransferred + ) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + MESSAGE *pMessage = nullptr; + DWORD dwSequenceNum = 0; + CSmartCardRequest *pRequest = nullptr; + BYTE* pbPayload = nullptr; + + *BytesTransferred = 0; + + if (InputBufferLength > MaxCbPayload) { + Status = STATUS_INVALID_BUFFER_SIZE; + goto Exit; + } + + pRequest = new CSmartCardRequest(this); + + if (pRequest == nullptr) { + Status = STATUS_INSUFFICIENT_RESOURCES; + goto Exit; + } + + pbPayload = new BYTE[InputBufferLength + sizeof(DWORD)]; + + if (pbPayload == nullptr) { + Status = STATUS_INSUFFICIENT_RESOURCES; + goto Exit; + } + + dwSequenceNum = pRequest->GetSequenceNum(); + + RtlCopyMemory(pbPayload, &dwSequenceNum, sizeof(DWORD)); + RtlCopyMemory(pbPayload + sizeof(DWORD), InputBuffer, InputBufferLength); + + pMessage = new MESSAGE(); + + if (pMessage == nullptr) { + Status = STATUS_INSUFFICIENT_RESOURCES; + goto Exit; + } + + pMessage->Initialize(SMARTCARD_MESSAGE_TYPE_TRANSMIT, (DWORD)InputBufferLength + sizeof(DWORD), pbPayload); + + EnterCriticalSection(&m_ConnectionLock); + + if (FAILED(m_pConnection->TransmitMessage(pMessage))) { + Status = STATUS_DATA_ERROR; + LeaveCriticalSection(&m_ConnectionLock); + goto Exit; + } + LeaveCriticalSection(&m_ConnectionLock); + + Status = pRequest->WaitForCompletion(); + + if (NT_SUCCESS(Status)) { + *BytesTransferred = pRequest->GetPayloadSize(); + + if (OutputBufferLength >= *BytesTransferred) { + RtlCopyMemory(OutputBuffer, pRequest->GetPayload(), *BytesTransferred); + } + else { + Status = STATUS_BUFFER_TOO_SMALL; + } + } + +Exit: + SAFE_DELETE(pMessage); + SAFE_DELETEARRAY(pbPayload); + SAFE_DELETE(pRequest); + + MethodReturn(Status, "Status = %!STATUS! Output buffer size = %d and payload size = %d", Status, (DWORD)OutputBufferLength, (DWORD)*BytesTransferred); +} + +BOOL CSmartCard::ResponseReceived(_In_ MESSAGE* pMessage) +{ + MethodEntry("..."); + + BOOL fHandled = FALSE; + DWORD dwSequenceNum = 0; + + if (CompareStringOrdinal(pMessage->m_szType, -1, SMARTCARD_MESSAGE_TYPE_TRANSMIT, -1, FALSE) == CSTR_EQUAL) { + if (pMessage->m_cbPayload >= sizeof(DWORD)) { + RtlCopyMemory(&dwSequenceNum, pMessage->m_Payload, sizeof(DWORD)); + TraceInfo("Sequence number = %d and Payload size = %d", dwSequenceNum, pMessage->m_cbPayload); + + EnterCriticalSection(&m_RequestListLock); + + for (LIST_ENTRY* pEntry = m_RequestList.Flink; + pEntry != &m_RequestList; + pEntry = pEntry->Flink) { + CSmartCardRequest *pRequest = CSmartCardRequest::FromListEntry(pEntry); + + if (dwSequenceNum == pRequest->GetSequenceNum()) { + pRequest->CompleteRequest(pMessage->m_cbPayload - sizeof(DWORD), pMessage->m_Payload + sizeof(DWORD)); + fHandled = TRUE; + break; + } + } + LeaveCriticalSection(&m_RequestListLock); + } + } + + MethodReturnBool(fHandled); +} + +DWORD CSmartCard::AddRequest(_In_ CSmartCardRequest *pRequest) +{ + NT_ASSERT(pRequest != nullptr); + + EnterCriticalSection(&m_RequestListLock); + InsertHeadList(&m_RequestList, pRequest->GetListEntry()); + LeaveCriticalSection(&m_RequestListLock); + + return InterlockedIncrement(&m_dwSequenceNum); +} + +VOID CSmartCard::RemoveRequest(_In_ CSmartCardRequest *pRequest) +{ + NT_ASSERT(pRequest != nullptr); + + EnterCriticalSection(&m_RequestListLock); + + for (LIST_ENTRY* pEntry = m_RequestList.Flink; + pEntry != &m_RequestList; + pEntry = pEntry->Flink) { + if (pRequest == CSmartCardRequest::FromListEntry(pEntry)) { + RemoveEntryList(pEntry); + break; + } + } + LeaveCriticalSection(&m_RequestListLock); +} + +NTSTATUS +CSmartCard::GetAtr( + _Out_writes_bytes_(*pcbAtr) PBYTE pbAtr, + _Inout_ LPDWORD pcbAtr + ) +{ + FunctionEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + WDFKEY hKey = nullptr; + WDFMEMORY Memory; + DWORD Type; + PBYTE Buffer = nullptr; + size_t BufferSize = 0; + DECLARE_CONST_UNICODE_STRING(KeyValue, L"Atr"); + + Status = WdfDeviceOpenRegistryKey( + m_Device, + PLUGPLAY_REGKEY_DEVICE, + KEY_READ, + WDF_NO_OBJECT_ATTRIBUTES, + &hKey); + + if (NT_SUCCESS(Status)) { + Status = WdfRegistryQueryMemory(hKey, &KeyValue, PagedPool, nullptr, &Memory, &Type); + + if (NT_SUCCESS(Status)) { + NT_ASSERT(Type == REG_BINARY); + + Buffer = (PBYTE) WdfMemoryGetBuffer(Memory, &BufferSize); + + if (*pcbAtr < BufferSize) { + Status = STATUS_BUFFER_TOO_SMALL; + } + else { + *pcbAtr = (DWORD)BufferSize; + RtlCopyMemory(pbAtr, Buffer, BufferSize); + } + + WdfObjectDelete(Memory); + } + + WdfRegistryClose(hKey); + } + + FunctionReturn(Status, "Status = %!STATUS!", Status); +} + +NTSTATUS +CSmartCard::GetIccType( + _Out_writes_bytes_(*pcbIccType) PBYTE pbIccType, + _Inout_ LPDWORD pcbIccType + ) +{ + FunctionEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + WDFKEY hKey = nullptr; + DWORD Value = 0; + BYTE bValue = 0; + DECLARE_CONST_UNICODE_STRING(KeyValue, L"IccType"); + + Status = WdfDeviceOpenRegistryKey( + m_Device, + PLUGPLAY_REGKEY_DEVICE, + KEY_READ, + WDF_NO_OBJECT_ATTRIBUTES, + &hKey); + + if (NT_SUCCESS(Status)) { + (VOID) WdfRegistryQueryULong(hKey, &KeyValue, &Value); + + bValue = (BYTE)(Value & 0xFF); + + if (*pcbIccType < sizeof(bValue)) { + Status = STATUS_BUFFER_TOO_SMALL; + } + else { + *pcbIccType = sizeof(bValue); + RtlCopyMemory(pbIccType, &bValue, sizeof(bValue)); + } + + WdfRegistryClose(hKey); + } + + FunctionReturn(Status, "Status = %!STATUS!", Status); +} diff --git a/nfc/Simulator/Src/SmartCard.h b/nfc/Simulator/Src/SmartCard.h new file mode 100644 index 000000000..0831c1f85 --- /dev/null +++ b/nfc/Simulator/Src/SmartCard.h @@ -0,0 +1,100 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + smartcard.h + +Abstract: + + The SmartCard simulates the communication channel with the selected smart card. + +Environment: + + User Mode + +--*/ +#pragma once + +static const int MaxResponsePayload = 1024; +static const int ResponseTimeoutInMs = 10000; + +#define SMARTCARD_MESSAGE_TYPE_TRANSMIT L"SCTransmit" + +class CSmartCard; + +class CSmartCardRequest +{ +public: + CSmartCardRequest(_In_ CSmartCard *pSmartCard); + ~CSmartCardRequest(); + +public: + VOID CompleteRequest(_In_ DWORD cbPayload, _In_reads_bytes_(cbPayload) PBYTE pbPayload); + NTSTATUS WaitForCompletion(); + +public: + DWORD GetSequenceNum() + { + return m_dwSequenceNum; + } + DWORD GetPayloadSize() + { + return m_cbPayload; + } + BYTE* GetPayload() + { + return (BYTE*)m_Payload; + } + PLIST_ENTRY GetListEntry() + { + return &m_ListEntry; + } + static CSmartCardRequest* FromListEntry(PLIST_ENTRY pEntry) + { + return (CSmartCardRequest*) CONTAINING_RECORD(pEntry, CSmartCardRequest, m_ListEntry); + } + +private: + CSmartCard* m_pSmartCard; // smart card object + HANDLE m_hCompletionEvent; // event indicate if the request is completed + DWORD m_dwSequenceNum; // sequence number for the transmit request/response + DWORD m_cbPayload; // size of the payload + BYTE m_Payload[MaxResponsePayload]; // payload buffer + LIST_ENTRY m_ListEntry; // list entry +}; + +class CSmartCard +{ +public: + CSmartCard(_In_ WDFDEVICE Device, _In_ CConnection* pConnection); + ~CSmartCard(); + +public: + NTSTATUS SendCommand( + _In_reads_bytes_(InputBufferLength) PVOID InputBuffer, + _In_ size_t InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PVOID OutputBuffer, + _In_ size_t OutputBufferLength, + _Out_ size_t* BytesTransferred); + + BOOL ResponseReceived(_In_ MESSAGE* pMessage); + + NTSTATUS GetAtr(_Out_writes_bytes_(*pcbAtr) PBYTE pbAtr, _Inout_ LPDWORD pcbAtr); + NTSTATUS GetIccType(_Out_writes_bytes_(*pcbIccType) PBYTE pbIccType, _Inout_ LPDWORD pcbIccType); + +private: + DWORD AddRequest(_In_ CSmartCardRequest *pRequest); + VOID RemoveRequest(_In_ CSmartCardRequest *pRequest); + +private: + friend class CSmartCardRequest; + + WDFDEVICE m_Device; + CRITICAL_SECTION m_ConnectionLock; // lock for the connection pointer + CConnection* m_pConnection; // connection object + DWORD volatile m_dwSequenceNum; // sequence number of the transmit command + LIST_ENTRY m_RequestList; // list of pending request + CRITICAL_SECTION m_RequestListLock; // lock for the response list +}; diff --git a/nfc/Simulator/Src/SmartCardReader.cpp b/nfc/Simulator/Src/SmartCardReader.cpp new file mode 100644 index 000000000..efae85d43 --- /dev/null +++ b/nfc/Simulator/Src/SmartCardReader.cpp @@ -0,0 +1,564 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + smartcardreader.cpp + +Abstract: + + Implements the NFC smart card reader class. + +Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + +#include "Internal.h" +#include "SmartCardReader.tmh" + +CSmartCardReader::CSmartCardReader(WDFDEVICE Device) : + m_Device(Device), + m_pSmartCard(nullptr), + m_fVirtualPowered(FALSE), + m_pCurrentClient(nullptr) +{ + srand((unsigned) GetTickCount()); + InitializeCriticalSection(&m_SmartCardLock); +} + +CSmartCardReader::~CSmartCardReader() +{ + DeleteCriticalSection(&m_SmartCardLock); +} + +NTSTATUS CSmartCardReader::Initialize() +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + WDF_IO_QUEUE_CONFIG QueueConfig; + + WDF_IO_QUEUE_CONFIG_INIT(&QueueConfig, WdfIoQueueDispatchManual); + QueueConfig.PowerManaged = WdfFalse; + + Status = WdfIoQueueCreate( + m_Device, + &QueueConfig, + WDF_NO_OBJECT_ATTRIBUTES, + &m_PresentQueue); + + if (NT_SUCCESS(Status)) { + WDF_IO_QUEUE_CONFIG_INIT(&QueueConfig, WdfIoQueueDispatchManual); + QueueConfig.PowerManaged = WdfFalse; + + Status = WdfIoQueueCreate( + m_Device, + &QueueConfig, + WDF_NO_OBJECT_ATTRIBUTES, + &m_AbsentQueue); + } + + MethodReturn(Status, "Status = %!STATUS!", Status); +} + +VOID CSmartCardReader::Deinitialize() +{ + MethodEntry("..."); + + // + // Queue objects are parented to device by default so they + // are automatically deleted by framework when parent is deleted + // + + SAFE_DELETE(m_pSmartCard); + + MethodReturnVoid(); +} + +NTSTATUS CSmartCardReader::AddClient(CFileObject *pFileObject) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + + if (InterlockedCompareExchangePointer((void**)&m_pCurrentClient, pFileObject, nullptr) != nullptr) { + Status = STATUS_ACCESS_DENIED; + goto Exit; + } + +Exit: + MethodReturn(Status, "Status = %!STATUS!", Status); +} + +VOID CSmartCardReader::RemoveClient(CFileObject *pFileObject) +{ + MethodEntry("..."); + + InterlockedCompareExchangePointer((void**)&m_pCurrentClient, nullptr, pFileObject); + + MethodReturnVoid(); +} + +NTSTATUS +CSmartCardReader::GetAttribute( + _In_ DWORD dwAttributeId, + _Out_writes_bytes_(*pcbAttributeValue) PBYTE pbAttributeValue, + _Inout_ LPDWORD pcbAttributeValue + ) +{ + MethodEntry("Attribute Id = 0x%x", dwAttributeId); + + static const CHAR SCReaderVendorName[] = "Microsoft"; + static const CHAR SCReaderVendorIfd[] = "IFD"; + static const DWORD SCReaderVendorIfdVersion = 0x01000010; //1.0.0.1 + static const DWORD SCReaderChannelId = SCARD_READER_TYPE_NFC << 16; + static const DWORD SCReaderProtocolTypes = SCARD_PROTOCOL_T1; + static const DWORD SCReaderDeviceUnit = 0; + static const DWORD SCReaderDefaultClk = 13560; // Default ICC CLK frequency + static const DWORD SCReaderMaxClk = 13560; // Max supported ICC CLK + static const DWORD SCReaderDefaultDataRate = 1; // ICC IO data rate + static const DWORD SCReaderMaxDataRate = 1; + static const DWORD SCReaderMaxIfsd = 254; // Max IFSD supported + static const DWORD SCReaderCharacteristics = SCARD_READER_CONTACTLESS; + static const DWORD SCReaderCurrentProtocolType = SCARD_PROTOCOL_T1; + static const DWORD SCReaderCurrentClk = 13560; // Current ICC CLK frequency in KHz + static const DWORD SCReaderCurrentD = 1; + static const DWORD SCReaderCurrentIfsc = 32; // Valid if current protocol is T=1 + static const DWORD SCReaderCurrentIfsd = 254; // Valid if current protocol is T=1 + static const DWORD SCReaderCurrentBwt = 4; // Valid if current protocol is T=1 + + const DISPATCH_ENTRY DispatchTable[] = + { + { SCARD_ATTR_VENDOR_NAME, (PBYTE)SCReaderVendorName, sizeof(SCReaderVendorName), &CSmartCardReader::GetAttributeGeneric }, + { SCARD_ATTR_VENDOR_IFD_TYPE, (PBYTE)SCReaderVendorIfd, sizeof(SCReaderVendorIfd), &CSmartCardReader::GetAttributeGeneric }, + { SCARD_ATTR_VENDOR_IFD_VERSION, (PBYTE)&SCReaderVendorIfdVersion, sizeof(SCReaderVendorIfdVersion), &CSmartCardReader::GetAttributeGeneric }, + { SCARD_ATTR_CHANNEL_ID, (PBYTE)&SCReaderChannelId, sizeof(SCReaderChannelId), &CSmartCardReader::GetAttributeGeneric }, + { SCARD_ATTR_PROTOCOL_TYPES, (PBYTE)&SCReaderProtocolTypes, sizeof(SCReaderProtocolTypes), &CSmartCardReader::GetAttributeGeneric }, + { SCARD_ATTR_DEVICE_UNIT, (PBYTE)&SCReaderDeviceUnit, sizeof(SCReaderDeviceUnit), &CSmartCardReader::GetAttributeGeneric }, + { SCARD_ATTR_DEFAULT_CLK, (PBYTE)&SCReaderDefaultClk, sizeof(SCReaderDefaultClk), &CSmartCardReader::GetAttributeGeneric }, + { SCARD_ATTR_MAX_CLK, (PBYTE)&SCReaderMaxClk, sizeof(SCReaderMaxClk), &CSmartCardReader::GetAttributeGeneric }, + { SCARD_ATTR_DEFAULT_DATA_RATE, (PBYTE)&SCReaderDefaultDataRate, sizeof(SCReaderDefaultDataRate), &CSmartCardReader::GetAttributeGeneric }, + { SCARD_ATTR_MAX_DATA_RATE, (PBYTE)&SCReaderMaxDataRate, sizeof(SCReaderMaxDataRate), &CSmartCardReader::GetAttributeGeneric }, + { SCARD_ATTR_MAX_IFSD, (PBYTE)&SCReaderMaxIfsd, sizeof(SCReaderMaxIfsd), &CSmartCardReader::GetAttributeGeneric }, + { SCARD_ATTR_CHARACTERISTICS, (PBYTE)&SCReaderCharacteristics, sizeof(SCReaderCharacteristics), &CSmartCardReader::GetAttributeGeneric }, + { SCARD_ATTR_CURRENT_PROTOCOL_TYPE, (PBYTE)&SCReaderCurrentProtocolType,sizeof(SCReaderCurrentProtocolType), &CSmartCardReader::GetCurrentProtocolType }, + { SCARD_ATTR_CURRENT_CLK, (PBYTE)&SCReaderCurrentClk, sizeof(SCReaderCurrentClk), &CSmartCardReader::GetAttributeGeneric }, + { SCARD_ATTR_CURRENT_D, (PBYTE)&SCReaderCurrentD, sizeof(SCReaderCurrentD), &CSmartCardReader::GetAttributeGeneric }, + { SCARD_ATTR_CURRENT_IFSC, (PBYTE)&SCReaderCurrentIfsc, sizeof(SCReaderCurrentIfsc), &CSmartCardReader::GetAttributeGeneric }, + { SCARD_ATTR_CURRENT_IFSD, (PBYTE)&SCReaderCurrentIfsd, sizeof(SCReaderCurrentIfsd), &CSmartCardReader::GetAttributeGeneric }, + { SCARD_ATTR_CURRENT_BWT, (PBYTE)&SCReaderCurrentBwt, sizeof(SCReaderCurrentBwt), &CSmartCardReader::GetAttributeGeneric }, + { SCARD_ATTR_ICC_PRESENCE, nullptr, 0, &CSmartCardReader::GetIccPresence }, + { SCARD_ATTR_ATR_STRING, nullptr, 0, &CSmartCardReader::GetAtrString }, + { SCARD_ATTR_ICC_TYPE_PER_ATR, nullptr, 0, &CSmartCardReader::GetIccTypePerAtr }, + }; + + NTSTATUS Status = STATUS_NOT_SUPPORTED; + + for (DWORD TableEntry = 0; TableEntry < _countof(DispatchTable); TableEntry++) { + if (DispatchTable[TableEntry].dwAttributeId == dwAttributeId) { + Status = (this->*DispatchTable[TableEntry].pfnDispatchHandler)(DispatchTable[TableEntry].pbAttributeValue, + (DWORD)DispatchTable[TableEntry].cbAttributeValue, + pbAttributeValue, + pcbAttributeValue); + break; + } + } + + MethodReturn(Status, "Status = %!STATUS!", Status); +} + +NTSTATUS CSmartCardReader::SetAttribute(_In_ DWORD dwAttributeId) +{ + return (dwAttributeId == SCARD_ATTR_DEVICE_IN_USE ? STATUS_SUCCESS : STATUS_NOT_SUPPORTED); +} + +NTSTATUS CSmartCardReader::SetPower(_In_ DWORD dwPower) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + + EnterCriticalSection(&m_SmartCardLock); + + if (m_pSmartCard == nullptr) { + Status = STATUS_NO_MEDIA; + goto Exit; + } + + switch (dwPower) + { + case SCARD_COLD_RESET: + case SCARD_WARM_RESET: + m_fVirtualPowered = TRUE; + break; + + case SCARD_POWER_DOWN: + m_fVirtualPowered = FALSE; + break; + + default: + Status = STATUS_INVALID_PARAMETER; + break; + } + +Exit: + LeaveCriticalSection(&m_SmartCardLock); + MethodReturn(Status, "Status = %!STATUS!", Status); +} + +NTSTATUS CSmartCardReader::SetProtocol(_In_ DWORD dwProtocol, _Out_ DWORD *pdwSelectedProtocol) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + + if (m_pSmartCard == nullptr) { + Status = STATUS_NO_MEDIA; + goto Exit; + } + + if (((dwProtocol & SCARD_PROTOCOL_DEFAULT) != 0) || + ((dwProtocol & SCARD_PROTOCOL_T1) != 0) ) { + *pdwSelectedProtocol = SCARD_PROTOCOL_T1; + } + else if (((dwProtocol & SCARD_PROTOCOL_RAW) != 0) || + ((dwProtocol & SCARD_PROTOCOL_T0) != 0) || + ((dwProtocol & SCARD_PROTOCOL_Tx) != 0) ) { + Status = STATUS_NOT_SUPPORTED; + } + else if (dwProtocol == SCARD_PROTOCOL_OPTIMAL) { + *pdwSelectedProtocol = SCARD_PROTOCOL_T1; + } + else { + Status = STATUS_INVALID_DEVICE_REQUEST; + } + +Exit: + MethodReturn(Status, "Status = %!STATUS!", Status); +} + +NTSTATUS CSmartCardReader::IsAbsent(_In_ WDFREQUEST Request) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + WDFREQUEST OutRequest = nullptr; + BOOL fCompleteRequest = TRUE; + + if (m_pSmartCard == nullptr) { + Status = STATUS_SUCCESS; + goto Exit; + } + + if (NT_SUCCESS(WdfIoQueueFindRequest( + m_AbsentQueue, + nullptr, + WdfRequestGetFileObject(Request), + nullptr, + &OutRequest))) { + WdfObjectDereference(OutRequest); + Status = STATUS_DEVICE_BUSY; + goto Exit; + } + + Status = WdfRequestForwardToIoQueue(Request, m_AbsentQueue); + fCompleteRequest = !NT_SUCCESS(Status); + +Exit: + MethodReturn(fCompleteRequest ? Status : STATUS_PENDING, "Status = %!STATUS!", Status); +} + +NTSTATUS CSmartCardReader::IsPresent(_In_ WDFREQUEST Request) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + WDFREQUEST OutRequest = nullptr; + BOOL fCompleteRequest = TRUE; + + if (m_pSmartCard != nullptr) { + Status = STATUS_SUCCESS; + goto Exit; + } + + if (NT_SUCCESS(WdfIoQueueFindRequest( + m_PresentQueue, + nullptr, + WdfRequestGetFileObject(Request), + nullptr, + &OutRequest))) { + WdfObjectDereference(OutRequest); + Status = STATUS_DEVICE_BUSY; + goto Exit; + } + + Status = WdfRequestForwardToIoQueue(Request, m_PresentQueue); + fCompleteRequest = !NT_SUCCESS(Status); + +Exit: + MethodReturn(fCompleteRequest ? Status : STATUS_PENDING, "Status = %!STATUS!", Status); +} + +NTSTATUS +CSmartCardReader::Transmit( + _In_reads_bytes_opt_(InputBufferLength) PBYTE InputBuffer, + _In_ size_t InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PBYTE OutputBuffer, + _In_ size_t OutputBufferLength, + _In_ size_t* BytesTransferred + ) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + SCARD_IO_REQUEST *InputRequest = (SCARD_IO_REQUEST*)InputBuffer; + SCARD_IO_REQUEST *OutputRequest = (SCARD_IO_REQUEST*)OutputBuffer; + + EnterCriticalSection(&m_SmartCardLock); + + if (m_pSmartCard == nullptr) { + Status = STATUS_NO_MEDIA; + goto Exit; + } + + if (InputRequest->dwProtocol != SCARD_PROTOCOL_T1) { + Status = STATUS_INVALID_DEVICE_STATE; + goto Exit; + } + + Status = m_pSmartCard->SendCommand( + InputBuffer + sizeof(SCARD_IO_REQUEST), + InputBufferLength - sizeof(SCARD_IO_REQUEST), + OutputBuffer + sizeof(SCARD_IO_REQUEST), + OutputBufferLength - sizeof(SCARD_IO_REQUEST), + BytesTransferred); + + if (NT_SUCCESS(Status)) { + // Store the header information (SCARD_IO_REQUEST) in the output buffer + OutputRequest->dwProtocol = SCARD_PROTOCOL_T1; + OutputRequest->cbPciLength = sizeof(SCARD_IO_REQUEST); + *BytesTransferred += sizeof(SCARD_IO_REQUEST); + } + +Exit: + LeaveCriticalSection(&m_SmartCardLock); + MethodReturn(Status, "Status = %!STATUS!", Status); +} + +DWORD CSmartCardReader::GetState() +{ + if (m_pSmartCard != nullptr) { + return (m_fVirtualPowered ? SCARD_SPECIFIC : SCARD_SWALLOWED); + } + + return SCARD_ABSENT; +} + +BOOL CSmartCardReader::MessageReceived(_In_ MESSAGE* pMessage) +{ + MethodEntry("..."); + + BOOL fHandled = FALSE; + + if (m_pSmartCard != nullptr) { + fHandled = m_pSmartCard->ResponseReceived(pMessage); + } + + MethodReturnBool(fHandled); +} + +BOOL CSmartCardReader::CardArrived(_In_ CConnection* pConnection) +{ + MethodEntry("void"); + + BOOL fTakeConnection = FALSE; + WDFREQUEST Request = nullptr; + + EnterCriticalSection(&m_SmartCardLock); + + m_fVirtualPowered = TRUE; + + if (m_pSmartCard == nullptr) { + m_pSmartCard = new CSmartCard(m_Device, pConnection); + + if (m_pSmartCard != nullptr) { + fTakeConnection = TRUE; + + while (NT_SUCCESS(WdfIoQueueRetrieveNextRequest(m_PresentQueue, &Request))) { + TraceInfo("%!FUNC! Completing Request with Status %!STATUS!", STATUS_SUCCESS); + WdfRequestComplete(Request, STATUS_SUCCESS); + } + } + } + + LeaveCriticalSection(&m_SmartCardLock); + + MethodReturnBool(fTakeConnection); +} + +BOOL CSmartCardReader::CardRemoved(_In_ CConnection* pConnection) +{ + MethodEntry("void"); + + UNREFERENCED_PARAMETER(pConnection); + + BOOL fConnectionDeleted = FALSE; + WDFREQUEST Request = nullptr; + + EnterCriticalSection(&m_SmartCardLock); + + m_fVirtualPowered = FALSE; + + if (m_pSmartCard != nullptr) { + SAFE_DELETE(m_pSmartCard); + + while (NT_SUCCESS(WdfIoQueueRetrieveNextRequest(m_AbsentQueue, &Request))) { + TraceInfo("%!FUNC! Completing Request with Status %!STATUS!", STATUS_SUCCESS); + WdfRequestComplete(Request, STATUS_SUCCESS); + } + + fConnectionDeleted = TRUE; + } + + LeaveCriticalSection(&m_SmartCardLock); + + MethodReturnBool(fConnectionDeleted); +} + +FORCEINLINE NTSTATUS +CopyToBuffer( + _In_reads_bytes_(cbAttributeValue) const VOID *pbAttributeValue, + _In_ DWORD cbAttributeValue, + _Out_writes_bytes_(*pcbOutputBuffer) PBYTE pbOutputBuffer, + _Inout_ LPDWORD pcbOutputBuffer + ) +{ + NTSTATUS Status = STATUS_SUCCESS; + + if (*pcbOutputBuffer < cbAttributeValue) { + Status = STATUS_BUFFER_TOO_SMALL; + goto Exit; + } + + RtlCopyMemory(pbOutputBuffer, pbAttributeValue, cbAttributeValue); + *pcbOutputBuffer = cbAttributeValue; + +Exit: + return Status; +} + +NTSTATUS +CSmartCardReader::GetAttributeGeneric( + _In_opt_bytecount_(cbAttributeValue) PBYTE pbAttributeValue, + _In_ DWORD cbAttributeValue, + _Out_bytecap_(*pcbOutputBuffer) PBYTE pbOutputBuffer, + _Inout_ LPDWORD pcbOutputBuffer + ) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + + Status = CopyToBuffer(pbAttributeValue, cbAttributeValue, pbOutputBuffer, pcbOutputBuffer); + + MethodReturn(Status, "Status = %!STATUS!", Status); +} + +NTSTATUS +CSmartCardReader::GetCurrentProtocolType( + _In_opt_bytecount_(cbCurrentProtocolType) PBYTE pbCurrentProtocolType, + _In_ DWORD cbCurrentProtocolType, + _Out_bytecap_(*pcbOutputBuffer) PBYTE pbOutputBuffer, + _Inout_ LPDWORD pcbOutputBuffer + ) +{ + MethodEntry("..."); + + NTSTATUS Status = STATUS_SUCCESS; + + if (m_pSmartCard == nullptr) { + Status = STATUS_INVALID_DEVICE_STATE; + goto Exit; + } + + Status = CopyToBuffer(pbCurrentProtocolType, cbCurrentProtocolType, pbOutputBuffer, pcbOutputBuffer); + +Exit: + MethodReturn(Status, "Status = %!STATUS!", Status); +} + +NTSTATUS +CSmartCardReader::GetAtrString( + _In_opt_bytecount_(cbAttributeValue) PBYTE pbAttributeValue, + _In_ DWORD cbAttributeValue, + _Out_bytecap_(*pcbOutputBuffer) PBYTE pbOutputBuffer, + _Inout_ LPDWORD pcbOutputBuffer + ) +{ + MethodEntry("..."); + + UNREFERENCED_PARAMETER(pbAttributeValue); + UNREFERENCED_PARAMETER(cbAttributeValue); + + NTSTATUS Status = STATUS_SUCCESS; + + if (m_pSmartCard == nullptr) { + Status = STATUS_INVALID_DEVICE_STATE; + goto Exit; + } + + Status = m_pSmartCard->GetAtr(pbOutputBuffer, pcbOutputBuffer); + +Exit: + MethodReturn(Status, "Status = %!STATUS!", Status); +} + +NTSTATUS +CSmartCardReader::GetIccTypePerAtr( + _In_opt_bytecount_(cbAttributeValue) PBYTE pbAttributeValue, + _In_ DWORD cbAttributeValue, + _Out_bytecap_(*pcbOutputBuffer) PBYTE pbOutputBuffer, + _Inout_ LPDWORD pcbOutputBuffer + ) +{ + MethodEntry("..."); + + UNREFERENCED_PARAMETER(pbAttributeValue); + UNREFERENCED_PARAMETER(cbAttributeValue); + + NTSTATUS Status = STATUS_SUCCESS; + + if (m_pSmartCard == nullptr) { + Status = STATUS_INVALID_DEVICE_STATE; + goto Exit; + } + + Status = m_pSmartCard->GetIccType(pbOutputBuffer, pcbOutputBuffer); + +Exit: + MethodReturn(Status, "Status = %!STATUS!", Status); +} + +NTSTATUS +CSmartCardReader::GetIccPresence( + _In_opt_bytecount_(cbAttributeValue) PBYTE pbAttributeValue, + _In_ DWORD cbAttributeValue, + _Out_bytecap_(*pcbOutputBuffer) PBYTE pbOutputBuffer, + _Inout_ LPDWORD pcbOutputBuffer + ) +{ + MethodEntry("..."); + + UNREFERENCED_PARAMETER(pbAttributeValue); + UNREFERENCED_PARAMETER(cbAttributeValue); + + NTSTATUS Status = STATUS_SUCCESS; + BYTE IccPresence = (m_pSmartCard == nullptr ? 0x0 : 0x1); + + Status = CopyToBuffer(&IccPresence, sizeof(IccPresence), pbOutputBuffer, pcbOutputBuffer); + + MethodReturn(Status, "Status = %!STATUS!", Status); +} diff --git a/nfc/Simulator/Src/SmartCardReader.h b/nfc/Simulator/Src/SmartCardReader.h new file mode 100644 index 000000000..fa0cf06ff --- /dev/null +++ b/nfc/Simulator/Src/SmartCardReader.h @@ -0,0 +1,87 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + smartcardreader.h + +Abstract: + + The SmartCardReader encapsulate all the smart card related operations. + +Environment: + + User Mode + +--*/ +#pragma once + +class CSmartCardReader +{ +public: + CSmartCardReader(WDFDEVICE Device); + ~CSmartCardReader(); + +public: + NTSTATUS Initialize(); + VOID Deinitialize(); + NTSTATUS AddClient(CFileObject *pFileObject); + VOID RemoveClient(CFileObject *pFileObject); + + NTSTATUS GetAttribute(_In_ DWORD dwAttributeId, _Out_writes_bytes_(*pcbAttributeValue) PBYTE pbAttributeValue, _Inout_ LPDWORD pcbAttributeValue); + NTSTATUS SetAttribute(_In_ DWORD dwAttributeId); + NTSTATUS IsAbsent(_In_ WDFREQUEST Request); + NTSTATUS IsPresent(_In_ WDFREQUEST Request); + NTSTATUS SetPower(_In_ DWORD dwPower); + NTSTATUS SetProtocol(_In_ DWORD dwProtocol, _Out_ DWORD *pdwSelectedProtocol); + + NTSTATUS Transmit( + _In_reads_bytes_opt_(InputBufferLength) PBYTE InputBuffer, + _In_ size_t InputBufferLength, + _Out_writes_bytes_opt_(OutputBufferLength) PBYTE OutputBuffer, + _In_ size_t OutputBufferLength, + _In_ size_t* BytesTransferred); + + DWORD GetState(); + BOOL CardArrived(_In_ CConnection* pConnection); + BOOL CardRemoved(_In_ CConnection* pConnection); + BOOL MessageReceived(_In_ MESSAGE* pMessage); + +private: + typedef NTSTATUS + (CSmartCardReader::EVT_DISPATCH_HANDLER) ( + _In_opt_bytecount_(cbAttributeValue) PBYTE pbAttributeValue, + _In_ DWORD cbAttributeValue, + _Out_bytecap_(*pcbOutputBuffer) PBYTE pbOutputBuffer, + _Inout_ LPDWORD pcbOutputBuffer); + + typedef NTSTATUS + (CSmartCardReader::*PFN_DISPATCH_HANDLER) ( + _In_opt_bytecount_(cbAttributeValue) PBYTE pbAttributeValue, + _In_ DWORD cbAttributeValue, + _Out_bytecap_(*pcbOutputBuffer) PBYTE pbOutputBuffer, + _Inout_ LPDWORD pcbOutputBuffer); + + EVT_DISPATCH_HANDLER GetAttributeGeneric; + EVT_DISPATCH_HANDLER GetCurrentProtocolType; + EVT_DISPATCH_HANDLER GetIccPresence; + EVT_DISPATCH_HANDLER GetAtrString; + EVT_DISPATCH_HANDLER GetIccTypePerAtr; + +private: + typedef struct _DISPATCH_ENTRY { + DWORD dwAttributeId; + PBYTE pbAttributeValue; + size_t cbAttributeValue; + PFN_DISPATCH_HANDLER pfnDispatchHandler; + } DISPATCH_ENTRY, *PDISPATCH_ENTRY; + + WDFDEVICE m_Device; + WDFQUEUE m_PresentQueue; + WDFQUEUE m_AbsentQueue; + CSmartCard* m_pSmartCard; + CRITICAL_SECTION m_SmartCardLock; + BOOL m_fVirtualPowered; + CFileObject* m_pCurrentClient; +}; diff --git a/nfc/Simulator/Src/SocketListener.cpp b/nfc/Simulator/Src/SocketListener.cpp new file mode 100644 index 000000000..1276c28d4 --- /dev/null +++ b/nfc/Simulator/Src/SocketListener.cpp @@ -0,0 +1,185 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + socketlistener.cpp + +Abstract: + + Implements a socket listener class + +Environment: + + User-mode only. + +--*/ + +#include "Internal.h" +#include "SocketListener.tmh" + +HRESULT SetSocketIpv6Only(_In_ SOCKET socket, _In_ BOOL Ipv6Only) +{ + FunctionEntry("..."); + + HRESULT hr = S_OK; + + if (setsockopt(socket, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&Ipv6Only, sizeof(Ipv6Only)) == SOCKET_ERROR) { + hr = HRESULT_FROM_WIN32(WSAGetLastError()); + } + + FunctionReturnHR(hr); +} + +HRESULT CSocketListener::EnableAccepting() +{ + MethodEntry("..."); + + HRESULT hr = S_OK; + + if (_ThreadpoolIo == nullptr) { + _ThreadpoolIo = CreateThreadpoolIo((HANDLE)_ListenSocket, s_AcceptThreadProc, this, nullptr); + + if (_ThreadpoolIo == nullptr) { + hr = HRESULT_FROM_WIN32(GetLastError()); + } + + if (SUCCEEDED(hr)) { + if (listen(_ListenSocket, 2) == SOCKET_ERROR) { + hr = HRESULT_FROM_WIN32(WSAGetLastError()); + } + } + + if (SUCCEEDED(hr)) { + hr = BeginAccept(); + + if (hr == HRESULT_FROM_WIN32(ERROR_IO_PENDING)) { + hr = S_OK; + } + } + } + + MethodReturnHR(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, nullptr); + + if (threadpoolIo != nullptr) { + // Don't wait for threadpool callbacks when this thread is actually the threadpool callback + if (_ThreadpoolThreadId != GetCurrentThreadId()) { + WaitForThreadpoolIoCallbacks(threadpoolIo, false); + } + CloseThreadpoolIo(threadpoolIo); + } + + if (_ClientSocket != INVALID_SOCKET) { + closesocket(_ClientSocket); + _ClientSocket = INVALID_SOCKET; + } + + MethodReturnVoid(); +} + +HRESULT CSocketListener::BeginAccept() +{ + MethodEntry("void"); + + HRESULT hr = S_OK; + ULONG_PTR cbReceived = 0; + + _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 != nullptr) { + StartThreadpoolIo(threadpoolIo); + 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)) { + 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)) { + _pValidator->ValidateAccept(_ClientSocket, &_AcceptBuffer.MagicPacket); + } + else { + closesocket(_ClientSocket); + } + + _ClientSocket = INVALID_SOCKET; + } + + BeginAccept(); + + MethodReturnVoid(); +} + +HRESULT CSocketListener::Bind() +{ + MethodEntry("void"); + + HRESULT hr = S_OK; + + if (SUCCEEDED(hr)) { + _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)) { + if (bind(_ListenSocket, (const sockaddr*)&_ListenAddress, (int)sizeof(_ListenAddress)) == SOCKET_ERROR) { + hr = HRESULT_FROM_WIN32(WSAGetLastError()); + } + } + } + + MethodReturnHR(hr); +} \ No newline at end of file diff --git a/nfc/Simulator/Src/SocketListener.h b/nfc/Simulator/Src/SocketListener.h new file mode 100644 index 000000000..dfbec3692 --- /dev/null +++ b/nfc/Simulator/Src/SocketListener.h @@ -0,0 +1,85 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Abstract: + + Declares a socket listener class + +Environment: + + User-mode only. + +--*/ +#pragma once + +#define NFC_DRIVER_SIM_LISTEN_PORT 9299 + +struct ACCEPT_BUFFER +{ + GUID MagicPacket; + SOCKADDR_STORAGE DestAddress; + SOCKADDR_STORAGE SourceAddress; +}; + +class IValidateAccept +{ +public: + virtual void ValidateAccept(_In_ SOCKET Socket, _In_ GUID* pMagicPacket) = 0; +}; + +class CSocketListener +{ +public: + CSocketListener(_In_ IValidateAccept* pValidator) : + _pValidator(pValidator), + _ThreadpoolIo(nullptr), + _ListenSocket(INVALID_SOCKET), + _ClientSocket(INVALID_SOCKET) + { + ZeroMemory(&_Overlapped, sizeof(_Overlapped)); + + ZeroMemory(&_ListenAddress, sizeof(_ListenAddress)); + _ListenAddress.sin6_family = AF_INET6; + _ListenAddress.sin6_port = htons(NFC_DRIVER_SIM_LISTEN_PORT); + } + + ~CSocketListener() + { + StopAccepting(); + _pValidator = nullptr; + } + +public: + HRESULT Bind(); + HRESULT EnableAccepting(); + 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: + sockaddr_in6 _ListenAddress; + ACCEPT_BUFFER _AcceptBuffer; + OVERLAPPED _Overlapped; + volatile PTP_IO _ThreadpoolIo; + DWORD _ThreadpoolThreadId; + IValidateAccept* _pValidator; + SOCKET _ListenSocket; + SOCKET _ClientSocket; +}; diff --git a/nfc/Simulator/Src/WppDefs.h b/nfc/Simulator/Src/WppDefs.h new file mode 100644 index 000000000..b102c4615 --- /dev/null +++ b/nfc/Simulator/Src/WppDefs.h @@ -0,0 +1,367 @@ +/*++ + +Copyright (c) Microsoft Corporation. All Rights Reserved + +Abstract: + + WPP tracing macro definitions. + +--*/ + +#define WPP_CONTROL_GUIDS \ + WPP_DEFINE_CONTROL_GUID(NFC, 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 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 false; + } + } + return true; +} + +// +// To be used only within 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) + { + 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 destruction +// +template +class AutoRestoredValue +{ +public: + __forceinline AutoRestoredValue(T* pVar, T newVal) + : _pVar(pVar) + , _oldVal() + { + 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, nullptr); + } + } + } + + __forceinline void Attach(Pointee* pCtx, DWORD dwSlotIndex) + { + _dwSlotIndex = dwSlotIndex; + TlsSetValue(_dwSlotIndex, pCtx); + } + +private: + DWORD _dwSlotIndex; +}; + +} // namespace TracingInternal + +// +// 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 nullptr 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 == nullptr) { \ + __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) ;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) ;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) ;return __pRet; \ + } + +// 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) ;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) ;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) ;return __pRet; \ + } + +// MACRO: TraceASSERT +// +// 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) ;NT_ASSERT(FALSE); \ + } + +//MACRO: TraceErrorHR +// +// 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) diff --git a/nfp/net/driver/NetNfpProvider.vcxproj b/nfp/net/driver/NetNfpProvider.vcxproj index 76f4232d1..d997ba267 100644 --- a/nfp/net/driver/NetNfpProvider.vcxproj +++ b/nfp/net/driver/NetNfpProvider.vcxproj @@ -19,12 +19,12 @@ - {6765F4B0-264E-4EA6-9EAE-CE43C3BE04DF} + {16A16D6D-A997-45E1-AE41-A75FF2F9A731} $(MSBuildProjectName) 1 Debug Win32 - {0884BE29-DDC3-4627-8B57-3997978EE1F4} + {C4F04FA5-7374-4984-8C83-66B22E99198A} @@ -251,7 +251,6 @@ - diff --git a/nfp/net/driver/NetNfpProvider.vcxproj.Filters b/nfp/net/driver/NetNfpProvider.vcxproj.Filters index e24f17e89..883ae71dc 100644 --- a/nfp/net/driver/NetNfpProvider.vcxproj.Filters +++ b/nfp/net/driver/NetNfpProvider.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {42E23DE8-32D3-4399-8E2F-4287E410E541} + {78477906-B665-46AF-BEE1-F72F97EFA6AB} h;hpp;hxx;hm;inl;inc;xsd - {A4E94E74-991C-42D2-9F4D-67AD587F6D11} + {73FBF93D-9555-45E9-A7ED-65370C0B708F} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {AE74648C-BE93-41BD-B6F8-BCE184684B75} + {5F848688-EEA7-4A9B-B7FB-A34954B9773B} inf;inv;inx;mof;mc; - {873765C6-CA93-4C7A-87AF-E0B883B4EBB2} + {8E34281F-9746-4660-BC7E-032C10E99B84} @@ -45,9 +45,6 @@ - - Driver Files - Driver Files diff --git a/nfp/net/driver/netnfpprovider.inx b/nfp/net/driver/netnfpprovider.inx index 0f2d246fa..23f1e3374 100644 --- a/nfp/net/driver/netnfpprovider.inx +++ b/nfp/net/driver/netnfpprovider.inx @@ -6,14 +6,14 @@ Signature="$WINDOWS NT$" Class=Proximity ClassGuid={5630831C-06C9-4856-B327-F5D32586E060} -Provider=%MSFT% +Provider=%ProviderName% CatalogFile=nfp.cat DriverVer=03/20/2003,5.00.3788 [Manufacturer] -%MSFTWUDF%=Microsoft,NT$ARCH$ +%ManufacturerName%=Standard,NT$ARCH$ -[Microsoft.NT$ARCH$] +[Standard.NT$ARCH$] %NetNfpProviderName%=NetNfpProvider_Install,WUDF\NetNfpProvider [SourceDisksFiles] @@ -74,10 +74,7 @@ NetNfpProvider.dll ; =================== Generic ================================== [Strings] -MSFT="Microsoft" -MSFTWUDF="Microsoft Windows Driver Kit Sample (Proximity)" -MediaDescription="Microsoft Network NearFieldProximity Provider Installation Media" +ProviderName="TODO-Set-Provider" +ManufacturerName="TODO-Set-Manufacturer" +MediaDescription="Network NearFieldProximity Provider Installation Media" NetNfpProviderName="Network NearFieldProximity Provider" - - - diff --git a/nfp/net/exe/NetNfpControl.vcxproj b/nfp/net/exe/NetNfpControl.vcxproj index 404f1b5a6..7c559cb8b 100644 --- a/nfp/net/exe/NetNfpControl.vcxproj +++ b/nfp/net/exe/NetNfpControl.vcxproj @@ -19,11 +19,11 @@ - {D40F7042-2180-40DF-A634-104BFB614097} + {E0504022-859C-4FAC-B559-42C5B5169FF4} $(MSBuildProjectName) Debug Win32 - {9F1521A4-16AA-4263-84BB-5026569D1020} + {5BEB11A1-BA4E-4F47-81C4-7DF470CB7551} @@ -248,7 +248,6 @@ - diff --git a/nfp/net/exe/NetNfpControl.vcxproj.Filters b/nfp/net/exe/NetNfpControl.vcxproj.Filters index c75f7dcf7..213c18d5d 100644 --- a/nfp/net/exe/NetNfpControl.vcxproj.Filters +++ b/nfp/net/exe/NetNfpControl.vcxproj.Filters @@ -3,15 +3,15 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {9C04ED10-5BA4-42E5-B5D5-4BD50217EBAE} + {01F54115-43C0-470E-9733-713A3DA6D227} h;hpp;hxx;hm;inl;inc;xsd - {53BD2E40-1C93-498E-8D59-323CB3D066BA} + {5818869A-EFFB-426B-B07A-62E3BB2C52E8} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {E9074112-F156-4968-A49A-5C281DDB3F60} + {FB99E1D5-4F14-4AC6-AD99-1C7671732649} diff --git a/nfp/net/netnfp.sln b/nfp/net/netnfp.sln index aabb3bbf4..36baed7a2 100644 --- a/nfp/net/netnfp.sln +++ b/nfp/net/netnfp.sln @@ -3,13 +3,13 @@ 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", "{D2BA67B7-002A-468C-A45C-ABC347AB834D}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Driver", "Driver", "{F924DB5A-7B7A-4820-9655-EA130F950DDD}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Exe", "Exe", "{439733D7-5A78-4FF7-9985-FFB40E84BAC2}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Exe", "Exe", "{F1ED8451-410E-44B1-B022-9A0D953267B6}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NetNfpProvider", "driver\NetNfpProvider.vcxproj", "{6765F4B0-264E-4EA6-9EAE-CE43C3BE04DF}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NetNfpProvider", "driver\NetNfpProvider.vcxproj", "{16A16D6D-A997-45E1-AE41-A75FF2F9A731}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NetNfpControl", "exe\NetNfpControl.vcxproj", "{D40F7042-2180-40DF-A634-104BFB614097}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NetNfpControl", "exe\NetNfpControl.vcxproj", "{C7F26FB2-DD29-465A-9C48-1165150B6E65}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -19,28 +19,28 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {6765F4B0-264E-4EA6-9EAE-CE43C3BE04DF}.Debug|Win32.ActiveCfg = Debug|Win32 - {6765F4B0-264E-4EA6-9EAE-CE43C3BE04DF}.Debug|Win32.Build.0 = Debug|Win32 - {6765F4B0-264E-4EA6-9EAE-CE43C3BE04DF}.Release|Win32.ActiveCfg = Release|Win32 - {6765F4B0-264E-4EA6-9EAE-CE43C3BE04DF}.Release|Win32.Build.0 = Release|Win32 - {6765F4B0-264E-4EA6-9EAE-CE43C3BE04DF}.Debug|x64.ActiveCfg = Debug|x64 - {6765F4B0-264E-4EA6-9EAE-CE43C3BE04DF}.Debug|x64.Build.0 = Debug|x64 - {6765F4B0-264E-4EA6-9EAE-CE43C3BE04DF}.Release|x64.ActiveCfg = Release|x64 - {6765F4B0-264E-4EA6-9EAE-CE43C3BE04DF}.Release|x64.Build.0 = Release|x64 - {D40F7042-2180-40DF-A634-104BFB614097}.Debug|Win32.ActiveCfg = Debug|Win32 - {D40F7042-2180-40DF-A634-104BFB614097}.Debug|Win32.Build.0 = Debug|Win32 - {D40F7042-2180-40DF-A634-104BFB614097}.Release|Win32.ActiveCfg = Release|Win32 - {D40F7042-2180-40DF-A634-104BFB614097}.Release|Win32.Build.0 = Release|Win32 - {D40F7042-2180-40DF-A634-104BFB614097}.Debug|x64.ActiveCfg = Debug|x64 - {D40F7042-2180-40DF-A634-104BFB614097}.Debug|x64.Build.0 = Debug|x64 - {D40F7042-2180-40DF-A634-104BFB614097}.Release|x64.ActiveCfg = Release|x64 - {D40F7042-2180-40DF-A634-104BFB614097}.Release|x64.Build.0 = Release|x64 + {16A16D6D-A997-45E1-AE41-A75FF2F9A731}.Debug|Win32.ActiveCfg = Debug|Win32 + {16A16D6D-A997-45E1-AE41-A75FF2F9A731}.Debug|Win32.Build.0 = Debug|Win32 + {16A16D6D-A997-45E1-AE41-A75FF2F9A731}.Release|Win32.ActiveCfg = Release|Win32 + {16A16D6D-A997-45E1-AE41-A75FF2F9A731}.Release|Win32.Build.0 = Release|Win32 + {16A16D6D-A997-45E1-AE41-A75FF2F9A731}.Debug|x64.ActiveCfg = Debug|x64 + {16A16D6D-A997-45E1-AE41-A75FF2F9A731}.Debug|x64.Build.0 = Debug|x64 + {16A16D6D-A997-45E1-AE41-A75FF2F9A731}.Release|x64.ActiveCfg = Release|x64 + {16A16D6D-A997-45E1-AE41-A75FF2F9A731}.Release|x64.Build.0 = Release|x64 + {C7F26FB2-DD29-465A-9C48-1165150B6E65}.Debug|Win32.ActiveCfg = Debug|Win32 + {C7F26FB2-DD29-465A-9C48-1165150B6E65}.Debug|Win32.Build.0 = Debug|Win32 + {C7F26FB2-DD29-465A-9C48-1165150B6E65}.Release|Win32.ActiveCfg = Release|Win32 + {C7F26FB2-DD29-465A-9C48-1165150B6E65}.Release|Win32.Build.0 = Release|Win32 + {C7F26FB2-DD29-465A-9C48-1165150B6E65}.Debug|x64.ActiveCfg = Debug|x64 + {C7F26FB2-DD29-465A-9C48-1165150B6E65}.Debug|x64.Build.0 = Debug|x64 + {C7F26FB2-DD29-465A-9C48-1165150B6E65}.Release|x64.ActiveCfg = Release|x64 + {C7F26FB2-DD29-465A-9C48-1165150B6E65}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {6765F4B0-264E-4EA6-9EAE-CE43C3BE04DF} = {D2BA67B7-002A-468C-A45C-ABC347AB834D} - {D40F7042-2180-40DF-A634-104BFB614097} = {439733D7-5A78-4FF7-9985-FFB40E84BAC2} + {16A16D6D-A997-45E1-AE41-A75FF2F9A731} = {F924DB5A-7B7A-4820-9655-EA130F950DDD} + {C7F26FB2-DD29-465A-9C48-1165150B6E65} = {F1ED8451-410E-44B1-B022-9A0D953267B6} EndGlobalSection EndGlobal diff --git a/pofx/PEP/ReadMe.md b/pofx/PEP/ReadMe.md new file mode 100644 index 000000000..41bef9084 --- /dev/null +++ b/pofx/PEP/ReadMe.md @@ -0,0 +1,9 @@ +PEP ACPI Sample +=============== + +The Power Engine Plugin (PEP) provides interfaces for platform power management including device power management (DPM), processor power management (PPM), and, starting with Windows 10, ACPI runtime methods. This sample documents the latter: an interface which allows a PEP to implement ACPI runtime methods natively via a Windows driver rather than firmware (AML). A PEP using the ACPI interface is often called a Platform Extension. Note that this interface can be used independently or in conjunction with the DPM and/or PPM interfaces as appropriate. + +Use the PEP ACPI interface if: +* You want to write peripheral (off-SoC) device power management in C rather than in ACPI Source Language (ASL). +* You need to override an ACPI method which exists in a platform's DSDT or SSDT firmware tables. +* Shipping, maintaining, and updating a driver binary suits your platform better than firmware updates (note you'll still need FADT, MADT, DBG2, etc. in firmware - this interface is only for runtime methods). diff --git a/pofx/PEP/acpi/acpispecific.c b/pofx/PEP/acpi/acpispecific.c new file mode 100644 index 000000000..0e501ad3d --- /dev/null +++ b/pofx/PEP/acpi/acpispecific.c @@ -0,0 +1,392 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + acpispecific.c + +Abstract: + + This module defines global variables that are used by the common library + for the platform extension. + + +Environment: + + Kernel mode + +--*/ + +// +//-------------------------------------------------------------------- Includes +// + +#include "pch.h" +#include "acpispecific.h" + +#if defined(EVENT_TRACING) +#include "acpispecific.tmh" +#endif + +// +// To add new devices that will be accpeted by the platform extension, +// follow to the example below. +// + +// +// Step 1: Define a unique device type for each new device. +// (See acpispecific.h for details) +// + +// +// Step 2: Define a name for each new device. +// (See acpispecific.h for details) +// + +// +//--------------------------------------------------------------------- Globals +// + +// +// The common library will consume the PepDeviceMatchArray to determine +// whether a device should be accpeted by the platform extension. +// And then the common library will consult PepDeviceDefinitionArray for +// the native methods and device-specific notification handlers of the accpeted +// devices. +// + +// +// Step 3: Edit the PepDeviceMatchArray to tell the common library which device +// should be accepted by the platform extension. For each accepted device, +// specify which kind of notification (ACPI/DPM/PPM) the platform extension +// will handle and also specify the way platform extension should match its +// name (partial match or full match) when identifying the device. +// In this example, the sample platform extension only handles ACPI +// notifications for all devices and all devices should be matched by their +// full names. +// + +PEP_DEVICE_MATCH PepDeviceMatchArray[] = { + + {PEP_DEVICE_TYPE_ROOT, + PEP_NOTIFICATION_CLASS_ACPI, + SAMPLE_ROOT_WCHAR, + PepDeviceIdMatchFull}, + + {PEP_DEVICE_TYPE_BUSD, + PEP_NOTIFICATION_CLASS_ACPI, + SAMPLE_BUSD_ACPI_NAME_WCHAR, + PepDeviceIdMatchFull}, + + {PEP_DEVICE_TYPE_GPIO, + PEP_NOTIFICATION_CLASS_ACPI, + SAMPLE_GPIO_ACPI_NAME_WCHAR, + PepDeviceIdMatchFull}, + + {PEP_DEVICE_TYPE_SPBD, + PEP_NOTIFICATION_CLASS_ACPI, + SAMPLE_SPBD_ACPI_NAME_WCHAR, + PepDeviceIdMatchFull}, + + {PEP_DEVICE_TYPE_TST1, + PEP_NOTIFICATION_CLASS_ACPI, + SAMPLE_TST1_ACPI_NAME_WCHAR, + PepDeviceIdMatchFull}, + + {PEP_DEVICE_TYPE_TST2, + PEP_NOTIFICATION_CLASS_ACPI, + SAMPLE_TST2_ACPI_NAME_WCHAR, + PepDeviceIdMatchFull}, + + {PEP_DEVICE_TYPE_TST3, + PEP_NOTIFICATION_CLASS_ACPI, + SAMPLE_TST3_ACPI_NAME_WCHAR, + PepDeviceIdMatchFull}, + + {PEP_DEVICE_TYPE_PWRR, + PEP_NOTIFICATION_CLASS_ACPI, + SAMPLE_PWRR_ACPI_NAME_WCHAR, + PepDeviceIdMatchFull} +}; + +ULONG PepDeviceMatchArraySize = ARRAYSIZE(PepDeviceMatchArray); + +// +// Step 4: Define the list of native methods under each device. +// In this example, the platform extension natively handles: +// 1). _HID and _CID methods for \_SB.BUSD; +// 2). _HID method for \_SB.GPIO and \_SB.SPBD; +// 3). _ADR and _CRS methods for \_SB.BUSD.TST1; +// 4). _ADR, _CRS and _DEP methods for \_SB.BUSD.TST2; +// 5). _HID and _CRS methods for \_SB.TST3; +// 5). _HID, _STA, _ON, and _OFF methods for \_SB.PWRR. +// + + +PEP_OBJECT_INFORMATION RootNativeMethods[] = +{ + // + // _Child Devices + // + + {(ULONG)SAMPLE_BUSD_REAL_NAME, 0, 1, PepAcpiObjectTypeDevice}, + {(ULONG)SAMPLE_GPIO_REAL_NAME, 0, 1, PepAcpiObjectTypeDevice}, + {(ULONG)SAMPLE_SPBD_REAL_NAME, 0, 1, PepAcpiObjectTypeDevice}, + {(ULONG)SAMPLE_TST3_REAL_NAME, 0, 1, PepAcpiObjectTypeDevice} +}; + +PEP_OBJECT_INFORMATION BusdNativeMethods[] = +{ + // _HID + {ACPI_OBJECT_NAME_HID, 0, 1, PepAcpiObjectTypeMethod}, + + // _CID + {ACPI_OBJECT_NAME_CID, 0, 1, PepAcpiObjectTypeMethod}, + + // + // _Child Devices + // + + {(ULONG)SAMPLE_TST1_REAL_NAME, 0, 1, PepAcpiObjectTypeDevice}, + {(ULONG)SAMPLE_TST2_REAL_NAME, 0, 1, PepAcpiObjectTypeDevice}, +}; + +PEP_OBJECT_INFORMATION GpioAndSpbdNativeMethods[] = +{ + // _HID + {ACPI_OBJECT_NAME_HID, 0, 1, PepAcpiObjectTypeMethod}, + + // _CID + {ACPI_OBJECT_NAME_CID, 0, 1, PepAcpiObjectTypeMethod} +}; + +PEP_OBJECT_INFORMATION Tst1NativeMethods[] = +{ + // _ADR + {ACPI_OBJECT_NAME_ADR, 0, 1, PepAcpiObjectTypeMethod}, + + // _CRS + {ACPI_OBJECT_NAME_CRS, 0, 1, PepAcpiObjectTypeMethod}, + + // _DEP + {ACPI_OBJECT_NAME_DEP, 0, 1, PepAcpiObjectTypeMethod} +}; + +PEP_OBJECT_INFORMATION Tst2NativeMethods[] = +{ + // _ADR + {ACPI_OBJECT_NAME_ADR, 0, 1, PepAcpiObjectTypeMethod}, + + // _CRS + {ACPI_OBJECT_NAME_CRS, 0, 1, PepAcpiObjectTypeMethod} +}; + +PEP_OBJECT_INFORMATION Tst3NativeMethods[] = +{ + // _HID + {ACPI_OBJECT_NAME_HID, 0, 1, PepAcpiObjectTypeMethod}, + + // _CID + {ACPI_OBJECT_NAME_CID, 0, 1, PepAcpiObjectTypeMethod}, +}; + +PEP_OBJECT_INFORMATION PwrrNativeMethods[] = +{ + // _HID + {ACPI_OBJECT_NAME_HID, 0, 1, PepAcpiObjectTypeMethod}, + + // _STA + {ACPI_OBJECT_NAME_STA, 0, 1, PepAcpiObjectTypeMethod}, + + // _ON + {ACPI_OBJECT_NAME_ON, 0, 1, PepAcpiObjectTypeMethod}, + + // _OFF + {ACPI_OBJECT_NAME_OFF, 0, 1, PepAcpiObjectTypeMethod}, +}; + +// +// Step 5: Define the list of device-specific notification handlers +// for each device. +// +// In this example: +// 1) all devices define PEP_NOTIFY_ACPI_EVALUATE_CONTROL_METHOD; +// 2) for \_SB.BUSD, its _HID method will be handled synchronously +// while its _CID method will be handled asynchronously; +// 3) for \_SB.GPIO and \_SB.SPBD, their _HID method will both be +// handled synchronously; +// 4) for \_SB.BUSD.TST1 and \_SB.BUSD.TST2, their _ADR and _DEP +// methods will be handled synchronously while _CRS method +// will be handled asynchronously. +// 5) \_SB.BUSD.TST1 also synchronously handles +// PEP_NOTIFY_ACPI_QUERY_DEVICE_CONTROL_RESOURCES. +// 6) \_SB.TST3 synchronously handles all +// PEP_NOTIFY_ACPI_EVALUATE_CONTROL_METHOD except for _CID. +// The processing for _CID is done in a self-managed manner (i.e. it is +// processed asynchronously but does not rely on common library worker). +// Hence there is no async worker registered. +// 7) \_SB.PWRR synchronously handles all +// PEP_NOTIFY_ACPI_EVALUATE_CONTROL_METHOD. +// + +PEP_DEVICE_NOTIFICATION_HANDLER RootNotificationHandler[] = { + {PEP_NOTIFY_ACPI_EVALUATE_CONTROL_METHOD, + RootSyncEvaluateControlMethod, + NULL} +}; + +PEP_DEVICE_NOTIFICATION_HANDLER BusdNotificationHandler[] = { + {PEP_NOTIFY_ACPI_EVALUATE_CONTROL_METHOD, + BusdSyncEvaluateControlMethod, + BusdWorkerCallbackEvaluateControlMethod} +}; + +PEP_DEVICE_NOTIFICATION_HANDLER GpioNotificationHandler[] = { + {PEP_NOTIFY_ACPI_EVALUATE_CONTROL_METHOD, + GpioSyncEvaluateControlMethod, + NULL} +}; + +PEP_DEVICE_NOTIFICATION_HANDLER SpbdNotificationHandler[] = { + {PEP_NOTIFY_ACPI_EVALUATE_CONTROL_METHOD, + SpbdSyncEvaluateControlMethod, + NULL} +}; + +PEP_DEVICE_NOTIFICATION_HANDLER Tst1NotificationHandler[] = { + {PEP_NOTIFY_ACPI_EVALUATE_CONTROL_METHOD, + Tst1SyncEvaluateControlMethod, + Tst1WorkerCallbackEvaluateControlMethod}, + + {PEP_NOTIFY_ACPI_QUERY_DEVICE_CONTROL_RESOURCES, + Tst1SyncQueryControlResources, + NULL} +}; + +PEP_DEVICE_NOTIFICATION_HANDLER Tst2NotificationHandler[] = { + {PEP_NOTIFY_ACPI_EVALUATE_CONTROL_METHOD, + Tst2SyncEvaluateControlMethod, + Tst2WorkerCallbackEvaluateControlMethod} +}; + +PEP_DEVICE_NOTIFICATION_HANDLER Tst3NotificationHandler[] = { + {PEP_NOTIFY_ACPI_EVALUATE_CONTROL_METHOD, + Tst3SyncEvaluateControlMethod, + NULL} +}; + +PEP_DEVICE_NOTIFICATION_HANDLER PwrrNotificationHandler[] = { + {PEP_NOTIFY_ACPI_EVALUATE_CONTROL_METHOD, + PwrrSyncEvaluateControlMethod, + NULL} +}; + +// +// Step 6: Edit the PepDeviceDefinitionArray to register any native methods +// and device-specific notification handlers. +// + +PEP_DEVICE_DEFINITION PepDeviceDefinitionArray[] = { + + // \_SB + {PEP_DEVICE_TYPE_ROOT, + sizeof(PEP_ACPI_DEVICE), + NULL, + ARRAYSIZE(RootNativeMethods), + RootNativeMethods, + ARRAYSIZE(RootNotificationHandler), + RootNotificationHandler, + 0, + NULL}, + + // \_SB.BUSD + {PEP_DEVICE_TYPE_BUSD, + sizeof(PEP_ACPI_DEVICE), + NULL, + ARRAYSIZE(BusdNativeMethods), + BusdNativeMethods, + ARRAYSIZE(BusdNotificationHandler), + BusdNotificationHandler, + 0, + NULL}, + + // \_SB.GPIO + {PEP_DEVICE_TYPE_GPIO, + sizeof(PEP_ACPI_DEVICE), + NULL, + ARRAYSIZE(GpioAndSpbdNativeMethods), + GpioAndSpbdNativeMethods, + ARRAYSIZE(GpioNotificationHandler), + GpioNotificationHandler, + 0, + NULL}, + + // \_SB.SPBD + {PEP_DEVICE_TYPE_SPBD, + sizeof(PEP_ACPI_DEVICE), + NULL, + ARRAYSIZE(GpioAndSpbdNativeMethods), + GpioAndSpbdNativeMethods, + ARRAYSIZE(SpbdNotificationHandler), + SpbdNotificationHandler, + 0, + NULL}, + + // \_SB.BUSD.TST1 + {PEP_DEVICE_TYPE_TST1, + sizeof(PEP_ACPI_DEVICE), + NULL, + ARRAYSIZE(Tst1NativeMethods), + Tst1NativeMethods, + ARRAYSIZE(Tst1NotificationHandler), + Tst1NotificationHandler, + 0, + NULL}, + + // \_SB.BUSD.TST2 + {PEP_DEVICE_TYPE_TST2, + sizeof(PEP_ACPI_DEVICE), + NULL, + ARRAYSIZE(Tst2NativeMethods), + Tst2NativeMethods, + ARRAYSIZE(Tst2NotificationHandler), + Tst2NotificationHandler, + 0, + NULL}, + + // \_SB.TST3 + {PEP_DEVICE_TYPE_TST3, + sizeof(PEP_ACPI_DEVICE), + NULL, + ARRAYSIZE(Tst3NativeMethods), + Tst3NativeMethods, + ARRAYSIZE(Tst3NotificationHandler), + Tst3NotificationHandler, + 0, + NULL}, + + // \_SB.PWRR + {PEP_DEVICE_TYPE_PWRR, + sizeof(PEP_ACPI_DEVICE), + NULL, + ARRAYSIZE(PwrrNativeMethods), + PwrrNativeMethods, + ARRAYSIZE(PwrrNotificationHandler), + PwrrNotificationHandler, + 0, + NULL}, +}; + +ULONG PepDeviceDefinitionArraySize = ARRAYSIZE(PepDeviceDefinitionArray); + +// +// Step 7: Implement the device-specific notification handlers. +// (See testdevice.c for details) +// + +// +// Debug +// +volatile BOOLEAN _SAMPLE_PEP_DEBUG_ENABLED = TRUE; + diff --git a/pofx/PEP/acpi/acpispecific.h b/pofx/PEP/acpi/acpispecific.h new file mode 100644 index 000000000..1291be72d --- /dev/null +++ b/pofx/PEP/acpi/acpispecific.h @@ -0,0 +1,298 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + acpispecific.h + +Abstract: + + This module defines constants for the sample platform extension. + + +Environment: + + Kernel mode + +--*/ + +// +//-------------------------------------------------------------------- Includes +// + +#include "pch.h" + +// +//----------------------------------------------------------------- Definitions +// + +// +// To add new devices that will be accpeted by the platform extension, +// follow to the example below. +// + +// +// Step 1: Define a unique device type for each new device. +// In this example, the sample platform extension accepts the +// following devices: +// 0). \_SB: Acpi Root +// 1). \_SB.BUSD: Bus device; +// 2). \_SB.GPIO: GPIO controller; +// 3). \_SB.SPBD: SPB controller; +// 4). \_SB.BUSD.TST1: Test device 1; +// 5). \_SB.BUSD.TST2: Test device 2; +// 6). \_SB.TST3: Test device 3; +// 7). \_SB.PWRR: Power rail device. +// + +#define PEP_DEVICE_TYPE_ROOT \ + PEP_MAKE_DEVICE_TYPE(PepMajorDeviceTypeAcpi, \ + PepAcpiMinorTypeDevice, \ + 0x0) + +#define PEP_DEVICE_TYPE_BUSD \ + PEP_MAKE_DEVICE_TYPE(PepMajorDeviceTypeAcpi, \ + PepAcpiMinorTypeDevice, \ + 0x1) + +#define PEP_DEVICE_TYPE_GPIO \ + PEP_MAKE_DEVICE_TYPE(PepMajorDeviceTypeAcpi, \ + PepAcpiMinorTypeDevice, \ + 0x2) + +#define PEP_DEVICE_TYPE_SPBD \ + PEP_MAKE_DEVICE_TYPE(PepMajorDeviceTypeAcpi, \ + PepAcpiMinorTypeDevice, \ + 0x3) + +#define PEP_DEVICE_TYPE_TST1 \ + PEP_MAKE_DEVICE_TYPE(PepMajorDeviceTypeAcpi, \ + PepAcpiMinorTypeDevice, \ + 0x4) + +#define PEP_DEVICE_TYPE_TST2 \ + PEP_MAKE_DEVICE_TYPE(PepMajorDeviceTypeAcpi, \ + PepAcpiMinorTypeDevice, \ + 0x5) + +#define PEP_DEVICE_TYPE_TST3 \ + PEP_MAKE_DEVICE_TYPE(PepMajorDeviceTypeAcpi, \ + PepAcpiMinorTypeDevice, \ + 0x6) + +#define PEP_DEVICE_TYPE_PWRR \ + PEP_MAKE_DEVICE_TYPE(PepMajorDeviceTypeAcpi, \ + PepAcpiMinorTypeDevice, \ + 0x7) + +// +// Step 2: Define a name for each new device. +// + +#define SAMPLE_ROOT_ANSI "\\_SB" +#define SAMPLE_ROOT_WCHAR L"\\_SB" +#define SAMPLE_BUSD_ACPI_NAME_ANSI "\\_SB.BUSD" +#define SAMPLE_BUSD_ACPI_NAME_WCHAR L"\\_SB.BUSD" +#define SAMPLE_GPIO_ACPI_NAME_ANSI "\\_SB.GPIO" +#define SAMPLE_GPIO_ACPI_NAME_WCHAR L"\\_SB.GPIO" +#define SAMPLE_SPBD_ACPI_NAME_ANSI "\\_SB.SPBD" +#define SAMPLE_SPBD_ACPI_NAME_WCHAR L"\\_SB.SPBD" +#define SAMPLE_TST1_ACPI_NAME_ANSI "\\_SB.BUSD.TST1" +#define SAMPLE_TST1_ACPI_NAME_WCHAR L"\\_SB.BUSD.TST1" +#define SAMPLE_TST2_ACPI_NAME_ANSI "\\_SB.BUSD.TST2" +#define SAMPLE_TST2_ACPI_NAME_WCHAR L"\\_SB.BUSD.TST2" +#define SAMPLE_TST3_ACPI_NAME_ANSI "\\_SB.TST3" +#define SAMPLE_TST3_ACPI_NAME_WCHAR L"\\_SB.TST3" +#define SAMPLE_PWRR_ACPI_NAME_ANSI "\\_SB.PWRR" +#define SAMPLE_PWRR_ACPI_NAME_WCHAR L"\\_SB.PWRR" + +#define SAMPLE_BUSD_REAL_NAME 'DSUB' +#define SAMPLE_GPIO_REAL_NAME 'OIPG' +#define SAMPLE_SPBD_REAL_NAME 'DBPS' +#define SAMPLE_TST1_REAL_NAME '1TST' +#define SAMPLE_TST2_REAL_NAME '2TST' +#define SAMPLE_TST3_REAL_NAME '3TST' +#define SAMPLE_PWRR_REAL_NAME 'PWRR' + +// +// Step 3: Edit the PepDeviceMatchArray to tell the common library which device +// should be accepted by the platform extension. +// (See acpispecific.c for details) +// + +// +// Step 4: Define the list of native methods under each device. +// (See acpispecific.c for details) +// + +// +// Step 5: Define the list of device-specific notification handlers +// for each device. +// (See acpispecific.c for details) +// + +// +// Step 6: Edit the PepDeviceDefinitionArray to register any native methods +// and device-specific notification handlers. +// (See acpispecific.c for details) +// + +// +// Step 7: Implement the device-specific notification handlers. +// (See testdevice.c for details) +// + +// +// Define the _HID, _CID, _ADR's for the test devices. +// + +// +// Define the _HID of the bus device. +// + +#define BUSD_HID "BUSD0001" + +// +// Define the _CID of the bus device. +// + +#define BUSD_CID "root\\dynambus" + +// +// Define the _HID for the GPIO controller. +// + +#define GPIO_HID "GPIO0001" + +// +// Define the _HID for the SPBD controller. +// + +#define SPBD_HID "SPBD0001" + +// +// Define the _CID for the GPIO and SPBD. +// + +#define GPIO_SPBD_CID "{b85b7c50-6a01-11d2-b841-00c04fad5171}\\MsToaster" + +// +// Define the _ADR for the test devices. +// + +#define TST1_ADR 0x1 +#define TST2_ADR 0x2 + +// +// Define the _HID and _CID for the test device 3. +// + +#define TST3_HID "TSTDV3" +#define TST3_CID "{b85b7c50-6a01-11d2-b841-00c04fad5171}\\MsToaster" + +// +// Define the UUIDs for the _DSM of test device 3. +// + +// {9BEC5298-A0A0-4725-916F-06AAF0CF69F4} +static const GUID TST3_DSM_UUID1 = +{ 0x9bec5298, 0xa0a0, 0x4725, { 0x91, 0x6f, 0x6, 0xaa, 0xf0, 0xcf, 0x69, 0xf4 } }; + +// {74DB3E61-06FA-4268-9A32-1101338FD38E} +static const GUID TST3_DSM_UUID2 = +{ 0x74db3e61, 0x6fa, 0x4268, { 0x9a, 0x32, 0x11, 0x1, 0x33, 0x8f, 0xd3, 0x8e } }; + +// +// Define the _HID for the power rail. +// + +#define PWRR_HID "PWRR0001" + +// +// Debugging +// + +extern volatile BOOLEAN _SAMPLE_PEP_DEBUG_ENABLED; + +#define DEBUG_BREAK() \ + {if (_SAMPLE_PEP_DEBUG_ENABLED) { \ + DbgBreakPoint(); \ + }} \ + +// +//------------------------------------------------------------------ Prototypes +// + +PEP_NOTIFICATION_HANDLER_RESULT +RootSyncEvaluateControlMethod ( + _In_ PVOID Data, + _Out_opt_ PPEP_WORK_INFORMATION PoFxWorkInfo + ); + +PEP_NOTIFICATION_HANDLER_RESULT +BusdSyncEvaluateControlMethod ( + _In_ PVOID Data, + _Out_opt_ PPEP_WORK_INFORMATION PoFxWorkInfo + ); + +PEP_NOTIFICATION_HANDLER_RESULT +BusdWorkerCallbackEvaluateControlMethod ( + _In_ PVOID Data, + _Out_opt_ PPEP_WORK_INFORMATION PoFxWorkInfo + ); + +PEP_NOTIFICATION_HANDLER_RESULT +GpioSyncEvaluateControlMethod ( + _In_ PVOID Data, + _Out_opt_ PPEP_WORK_INFORMATION PoFxWorkInfo + ); + +PEP_NOTIFICATION_HANDLER_RESULT +SpbdSyncEvaluateControlMethod ( + _In_ PVOID Data, + _Out_opt_ PPEP_WORK_INFORMATION PoFxWorkInfo + ); + +PEP_NOTIFICATION_HANDLER_RESULT +Tst1SyncEvaluateControlMethod ( + _In_ PVOID Data, + _Out_opt_ PPEP_WORK_INFORMATION PoFxWorkInfo + ); + +PEP_NOTIFICATION_HANDLER_RESULT +Tst1WorkerCallbackEvaluateControlMethod ( + _In_ PVOID Data, + _Out_opt_ PPEP_WORK_INFORMATION PoFxWorkInfo + ); + +PEP_NOTIFICATION_HANDLER_RESULT +Tst1SyncQueryControlResources ( + _In_ PVOID Data, + _Out_opt_ PPEP_WORK_INFORMATION PoFxWorkInfo + ); + +PEP_NOTIFICATION_HANDLER_RESULT +Tst2SyncEvaluateControlMethod ( + _In_ PVOID Data, + _Out_opt_ PPEP_WORK_INFORMATION PoFxWorkInfo + ); + +PEP_NOTIFICATION_HANDLER_RESULT +Tst2WorkerCallbackEvaluateControlMethod ( + _In_ PVOID Data, + _Out_opt_ PPEP_WORK_INFORMATION PoFxWorkInfo + ); + +PEP_NOTIFICATION_HANDLER_RESULT +Tst3SyncEvaluateControlMethod ( + _In_ PVOID Data, + _Out_opt_ PPEP_WORK_INFORMATION PoFxWorkInfo + ); + +PEP_NOTIFICATION_HANDLER_RESULT +PwrrSyncEvaluateControlMethod ( + _In_ PVOID Data, + _Out_opt_ PPEP_WORK_INFORMATION PoFxWorkInfo + ); + diff --git a/pofx/PEP/acpi/pchsrc.c b/pofx/PEP/acpi/pchsrc.c new file mode 100644 index 000000000..17305716a --- /dev/null +++ b/pofx/PEP/acpi/pchsrc.c @@ -0,0 +1 @@ +#include "pch.h" \ No newline at end of file diff --git a/pofx/PEP/acpi/sampleacpipep.inx b/pofx/PEP/acpi/sampleacpipep.inx new file mode 100644 index 000000000..95ec332a8 --- /dev/null +++ b/pofx/PEP/acpi/sampleacpipep.inx @@ -0,0 +1,85 @@ +;/*++ +; +;Copyright (c) 1990-1999 Microsoft Corporation All rights Reserved +; +;Module Name: +; +; SAMPLEACPIPEP.INF +; +;Abstract: +; INF file for installing the sample Platform Extension (ACPI PEP). +; +;--*/ + +[Version] +Signature="$WINDOWS NT$" +Class=System +ClassGuid={4D36E97D-E325-11CE-BFC1-08002BE10318} +Provider=%ProviderString% +DriverVer=11/14/2014,1.00.0000 +CatalogFile=sampleacpipep.cat + +[DestinationDirs] +DefaultDestDir = 12 + +[SourceDisksNames] +1 = %DiskId1%,,,"" + +[SourceDisksFiles] +sampleacpipep.sys = 1,, + +;***************************************** +; Pep Device Install Section +;***************************************** + +[Manufacturer] +%StdMfg%=Standard,NT$ARCH$ + +[Standard.NT$ARCH$] +%PepDevice.DeviceDesc%=Pep_Device,ACPI\SAMPLEACPIPEP + +[Pep_Device.NT] +CopyFiles=Pep_Device.NT.Copy + +[Pep_Device.NT.HW] +AddReg=Pep_Device.NT.AddReg + +[Pep_Device.NT.AddReg] +HKR,,DeviceCharacteristics,0x10001,0x0100 ; Use same security checks on relative opens +HKR,,Security,,"D:P(A;;GA;;;BA)(A;;GA;;;SY)" ; Allow generic-all access to Built-in administrators and Local system + +[Pep_Device.NT.Copy] +sampleacpipep.sys + +;-------------- Service installation + +[Pep_Device.NT.Services] +AddService = sampleacpipep,%SPSVCINST_ASSOCSERVICE%,pep_Service_Inst + +[pep_Service_Inst] +DisplayName = %sampleacpipep.SVCDESC% +ServiceType = %SERVICE_KERNEL_DRIVER% +StartType = %SERVICE_BOOT_START% +ErrorControl = %SERVICE_ERROR_NORMAL% +ServiceBinary = %12%\sampleacpipep.sys +LoadOrderGroup = Core Platform Extensions +;ensure the test PEP loads at same time as ACPI (before real PEPs) + +[Strings] +SPSVCINST_ASSOCSERVICE= 0x00000002 +ProviderString = "TODO-Set-Provider" +StdMfg = "(Standard system devices)" +ClassName = "System devices" +DiskId1 = "Dummy Power Engine Plugin Disk 1" +PepDevice.DeviceDesc = "Sample Platform Extension 01" +sampleacpipep.SVCDESC = "Sample Platform Extension 01" + +;Non-Localizable Strings +SERVICE_KERNEL_DRIVER = 1 +SERVICE_BOOT_START = 0 +SERVICE_DEMAND_START = 3 +SERVICE_ERROR_NORMAL = 1 + + + + diff --git a/pofx/PEP/acpi/sampleacpipep.rc b/pofx/PEP/acpi/sampleacpipep.rc new file mode 100644 index 000000000..0a6b221f2 --- /dev/null +++ b/pofx/PEP/acpi/sampleacpipep.rc @@ -0,0 +1,12 @@ +#include + +#include + +#define VER_FILETYPE VFT_DLL +#define VER_FILESUBTYPE VFT2_UNKNOWN +#define VER_FILEDESCRIPTION_STR "Sample ACPI PEP" +#define VER_INTERNALNAME_STR "sampleacpipep.sys" +#define VER_ORIGINALFILENAME_STR "sampleacpipep.sys" + +#include "common.ver" + diff --git a/pofx/PEP/acpi/sampleacpipep.vcxproj b/pofx/PEP/acpi/sampleacpipep.vcxproj new file mode 100644 index 000000000..6f827afde --- /dev/null +++ b/pofx/PEP/acpi/sampleacpipep.vcxproj @@ -0,0 +1,266 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {EF47791C-9559-4F0F-867F-A0E055054058} + $(MSBuildProjectName) + 1 + Debug + Win32 + {4ABAAE16-AA0F-41FC-91FF-086A659D2992} + + + + 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 + ENABLE_WPP_RECORDER=1 + ..\inc;%(AdditionalIncludeDirectories) + pch.h + Use + $(IntDir)\pch.h.pch + + + true + true + TraceEvents(LEVEL,FLAGS,MSG,...) + {km-WdfDefault.tpl}*.tmh + ENABLE_WPP_RECORDER=1 + ..\inc;%(AdditionalIncludeDirectories) + pch.h + Use + $(IntDir)\pch.h.pch + + + * + true + $(InfArch) + true + .\$(IntDir)\sampleacpipep.inf + + + true + true + TraceEvents(LEVEL,FLAGS,MSG,...) + {km-WdfDefault.tpl}*.tmh + ENABLE_WPP_RECORDER=1 + + + + sampleacpipep + + + sampleacpipep + + + sampleacpipep + + + sampleacpipep + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE;EVENT_TRACING + true + Level4 + + + %(PreprocessorDefinitions);UNICODE;_UNICODE;EVENT_TRACING + + + %(PreprocessorDefinitions);UNICODE;_UNICODE;EVENT_TRACING + + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE;EVENT_TRACING + true + Level4 + + + %(PreprocessorDefinitions);UNICODE;_UNICODE;EVENT_TRACING + + + %(PreprocessorDefinitions);UNICODE;_UNICODE;EVENT_TRACING + + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE;EVENT_TRACING + true + Level4 + + + %(PreprocessorDefinitions);UNICODE;_UNICODE;EVENT_TRACING + + + %(PreprocessorDefinitions);UNICODE;_UNICODE;EVENT_TRACING + + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE;EVENT_TRACING + true + Level4 + + + %(PreprocessorDefinitions);UNICODE;_UNICODE;EVENT_TRACING + + + %(PreprocessorDefinitions);UNICODE;_UNICODE;EVENT_TRACING + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ksguid.lib;$(DDK_LIB_PATH)\ntstrsafe.lib;$(DDK_LIB_PATH)\hal.lib;$(DDK_LIB_PATH)\ntoskrnl.lib;$(DDK_LIB_PATH)\wdmsec.lib;$(DDK_LIB_PATH)\wpprecorder.lib;.\..\common\$(IntDir)\pepcommon.lib + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ksguid.lib;$(DDK_LIB_PATH)\ntstrsafe.lib;$(DDK_LIB_PATH)\hal.lib;$(DDK_LIB_PATH)\ntoskrnl.lib;$(DDK_LIB_PATH)\wdmsec.lib;$(DDK_LIB_PATH)\wpprecorder.lib;.\..\common\$(IntDir)\pepcommon.lib + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ksguid.lib;$(DDK_LIB_PATH)\ntstrsafe.lib;$(DDK_LIB_PATH)\hal.lib;$(DDK_LIB_PATH)\ntoskrnl.lib;$(DDK_LIB_PATH)\wdmsec.lib;$(DDK_LIB_PATH)\wpprecorder.lib;.\..\common\$(IntDir)\pepcommon.lib + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ksguid.lib;$(DDK_LIB_PATH)\ntstrsafe.lib;$(DDK_LIB_PATH)\hal.lib;$(DDK_LIB_PATH)\ntoskrnl.lib;$(DDK_LIB_PATH)\wdmsec.lib;$(DDK_LIB_PATH)\wpprecorder.lib;.\..\common\$(IntDir)\pepcommon.lib + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + + + + + ..\inc;%(AdditionalIncludeDirectories) + pch.h + Create + $(IntDir)\pch.h.pch + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pofx/PEP/acpi/sampleacpipep.vcxproj.Filters b/pofx/PEP/acpi/sampleacpipep.vcxproj.Filters new file mode 100644 index 000000000..9f34a488a --- /dev/null +++ b/pofx/PEP/acpi/sampleacpipep.vcxproj.Filters @@ -0,0 +1,42 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {A57CB1F4-9530-4851-8C3E-570F22B7FD51} + + + h;hpp;hxx;hm;inl;inc;xsd + {A5639709-5FAC-4C0B-9540-FEED78ECAB9D} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {E30CD127-ACB4-487E-B0F8-A356EBD657AD} + + + inf;inv;inx;mof;mc; + {699DA905-BD41-4310-B92C-DF62A6AF87B9} + + + + + Source Files + + + Source Files + + + Source Files + + + + + Driver Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/pofx/PEP/acpi/testdevice.c b/pofx/PEP/acpi/testdevice.c new file mode 100644 index 000000000..2eb8e0722 --- /dev/null +++ b/pofx/PEP/acpi/testdevice.c @@ -0,0 +1,2093 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + testdevice.c + +Abstract: + + This module defines the device-specific notification handlers. + + The routines below demonstrate three different types of request processing + supported by the PEP common library. + 1. Synchronous processing: The request is completed in the context of + of the PEP notification. The handler returns + PEP_NOTIFICATION_HANDLER_COMPLETE to the PEP common library to indicate + all work is completed. + + 2. Async processing: The request processing is deferred to a worker + thread by returning PEP_NOTIFICATION_HANDLER_MORE_WORK from the sync + handler. The common library will then invoke the async handler in the + context of a worker thread (created by the library). + + 3. Self-managed work: The request processing is deferred but a worker + owned and managed by this module. This is suitable if the driver + maintains its own queue for requests. Although the request is + deferred, the sync handler must return + PEP_NOTIFICATION_HANDLER_COMPLETE to the PEP common library to indicate + no async processing is needed. Later when the work actually completes, + it is completed back to PoFx by creating a PepCompleteSelfManagedWork() + request. + + + +Environment: + + Kernel mode + +--*/ + +// +//-------------------------------------------------------------------- Includes +// + +#include "pch.h" +#include "acpispecific.h" + +#if defined(EVENT_TRACING) +#include "testdevice.tmh" +#endif + +// +//----------------------------------------------------------------- Definitions +// + +#define GET_STRING_LENGTH(_Str) ((ULONG)(strlen((_Str)) + 1) * sizeof(CHAR)) + +// +// Define the resource requirements for test devices. These resources +// are device-specific hence should be adjusted accordingly. +// + +#define TST1_MEM_RES_RW 0 +#define TST1_MEM_RES_MIN_ADDR 0x9fc00 +#define TST1_MEM_RES_MAX_ADDR 0x9fe00 +#define TST1_MEM_RES_ALIGNMENT 0x4 +#define TST1_MEM_RES_MEMSIZE 0x10 + +#define TST1_IO_RES_DECODE 1 +#define TST1_IO_RES_MIN_ADDR 0x0 +#define TST1_IO_RES_MAX_ADDR 0x100 +#define TST1_IO_RES_ALIGNMENT 0x4 +#define TST1_IO_RES_PORT_LEN 1 + +#define TST1_INT_RES_EDGE_LVL Latched +#define TST1_INT_RES_INT_LVL InterruptActiveLow +#define TST1_INT_RES_SHARE_TYPE FALSE +#define TST1_INT_RES_WAKE FALSE + +#define TST2_GPIOINT_RES_INT_TYPE Latched +#define TST2_GPIOINT_RES_INT_LVL InterruptActiveLow +#define TST2_GPIOINT_RES_PIN_CONFIG PullDefault +#define TST2_GPIOINT_RES_DEBOUNCE 0 + +#define TST2_SPB_RES_SLAVE_ADDR 0x0 +#define TST2_SPB_RES_CONN_SPD 100 + +// +//--------------------------------------------------------------------- Types +// + +typedef struct _TST3_CID_REQUEST_CONTEXT { + PEP_ACPI_EVALUATE_CONTROL_METHOD EcmBuffer; + BOOLEAN Synchronous; +} TST3_CID_REQUEST_CONTEXT, *PTST3_CID_REQUEST_CONTEXT; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(TST3_CID_REQUEST_CONTEXT, Tst3GetCidRequestContext) + +// +//--------------------------------------------------------------------- Globals +// + +USHORT Tst1GpioPinTable[] = {1}; +ULONG Tst1IntPinTable[] = {0xa}; +USHORT Tst2GpioIntPinTable[] = {2}; + +// +// The UNICODE string for GPIO resource name. +// + +UNICODE_STRING GpioResourceName = { + sizeof(SAMPLE_GPIO_ACPI_NAME_WCHAR) - sizeof(WCHAR), + sizeof(SAMPLE_GPIO_ACPI_NAME_WCHAR), + SAMPLE_GPIO_ACPI_NAME_WCHAR +}; + +UNICODE_STRING SpbResourceName = { + sizeof(SAMPLE_SPBD_ACPI_NAME_WCHAR) - sizeof(WCHAR), + sizeof(SAMPLE_SPBD_ACPI_NAME_WCHAR), + SAMPLE_SPBD_ACPI_NAME_WCHAR +}; + +// +// Define the state of the power rail device, where 1 means on, 0 means off. +// + +ULONG PowerRailState = 0; + +// +//------------------------------------------------------------------ Prototypes +// + +VOID +ReportNeedMoreWork ( + _Out_ PNTSTATUS Status, + _In_opt_ PCHAR MethodName, + _In_opt_ PCHAR DebugInfo, + _Out_ PPEP_NOTIFICATION_HANDLER_RESULT CompleteResult + ); + +VOID +ReportNotSupported ( + _Out_ PNTSTATUS Status, + _Out_ PULONG Count, + _Out_ PPEP_NOTIFICATION_HANDLER_RESULT CompleteResult + ); + +VOID +ReportNeedMoreSelfManagedWork ( + _Out_ PNTSTATUS Status, + _In_opt_ PCHAR MethodName, + _In_opt_ PCHAR DebugInfo, + _Out_ PPEP_NOTIFICATION_HANDLER_RESULT CompleteResult + ); + +VOID +HandleTst3DsmByUuid ( + _In_ ULONG UuidIndex, + _In_ ULONG RevisionLevel, + _In_ ULONG FunctionIndex, + _In_ PVOID Parameters, + _In_ ULONG ParametersSize, + _Out_ PPEP_ACPI_EVALUATE_CONTROL_METHOD EcmBuffer, + _Out_ PPEP_NOTIFICATION_HANDLER_RESULT CompleteResult + ); + +NTSTATUS +Tst3CreateSelfManagedCidRequest ( + _In_ PPEP_ACPI_EVALUATE_CONTROL_METHOD EcmBuffer + ); + +VOID +Tst3SelfManagedCidRequestWorkerWrapper ( + __in WDFWORKITEM WorkItem + ); + +VOID +Tst3ProcessSelfManagedCidRequest ( + _In_ PTST3_CID_REQUEST_CONTEXT RequestContext + ); + +// +//------------------------------------------------------------------- Functions +// + +// +// Notification handlers for \_SB. +// + +PEP_NOTIFICATION_HANDLER_RESULT +RootSyncEvaluateControlMethod ( + _In_ PVOID Data, + _Out_opt_ PPEP_WORK_INFORMATION PoFxWorkInfo + ) + +/*++ + +Routine Description: + + This routine handles PEP_NOTIFY_ACPI_EVALUATE_CONTROL_METHOD + notification for the bus device. + +Arguments: + + Data - Supplies a pointer to parameters buffer for this notification. + + PoFxWorkInfo - Unused. + +Return Value: + + None. + +--*/ + +{ + + PEP_NOTIFICATION_HANDLER_RESULT CompleteStatus; + PPEP_ACPI_EVALUATE_CONTROL_METHOD EcmBuffer; + + UNREFERENCED_PARAMETER(PoFxWorkInfo); + + EcmBuffer = (PPEP_ACPI_EVALUATE_CONTROL_METHOD)Data; + CompleteStatus = PEP_NOTIFICATION_HANDLER_COMPLETE; + switch(EcmBuffer->MethodName) { + default: + ReportNotSupported(&EcmBuffer->MethodStatus, + &EcmBuffer->OutputArgumentCount, + &CompleteStatus); + } + + // + // Return complete status. + // + + return CompleteStatus; +} + +// +// Notification handlers for \_SB.BUSD. +// + +PEP_NOTIFICATION_HANDLER_RESULT +BusdSyncEvaluateControlMethod ( + _In_ PVOID Data, + _Out_opt_ PPEP_WORK_INFORMATION PoFxWorkInfo + ) + +/*++ + +Routine Description: + + This routine handles PEP_NOTIFY_ACPI_EVALUATE_CONTROL_METHOD + notification for the bus device. It will try to evaluate the + _HID method synchronously, while leaving _CID to the asynchronous + method. + +Arguments: + + Data - Supplies a pointer to parameters buffer for this notification. + + PoFxWorkInfo - Unused. + +Return Value: + + None. + +--*/ + +{ + + PEP_NOTIFICATION_HANDLER_RESULT CompleteStatus; + PPEP_ACPI_EVALUATE_CONTROL_METHOD EcmBuffer; + + UNREFERENCED_PARAMETER(PoFxWorkInfo); + + EcmBuffer = (PPEP_ACPI_EVALUATE_CONTROL_METHOD)Data; + CompleteStatus = PEP_NOTIFICATION_HANDLER_COMPLETE; + switch(EcmBuffer->MethodName) { + case ACPI_OBJECT_NAME_HID: + PepReturnAcpiData(BUSD_HID, + ACPI_METHOD_ARGUMENT_STRING, + GET_STRING_LENGTH(BUSD_HID), + FALSE, + EcmBuffer->OutputArguments, + &EcmBuffer->OutputArgumentSize, + &EcmBuffer->OutputArgumentCount, + &EcmBuffer->MethodStatus, + "HID", + "BUSD", + &CompleteStatus); + + break; + + case ACPI_OBJECT_NAME_CID: + ReportNeedMoreWork(&EcmBuffer->MethodStatus, + "CID", + "BUSD", + &CompleteStatus); + + break; + + default: + ReportNotSupported(&EcmBuffer->MethodStatus, + &EcmBuffer->OutputArgumentCount, + &CompleteStatus); + } + + // + // Return complete status. + // + + return CompleteStatus; +} + +PEP_NOTIFICATION_HANDLER_RESULT +BusdWorkerCallbackEvaluateControlMethod ( + _In_ PVOID Data, + _Out_opt_ PPEP_WORK_INFORMATION PoFxWorkInfo + ) + +/*++ + +Routine Description: + + This routine handles PEP_NOTIFY_ACPI_EVALUATE_CONTROL_METHOD + notification for the bus device. It will be scheduled to run + asychronously. In this example, it only handles _CID method. + +Arguments: + + Data - Supplies a pointer to parameters buffer for this notification. + + PoFxWorkInfo - Supplies a pointer to the PEP_WORK structure used to + report result to PoFx. + +Return Value: + + None. + +--*/ + +{ + + PEP_NOTIFICATION_HANDLER_RESULT CompleteStatus; + PPEP_ACPI_EVALUATE_CONTROL_METHOD EcmBuffer; + PPEP_INTERNAL_DEVICE_HEADER PepInternalDevice; + + TraceEvents(INFO, + DBG_PEP, + "%s : Asychronous method scheduled to run.\n", + __FUNCTION__); + + EcmBuffer = (PPEP_ACPI_EVALUATE_CONTROL_METHOD)Data; + PepInternalDevice = (PPEP_INTERNAL_DEVICE_HEADER)EcmBuffer->DeviceHandle; + CompleteStatus = PEP_NOTIFICATION_HANDLER_COMPLETE; + PoFxWorkInfo->WorkType = PepWorkAcpiEvaluateControlMethodComplete; + PoFxWorkInfo->ControlMethodComplete.DeviceHandle = + PepInternalDevice->KernelHandle; + + PoFxWorkInfo->ControlMethodComplete.CompletionFlags = 0; + PoFxWorkInfo->ControlMethodComplete.CompletionContext = + EcmBuffer->CompletionContext; + + switch(EcmBuffer->MethodName) { + case ACPI_OBJECT_NAME_CID: + PepReturnAcpiData( + BUSD_CID, + ACPI_METHOD_ARGUMENT_STRING, + GET_STRING_LENGTH(BUSD_CID), + FALSE, + PoFxWorkInfo->ControlMethodComplete.OutputArguments, + &PoFxWorkInfo->ControlMethodComplete.OutputArgumentSize, + NULL, + &PoFxWorkInfo->ControlMethodComplete.MethodStatus, + "CID", + "BUSD", + &CompleteStatus); + + break; + + default: + ReportNotSupported(&PoFxWorkInfo->ControlMethodComplete.MethodStatus, + (PULONG)(&PoFxWorkInfo-> + ControlMethodComplete.OutputArgumentSize), + &CompleteStatus); + } + + // + // Return complete status. + // + + return CompleteStatus; +} + +// +// Notification handlers for \_SB.GPIO. +// + +PEP_NOTIFICATION_HANDLER_RESULT +GpioSyncEvaluateControlMethod ( + _In_ PVOID Data, + _Out_opt_ PPEP_WORK_INFORMATION PoFxWorkInfo + ) + +/*++ + +Routine Description: + + This routine handles PEP_NOTIFY_ACPI_EVALUATE_CONTROL_METHOD + notification for the GPIO controller. It will try to evaluate the + _HID method synchronously. + +Arguments: + + Data - Supplies a pointer to parameters buffer for this notification. + + PoFxWorkInfo - Unused. + +Return Value: + + None. + +--*/ + +{ + + PEP_NOTIFICATION_HANDLER_RESULT CompleteStatus; + PPEP_ACPI_EVALUATE_CONTROL_METHOD EcmBuffer; + + UNREFERENCED_PARAMETER(PoFxWorkInfo); + + EcmBuffer = (PPEP_ACPI_EVALUATE_CONTROL_METHOD)Data; + CompleteStatus = PEP_NOTIFICATION_HANDLER_COMPLETE; + switch(EcmBuffer->MethodName) { + case ACPI_OBJECT_NAME_HID: + PepReturnAcpiData( + GPIO_HID, + ACPI_METHOD_ARGUMENT_STRING, + GET_STRING_LENGTH(GPIO_HID), + FALSE, + EcmBuffer->OutputArguments, + &EcmBuffer->OutputArgumentSize, + &EcmBuffer->OutputArgumentCount, + &EcmBuffer->MethodStatus, + "HID", + "GPIO", + &CompleteStatus); + + break; + + case ACPI_OBJECT_NAME_CID: + PepReturnAcpiData( + GPIO_SPBD_CID, + ACPI_METHOD_ARGUMENT_STRING, + GET_STRING_LENGTH(GPIO_SPBD_CID), + FALSE, + EcmBuffer->OutputArguments, + &EcmBuffer->OutputArgumentSize, + &EcmBuffer->OutputArgumentCount, + &EcmBuffer->MethodStatus, + "CID", + "GPIO", + &CompleteStatus); + + break; + + default: + ReportNotSupported(&EcmBuffer->MethodStatus, + &EcmBuffer->OutputArgumentCount, + &CompleteStatus); + } + + // + // Return complete status. + // + + return CompleteStatus; +} + +// +// Notification handlers for \_SB.SPBD. +// + +PEP_NOTIFICATION_HANDLER_RESULT +SpbdSyncEvaluateControlMethod ( + _In_ PVOID Data, + _Out_opt_ PPEP_WORK_INFORMATION PoFxWorkInfo + ) + +/*++ + +Routine Description: + + This routine handles PEP_NOTIFY_ACPI_EVALUATE_CONTROL_METHOD + notification for the SPBD controller. It will try to evaluate the + _HID method synchronously. + +Arguments: + + Data - Supplies a pointer to parameters buffer for this notification. + + PoFxWorkInfo - Unused. + +Return Value: + + None. + +--*/ + +{ + + PEP_NOTIFICATION_HANDLER_RESULT CompleteStatus; + PPEP_ACPI_EVALUATE_CONTROL_METHOD EcmBuffer; + + UNREFERENCED_PARAMETER(PoFxWorkInfo); + + EcmBuffer = (PPEP_ACPI_EVALUATE_CONTROL_METHOD)Data; + CompleteStatus = PEP_NOTIFICATION_HANDLER_COMPLETE; + switch(EcmBuffer->MethodName) { + case ACPI_OBJECT_NAME_HID: + PepReturnAcpiData( + SPBD_HID, + ACPI_METHOD_ARGUMENT_STRING, + GET_STRING_LENGTH(SPBD_HID), + FALSE, + EcmBuffer->OutputArguments, + &EcmBuffer->OutputArgumentSize, + &EcmBuffer->OutputArgumentCount, + &EcmBuffer->MethodStatus, + "HID", + "SPBD", + &CompleteStatus); + + break; + + case ACPI_OBJECT_NAME_CID: + PepReturnAcpiData( + GPIO_SPBD_CID, + ACPI_METHOD_ARGUMENT_STRING, + GET_STRING_LENGTH(GPIO_SPBD_CID), + FALSE, + EcmBuffer->OutputArguments, + &EcmBuffer->OutputArgumentSize, + &EcmBuffer->OutputArgumentCount, + &EcmBuffer->MethodStatus, + "CID", + "SPBD", + &CompleteStatus); + + break; + + default: + ReportNotSupported(&EcmBuffer->MethodStatus, + &EcmBuffer->OutputArgumentCount, + &CompleteStatus); + } + + // + // Return complete status. + // + + return CompleteStatus; +} + +// +// Notification handlers for \_SB.BUSD.TST1. +// + +PEP_NOTIFICATION_HANDLER_RESULT +Tst1SyncEvaluateControlMethod ( + _In_ PVOID Data, + _Out_opt_ PPEP_WORK_INFORMATION PoFxWorkInfo + ) + +/*++ + +Routine Description: + + This routine handles PEP_NOTIFY_ACPI_EVALUATE_CONTROL_METHOD + notification for the test device 1. It will try to evaluate the + _ADR and _DEP method synchronously, while leaving _CRS to the + asynchronous method. + +Arguments: + + Data - Supplies a pointer to parameters buffer for this notification. + + PoFxWorkInfo - Unused. + +Return Value: + + None. + +--*/ + +{ + + ULONG DeviceAdr; + PEP_NOTIFICATION_HANDLER_RESULT CompleteStatus; + PPEP_ACPI_EVALUATE_CONTROL_METHOD EcmBuffer; + + UNREFERENCED_PARAMETER(PoFxWorkInfo); + + DeviceAdr = TST1_ADR; + EcmBuffer = (PPEP_ACPI_EVALUATE_CONTROL_METHOD)Data; + CompleteStatus = PEP_NOTIFICATION_HANDLER_COMPLETE; + switch(EcmBuffer->MethodName) { + case ACPI_OBJECT_NAME_ADR: + PepReturnAcpiData( + &DeviceAdr, + ACPI_METHOD_ARGUMENT_INTEGER, + (ULONG)(sizeof(ULONG)), + FALSE, + EcmBuffer->OutputArguments, + &EcmBuffer->OutputArgumentSize, + &EcmBuffer->OutputArgumentCount, + &EcmBuffer->MethodStatus, + "ADR", + "TST1", + &CompleteStatus); + + break; + + case ACPI_OBJECT_NAME_DEP: + + // + // Test device 1 has a dependency on the GPIO controller. + // + + PepReturnAcpiData( + SAMPLE_GPIO_ACPI_NAME_ANSI, + ACPI_METHOD_ARGUMENT_STRING, + GET_STRING_LENGTH(SAMPLE_GPIO_ACPI_NAME_ANSI), + TRUE, + EcmBuffer->OutputArguments, + &EcmBuffer->OutputArgumentSize, + &EcmBuffer->OutputArgumentCount, + &EcmBuffer->MethodStatus, + "DEP", + "TST1", + &CompleteStatus); + + break; + + case ACPI_OBJECT_NAME_CRS: + ReportNeedMoreWork(&EcmBuffer->MethodStatus, + "CRS", + "TST1", + &CompleteStatus); + break; + + default: + ReportNotSupported(&EcmBuffer->MethodStatus, + &EcmBuffer->OutputArgumentCount, + &CompleteStatus); + } + + // + // Return complete status. + // + + return CompleteStatus; +} + +PEP_NOTIFICATION_HANDLER_RESULT +Tst1WorkerCallbackEvaluateControlMethod ( + _In_ PVOID Data, + _Out_opt_ PPEP_WORK_INFORMATION PoFxWorkInfo + ) + +/*++ + +Routine Description: + + This routine handles PEP_NOTIFY_ACPI_EVALUATE_CONTROL_METHOD + notification for the test device 1. It will be scheduled to run + asychronously. In this example, it only handles _CRS method. + +Arguments: + + Data - Supplies a pointer to parameters buffer for this notification. + + PoFxWorkInfo - Supplies a pointer to the PEP_WORK structure used to + report result to PoFx. + +Return Value: + + None. + +--*/ + +{ + + PEP_NOTIFICATION_HANDLER_RESULT CompleteStatus; + PPEP_ACPI_EVALUATE_CONTROL_METHOD EcmBuffer; + PPEP_INTERNAL_DEVICE_HEADER PepInternalDevice; + +#if !defined(ARM) && !defined(ARM64) + + PEP_ACPI_RESOURCE Resources[3]; + NT_ASSERT(sizeof(Resources) == 3*sizeof(PEP_ACPI_RESOURCE)); + +#else + + PEP_ACPI_RESOURCE Resources[2]; + +#endif + + SIZE_T RequiredSize; + NTSTATUS Status; + + TraceEvents(INFO, + DBG_PEP, + "%s : Asychronous method scheduled to run.\n", + __FUNCTION__); + + EcmBuffer = (PPEP_ACPI_EVALUATE_CONTROL_METHOD)Data; + PepInternalDevice = (PPEP_INTERNAL_DEVICE_HEADER)EcmBuffer->DeviceHandle; + CompleteStatus = PEP_NOTIFICATION_HANDLER_COMPLETE; + PoFxWorkInfo->WorkType = PepWorkAcpiEvaluateControlMethodComplete; + PoFxWorkInfo->ControlMethodComplete.DeviceHandle = + PepInternalDevice->KernelHandle; + + PoFxWorkInfo->ControlMethodComplete.CompletionFlags = 0; + PoFxWorkInfo->ControlMethodComplete.CompletionContext = + EcmBuffer->CompletionContext; + + switch(EcmBuffer->MethodName) { + case ACPI_OBJECT_NAME_CRS: + + // + // Test device 1 requires memory resource. + // + + PEP_ACPI_INITIALIZE_MEMORY_RESOURCE( + TST1_MEM_RES_RW, + TST1_MEM_RES_MIN_ADDR, + TST1_MEM_RES_MAX_ADDR, + TST1_MEM_RES_ALIGNMENT, + TST1_MEM_RES_MEMSIZE, + &Resources[0]); + + // + // Test device 1 needs INT resource. + // + + PEP_ACPI_INITIALIZE_INTERRUPT_RESOURCE( + FALSE, + TST1_INT_RES_EDGE_LVL, + TST1_INT_RES_INT_LVL, + TST1_INT_RES_SHARE_TYPE, + TST1_INT_RES_WAKE, + Tst1IntPinTable, + (UCHAR)(ARRAYSIZE(Tst1IntPinTable)), + &Resources[1]); + +#if !defined(ARM) || !defined(ARM64) + + // + // Test device 1 needs I/O port resource. + // + + PEP_ACPI_INITIALIZE_IOPORT_RESOURCE( + TST1_IO_RES_DECODE, + TST1_IO_RES_MIN_ADDR, + TST1_IO_RES_MAX_ADDR, + TST1_IO_RES_ALIGNMENT, + TST1_IO_RES_PORT_LEN, + &Resources[2]); + +#endif + + // + // Find the length required to hold the BIOS resources. + // + + Status = PepGetBiosResourceSize(Resources, + sizeof(Resources), + "TST1", + &RequiredSize); + + if(!NT_SUCCESS(Status)) { + PoFxWorkInfo->ControlMethodComplete.MethodStatus = Status; + PoFxWorkInfo->ControlMethodComplete.OutputArgumentSize = 0; + goto Tst1WorkerCallbackEvaluateControlMethodEnd; + } + + PepReturnBiosResource( + Resources, + sizeof(Resources), + RequiredSize, + PoFxWorkInfo->ControlMethodComplete.OutputArguments, + &PoFxWorkInfo->ControlMethodComplete.OutputArgumentSize, + "TST1", + &PoFxWorkInfo->ControlMethodComplete.MethodStatus); + + break; + + default: + ReportNotSupported( + &PoFxWorkInfo->ControlMethodComplete.MethodStatus, + (PULONG)(&PoFxWorkInfo->ControlMethodComplete.OutputArgumentSize), + &CompleteStatus); + } + + // + // Return complete status. + // + +Tst1WorkerCallbackEvaluateControlMethodEnd: + return CompleteStatus; +} + +PEP_NOTIFICATION_HANDLER_RESULT +Tst1SyncQueryControlResources ( + _In_ PVOID Data, + _Out_opt_ PPEP_WORK_INFORMATION PoFxWorkInfo + ) + +/*++ + +Routine Description: + + This routine handles PEP_NOTIFY_ACPI_QUERY_DEVICE_CONTROL_RESOURCES + notification for the test device 1. In this example, control resource + needed for the firmware is GPIO I/O. + +Arguments: + + Data - Supplies a pointer to parameters buffer for this notification. + + PoFxWorkInfo - Unused. + +Return Value: + + None. + +--*/ + +{ + + PEP_ACPI_RESOURCE GpioIoResource; + PPEP_ACPI_QUERY_DEVICE_CONTROL_RESOURCES QueryResourcesBuffer; + SIZE_T RequiredSize; + NTSTATUS Status; + + UNREFERENCED_PARAMETER(PoFxWorkInfo); + + TraceEvents(INFO, + DBG_PEP, + "%s : %s Start processing.\n", + __FUNCTION__, + PepAcpiNotificationHandlers[ + PEP_NOTIFY_ACPI_QUERY_DEVICE_CONTROL_RESOURCES].Name); + + QueryResourcesBuffer = (PPEP_ACPI_QUERY_DEVICE_CONTROL_RESOURCES)Data; + + // + // Initialize the GPIO IO resource that test device 1 needs. + // + + PEP_ACPI_INITIALIZE_GPIO_IO_RESOURCE( + FALSE, + FALSE, + PullDefault, + 0, + 0, + IoRestrictionNone, + 0, + &GpioResourceName, + FALSE, + NULL, + 0, + (PUSHORT)Tst1GpioPinTable, + (USHORT)(ARRAYSIZE(Tst1GpioPinTable)), + &GpioIoResource); + + // + // Find the length required to hold the BIOS resources. + // + + Status = PepGetBiosResourceSize(&GpioIoResource, + sizeof(PEP_ACPI_RESOURCE), + "TST1", + &RequiredSize); + + if(!NT_SUCCESS(Status)) { + QueryResourcesBuffer->Status = Status; + goto QueryControlResourcesEnd; + } + + PepReturnBiosResource(&GpioIoResource, + sizeof(PEP_ACPI_RESOURCE), + RequiredSize, + QueryResourcesBuffer->BiosResources, + &QueryResourcesBuffer->BiosResourcesSize, + "TST1", + &QueryResourcesBuffer->Status); + +QueryControlResourcesEnd: + return PEP_NOTIFICATION_HANDLER_COMPLETE; +} + +// +// Notification handlers for \_SB.BUSD.TST2. +// + +PEP_NOTIFICATION_HANDLER_RESULT +Tst2SyncEvaluateControlMethod ( + _In_ PVOID Data, + _Out_opt_ PPEP_WORK_INFORMATION PoFxWorkInfo + ) + +/*++ + +Routine Description: + + This routine handles PEP_NOTIFY_ACPI_EVALUATE_CONTROL_METHOD + notification for the test device 2. It will try to evaluate the + _ADR method synchronously, while leaving _CRS to the asynchronous + method. + +Arguments: + + Data - Supplies a pointer to parameters buffer for this notification. + + PoFxWorkInfo - Unused. + +Return Value: + + None. + +--*/ + +{ + + ULONG DeviceAdr; + PEP_NOTIFICATION_HANDLER_RESULT CompleteStatus; + PPEP_ACPI_EVALUATE_CONTROL_METHOD EcmBuffer; + + UNREFERENCED_PARAMETER(PoFxWorkInfo); + + DeviceAdr = TST2_ADR; + EcmBuffer = (PPEP_ACPI_EVALUATE_CONTROL_METHOD)Data; + CompleteStatus = PEP_NOTIFICATION_HANDLER_COMPLETE; + switch(EcmBuffer->MethodName) { + case ACPI_OBJECT_NAME_ADR: + PepReturnAcpiData( + &DeviceAdr, + ACPI_METHOD_ARGUMENT_INTEGER, + (ULONG)(sizeof(ULONG)), + FALSE, + EcmBuffer->OutputArguments, + &EcmBuffer->OutputArgumentSize, + &EcmBuffer->OutputArgumentCount, + &EcmBuffer->MethodStatus, + "ADR", + "TST2", + &CompleteStatus); + + break; + + case ACPI_OBJECT_NAME_CRS: + ReportNeedMoreWork(&EcmBuffer->MethodStatus, + "CRS", + "TST2", + &CompleteStatus); + break; + + default: + ReportNotSupported(&EcmBuffer->MethodStatus, + &EcmBuffer->OutputArgumentCount, + &CompleteStatus); + } + + // + // Return complete status. + // + + return CompleteStatus; +} + +PEP_NOTIFICATION_HANDLER_RESULT +Tst2WorkerCallbackEvaluateControlMethod ( + _In_ PVOID Data, + _Out_opt_ PPEP_WORK_INFORMATION PoFxWorkInfo + ) + +/*++ + +Routine Description: + + This routine handles PEP_NOTIFY_ACPI_EVALUATE_CONTROL_METHOD + notification for the test device 2. It will be scheduled to run + asychronously. In this example, it only handles _CRS method. + +Arguments: + + Data - Supplies a pointer to parameters buffer for this notification. + + PoFxWorkInfo - Supplies a pointer to the PEP_WORK structure used to + report result to PoFx. + +Return Value: + + None. + +--*/ + +{ + + PEP_NOTIFICATION_HANDLER_RESULT CompleteStatus; + PPEP_ACPI_EVALUATE_CONTROL_METHOD EcmBuffer; + PPEP_INTERNAL_DEVICE_HEADER PepInternalDevice; + PEP_ACPI_RESOURCE Resources[2]; + SIZE_T RequiredSize; + NTSTATUS Status; + + TraceEvents(INFO, + DBG_PEP, + "%s : Asychronous method scheduled to run.\n", + __FUNCTION__); + + EcmBuffer = (PPEP_ACPI_EVALUATE_CONTROL_METHOD)Data; + PepInternalDevice = (PPEP_INTERNAL_DEVICE_HEADER)EcmBuffer->DeviceHandle; + CompleteStatus = PEP_NOTIFICATION_HANDLER_COMPLETE; + PoFxWorkInfo->WorkType = PepWorkAcpiEvaluateControlMethodComplete; + PoFxWorkInfo->ControlMethodComplete.DeviceHandle = + PepInternalDevice->KernelHandle; + + PoFxWorkInfo->ControlMethodComplete.CompletionFlags = 0; + PoFxWorkInfo->ControlMethodComplete.CompletionContext = + EcmBuffer->CompletionContext; + + switch(EcmBuffer->MethodName) { + case ACPI_OBJECT_NAME_CRS: + + // + // Test device 2 needs GPIO INT resource. + // + + PEP_ACPI_INITIALIZE_GPIO_INT_RESOURCE( + TST2_GPIOINT_RES_INT_TYPE, + TST2_GPIOINT_RES_INT_LVL, + FALSE, + FALSE, + TST2_GPIOINT_RES_PIN_CONFIG, + TST2_GPIOINT_RES_DEBOUNCE, + 0, + &GpioResourceName, + FALSE, + NULL, + 0, + Tst2GpioIntPinTable, + (UCHAR)(ARRAYSIZE(Tst2GpioIntPinTable)), + &Resources[0]); + + // + // Test device 2 needs SPB resource. + // + + PEP_ACPI_INITIALIZE_SPB_I2C_RESOURCE( + TST2_SPB_RES_SLAVE_ADDR, + FALSE, + TST2_SPB_RES_CONN_SPD, + FALSE, + &SpbResourceName, + 0, + FALSE, + FALSE, + NULL, + 0, + &Resources[1]); + + // + // Find the length required to hold the BIOS resources. + // + + Status = PepGetBiosResourceSize(Resources, + sizeof(Resources), + "TST2", + &RequiredSize); + + if(!NT_SUCCESS(Status)) { + PoFxWorkInfo->ControlMethodComplete.MethodStatus = Status; + PoFxWorkInfo->ControlMethodComplete.OutputArgumentSize = 0; + goto Tst2WorkerCallbackEvaluateControlMethodEnd; + } + + PepReturnBiosResource(Resources, + sizeof(Resources), + RequiredSize, + PoFxWorkInfo->ControlMethodComplete.OutputArguments, + &PoFxWorkInfo->ControlMethodComplete.OutputArgumentSize, + "TST2", + &PoFxWorkInfo->ControlMethodComplete.MethodStatus); + + break; + + default: + ReportNotSupported(&PoFxWorkInfo->ControlMethodComplete.MethodStatus, + (PULONG)(&PoFxWorkInfo-> + ControlMethodComplete.OutputArgumentSize), + &CompleteStatus); + } + + // + // Return complete status. + // + +Tst2WorkerCallbackEvaluateControlMethodEnd: + return CompleteStatus; +} + +PEP_NOTIFICATION_HANDLER_RESULT +Tst3SyncEvaluateControlMethod ( + _In_ PVOID Data, + _Out_opt_ PPEP_WORK_INFORMATION PoFxWorkInfo + ) + +/*++ + +Routine Description: + + This routine handles PEP_NOTIFY_ACPI_EVALUATE_CONTROL_METHOD + notification for the test device 1. It will be scheduled to run + asychronously. In this example, it only handles _CRS method. + +Arguments: + + Data - Supplies a pointer to parameters buffer for this notification. + + PoFxWorkInfo - Supplies a pointer to the PEP_WORK structure used to + report result to PoFx. + +Return Value: + + None. + +--*/ + +{ + + PACPI_METHOD_ARGUMENT InputArgument; + LPGUID DsmUUID; + PEP_NOTIFICATION_HANDLER_RESULT CompleteStatus; + PPEP_ACPI_EVALUATE_CONTROL_METHOD EcmBuffer; + PVOID FunctionArguments; + ULONG FunctionArgumentSize; + ULONG FunctionIndex; + NTSTATUS LocalStatus; + PPEP_INTERNAL_DEVICE_HEADER PepInternalDevice; + ULONG RevisionLevel; + ULONG UuidIndex; + + UNREFERENCED_PARAMETER(PoFxWorkInfo); + + EcmBuffer = (PPEP_ACPI_EVALUATE_CONTROL_METHOD)Data; + PepInternalDevice = (PPEP_INTERNAL_DEVICE_HEADER)EcmBuffer->DeviceHandle; + CompleteStatus = PEP_NOTIFICATION_HANDLER_COMPLETE; + switch(EcmBuffer->MethodName) { + case ACPI_OBJECT_NAME_HID: + PepReturnAcpiData( + TST3_HID, + ACPI_METHOD_ARGUMENT_STRING, + GET_STRING_LENGTH(TST3_HID), + FALSE, + EcmBuffer->OutputArguments, + &EcmBuffer->OutputArgumentSize, + &EcmBuffer->OutputArgumentCount, + &EcmBuffer->MethodStatus, + "CRS", + "TST3", + &CompleteStatus); + + break; + + case ACPI_OBJECT_NAME_CID: + LocalStatus = Tst3CreateSelfManagedCidRequest(EcmBuffer); + if (NT_SUCCESS(LocalStatus)) { + ReportNeedMoreSelfManagedWork(&EcmBuffer->MethodStatus, + "CID", + "TST3", + &CompleteStatus); + + } else { + TraceEvents(ERROR, + DBG_PEP, + "%s [TST3 _CID failed with status = %#x!\n", + PepAcpiNotificationHandlers[ + PEP_NOTIFY_ACPI_EVALUATE_CONTROL_METHOD].Name, + LocalStatus); + + EcmBuffer->MethodStatus = LocalStatus; + EcmBuffer->OutputArgumentCount = 0; + EcmBuffer->OutputArgumentSize = 0; + CompleteStatus = PEP_NOTIFICATION_HANDLER_COMPLETE; + } + + break; + + case ACPI_OBJECT_NAME_PR0: + case ACPI_OBJECT_NAME_PR2: + + // + // Test device 3 needs power rail device. + // + + PepReturnAcpiData( + SAMPLE_PWRR_ACPI_NAME_ANSI, + ACPI_METHOD_ARGUMENT_STRING, + GET_STRING_LENGTH(SAMPLE_PWRR_ACPI_NAME_ANSI), + TRUE, + EcmBuffer->OutputArguments, + &EcmBuffer->OutputArgumentSize, + &EcmBuffer->OutputArgumentCount, + &EcmBuffer->MethodStatus, + "CRS", + "TST3", + &CompleteStatus); + + break; + + case ACPI_OBJECT_NAME_DSM: + + // + // Test device 3 defines 2 DSMs. + // + + // + // _DSM requires 4 input arguments. + // + + EcmBuffer->OutputArgumentCount = 0; + EcmBuffer->OutputArgumentSize = 0; + if (EcmBuffer->InputArgumentCount != 4) { + TraceEvents(ERROR, + DBG_PEP, + "%s : Invalid number of DSM input arguments." + "Required = 4, Provided = %d.\n", + __FUNCTION__, + EcmBuffer->InputArgumentCount); + + EcmBuffer->MethodStatus = STATUS_INVALID_PARAMETER; + goto Tst3SyncEvaluateControlMethodEnd; + } + + // + // The first argument must be the UUID of the DSM. + // + + InputArgument = EcmBuffer->InputArguments; + if (InputArgument->Type != ACPI_METHOD_ARGUMENT_BUFFER) { + TraceEvents(ERROR, + DBG_PEP, + "%s : Invalid type of the first DSM argument." + "Required = ACPI_METHOD_ARGUMENT_BUFFER, " + "Provided = %d.\n", + __FUNCTION__, + InputArgument->Type); + + EcmBuffer->MethodStatus = STATUS_INVALID_PARAMETER_1; + goto Tst3SyncEvaluateControlMethodEnd; + } + + if (InputArgument->DataLength != (USHORT)(sizeof(GUID))) { + TraceEvents(ERROR, + DBG_PEP, + "%s : Invalid size of the first DSM argument." + "Required = %d, Provided = %d.\n", + __FUNCTION__, + (ULONG)(sizeof(GUID)), + (ULONG)(InputArgument->DataLength)); + + EcmBuffer->MethodStatus = STATUS_INVALID_PARAMETER_1; + goto Tst3SyncEvaluateControlMethodEnd; + } + + DsmUUID = (LPGUID)(&InputArgument->Data[0]); + + // + // The second argument must be the revision level. + // + + InputArgument = ACPI_METHOD_NEXT_ARGUMENT(InputArgument); + if (InputArgument->Type != ACPI_METHOD_ARGUMENT_INTEGER) { + TraceEvents(ERROR, + DBG_PEP, + "%s : Invalid type of the second DSM argument." + "Required = ACPI_METHOD_ARGUMENT_INTEGER, " + "Provided = %d.\n", + __FUNCTION__, + InputArgument->Type); + + EcmBuffer->MethodStatus = STATUS_INVALID_PARAMETER_2; + goto Tst3SyncEvaluateControlMethodEnd; + } + + RevisionLevel = InputArgument->Argument; + + // + // The third argument must be function index. + // + + InputArgument = ACPI_METHOD_NEXT_ARGUMENT(InputArgument); + if (InputArgument->Type != ACPI_METHOD_ARGUMENT_INTEGER) { + TraceEvents(ERROR, + DBG_PEP, + "%s : Invalid type of the third DSM argument." + "Required = ACPI_METHOD_ARGUMENT_INTEGER, " + "Provided = %d.\n", + __FUNCTION__, + InputArgument->Type); + + EcmBuffer->MethodStatus = STATUS_INVALID_PARAMETER_3; + goto Tst3SyncEvaluateControlMethodEnd; + } + + FunctionIndex = InputArgument->Argument; + + // + // The fourth argument must be a package. + // + + InputArgument = ACPI_METHOD_NEXT_ARGUMENT(InputArgument); + if (InputArgument->Type != ACPI_METHOD_ARGUMENT_PACKAGE && + InputArgument->Type != ACPI_METHOD_ARGUMENT_PACKAGE_EX) { + TraceEvents(ERROR, + DBG_PEP, + "%s : Invalid type of the fourth DSM argument." + "Required = ACPI_METHOD_ARGUMENT_PACKAGE(_EX), " + "Provided = %d.\n", + __FUNCTION__, + InputArgument->Type); + + EcmBuffer->MethodStatus = STATUS_INVALID_PARAMETER_4; + goto Tst3SyncEvaluateControlMethodEnd; + } + + FunctionArguments = + &(((PACPI_METHOD_ARGUMENT)(&InputArgument->Data[0]))->Data[0]); + + FunctionArgumentSize = + (ULONG)(((PACPI_METHOD_ARGUMENT) + (&InputArgument->Data[0]))->DataLength); + + // + // Check the first input argument to decide which UUID is called, and + // invoke the corresponding methods. + // + + UuidIndex = 0; + if (RtlCompareMemory(DsmUUID, &TST3_DSM_UUID1, sizeof(GUID)) == 0) { + UuidIndex = 1; + + } else if (RtlCompareMemory(DsmUUID, &TST3_DSM_UUID2, sizeof(GUID)) == 0) { + UuidIndex = 2; + + } else { + UuidIndex = 0; + } + + HandleTst3DsmByUuid(UuidIndex, + RevisionLevel, + FunctionIndex, + FunctionArguments, + FunctionArgumentSize, + EcmBuffer, + &CompleteStatus); + + break; + + default: + ReportNotSupported(&EcmBuffer->MethodStatus, + &EcmBuffer->OutputArgumentCount, + &CompleteStatus); + } + + // + // Return complete status. + // + +Tst3SyncEvaluateControlMethodEnd: + return CompleteStatus; +} + +NTSTATUS +Tst3CreateSelfManagedCidRequest ( + _In_ PPEP_ACPI_EVALUATE_CONTROL_METHOD EcmBuffer + ) + +/*++ + +Routine Description: + + This routine handles PEP_NOTIFY_ACPI_EVALUATE_CONTROL_METHOD for _CID + method on the TST3 device asynchronously in a self-managed manner (i.e., + doesn't rely on the common library worker). + +Arguments: + + EcmBuffer - Supplies a pointer to the PEP_ACPI_EVALUATE_CONTROL_METHOD + buffer. + +Return Value: + + NTSTATUS code. + +--*/ + +{ + + WDF_OBJECT_ATTRIBUTES Attributes; + PTST3_CID_REQUEST_CONTEXT Context; + NTSTATUS Status; + WDFWORKITEM WorkItem; + WDF_WORKITEM_CONFIG WorkItemConfiguration; + + WorkItem = NULL; + WDF_OBJECT_ATTRIBUTES_INIT(&Attributes); + WDF_OBJECT_ATTRIBUTES_SET_CONTEXT_TYPE(&Attributes, + TST3_CID_REQUEST_CONTEXT); + + Attributes.ParentObject = PepGlobalWdfDevice; + + // + // Initialize the handler routine and create a new workitem. + // + + WDF_WORKITEM_CONFIG_INIT(&WorkItemConfiguration, + Tst3SelfManagedCidRequestWorkerWrapper); + + WorkItemConfiguration.AutomaticSerialization = FALSE; + + // + // Create the work item and queue it. + // + + Status = WdfWorkItemCreate(&WorkItemConfiguration, + &Attributes, + &WorkItem); + + if (!NT_SUCCESS(Status)) { + TraceEvents(ERROR, + DBG_PEP, + "Failed to allocate work item for TST3 _CID request!" + "Status = %#x\n", + Status); + + goto Tst3CreateSelfManagedCidRequestEnd; + } + + Context = Tst3GetCidRequestContext(WorkItem); + + // + // Initialize the ECM buffer in the context to be supplied to the workitem + // handler. + // + + RtlZeroMemory(Context, sizeof(TST3_CID_REQUEST_CONTEXT)); + RtlCopyMemory(&Context->EcmBuffer, + EcmBuffer, + sizeof(PEP_ACPI_EVALUATE_CONTROL_METHOD)); + + // + // Queue a workitem to run the worker routine. + // + + WdfWorkItemEnqueue(WorkItem); + Status = STATUS_SUCCESS; + +Tst3CreateSelfManagedCidRequestEnd: + return Status; +} + +VOID +Tst3SelfManagedCidRequestWorkerWrapper ( + __in WDFWORKITEM WorkItem + ) + +/*++ + +Routine Description: + + This function is a wrapper that invokes the appropriate handler. + +Arguments: + + WorkItem - Supplies a handle to the workitem supplying the context. + +Return Value: + + None. + +--*/ + +{ + + PTST3_CID_REQUEST_CONTEXT Context; + + Context = Tst3GetCidRequestContext(WorkItem); + Tst3ProcessSelfManagedCidRequest(Context); + + // + // Delete the work item as it is no longer required. + // + + WdfObjectDelete(WorkItem); + return; +} + +VOID +Tst3ProcessSelfManagedCidRequest ( + _In_ PTST3_CID_REQUEST_CONTEXT Tst3CidRequestContext + ) + +/*++ + +Routine Description: + + This routine handles PEP_NOTIFY_ACPI_EVALUATE_CONTROL_METHOD for _CID + method on the TST3 device asynchronously in a self-managed manner (i.e., + doesn't rely on the common library worker). + +Arguments: + + Tst3CidRequestContext - Supplies a pointer to request context. + +Return Value: + + None. + +--*/ + +{ + + PEP_NOTIFICATION_HANDLER_RESULT CompleteStatus; + PPEP_ACPI_EVALUATE_CONTROL_METHOD EcmBuffer; + PPEP_INTERNAL_DEVICE_HEADER PepInternalDevice; + PEP_WORK_INFORMATION WorkInformation; + + EcmBuffer = + (PPEP_ACPI_EVALUATE_CONTROL_METHOD) + &Tst3CidRequestContext->EcmBuffer; + + PepInternalDevice = (PPEP_INTERNAL_DEVICE_HEADER)EcmBuffer->DeviceHandle; + CompleteStatus = PEP_NOTIFICATION_HANDLER_COMPLETE; + switch(EcmBuffer->MethodName) { + case ACPI_OBJECT_NAME_CID: + PepReturnAcpiData( + TST3_CID, + ACPI_METHOD_ARGUMENT_STRING, + GET_STRING_LENGTH(TST3_CID), + FALSE, + EcmBuffer->OutputArguments, + &EcmBuffer->OutputArgumentSize, + &EcmBuffer->OutputArgumentCount, + &EcmBuffer->MethodStatus, + "CID", + "TST3", + &CompleteStatus); + + break; + + default: + ReportNotSupported(&EcmBuffer->MethodStatus, + &EcmBuffer->OutputArgumentCount, + &CompleteStatus); + + EcmBuffer->OutputArgumentSize = 0; + } + + // + // Fill PEP_WORK_INFORMATION based on the ACPI data to be returned. + // + + RtlZeroMemory(&WorkInformation, sizeof(PEP_WORK_INFORMATION)); + WorkInformation.WorkType = PepWorkAcpiEvaluateControlMethodComplete; + WorkInformation.ControlMethodComplete.DeviceHandle = + PepInternalDevice->KernelHandle; + + WorkInformation.ControlMethodComplete.CompletionFlags = 0; + WorkInformation.ControlMethodComplete.CompletionContext = + EcmBuffer->CompletionContext; + + WorkInformation.ControlMethodComplete.MethodStatus = + EcmBuffer->MethodStatus; + + WorkInformation.ControlMethodComplete.OutputArgumentSize = + EcmBuffer->OutputArgumentSize; + + WorkInformation.ControlMethodComplete.OutputArguments = + EcmBuffer->OutputArguments; + + PepCompleteSelfManagedWork( + PEP_NOTIFICATION_CLASS_ACPI, + PEP_NOTIFY_ACPI_EVALUATE_CONTROL_METHOD, + PepInternalDevice, + &WorkInformation); + + return; +} + +// +// Notification handlers for \_SB.PWRR. +// + +PEP_NOTIFICATION_HANDLER_RESULT +PwrrSyncEvaluateControlMethod ( + _In_ PVOID Data, + _Out_opt_ PPEP_WORK_INFORMATION PoFxWorkInfo + ) + +/*++ + +Routine Description: + + This routine handles PEP_NOTIFY_ACPI_EVALUATE_CONTROL_METHOD + notification for the power rail device. It will evaluate _HID, _STA, + _ON and _OFF synchronously. + +Arguments: + + Data - Supplies a pointer to parameters buffer for this notification. + + PoFxWorkInfo - Unused. + +Return Value: + + None. + +--*/ + +{ + + PEP_NOTIFICATION_HANDLER_RESULT CompleteStatus; + PPEP_ACPI_EVALUATE_CONTROL_METHOD EcmBuffer; + + UNREFERENCED_PARAMETER(PoFxWorkInfo); + + EcmBuffer = (PPEP_ACPI_EVALUATE_CONTROL_METHOD)Data; + CompleteStatus = PEP_NOTIFICATION_HANDLER_COMPLETE; + switch(EcmBuffer->MethodName) { + case ACPI_OBJECT_NAME_HID: + PepReturnAcpiData( + PWRR_HID, + ACPI_METHOD_ARGUMENT_STRING, + GET_STRING_LENGTH(PWRR_HID), + FALSE, + EcmBuffer->OutputArguments, + &EcmBuffer->OutputArgumentSize, + &EcmBuffer->OutputArgumentCount, + &EcmBuffer->MethodStatus, + "HID", + "PWRR", + &CompleteStatus); + + break; + + case ACPI_OBJECT_NAME_STA: + PepReturnAcpiData( + &PowerRailState, + ACPI_METHOD_ARGUMENT_INTEGER, + (ULONG)(sizeof(ULONG)), + FALSE, + EcmBuffer->OutputArguments, + &EcmBuffer->OutputArgumentSize, + &EcmBuffer->OutputArgumentCount, + &EcmBuffer->MethodStatus, + "STA", + "PWRR", + &CompleteStatus); + + break; + + case ACPI_OBJECT_NAME_ON: + case ACPI_OBJECT_NAME_OFF: + if (EcmBuffer->MethodName == ACPI_OBJECT_NAME_ON) { + PowerRailState = 1; + TraceEvents(INFO, + DBG_PEP, + "%s : %s Power rail is turned ON.\n", + __FUNCTION__, + PepAcpiNotificationHandlers[ + PEP_NOTIFY_ACPI_EVALUATE_CONTROL_METHOD].Name); + + } else { + PowerRailState = 0; + TraceEvents(INFO, + DBG_PEP, + "%s : %s Power rail is turned OFF.\n", + __FUNCTION__, + PepAcpiNotificationHandlers[ + PEP_NOTIFY_ACPI_EVALUATE_CONTROL_METHOD].Name); + } + + EcmBuffer->OutputArgumentCount = 0; + EcmBuffer->OutputArgumentSize = 0; + EcmBuffer->MethodStatus = STATUS_SUCCESS; + break; + + default: + ReportNotSupported(&EcmBuffer->MethodStatus, + &EcmBuffer->OutputArgumentCount, + &CompleteStatus); + } + + // + // Return complete status. + // + + return CompleteStatus; +} + +// +// Helper functions. +// + +VOID +ReportNeedMoreWork ( + _Out_ PNTSTATUS Status, + _In_opt_ PCHAR MethodName, + _In_opt_ PCHAR DebugInfo, + _Out_ PPEP_NOTIFICATION_HANDLER_RESULT CompleteResult + ) + +/*++ + +Routine Description: + + This routine reports to PoFx that more work is needed. + +Arguments: + + Status - Supplies a pointer to receive the STATUS_PENDING. + + MethodName - Supplies an optional string that names the native method + used for logging. + + DebugInfo - Supplies an optional string that contains the debugging + information to be included in the log. + + CompleteResult - Supplies a pointer to receive the complete result. + +Return Value: + + None. + +--*/ + +{ + + *Status = STATUS_PENDING; + TraceEvents(INFO, + DBG_PEP, + "%s <%s> [%s] More work needed.\n", + __FUNCTION__, + NAME_DEBUG_INFO(DebugInfo), + NAME_NATIVE_METHOD(MethodName)); + + // + // Do nothing here but set the return status to + // PEP_NOTIFICATION_HANDLER_MORE_WORK, so that the async method will + // be invoked instead. + // + + *CompleteResult = PEP_NOTIFICATION_HANDLER_MORE_WORK; + return; +} + +VOID +ReportNotSupported ( + _Out_ PNTSTATUS Status, + _Out_ PULONG Count, + _Out_ PPEP_NOTIFICATION_HANDLER_RESULT CompleteResult + ) + +/*++ + +Routine Description: + + This routine reports to PoFx that the notification is not supported. + +Arguments: + + Status - Supplies a pointer to receive the evaluation status. + + Count - Supplies a pointer to receive the output argument count/size. + + CompleteResult - Supplies a pointer to receive the complete result. + +Return Value: + + None. + +--*/ + +{ + + *Count = 0; + *Status = STATUS_NOT_SUPPORTED; + TraceEvents(ERROR, + DBG_PEP, + "%s [UNKNOWN] Native method not supported.\n", + PepAcpiNotificationHandlers[ + PEP_NOTIFY_ACPI_EVALUATE_CONTROL_METHOD].Name); + + *CompleteResult = PEP_NOTIFICATION_HANDLER_COMPLETE; + return; +} + +VOID +ReportNeedMoreSelfManagedWork ( + _Out_ PNTSTATUS Status, + _In_opt_ PCHAR MethodName, + _In_opt_ PCHAR DebugInfo, + _Out_ PPEP_NOTIFICATION_HANDLER_RESULT CompleteResult + ) + +/*++ + +Routine Description: + + This routine reports to PoFx that more work is needed. + +Arguments: + + Status - Supplies a pointer to receive the STATUS_PENDING. + + MethodName - Supplies an optional string that names the native method + used for logging. + + DebugInfo - Supplies an optional string that contains the debugging + information to be included in the log. + + CompleteResult - Supplies a pointer to receive the complete result. + +Return Value: + + None. + +--*/ + +{ + + *Status = STATUS_PENDING; + TraceEvents(INFO, + DBG_PEP, + "%s <%s> [%s] More work needed - SELF Managed.\n", + __FUNCTION__, + NAME_DEBUG_INFO(DebugInfo), + NAME_NATIVE_METHOD(MethodName)); + + // + // Set the status to PEP_NOTIFICATION_HANDLER_COMPLETE to prevent the + // common library from invoking the async method. Instead, the work will + // be reported as finally complete when desired. + // + + *CompleteResult = PEP_NOTIFICATION_HANDLER_COMPLETE; + return; +} + +VOID +HandleTst3DsmByUuid ( + _In_ ULONG UuidIndex, + _In_ ULONG RevisionLevel, + _In_ ULONG FunctionIndex, + _In_ PVOID Parameters, + _In_ ULONG ParametersSize, + _Out_ PPEP_ACPI_EVALUATE_CONTROL_METHOD EcmBuffer, + _Out_ PPEP_NOTIFICATION_HANDLER_RESULT CompleteResult + ) + +/*++ + +Routine Description: + + This routine handles the DSM for test device 3 and report the result + back to PoFx. + +Arguments: + + UuidIndex - Supplies which DSM to handle. + 1 = TST3_DSM_UUID1; + 2 = TST3_DSM_UUID2; + anything else = UUID not supported. + + RevisionLevel - Supplies the revision of the DSM. + + FunctionIndex - Supplies the function index of the DSM. + + Parameters - Supplies the parameters for the DSM. + + ParametersSize - Supplies the size of the parameter buffer. + + EcmBuffer - Supplies a pointer to the output buffer. + + CompleteResult - Supplies a pointer to receive the complete result. + +Return Value: + + None. + +--*/ + +{ + + UCHAR ReturnBuffer[1]; + ULONG ReturnInteger; + NTSTATUS ReturnStatus; + USHORT ReturnType; + + UNREFERENCED_PARAMETER(Parameters); + UNREFERENCED_PARAMETER(ParametersSize); + + RtlZeroMemory(ReturnBuffer, sizeof(ReturnBuffer)); + ReturnInteger = 0; + ReturnStatus = STATUS_SUCCESS; + switch (UuidIndex) { + case 1: // TST3_DSM_UUID1 + switch (FunctionIndex) { + case 0: + + // + // If function index is 0, the return must be a buffer containing + // one bit for each supported function index. + // + + ReturnType = ACPI_METHOD_ARGUMENT_BUFFER; + + // + // Return a bitmap containing the supported function + // indicies according to the revision level. In this + // example, for DSM TST3_DSM_UUID1 revision 1, function + // 1 is supported. For revision 2, function 1 and 2 are + // supported. For any other revision, function 1 ~ 3 are + // supported. (Note that function 0 is always supported). + // + + switch (RevisionLevel) { + case 1: + ReturnBuffer[0] = 0x3; + break; + + case 2: + ReturnBuffer[0] = 0x7; + break; + + default: + ReturnBuffer[0] = 0xF; + break; + } + + break; + + case 1: + + // + // In this example, for simplicity, all the functions will just + // return their function index as integer. + // + + ReturnType = ACPI_METHOD_ARGUMENT_INTEGER; + ReturnInteger = 1; + break; + + case 2: + + // + // Check the revision here to make sure revision level is enough + // to run function 2, do the same for function 3. + // + + ReturnType = ACPI_METHOD_ARGUMENT_INTEGER; + if (RevisionLevel < 2) { + ReturnStatus = STATUS_INVALID_PARAMETER; + + } else { + ReturnInteger = 2; + } + + break; + + case 3: + ReturnType = ACPI_METHOD_ARGUMENT_INTEGER; + if (RevisionLevel < 3) { + ReturnStatus = STATUS_INVALID_PARAMETER; + + } else { + ReturnInteger = 3; + } + + break; + + default: + ReturnType = ACPI_METHOD_ARGUMENT_INTEGER; + ReturnStatus = STATUS_INVALID_PARAMETER; + break; + } + + break; + + case 2: // TST3_DSM_UUID2 + switch (FunctionIndex) { + case 0: + ReturnType = ACPI_METHOD_ARGUMENT_BUFFER; + + // + // In this example, for DSM TST3_DSM_UUID2, only function 1 + // is supported. + // + + ReturnBuffer[0] = 0x3; + break; + + case 1: + ReturnType = ACPI_METHOD_ARGUMENT_INTEGER; + ReturnInteger = 1; + break; + + default: + ReturnType = ACPI_METHOD_ARGUMENT_INTEGER; + ReturnStatus = STATUS_INVALID_PARAMETER; + break; + } + + break; + + default: // DSM not supported. + + // + // If the UUID is not supported, the return must be a buffer with + // bit 0 set to 0. + // + + ReturnType = ACPI_METHOD_ARGUMENT_BUFFER; + ReturnBuffer[0] = 0x0; + break; + } + + // + // Return the value back to PoFx. + // + + EcmBuffer->MethodStatus = ReturnStatus; + if (!NT_SUCCESS(ReturnStatus)) { + EcmBuffer->OutputArgumentCount = 0; + EcmBuffer->OutputArgumentSize = 0; + return; + } + + switch (ReturnType) { + case ACPI_METHOD_ARGUMENT_BUFFER: + PepReturnAcpiData( + ReturnBuffer, + ACPI_METHOD_ARGUMENT_BUFFER, + (ULONG)(sizeof(ReturnBuffer)), + FALSE, + EcmBuffer->OutputArguments, + &EcmBuffer->OutputArgumentSize, + &EcmBuffer->OutputArgumentCount, + &EcmBuffer->MethodStatus, + "DSM", + "TST3", + CompleteResult); + + return; + + case ACPI_METHOD_ARGUMENT_INTEGER: + PepReturnAcpiData( + &ReturnInteger, + ACPI_METHOD_ARGUMENT_INTEGER, + (ULONG)(sizeof(ULONG)), + FALSE, + EcmBuffer->OutputArguments, + &EcmBuffer->OutputArgumentSize, + &EcmBuffer->OutputArgumentCount, + &EcmBuffer->MethodStatus, + "DSM", + "TST3", + CompleteResult); + + return; + + default: + NT_ASSERT(FALSE); + return; + } +} + + diff --git a/pofx/PEP/common/acpinotify.c b/pofx/PEP/common/acpinotify.c new file mode 100644 index 000000000..f9d524adb --- /dev/null +++ b/pofx/PEP/common/acpinotify.c @@ -0,0 +1,716 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + acpinotify.c + +Abstract: + + This module implements PEP device notifications. + + +Environment: + + Kernel mode + +--*/ + +// +//-------------------------------------------------------------------- Includes +// + +#include "pch.h" + +#if defined(EVENT_TRACING) +#include "acpinotify.tmh" +#endif + +// +//--------------------------------------------------------------------- Pragmas +// + +#pragma alloc_text(PAGE, PepAcpiAbandonDevice) + +// +//------------------------------------------------------------------- Functions +// + +VOID +PepAcpiPrepareDevice ( + _Inout_updates_bytes_(sizeof(PEP_ACPI_PREPARE_DEVICE)) PVOID Data + ) + +/*++ + +Routine Description: + + This routine is called by default to prepare a device to be created. + +Arguments: + + Data - Supplies a pointer to a PEP_ACPI_PREPARE_DEVICE structure. + +Return Value: + + None. + +--*/ + +{ + + PPEP_ACPI_PREPARE_DEVICE AcpiPrepareDevice; + PPEP_DEVICE_DEFINITION DeviceDefinition; + + DeviceDefinition = NULL; + AcpiPrepareDevice = (PPEP_ACPI_PREPARE_DEVICE)Data; + AcpiPrepareDevice->OutputFlags = PEP_ACPI_PREPARE_DEVICE_OUTPUT_FLAG_NONE; + AcpiPrepareDevice->DeviceAccepted = + PepIsDeviceAccepted(PEP_NOTIFICATION_CLASS_ACPI, + AcpiPrepareDevice->AcpiDeviceName, + &DeviceDefinition); + + TraceEvents(INFO, + DBG_PEP, + "%s: %s: Device = %S, Accepted = %d.\n", + __FUNCTION__, + PepAcpiNotificationHandlers[ + PEP_NOTIFY_ACPI_PREPARE_DEVICE].Name, + AcpiPrepareDevice->AcpiDeviceName->Buffer, + (ULONG)AcpiPrepareDevice->DeviceAccepted); + + return; +} + +VOID +PepAcpiAbandonDevice ( + _Inout_updates_bytes_(sizeof(PEP_ACPI_ABANDON_DEVICE)) PVOID Data + ) + +/*++ + +Routine Description: + + This routine is called to abandon a device once it is being removed. + +Arguments: + + Data - Supplies a pointer to a PEP_ACPI_ABANDON_DEVICE structure. + +Return Value: + + None. + +--*/ + +{ + + PPEP_ACPI_ABANDON_DEVICE AcpiAbandonDevice; + PPEP_DEVICE_DEFINITION DeviceDefinition; + + PAGED_CODE(); + + AcpiAbandonDevice = (PPEP_ACPI_ABANDON_DEVICE)Data; + AcpiAbandonDevice->DeviceAccepted = + PepIsDeviceAccepted(PEP_NOTIFICATION_CLASS_ACPI, + AcpiAbandonDevice->AcpiDeviceName, + &DeviceDefinition); + + TraceEvents(INFO, + DBG_PEP, + "%s: %s: Device = %S, Accepted = %d.\n", + __FUNCTION__, + PepAcpiNotificationHandlers[ + PEP_NOTIFY_ACPI_ABANDON_DEVICE].Name, + AcpiAbandonDevice->AcpiDeviceName->Buffer, + AcpiAbandonDevice->DeviceAccepted); + + return; +} + +VOID +PepAcpiRegisterDevice ( + _Inout_updates_bytes_(sizeof(PEP_ACPI_REGISTER_DEVICE)) PVOID Data + ) + +/*++ + +Routine Description: + + This routine is called to claim responsibility for a device. As part of + registration, it will invoke the device-specific registered routine if + one is supplied. + +Arguments: + + Data - Supplies a pointer to a PEP_ACPI_REGISTER_DEVICE structure. + +Return Value: + + None. + +--*/ + +{ + + BOOLEAN DeviceAccepted; + PPEP_DEVICE_DEFINITION DeviceDefinition; + ULONG InstancePathOffset; + KIRQL Irql; + PPEP_INTERNAL_DEVICE_HEADER PepInternalDevice; + PPEP_ACPI_REGISTER_DEVICE RegisterDevice; + ULONG SizeNeeded; + NTSTATUS Status; + + PepInternalDevice = NULL; + RegisterDevice = (PPEP_ACPI_REGISTER_DEVICE)Data; + DeviceAccepted = PepIsDeviceAccepted(PEP_NOTIFICATION_CLASS_ACPI, + RegisterDevice->AcpiDeviceName, + &DeviceDefinition); + + if (DeviceAccepted == FALSE) { + TraceEvents(ERROR, + DBG_PEP, + "%s: %s: Device registration routine " + "failed. Device = %S.\n", + __FUNCTION__, + PepAcpiNotificationHandlers[ + PEP_NOTIFY_ACPI_REGISTER_DEVICE].Name, + RegisterDevice->AcpiDeviceName->Buffer); + + RegisterDevice->DeviceHandle = NULL; + goto RegisterDeviceEnd; + } + + InstancePathOffset = ALIGN_UP_BY(DeviceDefinition->ContextSize, + sizeof(WCHAR)); + + SizeNeeded = InstancePathOffset + + RegisterDevice->AcpiDeviceName->Length + + sizeof(WCHAR); + + PepInternalDevice = ExAllocatePoolWithTag(NonPagedPool, + SizeNeeded, + PEP_POOL_TAG); + + if (PepInternalDevice == NULL) { + TraceEvents(ERROR, + DBG_PEP, + "%s: %s: Failed to allocate internal " + "device context.\n", + __FUNCTION__, + PepAcpiNotificationHandlers[ + PEP_NOTIFY_ACPI_REGISTER_DEVICE].Name); + + RegisterDevice->DeviceHandle = NULL; + goto RegisterDeviceEnd; + } + + RtlZeroMemory(PepInternalDevice, SizeNeeded); + KeInitializeSpinLock(&PepInternalDevice->Lock); + PepInternalDevice->KernelHandle = RegisterDevice->KernelHandle; + PepInternalDevice->DeviceType = DeviceDefinition->Type; + PepInternalDevice->DeviceDefinition = DeviceDefinition; + PepInternalDevice->InstancePath = OffsetToPtr(PepInternalDevice, + InstancePathOffset); + + RtlCopyMemory(PepInternalDevice->InstancePath, + RegisterDevice->AcpiDeviceName->Buffer, + RegisterDevice->AcpiDeviceName->Length); + + // + // Invoke the device initialization routine if one is supplied. + // + + if (DeviceDefinition->Initialize != NULL) { + Status = DeviceDefinition->Initialize(PepInternalDevice); + if(!NT_SUCCESS(Status)) { + TraceEvents(ERROR, + DBG_PEP, + "%s: %s: Device initialization " + "routine failed. Status = %!STATUS!.\n", + __FUNCTION__, + PepAcpiNotificationHandlers[ + PEP_NOTIFY_ACPI_REGISTER_DEVICE].Name, + Status); + + RegisterDevice->DeviceHandle = NULL; + goto RegisterDeviceEnd; + } + } + + // + // Invoke the device-specific registered routine if one is supplied. + // + + PepInvokeNotificationHandler(PEP_NOTIFICATION_CLASS_ACPI, + NULL, + PepHandlerTypeSyncCritical, + PEP_NOTIFY_ACPI_REGISTER_DEVICE, + PepInternalDevice, + Data, + sizeof(PEP_ACPI_REGISTER_DEVICE), + NULL); + + // + // Store the device inside the internal list. + // + + KeAcquireSpinLock(&PepGlobalSpinLock, &Irql); + InsertTailList(&PepDeviceList, &PepInternalDevice->ListEntry); + KeReleaseSpinLock(&PepGlobalSpinLock,Irql); + + // + // Return the ACPI handle back to the PoFx. + // + + RegisterDevice->DeviceHandle = (PEPHANDLE)PepInternalDevice; + RegisterDevice->OutputFlags = PEP_ACPI_REGISTER_DEVICE_OUTPUT_FLAG_NONE; + TraceEvents(INFO, + DBG_PEP, + "%s: %s: SUCCESS! Device = %S, " + "PEPHANDLE = %p\n.", + __FUNCTION__, + PepAcpiNotificationHandlers[ + PEP_NOTIFY_ACPI_REGISTER_DEVICE].Name, + RegisterDevice->AcpiDeviceName->Buffer, + PepInternalDevice); + + PepInternalDevice = NULL; + +RegisterDeviceEnd: + if (PepInternalDevice != NULL) { + ExFreePoolWithTag(PepInternalDevice, PEP_POOL_TAG); + } + + return; +} + +VOID +PepAcpiUnregisterDevice ( + _Inout_updates_bytes_(sizeof(PEP_ACPI_UNREGISTER_DEVICE)) PVOID Data + ) + +/*++ + +Routine Description: + + This routine is called to release responsibility for a device. As part of + unregistration, it will invoke the device-specific registered routine if + one is supplied. + +Arguments: + + Data - Supplies a pointer to a PEP_ACPI_UNREGISTER_DEVICE structure. + +Return Value: + + None. + +--*/ + +{ + + PWSTR DeviceName; + KIRQL Irql; + PPEP_INTERNAL_DEVICE_HEADER PepInternalDevice; + PPEP_ACPI_UNREGISTER_DEVICE UnregisterDevice; + + UnregisterDevice = (PPEP_ACPI_UNREGISTER_DEVICE)Data; + PepInternalDevice = + (PPEP_INTERNAL_DEVICE_HEADER)UnregisterDevice->DeviceHandle; + + KeAcquireSpinLock(&PepGlobalSpinLock,&Irql); + RemoveEntryList(&PepInternalDevice->ListEntry); + KeReleaseSpinLock(&PepGlobalSpinLock,Irql); + + DeviceName = PepGetDeviceName(PepInternalDevice->DeviceType); + if (DeviceName != NULL) { + TraceEvents(INFO, + DBG_PEP, + "%s: %s: Device = %S.\n", + __FUNCTION__, + PepAcpiNotificationHandlers[ + PEP_NOTIFY_ACPI_UNREGISTER_DEVICE].Name, + DeviceName); + } + + ExFreePoolWithTag(PepInternalDevice, PEP_POOL_TAG); + return; +} + +VOID +PepAcpiEnumerateDeviceNamespace ( + _Inout_updates_bytes_(sizeof(PEP_ACPI_ENUMERATE_DEVICE_NAMESPACE)) + PVOID Data + ) + +/*++ + +Routine Description: + + This routine handles PEP_NOTIFY_ACPI_ENUMERATE_DEVICE_NAMESPACE + notification. As part of enumeration, it will invoke the device-specific + registered routine if one is supplied. + +Arguments: + + Data - Supplies a pointer to a PEP_ACPI_ENUMERATE_DEVICE_NAMESPACE + structure. + +Return Value: + + None. + +--*/ + +{ + + ULONG Count; + PPEP_DEVICE_DEFINITION DeviceDefinition; + PPEP_ACPI_ENUMERATE_DEVICE_NAMESPACE EdnBuffer; + ULONG Index; + SIZE_T ObjectBufferSize; + PPEP_INTERNAL_DEVICE_HEADER PepInternalDevice; + SIZE_T RequiredSize; + + EdnBuffer = (PPEP_ACPI_ENUMERATE_DEVICE_NAMESPACE)Data; + PepInternalDevice = (PPEP_INTERNAL_DEVICE_HEADER)EdnBuffer->DeviceHandle; + DeviceDefinition = PepInternalDevice->DeviceDefinition; + + // + // Always return method count regardless of success or failure. + // + + EdnBuffer->ObjectCount = DeviceDefinition->ObjectCount; + + // + // Check if the output buffer size is sufficient or not. + // + + ObjectBufferSize = EdnBuffer->ObjectBufferSize; + Count = DeviceDefinition->ObjectCount; + RequiredSize = Count * sizeof(PEP_ACPI_OBJECT_NAME_WITH_TYPE); + if (ObjectBufferSize < RequiredSize) { + EdnBuffer->Status = STATUS_BUFFER_TOO_SMALL; + TraceEvents(ERROR, + DBG_PEP, + "%s: %s: " + "Insufficient buffer size. " + "Required = %d, Provided = %d.\n", + __FUNCTION__, + PepAcpiNotificationHandlers[ + PEP_NOTIFY_ACPI_ENUMERATE_DEVICE_NAMESPACE].Name, + (ULONG)RequiredSize, + (ULONG)ObjectBufferSize); + + goto EnumerateDeviceNamespaceEnd; + } + + for (Index = 0; Index < Count; Index += 1) { + TraceEvents(INFO, + DBG_PEP, + "%s: %s: Enumerate method %d.\n", + __FUNCTION__, + PepAcpiNotificationHandlers[ + PEP_NOTIFY_ACPI_ENUMERATE_DEVICE_NAMESPACE].Name, + (ULONG)DeviceDefinition->Objects[Index].ObjectName); + + EdnBuffer->Objects[Index].Name.NameAsUlong = + DeviceDefinition->Objects[Index].ObjectName; + + EdnBuffer->Objects[Index].Type = + DeviceDefinition->Objects[Index].ObjectType; + } + + EdnBuffer->Status = STATUS_SUCCESS; + + // + // Invoke the device-specific registered routine if one is supplied. + // + + PepInvokeNotificationHandler(PEP_NOTIFICATION_CLASS_ACPI, + NULL, + PepHandlerTypeSyncCritical, + PEP_NOTIFY_ACPI_ENUMERATE_DEVICE_NAMESPACE, + PepInternalDevice, + Data, + sizeof(PEP_ACPI_ENUMERATE_DEVICE_NAMESPACE), + NULL); + +EnumerateDeviceNamespaceEnd: + return; +} + +VOID +PepAcpiQueryObjectInformation ( + _Inout_updates_bytes_(sizeof(PEP_ACPI_QUERY_OBJECT_INFORMATION)) PVOID Data + ) + +/*++ + +Routine Description: + + This routine handles PEP_NOTIFY_ACPI_QUERY_OBJECT_INFORMATION + notification. As part of query, it will invoke the device-specific + registered routine if one is supplied. + +Arguments: + + Data - Supplies a pointer to a PEP_NOTIFY_ACPI_QUERY_OBJECT_INFORMATION + structure. + +Return Value: + + None. + +--*/ + +{ + + ULONG Count; + PPEP_DEVICE_DEFINITION DeviceDefinition; + ULONG Index; + PPEP_ACPI_QUERY_OBJECT_INFORMATION QoiBuffer; + PPEP_INTERNAL_DEVICE_HEADER PepInternalDevice; + + QoiBuffer = (PPEP_ACPI_QUERY_OBJECT_INFORMATION)Data; + PepInternalDevice = (PPEP_INTERNAL_DEVICE_HEADER)QoiBuffer->DeviceHandle; + DeviceDefinition = PepInternalDevice->DeviceDefinition; + Count = DeviceDefinition->ObjectCount; + for (Index = 0; Index < Count; Index += 1) { + if (QoiBuffer->Name.NameAsUlong == + DeviceDefinition->Objects[Index].ObjectName) { + + QoiBuffer->MethodObject.InputArgumentCount = + DeviceDefinition->Objects[Index].InputArgumentCount; + + QoiBuffer->MethodObject.OutputArgumentCount = + DeviceDefinition->Objects[Index].OutputArgumentCount; + } + } + + // + // Invoke the device-specific registered routine if one is supplied. + // + + PepInvokeNotificationHandler(PEP_NOTIFICATION_CLASS_ACPI, + NULL, + PepHandlerTypeSyncCritical, + PEP_NOTIFY_ACPI_QUERY_OBJECT_INFORMATION, + PepInternalDevice, + Data, + sizeof(PEP_ACPI_QUERY_OBJECT_INFORMATION), + NULL); + + return; +} + +VOID +PepAcpiEvaluateControlMethod ( + _Inout_updates_bytes_(sizeof(PEP_ACPI_EVALUATE_CONTROL_METHOD)) PVOID Data + ) + +/*++ + +Routine Description: + + This routine handles PEP_NOTIFY_ACPI_EVALUATE_CONTROL_METHOD + notification. By default, this routine will fail the evaluation. If + device-specific registered routine is applied, it will be called instead. + +Arguments: + + Data - Supplies a pointer to a PEP_ACPI_EVALUATE_CONTROL_METHOD structure. + +Return Value: + + None. + +--*/ + +{ + + PPEP_ACPI_EVALUATE_CONTROL_METHOD EcmBuffer; + PPEP_INTERNAL_DEVICE_HEADER PepInternalDevice; + + EcmBuffer = (PPEP_ACPI_EVALUATE_CONTROL_METHOD)Data; + PepInternalDevice = (PPEP_INTERNAL_DEVICE_HEADER)EcmBuffer->DeviceHandle; + + // + // By default, assume the method evaluation will fail. + // + + EcmBuffer->MethodStatus = STATUS_NOT_IMPLEMENTED; + + // + // Invoke the device-specific registered routine if one is supplied. + // + + PepInvokeNotificationHandler(PEP_NOTIFICATION_CLASS_ACPI, + NULL, + PepHandlerTypeSyncCritical, + PEP_NOTIFY_ACPI_EVALUATE_CONTROL_METHOD, + PepInternalDevice, + Data, + sizeof(PEP_ACPI_EVALUATE_CONTROL_METHOD), + &EcmBuffer->MethodStatus); + + return; +} + +VOID +PepAcpiQueryDeviceControlResources ( + _Inout_updates_bytes_(sizeof(PEP_ACPI_QUERY_DEVICE_CONTROL_RESOURCES)) + PVOID Data + ) + +/*++ + +Routine Description: + + This routine handles PEP_NOTIFY_ACPI_QUERY_DEVICE_CONTROL_RESOURCES + notification. By default, this routine assumes no resource needed. If + device-specific registered routine is applied, it will be called instead. + If no handler is implemented, mark the request as success to indicate no + resource is needed. + +Arguments: + + Data - Supplies a pointer to a + PEP_NOTIFY_ACPI_QUERY_DEVICE_CONTROL_RESOURCES structure. + +Return Value: + + None. + +--*/ + +{ + + PPEP_INTERNAL_DEVICE_HEADER PepInternalDevice; + PPEP_ACPI_QUERY_DEVICE_CONTROL_RESOURCES ResourceBuffer; + + ResourceBuffer = (PPEP_ACPI_QUERY_DEVICE_CONTROL_RESOURCES)Data; + PepInternalDevice = + (PPEP_INTERNAL_DEVICE_HEADER)ResourceBuffer->DeviceHandle; + + // + // By default, assume the device doesn't need any BIOS control resources. + // If they are required, a device-specific handler will be installed to + // fill in the appropriate values. + // + + ResourceBuffer->Status = STATUS_NOT_IMPLEMENTED; + + // + // Invoke the device-specific registered routine if one is supplied. + // + + PepInvokeNotificationHandler(PEP_NOTIFICATION_CLASS_ACPI, + NULL, + PepHandlerTypeSyncCritical, + PEP_NOTIFY_ACPI_QUERY_DEVICE_CONTROL_RESOURCES, + PepInternalDevice, + Data, + sizeof(PEP_ACPI_QUERY_DEVICE_CONTROL_RESOURCES), + &ResourceBuffer->Status); + + // + // If no handler was implemented, then succeed the request to indicate + // no resources are needed. + // + + if (ResourceBuffer->Status == STATUS_NOT_IMPLEMENTED) { + TraceEvents(INFO, + DBG_PEP, + "%s: %s: No resource required.\n", + __FUNCTION__, + PepAcpiNotificationHandlers[ + PEP_NOTIFY_ACPI_QUERY_DEVICE_CONTROL_RESOURCES].Name); + + ResourceBuffer->BiosResourcesSize = 0; + ResourceBuffer->Status = STATUS_SUCCESS; + } + + return; +} + +VOID +PepAcpiTranslatedDeviceControlResources ( + _Inout_updates_bytes_(sizeof(PEP_ACPI_TRANSLATED_DEVICE_CONTROL_RESOURCES)) + PVOID Data + ) + +/*++ + +Routine Description: + + This routine handles PEP_NOTIFY_ACPI_TRANSLATED_DEVICE_CONTROL_RESOURCES + notification. By default, this routine does nothing. If device-specific + registered routine is applied, it will be called instead. + +Arguments: + + Data - Supplies a pointer a + PEP_NOTIFY_ACPI_TRANSLATED_DEVICE_CONTROL_RESOURCES structure. + +Return Value: + + None. + +--*/ + +{ + + PPEP_INTERNAL_DEVICE_HEADER PepInternalDevice; + PPEP_ACPI_TRANSLATED_DEVICE_CONTROL_RESOURCES ResourceBuffer; + + ResourceBuffer = (PPEP_ACPI_TRANSLATED_DEVICE_CONTROL_RESOURCES)Data; + PepInternalDevice = + (PPEP_INTERNAL_DEVICE_HEADER)ResourceBuffer->DeviceHandle; + + // + // Invoke the device-specific registered routine if one is supplied. + // + + PepInvokeNotificationHandler(PEP_NOTIFICATION_CLASS_ACPI, + NULL, + PepHandlerTypeSyncCritical, + PEP_NOTIFY_ACPI_TRANSLATED_DEVICE_CONTROL_RESOURCES, + PepInternalDevice, + Data, + sizeof(PEP_ACPI_TRANSLATED_DEVICE_CONTROL_RESOURCES), + NULL); + + return; +} + +VOID +PepAcpiWorkNotification ( + _Inout_updates_bytes_(sizeof(PEP_WORK)) PVOID Data + ) + +/*++ + +Routine Description: + + This routine is called to handle PEP_NOTIFY_ACPI_WORK notification. It calls + the worker routine to drain the completed work queue. + +Arguments: + + Data - Supplies a pointer to a PEP_WORK structure. + +Return Value: + + None. + +--*/ + +{ + + PepProcessCompleteWorkRequests(Data); + return; +} diff --git a/pofx/PEP/common/driver.c b/pofx/PEP/common/driver.c new file mode 100644 index 000000000..3a8b12395 --- /dev/null +++ b/pofx/PEP/common/driver.c @@ -0,0 +1,341 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + driver.c + +Abstract: + + This module implements DriverEntry for WDF driver. + + +Environment: + + Kernel Mode + +--*/ + +// +//-------------------------------------------------------------------- Includes +// + +#include "pch.h" + +#if defined(EVENT_TRACING) +#include "driver.tmh" +#endif + +// +//------------------------------------------------------------------- Globals +// + +WDFDEVICE PepGlobalWdfDevice; + +// +//------------------------------------------------------------------ Prototypes +// + +DRIVER_INITIALIZE DriverEntry; + +// +//--------------------------------------------------------------------- Pragmas +// + +#pragma alloc_text(INIT, DriverEntry) +#pragma alloc_text(PAGE, PepEvtDeviceAdd) + +// +//------------------------------------------------------------------- Functions +// + +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. + +Parameters Description: + + DriverObject - Supplies a pointer to the driver object. + + RegistryPath - Supplies a pointer to a unicode string representing the + path to the driver-specific key in the registry. + +Return Value: + + NTSTATUS. + +--*/ + +{ + + WDFDEVICE Device; + PWDFDEVICE_INIT DeviceInit; + WDFDRIVER Driver; + WDF_DRIVER_CONFIG DriverConfig; + WDF_OBJECT_ATTRIBUTES FdoAttributes; + NTSTATUS Status; + BOOLEAN WdfDriverCreated; + + UNREFERENCED_PARAMETER(DriverObject); + + // + // Initialize WPP tracing. + // + + Device = NULL; + DeviceInit = NULL; + Driver = NULL; + WdfDriverCreated = FALSE; + WPP_INIT_TRACING(DriverObject, RegistryPath); + + // + // Initialize the configuration object with necessary callbacks. + // + + PepGlobalWdfDevice = NULL; + WDF_DRIVER_CONFIG_INIT(&DriverConfig, PepEvtDeviceAdd); + DriverConfig.DriverPoolTag = PEP_POOL_TAG; + DriverConfig.EvtDriverUnload = PepEvtDriverUnload; + + // + // Create the WDF driver object + // + + Status = WdfDriverCreate(DriverObject, + RegistryPath, + WDF_NO_OBJECT_ATTRIBUTES, + &DriverConfig, + &Driver); + + if (!NT_SUCCESS(Status)) { + TraceEvents(ERROR, + DBG_INIT, + "%s: WdfDriverCreate() failed! Status = %!STATUS!.\n", + __FUNCTION__, + Status); + + goto DriverEntryEnd; + } + + WdfDriverCreated = TRUE; + + // + // Secure the device + // + + DeviceInit = WdfControlDeviceInitAllocate(Driver, &SDDL_DEVOBJ_SYS_ALL_ADM_ALL); + if (DeviceInit == NULL) { + TraceEvents(ERROR, + DBG_INIT, + "%s: WdfControlDeviceInitAllocate() failed!\n", + __FUNCTION__); + + Status = STATUS_UNSUCCESSFUL; + goto DriverEntryEnd; + } + + // + // Set various attributes for this device. + + WdfDeviceInitSetIoType(DeviceInit, WdfDeviceIoBuffered); + + // + // Initialize FDO attributes with the device extension. + // + + WDF_OBJECT_ATTRIBUTES_INIT(&FdoAttributes); + FdoAttributes.SynchronizationScope = WdfSynchronizationScopeNone; + + // + // Call the framework to create the device. + // + + Status = WdfDeviceCreate(&DeviceInit, &FdoAttributes, &Device); + if (!NT_SUCCESS(Status)) { + TraceEvents(ERROR, + DBG_INIT, + "%s: WdfDeviceCreate() failed! Status = %!STATUS!.\n", + __FUNCTION__, + Status); + + goto DriverEntryEnd; + } + + PepGlobalWdfDevice = Device; + Status = PepInitialize(DriverObject, Driver, RegistryPath); + if (!NT_SUCCESS(Status)) { + TraceEvents(ERROR, + DBG_INIT, + "%s: PepInitialize() failed! Status = %!STATUS!.\n", + __FUNCTION__, + Status); + + goto DriverEntryEnd; + } + + // + // As a non-PNP device we need to explicitly tell WDF to complete + // initialization + // + + WdfControlFinishInitializing(Device); + + // + // If the driver object was created, then clean up will be done in the + // unload routine. Otherwise, things need to be cleaned up here. + // + +DriverEntryEnd: + if ((!NT_SUCCESS(Status)) && (WdfDriverCreated == FALSE)) { + if (Device != NULL) { + WdfObjectDelete(Device); + Device = NULL; + + } else if (DeviceInit != NULL) { + WdfDeviceInitFree(DeviceInit); + DeviceInit = NULL; + } + + WPP_CLEANUP(DriverObject); + } + + return Status; +} + +NTSTATUS +PepEvtDeviceAdd ( + _In_ WDFDRIVER Driver, + _Inout_ PWDFDEVICE_INIT DeviceInit + ) + +/*++ + +Routine Description: + + This routine is the AddDevice entry point for the sample Platform Extension + driver. This can be called if there is a device corresponding to the + Platform Extension in ACPI namespace or a root-enumerated instance was + created during driver install. + +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(); + + UNREFERENCED_PARAMETER(Driver); + + // + // Set various attributes for this device. + + WdfDeviceInitSetIoType(DeviceInit, WdfDeviceIoBuffered); + + // + // Initialize FDO attributes with the device extension. + // + + WDF_OBJECT_ATTRIBUTES_INIT(&FdoAttributes); + FdoAttributes.SynchronizationScope = WdfSynchronizationScopeNone; + + // + // Call the framework to create the device. + // + + Status = WdfDeviceCreate(&DeviceInit, &FdoAttributes, &Device); + if (!NT_SUCCESS(Status)) { + TraceEvents(ERROR, + DBG_INIT, + "%s: WdfDeviceCreate() failed! Status = %!STATUS!.\n", + __FUNCTION__, + Status); + + goto DeviceAddEnd; + } + + // + // Mark the PEP device as not stoppable or removable. + // + + WdfDeviceSetStaticStopRemove(Device, FALSE); + +DeviceAddEnd: + return Status; +} + +VOID +PepEvtDriverUnload ( + __in WDFDRIVER Driver + ) + +/*++ + +Routine Description: + + This routine is called by WDF to allow final cleanup prior to unloading + the Resource Hub. This routine performs resource hub cleanup and stops + tracing. + +Arguments: + + Driver - Supplies a handle to a framework driver object. + +Return Value: + + None. + +--*/ + +{ + + PDRIVER_OBJECT DriverObject; + + // + // Platform extension should never be unloaded after it is registered. + // + + if (PepRegistered != FALSE) { +#pragma prefast(suppress:__WARNING_USE_OTHER_FUNCTION, "KeBugCheckEx must be used for all Framework rules violations") + + TraceEvents(ERROR, + DBG_INIT, + "%s: Fatal error - Driver is unloaded after it is registered!\n", + __FUNCTION__); + + KeBugCheckEx(DRIVER_VIOLATION, 0, 0, 0, 0); + } + + TraceEvents(VERBOSE, + DBG_INIT, + "%s: Driver unloaded!\n", + __FUNCTION__); + + DriverObject = WdfDriverWdmGetDriverObject(Driver); + WPP_CLEANUP(DriverObject); + return; +} + diff --git a/pofx/PEP/common/pchsrc.c b/pofx/PEP/common/pchsrc.c new file mode 100644 index 000000000..17305716a --- /dev/null +++ b/pofx/PEP/common/pchsrc.c @@ -0,0 +1 @@ +#include "pch.h" \ No newline at end of file diff --git a/pofx/PEP/common/pep.c b/pofx/PEP/common/pep.c new file mode 100644 index 000000000..4204ef168 --- /dev/null +++ b/pofx/PEP/common/pep.c @@ -0,0 +1,489 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + pep.c + +Abstract: + + This module implements PEP registration. + + +Environment: + + Kernel Mode + +--*/ + +// +//-------------------------------------------------------------------- Includes +// + +#include +#include "pch.h" + +#if defined(EVENT_TRACING) +#include "pep.tmh" +#endif + +// +//------------------------------------------------------------------- Globals +// + +PEP_KERNEL_INFORMATION PepKernelInformation; +LIST_ENTRY PepCompletedWorkList; +LIST_ENTRY PepDeviceList; +KSPIN_LOCK PepGlobalSpinLock; +LIST_ENTRY PepPendingWorkList; +BOOLEAN PepRegistered = FALSE; +WDFSPINLOCK PepWorkListLock; + +// +// Define the default ACPI notification handlers. +// + +PEP_GENERAL_NOTIFICATION_HANDLER PepAcpiNotificationHandlers[] = { + {0, NULL, "UNKNOWN"}, + {PEP_NOTIFY_ACPI_PREPARE_DEVICE, + PepAcpiPrepareDevice, + "PEP_ACPI_PREPARE_DEVICE"}, + + {PEP_NOTIFY_ACPI_ABANDON_DEVICE, + PepAcpiAbandonDevice, + "PEP_ACPI_ABANDON_DEVICE"}, + + {PEP_NOTIFY_ACPI_REGISTER_DEVICE, + PepAcpiRegisterDevice, + "PEP_ACPI_REGISTER_DEVICE"}, + + {PEP_NOTIFY_ACPI_UNREGISTER_DEVICE, + PepAcpiUnregisterDevice, + "PEP_ACPI_UNREGISTER_DEVICE"}, + + {PEP_NOTIFY_ACPI_ENUMERATE_DEVICE_NAMESPACE, + PepAcpiEnumerateDeviceNamespace, + "PEP_ACPI_ENUMERATE_DEVICE_NAMESPACE"}, + + {PEP_NOTIFY_ACPI_QUERY_OBJECT_INFORMATION, + PepAcpiQueryObjectInformation, + "PEP_ACPI_QUERY_OBJECT_INFORMATION"}, + + {PEP_NOTIFY_ACPI_EVALUATE_CONTROL_METHOD, + PepAcpiEvaluateControlMethod, + "PEP_ACPI_EVALUATE_CONTROL_METHOD"}, + + {PEP_NOTIFY_ACPI_QUERY_DEVICE_CONTROL_RESOURCES, + PepAcpiQueryDeviceControlResources, + "PEP_ACPI_QUERY_DEVICE_CONTROL_RESOURCES"}, + + {PEP_NOTIFY_ACPI_TRANSLATED_DEVICE_CONTROL_RESOURCES, + PepAcpiTranslatedDeviceControlResources, + "PEP_ACPI_TRANSLATED_DEVICE_CONTROL_RESOURCES"}, + + {PEP_NOTIFY_ACPI_WORK, + PepAcpiWorkNotification, + "PEP_ACPI_WORK"} +}; + +PEP_GENERAL_NOTIFICATION_HANDLER PepDpmNotificationHandlers[] = { + {0, NULL, "UNKNOWN"}, + +}; + +// +//--------------------------------------------------------------------- Pragmas +// + +#pragma alloc_text(INIT, PepRegister) +#pragma alloc_text(INIT, PepInitialize) + +// +//------------------------------------------------------------------- Functions +// + +NTSTATUS +PepRegister ( + _In_ BOOLEAN AcpiHandlerOptIn, + _In_ BOOLEAN DpmHandlerOptIn, + _In_ BOOLEAN PpmHandlerOptIn + ) + +/*++ + +Routine Description: + + This routine registers as a power engine plugin with the OS. + +Arguments: + + AcpiHandlerOptIn - Indicates whether the PEP handles ACPI notification. + + DpmHandlerOptIn - Indicates whether the DPM handles ACPI notification. + + PpmHandlerOptIn - Indicates whether the PPM handles ACPI notification. + +Return Value: + + NTSTATUS. + +--*/ + +{ + + PEP_INFORMATION PepInformation; + NTSTATUS Status; + + RtlZeroMemory(&PepInformation, sizeof(PepInformation)); + PepInformation.Version = PEP_INFORMATION_VERSION; + PepInformation.Size = sizeof(PepInformation); + if (AcpiHandlerOptIn != FALSE) { + PepInformation.AcceptAcpiNotification = PepAcpiNotify; + } + + if (DpmHandlerOptIn != FALSE) { + PepInformation.AcceptDeviceNotification = PepDpmNotify; + } + + if (PpmHandlerOptIn != FALSE) { + PepInformation.AcceptProcessorNotification = PepPpmNotify; + } + + RtlZeroMemory(&PepKernelInformation, sizeof(PepKernelInformation)); + PepKernelInformation.Version = PEP_KERNEL_INFORMATION_V3; + PepKernelInformation.Size = sizeof(PepKernelInformation); + Status = PoFxRegisterPlugin(&PepInformation, &PepKernelInformation); + if (!NT_SUCCESS(Status)) { + TraceEvents(ERROR, + DBG_INIT, + "%s: PoFxRegisterPlugin() Failed! Status = %!STATUS!.\n", + __FUNCTION__, + Status); + + goto RegisterEnd; + } + + NT_ASSERT(PepKernelInformation.Plugin != NULL); + NT_ASSERT(PepKernelInformation.RequestWorker != NULL); + +RegisterEnd: + return Status; +} + +NTSTATUS +PepInitialize ( + _In_ PDRIVER_OBJECT WdmDriverObject, + _In_ WDFDRIVER WdfDriver, + _In_ PUNICODE_STRING RegistryPath + ) + +/*++ + +Routine Description: + + This routine performs initialization operations. + +Arguments: + + WdmDriverObject - Supplies a pointer to the WDM driver object. + + WdfDriver - Supplies a handle to the WDF driver object. + + RegistryPath - Supplies a pointer to the driver specific registry key. + +Return Value: + + NTSTATUS. + +--*/ + +{ + + BOOLEAN AcpiOptin; + BOOLEAN DpmOptin; + WDF_OBJECT_ATTRIBUTES ObjectAttributes; + BOOLEAN PpmOptin; + NTSTATUS Status; + + KeInitializeSpinLock(&PepGlobalSpinLock); + InitializeListHead(&PepDeviceList); + InitializeListHead(&PepPendingWorkList); + InitializeListHead(&PepCompletedWorkList); + + // + // Create the spin-lock that is used to synchronize state during device + // powering down. + // + + WDF_OBJECT_ATTRIBUTES_INIT(&ObjectAttributes); + + Status = WdfSpinLockCreate(&ObjectAttributes, &PepWorkListLock); + if (!NT_SUCCESS(Status)) { + TraceEvents(ERROR, + DBG_INIT, + "%s: WdfSpinLockCreate() Failed! Status = %!STATUS!.\n", + __FUNCTION__, + Status); + + goto InitializeEnd; + } + + // + // Query the registry for flags. This registry flag may not be present + // and thus a failure is not treated as fatal. + // + + AcpiOptin = FALSE; + DpmOptin = FALSE; + PpmOptin = FALSE; + PepQueryRegisterOptions(WdmDriverObject, + WdfDriver, + RegistryPath, + &AcpiOptin, + &DpmOptin, + &PpmOptin); + + // + // The default behavior is to handle Acpi. + // + + if ((AcpiOptin == FALSE) && + (DpmOptin == FALSE) && + (PpmOptin == FALSE)) { + + AcpiOptin = TRUE; + } + + Status = PepRegister(AcpiOptin, DpmOptin, PpmOptin); + if (!NT_SUCCESS(Status)) { + TraceEvents(ERROR, + DBG_INIT, + "%s: PepRegister() Failed! Status = %!STATUS!.\n", + __FUNCTION__, + Status); + + } else { + PepRegistered = TRUE; + } + +InitializeEnd: + return Status; +} + +__drv_requiresIRQL(PASSIVE_LEVEL) +VOID +PepQueryRegisterOptions ( + _In_ PDRIVER_OBJECT WdmDriverObject, + _In_ WDFDRIVER WdfDriver, + _In_ PUNICODE_STRING RegistryPath, + _Out_ PBOOLEAN PepAcpiOptin, + _Out_ PBOOLEAN PepDpmOptin, + _Out_ PBOOLEAN PepPpmOptin + ) + +/*++ + +Routine Description: + + This routine queries options/flags set in the registry and intializes + respective variables. + +Arguments: + + WdmDriverObject - Supplies a pointer to the WDM driver object. + + WdfDriver - Supplies a handle to the WDF driver object. + + RegistryPath - Supplies a pointer to the driver specific registry key. + + PepAcpiOptin - Supplies a pointer that receives whether ACPI opt-in + is enabled. + + PepDpmOptin - Supplies a pointer that receives whether DPM opt-in + is enabled. + + PepPpmOptin - Supplies a pointer that receives whether PPM opt-in + is enabled. + +Return Value: + + None. + +--*/ + +{ + + DECLARE_CONST_UNICODE_STRING(Key, PEP_REGISTRY_OPTIN_KEY); + ULONG Flag; + NTSTATUS Status; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER(WdmDriverObject); + + *PepAcpiOptin = FALSE; + *PepDpmOptin = FALSE; + *PepPpmOptin = FALSE; + + // + // Check whether ACPI/DPM/PPM opt-in is enabled. + // + + Flag = 0x0; + Status = PepQueryRegistry(NULL, WdfDriver, RegistryPath, &Key, &Flag); + if (NT_SUCCESS(Status)) { + if (CHECK_FLAG(Flag, PEP_REGISTRY_OPTIN_KEY_ACPI_MASK)) { + *PepAcpiOptin = TRUE; + } + + if (CHECK_FLAG(Flag, PEP_REGISTRY_OPTIN_KEY_DPM_MASK)) { + *PepDpmOptin = TRUE; + } + + if (CHECK_FLAG(Flag, PEP_REGISTRY_OPTIN_KEY_PPM_MASK)) { + *PepPpmOptin = TRUE; + } + } + + return; +} + +BOOLEAN +PepDpmNotify ( + _In_ ULONG Notification, + _In_ PVOID Data + ) + +/*++ + +Routine Description: + + Handles all incoming DPM notifications from the OS. + +Arguments: + + Notification - Supplies the notification type. + + Data - Supplies a pointer to a data structure specific to the + notification type. + +Return Value: + + TRUE if the notification type was recognized; + FALSE otherwise. + +--*/ + +{ + + BOOLEAN Recognized; + + Recognized = FALSE; + if (Notification >= ARRAYSIZE(PepDpmNotificationHandlers)) { + goto DpmNotifyEnd; + } + + if ((PepDpmNotificationHandlers[Notification].Notification == 0) || + (PepDpmNotificationHandlers[Notification].Handler == NULL)) { + + goto DpmNotifyEnd; + } + + PepDpmNotificationHandlers[Notification].Handler(Data); + Recognized = TRUE; + +DpmNotifyEnd: + return Recognized; +} + +BOOLEAN +PepAcpiNotify ( + _In_ ULONG Notification, + _In_ PVOID Data + ) + +/*++ + +Routine Description: + + Handles all incoming ACPI notifications from the OS. + +Arguments: + + Notification - Supplies the notification type. + + Data - Supplies a pointer to a data structure specific to the + notification type. + +Return Value: + + TRUE if the notification type was recognized; + FALSE otherwise. + +--*/ + +{ + + BOOLEAN Recognized; + + Recognized = FALSE; + if (Notification >= ARRAYSIZE(PepAcpiNotificationHandlers)) { + goto AcpiNotifyEnd; + } + + if ((PepAcpiNotificationHandlers[Notification].Notification == 0) || + (PepAcpiNotificationHandlers[Notification].Handler == NULL)) { + + goto AcpiNotifyEnd; + } + + PepAcpiNotificationHandlers[Notification].Handler(Data); + Recognized = TRUE; + +AcpiNotifyEnd: + return Recognized; +} + +BOOLEAN +PepPpmNotify ( + _In_ PEPHANDLE Handle, + _In_ ULONG Notification, + _Inout_opt_ PVOID Data + ) + +/*++ + +Routine Description: + + Handles all incoming PPM notifications from the OS. Will be implemented + in the future. + +Arguments: + + Notification - Supplies the notification type. + + Data - Supplies a pointer to a data structure specific to the + notification type. + +Return Value: + + Returns TRUE if the notification type was recognized; + FALSE otherwise. + +--*/ + +{ + + UNREFERENCED_PARAMETER(Handle); + UNREFERENCED_PARAMETER(Notification); + UNREFERENCED_PARAMETER(Data); + + // + // Not implemented. + // + + NT_ASSERT(FALSE); + + return FALSE; +} diff --git a/pofx/PEP/common/pepcommon.vcxproj b/pofx/PEP/common/pepcommon.vcxproj new file mode 100644 index 000000000..7593fccb6 --- /dev/null +++ b/pofx/PEP/common/pepcommon.vcxproj @@ -0,0 +1,284 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {AD3FF770-602D-4C51-B349-5A04033971EB} + $(MSBuildProjectName) + 1 + Debug + Win32 + {D443CDF0-F81C-4D0A-A117-63ADBD6AC9C8} + + + + 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 + TraceEvents(LEVEL,FLAGS,MSG,...) + {km-WdfDefault.tpl}*.tmh + ENABLE_WPP_RECORDER=1 + ..\inc;%(AdditionalIncludeDirectories) + pch.h + Use + $(IntDir)\pch.h.pch + + + true + true + TraceEvents(LEVEL,FLAGS,MSG,...) + {km-WdfDefault.tpl}*.tmh + ENABLE_WPP_RECORDER=1 + ..\inc;%(AdditionalIncludeDirectories) + pch.h + Use + $(IntDir)\pch.h.pch + + + true + true + TraceEvents(LEVEL,FLAGS,MSG,...) + {km-WdfDefault.tpl}*.tmh + ENABLE_WPP_RECORDER=1 + ..\inc;%(AdditionalIncludeDirectories) + pch.h + Use + $(IntDir)\pch.h.pch + + + true + true + TraceEvents(LEVEL,FLAGS,MSG,...) + {km-WdfDefault.tpl}*.tmh + ENABLE_WPP_RECORDER=1 + ..\inc;%(AdditionalIncludeDirectories) + pch.h + Use + $(IntDir)\pch.h.pch + + + true + true + TraceEvents(LEVEL,FLAGS,MSG,...) + {km-WdfDefault.tpl}*.tmh + ENABLE_WPP_RECORDER=1 + ..\inc;%(AdditionalIncludeDirectories) + pch.h + Use + $(IntDir)\pch.h.pch + + + + pepcommon + + + pepcommon + + + pepcommon + + + pepcommon + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE;EVENT_TRACING + true + Level4 + + + %(PreprocessorDefinitions);UNICODE;_UNICODE;EVENT_TRACING + + + %(PreprocessorDefinitions);UNICODE;_UNICODE;EVENT_TRACING + + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE;EVENT_TRACING + true + Level4 + + + %(PreprocessorDefinitions);UNICODE;_UNICODE;EVENT_TRACING + + + %(PreprocessorDefinitions);UNICODE;_UNICODE;EVENT_TRACING + + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE;EVENT_TRACING + true + Level4 + + + %(PreprocessorDefinitions);UNICODE;_UNICODE;EVENT_TRACING + + + %(PreprocessorDefinitions);UNICODE;_UNICODE;EVENT_TRACING + + + + + %(PreprocessorDefinitions);UNICODE;_UNICODE;EVENT_TRACING + true + Level4 + + + %(PreprocessorDefinitions);UNICODE;_UNICODE;EVENT_TRACING + + + %(PreprocessorDefinitions);UNICODE;_UNICODE;EVENT_TRACING + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ksguid.lib;$(DDK_LIB_PATH)\ntstrsafe.lib;$(DDK_LIB_PATH)\hal.lib;$(DDK_LIB_PATH)\ntoskrnl.lib;$(DDK_LIB_PATH)\wdmsec.lib;$(DDK_LIB_PATH)\wpprecorder.lib + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ksguid.lib;$(DDK_LIB_PATH)\ntstrsafe.lib;$(DDK_LIB_PATH)\hal.lib;$(DDK_LIB_PATH)\ntoskrnl.lib;$(DDK_LIB_PATH)\wdmsec.lib;$(DDK_LIB_PATH)\wpprecorder.lib + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ksguid.lib;$(DDK_LIB_PATH)\ntstrsafe.lib;$(DDK_LIB_PATH)\hal.lib;$(DDK_LIB_PATH)\ntoskrnl.lib;$(DDK_LIB_PATH)\wdmsec.lib;$(DDK_LIB_PATH)\wpprecorder.lib + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ksguid.lib;$(DDK_LIB_PATH)\ntstrsafe.lib;$(DDK_LIB_PATH)\hal.lib;$(DDK_LIB_PATH)\ntoskrnl.lib;$(DDK_LIB_PATH)\wdmsec.lib;$(DDK_LIB_PATH)\wpprecorder.lib + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + + + + + ..\inc;%(AdditionalIncludeDirectories) + pch.h + Create + $(IntDir)\pch.h.pch + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pofx/PEP/common/pepcommon.vcxproj.Filters b/pofx/PEP/common/pepcommon.vcxproj.Filters new file mode 100644 index 000000000..2c3e5ecc4 --- /dev/null +++ b/pofx/PEP/common/pepcommon.vcxproj.Filters @@ -0,0 +1,41 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {6DEB6F91-5CCF-4138-B486-6BD5D8886632} + + + h;hpp;hxx;hm;inl;inc;xsd + {A73A5236-6C28-49F3-B08E-F25A7563B75E} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {7CE23150-AE2E-4E12-9575-83512D0FCC07} + + + inf;inv;inx;mof;mc; + {CE952DA3-FA63-434D-AE32-B870D7C7F0FB} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/pofx/PEP/common/util.c b/pofx/PEP/common/util.c new file mode 100644 index 000000000..cced8fb14 --- /dev/null +++ b/pofx/PEP/common/util.c @@ -0,0 +1,1077 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + util.c + +Abstract: + + This file implements all the utility routines. + + +Environment: + + Kernel mode + +--*/ + +// +//-------------------------------------------------------------------- Includes +// + +#include "pch.h" + +#if defined(EVENT_TRACING) +#include "util.tmh" +#endif + +// +//--------------------------------------------------------------------- Pragmas +// + +#pragma alloc_text(PAGE, PepQueryRegistry) + +// +//------------------------------------------------------------------- Functions +// + +__drv_requiresIRQL(PASSIVE_LEVEL) +NTSTATUS +PepQueryRegistry ( + _Inout_opt_ WDFKEY *ParametersKey, + _In_opt_ WDFDRIVER WdfDriver, + _In_ PUNICODE_STRING RegistryPath, + _In_ PCUNICODE_STRING KeyValueName, + _Out_ PULONG OptionValue + ) + +/*++ + +Routine Description: + + This routine retrieves driver-wide flags from the registry. Path is: + HKLM\CCS\SERVICES\\Parameters\ + +Arguments: + + ParametersKey - Supplies an optional pointer that provides a handle to + the registry to use. If the handle is NULL, then on output, this + parameters receives a handle to the "Parameters" key. The caller is + responsible for closing the handle later on. + + WdfDriver - Supplies an optional handle to the WDF driver object. This is + required if *ParametersKey == NULL. + + RegistryPath - Supplies a pointer to the driver specific registry key. + + KeyValueName - Supplies the key-value name to be queried. + + OptionValue - Supplies a pointer that receives the registry value. + +Return Value: + + NTSTATUS + +--*/ + +{ + BOOLEAN CloseKey; + WDFKEY Key; + NTSTATUS Status; + + PAGED_CODE(); + + CloseKey = FALSE; + Key = NULL; + if ((ParametersKey != NULL) && (*ParametersKey != NULL)) { + Key = *ParametersKey; + + } else { + Status = WdfDriverOpenParametersRegistryKey(WdfDriver, + KEY_READ, + WDF_NO_OBJECT_ATTRIBUTES, + &Key); + + if (!NT_SUCCESS(Status)) { + TraceEvents(ERROR, + DBG_INIT, + "%s: Failed to open parameters registry key! " + "Path = %S\\%S, Status= %!STATUS!.\n", + __FUNCTION__, + RegistryPath->Buffer, + L"Parameters", + Status); + + goto QueryRegistryEnd; + } + + CloseKey = TRUE; + } + + Status = WdfRegistryQueryULong(Key, KeyValueName, OptionValue); + if (!NT_SUCCESS(Status) && (Status != STATUS_OBJECT_NAME_NOT_FOUND)) { + TraceEvents(ERROR, + DBG_INIT, + "%s: Failed to query registry flags! Path = %S\\%S, " + "ValueKey = %S, Status= %!STATUS!.\n", + __FUNCTION__, + RegistryPath->Buffer, + L"Parameters", + KeyValueName->Buffer, + Status); + + goto QueryRegistryEnd; + + } else if (NT_SUCCESS(Status)) { + TraceEvents(VERBOSE, + DBG_INIT, + "%s: Successfully queried registry flags! Path = %S\\%S, " + "ValueKey = %S, Value = %#x.\n", + __FUNCTION__, + RegistryPath->Buffer, + L"Parameters", + KeyValueName->Buffer, + *OptionValue); + } + + // + // On success return the parameter key to caller if desired. + // + +QueryRegistryEnd: + if (NT_SUCCESS(Status) && + (ParametersKey != NULL) && + (*ParametersKey != NULL)) { + + *ParametersKey = Key; + + } else if ((CloseKey != FALSE) && (Key != NULL)) { + WdfRegistryClose(Key); + } + + return Status; +} + +BOOLEAN +PepIsDeviceAccepted ( + _In_ PEP_NOTIFICATION_CLASS OwnedType, + _In_ PCUNICODE_STRING DeviceId, + _Out_ PPEP_DEVICE_DEFINITION *DeviceDefinition + ) + +/*++ + +Routine Description: + + This routine is called to see if a device should be accepted by this PEP. + +Arguments: + + OwnedType - Supplies the expected type of this PEP, which can be a + combination of ACPI, DPM, PPM or NONE. + + DeviceId - Supplies a pointer to a unicode string that contains the + device ID or instance path for the device. + + DeviceDefinition - Supplies the pointer for storing the accepted device's + device-specific definitions. + +Return Value: + + TRUE if this PEP owns this device; + FALSE otherwise. + +--*/ + +{ + + PEP_DEVICE_ID_MATCH Compare; + ULONG Index; + BOOLEAN Match; + PEP_DEVICE_TYPE Type; + + Match = FALSE; + Type = PEP_INVALID_DEVICE_TYPE; + for (Index = 0; Index < PepDeviceMatchArraySize; Index += 1) { + if (PEP_CHECK_DEVICE_TYPE_ACCEPTED( + (PepDeviceMatchArray[Index].OwnedType), + OwnedType) == FALSE) { + + continue; + } + + // + // If the type is owned by this PEP, check the device id. + // + + Compare = PepDeviceMatchArray[Index].CompareMethod; + Match = PepIsDeviceIdMatched( + DeviceId->Buffer, + DeviceId->Length / sizeof(WCHAR), + PepDeviceMatchArray[Index].DeviceId, + (ULONG)wcslen(PepDeviceMatchArray[Index].DeviceId), + Compare); + + if (Match != FALSE) { + TraceEvents(VERBOSE, + DBG_INIT, + "%s: Found device whose type matches. " + "DeviceId: %S\n", + __FUNCTION__, + DeviceId->Buffer); + + Type = PepDeviceMatchArray[Index].Type; + break; + } + } + + if (Match == FALSE) { + goto IsDeviceAcceptedEnd; + } + + Match = FALSE; + for (Index = 0; Index < PepDeviceDefinitionArraySize; Index += 1) { + if (PepDeviceDefinitionArray[Index].Type == Type) { + TraceEvents(VERBOSE, + DBG_INIT, + "%s: Found device definition of the given type.\n", + __FUNCTION__); + + Match = TRUE; + *DeviceDefinition = &PepDeviceDefinitionArray[Index]; + break; + } + } + +IsDeviceAcceptedEnd: + return Match; +} + +BOOLEAN +PepIsDeviceIdMatched ( + _In_ PWSTR String, + _In_ ULONG StringLength, + _In_ PWSTR SearchString, + _In_ ULONG SearchStringLength, + _In_ PEP_DEVICE_ID_MATCH DeviceIdCompareMethod + ) + +/*++ + +Routine Description: + + This routine check whether two given strings matches in the way specified + by the DeviceIdCompare flag. + +Arguments: + + String - Supplies a pointer to a unicode string to search within. + + StringLength - Supplies the length of the string to search within. + + SearchString - Supplies a pointer to a unicode string to search for. + + SearchStringLength - Supplies the length of the string to search for. + + DeviceIdCompareMethod - Supplies the flag indicating the way two + strings should match. + - PepDeviceIdMatchPartial: substring match; + - PepDeviceIdMatchFull: whole string match. + +Return Value: + + TRUE if the search string is found; + FALSE otherwise. + +--*/ + +{ + + BOOLEAN Found; + ULONG Index; + ULONG Result; + UNICODE_STRING SearchStringUnicode; + UNICODE_STRING SourceStringUnicode; + + Found = FALSE; + switch (DeviceIdCompareMethod) { + case PepDeviceIdMatchFull: + RtlInitUnicodeString(&SourceStringUnicode, String); + RtlInitUnicodeString(&SearchStringUnicode, SearchString); + Result = RtlCompareUnicodeString(&SourceStringUnicode, + &SearchStringUnicode, + FALSE); + + if (Result == 0) { + Found = TRUE; + } + + break; + + case PepDeviceIdMatchPartial: + if (StringLength < SearchStringLength) { + goto IsDeviceIdMatchedEnd; + } + + for (Index = 0; Index <= (StringLength - SearchStringLength); + Index += 1) { + + if(!_wcsnicmp(String + Index, + SearchString, + SearchStringLength)) { + + Found = TRUE; + goto IsDeviceIdMatchedEnd; + } + } + + break; + + default: + TraceEvents(ERROR, + DBG_INIT, + "%s: Unknown DeviceIdCompare = %d.\n", + __FUNCTION__, + (ULONG)DeviceIdCompareMethod); + } + +IsDeviceIdMatchedEnd: + return Found; +} + +VOID +PepScheduleNotificationHandler ( + _In_ PEP_NOTIFICATION_CLASS WorkType, + _In_ ULONG NotificationId, + _In_ PPEP_INTERNAL_DEVICE_HEADER PepInternalDevice, + _In_opt_ PVOID WorkContext, + _In_ SIZE_T WorkContextSize, + _In_opt_ PNTSTATUS WorkRequestStatus + ) + +/*++ + +Routine Description: + + This routine schedules the device-specific handler. + +Arguments: + + WorkType - Supplies the type of the work (ACPI/DPM/PPM). + + NotificationId - Supplies the PEP notification type. + + PepInternalDevice - Supplies the internal PEP device. + + WorkContext - Supplies optional pointer to the context of the work request. + + WorkContextSize - Supplies the size of the work request context. + + WorkRequestStatus - Supplies optional pointer to report the + status of the work request. + +Return Value: + + None. + +--*/ + +{ + + PPEP_DEVICE_DEFINITION DeviceDefinition; + NTSTATUS Status; + PPEP_WORK_CONTEXT WorkRequest; + + DeviceDefinition = PepInternalDevice->DeviceDefinition; + Status = PepCreateWorkRequest(WorkType, + NotificationId, + PepInternalDevice, + DeviceDefinition, + WorkContext, + WorkContextSize, + WorkRequestStatus, + &WorkRequest); + + if (!NT_SUCCESS(Status)) { + TraceEvents(ERROR, + DBG_PEP, + "%s: PepCreateWorkRequest() failed!. " + "Status = %!STATUS!.\n ", + __FUNCTION__, + Status); + + goto ScheduleNotificationHandlerEnd; + } + + PepPendWorkRequest(WorkRequest); + + // + // Mark the work request status as pending. + // + + if (WorkRequestStatus != NULL) { + *WorkRequestStatus = STATUS_PENDING; + } + +ScheduleNotificationHandlerEnd: + return; +} + +_Requires_lock_not_held_(PepWorkListLock) +VOID +PepInvokeNotificationHandler ( + _In_ PEP_NOTIFICATION_CLASS WorkType, + _In_opt_ PPEP_WORK_CONTEXT WorkRequest, + _In_ PEP_HANDLER_TYPE HandlerType, + _In_ ULONG NotificationId, + _In_opt_ PPEP_INTERNAL_DEVICE_HEADER PepInternalDevice, + _In_ PVOID Data, + _In_ SIZE_T DataSize, + _In_opt_ PNTSTATUS WorkRequestStatus + ) + +/*++ + +Routine Description: + + This routine invokes the handler of the specified type if one is + registered. + +Arguments: + + WorkType - Supplies the type of the work (ACPI/DPM/PPM). + + WorkRequest -Supplies optional pointer to the work context for async work. + + HandlerType - Supplies the type of handler to be invoked. + + NotificationId - Supplies the PEP notification type. + + PepInternalDevice - Supplies the internal PEP device. + + Data - Supplies a pointer to data to be passed to the handler. + + DataSize - Supplies the size of the data. + + WorkRequestStatus - Supplies optional pointer to report the + status of the work request. + +Return Value: + + None. + +--*/ + +{ + + ULONG Count; + PPEP_DEVICE_DEFINITION DeviceDefinition; + PPEP_ACPI_EVALUATE_CONTROL_METHOD EcmBuffer; + PPEP_NOTIFICATION_HANDLER_ROUTINE Handler; + PEP_NOTIFICATION_HANDLER_RESULT HandlerResult; + ULONG Index; + BOOLEAN NoSyncHandler; + PPEP_WORK_INFORMATION PoFxWorkInfo; + PPEP_DEVICE_NOTIFICATION_HANDLER Table; + + DeviceDefinition = PepInternalDevice->DeviceDefinition; + if (WorkRequest != NULL) { + PoFxWorkInfo = &(WorkRequest->LocalPoFxWorkInfo); + } else { + PoFxWorkInfo = NULL; + } + + switch (WorkType) { + case PEP_NOTIFICATION_CLASS_ACPI: + Count = DeviceDefinition->AcpiNotificationHandlerCount; + Table = DeviceDefinition->AcpiNotificationHandlers; + break; + + case PEP_NOTIFICATION_CLASS_DPM: + Count = DeviceDefinition->DpmNotificationHandlerCount; + Table = DeviceDefinition->DpmNotificationHandlers; + break; + + default: + TraceEvents(ERROR, + DBG_PEP, + "%s: Unknown WorkType = %d.\n", + __FUNCTION__, + (ULONG)WorkType); + + goto InvokeNotificationHandlerEnd; + } + + for (Index = 0; Index < Count; Index += 1) { + if (Table[Index].Notification != NotificationId) { + continue; + } + + Handler = NULL; + NoSyncHandler = FALSE; + switch (HandlerType) { + case PepHandlerTypeSyncCritical: + Handler = Table[Index].Handler; + if (Handler == NULL) { + NoSyncHandler = TRUE; + Handler = Table[Index].WorkerCallbackHandler; + } + + break; + + case PepHandlerTypeWorkerCallback: + Handler = Table[Index].WorkerCallbackHandler; + break; + + default: + TraceEvents(ERROR, + DBG_PEP, + "%s: Unknown HandlerType = %d.\n", + __FUNCTION__, + (ULONG)HandlerType); + + goto InvokeNotificationHandlerEnd; + } + + if ((WorkType == PEP_NOTIFICATION_CLASS_ACPI) && + (WorkRequest != NULL)) { + + switch(NotificationId) { + case PEP_NOTIFY_ACPI_EVALUATE_CONTROL_METHOD: + EcmBuffer = (PPEP_ACPI_EVALUATE_CONTROL_METHOD)Data; + PoFxWorkInfo->ControlMethodComplete. + OutputArguments = EcmBuffer->OutputArguments; + + PoFxWorkInfo->ControlMethodComplete. + OutputArgumentSize = EcmBuffer->OutputArgumentSize; + + break; + + default: + break; + } + } + + HandlerResult = PEP_NOTIFICATION_HANDLER_MAX; + if (Handler != NULL) { + if (NoSyncHandler == FALSE) { + HandlerResult = Handler(Data, PoFxWorkInfo); + + // + // Result is expected to be either complete or need more work. + // + + NT_ASSERT(HandlerResult < PEP_NOTIFICATION_HANDLER_MAX); + } + + // + // If the handler completes the request and the work context is + // not NULL, report to PoFx. + // + + if (NoSyncHandler == FALSE && + HandlerResult == PEP_NOTIFICATION_HANDLER_COMPLETE) { + if (WorkRequest != NULL) { + PepCompleteWorkRequest(WorkRequest); + } + + } else { + + // + // Make sure the request has been dequeued. + // + + NT_ASSERT((WorkRequest == NULL) || + (IsListEmpty(&WorkRequest->ListEntry) != FALSE)); + + NT_ASSERT((NoSyncHandler != FALSE) || + (HandlerResult == PEP_NOTIFICATION_HANDLER_MORE_WORK)); + + // + // If the handler needs to do async work, schedule a worker. + // + + PepScheduleNotificationHandler(WorkType, + NotificationId, + PepInternalDevice, + Data, + DataSize, + WorkRequestStatus); + } + } + + break; + } + +InvokeNotificationHandlerEnd: + return; +} + +PWSTR +PepGetDeviceName ( + _In_ ULONG DeviceType + ) + +/*++ + +Routine Description: + + This routine retrives the device Id by device type. + +Arguments: + + DeviceType - Supplies a unique identifier of the device. + +Return Value: + + DeviceId if the device is accepted by PEP; + NULL otherwise. + +--*/ + +{ + + ULONG Index; + PWSTR DeviceId; + + DeviceId = NULL; + for (Index = 0; Index < PepDeviceMatchArraySize; Index += 1) { + if (PepDeviceMatchArray[Index].Type == DeviceType) { + DeviceId = PepDeviceMatchArray[Index].DeviceId; + break; + } + } + + return DeviceId; +} + +VOID +PepRequestCommonWork ( + _In_ PPEP_ACPI_REQUEST_CONVERT_TO_BIOS_RESOURCES Request + ) + +/*++ + +Routine Description: + + This routine retrives the device Id by device type. + +Arguments: + + DeviceType - Supplies a unique identifier of the device. + +Return Value: + + DeviceId if the device is accepted by PEP; + NULL otherwise. + +--*/ + +{ + + PepKernelInformation.RequestCommon( + PEP_ACPI_REQUEST_COMMON_CONVERT_TO_BIOS_RESOURCES, + Request); + + return; + +} + +NTSTATUS +PepGetBiosResourceSize ( + _In_ PPEP_ACPI_RESOURCE ResourceBuffer, + _In_ ULONG ResourceBufferSize, + _In_opt_ PCHAR DebugInfo, + _Out_ PSIZE_T BiosResourceSize + ) + +/*++ + +Routine Description: + + This routine queries the size of the BIOS resource. + +Arguments: + + ResourceBuffer - Supplies a pointer to the buffer containing the resource. + + ResourceBufferSize - Supplies the size of the resource buffer. + + DebugInfo - Supplies an optional string that contains the debugging + information to be included in the log. + + BiosResourceSize - Supplies a pointer to receive the required size. + +Return Value: + + NTSTATUS. + +--*/ + +{ + + PEP_ACPI_REQUEST_CONVERT_TO_BIOS_RESOURCES Request; + ULONG RequiredSize; + NTSTATUS Status; + + // + // Initalize BIOS Resource Request. + // + + Request.InputBuffer = ResourceBuffer; + Request.InputBufferSize = ResourceBufferSize; + Request.OutputBuffer = NULL; + Request.OutputBufferSize = 0; + Request.Flags = 0; + + // + // Find the length required to hold the BIOS resources. + // + + RequiredSize = 0; + PepRequestCommonWork(&Request); + Status = Request.TranslationStatus; + if(Status != STATUS_BUFFER_TOO_SMALL) { + TraceEvents(INFO, + DBG_PEP, + "%s <%s>: PepRequestCommonWork() failed to determine " + "the size of BIOS resource. Status = %!STATUS!.\n", + __FUNCTION__, + NAME_DEBUG_INFO(DebugInfo), + Request.TranslationStatus); + + } else { + Status = STATUS_SUCCESS; + RequiredSize = (ULONG)Request.OutputBufferSize; + TraceEvents(INFO, + DBG_PEP, + "%s <%s>: PepRequestCommonWork() determines " + "RequiredSize = %d.\n", + __FUNCTION__, + NAME_DEBUG_INFO(DebugInfo), + RequiredSize); + } + + *BiosResourceSize = RequiredSize; + return Status; +} + +VOID +PepReturnBiosResource ( + _In_ PPEP_ACPI_RESOURCE ResourceBuffer, + _In_ ULONG ResourceBufferSize, + _In_ SIZE_T RequiredSize, + _In_ PACPI_METHOD_ARGUMENT Arguments, + _Inout_ PSIZE_T BiosResourcesSize, + _In_opt_ PCHAR DebugInfo, + _Out_ PNTSTATUS Status + ) + +/*++ + +Routine Description: + + This routine reports to PoFx the BIOS resources. + +Arguments: + + ResourceBuffer - Supplies a pointer to the buffer containing the resource. + + ResourceBufferSize - Supplies the size of the resource buffer. + + RequiredSize - Supplies the required size of the output buffer. + + Arguments - Supplies a pointer to receive the returned resource. + + BiosResourcesSize - Supplies a pointer to receive the returned resource size. + + DebugInfo - Supplies an optional string that contains the debugging + information to be included in the log. + + Status -Supplies a pointer to receive the returned evaluation status. + +Return Value: + + None. + +--*/ + +{ + + PUCHAR BiosResource; + PEP_ACPI_REQUEST_CONVERT_TO_BIOS_RESOURCES Request; + SIZE_T RequiredSizeAugmented; + + // + // Return the required size to the caller if the input buffer size + // is not sufficient. + // + + RequiredSizeAugmented = ACPI_METHOD_ARGUMENT_LENGTH(RequiredSize); + if ((*BiosResourcesSize) < RequiredSizeAugmented) { + TraceEvents(INFO, + DBG_PEP, + "%s <%s>: " + "Buffer too small, Required=%d, Provided=%d.\n", + __FUNCTION__, + NAME_DEBUG_INFO(DebugInfo), + (ULONG)RequiredSizeAugmented, + (ULONG)(*BiosResourcesSize)); + + *BiosResourcesSize = RequiredSizeAugmented; + *Status = STATUS_BUFFER_TOO_SMALL; + + } else { + TraceEvents(INFO, + DBG_PEP, + "%s <%s>: " + "Convert to BIOS resource, " + "BufferSize = %d, RequiredSize = %d, ProvidedSize = %d.\n", + __FUNCTION__, + NAME_DEBUG_INFO(DebugInfo), + (ULONG)RequiredSize, + (ULONG)RequiredSizeAugmented, + (ULONG)(*BiosResourcesSize)); + + BiosResource = ExAllocatePoolWithTag(NonPagedPool, + RequiredSize, + PEP_POOL_TAG); + + Request.InputBuffer = ResourceBuffer; + Request.InputBufferSize = ResourceBufferSize; + Request.OutputBuffer = BiosResource; + Request.OutputBufferSize = RequiredSize; + Request.Flags = 0; + + // + // Call Pep Request Common to convert ACPI resources to BIOS resources. + // + + PepRequestCommonWork(&Request); + if(!NT_SUCCESS(Request.TranslationStatus)) { + TraceEvents(INFO, + DBG_PEP, + "%s <%s>: PepRequestCommonWork() failed convert " + "to BIOS resource. Status = %!STATUS!.\n", + __FUNCTION__, + NAME_DEBUG_INFO(DebugInfo), + Request.TranslationStatus); + + ExFreePoolWithTag(BiosResource, PEP_POOL_TAG); + *Status = Request.TranslationStatus; + goto ReturnBiosResourceEnd; + + } + + // + // N.B. ACPI_METHOD_SET_ARGUMENT_BUFFER will copy the buffer as well. + // + + ACPI_METHOD_SET_ARGUMENT_BUFFER(Arguments, + BiosResource, + (USHORT)Request.OutputBufferSize); + + ExFreePoolWithTag(BiosResource, PEP_POOL_TAG); + + // + // Return the output argument count, size and status. + // + + *Status = STATUS_SUCCESS; + TraceEvents(INFO, + DBG_PEP, + "%s <%s>: Successfully converted to BIOS resource.\n", + __FUNCTION__, + NAME_DEBUG_INFO(DebugInfo)); + } + +ReturnBiosResourceEnd: + return; +} + +VOID +PepReturnAcpiData ( + _In_ PVOID Value, + _In_ USHORT ValueType, + _In_ ULONG ValueLength, + _In_ BOOLEAN ReturnAsPackage, + _Out_ PACPI_METHOD_ARGUMENT Arguments, + _Inout_ PSIZE_T OutputArgumentSize, + _Out_opt_ PULONG OutputArgumentCount, + _Out_ PNTSTATUS Status, + _In_opt_ PCHAR MethodName, + _In_opt_ PCHAR DebugInfo, + _Out_ PPEP_NOTIFICATION_HANDLER_RESULT CompleteResult + ) + +/*++ + +Routine Description: + + This routine returns data of specific type back to PoFx. + +Arguments: + + Value - Supplies the pointer to the data returned. + + ValueType - Supplies the type of the data. + + ValueLength - Supplies the length (raw, without ACPI method argument) + of the data. + + ReturnAsPackage - Supplies a flag indicating whether to return the data + in a package. + + Arguments - Supplies a pointer to receive the returned package. + + OutputArgumentSize - Supplies a pointer to receive the returned + argument size. + + OutputArgumentCount - Supplies an optional pointer to receive the returned + argument number. + + Status - Supplies a pointer to receive the evaluation result. + + MethodName - Supplies an optional string that names the native method + used for logging. + + DebugInfo - Supplies an optional string that contains the debugging + information to be included in the log. + + CompleteResult - Supplies a pointer to receive the complete result. + +Return Value: + + None. + +--*/ + +{ + + PACPI_METHOD_ARGUMENT ArgumentLocal; + ULONG RequiredSize; + PULONG ValueAsInteger; + PUCHAR ValueAsString; + + TraceEvents(INFO, + DBG_PEP, + "%s <%s> [%s]: Start processing.\n", + __FUNCTION__, + NAME_DEBUG_INFO(DebugInfo), + NAME_NATIVE_METHOD(MethodName)); + + RequiredSize = ACPI_METHOD_ARGUMENT_LENGTH(ValueLength); + if (ReturnAsPackage != FALSE) { + ArgumentLocal = (PACPI_METHOD_ARGUMENT)&Arguments->Data[0]; + } else { + ArgumentLocal = Arguments; + } + + if ((*OutputArgumentSize) < RequiredSize) { + TraceEvents(ERROR, + DBG_PEP, + "%s <%s> [%s]: " + "Buffer too small, Required=%d, Provided=%d.\n", + __FUNCTION__, + NAME_DEBUG_INFO(DebugInfo), + NAME_NATIVE_METHOD(MethodName), + (ULONG)RequiredSize, + (ULONG)(*OutputArgumentSize)); + + *OutputArgumentSize = RequiredSize; + *Status = STATUS_BUFFER_TOO_SMALL; + if (OutputArgumentCount != NULL) { + *OutputArgumentCount = 0; + } + + } else { + + // + // Set the returned value base on the type. + // + + switch (ValueType) { + case ACPI_METHOD_ARGUMENT_INTEGER: + ValueAsInteger = (PULONG)Value; + ACPI_METHOD_SET_ARGUMENT_INTEGER(ArgumentLocal, (*ValueAsInteger)); + TraceEvents(INFO, + DBG_PEP, + "%s <%s> [%s]: Returntype = Integer, Result = %#x.\n", + __FUNCTION__, + NAME_DEBUG_INFO(DebugInfo), + NAME_NATIVE_METHOD(MethodName), + (ULONG)ArgumentLocal->Argument); + + break; + + case ACPI_METHOD_ARGUMENT_STRING: + ValueAsString = (PUCHAR)Value; + + // + // N.B. ACPI_METHOD_SET_ARGUMENT_STRING will copy the string as + // well. + // ACPI_METHOD_SET_ARGUMENT_STRING currently has a bug: + // error C4267: '=' : conversion from 'size_t' to 'USHORT', + // possible loss of data. + // + // error C4057: char * is different from PUCHAR. + // + + #pragma warning(suppress:4267 4057 4244) + ACPI_METHOD_SET_ARGUMENT_STRING(ArgumentLocal, ValueAsString); + TraceEvents(INFO, + DBG_PEP, + "%s <%s> [%s]: ReturnType = String, Result = %s.\n", + __FUNCTION__, + NAME_DEBUG_INFO(DebugInfo), + NAME_NATIVE_METHOD(MethodName), + (PSTR)&ArgumentLocal->Data[0]); + + break; + + case ACPI_METHOD_ARGUMENT_BUFFER: + ValueAsString = (PUCHAR)Value; + ACPI_METHOD_SET_ARGUMENT_BUFFER(ArgumentLocal, + ValueAsString, + (USHORT)ValueLength); + + TraceEvents(INFO, + DBG_PEP, + "%s <%s> [%s]: ReturnType = Buffer.\n", + __FUNCTION__, + NAME_DEBUG_INFO(DebugInfo), + NAME_NATIVE_METHOD(MethodName)); + + break; + + default: + NT_ASSERT(FALSE); + return; + } + + if (ReturnAsPackage != FALSE) { + Arguments->Type = ACPI_METHOD_ARGUMENT_PACKAGE_EX; + Arguments->DataLength = + ACPI_METHOD_ARGUMENT_LENGTH_FROM_ARGUMENT(ArgumentLocal); + } + + // + // Return the output argument count, size and status. + // + + if (OutputArgumentCount != NULL) { + *OutputArgumentCount = 1; + } + + *OutputArgumentSize = + ACPI_METHOD_ARGUMENT_LENGTH_FROM_ARGUMENT(Arguments); + + *Status = STATUS_SUCCESS; + } + + *CompleteResult = PEP_NOTIFICATION_HANDLER_COMPLETE; + return; +} + diff --git a/pofx/PEP/common/work.c b/pofx/PEP/common/work.c new file mode 100644 index 000000000..036efc243 --- /dev/null +++ b/pofx/PEP/common/work.c @@ -0,0 +1,737 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + work.c + +Abstract: + + This module implements scheduling system for synchronous or + asynchronous works. + + +Environment: + + Kernel Mode + +--*/ + +// +//-------------------------------------------------------------------- Includes +// + +#include +#include "pch.h" + +#if defined(EVENT_TRACING) +#include "work.tmh" +#endif + +// +//------------------------------------------------------------------- Functions +// + +_IRQL_requires_max_(DISPATCH_LEVEL) +NTSTATUS +PepCreateWorkRequest ( + _In_ PEP_NOTIFICATION_CLASS WorkType, + _In_ ULONG NotificationId, + _In_ PPEP_INTERNAL_DEVICE_HEADER PepInternalDevice, + _In_ PPEP_DEVICE_DEFINITION DeviceDefinitionEntry, + _In_opt_ PVOID WorkContext, + _In_ SIZE_T WorkContextSize, + _In_opt_ PNTSTATUS WorkRequestStatus, + _Out_ PPEP_WORK_CONTEXT *OutputWorkRequest + ) + +/*++ + +Routine Description: + + This routine creates a new work request. Note the caller is responsible + for adding this request to the pending queue after filling in + request-specific data. + +Arguments: + + WorkType - Supplies the type of the work (ACPI/DPM/PPM). + + NotificationId - Supplies the PEP notification type. + + PepInternalDevice - Supplies the internal PEP device. + + DeviceDefinitionEntry - Supplies a pointer to the device definition for + the device. + + WorkContext - Supplies optional pointer to the context of the work request. + + WorkContextSize - Supplies the size of the work request context. + + WorkRequestStatus - Supplies optional pointer to report the + status of the work request. + + OutputWorkRequest - Supplies a pointer that receives the created request. + +Return Value: + + NTSTATUS. + +--*/ + +{ + + PVOID LocalWorkContext; + NTSTATUS Status; + PPEP_WORK_CONTEXT WorkRequest; + + NT_ASSERT(WorkType != PEP_NOTIFICATION_CLASS_NONE); + + WorkRequest = ExAllocatePoolWithTag(NonPagedPoolNx, + sizeof(PEP_WORK_CONTEXT), + PEP_POOL_TAG); + + if (WorkRequest == NULL) { + Status = STATUS_INSUFFICIENT_RESOURCES; + TraceEvents(ERROR, + DBG_PEP, + "%s: Insufficient resource for creating work request.\n", + __FUNCTION__); + + goto CreateWorkRequestEnd; + } + + LocalWorkContext = NULL; + if (WorkContext != NULL) { + + NT_ASSERT(WorkContextSize != 0); + + LocalWorkContext = ExAllocatePoolWithTag(NonPagedPoolNx, + WorkContextSize, + PEP_POOL_TAG); + + if (LocalWorkContext == NULL) { + Status = STATUS_INSUFFICIENT_RESOURCES; + TraceEvents(ERROR, + DBG_PEP, + "%s: Insufficient resource for " + "creating work request context.\n", + __FUNCTION__); + + goto CreateWorkRequestEnd; + } + + RtlCopyMemory(LocalWorkContext, WorkContext, WorkContextSize); + + } else { + WorkContextSize = 0; + } + + RtlZeroMemory(WorkRequest, sizeof(PEP_WORK_CONTEXT)); + InitializeListHead(&WorkRequest->ListEntry); + WorkRequest->WorkType = WorkType; + WorkRequest->NotificationId = NotificationId; + WorkRequest->PepInternalDevice = PepInternalDevice; + WorkRequest->DeviceDefinitionEntry = DeviceDefinitionEntry; + WorkRequest->WorkContextSize = WorkContextSize; + WorkRequest->WorkContext = LocalWorkContext; + WorkRequest->WorkRequestStatus = WorkRequestStatus; + WorkRequest->WorkCompleted = FALSE; + *OutputWorkRequest = WorkRequest; + Status = STATUS_SUCCESS; + +CreateWorkRequestEnd: + return Status; +} + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +PepDestroyWorkRequest ( + _In_ PPEP_WORK_CONTEXT WorkRequest + ) + +/*++ + +Routine Description: + + This routine destroys the given work request. + +Arguments: + + WorkRequest - Supplies a pointer to the work request. + +Return Value: + + None. + +--*/ + +{ + + if (WorkRequest->WorkContext != NULL) { + ExFreePoolWithTag(WorkRequest->WorkContext, PEP_POOL_TAG); + } + + ExFreePoolWithTag(WorkRequest, PEP_POOL_TAG); + return; +} + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +PepPendWorkRequest ( + _In_ PPEP_WORK_CONTEXT WorkRequest + ) + +/*++ + +Routine Description: + + This routine adds the given work request to the pending queue. + +Arguments: + + WorkRequest - Supplies a pointer to the work request. + +Return Value: + + None. + +--*/ + +{ + + // + // Ensure that the request is not already on some other queue. + // + + NT_ASSERT(IsListEmpty(&WorkRequest->ListEntry) != FALSE); + + TraceEvents(INFO, + DBG_PEP, + "%s: Insert pending work request. " + "Device=%p, WorkType=%d, NotificationId=%d.\n", + __FUNCTION__, + (PVOID)WorkRequest->PepInternalDevice, + (ULONG)WorkRequest->WorkType, + (ULONG)WorkRequest->NotificationId); + + // + // Add the new request to the end of tail of the pending work queue. + // + + WdfSpinLockAcquire(PepWorkListLock); + InsertTailList(&PepPendingWorkList, &WorkRequest->ListEntry); + WdfSpinLockRelease(PepWorkListLock); + + // + // Schedule a worker to pick up the new work. + // + + PepScheduleWorker(WorkRequest); + return; +} + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +PepCompleteWorkRequest ( + _In_ PPEP_WORK_CONTEXT WorkRequest + ) + +/*++ + +Routine Description: + + This routine adds the given work request to the completed queue. + +Arguments: + + WorkRequest - Supplies a pointer to the work request. + +Return Value: + + None. + +--*/ + +{ + + // + // Mark the request as completed. + // + + PepMarkWorkRequestComplete(WorkRequest); + + // + // Ensure that the request is not already on some other queue. + // + + NT_ASSERT(IsListEmpty(&WorkRequest->ListEntry) != FALSE); + NT_ASSERT(WorkRequest->WorkCompleted != FALSE); + + TraceEvents(INFO, + DBG_PEP, + "%s: Insert complete work request. " + "Device=%p, WorkType=%d, NotificationId=%d.\n", + __FUNCTION__, + (PVOID)WorkRequest->PepInternalDevice, + (ULONG)WorkRequest->WorkType, + (ULONG)WorkRequest->NotificationId); + + // + // Move the request into the completed queue. + // + + WdfSpinLockAcquire(PepWorkListLock); + InsertTailList(&PepCompletedWorkList, &(WorkRequest->ListEntry)); + WdfSpinLockRelease(PepWorkListLock); + + // + // Request Windows Runtime Power framework to query PEP for more work. It + // will be notified of completed work in the context of that query. + // + + PepKernelInformation.RequestWorker(PepKernelInformation.Plugin); + return; +} + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +PepMarkWorkRequestComplete ( + _In_ PPEP_WORK_CONTEXT WorkRequest + ) + +/*++ + +Routine Description: + + This routine marks the given work request as completed. This will cause + it to be moved to the completion queue. + +Arguments: + + WorkRequest - Supplies a pointer to the work request. + +Return Value: + + None. + +--*/ + +{ + + // + // Ensure the request wasn't already completed in a different context + // (and thus potentially already on the completed queue). + // + + NT_ASSERT(WorkRequest->WorkCompleted == FALSE); + + WorkRequest->WorkCompleted = TRUE; + return; +} + +NTSTATUS +PepCompleteSelfManagedWork ( + _In_ PEP_NOTIFICATION_CLASS WorkType, + _In_ ULONG NotificationId, + _In_ PPEP_INTERNAL_DEVICE_HEADER PepInternalDevice, + _In_ PPEP_WORK_INFORMATION PoFxWorkInfo + ) + +/*++ + +Routine Description: + + This routine completes PEP self-managed work that was previously deferred. + +Arguments: + + WorkType - Supplies the type of the work (ACPI/DPM/PPM). + + NotificationId - Supplies the PEP notification type. + + PepInternalDevice - Supplies the internal PEP device. + + PoFxWorkInfo - Supplies a pointer to the PPEP_WORK_INFORMATION structure + used to report result to PoFx. + +Return Value: + + NTSTATUS code. + +--*/ + +{ + + PPEP_DEVICE_DEFINITION DeviceDefinition; + NTSTATUS Status; + PPEP_WORK_CONTEXT WorkRequest; + + DeviceDefinition = PepInternalDevice->DeviceDefinition; + Status = PepCreateWorkRequest(WorkType, + NotificationId, + PepInternalDevice, + DeviceDefinition, + NULL, + 0, + NULL, + &WorkRequest); + + if (!NT_SUCCESS(Status)) { + TraceEvents(ERROR, + DBG_PEP, + "%s: PepCreateWorkRequest() failed!. " + "Status = %!STATUS!.\n ", + __FUNCTION__, + Status); + + goto CompleteWorkSelfManagedEnd; + } + + RtlCopyMemory(&WorkRequest->LocalPoFxWorkInfo, + PoFxWorkInfo, + sizeof(PEP_WORK_INFORMATION)); + + PepCompleteWorkRequest(WorkRequest); + +CompleteWorkSelfManagedEnd: + return Status; +} + +NTSTATUS +PepScheduleWorker ( + _In_ PPEP_WORK_CONTEXT WorkContext + ) + +/*++ + +Routine Description: + + This function schedules a worker thread to process pending work requests. + +Arguments: + + WorkContext - Supplies the context of the work. + +Return Value: + + NTSTATUS. + +--*/ + +{ + + WDF_OBJECT_ATTRIBUTES Attributes; + NTSTATUS Status; + BOOLEAN Synchronous; + WDFWORKITEM WorkItem; + WDF_WORKITEM_CONFIG WorkItemConfiguration; + + WorkItem = NULL; + Synchronous = FALSE; + + // + // Create a workitem to process events. + // + + WDF_OBJECT_ATTRIBUTES_INIT(&Attributes); + WDF_OBJECT_ATTRIBUTES_SET_CONTEXT_TYPE(&Attributes, + PEP_WORK_ITEM_CONTEXT); + + Attributes.ParentObject = PepGlobalWdfDevice; + + // + // Initialize the handler routine and create a new workitem. + // + + WDF_WORKITEM_CONFIG_INIT(&WorkItemConfiguration, PepWorkerWrapper); + + // + // Disable automatic serialization by the framework for the worker thread. + // The parent device object is being serialized at device level (i.e., + // WdfSynchronizationScopeDevice), and the framework requires it to be + // passive level (i.e., WdfExecutionLevelPassive) if automatic + // synchronization is desired. + // + + WorkItemConfiguration.AutomaticSerialization = FALSE; + + // + // Create the work item and queue it. If the workitem cannot be created + // for some reason, just call the worker routine synchronously. + // + + Status = WdfWorkItemCreate(&WorkItemConfiguration, + &Attributes, + &WorkItem); + + if (!NT_SUCCESS(Status)) { + Synchronous = TRUE; + TraceEvents(INFO, + DBG_PEP, + "%s: Failed to allocate work item to process pending" + "work! Status = %!STATUS!. Will synchronously process.\n", + __FUNCTION__, + Status); + } + + // + // If the operation is to be performed synchronously, then directly + // invoke the worker routine. Otherwise, queue a workitem to run the + // worker routine. + // + + if (Synchronous != FALSE) { + PepProcessPendingWorkRequests(); + + } else { + TraceEvents(INFO, + DBG_PEP, + "%s: Work request scheduled to run asynchronously. " + "Device=%p, WorkType=%d, NotificationId=%d.\n", + __FUNCTION__, + (PVOID)WorkContext->PepInternalDevice, + (ULONG)WorkContext->WorkType, + (ULONG)WorkContext->NotificationId); + + WdfWorkItemEnqueue(WorkItem); + } + + return STATUS_SUCCESS; +} + +VOID +PepWorkerWrapper ( + _In_ WDFWORKITEM WorkItem + ) + +/*++ + +Routine Description: + + This routine is wrapper for the actual worker routine that processes + pending work. + +Arguments: + + WorkItem - Supplies a handle to the workitem supplying the context. + +Return Value: + + None. + +--*/ + +{ + + PPEP_WORK_ITEM_CONTEXT Context; + + Context = PepGetWorkItemContext(WorkItem); + PepProcessPendingWorkRequests(); + + // + // Delete the work item as it is no longer required. + // + + WdfObjectDelete(WorkItem); + return; +} + +VOID +PepProcessPendingWorkRequests ( + VOID + ) + +/*++ + +Routine Description: + + This function processes all pending work. It calls the handler + routine for each pending work. + +Arguments: + + WorkType - Supplies the type of the work (ACPI/DPM/PPM). + +Return Value: + + None. + +--*/ + +{ + + PLIST_ENTRY NextEntry; + PPEP_WORK_CONTEXT WorkRequest; + + // + // Go through pending work list and handle them. + // + + WdfSpinLockAcquire(PepWorkListLock); + + while (IsListEmpty(&PepPendingWorkList) == FALSE) { + NextEntry = RemoveHeadList(&PepPendingWorkList); + InitializeListHead(NextEntry); + WorkRequest = CONTAINING_RECORD(NextEntry, + PEP_WORK_CONTEXT, + ListEntry); + + // + // Drop the request list lock prior to processing work. + // + + WdfSpinLockRelease(PepWorkListLock); + + // + // Invoke the request processing async handler. + // + + NT_ASSERT(WorkRequest->WorkCompleted == FALSE); + + TraceEvents(INFO, + DBG_PEP, + "%s: Asynchronously processing request. " + "Device=%p, WorkType=%d, NotificationId=%d.\n", + __FUNCTION__, + (PVOID)WorkRequest->PepInternalDevice, + (ULONG)WorkRequest->WorkType, + (ULONG)WorkRequest->NotificationId); + + PepInvokeNotificationHandler( + WorkRequest->WorkType, + WorkRequest, + PepHandlerTypeWorkerCallback, + WorkRequest->NotificationId, + WorkRequest->PepInternalDevice, + WorkRequest->WorkContext, + WorkRequest->WorkContextSize, + WorkRequest->WorkRequestStatus); + + // + // Reacquire the request list lock prior to dequeuing next request. + // + + WdfSpinLockAcquire(PepWorkListLock); + } + + WdfSpinLockRelease(PepWorkListLock); + return; +} + +VOID +PepProcessCompleteWorkRequests ( + _In_ PVOID Data + ) + +/*++ + +Routine Description: + + This function completes work by calling into the specific + completion handler, which is responsible for filling in the PEP_WORK + structure. + + Differences with pending worker routine: + - Keeps invoking PepKernelInformation.RequestWorker until the + completed work queue is not drained completely. + +Arguments: + + Data - Supplies a pointer to the PEP_WORK structure. + + WorkType - Supplies the type of the work (ACPI/DPM/PPM). + +Return Value: + + None. + +--*/ + +{ + + PLIST_ENTRY NextEntry; + BOOLEAN MoreWork; + PPEP_WORK_CONTEXT WorkRequest; + PPEP_WORK PoFxWork; + + MoreWork = FALSE; + NextEntry = NULL; + PoFxWork = (PPEP_WORK)Data; + + // + // Grab the next item from the completed work queue. + // + + WdfSpinLockAcquire(PepWorkListLock); + + if (IsListEmpty(&PepCompletedWorkList) == FALSE) { + NextEntry = RemoveHeadList(&PepCompletedWorkList); + + // + // Check if there is more work after this request. + // + + if (IsListEmpty(&PepCompletedWorkList) == FALSE) { + MoreWork = TRUE; + } + } + + // + // Drop the request list lock prior to processing work. + // + + WdfSpinLockRelease(PepWorkListLock); + + // + // If a completed request was found, report back to PoFx and + // reclaim its resources. + // + + if (NextEntry != NULL) { + InitializeListHead(NextEntry); + WorkRequest = CONTAINING_RECORD(NextEntry, + PEP_WORK_CONTEXT, + ListEntry); + + // + // Invoke the request processing routine. + // + + switch (WorkRequest->WorkType) { + case PEP_NOTIFICATION_CLASS_ACPI: + PoFxWork->NeedWork = TRUE; + RtlCopyMemory(PoFxWork->WorkInformation, + &WorkRequest->LocalPoFxWorkInfo, + sizeof(PEP_WORK_INFORMATION)); + + case PEP_NOTIFICATION_CLASS_DPM: + break; + + default: + TraceEvents(ERROR, + DBG_PEP, + "%s: Unknown WorkType = %d.\n", + __FUNCTION__, + (ULONG)WorkRequest->WorkType); + } + + // + // Destroy the request. + // + + PepDestroyWorkRequest(WorkRequest); + } + + // + // If there is more work left, then request another PEP_WORK. + // + + if (MoreWork != FALSE) { + PepKernelInformation.RequestWorker(PepKernelInformation.Plugin); + } + + return; +} + diff --git a/pofx/PEP/inc/common.h b/pofx/PEP/inc/common.h new file mode 100644 index 000000000..f6219ddca --- /dev/null +++ b/pofx/PEP/inc/common.h @@ -0,0 +1,88 @@ +/*++ + +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 + +#pragma warning (push) +#pragma warning(disable:4091) +#include +#pragma warning (pop) + +#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/pofx/PEP/inc/pch.h b/pofx/PEP/inc/pch.h new file mode 100644 index 000000000..b373dbe10 --- /dev/null +++ b/pofx/PEP/inc/pch.h @@ -0,0 +1,60 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + pch.h + +Abstract: + + This file contains pre-compiled header files. + + +Environment: + + Kernel mode + +--*/ + +#pragma once + +#define NT_PROCESSOR_GROUPS 1 + +#include "common.h" // DDK, WDF headers + +#pragma warning(push) +#pragma warning(disable:4214) /* nonstandard extension used: bit field types other then int */ +#pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */ +#include +#include +#include "ntpoapi.h" // public PO interfaces + +// +// Opt-into V3 PEP interfaces +// +#define PEP_KERNEL_INFORMATION_VERSION PEP_KERNEL_INFORMATION_V3 +#include "pepfx.h" + +#if defined(EVENT_TRACING) + +#include + +#else + +// +// Mock version of the handle type used everywhere. +// We set all instances of this type to NULL. +// + +typedef PVOID RECORDER_LOG; + +#endif + +#include "trace.h" +#include "pep.h" + +#pragma warning(pop) + +#include + diff --git a/pofx/PEP/inc/pep.h b/pofx/PEP/inc/pep.h new file mode 100644 index 000000000..314cbd89a --- /dev/null +++ b/pofx/PEP/inc/pep.h @@ -0,0 +1,674 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + pep.h + +Abstract: + + This is the general header. + + +--*/ + +// +//--------------------------------------------------------------------- Pragmas +// + +#pragma once + +// +//-------------------------------------------------------------------- Includes +// + +#include +#include +#include "pch.h" + +// +//----------------------------------------------------------------- Definitions +// + +#define PEP_POOL_TAG 'xEPT' + +#define PEP_REGISTRY_OPTIN_KEY L"PepOptIn" +#define PEP_REGISTRY_OPTIN_KEY_ACPI_MASK 0x00000001 +#define PEP_REGISTRY_OPTIN_KEY_DPM_MASK 0x00000002 +#define PEP_REGISTRY_OPTIN_KEY_PPM_MASK 0x00000004 + +#define PEP_MAKE_DEVICE_TYPE(Major, Minor, UniqueId) \ + (ULONG)(((ULONG)(Major) << 24) | \ + (((ULONG)(Minor) & 0xFF) << 16) | \ + ((ULONG)(UniqueId) & 0xFFFF)) + +#define OffsetToPtr(base, offset) ((PVOID)((ULONG_PTR)(base) + (offset))) + +#define PEP_ACPI_NOTIFICATION_ID_TO_WORK_TYPE(Id) \ + (((Id) == PEP_NOTIFY_ACPI_EVALUATE_CONTROL_METHOD) ? \ + PepWorkAcpiEvaluateControlMethodComplete : \ + PepWorkAcpiNotify) + +// +// ACPI method names. +// + +#define ACPI_OBJECT_NAME_AC0 ((ULONG)'0CA_') +#define ACPI_OBJECT_NAME_AC1 ((ULONG)'1CA_') +#define ACPI_OBJECT_NAME_AC2 ((ULONG)'2CA_') +#define ACPI_OBJECT_NAME_AC3 ((ULONG)'3CA_') +#define ACPI_OBJECT_NAME_AC4 ((ULONG)'4CA_') +#define ACPI_OBJECT_NAME_AC5 ((ULONG)'5CA_') +#define ACPI_OBJECT_NAME_AC6 ((ULONG)'6CA_') +#define ACPI_OBJECT_NAME_AC7 ((ULONG)'7CA_') +#define ACPI_OBJECT_NAME_AC8 ((ULONG)'8CA_') +#define ACPI_OBJECT_NAME_AC9 ((ULONG)'9CA_') +#define ACPI_OBJECT_NAME_ADR ((ULONG)'RDA_') +#define ACPI_OBJECT_NAME_AL0 ((ULONG)'0LA_') +#define ACPI_OBJECT_NAME_AL1 ((ULONG)'1LA_') +#define ACPI_OBJECT_NAME_AL2 ((ULONG)'2LA_') +#define ACPI_OBJECT_NAME_AL3 ((ULONG)'3LA_') +#define ACPI_OBJECT_NAME_AL4 ((ULONG)'4LA_') +#define ACPI_OBJECT_NAME_AL5 ((ULONG)'5LA_') +#define ACPI_OBJECT_NAME_AL6 ((ULONG)'6LA_') +#define ACPI_OBJECT_NAME_AL7 ((ULONG)'7LA_') +#define ACPI_OBJECT_NAME_AL8 ((ULONG)'8LA_') +#define ACPI_OBJECT_NAME_AL9 ((ULONG)'9LA_') +#define ACPI_OBJECT_NAME_BST ((ULONG)'TSB_') +#define ACPI_OBJECT_NAME_CCA ((ULONG)'ACC_') +#define ACPI_OBJECT_NAME_CID ((ULONG)'DIC_') +#define ACPI_OBJECT_NAME_CLS ((ULONG)'SLC_') +#define ACPI_OBJECT_NAME_CRS ((ULONG)'SRC_') +#define ACPI_OBJECT_NAME_CRT ((ULONG)'TRC_') +#define ACPI_OBJECT_NAME_DCK ((ULONG)'KCD_') +#define ACPI_OBJECT_NAME_DDN ((ULONG)'NDD_') +#define ACPI_OBJECT_NAME_DEP ((ULONG)'PED_') +#define ACPI_OBJECT_NAME_DIS ((ULONG)'SID_') +#define ACPI_OBJECT_NAME_DLM ((ULONG)'MLD_') +#define ACPI_OBJECT_NAME_DSM ((ULONG)'MSD_') +#define ACPI_OBJECT_NAME_DSW ((ULONG)'WSD_') +#define ACPI_OBJECT_NAME_DTI ((ULONG)'ITD_') +#define ACPI_OBJECT_NAME_EJD ((ULONG)'DJE_') +#define ACPI_OBJECT_NAME_EJ0 ((ULONG)'0JE_') +#define ACPI_OBJECT_NAME_EJ1 ((ULONG)'1JE_') +#define ACPI_OBJECT_NAME_EJ2 ((ULONG)'2JE_') +#define ACPI_OBJECT_NAME_EJ3 ((ULONG)'3JE_') +#define ACPI_OBJECT_NAME_EJ4 ((ULONG)'4JE_') +#define ACPI_OBJECT_NAME_EJ5 ((ULONG)'5JE_') +#define ACPI_OBJECT_NAME_FST ((ULONG)'TSF_') +#define ACPI_OBJECT_NAME_GHID ((ULONG)'DIHG') +#define ACPI_OBJECT_NAME_HID ((ULONG)'DIH_') +#define ACPI_OBJECT_NAME_HRV ((ULONG)'VRH_') +#define ACPI_OBJECT_NAME_HOT ((ULONG)'TOH_') +#define ACPI_OBJECT_NAME_INI ((ULONG)'INI_') +#define ACPI_OBJECT_NAME_IRC ((ULONG)'CRI_') +#define ACPI_OBJECT_NAME_LCK ((ULONG)'KCL_') +#define ACPI_OBJECT_NAME_LID ((ULONG)'DIL_') +#define ACPI_OBJECT_NAME_MAT ((ULONG)'TAM_') +#define ACPI_OBJECT_NAME_NTT ((ULONG)'TTN_') +#define ACPI_OBJECT_NAME_OFF ((ULONG)'FFO_') +#define ACPI_OBJECT_NAME_ON ((ULONG)'_NO_') +#define ACPI_OBJECT_NAME_OSC ((ULONG)'CSO_') +#define ACPI_OBJECT_NAME_OST ((ULONG)'TSO_') +#define ACPI_OBJECT_NAME_PCCH ((ULONG)'HCCP') +#define ACPI_OBJECT_NAME_PR0 ((ULONG)'0RP_') +#define ACPI_OBJECT_NAME_PR1 ((ULONG)'1RP_') +#define ACPI_OBJECT_NAME_PR2 ((ULONG)'2RP_') +#define ACPI_OBJECT_NAME_PR3 ((ULONG)'3RP_') +#define ACPI_OBJECT_NAME_PRS ((ULONG)'SRP_') +#define ACPI_OBJECT_NAME_PRT ((ULONG)'TRP_') +#define ACPI_OBJECT_NAME_PRW ((ULONG)'WRP_') +#define ACPI_OBJECT_NAME_PS0 ((ULONG)'0SP_') +#define ACPI_OBJECT_NAME_PS1 ((ULONG)'1SP_') +#define ACPI_OBJECT_NAME_PS2 ((ULONG)'2SP_') +#define ACPI_OBJECT_NAME_PS3 ((ULONG)'3SP_') +#define ACPI_OBJECT_NAME_PSC ((ULONG)'CSP_') +#define ACPI_OBJECT_NAME_PSL ((ULONG)'LSP_') +#define ACPI_OBJECT_NAME_PSV ((ULONG)'VSP_') +#define ACPI_OBJECT_NAME_PSW ((ULONG)'WSP_') +#define ACPI_OBJECT_NAME_PTS ((ULONG)'STP_') +#define ACPI_OBJECT_NAME_REG ((ULONG)'GER_') +#define ACPI_OBJECT_NAME_RMV ((ULONG)'VMR_') +#define ACPI_OBJECT_NAME_S0 ((ULONG)'_0S_') +#define ACPI_OBJECT_NAME_S0D ((ULONG)'D0S_') +#define ACPI_OBJECT_NAME_S0W ((ULONG)'W0S_') +#define ACPI_OBJECT_NAME_S1 ((ULONG)'_1S_') +#define ACPI_OBJECT_NAME_S1D ((ULONG)'D1S_') +#define ACPI_OBJECT_NAME_S1W ((ULONG)'W1S_') +#define ACPI_OBJECT_NAME_S2 ((ULONG)'_2S_') +#define ACPI_OBJECT_NAME_S2D ((ULONG)'D2S_') +#define ACPI_OBJECT_NAME_S2W ((ULONG)'W2S_') +#define ACPI_OBJECT_NAME_S3 ((ULONG)'_3S_') +#define ACPI_OBJECT_NAME_S3D ((ULONG)'D3S_') +#define ACPI_OBJECT_NAME_S3W ((ULONG)'W3S_') +#define ACPI_OBJECT_NAME_S4 ((ULONG)'_4S_') +#define ACPI_OBJECT_NAME_S4D ((ULONG)'D4S_') +#define ACPI_OBJECT_NAME_S4W ((ULONG)'W4S_') +#define ACPI_OBJECT_NAME_S5 ((ULONG)'_5S_') +#define ACPI_OBJECT_NAME_S5D ((ULONG)'D5S_') +#define ACPI_OBJECT_NAME_S5W ((ULONG)'W5S_') +#define ACPI_OBJECT_NAME_SCP ((ULONG)'PCS_') +#define ACPI_OBJECT_NAME_SEG ((ULONG)'GES_') +#define ACPI_OBJECT_NAME_SI ((ULONG)'_IS_') +#define ACPI_OBJECT_NAME_SRS ((ULONG)'SRS_') +#define ACPI_OBJECT_NAME_SST ((ULONG)'TSS_') +#define ACPI_OBJECT_NAME_STA ((ULONG)'ATS_') +#define ACPI_OBJECT_NAME_STD ((ULONG)'DTS_') +#define ACPI_OBJECT_NAME_SUB ((ULONG)'BUS_') +#define ACPI_OBJECT_NAME_SUN ((ULONG)'NUS_') +#define ACPI_OBJECT_NAME_SWD ((ULONG)'DWS_') +#define ACPI_OBJECT_NAME_TC1 ((ULONG)'1CT_') +#define ACPI_OBJECT_NAME_TC2 ((ULONG)'2CT_') +#define ACPI_OBJECT_NAME_TMP ((ULONG)'PMT_') +#define ACPI_OBJECT_NAME_TSP ((ULONG)'PST_') +#define ACPI_OBJECT_NAME_TZD ((ULONG)'DZT_') +#define ACPI_OBJECT_NAME_UID ((ULONG)'DIU_') +#define ACPI_OBJECT_NAME_WAK ((ULONG)'KAW_') +#define ACPI_OBJECT_NAME_BBN ((ULONG)'NBB_') +#define ACPI_OBJECT_NAME_PXM ((ULONG)'MXP_') +#define ACPI_OBJECT_NAME_PLD ((ULONG)'DLP_') +#define ACPI_OBJECT_NAME_REV ((ULONG)'VER_') + +#define NAME_NATIVE_METHOD(_Name) (((_Name) == NULL) ? "Unknown" : (_Name)) +#define NAME_DEBUG_INFO(_Info) (((_Info) == NULL) ? "" : (_Info)) + +// +//----------------------------------------------------------------------- Types +// + +typedef enum _PEP_MAJOR_DEVICE_TYPE { + PepMajorDeviceTypeProcessor, + PepMajorDeviceTypeAcpi, + PepMajorDeviceTypeMaximum +} PEP_MAJOR_DEVICE_TYPE, *PPEP_MAJOR_DEVICE_TYPE; + +C_ASSERT(PepMajorDeviceTypeMaximum <= 0xFF); // 8 bits max + +typedef enum _PEP_ACPI_MINOR_DEVICE_TYPE { + PepAcpiMinorTypeDevice, + PepAcpiMinorTypePowerResource, + PepAcpiMinorTypeThermalZone, + PepAcpiMinorTypeMaximum +} PEP_ACPI_MINOR_DEVICE_TYPE, *PPEP_ACPI_MINOR_DEVICE_TYPE; + +C_ASSERT(PepAcpiMinorTypeMaximum <= 0xFF); // 8 bits max + +#define PEP_INVALID_DEVICE_TYPE \ + PEP_MAKE_DEVICE_TYPE(PepMajorDeviceTypeMaximum, \ + PepAcpiMinorTypeMaximum, \ + 0xFFFF) + +typedef enum _PEP_NOTIFICATION_HANDLER_RESULT { + PEP_NOTIFICATION_HANDLER_COMPLETE, + PEP_NOTIFICATION_HANDLER_MORE_WORK, + PEP_NOTIFICATION_HANDLER_MAX +} PEP_NOTIFICATION_HANDLER_RESULT, + *PPEP_NOTIFICATION_HANDLER_RESULT; + +typedef +VOID +PEP_GENERAL_NOTIFICATION_HANDLER_ROUTINE ( + _In_ PVOID Data + ); +typedef PEP_GENERAL_NOTIFICATION_HANDLER_ROUTINE + *PPEP_GENERAL_NOTIFICATION_HANDLER_ROUTINE; + +typedef +PEP_NOTIFICATION_HANDLER_RESULT +PEP_NOTIFICATION_HANDLER_ROUTINE ( + _In_ PVOID Data, + _Out_opt_ PPEP_WORK_INFORMATION PoFxWorkInfo + ); +typedef PEP_NOTIFICATION_HANDLER_ROUTINE + *PPEP_NOTIFICATION_HANDLER_ROUTINE; + +typedef struct _PEP_GENERAL_NOTIFICATION_HANDLER { + ULONG Notification; + PPEP_GENERAL_NOTIFICATION_HANDLER_ROUTINE Handler; + CONST PCHAR Name; +} PEP_GENERAL_NOTIFICATION_HANDLER, *PPEP_GENERAL_NOTIFICATION_HANDLER; + +typedef struct _PEP_DEVICE_NOTIFICATION_HANDLER { + ULONG Notification; + PPEP_NOTIFICATION_HANDLER_ROUTINE Handler; + PPEP_NOTIFICATION_HANDLER_ROUTINE WorkerCallbackHandler; +} PEP_DEVICE_NOTIFICATION_HANDLER, *PPEP_DEVICE_NOTIFICATION_HANDLER; + +typedef ULONG _PEP_DEVICE_TYPE, PEP_DEVICE_TYPE, *PPEP_DEVICE_TYPE; + +typedef struct _PEP_OBJECT_INFORMATION { + ULONG ObjectName; + ULONG InputArgumentCount; + ULONG OutputArgumentCount; + PEP_ACPI_OBJECT_TYPE ObjectType; +} PEP_OBJECT_INFORMATION, *PPEP_OBJECT_INFORMATION; + +struct _PEP_INTERNAL_DEVICE_HEADER; + +typedef +NTSTATUS +PEP_DEVICE_CONTEXT_INITIALIZE ( + _In_ struct _PEP_INTERNAL_DEVICE_HEADER *Context + ); +typedef PEP_DEVICE_CONTEXT_INITIALIZE *PPEP_DEVICE_CONTEXT_INITIALIZE; + +typedef struct _PEP_DEVICE_DEFINITION { + PEP_DEVICE_TYPE Type; + ULONG ContextSize; + PPEP_DEVICE_CONTEXT_INITIALIZE Initialize; + + // + // The following fields are valid if device is DPM-owned. + // + + // + // The following fields are valid if device is ACPI-owned. + // + + ULONG ObjectCount; + _Field_size_(ObjectCount) PPEP_OBJECT_INFORMATION Objects; + ULONG AcpiNotificationHandlerCount; + _Field_size_(AcpiNotificationHandlerCount) + PPEP_DEVICE_NOTIFICATION_HANDLER AcpiNotificationHandlers; + ULONG DpmNotificationHandlerCount; + _Field_size_(DpmNotificationHandlerCount) + PPEP_DEVICE_NOTIFICATION_HANDLER DpmNotificationHandlers; +} PEP_DEVICE_DEFINITION, *PPEP_DEVICE_DEFINITION; + +// +// Common device object header +// + +typedef struct _PEP_INTERNAL_DEVICE_HEADER { + LIST_ENTRY ListEntry; + PEP_DEVICE_TYPE DeviceType; + POHANDLE KernelHandle; + PWSTR InstancePath; + KSPIN_LOCK Lock; + PPEP_DEVICE_DEFINITION DeviceDefinition; +} PEP_INTERNAL_DEVICE_HEADER, *PPEP_INTERNAL_DEVICE_HEADER; + +typedef struct _PEP_PROCESSOR_DEVICE { + PEP_INTERNAL_DEVICE_HEADER Header; + + // + // Processor identification + // + + PROCESSOR_NUMBER Processor; + ULONG NtNumber; + + // + // Idle states + // + + ULONG SelectedIdleState; + ULONG IdleStateCount; + PEP_PROCESSOR_IDLE_STATE_V2 IdleStates[10]; + + // + // Performance states + // + + ULONG CurrentPerformance; + +} PEP_PROCESSOR_DEVICE, *PPEP_PROCESSOR_DEVICE; + +typedef struct _PEP_ACPI_DEVICE { + PEP_INTERNAL_DEVICE_HEADER Header; +} PEP_ACPI_DEVICE, *PPEP_ACPI_DEVICE; + +typedef enum _PEP_NOTIFICATION_CLASS { + PEP_NOTIFICATION_CLASS_NONE = 0, + PEP_NOTIFICATION_CLASS_ACPI = 1, + PEP_NOTIFICATION_CLASS_DPM = 2, + PEP_NOTIFICATION_CLASS_PPM = 4 +} PEP_NOTIFICATION_CLASS, *PPEP_NOTIFICATION_CLASS; + +#define PEP_CHECK_DEVICE_TYPE_ACCEPTED(_Type, _Mask) \ + (((_Type) & (_Mask)) == (_Mask)) + +typedef enum _PEP_DEVICE_ID_MATCH { + PepDeviceIdMatchPartial, // Substring match. + PepDeviceIdMatchFull, // Whole string match. +} PEP_DEVICE_ID_MATCH, *PPEP_DEVICE_ID_MATCH; + +typedef struct _PEP_DEVICE_MATCH { + PEP_DEVICE_TYPE Type; + PEP_NOTIFICATION_CLASS OwnedType; + PWSTR DeviceId; + PEP_DEVICE_ID_MATCH CompareMethod; +} PEP_DEVICE_MATCH, *PPEP_DEVICE_MATCH; + +typedef enum _PEP_HANDLER_TYPE { + PepHandlerTypeSyncCritical, + PepHandlerTypeWorkerCallback +} PEP_HANDLER_TYPE, *PPEP_HANDLER_TYPE; + +typedef struct _PEP_WORK_CONTEXT { + + // + // Entry of this request on its current (pending or completed) queue. + // + + LIST_ENTRY ListEntry; + + // + // Request signature (for validation purposes). + // + + ULONG Signature; + + // + // The type of the request. + // + + PEP_NOTIFICATION_CLASS WorkType; + ULONG NotificationId; + BOOLEAN WorkCompleted; + + // + // The device for which the request is associated with. This value may be + // NULL if request is tagged as a parent. + // + + PPEP_INTERNAL_DEVICE_HEADER PepInternalDevice; + PPEP_DEVICE_DEFINITION DeviceDefinitionEntry; + + // + // PoFx-supplied PEP_WORK for work requests. + // + + PEP_WORK_INFORMATION LocalPoFxWorkInfo; + + // + // Work item context + // + + PVOID WorkContext; + SIZE_T WorkContextSize; + PNTSTATUS WorkRequestStatus; +} PEP_WORK_CONTEXT, *PPEP_WORK_CONTEXT; + +typedef struct _PEP_WORK_ITEM_CONTEXT { + WDFWORKITEM WorkItem; + PEP_NOTIFICATION_CLASS WorkType; +} PEP_WORK_ITEM_CONTEXT, *PPEP_WORK_ITEM_CONTEXT; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(PEP_WORK_ITEM_CONTEXT, PepGetWorkItemContext) + +// +//--------------------------------------------------------------------- Globals +// + +extern PEP_GENERAL_NOTIFICATION_HANDLER PepAcpiNotificationHandlers[]; +extern PEP_GENERAL_NOTIFICATION_HANDLER PepDpmNotificationHandlers[]; +extern PEP_KERNEL_INFORMATION PepKernelInformation; +extern LIST_ENTRY PepCompletedWorkList; +extern PEP_DEVICE_DEFINITION PepDeviceDefinitionArray[]; +extern ULONG PepDeviceDefinitionArraySize; +extern LIST_ENTRY PepDeviceList; +extern KSPIN_LOCK PepGlobalSpinLock; +extern WDFDEVICE PepGlobalWdfDevice; +extern PEP_DEVICE_MATCH PepDeviceMatchArray[]; +extern ULONG PepDeviceMatchArraySize; +extern LIST_ENTRY PepPendingWorkList; +extern BOOLEAN PepRegistered; +extern WDFSPINLOCK PepWorkListLock; + +// +//------------------------------------------------------------------ Prototypes +// + +// +// pep.c +// + +NTSTATUS +PepRegister ( + _In_ BOOLEAN AcpiHandlerOptIn, + _In_ BOOLEAN DpmHandlerOptIn, + _In_ BOOLEAN PpmHandlerOptIn + ); + +NTSTATUS +PepInitialize ( + _In_ PDRIVER_OBJECT WdmDriverObject, + _In_ WDFDRIVER WdfDriver, + _In_ PUNICODE_STRING RegistryPath + ); + +__drv_requiresIRQL(PASSIVE_LEVEL) +VOID +PepQueryRegisterOptions ( + _In_ PDRIVER_OBJECT WdmDriverObject, + _In_ WDFDRIVER WdfDriver, + _In_ PUNICODE_STRING RegistryPath, + _Out_ PBOOLEAN PepAcpiOptin, + _Out_ PBOOLEAN PepDpmOptin, + _Out_ PBOOLEAN PepPpmOptin + ); + +BOOLEAN +PepAcpiNotify ( + _In_ ULONG Notification, + _In_ PVOID Data + ); + +BOOLEAN +PepDpmNotify ( + _In_ ULONG Notification, + _In_ PVOID Data + ); + +BOOLEAN +PepPpmNotify ( + _In_ PEPHANDLE Handle, + _In_ ULONG Notification, + _Inout_opt_ PVOID Data + ); + +// acpinotify.c + +PEP_GENERAL_NOTIFICATION_HANDLER_ROUTINE PepAcpiPrepareDevice; +PEP_GENERAL_NOTIFICATION_HANDLER_ROUTINE PepAcpiAbandonDevice; +PEP_GENERAL_NOTIFICATION_HANDLER_ROUTINE PepAcpiRegisterDevice; +PEP_GENERAL_NOTIFICATION_HANDLER_ROUTINE PepAcpiUnregisterDevice; +PEP_GENERAL_NOTIFICATION_HANDLER_ROUTINE PepAcpiEnumerateDeviceNamespace; +PEP_GENERAL_NOTIFICATION_HANDLER_ROUTINE PepAcpiQueryObjectInformation; +PEP_GENERAL_NOTIFICATION_HANDLER_ROUTINE PepAcpiEvaluateControlMethod; +PEP_GENERAL_NOTIFICATION_HANDLER_ROUTINE PepAcpiQueryDeviceControlResources; +PEP_GENERAL_NOTIFICATION_HANDLER_ROUTINE PepAcpiTranslatedDeviceControlResources; +PEP_GENERAL_NOTIFICATION_HANDLER_ROUTINE PepAcpiWorkNotification; +PEP_GENERAL_NOTIFICATION_HANDLER_ROUTINE PepAcpiIgnoreDeviceNotification; + +// +// dpmnotify.c +// + +PEP_GENERAL_NOTIFICATION_HANDLER_ROUTINE PepDpmPrepareDevice; +PEP_GENERAL_NOTIFICATION_HANDLER_ROUTINE PepDpmAbandonDevice; +PEP_GENERAL_NOTIFICATION_HANDLER_ROUTINE PepDpmRegisterDevice; +PEP_GENERAL_NOTIFICATION_HANDLER_ROUTINE PepDpmUnregisterDevice; +PEP_GENERAL_NOTIFICATION_HANDLER_ROUTINE PepDpmIgnoreDeviceNotification; +PEP_GENERAL_NOTIFICATION_HANDLER_ROUTINE PepDpmWorkNotification; + +// +// driver.c +// + +NTSTATUS +DriverEntry ( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ); + +EVT_WDF_DRIVER_DEVICE_ADD PepEvtDeviceAdd; + +VOID +PepEvtDriverUnload ( + _In_ WDFDRIVER Driver + ); + +// +// util.c +// + +__drv_requiresIRQL(PASSIVE_LEVEL) +NTSTATUS +PepQueryRegistry ( + _Inout_opt_ WDFKEY *ParametersKey, + _In_opt_ WDFDRIVER WdfDriver, + _In_ PUNICODE_STRING RegistryPath, + _In_ PCUNICODE_STRING KeyValueName, + _Out_ PULONG OptionValue + ); + +BOOLEAN +PepIsDeviceAccepted ( + _In_ PEP_NOTIFICATION_CLASS OwnedType, + _In_ PCUNICODE_STRING DeviceId, + _Out_ PPEP_DEVICE_DEFINITION *DeviceDefinition + ); + +BOOLEAN +PepIsDeviceIdMatched ( + _In_ PWSTR String, + _In_ ULONG StringLength, + _In_ PWSTR SearchString, + _In_ ULONG SearchStringLength, + _In_ PEP_DEVICE_ID_MATCH DeviceIdCompareMethod + ); + +VOID +PepScheduleNotificationHandler ( + _In_ PEP_NOTIFICATION_CLASS WorkType, + _In_ ULONG NotificationId, + _In_ PPEP_INTERNAL_DEVICE_HEADER PepInternalDevice, + _In_opt_ PVOID WorkContext, + _In_ SIZE_T WorkContextSize, + _In_opt_ PNTSTATUS WorkRequestStatus + ); + +VOID +PepInvokeNotificationHandler ( + _In_ PEP_NOTIFICATION_CLASS WorkType, + _In_opt_ PPEP_WORK_CONTEXT WorkRequest, + _In_ PEP_HANDLER_TYPE HandlerType, + _In_ ULONG NotificationId, + _In_opt_ PPEP_INTERNAL_DEVICE_HEADER PepInternalDevice, + _In_ PVOID Data, + _In_ SIZE_T DataSize, + _In_opt_ PNTSTATUS WorkRequestStatus + ); + +PWSTR +PepGetDeviceName ( + _In_ ULONG DeviceType + ); + +VOID +PepRequestCommonWork ( + _In_ PPEP_ACPI_REQUEST_CONVERT_TO_BIOS_RESOURCES Request + ); + +NTSTATUS +PepGetBiosResourceSize ( + _In_ PPEP_ACPI_RESOURCE ResourceBuffer, + _In_ ULONG ResourceBufferSize, + _In_opt_ PCHAR DebugInfo, + _Out_ PSIZE_T BiosResourceSize + ); + +VOID +PepReturnBiosResource ( + _In_ PPEP_ACPI_RESOURCE ResourceBuffer, + _In_ ULONG ResourceBufferSize, + _In_ SIZE_T RequiredSize, + _In_ PACPI_METHOD_ARGUMENT Arguments, + _Inout_ PSIZE_T BiosResourcesSize, + _In_opt_ PCHAR DebugInfo, + _Out_ PNTSTATUS Status + ); + +VOID +PepReturnAcpiData ( + _In_ PVOID Value, + _In_ USHORT ValueType, + _In_ ULONG ValueLength, + _In_ BOOLEAN ReturnAsPackage, + _Out_ PACPI_METHOD_ARGUMENT Arguments, + _Inout_ PSIZE_T OutputArgumentSize, + _Out_opt_ PULONG OutputArgumentCount, + _Out_ PNTSTATUS Status, + _In_opt_ PCHAR MethodName, + _In_opt_ PCHAR DebugInfo, + _Out_ PPEP_NOTIFICATION_HANDLER_RESULT CompleteResult + ); + +// +// work.c +// + +NTSTATUS +PepCreateWorkRequest ( + _In_ PEP_NOTIFICATION_CLASS WorkType, + _In_ ULONG NotificationId, + _In_ PPEP_INTERNAL_DEVICE_HEADER PepInternalDevice, + _In_ PPEP_DEVICE_DEFINITION DeviceDefinitionEntry, + _In_opt_ PVOID WorkContext, + _In_ SIZE_T WorkContextSize, + _In_opt_ PNTSTATUS WorkRequestStatus, + _Out_ PPEP_WORK_CONTEXT *OutputWorkRequest + ); + +VOID +PepDestroyWorkRequest ( + _In_ PPEP_WORK_CONTEXT WorkRequest + ); + +VOID +PepPendWorkRequest ( + _In_ PPEP_WORK_CONTEXT WorkRequest + ); + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +PepCompleteWorkRequest ( + _In_ PPEP_WORK_CONTEXT WorkRequest + ); + +NTSTATUS +PepCompleteSelfManagedWork ( + _In_ PEP_NOTIFICATION_CLASS WorkType, + _In_ ULONG NotificationId, + _In_ PPEP_INTERNAL_DEVICE_HEADER PepInternalDevice, + _In_ PPEP_WORK_INFORMATION PoFxWorkInfo + ); + +VOID +PepMarkWorkRequestComplete ( + _In_ PPEP_WORK_CONTEXT WorkRequest + ); + +NTSTATUS +PepScheduleWorker ( + _In_ PPEP_WORK_CONTEXT WorkContext + ); + +VOID +PepWorkerWrapper ( + _In_ WDFWORKITEM WorkItem + ); + +VOID +PepProcessPendingWorkRequests ( + VOID + ); + +VOID +PepProcessCompleteWorkRequests ( + _In_ PVOID Data + ); + diff --git a/pofx/PEP/inc/trace.h b/pofx/PEP/inc/trace.h new file mode 100644 index 000000000..2a4f2e048 --- /dev/null +++ b/pofx/PEP/inc/trace.h @@ -0,0 +1,99 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + trace.h + +Abstract: + + This file contains prototypes and definitions related to debugging. + + +Environment: + + Kernel mode + +--*/ + +#pragma once + +#include // For TRACE_LEVEL definitions + +#if defined(EVENT_TRACING) + +#define WPP_CHECK_FOR_NULL_STRING // to prevent exceptions due to NULL strings. + +// +// WPP_DEFINE_CONTROL_GUID specifies the GUID used for Sample ACPI PEP driver. +// {CA02EEA8-D08F-4C60-BC75-B8C81C485342} +// + +#define WPP_CONTROL_GUIDS \ + WPP_DEFINE_CONTROL_GUID(SampleAcpiPepTraceGuid,(CA02EEA8,D08F,4C60,BC75,B8C81C485342), \ + WPP_DEFINE_BIT(DBG_FUNC_TRACE) /* bit 0 = 0x00000001 */ \ + WPP_DEFINE_BIT(DBG_INIT) /* bit 1 = 0x00000002 */ \ + WPP_DEFINE_BIT(DBG_PNP) /* bit 2 = 0x00000004 */ \ + WPP_DEFINE_BIT(DBG_POWER) /* bit 3 = 0x00000008 */ \ + WPP_DEFINE_BIT(DBG_WMI) /* bit 4 = 0x00000010 */ \ + WPP_DEFINE_BIT(DBG_IRP_CREATE_CLOSE) /* bit 5 = 0x00000020 */ \ + WPP_DEFINE_BIT(DBG_IOCTL) /* bit 6 = 0x00000040 */ \ + WPP_DEFINE_BIT(DBG_DEVICE) /* bit 7 = 0x00000080 */ \ + WPP_DEFINE_BIT(DBG_REQUEST) /* bit 8 = 0x00000100 */ \ + WPP_DEFINE_BIT(DBG_PEP) /* bit 9 = 0x00000200 */ \ + WPP_DEFINE_BIT(DBG_ACPI) /* bit 10 = 0x00000400 */ \ + ) + +#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) + +#define WPP_RECORDER_LEVEL_FLAGS_ARGS(lvl,flags) \ + WPP_CONTROL(WPP_BIT_ ## flags).AutoLogContext, 0, WPP_BIT_ ## flags +#define WPP_RECORDER_LEVEL_FLAGS_FILTER(lvl,flags) \ + (lvl < TRACE_LEVEL_VERBOSE || WPP_CONTROL(WPP_BIT_ ## flags).AutoLogVerboseEnabled) + +#else + +// +// Define Debug Flags. +// + +#define DBG_FUNC_TRACE 0x00000001 +#define DBG_INIT 0x00000002 +#define DBG_PNP 0x00000004 +#define DBG_POWER 0x00000008 +#define DBG_WMI 0x00000010 +#define DBG_IRP_CREATE_CLOSE 0x00000020 +#define DBG_IOCTL 0x00000040 +#define DBG_DEVICE 0x00000080 +#define DBG_REQUEST 0x00000100 +#define DBG_PEP 0x00000200 +#define DBG_ACPI 0x00000400 + +VOID +TraceEvents ( + __in ULONG DebugPrintLevel, + __in ULONG DebugPrintFlag, + __drv_formatString(printf) __in PCSTR DebugMessage, + ... + ); + +#define WPP_INIT_TRACING(DriverObject, RegistryPath) +#define WPP_CLEANUP(DriverObject) + +#endif + +// +// Friendly names for various trace levels. +// + +typedef enum _INTERNAL_TRACE_LEVELS { + ERROR = TRACE_LEVEL_ERROR, + WARN = TRACE_LEVEL_WARNING, + INFO = TRACE_LEVEL_INFORMATION, + VERBOSE = TRACE_LEVEL_VERBOSE +} INTERNAL_TRACE_LEVELS, *PINTERNAL_TRACE_LEVELS; + +#define DebugAssert(_exp) NT_ASSERT(_exp) diff --git a/pofx/PEP/pepsamples.sln b/pofx/PEP/pepsamples.sln new file mode 100644 index 000000000..3c5054cf2 --- /dev/null +++ b/pofx/PEP/pepsamples.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}") = "Common", "Common", "{44A59CCA-30D5-4C80-B52D-1AAF629FA621}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Acpi", "Acpi", "{548347FF-B381-420B-A607-AE23FDD8E376}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pepcommon", "common\pepcommon.vcxproj", "{AD3FF770-602D-4C51-B349-5A04033971EB}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sampleacpipep", "acpi\sampleacpipep.vcxproj", "{EF47791C-9559-4F0F-867F-A0E055054058}" + ProjectSection(ProjectDependencies) = postProject + {AD3FF770-602D-4C51-B349-5A04033971EB} = {AD3FF770-602D-4C51-B349-5A04033971EB} + 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 + {AD3FF770-602D-4C51-B349-5A04033971EB}.Debug|Win32.ActiveCfg = Debug|Win32 + {AD3FF770-602D-4C51-B349-5A04033971EB}.Debug|Win32.Build.0 = Debug|Win32 + {AD3FF770-602D-4C51-B349-5A04033971EB}.Release|Win32.ActiveCfg = Release|Win32 + {AD3FF770-602D-4C51-B349-5A04033971EB}.Release|Win32.Build.0 = Release|Win32 + {AD3FF770-602D-4C51-B349-5A04033971EB}.Debug|x64.ActiveCfg = Debug|x64 + {AD3FF770-602D-4C51-B349-5A04033971EB}.Debug|x64.Build.0 = Debug|x64 + {AD3FF770-602D-4C51-B349-5A04033971EB}.Release|x64.ActiveCfg = Release|x64 + {AD3FF770-602D-4C51-B349-5A04033971EB}.Release|x64.Build.0 = Release|x64 + {EF47791C-9559-4F0F-867F-A0E055054058}.Debug|Win32.ActiveCfg = Debug|Win32 + {EF47791C-9559-4F0F-867F-A0E055054058}.Debug|Win32.Build.0 = Debug|Win32 + {EF47791C-9559-4F0F-867F-A0E055054058}.Release|Win32.ActiveCfg = Release|Win32 + {EF47791C-9559-4F0F-867F-A0E055054058}.Release|Win32.Build.0 = Release|Win32 + {EF47791C-9559-4F0F-867F-A0E055054058}.Debug|x64.ActiveCfg = Debug|x64 + {EF47791C-9559-4F0F-867F-A0E055054058}.Debug|x64.Build.0 = Debug|x64 + {EF47791C-9559-4F0F-867F-A0E055054058}.Release|x64.ActiveCfg = Release|x64 + {EF47791C-9559-4F0F-867F-A0E055054058}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {AD3FF770-602D-4C51-B349-5A04033971EB} = {44A59CCA-30D5-4C80-B52D-1AAF629FA621} + {EF47791C-9559-4F0F-867F-A0E055054058} = {548347FF-B381-420B-A607-AE23FDD8E376} + EndGlobalSection +EndGlobal diff --git a/pofx/UMDF2/App/PowerFxApp.vcxproj b/pofx/UMDF2/App/PowerFxApp.vcxproj index eb59260d8..2c9869566 100644 --- a/pofx/UMDF2/App/PowerFxApp.vcxproj +++ b/pofx/UMDF2/App/PowerFxApp.vcxproj @@ -19,11 +19,11 @@ - {14E1832B-DF5A-4B4C-A079-A9446881E742} + {21ED4A9A-E9AA-4337-A59A-118571AE54D9} $(MSBuildProjectName) Debug Win32 - {9DB58C83-24AF-4AFE-B48E-AD1449C17B9E} + {A97E9D3C-41CB-4AD7-9E98-11D0397FF579} @@ -93,16 +93,19 @@ %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc true Level4 + %(PreprocessorDefinitions);___TARGETNAME="""PowerFxApp.PROGRAM""" %(PreprocessorDefinitions);_UNICODE;UNICODE %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + %(PreprocessorDefinitions);___TARGETNAME="""PowerFxApp.PROGRAM""" %(PreprocessorDefinitions);_UNICODE;UNICODE %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + %(PreprocessorDefinitions);___TARGETNAME="""PowerFxApp.PROGRAM""" %(AdditionalDependencies);mincore.lib @@ -114,16 +117,19 @@ %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc true Level4 + %(PreprocessorDefinitions);___TARGETNAME="""PowerFxApp.PROGRAM""" %(PreprocessorDefinitions);_UNICODE;UNICODE %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + %(PreprocessorDefinitions);___TARGETNAME="""PowerFxApp.PROGRAM""" %(PreprocessorDefinitions);_UNICODE;UNICODE %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + %(PreprocessorDefinitions);___TARGETNAME="""PowerFxApp.PROGRAM""" %(AdditionalDependencies);mincore.lib @@ -135,16 +141,19 @@ %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc true Level4 + %(PreprocessorDefinitions);___TARGETNAME="""PowerFxApp.PROGRAM""" %(PreprocessorDefinitions);_UNICODE;UNICODE %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + %(PreprocessorDefinitions);___TARGETNAME="""PowerFxApp.PROGRAM""" %(PreprocessorDefinitions);_UNICODE;UNICODE %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + %(PreprocessorDefinitions);___TARGETNAME="""PowerFxApp.PROGRAM""" %(AdditionalDependencies);mincore.lib @@ -156,16 +165,19 @@ %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc true Level4 + %(PreprocessorDefinitions);___TARGETNAME="""PowerFxApp.PROGRAM""" %(PreprocessorDefinitions);_UNICODE;UNICODE %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + %(PreprocessorDefinitions);___TARGETNAME="""PowerFxApp.PROGRAM""" %(PreprocessorDefinitions);_UNICODE;UNICODE %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + %(PreprocessorDefinitions);___TARGETNAME="""PowerFxApp.PROGRAM""" %(AdditionalDependencies);mincore.lib @@ -178,7 +190,6 @@ - diff --git a/pofx/UMDF2/App/PowerFxApp.vcxproj.Filters b/pofx/UMDF2/App/PowerFxApp.vcxproj.Filters index 9a2f12691..60cbcf7b8 100644 --- a/pofx/UMDF2/App/PowerFxApp.vcxproj.Filters +++ b/pofx/UMDF2/App/PowerFxApp.vcxproj.Filters @@ -3,15 +3,15 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {20A2E7AD-88FC-4506-B935-ECBEEE1ADBA4} + {DDF47081-1E94-4138-8A95-3B83CFE8F7EF} h;hpp;hxx;hm;inl;inc;xsd - {B9BF62AC-CE9A-4203-BEF5-D3532A0B4D2F} + {E7D702DC-830A-4133-A615-169A82296264} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {019B37E5-DD40-4E7A-8889-3F4D51462380} + {CEC1B556-1599-4C29-9E19-52CA57C39F2B} diff --git a/pofx/UMDF2/Driver/SingleComp/SingleComponentSingleStateUm.inx b/pofx/UMDF2/Driver/SingleComp/SingleComponentSingleStateUm.inx index ef4eed6f3..2d4120648 100644 --- a/pofx/UMDF2/Driver/SingleComp/SingleComponentSingleStateUm.inx +++ b/pofx/UMDF2/Driver/SingleComp/SingleComponentSingleStateUm.inx @@ -17,7 +17,7 @@ Signature="$WINDOWS NT$" Class=Sample ClassGuid={78A1C341-4539-11d3-B88D-00C04FAD5171} -Provider=%MSFT% +Provider=%ProviderString% DriverVer=03/20/2003,5.00.3788 CatalogFile=wudf.cat @@ -81,7 +81,7 @@ UmdfLibraryVersion=$UMDFVERSION$ ServiceBinary=%12%\UMDF\SingleComponentSingleStateUm.dll [Strings] -MSFT = "Microsoft" +ProviderString = "TODO-Set-Provider" 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" diff --git a/pofx/UMDF2/Driver/SingleComp/SingleComponentSingleStateUm.vcxproj b/pofx/UMDF2/Driver/SingleComp/SingleComponentSingleStateUm.vcxproj index fd73ebc01..66e7ef575 100644 --- a/pofx/UMDF2/Driver/SingleComp/SingleComponentSingleStateUm.vcxproj +++ b/pofx/UMDF2/Driver/SingleComp/SingleComponentSingleStateUm.vcxproj @@ -19,12 +19,12 @@ - {54734149-462F-4F08-8921-8657E8244D5D} + {B1406B05-136D-40B7-AE20-4F38D724A219} $(MSBuildProjectName) 2 Debug Win32 - {0595E788-E11C-4E8F-9E6A-BDA9CD38B976} + {A7C29AAB-AA74-4214-8DD8-5F6F98AD3BE2} @@ -107,6 +107,7 @@ Level4 %(AdditionalIncludeDirectories);..\..\inc %(PreprocessorDefinitions);UNICODE;_UNICODE + %(PreprocessorDefinitions);___TARGETNAME="""SingleComponentSingleStateUm.DYNLINK""" @@ -114,11 +115,13 @@ %(PreprocessorDefinitions);WPP_MACRO_USE_KM_VERSION_FOR_UM=1 %(AdditionalIncludeDirectories);..\..\inc %(PreprocessorDefinitions);UNICODE;_UNICODE + %(PreprocessorDefinitions);___TARGETNAME="""SingleComponentSingleStateUm.DYNLINK""" %(PreprocessorDefinitions);WPP_MACRO_USE_KM_VERSION_FOR_UM=1 %(AdditionalIncludeDirectories);..\..\inc %(PreprocessorDefinitions);UNICODE;_UNICODE + %(PreprocessorDefinitions);___TARGETNAME="""SingleComponentSingleStateUm.DYNLINK""" %(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib @@ -131,6 +134,7 @@ Level4 %(AdditionalIncludeDirectories);..\..\inc %(PreprocessorDefinitions);UNICODE;_UNICODE + %(PreprocessorDefinitions);___TARGETNAME="""SingleComponentSingleStateUm.DYNLINK""" @@ -138,11 +142,13 @@ %(PreprocessorDefinitions);WPP_MACRO_USE_KM_VERSION_FOR_UM=1 %(AdditionalIncludeDirectories);..\..\inc %(PreprocessorDefinitions);UNICODE;_UNICODE + %(PreprocessorDefinitions);___TARGETNAME="""SingleComponentSingleStateUm.DYNLINK""" %(PreprocessorDefinitions);WPP_MACRO_USE_KM_VERSION_FOR_UM=1 %(AdditionalIncludeDirectories);..\..\inc %(PreprocessorDefinitions);UNICODE;_UNICODE + %(PreprocessorDefinitions);___TARGETNAME="""SingleComponentSingleStateUm.DYNLINK""" %(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib @@ -155,6 +161,7 @@ Level4 %(AdditionalIncludeDirectories);..\..\inc %(PreprocessorDefinitions);UNICODE;_UNICODE + %(PreprocessorDefinitions);___TARGETNAME="""SingleComponentSingleStateUm.DYNLINK""" @@ -162,11 +169,13 @@ %(PreprocessorDefinitions);WPP_MACRO_USE_KM_VERSION_FOR_UM=1 %(AdditionalIncludeDirectories);..\..\inc %(PreprocessorDefinitions);UNICODE;_UNICODE + %(PreprocessorDefinitions);___TARGETNAME="""SingleComponentSingleStateUm.DYNLINK""" %(PreprocessorDefinitions);WPP_MACRO_USE_KM_VERSION_FOR_UM=1 %(AdditionalIncludeDirectories);..\..\inc %(PreprocessorDefinitions);UNICODE;_UNICODE + %(PreprocessorDefinitions);___TARGETNAME="""SingleComponentSingleStateUm.DYNLINK""" %(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib @@ -179,6 +188,7 @@ Level4 %(AdditionalIncludeDirectories);..\..\inc %(PreprocessorDefinitions);UNICODE;_UNICODE + %(PreprocessorDefinitions);___TARGETNAME="""SingleComponentSingleStateUm.DYNLINK""" @@ -186,11 +196,13 @@ %(PreprocessorDefinitions);WPP_MACRO_USE_KM_VERSION_FOR_UM=1 %(AdditionalIncludeDirectories);..\..\inc %(PreprocessorDefinitions);UNICODE;_UNICODE + %(PreprocessorDefinitions);___TARGETNAME="""SingleComponentSingleStateUm.DYNLINK""" %(PreprocessorDefinitions);WPP_MACRO_USE_KM_VERSION_FOR_UM=1 %(AdditionalIncludeDirectories);..\..\inc %(PreprocessorDefinitions);UNICODE;_UNICODE + %(PreprocessorDefinitions);___TARGETNAME="""SingleComponentSingleStateUm.DYNLINK""" %(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib @@ -199,7 +211,6 @@ - diff --git a/pofx/UMDF2/Driver/SingleComp/SingleComponentSingleStateUm.vcxproj.Filters b/pofx/UMDF2/Driver/SingleComp/SingleComponentSingleStateUm.vcxproj.Filters index 1d74c304c..06b92c7db 100644 --- a/pofx/UMDF2/Driver/SingleComp/SingleComponentSingleStateUm.vcxproj.Filters +++ b/pofx/UMDF2/Driver/SingleComp/SingleComponentSingleStateUm.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {7C049D51-D629-4AF2-8583-A455CA3F6C83} + {E7E0A430-9CF0-4086-A0C7-41EE8E78549E} h;hpp;hxx;hm;inl;inc;xsd - {D157BB47-2A77-4F69-B485-150DF7755B16} + {AD32486E-D07C-46E3-ADB8-F2402155F314} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {0D9C7FEA-F5BC-44BC-82E3-86171939BAE4} + {FD85F7C5-1B90-451A-9FA2-1C9866B76D2A} inf;inv;inx;mof;mc; - {4B52E5EA-1DD3-41C5-B7B3-6530888DA16A} + {3920BA60-B0CF-461F-AC28-961B58798303} @@ -27,9 +27,6 @@ - - Driver Files - Driver Files diff --git a/pofx/UMDF2/ReadMe.md b/pofx/UMDF2/ReadMe.md index 5778bc45d..e492a3bdd 100644 --- a/pofx/UMDF2/ReadMe.md +++ b/pofx/UMDF2/ReadMe.md @@ -6,9 +6,6 @@ This solution demonstrates how a User-Mode Driver Framework (UMDF) version 2 dri ## Universal Windows Driver Compliant This sample builds a Universal Windows Driver. It uses only APIs and DDIs that are included in OneCoreUAP. -**Note** -Due to a known bug in the current Windows Driver Kit tools, when building this sample in Debug mode, the exe project may report some ApiValidator warnings. These can be safely ignored. - Related technologies -------------------- For related information, see the [KMDF Power Framework (PoFx) Sample](http://code.msdn.microsoft.com/windowshardware/PoFx-1974b51c). diff --git a/pofx/UMDF2/pofx.sln b/pofx/UMDF2/pofx.sln index ea920a248..133119d43 100644 --- a/pofx/UMDF2/pofx.sln +++ b/pofx/UMDF2/pofx.sln @@ -3,15 +3,15 @@ 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", "{EE8B0086-FE51-41C2-BC68-73296D84902B}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "App", "App", "{731A0A49-3729-494E-8418-F40171C91480}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SingleComp", "SingleComp", "{86B4235E-0C5C-4A44-ABD0-C43595C7B39B}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SingleComp", "SingleComp", "{D9E043A6-6577-4BCA-B9F0-56ABD68A9480}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Driver", "Driver", "{B554B2DD-3D1D-4665-850B-BB3D3E655C69}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Driver", "Driver", "{7E8F4CDD-DC14-4362-8380-C7725C743960}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PowerFxApp", "App\PowerFxApp.vcxproj", "{14E1832B-DF5A-4B4C-A079-A9446881E742}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PowerFxApp", "App\PowerFxApp.vcxproj", "{21ED4A9A-E9AA-4337-A59A-118571AE54D9}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SingleComponentSingleStateUm", "Driver\SingleComp\SingleComponentSingleStateUm.vcxproj", "{54734149-462F-4F08-8921-8657E8244D5D}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SingleComponentSingleStateUm", "Driver\SingleComp\SingleComponentSingleStateUm.vcxproj", "{B1406B05-136D-40B7-AE20-4F38D724A219}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -21,29 +21,29 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {14E1832B-DF5A-4B4C-A079-A9446881E742}.Debug|Win32.ActiveCfg = Debug|Win32 - {14E1832B-DF5A-4B4C-A079-A9446881E742}.Debug|Win32.Build.0 = Debug|Win32 - {14E1832B-DF5A-4B4C-A079-A9446881E742}.Release|Win32.ActiveCfg = Release|Win32 - {14E1832B-DF5A-4B4C-A079-A9446881E742}.Release|Win32.Build.0 = Release|Win32 - {14E1832B-DF5A-4B4C-A079-A9446881E742}.Debug|x64.ActiveCfg = Debug|x64 - {14E1832B-DF5A-4B4C-A079-A9446881E742}.Debug|x64.Build.0 = Debug|x64 - {14E1832B-DF5A-4B4C-A079-A9446881E742}.Release|x64.ActiveCfg = Release|x64 - {14E1832B-DF5A-4B4C-A079-A9446881E742}.Release|x64.Build.0 = Release|x64 - {54734149-462F-4F08-8921-8657E8244D5D}.Debug|Win32.ActiveCfg = Debug|Win32 - {54734149-462F-4F08-8921-8657E8244D5D}.Debug|Win32.Build.0 = Debug|Win32 - {54734149-462F-4F08-8921-8657E8244D5D}.Release|Win32.ActiveCfg = Release|Win32 - {54734149-462F-4F08-8921-8657E8244D5D}.Release|Win32.Build.0 = Release|Win32 - {54734149-462F-4F08-8921-8657E8244D5D}.Debug|x64.ActiveCfg = Debug|x64 - {54734149-462F-4F08-8921-8657E8244D5D}.Debug|x64.Build.0 = Debug|x64 - {54734149-462F-4F08-8921-8657E8244D5D}.Release|x64.ActiveCfg = Release|x64 - {54734149-462F-4F08-8921-8657E8244D5D}.Release|x64.Build.0 = Release|x64 + {21ED4A9A-E9AA-4337-A59A-118571AE54D9}.Debug|Win32.ActiveCfg = Debug|Win32 + {21ED4A9A-E9AA-4337-A59A-118571AE54D9}.Debug|Win32.Build.0 = Debug|Win32 + {21ED4A9A-E9AA-4337-A59A-118571AE54D9}.Release|Win32.ActiveCfg = Release|Win32 + {21ED4A9A-E9AA-4337-A59A-118571AE54D9}.Release|Win32.Build.0 = Release|Win32 + {21ED4A9A-E9AA-4337-A59A-118571AE54D9}.Debug|x64.ActiveCfg = Debug|x64 + {21ED4A9A-E9AA-4337-A59A-118571AE54D9}.Debug|x64.Build.0 = Debug|x64 + {21ED4A9A-E9AA-4337-A59A-118571AE54D9}.Release|x64.ActiveCfg = Release|x64 + {21ED4A9A-E9AA-4337-A59A-118571AE54D9}.Release|x64.Build.0 = Release|x64 + {B1406B05-136D-40B7-AE20-4F38D724A219}.Debug|Win32.ActiveCfg = Debug|Win32 + {B1406B05-136D-40B7-AE20-4F38D724A219}.Debug|Win32.Build.0 = Debug|Win32 + {B1406B05-136D-40B7-AE20-4F38D724A219}.Release|Win32.ActiveCfg = Release|Win32 + {B1406B05-136D-40B7-AE20-4F38D724A219}.Release|Win32.Build.0 = Release|Win32 + {B1406B05-136D-40B7-AE20-4F38D724A219}.Debug|x64.ActiveCfg = Debug|x64 + {B1406B05-136D-40B7-AE20-4F38D724A219}.Debug|x64.Build.0 = Debug|x64 + {B1406B05-136D-40B7-AE20-4F38D724A219}.Release|x64.ActiveCfg = Release|x64 + {B1406B05-136D-40B7-AE20-4F38D724A219}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {14E1832B-DF5A-4B4C-A079-A9446881E742} = {EE8B0086-FE51-41C2-BC68-73296D84902B} - {54734149-462F-4F08-8921-8657E8244D5D} = {86B4235E-0C5C-4A44-ABD0-C43595C7B39B} - {86B4235E-0C5C-4A44-ABD0-C43595C7B39B} = {B554B2DD-3D1D-4665-850B-BB3D3E655C69} + {21ED4A9A-E9AA-4337-A59A-118571AE54D9} = {731A0A49-3729-494E-8418-F40171C91480} + {B1406B05-136D-40B7-AE20-4F38D724A219} = {D9E043A6-6577-4BCA-B9F0-56ABD68A9480} + {D9E043A6-6577-4BCA-B9F0-56ABD68A9480} = {7E8F4CDD-DC14-4362-8380-C7725C743960} EndGlobalSection EndGlobal diff --git a/pofx/WDF/App/PowerFxApp.vcxproj b/pofx/WDF/App/PowerFxApp.vcxproj index 6bab5de8b..db7c20d3f 100644 --- a/pofx/WDF/App/PowerFxApp.vcxproj +++ b/pofx/WDF/App/PowerFxApp.vcxproj @@ -19,11 +19,11 @@ - {1A459D79-27B4-4E77-A42D-CC2F498A5822} + {8F8494D9-52B9-44B8-80C4-0D0053716870} $(MSBuildProjectName) Debug Win32 - {346F7370-1A09-4402-8CD3-22F8DAEEA624} + {C24C6508-5E4D-4755-B9C9-2B96218CC7DB} @@ -93,16 +93,19 @@ %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc true Level4 + %(PreprocessorDefinitions);___TARGETNAME="""PowerFxApp.PROGRAM""" %(PreprocessorDefinitions);_UNICODE;UNICODE %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + %(PreprocessorDefinitions);___TARGETNAME="""PowerFxApp.PROGRAM""" %(PreprocessorDefinitions);_UNICODE;UNICODE %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + %(PreprocessorDefinitions);___TARGETNAME="""PowerFxApp.PROGRAM""" %(AdditionalDependencies);mincore.lib @@ -114,16 +117,19 @@ %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc true Level4 + %(PreprocessorDefinitions);___TARGETNAME="""PowerFxApp.PROGRAM""" %(PreprocessorDefinitions);_UNICODE;UNICODE %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + %(PreprocessorDefinitions);___TARGETNAME="""PowerFxApp.PROGRAM""" %(PreprocessorDefinitions);_UNICODE;UNICODE %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + %(PreprocessorDefinitions);___TARGETNAME="""PowerFxApp.PROGRAM""" %(AdditionalDependencies);mincore.lib @@ -135,16 +141,19 @@ %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc true Level4 + %(PreprocessorDefinitions);___TARGETNAME="""PowerFxApp.PROGRAM""" %(PreprocessorDefinitions);_UNICODE;UNICODE %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + %(PreprocessorDefinitions);___TARGETNAME="""PowerFxApp.PROGRAM""" %(PreprocessorDefinitions);_UNICODE;UNICODE %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + %(PreprocessorDefinitions);___TARGETNAME="""PowerFxApp.PROGRAM""" %(AdditionalDependencies);mincore.lib @@ -156,16 +165,19 @@ %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc true Level4 + %(PreprocessorDefinitions);___TARGETNAME="""PowerFxApp.PROGRAM""" %(PreprocessorDefinitions);_UNICODE;UNICODE %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + %(PreprocessorDefinitions);___TARGETNAME="""PowerFxApp.PROGRAM""" %(PreprocessorDefinitions);_UNICODE;UNICODE %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc + %(PreprocessorDefinitions);___TARGETNAME="""PowerFxApp.PROGRAM""" %(AdditionalDependencies);mincore.lib @@ -178,7 +190,6 @@ - diff --git a/pofx/WDF/App/PowerFxApp.vcxproj.Filters b/pofx/WDF/App/PowerFxApp.vcxproj.Filters index 48b861b9f..13a8b406d 100644 --- a/pofx/WDF/App/PowerFxApp.vcxproj.Filters +++ b/pofx/WDF/App/PowerFxApp.vcxproj.Filters @@ -3,15 +3,15 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {DBFF1BEC-1FF0-4FC6-A3FB-36AB6A323683} + {8639B1FA-39F3-4BAE-85C0-36083D05DAF1} h;hpp;hxx;hm;inl;inc;xsd - {8DEA6B64-1CF1-4A28-B5D3-BFCA577AE595} + {727CB0E5-A5F9-419E-89BA-F38B39BB3DBF} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {F09BE8A3-EFB1-48A4-B813-6AE290BFBD71} + {B6122C14-06F7-4395-8C2D-3C240EF14875} diff --git a/pofx/WDF/Driver/MultiComp/driver/WdfMultiComp.inx b/pofx/WDF/Driver/MultiComp/driver/WdfMultiComp.inx index e6b93ed05..84d825358 100644 --- a/pofx/WDF/Driver/MultiComp/driver/WdfMultiComp.inx +++ b/pofx/WDF/Driver/MultiComp/driver/WdfMultiComp.inx @@ -6,7 +6,7 @@ Signature="$Windows NT$" Class=Sample ClassGuid={78A1C341-4539-11d3-B88D-00C04FAD5171} -Provider=%MSFT% +Provider=%ProviderString% CatalogFile=KmdfSamples.cat DriverVer=03/27/2011,0.0.0.1 @@ -33,7 +33,7 @@ HKR,,Icon,,-5 ; =================== KMDF Multi-Component Device ================================== [Manufacturer] -%MSFT%=Microsoft,NT$ARCH$ +%ManufacturerString%=Microsoft,NT$ARCH$ [Microsoft.NT$ARCH$] %WdfMultiCompDeviceName%=WdfMultiComp_Install,WDF\WdfMultiComp @@ -72,7 +72,8 @@ KmdfLibraryVersion=$KMDFVERSION$ ; =================== Generic ================================== [Strings] -MSFT="Microsoft" +ProviderString = "TODO-Set-Provider" +ManufacturerString = "TODO-Set-Manufacturer" 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 index 4253010f6..584585e4c 100644 --- a/pofx/WDF/Driver/MultiComp/driver/WdfMultiComp.vcxproj +++ b/pofx/WDF/Driver/MultiComp/driver/WdfMultiComp.vcxproj @@ -19,12 +19,12 @@ - {E47411F4-AAEA-4766-B62B-A5171ED47CCF} + {188D1CB3-96AB-43B5-8BCA-70D6115DC2EB} $(MSBuildProjectName) 1 Debug Win32 - {499A6FF7-57CD-4962-9500-BCF78D83750D} + {9F669764-A62B-46FE-B708-C9EEA2B7A6D3} @@ -104,14 +104,17 @@ true Level4 %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\..\inc + %(PreprocessorDefinitions);___TARGETNAME="""WdfMultiComp.DRIVER""" %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\..\inc + %(PreprocessorDefinitions);___TARGETNAME="""WdfMultiComp.DRIVER""" %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\..\inc + %(PreprocessorDefinitions);___TARGETNAME="""WdfMultiComp.DRIVER""" %(AdditionalDependencies);.\..\lib\$(IntDir)\WdfPoFx.lib @@ -122,14 +125,17 @@ true Level4 %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\..\inc + %(PreprocessorDefinitions);___TARGETNAME="""WdfMultiComp.DRIVER""" %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\..\inc + %(PreprocessorDefinitions);___TARGETNAME="""WdfMultiComp.DRIVER""" %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\..\inc + %(PreprocessorDefinitions);___TARGETNAME="""WdfMultiComp.DRIVER""" %(AdditionalDependencies);.\..\lib\$(IntDir)\WdfPoFx.lib @@ -140,14 +146,17 @@ true Level4 %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\..\inc + %(PreprocessorDefinitions);___TARGETNAME="""WdfMultiComp.DRIVER""" %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\..\inc + %(PreprocessorDefinitions);___TARGETNAME="""WdfMultiComp.DRIVER""" %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\..\inc + %(PreprocessorDefinitions);___TARGETNAME="""WdfMultiComp.DRIVER""" %(AdditionalDependencies);.\..\lib\$(IntDir)\WdfPoFx.lib @@ -158,14 +167,17 @@ true Level4 %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\..\inc + %(PreprocessorDefinitions);___TARGETNAME="""WdfMultiComp.DRIVER""" %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\..\inc + %(PreprocessorDefinitions);___TARGETNAME="""WdfMultiComp.DRIVER""" %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\..\..\inc + %(PreprocessorDefinitions);___TARGETNAME="""WdfMultiComp.DRIVER""" %(AdditionalDependencies);.\..\lib\$(IntDir)\WdfPoFx.lib @@ -174,7 +186,6 @@ - diff --git a/pofx/WDF/Driver/MultiComp/driver/WdfMultiComp.vcxproj.Filters b/pofx/WDF/Driver/MultiComp/driver/WdfMultiComp.vcxproj.Filters index deab83ee8..3e633a865 100644 --- a/pofx/WDF/Driver/MultiComp/driver/WdfMultiComp.vcxproj.Filters +++ b/pofx/WDF/Driver/MultiComp/driver/WdfMultiComp.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {0A9BD918-2221-4EB8-ADEE-BBD2CDD7492D} + {EA2B4AA3-6F74-4B21-8D79-F7F95D68E11D} h;hpp;hxx;hm;inl;inc;xsd - {919CB3FE-5154-4606-A075-B786F5FFFE57} + {17A66FDC-27CC-4BE2-98B3-FADA23D7A0C0} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {425BB5CA-635C-4170-8213-7A71C5055DC8} + {E53B8D43-3637-4AF9-BF63-19646FD67F74} inf;inv;inx;mof;mc; - {3CF8A5B4-951A-49C5-ADD3-0B82C94AD6CA} + {A926CCE1-F819-433D-9478-3EE8A0AFF8CA} @@ -27,9 +27,6 @@ - - Driver Files - Driver Files diff --git a/pofx/WDF/Driver/MultiComp/lib/WdfPoFx.vcxproj b/pofx/WDF/Driver/MultiComp/lib/WdfPoFx.vcxproj index 5e6d09e7b..04b9675a7 100644 --- a/pofx/WDF/Driver/MultiComp/lib/WdfPoFx.vcxproj +++ b/pofx/WDF/Driver/MultiComp/lib/WdfPoFx.vcxproj @@ -19,12 +19,12 @@ - {30087DFC-5BF1-4BF6-A3C2-EB767B04A346} + {6F5CB5B9-470D-49F1-8C84-3BBD828E7F34} $(MSBuildProjectName) 1 Debug Win32 - {0BE1315A-9E67-4D52-9B4A-7C3D83D80492} + {D6580125-3CC1-4E07-8BF3-BEF7C714FD13} @@ -169,7 +169,6 @@ - diff --git a/pofx/WDF/Driver/MultiComp/lib/WdfPoFx.vcxproj.Filters b/pofx/WDF/Driver/MultiComp/lib/WdfPoFx.vcxproj.Filters index 6aa5f9105..46754208f 100644 --- a/pofx/WDF/Driver/MultiComp/lib/WdfPoFx.vcxproj.Filters +++ b/pofx/WDF/Driver/MultiComp/lib/WdfPoFx.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {3D1EA9F6-249A-4E28-B709-6DF1A0364A1D} + {FEA07780-0554-4950-A6AA-EC74D647F750} h;hpp;hxx;hm;inl;inc;xsd - {F52C45CC-11FD-4B95-ACC4-8A9F96EE4C70} + {E923B5FD-33B2-488E-B3DF-D77981752321} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {DF786B9D-15CF-4A15-A833-C326C1B42E70} + {7306C4A1-F2CC-4150-B6DF-1857BAD0D0EC} inf;inv;inx;mof;mc; - {478A4E92-8150-4A53-8756-5B211AA8FF2F} + {C2725E02-5167-47AA-8A56-27B3C7A94B84} diff --git a/pofx/WDF/Driver/SingleComp/SingleComponentFStateDriver.vcxproj b/pofx/WDF/Driver/SingleComp/SingleComponentFStateDriver.vcxproj index 2e912d511..10a599d29 100644 --- a/pofx/WDF/Driver/SingleComp/SingleComponentFStateDriver.vcxproj +++ b/pofx/WDF/Driver/SingleComp/SingleComponentFStateDriver.vcxproj @@ -19,12 +19,12 @@ - {288FFC86-69F2-41A0-A08B-CD42CFF51401} + {BFB8BE60-7C1E-416D-B561-5700DCC94082} $(MSBuildProjectName) 1 Debug Win32 - {BA711105-13CB-4276-BD41-6D3CCAA448F7} + {440D7A5B-44B1-492A-91B4-F80489B06979} @@ -104,14 +104,17 @@ true Level4 %(AdditionalIncludeDirectories);..\..\inc + %(PreprocessorDefinitions);___TARGETNAME="""SingleComponentFStateDriver.DRIVER""" %(AdditionalIncludeDirectories);..\..\inc + %(PreprocessorDefinitions);___TARGETNAME="""SingleComponentFStateDriver.DRIVER""" %(AdditionalIncludeDirectories);..\..\inc + %(PreprocessorDefinitions);___TARGETNAME="""SingleComponentFStateDriver.DRIVER""" @@ -119,14 +122,17 @@ true Level4 %(AdditionalIncludeDirectories);..\..\inc + %(PreprocessorDefinitions);___TARGETNAME="""SingleComponentFStateDriver.DRIVER""" %(AdditionalIncludeDirectories);..\..\inc + %(PreprocessorDefinitions);___TARGETNAME="""SingleComponentFStateDriver.DRIVER""" %(AdditionalIncludeDirectories);..\..\inc + %(PreprocessorDefinitions);___TARGETNAME="""SingleComponentFStateDriver.DRIVER""" @@ -134,14 +140,17 @@ true Level4 %(AdditionalIncludeDirectories);..\..\inc + %(PreprocessorDefinitions);___TARGETNAME="""SingleComponentFStateDriver.DRIVER""" %(AdditionalIncludeDirectories);..\..\inc + %(PreprocessorDefinitions);___TARGETNAME="""SingleComponentFStateDriver.DRIVER""" %(AdditionalIncludeDirectories);..\..\inc + %(PreprocessorDefinitions);___TARGETNAME="""SingleComponentFStateDriver.DRIVER""" @@ -149,20 +158,22 @@ true Level4 %(AdditionalIncludeDirectories);..\..\inc + %(PreprocessorDefinitions);___TARGETNAME="""SingleComponentFStateDriver.DRIVER""" %(AdditionalIncludeDirectories);..\..\inc + %(PreprocessorDefinitions);___TARGETNAME="""SingleComponentFStateDriver.DRIVER""" %(AdditionalIncludeDirectories);..\..\inc + %(PreprocessorDefinitions);___TARGETNAME="""SingleComponentFStateDriver.DRIVER""" - diff --git a/pofx/WDF/Driver/SingleComp/SingleComponentFStateDriver.vcxproj.Filters b/pofx/WDF/Driver/SingleComp/SingleComponentFStateDriver.vcxproj.Filters index 01241dd79..27f70a04b 100644 --- a/pofx/WDF/Driver/SingleComp/SingleComponentFStateDriver.vcxproj.Filters +++ b/pofx/WDF/Driver/SingleComp/SingleComponentFStateDriver.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {06F67341-2370-4DB8-B281-2E622D2C8146} + {713090E4-DAA7-4D43-A866-96D227F3D9CD} h;hpp;hxx;hm;inl;inc;xsd - {81CB8FD0-9854-41A8-8288-C99CBEED8E9A} + {1C1A15C9-A910-4A09-A5B0-3DFD481936F2} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {00576151-6245-4316-B0D6-A9C1DEAD6E26} + {EF791C28-4DA1-42F3-81E8-9A2ACA703A98} inf;inv;inx;mof;mc; - {919E6D2D-111D-421B-9634-371A59B22921} + {8313F96A-1A6F-419B-8600-ED4496199E9E} @@ -27,9 +27,6 @@ - - Driver Files - Driver Files diff --git a/pofx/WDF/Driver/SingleComp/SingleComponentFStateSample.inx b/pofx/WDF/Driver/SingleComp/SingleComponentFStateSample.inx index 1c555f110..bf0522d73 100644 --- a/pofx/WDF/Driver/SingleComp/SingleComponentFStateSample.inx +++ b/pofx/WDF/Driver/SingleComp/SingleComponentFStateSample.inx @@ -17,7 +17,7 @@ Signature="$WINDOWS NT$" Class=Sample ClassGuid={78A1C341-4539-11d3-B88D-00C04FAD5171} -Provider=%MSFT% +Provider=%ProviderString% CatalogFile=KmdfSamples.cat [DestinationDirs] @@ -97,7 +97,7 @@ KmdfLibraryVersion = $KMDFVERSION$ [Strings] SPSVCINST_ASSOCSERVICE= 0x00000002 -MSFT = "Microsoft" +ProviderString = "TODO-Set-Provider" StdMfg = "(Standard system devices)" DiskId1 = "WDF Sample SingleComponentFStateDevice Installation Disk #1" SingleComponentFStateDevice.DeviceDesc = "Single comp F State Device" diff --git a/pofx/WDF/ReadMe.md b/pofx/WDF/ReadMe.md index 2d59df4c1..f351a587b 100644 --- a/pofx/WDF/ReadMe.md +++ b/pofx/WDF/ReadMe.md @@ -6,9 +6,6 @@ This solution consists of two samples that demonstrate how a KMDF driver can imp ## Universal Windows Driver Compliant This sample builds a Universal Windows Driver. It uses only APIs and DDIs that are included in OneCoreUAP. -**Note** -Due to a known bug in the current Windows Driver Kit tools, when building this sample in Debug mode, the exe project may report some ApiValidator warnings. These can be safely ignored. - Related technologies -------------------- [Supporting Functional Power States](http://msdn.microsoft.com/en-us/library/windows/hardware/hh451017) diff --git a/pofx/WDF/pofx.sln b/pofx/WDF/pofx.sln index 64ddbc7c3..1293a347a 100644 --- a/pofx/WDF/pofx.sln +++ b/pofx/WDF/pofx.sln @@ -3,28 +3,28 @@ 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", "{3EAA5179-3DB5-4480-98C6-5B5EA1965C68}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "App", "App", "{90FC8FAB-4FB4-445E-B20D-A18E37DE0E53}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Lib", "Lib", "{262CC7E2-0008-43E9-90AA-8031F479A615}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Lib", "Lib", "{86E47CFB-2B89-4229-B5DC-DD4291CC7A77}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "MultiComp", "MultiComp", "{A8DB64B7-4C39-4D20-85C2-1181FA6BBD36}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "MultiComp", "MultiComp", "{B54A12F3-7473-4302-BA54-DCCF31733B6E}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Driver", "Driver", "{6F6084BB-35B3-48A8-BAAC-D67835D818A2}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Driver", "Driver", "{B99E540E-91B3-499C-AF59-4465A03A69B0}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Driver", "Driver", "{24335DF0-0022-4BAA-8DDF-02DB6E1D6BC4}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Driver", "Driver", "{307FA296-9F9D-4D17-ADD5-728463581ADC}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SingleComp", "SingleComp", "{3D40DC7D-ECA2-4581-A5DE-676F30DF32C0}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SingleComp", "SingleComp", "{370534AF-5018-4005-928F-EF246DF92567}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PowerFxApp", "App\PowerFxApp.vcxproj", "{1A459D79-27B4-4E77-A42D-CC2F498A5822}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PowerFxApp", "App\PowerFxApp.vcxproj", "{8F8494D9-52B9-44B8-80C4-0D0053716870}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WdfPoFx", "Driver\MultiComp\lib\WdfPoFx.vcxproj", "{30087DFC-5BF1-4BF6-A3C2-EB767B04A346}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WdfPoFx", "Driver\MultiComp\lib\WdfPoFx.vcxproj", "{6F5CB5B9-470D-49F1-8C84-3BBD828E7F34}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WdfMultiComp", "Driver\MultiComp\driver\WdfMultiComp.vcxproj", "{E47411F4-AAEA-4766-B62B-A5171ED47CCF}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WdfMultiComp", "Driver\MultiComp\driver\WdfMultiComp.vcxproj", "{188D1CB3-96AB-43B5-8BCA-70D6115DC2EB}" ProjectSection(ProjectDependencies) = postProject - {30087DFC-5BF1-4BF6-A3C2-EB767B04A346} = {30087DFC-5BF1-4BF6-A3C2-EB767B04A346} + {6F5CB5B9-470D-49F1-8C84-3BBD828E7F34} = {6F5CB5B9-470D-49F1-8C84-3BBD828E7F34} EndProjectSection EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SingleComponentFStateDriver", "Driver\SingleComp\SingleComponentFStateDriver.vcxproj", "{288FFC86-69F2-41A0-A08B-CD42CFF51401}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SingleComponentFStateDriver", "Driver\SingleComp\SingleComponentFStateDriver.vcxproj", "{BFB8BE60-7C1E-416D-B561-5700DCC94082}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -34,50 +34,50 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {1A459D79-27B4-4E77-A42D-CC2F498A5822}.Debug|Win32.ActiveCfg = Debug|Win32 - {1A459D79-27B4-4E77-A42D-CC2F498A5822}.Debug|Win32.Build.0 = Debug|Win32 - {1A459D79-27B4-4E77-A42D-CC2F498A5822}.Release|Win32.ActiveCfg = Release|Win32 - {1A459D79-27B4-4E77-A42D-CC2F498A5822}.Release|Win32.Build.0 = Release|Win32 - {1A459D79-27B4-4E77-A42D-CC2F498A5822}.Debug|x64.ActiveCfg = Debug|x64 - {1A459D79-27B4-4E77-A42D-CC2F498A5822}.Debug|x64.Build.0 = Debug|x64 - {1A459D79-27B4-4E77-A42D-CC2F498A5822}.Release|x64.ActiveCfg = Release|x64 - {1A459D79-27B4-4E77-A42D-CC2F498A5822}.Release|x64.Build.0 = Release|x64 - {30087DFC-5BF1-4BF6-A3C2-EB767B04A346}.Debug|Win32.ActiveCfg = Debug|Win32 - {30087DFC-5BF1-4BF6-A3C2-EB767B04A346}.Debug|Win32.Build.0 = Debug|Win32 - {30087DFC-5BF1-4BF6-A3C2-EB767B04A346}.Release|Win32.ActiveCfg = Release|Win32 - {30087DFC-5BF1-4BF6-A3C2-EB767B04A346}.Release|Win32.Build.0 = Release|Win32 - {30087DFC-5BF1-4BF6-A3C2-EB767B04A346}.Debug|x64.ActiveCfg = Debug|x64 - {30087DFC-5BF1-4BF6-A3C2-EB767B04A346}.Debug|x64.Build.0 = Debug|x64 - {30087DFC-5BF1-4BF6-A3C2-EB767B04A346}.Release|x64.ActiveCfg = Release|x64 - {30087DFC-5BF1-4BF6-A3C2-EB767B04A346}.Release|x64.Build.0 = Release|x64 - {E47411F4-AAEA-4766-B62B-A5171ED47CCF}.Debug|Win32.ActiveCfg = Debug|Win32 - {E47411F4-AAEA-4766-B62B-A5171ED47CCF}.Debug|Win32.Build.0 = Debug|Win32 - {E47411F4-AAEA-4766-B62B-A5171ED47CCF}.Release|Win32.ActiveCfg = Release|Win32 - {E47411F4-AAEA-4766-B62B-A5171ED47CCF}.Release|Win32.Build.0 = Release|Win32 - {E47411F4-AAEA-4766-B62B-A5171ED47CCF}.Debug|x64.ActiveCfg = Debug|x64 - {E47411F4-AAEA-4766-B62B-A5171ED47CCF}.Debug|x64.Build.0 = Debug|x64 - {E47411F4-AAEA-4766-B62B-A5171ED47CCF}.Release|x64.ActiveCfg = Release|x64 - {E47411F4-AAEA-4766-B62B-A5171ED47CCF}.Release|x64.Build.0 = Release|x64 - {288FFC86-69F2-41A0-A08B-CD42CFF51401}.Debug|Win32.ActiveCfg = Debug|Win32 - {288FFC86-69F2-41A0-A08B-CD42CFF51401}.Debug|Win32.Build.0 = Debug|Win32 - {288FFC86-69F2-41A0-A08B-CD42CFF51401}.Release|Win32.ActiveCfg = Release|Win32 - {288FFC86-69F2-41A0-A08B-CD42CFF51401}.Release|Win32.Build.0 = Release|Win32 - {288FFC86-69F2-41A0-A08B-CD42CFF51401}.Debug|x64.ActiveCfg = Debug|x64 - {288FFC86-69F2-41A0-A08B-CD42CFF51401}.Debug|x64.Build.0 = Debug|x64 - {288FFC86-69F2-41A0-A08B-CD42CFF51401}.Release|x64.ActiveCfg = Release|x64 - {288FFC86-69F2-41A0-A08B-CD42CFF51401}.Release|x64.Build.0 = Release|x64 + {8F8494D9-52B9-44B8-80C4-0D0053716870}.Debug|Win32.ActiveCfg = Debug|Win32 + {8F8494D9-52B9-44B8-80C4-0D0053716870}.Debug|Win32.Build.0 = Debug|Win32 + {8F8494D9-52B9-44B8-80C4-0D0053716870}.Release|Win32.ActiveCfg = Release|Win32 + {8F8494D9-52B9-44B8-80C4-0D0053716870}.Release|Win32.Build.0 = Release|Win32 + {8F8494D9-52B9-44B8-80C4-0D0053716870}.Debug|x64.ActiveCfg = Debug|x64 + {8F8494D9-52B9-44B8-80C4-0D0053716870}.Debug|x64.Build.0 = Debug|x64 + {8F8494D9-52B9-44B8-80C4-0D0053716870}.Release|x64.ActiveCfg = Release|x64 + {8F8494D9-52B9-44B8-80C4-0D0053716870}.Release|x64.Build.0 = Release|x64 + {6F5CB5B9-470D-49F1-8C84-3BBD828E7F34}.Debug|Win32.ActiveCfg = Debug|Win32 + {6F5CB5B9-470D-49F1-8C84-3BBD828E7F34}.Debug|Win32.Build.0 = Debug|Win32 + {6F5CB5B9-470D-49F1-8C84-3BBD828E7F34}.Release|Win32.ActiveCfg = Release|Win32 + {6F5CB5B9-470D-49F1-8C84-3BBD828E7F34}.Release|Win32.Build.0 = Release|Win32 + {6F5CB5B9-470D-49F1-8C84-3BBD828E7F34}.Debug|x64.ActiveCfg = Debug|x64 + {6F5CB5B9-470D-49F1-8C84-3BBD828E7F34}.Debug|x64.Build.0 = Debug|x64 + {6F5CB5B9-470D-49F1-8C84-3BBD828E7F34}.Release|x64.ActiveCfg = Release|x64 + {6F5CB5B9-470D-49F1-8C84-3BBD828E7F34}.Release|x64.Build.0 = Release|x64 + {188D1CB3-96AB-43B5-8BCA-70D6115DC2EB}.Debug|Win32.ActiveCfg = Debug|Win32 + {188D1CB3-96AB-43B5-8BCA-70D6115DC2EB}.Debug|Win32.Build.0 = Debug|Win32 + {188D1CB3-96AB-43B5-8BCA-70D6115DC2EB}.Release|Win32.ActiveCfg = Release|Win32 + {188D1CB3-96AB-43B5-8BCA-70D6115DC2EB}.Release|Win32.Build.0 = Release|Win32 + {188D1CB3-96AB-43B5-8BCA-70D6115DC2EB}.Debug|x64.ActiveCfg = Debug|x64 + {188D1CB3-96AB-43B5-8BCA-70D6115DC2EB}.Debug|x64.Build.0 = Debug|x64 + {188D1CB3-96AB-43B5-8BCA-70D6115DC2EB}.Release|x64.ActiveCfg = Release|x64 + {188D1CB3-96AB-43B5-8BCA-70D6115DC2EB}.Release|x64.Build.0 = Release|x64 + {BFB8BE60-7C1E-416D-B561-5700DCC94082}.Debug|Win32.ActiveCfg = Debug|Win32 + {BFB8BE60-7C1E-416D-B561-5700DCC94082}.Debug|Win32.Build.0 = Debug|Win32 + {BFB8BE60-7C1E-416D-B561-5700DCC94082}.Release|Win32.ActiveCfg = Release|Win32 + {BFB8BE60-7C1E-416D-B561-5700DCC94082}.Release|Win32.Build.0 = Release|Win32 + {BFB8BE60-7C1E-416D-B561-5700DCC94082}.Debug|x64.ActiveCfg = Debug|x64 + {BFB8BE60-7C1E-416D-B561-5700DCC94082}.Debug|x64.Build.0 = Debug|x64 + {BFB8BE60-7C1E-416D-B561-5700DCC94082}.Release|x64.ActiveCfg = Release|x64 + {BFB8BE60-7C1E-416D-B561-5700DCC94082}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {1A459D79-27B4-4E77-A42D-CC2F498A5822} = {3EAA5179-3DB5-4480-98C6-5B5EA1965C68} - {30087DFC-5BF1-4BF6-A3C2-EB767B04A346} = {262CC7E2-0008-43E9-90AA-8031F479A615} - {E47411F4-AAEA-4766-B62B-A5171ED47CCF} = {24335DF0-0022-4BAA-8DDF-02DB6E1D6BC4} - {288FFC86-69F2-41A0-A08B-CD42CFF51401} = {3D40DC7D-ECA2-4581-A5DE-676F30DF32C0} - {262CC7E2-0008-43E9-90AA-8031F479A615} = {A8DB64B7-4C39-4D20-85C2-1181FA6BBD36} - {A8DB64B7-4C39-4D20-85C2-1181FA6BBD36} = {6F6084BB-35B3-48A8-BAAC-D67835D818A2} - {24335DF0-0022-4BAA-8DDF-02DB6E1D6BC4} = {A8DB64B7-4C39-4D20-85C2-1181FA6BBD36} - {3D40DC7D-ECA2-4581-A5DE-676F30DF32C0} = {6F6084BB-35B3-48A8-BAAC-D67835D818A2} + {8F8494D9-52B9-44B8-80C4-0D0053716870} = {90FC8FAB-4FB4-445E-B20D-A18E37DE0E53} + {6F5CB5B9-470D-49F1-8C84-3BBD828E7F34} = {86E47CFB-2B89-4229-B5DC-DD4291CC7A77} + {188D1CB3-96AB-43B5-8BCA-70D6115DC2EB} = {307FA296-9F9D-4D17-ADD5-728463581ADC} + {BFB8BE60-7C1E-416D-B561-5700DCC94082} = {370534AF-5018-4005-928F-EF246DF92567} + {86E47CFB-2B89-4229-B5DC-DD4291CC7A77} = {B54A12F3-7473-4302-BA54-DCCF31733B6E} + {B54A12F3-7473-4302-BA54-DCCF31733B6E} = {B99E540E-91B3-499C-AF59-4465A03A69B0} + {307FA296-9F9D-4D17-ADD5-728463581ADC} = {B54A12F3-7473-4302-BA54-DCCF31733B6E} + {370534AF-5018-4005-928F-EF246DF92567} = {B99E540E-91B3-499C-AF59-4465A03A69B0} EndGlobalSection EndGlobal diff --git a/pos/drivers/MagneticStripeReader/Device.cpp b/pos/drivers/MagneticStripeReader/Device.cpp new file mode 100644 index 000000000..85932fb26 --- /dev/null +++ b/pos/drivers/MagneticStripeReader/Device.cpp @@ -0,0 +1,101 @@ +#include + +#include "File.h" +#include "PosEvents.h" +#include "Ioctl.h" +#include "IoRead.h" + +/* +** Driver TODO: Complete the implementation of EvtDriverDeviceAdd for your specific device. +** +** WDF calls this callback when a device instance is added to the driver. Good drivers will do a lot of +** work here to set up everything necessary, such as adding callbacks for PNP power state changes. +** This function defines an IO queue for handling DeviceIoControl and file read requests, both of which are +** important to the POS magnetic stripe reader model. +** +** Note that this is not a complete device add implementation, as the PNP power callbacks are not handled. +** Additionally, driver writers may wish to set up additional queues to serialize device property requests +** (see Ioctl.cpp for more info). +*/ +NTSTATUS EvtDriverDeviceAdd(_In_ WDFDRIVER /* UnusedDriver */, _Inout_ PWDFDEVICE_INIT DeviceInit) +{ + NTSTATUS status = STATUS_SUCCESS; + WDF_FILEOBJECT_CONFIG fileConfig; + WDF_OBJECT_ATTRIBUTES deviceAttributes; + WDF_OBJECT_ATTRIBUTES fileAttributes; + WDFDEVICE device; + + // Handle file events + WDF_FILEOBJECT_CONFIG_INIT( + &fileConfig, + EvtDeviceFileCreate, + EvtFileClose, + WDF_NO_EVENT_CALLBACK + ); + + WDF_OBJECT_ATTRIBUTES_INIT(&fileAttributes); + WdfDeviceInitSetFileObjectConfig( + DeviceInit, + &fileConfig, + &fileAttributes + ); + + // Create Device + WDF_OBJECT_ATTRIBUTES_INIT(&deviceAttributes); + status = WdfDeviceCreate( + &DeviceInit, + &deviceAttributes, + &device + ); + + if (!NT_SUCCESS(status)) + { + return status; + } + + // Create a device interface for POS Magnetic Stripe Reader so that the device can be enumerated + status = WdfDeviceCreateDeviceInterface( + device, + &GUID_DEVINTERFACE_POS_MSR, + NULL + ); + + if (!NT_SUCCESS(status)) + { + return status; + } + + // Initialize the POS library + POS_CX_ATTRIBUTES posCxAttributes; + POS_CX_ATTRIBUTES_INIT(&posCxAttributes); + posCxAttributes.EvtDeviceOwnershipChange = EvtDeviceOwnershipChange; + + status = PosCxInit(device, &posCxAttributes); + + if (!NT_SUCCESS(status)) + { + return status; + } + + // Set up an IO queue to handle DeviceIoControl and ReadFile + WDF_IO_QUEUE_CONFIG queueConfig; + WDF_OBJECT_ATTRIBUTES attributes; + WDFQUEUE queue; + + WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&queueConfig, WdfIoQueueDispatchSequential); + queueConfig.EvtIoDeviceControl = EvtIoDeviceControl; + queueConfig.EvtIoRead = EvtIoRead; + + // Call us in PASSIVE_LEVEL + WDF_OBJECT_ATTRIBUTES_INIT(&attributes); + attributes.ExecutionLevel = WdfExecutionLevelPassive; + + status = WdfIoQueueCreate( + device, + &queueConfig, + &attributes, + &queue + ); + + return status; +} diff --git a/pos/drivers/MagneticStripeReader/Device.h b/pos/drivers/MagneticStripeReader/Device.h new file mode 100644 index 000000000..df9ad4091 --- /dev/null +++ b/pos/drivers/MagneticStripeReader/Device.h @@ -0,0 +1,3 @@ +#pragma once + +EVT_WDF_DRIVER_DEVICE_ADD EvtDriverDeviceAdd; diff --git a/pos/drivers/MagneticStripeReader/Driver.cpp b/pos/drivers/MagneticStripeReader/Driver.cpp new file mode 100644 index 000000000..5bcf2f336 --- /dev/null +++ b/pos/drivers/MagneticStripeReader/Driver.cpp @@ -0,0 +1,54 @@ +#include + +#include "Device.h" + +// Forward declaration +VOID EvtDriverCleanup(_In_ WDFOBJECT DriverObject); + +/* +** Driver TODO: +** +** This is the main entry point of the driver. POS APIs require that the driver sets up additional data in device add. +** +** Note that your driver may have additional configuration to do in this function, and it should not be assumed that this sample is complete. +*/ +NTSTATUS DriverEntry( + PDRIVER_OBJECT DriverObject, + PUNICODE_STRING RegistryPath + ) +{ + WDF_DRIVER_CONFIG config; + NTSTATUS status = STATUS_SUCCESS; + WDF_OBJECT_ATTRIBUTES attributes; + + WDF_OBJECT_ATTRIBUTES_INIT(&attributes); + attributes.EvtCleanupCallback = EvtDriverCleanup; + + WDF_DRIVER_CONFIG_INIT( + &config, + EvtDriverDeviceAdd + ); + + status = WdfDriverCreate( + DriverObject, + RegistryPath, + &attributes, + &config, + WDF_NO_HANDLE + ); + + return status; +} + +/* +** Driver TODO: +** +** This is the cleanup callback for the driver (as set above in DriverEntry). +** PosCx requires no cleanup at this point. +*/ +_Use_decl_annotations_ +VOID EvtDriverCleanup(WDFOBJECT /* UnusedDriverObject */) +{ + // Do any cleanup needed here + return; +} diff --git a/pos/drivers/MagneticStripeReader/File.cpp b/pos/drivers/MagneticStripeReader/File.cpp new file mode 100644 index 000000000..d9cf815de --- /dev/null +++ b/pos/drivers/MagneticStripeReader/File.cpp @@ -0,0 +1,37 @@ +#include + +/* +** Driver TODO: +** +** WDF calls this callback when a file handle is opened to the driver. Your implementation may require additional setup (such as creating a +** file-handle-based context structure). PosCxOpen must be called during this callback. +*/ +VOID EvtDeviceFileCreate(_In_ WDFDEVICE Device, _In_ WDFREQUEST Request, _In_ WDFFILEOBJECT FileObject) +{ + NTSTATUS status = PosCxOpen(Device, FileObject, MSR_INTERFACE_TAG); + + if (!NT_SUCCESS(status)) + { + // This should only fail in rare cases, but the failure will prevent all PosCx functions from performing correctly + } + + WdfRequestComplete(Request, status); +} + +/* +** Driver TODO: +** +** WDF calls this callback when a file handle to the driver is closed. Your implementation may require additional cleanup, but +** PosCxClose must be called during this callback. +*/ +VOID EvtFileClose(_In_ WDFFILEOBJECT FileObject) +{ + WDFDEVICE device = WdfFileObjectGetDevice(FileObject); + + NTSTATUS status = PosCxClose(device, FileObject); + + if (!NT_SUCCESS(status)) + { + // This will only fail if PosCxInit wasn't called successfully in EvtDriverDeviceAdd, or if PosCxOpen failed in EvtDeviceFileCreate + } +} \ No newline at end of file diff --git a/pos/drivers/MagneticStripeReader/File.h b/pos/drivers/MagneticStripeReader/File.h new file mode 100644 index 000000000..73f815e0d --- /dev/null +++ b/pos/drivers/MagneticStripeReader/File.h @@ -0,0 +1,4 @@ +#pragma once + +EVT_WDF_DEVICE_FILE_CREATE EvtDeviceFileCreate; +EVT_WDF_FILE_CLOSE EvtFileClose; diff --git a/pos/drivers/MagneticStripeReader/IoRead.cpp b/pos/drivers/MagneticStripeReader/IoRead.cpp new file mode 100644 index 000000000..c19ee7432 --- /dev/null +++ b/pos/drivers/MagneticStripeReader/IoRead.cpp @@ -0,0 +1,41 @@ +#include + +/* +** Driver TODO: Add logic to EvtIoRead to handle read requests from applications that don't use the Windows.Devices.PointOfService APIs. +** +** This is the callback for the IO queue that handles file read requests. In the POS magnetic +** stripe reader model, the application will always queue a read request in order to receive events +** such as the data received event, or the release-claim requested event. +** +** Note that apps that are developed against the Windows.Devices.PointOfService APIs will always +** expect event data to be returned by read requests. It is up to the driver to determine the +** behavior of ReadFile when the driver is opened by other types of applications. +*/ +VOID EvtIoRead(_In_ WDFQUEUE Queue, _In_ WDFREQUEST Request, _In_ size_t Length) +{ + NTSTATUS status; + WDFDEVICE device = WdfIoQueueGetDevice(Queue); + WDFFILEOBJECT fileObject = WdfRequestGetFileObject(Request); + + UNREFERENCED_PARAMETER(Length); + + // Check the flag that may have been set by PosCxMarkPosApp in Ioctl.cpp. + if (!PosCxIsPosApp(device, fileObject)) + { + // An application has opened a handle to this device without using the Windows.Devices.PointOfService APIs. + // You may change this to handle the read request differently. + + // In this example, just complete the read request with a failure. + WdfRequestComplete(Request, STATUS_UNSUCCESSFUL); + } + else + { + // If this returns success, it has taken ownership of Request + status = PosCxGetPendingEvent(device, Request); + + if (!NT_SUCCESS(status)) + { + WdfRequestComplete(Request, status); + } + } +} \ No newline at end of file diff --git a/pos/drivers/MagneticStripeReader/IoRead.h b/pos/drivers/MagneticStripeReader/IoRead.h new file mode 100644 index 000000000..20f7dcdd5 --- /dev/null +++ b/pos/drivers/MagneticStripeReader/IoRead.h @@ -0,0 +1,3 @@ +#pragma once + +EVT_WDF_IO_QUEUE_IO_READ EvtIoRead; \ No newline at end of file diff --git a/pos/drivers/MagneticStripeReader/Ioctl.cpp b/pos/drivers/MagneticStripeReader/Ioctl.cpp new file mode 100644 index 000000000..dd8ffb623 --- /dev/null +++ b/pos/drivers/MagneticStripeReader/Ioctl.cpp @@ -0,0 +1,878 @@ +#include + +NTSTATUS ProcessRetrieveDeviceAuthentication(_In_ WDFDEVICE Device, _In_ WDFFILEOBJECT FileObject, _In_ WDFREQUEST Request, _Inout_ ULONG_PTR* Information); +NTSTATUS ProcessAuthenticateDevice(_In_ WDFDEVICE Device, _In_ WDFFILEOBJECT FileObject, _In_ WDFREQUEST Request); +NTSTATUS ProcessDeauthenticateDevice(_In_ WDFDEVICE Device, _In_ WDFFILEOBJECT FileObject, _In_ WDFREQUEST Request); +NTSTATUS ProcessUpdateKey(_In_ WDFDEVICE Device, _In_ WDFFILEOBJECT FileObject, _In_ WDFREQUEST Request); +NTSTATUS ProcessGetPropertyRequest(_In_ WDFREQUEST Request, _In_ size_t InputBufferLength, _Inout_ ULONG_PTR* Information); +NTSTATUS ProcessSetPropertyRequest(_In_ WDFREQUEST Request, _In_ size_t InputBufferLength, _Inout_ ULONG_PTR* Information); +NTSTATUS ProcessRetrieveStatisticsRequest(_In_ WDFREQUEST Request, _In_ size_t OutputBufferLength, _Inout_ ULONG_PTR* Information); +NTSTATUS ProcessResetStatisticsRequest(_In_ WDFREQUEST Request); +NTSTATUS ProcessUpdateStatisticsRequest(_In_ WDFREQUEST Request); +NTSTATUS ProcessCheckHealthRequest(_In_ WDFREQUEST Request, _Inout_ ULONG_PTR* Information); +NTSTATUS ProcessGetDeviceBasicsRequest(_In_ WDFREQUEST Request, _Inout_ ULONG_PTR* Information); + +/* +** Driver TODO: Complete the implementation of EvtIoDeviceControl for your specific device (if necessary) +** +** WDF calls this callback when a device instance is added to the driver. Good drivers will do a lot of +** work here to set up everything necessary, such as adding callbacks for PNP power state changes. +** This function defines an IO queue for handling DeviceIoControl and file read requests, both of which are +** important to the POS magnetic stripe reader model. +** +** Note that this is not a complete device add implementation, as the PNP power callbacks are not handled. +** Additionally, driver writers may wish to set up additional queues to serialize device property requests +** (see Ioctl.cpp for more info). +*/ +VOID EvtIoDeviceControl(_In_ WDFQUEUE Queue, _In_ WDFREQUEST Request, _In_ size_t OutputBufferLength, _In_ size_t InputBufferLength, _In_ ULONG IoControlCode) +{ + UNREFERENCED_PARAMETER(Queue); + + NTSTATUS status = STATUS_SUCCESS; + ULONG_PTR information = 0; + WDFDEVICE device = WdfIoQueueGetDevice(Queue); + WDFFILEOBJECT fileObject = WdfRequestGetFileObject(Request); + + // These are the set of IOCTLs that your device should handle to work with the Windows.Devices.PointOfService APIs. + switch (IoControlCode) + { + // The first three IOCTLs shouldn't require additional processing other than handing them off to PosCx + case IOCTL_POINT_OF_SERVICE_CLAIM_DEVICE: + status = PosCxClaimDevice(device, Request); + break; + + case IOCTL_POINT_OF_SERVICE_RELEASE_DEVICE: + status = PosCxReleaseDevice(device, fileObject); + break; + + case IOCTL_POINT_OF_SERVICE_RETAIN_DEVICE: + status = PosCxRetainDevice(device, Request); + break; + + + case IOCTL_POINT_OF_SERVICE_MSR_RETRIEVE_DEVICE_AUTHENTICATION: + status = ProcessRetrieveDeviceAuthentication(device, fileObject, Request, &information); + break; + + case IOCTL_POINT_OF_SERVICE_MSR_AUTHENTICATE_DEVICE: + status = ProcessAuthenticateDevice(device, fileObject, Request); + break; + + case IOCTL_POINT_OF_SERVICE_MSR_DEAUTHENTICATE_DEVICE: + status = ProcessDeauthenticateDevice(device, fileObject, Request); + break; + + case IOCTL_POINT_OF_SERVICE_MSR_UPDATE_KEY: + status = ProcessUpdateKey(device, fileObject, Request); + break; + + case IOCTL_POINT_OF_SERVICE_GET_PROPERTY: + status = ProcessGetPropertyRequest(Request, InputBufferLength, &information); + break; + + case IOCTL_POINT_OF_SERVICE_SET_PROPERTY: + status = ProcessSetPropertyRequest(Request, InputBufferLength, &information); + break; + + case IOCTL_POINT_OF_SERVICE_RETRIEVE_STATISTICS: + status = ProcessRetrieveStatisticsRequest(Request, OutputBufferLength, &information); + break; + + case IOCTL_POINT_OF_SERVICE_RESET_STATISTICS: + status = ProcessResetStatisticsRequest(Request); + break; + + case IOCTL_POINT_OF_SERVICE_UPDATE_STATISTICS: + status = ProcessUpdateStatisticsRequest(Request); + break; + + case IOCTL_POINT_OF_SERVICE_CHECK_HEALTH: + status = ProcessCheckHealthRequest(Request, &information); + break; + + // The Get Device Basics IOCTL is always the first IOCTL called by an application using the Windows.Devices.PointOfService APIs. + // Use it to determine when to call PosCxMarkPosApp (see notes about apps marked this way in IoRead.cpp) + case IOCTL_POINT_OF_SERVICE_GET_DEVICE_BASICS: + status = ProcessGetDeviceBasicsRequest(Request, &information); + (void)PosCxMarkPosApp(device, fileObject, TRUE); + break; + + default: + // Your device may support additional IOCTLs. In this sample, we return failure for anything else. + status = STATUS_NOT_SUPPORTED; + break; + } + + if (status != STATUS_PENDING) + { + WdfRequestCompleteWithInformation(Request, status, information); + } +} + +/* +** Driver TODO: Add code to handle various get-property cases. +** +** Implement this function to handle requests for the device authentication information. +** This should be step 1 in authenticating or deauthenticating the device. +*/ +NTSTATUS ProcessRetrieveDeviceAuthentication(_In_ WDFDEVICE Device, _In_ WDFFILEOBJECT FileObject, _In_ WDFREQUEST Request, _Inout_ ULONG_PTR* Information) +{ + // If the caller is not the device owner, fail the request + if (!PosCxIsDeviceOwner(Device, FileObject)) + { + return STATUS_ACCESS_DENIED; + } + + *Information = (ULONG_PTR)sizeof(MSR_RETRIEVE_DEVICE_AUTHENTICATION_DATA); + + PMSR_RETRIEVE_DEVICE_AUTHENTICATION_DATA reqBufferPtr; + size_t reqBufferSize; + NTSTATUS status = WdfRequestRetrieveOutputBuffer( + Request, + sizeof(MSR_RETRIEVE_DEVICE_AUTHENTICATION_DATA), + reinterpret_cast(&reqBufferPtr), + &reqBufferSize + ); + + if (!NT_SUCCESS(status)) + { + return status; + } + + // TODO: Fill in the reqBufferPtr with the authentication information for your device. + + return status; +} + +/* +** Driver TODO: Add code to handle various get-property cases. +** +** Implement this function to handle requests to authenticate your device. +** This is step 2 in authenticating the device. +*/ +NTSTATUS ProcessAuthenticateDevice(_In_ WDFDEVICE Device, _In_ WDFFILEOBJECT FileObject, _In_ WDFREQUEST Request) +{ + // If the caller is not the device owner, fail the request + if (!PosCxIsDeviceOwner(Device, FileObject)) + { + return STATUS_ACCESS_DENIED; + } + + PMSR_AUTHENTICATE_DEVICE reqBufferPtr; + size_t reqBufferSize; + NTSTATUS status = WdfRequestRetrieveInputBuffer( + Request, + sizeof(MSR_AUTHENTICATE_DEVICE), + reinterpret_cast(&reqBufferPtr), + &reqBufferSize + ); + + if (!NT_SUCCESS(status)) + { + return status; + } + + // TODO: Send the authentication information to the device + + return status; +} + +/* +** Driver TODO: Add code to handle various get-property cases. +** +** Implement this function to handle requests to deauthenticate your device. +** This is step 2 in deauthenticating the device. +*/ +NTSTATUS ProcessDeauthenticateDevice(_In_ WDFDEVICE Device, _In_ WDFFILEOBJECT FileObject, _In_ WDFREQUEST Request) +{ + // If the caller is not the device owner, fail the request + if (!PosCxIsDeviceOwner(Device, FileObject)) + { + return STATUS_ACCESS_DENIED; + } + + PMSR_DEAUTHENTICATE_DEVICE reqBufferPtr; + size_t reqBufferSize; + NTSTATUS status = WdfRequestRetrieveInputBuffer( + Request, + sizeof(MSR_DEAUTHENTICATE_DEVICE), + reinterpret_cast(&reqBufferPtr), + &reqBufferSize + ); + + if (!NT_SUCCESS(status)) + { + return status; + } + + // TODO: Send the deauthentication information to the device + + return status; +} + +/* +** Driver TODO: Add code to handle various get-property cases. +** +** Implement this function to handle requests to update the key in your device. +*/ +NTSTATUS ProcessUpdateKey(_In_ WDFDEVICE Device, _In_ WDFFILEOBJECT FileObject, _In_ WDFREQUEST Request) +{ + // If the caller is not the device owner, fail the request + if (!PosCxIsDeviceOwner(Device, FileObject)) + { + return STATUS_ACCESS_DENIED; + } + + PMSR_UPDATE_KEY reqBufferPtr; + size_t reqBufferSize; + NTSTATUS status = WdfRequestRetrieveInputBuffer( + Request, + sizeof(MSR_UPDATE_KEY), + reinterpret_cast(&reqBufferPtr), + &reqBufferSize + ); + + if (!NT_SUCCESS(status)) + { + return status; + } + + // TODO: Send the updated key information to the device + + return status; +} + +/* +** Driver TODO: Add code to handle various get-property cases. +** +** Implement this function to handle property get requests. +*/ +NTSTATUS ProcessGetPropertyRequest(_In_ WDFREQUEST Request, _In_ size_t InputBufferLength, _Inout_ ULONG_PTR* Information) +{ + if (Information == nullptr || Request == nullptr || InputBufferLength < sizeof(PosPropertyId)) + { + return STATUS_INVALID_PARAMETER; + } + + // POS properties are identified by the property ID that's transmitted in the input buffer. + PosPropertyId* propertyId; + NTSTATUS status = WdfRequestRetrieveInputBuffer( + Request, + sizeof(PosPropertyId), + reinterpret_cast(&propertyId), + nullptr + ); + + if (!NT_SUCCESS(status)) + { + return status; + } + + // All get properties will need access to the output buffer in order to return results. + // The minimum size returned is a UINT32 + void* outputBuffer; + size_t outputBufferLength; + status = WdfRequestRetrieveOutputBuffer( + Request, + sizeof(UINT32), + &outputBuffer, + &outputBufferLength + ); + + if (!NT_SUCCESS(status)) + { + return status; + } + + // Handle this set of readable properties + switch (*propertyId) + { + case PosPropertyId::IsEnabled: + // BOOL result, true when the app has called SetProperty(IsEnabled) = TRUE + { + BOOL isEnabled = TRUE; // Get this value from device context or by querying the device + *((BOOL*)outputBuffer) = isEnabled; + *Information = sizeof(BOOL); + } + break; + + case PosPropertyId::IsDisabledOnDataReceived: + // BOOL result, true when the app has called SetProperty(IsDisabledOnDataReceived) = TRUE + { + BOOL isDisabledOnDataReceived = TRUE; // Get this value from device context or by querying the device + *((BOOL*)outputBuffer) = isDisabledOnDataReceived; + *Information = sizeof(BOOL); + } + break; + + case PosPropertyId::MagneticStripeReaderIsDecodeDataEnabled: + // BOOL result, true when the app has called SetProperty(MagneticStripeReaderIsDecodeDataEnabled) = TRUE + { + BOOL isDecodeDataEnabled = TRUE; // Get this value from device context or by querying the device + *((BOOL*)outputBuffer) = isDecodeDataEnabled; + *Information = sizeof(BOOL); + } + break; + + case PosPropertyId::MagneticStripeReaderCapabilities: + { + // PosMagneticStripeReaderCapabilitiesType result + // These capabilities are likely hard-coded for the specific device. In this case, example values are provided and should + // be replaced with values that match your hardware + PosMagneticStripeReaderCapabilitiesType capabilities; + capabilities.PowerReportingType = DriverUnifiedPosPowerReportingType::Standard; + capabilities.IsStatisticsReportingSupported = TRUE; + capabilities.IsStatisticsUpdatingSupported = TRUE; + capabilities.CardAuthenticationLength = 0; + capabilities.SupportedEncryptionAlgorithms = MsrDataEncryption::MsrDataEncryption_AES; + capabilities.AuthenticationLevel = DriverMagneticStripeReaderAuthenticationLevel::Optional; + capabilities.IsIsoSupported = TRUE; + capabilities.IsJisOneSupported = TRUE; + capabilities.IsJisTwoSupported = TRUE; + capabilities.IsTrackDataMaskingSupported = TRUE; + capabilities.IsTransmitSentinelsSupported = TRUE; + size_t bytesToCopy = sizeof(PosMagneticStripeReaderCapabilitiesType); + if (outputBufferLength < bytesToCopy) + { + bytesToCopy = outputBufferLength; + status = STATUS_BUFFER_OVERFLOW; + } + memcpy(outputBuffer, &capabilities, bytesToCopy); + *Information = bytesToCopy; + } + break; + + case PosPropertyId::MagneticStripeReaderSupportedCardTypes: + { + // Supported card types. The API understands Bank and AAMVA, but additional, device-specific values can be added as well + // This property is typically hard coded based on the device type + MSR_SUPPORTED_CARD_TYPES supportedCardTypes; + RtlZeroMemory(&supportedCardTypes, sizeof(MSR_SUPPORTED_CARD_TYPES)); + supportedCardTypes.Count = 2; + supportedCardTypes.CardTypes[0] = (unsigned int)MsrCardType::MsrCardType_Bank; + supportedCardTypes.CardTypes[1] = (unsigned int)MsrCardType::MsrCardType_Aamva; + size_t bytesToCopy = sizeof(MSR_SUPPORTED_CARD_TYPES); + if (outputBufferLength < bytesToCopy) + { + bytesToCopy = outputBufferLength; + status = STATUS_BUFFER_OVERFLOW; + } + memcpy(outputBuffer, &supportedCardTypes, bytesToCopy); + *Information = bytesToCopy; + } + break; + + case PosPropertyId::MagneticStripeReaderDeviceAuthenticationProtocol: + { + // Returns whether the device supports challenge/response authentication or not. + *((MsrAuthenticationProtocolType*)outputBuffer) = MsrAuthenticationProtocolType::MsrAuthenticationProtocolType_ChallengeResponse; + *Information = sizeof(MsrAuthenticationProtocolType); + } + break; + + case PosPropertyId::MagneticStripeReaderErrorReportingType: + { + // Returns whether the device should report errors at the card or track level. + // This value is typically retrieved from the device context based on a prior SetProperty(MagneticStripeReaderErrorReportingType) + *((MsrErrorReportingType*)outputBuffer) = MsrErrorReportingType::MsrErrorReportingType_CardLevel; + *Information = sizeof(MsrErrorReportingType); + } + break; + + case PosPropertyId::MagneticStripeReaderTracksToRead: + { + // typically this value is saved in the device context to track which tracks the application wants to read from the card + MsrTrackIds tracksToRead = (MsrTrackIds)(MsrTrackIds::MsrTrackIds_Track1 | MsrTrackIds::MsrTrackIds_Track2); + *((MsrTrackIds*)outputBuffer) = tracksToRead; + *Information = sizeof(MsrTrackIds); + } + break; + + case PosPropertyId::MagneticStripeReaderIsTransmitSentinelsEnabled: + { + // BOOL property. True if the data that is sent to the application will include start/end sentinals or not + // This property can return STATUS_NOT_SUPPORTED if the IsTransmitSentinelsSupported capability is FALSE + // Otherwise, the boolean that's returned should be captured from the device context or from the device itself + BOOL isTransmitSentinelsEnabled = TRUE; + *((BOOL*)outputBuffer) = isTransmitSentinelsEnabled; + *Information = sizeof(BOOL); + } + break; + + case PosPropertyId::MagneticStripeReaderIsDeviceAuthenticated: + { + // BOOL property. True if the the authentication process has been successful + // This value should come from the device context or from the device itself + BOOL isAuthenticated = TRUE; + *((BOOL*)outputBuffer) = isAuthenticated; + *Information = sizeof(BOOL); + } + break; + + case PosPropertyId::MagneticStripeReaderDataEncryptionAlgorithm: + { + // Returns the encryption algorithm used to decrypt the card data + // If the device supports multiple algorithms, it should store the current one in the device context and use that value here + // (or retrieve the value from the device itself) + MsrDataEncryption currentEncryptionAlgorithm = MsrDataEncryption::MsrDataEncryption_AES; + *((MsrDataEncryption*)outputBuffer) = currentEncryptionAlgorithm; + } + break; + + default: + // no other readable properties for magnetic stripe reader + return STATUS_INVALID_PARAMETER; + } + + return STATUS_SUCCESS; +} + +/* +** Driver TODO: Add code to handle various set-property cases. +** +** Implement this function to handle property set requests. +*/ +NTSTATUS ProcessSetPropertyRequest(_In_ WDFREQUEST Request, _In_ size_t InputBufferLength, _Inout_ ULONG_PTR* Information) +{ + if (Information == nullptr || Request == nullptr || InputBufferLength < sizeof(PosPropertyId)) + { + return STATUS_INVALID_PARAMETER; + } + + // POS properties are identified by the property ID that's transmitted in the input buffer. + // The data that is used to set the property immediately follows the property ID, so the input buffer must be big enough to contain both. + PosPropertyId* propertyId; + NTSTATUS status = WdfRequestRetrieveInputBuffer( + Request, + sizeof(PosPropertyId), + reinterpret_cast(&propertyId), + nullptr + ); + + if (!NT_SUCCESS(status)) + { + return status; + } + + size_t argumentLength = InputBufferLength - sizeof(PosPropertyId); + void* argumentData = (void*)(propertyId + 1); + + // Handle this set of writable properties + switch (*propertyId) + { + case PosPropertyId::IsEnabled: + // BOOL value + if (argumentLength >= sizeof(BOOL)) + { + // The driver should use this value to ensure the device is ready to take data. + // The value may also need to be cached in a device context object so that it can be returned in GetProperty(IsEnabled) + BOOL isEnabled = *((BOOL*)argumentData); + UNREFERENCED_PARAMETER(isEnabled); + } + else + { + status = STATUS_BUFFER_TOO_SMALL; + } + break; + + case PosPropertyId::IsDisabledOnDataReceived: + // BOOL value + if (argumentLength >= sizeof(BOOL)) + { + // Typically this value will get cached in the device context so that, when + // an MSR read occurs, the driver can disable the device. + BOOL isDisabledOnDataReceived = *((BOOL*)argumentData); + UNREFERENCED_PARAMETER(isDisabledOnDataReceived); + } + else + { + status = STATUS_BUFFER_TOO_SMALL; + } + break; + + case PosPropertyId::MagneticStripeReaderIsDecodeDataEnabled: + // BOOL value + if (argumentLength >= sizeof(BOOL)) + { + // Typically this value will get cached in the device context so that, when + // an MSR read occurs, the driver can decode the raw data to get the scan data label. + BOOL isDecodeDataEnabled = *((BOOL*)argumentData); + UNREFERENCED_PARAMETER(isDecodeDataEnabled); + } + else + { + status = STATUS_BUFFER_TOO_SMALL; + } + break; + + case PosPropertyId::MagneticStripeReaderErrorReportingType: + // MsrErrorReportingType value + if (argumentLength >= sizeof(MsrErrorReportingType)) + { + // Typically this value will get cached in the device context so that, when + // a failed MSR read occurs, the driver can report the error correctly + MsrErrorReportingType errorReporting = *((MsrErrorReportingType*)argumentData); + UNREFERENCED_PARAMETER(errorReporting); + } + else + { + status = STATUS_BUFFER_TOO_SMALL; + } + break; + + case PosPropertyId::MagneticStripeReaderTracksToRead: + // MsrTrackIds value + if (argumentLength >= sizeof(MsrErrorReportingType)) + { + // This value will either be sent to the device to limit the tracks that are read, + // or cached in the device context so that the driver can only report the given tracks + // during an MSR read. + MsrTrackIds tracksToRead = *((MsrTrackIds*)argumentData); + UNREFERENCED_PARAMETER(tracksToRead); + } + else + { + status = STATUS_BUFFER_TOO_SMALL; + } + break; + + case PosPropertyId::MagneticStripeReaderIsTransmitSentinelsEnabled: + // BOOL value + if (argumentLength >= sizeof(BOOL)) + { + // This value will either be sent to the device to enable or disable sending sentinel data, + // or cached in the device context so that the driver can insert or remove the sentinel data during an MSR read. + BOOL isTransmitSentinelsEnabled = *((BOOL*)argumentData); + UNREFERENCED_PARAMETER(isTransmitSentinelsEnabled); + } + else + { + status = STATUS_BUFFER_TOO_SMALL; + } + break; + + case PosPropertyId::MagneticStripeReaderDataEncryptionAlgorithm: + // MsrDataEncryption value + if (argumentLength >= sizeof(MsrDataEncryption)) + { + // This property may be rejected if the SupportedEncryptionAlgorithms capability indicates that no encryption is supported. + // Otherwise it should set the current decryption algorithm (either by sending it to the device or by caching it for use + // by the driver during an MSR read). + MsrDataEncryption encryptionAlgorithm = *((MsrDataEncryption*)argumentData); + UNREFERENCED_PARAMETER(encryptionAlgorithm); + } + else + { + status = STATUS_BUFFER_TOO_SMALL; + } + break; + + default: + // no other writable properties for magnetic stripe reader + return STATUS_INVALID_PARAMETER; + } + + return STATUS_SUCCESS; +} + +/* +** Driver TODO: Replace the data in the ProcessRetrieveStatisticsRequest with your own statistics data +** +** Implement this function to handle retrieve statistics requests. +*/ +NTSTATUS ProcessRetrieveStatisticsRequest(_In_ WDFREQUEST Request, _In_ size_t OutputBufferLength, _Inout_ ULONG_PTR* Information) +{ + if (Information == nullptr || Request == nullptr) + { + return STATUS_INVALID_PARAMETER; + } + + struct + { + PosStatisticsHeader Header; + PosValueStatisticsEntry Entries[1]; + } StatisticsData; + + StatisticsData.Header.DataLength = sizeof(StatisticsData); + wcscpy_s(StatisticsData.Header.DeviceInformation.DeviceCategory, L"MSR"); + wcscpy_s(StatisticsData.Header.DeviceInformation.FirmwareRevision, L""); + wcscpy_s(StatisticsData.Header.DeviceInformation.InstallationDate, L""); + wcscpy_s(StatisticsData.Header.DeviceInformation.Interface, L""); + wcscpy_s(StatisticsData.Header.DeviceInformation.ManufactureDate, L""); + wcscpy_s(StatisticsData.Header.DeviceInformation.ManufacturerName, L""); + wcscpy_s(StatisticsData.Header.DeviceInformation.MechanicalRevision, L""); + wcscpy_s(StatisticsData.Header.DeviceInformation.ModelName, L""); + wcscpy_s(StatisticsData.Header.DeviceInformation.SerialNumber, L""); + wcscpy_s(StatisticsData.Header.DeviceInformation.UnifiedPOSVersion, L"1.14"); + StatisticsData.Header.EntryCount = 1; + wcscpy_s(StatisticsData.Entries[0].EntryName, L""); + StatisticsData.Entries[0].Value = (LONG)1; + + // This IOCTL is called twice by the Windows.Devices.PointOfService APIs + // The first time will just retrieve the header to determine how big the buffer needs to be. + PVOID outputBuffer; + NTSTATUS status = WdfRequestRetrieveOutputBuffer( + Request, + sizeof(PosStatisticsHeader), + &outputBuffer, + nullptr + ); + + if (!NT_SUCCESS(status)) + { + *Information = sizeof(StatisticsData); + return status; + } + + if (OutputBufferLength == sizeof(PosStatisticsHeader)) + { + memcpy(outputBuffer, &(StatisticsData.Header), sizeof(PosStatisticsHeader)); + *Information = sizeof(PosStatisticsHeader); + } + else if (OutputBufferLength < sizeof(StatisticsData)) + { + *Information = sizeof(StatisticsData); + status = STATUS_BUFFER_TOO_SMALL; + } + else + { + memcpy(outputBuffer, &StatisticsData, sizeof(StatisticsData)); + *Information = sizeof(StatisticsData); + } + + return status; +} + +/* +** Driver TODO: loop over statisticsEntry[0]...statisticsEntry[inputBuffer->EntryCount - 1] and reset each statistics value named +** +** Implement this function to handle statistics reset requests. +*/ +NTSTATUS ProcessResetStatisticsRequest(_In_ WDFREQUEST Request) +{ + if (Request == nullptr) + { + return STATUS_INVALID_PARAMETER; + } + + // The input buffer must be PosStatisticsHeader followed by one or more PosValueStatisticsEntry (where the value is ignored, just the name is used to + // reset the statistics value). + PosStatisticsHeader* inputBuffer; + size_t totalLength; + NTSTATUS status = WdfRequestRetrieveInputBuffer( + Request, + sizeof(PosStatisticsHeader), + (PVOID*)&inputBuffer, + &totalLength); + + if (!NT_SUCCESS(status)) + { + return status; + } + + if (inputBuffer->DataLength > totalLength) + { + return STATUS_BUFFER_TOO_SMALL; + } + + size_t entryLength = inputBuffer->DataLength - sizeof(PosStatisticsHeader); + if ( + entryLength % sizeof(PosValueStatisticsEntry) || + (entryLength / sizeof(PosValueStatisticsEntry)) != inputBuffer->EntryCount || + inputBuffer->EntryCount == 0 + ) + { + return STATUS_INVALID_PARAMETER; + } + + PosValueStatisticsEntry* statisticsEntry = (PosValueStatisticsEntry*) (inputBuffer + 1); + + for (UINT32 index = 0; index < inputBuffer->EntryCount; ++index) + { + // reset this value: + statisticsEntry[index].EntryName; + } + + return STATUS_SUCCESS; +} + +/* +** Driver TODO: loop over statisticsEntry[0]...statisticsEntry[inputBuffer->EntryCount - 1] and update each statistics value named +** +** Implement this function to handle statistics update requests. +*/ +NTSTATUS ProcessUpdateStatisticsRequest(_In_ WDFREQUEST Request) +{ + if (Request == nullptr) + { + return STATUS_INVALID_PARAMETER; + } + + // The input buffer must be PosStatisticsHeader followed by one or more PosValueStatisticsEntry + PosStatisticsHeader* inputBuffer; + size_t totalLength; + NTSTATUS status = WdfRequestRetrieveInputBuffer( + Request, + sizeof(PosStatisticsHeader), + (PVOID*)&inputBuffer, + &totalLength); + + if (!NT_SUCCESS(status)) + { + return status; + } + + if (inputBuffer->DataLength > totalLength) + { + return STATUS_BUFFER_TOO_SMALL; + } + + size_t entryLength = inputBuffer->DataLength - sizeof(PosStatisticsHeader); + if ( + entryLength % sizeof(PosValueStatisticsEntry) || + (entryLength / sizeof(PosValueStatisticsEntry)) != inputBuffer->EntryCount || + inputBuffer->EntryCount == 0 + ) + { + return STATUS_INVALID_PARAMETER; + } + + PosValueStatisticsEntry* statisticsEntry = (PosValueStatisticsEntry*)(inputBuffer + 1); + + for (UINT32 index = 0; index < inputBuffer->EntryCount; ++index) + { + // update the statistics entry: + statisticsEntry[index].EntryName; + // with the value: + statisticsEntry[index].Value; + } + + return STATUS_SUCCESS; +} + +/* +** Driver TODO: Add code to ProcessCheckHealthRequest to handle different health check cases. The result should be a localized string that is returned to the user in the output buffer of the IOCTL. +** +** Implement this function to handle health check requests. +*/ +NTSTATUS ProcessCheckHealthRequest(_In_ WDFREQUEST Request, _Inout_ ULONG_PTR* Information) +{ + if (Information == nullptr || Request == nullptr) + { + return STATUS_INVALID_PARAMETER; + } + + DriverUnifiedPosHealthCheckLevel* level; + + NTSTATUS status = WdfRequestRetrieveInputBuffer( + Request, + sizeof(DriverUnifiedPosHealthCheckLevel), + (PVOID*)&level, + nullptr); + + if (!NT_SUCCESS(status)) + { + return status; + } + + PosStringType* outputBuffer; + size_t outputBufferLength; + status = WdfRequestRetrieveOutputBuffer( + Request, + sizeof(PosStringType), + (void**)(&outputBuffer), + &outputBufferLength + ); + + if (!NT_SUCCESS(status)) + { + return status; + } + + switch (*level) + { + case DriverUnifiedPosHealthCheckLevel::POSInternal: + case DriverUnifiedPosHealthCheckLevel::External: + case DriverUnifiedPosHealthCheckLevel::Interactive: + { + // Handle the specific health check level, depending on the applicability to your device. + // Return the result as a string that the user can use to determine whether the device is + // operational or needs attention. + LPCWSTR result = L"OK"; + size_t lengthInBytes = wcslen(result) * sizeof(WCHAR); + status = RtlSizeTToUInt32(lengthInBytes, &(outputBuffer->DataLengthInBytes)); + if (NT_SUCCESS(status)) + { + *Information = sizeof(PosStringType); + if (outputBufferLength >= sizeof(PosStringType)+outputBuffer->DataLengthInBytes) + { + void* outputStringStart = (void*)(outputBuffer + 1); + memcpy(outputStringStart, result, outputBuffer->DataLengthInBytes); + *Information += outputBuffer->DataLengthInBytes; + } + else + { + status = STATUS_BUFFER_OVERFLOW; + } + } + } + break; + default: + return STATUS_INVALID_PARAMETER; + } + + return STATUS_SUCCESS; +} + +/* +** Driver TODO: +** +** Implement this function to handle the initial handshake IOCTL for Windows.Devices.PointOfService API <-> Driver communication. +** This sample will likely work for most cases. +*/ +NTSTATUS ProcessGetDeviceBasicsRequest(_In_ WDFREQUEST Request, _Inout_ ULONG_PTR* Information) +{ + if (Information == nullptr || Request == nullptr) + { + return STATUS_INVALID_PARAMETER; + } + + UINT32* runtimeVersion; + PosDeviceBasicsType* outputData; + + NTSTATUS status = WdfRequestRetrieveInputBuffer( + Request, + sizeof(UINT32), + (PVOID*)&runtimeVersion, + nullptr); + + if (!NT_SUCCESS(status)) + { + return status; + } + + status = WdfRequestRetrieveOutputBuffer( + Request, + sizeof(PosDeviceBasicsType), + (PVOID*)&outputData, + nullptr); + + if (!NT_SUCCESS(status)) + { + return status; + } + + // Tell the runtime what version of the POS IOCTL interface this driver supports so that + // it won't send IOCTLs that the driver doesn't support. + outputData->Version = POS_DRIVER_VERSION; + // This is a magnetic stripe reader driver + outputData->DeviceType = PosDeviceType::PosDeviceType_MagneticStripeReader; + // This value will be used to set the initial ReadFile buffer size. A small size that is + // likely to cover most of the data events is suggested. The runtime will grow the ReadFile + // buffer size as needed. + outputData->RecommendedBufferSize = 128; + + *Information = sizeof(PosDeviceBasicsType); + + return STATUS_SUCCESS; +} diff --git a/pos/drivers/MagneticStripeReader/Ioctl.h b/pos/drivers/MagneticStripeReader/Ioctl.h new file mode 100644 index 000000000..55f629f68 --- /dev/null +++ b/pos/drivers/MagneticStripeReader/Ioctl.h @@ -0,0 +1,3 @@ +#pragma once + +EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL EvtIoDeviceControl; diff --git a/pos/drivers/MagneticStripeReader/MagneticStripeReader.sln b/pos/drivers/MagneticStripeReader/MagneticStripeReader.sln new file mode 100644 index 000000000..f1209ca75 --- /dev/null +++ b/pos/drivers/MagneticStripeReader/MagneticStripeReader.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}") = "SampleMagneticStripeReaderDrv", "SampleMagneticStripeReaderDrv.vcxproj", "{F9DEC69D-2609-48DF-BE52-A0099482FB40}" +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 + {F9DEC69D-2609-48DF-BE52-A0099482FB40}.Debug|Win32.ActiveCfg = Debug|Win32 + {F9DEC69D-2609-48DF-BE52-A0099482FB40}.Debug|Win32.Build.0 = Debug|Win32 + {F9DEC69D-2609-48DF-BE52-A0099482FB40}.Release|Win32.ActiveCfg = Release|Win32 + {F9DEC69D-2609-48DF-BE52-A0099482FB40}.Release|Win32.Build.0 = Release|Win32 + {F9DEC69D-2609-48DF-BE52-A0099482FB40}.Debug|x64.ActiveCfg = Debug|x64 + {F9DEC69D-2609-48DF-BE52-A0099482FB40}.Debug|x64.Build.0 = Debug|x64 + {F9DEC69D-2609-48DF-BE52-A0099482FB40}.Release|x64.ActiveCfg = Release|x64 + {F9DEC69D-2609-48DF-BE52-A0099482FB40}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/pos/drivers/MagneticStripeReader/PosEvents.cpp b/pos/drivers/MagneticStripeReader/PosEvents.cpp new file mode 100644 index 000000000..501a1a22f --- /dev/null +++ b/pos/drivers/MagneticStripeReader/PosEvents.cpp @@ -0,0 +1,50 @@ +#include + +/* +** Driver TODO: Add code to EvtDeviceOwnershipChange to reset the device state to a default. +** +** PosCx calls this callback to signal that the ownership of the device has transitioned from one file handle +** to another. When this happens, app developers expect that the settings for the device are restored to a +** "default" state, so the driver should do what is necessary to satisfy that expectation here. +** +*/ +VOID EvtDeviceOwnershipChange(_In_ WDFDEVICE Device, _In_opt_ WDFFILEOBJECT OldOwnerFileObj, _In_opt_ WDFFILEOBJECT NewOwnerFileObj) +{ + UNREFERENCED_PARAMETER(Device); + UNREFERENCED_PARAMETER(OldOwnerFileObj); + UNREFERENCED_PARAMETER(NewOwnerFileObj); + + // This function signals that ownership has transitioned from one file handle to another (typically from one app to another). + // As a result, the driver is expected to reset all settings to a default state. +} + +/* +** Driver TODO: +** +** This part of the sample demonstrates how the driver will return data to the runtime. How this method would be called depends +** on the driver implementation. +*/ +VOID EvtOnMsrScanDataRetrieved(_In_ WDFDEVICE Device) +{ + // Events that the runtime can handle for a magnetic stripe reader device are: + // PosEventType::MagneticStripeReaderDataReceived -- the standard event with MSR data + // PosEventType::MagneticStripeReaderErrorOccurred -- an event that should be sent if an error occured while scanning data + // PosEventType::StatusUpdated -- an event to indicate changes to power state + // + // Additionally, the following event is sent to the runtime, but is handled entirely by PosCx + // PosEventType::ReleaseDeviceRequested + + // The following shows an example of sending MSR data + + MSR_DATA_RECEIVED dataReceivedEventInfo; + + // Fill in all the fields in MSR_DATA_RECEIVED + + // This call actually pends the data to be send to the WinRT APIs. + NTSTATUS status = PosCxPutPendingEvent(Device, MSR_INTERFACE_TAG, PosEventType::MagneticStripeReaderDataReceived, sizeof(dataReceivedEventInfo), &dataReceivedEventInfo, POS_CX_EVENT_ATTR_DATA); + + if (!NT_SUCCESS(status)) + { + // This should only happen in rare cases such as out of memory (or that the device or interface tag isn't found). The driver should most likely drop the event. + } +} \ No newline at end of file diff --git a/pos/drivers/MagneticStripeReader/PosEvents.h b/pos/drivers/MagneticStripeReader/PosEvents.h new file mode 100644 index 000000000..817b3ad1e --- /dev/null +++ b/pos/drivers/MagneticStripeReader/PosEvents.h @@ -0,0 +1,3 @@ +#pragma once + +EVT_POS_CX_DEVICE_OWNERSHIP_CHANGE EvtDeviceOwnershipChange; diff --git a/pos/drivers/MagneticStripeReader/README.md b/pos/drivers/MagneticStripeReader/README.md new file mode 100644 index 000000000..5530f60ce --- /dev/null +++ b/pos/drivers/MagneticStripeReader/README.md @@ -0,0 +1,7 @@ +Magnetic Stripe Reader Driver Sample +==================================== +This sample serves as a template for creating a new Magnetic Stripe Reader driver. + +This sample uses UMDF 2.0 and enables basic functionality such as claiming and enabling the device for exclusive access. + +It serves as an example of how to include the libraries necessary to develop a PointOfService driver. Once a driver is developed using this template it can be compiled for, deployed, and used on x86, amd64, and ARM platforms. \ No newline at end of file diff --git a/pos/drivers/MagneticStripeReader/SampleMagneticStripeReaderDrv.inf b/pos/drivers/MagneticStripeReader/SampleMagneticStripeReaderDrv.inf new file mode 100644 index 000000000..f34011272 --- /dev/null +++ b/pos/drivers/MagneticStripeReader/SampleMagneticStripeReaderDrv.inf @@ -0,0 +1,78 @@ +; +; SampleMagneticStripeReaderDrv.inf +; + +[Version] +Signature="$Windows NT$" +Class=Sample ; +ClassGuid={60B92AD1-5773-4FF7-82E3-9F83198325D6} ; +Provider=Standard,NT$ARCH$ +CatalogFile=SampleMagneticStripeReaderDrv.cat +DriverVer=06/25/2015,14.29.18.671 + +[Manufacturer] +%ManufacturerName%=Standard,NT$ARCH$ + +[Standard.NT$ARCH$] +%DeviceName%=MyDevice_Install, Root\SampleMagneticStripeReaderDrv ; + + +[ClassInstall32] +AddReg=SampleClass_RegistryAdd + +[SampleClass_RegistryAdd] +HKR,,,,%ClassName% +HKR,,Icon,,"-10" + +[SourceDisksFiles] +SampleMagneticStripeReaderDrv.dll=1 + +[SourceDisksNames] +1 = %DiskName% + +; =================== UMDF Device ================================== + +[MyDevice_Install.NT] +CopyFiles=UMDriverCopy + +[MyDevice_Install.NT.hw] + +[MyDevice_Install.NT.Services] +AddService=WUDFRd,0x000001fa,WUDFRD_ServiceInstall + +[MyDevice_Install.NT.CoInstallers] +AddReg=CoInstallers_AddReg + +[MyDevice_Install.NT.Wdf] +UmdfService=SampleMagneticStripeReaderDrv,SampleMagneticStripeReaderDrv_Install +UmdfServiceOrder=SampleMagneticStripeReaderDrv + +[SampleMagneticStripeReaderDrv_Install] +UmdfLibraryVersion=2.15.0 +ServiceBinary=%12%\UMDF\SampleMagneticStripeReaderDrv.dll +UmdfExtensions=PosCx0102 + +[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 drivers\umdf + +[UMDriverCopy] +SampleMagneticStripeReaderDrv.dll + +; =================== Generic ================================== + +[Strings] +ManufacturerName="" ; +ClassName="Samples" ; +DiskName = "SampleMagneticStripeReaderDrv Installation Disk" +WudfRdDisplayName="Windows Driver Foundation - User-mode Driver Framework Reflector" +DeviceName="SampleMagneticStripeReaderDrv Device" diff --git a/pos/drivers/MagneticStripeReader/SampleMagneticStripeReaderDrv.vcxproj b/pos/drivers/MagneticStripeReader/SampleMagneticStripeReaderDrv.vcxproj new file mode 100644 index 000000000..a23fdcfb1 --- /dev/null +++ b/pos/drivers/MagneticStripeReader/SampleMagneticStripeReaderDrv.vcxproj @@ -0,0 +1,252 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {FA5835C3-4B1D-4B48-BE8E-2A9B5764932E} + $(MSBuildProjectName) + 2 + Debug + Win32 + {F3A9F934-71E7-434E-B9F4-486EF2F9B1D5} + + + + 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) + + + + + + + + + + + + + + + + SampleMagneticStripeReaderDrv + + + SampleMagneticStripeReaderDrv + + + SampleMagneticStripeReaderDrv + + + SampleMagneticStripeReaderDrv + + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + Sync + true + Level4 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(SDK_INC_PATH)\pos\1.1;..\inc + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(SDK_INC_PATH)\pos\1.1;..\inc + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(SDK_INC_PATH)\pos\1.1;..\inc + + + + + Sync + true + Level4 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(SDK_INC_PATH)\pos\1.1;..\inc + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(SDK_INC_PATH)\pos\1.1;..\inc + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(SDK_INC_PATH)\pos\1.1;..\inc + + + + + Sync + true + Level4 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(SDK_INC_PATH)\pos\1.1;..\inc + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(SDK_INC_PATH)\pos\1.1;..\inc + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(SDK_INC_PATH)\pos\1.1;..\inc + + + + + Sync + true + Level4 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(SDK_INC_PATH)\pos\1.1;..\inc + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(SDK_INC_PATH)\pos\1.1;..\inc + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(SDK_INC_PATH)\pos\1.1;..\inc + + + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\pos\1.1\poscxstub.lib + exports.def + + + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\pos\1.1\poscxstub.lib + exports.def + + + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\pos\1.1\poscxstub.lib + exports.def + + + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\pos\1.1\poscxstub.lib + exports.def + + + + + ;%(AdditionalIncludeDirectories) + pch.h + Use + $(IntDir)\pch.h.pch + + + ;%(AdditionalIncludeDirectories) + pch.h + Use + $(IntDir)\pch.h.pch + + + ;%(AdditionalIncludeDirectories) + pch.h + Use + $(IntDir)\pch.h.pch + + + ;%(AdditionalIncludeDirectories) + pch.h + Use + $(IntDir)\pch.h.pch + + + ;%(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/pos/drivers/MagneticStripeReader/SampleMagneticStripeReaderDrv.vcxproj.Filters b/pos/drivers/MagneticStripeReader/SampleMagneticStripeReaderDrv.vcxproj.Filters new file mode 100644 index 000000000..6721e14ed --- /dev/null +++ b/pos/drivers/MagneticStripeReader/SampleMagneticStripeReaderDrv.vcxproj.Filters @@ -0,0 +1,47 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {52A5F13E-45A2-4076-ABBD-A9DD641BD180} + + + h;hpp;hxx;hm;inl;inc;xsd + {BA08299B-8F7E-482D-9464-D2E1E3718EB8} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {BD45DA3A-8FEB-4DD6-BB19-6D73802A80B8} + + + inf;inv;inx;mof;mc; + {664C5380-85E0-4C11-B14E-3C7A80180599} + + + + + 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/pos/drivers/MagneticStripeReader/exports.def b/pos/drivers/MagneticStripeReader/exports.def new file mode 100644 index 000000000..22dad17ca --- /dev/null +++ b/pos/drivers/MagneticStripeReader/exports.def @@ -0,0 +1,2 @@ +LIBRARY "SampleMagneticStripeReaderDrv.dll" + diff --git a/pos/drivers/MagneticStripeReader/pch.h b/pos/drivers/MagneticStripeReader/pch.h new file mode 100644 index 000000000..5807e3620 --- /dev/null +++ b/pos/drivers/MagneticStripeReader/pch.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include +#include +#include + +#include "PointOfServiceCommonTypes.h" +#include "PointOfServiceDriverInterface.h" + +#include "PosCx.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif +DRIVER_INITIALIZE DriverEntry; +#ifdef __cplusplus +} +#endif + +#define MSR_INTERFACE_TAG ((ULONG) '0RSM') diff --git a/pos/drivers/MagneticStripeReader/pchsrc.cpp b/pos/drivers/MagneticStripeReader/pchsrc.cpp new file mode 100644 index 000000000..17305716a --- /dev/null +++ b/pos/drivers/MagneticStripeReader/pchsrc.cpp @@ -0,0 +1 @@ +#include "pch.h" \ No newline at end of file diff --git a/pos/drivers/barcodescanner/BarcodeScanner.sln b/pos/drivers/barcodescanner/BarcodeScanner.sln new file mode 100644 index 000000000..1072df65f --- /dev/null +++ b/pos/drivers/barcodescanner/BarcodeScanner.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}") = "SampleBarcodeScannerDrv", "SampleBarcodeScannerDrv.vcxproj", "{30F6FA25-B31E-46B0-AFBB-2AC9BA3319F0}" +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 + {30F6FA25-B31E-46B0-AFBB-2AC9BA3319F0}.Debug|Win32.ActiveCfg = Debug|Win32 + {30F6FA25-B31E-46B0-AFBB-2AC9BA3319F0}.Debug|Win32.Build.0 = Debug|Win32 + {30F6FA25-B31E-46B0-AFBB-2AC9BA3319F0}.Release|Win32.ActiveCfg = Release|Win32 + {30F6FA25-B31E-46B0-AFBB-2AC9BA3319F0}.Release|Win32.Build.0 = Release|Win32 + {30F6FA25-B31E-46B0-AFBB-2AC9BA3319F0}.Debug|x64.ActiveCfg = Debug|x64 + {30F6FA25-B31E-46B0-AFBB-2AC9BA3319F0}.Debug|x64.Build.0 = Debug|x64 + {30F6FA25-B31E-46B0-AFBB-2AC9BA3319F0}.Release|x64.ActiveCfg = Release|x64 + {30F6FA25-B31E-46B0-AFBB-2AC9BA3319F0}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/pos/drivers/barcodescanner/Device.cpp b/pos/drivers/barcodescanner/Device.cpp new file mode 100644 index 000000000..d122e60a9 --- /dev/null +++ b/pos/drivers/barcodescanner/Device.cpp @@ -0,0 +1,101 @@ +#include + +#include "File.h" +#include "PosEvents.h" +#include "Ioctl.h" +#include "IoRead.h" + +/* +** Driver TODO: Complete the implementation of EvtDriverDeviceAdd for your specific device. +** +** WDF calls this callback when a device instance is added to the driver. Good drivers will do a lot of +** work here to set up everything necessary, such as adding callbacks for PNP power state changes. +** This function defines an IO queue for handling DeviceIoControl and file read requests, both of which are +** important to the POS barcode scanner model. +** +** Note that this is not a complete device add implementation, as the PNP power callbacks are not handled. +** Additionally, driver writers may wish to set up additional queues to serialize device property requests +** (see Ioctl.cpp for more info). +*/ +NTSTATUS EvtDriverDeviceAdd(_In_ WDFDRIVER /* UnusedDriver */, _Inout_ PWDFDEVICE_INIT DeviceInit) +{ + NTSTATUS status = STATUS_SUCCESS; + WDF_FILEOBJECT_CONFIG fileConfig; + WDF_OBJECT_ATTRIBUTES deviceAttributes; + WDF_OBJECT_ATTRIBUTES fileAttributes; + WDFDEVICE device; + + // Handle file events + WDF_FILEOBJECT_CONFIG_INIT( + &fileConfig, + EvtDeviceFileCreate, + EvtFileClose, + WDF_NO_EVENT_CALLBACK + ); + + WDF_OBJECT_ATTRIBUTES_INIT(&fileAttributes); + WdfDeviceInitSetFileObjectConfig( + DeviceInit, + &fileConfig, + &fileAttributes + ); + + // Create Device + WDF_OBJECT_ATTRIBUTES_INIT(&deviceAttributes); + status = WdfDeviceCreate( + &DeviceInit, + &deviceAttributes, + &device + ); + + if (!NT_SUCCESS(status)) + { + return status; + } + + // Create a device interface for POS Barcode Scanner so that the device can be enumerated + status = WdfDeviceCreateDeviceInterface( + device, + &GUID_DEVINTERFACE_POS_SCANNER, + NULL + ); + + if (!NT_SUCCESS(status)) + { + return status; + } + + // Initialize the POS library + POS_CX_ATTRIBUTES posCxAttributes; + POS_CX_ATTRIBUTES_INIT(&posCxAttributes); + posCxAttributes.EvtDeviceOwnershipChange = EvtDeviceOwnershipChange; + + status = PosCxInit(device, &posCxAttributes); + + if (!NT_SUCCESS(status)) + { + return status; + } + + // Set up an IO queue to handle DeviceIoControl and ReadFile + WDF_IO_QUEUE_CONFIG queueConfig; + WDF_OBJECT_ATTRIBUTES attributes; + WDFQUEUE queue; + + WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&queueConfig, WdfIoQueueDispatchSequential); + queueConfig.EvtIoDeviceControl = EvtIoDeviceControl; + queueConfig.EvtIoRead = EvtIoRead; + + // Call us in PASSIVE_LEVEL + WDF_OBJECT_ATTRIBUTES_INIT(&attributes); + attributes.ExecutionLevel = WdfExecutionLevelPassive; + + status = WdfIoQueueCreate( + device, + &queueConfig, + &attributes, + &queue + ); + + return status; +} diff --git a/pos/drivers/barcodescanner/Device.h b/pos/drivers/barcodescanner/Device.h new file mode 100644 index 000000000..df9ad4091 --- /dev/null +++ b/pos/drivers/barcodescanner/Device.h @@ -0,0 +1,3 @@ +#pragma once + +EVT_WDF_DRIVER_DEVICE_ADD EvtDriverDeviceAdd; diff --git a/pos/drivers/barcodescanner/Driver.cpp b/pos/drivers/barcodescanner/Driver.cpp new file mode 100644 index 000000000..5bcf2f336 --- /dev/null +++ b/pos/drivers/barcodescanner/Driver.cpp @@ -0,0 +1,54 @@ +#include + +#include "Device.h" + +// Forward declaration +VOID EvtDriverCleanup(_In_ WDFOBJECT DriverObject); + +/* +** Driver TODO: +** +** This is the main entry point of the driver. POS APIs require that the driver sets up additional data in device add. +** +** Note that your driver may have additional configuration to do in this function, and it should not be assumed that this sample is complete. +*/ +NTSTATUS DriverEntry( + PDRIVER_OBJECT DriverObject, + PUNICODE_STRING RegistryPath + ) +{ + WDF_DRIVER_CONFIG config; + NTSTATUS status = STATUS_SUCCESS; + WDF_OBJECT_ATTRIBUTES attributes; + + WDF_OBJECT_ATTRIBUTES_INIT(&attributes); + attributes.EvtCleanupCallback = EvtDriverCleanup; + + WDF_DRIVER_CONFIG_INIT( + &config, + EvtDriverDeviceAdd + ); + + status = WdfDriverCreate( + DriverObject, + RegistryPath, + &attributes, + &config, + WDF_NO_HANDLE + ); + + return status; +} + +/* +** Driver TODO: +** +** This is the cleanup callback for the driver (as set above in DriverEntry). +** PosCx requires no cleanup at this point. +*/ +_Use_decl_annotations_ +VOID EvtDriverCleanup(WDFOBJECT /* UnusedDriverObject */) +{ + // Do any cleanup needed here + return; +} diff --git a/pos/drivers/barcodescanner/File.cpp b/pos/drivers/barcodescanner/File.cpp new file mode 100644 index 000000000..43f100eea --- /dev/null +++ b/pos/drivers/barcodescanner/File.cpp @@ -0,0 +1,37 @@ +#include + +/* +** Driver TODO: +** +** WDF calls this callback when a file handle is opened to the driver. Your implementation may require additional setup (such as creating a +** file-handle-based context structure). PosCxOpen must be called during this callback. +*/ +VOID EvtDeviceFileCreate(_In_ WDFDEVICE Device, _In_ WDFREQUEST Request, _In_ WDFFILEOBJECT FileObject) +{ + NTSTATUS status = PosCxOpen(Device, FileObject, SCANNER_INTERFACE_TAG); + + if (!NT_SUCCESS(status)) + { + // This should only fail in rare cases, but the failure will prevent all PosCx functions from performing correctly + } + + WdfRequestComplete(Request, status); +} + +/* +** Driver TODO: +** +** WDF calls this callback when a file handle to the driver is closed. Your implementation may require additional cleanup, but +** PosCxClose must be called during this callback. +*/ +VOID EvtFileClose(_In_ WDFFILEOBJECT FileObject) +{ + WDFDEVICE device = WdfFileObjectGetDevice(FileObject); + + NTSTATUS status = PosCxClose(device, FileObject); + + if (!NT_SUCCESS(status)) + { + // This will only fail if PosCxInit wasn't called successfully in EvtDriverDeviceAdd, or if PosCxOpen failed in EvtDeviceFileCreate + } +} \ No newline at end of file diff --git a/pos/drivers/barcodescanner/File.h b/pos/drivers/barcodescanner/File.h new file mode 100644 index 000000000..73f815e0d --- /dev/null +++ b/pos/drivers/barcodescanner/File.h @@ -0,0 +1,4 @@ +#pragma once + +EVT_WDF_DEVICE_FILE_CREATE EvtDeviceFileCreate; +EVT_WDF_FILE_CLOSE EvtFileClose; diff --git a/pos/drivers/barcodescanner/IoRead.cpp b/pos/drivers/barcodescanner/IoRead.cpp new file mode 100644 index 000000000..4f188d890 --- /dev/null +++ b/pos/drivers/barcodescanner/IoRead.cpp @@ -0,0 +1,41 @@ +#include + +/* +** Driver TODO: Add logic to EvtIoRead to handle read requests from applications that don't use the Windows.Devices.PointOfService APIs. +** +** This is the callback for the IO queue that handles file read requests. In the POS barcode +** scanner model, the application will always queue a read request in order to receive events +** such as the data received event, or the release-claim requested event. +** +** Note that apps that are developed against the Windows.Devices.PointOfService APIs will always +** expect event data to be returned by read requests. It is up to the driver to determine the +** behavior of ReadFile when the driver is opened by other types of applications. +*/ +VOID EvtIoRead(_In_ WDFQUEUE Queue, _In_ WDFREQUEST Request, _In_ size_t Length) +{ + NTSTATUS status; + WDFDEVICE device = WdfIoQueueGetDevice(Queue); + WDFFILEOBJECT fileObject = WdfRequestGetFileObject(Request); + + UNREFERENCED_PARAMETER(Length); + + // Check the flag that may have been set by PosCxMarkPosApp in Ioctl.cpp. + if (!PosCxIsPosApp(device, fileObject)) + { + // An application has opened a handle to this device without using the Windows.Devices.PointOfService APIs. + // You may change this to handle the read request differently. + + // In this example, just complete the read request with a failure. + WdfRequestComplete(Request, STATUS_UNSUCCESSFUL); + } + else + { + // If this returns success, it has taken ownership of Request + status = PosCxGetPendingEvent(device, Request); + + if (!NT_SUCCESS(status)) + { + WdfRequestComplete(Request, status); + } + } +} \ No newline at end of file diff --git a/pos/drivers/barcodescanner/IoRead.h b/pos/drivers/barcodescanner/IoRead.h new file mode 100644 index 000000000..20f7dcdd5 --- /dev/null +++ b/pos/drivers/barcodescanner/IoRead.h @@ -0,0 +1,3 @@ +#pragma once + +EVT_WDF_IO_QUEUE_IO_READ EvtIoRead; \ No newline at end of file diff --git a/pos/drivers/barcodescanner/Ioctl.cpp b/pos/drivers/barcodescanner/Ioctl.cpp new file mode 100644 index 000000000..e9c01dff5 --- /dev/null +++ b/pos/drivers/barcodescanner/Ioctl.cpp @@ -0,0 +1,720 @@ +#include + +NTSTATUS ProcessGetPropertyRequest(_In_ WDFREQUEST Request, _In_ size_t InputBufferLength, _Inout_ ULONG_PTR* Information); +NTSTATUS ProcessSetPropertyRequest(_In_ WDFREQUEST Request, _In_ size_t InputBufferLength, _Inout_ ULONG_PTR* Information); +NTSTATUS ProcessRetrieveStatisticsRequest(_In_ WDFREQUEST Request, _In_ size_t OutputBufferLength, _Inout_ ULONG_PTR* Information); +NTSTATUS ProcessResetStatisticsRequest(_In_ WDFREQUEST Request); +NTSTATUS ProcessUpdateStatisticsRequest(_In_ WDFREQUEST Request); +NTSTATUS ProcessCheckHealthRequest(_In_ WDFREQUEST Request, _Inout_ ULONG_PTR* Information); +NTSTATUS ProcessGetDeviceBasicsRequest(_In_ WDFREQUEST Request, _Inout_ ULONG_PTR* Information); + +/* +** Driver TODO: Complete the implementation of EvtIoDeviceControl for your specific device (if necessary) +** +** WDF calls this callback when a device instance is added to the driver. Good drivers will do a lot of +** work here to set up everything necessary, such as adding callbacks for PNP power state changes. +** This function defines an IO queue for handling DeviceIoControl and file read requests, both of which are +** important to the POS barcode scanner model. +** +** Note that this is not a complete device add implementation, as the PNP power callbacks are not handled. +** Additionally, driver writers may wish to set up additional queues to serialize device property requests +** (see Ioctl.cpp for more info). +*/ +VOID EvtIoDeviceControl(_In_ WDFQUEUE Queue, _In_ WDFREQUEST Request, _In_ size_t OutputBufferLength, _In_ size_t InputBufferLength, _In_ ULONG IoControlCode) +{ + UNREFERENCED_PARAMETER(Queue); + + NTSTATUS status = STATUS_SUCCESS; + ULONG_PTR information = 0; + WDFDEVICE device = WdfIoQueueGetDevice(Queue); + WDFFILEOBJECT fileObject = WdfRequestGetFileObject(Request); + + // These are the set of IOCTLs that your device should handle to work with the Windows.Devices.PointOfService APIs. + switch (IoControlCode) + { + // The first three IOCTLs shouldn't require additional processing other than handing them off to PosCx + case IOCTL_POINT_OF_SERVICE_CLAIM_DEVICE: + status = PosCxClaimDevice(device, Request); + break; + + case IOCTL_POINT_OF_SERVICE_RELEASE_DEVICE: + status = PosCxReleaseDevice(device, fileObject); + break; + + case IOCTL_POINT_OF_SERVICE_RETAIN_DEVICE: + status = PosCxRetainDevice(device, Request); + break; + + + case IOCTL_POINT_OF_SERVICE_GET_PROPERTY: + status = ProcessGetPropertyRequest(Request, InputBufferLength, &information); + break; + + case IOCTL_POINT_OF_SERVICE_SET_PROPERTY: + status = ProcessSetPropertyRequest(Request, InputBufferLength, &information); + break; + + case IOCTL_POINT_OF_SERVICE_RETRIEVE_STATISTICS: + status = ProcessRetrieveStatisticsRequest(Request, OutputBufferLength, &information); + break; + + case IOCTL_POINT_OF_SERVICE_RESET_STATISTICS: + status = ProcessResetStatisticsRequest(Request); + break; + + case IOCTL_POINT_OF_SERVICE_UPDATE_STATISTICS: + status = ProcessUpdateStatisticsRequest(Request); + break; + + case IOCTL_POINT_OF_SERVICE_CHECK_HEALTH: + status = ProcessCheckHealthRequest(Request, &information); + break; + + // The Get Device Basics IOCTL is always the first IOCTL called by an application using the Windows.Devices.PointOfService APIs. + // Use it to determine when to call PosCxMarkPosApp (see notes about apps marked this way in IoRead.cpp) + case IOCTL_POINT_OF_SERVICE_GET_DEVICE_BASICS: + status = ProcessGetDeviceBasicsRequest(Request, &information); + (void)PosCxMarkPosApp(device, fileObject, TRUE); + break; + + default: + // Your device may support additional IOCTLs. In this sample, we return failure for anything else. + status = STATUS_NOT_SUPPORTED; + break; + } + + if (status != STATUS_PENDING) + { + WdfRequestCompleteWithInformation(Request, status, information); + } +} + +/* +** Driver TODO: Add code to handle various get-property cases. +** +** Implement this function to handle property get requests. +*/ +NTSTATUS ProcessGetPropertyRequest(_In_ WDFREQUEST Request, _In_ size_t InputBufferLength, _Inout_ ULONG_PTR* Information) +{ + if (Information == nullptr || Request == nullptr || InputBufferLength < sizeof(PosPropertyId)) + { + return STATUS_INVALID_PARAMETER; + } + + // POS properties are identified by the property ID that's transmitted in the input buffer. + PosPropertyId* propertyId; + NTSTATUS status = WdfRequestRetrieveInputBuffer( + Request, + sizeof(PosPropertyId), + reinterpret_cast(&propertyId), + nullptr + ); + + if (!NT_SUCCESS(status)) + { + return status; + } + + // All get properties will need access to the output buffer in order to return results. + // The minimum size returned is a UINT32 + void* outputBuffer; + size_t outputBufferLength; + status = WdfRequestRetrieveOutputBuffer( + Request, + sizeof(UINT32), + &outputBuffer, + &outputBufferLength + ); + + if (!NT_SUCCESS(status)) + { + return status; + } + + // Handle this set of readable properties + switch (*propertyId) + { + case PosPropertyId::IsEnabled: + // BOOL result, true when the app has called SetProperty(IsEnabled) = TRUE + { + BOOL isEnabled = TRUE; // Get this value from device context or by querying the device + *((BOOL*)outputBuffer) = isEnabled; + *Information = sizeof(BOOL); + } + break; + + case PosPropertyId::IsDisabledOnDataReceived: + // BOOL result, true when the app has called SetProperty(IsDisabledOnDataReceived) = TRUE + { + BOOL isDisabledOnDataReceived = TRUE; // Get this value from device context or by querying the device + *((BOOL*)outputBuffer) = isDisabledOnDataReceived; + *Information = sizeof(BOOL); + } + break; + + case PosPropertyId::BarcodeScannerIsDecodeDataEnabled: + // BOOL result, true when the app has called SetProperty(BarcodeScannerIsDecodeDataEnabled) = TRUE + { + BOOL isDecodeDataEnabled = TRUE; // Get this value from device context or by querying the device + *((BOOL*)outputBuffer) = isDecodeDataEnabled; + *Information = sizeof(BOOL); + } + break; + + case PosPropertyId::BarcodeScannerCapabilities: + { + // PosBarcodeScannerCapabilitiesType2 result + // These capabilities are likely hard-coded for the specific device + PosBarcodeScannerCapabilitiesType2 capabilities; + capabilities.PosBarcodeScannerCapabilities.IsImagePreviewSupported = TRUE; + capabilities.PosBarcodeScannerCapabilities.IsStatisticsReportingSupported = TRUE; + capabilities.PosBarcodeScannerCapabilities.IsStatisticsUpdatingSupported = TRUE; + capabilities.PosBarcodeScannerCapabilities.PowerReportingType = DriverUnifiedPosPowerReportingType::Standard; + capabilities.IsSoftwareTriggerSupported = TRUE; + size_t bytesToCopy = sizeof(PosBarcodeScannerCapabilitiesType2); + if (outputBufferLength < bytesToCopy) + { + bytesToCopy = outputBufferLength; + status = STATUS_BUFFER_OVERFLOW; + } + memcpy(outputBuffer, &capabilities, bytesToCopy); + *Information = bytesToCopy; + } + break; + + case PosPropertyId::BarcodeScannerSupportedSymbologies: + { + // Returns a length-prefixed array of symbologies + BarcodeSymbology exampleSymbologies[] = { BarcodeSymbology::Upca, BarcodeSymbology::Ean13, BarcodeSymbology::Pdf417 }; + UINT32* outputData = (UINT32*)outputBuffer; + size_t copiedDataLength = 0; + if (outputBufferLength >= sizeof(UINT32)) + { + *outputData = ARRAYSIZE(exampleSymbologies); + copiedDataLength += sizeof(UINT32); + } + for (UINT32 index = 0; index < ARRAYSIZE(exampleSymbologies); ++index) + { + if (copiedDataLength + sizeof(UINT32) > outputBufferLength) + { + break; + } + // Ensure the output array elements are stored as UINT32s + outputData[index + 1] = (UINT32)exampleSymbologies[index]; + copiedDataLength += sizeof(UINT32); + } + *Information = copiedDataLength; + } + break; + + case PosPropertyId::BarcodeScannerSupportedProfiles: + { + // Profiles are sets of settings that can be applied together. This property returns an encoded array of profile names + LPCWSTR exampleProfiles[] = { L"Profile1", L"Profile2" }; + + if (outputBufferLength >= sizeof(PosProfileType)) + { + PosProfileType* header = (PosProfileType*)outputBuffer; + header->BufferSize = sizeof(PosProfileType); + header->ProfileCount = 0; + + *Information = header->BufferSize; + + for (UINT32 profileIndex = 0; profileIndex < ARRAYSIZE(exampleProfiles); ++profileIndex) + { + UINT32 stringLen; + if (!NT_SUCCESS(status = RtlSizeTToUInt32(wcslen(exampleProfiles[profileIndex]), &stringLen))) + { + break; + } + + UINT32 stringLenInBytes = stringLen*sizeof(WCHAR); + + // If there's enough room in the output buffer, we can use the previous length as the starting point to copy the profile string + UINT32 previousLength = header->BufferSize; + + // each string has it's own header (that just contains the string length in bytes) + header->BufferSize += sizeof(PosStringType); + header->BufferSize += stringLenInBytes; + ++(header->ProfileCount); + + if (outputBufferLength >= header->BufferSize) + { + // There's enough room for this string + PosStringType* stringHeader = (PosStringType*)((BYTE*)outputBuffer + previousLength); + stringHeader->DataLengthInBytes = stringLenInBytes; + + WCHAR* stringStart = (WCHAR*)((BYTE*)outputBuffer + previousLength + sizeof(PosStringType)); + + // memcpy because we don't null terminate these strings + memcpy(stringStart, exampleProfiles[profileIndex], stringLenInBytes); + + *Information = header->BufferSize; + } + } + + if (header->BufferSize > outputBufferLength) + { + status = STATUS_BUFFER_OVERFLOW; + } + } + else + { + status = STATUS_BUFFER_TOO_SMALL; + } + } + + break; + default: + // no other readable properties for barcode scanner + return STATUS_INVALID_PARAMETER; + } + + return STATUS_SUCCESS; +} + +/* +** Driver TODO: Add code to handle various set-property cases. +** +** Implement this function to handle property set requests. +*/ +NTSTATUS ProcessSetPropertyRequest(_In_ WDFREQUEST Request, _In_ size_t InputBufferLength, _Inout_ ULONG_PTR* Information) +{ + if (Information == nullptr || Request == nullptr || InputBufferLength < sizeof(PosPropertyId)) + { + return STATUS_INVALID_PARAMETER; + } + + // POS properties are identified by the property ID that's transmitted in the input buffer. + // The data that is used to set the property immediately follows the property ID, so the input buffer must be big enough to contain both. + PosPropertyId* propertyId; + NTSTATUS status = WdfRequestRetrieveInputBuffer( + Request, + sizeof(PosPropertyId), + reinterpret_cast(&propertyId), + nullptr + ); + + if (!NT_SUCCESS(status)) + { + return status; + } + + size_t argumentLength = InputBufferLength - sizeof(PosPropertyId); + void* argumentData = (void*)(propertyId + 1); + + // Handle this set of writable properties + switch (*propertyId) + { + case PosPropertyId::IsEnabled: + // BOOL value + if (argumentLength >= sizeof(BOOL)) + { + // The driver should use this value to ensure the device is ready to take data. + // The value may also need to be cached in a device context object so that it can be returned in GetProperty(IsEnabled) + BOOL isEnabled = *((BOOL*)argumentData); + UNREFERENCED_PARAMETER(isEnabled); + } + else + { + status = STATUS_BUFFER_TOO_SMALL; + } + break; + + case PosPropertyId::IsDisabledOnDataReceived: + // BOOL value + if (argumentLength >= sizeof(BOOL)) + { + // Typically this value will get cached in the device context so that, when + // a barcode scan occurs, the driver can disable the device. + BOOL isDisabledOnDataReceived = *((BOOL*)argumentData); + UNREFERENCED_PARAMETER(isDisabledOnDataReceived); + } + else + { + status = STATUS_BUFFER_TOO_SMALL; + } + break; + + case PosPropertyId::BarcodeScannerIsDecodeDataEnabled: + // BOOL value + if (argumentLength >= sizeof(BOOL)) + { + // Typically this value will get cached in the device context so that, when + // a barcode scan occurs, the driver can decode the raw data to get the scan data label. + BOOL isDecodeDataEnabled = *((BOOL*)argumentData); + UNREFERENCED_PARAMETER(isDecodeDataEnabled); + } + else + { + status = STATUS_BUFFER_TOO_SMALL; + } + break; + + case PosPropertyId::BarcodeScannerActiveSymbologies: + // UINT32 array with length prefix value + if (argumentLength >= sizeof(UINT32)) + { + UINT32 symbologyCount = *((UINT32*)argumentData); + + // knowing the number of symbology values in the input buffer, we know how big the buffer should be. + // Add 1 to count to include the count value itself. + UINT32 requiredArgumentLength = (symbologyCount + 1) * sizeof(UINT32); + + if (argumentLength >= requiredArgumentLength) + { + // The driver can copy this array into the device context, so that it can look up the symbology + // of an incoming scan to see whether its valid to pass on to the application. + // Alternatively, if the hardware supports restricting the data to specific symbologies, the + // driver can do that now. + for (UINT32 index = 0; index < symbologyCount; ++index) + { + BarcodeSymbology symbologyValue = (BarcodeSymbology)((UINT32*)argumentData)[index + 1]; + UNREFERENCED_PARAMETER(symbologyValue); + } + } + else + { + status = STATUS_BUFFER_TOO_SMALL; + } + } + else + { + status = STATUS_BUFFER_TOO_SMALL; + } + break; + + case PosPropertyId::BarcodeScannerActiveProfile: + // PosStringType value (length prefixed wide string) + if (argumentLength >= sizeof(PosStringType)) + { + PosStringType* header = (PosStringType*)argumentData; + if (argumentLength >= (sizeof(PosStringType)+header->DataLengthInBytes)) + { + WCHAR* profileName = (WCHAR*)(header + 1); + size_t profileLength = header->DataLengthInBytes / sizeof(WCHAR); + + // Determine the profile requested and apply the settings if found + if (!wcsncmp(L"Profile1", profileName, profileLength)) + { + // For example, apply Profile1 settings + } + } + } + break; + + default: + // no other writable properties for barcode scanner + return STATUS_INVALID_PARAMETER; + } + + return STATUS_SUCCESS; +} + +/* +** Driver TODO: Replace the data in the ProcessRetrieveStatisticsRequest with your own statistics data +** +** Implement this function to handle retrieve statistics requests. +*/ +NTSTATUS ProcessRetrieveStatisticsRequest(_In_ WDFREQUEST Request, _In_ size_t OutputBufferLength, _Inout_ ULONG_PTR* Information) +{ + if (Information == nullptr || Request == nullptr) + { + return STATUS_INVALID_PARAMETER; + } + + struct + { + PosStatisticsHeader Header; + PosValueStatisticsEntry Entries[1]; + } StatisticsData; + + StatisticsData.Header.DataLength = sizeof(StatisticsData); + wcscpy_s(StatisticsData.Header.DeviceInformation.DeviceCategory, L"Scanner"); + wcscpy_s(StatisticsData.Header.DeviceInformation.FirmwareRevision, L""); + wcscpy_s(StatisticsData.Header.DeviceInformation.InstallationDate, L""); + wcscpy_s(StatisticsData.Header.DeviceInformation.Interface, L""); + wcscpy_s(StatisticsData.Header.DeviceInformation.ManufactureDate, L""); + wcscpy_s(StatisticsData.Header.DeviceInformation.ManufacturerName, L""); + wcscpy_s(StatisticsData.Header.DeviceInformation.MechanicalRevision, L""); + wcscpy_s(StatisticsData.Header.DeviceInformation.ModelName, L""); + wcscpy_s(StatisticsData.Header.DeviceInformation.SerialNumber, L""); + wcscpy_s(StatisticsData.Header.DeviceInformation.UnifiedPOSVersion, L"1.14"); + StatisticsData.Header.EntryCount = 1; + wcscpy_s(StatisticsData.Entries[0].EntryName, L""); + StatisticsData.Entries[0].Value = (LONG)1; + + // This IOCTL is called twice by the Windows.Devices.PointOfService APIs + // The first time will just retrieve the header to determine how big the buffer needs to be. + PVOID outputBuffer; + NTSTATUS status = WdfRequestRetrieveOutputBuffer( + Request, + sizeof(PosStatisticsHeader), + &outputBuffer, + nullptr + ); + + if (!NT_SUCCESS(status)) + { + *Information = sizeof(StatisticsData); + return status; + } + + if (OutputBufferLength == sizeof(PosStatisticsHeader)) + { + memcpy(outputBuffer, &(StatisticsData.Header), sizeof(PosStatisticsHeader)); + *Information = sizeof(PosStatisticsHeader); + } + else if (OutputBufferLength < sizeof(StatisticsData)) + { + *Information = sizeof(StatisticsData); + status = STATUS_BUFFER_TOO_SMALL; + } + else + { + memcpy(outputBuffer, &StatisticsData, sizeof(StatisticsData)); + *Information = sizeof(StatisticsData); + } + + return status; +} + +/* +** Driver TODO: loop over statisticsEntry[0]...statisticsEntry[inputBuffer->EntryCount - 1] and reset each statistics value named +** +** Implement this function to handle statistics reset requests. +*/ +NTSTATUS ProcessResetStatisticsRequest(_In_ WDFREQUEST Request) +{ + if (Request == nullptr) + { + return STATUS_INVALID_PARAMETER; + } + + // The input buffer must be PosStatisticsHeader followed by one or more PosValueStatisticsEntry (where the value is ignored, just the name is used to + // reset the statistics value). + PosStatisticsHeader* inputBuffer; + size_t totalLength; + NTSTATUS status = WdfRequestRetrieveInputBuffer( + Request, + sizeof(PosStatisticsHeader), + (PVOID*)&inputBuffer, + &totalLength); + + if (!NT_SUCCESS(status)) + { + return status; + } + + if (inputBuffer->DataLength > totalLength) + { + return STATUS_BUFFER_TOO_SMALL; + } + + size_t entryLength = inputBuffer->DataLength - sizeof(PosStatisticsHeader); + if ( + entryLength % sizeof(PosValueStatisticsEntry) || + (entryLength / sizeof(PosValueStatisticsEntry)) != inputBuffer->EntryCount || + inputBuffer->EntryCount == 0 + ) + { + return STATUS_INVALID_PARAMETER; + } + + PosValueStatisticsEntry* statisticsEntry = (PosValueStatisticsEntry*) (inputBuffer + 1); + + for (UINT32 index = 0; index < inputBuffer->EntryCount; ++index) + { + // reset this value: + statisticsEntry[index].EntryName; + } + + return STATUS_SUCCESS; +} + +/* +** Driver TODO: loop over statisticsEntry[0]...statisticsEntry[inputBuffer->EntryCount - 1] and update each statistics value named +** +** Implement this function to handle statistics update requests. +*/ +NTSTATUS ProcessUpdateStatisticsRequest(_In_ WDFREQUEST Request) +{ + if (Request == nullptr) + { + return STATUS_INVALID_PARAMETER; + } + + // The input buffer must be PosStatisticsHeader followed by one or more PosValueStatisticsEntry + PosStatisticsHeader* inputBuffer; + size_t totalLength; + NTSTATUS status = WdfRequestRetrieveInputBuffer( + Request, + sizeof(PosStatisticsHeader), + (PVOID*)&inputBuffer, + &totalLength); + + if (!NT_SUCCESS(status)) + { + return status; + } + + if (inputBuffer->DataLength > totalLength) + { + return STATUS_BUFFER_TOO_SMALL; + } + + size_t entryLength = inputBuffer->DataLength - sizeof(PosStatisticsHeader); + if ( + entryLength % sizeof(PosValueStatisticsEntry) || + (entryLength / sizeof(PosValueStatisticsEntry)) != inputBuffer->EntryCount || + inputBuffer->EntryCount == 0 + ) + { + return STATUS_INVALID_PARAMETER; + } + + PosValueStatisticsEntry* statisticsEntry = (PosValueStatisticsEntry*)(inputBuffer + 1); + + for (UINT32 index = 0; index < inputBuffer->EntryCount; ++index) + { + // update the statistics entry: + statisticsEntry[index].EntryName; + // with the value: + statisticsEntry[index].Value; + } + + return STATUS_SUCCESS; +} + +/* +** Driver TODO: Add code to ProcessCheckHealthRequest to handle different health check cases. The result should be a localized string that is returned to the user in the output buffer of the IOCTL. +** +** Implement this function to handle health check requests. +*/ +NTSTATUS ProcessCheckHealthRequest(_In_ WDFREQUEST Request, _Inout_ ULONG_PTR* Information) +{ + if (Information == nullptr || Request == nullptr) + { + return STATUS_INVALID_PARAMETER; + } + + DriverUnifiedPosHealthCheckLevel* level; + + NTSTATUS status = WdfRequestRetrieveInputBuffer( + Request, + sizeof(DriverUnifiedPosHealthCheckLevel), + (PVOID*)&level, + nullptr); + + if (!NT_SUCCESS(status)) + { + return status; + } + + PosStringType* outputBuffer; + size_t outputBufferLength; + status = WdfRequestRetrieveOutputBuffer( + Request, + sizeof(PosStringType), + (void**)(&outputBuffer), + &outputBufferLength + ); + + if (!NT_SUCCESS(status)) + { + return status; + } + + switch (*level) + { + case DriverUnifiedPosHealthCheckLevel::POSInternal: + case DriverUnifiedPosHealthCheckLevel::External: + case DriverUnifiedPosHealthCheckLevel::Interactive: + { + // Handle the specific health check level, depending on the applicability to your device. + // Return the result as a string that the user can use to determine whether the device is + // operational or needs attention. + LPCWSTR result = L"OK"; + size_t lengthInBytes = wcslen(result) * sizeof(WCHAR); + status = RtlSizeTToUInt32(lengthInBytes, &(outputBuffer->DataLengthInBytes)); + if (NT_SUCCESS(status)) + { + *Information = sizeof(PosStringType); + if (outputBufferLength >= sizeof(PosStringType)+outputBuffer->DataLengthInBytes) + { + void* outputStringStart = (void*)(outputBuffer + 1); + memcpy(outputStringStart, result, outputBuffer->DataLengthInBytes); + *Information += outputBuffer->DataLengthInBytes; + } + else + { + status = STATUS_BUFFER_OVERFLOW; + } + } + } + break; + + default: + return STATUS_INVALID_PARAMETER; + } + + return STATUS_SUCCESS; +} + +/* +** Driver TODO: +** +** Implement this function to handle the initial handshake IOCTL for Windows.Devices.PointOfService API <-> Driver communication. +** This sample will likely work for most cases. +*/ +NTSTATUS ProcessGetDeviceBasicsRequest(_In_ WDFREQUEST Request, _Inout_ ULONG_PTR* Information) +{ + if (Information == nullptr || Request == nullptr) + { + return STATUS_INVALID_PARAMETER; + } + + UINT32* runtimeVersion; + PosDeviceBasicsType* outputData; + + NTSTATUS status = WdfRequestRetrieveInputBuffer( + Request, + sizeof(UINT32), + (PVOID*)&runtimeVersion, + nullptr); + + if (!NT_SUCCESS(status)) + { + return status; + } + + if (*runtimeVersion < POS_VERSION_1_2) + { + // This runtime was for earlier versions of windows and doesn't support software trigger + } + + status = WdfRequestRetrieveOutputBuffer( + Request, + sizeof(PosDeviceBasicsType), + (PVOID*)&outputData, + nullptr); + + if (!NT_SUCCESS(status)) + { + return status; + } + + // Tell the runtime what version of the POS IOCTL interface this driver supports so that + // it won't send IOCTLs that the driver doesn't support. + outputData->Version = POS_DRIVER_VERSION; + // This is a barcode scanner driver + outputData->DeviceType = PosDeviceType::PosDeviceType_BarcodeScanner; + // This value will be used to set the initial ReadFile buffer size. A small size that is + // likely to cover most of the data events is suggested. The runtime will grow the ReadFile + // buffer size as needed. + outputData->RecommendedBufferSize = 128; + + *Information = sizeof(PosDeviceBasicsType); + + return STATUS_SUCCESS; +} diff --git a/pos/drivers/barcodescanner/Ioctl.h b/pos/drivers/barcodescanner/Ioctl.h new file mode 100644 index 000000000..55f629f68 --- /dev/null +++ b/pos/drivers/barcodescanner/Ioctl.h @@ -0,0 +1,3 @@ +#pragma once + +EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL EvtIoDeviceControl; diff --git a/pos/drivers/barcodescanner/PosEvents.cpp b/pos/drivers/barcodescanner/PosEvents.cpp new file mode 100644 index 000000000..1d2443bb7 --- /dev/null +++ b/pos/drivers/barcodescanner/PosEvents.cpp @@ -0,0 +1,97 @@ +#include + +/* +** Driver TODO: Add code to EvtDeviceOwnershipChange to reset the device state to a default. +** +** PosCx calls this callback to signal that the ownership of the device has transitioned from one file handle +** to another. When this happens, app developers expect that the settings for the device are restored to a +** "default" state, so the driver should do what is necessary to satisfy that expectation here. +** +*/ +VOID EvtDeviceOwnershipChange(_In_ WDFDEVICE Device, _In_opt_ WDFFILEOBJECT OldOwnerFileObj, _In_opt_ WDFFILEOBJECT NewOwnerFileObj) +{ + UNREFERENCED_PARAMETER(Device); + UNREFERENCED_PARAMETER(OldOwnerFileObj); + UNREFERENCED_PARAMETER(NewOwnerFileObj); + + // This function signals that ownership has transitioned from one file handle to another (typically from one app to another). + // As a result, the driver is expected to reset all settings to a default state. +} + +/* +** Driver TODO: +** +** This part of the sample demonstrates how the driver will return data to the runtime. How this method would be called depends +** on the driver implementation. +*/ +VOID EvtOnBarcodeScanDataRetrieved(_In_ WDFDEVICE Device) +{ + // Events that the runtime can handle for a barcode scanner device are: + // PosEventType::BarcodeScannerDataReceived -- the standard event with barcode scan data + // PosEventType::BarcodeScannerErrorOccurred -- an event that should be sent if an error occured while scanning data + // PosEventType::BarcodeScannerImagePreviewReceived -- an event that sends a complete .bmp image to the runtime for imagers + // PosEventType::BarcodeScannerTriggerPressed -- an event indicating that the trigger on the device has been pushed and the device is looking for data + // PosEventType::BarcodeScannerTriggerReleased -- an event indicating that the trigger on the device has been released and the device is no longer looking for data + // PosEventType::StatusUpdated -- an event to indicate changes to power state + // + // Additionally, the following event is sent to the runtime, but is handled entirely by PosCx + // PosEventType::ReleaseDeviceRequested + + // The following shows an example of sending barcode scan data + WCHAR exampleData[] = L"]0A12345"; + WCHAR exampleDataLabel[] = L"12345"; + + // total size is the struct plus the size of the two strings (minus the null terminators which aren't transmitted). + size_t totalSize = sizeof(PosBarcodeScannerDataReceivedEventData) + sizeof(exampleData) - sizeof(WCHAR) + sizeof(exampleDataLabel) - sizeof(WCHAR); + + + // PosCx supports two methods of pending the event data -- one where it takes the WDFMEMORY for the event, and the other where it creates it + // The subtle difference between the two is that the one that takes the WDFMEMORY must have the event header information already added. + // + // Since that's the case with the PosBarcodeScannerDataReceivedEventData data structure, barcode scanner drivers should create the WDFMEMORY objects + // and pass them to PosCx. + + WDF_OBJECT_ATTRIBUTES Attributes; + WDF_OBJECT_ATTRIBUTES_INIT(&Attributes); + Attributes.ParentObject = Device; + + BYTE* eventData = nullptr; + WDFMEMORY eventMemory = NULL; + NTSTATUS status = WdfMemoryCreate( + &Attributes, + NonPagedPoolNx, + (ULONG)'eSOP', + totalSize, + &eventMemory, + (PVOID*)&eventData + ); + + if (!NT_SUCCESS(status)) + { + // handle out of memory error + return; + } + + PosBarcodeScannerDataReceivedEventData* eventHeader = (PosBarcodeScannerDataReceivedEventData*)eventData; + eventHeader->Header.EventType = PosEventType::BarcodeScannerDataReceived; + eventHeader->Header.DataLength = (UINT32)totalSize; + eventHeader->DataType = BarcodeSymbology::Ean13; + + // These use memcpy because wcscpy would append the null terminator and the event data doesn't use it + BYTE* eventScanData = eventData + sizeof(PosBarcodeScannerDataReceivedEventData); + size_t exampleDataByteCount = sizeof(exampleData) - sizeof(WCHAR); + memcpy(eventScanData, exampleData, exampleDataByteCount); + BYTE* eventLabelData = eventScanData + exampleDataByteCount; + size_t exampleLabelByteCount = sizeof(exampleDataLabel) - sizeof(WCHAR); + memcpy(eventLabelData, exampleDataLabel, exampleLabelByteCount); + + + // This call actually pends the data to be send to the WinRT APIs. + status = PosCxPutPendingEventMemory(Device, SCANNER_INTERFACE_TAG, eventMemory, POS_CX_EVENT_ATTR_DATA); + + if (!NT_SUCCESS(status)) + { + // This should only happen in rare cases such as out of memory (or that the device or interface tag isn't found). The driver should most likely drop the event. + WdfObjectDelete(eventMemory); + } +} \ No newline at end of file diff --git a/pos/drivers/barcodescanner/PosEvents.h b/pos/drivers/barcodescanner/PosEvents.h new file mode 100644 index 000000000..66ff095ad --- /dev/null +++ b/pos/drivers/barcodescanner/PosEvents.h @@ -0,0 +1,3 @@ +#pragma once + +EVT_POS_CX_DEVICE_OWNERSHIP_CHANGE EvtDeviceOwnershipChange; \ No newline at end of file diff --git a/pos/drivers/barcodescanner/README.md b/pos/drivers/barcodescanner/README.md new file mode 100644 index 000000000..8df934b33 --- /dev/null +++ b/pos/drivers/barcodescanner/README.md @@ -0,0 +1,7 @@ +Barcode Scanner Driver Sample +==================================== +This sample serves as a template for creating a new Barcode Scanner driver. + +This sample uses UMDF 2.0 and enables basic functionality such as claiming and enabling the device for exclusive access. + +It serves as an example of how to include the libraries necessary to develop a PointOfService driver. Once a driver is developed using this template it can be compiled for, deployed, and used on x86, amd64, and ARM platforms. \ No newline at end of file diff --git a/pos/drivers/barcodescanner/SampleBarcodeScannerDrv.inf b/pos/drivers/barcodescanner/SampleBarcodeScannerDrv.inf new file mode 100644 index 000000000..9d32732fa --- /dev/null +++ b/pos/drivers/barcodescanner/SampleBarcodeScannerDrv.inf @@ -0,0 +1,78 @@ +; +; SampleBarcodeScannerDrv.inf +; + +[Version] +Signature="$Windows NT$" +Class=Sample ; +ClassGuid={70DF6E9F-68AA-4E29-AF0C-5AE9DB219214} ; +Provider=Standard,NT$ARCH$ +CatalogFile=SampleBarcodeScannerDrv.cat +DriverVer=06/25/2015,14.29.18.671 + +[Manufacturer] +%ManufacturerName%=Standard,NT$ARCH$ + +[Standard.NT$ARCH$] +%DeviceName%=MyDevice_Install, Root\SampleBarcodeScannerDrv ; + + +[ClassInstall32] +AddReg=SampleClass_RegistryAdd + +[SampleClass_RegistryAdd] +HKR,,,,%ClassName% +HKR,,Icon,,"-10" + +[SourceDisksFiles] +SampleBarcodeScannerDrv.dll=1 + +[SourceDisksNames] +1 = %DiskName% + +; =================== UMDF Device ================================== + +[MyDevice_Install.NT] +CopyFiles=UMDriverCopy + +[MyDevice_Install.NT.hw] + +[MyDevice_Install.NT.Services] +AddService=WUDFRd,0x000001fa,WUDFRD_ServiceInstall + +[MyDevice_Install.NT.CoInstallers] +AddReg=CoInstallers_AddReg + +[MyDevice_Install.NT.Wdf] +UmdfService=SampleBarcodeScannerDrv,SampleBarcodeScannerDrv_Install +UmdfServiceOrder=SampleBarcodeScannerDrv + +[SampleBarcodeScannerDrv_Install] +UmdfLibraryVersion=2.15.0 +ServiceBinary=%12%\UMDF\SampleBarcodeScannerDrv.dll +UmdfExtensions=PosCx0102 + +[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 drivers\umdf + +[UMDriverCopy] +SampleBarcodeScannerDrv.dll + +; =================== Generic ================================== + +[Strings] +ManufacturerName="" ; +ClassName="Samples" ; +DiskName = "SampleBarcodeScannerDrv Installation Disk" +WudfRdDisplayName="Windows Driver Foundation - User-mode Driver Framework Reflector" +DeviceName="SampleBarcodeScannerDrv Device" diff --git a/pos/drivers/barcodescanner/SampleBarcodeScannerDrv.vcxproj b/pos/drivers/barcodescanner/SampleBarcodeScannerDrv.vcxproj new file mode 100644 index 000000000..cf354ec3c --- /dev/null +++ b/pos/drivers/barcodescanner/SampleBarcodeScannerDrv.vcxproj @@ -0,0 +1,252 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {E08242CA-297F-4C12-A99F-EC78245117F5} + $(MSBuildProjectName) + 2 + Debug + Win32 + {294BDD9E-A272-4E3A-804F-5C117FC85E08} + + + + 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) + + + + + + + + + + + + + + + + SampleBarcodeScannerDrv + + + SampleBarcodeScannerDrv + + + SampleBarcodeScannerDrv + + + SampleBarcodeScannerDrv + + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + Sync + true + Level4 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(SDK_INC_PATH)\pos\1.1;..\inc + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(SDK_INC_PATH)\pos\1.1;..\inc + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(SDK_INC_PATH)\pos\1.1;..\inc + + + + + Sync + true + Level4 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(SDK_INC_PATH)\pos\1.1;..\inc + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(SDK_INC_PATH)\pos\1.1;..\inc + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(SDK_INC_PATH)\pos\1.1;..\inc + + + + + Sync + true + Level4 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(SDK_INC_PATH)\pos\1.1;..\inc + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(SDK_INC_PATH)\pos\1.1;..\inc + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(SDK_INC_PATH)\pos\1.1;..\inc + + + + + Sync + true + Level4 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(SDK_INC_PATH)\pos\1.1;..\inc + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(SDK_INC_PATH)\pos\1.1;..\inc + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(SDK_INC_PATH)\pos\1.1;..\inc + + + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\pos\1.1\poscxstub.lib + exports.def + + + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\pos\1.1\poscxstub.lib + exports.def + + + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\pos\1.1\poscxstub.lib + exports.def + + + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\pos\1.1\poscxstub.lib + exports.def + + + + + ;%(AdditionalIncludeDirectories) + pch.h + Use + $(IntDir)\pch.h.pch + + + ;%(AdditionalIncludeDirectories) + pch.h + Use + $(IntDir)\pch.h.pch + + + ;%(AdditionalIncludeDirectories) + pch.h + Use + $(IntDir)\pch.h.pch + + + ;%(AdditionalIncludeDirectories) + pch.h + Use + $(IntDir)\pch.h.pch + + + ;%(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/pos/drivers/barcodescanner/SampleBarcodeScannerDrv.vcxproj.Filters b/pos/drivers/barcodescanner/SampleBarcodeScannerDrv.vcxproj.Filters new file mode 100644 index 000000000..4268199b5 --- /dev/null +++ b/pos/drivers/barcodescanner/SampleBarcodeScannerDrv.vcxproj.Filters @@ -0,0 +1,47 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {47F49773-F319-4591-B32D-BA38E9E75F22} + + + h;hpp;hxx;hm;inl;inc;xsd + {74AA7E37-6BDE-408A-AF19-36CBA1A80849} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {58E2BE2E-4AAA-48F9-95E8-65E24E014E15} + + + inf;inv;inx;mof;mc; + {E145DF39-11C3-4472-BDA5-35448FA78D69} + + + + + 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/pos/drivers/barcodescanner/exports.def b/pos/drivers/barcodescanner/exports.def new file mode 100644 index 000000000..a391d3b8f --- /dev/null +++ b/pos/drivers/barcodescanner/exports.def @@ -0,0 +1,2 @@ +LIBRARY "SampleBarcodeScannerDrv.dll" + diff --git a/pos/drivers/barcodescanner/pch.h b/pos/drivers/barcodescanner/pch.h new file mode 100644 index 000000000..b8afbb38a --- /dev/null +++ b/pos/drivers/barcodescanner/pch.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include +#include +#include + +#include "PointOfServiceCommonTypes.h" +#include "PointOfServiceDriverInterface.h" + +#include "PosCx.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif +DRIVER_INITIALIZE DriverEntry; +#ifdef __cplusplus +} +#endif + +#define SCANNER_INTERFACE_TAG ((ULONG) '0SCB') diff --git a/pos/drivers/barcodescanner/pchsrc.cpp b/pos/drivers/barcodescanner/pchsrc.cpp new file mode 100644 index 000000000..17305716a --- /dev/null +++ b/pos/drivers/barcodescanner/pchsrc.cpp @@ -0,0 +1 @@ +#include "pch.h" \ No newline at end of file diff --git a/print/SampleOpenXPS/BlankProjSrc/BlankProject.vcxproj b/print/SampleOpenXPS/BlankProjSrc/BlankProject.vcxproj index 179b5e407..2f495e7eb 100644 --- a/print/SampleOpenXPS/BlankProjSrc/BlankProject.vcxproj +++ b/print/SampleOpenXPS/BlankProjSrc/BlankProject.vcxproj @@ -19,12 +19,12 @@ - {51F80EDA-1959-4685-91F9-C820EB0F9550} + {A6506DFF-09F0-4A51-924B-029B10851715} $(MSBuildProjectName) false Debug Win32 - {3411A7DF-973F-40B3-A8C5-80134DE29E92} + {D32AB5B6-83E9-4E4B-B7D1-C7B0958ABA30} @@ -127,7 +127,6 @@ - diff --git a/print/SampleOpenXPS/BlankProjSrc/BlankProject.vcxproj.Filters b/print/SampleOpenXPS/BlankProjSrc/BlankProject.vcxproj.Filters index 91b3d644f..7979572f8 100644 --- a/print/SampleOpenXPS/BlankProjSrc/BlankProject.vcxproj.Filters +++ b/print/SampleOpenXPS/BlankProjSrc/BlankProject.vcxproj.Filters @@ -3,15 +3,15 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {6352E8DC-DB14-463B-8201-DBB7F41937D7} + {B0DC7468-3503-4ED6-ABAE-00AA29B5F69F} h;hpp;hxx;hm;inl;inc;xsd - {6CBD6890-5328-4EFE-B850-7A28B1683741} + {8FCA6549-A9E5-4659-A769-9E49F69E54FE} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {6F8E2B21-C7AA-4015-931A-B23BB2400C33} + {81ADEDFC-A044-4244-ACF2-864D2B31CAAE} \ No newline at end of file diff --git a/print/SampleOpenXPS/SampleOpenXPS.sln b/print/SampleOpenXPS/SampleOpenXPS.sln index a11afc003..21ad52040 100644 --- a/print/SampleOpenXPS/SampleOpenXPS.sln +++ b/print/SampleOpenXPS/SampleOpenXPS.sln @@ -3,7 +3,7 @@ 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", "{51F80EDA-1959-4685-91F9-C820EB0F9550}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BlankProject", "BlankProjSrc\BlankProject.vcxproj", "{A6506DFF-09F0-4A51-924B-029B10851715}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {51F80EDA-1959-4685-91F9-C820EB0F9550}.Debug|Win32.ActiveCfg = Debug|Win32 - {51F80EDA-1959-4685-91F9-C820EB0F9550}.Debug|Win32.Build.0 = Debug|Win32 - {51F80EDA-1959-4685-91F9-C820EB0F9550}.Release|Win32.ActiveCfg = Release|Win32 - {51F80EDA-1959-4685-91F9-C820EB0F9550}.Release|Win32.Build.0 = Release|Win32 - {51F80EDA-1959-4685-91F9-C820EB0F9550}.Debug|x64.ActiveCfg = Debug|x64 - {51F80EDA-1959-4685-91F9-C820EB0F9550}.Debug|x64.Build.0 = Debug|x64 - {51F80EDA-1959-4685-91F9-C820EB0F9550}.Release|x64.ActiveCfg = Release|x64 - {51F80EDA-1959-4685-91F9-C820EB0F9550}.Release|x64.Build.0 = Release|x64 + {A6506DFF-09F0-4A51-924B-029B10851715}.Debug|Win32.ActiveCfg = Debug|Win32 + {A6506DFF-09F0-4A51-924B-029B10851715}.Debug|Win32.Build.0 = Debug|Win32 + {A6506DFF-09F0-4A51-924B-029B10851715}.Release|Win32.ActiveCfg = Release|Win32 + {A6506DFF-09F0-4A51-924B-029B10851715}.Release|Win32.Build.0 = Release|Win32 + {A6506DFF-09F0-4A51-924B-029B10851715}.Debug|x64.ActiveCfg = Debug|x64 + {A6506DFF-09F0-4A51-924B-029B10851715}.Debug|x64.Build.0 = Debug|x64 + {A6506DFF-09F0-4A51-924B-029B10851715}.Release|x64.ActiveCfg = Release|x64 + {A6506DFF-09F0-4A51-924B-029B10851715}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/print/SampleXPS/BlankProjSrc/BlankProject.vcxproj b/print/SampleXPS/BlankProjSrc/BlankProject.vcxproj index 6e631d9eb..806cb3783 100644 --- a/print/SampleXPS/BlankProjSrc/BlankProject.vcxproj +++ b/print/SampleXPS/BlankProjSrc/BlankProject.vcxproj @@ -19,12 +19,12 @@ - {374C5441-19FB-43F0-9F91-70B2255B56F9} + {577BA11F-E625-430A-B8AB-009A64EE1D20} $(MSBuildProjectName) false Debug Win32 - {BFA1B08D-DEAD-42D3-905F-2DF39E5AE1D1} + {89701A44-B6B5-4C91-A19B-7269514D4027} @@ -127,7 +127,6 @@ - diff --git a/print/SampleXPS/BlankProjSrc/BlankProject.vcxproj.Filters b/print/SampleXPS/BlankProjSrc/BlankProject.vcxproj.Filters index 42201c912..13da98cd3 100644 --- a/print/SampleXPS/BlankProjSrc/BlankProject.vcxproj.Filters +++ b/print/SampleXPS/BlankProjSrc/BlankProject.vcxproj.Filters @@ -3,15 +3,15 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {857EAB9E-D7F0-451B-ABA4-71DFA74756A3} + {825864FD-D8B8-4188-B424-C8035CBD9F9F} h;hpp;hxx;hm;inl;inc;xsd - {D187F73D-A17E-4192-9F70-A8B25DB85600} + {EEB5B623-0CEF-42CE-A428-431A32EC5F70} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {42B46C1A-D46A-4720-9CB2-7C73C57409B2} + {51BCF16C-FE65-44F0-BD4C-CEF591DB4327} \ No newline at end of file diff --git a/print/SampleXPS/SampleXPS.sln b/print/SampleXPS/SampleXPS.sln index 536becc72..528ebb085 100644 --- a/print/SampleXPS/SampleXPS.sln +++ b/print/SampleXPS/SampleXPS.sln @@ -3,7 +3,7 @@ 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", "{374C5441-19FB-43F0-9F91-70B2255B56F9}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BlankProject", "BlankProjSrc\BlankProject.vcxproj", "{577BA11F-E625-430A-B8AB-009A64EE1D20}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {374C5441-19FB-43F0-9F91-70B2255B56F9}.Debug|Win32.ActiveCfg = Debug|Win32 - {374C5441-19FB-43F0-9F91-70B2255B56F9}.Debug|Win32.Build.0 = Debug|Win32 - {374C5441-19FB-43F0-9F91-70B2255B56F9}.Release|Win32.ActiveCfg = Release|Win32 - {374C5441-19FB-43F0-9F91-70B2255B56F9}.Release|Win32.Build.0 = Release|Win32 - {374C5441-19FB-43F0-9F91-70B2255B56F9}.Debug|x64.ActiveCfg = Debug|x64 - {374C5441-19FB-43F0-9F91-70B2255B56F9}.Debug|x64.Build.0 = Debug|x64 - {374C5441-19FB-43F0-9F91-70B2255B56F9}.Release|x64.ActiveCfg = Release|x64 - {374C5441-19FB-43F0-9F91-70B2255B56F9}.Release|x64.Build.0 = Release|x64 + {577BA11F-E625-430A-B8AB-009A64EE1D20}.Debug|Win32.ActiveCfg = Debug|Win32 + {577BA11F-E625-430A-B8AB-009A64EE1D20}.Debug|Win32.Build.0 = Debug|Win32 + {577BA11F-E625-430A-B8AB-009A64EE1D20}.Release|Win32.ActiveCfg = Release|Win32 + {577BA11F-E625-430A-B8AB-009A64EE1D20}.Release|Win32.Build.0 = Release|Win32 + {577BA11F-E625-430A-B8AB-009A64EE1D20}.Debug|x64.ActiveCfg = Debug|x64 + {577BA11F-E625-430A-B8AB-009A64EE1D20}.Debug|x64.Build.0 = Debug|x64 + {577BA11F-E625-430A-B8AB-009A64EE1D20}.Release|x64.ActiveCfg = Release|x64 + {577BA11F-E625-430A-B8AB-009A64EE1D20}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/print/SimplePipelineFilter/SimplePipelineFilter.sln b/print/SimplePipelineFilter/SimplePipelineFilter.sln index f7d259a10..d0ffa365b 100644 --- a/print/SimplePipelineFilter/SimplePipelineFilter.sln +++ b/print/SimplePipelineFilter/SimplePipelineFilter.sln @@ -3,7 +3,7 @@ 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", "{73744205-4B30-4AA3-9C04-DA4826B3E07C}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WdkPipelineFilter", "WdkPipelineFilter.vcxproj", "{E3CB8C5C-A0FD-408A-A7A8-5ACF1B8C5ACE}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {73744205-4B30-4AA3-9C04-DA4826B3E07C}.Debug|Win32.ActiveCfg = Debug|Win32 - {73744205-4B30-4AA3-9C04-DA4826B3E07C}.Debug|Win32.Build.0 = Debug|Win32 - {73744205-4B30-4AA3-9C04-DA4826B3E07C}.Release|Win32.ActiveCfg = Release|Win32 - {73744205-4B30-4AA3-9C04-DA4826B3E07C}.Release|Win32.Build.0 = Release|Win32 - {73744205-4B30-4AA3-9C04-DA4826B3E07C}.Debug|x64.ActiveCfg = Debug|x64 - {73744205-4B30-4AA3-9C04-DA4826B3E07C}.Debug|x64.Build.0 = Debug|x64 - {73744205-4B30-4AA3-9C04-DA4826B3E07C}.Release|x64.ActiveCfg = Release|x64 - {73744205-4B30-4AA3-9C04-DA4826B3E07C}.Release|x64.Build.0 = Release|x64 + {E3CB8C5C-A0FD-408A-A7A8-5ACF1B8C5ACE}.Debug|Win32.ActiveCfg = Debug|Win32 + {E3CB8C5C-A0FD-408A-A7A8-5ACF1B8C5ACE}.Debug|Win32.Build.0 = Debug|Win32 + {E3CB8C5C-A0FD-408A-A7A8-5ACF1B8C5ACE}.Release|Win32.ActiveCfg = Release|Win32 + {E3CB8C5C-A0FD-408A-A7A8-5ACF1B8C5ACE}.Release|Win32.Build.0 = Release|Win32 + {E3CB8C5C-A0FD-408A-A7A8-5ACF1B8C5ACE}.Debug|x64.ActiveCfg = Debug|x64 + {E3CB8C5C-A0FD-408A-A7A8-5ACF1B8C5ACE}.Debug|x64.Build.0 = Debug|x64 + {E3CB8C5C-A0FD-408A-A7A8-5ACF1B8C5ACE}.Release|x64.ActiveCfg = Release|x64 + {E3CB8C5C-A0FD-408A-A7A8-5ACF1B8C5ACE}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/print/SimplePipelineFilter/WdkPipelineFilter.vcxproj b/print/SimplePipelineFilter/WdkPipelineFilter.vcxproj index d319d2d4c..e3057992c 100644 --- a/print/SimplePipelineFilter/WdkPipelineFilter.vcxproj +++ b/print/SimplePipelineFilter/WdkPipelineFilter.vcxproj @@ -19,11 +19,11 @@ - {73744205-4B30-4AA3-9C04-DA4826B3E07C} + {E3CB8C5C-A0FD-408A-A7A8-5ACF1B8C5ACE} $(MSBuildProjectName) Debug Win32 - {762535A9-A614-4687-AB86-D73CBAAA409F} + {F8371080-4BF5-48D8-96C4-F5943D3FC7BD} @@ -280,7 +280,6 @@ - diff --git a/print/SimplePipelineFilter/WdkPipelineFilter.vcxproj.Filters b/print/SimplePipelineFilter/WdkPipelineFilter.vcxproj.Filters index e411e036b..06aeb935c 100644 --- a/print/SimplePipelineFilter/WdkPipelineFilter.vcxproj.Filters +++ b/print/SimplePipelineFilter/WdkPipelineFilter.vcxproj.Filters @@ -3,15 +3,15 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {11B1E8EE-5ECF-4526-81C3-DD599D206041} + {54D55C8A-FC51-4BE4-BD49-1B09021950CE} h;hpp;hxx;hm;inl;inc;xsd - {950D7CF8-7A8C-4160-8862-253FA4766F3F} + {E07FAB3D-C23F-4F19-89C2-96F7C3E4B12C} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {24D44278-1B4F-454E-8344-E9A8C98D75EF} + {66365D3C-28A5-45BF-AC68-92EA70CC54A4} diff --git a/print/XPSDrvSmpl/Package/package.VcxProj b/print/XPSDrvSmpl/Package/package.VcxProj index c33ab5d86..e45b373d4 100644 --- a/print/XPSDrvSmpl/Package/package.VcxProj +++ b/print/XPSDrvSmpl/Package/package.VcxProj @@ -18,6 +18,38 @@ x64 + + + {5DBE2309-E951-4840-A138-3284BD2B61C6} + + + {5DD79EE3-3778-4674-9537-70EA840F13B4} + + + {0230CB91-5810-491F-83CA-BFEF080C100C} + + + {9D2A1569-C11C-42CE-BF2F-86A10E869BB3} + + + {FBC26C8C-2B0B-4394-8477-C40A418C5540} + + + {5F377CEC-1783-4793-96E7-FF05DF1770D7} + + + {5B69A33D-2119-49E7-961F-136998752761} + + + {EEEB99E0-6403-4D91-AC46-9894657F2295} + + + {7B6DB6B7-543A-4FBB-B4A0-F9542C934D46} + + + {F6900780-9A02-439C-A7F1-D10CEBEE2CA3} + + WindowsKernelModeDriver10.0 Utility @@ -27,8 +59,8 @@ - {419B0310-99CD-4866-A8E6-B6F43E6B8513} - {9C2ED56D-6953-498B-9DC6-826F1AEE4F15} + {6DC00584-D5C7-430E-98EA-9B103C5DC293} + {1C40097E-EA72-4247-A54E-023A86EDDB06} $(MSBuildProjectName) @@ -73,42 +105,6 @@ - - - - - - - {AF94DFED-1979-486B-A16E-7BBB14711EA0} - - - {B0A0CBB7-DC33-4FE1-B356-C4978A035097} - - - {49DD5381-63D8-41FB-B641-AFE52BFBBC32} - - - {AE18BFE5-EA00-477B-A051-F28DD3078679} - - - {DDC3CDA6-A188-4A21-A321-37E8A044EC22} - - - {06548B4B-6104-4F36-8BAA-127D23F73CD4} - - - {268439B8-E924-47E4-BFE9-01DED439AFFE} - - - {89DE916B-9E07-4E1F-BFF9-3FC16328B0DD} - - - {BCB6A109-EB2A-4D55-908E-22EE132F75AD} - - - {20DDD6AA-CA85-46F3-8446-EDA619839273} - - diff --git a/print/XPSDrvSmpl/Package/package.VcxProj.Filters b/print/XPSDrvSmpl/Package/package.VcxProj.Filters index 1ff2a8dff..77412b334 100644 --- a/print/XPSDrvSmpl/Package/package.VcxProj.Filters +++ b/print/XPSDrvSmpl/Package/package.VcxProj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {7620518A-2F71-40A2-BDB7-BCE6E48AB046} + {54CEB06B-F0D3-48F6-AF6F-9ED6AF0BF224} h;hpp;hxx;hm;inl;inc;xsd - {C711D8E7-2B9C-4F45-8379-E6A59904E3B7} + {2CC18F8F-FB76-4C4B-ADBF-A2F2CFF8FE0C} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {A7890E75-2E06-42D1-B99F-2664864649B8} + {D41BE4BE-FBE4-4C2F-8C19-100F34654889} inf;inv;inx;mof;mc; - {540AC39A-B8D9-430D-BAC0-44BACAB69C1E} + {F120CD77-32F2-4CD0-93D2-BF7CC002976B} \ No newline at end of file diff --git a/print/XPSDrvSmpl/XPSDrvSmpl.sln b/print/XPSDrvSmpl/XPSDrvSmpl.sln index f2525eaee..50f96d186 100644 --- a/print/XPSDrvSmpl/XPSDrvSmpl.sln +++ b/print/XPSDrvSmpl/XPSDrvSmpl.sln @@ -3,91 +3,91 @@ 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", "{15E89F3C-10FD-40C7-A13E-707394EAE0C0}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Package", "Package", "{74E49D79-D81D-40F6-8C1F-DA726BEE5AD4}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Debug", "Debug", "{A249B218-7739-42E2-BCAF-46500B971F83}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Debug", "Debug", "{D4C874CD-D5AB-421E-900F-ACBC3B44D4F0}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Src", "Src", "{D67EEBCA-A813-4CD8-8F93-CA40013A6EAF}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Src", "Src", "{C2462BC2-F883-4F88-93F7-E8C46DF1529B}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Common", "Common", "{A088C774-8AD8-4256-A851-10528BEE6E7F}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Common", "Common", "{B84EC938-27BF-456D-967F-7213B4583412}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Ui", "Ui", "{93EE619A-8EA7-4755-A6FD-1F2DF51B628C}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Ui", "Ui", "{063FD5E1-8D60-48E1-A6F0-991C53E9A471}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Common", "Common", "{EDE58C09-846E-4B4D-A4C7-20D713FC48B6}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Common", "Common", "{8F974CAC-93A0-4406-9FA3-2AFBA0D39898}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Filters", "Filters", "{22B3AF3E-2F29-41FF-800D-C482843900CA}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Filters", "Filters", "{78F5E606-4302-4897-9F60-B752E3BED1B1}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Xdcont", "Xdcont", "{CF0E3C90-89E7-4D5A-9EFE-319152E67DB0}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Xdcont", "Xdcont", "{6CC50F4A-8D9C-4003-A2DD-8ADCDB29B8EA}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Watermark", "Watermark", "{11B663E8-C2A1-460E-A03E-2E11744CDC07}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Watermark", "Watermark", "{C23762DB-EFC6-4F60-8653-28DBCB86C9E6}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Scaling", "Scaling", "{9FC13A6C-6504-47D6-B543-AB5B80FC29ED}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Scaling", "Scaling", "{42751759-00EF-4C19-BB2A-A4E38FE039A5}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Nup", "Nup", "{F5F774EA-448F-47CA-B541-38577DE88B73}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Nup", "Nup", "{40F288AA-6615-4D85-87BF-0EEB277C9287}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Color", "Color", "{0E961C80-9F70-4327-80B0-FF1C5A0BF6F0}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Color", "Color", "{E2CF5E9C-86F5-4DA3-8FAB-4329A376C948}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Booklet", "Booklet", "{A340E86D-0D37-482B-9EE7-153DBA7B72F2}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Booklet", "Booklet", "{611922F5-FF89-42C8-8D6C-EBECF0A20E43}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "package", "Package\package.VcxProj", "{419B0310-99CD-4866-A8E6-B6F43E6B8513}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "package", "Package\package.VcxProj", "{6DC00584-D5C7-430E-98EA-9B103C5DC293}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xdsdbg", "src\debug\xdsdbg.vcxproj", "{B0A0CBB7-DC33-4FE1-B356-C4978A035097}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xdsdbg", "src\debug\xdsdbg.vcxproj", "{5DD79EE3-3778-4674-9537-70EA840F13B4}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xdsmplcmn", "src\common\xdsmplcmn.vcxproj", "{AF94DFED-1979-486B-A16E-7BBB14711EA0}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xdsmplcmn", "src\common\xdsmplcmn.vcxproj", "{5DBE2309-E951-4840-A138-3284BD2B61C6}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xdsmplui", "src\ui\xdsmplui.vcxproj", "{20DDD6AA-CA85-46F3-8446-EDA619839273}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xdsmplui", "src\ui\xdsmplui.vcxproj", "{F6900780-9A02-439C-A7F1-D10CEBEE2CA3}" ProjectSection(ProjectDependencies) = postProject - {B0A0CBB7-DC33-4FE1-B356-C4978A035097} = {B0A0CBB7-DC33-4FE1-B356-C4978A035097} - {AF94DFED-1979-486B-A16E-7BBB14711EA0} = {AF94DFED-1979-486B-A16E-7BBB14711EA0} + {5DD79EE3-3778-4674-9537-70EA840F13B4} = {5DD79EE3-3778-4674-9537-70EA840F13B4} + {5DBE2309-E951-4840-A138-3284BD2B61C6} = {5DBE2309-E951-4840-A138-3284BD2B61C6} EndProjectSection EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xdfltcmn", "src\filters\common\xdfltcmn.vcxproj", "{DDC3CDA6-A188-4A21-A321-37E8A044EC22}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xdfltcmn", "src\filters\common\xdfltcmn.vcxproj", "{FBC26C8C-2B0B-4394-8477-C40A418C5540}" ProjectSection(ProjectDependencies) = postProject - {B0A0CBB7-DC33-4FE1-B356-C4978A035097} = {B0A0CBB7-DC33-4FE1-B356-C4978A035097} - {AF94DFED-1979-486B-A16E-7BBB14711EA0} = {AF94DFED-1979-486B-A16E-7BBB14711EA0} + {5DD79EE3-3778-4674-9537-70EA840F13B4} = {5DD79EE3-3778-4674-9537-70EA840F13B4} + {5DBE2309-E951-4840-A138-3284BD2B61C6} = {5DBE2309-E951-4840-A138-3284BD2B61C6} EndProjectSection EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xdcont", "src\filters\xdcont\xdcont.vcxproj", "{BCB6A109-EB2A-4D55-908E-22EE132F75AD}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xdcont", "src\filters\xdcont\xdcont.vcxproj", "{7B6DB6B7-543A-4FBB-B4A0-F9542C934D46}" ProjectSection(ProjectDependencies) = postProject - {B0A0CBB7-DC33-4FE1-B356-C4978A035097} = {B0A0CBB7-DC33-4FE1-B356-C4978A035097} - {AF94DFED-1979-486B-A16E-7BBB14711EA0} = {AF94DFED-1979-486B-A16E-7BBB14711EA0} - {DDC3CDA6-A188-4A21-A321-37E8A044EC22} = {DDC3CDA6-A188-4A21-A321-37E8A044EC22} + {5DD79EE3-3778-4674-9537-70EA840F13B4} = {5DD79EE3-3778-4674-9537-70EA840F13B4} + {5DBE2309-E951-4840-A138-3284BD2B61C6} = {5DBE2309-E951-4840-A138-3284BD2B61C6} + {FBC26C8C-2B0B-4394-8477-C40A418C5540} = {FBC26C8C-2B0B-4394-8477-C40A418C5540} EndProjectSection EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xdwmark", "src\filters\watermark\xdwmark.vcxproj", "{89DE916B-9E07-4E1F-BFF9-3FC16328B0DD}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xdwmark", "src\filters\watermark\xdwmark.vcxproj", "{EEEB99E0-6403-4D91-AC46-9894657F2295}" ProjectSection(ProjectDependencies) = postProject - {B0A0CBB7-DC33-4FE1-B356-C4978A035097} = {B0A0CBB7-DC33-4FE1-B356-C4978A035097} - {AF94DFED-1979-486B-A16E-7BBB14711EA0} = {AF94DFED-1979-486B-A16E-7BBB14711EA0} - {DDC3CDA6-A188-4A21-A321-37E8A044EC22} = {DDC3CDA6-A188-4A21-A321-37E8A044EC22} + {5DD79EE3-3778-4674-9537-70EA840F13B4} = {5DD79EE3-3778-4674-9537-70EA840F13B4} + {5DBE2309-E951-4840-A138-3284BD2B61C6} = {5DBE2309-E951-4840-A138-3284BD2B61C6} + {FBC26C8C-2B0B-4394-8477-C40A418C5540} = {FBC26C8C-2B0B-4394-8477-C40A418C5540} EndProjectSection EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xdscale", "src\filters\scaling\xdscale.vcxproj", "{268439B8-E924-47E4-BFE9-01DED439AFFE}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xdscale", "src\filters\scaling\xdscale.vcxproj", "{5B69A33D-2119-49E7-961F-136998752761}" ProjectSection(ProjectDependencies) = postProject - {BCB6A109-EB2A-4D55-908E-22EE132F75AD} = {BCB6A109-EB2A-4D55-908E-22EE132F75AD} - {B0A0CBB7-DC33-4FE1-B356-C4978A035097} = {B0A0CBB7-DC33-4FE1-B356-C4978A035097} - {AF94DFED-1979-486B-A16E-7BBB14711EA0} = {AF94DFED-1979-486B-A16E-7BBB14711EA0} - {DDC3CDA6-A188-4A21-A321-37E8A044EC22} = {DDC3CDA6-A188-4A21-A321-37E8A044EC22} + {7B6DB6B7-543A-4FBB-B4A0-F9542C934D46} = {7B6DB6B7-543A-4FBB-B4A0-F9542C934D46} + {5DD79EE3-3778-4674-9537-70EA840F13B4} = {5DD79EE3-3778-4674-9537-70EA840F13B4} + {5DBE2309-E951-4840-A138-3284BD2B61C6} = {5DBE2309-E951-4840-A138-3284BD2B61C6} + {FBC26C8C-2B0B-4394-8477-C40A418C5540} = {FBC26C8C-2B0B-4394-8477-C40A418C5540} EndProjectSection EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xdnup", "src\filters\nup\xdnup.vcxproj", "{06548B4B-6104-4F36-8BAA-127D23F73CD4}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xdnup", "src\filters\nup\xdnup.vcxproj", "{5F377CEC-1783-4793-96E7-FF05DF1770D7}" ProjectSection(ProjectDependencies) = postProject - {B0A0CBB7-DC33-4FE1-B356-C4978A035097} = {B0A0CBB7-DC33-4FE1-B356-C4978A035097} - {AF94DFED-1979-486B-A16E-7BBB14711EA0} = {AF94DFED-1979-486B-A16E-7BBB14711EA0} - {DDC3CDA6-A188-4A21-A321-37E8A044EC22} = {DDC3CDA6-A188-4A21-A321-37E8A044EC22} + {5DD79EE3-3778-4674-9537-70EA840F13B4} = {5DD79EE3-3778-4674-9537-70EA840F13B4} + {5DBE2309-E951-4840-A138-3284BD2B61C6} = {5DBE2309-E951-4840-A138-3284BD2B61C6} + {FBC26C8C-2B0B-4394-8477-C40A418C5540} = {FBC26C8C-2B0B-4394-8477-C40A418C5540} EndProjectSection EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "XDColMan", "src\filters\color\XDColMan.vcxproj", "{AE18BFE5-EA00-477B-A051-F28DD3078679}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "XDColMan", "src\filters\color\XDColMan.vcxproj", "{9D2A1569-C11C-42CE-BF2F-86A10E869BB3}" ProjectSection(ProjectDependencies) = postProject - {B0A0CBB7-DC33-4FE1-B356-C4978A035097} = {B0A0CBB7-DC33-4FE1-B356-C4978A035097} - {AF94DFED-1979-486B-A16E-7BBB14711EA0} = {AF94DFED-1979-486B-A16E-7BBB14711EA0} - {DDC3CDA6-A188-4A21-A321-37E8A044EC22} = {DDC3CDA6-A188-4A21-A321-37E8A044EC22} + {5DD79EE3-3778-4674-9537-70EA840F13B4} = {5DD79EE3-3778-4674-9537-70EA840F13B4} + {5DBE2309-E951-4840-A138-3284BD2B61C6} = {5DBE2309-E951-4840-A138-3284BD2B61C6} + {FBC26C8C-2B0B-4394-8477-C40A418C5540} = {FBC26C8C-2B0B-4394-8477-C40A418C5540} EndProjectSection EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xdbook", "src\filters\booklet\xdbook.vcxproj", "{49DD5381-63D8-41FB-B641-AFE52BFBBC32}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xdbook", "src\filters\booklet\xdbook.vcxproj", "{0230CB91-5810-491F-83CA-BFEF080C100C}" ProjectSection(ProjectDependencies) = postProject - {B0A0CBB7-DC33-4FE1-B356-C4978A035097} = {B0A0CBB7-DC33-4FE1-B356-C4978A035097} - {AF94DFED-1979-486B-A16E-7BBB14711EA0} = {AF94DFED-1979-486B-A16E-7BBB14711EA0} - {DDC3CDA6-A188-4A21-A321-37E8A044EC22} = {DDC3CDA6-A188-4A21-A321-37E8A044EC22} + {5DD79EE3-3778-4674-9537-70EA840F13B4} = {5DD79EE3-3778-4674-9537-70EA840F13B4} + {5DBE2309-E951-4840-A138-3284BD2B61C6} = {5DBE2309-E951-4840-A138-3284BD2B61C6} + {FBC26C8C-2B0B-4394-8477-C40A418C5540} = {FBC26C8C-2B0B-4394-8477-C40A418C5540} EndProjectSection EndProject Global @@ -98,120 +98,120 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {419B0310-99CD-4866-A8E6-B6F43E6B8513}.Debug|Win32.ActiveCfg = Debug|Win32 - {419B0310-99CD-4866-A8E6-B6F43E6B8513}.Debug|Win32.Build.0 = Debug|Win32 - {419B0310-99CD-4866-A8E6-B6F43E6B8513}.Release|Win32.ActiveCfg = Release|Win32 - {419B0310-99CD-4866-A8E6-B6F43E6B8513}.Release|Win32.Build.0 = Release|Win32 - {419B0310-99CD-4866-A8E6-B6F43E6B8513}.Debug|x64.ActiveCfg = Debug|x64 - {419B0310-99CD-4866-A8E6-B6F43E6B8513}.Debug|x64.Build.0 = Debug|x64 - {419B0310-99CD-4866-A8E6-B6F43E6B8513}.Release|x64.ActiveCfg = Release|x64 - {419B0310-99CD-4866-A8E6-B6F43E6B8513}.Release|x64.Build.0 = Release|x64 - {B0A0CBB7-DC33-4FE1-B356-C4978A035097}.Debug|Win32.ActiveCfg = Debug|Win32 - {B0A0CBB7-DC33-4FE1-B356-C4978A035097}.Debug|Win32.Build.0 = Debug|Win32 - {B0A0CBB7-DC33-4FE1-B356-C4978A035097}.Release|Win32.ActiveCfg = Release|Win32 - {B0A0CBB7-DC33-4FE1-B356-C4978A035097}.Release|Win32.Build.0 = Release|Win32 - {B0A0CBB7-DC33-4FE1-B356-C4978A035097}.Debug|x64.ActiveCfg = Debug|x64 - {B0A0CBB7-DC33-4FE1-B356-C4978A035097}.Debug|x64.Build.0 = Debug|x64 - {B0A0CBB7-DC33-4FE1-B356-C4978A035097}.Release|x64.ActiveCfg = Release|x64 - {B0A0CBB7-DC33-4FE1-B356-C4978A035097}.Release|x64.Build.0 = Release|x64 - {AF94DFED-1979-486B-A16E-7BBB14711EA0}.Debug|Win32.ActiveCfg = Debug|Win32 - {AF94DFED-1979-486B-A16E-7BBB14711EA0}.Debug|Win32.Build.0 = Debug|Win32 - {AF94DFED-1979-486B-A16E-7BBB14711EA0}.Release|Win32.ActiveCfg = Release|Win32 - {AF94DFED-1979-486B-A16E-7BBB14711EA0}.Release|Win32.Build.0 = Release|Win32 - {AF94DFED-1979-486B-A16E-7BBB14711EA0}.Debug|x64.ActiveCfg = Debug|x64 - {AF94DFED-1979-486B-A16E-7BBB14711EA0}.Debug|x64.Build.0 = Debug|x64 - {AF94DFED-1979-486B-A16E-7BBB14711EA0}.Release|x64.ActiveCfg = Release|x64 - {AF94DFED-1979-486B-A16E-7BBB14711EA0}.Release|x64.Build.0 = Release|x64 - {20DDD6AA-CA85-46F3-8446-EDA619839273}.Debug|Win32.ActiveCfg = Debug|Win32 - {20DDD6AA-CA85-46F3-8446-EDA619839273}.Debug|Win32.Build.0 = Debug|Win32 - {20DDD6AA-CA85-46F3-8446-EDA619839273}.Release|Win32.ActiveCfg = Release|Win32 - {20DDD6AA-CA85-46F3-8446-EDA619839273}.Release|Win32.Build.0 = Release|Win32 - {20DDD6AA-CA85-46F3-8446-EDA619839273}.Debug|x64.ActiveCfg = Debug|x64 - {20DDD6AA-CA85-46F3-8446-EDA619839273}.Debug|x64.Build.0 = Debug|x64 - {20DDD6AA-CA85-46F3-8446-EDA619839273}.Release|x64.ActiveCfg = Release|x64 - {20DDD6AA-CA85-46F3-8446-EDA619839273}.Release|x64.Build.0 = Release|x64 - {DDC3CDA6-A188-4A21-A321-37E8A044EC22}.Debug|Win32.ActiveCfg = Debug|Win32 - {DDC3CDA6-A188-4A21-A321-37E8A044EC22}.Debug|Win32.Build.0 = Debug|Win32 - {DDC3CDA6-A188-4A21-A321-37E8A044EC22}.Release|Win32.ActiveCfg = Release|Win32 - {DDC3CDA6-A188-4A21-A321-37E8A044EC22}.Release|Win32.Build.0 = Release|Win32 - {DDC3CDA6-A188-4A21-A321-37E8A044EC22}.Debug|x64.ActiveCfg = Debug|x64 - {DDC3CDA6-A188-4A21-A321-37E8A044EC22}.Debug|x64.Build.0 = Debug|x64 - {DDC3CDA6-A188-4A21-A321-37E8A044EC22}.Release|x64.ActiveCfg = Release|x64 - {DDC3CDA6-A188-4A21-A321-37E8A044EC22}.Release|x64.Build.0 = Release|x64 - {BCB6A109-EB2A-4D55-908E-22EE132F75AD}.Debug|Win32.ActiveCfg = Debug|Win32 - {BCB6A109-EB2A-4D55-908E-22EE132F75AD}.Debug|Win32.Build.0 = Debug|Win32 - {BCB6A109-EB2A-4D55-908E-22EE132F75AD}.Release|Win32.ActiveCfg = Release|Win32 - {BCB6A109-EB2A-4D55-908E-22EE132F75AD}.Release|Win32.Build.0 = Release|Win32 - {BCB6A109-EB2A-4D55-908E-22EE132F75AD}.Debug|x64.ActiveCfg = Debug|x64 - {BCB6A109-EB2A-4D55-908E-22EE132F75AD}.Debug|x64.Build.0 = Debug|x64 - {BCB6A109-EB2A-4D55-908E-22EE132F75AD}.Release|x64.ActiveCfg = Release|x64 - {BCB6A109-EB2A-4D55-908E-22EE132F75AD}.Release|x64.Build.0 = Release|x64 - {89DE916B-9E07-4E1F-BFF9-3FC16328B0DD}.Debug|Win32.ActiveCfg = Debug|Win32 - {89DE916B-9E07-4E1F-BFF9-3FC16328B0DD}.Debug|Win32.Build.0 = Debug|Win32 - {89DE916B-9E07-4E1F-BFF9-3FC16328B0DD}.Release|Win32.ActiveCfg = Release|Win32 - {89DE916B-9E07-4E1F-BFF9-3FC16328B0DD}.Release|Win32.Build.0 = Release|Win32 - {89DE916B-9E07-4E1F-BFF9-3FC16328B0DD}.Debug|x64.ActiveCfg = Debug|x64 - {89DE916B-9E07-4E1F-BFF9-3FC16328B0DD}.Debug|x64.Build.0 = Debug|x64 - {89DE916B-9E07-4E1F-BFF9-3FC16328B0DD}.Release|x64.ActiveCfg = Release|x64 - {89DE916B-9E07-4E1F-BFF9-3FC16328B0DD}.Release|x64.Build.0 = Release|x64 - {268439B8-E924-47E4-BFE9-01DED439AFFE}.Debug|Win32.ActiveCfg = Debug|Win32 - {268439B8-E924-47E4-BFE9-01DED439AFFE}.Debug|Win32.Build.0 = Debug|Win32 - {268439B8-E924-47E4-BFE9-01DED439AFFE}.Release|Win32.ActiveCfg = Release|Win32 - {268439B8-E924-47E4-BFE9-01DED439AFFE}.Release|Win32.Build.0 = Release|Win32 - {268439B8-E924-47E4-BFE9-01DED439AFFE}.Debug|x64.ActiveCfg = Debug|x64 - {268439B8-E924-47E4-BFE9-01DED439AFFE}.Debug|x64.Build.0 = Debug|x64 - {268439B8-E924-47E4-BFE9-01DED439AFFE}.Release|x64.ActiveCfg = Release|x64 - {268439B8-E924-47E4-BFE9-01DED439AFFE}.Release|x64.Build.0 = Release|x64 - {06548B4B-6104-4F36-8BAA-127D23F73CD4}.Debug|Win32.ActiveCfg = Debug|Win32 - {06548B4B-6104-4F36-8BAA-127D23F73CD4}.Debug|Win32.Build.0 = Debug|Win32 - {06548B4B-6104-4F36-8BAA-127D23F73CD4}.Release|Win32.ActiveCfg = Release|Win32 - {06548B4B-6104-4F36-8BAA-127D23F73CD4}.Release|Win32.Build.0 = Release|Win32 - {06548B4B-6104-4F36-8BAA-127D23F73CD4}.Debug|x64.ActiveCfg = Debug|x64 - {06548B4B-6104-4F36-8BAA-127D23F73CD4}.Debug|x64.Build.0 = Debug|x64 - {06548B4B-6104-4F36-8BAA-127D23F73CD4}.Release|x64.ActiveCfg = Release|x64 - {06548B4B-6104-4F36-8BAA-127D23F73CD4}.Release|x64.Build.0 = Release|x64 - {AE18BFE5-EA00-477B-A051-F28DD3078679}.Debug|Win32.ActiveCfg = Debug|Win32 - {AE18BFE5-EA00-477B-A051-F28DD3078679}.Debug|Win32.Build.0 = Debug|Win32 - {AE18BFE5-EA00-477B-A051-F28DD3078679}.Release|Win32.ActiveCfg = Release|Win32 - {AE18BFE5-EA00-477B-A051-F28DD3078679}.Release|Win32.Build.0 = Release|Win32 - {AE18BFE5-EA00-477B-A051-F28DD3078679}.Debug|x64.ActiveCfg = Debug|x64 - {AE18BFE5-EA00-477B-A051-F28DD3078679}.Debug|x64.Build.0 = Debug|x64 - {AE18BFE5-EA00-477B-A051-F28DD3078679}.Release|x64.ActiveCfg = Release|x64 - {AE18BFE5-EA00-477B-A051-F28DD3078679}.Release|x64.Build.0 = Release|x64 - {49DD5381-63D8-41FB-B641-AFE52BFBBC32}.Debug|Win32.ActiveCfg = Debug|Win32 - {49DD5381-63D8-41FB-B641-AFE52BFBBC32}.Debug|Win32.Build.0 = Debug|Win32 - {49DD5381-63D8-41FB-B641-AFE52BFBBC32}.Release|Win32.ActiveCfg = Release|Win32 - {49DD5381-63D8-41FB-B641-AFE52BFBBC32}.Release|Win32.Build.0 = Release|Win32 - {49DD5381-63D8-41FB-B641-AFE52BFBBC32}.Debug|x64.ActiveCfg = Debug|x64 - {49DD5381-63D8-41FB-B641-AFE52BFBBC32}.Debug|x64.Build.0 = Debug|x64 - {49DD5381-63D8-41FB-B641-AFE52BFBBC32}.Release|x64.ActiveCfg = Release|x64 - {49DD5381-63D8-41FB-B641-AFE52BFBBC32}.Release|x64.Build.0 = Release|x64 + {6DC00584-D5C7-430E-98EA-9B103C5DC293}.Debug|Win32.ActiveCfg = Debug|Win32 + {6DC00584-D5C7-430E-98EA-9B103C5DC293}.Debug|Win32.Build.0 = Debug|Win32 + {6DC00584-D5C7-430E-98EA-9B103C5DC293}.Release|Win32.ActiveCfg = Release|Win32 + {6DC00584-D5C7-430E-98EA-9B103C5DC293}.Release|Win32.Build.0 = Release|Win32 + {6DC00584-D5C7-430E-98EA-9B103C5DC293}.Debug|x64.ActiveCfg = Debug|x64 + {6DC00584-D5C7-430E-98EA-9B103C5DC293}.Debug|x64.Build.0 = Debug|x64 + {6DC00584-D5C7-430E-98EA-9B103C5DC293}.Release|x64.ActiveCfg = Release|x64 + {6DC00584-D5C7-430E-98EA-9B103C5DC293}.Release|x64.Build.0 = Release|x64 + {5DD79EE3-3778-4674-9537-70EA840F13B4}.Debug|Win32.ActiveCfg = Debug|Win32 + {5DD79EE3-3778-4674-9537-70EA840F13B4}.Debug|Win32.Build.0 = Debug|Win32 + {5DD79EE3-3778-4674-9537-70EA840F13B4}.Release|Win32.ActiveCfg = Release|Win32 + {5DD79EE3-3778-4674-9537-70EA840F13B4}.Release|Win32.Build.0 = Release|Win32 + {5DD79EE3-3778-4674-9537-70EA840F13B4}.Debug|x64.ActiveCfg = Debug|x64 + {5DD79EE3-3778-4674-9537-70EA840F13B4}.Debug|x64.Build.0 = Debug|x64 + {5DD79EE3-3778-4674-9537-70EA840F13B4}.Release|x64.ActiveCfg = Release|x64 + {5DD79EE3-3778-4674-9537-70EA840F13B4}.Release|x64.Build.0 = Release|x64 + {5DBE2309-E951-4840-A138-3284BD2B61C6}.Debug|Win32.ActiveCfg = Debug|Win32 + {5DBE2309-E951-4840-A138-3284BD2B61C6}.Debug|Win32.Build.0 = Debug|Win32 + {5DBE2309-E951-4840-A138-3284BD2B61C6}.Release|Win32.ActiveCfg = Release|Win32 + {5DBE2309-E951-4840-A138-3284BD2B61C6}.Release|Win32.Build.0 = Release|Win32 + {5DBE2309-E951-4840-A138-3284BD2B61C6}.Debug|x64.ActiveCfg = Debug|x64 + {5DBE2309-E951-4840-A138-3284BD2B61C6}.Debug|x64.Build.0 = Debug|x64 + {5DBE2309-E951-4840-A138-3284BD2B61C6}.Release|x64.ActiveCfg = Release|x64 + {5DBE2309-E951-4840-A138-3284BD2B61C6}.Release|x64.Build.0 = Release|x64 + {F6900780-9A02-439C-A7F1-D10CEBEE2CA3}.Debug|Win32.ActiveCfg = Debug|Win32 + {F6900780-9A02-439C-A7F1-D10CEBEE2CA3}.Debug|Win32.Build.0 = Debug|Win32 + {F6900780-9A02-439C-A7F1-D10CEBEE2CA3}.Release|Win32.ActiveCfg = Release|Win32 + {F6900780-9A02-439C-A7F1-D10CEBEE2CA3}.Release|Win32.Build.0 = Release|Win32 + {F6900780-9A02-439C-A7F1-D10CEBEE2CA3}.Debug|x64.ActiveCfg = Debug|x64 + {F6900780-9A02-439C-A7F1-D10CEBEE2CA3}.Debug|x64.Build.0 = Debug|x64 + {F6900780-9A02-439C-A7F1-D10CEBEE2CA3}.Release|x64.ActiveCfg = Release|x64 + {F6900780-9A02-439C-A7F1-D10CEBEE2CA3}.Release|x64.Build.0 = Release|x64 + {FBC26C8C-2B0B-4394-8477-C40A418C5540}.Debug|Win32.ActiveCfg = Debug|Win32 + {FBC26C8C-2B0B-4394-8477-C40A418C5540}.Debug|Win32.Build.0 = Debug|Win32 + {FBC26C8C-2B0B-4394-8477-C40A418C5540}.Release|Win32.ActiveCfg = Release|Win32 + {FBC26C8C-2B0B-4394-8477-C40A418C5540}.Release|Win32.Build.0 = Release|Win32 + {FBC26C8C-2B0B-4394-8477-C40A418C5540}.Debug|x64.ActiveCfg = Debug|x64 + {FBC26C8C-2B0B-4394-8477-C40A418C5540}.Debug|x64.Build.0 = Debug|x64 + {FBC26C8C-2B0B-4394-8477-C40A418C5540}.Release|x64.ActiveCfg = Release|x64 + {FBC26C8C-2B0B-4394-8477-C40A418C5540}.Release|x64.Build.0 = Release|x64 + {7B6DB6B7-543A-4FBB-B4A0-F9542C934D46}.Debug|Win32.ActiveCfg = Debug|Win32 + {7B6DB6B7-543A-4FBB-B4A0-F9542C934D46}.Debug|Win32.Build.0 = Debug|Win32 + {7B6DB6B7-543A-4FBB-B4A0-F9542C934D46}.Release|Win32.ActiveCfg = Release|Win32 + {7B6DB6B7-543A-4FBB-B4A0-F9542C934D46}.Release|Win32.Build.0 = Release|Win32 + {7B6DB6B7-543A-4FBB-B4A0-F9542C934D46}.Debug|x64.ActiveCfg = Debug|x64 + {7B6DB6B7-543A-4FBB-B4A0-F9542C934D46}.Debug|x64.Build.0 = Debug|x64 + {7B6DB6B7-543A-4FBB-B4A0-F9542C934D46}.Release|x64.ActiveCfg = Release|x64 + {7B6DB6B7-543A-4FBB-B4A0-F9542C934D46}.Release|x64.Build.0 = Release|x64 + {EEEB99E0-6403-4D91-AC46-9894657F2295}.Debug|Win32.ActiveCfg = Debug|Win32 + {EEEB99E0-6403-4D91-AC46-9894657F2295}.Debug|Win32.Build.0 = Debug|Win32 + {EEEB99E0-6403-4D91-AC46-9894657F2295}.Release|Win32.ActiveCfg = Release|Win32 + {EEEB99E0-6403-4D91-AC46-9894657F2295}.Release|Win32.Build.0 = Release|Win32 + {EEEB99E0-6403-4D91-AC46-9894657F2295}.Debug|x64.ActiveCfg = Debug|x64 + {EEEB99E0-6403-4D91-AC46-9894657F2295}.Debug|x64.Build.0 = Debug|x64 + {EEEB99E0-6403-4D91-AC46-9894657F2295}.Release|x64.ActiveCfg = Release|x64 + {EEEB99E0-6403-4D91-AC46-9894657F2295}.Release|x64.Build.0 = Release|x64 + {5B69A33D-2119-49E7-961F-136998752761}.Debug|Win32.ActiveCfg = Debug|Win32 + {5B69A33D-2119-49E7-961F-136998752761}.Debug|Win32.Build.0 = Debug|Win32 + {5B69A33D-2119-49E7-961F-136998752761}.Release|Win32.ActiveCfg = Release|Win32 + {5B69A33D-2119-49E7-961F-136998752761}.Release|Win32.Build.0 = Release|Win32 + {5B69A33D-2119-49E7-961F-136998752761}.Debug|x64.ActiveCfg = Debug|x64 + {5B69A33D-2119-49E7-961F-136998752761}.Debug|x64.Build.0 = Debug|x64 + {5B69A33D-2119-49E7-961F-136998752761}.Release|x64.ActiveCfg = Release|x64 + {5B69A33D-2119-49E7-961F-136998752761}.Release|x64.Build.0 = Release|x64 + {5F377CEC-1783-4793-96E7-FF05DF1770D7}.Debug|Win32.ActiveCfg = Debug|Win32 + {5F377CEC-1783-4793-96E7-FF05DF1770D7}.Debug|Win32.Build.0 = Debug|Win32 + {5F377CEC-1783-4793-96E7-FF05DF1770D7}.Release|Win32.ActiveCfg = Release|Win32 + {5F377CEC-1783-4793-96E7-FF05DF1770D7}.Release|Win32.Build.0 = Release|Win32 + {5F377CEC-1783-4793-96E7-FF05DF1770D7}.Debug|x64.ActiveCfg = Debug|x64 + {5F377CEC-1783-4793-96E7-FF05DF1770D7}.Debug|x64.Build.0 = Debug|x64 + {5F377CEC-1783-4793-96E7-FF05DF1770D7}.Release|x64.ActiveCfg = Release|x64 + {5F377CEC-1783-4793-96E7-FF05DF1770D7}.Release|x64.Build.0 = Release|x64 + {9D2A1569-C11C-42CE-BF2F-86A10E869BB3}.Debug|Win32.ActiveCfg = Debug|Win32 + {9D2A1569-C11C-42CE-BF2F-86A10E869BB3}.Debug|Win32.Build.0 = Debug|Win32 + {9D2A1569-C11C-42CE-BF2F-86A10E869BB3}.Release|Win32.ActiveCfg = Release|Win32 + {9D2A1569-C11C-42CE-BF2F-86A10E869BB3}.Release|Win32.Build.0 = Release|Win32 + {9D2A1569-C11C-42CE-BF2F-86A10E869BB3}.Debug|x64.ActiveCfg = Debug|x64 + {9D2A1569-C11C-42CE-BF2F-86A10E869BB3}.Debug|x64.Build.0 = Debug|x64 + {9D2A1569-C11C-42CE-BF2F-86A10E869BB3}.Release|x64.ActiveCfg = Release|x64 + {9D2A1569-C11C-42CE-BF2F-86A10E869BB3}.Release|x64.Build.0 = Release|x64 + {0230CB91-5810-491F-83CA-BFEF080C100C}.Debug|Win32.ActiveCfg = Debug|Win32 + {0230CB91-5810-491F-83CA-BFEF080C100C}.Debug|Win32.Build.0 = Debug|Win32 + {0230CB91-5810-491F-83CA-BFEF080C100C}.Release|Win32.ActiveCfg = Release|Win32 + {0230CB91-5810-491F-83CA-BFEF080C100C}.Release|Win32.Build.0 = Release|Win32 + {0230CB91-5810-491F-83CA-BFEF080C100C}.Debug|x64.ActiveCfg = Debug|x64 + {0230CB91-5810-491F-83CA-BFEF080C100C}.Debug|x64.Build.0 = Debug|x64 + {0230CB91-5810-491F-83CA-BFEF080C100C}.Release|x64.ActiveCfg = Release|x64 + {0230CB91-5810-491F-83CA-BFEF080C100C}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {419B0310-99CD-4866-A8E6-B6F43E6B8513} = {15E89F3C-10FD-40C7-A13E-707394EAE0C0} - {B0A0CBB7-DC33-4FE1-B356-C4978A035097} = {A249B218-7739-42E2-BCAF-46500B971F83} - {AF94DFED-1979-486B-A16E-7BBB14711EA0} = {A088C774-8AD8-4256-A851-10528BEE6E7F} - {20DDD6AA-CA85-46F3-8446-EDA619839273} = {93EE619A-8EA7-4755-A6FD-1F2DF51B628C} - {DDC3CDA6-A188-4A21-A321-37E8A044EC22} = {EDE58C09-846E-4B4D-A4C7-20D713FC48B6} - {BCB6A109-EB2A-4D55-908E-22EE132F75AD} = {CF0E3C90-89E7-4D5A-9EFE-319152E67DB0} - {89DE916B-9E07-4E1F-BFF9-3FC16328B0DD} = {11B663E8-C2A1-460E-A03E-2E11744CDC07} - {268439B8-E924-47E4-BFE9-01DED439AFFE} = {9FC13A6C-6504-47D6-B543-AB5B80FC29ED} - {06548B4B-6104-4F36-8BAA-127D23F73CD4} = {F5F774EA-448F-47CA-B541-38577DE88B73} - {AE18BFE5-EA00-477B-A051-F28DD3078679} = {0E961C80-9F70-4327-80B0-FF1C5A0BF6F0} - {49DD5381-63D8-41FB-B641-AFE52BFBBC32} = {A340E86D-0D37-482B-9EE7-153DBA7B72F2} - {A249B218-7739-42E2-BCAF-46500B971F83} = {D67EEBCA-A813-4CD8-8F93-CA40013A6EAF} - {A088C774-8AD8-4256-A851-10528BEE6E7F} = {D67EEBCA-A813-4CD8-8F93-CA40013A6EAF} - {93EE619A-8EA7-4755-A6FD-1F2DF51B628C} = {D67EEBCA-A813-4CD8-8F93-CA40013A6EAF} - {EDE58C09-846E-4B4D-A4C7-20D713FC48B6} = {22B3AF3E-2F29-41FF-800D-C482843900CA} - {22B3AF3E-2F29-41FF-800D-C482843900CA} = {D67EEBCA-A813-4CD8-8F93-CA40013A6EAF} - {CF0E3C90-89E7-4D5A-9EFE-319152E67DB0} = {22B3AF3E-2F29-41FF-800D-C482843900CA} - {11B663E8-C2A1-460E-A03E-2E11744CDC07} = {22B3AF3E-2F29-41FF-800D-C482843900CA} - {9FC13A6C-6504-47D6-B543-AB5B80FC29ED} = {22B3AF3E-2F29-41FF-800D-C482843900CA} - {F5F774EA-448F-47CA-B541-38577DE88B73} = {22B3AF3E-2F29-41FF-800D-C482843900CA} - {0E961C80-9F70-4327-80B0-FF1C5A0BF6F0} = {22B3AF3E-2F29-41FF-800D-C482843900CA} - {A340E86D-0D37-482B-9EE7-153DBA7B72F2} = {22B3AF3E-2F29-41FF-800D-C482843900CA} + {6DC00584-D5C7-430E-98EA-9B103C5DC293} = {74E49D79-D81D-40F6-8C1F-DA726BEE5AD4} + {5DD79EE3-3778-4674-9537-70EA840F13B4} = {D4C874CD-D5AB-421E-900F-ACBC3B44D4F0} + {5DBE2309-E951-4840-A138-3284BD2B61C6} = {B84EC938-27BF-456D-967F-7213B4583412} + {F6900780-9A02-439C-A7F1-D10CEBEE2CA3} = {063FD5E1-8D60-48E1-A6F0-991C53E9A471} + {FBC26C8C-2B0B-4394-8477-C40A418C5540} = {8F974CAC-93A0-4406-9FA3-2AFBA0D39898} + {7B6DB6B7-543A-4FBB-B4A0-F9542C934D46} = {6CC50F4A-8D9C-4003-A2DD-8ADCDB29B8EA} + {EEEB99E0-6403-4D91-AC46-9894657F2295} = {C23762DB-EFC6-4F60-8653-28DBCB86C9E6} + {5B69A33D-2119-49E7-961F-136998752761} = {42751759-00EF-4C19-BB2A-A4E38FE039A5} + {5F377CEC-1783-4793-96E7-FF05DF1770D7} = {40F288AA-6615-4D85-87BF-0EEB277C9287} + {9D2A1569-C11C-42CE-BF2F-86A10E869BB3} = {E2CF5E9C-86F5-4DA3-8FAB-4329A376C948} + {0230CB91-5810-491F-83CA-BFEF080C100C} = {611922F5-FF89-42C8-8D6C-EBECF0A20E43} + {D4C874CD-D5AB-421E-900F-ACBC3B44D4F0} = {C2462BC2-F883-4F88-93F7-E8C46DF1529B} + {B84EC938-27BF-456D-967F-7213B4583412} = {C2462BC2-F883-4F88-93F7-E8C46DF1529B} + {063FD5E1-8D60-48E1-A6F0-991C53E9A471} = {C2462BC2-F883-4F88-93F7-E8C46DF1529B} + {8F974CAC-93A0-4406-9FA3-2AFBA0D39898} = {78F5E606-4302-4897-9F60-B752E3BED1B1} + {78F5E606-4302-4897-9F60-B752E3BED1B1} = {C2462BC2-F883-4F88-93F7-E8C46DF1529B} + {6CC50F4A-8D9C-4003-A2DD-8ADCDB29B8EA} = {78F5E606-4302-4897-9F60-B752E3BED1B1} + {C23762DB-EFC6-4F60-8653-28DBCB86C9E6} = {78F5E606-4302-4897-9F60-B752E3BED1B1} + {42751759-00EF-4C19-BB2A-A4E38FE039A5} = {78F5E606-4302-4897-9F60-B752E3BED1B1} + {40F288AA-6615-4D85-87BF-0EEB277C9287} = {78F5E606-4302-4897-9F60-B752E3BED1B1} + {E2CF5E9C-86F5-4DA3-8FAB-4329A376C948} = {78F5E606-4302-4897-9F60-B752E3BED1B1} + {611922F5-FF89-42C8-8D6C-EBECF0A20E43} = {78F5E606-4302-4897-9F60-B752E3BED1B1} EndGlobalSection EndGlobal diff --git a/print/XPSDrvSmpl/install/xdsmpl.inf b/print/XPSDrvSmpl/install/xdsmpl.inf index 762e4bc9c..eb94e358a 100644 --- a/print/XPSDrvSmpl/install/xdsmpl.inf +++ b/print/XPSDrvSmpl/install/xdsmpl.inf @@ -18,30 +18,30 @@ ; [Version] Signature="$Windows NT$" -Provider=%MS% +Provider=%ProviderString% ClassGUID={4D36E979-E325-11CE-BFC1-08002BE10318} Class=Printer DriverVer=10/17/2008,6.1.6930.0 [Manufacturer] -%Microsoft%=Microsoft,NTx86,NTia64,NTamd64,NTx86.6.0,NTia64.6.0,NTamd64.6.0 +%ManufacturerName%=Standard,NTx86,NTia64,NTamd64,NTx86.6.0,NTia64.6.0,NTamd64.6.0 -[Microsoft.NTx86] +[Standard.NTx86] "XPSDrv Sample Driver" = INSTALL_XDSMPL_FILTERS_PRE_VISTA -[Microsoft.NTia64] +[Standard.NTia64] "XPSDrv Sample Driver" = INSTALL_XDSMPL_FILTERS_PRE_VISTA -[Microsoft.NTamd64] +[Standard.NTamd64] "XPSDrv Sample Driver" = INSTALL_XDSMPL_FILTERS_PRE_VISTA -[Microsoft.NTx86.6.0] +[Standard.NTx86.6.0] "XPSDrv Sample Driver" = INSTALL_XDSMPL_FILTERS_VISTA -[Microsoft.NTia64.6.0] +[Standard.NTia64.6.0] "XPSDrv Sample Driver" = INSTALL_XDSMPL_FILTERS_VISTA -[Microsoft.NTamd64.6.0] +[Standard.NTamd64.6.0] "XPSDrv Sample Driver" = INSTALL_XDSMPL_FILTERS_VISTA [INSTALL_XDSMPL_FILTERS_PRE_VISTA] @@ -135,5 +135,5 @@ xdscale.dll = 2 [Strings] Location="XPSDrv Sample Driver Location" -MS="Microsoft" -Microsoft="Microsoft" \ No newline at end of file +ManufacturerName="TODO-Set-Manufacturer" +ProviderString = "TODO-Set-Provider" \ No newline at end of file diff --git a/print/XPSDrvSmpl/src/common/precomp.h b/print/XPSDrvSmpl/src/common/precomp.h index 808f191bd..d871a9fd6 100644 --- a/print/XPSDrvSmpl/src/common/precomp.h +++ b/print/XPSDrvSmpl/src/common/precomp.h @@ -53,10 +53,13 @@ _Analysis_mode_(_Analysis_code_type_user_driver_) #include +#pragma warning (push) +#pragma warning (disable:4458) // // GDIPlus includes // #include +#pragma warning (pop) // // MSXML includes diff --git a/print/XPSDrvSmpl/src/common/xdsmplcmn.vcxproj b/print/XPSDrvSmpl/src/common/xdsmplcmn.vcxproj index 663cc1c77..841c6eb51 100644 --- a/print/XPSDrvSmpl/src/common/xdsmplcmn.vcxproj +++ b/print/XPSDrvSmpl/src/common/xdsmplcmn.vcxproj @@ -19,13 +19,13 @@ - {AF94DFED-1979-486B-A16E-7BBB14711EA0} + {5DBE2309-E951-4840-A138-3284BD2B61C6} $(MSBuildProjectName) false true Debug Win32 - {C8A4DBF6-79D6-43AC-94AA-E6844FAA3175} + {4A25C446-72DB-45A6-BF11-2CE5A71EEC20} @@ -400,7 +400,6 @@ - diff --git a/print/XPSDrvSmpl/src/common/xdsmplcmn.vcxproj.Filters b/print/XPSDrvSmpl/src/common/xdsmplcmn.vcxproj.Filters index 5129bb07f..eceba8756 100644 --- a/print/XPSDrvSmpl/src/common/xdsmplcmn.vcxproj.Filters +++ b/print/XPSDrvSmpl/src/common/xdsmplcmn.vcxproj.Filters @@ -3,15 +3,15 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {E12D861C-ACB5-4387-B204-B199497A1C30} + {D2467526-C66A-4432-AB2D-37D6FCB22F9A} h;hpp;hxx;hm;inl;inc;xsd - {DDCEEEF8-0261-4A4E-AD8A-4CDE8C263087} + {7E076CFA-88A7-46D6-9516-CC33F073A176} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {1E366A31-9605-41C9-A6D1-CEA007B63CEB} + {0F86A542-6832-411B-8749-19D19A6588A4} diff --git a/print/XPSDrvSmpl/src/filters/booklet/xdbook.vcxproj b/print/XPSDrvSmpl/src/filters/booklet/xdbook.vcxproj index 47c0edb37..9efc79181 100644 --- a/print/XPSDrvSmpl/src/filters/booklet/xdbook.vcxproj +++ b/print/XPSDrvSmpl/src/filters/booklet/xdbook.vcxproj @@ -19,13 +19,13 @@ - {49DD5381-63D8-41FB-B641-AFE52BFBBC32} + {0230CB91-5810-491F-83CA-BFEF080C100C} $(MSBuildProjectName) false true Debug Win32 - {4A9E6343-D066-42E3-BD7A-5EEE1DEFC23E} + {39193228-64BA-4C88-881C-FC45204FDE6B} @@ -291,7 +291,6 @@ - diff --git a/print/XPSDrvSmpl/src/filters/booklet/xdbook.vcxproj.Filters b/print/XPSDrvSmpl/src/filters/booklet/xdbook.vcxproj.Filters index bf3b77ae9..d8b67acb3 100644 --- a/print/XPSDrvSmpl/src/filters/booklet/xdbook.vcxproj.Filters +++ b/print/XPSDrvSmpl/src/filters/booklet/xdbook.vcxproj.Filters @@ -3,15 +3,15 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {27BD41E6-67B6-4534-96A7-FFC8FA13D6ED} + {41BC1ABC-6EF2-4707-B1DB-DA30BBF44B0B} h;hpp;hxx;hm;inl;inc;xsd - {6CF19858-A7CF-4450-B47E-44A2EE4773C2} + {F99132B3-716C-4EF8-86E1-685C21458AC0} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {D1BDA0AD-A88B-442F-81BE-EF261866789B} + {A07111D3-1DE5-499D-9303-5CCF01694085} diff --git a/print/XPSDrvSmpl/src/filters/color/XDColMan.vcxproj b/print/XPSDrvSmpl/src/filters/color/XDColMan.vcxproj index 2472150e8..8029349cc 100644 --- a/print/XPSDrvSmpl/src/filters/color/XDColMan.vcxproj +++ b/print/XPSDrvSmpl/src/filters/color/XDColMan.vcxproj @@ -19,13 +19,13 @@ - {AE18BFE5-EA00-477B-A051-F28DD3078679} + {9D2A1569-C11C-42CE-BF2F-86A10E869BB3} $(MSBuildProjectName) false true Debug Win32 - {CE9ADAB3-2A6D-489C-BA95-DEF7AC111A28} + {60685394-81B9-4D21-B2E4-D368D3A90309} @@ -361,7 +361,6 @@ - diff --git a/print/XPSDrvSmpl/src/filters/color/XDColMan.vcxproj.Filters b/print/XPSDrvSmpl/src/filters/color/XDColMan.vcxproj.Filters index 85d37d232..d11137669 100644 --- a/print/XPSDrvSmpl/src/filters/color/XDColMan.vcxproj.Filters +++ b/print/XPSDrvSmpl/src/filters/color/XDColMan.vcxproj.Filters @@ -3,15 +3,15 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {B1FD41D6-358B-4E6D-A97D-DB5CCC98735B} + {30ED88F3-0B8F-403A-AAC4-6FD656A9FF18} h;hpp;hxx;hm;inl;inc;xsd - {E2C67D9D-457A-45BF-A427-1139C5F38C03} + {286C3D15-1E8F-46FB-A481-F62D38B36F7C} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {08A09411-2B1D-4B63-9668-53608DD4A907} + {7DBC19A9-4E46-44A0-98FF-674261B9D1B1} diff --git a/print/XPSDrvSmpl/src/filters/common/xdfltcmn.vcxproj b/print/XPSDrvSmpl/src/filters/common/xdfltcmn.vcxproj index aa44d0472..359717c3b 100644 --- a/print/XPSDrvSmpl/src/filters/common/xdfltcmn.vcxproj +++ b/print/XPSDrvSmpl/src/filters/common/xdfltcmn.vcxproj @@ -19,13 +19,13 @@ - {DDC3CDA6-A188-4A21-A321-37E8A044EC22} + {FBC26C8C-2B0B-4394-8477-C40A418C5540} $(MSBuildProjectName) false true Debug Win32 - {6F603421-7288-4EC6-94EF-3DEF8C069604} + {EA9A3185-0004-4486-B94D-3E2F618CCC88} @@ -312,7 +312,6 @@ - diff --git a/print/XPSDrvSmpl/src/filters/common/xdfltcmn.vcxproj.Filters b/print/XPSDrvSmpl/src/filters/common/xdfltcmn.vcxproj.Filters index b1a98d475..ec1dace40 100644 --- a/print/XPSDrvSmpl/src/filters/common/xdfltcmn.vcxproj.Filters +++ b/print/XPSDrvSmpl/src/filters/common/xdfltcmn.vcxproj.Filters @@ -3,15 +3,15 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {5AE8E0BE-9490-4B67-8D0D-F1CE31744D4F} + {6CFEAA33-503C-4C96-96CC-C4FA37D46713} h;hpp;hxx;hm;inl;inc;xsd - {C415BB35-3B6A-474A-94EB-62A11CC60EFD} + {F836959F-7FDA-423C-AF5E-10951177293C} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {D121133F-D487-4B70-AD04-C77945981F69} + {C3AEE0DB-ED86-4457-8872-36EA5C7B7BDE} diff --git a/print/XPSDrvSmpl/src/filters/nup/xdnup.vcxproj b/print/XPSDrvSmpl/src/filters/nup/xdnup.vcxproj index 8be08f787..cbfa00b53 100644 --- a/print/XPSDrvSmpl/src/filters/nup/xdnup.vcxproj +++ b/print/XPSDrvSmpl/src/filters/nup/xdnup.vcxproj @@ -19,13 +19,13 @@ - {06548B4B-6104-4F36-8BAA-127D23F73CD4} + {5F377CEC-1783-4793-96E7-FF05DF1770D7} $(MSBuildProjectName) false true Debug Win32 - {A8424516-5DB1-4D7C-9199-604456506188} + {C5BED705-CBB1-4DE0-A7DB-C59456CEFF70} @@ -303,7 +303,6 @@ - diff --git a/print/XPSDrvSmpl/src/filters/nup/xdnup.vcxproj.Filters b/print/XPSDrvSmpl/src/filters/nup/xdnup.vcxproj.Filters index 83b4f1806..5c76e89cb 100644 --- a/print/XPSDrvSmpl/src/filters/nup/xdnup.vcxproj.Filters +++ b/print/XPSDrvSmpl/src/filters/nup/xdnup.vcxproj.Filters @@ -3,15 +3,15 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {BE7F4E72-895A-4B10-AED0-F7E2DA352886} + {A5C47951-E9C1-4D4B-B284-DA3689F28E10} h;hpp;hxx;hm;inl;inc;xsd - {8B40F3CB-A605-4B5A-847F-9AAF83A28B5E} + {992BC590-1057-449F-8D24-6C34906E4B64} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {577EA8D3-BCBF-4CCC-9F9F-73F6A6E3D3DF} + {183AEDCD-FB6D-4697-8AB3-B88AEBDB609F} diff --git a/print/XPSDrvSmpl/src/filters/precomp.h b/print/XPSDrvSmpl/src/filters/precomp.h index 640660ec5..682929957 100644 --- a/print/XPSDrvSmpl/src/filters/precomp.h +++ b/print/XPSDrvSmpl/src/filters/precomp.h @@ -64,10 +64,13 @@ _Analysis_mode_(_Analysis_code_type_user_driver_) // #include +#pragma warning (push) +#pragma warning (disable:4458) // // GDIPlus includes // #include +#pragma warning (pop) // // MSXML includes diff --git a/print/XPSDrvSmpl/src/filters/scaling/xdscale.vcxproj b/print/XPSDrvSmpl/src/filters/scaling/xdscale.vcxproj index 223908a04..213e55c06 100644 --- a/print/XPSDrvSmpl/src/filters/scaling/xdscale.vcxproj +++ b/print/XPSDrvSmpl/src/filters/scaling/xdscale.vcxproj @@ -19,13 +19,13 @@ - {268439B8-E924-47E4-BFE9-01DED439AFFE} + {5B69A33D-2119-49E7-961F-136998752761} $(MSBuildProjectName) false true Debug Win32 - {48DA3138-F4A4-4566-8485-21ABE98B0187} + {905E8EC2-5199-404B-AF08-89DA9CF386C0} @@ -313,7 +313,6 @@ - diff --git a/print/XPSDrvSmpl/src/filters/scaling/xdscale.vcxproj.Filters b/print/XPSDrvSmpl/src/filters/scaling/xdscale.vcxproj.Filters index 0bdfddcac..22d5c595e 100644 --- a/print/XPSDrvSmpl/src/filters/scaling/xdscale.vcxproj.Filters +++ b/print/XPSDrvSmpl/src/filters/scaling/xdscale.vcxproj.Filters @@ -3,15 +3,15 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {7FC932B7-0F2E-4826-B0FC-52E916DD3022} + {85451375-6C89-4F95-B93D-F1F0163900B0} h;hpp;hxx;hm;inl;inc;xsd - {7E202E93-C032-4246-8F2C-C782F069F11A} + {33866AA5-FA1F-4FEF-BA96-2075E91D7C65} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {0443A342-9E26-4F36-891C-FFEB01C44016} + {EE9A21C7-995D-4D8A-8429-CA5C53C97A46} diff --git a/print/XPSDrvSmpl/src/filters/watermark/xdwmark.vcxproj b/print/XPSDrvSmpl/src/filters/watermark/xdwmark.vcxproj index de8aa0e3c..6c64a17ad 100644 --- a/print/XPSDrvSmpl/src/filters/watermark/xdwmark.vcxproj +++ b/print/XPSDrvSmpl/src/filters/watermark/xdwmark.vcxproj @@ -19,13 +19,13 @@ - {89DE916B-9E07-4E1F-BFF9-3FC16328B0DD} + {EEEB99E0-6403-4D91-AC46-9894657F2295} $(MSBuildProjectName) false true Debug Win32 - {FC896AD8-0326-42FA-84FF-74DFFBBB8BCD} + {A9E144B4-A4B9-4D68-BA0E-7450D6DDF9EB} @@ -333,7 +333,6 @@ - diff --git a/print/XPSDrvSmpl/src/filters/watermark/xdwmark.vcxproj.Filters b/print/XPSDrvSmpl/src/filters/watermark/xdwmark.vcxproj.Filters index 4abb1cc8c..828b5ed99 100644 --- a/print/XPSDrvSmpl/src/filters/watermark/xdwmark.vcxproj.Filters +++ b/print/XPSDrvSmpl/src/filters/watermark/xdwmark.vcxproj.Filters @@ -3,15 +3,15 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {0CD3E75F-8D40-4264-B579-7CA300A9F466} + {078B1335-1447-4055-8387-D7B159DA59D2} h;hpp;hxx;hm;inl;inc;xsd - {E1482037-819F-42C2-8DC7-4E9CE5611C32} + {558B378E-D0B8-4CF8-B67B-65FC2ED9BF55} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {3F288E23-A157-4F1F-9D76-99A7C00BCE32} + {FDE994F7-292B-405E-AB33-74DD61427F8B} diff --git a/print/XPSDrvSmpl/src/filters/xdcont/xdcont.vcxproj b/print/XPSDrvSmpl/src/filters/xdcont/xdcont.vcxproj index 549334d6b..793adfc11 100644 --- a/print/XPSDrvSmpl/src/filters/xdcont/xdcont.vcxproj +++ b/print/XPSDrvSmpl/src/filters/xdcont/xdcont.vcxproj @@ -19,13 +19,13 @@ - {BCB6A109-EB2A-4D55-908E-22EE132F75AD} + {7B6DB6B7-543A-4FBB-B4A0-F9542C934D46} $(MSBuildProjectName) false true Debug Win32 - {AF55903D-6801-4AD7-AB79-C3BFA6DAA878} + {6FB0FD49-721E-488D-9AE0-C780174D33C7} @@ -268,7 +268,6 @@ - diff --git a/print/XPSDrvSmpl/src/filters/xdcont/xdcont.vcxproj.Filters b/print/XPSDrvSmpl/src/filters/xdcont/xdcont.vcxproj.Filters index f57f6b02f..3af481abf 100644 --- a/print/XPSDrvSmpl/src/filters/xdcont/xdcont.vcxproj.Filters +++ b/print/XPSDrvSmpl/src/filters/xdcont/xdcont.vcxproj.Filters @@ -3,15 +3,15 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {1DF780BE-6E8F-4617-AE75-8C606750D00D} + {D79D5C4F-F14F-43A1-90D5-9F37915E98C2} h;hpp;hxx;hm;inl;inc;xsd - {F116F7A3-4916-4038-841E-4A11113EDD9E} + {9FF2E739-4718-4353-96A2-A3B1B29B8320} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {12FD05BA-ED9A-4299-BA75-FA51A639EAD8} + {9E74AB77-9C68-4352-A399-7C84C91B4570} diff --git a/print/XPSDrvSmpl/src/ui/precomp.h b/print/XPSDrvSmpl/src/ui/precomp.h index 579abbab3..b9847ea03 100644 --- a/print/XPSDrvSmpl/src/ui/precomp.h +++ b/print/XPSDrvSmpl/src/ui/precomp.h @@ -59,10 +59,13 @@ _Analysis_mode_(_Analysis_code_type_user_driver_) // #include +#pragma warning (push) +#pragma warning (disable:4458) // // GDIPlus includes // #include +#pragma warning (pop) // // MSXML includes diff --git a/print/XPSDrvSmpl/src/ui/xdsmplui.vcxproj b/print/XPSDrvSmpl/src/ui/xdsmplui.vcxproj index 3183440c1..36fc9888f 100644 --- a/print/XPSDrvSmpl/src/ui/xdsmplui.vcxproj +++ b/print/XPSDrvSmpl/src/ui/xdsmplui.vcxproj @@ -19,13 +19,13 @@ - {20DDD6AA-CA85-46F3-8446-EDA619839273} + {F6900780-9A02-439C-A7F1-D10CEBEE2CA3} $(MSBuildProjectName) false true Debug Win32 - {8254BBF3-16D5-4993-9FDA-F4BD174AD743} + {81593338-DF34-4DE1-87C6-D4B982A4F31F} @@ -353,7 +353,6 @@ - diff --git a/print/XPSDrvSmpl/src/ui/xdsmplui.vcxproj.Filters b/print/XPSDrvSmpl/src/ui/xdsmplui.vcxproj.Filters index 335597357..4f99ee115 100644 --- a/print/XPSDrvSmpl/src/ui/xdsmplui.vcxproj.Filters +++ b/print/XPSDrvSmpl/src/ui/xdsmplui.vcxproj.Filters @@ -3,15 +3,15 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {7ABCDA75-F2B6-43F8-9F0C-5C23F4A47C6B} + {1FA60AAB-05FB-4829-B12B-C4BA8445AF0E} h;hpp;hxx;hm;inl;inc;xsd - {C1E80132-1D96-4658-94D3-6D189ABDA762} + {59474171-2BF7-42E9-9048-8221E611FB17} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {EFFEF9F4-65D8-4348-BF3A-8F4E00BA8219} + {F872E78F-66C1-44FF-B1D5-955CB3E4C001} diff --git a/print/XpsRasFilter/XpsRasFilter.sln b/print/XpsRasFilter/XpsRasFilter.sln index db7de92c0..f19d849ec 100644 --- a/print/XpsRasFilter/XpsRasFilter.sln +++ b/print/XpsRasFilter/XpsRasFilter.sln @@ -3,7 +3,7 @@ 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", "{7D6A9E48-4B15-454E-846E-580BD64DDAF9}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xpsrasfilter", "src\xpsrasfilter.vcxproj", "{275AABC8-E628-42BE-902B-9FBA423934D3}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {7D6A9E48-4B15-454E-846E-580BD64DDAF9}.Debug|Win32.ActiveCfg = Debug|Win32 - {7D6A9E48-4B15-454E-846E-580BD64DDAF9}.Debug|Win32.Build.0 = Debug|Win32 - {7D6A9E48-4B15-454E-846E-580BD64DDAF9}.Release|Win32.ActiveCfg = Release|Win32 - {7D6A9E48-4B15-454E-846E-580BD64DDAF9}.Release|Win32.Build.0 = Release|Win32 - {7D6A9E48-4B15-454E-846E-580BD64DDAF9}.Debug|x64.ActiveCfg = Debug|x64 - {7D6A9E48-4B15-454E-846E-580BD64DDAF9}.Debug|x64.Build.0 = Debug|x64 - {7D6A9E48-4B15-454E-846E-580BD64DDAF9}.Release|x64.ActiveCfg = Release|x64 - {7D6A9E48-4B15-454E-846E-580BD64DDAF9}.Release|x64.Build.0 = Release|x64 + {275AABC8-E628-42BE-902B-9FBA423934D3}.Debug|Win32.ActiveCfg = Debug|Win32 + {275AABC8-E628-42BE-902B-9FBA423934D3}.Debug|Win32.Build.0 = Debug|Win32 + {275AABC8-E628-42BE-902B-9FBA423934D3}.Release|Win32.ActiveCfg = Release|Win32 + {275AABC8-E628-42BE-902B-9FBA423934D3}.Release|Win32.Build.0 = Release|Win32 + {275AABC8-E628-42BE-902B-9FBA423934D3}.Debug|x64.ActiveCfg = Debug|x64 + {275AABC8-E628-42BE-902B-9FBA423934D3}.Debug|x64.Build.0 = Debug|x64 + {275AABC8-E628-42BE-902B-9FBA423934D3}.Release|x64.ActiveCfg = Release|x64 + {275AABC8-E628-42BE-902B-9FBA423934D3}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/print/XpsRasFilter/install/xpsrassmpl.inf b/print/XpsRasFilter/install/xpsrassmpl.inf index 0683802a7..3e7a82244 100644 --- a/print/XpsRasFilter/install/xpsrassmpl.inf +++ b/print/XpsRasFilter/install/xpsrassmpl.inf @@ -18,22 +18,22 @@ ; [Version] Signature="$Windows NT$" -Provider=%MS% +Provider=%ProviderString% 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 +%ManufacturerName%=Standard,NTx86.6.1,NTia64.6.1,NTamd64.6.1 -[Microsoft.NTx86.6.1] +[Standard.NTx86.6.1] "XPSRas WDK Sample Driver" = INSTALL_FILTER -[Microsoft.NTia64.6.1] +[Standard.NTia64.6.1] "XPSRas WDK Sample Driver" = INSTALL_FILTER -[Microsoft.NTamd64.6.1] +[Standard.NTamd64.6.1] "XPSRas WDK Sample Driver" = INSTALL_FILTER [INSTALL_FILTER] @@ -83,5 +83,5 @@ xpsrasfilter.dll = 2 [Strings] Location="XPSRas WDK Sample Driver" -MS="Microsoft" -Microsoft="Microsoft" \ No newline at end of file +ProviderString = "TODO-Set-Provider" +ManufacturerName="TODO-Set-Manufacturer" \ No newline at end of file diff --git a/print/XpsRasFilter/src/xpsrasfilter.vcxproj b/print/XpsRasFilter/src/xpsrasfilter.vcxproj index 94c791b0a..3512848fd 100644 --- a/print/XpsRasFilter/src/xpsrasfilter.vcxproj +++ b/print/XpsRasFilter/src/xpsrasfilter.vcxproj @@ -19,11 +19,11 @@ - {7D6A9E48-4B15-454E-846E-580BD64DDAF9} + {275AABC8-E628-42BE-902B-9FBA423934D3} $(MSBuildProjectName) Debug Win32 - {05F4D921-7C2D-4B7E-92D8-44E0887DBD76} + {2B2A5B0E-B1F4-4870-B5D4-B28852CCE89C} @@ -335,7 +335,6 @@ - diff --git a/print/XpsRasFilter/src/xpsrasfilter.vcxproj.Filters b/print/XpsRasFilter/src/xpsrasfilter.vcxproj.Filters index e42cec79c..8c2d7410b 100644 --- a/print/XpsRasFilter/src/xpsrasfilter.vcxproj.Filters +++ b/print/XpsRasFilter/src/xpsrasfilter.vcxproj.Filters @@ -3,15 +3,15 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {9A48947D-56D1-4B56-A0D9-6405186AF999} + {961E183F-B10B-4CE0-BA81-28D7E70FF523} h;hpp;hxx;hm;inl;inc;xsd - {CE57A622-98E6-419C-A031-506952D9F0B2} + {9D54B4AA-BFFC-412B-AE66-716E64234BA0} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {F6700635-AB37-4734-B021-AAA6C38430A0} + {877BFD21-8F5C-484F-A241-CDA24DB19D6B} diff --git a/print/autoconfig/AutoCnfg.inf b/print/autoconfig/AutoCnfg.inf index c76422e9b..172092195 100644 --- a/print/autoconfig/AutoCnfg.inf +++ b/print/autoconfig/AutoCnfg.inf @@ -7,7 +7,7 @@ [Version] Signature="$Windows NT$" -Provider=%MS% +Provider=%ProviderString% LayoutFile=ntprint.inf ClassGUID={4D36E979-E325-11CE-BFC1-08002BE10318} Class=Printer @@ -21,7 +21,7 @@ CatalogFile=AutoCnfg.cat ; that we will display in the Dialog box ; [Manufacturer] -"Microsoft"=Microsoft, NTx86, NTamd64, NTia64 +%ManufacturerName%=Standard, NTx86, NTamd64, NTia64 ; ; Model sections. @@ -30,15 +30,15 @@ CatalogFile=AutoCnfg.cat ; [Manufacturer] section, above. The models will be displayed in the order ; that they appear in the INF file. ; -[Microsoft.NTx86] +[Standard.NTx86] "Unidrv AutoConfiguration Sample" = INSTALL_AUTO_CONFIG.UNI,DO_NOT_USE_THIS_HWID1 "PScript5 AutoConfiguration Sample" = INSTALL_AUTO_CONFIG.PS,DO_NOT_USE_THIS_HWID2 -[Microsoft.NTamd64] +[Standard.NTamd64] "Unidrv AutoConfiguration Sample" = INSTALL_AUTO_CONFIG.UNI,DO_NOT_USE_THIS_HWID1 "PScript5 AutoConfiguration Sample" = INSTALL_AUTO_CONFIG.PS,DO_NOT_USE_THIS_HWID2 -[Microsoft.NTia64] +[Standard.NTia64] "Unidrv AutoConfiguration Sample" = INSTALL_AUTO_CONFIG.UNI,DO_NOT_USE_THIS_HWID1 "PScript5 AutoConfiguration Sample" = INSTALL_AUTO_CONFIG.PS,DO_NOT_USE_THIS_HWID2 @@ -107,5 +107,6 @@ DefaultDestDir=66000 ; Localizable Strings ; [Strings] -MS="Microsoft DDK Sample" +ProviderString = "TODO-Set-Provider" +ManufacturerName="TODO-Set-Manufacturer" AutoConfigSample="AutoConfiguration Samples" diff --git a/print/autoconfig/AutoConfig.sln b/print/autoconfig/AutoConfig.sln index bf0ee61f2..a098a1511 100644 --- a/print/autoconfig/AutoConfig.sln +++ b/print/autoconfig/AutoConfig.sln @@ -3,13 +3,13 @@ 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", "{4259D226-70CB-4CC3-A9C5-6BE1F4A92C76}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Package", "Package", "{0AEA9999-6E3A-4942-AF1E-6F5030EAF466}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AutoConfig", "AutoConfig", "{05648001-0846-4B8A-BC4C-C2B1099A81ED}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AutoConfig", "AutoConfig", "{62C0F048-4EA3-4474-8E70-79BE855C10F3}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "package", "Package\package.VcxProj", "{2413CDC5-F87A-4C93-A58F-E7AB497C9863}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "package", "Package\package.VcxProj", "{1307660E-E51C-463A-8D10-46CFEFD4BC1F}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AutoConfig", "AutoConfig.vcxproj", "{3AC4649E-0746-4815-BE4D-CA5F491BA78C}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AutoConfig", "AutoConfig.vcxproj", "{84059BE0-133C-4AA0-8290-B5A1A6A2B815}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -19,29 +19,29 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {2413CDC5-F87A-4C93-A58F-E7AB497C9863}.Debug|Win32.ActiveCfg = Debug|Win32 - {2413CDC5-F87A-4C93-A58F-E7AB497C9863}.Debug|Win32.Build.0 = Debug|Win32 - {2413CDC5-F87A-4C93-A58F-E7AB497C9863}.Release|Win32.ActiveCfg = Release|Win32 - {2413CDC5-F87A-4C93-A58F-E7AB497C9863}.Release|Win32.Build.0 = Release|Win32 - {2413CDC5-F87A-4C93-A58F-E7AB497C9863}.Debug|x64.ActiveCfg = Debug|x64 - {2413CDC5-F87A-4C93-A58F-E7AB497C9863}.Debug|x64.Build.0 = Debug|x64 - {2413CDC5-F87A-4C93-A58F-E7AB497C9863}.Release|x64.ActiveCfg = Release|x64 - {2413CDC5-F87A-4C93-A58F-E7AB497C9863}.Release|x64.Build.0 = Release|x64 - {3AC4649E-0746-4815-BE4D-CA5F491BA78C}.Debug|Win32.ActiveCfg = Debug|Win32 - {3AC4649E-0746-4815-BE4D-CA5F491BA78C}.Debug|Win32.Build.0 = Debug|Win32 - {3AC4649E-0746-4815-BE4D-CA5F491BA78C}.Release|Win32.ActiveCfg = Release|Win32 - {3AC4649E-0746-4815-BE4D-CA5F491BA78C}.Release|Win32.Build.0 = Release|Win32 - {3AC4649E-0746-4815-BE4D-CA5F491BA78C}.Debug|x64.ActiveCfg = Debug|x64 - {3AC4649E-0746-4815-BE4D-CA5F491BA78C}.Debug|x64.Build.0 = Debug|x64 - {3AC4649E-0746-4815-BE4D-CA5F491BA78C}.Release|x64.ActiveCfg = Release|x64 - {3AC4649E-0746-4815-BE4D-CA5F491BA78C}.Release|x64.Build.0 = Release|x64 + {1307660E-E51C-463A-8D10-46CFEFD4BC1F}.Debug|Win32.ActiveCfg = Debug|Win32 + {1307660E-E51C-463A-8D10-46CFEFD4BC1F}.Debug|Win32.Build.0 = Debug|Win32 + {1307660E-E51C-463A-8D10-46CFEFD4BC1F}.Release|Win32.ActiveCfg = Release|Win32 + {1307660E-E51C-463A-8D10-46CFEFD4BC1F}.Release|Win32.Build.0 = Release|Win32 + {1307660E-E51C-463A-8D10-46CFEFD4BC1F}.Debug|x64.ActiveCfg = Debug|x64 + {1307660E-E51C-463A-8D10-46CFEFD4BC1F}.Debug|x64.Build.0 = Debug|x64 + {1307660E-E51C-463A-8D10-46CFEFD4BC1F}.Release|x64.ActiveCfg = Release|x64 + {1307660E-E51C-463A-8D10-46CFEFD4BC1F}.Release|x64.Build.0 = Release|x64 + {84059BE0-133C-4AA0-8290-B5A1A6A2B815}.Debug|Win32.ActiveCfg = Debug|Win32 + {84059BE0-133C-4AA0-8290-B5A1A6A2B815}.Debug|Win32.Build.0 = Debug|Win32 + {84059BE0-133C-4AA0-8290-B5A1A6A2B815}.Release|Win32.ActiveCfg = Release|Win32 + {84059BE0-133C-4AA0-8290-B5A1A6A2B815}.Release|Win32.Build.0 = Release|Win32 + {84059BE0-133C-4AA0-8290-B5A1A6A2B815}.Debug|x64.ActiveCfg = Debug|x64 + {84059BE0-133C-4AA0-8290-B5A1A6A2B815}.Debug|x64.Build.0 = Debug|x64 + {84059BE0-133C-4AA0-8290-B5A1A6A2B815}.Release|x64.ActiveCfg = Release|x64 + {84059BE0-133C-4AA0-8290-B5A1A6A2B815}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {2413CDC5-F87A-4C93-A58F-E7AB497C9863} = {4259D226-70CB-4CC3-A9C5-6BE1F4A92C76} - {3AC4649E-0746-4815-BE4D-CA5F491BA78C} = {05648001-0846-4B8A-BC4C-C2B1099A81ED} - {4259D226-70CB-4CC3-A9C5-6BE1F4A92C76} = {05648001-0846-4B8A-BC4C-C2B1099A81ED} + {1307660E-E51C-463A-8D10-46CFEFD4BC1F} = {0AEA9999-6E3A-4942-AF1E-6F5030EAF466} + {84059BE0-133C-4AA0-8290-B5A1A6A2B815} = {62C0F048-4EA3-4474-8E70-79BE855C10F3} + {0AEA9999-6E3A-4942-AF1E-6F5030EAF466} = {62C0F048-4EA3-4474-8E70-79BE855C10F3} EndGlobalSection EndGlobal diff --git a/print/autoconfig/AutoConfig.vcxproj b/print/autoconfig/AutoConfig.vcxproj index 2a0d49229..e250f92e7 100644 --- a/print/autoconfig/AutoConfig.vcxproj +++ b/print/autoconfig/AutoConfig.vcxproj @@ -19,13 +19,13 @@ - {3AC4649E-0746-4815-BE4D-CA5F491BA78C} + {84059BE0-133C-4AA0-8290-B5A1A6A2B815} $(MSBuildProjectName) false true Debug Win32 - {5361FE42-60AC-4D05-AA79-9928B15AD552} + {5B35F6AC-7657-4BB2-8B68-C630F92EAC44} @@ -128,7 +128,6 @@ - diff --git a/print/autoconfig/AutoConfig.vcxproj.Filters b/print/autoconfig/AutoConfig.vcxproj.Filters index 3b9f54c41..e95a31ee5 100644 --- a/print/autoconfig/AutoConfig.vcxproj.Filters +++ b/print/autoconfig/AutoConfig.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {CC0020D1-88BF-4AA4-8E5C-08FA0FC25EB2} + {52FFD518-97B8-4DEE-83B6-1078C1F8AE26} h;hpp;hxx;hm;inl;inc;xsd - {125816B7-55A7-4B3D-AD8D-27FC65BA0320} + {E20DE14D-5AE1-4C49-BA29-82BCF304ECCD} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {207C912A-A306-4C9E-8530-7D55313903B7} + {F1512473-81C1-4901-A19C-30FDD8D1AA25} inf;inv;inx;mof;mc; - {9174D187-924B-4401-A505-E3744534AA84} + {441F6CA0-262E-4B74-BCA7-94431F2C10EA} \ No newline at end of file diff --git a/print/autoconfig/Package/package.VcxProj b/print/autoconfig/Package/package.VcxProj index fe3b9df5d..1b4ccb2e0 100644 --- a/print/autoconfig/Package/package.VcxProj +++ b/print/autoconfig/Package/package.VcxProj @@ -18,6 +18,11 @@ x64 + + + {84059BE0-133C-4AA0-8290-B5A1A6A2B815} + + WindowsKernelModeDriver10.0 Utility @@ -27,8 +32,8 @@ - {2413CDC5-F87A-4C93-A58F-E7AB497C9863} - {721ABA50-F9B7-47C3-AE46-D758904898FE} + {1307660E-E51C-463A-8D10-46CFEFD4BC1F} + {F89C283D-E536-425B-B0D3-198EC54B9C41} $(MSBuildProjectName) @@ -73,15 +78,6 @@ - - - - - - - {3AC4649E-0746-4815-BE4D-CA5F491BA78C} - - diff --git a/print/autoconfig/Package/package.VcxProj.Filters b/print/autoconfig/Package/package.VcxProj.Filters index cca2dd89f..96bfbf488 100644 --- a/print/autoconfig/Package/package.VcxProj.Filters +++ b/print/autoconfig/Package/package.VcxProj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {B8DF1233-DF16-44AA-9934-5E306B564E71} + {F975B5A6-8258-44FA-A725-71F166C3FEE9} h;hpp;hxx;hm;inl;inc;xsd - {252924E9-6243-4097-8F0E-531E56389AE2} + {064E7CE7-CEC3-4279-A37A-4091AE696A5D} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {98522D6D-8655-4174-8A06-C4BA5DFE1A53} + {F0079264-B967-4798-9E1C-B541E2765805} inf;inv;inx;mof;mc; - {57663FE0-B30B-4B3E-929F-05EEDA920360} + {9318EDCF-6CAB-4750-A7A3-F5998AF7F526} \ No newline at end of file diff --git a/print/cpsuisam/cpsuisam.sln b/print/cpsuisam/cpsuisam.sln index b5112f28f..839a1074a 100644 --- a/print/cpsuisam/cpsuisam.sln +++ b/print/cpsuisam/cpsuisam.sln @@ -3,7 +3,7 @@ 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", "{A0AB72F8-43E4-4DF9-9BF8-5BE5943F5087}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cpsuisam", "cpsuisam.vcxproj", "{077CC61A-B8F6-44EF-8556-62EB8CF33F0F}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {A0AB72F8-43E4-4DF9-9BF8-5BE5943F5087}.Debug|Win32.ActiveCfg = Debug|Win32 - {A0AB72F8-43E4-4DF9-9BF8-5BE5943F5087}.Debug|Win32.Build.0 = Debug|Win32 - {A0AB72F8-43E4-4DF9-9BF8-5BE5943F5087}.Release|Win32.ActiveCfg = Release|Win32 - {A0AB72F8-43E4-4DF9-9BF8-5BE5943F5087}.Release|Win32.Build.0 = Release|Win32 - {A0AB72F8-43E4-4DF9-9BF8-5BE5943F5087}.Debug|x64.ActiveCfg = Debug|x64 - {A0AB72F8-43E4-4DF9-9BF8-5BE5943F5087}.Debug|x64.Build.0 = Debug|x64 - {A0AB72F8-43E4-4DF9-9BF8-5BE5943F5087}.Release|x64.ActiveCfg = Release|x64 - {A0AB72F8-43E4-4DF9-9BF8-5BE5943F5087}.Release|x64.Build.0 = Release|x64 + {077CC61A-B8F6-44EF-8556-62EB8CF33F0F}.Debug|Win32.ActiveCfg = Debug|Win32 + {077CC61A-B8F6-44EF-8556-62EB8CF33F0F}.Debug|Win32.Build.0 = Debug|Win32 + {077CC61A-B8F6-44EF-8556-62EB8CF33F0F}.Release|Win32.ActiveCfg = Release|Win32 + {077CC61A-B8F6-44EF-8556-62EB8CF33F0F}.Release|Win32.Build.0 = Release|Win32 + {077CC61A-B8F6-44EF-8556-62EB8CF33F0F}.Debug|x64.ActiveCfg = Debug|x64 + {077CC61A-B8F6-44EF-8556-62EB8CF33F0F}.Debug|x64.Build.0 = Debug|x64 + {077CC61A-B8F6-44EF-8556-62EB8CF33F0F}.Release|x64.ActiveCfg = Release|x64 + {077CC61A-B8F6-44EF-8556-62EB8CF33F0F}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/print/cpsuisam/cpsuisam.vcxproj b/print/cpsuisam/cpsuisam.vcxproj index 8303081ad..b5d292d52 100644 --- a/print/cpsuisam/cpsuisam.vcxproj +++ b/print/cpsuisam/cpsuisam.vcxproj @@ -19,11 +19,11 @@ - {A0AB72F8-43E4-4DF9-9BF8-5BE5943F5087} + {077CC61A-B8F6-44EF-8556-62EB8CF33F0F} $(MSBuildProjectName) Debug Win32 - {9796197E-B5DF-412B-B5C7-53D2157DAEA4} + {F065B7FE-BE74-49A4-B776-D44DE3B7466D} @@ -241,7 +241,6 @@ - diff --git a/print/cpsuisam/cpsuisam.vcxproj.Filters b/print/cpsuisam/cpsuisam.vcxproj.Filters index 84a24cc99..647c9f9ad 100644 --- a/print/cpsuisam/cpsuisam.vcxproj.Filters +++ b/print/cpsuisam/cpsuisam.vcxproj.Filters @@ -3,15 +3,15 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {B311EA89-CEDD-4A4B-8E38-ED0D084243BD} + {A1B8DE9F-3E23-478B-8C26-A16A2F9ED6F8} h;hpp;hxx;hm;inl;inc;xsd - {2F9561F0-63CF-4857-87E0-DA7247F9B6D3} + {97C418E9-5841-4EBC-A478-E2C5C529A251} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {C6380BED-A054-4AB8-85AE-2990733CF187} + {1873E8F4-28C3-4E6C-B41A-9B72414DC680} diff --git a/print/v4PrintDriverSamples/PrinterExtensionSample/PrinterExtensionSample.sln b/print/v4PrintDriverSamples/PrinterExtensionSample/PrinterExtensionSample.sln index 355378eae..758bc0706 100644 --- a/print/v4PrintDriverSamples/PrinterExtensionSample/PrinterExtensionSample.sln +++ b/print/v4PrintDriverSamples/PrinterExtensionSample/PrinterExtensionSample.sln @@ -3,9 +3,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0 MinimumVisualStudioVersion = 12.0 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ExtensionSample", "ExtensionSample", "{46FA638A-C5E2-4257-9A15-1E0AD088D8C7}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ExtensionSample", "ExtensionSample", "{7A7BF1F8-8D36-48A9-8217-4210C77777FF}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "PrinterExtensionLibrary", "PrinterExtensionLibrary", "{7996DD32-8A65-4596-877B-804470DCCAE6}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "PrinterExtensionLibrary", "PrinterExtensionLibrary", "{D39459E3-626A-4D27-9EF1-38705C2CA93F}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PrinterExtensionSample", "ExtensionSample\PrinterExtensionSample.csproj", "{CF554A99-6889-4B86-934F-B6AADBFEFC01}" EndProject @@ -80,7 +80,7 @@ Global HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {CF554A99-6889-4B86-934F-B6AADBFEFC01} = {46FA638A-C5E2-4257-9A15-1E0AD088D8C7} - {D8DA0C4D-F972-4546-9068-8EB256F222F7} = {7996DD32-8A65-4596-877B-804470DCCAE6} + {CF554A99-6889-4B86-934F-B6AADBFEFC01} = {7A7BF1F8-8D36-48A9-8217-4210C77777FF} + {D8DA0C4D-F972-4546-9068-8EB256F222F7} = {D39459E3-626A-4D27-9EF1-38705C2CA93F} EndGlobalSection EndGlobal diff --git a/print/v4PrintDriverSamples/v4PrintDriver-ConstraintScript/ConstraintScript.sln b/print/v4PrintDriverSamples/v4PrintDriver-ConstraintScript/ConstraintScript.sln index c767c9bbd..7f8be1f41 100644 --- a/print/v4PrintDriverSamples/v4PrintDriver-ConstraintScript/ConstraintScript.sln +++ b/print/v4PrintDriverSamples/v4PrintDriver-ConstraintScript/ConstraintScript.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0 MinimumVisualStudioVersion = 12.0 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ConstraintScript", "ConstraintScript.vcxproj", "{CB1E04C7-6B76-4C0D-8022-9C8DDCA2CAF7}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ConstraintScript", "ConstraintScript.vcxproj", "{76CA47A0-FCC7-4E34-91AC-1B5E224E19AD}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {CB1E04C7-6B76-4C0D-8022-9C8DDCA2CAF7}.Debug|Win32.ActiveCfg = Debug|Win32 - {CB1E04C7-6B76-4C0D-8022-9C8DDCA2CAF7}.Debug|Win32.Build.0 = Debug|Win32 - {CB1E04C7-6B76-4C0D-8022-9C8DDCA2CAF7}.Release|Win32.ActiveCfg = Release|Win32 - {CB1E04C7-6B76-4C0D-8022-9C8DDCA2CAF7}.Release|Win32.Build.0 = Release|Win32 - {CB1E04C7-6B76-4C0D-8022-9C8DDCA2CAF7}.Debug|x64.ActiveCfg = Debug|x64 - {CB1E04C7-6B76-4C0D-8022-9C8DDCA2CAF7}.Debug|x64.Build.0 = Debug|x64 - {CB1E04C7-6B76-4C0D-8022-9C8DDCA2CAF7}.Release|x64.ActiveCfg = Release|x64 - {CB1E04C7-6B76-4C0D-8022-9C8DDCA2CAF7}.Release|x64.Build.0 = Release|x64 + {76CA47A0-FCC7-4E34-91AC-1B5E224E19AD}.Debug|Win32.ActiveCfg = Debug|Win32 + {76CA47A0-FCC7-4E34-91AC-1B5E224E19AD}.Debug|Win32.Build.0 = Debug|Win32 + {76CA47A0-FCC7-4E34-91AC-1B5E224E19AD}.Release|Win32.ActiveCfg = Release|Win32 + {76CA47A0-FCC7-4E34-91AC-1B5E224E19AD}.Release|Win32.Build.0 = Release|Win32 + {76CA47A0-FCC7-4E34-91AC-1B5E224E19AD}.Debug|x64.ActiveCfg = Debug|x64 + {76CA47A0-FCC7-4E34-91AC-1B5E224E19AD}.Debug|x64.Build.0 = Debug|x64 + {76CA47A0-FCC7-4E34-91AC-1B5E224E19AD}.Release|x64.ActiveCfg = Release|x64 + {76CA47A0-FCC7-4E34-91AC-1B5E224E19AD}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/print/v4PrintDriverSamples/v4PrintDriver-ConstraintScript/ConstraintScript.vcxproj b/print/v4PrintDriverSamples/v4PrintDriver-ConstraintScript/ConstraintScript.vcxproj index 0ceaac0e0..045f8bd5b 100644 --- a/print/v4PrintDriverSamples/v4PrintDriver-ConstraintScript/ConstraintScript.vcxproj +++ b/print/v4PrintDriverSamples/v4PrintDriver-ConstraintScript/ConstraintScript.vcxproj @@ -19,12 +19,12 @@ - {CB1E04C7-6B76-4C0D-8022-9C8DDCA2CAF7} + {76CA47A0-FCC7-4E34-91AC-1B5E224E19AD} $(MSBuildProjectName) false Debug Win32 - {817B63FA-2B1C-4CBD-9CA7-7BCEECDA4B4F} + {961B903B-595B-431F-8FF2-E40BEE1C4678} @@ -118,7 +118,6 @@ - diff --git a/print/v4PrintDriverSamples/v4PrintDriver-ConstraintScript/ConstraintScript.vcxproj.Filters b/print/v4PrintDriverSamples/v4PrintDriver-ConstraintScript/ConstraintScript.vcxproj.Filters index f711c36be..85c296988 100644 --- a/print/v4PrintDriverSamples/v4PrintDriver-ConstraintScript/ConstraintScript.vcxproj.Filters +++ b/print/v4PrintDriverSamples/v4PrintDriver-ConstraintScript/ConstraintScript.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {32D0EF97-FC2B-4E1F-9E36-6AD986687F61} + {84E25432-DD4E-49C2-AA48-B9C0706D80E4} h;hpp;hxx;hm;inl;inc;xsd - {29FB739F-C9CC-464B-A7B5-42F18A18F286} + {149158A2-638A-4BD9-84AB-1FFFAA0A4341} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {D3DA9AC3-690E-42E3-8BE5-002D7099D700} + {B7468846-AED8-4516-8214-F323DC7841F4} inf;inv;inx;mof;mc; - {F90BC5C4-C6B9-417A-AD02-5507849101EF} + {AD7E68BA-F3EC-4AC8-8677-7B316C3337DA} \ No newline at end of file diff --git a/print/v4PrintDriverSamples/v4PrintDriver-HostBasedSampleDriver/HostBasedSampleDriver.sln b/print/v4PrintDriverSamples/v4PrintDriver-HostBasedSampleDriver/HostBasedSampleDriver.sln index e08b4c015..0b2d30fd0 100644 --- a/print/v4PrintDriverSamples/v4PrintDriver-HostBasedSampleDriver/HostBasedSampleDriver.sln +++ b/print/v4PrintDriverSamples/v4PrintDriver-HostBasedSampleDriver/HostBasedSampleDriver.sln @@ -3,11 +3,11 @@ 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", "{29C54707-CBCF-4EE7-8664-5A8809CD30D9}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Package", "Package", "{E471F8AB-0B10-45C2-B788-E5E441CFCDC9}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "package", "Package\package.VcxProj", "{F1081D0D-31B7-4B48-9AD6-6C7336ADEA61}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "package", "Package\package.VcxProj", "{24F15521-602D-47EF-ABFE-5765AB32E694}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "HostBasedSampleDriver", "HostBasedSampleDriver.vcxproj", "{C4923416-0CA0-4D73-98F2-BC10C70C6689}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "HostBasedSampleDriver", "HostBasedSampleDriver.vcxproj", "{A78FB2A6-2C70-4886-A438-FDA24E741537}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -17,27 +17,27 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {F1081D0D-31B7-4B48-9AD6-6C7336ADEA61}.Debug|Win32.ActiveCfg = Debug|Win32 - {F1081D0D-31B7-4B48-9AD6-6C7336ADEA61}.Debug|Win32.Build.0 = Debug|Win32 - {F1081D0D-31B7-4B48-9AD6-6C7336ADEA61}.Release|Win32.ActiveCfg = Release|Win32 - {F1081D0D-31B7-4B48-9AD6-6C7336ADEA61}.Release|Win32.Build.0 = Release|Win32 - {F1081D0D-31B7-4B48-9AD6-6C7336ADEA61}.Debug|x64.ActiveCfg = Debug|x64 - {F1081D0D-31B7-4B48-9AD6-6C7336ADEA61}.Debug|x64.Build.0 = Debug|x64 - {F1081D0D-31B7-4B48-9AD6-6C7336ADEA61}.Release|x64.ActiveCfg = Release|x64 - {F1081D0D-31B7-4B48-9AD6-6C7336ADEA61}.Release|x64.Build.0 = Release|x64 - {C4923416-0CA0-4D73-98F2-BC10C70C6689}.Debug|Win32.ActiveCfg = Debug|Win32 - {C4923416-0CA0-4D73-98F2-BC10C70C6689}.Debug|Win32.Build.0 = Debug|Win32 - {C4923416-0CA0-4D73-98F2-BC10C70C6689}.Release|Win32.ActiveCfg = Release|Win32 - {C4923416-0CA0-4D73-98F2-BC10C70C6689}.Release|Win32.Build.0 = Release|Win32 - {C4923416-0CA0-4D73-98F2-BC10C70C6689}.Debug|x64.ActiveCfg = Debug|x64 - {C4923416-0CA0-4D73-98F2-BC10C70C6689}.Debug|x64.Build.0 = Debug|x64 - {C4923416-0CA0-4D73-98F2-BC10C70C6689}.Release|x64.ActiveCfg = Release|x64 - {C4923416-0CA0-4D73-98F2-BC10C70C6689}.Release|x64.Build.0 = Release|x64 + {24F15521-602D-47EF-ABFE-5765AB32E694}.Debug|Win32.ActiveCfg = Debug|Win32 + {24F15521-602D-47EF-ABFE-5765AB32E694}.Debug|Win32.Build.0 = Debug|Win32 + {24F15521-602D-47EF-ABFE-5765AB32E694}.Release|Win32.ActiveCfg = Release|Win32 + {24F15521-602D-47EF-ABFE-5765AB32E694}.Release|Win32.Build.0 = Release|Win32 + {24F15521-602D-47EF-ABFE-5765AB32E694}.Debug|x64.ActiveCfg = Debug|x64 + {24F15521-602D-47EF-ABFE-5765AB32E694}.Debug|x64.Build.0 = Debug|x64 + {24F15521-602D-47EF-ABFE-5765AB32E694}.Release|x64.ActiveCfg = Release|x64 + {24F15521-602D-47EF-ABFE-5765AB32E694}.Release|x64.Build.0 = Release|x64 + {A78FB2A6-2C70-4886-A438-FDA24E741537}.Debug|Win32.ActiveCfg = Debug|Win32 + {A78FB2A6-2C70-4886-A438-FDA24E741537}.Debug|Win32.Build.0 = Debug|Win32 + {A78FB2A6-2C70-4886-A438-FDA24E741537}.Release|Win32.ActiveCfg = Release|Win32 + {A78FB2A6-2C70-4886-A438-FDA24E741537}.Release|Win32.Build.0 = Release|Win32 + {A78FB2A6-2C70-4886-A438-FDA24E741537}.Debug|x64.ActiveCfg = Debug|x64 + {A78FB2A6-2C70-4886-A438-FDA24E741537}.Debug|x64.Build.0 = Debug|x64 + {A78FB2A6-2C70-4886-A438-FDA24E741537}.Release|x64.ActiveCfg = Release|x64 + {A78FB2A6-2C70-4886-A438-FDA24E741537}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {F1081D0D-31B7-4B48-9AD6-6C7336ADEA61} = {29C54707-CBCF-4EE7-8664-5A8809CD30D9} + {24F15521-602D-47EF-ABFE-5765AB32E694} = {E471F8AB-0B10-45C2-B788-E5E441CFCDC9} EndGlobalSection EndGlobal diff --git a/print/v4PrintDriverSamples/v4PrintDriver-HostBasedSampleDriver/HostBasedSampleDriver.vcxproj b/print/v4PrintDriverSamples/v4PrintDriver-HostBasedSampleDriver/HostBasedSampleDriver.vcxproj index 4056d79e4..11b44ad03 100644 --- a/print/v4PrintDriverSamples/v4PrintDriver-HostBasedSampleDriver/HostBasedSampleDriver.vcxproj +++ b/print/v4PrintDriverSamples/v4PrintDriver-HostBasedSampleDriver/HostBasedSampleDriver.vcxproj @@ -19,13 +19,13 @@ - {C4923416-0CA0-4D73-98F2-BC10C70C6689} + {A78FB2A6-2C70-4886-A438-FDA24E741537} $(MSBuildProjectName) false true Debug Win32 - {192B3507-9B3B-4F9D-8757-FB236A173A4C} + {D222C8EC-FFAE-4F5B-9FF9-EC2D0D916D18} @@ -130,7 +130,6 @@ - diff --git a/print/v4PrintDriverSamples/v4PrintDriver-HostBasedSampleDriver/HostBasedSampleDriver.vcxproj.Filters b/print/v4PrintDriverSamples/v4PrintDriver-HostBasedSampleDriver/HostBasedSampleDriver.vcxproj.Filters index 32c3897e1..ecebc8207 100644 --- a/print/v4PrintDriverSamples/v4PrintDriver-HostBasedSampleDriver/HostBasedSampleDriver.vcxproj.Filters +++ b/print/v4PrintDriverSamples/v4PrintDriver-HostBasedSampleDriver/HostBasedSampleDriver.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {805C7C5E-A9EF-42CA-8F3A-175C6CCDFD53} + {26A53244-E3EA-4D6F-9B29-5AAD4B47D63F} h;hpp;hxx;hm;inl;inc;xsd - {A2CB3D9E-1732-4426-8E35-7341BE09E5BD} + {F14350A1-7C38-4AD9-B85E-7D2200E10B4D} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {71B188DF-173E-402E-9FB3-D936E33C3F93} + {26731250-FFAE-42F3-BD80-933169EB7B05} inf;inv;inx;mof;mc; - {7A9313F9-BD09-46CB-8B04-D289D34FFDE0} + {9640FB3F-4A16-42E7-85C4-CC9B63A1BC84} diff --git a/print/v4PrintDriverSamples/v4PrintDriver-HostBasedSampleDriver/Package/package.VcxProj b/print/v4PrintDriverSamples/v4PrintDriver-HostBasedSampleDriver/Package/package.VcxProj index d1f97a316..b1c76f360 100644 --- a/print/v4PrintDriverSamples/v4PrintDriver-HostBasedSampleDriver/Package/package.VcxProj +++ b/print/v4PrintDriverSamples/v4PrintDriver-HostBasedSampleDriver/Package/package.VcxProj @@ -18,6 +18,11 @@ x64 + + + {A78FB2A6-2C70-4886-A438-FDA24E741537} + + WindowsKernelModeDriver10.0 Utility @@ -27,8 +32,8 @@ - {F1081D0D-31B7-4B48-9AD6-6C7336ADEA61} - {6D0283E4-148C-44BA-98FA-A3D4EEBF561E} + {24F15521-602D-47EF-ABFE-5765AB32E694} + {7B686CC4-2BC7-439F-BB22-27DC9D1EE60D} $(MSBuildProjectName) @@ -73,15 +78,6 @@ - - - - - - - {C4923416-0CA0-4D73-98F2-BC10C70C6689} - - diff --git a/print/v4PrintDriverSamples/v4PrintDriver-HostBasedSampleDriver/Package/package.VcxProj.Filters b/print/v4PrintDriverSamples/v4PrintDriver-HostBasedSampleDriver/Package/package.VcxProj.Filters index a9ed8cc61..f5ff27db4 100644 --- a/print/v4PrintDriverSamples/v4PrintDriver-HostBasedSampleDriver/Package/package.VcxProj.Filters +++ b/print/v4PrintDriverSamples/v4PrintDriver-HostBasedSampleDriver/Package/package.VcxProj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {82BBB724-F089-4441-8ECE-61571F0CD9ED} + {F52AC47D-602A-4466-8B14-C1FB06EF1B63} h;hpp;hxx;hm;inl;inc;xsd - {24C56EA9-2A47-4CDD-ADAF-AC403C6FDDD1} + {73FCE8FA-3B30-4BFB-A03A-24194DE06E89} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {C7204588-58DE-470B-B532-39199F0C5930} + {EBC9E52A-4E4D-4BD8-A6D9-7586927E1DB1} inf;inv;inx;mof;mc; - {40292FDA-3C17-4357-8861-B57A7A726884} + {4A1D0022-452B-4379-AD3C-CF6F708AC3ED} \ No newline at end of file diff --git a/print/v4PrintDriverSamples/v4PrintDriver-HostBasedSampleDriver/usb_host_based_sample.inf b/print/v4PrintDriverSamples/v4PrintDriver-HostBasedSampleDriver/usb_host_based_sample.inf index 4297a35a3..92bf72b29 100644 --- a/print/v4PrintDriverSamples/v4PrintDriver-HostBasedSampleDriver/usb_host_based_sample.inf +++ b/print/v4PrintDriverSamples/v4PrintDriver-HostBasedSampleDriver/usb_host_based_sample.inf @@ -7,7 +7,7 @@ ; [Version] Signature="$Windows NT$" -Provider=%MS% +Provider=%ProviderString% CatalogFile=usb_host_based_sample.cat ClassGUID={4D36E979-E325-11CE-BFC1-08002BE10318} Class=Printer @@ -21,7 +21,7 @@ ClassVer=4.0 ; that we will display in the Dialog box ; [Manufacturer] -%MS%=Microsoft, NTx86, NTamd64, NTarm, NTarm64 +%ManufacturerName%=Standard, NTx86, NTamd64, NTarm, NTarm64 ; ; Model sections @@ -31,17 +31,17 @@ ClassVer=4.0 ; that they appear in the INF file. ; -[Microsoft.NTx86] -"Microsoft USB Host Based Sample Driver" = USB_HOST_BASED_SAMPLE, DO_NOT_USE_THIS_HWID1, +[Standard.NTx86] +"USB Host Based Sample Driver" = USB_HOST_BASED_SAMPLE, DO_NOT_USE_THIS_HWID1, -[Microsoft.NTamd64] -"Microsoft USB Host Based Sample Driver" = USB_HOST_BASED_SAMPLE, DO_NOT_USE_THIS_HWID1 +[Standard.NTamd64] +"USB Host Based Sample Driver" = USB_HOST_BASED_SAMPLE, DO_NOT_USE_THIS_HWID1 -[Microsoft.NTarm] -"Microsoft USB Host Based Sample Driver" = USB_HOST_BASED_SAMPLE, DO_NOT_USE_THIS_HWID1 +[Standard.NTarm] +"USB Host Based Sample Driver" = USB_HOST_BASED_SAMPLE, DO_NOT_USE_THIS_HWID1 -[Microsoft.NTarm64] -"Microsoft USB Host Based Sample Driver" = USB_HOST_BASED_SAMPLE, DO_NOT_USE_THIS_HWID1 +[Standard.NTarm64] +"USB Host Based Sample Driver" = USB_HOST_BASED_SAMPLE, DO_NOT_USE_THIS_HWID1 ; ; Installer Sections @@ -105,4 +105,5 @@ usb_host_based_sample_events.xml = 1 ; [Strings] Disk1="." -MS="Microsoft" +ProviderString = "TODO-Set-Provider" +ManufacturerName="TODO-Set-Manufacturer" \ No newline at end of file diff --git a/print/v4PrintDriverSamples/v4PrintDriver-USBMon-Bidi-Extension/USBMon-Bidi-Extension.sln b/print/v4PrintDriverSamples/v4PrintDriver-USBMon-Bidi-Extension/USBMon-Bidi-Extension.sln index d9dd00fb5..a50d381d8 100644 --- a/print/v4PrintDriverSamples/v4PrintDriver-USBMon-Bidi-Extension/USBMon-Bidi-Extension.sln +++ b/print/v4PrintDriverSamples/v4PrintDriver-USBMon-Bidi-Extension/USBMon-Bidi-Extension.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0 MinimumVisualStudioVersion = 12.0 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "USBMon-Bidi-Extension", "USBMon-Bidi-Extension.vcxproj", "{AAFFC04F-26D7-4533-B15E-1E1687EB77D3}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "USBMon-Bidi-Extension", "USBMon-Bidi-Extension.vcxproj", "{FFCFDDDA-766F-4158-A22C-9A26D626C1DB}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {AAFFC04F-26D7-4533-B15E-1E1687EB77D3}.Debug|Win32.ActiveCfg = Debug|Win32 - {AAFFC04F-26D7-4533-B15E-1E1687EB77D3}.Debug|Win32.Build.0 = Debug|Win32 - {AAFFC04F-26D7-4533-B15E-1E1687EB77D3}.Release|Win32.ActiveCfg = Release|Win32 - {AAFFC04F-26D7-4533-B15E-1E1687EB77D3}.Release|Win32.Build.0 = Release|Win32 - {AAFFC04F-26D7-4533-B15E-1E1687EB77D3}.Debug|x64.ActiveCfg = Debug|x64 - {AAFFC04F-26D7-4533-B15E-1E1687EB77D3}.Debug|x64.Build.0 = Debug|x64 - {AAFFC04F-26D7-4533-B15E-1E1687EB77D3}.Release|x64.ActiveCfg = Release|x64 - {AAFFC04F-26D7-4533-B15E-1E1687EB77D3}.Release|x64.Build.0 = Release|x64 + {FFCFDDDA-766F-4158-A22C-9A26D626C1DB}.Debug|Win32.ActiveCfg = Debug|Win32 + {FFCFDDDA-766F-4158-A22C-9A26D626C1DB}.Debug|Win32.Build.0 = Debug|Win32 + {FFCFDDDA-766F-4158-A22C-9A26D626C1DB}.Release|Win32.ActiveCfg = Release|Win32 + {FFCFDDDA-766F-4158-A22C-9A26D626C1DB}.Release|Win32.Build.0 = Release|Win32 + {FFCFDDDA-766F-4158-A22C-9A26D626C1DB}.Debug|x64.ActiveCfg = Debug|x64 + {FFCFDDDA-766F-4158-A22C-9A26D626C1DB}.Debug|x64.Build.0 = Debug|x64 + {FFCFDDDA-766F-4158-A22C-9A26D626C1DB}.Release|x64.ActiveCfg = Release|x64 + {FFCFDDDA-766F-4158-A22C-9A26D626C1DB}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/print/v4PrintDriverSamples/v4PrintDriver-USBMon-Bidi-Extension/USBMon-Bidi-Extension.vcxproj b/print/v4PrintDriverSamples/v4PrintDriver-USBMon-Bidi-Extension/USBMon-Bidi-Extension.vcxproj index d6ff88cad..bec1f351b 100644 --- a/print/v4PrintDriverSamples/v4PrintDriver-USBMon-Bidi-Extension/USBMon-Bidi-Extension.vcxproj +++ b/print/v4PrintDriverSamples/v4PrintDriver-USBMon-Bidi-Extension/USBMon-Bidi-Extension.vcxproj @@ -19,12 +19,12 @@ - {AAFFC04F-26D7-4533-B15E-1E1687EB77D3} + {FFCFDDDA-766F-4158-A22C-9A26D626C1DB} $(MSBuildProjectName) false Debug Win32 - {7DC67506-ED7D-464A-8950-91E1A3485356} + {7C4C2545-C904-4089-9287-4402D04091AC} @@ -119,7 +119,6 @@ - diff --git a/print/v4PrintDriverSamples/v4PrintDriver-USBMon-Bidi-Extension/USBMon-Bidi-Extension.vcxproj.Filters b/print/v4PrintDriverSamples/v4PrintDriver-USBMon-Bidi-Extension/USBMon-Bidi-Extension.vcxproj.Filters index bc3478709..459967ead 100644 --- a/print/v4PrintDriverSamples/v4PrintDriver-USBMon-Bidi-Extension/USBMon-Bidi-Extension.vcxproj.Filters +++ b/print/v4PrintDriverSamples/v4PrintDriver-USBMon-Bidi-Extension/USBMon-Bidi-Extension.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {08A89D46-9794-49C4-A5DA-AED702601A98} + {100A6515-E3CE-44BD-9E1C-F9A96A502B10} h;hpp;hxx;hm;inl;inc;xsd - {98EBFD2A-3A4F-479C-964C-8F3F91CF1ED9} + {2B73D328-40DC-4998-BB56-36A3878E78E8} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {827B912F-6627-485D-ACE9-ACF6A014186B} + {905A2606-E80B-4276-AA83-D3A52542BAE7} inf;inv;inx;mof;mc; - {C469AF55-3C55-4086-BC4C-7597B99B1528} + {3051E2BC-E27E-461F-9913-AEFAE1198D1E} diff --git a/print/v4PrintDriverSamples/v4PrintDriver-WSDMon-Bidi-Extension/WSDMon-Bidi-Extension.sln b/print/v4PrintDriverSamples/v4PrintDriver-WSDMon-Bidi-Extension/WSDMon-Bidi-Extension.sln index 24a86e72f..07e561016 100644 --- a/print/v4PrintDriverSamples/v4PrintDriver-WSDMon-Bidi-Extension/WSDMon-Bidi-Extension.sln +++ b/print/v4PrintDriverSamples/v4PrintDriver-WSDMon-Bidi-Extension/WSDMon-Bidi-Extension.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0 MinimumVisualStudioVersion = 12.0 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WSDMon-Bidi-Extension", "WSDMon-Bidi-Extension.vcxproj", "{9629A925-8512-41CB-AD2E-F730F7D457AB}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WSDMon-Bidi-Extension", "WSDMon-Bidi-Extension.vcxproj", "{80A97640-584A-4DFC-AD66-0DD33D1D3853}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {9629A925-8512-41CB-AD2E-F730F7D457AB}.Debug|Win32.ActiveCfg = Debug|Win32 - {9629A925-8512-41CB-AD2E-F730F7D457AB}.Debug|Win32.Build.0 = Debug|Win32 - {9629A925-8512-41CB-AD2E-F730F7D457AB}.Release|Win32.ActiveCfg = Release|Win32 - {9629A925-8512-41CB-AD2E-F730F7D457AB}.Release|Win32.Build.0 = Release|Win32 - {9629A925-8512-41CB-AD2E-F730F7D457AB}.Debug|x64.ActiveCfg = Debug|x64 - {9629A925-8512-41CB-AD2E-F730F7D457AB}.Debug|x64.Build.0 = Debug|x64 - {9629A925-8512-41CB-AD2E-F730F7D457AB}.Release|x64.ActiveCfg = Release|x64 - {9629A925-8512-41CB-AD2E-F730F7D457AB}.Release|x64.Build.0 = Release|x64 + {80A97640-584A-4DFC-AD66-0DD33D1D3853}.Debug|Win32.ActiveCfg = Debug|Win32 + {80A97640-584A-4DFC-AD66-0DD33D1D3853}.Debug|Win32.Build.0 = Debug|Win32 + {80A97640-584A-4DFC-AD66-0DD33D1D3853}.Release|Win32.ActiveCfg = Release|Win32 + {80A97640-584A-4DFC-AD66-0DD33D1D3853}.Release|Win32.Build.0 = Release|Win32 + {80A97640-584A-4DFC-AD66-0DD33D1D3853}.Debug|x64.ActiveCfg = Debug|x64 + {80A97640-584A-4DFC-AD66-0DD33D1D3853}.Debug|x64.Build.0 = Debug|x64 + {80A97640-584A-4DFC-AD66-0DD33D1D3853}.Release|x64.ActiveCfg = Release|x64 + {80A97640-584A-4DFC-AD66-0DD33D1D3853}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/print/v4PrintDriverSamples/v4PrintDriver-WSDMon-Bidi-Extension/WSDMon-Bidi-Extension.vcxproj b/print/v4PrintDriverSamples/v4PrintDriver-WSDMon-Bidi-Extension/WSDMon-Bidi-Extension.vcxproj index 136ac8d4a..ccce41a42 100644 --- a/print/v4PrintDriverSamples/v4PrintDriver-WSDMon-Bidi-Extension/WSDMon-Bidi-Extension.vcxproj +++ b/print/v4PrintDriverSamples/v4PrintDriver-WSDMon-Bidi-Extension/WSDMon-Bidi-Extension.vcxproj @@ -19,12 +19,12 @@ - {9629A925-8512-41CB-AD2E-F730F7D457AB} + {80A97640-584A-4DFC-AD66-0DD33D1D3853} $(MSBuildProjectName) false Debug Win32 - {6B788CC8-550C-41A8-8182-DDD6FF0808F5} + {DC30E67C-A2CC-41B7-BF2B-5DBF568AF468} @@ -118,7 +118,6 @@ - diff --git a/print/v4PrintDriverSamples/v4PrintDriver-WSDMon-Bidi-Extension/WSDMon-Bidi-Extension.vcxproj.Filters b/print/v4PrintDriverSamples/v4PrintDriver-WSDMon-Bidi-Extension/WSDMon-Bidi-Extension.vcxproj.Filters index aafacd9e2..ed8624552 100644 --- a/print/v4PrintDriverSamples/v4PrintDriver-WSDMon-Bidi-Extension/WSDMon-Bidi-Extension.vcxproj.Filters +++ b/print/v4PrintDriverSamples/v4PrintDriver-WSDMon-Bidi-Extension/WSDMon-Bidi-Extension.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {011840BF-3888-4C28-B34B-DD44E4396358} + {CDFE8644-195E-45B2-8CCC-8896C4A2EDBC} h;hpp;hxx;hm;inl;inc;xsd - {F231759E-CDB4-470A-AEFE-E7D23D6E2882} + {BB9E0536-334F-48B8-B8ED-0C618F8391C4} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {D3DF558A-B1AE-4E01-B213-14060FF647F1} + {490F11F6-6EF1-4424-92B7-CAABBEFE3C1D} inf;inv;inx;mof;mc; - {B51D78AF-436E-453E-A710-1E92BC35C51F} + {B8612D84-B141-4997-BA9F-B0209185A4CA} diff --git a/sd/miniport/sdhc/README.md b/sd/miniport/sdhc/README.md index a1fafe5ce..b5c350933 100644 --- a/sd/miniport/sdhc/README.md +++ b/sd/miniport/sdhc/README.md @@ -3,8 +3,8 @@ Standard SD Host Controller Miniport This is a sample for a Secure Digital (SD) Host Controller miniport driver. The driver works in conjunction with sdport.sys, which implements SD/SDIO/eMMC protocol and WDM interfaces, to provide the host register interface. -## Universal Windows Driver Compliant -This sample builds a Universal Windows Driver. It uses only APIs and DDIs that are included in OneCoreUAP. +## Universal Compliant +This sample builds a Windows Universal driver. It uses only APIs and DDIs that are included in Windows Core. This driver, sdhc.sys, provides a functional miniport implementation for a standard SD host controller. However, it does not have support for many more recent features such as: diff --git a/sd/miniport/sdhc/inbox/sdhc.vcxproj b/sd/miniport/sdhc/inbox/sdhc.vcxproj index 04459aa79..cd4c42738 100644 --- a/sd/miniport/sdhc/inbox/sdhc.vcxproj +++ b/sd/miniport/sdhc/inbox/sdhc.vcxproj @@ -19,11 +19,11 @@ - {696C1C9C-405C-4675-B344-B5FF53BAB93A} + {2619C7A0-9593-4B9D-B7B6-39315EB8B689} $(MSBuildProjectName) Debug Win32 - {014CCC56-7545-4368-82D4-5DBD32E969C6} + {2621B937-3FC0-4A3C-8DCD-507E2A3C8CCF} @@ -142,7 +142,6 @@ - diff --git a/sd/miniport/sdhc/inbox/sdhc.vcxproj.Filters b/sd/miniport/sdhc/inbox/sdhc.vcxproj.Filters index 398abeabd..b79c2a1f6 100644 --- a/sd/miniport/sdhc/inbox/sdhc.vcxproj.Filters +++ b/sd/miniport/sdhc/inbox/sdhc.vcxproj.Filters @@ -3,25 +3,22 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {9BCD4C03-CF88-47B5-BB78-F6C8FF86F96C} + {0D7C22F4-3833-4FF6-AEC7-EB6D0AA2C27A} h;hpp;hxx;hm;inl;inc;xsd - {BCF0B6A6-7B9C-4BF8-A56B-6FE9AEF11C02} + {32B2D023-A9AF-4DF5-A1A0-6D1B8BD5DE92} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {70DA8E30-872B-447B-8759-726797BFE240} + {AC9D724E-A9F5-4CB5-86A4-5DBA92452CF4} inf;inv;inx;mof;mc; - {428B9C34-C4C4-4109-B1A9-9054F8F7C20A} + {4A3A774C-B573-4EC1-9364-469F243356D0} - - Driver Files - Driver Files diff --git a/sd/miniport/sdhc/sdhc.c b/sd/miniport/sdhc/sdhc.c index c7d85274a..35403a306 100644 --- a/sd/miniport/sdhc/sdhc.c +++ b/sd/miniport/sdhc/sdhc.c @@ -86,6 +86,7 @@ Return Value: InitializationData.RequestDpc = SdhcRequestDpc; InitializationData.SaveContext = SdhcSaveContext; InitializationData.RestoreContext = SdhcRestoreContext; + InitializationData.PowerControlCallback = SdhcPoFxPowerControlCallback; // // Provide the number of slots and their size. @@ -295,6 +296,15 @@ Return value: (CapabilitiesReg.SystemBus64Support ? sizeof(ULONGLONG) : sizeof(ULONG)) - 1; + // + // For small transfers (SDIO) we want to use PIO for performance, + // for both reads and writes <= 64 bytes. + // + + Capabilities->PioTransferMaxThreshold = 64; + Capabilities->Flags.UsePioForRead = TRUE; + Capabilities->Flags.UsePioForWrite = TRUE; + if (CapabilitiesReg.Adma2Support) { Capabilities->Supported.ScatterGatherDma = 1; } @@ -356,6 +366,7 @@ Return value: Capabilities->Supported.SoftwareTuning = 1; } + Capabilities->Supported.AutoCmd12 = 1; if (SpecVersion >= SDHC_SPEC_VERSION_3) { Capabilities->Supported.AutoCmd23 = 1; } @@ -511,6 +522,13 @@ Return value: break; + case SdSetBlockGapInterrupt: + Status = SdhcEnableBlockGapInterrupt( + SdhcExtension, + BusOperation->Parameters.BlockGapIntEnabled); + + break; + case SdExecuteTuning: Status = SdhcExecuteTuning(SdhcExtension); break; @@ -959,6 +977,60 @@ Return value: UNREFERENCED_PARAMETER(PrivateExtension); } +NTSTATUS +SdhcPoFxPowerControlCallback( + _In_ PSD_MINIPORT Miniport, + _In_ LPCGUID PowerControlCode, + _In_reads_bytes_opt_(InputBufferSize) PVOID InputBuffer, + _In_ SIZE_T InputBufferSize, + _Out_writes_bytes_opt_(OutputBufferSize) PVOID OutputBuffer, + _In_ SIZE_T OutputBufferSize, + _Out_opt_ PSIZE_T BytesReturned + ) + +/*++ + +Routine Description: + + Handle any PoFxPowerControl callbacks. + +Arguments: + + Miniport - Miniport interface for the controller. + + PowerControlCode - GUID defining a platform-specific PoFxPowerControl + method. + + InputBuffer - Buffer containing any input arguments. + + InputBufferSize - Size of InputBuffer in bytes. + + OutputBuffer - Buffer containing any output results. + + OutputBufferSize - Size of OutputBuffer in bytes. + + BytesReturned - Number of bytes returned. + +Return value: + + NTSTATUS + +--*/ + +{ + + UNREFERENCED_PARAMETER(Miniport); + UNREFERENCED_PARAMETER(PowerControlCode); + UNREFERENCED_PARAMETER(InputBuffer); + UNREFERENCED_PARAMETER(InputBufferSize); + UNREFERENCED_PARAMETER(OutputBuffer); + UNREFERENCED_PARAMETER(OutputBufferSize); + UNREFERENCED_PARAMETER(BytesReturned); + + return STATUS_NOT_IMPLEMENTED; +} + + //----------------------------------------------------------------------------- // Host routine implementations. //----------------------------------------------------------------------------- @@ -1753,6 +1825,48 @@ Return value: return STATUS_SUCCESS; } +NTSTATUS +SdhcEnableBlockGapInterrupt( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ BOOLEAN Enable + ) + +/*++ + +Routine Description: + + Enables block gap interrupts for SDIO cards in 4-bit mode. + Caller has responsibility to make sure this is only called for + appropriate devices. + +Arguments: + + SdhcExtension - Host controller specific driver context. + + Enable - Enable or disable block gap interrupts. + +Return value: + + None. + +--*/ + +{ + + UCHAR Control; + + Control = SdhcReadRegisterUchar(SdhcExtension, SDHC_BLOCKGAP_CONTROL); + if (Enable) { + Control |= SDHC_BGC_INTERRUPT_ENABLE; + + } else { + Control &= ~SDHC_BGC_INTERRUPT_ENABLE; + } + + SdhcWriteRegisterUchar(SdhcExtension, SDHC_BLOCKGAP_CONTROL, Control); + return STATUS_SUCCESS; +} + VOID SdhcSetBlockGapControl( _In_ PSDHC_EXTENSION SdhcExtension, diff --git a/sd/miniport/sdhc/sdhc.h b/sd/miniport/sdhc/sdhc.h index e16044b7a..74d0a7c2d 100644 --- a/sd/miniport/sdhc/sdhc.h +++ b/sd/miniport/sdhc/sdhc.h @@ -1520,6 +1520,20 @@ SdhcRestoreContext( ); */ +SDPORT_PO_FX_POWER_CONTROL_CALLBACK SdhcPoFxPowerControlCallback; +/* +NTSTATUS +SdhcPoFxPowerControlCallback( + _In_ PSD_MINIPORT Miniport, + _In_ LPCGUID PowerControlCode, + _In_reads_bytes_opt_(InputBufferSize) PVOID InputBuffer, + _In_ SIZE_T InputBufferSize, + _Out_writes_bytes_opt_(OutputBufferSize) PVOID OutputBuffer, + _In_ SIZE_T OutputBufferSize, + _Out_opt_ PSIZE_T BytesReturned + ) +*/ + //----------------------------------------------------------------------------- // Hardware access routines. //----------------------------------------------------------------------------- @@ -1589,6 +1603,12 @@ SdhcSetPresetValue( _In_ BOOLEAN Enable ); +NTSTATUS +SdhcEnableBlockGapInterrupt( + _In_ PSDHC_EXTENSION SdhcExtension, + _In_ BOOLEAN Enable + ); + VOID SdhcSetBlockGapControl( _In_ PSDHC_EXTENSION SdhcExtension, diff --git a/sd/miniport/sdhc/sdhc.inx b/sd/miniport/sdhc/sdhc.inx index 7e7f7abba852ac2f9d013a294540381685243854..8bfb88a6868221e3e35965b9bafbf6c1449cc263 100644 GIT binary patch delta 260 zcmewqzQcBd0i$dHLlHwhLm5LRLkdGGkR8lW0wgmT@)*(?R3}F=&gVB|P++hHVi%xj zF+=v`C0zzI9h1wos{yx$Nd^D_ literal 11250 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 delta 177 zcmbQCxI<~fIYv%i1}+9`1_cJsiKiuf{TT8YN*GcZiW#gJAQBLglOYr+mdTLEkPcMp z0#s4PkPK9t2UMrP5W-N&PyiHDU{GR6LC8&RrMlynS7Z|lUWH! u|7TO4s8BaKgXs>lFGKL=c;-lM5My#9zvAQx{5-6-VAX|;!kY#7I~V~ob|pjr diff --git a/sd/sdiomars/mars.vcxproj b/sd/sdiomars/mars.vcxproj index 37b47d09d..f974e79c1 100644 --- a/sd/sdiomars/mars.vcxproj +++ b/sd/sdiomars/mars.vcxproj @@ -19,12 +19,12 @@ - {DF923AC2-B46E-4D14-9B5D-C55006B8AECE} + {B1304510-A6A8-4646-9C48-EDDD5ABEB540} $(MSBuildProjectName) 1 Debug Win32 - {42F7E138-B841-4F20-B19B-9AAB78D9B502} + {8C68F76C-ECAC-4889-BC6F-C1FFAB76AD9B} @@ -166,7 +166,6 @@ - diff --git a/sd/sdiomars/mars.vcxproj.Filters b/sd/sdiomars/mars.vcxproj.Filters index d691397d6..267b51220 100644 --- a/sd/sdiomars/mars.vcxproj.Filters +++ b/sd/sdiomars/mars.vcxproj.Filters @@ -3,25 +3,22 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {E3DC03C6-1CAC-44C6-9FC4-0EE64DBBAD0A} + {375A493C-03C2-4C20-B2F4-236061BA63EE} h;hpp;hxx;hm;inl;inc;xsd - {F0C0BF8A-3AA1-4D6F-858E-25C590B9BF47} + {C1A39E19-0808-487E-8B2B-34B586AA6F50} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {27E4CB77-8F83-4C7D-90AA-87026013ACDA} + {74D04A5E-CCE1-40C8-A807-963F781F4607} inf;inv;inx;mof;mc; - {43996541-713E-472F-87E9-8B79F7EC2A98} + {23C205B5-BAEA-4EC6-BF45-4D7FA8BF1E93} - - Driver Files - Driver Files diff --git a/sd/sdiomars/sdiomars.sln b/sd/sdiomars/sdiomars.sln index 2a6d1cdb2..b0db009e6 100644 --- a/sd/sdiomars/sdiomars.sln +++ b/sd/sdiomars/sdiomars.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0 MinimumVisualStudioVersion = 12.0 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mars", "mars.vcxproj", "{DF923AC2-B46E-4D14-9B5D-C55006B8AECE}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mars", "mars.vcxproj", "{B1304510-A6A8-4646-9C48-EDDD5ABEB540}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {DF923AC2-B46E-4D14-9B5D-C55006B8AECE}.Debug|Win32.ActiveCfg = Debug|Win32 - {DF923AC2-B46E-4D14-9B5D-C55006B8AECE}.Debug|Win32.Build.0 = Debug|Win32 - {DF923AC2-B46E-4D14-9B5D-C55006B8AECE}.Release|Win32.ActiveCfg = Release|Win32 - {DF923AC2-B46E-4D14-9B5D-C55006B8AECE}.Release|Win32.Build.0 = Release|Win32 - {DF923AC2-B46E-4D14-9B5D-C55006B8AECE}.Debug|x64.ActiveCfg = Debug|x64 - {DF923AC2-B46E-4D14-9B5D-C55006B8AECE}.Debug|x64.Build.0 = Debug|x64 - {DF923AC2-B46E-4D14-9B5D-C55006B8AECE}.Release|x64.ActiveCfg = Release|x64 - {DF923AC2-B46E-4D14-9B5D-C55006B8AECE}.Release|x64.Build.0 = Release|x64 + {B1304510-A6A8-4646-9C48-EDDD5ABEB540}.Debug|Win32.ActiveCfg = Debug|Win32 + {B1304510-A6A8-4646-9C48-EDDD5ABEB540}.Debug|Win32.Build.0 = Debug|Win32 + {B1304510-A6A8-4646-9C48-EDDD5ABEB540}.Release|Win32.ActiveCfg = Release|Win32 + {B1304510-A6A8-4646-9C48-EDDD5ABEB540}.Release|Win32.Build.0 = Release|Win32 + {B1304510-A6A8-4646-9C48-EDDD5ABEB540}.Debug|x64.ActiveCfg = Debug|x64 + {B1304510-A6A8-4646-9C48-EDDD5ABEB540}.Debug|x64.Build.0 = Debug|x64 + {B1304510-A6A8-4646-9C48-EDDD5ABEB540}.Release|x64.ActiveCfg = Release|x64 + {B1304510-A6A8-4646-9C48-EDDD5ABEB540}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/security/elam/elam.sln b/security/elam/elam.sln index 53aa1f57a..87c20172b 100644 --- a/security/elam/elam.sln +++ b/security/elam/elam.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0 MinimumVisualStudioVersion = 12.0 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "elamsample", "elamsample.vcxproj", "{691F9AAE-40A3-4950-8529-1C817ED43A61}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "elamsample", "elamsample.vcxproj", "{0E33FA65-3CCE-467E-911D-B14F2B58EB80}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {691F9AAE-40A3-4950-8529-1C817ED43A61}.Debug|Win32.ActiveCfg = Debug|Win32 - {691F9AAE-40A3-4950-8529-1C817ED43A61}.Debug|Win32.Build.0 = Debug|Win32 - {691F9AAE-40A3-4950-8529-1C817ED43A61}.Release|Win32.ActiveCfg = Release|Win32 - {691F9AAE-40A3-4950-8529-1C817ED43A61}.Release|Win32.Build.0 = Release|Win32 - {691F9AAE-40A3-4950-8529-1C817ED43A61}.Debug|x64.ActiveCfg = Debug|x64 - {691F9AAE-40A3-4950-8529-1C817ED43A61}.Debug|x64.Build.0 = Debug|x64 - {691F9AAE-40A3-4950-8529-1C817ED43A61}.Release|x64.ActiveCfg = Release|x64 - {691F9AAE-40A3-4950-8529-1C817ED43A61}.Release|x64.Build.0 = Release|x64 + {0E33FA65-3CCE-467E-911D-B14F2B58EB80}.Debug|Win32.ActiveCfg = Debug|Win32 + {0E33FA65-3CCE-467E-911D-B14F2B58EB80}.Debug|Win32.Build.0 = Debug|Win32 + {0E33FA65-3CCE-467E-911D-B14F2B58EB80}.Release|Win32.ActiveCfg = Release|Win32 + {0E33FA65-3CCE-467E-911D-B14F2B58EB80}.Release|Win32.Build.0 = Release|Win32 + {0E33FA65-3CCE-467E-911D-B14F2B58EB80}.Debug|x64.ActiveCfg = Debug|x64 + {0E33FA65-3CCE-467E-911D-B14F2B58EB80}.Debug|x64.Build.0 = Debug|x64 + {0E33FA65-3CCE-467E-911D-B14F2B58EB80}.Release|x64.ActiveCfg = Release|x64 + {0E33FA65-3CCE-467E-911D-B14F2B58EB80}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/security/elam/elamsample.vcxproj b/security/elam/elamsample.vcxproj index fd4a3004f..316557211 100644 --- a/security/elam/elamsample.vcxproj +++ b/security/elam/elamsample.vcxproj @@ -19,12 +19,12 @@ - {691F9AAE-40A3-4950-8529-1C817ED43A61} + {0E33FA65-3CCE-467E-911D-B14F2B58EB80} $(MSBuildProjectName) 1 Debug Win32 - {06CC2AEF-6CBC-49CE-B869-459F80D28EC0} + {82F4051E-C769-44EF-87B0-48E79838EC69} @@ -127,7 +127,6 @@ - diff --git a/security/elam/elamsample.vcxproj.Filters b/security/elam/elamsample.vcxproj.Filters index 6e8c26bb1..cebe2d7e0 100644 --- a/security/elam/elamsample.vcxproj.Filters +++ b/security/elam/elamsample.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {D19514A8-1E7E-43BE-80B3-850E5BFCD858} + {2AEB0142-8F7A-418D-B59A-C9F62C775A85} h;hpp;hxx;hm;inl;inc;xsd - {379127B6-7854-4E76-996A-4C2A635EE774} + {33D6F5BF-1988-4BEC-BA1C-5F4C303FA17B} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {EF2BCD73-C364-4E77-BB95-15C633EAB208} + {E49CA825-9807-40E4-A35D-482004F9DF7F} inf;inv;inx;mof;mc; - {A4C7ECB5-5FA4-4E9E-BF1C-5DF0BAF5BFE9} + {A8DA148D-80CB-42AF-BF3E-371997545686} diff --git a/sensors/ADXL345Acc/ADXL345Acc.inx b/sensors/ADXL345Acc/ADXL345Acc.inx index 5ba986621..1366a2dd0 100644 --- a/sensors/ADXL345Acc/ADXL345Acc.inx +++ b/sensors/ADXL345Acc/ADXL345Acc.inx @@ -14,7 +14,7 @@ Signature = "$WINDOWS NT$" Class = Sensor ClassGuid = {5175D334-C371-4806-B3BA-71FD53C9258D} -Provider = %MSFT% ;Put the name of your company here +Provider = %ProviderName% ;Put the name of your company here CatalogFile = ADXL345Acc.cat DriverVer = 08/14/2014,2.00.00.01 ;Should be handled by tool that converts from inx to inf @@ -29,7 +29,7 @@ DefaultDestDir = 12,UMDF ADXL345Acc.dll = 1,, [Manufacturer] -%MSFT% = ADXL345Acc_Device, NT$ARCH$ +%ManufacturerName% = ADXL345Acc_Device, NT$ARCH$ ;******************************* @@ -85,11 +85,14 @@ UmdfExtensions = SensorsCx0102 HKR,,CoInstallers32,0x00010000,"WudfCoinstaller.dll" [Strings] +;Localizable Strings MediaDescription = "ADXL345 accelerometer sample Driver" -MSFT = "Microsoft" +ProviderName = "TODO-Set-Provider" +ManufacturerName = "TODO-Set-Manufacturer" ADXL345Acc_DevDesc = "ADXL345 accelerometer sample" WudfRdDisplayName = "Windows Driver Foundation - User-mode Driver Framework Reflector" +;Non-Localizable Strings SERVICE_KERNEL_DRIVER = 1 SERVICE_DEMAND_START = 3 SERVICE_ERROR_NORMAL = 1 diff --git a/sensors/ADXL345Acc/ADXL345Acc.sln b/sensors/ADXL345Acc/ADXL345Acc.sln index c34fd00c4..5c27390f0 100644 --- a/sensors/ADXL345Acc/ADXL345Acc.sln +++ b/sensors/ADXL345Acc/ADXL345Acc.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0 MinimumVisualStudioVersion = 12.0 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ADXL345Acc", "ADXL345Acc.vcxproj", "{AA39917C-9A08-46B6-9825-BBADE84E1AA4}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ADXL345Acc", "ADXL345Acc.vcxproj", "{C684376E-6250-4AB8-A48E-DC4FB6EE3A16}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {AA39917C-9A08-46B6-9825-BBADE84E1AA4}.Debug|Win32.ActiveCfg = Debug|Win32 - {AA39917C-9A08-46B6-9825-BBADE84E1AA4}.Debug|Win32.Build.0 = Debug|Win32 - {AA39917C-9A08-46B6-9825-BBADE84E1AA4}.Release|Win32.ActiveCfg = Release|Win32 - {AA39917C-9A08-46B6-9825-BBADE84E1AA4}.Release|Win32.Build.0 = Release|Win32 - {AA39917C-9A08-46B6-9825-BBADE84E1AA4}.Debug|x64.ActiveCfg = Debug|x64 - {AA39917C-9A08-46B6-9825-BBADE84E1AA4}.Debug|x64.Build.0 = Debug|x64 - {AA39917C-9A08-46B6-9825-BBADE84E1AA4}.Release|x64.ActiveCfg = Release|x64 - {AA39917C-9A08-46B6-9825-BBADE84E1AA4}.Release|x64.Build.0 = Release|x64 + {C684376E-6250-4AB8-A48E-DC4FB6EE3A16}.Debug|Win32.ActiveCfg = Debug|Win32 + {C684376E-6250-4AB8-A48E-DC4FB6EE3A16}.Debug|Win32.Build.0 = Debug|Win32 + {C684376E-6250-4AB8-A48E-DC4FB6EE3A16}.Release|Win32.ActiveCfg = Release|Win32 + {C684376E-6250-4AB8-A48E-DC4FB6EE3A16}.Release|Win32.Build.0 = Release|Win32 + {C684376E-6250-4AB8-A48E-DC4FB6EE3A16}.Debug|x64.ActiveCfg = Debug|x64 + {C684376E-6250-4AB8-A48E-DC4FB6EE3A16}.Debug|x64.Build.0 = Debug|x64 + {C684376E-6250-4AB8-A48E-DC4FB6EE3A16}.Release|x64.ActiveCfg = Release|x64 + {C684376E-6250-4AB8-A48E-DC4FB6EE3A16}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/sensors/ADXL345Acc/ADXL345Acc.vcxproj b/sensors/ADXL345Acc/ADXL345Acc.vcxproj index d6a262fa1..513049eef 100644 --- a/sensors/ADXL345Acc/ADXL345Acc.vcxproj +++ b/sensors/ADXL345Acc/ADXL345Acc.vcxproj @@ -19,12 +19,12 @@ - {AA39917C-9A08-46B6-9825-BBADE84E1AA4} + {C684376E-6250-4AB8-A48E-DC4FB6EE3A16} $(MSBuildProjectName) 2 Debug Win32 - {59D15CA4-F585-4DF1-BC42-AAE6E12222A8} + {F9A020EE-6465-415B-8D4F-8553D7597B98} @@ -191,7 +191,6 @@ - diff --git a/sensors/ADXL345Acc/ADXL345Acc.vcxproj.Filters b/sensors/ADXL345Acc/ADXL345Acc.vcxproj.Filters index 4140ca88d..e4d44236f 100644 --- a/sensors/ADXL345Acc/ADXL345Acc.vcxproj.Filters +++ b/sensors/ADXL345Acc/ADXL345Acc.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {C20A2709-5B6F-46F4-8717-93474B5C7924} + {583B57D3-E580-4334-B9B9-8C25E4D50003} h;hpp;hxx;hm;inl;inc;xsd - {B1F5FC9F-2060-43A9-8D30-62E524609F21} + {68AAB8F0-C0F1-4F7D-BBD7-CB42F7AF2091} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {D04E0A09-D852-49DD-899C-68ABED2092F1} + {3C7195C2-BEDB-4C2D-9CE5-C27C3F75E540} inf;inv;inx;mof;mc; - {5933B2A7-3E1B-4302-BB84-18D793739C74} + {71282B7A-48AC-4B37-8C18-C4F4D1336658} @@ -33,9 +33,6 @@ - - Driver Files - Driver Files diff --git a/sensors/ADXL345Acc/Device.h b/sensors/ADXL345Acc/Device.h index 51222a0e4..7212d7b88 100644 --- a/sensors/ADXL345Acc/Device.h +++ b/sensors/ADXL345Acc/Device.h @@ -45,7 +45,9 @@ typedef enum SENSOR_ENUMERATION_PROPERTY_TYPE = 0, SENSOR_ENUMERATION_PROPERTY_MANUFACTURER, SENSOR_ENUMERATION_PROPERTY_MODEL, + SENSOR_ENUMERATION_PROPERTY_CONNECTION_TYPE, SENSOR_ENUMERATION_PROPERTY_PERSISTENT_UNIQUE_ID, + SENSOR_ENUMERATION_PROPERTY_CATEGORY, SENSOR_ENUMERATION_PROPERTIES_COUNT } SENSOR_ENUMERATION_PROPERTIES_INDEX; diff --git a/sensors/ADXL345Acc/client.cpp b/sensors/ADXL345Acc/client.cpp index 937949b7c..cd46fe66f 100644 --- a/sensors/ADXL345Acc/client.cpp +++ b/sensors/ADXL345Acc/client.cpp @@ -120,9 +120,18 @@ NTSTATUS ADXL345AccDevice::Initialize( InitPropVariantFromString(SENSOR_ACCELEROMETER_MODEL, &(m_pEnumerationProperties->List[SENSOR_ENUMERATION_PROPERTY_MODEL].Value)); + m_pEnumerationProperties->List[SENSOR_ENUMERATION_PROPERTY_CONNECTION_TYPE].Key = DEVPKEY_Sensor_ConnectionType; + // The DEVPKEY_Sensor_ConnectionType values match the SensorConnectionType enumeration + InitPropVariantFromUInt32(0, // 0: INTEGRATED, 1: ATTACHED, 2: EXTERNAL + &(m_pEnumerationProperties->List[SENSOR_ENUMERATION_PROPERTY_CONNECTION_TYPE].Value)); + m_pEnumerationProperties->List[SENSOR_ENUMERATION_PROPERTY_PERSISTENT_UNIQUE_ID].Key = DEVPKEY_Sensor_PersistentUniqueId; InitPropVariantFromCLSID(GUID_Adxl345Device_UniqueID, &(m_pEnumerationProperties->List[SENSOR_ENUMERATION_PROPERTY_PERSISTENT_UNIQUE_ID].Value)); + + m_pEnumerationProperties->List[SENSOR_ENUMERATION_PROPERTY_CATEGORY].Key = DEVPKEY_Sensor_Category; + InitPropVariantFromCLSID(GUID_SensorCategory_Motion, + &(m_pEnumerationProperties->List[SENSOR_ENUMERATION_PROPERTY_CATEGORY].Value)); } } @@ -147,7 +156,7 @@ NTSTATUS ADXL345AccDevice::Initialize( { FILETIME time; m_pSensorData->List[SENSOR_DATA_TIMESTAMP].Key = PKEY_SensorData_Timestamp; - GetSystemTimeAsFileTime(&time); + GetSystemTimePreciseAsFileTime(&time); InitPropVariantFromFileTime(&time, &(m_pSensorData->List[SENSOR_DATA_TIMESTAMP].Value)); m_pSensorData->List[SENSOR_DATA_ACCELERATION_X_G].Key = PKEY_SensorData_AccelerationX_Gs; @@ -311,7 +320,7 @@ NTSTATUS ADXL345AccDevice::GetData() InitPropVariantFromFloat(Sample.Z, &(m_pSensorData->List[SENSOR_DATA_ACCELERATION_Z_G].Value)); FILETIME Timestamp = {}; - GetSystemTimeAsFileTime(&Timestamp); + GetSystemTimePreciseAsFileTime(&Timestamp); InitPropVariantFromFileTime(&Timestamp, &(m_pSensorData->List[SENSOR_DATA_TIMESTAMP].Value)); SensorsCxSensorDataReady(m_SensorInstance, m_pSensorData); @@ -1033,4 +1042,4 @@ NTSTATUS ADXL345AccDevice::OnIoControl( SENSOR_FunctionExit(Status); return Status; -} \ No newline at end of file +} diff --git a/sensors/Activity/Activity.inx b/sensors/Activity/Activity.inx index 4742eb02b..e3c036c37 100644 --- a/sensors/Activity/Activity.inx +++ b/sensors/Activity/Activity.inx @@ -17,7 +17,7 @@ Signature = "$WINDOWS NT$" Class = Sensor ClassGuid = {5175D334-C371-4806-B3BA-71FD53C9258D} -Provider = %MSFT% +Provider = %ProviderName% CatalogFile = Activity.cat DriverVer = 10/27/2014,2.00.00.01 @@ -32,7 +32,7 @@ DefaultDestDir = 12,UMDF Activity.dll = 1,, [Manufacturer] -%MSFT% = Activity_Device, NT$ARCH$ +%ManufacturerName% = Activity_Device, NT$ARCH$ ;******************************* ; Activity Install Section @@ -85,11 +85,14 @@ UmdfExtensions = SensorsCx0102 HKR,,CoInstallers32,0x00010000,"WudfCoinstaller.dll" [Strings] +;Localizable Strings MediaDescription = "Windows Activity Driver" -MSFT = "Microsoft" +ProviderName = "TODO-Set-Provider" +ManufacturerName = "TODO-Set-Manufacturer" Activity_DevDesc = "Activity" WudfRdDisplayName = "Windows Driver Foundation - User-mode Driver Framework Reflector" +;Non-Localizable Strings SERVICE_KERNEL_DRIVER = 1 SERVICE_DEMAND_START = 3 SERVICE_ERROR_NORMAL = 1 diff --git a/sensors/Activity/Activity.sln b/sensors/Activity/Activity.sln index f1fb86770..ed653c1d6 100644 --- a/sensors/Activity/Activity.sln +++ b/sensors/Activity/Activity.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0 MinimumVisualStudioVersion = 12.0 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Activity", "Activity.vcxproj", "{92029423-4EFC-49A3-BE21-B69213B42AD6}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Activity", "Activity.vcxproj", "{8D62CC9B-7F6B-4B7C-A650-32595DA9B28D}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {92029423-4EFC-49A3-BE21-B69213B42AD6}.Debug|Win32.ActiveCfg = Debug|Win32 - {92029423-4EFC-49A3-BE21-B69213B42AD6}.Debug|Win32.Build.0 = Debug|Win32 - {92029423-4EFC-49A3-BE21-B69213B42AD6}.Release|Win32.ActiveCfg = Release|Win32 - {92029423-4EFC-49A3-BE21-B69213B42AD6}.Release|Win32.Build.0 = Release|Win32 - {92029423-4EFC-49A3-BE21-B69213B42AD6}.Debug|x64.ActiveCfg = Debug|x64 - {92029423-4EFC-49A3-BE21-B69213B42AD6}.Debug|x64.Build.0 = Debug|x64 - {92029423-4EFC-49A3-BE21-B69213B42AD6}.Release|x64.ActiveCfg = Release|x64 - {92029423-4EFC-49A3-BE21-B69213B42AD6}.Release|x64.Build.0 = Release|x64 + {8D62CC9B-7F6B-4B7C-A650-32595DA9B28D}.Debug|Win32.ActiveCfg = Debug|Win32 + {8D62CC9B-7F6B-4B7C-A650-32595DA9B28D}.Debug|Win32.Build.0 = Debug|Win32 + {8D62CC9B-7F6B-4B7C-A650-32595DA9B28D}.Release|Win32.ActiveCfg = Release|Win32 + {8D62CC9B-7F6B-4B7C-A650-32595DA9B28D}.Release|Win32.Build.0 = Release|Win32 + {8D62CC9B-7F6B-4B7C-A650-32595DA9B28D}.Debug|x64.ActiveCfg = Debug|x64 + {8D62CC9B-7F6B-4B7C-A650-32595DA9B28D}.Debug|x64.Build.0 = Debug|x64 + {8D62CC9B-7F6B-4B7C-A650-32595DA9B28D}.Release|x64.ActiveCfg = Release|x64 + {8D62CC9B-7F6B-4B7C-A650-32595DA9B28D}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/sensors/Activity/Activity.vcxproj b/sensors/Activity/Activity.vcxproj index 7414cdf10..4e67e58b2 100644 --- a/sensors/Activity/Activity.vcxproj +++ b/sensors/Activity/Activity.vcxproj @@ -19,12 +19,12 @@ - {92029423-4EFC-49A3-BE21-B69213B42AD6} + {8D62CC9B-7F6B-4B7C-A650-32595DA9B28D} $(MSBuildProjectName) 2 Debug Win32 - {74D183AB-2BAC-41B6-8969-14F6FCEF6376} + {2AA63371-1F17-4DD3-A61C-A4B309500609} @@ -191,7 +191,6 @@ - diff --git a/sensors/Activity/Activity.vcxproj.Filters b/sensors/Activity/Activity.vcxproj.Filters index b97826690..b030df774 100644 --- a/sensors/Activity/Activity.vcxproj.Filters +++ b/sensors/Activity/Activity.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {2CABA5C9-80C7-4E60-BEA7-32D9A2EAA7BB} + {41DFCDBE-6A67-4D84-AB7B-45C8FE834475} h;hpp;hxx;hm;inl;inc;xsd - {A2ACE853-D985-44E1-B2A8-A7A4DAB1AAB2} + {DD7B7ED0-4EF4-4753-AD23-72366C6CB41E} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {F30F39F6-AB44-41F0-9B71-C4C30F923ED3} + {C8299C55-D177-4014-A1C4-2502A34B3F09} inf;inv;inx;mof;mc; - {CE567ED5-70CD-43EE-AEC5-2831011DF02E} + {4C33DA67-A0C3-4947-93E0-01E52A3E1B62} @@ -36,9 +36,6 @@ - - Driver Files - Driver Files diff --git a/sensors/Activity/client.cpp b/sensors/Activity/client.cpp index 0bf9e1fc5..51c53e326 100644 --- a/sensors/Activity/client.cpp +++ b/sensors/Activity/client.cpp @@ -60,7 +60,7 @@ NTSTATUS ActivityDevice::GetData() // update last sample FILETIME TimeStamp = {}; memcpy_s(m_pLastSample, m_pFilteredSample->AllocatedSizeInBytes, m_pFilteredSample, m_pFilteredSample->AllocatedSizeInBytes); - GetSystemTimeAsFileTime(&TimeStamp); + GetSystemTimePreciseAsFileTime(&TimeStamp); InitPropVariantFromFileTime(&TimeStamp, &(m_pLastSample->List[ACTIVITY_DATA_TIMESTAMP].Value)); // push to clx @@ -315,7 +315,7 @@ VOID ActivityDevice::OnHistoryTimerExpire(_In_ WDFTIMER historyTimer) status = STATUS_INVALID_PARAMETER; } } - GetSystemTimeAsFileTime(&(data.Timestamp)); + GetSystemTimePreciseAsFileTime(&(data.Timestamp)); if (NT_SUCCESS(status)) { @@ -1018,4 +1018,4 @@ NTSTATUS ActivityDevice::OnIoControl( SENSOR_FunctionExit(STATUS_NOT_SUPPORTED); return STATUS_NOT_SUPPORTED; -} \ No newline at end of file +} diff --git a/sensors/Activity/hardwaresimulator.cpp b/sensors/Activity/hardwaresimulator.cpp index bad49617e..29d9203b1 100644 --- a/sensors/Activity/hardwaresimulator.cpp +++ b/sensors/Activity/hardwaresimulator.cpp @@ -18,8 +18,8 @@ const ActivitySample c_SimulatorData[][MAX_ACTIVITY_STATE_PER_SAMPLE] = { // state and confidence pair. { { {}, ActivityState_InVehicle, 40 }, { {}, ActivityState_Biking, 30 }, { {}, ActivityState_Walking, 20 }, { } }, // InVehicle 40%, Biking 30%, Walking 20% { { {}, ActivityState_Biking, 45 }, { {}, ActivityState_InVehicle, 40 }, { {}, ActivityState_Fidgeting, 30 }, { {}, ActivityState_Walking, 20 } }, // Biking 45%, InVehicle 40% Fidgeting 30%, Walking 20% - { { {}, ActivityState_Fidgeting, 80 }, { {}, ActivityState_InVehicle, 60 }, { {}, ActivityState_Biking, 35 }, { {}, ActivityState_Walking, 20 } }, // Fidgeting 80%, InVehicle 60%, Biking 35%, Walking 20% - { { {}, ActivityState_InVehicle, 75 }, { {}, ActivityState_Biking, 55 }, { {}, ActivityState_Walking, 18 }, { } }, // InVehicle 75%, Biking 55%, Walking 18% + { { {}, ActivityState_Idle, 80 }, { {}, ActivityState_InVehicle, 60 }, { {}, ActivityState_Biking, 35 }, { {}, ActivityState_Walking, 20 } }, // Idle 80%, InVehicle 60%, Biking 35%, Walking 20% + { { {}, ActivityState_InVehicle, 75 }, { {}, ActivityState_Idle, 55 }, { {}, ActivityState_Walking, 18 }, { } }, // InVehicle 75%, Idle 55%, Walking 18% { { {}, ActivityState_Biking, 90 }, { }, { }, { } }, // Biking 90% }; diff --git a/sensors/CustomSensors/CustomSensors.inx b/sensors/CustomSensors/CustomSensors.inx index 0d2d774ed..ac4b634b4 100644 --- a/sensors/CustomSensors/CustomSensors.inx +++ b/sensors/CustomSensors/CustomSensors.inx @@ -17,7 +17,7 @@ Signature = "$WINDOWS NT$" Class = Sensor ClassGuid = {5175D334-C371-4806-B3BA-71FD53C9258D} -Provider = %MSFT% +Provider = %ProviderName% CatalogFile = CustomSensors.cat DriverVer = 08/14/2014,2.00.00.01 @@ -32,7 +32,7 @@ DefaultDestDir = 12,UMDF CustomSensors.dll = 1,, [Manufacturer] -%MSFT% = CustomSensors_Device, NT$ARCH$ +%ManufacturerName% = CustomSensors_Device, NT$ARCH$ ;******************************* ; Custom Sensor Install Section @@ -85,11 +85,14 @@ UmdfExtensions = SensorsCx0102 HKR,,CoInstallers32,0x00010000,"WudfCoinstaller.dll" [Strings] +;Localizable Strings MediaDescription = "Windows Custom Sensor sample Driver" -MSFT = "Microsoft" +ProviderName = "TODO-Set-Provider" +ManufacturerName = "TODO-Set-Manufacturer" CustomSensors_DevDesc = "Custom Sensor sample" WudfRdDisplayName = "Windows Driver Foundation - User-mode Driver Framework Reflector" +;Non-Localizable Strings SERVICE_KERNEL_DRIVER = 1 SERVICE_DEMAND_START = 3 SERVICE_ERROR_NORMAL = 1 diff --git a/sensors/CustomSensors/CustomSensors.sln b/sensors/CustomSensors/CustomSensors.sln index f6177827e..50cc668f9 100644 --- a/sensors/CustomSensors/CustomSensors.sln +++ b/sensors/CustomSensors/CustomSensors.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0 MinimumVisualStudioVersion = 12.0 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CustomSensors", "CustomSensors.vcxproj", "{581FD075-974D-4650-8524-C17DA15E744B}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CustomSensors", "CustomSensors.vcxproj", "{C503E2ED-A654-4A89-86C0-2C52BF1232FD}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {581FD075-974D-4650-8524-C17DA15E744B}.Debug|Win32.ActiveCfg = Debug|Win32 - {581FD075-974D-4650-8524-C17DA15E744B}.Debug|Win32.Build.0 = Debug|Win32 - {581FD075-974D-4650-8524-C17DA15E744B}.Release|Win32.ActiveCfg = Release|Win32 - {581FD075-974D-4650-8524-C17DA15E744B}.Release|Win32.Build.0 = Release|Win32 - {581FD075-974D-4650-8524-C17DA15E744B}.Debug|x64.ActiveCfg = Debug|x64 - {581FD075-974D-4650-8524-C17DA15E744B}.Debug|x64.Build.0 = Debug|x64 - {581FD075-974D-4650-8524-C17DA15E744B}.Release|x64.ActiveCfg = Release|x64 - {581FD075-974D-4650-8524-C17DA15E744B}.Release|x64.Build.0 = Release|x64 + {C503E2ED-A654-4A89-86C0-2C52BF1232FD}.Debug|Win32.ActiveCfg = Debug|Win32 + {C503E2ED-A654-4A89-86C0-2C52BF1232FD}.Debug|Win32.Build.0 = Debug|Win32 + {C503E2ED-A654-4A89-86C0-2C52BF1232FD}.Release|Win32.ActiveCfg = Release|Win32 + {C503E2ED-A654-4A89-86C0-2C52BF1232FD}.Release|Win32.Build.0 = Release|Win32 + {C503E2ED-A654-4A89-86C0-2C52BF1232FD}.Debug|x64.ActiveCfg = Debug|x64 + {C503E2ED-A654-4A89-86C0-2C52BF1232FD}.Debug|x64.Build.0 = Debug|x64 + {C503E2ED-A654-4A89-86C0-2C52BF1232FD}.Release|x64.ActiveCfg = Release|x64 + {C503E2ED-A654-4A89-86C0-2C52BF1232FD}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/sensors/CustomSensors/CustomSensors.vcxproj b/sensors/CustomSensors/CustomSensors.vcxproj index d8e0699fd..6456012f8 100644 --- a/sensors/CustomSensors/CustomSensors.vcxproj +++ b/sensors/CustomSensors/CustomSensors.vcxproj @@ -19,12 +19,12 @@ - {581FD075-974D-4650-8524-C17DA15E744B} + {C503E2ED-A654-4A89-86C0-2C52BF1232FD} $(MSBuildProjectName) 2 Debug Win32 - {26FA62DC-6E68-426D-9DDE-1D66E2CDEA41} + {C67AC46D-32C4-42DD-AD6C-B266FB88E573} @@ -191,7 +191,6 @@ - diff --git a/sensors/CustomSensors/CustomSensors.vcxproj.Filters b/sensors/CustomSensors/CustomSensors.vcxproj.Filters index 275aed164..2fea2ab32 100644 --- a/sensors/CustomSensors/CustomSensors.vcxproj.Filters +++ b/sensors/CustomSensors/CustomSensors.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {4737C037-E6F5-419C-AC9F-B88532462988} + {1DE373A7-02B2-49BA-8B3B-8D3E3881B654} h;hpp;hxx;hm;inl;inc;xsd - {C6BF209A-BDDA-4488-883C-E4C3BDD7AB16} + {D1781583-B556-4421-85B6-F46EDFB366EE} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {EB50A155-6055-4736-A912-5B2970F2EFBE} + {29D44582-FC0F-431D-A97A-971DAE2C6849} inf;inv;inx;mof;mc; - {3B9BA3C9-953B-49F5-8F39-A7FE1522F2A5} + {4736CE2D-C8AC-421F-B9F5-A4007B8A66A0} @@ -36,9 +36,6 @@ - - Driver Files - Driver Files diff --git a/sensors/CustomSensors/client.cpp b/sensors/CustomSensors/client.cpp index 81bba208b..66afd38dc 100644 --- a/sensors/CustomSensors/client.cpp +++ b/sensors/CustomSensors/client.cpp @@ -45,7 +45,7 @@ NTSTATUS CustomSensorDevice::GetData() // push to clx InitPropVariantFromFloat(pSimulator->GetSample(), &(m_pData->List[CSTM_DATA_CO2_LEVEL_PERCENT].Value)); - GetSystemTimeAsFileTime(&TimeStamp); + GetSystemTimePreciseAsFileTime(&TimeStamp); InitPropVariantFromFileTime(&TimeStamp, &(m_pData->List[CSTM_DATA_TIMESTAMP].Value)); SensorsCxSensorDataReady(m_SensorInstance, m_pData); diff --git a/sensors/CustomSensors/device.cpp b/sensors/CustomSensors/device.cpp index 2b709637a..e8f956e6d 100644 --- a/sensors/CustomSensors/device.cpp +++ b/sensors/CustomSensors/device.cpp @@ -183,7 +183,7 @@ NTSTATUS CustomSensorDevice::Initialize( m_pData->Count = CSTM_DATA_COUNT; m_pData->List[CSTM_DATA_TIMESTAMP].Key = PKEY_SensorData_Timestamp; - GetSystemTimeAsFileTime(&Time); + GetSystemTimePreciseAsFileTime(&Time); InitPropVariantFromFileTime(&Time, &(m_pData->List[CSTM_DATA_TIMESTAMP].Value)); // Initialize the sample, at this point of time the sensor is not started yet, @@ -592,4 +592,4 @@ CustomSensorDevice::OnD0Exit( Exit: SENSOR_FunctionExit(Status); return Status; -} \ No newline at end of file +} diff --git a/sensors/Pedometer/Pedometer.inx b/sensors/Pedometer/Pedometer.inx index 5d8bc1e84..1dcef53b0 100644 --- a/sensors/Pedometer/Pedometer.inx +++ b/sensors/Pedometer/Pedometer.inx @@ -17,7 +17,7 @@ Signature = "$WINDOWS NT$" Class = Sensor ClassGuid = {5175D334-C371-4806-B3BA-71FD53C9258D} -Provider = %MSFT% +Provider = %ProviderName% CatalogFile = Pedometer.cat DriverVer = 09/09/2014,2.00.00.01 @@ -32,7 +32,7 @@ DefaultDestDir = 12,UMDF Pedometer.dll = 1,, [Manufacturer] -%MSFT% = Pedometer_Device, NT$ARCH$ +%ManufacturerName% = Pedometer_Device, NT$ARCH$ ;******************************* ; Pedometer Install Section @@ -85,11 +85,14 @@ UmdfExtensions = SensorsCx0102 HKR,,CoInstallers32,0x00010000,"WudfCoinstaller.dll" [Strings] +;Localizable Strings MediaDescription = "Windows Pedometer Driver" -MSFT = "Microsoft" +ProviderName = "TODO-Set-Provider" +ManufacturerName = "TODO-Set-Manufacturer" Pedometer_DevDesc = "Pedometer" WudfRdDisplayName = "Windows Driver Foundation - User-mode Driver Framework Reflector" +;Non-Localizable Strings SERVICE_KERNEL_DRIVER = 1 SERVICE_DEMAND_START = 3 SERVICE_ERROR_NORMAL = 1 diff --git a/sensors/Pedometer/Pedometer.sln b/sensors/Pedometer/Pedometer.sln index 1411fcd26..92ceadbd9 100644 --- a/sensors/Pedometer/Pedometer.sln +++ b/sensors/Pedometer/Pedometer.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0 MinimumVisualStudioVersion = 12.0 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Pedometer", "Pedometer.vcxproj", "{E4AB8CAA-0898-4F87-B6DB-273E1D661550}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Pedometer", "Pedometer.vcxproj", "{7F064CE7-8D48-416A-9F3A-9E13074576F2}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {E4AB8CAA-0898-4F87-B6DB-273E1D661550}.Debug|Win32.ActiveCfg = Debug|Win32 - {E4AB8CAA-0898-4F87-B6DB-273E1D661550}.Debug|Win32.Build.0 = Debug|Win32 - {E4AB8CAA-0898-4F87-B6DB-273E1D661550}.Release|Win32.ActiveCfg = Release|Win32 - {E4AB8CAA-0898-4F87-B6DB-273E1D661550}.Release|Win32.Build.0 = Release|Win32 - {E4AB8CAA-0898-4F87-B6DB-273E1D661550}.Debug|x64.ActiveCfg = Debug|x64 - {E4AB8CAA-0898-4F87-B6DB-273E1D661550}.Debug|x64.Build.0 = Debug|x64 - {E4AB8CAA-0898-4F87-B6DB-273E1D661550}.Release|x64.ActiveCfg = Release|x64 - {E4AB8CAA-0898-4F87-B6DB-273E1D661550}.Release|x64.Build.0 = Release|x64 + {7F064CE7-8D48-416A-9F3A-9E13074576F2}.Debug|Win32.ActiveCfg = Debug|Win32 + {7F064CE7-8D48-416A-9F3A-9E13074576F2}.Debug|Win32.Build.0 = Debug|Win32 + {7F064CE7-8D48-416A-9F3A-9E13074576F2}.Release|Win32.ActiveCfg = Release|Win32 + {7F064CE7-8D48-416A-9F3A-9E13074576F2}.Release|Win32.Build.0 = Release|Win32 + {7F064CE7-8D48-416A-9F3A-9E13074576F2}.Debug|x64.ActiveCfg = Debug|x64 + {7F064CE7-8D48-416A-9F3A-9E13074576F2}.Debug|x64.Build.0 = Debug|x64 + {7F064CE7-8D48-416A-9F3A-9E13074576F2}.Release|x64.ActiveCfg = Release|x64 + {7F064CE7-8D48-416A-9F3A-9E13074576F2}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/sensors/Pedometer/Pedometer.vcxproj b/sensors/Pedometer/Pedometer.vcxproj index 8b6b31227..f237c9009 100644 --- a/sensors/Pedometer/Pedometer.vcxproj +++ b/sensors/Pedometer/Pedometer.vcxproj @@ -19,12 +19,12 @@ - {E4AB8CAA-0898-4F87-B6DB-273E1D661550} + {7F064CE7-8D48-416A-9F3A-9E13074576F2} $(MSBuildProjectName) 2 Debug Win32 - {1DF52CE2-1A85-4EAE-A0B3-FC48C81A3E3D} + {879AB2E4-FCDE-4858-B211-4313A724EC69} @@ -191,7 +191,6 @@ - diff --git a/sensors/Pedometer/Pedometer.vcxproj.Filters b/sensors/Pedometer/Pedometer.vcxproj.Filters index 687bc30ed..133d34f1c 100644 --- a/sensors/Pedometer/Pedometer.vcxproj.Filters +++ b/sensors/Pedometer/Pedometer.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {4069EBC4-432A-48E0-AD1E-9EDEAB260F0E} + {C8EAE043-CA7E-41A8-821D-EFCA0B010195} h;hpp;hxx;hm;inl;inc;xsd - {6DA94DA3-51F2-4464-BDF8-6F25FA6ABEC6} + {2FE25726-3DE9-439A-BE0F-0004BF801CE9} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {440472A3-6A34-466D-AEF0-81507558AE46} + {1C6ED801-206E-4535-B7D3-56331A35A769} inf;inv;inx;mof;mc; - {DB1109AA-F1A5-43B8-B112-1CA70243A5AB} + {385CA6C0-4DDF-4651-AF87-3A05BB9DD45D} @@ -36,9 +36,6 @@ - - Driver Files - Driver Files diff --git a/sensors/Pedometer/device.cpp b/sensors/Pedometer/device.cpp index a3d3ba309..0a2198362 100644 --- a/sensors/Pedometer/device.cpp +++ b/sensors/Pedometer/device.cpp @@ -133,7 +133,7 @@ PedometerDevice::Initialize( m_pData->Count = PEDOMETER_DATA_COUNT; m_pData->List[PEDOMETER_DATA_TIMESTAMP].Key = PKEY_SensorData_Timestamp; - GetSystemTimeAsFileTime(&Time); + GetSystemTimePreciseAsFileTime(&Time); InitPropVariantFromFileTime(&Time, &(m_pData->List[PEDOMETER_DATA_TIMESTAMP].Value)); m_pData->List[PEDOMETER_DATA_FIRST_AFTER_RESET].Key = PKEY_SensorData_PedometerReset; diff --git a/sensors/Pedometer/hardwaresimulator.cpp b/sensors/Pedometer/hardwaresimulator.cpp index 6328ea416..6ab45953e 100644 --- a/sensors/Pedometer/hardwaresimulator.cpp +++ b/sensors/Pedometer/hardwaresimulator.cpp @@ -387,7 +387,7 @@ HardwareSimulator::GetSample( WdfWaitLockRelease(m_Lock); - GetSystemTimeAsFileTime(&Sample->Timestamp); + GetSystemTimePreciseAsFileTime(&Sample->Timestamp); } SENSOR_FunctionExit(Status); diff --git a/sensors/SimpleDeviceOrientationSensor/SimpleDeviceOrientationSensor.inx b/sensors/SimpleDeviceOrientationSensor/SimpleDeviceOrientationSensor.inx index 16d523da7..9323c4c8c 100644 --- a/sensors/SimpleDeviceOrientationSensor/SimpleDeviceOrientationSensor.inx +++ b/sensors/SimpleDeviceOrientationSensor/SimpleDeviceOrientationSensor.inx @@ -17,7 +17,7 @@ Signature = "$WINDOWS NT$" Class = Sensor ClassGuid = {5175D334-C371-4806-B3BA-71FD53C9258D} -Provider = %MSFT% +Provider = %ProviderName% CatalogFile = SimpleDeviceOrientationSensor.cat DriverVer = 08/14/2014,2.00.00.01 @@ -32,7 +32,7 @@ DefaultDestDir = 12,UMDF SimpleDeviceOrientationSensor.dll = 1,, [Manufacturer] -%MSFT% = SimpleDeviceOrientationSensor_Device, NT$ARCH$ +%ManufacturerName% = SimpleDeviceOrientationSensor_Device, NT$ARCH$ ;******************************* ; Simple Device Orientation Sensor Install Section @@ -85,12 +85,14 @@ UmdfExtensions = SensorsCx0102 HKR,,CoInstallers32,0x00010000,"WudfCoinstaller.dll" [Strings] +;Localizable Strings MediaDescription = "Windows Simple Device Orientation Sensor sample Driver" -MSFT = "Microsoft" -StdMfg = "Microsoft" +ProviderName = "TODO-Set-Provider" +ManufacturerName = "TODO-Set-Manufacturer" SimpleDeviceOrientationSensor_DevDesc = "Simple Device Orientation Sensor sample" WudfRdDisplayName = "Windows Driver Foundation - User-mode Driver Framework Reflector" +;Non-Localizable Strings SERVICE_KERNEL_DRIVER = 1 SERVICE_DEMAND_START = 3 SERVICE_ERROR_NORMAL = 1 diff --git a/sensors/SimpleDeviceOrientationSensor/SimpleDeviceOrientationSensor.sln b/sensors/SimpleDeviceOrientationSensor/SimpleDeviceOrientationSensor.sln index db9504a31..fc229befe 100644 --- a/sensors/SimpleDeviceOrientationSensor/SimpleDeviceOrientationSensor.sln +++ b/sensors/SimpleDeviceOrientationSensor/SimpleDeviceOrientationSensor.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0 MinimumVisualStudioVersion = 12.0 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SimpleDeviceOrientationSensor", "SimpleDeviceOrientationSensor.vcxproj", "{BD66C84A-75D9-48E7-848C-E3E8BCF303F9}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SimpleDeviceOrientationSensor", "SimpleDeviceOrientationSensor.vcxproj", "{EC36A603-B217-4538-80D9-C78BA42A3CA7}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {BD66C84A-75D9-48E7-848C-E3E8BCF303F9}.Debug|Win32.ActiveCfg = Debug|Win32 - {BD66C84A-75D9-48E7-848C-E3E8BCF303F9}.Debug|Win32.Build.0 = Debug|Win32 - {BD66C84A-75D9-48E7-848C-E3E8BCF303F9}.Release|Win32.ActiveCfg = Release|Win32 - {BD66C84A-75D9-48E7-848C-E3E8BCF303F9}.Release|Win32.Build.0 = Release|Win32 - {BD66C84A-75D9-48E7-848C-E3E8BCF303F9}.Debug|x64.ActiveCfg = Debug|x64 - {BD66C84A-75D9-48E7-848C-E3E8BCF303F9}.Debug|x64.Build.0 = Debug|x64 - {BD66C84A-75D9-48E7-848C-E3E8BCF303F9}.Release|x64.ActiveCfg = Release|x64 - {BD66C84A-75D9-48E7-848C-E3E8BCF303F9}.Release|x64.Build.0 = Release|x64 + {EC36A603-B217-4538-80D9-C78BA42A3CA7}.Debug|Win32.ActiveCfg = Debug|Win32 + {EC36A603-B217-4538-80D9-C78BA42A3CA7}.Debug|Win32.Build.0 = Debug|Win32 + {EC36A603-B217-4538-80D9-C78BA42A3CA7}.Release|Win32.ActiveCfg = Release|Win32 + {EC36A603-B217-4538-80D9-C78BA42A3CA7}.Release|Win32.Build.0 = Release|Win32 + {EC36A603-B217-4538-80D9-C78BA42A3CA7}.Debug|x64.ActiveCfg = Debug|x64 + {EC36A603-B217-4538-80D9-C78BA42A3CA7}.Debug|x64.Build.0 = Debug|x64 + {EC36A603-B217-4538-80D9-C78BA42A3CA7}.Release|x64.ActiveCfg = Release|x64 + {EC36A603-B217-4538-80D9-C78BA42A3CA7}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/sensors/SimpleDeviceOrientationSensor/SimpleDeviceOrientationSensor.vcxproj b/sensors/SimpleDeviceOrientationSensor/SimpleDeviceOrientationSensor.vcxproj index 85ce76fc4..1d4e80296 100644 --- a/sensors/SimpleDeviceOrientationSensor/SimpleDeviceOrientationSensor.vcxproj +++ b/sensors/SimpleDeviceOrientationSensor/SimpleDeviceOrientationSensor.vcxproj @@ -19,12 +19,12 @@ - {BD66C84A-75D9-48E7-848C-E3E8BCF303F9} + {EC36A603-B217-4538-80D9-C78BA42A3CA7} $(MSBuildProjectName) 2 Debug Win32 - {835CB733-E6FD-434E-8E5D-6A44D0666B3D} + {7DC023F6-D608-4C6D-A3F0-523D5E12FE9A} @@ -191,7 +191,6 @@ - diff --git a/sensors/SimpleDeviceOrientationSensor/SimpleDeviceOrientationSensor.vcxproj.Filters b/sensors/SimpleDeviceOrientationSensor/SimpleDeviceOrientationSensor.vcxproj.Filters index 964e0d85c..f865d363f 100644 --- a/sensors/SimpleDeviceOrientationSensor/SimpleDeviceOrientationSensor.vcxproj.Filters +++ b/sensors/SimpleDeviceOrientationSensor/SimpleDeviceOrientationSensor.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {8FE1EA5F-0794-452D-8ADA-B87816A19EB9} + {FD6ED732-A690-4E99-AA26-09F10BFFED69} h;hpp;hxx;hm;inl;inc;xsd - {8137339A-0DE2-48F6-B4B6-29FF3DBF10FA} + {BB77592E-3BBA-49CC-8A42-844511BBEFC2} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {A44CA721-816C-481C-AFB4-FD9F44215A4B} + {318968FF-3ECA-45D1-AC01-8FDEDBD8C1C0} inf;inv;inx;mof;mc; - {B68042DD-8FCE-4A4B-82E3-3F536F3C45DE} + {63340E09-99C6-45A8-B190-3D61226BF8C2} @@ -36,9 +36,6 @@ - - Driver Files - Driver Files diff --git a/sensors/SimpleDeviceOrientationSensor/client.cpp b/sensors/SimpleDeviceOrientationSensor/client.cpp index 1e9da4060..d06685443 100644 --- a/sensors/SimpleDeviceOrientationSensor/client.cpp +++ b/sensors/SimpleDeviceOrientationSensor/client.cpp @@ -53,7 +53,7 @@ NTSTATUS SdoDevice::GetData() // push to clx InitPropVariantFromUInt32(m_LastSample, &(m_pData->List[SDO_DATA_SIMPLEDEVICEORIENTATION].Value)); - GetSystemTimeAsFileTime(&TimeStamp); + GetSystemTimePreciseAsFileTime(&TimeStamp); InitPropVariantFromFileTime(&TimeStamp, &(m_pData->List[SDO_DATA_TIMESTAMP].Value)); SensorsCxSensorDataReady(m_SensorInstance, m_pData); @@ -574,4 +574,4 @@ NTSTATUS SdoDevice::OnIoControl( SENSOR_FunctionExit(Status); return Status; -} \ No newline at end of file +} diff --git a/sensors/SimpleDeviceOrientationSensor/device.cpp b/sensors/SimpleDeviceOrientationSensor/device.cpp index 3acc6d4aa..369d367cb 100644 --- a/sensors/SimpleDeviceOrientationSensor/device.cpp +++ b/sensors/SimpleDeviceOrientationSensor/device.cpp @@ -170,7 +170,7 @@ NTSTATUS SdoDevice::Initialize( m_pData->Count = SDO_DATA_COUNT; m_pData->List[SDO_DATA_TIMESTAMP].Key = PKEY_SensorData_Timestamp; - GetSystemTimeAsFileTime(&Time); + GetSystemTimePreciseAsFileTime(&Time); InitPropVariantFromFileTime(&Time, &(m_pData->List[SDO_DATA_TIMESTAMP].Value)); m_pData->List[SDO_DATA_SIMPLEDEVICEORIENTATION].Key = PKEY_SensorData_SimpleDeviceOrientation; @@ -564,4 +564,4 @@ SdoDevice::OnD0Exit( Exit: SENSOR_FunctionExit(Status); return Status; -} \ No newline at end of file +} diff --git a/serial/VirtualSerial2/ReadMe.md b/serial/VirtualSerial2/ReadMe.md index 2b1bc5130..3c5baf787 100644 --- a/serial/VirtualSerial2/ReadMe.md +++ b/serial/VirtualSerial2/ReadMe.md @@ -13,47 +13,32 @@ For more information, see [Serial Controller and Device Drivers](http://msdn.mic Code tour --------- -File manifest - -Description - -comsup.cpp & comsup.h - +#### 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 the 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 - +#### 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 - +#### exports.def This file lists the functions that the driver DLL exports. -internal.h - +#### internal.h This is the main header file for the sample driver. -driver.cpp & driver.h - +#### driver.cpp & driver.h Definition and implementation of the driver callback class (CMyDriver) for the sample. This includes **DriverEntry** and events on the framework driver object. -device.cpp & driver.h - +#### device.cpp & driver.h Definition and implementation of the device callback class (CMyDriver) for the sample. This includes events on the framework device object. -queue.cpp & queue.h - +#### queue.cpp & queue.h Definition and implementation of the base queue callback class (CMyQueue). This includes events on the framework I/O queue object. -VirtualSerial.rc /FakeModem.rc - +#### VirtualSerial.rc /FakeModem.rc This file defines resource information for the sample driver. -VirtualSerial.inf / FakeModem.inf - +#### VirtualSerial.inf / FakeModem.inf INF file that contains installation information for this driver. diff --git a/setup/DIFxAPI/AppDrv/AppDrv.vcxproj b/setup/DIFxAPI/AppDrv/AppDrv.vcxproj index b9c158a5f..728bebcb3 100644 --- a/setup/DIFxAPI/AppDrv/AppDrv.vcxproj +++ b/setup/DIFxAPI/AppDrv/AppDrv.vcxproj @@ -19,11 +19,11 @@ - {CF7081EA-BC95-4C48-8829-39228285EB60} + {AD9DA6AF-774B-44BE-A2D6-FDFDF465040A} $(MSBuildProjectName) Debug Win32 - {8A763ADF-480B-4CEB-9240-3C962CEC774B} + {3C7BBEA0-B28D-4F55-906C-8BACD934B0E6} @@ -173,7 +173,6 @@ - diff --git a/setup/DIFxAPI/AppDrv/AppDrv.vcxproj.Filters b/setup/DIFxAPI/AppDrv/AppDrv.vcxproj.Filters index 061dfdbd3..15c2b2a39 100644 --- a/setup/DIFxAPI/AppDrv/AppDrv.vcxproj.Filters +++ b/setup/DIFxAPI/AppDrv/AppDrv.vcxproj.Filters @@ -3,15 +3,15 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {C20EAAB1-6327-43E1-91D3-E9A0114A2F12} + {A172B090-25B7-4514-8F55-96E7C6A68739} h;hpp;hxx;hm;inl;inc;xsd - {62C6EDFE-CF17-4164-A7B5-ECD8A4BD7FD9} + {CF690082-91EA-49D0-BBA9-7DFB89E69FD9} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {2B8AA188-7F46-4526-ABB6-BC3389BC9506} + {C14644AC-BA2E-46C9-968B-4C2B2D5CCC3F} diff --git a/setup/DIFxAPI/DIFxAPI.sln b/setup/DIFxAPI/DIFxAPI.sln index 32c2afcf5..03954267a 100644 --- a/setup/DIFxAPI/DIFxAPI.sln +++ b/setup/DIFxAPI/DIFxAPI.sln @@ -3,13 +3,13 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0 MinimumVisualStudioVersion = 12.0 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AppDrv", "AppDrv", "{934A7B3A-B938-4EDA-9E5F-E7F433FA087F}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AppDrv", "AppDrv", "{1DD702C0-4A0D-4B8E-880A-2BE850481A3C}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DIFxCmd", "DIFxCmd", "{C23C988D-F193-4B5F-8AEC-BBB7DFA9563A}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DIFxCmd", "DIFxCmd", "{612D3FC0-9B4E-4193-87B4-494CED38A160}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AppDrv", "AppDrv\AppDrv.vcxproj", "{CF7081EA-BC95-4C48-8829-39228285EB60}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AppDrv", "AppDrv\AppDrv.vcxproj", "{AD9DA6AF-774B-44BE-A2D6-FDFDF465040A}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DIFxCmd", "DIFxCmd\DIFxCmd.vcxproj", "{C849846A-EA1D-491D-BB56-139869BE3B21}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DIFxCmd", "DIFxCmd\DIFxCmd.vcxproj", "{66FF5637-A914-478C-A484-0395CF982522}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -19,28 +19,28 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {CF7081EA-BC95-4C48-8829-39228285EB60}.Debug|Win32.ActiveCfg = Debug|Win32 - {CF7081EA-BC95-4C48-8829-39228285EB60}.Debug|Win32.Build.0 = Debug|Win32 - {CF7081EA-BC95-4C48-8829-39228285EB60}.Release|Win32.ActiveCfg = Release|Win32 - {CF7081EA-BC95-4C48-8829-39228285EB60}.Release|Win32.Build.0 = Release|Win32 - {CF7081EA-BC95-4C48-8829-39228285EB60}.Debug|x64.ActiveCfg = Debug|x64 - {CF7081EA-BC95-4C48-8829-39228285EB60}.Debug|x64.Build.0 = Debug|x64 - {CF7081EA-BC95-4C48-8829-39228285EB60}.Release|x64.ActiveCfg = Release|x64 - {CF7081EA-BC95-4C48-8829-39228285EB60}.Release|x64.Build.0 = Release|x64 - {C849846A-EA1D-491D-BB56-139869BE3B21}.Debug|Win32.ActiveCfg = Debug|Win32 - {C849846A-EA1D-491D-BB56-139869BE3B21}.Debug|Win32.Build.0 = Debug|Win32 - {C849846A-EA1D-491D-BB56-139869BE3B21}.Release|Win32.ActiveCfg = Release|Win32 - {C849846A-EA1D-491D-BB56-139869BE3B21}.Release|Win32.Build.0 = Release|Win32 - {C849846A-EA1D-491D-BB56-139869BE3B21}.Debug|x64.ActiveCfg = Debug|x64 - {C849846A-EA1D-491D-BB56-139869BE3B21}.Debug|x64.Build.0 = Debug|x64 - {C849846A-EA1D-491D-BB56-139869BE3B21}.Release|x64.ActiveCfg = Release|x64 - {C849846A-EA1D-491D-BB56-139869BE3B21}.Release|x64.Build.0 = Release|x64 + {AD9DA6AF-774B-44BE-A2D6-FDFDF465040A}.Debug|Win32.ActiveCfg = Debug|Win32 + {AD9DA6AF-774B-44BE-A2D6-FDFDF465040A}.Debug|Win32.Build.0 = Debug|Win32 + {AD9DA6AF-774B-44BE-A2D6-FDFDF465040A}.Release|Win32.ActiveCfg = Release|Win32 + {AD9DA6AF-774B-44BE-A2D6-FDFDF465040A}.Release|Win32.Build.0 = Release|Win32 + {AD9DA6AF-774B-44BE-A2D6-FDFDF465040A}.Debug|x64.ActiveCfg = Debug|x64 + {AD9DA6AF-774B-44BE-A2D6-FDFDF465040A}.Debug|x64.Build.0 = Debug|x64 + {AD9DA6AF-774B-44BE-A2D6-FDFDF465040A}.Release|x64.ActiveCfg = Release|x64 + {AD9DA6AF-774B-44BE-A2D6-FDFDF465040A}.Release|x64.Build.0 = Release|x64 + {66FF5637-A914-478C-A484-0395CF982522}.Debug|Win32.ActiveCfg = Debug|Win32 + {66FF5637-A914-478C-A484-0395CF982522}.Debug|Win32.Build.0 = Debug|Win32 + {66FF5637-A914-478C-A484-0395CF982522}.Release|Win32.ActiveCfg = Release|Win32 + {66FF5637-A914-478C-A484-0395CF982522}.Release|Win32.Build.0 = Release|Win32 + {66FF5637-A914-478C-A484-0395CF982522}.Debug|x64.ActiveCfg = Debug|x64 + {66FF5637-A914-478C-A484-0395CF982522}.Debug|x64.Build.0 = Debug|x64 + {66FF5637-A914-478C-A484-0395CF982522}.Release|x64.ActiveCfg = Release|x64 + {66FF5637-A914-478C-A484-0395CF982522}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {CF7081EA-BC95-4C48-8829-39228285EB60} = {934A7B3A-B938-4EDA-9E5F-E7F433FA087F} - {C849846A-EA1D-491D-BB56-139869BE3B21} = {C23C988D-F193-4B5F-8AEC-BBB7DFA9563A} + {AD9DA6AF-774B-44BE-A2D6-FDFDF465040A} = {1DD702C0-4A0D-4B8E-880A-2BE850481A3C} + {66FF5637-A914-478C-A484-0395CF982522} = {612D3FC0-9B4E-4193-87B4-494CED38A160} EndGlobalSection EndGlobal diff --git a/setup/DIFxAPI/DIFxCmd/DIFxCmd.vcxproj b/setup/DIFxAPI/DIFxCmd/DIFxCmd.vcxproj index 20d87ab46..eea47159e 100644 --- a/setup/DIFxAPI/DIFxCmd/DIFxCmd.vcxproj +++ b/setup/DIFxAPI/DIFxCmd/DIFxCmd.vcxproj @@ -19,11 +19,11 @@ - {C849846A-EA1D-491D-BB56-139869BE3B21} + {66FF5637-A914-478C-A484-0395CF982522} $(MSBuildProjectName) Debug Win32 - {67461C3B-65C3-4497-9785-A7B43A0D539C} + {4C70B696-E995-42AF-99EE-F18B509A43A7} @@ -173,7 +173,6 @@ - diff --git a/setup/DIFxAPI/DIFxCmd/DIFxCmd.vcxproj.Filters b/setup/DIFxAPI/DIFxCmd/DIFxCmd.vcxproj.Filters index f817f4273..e797a87a1 100644 --- a/setup/DIFxAPI/DIFxCmd/DIFxCmd.vcxproj.Filters +++ b/setup/DIFxAPI/DIFxCmd/DIFxCmd.vcxproj.Filters @@ -3,15 +3,15 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {C0AD9456-1A17-4002-A2F1-D7267C7E5A03} + {48B1E348-F61D-4DDB-BAAE-3C46E25E7398} h;hpp;hxx;hm;inl;inc;xsd - {7C1C7A58-A86E-40FC-B36C-AC97607BAA7A} + {9EE62BE7-B509-4B1E-ADB3-C99D5052EAE0} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {52B4B894-1310-435B-B6D1-9A452A91D303} + {C053FE62-1D7A-499E-B3D7-19DAA77C2372} diff --git a/setup/devcon/devcon.sln b/setup/devcon/devcon.sln index 68b6a2f90..d6151eac9 100644 --- a/setup/devcon/devcon.sln +++ b/setup/devcon/devcon.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0 MinimumVisualStudioVersion = 12.0 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "devcon", "devcon.vcxproj", "{B3ABCC72-91F9-40CB-A8F4-CB9C1400C5FC}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "devcon", "devcon.vcxproj", "{86BCB988-7719-4050-B570-6863C4169137}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {B3ABCC72-91F9-40CB-A8F4-CB9C1400C5FC}.Debug|Win32.ActiveCfg = Debug|Win32 - {B3ABCC72-91F9-40CB-A8F4-CB9C1400C5FC}.Debug|Win32.Build.0 = Debug|Win32 - {B3ABCC72-91F9-40CB-A8F4-CB9C1400C5FC}.Release|Win32.ActiveCfg = Release|Win32 - {B3ABCC72-91F9-40CB-A8F4-CB9C1400C5FC}.Release|Win32.Build.0 = Release|Win32 - {B3ABCC72-91F9-40CB-A8F4-CB9C1400C5FC}.Debug|x64.ActiveCfg = Debug|x64 - {B3ABCC72-91F9-40CB-A8F4-CB9C1400C5FC}.Debug|x64.Build.0 = Debug|x64 - {B3ABCC72-91F9-40CB-A8F4-CB9C1400C5FC}.Release|x64.ActiveCfg = Release|x64 - {B3ABCC72-91F9-40CB-A8F4-CB9C1400C5FC}.Release|x64.Build.0 = Release|x64 + {86BCB988-7719-4050-B570-6863C4169137}.Debug|Win32.ActiveCfg = Debug|Win32 + {86BCB988-7719-4050-B570-6863C4169137}.Debug|Win32.Build.0 = Debug|Win32 + {86BCB988-7719-4050-B570-6863C4169137}.Release|Win32.ActiveCfg = Release|Win32 + {86BCB988-7719-4050-B570-6863C4169137}.Release|Win32.Build.0 = Release|Win32 + {86BCB988-7719-4050-B570-6863C4169137}.Debug|x64.ActiveCfg = Debug|x64 + {86BCB988-7719-4050-B570-6863C4169137}.Debug|x64.Build.0 = Debug|x64 + {86BCB988-7719-4050-B570-6863C4169137}.Release|x64.ActiveCfg = Release|x64 + {86BCB988-7719-4050-B570-6863C4169137}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/setup/devcon/devcon.vcxproj b/setup/devcon/devcon.vcxproj index 096388e69..f8170400c 100644 --- a/setup/devcon/devcon.vcxproj +++ b/setup/devcon/devcon.vcxproj @@ -19,11 +19,11 @@ - {B3ABCC72-91F9-40CB-A8F4-CB9C1400C5FC} + {86BCB988-7719-4050-B570-6863C4169137} $(MSBuildProjectName) Debug Win32 - {6B50AB2B-3ADE-4D0B-A859-3E6EB578AB42} + {23BBD173-5CB7-48C8-8F01-AC3B501E40AB} @@ -169,7 +169,6 @@ - diff --git a/setup/devcon/devcon.vcxproj.Filters b/setup/devcon/devcon.vcxproj.Filters index 38a699dc2..58210edf8 100644 --- a/setup/devcon/devcon.vcxproj.Filters +++ b/setup/devcon/devcon.vcxproj.Filters @@ -3,15 +3,15 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {5372BDCC-9AF5-4735-84C0-9AB168DD5DF1} + {5160D4D4-1A78-4E9D-BF18-F5E7B9C9E472} h;hpp;hxx;hm;inl;inc;xsd - {108192B6-9F15-48AA-A1E2-5C076B2ACFFB} + {DB4773A9-01E9-4B56-BA48-0B6BE056675F} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {FBAAD2FE-6B28-4F66-90D0-2B6662CA1195} + {F55A1D3D-0157-4FC5-A912-775FE35A697D} diff --git a/simbatt/ReadMe.md b/simbatt/ReadMe.md new file mode 100644 index 000000000..deae406bf --- /dev/null +++ b/simbatt/ReadMe.md @@ -0,0 +1,4 @@ +SimBatt: Battery Battery Driver Sample +====================================== +SimBatt is simulated battery device driver, this source code is intended to demonstrate implementation of Windows battery driver interfaces. +This is a KMDF based sample. You may use this sample as a starting point to implement a battery miniport specific to your needs. \ No newline at end of file diff --git a/simbatt/SimBatt.sln b/simbatt/SimBatt.sln new file mode 100644 index 000000000..c7d0a7087 --- /dev/null +++ b/simbatt/SimBatt.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}") = "simbatt", "func\simbatt.vcxproj", "{C71BB928-C9A7-49F2-9D72-86E14436DC51}" +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 + {C71BB928-C9A7-49F2-9D72-86E14436DC51}.Debug|Win32.ActiveCfg = Debug|Win32 + {C71BB928-C9A7-49F2-9D72-86E14436DC51}.Debug|Win32.Build.0 = Debug|Win32 + {C71BB928-C9A7-49F2-9D72-86E14436DC51}.Release|Win32.ActiveCfg = Release|Win32 + {C71BB928-C9A7-49F2-9D72-86E14436DC51}.Release|Win32.Build.0 = Release|Win32 + {C71BB928-C9A7-49F2-9D72-86E14436DC51}.Debug|x64.ActiveCfg = Debug|x64 + {C71BB928-C9A7-49F2-9D72-86E14436DC51}.Debug|x64.Build.0 = Debug|x64 + {C71BB928-C9A7-49F2-9D72-86E14436DC51}.Release|x64.ActiveCfg = Release|x64 + {C71BB928-C9A7-49F2-9D72-86E14436DC51}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/simbatt/func/batclass_prepublish.h b/simbatt/func/batclass_prepublish.h new file mode 100644 index 000000000..a952222b3 --- /dev/null +++ b/simbatt/func/batclass_prepublish.h @@ -0,0 +1,51 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + batclass_prepublish.h + +Abstract: + + This module defines pre-publish definations to be made available in DDK/SDK. + + N.B. This file must not be included when batclass.h defines + BATTERY_MINIPORT_INFO_V1_1 type and BATTERY_CLASS_MINOR_VERSION_1. + + N.B. This code is provided "AS IS" without any expressed or implied warranty. + +--*/ + +//---------------------------------------------------------------------- Pragmas + +#pragma once + +//--------------------------------------------------------------------- Includes + +#include + +//-------------------------------------------------- Would be Public Definitions + +#ifndef BATTERY_CLASS_MINOR_VERSION_1 + +typedef struct { + USHORT MajorVersion; + USHORT MinorVersion; + + PVOID Context; // Miniport context + + BCLASS_QUERY_TAG QueryTag; + BCLASS_QUERY_INFORMATION QueryInformation; + BCLASS_SET_INFORMATION SetInformation; + BCLASS_QUERY_STATUS QueryStatus; + BCLASS_SET_STATUS_NOTIFY SetStatusNotify; + BCLASS_DISABLE_STATUS_NOTIFY DisableStatusNotify; + PDEVICE_OBJECT Pdo; + PUNICODE_STRING DeviceName; + PDEVICE_OBJECT Fdo; +} BATTERY_MINIPORT_INFO_V1_1, *PBATTERY_MINIPORT_INFO_V1_1; + +#define BATTERY_CLASS_MINOR_VERSION_1 0x0001 + +#endif // BATTERY_CLASS_MINOR_VERSION_1 diff --git a/simbatt/func/miniclass.c b/simbatt/func/miniclass.c new file mode 100644 index 000000000..ba2df340b --- /dev/null +++ b/simbatt/func/miniclass.c @@ -0,0 +1,1634 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + miniclass.c + +Abstract: + + This module implements battery miniclass functionality specific to the + simulated battery driver. + + N.B. This code is provided "AS IS" without any expressed or implied warranty. + +--*/ + +//--------------------------------------------------------------------- Includes + +#include "simbatt.h" +#include "simbattdriverif.h" + +//--------------------------------------------------------------------- Literals + +#define DEFAULT_NAME L"SimulatedBattery" +#define DEFAULT_MANUFACTURER L"Microsoft Corp" +#define DEFAULT_SERIALNO L"0000" +#define DEFAULT_UNIQUEID L"SimulatedBattery0000" + +//------------------------------------------------------------------- Prototypes + +_IRQL_requires_same_ +VOID +SimBattUpdateTag ( + _Inout_ PSIMBATT_FDO_DATA DevExt + ); + +BCLASS_QUERY_TAG_CALLBACK SimBattQueryTag; +BCLASS_QUERY_INFORMATION_CALLBACK SimBattQueryInformation; +BCLASS_SET_INFORMATION_CALLBACK SimBattSetInformation; +BCLASS_QUERY_STATUS_CALLBACK SimBattQueryStatus; +BCLASS_SET_STATUS_NOTIFY_CALLBACK SimBattSetStatusNotify; +BCLASS_DISABLE_STATUS_NOTIFY_CALLBACK SimBattDisableStatusNotify; + +_Must_inspect_result_ +_Success_(return==STATUS_SUCCESS) +NTSTATUS +SimBattSetBatteryStatus ( + _In_ WDFDEVICE Device, + _In_ PBATTERY_STATUS BatteryStatus + ); + +_Must_inspect_result_ +_Success_(return==STATUS_SUCCESS) +NTSTATUS +SimBattSetBatteryInformation ( + _In_ WDFDEVICE Device, + _In_ PBATTERY_INFORMATION BatteryInformation + ); + +_Must_inspect_result_ +_Success_(return==STATUS_SUCCESS) +NTSTATUS +SimBattSetBatteryManufactureDate ( + _In_ WDFDEVICE Device, + _In_ PBATTERY_MANUFACTURE_DATE ManufactureDate + ); + +_Must_inspect_result_ +_Success_(return==STATUS_SUCCESS) +NTSTATUS +SimBattSetBatteryGranularityScale ( + _In_ WDFDEVICE Device, + _In_reads_(ScaleCount) PBATTERY_REPORTING_SCALE Scale, + _In_ ULONG ScaleCount + ); + +_Must_inspect_result_ +_Success_(return==STATUS_SUCCESS) +NTSTATUS +SimBattSetBatteryEstimatedTime ( + _In_ WDFDEVICE Device, + _In_ ULONG EstimatedTime + ); + +_Must_inspect_result_ +_Success_(return==STATUS_SUCCESS) +NTSTATUS +SimBattSetBatteryTemperature ( + _In_ WDFDEVICE Device, + _In_ ULONG Temperature + ); + +_Success_(return==STATUS_SUCCESS) +NTSTATUS +SimBattSetBatteryString ( + _In_ PCWSTR String, + _Out_writes_(MAX_BATTERY_STRING_SIZE) PWCHAR Destination + ); + +_Must_inspect_result_ +_Success_(return==STATUS_SUCCESS) +NTSTATUS +SimBattGetBatteryMaxChargingCurrent ( + _In_ WDFDEVICE Device, + _Out_ PULONG MaxChargingCurrent + ); + +_Must_inspect_result_ +_Success_(return==STATUS_SUCCESS) +NTSTATUS +GetSimBattStateFromRegistry ( + _In_ WDFDEVICE Device, + _Out_ PSIMBATT_STATE State + ); + +_Success_(return==STATUS_SUCCESS) +NTSTATUS +SaveSimBattStateToRegistry ( + _In_ WDFDEVICE Device, + _In_ PSIMBATT_STATE State + ); + +//---------------------------------------------------------------------- Pragmas + +#pragma alloc_text(PAGE, SimBattPrepareHardware) +#pragma alloc_text(PAGE, SimBattUpdateTag) +#pragma alloc_text(PAGE, SimBattQueryTag) +#pragma alloc_text(PAGE, SimBattQueryInformation) +#pragma alloc_text(PAGE, SimBattQueryStatus) +#pragma alloc_text(PAGE, SimBattSetStatusNotify) +#pragma alloc_text(PAGE, SimBattDisableStatusNotify) +#pragma alloc_text(PAGE, SimBattSetInformation) +#pragma alloc_text(PAGE, SimBattIoDeviceControl) +#pragma alloc_text(PAGE, SimBattSetBatteryStatus) +#pragma alloc_text(PAGE, SimBattSetBatteryInformation) +#pragma alloc_text(PAGE, SimBattSetBatteryManufactureDate) +#pragma alloc_text(PAGE, SimBattSetBatteryGranularityScale) +#pragma alloc_text(PAGE, SimBattSetBatteryEstimatedTime) +#pragma alloc_text(PAGE, SimBattSetBatteryTemperature) +#pragma alloc_text(PAGE, SimBattSetBatteryString) +#pragma alloc_text(PAGE, SimBattGetBatteryMaxChargingCurrent) +#pragma alloc_text(PAGE, GetSimBattStateFromRegistry) +#pragma alloc_text(PAGE, SaveSimBattStateToRegistry) + +//------------------------------------------------------------ Battery Interface + +_Use_decl_annotations_ +VOID +SimBattPrepareHardware ( + WDFDEVICE Device + ) + +/*++ + +Routine Description: + + This routine is called to initialize battery data to sane values. + + A real battery would query hardware to determine if a battery is present, + query its static capabilities, etc. + +Arguments: + + Device - Supplies the device to initialize. + +Return Value: + + NTSTATUS + +--*/ + +{ + + PSIMBATT_FDO_DATA DevExt; + WDF_OBJECT_ATTRIBUTES MemoryAttributes; + WDFMEMORY MemoryObject; + PSIMBATT_STATE RegState; + NTSTATUS Status; + + DebugEnter(); + PAGED_CODE(); + + DevExt = GetDeviceExtension(Device); + + // + // Get this battery's state stored in the registry, otherwise use defaults. + // + + RegState = NULL; + WDF_OBJECT_ATTRIBUTES_INIT(&MemoryAttributes); + MemoryAttributes.ParentObject = Device; + Status = WdfMemoryCreate(&MemoryAttributes, + PagedPool, + SIMBATT_TAG, + sizeof(SIMBATT_STATE), + &MemoryObject, + &((PVOID)(RegState))); + + if (!NT_SUCCESS(Status)) { + goto SimBattPrepareHardwareEnd; + } + + Status = GetSimBattStateFromRegistry(Device, RegState); + if (!NT_SUCCESS(Status)) { + + RtlZeroMemory(RegState, sizeof(SIMBATT_STATE)); + WdfWaitLockAcquire(DevExt->StateLock, NULL); + SimBattUpdateTag(DevExt); + DevExt->State.Version = RegState->Version; + DevExt->State.BatteryStatus.PowerState = RegState->BatteryStatus.PowerState; + DevExt->State.BatteryStatus.Capacity = RegState->BatteryStatus.Capacity; + DevExt->State.BatteryStatus.Voltage = RegState->BatteryStatus.Voltage; + DevExt->State.BatteryStatus.Rate = RegState->BatteryStatus.Rate; + DevExt->State.BatteryInfo.Capabilities = RegState->BatteryInfo.Capabilities; + DevExt->State.BatteryInfo.Technology = RegState->BatteryInfo.Technology; + DevExt->State.BatteryInfo.Chemistry[0] = RegState->BatteryInfo.Chemistry[0]; + DevExt->State.BatteryInfo.Chemistry[1] = RegState->BatteryInfo.Chemistry[1]; + DevExt->State.BatteryInfo.Chemistry[2] = RegState->BatteryInfo.Chemistry[2]; + DevExt->State.BatteryInfo.Chemistry[3] = RegState->BatteryInfo.Chemistry[3]; + DevExt->State.BatteryInfo.DesignedCapacity = RegState->BatteryInfo.DesignedCapacity; + DevExt->State.BatteryInfo.FullChargedCapacity = RegState->BatteryInfo.FullChargedCapacity; + DevExt->State.BatteryInfo.DefaultAlert1 = RegState->BatteryInfo.DefaultAlert1; + DevExt->State.BatteryInfo.DefaultAlert2 = RegState->BatteryInfo.DefaultAlert2; + DevExt->State.BatteryInfo.CriticalBias = RegState->BatteryInfo.CriticalBias; + DevExt->State.BatteryInfo.CycleCount = RegState->BatteryInfo.CycleCount; + DevExt->State.MaxCurrentDraw = RegState->MaxCurrentDraw; + SimBattSetBatteryString(RegState->DeviceName, DevExt->State.DeviceName); + SimBattSetBatteryString(RegState->ManufacturerName, DevExt->State.ManufacturerName); + SimBattSetBatteryString(RegState->SerialNumber, DevExt->State.SerialNumber); + SimBattSetBatteryString(RegState->UniqueId, DevExt->State.UniqueId); + WdfWaitLockRelease(DevExt->StateLock); + + } else { + WdfWaitLockAcquire(DevExt->StateLock, NULL); + SimBattUpdateTag(DevExt); + DevExt->State.Version = SIMBATT_STATE_VERSION; + DevExt->State.BatteryStatus.PowerState = BATTERY_POWER_ON_LINE; + DevExt->State.BatteryStatus.Capacity = 100; + DevExt->State.BatteryStatus.Voltage = BATTERY_UNKNOWN_VOLTAGE; + DevExt->State.BatteryStatus.Rate = 0; + DevExt->State.BatteryInfo.Capabilities = BATTERY_SYSTEM_BATTERY | + BATTERY_CAPACITY_RELATIVE; + + DevExt->State.BatteryInfo.Technology = 1; + DevExt->State.BatteryInfo.Chemistry[0] = 'F'; + DevExt->State.BatteryInfo.Chemistry[1] = 'a'; + DevExt->State.BatteryInfo.Chemistry[2] = 'k'; + DevExt->State.BatteryInfo.Chemistry[3] = 'e'; + DevExt->State.BatteryInfo.DesignedCapacity = 100; + DevExt->State.BatteryInfo.FullChargedCapacity = 100; + DevExt->State.BatteryInfo.DefaultAlert1 = 0; + DevExt->State.BatteryInfo.DefaultAlert2 = 0; + DevExt->State.BatteryInfo.CriticalBias = 0; + DevExt->State.BatteryInfo.CycleCount = 100; + DevExt->State.MaxCurrentDraw = UNKNOWN_CURRENT; + SimBattSetBatteryString(DEFAULT_NAME, DevExt->State.DeviceName); + SimBattSetBatteryString(DEFAULT_MANUFACTURER, + DevExt->State.ManufacturerName); + + SimBattSetBatteryString(DEFAULT_SERIALNO, DevExt->State.SerialNumber); + SimBattSetBatteryString(DEFAULT_UNIQUEID, DevExt->State.UniqueId); + WdfWaitLockRelease(DevExt->StateLock); + + // + // Save new defaults to registry. + // Normally this only happens the first time the device starts after + // install because the key should exist after that point. + // + + SaveSimBattStateToRegistry(Device, &DevExt->State); + } + + if (RegState != NULL) { + WdfObjectDelete(MemoryObject); + } + +SimBattPrepareHardwareEnd: + DebugExitStatus(Status); + return; +} + +_Use_decl_annotations_ +VOID +SimBattUpdateTag ( + PSIMBATT_FDO_DATA DevExt + ) + +/*++ + +Routine Description: + + This routine is called when static battery properties have changed to + update the battery tag. + +Arguments: + + DevExt - Supplies a pointer to the device extension of the battery to + update. + +Return Value: + + None + +--*/ + +{ + + PAGED_CODE(); + + DevExt->BatteryTag += 1; + if (DevExt->BatteryTag == BATTERY_TAG_INVALID) { + DevExt->BatteryTag += 1; + } + + return; +} + +_Use_decl_annotations_ +NTSTATUS +SimBattQueryTag ( + PVOID Context, + PULONG BatteryTag + ) + +/*++ + +Routine Description: + + This routine is called to get the value of the current battery tag. + +Arguments: + + Context - Supplies the miniport context value for battery + + BatteryTag - Supplies a pointer to a ULONG to receive the battery tag. + +Return Value: + + NTSTATUS + +--*/ + +{ + PSIMBATT_FDO_DATA DevExt; + NTSTATUS Status; + + DebugEnter(); + PAGED_CODE(); + + DevExt = (PSIMBATT_FDO_DATA)Context; + WdfWaitLockAcquire(DevExt->StateLock, NULL); + *BatteryTag = DevExt->BatteryTag; + WdfWaitLockRelease(DevExt->StateLock); + if (*BatteryTag == BATTERY_TAG_INVALID) { + Status = STATUS_NO_SUCH_DEVICE; + } else { + Status = STATUS_SUCCESS; + } + + DebugExitStatus(Status); + return Status; +} + +_Use_decl_annotations_ +NTSTATUS +SimBattQueryInformation ( + PVOID Context, + ULONG BatteryTag, + BATTERY_QUERY_INFORMATION_LEVEL Level, + LONG AtRate, + PVOID Buffer, + ULONG BufferLength, + PULONG ReturnedLength + ) + +/*++ + +Routine Description: + + Called by the class driver to retrieve battery information + + The battery class driver will serialize all requests it issues to + the miniport for a given battery. + + Return invalid parameter when a request for a specific level of information + can't be handled. This is defined in the battery class spec. + +Arguments: + + Context - Supplies the miniport context value for battery + + BatteryTag - Supplies the tag of current battery + + Level - Supplies the type of information required + + AtRate - Supplies the rate of drain for the BatteryEstimatedTime level + + Buffer - Supplies a pointer to a buffer to place the information + + BufferLength - Supplies the length in bytes of the buffer + + ReturnedLength - Supplies the length in bytes of the returned data + +Return Value: + + Success if there is a battery currently installed, else no such device. + +--*/ + +{ + PSIMBATT_FDO_DATA DevExt; + ULONG ResultValue; + PVOID ReturnBuffer; + size_t ReturnBufferLength; + NTSTATUS Status; + + UNREFERENCED_PARAMETER(AtRate); + + DebugEnter(); + PAGED_CODE(); + + DevExt = (PSIMBATT_FDO_DATA)Context; + WdfWaitLockAcquire(DevExt->StateLock, NULL); + if (BatteryTag != DevExt->BatteryTag) { + Status = STATUS_NO_SUCH_DEVICE; + goto QueryInformationEnd; + } + + // + // Determine the value of the information being queried for and return it. + // In a real battery, this would require hardware/firmware accesses. The + // simulated battery fakes this by storing the data to be returned in + // memory. + // + + ReturnBuffer = NULL; + ReturnBufferLength = 0; + DebugPrint(SIMBATT_INFO, "Query for information level 0x%x\n", Level); + Status = STATUS_INVALID_DEVICE_REQUEST; + switch (Level) { + case BatteryInformation: + ReturnBuffer = &DevExt->State.BatteryInfo; + ReturnBufferLength = sizeof(BATTERY_INFORMATION); + Status = STATUS_SUCCESS; + break; + + case BatteryEstimatedTime: + if (DevExt->State.EstimatedTime == SIMBATT_RATE_CALCULATE) { + if (AtRate == 0) { + AtRate = DevExt->State.BatteryStatus.Rate; + } + + if (AtRate < 0) { + ResultValue = (3600 * DevExt->State.BatteryStatus.Capacity) / + (-AtRate); + + } else { + ResultValue = BATTERY_UNKNOWN_TIME; + } + + } else { + ResultValue = DevExt->State.EstimatedTime; + } + + ReturnBuffer = &ResultValue; + ReturnBufferLength = sizeof(ResultValue); + Status = STATUS_SUCCESS; + break; + + case BatteryUniqueID: + ReturnBuffer = DevExt->State.UniqueId; + Status = RtlStringCbLengthW(DevExt->State.UniqueId, + sizeof(DevExt->State.UniqueId), + &ReturnBufferLength); + + ReturnBufferLength += sizeof(WCHAR); + break; + + case BatteryManufactureName: + ReturnBuffer = DevExt->State.ManufacturerName; + Status = RtlStringCbLengthW(DevExt->State.ManufacturerName, + sizeof(DevExt->State.ManufacturerName), + &ReturnBufferLength); + + ReturnBufferLength += sizeof(WCHAR); + break; + + case BatteryDeviceName: + ReturnBuffer = DevExt->State.DeviceName; + Status = RtlStringCbLengthW(DevExt->State.DeviceName, + sizeof(DevExt->State.DeviceName), + &ReturnBufferLength); + + ReturnBufferLength += sizeof(WCHAR); + break; + + case BatterySerialNumber: + ReturnBuffer = DevExt->State.SerialNumber; + Status = RtlStringCbLengthW(DevExt->State.SerialNumber, + sizeof(DevExt->State.SerialNumber), + &ReturnBufferLength); + + ReturnBufferLength += sizeof(WCHAR); + break; + + case BatteryManufactureDate: + if (DevExt->State.ManufactureDate.Day != 0) { + ReturnBuffer = &DevExt->State.ManufactureDate; + ReturnBufferLength = sizeof(BATTERY_MANUFACTURE_DATE); + Status = STATUS_SUCCESS; + } + + break; + + case BatteryGranularityInformation: + if (DevExt->State.GranularityCount > 0) { + ReturnBuffer = DevExt->State.GranularityScale; + ReturnBufferLength = DevExt->State.GranularityCount * + sizeof(BATTERY_REPORTING_SCALE); + + Status = STATUS_SUCCESS; + } + + break; + + case BatteryTemperature: + ReturnBuffer = &DevExt->State.Temperature; + ReturnBufferLength = sizeof(ULONG); + Status = STATUS_SUCCESS; + break; + + default: + Status = STATUS_INVALID_PARAMETER; + break; + } + + NT_ASSERT(((ReturnBufferLength == 0) && (ReturnBuffer == NULL)) || + ((ReturnBufferLength > 0) && (ReturnBuffer != NULL))); + + if (NT_SUCCESS(Status)) { + *ReturnedLength = (ULONG)ReturnBufferLength; + if (ReturnBuffer != NULL) { + if ((Buffer == NULL) || (BufferLength < ReturnBufferLength)) { + Status = STATUS_BUFFER_TOO_SMALL; + + } else { + memcpy(Buffer, ReturnBuffer, ReturnBufferLength); + } + } + + } else { + *ReturnedLength = 0; + } + +QueryInformationEnd: + WdfWaitLockRelease(DevExt->StateLock); + DebugExitStatus(Status); + return Status; +} + +_Use_decl_annotations_ +NTSTATUS +SimBattQueryStatus ( + PVOID Context, + ULONG BatteryTag, + PBATTERY_STATUS BatteryStatus + ) + +/*++ + +Routine Description: + + Called by the class driver to retrieve the batteries current status + + The battery class driver will serialize all requests it issues to + the miniport for a given battery. + +Arguments: + + Context - Supplies the miniport context value for battery + + BatteryTag - Supplies the tag of current battery + + BatteryStatus - Supplies a pointer to the structure to return the current + battery status in + +Return Value: + + Success if there is a battery currently installed, else no such device. + +--*/ + +{ + PSIMBATT_FDO_DATA DevExt; + NTSTATUS Status; + + DebugEnter(); + PAGED_CODE(); + + DevExt = (PSIMBATT_FDO_DATA)Context; + WdfWaitLockAcquire(DevExt->StateLock, NULL); + if (BatteryTag != DevExt->BatteryTag) { + Status = STATUS_NO_SUCH_DEVICE; + goto QueryStatusEnd; + } + + RtlCopyMemory(BatteryStatus, + &DevExt->State.BatteryStatus, + sizeof(BATTERY_STATUS)); + + Status = STATUS_SUCCESS; + +QueryStatusEnd: + WdfWaitLockRelease(DevExt->StateLock); + DebugExitStatus(Status); + return Status; +} + +_Use_decl_annotations_ +NTSTATUS +SimBattSetStatusNotify ( + PVOID Context, + ULONG BatteryTag, + PBATTERY_NOTIFY BatteryNotify + ) + +/*++ + +Routine Description: + + Called by the class driver to set the capacity and power state levels + at which the class driver requires notification. + + The battery class driver will serialize all requests it issues to + the miniport for a given battery. + +Arguments: + + Context - Supplies the miniport context value for battery + + BatteryTag - Supplies the tag of current battery + + BatteryNotify - Supplies a pointer to a structure containing the + notification critera. + +Return Value: + + Success if there is a battery currently installed, else no such device. + +--*/ + +{ + PSIMBATT_FDO_DATA DevExt; + NTSTATUS Status; + + UNREFERENCED_PARAMETER(BatteryNotify); + + DebugEnter(); + PAGED_CODE(); + + DevExt = (PSIMBATT_FDO_DATA)Context; + WdfWaitLockAcquire(DevExt->StateLock, NULL); + if (BatteryTag != DevExt->BatteryTag) { + Status = STATUS_NO_SUCH_DEVICE; + goto SetStatusNotifyEnd; + } + + Status = STATUS_NOT_SUPPORTED; + +SetStatusNotifyEnd: + WdfWaitLockRelease(DevExt->StateLock); + DebugExitStatus(Status); + return Status; +} + +_Use_decl_annotations_ +NTSTATUS +SimBattDisableStatusNotify ( + PVOID Context + ) + +/*++ + +Routine Description: + + Called by the class driver to disable notification. + + The battery class driver will serialize all requests it issues to + the miniport for a given battery. + +Arguments: + + Context - Supplies the miniport context value for battery + +Return Value: + + Success if there is a battery currently installed, else no such device. + +--*/ + +{ + NTSTATUS Status; + + UNREFERENCED_PARAMETER(Context); + + DebugEnter(); + PAGED_CODE(); + + Status = STATUS_NOT_SUPPORTED; + DebugExitStatus(Status); + return Status; +} + +_Use_decl_annotations_ +NTSTATUS +SimBattSetInformation ( + PVOID Context, + ULONG BatteryTag, + BATTERY_SET_INFORMATION_LEVEL Level, + PVOID Buffer + ) + +/* + Routine Description: + + Called by the class driver to set the battery's charge/discharge state, + critical bias, or charge current. + +Arguments: + + Context - Supplies the miniport context value for battery + + BatteryTag - Supplies the tag of current battery + + Level - Supplies action requested + + Buffer - Supplies a critical bias value if level is BatteryCriticalBias. + +Return Value: + + NTSTATUS + +--*/ + +{ + PBATTERY_CHARGING_SOURCE ChargingSource; + PSIMBATT_FDO_DATA DevExt; + NTSTATUS Status; + + DebugEnter(); + PAGED_CODE(); + + DevExt = (PSIMBATT_FDO_DATA)Context; + WdfWaitLockAcquire(DevExt->StateLock, NULL); + if (BatteryTag != DevExt->BatteryTag) { + Status = STATUS_NO_SUCH_DEVICE; + goto SetInformationEnd; + } + + if (Buffer == NULL) { + Status = STATUS_INVALID_PARAMETER_4; + + } else if (Level == BatteryChargingSource) { + ChargingSource = (PBATTERY_CHARGING_SOURCE)Buffer; + DevExt->State.MaxCurrentDraw = ChargingSource->MaxCurrent; + DebugPrint(SIMBATT_INFO, + "SimBatt : Set MaxCurrentDraw = %u mA\n", + DevExt->State.MaxCurrentDraw); + + Status = STATUS_SUCCESS; + } else { + Status = STATUS_NOT_SUPPORTED; + } + +SetInformationEnd: + WdfWaitLockRelease(DevExt->StateLock); + DebugExitStatus(Status); + return Status; +} + +//------------------------------------------------- Battery Simulation Interface +// +// The following IO control handler and associated SimBattSetXxx routines +// implement the control side of the simulated battery. A real battery would +// not implement this interface, and instead read battery data from hardware/ +// firmware interfaces. +// + +VOID +SimBattIoDeviceControl ( + WDFQUEUE Queue, + WDFREQUEST Request, + size_t OutputBufferLength, + size_t InputBufferLength, + ULONG IoControlCode + ) + +/*++ + +Routine Description: + + Handle changes to the simulated battery state. + +Arguments: + + Queue - Supplies a handle to the framework queue object that is associated + with the I/O request. + + Request - Supplies a handle to a framework request object. This one + represents the IRP_MJ_DEVICE_CONTROL IRP received by the framework. + + OutputBufferLength - Supplies the length, in bytes, of the request's output + buffer, if an output buffer is available. + + InputBufferLength - Supplies the length, in bytes, of the request's input + buffer, if an input buffer is available. + + IoControlCode - Supplies the Driver-defined or system-defined I/O control + code (IOCTL) that is associated with the request. + +Return Value: + + VOID + +--*/ + +{ + + PBATTERY_INFORMATION BatteryInformation; + PBATTERY_STATUS BatteryStatus; + ULONG BytesReturned; + PWCHAR DestinationString; + PSIMBATT_FDO_DATA DevExt; + WDFDEVICE Device; + PULONG EstimatedRunTime; + ULONG GranularityEntries; + PBATTERY_REPORTING_SCALE GranularityScale; + size_t Length; + PBATTERY_MANUFACTURE_DATE ManufactureDate; + PULONG MaxCurrentDraw; + NTSTATUS Status; + PWSTR String; + PULONG Temperature; + NTSTATUS TempStatus; + + UNREFERENCED_PARAMETER(OutputBufferLength); + + PAGED_CODE(); + + BytesReturned = 0; + Device = WdfIoQueueGetDevice(Queue); + DevExt = GetDeviceExtension(Device); + DebugPrint(SIMBATT_INFO, "SimBattIoDeviceControl: 0x%p\n", Device); + Status = STATUS_INVALID_PARAMETER; + switch (IoControlCode) { + case IOCTL_SIMBATT_SET_STATUS: + TempStatus = WdfRequestRetrieveInputBuffer(Request, + sizeof(BATTERY_STATUS), + &BatteryStatus, + &Length); + + if (NT_SUCCESS(TempStatus) && (Length == sizeof(BATTERY_STATUS))) { + Status = SimBattSetBatteryStatus(Device, BatteryStatus); + } + + break; + + case IOCTL_SIMBATT_SET_INFORMATION: + TempStatus = WdfRequestRetrieveInputBuffer(Request, + sizeof(BATTERY_INFORMATION), + &BatteryInformation, + &Length); + + if (NT_SUCCESS(TempStatus) && (Length == sizeof(BATTERY_INFORMATION))) { + Status = SimBattSetBatteryInformation(Device, BatteryInformation); + } + + break; + + case IOCTL_SIMBATT_GET_MAXCHARGINGCURRENT: + TempStatus = WdfRequestRetrieveOutputBuffer(Request, + sizeof(*MaxCurrentDraw), + &MaxCurrentDraw, + &Length); + + if (NT_SUCCESS(TempStatus) && (Length == sizeof(ULONG))) { + Status = SimBattGetBatteryMaxChargingCurrent(Device, + MaxCurrentDraw); + + if (NT_SUCCESS(Status)) { + BytesReturned = sizeof(*MaxCurrentDraw); + } + } + + break; + + case IOCTL_SIMBATT_SET_MANUFACTURE_DATE: + TempStatus = WdfRequestRetrieveInputBuffer( + Request, + sizeof(BATTERY_MANUFACTURE_DATE), + &ManufactureDate, + &Length); + + if (NT_SUCCESS(TempStatus) && + (Length == sizeof(BATTERY_MANUFACTURE_DATE))) { + + Status = SimBattSetBatteryManufactureDate(Device, ManufactureDate); + } + + break; + + case IOCTL_SIMBATT_SET_TEMPERATURE: + TempStatus = WdfRequestRetrieveInputBuffer(Request, + sizeof(ULONG), + &Temperature, + &Length); + + if (NT_SUCCESS(TempStatus) && (Length == sizeof(ULONG))) { + Status = SimBattSetBatteryTemperature(Device, *Temperature); + } + + break; + + case IOCTL_SIMBATT_SET_ESTIMATED_TIME: + TempStatus = WdfRequestRetrieveInputBuffer(Request, + sizeof(ULONG), + &EstimatedRunTime, + &Length); + + if (NT_SUCCESS(TempStatus) && (Length == sizeof(ULONG))) { + Status = SimBattSetBatteryEstimatedTime(Device, *EstimatedRunTime); + } + + break; + + case IOCTL_SIMBATT_SET_GRANULARITY_INFORMATION: + GranularityEntries = (ULONG)(InputBufferLength / + sizeof(PBATTERY_REPORTING_SCALE)); + + TempStatus = WdfRequestRetrieveInputBuffer( + Request, + GranularityEntries * sizeof(PBATTERY_REPORTING_SCALE), + &GranularityScale, + &Length); + + if (NT_SUCCESS(TempStatus) && + (Length == GranularityEntries * sizeof(PBATTERY_REPORTING_SCALE))) { + + Status = SimBattSetBatteryGranularityScale(Device, + GranularityScale, + GranularityEntries); + } + + break; + + case IOCTL_SIMBATT_SET_DEVICE_NAME: + case IOCTL_SIMBATT_SET_MANUFACTURE_NAME: + case IOCTL_SIMBATT_SET_SERIAL_NUMBER: + case IOCTL_SIMBATT_SET_UNIQUE_ID: + TempStatus = WdfRequestRetrieveInputBuffer(Request, + sizeof(WCHAR), + &String, + &Length); + + if (NT_SUCCESS(TempStatus) && + (Length % sizeof(WCHAR) == 0) && + (String[(Length / sizeof(WCHAR)) - 1] == UNICODE_NULL)) { + + // + // Explicitly set the terminating null to silence prefast + // warnings. + // + + String[(Length / sizeof(WCHAR)) - 1] = UNICODE_NULL; + switch (IoControlCode) { + case IOCTL_SIMBATT_SET_DEVICE_NAME: + DestinationString = DevExt->State.DeviceName; + break; + + case IOCTL_SIMBATT_SET_MANUFACTURE_NAME: + DestinationString = DevExt->State.ManufacturerName; + break; + + case IOCTL_SIMBATT_SET_SERIAL_NUMBER: + DestinationString = DevExt->State.SerialNumber; + break; + + case IOCTL_SIMBATT_SET_UNIQUE_ID: + DestinationString = DevExt->State.UniqueId; + break; + + default: + DestinationString = NULL; + break; + } + + // + // Supress invalid failure: Redundant Pointer Test on DestinationString + // + + #pragma warning(suppress: 28922) + if (DestinationString != NULL) { + WdfWaitLockAcquire(DevExt->StateLock, NULL); + SimBattUpdateTag(DevExt); + Status = SimBattSetBatteryString(String, DestinationString); + WdfWaitLockRelease(DevExt->StateLock); + } + } + + default: + break; + } + + // + // Update the state stored in registry since the state has likely changed. + // + + SaveSimBattStateToRegistry(Device, &DevExt->State); + WdfRequestCompleteWithInformation(Request, Status, BytesReturned); + return; +} + +_Use_decl_annotations_ +NTSTATUS +SimBattSetBatteryStatus ( + WDFDEVICE Device, + PBATTERY_STATUS BatteryStatus + ) + +/*++ + +Routine Description: + + Set the simulated battery status structure values. + +Arguments: + + Device - Supplies the device to set data for. + + BatteryStatus - Supplies the new status data to set. + +Return Value: + + NTSTATUS + +--*/ + +{ + + PSIMBATT_FDO_DATA DevExt; + NTSTATUS Status; + ULONG ValidPowerState; + + PAGED_CODE(); + + Status = STATUS_INVALID_PARAMETER; + DevExt = GetDeviceExtension(Device); + ValidPowerState = BATTERY_CHARGING | + BATTERY_DISCHARGING | + BATTERY_CRITICAL | + BATTERY_POWER_ON_LINE; + + if ((BatteryStatus->PowerState & ~ValidPowerState) != 0) { + goto SetBatteryStatusEnd; + } + + WdfWaitLockAcquire(DevExt->StateLock, NULL); + DevExt->State.BatteryStatus.PowerState = BatteryStatus->PowerState; + DevExt->State.BatteryStatus.Capacity = BatteryStatus->Capacity; + DevExt->State.BatteryStatus.Voltage = BatteryStatus->Voltage; + DevExt->State.BatteryStatus.Rate = BatteryStatus->Rate; + WdfWaitLockRelease(DevExt->StateLock); + BatteryClassStatusNotify(DevExt->ClassHandle); + Status = STATUS_SUCCESS; + +SetBatteryStatusEnd: + return Status; +} + +_Use_decl_annotations_ +NTSTATUS +SimBattSetBatteryInformation ( + WDFDEVICE Device, + PBATTERY_INFORMATION BatteryInformation + ) + +/*++ + +Routine Description: + + Set the simulated battery information structure values. + +Arguments: + + Device - Supplies the device to set data for. + + BatteryInformation - Supplies the new information data to set. + +Return Value: + + NTSTATUS + +--*/ + +{ + + PSIMBATT_FDO_DATA DevExt; + NTSTATUS Status; + ULONG ValidCapabilities; + + PAGED_CODE(); + + Status = STATUS_INVALID_PARAMETER; + DevExt = GetDeviceExtension(Device); + ValidCapabilities = BATTERY_CAPACITY_RELATIVE | + BATTERY_IS_SHORT_TERM | + BATTERY_SYSTEM_BATTERY; + + if ((BatteryInformation->Capabilities & ~ValidCapabilities) != 0) { + goto SetBatteryInformationEnd; + } + + if (BatteryInformation->Technology > 1) { + goto SetBatteryInformationEnd; + } + + WdfWaitLockAcquire(DevExt->StateLock, NULL); + DevExt->State.BatteryInfo.Capabilities = BatteryInformation->Capabilities; + DevExt->State.BatteryInfo.Technology = BatteryInformation->Technology; + DevExt->State.BatteryInfo.Chemistry[0] = BatteryInformation->Chemistry[0]; + DevExt->State.BatteryInfo.Chemistry[1] = BatteryInformation->Chemistry[1]; + DevExt->State.BatteryInfo.Chemistry[2] = BatteryInformation->Chemistry[2]; + DevExt->State.BatteryInfo.Chemistry[3] = BatteryInformation->Chemistry[3]; + DevExt->State.BatteryInfo.DesignedCapacity = + BatteryInformation->DesignedCapacity; + + DevExt->State.BatteryInfo.FullChargedCapacity = + BatteryInformation->FullChargedCapacity; + + DevExt->State.BatteryInfo.DefaultAlert1 = BatteryInformation->DefaultAlert1; + DevExt->State.BatteryInfo.DefaultAlert2 = BatteryInformation->DefaultAlert2; + DevExt->State.BatteryInfo.CriticalBias = BatteryInformation->CriticalBias; + DevExt->State.BatteryInfo.CycleCount = BatteryInformation->CycleCount; + + // + // To indicate that battery information has changed, update the battery tag + // and notify the class driver that the battery status has updated. The + // status query will fail due to a different battery tag, causing the class + // driver to query for the new tag and new information. + // + + SimBattUpdateTag(DevExt); + WdfWaitLockRelease(DevExt->StateLock); + BatteryClassStatusNotify(DevExt->ClassHandle); + Status = STATUS_SUCCESS; + +SetBatteryInformationEnd: + return Status; +} + +_Use_decl_annotations_ +NTSTATUS +SimBattSetBatteryManufactureDate ( + WDFDEVICE Device, + PBATTERY_MANUFACTURE_DATE ManufactureDate + ) + +/*++ + +Routine Description: + + Set the simulated battery manufacture date structure values. + +Arguments: + + Device - Supplies the device to set data for. + + ManufactureDate - Supplies the new manufacture date to set. + +Return Value: + + NTSTATUS + +--*/ + +{ + + PSIMBATT_FDO_DATA DevExt; + NTSTATUS Status; + + PAGED_CODE(); + + Status = STATUS_INVALID_PARAMETER; + DevExt = GetDeviceExtension(Device); + if ((ManufactureDate->Year == 0) || + (ManufactureDate->Month == 0) || + (ManufactureDate->Day == 0)) { + + // + // All zeroes indicates that the manufacture date is unknown. + // + + if ((ManufactureDate->Year != 0) || + (ManufactureDate->Month != 0) || + (ManufactureDate->Day != 0)) { + + goto SetBatteryManufactureDateEnd; + } + + } else { + + // + // Make sure the dates are close to reasonable. + // + + if ((ManufactureDate->Month > 12) || + (ManufactureDate->Day > 31)) { + + goto SetBatteryManufactureDateEnd; + } + } + + WdfWaitLockAcquire(DevExt->StateLock, NULL); + DevExt->State.ManufactureDate.Year = ManufactureDate->Year; + DevExt->State.ManufactureDate.Month = ManufactureDate->Month; + DevExt->State.ManufactureDate.Day = ManufactureDate->Day; + SimBattUpdateTag(DevExt); + WdfWaitLockRelease(DevExt->StateLock); + Status = STATUS_SUCCESS; + +SetBatteryManufactureDateEnd: + return Status; +} + +_Use_decl_annotations_ +NTSTATUS +SimBattSetBatteryGranularityScale ( + WDFDEVICE Device, + PBATTERY_REPORTING_SCALE Scale, + ULONG ScaleCount + ) + +/*++ + +Routine Description: + + Set the simulated battery status structure values. + +Arguments: + + Device - Supplies the device to set data for. + + Scale - Supplies the new granularity scale to set. + + ScaleCount - Supplies the number of granularity scale entries to set. + +Return Value: + + NTSTATUS + +--*/ + +{ + + PSIMBATT_FDO_DATA DevExt; + ULONG ScaleIndex; + NTSTATUS Status; + + PAGED_CODE(); + + Status = STATUS_INVALID_PARAMETER; + DevExt = GetDeviceExtension(Device); + if (ScaleCount > 4) { + goto SetBatteryGranularityScaleEnd; + } + + // + // Scale regions are listed in increasing order of capacity ranges they + // apply to. + // + + for (ScaleIndex = 1; ScaleIndex < ScaleCount; ScaleIndex += 1) { + if (Scale[ScaleIndex].Capacity <= Scale[ScaleIndex - 1].Capacity) { + goto SetBatteryGranularityScaleEnd; + } + } + + WdfWaitLockAcquire(DevExt->StateLock, NULL); + for (ScaleIndex = 0; ScaleIndex < ScaleCount; ScaleIndex += 1) { + DevExt->State.GranularityScale[ScaleIndex].Granularity = + Scale[ScaleIndex].Granularity; + + DevExt->State.GranularityScale[ScaleIndex].Capacity = + Scale[ScaleIndex].Capacity; + } + + DevExt->State.GranularityCount = ScaleCount; + SimBattUpdateTag(DevExt); + WdfWaitLockRelease(DevExt->StateLock); + Status = STATUS_SUCCESS; + +SetBatteryGranularityScaleEnd: + return Status; +} + +_Use_decl_annotations_ +NTSTATUS +SimBattSetBatteryEstimatedTime ( + WDFDEVICE Device, + ULONG EstimatedTime + ) + +/*++ + +Routine Description: + + Set the simulated battery estimated charge/run time. The value + SIMBATT_RATE_CALCULATE causes the estimated time to be calculated based on + charge/discharge status, the charge/discharge rate, the current capacity, + and the last full charge capacity. + +Arguments: + + Device - Supplies the device to set data for. + + EstimatedTime - Supplies the new estimated run/charge time to set. + +Return Value: + + NTSTATUS + +--*/ + +{ + + PSIMBATT_FDO_DATA DevExt; + + PAGED_CODE(); + + DevExt = GetDeviceExtension(Device); + WdfWaitLockAcquire(DevExt->StateLock, NULL); + DevExt->State.EstimatedTime = EstimatedTime; + WdfWaitLockRelease(DevExt->StateLock); + return STATUS_SUCCESS; +} + +_Use_decl_annotations_ +NTSTATUS +SimBattSetBatteryTemperature ( + WDFDEVICE Device, + ULONG Temperature + ) + +/*++ + +Routine Description: + + Set the simulated battery temperature value. + +Arguments: + + Device - Supplies the device to set data for. + + Temperature - Supplies the new temperature to set. + +Return Value: + + NTSTATUS + +--*/ + +{ + + PSIMBATT_FDO_DATA DevExt; + + PAGED_CODE(); + + DevExt = GetDeviceExtension(Device); + WdfWaitLockAcquire(DevExt->StateLock, NULL); + DevExt->State.Temperature = Temperature; + WdfWaitLockRelease(DevExt->StateLock); + return STATUS_SUCCESS; +} + +_Use_decl_annotations_ +NTSTATUS +SimBattSetBatteryString ( + PCWSTR String, + PWCHAR Destination + ) + +/*++ + +Routine Description: + + Set one of the simulated battery strings. + +Arguments: + + String - Supplies the new string value to set. + + Destination - Supplies a pointer to the buffer to store the new string. + +Return Value: + + NTSTATUS + +--*/ + +{ + + PAGED_CODE(); + + return RtlStringCchCopyW(Destination, MAX_BATTERY_STRING_SIZE, String); +} + +_Use_decl_annotations_ +NTSTATUS +SimBattGetBatteryMaxChargingCurrent ( + WDFDEVICE Device, + PULONG MaxChargingCurrent + ) + +/* + Routine Description: + + Called by the class driver to get the battery's maximum charging current. + +Arguments: + + Context - Supplies the miniport context value for battery + + MaxChargingCurrent - Supplies the pointer to return the value to + +Return Value: + + NTSTATUS + +--*/ + +{ + + PSIMBATT_FDO_DATA DevExt; + NTSTATUS Status; + + PAGED_CODE(); + + DevExt = GetDeviceExtension(Device); + *MaxChargingCurrent = DevExt->State.MaxCurrentDraw; + Status = STATUS_SUCCESS; + + return Status; +} + +_Use_decl_annotations_ +NTSTATUS +GetSimBattStateFromRegistry ( + WDFDEVICE Device, + PSIMBATT_STATE State + ) + +/* + Routine Description: + + Called to return simbatt state data from the registry if it exists. + +Arguments: + + Device - Supplies WDF device handle. + + State - Supplies the pointer to return the simbatt state. + +Return Value: + + NTSTATUS + +--*/ + +{ + + ULONG BufSize; + WDFKEY KeyHandle; + DECLARE_CONST_UNICODE_STRING(SimbattStateRegNameStr, SIMBATT_STATE_REG_NAME); + NTSTATUS Status; + ULONG ValueType; + + PAGED_CODE(); + + Status = WdfDeviceOpenRegistryKey( + Device, + PLUGPLAY_REGKEY_DEVICE, + KEY_READ, + NULL, + &KeyHandle + ); + + if (!NT_SUCCESS (Status)) { + goto GetSimBattStateFromRegistryEnd; + } + + Status = WdfRegistryQueryValue( + KeyHandle, + &SimbattStateRegNameStr, + sizeof(SIMBATT_STATE), + State, + &BufSize, + &ValueType + ); + + WdfRegistryClose(KeyHandle); + + if (!NT_SUCCESS (Status)) { + goto GetSimBattStateFromRegistryEnd; + } + + if (ValueType != REG_BINARY) { + Status = STATUS_INVALID_INFO_CLASS; + goto GetSimBattStateFromRegistryEnd; + } + + if (BufSize != sizeof(SIMBATT_STATE)) { + + // + // WdfRegistryQueryValue will fail if the buffer was too small. + // This check is validating if the data is smaller than the buffer. + // + + Status = STATUS_INFO_LENGTH_MISMATCH; + goto GetSimBattStateFromRegistryEnd; + } + + if (State->Version != SIMBATT_STATE_VERSION) { + Status = STATUS_REVISION_MISMATCH; + goto GetSimBattStateFromRegistryEnd; + } + +GetSimBattStateFromRegistryEnd: + return Status; +} + +_Use_decl_annotations_ +NTSTATUS +SaveSimBattStateToRegistry ( + WDFDEVICE Device, + PSIMBATT_STATE State + ) + +/* + Routine Description: + + Called to save simbatt state data to the registry. + +Arguments: + + Device - Supplies WDF device handle. + + State - Supplies the pointer to the simbatt state. + +Return Value: + + NTSTATUS + +--*/ + +{ + + WDFKEY KeyHandle; + DECLARE_CONST_UNICODE_STRING(SimbattStateRegNameStr, SIMBATT_STATE_REG_NAME); + NTSTATUS Status; + + PAGED_CODE(); + + Status = WdfDeviceOpenRegistryKey( + Device, + PLUGPLAY_REGKEY_DEVICE, + KEY_WRITE, + NULL, + &KeyHandle + ); + + if (!NT_SUCCESS (Status)) { + goto SaveSimBattStateToRegistryEnd; + } + + Status = WdfRegistryAssignValue( + KeyHandle, + &SimbattStateRegNameStr, + REG_BINARY, + sizeof(SIMBATT_STATE), + State + ); + + WdfRegistryClose(KeyHandle); + if (!NT_SUCCESS (Status)) { + goto SaveSimBattStateToRegistryEnd; + } + +SaveSimBattStateToRegistryEnd: + return Status; +} + +_Use_decl_annotations_ +VOID +SimBattPrint ( + ULONG Level, + PCSTR Format, + ... + ) + +/*++ + +Routine Description: + + This routine emits the debugger message. + +Arguments: + + Level - Supplies the criticality of message being printed. + + Format - Message to be emitted in varible argument format. + +Return Value: + + None. + +--*/ + +{ + + va_list Arglist; + va_start(Arglist, Format); + vDbgPrintEx(DPFLTR_IHVDRIVER_ID, Level, Format, Arglist); +} diff --git a/simbatt/func/simbatt-wdfcoinstall.inx b/simbatt/func/simbatt-wdfcoinstall.inx new file mode 100644 index 000000000..21696763d --- /dev/null +++ b/simbatt/func/simbatt-wdfcoinstall.inx @@ -0,0 +1,108 @@ +;/*++ +; +;Copyright (c) Microsoft Corporation All rights Reserved +; +;Module Name: +; +; simbatt.inf +; +;Abstract: +; +; INF file for installing Simulated Battery WDF driver. +; +; N.B. Search the file for TODO comments and make necessary changes to make +; this INF file functional. +; +; N.B. Place the WdfCoInstaller files in the same directory as this file to +; make this INF file functional. +; +;--*/ + +[Version] +Signature="$WINDOWS NT$" +Class=Battery +ClassGuid={72631e54-78a4-11d0-bcf7-00aa00b7b32a} +Provider=%ProviderName% +DriverVer=04/30/2008, 6.01.6598 +CatalogFile=simbatt-wdfcoinstall.cat ; TODO: Add a signed catalog file + +[DestinationDirs] +DefaultDestDir = 12 +SimBatt_Device_CoInstaller_CopyFiles = 11 + +[SourceDisksNames] +1 = %DiskId1%,,,"" + +[SourceDisksFiles] +simbatt.sys = 1 +WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll = 1 + +;***************************************** +; Simulated Battery Install Section +;***************************************** + +[Manufacturer] +%StdMfg%=Standard,NT$ARCH$ + +[Standard.NT$ARCH$] +%SimBatt.DeviceDesc% = SimBatt_Device,{6B34C467-CE1F-4c0d-A3E4-F98A5718A9D6}\SimBatt + +[SimBatt_Device.NT] +CopyFiles=SimBatt_Device_Drivers +Include=battery.inf +Needs=Battery_Inst + +[SimBatt_Device.NT.HW] +AddReg=SimBatt_Device.NT.AddReg + +[SimBatt_Device.NT.AddReg] +HKR,,Security,,"D:P(A;;GA;;;AU)(A;;GA;;;S-1-15-2-1)" ; Allow generic-all users and appcontainers + +[SimBatt_Device_Drivers] +simbatt.sys + +;-------------- Service installation + +[SimBatt_Device.NT.Services] +AddService = simbatt,%SPSVCINST_ASSOCSERVICE%,SimBatt_Service_Inst + +; -------------- simbatt driver install sections + +[SimBatt_Service_Inst] +DisplayName = %SimBatt.SVCDESC% +ServiceType = 1 ; SERVICE_KERNEL_DRIVER +StartType = 3 ; SERVICE_DEMAND_START +ErrorControl = 1 ; SERVICE_ERROR_NORMAL +ServiceBinary = %12%\simbatt.sys + +;***************************************** +; WDF CoInstaller +;***************************************** + +[SimBatt_Device.NT.CoInstallers] +AddReg=SimBatt_Device_CoInstaller_AddReg +CopyFiles=SimBatt_Device_CoInstaller_CopyFiles + +[SimBatt_Device_CoInstaller_AddReg] +HKR,,CoInstallers32,0x00010000, "WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll,WdfCoInstaller" + +[SimBatt_Device_CoInstaller_CopyFiles] +WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll + +[SimBatt_Device.NT.Wdf] +KmdfService = SimBatt, SimBatt_wdfsect + +[SimBatt_wdfsect] +KmdfLibraryVersion = $KMDFVERSION$ + +;***************************************** +; Literals +;***************************************** + +[Strings] +SPSVCINST_ASSOCSERVICE= 0x00000002 +ProviderName = "TODO-Set-Provider" +StdMfg = "TODO-Set-Manufacturer" +DiskId1 = "Simulated Battery Installation Disk" +SimBatt.DeviceDesc = "Simulated Battery" +SimBatt.SVCDESC = "Simulated Battery" diff --git a/simbatt/func/simbatt.h b/simbatt/func/simbatt.h new file mode 100644 index 000000000..c74051cb5 --- /dev/null +++ b/simbatt/func/simbatt.h @@ -0,0 +1,136 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + simbatt.h + +Abstract: + + This is the header file for the simulated battery driver. + + N.B. This code is provided "AS IS" without any expressed or implied warranty. + +--*/ + +//---------------------------------------------------------------------- Pragmas + +#pragma once + +//--------------------------------------------------------------------- Includes + +#include +#include +#include +#include +#include +#include + +//------------------------------------------------------------- Debug Facilities + +#define SIMBATT_ERROR DPFLTR_ERROR_LEVEL // ed Kd_IHVDRIVER_Mask 0x1 +#define SIMBATT_WARN DPFLTR_WARNING_LEVEL // ed Kd_IHVDRIVER_Mask 0x2 +#define SIMBATT_TRACE DPFLTR_TRACE_LEVEL // ed Kd_IHVDRIVER_Mask 0x4 +#define SIMBATT_INFO DPFLTR_INFO_LEVEL // ed Kd_IHVDRIVER_Mask 0x8 + +#if defined(DEBUGPRINT) + #define DebugPrint(_Level, _Msg, ...) \ + SimBattPrint(_Level, _Msg, __VA_ARGS__) + + #define DebugEnter() \ + DebugPrint(SIMBATT_TRACE, "Entering " __FUNCTION__ "\n") + + #define DebugExit() \ + DebugPrint(SIMBATT_TRACE, "Leaving " __FUNCTION__ "\n") + + #define DebugExitStatus(_status_) \ + DebugPrint(SIMBATT_TRACE, \ + "Leaving " __FUNCTION__ ": Status=0x%x\n", \ + _status_) + +#else + #define DebugPrint(l, m, ...) + #define DebugEnter() + #define DebugExit() + #define DebugExitStatus(_status_) +#endif + +//--------------------------------------------------------------------- Literals + +#define SIMBATT_TAG 'StaB' +#define SIMBATT_STATE_REG_NAME L"SimbattState" + +//------------------------------------------------------------------ Definitions + +typedef struct { + UNICODE_STRING RegistryPath; +} SIMBATT_GLOBAL_DATA, *PSIMBATT_GLOBAL_DATA; + +#define SIMBATT_STATE_VERSION_1 1 +#define SIMBATT_STATE_VERSION SIMBATT_STATE_VERSION_1 + +typedef struct { + USHORT Version; + BATTERY_MANUFACTURE_DATE ManufactureDate; + BATTERY_INFORMATION BatteryInfo; + BATTERY_STATUS BatteryStatus; + BATTERY_REPORTING_SCALE GranularityScale[4]; + ULONG GranularityCount; + ULONG EstimatedTime; + ULONG Temperature; + ULONG MaxCurrentDraw; + WCHAR DeviceName[MAX_BATTERY_STRING_SIZE]; + WCHAR ManufacturerName[MAX_BATTERY_STRING_SIZE]; + WCHAR SerialNumber[MAX_BATTERY_STRING_SIZE]; + WCHAR UniqueId[MAX_BATTERY_STRING_SIZE]; +} SIMBATT_STATE, *PSIMBATT_STATE; + +typedef struct { + + // + // Battery class registration + // + + PVOID ClassHandle; + WDFWAITLOCK ClassInitLock; + WMILIB_CONTEXT WmiLibContext; + + // + // Battery state + // + + WDFWAITLOCK StateLock; + ULONG BatteryTag; + SIMBATT_STATE State; +} SIMBATT_FDO_DATA, *PSIMBATT_FDO_DATA; + +//------------------------------------------------------ WDF Context Declaration + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(SIMBATT_GLOBAL_DATA, GetGlobalData); +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(SIMBATT_FDO_DATA, GetDeviceExtension); + +//----------------------------------------------------- Prototypes (miniclass.c) + +_IRQL_requires_same_ +VOID +SimBattPrepareHardware ( + _In_ WDFDEVICE Device + ); + +EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL SimBattIoDeviceControl; +BCLASS_QUERY_TAG_CALLBACK SimBattQueryTag; +BCLASS_QUERY_INFORMATION_CALLBACK SimBattQueryInformation; +BCLASS_SET_INFORMATION_CALLBACK SimBattSetInformation; +BCLASS_QUERY_STATUS_CALLBACK SimBattQueryStatus; +BCLASS_SET_STATUS_NOTIFY_CALLBACK SimBattSetStatusNotify; +BCLASS_DISABLE_STATUS_NOTIFY_CALLBACK SimBattDisableStatusNotify; + +_IRQL_requires_same_ +VOID +SimBattPrint ( + _In_ ULONG Level, + _In_ PCSTR Format, + ... + ); + diff --git a/simbatt/func/simbatt.inx b/simbatt/func/simbatt.inx new file mode 100644 index 000000000..fe6456d27 --- /dev/null +++ b/simbatt/func/simbatt.inx @@ -0,0 +1,80 @@ + ;/*++ +; +;Copyright (c) Microsoft Corporation All rights Reserved +; +;Module Name: +; +; simbatt.inf +; +;Abstract: +; +; INF file for installing Simulated Battery WDF driver. +; +;--*/ + +[Version] +Signature="$WINDOWS NT$" +Class=Battery +ClassGuid={72631e54-78a4-11d0-bcf7-00aa00b7b32a} +Provider=%ProviderName% +DriverVer=04/30/2008, 6.01.6598 +CatalogFile=simbatt.cat + +[DestinationDirs] +DefaultDestDir = 12 + +[SourceDisksNames] +1 = %DiskId1%,,,"" + +[SourceDisksFiles] +simbatt.sys = 1,, + +;***************************************** +; Simulated Battery Install Section +;***************************************** + +[Manufacturer] +%StdMfg%=Standard,NT$ARCH$ + +[Standard.NT$ARCH$] +%SimBatt.DeviceDesc% = SimBatt_Device,{6B34C467-CE1F-4c0d-A3E4-F98A5718A9D6}\SimBatt + +[SimBatt_Device.NT] +CopyFiles=SimBatt_Device_Drivers +Include=battery.inf +Needs=Battery_Inst + +[SimBatt_Device.NT.HW] +AddReg=SimBatt_Device.NT.AddReg + +[SimBatt_Device.NT.AddReg] +HKR,,Security,,"D:P(A;;GA;;;AU)(A;;GA;;;S-1-15-2-1)" ; Allow generic-all users and appcontainers + +[SimBatt_Device_Drivers] +simbatt.sys + +;-------------- Service installation + +[SimBatt_Device.NT.Services] +AddService = simbatt,%SPSVCINST_ASSOCSERVICE%,SimBatt_Service_Inst + +; -------------- simbatt driver install sections + +[SimBatt_Service_Inst] +DisplayName = %SimBatt.SVCDESC% +ServiceType = 1 ; SERVICE_KERNEL_DRIVER +StartType = 3 ; SERVICE_DEMAND_START +ErrorControl = 1 ; SERVICE_ERROR_NORMAL +ServiceBinary = %12%\simbatt.sys + +;***************************************** +; Literals +;***************************************** + +[Strings] +SPSVCINST_ASSOCSERVICE= 0x00000002 +ProviderName = "TODO-Set-Provider" +StdMfg = "TODO-Set-Manufacturer" +DiskId1 = "Simulated Battery Installation Disk" +SimBatt.DeviceDesc = "Simulated Battery" +SimBatt.SVCDESC = "Simulated Battery" diff --git a/simbatt/func/simbatt.rc b/simbatt/func/simbatt.rc new file mode 100644 index 000000000..76cca8ded --- /dev/null +++ b/simbatt/func/simbatt.rc @@ -0,0 +1,11 @@ +#include + +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "Simulated Battery Driver" +#define VER_INTERNALNAME_STR "simbatt.sys" +#define VER_ORIGINALFILENAME_STR "simbatt.sys" + +#include "common.ver" \ No newline at end of file diff --git a/simbatt/func/simbatt.vcxproj b/simbatt/func/simbatt.vcxproj new file mode 100644 index 000000000..5cb654ecb --- /dev/null +++ b/simbatt/func/simbatt.vcxproj @@ -0,0 +1,193 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {C71BB928-C9A7-49F2-9D72-86E14436DC51} + $(MSBuildProjectName) + 1 + Debug + Win32 + {AE6873E7-2D8B-4E61-B7A9-EED499F52417} + + + + 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) + + + + + + + + + + + + + + + + simbatt + + + simbatt + + + simbatt + + + simbatt + + + + true + Level4 + %(PreprocessorDefinitions);DRIVER;DEBUGPRINT + %(AdditionalIncludeDirectories);..\..\inc + + + + + %(PreprocessorDefinitions);DRIVER;DEBUGPRINT + %(AdditionalIncludeDirectories);..\..\inc + + + %(PreprocessorDefinitions);DRIVER;DEBUGPRINT + %(AdditionalIncludeDirectories);..\..\inc + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\battc.lib + + + + + true + Level4 + %(PreprocessorDefinitions);DRIVER;DEBUGPRINT + %(AdditionalIncludeDirectories);..\..\inc + + + + + %(PreprocessorDefinitions);DRIVER;DEBUGPRINT + %(AdditionalIncludeDirectories);..\..\inc + + + %(PreprocessorDefinitions);DRIVER;DEBUGPRINT + %(AdditionalIncludeDirectories);..\..\inc + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\battc.lib + + + + + true + Level4 + %(PreprocessorDefinitions);DRIVER;DEBUGPRINT + %(AdditionalIncludeDirectories);..\..\inc + + + + + %(PreprocessorDefinitions);DRIVER;DEBUGPRINT + %(AdditionalIncludeDirectories);..\..\inc + + + %(PreprocessorDefinitions);DRIVER;DEBUGPRINT + %(AdditionalIncludeDirectories);..\..\inc + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\battc.lib + + + + + true + Level4 + %(PreprocessorDefinitions);DRIVER;DEBUGPRINT + %(AdditionalIncludeDirectories);..\..\inc + + + + + %(PreprocessorDefinitions);DRIVER;DEBUGPRINT + %(AdditionalIncludeDirectories);..\..\inc + + + %(PreprocessorDefinitions);DRIVER;DEBUGPRINT + %(AdditionalIncludeDirectories);..\..\inc + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\battc.lib + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/simbatt/func/simbatt.vcxproj.Filters b/simbatt/func/simbatt.vcxproj.Filters new file mode 100644 index 000000000..937270671 --- /dev/null +++ b/simbatt/func/simbatt.vcxproj.Filters @@ -0,0 +1,34 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {E2E73BD2-D4AB-4FDC-A201-45D9C7DA4866} + + + h;hpp;hxx;hm;inl;inc;xsd + {00C17D13-57C5-40E5-A333-10F751A23D7D} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {5B9DC3A0-2CF3-497D-BC22-5BDD1D46B87E} + + + inf;inv;inx;mof;mc; + {5FB80A73-BC97-4815-868E-D4847DE0C7F5} + + + + + Source Files + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/simbatt/func/simbattdriverif.h b/simbatt/func/simbattdriverif.h new file mode 100644 index 000000000..6fb45d8bb --- /dev/null +++ b/simbatt/func/simbattdriverif.h @@ -0,0 +1,128 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + simbattdriverif.h + +Abstract: + + This module contains the interfaces used to communicate between usermode + and the simulated battery driver stack. + + N.B. This code is provided "AS IS" without any expressed or implied warranty. + +--*/ + +//---------------------------------------------------------------------- Pragmas + +#pragma once + +//--------------------------------------------------------------------- Includes + +#include + +//------------------------------------------------------------------ Definitions + +// +// Battery bus driver interface +// + +// {780AC894-01FF-4b5e-B4C8-9C00709200EB} +DEFINE_GUID(BATTBUS_DEVINTERFACE_GUID, + 0x780ac894, 0x1ff, 0x4b5e, 0xb4, 0xc8, 0x9c, 0x0, 0x70, 0x92, 0x0, 0xeb); + +#define BATTBUS_IOCTL(_index_) \ + CTL_CODE(FILE_DEVICE_BUS_EXTENDER, _index_, METHOD_BUFFERED, FILE_READ_DATA) + +#define IOCTL_BATTBUS_PLUGIN_HARDWARE BATTBUS_IOCTL(0x0) +#define IOCTL_BATTBUS_UNPLUG_HARDWARE BATTBUS_IOCTL(0x1) + +// +// Simulated battery ioctl interface +// + +// {DAD1F940-CDD0-461f-B23F-C2C663D6E9EB} +DEFINE_GUID(SIMBATT_DEVINTERFACE_GUID, + 0xdad1f940, 0xcdd0, 0x461f, 0xb2, 0x3f, 0xc2, 0xc6, 0x63, 0xd6, 0xe9, 0xeb); + +#define SIMBATT_IOCTL(_index_) \ + CTL_CODE(FILE_DEVICE_BATTERY, _index_, METHOD_BUFFERED, FILE_WRITE_DATA) + +#define IOCTL_SIMBATT_SET_STATUS SIMBATT_IOCTL(0x800) +#define IOCTL_SIMBATT_SET_DEVICE_NAME SIMBATT_IOCTL(0x801) +#define IOCTL_SIMBATT_SET_ESTIMATED_TIME SIMBATT_IOCTL(0x802) +#define IOCTL_SIMBATT_SET_GRANULARITY_INFORMATION SIMBATT_IOCTL(0x803) +#define IOCTL_SIMBATT_SET_INFORMATION SIMBATT_IOCTL(0x804) +#define IOCTL_SIMBATT_SET_MANUFACTURE_DATE SIMBATT_IOCTL(0x805) +#define IOCTL_SIMBATT_SET_MANUFACTURE_NAME SIMBATT_IOCTL(0x806) +#define IOCTL_SIMBATT_SET_SERIAL_NUMBER SIMBATT_IOCTL(0x807) +#define IOCTL_SIMBATT_SET_TEMPERATURE SIMBATT_IOCTL(0x808) +#define IOCTL_SIMBATT_SET_UNIQUE_ID SIMBATT_IOCTL(0x809) +#define IOCTL_SIMBATT_GET_MAXCHARGINGCURRENT SIMBATT_IOCTL(0x810) +#define SIMBATT_RATE_CALCULATE 0x7fffffff +#define MAX_SUPPORTED_SIMBATT_CHILDREN 20 + +//------------------------------------------------------------------- Data Types + +// +// Data structure used in PlugIn and UnPlug ioctls +// + +#define BATTBUS_TYPE_SIMBATT 0; + +typedef struct _BATTBUS_PLUGIN_HARDWARE +{ + // + // Size of this type. + // + + ULONG Size; + + // + // Unique serial number of the device to be enumerated. + // Enumeration will be failed if another device on the + // bus has the same serial number. + // + + ULONG SerialNo; + + // + // UI number. + // + + ULONG UINumber; + + // + // Type of device being enumerated + // + // Reserved value, set to 0. + // + + ULONG Type; + +} BATTBUS_PLUGIN_HARDWARE, *PBATTBUS_PLUGIN_HARDWARE; + +typedef struct _BATTBUS_UNPLUG_HARDWARE +{ + // + // size of this type + // + + ULONG Size; + + // + // Serial number of the device to be unplugged. + // + + ULONG SerialNo; + + // + // Must not be referenced used. + // + + ULONG Reserved[2]; + +} BATTBUS_UNPLUG_HARDWARE, *PBATTBUS_UNPLUG_HARDWARE; + diff --git a/simbatt/func/wdf.c b/simbatt/func/wdf.c new file mode 100644 index 000000000..cb0adbb17 --- /dev/null +++ b/simbatt/func/wdf.c @@ -0,0 +1,935 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + wdf.c + +Abstract: + This module implements WDF and WDM functionality required to register as a + device driver, instantiate devices, and register those devices with the + battery class driver. + + N.B. This code is provided "AS IS" without any expressed or implied warranty. + +--*/ + +//--------------------------------------------------------------------- Includes + +#include "simbatt.h" +#include "simbattdriverif.h" +#include "batclass_prepublish.h" + +//------------------------------------------------------------------- Prototypes + +DRIVER_INITIALIZE DriverEntry; +EVT_WDF_DRIVER_DEVICE_ADD SimBattDriverDeviceAdd; +EVT_WDF_DEVICE_SELF_MANAGED_IO_INIT SimBattSelfManagedIoInit; +EVT_WDF_DEVICE_SELF_MANAGED_IO_CLEANUP SimBattSelfManagedIoCleanup; +EVT_WDF_DEVICE_QUERY_STOP SimBattQueryStop; +EVT_WDF_DEVICE_PREPARE_HARDWARE SimBattDevicePrepareHardware; +EVT_WDFDEVICE_WDM_IRP_PREPROCESS SimBattWdmIrpPreprocessDeviceControl; +EVT_WDFDEVICE_WDM_IRP_PREPROCESS SimBattWdmIrpPreprocessSystemControl; +WMI_QUERY_REGINFO_CALLBACK SimBattQueryWmiRegInfo; +WMI_QUERY_DATABLOCK_CALLBACK SimBattQueryWmiDataBlock; + +//---------------------------------------------------------------------- Pragmas + +#pragma alloc_text(INIT, DriverEntry) +#pragma alloc_text(PAGE, SimBattSelfManagedIoInit) +#pragma alloc_text(PAGE, SimBattSelfManagedIoCleanup) +#pragma alloc_text(PAGE, SimBattQueryStop) +#pragma alloc_text(PAGE, SimBattDriverDeviceAdd) +#pragma alloc_text(PAGE, SimBattDevicePrepareHardware) +#pragma alloc_text(PAGE, SimBattWdmIrpPreprocessDeviceControl) +#pragma alloc_text(PAGE, SimBattWdmIrpPreprocessSystemControl) +#pragma alloc_text(PAGE, SimBattQueryWmiRegInfo) +#pragma alloc_text(PAGE, SimBattQueryWmiDataBlock) + +//-------------------------------------------------------------------- Functions + +_Use_decl_annotations_ +NTSTATUS +DriverEntry ( + PDRIVER_OBJECT DriverObject, + 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 - Supplies a pointer to the driver object. + + RegistryPath - Supplies a pointer to a unicode string representing the path + to the driver-specific key in the registry. + +Return Value: + + NTSTATUS. + +--*/ + +{ + + WDF_OBJECT_ATTRIBUTES DriverAttributes; + WDF_DRIVER_CONFIG DriverConfig; + PSIMBATT_GLOBAL_DATA GlobalData; + NTSTATUS Status; + + DebugEnter(); + + WDF_DRIVER_CONFIG_INIT(&DriverConfig, SimBattDriverDeviceAdd); + + // + // Initialize attributes and a context area for the driver object. + // + // N.B. ExecutionLevel is set to Passive because this driver expect callback + // functions to be called at PASSIVE_LEVEL. + // + // N.B. SynchronizationScope is not specified and therefore it is set to + // None. This means that the WDF framework does not synchronize the + // callbacks, you may want to set this to a different value based on + // how the callbacks are required to be synchronized in your driver. + // + + WDF_OBJECT_ATTRIBUTES_INIT(&DriverAttributes); + WDF_OBJECT_ATTRIBUTES_SET_CONTEXT_TYPE(&DriverAttributes, + SIMBATT_GLOBAL_DATA); + + DriverAttributes.ExecutionLevel = WdfExecutionLevelPassive; + + // + // Create the driver object + // + + Status = WdfDriverCreate(DriverObject, + RegistryPath, + &DriverAttributes, + &DriverConfig, + WDF_NO_HANDLE); + + if (!NT_SUCCESS(Status)) { + DebugPrint(SIMBATT_ERROR, + "WdfDriverCreate() Failed. Status 0x%x\n", + Status); + + goto DriverEntryEnd; + } + + GlobalData = GetGlobalData(WdfGetDriver()); + GlobalData->RegistryPath.MaximumLength = RegistryPath->Length + + sizeof(UNICODE_NULL); + + GlobalData->RegistryPath.Length = RegistryPath->Length; + GlobalData->RegistryPath.Buffer = WdfDriverGetRegistryPath(WdfGetDriver()); + +DriverEntryEnd: + DebugExitStatus(Status); + return Status; +} + +_Use_decl_annotations_ +NTSTATUS +SimBattDriverDeviceAdd ( + WDFDRIVER Driver, + PWDFDEVICE_INIT DeviceInit + ) + +/*++ +Routine Description: + + EvtDriverDeviceAdd is called by the framework in response to AddDevice + call from the PnP manager. A WDF device object is created and initialized to + represent a new instance of the battery device. + +Arguments: + + Driver - Supplies a handle to the WDF Driver object. + + DeviceInit - Supplies a pointer to a framework-allocated WDFDEVICE_INIT + structure. + +Return Value: + + NTSTATUS + +--*/ + +{ + + WDF_OBJECT_ATTRIBUTES DeviceAttributes; + PSIMBATT_FDO_DATA DevExt; + WDFDEVICE DeviceHandle; + WDF_FILEOBJECT_CONFIG FileObjectConfig; + WDF_OBJECT_ATTRIBUTES LockAttributes; + WDF_PNPPOWER_EVENT_CALLBACKS PnpPowerCallbacks; + WDFQUEUE Queue; + WDF_IO_QUEUE_CONFIG QueueConfig; + NTSTATUS Status; + + UNREFERENCED_PARAMETER(Driver); + + DebugEnter(); + PAGED_CODE(); + + // + // Initialize the PnpPowerCallbacks structure. Callback events for PNP + // and Power are specified here. + // + + WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&PnpPowerCallbacks); + PnpPowerCallbacks.EvtDevicePrepareHardware = SimBattDevicePrepareHardware; + PnpPowerCallbacks.EvtDeviceSelfManagedIoInit = SimBattSelfManagedIoInit; + PnpPowerCallbacks.EvtDeviceSelfManagedIoCleanup = SimBattSelfManagedIoCleanup; + PnpPowerCallbacks.EvtDeviceQueryStop = SimBattQueryStop; + WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &PnpPowerCallbacks); + + // + // Configure file handlers to forward all create, close, and cleanup + // requests to the PDO. + // + + WDF_FILEOBJECT_CONFIG_INIT(&FileObjectConfig, + WDF_NO_EVENT_CALLBACK, + WDF_NO_EVENT_CALLBACK, + WDF_NO_EVENT_CALLBACK); + + FileObjectConfig.AutoForwardCleanupClose = WdfTrue; + WdfDeviceInitSetFileObjectConfig(DeviceInit, + &FileObjectConfig, + WDF_NO_OBJECT_ATTRIBUTES); + + // + // Register WDM preprocess callbacks for IRP_MJ_DEVICE_CONTROL and + // IRP_MJ_SYSTEM_CONTROL. The battery class driver needs to handle these IO + // requests directly. + // + + Status = WdfDeviceInitAssignWdmIrpPreprocessCallback( + DeviceInit, + SimBattWdmIrpPreprocessDeviceControl, + IRP_MJ_DEVICE_CONTROL, + NULL, + 0); + + if (!NT_SUCCESS(Status)) { + DebugPrint(SIMBATT_ERROR, + "WdfDeviceInitAssignWdmIrpPreprocessCallback" + "(IRP_MJ_DEVICE_CONTROL) Failed. 0x%x\n", + Status); + + goto DriverDeviceAddEnd; + } + + Status = WdfDeviceInitAssignWdmIrpPreprocessCallback( + DeviceInit, + SimBattWdmIrpPreprocessSystemControl, + IRP_MJ_SYSTEM_CONTROL, + NULL, + 0); + + if (!NT_SUCCESS(Status)) { + DebugPrint(SIMBATT_ERROR, + "WdfDeviceInitAssignWdmIrpPreprocessCallback" + "(IRP_MJ_SYSTEM_CONTROL) Failed. 0x%x\n", + Status); + + goto DriverDeviceAddEnd; + } + + // + // Initialize attributes and a context area for the device object. + // + + WDF_OBJECT_ATTRIBUTES_INIT(&DeviceAttributes); + WDF_OBJECT_ATTRIBUTES_SET_CONTEXT_TYPE(&DeviceAttributes, SIMBATT_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, &DeviceAttributes, &DeviceHandle); + if (!NT_SUCCESS(Status)) { + DebugPrint(SIMBATT_ERROR, "WdfDeviceCreate() Failed. 0x%x\n", Status); + goto DriverDeviceAddEnd; + } + + // + // Configure a default queue for IO requests that are not handled by the + // class driver. For the simulated battery, this queue processes requests + // to set the simulated status. + // + + WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&QueueConfig, + WdfIoQueueDispatchSequential); + + QueueConfig.EvtIoDeviceControl = SimBattIoDeviceControl; + Status = WdfIoQueueCreate(DeviceHandle, + &QueueConfig, + WDF_NO_OBJECT_ATTRIBUTES, + &Queue); + + if (!NT_SUCCESS(Status)) { + DebugPrint(SIMBATT_ERROR, "WdfIoQueueCreate() Failed. 0x%x\n", Status); + goto DriverDeviceAddEnd; + } + + // + // Create a device interface for this device to advertise the simulated + // battery IO interface. + // + + Status = WdfDeviceCreateDeviceInterface(DeviceHandle, + &SIMBATT_DEVINTERFACE_GUID, + NULL); + + if (!NT_SUCCESS(Status)) { + goto DriverDeviceAddEnd; + } + + // + // Finish initializing the device context area. + // + + DevExt = GetDeviceExtension(DeviceHandle); + DevExt->BatteryTag = BATTERY_TAG_INVALID; + DevExt->ClassHandle = NULL; + WDF_OBJECT_ATTRIBUTES_INIT(&LockAttributes); + LockAttributes.ParentObject = DeviceHandle; + Status = WdfWaitLockCreate(&LockAttributes, + &DevExt->ClassInitLock); + + if (!NT_SUCCESS(Status)) { + DebugPrint(SIMBATT_ERROR, + "WdfWaitLockCreate(ClassInitLock) Failed. Status 0x%x\n", + Status); + + goto DriverDeviceAddEnd; + } + + WDF_OBJECT_ATTRIBUTES_INIT(&LockAttributes); + LockAttributes.ParentObject = DeviceHandle; + Status = WdfWaitLockCreate(&LockAttributes, + &DevExt->StateLock); + + if (!NT_SUCCESS(Status)) { + DebugPrint(SIMBATT_ERROR, + "WdfWaitLockCreate(StateLock) Failed. Status 0x%x\n", + Status); + + goto DriverDeviceAddEnd; + } + +DriverDeviceAddEnd: + DebugExitStatus(Status); + return Status; +} + +_Use_decl_annotations_ +NTSTATUS +SimBattSelfManagedIoInit ( + WDFDEVICE Device + ) + +/*++ + +Routine Description: + + The framework calls this function once per device after EvtDeviceD0Entry + callback has been called for the first time. This function is not called + again unless device is remove and reconnected or the drivers are reloaded. + +Arguments: + + Device - Supplies a handle to a framework device object. + +Return Value: + + NTSTATUS + +--*/ + +{ + + BATTERY_MINIPORT_INFO_V1_1 BattInit; + PSIMBATT_FDO_DATA DevExt; + PDEVICE_OBJECT DeviceObject; + NTSTATUS Status; + + DebugEnter(); + PAGED_CODE(); + + DevExt = GetDeviceExtension(Device); + + // + // Attach to the battery class driver. + // + + RtlZeroMemory(&BattInit, sizeof(BattInit)); + BattInit.MajorVersion = BATTERY_CLASS_MAJOR_VERSION; + BattInit.MinorVersion = BATTERY_CLASS_MINOR_VERSION_1; + BattInit.Context = DevExt; + BattInit.QueryTag = SimBattQueryTag; + BattInit.QueryInformation = SimBattQueryInformation; + BattInit.SetInformation = SimBattSetInformation; + BattInit.QueryStatus = SimBattQueryStatus; + BattInit.SetStatusNotify = SimBattSetStatusNotify; + BattInit.DisableStatusNotify = SimBattDisableStatusNotify; + BattInit.Pdo = WdfDeviceWdmGetPhysicalDevice(Device); + BattInit.DeviceName = NULL; + BattInit.Fdo = WdfDeviceWdmGetDeviceObject(Device); + WdfWaitLockAcquire(DevExt->ClassInitLock, NULL); + Status = BatteryClassInitializeDevice((PBATTERY_MINIPORT_INFO)&BattInit, + &DevExt->ClassHandle); + + WdfWaitLockRelease(DevExt->ClassInitLock); + if (!NT_SUCCESS(Status)) { + goto DevicePrepareHardwareEnd; + } + + // + // Register the device as a WMI data provider. This is done using WDM + // methods because the battery class driver uses WDM methods to complete + // WMI requests. + // + + DevExt->WmiLibContext.GuidCount = 0; + DevExt->WmiLibContext.GuidList = NULL; + DevExt->WmiLibContext.QueryWmiRegInfo = SimBattQueryWmiRegInfo; + DevExt->WmiLibContext.QueryWmiDataBlock = SimBattQueryWmiDataBlock; + DevExt->WmiLibContext.SetWmiDataBlock = NULL; + DevExt->WmiLibContext.SetWmiDataItem = NULL; + DevExt->WmiLibContext.ExecuteWmiMethod = NULL; + DevExt->WmiLibContext.WmiFunctionControl = NULL; + DeviceObject = WdfDeviceWdmGetDeviceObject(Device); + Status = IoWMIRegistrationControl(DeviceObject, WMIREG_ACTION_REGISTER); + + // + // Failure to register with WMI is nonfatal. + // + + if (!NT_SUCCESS(Status)) { + DebugPrint(SIMBATT_WARN, + "IoWMIRegistrationControl() Failed. Status 0x%x\n", + Status); + + Status = STATUS_SUCCESS; + } + +DevicePrepareHardwareEnd: + DebugExitStatus(Status); + return Status; +} + +_Use_decl_annotations_ +VOID +SimBattSelfManagedIoCleanup ( + WDFDEVICE Device + ) + +/*++ + +Routine Description: + + This function is called after EvtDeviceSelfManagedIoSuspend callback. This + function must release any sel-managed I/O operation data. + +Arguments: + + Device - Supplies a handle to a framework device object. + +Return Value: + + NTSTATUS - Failures will be logged, but not acted on. + +--*/ + +{ + + PSIMBATT_FDO_DATA DevExt; + PDEVICE_OBJECT DeviceObject; + NTSTATUS Status; + + DebugEnter(); + PAGED_CODE(); + + DeviceObject = WdfDeviceWdmGetDeviceObject(Device); + Status = IoWMIRegistrationControl(DeviceObject, WMIREG_ACTION_DEREGISTER); + if (!NT_SUCCESS(Status)) { + DebugPrint(SIMBATT_WARN, + "IoWMIRegistrationControl() Failed. Status 0x%x\n", + Status); + + Status = STATUS_SUCCESS; + } + + DevExt = GetDeviceExtension(Device); + WdfWaitLockAcquire(DevExt->ClassInitLock, NULL); + if (DevExt->ClassHandle != NULL) { + Status = BatteryClassUnload(DevExt->ClassHandle); + DevExt->ClassHandle = NULL; + } + + WdfWaitLockRelease(DevExt->ClassInitLock); + DebugExitStatus(Status); + return; +} + +_Use_decl_annotations_ +NTSTATUS +SimBattQueryStop ( + _In_ WDFDEVICE Device + ) + +/*++ + +Routine Description: + + EvtDeviceQueryStop event callback function determines whether a specified + device can be stopped so that the PnP manager can redistribute system + hardware resources. + + SimBatt is designed to fail a rebalance operation, for reasons described + below. Note however that this approach must *not* be adopted by an actual + battery driver. + + SimBatt unregisters itself as a Battery driver by calling + BatteryClassUnload() when IRP_MN_STOP_DEVICE arrives at the driver. It + re-establishes itself as a Battery driver on arrival of IRP_MN_START_DEVICE. + This results in any IOCTLs normally handeled by the Battery Class driver to + be delivered to SimBatt. The IO Queue employed by SimBatt is power managed, + it causes these IOCTLs to be pended when SimBatt is not in D0. Now if the + device attempts to do a shutdown while an IOCTL is pended in SimBatt, it + would result in a 0x9F bugcheck. By opting out of PNP rebalance operation + SimBatt circumvents this issue. + +Arguments: + + Device - Supplies a handle to a framework device object. + +Return Value: + + NTSTATUS + +--*/ + +{ + + UNREFERENCED_PARAMETER(Device); + + return STATUS_UNSUCCESSFUL; +} + +_Use_decl_annotations_ +NTSTATUS +SimBattDevicePrepareHardware ( + 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. + +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. Use this list + of resources to map I/O space and device-accessible memory into virtual + address space + +Return Value: + + NTSTATUS + +--*/ + +{ + + NTSTATUS Status; + + UNREFERENCED_PARAMETER(ResourcesRaw); + UNREFERENCED_PARAMETER(ResourcesTranslated); + + DebugEnter(); + PAGED_CODE(); + + SimBattPrepareHardware(Device); + Status = STATUS_SUCCESS; + DebugExitStatus(Status); + return Status; +} + +_Use_decl_annotations_ +NTSTATUS +SimBattWdmIrpPreprocessDeviceControl ( + WDFDEVICE Device, + PIRP Irp + ) + +/*++ + +Routine Description: + + This event is called when the framework receives IRP_MJ_DEVICE_CONTROL + requests from the system. + + N.B. Battery stack requires the device IOCTLs be sent to it at + PASSIVE_LEVEL only, any IOCTL comming from user mode is therefore + fine, kernel components, such as filter drivers sitting on top of + the battery drivers should be careful to not voilate this + requirement. + +Arguments: + + Device - Supplies a handle to a framework device object. + + Irp - Supplies the IO request being processed. + +Return Value: + + NTSTATUS + +--*/ + +{ + + PSIMBATT_FDO_DATA DevExt; + NTSTATUS Status; + + PAGED_CODE(); + DebugEnter(); + + ASSERTMSG("Must be called at IRQL = PASSIVE_LEVEL", + (KeGetCurrentIrql() == PASSIVE_LEVEL)); + + DevExt = GetDeviceExtension(Device); + Status = STATUS_NOT_SUPPORTED; + + // + // Suppress 28118:Irq Exceeds Caller, see Routine Description for + // explaination. + // + + #pragma warning(suppress: 28118) + WdfWaitLockAcquire(DevExt->ClassInitLock, NULL); + + // + // N.B. An attempt to queue the IRP with the port driver should happen + // before WDF assumes ownership of this IRP, i.e. before + // WdfDeviceWdmDispatchPreprocessedIrp is called, this is so that the + // Battery port driver, which is a WDM driver, may complete the IRP if + // it does endup procesing it. + // + + if (DevExt->ClassHandle != NULL) { + + // + // Suppress 28118:Irq Exceeds Caller, see above N.B. + // + + #pragma warning(suppress: 28118) + Status = BatteryClassIoctl(DevExt->ClassHandle, Irp); + } + + WdfWaitLockRelease(DevExt->ClassInitLock); + if (Status == STATUS_NOT_SUPPORTED) { + IoSkipCurrentIrpStackLocation(Irp); + Status = WdfDeviceWdmDispatchPreprocessedIrp(Device, Irp); + } + + DebugExitStatus(Status); + return Status; +} + +_Use_decl_annotations_ +NTSTATUS +SimBattWdmIrpPreprocessSystemControl ( + WDFDEVICE Device, + PIRP Irp + ) + +/*++ + +Routine Description: + + This event is called when the framework receives IRP_MJ_SYSTEM_CONTROL + requests from the system. + + N.B. Battery stack requires the device IOCTLs be sent to it at + PASSIVE_LEVEL only, any IOCTL comming from user mode is therefore + fine, kernel components, such as filter drivers sitting on top of + the battery drivers should be careful to not voilate this + requirement. + +Arguments: + + Device - Supplies a handle to a framework device object. + + Irp - Supplies the IO request being processed. + +Return Value: + + NTSTATUS + +--*/ + +{ + + PSIMBATT_FDO_DATA DevExt; + PDEVICE_OBJECT DeviceObject; + SYSCTL_IRP_DISPOSITION Disposition; + NTSTATUS Status; + + DebugEnter(); + PAGED_CODE(); + + ASSERTMSG("Must be called at IRQL = PASSIVE_LEVEL",(KeGetCurrentIrql() == PASSIVE_LEVEL)); + + Status = STATUS_NOT_IMPLEMENTED; + DevExt = GetDeviceExtension(Device); + Disposition = IrpForward; + + // + // Acquire the class initialization lock and attempt to queue the IRP with + // the class driver. + // + // Suppress 28118:Irq Exceeds Caller, see Routine Description for + // explaination. + // + + #pragma warning(suppress: 28118) + WdfWaitLockAcquire(DevExt->ClassInitLock, NULL); + if (DevExt->ClassHandle != NULL) { + DeviceObject = WdfDeviceWdmGetDeviceObject(Device); + Status = BatteryClassSystemControl(DevExt->ClassHandle, + &DevExt->WmiLibContext, + DeviceObject, + Irp, + &Disposition); + } + + WdfWaitLockRelease(DevExt->ClassInitLock); + switch (Disposition) { + case IrpProcessed: + break; + + case IrpNotCompleted: + IoCompleteRequest(Irp, IO_NO_INCREMENT); + break; + + case IrpForward: + case IrpNotWmi: + default: + IoSkipCurrentIrpStackLocation(Irp); + Status = WdfDeviceWdmDispatchPreprocessedIrp(Device, Irp); + break; + } + + DebugExitStatus(Status); + return Status; +} + +_Use_decl_annotations_ +NTSTATUS +SimBattQueryWmiRegInfo ( + PDEVICE_OBJECT DeviceObject, + PULONG RegFlags, + PUNICODE_STRING InstanceName, + PUNICODE_STRING *RegistryPath, + PUNICODE_STRING MofResourceName, + PDEVICE_OBJECT *Pdo + ) + +/*++ + +Routine Description: + + This routine is a callback into the driver to retrieve the list of + guids or data blocks that the driver wants to register with WMI. This + routine may not pend or block. Driver should NOT call + WmiCompleteRequest. + +Arguments: + + DeviceObject - Supplies the device whose data block is being queried. + + RegFlags - Supplies a pointer to return a set of flags that describe the + guids being registered for this device. If the device wants enable and + disable collection callbacks before receiving queries for the registered + guids then it should return the WMIREG_FLAG_EXPENSIVE flag. Also the + returned flags may specify WMIREG_FLAG_INSTANCE_PDO in which case + the instance name is determined from the PDO associated with the + device object. Note that the PDO must have an associated devnode. If + WMIREG_FLAG_INSTANCE_PDO is not set then Name must return a unique + name for the device. + + InstanceName - Supplies a pointer to return the instance name for the guids + if WMIREG_FLAG_INSTANCE_PDO is not set in the returned *RegFlags. The + caller will call ExFreePool with the buffer returned. + + RegistryPath - Supplies a pointer to return the registry path of the driver. + + MofResourceName - Supplies a pointer to return the name of the MOF resource + attached to the binary file. If the driver does not have a mof resource + attached then this can be returned as NULL. + + Pdo - Supplies a pointer to return the device object for the PDO associated + with this device if the WMIREG_FLAG_INSTANCE_PDO flag is returned in + *RegFlags. + +Return Value: + + NTSTATUS + +--*/ + +{ + + WDFDEVICE Device; + PSIMBATT_GLOBAL_DATA GlobalData; + NTSTATUS Status; + + UNREFERENCED_PARAMETER(MofResourceName); + UNREFERENCED_PARAMETER(InstanceName); + + DebugEnter(); + PAGED_CODE(); + + Device = WdfWdmDeviceGetWdfDeviceHandle(DeviceObject); + GlobalData = GetGlobalData(WdfGetDriver()); + *RegFlags = WMIREG_FLAG_INSTANCE_PDO; + *RegistryPath = &GlobalData->RegistryPath; + *Pdo = WdfDeviceWdmGetPhysicalDevice(Device); + Status = STATUS_SUCCESS; + DebugExitStatus(Status); + return Status; +} + +_Use_decl_annotations_ +NTSTATUS +SimBattQueryWmiDataBlock ( + PDEVICE_OBJECT DeviceObject, + PIRP Irp, + ULONG GuidIndex, + ULONG InstanceIndex, + ULONG InstanceCount, + PULONG InstanceLengthArray, + ULONG BufferAvail, + PUCHAR Buffer + ) + +/*++ + +Routine Description: + + This routine is a callback into the driver to query for the contents of + a data block. When the driver has finished filling the data block it + must call WmiCompleteRequest to complete the irp. The driver can + return STATUS_PENDING if the irp cannot be completed immediately. + +Arguments: + + DeviceObject - Supplies the device whose data block is being queried. + + Irp - Supplies the Irp that makes this request. + + GuidIndex - Supplies the index into the list of guids provided when the + device registered. + + InstanceIndex - Supplies the index that denotes which instance of the data + block is being queried. + + InstanceCount - Supplies the number of instances expected to be returned for + the data block. + + InstanceLengthArray - Supplies a pointer to an array of ULONG that returns + the lengths of each instance of the data block. If this is NULL then + there was not enough space in the output buffer to fulfill the request + so the irp should be completed with the buffer needed. + + BufferAvail - Supplies the maximum size available to write the data + block. + + Buffer - Supplies a pointer to a buffer to return the data block. + + +Return Value: + + NTSTATUS + +--*/ + +{ + + PSIMBATT_FDO_DATA DevExt; + WDFDEVICE Device; + NTSTATUS Status; + + UNREFERENCED_PARAMETER(InstanceIndex); + UNREFERENCED_PARAMETER(InstanceCount); + + DebugEnter(); + PAGED_CODE(); + + ASSERT((InstanceIndex == 0) && (InstanceCount == 1)); + + if (InstanceLengthArray == NULL) { + Status = STATUS_BUFFER_TOO_SMALL; + goto SimBattQueryWmiDataBlockEnd; + } + + Device = WdfWdmDeviceGetWdfDeviceHandle(DeviceObject); + DevExt = GetDeviceExtension(Device); + + // + // The class driver guarantees that all outstanding IO requests will be + // completed before it finishes unregistering. As a result, the class + // initialization lock does not need to be acquired in this callback, since + // it is called during class driver processing of a WMI IRP. + // + + Status = BatteryClassQueryWmiDataBlock(DevExt->ClassHandle, + DeviceObject, + Irp, + GuidIndex, + InstanceLengthArray, + BufferAvail, + Buffer); + + if (Status == STATUS_WMI_GUID_NOT_FOUND) { + Status = WmiCompleteRequest(DeviceObject, + Irp, + STATUS_WMI_GUID_NOT_FOUND, + 0, + IO_NO_INCREMENT); + } + +SimBattQueryWmiDataBlockEnd: + DebugExitStatus(Status); + return Status; +} diff --git a/smartcrd/ReadMe.md b/smartcrd/ReadMe.md index d3d0f6001..b623b669f 100644 --- a/smartcrd/ReadMe.md +++ b/smartcrd/ReadMe.md @@ -1,6 +1,8 @@ PCMCIA Smart Card Driver ======================== +**Warning, this sample and its documentation are known to be out of date. They will be updated soon. Please be aware that some functionality may not work as expected.** + The PCMCIA Smart Card Driver is used for the SCM PCMCIA smart card reader. This driver is written using Kernel-Mode Driver Framework. This driver in its original form was written in WDM. It was converted to KMDF to take advantage of all the benefits provided by KMDF in terms of reducing complexity and making it robust. Since this driver still needs to work with the existing smartcard library to handle smartcard specific processing, the driver is not restricted to using only KMDF interfaces. Escaping out of KMDF is necessary for processing I/O requests to get the underlying IRPs and provide that to the smartcard library. The driver also uses advanced IRP handling techniques to work around the limitations imposed by the smartcard library. Except for this quirk, the driver is a fully functional KMDF driver. As a sample, it also makes it easier to adapt this driver for USB devices since KMDF has good support for interfacing with USB devices. @@ -10,16 +12,6 @@ Power Management is described in detail in the WDK documentation. There is, howe A card reader will not see any card insertion or removal events in these modes, because the bus might not even have power. The card state must be saved before the reader goes into standby or hibernation mode. After the system returns from these modes, it is necessary to determine what the state of the card is. Card tracking calls must complete whenever there was a card in the reader before standby or hibernation mode or whenever there is a card in the reader after these modes. This step is necessary because the user could have changed the card while the system was in a low-power mode. -Build the sample ----------------- - -For information on how to build a driver solution using Microsoft Visual Studio, see [Building a Driver](http://msdn.microsoft.com/en-us/library/windows/hardware/ff554644). - -**Note**  Starting in Windows 8, the WDK no longer contains the co-installers by default. You can obtain the co-installers by downloading the *wdfcoinstaller.msi* package from [WDK 8 Redistributable Components](http://go.microsoft.com/fwlink/p/?LinkID=226396). - -Run the sample --------------- - Installation ------------ diff --git a/smartcrd/pscr/Pscr.vcxproj b/smartcrd/pscr/Pscr.vcxproj index 88bc78911..a2a60ea8f 100644 --- a/smartcrd/pscr/Pscr.vcxproj +++ b/smartcrd/pscr/Pscr.vcxproj @@ -19,12 +19,12 @@ - {E963C756-D675-4202-A229-6B3ADB94B252} + {C90317D3-294B-41DD-9E58-EA5C4592B822} $(MSBuildProjectName) 1 Debug Win32 - {1C4F5FFD-0F09-4478-A3F7-DEF422837617} + {4497680E-B15A-4377-9690-CC9B9A94CBBC} @@ -169,7 +169,6 @@ - diff --git a/smartcrd/pscr/Pscr.vcxproj.Filters b/smartcrd/pscr/Pscr.vcxproj.Filters index 55f941f42..1bad5e499 100644 --- a/smartcrd/pscr/Pscr.vcxproj.Filters +++ b/smartcrd/pscr/Pscr.vcxproj.Filters @@ -3,25 +3,22 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {7EB6DC3A-11A4-48F2-9730-419C9A104242} + {54D65CE6-224F-4E82-BCA5-3348D0F43339} h;hpp;hxx;hm;inl;inc;xsd - {2AAA41D4-E19E-409F-87C1-EA4C640F0BAA} + {FB0DF8C2-7B63-4219-B2EF-23A2BA12B7EC} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {9FEF86B3-6289-44F6-B036-2C15E61CDDDE} + {9C2A7E2B-9B74-4579-A1D3-5C5B3D397F7E} inf;inv;inx;mof;mc; - {A832F7F9-C73F-4032-83DF-1CA14DE6C0FF} + {B796B525-44D3-4260-8C76-705DBADA1043} - - Driver Files - Driver Files diff --git a/smartcrd/pscr/pscr.inx b/smartcrd/pscr/pscr.inx index 28e938160657c8ffef1f4603c7c672d2a4914827..50cd311c414f35174172d319a60dae536ab545cf 100644 GIT binary patch delta 97 zcmbPYc*t0t}o$Xv?6$ cpu`Zu;LqR!#JUW@KrJOe8qJW+*5Z+j0Cb`i7XSbN delta 63 zcmX?PIK^^hLX+i8O=pip&SNIFtlY*U{GT4WyoYmW+-CF2P(`5E6o&- GWCQ>KI1X+A diff --git a/smartcrd/smartcrd.sln b/smartcrd/smartcrd.sln index c0f3b05e8..a8cd3b2f6 100644 --- a/smartcrd/smartcrd.sln +++ b/smartcrd/smartcrd.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0 MinimumVisualStudioVersion = 12.0 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Pscr", "pscr\Pscr.vcxproj", "{E963C756-D675-4202-A229-6B3ADB94B252}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Pscr", "pscr\Pscr.vcxproj", "{C90317D3-294B-41DD-9E58-EA5C4592B822}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {E963C756-D675-4202-A229-6B3ADB94B252}.Debug|Win32.ActiveCfg = Debug|Win32 - {E963C756-D675-4202-A229-6B3ADB94B252}.Debug|Win32.Build.0 = Debug|Win32 - {E963C756-D675-4202-A229-6B3ADB94B252}.Release|Win32.ActiveCfg = Release|Win32 - {E963C756-D675-4202-A229-6B3ADB94B252}.Release|Win32.Build.0 = Release|Win32 - {E963C756-D675-4202-A229-6B3ADB94B252}.Debug|x64.ActiveCfg = Debug|x64 - {E963C756-D675-4202-A229-6B3ADB94B252}.Debug|x64.Build.0 = Debug|x64 - {E963C756-D675-4202-A229-6B3ADB94B252}.Release|x64.ActiveCfg = Release|x64 - {E963C756-D675-4202-A229-6B3ADB94B252}.Release|x64.Build.0 = Release|x64 + {C90317D3-294B-41DD-9E58-EA5C4592B822}.Debug|Win32.ActiveCfg = Debug|Win32 + {C90317D3-294B-41DD-9E58-EA5C4592B822}.Debug|Win32.Build.0 = Debug|Win32 + {C90317D3-294B-41DD-9E58-EA5C4592B822}.Release|Win32.ActiveCfg = Release|Win32 + {C90317D3-294B-41DD-9E58-EA5C4592B822}.Release|Win32.Build.0 = Release|Win32 + {C90317D3-294B-41DD-9E58-EA5C4592B822}.Debug|x64.ActiveCfg = Debug|x64 + {C90317D3-294B-41DD-9E58-EA5C4592B822}.Debug|x64.Build.0 = Debug|x64 + {C90317D3-294B-41DD-9E58-EA5C4592B822}.Release|x64.ActiveCfg = Release|x64 + {C90317D3-294B-41DD-9E58-EA5C4592B822}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/spb/SkeletonI2C/SkeletonI2C.sln b/spb/SkeletonI2C/SkeletonI2C.sln index 17f4527d1..bdd9bf879 100644 --- a/spb/SkeletonI2C/SkeletonI2C.sln +++ b/spb/SkeletonI2C/SkeletonI2C.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0 MinimumVisualStudioVersion = 12.0 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "skeletoni2c", "skeletoni2c.vcxproj", "{77250DF0-F449-4C59-9081-B87E6D14E311}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "skeletoni2c", "skeletoni2c.vcxproj", "{82A2B7D2-6711-4545-8561-EEFBA6A4DD8A}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {77250DF0-F449-4C59-9081-B87E6D14E311}.Debug|Win32.ActiveCfg = Debug|Win32 - {77250DF0-F449-4C59-9081-B87E6D14E311}.Debug|Win32.Build.0 = Debug|Win32 - {77250DF0-F449-4C59-9081-B87E6D14E311}.Release|Win32.ActiveCfg = Release|Win32 - {77250DF0-F449-4C59-9081-B87E6D14E311}.Release|Win32.Build.0 = Release|Win32 - {77250DF0-F449-4C59-9081-B87E6D14E311}.Debug|x64.ActiveCfg = Debug|x64 - {77250DF0-F449-4C59-9081-B87E6D14E311}.Debug|x64.Build.0 = Debug|x64 - {77250DF0-F449-4C59-9081-B87E6D14E311}.Release|x64.ActiveCfg = Release|x64 - {77250DF0-F449-4C59-9081-B87E6D14E311}.Release|x64.Build.0 = Release|x64 + {82A2B7D2-6711-4545-8561-EEFBA6A4DD8A}.Debug|Win32.ActiveCfg = Debug|Win32 + {82A2B7D2-6711-4545-8561-EEFBA6A4DD8A}.Debug|Win32.Build.0 = Debug|Win32 + {82A2B7D2-6711-4545-8561-EEFBA6A4DD8A}.Release|Win32.ActiveCfg = Release|Win32 + {82A2B7D2-6711-4545-8561-EEFBA6A4DD8A}.Release|Win32.Build.0 = Release|Win32 + {82A2B7D2-6711-4545-8561-EEFBA6A4DD8A}.Debug|x64.ActiveCfg = Debug|x64 + {82A2B7D2-6711-4545-8561-EEFBA6A4DD8A}.Debug|x64.Build.0 = Debug|x64 + {82A2B7D2-6711-4545-8561-EEFBA6A4DD8A}.Release|x64.ActiveCfg = Release|x64 + {82A2B7D2-6711-4545-8561-EEFBA6A4DD8A}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/spb/SkeletonI2C/skeletoni2c.inx b/spb/SkeletonI2C/skeletoni2c.inx index 6808f6ab64569a31e0a8444993b2081961b13973..1a911e9df46b20c3ab7f60c233b114a4c177ceb6 100644 GIT binary patch delta 115 zcmbOrw@7Y70<&}gLlHwhLm5LRLkdGGkR8lW0wgmT@)*)L7c+0>bj729L4m;*h?N*Z Z82lMrfLIr(Jr!txE(4mb&56A083E@l7{UMm delta 97 zcmZ1^H$iSg0<*9$LokCILkN&`WbkDOVDMpZ-CWJQnbR7J1cL&DEf6aK6=pIdGZZo8 dGZX`H8bb+CG?*cgA(x?mA%`J#b20CFMgYBq6K? - {77250DF0-F449-4C59-9081-B87E6D14E311} + {82A2B7D2-6711-4545-8561-EEFBA6A4DD8A} $(MSBuildProjectName) 1 Debug Win32 - {B0247A4A-90C2-48D8-AC68-F6265E298B6B} + {57895255-1229-4122-B78F-CF741B96891B} Windows10 False - Desktop + Universal KMDF WindowsKernelModeDriver10.0 Driver @@ -38,7 +38,7 @@ Windows10 True - Desktop + Universal KMDF WindowsKernelModeDriver10.0 Driver @@ -46,7 +46,7 @@ Windows10 False - Desktop + Universal KMDF WindowsKernelModeDriver10.0 Driver @@ -54,7 +54,7 @@ Windows10 True - Desktop + Universal KMDF WindowsKernelModeDriver10.0 Driver @@ -184,7 +184,6 @@ - diff --git a/spb/SkeletonI2C/skeletoni2c.vcxproj.Filters b/spb/SkeletonI2C/skeletoni2c.vcxproj.Filters index 5833a2769..851192f0d 100644 --- a/spb/SkeletonI2C/skeletoni2c.vcxproj.Filters +++ b/spb/SkeletonI2C/skeletoni2c.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {8A872C5C-6393-442C-B35C-2ED1A0D0B371} + {FB780344-5D6B-417C-AF29-2AEF28ACDABE} h;hpp;hxx;hm;inl;inc;xsd - {248FB7FE-BE5E-4C18-8D1E-F9E61E52E9F9} + {E951BA09-BC5C-4109-A597-C43EF84D0C73} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {03825A97-CBC2-4083-ABEE-A7048E343989} + {DA64D74C-5224-42DB-B9FC-91148D4DC554} inf;inv;inx;mof;mc; - {C23BD71F-B503-41AF-A8FF-9282357B5CAF} + {AFA014CD-3F5B-4893-BB99-29C47F1D4B66} @@ -33,9 +33,6 @@ - - Driver Files - Driver Files diff --git a/spb/SpbTestTool/SpbTestTool.sln b/spb/SpbTestTool/SpbTestTool.sln index 78093615b..2177a4465 100644 --- a/spb/SpbTestTool/SpbTestTool.sln +++ b/spb/SpbTestTool/SpbTestTool.sln @@ -3,13 +3,13 @@ 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", "{F3AE97B3-4892-4B4F-BC6A-2BD54D73E455}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Exe", "Exe", "{938FF149-F06F-48E6-BB9F-3F3A8E92B45D}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sys", "Sys", "{13A0DE9D-F4EC-442A-97A5-246D5F2012B4}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sys", "Sys", "{0D9EFB9A-B31E-4E42-A997-AED20568BAE8}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SpbTestTool", "exe\SpbTestTool.vcxproj", "{57CB2DD4-0C4A-42B2-AF1C-CA8D0BB7CF88}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SpbTestTool", "exe\SpbTestTool.vcxproj", "{F8895649-C818-4F37-B427-1728D37B619F}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SpbTestTool", "sys\SpbTestTool.vcxproj", "{9164F300-DAA4-40F6-B7CA-8523CC819138}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SpbTestTool", "sys\SpbTestTool.vcxproj", "{EFAE8BC2-DFF1-4DD4-82DF-E0BA1DA065F2}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -19,28 +19,28 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {57CB2DD4-0C4A-42B2-AF1C-CA8D0BB7CF88}.Debug|Win32.ActiveCfg = Debug|Win32 - {57CB2DD4-0C4A-42B2-AF1C-CA8D0BB7CF88}.Debug|Win32.Build.0 = Debug|Win32 - {57CB2DD4-0C4A-42B2-AF1C-CA8D0BB7CF88}.Release|Win32.ActiveCfg = Release|Win32 - {57CB2DD4-0C4A-42B2-AF1C-CA8D0BB7CF88}.Release|Win32.Build.0 = Release|Win32 - {57CB2DD4-0C4A-42B2-AF1C-CA8D0BB7CF88}.Debug|x64.ActiveCfg = Debug|x64 - {57CB2DD4-0C4A-42B2-AF1C-CA8D0BB7CF88}.Debug|x64.Build.0 = Debug|x64 - {57CB2DD4-0C4A-42B2-AF1C-CA8D0BB7CF88}.Release|x64.ActiveCfg = Release|x64 - {57CB2DD4-0C4A-42B2-AF1C-CA8D0BB7CF88}.Release|x64.Build.0 = Release|x64 - {9164F300-DAA4-40F6-B7CA-8523CC819138}.Debug|Win32.ActiveCfg = Debug|Win32 - {9164F300-DAA4-40F6-B7CA-8523CC819138}.Debug|Win32.Build.0 = Debug|Win32 - {9164F300-DAA4-40F6-B7CA-8523CC819138}.Release|Win32.ActiveCfg = Release|Win32 - {9164F300-DAA4-40F6-B7CA-8523CC819138}.Release|Win32.Build.0 = Release|Win32 - {9164F300-DAA4-40F6-B7CA-8523CC819138}.Debug|x64.ActiveCfg = Debug|x64 - {9164F300-DAA4-40F6-B7CA-8523CC819138}.Debug|x64.Build.0 = Debug|x64 - {9164F300-DAA4-40F6-B7CA-8523CC819138}.Release|x64.ActiveCfg = Release|x64 - {9164F300-DAA4-40F6-B7CA-8523CC819138}.Release|x64.Build.0 = Release|x64 + {F8895649-C818-4F37-B427-1728D37B619F}.Debug|Win32.ActiveCfg = Debug|Win32 + {F8895649-C818-4F37-B427-1728D37B619F}.Debug|Win32.Build.0 = Debug|Win32 + {F8895649-C818-4F37-B427-1728D37B619F}.Release|Win32.ActiveCfg = Release|Win32 + {F8895649-C818-4F37-B427-1728D37B619F}.Release|Win32.Build.0 = Release|Win32 + {F8895649-C818-4F37-B427-1728D37B619F}.Debug|x64.ActiveCfg = Debug|x64 + {F8895649-C818-4F37-B427-1728D37B619F}.Debug|x64.Build.0 = Debug|x64 + {F8895649-C818-4F37-B427-1728D37B619F}.Release|x64.ActiveCfg = Release|x64 + {F8895649-C818-4F37-B427-1728D37B619F}.Release|x64.Build.0 = Release|x64 + {EFAE8BC2-DFF1-4DD4-82DF-E0BA1DA065F2}.Debug|Win32.ActiveCfg = Debug|Win32 + {EFAE8BC2-DFF1-4DD4-82DF-E0BA1DA065F2}.Debug|Win32.Build.0 = Debug|Win32 + {EFAE8BC2-DFF1-4DD4-82DF-E0BA1DA065F2}.Release|Win32.ActiveCfg = Release|Win32 + {EFAE8BC2-DFF1-4DD4-82DF-E0BA1DA065F2}.Release|Win32.Build.0 = Release|Win32 + {EFAE8BC2-DFF1-4DD4-82DF-E0BA1DA065F2}.Debug|x64.ActiveCfg = Debug|x64 + {EFAE8BC2-DFF1-4DD4-82DF-E0BA1DA065F2}.Debug|x64.Build.0 = Debug|x64 + {EFAE8BC2-DFF1-4DD4-82DF-E0BA1DA065F2}.Release|x64.ActiveCfg = Release|x64 + {EFAE8BC2-DFF1-4DD4-82DF-E0BA1DA065F2}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {57CB2DD4-0C4A-42B2-AF1C-CA8D0BB7CF88} = {F3AE97B3-4892-4B4F-BC6A-2BD54D73E455} - {9164F300-DAA4-40F6-B7CA-8523CC819138} = {13A0DE9D-F4EC-442A-97A5-246D5F2012B4} + {F8895649-C818-4F37-B427-1728D37B619F} = {938FF149-F06F-48E6-BB9F-3F3A8E92B45D} + {EFAE8BC2-DFF1-4DD4-82DF-E0BA1DA065F2} = {0D9EFB9A-B31E-4E42-A997-AED20568BAE8} EndGlobalSection EndGlobal diff --git a/spb/SpbTestTool/exe/SpbTestTool.vcxproj b/spb/SpbTestTool/exe/SpbTestTool.vcxproj index aade66fcf..8945336d7 100644 --- a/spb/SpbTestTool/exe/SpbTestTool.vcxproj +++ b/spb/SpbTestTool/exe/SpbTestTool.vcxproj @@ -19,11 +19,11 @@ - {57CB2DD4-0C4A-42B2-AF1C-CA8D0BB7CF88} + {F8895649-C818-4F37-B427-1728D37B619F} $(MSBuildProjectName) Debug Win32 - {1F37BEE4-37DF-4FFF-AE29-19EB3043B696} + {1B59D410-4ABC-4E35-94E8-9AE24DC45747} @@ -191,7 +191,6 @@ - diff --git a/spb/SpbTestTool/exe/SpbTestTool.vcxproj.Filters b/spb/SpbTestTool/exe/SpbTestTool.vcxproj.Filters index 3247ab25c..c937b0f2b 100644 --- a/spb/SpbTestTool/exe/SpbTestTool.vcxproj.Filters +++ b/spb/SpbTestTool/exe/SpbTestTool.vcxproj.Filters @@ -3,15 +3,15 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {2FB2155E-8ED8-434F-9145-27D7AF8E91AA} + {D20D7072-6F0A-4B23-A417-F0793F7410DC} h;hpp;hxx;hm;inl;inc;xsd - {C8220CFA-6854-4211-A434-EB211E4F307F} + {7BAC0BE4-C2E6-4A0F-BEC5-1566EE479BC6} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {FB1A6735-E65A-45FB-886E-F357664D28C3} + {2E9F5BF5-1259-459E-B8BF-5D476A6F3F48} diff --git a/spb/SpbTestTool/sys/SpbTestTool.vcxproj b/spb/SpbTestTool/sys/SpbTestTool.vcxproj index f9df87ddd..3efb59d65 100644 --- a/spb/SpbTestTool/sys/SpbTestTool.vcxproj +++ b/spb/SpbTestTool/sys/SpbTestTool.vcxproj @@ -19,18 +19,18 @@ - {9164F300-DAA4-40F6-B7CA-8523CC819138} + {EFAE8BC2-DFF1-4DD4-82DF-E0BA1DA065F2} $(MSBuildProjectName) 1 Debug Win32 - {6185C64E-9336-44A4-80EB-86AF71C788B1} + {E48CC9B0-A6B5-4F58-99AF-BEE1FE37C03C} Windows10 False - Desktop + Universal KMDF WindowsKernelModeDriver10.0 Driver @@ -38,7 +38,7 @@ Windows10 True - Desktop + Universal KMDF WindowsKernelModeDriver10.0 Driver @@ -46,7 +46,7 @@ Windows10 False - Desktop + Universal KMDF WindowsKernelModeDriver10.0 Driver @@ -54,7 +54,7 @@ Windows10 True - Desktop + Universal KMDF WindowsKernelModeDriver10.0 Driver @@ -184,7 +184,6 @@ - diff --git a/spb/SpbTestTool/sys/SpbTestTool.vcxproj.Filters b/spb/SpbTestTool/sys/SpbTestTool.vcxproj.Filters index f03b774b3..317eccac8 100644 --- a/spb/SpbTestTool/sys/SpbTestTool.vcxproj.Filters +++ b/spb/SpbTestTool/sys/SpbTestTool.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {91FD9DF9-6894-438F-9FE7-54F26AAE0251} + {E3801549-0C13-4FAB-9FF9-2A093377385F} h;hpp;hxx;hm;inl;inc;xsd - {773429C3-6E0E-4E2B-B859-13563580C523} + {73E6AB4E-A287-4AD7-BC48-66794144F852} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {D591E539-1E9D-44A8-B13D-6D5217A95A29} + {B3ADF85B-456B-4032-BB7F-1007032D9689} inf;inv;inx;mof;mc; - {5A587D2B-32E7-4FAD-BF91-69C37B635B94} + {FDB36A4E-17A0-425D-AC61-48B005A00D91} @@ -30,9 +30,6 @@ - - Driver Files - Driver Files diff --git a/spb/SpbTestTool/sys/spbtesttool.inx b/spb/SpbTestTool/sys/spbtesttool.inx index e1655ab243f1081209f16f8537b1d4e66ae9a4b2..ae1a4fe46b331e10b6666b44e88fa55d87afbafe 100644 GIT binary patch delta 99 zcmX@5azSN77qfT(LlHwhLm5LRLkdGGknP8i$dJpBx_K_MBo9BDFWa Y{tPZatjiD#)Kmhb(Nu2c6mVn$0HCcEwg3PC delta 97 zcmcbha!O@G7qhT0LokCILkN&`WbkDOVDMpZ-Mo-lk_Vf_WJLk#$r}XN - {1683D068-B3E1-4216-8BA9-A8ADC10DBAC6} + {5CAAC17A-F6C8-4F60-8599-BF5B710B9E75} $(MSBuildProjectName) 1 false Debug Win32 - {E200755F-0634-4431-B800-411C41E79190} + {1D0354B6-7638-4B49-A326-3F635DF4792D} @@ -155,7 +155,6 @@ - diff --git a/storage/class/cdrom/src/cdrom.vcxproj.Filters b/storage/class/cdrom/src/cdrom.vcxproj.Filters index 35fd8b4c0..984e47ffc 100644 --- a/storage/class/cdrom/src/cdrom.vcxproj.Filters +++ b/storage/class/cdrom/src/cdrom.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {00DD8EE3-FDCD-4456-BC4F-E74036AB1AC1} + {F4624455-84A0-4D68-9FDA-5A45BDDF16E9} h;hpp;hxx;hm;inl;inc;xsd - {ED90C8BD-839B-498E-801C-9D36D5AE7AA7} + {59782596-7387-4C2F-8B3F-14DD7DBBE39E} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {4FF8F695-EF19-47F0-B961-AA636092E365} + {F55549B9-4C00-441C-8960-79D8E04B2640} inf;inv;inx;mof;mc; - {AB325463-69C0-4899-82FF-C8D632CBE6C9} + {BE2B60C1-58DD-4635-A2B4-E75CEC446355} @@ -60,9 +60,6 @@ - - Driver Files - Driver Files diff --git a/storage/class/classpnp/classpnp.sln b/storage/class/classpnp/classpnp.sln index 3be888146..b11f57682 100644 --- a/storage/class/classpnp/classpnp.sln +++ b/storage/class/classpnp/classpnp.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0 MinimumVisualStudioVersion = 12.0 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "classpnp", "src\classpnp.vcxproj", "{A5AC0FD0-F93F-4FAD-940A-704DF40D9283}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "classpnp", "src\classpnp.vcxproj", "{427E597B-35E8-4D6C-8AA5-35A4519B33D5}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {A5AC0FD0-F93F-4FAD-940A-704DF40D9283}.Debug|Win32.ActiveCfg = Debug|Win32 - {A5AC0FD0-F93F-4FAD-940A-704DF40D9283}.Debug|Win32.Build.0 = Debug|Win32 - {A5AC0FD0-F93F-4FAD-940A-704DF40D9283}.Release|Win32.ActiveCfg = Release|Win32 - {A5AC0FD0-F93F-4FAD-940A-704DF40D9283}.Release|Win32.Build.0 = Release|Win32 - {A5AC0FD0-F93F-4FAD-940A-704DF40D9283}.Debug|x64.ActiveCfg = Debug|x64 - {A5AC0FD0-F93F-4FAD-940A-704DF40D9283}.Debug|x64.Build.0 = Debug|x64 - {A5AC0FD0-F93F-4FAD-940A-704DF40D9283}.Release|x64.ActiveCfg = Release|x64 - {A5AC0FD0-F93F-4FAD-940A-704DF40D9283}.Release|x64.Build.0 = Release|x64 + {427E597B-35E8-4D6C-8AA5-35A4519B33D5}.Debug|Win32.ActiveCfg = Debug|Win32 + {427E597B-35E8-4D6C-8AA5-35A4519B33D5}.Debug|Win32.Build.0 = Debug|Win32 + {427E597B-35E8-4D6C-8AA5-35A4519B33D5}.Release|Win32.ActiveCfg = Release|Win32 + {427E597B-35E8-4D6C-8AA5-35A4519B33D5}.Release|Win32.Build.0 = Release|Win32 + {427E597B-35E8-4D6C-8AA5-35A4519B33D5}.Debug|x64.ActiveCfg = Debug|x64 + {427E597B-35E8-4D6C-8AA5-35A4519B33D5}.Debug|x64.Build.0 = Debug|x64 + {427E597B-35E8-4D6C-8AA5-35A4519B33D5}.Release|x64.ActiveCfg = Release|x64 + {427E597B-35E8-4D6C-8AA5-35A4519B33D5}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/storage/class/classpnp/src/class.c b/storage/class/classpnp/src/class.c index 2f92e453b..6113cd7ed 100644 --- a/storage/class/classpnp/src/class.c +++ b/storage/class/classpnp/src/class.c @@ -1938,7 +1938,7 @@ NTSTATUS ClassPnpStartDevice(IN PDEVICE_OBJECT DeviceObject) // fdoExtension->PrivateFdoData->InterpretSenseInfo = driverExtension->InterpretSenseInfo; - fdoExtension->PrivateFdoData->MaxNumberOfIoRetries = NUM_IO_RETRIES; + fdoExtension->PrivateFdoData->MaxNumberOfIoRetries = NUM_IO_RETRIES; // // Initialize release queue extended SRB @@ -3925,6 +3925,17 @@ ClassIoComplete( Irp->IoStatus.Information = 0; } + // + // If disk firmware update succeeded, log a system event. + // + + if (NT_SUCCESS(status) && + (irpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL) && + (irpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_STORAGE_FIRMWARE_ACTIVATE)) { + + ClasspLogSystemEventWithDeviceNumber(Fdo, IO_WARNING_DISK_FIRMWARE_UPDATED); + } + // // If pending has be returned for this irp then mark the current stack as // pending. @@ -4038,6 +4049,7 @@ ClassSendSrbSynchronous( NT_ASSERT( ((fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_SCSI_REQUEST_BLOCK) && (Srb->Function == SRB_FUNCTION_EXECUTE_SCSI)) || ((fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) && (Srb->Function == SRB_FUNCTION_STORAGE_REQUEST_BLOCK)) ); + // // Sense buffer is in aligned nonpaged pool. // @@ -4316,6 +4328,7 @@ ClassSendSrbSynchronous( } + // // If retries are not exhausted then retry this operation. // @@ -6099,6 +6112,7 @@ ClassInterpretSenseInfo( break; } + default: { logError = TRUE; logStatus = IO_ERR_CONTROLLER_ERROR; diff --git a/storage/class/classpnp/src/classp.h b/storage/class/classpnp/src/classp.h index b46b2d8b5..e0046be8e 100644 --- a/storage/class/classpnp/src/classp.h +++ b/storage/class/classpnp/src/classp.h @@ -113,7 +113,6 @@ extern ULONG ClassMaxInterleavePerCriticalIo; #define CLASSP_REG_DISABLE_D3COLD (L"DisableD3Cold") #define CLASSP_REG_QERR_OVERRIDE_MODE (L"QERROverrideMode") #define CLASSP_REG_LEGACY_ERROR_HANDLING (L"LegacyErrorHandling") -#define CLASSP_REG_IO_TIMEOUT_RETRY_COUNT (L"MaxIoTimeoutRetryCount") #define CLASS_PERF_RESTORE_MINIMUM (0x10) #define CLASS_ERROR_LEVEL_1 (0x4) @@ -956,6 +955,12 @@ struct _CLASS_PRIVATE_FDO_DATA { // Maximum number of retries allowed for IO requests for this device. // UCHAR MaxNumberOfIoRetries; + + // + // Disable All Throttling in case of Error + // + BOOLEAN DisableThrottling; + }; // @@ -1175,6 +1180,10 @@ typedef struct _IO_RETRIED_LOG_MESSAGE_CONTEXT { #define CLASS_STARVATION_INTERVAL 500 // 500 milliseconds #define CLASS_IDLE_TIMER_TICKS 4 +// +// Value of 50 milliseconds in 100 nanoseconds units +// +#define FIFTY_MS_IN_100NS_UNITS 50 * 100 /* * Simple singly-linked-list queuing macros, with no synchronization. @@ -1967,6 +1976,7 @@ ClasspDeviceMediaTypeProperty( _Inout_ PSCSI_REQUEST_BLOCK Srb ); + _IRQL_requires_same_ PUCHAR ClasspBinaryToAscii( @@ -2597,6 +2607,19 @@ ClasspLowerLayerNotSupport ( (Status == STATUS_INVALID_PARAMETER_1)); } +__inline +BOOLEAN +ClasspSrbTimeOutStatus ( + _In_ PSTORAGE_REQUEST_BLOCK_HEADER Srb + ) +{ + UCHAR srbStatus = SrbGetSrbStatus(Srb); + return ((srbStatus == SRB_STATUS_BUS_RESET) || + (srbStatus == SRB_STATUS_TIMEOUT) || + (srbStatus == SRB_STATUS_COMMAND_TIMEOUT) || + (srbStatus == SRB_STATUS_ABORTED)); +} + NTSTATUS ClassDeviceHwFirmwareGetInfoProcess( _In_ PDEVICE_OBJECT DeviceObject, diff --git a/storage/class/classpnp/src/classpnp.vcxproj b/storage/class/classpnp/src/classpnp.vcxproj index ca387b676..20ea07c2e 100644 --- a/storage/class/classpnp/src/classpnp.vcxproj +++ b/storage/class/classpnp/src/classpnp.vcxproj @@ -19,12 +19,12 @@ - {A5AC0FD0-F93F-4FAD-940A-704DF40D9283} + {427E597B-35E8-4D6C-8AA5-35A4519B33D5} $(MSBuildProjectName) false Debug Win32 - {36EE04FE-C8BB-410A-90C6-DD06D6865FAF} + {F2A79FAF-7AB2-460E-A8F9-E0CB6AE58519} @@ -308,7 +308,6 @@ - diff --git a/storage/class/classpnp/src/classpnp.vcxproj.Filters b/storage/class/classpnp/src/classpnp.vcxproj.Filters index a0c569935..de33c9043 100644 --- a/storage/class/classpnp/src/classpnp.vcxproj.Filters +++ b/storage/class/classpnp/src/classpnp.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {1400A9A1-2ED5-462A-A94C-FCE810F5566E} + {E57AA4D3-2361-468E-BADF-7E50D1657C78} h;hpp;hxx;hm;inl;inc;xsd - {5BE1D83C-D225-42F8-AA81-E3E4C5A7200D} + {73F07EE3-6460-42CC-9827-F4EB5CBD6FFA} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {29F75A8E-F140-40BB-A154-FCC5F6584668} + {01AD74A8-90EE-49F4-B392-6C515177B2CB} inf;inv;inx;mof;mc; - {EA1725C9-0F85-48DD-8BB8-7234C2DDF3FA} + {F1A14E0E-14F3-4FBA-B152-C150E51DFCB6} diff --git a/storage/class/classpnp/src/retry.c b/storage/class/classpnp/src/retry.c index 90d70ce37..1a74c0b28 100644 --- a/storage/class/classpnp/src/retry.c +++ b/storage/class/classpnp/src/retry.c @@ -241,6 +241,28 @@ BOOLEAN InterpretTransferPacketError(PTRANSFER_PACKET Pkt) if (Pkt->Irp->IoStatus.Status == STATUS_VERIFY_REQUIRED) { shouldRetry = TRUE; } +#if (NTDDI_VERSION >= NTDDI_WINBLUE) + else if (ClasspSrbTimeOutStatus(Pkt->Srb)) { + + Pkt->TimedOut = TRUE; + + if (shouldRetry) { + // + // For requests that have timed-out we may only perform a limited + // number of retries. This is typically less than the general + // number of retries allowed. + // + if (Pkt->NumIoTimeoutRetries == 0) { + shouldRetry = FALSE; + } else { + Pkt->NumIoTimeoutRetries--; + // + // We expect to be able to retry if there are some general retries remaining. + // + } + } + } +#endif if (shouldRetry) { @@ -286,10 +308,7 @@ BOOLEAN InterpretTransferPacketError(PTRANSFER_PACKET Pkt) } #if (NTDDI_VERSION >= NTDDI_WINBLUE) - else if (SrbGetSrbStatus(Pkt->Srb) == SRB_STATUS_BUS_RESET || - SrbGetSrbStatus(Pkt->Srb) == SRB_STATUS_TIMEOUT || - SrbGetSrbStatus(Pkt->Srb) == SRB_STATUS_COMMAND_TIMEOUT || - SrbGetSrbStatus(Pkt->Srb) == SRB_STATUS_ABORTED) { + else if (ClasspSrbTimeOutStatus(Pkt->Srb)) { Pkt->TimedOut = TRUE; @@ -306,7 +325,6 @@ BOOLEAN InterpretTransferPacketError(PTRANSFER_PACKET Pkt) // // We expect to be able to retry if there are some general retries remaining. // - NT_ASSERT(Pkt->NumRetries > 0); } } } @@ -436,30 +454,37 @@ BOOLEAN RetryTransferPacket(PTRANSFER_PACKET Pkt) { BOOLEAN packetDone; BOOLEAN scaleDown = FALSE; + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)Pkt->Fdo->DeviceExtension; + PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData; + PCDB pCdb = SrbGetCdb(Pkt->Srb); TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "retrying failed transfer (pkt=%ph, op=%s)", Pkt, DBGGETSCSIOPSTR(Pkt->Srb))); NT_ASSERT(Pkt->NumRetries > 0 || Pkt->RetryHistory); Pkt->NumRetries--; - // - // If this is the last retry, then turn off disconnect, sync transfer, - // and tagged queuing. On all other retries, leave the original settings. - // - if (Pkt->NumRetries == 0) { - scaleDown = TRUE; - } + if (!fdoData->DisableThrottling) { + + // + // If this is the last retry, then turn off disconnect, sync transfer, + // and tagged queuing. On all other retries, leave the original settings. + // + if (Pkt->NumRetries == 0) { + scaleDown = TRUE; + } #if (NTDDI_VERSION >= NTDDI_WINBLUE) - // - // If this request previously timed-out and there are no more retries left - // for timed-out requests then we should also apply the scale down. - // - if (Pkt->TimedOut && - Pkt->NumIoTimeoutRetries == 0) { - scaleDown = TRUE; - } + // + // If this request previously timed-out and there are no more retries left + // for timed-out requests then we should also apply the scale down. + // + if (Pkt->TimedOut && + Pkt->NumIoTimeoutRetries == 0) { + scaleDown = TRUE; + } #endif + } + if (scaleDown) { /* @@ -476,13 +501,9 @@ BOOLEAN RetryTransferPacket(PTRANSFER_PACKET Pkt) if (Pkt->Irp->IoStatus.Status == STATUS_INSUFFICIENT_RESOURCES) { - PCDB pCdb = SrbGetCdb(Pkt->Srb); UCHAR cdbOpcode = 0; BOOLEAN isReadWrite = FALSE; - PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Pkt->Fdo->DeviceExtension; - PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData; - if (pCdb) { cdbOpcode = pCdb->CDB10.OperationCode; isReadWrite = IS_SCSIOP_READWRITE(cdbOpcode); diff --git a/storage/class/classpnp/src/utils.c b/storage/class/classpnp/src/utils.c index b503f890a..20917d83c 100644 --- a/storage/class/classpnp/src/utils.c +++ b/storage/class/classpnp/src/utils.c @@ -436,37 +436,40 @@ ClasspPerfIncrementErrorCount( fdoData->Perf.SuccessfulIO = 0; // implicit interlock errors = InterlockedIncrement((volatile LONG *)&FdoExtension->ErrorCount); - if (errors >= CLASS_ERROR_LEVEL_1) { + if (!fdoData->DisableThrottling) { - // - // If the error count has exceeded the error limit, then disable - // any tagged queuing, multiple requests per lu queueing - // and sychronous data transfers. - // - // Clearing the no queue freeze flag prevents the port driver - // from sending multiple requests per logical unit. - // + if (errors >= CLASS_ERROR_LEVEL_1) { + + // + // If the error count has exceeded the error limit, then disable + // any tagged queuing, multiple requests per lu queueing + // and sychronous data transfers. + // + // Clearing the no queue freeze flag prevents the port driver + // from sending multiple requests per logical unit. + // - CLEAR_FLAG(FdoExtension->SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE); - CLEAR_FLAG(FdoExtension->SrbFlags, SRB_FLAGS_QUEUE_ACTION_ENABLE); + CLEAR_FLAG(FdoExtension->SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE); + CLEAR_FLAG(FdoExtension->SrbFlags, SRB_FLAGS_QUEUE_ACTION_ENABLE); - SET_FLAG(FdoExtension->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER); + SET_FLAG(FdoExtension->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER); - TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "ClasspPerfIncrementErrorCount: " - "Too many errors; disabling tagged queuing and " - "synchronous data tranfers.\n")); + TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "ClasspPerfIncrementErrorCount: " + "Too many errors; disabling tagged queuing and " + "synchronous data tranfers.\n")); - } + } - if (errors >= CLASS_ERROR_LEVEL_2) { + if (errors >= CLASS_ERROR_LEVEL_2) { - // - // If a second threshold is reached, disable disconnects. - // + // + // If a second threshold is reached, disable disconnects. + // - SET_FLAG(FdoExtension->SrbFlags, SRB_FLAGS_DISABLE_DISCONNECT); - TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "ClasspPerfIncrementErrorCount: " - "Too many errors; disabling disconnects.\n")); + SET_FLAG(FdoExtension->SrbFlags, SRB_FLAGS_DISABLE_DISCONNECT); + TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "ClasspPerfIncrementErrorCount: " + "Too many errors; disabling disconnects.\n")); + } } KeReleaseSpinLock(&fdoData->SpinLock, oldIrql); @@ -1635,6 +1638,7 @@ ClassReadCapacity16 ( //prepare the Srb if (NT_SUCCESS(status)) { + SrbSetTimeOutValue(Srb, FdoExtension->TimeOutValue); SrbSetRequestTag(Srb, SP_UNTAGGED); SrbSetRequestAttribute(Srb, SRB_SIMPLE_TAG_REQUEST); @@ -8193,6 +8197,42 @@ ClasspGetHwFirmwareInfo( #endif // #if (NTDDI_VERSION >= NTDDI_WINTHRESHOLD) +__inline +BOOLEAN +ClassDeviceHwFirmwareIsPortDriverSupported( + _In_ PDEVICE_OBJECT DeviceObject + ) +/* +Routine Description: + + This function informs the caller whether the port driver supports hardware firmware requests. + +Arguments: + DeviceObject: The target object. + +Return Value: + + TRUE if the port driver is supported. + +--*/ +{ + // + // If the request is for a FDO, process the request for Storport, SDstor and Spaceport only. + // Don't process it if we don't have a miniport descriptor. + // + PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; + PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension; + + BOOLEAN isSupported = FALSE; + if (commonExtension->IsFdo && (fdoExtension->MiniportDescriptor != NULL)) { + isSupported = ((fdoExtension->MiniportDescriptor->Portdriver == StoragePortCodeSetStorport) || + (fdoExtension->MiniportDescriptor->Portdriver == StoragePortCodeSetSpaceport) || + (fdoExtension->MiniportDescriptor->Portdriver == StoragePortCodeSetSDport )); + } + + return isSupported; +} + NTSTATUS ClassDeviceHwFirmwareGetInfoProcess( _In_ PDEVICE_OBJECT DeviceObject, @@ -8245,20 +8285,16 @@ Return Value: } // - // If the request is for a FDO, process the request for Storport, SDstor and Spaceport only. + // Only process the request for a supported port driver. // - if (commonExtension->IsFdo && - (fdoExtension->MiniportDescriptor->Portdriver != StoragePortCodeSetStorport) && - (fdoExtension->MiniportDescriptor->Portdriver != StoragePortCodeSetSpaceport) && - (fdoExtension->MiniportDescriptor->Portdriver != StoragePortCodeSetSDport)) { - + if (!ClassDeviceHwFirmwareIsPortDriverSupported(DeviceObject)) { status = STATUS_NOT_IMPLEMENTED; goto Exit_Firmware_Get_Info; } // - // Buffer "FunctionSupportInfo" is allocated during start device process. Following check defenses the situation - // of receiving this IOCTL when the device is created but not started, or device start failed but not get removed yet. + // Buffer "FunctionSupportInfo" is allocated during start device process. Following check defends against the situation + // of receiving this IOCTL when the device is created but not started, or device start failed but did not get removed yet. // if (commonExtension->IsFdo && (fdoExtension->FunctionSupportInfo == NULL)) { @@ -8413,20 +8449,16 @@ ClassDeviceHwFirmwareDownloadProcess( } // - // If the request is for a FDO, process the request for Storport, SDstor and Spaceport only. + // Only process the request for a supported port driver. // - if (commonExtension->IsFdo && - (fdoExtension->MiniportDescriptor->Portdriver != StoragePortCodeSetStorport) && - (fdoExtension->MiniportDescriptor->Portdriver != StoragePortCodeSetSpaceport) && - (fdoExtension->MiniportDescriptor->Portdriver != StoragePortCodeSetSDport)) { - + if (!ClassDeviceHwFirmwareIsPortDriverSupported(DeviceObject)) { status = STATUS_NOT_IMPLEMENTED; goto Exit_Firmware_Download; } // - // Buffer "FunctionSupportInfo" is allocated during start device process. Following check defenses the situation - // of receiving this IOCTL when the device is created but not started, or device start failed but not get removed yet. + // Buffer "FunctionSupportInfo" is allocated during start device process. Following check defends against the situation + // of receiving this IOCTL when the device is created but not started, or device start failed but did not get removed yet. // if (commonExtension->IsFdo && (fdoExtension->FunctionSupportInfo == NULL)) { @@ -8741,20 +8773,16 @@ ClassDeviceHwFirmwareActivateProcess( } // - // If the request is for a FDO, process the request for Storport, SDstor and Spaceport only. + // Only process the request for a supported port driver. // - if (commonExtension->IsFdo && - (fdoExtension->MiniportDescriptor->Portdriver != StoragePortCodeSetStorport) && - (fdoExtension->MiniportDescriptor->Portdriver != StoragePortCodeSetSpaceport) && - (fdoExtension->MiniportDescriptor->Portdriver != StoragePortCodeSetSDport)) { - + if (!ClassDeviceHwFirmwareIsPortDriverSupported(DeviceObject)) { status = STATUS_NOT_IMPLEMENTED; goto Exit_Firmware_Activate; } // - // Buffer "FunctionSupportInfo" is allocated during start device process. Following check defenses the situation - // of receiving this IOCTL when the device is created but not started, or device start failed but not get removed yet. + // Buffer "FunctionSupportInfo" is allocated during start device process. Following check defends against the situation + // of receiving this IOCTL when the device is created but not started, or device start failed but did not get removed yet. // if (commonExtension->IsFdo && (fdoExtension->FunctionSupportInfo == NULL)) { @@ -8901,3 +8929,4 @@ ClassDeviceHwFirmwareActivateProcess( FREE_POOL(Srb); return status; } + diff --git a/storage/class/classpnp/src/xferpkt.c b/storage/class/classpnp/src/xferpkt.c index 159a59b31..db7287705 100644 --- a/storage/class/classpnp/src/xferpkt.c +++ b/storage/class/classpnp/src/xferpkt.c @@ -157,7 +157,7 @@ NTSTATUS InitializeTransferPackets(PDEVICE_OBJECT Fdo) // this is Server SKU // Note: the addition max here to make sure we set the min to be at least // MIN_WORKINGSET_TRANSFER_PACKETS_Server_LowerBound no matter what maxOutstandingIOPerLUN - // reported. We shouldn’t set this value to be smaller than client system. + // reported. We shouldn't set this value to be smaller than client system. // In other words, the minWorkingSetTransferPackets for server will always between // MIN_WORKINGSET_TRANSFER_PACKETS_Server_LowerBound and MIN_WORKINGSET_TRANSFER_PACKETS_Server_UpperBound @@ -823,6 +823,7 @@ VOID SetupReadWriteTransferPacket( PTRANSFER_PACKET Pkt, Pkt->NumRetries = fdoData->MaxNumberOfIoRetries; Pkt->SyncEventPtr = NULL; Pkt->CompleteOriginalIrpWhenLastPacketCompletes = TRUE; + Pkt->NumIoTimeoutRetries = fdoData->MaxNumberOfIoRetries; if (pCdb) { @@ -1451,6 +1452,7 @@ VOID SetupDriveCapacityTransferPacket( TRANSFER_PACKET *Pkt, PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData; PCDB pCdb; ULONG srbLength; + ULONG timeoutValue = fdoExt->TimeOutValue; if (fdoExt->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) { srbLength = ((PSTORAGE_REQUEST_BLOCK) fdoData->SrbTemplate)->SrbLength; @@ -1464,7 +1466,9 @@ VOID SetupDriveCapacityTransferPacket( TRANSFER_PACKET *Pkt, SrbSetOriginalRequest(Pkt->Srb, Pkt->Irp); SrbSetSenseInfoBuffer(Pkt->Srb, &Pkt->SrbErrorSenseData); SrbSetSenseInfoBufferLength(Pkt->Srb, sizeof(Pkt->SrbErrorSenseData)); - SrbSetTimeOutValue(Pkt->Srb, fdoExt->TimeOutValue); + + + SrbSetTimeOutValue(Pkt->Srb, timeoutValue); SrbSetDataBuffer(Pkt->Srb, ReadCapacityBuffer); SrbSetDataTransferLength(Pkt->Srb, ReadCapacityBufferLen); @@ -1489,6 +1493,8 @@ VOID SetupDriveCapacityTransferPacket( TRANSFER_PACKET *Pkt, Pkt->OriginalIrp = OriginalIrp; Pkt->NumRetries = NUM_DRIVECAPACITY_RETRIES; + Pkt->NumIoTimeoutRetries = NUM_DRIVECAPACITY_RETRIES; + Pkt->SyncEventPtr = SyncEventPtr; Pkt->CompleteOriginalIrpWhenLastPacketCompletes = FALSE; } diff --git a/storage/class/disk/disk.sln b/storage/class/disk/disk.sln index ad08d643a..3632e9319 100644 --- a/storage/class/disk/disk.sln +++ b/storage/class/disk/disk.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0 MinimumVisualStudioVersion = 12.0 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "disk", "src\disk.vcxproj", "{627F8680-3389-4050-A0E1-9EA14F615498}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "disk", "src\disk.vcxproj", "{0900F953-9DAF-4C58-9137-45A2DEC9C62C}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {627F8680-3389-4050-A0E1-9EA14F615498}.Debug|Win32.ActiveCfg = Debug|Win32 - {627F8680-3389-4050-A0E1-9EA14F615498}.Debug|Win32.Build.0 = Debug|Win32 - {627F8680-3389-4050-A0E1-9EA14F615498}.Release|Win32.ActiveCfg = Release|Win32 - {627F8680-3389-4050-A0E1-9EA14F615498}.Release|Win32.Build.0 = Release|Win32 - {627F8680-3389-4050-A0E1-9EA14F615498}.Debug|x64.ActiveCfg = Debug|x64 - {627F8680-3389-4050-A0E1-9EA14F615498}.Debug|x64.Build.0 = Debug|x64 - {627F8680-3389-4050-A0E1-9EA14F615498}.Release|x64.ActiveCfg = Release|x64 - {627F8680-3389-4050-A0E1-9EA14F615498}.Release|x64.Build.0 = Release|x64 + {0900F953-9DAF-4C58-9137-45A2DEC9C62C}.Debug|Win32.ActiveCfg = Debug|Win32 + {0900F953-9DAF-4C58-9137-45A2DEC9C62C}.Debug|Win32.Build.0 = Debug|Win32 + {0900F953-9DAF-4C58-9137-45A2DEC9C62C}.Release|Win32.ActiveCfg = Release|Win32 + {0900F953-9DAF-4C58-9137-45A2DEC9C62C}.Release|Win32.Build.0 = Release|Win32 + {0900F953-9DAF-4C58-9137-45A2DEC9C62C}.Debug|x64.ActiveCfg = Debug|x64 + {0900F953-9DAF-4C58-9137-45A2DEC9C62C}.Debug|x64.Build.0 = Debug|x64 + {0900F953-9DAF-4C58-9137-45A2DEC9C62C}.Release|x64.ActiveCfg = Release|x64 + {0900F953-9DAF-4C58-9137-45A2DEC9C62C}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/storage/class/disk/src/disk.vcxproj b/storage/class/disk/src/disk.vcxproj index 7d749d1cc..1f52e3703 100644 --- a/storage/class/disk/src/disk.vcxproj +++ b/storage/class/disk/src/disk.vcxproj @@ -19,12 +19,12 @@ - {627F8680-3389-4050-A0E1-9EA14F615498} + {0900F953-9DAF-4C58-9137-45A2DEC9C62C} $(MSBuildProjectName) false Debug Win32 - {0AA04C84-504B-44EC-90C4-BDC48593BE4F} + {6222553B-2B65-4267-9522-973010FB316D} @@ -181,7 +181,6 @@ - diff --git a/storage/class/disk/src/disk.vcxproj.Filters b/storage/class/disk/src/disk.vcxproj.Filters index 7c1cdf7d6..1a2af0529 100644 --- a/storage/class/disk/src/disk.vcxproj.Filters +++ b/storage/class/disk/src/disk.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {F46D90F7-048E-494E-B795-E7FE53BA14F3} + {9D58AF57-E3D3-492B-A93C-CD4BD860C101} h;hpp;hxx;hm;inl;inc;xsd - {99D7E70D-CDF2-4F0C-A0DC-E296CCFD9C4D} + {5021C225-A54E-4A00-A24F-A0AEC3B6B92A} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {3C108ECD-99BB-4DF0-9937-B9E5541E1831} + {2092A6B9-C332-464C-B263-00060DE1B3E0} inf;inv;inx;mof;mc; - {64085F1A-1949-4F74-A70E-5A1FB0584FD8} + {2F3C9ECA-BD6F-472C-915B-51096E28B70C} diff --git a/storage/class/disk/src/diskdev.inf b/storage/class/disk/src/diskdev.inf index 78bea6a48..73a26b23f 100644 --- a/storage/class/disk/src/diskdev.inf +++ b/storage/class/disk/src/diskdev.inf @@ -10,7 +10,7 @@ [Version] Signature="$Windows NT$" -Provider=%MS% +Provider=%ProviderName% ClassGUID={4d36e967-e325-11ce-bfc1-08002be10318} Class=DiskDrive DriverVer=08/27/1999,5.2.1425.0 @@ -24,19 +24,19 @@ DefaultDestDir = 12 ; [Manufacturer] -%MS% = MS.Mfg, NTx86, NTia64, NTamd64 +%MfgName% = Standard.Mfg, NTx86, NTia64, NTamd64 -[MS.Mfg.NTx86] -%MS.DeviceDesc0% = disk, GenDisk -%MS.DeviceDesc1% = disk, GenOptical +[Standard.Mfg.NTx86] +%GenDisk.DeviceDesc% = disk, GenDisk +%GenOptical.DeviceDesc% = disk, GenOptical -[MS.Mfg.NTia64] -%MS.DeviceDesc0% = disk, GenDisk -%MS.DeviceDesc1% = disk, GenOptical +[Standard.Mfg.NTia64] +%GenDisk.DeviceDesc% = disk, GenDisk +%GenOptical.DeviceDesc% = disk, GenOptical -[MS.Mfg.NTamd64] -%MS.DeviceDesc0% = disk, GenDisk -%MS.DeviceDesc1% = disk, GenOptical +[Standard.Mfg.NTamd64] +%GenDisk.DeviceDesc% = disk, GenDisk +%GenOptical.DeviceDesc% = disk, GenOptical ; @@ -84,7 +84,6 @@ ServiceBinary = %12%\disk.sys 1 = %DiskId1%,,,\amd64 [SourceDisksFiles] -; Files for disk Microsoft Corp. Installation Disk #1 (DiskDrive) disk.sys = 1,, @@ -99,15 +98,16 @@ REG_MULTI_SZ = 0x00010000 REG_EXPAND_SZ = 0x00020000 REG_BINARY = 0x00000001 REG_DWORD = 0x00010001 -SERVICEROOT = "System\CurrentControlSet\Services" ; ; Localizable Strings ; -MS.DeviceDesc0 = "Disk drive" -MS.DeviceDesc1 = "Optical disk drive" -DiskId1 = "Microsoft Corp. Installation Disk #1 (DiskDrive)" -MS = "Microsoft Corp." +ProviderName = "TODO-Set-Provider" +MfgName = "TODO-Set-Manufacturer" + +GenDisk.DeviceDesc = "Disk drive" +GenOptical.DeviceDesc = "Optical disk drive" +DiskId1 = "Install disk" disk.SvcDesc="Disk Drive" diff --git a/storage/filters/addfilter/addfilter.sln b/storage/filters/addfilter/addfilter.sln index 539a5073b..d0a0bda90 100644 --- a/storage/filters/addfilter/addfilter.sln +++ b/storage/filters/addfilter/addfilter.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0 MinimumVisualStudioVersion = 12.0 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "addfilter", "src\addfilter.vcxproj", "{7FB29A64-E54D-40CA-AF78-2347B8E90BCB}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "addfilter", "src\addfilter.vcxproj", "{333BB1A9-B991-4148-8E4A-717743859C27}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {7FB29A64-E54D-40CA-AF78-2347B8E90BCB}.Debug|Win32.ActiveCfg = Debug|Win32 - {7FB29A64-E54D-40CA-AF78-2347B8E90BCB}.Debug|Win32.Build.0 = Debug|Win32 - {7FB29A64-E54D-40CA-AF78-2347B8E90BCB}.Release|Win32.ActiveCfg = Release|Win32 - {7FB29A64-E54D-40CA-AF78-2347B8E90BCB}.Release|Win32.Build.0 = Release|Win32 - {7FB29A64-E54D-40CA-AF78-2347B8E90BCB}.Debug|x64.ActiveCfg = Debug|x64 - {7FB29A64-E54D-40CA-AF78-2347B8E90BCB}.Debug|x64.Build.0 = Debug|x64 - {7FB29A64-E54D-40CA-AF78-2347B8E90BCB}.Release|x64.ActiveCfg = Release|x64 - {7FB29A64-E54D-40CA-AF78-2347B8E90BCB}.Release|x64.Build.0 = Release|x64 + {333BB1A9-B991-4148-8E4A-717743859C27}.Debug|Win32.ActiveCfg = Debug|Win32 + {333BB1A9-B991-4148-8E4A-717743859C27}.Debug|Win32.Build.0 = Debug|Win32 + {333BB1A9-B991-4148-8E4A-717743859C27}.Release|Win32.ActiveCfg = Release|Win32 + {333BB1A9-B991-4148-8E4A-717743859C27}.Release|Win32.Build.0 = Release|Win32 + {333BB1A9-B991-4148-8E4A-717743859C27}.Debug|x64.ActiveCfg = Debug|x64 + {333BB1A9-B991-4148-8E4A-717743859C27}.Debug|x64.Build.0 = Debug|x64 + {333BB1A9-B991-4148-8E4A-717743859C27}.Release|x64.ActiveCfg = Release|x64 + {333BB1A9-B991-4148-8E4A-717743859C27}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/storage/filters/addfilter/src/addfilter.vcxproj b/storage/filters/addfilter/src/addfilter.vcxproj index 401a37eb3..5ed662381 100644 --- a/storage/filters/addfilter/src/addfilter.vcxproj +++ b/storage/filters/addfilter/src/addfilter.vcxproj @@ -19,11 +19,11 @@ - {7FB29A64-E54D-40CA-AF78-2347B8E90BCB} + {333BB1A9-B991-4148-8E4A-717743859C27} $(MSBuildProjectName) Debug Win32 - {397B8C6C-A536-444A-8878-DCEDB2A025F9} + {4D1BE240-118A-47DA-A950-1599577B4F12} @@ -178,7 +178,6 @@ - diff --git a/storage/filters/addfilter/src/addfilter.vcxproj.Filters b/storage/filters/addfilter/src/addfilter.vcxproj.Filters index ed2c76b52..b8180f805 100644 --- a/storage/filters/addfilter/src/addfilter.vcxproj.Filters +++ b/storage/filters/addfilter/src/addfilter.vcxproj.Filters @@ -3,15 +3,15 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {C7A4F292-62DA-4FC0-83E2-5555F22B36BF} + {2EA932C6-BBAE-4F02-8679-F051D9F4FC59} h;hpp;hxx;hm;inl;inc;xsd - {0FDB2550-C5A7-43FD-B4B2-07773CEFEDDB} + {AB31727D-D0C9-437A-9354-C58898448CE9} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {6B9535FA-975A-43A7-B0E4-9DF2361CA2D8} + {A9B9B1E8-BC9B-4D25-BD02-7849CB9CBFA2} diff --git a/storage/iscsi/iscsi.sln b/storage/iscsi/iscsi.sln index 09bf338b8..1af681263 100644 --- a/storage/iscsi/iscsi.sln +++ b/storage/iscsi/iscsi.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0 MinimumVisualStudioVersion = 12.0 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "EmptyProject", "src\EmptyProject.vcxproj", "{FD5B8601-22DE-4A00-BAF6-9DD8D0BFA736}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "EmptyProject", "src\EmptyProject.vcxproj", "{455C49DA-CEA2-4534-BD01-3580A803AE6F}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {FD5B8601-22DE-4A00-BAF6-9DD8D0BFA736}.Debug|Win32.ActiveCfg = Debug|Win32 - {FD5B8601-22DE-4A00-BAF6-9DD8D0BFA736}.Debug|Win32.Build.0 = Debug|Win32 - {FD5B8601-22DE-4A00-BAF6-9DD8D0BFA736}.Release|Win32.ActiveCfg = Release|Win32 - {FD5B8601-22DE-4A00-BAF6-9DD8D0BFA736}.Release|Win32.Build.0 = Release|Win32 - {FD5B8601-22DE-4A00-BAF6-9DD8D0BFA736}.Debug|x64.ActiveCfg = Debug|x64 - {FD5B8601-22DE-4A00-BAF6-9DD8D0BFA736}.Debug|x64.Build.0 = Debug|x64 - {FD5B8601-22DE-4A00-BAF6-9DD8D0BFA736}.Release|x64.ActiveCfg = Release|x64 - {FD5B8601-22DE-4A00-BAF6-9DD8D0BFA736}.Release|x64.Build.0 = Release|x64 + {455C49DA-CEA2-4534-BD01-3580A803AE6F}.Debug|Win32.ActiveCfg = Debug|Win32 + {455C49DA-CEA2-4534-BD01-3580A803AE6F}.Debug|Win32.Build.0 = Debug|Win32 + {455C49DA-CEA2-4534-BD01-3580A803AE6F}.Release|Win32.ActiveCfg = Release|Win32 + {455C49DA-CEA2-4534-BD01-3580A803AE6F}.Release|Win32.Build.0 = Release|Win32 + {455C49DA-CEA2-4534-BD01-3580A803AE6F}.Debug|x64.ActiveCfg = Debug|x64 + {455C49DA-CEA2-4534-BD01-3580A803AE6F}.Debug|x64.Build.0 = Debug|x64 + {455C49DA-CEA2-4534-BD01-3580A803AE6F}.Release|x64.ActiveCfg = Release|x64 + {455C49DA-CEA2-4534-BD01-3580A803AE6F}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/storage/iscsi/src/EmptyProject.vcxproj b/storage/iscsi/src/EmptyProject.vcxproj index 1859ceefe..9623b5947 100644 --- a/storage/iscsi/src/EmptyProject.vcxproj +++ b/storage/iscsi/src/EmptyProject.vcxproj @@ -19,12 +19,12 @@ - {FD5B8601-22DE-4A00-BAF6-9DD8D0BFA736} + {455C49DA-CEA2-4534-BD01-3580A803AE6F} $(MSBuildProjectName) false Debug Win32 - {90DC0765-F04B-4C99-9D2F-643FAF30E4ED} + {DBC645C7-90F6-4E12-9D2A-F5B9CF347789} @@ -127,7 +127,6 @@ - diff --git a/storage/iscsi/src/EmptyProject.vcxproj.Filters b/storage/iscsi/src/EmptyProject.vcxproj.Filters index 4418304b6..e26968ac4 100644 --- a/storage/iscsi/src/EmptyProject.vcxproj.Filters +++ b/storage/iscsi/src/EmptyProject.vcxproj.Filters @@ -3,15 +3,15 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {E173FC59-40EE-4F3E-8AD1-F7AAD06B8E6A} + {924AB849-60C2-446A-918F-F4F4F8DECE00} h;hpp;hxx;hm;inl;inc;xsd - {DBEDD0D9-C92D-4107-BC37-22DEEBD50A60} + {67F9C43E-310B-4E5D-9F7E-42661F07ABD0} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {FEC8E2D6-F35E-4CF0-B0F1-E9CD45C55166} + {D255EEDA-7C9B-4D68-8180-B81EE573C108} \ No newline at end of file diff --git a/storage/iscsi/src/iscsihba.mof b/storage/iscsi/src/iscsihba.mof index 71a7d9d60..35307c755 100644 --- a/storage/iscsi/src/iscsihba.mof +++ b/storage/iscsi/src/iscsihba.mof @@ -362,6 +362,21 @@ + + + + + + + + + + + + + + + @@ -4143,6 +4158,21 @@ class ISCSI_TargetMapping + + + + + + + + + + + + + + + @@ -8374,6 +8404,21 @@ class MSiSCSI_RADIUSConfig + + + + + + + + + + + + + + + diff --git a/storage/miniports/lsi_u3/lsi_u3.sln b/storage/miniports/lsi_u3/lsi_u3.sln index 3a65768d7..4a7f30f9d 100644 --- a/storage/miniports/lsi_u3/lsi_u3.sln +++ b/storage/miniports/lsi_u3/lsi_u3.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0 MinimumVisualStudioVersion = 12.0 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lsi_u3", "src\lsi_u3.vcxproj", "{5604AA6B-FB19-4251-9086-76975D9B64A9}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lsi_u3", "src\lsi_u3.vcxproj", "{87B36607-F24D-435F-9BFB-7A6A34281099}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {5604AA6B-FB19-4251-9086-76975D9B64A9}.Debug|Win32.ActiveCfg = Debug|Win32 - {5604AA6B-FB19-4251-9086-76975D9B64A9}.Debug|Win32.Build.0 = Debug|Win32 - {5604AA6B-FB19-4251-9086-76975D9B64A9}.Release|Win32.ActiveCfg = Release|Win32 - {5604AA6B-FB19-4251-9086-76975D9B64A9}.Release|Win32.Build.0 = Release|Win32 - {5604AA6B-FB19-4251-9086-76975D9B64A9}.Debug|x64.ActiveCfg = Debug|x64 - {5604AA6B-FB19-4251-9086-76975D9B64A9}.Debug|x64.Build.0 = Debug|x64 - {5604AA6B-FB19-4251-9086-76975D9B64A9}.Release|x64.ActiveCfg = Release|x64 - {5604AA6B-FB19-4251-9086-76975D9B64A9}.Release|x64.Build.0 = Release|x64 + {87B36607-F24D-435F-9BFB-7A6A34281099}.Debug|Win32.ActiveCfg = Debug|Win32 + {87B36607-F24D-435F-9BFB-7A6A34281099}.Debug|Win32.Build.0 = Debug|Win32 + {87B36607-F24D-435F-9BFB-7A6A34281099}.Release|Win32.ActiveCfg = Release|Win32 + {87B36607-F24D-435F-9BFB-7A6A34281099}.Release|Win32.Build.0 = Release|Win32 + {87B36607-F24D-435F-9BFB-7A6A34281099}.Debug|x64.ActiveCfg = Debug|x64 + {87B36607-F24D-435F-9BFB-7A6A34281099}.Debug|x64.Build.0 = Debug|x64 + {87B36607-F24D-435F-9BFB-7A6A34281099}.Release|x64.ActiveCfg = Release|x64 + {87B36607-F24D-435F-9BFB-7A6A34281099}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/storage/miniports/lsi_u3/src/lsi_u3.vcxproj b/storage/miniports/lsi_u3/src/lsi_u3.vcxproj index 865d16492..60efb2250 100644 --- a/storage/miniports/lsi_u3/src/lsi_u3.vcxproj +++ b/storage/miniports/lsi_u3/src/lsi_u3.vcxproj @@ -19,11 +19,11 @@ - {5604AA6B-FB19-4251-9086-76975D9B64A9} + {87B36607-F24D-435F-9BFB-7A6A34281099} $(MSBuildProjectName) Debug Win32 - {5E93E1AB-7A16-49EE-95FC-15786A9773CE} + {CC0A1E13-737D-44AB-AEA7-090A615626CD} @@ -170,7 +170,6 @@ - diff --git a/storage/miniports/lsi_u3/src/lsi_u3.vcxproj.Filters b/storage/miniports/lsi_u3/src/lsi_u3.vcxproj.Filters index a2cdb4802..caefea6ee 100644 --- a/storage/miniports/lsi_u3/src/lsi_u3.vcxproj.Filters +++ b/storage/miniports/lsi_u3/src/lsi_u3.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {9B1331DA-4E8F-4E3A-8367-CAC8F077EC8B} + {09EE6680-A14D-44ED-821E-84C9D83F8B90} h;hpp;hxx;hm;inl;inc;xsd - {F4B1DC38-0127-43F5-B17A-2602A68C099A} + {D43CC645-EED6-45A1-A624-6B263A67507F} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {7F228B50-E184-486C-A497-F81D562ED33A} + {63C10C98-8018-475C-9CA4-ABE5369AAECC} inf;inv;inx;mof;mc; - {7817713B-6E96-45AD-AD6E-2A26F7D9741E} + {8C8BEEAF-38C4-4D3F-AC07-C116824B0A79} diff --git a/storage/miniports/storahci/src/inbox/storahci.vcxproj b/storage/miniports/storahci/src/inbox/storahci.vcxproj index d01b9e8b6..814b30bb5 100644 --- a/storage/miniports/storahci/src/inbox/storahci.vcxproj +++ b/storage/miniports/storahci/src/inbox/storahci.vcxproj @@ -19,12 +19,12 @@ - {8DC63CF7-4E1A-460D-844C-3E66E9763E83} + {A0F8FE2B-5512-436E-A77F-4A24EAAACA7C} $(MSBuildProjectName) false Debug Win32 - {874F1EA1-366A-414A-BF67-108615454853} + {B2834C28-91D9-4DD3-834C-5B0E01FE209E} @@ -177,7 +177,6 @@ - diff --git a/storage/miniports/storahci/src/inbox/storahci.vcxproj.Filters b/storage/miniports/storahci/src/inbox/storahci.vcxproj.Filters index cadb74aaa..28955cf70 100644 --- a/storage/miniports/storahci/src/inbox/storahci.vcxproj.Filters +++ b/storage/miniports/storahci/src/inbox/storahci.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {20D3C073-33A3-4EA0-AFC3-00B7B63AFE8F} + {34CAC986-8E31-4820-81B7-24D604716F2F} h;hpp;hxx;hm;inl;inc;xsd - {66457EF6-68E0-4E0D-97B0-CDB4D856FF3C} + {731AD279-B426-4E8B-92A3-B85F17830429} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {95130444-F6D4-4084-BBD5-30BF9A6B72E3} + {F7069D72-4B89-4988-B16F-C3B43A6CD3AF} inf;inv;inx;mof;mc; - {41883C82-5F94-4FFF-8959-40226FA13DA4} + {1390D1D1-490F-4A63-903E-AADFFF96098E} diff --git a/storage/miniports/storahci/storahci.sln b/storage/miniports/storahci/storahci.sln index b213872f5..bc97b455f 100644 --- a/storage/miniports/storahci/storahci.sln +++ b/storage/miniports/storahci/storahci.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0 MinimumVisualStudioVersion = 12.0 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "storahci", "src\inbox\storahci.vcxproj", "{8DC63CF7-4E1A-460D-844C-3E66E9763E83}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "storahci", "src\inbox\storahci.vcxproj", "{A0F8FE2B-5512-436E-A77F-4A24EAAACA7C}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {8DC63CF7-4E1A-460D-844C-3E66E9763E83}.Debug|Win32.ActiveCfg = Debug|Win32 - {8DC63CF7-4E1A-460D-844C-3E66E9763E83}.Debug|Win32.Build.0 = Debug|Win32 - {8DC63CF7-4E1A-460D-844C-3E66E9763E83}.Release|Win32.ActiveCfg = Release|Win32 - {8DC63CF7-4E1A-460D-844C-3E66E9763E83}.Release|Win32.Build.0 = Release|Win32 - {8DC63CF7-4E1A-460D-844C-3E66E9763E83}.Debug|x64.ActiveCfg = Debug|x64 - {8DC63CF7-4E1A-460D-844C-3E66E9763E83}.Debug|x64.Build.0 = Debug|x64 - {8DC63CF7-4E1A-460D-844C-3E66E9763E83}.Release|x64.ActiveCfg = Release|x64 - {8DC63CF7-4E1A-460D-844C-3E66E9763E83}.Release|x64.Build.0 = Release|x64 + {A0F8FE2B-5512-436E-A77F-4A24EAAACA7C}.Debug|Win32.ActiveCfg = Debug|Win32 + {A0F8FE2B-5512-436E-A77F-4A24EAAACA7C}.Debug|Win32.Build.0 = Debug|Win32 + {A0F8FE2B-5512-436E-A77F-4A24EAAACA7C}.Release|Win32.ActiveCfg = Release|Win32 + {A0F8FE2B-5512-436E-A77F-4A24EAAACA7C}.Release|Win32.Build.0 = Release|Win32 + {A0F8FE2B-5512-436E-A77F-4A24EAAACA7C}.Debug|x64.ActiveCfg = Debug|x64 + {A0F8FE2B-5512-436E-A77F-4A24EAAACA7C}.Debug|x64.Build.0 = Debug|x64 + {A0F8FE2B-5512-436E-A77F-4A24EAAACA7C}.Release|x64.ActiveCfg = Release|x64 + {A0F8FE2B-5512-436E-A77F-4A24EAAACA7C}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/storage/msdsm/ReadMe.md b/storage/msdsm/ReadMe.md new file mode 100644 index 000000000..6bfdbc06a --- /dev/null +++ b/storage/msdsm/ReadMe.md @@ -0,0 +1,197 @@ +Multipath I/O (MPIO) DSM Sample +=============================== + +The MPIO DSM Sample is intended to serve as an example to follow when building your own vendor specific device specific modules (DSM). This sample DSM supports iSCSI and Fibre Channel devices. + + +Build the sample +---------------- + +You can build the sample in two ways: using Microsoft Visual Studio or the command line (*MSBuild*). + +Building a Driver Using Visual Studio +------------------------------------- + +You build a driver the same way you build any project or solution in Visual Studio. When you create a new driver project using a Windows driver template, the template defines a default (active) project configuration and a default (active) solution build configuration. When you create a project from existing driver sources or convert existing driver code that was built with previous versions of the WDK, the conversion process preserves the target version information (operating systems and platform). + +The default Solution build configuration is Windows 8.1 Debug and Win32. + +### To select a configuration and build a driver or an application + +1. Open the driver project or solution in Visual Studio (find *samplename*.sln or *samplename*.vcxproj). +2. Right-click the solution in the **Solutions Explorer** and select **Configuration Manager**. +3. From the **Configuration Manager**, select the **Active Solution Configuration** (for example, Windows 8.1 Debug or Windows 8.1 Release) and the **Active Solution Platform** (for example, Win32) that correspond to the type of build you are interested in. +4. From the Build menu, click **Build Solution** (Ctrl+Shift+B). + +Building a Driver Using the Command Line (MSBuild) +-------------------------------------------------- + +You can build a driver from the command line using the Visual Studio Command Prompt window and the Microsoft Build Engine (MSBuild.exe) Previous versions of the WDK used the Windows Build utility (Build.exe) and provided separate build environment windows for each of the supported build configurations. You can now use the Visual Studio Command Prompt window for all build configurations. + +### To select a configuration and build a driver or an application + +1. Open a Visual Studio Command Prompt window at the **Start** screen. From this window you can use MsBuild.exe to build any Visual Studio project by specifying the project (.VcxProj) or solutions (.Sln) file. +2. Navigate to the project directory and enter the **MSbuild** command for your target. For example, to perform a clean build of a Visual Studio driver project called *filtername*.vcxproj, navigate to the project directory and enter the following MSBuild command: **msbuild /t:clean /t:build .\\***samplename***.vcxproj**. + +Installation and Operation +-------------------------- + +The installation process depends on proper construction of your DSM's INF as well as an installation program provided by you. These are important aspects of complying with the Designed for Windows logo program. The installation was designed to allow for multiple vendors to easily add DSMs and to eliminate rebooting as much as possible. The installation process will require you to update your installation routines and to use the new .INF files. With the new process, you can only modify your DSM's INF file. + +The installer sample only needs to be called one time with the INF/driver source path, the name of the DSM INF, and the DSM hardware ID. Typically this would be called from an MSI or setup package, such as one created by InstallShield or other installer technology. + +The following annotated DSM INF file illustrates the correct format for your DSM. Replace only those items that are in bold italics. Remember, you must not use "GENDSM" or "MSISCDSM" or "MSDSM" as the name of your DSM. Therefore, you must replace any instances of those strings with the proper name of your DSM. + +; +; Copyright (c) . All rights reserved. +; + + +In the Version section, make sure the DriverVer is correct for your DSM. Ideally it should match the version in the RC file. You must specify a different catalog file since the MPIO core drivers now come pre-signed: + +[Version] +Signature = "$WINDOWS NT$" +Class = System +ClassGuid = {4D36E97D-E325-11CE-BFC1-08002BE10318} +Provider = %VNDR% +CatalogFile = mydsm.cat +DriverVer = MM/DD/YYYY,x.x.xxxx + +[DestinationDirs] +DefaultDestDir = 12 + +; +; Multi-Path Device-Specific Module +; + +[Manufacturer] +%std_mfg% = std_mfg + +Substitute all instances of "gendsm" with the proper name for your DSM. For example, "mydsm": + + +[std_mfg] +%mydsm_devicedesc% = mydsm_install, Root\MYDSM + +[mydsm_install] +copyfiles = @mydsm.sys + +[mydsm_install.Services] +AddService = mydsm, %SPSVCINST_ASSOCSERVICE%, mydsm_service + +[mydsm_service] +DisplayName = %mydsm_desc% +ServiceType = %SERVICE_KERNEL_DRIVER% +StartType = %SERVICE_BOOT_START% +ErrorControl = %SERVICE_ERROR_NORMAL% +ServiceBinary = %12%\mydsm.sys +LoadOrderGroup = "System Bus Extender" +AddReg = mydsm_addreg + + +This next section contains the Hardware ID strings for your devices. You can have more than one. Sample format: "VENDOR PRODUCT " - remember to use spaces in a field (vendor, product ID) to pad this to be eight characters for the vendor name (as registered with STA) and sixteen for the product ID (unless the supported devices share a common prefix, in which case the product ID can be less than 16 characters). + +**Note**   Underscores that are part of the inquiry string (applies to vendor ID as well as product ID fields) must NOT be replaced with spaces. + +In this sample, there are two different strings: + +; +; The following cannot be grouped (as above) +; + +HKLM, "SYSTEM\CurrentControlSet\Control\MPDEV", "MPIOSupportedDeviceList", %REG_MULTI_SZ_APPEND%, "VENDOR1 PRODUCT1 " +HKLM, "SYSTEM\CurrentControlSet\Control\MPDEV", "MPIOSupportedDeviceList", %REG_MULTI_SZ_APPEND%, "VENDOR2 PRODUCT2 " + +These are valid samples: + + +HKLM, "SYSTEM\CurrentControlSet\Control\MPDEV", "MPIOSupportedDeviceList", %REG_MULTI_SZ_APPEND%, "MAXTOR ATLASU320_18_WLS" + +HKLM, "SYSTEM\CurrentControlSet\Control\MPDEV", "MPIOSupportedDeviceList", %REG_MULTI_SZ_APPEND%, "VENDOR3 PROD_PREFIX" + + +(to replace "VENDOR3 PROD\_PREFIX\_A ", "VENDOR3 PROD\_PREFIX\_B " and "VENDOR3 PROD\_PREFIX\_C ") + +In the above example, it is assumed that this sample DSM will be used to support three devices from vendor "VENDOR3" with product IDs "PROD\_PREFIX\_A", "PROD\_PREFIX\_B" and "PROD\_PREFIX\_C" respectively. Since all the three devices share the common product ID sub-string "PROD\_PREFIX", we can replace separate entries (in MPIOSupportedDeviceList) for each one of them with just one entry that uses the product ID sub-string that is common to them, without padding it with spaces to make it 16 characters. + +It is advisable to use this format if your storage devices generate product IDs on-the-fly using a known product ID prefix. This can significantly reduce the size of your INF file and makes future changes to the INF file less prone to human error. Large INF files can result in very long device installation times and will fill the registry with unnecessary information. Please make sure you take advantage of this new capability as it will improve your customers' experience with MPIO. + +Add one entry for each WMI GUID that you use in your DSM. This is required: + + +HKLM, "SYSTEM\CurrentControlSet\Control\WMI\Security", "04517f7e-92bb-4ebe-aed0-54339fa5f544",\%REG_BINARY_NOCLOBBER%,\ + 01,00,04,80,14,00,00,00,24,00,00,00,00,00,00,00,\ + 34,00,00,00,01,02,00,00,00,00,00,05,20,00,00,00,\ + 20,02,00,00,01,02,00,00,00,00,00,05,20,00,00,00,\ + 20,02,00,00,02,00,20,00,01,00,00,00,00,00,18,00,\ + 1f,00,12,00,01,02,00,00,00,00,00,05,20,00,00,00,\ + 20,02,00,00 +HKLM, "SYSTEM\CurrentControlSet\Control\WMI\Security", "d13373f6-0114-4fe3-b91b-f52c95dfc417",\%REG_BINARY_NOCLOBBER%,\ + 01,00,04,80,14,00,00,00,24,00,00,00,00,00,00,00,\ + 34,00,00,00,01,02,00,00,00,00,00,05,20,00,00,00,\ + 20,02,00,00,01,02,00,00,00,00,00,05,20,00,00,00,\ + 20,02,00,00,02,00,48,00,03,00,00,00,00,00,18,00,\ + ff,0f,12,00,01,02,00,00,00,00,00,05,20,00,00,00,\ + 20,02,00,00,00,00,14,00,0d,00,12,00,01,01,00,00,\ + 00,00,00,01,00,00,00,00,00,00,14,00,ff,07,12,00,\ + 01,01,00,00,00,00,00,05,12,00,00,00 +HKLM, "SYSTEM\CurrentControlSet\Control\WMI\Security", "d6dc1bf0-95fa-4246-afd7-40a030458f48",\%REG_BINARY_NOCLOBBER%,\ + 01,00,04,80,14,00,00,00,24,00,00,00,00,00,00,00,\ + 34,00,00,00,01,02,00,00,00,00,00,05,20,00,00,00,\ + 20,02,00,00,01,02,00,00,00,00,00,05,20,00,00,00,\ + 20,02,00,00,02,00,48,00,03,00,00,00,00,00,18,00,\ + ff,0f,12,00,01,02,00,00,00,00,00,05,20,00,00,00,\ + 20,02,00,00,00,00,14,00,09,00,12,00,01,01,00,00,\ + 00,00,00,01,00,00,00,00,00,00,14,00,09,00,12,00,\ + 01,01,00,00,00,00,00,05,12,00,00,00 + +; +; Localizable Strings +; + + +Finally, modify the following strings: + +[Strings] +VNDR = "Your Company Name Here" +std_mfg = "(Standard system devices)" +mydsm_devicedesc = " Multi-Path Device Specific Module" + +The following string is displayed as the friendly name of your DSM: + +mydsm_desc = " Multi-Path DSM" + +; +; Handy macro substitutions (non-localizable) +; + +SERVICE_KERNEL_DRIVER = 1 + +SERVICE_BOOT_START = 0 +SERVICE_SYSTEM_START = 1 +SERVICE_DEMAND_START = 3 + +SERVICE_ERROR_IGNORE = 0 +SERVICE_ERROR_NORMAL = 1 +SERVICE_ERROR_CRITICAL = 3 + +SPSVCINST_ASSOCSERVICE = 2 + +REG_MULTI_SZ = 0x00010000 +REG_MULTI_SZ_APPEND = 0x00010008 +REG_EXPAND_SZ = 0x00020000 +REG_DWORD = 0x00010001 +REG_BINARY_NOCLOBBER = 0x00030003 + +You should be aware of the following when you install the MPIO DSM sample: + +1. The install sample assumes that all necessary files have already been copied over to a vendor specific directory (preferably a folder under Program Files) and takes that path as one of the parameters. This eliminates requests for the original media when new devices appear. + +2. As the port filter needs to go on top of every adapter that hosts (or might host) a path to the disk, all SCSI adapters are restarted at the end of the install + + It is expected that the adapter that hosts the system volumes (boot/paging) will not restart, but that should not be problem if you are not multipathing the boot volume. However, if you are multipathing the boot volume, you will need to restart the system. + +**Note**  Other filter drivers installed as port filters may interfere with the proper operation of the MPIO port filter. Microsoft does not recommend the use of such filter drivers which may be supplied by HBA miniport vendors. + +**Note**  Since your DSM binary is not signed, you will get Unsigned Driver Pop-Ups. Ignore these and accept the installation of the new driver. Once your package has been successfully qualified by WHQL, your binaries will get signed and your customers will not get unsigned driver popups. + diff --git a/storage/msdsm/msdsm.sln b/storage/msdsm/msdsm.sln new file mode 100644 index 000000000..c70a97eaf --- /dev/null +++ b/storage/msdsm/msdsm.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}") = "SampleDSM", "src\SampleDSM.vcxproj", "{D093D3C3-53D2-4E2A-A634-CFBE482CC29D}" +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 + {D093D3C3-53D2-4E2A-A634-CFBE482CC29D}.Debug|Win32.ActiveCfg = Debug|Win32 + {D093D3C3-53D2-4E2A-A634-CFBE482CC29D}.Debug|Win32.Build.0 = Debug|Win32 + {D093D3C3-53D2-4E2A-A634-CFBE482CC29D}.Release|Win32.ActiveCfg = Release|Win32 + {D093D3C3-53D2-4E2A-A634-CFBE482CC29D}.Release|Win32.Build.0 = Release|Win32 + {D093D3C3-53D2-4E2A-A634-CFBE482CC29D}.Debug|x64.ActiveCfg = Debug|x64 + {D093D3C3-53D2-4E2A-A634-CFBE482CC29D}.Debug|x64.Build.0 = Debug|x64 + {D093D3C3-53D2-4E2A-A634-CFBE482CC29D}.Release|x64.ActiveCfg = Release|x64 + {D093D3C3-53D2-4E2A-A634-CFBE482CC29D}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/storage/msdsm/src/SampleDSM.inf b/storage/msdsm/src/SampleDSM.inf new file mode 100644 index 0000000000000000000000000000000000000000..f632f6e50ca7f0ff15b973d45dd536803947494c GIT binary patch literal 5354 zcmdUzYfl?T6o${|O8pNjR7;5jh)bJNMft$RNl?K?wv)CA6*3qIDi~s$q-oW^-u8Lt zc$nF>7oxUbD$BdGGjqzFpakUD_#6`*zD{Vi&e#2dqr& z9kaXE=DNd<53HPUUxMz2`w7oyVNSHyHVE|M^oadk<{sajw9WdBeXyH404 zTJasRW6b|7tZDxU3SA}11*4%Yv%9cOq`c&wc#Xr;DfFkze&9M_uWPBR{%`GdSRGG! z{TybyL7rha;}%*;uirEN3-*=$fc`DCX*23F*Rme73xReUDf*nL)AOHbyXZPb?o;fm z)6?D;Sm^@!(mB7|*PO6!PpoO**|&_B7_HlyeZy##-!{)nwq?&5tuk7%4gM?a*kWas z(K^=+`y-rnfF9SuTZ5o+ludFq@N9Xti1O;ce`St7&A8(?|f$iY~=al48yvYBuYr^ji z&m-nt7VVLZJnABa?{N#P;^FAMbXVk#GkggthuBA66!vvOlBA0lm@Q`2Jo-xNLCke4S zZ9Zm?z>nvnwD^lEY4L*E^A+(k?6i9D4pM#N{8pZG?3$~?EGaW8B1 zuwjR*b8Es$Idv3JHH534RH5p*iMKLI2l-mzz7Nm6JNa^ZZ-t1Iyq_|T$_g{pRo zu_iz5AxR(1w4wHD@FGZeixpkd1@uX^s-U9WhRA7xT-7h-UrFON&k@*R=6tGO^hDHk z){jD*aI5Ni{y-e$I~_YO@n$C=Q$2~g0o4deraULa?r^_C3+dz?gH{L3r=DdmWWGt* zub5QzT$x6u>ZtfEgbqRa%9jQ8)N7^s8r88}(*oKjQ=7=nsi*4E-3Xs&0RyU`o>65l zRmvg1>%i?e>^tWyWoV&VUBM&vD0B7gn0e*%$9KJqdZj-3Tl~k+c)y}PO*8LUlb1h+ zyJmKSZ3_JBH9)pfUseQb6nNu0U`w)17)wIM=mqxl6Ou(;BiZFqpow-@g?x`Js%(-~ zlDk~OovFXvXy z=yhku(^6#pGwkOmOfi*EDg<4D9)D{P$`y~o|HEi4h&9FDkll4Lc01$0{OzOwLms_1 z!OG*nO}$!y6lWzVw3fUs4V0_uV=3t<-00oP_oiqSwepmrdRBgqSs5DMj=GQns;nn|DS<-GqCU((C#|?_{x8nB`YT>3nCsU#_B& zZ@y2NdES=@p(cKVP!7EB8-$-O>&E=i!j?nypKnvugO^xX5v6K$#n`_|cpW?r`S$_~ z-tzaa5&o9t{B=^fo4&6LjlI(9T{l#vo!4z9%gML?Mp32fTB?zGrI z*428KuX=T#R69%8ypB`3Yls}@bJ_@U)pN@8QXQxG)ktWKoSOC{IU#xe&D3v^hu_OG ztxfyqvUWRsBhwqr&RhU3n*-%d~}0TgcP_&HjG0P0nreZldp9of5L5#n-2t HlJEZpK+h+q literal 0 HcmV?d00001 diff --git a/storage/msdsm/src/SampleDSM.vcxproj b/storage/msdsm/src/SampleDSM.vcxproj new file mode 100644 index 000000000..c4870a687 --- /dev/null +++ b/storage/msdsm/src/SampleDSM.vcxproj @@ -0,0 +1,301 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {D093D3C3-53D2-4E2A-A634-CFBE482CC29D} + $(MSBuildProjectName) + false + Debug + Win32 + {0B2AAAFE-CC5D-4581-92BE-2CFF7879B4E8} + + + + 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 + TracePrint((LEVEL,FLAGS,MSG,...)) + ;%(AdditionalIncludeDirectories) + precomp.h + Use + $(IntDir)\precomp.pch + + + true + true + true + true + TracePrint((LEVEL,FLAGS,MSG,...)) + ;%(AdditionalIncludeDirectories) + precomp.h + Use + $(IntDir)\precomp.pch + + + true + true + true + true + TracePrint((LEVEL,FLAGS,MSG,...)) + ;%(AdditionalIncludeDirectories) + precomp.h + Use + $(IntDir)\precomp.pch + + + true + true + true + true + TracePrint((LEVEL,FLAGS,MSG,...)) + ;%(AdditionalIncludeDirectories) + precomp.h + Use + $(IntDir)\precomp.pch + + + true + true + true + true + TracePrint((LEVEL,FLAGS,MSG,...)) + + + .\$(IntDir)\msdsmwmi.h + .\$(IntDir)\msdsm.vbs + + + .\$(IntDir)\msdsmdsm.h + .\$(IntDir)\msdsmdsm.vbs + + + + + true + Level4 + + + + + true + Level4 + + + + + true + Level4 + + + + + true + Level4 + + + + SampleDSM + + + SampleDSM + + + SampleDSM + + + SampleDSM + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\mpio.lib;$(DDK_LIB_PATH)\Rtlver.lib + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\mpio.lib;$(DDK_LIB_PATH)\Rtlver.lib + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\mpio.lib;$(DDK_LIB_PATH)\Rtlver.lib + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\mpio.lib;$(DDK_LIB_PATH)\Rtlver.lib + + + + + %(AdditionalIncludeDirectories);..\inc + %(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE + + + %(AdditionalIncludeDirectories);..\inc + %(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE + + + + + %(AdditionalIncludeDirectories);..\inc + %(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE + + + + + %(AdditionalIncludeDirectories);..\inc + %(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE + + + %(AdditionalIncludeDirectories);..\inc + %(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE + + + + + %(AdditionalIncludeDirectories);..\inc + %(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE + + + + + %(AdditionalIncludeDirectories);..\inc + %(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE + + + %(AdditionalIncludeDirectories);..\inc + %(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE + + + + + %(AdditionalIncludeDirectories);..\inc + %(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE + + + + + %(AdditionalIncludeDirectories);..\inc + %(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE + + + %(AdditionalIncludeDirectories);..\inc + %(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE + + + + + %(AdditionalIncludeDirectories);..\inc + %(PreprocessorDefinitions);_WIN2K_COMPAT_SLIST_USAGE + + + + + ;%(AdditionalIncludeDirectories) + precomp.h + Create + $(IntDir)\precomp.pch + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/storage/msdsm/src/SampleDSM.vcxproj.Filters b/storage/msdsm/src/SampleDSM.vcxproj.Filters new file mode 100644 index 000000000..ab3c118ae --- /dev/null +++ b/storage/msdsm/src/SampleDSM.vcxproj.Filters @@ -0,0 +1,51 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {5467502E-BE53-48E3-9B4D-2727B895913F} + + + h;hpp;hxx;hm;inl;inc;xsd + {E67880DC-0E2B-4629-9BC4-6DE655C52134} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {77134D09-E871-4C7B-8EBB-BC35E319ABF8} + + + inf;inv;inx;mof;mc; + {029DDD29-646E-4A6D-AF49-A8F30315A59D} + + + + + 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/storage/msdsm/src/dsmmain.c b/storage/msdsm/src/dsmmain.c new file mode 100644 index 000000000..1b4140813 --- /dev/null +++ b/storage/msdsm/src/dsmmain.c @@ -0,0 +1,9752 @@ +/*++ + +Copyright (C) 2004-2010 Microsoft Corporation + +Module Name: + + dsmmain.c + +Abstract: + + This driver is the Microsoft Device Specific Module (DSM). + It exports behaviours that mpio.sys will use to determine how to + multipath SPC-3 conforming devices. + + This file contains routines that are internal to MSDSM. + +Environment: + + kernel mode only + +Notes: + +--*/ + +#include "precomp.h" + +#ifdef DEBUG_USE_WPP +#include "dsmmain.tmh" +#endif + +#pragma warning (disable:4305) + +extern BOOLEAN DoAssert; + +#ifdef ALLOC_PRAGMA + #pragma alloc_text(PAGE, DsmpRegisterPersistentReservationKeys) +#endif + +VOID +DsmpFreeDSMResources( + _In_ IN PDSM_CONTEXT DsmContext + ) +/*++ + +Routine Description: + + This routine will free the resources allocated by the DSM. This routine + should be called when the DSM is being unloaded. + +Arguements: + + DsmContext - DSM context given to MPIO during initialization + +Return Value: + + None +--*/ +{ + PDSM_WMILIB_CONTEXT wmiInfo; + PVOID tempAddress = (PVOID)DsmContext; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_INIT, + "DsmpFreeDSMResources (DsmCtxt %p): Entering function.\n", + DsmContext)); + + // + // First free the buffer allocated for storing the registry path. + // + wmiInfo = &gDsmInitData.DsmWmiInfo; + + if (wmiInfo->RegistryPath.Buffer) { + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_INIT, + "DsmpFreeDSMResources (DsmCtxt %p): Freeing wmiInfo's registry buffer.\n", + DsmContext)); + + DsmpFreePool(wmiInfo->RegistryPath.Buffer); + } + + if (DsmContext) { + PLIST_ENTRY entry; + PDSM_DEVICE_INFO deviceInfo; + PDSM_GROUP_ENTRY groupEntry; + PDSM_FAILOVER_GROUP failGroup; + PDSM_CONTROLLER_LIST_ENTRY controllerEntry; + + ExDeleteNPagedLookasideList(&(DsmContext->CompletionContextList)); + + // + // Free up the devices (DeviceInfo) list. + // + while (!IsListEmpty(&DsmContext->DeviceList)) { + + entry = DsmContext->DeviceList.Flink; + + NT_ASSERT(entry); + + deviceInfo = CONTAINING_RECORD(entry, DSM_DEVICE_INFO, ListEntry); + + if (deviceInfo) { + + DsmpRemoveDeviceFailGroup(DsmContext, deviceInfo->FailGroup, deviceInfo, TRUE); + DsmpRemoveDeviceEntry(DsmContext, deviceInfo->Group, deviceInfo); + } + } + + NT_ASSERT(!DsmContext->NumberDevices && + !DsmContext->NumberFOGroups && + !DsmContext->NumberGroups); + + // + // By now, there should be no group entries left but play it safe and + // free up the GROUP list. + // + while (!IsListEmpty(&DsmContext->GroupList)) { + + entry = DsmContext->GroupList.Flink; + + NT_ASSERT(entry); + + groupEntry = CONTAINING_RECORD(entry, DSM_GROUP_ENTRY, ListEntry); + + if (groupEntry) { + + DsmpRemoveGroupEntry(DsmContext, groupEntry, TRUE); + + DsmpFreePool(groupEntry); + } + } + + // + // By now there should be no FOG entries left but we play it safe and + // free up the FOG list. + // + while (!IsListEmpty(&DsmContext->FailGroupList)) { + + entry = RemoveHeadList(&DsmContext->FailGroupList); + + if (entry) { + + failGroup = CONTAINING_RECORD(entry, DSM_FAILOVER_GROUP, ListEntry); + + if (failGroup) { + + PDSM_FOG_DEVICELIST_ENTRY fogDeviceListEntry = NULL; + PLIST_ENTRY deviceEntry = NULL; + + while (!IsListEmpty(&failGroup->FOG_DeviceList)) { + + deviceEntry = RemoveHeadList(&failGroup->FOG_DeviceList); + + if (deviceEntry) { + + fogDeviceListEntry = CONTAINING_RECORD(deviceEntry, DSM_FOG_DEVICELIST_ENTRY, ListEntry); + + if (!fogDeviceListEntry) { + continue; + } + + (fogDeviceListEntry->DeviceInfo)->FailGroup = NULL; + + DsmpFreePool(fogDeviceListEntry); + InterlockedDecrement((LONG volatile*)&failGroup->Count); + } + } + + DsmpFreeZombieGroupList(failGroup); + DsmpFreePool(failGroup); + InterlockedDecrement((LONG volatile*)&DsmContext->NumberFOGroups); + } + } + } + + // + // Free up the controller list. + // + while (!IsListEmpty(&DsmContext->ControllerList)) { + + entry = RemoveHeadList(&DsmContext->ControllerList); + + if (entry) { + + controllerEntry = CONTAINING_RECORD(entry, DSM_CONTROLLER_LIST_ENTRY, ListEntry); + + if (controllerEntry) { + + DsmpFreeControllerEntry(DsmContext, controllerEntry); + + InterlockedDecrement((LONG volatile*)&DsmContext->NumberControllers); + } + } + } + + NT_ASSERT(!DsmContext->NumberControllers); + + // + // Free up the stale FOG list. + // + while (!IsListEmpty(&DsmContext->StaleFailGroupList)) { + + entry = RemoveHeadList(&DsmContext->StaleFailGroupList); + + if (entry) { + + failGroup = CONTAINING_RECORD(entry, DSM_FAILOVER_GROUP, ListEntry); + + if (failGroup) { + + InterlockedDecrement((LONG volatile*)&DsmContext->NumberStaleFOGroups); + NT_ASSERT(IsListEmpty(&failGroup->FOG_DeviceList)); + DsmpFreeZombieGroupList(failGroup); + DsmpFreePool(failGroup); + } + } + } + + // + // Free up the supported devices list buffer. + // + DsmpFreePool(DsmContext->SupportedDevices.Buffer); + + // + // It's the responsibility of the mpio bus driver to have already + // destroyed all devices and paths. As those functions free allocations + // for the objects, the only thing needed here is to free the DsmContext. + // + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_INIT, + "DsmpFreeDSMResources (DsmCtxt %p): Freeing the DsmContext.\n", + DsmContext)); + + DsmpFreePool(DsmContext); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_INIT, + "DsmpFreeDSMResources (DsmCtxt %p): Exiting function.\n", + tempAddress)); + + return; +} + + +PDSM_GROUP_ENTRY +DsmpFindDevice( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_DEVICE_INFO DeviceInfo, + _In_ IN BOOLEAN AcquireDSMLockExclusive + ) +/*++ + +Routine Description: + + This routine searches for a serial number match between DeviceInfo and + the rest of the devices currently being driven by this DSM. + +Arguments: + + DsmContext - DSM context given to MPIO during initialization + DeviceInfo - The deviceInfo containing serial number for which to search. + AcquireDSMLockExclusive - If TRUE this routine should acquire DsmContextLock Exclusively + +Return Value: + + The multi-path group entry in which the device resides. + +--*/ +{ + PDSM_DEVICE_INFO deviceInfo; + PLIST_ENTRY entry; + PDSM_GROUP_ENTRY groupEntry = NULL; + ULONG i; + KIRQL irql = PASSIVE_LEVEL; // Initialize variable to prevent C4701 error + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpFindDevice (DevInfo %p): Entering function.\n", + DeviceInfo)); + + if (AcquireDSMLockExclusive) { + irql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); + } + + // + // Run through the DeviceInfo List + // + entry = DsmContext->DeviceList.Flink; + for (i = 0; i < DsmContext->NumberDevices; i++, entry = entry->Flink) { + + // + // Extract the deviceInfo structure. + // + deviceInfo = CONTAINING_RECORD(entry, DSM_DEVICE_INFO, ListEntry); + DSM_ASSERT(deviceInfo); + + if (deviceInfo) { + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpFindDevice (DevInfo %p): Comparing with %p.\n", + DeviceInfo, + deviceInfo)); + + // + // Call the Serial Number compare routine. + // + if (DsmCompareDevices(DsmContext, + DeviceInfo, + deviceInfo)) { + + groupEntry = deviceInfo->Group; + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_PNP, + "DsmpFindDevice (DevInfo %p): Found matching multi-path group %p.\n", + DeviceInfo, + groupEntry)); + + break; + } + } + } + + if (AcquireDSMLockExclusive) { + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpFindDevice (DevInfo %p): Exiting function with groupEntry %p.\n", + DeviceInfo, + groupEntry)); + + return groupEntry; +} + + +PDSM_GROUP_ENTRY +DsmpBuildGroupEntry( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_DEVICE_INFO DeviceInfo + ) +/*++ + +Routine Description: + + This will allocate and partially initialise a multi-path group entry. + + N.B: This routine must be called with DsmContextLock held in Exclusive mode. + +Arguments: + + DsmContext - DSM context given to MPIO during initialization + DeviceInfo - The first device to be added to the group. + +Return Value: + + The new group entry. + +--*/ +{ + PDSM_GROUP_ENTRY group; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpBuildGroupEntry (DevInfo %p): Entering function.\n", + DeviceInfo)); + + // + // Allocate the memory for the multi-path group. + // + group = DsmpAllocatePool(NonPagedPoolNx, + sizeof(DSM_GROUP_ENTRY), + DSM_TAG_GROUP_ENTRY); + + if (group) { + + InitializeListHead(&group->FailingDevInfoList); + group->GroupNumber = InterlockedIncrement((LONG volatile*)&DsmContext->NumberGroups); + group->GroupSig = DSM_GROUP_SIG; + group->State = DSM_GP_NORMAL; + + // + // Add it to the list of multi-path groups. + // + InsertTailList(&DsmContext->GroupList, &group->ListEntry); + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpBuildGroupEntry (DevInfo %p): Failed to allocate memory for the group.\n", + DeviceInfo)); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpBuildGroupEntry (DevInfo %p): Exiting function with group %p.\n", + DeviceInfo, + group)); + + return group; +} + + +NTSTATUS +DsmpParseTargetPortGroupsInformation( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_GROUP_ENTRY Group, + _In_reads_bytes_(TargetPortGroupsInfoLength) IN PUCHAR TargetPortGroupsInfo, + _In_ IN ULONG TargetPortGroupsInfoLength + ) +/*++ + +Routine Description: + + This will parse the information returned back from a previously + made call to ReportTargetPortGroups and build new TPG entries or + update old ones. + + N.B: This routine must be called with DsmContextLock held in Exclusive mode. + +Arguments: + + DsmContext - DsmContext + Group - group entry + TargetPortGroupsInfo - Pointer to the ReportTPG returned buffer. + TargetPortGroupsInfoLength - length of the buffer. + +Return Value: + + STATUS_SUCCESS or appropriate error code. + +--*/ +{ + PUCHAR targetPortGroupsInfoIndex; + ULONG bytes = SPC3_TARGET_PORT_GROUPS_HEADER_SIZE; + PDSM_TARGET_PORT_GROUP_ENTRY targetPortGroupEntry = NULL; + ULONG descriptorSize = 0; + NTSTATUS status = STATUS_SUCCESS; + ULONG index; + DSM_DEVICE_STATE tpgState = DSM_DEV_NOT_USED_STATE; + ULONG bytesLeft; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpParseTargetPortGroupsInformation (Group %p): Entering function.\n", + Group)); + + targetPortGroupsInfoIndex = TargetPortGroupsInfo + bytes; + bytesLeft = TargetPortGroupsInfoLength - bytes; + + while (bytes < TargetPortGroupsInfoLength && NT_SUCCESS(status)) { + + targetPortGroupEntry = DsmpFindTargetPortGroupEntry(DsmContext, + Group, + targetPortGroupsInfoIndex, + bytesLeft); + + if (targetPortGroupEntry) { + + targetPortGroupEntry = DsmpUpdateTargetPortGroupEntry(DsmContext, + targetPortGroupEntry, + targetPortGroupsInfoIndex, + bytesLeft, + &descriptorSize); + } else { + + targetPortGroupEntry = DsmpBuildTargetPortGroupEntry(DsmContext, + Group, + targetPortGroupsInfoIndex, + bytesLeft, + &descriptorSize); + + if (targetPortGroupEntry) { + + // + // Insert this TPG entry into array + // + for (index = 0; index < DSM_MAX_PATHS; index++) { + + if (!Group->TargetPortGroupList[index]) { + + Group->TargetPortGroupList[index] = targetPortGroupEntry; + InterlockedIncrement((LONG volatile*)&Group->NumberTargetPortGroups); + targetPortGroupEntry->Group = Group; + break; + } + } + + if (index == DSM_MAX_PATHS) { + + NT_ASSERT(index < DSM_MAX_PATHS); + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_GENERAL, + "DsmpParseTargetPortGroupsInformation (Group %p): Number of paths exceeded max supported.\n", + Group)); + + status = STATUS_UNSUCCESSFUL; + goto __Exit_DsmpParseTargetPortGroupsInformation; + } + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_GENERAL, + "DsmpParseTargetPortGroupsInformation (Group %p): Insufficient resources to build TPG.\n", + Group)); + + status = STATUS_INSUFFICIENT_RESOURCES; + } + } + + if (NT_SUCCESS(status)) { + + // + // If this is the first TPG being parsed, save off its AA state. + // + if (tpgState == DSM_DEV_NOT_USED_STATE) { + + tpgState = targetPortGroupEntry->AsymmetricAccessState; + + } else { + + // + // Check if this TPG's AA state differs from the previous one's. + // Symmetric LU access means that TPG access states must be the + // same for TPGs. If this one is different, we know that the + // device supports Asymmetric LU access. + // + if (tpgState != targetPortGroupEntry->AsymmetricAccessState) { + + Group->Symmetric = FALSE; + } + } + } + + if (targetPortGroupEntry) { + + // + // Set the flag to indicate that we've encountered this TPG in the RTPG information. + // + targetPortGroupEntry->Traversed = TRUE; + } + + bytes += descriptorSize; + targetPortGroupsInfoIndex += descriptorSize; + bytesLeft -= descriptorSize; + } + + // + // Since we've gone through the entire information reported by back RTPG, it + // is now time to delete the stale entries. + // + for (index = 0; index < DSM_MAX_PATHS; index++) { + + targetPortGroupEntry = Group->TargetPortGroupList[index]; + + if (targetPortGroupEntry) { + + if (targetPortGroupEntry->Traversed) { + + // + // Entry needs to continue to exist. Reset the flag and continue. + // + targetPortGroupEntry->Traversed = FALSE; + continue; + + } else { + + PLIST_ENTRY entry; + PLIST_ENTRY tempEntry; + PDSM_TARGET_PORT_LIST_ENTRY targetPort; + + // + // For this target port group, clean up all its target ports if + // the port doesn't expose any instance of this device. + // + for (entry = targetPortGroupEntry->TargetPortList.Flink; + entry != NULL && entry != &targetPortGroupEntry->TargetPortList; + entry = entry->Flink) { + + targetPort = CONTAINING_RECORD(entry, DSM_TARGET_PORT_LIST_ENTRY, ListEntry); + + if (targetPort) { + + // + // If the TP doesn't expose this device, it is safe + // to delete it. + // + if (IsListEmpty(&targetPort->TP_DeviceList)) { + + tempEntry = entry; + entry = entry->Blink; + + RemoveEntryList(tempEntry); + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpParseTargetPortGroupsInformation (Group %p): Deleting empty target port %p from TPG %p list.\n", + Group, + targetPort, + targetPortGroupEntry)); + + DsmpFreePool(targetPort); + + InterlockedDecrement((LONG volatile*)&targetPortGroupEntry->NumberTargetPorts); + } + } + } + + // + // If the TPG doesn't have any TPs, it is safe to delete it. + // + if (!targetPortGroupEntry->NumberTargetPorts) { + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpParseTargetPortGroupsInformation (Group %p): Deleting target port group %p.\n", + Group, + targetPortGroupEntry)); + + DsmpFreePool(targetPortGroupEntry); + + InterlockedDecrement((LONG volatile*)&Group->NumberTargetPortGroups); + + Group->TargetPortGroupList[index] = NULL; + } + } + } + } + +__Exit_DsmpParseTargetPortGroupsInformation: + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpParseTargetPortGroupsInformation (Group %p): Exiting function with status %x\n", + Group, + status)); + + return status; +} + + +PDSM_TARGET_PORT_GROUP_ENTRY +DsmpFindTargetPortGroupEntry( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_GROUP_ENTRY Group, + _In_reads_bytes_(TPGs_BufferLength) IN PUCHAR TargetPortGroupsDescriptor, + _In_ IN ULONG TPGs_BufferLength + ) +/*++ + +Routine Description: + + This will search the group's TPG array to look for an identifier match. + + N.B: This routine must be called with DsmContextLock held in either Shared + or Exclusive mode. + +Arguments: + + DsmContext - DsmContext + Group - group entry + TargetPortGroupsDescriptor - Pointer to the TPG descriptor. + TPGs_BufferLength - Length of the passed in TargetPortGroupsDescriptor buffer. + +Return Value: + + Pointer to the array element that matches, else NULL. + +--*/ +{ + PDSM_TARGET_PORT_GROUP_ENTRY targetPortGroup = NULL; + PSPC3_REPORT_TARGET_PORT_GROUP_DESCRIPTOR descriptor = (PSPC3_REPORT_TARGET_PORT_GROUP_DESCRIPTOR)TargetPortGroupsDescriptor; + ULONG index; + BOOLEAN found = FALSE; + USHORT identifier = ((descriptor->TPG_Identifier & 0x00FF) << 8) | ((descriptor->TPG_Identifier & 0xFF00) >> 8); + + UNREFERENCED_PARAMETER(TPGs_BufferLength); + UNREFERENCED_PARAMETER(DsmContext); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpFindTargetPortGroupEntry (Group %p): Entering function.\n", + Group)); + + for (index = 0; index < DSM_MAX_PATHS && !found; index++) { + + targetPortGroup = Group->TargetPortGroupList[index]; + + if (targetPortGroup) { + + if (targetPortGroup->Identifier == identifier) { + + found = TRUE; + } + } + } + + if (!found) { + targetPortGroup = NULL; + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpFindTargetPortGroupEntry (Group %p): Exiting function with targetPortGroup %p.\n", + Group, + targetPortGroup)); + + return targetPortGroup; +} + +_Success_(return!=0) +PDSM_TARGET_PORT_GROUP_ENTRY +DsmpUpdateTargetPortGroupEntry( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_TARGET_PORT_GROUP_ENTRY TargetPortGroup, + _In_reads_bytes_(TPGs_BufferLength) IN PUCHAR TargetPortGroupsDescriptor, + _In_ IN ULONG TPGs_BufferLength, + _Out_ OUT PULONG DescriptorSize + ) +/*++ + +Routine Description: + + This routine will update the target port group with information contained + in the passed in descriptor. + + N.B: This routine must be called with DsmContextLock held in Exclusive mode. + +Arguments: + + DsmContext - DsmContext + TargetPortGroup - Pointer to the TPG entry to update. + TargetPortGroupsDescriptor - Pointer to the TPG descriptor. + TPGs_BufferLength - Length of the passed in TargetPortGroupsDescriptor buffer. + DescriptorSize - return value of the size of the descriptor. + +Return Value: + + The updated target port group entry on success, NULL in case of failure. + +--*/ +{ + PLIST_ENTRY entry; + PDSM_TARGET_PORT_GROUP_ENTRY targetPortGroup = TargetPortGroup; + PSPC3_REPORT_TARGET_PORT_GROUP_DESCRIPTOR descriptor = (PSPC3_REPORT_TARGET_PORT_GROUP_DESCRIPTOR)TargetPortGroupsDescriptor; + ULONG numberTargetPorts = 0; + PULONG descriptorIndex; + ULONG index; + PDSM_TARGET_PORT_LIST_ENTRY listEntry; + NTSTATUS status = STATUS_SUCCESS; + ULONG identifier; + PLIST_ENTRY tempEntry = NULL; + ULONG delCount; + PUCHAR endOfBuffer = TargetPortGroupsDescriptor + TPGs_BufferLength - 1; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpUpdateTargetPortGroupEntry (TPG %p): Entering function.\n", + TargetPortGroup)); + + if (DescriptorSize == NULL) { + status = STATUS_INVALID_PARAMETER; + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_GENERAL, + "DsmpUpdateTargetPortGroupEntry (TPG %p): Status %x due to null passed in DescriptorSize pointer\n", + TargetPortGroup, + status)); + + goto __Exit_DsmpUpdateTargetPortGroupEntry; + } + + *DescriptorSize = sizeof(SPC3_REPORT_TARGET_PORT_GROUP_DESCRIPTOR) + + (targetPortGroup->NumberTargetPorts * sizeof(ULONG)); + + if (((PUCHAR)descriptor + sizeof(SPC3_REPORT_TARGET_PORT_GROUP_DESCRIPTOR) - 1) > endOfBuffer) { + + status = STATUS_INVALID_PARAMETER; + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_GENERAL, + "DsmpUpdateTargetPortGroupEntry (TPG %p): Status %x due to incorrect passed in TPG buffer size (%u).\n", + TargetPortGroup, + status, + TPGs_BufferLength)); + + goto __Exit_DsmpUpdateTargetPortGroupEntry; + } + + identifier = ((descriptor->TPG_Identifier & 0x00FF) << 8) | ((descriptor->TPG_Identifier & 0xFF00) >> 8); + NT_ASSERT(targetPortGroup->Identifier == (USHORT)identifier); + NT_ASSERT(targetPortGroup->ActiveOptimizedSupported == (descriptor->ActiveOptimizedSupported) ? TRUE : FALSE); + NT_ASSERT(targetPortGroup->ActiveUnoptimizedSupported == (descriptor->ActiveUnoptimizedSupported) ? TRUE : FALSE); + NT_ASSERT(targetPortGroup->StandBySupported == (descriptor->StandbySupported) ? TRUE : FALSE); + NT_ASSERT(targetPortGroup->UnavailableSupported == (descriptor->UnavailableSupported) ? TRUE : FALSE); + NT_ASSERT(targetPortGroup->TransitioningSupported == (descriptor->TransitioningSupported) ? TRUE : FALSE); + DSM_ASSERT(targetPortGroup->VendorUnique == descriptor->VendorUnique); + + // + // It is possible that the asymmetric access state, status code and number of port + // may have changed + // + if ((targetPortGroup->AsymmetricAccessState) != (descriptor->AsymmetricAccessState & 0xF)) + { + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpUpdateTargetPortGroupEntry (TPG %p): Asymmetric access state has changed.\n", + TargetPortGroup)); + + + targetPortGroup->AsymmetricAccessState = descriptor->AsymmetricAccessState & 0xF; + } + + targetPortGroup->Preferred = (descriptor->Preferred) ? TRUE : FALSE; + + targetPortGroup->StatusCode = descriptor->StatusCode; + + numberTargetPorts = descriptor->NumberTargetPorts; + + NT_ASSERT(numberTargetPorts > 0); + + // + // Point to first target port identifier + // + descriptorIndex = descriptor->TargetPortIds; + + for (index = 0; index < numberTargetPorts && NT_SUCCESS(status); index++) { + + if (((PUCHAR)descriptorIndex + ((index + 1) * sizeof(ULONG)) - 1) > endOfBuffer) { + + status = STATUS_INVALID_PARAMETER; + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_GENERAL, + "DsmpUpdateTargetPortGroupEntry (TPG %p): Status %x due to incorrect TPG buffer size (%u) passed in.\n", + TargetPortGroup, + status, + TPGs_BufferLength)); + + goto __Exit_DsmpUpdateTargetPortGroupEntry; + } + + GetUlongFrom4ByteArray((PUCHAR)(&descriptorIndex[index]), identifier); + + listEntry = DsmpFindTargetPortListEntry(DsmContext, + targetPortGroup, + identifier); + + if (listEntry) { + + RemoveEntryList(&listEntry->ListEntry); + InsertHeadList(&targetPortGroup->TargetPortList, &listEntry->ListEntry); + + } else { + + listEntry = DsmpBuildTargetPortListEntry(DsmContext, + targetPortGroup, + identifier); + + if (listEntry) { + + InsertHeadList(&targetPortGroup->TargetPortList, &listEntry->ListEntry); + InterlockedIncrement((LONG volatile*)&targetPortGroup->NumberTargetPorts); + + } else { + + status = STATUS_INSUFFICIENT_RESOURCES; + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_GENERAL, + "DsmpUpdateTargetPortGroupEntry (TPG %p): Failed to allocate TargetPort (identifier %x).\n", + TargetPortGroup, + identifier)); + } + } + } + + // + // Ignore the status & carry on. Even if we weren't able to build TP entries + // for the new target ports, we are no worse off than before. + // + DSM_ASSERT(NT_SUCCESS(status)); + + *DescriptorSize = sizeof(SPC3_REPORT_TARGET_PORT_GROUP_DESCRIPTOR) + + (numberTargetPorts * sizeof(ULONG)); + + for (index = 0, entry = targetPortGroup->TargetPortList.Flink; + index < numberTargetPorts; + index++, entry = entry->Flink); + + delCount = targetPortGroup->NumberTargetPorts - numberTargetPorts; + + for (index = 0; index < delCount; index++) { + + tempEntry = entry; + entry = entry->Flink; + + RemoveEntryList(tempEntry); + InterlockedDecrement((LONG volatile*)&targetPortGroup->NumberTargetPorts); + + listEntry = CONTAINING_RECORD(tempEntry, DSM_TARGET_PORT_LIST_ENTRY, ListEntry); + NT_ASSERT(listEntry); + + if (listEntry) { + + PLIST_ENTRY deviceEntry; + PDSM_TARGET_PORT_DEVICELIST_ENTRY tp_device; + + while (!IsListEmpty(&listEntry->TP_DeviceList)) { + + deviceEntry = RemoveHeadList(&listEntry->TP_DeviceList); + InterlockedDecrement((LONG volatile*)&listEntry->Count); + + if (deviceEntry) { + + tp_device = CONTAINING_RECORD(deviceEntry, DSM_TARGET_PORT_DEVICELIST_ENTRY, ListEntry); + + if (tp_device) { + + if (tp_device->DeviceInfo) { + + tp_device->DeviceInfo->TargetPort = NULL; + } + + DsmpFreePool(tp_device); + } + } + } + + DsmpFreePool(listEntry); + } + } + +__Exit_DsmpUpdateTargetPortGroupEntry: + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpUpdateTargetPortGroupEntry (TPG %p): Exiting function.\n", + targetPortGroup)); + + return targetPortGroup; +} + + +PDSM_TARGET_PORT_GROUP_ENTRY +DsmpBuildTargetPortGroupEntry( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_GROUP_ENTRY Group, + _In_reads_bytes_(TPGs_BufferLength) IN PUCHAR TargetPortGroupsDescriptor, + _In_ IN ULONG TPGs_BufferLength, + _Out_ OUT PULONG DescriptorSize + ) +/*++ + +Routine Description: + + This will allocate and partially initialise a target port group entry. + + N.B: This routine must be called with DsmContextLock held in Exclusive mode. + +Arguments: + + DsmContext - DsmContext + Group - The group that this newly going to be built TPG belongs to. + TargetPortGroupsDescriptor - Pointer to the TPG descriptor. + TPGs_BufferLength - Length of the passed in TargetPortGroupsDescriptor buffer. + DescriptorSize - return value of the size of the descriptor. + +Return Value: + + The new target port group entry. + +--*/ +{ + PDSM_TARGET_PORT_GROUP_ENTRY entry = NULL; + PSPC3_REPORT_TARGET_PORT_GROUP_DESCRIPTOR descriptor = (PSPC3_REPORT_TARGET_PORT_GROUP_DESCRIPTOR)TargetPortGroupsDescriptor; + ULONG numberTargetPorts = 0; + PULONG descriptorIndex; + ULONG index = 0; + PDSM_TARGET_PORT_LIST_ENTRY listEntry; + NTSTATUS status = STATUS_SUCCESS; + ULONG identifier; + PUCHAR endOfBuffer = TargetPortGroupsDescriptor + TPGs_BufferLength - 1; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpBuildTargetPortGroupEntry (Group %p): Entering function.\n", + Group)); + + if (DescriptorSize == NULL) { + + status = STATUS_INVALID_PARAMETER; + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_GENERAL, + "DsmpBuildTargetPortGroupEntry (Group %p): Status %x due to null passed in DescriptorSize pointer\n", + Group, + status)); + + goto __Exit_DsmpBuildTargetPortGroupEntry; + } + + *DescriptorSize = 0; + + if (((PUCHAR)descriptor + sizeof(SPC3_REPORT_TARGET_PORT_GROUP_DESCRIPTOR) - 1) > endOfBuffer) { + + status = STATUS_INVALID_PARAMETER; + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_GENERAL, + "DsmpBuildTargetPortGroupEntry (Group %p): Status %x due to incorrect passed in TPG buffer size (%u).\n", + Group, + status, + TPGs_BufferLength)); + + goto __Exit_DsmpBuildTargetPortGroupEntry; + } + + // + // Allocate the memory for the multi-path group. + // + entry = DsmpAllocatePool(NonPagedPoolNx, + sizeof(DSM_TARGET_PORT_GROUP_ENTRY), + DSM_TAG_TARGET_PORT_GROUP_ENTRY); + + if (entry) { + + entry->TargetPortGroupSig = DSM_TARGET_PORT_GROUP_SIG; + + // + // Target Port Group's access state + // + entry->AsymmetricAccessState = descriptor->AsymmetricAccessState & 0xF; + + // + // Target Port Group's supported states + // + entry->ActiveOptimizedSupported = (descriptor->ActiveOptimizedSupported) ? TRUE : FALSE; + entry->ActiveUnoptimizedSupported = (descriptor->ActiveUnoptimizedSupported) ? TRUE : FALSE; + entry->StandBySupported = (descriptor->StandbySupported) ? TRUE : FALSE; + entry->UnavailableSupported = (descriptor->UnavailableSupported) ? TRUE : FALSE; + + // + // Target Port Group's Preference and support for reporting transitioning + // + entry->Preferred = (descriptor->Preferred) ? TRUE : FALSE; + entry->TransitioningSupported = (descriptor->TransitioningSupported) ? TRUE : FALSE; + + // + // Target Port Group's identifier + // + entry->Identifier = ((descriptor->TPG_Identifier & 0x00FF) << 8) | ((descriptor->TPG_Identifier & 0xFF00) >> 8); + + // + // Target Port Group's status code + // + entry->StatusCode = descriptor->StatusCode; + + // + // Vendor unique + // + entry->VendorUnique = descriptor->VendorUnique; + + // + // Number of target ports + // + numberTargetPorts = descriptor->NumberTargetPorts; + + NT_ASSERT(numberTargetPorts > 0); + + // + // Point to first target port identifier + // + descriptorIndex = descriptor->TargetPortIds; + + InitializeListHead(&entry->TargetPortList); + + for (index = 0; index < numberTargetPorts && NT_SUCCESS(status); index++) { + + if (((PUCHAR)descriptorIndex + ((index + 1) * sizeof(ULONG)) - 1) > endOfBuffer) { + + status = STATUS_INVALID_PARAMETER; + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_GENERAL, + "DsmpBuildTargetPortGroupEntry (Group %p): Status %x due to incorrect TPG buffer size (%u) passed in.\n", + Group, + status, + TPGs_BufferLength)); + + break; + } + + GetUlongFrom4ByteArray((PUCHAR)(&descriptorIndex[index]), identifier); + + listEntry = DsmpBuildTargetPortListEntry(DsmContext, + entry, + identifier); + + if (listEntry) { + + InsertTailList(&entry->TargetPortList, &listEntry->ListEntry); + InterlockedIncrement((LONG volatile*)&entry->NumberTargetPorts); + + } else { + + status = STATUS_INSUFFICIENT_RESOURCES; + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_GENERAL, + "DsmpBuildTargetPortGroupEntry (Group %p): Failed to allocate memory for TP (identifier %x) of TPG %p.\n", + Group, + identifier, + entry)); + } + } + + if (NT_SUCCESS(status)) { + *DescriptorSize = sizeof(SPC3_REPORT_TARGET_PORT_GROUP_DESCRIPTOR) + + (numberTargetPorts * sizeof(ULONG)); + } + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_GENERAL, + "DsmpBuildTargetPortGroupEntry (Group %p): Failed to allocate memory for the TPG.\n", + Group)); + + status = STATUS_INSUFFICIENT_RESOURCES; + } + + if (!NT_SUCCESS(status)) { + + // + // Delete the target port list and the target port group entry + // + numberTargetPorts = index - 1; + + if (entry) { + + PLIST_ENTRY delEntry; + + for (index = 0; index < numberTargetPorts; index++) { + + delEntry = RemoveHeadList(&entry->TargetPortList); + listEntry = CONTAINING_RECORD(delEntry, DSM_TARGET_PORT_LIST_ENTRY, ListEntry); + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpBuildTargetPortGroupEntry (Group %p): Cleaning up TPG %p's TP %x.\n", + Group, + entry, + listEntry->Identifier)); + + DsmpFreePool(listEntry); + } + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpBuildTargetPortGroupEntry (Group %p): Cleaning up TPG %p.\n", + Group, + entry)); + + DsmpFreePool(entry); + entry = NULL; + } + } + +__Exit_DsmpBuildTargetPortGroupEntry: + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpBuildTargetPortGroupEntry (Group %p): Exiting function with entry %p.\n", + Group, + entry)); + + return entry; +} + + +PDSM_TARGET_PORT_LIST_ENTRY +DsmpFindTargetPortListEntry( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_TARGET_PORT_GROUP_ENTRY TargetPortGroup, + _In_ IN ULONG RelativeTargetPortId + ) +/*++ + +Routine Description: + + This will search the passed in TPG's target port list for an identifier match. + + N.B: This routine must be called with DsmContextLock held in either Shared or + Exclusive mode. + +Arguments: + + DsmContext - DsmContext + TargetPortGroup - The Target Port Group whose target ports need to be searched. + RelativeTargetPortId - Identifier of the target port entry being matched. + +Return Value: + + The target port list entry if match found, else NULL. + +--*/ +{ + PLIST_ENTRY entry = NULL; + PDSM_TARGET_PORT_LIST_ENTRY targetPort = NULL; + BOOLEAN found = FALSE; + + UNREFERENCED_PARAMETER(DsmContext); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpFindTargetPortListEntry (TPG %p): Entering function.\n", + TargetPortGroup)); + + for (entry = TargetPortGroup->TargetPortList.Flink; + entry != &TargetPortGroup->TargetPortList && !found; + entry = entry->Flink) { + + targetPort = CONTAINING_RECORD(entry, DSM_TARGET_PORT_LIST_ENTRY, ListEntry); + NT_ASSERT(targetPort); + + if (targetPort) { + + if (targetPort->Identifier == RelativeTargetPortId) { + + NT_ASSERT(targetPort->TargetPortGroup == TargetPortGroup); + + found = TRUE; + } + } + } + + if (!found) { + targetPort = NULL; + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpFindTargetPortListEntry (TPG %p): Exiting function with target port %p.\n", + TargetPortGroup, + targetPort)); + + return targetPort; +} + + +PDSM_TARGET_PORT_LIST_ENTRY +DsmpBuildTargetPortListEntry( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_TARGET_PORT_GROUP_ENTRY TargetPortGroup, + _In_ IN ULONG RelativeTargetPortId + ) +/*++ + +Routine Description: + + This will allocate and partially initialize a target port list entry. + + N.B: This routine must be called with DsmContextLock held in Exclusive mode. + +Arguments: + + DsmContext - DsmContext + TargetPortGroup - The Target Port Group that this target port belongs to. + RelativeTargetPortId - Identifier of the target port entry being added. + +Return Value: + + The new target port list entry. + +--*/ +{ + PDSM_TARGET_PORT_LIST_ENTRY entry; + + UNREFERENCED_PARAMETER(DsmContext); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpBuildTargetPortListEntry (TPG %p): Entering function.\n", + TargetPortGroup)); + + entry = DsmpAllocatePool(NonPagedPoolNx, + sizeof(DSM_TARGET_PORT_LIST_ENTRY), + DSM_TAG_TARGET_PORT_LIST_ENTRY); + + if (entry) { + + InitializeListHead(&entry->TP_DeviceList); + + entry->Identifier = RelativeTargetPortId; + entry->TargetPortGroup = TargetPortGroup; + entry->TargetPortSig = DSM_TARGET_PORT_SIG; + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_GENERAL, + "DsmpBuildTargetPortListEntry (TPG %p): Failed to allocate memory for target port (identifier %x).\n", + TargetPortGroup, + RelativeTargetPortId)); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpBuildTargetPortListEntry (TPG %p): Exiting function with entry %p.\n", + TargetPortGroup, + entry)); + + return entry; +} + + +PDSM_TARGET_PORT_GROUP_ENTRY +DsmpFindTargetPortGroup( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_GROUP_ENTRY Group, + _In_ IN PUSHORT TargetPortGroupId + ) +/*++ + +Routine Description: + + This routine searches the list of TargetPortGroups of a Group to + find a match for the passed in TargetPortGroupId. + + N.B: This routine must be called with DsmContextLock held in either Shared or + Exclusive mode. + +Arguments: + + DsmContext - DSM context. + Group - The group whose target port groups to search for a match. + TargetPortGroupId - Identifier of the target port group entry being searched. + +Return Value: + + The target port group entry which matches the passed in identifier. + +--*/ +{ + ULONG index; + PDSM_TARGET_PORT_GROUP_ENTRY targetPortGroupEntry = NULL; + BOOLEAN found = FALSE; + + UNREFERENCED_PARAMETER(DsmContext); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpFindTargetPortGroup (Group %p): Entering function.\n", + Group)); + + // + // Run through the target port group array + // + for (index = 0; index < DSM_MAX_PATHS && !found; index++) { + + targetPortGroupEntry = Group->TargetPortGroupList[index]; + + if (targetPortGroupEntry) { + + if (targetPortGroupEntry->Identifier == *TargetPortGroupId) { + + found = TRUE; + } + } + } + + if (!found) { + + targetPortGroupEntry = NULL; + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpFindTargetPortGroup (Group %p): Exiting function with targetPortGroupEntry %p.\n", + Group, + targetPortGroupEntry)); + + return targetPortGroupEntry; +} + + +PDSM_TARGET_PORT_LIST_ENTRY +DsmpFindTargetPort( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_TARGET_PORT_GROUP_ENTRY TargetPortGroup, + _In_ IN PULONG TargetPortGroupId + ) +/*++ + +Routine Description: + + This routine searches the list of TargetPorts to + find a match for the passed in TargetPortGroup and RelativeTargetPortId. + + N.B. Spin lock must be held by caller. + +Arguments: + + DsmContext - DSM context. + TargetPortGroup - the Target Port Group of which this target port is a member. + RelativeTargetPortId - Identifier of the target port entry being searched. + +Return Value: + + The target port entry which matches the passed in identifier. + +--*/ +{ + PLIST_ENTRY entry; + PDSM_TARGET_PORT_LIST_ENTRY targetPortListEntry = NULL; + BOOLEAN found = FALSE; + + UNREFERENCED_PARAMETER(DsmContext); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpFindTargetPort (TPG %p): Entering function.\n", + TargetPortGroup)); + + // + // Run through the Target Port List + // + for (entry = TargetPortGroup->TargetPortList.Flink; + entry != &TargetPortGroup->TargetPortList && !found; + entry = entry->Flink) { + + // + // Extract the target port group structure. + // + targetPortListEntry = CONTAINING_RECORD(entry, + DSM_TARGET_PORT_LIST_ENTRY, + ListEntry); + NT_ASSERT(targetPortListEntry); + + if (targetPortListEntry) { + + NT_ASSERT(TargetPortGroup == targetPortListEntry->TargetPortGroup); + + // + // Compare with passed in identifier. + // + if (targetPortListEntry->Identifier == *TargetPortGroupId) { + + found = TRUE; + } + } + } + + if (!found) { + + targetPortListEntry = NULL; + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpFindTargetPort (TPG %p): Exiting function with targetPortListEntry %p.\n", + TargetPortGroup, + targetPortListEntry)); + + return targetPortListEntry; +} + + +NTSTATUS +DsmpAddDeviceEntry( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_GROUP_ENTRY Group, + _In_ IN PDSM_DEVICE_INFO DeviceInfo + ) +/*++ + +Routine Description: + + This routine adds DeviceInfo to an existing multi-path group. + + N.B: This routine MUST be called with DsmContextLock held in Exclusive mode. + +Arguments: + + DsmContext - DSM context given to MPIO during initialization + Group - The multi-path group to which DeviceInfo should be added. + DeviceInfo - The new device. + DeviceState - The initial device state (active, passive,...) + +Return Value: + + UNSUCCESSFUL - If there are too many paths already. + SUCCESS + +--*/ +{ + ULONG numberDevices; + NTSTATUS status = STATUS_SUCCESS; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpAddDeviceEntry (DevInfo %p): Entering function.\n", + DeviceInfo)); + + // + // Ensure that this is a valid config - namely, it hasn't + // exceeded the number of paths supported. + // + numberDevices = * (volatile ULONG *) &Group->NumberDevices; + if (numberDevices < DSM_MAX_PATHS) { + +#if DBG + ULONG i; + + // + // Ensure that this isn't a second copy of the same pdo. + // + for (i = 0; i < numberDevices; i++) { + if (Group->DeviceList[i]->PortPdo == DeviceInfo->PortPdo) { + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpAddDeviceEntry (DevInfo %p): Received same PDO %p twice.\n", + DeviceInfo, + DeviceInfo->PortPdo)); + } + } +#endif + + // + // Indicate one more device is present in this group. + // + Group->DeviceList[numberDevices] = DeviceInfo; + + // + // Indicate one more in the list. + // + InterlockedIncrement((LONG volatile*)&Group->NumberDevices); + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpAddDeviceEntry (DevInfo %p): Adding Device to Group %p\n", + DeviceInfo, + Group)); + + // + // Set-up this device's group id. + // + DeviceInfo->Group = Group; + + // + // One more deviceInfo entry. + // + InterlockedIncrement((LONG volatile*)&DsmContext->NumberDevices); + + // + // Finally, add it to the global list of devices. + // + InsertTailList(&DsmContext->DeviceList, + &DeviceInfo->ListEntry); + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpAddDeviceEntry (DevInfo %p): Max Paths already added for Group %p.\n", + DeviceInfo, + Group)); + + status = STATUS_UNSUCCESSFUL; + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpAddDeviceEntry (DevInfo %p): Exiting function with status %x.\n", + DeviceInfo, + status)); + + return status; +} + + +PDSM_CONTROLLER_LIST_ENTRY +DsmpFindControllerEntry( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDEVICE_OBJECT PortObject, + _In_ IN PSCSI_ADDRESS ScsiAddress, + _In_reads_(ControllerSerialNumberLength) IN PSTR ControllerSerialNumber, + _In_ IN SIZE_T ControllerSerialNumberLength, + _In_ IN STORAGE_IDENTIFIER_CODE_SET CodeSet, + _In_ IN BOOLEAN AcquireLock + ) +/*++ + +Routine Description: + + This routine compares the passed in serial number and SCSI address with the + entries in the list of controller objects. + +Arguments: + + DsmContext - DSM context given to MPIO during initialization. + PortObject - Port FDO exposing the controller. + ScsiAddress - The scsi address to match. + ControllerSerialNumber - The serial number for which to find a match. + ControllerSerialNumberLength - Length of the passed in serial number, in bytes. + CodeSet - Code set used when building the passed in serial number. + AcquireLock - FALSE indicates that the caller has already acquired the spin lock. + +Return Value: + + Controller list entry if a match is found, else NULL + +--*/ +{ + KIRQL oldIrql = PASSIVE_LEVEL; // Initialize variable to prevent C4701 error + PLIST_ENTRY entry; + PDSM_CONTROLLER_LIST_ENTRY controllerEntry = NULL; + BOOLEAN found = FALSE; + PDSM_CONTROLLER_LIST_ENTRY candidate = NULL; + + UNREFERENCED_PARAMETER(CodeSet); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpFindControllerEntry (SN %s): Entering function.\n", + ControllerSerialNumber)); + + if (AcquireLock) { + + oldIrql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); + } + + for (entry = DsmContext->ControllerList.Flink; + entry != &DsmContext->ControllerList && !found; + entry = entry->Flink) { + + controllerEntry = CONTAINING_RECORD(entry, DSM_CONTROLLER_LIST_ENTRY, ListEntry); + NT_ASSERT(controllerEntry); + + if (!controllerEntry) { + + continue; + } + + // + // Serial numbers and Portal, Bus, and Target of the SCSI address must match. + // + if (!strncmp((const char*)controllerEntry->Identifier, + ControllerSerialNumber, + ControllerSerialNumberLength) && + (controllerEntry->ScsiAddress->PortNumber == ScsiAddress->PortNumber && + controllerEntry->ScsiAddress->PathId == ScsiAddress->PathId && + controllerEntry->ScsiAddress->TargetId == ScsiAddress->TargetId)) { + + if (controllerEntry->IdLength == ControllerSerialNumberLength) { + + found = TRUE; + + } else { + + if ((!candidate) || + (controllerEntry->IdLength > ControllerSerialNumberLength && ControllerSerialNumberLength == 32)) { + + candidate = controllerEntry; + } + } + } + } + + if (!found) { + + if (candidate) { + + controllerEntry = candidate; + + } else { + + controllerEntry = NULL; + } + } + + // + // If we found a matching controller entry, we need to make sure the Port + // Object (FDO) is updated. We also don't care about the LUN part of the + // SCSI address so we just set it to zero. + // + if (controllerEntry) { + controllerEntry->PortObject = PortObject; + controllerEntry->ScsiAddress->Lun = 0; + } + + if (AcquireLock) { + + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), oldIrql); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpFindControllerEntry (SN %s): Exiting function with controllerEntry %p\n", + ControllerSerialNumber, + controllerEntry)); + + return controllerEntry; +} + + +_Ret_maybenull_ +_Must_inspect_result_ +_When_(return != NULL, __drv_allocatesMem(Mem)) +PDSM_CONTROLLER_LIST_ENTRY +DsmpBuildControllerEntry( + _In_ IN PDSM_CONTEXT DsmContext, + _In_opt_ IN PDEVICE_OBJECT DeviceObject, + _In_ IN PDEVICE_OBJECT PortObject, + _In_ IN PSCSI_ADDRESS ScsiAddress, + _In_ IN PSTR ControllerSerialNumber, + _In_ IN STORAGE_IDENTIFIER_CODE_SET CodeSet, + _In_ IN BOOLEAN AcquireLock + ) +/*++ + +Routine Description: + + This routine builds a new controller list entry with the passed in serial number info. + +Arguments: + + DsmContext - DSM context given to MPIO during initialization. + DeviceObject - Controller's PDO. + PortObject - Port FDO exposing the controller. + ScsiAddress - scsi address of the controller. + ControllerSerialNumber - The serial number to associate with new entry. + CodeSet - Code set of the identifier that was used to build the serial number. + AcquireLock - TRUE indicates that the function must grab the spinlock. FALSE indicates + that caller has the spin lock held. + +Return Value: + + New controller list entry if we successfully built one, else NULL + +--*/ +{ + KIRQL oldIrql = PASSIVE_LEVEL; // Initialize variable to prevent C4701 error + PDSM_CONTROLLER_LIST_ENTRY controllerEntry = NULL; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpBuildControllerEntry (SN %s): Entering function - Controller %p seen through PortFDO %p.\n", + ControllerSerialNumber, + DeviceObject, + PortObject)); + + if (AcquireLock) { + + oldIrql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); + } + + controllerEntry = DsmpAllocatePool(NonPagedPoolNx, + sizeof(DSM_CONTROLLER_LIST_ENTRY), + DSM_TAG_CONTROLLER_LIST_ENTRY); + + if (controllerEntry) { + + // + // Note: + // ControllerSerialNumber's length fits in a 32-bit value. + // See implementation in DsmpParseDeviceID() + // + ULONG length = (ULONG)strlen(ControllerSerialNumber); + + controllerEntry->Identifier = DsmpAllocatePool(NonPagedPoolNx, + length + 1, + DSM_TAG_SERIAL_NUM); + + if (controllerEntry->Identifier) { + + controllerEntry->ScsiAddress = DsmpAllocatePool(NonPagedPoolNx, + sizeof(SCSI_ADDRESS), + DSM_TAG_SCSI_ADDRESS); + if (controllerEntry->ScsiAddress) { + + RtlCopyMemory(controllerEntry->ScsiAddress, ScsiAddress, sizeof(SCSI_ADDRESS)); + + controllerEntry->DeviceObject = DeviceObject; + controllerEntry->PortObject = PortObject; + controllerEntry->ControllerSig = DSM_CONTROLLER_SIG; + controllerEntry->IdLength = length; + controllerEntry->IdCodeSet = CodeSet; + + RtlCopyMemory(controllerEntry->Identifier, + ControllerSerialNumber, + length); + + controllerEntry->RefCount = 0; + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpBuildControllerEntry (SN %s): Failed to allocate resources for scsiaddress (controllerEntry %p).\n", + ControllerSerialNumber, + controllerEntry)); + + DsmpFreePool(controllerEntry->Identifier); + controllerEntry->Identifier = NULL; + DsmpFreePool(controllerEntry); + controllerEntry = NULL; + } + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpBuildControllerEntry (SN %s): Failed to allocate resources for identifier (controllerEntry %p).\n", + ControllerSerialNumber, + controllerEntry)); + + DsmpFreePool(controllerEntry); + controllerEntry = NULL; + } + + } else { + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpBuildControllerEntry (SN %s): Failed to allocate memory for ControllerEntry.\n", + ControllerSerialNumber)); + } + + if (AcquireLock) { + + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), oldIrql); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpBuildControllerEntry (SN %s): Exiting function with controllerEntry %p\n", + ControllerSerialNumber, + controllerEntry)); + + return controllerEntry; +} + + +VOID +DsmpFreeControllerEntry( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ __drv_freesMem(Mem) IN PDSM_CONTROLLER_LIST_ENTRY ControllerEntry + ) +/*++ + +Routine Description: + + This routine frees the allocations of the passed in controller list entry. + +Arguments: + + DsmContext - DSM context given to MPIO during initialization. + ControllerEntry - Controller list entry. + +Return Value: + + Nothing + +--*/ +{ + PVOID tempAddress = (PVOID)ControllerEntry; + + UNREFERENCED_PARAMETER(DsmContext); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpFreeControllerEntry (Entry %p): Entering function.\n", + ControllerEntry)); + + if (ControllerEntry->Identifier) { + DsmpFreePool(ControllerEntry->Identifier); + } + + if (ControllerEntry->ScsiAddress) { + DsmpFreePool(ControllerEntry->ScsiAddress); + } + + DsmpFreePool(ControllerEntry); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpFreeControllerEntry (Entry %p): Exiting function.\n", + tempAddress)); + + return; +} + + +BOOLEAN +DsmpIsDeviceBelongsToController( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_DEVICE_INFO DeviceInfo, + _In_ IN PDSM_CONTROLLER_LIST_ENTRY ControllerEntry + ) +/*++ + +Routine Description: + + This routine determines if the device passed in was exposed via the passed + in controller. + The match is to be based on VID and SCSI Address (using the Port, Bus + and Target comparison). + +Arguments: + + DsmContext - DSM context given to MPIO during initialization. + DeviceInfo - The device instance to match. + ControllerEntry - The controller object which we need to determine whether + DeviceInfo is exposed from. + +Return Value: + + TRUE - if the controller's VID and scsi address match + FALSE - not matched + +--*/ +{ + BOOLEAN saMatch = FALSE; + BOOLEAN vMatch = FALSE; + BOOLEAN match = FALSE; + + UNREFERENCED_PARAMETER(DsmContext); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpIsDeviceBelongsToController (DevInfo %p): Entering function - ControllerEntry is %p.\n", + DeviceInfo, + ControllerEntry)); + + if (DeviceInfo->ScsiAddress && ControllerEntry->ScsiAddress) { + + saMatch = (DeviceInfo->ScsiAddress->PathId == ControllerEntry->ScsiAddress->PathId && + DeviceInfo->ScsiAddress->PortNumber == ControllerEntry->ScsiAddress->PortNumber && + DeviceInfo->ScsiAddress->TargetId == ControllerEntry->ScsiAddress->TargetId); + } + + if (saMatch) { + + INQUIRYDATA inquiryData = {0}; + UCHAR controllerVID[9] = {0}; + UCHAR deviceVID[9] = {0}; + + if (NT_SUCCESS(DsmpGetStandardInquiryData(ControllerEntry->DeviceObject, &inquiryData))) { + + RtlStringCchCopyA((PSTR)controllerVID, + ARRAYSIZE(controllerVID), + (PCSTR)(&inquiryData.VendorId)); + + RtlStringCchCopyA((PSTR)deviceVID, + ARRAYSIZE(deviceVID), + (PCSTR)(&DeviceInfo->Descriptor) + DeviceInfo->Descriptor.VendorIdOffset); + + + if (!strcmp((const char*)controllerVID, (const char*)deviceVID)) { + + vMatch = TRUE; + } + } + } + + match = saMatch & vMatch; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpIsDeviceBelongsToController (DevInfo %p): ControllerEntry %p. Exiting function with match = %x.\n", + DeviceInfo, + ControllerEntry, + match)); + + return match; +} + + +PDSM_DEVICE_INFO +DsmpFindDevInfoFromGroupAndFOGroup( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_GROUP_ENTRY Group, + _In_ IN PDSM_FAILOVER_GROUP FOGroup + ) +/*++ + +Routine Description: + + This routine will find the deviceInfo that is part of both the passed in Group + as well as passed in Fail-Over group. + + N.B: This routine MUST be called with DsmContextLock held in either Shared or + Exclusive mode. + +Arguments: + + DsmContext - DSM context given to MPIO during initialization + Group - The group that represents the device. + FOGroup - The FOG that the device is part of. + +Return Value: + + The deviceInfo that is part of both. + NULL - if not found. + +--*/ +{ + ULONG i; + PDSM_DEVICE_INFO deviceInfo = NULL; + + UNREFERENCED_PARAMETER(DsmContext); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmpFindDevInfoFromGroupAndFOGroup (Group %p FOG %p): Entering function.\n", + Group, + FOGroup)); + + if (Group && FOGroup) { + + // + // Run through the list of devInfos in passed in Group + // + for (i = 0; i < DSM_MAX_PATHS; i++) { + + deviceInfo = Group->DeviceList[i]; + + if (deviceInfo) { + + if (deviceInfo->FailGroup == FOGroup) { + + break; + + } else { + + deviceInfo = NULL; + } + } + } + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmpFindFOGroup (Group %p FOG %p): Exiting function with deviceInfo %p.\n", + Group, + FOGroup, + deviceInfo)); + + return deviceInfo; +} + + +PDSM_FAILOVER_GROUP +DsmpFindFOGroup( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PVOID PathId + ) +/*++ + +Routine Description: + + This routine will find the Fail-Over group that corresponds to PathId. + + N.B: This routine MUST be called with DsmContextLock held in either Shared or + Exclusive mode. + +Arguments: + + DsmContext - DSM context given to MPIO during initialization + PathId - The Path Identifier that corresponds to + an adapter/adapter-controller + +Return Value: + + The fail-over group. + NULL - if not found. + +--*/ +{ + PDSM_FAILOVER_GROUP failOverGroup = NULL; + PDSM_FAILOVER_GROUP retFOGroup = NULL; + PLIST_ENTRY entry; + ULONG i; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpFindFOGroup (PathId %p): Entering function.\n", + PathId)); + + // + // Run through the list of Fail-Over Groups + // + entry = DsmContext->FailGroupList.Flink; + for (i = 0; i < DsmContext->NumberFOGroups; i++, entry = entry->Flink) { + + // + // Extract the fail-over group structure. + // + failOverGroup = CONTAINING_RECORD(entry, DSM_FAILOVER_GROUP, ListEntry); + NT_ASSERT(failOverGroup); + + if (!failOverGroup) { + continue; + } + + // + // Check for a match of the PathId. + // + if (failOverGroup->PathId == PathId) { + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpFindFOGroup (PathId %p): Found a FO group %p.\n", + PathId, + failOverGroup)); + + retFOGroup = failOverGroup; + + break; + } + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpFindFOGroup (PathId %p): Exiting function with retFOGroup %p.\n", + PathId, + retFOGroup)); + + return retFOGroup; +} + + +PDSM_FAILOVER_GROUP +DsmpBuildFOGroup( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_DEVICE_INFO DeviceInfo, + _In_ IN PVOID *PathId + ) +/*++ + +Routine Description: + + This routine will build and partially initialise a fail-over group entry. + The FOG corresponds to the device list which will fail as a group. + + N.B: This routine MUST be called with DsmContextLock held in Exclusive mode. + +Arguments: + + DsmContext - DSM context given to MPIO during initialization + DeviceInfo - The first device to add to the group. + PathId - An identifier that is returned to mpio that id's the path. + +Return Value: + + The fail-over group entry. + NULL - on failed allocation. + +--*/ +{ + PDSM_FAILOVER_GROUP failOverGroup; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpBuildFOGroup (PathId %p): Entering function.\n", PathId)); + + // + // Allocate a new Fail Over Group + // + failOverGroup = DsmpAllocatePool(NonPagedPoolNx, + sizeof(DSM_FAILOVER_GROUP), + DSM_TAG_FO_GROUP); + if (failOverGroup) { + + InitializeListHead(&failOverGroup->FOG_DeviceList); + InitializeListHead(&failOverGroup->ZombieGroupList); + + // + // Get the current number of groups, and add the one that's being created. + // + InterlockedIncrement((LONG volatile*)&DsmContext->NumberFOGroups); + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpBuildFOGroup (PathId %p): Path that will be used for %p is %p.\n", + PathId, + DeviceInfo, + *PathId)); + + failOverGroup->PathId = *PathId; + + // + // Set the initial state to NORMAL. + // + failOverGroup->State = DSM_FG_NORMAL; + + failOverGroup->FailOverSig = DSM_FOG_SIG; + + // + // Add it to the global list. + // + InsertTailList(&DsmContext->FailGroupList, + &failOverGroup->ListEntry); + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpBuildFOGroup (PathId %p): Added new FOGroup %p with path %p. Count of FO Group %d.\n", + PathId, + failOverGroup, + *PathId, + DsmContext->NumberFOGroups)); + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpBuildFOGroup (PathId %p): Failed to allocate memory for FailOverGroup.\n", + PathId)); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpBuildFOGroup (PathId %p): Exiting function with failOverGroup %p.\n", + PathId, + failOverGroup)); + + return failOverGroup; +} + + +NTSTATUS +DsmpUpdateFOGroup( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_FAILOVER_GROUP FailGroup, + _In_ IN PDSM_DEVICE_INFO DeviceInfo + ) +/*++ + +Routine Description: + + This routine will add DeviceInfo to an existing FOG. + + N.B: This routine MUST be called with DsmContextLock held in Exclusive mode. + +Arguments: + + DsmContext - DSM context given to MPIO during initialization + FailGroup - The fail-over group entry. + DeviceInfo - The new device. + +Return Value: + + STATUS_SUCCESS or appropriate error code. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + PDSM_FOG_DEVICELIST_ENTRY fogDeviceListEntry; + + UNREFERENCED_PARAMETER(DsmContext); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpUpdateFOGroup (FOG %p): Entering function. DeviceInfo %p.\n", + FailGroup, + DeviceInfo)); + + if (DeviceInfo && FailGroup) { + + fogDeviceListEntry = DsmpAllocatePool(NonPagedPoolNx, + sizeof(DSM_FOG_DEVICELIST_ENTRY), + DSM_TAG_FOG_DEV_ENTRY); + + if (fogDeviceListEntry) { + + // + // Add the device to the list of devices that are on this path. + // + fogDeviceListEntry->DeviceInfo = DeviceInfo; + InterlockedIncrement((LONG volatile*)&FailGroup->Count); + InsertTailList(&FailGroup->FOG_DeviceList, &fogDeviceListEntry->ListEntry); + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpUpdateFOGroup (FOG %p): DevInfo %p added (current count: %d)\n", + FailGroup, + DeviceInfo, + FailGroup->Count)); + + // + // Set the device's F.O. Group. + // + DeviceInfo->FailGroup = FailGroup; + + } else { + + status = STATUS_INSUFFICIENT_RESOURCES; + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpUpdateFOGroup (FOG %p): Failed to allocate memory for FOG devlist entry.\n", + FailGroup)); + } + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpUpdateFOGroup (FOG %p): Exiting function with status %x.\n", + FailGroup, + status)); + + return status; +} + + +VOID +DsmpRemoveDeviceFailGroup( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_FAILOVER_GROUP FailGroup, + _In_ IN PDSM_DEVICE_INFO DeviceInfo, + _In_ IN BOOLEAN AcquireDSMLockExclusive + ) +/*++ + +Routine Description: + + This routine will remove DeviceInfo from the FOG. + This routine is called in response to a removal of the device. + +Arguments: + + DsmContext - DSM context given to MPIO during initialization + FailGroup - The FOG from which DeviceInfo should be removed. + DeviceInfo - The now missing device. + AcquireDSMLockExclusive - If TRUE this routine should acquire DsmContextLock Exclusively + +Return Value: + + NOTHING + +--*/ +{ + KIRQL irql = PASSIVE_LEVEL; // Initialize variable to prevent C4701 warnings + PLIST_ENTRY entry; + PDSM_FOG_DEVICELIST_ENTRY fogDeviceListEntry; + PLIST_ENTRY zombieEntry; + PDSM_ZOMBIEGROUP_ENTRY zombieGroup; + PDSM_ZOMBIEGROUP_ENTRY newZombieGroup; + BOOLEAN groupInZombieList = FALSE; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpRemoveDeviceFailGroup (FOG %p): Entering function. DeviceInfo %p.\n", + FailGroup, + DeviceInfo)); + + if (FailGroup && DeviceInfo) { + + if (AcquireDSMLockExclusive) { + irql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); + } + + for (entry = FailGroup->FOG_DeviceList.Flink; + entry != &FailGroup->FOG_DeviceList; + entry = entry->Flink) { + + fogDeviceListEntry = CONTAINING_RECORD(entry, + DSM_FOG_DEVICELIST_ENTRY, + ListEntry); + DSM_ASSERT(fogDeviceListEntry); + + if (!fogDeviceListEntry) { + continue; + } + + if (fogDeviceListEntry->DeviceInfo == DeviceInfo) { + + DeviceInfo->FailGroup = NULL; + RemoveEntryList(entry); + DsmpFreePool(fogDeviceListEntry); + + InterlockedDecrement((LONG volatile*)&FailGroup->Count); + + // + // If a DeviceInfo is removed, we need to keep its group in a + // "zombie" list so that we can still access a fail-over group's + // associated groups even when all its devices are gone. + // + for (zombieEntry = FailGroup->ZombieGroupList.Flink; + zombieEntry != &(FailGroup->ZombieGroupList); + zombieEntry = zombieEntry->Flink) { + + zombieGroup = CONTAINING_RECORD(zombieEntry, DSM_ZOMBIEGROUP_ENTRY, ListEntry); + if (zombieGroup != NULL && + zombieGroup->Group != NULL && + zombieGroup->Group == DeviceInfo->Group) { + + groupInZombieList = TRUE; + break; + } + } + + // + // Create a new entry if the group does not exist in the zombie group list. + // + if (groupInZombieList == FALSE) { + newZombieGroup = (PDSM_ZOMBIEGROUP_ENTRY)DsmpAllocatePool(NonPagedPool, + sizeof(DSM_ZOMBIEGROUP_ENTRY), + DSM_TAG_ZOMBIEGROUP_ENTRY); + if (newZombieGroup != NULL) { + newZombieGroup->Group = DeviceInfo->Group; + InsertTailList(&FailGroup->ZombieGroupList, &newZombieGroup->ListEntry); + } else { + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpRemoveDeviceFailGroup (DevInfo %p): Failed to allocate memory for the zombie group.\n", + DeviceInfo)); + } + } + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpRemoveDeviceFailGroup (FOG %p): DevInfo %p removed from FOG (current count: %d)\n", + FailGroup, + DeviceInfo, + FailGroup->Count)); + + break; + } + } + + if (AcquireDSMLockExclusive) { + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); + } + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpRemoveDeviceFailGroup (FOG %p): Exiting function.\n", + FailGroup)); + + return; +} + + +ULONG +DsmpRemoveDeviceEntry( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_GROUP_ENTRY Group, + _In_ IN PDSM_DEVICE_INFO DeviceInfo + ) +/*++ + +Routine Description: + + This routine will remove DeviceInfo from Group. If it is the last DeviceInfo + in the Group, it has the added side-effect of cleaning up the Group entry + also. + +Arguments: + + DsmContext - DSM context given to MPIO during initialization + Group - The multi-path group from which DeviceInfo should be removed. + DeviceInfo - The device to remove. + +Return Value: + + Number of devices left in group. + +--*/ +{ + KIRQL irql; + ULONG i; + ULONG j; + ULONG numberDevices; + BOOLEAN freeGroup = FALSE; + PVOID tempAddress = (PVOID)Group; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpRemoveDeviceEntry (Group %p): Entering function. DeviceInfo %p.\n", + Group, + DeviceInfo)); + + irql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); + + // + // Find it's offset in the array of devices. + // + for (i = 0; i < Group->NumberDevices; i++) { + + if (Group->DeviceList[i] == DeviceInfo) { + + // + // Zero out it's entry. + // + Group->DeviceList[i] = NULL; + + // + // Reduce the number in the group. + // + InterlockedDecrement((LONG volatile*)&Group->NumberDevices); + + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpRemoveDeviceEntry (Group %p): Removing Device %p (desiredState %u) from Group\n", + Group, + DeviceInfo, + DeviceInfo->DesiredState)); + + // + // Collapse the array. + // Holding the spinlock, so that the state is consistent in other + // routines. + // + for (j = i; j < Group->NumberDevices; j++) { + + // + // Shuffle all entries down to fill the hole. + // + Group->DeviceList[j] = Group->DeviceList[j + 1]; + } + + // + // Zero out the last one. + // + Group->DeviceList[j] = NULL; + break; + } + } + + // + // Remove this devInfo from the TargetPort deviceList + // + DsmpRemoveDeviceFromTargetPortList(DeviceInfo); + + numberDevices = Group->NumberDevices; + + // + // See if anything is left in the Group. + // + if (Group->NumberDevices == 0) { + + Group->State = DSM_GP_FAILED; + + // + // Yank it from the Group list. + // + DsmpRemoveGroupEntry(DsmContext, Group, FALSE); + + freeGroup = TRUE; + } + + // + // Yank the device out of the Global list. + // + RemoveEntryList(&DeviceInfo->ListEntry); + InterlockedDecrement((LONG volatile*)&DsmContext->NumberDevices); + + // + // If the serial number buffer was allocated, need to free it. + // + if (DeviceInfo->SerialNumberAllocated) { + DsmpFreePool(DeviceInfo->SerialNumber); + } + + if (DeviceInfo->ScsiAddress) { + DsmpFreePool(DeviceInfo->ScsiAddress); + } + + // + // Fix up the Reservation List, if needed. + // + if (!freeGroup && Group->ReservationList) { + ULONG oldList; + + // + // Capture the list for debugging. + // + oldList = Group->ReservationList; + Group->ReservationList = 0; + + // + // Go through all devices in this group and find the one(s) registered. + // + for (i = 0; i < Group->NumberDevices; i++) { + + if (Group->DeviceList[i]->RegisterServiced) { + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_PNP, + "DsmRemoveDeviceEntry (Group %p): Device %p at %d registered.\n", + Group, + Group->DeviceList[i], + i)); + + // + // Indicate its place. + // + Group->ReservationList |= (1 << i); + } + } + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmRemoveDeviceEntry (Group %p): Reservations Old (%x) New (%x).\n", + Group, + oldList, + Group->ReservationList)); + } + + + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); + + // + // Free the allocation. + // + DsmpFreePool(DeviceInfo); + + if (freeGroup) { + + // + // Free the allocations. + // + if (Group->RegistryKeyName) { + DsmpFreePool(Group->RegistryKeyName); + } + + if (Group->HardwareId) { + DsmpFreePool(Group->HardwareId); + } + + DsmpFreePool(Group); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpRemoveDeviceEntry (Group %p): Exiting function - numberDevices = %x.\n", + tempAddress, + numberDevices)); + + return numberDevices; +} + + +VOID +DsmpRemoveDeviceFromTargetPortList( + _In_ IN PDSM_DEVICE_INFO DeviceInfo + ) +/*++ + +Routine Description: + + This will remove a DeviceInfo from its target port device list. + + The caller should ensure that the DsmContext->SpinLock is held before + calling this function. + +Arguments: + + DeviceInfo - The DeviceInfo to be removed. + +Return Value: + + None + +--*/ +{ + if (DeviceInfo->TargetPort) { + + PLIST_ENTRY entry; + PDSM_TARGET_PORT_DEVICELIST_ENTRY listEntry; + + for (entry = DeviceInfo->TargetPort->TP_DeviceList.Flink; + entry != NULL && entry != &DeviceInfo->TargetPort->TP_DeviceList; + entry = entry->Flink) { + + listEntry = CONTAINING_RECORD(entry, DSM_TARGET_PORT_DEVICELIST_ENTRY, ListEntry); + + if (listEntry) { + + if (listEntry->DeviceInfo == DeviceInfo) { + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpRemoveDeviceFromTargetPortList: Removing device %p from target port entry %p.\n", + DeviceInfo, + listEntry)); + + RemoveEntryList(entry); + InterlockedDecrement((LONG volatile*)&DeviceInfo->TargetPort->Count); + DsmpFreePool(listEntry); + + DeviceInfo->TargetPort = NULL; + DeviceInfo->TargetPortGroup = NULL; + + break; + } + } + } + } +} + + +VOID +DsmpRemoveZombieGroupEntry( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_GROUP_ENTRY ZombieGroup + ) +/* ++ + +Routine Description: + + This will scan through all the Failover Groups and remove the given Group + from each Failover Group's zombie group list. + + The DSM lock should be aquired by the caller. + +Arguments: + + DsmContext - DSM context given to MPIO during initialization + ZombieGroup - Group entry that should be removed from FOGs' ZombieGroupList + +Return Value: + + None + +-- */ +{ + // + // Run through the list of Fail-Over Groups + // + ULONG i; + PDSM_FAILOVER_GROUP failOverGroup = NULL; + PLIST_ENTRY fogEntry; + PLIST_ENTRY groupEntry; + PDSM_ZOMBIEGROUP_ENTRY zombieGroupEntry; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpRemoveZombieGroupEntry (Group %p): Entering function.\n", + ZombieGroup)); + + fogEntry = DsmContext->FailGroupList.Flink; + for (i = 0; fogEntry != NULL && i < DsmContext->NumberFOGroups; i++, fogEntry = fogEntry->Flink) { + + failOverGroup = CONTAINING_RECORD(fogEntry, DSM_FAILOVER_GROUP, ListEntry); + if (failOverGroup != NULL) { + + for (groupEntry = failOverGroup->ZombieGroupList.Flink; + groupEntry != &(failOverGroup->ZombieGroupList); + groupEntry = groupEntry->Flink) { + + zombieGroupEntry = CONTAINING_RECORD(groupEntry, DSM_ZOMBIEGROUP_ENTRY, ListEntry); + if (zombieGroupEntry != NULL && + zombieGroupEntry->Group != NULL && + zombieGroupEntry->Group == ZombieGroup) { + + RemoveEntryList(groupEntry); + DsmpFreePool(zombieGroupEntry); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpRemoveZombieGroupEntry (Group %p): Found and removed a zombie group in (FOG %p)\n", + ZombieGroup, + failOverGroup)); + + // + // We removed the zombie group entry from this fail-over + // group, so we can move on to the next fail-over group. + // + break; + } + } + } + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpRemoveZombieGroupEntry (Group %p): Exiting function.\n", + ZombieGroup)); +} + + +VOID +DsmpRemoveGroupEntry( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_GROUP_ENTRY GroupEntry, + _In_ IN BOOLEAN AcquireDSMLockExclusive + ) +/*++ + +Routine Description: + + This will remove a group entry from the DSM's list. + +Arguments: + + DsmContext - DSM context given to MPIO during initialization + GroupEntry - Group entry that should be removed from DSM's list + AcquireDSMLockExclusive - If TRUE this routine should acquire DsmContextLock Exclusively + +Return Value: + + None + +--*/ +{ + KIRQL irql = PASSIVE_LEVEL; // Initialize variable to prevent C4701 warnings + ULONG index; + PDSM_TARGET_PORT_GROUP_ENTRY targetPortGroup; + PLIST_ENTRY entry; + PDSM_TARGET_PORT_LIST_ENTRY targetPort; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpRemoveGroupEntry (Group %p): Entering function.\n", + GroupEntry)); + + if (AcquireDSMLockExclusive) { + irql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); + } + + NT_ASSERT(GroupEntry && GroupEntry->ListEntry.Flink && GroupEntry->ListEntry.Blink); + + // + // Since this group is being removed, we need to make sure it is also + // removed from all fail-over groups' zombie group lists. + // + DsmpRemoveZombieGroupEntry(DsmContext, GroupEntry); + + // + // Add it to the list of multi-path groups. + // + RemoveEntryList(&GroupEntry->ListEntry); + + GroupEntry->ListEntry.Flink = GroupEntry->ListEntry.Blink = NULL; + + InterlockedDecrement((LONG volatile*)&DsmContext->NumberGroups); + + for (index = 0; index < DSM_MAX_PATHS; index++) { + + // + // Clean up all its Target Port Groups + // + targetPortGroup = GroupEntry->TargetPortGroupList[index]; + + if (targetPortGroup) { + + GroupEntry->TargetPortGroupList[index] = NULL; + + // + // For each target port group, clean up all its target ports + // + while (!IsListEmpty(&targetPortGroup->TargetPortList)) { + + entry = RemoveHeadList(&targetPortGroup->TargetPortList); + + if (entry) { + + targetPort = CONTAINING_RECORD(entry, DSM_TARGET_PORT_LIST_ENTRY, ListEntry); + + if (targetPort) { + + PLIST_ENTRY deviceEntry; + PDSM_TARGET_PORT_DEVICELIST_ENTRY listEntry; + + while (!IsListEmpty(&targetPort->TP_DeviceList)) { + + deviceEntry = RemoveHeadList(&targetPort->TP_DeviceList); + + if (deviceEntry) { + + listEntry = CONTAINING_RECORD(deviceEntry, + DSM_TARGET_PORT_DEVICELIST_ENTRY, + ListEntry); + + if (listEntry) { + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpRemoveGroupEntry (Group %p): Deleting device %p from TP %p list (TPG %p).\n", + GroupEntry, + listEntry->DeviceInfo, + targetPort, + targetPortGroup)); + + DsmpFreePool(listEntry); + + InterlockedDecrement((LONG volatile*)&targetPort->Count); + } + } + } + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpRemoveGroupEntry (Group %p): Deleting target port %p from TPG %p list.\n", + GroupEntry, + targetPort, + targetPortGroup)); + + DsmpFreePool(targetPort); + + InterlockedDecrement((LONG volatile*)&targetPortGroup->NumberTargetPorts); + } + } + } + + NT_ASSERT(targetPortGroup->NumberTargetPorts == 0); + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpRemoveGroupEntry (Group %p): Deleting target port group %p.\n", + GroupEntry, + targetPortGroup)); + + DsmpFreePool(targetPortGroup); + + InterlockedDecrement((LONG volatile*)&GroupEntry->NumberTargetPortGroups); + } + } + + NT_ASSERT(GroupEntry->NumberTargetPortGroups == 0); + + if (AcquireDSMLockExclusive) { + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpRemoveGroupEntry (Group %p): Exiting function.\n", + GroupEntry)); + + return; +} + + +PDSM_FAILOVER_GROUP +DsmpSetNewPath( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_DEVICE_INFO FailingDevice + ) +/*++ + +Routine Description: + + This routine will assign a new path to the multi-path group in + which FailingDevice resides. + + Caller must NOT hold spin lock. + +Arguments: + + DsmContext - DSM context given to MPIO during initialization + + FailingDevice - The device-path that is being moved + (due to failure, or admin. request) + +Return Value: + + The FOG containing the new path. + +--*/ +{ + ULONG SpecialHandlingFlag = 0; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpSetNewPath (DevInfo %p): Entering function.\n", + FailingDevice)); + + if (DsmpIsSymmetricAccess(FailingDevice)) { + + DsmpSetLBForPathRemoval(DsmContext, FailingDevice, NULL, SpecialHandlingFlag); + + } else { + + DsmpSetLBForPathRemovalALUA(DsmContext, FailingDevice, NULL, SpecialHandlingFlag); + } + + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpSetNewPath (DevInfo %p): Exiting function with path (failGroup) %p.\n", + FailingDevice, + FailingDevice->Group->PathToBeUsed)); + + return FailingDevice->Group->PathToBeUsed; +} + + +PDSM_FAILOVER_GROUP +DsmpSetNewPathUsingGroup( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_GROUP_ENTRY Group + ) +/*++ + +Routine Description: + + This routine will try to assign a new path using the given multi-path group. + This function should only be called during failover in the event that there + is no DeviceInfo with which to call DsmpSetNewPath(). + + Typically this will be called with one of a fail-over group's zombie groups. + + Caller must NOT hold spin lock. + +Arguments: + + DsmContext - DSM context given to MPIO during initialization. + + Group - The multi-path group which to assign a new path. + +Return Value: + + The FOG containing the new path or NULL if no path was found. + +--*/ + +{ + ULONG i; + PDSM_DEVICE_INFO pDevInfo = NULL; + ULONG SpecialHandlingFlag = 0; + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_GENERAL, + "DsmpSetNewPathUsingGroup (Group %p): Entering function.\n", + Group)); + + // + // Get the first available DeviceInfo. + // + for (i = 0; i < DSM_MAX_PATHS; i ++) { + if (Group->DeviceList[i] != NULL) { + pDevInfo = Group->DeviceList[i]; + break; + } + } + + if (pDevInfo == NULL) { + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_GENERAL, + "DsmpSetNewPathForZombieGroup (ZombieGroup %p): No failover group can be found.\n", + Group)); + return NULL; + } + + + if (DsmpIsSymmetricAccess(pDevInfo)) { + + DsmpSetLBForPathRemoval(DsmContext, pDevInfo, Group, SpecialHandlingFlag); + + } else { + + DsmpSetLBForPathRemovalALUA(DsmContext, pDevInfo, Group, SpecialHandlingFlag); + } + + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_GENERAL, + "DsmpSetNewPathForZombieGroup (ZombieGroup %p): Exiting function with path (failGroup) %p.\n", + Group, + Group->PathToBeUsed)); + + return Group->PathToBeUsed; + +} + + +NTSTATUS +DsmpUpdateTargetPortGroupDevicesStates( + _In_ IN PDSM_TARGET_PORT_GROUP_ENTRY TargetPortGroup, + _In_ IN DSM_DEVICE_STATE NewState + ) +/*++ + +Routine Description: + + This routine will update the target port group and all its appropriate + devInfos (ones not in remove pending, removed, or invalidated) with the + new state. The ALUAState will only be updated, NOT the real State. + Caller needs to update the real State based on the current LB policy. + + Note: This should be called with DsmContext Lock held and should only be + called after a SetTargetPortGroups request was sent down. + +Arguments: + + TargetPortGroup - TargetPortGroup whose state and deviceInfos need to be + updated + + NewState - The new state + +Return Value: + + STATUS_SUCCESS or appropriate failure code. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + PLIST_ENTRY entry = NULL; + PDSM_TARGET_PORT_LIST_ENTRY targetPort = NULL; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpUpdateTargetPortGroupDevicesStates (TPG %p): Entering function.\n", + TargetPortGroup)); + + if (!TargetPortGroup) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_GENERAL, + "DsmpUpdateTargetPortGroupDevicesStates (TPG %p): Invalid TPG passed in.\n", + TargetPortGroup)); + + status = STATUS_INVALID_PARAMETER; + goto __Exit_DsmpUpdateTargetPortGroupDevicesStates; + } + + // + // First update TPG's asymmetric access state. + // + TargetPortGroup->AsymmetricAccessState = NewState; + + // + // Now update state of each of the devices belonging to this TPG. + // + for (entry = TargetPortGroup->TargetPortList.Flink; + entry != &TargetPortGroup->TargetPortList; + entry = entry->Flink) { + + targetPort = CONTAINING_RECORD(entry, DSM_TARGET_PORT_LIST_ENTRY, ListEntry); + NT_ASSERT(targetPort); + + if (targetPort) { + + PLIST_ENTRY deviceEntry; + PDSM_TARGET_PORT_DEVICELIST_ENTRY tp_device; + + for (deviceEntry = targetPort->TP_DeviceList.Flink; + deviceEntry != &targetPort->TP_DeviceList; + deviceEntry = deviceEntry->Flink) { + + tp_device = CONTAINING_RECORD(deviceEntry, + DSM_TARGET_PORT_DEVICELIST_ENTRY, + ListEntry); + + if (tp_device) { + + tp_device->DeviceInfo->ALUAState = NewState; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpUpdateTargetPortGroupDevicesStates (TPG %p): Updated device %p alua state to %x.\n", + TargetPortGroup, + tp_device->DeviceInfo, + tp_device->DeviceInfo->ALUAState)); + } + } + } + } + +__Exit_DsmpUpdateTargetPortGroupDevicesStates: + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpUpdateTargetPortGroupDevicesStates (TPG %p): Exiting function with status %x.\n", + TargetPortGroup, + status)); + + return status; +} + + +VOID +DsmpIncrementCounters( + _In_ PDSM_FAILOVER_GROUP FailGroup, + _In_ PSCSI_REQUEST_BLOCK Srb + ) +{ + ULONG bytes = 0; + PCDB cdb = NULL; + ULONG cdbLength = 0; + BOOLEAN isReadWrite = FALSE; + ULONGLONG lastLba = 0; + ULONG numBlocks = 0; + ULONGLONG startLba = 0; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpIncrementCounters (FOG %p): Entering function.\n", + FailGroup)); + + if (Srb) { + + cdb = SrbGetCdb(Srb); + + if (cdb && DsmIsReadWrite(cdb->AsByte[0])) { + + isReadWrite = TRUE; + } + } + + InterlockedIncrement(&FailGroup->NumberOfRequestsInFlight); + + // + // Update counters that apply to read/write requests + // + if (isReadWrite) { + + bytes = SrbGetDataTransferLength(Srb); + + InterlockedExchangeAdd64((LONGLONG volatile*)&FailGroup->OutstandingBytesOfIO, bytes); + + cdbLength = SrbGetCdbLength(Srb); + + if (cdbLength == 16) { + + REVERSE_BYTES_QUAD(&startLba, &cdb->CDB16.LogicalBlock); + REVERSE_BYTES(&numBlocks, &cdb->CDB16.TransferLength); + + } else { + + REVERSE_BYTES(&startLba, &cdb->CDB10.LogicalBlockByte0); + REVERSE_BYTES_SHORT(&numBlocks, &cdb->CDB10.TransferBlocksMsb); + } + + lastLba = startLba + numBlocks - 1; + + InterlockedExchange64((LONGLONG volatile*)&FailGroup->LastLba, lastLba); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpIncrementCounters (FOG %p): Exiting function.\n", + FailGroup)); + + return; +} + + +BOOLEAN +DsmpDecrementCounters( + _In_ PDSM_FAILOVER_GROUP FailGroup, + _In_ PSCSI_REQUEST_BLOCK Srb + ) +{ + ULONG bytes = 0; + PCDB cdb = NULL; + BOOLEAN isReadWrite = FALSE; + BOOLEAN isDeletionEligible = FALSE; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpDecrementCounters (FOG %p): Entering function.\n", + FailGroup)); + + if (Srb) { + + cdb = SrbGetCdb(Srb); + + if (cdb && DsmIsReadWrite(cdb->AsByte[0])) { + + isReadWrite = TRUE; + } + } + + // + // Update counters that apply to read/write requests + // + if (isReadWrite) { + + bytes = SrbGetDataTransferLength(Srb); + + InterlockedExchangeAdd64((LONGLONG volatile*)&FailGroup->OutstandingBytesOfIO, -(LONGLONG)bytes); + } + + NT_ASSERT(FailGroup->NumberOfRequestsInFlight > 0); + if (InterlockedCompareExchange(&FailGroup->NumberOfRequestsInFlight, 0, 0) > 0) { + + if(InterlockedDecrement(&FailGroup->NumberOfRequestsInFlight) == 0){ + + // + // If the inflight requests on the path is zero, if needed path can be removed. + // + isDeletionEligible = TRUE; + } + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpDecrementCounters (FOG %p): Exiting function.\n", + FailGroup)); + + return isDeletionEligible; +} + + +PDSM_FAILOVER_GROUP +DsmpGetPath( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_IDS DsmList, + _In_ IN PSCSI_REQUEST_BLOCK Srb, + _In_ IN ULONG SpecialHandlingFlag + ) +/*++ + +Routine Description: + + This routine will pick a path, for processing a request, based + on the current LoadBalance policy that is set. + + N.B: This routine must be called with DSM Context Lock held in Shared mode. + +Arguments: + + DsmContext - DSM context given to MPIO during initialization + DsmList - List of DSM Ids sent by MPIO + Srb - The read/write/verify request + SpecialHandlingFlag - Flags to indicate any special handling requirement + +Return Value: + + FailOver Group that should be used for processing the request +--*/ +{ + // + // Algorithm: + // ========== + // Failover-only: + // -------------- + // If symmetric LUA (ie. ALUA not supported, or symmetric LUA using ALUA semantics (viz. storage reports + // implicit-only transitions and all TPGs in A/O): + // One path AO, <- this will be the only path used for I/O + // M paths in SB, <- one of these will be made active on failure of the above active path + // Rest of the paths Failed (Invalidated/PendingRemove/Removed) + // + // If ALUA: + // One path AO, <- this will be the only path used for I/O + // M paths in AU, SB or UA <- one of these made active on failover. Pref: AU > SB > UA. Also, controller affinity. + // Rest of the paths Failed + // + // Automatic failback will happen only if Preferred path has been set. + // This is the only policy that will support failback. + // + // Round-Robin: + // ------------ + // If symmetric LUA: + // N paths AO, <- round robin among these + // Rest of the paths Failed + // + // If ALUA: + // Round Robin policy not supported since all paths can't be in A/O state. + // + // Round-Robin With Subset: + // ------------------------ + // If symmetric LUA: + // N paths AO, <- round robin among these + // M paths SB, <- if no active paths left, make one of these active + // Rest of the paths Failed + // + // If ALUA: + // N paths AO, <- round robin among these (NOTE: paths in AU not considered) + // M paths AU, SB or UA <- if no active paths, make subset of these active (based on TPG states after transition) + // Rest of the paths Failed + // + // Least-Queue Depth: + // ------------------ + // If symmetric LUA: + // N paths AO, <- one with least outstanding I/O is chosen + // Rest of the paths Failed + // + // If ALUA: + // N paths AO, <- one with least outstanding I/O is chosen + // M paths AU, SB or UA <- if no AO paths available, subset of these become active (based on TPG + // states after transition) - one with least outstanding I/O is chosen. + // Rest of the paths Failed + // + // Least-Weighted: + // --------------- + // If symmetric LUA: + // N paths AO, <- every path has an associated weight, path with least weight is used. + // Rest of the paths Failed + // + // If ALUA: + // N paths AO, + // M paths AU, SB or UA <- if no AO paths available, subset of these become active (based on TPG + // states after transition) - path with least weight used. + // Rest of the paths Failed + // + // Least-Blocks: + // ------------- + // If symmetric LUA: + // N paths AO, <- one with least cumulative outstanding IO is chosen + // Rest of the paths Failed + // + // If ALUA: + // N paths AO, <- one with least cumulative outstanding IO is chosen + // M paths AU, SB or UA <- if no AO paths available, subset of these become active (based on TPG + // states after transition) - one with least cumulative outstanding is chosen. + // + // Actual implementation of algorithm happens in the following routines: DsmpGetAnyActivePath, + // DsmpGetActivePathToBeUsed, flavors of DsmpSetLBForPathXXX. + // + + PDSM_FAILOVER_GROUP failGroup = NULL; + PDSM_DEVICE_INFO deviceInfo = DsmList->IdList[0]; + PDSM_GROUP_ENTRY groupEntry; + ULONG inx = 0; + + UNREFERENCED_PARAMETER(DsmContext); + UNREFERENCED_PARAMETER(SpecialHandlingFlag); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmpGetPath (DsmIds %p): Entering function.\n", + DsmList)); + + if (!(DsmList->Count && deviceInfo)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmpGetPath (DsmIds %p): Called with no available paths.\n", + DsmList)); + + goto __Exit_DsmpGetPath; + } + + groupEntry = deviceInfo->Group; + DSM_ASSERT(groupEntry->GroupSig == DSM_GROUP_SIG); + + switch (groupEntry->LoadBalanceType) { + + case DSM_LB_FAILOVER: + case DSM_LB_WEIGHTED_PATHS: { + + // + // For FailOverOnly there is only one active path so we can + // just grab it from the cached location and go with it. + // For LeastWeightPath we always choose the lowest weighted + // one so we grab that and go + // + failGroup = groupEntry->PathToBeUsed; + + break; + } + + case DSM_LB_ROUND_ROBIN: + case DSM_LB_ROUND_ROBIN_WITH_SUBSET: { + + PDSM_DEVICE_INFO candidateDevice = NULL; + PDSM_GROUP_ENTRY newGroup = NULL; + ULONG newPath; + BOOLEAN foundPath = FALSE; + ULONG jnx = 0; + ULONG counter = 0; + BOOLEAN reset = FALSE; + + for (inx = 0; inx < DsmList->Count; inx++) { + + deviceInfo = DsmList->IdList[inx]; + + if (!(deviceInfo && DsmpIsDeviceInitialized(deviceInfo) && DsmpIsDeviceUsable(deviceInfo) && DsmpIsDeviceUsablePR(deviceInfo))) { + + continue; + } + + + if (deviceInfo->FailGroup == groupEntry->PathToBeUsed) { + + // + // We've reached the devInfo that corresponds to the path + // that we should be using. If this devInfo is not in the + // right state to be used, we need to find the first candidate + // starting from this one to satisfy the request. + // To play it safe, we may have already considered a previous + // devInfo to be a candidate, that now needs to be reset to + // the one that we now find. + // + reset = TRUE; + } + +#if DBG + if (deviceInfo->TargetPortGroup && !DsmpIsDeviceFailedState(deviceInfo->State)) { + + if (deviceInfo->State != deviceInfo->ALUAState) { + + DSM_ASSERT(groupEntry->LoadBalanceType == DSM_LB_ROUND_ROBIN_WITH_SUBSET && + deviceInfo->State == DSM_DEV_ACTIVE_UNOPTIMIZED && + deviceInfo->DesiredState != DSM_DEV_ACTIVE_OPTIMIZED); + } + } +#endif + + if (deviceInfo->State == DSM_DEV_ACTIVE_OPTIMIZED) { + + if (!candidateDevice || reset) { + + candidateDevice = deviceInfo; + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_RW, + "DsmpGetPath (DsmIds %p): Candidate device %p.\n", + DsmList, + candidateDevice)); + + jnx = inx; + } + + if (!groupEntry->PathToBeUsed || deviceInfo->FailGroup == groupEntry->PathToBeUsed) { + + // + // The devInfo that corresponds to the path that we were + // supposed to use, is in a state that makes it usable. + // So we've found our devInfo. + // + InterlockedExchangePointer(&(groupEntry->PathToBeUsed), (PVOID)deviceInfo->FailGroup); + foundPath = TRUE; + candidateDevice = NULL; + + break; + } + } + } + + if (!foundPath) { + + if (candidateDevice) { + + InterlockedExchangePointer(&(groupEntry->PathToBeUsed), (PVOID)candidateDevice->FailGroup); + inx = jnx; + candidateDevice = NULL; + + } else { + + inx = 0; + } + } + + failGroup = groupEntry->PathToBeUsed; + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_RW, + "DsmpGetPath (DsmIds %p): Path to be used is %p.\n", + DsmList, + groupEntry->PathToBeUsed)); + + // + // The current chosen path is given by failGroup. Find the next path + // that should be chosen in the RoundRobin policy. Start with the + // device at index inx + 1, and look for the one with Active state. + // + for (counter = 0, jnx = inx + 1; + counter < DsmList->Count && !newGroup; + counter++, jnx++) { + + newPath = jnx % DsmList->Count; + + deviceInfo = DsmList->IdList[newPath]; + + if (!(deviceInfo && DsmpIsDeviceInitialized(deviceInfo) && DsmpIsDeviceUsable(deviceInfo) && DsmpIsDeviceUsablePR(deviceInfo))) { + + continue; + } + + + if (deviceInfo->State == DSM_DEV_ACTIVE_OPTIMIZED) { + + newGroup = deviceInfo->Group; + DSM_ASSERT(newGroup == groupEntry); + + InterlockedExchangePointer(&(newGroup->PathToBeUsed), (PVOID)deviceInfo->FailGroup); + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_RW, + "DsmpGetPath (DsmIds %p): New Path is %p.\n", + DsmList, + newGroup->PathToBeUsed)); + + break; + } + } + + break; + } + + case DSM_LB_DYN_LEAST_QUEUE_DEPTH: { + + LONG leastQueueDepth = 0x7FFFFFFF; + + for (inx = 0; inx < DsmList->Count; inx++) { + + deviceInfo = DsmList->IdList[inx]; + + if (!(deviceInfo && DsmpIsDeviceInitialized(deviceInfo) && DsmpIsDeviceUsable(deviceInfo) && DsmpIsDeviceUsablePR(deviceInfo))) { + + continue; + } + + + if (deviceInfo->State == DSM_DEV_ACTIVE_OPTIMIZED && + deviceInfo->FailGroup->NumberOfRequestsInFlight < leastQueueDepth) { + + leastQueueDepth = deviceInfo->FailGroup->NumberOfRequestsInFlight; + failGroup = deviceInfo->FailGroup; + } + } + + if (failGroup) { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_RW, + "DsmpGetPath (DsmIds %p): Path to be used for LQD is %p.\n", + DsmList, + failGroup)); + + } else { + + // + // For ALUA storage there are two cases where we are left with no + // TPG in the A/O state: + // 1) On storage that supports implicit transitions, a transition + // was initiated that left no TPG in the A/O state. + // 2) On storage that has explicit only transitions enabled, we tried + // making at least one path as A/O and failed. This can happen, + // for example, when STPG fails because this initiator is not + // registered or does not hold exclusive reservation over the + // target. + // + // For such storages, we should return some path instead of just + // failing the I/O. The path will likely be an A/U path until the + // storage does a transition to make a TPG A/O. + // + if (!DsmpIsSymmetricAccess((PDSM_DEVICE_INFO)DsmList->IdList[0])) { + + // + // Use the same path as the one used for the previous request. + // + failGroup = groupEntry->PathToBeUsed; + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_PNP, + "DsmpGetPath (DsmIds %p): Using same path (FOG %p) as previous request for LQD.\n", + DsmList, + failGroup)); + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmpGetPath (DsmIds %p): Failed to find a path for LQD.\n", + DsmList)); + } + } + + break; + } + + case DSM_LB_LEAST_BLOCKS: { + + ULONG bytes = 0; + PCDB cdb = NULL; + ULONG cdbLength = 0; + BOOLEAN isRead = FALSE; + BOOLEAN isWrite = FALSE; + PDSM_FAILOVER_GROUP lastPathUsed = groupEntry->PathToBeUsed; + ULONGLONG leastOutstandingIO = MAXULONGLONG; + ULONGLONG startLba = 0; + + // + // Use the last path under the following conditions: + // + // 1. This is not a read/write request or + // 2. This is a read/write request and + // a. The request is sequential and + // b. The cache is not exhausted + // + + if (Srb) { + + cdb = SrbGetCdb(Srb); + + if (cdb && DsmIsReadRequest(cdb->AsByte[0])) { + + isRead = TRUE; + } + + if (cdb && DsmIsWriteRequest(cdb->AsByte[0])) { + + isWrite = TRUE; + } + } + + if (isRead || isWrite) { + + if (groupEntry->UseCacheForLeastBlocks) { + + bytes = SrbGetDataTransferLength(Srb); + + cdbLength = SrbGetCdbLength(Srb); + + if (cdbLength == 16) { + + REVERSE_BYTES_QUAD(&startLba, &cdb->CDB16.LogicalBlock); + + } else { + + REVERSE_BYTES(&startLba, &cdb->CDB10.LogicalBlockByte0); + } + + // + // Check if: + // 1. The IO is sequential, AND + // 2. It is either: + // a. read request, OR + // b. write request and outstanding bytes will be within the cache limit + // + if ((lastPathUsed != NULL) && + (startLba >= lastPathUsed->LastLba) && + ((isRead) || + (isWrite && lastPathUsed->OutstandingBytesOfIO + bytes <= groupEntry->CacheSizeForLeastBlocks))) { + + failGroup = groupEntry->PathToBeUsed; + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_RW, + "DsmpGetPath (DsmIds %p): Sequential IO, so using same path %p for LeastBlocks.\n", + DsmList, + failGroup)); + } + } + + } else { + // + // The request is neither a read nor a write so use the same path. + // + failGroup = groupEntry->PathToBeUsed; + } + + if (!failGroup) { + + // + // Choose whichever Active/Optimized path has the least outstanding bytes. + // + for (inx = 0; inx < DsmList->Count; inx++) { + + deviceInfo = DsmList->IdList[inx]; + + if (!(deviceInfo && DsmpIsDeviceInitialized(deviceInfo) && DsmpIsDeviceUsable(deviceInfo) && DsmpIsDeviceUsablePR(deviceInfo))) { + + continue; + } + + + if (deviceInfo->State == DSM_DEV_ACTIVE_OPTIMIZED && + deviceInfo->FailGroup->OutstandingBytesOfIO < leastOutstandingIO) { + + leastOutstandingIO = deviceInfo->FailGroup->OutstandingBytesOfIO; + failGroup = deviceInfo->FailGroup; + } + } + } + + if (failGroup) { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_RW, + "DsmpGetPath (DsmIds %p): Path to be used for LeastBlocks is %p.\n", + DsmList, + failGroup)); + + } else { + + // + // For ALUA storage there are two cases where we are left with no + // TPG in the A/O state: + // 1) On storage that supports implicit transitions, a transition + // was initiated that left no TPG in the A/O state. + // 2) On storage that has explicit only transitions enabled, we tried + // making at least one path as A/O and failed. This can happen, + // for example, when STPG fails because this initiator is not + // registered or does not hold exclusive reservation over the + // target. + // + // For such storages, we should return some path instead of just + // failing the I/O. The path will likely be an A/U path until the + // storage does a transition to make a TPG A/O. + // + if (!DsmpIsSymmetricAccess((PDSM_DEVICE_INFO)DsmList->IdList[0])) { + + // + // Use the same path as the one used for the previous request. + // + failGroup = groupEntry->PathToBeUsed; + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_PNP, + "DsmpGetPath (DsmIds %p): Using same path (FOG %p) as previous request for LeastBlocks.\n", + DsmList, + failGroup)); + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmpGetPath (DsmIds %p): Failed to find a path for LeastBlocks.\n", + DsmList)); + } + } + + break; + } + + default: { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmpGetPath (DsmIds %p): Invalid LB Type %d set for group %p.\n", + DsmList, + groupEntry->LoadBalanceType, + groupEntry)); + + DSM_ASSERT(FALSE); + + break; + } + } + +__Exit_DsmpGetPath: + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmpGetPath (DsmIds %p): Exiting function with failGroup %p.\n", + DsmList, + failGroup)); + + return failGroup; +} + + +PVOID +DsmpGetPathIdFromPassThroughPath( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_IDS DsmList, + _In_ PIRP Irp, + _Inout_ IN OUT NTSTATUS *Status + ) +/*++ + +Routine Description: + + This routine will pick the path that corresponds to the PathId + in the mpio pass through structure. + + NOTE: Caller must ensure that the IRP is either MPTP or MPTPD. + +Arguments: + + DsmContext - DSM context given to MPIO during initialization + DsmList - List of DSM Ids sent by MPIO + Irp - The MPTP or MPTPD request + Status - Returned status + +Return Value: + + The PathId to which the request should be sent +--*/ +{ + PDSM_FAILOVER_GROUP failGroup = NULL; + PDSM_GROUP_ENTRY groupEntry; + PDSM_DEVICE_INFO deviceInfo; + ULONG inx = 0; + NTSTATUS status = STATUS_INVALID_PARAMETER; + KIRQL irql; + PVOID newPath = NULL; + BOOLEAN found = FALSE; + BOOLEAN useScsiAddress = FALSE; + PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); + ULONG controlCode = irpStack->Parameters.DeviceIoControl.IoControlCode; + UCHAR pathId = 0; + UCHAR targetId = 0; + UCHAR portNumber = 0; + ULONGLONG mpioPathId = 0; + +#if DBG + BOOLEAN useMpioPathId = FALSE; +#endif + + // + // Extract the parameters from the passthrough based on the bitness of the + // process (32 or 64) and the type of passthrough (legacy or extended). + // +#if defined (_WIN64) + if (IoIs32bitProcess(Irp)) { + + if (DsmpIsMPIOPassThroughEx(controlCode)) { + PMPIO_PASS_THROUGH_PATH32_EX mpioPassThroughPath32 = (PMPIO_PASS_THROUGH_PATH32_EX)(Irp->AssociatedIrp.SystemBuffer); + PSCSI_PASS_THROUGH32_EX passThrough32 = (PSCSI_PASS_THROUGH32_EX)((PUCHAR)mpioPassThroughPath32 + mpioPassThroughPath32->PassThroughOffset); + + useScsiAddress = mpioPassThroughPath32->Flags & MPIO_IOCTL_FLAG_USE_SCSIADDRESS; + #if DBG + useMpioPathId = mpioPassThroughPath32->Flags & MPIO_IOCTL_FLAG_USE_PATHID; + #endif + + if (useScsiAddress) { + PSTOR_ADDRESS address; + if (passThrough32->StorAddressOffset < sizeof(SCSI_PASS_THROUGH_EX) || + passThrough32->StorAddressLength < sizeof(STOR_ADDRESS)) { + *Status = STATUS_INVALID_PARAMETER; + return NULL; + } + address = (PSTOR_ADDRESS)((PUCHAR)passThrough32 + passThrough32->StorAddressOffset); + if (address->Type != STOR_ADDRESS_TYPE_BTL8 || + address->AddressLength < STOR_ADDR_BTL8_ADDRESS_LENGTH) { + *Status = STATUS_INVALID_PARAMETER; + return NULL; + } + pathId = ((PSTOR_ADDR_BTL8)address)->Path; + targetId = ((PSTOR_ADDR_BTL8)address)->Target; + portNumber = mpioPassThroughPath32->PortNumber; + } else { + mpioPathId = mpioPassThroughPath32->MpioPathId; + } + + } else { + PMPIO_PASS_THROUGH_PATH32 mpioPassThroughPath32 = (PMPIO_PASS_THROUGH_PATH32)(Irp->AssociatedIrp.SystemBuffer); + + useScsiAddress = mpioPassThroughPath32->Flags & MPIO_IOCTL_FLAG_USE_SCSIADDRESS; + #if DBG + useMpioPathId = mpioPassThroughPath32->Flags & MPIO_IOCTL_FLAG_USE_PATHID; + #endif + + if (useScsiAddress) { + pathId = mpioPassThroughPath32->PassThrough.PathId; + targetId = mpioPassThroughPath32->PassThrough.TargetId; + portNumber = mpioPassThroughPath32->PortNumber; + } else { + mpioPathId = mpioPassThroughPath32->MpioPathId; + } + } + } else +#endif + if (DsmpIsMPIOPassThroughEx(controlCode)) { + PMPIO_PASS_THROUGH_PATH_EX mpioPassThroughPath = (PMPIO_PASS_THROUGH_PATH_EX)(Irp->AssociatedIrp.SystemBuffer); + PSCSI_PASS_THROUGH_EX passThrough = (PSCSI_PASS_THROUGH_EX)((PUCHAR)mpioPassThroughPath + mpioPassThroughPath->PassThroughOffset); + + useScsiAddress = mpioPassThroughPath->Flags & MPIO_IOCTL_FLAG_USE_SCSIADDRESS; + #if DBG + useMpioPathId = mpioPassThroughPath->Flags & MPIO_IOCTL_FLAG_USE_PATHID; + #endif + + if (useScsiAddress) { + PSTOR_ADDRESS address; + if (passThrough->StorAddressOffset < sizeof(SCSI_PASS_THROUGH_EX) || + passThrough->StorAddressLength < sizeof(STOR_ADDRESS)) { + *Status = STATUS_INVALID_PARAMETER; + return NULL; + } + address = (PSTOR_ADDRESS)((PUCHAR)passThrough + passThrough->StorAddressOffset); + if (address->Type != STOR_ADDRESS_TYPE_BTL8 || + address->AddressLength < STOR_ADDR_BTL8_ADDRESS_LENGTH) { + *Status = STATUS_INVALID_PARAMETER; + return NULL; + } + pathId = ((PSTOR_ADDR_BTL8)address)->Path; + targetId = ((PSTOR_ADDR_BTL8)address)->Target; + portNumber = mpioPassThroughPath->PortNumber; + } else { + mpioPathId = mpioPassThroughPath->MpioPathId; + } + } else { + PMPIO_PASS_THROUGH_PATH mpioPassThroughPath = (PMPIO_PASS_THROUGH_PATH)(Irp->AssociatedIrp.SystemBuffer); + + useScsiAddress = mpioPassThroughPath->Flags & MPIO_IOCTL_FLAG_USE_SCSIADDRESS; + #if DBG + useMpioPathId = mpioPassThroughPath->Flags & MPIO_IOCTL_FLAG_USE_PATHID; + #endif + + if (useScsiAddress) { + pathId = mpioPassThroughPath->PassThrough.PathId; + targetId = mpioPassThroughPath->PassThrough.TargetId; + portNumber = mpioPassThroughPath->PortNumber; + } else { + mpioPathId = mpioPassThroughPath->MpioPathId; + } + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmpGetPathIdFromPassThroughPath (DsmIds %p): Entering function.\n", + DsmList)); + + irql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); + + deviceInfo = DsmList->IdList[0]; + groupEntry = deviceInfo->Group; + DSM_ASSERT(groupEntry->GroupSig == DSM_GROUP_SIG); + // + // useMpioPathId is BOOLEAN (0 or 1) since MPIO_IOCTL_FLAG_USE_PATHID = 1 + // But since MPIO_IOCTL_FLAG_USE_SCSIADDRESS = 0x2, + // useScsiAddress could have a value of 2 if set. Use logical NOT to make boolean before comparing below + // + DSM_ASSERT(useMpioPathId == !useScsiAddress); + + for (inx = 0; inx < DSM_MAX_PATHS; inx++) { + + deviceInfo = groupEntry->DeviceList[inx]; + + if (deviceInfo) { + + failGroup = deviceInfo->FailGroup; + + if (failGroup) { + + if (useScsiAddress) { + + if (portNumber == deviceInfo->ScsiAddress->PortNumber && + pathId == deviceInfo->ScsiAddress->PathId && + targetId == deviceInfo->ScsiAddress->TargetId) { + + found = TRUE; + break; + } + } else { + + NT_ASSERT(useMpioPathId); + + if ((ULONGLONG)((ULONG_PTR)(failGroup->PathId)) == mpioPathId) { + + found = TRUE; + break; + } + } + } + } + } + + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); + + if (found) { + + newPath = failGroup->PathId; + status = STATUS_SUCCESS; + + // + // This should not affect the next path chosen based on the + // current LB policy, so do NOT update groupEntry->PathToBeUsed + // + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmpGetPathIdFromPassThroughPath (DsmIds %p): Failed to get corresponding path.\n", + DsmList)); + } + + if (Status) { + + *Status = status; + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmpGetPathIdFromPassThroughPath (DsmIds %p): Exiting function with path %p and status %x.\n", + DsmList, + newPath, + status)); + + return newPath; +} + + +BOOLEAN +DsmpShouldRetryTPGRequest( + _In_ IN PVOID SenseData, + _In_ IN UCHAR SenseDataSize + ) +/*++ + +Routine Description: + + This routine determines if a Report/Set TargetPortGroup request (sent either + as a passThrough or as an IRP_MJ_SCSI) needs to be retried. + +Arguments: + + SenseData - Pointer to Sense Data information buffer. + SenseDataSize - Size of the passed in sense data buffer. + +Return Value: + + TRUE if sense information indicates a retry-able error, else FALSE. + +--*/ +{ + BOOLEAN retry = FALSE; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpShouldRetryTPGRequest (SenseData %p): Entering function.\n", + SenseData)); + + // + // Two types of conditions need to be retried: + // 1. Asymmetric Access State Changed + // 2. Asymmetric Access State Transition + // + + // + // Check if asymmetric access state changed + // + retry = DsmpShouldRetryPassThroughRequest(SenseData, SenseDataSize); + if (!retry) { + + BOOLEAN validSense = FALSE; + UCHAR senseKey = 0; + UCHAR addSenseCode = 0; + UCHAR addSenseCodeQualifier = 0; + + validSense = ScsiGetSenseKeyAndCodes(SenseData, + SenseDataSize, + SCSI_SENSE_OPTIONS_FIXED_FORMAT_IF_UNKNOWN_FORMAT_INDICATED, + &senseKey, + &addSenseCode, + &addSenseCodeQualifier); + if (validSense) { + + if (senseKey == SCSI_SENSE_NOT_READY) { + + switch (addSenseCode) { + case SCSI_ADSENSE_LUN_NOT_READY: { + + // + // Check if asymmetric access state transitioning + // + if (addSenseCodeQualifier == SPC3_SCSI_SENSEQ_ASYMMETRIC_ACCESS_STATE_TRANSITION) { + + retry = TRUE; + } + break; + } + + case SCSI_ADSENSE_OPERATING_CONDITIONS_CHANGED: { + + if (addSenseCodeQualifier == SCSI_SENSEQ_REPORTED_LUNS_DATA_CHANGED) { + + retry = TRUE; + } + break; + } + + default: { + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_GENERAL, + "DsmpShouldRetryTPGRequest (SenseData %p): AddSenseCode %x. Not retrying.\n", + SenseData, + addSenseCode)); + + retry = FALSE; + break; + } + } + } + } else { + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpShouldRetryTPGRequest (SenseData %p): Sense data size %d not big enough.\n", + SenseData, + SenseDataSize)); + } + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpShouldRetryTPGRequest (SenseData %p): Exiting function with retry %x.\n", + SenseData, + retry)); + + return retry; +} + + +BOOLEAN +DsmpIsDeviceRemoved( + _In_ IN PVOID SenseData, + _In_ IN UCHAR SenseDataSize + ) +/*++ + +Routine Description: + + This routine evaluate Sense Data and determine if LUN is available or not. + +Arguments: + + SenseData - Pointer to Sense Data information buffer. + SenseDataSize - Size of the passed in sense data buffer. + +Return Value: + + TRUE if device is no longer available, else FALSE. + +--*/ +{ + BOOLEAN validSense = FALSE; + UCHAR senseKey = 0; + UCHAR addSenseCode = 0; + UCHAR addSenseCodeQualifier = 0; + BOOLEAN bRemoved = FALSE; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpIsDeviceRemoved (SenseData %p): Entering function.\n", + SenseData)); + + validSense = ScsiGetSenseKeyAndCodes(SenseData, + SenseDataSize, + SCSI_SENSE_OPTIONS_FIXED_FORMAT_IF_UNKNOWN_FORMAT_INDICATED, + &senseKey, + &addSenseCode, + &addSenseCodeQualifier); + + if (validSense) { + // + // SPC 3 6.25 suggests response should follow Test Unit Ready responses + // For now, we accept Ileegal Request as an indication of device not in available + // state. + // + if (senseKey == SCSI_SENSE_ILLEGAL_REQUEST) { + + ASSERT(addSenseCodeQualifier == 0); //LOGICAL UNIT NOT SUPPORTED + + bRemoved = TRUE; + } + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_GENERAL, + "DsmpIsDeviceRemoved (SenseData %p): SenseKey %x AddSenseCode %x. Remove %x\n", + SenseData, + senseKey, + addSenseCode, + bRemoved)); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpIsDeviceRemoved (SenseData %p): Exiting function. Removed %x.\n", + SenseData, + bRemoved)); + + return bRemoved; +} + + +BOOLEAN +DsmpReservationCommand( + _In_ IN PIRP Irp, + _In_ IN PSCSI_REQUEST_BLOCK Srb + ) +/*++ + +Routine Description: + + This routine examines the DeviceIoControlCode and Srb OpCode to determine + if this is PR request. + +Arguments: + + Irp - The Irp containing Srb. + Srb - The current non-read/write Srb. + +Return Value: + + TRUE - If it's a special-case command (some reservation-handling request). + +--*/ +{ + PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); + UCHAR opCode = 0; + BOOLEAN isReservationCommand = FALSE; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_IOCTL, + "DsmpReservationCommand (Irp %p): Entering function.\n", + Irp)); + + // + // Ensure it's a scsi request before checking the opcode. + // + if (irpStack->MajorFunction == IRP_MJ_SCSI) { + + PCDB cdb = SrbGetCdb(Srb); + if (cdb != NULL) { + opCode = cdb->AsByte[0]; + + if (opCode == SCSIOP_PERSISTENT_RESERVE_IN || opCode == SCSIOP_PERSISTENT_RESERVE_OUT) { + + // + // Set or release a reservation. + // + isReservationCommand = TRUE; + } + } + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_IOCTL, + "DsmpReservationCommand (Irp %p): Exiting function - IsReservationCmd %x.\n", + Irp, + isReservationCommand)); + + return isReservationCommand; +} + + +BOOLEAN +DsmpMpioPassThroughPathCommand( + _In_ IN PIRP Irp + ) +/*++ + +Routine Description: + + This routine examines the DeviceIoControlCode to determine whether this is + either a mpio pass through or a mpio pass through direct. If so, it needs + to be handled via a specific path indicated by the caller. + +Arguments: + + Irp - The Irp. + +Return Value: + + TRUE - If it is either MPTP or MPTPD. + FALSE - Otherwise. + +--*/ +{ + PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); + ULONG ioctlCode; + BOOLEAN isMPTPCommand = FALSE; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_IOCTL, + "DsmpMpioPassThroughPathCommand (Irp %p): Entering function.\n", + Irp)); + + if (irpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL) { + + // + // Check whether this is a MPTP, MPTPD, or an extended flavor. + // + ioctlCode = irpStack->Parameters.DeviceIoControl.IoControlCode; + + if (ioctlCode == IOCTL_MPIO_PASS_THROUGH_PATH || + ioctlCode == IOCTL_MPIO_PASS_THROUGH_PATH_DIRECT || + ioctlCode == IOCTL_MPIO_PASS_THROUGH_PATH_EX || + ioctlCode == IOCTL_MPIO_PASS_THROUGH_PATH_DIRECT_EX) { + + isMPTPCommand = TRUE; + } + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_IOCTL, + "DsmpMpioPassThroughPathCommand (Irp %p): Exiting function - IsMpioPassThruPathCmd %!bool!.\n", + Irp, + isMPTPCommand)); + + return isMPTPCommand; +} + + +VOID +DsmpRequestComplete( + _In_ IN PVOID DsmId, + _In_ IN PIRP Irp, + _In_ IN PSCSI_REQUEST_BLOCK Srb, + _In_ IN PVOID DsmContext + ) +/*++ + +Routine Description: + + This routine is called from mpio's completion routine when the Irp + has been completed by the port driver. Currently, it updates some counters + and free's the context back to the look-aside list. + +Arguments: + + DsmIds - The collection of DSM IDs that pertain to the MPDISK. + Irp - Irp containing SRB. + Srb - Scsi request block + DsmContext - DSM context given to MPIO during initialization + +Return Value: + + NONE + +--*/ + +{ + PDSM_DEVICE_INFO deviceInfo = DsmId; + PDSM_CONTEXT dsmContext = (PDSM_CONTEXT)DsmContext; + UCHAR opCode = 0xFF; + ULONG dataTransferLength = 0; + PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); + PDSM_FAILOVER_GROUP failGroup = irpStack->Parameters.Others.Argument3; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmpRequestComplete (DevInfo %p): Entering function.\n", + DsmId)); + + DSM_ASSERT(DsmContext); + + if (Srb) { + PCDB cdb = SrbGetCdb(Srb); + if (cdb) { + opCode = cdb->AsByte[0]; + } + dataTransferLength = SrbGetDataTransferLength(Srb); + } + + // + // Extract the interesting bits from the context struct. + // + + if (failGroup) { + + if (DsmpDecrementCounters(failGroup, Srb)) { + + // + // If there are no requests on a path that is supposed to be removed, remove it now. + // + if (failGroup->State == DSM_FG_PENDING_REMOVE) { + + KIRQL oldIrql; + + NT_ASSERT(failGroup->Count == 0); + + oldIrql = ExAcquireSpinLockExclusive(&(dsmContext->DsmContextLock)); + RemoveEntryList(&failGroup->ListEntry); + InterlockedDecrement((LONG volatile*)&dsmContext->NumberStaleFOGroups); + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpRequestComplete (DevInfo %p): Removing FOGroup %p with path %p.\n", + DsmId, + failGroup, + failGroup->PathId)); + + DsmpFreePool(failGroup); + ExReleaseSpinLockExclusive(&(dsmContext->DsmContextLock), oldIrql); + } + } + } + + // + // Note: We use the deviceInfo passed in since the one saved off in the + // context may be stale in the case of a retried I/O + // + if (deviceInfo) { + + // + // If statistics gathering is enabled update the inflight request count + // for this device-path pairing. + // + if (!dsmContext->DisableStatsGathering) { + + // + // Indicate one less request on this device. + // Update the path that on which the increment was done. + // + if (InterlockedCompareExchange((LONG volatile*)&deviceInfo->NumberOfRequestsInProgress, 0, 0) > 0) { + InterlockedDecrement(&(deviceInfo->NumberOfRequestsInProgress)); + } + } + + // + // If statistics gathering is enabled, we are interested in read/write requests + // + if (!dsmContext->DisableStatsGathering) { + + // + // If it's a read or a write, update the stats. + // Use the path that was cached during dispatch. + // + if (DsmIsReadRequest(opCode)) { + + if (deviceInfo->DeviceStats.NumberReads <= MAXULONG) { + + InterlockedIncrement((LONG volatile*)&deviceInfo->DeviceStats.NumberReads); + } + + if ((MAXULONGLONG - dataTransferLength) > deviceInfo->DeviceStats.BytesRead) { + + deviceInfo->DeviceStats.BytesRead += dataTransferLength; + + } else { + + deviceInfo->DeviceStats.BytesRead = MAXULONGLONG; + } + + } else if (DsmIsWriteRequest(opCode)) { + + if (deviceInfo->DeviceStats.NumberWrites <= MAXULONG) { + + InterlockedIncrement((LONG volatile*)&deviceInfo->DeviceStats.NumberWrites); + } + + if ((MAXULONGLONG - dataTransferLength) > deviceInfo->DeviceStats.BytesWritten) { + + deviceInfo->DeviceStats.BytesWritten += dataTransferLength; + + } else { + + deviceInfo->DeviceStats.BytesWritten = MAXULONGLONG; + } + } + } + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmpRequestComplete (DevInfo %p): Exiting function.\n", + DsmId)); + + return; +} + + +NTSTATUS +DsmpRegisterPersistentReservationKeys( + _In_ IN PDSM_DEVICE_INFO DeviceInfo, + _In_ IN BOOLEAN Register + ) +/*++ + +Routine Description: + + This routine is used to build and send down the request to register + or unregister the persistent reservation keys to the device down + the path given by DeviceInfo. + +Arguments: + + DeviceInfo - Device-path pair to use for sending down the request + Register - Flag to indicate whether to register or unregister the keys. + +Return Value: + + STATUS_SUCCESS on success, else appropriate failure code. + +--*/ +{ + PSCSI_PASS_THROUGH_WITH_BUFFERS passThrough = NULL; + PCDB cdb; + PPRO_PARAMETER_LIST parameters; + IO_STATUS_BLOCK ioStatus; + NTSTATUS status = STATUS_SUCCESS; + ULONG length; + PDSM_DEVICE_INFO deviceInfo = DeviceInfo; + PDSM_GROUP_ENTRY group; + ULONGLONG saKey; + + PAGED_CODE(); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_IOCTL, + "DsmpRegisterPersistentReservationKeys (DevInfo %p): Entering function - Register = %x.\n", + deviceInfo, + Register)); + + group = DeviceInfo->Group; + + NT_ASSERT(group && group->PRKeyValid); + + if (DeviceInfo->State >= DSM_DEV_FAILED) { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_IOCTL, + "DsmpRegisterPersistentReservationKeys (DevInfo %p): Unusable - state %d.\n", + deviceInfo, + deviceInfo->State)); + + status = STATUS_UNSUCCESSFUL; + goto __Exit_DsmpRegisterPersistentReservationKeys; + } + + // + // Build a pass through command to process Persistent Reserve Out + // for registering the device. + // + length = sizeof(SCSI_PASS_THROUGH_WITH_BUFFERS); + + passThrough = DsmpAllocatePool(NonPagedPoolNx, + length, + DSM_TAG_PASS_THRU); + if (!passThrough) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_IOCTL, + "DsmpRegisterPersistentReservationKeys (DevInfo %p): Failed to allocate memory for persistent reserve.\n", + deviceInfo)); + + status = STATUS_INSUFFICIENT_RESOURCES; + goto __Exit_DsmpRegisterPersistentReservationKeys; + } + + REVERSE_BYTES_QUAD(&saKey, &group->PersistentReservationRegisteredKey); + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_IOCTL, + "DsmpRegisterPersistentReservationKeys (DevInfo %p): Attempting PR-Out SA %u, Type %u, Scope %u, PR-Key %I64x.\n", + deviceInfo, + group->PRServiceAction, + group->PRType, + group->PRScope, + saKey)); + +__RetryRequest: + + // + // Build the cdb to reserve the device (Logical Unit). The type of reservation + // scope and service action is whatever cluster service provided at the time of + // sending down registration to this device before this particular path was available. + // + cdb = (PCDB) passThrough->ScsiPassThrough.Cdb; + cdb->PERSISTENT_RESERVE_OUT.OperationCode = SCSIOP_PERSISTENT_RESERVE_OUT; + cdb->PERSISTENT_RESERVE_OUT.ServiceAction = group->PRServiceAction; + cdb->PERSISTENT_RESERVE_OUT.Scope = group->PRScope; + cdb->PERSISTENT_RESERVE_OUT.Type = group->PRType; + cdb->PERSISTENT_RESERVE_OUT.ParameterListLength[1] = sizeof(PRO_PARAMETER_LIST); + + passThrough->ScsiPassThrough.Length = sizeof(SCSI_PASS_THROUGH); + passThrough->ScsiPassThrough.CdbLength = 10; + passThrough->ScsiPassThrough.SenseInfoLength = SPTWB_SENSE_LENGTH; + passThrough->ScsiPassThrough.DataIn = 0; + passThrough->ScsiPassThrough.DataTransferLength = sizeof(PRO_PARAMETER_LIST); + passThrough->ScsiPassThrough.TimeOutValue = 20; + passThrough->ScsiPassThrough.SenseInfoOffset = FIELD_OFFSET(SCSI_PASS_THROUGH_WITH_BUFFERS, SenseInfoBuffer); + passThrough->ScsiPassThrough.DataBufferOffset = FIELD_OFFSET(SCSI_PASS_THROUGH_WITH_BUFFERS, DataBuffer); + + parameters = (PPRO_PARAMETER_LIST)(passThrough->DataBuffer); + + // + // Copy the persistent reservation key given by cluster service to + // Service Action Reservation Key. This key will be registered + // with the device. + // + // Set ServiceActionReservationKey to the well-known key if we are registering. + // Note that to unregister ServiceActionReservationKey needs to be set to 0. + // + if (Register) { + + RtlCopyMemory(parameters->ServiceActionReservationKey, group->PersistentReservationRegisteredKey, 8); + + } else { + + RtlCopyMemory(parameters->ReservationKey, group->PersistentReservationRegisteredKey, 8); + RtlZeroMemory(parameters->ServiceActionReservationKey, 8); + } + + DsmSendDeviceIoControlSynchronous(IOCTL_SCSI_PASS_THROUGH, + DeviceInfo->TargetObject, + passThrough, + passThrough, + length, + length, + FALSE, + &ioStatus); + + status = ioStatus.Status; + + if ((passThrough->ScsiPassThrough.ScsiStatus == SCSISTAT_GOOD) && (NT_SUCCESS(ioStatus.Status))) { + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_IOCTL, + "DsmpRegisterPersistentReservationKeys (DevInfo %p): Persistent Reserve (Register Key) succeeded using %p.\n", + deviceInfo, + DeviceInfo)); + + } else { + + PUCHAR senseData; + UCHAR senseInfoLength; + + senseData = (PUCHAR)(passThrough->SenseInfoBuffer); + senseInfoLength = passThrough->ScsiPassThrough.SenseInfoLength; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_IOCTL, + "DsmpRegisterPersistentReservationKeys (DevInfo %p): DevInfo %p, Register keys (%d): NTStatus %x, ScsiStatus %x.\n", + deviceInfo, + DeviceInfo, + Register, + ioStatus.Status, + passThrough->ScsiPassThrough.ScsiStatus)); + + if (DsmpShouldRetryPassThroughRequest((PVOID)senseData, senseInfoLength)) { + + length = sizeof(SCSI_PASS_THROUGH_WITH_BUFFERS); + + RtlZeroMemory(passThrough, length); + + goto __RetryRequest; + + } else if (NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_IOCTL, + "DsmpRegisterPersistentReservationKeys (DevInfo %p): Will change success to error status for register\n", + deviceInfo)); + + status = STATUS_INVALID_DEVICE_REQUEST; + } + } + + // + // Free the passthrough + data buffer. + // + DsmpFreePool(passThrough); + +__Exit_DsmpRegisterPersistentReservationKeys: + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_IOCTL, + "DsmpRegisterPersistentReservationKeys (DevInfo %p): Exiting function with status %x.\n", + DeviceInfo, + status)); + + return status; +} + + + +BOOLEAN +DsmpShouldRetryPassThroughRequest( + _In_ IN PVOID SenseData, + _In_ IN UCHAR SenseDataSize + ) +/*++ + +Routine Description: + + This routine determines if a passthrough request needs to be retried based on the + information in the passed in sense data. + +Arguments: + + SenseData - Pointer to Sense Data information buffer. + SenseDataSize - Size of the passed in sense data buffer. + +Return Value: + + TRUE if sense information indicates a retry-able error, else FALSE. + +--*/ +{ + BOOLEAN validSense = FALSE; + UCHAR senseKey = 0; + UCHAR addSenseCode = 0; + BOOLEAN retry = FALSE; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpShouldRetryPassThroughRequest (SenseData %p): Entering function.\n", + SenseData)); + +#if DBG + if (SenseDataSize > 0) { + + ULONG inx; + PUCHAR senseInfo; + + + senseInfo = (PUCHAR) SenseData; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpShouldRetryPassThroughRequest (SenseData %p): Sense info length %d. Sense Info : ", + SenseData, + SenseDataSize)); + + for (inx = 0; inx < SenseDataSize; inx++) { + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "%x ", + senseInfo[inx])); + } + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "\n")); + } +#endif + + validSense = ScsiGetSenseKeyAndCodes(SenseData, + SenseDataSize, + SCSI_SENSE_OPTIONS_FIXED_FORMAT_IF_UNKNOWN_FORMAT_INDICATED, + &senseKey, + &addSenseCode, + NULL); + if (validSense) { + if (senseKey == SCSI_SENSE_UNIT_ATTENTION) { + + switch (addSenseCode) { + case SCSI_ADSENSE_OPERATING_CONDITIONS_CHANGED: + case SCSI_ADSENSE_BUS_RESET: + case SCSI_ADSENSE_PARAMETERS_CHANGED: { + retry = TRUE; + break; + } + + default: { + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_GENERAL, + "DsmpShouldRetryPassThroughRequest (SenseData %p): AddSenseCode %x. Not retrying.\n", + SenseData, + addSenseCode)); + + retry = FALSE; + break; + } + } + } + } else { + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpShouldRetryPassThroughRequest (SenseData %p): Sense data size %d not big enough.\n", + SenseData, + SenseDataSize)); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpShouldRetryPassThroughRequest (SenseData %p): Exiting function with retry %x.\n", + SenseData, + retry)); + + return retry; +} + + +BOOLEAN +DsmpShouldRetryPersistentReserveCommand( + _In_ IN PVOID SenseData, + _In_ IN UCHAR SenseDataSize + ) +/*++ + +Routine Description: + + This routine determines if a a PR request needs to be retried based on the + information in the passed in sense data. + +Arguments: + + SenseData - Pointer to Sense Data information buffer. + SenseDataSize - Size of the passed in sense data buffer. + +Return Value: + + TRUE if sense information indicates a retry-able error, else FALSE. + +--*/ +{ + BOOLEAN retry = FALSE; + BOOLEAN validSense = FALSE; + UCHAR senseKey = 0; + UCHAR addSenseCode = 0; + UCHAR addSenseCodeQualifier = 0; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpShouldRetryPersistentReserveCommand (SenseData %p): Entering function.\n", + SenseData)); + + retry = DsmpShouldRetryPassThroughRequest(SenseData, SenseDataSize); + + if (!retry) { + validSense = ScsiGetSenseKeyAndCodes(SenseData, + SenseDataSize, + SCSI_SENSE_OPTIONS_FIXED_FORMAT_IF_UNKNOWN_FORMAT_INDICATED, + &senseKey, + &addSenseCode, + &addSenseCodeQualifier); + if (validSense) { + + // + // If the TPG is in transitioning state, retry the request + // + if ((senseKey == SCSI_SENSE_UNIT_ATTENTION || senseKey == SCSI_SENSE_NOT_READY) && + (addSenseCode == SCSI_ADSENSE_LUN_NOT_READY && + addSenseCodeQualifier == SPC3_SCSI_SENSEQ_ASYMMETRIC_ACCESS_STATE_TRANSITION)) { + + retry = TRUE; + } + } else { + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpShouldRetryPersistentReserveCommand (SenseData %p): Sense data size %d not big enough.\n", + SenseData, + SenseDataSize)); + } + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpShouldRetryPersistentReserveCommand (SenseData %p): Exiting function with retry %x.\n", + SenseData, + retry)); + + return retry; +} + + +VOID +DsmpAllowStandbyPathsToRest( + _In_ PDSM_GROUP_ENTRY Group + ) +/*++ + +Routine Description: + + This routine is called when a new path is available for a device + and has a desired state of ACTIVE_O. Since this will be an ACTIVE_O + path we see if there are any paths with a desired state of Standby + but that are currently active. These paths can be safely moved by + to standby. + + This routine assumes that the lock is held + +Arguements: + + Group is the multipath group + +Return Value: + + None +--*/ +{ + PDSM_DEVICE_INFO existingDeviceInfo; + ULONG inx; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpAllowStandbyPathsToRest (Group %p): Entering function.\n", + Group)); + + for (inx = 0; inx < Group->NumberDevices; inx++) { + + existingDeviceInfo = Group->DeviceList[inx]; + + if ((existingDeviceInfo->DesiredState == DSM_DEV_STANDBY) && + (existingDeviceInfo->State == DSM_DEV_ACTIVE_OPTIMIZED)) { + + existingDeviceInfo->State = DSM_DEV_STANDBY; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpAllowStandbyPathsToRest (Group %p): DevInfo %p changed to state %d at %d\n", + Group, + existingDeviceInfo, + existingDeviceInfo->State, + __LINE__)); + } + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpAllowStandbyPathsToRest (Group %p): Exiting function.\n", + Group)); + return; +} + + +PDSM_DEVICE_INFO +DsmpGetAnyActivePath( + _In_ PDSM_GROUP_ENTRY Group, + _In_ BOOLEAN Exception, + _In_opt_ PDSM_DEVICE_INFO DeviceInfo, + _In_ IN ULONG SpecialHandlingFlag + ) +/*++ + +Routine Description: + + This routine will return an active path from the list + + This routine assumes that the DSM lock is held + +Arguements: + + Group is the multipath group + Exception - if TRUE, indicates that the returned devInfo must not be the same + as the one passed in. + DeviceInfo - the must-not-match devInfo. Valid parameter only if Exception is + TRUE. + SpecialHandlingFlag - Flags to indicate any special handling requirement + +Return Value: + + active path or NULL + +--*/ +{ + PDSM_DEVICE_INFO existingDeviceInfo; + PDSM_DEVICE_INFO candidateDevInfo = NULL; + ULONG inx; + + UNREFERENCED_PARAMETER(SpecialHandlingFlag); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpGetAnyActivePath (Group %p): Entering function.\n", + Group)); + + for (inx = 0; inx < DSM_MAX_PATHS; inx++) { + + existingDeviceInfo = Group->DeviceList[inx]; + + if (existingDeviceInfo && + existingDeviceInfo->State == DSM_DEV_ACTIVE_OPTIMIZED && + DsmpIsDeviceInitialized(existingDeviceInfo) && + DsmpIsDeviceUsable(existingDeviceInfo) && + DsmpIsDeviceUsablePR(existingDeviceInfo)) { + + + if (Exception && existingDeviceInfo == DeviceInfo) { + continue; + } + + candidateDevInfo = existingDeviceInfo; + break; + } + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpGetAnyActivePath (Group %p): Exiting function with DevInfo %p\n", + Group, + candidateDevInfo)); + + return candidateDevInfo; +} + + +PDSM_DEVICE_INFO +DsmpGetActivePathToBeUsed( + _In_ PDSM_GROUP_ENTRY Group, + _In_ BOOLEAN Symmetric, + _In_ IN ULONG SpecialHandlingFlag + ) +/*++ + +Routine Description: + + This routine will return an active path from the list that should + be the next one used by the DSM + + This routine assumes that the DSM lock is held + +Arguements: + + Group is the multipath group + SpecialHandlingFlag - Flags to indicate any special handling requirement + +Return Value: + + active path or NULL + +--*/ +{ + PDSM_DEVICE_INFO deviceInfo; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpGetActivePathToBeUsed (Group %p): Entering function.\n", + Group)); + + deviceInfo = NULL; + + switch (Group->LoadBalanceType) { + + case DSM_LB_LEAST_BLOCKS: + case DSM_LB_DYN_LEAST_QUEUE_DEPTH: { + + // + // Since we choose the path with the smallest queue or cumulative size in + // DsmpGetPath, we just pick any path now + // + + // fall through + } + + case DSM_LB_ROUND_ROBIN_WITH_SUBSET: + case DSM_LB_ROUND_ROBIN: { + + // + // For RR and RRS we just pick any active path to start with + // and the DsmpGetPath will do the round robining + // + } + + case DSM_LB_FAILOVER: { + + deviceInfo = DsmpGetAnyActivePath(Group, FALSE, NULL, SpecialHandlingFlag); + + break; + } + + case DSM_LB_WEIGHTED_PATHS: { + + PDSM_DEVICE_INFO workDeviceInfo; + ULONG weight = (ULONG) -1; + ULONG inx; + + for (inx = 0; inx < Group->NumberDevices; inx++) { + + workDeviceInfo = Group->DeviceList[inx]; + + if ((workDeviceInfo) && + (DsmpIsDeviceInitialized(workDeviceInfo)) && + (DsmpIsDeviceUsable(workDeviceInfo)) && + (DsmpIsDeviceUsablePR(workDeviceInfo)) && + (workDeviceInfo->State == DSM_DEV_ACTIVE_OPTIMIZED) && + (workDeviceInfo->PathWeight < weight)) { + + // + // We found a path that is active and is at + // the lowest weight. Remember it. + // + weight = workDeviceInfo->PathWeight; + + deviceInfo = workDeviceInfo; + } + } + + break; + } + + default: { + + break; + } + } + + if (!deviceInfo && !Symmetric) { + + // + // In the case of implicit transitions, it is possible that a TPG hasn't yet + // been made A/O. So instead of not setting any path, fall back to using some + // other path. IO sent down this path may fail, but will be retried in + // InterpretError(). Hopefully by then, at least one TPG will have transitioned + // to A/O state. + // + // The same argument holds true if the storage supports both implicit and + // explicit transitions, since it is possible that after we explicitly changed + // the TPG states, an implicit transition left us with no path in A/O state. + // + // In the case of explicit only transitions, we tried making at least one path + // as A/O and failed. This can happen, for example, when STPG fails because + // this initiator is not registered or does not hold exclusive reservation over + // the target. Instead of not using any path, we can consider a path in A/U state, + // A/U being just a functional path state. + // + BOOLEAN sendTPG = FALSE; + + deviceInfo = DsmpFindStandbyPathToActivateALUA(Group, &sendTPG, SpecialHandlingFlag); + + if ((deviceInfo != NULL) && + ((deviceInfo->ALUASupport != DSM_DEVINFO_ALUA_EXPLICIT) || + ((deviceInfo->ALUASupport == DSM_DEVINFO_ALUA_EXPLICIT) && + (deviceInfo->State <= DSM_DEV_ACTIVE_UNOPTIMIZED)))) { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_PNP, + "DsmpGetActivePathToBeUsed (Group %p): Using best alternative candidate device %p\n", + Group, + deviceInfo)); + } else { + + deviceInfo = NULL; + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_PNP, + "DsmpGetActivePathToBeUsed (Group %p): No active/alternative path available for group\n", + Group)); + } + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpGetActivePathToBeUsed (Group %p): Exiting function with devInfo %p.\n", + Group, + deviceInfo)); + + return deviceInfo; +} + + +PDSM_DEVICE_INFO +DsmpFindStandbyPathToActivate( + _In_ PDSM_GROUP_ENTRY Group, + _In_ IN ULONG SpecialHandlingFlag + ) +/*++ + +Routine Description: + + This routine will find another path in the group that is active + + This routine assumes that the DSM lock is held + + This is used by devices that support symmetric LUA. + +Arguements: + + Group is the multipath group + SpecialHandlingFlag - Flags to indicate any special handling requirement + +Return Value: + + Standby path or NULL if no standby path is available + +--*/ +{ + PDSM_DEVICE_INFO existingDeviceInfo; + ULONG inx; + PDSM_DEVICE_INFO candidateDevInfo = NULL; + + UNREFERENCED_PARAMETER(SpecialHandlingFlag); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpFindStandbyPathToActivate(Group %p): Entering function.\n", + Group)); + + for (inx = 0; inx < Group->NumberDevices; inx++) { + + existingDeviceInfo = Group->DeviceList[inx]; + + if (existingDeviceInfo && + existingDeviceInfo->State == DSM_DEV_STANDBY && + DsmpIsDeviceInitialized(existingDeviceInfo) && + DsmpIsDeviceUsable(existingDeviceInfo) && + DsmpIsDeviceUsablePR(existingDeviceInfo)) { + + // + // If we don't as yet have a candidate, pick the first available one. + // However, our preference is one that is through the preferred TPG. + // + if (!candidateDevInfo || + existingDeviceInfo->TargetPortGroup && existingDeviceInfo->TargetPortGroup->Preferred) { + + candidateDevInfo = existingDeviceInfo; + } + } + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpFindStandbyPathToActivate (Group %p): Exiting function with devInfo %p.\n", + Group, + candidateDevInfo)); + + return candidateDevInfo; +} + + +PDSM_DEVICE_INFO +DsmpFindStandbyPathToActivateALUA( + _In_ IN PDSM_GROUP_ENTRY Group, + _In_ IN PBOOLEAN SendTPG, + _In_ IN ULONG SpecialHandlingFlag + ) +/*++ + +Routine Description: + + This routine will find another path in the group that is active + + This is used by devices that don't support symmetric LUA. + + N.B: This routine MUST be called with DsmContextLock held in either Shared or + Exclusive mode. + +Arguements: + + Group is the multipath group + SendTPG - output parameter that indicates if TPG command need to be sent down. + SpecialHandlingFlag - Flags to indicate any special handling requirement + +Return Value: + + Standby path or NULL if no standby path is available + +--*/ +{ + PDSM_DEVICE_INFO existingDeviceInfo; + ULONG inx; + PDSM_DEVICE_INFO candidateDevInfo = NULL; + + UNREFERENCED_PARAMETER(SpecialHandlingFlag); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpFindStandbyPathToActivateALUA (Group %p): Entering function.\n", + Group)); + + for (inx = 0; inx < Group->NumberDevices; inx++) { + + existingDeviceInfo = Group->DeviceList[inx]; + + // + // The candidate for making A/O obviously mustn't be in a failed state + // and should have a path assigned. + // + if (existingDeviceInfo && + !DsmpIsDeviceFailedState(existingDeviceInfo->State) && + DsmpIsDeviceInitialized(existingDeviceInfo) && + DsmpIsDeviceUsable(existingDeviceInfo) && + DsmpIsDeviceUsablePR(existingDeviceInfo)) { + + // + // If we don't have any candidate currently, choose the very first + // one that is in a non-failure state, regardless of what state it + // may be in. + // + if (!candidateDevInfo) { + + candidateDevInfo = existingDeviceInfo; + *SendTPG = TRUE; + } + + // + // Might as well use one that the Admin desires for to be in A/O + // + if (existingDeviceInfo->DesiredState == DSM_DEV_ACTIVE_OPTIMIZED) { + + // + // However, such a devInfo is not a better candidate if our candidate + // devInfo is also one that the Admin desires be in A/O, and it is + // through a preferred TPG. + // + if (!(existingDeviceInfo->DesiredState == candidateDevInfo->DesiredState && + candidateDevInfo->TargetPortGroup->Preferred)) { + + candidateDevInfo = existingDeviceInfo; + *SendTPG = TRUE; + } + } + + // + // Check if the current one is at least better than the candidate. + // + if (DsmpIsBetterDeviceState(candidateDevInfo->State, existingDeviceInfo->State)) { + + candidateDevInfo = existingDeviceInfo; + *SendTPG = TRUE; + } + + // + // We found one that we may have just masked as non-A/O. This is the + // best option as we don't have to send down an STPG. + // + if (existingDeviceInfo->ALUAState == DSM_DEV_ACTIVE_OPTIMIZED) { + + candidateDevInfo = existingDeviceInfo; + *SendTPG = FALSE; + break; + } + } + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpFindStandbyPathToActivateALUA (Group %p): Exiting function with devInfo %p.\n", + Group, + candidateDevInfo)); + + return candidateDevInfo; +} + + +PDSM_DEVICE_INFO +DsmpFindStandbyPathInAlternateTpgALUA( + _In_ IN PDSM_GROUP_ENTRY Group, + _In_ IN PDSM_DEVICE_INFO DeviceInfo, + _In_ IN ULONG SpecialHandlingFlag + ) +/*++ + +Routine Description: + + This routine will find another path in the group that is not in the same + TPG as the passed in DeviceInfo. + + This routine assumes that the DSM lock is held + + This is used by devices that support ALUA. + +Arguements: + + Group is the multipath group + DeviceInfo is the devInfo whose TPG must not be matched + SpecialHandlingFlag - Flags to indicate any special handling requirement + +Return Value: + + Standby path not in same TPG as passed in DeviceInfo + or NULL if no standby path is available + +--*/ +{ + PDSM_TARGET_PORT_GROUP_ENTRY targetPortGroup = DeviceInfo->TargetPortGroup; + PDSM_DEVICE_INFO existingDeviceInfo; + ULONG inx; + PDSM_DEVICE_INFO candidateDevInfo = NULL; + + UNREFERENCED_PARAMETER(SpecialHandlingFlag); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpFindStandbyPathInAlternateTpgALUA (DevInfo %p): Entering function.\n", + DeviceInfo)); + + for (inx = 0; inx < Group->NumberDevices; inx++) { + + existingDeviceInfo = Group->DeviceList[inx]; + + // + // We only care about deviceInfo if TPG is different + // + if (existingDeviceInfo && existingDeviceInfo->TargetPortGroup != targetPortGroup) { + + // + // The candidate for making A/O obviously mustn't be in a failed state + // and must be initialized + // + if (!DsmpIsDeviceFailedState(existingDeviceInfo->State) && + DsmpIsDeviceInitialized(existingDeviceInfo) && + DsmpIsDeviceUsable(existingDeviceInfo) && + DsmpIsDeviceUsablePR(existingDeviceInfo)) { + + // + // If we don't have any candidate currently, choose the very first + // one that is in a non-failure state, regardless of what state it + // may be in. + // + if (!candidateDevInfo) { + + candidateDevInfo = existingDeviceInfo; + continue; + } + + // + // Check if the current one is at least better than the candidate. + // + if (DsmpIsBetterDeviceState(candidateDevInfo->State, existingDeviceInfo->State)) { + + candidateDevInfo = existingDeviceInfo; + } + } + } + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpFindStandbyPathInAlternateTpgALUA (DevInfo %p): Exiting function with devInfo %p.\n", + DeviceInfo, + candidateDevInfo)); + + return candidateDevInfo; +} + + +NTSTATUS +DsmpSetLBForDsmPolicyAdjustment( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN DSM_LOAD_BALANCE_TYPE LoadBalanceType, + _In_ IN ULONGLONG PreferredPath + ) + +/*++ + +Routine Description: + + This routine is called when a change is made to the DSM-wide default + load balance policy. It goes through each LUN representation (ie. Group + entry) and updates the appropriate ones (ie. ones for which the policy + was not chosen based on VID/PID or because of an explicit settings on + the LUN). It also then updates the path states in accordance with the + new LB policy. + +Arguements: + + DsmContext is the DSM context + LoadBalanceType is the new load balance policy to be applied + PreferredPath is the preferred failback path to be used (applicable only + if LB policy is Failover) + +Return Value: + + Success + +--*/ + +{ + NTSTATUS status = STATUS_SUCCESS; + KIRQL oldIrql; + PLIST_ENTRY entry; + ULONG groupIndex = 0; + ULONG devInfoIndex; + PDSM_GROUP_ENTRY group; + PDSM_DEVICE_INFO devInfo; + DSM_LOAD_BALANCE_TYPE newLoadBalancePolicy; + ULONG SpecialHandlingFlag = 0; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpSetLBForDsmPolicyAdjustment (DsmContext %p): Entering function.\n", + DsmContext)); + + oldIrql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); + + for (entry = DsmContext->GroupList.Flink; entry != &(DsmContext->GroupList); entry = entry->Flink, groupIndex++) { + + group = CONTAINING_RECORD(entry, DSM_GROUP_ENTRY, ListEntry); + + newLoadBalancePolicy = LoadBalanceType; + + // + // Only LUNs that don't have their policy explicitly set + // and ones that don't have it set based on VID/PID are + // of interest to us here. + // + if (group->LBPolicySelection == DSM_DEFAULT_LB_POLICY_ALUA_CAPABILITY || + group->LBPolicySelection == DSM_DEFAULT_LB_POLICY_DSM_WIDE) { + + // + // Also, if the caller is trying to clear the DSM-wide + // default policy, then we don't even care about those + // LUNs whose policies were not set using this value. + // + if (newLoadBalancePolicy < DSM_LB_FAILOVER && + group->LBPolicySelection != DSM_DEFAULT_LB_POLICY_DSM_WIDE) { + + continue; + } + + // + // If the DSM-wide setting is being cleared, we need to fall back + // to using the default based on the array's ALUA capabilities. + // + if (newLoadBalancePolicy < DSM_LB_FAILOVER) { + + newLoadBalancePolicy = DSM_LB_ROUND_ROBIN; + group->PreferredPath = (ULONGLONG)((ULONG_PTR)MAXULONG); + group->LBPolicySelection = DSM_DEFAULT_LB_POLICY_ALUA_CAPABILITY; + + + } else { + + // + // Since a new policy has been selected for the DSM-wide + // one, it needs to be applied to this LUN. + // + group->PreferredPath = (ULONGLONG)((ULONG_PTR)PreferredPath); + group->LBPolicySelection = DSM_DEFAULT_LB_POLICY_DSM_WIDE; + } + + // + // If Round Robin is set and ALUA is enabled, we need to change the + // policy to Round Robin with Subset. + // + if (!DsmpIsSymmetricAccess(group->DeviceList[0]) && newLoadBalancePolicy == DSM_LB_ROUND_ROBIN) { + + newLoadBalancePolicy = DSM_LB_ROUND_ROBIN_WITH_SUBSET; + } + + // + // Finally set the new load balance policy. + // + group->LoadBalanceType = newLoadBalancePolicy; + + // + // Path states need to be updated in accordance with the new policy. + // + for (devInfoIndex = 0; devInfoIndex < DSM_MAX_PATHS; devInfoIndex++) { + + devInfo = group->DeviceList[devInfoIndex]; + DsmpSetNewDefaultLBPolicy(DsmContext, devInfo, group->LoadBalanceType, SpecialHandlingFlag); + } + } + } + + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), oldIrql); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpSetLBForDsmPolicyAdjustment (DsmContext %p): Exiting function with status %x\n", + DsmContext, + status)); + + return status; +} + + +NTSTATUS +DsmpSetLBForVidPidPolicyAdjustment( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PWSTR TargetHardwareId, + _In_ IN DSM_LOAD_BALANCE_TYPE LoadBalanceType, + _In_ IN ULONGLONG PreferredPath + ) + +/*++ + +Routine Description: + + This routine is called when a change is made to the default load balance + policy for a VID/PID. It goes through each LUN representation (ie. Group + entry) and updates the appropriate ones (ie. ones for which the policy + was not because of an explicit settings on the LUN). It also then updates + the path states in accordance with the new LB policy. + +Arguements: + + DsmContext is the DSM context + TargetHardwareId is the VID/PID whose matching LUNs policy need to be updated + LoadBalanceType is the new load balance policy to be applied + PreferredPath is the preferred failback path to be used (applicable only + if LB policy is Failover) + +Return Value: + + Success + +--*/ + +{ + NTSTATUS status = STATUS_SUCCESS; + KIRQL oldIrql; + PLIST_ENTRY entry; + ULONG groupIndex = 0; + ULONG devInfoIndex; + PDSM_GROUP_ENTRY group; + PDSM_DEVICE_INFO devInfo; + DSM_LOAD_BALANCE_TYPE dsmLoadBalanceType; + ULONGLONG dsmPreferredPath; + BOOLEAN useDsmLBSettings = FALSE; + ULONG SpecialHandlingFlag = 0; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpSetLBForVidPidPolicyAdjustment (%ws): Entering function.\n", + TargetHardwareId)); + + status = DsmpQueryDsmLBPolicyFromRegistry(&dsmLoadBalanceType, &dsmPreferredPath); + + if (NT_SUCCESS(status)) { + + useDsmLBSettings = TRUE; + + } else { + + if (status == STATUS_OBJECT_NAME_NOT_FOUND) { + + status = STATUS_SUCCESS; + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_WMI, + "DsmpSetLBForVidPidPolicyAdjustment (%ws): MSDSM-wide default LB policy not set.\n", + TargetHardwareId)); + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpSetLBForVidPidPolicyAdjustment (%ws): Failed to query MSDMS-wide default LB setting. Status %x.\n", + TargetHardwareId, + status)); + } + } + + oldIrql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); + + for (entry = DsmContext->GroupList.Flink; entry != &(DsmContext->GroupList); entry = entry->Flink, groupIndex++) { + + group = CONTAINING_RECORD(entry, DSM_GROUP_ENTRY, ListEntry); + + // + // Only LUNs that don't have their policy explicitly set + // are of interest to us here. + // + if (group->LBPolicySelection < DSM_DEFAULT_LB_POLICY_LUN_EXPLICIT) { + + // + // Figure out if this LUN matches the VID/PID of interest + // Device not of interest if it doesn't match the passed in target ID + // + if (wcscmp(group->HardwareId, TargetHardwareId) != 0) { + + continue; + } + + // + // Also, if the caller is trying to clear the VID/PID + // default policy, then we don't even care about those + // LUNs whose policies were not set using this value. + // + if (LoadBalanceType < DSM_LB_FAILOVER && + group->LBPolicySelection != DSM_DEFAULT_LB_POLICY_VID_PID) { + + continue; + } + + // + // If the VID/PID setting is being cleared, we need to fall back + // to using the DSM-wide default policy if it has been set, else + // we need to use the default based on the array's ALUA capabilities. + // + if (LoadBalanceType < DSM_LB_FAILOVER) { + + if (useDsmLBSettings) { + + // + // Even if the MSDSM-wide policy is specified as RR, if the storage + // is ALUA, we can't have the policy as RR, so we'll change it to + // RRWS instead. + // + if (!DsmpIsSymmetricAccess(group->DeviceList[0]) && dsmLoadBalanceType == DSM_LB_ROUND_ROBIN) { + + group->LoadBalanceType = DSM_LB_ROUND_ROBIN_WITH_SUBSET; + + } else { + + group->LoadBalanceType = dsmLoadBalanceType; + } + + group->PreferredPath = (ULONGLONG)((ULONG_PTR)dsmPreferredPath); + group->LBPolicySelection = DSM_DEFAULT_LB_POLICY_DSM_WIDE; + + } else { + + // + // Default LB type: + // is Round Robin if ALUA is not supported, or if ALUA support is implicit but access is symmetric, + // else Round Robin With Subset (since in ALUA, all paths aren't in A/O). + // + if (DsmpIsSymmetricAccess(group->DeviceList[0])) { + + group->LoadBalanceType = DSM_LB_ROUND_ROBIN; + + } else { + + group->LoadBalanceType = DSM_LB_ROUND_ROBIN_WITH_SUBSET; + } + + + group->PreferredPath = (ULONGLONG)((ULONG_PTR)MAXULONG); + group->LBPolicySelection = DSM_DEFAULT_LB_POLICY_ALUA_CAPABILITY; + } + + } else { + + // + // Since a new policy has been selected for the DSM-wide + // one, it needs to be applied to this LUN. + // + // However, if the VID/PID policy is specified as RR but the storage + // is ALUA, we can't have the policy as RR, so we'll change it to + // RRWS instead. + // + if (!DsmpIsSymmetricAccess(group->DeviceList[0]) && LoadBalanceType == DSM_LB_ROUND_ROBIN) { + + group->LoadBalanceType = DSM_LB_ROUND_ROBIN_WITH_SUBSET; + + } else { + + group->LoadBalanceType = LoadBalanceType; + } + + group->PreferredPath = (ULONGLONG)((ULONG_PTR)PreferredPath); + group->LBPolicySelection = DSM_DEFAULT_LB_POLICY_VID_PID; + } + + // + // Path states need to be updated in accordance with the new policy. + // + for (devInfoIndex = 0; devInfoIndex < DSM_MAX_PATHS; devInfoIndex++) { + + devInfo = group->DeviceList[devInfoIndex]; + DsmpSetNewDefaultLBPolicy(DsmContext, devInfo, group->LoadBalanceType, SpecialHandlingFlag); + } + } + } + + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), oldIrql); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpSetLBForVidPidPolicyAdjustment (%ws): Exiting function with status %x\n", + TargetHardwareId, + status)); + + return status; +} + + +NTSTATUS +DsmpSetNewDefaultLBPolicy( + _In_ IN PDSM_CONTEXT DsmContext, + _In_opt_ IN PDSM_DEVICE_INFO DeviceInfo, + _In_ IN DSM_LOAD_BALANCE_TYPE LoadBalanceType, + _In_ IN ULONG SpecialHandlingFlag + ) +/*++ + +Routine Description: + + This routine is called to adjust the path state of an instance of a + LUN for which a new default load balance policy was applied + following an admin request for such a change. + + This routine must be called with spinlock held. + +Arguements: + + DsmContext is the DSM context + DeviceInfo is the device info on which the new path state needs to be set + LoadBalanceType is the load balance policy in accordance with which the path state needs to be adjusted + SpecialHandlingFlag - Flags to indicate any special handling requirement + +Return Value: + + Status + +--*/ +{ + PDSM_GROUP_ENTRY group; + NTSTATUS status = STATUS_SUCCESS; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpSetNewDefaultLBPolicy (DevInfo %p): Entering function\n", + DeviceInfo)); + + if (!DeviceInfo) { + status = STATUS_INVALID_PARAMETER; + goto __Exit_DsmpSetNewDefaultLBPolicy; + } + + if (!(DsmpIsDeviceInitialized(DeviceInfo) && DsmpIsDeviceUsable(DeviceInfo) && DsmpIsDeviceUsablePR(DeviceInfo)) || + DsmpIsDeviceFailedState(DeviceInfo->State)) { + + status = STATUS_UNSUCCESSFUL; + goto __Exit_DsmpSetNewDefaultLBPolicy; + } + + + group = DeviceInfo->Group; + + if (!DsmpIsSymmetricAccess(DeviceInfo)) { + + DsmpAdjustDeviceStatesALUA(group, NULL, SpecialHandlingFlag); + + } else { + + switch (LoadBalanceType) { + + // + // For failover, it is important the right path is + // chosen, ie. preferred path needs to be taken into + // consideration. + // + case DSM_LB_FAILOVER: { + + DsmpSetLBForPathArrival(DsmContext, DeviceInfo, SpecialHandlingFlag); + + break; + } + + // + // For all other policies, the state must be A/O. + // + default: { + + DeviceInfo->PreviousState = DeviceInfo->State; + DeviceInfo->State = DSM_DEV_ACTIVE_OPTIMIZED; + + break; + } + } + } + +__Exit_DsmpSetNewDefaultLBPolicy: + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpSetNewDefaultLBPolicy (DevInfo %p): Exiting function with status %x\n", + DeviceInfo, + status)); + + return status; +} + + +NTSTATUS +DsmpSetLBForPathArrival( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_DEVICE_INFO NewDeviceInfo, + _In_ IN ULONG SpecialHandlingFlag + ) +/*++ + +Routine Description: + + This routine is called when a new path arrives for a multipath + group that doesn't support ALUA. The routine will set the path + to the appropriate state and fix up the other paths state if + they need to change. + + This is used by devices NOT supporting ALUA. + + This routine must be called with spinlock held. + +Arguements: + + DsmContext is the DSM context + NewDeviceInfo is the device info for the newly arrived path + SpecialHandlingFlag - Flags to indicate any special handling requirement + +Return Value: + + Status + +--*/ +{ + PDSM_GROUP_ENTRY group; + PDSM_DEVICE_INFO deviceInfo; + NTSTATUS status = STATUS_SUCCESS; + + UNREFERENCED_PARAMETER(DsmContext); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpSetLBForPathArrival (DevInfo %p): Entering function.\n", + NewDeviceInfo)); + + group = NewDeviceInfo->Group; + + if (!(DsmpIsDeviceInitialized(NewDeviceInfo) && DsmpIsDeviceUsable(NewDeviceInfo) && DsmpIsDeviceUsablePR(NewDeviceInfo))) { + + // + // Bad device instance. Nothing can be done about it. + // + NewDeviceInfo->PreviousState = NewDeviceInfo->State; + NewDeviceInfo->State = DSM_DEV_UNDETERMINED; + + NT_ASSERT(NewDeviceInfo->FailGroup == NULL); + + goto __Exit_DsmpSetLBForPathArrival; + } + + + if (group->NumberDevices == 1) { + + // + // if this is the only device for the group then we will always + // be active as every group must have at least one active path + // + if (NewDeviceInfo->State == DSM_DEV_ACTIVE_OPTIMIZED) { + + // + // All's good + // + goto __Exit_DsmpSetLBForPathArrival; + + } else { + + NewDeviceInfo->PreviousState = NewDeviceInfo->State; + NewDeviceInfo->State = DSM_DEV_ACTIVE_OPTIMIZED; + } + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpSetLBForPathArrival (DevInfo %p): State changed to %d at %d\n", + NewDeviceInfo, + NewDeviceInfo->State, + __LINE__)); + + goto __Exit_DsmpSetLBForPathArrival; + } + + switch(group->LoadBalanceType) { + case DSM_LB_FAILOVER: { + + // + // Get the current active path. + // + deviceInfo = DsmpGetAnyActivePath(group, FALSE, NULL, SpecialHandlingFlag); + + // + // If the newly arriving path is the preferred path, this now should + // become our active path. + // + if (group->PreferredPath == ((ULONGLONG)((ULONG_PTR)(NewDeviceInfo->FailGroup->PathId)))) { + + // + // If current active path is not the preferred path, change its + // path state to standby. + // + if (deviceInfo && deviceInfo != NewDeviceInfo) { + + deviceInfo->PreviousState = deviceInfo->State; + deviceInfo->State = DSM_DEV_STANDBY; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpSetLBForPathArrival (Group %p): Preferred path back online. DevInfo %p changed to state %d\n", + group, + deviceInfo, + deviceInfo->State)); + } + + NewDeviceInfo->PreviousState = NewDeviceInfo->State; + NewDeviceInfo->State = DSM_DEV_ACTIVE_OPTIMIZED; + + } else { + + // + // In the case of failover, we must have only a single + // active path. If this newly added path is configured as + // the one that is desired to be active then we make this + // active and set the standby path that was active back to + // standby unless the preferred path is the currently active + // path. If the newly added path is supposed to be + // standby then we leave it as standby. + // + if (NewDeviceInfo->DesiredState == DSM_DEV_ACTIVE_OPTIMIZED) { + + if (deviceInfo) { + + // + // If the preferred path is currently active, don't change + // it regardless of this path wanting to be in active state. + // + if (group->PreferredPath == ((ULONGLONG)((ULONG_PTR)(deviceInfo->FailGroup->PathId)))) { + + if (NewDeviceInfo != deviceInfo) { + + NewDeviceInfo->PreviousState = NewDeviceInfo->State; + NewDeviceInfo->State = DSM_DEV_STANDBY; + } + + } else { + + // + // Since the preferred path is not active, make this + // path active since it wants to be so. This means + // changing the current active path to standby. + // + deviceInfo->PreviousState = deviceInfo->State; + deviceInfo->State = DSM_DEV_STANDBY; + + NewDeviceInfo->PreviousState = NewDeviceInfo->State; + NewDeviceInfo->State = DSM_DEV_ACTIVE_OPTIMIZED; + } + } else { + + NewDeviceInfo->PreviousState = NewDeviceInfo->State; + NewDeviceInfo->State = DSM_DEV_ACTIVE_OPTIMIZED; + } + + } else { + + if (deviceInfo) { + + if (deviceInfo != NewDeviceInfo) { + + // + // This newly arrived device doesn't want to be in + // A/O, and we already have an active path, so make + // it standby. + // + NewDeviceInfo->PreviousState = NewDeviceInfo->State; + NewDeviceInfo->State = DSM_DEV_STANDBY; + } + } else { + + // + // Since we currently don't have an active path, this one + // needs to be made active, regardless of its path it wishes + // to be in. + // + NewDeviceInfo->PreviousState = NewDeviceInfo->State; + NewDeviceInfo->State = DSM_DEV_ACTIVE_OPTIMIZED; + } + } + } + + break; + } + + case DSM_LB_ROUND_ROBIN_WITH_SUBSET: { + + // + // In RRWS, a set of paths can be active and another set of + // paths can be standby. We set the new path to the desired + // state unless the desired state is standby, but there are + // no active paths. Also if the desired state is Active we + // need to check if there are any existing paths that are + // also active but have a desired state of standby. For + // those we can move them back to standby. + // + if (NewDeviceInfo->DesiredState == DSM_DEV_ACTIVE_OPTIMIZED) { + + // + // We are the active path coming back. Find out who has + // been the active one and place him back to standby + // + DsmpAllowStandbyPathsToRest(group); + NewDeviceInfo->PreviousState = NewDeviceInfo->State; + NewDeviceInfo->State = DSM_DEV_ACTIVE_OPTIMIZED; + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_PNP, + "DsmpSetLBForPathArrival (DevInfo %p): Changed to state %d at %d\n", + NewDeviceInfo, + NewDeviceInfo->State, + __LINE__)); + + } else { + + // + // if there are no paths already active then we've got + // to make this one AO, otherwise we can be non-AO + // + deviceInfo = DsmpGetAnyActivePath(group, FALSE, NULL, SpecialHandlingFlag); + + if (!deviceInfo || deviceInfo == NewDeviceInfo) { + + NewDeviceInfo->PreviousState = NewDeviceInfo->State; + NewDeviceInfo->State = DSM_DEV_ACTIVE_OPTIMIZED; + + } else { + + NewDeviceInfo->State = DSM_DEV_STANDBY; + } + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpSetLBForPathArrival (%p): Changed to state %d at %d. Status %x\n", + NewDeviceInfo, + NewDeviceInfo->State, + __LINE__, + status)); + } + + status = STATUS_SUCCESS; + + break; + } + + case DSM_LB_LEAST_BLOCKS: + case DSM_LB_ROUND_ROBIN: + case DSM_LB_DYN_LEAST_QUEUE_DEPTH: + case DSM_LB_WEIGHTED_PATHS: { + + // + // In RR, LWP, LB and LQD all paths are active so the new device + // becomes AO or AU. + // + if (NewDeviceInfo->State != DSM_DEV_ACTIVE_OPTIMIZED) { + + NewDeviceInfo->PreviousState = NewDeviceInfo->State; + NewDeviceInfo->State = DSM_DEV_ACTIVE_OPTIMIZED; + } + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpSetLBForPathArrival (DevInfo %p): Changed to state %d at %d. Status %x\n", + NewDeviceInfo, + NewDeviceInfo->State, + __LINE__, + status)); + + status = STATUS_SUCCESS; + + break; + } + + default: { + status = STATUS_INVALID_PARAMETER; + break; + } + } + +__Exit_DsmpSetLBForPathArrival: + + // + // Update the next path to be used for the group + // + deviceInfo = DsmpGetActivePathToBeUsed(group, + DsmpIsSymmetricAccess(NewDeviceInfo), + SpecialHandlingFlag); + if (deviceInfo != NULL) { + + InterlockedExchangePointer(&(group->PathToBeUsed), (PVOID)deviceInfo->FailGroup); + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_PNP, + "DsmpSetLBForPathArrival (DevInfo %p): Updating PathToBeUsed in %p to %p\n", + NewDeviceInfo, + group, + group->PathToBeUsed)); + } else { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_PNP, + "DsmpSetLBForPathArrival (DevInfo %p): No FOG available for group %p\n", + NewDeviceInfo, + group)); + + InterlockedExchangePointer(&(group->PathToBeUsed), NULL); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpSetLBForPathArrival (DevInfo %p): Exiting function with status %x\n", + NewDeviceInfo, + status)); + + return status; +} + + +NTSTATUS +DsmpSetLBForPathArrivalALUA( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_DEVICE_INFO NewDeviceInfo, + _In_ IN ULONG SpecialHandlingFlag + ) +/*++ + +Routine Description: + + This routine is called when a new path arrives for a multipath + group. The routine will set the path to the appropriate state and + fix up the other paths state if they need to change. + + Spin lock must NOT be held by caller + +Arguements: + + DsmContext is the DSM context + NewDeviceInfo is the device info for the newly arrived path + SpecialHandlingFlag - Flags to indicate any special handling requirement + +Return Value: + + Status + +--*/ +{ + PDSM_GROUP_ENTRY group; + PDSM_DEVICE_INFO deviceInfo = NULL; + NTSTATUS status = STATUS_SUCCESS; + KIRQL irql = PASSIVE_LEVEL; // Initialize variable to prevent C4701 warnings; + BOOLEAN lockHeld = FALSE; + PDSM_DEVICE_INFO preferredActiveDeviceInfo = NULL; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpSetLBForPathArrivalALUA (DevInfo %p): Entering function.\n", + NewDeviceInfo)); + + group = NewDeviceInfo->Group; + + if (!(DsmpIsDeviceInitialized(NewDeviceInfo) && DsmpIsDeviceUsable(NewDeviceInfo) && DsmpIsDeviceUsablePR(NewDeviceInfo))) { + + // + // Bad device instance. Nothing can be done about it. + // + NewDeviceInfo->PreviousState = NewDeviceInfo->State; + NewDeviceInfo->State = DSM_DEV_UNDETERMINED; + + NT_ASSERT(NewDeviceInfo->FailGroup == NULL); + + goto __Exit_DsmpSetLBForPathArrivalALUA; + } + + + irql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); + lockHeld = TRUE; + + if (group->NumberDevices == 1) { + + // + // If this is the only device for the group then it should be the + // active instance as every group must have at least one active path. + // + if (NewDeviceInfo->State != DSM_DEV_ACTIVE_OPTIMIZED) { + + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); + lockHeld = FALSE; + + if (NewDeviceInfo->ALUASupport >= DSM_DEVINFO_ALUA_EXPLICIT) { + + // + // If the device supports explicit transitions, set its state to A/O + // + status = DsmpSetDeviceALUAState(DsmContext, NewDeviceInfo, DSM_DEV_ACTIVE_OPTIMIZED); + + } else { + + // + // Since the device supports only implicit transitions, send down + // RTPG and hope that the controller has made this one the A/O path. + // + status = DsmpGetDeviceALUAState(DsmContext, NewDeviceInfo, NULL); + + if (NT_SUCCESS(status)) { + + // + // Remember that at this point it is possible that this path + // is still non-A/O. We'll need to handle this in DsmGetPath + // as a special case where we don't find an A/O path but the + // storage supports implicit-only transitions. At that time, + // we mustn't blindly return a NULL path back. + // + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpSetLBForPathArrivalALUA (DevInfo %p): RTPG returned state = %x, ALUA state = %x.\n", + NewDeviceInfo, + NewDeviceInfo->State, + NewDeviceInfo->ALUAState)); + } + } + } + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpSetLBForPathArrivalALUA (DevInfo %p): State changed to %d at %d\n", + NewDeviceInfo, + NewDeviceInfo->State, + __LINE__)); + + } else { + + deviceInfo = DsmpGetAnyActivePath(group, FALSE, NULL, SpecialHandlingFlag); + + // + // Irrespecitve of the policy, we must have at least one A/O path. + // + // For RRWS and FOO, this path is of interest if it is desired to be in A/O. + // + // Also, for failover-only, this new path is of interest if it is + // the preferred path. + // + // This path is also of interest if it is not A/O and desired state has not + // explicitly been set to non-A/O and it has been exposed through the + // preferred TPG. + // + if ((!deviceInfo) || + ((NewDeviceInfo->DesiredState == DSM_DEV_ACTIVE_OPTIMIZED) && + (group->LoadBalanceType == DSM_LB_FAILOVER || + group->LoadBalanceType == DSM_LB_ROUND_ROBIN_WITH_SUBSET)) || + (group->PreferredPath == (ULONGLONG)((ULONG_PTR)(NewDeviceInfo->FailGroup->PathId)) && + group->LoadBalanceType == DSM_LB_FAILOVER) || + (NewDeviceInfo->State != DSM_DEV_ACTIVE_OPTIMIZED && + NewDeviceInfo->DesiredState == DSM_DEV_UNDETERMINED && + NewDeviceInfo->TargetPortGroup->Preferred)) { + + // + // Since this path is supposed to be active, we make it + // active and then allow any paths that are supposed to + // be standby go back to being standby + // + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); + lockHeld = FALSE; + + // + // If explicit ALUA is supported, we need to send down STPG to make the change. + // + if (NewDeviceInfo->ALUASupport >= DSM_DEVINFO_ALUA_EXPLICIT) { + + status = DsmpSetDeviceALUAState(DsmContext, NewDeviceInfo, DSM_DEV_ACTIVE_OPTIMIZED); + + } else { + + // + // If implicit ALUA, the controller may have made some + // changes to the TPG states. We just need to query it. + // We'll try and honor the Admin's request but can't + // guarantee it. + // + status = DsmpGetDeviceALUAState(DsmContext, NewDeviceInfo, NULL); + } + + // + // We prefer this newly arrived devInfo to be A/O + // + preferredActiveDeviceInfo = NewDeviceInfo; + } + } + + if (!lockHeld) { + + irql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); + lockHeld = TRUE; + } + + if (NT_SUCCESS(status)) { + + DsmpAdjustDeviceStatesALUA(group, preferredActiveDeviceInfo, SpecialHandlingFlag); + + } else { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_PNP, + "DsmpSetLBForPathArrivalALUA (DevInfo %p): Trying to query ALUA state failed with %x\n", + NewDeviceInfo, + status)); + } + + status = STATUS_SUCCESS; + +__Exit_DsmpSetLBForPathArrivalALUA: + + // + // Update the next path to be used for the group + // + deviceInfo = DsmpGetActivePathToBeUsed(group, + DsmpIsSymmetricAccess(NewDeviceInfo), + SpecialHandlingFlag); + if (deviceInfo != NULL) { + + InterlockedExchangePointer(&(group->PathToBeUsed), (PVOID)deviceInfo->FailGroup); + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_PNP, + "DsmpSetLBForPathArrivalALUA (DevInfo %p): Updating PathToBeUsed in %p to %p\n", + NewDeviceInfo, + group, + group->PathToBeUsed)); + } else { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_PNP, + "DsmpSetLBForPathArrivalALUA (DevInfo %p): No active/alternative path available for group %p\n", + NewDeviceInfo, + group)); + + InterlockedExchangePointer(&(group->PathToBeUsed), NULL); + } + + if (lockHeld) { + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpSetLBForPathArrivalALUA (DevInfo %p): Exiting function with status %x\n", + NewDeviceInfo, + status)); + + return status; +} + + +NTSTATUS +DsmpSetLBForPathRemoval( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_DEVICE_INFO RemovedDeviceInfo, + _In_opt_ IN OPTIONAL PDSM_GROUP_ENTRY Group, + _In_ IN ULONG SpecialHandlingFlag + ) +/*++ + +Routine Description: + + This routine is called when a path is removed from a multipath + group. The routine will set the path to the appropriate state and + fix up the other paths state if they need to change. + + Note: This is used by devices NOT supporting ALUA. + +Arguements: + + DsmContext is the DSM context + + RemovedDeviceInfo is the device info for failing/going-away path + + Group is an optional group override. That is, if Group is not NULL, this + function will run the load balance policy on the given Group and not + the Group from the RemovedDeviceInfo. This should only be used when + it's impossible to get a pointer to the RemovedDeviceInfo. + + SpecialHandlingFlag - Flags to indicate any special handling requirement + +Return Value: + + Status + +--*/ +{ + PDSM_GROUP_ENTRY group; + PDSM_DEVICE_INFO deviceInfo; + NTSTATUS status = STATUS_SUCCESS; + KIRQL irql; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpSetLBForPathRemoval (DevInfo %p, Group %p): Entering function.\n", + RemovedDeviceInfo, + Group)); + + irql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); + + if (Group == NULL) { + + if (!(DsmpIsDeviceFailedState(RemovedDeviceInfo->State))) { + + RemovedDeviceInfo->LastKnownGoodState = RemovedDeviceInfo->State; + } + + RemovedDeviceInfo->PreviousState = RemovedDeviceInfo->State; + RemovedDeviceInfo->State = DSM_DEV_FAILED; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpSetLBForPathRemoval (DevInfo %p): changed to state %d at %d\n", + RemovedDeviceInfo, + RemovedDeviceInfo->State, + __LINE__)); + + group = RemovedDeviceInfo->Group; + + } else { + // + // The caller has chosen to override the RemovedDeviceInfo->Group. + // + group = Group; + } + + switch(group->LoadBalanceType) { + case DSM_LB_FAILOVER: + case DSM_LB_ROUND_ROBIN_WITH_SUBSET: { + + // + // In the case of failover, we must have only a single + // active path. If the removed path was the active path we + // need to find another path to become active + // + // In RRWS, a set of paths can be active and another set of + // paths can be standby. If the removed path is an active + // path then we need to make sure there is another active + // path. If there is already another active path then there + // is nothing to do. If not then a path needs to be made + // active. + // + if (!DsmpGetAnyActivePath(group, FALSE, NULL, SpecialHandlingFlag)) { + + deviceInfo = DsmpFindStandbyPathToActivate(group, SpecialHandlingFlag); + if (deviceInfo) { + + deviceInfo->PreviousState = deviceInfo->State; + deviceInfo->State = DSM_DEV_ACTIVE_OPTIMIZED; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpSetLBForPathRemoval (DevInfo %p): DevInfo %p changed to state %d at %d\n", + RemovedDeviceInfo, + deviceInfo, + deviceInfo->State, + __LINE__)); + } + } else { + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpSetLBForPathRemoval (DevInfo %p): LB Policy FO/RRWS and other paths active, no path made active at %d\n", + RemovedDeviceInfo, + __LINE__)); + } + + break; + } + + case DSM_LB_LEAST_BLOCKS: + case DSM_LB_ROUND_ROBIN: + case DSM_LB_WEIGHTED_PATHS: + case DSM_LB_DYN_LEAST_QUEUE_DEPTH: { + + // + // In RR, LQD, LB and LWP, all paths are active so we don't + // need to worry about activating a new path + // + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpSetLBForPathRemoval (DevInfo %p): LB Policy RR, LWP or LQD, no path made active at %d\n", + RemovedDeviceInfo, + __LINE__)); + break; + } + + default: { + status = STATUS_INVALID_PARAMETER; + break; + } + } + + // + // Update the next path to be used for the group + // + deviceInfo = DsmpGetActivePathToBeUsed(group, + DsmpIsSymmetricAccess(RemovedDeviceInfo), + SpecialHandlingFlag); + if (deviceInfo != NULL) { + + InterlockedExchangePointer(&(group->PathToBeUsed), deviceInfo->FailGroup); + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpSetLBForPathRemoval (DevInfo %p): Removal: Updating PathToBeUsed in %p to %p\n", + RemovedDeviceInfo, + group, + group->PathToBeUsed)); + } else { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_PNP, + "DsmpSetLBForPathRemoval (DevInfo %p): After remove No FOG available for group %p\n", + RemovedDeviceInfo, + group)); + + InterlockedExchangePointer(&(group->PathToBeUsed), NULL); + } + + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpSetLBForPathRemoval (DevInfo %p): Exiting function with status %x\n", + RemovedDeviceInfo, + status)); + + return status; +} + + +NTSTATUS +DsmpSetLBForPathRemovalALUA( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_DEVICE_INFO RemovedDeviceInfo, + _In_opt_ IN OPTIONAL PDSM_GROUP_ENTRY Group, + _In_ IN ULONG SpecialHandlingFlag + ) +/*++ + +Routine Description: + + This routine is called when a path is removed from a multipath + group. The routine will set the path to the appropriate state and + fix up the other paths state if they need to change. + + Note: This should NOT be called with DsmContext Lock held + This is used for devices supporting ALUA. + +Arguements: + + DsmContext is the DSM context + + RemovedDeviceInfo is the device info for the failing/going-away path + + Group is an optional group override. That is, if Group is not NULL, this + function will run the load balance policy on the given Group and not + the Group from the RemovedDeviceInfo. This should only be used when + it's impossible to get a pointer to the RemovedDeviceInfo. + + SpecialHandlingFlag - Flags to indicate any special handling requirement + +Return Value: + + Status + +--*/ +{ + PDSM_GROUP_ENTRY group; + PDSM_DEVICE_INFO deviceInfo = NULL; + NTSTATUS status = STATUS_SUCCESS; + KIRQL irql; + BOOLEAN lockHeld = FALSE; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpSetLBForPathRemovalALUA (DevInfo %p, Group %p): Entering function.\n", + RemovedDeviceInfo, + Group)); + + irql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); + lockHeld = TRUE; + + if (Group == NULL) { + + if (!(DsmpIsDeviceFailedState(RemovedDeviceInfo->State))) { + + RemovedDeviceInfo->LastKnownGoodState = RemovedDeviceInfo->State; + } + + RemovedDeviceInfo->PreviousState = RemovedDeviceInfo->State; + RemovedDeviceInfo->State = DSM_DEV_FAILED; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpSetLBForPathRemovalALUA (DevInfo %p): changed to state %d at %d\n", + RemovedDeviceInfo, + RemovedDeviceInfo->State, + __LINE__)); + + group = RemovedDeviceInfo->Group; + + } else { + // + // The caller has chosen to override the RemovedDeviceInfo->Group. + // + group = Group; + } + + if (group->LoadBalanceType < DSM_LB_FAILOVER || + group->LoadBalanceType > DSM_LB_LEAST_BLOCKS) { + + status = STATUS_INVALID_PARAMETER; + + } else { + + // + // In the case of failover, we must have only a single + // active path. If the removed path was the active path we + // need to find another path to become active + // + // In rest of policies, set of paths can be active and another set of + // paths can be standby. If the removed path is an active + // path then we need to make sure there is another active + // path. If there is already another active path then there + // is nothing to do. If not then a path needs to be made + // active. + // + if (!DsmpGetAnyActivePath(group, FALSE, NULL, SpecialHandlingFlag)) { + + BOOLEAN sendTPG = TRUE; + + deviceInfo = DsmpFindStandbyPathToActivateALUA(group, &sendTPG, SpecialHandlingFlag); + + if (deviceInfo) { + + if (sendTPG) { + + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); + lockHeld = FALSE; + + // + // If explicit transition supported, we need to send down STPG to make the change. + // + if (deviceInfo->ALUASupport >= DSM_DEVINFO_ALUA_EXPLICIT) { + + status = DsmpSetDeviceALUAState(DsmContext, deviceInfo, DSM_DEV_ACTIVE_OPTIMIZED); + + } else { + + // + // If implicit ALUA, the controller may have made necessary + // changes to the TPG states. We just need to query it. + // + status = DsmpGetDeviceALUAState(DsmContext, deviceInfo, NULL); + } + + if (!lockHeld) { + irql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); + lockHeld = TRUE; + } + + if (NT_SUCCESS(status)) { + + DsmpAdjustDeviceStatesALUA(group, deviceInfo, SpecialHandlingFlag); + + } else { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_PNP, + "DsmpSetLBForPathRemovalALUA (DevInfo %p): Trying to query for ALUA state failed with status %x\n", + RemovedDeviceInfo, + status)); + } + } else { + + deviceInfo->PreviousState = deviceInfo->State; + deviceInfo->State = DSM_DEV_ACTIVE_OPTIMIZED; + } + + if (NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpSetLBForPathRemovalALUA (DevInfo %p): Device %p changed to state %d at %d\n", + RemovedDeviceInfo, + deviceInfo, + deviceInfo->State, + __LINE__)); + } + } + } else { + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpSetLBForPathRemovalALUA (DevInfo %p): Other paths active, no path made active at %d\n", + RemovedDeviceInfo, + __LINE__)); + } + + status = STATUS_SUCCESS; + } + + // + // Update the next path to be used for the group + // + deviceInfo = DsmpGetActivePathToBeUsed(group, + DsmpIsSymmetricAccess(RemovedDeviceInfo), + SpecialHandlingFlag); + if (deviceInfo != NULL) { + + InterlockedExchangePointer(&(group->PathToBeUsed), deviceInfo->FailGroup); + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpSetLBForPathRemovalALUA (DevInfo %p): Removal: Updating PathToBeUsed in %p to %p\n", + RemovedDeviceInfo, + group, + group->PathToBeUsed)); + } else { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_PNP, + "DsmpSetLBForPathRemovalALUA (DevInfo %p): No active/alternative path available for group %p\n", + RemovedDeviceInfo, + group)); + + InterlockedExchangePointer(&(group->PathToBeUsed), NULL); + } + + if (lockHeld) { + + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpSetLBForPathRemovalALUA (DevInfo %p): Exiting function with status %x.\n", + RemovedDeviceInfo, + status)); + + return status; +} + + +NTSTATUS +DsmpSetLBForPathFailing( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_DEVICE_INFO FailingDeviceInfo, + _In_ IN BOOLEAN MarkDevInfoFailed, + _In_ IN ULONG SpecialHandlingFlag + ) +/*++ + +Routine Description: + + This routine is called when an IO that was sent using this path fails with + a fatal error. The routine will set the path to the appropriate state and + fix up the other paths state if they need to change. + + Note: This is used by devices NOT supporting ALUA. + +Arguements: + + DsmContext is the DSM context + + FailingDeviceInfo is the device info for the path on which IO failed + + SpecialHandlingFlag - Flags to indicate any special handling requirement + +Return Value: + + Status + +--*/ +{ + NTSTATUS status; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmpSetLBForPathFailing (DevInfo %p): Entering function.\n", + FailingDeviceInfo)); + + // + // We need to do exactly what DsmpSetLBForPathRemoval() does, except + // that the devInfo may not really go away (may come back before a + // Pnp remove comes down for the real LUN) + // + if (MarkDevInfoFailed) { + status = DsmpSetLBForPathRemoval(DsmContext, FailingDeviceInfo, NULL, SpecialHandlingFlag); + } else { + status = DsmpSetLBForPathRemoval(DsmContext, FailingDeviceInfo, FailingDeviceInfo->Group, SpecialHandlingFlag); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmpSetLBForPathFailing (DevInfo %p): Exiting function with status %x\n", + FailingDeviceInfo, + status)); + + return status; +} + + +NTSTATUS +DsmpSetLBForPathFailingALUA( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_DEVICE_INFO FailingDeviceInfo, + _In_ IN BOOLEAN MarkDevInfoFailed, + _In_ IN ULONG SpecialHandlingFlag + ) +/*++ + +Routine Description: + + This routine is called when an IO that was sent using this path fails with + a fatal error. The routine will set the path to the appropriate state and + send down a Set Target Port Groups command asynchronously to fix up the + other paths state if they need to change (actual work done in the completion + routine). + + Note: This should NOT be called with DsmContext Lock held + This is used for devices supporting ALUA. + +Arguements: + + DsmContext is the DSM context + + FailingDeviceInfo is the device info for the failing/going-away path + + SpecialHandlingFlag - Flags to indicate any special handling requirement + +Return Value: + + Status + +--*/ +{ + PDSM_GROUP_ENTRY group; + PDSM_FAIL_PATH_PROCESSING_LIST_ENTRY failDevInfoListEntry = NULL; + PDSM_DEVICE_INFO deviceInfo = NULL; + NTSTATUS status = STATUS_SUCCESS; + KIRQL irql; + PUCHAR targetPortGroupsInfo = NULL; + ULONG targetPortGroupsInfoLength; + PSPC3_SET_TARGET_PORT_GROUP_DESCRIPTOR tpgDescriptor = NULL; + PDSM_COMPLETION_CONTEXT completionContext = NULL; + PVOID senseInfo = NULL; + PSCSI_REQUEST_BLOCK srb = NULL; + PDSM_TPG_COMPLETION_CONTEXT tpgCompletionContext = NULL; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmpSetLBForPathFailingALUA (DevInfo %p): Entering function.\n", + FailingDeviceInfo)); + + if (MarkDevInfoFailed) { + if (!(DsmpIsDeviceFailedState(FailingDeviceInfo->State))) { + + FailingDeviceInfo->LastKnownGoodState = FailingDeviceInfo->State; + } + + FailingDeviceInfo->PreviousState = FailingDeviceInfo->State; + FailingDeviceInfo->State = DSM_DEV_FAILED; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_RW, + "DsmpSetLBForPathFailingALUA (DevInfo %p): changed to state %d at %d\n", + FailingDeviceInfo, + FailingDeviceInfo->State, + __LINE__)); + } + + group = FailingDeviceInfo->Group; + + if (group->LoadBalanceType < DSM_LB_FAILOVER || + group->LoadBalanceType > DSM_LB_LEAST_BLOCKS) { + + status = STATUS_INVALID_PARAMETER; + + } else { + + irql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); + + // + // Check if there are any active paths that can be used. + // + deviceInfo = DsmpGetAnyActivePath(group, FALSE, NULL, SpecialHandlingFlag); + if (!deviceInfo) { + + // + // Check if an Set/Report TPG has already been sent for this failing devInfo + // + failDevInfoListEntry = DsmpFindFailPathDevInfoEntry(DsmContext, group, FailingDeviceInfo); + + if (!failDevInfoListEntry) { + + BOOLEAN sendTPG = TRUE; + + deviceInfo = DsmpFindStandbyPathToActivateALUA(group, &sendTPG, SpecialHandlingFlag); + + if (deviceInfo) { + + if (sendTPG) { + + tpgCompletionContext = DsmpAllocatePool(NonPagedPoolNx, + sizeof(DSM_TPG_COMPLETION_CONTEXT), + DSM_TAG_TPG_COMPLETION_CONTEXT); + + if (tpgCompletionContext) { + UCHAR senseInfoLength = SENSE_BUFFER_SIZE_EX; + + senseInfo = DsmpAllocatePool(NonPagedPoolNx, + senseInfoLength, + DSM_TAG_SCSI_SENSE_INFO); + + if (senseInfo) { + + srb = DsmpAllocatePool(NonPagedPoolNx, + sizeof(SCSI_REQUEST_BLOCK), + DSM_TAG_SCSI_REQUEST_BLOCK); + + if (srb) { + + srb->Length = SCSI_REQUEST_BLOCK_SIZE; + srb->Function = SRB_FUNCTION_EXECUTE_SCSI; + + completionContext = ExAllocateFromNPagedLookasideList(&DsmContext->CompletionContextList); + if (completionContext) { + + // + // Update the target port group that needs to be made + // active/optimized. We will send down an STPG for + // storages that support both implicit and explicit. + // If the storage does NOT like our choice of A/O TPG, + // it will make an implicit transition. This is still + // a better option than solely relying on the storage's + // implicit transitions at this stage and ending up with + // no path in A/O state. + // + if (deviceInfo->ALUASupport >= DSM_DEVINFO_ALUA_EXPLICIT) { + + targetPortGroupsInfoLength = SPC3_TARGET_PORT_GROUPS_HEADER_SIZE + + sizeof(SPC3_SET_TARGET_PORT_GROUP_DESCRIPTOR); + + } else { + + // + // Find an active/optimized target port group that should + // have been set by the controller + // + // Take care of worst case scenario, which is: + // 1. 4-byte header (for allocation length) + // 2. 32 8-byte descriptors (for TPGs) + // 3. Each descriptor containing 32 4-byte identifiers (for TPs in each TPG) + // + targetPortGroupsInfoLength = SPC3_TARGET_PORT_GROUPS_HEADER_SIZE + + (DSM_MAX_PATHS * (sizeof(SPC3_REPORT_TARGET_PORT_GROUP_DESCRIPTOR) + + DSM_MAX_PATHS * sizeof(ULONG))); + } + + targetPortGroupsInfo = DsmpAllocatePool(NonPagedPoolNx, + targetPortGroupsInfoLength, + DSM_TAG_TARGET_PORT_GROUPS); + + if (targetPortGroupsInfo) { + + failDevInfoListEntry = DsmpBuildFailPathDevInfoEntry(DsmContext, + group, + FailingDeviceInfo, + deviceInfo); + + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); + + if (failDevInfoListEntry) { + + tpgDescriptor = (PSPC3_SET_TARGET_PORT_GROUP_DESCRIPTOR)(targetPortGroupsInfo + SPC3_TARGET_PORT_GROUPS_HEADER_SIZE); + tpgDescriptor->AsymmetricAccessState = DSM_DEV_ACTIVE_OPTIMIZED; + REVERSE_BYTES_SHORT(&tpgDescriptor->TPG_Identifier, &deviceInfo->TargetPortGroup->Identifier); + + // + // Prevent the device info from being removed when a TPG is in-flight. + // + InterlockedIncrement(&FailingDeviceInfo->BlockRemove); + + completionContext->DeviceInfo = FailingDeviceInfo; + completionContext->DsmContext = DsmContext; + completionContext->RequestUnique1 = deviceInfo; + completionContext->RequestUnique2 = FALSE; + + tpgCompletionContext->CompletionContext = completionContext; + tpgCompletionContext->Srb = srb; + tpgCompletionContext->SenseInfoBuffer = senseInfo; + tpgCompletionContext->SenseInfoBufferLength = senseInfoLength; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_RW, + "DsmpSetLBForPathFailingALUA (DevInfo %p): Sending down TPG asynchronously for %p using devInfo %p (path %p).\n", + FailingDeviceInfo, + FailingDeviceInfo->FailGroup->PathId, + deviceInfo, + deviceInfo->FailGroup->PathId)); + + if (deviceInfo->ALUASupport >= DSM_DEVINFO_ALUA_EXPLICIT) { + + status = DsmpSetTargetPortGroupsAsync(deviceInfo, + DsmpPhase1ProcessPathFailingALUA, + tpgCompletionContext, + targetPortGroupsInfoLength, + targetPortGroupsInfo); + } else { + + status = DsmpReportTargetPortGroupsAsync(deviceInfo, + DsmpPhase2ProcessPathFailingALUA, + tpgCompletionContext, + targetPortGroupsInfoLength, + targetPortGroupsInfo); + } + + if (status != STATUS_PENDING) { + + // + // Request not sent down successfully. Free the allocations. + // + DsmpFreePool(targetPortGroupsInfo); + ExFreeToNPagedLookasideList(&DsmContext->CompletionContextList, completionContext); + DsmpFreePool(srb); + DsmpFreePool(senseInfo); + DsmpFreePool(tpgCompletionContext); + + // + // Allow the failing device to be removed. + // + InterlockedDecrement(&FailingDeviceInfo->BlockRemove); + } + } else { + + // + // Fail to build DevInfo entry. Free the allocations. + // + DsmpFreePool(targetPortGroupsInfo); + ExFreeToNPagedLookasideList(&DsmContext->CompletionContextList, completionContext); + DsmpFreePool(srb); + DsmpFreePool(senseInfo); + DsmpFreePool(tpgCompletionContext); + + } + } else { + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); + + ExFreeToNPagedLookasideList(&DsmContext->CompletionContextList, completionContext); + DsmpFreePool(srb); + DsmpFreePool(senseInfo); + DsmpFreePool(tpgCompletionContext); + } + } else { + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); + + NT_ASSERT(completionContext != NULL); + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmpSetLBForPathFailingALUA (DevInfo %p): Failed to allocate completion context. Failing path %p.\n", + FailingDeviceInfo, + FailingDeviceInfo->FailGroup->PathId)); + + DsmpFreePool(srb); + DsmpFreePool(senseInfo); + DsmpFreePool(tpgCompletionContext); + } + } else { + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); + + NT_ASSERT(srb != NULL); + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmpSetLBForPathFailingALUA (DevInfo %p): Failed to allocate SRB. Failing path %p.\n", + FailingDeviceInfo, + FailingDeviceInfo->FailGroup->PathId)); + + DsmpFreePool(senseInfo); + DsmpFreePool(tpgCompletionContext); + } + } else { + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); + + NT_ASSERT(senseInfo != NULL); + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmpSetLBForPathFailingALUA (DevInfo %p): Failed to allocate senseInfo. Failing path %p.\n", + FailingDeviceInfo, + FailingDeviceInfo->FailGroup->PathId)); + + DsmpFreePool(tpgCompletionContext); + } + } else { + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); + + NT_ASSERT(tpgCompletionContext != NULL); + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmpSetLBForPathFailingALUA (DevInfo %p): Failed to allocate TPG completion context. Failing path %p.\n", + FailingDeviceInfo, + FailingDeviceInfo->FailGroup->PathId)); + } + } else { + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); + + deviceInfo->PreviousState = deviceInfo->State; + deviceInfo->State = DSM_DEV_ACTIVE_OPTIMIZED; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_RW, + "DsmpSetLBForPathFailingALUA (DevInfo %p): Found alternative devInfo %p for failing path %p without need for TPG\n", + FailingDeviceInfo, + deviceInfo, + FailingDeviceInfo->FailGroup->PathId)); + } + } else { + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmpSetLBForPathFailingALUA (DevInfo %p): Couldn't find a standby path to activate for failing path %p.\n", + FailingDeviceInfo, + FailingDeviceInfo->FailGroup->PathId)); + } + } else { + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); + + deviceInfo = failDevInfoListEntry->TempDeviceInfo; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_RW, + "DsmpSetLBForPathFailingALUA (DevInfo %p): There is an RTPG/STPG already in progress for this path %p. Returning alternative %p.\n", + FailingDeviceInfo, + FailingDeviceInfo->FailGroup->PathId, + deviceInfo)); + } + } else { + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_RW, + "DsmpSetLBForPathFailingALUA (DevInfo %p): Other paths active, no path made active at %d\n", + FailingDeviceInfo, + __LINE__)); + } + + status = STATUS_SUCCESS; + } + + if (deviceInfo) { + + // + // Update temporarily the next path to be used for the group as this devInfo + // + InterlockedExchangePointer(&(group->PathToBeUsed), deviceInfo->FailGroup); + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_RW, + "DsmpSetLBForPathFailingALUA (DevInfo %p): Updating PathToBeUsed in %p to %p\n", + FailingDeviceInfo, + group, + group->PathToBeUsed)); + + } else { + + InterlockedExchangePointer(&(group->PathToBeUsed), NULL); + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_RW, + "DsmpSetLBForPathRemovalALUA (DevInfo %p): No FOG available for group %p\n", + FailingDeviceInfo, + group)); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmpSetLBForPathFailingALUA (DevInfo %p): Exiting function with status %x.\n", + FailingDeviceInfo, + status)); + + return status; +} + + +NTSTATUS +DsmpSetPathForIoRetryALUA( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_DEVICE_INFO FailingDeviceInfo, + _In_ IN BOOLEAN TPGException, + _In_ IN BOOLEAN DeviceInfoException + ) +/*++ + +Routine Description: + + This routine is called when an IO that was sent using this path fails with + a retry-able "ALUA" error. What this basically means is that most likely an + implicit transition has taken place and we need an RTPG to get the updated + path states. The routine will send down a Report Target Port Groups command + asynchronously to get the paths states (actual work done in the completion + routine). + + Note: This should NOT be called with DsmContext Lock held + This is used for devices supporting ALUA. + +Arguements: + + DsmContext is the DSM context + + FailingDeviceInfo is the device info for the failing/going-away path + + TPGException is a flag used to indicate if the selected path must be from a + TPG that is different from FailingDeviceInfo's. This is + special handling for UA with sense "TPG in SB/UA state" + + DeviceInfoException is a flag used to indicate that the current FailindDeviceInfo + itself needs to be used again. This is special handling for + UA with sense "Asymmetric Access State Changed" + + (NOTE: TPGException and DeviceInfoException are mutually exclusive, although + it is okay for both to be FALSE) + +Return Value: + + Status + +--*/ +{ + PDSM_GROUP_ENTRY group; + PDSM_DEVICE_INFO deviceInfo = NULL; + NTSTATUS status = STATUS_SUCCESS; + KIRQL irql; + PUCHAR targetPortGroupsInfo = NULL; + ULONG targetPortGroupsInfoLength; + PDSM_COMPLETION_CONTEXT completionContext = NULL; + PVOID senseInfo = NULL; + PSCSI_REQUEST_BLOCK srb = NULL; + PDSM_TPG_COMPLETION_CONTEXT tpgCompletionContext = NULL; + NTSTATUS throttleStatus = STATUS_UNSUCCESSFUL; + ULONG inflightRTPG; + ULONG SpecialHandlingFlag = 0; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmpSetPathForIoRetryALUA (DevInfo %p): Entering function.\n", + FailingDeviceInfo)); + + group = FailingDeviceInfo->Group; + + + if (group->LoadBalanceType < DSM_LB_FAILOVER || + group->LoadBalanceType > DSM_LB_LEAST_BLOCKS) { + + status = STATUS_INVALID_PARAMETER; + + } else { + + // + // First check to see if we need to find a candidate from a different TPG. + // + if (TPGException) { + + irql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); + + // + // Find a candidate in a TPG that is different from this one + // + deviceInfo = DsmpFindStandbyPathInAlternateTpgALUA(group, FailingDeviceInfo, SpecialHandlingFlag); + + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_RW, + "DsmpSetPathForIoRetryALUA (DevInfo %p): Need to try a different TPG deviceInfo %p.\n", + FailingDeviceInfo, + deviceInfo)); + + } else if (DeviceInfoException) { + + // + // Retry on the same path + // + deviceInfo = FailingDeviceInfo; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_RW, + "DsmpSetPathForIoRetryALUA (DevInfo %p): Will retry using same deviceInfo %p.\n", + FailingDeviceInfo, + deviceInfo)); + } + + // + // Check if an Report TPG has already been sent for this group + // + inflightRTPG = InterlockedCompareExchange((LONG volatile*)&group->InFlightRTPG, 0, 0); + + // + // If there is no RTPG currently in flight, use the best candidate found + // above to send down the RTPG. If there is already an RTPG inflight, + // we're done. The result of the RTPG should fix the path states. + // + if (!inflightRTPG) { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_RW, + "DsmpSetPathForIoRetryALUA (DevInfo %p): No RTPG in flight. Try sending one down.\n", + FailingDeviceInfo)); + + // + // If we need a candidate device, first get the currently active one. + // If we can't find one that way, resort to finding the best alternative. + // Basic idea is find SOME path instead of failing IOs back to the application. + // + if (!deviceInfo) { + + irql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); + + // + // Find the best candidate - ie. either a currently A/O path or + // the best alternative path to be made A/O. + // + deviceInfo = DsmpGetAnyActivePath(group, TRUE, deviceInfo, SpecialHandlingFlag); + if (!deviceInfo) { + + BOOLEAN sendTPG = TRUE; + + deviceInfo = DsmpFindStandbyPathToActivateALUA(group, &sendTPG, SpecialHandlingFlag); + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_RW, + "DsmpSetPathForIoRetryALUA (DevInfo %p): No active path. Best alternative %p.\n", + FailingDeviceInfo, + deviceInfo)); + } + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); + } + + if (deviceInfo) { + + tpgCompletionContext = DsmpAllocatePool(NonPagedPoolNx, + sizeof(DSM_TPG_COMPLETION_CONTEXT), + DSM_TAG_TPG_COMPLETION_CONTEXT); + + if (tpgCompletionContext) { + UCHAR senseInfoLength = SENSE_BUFFER_SIZE_EX; + + senseInfo = DsmpAllocatePool(NonPagedPoolNx, + senseInfoLength, + DSM_TAG_SCSI_SENSE_INFO); + + if (senseInfo) { + + srb = DsmpAllocatePool(NonPagedPoolNx, + sizeof(SCSI_REQUEST_BLOCK), + DSM_TAG_SCSI_REQUEST_BLOCK); + + if (srb) { + + srb->Length = SCSI_REQUEST_BLOCK_SIZE; + srb->Function = SRB_FUNCTION_EXECUTE_SCSI; + + completionContext = ExAllocateFromNPagedLookasideList(&DsmContext->CompletionContextList); + + if (completionContext) { + + // + // Find an active/optimized target port group that should + // have been set by the controller + // + // Take care of worst case scenario, which is: + // 1. 4-byte header (for allocation length) + // 2. 32 8-byte descriptors (for TPGs) + // 3. Each descriptor containing 32 4-byte identifiers (for TPs in each TPG) + // + targetPortGroupsInfoLength = SPC3_TARGET_PORT_GROUPS_HEADER_SIZE + + (DSM_MAX_PATHS * (sizeof(SPC3_REPORT_TARGET_PORT_GROUP_DESCRIPTOR) + + DSM_MAX_PATHS * sizeof(ULONG))); + + targetPortGroupsInfo = DsmpAllocatePool(NonPagedPoolNx, + targetPortGroupsInfoLength, + DSM_TAG_TARGET_PORT_GROUPS); + + if (targetPortGroupsInfo) { + + completionContext->DeviceInfo = FailingDeviceInfo; + completionContext->DsmContext = DsmContext; + completionContext->RequestUnique1 = deviceInfo; + completionContext->RequestUnique2 = TRUE; + + tpgCompletionContext->CompletionContext = completionContext; + tpgCompletionContext->Srb = srb; + tpgCompletionContext->SenseInfoBuffer = senseInfo; + tpgCompletionContext->SenseInfoBufferLength = senseInfoLength; + + // + // Now we are all set to send the RTPG request. + // check and set InFlightRTPG to make sure this thread is the only one with + // the RTPG active for this group, since it is possible to have more than one + // threads reaching up to this point in parallel + // + inflightRTPG = InterlockedCompareExchange((LONG volatile*)&group->InFlightRTPG, 1, 0); + if (inflightRTPG) { + + DsmpFreePool(targetPortGroupsInfo); + ExFreeToNPagedLookasideList(&DsmContext->CompletionContextList, completionContext); + DsmpFreePool(srb); + DsmpFreePool(senseInfo); + DsmpFreePool(tpgCompletionContext); + } else { + + // + // Prevent the device info from being removed when a TPG is in-flight. + // + InterlockedIncrement(&FailingDeviceInfo->BlockRemove); + + // + // First try and throttle the IO. The completion routine will + // take care of resuming the IO. + // + if (!InterlockedCompareExchange((LONG volatile*)&group->Throttled, 1, 0)) { + + DsmNotification(((PDSM_CONTEXT)DsmContext)->MPIOContext, + ThrottleIO_V2, + deviceInfo, + FALSE, + &throttleStatus, + 0); + + if (NT_SUCCESS(throttleStatus)) { + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_RW, + "DsmpSetPathForIoRetryALUA (DevInfo %p): Successfully throttled IO. About to send RTPG. Failing path %p.\n", + FailingDeviceInfo, + FailingDeviceInfo->FailGroup->PathId)); + + } else { + + // + // Throttle can fail when the MPDisk is + // 1. Being removed. (or) + // 2. In any other state other than Normal or Degraded. + // + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_RW, + "DsmpSetPathForIoRetryALUA (DevInfo %p): Throttle before RTPG failed. Failing path %p.\n", + FailingDeviceInfo, + FailingDeviceInfo->FailGroup->PathId)); + + InterlockedDecrement((LONG volatile*)&FailingDeviceInfo->Group->Throttled); + } + } else { + + // + // Currently we don't expect this to happen + // + NT_ASSERT(FALSE); + } + + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_RW, + "DsmpSetPathForIoRetryALUA (DevInfo %p): Sending RTPG asynchronously. Failing path %p.\n", + FailingDeviceInfo, + FailingDeviceInfo->FailGroup->PathId)); + + if (STATUS_PENDING != DsmpReportTargetPortGroupsAsync(deviceInfo, + DsmpPhase2ProcessPathFailingALUA, + tpgCompletionContext, + targetPortGroupsInfoLength, + targetPortGroupsInfo)) { + + + + // + // Request not sent down successfully. Free the allocations. + // + DsmpFreePool(targetPortGroupsInfo); + ExFreeToNPagedLookasideList(&DsmContext->CompletionContextList, completionContext); + DsmpFreePool(srb); + DsmpFreePool(senseInfo); + DsmpFreePool(tpgCompletionContext); + + // + // Allow the failing device to be removed. + // + InterlockedDecrement(&FailingDeviceInfo->BlockRemove); + + // + // Resume IO if we throttled requests before calling DsmpReportTargetPortGroupsAsync + // + if (InterlockedCompareExchange((LONG volatile*)&group->Throttled, 0, 1)) { + + NTSTATUS resumeStatus = STATUS_UNSUCCESSFUL; + + DsmNotification(((PDSM_CONTEXT)deviceInfo->DsmContext)->MPIOContext, + ResumeIO_V2, + deviceInfo, + TRUE, + &resumeStatus, + 0); + + if (!NT_SUCCESS(resumeStatus)) { + + // + // Resume can fail when + // 1. The MPDisk is being removed (or) + // 2. The MPDisk is any other state other than throttled (or) + // 3. There is a problem dispatching throttled requests. + // + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_RW, + "DsmpSetPathForIoRetryALUA (DevInfo %p): Resume IO failed.\n", + deviceInfo)); + } + + } + + InterlockedDecrement((LONG volatile*)&group->InFlightRTPG); + } + } + } else { + + NT_ASSERT(targetPortGroupsInfo != NULL); + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmpSetPathForIoRetryALUA (DevInfo %p): Couldn't allocate TPG info buffer. Failing path %p.\n", + FailingDeviceInfo, + FailingDeviceInfo->FailGroup->PathId)); + + ExFreeToNPagedLookasideList(&DsmContext->CompletionContextList, completionContext); + ExFreePool(srb); + ExFreePool(senseInfo); + ExFreePool(tpgCompletionContext); + } + } else { + + NT_ASSERT(completionContext != NULL); + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmpSetPathForIoRetryALUA (DevInfo %p): Couldn't allocate completion context. Failing path %p.\n", + FailingDeviceInfo, + FailingDeviceInfo->FailGroup->PathId)); + + ExFreePool(srb); + ExFreePool(senseInfo); + ExFreePool(tpgCompletionContext); + } + } else { + + NT_ASSERT(srb != NULL); + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmpSetPathForIoRetryALUA (DevInfo %p): Couldn't allocate SRB. Failing path %p.\n", + FailingDeviceInfo, + FailingDeviceInfo->FailGroup->PathId)); + + ExFreePool(senseInfo); + ExFreePool(tpgCompletionContext); + } + } else { + + NT_ASSERT(senseInfo != NULL); + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmpSetPathForIoRetryALUA (DevInfo %p): Couldn't allocate senseInfo. Failing path %p.\n", + FailingDeviceInfo, + FailingDeviceInfo->FailGroup->PathId)); + + ExFreePool(tpgCompletionContext); + } + } else { + + NT_ASSERT(tpgCompletionContext != NULL); + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmpSetPathForIoRetryALUA (DevInfo %p): Couldn't allocate TPG completion context. Failing path %p.\n", + FailingDeviceInfo, + FailingDeviceInfo->FailGroup->PathId)); + } + } else { + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmpSetPathForIoRetryALUA (DevInfo %p): Couldn't find a path for RTPG. Failing path %p.\n", + FailingDeviceInfo, + FailingDeviceInfo->FailGroup->PathId)); + } + } else { + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_RW, + "DsmpSetPathForIoRetryALUA (DevInfo %p): Other paths active, no path made active at %d\n", + FailingDeviceInfo, + __LINE__)); + } + + if(!deviceInfo) { + + // + // It is possible that there are only two TPGs with one of them in U/A + // state and the other in transitioning state. In such a case, we would + // not find an alternative TPG deviceInfo. In addition, if there is an + // RTPG in flight, we won't go down the path of forcibly picking any + // deviceInfo. This is to cover that scenario, else we're left with no + // deviceInfo to do the retry and we'll end up setting the group's PTBU + // to NULL thus failing the retried request (if for eg. the LB is RRWS). + // Need to ensure that we handle this exception case. + // + if (inflightRTPG) { + + irql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); + + // + // Find the best candidate - ie. either a currently A/O path or + // the best alternative path to be made A/O. + // + deviceInfo = DsmpGetAnyActivePath(group, TRUE, deviceInfo, SpecialHandlingFlag); + if (!deviceInfo) { + + BOOLEAN sendTPG = TRUE; + + deviceInfo = DsmpFindStandbyPathToActivateALUA(group, &sendTPG, SpecialHandlingFlag); + } + + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); + } + } + + if(deviceInfo) { + + // + // Update temporarily the next path to be used for the group as this devInfo + // + InterlockedExchangePointer(&(group->PathToBeUsed), deviceInfo->FailGroup); + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_RW, + "DsmpSetPathForIoRetryALUA (DevInfo %p): Updating PathToBeUsed in %p to %p\n", + FailingDeviceInfo, + group, + group->PathToBeUsed)); + + } else { + InterlockedExchangePointer(&(group->PathToBeUsed), NULL); + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmpSetPathForIoRetryALUA (DevInfo %p): No FOG available for group %p\n", + FailingDeviceInfo, + group)); + } + + status = STATUS_SUCCESS; + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmpSetPathForIoRetryALUA (DevInfo %p): Exiting function with status %x.\n", + FailingDeviceInfo, + status)); + + return status; +} + + +PDSM_FAIL_PATH_PROCESSING_LIST_ENTRY +DsmpFindFailPathDevInfoEntry( + _In_ IN PDSM_CONTEXT Context, + _In_ IN PDSM_GROUP_ENTRY Group, + _In_ IN PDSM_DEVICE_INFO FailingDevInfo + ) +/*++ + +Routine Description: + + This routine finds the entry that contains the alternate devInfo to use for + a failing one for the passed in devInfo. + + N.B: This routine MUST be called with DsmContextLock held in either Shared or + Exclusive mode. + +Arguments: + + Context is the DSM's context info. + + Group is the group entry representing the device. + + FailingDevInfo is the device info whose entry needs to be found. + +Return Value: + + Pointer to the entry. NULL if it doesn't exist. + +--*/ +{ + PDSM_FAIL_PATH_PROCESSING_LIST_ENTRY failDevInfoListEntry = NULL; + PLIST_ENTRY entry = NULL; + + UNREFERENCED_PARAMETER(Context); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmpFindFailPathDevInfoEntry (DevInfo %p): Entering function.\n", + FailingDevInfo)); + + for (entry = Group->FailingDevInfoList.Flink; + entry != &Group->FailingDevInfoList; + entry = entry->Flink) { + + failDevInfoListEntry = CONTAINING_RECORD(entry, DSM_FAIL_PATH_PROCESSING_LIST_ENTRY, ListEntry); + NT_ASSERT(failDevInfoListEntry); + + if (failDevInfoListEntry) { + + if (failDevInfoListEntry->FailingDeviceInfo == FailingDevInfo) { + + break; + + } else { + + failDevInfoListEntry = NULL; + } + } + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmpFindFailPathDevInfoEntry (DevInfo %p): Exiting function returning entry %p.\n", + FailingDevInfo, + failDevInfoListEntry)); + + return failDevInfoListEntry; +} + + +PDSM_FAIL_PATH_PROCESSING_LIST_ENTRY +DsmpBuildFailPathDevInfoEntry( + _In_ IN PDSM_CONTEXT Context, + _In_ IN PDSM_GROUP_ENTRY Group, + _In_ IN PDSM_DEVICE_INFO FailingDevInfo, + _In_ IN PDSM_DEVICE_INFO AlternateDevInfo + ) +/*++ + +Routine Description: + + When InterpretError() is called with an IRP that has failed with a fatal error, + if the device is ALUA it is possible that STPG needs to be sent to update a new + devInfo as being Active/Optimized. However, for in-flight IOs that weren't + queued by MPIO, we still need to return a path that can be used. + + This routine is builds an entry that contains the alternate devInfo to use for + a failing one. + + NOTE: Calling function should be holding the spin lock. + +Arguments: + + Context is the DSM's context info. + + Group is the group entry representing the device. + + FailingDevInfo is the device info that was used when the IRP failed. + + AlternateDevInfo is the new one to temporarily use until its state can be properly set. + +Return Value: + + Pointer to the newly built entry. + NULL if there were any errors building it. + +--*/ +{ + PDSM_FAIL_PATH_PROCESSING_LIST_ENTRY failDevInfoListEntry = NULL; + + UNREFERENCED_PARAMETER(Context); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmpBuildFailPathDevInfoEntry (DevInfo %p): Entering function.\n", + FailingDevInfo)); + + failDevInfoListEntry = DsmpAllocatePool(NonPagedPoolNx, + sizeof(DSM_FAIL_PATH_PROCESSING_LIST_ENTRY), + DSM_TAG_FAIL_DEVINFO_LIST_ENTRY); + + if (failDevInfoListEntry) { + + failDevInfoListEntry->FailingDeviceInfo = FailingDevInfo; + failDevInfoListEntry->TempDeviceInfo = AlternateDevInfo; + InsertTailList(&Group->FailingDevInfoList, &failDevInfoListEntry->ListEntry); + InterlockedIncrement((LONG volatile*)&Group->NumberFailingDevInfos); + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmpBuildFailPathDevInfoEntry (DevInfo %p): Failed to allocate memory for entry.\n", + FailingDevInfo)); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmpBuildFailPathDevInfoEntry (DevInfo %p): Exiting function returning entry %p.\n", + FailingDevInfo, + failDevInfoListEntry)); + + return failDevInfoListEntry; +} + + +NTSTATUS +DsmpPhase1ProcessPathFailingALUA( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ) +/*++ + +Routine Description: + + This is the completion routine that is called when the STPG is sent down by + DsmpSetLBForPathFailingALUA. + + The caller SHOULD NOT acquire the DSM Context lock before calling this routine. + +Arguements: + + DeviceObject is the target device object to which Irp was sent + + Irp is the scsi pass through request for STPG + + Context is the completion context. + +Return Value: + + Status + +--*/ +{ + PIO_STACK_LOCATION nextIrpStack = IoGetNextIrpStackLocation(Irp); + PDSM_TPG_COMPLETION_CONTEXT context = (PDSM_TPG_COMPLETION_CONTEXT)Context; + PSCSI_REQUEST_BLOCK srb = context->Srb; + PVOID senseData = context->SenseInfoBuffer; + UCHAR senseDataLength = context->SenseInfoBufferLength; + NTSTATUS status = Irp->IoStatus.Status; + ULONG targetPortGroupsInfoLength = SPC3_TARGET_PORT_GROUPS_HEADER_SIZE + sizeof(SPC3_SET_TARGET_PORT_GROUP_DESCRIPTOR); + PUCHAR targetPortGroupsInfo; + PDSM_DEVICE_INFO deviceInfo = (PDSM_DEVICE_INFO)(context->CompletionContext->RequestUnique1); + PDSM_FAIL_PATH_PROCESSING_LIST_ENTRY failDevInfoListEntry = NULL; + BOOLEAN releaseCompletionContextResources = TRUE; + KIRQL irql; + UCHAR scsiStatus = SrbGetScsiStatus(srb); + + UNREFERENCED_PARAMETER(DeviceObject); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmpPhase1ProcessPathFailingALUA (DevInfo %p): Entering function.\n", + deviceInfo)); + +#if DBG + KeQuerySystemTime(&context->CompletionContext->TickCount); +#endif + + if ((scsiStatus == SCSISTAT_GOOD) && + (NT_SUCCESS(status))) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmpPhase1ProcessPathFailingALUA (DevInfo %p): STPG succeeded.\n", + deviceInfo)); + + } else if (NT_SUCCESS(status) && + scsiStatus == SCSISTAT_CHECK_CONDITION && + DsmpShouldRetryTPGRequest(senseData, senseDataLength)) { + + if ((context->NumberRetries)--) { + + // + // Retry the request + // + + NT_ASSERT(SrbGetDataBuffer(srb) == MmGetMdlVirtualAddress(Irp->MdlAddress)); + + // + // Reset byte count of transfer in SRB Extension. + // + SrbSetDataTransferLength(srb, Irp->MdlAddress->ByteCount); + + // + // Zero SRB statuses. + // + srb->SrbStatus = 0; + SrbSetScsiStatus(srb, 0); + + nextIrpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; + nextIrpStack->MinorFunction = IRP_MN_SCSI_CLASS; + + // + // Save SRB address in next stack for port driver. + // + nextIrpStack->Parameters.Scsi.Srb = srb; + IoSetCompletionRoutine(Irp, DsmpPhase1ProcessPathFailingALUA, Context, TRUE, TRUE, TRUE); + + IoMarkIrpPending(Irp); + + // + // Send the IRP asynchronously + // + DsmSendRequestEx(context->CompletionContext->DsmContext->MPIOContext, + deviceInfo->TargetObject, + Irp, + deviceInfo, + DSM_CALL_COMPLETION_ON_MPIO_ERROR); + + // + // We know that the completion routine will always be called. + // + status = STATUS_PENDING; + goto __Exit_DsmpPhase1ProcessPathFailingALUA; + } + } else { + + irql = ExAcquireSpinLockExclusive(&(context->CompletionContext->DsmContext->DsmContextLock)); + + failDevInfoListEntry = DsmpFindFailPathDevInfoEntry(context->CompletionContext->DsmContext, + context->CompletionContext->DeviceInfo->Group, + context->CompletionContext->DeviceInfo); + + if (failDevInfoListEntry) { + + DsmpRemoveFailPathDevInfoEntry(context->CompletionContext->DsmContext, + context->CompletionContext->DeviceInfo->Group, + failDevInfoListEntry); + } + + ExReleaseSpinLockExclusive(&(context->CompletionContext->DsmContext->DsmContextLock), irql); + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmpPhase1ProcessPathFailingALUA (DevInfo %p): NTStatus 0%x, ScsiStatus 0x%x.\n", + deviceInfo, + status, + scsiStatus)); + } + + if (NT_SUCCESS(status)) { + + // + // An explicit transition may cause changes to some other TPGs. + // So we need to query for the states of all the TPGs and update + // our internal list and its elements. + // + + // + // Take care of worst case scenario, which is: + // 1. 4-byte header (for allocation length) + // 2. 32 8-byte descriptors (for TPGs) + // 3. Each descriptor containing 32 4-byte identifiers (for TPs in each TPG) + // + targetPortGroupsInfoLength = SPC3_TARGET_PORT_GROUPS_HEADER_SIZE + + (DSM_MAX_PATHS * (sizeof(SPC3_REPORT_TARGET_PORT_GROUP_DESCRIPTOR) + + DSM_MAX_PATHS * sizeof(ULONG))); + + targetPortGroupsInfo = DsmpAllocatePool(NonPagedPoolNx, + targetPortGroupsInfoLength, + DSM_TAG_TARGET_PORT_GROUPS); + + if (targetPortGroupsInfo) { + + if (STATUS_PENDING == DsmpReportTargetPortGroupsAsync(deviceInfo, + DsmpPhase2ProcessPathFailingALUA, + Context, + targetPortGroupsInfoLength, + targetPortGroupsInfo)) { + + releaseCompletionContextResources = FALSE; + + } else { + + DsmpFreePool(targetPortGroupsInfo); + } + } else { + + irql = ExAcquireSpinLockExclusive(&(context->CompletionContext->DsmContext->DsmContextLock)); + + failDevInfoListEntry = DsmpFindFailPathDevInfoEntry(context->CompletionContext->DsmContext, + context->CompletionContext->DeviceInfo->Group, + context->CompletionContext->DeviceInfo); + + if (failDevInfoListEntry) { + + DsmpRemoveFailPathDevInfoEntry(context->CompletionContext->DsmContext, + context->CompletionContext->DeviceInfo->Group, + failDevInfoListEntry); + } + + ExReleaseSpinLockExclusive(&(context->CompletionContext->DsmContext->DsmContextLock), irql); + } + } + + // + // Free the allocations. + // + IoFreeMdl(Irp->MdlAddress); + Irp->MdlAddress = NULL; + + DsmpFreePool(Irp->UserBuffer); + + IoFreeIrp(Irp); + Irp = (PIRP) NULL; + + if (releaseCompletionContextResources) { + + // + // Release our hold on the device info so that it can be removed. + // + InterlockedDecrement(&context->CompletionContext->DeviceInfo->BlockRemove); + + ExFreeToNPagedLookasideList(&(context->CompletionContext->DsmContext)->CompletionContextList, context->CompletionContext); + DsmpFreePool(context->Srb); + DsmpFreePool(context->SenseInfoBuffer); +#pragma warning(suppress:6001) // DevDiv 818965 + DsmpFreePool(context); + } + +__Exit_DsmpPhase1ProcessPathFailingALUA: + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmpPhase1ProcessPathFailingALUA (DevInfo %p): Exiting function.\n", + deviceInfo)); + + return STATUS_MORE_PROCESSING_REQUIRED; +} + + +NTSTATUS +DsmpRemoveFailPathDevInfoEntry( + _In_ IN PDSM_CONTEXT Context, + _In_ IN PDSM_GROUP_ENTRY Group, + _In_ IN PDSM_FAIL_PATH_PROCESSING_LIST_ENTRY FailPathDevInfoEntry + ) +/*++ + +Routine Description: + + This routine removes the entry pointed to from the passed in Group's list. + + NOTE: Calling function should be holding the spin lock. + +Arguments: + + Context is the DSM's context info. + + Group is the group entry representing the device. + + FailingPathDevInfoEntry is the entry that needs to be removed. + +Return Value: + + STATUS_SUCCESS if successful, else appropriate NT error code. + +--*/ +{ + PLIST_ENTRY entry = &FailPathDevInfoEntry->ListEntry; + PDSM_DEVICE_INFO deviceInfo = FailPathDevInfoEntry->FailingDeviceInfo; + NTSTATUS status = STATUS_SUCCESS; + + UNREFERENCED_PARAMETER(Context); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmpRemoveFailPathDevInfoEntry (DevInfo %p): Entering function.\n", + deviceInfo)); + + RemoveEntryList(entry); + DsmpFreePool(FailPathDevInfoEntry); + InterlockedDecrement((LONG volatile*)&Group->NumberFailingDevInfos); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmpRemoveFailPathDevInfoEntry (DevInfo %p): Exiting function with status %x.\n", + deviceInfo, + status)); + + return status; +} + + +NTSTATUS +DsmpPhase2ProcessPathFailingALUA( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ) +/*++ + +Routine Description: + + This is the completion routine that is called when the RTPG is sent down by + DsmpPhase1ProcessPathFailingALUA. + + The caller SHOULD NOT acquire the DSM Context lock before calling this routine. + +Arguements: + + DeviceObject is the target device object to which Irp was sent + + Irp is the scsi pass through request for RTPG + + Context is the completion context. + +Return Value: + + Status + +--*/ +{ + PIO_STACK_LOCATION nextIrpStack = IoGetNextIrpStackLocation(Irp); + PDSM_TPG_COMPLETION_CONTEXT context = (PDSM_TPG_COMPLETION_CONTEXT)Context; + PSCSI_REQUEST_BLOCK srb = context->Srb; + PVOID senseData = context->SenseInfoBuffer; + UCHAR senseDataLength = context->SenseInfoBufferLength; + NTSTATUS status = Irp->IoStatus.Status; + PUCHAR header; + ULONG returnedDataLength = 0; + PUCHAR targetPortGroupsInfo = NULL; + ULONG targetPortGroupsInfoLength = 0; + PDSM_DEVICE_INFO deviceInfo = (PDSM_DEVICE_INFO)(context->CompletionContext->RequestUnique1); + BOOLEAN decrementRTPGcount = (BOOLEAN)(context->CompletionContext->RequestUnique2); + KIRQL irql; + ULONG index; + PDSM_DEVICE_INFO devInfo; + PDSM_TARGET_PORT_GROUP_ENTRY targetPortGroup = NULL; + PDSM_GROUP_ENTRY group = deviceInfo->Group; + PDSM_FAIL_PATH_PROCESSING_LIST_ENTRY failDevInfoListEntry = NULL; + UCHAR scsiStatus = SrbGetScsiStatus(srb); + + UNREFERENCED_PARAMETER(DeviceObject); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmpPhase2ProcessPathFailingALUA (DevInfo %p): Entering function.\n", + deviceInfo)); + +#if DBG + KeQuerySystemTime(&(context->CompletionContext)->TickCount); +#endif + + if ((status == STATUS_BUFFER_OVERFLOW) || + (NT_SUCCESS(status) && + (scsiStatus == SCSISTAT_GOOD))) { + + header = (PUCHAR)((PUCHAR)SrbGetDataBuffer(srb)); + GetUlongFrom4ByteArray(header, returnedDataLength); + + status = STATUS_SUCCESS; + if (returnedDataLength > SrbGetDataTransferLength(srb)) { + + status = STATUS_BUFFER_OVERFLOW; + } + } + + if ((scsiStatus == SCSISTAT_GOOD) && + (NT_SUCCESS(status))) { + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_RW, + "DsmpPhase2ProcessPathFailingALUA (DevInfo %p): RTPG using path %p succeeded.\n", + deviceInfo, + deviceInfo->FailGroup->PathId)); + + header = (PUCHAR)((PUCHAR)SrbGetDataBuffer(srb)); + GetUlongFrom4ByteArray(header, returnedDataLength); + + // + // Allocate a buffer to hold the TPG info. + // + targetPortGroupsInfo = DsmpAllocatePool(NonPagedPoolNx, + SPC3_TARGET_PORT_GROUPS_HEADER_SIZE + returnedDataLength, + DSM_TAG_TARGET_PORT_GROUPS); + + if (targetPortGroupsInfo) { + + targetPortGroupsInfoLength = SPC3_TARGET_PORT_GROUPS_HEADER_SIZE + returnedDataLength; + + // + // Copy it over. + // + RtlCopyMemory(targetPortGroupsInfo, + header, + targetPortGroupsInfoLength); + + } else { + + irql = ExAcquireSpinLockExclusive(&(context->CompletionContext->DsmContext->DsmContextLock)); + + failDevInfoListEntry = DsmpFindFailPathDevInfoEntry(context->CompletionContext->DsmContext, + context->CompletionContext->DeviceInfo->Group, + context->CompletionContext->DeviceInfo); + + if (failDevInfoListEntry) { + + DsmpRemoveFailPathDevInfoEntry(context->CompletionContext->DsmContext, + context->CompletionContext->DeviceInfo->Group, + failDevInfoListEntry); + } + + ExReleaseSpinLockExclusive(&(context->CompletionContext->DsmContext->DsmContextLock), irql); + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmpPhase2ProcessPathFailingALUA (DevInfo %p): Failed to allocate mem for TPG.\n", + deviceInfo)); + + status = STATUS_INSUFFICIENT_RESOURCES; + } + + } else if (NT_SUCCESS(status) && + scsiStatus == SCSISTAT_CHECK_CONDITION && + DsmpShouldRetryTPGRequest(senseData, senseDataLength)) { + + if ((context->NumberRetries)--) { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_RW, + "DsmpPhase2ProcessPathFailingALUA (DevInfo %p): Retrying check condition using path %p. Retries remaining %u.\n", + deviceInfo, + deviceInfo->FailGroup->PathId, + context->NumberRetries)); + + // + // Retry the request + // + + NT_ASSERT(SrbGetDataBuffer(srb) == MmGetMdlVirtualAddress(Irp->MdlAddress)); + + // + // Reset byte count of transfer in SRB Extension to true length. + // + SrbSetDataTransferLength(srb, targetPortGroupsInfoLength); + + // + // Zero SRB statuses. + // + srb->SrbStatus = 0; + SrbSetScsiStatus(srb, 0); + + nextIrpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; + nextIrpStack->MinorFunction = IRP_MN_SCSI_CLASS; + + // + // Save SRB address in next stack for port driver. + // + nextIrpStack->Parameters.Scsi.Srb = srb; + IoSetCompletionRoutine(Irp, DsmpPhase2ProcessPathFailingALUA, Context, TRUE, TRUE, TRUE); + + IoMarkIrpPending(Irp); + + // + // Send the IRP asynchronously + // + DsmSendRequestEx(context->CompletionContext->DsmContext->MPIOContext, + deviceInfo->TargetObject, + Irp, + deviceInfo, + DSM_CALL_COMPLETION_ON_MPIO_ERROR); + + // + // We know that the completion routine will always be called. + // + status = STATUS_PENDING; + goto __Exit_DsmpPhase2ProcessPathFailingALUA; + } + } else { + + irql = ExAcquireSpinLockExclusive(&(context->CompletionContext->DsmContext->DsmContextLock)); + + failDevInfoListEntry = DsmpFindFailPathDevInfoEntry(context->CompletionContext->DsmContext, + context->CompletionContext->DeviceInfo->Group, + context->CompletionContext->DeviceInfo); + + if (failDevInfoListEntry) { + + DsmpRemoveFailPathDevInfoEntry(context->CompletionContext->DsmContext, + context->CompletionContext->DeviceInfo->Group, + failDevInfoListEntry); + } + + ExReleaseSpinLockExclusive(&(context->CompletionContext->DsmContext->DsmContextLock), irql); + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmpPhase2ProcessPathFailingALUA (DevInfo %p): NTStatus 0%x, ScsiStatus 0x%x.\n", + deviceInfo, + status, + SrbGetScsiStatus(srb))); + + // Failed to get TPG Info. + // Here it is possible status is success, but scsiStatus is not. + // If so, set status to unsuccessful. + + if (NT_SUCCESS(status)) { + status = STATUS_UNSUCCESSFUL; + } + } + + if (NT_SUCCESS(status)) { + + irql = ExAcquireSpinLockExclusive(&(context->CompletionContext->DsmContext->DsmContextLock)); + + // + // Parse the TPG information and update the device path states + // + status = DsmpParseTargetPortGroupsInformation(context->CompletionContext->DsmContext, + deviceInfo->Group, + targetPortGroupsInfo, + targetPortGroupsInfoLength); + + for (index = 0; index < DSM_MAX_PATHS; index++) { + + targetPortGroup = deviceInfo->Group->TargetPortGroupList[index]; + + if (targetPortGroup) { + + DsmpUpdateTargetPortGroupDevicesStates(targetPortGroup, targetPortGroup->AsymmetricAccessState); + } + } + + if (NT_SUCCESS(status)) { + + PDSM_DEVICE_INFO tempDevice = NULL; + + // + // Update all the devInfo states. If the device is AO but + // not this device, make it fake AU. + // If device not in AO, make sure that it matches the TPG + // state. + // Ensure that: + // 1. All devices match their ALUA state. + // 2. For RRWS, if a device's desired state is non-A/O, but ALUA state is A/O, mask it. + // 3. For FOO there must be only one A/O device. Preferably the preferred path. + // + for (index = 0; index < DSM_MAX_PATHS; index++) { + + devInfo = group->DeviceList[index]; + + if (devInfo) { + + if (devInfo->ALUAState == DSM_DEV_ACTIVE_OPTIMIZED) { + + // + // In implicit transitions, there is no guarantee that + // the TPG of chosen "deviceInfo" is in A/O state. So + // to play it safe, we hang on to the very first devInfo + // whose TPG is in A/O state. + // + if (!tempDevice && + !DsmpIsDeviceFailedState(devInfo->State)) { + + devInfo->PreviousState = devInfo->State; + devInfo->State = devInfo->ALUAState; + + tempDevice = devInfo; + } + + if (devInfo != deviceInfo) { + + if (!DsmpIsDeviceFailedState(devInfo->State)) { + + // + // For FOO, only one path can be in A/O. + // For RRWS, mask an A/O path if that isn't the desired state. + // + if ((group->LoadBalanceType == DSM_LB_FAILOVER) || + (group->LoadBalanceType == DSM_LB_ROUND_ROBIN_WITH_SUBSET && + devInfo->DesiredState != DSM_DEV_ACTIVE_OPTIMIZED && + devInfo->DesiredState != DSM_DEV_UNDETERMINED)) { + + // + // For implicit transitions, we may have saved off an A/O path. + // Don't undo that. + // + if (tempDevice != devInfo) { + + devInfo->PreviousState = devInfo->State; + devInfo->State = DSM_DEV_ACTIVE_UNOPTIMIZED; + } + + } else { + + devInfo->PreviousState = devInfo->State; + devInfo->State = devInfo->ALUAState; + } + } + } else { + + devInfo->PreviousState = devInfo->State; + + // + // For FOO, only one path can be in A/O state. + // The TPG of the selected "deviceInfo" is in A/O, so this + // can now very well be made the candidate. However, since + // it is possible that we saved off another candidate, we + // now need to replace that with this. + // + if (!DsmpIsDeviceFailedState(devInfo->State) && + devInfo->Group->LoadBalanceType == DSM_LB_FAILOVER && + tempDevice) { + + tempDevice->State = DSM_DEV_ACTIVE_UNOPTIMIZED; + } + + devInfo->State = devInfo->ALUAState; + + tempDevice = devInfo; + } + } else { + + if (!DsmpIsDeviceFailedState(devInfo->State)) { + + devInfo->PreviousState = devInfo->State; + devInfo->State = devInfo->ALUAState; + } + } + } + } + } + + failDevInfoListEntry = DsmpFindFailPathDevInfoEntry(context->CompletionContext->DsmContext, + context->CompletionContext->DeviceInfo->Group, + context->CompletionContext->DeviceInfo); + + if (failDevInfoListEntry) { + + DsmpRemoveFailPathDevInfoEntry(context->CompletionContext->DsmContext, + context->CompletionContext->DeviceInfo->Group, + failDevInfoListEntry); + } + + ExReleaseSpinLockExclusive(&(context->CompletionContext->DsmContext->DsmContextLock), irql); + } + + + // + // Resume IO if we throttled requests. + // + if (InterlockedCompareExchange((LONG volatile*)&group->Throttled, 0, 1)) { + + NTSTATUS resumeStatus = STATUS_UNSUCCESSFUL; + + DsmNotification(((PDSM_CONTEXT)deviceInfo->DsmContext)->MPIOContext, + ResumeIO_V2, + deviceInfo, + TRUE, + &resumeStatus, + 0); + + if (!NT_SUCCESS(resumeStatus)) { + + // + // Resume can fail when + // 1. The MPDisk is being removed (or) + // 2. The MPDisk is any other state other than throttled (or) + // 3. There is a problem dispatching throttled requests. + // + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_RW, + "DsmpPhase2ProcessPathFailingALUA (DevObj %p): Resume IO failed.\n", + DeviceObject)); + } + + } + + if (decrementRTPGcount) { + + // + // Resetting InFlightRTPG after resume so that we don't get into situation where + // new DsmpSetPathForIoRetryALUA caller thread finds that InFlightRTPG is not set but Throttled is set + // + ULONG count = InterlockedCompareExchange((LONG volatile*)&group->InFlightRTPG, 0, 1); + + // + // If decrementRTPGCount flag is set, there must be atleast one RTPG in flight. + // + NT_ASSERT(count); + + UNREFERENCED_PARAMETER(count); + + } + + // + // Free the allocations. + // + if (targetPortGroupsInfo) { + + DsmpFreePool(targetPortGroupsInfo); + } + + // + // Release our hold on the device info so that it can be removed. + // + InterlockedDecrement(&context->CompletionContext->DeviceInfo->BlockRemove); + + IoFreeMdl(Irp->MdlAddress); + Irp->MdlAddress = NULL; + + DsmpFreePool(Irp->UserBuffer); + + IoFreeIrp(Irp); + Irp = (PIRP) NULL; + + ExFreeToNPagedLookasideList(&(context->CompletionContext->DsmContext)->CompletionContextList, context->CompletionContext); + DsmpFreePool(srb); + DsmpFreePool(senseData); +#pragma warning(suppress:6001) // DevDiv 818965 + DsmpFreePool(context); + +__Exit_DsmpPhase2ProcessPathFailingALUA: + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmpPhase2ProcessPathFailingALUA (DevInfo %p): Exiting function.\n", + deviceInfo)); + + return STATUS_MORE_PROCESSING_REQUIRED; +} + + +NTSTATUS +DsmpPersistentReserveOut( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_IDS DsmIds, + _In_ IN PIRP Irp, + _In_ IN PSCSI_REQUEST_BLOCK Srb, + _In_ IN PKEVENT Event + ) +/*++ + +Routine Description: + + This routine will handle determine which devices to send the request to based + on the service action of the PR-out command. + On REGISTER/REGISTER_AND_IGNORE_EXISTING, it will send the command down all + paths. If any path succeeds, the PR key will be stored. If failure down any + path, return failure. + On REGISTER/REGISTER_AND_IGNORE_EXISTING with key == 0 (ie. UNREGISTER), + the request is sent down every path. Failure is returned if request fails down + any path (but error is ignored if path happens to be one where prior + REGISTER/REGISTER_AND_IGNORE_EXISTING had failed in the first place). The + stored PR key is cleared irrespective of success/failure being returned. + On RESERVE/RELEASE, the command is sent down one path. If it fails, another + path is tried. Failure is returned only if none succeed. + On CLEAR, command is sent down one path. If it fails, another path is tried. + Failure is returned only if none succeed. The stored PR key is cleared + irrespective of success/failure being returned. + On PREEMPT, command is sent down one path. If it fails, another path is + tried. Failure is returned only if none succeed. + On PREEMPT_AND_ABORT, command is sent down one path. Failed request is not + retried. + + NOTE: If a path shows up later, REGISTER_AND_IGNORE_EXISTING request is + built by the IsPathActive routine using the saved PR key and sent down new path. + +Arguments: + + DsmContext - DSM context given to MPIO during initialization + DsmIds - The collection of DSM IDs that pertain to the MPDISK. + Irp - Irp containing SRB. + Srb - Scsi request block + Event - The event to + +Return Value: + + NTSTATUS of the operation. + +--*/ +{ + PDSM_DEVICE_INFO deviceInfo; + PDSM_DEVICE_INFO servicingDeviceInfo = NULL; + PDSM_GROUP_ENTRY group; + LONG i; + ULONG count; + NTSTATUS status = STATUS_UNSUCCESSFUL; + PDSM_COMPLETION_CONTEXT completionContext; + PCDB cdb = SrbGetCdb(Srb); + UCHAR serviceAction; + NTSTATUS returnStatus = STATUS_SUCCESS; + BOOLEAN sendDownAll = FALSE; + BOOLEAN savePRKeyIfAnySucceed = FALSE; + BOOLEAN retryOnAnother = FALSE; + BOOLEAN passOnlyIfAllSucceed = FALSE; + BOOLEAN ignoreIfPreviousFailed = FALSE; + BOOLEAN clearPRKey = FALSE; + KEVENT event; + PPRO_PARAMETER_LIST prOutParam = Irp->AssociatedIrp.SystemBuffer; + PUCHAR index = NULL; + UCHAR prKey[8] = {0}; + PSTORAGE_REQUEST_BLOCK_HEADER srbCopy = NULL; + PIO_STACK_LOCATION irpStack; + PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp); + BOOLEAN statusUpdated = FALSE; + ULONGLONG currentTickCount; + ULONGLONG finalTickCount; + ULONG tickLength = KeQueryTimeIncrement(); + PVOID senseInfoBuffer = NULL; + UCHAR senseInfoBufferLength = 0; + BOOLEAN srbCopySucceeded = FALSE; + UCHAR prType; + UCHAR prScope; + ULONGLONG saKey; + ULONGLONG resKey; + ULONG SpecialHandlingFlag = 0; + + UNREFERENCED_PARAMETER(Event); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_IOCTL, + "DsmpPersistentReserveOut (DsmIds %p): Entering function.\n", + DsmIds)); + + // + // Cache away a copy of the SRB + // + srbCopy = SrbAllocateCopy(Srb, NonPagedPoolNx, DSM_TAG_SCSI_REQUEST_BLOCK); + if (srbCopy == NULL) { + returnStatus = STATUS_INSUFFICIENT_RESOURCES; + goto __Exit_DsmpPersistentReserveOut; + } + + deviceInfo = DsmIds->IdList[0]; + group = deviceInfo->Group; + + prType = cdb->PERSISTENT_RESERVE_OUT.Type; + prScope = cdb->PERSISTENT_RESERVE_OUT.Scope; + serviceAction = cdb->PERSISTENT_RESERVE_OUT.ServiceAction; + + NT_ASSERT(serviceAction >= RESERVATION_ACTION_REGISTER && serviceAction <= RESERVATION_ACTION_REGISTER_IGNORE_EXISTING); + + index = prOutParam->ServiceActionReservationKey; + RtlCopyMemory(&prKey, index, 8); + REVERSE_BYTES_QUAD(&saKey, &prOutParam->ServiceActionReservationKey); + + REVERSE_BYTES_QUAD(&resKey, &prOutParam->ReservationKey); + + switch (serviceAction) { + case RESERVATION_ACTION_REGISTER: + case RESERVATION_ACTION_REGISTER_IGNORE_EXISTING: { + + // + // The command must be sent down all paths. + // + sendDownAll = TRUE; + + // + // Return failure if it fails down even one of the paths. + // + passOnlyIfAllSucceed = TRUE; + + if (DsmpIsPersistentReservationKeyZeroKey(ARRAY_SIZE(prKey), prKey)) { + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_IOCTL, + "DsmpPersistentReserveOut (DsmIds %p): NULL PR key, service action %u\n", + DsmIds, + serviceAction)); + + // + // If unregister fails, don't report it back as error if the + // previous register/register_and_ignore_existing down that + // path had failed too. + // + ignoreIfPreviousFailed = TRUE; + + // + // Clear the group PR key irrespective of the status that is + // going to be returned to clusdisk. + // + clearPRKey = TRUE; + + } else { + + // + // If register/register_and_ignore_existing succeed down any of + // the paths, save off the PR key for the group entry. + // + savePRKeyIfAnySucceed = TRUE; + } + + break; + } + + case RESERVATION_ACTION_RESERVE: + case RESERVATION_ACTION_RELEASE: + case RESERVATION_ACTION_PREEMPT: + case RESERVATION_ACTION_PREEMPT_ABORT: + case RESERVATION_ACTION_CLEAR: { + + if (serviceAction != RESERVATION_ACTION_PREEMPT_ABORT) { + + // + // Apart from preempt_abort, all the others must be retried + // (down another path) if they fail down the chosen path. + // + retryOnAnother = TRUE; + } + + if (serviceAction == RESERVATION_ACTION_CLEAR) { + + // + // Clear the stored PR key for the group entry irrespective of + // the status that is going to be returned back. + // + clearPRKey = TRUE; + } + + break; + } + + default: { + + returnStatus = STATUS_INVALID_PARAMETER; + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_IOCTL, + "DsmpPersistentReserveOut (DsmIds %p): Invalid service action %u.\n", + DsmIds, + serviceAction)); + + goto __Exit_DsmpPersistentReserveOut; + } + } + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_IOCTL, + "DsmpPersistentReserveOut (DsmIds %p): Srb %p, Service Action %u, Type %u, Scope %u, \ + \n\t\t\t\tservice action reservation key %I64x, reservation key %I64x.\n", + DsmIds, + Srb, + serviceAction, + prType, + prScope, + saKey, + resKey)); + + // + // Allocate a context for the completion routine. + // + completionContext = ExAllocateFromNPagedLookasideList(&DsmContext->CompletionContextList); + if (!completionContext) { + + returnStatus = STATUS_INSUFFICIENT_RESOURCES; + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_IOCTL, + "DsmpPersistentReserveOut (DsmIds %p): PR_OUT %u - Failed to allocate completion context.\n", + DsmIds, + serviceAction)); + + goto __Exit_DsmpPersistentReserveOut; + } + + KeInitializeEvent(&event, NotificationEvent, FALSE); + + // + // Indicate the target for this request. + // + completionContext->DsmContext = DsmContext; + completionContext->RequestUnique1 = (PVOID)&event; + completionContext->RequestUnique2 = cdb->PERSISTENT_RESERVE_OUT.OperationCode; + + count = group->NumberDevices; + + for (i = count - 1; i >= 0; i--) { + + // + // A PR command may fail with a "retry-able" UA when reservation is + // released or preempted (on every I_T_L nexus except the one on which + // it was released/preempted). In such a case we should retry the PR + // command on the same path. + // + KeQueryTickCount((PLARGE_INTEGER)¤tTickCount); + finalTickCount = currentTickCount + (DSM_SECONDS_TO_TICKS(group->MaxPRRetryTimeDuringStateTransition) / tickLength); + + if (!sendDownAll && !retryOnAnother) { + + // + // If the request doesn't need to be retried (down another path) on + // failure, better choose the path that has maximum chances of + // success. + // + deviceInfo = DsmpGetActivePathToBeUsed(group, + DsmpIsSymmetricAccess((PDSM_DEVICE_INFO)DsmIds->IdList[0]), + SpecialHandlingFlag); + + if (!deviceInfo) { + + returnStatus = STATUS_UNSUCCESSFUL; + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_IOCTL, + "DsmpPersistentReserveOut (DsmIds %p): PR_OUT %u - No active/alternative path for device %p.\n", + DsmIds, + serviceAction, + group)); + + break; + } + + } else { + + deviceInfo = group->DeviceList[i]; + + // + // Ignore "bad" paths for now. If the path becomes "good" again, + // IsPathActive() will send down the register. + // Also, don't consider newly arrived paths for which the group has + // a reservation but register has not yet been sent down. This rule + // applies only to requests that are not Register. + // + if ((DsmpIsDeviceFailedState(deviceInfo->State) || !DsmpIsDeviceInitialized(deviceInfo)) || + (!DsmpIsDeviceUsablePR(deviceInfo) && !sendDownAll)) { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_IOCTL, + "DsmpPersistentReserveOut (DsmIds %p): Ignoring bad instance - state %x, init %x, key reg %x (key valid %x).\n", + DsmIds, + deviceInfo->State, + deviceInfo->Initialized, + deviceInfo->PRKeyRegistered, + deviceInfo->Group->PRKeyValid)); + + deviceInfo = NULL; + } + + } + + if (!deviceInfo) { + + // + // Maybe a remove came through and caused a collapse of the device + // list, thus making this entry empty. + // + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_IOCTL, + "DsmpPersistentReserveOut (DsmIds %p): PR_OUT %u - Couldn't find path for device %p.\n", + DsmIds, + serviceAction, + group)); + + continue; + } + +__DsmpPersistentReserveOut_RetryRequest: + + IoMarkIrpPending(Irp); + + completionContext->DeviceInfo = deviceInfo; + + // + // Set-up a completion routine. + // + IoSetCompletionRoutine(Irp, + DsmpPersistentReserveCompletion, + completionContext, + TRUE, + TRUE, + TRUE); + + // + // Always send the original request down a new path + // + irpStack = IoGetNextIrpStackLocation(Irp); + srbCopySucceeded = SrbCopySrb(Srb, SrbGetSrbLength(Srb), srbCopy); + NT_ASSERT(srbCopySucceeded == TRUE); + irpStack->Parameters.Scsi.Srb = Srb; + + // + // Clear the sense buffer if it exists + // + senseInfoBuffer = SrbGetSenseInfoBuffer(Srb); + senseInfoBufferLength = SrbGetSenseInfoBufferLength(Srb); + if (senseInfoBuffer) { + RtlZeroMemory(senseInfoBuffer, senseInfoBufferLength); + } + + servicingDeviceInfo = deviceInfo; + + // + // Issue the request and wait. + // + status = DsmSendRequest(DsmContext->MPIOContext, + deviceInfo->TargetObject, + Irp, + deviceInfo); + + if (status == STATUS_PENDING) { + + KeWaitForSingleObject(&event, + Executive, + KernelMode, + FALSE, + NULL); + + status = Irp->IoStatus.Status; + } + + if (NT_SUCCESS(status) || status == STATUS_BUFFER_OVERFLOW) { + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_IOCTL, + "DsmpPersistentReserveOut (DsmIds %p): PR_OUT %u sent down successfully on %p.\n", + DsmIds, + serviceAction, + deviceInfo->FailGroup->PathId)); + + if (!passOnlyIfAllSucceed) { + + // + // Success down any one path means success + // + returnStatus = status; + } + + if (savePRKeyIfAnySucceed) { + + RtlCopyMemory(&group->PersistentReservationRegisteredKey, &prKey, 8); + + group->PRServiceAction = serviceAction; + group->PRType = prType; + group->PRScope = prScope; + group->PRKeyValid = TRUE; + deviceInfo->PRKeyRegistered = TRUE; + } + + if (!sendDownAll) { + + // + // Need for retrying on another path only necessary in the case + // of request failing down the chosen path. Since the request + // succeeded down this path, we are done. + // + break; + } + } else { + + BOOLEAN recordFailure; + + // + // Check to see if the request failed because of a "transient error", + // like reservations released for example. If so, this is NOT an actual + // error and the request must be retried. Multiple retries may be required + // if for example the UA indicates that the TPGs are in transitioning state. + // + if (Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID && + Srb->SrbStatus & SRB_STATUS_ERROR && + SrbGetScsiStatus(Srb) == SCSISTAT_CHECK_CONDITION) { + + KeQueryTickCount((PLARGE_INTEGER)¤tTickCount); + + senseInfoBuffer = SrbGetSenseInfoBuffer(Srb); + senseInfoBufferLength = SrbGetSenseInfoBufferLength(Srb); + + if (DsmpShouldRetryPersistentReserveCommand(senseInfoBuffer, senseInfoBufferLength) && + currentTickCount < finalTickCount) { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_IOCTL, + "DsmpPersistentReserveOut (DsmIds %p): PR_OUT %u returned UA with error %x. Retrying same path %p.\n", + DsmIds, + serviceAction, + status, + deviceInfo->FailGroup->PathId)); + + KeResetEvent(&event); + Irp->IoStatus.Status = 0; + + goto __DsmpPersistentReserveOut_RetryRequest; + } + } + + // + // The return status is STATUS_SUCCESS by default. This means that if the + // request failed on the first path and was retried down every other path + // but fails down all of them, the return status is never updated. + // So cache the first failure status to cover the above scenario. + // + if (!statusUpdated) { + + returnStatus = status; + statusUpdated = TRUE; + } + + recordFailure = TRUE; + if (ignoreIfPreviousFailed && !deviceInfo->PRKeyRegistered) { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_IOCTL, + "DsmpPersistentReserveOut (DsmIds %p): PR_OUT %u - Ignoring status %x for path %p.\n", + DsmIds, + serviceAction, + status, + deviceInfo->FailGroup->PathId)); + + // + // Okay to ignore this failure if the previous failed. + // + recordFailure = FALSE; + } + + if (passOnlyIfAllSucceed && recordFailure) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_IOCTL, + "DsmpPersistentReserveOut (DsmIds %p): PR_OUT %u - Saving status %x for return.\n", + DsmIds, + serviceAction, + status)); + + // + // Save the failure status to return back. + // + returnStatus = status; + } + + // + // If the request is not to be sent down all paths, and also + // a retry (along a different path) on failure is not required, + // we're done - just return this failure. + // + if (!(sendDownAll || retryOnAnother)) { + + returnStatus = status; + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_IOCTL, + "DsmpPersistentReserveOut (DsmIds %p): PR_OUT %u sent down %p failed with %x. Breaking out.\n", + DsmIds, + serviceAction, + deviceInfo->FailGroup->PathId, + status)); + + break; + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_IOCTL, + "DsmpPersistentReserveOut (DsmIds %p): PR_OUT %u sent down %p failed with %x. Sending down another path.\n", + DsmIds, + serviceAction, + deviceInfo->FailGroup->PathId, + status)); + } + } + + // + // If we are here, it is either because the request needs to be sent down + // all paths, or because the request failed down the chosen path and needs + // to be retried down a new path. + // + KeResetEvent(&event); + Irp->IoStatus.Status = 0; + } + + if (clearPRKey) { + + for (i = 0; (ULONG)i < group->NumberDevices; i++) { + + deviceInfo = group->DeviceList[i]; + + if (deviceInfo) { + + deviceInfo->RegisterServiced = FALSE; + deviceInfo->PRKeyRegistered = FALSE; + } + } + group->PersistentReservationRegisteredKey[0] = group->PersistentReservationRegisteredKey[1] = + group->PersistentReservationRegisteredKey[2] = group->PersistentReservationRegisteredKey[3] = + group->PersistentReservationRegisteredKey[4] = group->PersistentReservationRegisteredKey[5] = + group->PersistentReservationRegisteredKey[6] = group->PersistentReservationRegisteredKey[7] = 0; + + group->PRKeyValid = FALSE; + group->ReservationList = 0; + } + + if (savePRKeyIfAnySucceed && group->PRKeyValid) { + + ULONG ordinal; + + for (i = 0; (ULONG)i < group->NumberDevices; i++) { + + deviceInfo = group->DeviceList[i]; + + if (deviceInfo) { + + deviceInfo->RegisterServiced = TRUE; + ordinal = (1 << i); + group->ReservationList |= ordinal; + } + } + } + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_IOCTL, + "DsmpPersistentReserveOut (DsmIds %p): PR_OUT for %u completed with status %x.\n", + DsmIds, + serviceAction, + returnStatus)); + + ExFreeToNPagedLookasideList(&DsmContext->CompletionContextList, completionContext); + +__Exit_DsmpPersistentReserveOut: + + if (srbCopy != NULL) { + DsmpFreePool(srbCopy); + } + + currentIrpStack->Parameters.Others.Argument3 = servicingDeviceInfo; + Irp->IoStatus.Status = returnStatus; + if ((!NT_SUCCESS(returnStatus)) && + (SrbGetSrbStatus(Srb) == SRB_STATUS_SUCCESS)) { + SrbSetSrbStatus(Srb, DsmpNtStatusToSrbStatus(returnStatus)); + } + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_IOCTL, + "DsmpPersistentReserveOut (DsmIds %p): Exiting function returning IRP status %x.\n", + DsmIds, + returnStatus)); + + return returnStatus; +} + + + +NTSTATUS +DsmpPersistentReserveIn( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_IDS DsmIds, + _In_ IN PIRP Irp, + _In_ IN PSCSI_REQUEST_BLOCK Srb, + _In_ IN PKEVENT Event + ) +/*++ + +Routine Description: + + This routine will handle determine which devices to send the request to based + on the service action of the PR-in command. + On READ KEYS, it will send down one path. In case of failure, other paths will + be tried until one succeeds. Failure is returned only if it fails down all paths. + On READ_RESERVATION/REPORT_CAPABILITIES, command is sent down one path. Failed + request is not retried. + +Arguments: + + DsmContext - DSM context given to MPIO during initialization + DsmIds - The collection of DSM IDs that pertain to the MPDISK. + Irp - Irp containing SRB. + Srb - Scsi request block + Event - The event to + +Return Value: + + NTSTATUS of the operation. + +--*/ +{ + PDSM_DEVICE_INFO deviceInfo; + PDSM_DEVICE_INFO servicingDeviceInfo = NULL; + PDSM_GROUP_ENTRY group; + LONG i; + ULONG count; + NTSTATUS status = STATUS_UNSUCCESSFUL; + PDSM_COMPLETION_CONTEXT completionContext; + PCDB cdb = SrbGetCdb(Srb); + UCHAR serviceAction; + BOOLEAN retryOnAnother = FALSE; + KEVENT event; + PSTORAGE_REQUEST_BLOCK_HEADER srbCopy = NULL; + PIO_STACK_LOCATION irpStack; + PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp); + ULONGLONG currentTickCount; + ULONGLONG finalTickCount; + ULONG tickLength = KeQueryTimeIncrement(); + PVOID senseInfoBuffer = NULL; + UCHAR senseInfoBufferLength = 0; + BOOLEAN srbCopySucceeded = FALSE; + ULONG SpecialHandlingFlag = 0; + + UNREFERENCED_PARAMETER(Event); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_IOCTL, + "DsmpPersistentReserveIn (DsmIds %p): Entering function.\n", + DsmIds)); + + // + // Cache away a copy of the SRB + // + srbCopy = SrbAllocateCopy(Srb, NonPagedPoolNx, DSM_TAG_SCSI_REQUEST_BLOCK); + if (srbCopy == NULL) { + status = STATUS_INSUFFICIENT_RESOURCES; + goto __Exit_DsmpPersistentReserveIn; + } + + deviceInfo = DsmIds->IdList[0]; + group = deviceInfo->Group; + + serviceAction = cdb->PERSISTENT_RESERVE_IN.ServiceAction; + + switch (serviceAction) { + case RESERVATION_ACTION_READ_RESERVATIONS: + case RESERVATION_ACTION_READ_KEYS: { + + // + // If there is a failure on the chosen path, retry on another path. + // + retryOnAnother = TRUE; + break; + } + + case SPC3_RESERVATION_ACTION_REPORT_CAPABILITIES: { + + break; + } + + default: { + + NT_ASSERT(FALSE); + status = STATUS_INVALID_PARAMETER; + goto __Exit_DsmpPersistentReserveIn; + } + } + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_IOCTL, + "DsmpPersistentReserveIn (DsmIds %p): Srb %p. Service Action %u.\n", + DsmIds, + Srb, + serviceAction)); + + // + // Allocate a context for the completion routine. + // + completionContext = ExAllocateFromNPagedLookasideList(&DsmContext->CompletionContextList); + if (!completionContext) { + + status = STATUS_INSUFFICIENT_RESOURCES; + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_IOCTL, + "DsmpPersistentReserveIn (DsmIds %p): PR_IN %u - Failed to allocate completion context.\n", + DsmIds, + serviceAction)); + + goto __Exit_DsmpPersistentReserveIn; + } + + KeInitializeEvent(&event, NotificationEvent, FALSE); + + // + // Indicate the target for this request. + // + completionContext->DsmContext = DsmContext; + completionContext->RequestUnique1 = (PVOID)&event; + completionContext->RequestUnique2 = cdb->PERSISTENT_RESERVE_IN.OperationCode; + + count = group->NumberDevices; + + for (i = count - 1; i >= 0; i--) { + + // + // A PR command may fail with a "retry-able" UA when reservation is + // released or preempted (on every I_T_L nexus except the one on which + // it was released/preempted). In such a case we should retry the PR + // command on the same path. + // + KeQueryTickCount((PLARGE_INTEGER)¤tTickCount); + finalTickCount = currentTickCount + (DSM_SECONDS_TO_TICKS(group->MaxPRRetryTimeDuringStateTransition) / tickLength); + + if (!retryOnAnother) { + + // + // If the request doesn't need to be retried (down another path) on + // failure, better choose the path that has maximum chances of + // success. + // + deviceInfo = DsmpGetActivePathToBeUsed(group, + DsmpIsSymmetricAccess((PDSM_DEVICE_INFO)DsmIds->IdList[0]), + SpecialHandlingFlag); + + if (!deviceInfo) { + + status = STATUS_UNSUCCESSFUL; + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_IOCTL, + "DsmpPersistentReserveIn (DsmIds %p): PR_IN %u - No active/alternative path for device %p.\n", + DsmIds, + serviceAction, + group)); + + break; + } + + } else { + + deviceInfo = group->DeviceList[i]; + + if (DsmpIsDeviceFailedState(deviceInfo->State) || !DsmpIsDeviceInitialized(deviceInfo)) { + + // + // Ignore "bad" paths for now. + // + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_IOCTL, + "DsmpPersistentReserveIn (DsmIds %p): Ignoring bad instance - state %x, init %x.\n", + DsmIds, + deviceInfo->State, + deviceInfo->Initialized)); + + deviceInfo = NULL; + } + } + + if (!deviceInfo) { + + // + // Maybe a remove came through and caused a collapse of the device + // list, thus making this entry empty. + // + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_IOCTL, + "DsmpPersistentReserveIn (DsmIds %p): PR_IN %u - Couldn't find path for device %p.\n", + DsmIds, + serviceAction, + group)); + + continue; + } + +__DsmpPersistentReserveIn_RetryRequest: + + IoMarkIrpPending(Irp); + + completionContext->DeviceInfo = deviceInfo; + + // + // Set-up a completion routine. + // + IoSetCompletionRoutine(Irp, + DsmpPersistentReserveCompletion, + completionContext, + TRUE, + TRUE, + TRUE); + + // + // Always send the original request down a new path + // + irpStack = IoGetNextIrpStackLocation(Irp); + srbCopySucceeded = SrbCopySrb(Srb, SrbGetSrbLength(Srb), srbCopy); + NT_ASSERT(srbCopySucceeded == TRUE); + irpStack->Parameters.Scsi.Srb = Srb; + + // + // Clear the sense buffer if it exists + // + senseInfoBuffer = SrbGetSenseInfoBuffer(Srb); + senseInfoBufferLength = SrbGetSenseInfoBufferLength(Srb); + if (senseInfoBuffer) { + RtlZeroMemory(senseInfoBuffer, senseInfoBufferLength); + } + + servicingDeviceInfo = deviceInfo; + + // + // Issue the request and wait. + // + status = DsmSendRequest(DsmContext->MPIOContext, + deviceInfo->TargetObject, + Irp, + deviceInfo); + + if (status == STATUS_PENDING) { + + KeWaitForSingleObject(&event, + Executive, + KernelMode, + FALSE, + NULL); + + status = Irp->IoStatus.Status; + } + + if (NT_SUCCESS(status) || status == STATUS_BUFFER_OVERFLOW) { + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_IOCTL, + "DsmpPersistentReserveIn (DsmIds %p): PR_IN for %u sent down successfully on %p.\n", + DsmIds, + serviceAction, + deviceInfo->FailGroup->PathId)); +#if DBG + if (serviceAction == RESERVATION_ACTION_READ_KEYS) { + + PPRI_REGISTRATION_LIST prInRegistrationList = Irp->AssociatedIrp.SystemBuffer; + ULONG numberOfKeys; + ULONG keyIndex; + ULONGLONG prKey; + + REVERSE_BYTES(&numberOfKeys, &prInRegistrationList->AdditionalLength); + numberOfKeys /= 8; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_IOCTL, + "DsmpPersistentReserveIn (DsmIds %p): %u registrations keys present:\n", + DsmIds, + numberOfKeys)); + + for (keyIndex = 0; keyIndex < numberOfKeys; keyIndex++) { + + REVERSE_BYTES_QUAD(&prKey, &(prInRegistrationList->ReservationKeyList[keyIndex])); + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_IOCTL, + "DsmpPersistentReserveIn (DsmIds %p): Registration Key %u: %I64x\n", + DsmIds, + keyIndex, + prKey)); + } + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_IOCTL, + "\n")); + + } else if (serviceAction == RESERVATION_ACTION_READ_RESERVATIONS) { + + PPRI_RESERVATION_LIST prInReservationList = Irp->AssociatedIrp.SystemBuffer; + ULONG numberOfDescriptors; + PPRI_RESERVATION_DESCRIPTOR prInReservationDescriptor = prInReservationList->Reservations; + ULONGLONG prKey = 0; + + REVERSE_BYTES(&numberOfDescriptors, &prInReservationList->AdditionalLength); + numberOfDescriptors /= sizeof(PRI_RESERVATION_DESCRIPTOR); + NT_ASSERT(numberOfDescriptors <= 1); + + if (numberOfDescriptors == 1) { + REVERSE_BYTES_QUAD(&prKey, &prInReservationDescriptor->ReservationKey); + } + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_IOCTL, + "DsmpPersistentReserveIn (DsmIds %p): %u Reservation Key: %I64x\n", + DsmIds, + numberOfDescriptors, + prKey)); + } +#endif + // + // Done. + // + break; + + } else { + + // + // Check to see if the request failed because of a "transient error", + // like reservations released for example. If so, this is NOT an actual + // error and the request must be retried. Multiple retries may be required + // if for example the UA indicates that the TPGs are in transitioning state. + // + if (Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID && + Srb->SrbStatus & SRB_STATUS_ERROR && + SrbGetScsiStatus(Srb) == SCSISTAT_CHECK_CONDITION) { + + KeQueryTickCount((PLARGE_INTEGER)¤tTickCount); + + senseInfoBuffer = SrbGetSenseInfoBuffer(Srb); + senseInfoBufferLength = SrbGetSenseInfoBufferLength(Srb); + + if (group->PRKeyValid && + DsmpShouldRetryPersistentReserveCommand(senseInfoBuffer, senseInfoBufferLength) && + currentTickCount < finalTickCount) { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_IOCTL, + "DsmpPersistentReserveIn (DsmIds %p): PR_IN %u returned UA with error %x. Retrying same path %p.\n", + DsmIds, + serviceAction, + status, + deviceInfo->FailGroup->PathId)); + + KeResetEvent(&event); + Irp->IoStatus.Status = 0; + + goto __DsmpPersistentReserveIn_RetryRequest; + } + } + + // + // If a retry (along a different path) on failure is not required, + // we're done - just return this failure. + // + if (!retryOnAnother) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_IOCTL, + "DsmpPersistentReserveIn (DsmIds %p): PR_IN for %u down %p failed with %x. Breaking out.\n", + DsmIds, + serviceAction, + deviceInfo->FailGroup->PathId, + status)); + + break; + } + } + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_IOCTL, + "DsmpPersistentReserveIn (DsmIds %p): PR_IN for %u down %p failed with %x. Sending down another path.\n", + DsmIds, + serviceAction, + deviceInfo->FailGroup->PathId, + status)); + + // + // If we are here, it is because the request failed down the chosen path + // and needs to be retried down a new path. + // + KeResetEvent(&event); + Irp->IoStatus.Status = 0; + } + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_IOCTL, + "DsmpPersistentReserveIn (DsmIds %p): PR_IN for %u completed with status %x.\n", + DsmIds, + serviceAction, + status)); + + ExFreeToNPagedLookasideList(&DsmContext->CompletionContextList, completionContext); + +__Exit_DsmpPersistentReserveIn: + + if (srbCopy != NULL) { + DsmpFreePool(srbCopy); + } + + currentIrpStack->Parameters.Others.Argument3 = servicingDeviceInfo; + Irp->IoStatus.Status = status; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_IOCTL, + "DsmpPersistentReserveIn (DsmIds %p): Exiting function returning IRP status %x.\n", + DsmIds, + status)); + + return status; +} + + +NTSTATUS +DsmpPersistentReserveCompletion( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ) +/*++ + +Routine Description: + + General-purpose completion routine for PR in and out commands sent synchronously. + +Arguments: + + DeviceObject - Target of the request. + Irp - Command being sent. + Context - The event on which the caller is waiting. + +Return Value: + + NTSTATUS + +--*/ + +{ + PDSM_COMPLETION_CONTEXT context = Context; + PKEVENT event; + + // It is required to specify a DSM completion context + // when setting DsmpPersistentReserveCompletion as completion routine. + _Analysis_assume_(context != NULL); + + event = (PKEVENT)(context->RequestUnique1); + + UNREFERENCED_PARAMETER(DeviceObject); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_IOCTL, + "DsmpPersistentReserveCompletion: DevInfo %p, IRP %p, Context %p\n", + context->DeviceInfo, + Irp, + Context)); + + if (Irp->PendingReturned) { + + IoMarkIrpPending(Irp); + } + + KeSetEvent(event, 0, FALSE); + + return STATUS_MORE_PROCESSING_REQUIRED; +} + diff --git a/storage/msdsm/src/dsmtrace.mof b/storage/msdsm/src/dsmtrace.mof new file mode 100644 index 000000000..d9e8cf4cc --- /dev/null +++ b/storage/msdsm/src/dsmtrace.mof @@ -0,0 +1,111 @@ +#pragma classflags("forceupdate") +#pragma namespace("\\\\.\\root\\WMI") +// +// Copyright (C) 2004 Microsoft Corporation +// +// WPP Generated File +// + +//ModuleName = wppCtlGuid (Init called in Function DriverEntry) +[Dynamic, + Description("MSDSM Driver Tracing Provider"), + guid("{DEDADFF5-F99F-4600-B8C9-2D4D9B806B5B}"), + locale("MS\\0x409")] +class MSDSMGuid : EventTrace +{ + [Description ("Enable Flags"), + ValueDescriptions{ + "TRACE_FLAG_GENERAL Flag", + "TRACE_FLAG_PNP Flag", + "TRACE_FLAG_POWER Flag", + "TRACE_FLAG_RW Flag", + "TRACE_FLAG_IOCTL Flag", + "TRACE_FLAG_QUEUE Flag", + "TRACE_FLAG_WMI Flag", + "TRACE_FLAG_TIMER Flag", + "TRACE_FLAG_INIT Flag", + "TRACE_FLAG_LOCK Flag", + "TRACE_FLAG_DEBUG1 Flag", + "TRACE_FLAG_DEBUG2 Flag", + "TRACE_FLAG_MCN Flag", + "TRACE_FLAG_ISR Flag", + "TRACE_FLAG_ENUM Flag"}, + DefineValues{ + "TRACE_FLAG_GENERAL", + "TRACE_FLAG_PNP", + "TRACE_FLAG_POWER", + "TRACE_FLAG_RW", + "TRACE_FLAG_IOCTL", + "TRACE_FLAG_QUEUE", + "TRACE_FLAG_WMI", + "TRACE_FLAG_TIMER", + "TRACE_FLAG_INIT", + "TRACE_FLAG_LOCK", + "TRACE_FLAG_DEBUG1", + "TRACE_FLAG_DEBUG2", + "TRACE_FLAG_MCN", + "TRACE_FLAG_ISR", + "TRACE_FLAG_ENUM"}, + Values{ + "TRACE_FLAG_GENERAL", + "TRACE_FLAG_PNP", + "TRACE_FLAG_POWER", + "TRACE_FLAG_RW", + "TRACE_FLAG_IOCTL", + "TRACE_FLAG_QUEUE", + "TRACE_FLAG_WMI", + "TRACE_FLAG_TIMER", + "TRACE_FLAG_INIT", + "TRACE_FLAG_LOCK", + "TRACE_FLAG_DEBUG1", + "TRACE_FLAG_DEBUG2", + "TRACE_FLAG_MCN", + "TRACE_FLAG_ISR", + "TRACE_FLAG_ENUM"}, + ValueMap{ + "0x00000001", + "0x00000002", + "0x00000004", + "0x00000008", + "0x00000010", + "0x00000020", + "0x00000040", + "0x00000080", + "0x00000100", + "0x00000200", + "0x00000400", + "0x00000800", + "0x00001000", + "0x00002000", + "0x00004000"} + ] + uint32 Flags; + [Description ("Levels"), + ValueDescriptions{ + "Abnormal exit or termination", + "Severe errors that need logging", + "Warnings such as allocation failure", + "Includes non-error cases", + "Detailed traces from intermediate steps" }, + DefineValues{ + "TRACE_LEVEL_FATAL", + "TRACE_LEVEL_ERROR", + "TRACE_LEVEL_WARNING" + "TRACE_LEVEL_INFORMATION", + "TRACE_LEVEL_VERBOSE" }, + Values{ + "Fatal", + "Error", + "Warning", + "Information", + "Verbose" }, + ValueMap{ + "0x1", + "0x2", + "0x3", + "0x4", + "0x5" }, + ValueType("index") + ] + uint32 Level; +}; diff --git a/storage/msdsm/src/intrface.c b/storage/msdsm/src/intrface.c new file mode 100644 index 000000000..ab05199af --- /dev/null +++ b/storage/msdsm/src/intrface.c @@ -0,0 +1,5198 @@ +/*++ + +Copyright (C) 2004-2010 Microsoft Corporation + +Module Name: + + intrface.c + +Abstract: + + This driver is the Microsoft Device Specific Module (DSM) + devices that conform with SPC-3 specs. + It exports behaviors that mpio.sys will use to determine how to + multipath these devices. + + This file contains DriverEntry and all the functions that are + exported to MPIO. + + This DSM is targetted towards Windows 2008 and above. + +Environment: + + kernel mode only + +--*/ + +#define DEBUG_MAIN_SOURCE 1 + +#include "precomp.h" + +#ifdef DEBUG_USE_WPP +#include "intrface.tmh" +#endif + +#pragma warning (disable:4305) + + +// +// Flag to indicate whether to NT_ASSERT or ignore a paritcular condition. +// +BOOLEAN DoAssert = TRUE; + +// +// OS Version Info +// MSDSM is targetted towards Windows Server 2008 and above. +// +BOOLEAN gServer2008AndAbove = FALSE; + +// +// Global to cache MPIO's Control Object. +// +PDEVICE_OBJECT gMPIOControlObject = NULL; + +// +// Flag to indicate if the MPIO control object was referenced. +// +BOOLEAN gMPIOControlObjectRefd = FALSE; + +// +// Global to cache the Driver Object. +// +PDRIVER_OBJECT gDsmDriverObject = NULL; + + +#ifdef ALLOC_PRAGMA + #pragma alloc_text(INIT, DriverEntry) +#endif + +// +// The code. +// +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Description: + + This routine is called when the driver is loaded. + +Arguments: + + DriverObject - Supplies the driver object. + RegistryPath - Supplies the registry path. + +Return Value: + + NTSTATUS + +--*/ +{ + PDSM_CONTEXT dsmContext = NULL; + PFILE_OBJECT fileObject; + WCHAR dosDeviceName[64] = DSM_MPIO_CONTROL_OBJECT_SYMLINK; + UNICODE_STRING mpUnicodeName; + NTSTATUS status = STATUS_SUCCESS; + MPIO_VERSION_INFO versionInfo = {0}; + DSM_TYPE dsmMode = DsmType3; + DSM_MPIO_CONTEXT mpctlContext; + IO_STATUS_BLOCK ioStatus; + + + // + // Initialize the tracing subsystem. + // Any failure is handled by ETW itself. + // + WPP_INIT_TRACING(DriverObject, RegistryPath); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_INIT, + "DriverEntry (DrvObj %p): Entering function.\n", + DriverObject)); + + gDsmDriverObject = DriverObject; + + // + // Determine the OS version. + // + gServer2008AndAbove = RtlIsNtDdiVersionAvailable(NTDDI_VISTA); + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_INIT, + "DriverEntry (DrvObj %p): Server2008AndAbove is %!bool!.\n", + DriverObject, + gServer2008AndAbove)); + + // + // MSDSM is supported only on Server 2008 and above. + // + if (!gServer2008AndAbove) { + + status = STATUS_NOT_SUPPORTED; + goto __Exit_DriverEntry; + } + + // + // Build the mpio symbolic link name. + // + RtlInitUnicodeString(&mpUnicodeName, dosDeviceName); + + // + // Get a pointer to mpio's deviceObject. + // + status = IoGetDeviceObjectPointer(&mpUnicodeName, + FILE_READ_ATTRIBUTES, + &fileObject, + &gMPIOControlObject); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_FATAL, + TRACE_FLAG_INIT, + "DriverEntry (DrvObj %p): Failed to communicate with MPIO control object. Status %x.\n", + DriverObject, + status)); + + goto __Exit_DriverEntry; + } + + ObReferenceObject(gMPIOControlObject); + gMPIOControlObjectRefd = TRUE; + ObDereferenceObject(fileObject); + + status = DsmGetVersion(&versionInfo, sizeof(MPIO_VERSION_INFO)); + + if (!NT_SUCCESS(status)) { + + // + // If we can't get the version, that means we aren't using a compatible + // version of MPIO drivers and so should not continue. + // + TracePrint((TRACE_LEVEL_FATAL, + TRACE_FLAG_INIT, + "DriverEntry (DrvObj %p): MPIO version unknown - DSM exiting.\n", + DriverObject)); + + status = STATUS_UNSUCCESSFUL; + goto __Exit_DriverEntry; + } + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_INIT, + "DriverEntry (DrvObj %p): MPIO version %d.%d.%d.%d.\n", + DriverObject, + versionInfo.MajorVersion, + versionInfo.MinorVersion, + versionInfo.ProductBuild, + versionInfo.QfeNumber)); + + RtlZeroMemory(&gDsmInitData, sizeof(DSM_INIT_DATA)); + + // + // Must be newer than 1.0.7.0 to support DSM type 2 upwards. + // + if ((versionInfo.MajorVersion > 1) || + (versionInfo.MinorVersion >= 1) || + (versionInfo.ProductBuild > 7) || + (versionInfo.QfeNumber >= 1)) { + + // + // Must be newer than 1.18 to support DSM's versioning + // + if (versionInfo.MajorVersion > 1 || + versionInfo.MinorVersion > 17) { + + dsmMode = DsmType6; + + { + RTL_OSVERSIONINFOW osVersion = {0}; + + osVersion.dwOSVersionInfoSize = sizeof(OSVERSIONINFOW); + RtlGetVersion(&osVersion); + + gDsmInitData.DsmVersion.MajorVersion = osVersion.dwMajorVersion; + gDsmInitData.DsmVersion.MinorVersion = osVersion.dwMinorVersion; + gDsmInitData.DsmVersion.ProductBuild = osVersion.dwBuildNumber; + gDsmInitData.DsmVersion.QfeNumber = 0; + } + } + } else { + + // + // We cannot use this DSM with older versions of the MPIO drivers. + // + TracePrint((TRACE_LEVEL_FATAL, + TRACE_FLAG_INIT, + "DriverEntry (DrvObj %p): MPIO version not supported - DSM exiting.\n", + DriverObject)); + + status = STATUS_UNSUCCESSFUL; + goto __Exit_DriverEntry; + } + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_INIT, + "DriverEntry (DrvObj %p): Setting DSM type to %d.\n", + DriverObject, + dsmMode)); + + // + // Build the init data structure. + // + dsmContext = DsmpAllocatePool(NonPagedPoolNx, + sizeof(DSM_CONTEXT), + DSM_TAG_DSM_CONTEXT); + if (!dsmContext) { + + TracePrint((TRACE_LEVEL_FATAL, + TRACE_FLAG_INIT, + "DriverEntry (DrvObj %p): Failed to allocate memory for DSM Context.\n", + DriverObject)); + + status = STATUS_INSUFFICIENT_RESOURCES; + goto __Exit_DriverEntry; + } + + // + // Set-up the init data + // + gDsmInitData.DsmContext = (PVOID) dsmContext; + gDsmInitData.InitDataSize = sizeof(DSM_INIT_DATA); + + gDsmInitData.DsmInquireDriver = DsmInquire; + gDsmInitData.DsmCompareDevices = DsmCompareDevices; + gDsmInitData.DsmGetControllerInfo = DsmGetControllerInfo; + gDsmInitData.DsmSetDeviceInfo = DsmSetDeviceInfo; + gDsmInitData.DsmIsPathActive = DsmIsPathActive; + gDsmInitData.DsmPathVerify = DsmPathVerify; + gDsmInitData.DsmInvalidatePath = DsmInvalidatePath; + gDsmInitData.DsmMoveDevice = DsmMoveDevice; + gDsmInitData.DsmRemovePending = DsmRemovePending; + gDsmInitData.DsmRemoveDevice = DsmRemoveDevice; + gDsmInitData.DsmRemovePath = DsmRemovePath; + gDsmInitData.DsmSrbDeviceControl = DsmSrbDeviceControl; + gDsmInitData.DsmLBGetPath = DsmLBGetPath; + gDsmInitData.DsmInterpretErrorEx = DsmInterpretError; + gDsmInitData.DsmUnload = DsmUnload; + gDsmInitData.DsmSetCompletion = DsmSetCompletion; + gDsmInitData.DsmCategorizeRequest = DsmCategorizeRequest; + gDsmInitData.DsmBroadcastSrb = DsmBroadcastRequest; + gDsmInitData.DsmIsAddressTypeSupported = DsmIsAddressTypeSupported; + gDsmInitData.DsmDeviceNotUsed = DsmDeviceNotUsed; + + // + // Since MSDSM is for SPC-3 compliant devices, MPIO should be able to build + // a serial number for the device. + // + gDsmInitData.DsmDeviceSerialNumber = NULL; + + // + // Notifies MPIO of the appropriate Type support + // + gDsmInitData.DsmType = dsmMode; + + gDsmInitData.DriverObject = DriverObject; + + + // + // Set-up the WMI Info. + // + DsmpWmiInitialize(&gDsmInitData.DsmWmiInfo, RegistryPath); + DsmpDsmWmiInitialize(&gDsmInitData.DsmWmiGlobalInfo, RegistryPath); + + RtlInitUnicodeString(&gDsmInitData.DisplayName, DSM_FRIENDLY_NAME); + + // + // Initialize some of the fields in DSM Context structure. + // + KeInitializeSpinLock(&dsmContext->SupportedDevicesListLock); + InitializeListHead(&dsmContext->GroupList); + InitializeListHead(&dsmContext->DeviceList); + InitializeListHead(&dsmContext->FailGroupList); + InitializeListHead(&dsmContext->ControllerList); + InitializeListHead(&dsmContext->StaleFailGroupList); + + // + // Build the list context structures used for completion processing. + // + ExInitializeNPagedLookasideList(&dsmContext->CompletionContextList, + NULL, + NULL, + POOL_NX_ALLOCATION, + sizeof(DSM_COMPLETION_CONTEXT), + DSM_TAG_GENERIC, + 0); + + RtlZeroMemory(&mpctlContext, sizeof(DSM_MPIO_CONTEXT)); + + // + // Send the IOCTL to mpio.sys to register ourselves. + // + DsmSendDeviceIoControlSynchronous(IOCTL_MPDSM_REGISTER, + gMPIOControlObject, + &gDsmInitData, + &mpctlContext, + sizeof(DSM_INIT_DATA), + sizeof(DSM_MPIO_CONTEXT), + TRUE, + &ioStatus); + + status = ioStatus.Status; + + if (NT_SUCCESS(status)) { + + dsmContext->MPIOContext = mpctlContext.MPIOContext; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_INIT, + "DriverEntry (DrvObj %p): Registered with MPIO.\n", + DriverObject)); + + DriverObject->DriverUnload = DsmDriverUnload; + + // + // Query the registry for disabling/enabling statistics gathering + // + if (STATUS_OBJECT_NAME_NOT_FOUND == DsmpGetStatsGatheringChoice(dsmContext, (PULONG)&dsmContext->DisableStatsGathering)) { + + // + // If the value does not exist, write the default to registry. + // + DsmpSetStatsGatheringChoice(dsmContext, (ULONG)dsmContext->DisableStatsGathering); + } + } + +__Exit_DriverEntry: + + if (NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_INIT, + "DriverEntry (DrvObj %p): Exiting function successfully.\n", + DriverObject)); + } else { + + // + // Since the DSM is going to be unloaded but without DriverUnload being + // called, we need to perform cleanup here. + // + if (dsmContext != NULL) { + DsmpFreeDSMResources(dsmContext); + dsmContext = NULL; + } + + if (gMPIOControlObjectRefd) { + + // + // Drop the reference on MPIO's control object. + // + ObDereferenceObject(gMPIOControlObject); + gMPIOControlObjectRefd = FALSE; + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_INIT, + "DriverEntry (DrvObj %p): Exiting function with status %x.\n", + DriverObject, + status)); + + // + // Stop the tracing subsystem. + // NOTE: once we unregister ETW, no more TracePrint can be done, so we + // must ensure that ETW unregister is the last thing that happens. + // + WPP_CLEANUP(gDsmDriverObject); + } + + return status; +} + + +VOID +DsmDriverUnload( + _In_ IN PDRIVER_OBJECT DriverObject + ) +/*++ + +Routine Description: + + This routine is called when the driver is unloaded. + +Arguments: + + DriverObject - Supplies the driver object. + +Return Value: + + Nothing + +--*/ +{ + DSM_DEREGISTER_DATA deregisterData; + IO_STATUS_BLOCK ioStatus; + + deregisterData.DeregisterDataSize = sizeof(DSM_DEREGISTER_DATA); + deregisterData.DriverObject = DriverObject; + deregisterData.DsmContext = gDsmInitData.DsmContext; + deregisterData.MpioContext = ((PDSM_CONTEXT)(gDsmInitData.DsmContext))->MPIOContext; + // + // Send the IOCTL to mpio.sys to de-register ourselves. + // + DsmSendDeviceIoControlSynchronous(IOCTL_MPDSM_DEREGISTER, + gMPIOControlObject, + &deregisterData, + NULL, + sizeof(DSM_DEREGISTER_DATA), + 0, + TRUE, + &ioStatus); + + NT_ASSERT(NT_SUCCESS(ioStatus.Status)); + + return; +} + + +NTSTATUS +DsmInquire( + _In_ IN PVOID DsmContext, + _In_ IN PDEVICE_OBJECT TargetDevice, + _In_ IN PDEVICE_OBJECT PortObject, + _In_ IN PSTORAGE_DEVICE_DESCRIPTOR Descriptor, + _In_ IN PSTORAGE_DEVICE_ID_DESCRIPTOR DeviceIdList, + _Out_ OUT PVOID *DsmIdentifier + ) +/*++ + +Routine Description: + + This routine is used to determine if TargetDevice belongs to + the DSM. If this is a supported device DsmIdentifier will be + updated with 'deviceInfo'. + +Arguments: + + DsmContext - Context value given to the multipath driver during + registration. + TargetDevice - DeviceObject for the child device. + PortObject - The Port driver FDO on which TargetDevice resides. + Descriptor - Pointer to the device descriptor corresponding to TargetDevice. + Rehash of inquiry data, plus serial number information + (if applicable). + DeviceIdList - VPD Page 0x83 information. + DsmIdentifier - Pointer to be filled in by the DSM on success. + +Return Value: + + STATUS_NOT_SUPPORTED - if not on the SupportList. + STATUS_INSUFFICIENT_RESOURCES - No mem. + STATUS_SUCCESS +--*/ +{ + PDSM_CONTEXT dsmContext = DsmContext; + PDSM_DEVICE_INFO deviceInfo = NULL; + PDSM_GROUP_ENTRY group; + BOOLEAN newGroup; + PDSM_TARGET_PORT_GROUP_ENTRY targetPortGroupEntry = NULL; + PDSM_TARGET_PORT_LIST_ENTRY targetPortEntry = NULL; + PSTR serialNumber = NULL; + SIZE_T serialNumberLength = 0; + NTSTATUS status; + ULONG allocationLength; + BOOLEAN serialNumberAllocated = FALSE; + KIRQL irql = PASSIVE_LEVEL; // Initialize variable to prevent C4701 error + BOOLEAN supported = FALSE; + BOOLEAN spinlockHeld = FALSE; + UCHAR vendorId[9] = {0}; + UCHAR productId[17] = {0}; + INQUIRYDATA inquiryData; + UCHAR alua = DSM_DEVINFO_ALUA_NOT_SUPPORTED; + ULONG index; + PDSM_IDS controllerObjects = NULL; + PDEVICE_OBJECT controllerDeviceObject; + PLIST_ENTRY entry = NULL; + PSTORAGE_DESCRIPTOR_HEADER controllerIdHeader = NULL; + PULONG relativeTargetPortId = NULL; + PUSHORT targetPortGroupId = NULL; + PUCHAR targetPortGroupsInfo = NULL; + ULONG targetPortGroupsInfoLength = 0; + PSTR controllerSerialNumber; + BOOLEAN match = FALSE; + BOOLEAN doneUpdating = FALSE; + PDSM_CONTROLLER_LIST_ENTRY controllerEntry = NULL; + PDSM_TARGET_PORT_DEVICELIST_ENTRY tp_device = NULL; + PWSTR hardwareId = NULL; + PWCHAR deviceName = NULL; + ULONG tempResult = 0; + ULONG maxPRRetryTimeDuringStateTransition = DSM_MAX_PR_UNIT_ATTENTION_RETRY_TIME; + BOOLEAN useCacheForLeastBlocks = FALSE; + ULONGLONG cacheSizeForLeastBlocks = 0; + BOOLEAN fakeControllerEntryExists = FALSE; + STORAGE_IDENTIFIER_CODE_SET serialNumberCodeSet = StorageIdCodeSetReserved; + +#if DBG + BOOLEAN multiport; +#endif + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): Entering function.\n", + TargetDevice)); + + // + // 1. Get standard inquiry for the device. Check if SPC-3 compliant. + // If not compliant, check SupportedDeviceList. + // 2. Create device serial number. + // 3. Create a partially populated deviceInfo. + // DeviceDescriptor. + // SCSI address. + // Save off serial number. + // ALUA, port FDO, etc. + // 4. Create device name. + // 5. If ALUA support, send down Report Target Port Groups. + // 6. Find the group. If none, build one. + // 7. If new group, build target port groups and target ports info. + // Else, update target port groups and target ports info. + // 8. If both implicit as well as explicit transitions allowed, disable implicit. + // 9. Get list of controllers objects and get VPD 0x83 for each (only if no + // match for existing ones). + // Match returned ids of type 0x5 with what was returned in Report Target Port Groups. + // If no type 0x5 identifier, use SCSI address. + // Create controller list (delete stale entries). + // + + + // + // Query the registry to find out what devices are being supported + // on this machine. + // + DsmpGetDeviceList(dsmContext); + + status = DsmpGetStandardInquiryData(TargetDevice, &inquiryData); + + if (NT_SUCCESS(status)) { + + supported = DsmpCheckScsiCompliance(TargetDevice, + &inquiryData, + Descriptor, + DeviceIdList); + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): Failed to get inquiry data with status %x.\n", + TargetDevice, + status)); + + status = STATUS_NOT_SUPPORTED; + goto __Exit_DsmInquire; + } + + // + // Since the device isn't SPC-3 compliant, check if the device is on the + // SupportedDeviceList. + // + if (!supported) { + + + if (!supported) { + + // + // Get the inquiry data embedded in the device descriptor. + // + RtlStringCchCopyA((LPSTR)vendorId, + sizeof(vendorId) / sizeof(vendorId[0]), + (LPCSTR)(&inquiryData.VendorId)); + + RtlStringCchCopyA((LPSTR)productId, + sizeof(productId) / sizeof(productId[0]), + (LPCSTR)(&inquiryData.ProductId)); + + supported = DsmpDeviceSupported(dsmContext, + (PCSZ)vendorId, + (PCSZ)productId); + } + + if (!supported) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): Unsupported Device.\n", + TargetDevice)); + + status = STATUS_NOT_SUPPORTED; + goto __Exit_DsmInquire; + } + } + + // + // Find out if device can be accessed via mulitple ports. This info is + // important since it will determine whether or not to send down a + // ReportTargetPortGroups command. + // +#if DBG + multiport = (inquiryData.MultiPort & 0x10) ? TRUE : FALSE; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): Is %ws multiported.\n", + TargetDevice, + multiport ? L"" : L"not")); +#endif + + // + // Query the assymmetric states transition method + // + switch ((inquiryData.Reserved >> 0x4) & 0x3) { + case 1: alua = DSM_DEVINFO_ALUA_IMPLICIT; + break; + + case 2: alua = DSM_DEVINFO_ALUA_EXPLICIT; + break; + + case 3: alua = DSM_DEVINFO_ALUA_IMPLICIT | DSM_DEVINFO_ALUA_EXPLICIT; + break; + + default: alua = DSM_DEVINFO_ALUA_NOT_SUPPORTED; + break; + } + + // + // Get some information about this device. The preferred info is + // from the Device ID Page. + // + if (DeviceIdList) { + + // + // This will parse out the 'best' identifier and return + // a NULL-terminated ascii string. + // + serialNumber = (PSTR)DsmpParseDeviceID(DeviceIdList, + DSM_DEVID_SERIAL_NUMBER, + NULL, + &serialNumberCodeSet, + FALSE); + + if (!serialNumber) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): NULL serial number.\n", + TargetDevice)); + + // + // Either an allocation failed, or the DeviceIdList is malformed. + // + status = STATUS_NOT_SUPPORTED; + goto __Exit_DsmInquire; + } + + // + // Indicate that the serialnumber buffer is allocated. + // + serialNumberAllocated = TRUE; + serialNumberLength = strlen((const char*)serialNumber); + + } else { + + // + // Get the serial number of this device. Use the serial number + // page (0x80). Ensure that the device's serial number is + // present. If not, can't claim support for this drive. + // + + if (!Descriptor || + (Descriptor->SerialNumberOffset == MAXULONG) || + (Descriptor->SerialNumberOffset == 0)) { + + // + // The port driver currently doesn't get the VPD page 0x80, + // if the device doesn't support GET_SUPPORTED_PAGES. Check to + // see whether there actually is a serial number. + // + serialNumber = DsmpGetSerialNumber(TargetDevice); + + if (!serialNumber) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): serialNumber = NULL.\n", + TargetDevice)); + + status = STATUS_NOT_SUPPORTED; + goto __Exit_DsmInquire; + + } else { + serialNumberAllocated = TRUE; + serialNumberLength = strlen((const char*)serialNumber); + } + } + } + + // + // Allocate for the device. This is also used as DsmId. + // + allocationLength = sizeof(DSM_DEVICE_INFO); + + // + // As DSM_DEVICE_INFO has storage for the descriptor, add only + // the additional stuff that's at the end. + // + if (Descriptor) { + status = RtlULongSub(Descriptor->Size, sizeof(STORAGE_DEVICE_DESCRIPTOR), &tempResult); + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): Arithmetic underflow - status %x.\n", + TargetDevice, + status)); + + status = STATUS_NOT_SUPPORTED; + goto __Exit_DsmInquire; + } + } + + status = RtlULongAdd(allocationLength, tempResult, &allocationLength); + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): Arithmetic overflow - status %x.\n", + TargetDevice, + status)); + + status = STATUS_NOT_SUPPORTED; + goto __Exit_DsmInquire; + } + + deviceInfo = DsmpAllocatePool(NonPagedPoolNx, + allocationLength, + DSM_TAG_DEV_INFO); + if (!deviceInfo) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): Failed to allocate Device Info.\n", + TargetDevice)); + + status = STATUS_NOT_SUPPORTED; + goto __Exit_DsmInquire; + } + + deviceInfo->State = deviceInfo->PreviousState = deviceInfo->TempPreviousStateForLB = deviceInfo->ALUAState = deviceInfo->LastKnownGoodState = DSM_DEV_NOT_USED_STATE; + deviceInfo->DesiredState = DSM_DEV_UNDETERMINED; + // + // Copy over the StorageDescriptor. + // + if (Descriptor) { + RtlCopyMemory(&deviceInfo->Descriptor, + Descriptor, + Descriptor->Size); + } + + // + // Get the scsi address for this device. Note that on success, DsmGetScsiAddress() + // will allocate memory which we are responsible for freeing. + // + status = DsmGetScsiAddress(TargetDevice, + &deviceInfo->ScsiAddress); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): Error %x while getting scsi address.\n", + TargetDevice, + status)); + + status = STATUS_NOT_SUPPORTED; + goto __Exit_DsmInquire; + } + + // + // Capture the serial number allocated flag. + // + deviceInfo->SerialNumberAllocated = serialNumberAllocated; + + // + // Set the serial number. + // + if (!serialNumberAllocated) { + + PSTORAGE_DEVICE_DESCRIPTOR descriptor; + + // + // serialNumber is not pointing to the buffer passed by MPIO. Update + // it to point to the Device Descriptor allocated by the DSM. + // + descriptor = &(deviceInfo->Descriptor); + + NT_ASSERT(descriptor->SerialNumberOffset != 0 && descriptor->SerialNumberOffset != MAXULONG); + + serialNumber = (PCHAR)descriptor + descriptor->SerialNumberOffset; + serialNumberLength = strlen((const char*)serialNumber); + } + + if (alua == (DSM_DEVINFO_ALUA_IMPLICIT | DSM_DEVINFO_ALUA_EXPLICIT)) { + + BOOLEAN disableImplicit = FALSE; + + status = DsmpDisableImplicitStateTransition(TargetDevice, &disableImplicit); + + if (NT_SUCCESS(status)) { + + if (disableImplicit) { + + alua &= ~DSM_DEVINFO_ALUA_IMPLICIT; + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): Disabled implicit ALUA state transition.\n", + TargetDevice)); + + // + // Record that the storage actually supported implicit also, but we + // turned it OFF. + // + deviceInfo->ImplicitDisabled = TRUE; + + } else { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): Storage support both transitions but does NOT allow disabling Implicit.\n", + TargetDevice)); + } + } else { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): Failed to disable implicit ALUA state transitions - status %x.\n", + TargetDevice, + status)); + } + } + + deviceInfo->SerialNumber = serialNumber; + + // + // Save the Physical Device Object (PDO) of the device. + // Used to verify that no two devices have the same PDO. + // + deviceInfo->PortPdo = TargetDevice; + + // + // Save the FDO of the adapter. Used for handling reserve\release + // + deviceInfo->PortFdo = PortObject; + + // + // Set the signature. + // + deviceInfo->DeviceSig = DSM_DEVICE_SIG; + + deviceInfo->DsmContext = DsmContext; + + deviceInfo->ALUASupport = alua; + + // + // Build the name (using serialnumber) that will be used as registry key + // to store Load Balance settings for this device. + // + deviceName = DsmpBuildDeviceName(deviceInfo, serialNumber, serialNumberLength); + + if (!deviceName) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): Failed to allocate device name for %p.\n", + TargetDevice, + deviceInfo)); + + status = STATUS_NOT_SUPPORTED; + goto __Exit_DsmInquire; + } + + + // + // Send down ReportTargetPortGroups command and keep the info handy. + // + if (alua != DSM_DEVINFO_ALUA_NOT_SUPPORTED) { + + status = DsmpReportTargetPortGroups(TargetDevice, + &targetPortGroupsInfo, + &targetPortGroupsInfoLength); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): Failed to report target port groups for %p. Status %x.\n", + TargetDevice, + deviceInfo, + status)); + + status = STATUS_NOT_SUPPORTED; + goto __Exit_DsmInquire; + } + + // + // We've just sent down an RTPG (relatively expensive operation), and it + // succeeded, so sending down one more as part part of the initialization + // in PathVerify() since it is going to be called almost immediately. + // + deviceInfo->IgnorePathVerify = TRUE; + } + + // + // Query the registry for max time to retry failed PR requests + // + DsmpGetMaxPRRetryTime(DsmContext, &maxPRRetryTimeDuringStateTransition); + + // + // Query the registry to see if the user has overridden the default + // Least Blocks settings. + // + status = DsmpQueryCacheInformationFromRegistry(DsmContext, + &useCacheForLeastBlocks, + &cacheSizeForLeastBlocks); + + if (!NT_SUCCESS(status)) { + // + // Couldn't get the settings from the registry so fall back on the + // default for Least Blocks. + // + useCacheForLeastBlocks = TRUE; + cacheSizeForLeastBlocks = DSM_LEAST_BLOCKS_DEFAULT_THRESHOLD; + } + + // + // Build LUN's hardware id. Needs to be called at PASSIVE_LEVEL, so + // do it before grabbing the lock. The hardware id of the group is + // later set under the protection of the lock. + // + hardwareId = DsmpBuildHardwareId(deviceInfo); + + irql = ExAcquireSpinLockExclusive(&(((PDSM_CONTEXT)DsmContext)->DsmContextLock)); + spinlockHeld = TRUE; + + status = STATUS_SUCCESS; + + // + // See if there is an existing Multi-path group to which this belongs. + // (same serial number). + // + group = DsmpFindDevice(DsmContext, deviceInfo, FALSE); + if (!group) { + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): First device %p in the group.\n", + TargetDevice, + deviceInfo)); + + newGroup = TRUE; + + // + // This device doesn't belong to any group yet. So Build a multi-path + // group entry. This'll represents all paths to a particular device. + // + group = DsmpBuildGroupEntry(DsmContext, deviceInfo); + if (group) { + + // + // Set the registry key name for the new group + // + group->RegistryKeyName = deviceName; + deviceName = NULL; + + // + // Cache the LUN's hardware id + // + if (!hardwareId) { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): Failed to build a hardwareId for %p.\n", + TargetDevice, + deviceInfo)); + } + + group->HardwareId = hardwareId; + hardwareId = NULL; + + group->UseCacheForLeastBlocks = useCacheForLeastBlocks; + group->CacheSizeForLeastBlocks = cacheSizeForLeastBlocks; + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): Failed to allocate Group Entry for %p.\n", + TargetDevice, + deviceInfo)); + + status = STATUS_NOT_SUPPORTED; + goto __Exit_DsmInquire; + } + } else { + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): Found group %p for device %p.\n", + TargetDevice, + group, + deviceInfo)); + + newGroup = FALSE; + + if (!group->HardwareId) { + + // + // If we weren't successful in previously building the hardware id for this LUN, + // retry doing it again now. + // + hardwareId = DsmpBuildHardwareId(deviceInfo); + if (!hardwareId) { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): Failed to build a hardwareId for %p.\n", + TargetDevice, + deviceInfo)); + } + + group->HardwareId = hardwareId; + hardwareId = NULL; + } + + // + // Sanity check that we haven't been presented with device instances + // with different ALUA support. So compare with the first device instance. + // + for (index = 0; index < DSM_MAX_PATHS; index++) { + + if (group->DeviceList[index]) { + + break; + } + } + + if (index < DSM_MAX_PATHS) { + + // + // Only acceptable conditions are: + // 1. both have same support, + // 2. one has explicit, while other has both explicit-and-implicit (this + // is a potential valid case because DsmpDisableImplicitStateTransition + // may have failed). + // + if (!((deviceInfo->ALUASupport == group->DeviceList[index]->ALUASupport) || + ((deviceInfo->ALUASupport == DSM_DEVINFO_ALUA_EXPLICIT && deviceInfo->ImplicitDisabled) && + (group->DeviceList[index]->ALUASupport == (DSM_DEVINFO_ALUA_IMPLICIT | DSM_DEVINFO_ALUA_EXPLICIT))) || + ((group->DeviceList[index]->ALUASupport == DSM_DEVINFO_ALUA_EXPLICIT && group->DeviceList[index]->ImplicitDisabled) && + (deviceInfo->ALUASupport == (DSM_DEVINFO_ALUA_IMPLICIT | DSM_DEVINFO_ALUA_EXPLICIT))))) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): Mismatch in device instances' ALUA support %d vs %d.\n", + TargetDevice, + deviceInfo->ALUASupport, + group->DeviceList[index]->ALUASupport)); + + status = STATUS_NOT_SUPPORTED; + goto __Exit_DsmInquire; + } + } + } + + if (NT_SUCCESS(status)) { + + NT_ASSERT(group); + + group->MaxPRRetryTimeDuringStateTransition = maxPRRetryTimeDuringStateTransition; + + if (alua == DSM_DEVINFO_ALUA_NOT_SUPPORTED) { + + // + // Since the device doesn't support ALUA, it is automatically + // symmetric LU access. + // + group->Symmetric = TRUE; + + if (newGroup) { + + // + // This is the first in the group, so make it the active device. + // The actual active/passive devices will be set-up when + // LB policies are set by the user. + // + deviceInfo->PreviousState = deviceInfo->State; + deviceInfo->State = DSM_DEV_ACTIVE_OPTIMIZED; + + } else { + + // + // Already something active, this will be the fail-over device + // until the load-balance groups are set-up. + // + deviceInfo->PreviousState = deviceInfo->State; + deviceInfo->State = DSM_DEV_STANDBY; + } + + } else { + + if (DeviceIdList == NULL) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): No Device ID List.\n", + TargetDevice)); + + status = STATUS_NOT_SUPPORTED; + goto __Exit_DsmInquire; + } + + if (alua == DSM_DEVINFO_ALUA_IMPLICIT) { + + // + // Assume that the LU access is symmetric. When parsing the TPG + // info, if we find that not all TPGs are in the same LU access + // state, then we know that this the access is asymmetric. + // + group->Symmetric = TRUE; + } + + // + // Build TPG and TP info + // + status = DsmpParseTargetPortGroupsInformation(DsmContext, + group, + targetPortGroupsInfo, + targetPortGroupsInfoLength); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): Failed to build TPG information - status %x.\n", + TargetDevice, + status)); + + status = STATUS_NOT_SUPPORTED; + goto __Exit_DsmInquire; + } + + for (index = 0; index < DSM_MAX_PATHS; index++) { + + PDSM_TARGET_PORT_GROUP_ENTRY targetPortGroup; + + targetPortGroup = group->TargetPortGroupList[index]; + + if (targetPortGroup) { + + DsmpUpdateTargetPortGroupDevicesStates(targetPortGroup, targetPortGroup->AsymmetricAccessState); + } + } + + // + // Find the target port through which this devInfo was exposed. + // + relativeTargetPortId = (PULONG)DsmpParseDeviceID(DeviceIdList, + DSM_DEVID_RELATIVE_TARGET_PORT, + NULL, + NULL, + FALSE); + NT_ASSERT(relativeTargetPortId); + + if (!relativeTargetPortId) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): Couldn't retrieve relative TP id.\n", + TargetDevice)); + + status = STATUS_NOT_SUPPORTED; + goto __Exit_DsmInquire; + } + + // + // Find the target port group + // + targetPortGroupId = (PUSHORT)DsmpParseDeviceID(DeviceIdList, + DSM_DEVID_TARGET_PORT_GROUP, + NULL, + NULL, + FALSE); + NT_ASSERT(targetPortGroupId); + + if (targetPortGroupId) { + + // + // Find the target port group entry + // + targetPortGroupEntry = DsmpFindTargetPortGroup(DsmContext, + group, + targetPortGroupId); + + NT_ASSERT(targetPortGroupEntry); + + if (targetPortGroupEntry) { + + // + // Look through the target port group to find the target port + // + targetPortEntry = DsmpFindTargetPort(DsmContext, + targetPortGroupEntry, + relativeTargetPortId); + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): Couldn't find TPG Id %x's entry.\n", + TargetDevice, + *targetPortGroupId)); + + status = STATUS_NOT_SUPPORTED; + goto __Exit_DsmInquire; + } + + NT_ASSERT(targetPortEntry); + + if (!targetPortEntry) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): Couldn't find relative TP %x's entry.\n", + TargetDevice, + *relativeTargetPortId)); + + status = STATUS_NOT_SUPPORTED; + goto __Exit_DsmInquire; + } + + // + // Update the devInfo with the target port and target port group + // info + // + deviceInfo->TargetPortGroup = targetPortGroupEntry; + deviceInfo->TargetPort = targetPortEntry; + deviceInfo->PreviousState = deviceInfo->State; + deviceInfo->State = deviceInfo->ALUAState = deviceInfo->TargetPortGroup->AsymmetricAccessState; + + tp_device = DsmpAllocatePool(NonPagedPoolNx, + sizeof(DSM_TARGET_PORT_DEVICELIST_ENTRY), + DSM_TAG_TP_DEVICE_LIST_ENTRY); + + if (!tp_device) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): Insufficient resources allocating TP device list entry.\n", + TargetDevice)); + + status = STATUS_NOT_SUPPORTED; + goto __Exit_DsmInquire; + } + + // + // Add the device to the list of devices that are exposed via this target port. + // + tp_device->DeviceInfo = deviceInfo; + InterlockedIncrement((LONG volatile*)&targetPortEntry->Count); + InsertTailList(&targetPortEntry->TP_DeviceList, &tp_device->ListEntry); + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): Failed to retrieve TPG Id.\n", + TargetDevice)); + + status = STATUS_NOT_SUPPORTED; + goto __Exit_DsmInquire; + } + } + + if (NT_SUCCESS(status)) { + + // + // Add the deviceInfo to the list. DO NOT modify the status + // variable if this function returns SUCCESS. + // + status = DsmpAddDeviceEntry(DsmContext, + group, + deviceInfo); + if (NT_SUCCESS(status)) { + + *DsmIdentifier = deviceInfo; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): Added device %p to group %p.\n", + TargetDevice, + *DsmIdentifier, + group)); + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): Failed to add device %p to group %p - status %x.\n", + TargetDevice, + deviceInfo, + group, + status)); + + // + // We weren't able to add this deviceInfo to the list so we must + // remove its entry on the target port list before the deviceInfo + // is freed. + // + DsmpRemoveDeviceFromTargetPortList(deviceInfo); + + if (newGroup) { + + DsmpRemoveGroupEntry(DsmContext, group, FALSE); + + DsmpFreePool(group); + group = NULL; + } + + status = STATUS_NOT_SUPPORTED; + goto __Exit_DsmInquire; + } + } + } + + ExReleaseSpinLockExclusive(&(dsmContext->DsmContextLock), irql); + spinlockHeld = FALSE; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): Device %p added. State %d, Desired State %d\n", + TargetDevice, + deviceInfo, + deviceInfo->State, + deviceInfo->DesiredState)); + + // + // Update the global list of controller objects + // + controllerObjects = DsmGetAssociatedDevice(dsmContext->MPIOContext, + PortObject, + 0x0C); + if (controllerObjects) { + + // + // This loop needs its own status variable so that it does not + // inadvertently overwrite a STATUS_SUCCESS from the code above. + // + NTSTATUS matchStatus = STATUS_SUCCESS; + PSCSI_ADDRESS controllerScsiAddress = NULL; + + // + // Walk through the list and get VPD 0x83 data and associate the devInfo + // with the controller object. + // + for (index = 0; index < controllerObjects->Count; index++) { + + STORAGE_IDENTIFIER_CODE_SET codeSet = StorageIdCodeSetReserved; + + // + // Free the previously allocated SCSI address, if any. + // + if (controllerScsiAddress) { + DsmpFreePool(controllerScsiAddress); + controllerScsiAddress = NULL; + } + + controllerDeviceObject = (PDEVICE_OBJECT)controllerObjects->IdList[index]; + NT_ASSERT(controllerDeviceObject); + + if (!controllerDeviceObject) { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): Controller list %p's index %x is NULL.\n", + TargetDevice, + controllerObjects, + index)); + + continue; + } + + matchStatus = DsmpGetDeviceIdList(controllerDeviceObject, &controllerIdHeader); + NT_ASSERT(NT_SUCCESS(matchStatus)); + + if (!NT_SUCCESS(matchStatus)) { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): Failed to get DeviceId list for controller %p - status %x.\n", + TargetDevice, + controllerDeviceObject, + matchStatus)); + + continue; + } + + controllerSerialNumber = DsmpParseDeviceID((PSTORAGE_DEVICE_ID_DESCRIPTOR)controllerIdHeader, + DSM_DEVID_SERIAL_NUMBER, + NULL, + &codeSet, + FALSE); + NT_ASSERT(controllerSerialNumber); + DsmpFreePool(controllerIdHeader); + + if (!controllerSerialNumber) { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): Failed to parse serial number for controller %p.\n", + TargetDevice, + controllerDeviceObject)); + + continue; + } + + // + // Note that on success, DsmGetScsiAddress() will allocate memory + // which we are responsible for freeing. + // + matchStatus = DsmGetScsiAddress(controllerDeviceObject, &controllerScsiAddress); + NT_ASSERT(NT_SUCCESS(matchStatus)); + + if (!NT_SUCCESS(matchStatus)) { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): Failed to get controller %p's scsi address - status %x.\n", + TargetDevice, + controllerDeviceObject, + matchStatus)); + + continue; + } + + controllerEntry = DsmpFindControllerEntry(DsmContext, + PortObject, + controllerScsiAddress, + controllerSerialNumber, + strlen(controllerSerialNumber), + codeSet, + TRUE); + + if (!controllerEntry) { + + controllerEntry = DsmpBuildControllerEntry(DsmContext, + controllerDeviceObject, + PortObject, + controllerScsiAddress, + controllerSerialNumber, + codeSet, + TRUE); + + if (!controllerEntry) { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): Failed to build an entry for controller %p.\n", + TargetDevice, + controllerDeviceObject)); + + continue; + } + + InsertHeadList(&dsmContext->ControllerList, &controllerEntry->ListEntry); + InterlockedIncrement((LONG volatile*)&dsmContext->NumberControllers); + } + + controllerEntry->DeviceObject = controllerDeviceObject; + + // + // Parse the DeviceIdList for all the 0x5 type identifiers + // and for each, compare the target port groups and target ports to match + // the device to its controller. + // + if (!match) { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): Failed to match devInfo %p with controller %p's Ids.\n", + TargetDevice, + deviceInfo, + controllerDeviceObject)); + + match = DsmpIsDeviceBelongsToController(DsmContext, + deviceInfo, + controllerEntry); + } + + if (match && !doneUpdating) { + + InterlockedIncrement((LONG volatile*)&(controllerEntry->RefCount)); + deviceInfo->Controller = controllerEntry; + doneUpdating = TRUE; + } + } + + // + // Free the last SCSI address allocated in the loop, if any. + // + if (controllerScsiAddress) { + DsmpFreePool(controllerScsiAddress); + controllerScsiAddress = NULL; + } + } + + // + // If there was no controller to associate this device with, use a fake one. + // Note that we only really care about matching on the Port and Target + // portions of the SCSI address. + // + if (!deviceInfo->Controller) { + + for (entry = dsmContext->ControllerList.Flink; + entry != &dsmContext->ControllerList; + entry = entry->Flink) { + + controllerEntry = CONTAINING_RECORD(entry, DSM_CONTROLLER_LIST_ENTRY, ListEntry); + + if ((controllerEntry->IsFakeController) && + (controllerEntry->ScsiAddress->PortNumber == deviceInfo->ScsiAddress->PortNumber) && + (controllerEntry->ScsiAddress->TargetId == deviceInfo->ScsiAddress->TargetId)) { + + fakeControllerEntryExists = TRUE; + break; + } + } + + // + // If no fake one exists as yet for this port FDO, create one now. + // + if (!fakeControllerEntryExists) { + + CHAR fakeControllerSerialNumber[] = "FakeController"; + SCSI_ADDRESS fakeControllerScsiAddress = {0}; + fakeControllerScsiAddress.PortNumber = deviceInfo->ScsiAddress->PortNumber; + fakeControllerScsiAddress.TargetId = deviceInfo->ScsiAddress->TargetId; + + controllerEntry = DsmpBuildControllerEntry(DsmContext, + NULL, + PortObject, + &fakeControllerScsiAddress, + fakeControllerSerialNumber, + StorageIdCodeSetBinary, + TRUE); + + if (controllerEntry) { + + InsertHeadList(&dsmContext->ControllerList, &controllerEntry->ListEntry); + InterlockedIncrement((LONG volatile*)&dsmContext->NumberControllers); + controllerEntry->IsFakeController = TRUE; + } + } + + if (controllerEntry) { + InterlockedIncrement((LONG volatile*)&(controllerEntry->RefCount)); + } + + deviceInfo->Controller = controllerEntry; + } + +__Exit_DsmInquire: + + if (spinlockHeld) { + ExReleaseSpinLockExclusive(&(dsmContext->DsmContextLock), irql); + } + + if (NT_SUCCESS(status)) { + + NT_ASSERT(*DsmIdentifier); + + } else { + + // + // If there was any sort of ERROR, the deviceInfo will NOT be put on + // MSDSM's internal list that is accessible to other threads. Thus, + // we are safe to free the memory below and we do not require any + // synchronization mechanism to do so. + // + + // + // Check to see whether the serial number buffer was allocated, or just + // an offset into the Descriptor. + // + if (serialNumberAllocated) { + + // + // Need to free this before returning. + // + DsmpFreePool(serialNumber); + } + + if (deviceInfo) { + + if (deviceInfo->ScsiAddress) { + DsmpFreePool(deviceInfo->ScsiAddress); + } + + DsmpFreePool(deviceInfo); + } + } + + // + // If deviceName is not NULL then it hasn't been assigned to any GROUP. + // Free the allocated memory. + // + if (deviceName) { + DsmpFreePool(deviceName); + } + + // + // If hardwareId is not NULL then it hasn't been assigned to any GROUP. + // Free the allocated memory. + // + if (hardwareId) { + DsmpFreePool(hardwareId); + } + + if (targetPortGroupsInfo) { + DsmpFreePool(targetPortGroupsInfo); + } + + if (relativeTargetPortId) { + DsmpFreePool(relativeTargetPortId); + } + + if (targetPortGroupId) { + DsmpFreePool(targetPortGroupId); + } + + if (controllerObjects) { + DsmpFreePool(controllerObjects); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmInquire (DevObj %p): Exiting function with status %x.\n", + TargetDevice, + status)); + + return status; +} + + +BOOLEAN +DsmCompareDevices( + _In_ IN PVOID DsmContext, + _In_ IN PVOID DsmId1, + _In_ IN PVOID DsmId2 + ) +/*++ + +Routine Description: + + This routine is called to determine if the device ids represent + the same underlying physical device. + +Arguments: + + DsmContext - Context value given to the multipath driver during + registration. + DsmId1/2 - Identifers returned from DMS_INQUIRE_DRIVER. + +Return Value: + + TRUE if DsmIds correspond to the same underlying device. + +--*/ +{ + PDSM_DEVICE_INFO deviceInfo0 = DsmId1; + PDSM_DEVICE_INFO deviceInfo1 = DsmId2; + PSTR serialNumber0; + PSTR serialNumber1; + SIZE_T length; + BOOLEAN match = FALSE; + + UNREFERENCED_PARAMETER(DsmContext); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmCompareDevices (DevInfo %p): Entering function - comparing with %p.\n", + deviceInfo0, + deviceInfo1)); + + // + // Get the two serial numbers. They were either embedded in + // the STORAGE_DEVICE_DESCRIPTOR or built by directly issuing + // the VPD request. + // + serialNumber0 = deviceInfo0->SerialNumber; + serialNumber1 = deviceInfo1->SerialNumber; + + if (serialNumber0 && serialNumber1) { + + // + // Get the length of the base-device Serial Number. + // + length = strlen((const char*)serialNumber0); + + // + // If the lengths match, compare the contents. + // + if (length == strlen((const char*)serialNumber1)) { + + if (RtlEqualMemory(serialNumber0, serialNumber1, length)) { + match = TRUE; + } + } + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmCompareDevices (DevInfo %p): Serialnumber not assigned for %p and\\or %p.\n", + DsmId1, + deviceInfo0, + deviceInfo1)); + } + + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmCompareDevices (DevInfo %p): Exiting function with match = %!bool!.\n", + DsmId1, + match)); + + return match; +} + + +NTSTATUS +DsmGetControllerInfo( + _In_ IN PVOID DsmContext, + _In_ IN PVOID DsmId, + _In_ IN ULONG Flags, + _Inout_ IN OUT PCONTROLLER_INFO *ControllerInfo + ) +/*++ + +Routine Description: + + This routine is used to get information about the controller that + the device corresponding to DsmId in on. Currently this DSM controls + hardware that doesn't expose controllers directly. Therefore State + is always NO_CNTRL. This information is used mainly by whatever + WMI admin utilities want it. + +Arguments: + + DsmContext - Context value given to the multipath driver during + registration. + + DsmId - Value returned from DMSInquireDriver. + + Flags - Bitfield of modifiers. If ALLOCATE is not set, ControllerInfo + will have a valid buffer for the DSM to operate on. + + ControllerInfo - Pointer for the DSM to place the allocated controller + info pertaining to DsmId + +Return Value: + + STATUS_INSUFFICIENT_RESOURCES if memory allocation fails. + + STATUS_SUCCESS on success + +--*/ +{ + PDSM_DEVICE_INFO deviceInfo = DsmId; + PDSM_CONTROLLER_LIST_ENTRY controllerEntry = deviceInfo->Controller; + PCONTROLLER_INFO controllerInfo = NULL; + LARGE_INTEGER time; + ULONG controllerId = 0; + NTSTATUS status = STATUS_SUCCESS; + UNREFERENCED_PARAMETER(DsmContext); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmGetControllerInfo (DevInfo %p): Entering function.\n", + DsmId)); + + // + // Check to see whether a controller id has already been made-up. + // + if (!controllerEntry) { + + // + // Since this device is in an enclosure that doesn't have controllers, + // e.g. JBOD, make one up. + // + KeQuerySystemTime(&time); + + // + // Use only the lower 32-bits. + // + controllerId = time.LowPart; + } + + // + // Check the Flags + // + if (Flags & DSM_CNTRL_FLAGS_ALLOCATE) { + + // + // This is the first call. Need to allocate the controller structure. + // + controllerInfo = DsmpAllocatePool(NonPagedPoolNx, + sizeof(CONTROLLER_INFO), + DSM_TAG_CTRL_INFO); + if (!controllerInfo) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmGetControllerInfo (DevInfo %p): Failed to allocate memory for Controller Info\n", + DsmId)); + + status = STATUS_INSUFFICIENT_RESOURCES; + goto __Exit_DsmGetControllerInfo; + } + + if (!controllerEntry) { + + // + // Indicate that there are no specific controllers. + // + controllerInfo->State = DSM_CONTROLLER_NO_CNTRL; + + // + // Set the identifier to the value generated earlier. + // Indicate that it's Binary, not ASCII. + // + controllerInfo->Identifier.Type = StorageIdCodeSetBinary; + controllerInfo->Identifier.Length = 8; + + RtlCopyMemory(controllerInfo->Identifier.SerialNumber, + &controllerId, + sizeof(controllerId)); + + } else { + + // + // If either implicit or explicit ALUA state transition is supported, + // every controller is active. Else, if the devInfo's is in Active + // state, the controller is obviously in the active state. + // + if ((deviceInfo->ALUASupport != DSM_DEVINFO_ALUA_NOT_SUPPORTED) || + (DsmpIsDeviceStateActive(deviceInfo->State))) { + + controllerInfo->State = DSM_CONTROLLER_ACTIVE; + + } else { + + controllerInfo->State = DSM_CONTROLLER_STANDBY; + } + + controllerInfo->Identifier.Type = controllerEntry->IdCodeSet; + controllerInfo->Identifier.Length = controllerEntry->IdLength; + + if (controllerInfo->Identifier.Length > 32) { + + controllerInfo->Identifier.Length = 32; + } + + RtlCopyMemory(controllerInfo->Identifier.SerialNumber, + controllerEntry->Identifier, + controllerInfo->Identifier.Length); + + controllerInfo->DeviceObject = controllerEntry->DeviceObject; + } + + *ControllerInfo = controllerInfo; + + } else if (Flags & DSM_CNTRL_FLAGS_CHECK_STATE) { + + // + // Get the passed in struct. + // + controllerInfo = *ControllerInfo; + + // + // If the enclosures supported by this DSM actually had controllers, + // there would be a list of them and a search based on + // ControllerIdentifier would be made. + // + controllerEntry = deviceInfo->Controller; + + if (!controllerEntry) { + + controllerInfo->State = DSM_CONTROLLER_NO_CNTRL; + + } else { + + // + // If either implicit or explicit ALUA state transition is supported, + // every controller is active. Else, if the devInfo's is in Active + // state, the controller is obviously in the active state. + // + if ((deviceInfo->ALUASupport != DSM_DEVINFO_ALUA_NOT_SUPPORTED) || + (DsmpIsDeviceStateActive(deviceInfo->State))) { + + controllerInfo->State = DSM_CONTROLLER_ACTIVE; + + } else { + + controllerInfo->State = DSM_CONTROLLER_STANDBY; + } + } + } + +__Exit_DsmGetControllerInfo: + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmGetControllerInfo (DevInfo %p): Exiting function with status %x.\n", + DsmId, + status)); + + return status; +} + + +NTSTATUS +DsmSetDeviceInfo( + _In_ IN PVOID DsmContext, + _In_ IN PDEVICE_OBJECT TargetObject, + _In_ IN PVOID DsmId, + _Inout_ IN OUT PVOID *PathId + ) +/*++ + +Routine Description: + + This routine associates the DsmId to the controlling MPDisk PDO, + the targetObject for DSM-initiated requests, and to a Path + (given by PathId). + This routine will update the PathId in a way that better explains + the topology to MPIO. + Additionally, if we are in failover LB policy, failback if this + path is preferred path. + Also, if PR is being used, send registration down this path. + +Arguments: + + DsmContext - Context value given to the multipath driver during + registration. + TargetObject - The D.O. to which DSM-initiated requests should be sent. + DsmId - Value returned from DMSInquireDriver. + PathId - Id that represents the path. The value passed in may be used + as is, or the DSM optionally can update it if it requires + additional state info to be kept. + +Return Value: + + INSUFFICENT_RESOURCES for no-mem conditions. + STATUS_SUCCESS + +--*/ +{ + PDSM_DEVICE_INFO deviceInfo = DsmId; + PDSM_GROUP_ENTRY group = deviceInfo->Group; + PDSM_FAILOVER_GROUP failGroup; + PDSM_CONTEXT dsmContext; + PSCSI_ADDRESS scsiAddress; + ULONG primaryPath = 0; + ULONG optimizedPath = 0; + ULONG pathWeight = 0; + ULONG pathId; + NTSTATUS status = STATUS_SUCCESS; + WCHAR registryKeyName[256] = {0}; + BOOLEAN newFOGroup = FALSE; + BOOLEAN registryKeyExists = FALSE; + KIRQL irql; + PVOID tempPathId = *PathId; + DSM_LOAD_BALANCE_TYPE loadBalanceType; + ULONGLONG preferredPath = (ULONGLONG)((ULONG_PTR)MAXULONG); + UCHAR explicitlySet = FALSE; + BOOLEAN vidpidPolicySet = FALSE; + BOOLEAN overallPolicySet = FALSE; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmSetDeviceInfo (DevInfo %p): Entering function.\n", + DsmId)); + + // + // 1. Set default LB policy. + // 2. Query LB policy from registry and update if necessary. + // 3. Set default value for primaryPath and optimizedPath based on device's + // access state + // 4. Map deviceInfo to real LUN by saving off the target for I/O + // 5. Build pathId from SCSI address + // 6. Find FOG for device. If none found, build one. + // Add deviceInfo to FOG. + // 7. Query registry for pathWeight, primaryPath and optimizedPath + // Update deviceInfo with results of query. + // 8. Compare deviceInfo access state with persistent value (based on + // primaryPath and optimizedPath) and update its DesiredState. + // + + if (!TargetObject) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmSetDeviceInfo (DevInfo %p): No target object.\n", + deviceInfo)); + + // + // This deviceInfo will have no path or targetObject associated with it. + // Mark it in a failed state so it won't be used to handle any requests. + // + deviceInfo->PreviousState = deviceInfo->State; + deviceInfo->State = DSM_DEV_UNDETERMINED; + + goto __Exit_DsmSetDeviceInfo; + } + + // + // Default LB type is Round Robin. + // + loadBalanceType = DSM_LB_ROUND_ROBIN; + + // + // Override the default with whatever is the overall policy that needs to be + // applied for all LUNs controlled by MSDSM. + // + // Override that policy if one has been set for this device's VID/PID. + // + // Override that policy with whatever has been explicitly set for this particular + // device. + // + // In order to perform the above, first query the policy for this particular device. + // If it has not been explicity set, use MSDSM's overall policy or VID/PID policy. + // + status = DsmpQueryDeviceLBPolicyFromRegistry(deviceInfo, + group->RegistryKeyName, + &loadBalanceType, + &preferredPath, + &explicitlySet); + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmSetDeviceInfo (DevInfo %p): Failed to query LB policy from registry. Status %x.\n", + deviceInfo, + status)); + + NT_ASSERT(NT_SUCCESS(status)); + + // + // This deviceInfo will have no path or targetObject associated with it. + // Mark it in a failed state so it won't be used to handle any requests. + // + deviceInfo->PreviousState = deviceInfo->State; + deviceInfo->State = DSM_DEV_UNDETERMINED; + + goto __Exit_DsmSetDeviceInfo; + } + + // + // If this device's policy was not explicitly set, check to see if a policy + // was set for this device's VID/PID and use that. + // If VID/PID policy is not set, query the overall default policy + // that needs to be applied to all devices controlled by this DSM. + // If this setting hasn't been set, we'll fall back to using the default that was + // determined based on the storage's ALUA capabilities. + // + if (!explicitlySet) { + + status = DsmpQueryTargetLBPolicyFromRegistry(deviceInfo, + &loadBalanceType, + &preferredPath); + + if (NT_SUCCESS(status)) { + + group->LBPolicySelection = DSM_DEFAULT_LB_POLICY_VID_PID; + vidpidPolicySet = TRUE; + + } else if (status == STATUS_OBJECT_NAME_NOT_FOUND) { + + // + // Since the policy hasn't been set for this VID/PID, check if + // overall MSDSM-wide policy has been set. + // + status = DsmpQueryDsmLBPolicyFromRegistry(&loadBalanceType, + &preferredPath); + if (NT_SUCCESS(status)) { + + group->LBPolicySelection = DSM_DEFAULT_LB_POLICY_DSM_WIDE; + overallPolicySet = TRUE; + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmSetDeviceInfo (DevInfo %p): Failed to query Dsm overall LB policy from registry. Status %x.\n", + deviceInfo, + status)); + + NT_ASSERT(status == STATUS_OBJECT_NAME_NOT_FOUND); + status = STATUS_SUCCESS; + } + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmSetDeviceInfo (DevInfo %p): Failed to query VID/PID LB policy from registry. Status %x.\n", + deviceInfo, + status)); + + NT_ASSERT(status == STATUS_OBJECT_NAME_NOT_FOUND); + status = STATUS_SUCCESS; + } + } else { + + group->LBPolicySelection = DSM_DEFAULT_LB_POLICY_LUN_EXPLICIT; + } + + if (!explicitlySet && !vidpidPolicySet && !overallPolicySet) { + + group->LBPolicySelection = DSM_DEFAULT_LB_POLICY_ALUA_CAPABILITY; + + } + + // + // If ALUA is enabled and the load balance policy is set to Round Robin, + // we need to set it to Round Robin with Subset instead. + // + if (!DsmpIsSymmetricAccess(deviceInfo) && loadBalanceType == DSM_LB_ROUND_ROBIN) { + loadBalanceType = DSM_LB_ROUND_ROBIN_WITH_SUBSET; + } + + group->LoadBalanceType = loadBalanceType; + group->PreferredPath = preferredPath; + dsmContext = (PDSM_CONTEXT) DsmContext; + + irql = ExAcquireSpinLockExclusive(&(dsmContext->DsmContextLock)); + + // + // Save the registry key name under which Load balance policies + // are stored. This will be used to query the LB policy later. + // + if (group->RegistryKeyName) { + + registryKeyExists = TRUE; + + if (!NT_SUCCESS(RtlStringCchCopyNW(registryKeyName, + sizeof(registryKeyName) / sizeof(registryKeyName[0]), + group->RegistryKeyName, + ((sizeof(registryKeyName) / sizeof(registryKeyName[0])) - sizeof(WCHAR))))) { + + registryKeyName[(sizeof(registryKeyName) / sizeof(registryKeyName[0])) - 1] = L'\0'; + } + } + + // + // TargetObject is the destination for any requests created by this driver. + // Save this for future reference. + // + deviceInfo->TargetObject = TargetObject; + + // + // Set the PathId - All devices on the same PathId will + // failover together. Currently the pathId is constructed + // from Port Number, Bus Number, and Target Id of the device. + // + scsiAddress = deviceInfo->ScsiAddress; + NT_ASSERT(scsiAddress); + + pathId = 0x77; + pathId <<= 8; + pathId |= scsiAddress->PortNumber; + pathId <<= 8; + pathId |= scsiAddress->PathId; + pathId <<= 8; + pathId |= scsiAddress->TargetId; + + *PathId = ((PVOID)((ULONG_PTR)(pathId))); + + // + // PathId indicates the path on which this device resides. Meaning + // that when a Fail-Over occurs all device's on the same path fail + // together. Search for a matching F.O. Group + // + failGroup = DsmpFindFOGroup(DsmContext, *PathId); + + // + // If not found, create a new failover group + // + if (!failGroup) { + + failGroup = DsmpBuildFOGroup(DsmContext, deviceInfo, PathId); + + if (failGroup) { + + newFOGroup = TRUE; + failGroup->MPIOPath = tempPathId; + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmSetDeviceInfo (DevInfo %p): Failed to build FO Group.\n", + DsmId)); + + status = STATUS_INSUFFICIENT_RESOURCES; + } + } + + if (NT_SUCCESS(status)) { + + // + // If this path is in the midst of failover processing, mark it as "good" + // again. + // + failGroup->State = DSM_FG_NORMAL; + + // + // add this deviceInfo to the f.o. group. + // + status = DsmpUpdateFOGroup(DsmContext, failGroup, deviceInfo); + NT_ASSERT(NT_SUCCESS(status)); + } + + ExReleaseSpinLockExclusive(&(dsmContext->DsmContextLock), irql); + + if (NT_SUCCESS(status)) { + + if (registryKeyExists) { + + NTSTATUS queryStatus = STATUS_INVALID_PARAMETER; + ULONGLONG pathId64; + + // + // If the overall default policy or a target-level policy has been set and + // this device's policy has not been explicitly set, there's no use querying + // its individual path (desired) states. + // + if ((!overallPolicySet && !vidpidPolicySet) || (explicitlySet)) { + + // + // Created a new failover group. Query the LB policy + // for this device from registry. + // + pathId64 = (ULONGLONG)((ULONG_PTR)*PathId); + + queryStatus = DsmpQueryLBPolicyForDevice(registryKeyName, + pathId64, + loadBalanceType, + &primaryPath, + &optimizedPath, + &pathWeight); + } + + irql = ExAcquireSpinLockExclusive(&(dsmContext->DsmContextLock)); + + if (NT_SUCCESS(queryStatus)) { + + deviceInfo->PathWeight = pathWeight; + + // + // If device doesn't support ALUA, update the device state + // based on the primary path info in the registry. + // + if (DsmpIsSymmetricAccess(deviceInfo)) { + + if (primaryPath) { + + deviceInfo->DesiredState = DSM_DEV_ACTIVE_OPTIMIZED; + + } else { + + deviceInfo->DesiredState = DSM_DEV_STANDBY; + } + + } else { + + DSM_DEVICE_STATE devState; + + if (primaryPath) { + + devState = optimizedPath ? DSM_DEV_ACTIVE_OPTIMIZED : DSM_DEV_ACTIVE_UNOPTIMIZED; + + } else { + + devState = optimizedPath ? DSM_DEV_STANDBY : DSM_DEV_UNAVAILABLE; + } + + // + // For ALUA, desired state makes sense for FOO. + // For RRWS, we assume desired state was explicitly selected + // by Admin if the ALUA state is different from the path + // state. Only under such cases would the path state have + // been saved in registry. + // In all other policies, state must just match the TPG state. + // + if (group->LoadBalanceType == DSM_LB_FAILOVER || + group->LoadBalanceType == DSM_LB_ROUND_ROBIN_WITH_SUBSET) { + + deviceInfo->DesiredState = devState; + + } else { + + deviceInfo->DesiredState = DSM_DEV_UNDETERMINED; + } + } + } else if (queryStatus == STATUS_OBJECT_NAME_NOT_FOUND) { + + deviceInfo->PathWeight = pathWeight; + deviceInfo->DesiredState = DSM_DEV_UNDETERMINED; + + } else { + + deviceInfo->PathWeight = 0; + deviceInfo->DesiredState = DSM_DEV_UNDETERMINED; + } + + ExReleaseSpinLockExclusive(&(dsmContext->DsmContextLock), irql); + } + } + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmSetDeviceInfo (DevInfo %p): PathWeight %x, DesiredState %x, State %x, PrevState %x.\n", + deviceInfo, + deviceInfo->PathWeight, + deviceInfo->DesiredState, + deviceInfo->State, + deviceInfo->PreviousState)); + + if (NT_SUCCESS(status)) { + + deviceInfo->Initialized = TRUE; + + } else if (!NT_SUCCESS(status) && newFOGroup) { + + // + // This deviceInfo will have no path associated with it. + // Mark it in a failed state so it won't be used to handle any requests. + // + deviceInfo->PreviousState = deviceInfo->State; + deviceInfo->State = DSM_DEV_UNDETERMINED; + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmSetDeviceInfo (DevInfo %p): No path associated with instance. Changing state from %u to %u.\n", + deviceInfo, + deviceInfo->PreviousState, + deviceInfo->State)); + + DsmpRemoveDeviceFailGroup(DsmContext, failGroup, deviceInfo, TRUE); + + if (failGroup->Count == 0) { + + // + // Yank it from the list. + // + RemoveEntryList(&failGroup->ListEntry); + InterlockedDecrement((LONG volatile*)&dsmContext->NumberFOGroups); + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmSetDeviceInfo (DevInfo %p): Removing FOGroup %p with path %p. Count of FOGroups %d.\n", + DsmId, + failGroup, + failGroup->PathId, + dsmContext->NumberFOGroups)); + + // + // Free the zombie group list and then the failover group. + // + DsmpFreeZombieGroupList(failGroup); + DsmpFreePool(failGroup); + } + } + +__Exit_DsmSetDeviceInfo: + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmSetDeviceInfo (DevInfo %p): Exiting function with status %x.\n", + DsmId, + status)); + + return status; +} + + +BOOLEAN +DsmIsPathActive( + _In_ IN PVOID DsmContext, + _In_ IN PVOID PathId, + _In_ IN PVOID DsmId + ) +/*++ + +Routine Description: + + This routine is used to determine whether the path to DsmId is usable + (ie. able to handle requests without a failover). + + Also, after a failover, the path validity will be queried. + If the path error was transitory and the DSM feels that the path is good, + then this request will be re-issued to determine whether it is usable. + +Arguments: + + DsmContext - Context value given to the multipath driver during + registration. + PathId - Value set in SetPathId. + DsmId - DSM Id returned during DsmInquire. + +Return Value: + + TRUE if the path is active. FALSE otherwise. +--*/ +{ + PDSM_FAILOVER_GROUP foGroup; + PDSM_DEVICE_INFO deviceInfo = DsmId; + PDSM_GROUP_ENTRY group = deviceInfo->Group; + PDSM_CONTEXT dsmContext = (PDSM_CONTEXT) DsmContext; + KIRQL irql; + BOOLEAN retVal; + ULONG SpecialHandlingFlag = 0; + + // + // 1. If PR and reserved by this node, register the PR keys. + // 2. Find the FOG for the passed in PathId + // 3. Depending on the LB policy, set the appropriate devInfo states + // If FailOver, and DesiredState is AO, change the active + // devInfos to non-active state and make this one AO. + // If ALUA supported, send down SetTPG to make this change, + // else directly make the change. + // If RR/LWP/LQD, make this DevInfo ActiveOptimized. + // If RRS, and DesiredState is AO, change the active devInfos to + // their desired states and then make this one AO. + // If DesiredState is not AO, find a devInfo in AO state. If + // one is found, make this devInfo's state its desired state, + // else if one isn't found, make this one AO. + // 3. If this is preferredPath, and LB policy is failover-only, change the + // access state of deviceInfo to AO. + // If there is another devInfo currently in AO, change its state too. + // If ALUA supported, send down SetTPG to make these changes. + // 4. Get the appropriate AO DeviceInfo and mark the group's PTBU to its + // pathId. + // + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmIsPathActive (DevInfo %p): Entering function.\n", + DsmId)); + + // + // Initialize this instance to be usable so that during the possible processing + // of PR register, this device can be a candidate for certain kind of requests. + // + deviceInfo->Usable = TRUE; + + // + // New path arriving. If this Node owns the reservation register this path. + // + if (group->PRKeyValid) { + + NTSTATUS prRegStatus; + ULONG i; + PDSM_DEVICE_INFO devInfo; + ULONG ordinal; + + prRegStatus = DsmpRegisterPersistentReservationKeys(deviceInfo, TRUE); + + deviceInfo->RegisterServiced = TRUE; + + if (NT_SUCCESS(prRegStatus)) { + + deviceInfo->PRKeyRegistered = TRUE; + + } else { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_PNP, + "DsmIsPathActive (DevInfo %p): Failed (status %x) to register PR key\n", + deviceInfo, + prRegStatus)); + } + + for (i = 0; i < group->NumberDevices; i++) { + + devInfo = group->DeviceList[i]; + if (devInfo && devInfo == deviceInfo) { + + ordinal = (1 << i); + group->ReservationList |= ordinal; + break; + } + } + } + + + irql = ExAcquireSpinLockExclusive(&(dsmContext->DsmContextLock)); + + // + // Get the F.O. Group information. + // + foGroup = DsmpFindFOGroup(DsmContext, PathId); + + // + // If there are any devices on this path, and it's not in a failed state + // it's capable of handling requests. So it's active. + // + if ((foGroup) && + (foGroup->Count) && + (foGroup->State == DSM_FG_NORMAL)) { + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmIsPathActive (DevInfo %p): Path %p is usable.\n", + DsmId, + PathId)); + + retVal = TRUE; + + // + // Update the next path to be used for the group if it not set already. + // + deviceInfo = (PDSM_DEVICE_INFO)DsmId; + + group = deviceInfo->Group; + DSM_ASSERT(group != NULL); + DSM_ASSERT(group->GroupSig == DSM_GROUP_SIG); + + // + // If an invalidated path came back online before PnP removes came in, + // then MPIO's path recovery thread would have sent down a PathVerify + // just moments before by which we changed the state of the FOG to + // normal. Now it is time to change the deviceInfo's state to a "good" + // state. + // + if (deviceInfo->State >= DSM_DEV_FAILED) { + + DSM_ASSERT(deviceInfo->State == DSM_DEV_INVALIDATED); + + if (DsmpIsSymmetricAccess(deviceInfo)) { + + // + // Mark it as AO. The SetLBForPathArrival will update the state + // appropriately. + // + deviceInfo->State = DSM_DEV_ACTIVE_OPTIMIZED; + + } else { + + // + // Set it to the state that was reported during the last RTPG + // call that was made. + // + deviceInfo->State = deviceInfo->ALUAState; + } + } + + if (DsmpIsSymmetricAccess(deviceInfo)) { + + DsmpSetLBForPathArrival(DsmContext, deviceInfo, SpecialHandlingFlag); + + } else { + + ExReleaseSpinLockExclusive(&(dsmContext->DsmContextLock), irql); + DsmpSetLBForPathArrivalALUA(DsmContext, deviceInfo, SpecialHandlingFlag); + irql = ExAcquireSpinLockExclusive(&(dsmContext->DsmContextLock)); + } + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmIsPathActive (DevInfo %p): State set to %d\n", + deviceInfo, + deviceInfo->State)); + + if (group->PathToBeUsed == NULL) { + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmIsPathActive (DevInfo %p): Will set PathToBeUsed for %p\n", + deviceInfo, + group)); + + deviceInfo = DsmpGetActivePathToBeUsed(group, + DsmpIsSymmetricAccess(deviceInfo), + SpecialHandlingFlag); + if (deviceInfo != NULL) { + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmIsPathActive (DevInfo %p): FOG %p set for PathToBeUsed for %p\n", + deviceInfo, + deviceInfo->FailGroup, + group)); + + InterlockedExchangePointer(&(group->PathToBeUsed), deviceInfo->FailGroup); + + } else { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_PNP, + "DsmIsPathActive (DevInfo %p): No active/alternative path available for group %p\n", + DsmId, + group)); + + InterlockedExchangePointer(&(group->PathToBeUsed), NULL); + } + } + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmIsPathActive (DevInfo %p): Path %p is NOT usable.\n", + DsmId, + PathId)); + + retVal = FALSE; + } + + ((PDSM_DEVICE_INFO)DsmId)->Usable = retVal; + + ExReleaseSpinLockExclusive(&(dsmContext->DsmContextLock), irql); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmIsPathActive (DevInfo %p): Exiting function with retVal = %!bool!.\n", + DsmId, + retVal)); + + return retVal; +} + + +NTSTATUS +DsmPathVerify( + _In_ IN PVOID DsmContext, + _In_ IN PVOID DsmId, + _In_ IN PVOID PathId + ) +/*++ + +Routine Description: + + This routine ensures that the path to the device indicated by DsmId + is healthy. It's called periodically by the bus driver, and also + after a fail-over condition has been dealt with to ensure that + the path is able to handle requests. + +Arguments: + + DsmContext - Context value given to the multipath driver during + registration. + DsmId - Value returned from DMSInquire. + PathId - Value set in SetPathId. + +Return Value: + + NTSTATUS +--*/ + +{ + PDSM_CONTEXT dsmCtxt = (PDSM_CONTEXT) DsmContext; + PDSM_DEVICE_INFO deviceInfo = DsmId; + PDSM_FAILOVER_GROUP foGroup; + NTSTATUS status = STATUS_UNSUCCESSFUL; + BOOLEAN found = FALSE; + KIRQL irql; + PLIST_ENTRY entry; + PDSM_FOG_DEVICELIST_ENTRY fogDeviceListEntry = NULL; + PDSM_GROUP_ENTRY group = deviceInfo->Group; + ULONG SpecialHandlingFlag = 0; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmPathVerify (DevInfo %p): Entering function.\n", + DsmId)); + + if (DsmpIsDeviceInitialized(deviceInfo)) { + + irql = ExAcquireSpinLockExclusive(&(dsmCtxt->DsmContextLock)); + + // + // Get the failover group + // + foGroup = DsmpFindFOGroup(DsmContext, PathId); + + if (foGroup) { + + // + // Find the device. + // + for (entry = foGroup->FOG_DeviceList.Flink; + entry != &foGroup->FOG_DeviceList; + entry = entry->Flink) { + + fogDeviceListEntry = CONTAINING_RECORD(entry, DSM_FOG_DEVICELIST_ENTRY, ListEntry); + + if (fogDeviceListEntry && fogDeviceListEntry->DeviceInfo == deviceInfo) { + + status = STATUS_SUCCESS; + found = TRUE; + + break; + } + } + } else { + + // + // This is not a good thing. It indicates that either we + // returned a bogus path to the bus-driver on a fail-over, + // or that the path evaporated between polls and PnP hasn't + // torn stuff down. + // + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmPathVerify (DevInfo %p): Failed to find failover group for path %p.\n", + DsmId, + PathId)); + + status = STATUS_DEVICE_NOT_CONNECTED; + } + + ExReleaseSpinLockExclusive(&(dsmCtxt->DsmContextLock), irql); + + if (NT_SUCCESS(status)) { + + if (found) { + + // + // Send down TUR if ALUA is not supported. + // Else, send down ReportTargetPortGroups (sending TUR down non-A/O path will + // always result in a check condition). + // + if (deviceInfo->ALUASupport == DSM_DEVINFO_ALUA_NOT_SUPPORTED) { + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmPathVerify (DevInfo %p): Sending TUR using %p to verify path %p.\n", + DsmId, + deviceInfo, + deviceInfo->FailGroup->PathId)); + + status = DsmSendTUR(deviceInfo->TargetObject); + + } else { + + // + // Check for whether we should ignore sending down an RTPG: + // Flag set indicates that this PathVerify() is happening in response to device + // arrival and can be skipped since Inquire() has just already sent down an RTPG. + // All that needs to be done is to clear the flag so that subsequent PathVerify() + // sent in response to InitiateFO will send RTPG as a ping. + // This is an optimization with the idea of helping speed up boot time, which is + // is adversely impacted, especially if there are many LUNs, each with many paths. + // + if (deviceInfo->IgnorePathVerify) { + + deviceInfo->IgnorePathVerify = FALSE; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmPathVerify (DevInfo %p): Returning success immediately since RTPG was already just sent.\n", + DsmId)); + + status = STATUS_SUCCESS; + + } else { + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmPathVerify (DevInfo %p): Sending RTPG using %p to verify path %p.\n", + DsmId, + deviceInfo, + deviceInfo->FailGroup->PathId)); + + status = DsmpGetDeviceALUAState(dsmCtxt, deviceInfo, NULL); + + // + // Since this RTPG may have resulted in us losing a UA, adjust + // the states if needed. + // + if (NT_SUCCESS(status)) { + + DsmpAdjustDeviceStatesALUA(group, NULL, SpecialHandlingFlag); + } + } + } + } + + if (NT_SUCCESS(status)) { + + if (deviceInfo->State >= DSM_DEV_FAILED) { + + foGroup->State = DSM_FG_NORMAL; + deviceInfo->State = deviceInfo->LastKnownGoodState; + } + } + } + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmPathVerify (DevInfo %p): Exiting function with status %x.\n", + DsmId, + status)); + + return status; +} + + +NTSTATUS +DsmInvalidatePath( + _In_ IN PVOID DsmContext, + _In_ IN ULONG ErrorMask, + _In_ IN PVOID PathId, + _Inout_ IN OUT PVOID *NewPathId + ) +/*++ + +Routine Description: + + This routine will mark up devices as failed on PathId, and find + an appropriate path to return to MPIO. + +Arguments: + + DsmContext - Context value given to the multipath driver during + registration. + ErrorMask - Value returned from InterpretError. + PathId - The failing path. + NewPathId - Pointer to the new path. + +Return Value: + + NTSTATUS of the operation. + +--*/ +{ + PDSM_CONTEXT context = DsmContext; + PDSM_FAILOVER_GROUP failGroup; + PDSM_FAILOVER_GROUP newPath = NULL; + PDSM_FAILOVER_GROUP pathId; + PDSM_DEVICE_INFO deviceInfo; + LIST_ENTRY reservedDeviceList; + NTSTATUS status = STATUS_SUCCESS; + KIRQL irql; + PLIST_ENTRY entry; + PDSM_FOG_DEVICELIST_ENTRY fogDeviceListEntry = NULL; + BOOLEAN lockHeld = FALSE; + + UNREFERENCED_PARAMETER(ErrorMask); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmInvalidatePath (PathId %p): Entering function.\n", + PathId)); + + DSM_ASSERT(ErrorMask & DSM_FATAL_ERROR); + + *NewPathId = NULL; + + InitializeListHead(&reservedDeviceList); + + irql = ExAcquireSpinLockExclusive(&(context->DsmContextLock)); + lockHeld = TRUE; + + // + // Get the fail-over group corresponding to the PathId. + // + failGroup = DsmpFindFOGroup(DsmContext, PathId); + + if (!failGroup || failGroup->State == DSM_FG_FAILED) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmInvalidatePath (PathId %p): Failed to find FailOver group.\n", + PathId)); + + status = STATUS_NO_SUCH_DEVICE; + goto __Exit_DsmInvalidatePath; + } + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_RW, + "DsmInvalidatePath (PathId %p): Context %p, FOG %p failing.\n", + PathId, + DsmContext, + failGroup)); + + // + // Mark the path as failed. + // + failGroup->State = DSM_FG_FAILED; + + // + // Check to see whether the port driver and PnP removed the devices + // BEFORE the fail-over indication actually occurred. Work-around + // of several Fibre miniports. + // + if (failGroup->Count == 0) { + + // + // There are no longer any devices in this fail-over group, which means + // in order to get a back-pointer to the groups using this fail-over + // group, we need to go through the "zombie" group list. This should + // allow us to find a new path ID to return. + //Then go through failGroup->ZombieGroupList to do failover for each group. + // + PDSM_ZOMBIEGROUP_ENTRY group; + PDSM_GROUP_ENTRY groupEntry; + + // + // Initialize all the entries to indicate that they haven't been processed. + // + for (entry = failGroup->ZombieGroupList.Flink; entry != &(failGroup->ZombieGroupList); entry = entry->Flink) { + + group = CONTAINING_RECORD(entry, DSM_ZOMBIEGROUP_ENTRY, ListEntry); + group->Processed = FALSE; + } + + // + // Since we need to drop the spin lock while processing an entry, it is possible + // that a removal in parallel frees up this entry during that time, thus making it + // impossible for us to move to the next entry in the list. + // In order to safely access each of the entries, we mark an entry as being processed + // just before dropping the spinlock, and always start processing from the beginning + // of the list, skipping over the already processed ones. + // + entry = failGroup->ZombieGroupList.Flink; + + while (entry != &(failGroup->ZombieGroupList)) { + + group = CONTAINING_RECORD(entry, DSM_ZOMBIEGROUP_ENTRY, ListEntry); + entry = entry->Flink; + + if (!group || !group->Group || group->Processed) { + continue; + } + + group->Processed = TRUE; + groupEntry = group->Group; + + ExReleaseSpinLockExclusive(&context->DsmContextLock, irql); + lockHeld = FALSE; + + pathId = DsmpSetNewPathUsingGroup((PDSM_CONTEXT)DsmContext, groupEntry); + + if (!newPath) { + newPath = pathId; // Save off first good alternative path that we find + } + + if (!lockHeld) { + irql = ExAcquireSpinLockExclusive(&(context->DsmContextLock)); + lockHeld = TRUE; + entry = failGroup->ZombieGroupList.Flink; + } + } + + if (!newPath) { + // + // This indicates that all of the devices have already been removed. + // If there were reservations outstanding, the RemoveDevice code + // should have updated them. + // + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmInvalidatePath (PathId %p): Failed to find new path using zombie group list.\n", + PathId)); + } + + } else { + + + // + // Process each device in the fail-over group + // + for (entry = failGroup->FOG_DeviceList.Flink; + entry != &failGroup->FOG_DeviceList; + entry = entry->Flink) { + + fogDeviceListEntry = CONTAINING_RECORD(entry, DSM_FOG_DEVICELIST_ENTRY, ListEntry); + + if (!fogDeviceListEntry) { + continue; + } + + // + // Get the deviceInfo. + // + deviceInfo = fogDeviceListEntry->DeviceInfo; + + if (!(DsmpIsDeviceFailedState(deviceInfo->State))) { + + deviceInfo->LastKnownGoodState = deviceInfo->State; + } + + // + // Set the state of the Failing Device + // + deviceInfo->PreviousState = deviceInfo->State; + deviceInfo->State = DSM_DEV_INVALIDATED; + + InterlockedIncrement(&deviceInfo->BlockRemove); + + ExReleaseSpinLockExclusive(&(context->DsmContextLock), irql); + lockHeld = FALSE; + + pathId = DsmpSetNewPath(DsmContext, deviceInfo); + + if (!newPath) { + newPath = pathId; // Save off first good alternative path that we find + } + + if (!lockHeld) { + irql = ExAcquireSpinLockExclusive(&(context->DsmContextLock)); + lockHeld = TRUE; + } + + InterlockedDecrement(&deviceInfo->BlockRemove); + } + } + + if (!newPath) { + + // + // This indicates that no acceptable paths + // were found. Return the error to mpctl. + // + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmInvalidatePath (PathId %p): No valid path found.\n", + PathId)); + + status = STATUS_NO_SUCH_DEVICE; + + } else { + + // + // return the new path. + // + *NewPathId = newPath->PathId; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_RW, + "DsmInvalidatePath (PathId %p): Returning %p as newPath.\n", + PathId, + newPath->PathId)); + } + +__Exit_DsmInvalidatePath: + + if (lockHeld) { + ExReleaseSpinLockExclusive(&(context->DsmContextLock), irql); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmInvalidatePath (PathId %p): Exiting function with status %x.\n", + PathId, + status)); + + return status; +} + + +NTSTATUS +DsmMoveDevice( + _In_ IN PVOID DsmContext, + _In_ IN PDSM_IDS DsmIds, + _In_ IN PVOID MPIOPath, + _In_ IN PVOID SuggestedPath, + _In_ IN ULONG Flags + ) +/*++ + +Routine Description: + + This routine is invoked in response to an administrative request. + The device that's associated with SuggestedPath will be made active, and the + current active device, moved to stand-by. + +Arguments: + + DsmContext - Context value given to the multipath driver during registration. + DsmIds - The collection of DSM IDs that pertain to the MPDisk. + MPIOPath - The original path value passed to SetDeviceInfo. + SuggestedPath - The path which should become the active path. + Flags - Bitmask indicating the intent of the move. + +Return Value: + + NTSTATUS - STATUS_SUCCESS, unless SuggestedPath is somehow invalid. + STATUS_INVALID_PARAMETER is ADMIN is set and the path is invalid. + +--*/ +{ + PDSM_CONTEXT context = DsmContext; + PDSM_DEVICE_INFO deviceInfo; + PDSM_FAILOVER_GROUP failGroup; + ULONG i; + NTSTATUS status; + KIRQL irql; + BOOLEAN adminRequest = FALSE; + PDSM_GROUP_ENTRY group = NULL; + ULONG SpecialHandlingFlag = 0; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmMoveDevice (DsmIds %p): Entering function - DsmContext %p MPIOPath (%p) SuggestedPath %p.\n", + DsmIds, + DsmContext, + MPIOPath, + SuggestedPath)); + + // + // Capture the value of the ADMIN flag bit. + // Currently, permanent assignment of the device to "preferred path" isn't supported. + // This driver doesn't care about the pending remove flag (currently). + // + adminRequest = (BOOLEAN)(Flags & DSM_MOVE_ADMIN_REQUEST); + + irql = ExAcquireSpinLockExclusive(&(context->DsmContextLock)); + + group = ((PDSM_DEVICE_INFO)(DsmIds->IdList[0]))->Group; + + // + // Find the first active device. + // + deviceInfo = DsmpGetActivePathToBeUsed(group, + DsmpIsSymmetricAccess((PDSM_DEVICE_INFO)DsmIds->IdList[0]), + SpecialHandlingFlag); + + if (!deviceInfo) { + + // + // Didn't find an active device. Should LOG. + // Use the first one to piggy-back the request. + // + deviceInfo = DsmIds->IdList[0]; + } + + // + // Get the fail-over group associated with the Path. + // + failGroup = DsmpFindFOGroup(DsmContext, + SuggestedPath); + + if (!failGroup) { + + // + // The caller has made a terrible mistake. + // If it's an ADMIN request, blow it off. + // + if (adminRequest) { + status = STATUS_INVALID_PARAMETER; + } else { + + // + // Try to set another path. + // + // Note that failGroup will be NULL going into + // SetNewPath. This is OK. + // + status = STATUS_SUCCESS; + } + } else { + status = STATUS_SUCCESS; + } + + if (status == STATUS_SUCCESS) { + + // + // Set the new path, using SuggestedPath. + // + InterlockedIncrement(&deviceInfo->BlockRemove); + ExReleaseSpinLockExclusive(&context->DsmContextLock, irql); + failGroup = DsmpSetNewPath(context, + deviceInfo); + irql = ExAcquireSpinLockExclusive(&(context->DsmContextLock)); + InterlockedDecrement(&deviceInfo->BlockRemove); + + // + // If we were able to make the suggested path active, that should be used. + // + for (i = 0, status = STATUS_UNSUCCESSFUL; i < DsmIds->Count && !NT_SUCCESS(status); i++) { + + deviceInfo = DsmIds->IdList[i]; + + if (deviceInfo->FailGroup == failGroup) { + + if (deviceInfo->State == DSM_DEV_ACTIVE_OPTIMIZED) { + + InterlockedExchangePointer(&(group->PathToBeUsed), (PVOID)failGroup); + status = STATUS_SUCCESS; + } + } + } + } + + ExReleaseSpinLockExclusive(&(context->DsmContextLock), irql); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmMoveDevice (DsmIds %p): Exiting function with status %x.\n", + DsmIds, + status)); + + return status; +} + + +NTSTATUS +DsmRemovePending( + _In_ IN PVOID DsmContext, + _In_ IN PVOID DsmId + ) +/*++ + +Routine Description: + + This routine indicates that the device represented by DsmId will be + removed, so the deviceInfo is marked up to indicate the pending removal, + so that it won't be used. + +Arguments: + + DsmContext - Context value given to the multipath driver + during registration. + DsmId - Value referring to the failed device. + +Return Value: + + STATUS_SUCCESS + +--*/ + +{ + PDSM_CONTEXT dsmContext = DsmContext; + PDSM_DEVICE_INFO deviceInfo = DsmId; + KIRQL irql; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmRemovePending (DevInfo %p): Entering function.\n", + DsmId)); + + // + // DsmpSetNewPath then finds the next available device. This is basically a + // fail-over for just this device. + // + InterlockedIncrement(&deviceInfo->BlockRemove); + DsmpSetNewPath(DsmContext, deviceInfo); + irql = ExAcquireSpinLockExclusive(&(dsmContext->DsmContextLock)); + InterlockedDecrement(&deviceInfo->BlockRemove); + + if (!(DsmpIsDeviceFailedState(deviceInfo->State))) { + + deviceInfo->LastKnownGoodState = deviceInfo->State; + } + + // + // Mark the device as being unavailable since remove will be sent shortly. + // + deviceInfo->PreviousState = deviceInfo->State; + deviceInfo->State = DSM_DEV_REMOVE_PENDING; + + ExReleaseSpinLockExclusive(&(dsmContext->DsmContextLock), irql); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmRemovePending (DevInfo %p): Exiting function.\n", + DsmId)); + + return STATUS_SUCCESS; +} + +NTSTATUS +DsmRemoveDevice( + _In_ IN PVOID DsmContext, + _In_ IN PVOID DsmId, + _In_ IN PVOID PathId + ) +/*++ + +Routine Description: + + The device is gone and the port pdo has been removed. This routine will + update the internal structures and free any allocations. + +Arguments: + + DsmContext - Context value given to the multipath driver during + registration. + DsmId - Value referring to the failed device. + PathId - The path on which the Device lives. + +Return Value: + + STATUS_SUCCESS + +--*/ + +{ + PDSM_CONTEXT dsmContext = DsmContext; + PDSM_DEVICE_INFO deviceInfo = DsmId; + KIRQL irql; + PDSM_FAILOVER_GROUP failGroup = deviceInfo->FailGroup; + PDSM_GROUP_ENTRY group = deviceInfo->Group; + LONG block; + + UNREFERENCED_PARAMETER(PathId); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmRemoveDevice (DevInfo %p): Entering function.\n", + DsmId)); + + do { + + irql = ExAcquireSpinLockExclusive(&(dsmContext->DsmContextLock)); + block = deviceInfo->BlockRemove; + NT_ASSERT(block >= 0); + + if (block) { + + ExReleaseSpinLockExclusive(&(dsmContext->DsmContextLock), irql); + KeStallExecutionProcessor(10000); + } + + } while (block); + + if (!(DsmpIsDeviceFailedState(deviceInfo->State))) { + + deviceInfo->LastKnownGoodState = deviceInfo->State; + } + + deviceInfo->PreviousState = deviceInfo->State; + deviceInfo->State = DSM_DEV_REMOVED; + + // + // Decrement the reference count for this device's controller entry and + // delete the entry if its reference count is now zero. + // + if (deviceInfo->Controller) { + + if (InterlockedDecrement((LONG volatile*)&(deviceInfo->Controller->RefCount)) == 0) { + + RemoveEntryList(&(deviceInfo->Controller->ListEntry)); + DsmpFreeControllerEntry(dsmContext, deviceInfo->Controller); + deviceInfo->Controller = NULL; + InterlockedDecrement((LONG volatile*)&(dsmContext->NumberControllers)); + } + } + + // + // Ensure that the device has been fully initialized before trying to + // remove it from the FOG. If SetDeviceInfo has yet to be invoked, there + // will yet to be an association set. + // + if (failGroup) { + + // + // Remove its entry from the Fail-Over Group. + // + DsmpRemoveDeviceFailGroup(DsmContext, failGroup, deviceInfo, FALSE); + } + + ExReleaseSpinLockExclusive(&(dsmContext->DsmContextLock), irql); + + // + // Remove it from it's multi-path group. This has the side-effect + // of cleaning up the Group if the number of devices goes to zero. + // + DsmpRemoveDeviceEntry(DsmContext, group, deviceInfo); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmRemoveDevice (DevInfo %p): Exiting function.\n", + DsmId)); + + return STATUS_SUCCESS; +} + + +NTSTATUS +DsmRemovePath( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PVOID PathId + ) +/*++ + +Routine Description: + + This routine indicates that the path is no longer valid, and that it should + be removed. Internal counts will be updated and any allocations associated + with this path freed. + +Arguments: + + DsmContext - Context value given to the multipath driver during registration. + PathId - The path to remove. + +Return Value: + + NTSTATUS of the operation. + +--*/ + +{ + PDSM_FAILOVER_GROUP failGroup; + KIRQL irql; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmRemovePath (PathId %p): Entering function.\n", + PathId)); + + irql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); + + failGroup = DsmpFindFOGroup(DsmContext, PathId); + + if (failGroup) { + + // + // The claim is that a path won't be removed, until all + // the devices on it are. + // + if (failGroup->Count == 0) { + + // + // Yank it from the list. + // + RemoveEntryList(&failGroup->ListEntry); + InterlockedDecrement((LONG volatile*)&DsmContext->NumberFOGroups); + + // + // Move this over to the stale FOG list if there are inflight requests. + // Otherwise free the allocation. + // + if (InterlockedCompareExchange(&failGroup->NumberOfRequestsInFlight, 0, 0) > 0) { + + failGroup->State = DSM_FG_PENDING_REMOVE; + InsertTailList(&DsmContext->StaleFailGroupList, &failGroup->ListEntry); + InterlockedIncrement((LONG volatile*)&DsmContext->NumberStaleFOGroups); + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmRemovePath (PathId %p): Outstanding requests %d. Moving FOGroup %p with path %p to stale path list.\n", + PathId, + failGroup->NumberOfRequestsInFlight, + failGroup, + failGroup->PathId)); + } else { + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmRemovePath (PathId %p): Removing FOGroup %p with path %p. Count of FOGroups %d.\n", + PathId, + failGroup, + failGroup->PathId, + DsmContext->NumberFOGroups)); + + // + // Free the zombie group list and then the failover group. + // + DsmpFreeZombieGroupList(failGroup); + DsmpFreePool(failGroup); + } + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmRemovePath (PathId %p): Count %d. Not removing FOGroup %p.\n", + PathId, + failGroup->Count, + failGroup)); + + // + // Should never be here. + // + NT_ASSERT(failGroup->Count == 0); + } + } else { + + // + // It's already been removed. + // + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmRemovePath (PathId %p): Did not find the FO group.\n", + PathId)); + + NT_ASSERT(failGroup); + } + + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmRemovePath (PathId %p): Exiting function.\n", + PathId)); + + return STATUS_SUCCESS; +} + + +PVOID +DsmLBGetPath( + _In_ IN PVOID DsmContext, + _In_ IN PSCSI_REQUEST_BLOCK Srb, + _In_ IN PDSM_IDS DsmList, + _In_ IN PVOID CurrentPath, + _Out_ OUT NTSTATUS *Status + ) +/*++ + +Routine Description: + + This routine is used by mpio to handle load-balancing. + +Arguments: + + DsmContext - Context value given to the multipath driver during + registration. + Srb - The current read/write Srb. + DsmList - List of our DSM IDs. + CurrentPath - The last path that was returned for this multi-path group. + Status - Storage to place NTSTATUS of the call. + +Return Value: + + The path ID to which the request should be sent. + +--*/ + +{ + PDSM_CONTEXT dsmContext = DsmContext; + PDSM_DEVICE_INFO deviceInfo; + PDSM_GROUP_ENTRY group; + PDSM_FAILOVER_GROUP failGroup = NULL; + PVOID newPath = NULL; + PDSM_FAILOVER_GROUP oldFailGroup = NULL; + PDSM_FAIL_PATH_PROCESSING_LIST_ENTRY failPathDevInfoEntry = NULL; + PCDB cdb = NULL; + UCHAR opCode = 0xFF; + BOOLEAN lockInExclusiveMode = FALSE; + ULONG SpecialHandlingFlag = 0; + + + if (Srb) { + cdb = SrbGetCdb(Srb); + if (cdb) { + opCode = cdb->AsByte[0]; + + } + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmLBGetPath (DsmIds %p): Entering function.\n", + DsmList)); + + // + // Up-front checking to minimally validate the list of + // DsmId's being passed in. + // + NT_ASSERT(DsmList->Count && DsmList->IdList[0]); + if (!(DsmList->Count && DsmList->IdList[0])) { + + *Status = STATUS_NO_SUCH_DEVICE; + goto __Exit_DsmLBGetPath; + } + + deviceInfo = DsmList->IdList[0]; + group = deviceInfo->Group; + + + failGroup = DsmpGetPath(dsmContext, DsmList, Srb, SpecialHandlingFlag); + + // + // If there wasn't a single active/optimized path found, check to see if + // there is an STPG in progress that may be making a path A/O. + // + if (!failGroup) { + + // + // Take the last path used. + // + oldFailGroup = DsmpFindFOGroup(dsmContext, CurrentPath); + + // + // Find the devInfo corresponding to this path. + // + deviceInfo = DsmpFindDevInfoFromGroupAndFOGroup(dsmContext, + group, + oldFailGroup); + + if (deviceInfo) { + + // + // Check if there is an alternate devInfo to be used temporarily + // for this deviceInfo + // + failPathDevInfoEntry = DsmpFindFailPathDevInfoEntry(dsmContext, + group, + deviceInfo); + + if (failPathDevInfoEntry) { + + // + // Use the alternate devInfo for now temporarily while the STPG + // that was previously sent (asynchronously) works on making the + // appropriate path active/optimized. + // + failGroup = (failPathDevInfoEntry->TempDeviceInfo)->FailGroup; + } + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_RW, + "DsmLBGetPath (DsmIds %p): Couldn't find FOG but FO in progress, so returning devInfo %p (FOG %p path %p).\n", + DsmList, + deviceInfo, + deviceInfo->FailGroup, + deviceInfo->FailGroup->PathId)); + } else { + + // + // Check if there is an RTPG in progress, if yes, return some path + // for the IO to be sent down. + // + if (InterlockedCompareExchange((LONG volatile*)&group->InFlightRTPG, 0, 0)) { + + BOOLEAN sendTPG = FALSE; + deviceInfo = DsmpFindStandbyPathToActivateALUA(group, &sendTPG, SpecialHandlingFlag); + + if (deviceInfo) { + + failGroup = deviceInfo->FailGroup; + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_RW, + "DsmLBGetPath (DsmIds %p): Couldn't find FOG but RTPG inflight, so returning devInfo %p (FOG %p path %p).\n", + DsmList, + deviceInfo, + deviceInfo->FailGroup, + deviceInfo->FailGroup->PathId)); + } else { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_RW, + "DsmLBGetPath (DsmIds %p): Couldn't find FOG but RTPG inflight, even then couldn't find alternative devInfo.\n", + DsmList)); + } + } + } + } + + if (failGroup) { + + newPath = failGroup->PathId; + *Status = STATUS_SUCCESS; + + // + // If this is a retried request, our SetCompletion would have been bypassed, + // and our completion routine won't yet get called, so update the old and + // the new paths' stats. + // + if (Srb && DsmIsReadWrite(opCode)) { + + PDSM_FAILOVER_GROUP oldPath; + PIRP irp = (PIRP)SrbGetOriginalRequest(Srb); + PIO_STACK_LOCATION irpStack; + + // + // This indicates that the request is being retried. So we need to: + // 1. Update old path's and new path's request count + // 2. If the old path was supposed to be removed, check if there are + // no more requests are outstanding, and if yes, remove the path + // + + irpStack = IoGetCurrentIrpStackLocation(irp); + oldPath = irpStack->Parameters.Others.Argument3; + + if (oldPath) { + + NT_ASSERT(oldPath->FailOverSig == DSM_FOG_SIG); + + if (DsmpDecrementCounters(oldPath, Srb)) { + + // + // If there are no requests on a path that is supposed to be removed, + // remove it now. + // + if (oldPath->State == DSM_FG_PENDING_REMOVE) { + KIRQL irql; + + NT_ASSERT(oldPath->Count == 0); + + // + // We need to acquire the DsmContextLock in Exclusive mode since + // we are removing a path from the Failover Group list. + // + irql = ExAcquireSpinLockExclusive(&(dsmContext->DsmContextLock)); + lockInExclusiveMode = TRUE; + + RemoveEntryList(&oldPath->ListEntry); + InterlockedDecrement((LONG volatile*)&dsmContext->NumberStaleFOGroups); + + ExReleaseSpinLockExclusive(&(dsmContext->DsmContextLock), irql); + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmLBGetPath (DsmIds %p): Removing FOGroup %p with path %p.\n", + DsmList, + oldPath, + oldPath->PathId)); + + DsmpFreePool(oldPath); + } + } + + irpStack->Parameters.Others.Argument3 = failGroup; + + DsmpIncrementCounters(failGroup, Srb); + } + } + + } else { + + *Status = STATUS_NO_SUCH_DEVICE; + + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmLBGetPath (DsmIds %p): Failed to get FO group in LBGetPath.\n", + DsmList)); + + + } + +__Exit_DsmLBGetPath: + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmLBGetPath (DsmIds %p): Exiting function returning path %p for request %p.\n", + DsmList, + newPath, + Srb)); + + return newPath; +} + +_Success_(return == DSM_PATH_SET) +ULONG +DsmCategorizeRequest( + _In_ IN PVOID DsmContext, + _In_ IN PDSM_IDS DsmIds, + _In_ IN PIRP Irp, + _In_ IN PSCSI_REQUEST_BLOCK Srb, + _In_ IN PVOID CurrentPath, + _Outptr_result_maybenull_ OUT PVOID *PathId, + _Out_ OUT NTSTATUS *Status + ) +/*++ + +Routine Description: + + This routine is called when a request is received other than a read/write. + It will determine the best path to which the request is to be sent. + + In order to support clusters, reserve and release need to be handled + via SrbControl. + +Arguments: + + DsmContext - Context value given to the multipath driver during + registration. + DsmIds - List of our DSM IDs. + Irp - The Irp containing Srb. + Srb - The current non-read/write Srb. + CurrentPath - The last path that was returned for this multi-path group. + PathId - Placeholder for the PathID + Status - Storage to place NTSTATUS of the call. + +Return Value: + + DSM_PATH_SET - Indicates PathID is valid. + DSM_ERROR - Couldn't get a path. + +--*/ +{ + ULONG dsmStatus; + NTSTATUS status = STATUS_UNSUCCESSFUL; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_IOCTL, + "DsmCategorizeRequest (DsmIds %p): Entering function.\n", + DsmIds)); + + // + // Determine whether this is a special-case request - PR or QOS. + // + if (DsmpReservationCommand(Irp, Srb)) { + + dsmStatus = DSM_WILL_HANDLE; + goto __Exit_DsmCategorizeRequest; + } + + + // + // If this is a mpio pass through or a mpio pass through direct request, + // pick the path that corresponds to the pathId specified. + // + if (DsmpMpioPassThroughPathCommand(Irp)) { + + *PathId = DsmpGetPathIdFromPassThroughPath(DsmContext, + DsmIds, + Irp, + &status); + } else { + + // + // For requests other than reservation-handling and pass through, punt + // it back to the bus-driver. Need to get a path for the request first, + // so call the Load-Balance function. + // + *PathId = DsmLBGetPath(DsmContext, + Srb, + DsmIds, + CurrentPath, + &status); + } + + if (NT_SUCCESS(status)) { + + if (!*PathId) { + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_IOCTL, + "DsmCategorizeRequest (DsmIds %p): DSM_PATH_SET didn't return a path.\n", + DsmIds)); + } + + // + // Indicate that the path is updated, and mpctl should handle the request. + // + dsmStatus = DSM_PATH_SET; + + } else { + + // + // Indicate the error back to mpctl. + // + dsmStatus = DSM_ERROR; + + // + // Mark-up the Srb to show that a failure has occurred. + // This value is really only for this DSM to know what to do + // in the InterpretError routine - Fatal Error. + // It could be something more meaningful. + // + if (Srb) { + Srb->SrbStatus = SRB_STATUS_NO_DEVICE; + } + + *PathId = NULL; + } + + // + // Pass back status info to mpctl. + // + *Status = status; + +__Exit_DsmCategorizeRequest: + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_IOCTL, + "DsmCategorizeRequest (DsmIds %p): Exiting function with categorization %x.\n", + DsmIds, + dsmStatus)); + + return dsmStatus; +} + + +NTSTATUS +DsmBroadcastRequest( + _In_ IN PVOID DsmContext, + _In_ IN PDSM_IDS DsmIds, + _In_ IN PIRP Irp, + _In_ IN PSCSI_REQUEST_BLOCK Srb, + _In_ IN PKEVENT Event + ) +/*++ + +Routine Description: + + This routine is called when the DSM has indicated that Srb should be + sent to the device down all paths. The DSM will update IoStatus + information and status, but not complete the request. + + Currently MSDSM doesn't have a need for this. + +Arguments: + + DsmIds - The collection of DSM IDs that pertain to the MPDisk. + Irp - Irp containing SRB. + Srb - Scsi request block + Event - DSM sets this once all sub-requests have completed and + the original request's IoStatus has been setup. + +Return Value: + + NTSTATUS of the operation. + +--*/ +{ + NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST; + + UNREFERENCED_PARAMETER(DsmContext); + UNREFERENCED_PARAMETER(Srb); + UNREFERENCED_PARAMETER(Irp); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_IOCTL, + "DsmBroadcastRequest (DsmIds %p): Entering function.\n", + DsmIds)); + + // + // Currently nothing is handled via Broadcast. Just set the event to + // free up the request handling in the bus-driver. + // + NT_ASSERT(NT_SUCCESS(status)); + KeSetEvent(Event, 0, FALSE); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_IOCTL, + "DsmBroadcastReqeust (DsmIds %p): Exiting function with status %x.\n", + DsmIds, + status)); + + return status; +} + + +NTSTATUS +DsmSrbDeviceControl( + _In_ IN PVOID DsmContext, + _In_ IN PDSM_IDS DsmIds, + _In_ IN PIRP Irp, + _In_ IN PSCSI_REQUEST_BLOCK Srb, + _In_ IN PKEVENT Event + ) +/*++ + +Routine Description: + + This routine is called when the DSM has indicated that it wants to handle + it internally (via returning DSM_WILL_HANDLE in CategorizeRequest). + + It should set IoStatus (Status and Information) and the Event, but not + complete the request. + +Arguments: + + DsmContext - The DSM's context + DsmIds - The collection of DSM IDs that pertain to the MPDISK. + Irp - Irp containing SRB. + Srb - Scsi request block + Event - Event to be set when the DSM is finished if DsmHandled is TRUE + +Return Value: + + NTSTATUS of the request. + +--*/ +{ + PDSM_CONTEXT dsmContext = DsmContext; + PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); + NTSTATUS status; + UCHAR opCode = 0; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_IOCTL, + "DsmSrbDeviceControl (DsmIds %p): Entering function.\n", + DsmIds)); + + if (!DsmIds || !DsmIds->Count) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_IOCTL, + "DsmSrbDeviceControl (DsmIds %p): No DsmIds passed in.\n", + DsmIds)); + + status = STATUS_NO_SUCH_DEVICE; + goto __Exit_DsmSrbDeviceControl; + } + + if (irpStack->MajorFunction == IRP_MJ_SCSI) { + + // + // Determine the operation. + // + PCDB cdb = SrbGetCdb(Srb); + if (cdb) { + opCode = cdb->AsByte[0]; + } + + if (opCode == SCSIOP_PERSISTENT_RESERVE_OUT) { + + status = DsmpPersistentReserveOut(dsmContext, + DsmIds, + Irp, + Srb, + Event); + + } else if (opCode == SCSIOP_PERSISTENT_RESERVE_IN) { + + status = DsmpPersistentReserveIn(dsmContext, + DsmIds, + Irp, + Srb, + Event); + + } else { + + // + // Should never be here. + // + DSM_ASSERT(FALSE); + status = STATUS_INVALID_DEVICE_REQUEST; + } + } else { + // + // Should never be here. + // + DSM_ASSERT(irpStack->MajorFunction == IRP_MJ_SCSI); + status = STATUS_INVALID_DEVICE_REQUEST; + } + +__Exit_DsmSrbDeviceControl: + if (status != STATUS_PENDING) { + + // + // Set-up the Irp status for mpio's completion of the request. + // If it was IRP_MJ_SCSI, one of the helper routines set Srb->SrbStatus + // already. + // + if ((irpStack->MajorFunction == IRP_MJ_SCSI) && + (Srb != NULL) && + (Srb->SrbStatus == SRB_STATUS_PENDING)) { + + Srb->SrbStatus = SRB_STATUS_ERROR; + } + + Irp->IoStatus.Status = status; + + // + // Set the event to free up the request handling in the bus-driver. + // + KeSetEvent(Event, 0, FALSE); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_IOCTL, + "DsmSrbDeviceControl (DsmIds %p): Exiting function with status %x.\n", + DsmIds, + status)); + + return status; +} + + +VOID +DsmSetCompletion( + _In_ IN PVOID DsmContext, + _In_ IN PVOID DsmId, + _In_ IN PIRP Irp, + _In_ IN PSCSI_REQUEST_BLOCK Srb, + _Inout_ IN OUT PDSM_COMPLETION_INFO DsmCompletion + ) +/*++ + +Routine Description: + + This routine is called before the actual submission of a request, + but after the categorisation of the I/O. This will be called only + for those requests not handled by the DSM directly: + Read/Write + Other requests not handled by SrbControl or Broadcast + +Arguments: + + DsmContext - The DSM's context. + DsmId - Identifer that was indicated when the request was + categorized (or be LBGetPath) + Irp - Irp containing Srb. + Srb - The request + DsmCompletion - Completion info structure to be filled out by DSM. + +Return Value: + + None + +--*/ +{ + PDSM_CONTEXT dsmContext = DsmContext; + PDSM_DEVICE_INFO deviceInfo = DsmId; + PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); + PDSM_FAILOVER_GROUP failGroup = deviceInfo->FailGroup; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmSetCompletion (DevInfo %p): Entering function.\n", + DsmId)); + + // + // Save off the path that was selected to service this request in Argument3. + // + irpStack->Parameters.Others.Argument3 = failGroup; + + DsmpIncrementCounters(failGroup, Srb); + + if (!dsmContext->DisableStatsGathering) { + + // + // Indicate one more request on this device down this path. + // + InterlockedIncrement(&deviceInfo->NumberOfRequestsInProgress); + } + + // + // Update the passed-in struct with our routine and context values. + // + DsmCompletion->DsmCompletionRoutine = DsmpRequestComplete; + DsmCompletion->DsmContext = DsmContext; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmSetCompletion (DevInfo %p): Exiting function.\n", + DsmId)); + + return; +} + + +ULONG +DsmInterpretError( + _In_ IN PVOID DsmContext, + _In_ IN PVOID DsmId, + _In_ IN PSCSI_REQUEST_BLOCK Srb, + _Inout_ IN OUT NTSTATUS *Status, + _Out_ OUT PBOOLEAN Retry, + _Out_ OUT PLONG RetryInterval, + ... + ) +/*++ + +Routine Description: + + This routine is invoked by MPIO if Status is other than SUCCESS. + A few NTSTATUS and SRB_STATUS values indicate a fatal error. + Also checked are unit attentions, for which a retry is requested. + +Arguments: + + DsmContext - The DSM's context. + DsmId - Identifers returned from DMS_INQUIRE_DRIVER. + Srb - The Srb with an error. + Status - NTSTATUS of the operation. Can be updated. + Retry - Allows the DSM to indicate whether to retry the IO. + RetryInterval - Lets DSM specify (in seconds) when this specific I/O + should be retried. Use MAXLONG to use the default + retry interval. Use zero to retry immediately. + +Return Value: + + DSM_FATAL_ERROR indicates a fatal error. + +--*/ +{ + // + // The requests that will be encountered can be divided into four categories: + // 1. The request that has failed. + // 2. Subsequent requests that were sent down the failing path that will + // complete with failure. + // 3. Requests that were already submitted to LBGetPath() just before InterpretError() + // was called for the failed request (but have yet to have the LB policy + // algo run). + // 4. Requests that come into the Dispatch() routine after the failed request + // has been processed by InterpretError(). + // + // For the failed request: + // ======================= + // 1. Find a standby path to make active/optimized. + // 2. Send STPG asynchronously as a scsi pass through via IRP_MJ_SCSI (this + // way it can be sent at DISPATCH_IRQL) after setting a completion routine. + // 3. Save the devInfo corresponding to the standby path for the failing devInfo. + // 4. Return FATAL to MPIO so that new IO are queued. + // 5. In the completion routine, update the new states for the devInfos. Then + // clear the saved (previously) standby devInfo for the failing devInfo. + // + // For the subsequent request that will fail (since it was sent on the failing path): + // ================================================================================== + // 1. If a standby devInfo has been saved off, it indicates that an STPG was + // already sent, so no need to send another one. + // 2. Return FATAL to MPIO so that this request gets queued. + // + // For the requests that were already submitted to LBGetPath() during this time: + // ============================================================================= + // 1. If there is no active path, check if a standby devInfo has been saved + // away. If it has, return this path. Such requests will fail with check + // condition saying path used is in standby. + // 2. In InterpretError() retry (since the error indicates that request + // completed before STPG completed) without decrementing the remaining + // retries count. + // + // For new requests that come into Dispatch() after above processing: + // ================================================================== + // We don't need to worry about such requests, since MPIO will queue them + // automatically. + // + + PDSM_DEVICE_INFO deviceInfo = DsmId; + ULONG errorMask = 0; + PVOID senseData = SrbGetSenseInfoBuffer(Srb); + UCHAR senseDataLength = SrbGetSenseInfoBufferLength(Srb); + BOOLEAN failover = FALSE; + BOOLEAN retry = FALSE; + BOOLEAN handled = FALSE; + BOOLEAN sendTPG = FALSE; + BOOLEAN tpgException = FALSE; + BOOLEAN devInfoException = FALSE; + PCDB cdb = SrbGetCdb(Srb); + UCHAR opCode = 0; + UCHAR scsiStatus = SrbGetScsiStatus(Srb); + BOOLEAN validSense = FALSE; + UCHAR senseKey = 0; + UCHAR addSenseCode = 0; + UCHAR addSenseCodeQualifier = 0; + + if (cdb) { + opCode = cdb->AsByte[0]; + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmInterpretError (DevInfo %p): Entering function.\n", + DsmId)); + + *RetryInterval = MAXLONG; + + if ((scsiStatus == SCSISTAT_RESERVATION_CONFLICT) || + (*Status == STATUS_DEVICE_BUSY)) { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_RW, + "DsmInterpretError (DevInfo %p): Srb %p. Either busy or res. conflict (%x %x).\n", + DsmId, + Srb, + scsiStatus, + *Status)); + } + + // + // Go ahead and get the sense data if it's valid. + // + if (Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID) { + + NT_ASSERT(senseData != NULL); + + validSense = ScsiGetSenseKeyAndCodes(senseData, + senseDataLength, + SCSI_SENSE_OPTIONS_FIXED_FORMAT_IF_UNKNOWN_FORMAT_INDICATED, + &senseKey, + &addSenseCode, + &addSenseCodeQualifier); + } + + // + // Sense data relating to logical block provisioning should be failed + // immediately back to the class layer for handling. + // + if (validSense) { + if (senseKey == SCSI_SENSE_NOT_READY && + addSenseCode == SCSI_ADSENSE_LUN_NOT_READY && + addSenseCodeQualifier == SCSI_SENSEQ_SPACE_ALLOC_IN_PROGRESS) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmInterpretError (DevInfo %p): Temporary resource exhaustion. Fail Srb %p.\n", + DsmId, + Srb)); + + handled = TRUE; + + } else if (senseKey == SCSI_SENSE_DATA_PROTECT && + addSenseCode == SCSI_ADSENSE_WRITE_PROTECT && + addSenseCodeQualifier == SCSI_SENSEQ_SPACE_ALLOC_FAILED_WRITE_PROTECT) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmInterpretError (DevInfo %p): Permanent resource exhaustion. Fail Srb %p.\n", + DsmId, + Srb)); + + handled = TRUE; + + } else if (senseKey == SCSI_SENSE_UNIT_ATTENTION && + addSenseCode == SCSI_ADSENSE_LB_PROVISIONING && + addSenseCodeQualifier == SCSI_SENSEQ_SOFT_THRESHOLD_REACHED) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmInterpretError (DevInfo %p): Soft threshold reached. Fail Srb %p.\n", + DsmId, + Srb)); + + handled = TRUE; + + } else if (senseKey == SCSI_SENSE_UNIT_ATTENTION && + addSenseCode == SCSI_ADSENSE_OPERATING_CONDITIONS_CHANGED && + addSenseCodeQualifier == SCSI_SENSEQ_INQUIRY_DATA_CHANGED) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmInterpretError (DevInfo %p): Inquiry data changed. Fail Srb %p.\n", + DsmId, + Srb)); + + handled = TRUE; + } else if (senseKey == SCSI_SENSE_UNIT_ATTENTION && + addSenseCode == SCSI_ADSENSE_PARAMETERS_CHANGED && + addSenseCodeQualifier == SCSI_SENSEQ_CAPACITY_DATA_CHANGED) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmInterpretError (DevInfo %p): Capacity data changed. Fail Srb %p.\n", + DsmId, + Srb)); + + handled = TRUE; + } + } + + if (handled) { + return errorMask; + } + + // + // Check the NT Status first. + // Several are clearly failover conditions. + // + switch (*Status) { + case STATUS_DEVICE_NOT_CONNECTED: + case STATUS_DEVICE_DOES_NOT_EXIST: + case STATUS_NO_SUCH_DEVICE: + case STATUS_DELETE_PENDING: { + + // + // The port pdo has either been removed or is + // very broken. A fail-over is necessary. + // + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmInterpretError (DevInfo %p): Will initiate fail over. Status %x. Opcode %x.\n", + DsmId, + *Status, + opCode)); + + handled = TRUE; + failover = TRUE; + break; + } + + case STATUS_IO_DEVICE_ERROR: { + + if (Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID) { + + if (validSense) { + + // + // See if it's a unit attention. + // + if (senseKey == SCSI_SENSE_UNIT_ATTENTION) { + + switch (addSenseCode) { + + case SCSI_ADSENSE_PARAMETERS_CHANGED: { + + switch (addSenseCodeQualifier) { + + case SPC3_SCSI_SENSEQ_ASYMMETRIC_ACCESS_STATE_CHANGED: + case SPC3_SCSI_SENSEQ_IMPLICIT_ASYMMETRIC_ACCESS_STATE_TRANSITION_FAILED: { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmInterpretError (DevInfo %p): TPG states have changed. Requesting retry on Srb %p. Will send asyn RTPG.\n", + DsmId, + Srb)); + + // + // Retry but after sending RTPG, which will update the path states. + // + sendTPG = TRUE; + retry = TRUE; + handled = TRUE; + errorMask = DSM_RETRY_DONT_DECREMENT; + + if (addSenseCodeQualifier == SPC3_SCSI_SENSEQ_ASYMMETRIC_ACCESS_STATE_CHANGED) { + + // + // Worth retrying on the same path. + // + devInfoException = TRUE; + NT_ASSERT(!tpgException); + } + + break; + } + + + case SPC3_SCSI_SENSEQ_RESERVATIONS_RELEASED: { + + // + // This request needs to be immediately retried down the same path. + // + retry = TRUE; + *RetryInterval = 0; + handled = TRUE; + InterlockedExchangePointer(&(deviceInfo->Group->PathToBeUsed), deviceInfo->FailGroup); + break; + } + + case SPC3_SCSI_SENSEQ_MODE_PARAMETERS_CHANGED: + case SPC3_SCSI_SENSEQ_RESERVATIONS_PREEMPTED: + case SPC3_SCSI_SENSEQ_REGISTRATIONS_PREEMPTED: + case SPC3_SCSI_SENSEQ_CAPACITY_DATA_HAS_CHANGED: { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmInterpretError (DevInfo %p): Failing request. STATUS_IO_DEVICE_ERROR (params changed). SrbStatus (%x) Scsi (%x) AddQual (%u).\n", + DsmId, + Srb->SrbStatus, + scsiStatus, + addSenseCodeQualifier)); + + // + // Just fail these back. + // + handled = TRUE; + break; + } + + default: { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmInterpretError (DevInfo %p): UNIT_ATTENTION for params changed. ASCQ %x. Asking for retry on Srb %p.\n", + DsmId, + addSenseCodeQualifier, + Srb)); + + // + // Indicate that a retry is necessary. + // + retry = TRUE; + handled = TRUE; + + break; + } + } + + break; + } + + + case SPC3_SCSI_ADSENSE_COMMANDS_CLEARED_BY_ANOTHER_INITIATOR: { + + if (addSenseCodeQualifier == 0x00) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmInterpretError (DevInfo %p): UNIT_ATTENTION (commands cleared by another initiator). Fail back to upper level. Srb %p.\n", + DsmId, + Srb)); + + // + // Commands cleared by another Initiator + // + handled = TRUE; + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmInterpretError (DevInfo %p): UNIT_ATTENTION (commands cleared by another initiator). ASCQ %x. Asking for retry on Srb %p.\n", + DsmId, + addSenseCodeQualifier, + Srb)); + + // + // Indicate that a retry is necessary. + // + retry = TRUE; + handled = TRUE; + } + + + break; + } + + case SCSI_ADSENSE_OPERATING_CONDITIONS_CHANGED: { + + if (addSenseCodeQualifier == SCSI_SENSEQ_VOLUME_SET_MODIFIED || + addSenseCodeQualifier == SCSI_SENSEQ_REPORTED_LUNS_DATA_CHANGED) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmInterpretError (DevInfo %p): VolumeSet/LunsData changed. Fail Srb %p.\n", + DsmId, + Srb)); + + // + // Fail back to upper layers. + // + handled = TRUE; + + break; + + } else { + + // + // Fall through to default case (ie. retry the request) + // + } + } + + default: { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmInterpretError (DevInfo %p): UNIT_ATTENTION. ASC %x, ASCQ %x. Asking for retry on Srb %p.\n", + DsmId, + addSenseCode, + addSenseCodeQualifier, + Srb)); + + // + // Indicate that a retry is necessary. + // + retry = TRUE; + handled = TRUE; + + break; + } + } + } else if (senseKey == SCSI_SENSE_NOT_READY) { + + if (addSenseCode == SCSI_ADSENSE_LUN_NOT_READY) { + + if (scsiStatus == SCSISTAT_CHECK_CONDITION) { + + switch (addSenseCodeQualifier) { + + // + // See if failure is due to device's current TPG state. + // + // If the failure is PORT_IN_STANDBY_STATE, we leave DSM_RETRY_DONT_DECREMENT unset if no active path exists, + // because otherwise MPIO will not be able to find a better path, and it will get into an infinite loop + // of trying and failing the command on a Standby path. See WCxeTfs:89150 + // + case SPC3_SCSI_SENSEQ_ASYMMETRIC_ACCESS_STATE_TRANSITION: + case SPC3_SCSI_SENSEQ_TARGET_PORT_IN_UNAVAILABLE_STATE: + + errorMask = DSM_RETRY_DONT_DECREMENT; + + case SPC3_SCSI_SENSEQ_TARGET_PORT_IN_STANDBY_STATE: + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmInterpretError (DevInfo %p): TPG-transition/TPG-SB/TPG-UA. ASCQ %x. Will send down async RTPG. Asking for retry on Srb %p.\n", + DsmId, + addSenseCodeQualifier, + Srb)); + + // + // Indicate that a retry is necessary but without decrementing the remaining + // retries count. However, we may need to send down an STPG/RTPG also. + // And we must set PTBU to a path that is in a different TPG. + // + sendTPG = TRUE; + tpgException = TRUE; + NT_ASSERT(!devInfoException); + retry = TRUE; + handled = TRUE; + + if ((addSenseCodeQualifier == SPC3_SCSI_SENSEQ_TARGET_PORT_IN_STANDBY_STATE) && + DsmIsReadWrite(opCode)) { + + PDSM_CONTEXT context = (PDSM_CONTEXT) deviceInfo->DsmContext; + KIRQL oldIrql = ExAcquireSpinLockExclusive(&(context->DsmContextLock)); + BOOLEAN activePathExists = ( NULL != DsmpGetAnyActivePath(deviceInfo->Group, FALSE, NULL, 0) ); + ExReleaseSpinLockExclusive(&(context->DsmContextLock), oldIrql); + + if (activePathExists) { + errorMask = DSM_RETRY_DONT_DECREMENT; + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmInterpretError (DevInfo %p): Not decrementing error counter, as an active path exists in group %p and opcode %x is r/w\n", + DsmId, + deviceInfo->Group, + opCode)); + } + } + + break; + + case SCSI_SENSEQ_MANUAL_INTERVENTION_REQUIRED: + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmInterpretError (DevInfo %p): Manual intervention required. Asking for retry on Srb %p.\n", + DsmId, + Srb)); + + // + // This may be caused by NDU of controller firmware. It does not + // necessarily indicate that the device won't be ready via other path(s). + // Worth retrying instead of immediately failing back. + // + retry = TRUE; + handled = TRUE; + + break; + } + } + } + } + } + + } else if (Srb->SrbStatus == SRB_STATUS_BUS_RESET) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmInterpretError (DevInfo %p): BUS_RESET. Failing back Srb %p.\n", + DsmId, + Srb)); + + // + // Upper layers will retry in this case. If we retry here it will + // have a multiplicative effect which may result in a very long + // IO completion time if the device persistently times out. + // + retry = FALSE; + handled = TRUE; + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmInterpretError (DevInfo %p): Failing request. STATUS_IO_DEVICE_ERROR. SrbStatus (%x) ScsiStatus (%x).\n", + DsmId, + Srb->SrbStatus, + scsiStatus)); + } + + break; + } + + case STATUS_BUFFER_OVERFLOW: { + + if (DsmIsReadWrite(opCode)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmInterpretError (DevInfo %p): BUFFER_OVERFLOW: Retry.\n", + DsmId)); + + // + // Retry these, as this condition might indicate a torn write. + // + retry = TRUE; + handled = TRUE; + } + + break; + } + + case STATUS_DEVICE_BUSY: { + + // + // See if it's a check condition for TPG states in transition. + // + if (Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID && + scsiStatus == SCSISTAT_CHECK_CONDITION) { + + if (validSense) { + + if (senseKey == SCSI_SENSE_NOT_READY && + addSenseCode == SCSI_ADSENSE_LUN_NOT_READY && + addSenseCodeQualifier == SPC3_SCSI_SENSEQ_ASYMMETRIC_ACCESS_STATE_TRANSITION) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmInterpretError (DevInfo %p): TPG transition. Will send down async RTPG. Asking for retry on Srb %p.\n", + DsmId, + Srb)); + + // + // Indicate that a retry is necessary but without decrementing the remaining + // retries count. However, we may need to send down an STPG/RTPG also. + // And we must set PTBU to a path that is in a different TPG. + // + sendTPG = TRUE; + tpgException = TRUE; + NT_ASSERT(!devInfoException); + retry = TRUE; + handled = TRUE; + errorMask = DSM_RETRY_DONT_DECREMENT; + } + + } + } + + break; + } + + case STATUS_DEVICE_NOT_READY: { + + if (Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID && + scsiStatus == SCSISTAT_CHECK_CONDITION) { + + if (validSense) { + if (senseKey == SCSI_SENSE_NOT_READY && + addSenseCode == SCSI_ADSENSE_LUN_NOT_READY) { + + switch (addSenseCodeQualifier) { + + case SCSI_SENSEQ_MANUAL_INTERVENTION_REQUIRED: { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmInterpretError (DevInfo %p): Manual intervention required. Asking for retry on Srb %p.\n", + DsmId, + Srb)); + + // + // This may be caused by NDU of controller firmware. It does not + // necessarily indicate that the device won't be ready via other path(s). + // Worth retrying instead of immediately failing back. + // + retry = TRUE; + handled = TRUE; + + break; + } + + case SCSI_SENSEQ_SPACE_ALLOC_IN_PROGRESS: { + // + // This indicates a logical block provisioning temporary resource exhaustion + // condition and therefore we must allow the class layer to handle it. + // + retry = FALSE; + handled = TRUE; + + break; + } + + default: { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmInterpretError (DevInfo %p): Unhandled AddQual %x.\n", + DsmId, + addSenseCodeQualifier)); + + break; + } + } + } + } + } + } + + + default: { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_RW, + "DsmInterpretError (DevInfo %p): Unhandled status code %x.\n", + DsmId, + *Status)); + + break; + } + } + + if (!handled) { + + // + // The NTSTATUS didn't indicate a fail-over condition, but + // check various srb status for failover-class error. + // + switch (Srb->SrbStatus) { + case SRB_STATUS_SELECTION_TIMEOUT: + case SRB_STATUS_INVALID_LUN: + case SRB_STATUS_INVALID_TARGET_ID: + case SRB_STATUS_NO_DEVICE: + case SRB_STATUS_NO_HBA: + case SRB_STATUS_INVALID_PATH_ID: { + + // + // All of these are fatal. + // + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmInterpretError (DevInfo %p): SrbStatus 0x%x. Will initiate fail over.\n", + DsmId, + Srb->SrbStatus)); + + failover = TRUE; + break; + } + + + default: { + + if ((scsiStatus == SCSISTAT_CHECK_CONDITION) && + (Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID)) { + + if (validSense) { + + switch (senseKey) { + + case SCSI_SENSE_NO_SENSE: { + + if (addSenseCode == SCSI_ADSENSE_NO_SENSE && + addSenseCodeQualifier == SCSI_SENSEQ_CAUSE_NOT_REPORTABLE) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmInterpretError (DevInfo %p): CheckCondition with no sense info. Will initiate fail over.\n", + DsmId)); + + // + // This could be a transient error generated + // in response to potentially a hardware fault. + // Worth trying another path. + // + failover = TRUE; + handled = TRUE; + } + + break; + } + + case SCSI_SENSE_ILLEGAL_REQUEST: { + + if (addSenseCode == SCSI_ADSENSE_INVALID_LUN) { + + if (addSenseCodeQualifier == 0x00) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmInterpretError (DevInfo %p): Invalid LUN. Will initiate fail over.\n", + DsmId)); + + // + // LUN may still exist on other path(s). + // Worth a failover. + // + failover = TRUE; + handled = TRUE; + } + } + + break; + } + + case SCSI_SENSE_HARDWARE_ERROR: { + + if (addSenseCode == SPC3_SCSI_ADSENSE_LOGICAL_UNIT_COMMAND_FAILED) { + + if (addSenseCodeQualifier == SPC3_SCSI_SENSEQ_SET_TARGET_PORT_GROUPS_FAILED) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmInterpretError (DevInfo %p): STPG failed. Will initiate fail over.\n", + DsmId)); + + // + // If an STPG failed, treat as FATAL and get another + // path set to A/O via another STPG. + // + failover = TRUE; + handled = TRUE; + } + } else if ((addSenseCode == SCSI_ADSENSE_LOGICAL_UNIT_ERROR && addSenseCodeQualifier == SCSI_SENSEQ_TIMEOUT_ON_LOGICAL_UNIT) || + (addSenseCode == SCSI_ADSENSE_DATA_TRANSFER_ERROR && addSenseCodeQualifier == SCSI_SENSEQ_INITIATOR_RESPONSE_TIMEOUT)) { + + // + // Could potentially indicate a dropped FC packet. Retry (along another + // path, based on the LB policy). + // + retry = TRUE; + handled = TRUE; + } + + break; + } + + default: { + + break; + } + } + } + } + + if (!handled) { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_RW, + "DsmInterpretError (DevInfo %p): Unhandled SRB Status 0x%x. Sense data %x|%x|%x.\n", + DsmId, + Srb->SrbStatus, + validSense ? senseKey : 0xFF, + validSense ? addSenseCode : 0xFF, + validSense ? addSenseCodeQualifier : 0xFF)); + } + + break; + } + } + } + + if (failover) { + ULONG SpecialHandlingFlag = 0; + + // + // If ALUA is supported, then it is possible that we may need to send + // down an STPG so build an IRP and fill in the SRB for STPG and send it down. + // + if (!DsmpIsSymmetricAccess(deviceInfo)) { + + DsmpSetLBForPathFailingALUA(DsmContext, deviceInfo, TRUE, SpecialHandlingFlag); + + } else { + + // + // If device doesn't support ALUA, we just need to update + // states without sending down any commands (STPG) + // + DsmpSetLBForPathFailing(DsmContext, deviceInfo, TRUE, SpecialHandlingFlag); + } + + errorMask = DSM_FATAL_ERROR; + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_RW, + "DsmInterpretError(DevInfo %p): Device changed to state %d\n", + deviceInfo, + deviceInfo->State)); + +#if DBG + { + ULONG inx; + PDSM_GROUP_ENTRY group = deviceInfo->Group; + PDSM_DEVICE_INFO tempDevInfo; + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmInterpretError (DevInfo %p): Device %p in group %p being marked as failed. NTStatus 0x%x.\n", + DsmId, + deviceInfo, + group, + *Status)); + + for (inx = 0; inx < group->NumberDevices; inx++) { + + tempDevInfo = group->DeviceList[inx]; + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmInterpretError (DevInfo %p): Device %p at %d. State %d.\n", + DsmId, + tempDevInfo, + inx, + tempDevInfo->State)); + } + } +#endif // DBG + } + + if (retry) { + + if (sendTPG) { + + // + // If ALUA is supported, send down STPG/RTPG as appropriate. + // + if (!DsmpIsSymmetricAccess(deviceInfo)) { + + DsmpSetPathForIoRetryALUA(DsmContext, deviceInfo, tpgException, devInfoException); + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_RW, + "DsmInterpretError(DevInfo %p): SRB request %p will be retried. PTBU set to %p.\n", + deviceInfo, + Srb, + deviceInfo->Group->PathToBeUsed)); + } + } + } + + + *Retry = retry; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmInterpretError (DevInfo %p): Exiting function returning errorMask %x.\n", + DsmId, + errorMask)); + + return errorMask; +} + +BOOLEAN +DsmIsAddressTypeSupported( + _In_ IN PVOID DsmContext, + _In_ IN ULONG AddressType + ) +/*++ + +Routine Description: + + This routine is called when MPIO wants to know if the DSM supports a + particular storage address type. + + This routine must be provided for DSMs of DsmType6 or higher. + +Arguments: + + DsmContext - Context value passed to DsmInitialize() + AddressType - The storage address type being queried. + +Return Value: + + TRUE - If the DSM supports the given storage address type. + FALSE - If the DSM does not support the given storage address type. + +--*/ +{ + UNREFERENCED_PARAMETER(DsmContext); + + if (AddressType == STORAGE_ADDRESS_TYPE_BTL8) + { + return TRUE; + } + + return FALSE; +} + +NTSTATUS +DsmDeviceNotUsed( + _In_ IN PVOID DsmContext, + _In_ IN PVOID DsmId + ) +/*++ + +Routine Description: + + This routine indicates that the device represented by DsmId will not be + initialized completely by MPIO. + The DSM_ID list passed to other functions will no longer contain DsmId, + so internal structures should be updated accordingly. + + This routine must be provided for DSMs of DsmType6 or higher. + +Arguments: + + DsmContext - Context value given to the multipath driver during registration. + DsmId - Value referring to the uninitialized device. + +Return Value: + + NTSTATUS of the operation. + +--*/ +{ + PDSM_DEVICE_INFO deviceInfo = (PDSM_DEVICE_INFO)DsmId; + + DSM_ASSERT(deviceInfo->Group != NULL); + DSM_ASSERT(deviceInfo->Group->GroupSig == DSM_GROUP_SIG); + + // + // Undo anything we did to build up the device in DsmInquire(). + // + DsmRemoveDevice((PDSM_CONTEXT)DsmContext, DsmId, deviceInfo->FailGroup); + + return STATUS_SUCCESS; +} + +NTSTATUS +DsmUnload( + _In_ IN PVOID DsmContext + ) +/*++ + +Routine Description: + + This routine is called when the main module requires the DSM to be unloaded + (ie. prior to the main module unload). + +Arguments: + + DsmContext - Context value passed to DsmInitialize() + +Return Value: + + STATUS_SUCCESS; + +--*/ + +{ + PVOID tempAddress = DsmContext; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_INIT, + "DsmUnload (DsmCtxt %p): Entering function.\n", + DsmContext)); + + DsmpFreeDSMResources((PDSM_CONTEXT) DsmContext); + + if (gMPIOControlObjectRefd) { + + ObDereferenceObject(gMPIOControlObject); + gMPIOControlObjectRefd = FALSE; + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_INIT, + "DsmUnload (DsmCtxt %p): Exiting function.\n", + tempAddress)); + + // + // Stop the tracing subsystem. + // + WPP_CLEANUP(gDsmDriverObject); + + return STATUS_SUCCESS; +} + diff --git a/storage/msdsm/src/msdsm.h b/storage/msdsm/src/msdsm.h new file mode 100644 index 000000000..304e8fc95 --- /dev/null +++ b/storage/msdsm/src/msdsm.h @@ -0,0 +1,1403 @@ +/*++ + +Copyright (C) 2004-2010 Microsoft Corporation + +Module Name: + + msdsm.h + +Abstract: + + Header for the Microsoft Device Specific Module (DSM). + +Environment: + + kernel mode only + +Notes: + +--*/ + +#ifndef _MSDSM_H_ +#define _MSDSM_H_ + +// +// Maximum number of paths per device supported by the DSM. +// This is a limit currently set by MPIO itself and needs to be updated if MPIO +// supports more paths-per-device in the future. +// +#define DSM_MAX_PATHS 32 + +// +// MPIO control object's well known symbolic name +// +#define DSM_MPIO_CONTROL_OBJECT_SYMLINK L"\\DosDevices\\MPIOControl" + +// +// Location of System class node in the registry +// +#define DSM_SYSTEM_CLASS_GUID_KEY L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Class\\{4D36E97D-E325-11CE-BFC1-08002BE10318}" + +// +// Values used for matching and figuring out the DriverVersion +// +#define DSM_INF_PATH L"InfPath" +#define DSM_MSDSM_INF_PATH L"msdsm.inf" +#define DSM_DRIVER_VERSION L"DriverVersion" +#define DSM_DRIVER_VERSION_FIELD_DELIMITER L'.' +#define DSM_BUFFER_MAXCOUNT 64 + +// +// MSDSM's display name. +// +#define DSM_FRIENDLY_NAME L"Microsoft DSM" + +// +// Name of the value for the supported devices in the registry, found in the +// DSM's Services' Parameters key +// +#define DSM_SUPPORTED_DEVICELIST_VALUE_NAME L"DsmSupportedDeviceList" + +// +// Value used to determine if per-IO statistics gathering needs to be turned OFF +// +#define DSM_DISABLE_STATISTICS L"DsmDisableStatistics" + +// +// Names of the values in the registry for whether to use the same path for +// sequential IOs when employing Least Blocks load balance policy, as well +// as its size. +// +#define DSM_USE_CACHE_FOR_LEAST_BLOCKS L"DsmUseCacheForLeastBlocks" +#define DSM_CACHE_SIZE_FOR_LEAST_BLOCKS L"DsmCacheSizeForLeastBlocks" + +// +// Name of the value in the registry for the maximum request retry time during ALUA +// state transitions. This value is found in the DSM's Services' Parameters key, and +// applies only to Persistent Reservation commands. +// +#define DSM_MAX_STATE_TRANSITION_TIME_VALUE_NAME L"DsmMaximumStateTransitionTime" + + +// +// Default max amount of time (in seconds) that a PR failing with retry-able UA will be retried +// +#define DSM_MAX_PR_UNIT_ATTENTION_RETRY_TIME 3 + +// +// Macro to translate seconds to ticks. Each system tick is 10^(-7) seconds. +// +#define DSM_SECONDS_TO_TICKS(_Seconds) ((_Seconds) * 10000000) + +// +// Size of the buffer allocated to retrieve device serial number. +// This is as defined by SPC-3 spec. The identifier with the biggest size is +// SCSI name type (0x8). +// +#define DSM_SERIAL_NUMBER_BUFFER_SIZE 255 + +// +// Number of LB Policies that are supported by this driver. +// +#define DSM_NUMBER_OF_LB_POLICIES 6 + +// +// Size of the buffer passed to read in Persistent Reserve keys. +// +#define DSM_READ_PERSISTENT_KEYS_BUFFER_SIZE 4096 + +// +// The default threshold for sequential IO for the Least Blocks load balance +// policy is 1MB. +// +#define DSM_LEAST_BLOCKS_DEFAULT_THRESHOLD 0x00100000 + +// +// Initialization data structure that needs to be filled in for MPIO +// +DSM_INIT_DATA gDsmInitData; + +// +// Macro used to round of a number to the nearest 8 byte aligned one. +// +#ifdef AlignOn8Bytes +#undef AlignOn8Bytes +#endif +#define AlignOn8Bytes(x) (((x) + 7) & ~7) + +// +// Macro for determining minimum of two numbers +// +#ifdef MIN +#undef MIN +#endif +#define MIN(a, b) ((ULONGLONG)(a) < (ULONGLONG)(b) ? (a) : (b)) + +// +// Macro used to convert a 4 byte array to a ULONG (where byte 0 MSB, byte 3 LSB) +// +#define GetUlongFrom4ByteArray(UCharArray, ULongValue) \ + ((UNALIGNED UCHAR *)&(ULongValue))[3] = ((UNALIGNED UCHAR *)(UCharArray))[0]; \ + ((UNALIGNED UCHAR *)&(ULongValue))[2] = ((UNALIGNED UCHAR *)(UCharArray))[1]; \ + ((UNALIGNED UCHAR *)&(ULongValue))[1] = ((UNALIGNED UCHAR *)(UCharArray))[2]; \ + ((UNALIGNED UCHAR *)&(ULongValue))[0] = ((UNALIGNED UCHAR *)(UCharArray))[3]; + +// +// Macro used to convert a ULONG into a 4 byte array (as big-endian) +// +#define Get4ByteArrayFromUlong(ULongValue, UCharArray) \ + ((UNALIGNED UCHAR *)(UCharArray))[3] = ((UNALIGNED UCHAR *)&(ULongValue))[0]; \ + ((UNALIGNED UCHAR *)(UCharArray))[2] = ((UNALIGNED UCHAR *)&(ULongValue))[1]; \ + ((UNALIGNED UCHAR *)(UCharArray))[1] = ((UNALIGNED UCHAR *)&(ULongValue))[2]; \ + ((UNALIGNED UCHAR *)(UCharArray))[0] = ((UNALIGNED UCHAR *)&(ULongValue))[3]; + +// +// Macro to check if passed in opcode is a read, write +// +#define DsmIsReadRequest(_Opcode) (_Opcode == SCSIOP_READ || _Opcode == SCSIOP_READ16) +#define DsmIsWriteRequest(_Opcode) (_Opcode == SCSIOP_WRITE || _Opcode == SCSIOP_WRITE16) +#define DsmIsReadWrite(_Opcode) (_Opcode == SCSIOP_READ || _Opcode == SCSIOP_READ16 || \ + _Opcode == SCSIOP_WRITE || _Opcode == SCSIOP_WRITE16) + +#define DsmIsReadCapacity( _Opcode ) (_Opcode == SCSIOP_READ_CAPACITY || _Opcode == SCSIOP_READ_CAPACITY16) + + +// +// Macro to find the number of bytes consumed by the array +// +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) + + +// +// Signature used to identify various structures. +// Used solely for debugging purposes. +// +#define DSM_DEVICE_SIG 0xAAAAAAAA +#define DSM_GROUP_SIG 0x55555555 +#define DSM_FOG_SIG 0x88888888 +#define DSM_TARGET_PORT_GROUP_SIG 0x33333333 +#define DSM_TARGET_PORT_SIG 0xCCCCCCCC +#define DSM_CONTROLLER_SIG 0xEEEEEEEE + +#define WNULL (L'\0') +#define WNULL_SIZE (sizeof(WNULL)) + +#if DBG + +// +// NT_ASSERT wrapper. +// +#define DSM_ASSERT(exp) if (DoAssert) { \ + NT_ASSERT(exp); \ + } + +#else // DBG + +#define DSM_ASSERT(exp) + +#endif // DBG + +#define DSM_PARAMETER_PATH_W L"MSDSM\\Parameters" + +// +// Pool Tags used in memory allocation +// +#define DSM_TAG_GENERIC '00ZZ' +#define DSM_TAG_PASS_THRU '10ZZ' +#define DSM_TAG_GROUP_ENTRY '20ZZ' +#define DSM_TAG_FO_GROUP '30ZZ' +#define DSM_TAG_DSM_CONTEXT '40ZZ' +#define DSM_TAG_DEV_INFO '50ZZ' +#define DSM_TAG_SERIAL_NUM '60ZZ' +#define DSM_TAG_CTRL_INFO '70ZZ' +#define DSM_TAG_SUPPORTED_DEV '80ZZ' +#define DSM_TAG_REG_PATH '90ZZ' +#define DSM_TAG_FOG_DEV_ENTRY 'A0ZZ' +#define DSM_TAG_DEV_ID 'B0ZZ' +#define DSM_TAG_DEV_NAME 'C0ZZ' +#define DSM_TAG_LB_POLICY 'D0ZZ' +#define DSM_TAG_PR_KEYS 'E0ZZ' +#define DSM_TAG_RESERVED_DEVICE 'F0ZZ' +#define DSM_TAG_BIN_TO_ASCII '01ZZ' +#define DSM_TAG_TARGET_PORT_LIST_ENTRY '11ZZ' +#define DSM_TAG_TARGET_PORT_GROUP_ENTRY '21ZZ' +#define DSM_TAG_RELATIVE_TARGET_PORT_ID '31ZZ' +#define DSM_TAG_TARGET_PORT_GROUPS '41ZZ' +#define DSM_TAG_CONTROLLER_LIST_ENTRY '51ZZ' +#define DSM_TAG_CONTROLLER_INFO '61ZZ' +#define DSM_TAG_IO_STATUS_BLOCK '71ZZ' +#define DSM_TAG_DEVICE_ID_LIST '81ZZ' +#define DSM_TAG_TP_DEVICE_LIST_ENTRY '91ZZ' +#define DSM_TAG_RETRY_RESERVE 'A1ZZ' +#define DSM_TAG_WORKITEM 'B1ZZ' +#define DSM_TAG_SCSI_ADDRESS 'C1ZZ' +#define DSM_TAG_FAIL_DEVINFO_LIST_ENTRY 'D1ZZ' +#define DSM_TAG_TPG_COMPLETION_CONTEXT 'E1ZZ' +#define DSM_TAG_SCSI_REQUEST_BLOCK 'F1ZZ' +#define DSM_TAG_SCSI_SENSE_INFO '02ZZ' +#define DSM_TAG_SPT_DATA_BUFFER '12ZZ' +#define DSM_TAG_REG_KEY_RELATED '22ZZ' +#define DSM_TAG_DEV_HARDWARE_ID '32ZZ' +#define DSM_TAG_REG_VALUE_RELATED '42ZZ' +#define DSM_TAG_ZOMBIEGROUP_ENTRY '52ZZ' +#define DSM_TAG_PERSISTENT_RESERVATION '62ZZ' + +// +// Parameters subkey name under HKLM\System\CCS\Services\MSDSM +// +#define DSM_SERVICE_PARAMETERS L"Parameters" + +// +// Load Balance settings are persisted in the registry under this key +// +#define DSM_LOAD_BALANCE_SETTINGS L"DsmLoadBalanceSettings" + +// +// Load Balance settings on a VID/PID basis are persistented in the registry +// under this key +// +#define DSM_TARGETS_LOAD_BALANCE_SETTING L"DsmTargetsLoadBalanceSetting" + +// +// Values persisted per device: +// 1. Load Balance Policy +// 2. Preferred Path +// 3. Whether LB policy has been explicitly set +// +#define DSM_LOAD_BALANCE_POLICY L"DsmLoadBalancePolicy" +#define DSM_PREFERRED_PATH L"DsmPreferredPath" +#define DSM_POLICY_EXPLICITLY_SET L"DsmLoadBalancePolicyExplicitlySet" + +// +// Prefix for subkey created for each path +// +#define DSM_PATH L"DSMPath" + +// +// Values persisted per path: +// 1. Whether primary +// 2. Whether optimized +// 3. Path weight. +// +// Primary Optimized State +//==================================== +// True True Active-Optimized +// True False Active-Unoptimized +// False True StandBy +// False False Unavailable +// +#define DSM_PRIMARY_PATH L"DsmPrimaryPath" +#define DSM_OPTIMIZED_PATH L"DsmOptimizedPath" +#define DSM_PATH_WEIGHT L"DsmPathWeight" + +// +// Indicates that device doesn't support ALUA. +// +#define DSM_DEVINFO_ALUA_NOT_SUPPORTED 0 + +// +// Implies that device supports implicit ALUA transistions. +// +#define DSM_DEVINFO_ALUA_IMPLICIT 1 + +// +// Implies that device supports explicit ALUA state transitions. +// +#define DSM_DEVINFO_ALUA_EXPLICIT 2 + +// +// Type of device identifier (VPD 0x83) +// +typedef enum _DSM_DEVID_TYPE { + DSM_DEVID_SERIAL_NUMBER = 1, + DSM_DEVID_RELATIVE_TARGET_PORT, + DSM_DEVID_TARGET_PORT_GROUP +} DSM_DEVID_TYPE, *PDSM_DEVID_TYPE; + +#define _DSM_TERNARY_BOOLEAN UCHAR +typedef _DSM_TERNARY_BOOLEAN DSM_TERNARY_BOOLEAN, *PDSM_TERNARY_BOOLEAN; +#define DSM_TERNARY_UNKNOWN 0 +#define DSM_TERNARY_TRUE 1 +#define DSM_TERNARY_FALSE 2 + +// +// Macro to determine if _Id2 is more preferred than _Id1 to build a device's +// serial number. +// +#define DsmpIsPreferredDeviceId(_Id1, _Id2) (((_Id2) == StorageIdTypeScsiNameString) || \ + ((_Id2) == StorageIdTypeFCPHName && (_Id1) != StorageIdTypeScsiNameString) || \ + ((_Id2) == StorageIdTypeEUI64 && (_Id1) != StorageIdTypeScsiNameString && (_Id1) != StorageIdTypeFCPHName) || \ + ((_Id2) == StorageIdTypeVendorId && (_Id1) != StorageIdTypeScsiNameString && (_Id1) != StorageIdTypeFCPHName && (_Id1) != StorageIdTypeEUI64) || \ + ((_Id2) == StorageIdTypeVendorSpecific && (_Id1) != StorageIdTypeScsiNameString && (_Id1) != StorageIdTypeFCPHName && (_Id1) != StorageIdTypeEUI64 && (_Id1) != StorageIdTypeVendorId)) + +// +// Device State +// +typedef enum _DSM_DEVICE_STATE { + + // + // If ALUA is not supported, this state indicates that the device is active + // and a request can be sent to the device. + // If ALUA is supported, then this state indicates optimizied device-path + // pair for the device. + // + DSM_DEV_ACTIVE_OPTIMIZED = 0, + + // + // If ALUA is not supported, this state is not used. + // If ALUA is supported, then this state indicates active but unoptimized + // device-path pairing for the device. Can be used in in case no + // active/optimized path is available to service the IO. + // + DSM_DEV_ACTIVE_UNOPTIMIZED, + + // + // If ALUA is not supported, this state indicates that the device is in + // standby state. A request can be sent to the device in this state. + // If ALUA is supported, then this state indicates standby device-path + // pairing and only certain requests can be handled in this state. + // + DSM_DEV_STANDBY, + + // + // If ALUA is not supported, this state is not used. + // If ALUA is supported, then this state indicates that the device-path pairing + // is not active and incapable of handling any requests. + // + DSM_DEV_UNAVAILABLE, + + // + // If ALUA is not supported, this state is not used. + // If ALUA is supported, then this state indicates that the device-path pairing + // (actually its TPG) is in a transitioning state. + // + DSM_DEV_TRANSITIONING = 15, + + // + // Initial state when devInfo is created. + // + DSM_DEV_NOT_USED_STATE = 16, + + // + // Indicates that the state was undetermined (this is applicable only for + // a deviceInfo's DesiredState or if the device instance's path was not + // determined). + // + DSM_DEV_UNDETERMINED, + + // + // Indicates that a request sent down previously failed with a fatal error + // + DSM_DEV_FAILED, + + // + // Indicates that InvalidatePath has been called + // + DSM_DEV_INVALIDATED, + + // + // This indicates the device is about to be removed. No new request + // should be sent to the device. + // + DSM_DEV_REMOVE_PENDING, + + // + // This indicates the device has been removed. + // + DSM_DEV_REMOVED + +} DSM_DEVICE_STATE, *PDSM_DEVICE_STATE; + +// +// Device states supported +// +#define DSM_STATE_ACTIVE_OPTIMIZED_SUPPORTED 0 +#define DSM_STATE_STANDBY_SUPPORTED 1 +#define DSM_STATE_ACTIVE_UNOPTIMIZED_SUPPORTED 2 +#define DSM_STATE_UNAVAILABLE_SUPPORTED 4 + + +// +// Macro to determine if devInfo is in a failure state. +// +#define DsmpIsDeviceFailedState(_State) ((_State) > DSM_DEV_NOT_USED_STATE) + +// +// Macro to determine if devInfo was initialized. +// +#define DsmpIsDeviceInitialized(_DeviceInfo) ((_DeviceInfo)->Initialized) + +// +// Macro to determine if device is "usable" (ie. IsPathActive was successfully called). +// +#define DsmpIsDeviceUsable(_DeviceInfo) ((_DeviceInfo)->Usable) + +// +// Macro to determine if devInfo was used to send down registration. +// It the devInfo's group is not reserved, then the devInfo doesn't need to have +// had a register go down it. +// It the group is reserved, then the devInfo MUST have had a register go down it +// for it to be used. +// +#define DsmpIsDeviceUsablePR(_DeviceInfo) (!(_DeviceInfo)->Group->PRKeyValid || (_DeviceInfo)->PRKeyRegistered) + + +// +// Macro to determine if _State2 is a more preferred state than _State1. +// +#define DsmpIsBetterDeviceState(_State1, _State2) (((_State1) == DSM_DEV_STANDBY && (_State2) == DSM_DEV_ACTIVE_UNOPTIMIZED) || \ + ((_State1) == DSM_DEV_UNAVAILABLE && \ + ((_State2) == DSM_DEV_ACTIVE_UNOPTIMIZED || (_State2) == DSM_DEV_STANDBY)) || \ + ((_State1) == DSM_DEV_TRANSITIONING && \ + ((_State2) == DSM_DEV_ACTIVE_UNOPTIMIZED || (_State2) == DSM_DEV_STANDBY) || (_State2) == DSM_DEV_UNAVAILABLE)) + +// +// Macro to determine if passed in _State is active. +// +#define DsmpIsDeviceStateActive(_State) ((_State) == DSM_DEV_ACTIVE_OPTIMIZED || (_State) == DSM_DEV_ACTIVE_UNOPTIMIZED) + +// +// Macro to determine if symmetric access to the storage +// +#define DsmpIsSymmetricAccess(_DeviceInfo) ((_DeviceInfo)->ALUASupport == DSM_DEVINFO_ALUA_NOT_SUPPORTED || \ + ((_DeviceInfo)->ALUASupport == DSM_DEVINFO_ALUA_IMPLICIT && \ + (_DeviceInfo)->Group->Symmetric)) + +// +// Multi-path Group State +// +typedef enum _DSM_GROUP_STATE { + + // + // This indicates that the device is in working state. + // + DSM_GP_NORMAL = 1, + + // + // This indicates that there is a pending reservation failover + // + DSM_GP_PENDING, + + // + // This indicates that the device has lost all its paths + // + DSM_GP_FAILED + +} DSM_GROUP_STATE, *PDSM_GROUP_STATE; + +// +// Fail-Over Group State +// +typedef enum _DSM_FAILOVER_GROUP_STATE { + + // + // This indicates that the path is in working state. + // + DSM_FG_NORMAL = 1, + + // + // This indicates the path which had failed earlier + // is back to working state now. + // + DSM_FG_FAILBACK, + + // + // This indicates the path is about to be removed + // + DSM_FG_PENDING_REMOVE, + + // + // This indicates the path has failed. + // + DSM_FG_FAILED + +} DSM_FAILOVER_GROUP_STATE, *PDSM_FAILOVER_GROUP_STATE; + +#define DsmpIsPathFailedState(_State) ((_State) >= DSM_FG_PENDING_REMOVE) + +// +// DSM Context is the global driver context that gets passed to each of the DSM +// entry points. +// +// The DSM Context will maintain a list of all DeviceInfos (device-path pairing). +// It will maintain a list of Group entries. Each entry in the Group list will +// represent a LUN's different instances down different paths (i.e. DeviceInfos). +// Each entry in the Group will maintain a list of target port groups. +// Each entry in the target port group list will maintain a list of target +// ports that make up the target port group. Every deviceInfo that isn't +// in a failure state will be in the same state as the Asymmetric Access +// State of the target port group. +// There will be a list of Fail Over Group entries, where each entry represents +// the list of devices that fail over as a group (i.e. devices on the same path). +// There will also be a list of controller entries, representing the controllers +// on all storages connected to the system. +// +typedef struct _DSM_CONTEXT { + + // + // Used to synchronize access to the SupportedDevices list. + // + KSPIN_LOCK SupportedDevicesListLock; + + // + // List of supported devices - added into the INF. + // + UNICODE_STRING SupportedDevices; + + // + // Used to synchronize access to the elements in this structure. + // + EX_SPIN_LOCK DsmContextLock; + + // + // Flag cached that indicates if statistics don't need to be gathered + // + BOOLEAN DisableStatsGathering; + + UCHAR Reserved[3]; + + // + // Number of devices currently found. + // + ULONG NumberDevices; + + // + // List of devices. + // + LIST_ENTRY DeviceList; + + // + // Number of multi-path groups. + // + ULONG NumberGroups; + + // + // List of multi-path groups. + // + LIST_ENTRY GroupList; + + // + // Number of fail-over groups. + // + ULONG NumberFOGroups; + + // + // List of fail-over groups. + // + LIST_ENTRY FailGroupList; + + // + // Number of controllers. + // + ULONG NumberControllers; + + // + // List of controllers + // + LIST_ENTRY ControllerList; + + // + // Number of stale fail-over groups + // + ULONG NumberStaleFOGroups; + + // + // List of stale fail-over groups maintained for paths for which all devices + // have gotten removed but for which there is still outstanding IO-statistics + // + LIST_ENTRY StaleFailGroupList; + + // + // Context value passed to the DSM from MPIO. + // + PVOID MPIOContext; + + + // + // Look-aside list of completion routine context structures. + // + NPAGED_LOOKASIDE_LIST CompletionContextList; + +} DSM_CONTEXT, *PDSM_CONTEXT; + +// +// Statistics structure. Used by the device and path routines. +// +typedef struct _DSM_STATS { + + ULONG NumberReads; + ULONG NumberWrites; + ULONGLONG BytesRead; + ULONGLONG BytesWritten; + +} DSM_STATS, *PDSM_STATS; + + +// +// Information about each device that is supported by the DSM. +// +typedef struct _DSM_DEVICE_INFO { + + // + // To link to the next device info structure in the list + // + LIST_ENTRY ListEntry; + + // + // The device SIG. Used for debug. + // + ULONG DeviceSig; + + // + // Back-pointer to the DSM_CONTEXT. + // + PVOID DsmContext; + + // + // The underlying port driver PDO. + // + PDEVICE_OBJECT PortPdo; + + // + // The port FDO to which PortPdo is attached. + // + PDEVICE_OBJECT PortFdo; + + // + // The DeviceObject to which I/Os generated by the DSM should + // be sent. This is given to us by MPIO. + // + PDEVICE_OBJECT TargetObject; + + // + // The multi-path group to which this device belongs. + // + struct _DSM_GROUP_ENTRY *Group; + + // + // The fail-over group to which this device belongs. + // + struct _DSM_FAILOVER_GROUP *FailGroup; + + // + // The controller through which this device showed up. + // + struct _DSM_CONTROLLER_LIST_ENTRY *Controller; + + // + // The Target Port Group that this device belongs to. + // + struct _DSM_TARGET_PORT_GROUP_ENTRY *TargetPortGroup; + + // + // The Target Port that this device was exposed via. + // + struct _DSM_TARGET_PORT_LIST_ENTRY *TargetPort; + + // + // The current state of this device: ACTIVE_O, ACTIVE_U, STANDBY, UNAVAILABLE, etc. + // + DSM_DEVICE_STATE State; + + // + // Previous state of this device. Updated whenever this deviceInfo makes a + // state transition. + // + DSM_DEVICE_STATE PreviousState; + + // + // The desired state of this device: based on PrimaryPath and OptimizedPath + // specified in the registry. + // + DSM_DEVICE_STATE DesiredState; + + // + // The ALUA state of the TPG immediately after a ReportTPG is issued. + // + DSM_DEVICE_STATE ALUAState; + + // + // Holds state information temporarily while applying LB policy. Used in case + // changes need to be reverted in case of failure to apply the policy. + // + DSM_DEVICE_STATE TempPreviousStateForLB; + + // + // This is to save off the last known non-failed state. + // In case of an error down this deviceInfo, it is marked to be in Failed state. + // However, if no remove comes down for this device and a PathVerify down this + // deviceInfo succeeds, we need to put the deviceInfo back into a usable state. + // + DSM_DEVICE_STATE LastKnownGoodState; + + // + // This counter indicates that this deviceInfo is being used and a remove + // must thus wait until the counter falls to 0. + // + LONG BlockRemove; + + + // + // This indicates whether this device has handled a register/register_ignore_existing request, + // irrespective of the actual status of the operation. + // + BOOLEAN RegisterServiced; + + // + // This flag is set when a register/register_ignore_existing succeeds down this device-path pair. + // + BOOLEAN PRKeyRegistered; + + // + // Indicates whether the serial number was embedded in the device + // descriptor, or it was allocated. + // + BOOLEAN SerialNumberAllocated; + + // + // Flag to indicate that SetDeviceInfo has been called (and succeeded) on this device + // + BOOLEAN Initialized; + + // + // Flag to indicate that IsPathActive has been called (and succeeded) on this device. + // + BOOLEAN Usable; + + // + // Flag to indicate if IALUAE was disabled (via mode select) + // + BOOLEAN ImplicitDisabled; + + // + // Flag to indicate that RTPG has already been sent down in Inquire, so + // PathVerify can ignore sending down one more if it is called during + // device initialization. + // + BOOLEAN IgnorePathVerify; + + // + // Bit map indicating whether (and what kind) of ALUA support. + // + UCHAR ALUASupport; + + // + // Weight assigned to this path by management application. This is used + // when doing Load Balancing based on weighted paths. + // + ULONG PathWeight; + + // + // Number of requests outstanding on this device. + // + LONG NumberOfRequestsInProgress; + + // + // I/O, Fail-Over statistics. + // + DSM_STATS DeviceStats; + + // + // The device's serial number. + // + PSTR SerialNumber; + + // + // The scsi address of the port pdo. + // + PSCSI_ADDRESS ScsiAddress; + + + // + // Kernel structure that describes this device. Passed in to Inquire. + // + // NOTE: Descriptor should be the LAST field in this structure + // + STORAGE_DEVICE_DESCRIPTOR Descriptor; + +} DSM_DEVICE_INFO, *PDSM_DEVICE_INFO; + +typedef enum _DSM_DEFAULT_LB_POLICY_TYPE { + DSM_DEFAULT_LB_POLICY_ALUA_CAPABILITY = 0, // DSM assigned based on LUN access capability + DSM_DEFAULT_LB_POLICY_DSM_WIDE, // Admin has set a DSM-wide default policy + DSM_DEFAULT_LB_POLICY_VID_PID, // Admin has set a default policy for LUN's VID/PID + DSM_DEFAULT_LB_POLICY_LUN_EXPLICIT // Admin has explicitly set the policy on the LUN +} DSM_DEFAULT_LB_POLICY_TYPE, *PDSM_DEFAULT_LB_POLICY_TYPE; + +typedef ULONG DSM_LOAD_BALANCE_TYPE, *PDSM_LOAD_BALANCE_TYPE; + + +// +// Information about multi-path groups: The same device found via multiple paths +// are put under one group. Each group will have it's own Load Balance policy +// settings. In other words, Load Balance policy settings are on per-device basis. +// +typedef struct _DSM_GROUP_ENTRY { + + // + // To link to the next entry in the multi-path group. + // + LIST_ENTRY ListEntry; + + // + // Group signature. Used for debug. + // + ULONG GroupSig; + + // + // Ordinal of creation. Never decremented. + // + ULONG GroupNumber; + + // + // State of the group. + // + DSM_GROUP_STATE State; + + // + // Number of devices in the multi-path group. + // + ULONG NumberDevices; + + // + // Array of devices belonging to this group. + // + PDSM_DEVICE_INFO DeviceList[DSM_MAX_PATHS]; + + // + // Max time to retry failed PR requests + // + ULONG MaxPRRetryTimeDuringStateTransition; + + // + // Number of target port groups that this device is accessible via. + // + ULONG NumberTargetPortGroups; + + // + // Array of the target port groups that this LUN belongs in. + // + struct _DSM_TARGET_PORT_GROUP_ENTRY *TargetPortGroupList[DSM_MAX_PATHS]; + + // + // Key used in Persistent Reserve\Release. This key is provided to the DSM + // by Cluster service. If cluster service has provided the key PRKeyValid + // is set to TRUE. PRKeyValid is set to FALSE otherwise. + // PRServiceAction, PRType and PRScope are the service action, type and + // scope associated with the PR registration. + // + UCHAR PersistentReservationRegisteredKey[8]; + UCHAR PRServiceAction; + UCHAR PRType; + UCHAR PRScope; + UCHAR PRKeyValid; + + + // + // Flag used to denote that LU access is symmetric down all paths + // + BOOLEAN Symmetric; + + // + // Flag to indicate whether or not to use same path for sequential IO + // when employing Least Blocks load balance policy. + // + BOOLEAN UseCacheForLeastBlocks; + + // + // Flag used to indicate if a throttle request succeeded. + // + ULONG Throttled; + + // + // Counter to track the number of RTPG in flight. + // + ULONG InFlightRTPG; + + // + // A bitmask of which devices are currently reserved. + // + ULONG ReservationList; + + // + // Which type of Load Balancing is being performed. + // + DSM_LOAD_BALANCE_TYPE LoadBalanceType; + + // + // Indicates how the Load Balancing policy was selected. + // + DSM_DEFAULT_LB_POLICY_TYPE LBPolicySelection; + + // + // The path to use when possible - if in F.O. Only, if failover had taken + // place and this path comes back online, failback to this path will take + // place. + // + ULONGLONG PreferredPath; + + // + // The path to choose when Round Robin Load Balance policy is in use + // + PVOID PathToBeUsed; + + // + // Size of cache set by Admin. Used in case of handling sequential + // IO in Least Blocks policy. + // + ULONGLONG CacheSizeForLeastBlocks; + + // + // The HardwareId (VID/PID) of the LUN + // + PWSTR HardwareId; + + // + // The registry key under which Load Balance Policy settings + // are stored in the registry for this Device Group. + // + PWSTR RegistryKeyName; + + // + // Number of failing deviceInfos + // + ULONG NumberFailingDevInfos; + + // + // To link the list of failed A/O devInfos and the corresponding non-A/O + // devInfos that are temporarily being used to service IO until STPG can + // properly update the device states. This is applicable only for ALUA + // devices. + // + LIST_ENTRY FailingDevInfoList; + + // + // General Purpose Event. + // + KEVENT Event; + +} DSM_GROUP_ENTRY, *PDSM_GROUP_ENTRY; + +// +// The collection of devices on one path. These fail-over as a unit. +// A path is considered an I_T nexus, i.e. Initiator port to Target (controller) port. +// +typedef struct _DSM_FAILOVER_GROUP { + + // + // To link to the next entry in the failover group + // + LIST_ENTRY ListEntry; + + // + // Signature. Used for debug. + // + ULONG FailOverSig; + + // + // State of the Path. + // + DSM_FAILOVER_GROUP_STATE State; + + // + // The pathId corresponding to this FOG. It may or may not be + // the same as what MPIO gave us as the default value. + // + PVOID PathId; + + // + // The default pathId (port FDO). + // + PDEVICE_OBJECT MPIOPath; + + // + // Last LBA + // + ULONGLONG LastLba; + + // + // Cumulative outstanding IO (in terms of size) + // + ULONGLONG OutstandingBytesOfIO; + + // + // Count of inflight IOs. This will be used in LQD load balance policy. + // + volatile LONG NumberOfRequestsInFlight; + + // + // Number of devices in this FOG. + // + ULONG Count; + + // + // List of devices that will over together. + // + LIST_ENTRY FOG_DeviceList; + + // + // List of zombie groups (in case a device is removed before the failover + // processing begins). + // + LIST_ENTRY ZombieGroupList; + +} DSM_FAILOVER_GROUP, *PDSM_FAILOVER_GROUP; + + +// +// Information about a target port group entry for a given LUN. +// Note: This is not a global list of all TPGs that are built. It is local to a Group entry. +// +typedef struct _DSM_TARGET_PORT_GROUP_ENTRY { + + // + // Signature. Used for debug. + // + ULONG TargetPortGroupSig; + + // + // The asymmetric access state for this target port group: + // ACTIVE_O, ACTIVE_U, STANDBY or UNAVAILABLE + // + DSM_DEVICE_STATE AsymmetricAccessState; + + // + // Flag to indicate if this is the preferred target port group. + // + BOOLEAN Preferred; + + // + // Supported access states + // + BOOLEAN ActiveOptimizedSupported; + BOOLEAN ActiveUnoptimizedSupported; + BOOLEAN StandBySupported; + BOOLEAN UnavailableSupported; + + // + // Indicates if the device reports asymmetric state as being under transition. + // + BOOLEAN TransitioningSupported; + + // + // Flag to indicate if this has been returned in any subsequent RTPG after + // it is initially built. (If this flag is not set after parsing the RTPG + // information, it indicates that this TPG entry is stale and should be + // deleted). + // + BOOLEAN Traversed; + + UCHAR Reserved; + + // + // The target group identifier + // + USHORT Identifier; + + // + // Status code + // + UCHAR StatusCode; + + // + // Vendor unique + // + UCHAR VendorUnique; + + // + // Backpointer to owning group + // + PDSM_GROUP_ENTRY Group; + + // + // Number of target ports that make up this group + // + ULONG NumberTargetPorts; + + // + // Linked list of target ports that make up this target port group. + // + LIST_ENTRY TargetPortList; + +} DSM_TARGET_PORT_GROUP_ENTRY, *PDSM_TARGET_PORT_GROUP_ENTRY; + + +// +// Information about each target port list entry for a given target port group. +// Note: this is not a global list of all TPs. It is local to a given TPG entry. +// +typedef struct _DSM_TARGET_PORT_LIST_ENTRY { + + // + // Link + // + LIST_ENTRY ListEntry; + + // + // Signature. Used for debug. + // + ULONG TargetPortSig; + + // + // Relative target port identifier + // + ULONG Identifier; + + // + // Backpointer to owning target port group + // + PDSM_TARGET_PORT_GROUP_ENTRY TargetPortGroup; + + // + // Number of device instances exposed via this target port + // + ULONG Count; + + // + // List of device instances exposed via this target port + // + LIST_ENTRY TP_DeviceList; + +} DSM_TARGET_PORT_LIST_ENTRY, *PDSM_TARGET_PORT_LIST_ENTRY; + +// +// Information about each controller entry +// +typedef struct _DSM_CONTROLLER_LIST_ENTRY { + + // + // To link to the next contoller entry. + // + LIST_ENTRY ListEntry; + + // + // It's signature. Used for debug. + // + ULONG ControllerSig; + + // + // Device object (this controller's PDO). + // + PDEVICE_OBJECT DeviceObject; + + // + // Port FDO through which this controller object was exposed. + // + PDEVICE_OBJECT PortObject; + + // + // Identifier. + // + _Field_size_(IdLength) PUCHAR Identifier; + + // + // Identifier length. + // + ULONG IdLength; + + // + // Identifier code set. + // + STORAGE_IDENTIFIER_CODE_SET IdCodeSet; + + // + // Controller's SCSI address. + // + PSCSI_ADDRESS ScsiAddress; + + // + // Number of references to this entry. + // + UCHAR RefCount; + + // + // Flag to indicate whether this is a fake entry built for storage that do + // NOT have controllers + // + BOOLEAN IsFakeController; + + UCHAR Reserved[2]; + +} DSM_CONTROLLER_LIST_ENTRY, *PDSM_CONTROLLER_LIST_ENTRY; + +// +// Generic linked list of devices +// +typedef struct _DSM_DEVICELIST_ENTRY { + + // + // To link to the next device info structure in the list + // + LIST_ENTRY ListEntry; + + // + // Representation of device-path pair + // + PDSM_DEVICE_INFO DeviceInfo; + +} DSM_DEVICELIST_ENTRY, *PDSM_DEVICELIST_ENTRY; + +// +// Zombie Group List Entry +// +typedef struct _DSM_ZOMBIEGROUP_ENTRY { + + // + // To link to the next zombie group structure in the list + // + LIST_ENTRY ListEntry; + + // + // Pointer to actual group entry + // + PDSM_GROUP_ENTRY Group; + + // + // Flag to indicate that the failover thread has processed this entry. + // + BOOLEAN Processed; + +} DSM_ZOMBIEGROUP_ENTRY, *PDSM_ZOMBIEGROUP_ENTRY; + +// +// Linked list of devices that will failover as a group +// +typedef DSM_DEVICELIST_ENTRY DSM_FOG_DEVICELIST_ENTRY, *PDSM_FOG_DEVICELIST_ENTRY; + +// +// Linked list of the same device being exposed off of a particular target port +// (possibly because the controller is connected to multiple HBAs). +// +typedef DSM_DEVICELIST_ENTRY DSM_TARGET_PORT_DEVICELIST_ENTRY, *PDSM_TARGET_PORT_DEVICELIST_ENTRY; + +// +// Information about each failing devInfo and its corresponding devInfo +// being used temporarily to service requests until STPG can update new +// device states. +// +typedef struct _DSM_FAIL_PATH_PROCESSING_LIST_ENTRY { + + // + // To link to the next device info structure in the list + // + LIST_ENTRY ListEntry; + + // + // Representation of the failing device-path pair + // + PDSM_DEVICE_INFO FailingDeviceInfo; + + // + // Representation of the new candidate device-path pair that will take over + // processing of requests + // + PDSM_DEVICE_INFO TempDeviceInfo; + +} DSM_FAIL_PATH_PROCESSING_LIST_ENTRY, *PDSM_FAIL_PATH_PROCESSING_LIST_ENTRY; + +// +// Completion context structure. +// +typedef struct _DSM_COMPLETION_CONTEXT { + + // + // The device that handled the request. + // + PDSM_DEVICE_INFO DeviceInfo; + + // + // The global context. + // + PDSM_CONTEXT DsmContext; + + // + // These are used to store control code, pointer to KEVENT, etc. + // + PVOID RequestUnique1; + + ULONG_PTR RequestUnique2; + +#if DBG + // + // Request time-stamp. + // + LARGE_INTEGER TickCount; +#endif + +} DSM_COMPLETION_CONTEXT, *PDSM_COMPLETION_CONTEXT; + +// +// Completion context structure for report/set target port groups. +// +typedef struct _DSM_TPG_COMPLETION_CONTEXT { + + PDSM_COMPLETION_CONTEXT CompletionContext; + + PSCSI_REQUEST_BLOCK Srb; + + PVOID SenseInfoBuffer; + + ULONG NumberRetries; + + UCHAR SenseInfoBufferLength; + +} DSM_TPG_COMPLETION_CONTEXT, *PDSM_TPG_COMPLETION_CONTEXT; + +// +// Version number used to determine whice version of MPIO_DSM_Path to use. +// +#define DSM_WMI_VERSION_1 1 +#define DSM_WMI_VERSION_2 2 + +// +// Version of MPIO_DSM_Path that is currently supported by this DSM. +// +#define DSM_WMI_VERSION DSM_WMI_VERSION_2 + +// +// This struct is used to save Load Balance Policy Settings in the registry +// +typedef struct _DSM_LOAD_BALANCE_POLICY_SETTINGS { + + WCHAR RegistryKeyName[256]; + ULONG LoadBalancePolicy; + ULONG PathCount; + MPIO_DSM_Path_V2 DsmPath[1]; + +} DSM_LOAD_BALANCE_POLICY_SETTINGS, *PDSM_LOAD_BALANCE_POLICY_SETTINGS; + +// +// This structure is used to pass in information used by the workitem +// to failover reservations down another path. +// +typedef struct _DSM_RETRY_RESERVE { + + PDSM_COMPLETION_CONTEXT CompletionContext; + + PIRP Irp; + + PKEVENT Event; + +} DSM_RETRY_RESERVE, *PDSM_RETRY_RESERVE; + +// +// This structure defines the workitem that will be used to handle reservation +// failover. +// +typedef struct _DSM_WORKITEM { + + // + // Work item that should be freed by the worker routine + // + PIO_WORKITEM WorkItem; + + // + // Context to be passed to worker routine + // + PVOID Context; + +} DSM_WORKITEM, *PDSM_WORKITEM; + +#endif // _MSDSM_H + + diff --git a/storage/msdsm/src/msdsm.mof b/storage/msdsm/src/msdsm.mof new file mode 100644 index 000000000..53ee61392 --- /dev/null +++ b/storage/msdsm/src/msdsm.mof @@ -0,0 +1,82 @@ +// +// Copyright (C) 2004 Microsoft Corporation +// +// +// Microsoft DSM's internal classes +// + +// +// Perf class. +// +[WMI, + guid("{a34d03ec-6b0b-46a1-9178-82525f41133f}")] +class MSDSM_DEVICEPATH_PERF +{ + [WmiDataId(1), + Description("Path Identifier.") : amended + ] uint64 PathId; + + [WmiDataId(2), + Description("Number of Read Requests.") : amended + ] uint32 NumberReads; + + [WmiDataId(3), + Description("Number of Write Requests.") : amended + ] uint32 NumberWrites; + + [WmiDataId(4), + Description("Total Bytes Read.") : amended + ] uint64 BytesRead; + + [WmiDataId(5), + Description("Total Bytes Written.") : amended + ] uint64 BytesWritten; +}; + +[WMI, + Dynamic, + Provider("WmiProv"), + Description("Retrieve MSDSM Performance Information.") : amended, + Locale("MS\\0x409"), + guid("{875b8871-4889-4114-93f6-cd064c001cea}")] +class MSDSM_DEVICE_PERF +{ + [key, read] + string InstanceName; + [read] boolean Active; + + [WmiDataId(1), + read, + Description("Number of paths.") : amended + ] uint32 NumberPaths; + + [WmiDataId(2), + read, + Description("Array of Performance Information per path for the device.") : amended, + WmiSizeIs("NumberPaths") + ] MSDSM_DEVICEPATH_PERF PerfInfo[]; +}; + +// +// Methods +// Clear perf counters. +// +[Dynamic, + Provider("WMIProv"), + WMI, + Description("MSDSM WMI Methods") : amended, + guid("{04517f7e-92bb-4ebe-aed0-54339fa5f544}"), + locale("MS\\0x409") +] +class MSDSM_WMI_METHODS +{ + + [key, read] + string InstanceName; + [read] boolean Active; + + [WmiMethodId(1), + Implemented, + Description("Clear path performance counters for the device.") : amended + ] void MSDsmClearCounters(); +}; diff --git a/storage/msdsm/src/msdsm.rc b/storage/msdsm/src/msdsm.rc new file mode 100644 index 000000000..008001123 --- /dev/null +++ b/storage/msdsm/src/msdsm.rc @@ -0,0 +1,24 @@ +//+------------------------------------------------------------------------- +// +// Microsoft Windows +// +// Copyright (C) Microsoft Corporation, 2004 +// +// File: msdsm.rc +// +//-------------------------------------------------------------------------- + +#include + +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "Microsoft Device Specific Module" +#define VER_INTERNALNAME_STR "msdsm.sys" +#define VER_ORIGINALFILENAME_STR "msdsm.sys" + +#include "common.ver" + +MofResourceName MOFDATA msdsm.bmf +DsmMofResourceName MOFDATA msdsmdsm.bmf diff --git a/storage/msdsm/src/msdsmdsm.mof b/storage/msdsm/src/msdsmdsm.mof new file mode 100644 index 000000000..f85eea270 --- /dev/null +++ b/storage/msdsm/src/msdsmdsm.mof @@ -0,0 +1,141 @@ +// +// Copyright (C) 2004 Microsoft Corporation +// +// Microsoft DSM's DSM-specific classes +// + +// +// Class used for retrieving and setting MSDSM-wide default load balance policy. +// +[WMI, + Dynamic, + Provider("WmiProv"), + Description("MSDSM-wide default load balance policies.") : amended, + Locale("MS\\0x409"), + guid("{c81b5681-f3ca-4c98-9325-707d0d62ffc4}")] +class MSDSM_DEFAULT_LOAD_BALANCE_POLICY +{ + [key, read] + string InstanceName; + [read] boolean Active; + + [WmiDataId(1), + read, write, + Description("Load Balance Policy to be applied to devices controlled by MSDSM.") : amended + ] uint32 LoadBalancePolicy; + + [WmiDataId(2), + read, + Description("Reserved.") : amended + ] uint32 Reserved; + + // + // Preferred path. + // + [WmiDataId(3), + read, write, + Description("Preferred Path.") : amended + ] uint64 PreferredPath; +}; + +// +// Embedded class that describes a target and the default load balance policy +// of its LUNs. +// +[WMI, + guid("{ddb00a72-0fab-418b-a89e-97370ae293a4}")] +class MSDSM_TARGET_DEFAULT_POLICY_INFO +{ + // + // VID-PID string as an 8 + 16 character concatenated string. + // Spaces should be used to make the VID 8 chars and the PID 16 chars. + // + [WmiDataId(1), + MaxLen(31), + Description("Concatenated VendorID (8 characters) and ProductID (16 characters).") : amended + ] string HardwareId; + + // + // The default load balance policy to be applied to LUNs from the target + // whose hardware id matches the VID/PID above. + // NOTE: Setting this to 0 will act as removal of default setting for this + // target. + // + [WmiDataId(2)] uint32 LoadBalancePolicy; + + // + // Used for alignment reasons. + // + [WmiDataId(3)] uint32 Reserved; + + // + // Preferred path. + // + [WmiDataId(4)] uint64 PreferredPath; +}; + +// +// Class used for retrieving and setting target-level default load balance policy. +// +[WMI, + Dynamic, + Provider("WmiProv"), + Description("Target-level default load balance policies.") : amended, + Locale("MS\\0x409"), + guid("{5ccbcd91-1b56-4327-a2f3-0960335f8846}")] +class MSDSM_TARGETS_DEFAULT_LOAD_BALANCE_POLICY +{ + [key, read] + string InstanceName; + [read] boolean Active; + + [WmiDataId(1), + read, write, + Description("Number of targets specified.") : amended + ] uint32 NumberDevices; + + [WmiDataId(2), + read, + Description("Reserved.") : amended + ] uint32 Reserved; + + [WmiDataId(3), + read, write, + MaxLen(31), + Description("Array of target hardware identifiers with policy and preferred path information.") : amended, + WmiSizeIs("NumberDevices") + ] MSDSM_TARGET_DEFAULT_POLICY_INFO TargetDefaultPolicyInfo[]; +}; + +// +// Supported devices list class. +// +[WMI, + Dynamic, + Provider("WmiProv"), + Description("Retrieve MSDSM's supported devices list.") : amended, + Locale("MS\\0x409"), + guid("{c362d67c-371e-44d8-8bba-044619e4f245}")] +class MSDSM_SUPPORTED_DEVICES_LIST +{ + [key, read] + string InstanceName; + [read] boolean Active; + + [WmiDataId(1), + read, + Description("Number of supported devices.") : amended + ] uint32 NumberDevices; + + [WmiDataId(2), + read, + Description("Reserved.") : amended + ] uint32 Reserved; + + [WmiDataId(3), + read, + MaxLen(31), + Description("Array of device hardware identifiers.") : amended, + WmiSizeIs("NumberDevices") + ] string DeviceId[]; +}; diff --git a/storage/msdsm/src/precomp.h b/storage/msdsm/src/precomp.h new file mode 100644 index 000000000..de4f4e1fa --- /dev/null +++ b/storage/msdsm/src/precomp.h @@ -0,0 +1,32 @@ + +/*++ + +Copyright (c) 2004 Microsoft Corporation + +Module Name: + + precomp.h + +Abstract: + + Precompiled header file for Microsoft Device Specific Module (DSM). + +Revision History: + +--*/ + +#pragma once + +#include +#include + +#include "dsm.h" +#include "mpiodisk.h" +#include "msdsm.h" +#include "prototypes.h" +#include "trace.h" +#include "srbhelper.h" + +#include +#include + diff --git a/storage/msdsm/src/precompsrc.c b/storage/msdsm/src/precompsrc.c new file mode 100644 index 000000000..5944cf515 --- /dev/null +++ b/storage/msdsm/src/precompsrc.c @@ -0,0 +1 @@ +#include "precomp.h" \ No newline at end of file diff --git a/storage/msdsm/src/prototypes.h b/storage/msdsm/src/prototypes.h new file mode 100644 index 000000000..1df581975 --- /dev/null +++ b/storage/msdsm/src/prototypes.h @@ -0,0 +1,1436 @@ + +/*++ + +Copyright (C) 2004 Microsoft Corporation + +Module Name: + + prototypes.h + +Abstract: + + Contains function prototypes for all the functions defined + by Microsoft Device Specific Module (DSM). + +Environment: + + kernel mode only + +Notes: + +--*/ + +#pragma warning (disable:4214) // bit field usage +#pragma warning (disable:4200) // zero-sized array + +#ifndef _PROTOTYPES_H_ +#define _PROTOTYPES_H_ + +#define DSM_VENDOR_ID_LEN 8 +#define DSM_PRODUCT_ID_LEN 16 +#define DSM_VENDPROD_ID_LEN 24 + +// +// In accordance with SPC-3 specs +// +#define SPC3_TARGET_PORT_GROUPS_HEADER_SIZE 4 + +typedef struct _SPC3_CDB_REPORT_TARGET_PORT_GROUPS { + UCHAR OperationCode; + UCHAR ServiceAction : 5; + UCHAR Reserved1 : 3; + UCHAR Reserved2[4]; + UCHAR AllocationLength[4]; + UCHAR Reserved3; + UCHAR Control; +} SPC3_CDB_REPORT_TARGET_PORT_GROUPS, *PSPC3_CDB_REPORT_TARGET_PORT_GROUPS; + +typedef struct _SPC3_REPORT_TARGET_PORT_GROUP_DESCRIPTOR { + UCHAR AsymmetricAccessState : 4; + UCHAR Reserved : 3; + UCHAR Preferred : 1; + UCHAR ActiveOptimizedSupported : 1; + UCHAR ActiveUnoptimizedSupported : 1; + UCHAR StandbySupported : 1; + UCHAR UnavailableSupported : 1; + UCHAR Reserved2 : 3; + UCHAR TransitioningSupported : 1; + USHORT TPG_Identifier; + UCHAR Reserved3; + UCHAR StatusCode; + UCHAR VendorUnique; + UCHAR NumberTargetPorts; + ULONG TargetPortIds[0]; +} SPC3_REPORT_TARGET_PORT_GROUP_DESCRIPTOR, *PSPC3_REPORT_TARGET_PORT_GROUP_DESCRIPTOR; + +typedef struct _SPC3_CDB_SET_TARGET_PORT_GROUPS { + UCHAR OperationCode; + UCHAR ServiceAction : 5; + UCHAR Reserved1 : 3; + UCHAR Reserved2[4]; + UCHAR ParameterListLength[4]; + UCHAR Reserved3; + UCHAR Control; +} SPC3_CDB_SET_TARGET_PORT_GROUPS, *PSPC3_CDB_SET_TARGET_PORT_GROUPS; + +typedef struct _SPC3_SET_TARGET_PORT_GROUP_DESCRIPTOR { + UCHAR AsymmetricAccessState : 4; + UCHAR Reserved1 : 4; + UCHAR Reserved2; + USHORT TPG_Identifier; +} SPC3_SET_TARGET_PORT_GROUP_DESCRIPTOR, *PSPC3_SET_TARGET_PORT_GROUP_DESCRIPTOR; + +typedef struct _SPC3_CONTROL_EXTENSION_MODE_PAGE { + UCHAR PageCode : 6; + UCHAR SubpageFormat : 1; + UCHAR ParametersSavable : 1; + UCHAR SubpageCode; + UCHAR PageLength[2]; + UCHAR ImplicitALUAEnable : 1; + UCHAR ScsiPrecendence : 1; + UCHAR TimestampChangeable : 1; + UCHAR Reserved1 : 5; + UCHAR InitialPriority : 4; + UCHAR Reserved2 : 4; + UCHAR Reserved3[26]; +} SPC3_CONTROL_EXTENSION_MODE_PAGE, *PSPC3_CONTROL_EXTENSION_MODE_PAGE; + +#define SPC3_SCSIOP_REPORT_TARGET_PORT_GROUPS 0xA3 +#define SPC3_SCSIOP_SET_TARGET_PORT_GROUPS 0xA4 +#define SPC3_SERVICE_ACTION_TARGET_PORT_GROUPS 0xA +#define SPC3_RESERVATION_ACTION_REPORT_CAPABILITIES 0x2 + +#define SPC3_SCSI_ADSENSE_COMMANDS_CLEARED_BY_ANOTHER_INITIATOR 0x2F +#define SPC3_SCSI_ADSENSE_LOGICAL_UNIT_COMMAND_FAILED 0x67 + +#define SPC3_SCSI_SENSEQ_MODE_PARAMETERS_CHANGED 0x1 +#define SPC3_SCSI_SENSEQ_RESERVATIONS_PREEMPTED 0x3 +#define SPC3_SCSI_SENSEQ_RESERVATIONS_RELEASED 0x4 +#define SPC3_SCSI_SENSEQ_REGISTRATIONS_PREEMPTED 0x5 +#define SPC3_SCSI_SENSEQ_ASYMMETRIC_ACCESS_STATE_CHANGED 0x6 +#define SPC3_SCSI_SENSEQ_IMPLICIT_ASYMMETRIC_ACCESS_STATE_TRANSITION_FAILED 0x7 +#define SPC3_SCSI_SENSEQ_CAPACITY_DATA_HAS_CHANGED 0x9 +#define SPC3_SCSI_SENSEQ_ASYMMETRIC_ACCESS_STATE_TRANSITION 0xA +#define SPC3_SCSI_SENSEQ_TARGET_PORT_IN_STANDBY_STATE 0xB +#define SPC3_SCSI_SENSEQ_TARGET_PORT_IN_UNAVAILABLE_STATE 0xC + +#define SPC3_SCSI_SENSEQ_SET_TARGET_PORT_GROUPS_FAILED 0xA + +#define SPC3_SET_TARGET_PORT_GROUPS_TIMEOUT 10 +#define SPC3_REPORT_TARGET_PORT_GROUPS_TIMEOUT 10 + + +// +// Function prototypes for functions intrface.c +// + +DRIVER_INITIALIZE DriverEntry; +DRIVER_UNLOAD DsmDriverUnload; + +NTSTATUS +DsmInquire ( + _In_ IN PVOID DsmContext, + _In_ IN PDEVICE_OBJECT TargetDevice, + _In_ IN PDEVICE_OBJECT PortObject, + _In_ IN PSTORAGE_DEVICE_DESCRIPTOR Descriptor, + _In_ IN PSTORAGE_DEVICE_ID_DESCRIPTOR DeviceIdList, + _Out_ OUT PVOID *DsmIdentifier + ); + +BOOLEAN +DsmCompareDevices( + _In_ IN PVOID DsmContext, + _In_ IN PVOID DsmId1, + _In_ IN PVOID DsmId2 + ); + +NTSTATUS +DsmGetControllerInfo( + _In_ IN PVOID DsmContext, + _In_ IN PVOID DsmId, + _In_ IN ULONG Flags, + _Inout_ IN OUT PCONTROLLER_INFO *ControllerInfo + ); + +NTSTATUS +DsmSetDeviceInfo( + _In_ IN PVOID DsmContext, + _In_ IN PDEVICE_OBJECT TargetObject, + _In_ IN PVOID DsmId, + _Inout_ IN OUT PVOID *PathId + ); + +BOOLEAN +DsmIsPathActive( + _In_ IN PVOID DsmContext, + _In_ IN PVOID PathId, + _In_ IN PVOID DsmId + ); + +NTSTATUS +DsmPathVerify( + _In_ IN PVOID DsmContext, + _In_ IN PVOID DsmId, + _In_ IN PVOID PathId + ); + +NTSTATUS +DsmInvalidatePath( + _In_ IN PVOID DsmContext, + _In_ IN ULONG ErrorMask, + _In_ IN PVOID PathId, + _Inout_ IN OUT PVOID *NewPathId + ); + +NTSTATUS +DsmMoveDevice( + _In_ IN PVOID DsmContext, + _In_ IN PDSM_IDS DsmIds, + _In_ IN PVOID MPIOPath, + _In_ IN PVOID SuggestedPath, + _In_ IN ULONG Flags + ); + +NTSTATUS +DsmRemovePending( + _In_ IN PVOID DsmContext, + _In_ IN PVOID DsmId + ); + +NTSTATUS +DsmRemoveDevice( + _In_ IN PVOID DsmContext, + _In_ IN PVOID DsmId, + _In_ IN PVOID PathId + ); + +NTSTATUS +DsmRemovePath( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PVOID PathId + ); + +NTSTATUS +DsmSrbDeviceControl( + _In_ IN PVOID DsmContext, + _In_ IN PDSM_IDS DsmIds, + _In_ IN PIRP Irp, + _In_ IN PSCSI_REQUEST_BLOCK Srb, + _In_ IN PKEVENT Event + ); + +PVOID +DsmLBGetPath( + _In_ IN PVOID DsmContext, + _In_ IN PSCSI_REQUEST_BLOCK Srb, + _In_ IN PDSM_IDS DsmList, + _In_ IN PVOID CurrentPath, + _Out_ OUT NTSTATUS *Status + ); + +ULONG +DsmInterpretError( + _In_ IN PVOID DsmContext, + _In_ IN PVOID DsmId, + _In_ IN PSCSI_REQUEST_BLOCK Srb, + _Inout_ IN OUT NTSTATUS *Status, + _Out_ OUT PBOOLEAN Retry, + _Out_ OUT PLONG RetryInterval, + ... + ); + +NTSTATUS +DsmUnload( + _In_ IN PVOID DsmContext + ); + +VOID +DsmSetCompletion( + _In_ IN PVOID DsmContext, + _In_ IN PVOID DsmId, + _In_ IN PIRP Irp, + _In_ IN PSCSI_REQUEST_BLOCK Srb, + _Inout_ IN OUT PDSM_COMPLETION_INFO DsmCompletion + ); + +_Success_(return == DSM_PATH_SET) +ULONG +DsmCategorizeRequest( + _In_ IN PVOID DsmContext, + _In_ IN PDSM_IDS DsmIds, + _In_ IN PIRP Irp, + _In_ IN PSCSI_REQUEST_BLOCK Srb, + _In_ IN PVOID CurrentPath, + _Outptr_result_maybenull_ OUT PVOID *PathId, + _Out_ OUT NTSTATUS *Status + ); + +NTSTATUS +DsmBroadcastRequest( + _In_ IN PVOID DsmContext, + _In_ IN PDSM_IDS DsmIds, + _In_ IN PIRP Irp, + _In_ IN PSCSI_REQUEST_BLOCK Srb, + _In_ IN PKEVENT Event + ); + +BOOLEAN +DsmIsAddressTypeSupported( + _In_ IN PVOID DsmContext, + _In_ IN ULONG AddressType + ); + +NTSTATUS +DsmDeviceNotUsed( + _In_ IN PVOID DsmContext, + _In_ IN PVOID DsmId + ); + + +// +// Function prototypes for functions in dsmmain.c +// + +VOID +DsmpFreeDSMResources( + _In_ IN PDSM_CONTEXT DsmContext + ); + +PDSM_GROUP_ENTRY +DsmpFindDevice( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_DEVICE_INFO DeviceInfo, + _In_ IN BOOLEAN AcquireDSMLockExclusive + ); + +PDSM_GROUP_ENTRY +DsmpBuildGroupEntry( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_DEVICE_INFO DeviceInfo + ); + +NTSTATUS +DsmpParseTargetPortGroupsInformation( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_GROUP_ENTRY Group, + _In_reads_bytes_(TargetPortGroupsInfoLength) IN PUCHAR TargetPortGroupsInfo, + _In_ IN ULONG TargetPortGroupsInfoLength + ); + +PDSM_TARGET_PORT_GROUP_ENTRY +DsmpFindTargetPortGroupEntry( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_GROUP_ENTRY Group, + _In_reads_bytes_(TPGs_BufferLength) IN PUCHAR TargetPortGroupsDescriptor, + _In_ IN ULONG TPGs_BufferLength + ); + +_Success_(return!=0) +PDSM_TARGET_PORT_GROUP_ENTRY +DsmpUpdateTargetPortGroupEntry( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_TARGET_PORT_GROUP_ENTRY TargetPortGroup, + _In_reads_bytes_(TPGs_BufferLength) IN PUCHAR TargetPortGroupsDescriptor, + _In_ IN ULONG TPGs_BufferLength, + _Out_ OUT PULONG DescriptorSize + ); + +PDSM_TARGET_PORT_GROUP_ENTRY +DsmpBuildTargetPortGroupEntry( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_GROUP_ENTRY Group, + _In_reads_bytes_(TPGs_BufferLength) IN PUCHAR TargetPortGroupsDescriptor, + _In_ IN ULONG TPGs_BufferLength, + _Out_ OUT PULONG DescriptorSize + ); + +PDSM_TARGET_PORT_LIST_ENTRY +DsmpFindTargetPortListEntry( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_TARGET_PORT_GROUP_ENTRY TargetPortGroup, + _In_ IN ULONG RelativeTargetPortId + ); + +PDSM_TARGET_PORT_LIST_ENTRY +DsmpBuildTargetPortListEntry( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_TARGET_PORT_GROUP_ENTRY TargetPortGroup, + _In_ IN ULONG RelativeTargetPortId + ); + +PDSM_TARGET_PORT_GROUP_ENTRY +DsmpFindTargetPortGroup( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_GROUP_ENTRY Group, + _In_ IN PUSHORT TargetPortGroupId + ); + +PDSM_TARGET_PORT_LIST_ENTRY +DsmpFindTargetPort( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_TARGET_PORT_GROUP_ENTRY TargetPortGroup, + _In_ IN PULONG TargetPortGroupId + ); + +NTSTATUS +DsmpAddDeviceEntry( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_GROUP_ENTRY Group, + _In_ IN PDSM_DEVICE_INFO DeviceInfo + ); + +PDSM_CONTROLLER_LIST_ENTRY +DsmpFindControllerEntry( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDEVICE_OBJECT PortObject, + _In_ IN PSCSI_ADDRESS ScsiAddress, + _In_reads_(ControllerSerialNumberLength) IN PSTR ControllerSerialNumber, + _In_ IN SIZE_T ControllerSerialNumberLength, + _In_ IN STORAGE_IDENTIFIER_CODE_SET CodeSet, + _In_ IN BOOLEAN AcquireLock + ); + +_Ret_maybenull_ +_Must_inspect_result_ +_When_(return != NULL, __drv_allocatesMem(Mem)) +PDSM_CONTROLLER_LIST_ENTRY +DsmpBuildControllerEntry( + _In_ IN PDSM_CONTEXT DsmContext, + _In_opt_ IN PDEVICE_OBJECT DeviceObject, + _In_ IN PDEVICE_OBJECT PortObject, + _In_ IN PSCSI_ADDRESS ScsiAddress, + _In_ IN PSTR ControllerSerialNumber, + _In_ IN STORAGE_IDENTIFIER_CODE_SET CodeSet, + _In_ IN BOOLEAN AcquireLock + ); + +VOID +DsmpFreeControllerEntry( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ __drv_freesMem(Mem) IN PDSM_CONTROLLER_LIST_ENTRY ControllerEntry + ); + +BOOLEAN +DsmpIsDeviceBelongsToController( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_DEVICE_INFO DeviceInfo, + _In_ IN PDSM_CONTROLLER_LIST_ENTRY ControllerEntry + ); + +PDSM_DEVICE_INFO +DsmpFindDevInfoFromGroupAndFOGroup( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_GROUP_ENTRY Group, + _In_ IN PDSM_FAILOVER_GROUP FOGroup + ); + +PDSM_FAILOVER_GROUP +DsmpFindFOGroup( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PVOID PathId + ); + +PDSM_FAILOVER_GROUP +DsmpBuildFOGroup( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_DEVICE_INFO DeviceInfo, + _In_ IN PVOID *PathId + ); + +NTSTATUS +DsmpUpdateFOGroup( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_FAILOVER_GROUP FailGroup, + _In_ IN PDSM_DEVICE_INFO DeviceInfo + ); + +VOID +DsmpRemoveDeviceFailGroup( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_FAILOVER_GROUP FailGroup, + _In_ IN PDSM_DEVICE_INFO DeviceInfo, + _In_ IN BOOLEAN AcquireDSMLockExclusive + ); + +ULONG +DsmpRemoveDeviceEntry( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_GROUP_ENTRY Group, + _In_ IN PDSM_DEVICE_INFO DeviceInfo + ); + +VOID +DsmpRemoveDeviceFromTargetPortList( + _In_ IN PDSM_DEVICE_INFO DeviceInfo + ); + +PDSM_FAILOVER_GROUP +DsmpSetNewPath( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_DEVICE_INFO FailingDevice + ); + +PDSM_FAILOVER_GROUP +DsmpSetNewPathUsingGroup( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_GROUP_ENTRY Group + ); + +VOID +DsmpRemoveZombieGroupEntry( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_GROUP_ENTRY ZombieGroup + ); + +NTSTATUS +DsmpUpdateTargetPortGroupDevicesStates( + _In_ IN PDSM_TARGET_PORT_GROUP_ENTRY TargetPortGroup, + _In_ IN DSM_DEVICE_STATE NewState + ); + +VOID +DsmpIncrementCounters( + _In_ PDSM_FAILOVER_GROUP FailGroup, + _In_ PSCSI_REQUEST_BLOCK Srb + ); + +BOOLEAN +DsmpDecrementCounters( + _In_ PDSM_FAILOVER_GROUP FailGroup, + _In_ PSCSI_REQUEST_BLOCK Srb + ); + +PDSM_FAILOVER_GROUP +DsmpGetPath( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_IDS DsmList, + _In_ IN PSCSI_REQUEST_BLOCK Srb, + _In_ IN ULONG SpecialHandlingFlag + ); + +PVOID +DsmpGetPathIdFromPassThroughPath( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_IDS DsmList, + _In_ IN PIRP Irp, + _Inout_ IN OUT NTSTATUS *Status + ); + +VOID +DsmpRemoveGroupEntry( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_GROUP_ENTRY GroupEntry, + _In_ IN BOOLEAN AcquireDSMLockExclusive + ); + +BOOLEAN +DsmpMpioPassThroughPathCommand( + _In_ IN PIRP Irp + ); + +BOOLEAN +DsmpReservationCommand( + _In_ IN PIRP Irp, + _In_ IN PSCSI_REQUEST_BLOCK Srb + ); + +VOID +DsmpRequestComplete( + _In_ IN PVOID DsmId, + _In_ IN PIRP Irp, + _In_ IN PSCSI_REQUEST_BLOCK Srb, + _In_ IN PVOID DsmContext + ); + +NTSTATUS +DsmpRegisterPersistentReservationKeys( + _In_ IN PDSM_DEVICE_INFO DeviceInfo, + _In_ IN BOOLEAN Register + ); + + +BOOLEAN +DsmpShouldRetryPassThroughRequest( + _In_ IN PVOID SenseData, + _In_ IN UCHAR SenseDataSize + ); + +BOOLEAN +DsmpShouldRetryPersistentReserveCommand( + _In_ IN PVOID SenseData, + _In_ IN UCHAR SenseDataSize + ); + +BOOLEAN +DsmpShouldRetryTPGRequest( + _In_ IN PVOID SenseData, + _In_ IN UCHAR SenseDataSize + ); + +BOOLEAN +DsmpIsDeviceRemoved( + _In_ IN PVOID SenseData, + _In_ IN UCHAR SenseDataSize + ); + +PDSM_DEVICE_INFO +DsmpGetActivePathToBeUsed( + _In_ PDSM_GROUP_ENTRY Group, + _In_ BOOLEAN Symmetric, + _In_ IN ULONG SpecialHandlingFlag + ); + +PDSM_DEVICE_INFO +DsmpGetAnyActivePath( + _In_ PDSM_GROUP_ENTRY Group, + _In_ BOOLEAN Exception, + _In_opt_ PDSM_DEVICE_INFO DeviceInfo, + _In_ IN ULONG SpecialHandlingFlag + ); + +PDSM_DEVICE_INFO +DsmpFindStandbyPathToActivate( + _In_ IN PDSM_GROUP_ENTRY Group, + _In_ IN ULONG SpecialHandlingFlag + ); + +PDSM_DEVICE_INFO +DsmpFindStandbyPathToActivateALUA( + _In_ IN PDSM_GROUP_ENTRY Group, + _In_ IN PBOOLEAN SendTPG, + _In_ IN ULONG SpecialHandlingFlag + ); + +PDSM_DEVICE_INFO +DsmpFindStandbyPathInAlternateTpgALUA( + _In_ IN PDSM_GROUP_ENTRY Group, + _In_ IN PDSM_DEVICE_INFO DeviceInfo, + _In_ IN ULONG SpecialHandlingFlag + ); + +NTSTATUS +DsmpSetLBForDsmPolicyAdjustment( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN DSM_LOAD_BALANCE_TYPE LoadBalanceType, + _In_ IN ULONGLONG PreferredPath + ); + +NTSTATUS +DsmpSetLBForVidPidPolicyAdjustment( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PWSTR TargetHardwareId, + _In_ IN DSM_LOAD_BALANCE_TYPE LoadBalanceType, + _In_ IN ULONGLONG PreferredPath + ); + +NTSTATUS +DsmpSetNewDefaultLBPolicy( + _In_ IN PDSM_CONTEXT DsmContext, + _In_opt_ IN PDSM_DEVICE_INFO NewDeviceInfo, + _In_ IN DSM_LOAD_BALANCE_TYPE LoadBalanceType, + _In_ IN ULONG SpecialHandlingFlag + ); + +NTSTATUS +DsmpSetLBForPathArrival( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_DEVICE_INFO NewDeviceInfo, + _In_ IN ULONG SpecialHandlingFlag + ); + +NTSTATUS +DsmpSetLBForPathArrivalALUA( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_DEVICE_INFO NewDeviceInfo, + _In_ IN ULONG SpecialHandlingFlag + ); + +NTSTATUS +DsmpSetLBForPathRemoval( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_DEVICE_INFO RemovedDeviceInfo, + _In_opt_ IN OPTIONAL PDSM_GROUP_ENTRY Group, + _In_ IN ULONG SpecialHandlingFlag + ); + +NTSTATUS +DsmpSetLBForPathRemovalALUA( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_DEVICE_INFO RemovedDeviceInfo, + _In_opt_ IN OPTIONAL PDSM_GROUP_ENTRY Group, + _In_ IN ULONG SpecialHandlingFlag + ); + +NTSTATUS +DsmpSetLBForPathFailing( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_DEVICE_INFO FailingDeviceInfo, + _In_ IN BOOLEAN MarkDevInfoFailed, + _In_ IN ULONG SpecialHandlingFlag + ); + +NTSTATUS +DsmpSetLBForPathFailingALUA( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_DEVICE_INFO FailingDeviceInfo, + _In_ IN BOOLEAN MarkDevInfoFailed, + _In_ IN ULONG SpecialHandlingFlag + ); + +NTSTATUS +DsmpSetPathForIoRetryALUA( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_DEVICE_INFO FailingDeviceInfo, + _In_ IN BOOLEAN TPGException, + _In_ IN BOOLEAN DeviceInfoException + ); + +PDSM_FAIL_PATH_PROCESSING_LIST_ENTRY +DsmpFindFailPathDevInfoEntry( + _In_ IN PDSM_CONTEXT Context, + _In_ IN PDSM_GROUP_ENTRY Group, + _In_ IN PDSM_DEVICE_INFO FailingDevInfo + ); + +PDSM_FAIL_PATH_PROCESSING_LIST_ENTRY +DsmpBuildFailPathDevInfoEntry( + _In_ IN PDSM_CONTEXT Context, + _In_ IN PDSM_GROUP_ENTRY Group, + _In_ IN PDSM_DEVICE_INFO FailingDevInfo, + _In_ IN PDSM_DEVICE_INFO AlternateDevInfo + ); + +IO_COMPLETION_ROUTINE DsmpPhase1ProcessPathFailingALUA; + +NTSTATUS +DsmpRemoveFailPathDevInfoEntry( + _In_ IN PDSM_CONTEXT Context, + _In_ IN PDSM_GROUP_ENTRY Group, + _In_ IN PDSM_FAIL_PATH_PROCESSING_LIST_ENTRY FailPathDevInfoEntry + ); + +IO_COMPLETION_ROUTINE DsmpPhase2ProcessPathFailingALUA; + +NTSTATUS +DsmpPersistentReserveOut( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_IDS DsmIds, + _In_ IN PIRP Irp, + _In_ IN PSCSI_REQUEST_BLOCK Srb, + _In_ IN PKEVENT Event + ); + +__inline +BOOLEAN +DsmpIsPersistentReservationKeyZeroKey( + _In_ ULONG KeyLength, + _In_reads_bytes_(KeyLength) PUCHAR Key + ) +{ + BOOLEAN zeroKey = FALSE; + + NT_ASSERT(KeyLength == 8); + + if ((KeyLength) == 8 && + (Key[0] == 0 && Key[1] == 0 && Key[2] == 0 && Key[3] == 0 && + Key[4] == 0 && Key[5] == 0 && Key[6] == 0 && Key[7] == 0)) { + + zeroKey = TRUE; + } + + return zeroKey; +} + + +NTSTATUS +DsmpPersistentReserveIn( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_IDS DsmIds, + _In_ IN PIRP Irp, + _In_ IN PSCSI_REQUEST_BLOCK Srb, + _In_ IN PKEVENT Event + ); + +IO_COMPLETION_ROUTINE DsmpPersistentReserveCompletion; + + +// +// Function prototypes for functions in utils.c +// + +_Success_(return != NULL) +__drv_allocatesMem(Mem) +_When_(((PoolType&0x1))!=0, _IRQL_requires_max_(APC_LEVEL)) +_When_(((PoolType&0x1))==0, _IRQL_requires_max_(DISPATCH_LEVEL)) +_When_(((PoolType&0x2))!=0, + __drv_reportError("Must succeed pool allocations are forbidden. " + "Allocation failures cause a system crash")) +_When_(((PoolType&(0x2|POOL_RAISE_IF_ALLOCATION_FAILURE)))==0, + _Post_maybenull_ _Must_inspect_result_) +_When_(((PoolType&(0x2|POOL_RAISE_IF_ALLOCATION_FAILURE)))!=0, + _Post_notnull_) +_When_((PoolType&NonPagedPoolMustSucceed)!=0, + __drv_reportError("Must succeed pool allocations are forbidden. " + "Allocation failures cause a system crash")) +_Post_writable_byte_size_(NumberOfBytes) +PVOID +DsmpAllocatePool( + _In_ _Strict_type_match_ IN POOL_TYPE PoolType, + _In_ IN SIZE_T NumberOfBytes, + _In_ IN ULONG Tag + ); + +_Success_(return != NULL) +_Post_maybenull_ +_Must_inspect_result_ +__drv_allocatesMem(Mem) +_Post_writable_byte_size_(*BytesAllocated) +_When_(((PoolType&0x1))!=0, _IRQL_requires_max_(APC_LEVEL)) +_When_(((PoolType&0x1))==0, _IRQL_requires_max_(DISPATCH_LEVEL)) +_When_((PoolType&NonPagedPoolMustSucceed)!=0, + __drv_reportError("Must succeed pool allocations are forbidden. " + "Allocation failures cause a system crash")) +PVOID +DsmpAllocateAlignedPool( + _In_ IN POOL_TYPE PoolType, + _In_ IN SIZE_T NumberOfBytes, + _In_ IN ULONG AlignmentMask, + _In_ IN ULONG Tag, + _Out_ OUT SIZE_T *BytesAllocated + ); + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +DsmpFreePool( + _In_opt_ __drv_freesMem(Mem) IN PVOID Block + ); + +NTSTATUS +DsmpGetStatsGatheringChoice( + _In_ IN PDSM_CONTEXT Context, + _Out_ OUT PULONG StatsGatherChoice + ); + +NTSTATUS +DsmpSetStatsGatheringChoice( + _In_ IN PDSM_CONTEXT Context, + _In_ IN ULONG StatsGatherChoice + ); + + +NTSTATUS +DsmpGetDeviceList( + _In_ IN PDSM_CONTEXT Context + ); + +_Success_(return==0) +NTSTATUS +DsmpGetStandardInquiryData( + _In_ IN PDEVICE_OBJECT DeviceObject, + _Out_ OUT PINQUIRYDATA InquiryData + ); + +BOOLEAN +DsmpCheckScsiCompliance( + _In_ IN PDEVICE_OBJECT DeviceObject, + _In_ IN PINQUIRYDATA InquiryData, + _In_ IN PSTORAGE_DEVICE_DESCRIPTOR Descriptor, + _In_ IN PSTORAGE_DEVICE_ID_DESCRIPTOR DeviceIdList + ); + +BOOLEAN +DsmpDeviceSupported( + _In_ IN PDSM_CONTEXT Context, + _In_ IN PCSTR VendorId, + _In_ IN PCSTR ProductId + ); + +BOOLEAN +DsmpFindSupportedDevice( + _In_ IN PUNICODE_STRING DeviceName, + _In_ IN PUNICODE_STRING SupportedDevices + ); + +_Success_(return!=0) +PVOID +DsmpParseDeviceID ( + _In_ IN PSTORAGE_DEVICE_ID_DESCRIPTOR DeviceID, + _In_ IN DSM_DEVID_TYPE DeviceIdType, + _In_opt_ IN PULONG IdNumber, + _Out_opt_ PSTORAGE_IDENTIFIER_CODE_SET CodeSet, + _In_ IN BOOLEAN Legacy + ); + +PUCHAR +DsmpBinaryToAscii( + _In_reads_(Length) IN PUCHAR HexBuffer, + _In_ IN ULONG Length, + _Inout_ IN OUT PULONG UpdateLength, + _In_ IN BOOLEAN Legacy + ); + +PSTR +DsmpGetSerialNumber( + _In_ IN PDEVICE_OBJECT DeviceObject + ); + + +NTSTATUS +DsmpDisableImplicitStateTransition( + _In_ IN PDEVICE_OBJECT DeviceObject, + _Out_ OUT PBOOLEAN DisableImplicit + ); + +PWSTR +DsmpBuildHardwareId( + _In_ IN PDSM_DEVICE_INFO DeviceInfo + ); + +PWSTR +DsmpBuildDeviceNameLegacyPage0x80( + _In_ IN PDSM_DEVICE_INFO DeviceInfo + ); + + +PWSTR +DsmpBuildDeviceName( + _In_ IN PDSM_DEVICE_INFO DeviceInfo, + _In_reads_(SerialNumberLength) IN PSTR SerialNumber, + _In_ IN SIZE_T SerialNumberLength + ); + +NTSTATUS +DsmpApplyDeviceNameCorrection( + _In_ IN PDSM_DEVICE_INFO DeviceInfo, + _In_reads_(DeviceNameLegacyLen) PWSTR DeviceNameLegacy, + _In_ IN SIZE_T DeviceNameLegacyLen, + _In_reads_(DeviceNameLen) PWSTR DeviceName, + _In_ IN SIZE_T DeviceNameLen + ); + +NTSTATUS +DsmpQueryDeviceLBPolicyFromRegistry( + _In_ PDSM_DEVICE_INFO DeviceInfo, + _In_ PWSTR RegistryKeyName, + _Inout_ PDSM_LOAD_BALANCE_TYPE LoadBalanceType, + _Inout_ PULONGLONG PreferredPath, + _Inout_ PUCHAR ExplicitlySet + ); + +NTSTATUS +DsmpQueryTargetLBPolicyFromRegistry( + _In_ IN PDSM_DEVICE_INFO DeviceInfo, + _Out_ OUT PDSM_LOAD_BALANCE_TYPE LoadBalanceType, + _Out_ OUT PULONGLONG PreferredPath + ); + +NTSTATUS +DsmpQueryDsmLBPolicyFromRegistry( + _Out_ OUT PDSM_LOAD_BALANCE_TYPE LoadBalanceType, + _Out_ OUT PULONGLONG PreferredPath + ); + +NTSTATUS +DsmpSetDsmLBPolicyInRegistry( + _In_ IN DSM_LOAD_BALANCE_TYPE LoadBalanceType, + _In_ IN ULONGLONG PreferredPath + ); + +NTSTATUS +DsmpSetVidPidLBPolicyInRegistry( + _In_ IN PWSTR TargetHardwareId, + _In_ IN DSM_LOAD_BALANCE_TYPE LoadBalanceType, + _In_ IN ULONGLONG PreferredPath + ); + +NTSTATUS +DsmpOpenLoadBalanceSettingsKey( + _In_ IN ACCESS_MASK AccessMask, + _Out_ OUT PHANDLE LoadBalanceSettingsKey + ); + +NTSTATUS +DsmpOpenTargetsLoadBalanceSettingKey( + _In_ IN ACCESS_MASK AccessMask, + _Out_ OUT PHANDLE TargetsLoadBalanceSettingKey + ); + +NTSTATUS +DsmpOpenDsmServicesParametersKey( + _In_ IN ACCESS_MASK AccessMask, + _Out_ OUT PHANDLE ParametersSettingsKey + ); + +IO_COMPLETION_ROUTINE DsmpReportTargetPortGroupsSyncCompletion; + +_Success_(return==0) +NTSTATUS +DsmpReportTargetPortGroups( + _In_ PDEVICE_OBJECT DeviceObject, + _Outptr_result_buffer_maybenull_(*TargetPortGroupsInfoLength) PUCHAR *TargetPortGroupsInfo, + _Out_ PULONG TargetPortGroupsInfoLength + ); + +NTSTATUS +DsmpReportTargetPortGroupsAsync( + _In_ IN PDSM_DEVICE_INFO DeviceInfo, + _In_ IN PIO_COMPLETION_ROUTINE CompletionRoutine, + _Inout_ __drv_aliasesMem IN PDSM_TPG_COMPLETION_CONTEXT CompletionContext, + _In_ IN ULONG TargetPortGroupsInfoLength, + _Inout_ __drv_aliasesMem IN OUT PUCHAR TargetPortGroupsInfo + ); + +NTSTATUS +DsmpQueryLBPolicyForDevice( + _In_ IN PWSTR RegistryKeyName, + _In_ IN ULONGLONG PathId, + _In_ IN DSM_LOAD_BALANCE_TYPE LoadBalanceType, + _Out_ OUT PULONG PrimaryPath, + _Out_ OUT PULONG OptimizedPath, + _Out_ OUT PULONG PathWeight + ); + +VOID +DsmpGetDSMPathKeyName( + _In_ ULONGLONG DSMPathId, + _Out_writes_(DsmPathKeyNameSize) PWCHAR DsmPathKeyName, + _In_ ULONG DsmPathKeyNameSize + ); + +UCHAR +DsmpGetAsciiForBinary( + _In_ UCHAR BinaryChar + ); + +NTSTATUS +DsmpGetDeviceIdList ( + _In_ IN PDEVICE_OBJECT DeviceObject, + _Out_ OUT PSTORAGE_DESCRIPTOR_HEADER *Descriptor + ); + +NTSTATUS +DsmpSetTargetPortGroups( + _In_ IN PDEVICE_OBJECT DeviceObject, + _In_reads_bytes_(TargetPortGroupsInfoLength) IN PUCHAR TargetPortGroupsInfo, + _In_ IN ULONG TargetPortGroupsInfoLength + ); + +NTSTATUS +DsmpSetTargetPortGroupsAsync( + _In_ IN PDSM_DEVICE_INFO DeviceInfo, + _In_ IN PIO_COMPLETION_ROUTINE CompletionRoutine, + _In_ __drv_aliasesMem IN PDSM_TPG_COMPLETION_CONTEXT CompletionContext, + _In_ IN ULONG TargetPortGroupsInfoLength, + _In_ __drv_aliasesMem IN PUCHAR TargetPortGroupsInfo + ); + +PDSM_LOAD_BALANCE_POLICY_SETTINGS +DsmpCopyLoadBalancePolicies( + _In_ IN PDSM_GROUP_ENTRY GroupEntry, + _In_ IN ULONG DsmWmiVersion, + _In_ IN PVOID SupportedLBPolicies + ); + +NTSTATUS +DsmpPersistLBSettings( + _In_ IN PDSM_LOAD_BALANCE_POLICY_SETTINGS LoadBalanceSettings + ); + +NTSTATUS +DsmpSetDeviceALUAState( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_DEVICE_INFO DeviceInfo, + _In_ IN DSM_DEVICE_STATE DevState + ); + +NTSTATUS +DsmpGetDeviceALUAState( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_DEVICE_INFO DeviceInfo, + _In_opt_ IN PDSM_DEVICE_STATE DevState + ); + +NTSTATUS +DsmpAdjustDeviceStatesALUA( + _In_ IN PDSM_GROUP_ENTRY Group, + _In_opt_ IN PDSM_DEVICE_INFO PreferredActiveDeviceInfo, + _In_ IN ULONG SpecialHandlingFlag + ); + +PDSM_WORKITEM +DsmpAllocateWorkItem( + _In_ IN PDEVICE_OBJECT DeviceObject, + _In_ IN PVOID Context + ); + +VOID +DsmpFreeWorkItem( + _In_ IN PDSM_WORKITEM DsmWorkItem + ); + +VOID +DsmpFreeZombieGroupList( + _In_ IN PDSM_FAILOVER_GROUP FailGroup + ); + +NTSTATUS +DsmpRegCopyTree( + _In_ IN HANDLE SourceKey, + _In_ IN HANDLE DestKey + ); + +NTSTATUS +DsmpRegDeleteTree( + _In_ IN HANDLE KeyRoot + ); + +#if defined (_WIN64) +VOID +DsmpPassThroughPathTranslate32To64( + _In_ IN PMPIO_PASS_THROUGH_PATH32 MpioPassThroughPath32, + _Inout_ IN OUT PMPIO_PASS_THROUGH_PATH MpioPassThroughPath64 + ); + +VOID +DsmpPassThroughPathTranslate64To32( + _In_ IN PMPIO_PASS_THROUGH_PATH MpioPassThroughPath64, + _Inout_ IN OUT PMPIO_PASS_THROUGH_PATH32 MpioPassThroughPath32 + ); +#endif + +NTSTATUS +DsmpGetMaxPRRetryTime( + _In_ IN PDSM_CONTEXT Context, + _Out_ OUT PULONG RetryTime + ); + +NTSTATUS +DsmpQueryCacheInformationFromRegistry( + _In_ IN PDSM_CONTEXT DsmContext, + _Out_ OUT PBOOLEAN UseCacheForLeastBlocks, + _Out_ OUT PULONGLONG CacheSizeForLeastBlocks + ); + +BOOLEAN +DsmpConvertSharedSpinLockToExclusive( + _Inout_ _Requires_lock_held_(*_Curr_) PEX_SPIN_LOCK SpinLock + ); + + +// +// Function prototypes for functions in wmi.c +// + +VOID +DsmpDsmWmiInitialize( + _In_ IN PDSM_WMILIB_CONTEXT WmiGlobalInfo, + _In_ IN PUNICODE_STRING RegistryPath + ); + +NTSTATUS +DsmGlobalQueryData( + _In_ IN PVOID DsmContext, + _In_ IN PDSM_IDS DsmIds, + _In_ IN PIRP Irp, + _In_ IN ULONG GuidIndex, + _In_ IN ULONG InstanceIndex, + _In_ IN ULONG InstanceCount, + _Inout_ IN OUT PULONG InstanceLengthArray, + _In_ IN ULONG BufferAvail, + _Out_writes_to_(BufferAvail, *DataLength) OUT PUCHAR Buffer, + _Out_ OUT PULONG DataLength, + ... + ); + +NTSTATUS +DsmGlobalSetData( + _In_ IN PVOID DsmContext, + _In_ IN PDSM_IDS DsmIds, + _In_ IN PIRP Irp, + _In_ IN ULONG GuidIndex, + _In_ IN ULONG InstanceIndex, + _In_ IN ULONG BufferAvail, + _In_reads_bytes_(BufferAvail) IN PUCHAR Buffer, + ... + ); + +VOID +DsmpWmiInitialize( + _In_ IN PDSM_WMILIB_CONTEXT WmiInfo, + _In_ IN PUNICODE_STRING RegistryPath + ); + +NTSTATUS +DsmQueryData( + _In_ IN PVOID DsmContext, + _In_ IN PDSM_IDS DsmIds, + _In_ IN PIRP Irp, + _In_ IN ULONG GuidIndex, + _In_ IN ULONG InstanceIndex, + _In_ IN ULONG InstanceCount, + _Inout_ IN OUT PULONG InstanceLengthArray, + _In_ IN ULONG BufferAvail, + _When_(GuidIndex == 0 || GuidIndex == 7, _Pre_notnull_ _Const_) + _When_(!(GuidIndex == 0 || GuidIndex == 7), _Out_writes_to_(BufferAvail, *DataLength)) + OUT PUCHAR Buffer, + _Out_ OUT PULONG DataLength, + ... + ); + +NTSTATUS +DsmpQueryLoadBalancePolicy( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_IDS DsmIds, + _In_ IN ULONG DsmWmiVersion, + _In_ IN ULONG InBufferSize, + _In_ IN PULONG OutBufferSize, + _Out_writes_bytes_(*OutBufferSize) OUT PVOID Buffer + ); + +NTSTATUS +DsmpQuerySupportedLBPolicies( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_IDS DsmIds, + _In_ IN ULONG BufferAvail, + _In_ IN ULONG DsmWmiVersion, + _Out_ OUT PULONG OutBufferSize, + _Out_writes_to_(BufferAvail, *OutBufferSize) OUT PUCHAR Buffer + ); + +NTSTATUS +DsmExecuteMethod( + _In_ IN PVOID DsmContext, + _In_ IN PDSM_IDS DsmIds, + _In_ IN PIRP Irp, + _In_ IN ULONG GuidIndex, + _In_ IN ULONG InstanceIndex, + _In_ IN ULONG MethodId, + _In_ IN ULONG InBufferSize, + _In_ IN PULONG OutBufferSize, + _Inout_ IN OUT PUCHAR Buffer, + ... + ); + +NTSTATUS +DsmpClearLoadBalancePolicy( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_IDS DsmIds + ); + +NTSTATUS +DsmpSetLoadBalancePolicy( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_IDS DsmIds, + _In_ IN ULONG DsmWmiVersion, + _In_ IN ULONG InBufferSize, + _In_ IN PULONG OutBufferSize, + _In_ IN PVOID Buffer + ); + +NTSTATUS +DsmpValidateSetLBPolicyInput( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_IDS DsmIds, + _In_ IN ULONG DsmWmiVersion, + _In_ IN PVOID SetLoadBalancePolicyIN, + _In_ IN ULONG InBufferSize + ); + +VOID +DsmpSaveDeviceState( + _In_ IN PVOID SupportedLBPolicies, + _In_ IN ULONG DsmWmiVersion + ); + +VOID +DsmpRestorePreviousDeviceState( + _In_ IN PVOID SupportedLBPolicies, + _In_ IN ULONG DsmWmiVersion + ); + +VOID +DsmpUpdateDesiredStateAndWeight( + _In_ IN PDSM_GROUP_ENTRY Group, + _In_ IN ULONG DsmWmiVersion, + _In_ IN PVOID SupportedLBPolicies + ); + +NTSTATUS +DsmpQueryDevicePerf( + _In_ PDSM_CONTEXT DsmContext, + _In_ PDSM_IDS DsmIds, + _In_ ULONG InBufferSize, + _Inout_ PULONG OutBufferSize, + _Out_writes_to_(*OutBufferSize, *OutBufferSize) PUCHAR Buffer + ); + +NTSTATUS +DsmpClearPerfCounters( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_IDS DsmIds + ); + +NTSTATUS +DsmpQuerySupportedDevicesList( + _In_ PDSM_CONTEXT DsmContext, + _In_ ULONG InBufferSize, + _Inout_ PULONG OutBufferSize, + _Out_writes_to_(*OutBufferSize, *OutBufferSize) PUCHAR Buffer + ); + +NTSTATUS +DsmpQueryTargetsDefaultPolicy( + _In_ PDSM_CONTEXT DsmContext, + _In_ ULONG InBufferSize, + _Inout_ PULONG OutBufferSize, + _Out_writes_to_(*OutBufferSize, *OutBufferSize) PUCHAR Buffer + ); + +NTSTATUS +DsmpQueryDsmDefaultPolicy( + _In_ PDSM_CONTEXT DsmContext, + _In_ ULONG InBufferSize, + _Inout_ PULONG OutBufferSize, + _Out_writes_to_(*OutBufferSize, *OutBufferSize) PUCHAR Buffer + ); + + +// +// Function prototypes for functions in debug.c +// + +VOID +DsmpDebugPrint( + _In_ ULONG DebugPrintLevel, + _In_ PCCHAR DebugMessage, + ... + ); + +// +// SRB Helpers not found in srbhelper.h +// +_Success_(return != 0) +__drv_allocatesMem(mem) +_When_(((PoolType&0x1))!=0, _IRQL_requires_max_(APC_LEVEL)) +_When_(((PoolType&0x1))==0, _IRQL_requires_max_(DISPATCH_LEVEL)) +_When_(((PoolType&0x2))!=0, + __drv_reportError("Must succeed pool allocations are forbidden. " + "Allocation failures cause a system crash")) +_When_(((PoolType&(0x2|POOL_RAISE_IF_ALLOCATION_FAILURE)))==0, + _Post_maybenull_ _Must_inspect_result_) +_When_(((PoolType&(0x2|POOL_RAISE_IF_ALLOCATION_FAILURE)))!=0, + _Post_notnull_ ) +__inline PSTORAGE_REQUEST_BLOCK_HEADER +SrbAllocateCopy( + _Inout_ PVOID Srb, + _In_ _Strict_type_match_ POOL_TYPE PoolType, + _In_ ULONG Tag + ) +/* + +Description: + This function returns an allocated copy of the given SRB. The memory is + allocated using DsmpAllocatePool(). + + ***It is up to the caller to free the memory returned by this function.*** + +Arguments: + Srb - A pointer to either a STORAGE_REQUEST_BLOCK or a SCSI_REQUEST_BLOCK. + PoolType - The pool type to use. See documentation for ExAllocatePoolWithTag(). + Tag - The allocation tag to use. See documentation for ExAllocatePoolWithTag(). + +Returns: + NULL, if the copy could not be allocated; or + A pointer to either a STORAGE_REQUEST_BLOCK or a SCSI_REQUEST_BLOCK that is + direct copy of the given SRB. + +*/ +{ + PSTORAGE_REQUEST_BLOCK srb = (PSTORAGE_REQUEST_BLOCK)Srb; + PSTORAGE_REQUEST_BLOCK_HEADER srbCopy = NULL; + ULONG allocationSize = 0; + + if (srb->Function == SRB_FUNCTION_STORAGE_REQUEST_BLOCK) + { + allocationSize = srb->SrbLength; + NT_ASSERT(allocationSize >= (sizeof(STORAGE_REQUEST_BLOCK) + sizeof(STOR_ADDR_BTL8))); + } + else + { + allocationSize = SCSI_REQUEST_BLOCK_SIZE; + NT_ASSERT(allocationSize >= sizeof(SCSI_REQUEST_BLOCK)); + } + + #pragma warning(suppress: 28160 28118) // False-positive; PoolType is simply passed through + srbCopy = (PSTORAGE_REQUEST_BLOCK_HEADER)DsmpAllocatePool(PoolType, allocationSize, Tag); + if (srbCopy != NULL) + { + RtlCopyMemory(srbCopy, Srb, allocationSize); + } + + return srbCopy; +} + +__inline +BOOLEAN DsmpIsMPIOPassThroughEx( + ULONG ControlCode + ) +// +// Returns TRUE if the given passthrough IOCTL's control code indicates it is +// an "extended" passthrough. Returns FALSE otherwise. +// +{ + if (ControlCode == IOCTL_MPIO_PASS_THROUGH_PATH_EX || + ControlCode == IOCTL_MPIO_PASS_THROUGH_PATH_DIRECT_EX) { + return TRUE; + } else { + return FALSE; + } +} + +__inline +UCHAR DsmpNtStatusToSrbStatus( + _In_ NTSTATUS Status + ) +/*++ + +Routine Description: + + Translate an NT status value into a SCSI Srb status code. + +Arguments: + + Status - Supplies the NT status code to translate. + +Return Value: + + SRB status code. + +--*/ +{ + switch (Status) { + + case STATUS_DEVICE_BUSY: + return SRB_STATUS_BUSY; + + case STATUS_INVALID_DEVICE_REQUEST: + return SRB_STATUS_BAD_FUNCTION; + + case STATUS_INSUFFICIENT_RESOURCES: + return SRB_STATUS_INTERNAL_ERROR; + + case STATUS_INVALID_PARAMETER: + return SRB_STATUS_INVALID_REQUEST; + + default: + if (NT_SUCCESS (Status)) { + return SRB_STATUS_SUCCESS; + } else { + return SRB_STATUS_ERROR; + } + } +} + + +#endif // _PROTOTYPES_H_ + diff --git a/storage/msdsm/src/trace.h b/storage/msdsm/src/trace.h new file mode 100644 index 000000000..aeefbf272 --- /dev/null +++ b/storage/msdsm/src/trace.h @@ -0,0 +1,35 @@ + +/*++ + +Copyright (C) 2004 Microsoft Corporation + +Module Name: + + trace.h + +Abstract: + + Header file included by the Microsoft Device Specific Module (DSM). + + This file contains Windows tracing related defines. + +Environment: + + kernel mode only + +Notes: + +--*/ + +// +// Set component ID for DbgPrintEx calls +// +#define DEBUG_COMP_ID DPFLTR_MSDSM_ID + +// +// Include header file and setup GUID for tracing +// +#include +#define WPP_GUID_MSDSM (DEDADFF5, F99F, 4600, B8C9, 2D4D9B806B5B) +#define WPP_CONTROL_GUIDS WPP_CONTROL_GUIDS_NORMAL_FLAGS(WPP_GUID_MSDSM) + diff --git a/storage/msdsm/src/utils.c b/storage/msdsm/src/utils.c new file mode 100644 index 000000000..529e7536b --- /dev/null +++ b/storage/msdsm/src/utils.c @@ -0,0 +1,7932 @@ + +/*++ + +Copyright (C) 2004-2010 Microsoft Corporation + +Module Name: + + utils.c + +Abstract: + + This driver is the Microsoft Device Specific Module (DSM). + It exports behaviours that mpio.sys will use to determine how to + multipath SPC-3 compliant devices. + + This file contains utility routines. + +Environment: + + kernel mode only + +Notes: + +--*/ + +#include "precomp.h" + +#ifdef DEBUG_USE_WPP +#include "utils.tmh" +#endif + +#pragma warning (disable:4305) + +extern BOOLEAN DoAssert; + +#ifdef ALLOC_PRAGMA + #pragma alloc_text(PAGE, DsmpBuildDeviceNameLegacyPage0x80) + #pragma alloc_text(PAGE, DsmpBuildDeviceName) + #pragma alloc_text(PAGE, DsmpApplyDeviceNameCorrection) + #pragma alloc_text(PAGE, DsmpOpenLoadBalanceSettingsKey) + #pragma alloc_text(PAGE, DsmpQueryLBPolicyForDevice) + #pragma alloc_text(PAGE, DsmpOpenTargetsLoadBalanceSettingKey) + #pragma alloc_text(PAGE, DsmpOpenDsmServicesParametersKey) +#endif + +_Success_(return != NULL) +__drv_allocatesMem(Mem) +_When_(((PoolType&0x1))!=0, _IRQL_requires_max_(APC_LEVEL)) +_When_(((PoolType&0x1))==0, _IRQL_requires_max_(DISPATCH_LEVEL)) +_When_(((PoolType&0x2))!=0, + __drv_reportError("Must succeed pool allocations are forbidden. " + "Allocation failures cause a system crash")) +_When_(((PoolType&(0x2|POOL_RAISE_IF_ALLOCATION_FAILURE)))==0, + _Post_maybenull_ _Must_inspect_result_) +_When_(((PoolType&(0x2|POOL_RAISE_IF_ALLOCATION_FAILURE)))!=0, + _Post_notnull_ ) +_When_((PoolType&NonPagedPoolMustSucceed)!=0, + __drv_reportError("Must succeed pool allocations are forbidden. " + "Allocation failures cause a system crash")) +_Post_writable_byte_size_(NumberOfBytes) +PVOID +DsmpAllocatePool( + _In_ _Strict_type_match_ IN POOL_TYPE PoolType, + _In_ IN SIZE_T NumberOfBytes, + _In_ IN ULONG Tag + ) +/*+++ + +Routine Description : + + Allocates memory from the specified pool using the given tag. + If the allocation is successful, the entire buffer will be zeroed. + +Arguements: + + PoolType - Pool to allocate from (NonPaged, Paged, etc) + NumberOfBytes - Size of the buffer to allocate + Tag - Tag (DSM_TAG_XXX) to be used for this allocation. + These tags are defined in msdsm.h + +Return Value: + + Pointer to the buffer if allocation is successful + NULL otherwise + +--*/ +{ + PVOID Block = NULL; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpAllocatePool (Tag %u): Entering function.\n", + Tag)); + + #pragma warning(suppress: 28118) // False-positive; PoolType is simply passed through + Block = ExAllocatePoolWithTag(PoolType, NumberOfBytes, Tag); + if (Block) { + RtlZeroMemory(Block, NumberOfBytes); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpAllocatePool (Tag %u): Exiting function with allocated block %p.\n", + Tag, + Block)); + + return Block; +} + + +_Success_(return != NULL) +_Post_maybenull_ +_Must_inspect_result_ +__drv_allocatesMem(Mem) +_Post_writable_byte_size_(*BytesAllocated) +_When_(((PoolType&0x1))!=0, _IRQL_requires_max_(APC_LEVEL)) +_When_(((PoolType&0x1))==0, _IRQL_requires_max_(DISPATCH_LEVEL)) +_When_((PoolType&NonPagedPoolMustSucceed)!=0, + __drv_reportError("Must succeed pool allocations are forbidden. " + "Allocation failures cause a system crash")) +PVOID +#pragma warning(suppress:28195) // Allocation is not guaranteed, caller needs to check return value +DsmpAllocateAlignedPool( + _In_ IN POOL_TYPE PoolType, + _In_ IN SIZE_T NumberOfBytes, + _In_ IN ULONG AlignmentMask, + _In_ IN ULONG Tag, + _Out_ OUT SIZE_T *BytesAllocated + ) +/*+++ + +Routine Description : + + Allocates memory from the specified pool using the given tag and alignment requirement. + If the allocation is successful, the entire buffer will be zeroed. + +Arguements: + + PoolType - Pool to allocate from (NonPaged, Paged, etc) + NumberOfBytes - Size of the buffer to allocate + AlignmentMask - Alignment requirement specified by the device + Tag - Tag (DSM_TAG_XXX) to be used for this allocation. + These tags are defined in msdsm.h + BytesAllocated - Returns the number of bytes allocated, if the routine was successful + +Return Value: + + Pointer to the buffer if allocation is successful + NULL otherwise + +--*/ +{ + PVOID Block = NULL; + UINT_PTR align64 = (UINT_PTR)AlignmentMask; + ULONG totalSize = (ULONG)NumberOfBytes; + NTSTATUS status = STATUS_SUCCESS; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpAllocateAlignedPool (Tag %u): Entering function.\n", + Tag)); + + if (BytesAllocated == NULL) { + + status = STATUS_INVALID_PARAMETER; + goto __Exit; + } + + *BytesAllocated = 0; + + if (AlignmentMask) { + + status = RtlULongAdd((ULONG)NumberOfBytes, AlignmentMask, &totalSize); + } + + if (NT_SUCCESS(status)) { + + #pragma warning(suppress: 6014 28118) // Block isn't leaked, this function is marked as an allocator; PoolType is simply passed through + Block = ExAllocatePoolWithTag(PoolType, totalSize, Tag); + + if (Block != NULL) { + + if (AlignmentMask) { + + Block = (PVOID)(((UINT_PTR)Block + align64) & ~align64); + } + } else { + + status = STATUS_INSUFFICIENT_RESOURCES; + } + } + +__Exit: + + if (NT_SUCCESS(status)) { + + RtlZeroMemory(Block, totalSize); + *BytesAllocated = totalSize; + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpAllocateAlignedPool (Tag %u): Exiting function with allocated block %p.\n", + Tag, + Block)); + + return Block; +} + + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +DsmpFreePool( + _In_opt_ __drv_freesMem(Mem) IN PVOID Block + ) +/*+++ + +Routine Description : + + Frees the block passed in. + +Arguements: + + Block - pointer to the memory to free. + +Return Value: + + Nothing + +--*/ +{ + PVOID tempAddress = Block; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpFreePool (Block %p): Entering function.\n", + Block)); + + if (Block) { + + ExFreePool(Block); + Block = NULL; + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpFreePool (Block %p): Exiting function.\n", + tempAddress)); + + return; +} + + +NTSTATUS +DsmpGetStatsGatheringChoice( + _In_ IN PDSM_CONTEXT Context, + _Out_ OUT PULONG StatsGatherChoice + ) +/*++ + +Routine Description: + + This routine is used to determine if the Admin wants statitics to be collected + on every IO. It queries the the services key for the value under + "msdsm\Parameters\DsmDisableStatistics" + +Arguments: + + Context - The DSM Context value. + StatsGatherChoice - Returns the choice of whether or not to gather statistics + +Return Value: + + Status of the RtlQueryRegistryValues call. + +--*/ +{ + RTL_QUERY_REGISTRY_TABLE queryTable[2]; + WCHAR registryKeyName[56] = {0}; + NTSTATUS status = STATUS_INVALID_PARAMETER; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpGetStatsGatherChoice (DsmCtxt %p): Entering function.\n", + Context)); + + if (!StatsGatherChoice) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "DsmpGetStatsGatherChoice (DsmCtxt %p): Invalid parameter - StatsGatherChoice is NULL.\n", + Context)); + + goto __Exit_DsmpGetStatsGatherChoice; + } + + RtlZeroMemory(queryTable, sizeof(queryTable)); + + // + // Build the key value name that we want as the base of the query. + // + RtlStringCbPrintfW(registryKeyName, + sizeof(registryKeyName), + DSM_PARAMETER_PATH_W); + + // + // The query table has two entries. One for the supporteddeviceList and + // the second which is the 'NULL' terminator. + // + queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED; + queryTable[0].Name = DSM_DISABLE_STATISTICS; + queryTable[0].EntryContext = StatsGatherChoice; + + status = RtlQueryRegistryValues(RTL_REGISTRY_SERVICES, + registryKeyName, + queryTable, + registryKeyName, + NULL); + +__Exit_DsmpGetStatsGatherChoice: + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpGetStatsGatherChoice (DsmCtxt %p): Exiting function with status %x.\n", + Context, + status)); + + return status; +} + + +NTSTATUS +DsmpSetStatsGatheringChoice( + _In_ IN PDSM_CONTEXT Context, + _In_ IN ULONG StatsGatherChoice + ) +/*++ + +Routine Description: + + This routine is used to set the value that indicates whether statistics will + be gathered on every IO. It updates the services key for the value under + "msdsm\Parameters\DsmDisableStatistics" + +Arguments: + + Context - The DSM Context value. + StatsGatherChoice - Value indicating whether to gather statistics (TRUE) or not (FALSE) + +Return Value: + + Status of the RtlWriteRegistryValue call. + +--*/ +{ + WCHAR registryKeyName[56] = {0}; + NTSTATUS status = STATUS_SUCCESS; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpSetStatsGatherChoice (DsmCtxt %p): Entering function.\n", + Context)); + + // + // Build the key value name that we want as the base of the query. + // + RtlStringCbPrintfW(registryKeyName, + sizeof(registryKeyName), + DSM_PARAMETER_PATH_W); + + + status = RtlWriteRegistryValue(RTL_REGISTRY_SERVICES, + registryKeyName, + DSM_DISABLE_STATISTICS, + REG_DWORD, + &StatsGatherChoice, + sizeof(ULONG)); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpSetStatsGatherChoice (DsmCtxt %p): Exiting function with status %x.\n", + Context, + status)); + + return status; +} + + + +NTSTATUS +DsmpGetDeviceList( + _In_ IN PDSM_CONTEXT Context + ) +/*++ + +Routine Description: + + This routine is used to build the supported device list by querying the services + key for the values under "msdsm\Parameters\DsmSupportedDeviceList" + +Arguments: + + Context - The DSM Context value. It contains storage for the multi_sz string that may + be built. + +Return Value: + + Status of the RtlQueryRegistryValues call. + +--*/ +{ + RTL_QUERY_REGISTRY_TABLE queryTable[2]; + WCHAR registryKeyName[56] = {0}; + UNICODE_STRING inquiryStrings; + WCHAR defaultIDs[] = { L"\0" }; + NTSTATUS status; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpGetDeviceList (DsmCtxt %p): Entering function.\n", + Context)); + + RtlZeroMemory(queryTable, sizeof(queryTable)); + RtlInitUnicodeString(&inquiryStrings, NULL); + + // + // Build the key value name that we want as the base of the query. + // + RtlStringCbPrintfW(registryKeyName, + sizeof(registryKeyName), + DSM_PARAMETER_PATH_W); + + // + // The query table has two entries. One for the supporteddeviceList and + // the second which is the 'NULL' terminator. + // + // Indicate that there is NO call-back routine, and to give back the MULTI_SZ as + // one blob, as opposed to individual unicode strings. + // + queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_NOEXPAND; + + // + // The value to query. + // + queryTable[0].Name = DSM_SUPPORTED_DEVICELIST_VALUE_NAME; + + // + // Where to put the strings. Note that we need to use an empty unicode_string + // for the query or else RtlQueryRegistryValues will only fill in enough + // entries as specified by the size of the unicode string's buffer, which + // is why we can't use Context->SupportedDevices directly in the call. + // + queryTable[0].EntryContext = &inquiryStrings; + queryTable[0].DefaultType = REG_MULTI_SZ; + queryTable[0].DefaultData = defaultIDs; + queryTable[0].DefaultLength = sizeof(defaultIDs); + + status = RtlQueryRegistryValues(RTL_REGISTRY_SERVICES, + registryKeyName, + queryTable, + registryKeyName, + NULL); + + // + // If we successfully queried for the supported device list, we need to delete + // our cached list and update it with this new one. + // + if (NT_SUCCESS(status)) { + + KIRQL oldIrql; + PWCHAR tempBuffer = NULL; + + tempBuffer = DsmpAllocatePool(NonPagedPoolNx, inquiryStrings.MaximumLength, DSM_TAG_REG_VALUE_RELATED); + + // + // This is a "best effort" operation. If we are unable to allocate a + // buffer for the strings, we just continue using our old cached list. + // We do NOT fall back to using inquiryStrings's buffer as we want to + // be able to work with the supported devices list at raised IRQL. + // + if (tempBuffer) { + + RtlCopyMemory(tempBuffer, inquiryStrings.Buffer, inquiryStrings.Length); + + KeAcquireSpinLock(&Context->SupportedDevicesListLock, &oldIrql); + DsmpFreePool(Context->SupportedDevices.Buffer); + Context->SupportedDevices.Buffer = tempBuffer; + Context->SupportedDevices.Length = inquiryStrings.Length; + Context->SupportedDevices.MaximumLength = inquiryStrings.MaximumLength; + KeReleaseSpinLock(&Context->SupportedDevicesListLock, oldIrql); + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpGetDeviceList (DsmCtxt %p): Failed to allocate supported device list's buffer.\n", + Context)); + + status = STATUS_INSUFFICIENT_RESOURCES; + } + + ExFreePool(inquiryStrings.Buffer); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpGetDeviceList (DsmCtxt %p): Exiting function with status %x.\n", + Context, + status)); + + return status; +} + + +_Success_(return==0) +NTSTATUS +DsmpGetStandardInquiryData( + _In_ IN PDEVICE_OBJECT DeviceObject, + _Out_ OUT PINQUIRYDATA InquiryData + ) +/*++ + +Routine Description: + + Helper routine to send an inquiry with EVPD cleared to get the standard inquiry data. + +Arguments: + + DeviceObject - The port PDO to which the command should be sent. + InquiryData - Pointer to inquiry data that will be returned to caller. + +Return Value: + + STATUS_SUCCESS or failure NTSTATUS code. + +--*/ +{ + PSCSI_PASS_THROUGH_WITH_BUFFERS passThrough = NULL; + PCDB cdb; + IO_STATUS_BLOCK ioStatus; + ULONG length; + NTSTATUS status = STATUS_SUCCESS; + PINQUIRYDATA inquiryData; + PSENSE_DATA senseData; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpGetStandardInquiryData (DevObj %p): Entering function.\n", + DeviceObject)); + + if (InquiryData == NULL) { + status = STATUS_INVALID_PARAMETER; + goto __Exit_DsmpGetStandardInquiryData; + } + + // + // Build a standard inquiry command. + // + length = sizeof(SCSI_PASS_THROUGH_WITH_BUFFERS); + + passThrough = DsmpAllocatePool(NonPagedPoolNx, + length, + DSM_TAG_PASS_THRU); + if (!passThrough) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpGetStandardInquiryData (DevObj %p): Failed to allocate mem for passthrough.\n", + DeviceObject)); + + status = STATUS_INSUFFICIENT_RESOURCES; + goto __Exit_DsmpGetStandardInquiryData; + } + +__Retry_Request: + + // + // Build the cdb for SCSI-3 standard inquiry. + // + cdb = (PCDB)passThrough->ScsiPassThrough.Cdb; + cdb->CDB6INQUIRY3.OperationCode = SCSIOP_INQUIRY; + cdb->CDB6INQUIRY3.EnableVitalProductData = 0; + cdb->CDB6INQUIRY3.AllocationLength = sizeof(INQUIRYDATA); + + passThrough->ScsiPassThrough.Length = sizeof(SCSI_PASS_THROUGH); + passThrough->ScsiPassThrough.CdbLength = 6; + passThrough->ScsiPassThrough.SenseInfoLength = SPTWB_SENSE_LENGTH; + passThrough->ScsiPassThrough.DataIn = 1; + passThrough->ScsiPassThrough.DataTransferLength = sizeof(INQUIRYDATA); + passThrough->ScsiPassThrough.TimeOutValue = 20; + passThrough->ScsiPassThrough.SenseInfoOffset = FIELD_OFFSET(SCSI_PASS_THROUGH_WITH_BUFFERS, SenseInfoBuffer); + passThrough->ScsiPassThrough.DataBufferOffset = FIELD_OFFSET(SCSI_PASS_THROUGH_WITH_BUFFERS, DataBuffer); + + DsmSendDeviceIoControlSynchronous(IOCTL_SCSI_PASS_THROUGH, + DeviceObject, + passThrough, + passThrough, + length, + length, + FALSE, + &ioStatus); + + status = ioStatus.Status; + senseData = (PSENSE_DATA)(passThrough->SenseInfoBuffer); + + if ((passThrough->ScsiPassThrough.ScsiStatus == SCSISTAT_GOOD) && (NT_SUCCESS(status))) { + + // + // Get the returned data. + // + inquiryData = (PINQUIRYDATA)(passThrough->DataBuffer); + + RtlCopyMemory(InquiryData, inquiryData, sizeof(INQUIRYDATA)); + + } else if ((passThrough->ScsiPassThrough.ScsiStatus == SCSISTAT_CHECK_CONDITION) && + (NT_SUCCESS(ioStatus.Status)) && + (DsmpShouldRetryPassThroughRequest(senseData, passThrough->ScsiPassThrough.SenseInfoLength))) { + + length = sizeof(SCSI_PASS_THROUGH_WITH_BUFFERS); + + // + // Retry the request + // + RtlZeroMemory(passThrough, length); + goto __Retry_Request; + + } else { + + // Failed to get inquiry data + // Here it is possible that status is success, but scsi status is not. + // If so, set status to unsuccessful. + if (NT_SUCCESS(status)){ + status = STATUS_UNSUCCESSFUL; + } + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpGetStandardInquiryData (DevObj %p): NTStatus 0x%x, ScsiStatus 0x%x.\n", + DeviceObject, + status, + passThrough->ScsiPassThrough.ScsiStatus)); + } + +__Exit_DsmpGetStandardInquiryData: + + // + // Free the passthrough + data buffer. + // + if (passThrough) { + DsmpFreePool(passThrough); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpGetStandardInquiryData (DevObj %p): Exiting function with status %x.\n", + DeviceObject, + status)); + + return status; +} + + +BOOLEAN +DsmpCheckScsiCompliance( + _In_ IN PDEVICE_OBJECT TargetObject, + _In_ IN PINQUIRYDATA InquiryData, + _In_ IN PSTORAGE_DEVICE_DESCRIPTOR Descriptor, + _In_ IN PSTORAGE_DEVICE_ID_DESCRIPTOR DeviceIdList + ) +/*++ + +Routine Description: + + Helper routine to determine if the device is SPC-3 compliant. + +Arguments: + + DeviceObject - The port PDO that we're determining compliance for. + InquiryData - Pointer to its inquiry data. + Descriptor - Pointer to its VPD page 0x80 data + DeviceIdList - Pointer to its VPD page 0x83 data + +Return Value: + + TRUE if compliant, else FALSE. + +--*/ +{ + BOOLEAN supported = FALSE /* TRUE */; + UCHAR deviceType; + UCHAR qualifier; + + UNREFERENCED_PARAMETER(DeviceIdList); + UNREFERENCED_PARAMETER(Descriptor); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpCheckScsiCompliance (DevObj %p): Entering function.\n", + TargetObject)); + + deviceType = InquiryData->DeviceType & 0x1F; + qualifier = (InquiryData->DeviceTypeQualifier >> 0x5) & 0x7; + + if ((deviceType | qualifier) == 0x7F) { + + supported = FALSE; + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpCheckScsiCompliance (DevObj %p): Exiting function with Supported = %u.\n", + TargetObject, + supported)); + + return supported; +} + + +BOOLEAN +DsmpDeviceSupported( + _In_ IN PDSM_CONTEXT Context, + _In_ IN PCSTR VendorId, + _In_ IN PCSTR ProductId + ) +/*++ + +Routine Description: + + This routine determines whether the device is supported by traversing the SupportedDevice + list and comparing to the VendorId/ProductId values passed in. + +Arguments: + + Context - Context value given to the multipath driver during registration. + VendorId - Pointer to the inquiry data VendorId. + ProductId - Pointer to the inquiry data ProductId. + +Return Value: + + TRUE - If VendorId/ProductId is found. + +--*/ +{ + UNICODE_STRING deviceName; + UNICODE_STRING productName; + ANSI_STRING ansiVendor; + ANSI_STRING ansiProduct; + NTSTATUS status; + BOOLEAN supported = FALSE; + KIRQL oldIrql; + UNICODE_STRING tempStrings; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpDeviceSupported (DsmCtxt %p): Entering function.\n", + Context)); + + KeAcquireSpinLock(&Context->SupportedDevicesListLock, &oldIrql); + + RtlInitUnicodeString(&tempStrings, NULL); + tempStrings.Buffer = DsmpAllocatePool(NonPagedPoolNx, Context->SupportedDevices.MaximumLength, DSM_TAG_REG_VALUE_RELATED); + + if (tempStrings.Buffer) { + + RtlCopyMemory(tempStrings.Buffer, Context->SupportedDevices.Buffer, Context->SupportedDevices.Length); + tempStrings.Length = Context->SupportedDevices.Length; + tempStrings.MaximumLength = Context->SupportedDevices.MaximumLength; + + } else { + + status = STATUS_INSUFFICIENT_RESOURCES; + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpDeviceSupported (DsmCtxt %p): Failed to allocate temporary list (error %x).\n", + Context, + status)); + + KeReleaseSpinLock(&Context->SupportedDevicesListLock, oldIrql); + + goto __Exit_DsmpDeviceSupported; + } + + KeReleaseSpinLock(&Context->SupportedDevicesListLock, oldIrql); + + // + // The SupportedDevice list was built in DriverEntry from the services key. + // + if (tempStrings.MaximumLength == 0) { + + // + // List is empty. + // + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpDeviceSupported (DsmCtxt %p): No supported Device in the list.\n", + Context)); + + goto __Exit_DsmpDeviceSupported; + } + + RtlInitUnicodeString(&productName, NULL); + + // + // Convert the inquiry fields into ansi strings. + // + RtlInitAnsiString(&ansiVendor, VendorId); + RtlInitAnsiString(&ansiProduct, ProductId); + + // + // Allocate the deviceName buffer. Needs to be 8+16 plus NULL. + // (productId length + vendorId length + NULL). + // + deviceName.MaximumLength = 25 * sizeof(WCHAR); + deviceName.Buffer = DsmpAllocatePool(PagedPool, deviceName.MaximumLength, DSM_TAG_SUPPORTED_DEV); + + if (deviceName.Buffer) { + + // + // Convert the vendorId to unicode. + // + status = RtlAnsiStringToUnicodeString(&deviceName, &ansiVendor, FALSE); + if (NT_SUCCESS(status)) { + + // + // Convert the productId to unicode. + // + status = RtlAnsiStringToUnicodeString(&productName, &ansiProduct, TRUE); + + if (NT_SUCCESS(status)) { + + // + // 'cat' them. + // + status = RtlAppendUnicodeStringToString(&deviceName, &productName); + + if (NT_SUCCESS(status)) { + + // + // Run the list of supported devices that was captured from the registry + // and see if this one is in the list. + // + supported = DsmpFindSupportedDevice(&deviceName, + &tempStrings); + } + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpDeviceSupported (DsmCtxt %p): Failed to append product name. Status %x.\n", + Context, + status)); + } + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpDeviceSupported (DsmCtxt %p): Failed to convert ansi vendor string to unicode. Status %x\n", + Context, + status)); + } + + DsmpFreePool(deviceName.Buffer); + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpDeviceSupported (DsmCtxt %p): Failed to allocate device name buffer.\n", + Context)); + } + +__Exit_DsmpDeviceSupported: + + if (tempStrings.Buffer) { + DsmpFreePool(tempStrings.Buffer); + } + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpDeviceSupported (DsmCtxt %p): Exiting function with supported = %u.\n", + Context, + supported)); + + return supported; +} + + +BOOLEAN +DsmpFindSupportedDevice( + _In_ IN PUNICODE_STRING DeviceName, + _In_ IN PUNICODE_STRING SupportedDevices + ) +/*++ + +Routine Description: + + This routine compares the two unicode strings for a match. + +Arguments: + + DeviceName - String built from the current device's inquiry data. + SupportedDevices - MULTI_SZ of devices that are supported. + +Return Value: + + TRUE - If VendorId/ProductId is found. + +--*/ +{ + PWSTR devices = SupportedDevices->Buffer; + ULONG bufferLengthLeft = SupportedDevices->MaximumLength / sizeof(WCHAR); + UNICODE_STRING unicodeString; + USHORT originalLength = DeviceName->Length; + LONG compare; + BOOLEAN supported = FALSE; + WCHAR tempString[32]; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpFindSupportedDevice (DevName %ws): Entering function.\n", + DeviceName->Buffer)); + + // + // 'devices' is the current buffer in the MULTI_SZ built from + // the registry. + // + while (devices[0]) { + + RtlZeroMemory(tempString, sizeof(tempString)); + + if (!NT_SUCCESS(RtlStringCchCopyNW(tempString, sizeof(tempString) / sizeof(tempString[0]), devices, bufferLengthLeft))) { + + tempString[(sizeof(tempString) / sizeof(tempString)) - 1] = L'\0'; + } + + // + // Make the current entry into a unicode string. + // + RtlInitUnicodeString(&unicodeString, tempString); + + // + // Compare this one with the current device. + // However, for storages that make up the product id on-the-fly, MPIO + // allows for matching based just on substring (product-id-prefix so to + // speak). + // + if (unicodeString.Length < DeviceName->Length) { + DeviceName->Length = unicodeString.Length; + } + + compare = RtlCompareUnicodeStrings(unicodeString.Buffer, + unicodeString.Length / sizeof(WCHAR), + DeviceName->Buffer, + DeviceName->Length / sizeof(WCHAR), + TRUE); + + DeviceName->Length = originalLength; + + if (compare == 0) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpFindSupportedDevice (DevName %ws): Device support found in the registry.\n", + DeviceName->Buffer)); + + supported = TRUE; + break; + } + + // + // Advance to next entry in the MULTI_SZ. + // + devices += (unicodeString.MaximumLength / sizeof(WCHAR)); + + bufferLengthLeft -= (unicodeString.MaximumLength / sizeof(WCHAR)); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpFindSupportedDevice (DevName %ws): Exiting function with Supported = %u.\n", + DeviceName->Buffer, + supported)); + + return supported; +} + +_Success_(return!=0) +PVOID +DsmpParseDeviceID( + _In_ IN PSTORAGE_DEVICE_ID_DESCRIPTOR DeviceID, + _In_ IN DSM_DEVID_TYPE DeviceIdType, + _In_opt_ IN PULONG IdNumber, + _Out_opt_ OUT PSTORAGE_IDENTIFIER_CODE_SET CodeSet, + _In_ IN BOOLEAN Legacy + ) +/*++ + +Routine Description: + + This routine builds a serial number string based on the information + in the VPD page 0x83 data if serial number is requested, else it + returns the appropriate identifier requested. + + Caller must free the buffer. + +Arguments: + + DeviceIdList - VPD Page 0x83 information. + DeviceIdType - Type of identifier that the DeviceID is being parsed for + IdNumber - If there are multiple identifiers of type DeviceIdType, this parameter + determines which among them to actually return. + IMPORTANT: This number is one-based (not zero-based). + CodeSet - Of relevance only if the DeviceIdType is DSM_DEVID_SERIAL_NUMBER. This + returns the code set that was used when building the serial number. + Legacy - Of relevance only if the DeviceIdType is DSM_DEVID_SERIAL_NUMBER. If the + code set of the identifier is StorageIdCodeSetBinary, this determines + whether to use the legacy method of binary to ascii conversion. + +Return Value: + + Requested Device identifier. + +--*/ +{ + PSTORAGE_IDENTIFIER identifier; + STORAGE_IDENTIFIER_CODE_SET codeSet = StorageIdCodeSetReserved; // Preload with a bogus value. + STORAGE_IDENTIFIER_TYPE type = 0xF; + STORAGE_ASSOCIATION_TYPE association = 0xF; + ULONG numberIds; + ULONG i; + ULONG identifierSize = 0; + PUCHAR bytes = NULL; + PVOID buffer = NULL; + BOOLEAN done = FALSE; + ULONG idNumber = MAXULONG; + ULONG matches = 0; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpParseDeviceID (DevIdDesc %p): Entering function - IdType %x.\n", + DeviceID, + DeviceIdType)); + + if (IdNumber) { + idNumber = *IdNumber; + } + + // + // Get the number of encapsulated identifiers. + // + numberIds = DeviceID->NumberOfIdentifiers; + + if (idNumber != MAXULONG && idNumber > numberIds) { + goto __Exit_DsmpParseDeviceID; + } + + // + // Get a pointer to the first one. + // + identifier = (PSTORAGE_IDENTIFIER)(DeviceID->Identifiers); + + for (i = 0; i < numberIds && !done; i++) { + + switch (DeviceIdType) { + + case DSM_DEVID_SERIAL_NUMBER: { + + // + // The way this works is that we will go through all the identifiers + // Order of preference will be LUN-associated over Target-associated. + // Further, upon same association, preference will be based on type as + // follows: 0x8, 0x3, 0x2, 0x1, 0x0. + // So an existing identifier will be discarded if a better one is found. + // If two identifiers have the same type, we will prefer the one will + // the larger length. + // + + // + // 1. Ensure that the association is for either the LUN or target. (If neither, ignore id). + // 2. If association is with target, don't it consider if current candidate has assocation with LUN. + // 3. If considering this identifier, order of preference is 8 > 3 > 2 > 1 > 0. + // 4. If this id type is same as current candidate, consider it only if it is of greater length. + // + if (((identifier->Association == StorageIdAssocDevice) || + (identifier->Association == 0x2 && association != StorageIdAssocDevice)) && + ((type == identifier->Type && identifierSize < identifier->IdentifierSize) || + (type != identifier->Type && DsmpIsPreferredDeviceId(type, identifier->Type)))) { + + // + // Get a pointer to the id itself. + // + bytes = identifier->Identifier; + + // + // The id's size. + // + identifierSize = identifier->IdentifierSize; + + // + // Get the type, code set, and association. + // + type = identifier->Type; + codeSet = identifier->CodeSet; + association = identifier->Association; + + matches++; + } + + break; + } + + case DSM_DEVID_RELATIVE_TARGET_PORT: { + + // + // Ensure that the association is for the target port. + // + if (identifier->Association != StorageIdAssocPort) { + + if ((i + 1) < numberIds) { + identifier = (PSTORAGE_IDENTIFIER)((PUCHAR)identifier + identifier->NextOffset); + } + + continue; + } + + if (identifier->Type == StorageIdTypePortRelative) { + + // + // Get a pointer to the id itself. + // + bytes = identifier->Identifier; + + // + // The id's size. + // + identifierSize = identifier->IdentifierSize; + + type = identifier->Type; + codeSet = identifier->CodeSet; + association = identifier->Association; + + matches++; + } + + break; + } + + case DSM_DEVID_TARGET_PORT_GROUP: { + + // + // Ensure that the association is for the target port. + // + if (identifier->Association != StorageIdAssocPort) { + + if ((i + 1) < numberIds) { + identifier = (PSTORAGE_IDENTIFIER)((PUCHAR)identifier + identifier->NextOffset); + } + + continue; + } + + if (identifier->Type == 0x5) { + + // + // Get a pointer to the id itself. + // + bytes = identifier->Identifier; + + // + // Move this by two bytes because first two bytes are reservered + // + bytes += sizeof(USHORT); + + // + // The id's size. Reduce the size by 2 bytes (to account + // for the reservered bytes) + // + identifierSize = identifier->IdentifierSize - sizeof(USHORT); + + type = identifier->Type; + codeSet = identifier->CodeSet; + association = identifier->Association; + + matches++; + } + + break; + } + + default: break; + } + + + if (idNumber != MAXULONG && idNumber == matches) { + done = TRUE; + } + + // + // Advance to the next identifier in the buffer. + // + if ((i + 1) < numberIds) { + identifier = (PSTORAGE_IDENTIFIER)((PUCHAR)identifier + identifier->NextOffset); + } + } + + if (idNumber != MAXULONG && idNumber > matches) { + goto __Exit_DsmpParseDeviceID; + } + + if (DeviceIdType == DSM_DEVID_SERIAL_NUMBER) { + + if (type != StorageIdTypeScsiNameString && + type != StorageIdTypeFCPHName && + type != StorageIdTypeEUI64 && + type != StorageIdTypeVendorId && + type != StorageIdTypeVendorSpecific) { + + DSM_ASSERT(FALSE); + bytes = NULL; + identifierSize = 0; + type = association = 0xF; + codeSet = StorageIdCodeSetReserved; + } + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpParseDeviceID (DevIdDesc %p): IdentifierSize = %u, Type = %u, Association = %u, CodeSet = %u.\n", + DeviceID, + identifierSize, + type, + association, + codeSet)); + + if (!bytes) { + goto __Exit_DsmpParseDeviceID; + } + + if (codeSet == StorageIdCodeSetBinary) { + + // + // Need to convert to ascii. + // + buffer = DsmpBinaryToAscii(bytes, + identifierSize, + &identifierSize, + Legacy); + + } else { + + if (identifierSize) { + // + // Allocate a buffer that is the size of the data, plus one for NULL. + // + buffer = DsmpAllocatePool(NonPagedPoolNx, identifierSize + 1, DSM_TAG_DEV_ID); + DSM_ASSERT(buffer); + + if (buffer) { + + // + // Copy over the id. + // + RtlCopyMemory(buffer, bytes, identifierSize); + } + } + } + + if (CodeSet) { + *CodeSet = codeSet; + } + + } else { + + if (identifierSize) { + + DSM_ASSERT((DeviceIdType == DSM_DEVID_RELATIVE_TARGET_PORT && identifierSize == sizeof(ULONG)) || + (DeviceIdType == DSM_DEVID_TARGET_PORT_GROUP && identifierSize == sizeof(USHORT))); + + _Analysis_assume_((DeviceIdType == DSM_DEVID_RELATIVE_TARGET_PORT && identifierSize == sizeof(ULONG)) || + (DeviceIdType == DSM_DEVID_TARGET_PORT_GROUP && identifierSize == sizeof(USHORT))); + + buffer = DsmpAllocatePool(NonPagedPoolNx, identifierSize, DSM_TAG_DEV_ID); + + if (buffer) { + + if (DeviceIdType == DSM_DEVID_RELATIVE_TARGET_PORT) { + + GetUlongFrom4ByteArray(bytes, *((PULONG)buffer)); + + } else if (DeviceIdType == DSM_DEVID_TARGET_PORT_GROUP) { + + *((PUSHORT)buffer) = (bytes[0] << 8) | (bytes[1]); + } + } + } + } + +__Exit_DsmpParseDeviceID: + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpParseDeviceID (DevIdDesc %p): Exiting function with buffer %p.\n", + DeviceID, + buffer)); + + return buffer; +} + + +PUCHAR +DsmpBinaryToAscii( + _In_reads_(Length) IN PUCHAR HexBuffer, + _In_ IN ULONG Length, + _Inout_ IN OUT PULONG UpdateLength, + _In_ IN BOOLEAN Legacy + ) +/*++ + +Routine Description: + + This routine will convert HexBuffer into an ascii NULL-terminated string. + + Note: This routine will allocate memory for storing the ascii string. It is + the responsibility of the caller to free this buffer. + +Arguments: + + HexBuffer - Pointer to the binary data. + Length - Length, in bytes, of HexBuffer. + UpdateLength - Storage to place the actual length of the returned string. + Legacy - Use the legacy method for the conversion. + +Return Value: + + Serial Number string, or NULL if an error occurred. + +--*/ +{ + static UCHAR IntegerTable[] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; + ULONG i; + ULONG j; + ULONG actualLength; + PUCHAR buffer = NULL; + UCHAR highWord; + UCHAR lowWord; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpBinaryToAscii (HexBuff %p): Entering function.\n", + HexBuffer)); + + if (Length == 0) { + *UpdateLength = 0; + goto __Exit_DsmpBinaryToAscii; + } + + if (Legacy) { + // + // Do a pre-test on the buffer to determine the length actually needed. + // + for (i = 0, actualLength = 0; i < Length; i++) { + + if (HexBuffer[i] < 0x10) { + actualLength++; + } else { + actualLength += 2; + } + } + + // + // Add room for a terminating NULL. + // + actualLength++; + } else { + // + // We need one character for each nibble, plus one for the terminating NULL. + // + actualLength = (Length * 2) + 1; + } + + // + // Allocate the buffer. + // + buffer = DsmpAllocatePool(NonPagedPoolNx, + actualLength, + DSM_TAG_BIN_TO_ASCII); + if (!buffer) { + *UpdateLength = 0; + goto __Exit_DsmpBinaryToAscii; + } + + for (i = 0, j = 0; i < Length && j < actualLength; i++) { + + if (Legacy && (HexBuffer[i] < 0x10)) { + + // + // If legacy is mentioned and it's 0x0F or less, + // just convert the entire byte. + // + buffer[j++] = IntegerTable[HexBuffer[i]]; + } else { + + // + // Split out each nibble from the binary byte. + // + highWord = HexBuffer[i] >> 4; + lowWord = HexBuffer[i] & 0x0F; + + // + // Using the lookup table, convert and stuff into + // the ascii buffer. + // + buffer[j++] = IntegerTable[highWord]; + buffer[j++] = IntegerTable[lowWord]; + } + } + + // + // Update the caller's length field. + // + *UpdateLength = actualLength; + +__Exit_DsmpBinaryToAscii: + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpBinaryToAscii (HexBuff %p): Exiting function with buffer %s.\n", + HexBuffer, + (const char*) buffer)); + + return buffer; +} + + +PSTR +DsmpGetSerialNumber( + _In_ IN PDEVICE_OBJECT DeviceObject + ) +/*++ + +Routine Description: + + Helper routine to send an inquiry with EVPD set to get the serial number page. + Used if the serial number is not embedded in the device descriptor (this device probably + doesn't support VPD page 0x00). + + Note: This routine will allocate memory for storing the serial number. It is + the responsibility of the caller to free this buffer. + +Arguments: + + DeviceObject - The port PDO to which the command should be sent. + +Return Value: + + The serial number (null-terminated string) or NULL if the call fails. + +--*/ +{ + PSCSI_PASS_THROUGH_WITH_BUFFERS passThrough = NULL; + PVPD_SERIAL_NUMBER_PAGE serialPage; + PCDB cdb; + PSTR serialNumber = NULL; + IO_STATUS_BLOCK ioStatus; + ULONG length; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpGetSerialNumber (DevObj %p): Entering function.\n", + DeviceObject)); + + // + // Build an inquiry command with EVPD and pagecode of 0x80 (serial number). + // + length = sizeof(SCSI_PASS_THROUGH_WITH_BUFFERS); + + passThrough = DsmpAllocatePool(NonPagedPoolNx, + length, + DSM_TAG_PASS_THRU); + if (!passThrough) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpGetSerialNumber (DevObj %p): Failed to allocate mem for passthrough.\n", + DeviceObject)); + + goto __Exit_DsmpGetSerialNumber; + } + + // + // Build the cdb. + // + cdb = (PCDB)passThrough->ScsiPassThrough.Cdb; + cdb->CDB6INQUIRY.OperationCode = SCSIOP_INQUIRY; + cdb->CDB6INQUIRY.Reserved1 = 1; + cdb->CDB6INQUIRY.PageCode = VPD_SERIAL_NUMBER; + cdb->CDB6INQUIRY.AllocationLength = DSM_SERIAL_NUMBER_BUFFER_SIZE; + + passThrough->ScsiPassThrough.Length = sizeof(SCSI_PASS_THROUGH); + passThrough->ScsiPassThrough.CdbLength = 6; + passThrough->ScsiPassThrough.SenseInfoLength = SPTWB_SENSE_LENGTH; + passThrough->ScsiPassThrough.DataIn = 1; + passThrough->ScsiPassThrough.DataTransferLength = DSM_SERIAL_NUMBER_BUFFER_SIZE; + passThrough->ScsiPassThrough.TimeOutValue = 20; + passThrough->ScsiPassThrough.SenseInfoOffset = FIELD_OFFSET(SCSI_PASS_THROUGH_WITH_BUFFERS, SenseInfoBuffer); + passThrough->ScsiPassThrough.DataBufferOffset = FIELD_OFFSET(SCSI_PASS_THROUGH_WITH_BUFFERS, DataBuffer); + + DsmSendDeviceIoControlSynchronous(IOCTL_SCSI_PASS_THROUGH, + DeviceObject, + passThrough, + passThrough, + length, + length, + FALSE, + &ioStatus); + if ((passThrough->ScsiPassThrough.ScsiStatus == SCSISTAT_GOOD) && + (NT_SUCCESS(ioStatus.Status))) { + + ULONG inx; + + // + // Get the returned data. + // + serialPage = (PVPD_SERIAL_NUMBER_PAGE)(passThrough->DataBuffer); + + // + // Allocate a buffer to hold just the serial number plus a null terminator + // + serialNumber = DsmpAllocatePool(NonPagedPoolNx, + serialPage->PageLength + 1, + DSM_TAG_SERIAL_NUM); + if (serialNumber) { + + // + // Copy it over. + // + RtlCopyMemory(serialNumber, serialPage->SerialNumber, serialPage->PageLength); + + // + // Some devices return binary data for the serial number. + // Convert to a more ascii-ish format so that other routines don't have a problem. + // + for (inx = 0; inx < serialPage->PageLength; inx++) { + if (serialNumber[inx] == '\0') { + serialNumber[inx] = ' '; + } + } + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpGetSerialNumber (DevObj %p): Failed to allocate mem for serialnumber.\n", + DeviceObject)); + } + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpGetSerialNumber (DevObj %p): NTStatus 0%x, ScsiStatus 0x%x.\n", + DeviceObject, + ioStatus.Status, + passThrough->ScsiPassThrough.ScsiStatus)); + } + +__Exit_DsmpGetSerialNumber: + + // + // Free the passthrough + data buffer. + // + if (passThrough) { + + DsmpFreePool(passThrough); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpGetSerialNumber (DevObj %p): Exiting function with serial number %s.\n", + DeviceObject, + (const char*)serialNumber)); + + // + // Return the sn. + // + return serialNumber; +} + + +NTSTATUS +DsmpDisableImplicitStateTransition( + _In_ IN PDEVICE_OBJECT TargetDevice, + _Out_ OUT PBOOLEAN DisableImplicit + ) +/*++ + +Routine Description: + + Send down request to disable implicit ALUA state transition. + The function first sends down a mode sense to get the control extension mode + sense data. It then clears the IALUAE bit and sends down a mode select. + +Arguements: + + TargetDevice - Device object that will be target of this command. + DisableImplicit - Flag returned to the caller to indicate whether or not + implicit transitions are disabled. + +Return Value : + + STATUS_SUCCESS if the command succeeds. + Appropriate NTSTATUS code on failure + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + PSCSI_PASS_THROUGH_WITH_BUFFERS passThrough = NULL; + PCDB cdb; + IO_STATUS_BLOCK ioStatus; + ULONG length; + PSPC3_CONTROL_EXTENSION_MODE_PAGE controlExtensionPage = NULL; + PSENSE_DATA senseData = NULL; + BOOLEAN implicitDisabled = FALSE; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpDisableImplicitStateTransition (DevObj %p): Entering function.\n", + TargetDevice)); + + // + // First build the mode sense command to get the control extension parameters. + // + length = sizeof(SCSI_PASS_THROUGH_WITH_BUFFERS); + + passThrough = DsmpAllocatePool(NonPagedPoolNx, + length, + DSM_TAG_PASS_THRU); + if (!passThrough) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpDisableImplicitStateTransition (DevObj %p): Failed to allocate mem for passthrough.\n", + TargetDevice)); + + status = STATUS_INSUFFICIENT_RESOURCES; + goto __Exit_DsmpDisableImplicitStateTransition; + } + +__Retry_ModeSense: + + passThrough->ScsiPassThrough.Length = sizeof(SCSI_PASS_THROUGH); + passThrough->ScsiPassThrough.CdbLength = 6; + passThrough->ScsiPassThrough.SenseInfoLength = SPTWB_SENSE_LENGTH; + passThrough->ScsiPassThrough.DataIn = 1; + passThrough->ScsiPassThrough.DataTransferLength = sizeof(SPC3_CONTROL_EXTENSION_MODE_PAGE); + passThrough->ScsiPassThrough.TimeOutValue = 20; + passThrough->ScsiPassThrough.SenseInfoOffset = FIELD_OFFSET(SCSI_PASS_THROUGH_WITH_BUFFERS, SenseInfoBuffer); + passThrough->ScsiPassThrough.DataBufferOffset = FIELD_OFFSET(SCSI_PASS_THROUGH_WITH_BUFFERS, DataBuffer); + + // + // Build the cdb for mode sense. + // + cdb = (PCDB)passThrough->ScsiPassThrough.Cdb; + cdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE; + cdb->MODE_SENSE.Dbd = 1; + cdb->MODE_SENSE.PageCode = 0xA; + cdb->MODE_SENSE.SubPageCode = 0x01; + cdb->MODE_SENSE.AllocationLength = sizeof(SPC3_CONTROL_EXTENSION_MODE_PAGE); + + DsmSendDeviceIoControlSynchronous(IOCTL_SCSI_PASS_THROUGH, + TargetDevice, + passThrough, + passThrough, + length, + length, + FALSE, + &ioStatus); + + status = ioStatus.Status; + senseData = (PSENSE_DATA)(passThrough->SenseInfoBuffer); + + if ((passThrough->ScsiPassThrough.ScsiStatus == SCSISTAT_GOOD) && (NT_SUCCESS(status))) { + + controlExtensionPage = (PSPC3_CONTROL_EXTENSION_MODE_PAGE)(passThrough->DataBuffer); + + if (controlExtensionPage->ImplicitALUAEnable) { + + controlExtensionPage->ImplicitALUAEnable = 0; + +__Retry_ModeSelect: + + RtlZeroMemory(passThrough->SenseInfoBuffer, passThrough->ScsiPassThrough.SenseInfoLength); + + passThrough->ScsiPassThrough.DataIn = 0; + + // + // Build the cdb for mode select. + // + RtlZeroMemory(cdb, 6); + cdb->MODE_SELECT.OperationCode = SCSIOP_MODE_SELECT; + cdb->MODE_SELECT.SPBit = 0; + cdb->MODE_SELECT.PFBit = 1; + cdb->MODE_SELECT.ParameterListLength = sizeof(SPC3_CONTROL_EXTENSION_MODE_PAGE); + + length = sizeof(SCSI_PASS_THROUGH_WITH_BUFFERS); + + DsmSendDeviceIoControlSynchronous(IOCTL_SCSI_PASS_THROUGH, + TargetDevice, + passThrough, + passThrough, + length, + length, + FALSE, + &ioStatus); + + status = ioStatus.Status; + senseData = (PSENSE_DATA)(passThrough->SenseInfoBuffer); + + if ((passThrough->ScsiPassThrough.ScsiStatus == SCSISTAT_GOOD) && (NT_SUCCESS(status))) { + + implicitDisabled = TRUE; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpDisableImplicitStateTransition (DevObj %p): Implicit transitions turned off successfully.\n", + TargetDevice)); + + } else if ((passThrough->ScsiPassThrough.ScsiStatus == SCSISTAT_CHECK_CONDITION) && + (NT_SUCCESS(status)) && + (DsmpShouldRetryPassThroughRequest(senseData, passThrough->ScsiPassThrough.SenseInfoLength))) { + + // + // Retry the request + // + goto __Retry_ModeSelect; + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpDisableImplicitStateTransition (DevObj %p): ModeSelect failed - NTStatus 0x%x, ScsiStatus 0x%x.\n", + TargetDevice, + status, + passThrough->ScsiPassThrough.ScsiStatus)); + } + } else { + + implicitDisabled = TRUE; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpDisableImplicitStateTransition (DevObj %p): Implicit transitions already turned OFF.\n", + TargetDevice)); + } + } else if ((passThrough->ScsiPassThrough.ScsiStatus == SCSISTAT_CHECK_CONDITION) && + (NT_SUCCESS(status)) && + (DsmpShouldRetryPassThroughRequest(senseData, passThrough->ScsiPassThrough.SenseInfoLength))) { + + length = sizeof(SCSI_PASS_THROUGH_WITH_BUFFERS); + + // + // Retry the request + // + RtlZeroMemory(passThrough, length); + goto __Retry_ModeSense; + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpDisableImplicitStateTransition (DevObj %p): ModeSense failed - NTStatus 0x%x, ScsiStatus 0x%x.\n", + TargetDevice, + status, + passThrough->ScsiPassThrough.ScsiStatus)); + } + +__Exit_DsmpDisableImplicitStateTransition: + + // + // Free the passthrough + data buffer. + // + if (passThrough) { + DsmpFreePool(passThrough); + } + + // + // Return whether IALUAE is set to 0. + // + if (DisableImplicit) { + + *DisableImplicit = implicitDisabled; + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpDisableImplicitStateTransition (DevObj %p): Exiting function with status %x.\n", + TargetDevice, + status)); + + return status; +} + + +PWSTR +DsmpBuildHardwareId( + _In_ IN PDSM_DEVICE_INFO DeviceInfo + ) +/*++ + +Routine Description: + + Construct a string concatinating VendorId with ProductId. + +Arguements: + + DeviceInfo - Device Extension + +Return Value : + + NULL terminated hardware id if it was built successfully. + NULL in case of failure. + +--*/ +{ + PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor; + PWSTR hardwareId = NULL; + SIZE_T vendorIDLength = 0; + SIZE_T productIDLength = 0; + PCSZ vendorIdOffset; + PCSZ productIdOffset; + SIZE_T sizeNeeded; + NTSTATUS status = STATUS_SUCCESS; + ANSI_STRING ansiString; + UNICODE_STRING unicodeString; + ULONG offset; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpBuildHardwareId (DevInfo %p): Entering function.\n", + DeviceInfo)); + + deviceDescriptor = &(DeviceInfo->Descriptor); + + // + // Save the vendorid and productid offset in Device Descriptor + // + offset = deviceDescriptor->ProductIdOffset; + if ((offset != 0) && (offset != MAXULONG)) { + + productIDLength = (strlen(((PCHAR)deviceDescriptor) + offset) * sizeof(WCHAR)) + WNULL_SIZE; + } + + offset = deviceDescriptor->VendorIdOffset; + if ((offset != 0) && (offset != MAXULONG)) { + + vendorIDLength = (strlen(((PCHAR)deviceDescriptor) + offset) * sizeof(WCHAR)) + WNULL_SIZE; + } + + if (!vendorIDLength || !productIDLength) { + + status = STATUS_UNSUCCESSFUL; + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_PNP, + "DsmpBuildHardwareId (DevInfo %p): Invalid vendor and/or product id.\n", + DeviceInfo)); + + goto __Exit_DsmpBuildHardwareId; + } + + sizeNeeded = vendorIDLength + productIDLength; + hardwareId = DsmpAllocatePool(NonPagedPoolNx, sizeNeeded, DSM_TAG_DEV_HARDWARE_ID); + if (!hardwareId) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpBuildHardwareId (DevInfo %p): Failed to allocate memory for device name.\n", + DeviceInfo)); + + status = STATUS_INSUFFICIENT_RESOURCES; + goto __Exit_DsmpBuildHardwareId; + } + + // + // Build the NULL terminated hardwareId whose format is : + // + // VendorIdProductId + // + vendorIdOffset = (PCSZ)((PUCHAR)deviceDescriptor + deviceDescriptor->VendorIdOffset); + RtlInitAnsiString(&ansiString, vendorIdOffset); + unicodeString.Length = 0; + unicodeString.MaximumLength = (USHORT)vendorIDLength; + unicodeString.Buffer = hardwareId; + + status = RtlAnsiStringToUnicodeString(&unicodeString, + &ansiString, + FALSE); + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpBuildHardwareId (DevInfo %p): Failed to convert vendor id to unicode string.\n", + DeviceInfo)); + + goto __Exit_DsmpBuildHardwareId; + } + + productIdOffset = (PCSZ)((PUCHAR)deviceDescriptor + deviceDescriptor->ProductIdOffset); + RtlInitAnsiString(&ansiString, productIdOffset); + unicodeString.Length = 0; + unicodeString.MaximumLength = (USHORT)productIDLength; + unicodeString.Buffer = hardwareId + strlen(((PCHAR)deviceDescriptor) + offset); + + status = RtlAnsiStringToUnicodeString(&unicodeString, + &ansiString, + FALSE); + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpBuildHardwareId (DevInfo %p): Failed to convert product id to unicode string.\n", + DeviceInfo)); + + goto __Exit_DsmpBuildHardwareId; + } + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpBuildHardwareId (DevInfo %p): HardwareId is %ws.\n", + DeviceInfo, + hardwareId)); + +__Exit_DsmpBuildHardwareId: + + if (hardwareId && !NT_SUCCESS(status)) { + DsmpFreePool(hardwareId); + hardwareId = NULL; + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpBuildHardwareId (DevInfo %p): Exiting function with deviceName %ws.\n", + DeviceInfo, + hardwareId)); + + return hardwareId; +} + + +PWSTR +DsmpBuildDeviceNameLegacyPage0x80( + _In_ IN PDSM_DEVICE_INFO DeviceInfo + ) +/*++ + +Routine Description: + + Construct a string from VendorId, ProductId, and SerialNumber (page 0x80 + info) of the device. + +Arguements: + + DeviceInfo - Device Extension + +Return Value : + + STATUS_SUCCESS if the device name was built successfully. + + Appropriate NTSTATUS code on failure + +--*/ +{ + PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor; + PWCHAR deviceName = NULL; + PWCHAR tmpPtr; + PWCHAR vendorID = NULL; + PWCHAR productID = NULL; + PWCHAR serialID = NULL; + ANSI_STRING ansiString; + UNICODE_STRING unicodeString; + UNICODE_STRING unicodeDeviceName; + SIZE_T vendorIDLength = 0; + SIZE_T productIDLength = 0; + SIZE_T serialIDLength = 0; + ULONG offset; + SIZE_T sizeNeeded; + NTSTATUS status = STATUS_SUCCESS; + + PAGED_CODE(); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpBuildDeviceNameLegacyPage0x80 (DevInfo %p): Entering function.\n", + DeviceInfo)); + + deviceDescriptor = &(DeviceInfo->Descriptor); + + // + // Save the vendorid, productid, and serialnumber offset + // in Device Descriptor + // + offset = deviceDescriptor->VendorIdOffset; + if ((offset != 0) && (offset != MAXULONG)) { + + vendorIDLength = (strlen(((PCHAR)deviceDescriptor) + offset) * sizeof(WCHAR)) + WNULL_SIZE; + } + + offset = deviceDescriptor->ProductIdOffset; + if ((offset != 0) && (offset != MAXULONG)) { + + productIDLength = (strlen(((PCHAR)deviceDescriptor) + offset) * sizeof(WCHAR)) + WNULL_SIZE; + } + + offset = deviceDescriptor->SerialNumberOffset; + if ((offset != 0) && (offset != MAXULONG)) { + + serialIDLength = (strlen(((PCHAR)deviceDescriptor) + offset) * sizeof(WCHAR)) + WNULL_SIZE; + } + + // + // Allocate buffers to use to convert the IDs from ANSI to Unicode and + // eventually build the device name. + // + if (vendorIDLength > 0) { + vendorID = (PWCHAR)DsmpAllocatePool(NonPagedPoolNx, vendorIDLength, DSM_TAG_DEV_NAME); + if (!vendorID) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpBuildDeviceNameLegacyPage0x80 (DevInfo %p): Failed to allocate memory for vendor ID.\n", + DeviceInfo)); + + status = STATUS_INSUFFICIENT_RESOURCES; + } + } + + if (productIDLength > 0) { + productID = (PWCHAR)DsmpAllocatePool(NonPagedPoolNx, productIDLength, DSM_TAG_DEV_NAME); + if (!productID) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpBuildDeviceNameLegacyPage0x80 (DevInfo %p): Failed to allocate memory for product ID.\n", + DeviceInfo)); + + status = STATUS_INSUFFICIENT_RESOURCES; + } + } + + if (serialIDLength > 0) { + serialID = (PWCHAR)DsmpAllocatePool(NonPagedPoolNx, serialIDLength, DSM_TAG_DEV_NAME); + if (!serialID) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpBuildDeviceNameLegacyPage0x80 (DevInfo %p): Failed to allocate memory for serial ID.\n", + DeviceInfo)); + + status = STATUS_INSUFFICIENT_RESOURCES; + } + } + + sizeNeeded = vendorIDLength + productIDLength + serialIDLength; + if (sizeNeeded > 0) { + + // + // Account for the terminating NULL if serial id is empty. + // + + sizeNeeded += (serialIDLength ? 0 : WNULL_SIZE); + + deviceName = (PWCHAR)DsmpAllocatePool(NonPagedPoolNx, sizeNeeded, DSM_TAG_DEV_NAME); + if (!deviceName) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpBuildDeviceNameLegacyPage0x80 (DevInfo %p): Failed to allocate memory for device name.\n", + DeviceInfo)); + + status = STATUS_INSUFFICIENT_RESOURCES; + } + } else { + + status = STATUS_UNSUCCESSFUL; + } + + if (!NT_SUCCESS(status)) { + goto __Exit_DsmpBuildDeviceNameLegacyPage0x80; + } + + // + // Build the NULL terminated device name whose format is : + // + // VendorId_ProductId_SerialNumber + // + + unicodeDeviceName.Length = 0; + unicodeDeviceName.MaximumLength = (USHORT)sizeNeeded; + unicodeDeviceName.Buffer = deviceName; + + if (vendorIDLength) { + + PCSZ vendorIdOffset; + + vendorIdOffset = (PCSZ)((PUCHAR)deviceDescriptor + + deviceDescriptor->VendorIdOffset); + + RtlInitAnsiString(&ansiString, vendorIdOffset); + + unicodeString.Length = 0; + unicodeString.MaximumLength = (USHORT) vendorIDLength; + unicodeString.Buffer = vendorID; + + status = RtlAnsiStringToUnicodeString(&unicodeString, + &ansiString, + FALSE); + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpBuildDeviceNameLegacyPage0x80 (DevInfo %p): Failed to convert vendor id to unicode string.\n", + DeviceInfo)); + + goto __Exit_DsmpBuildDeviceNameLegacyPage0x80; + } + + // + // If there are spaces in the id, set NULL at the first space. + // + tmpPtr = wcschr(vendorID, L' '); + if (tmpPtr != NULL) { + *tmpPtr = WNULL; + } + + status = RtlUnicodeStringCatString(&unicodeDeviceName, vendorID); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpBuildDeviceNameLegacyPage0x80 (DevInfo %p): Failed to concatenate vendor ID to device name.\n", + DeviceInfo)); + + goto __Exit_DsmpBuildDeviceNameLegacyPage0x80; + } + + RtlUnicodeStringCatString(&unicodeDeviceName, L"_"); + } + + if (productIDLength) { + + PCSZ productIdOffset; + + productIdOffset = (PCSZ)((PUCHAR)deviceDescriptor + + deviceDescriptor->ProductIdOffset); + + RtlInitAnsiString(&ansiString, productIdOffset); + + unicodeString.Length = 0; + unicodeString.MaximumLength = (USHORT) productIDLength; + unicodeString.Buffer = productID; + + status = RtlAnsiStringToUnicodeString(&unicodeString, + &ansiString, + FALSE); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpBuildDeviceNameLegacyPage0x80 (DevInfo %p): Failed to convert product id to unicode string.\n", + DeviceInfo)); + + goto __Exit_DsmpBuildDeviceNameLegacyPage0x80; + } + + // + // If there are spaces in the id, set NULL at the first space. + // + tmpPtr = wcschr(productID, L' '); + if (tmpPtr != NULL) { + *tmpPtr = WNULL; + } + + status = RtlUnicodeStringCatString(&unicodeDeviceName, productID); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpBuildDeviceNameLegacyPage0x80 (DevInfo %p): Failed to concatenate product ID to device name.\n", + DeviceInfo)); + + goto __Exit_DsmpBuildDeviceNameLegacyPage0x80; + } + + RtlUnicodeStringCatString(&unicodeDeviceName, L"_"); + } + + // + // Serial number + // + if (serialIDLength) { + + PCSZ serialNumberOffset; + + serialNumberOffset = (PCSZ)((PUCHAR)deviceDescriptor + + deviceDescriptor->SerialNumberOffset); + + RtlInitAnsiString(&ansiString, serialNumberOffset); + + unicodeString.Length = 0; + unicodeString.MaximumLength = (USHORT) serialIDLength; + unicodeString.Buffer = serialID; + + status = RtlAnsiStringToUnicodeString(&unicodeString, + &ansiString, + FALSE); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpBuildDeviceNameLegacyPage0x80 (DevInfo %p): Failed to convert serial number to unicode string.\n", + DeviceInfo)); + + goto __Exit_DsmpBuildDeviceNameLegacyPage0x80; + } + + // + // If there are spaces in the id, set NULL at the first space. + // + tmpPtr = wcschr(serialID, L' '); + if (tmpPtr != NULL) { + *tmpPtr = WNULL; + } + + status = RtlUnicodeStringCatString(&unicodeDeviceName, serialID); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpBuildDeviceNameLegacyPage0x80 (DevInfo %p): Failed to concatenate serial number to device name.\n", + DeviceInfo)); + + goto __Exit_DsmpBuildDeviceNameLegacyPage0x80; + } + } + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpBuildDeviceNameLegacyPage0x80 (DevInfo %p): Device Name is %ws.\n", + DeviceInfo, + deviceName)); + +__Exit_DsmpBuildDeviceNameLegacyPage0x80: + + if (vendorID) { + DsmpFreePool(vendorID); + } + + if (productID) { + DsmpFreePool(productID); + } + + if (serialID) { + DsmpFreePool(serialID); + } + + if (deviceName && !NT_SUCCESS(status)) { + DsmpFreePool(deviceName); + deviceName = NULL; + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpBuildDeviceNameLegacyPage0x80 (DevInfo %p): Exiting function with deviceName %ws.\n", + DeviceInfo, + deviceName)); + + return deviceName; +} + + + +PWSTR +DsmpBuildDeviceName( + _In_ IN PDSM_DEVICE_INFO DeviceInfo, + _In_reads_(SerialNumberLength) IN PSTR SerialNumber, + _In_ IN SIZE_T SerialNumberLength + ) +/*++ + +Routine Description: + + Construct a string from VendorId, ProductId, and SerialNumber (page 0x83 + identifiers) of the device. + +Arguements: + + DeviceInfo - Device Extension + SerialNumber - Device serial number built from appropriate page 0x83 identifier + SerialNumberLength - Length (in chars) of the passed in serial number buffer + +Return Value : + + Device name if it was built successfully. + NULL in case of failure. + +--*/ +{ + PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor; + PWCHAR deviceName = NULL; + PWCHAR tmpPtr; + PWCHAR vendorID = NULL; + PWCHAR productID = NULL; + PWCHAR serialID = NULL; + ANSI_STRING ansiString; + UNICODE_STRING unicodeString; + UNICODE_STRING unicodeDeviceName; + SIZE_T vendorIDLength = 0; + SIZE_T productIDLength = 0; + SIZE_T serialIDLength = 0; + ULONG offset; + SIZE_T sizeNeeded; + NTSTATUS status = STATUS_SUCCESS; + + PAGED_CODE(); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpBuildDeviceName (DevInfo %p): Entering function.\n", + DeviceInfo)); + + deviceDescriptor = &(DeviceInfo->Descriptor); + + // + // Save the vendorid, productid, and serialnumber offset + // in Device Descriptor + // + offset = deviceDescriptor->VendorIdOffset; + if ((offset != 0) && (offset != MAXULONG)) { + + vendorIDLength = (strlen(((PCHAR)deviceDescriptor) + offset) * sizeof(WCHAR)) + WNULL_SIZE; + } + + offset = deviceDescriptor->ProductIdOffset; + if ((offset != 0) && (offset != -1)) { + + productIDLength = (strlen(((PCHAR)deviceDescriptor) + offset) * sizeof(WCHAR)) + WNULL_SIZE; + } + + if (SerialNumber) { + + serialIDLength = (SerialNumberLength * sizeof(WCHAR)) + WNULL_SIZE; + } + + // + // Allocate buffers to use to convert the IDs from ANSI to Unicode and + // eventually build the device name. + // + if (vendorIDLength > 0) { + vendorID = (PWCHAR)DsmpAllocatePool(NonPagedPoolNx, vendorIDLength, DSM_TAG_DEV_NAME); + if (!vendorID) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpBuildDeviceName (DevInfo %p): Failed to allocate memory for vendor ID.\n", + DeviceInfo)); + + status = STATUS_INSUFFICIENT_RESOURCES; + } + } + + if (productIDLength > 0) { + productID = (PWCHAR)DsmpAllocatePool(NonPagedPoolNx, productIDLength, DSM_TAG_DEV_NAME); + if (!productID) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpBuildDeviceName (DevInfo %p): Failed to allocate memory for product ID.\n", + DeviceInfo)); + + status = STATUS_INSUFFICIENT_RESOURCES; + } + } + + if (serialIDLength > 0) { + serialID = (PWCHAR)DsmpAllocatePool(NonPagedPoolNx, serialIDLength, DSM_TAG_DEV_NAME); + if (!serialID) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpBuildDeviceName (DevInfo %p): Failed to allocate memory for serial ID.\n", + DeviceInfo)); + + status = STATUS_INSUFFICIENT_RESOURCES; + } + } + + sizeNeeded = vendorIDLength + productIDLength + serialIDLength; + if (sizeNeeded > 0) { + + // + // Account for the terminating NULL if serial id is empty. + // + + sizeNeeded += (serialIDLength ? 0 : WNULL_SIZE); + + deviceName = (PWCHAR)DsmpAllocatePool(NonPagedPoolNx, sizeNeeded, DSM_TAG_DEV_NAME); + if (!deviceName) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpBuildDeviceName (DevInfo %p): Failed to allocate memory for device name.\n", + DeviceInfo)); + + status = STATUS_INSUFFICIENT_RESOURCES; + } + } else { + + status = STATUS_UNSUCCESSFUL; + } + + if (!NT_SUCCESS(status)) { + goto __Exit_DsmpBuildDeviceName; + } + + // + // Build the NULL terminated device name whose format is : + // + // VendorId_ProductId_SerialNumber + // + + unicodeDeviceName.Length = 0; + unicodeDeviceName.MaximumLength = (USHORT)sizeNeeded; + unicodeDeviceName.Buffer = deviceName; + + if (vendorIDLength) { + + PCSZ vendorIdOffset; + + vendorIdOffset = (PCSZ)((PUCHAR)deviceDescriptor + + deviceDescriptor->VendorIdOffset); + + RtlInitAnsiString(&ansiString, vendorIdOffset); + + unicodeString.Length = 0; + unicodeString.MaximumLength = (USHORT) vendorIDLength; + unicodeString.Buffer = vendorID; + + status = RtlAnsiStringToUnicodeString(&unicodeString, + &ansiString, + FALSE); + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpBuildDeviceName (DevInfo %p): Failed to convert vendor id to unicode string.\n", + DeviceInfo)); + + goto __Exit_DsmpBuildDeviceName; + } + + // + // If there are spaces in the id, set NULL at the first space. + // + tmpPtr = wcschr(vendorID, L' '); + if (tmpPtr != NULL) { + *tmpPtr = WNULL; + } + + status = RtlUnicodeStringCatString(&unicodeDeviceName, vendorID); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpBuildDeviceName (DevInfo %p): Failed to concatenate vendor ID to device name.\n", + DeviceInfo)); + + goto __Exit_DsmpBuildDeviceName; + } + + RtlUnicodeStringCatString(&unicodeDeviceName, L"_"); + } + + if (productIDLength) { + + PCSZ productIdOffset; + + productIdOffset = (PCSZ)((PUCHAR)deviceDescriptor + + deviceDescriptor->ProductIdOffset); + + RtlInitAnsiString(&ansiString, productIdOffset); + + unicodeString.Length = 0; + unicodeString.MaximumLength = (USHORT) productIDLength; + unicodeString.Buffer = productID; + + status = RtlAnsiStringToUnicodeString(&unicodeString, + &ansiString, + FALSE); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpBuildDeviceName (DevInfo %p): Failed to convert product id to unicode string.\n", + DeviceInfo)); + + goto __Exit_DsmpBuildDeviceName; + } + + // + // If there are spaces in the id, set NULL at the first space. + // + tmpPtr = wcschr(productID, L' '); + if (tmpPtr != NULL) { + *tmpPtr = WNULL; + } + + status = RtlUnicodeStringCatString(&unicodeDeviceName, productID); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpBuildDeviceName (DevInfo %p): Failed to concatenate product ID to device name.\n", + DeviceInfo)); + + goto __Exit_DsmpBuildDeviceName; + } + + RtlUnicodeStringCatString(&unicodeDeviceName, L"_"); + } + + // + // Serial number + // + if (serialIDLength) { + + PSTR serialNumberOffset; + + serialNumberOffset = SerialNumber; + + RtlInitAnsiString(&ansiString, serialNumberOffset); + + unicodeString.Length = 0; + unicodeString.MaximumLength = (USHORT) serialIDLength; + unicodeString.Buffer = serialID; + + status = RtlAnsiStringToUnicodeString(&unicodeString, + &ansiString, + FALSE); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpBuildDeviceName (DevInfo %p): Failed to convert serial number to unicode string.\n", + DeviceInfo)); + + goto __Exit_DsmpBuildDeviceName; + } + + // + // If there are spaces in the id, set NULL at the first space. + // + tmpPtr = wcschr(serialID, L' '); + if (tmpPtr != NULL) { + *tmpPtr = WNULL; + } + + status = RtlUnicodeStringCatString(&unicodeDeviceName, serialID); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpBuildDeviceName (DevInfo %p): Failed to concatenate serial number to device name.\n", + DeviceInfo)); + + goto __Exit_DsmpBuildDeviceName; + } + } + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpBuildDeviceName (DevInfo %p): Device Name is %ws.\n", + DeviceInfo, + deviceName)); + +__Exit_DsmpBuildDeviceName: + + if (vendorID) { + DsmpFreePool(vendorID); + } + + if (productID) { + DsmpFreePool(productID); + } + + if (serialID) { + DsmpFreePool(serialID); + } + + if (deviceName && !NT_SUCCESS(status)) { + DsmpFreePool(deviceName); + deviceName = NULL; + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpBuildDeviceName (DevInfo %p): Exiting function with deviceName %ws.\n", + DeviceInfo, + deviceName)); + + return deviceName; +} + + +NTSTATUS +DsmpApplyDeviceNameCorrection( + _In_ IN PDSM_DEVICE_INFO DeviceInfo, + _In_reads_(DeviceNameLegacyLen) PWSTR DeviceNameLegacy, + _In_ IN SIZE_T DeviceNameLegacyLen, + _In_reads_(DeviceNameLen) PWSTR DeviceName, + _In_ IN SIZE_T DeviceNameLen + ) +/*++ + +Routine Description: + + If the registry has a key name built with a legacy device name, this + function updates the key name with the current device name. + +Arguements: + + DeviceInfo - Device instance + DeviceNameLegacy - Device name built using legacy methods. + DeviceNameLegacyLen - Number of chars (including NULL) of the DeviceNameLegacy buffer. + DeviceName - Device name built using current methods. + DeviceNameLen - Number of chars (including NULL) of the DeviceName buffer. + +Return Value : + + STATUS_SUCCESS if the device's key was updated successfully. + + Appropriate NTSTATUS code on failure + +--*/ +{ + HANDLE lbSettingsKey = NULL; + HANDLE deviceKeyLegacy = NULL; + HANDLE deviceKey = NULL; + OBJECT_ATTRIBUTES objectAttributes; + NTSTATUS status; + UNICODE_STRING deviceNameLegacy; + UNICODE_STRING deviceName; + + PAGED_CODE(); + + UNREFERENCED_PARAMETER(DeviceNameLen); + UNREFERENCED_PARAMETER(DeviceNameLegacyLen); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpApplyDeviceNameCorrection (DevInfo %p): Entering function.\n", + DeviceInfo)); + + // + // First open LoadBalanceSettings key under the service key. + // + status = DsmpOpenLoadBalanceSettingsKey(KEY_ALL_ACCESS, &lbSettingsKey); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpApplyDeviceNameCorrection (DevInfo %p): Failed to open LB Settings key. Status %x.\n", + DeviceInfo, + status)); + + goto __Exit_DsmpApplyDeviceNameCorrection; + } + + RtlInitUnicodeString(&deviceNameLegacy, DeviceNameLegacy); + + InitializeObjectAttributes(&objectAttributes, + &deviceNameLegacy, + (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE), + lbSettingsKey, + (PSECURITY_DESCRIPTOR) NULL); + + // + // Open the old device key under DsmLoadBalanceSettings key. + // The name of this key is the one built using legacy methods - either a + // serial number from VPD page 0x80 or an aliased serial number from VPD + // page 0x83. + // + status = ZwOpenKey(&deviceKeyLegacy, + KEY_ALL_ACCESS, + &objectAttributes); + + if (NT_SUCCESS(status)) { + + ULONG disposition; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpApplyDeviceNameCorrection (DevInfo %p): Key with old device name exists.\n", + DeviceInfo)); + + RtlInitUnicodeString(&deviceName, DeviceName); + + InitializeObjectAttributes(&objectAttributes, + &deviceName, + (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE), + lbSettingsKey, + (PSECURITY_DESCRIPTOR) NULL); + + // + // Since the old name key exists, create one with the new name. + // + status = ZwCreateKey(&deviceKey, + KEY_ALL_ACCESS, + &objectAttributes, + 0, + NULL, + REG_OPTION_NON_VOLATILE, + &disposition); + + if (NT_SUCCESS(status)) { + + // + // The new key shouldn't exist if the old one does. + // If it does, it indicates a error occured the previous time + // this was tried, so just copy over the old subtree anyways now. + // + DSM_ASSERT(disposition == REG_CREATED_NEW_KEY); + + // + // Copy over the entire subtree of the old key over to the new key. + // + status = DsmpRegCopyTree(deviceKeyLegacy, deviceKey); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpApplyDeviceNameCorrection (DevInfo %p): Failed to copy over the old device key's subtree. Status %x.\n", + DeviceInfo, + status)); + + goto __Exit_DsmpApplyDeviceNameCorrection; + } + + // + // Delete the old key name. + // + status = DsmpRegDeleteTree(deviceKeyLegacy); + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpApplyDeviceNameCorrection (DevInfo %p): Failed to delete the old device key's subtree. Status %x.\n", + DeviceInfo, + status)); + + goto __Exit_DsmpApplyDeviceNameCorrection; + } + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpApplyDeviceNameCorrection (DevInfo %p): Failed to create the new device key. Status %x.\n", + DeviceInfo, + status)); + + goto __Exit_DsmpApplyDeviceNameCorrection; + } + + } else if (status == STATUS_INVALID_HANDLE || + status == STATUS_OBJECT_NAME_NOT_FOUND) { + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpApplyDeviceNameCorrection (DevInfo %p): Key with old device name does not exist.\n", + DeviceInfo)); + + status = STATUS_SUCCESS; + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpApplyDeviceNameCorrection (DevInfo %p): Failed to query key with old device name. Status %x\n", + DeviceInfo, + status)); + } + +__Exit_DsmpApplyDeviceNameCorrection: + + if (deviceKey) { + ZwClose(deviceKey); + } + + if (deviceKeyLegacy) { + ZwClose(deviceKeyLegacy); + } + + if (lbSettingsKey) { + ZwClose(lbSettingsKey); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpApplyDeviceNameCorrection (DevInfo %p): Exiting function with status %x\n", + DeviceInfo, + status)); + + return status; +} + + +NTSTATUS +DsmpQueryDeviceLBPolicyFromRegistry( + _In_ PDSM_DEVICE_INFO DeviceInfo, + _In_ PWSTR RegistryKeyName, + _Inout_ PDSM_LOAD_BALANCE_TYPE LoadBalanceType, + _Inout_ PULONGLONG PreferredPath, + _Inout_ PUCHAR ExplicitlySet + ) +/*++ + +Routine Description: + + Query the saved load balance policy and preferred path for this device from + the registry. + Also returns whether this setting was explicitly set via WMI call to SetLBPolicy, + (as opposed to the settings being made based on defaults determined through the + storage's ALUA capabilities). + +Arguements: + + DeviceInfo - The instance of the LUN through a paricular path + RegistryKeyName - DeviceName representing this LUN + LoadBalanceType - Type of LB policy. + PreferredPath - The preferred path for the device. + ExplicitlySet - Flag reflecting if LB policy was explicitly set. + +Return Value : + + STATUS_SUCCESS if we were able to successfully query the registry for the info. + + Appropriate NTSTATUS code on failure + +--*/ +{ + HANDLE lbSettingsKey = NULL; + HANDLE deviceKey = NULL; + UNICODE_STRING subKeyName; + OBJECT_ATTRIBUTES objectAttributes; + NTSTATUS status; + UNICODE_STRING keyValueName; + ULONG length; + struct _explicitSet { + KEY_VALUE_PARTIAL_INFORMATION KeyValueInfo; + UCHAR Data; + } explicitSet; + struct _preferredPath { + KEY_VALUE_PARTIAL_INFORMATION KeyValueInfo; + ULONGLONG Data; + } preferredPath; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpQueryDeviceLBPolicyFromRegistry (DevInfo %p): Entering function.\n", + DeviceInfo)); + + // + // Query the Load Balance settings for the given device from the registry. + // First open LoadBalanceSettings key under the service key. + // + status = DsmpOpenLoadBalanceSettingsKey(KEY_ALL_ACCESS, &lbSettingsKey); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpQueryDeviceLBPolicyFromRegistry (DevInfo %p): Failed to open LB Settings key. Status %x.\n", + DeviceInfo, + status)); + + goto __Exit_DsmpQueryDeviceLBPolicyFromRegistry; + } + + RtlInitUnicodeString(&subKeyName, RegistryKeyName); + + InitializeObjectAttributes(&objectAttributes, + &subKeyName, + (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE), + lbSettingsKey, + (PSECURITY_DESCRIPTOR) NULL); + + // + // Create or Open the device key under DsmLoadBalanceSettings key. + // The name of this key is the one built in DsmpBuildDeviceName + // + status = ZwCreateKey(&deviceKey, + KEY_ALL_ACCESS, + &objectAttributes, + 0, + NULL, + REG_OPTION_NON_VOLATILE, + NULL); + + if (NT_SUCCESS(status)) { + + RTL_QUERY_REGISTRY_TABLE queryTable[2]; + + RtlZeroMemory(queryTable, sizeof(queryTable)); + + queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | + RTL_QUERY_REGISTRY_REQUIRED; + queryTable[0].Name = DSM_LOAD_BALANCE_POLICY; + queryTable[0].EntryContext = LoadBalanceType; + + status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE, + deviceKey, + queryTable, + deviceKey, + NULL); + if (NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpQueryDeviceLBPolicyFromRegistry (DevInfo %p): LB Policy is %d.\n", + DeviceInfo, + *LoadBalanceType)); + + } else if (status == STATUS_OBJECT_NAME_NOT_FOUND) { + + // + // The device key must have been newly created. + // Set the default load balance policy for this device + // + + status = RtlWriteRegistryValue(RTL_REGISTRY_HANDLE, + deviceKey, + DSM_LOAD_BALANCE_POLICY, + REG_DWORD, + LoadBalanceType, + sizeof(ULONG)); + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpQueryDeviceLBPolicyFromRegistry (DevInfo %p): Failed to write LB policy. Status %x.\n", + DeviceInfo, + status)); + + goto __Exit_DsmpQueryDeviceLBPolicyFromRegistry; + } + } + + if (NT_SUCCESS(status)) { + + RtlInitUnicodeString(&keyValueName, DSM_POLICY_EXPLICITLY_SET); + status = ZwQueryValueKey(deviceKey, + &keyValueName, + KeyValuePartialInformation, + &explicitSet, + sizeof(explicitSet), + &length); + + if (NT_SUCCESS(status)) { + + NT_ASSERT(explicitSet.KeyValueInfo.DataLength == sizeof(UCHAR)); + + *ExplicitlySet = *((UCHAR UNALIGNED *)&(explicitSet.KeyValueInfo.Data)); + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpQueryDeviceLBPolicyFromRegistry (DevInfo %p): ExplicitlySet is %!bool!.\n", + DeviceInfo, + *ExplicitlySet)); + + } else if (status == STATUS_OBJECT_NAME_NOT_FOUND) { + + *ExplicitlySet = FALSE; + + // + // The device key must have been newly created. + // Set ExplicitlySet to 0 to indicate that the default was used. + // + status = RtlWriteRegistryValue(RTL_REGISTRY_HANDLE, + deviceKey, + DSM_POLICY_EXPLICITLY_SET, + REG_BINARY, + ExplicitlySet, + sizeof(UCHAR)); + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpQueryDeviceLBPolicyFromRegistry (DevInfo %p): Failed to write ExplicitlySet. Status %x.\n", + DeviceInfo, + status)); + } + } + + if (NT_SUCCESS(status)) { + + RtlInitUnicodeString(&keyValueName, DSM_PREFERRED_PATH); + status = ZwQueryValueKey(deviceKey, + &keyValueName, + KeyValuePartialInformation, + &preferredPath, + sizeof(preferredPath), + &length); + + if (NT_SUCCESS(status)) { + + NT_ASSERT(preferredPath.KeyValueInfo.DataLength == sizeof(ULONGLONG)); + + *PreferredPath = *((ULONGLONG UNALIGNED *)&(preferredPath.KeyValueInfo.Data)); + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpQueryDeviceLBPolicyFromRegistry (DevInfo %p): PreferredPath is %I64x.\n", + DeviceInfo, + *PreferredPath)); + + } else if (status == STATUS_OBJECT_NAME_NOT_FOUND) { + + *PreferredPath = (ULONGLONG)((ULONG_PTR)MAXULONG); + + // + // The device key must have been newly created. + // Set a bogus preferred path as default. + // + status = RtlWriteRegistryValue(RTL_REGISTRY_HANDLE, + deviceKey, + DSM_PREFERRED_PATH, + REG_BINARY, + PreferredPath, + sizeof(ULONGLONG)); + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpQueryDeviceLBPolicyFromRegistry (DevInfo %p): Failed to write PreferredPath. Status %x.\n", + DeviceInfo, + status)); + } + } + } + } + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpQueryDeviceLBPolicyFromRegistry (DevInfo %p): Failed to create LB policy registry key. Status %x.\n", + DeviceInfo, + status)); + + deviceKey = NULL; + } + +__Exit_DsmpQueryDeviceLBPolicyFromRegistry: + + if (deviceKey) { + ZwClose(deviceKey); + } + + if (lbSettingsKey) { + ZwClose(lbSettingsKey); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpQueryDeviceLBPolicyFromRegistry (DevInfo %p): Exiting function with status %x.\n", + DeviceInfo, + status)); + + return status; +} + + +NTSTATUS +DsmpQueryTargetLBPolicyFromRegistry( + _In_ IN PDSM_DEVICE_INFO DeviceInfo, + _Out_ OUT PDSM_LOAD_BALANCE_TYPE LoadBalanceType, + _Out_ OUT PULONGLONG PreferredPath + ) +/*++ + +Routine Description: + + Query the load balance policy for the VID/PID of the passed in device from + the registry if it has been set. + +Arguements: + + DeviceInfo - Device's whose VID/PID we need to compare against. + LoadBalanceType - Type of LB policy. + PreferredPath - The preferred path for the device. + +Return Value : + + STATUS_SUCCESS if we were able to successfully query the registry for the info. + + Appropriate NTSTATUS code on failure + +--*/ +{ + HANDLE targetsLBSettingKey = NULL; + HANDLE targetKey = NULL; + UNICODE_STRING subKeyName; + OBJECT_ATTRIBUTES objectAttributes; + NTSTATUS status = STATUS_INVALID_PARAMETER; + UNICODE_STRING keyValueName; + ULONG length; + struct _preferredPath { + KEY_VALUE_PARTIAL_INFORMATION KeyValueInfo; + ULONGLONG Data; + } preferredPath; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpQueryTargetLBPolicyFromRegistry (DevInfo %p): Entering function.\n", + DeviceInfo)); + + if (!LoadBalanceType || !PreferredPath) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpQueryTargetLBPolicyFromRegistry (DevInfo %p): Invalid parameter.\n", + DeviceInfo)); + + goto __Exit_DsmpQueryTargetLBPolicyFromRegistry; + } + + if (!DeviceInfo->Group->HardwareId) { + + status = STATUS_UNSUCCESSFUL; + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpQueryTargetLBPolicyFromRegistry (DevInfo %p): Couldn't build hardware id for passed in device.\n", + DeviceInfo)); + + goto __Exit_DsmpQueryTargetLBPolicyFromRegistry; + } + + // + // Query the Load Balance settings for the given target from the registry. + // First open TargetsLoadBalanceSetting key under the service key. + // + status = DsmpOpenTargetsLoadBalanceSettingKey(KEY_ALL_ACCESS, &targetsLBSettingKey); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpQueryTargetLBPolicyFromRegistry (DevInfo %p): Failed to open Targets LB Setting key. Status %x.\n", + DeviceInfo, + status)); + + goto __Exit_DsmpQueryTargetLBPolicyFromRegistry; + } + + RtlInitUnicodeString(&subKeyName, DeviceInfo->Group->HardwareId); + + InitializeObjectAttributes(&objectAttributes, + &subKeyName, + (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE), + targetsLBSettingKey, + (PSECURITY_DESCRIPTOR) NULL); + + // + // Open the VID/PID key under DsmTargetsLoadBalanceSetting key. + // + status = ZwOpenKey(&targetKey, KEY_ALL_ACCESS, &objectAttributes); + + if (NT_SUCCESS(status)) { + + RTL_QUERY_REGISTRY_TABLE queryTable[2]; + + RtlZeroMemory(queryTable, sizeof(queryTable)); + + queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | + RTL_QUERY_REGISTRY_REQUIRED; + queryTable[0].Name = DSM_LOAD_BALANCE_POLICY; + queryTable[0].EntryContext = LoadBalanceType; + + status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE, + targetKey, + queryTable, + targetKey, + NULL); + if (NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpQueryTargetLBPolicyFromRegistry (DevInfo %p): LB Policy is %d.\n", + DeviceInfo, + *LoadBalanceType)); + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpQueryTargetLBPolicyFromRegistry (DevInfo %p): Failed to query LB Policy - error %x.\n", + DeviceInfo, + status)); + } + + if (NT_SUCCESS(status)) { + + RtlInitUnicodeString(&keyValueName, DSM_PREFERRED_PATH); + status = ZwQueryValueKey(targetKey, + &keyValueName, + KeyValuePartialInformation, + &preferredPath, + sizeof(preferredPath), + &length); + + if (NT_SUCCESS(status)) { + + NT_ASSERT(preferredPath.KeyValueInfo.DataLength == sizeof(ULONGLONG)); + + *PreferredPath = *((ULONGLONG UNALIGNED *)&(preferredPath.KeyValueInfo.Data)); + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpQueryTargetLBPolicyFromRegistry (DevInfo %p): PreferredPath is %I64x.\n", + DeviceInfo, + *PreferredPath)); + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpQueryTargetLBPolicyFromRegistry (DevInfo %p): Failed to query PreferredPath. Status %x.\n", + DeviceInfo, + status)); + } + } + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpQueryTargetLBPolicyFromRegistry (DevInfo %p): Failed to open LB policy registry key. Status %x.\n", + DeviceInfo, + status)); + + targetKey = NULL; + } + +__Exit_DsmpQueryTargetLBPolicyFromRegistry: + + if (targetKey) { + ZwClose(targetKey); + } + + if (targetsLBSettingKey) { + ZwClose(targetsLBSettingKey); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpQueryTargetLBPolicyFromRegistry (DevInfo %p): Exiting function with status %x.\n", + DeviceInfo, + status)); + + return status; +} + + +NTSTATUS +DsmpQueryDsmLBPolicyFromRegistry( + _Out_ OUT PDSM_LOAD_BALANCE_TYPE LoadBalanceType, + _Out_ OUT PULONGLONG PreferredPath + ) +/*++ + +Routine Description: + + Query the overall load balance policy for MSDSM controlled devices from + the registry if it has been set. + +Arguements: + + LoadBalanceType - Type of LB policy. + PreferredPath - The preferred path for the device. + +Return Value : + + STATUS_SUCCESS if we were able to successfully query the registry for the info. + + Appropriate NTSTATUS code on failure + +--*/ +{ + HANDLE parametersKey = NULL; + NTSTATUS status = STATUS_INVALID_PARAMETER; + UNICODE_STRING keyValueName; + RTL_QUERY_REGISTRY_TABLE queryTable[2]; + ULONG length; + struct _preferredPath { + KEY_VALUE_PARTIAL_INFORMATION KeyValueInfo; + ULONGLONG Data; + } preferredPath; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpQueryDsmLBPolicyFromRegistry: Entering function.\n")); + + if (!LoadBalanceType || !PreferredPath) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpQueryDsmLBPolicyFromRegistry: Invalid parameter.\n")); + + goto __Exit_DsmpQueryDsmLBPolicyFromRegistry; + } + + // + // Query the overall default Load Balance settings for MSDSM from the registry. + // First open the Parameters key under the service key. + // + status = DsmpOpenDsmServicesParametersKey(KEY_ALL_ACCESS, ¶metersKey); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpQueryDsmLBPolicyFromRegistry: Failed to open Parameters key. Status %x.\n", + status)); + + goto __Exit_DsmpQueryDsmLBPolicyFromRegistry; + } + + RtlZeroMemory(queryTable, sizeof(queryTable)); + + queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | + RTL_QUERY_REGISTRY_REQUIRED; + queryTable[0].Name = DSM_LOAD_BALANCE_POLICY; + queryTable[0].EntryContext = LoadBalanceType; + + status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE, + parametersKey, + queryTable, + parametersKey, + NULL); + if (NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpQueryDsmLBPolicyFromRegistry: LB Policy is %d.\n", + *LoadBalanceType)); + + } else { + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpQueryDsmLBPolicyFromRegistry: Failed to query LB policy. Status %x.\n", + status)); + + goto __Exit_DsmpQueryDsmLBPolicyFromRegistry; + } + + if (NT_SUCCESS(status)) { + + RtlInitUnicodeString(&keyValueName, DSM_PREFERRED_PATH); + status = ZwQueryValueKey(parametersKey, + &keyValueName, + KeyValuePartialInformation, + &preferredPath, + sizeof(preferredPath), + &length); + + if (NT_SUCCESS(status)) { + + NT_ASSERT(preferredPath.KeyValueInfo.DataLength == sizeof(ULONGLONG)); + + *PreferredPath = *((ULONGLONG UNALIGNED *)&(preferredPath.KeyValueInfo.Data)); + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpQueryDsmLBPolicyFromRegistry: PreferredPath is %I64x.\n", + *PreferredPath)); + + } else { + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpQueryDsmLBPolicyFromRegistry: Failed to query PreferredPath. Status %x.\n", + status)); + } + } + +__Exit_DsmpQueryDsmLBPolicyFromRegistry: + + if (parametersKey) { + ZwClose(parametersKey); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpQueryDsmLBPolicyFromRegistry: Exiting function with status %x.\n", + status)); + + return status; +} + + +NTSTATUS +DsmpSetDsmLBPolicyInRegistry( + _In_ IN DSM_LOAD_BALANCE_TYPE LoadBalanceType, + _In_ IN ULONGLONG PreferredPath + ) +/*++ + +Routine Description: + + Set the overall load balance policy for MSDSM controlled devices in + the registry. + Note: If the policy specified is 0, remove the currently set values + for policy and preferred path. + +Arguements: + + LoadBalanceType - Type of LB policy. + PreferredPath - The preferred path for devices controlled by DSM. + +Return Value : + + STATUS_SUCCESS if we were able to successfully set the info in the registry. + + Appropriate NTSTATUS code on failure + +--*/ +{ + HANDLE parametersKey = NULL; + NTSTATUS status; + UNICODE_STRING lbPolicyValueName; + UNICODE_STRING preferredPathValueName; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpSetDsmLBPolicyInRegistry: Entering function.\n")); + + // + // First open the Parameters key under the service key. + // + status = DsmpOpenDsmServicesParametersKey(KEY_ALL_ACCESS, ¶metersKey); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpSetDsmLBPolicyInRegistry: Failed to open Parameters key. Status %x.\n", + status)); + + goto __Exit_DsmpSetDsmLBPolicyInRegistry; + } + + RtlInitUnicodeString(&lbPolicyValueName, DSM_LOAD_BALANCE_POLICY); + RtlInitUnicodeString(&preferredPathValueName, DSM_PREFERRED_PATH); + + // + // If the LB policy is specified as 0, we need to delete the values. + // + if (LoadBalanceType < DSM_LB_FAILOVER) { + + status = ZwDeleteValueKey(parametersKey, &preferredPathValueName); + + if (NT_SUCCESS(status) || status == STATUS_OBJECT_NAME_NOT_FOUND) { + + status = ZwDeleteValueKey(parametersKey, &lbPolicyValueName); + } + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpSetDsmLBPolicyInRegistry: Failed to delete either preferredPath or lbPolicy. Status %x.\n", + status)); + } + } else { + + status = ZwSetValueKey(parametersKey, + &lbPolicyValueName, + 0, + REG_DWORD, + &LoadBalanceType, + sizeof(ULONG)); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpSetDsmLBPolicyInRegistry: Failed to set LB policy in registry. Status %x.\n", + status)); + + goto __Exit_DsmpSetDsmLBPolicyInRegistry; + } + + status = ZwSetValueKey(parametersKey, + &preferredPathValueName, + 0, + REG_BINARY, + &PreferredPath, + sizeof(ULONGLONG)); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpSetDsmLBPolicyInRegistry: Failed to set preferred path in registry. Status %x.\n", + status)); + } + } + +__Exit_DsmpSetDsmLBPolicyInRegistry: + + if (parametersKey) { + ZwClose(parametersKey); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpSetDsmLBPolicyInRegistry: Exiting function with status %x.\n", + status)); + + return status; +} + + +NTSTATUS +DsmpSetVidPidLBPolicyInRegistry( + _In_ IN PWSTR TargetHardwareId, + _In_ IN DSM_LOAD_BALANCE_TYPE LoadBalanceType, + _In_ IN ULONGLONG PreferredPath + ) +/*++ + +Routine Description: + + Set the default load balance policy for MSDSM controlled devices for + a particular target VID/PID in the registry. + Note: If the policy specified is 0, remove the subkey that matches + the passed in TargetHardwareId. + +Arguements: + + TargetHardwareId - The VID/PID for which a default LB policy is being set. + LoadBalanceType - Type of LB policy. + PreferredPath - The preferred path for devices controlled by DSM. + +Return Value : + + STATUS_SUCCESS if we were able to successfully set the info in the registry. + + Appropriate NTSTATUS code on failure + +--*/ +{ + HANDLE targetsLBSettingKey = NULL; + HANDLE targetSubKey = NULL; + NTSTATUS status; + UNICODE_STRING vidPidKeyName; + UNICODE_STRING lbPolicyValueName; + UNICODE_STRING preferredPathValueName; + OBJECT_ATTRIBUTES objectAttributes; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpSetVidPidLBPolicyInRegistry (%ws): Entering function.\n", + TargetHardwareId)); + + // + // First open the DsmTargetsLoadBalanceSetting key under the service's parameters key. + // + status = DsmpOpenTargetsLoadBalanceSettingKey(KEY_ALL_ACCESS, &targetsLBSettingKey); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpSetVidPidLBPolicyInRegistry (%ws): Failed to open Targets Policy settings key. Status %x.\n", + TargetHardwareId, + status)); + + goto __Exit_DsmpSetVidPidLBPolicyInRegistry; + } + + RtlInitUnicodeString(&vidPidKeyName, TargetHardwareId); + InitializeObjectAttributes(&objectAttributes, + &vidPidKeyName, + (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE), + targetsLBSettingKey, + (PSECURITY_DESCRIPTOR) NULL); + + // + // If the LB policy is specified as 0, we need to delete the values. + // + if (LoadBalanceType < DSM_LB_FAILOVER) { + + // + // Open the VID/PID key under DsmTargetsLoadBalanceSetting key. + // + status = ZwOpenKey(&targetSubKey, KEY_ALL_ACCESS, &objectAttributes); + + if (NT_SUCCESS(status)) { + + status = ZwDeleteKey(targetSubKey); + } + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpSetVidPidLBPolicyInRegistry (%ws): Failed to either open or delete. Status %x.\n", + TargetHardwareId, + status)); + } + } else { + + RtlInitUnicodeString(&lbPolicyValueName, DSM_LOAD_BALANCE_POLICY); + RtlInitUnicodeString(&preferredPathValueName, DSM_PREFERRED_PATH); + + status = ZwCreateKey(&targetSubKey, + KEY_ALL_ACCESS, + &objectAttributes, + 0, + NULL, + REG_OPTION_NON_VOLATILE, + NULL); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpSetVidPidLBPolicyInRegistry (%ws): Failed to open/create key in registry. Status %x.\n", + TargetHardwareId, + status)); + + goto __Exit_DsmpSetVidPidLBPolicyInRegistry; + } + + status = ZwSetValueKey(targetSubKey, + &lbPolicyValueName, + 0, + REG_DWORD, + &LoadBalanceType, + sizeof(ULONG)); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpSetVidPidLBPolicyInRegistry (%ws): Failed to set LB policy in registry. Status %x.\n", + TargetHardwareId, + status)); + + goto __Exit_DsmpSetVidPidLBPolicyInRegistry; + } + + status = ZwSetValueKey(targetSubKey, + &preferredPathValueName, + 0, + REG_BINARY, + &PreferredPath, + sizeof(ULONGLONG)); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpSetVidPidLBPolicyInRegistry (%ws): Failed to set preferred path in registry. Status %x.\n", + TargetHardwareId, + status)); + } + } + +__Exit_DsmpSetVidPidLBPolicyInRegistry: + + if (targetSubKey) { + ZwClose(targetSubKey); + } + + if (targetsLBSettingKey) { + ZwClose(targetsLBSettingKey); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpSetVidPidLBPolicyInRegistry (%ws): Exiting function with status %x.\n", + TargetHardwareId, + status)); + + return status; +} + + +NTSTATUS +DsmpOpenLoadBalanceSettingsKey( + _In_ IN ACCESS_MASK AccessMask, + _Out_ OUT PHANDLE LoadBalanceSettingsKey + ) +/*++ + +Routine Description: + + Open the device key in the registry. + + NOTE: It is the responsibility of the caller to close the returned handle. + +Arguements: + + AccessMask - Requested access with which to open key + LoadBalanceSettingsKey - handle of the key that is returned to the caller + +Return Value : + + STATUS_SUCCESS if we were able to successfully open the registry key. + + Appropriate NTSTATUS code on failure + +--*/ +{ + HANDLE serviceKey = NULL; + HANDLE parametersKey = NULL; + PUNICODE_STRING registryPath = &(gDsmInitData.DsmWmiInfo.RegistryPath); + OBJECT_ATTRIBUTES objectAttributes; + UNICODE_STRING parametersKeyName; + UNICODE_STRING subKeyName; + NTSTATUS status = STATUS_UNSUCCESSFUL; + + PAGED_CODE(); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpOpenLoadBalanceSettingsKey (RegPath %p): Entering function.\n", + registryPath)); + + *LoadBalanceSettingsKey = NULL; + + // + // First check if registry path is available for msdsm. + // + if (!registryPath->Buffer) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpOpenLoadBalanceSettingsKey (RegPath %p): Registry Path not set.\n", + registryPath)); + + goto __Exit_DsmpOpenLoadBalanceSettingsKey; + } + + // + // Open the service key first + // + InitializeObjectAttributes(&objectAttributes, + registryPath, + (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE), + NULL, + NULL); + + status = ZwOpenKey(&serviceKey, + AccessMask, + &objectAttributes); + if (NT_SUCCESS(status)) { + + // + // Open Parameters key under the Service key + // + RtlInitUnicodeString(¶metersKeyName, DSM_SERVICE_PARAMETERS); + + RtlZeroMemory(&objectAttributes, sizeof(OBJECT_ATTRIBUTES)); + + InitializeObjectAttributes(&objectAttributes, + ¶metersKeyName, + (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE), + serviceKey, + (PSECURITY_DESCRIPTOR) NULL); + + status = ZwOpenKey(¶metersKey, + AccessMask, + &objectAttributes); + + if (NT_SUCCESS(status)) { + + // + // Open LoadBalanceSettings key under the Parameters key + // + RtlInitUnicodeString(&subKeyName, DSM_LOAD_BALANCE_SETTINGS); + + RtlZeroMemory(&objectAttributes, sizeof(OBJECT_ATTRIBUTES)); + + InitializeObjectAttributes(&objectAttributes, + &subKeyName, + (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE), + parametersKey, + (PSECURITY_DESCRIPTOR) NULL); + + status = ZwCreateKey(LoadBalanceSettingsKey, + AccessMask, + &objectAttributes, + 0, + NULL, + REG_OPTION_NON_VOLATILE, + NULL); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpOpenLoadBalanceSettingsKey (RegPath %p): Failed to open/create LBSettings key. Status %x.\n", + registryPath, + status)); + + *LoadBalanceSettingsKey = NULL; + } + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpOpenLoadBalanceSettingsKey (RegPath %p): Failed to open parameters key. Status %x.\n", + registryPath, + status)); + } + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpOpenLoadBalanceSettingsKey (RegPath %p): Failed to open service key %ws. Status %x.\n", + registryPath, + registryPath->Buffer, + status)); + } + +__Exit_DsmpOpenLoadBalanceSettingsKey: + + if (parametersKey) { + ZwClose(parametersKey); + } + + if (serviceKey) { + ZwClose(serviceKey); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpOpenLoadBalanceSettingsKey (RegPath %p): Exiting function with status %x.\n", + registryPath, + status)); + + return status; +} + + +NTSTATUS +DsmpOpenTargetsLoadBalanceSettingKey( + _In_ IN ACCESS_MASK AccessMask, + _Out_ OUT PHANDLE TargetsLoadBalanceSettingKey + ) +/*++ + +Routine Description: + + Open the target key in the registry. + + NOTE: It is the responsibility of the caller to close the returned handle. + +Arguements: + + AccessMask - Requested access with which to open key + LoadBalanceSettingsKey - handle of the key that is returned to the caller + +Return Value : + + STATUS_SUCCESS if we were able to successfully open the registry key. + + Appropriate NTSTATUS code on failure + +--*/ +{ + HANDLE serviceKey = NULL; + HANDLE parametersKey = NULL; + PUNICODE_STRING registryPath = &(gDsmInitData.DsmWmiInfo.RegistryPath); + OBJECT_ATTRIBUTES objectAttributes; + UNICODE_STRING parametersKeyName; + UNICODE_STRING subKeyName; + NTSTATUS status = STATUS_UNSUCCESSFUL; + + PAGED_CODE(); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpOpenTargetsLoadBalanceSettingKey (RegPath %p): Entering function.\n", + registryPath)); + + if (!TargetsLoadBalanceSettingKey) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpOpenTargetsLoadBalanceSettingKey (RegPath %p): Invalid parameter.\n", + registryPath)); + + status = STATUS_INVALID_PARAMETER; + goto __Exit_DsmpOpenTargetsLoadBalanceSettingKey; + } + + *TargetsLoadBalanceSettingKey = NULL; + + // + // First check if registry path is available for msdsm. + // + if (!registryPath->Buffer) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpOpenTargetsLoadBalanceSettingKey (RegPath %p): Registry Path not set.\n", + registryPath)); + + goto __Exit_DsmpOpenTargetsLoadBalanceSettingKey; + } + + // + // Open the service key first + // + InitializeObjectAttributes(&objectAttributes, + registryPath, + (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE), + NULL, + NULL); + + status = ZwOpenKey(&serviceKey, + AccessMask, + &objectAttributes); + if (NT_SUCCESS(status)) { + + // + // Open Parameters key under the Service key + // + RtlInitUnicodeString(¶metersKeyName, DSM_SERVICE_PARAMETERS); + + RtlZeroMemory(&objectAttributes, sizeof(OBJECT_ATTRIBUTES)); + + InitializeObjectAttributes(&objectAttributes, + ¶metersKeyName, + (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE), + serviceKey, + (PSECURITY_DESCRIPTOR) NULL); + + status = ZwOpenKey(¶metersKey, + AccessMask, + &objectAttributes); + + if (NT_SUCCESS(status)) { + + // + // Open LoadBalanceSettings key under the Parameters key + // + RtlInitUnicodeString(&subKeyName, DSM_TARGETS_LOAD_BALANCE_SETTING); + + RtlZeroMemory(&objectAttributes, sizeof(OBJECT_ATTRIBUTES)); + + InitializeObjectAttributes(&objectAttributes, + &subKeyName, + (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE), + parametersKey, + (PSECURITY_DESCRIPTOR) NULL); + + status = ZwCreateKey(TargetsLoadBalanceSettingKey, + AccessMask, + &objectAttributes, + 0, + NULL, + REG_OPTION_NON_VOLATILE, + NULL); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpOpenTargetsLoadBalanceSettingKey (RegPath %p): Failed to open/create TargetsLBSetting key. Status %x.\n", + registryPath, + status)); + + *TargetsLoadBalanceSettingKey = NULL; + } + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpOpenTargetsLoadBalanceSettingKey (RegPath %p): Failed to open parameters key. Status %x.\n", + registryPath, + status)); + } + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpOpenTargetsLoadBalanceSettingKey (RegPath %p): Failed to open service key %ws. Status %x.\n", + registryPath, + registryPath->Buffer, + status)); + } + +__Exit_DsmpOpenTargetsLoadBalanceSettingKey: + + if (parametersKey) { + ZwClose(parametersKey); + } + + if (serviceKey) { + ZwClose(serviceKey); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpOpenTargetsLoadBalanceSettingKey (RegPath %p): Exiting function with status %x.\n", + registryPath, + status)); + + return status; +} + + +NTSTATUS +DsmpOpenDsmServicesParametersKey( + _In_ IN ACCESS_MASK AccessMask, + _Out_ OUT PHANDLE ParametersKey + ) +/*++ + +Routine Description: + + Open the DSM's Parameters key in the registry. + + NOTE: It is the responsibility of the caller to close the returned handle. + +Arguements: + + AccessMask - Requested access with which to open key + ParametersKey - handle of the key that is returned to the caller + +Return Value : + + STATUS_SUCCESS if we were able to successfully open the registry key. + + Appropriate NTSTATUS code on failure + +--*/ +{ + HANDLE serviceKey = NULL; + PUNICODE_STRING registryPath = &(gDsmInitData.DsmWmiInfo.RegistryPath); + OBJECT_ATTRIBUTES objectAttributes; + UNICODE_STRING parametersKeyName; + NTSTATUS status = STATUS_UNSUCCESSFUL; + + PAGED_CODE(); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpOpenDsmServicesParametersKey (RegPath %p): Entering function.\n", + registryPath)); + + if (!ParametersKey) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpOpenDsmServicesParametersKey (RegPath %p): Invalid parameter.\n", + registryPath)); + + status = STATUS_INVALID_PARAMETER; + goto __Exit_DsmpOpenDsmServicesParametersKey; + } + + *ParametersKey = NULL; + + // + // First check if registry path is available for msdsm. + // + if (!registryPath->Buffer) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpOpenDsmServicesParametersKey (RegPath %p): Registry Path not set.\n", + registryPath)); + + goto __Exit_DsmpOpenDsmServicesParametersKey; + } + + // + // Open the service key first + // + InitializeObjectAttributes(&objectAttributes, + registryPath, + (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE), + NULL, + NULL); + + status = ZwOpenKey(&serviceKey, + AccessMask, + &objectAttributes); + if (NT_SUCCESS(status)) { + + // + // Open Parameters key under the Service key + // + RtlInitUnicodeString(¶metersKeyName, DSM_SERVICE_PARAMETERS); + + RtlZeroMemory(&objectAttributes, sizeof(OBJECT_ATTRIBUTES)); + + InitializeObjectAttributes(&objectAttributes, + ¶metersKeyName, + (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE), + serviceKey, + (PSECURITY_DESCRIPTOR) NULL); + + status = ZwOpenKey(ParametersKey, + AccessMask, + &objectAttributes); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpOpenDsmServicesParametersKey (RegPath %p): Failed to open parameters key. Status %x.\n", + registryPath, + status)); + + *ParametersKey = NULL; + } + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpOpenDsmServicesParametersKey (RegPath %p): Failed to open service key %ws. Status %x.\n", + registryPath, + registryPath->Buffer, + status)); + } + +__Exit_DsmpOpenDsmServicesParametersKey: + + if (serviceKey) { + ZwClose(serviceKey); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpOpenDsmServicesParametersKey (RegPath %p): Exiting function with status %x.\n", + registryPath, + status)); + + return status; +} + +NTSTATUS +DsmpReportTargetPortGroupsSyncCompletion( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ) +{ + UNREFERENCED_PARAMETER(DeviceObject); + UNREFERENCED_PARAMETER(Context); + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpReportTargetPortGroupsSyncCompletion: IRP %p, Context %p\n", + Irp, Context)); + + KeSetEvent(Irp->UserEvent, 0, FALSE); + + return STATUS_MORE_PROCESSING_REQUIRED; +} + +_Success_(return==0) +NTSTATUS +DsmpReportTargetPortGroups( + _In_ PDEVICE_OBJECT DeviceObject, + _Outptr_result_buffer_maybenull_(*TargetPortGroupsInfoLength) PUCHAR *TargetPortGroupsInfo, + _Out_ PULONG TargetPortGroupsInfoLength + ) +/*++ + +Routine Description: + + Helper routine to send down ReportTargetPortGroups request synchronously. + Used if device supports ALUA. + + Note: This routine will allocate memory for the TPG info. It is the + responsibility of the caller to free this buffer, but only if the function + returns STATUS_SUCCESS. + +Arguments: + + DeviceObject - The port PDO to which the command should be sent. + TargetPortGroupsInfo - buffer containing the returned data. + TargetPortGroupsInfoLength - size of the returned buffer. + +Return Value: + + STATUS_SUCCESS or appropriate failure code. + +--*/ +{ + PSPC3_CDB_REPORT_TARGET_PORT_GROUPS cdb; + NTSTATUS status = STATUS_SUCCESS; + PIRP irp = NULL; + PMDL mdl = NULL; + PSCSI_REQUEST_BLOCK srb = NULL; + PSENSE_DATA_EX senseInfoBuffer = NULL; + UCHAR senseInfoBufferLength = 0; + KEVENT completionEvent; + ULONG targetPortGroupsInfoLength = 0; + PUCHAR targetPortGroupsInfo = NULL; + PIO_STACK_LOCATION irpStack = NULL; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpReportTargetPortGroups (DevObj %p): Entering function.\n", + DeviceObject)); + + if (TargetPortGroupsInfoLength == NULL || + TargetPortGroupsInfo == NULL) { + status = STATUS_INVALID_PARAMETER; + goto __Exit_DsmpReportTargetPortGroups; + } + + *TargetPortGroupsInfoLength = 0; + *TargetPortGroupsInfo = NULL; + + senseInfoBuffer = (PSENSE_DATA_EX)DsmpAllocatePool(NonPagedPoolNx, + SENSE_BUFFER_SIZE_EX, + DSM_TAG_SCSI_SENSE_INFO); + if (senseInfoBuffer != NULL) { + + senseInfoBufferLength = SENSE_BUFFER_SIZE_EX; + + srb = (PSCSI_REQUEST_BLOCK)DsmpAllocatePool(NonPagedPoolNx, + sizeof(SCSI_REQUEST_BLOCK), + DSM_TAG_SCSI_REQUEST_BLOCK); + if (srb != NULL) { + + SrbSetSenseInfoBufferLength(srb, senseInfoBufferLength); + SrbSetSenseInfoBuffer(srb, senseInfoBuffer); + + // + // Take care of worst case scenario, which is: + // 1. 4-byte header (for allocation length) + // 2. 32 8-byte descriptors (for TPGs) + // 3. Each descriptor containing 32 4-byte identifiers (for TPs in each TPG) + // + targetPortGroupsInfoLength = SPC3_TARGET_PORT_GROUPS_HEADER_SIZE + + (DSM_MAX_PATHS * (sizeof(SPC3_REPORT_TARGET_PORT_GROUP_DESCRIPTOR) + + DSM_MAX_PATHS * sizeof(ULONG))); + + targetPortGroupsInfo = (PUCHAR)DsmpAllocatePool(NonPagedPoolNx, + targetPortGroupsInfoLength, + DSM_TAG_TARGET_PORT_GROUPS); + + if (targetPortGroupsInfo == NULL) { + status = STATUS_INSUFFICIENT_RESOURCES; + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_GENERAL, + "DsmpReportTargetPortGroups (DevObj %p): Failed to allocate TPG info.\n", + DeviceObject)); + goto __Exit_DsmpReportTargetPortGroups; + } + + } else { + status = STATUS_INSUFFICIENT_RESOURCES; + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_GENERAL, + "DsmpReportTargetPortGroups (DevObj %p): Failed to allocate SRB.\n", + DeviceObject)); + goto __Exit_DsmpReportTargetPortGroups; + } + + } else { + status = STATUS_INSUFFICIENT_RESOURCES; + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_GENERAL, + "DsmpReportTargetPortGroups (DevObj %p): Failed to allocate Sense Info Buffer.\n", + DeviceObject)); + goto __Exit_DsmpReportTargetPortGroups; + } + + irp = IoAllocateIrp(DeviceObject->StackSize + 1, FALSE); + if (irp == NULL) { + status = STATUS_INSUFFICIENT_RESOURCES; + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_GENERAL, + "DsmpReportTargetPortGroups (DevObj %p): Failed to allocate IRP.\n", + DeviceObject)); + goto __Exit_DsmpReportTargetPortGroups; + } + + mdl = IoAllocateMdl(targetPortGroupsInfo, + targetPortGroupsInfoLength, + FALSE, + FALSE, + irp); + + if (mdl == NULL) { + status = STATUS_INSUFFICIENT_RESOURCES; + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_GENERAL, + "DsmpReportTargetPortGroups (DevObj %p): Failed to allocate MDL.\n", + DeviceObject)); + goto __Exit_DsmpReportTargetPortGroups; + } + + MmBuildMdlForNonPagedPool(mdl); + +__Retry_DsmpReportTargetPortGroups: + + irp->MdlAddress = mdl; + + // + // Set up SRB for execute scsi request. Save SRB address in next stack + // for the port driver. + // + irpStack = IoGetNextIrpStackLocation(irp); + irpStack->MajorFunction = IRP_MJ_SCSI; + irpStack->MinorFunction = IRP_MN_SCSI_CLASS; + irpStack->Parameters.Scsi.Srb = (PSCSI_REQUEST_BLOCK)srb; + irpStack->DeviceObject = DeviceObject; + + // + // Set the completion event and the completion routine. + // + KeInitializeEvent(&completionEvent, NotificationEvent, FALSE); + irp->UserEvent = &completionEvent; + IoSetCompletionRoutine(irp, + DsmpReportTargetPortGroupsSyncCompletion, + srb, + TRUE, + TRUE, + TRUE); + + srb->Function = SRB_FUNCTION_EXECUTE_SCSI; + srb->Length = sizeof(SCSI_REQUEST_BLOCK); + + SrbSetCdbLength(srb, sizeof(SPC3_CDB_REPORT_TARGET_PORT_GROUPS)); + cdb = (PSPC3_CDB_REPORT_TARGET_PORT_GROUPS)SrbGetCdb(srb); + cdb->OperationCode = SPC3_SCSIOP_REPORT_TARGET_PORT_GROUPS; + cdb->ServiceAction = SPC3_SERVICE_ACTION_TARGET_PORT_GROUPS; + REVERSE_BYTES(&(cdb->AllocationLength), &targetPortGroupsInfoLength); + + SrbSetTimeOutValue(srb, SPC3_REPORT_TARGET_PORT_GROUPS_TIMEOUT); + SrbSetDataTransferLength(srb, targetPortGroupsInfoLength); + SrbSetDataBuffer(srb, targetPortGroupsInfo); + srb->SrbStatus = 0; + SrbSetScsiStatus(srb, 0); + SrbSetNextSrb(srb, NULL); + SrbSetSrbFlags(srb, SRB_FLAGS_DONT_START_NEXT_PACKET | SRB_FLAGS_QUEUE_ACTION_ENABLE | + SRB_FLAGS_DATA_IN | SRB_FLAGS_DISABLE_SYNCH_TRANSFER | + SRB_FLAGS_BYPASS_FROZEN_QUEUE | SRB_FLAGS_NO_QUEUE_FREEZE); + SrbSetQueueAction(srb, SRB_HEAD_OF_QUEUE_TAG_REQUEST); + SrbSetOriginalRequest(srb, irp); + + ObReferenceObject(DeviceObject); + + // + // Finally, send the IRP down and wait for its completion. + // + status = IoCallDriver(DeviceObject, irp); + + if (status == STATUS_PENDING) { + KeWaitForSingleObject(&completionEvent, + Executive, + KernelMode, + FALSE, + NULL); + status = irp->IoStatus.Status; + } + + ObDereferenceObject(DeviceObject); + + if ((status == STATUS_BUFFER_OVERFLOW) || + (NT_SUCCESS(status) && (SrbGetScsiStatus(srb) == SCSISTAT_GOOD))) { + + // + // The first 4 bytes of the returned data are the Returned Data Length + // field of the RTPG header. + // + ULONG returnedDataLength = 0; + REVERSE_BYTES(&returnedDataLength, targetPortGroupsInfo); + + status = STATUS_SUCCESS; + if (returnedDataLength > SrbGetDataTransferLength(srb)) { + + status = STATUS_BUFFER_OVERFLOW; + } + } + + if (NT_SUCCESS(status) && SrbGetScsiStatus(srb) == SCSISTAT_GOOD) { + + // + // RTPG was successful so return the TPG info to the caller. + // + + // + // The first 4 bytes of the returned data are the Returned Data Length + // field of the RTPG header. We need to return this value plus the header size. + // + ULONG returnedDataLength = 0; + REVERSE_BYTES(&returnedDataLength, targetPortGroupsInfo); + *TargetPortGroupsInfoLength = SPC3_TARGET_PORT_GROUPS_HEADER_SIZE + returnedDataLength; + + *TargetPortGroupsInfo = targetPortGroupsInfo; + + } else if (SrbGetScsiStatus(srb) == SCSISTAT_CHECK_CONDITION) { + + if (DsmpShouldRetryTPGRequest(senseInfoBuffer, senseInfoBufferLength)) { + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpReportTargetPortGroups (DevObj %p): Retrying request.\n", + DeviceObject)); + + IoReuseIrp(irp, STATUS_SUCCESS); + + RtlZeroMemory(senseInfoBuffer, senseInfoBufferLength); + + goto __Retry_DsmpReportTargetPortGroups; + } + + if (DsmpIsDeviceRemoved(senseInfoBuffer, senseInfoBufferLength)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_GENERAL, + "DsmpReportTargetPortGroups (DevObj %p): Device not available.\n", + DeviceObject)); + + // + // Sense key was illegal request. SPC 6.25 says response to TPG should follow Test Unit Ready responses + // + status = STATUS_NO_SUCH_DEVICE; + + } + + // RTPG was unsuccessful + // Here it is possible that status is success, but scsi status is not. + // and there was no RTPG retry. If so, set status to unsuccessful. + if (NT_SUCCESS(status)) { + status = STATUS_UNSUCCESSFUL; + } + + // + // TPG resulted HW to respond with Check Condition but Sense Key indicates it is not for retry or illegal request + // + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_GENERAL, + "DsmpReportTargetPortGroups (DevObj %p): TPG returned Check Condition, NTStatus 0x%x, ScsiStatus 0x%x.\n", + DeviceObject, + status, + SrbGetScsiStatus(srb))); + } else { + + // RTPG was unsuccessful + // Here it is possible that status is success, but scsi status is not. + // If so, set status to unsuccessful. + if (NT_SUCCESS(status)) { + status = STATUS_UNSUCCESSFUL; + } + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_GENERAL, + "DsmpReportTargetPortGroups (DevObj %p): NTStatus 0x%x, ScsiStatus 0x%x.\n", + DeviceObject, + status, + SrbGetScsiStatus(srb))); + } + +__Exit_DsmpReportTargetPortGroups: + + // + // The port driver may have allocated its own sense buffer so we need to + // make sure we free that here. + // + if (srb != NULL && + SrbGetSrbFlags(srb) & SRB_FLAGS_PORT_DRIVER_ALLOCSENSE && + SrbGetSrbFlags(srb) & SRB_FLAGS_FREE_SENSE_BUFFER && + SrbGetSenseInfoBuffer(srb) != NULL) { + DsmpFreePool(SrbGetSenseInfoBuffer(srb)); + } + + if (senseInfoBuffer) { + DsmpFreePool(senseInfoBuffer); + } + + if (srb) { + DsmpFreePool(srb); + } + + if (irp) { + if (irp->MdlAddress) { + IoFreeMdl(irp->MdlAddress); + } + IoFreeIrp(irp); + } + + if (!NT_SUCCESS(status) && targetPortGroupsInfo) { + DsmpFreePool(targetPortGroupsInfo); + *TargetPortGroupsInfoLength = 0; + *TargetPortGroupsInfo = NULL; + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpReportTargetPortGroups (DevObj %p): Exiting function with status %x.\n", + DeviceObject, + status)); + + return status; +} + +NTSTATUS +DsmpReportTargetPortGroupsAsync( + _In_ IN PDSM_DEVICE_INFO DeviceInfo, + _In_ IN PIO_COMPLETION_ROUTINE CompletionRoutine, + _Inout_ __drv_aliasesMem IN PDSM_TPG_COMPLETION_CONTEXT CompletionContext, + _In_ IN ULONG TargetPortGroupsInfoLength, + _Inout_ __drv_aliasesMem IN OUT PUCHAR TargetPortGroupsInfo + ) +/*++ + +Routine Description: + + Helper routine to send down ReportTargetPortGroups request asynchronously. + Used if device supports ALUA. + + NOTE: Caller needs to free Irp, system buffer, and passThrough buffer. + +Arguments: + + DeviceInfo - The deviceInfo whose corresponding port PDO the command should be sent to. + CompletionRoutine - completion routine passed in by the caller. + CompletionContext - context to be passed to be completion routine. + TargetPortGroupsInfoLength - size of the returned buffer. + TargetPortGroupsInfo - preallocated (by caller) buffer that'll contain the returned data. + +Return Value: + + STATUS_SUCCESS or appropriate failure code. + +--*/ +{ + PDSM_TPG_COMPLETION_CONTEXT tpgCompletionContext = CompletionContext; + PSCSI_REQUEST_BLOCK srb = NULL; + PSPC3_CDB_REPORT_TARGET_PORT_GROUPS cdb; + NTSTATUS status; + PIRP irp = NULL; + PIO_STACK_LOCATION irpStack; + PMDL mdl = NULL; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmpReportTargetPortGroupsAsync (DevInfo %p): Entering function.\n", + DeviceInfo)); + + srb = tpgCompletionContext->Srb; + + SrbZeroSrb(srb); + + // + // Allocate an irp. + // + irp = IoAllocateIrp(DeviceInfo->TargetObject->StackSize + 1, FALSE); + if (!irp) { + status = STATUS_INSUFFICIENT_RESOURCES; + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmpReportTargetPortGroupsAsync (DevInfo %p): Failed to allocate IRP.\n", + DeviceInfo)); + goto __Exit_DsmpReportTargetPortGroupsAsync; + } + + mdl = IoAllocateMdl(TargetPortGroupsInfo, + TargetPortGroupsInfoLength, + FALSE, + FALSE, + irp); + if (!mdl) { + + status = STATUS_INSUFFICIENT_RESOURCES; + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmpReportTargetPortGroupsAsync (DevInfo %p): Failed to allocate MDL.\n", + DeviceInfo)); + goto __Exit_DsmpReportTargetPortGroupsAsync; + } + + MmBuildMdlForNonPagedPool(irp->MdlAddress); + + // + // It is possible that if an implicit access state transition took place, + // each I_T nexus will return UA for asymmetric access state changed. So + // set the number of retries to be one more than the total number of paths. + // Worst case scenario is the it is sent down each path once (assuming every + // is a different I_T nexus) and then one more for a retry one one of the + // paths. + // + tpgCompletionContext->NumberRetries = DeviceInfo->Group->NumberDevices + 1; + + // + // Set-up the completion routine. + // + IoSetCompletionRoutine(irp, + CompletionRoutine, + (PVOID)CompletionContext, + TRUE, + TRUE, + TRUE); + + // + // Get the recipient's irpstack location. + // + irpStack = IoGetNextIrpStackLocation(irp); + + irpStack->Parameters.Scsi.Srb = srb; + irpStack->DeviceObject = DeviceInfo->TargetObject; + + // + // Set the major function code to IRP_MJ_SCSI. + // + irpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; + + // + // Set the minor function, or many requests will get kicked by by port. + // + irpStack->MinorFunction = IRP_MN_SCSI_CLASS; + + srb->Function = SRB_FUNCTION_EXECUTE_SCSI; + srb->Length = sizeof(SCSI_REQUEST_BLOCK); + + SrbSetCdbLength(srb, sizeof(SPC3_CDB_REPORT_TARGET_PORT_GROUPS)); + cdb = (PSPC3_CDB_REPORT_TARGET_PORT_GROUPS)SrbGetCdb(srb); + cdb->OperationCode = SPC3_SCSIOP_REPORT_TARGET_PORT_GROUPS; + cdb->ServiceAction = SPC3_SERVICE_ACTION_TARGET_PORT_GROUPS; + Get4ByteArrayFromUlong(TargetPortGroupsInfoLength, cdb->AllocationLength); + + SrbSetTimeOutValue(srb, SPC3_REPORT_TARGET_PORT_GROUPS_TIMEOUT); + SrbSetSenseInfoBuffer(srb, tpgCompletionContext->SenseInfoBuffer); + SrbSetSenseInfoBufferLength(srb, tpgCompletionContext->SenseInfoBufferLength); + SrbSetDataTransferLength(srb, TargetPortGroupsInfoLength); + SrbSetDataBuffer(srb, TargetPortGroupsInfo); + srb->SrbStatus = 0; + SrbSetScsiStatus(srb, 0); + SrbSetNextSrb(srb, NULL); + SrbSetSrbFlags(srb, SRB_FLAGS_DONT_START_NEXT_PACKET | SRB_FLAGS_QUEUE_ACTION_ENABLE | + SRB_FLAGS_DATA_IN | SRB_FLAGS_DISABLE_SYNCH_TRANSFER | + SRB_FLAGS_BYPASS_FROZEN_QUEUE | SRB_FLAGS_NO_QUEUE_FREEZE); + SrbSetQueueAction(srb, SRB_HEAD_OF_QUEUE_TAG_REQUEST); + SrbSetOriginalRequest(srb, irp); + + irp->UserBuffer = TargetPortGroupsInfo; + irp->Tail.Overlay.Thread = PsGetCurrentThread(); + + // + // Send the IRP asynchronously + // + DsmSendRequestEx(((PDSM_CONTEXT)(DeviceInfo->DsmContext))->MPIOContext, + DeviceInfo->TargetObject, + irp, + (PVOID)DeviceInfo, + DSM_CALL_COMPLETION_ON_MPIO_ERROR); + + // + // We know that the completion routine will always be called. + // + status = STATUS_PENDING; + +__Exit_DsmpReportTargetPortGroupsAsync: + + if (status != STATUS_PENDING) { + + // + // This indicates Irp was never sent down to stack (completion routine was never called). + // We need to clean up. + // + if (irp) { + + if (irp->MdlAddress) { + IoFreeMdl(irp->MdlAddress); + } + + IoFreeIrp(irp); + } + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmpReportTargetPortGroupsAsync (DevInfo %p): Exiting function with status %x\n.", + DeviceInfo, + status)); + + return status; +} + + +NTSTATUS +DsmpQueryLBPolicyForDevice( + _In_ IN PWSTR RegistryKeyName, + _In_ IN ULONGLONG PathId, + _In_ IN DSM_LOAD_BALANCE_TYPE LoadBalanceType, + _Out_ OUT PULONG PrimaryPath, + _Out_ OUT PULONG OptimizedPath, + _Out_ OUT PULONG PathWeight + ) +/*++ + +Routine Description: + + This routine opens the device's registry subkey, builds the path subkey from + the passed in PathId, then queries that subkey for the value of PrimaryPath, + OptimizedPath and PathWeight. + +Arguments: + + RegistryKeyName - The device's registry subkey name. + PathId - The pathId for this instance of the device. + LoadBalanceType - The current load balance policy. + PrimaryPath - Output of the queried PrimaryPath value. + OptimizedPath - Output of the queried OptimizedPath value. + PathWeight - Output of the queried PathWeight value. + +Return Value: + + STATUS_SUCCESS or appropriate failure code. + +--*/ +{ + HANDLE lbSettingsKey = NULL; + HANDLE deviceKey = NULL; + HANDLE dsmPathKey = NULL; + UNICODE_STRING subKeyName; + WCHAR dsmPathName[128] = {0}; + OBJECT_ATTRIBUTES objectAttributes; + NTSTATUS status; + NTSTATUS pathWeightQueryStatus = STATUS_SUCCESS; + + PAGED_CODE(); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpQueryLBPolicyForDevice (DevName %ws): Entering function.\n", + RegistryKeyName)); + + // + // Query PrimaryPath and PathWeight for the given path. + // These values are stored under DsmPath#Suffix key for + // this path. If this key doesn't exist create it and + // create PrimaryPath and PathWeight values - use the + // values passed in PrimaryPath and PathWeight in this case. + // + status = DsmpOpenLoadBalanceSettingsKey(KEY_ALL_ACCESS, &lbSettingsKey); + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpQueryLBPolicyForDevice (DevName %ws): Failed to open LB Settings key. Status %x.\n", + RegistryKeyName, + status)); + + goto __Exit_DsmpQueryLBPolicyForDevice; + } + + RtlInitUnicodeString(&subKeyName, RegistryKeyName); + + InitializeObjectAttributes(&objectAttributes, + &subKeyName, + (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE), + lbSettingsKey, + (PSECURITY_DESCRIPTOR) NULL); + + status = ZwOpenKey(&deviceKey, KEY_ALL_ACCESS, &objectAttributes); + if (NT_SUCCESS(status)) { + + // + // Create or open DsmPath#Suffix key for this path + // + DsmpGetDSMPathKeyName(PathId, dsmPathName, 128); + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_PNP, + "DsmpQueryLBPolicyForDevice (DevName %ws): Will query %ws for PrimaryPath, OptimizedPath and PathWeight.\n", + RegistryKeyName, + dsmPathName)); + + RtlInitUnicodeString(&subKeyName, dsmPathName); + + RtlZeroMemory(&objectAttributes, sizeof(OBJECT_ATTRIBUTES)); + + InitializeObjectAttributes(&objectAttributes, + &subKeyName, + (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE), + deviceKey, + (PSECURITY_DESCRIPTOR) NULL); + + status = ZwCreateKey(&dsmPathKey, + KEY_ALL_ACCESS, + &objectAttributes, + 0, + NULL, + REG_OPTION_NON_VOLATILE, + NULL); + + if (NT_SUCCESS(status)) { + + RTL_QUERY_REGISTRY_TABLE queryTable[2]; + + // + // Query the Path Weight value. + // + + RtlZeroMemory(queryTable, sizeof(queryTable)); + + queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | + RTL_QUERY_REGISTRY_REQUIRED; + queryTable[0].Name = DSM_PATH_WEIGHT; + queryTable[0].EntryContext = PathWeight; + + pathWeightQueryStatus = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE, + dsmPathKey, + queryTable, + dsmPathKey, + NULL); + + if (!NT_SUCCESS(pathWeightQueryStatus)) { + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpQueryLBPolicyForDevice (DevName %ws): Failed to query PathWeight. Status %x.\n", + RegistryKeyName, + pathWeightQueryStatus)); + } + + // + // Query the Primary Path value. + // + + RtlZeroMemory(queryTable, sizeof(queryTable)); + + queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | + RTL_QUERY_REGISTRY_REQUIRED; + queryTable[0].Name = DSM_PRIMARY_PATH; + queryTable[0].EntryContext = PrimaryPath; + + status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE, + dsmPathKey, + queryTable, + dsmPathKey, + NULL); + if (NT_SUCCESS(status)) { + + // + // Query the Optimized Path value. + // + + RtlZeroMemory(queryTable, sizeof(queryTable)); + + queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | + RTL_QUERY_REGISTRY_REQUIRED; + queryTable[0].Name = DSM_OPTIMIZED_PATH; + queryTable[0].EntryContext = OptimizedPath; + + status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE, + dsmPathKey, + queryTable, + dsmPathKey, + NULL); + if (!NT_SUCCESS(status)) { + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpQueryLBPolicyForDevice (DevName %ws): Failed to query OptimizedPath. Status %x.\n", + RegistryKeyName, + status)); + } + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpQueryLBPolicyForDevice (DevName %ws): Failed to query PrimaryPath. Status %x.\n", + RegistryKeyName, + status)); + } + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpQueryLBPolicyForDevice (DevName %ws): Failed to create DSM Path key %ws. Status %x.\n", + RegistryKeyName, + dsmPathName, + status)); + } + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpQueryLBPolicyForDevice (DevName %ws): Failed to open key. Status %x.\n", + RegistryKeyName, + status)); + } + + if (NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpQueryLBPolicyForDevice (DevName %ws): PrimaryPath %d, OptmizedPath %d, PathWeight %d.\n", + RegistryKeyName, + *PrimaryPath, + *OptimizedPath, + *PathWeight)); + } + +__Exit_DsmpQueryLBPolicyForDevice: + + if (dsmPathKey) { + ZwClose(dsmPathKey); + } + + if (deviceKey) { + ZwClose(deviceKey); + } + + if (lbSettingsKey) { + ZwClose(lbSettingsKey); + } + + // + // If the load balance policy is Weighted Paths and we failed to read in + // the path weight value, we need to return the failure status from the + // path weight value query. + // + if (LoadBalanceType == DSM_LB_WEIGHTED_PATHS && !NT_SUCCESS(pathWeightQueryStatus)) { + status = pathWeightQueryStatus; + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpQueryLBPolicyForDevice (DevName %ws): Exiting function with status %x.\n", + RegistryKeyName, + status)); + + return status; +} + + +VOID +DsmpGetDSMPathKeyName( + _In_ ULONGLONG DSMPathId, + _Out_writes_(DsmPathKeyNameSize) PWCHAR DsmPathKeyName, + _In_ ULONG DsmPathKeyNameSize + ) +/*++ + +Routine Description: + + This routine builds the string that corresponds to the device's Path subkey + name in the registry. + +Arguments: + + DSMPathId - The pathId of this instance of the device. + DsmPathKeyName - Output buffer in which the subkey name for path is returned. + DsmPathKeyNameSize - size of the output buffer in WCHARs. + +Return Value: + + STATUS_SUCCESS or appropriate failure code. + +--*/ +{ + PWCHAR pathPtr; + SIZE_T wcharsLeft; + SIZE_T size; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpGetDSMPathKeyName (PathId %I64x): Entering function.\n", + DSMPathId)); + + // + // This routine will build a name for a given DSM Path. + // The name is of the format DsmPath#Suffix, where Suffix + // is derived from the PathId + // + pathPtr = DsmPathKeyName; + + wcharsLeft = DsmPathKeyNameSize; + + size = wcslen(DSM_PATH); + + if (size < wcharsLeft) { + + // + // First copy the string DsmPath# + // + if (NT_SUCCESS(RtlStringCchCopyNW(pathPtr, wcharsLeft, DSM_PATH, wcslen(DSM_PATH)))) { + + wcharsLeft -= size; + pathPtr += size; + + if (wcharsLeft > 2) { + + RtlStringCchCatW(pathPtr, wcharsLeft, L"#"); + wcharsLeft--; + pathPtr++; + + // + // Each nibble in the path id would need 1 WCHAR + // upon conversion to WCHAR string. So we'll need + // 2 WCHARs for each byte. Include the NULL char also + // + size = (sizeof(PVOID) + 1) * 2; + if (size <= wcharsLeft) { + + PVOID pathId; + PUCHAR pathIdPtr; + ULONG inx; + UCHAR tmpChar; + + // + // Convert the ULONGLONG path id to a string and + // append that to DsmPath# + // + pathId = (PVOID) DSMPathId; + + pathIdPtr = (PUCHAR) &pathId; + + for (inx = 0; inx < sizeof(PVOID); inx++) { + + tmpChar = (*pathIdPtr & 0xF0) >> 4; + *pathPtr++ = DsmpGetAsciiForBinary(tmpChar); + + tmpChar = (*pathIdPtr & 0x0F); + *pathPtr++ = DsmpGetAsciiForBinary(tmpChar); + + pathIdPtr++; + } + + *pathPtr = WNULL; + } + } + } + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpGetDSMPathKeyName (PathId %I64x): Exiting function.\n", + DSMPathId)); + + return; +} + + +UCHAR +DsmpGetAsciiForBinary( + _In_ UCHAR BinaryChar + ) +/*++ + +Routine Description: + + This routine converts the passed in binary value into ASCII equivalent. + +Arguments: + + BinaryChar - The binary value that needs to be converted. + +Return Value: + + Corresponding ASCII value. + +--*/ +{ + UCHAR outChar = 0; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpGetAsciiForBinary (BinaryChar %d): Entering function.\n", + BinaryChar)); + + // + // Convert a binary nibble into an ASCII character. + // + if ((BinaryChar >= 0) && (BinaryChar <= 9)) { + outChar = BinaryChar + '0'; + } else { + outChar = BinaryChar + 'A' - 10; + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpGetAsciiForBinary (BinaryChar %d): Exiting function with outChar %c.\n", + BinaryChar, + outChar)); + + return outChar; +} + + +NTSTATUS +DsmpGetDeviceIdList( + _In_ IN PDEVICE_OBJECT DeviceObject, + _Out_ OUT PSTORAGE_DESCRIPTOR_HEADER *Descriptor + ) +/*++ + +Routine Description: + + This routine will perform a query for the StorageDeviceIdProperty and will + allocate a non-paged buffer to store the data in. + IMPORTANT: It is the responsibility of the caller to ensure that this buffer is freed. + +Arguments: + + DeviceObject - the device to query + Descriptor - a location to store a pointer to the buffer we allocate + +Return Value: + + status. + +--*/ +{ + STORAGE_PROPERTY_QUERY query; + PIO_STATUS_BLOCK ioStatus = NULL; + PSTORAGE_DESCRIPTOR_HEADER descriptor = NULL; + ULONG length; + NTSTATUS status = STATUS_UNSUCCESSFUL; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpGetDeviceIdList (DevObj %p): Entering function.\n", + DeviceObject)); + + if (!DeviceObject) { + + status = STATUS_INVALID_PARAMETER; + goto __Exit_DsmpGetDeviceIdList; + } + + // + // Poison the passed in descriptor. + // + *Descriptor = NULL; + + // + // Setup the query buffer. + // + query.PropertyId = StorageDeviceIdProperty; + query.QueryType = PropertyStandardQuery; + query.AdditionalParameters[0] = 0; + + ioStatus = DsmpAllocatePool(NonPagedPoolNx, sizeof(IO_STATUS_BLOCK), DSM_TAG_IO_STATUS_BLOCK); + + if (!ioStatus) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpGetDeviceIdList (DevObj %p): Failed to allocate an IO_STATUS_BLOCK.\n", + DeviceObject)); + status = STATUS_INSUFFICIENT_RESOURCES; + goto __Exit_DsmpGetDeviceIdList; + } + + ioStatus->Status = 0; + ioStatus->Information = 0; + + // + // On the first call, just need to get the length of the descriptor. + // + descriptor = (PVOID)&query; + DsmSendDeviceIoControlSynchronous(IOCTL_STORAGE_QUERY_PROPERTY, + DeviceObject, + &query, + &query, + sizeof(STORAGE_PROPERTY_QUERY), + sizeof(STORAGE_DESCRIPTOR_HEADER), + FALSE, + ioStatus); + + status = ioStatus->Status; + + if(!NT_SUCCESS(status)) { + + descriptor = NULL; + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpGetDeviceIdList (DevObj %p): Query failed (%x) on attempt 1.\n", + DeviceObject, + ioStatus->Status)); + + goto __Exit_DsmpGetDeviceIdList; + } + + NT_ASSERT(descriptor->Size); + if (descriptor->Size == 0) { + status = STATUS_UNSUCCESSFUL; + goto __Exit_DsmpGetDeviceIdList; + } + + // + // This time we know how much data there is so we can + // allocate a buffer of the correct size + // + length = descriptor->Size; + + descriptor = DsmpAllocatePool(NonPagedPoolNx, length, DSM_TAG_DEVICE_ID_LIST); + + if(!descriptor) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpGetDeviceIdList (DevObj %p): Couldn't allocate descriptor of %ld.\n", + DeviceObject, + length)); + + status = STATUS_INSUFFICIENT_RESOURCES; + goto __Exit_DsmpGetDeviceIdList; + } + + // + // setup the query again. + // + query.PropertyId = StorageDeviceIdProperty; + query.QueryType = PropertyStandardQuery; + query.AdditionalParameters[0] = 0; + + // + // copy the input to the new outputbuffer + // + RtlCopyMemory(descriptor, + &query, + sizeof(STORAGE_PROPERTY_QUERY)); + + DsmSendDeviceIoControlSynchronous(IOCTL_STORAGE_QUERY_PROPERTY, + DeviceObject, + descriptor, + descriptor, + sizeof(STORAGE_PROPERTY_QUERY), + length, + 0, + ioStatus); + + status = ioStatus->Status; + + if(!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpGetDeviceIdList (DevObj %p): Query Failed (%x) on attempt 2.\n", + DeviceObject, + ioStatus->Status)); + + goto __Exit_DsmpGetDeviceIdList; + } + +__Exit_DsmpGetDeviceIdList: + + if (ioStatus) { + DsmpFreePool(ioStatus); + } + + if (!NT_SUCCESS(status)) { + + if (descriptor) { + DsmpFreePool(descriptor); + } + + } else { + *Descriptor = descriptor; + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpGetDeviceIdList (DevObj %p): Exiting function with status %x.\n", + DeviceObject, + status)); + + return status; +} + + +NTSTATUS +DsmpSetTargetPortGroups( + _In_ IN PDEVICE_OBJECT DeviceObject, + _In_reads_bytes_(TargetPortGroupsInfoLength) IN PUCHAR TargetPortGroupsInfo, + _In_ IN ULONG TargetPortGroupsInfoLength + ) +/*++ + +Routine Description: + + Helper routine to send down SetTargetPortGroups request. + +Arguments: + + DeviceObject - The port PDO to which the command should be sent. + TargetPortGroupsInfo - buffer containing the TPG data. + TargetPortGroupsInfoLength - size of the TPG buffer. + +Return Value: + + STATUS_SUCCESS or appropriate failure code. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER passThrough; + PSPC3_CDB_SET_TARGET_PORT_GROUPS cdb; + IO_STATUS_BLOCK ioStatus; + ULONG alignmentMask = DeviceObject->AlignmentRequirement; + PUCHAR dataBuffer = NULL; + SIZE_T allocatedLength = 0; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpSetTargetPortGroups (DevObj %p): Entering function.\n", + DeviceObject)); + + NT_ASSERT(TargetPortGroupsInfoLength && TargetPortGroupsInfo); + + // + // Build request. + // + RtlZeroMemory(&passThrough, sizeof(passThrough)); + + dataBuffer = DsmpAllocateAlignedPool(NonPagedPoolNx, + TargetPortGroupsInfoLength, + alignmentMask, + DSM_TAG_PASS_THRU, + &allocatedLength); + if (!dataBuffer) { + + status = STATUS_INSUFFICIENT_RESOURCES; + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_GENERAL, + "DsmpSetTargetPortGroups (DevObj %p): Failed to allocate mem for passthrough's databuffer.\n", + DeviceObject)); + goto __Exit_DsmpSetTargetPortGroups; + } + +__Retry_Request: + + // + // Build the cdb. + // + cdb = (PSPC3_CDB_SET_TARGET_PORT_GROUPS)passThrough.ScsiPassThroughDirect.Cdb; + + cdb->OperationCode = SPC3_SCSIOP_SET_TARGET_PORT_GROUPS; + cdb->ServiceAction = SPC3_SERVICE_ACTION_TARGET_PORT_GROUPS; + Get4ByteArrayFromUlong(TargetPortGroupsInfoLength, cdb->ParameterListLength); + + passThrough.ScsiPassThroughDirect.Length = sizeof(SCSI_PASS_THROUGH_DIRECT); + passThrough.ScsiPassThroughDirect.CdbLength = 12; + passThrough.ScsiPassThroughDirect.SenseInfoLength = SPTWB_SENSE_LENGTH; + passThrough.ScsiPassThroughDirect.DataIn = 0; + passThrough.ScsiPassThroughDirect.DataTransferLength = TargetPortGroupsInfoLength; + passThrough.ScsiPassThroughDirect.TimeOutValue = 20; + passThrough.ScsiPassThroughDirect.SenseInfoOffset = offsetof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER, SenseInfoBuffer); + passThrough.ScsiPassThroughDirect.DataBuffer = dataBuffer; + RtlCopyMemory(dataBuffer, + TargetPortGroupsInfo, + TargetPortGroupsInfoLength); + + DsmSendDeviceIoControlSynchronous(IOCTL_SCSI_PASS_THROUGH_DIRECT, + DeviceObject, + &passThrough, + &passThrough, + sizeof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER), + sizeof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER), + FALSE, + &ioStatus); + + if ((passThrough.ScsiPassThroughDirect.ScsiStatus == SCSISTAT_GOOD) && + (NT_SUCCESS(ioStatus.Status))) { + + status = STATUS_SUCCESS; + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_GENERAL, + "DsmpSetTargetPortGroups (DevObj %p): STPG succeeded.\n", + DeviceObject)); + + } else if (NT_SUCCESS(ioStatus.Status) && + passThrough.ScsiPassThroughDirect.ScsiStatus == SCSISTAT_CHECK_CONDITION && + DsmpShouldRetryTPGRequest((PSENSE_DATA)&passThrough.SenseInfoBuffer, passThrough.ScsiPassThroughDirect.SenseInfoLength)) { + + // + // Retry the request + // + RtlZeroMemory(dataBuffer, TargetPortGroupsInfoLength); + goto __Retry_Request; + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_GENERAL, + "DsmpSetTargetPortGroups (DevObj %p): NTStatus 0%x, ScsiStatus 0x%x.\n", + DeviceObject, + ioStatus.Status, + passThrough.ScsiPassThroughDirect.ScsiStatus)); + + status = ioStatus.Status; + } + +__Exit_DsmpSetTargetPortGroups: + + // + // Free the passthrough + data buffer. + // + if (dataBuffer) { + DsmpFreePool(dataBuffer); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpSetTargetPortGroups (DevObj %p): Exiting function with status %x.\n", + DeviceObject, + status)); + + return status; +} + + +NTSTATUS +DsmpSetTargetPortGroupsAsync( + _In_ IN PDSM_DEVICE_INFO DeviceInfo, + _In_ IN PIO_COMPLETION_ROUTINE CompletionRoutine, + _In_ __drv_aliasesMem IN PDSM_TPG_COMPLETION_CONTEXT CompletionContext, + _In_ IN ULONG TargetPortGroupsInfoLength, + _In_ __drv_aliasesMem IN PUCHAR TargetPortGroupsInfo + ) +/*++ + +Routine Description: + + Helper routine to send down SetTargetPortGroups request asynchronously. + + IMPORTANT: Caller needs to free the IRP and allocated system buffer. + +Arguments: + + DeviceInfo - The deviceInfo whose corresponding port PDO the command should be sent to. + CompletionRoutine - completion routine provided by the caller. + CompletionContext - context passed into the completion routine. + TargetPortGroupsInfoLength - size of the TPG buffer. + TargetPortGroupsInfo - buffer containing the TPG data. + +Return Value: + + STATUS_SUCCESS or appropriate failure code. + +--*/ +{ + PDSM_TPG_COMPLETION_CONTEXT tpgCompletionContext = CompletionContext; + PSCSI_REQUEST_BLOCK srb; + PSPC3_CDB_SET_TARGET_PORT_GROUPS cdb; + NTSTATUS status; + PIRP irp = NULL; + PIO_STACK_LOCATION irpStack; + PMDL mdl = NULL; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmpSetTargetPortGroupsAsync (DevInfo %p): Entering function.\n", + DeviceInfo)); + + srb = tpgCompletionContext->Srb; + + SrbZeroSrb(srb); + + // + // Allocate an irp. + // + irp = IoAllocateIrp(DeviceInfo->TargetObject->StackSize + 1, FALSE); + if (!irp) { + status = STATUS_INSUFFICIENT_RESOURCES; + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmpSetTargetPortGroupsAsync (DevInfo %p): Failed to allocate IRP.\n", + DeviceInfo)); + goto __Exit_DsmpSetTargetPortGroupsAsync; + } + + mdl = IoAllocateMdl(TargetPortGroupsInfo, + TargetPortGroupsInfoLength, + FALSE, + FALSE, + irp); + if (!mdl) { + + status = STATUS_INSUFFICIENT_RESOURCES; + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_RW, + "DsmpSetTargetPortGroupsAsync (DevInfo %p): Failed to allocate MDL.\n", + DeviceInfo)); + goto __Exit_DsmpSetTargetPortGroupsAsync; + } + + MmBuildMdlForNonPagedPool(irp->MdlAddress); + + // + // It is possible that an implicit state transition may have occurred which + // will cause every I_T nexus to return an UA (for asymmetric access state + // changed). So set the number of retries to number of paths (worst case of + // every path being a separate I_T nexus) plus one for a retry down one of + // paths. + // + tpgCompletionContext->NumberRetries = DeviceInfo->Group->NumberDevices + 1; + + // + // Set-up the completion routine. + // + IoSetCompletionRoutine(irp, + CompletionRoutine, + (PVOID)CompletionContext, + TRUE, + TRUE, + TRUE); + + // + // Get the recipient's irpstack location. + // + irpStack = IoGetNextIrpStackLocation(irp); + + irpStack->Parameters.Scsi.Srb = srb; + irpStack->DeviceObject = DeviceInfo->TargetObject; + + // + // Set the major function code to IRP_MJ_SCSI. + // + irpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; + + // + // Set the minor function, or many requests will get kicked by by port. + // + irpStack->MinorFunction = IRP_MN_SCSI_CLASS; + + srb->Function = SRB_FUNCTION_EXECUTE_SCSI; + srb->Length = sizeof(SCSI_REQUEST_BLOCK); + + SrbSetCdbLength(srb, sizeof(SPC3_CDB_SET_TARGET_PORT_GROUPS)); + cdb = (PSPC3_CDB_SET_TARGET_PORT_GROUPS)SrbGetCdb(srb); + cdb->OperationCode = SPC3_SCSIOP_SET_TARGET_PORT_GROUPS; + cdb->ServiceAction = SPC3_SERVICE_ACTION_TARGET_PORT_GROUPS; + Get4ByteArrayFromUlong(TargetPortGroupsInfoLength, cdb->ParameterListLength); + + SrbSetTimeOutValue(srb, SPC3_SET_TARGET_PORT_GROUPS_TIMEOUT); + SrbSetSenseInfoBuffer(srb, tpgCompletionContext->SenseInfoBuffer); + SrbSetSenseInfoBufferLength(srb, tpgCompletionContext->SenseInfoBufferLength); + SrbSetDataTransferLength(srb, TargetPortGroupsInfoLength); + SrbSetDataBuffer(srb, TargetPortGroupsInfo); + srb->SrbStatus = 0; + SrbSetScsiStatus(srb, 0); + SrbSetNextSrb(srb, NULL); + SrbSetSrbFlags(srb, SRB_FLAGS_DONT_START_NEXT_PACKET | SRB_FLAGS_QUEUE_ACTION_ENABLE | + SRB_FLAGS_DATA_OUT | SRB_FLAGS_DISABLE_SYNCH_TRANSFER | + SRB_FLAGS_BYPASS_FROZEN_QUEUE | SRB_FLAGS_NO_QUEUE_FREEZE); + SrbSetQueueAction(srb, SRB_HEAD_OF_QUEUE_TAG_REQUEST); + SrbSetOriginalRequest(srb, irp); + + irp->UserBuffer = TargetPortGroupsInfo; + irp->Tail.Overlay.Thread = PsGetCurrentThread(); + + // + // Send the IRP asynchronously + // + DsmSendRequestEx(((PDSM_CONTEXT)(DeviceInfo->DsmContext))->MPIOContext, + DeviceInfo->TargetObject, + irp, + DeviceInfo, + DSM_CALL_COMPLETION_ON_MPIO_ERROR); + + // + // We know that the completion routine will always be called. + // + status = STATUS_PENDING; + + +__Exit_DsmpSetTargetPortGroupsAsync: + + if (status != STATUS_PENDING) { + + // + // This indicates Irp was never sent down to stack (completion routine was never called). + // We need to clean up. + // + if (irp) { + + if (irp->MdlAddress) { + IoFreeMdl(irp->MdlAddress); + } + + IoFreeIrp(irp); + } + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_RW, + "DsmpSetTargetPortGroupsAsync (DevInfo %p): Exiting function with status %x.\n", + DeviceInfo, + status)); + + return status; +} + + +PDSM_LOAD_BALANCE_POLICY_SETTINGS +DsmpCopyLoadBalancePolicies( + _In_ IN PDSM_GROUP_ENTRY GroupEntry, + _In_ IN ULONG DsmWmiVersion, + _In_ IN PVOID SupportedLBPolicies + ) +/*+++ + +Routine Description: + + This routine copies the LB Policies that needs to be persisted in registry. + This is done because registry routines can be called at PASSIVE IRQL only. + So a spinlock cannot be held while accessing registry. So hold a spinlock, + save the values in a temp buffer, release spinlock, and save data to registry + from the temp buffer. + + NOTE: This routine MUST be called with DSM_CONTEXT lock held. + +Arguements: + + GroupEntry - Group entry + DsmWmiVersion - version of the MPIO_DSM_Path class to use + SupportedLBPolicies - LB policy for the group + + Return Value: + + Pointer to LOAD_BALANCE_POLICY_SETTINGS if successful. Else, NULL +--*/ +{ + PDSM_LOAD_BALANCE_POLICY_SETTINGS lbSettings = NULL; + ULONG sizeNeeded; + ULONG inx; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpCopyLoadBalancePolicies (Group %p): Entering function.\n", + GroupEntry)); + + if (((PDSM_Load_Balance_Policy_V2)SupportedLBPolicies)->DSMPathCount == 0) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpCopyLoadBalancePolicies (Group %p): No paths specified in Set LB policies.\n", + GroupEntry)); + + goto __Exit_DsmpCopyLoadBalancePolicies; + } + + sizeNeeded = sizeof(DSM_LOAD_BALANCE_POLICY_SETTINGS) + + ((((PDSM_Load_Balance_Policy_V2)SupportedLBPolicies)->DSMPathCount - 1) * sizeof(MPIO_DSM_Path_V2));; + + lbSettings = DsmpAllocatePool(NonPagedPoolNx, + sizeNeeded, + DSM_TAG_LB_POLICY); + + if (!lbSettings) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpCopyLoadBalancePolicies (Group %p): Failed to allocate memory for LBSettings.\n", + GroupEntry)); + goto __Exit_DsmpCopyLoadBalancePolicies; + } + + // + // Copy the registry key name used to store the LB policies. + // + RtlStringCchCopyNW(lbSettings->RegistryKeyName, + sizeof(lbSettings->RegistryKeyName) / sizeof(lbSettings->RegistryKeyName[0]), + GroupEntry->RegistryKeyName, + ((sizeof(lbSettings->RegistryKeyName) - sizeof(WCHAR))/sizeof(WCHAR))); + + // + // Copy the Load Balance settings for this group + // + lbSettings->LoadBalancePolicy = ((PDSM_Load_Balance_Policy_V2)SupportedLBPolicies)->LoadBalancePolicy; + + lbSettings->PathCount = ((PDSM_Load_Balance_Policy_V2)SupportedLBPolicies)->DSMPathCount; + + for (inx = 0; inx < ((PDSM_Load_Balance_Policy_V2)SupportedLBPolicies)->DSMPathCount; inx++) { + + if (DsmWmiVersion == DSM_WMI_VERSION_1) { + + RtlCopyMemory(&(lbSettings->DsmPath[inx]), + &(((PDSM_Load_Balance_Policy)SupportedLBPolicies)->DSM_Paths[inx]), + sizeof(MPIO_DSM_Path)); + + // + // DSM_WMI_VERSION_1 supports only active and standby states + // + (lbSettings->DsmPath[inx]).OptimizedPath = TRUE; + + } else { + + RtlCopyMemory(&(lbSettings->DsmPath[inx]), + &(((PDSM_Load_Balance_Policy_V2)SupportedLBPolicies)->DSM_Paths[inx]), + sizeof(MPIO_DSM_Path_V2)); + } + } + +__Exit_DsmpCopyLoadBalancePolicies: + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpCopyLoadBalancePolicies (Group %p): Exiting function with lbSettings %p.\n", + GroupEntry, + lbSettings)); + + return lbSettings; +} + + +NTSTATUS +DsmpPersistLBSettings( + _In_ IN PDSM_LOAD_BALANCE_POLICY_SETTINGS LoadBalanceSettings + ) +/*+++ + +Routine Description: + + This routine will save the Load Balance settings from LoadBalanceSettings + to registry. + + NOTE: This routine MUST be called at PASSIVE IRQL + + The format of the registry tree is : + + Services\MSDSM\LoadBalanceSettings -> + + DeviceName -> LoadBalancePolicy REG_DWORD + + DsmPath#Suffix -> PrimaryPath REG_DWORD + OptimizedPath REG_DWORD + PathWeight REG_DWORD + + The device name is the one built in DsmpBuildDeviceName + + The Suffix in DsmPath#Suffix is built from the PathId. It is built in + the routine DsmpGetDSMPathKeyName. + +Arguements: + + LoadBalanceSettings - Load Balance settings to be persisted in registry + +Return Value: + + STATUS_SUCCESS if the data could be successfully stored in the registry + Appropriate NT Status code on failure. +--*/ +{ + PMPIO_DSM_Path_V2 dsmPath; + HANDLE lbSettingsKey = NULL; + HANDLE deviceKey = NULL; + HANDLE dsmPathKey = NULL; + UNICODE_STRING subKeyName; + WCHAR dsmPathName[128]; + OBJECT_ATTRIBUTES objectAttributes; + NTSTATUS status; + ULONG inx; + PMPIO_DSM_Path_V2 preferredPath = NULL; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpPersistLBSettings (DevName %ws): Entering function.\n", + LoadBalanceSettings->RegistryKeyName)); + + // + // First open LoadBalanceSettings key under the Service key + // + status = DsmpOpenLoadBalanceSettingsKey(KEY_ALL_ACCESS, &lbSettingsKey); + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpPersistLBSettings (DevName %ws): Failed to open LB Settings key. Status %x.\n", + LoadBalanceSettings->RegistryKeyName, + status)); + + goto __Exit_DsmpPersistLBSettings; + } + + // + // Now open the key under which the LB settings for the given device is stored + // + RtlInitUnicodeString(&subKeyName, LoadBalanceSettings->RegistryKeyName); + + InitializeObjectAttributes(&objectAttributes, + &subKeyName, + (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE), + lbSettingsKey, + (PSECURITY_DESCRIPTOR) NULL); + + status = ZwOpenKey(&deviceKey, KEY_ALL_ACCESS, &objectAttributes); + + if (NT_SUCCESS(status)) { + + // + // Remove all LB policy information as we are going to rewrite it. + // We do this in case there is stale information about a path that + // no longer exists + // + status = DsmpRegDeleteTree(deviceKey); + + if (NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_WMI, + "DsmpPersistLBSettings (DevName %ws): Deleted key along with its subkeys.\n", + LoadBalanceSettings->RegistryKeyName)); + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpPersistLBSettings (DevName %ws): Failed to delete key. Status %x\n", + LoadBalanceSettings->RegistryKeyName, + status)); + + } + + ZwClose(deviceKey); + deviceKey = NULL; + + } + + status = ZwCreateKey(&deviceKey, + KEY_ALL_ACCESS, + &objectAttributes, + 0, + NULL, + REG_OPTION_NON_VOLATILE, + NULL); + + if (NT_SUCCESS(status)) { + + PDSM_DEVICE_INFO devInfo; + + for (inx = 0; inx < LoadBalanceSettings->PathCount; inx++) { + + dsmPath = &(LoadBalanceSettings->DsmPath[inx]); + + if (dsmPath->DsmPathId == 0) { + + continue; + } + + RtlZeroMemory(dsmPathName, sizeof(dsmPathName)); + + // + // Get the sub key name under which the LB settings for + // the given path is stored. + // + DsmpGetDSMPathKeyName(dsmPath->DsmPathId, dsmPathName, 128); + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_WMI, + "DsmpPersistLBSettings (DevName %ws): Will open subkey %ws.\n", + LoadBalanceSettings->RegistryKeyName, + dsmPathName)); + + RtlInitUnicodeString(&subKeyName, dsmPathName); + + RtlZeroMemory(&objectAttributes, sizeof(OBJECT_ATTRIBUTES)); + + InitializeObjectAttributes(&objectAttributes, + &subKeyName, + (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE), + deviceKey, + (PSECURITY_DESCRIPTOR) NULL); + + status = ZwCreateKey(&dsmPathKey, + KEY_ALL_ACCESS, + &objectAttributes, + 0, + NULL, + REG_OPTION_NON_VOLATILE, + NULL); + + if (NT_SUCCESS(status)) { + + if (dsmPath->PreferredPath) { + + preferredPath = dsmPath; + } + + devInfo = (PDSM_DEVICE_INFO)dsmPath->Reserved; + + // + // Save PrimaryPath, PathWeight and OptimizedPath values for this path + // + if (devInfo->DesiredState != DSM_DEV_UNDETERMINED) { + + status = RtlWriteRegistryValue(RTL_REGISTRY_HANDLE, + dsmPathKey, + DSM_PRIMARY_PATH, + REG_DWORD, + &(dsmPath->PrimaryPath), + sizeof(ULONG)); + + if (NT_SUCCESS(status)) { + + status = RtlWriteRegistryValue(RTL_REGISTRY_HANDLE, + dsmPathKey, + DSM_OPTIMIZED_PATH, + REG_DWORD, + &(dsmPath->OptimizedPath), + sizeof(ULONG)); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpPersistLBSettings (DevName %ws): Failed to save OptimizedPath. Status %x.\n", + LoadBalanceSettings->RegistryKeyName, + status)); + } + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpPersistLBSettings (DevName %ws): Failed to save Primary Path. Status %x.\n", + LoadBalanceSettings->RegistryKeyName, + status)); + } + } + + if (NT_SUCCESS(status)) { + + if (LoadBalanceSettings->LoadBalancePolicy == DSM_LB_WEIGHTED_PATHS) { + + status = RtlWriteRegistryValue(RTL_REGISTRY_HANDLE, + dsmPathKey, + DSM_PATH_WEIGHT, + REG_DWORD, + &(dsmPath->PathWeight), + sizeof(ULONG)); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpPersistLBSettings (DevName %ws): Failed to save PathWeight. Status %x.\n", + LoadBalanceSettings->RegistryKeyName, + status)); + } + } + } + + ZwClose(dsmPathKey); + dsmPathKey = NULL; + } else { + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpPersistLBSettings (DevName %ws): Failed to open DSM Path key. Status %x.\n", + LoadBalanceSettings->RegistryKeyName, + status)); + } + + if (!NT_SUCCESS(status)) { + break; + } + } + + if (NT_SUCCESS(status)) { + + // + // Save the new Load Balance Policy value, + // + status = RtlWriteRegistryValue(RTL_REGISTRY_HANDLE, + deviceKey, + DSM_LOAD_BALANCE_POLICY, + REG_DWORD, + &(LoadBalanceSettings->LoadBalancePolicy), + sizeof(ULONG)); + if (NT_SUCCESS(status)) { + + UCHAR explicitlySet = TRUE; + + // + // Write out that the policy has been explicitly set + // + status = RtlWriteRegistryValue(RTL_REGISTRY_HANDLE, + deviceKey, + DSM_POLICY_EXPLICITLY_SET, + REG_BINARY, + &explicitlySet, + sizeof(UCHAR)); + + if (NT_SUCCESS(status)) { + + // + // If FailOver-Only policy, set the PreferredPath, if specified + // + if (preferredPath) { + + status = RtlWriteRegistryValue(RTL_REGISTRY_HANDLE, + deviceKey, + DSM_PREFERRED_PATH, + REG_BINARY, + &(preferredPath->DsmPathId), + sizeof(ULONGLONG)); + } + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpPersistLBSettings (DevName %ws): Failed to save LB Settings (ES).\n", + LoadBalanceSettings->RegistryKeyName)); + } + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpPersistLBSettings (DevName %ws): Failed to save LB Settings (LBP).\n", + LoadBalanceSettings->RegistryKeyName)); + } + } + } + +__Exit_DsmpPersistLBSettings: + + if (dsmPathKey) { + ZwClose(dsmPathKey); + } + + if (deviceKey) { + ZwClose(deviceKey); + } + + if (lbSettingsKey) { + ZwClose(lbSettingsKey); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpPersistLBSettings (DevName %ws): Exiting function with status %x.\n", + LoadBalanceSettings->RegistryKeyName, + status)); + + return status; +} + + +NTSTATUS +DsmpSetDeviceALUAState( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_DEVICE_INFO DeviceInfo, + _In_ IN DSM_DEVICE_STATE DevState + ) +/*++ + +Routine Description: + + Helper routine to build the STPG info and send it down to modify the passed in + devInfo's state. + +Arguments: + + DsmContext - DSM context. + DeviceInfo - DevInfo whose state needs to be changed. + DevState - New state to be set. + +Return Value: + + STATUS_SUCCESS or appropriate failure code. + +--*/ +{ + PUCHAR targetPortGroupsInfo = NULL; + ULONG targetPortGroupsInfoLength; + PSPC3_SET_TARGET_PORT_GROUP_DESCRIPTOR tpgDescriptor = NULL; + NTSTATUS status; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpSetDeviceALUAState (DevInfo %p): Entering function.\n", + DeviceInfo)); + + // + // Send down SetTPG to set the appropriate access state + // (The TPG block will contain the header and a SetTPG descriptor). + // + targetPortGroupsInfoLength = SPC3_TARGET_PORT_GROUPS_HEADER_SIZE + + sizeof(SPC3_SET_TARGET_PORT_GROUP_DESCRIPTOR); + + targetPortGroupsInfo = DsmpAllocatePool(NonPagedPoolNx, + targetPortGroupsInfoLength, + DSM_TAG_TARGET_PORT_GROUPS); + + if (targetPortGroupsInfo) { + + tpgDescriptor = (PSPC3_SET_TARGET_PORT_GROUP_DESCRIPTOR)(targetPortGroupsInfo + SPC3_TARGET_PORT_GROUPS_HEADER_SIZE); + tpgDescriptor->AsymmetricAccessState = DevState; + REVERSE_BYTES_SHORT(&tpgDescriptor->TPG_Identifier, &DeviceInfo->TargetPortGroup->Identifier); + + status = DsmpSetTargetPortGroups(DeviceInfo->TargetObject, + targetPortGroupsInfo, + targetPortGroupsInfoLength); + + if (NT_SUCCESS(status)) { + + // + // An explicit transition may cause changes to some other TPGs. + // So we need to query for the states of all the TPGs and update + // our internal list and its elements. + // + status = DsmpGetDeviceALUAState(DsmContext, + DeviceInfo, + NULL); + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_GENERAL, + "DsmpSetDeviceALUAState (DevInfo %p): Failed to SetTPG with %x.\n", + DeviceInfo, + status)); + } + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_GENERAL, + "DsmpSetDeviceALUAState (DevInfo %p): Failed to allocate TPG.\n", + DeviceInfo)); + status = STATUS_INSUFFICIENT_RESOURCES; + } + + if (targetPortGroupsInfo) { + DsmpFreePool(targetPortGroupsInfo); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpSetDeviceALUAState (DevInfo %p): Exiting function with status %x\n", + DeviceInfo, + status)); + + return status; +} + + +NTSTATUS +DsmpAdjustDeviceStatesALUA( + _In_ IN PDSM_GROUP_ENTRY Group, + _In_opt_ IN PDSM_DEVICE_INFO PreferredActiveDeviceInfo, + _In_ IN ULONG SpecialHandlingFlag + ) +/*++ + +Routine Description: + + Helper routine to build the adjust every device state in the group taking + the following into consideration: + 1. PreferredActiveDeviceInfo + 2. DeviceInfo's TPG state + 3. Preferred Path + 4. LB Policy + +Arguments: + + Group - Pseudo-LUN whose path states need to be adjusted. + PreferredActiveDeviceInfo - DevInfo whose state needs to preferrably made + A/O, if possible. This parameter is optional. + + SpecialHandlingFlag - Flags to indicate any special handling requirement + +Return Value: + + STATUS_SUCCESS or appropriate failure code. + +--*/ +{ + ULONG index; + PDSM_DEVICE_INFO deviceInfo; + PDSM_DEVICE_INFO activeDevice = NULL; + DSM_DEVICE_STATE devState; + NTSTATUS status = STATUS_SUCCESS; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpAdjustDeviceStatesALUA (Group %p): Entering function with preferred active devInfo %p.\n", + Group, + PreferredActiveDeviceInfo)); + + // + // Ensure that: + // 1. All devices match their ALUA state. + // 2. For RRWS, if a device's desired state is non-A/O, but ALUA state is A/O, mask it. + // 3. For FOO there must be only one A/O device. Preferably the preferred path. + // + for (index = 0; index < DSM_MAX_PATHS; index++) { + + deviceInfo = Group->DeviceList[index]; + + if (deviceInfo) { + + devState = deviceInfo->State; + + if (!DsmpIsDeviceFailedState(deviceInfo->State) && + DsmpIsDeviceInitialized(deviceInfo) && + DsmpIsDeviceUsable(deviceInfo) && + DsmpIsDeviceUsablePR(deviceInfo)) { + + + deviceInfo->PreviousState = deviceInfo->State; + deviceInfo->State = deviceInfo->ALUAState; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p transitioning from %u to %u (its ALUA state).\n", + Group, + deviceInfo, + deviceInfo->PreviousState, + deviceInfo->State)); + + if (deviceInfo->ALUAState == DSM_DEV_ACTIVE_OPTIMIZED) { + + // + // In FOO and RRWS, we need to mask states. + // + switch (Group->LoadBalanceType) { + case DSM_LB_FAILOVER: { + + // + // Cache the first available devInfo that is in A/O + // + if (!activeDevice) { + + activeDevice = deviceInfo; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p choosen as the active device.\n", + Group, + activeDevice)); + + break; + } + + // + // Check if this deviceInfo is the preferred path. If yes, + // mask the active device's state and make this the new + // active device. + // + if (Group->PreferredPath == (ULONGLONG)((ULONG_PTR)deviceInfo->FailGroup->PathId)) { + + activeDevice->PreviousState = activeDevice->State; + activeDevice->State = (activeDevice->DesiredState == DSM_DEV_UNDETERMINED || + activeDevice->DesiredState == DSM_DEV_ACTIVE_OPTIMIZED) ? DSM_DEV_ACTIVE_UNOPTIMIZED : activeDevice->DesiredState; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpAdjustDeviceStatesALUA (Group %p): Previous active devInfo %p transitioning from %u to %u.\n", + Group, + activeDevice, + activeDevice->PreviousState, + activeDevice->State)); + + activeDevice = deviceInfo; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p is now the new active device (preferred path).\n", + Group, + activeDevice)); + + break; + } + + // + // If active device's desired state is not A/O but this + // deviceInfo's is, then mask the active device's state + // and make this one the new active device. + // + if (activeDevice->DesiredState != DSM_DEV_ACTIVE_OPTIMIZED && + activeDevice->DesiredState != DSM_DEV_UNDETERMINED) { + + // + // The exception though is if the current active device + // is the preferred path + // + if (Group->PreferredPath == (ULONGLONG)((ULONG_PTR)activeDevice->FailGroup->PathId)) { + + deviceInfo->PreviousState = deviceInfo->State; + deviceInfo->State = (deviceInfo->DesiredState == DSM_DEV_UNDETERMINED || + deviceInfo->DesiredState == DSM_DEV_ACTIVE_OPTIMIZED) ? DSM_DEV_ACTIVE_UNOPTIMIZED : deviceInfo->DesiredState; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p transitioning from %u to %u (active DI is PrefPath).\n", + Group, + deviceInfo, + deviceInfo->PreviousState, + deviceInfo->State)); + } else { + + // + // If this is the devInfo that is preferred to be A/O, make it such + // + if (PreferredActiveDeviceInfo && + PreferredActiveDeviceInfo == deviceInfo) { + + activeDevice->PreviousState = activeDevice->State; + activeDevice->State = activeDevice->DesiredState; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p transitioning from %u to %u (found a preferred active DI).\n", + Group, + activeDevice, + activeDevice->PreviousState, + activeDevice->State)); + + activeDevice = deviceInfo; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p is now the new active DI (preferred).\n", + Group, + activeDevice)); + } else { + + // + // Check if this devInfo desires to be in A/O, since the currently + // active one doesn't want to be. + // + if (deviceInfo->DesiredState != DSM_DEV_ACTIVE_OPTIMIZED && + deviceInfo->DesiredState != DSM_DEV_UNDETERMINED) { + + // + // This deviceInfo's desire is also not to be in A/O, + // so just leave the current one active. + // + if (devState == DSM_DEV_ACTIVE_OPTIMIZED) { + + // + // Exception is if we're processing the device whose state before + // RTPG was sent was already A/O, it is best to leave this device + // in A/O state. + // + + activeDevice->PreviousState = activeDevice->State; + activeDevice->State = (activeDevice->DesiredState == DSM_DEV_UNDETERMINED || + activeDevice->DesiredState == DSM_DEV_ACTIVE_OPTIMIZED) ? DSM_DEV_ACTIVE_UNOPTIMIZED : activeDevice->DesiredState; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p transitioning from %u to %u. Found a DI that was previously active.\n", + Group, + activeDevice, + activeDevice->PreviousState, + activeDevice->State)); + + activeDevice = deviceInfo; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p now the new active device (previously A/O).\n", + Group, + activeDevice)); + } else { + + // + // This device wasn't in A/O state before, so just leave + // the currently selected active device as is. + // + deviceInfo->PreviousState = deviceInfo->State; + deviceInfo->State = deviceInfo->DesiredState; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p transitioning from %u to %u (active device already exists).\n", + Group, + deviceInfo, + deviceInfo->PreviousState, + deviceInfo->State)); + } + } else { + + // + // Current devInfo wants (or doesn't) mind being in + // A/O, whereas the current active device doesn't, so + // mask the active device and make this devInfo the + // active device. + // + activeDevice->PreviousState = activeDevice->State; + activeDevice->State = activeDevice->DesiredState; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p transitioning from %u to %u. Current DI prefers being A/O.\n", + Group, + activeDevice, + activeDevice->PreviousState, + activeDevice->State)); + + activeDevice = deviceInfo; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p is now the new active device (desired state).\n", + Group, + activeDevice)); + } + } + } + } else { + + // + // The single overriding factor is always the preferred path. + // Everything else is secondary, so first check if the currently + // active device can even be overridden by another one. + // + if (Group->PreferredPath != (ULONGLONG)((ULONG_PTR)activeDevice->FailGroup->PathId)) { + + // + // It can't be overridden, so we're done. + // + deviceInfo->PreviousState = deviceInfo->State; + deviceInfo->State = (deviceInfo->DesiredState == DSM_DEV_UNDETERMINED || + deviceInfo->DesiredState == DSM_DEV_ACTIVE_OPTIMIZED) ? DSM_DEV_ACTIVE_UNOPTIMIZED : deviceInfo->DesiredState; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p transitioning from %u to %u (current active DI is PrefPath).\n", + Group, + deviceInfo, + deviceInfo->PreviousState, + deviceInfo->State)); + } else { + + // + // Active device's desired state is A/O but it isn't the preferred + // path. Check if this devInfo is preferred as A/O. + // + if (PreferredActiveDeviceInfo && + PreferredActiveDeviceInfo == deviceInfo) { + + activeDevice->PreviousState = activeDevice->State; + activeDevice->State = (activeDevice->DesiredState == DSM_DEV_UNDETERMINED || + activeDevice->DesiredState == DSM_DEV_ACTIVE_OPTIMIZED) ? DSM_DEV_ACTIVE_UNOPTIMIZED : activeDevice->DesiredState; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p transitioning from %u to %u. New DI is preferred active.\n", + Group, + activeDevice, + activeDevice->PreviousState, + activeDevice->State)); + + activeDevice = deviceInfo; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p is now the new active device (this DI is preferred active).\n", + Group, + activeDevice)); + } else { + + // + // Active device's desired state is A/O but it isn't the + // preferred path. Check if this devInfo's desired state + // is also A/O. If yes, we'll need to make certain decisions. + // + if (deviceInfo->DesiredState != DSM_DEV_ACTIVE_OPTIMIZED && + deviceInfo->DesiredState != DSM_DEV_UNDETERMINED) { + + // + // Since this device doesn't desire to be in + // A/O and we already have an active device, just + // mask its state. + // + deviceInfo->PreviousState = deviceInfo->State; + deviceInfo->State = deviceInfo->DesiredState; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p transitioning from %u to %u (prefers being in non-A/O).\n", + Group, + deviceInfo, + deviceInfo->PreviousState, + deviceInfo->State)); + } else { + + // + // Active device is in A/O and this device desires to be in + // A/O too. Make this the new active device only if its state + // before the RTPG was already A/O. + // + if (devState == DSM_DEV_ACTIVE_OPTIMIZED) { + + activeDevice->PreviousState = activeDevice->State; + activeDevice->State = (activeDevice->DesiredState == DSM_DEV_UNDETERMINED || + activeDevice->DesiredState == DSM_DEV_ACTIVE_OPTIMIZED) ? DSM_DEV_ACTIVE_UNOPTIMIZED : activeDevice->DesiredState; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p transitioning from %u to %u (new DI was already in A/O previously).\n", + Group, + activeDevice, + activeDevice->PreviousState, + activeDevice->State)); + + activeDevice = deviceInfo; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p is new active device (since it was in A/O previously too).\n", + Group, + activeDevice)); + } else { + + // + // Just leave the currently active one alone. + // + deviceInfo->PreviousState = deviceInfo->State; + deviceInfo->State = (deviceInfo->DesiredState == DSM_DEV_UNDETERMINED || + deviceInfo->DesiredState == DSM_DEV_ACTIVE_OPTIMIZED) ? DSM_DEV_ACTIVE_UNOPTIMIZED : deviceInfo->DesiredState; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p transitioning from %u to %u (leave current active DI alone).\n", + Group, + deviceInfo, + deviceInfo->PreviousState, + deviceInfo->State)); + } + } + } + } + } + break; + } + + case DSM_LB_ROUND_ROBIN_WITH_SUBSET: { + + // + // At least one path needs to be in A/O state, so + // cache the first available devInfo that is in A/O + // + if (!activeDevice) { + + activeDevice = deviceInfo; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpAdjustDeviceStatesALUA (Group %p): We have atleast one A/O DevInfo %p.\n", + Group, + activeDevice)); + + break; + } + + // + // Check if this device is preferred to be in A/O + // + if (PreferredActiveDeviceInfo && + PreferredActiveDeviceInfo == deviceInfo) { + + // + // If the currently active device, doesn't desire to be in + // A/O state, mask its state. + // + if (activeDevice->DesiredState != DSM_DEV_ACTIVE_OPTIMIZED && + activeDevice->DesiredState != DSM_DEV_UNDETERMINED) { + + activeDevice->PreviousState = activeDevice->State; + activeDevice->State = activeDevice->DesiredState; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p transitioning from %u to %u (desired state non-A/O).\n", + Group, + activeDevice, + activeDevice->PreviousState, + activeDevice->State)); + } + + activeDevice = deviceInfo; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p now the new active device (preferred active DI).\n", + Group, + activeDevice)); + } else { + + // + // If this device's desired state is specified and not A/O, + // mask its path state. + // + if (deviceInfo->DesiredState != DSM_DEV_ACTIVE_OPTIMIZED && + deviceInfo->DesiredState != DSM_DEV_UNDETERMINED) { + + deviceInfo->PreviousState = deviceInfo->State; + deviceInfo->State = deviceInfo->DesiredState; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p transitioning from %u to %u (desires to be in non-A/O).\n", + Group, + deviceInfo, + deviceInfo->PreviousState, + deviceInfo->State)); + } else { + + // + // Since this devInfo desires to be in A/O, we are assured + // of at least one path in A/O. So check to see if the + // currently active device doesn't desire to be in A/O. + // + if (activeDevice->DesiredState != DSM_DEV_ACTIVE_OPTIMIZED && + activeDevice->DesiredState != DSM_DEV_UNDETERMINED) { + + activeDevice->PreviousState = activeDevice->State; + activeDevice->State = activeDevice->DesiredState; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p transitioning from %u to %u (new DI desires to be in A/O).\n", + Group, + activeDevice, + activeDevice->PreviousState, + activeDevice->State)); + + activeDevice = deviceInfo; + + TracePrint((TRACE_LEVEL_INFORMATION, + TRACE_FLAG_GENERAL, + "DsmpAdjustDeviceStatesALUA (Group %p): DevInfo %p now the new active DI (desires to be in A/O).\n", + Group, + activeDevice)); + } + } + } + + break; + } + + default: { + + // + // For RR, LQD and WP, paths must be in the same + // state as their corresponding TPG. Preferably + // all should be A/O. + // + if (deviceInfo->State != DSM_DEV_ACTIVE_OPTIMIZED) { + DSM_ASSERT(deviceInfo->State == deviceInfo->ALUAState); + } + + break; + } + } + } + } + } + } + + // + // There may have been a change to the device states. + // DsmpGetPath() will pick these changes for RR, RRWS and LQD. + // However, it won't for FOO and WP, so update PTBU if needed. + // + if (Group->LoadBalanceType == DSM_LB_FAILOVER || + Group->LoadBalanceType == DSM_LB_WEIGHTED_PATHS) { + + deviceInfo = DsmpGetActivePathToBeUsed(Group, FALSE, SpecialHandlingFlag); + + if (deviceInfo) { + + InterlockedExchangePointer(&(Group->PathToBeUsed), deviceInfo->FailGroup); + } + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpAdjustDeviceStatesALUA (Group %p): Exiting function with status %x\n", + Group, + status)); + + return status; +} + + +PDSM_WORKITEM +DsmpAllocateWorkItem( + _In_ IN PDEVICE_OBJECT DeviceObject, + _In_ IN PVOID Context + ) +/*++ + +Routine Description: + + Allocates a work item to handle reservation failover. + +Arguments: + + DeviceObject - Target device. + Context - Workitem context + +Return Value: + + Allocated workitem or NULL (if low memory). + +--*/ +{ + PDSM_WORKITEM dsmWorkItem = NULL; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_IOCTL, + "DsmpAllocateWorkItem (DevObj %p): Entering function.\n", + DeviceObject)); + + dsmWorkItem = DsmpAllocatePool(NonPagedPoolNx, + sizeof(DSM_WORKITEM), + DSM_TAG_WORKITEM); + if (dsmWorkItem != NULL) { + + dsmWorkItem->WorkItem = IoAllocateWorkItem(DeviceObject); + if (dsmWorkItem->WorkItem != NULL) { + + dsmWorkItem->Context = Context; + } else { + + DsmpFreePool(dsmWorkItem); + dsmWorkItem = NULL; + } + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_IOCTL, + "DsmpAllocateWorkItem (DevObj %p): Exiting function. dsmWorkItem %p.\n", + DeviceObject, + dsmWorkItem)); + + return dsmWorkItem; +} + + +VOID +DsmpFreeWorkItem( + _In_ IN PDSM_WORKITEM DsmWorkItem + ) +{ + PVOID temp = DsmWorkItem; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_IOCTL, + "DsmpFreeWorkItem (WorkItem %p): Entering function.\n", + DsmWorkItem)); + + if (DsmWorkItem != NULL) { + + if (DsmWorkItem->WorkItem != NULL) { + IoFreeWorkItem(DsmWorkItem->WorkItem); + } + + DsmpFreePool(DsmWorkItem); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_IOCTL, + "DsmpFreeWorkItem (WorkItem %p): Exiting function.\n", + temp)); + + return; +} + + +VOID +DsmpFreeZombieGroupList( + _In_ IN PDSM_FAILOVER_GROUP FailGroup + ) +{ + PLIST_ENTRY zombieEntry = NULL; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpFreeZombieGroupList (FailGroup %p): Entering function.\n", + FailGroup)); + + while (!IsListEmpty(&FailGroup->ZombieGroupList)) { + + zombieEntry = RemoveHeadList(&FailGroup->ZombieGroupList); + + if (zombieEntry) { + + DsmpFreePool(zombieEntry); + } + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpFreeZombieGroupList (FailGroup %p): Exiting function.\n", + FailGroup)); +} + + +NTSTATUS +DsmpGetDeviceALUAState( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_DEVICE_INFO DeviceInfo, + _In_opt_ IN PDSM_DEVICE_STATE DevState + ) +/*++ + +Routine Description: + + Helper routine to build the RTPG info and send it down to retrieve the + devInfo's current state. + +Arguments: + + DsmContext - DSM context. + DeviceInfo - DevInfo whose state needs to be changed. + DevState - Current state of passed in DeviceInfo. + +Return Value: + + STATUS_SUCCESS or appropriate failure code. + +--*/ +{ + PUCHAR targetPortGroupsInfo = NULL; + ULONG targetPortGroupsInfoLength = 0; + PDSM_TARGET_PORT_GROUP_ENTRY targetPortGroup = NULL; + KIRQL irql; + NTSTATUS status; + ULONG index; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpGetDeviceALUAState (DevInfo %p): Entering function.\n", + DeviceInfo)); + + status = DsmpReportTargetPortGroups(DeviceInfo->TargetObject, + &targetPortGroupsInfo, + &targetPortGroupsInfoLength); + + + if (NT_SUCCESS(status) && targetPortGroupsInfo != NULL) { + + irql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); + + status = DsmpParseTargetPortGroupsInformation(DsmContext, + DeviceInfo->Group, + targetPortGroupsInfo, + targetPortGroupsInfoLength); + + for (index = 0; index < DSM_MAX_PATHS; index++) { + + targetPortGroup = DeviceInfo->Group->TargetPortGroupList[index]; + + if (targetPortGroup) { + + DsmpUpdateTargetPortGroupDevicesStates(targetPortGroup, targetPortGroup->AsymmetricAccessState); + } + } + + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); + + if (DevState) { + + *DevState = DeviceInfo->State; + } + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_GENERAL, + "DsmpGetDeviceALUAState (DevInfo %p): ReportTPG failed with %x.\n", + DeviceInfo, + status)); + } + + if (targetPortGroupsInfo) { + + DsmpFreePool(targetPortGroupsInfo); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_GENERAL, + "DsmpGetDeviceALUAState (DevInfo %p): Exiting function with status %x\n", + DeviceInfo, + status)); + + return status; +} + + +NTSTATUS +DsmpRegCopyTree( + _In_ IN HANDLE SourceKey, + _In_ IN HANDLE DestKey + ) +/*++ + +Routine Description: + + Copies a reg subtree from source key to destination key. + This routine will first copy over all the key's values, and then + copy the subkeys, each time recursively handling the subkey's + values and its subtree. + +Arguments: + + SourceKey - Handle to the root of the subtree to copy over. + DestKey - Handle to the root of the new tree. + +Return Value: + + STATUS_SUCCESS upon successfully coping over the tree. + Appropriate NT error code in case of failure. + +--*/ +{ + ULONG numValues = 0; + ULONG numSubKeys = 0; + ULONG lengthOfValueName = 0; + ULONG lengthOfValueData = 0; + ULONG lengthOfKeyName = 0; + LPWSTR valueBuf = NULL; + BYTE *valueDataBuf = NULL; + ULONG valueDataType; + ULONG titleIndex; + HANDLE srcSubKey = NULL; + HANDLE destSubKey = NULL; + LPWSTR subKey = NULL; + NTSTATUS status; + PKEY_FULL_INFORMATION keyFullInfo = NULL; + ULONG length = sizeof(KEY_FULL_INFORMATION); + ULONG index = 0; + PKEY_VALUE_FULL_INFORMATION keyValueFullInfo = NULL; + PKEY_BASIC_INFORMATION keyBasicInfo = NULL; + OBJECT_ATTRIBUTES objectAttributes; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpRegCopyTree (SrcKey %p): Entering function.\n", + SourceKey)); + + if (!SourceKey || !DestKey) { + + status = STATUS_INVALID_PARAMETER; + goto __Exit_DsmpRegCopyTree; + } + + // + // Query the source key for information about number of subkeys, number of values, etc. + // + do { + if (keyFullInfo) { + + DsmpFreePool(keyFullInfo); + } + + keyFullInfo = DsmpAllocatePool(NonPagedPoolNxCacheAligned, length, DSM_TAG_REG_KEY_RELATED); + + if (!keyFullInfo) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpRegCopyTree (SrcKey %p): Failed to allocate resources for key full info.\n", + SourceKey)); + + status = STATUS_INSUFFICIENT_RESOURCES; + goto __Exit_DsmpRegCopyTree; + } + + status = ZwQueryKey(SourceKey, + KeyFullInformation, + keyFullInfo, + length, + &length); + + } while (status == STATUS_BUFFER_TOO_SMALL || status == STATUS_BUFFER_OVERFLOW); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpRegCopyTree (SrcKey %p): Failed to query key. Status %x.\n", + SourceKey, + status)); + + goto __Exit_DsmpRegCopyTree; + } + + numSubKeys = keyFullInfo->SubKeys; + numValues = keyFullInfo->Values; + lengthOfKeyName = keyFullInfo->MaxNameLen + sizeof(WCHAR); + lengthOfValueName = keyFullInfo->MaxValueNameLen + sizeof(WCHAR); + lengthOfValueData = keyFullInfo->MaxValueDataLen + sizeof(WCHAR); + + // + // Allocate a buffer for the name of the value + // + valueBuf = DsmpAllocatePool(NonPagedPoolNxCacheAligned, + lengthOfValueName, + DSM_TAG_REG_KEY_RELATED); + if (!valueBuf) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpRegCopyTree (SrcKey %p): Failed to allocate resources for value name.\n", + SourceKey)); + + status = STATUS_INSUFFICIENT_RESOURCES; + goto __Exit_DsmpRegCopyTree; + } + + // + // Allocate a buffer for the value data + // + valueDataBuf = DsmpAllocatePool(NonPagedPoolNxCacheAligned, + lengthOfValueData, + DSM_TAG_REG_KEY_RELATED); + + if (!valueDataBuf) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpRegCopyTree (SrcKey %p): Failed to allocate resources for value's data.\n", + SourceKey)); + + status = STATUS_INSUFFICIENT_RESOURCES; + goto __Exit_DsmpRegCopyTree; + } + + // + // First enumerate all of the values + // + status = STATUS_SUCCESS; + for (index = 0; index < numValues && NT_SUCCESS(status); index++) { + + UNICODE_STRING valueName; + + length = sizeof(KEY_VALUE_FULL_INFORMATION); + + do { + + if (keyValueFullInfo) { + + DsmpFreePool(keyValueFullInfo); + } + + keyValueFullInfo = DsmpAllocatePool(NonPagedPoolNxCacheAligned, length, DSM_TAG_REG_KEY_RELATED); + + if (!keyValueFullInfo) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpRegCopyTree (SrcKey %p): Failed to allocate resources for value full info.\n", + SourceKey)); + + status = STATUS_INSUFFICIENT_RESOURCES; + goto __Exit_DsmpRegCopyTree; + } + + // + // Get the information of the index'th value + // + status = ZwEnumerateValueKey(SourceKey, + index, + KeyValueFullInformation, + keyValueFullInfo, + length, + &length); + + } while (status == STATUS_BUFFER_TOO_SMALL || status == STATUS_BUFFER_OVERFLOW); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpRegCopyTree (SrcKey %p): Failed to enumerate key's value information. Status %x.\n", + SourceKey, + status)); + + goto __Exit_DsmpRegCopyTree; + } + + // + // Capture the data type, data value, and value name. + // + titleIndex = keyValueFullInfo->TitleIndex; + valueDataType = keyValueFullInfo->Type; + + RtlZeroMemory(valueDataBuf, lengthOfValueData); + RtlCopyMemory(valueDataBuf, + (PUCHAR)keyValueFullInfo + keyValueFullInfo->DataOffset, + keyValueFullInfo->DataLength); + + RtlZeroMemory(valueBuf, lengthOfValueName); + RtlStringCbCopyNW(valueBuf, lengthOfValueName, keyValueFullInfo->Name, keyValueFullInfo->NameLength); + RtlInitUnicodeString(&valueName, valueBuf); + + // + // Copy the value over to the new key + // + status = ZwSetValueKey(DestKey, + &valueName, + titleIndex, + valueDataType, + valueDataBuf, + keyValueFullInfo->DataLength); + } + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpRegCopyTree (SrcKey %p): Failed to allocate set new key's value information. Status %x.\n", + SourceKey, + status)); + + goto __Exit_DsmpRegCopyTree; + } + + // + // Allocate buffer for subkey name + // + subKey = DsmpAllocatePool(NonPagedPoolNxCacheAligned, + lengthOfKeyName, + DSM_TAG_REG_KEY_RELATED); + + if(!subKey) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpRegCopyTree (SrcKey %p): Failed to allocate resources for sub key name.\n", + SourceKey)); + + status = STATUS_INSUFFICIENT_RESOURCES; + goto __Exit_DsmpRegCopyTree; + } + + // + // Now Enumerate all of the subkeys + // + length = sizeof(KEY_BASIC_INFORMATION); + for(index = 0; index < numSubKeys && NT_SUCCESS(status); index++) { + + UNICODE_STRING subKeyName; + + do { + if (keyBasicInfo) { + + DsmpFreePool(keyBasicInfo); + } + + keyBasicInfo = DsmpAllocatePool(NonPagedPoolNxCacheAligned, + length, + DSM_TAG_REG_KEY_RELATED); + + if (!keyBasicInfo) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpRegCopyTree (SrcKey %p): Failed to allocate resources for key basic info.\n", + SourceKey)); + + status = STATUS_INSUFFICIENT_RESOURCES; + goto __Exit_DsmpRegCopyTree; + } + + // + // Enumerate the index'th subkey + // + status = ZwEnumerateKey(SourceKey, + index, + KeyBasicInformation, + keyBasicInfo, + length, + &length); + + } while (status == STATUS_BUFFER_TOO_SMALL || status == STATUS_BUFFER_OVERFLOW); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpRegCopyTree (SrcKey %p): Failed to enumerate sub key's info. Status %x.\n", + SourceKey, + status)); + + goto __Exit_DsmpRegCopyTree; + } + + RtlZeroMemory(subKey, lengthOfKeyName); + RtlStringCbCopyNW(subKey, lengthOfKeyName, keyBasicInfo->Name, keyBasicInfo->NameLength); + RtlInitUnicodeString(&subKeyName, subKey); + + // + // Open a handle to the the subkey on the old device. + // + InitializeObjectAttributes(&objectAttributes, + &subKeyName, + (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE), + SourceKey, + (PSECURITY_DESCRIPTOR) NULL); + + if (srcSubKey) { + ZwClose(srcSubKey); + srcSubKey = NULL; + } + + status = ZwOpenKey(&srcSubKey, + KEY_ALL_ACCESS, + &objectAttributes); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpRegCopyTree (SrcKey %p): Failed to open reg key %ws. Status %x.\n", + SourceKey, + subKey, + status)); + + goto __Exit_DsmpRegCopyTree; + } + + InitializeObjectAttributes(&objectAttributes, + &subKeyName, + (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE), + DestKey, + (PSECURITY_DESCRIPTOR) NULL); + + if (destSubKey) { + ZwClose(destSubKey); + destSubKey = NULL; + } + + // + // Create the subkey on the new device. + // + status = ZwCreateKey(&destSubKey, + KEY_ALL_ACCESS, + &objectAttributes, + 0, + NULL, + REG_OPTION_NON_VOLATILE, + NULL); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpRegCopyTree (SrcKey %p): Failed to create reg key %ws. Status %x.\n", + SourceKey, + subKey, + status)); + + goto __Exit_DsmpRegCopyTree; + } + + // + // That's it. We've got everything we need (ie. handles to the two new + // subtrees' roots. Call recursively. + // + status = DsmpRegCopyTree(srcSubKey, destSubKey); + } + +__Exit_DsmpRegCopyTree: + + if (keyFullInfo) { + DsmpFreePool(keyFullInfo); + } + + if (valueBuf) { + DsmpFreePool(valueBuf); + } + + if (valueDataBuf) { + DsmpFreePool(valueDataBuf); + } + + if (keyValueFullInfo) { + DsmpFreePool(keyValueFullInfo); + } + + if (subKey) { + DsmpFreePool(subKey); + } + + if (keyBasicInfo) { + DsmpFreePool(keyBasicInfo); + } + + if (srcSubKey) { + ZwClose(srcSubKey); + } + + if (destSubKey) { + ZwClose(destSubKey); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpRegCopyTree (SrcKey %p): Exiting function with status %x.\n", + SourceKey, + status)); + + return status; +} + + +NTSTATUS +DsmpRegDeleteTree( + _In_ IN HANDLE KeyRoot + ) +/*++ +Routine Description: + + This routine is a recursive worker that enumerates the subkeys + of a given key, applies itself to each one, then deletes itself. + +Arguments: + + KeyRoot - Supplies a handle to the root of subtree to be deleted. + +Return Value: + + STATUS_SUCCESS - upon successful deletion of subtree. + Appropriate NT error code upon failure. + +--*/ +{ + NTSTATUS status; + PKEY_FULL_INFORMATION keyFullInfo = NULL; + ULONG length = sizeof(KEY_FULL_INFORMATION); + ULONG numSubKeys; + ULONG lengthOfKeyName; + LPWSTR subKey = NULL; + PKEY_BASIC_INFORMATION keyBasicInfo = NULL; + ULONG index = 0; + HANDLE srcSubKey = NULL; + OBJECT_ATTRIBUTES objectAttributes; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpRegDeleteTree (SrcKey %p): Entering function.\n", + KeyRoot)); + + if (!KeyRoot) { + + status = STATUS_INVALID_PARAMETER; + goto __Exit_DsmpRegDeleteTree; + } + + // + // Query the source key for information about number of subkeys and max + // length needed for subkey name. + // + do { + if (keyFullInfo) { + + DsmpFreePool(keyFullInfo); + } + + keyFullInfo = DsmpAllocatePool(NonPagedPoolNxCacheAligned, length, DSM_TAG_REG_KEY_RELATED); + + if (!keyFullInfo) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpRegDeleteTree (SrcKey %p): Failed to allocate resources for key full info.\n", + KeyRoot)); + + status = STATUS_INSUFFICIENT_RESOURCES; + goto __Exit_DsmpRegDeleteTree; + } + + status = ZwQueryKey(KeyRoot, + KeyFullInformation, + keyFullInfo, + length, + &length); + + } while (status == STATUS_BUFFER_TOO_SMALL || status == STATUS_BUFFER_OVERFLOW); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpRegDeleteTree (SrcKey %p): Failed to query key. Status %x.\n", + KeyRoot, + status)); + + goto __Exit_DsmpRegDeleteTree; + } + + numSubKeys = keyFullInfo->SubKeys; + lengthOfKeyName = keyFullInfo->MaxNameLen + sizeof(WCHAR); + + if (numSubKeys) { + + // + // Allocate buffer for subkey name + // + subKey = DsmpAllocatePool(NonPagedPoolNxCacheAligned, + lengthOfKeyName, + DSM_TAG_REG_KEY_RELATED); + + if(!subKey) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpRegDeleteTree (SrcKey %p): Failed to allocate resources for sub key.\n", + KeyRoot)); + + status = STATUS_INSUFFICIENT_RESOURCES; + goto __Exit_DsmpRegDeleteTree; + } + + // + // Now Enumerate all of the subkeys + // + index = numSubKeys - 1; + length = sizeof(KEY_BASIC_INFORMATION); + do { + + UNICODE_STRING subKeyName; + + do { + if (keyBasicInfo) { + + DsmpFreePool(keyBasicInfo); + } + + keyBasicInfo = DsmpAllocatePool(NonPagedPoolNxCacheAligned, + length, + DSM_TAG_REG_KEY_RELATED); + + if (!keyBasicInfo) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpRegDeleteTree (SrcKey %p): Failed to allocate resources for key basic info.\n", + KeyRoot)); + + status = STATUS_INSUFFICIENT_RESOURCES; + goto __Exit_DsmpRegDeleteTree; + } + + // + // Enumerate the index'th subkey + // + status = ZwEnumerateKey(KeyRoot, + index, + KeyBasicInformation, + keyBasicInfo, + length, + &length); + + } while (status == STATUS_BUFFER_TOO_SMALL || status == STATUS_BUFFER_OVERFLOW); + + if (NT_SUCCESS(status)) { + + RtlZeroMemory(subKey, lengthOfKeyName); + RtlStringCbCopyNW(subKey, lengthOfKeyName, keyBasicInfo->Name, keyBasicInfo->NameLength); + RtlInitUnicodeString(&subKeyName, subKey); + + // + // Open a handle to the the current root's subkey. + // + InitializeObjectAttributes(&objectAttributes, + &subKeyName, + (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE), + KeyRoot, + (PSECURITY_DESCRIPTOR) NULL); + + status = ZwOpenKey(&srcSubKey, + KEY_ALL_ACCESS, + &objectAttributes); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpRegDeleteTree (SrcKey %p): Failed to open key %ws. Status %x.\n", + KeyRoot, + subKey, + status)); + + goto __Exit_DsmpRegDeleteTree; + } + + // + // Delete this key's subtree (recursively). + // + status = DsmpRegDeleteTree(srcSubKey); + + ZwClose(srcSubKey); + srcSubKey = NULL; + } + + index--; + + } while (status != STATUS_NO_MORE_ENTRIES && (LONG)index >= 0); + + if (status == STATUS_NO_MORE_ENTRIES) { + + status = STATUS_SUCCESS; + } + } + + ZwDeleteKey(KeyRoot); + +__Exit_DsmpRegDeleteTree: + + if (srcSubKey) { + ZwClose(srcSubKey); + } + + if (keyFullInfo) { + DsmpFreePool(keyFullInfo); + } + + if (subKey) { + DsmpFreePool(subKey); + } + + if (keyBasicInfo) { + DsmpFreePool(keyBasicInfo); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpRegDeleteTree (SrcKey %p): Exiting function with status %x.\n", + KeyRoot, + status)); + + return status; +} + + +#if defined (_WIN64) +VOID +DsmpPassThroughPathTranslate32To64( + _In_ IN PMPIO_PASS_THROUGH_PATH32 MpioPassThroughPath32, + _Inout_ IN OUT PMPIO_PASS_THROUGH_PATH MpioPassThroughPath64 + ) +/*++ + +Routine Description: + + On WIN64, the SCSI_PASS_THROUGH field of the MPIO_PASS_THROUGH_PATH structure + sent down by a 32-bit application must be marshaled into a 64-bit version + of the structure. This function performs that marshaling. + +Arguments: + + MpioPassThroughPath32 - Supplies a pointer to a 32-bit MPIO_PASS_THROUGH_PATH + struct. + + MpioPassThroughPath64 - Supplies a pointer to a 64-bit MPIO_PASS_THROUGH_PATH + structure, into which we'll copy the marshaled + 32-bit data. + +Return Value: + + None. + +--*/ +{ + // + // Copy the first set of fields out of the 32-bit structure. These + // fields all line up between the 32 & 64 bit versions. + // + // Note that we do NOT adjust the length in the SrbControl. This is to + // allow the calling routine to compare the length of the actual + // control area against the offsets embedded within. If we adjusted the + // length then requests with the sense area backed against the control + // area would be rejected because the 64-bit control area is 4 bytes + // longer. + // + RtlCopyMemory(MpioPassThroughPath64, + MpioPassThroughPath32, + FIELD_OFFSET(SCSI_PASS_THROUGH, DataBufferOffset)); + + // + // Copy over the CDB. + // + RtlCopyMemory(MpioPassThroughPath64->PassThrough.Cdb, + MpioPassThroughPath32->PassThrough.Cdb, + 16 * sizeof(UCHAR) + ); + + // + // Copy over the rest of the fields of the structure. + // + MpioPassThroughPath64->Version = MpioPassThroughPath32->Version; + MpioPassThroughPath64->Length = MpioPassThroughPath32->Length; + MpioPassThroughPath64->Flags = MpioPassThroughPath32->Flags; + MpioPassThroughPath64->PortNumber = MpioPassThroughPath32->PortNumber; + MpioPassThroughPath64->MpioPathId = MpioPassThroughPath32->MpioPathId; + + // + // Copy the fields that follow the ULONG_PTR. + // + MpioPassThroughPath64->PassThrough.DataBufferOffset = (ULONG_PTR)MpioPassThroughPath32->PassThrough.DataBufferOffset; + MpioPassThroughPath64->PassThrough.SenseInfoOffset = MpioPassThroughPath32->PassThrough.SenseInfoOffset; + + return; +} + + +VOID +DsmpPassThroughPathTranslate64To32( + _In_ IN PMPIO_PASS_THROUGH_PATH MpioPassThroughPath64, + _Inout_ IN OUT PMPIO_PASS_THROUGH_PATH32 MpioPassThroughPath32 + ) +/*++ + +Routine Description: + + On WIN64, the SCSI_PASS_THROUGH field of MPIO_PASS_THROUGH_PATH structure + sent down by a 32-bit application must be marshaled into a 64-bit version + of the structure. This function marshals a 64-bit version of the structure + back into a 32-bit version. + +Arguments: + + MpioPassThroughPath64 - Supplies a pointer to a 64-bit MPIO_PASS_THROUGH_PATH + struct. + + MpioPassThroughPath32 - Supplies the address of a pointer to a 32-bit + MPIO_PASS_THROUGH_PATH structure, into which we'll + copy the marshaled 64-bit data. + +Return Value: + + None. + +--*/ +{ + // + // Copy back the fields through the data offsets. + // + RtlCopyMemory(MpioPassThroughPath32, + MpioPassThroughPath64, + FIELD_OFFSET(SCSI_PASS_THROUGH, DataBufferOffset)); + + + // + // Copy over the CDB. + // + RtlCopyMemory(MpioPassThroughPath32->PassThrough.Cdb, + MpioPassThroughPath64->PassThrough.Cdb, + 16 * sizeof(UCHAR) + ); + + // + // Copy over the rest of the fields of the structure. + // + MpioPassThroughPath32->Version = MpioPassThroughPath64->Version; + MpioPassThroughPath32->Length = MpioPassThroughPath64->Length; + MpioPassThroughPath32->Flags = MpioPassThroughPath64->Flags; + MpioPassThroughPath32->PortNumber = MpioPassThroughPath64->PortNumber; + MpioPassThroughPath32->MpioPathId = MpioPassThroughPath64->MpioPathId; + + return; +} +#endif + + +NTSTATUS +DsmpGetMaxPRRetryTime( + _In_ IN PDSM_CONTEXT Context, + _Out_ OUT PULONG RetryTime + ) +/*++ + +Routine Description: + + This routine is used to get the max time period for which a PR request failing + with a retry-able unit attention should be retried before failing back to MSCS. + The value is determined by querying the value found at + "msdsm\Parameters\DsmMaximumStateTransitionTime" + +Arguments: + + Context - The DSM Context value. + RetryTime - The output parameter that will receive the value to be used. + +Return Value: + + Status of the RtlQueryRegistryValues call. + +--*/ +{ + RTL_QUERY_REGISTRY_TABLE queryTable[2]; + WCHAR registryKeyName[56] = {0}; + NTSTATUS status; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpGetMaxPRRetryTime (DsmCtxt %p): Entering function.\n", + Context)); + + NT_ASSERT(RetryTime); + *RetryTime = DSM_MAX_PR_UNIT_ATTENTION_RETRY_TIME; + + RtlZeroMemory(queryTable, sizeof(queryTable)); + + // + // Build the key value name that we want as the base of the query. + // + RtlStringCbPrintfW(registryKeyName, + sizeof(registryKeyName), + DSM_PARAMETER_PATH_W); + + // + // The query table has two entries. One for the state transition time and + // the second which is the 'NULL' terminator. + // + queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED; + queryTable[0].Name = DSM_MAX_STATE_TRANSITION_TIME_VALUE_NAME; + queryTable[0].EntryContext = RetryTime; + + status = RtlQueryRegistryValues(RTL_REGISTRY_SERVICES, + registryKeyName, + queryTable, + registryKeyName, + NULL); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpGetMaxPRRetryTime (DsmCtxt %p): Exiting function with status %x.\n", + Context, + status)); + + return status; +} + + +NTSTATUS +DsmpQueryCacheInformationFromRegistry( + _In_ IN PDSM_CONTEXT DsmContext, + _Out_ OUT PBOOLEAN UseCacheForLeastBlocks, + _Out_ OUT PULONGLONG CacheSizeForLeastBlocks + ) +/*++ + +Routine Description: + + This routine is used to get the information about whether sequential IO + should use the same path when employing Least Blocks policy. + It also queries the size of cache set by the administrator. + The value is determined by querying the value found at + "msdsm\Parameters\DsmUseCacheForLeastBlocks" and + "msdsm\Parameters\DsmCacheSizeForLeastBlocks" + +Arguments: + + Context - The DSM Context value. + UseCacheForLeastBlocks - Returns the flag that indicates whether or not to + use same path for sequential IO when LB policy + is Least Blocks. + CacheSizeForLeastBlocks - Returns the size of the cache (in bytes) set by + the Admin to indicate the amount of sequential + data that should be use the same path when LB + policy is Least Blocks. + +Return Value: + + Status of the RtlQueryRegistryValues call. + +--*/ +{ + RTL_QUERY_REGISTRY_TABLE queryTable[2] = {0}; + WCHAR registryKeyName[56] = {0}; + HANDLE parametersKey = NULL; + UNICODE_STRING keyValueName; + NTSTATUS status; + struct _cacheSizeForLeastBlocks { + KEY_VALUE_PARTIAL_INFORMATION KeyValueInfo; + ULONGLONG Data; + } cacheSizeForLeastBlocks; + ULONG length = 0; + BOOLEAN useCacheForLeastBlocksDefault = FALSE; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpQueryCacheInformationFromRegistry (DsmCtxt %p): Entering function.\n", + DsmContext)); + + NT_ASSERT(UseCacheForLeastBlocks); + NT_ASSERT(CacheSizeForLeastBlocks); + + RtlZeroMemory(queryTable, sizeof(queryTable)); + + // + // Build the key value name that we want as the base of the query. + // + RtlStringCbPrintfW(registryKeyName, + sizeof(registryKeyName), + DSM_PARAMETER_PATH_W); + + // + // The query table has two entries. One for whether to use cache, and + // and the second which is the 'NULL' terminator. + // + queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED; + queryTable[0].Name = DSM_USE_CACHE_FOR_LEAST_BLOCKS; + queryTable[0].EntryContext = UseCacheForLeastBlocks; + queryTable[0].DefaultType = REG_BINARY; + queryTable[0].DefaultLength = sizeof(BOOLEAN); + queryTable[0].DefaultData = &useCacheForLeastBlocksDefault; + + status = RtlQueryRegistryValues(RTL_REGISTRY_SERVICES, + registryKeyName, + queryTable, + registryKeyName, + NULL); + + if (NT_SUCCESS(status)) { + + status = DsmpOpenDsmServicesParametersKey(KEY_QUERY_VALUE, ¶metersKey); + + if (NT_SUCCESS(status)) { + + RtlInitUnicodeString(&keyValueName, DSM_CACHE_SIZE_FOR_LEAST_BLOCKS); + + status = ZwQueryValueKey(parametersKey, + &keyValueName, + KeyValuePartialInformation, + &cacheSizeForLeastBlocks, + sizeof(cacheSizeForLeastBlocks), + &length); + + if (NT_SUCCESS(status)) { + + NT_ASSERT(cacheSizeForLeastBlocks.KeyValueInfo.DataLength == sizeof(ULONGLONG)); + *CacheSizeForLeastBlocks = *((ULONGLONG UNALIGNED *)&(cacheSizeForLeastBlocks.KeyValueInfo.Data)); + } + } + + if (parametersKey) { + ZwClose(parametersKey); + } + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_PNP, + "DsmpQueryCacheInformationFromRegistry (DsmCtxt %p): Exiting function with status %x.\n", + DsmContext, + status)); + + return status; +} + +BOOLEAN +DsmpConvertSharedSpinLockToExclusive( + _Inout_ _Requires_lock_held_(*_Curr_) PEX_SPIN_LOCK SpinLock + ) +/*++ + +Routine Description: + + This routine is a wrapper around ExTryConvertSharedSpinLockExclusive() that + guarantees the given EX_SPIN_LOCK will be acquired in Exclusive mode once + this function returns. + + It's possible the lock may be released and re-acquired within this function + so the caller should be very careful about the use of this function. + + N.B. The caller MUST have acquired the given lock in Shared mode before + calling this function. + +Arguments: + + SpinLock - The EX_SPIN_LOCK to convert from Shared to Exclusive mode. + +Return Value: + + Status of the ExTryConvertSharedSpinLockExclusive() call. This function + will always return with the lock acquired in Exclusive mode. The FALSE is + returned, then the lock had to be released and re-acquired. + +--*/ +{ + BOOLEAN converted = FALSE; + + converted = (BOOLEAN)ExTryConvertSharedSpinLockExclusive(SpinLock); + + // + // If the conversion attempt failed, then we should release the lock from + // Shared mode and try to pick it back up in Exclusive mode to guarantee + // this function will always return with the lock in Exclusive mode. + // + if (converted == FALSE) { + ExReleaseSpinLockSharedFromDpcLevel(SpinLock); + ExAcquireSpinLockExclusiveAtDpcLevel(SpinLock); + } + + return converted; +} + + diff --git a/storage/msdsm/src/wmi.c b/storage/msdsm/src/wmi.c new file mode 100644 index 000000000..8dcf309a2 --- /dev/null +++ b/storage/msdsm/src/wmi.c @@ -0,0 +1,3821 @@ + +/*++ + +Copyright (C) 2004-2010 Microsoft Corporation + +Module Name: + + wmi.c + +Abstract: + + This driver is the Microsoft Device Specific Module (DSM). + It exports behaviours that mpio.sys will use to determine how to + multipath SPC-3 compliant devices. + + This file contains WMI related functions. + +Environment: + + kernel mode only + +Notes: + +--*/ + + + +#include "precomp.h" +#include "msdsmwmi.h" +#include "msdsmdsm.h" + +#ifdef DEBUG_USE_WPP +#include "wmi.tmh" +#endif + +#pragma warning (disable:4305) + +extern BOOLEAN DoAssert; + +#define USE_BINARY_MOF_RESOURCE + +#define DSM_INVALID_LOAD_BALANCE_POLICY STATUS_INVALID_PARAMETER +#define DSM_UNSUPPORTED_VERSION STATUS_NOT_SUPPORTED + +// +// Max length for each of the DeviceId strings (supported device list) +// NOTE: This must be kept in sync with msdsmdsm.mof +// +#define MSDSM_MAX_DEVICE_ID_LENGTH 31 +#define MSDSM_MAX_DEVICE_ID_SIZE (MSDSM_MAX_DEVICE_ID_LENGTH * sizeof(WCHAR)) + +// +// List of supported DSM-centric guids +// +GUID MSDSM_SUPPORTED_DEVICES_LISTGUID = MSDSM_SUPPORTED_DEVICES_LISTGuid; +GUID MSDSM_TARGETS_DEFAULT_LOAD_BALANCE_POLICYGUID = MSDSM_TARGETS_DEFAULT_LOAD_BALANCE_POLICYGuid; +GUID MSDSM_DEFAULT_LOAD_BALANCE_POLICYGUID = MSDSM_DEFAULT_LOAD_BALANCE_POLICYGuid; + +// +// Symbolic names for the DSM-centric guid indexes +// +#define MSDSM_SUPPORTED_DEVICES_LISTGUID_Index 0 +#define MSDSM_TARGETS_DEFAULT_LOAD_BALANCE_POLICYGUID_Index 1 +#define MSDSM_DEFAULT_LOAD_BALANCE_POLICYGUID_Index 2 + +WMIGUIDREGINFO MSDsmGuidList[] = { + { + &MSDSM_SUPPORTED_DEVICES_LISTGUID, + 1, + 0 + }, + + { + &MSDSM_TARGETS_DEFAULT_LOAD_BALANCE_POLICYGUID, + 1, + 0 + }, + + { + &MSDSM_DEFAULT_LOAD_BALANCE_POLICYGUID, + 1, + 0 + } +}; + +#define MSDsmGuidCount (sizeof(MSDsmGuidList) / sizeof(WMIGUIDREGINFO)) + +// +// List of supported Device-centric guids +// +GUID DSM_LBOperationsGUID = DSM_LB_OperationsGuid; +GUID DSM_QueryLBPolicyGUID = DSM_QueryLBPolicyGuid; +GUID DSM_QuerySupportedLBPoliciesGUID = DSM_QuerySupportedLBPoliciesGuid; +GUID DSM_QueryDsmUniqueIdGUID = DSM_QueryUniqueIdGuid; +GUID DSM_QueryLBPolicyV2GUID = DSM_QueryLBPolicy_V2Guid; +GUID DSM_QuerySupportedLBPoliciesV2GUID = DSM_QuerySupportedLBPolicies_V2Guid; +GUID MSDSM_DEVICE_PERFGUID = MSDSM_DEVICE_PERFGuid; +GUID MSDSM_WMI_METHODSGUID = MSDSM_WMI_METHODSGuid; + +// +// Symbolic names for the Device-centric guid indexes +// +#define DSM_LBOperationsGUID_Index 0 +#define DSM_QueryLBPolicyGUID_Index 1 +#define DSM_QuerySupportedLBPoliciesGUID_Index 2 +#define DSM_QueryDsmUniqueIdGUID_Index 3 +#define DSM_QueryLBPolicyV2GUID_Index 4 +#define DSM_QuerySupportedLBPoliciesV2GUID_Index 5 +#define MSDSM_DEVICE_PERFGuidIndex 6 +#define MSDSM_WMI_METHODSGuidIndex 7 + +WMIGUIDREGINFO DsmGuidList[] = { + { + &DSM_LBOperationsGUID, + 1, + 0 + }, + + { + &DSM_QueryLBPolicyGUID, + 1, + 0 + }, + + { + &DSM_QuerySupportedLBPoliciesGUID, + 1, + 0 + }, + + { + &DSM_QueryDsmUniqueIdGUID, + 1, + 0 + }, + + { + &DSM_QueryLBPolicyV2GUID, + 1, + 0 + }, + + { + &DSM_QuerySupportedLBPoliciesV2GUID, + 1, + 0 + }, + + { + &MSDSM_DEVICE_PERFGUID, + 1, + 0 + }, + + { + &MSDSM_WMI_METHODSGUID, + 1, + 0 + } +}; + +#define DsmGuidCount (sizeof(DsmGuidList) / sizeof(WMIGUIDREGINFO)) + +VOID +DsmpDsmWmiInitialize( + _In_ IN PDSM_WMILIB_CONTEXT WmiGlobalInfo, + _In_ IN PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Description: + + This routine intializes the DSM-specific WmiGlobalInfo structure that is passed + back to MPIO during DriverEntry. + +Arguments: + + WmiGlobalInfo - WMI information structure to initialize. + RegistryPath - Registry path to the service key for this driver. + +Return Value: + + None + +--*/ +{ + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_INIT, + "DsmpDsmWmiInitialize (RegPath %ws): Entering function.\n", + RegistryPath->Buffer)); + + RtlZeroMemory(WmiGlobalInfo, sizeof(DSM_WMILIB_CONTEXT)); + + // + // Build the mof resource name. This tells wmi via the busdriver, + // where to find the mof data. This is found in the .rc. + // + RtlInitUnicodeString(&WmiGlobalInfo->MofResourceName, L"DsmMofResourceName"); + + // + // This will jam in the entry points and guids for supported WMI + // operations. SetDataBlock, SetDataItem, ExecuteMethod and FunctionControl are + // currently not needed, so leave them set to zero. + // + WmiGlobalInfo->GuidCount = MSDsmGuidCount; + WmiGlobalInfo->GuidList = MSDsmGuidList; + + WmiGlobalInfo->QueryWmiDataBlockEx = DsmGlobalQueryData; + WmiGlobalInfo->SetWmiDataBlockEx = DsmGlobalSetData; + + // + // Allocate a buffer for the reg. path. + // + WmiGlobalInfo->RegistryPath.Buffer = DsmpAllocatePool(NonPagedPoolNx, + RegistryPath->MaximumLength, + DSM_TAG_REG_PATH); + if (WmiGlobalInfo->RegistryPath.Buffer) { + + // + // Set maximum length of the new string and copy it. + // + WmiGlobalInfo->RegistryPath.MaximumLength = RegistryPath->MaximumLength; + + RtlCopyUnicodeString(&WmiGlobalInfo->RegistryPath, RegistryPath); + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "DsmpDsmWmiInitialize (RegPath %ws): Failed to allocate memory for Registry path in WmiGlobalInfo.\n", + RegistryPath->Buffer)); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_INIT, + "DsmpDsmWmiInitialize (RegPath %ws): Exiting function.\n", + RegistryPath->Buffer)); + + return; +} + + +NTSTATUS +DsmGlobalQueryData( + _In_ IN PVOID DsmContext, + _In_ IN PDSM_IDS DsmIds, + _In_ IN PIRP Irp, + _In_ IN ULONG GuidIndex, + _In_ IN ULONG InstanceIndex, + _In_ IN ULONG InstanceCount, + _Inout_ IN OUT PULONG InstanceLengthArray, + _In_ IN ULONG BufferAvail, + _Out_writes_to_(BufferAvail, *DataLength) OUT PUCHAR Buffer, + _Out_ OUT PULONG DataLength, + ... + ) +/*++ + +Routine Description: + + This is the WMI query entry point for DSM-specific GUIDs. The index into the + GUID array is found and assuming the buffer is large enough, the data will be + copied over. + +Arguments: + + DsmContext - Global DSM Context + DsmIds - Dsm Ids + Irp - The WMI Irp + GuidIndex - Index into the WMIGUIDINFO array + InstanceIndex - Index of the data instance + InstanceCount - Number of instances + InstanceLengthArray - Array of ULONGs that indicate per-instance data lengths. + BufferAvail - Size of the buffer in which data is returned. + Buffer - Buffer in which the data is returned. + DataLength - Storage for the actual data length written. + +Return Value: + + STATUS_BUFFER_TOO_SMALL - If output buffer is not big enough to + to return all the available data. + STATUS_WMI_GUID_NOT_FOUND - If GuidIndex doesn't correspond to an actual entry + in the reginfo array. + STATUS_SUCCESS - On success. + +--*/ +{ + NTSTATUS status = STATUS_WMI_GUID_NOT_FOUND; + UNREFERENCED_PARAMETER(DsmContext); + UNREFERENCED_PARAMETER(InstanceLengthArray); + UNREFERENCED_PARAMETER(InstanceCount); + UNREFERENCED_PARAMETER(InstanceIndex); + UNREFERENCED_PARAMETER(Irp); + UNREFERENCED_PARAMETER(DsmIds); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmGlobalQueryData (DsmContext %p): Entering function - GuidIndex %u.\n", + DsmContext, + GuidIndex)); + + // + // Check the GuidIndex - the index into the DsmGuildList array - to see + // whether this is a supported GUID or not. + // + switch(GuidIndex) { + + case MSDSM_SUPPORTED_DEVICES_LISTGUID_Index: { + + *DataLength = BufferAvail; + + status = DsmpQuerySupportedDevicesList(DsmContext, + BufferAvail, + DataLength, + Buffer); + + break; + } + + case MSDSM_TARGETS_DEFAULT_LOAD_BALANCE_POLICYGUID_Index: { + + *DataLength = BufferAvail; + + status = DsmpQueryTargetsDefaultPolicy(DsmContext, + BufferAvail, + DataLength, + Buffer); + + break; + } + + case MSDSM_DEFAULT_LOAD_BALANCE_POLICYGUID_Index: { + + *DataLength = BufferAvail; + + status = DsmpQueryDsmDefaultPolicy(DsmContext, + BufferAvail, + DataLength, + Buffer); + + break; + } + + default: { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmGlobalQueryData (DsmContext %p): Unknown GuidIndex %d.\n", + DsmContext, + GuidIndex)); + + *DataLength = 0; + + break; + } + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmGlobalQueryData (DsmContext %p): Exiting function with status 0x%x.\n", + DsmContext, + status)); + + return status; +} + + +NTSTATUS +DsmGlobalSetData( + _In_ IN PVOID DsmContext, + _In_ IN PDSM_IDS DsmIds, + _In_ IN PIRP Irp, + _In_ IN ULONG GuidIndex, + _In_ IN ULONG InstanceIndex, + _In_ IN ULONG BufferAvail, + _In_reads_bytes_(BufferAvail) IN PUCHAR Buffer, + ... + ) +/*++ + +Routine Description: + + This is the WMI set entry point for DSM-specific GUIDs. The index into the + GUID array is found and the contents of the buffer are set to the passed in + instance index. + +Arguments: + + DsmContext - Global DSM Context + DsmIds - Dsm Ids + Irp - The WMI Irp + GuidIndex - Index into the WMIGUIDINFO array + InstanceIndex - Index of the data instance + BufferAvail - Size of the buffer in which data is returned. + Buffer - Buffer in which the data is returned. + +Return Value: + + STATUS_BUFFER_TOO_SMALL - If output buffer is not big enough to + to return all the available data. + STATUS_WMI_GUID_NOT_FOUND - If GuidIndex doesn't correspond to an actual entry + in the reginfo array. + STATUS_SUCCESS - On success. + +--*/ +{ + NTSTATUS status = STATUS_WMI_GUID_NOT_FOUND; + ULONG dataLength; + PDSM_CONTEXT dsmContext = (PDSM_CONTEXT)DsmContext; + + UNREFERENCED_PARAMETER(DsmIds); + UNREFERENCED_PARAMETER(Irp); + UNREFERENCED_PARAMETER(InstanceIndex); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmGlobalSetData (DsmContext %p): Entering function - GuidIndex %u.\n", + DsmContext, + GuidIndex)); + + switch (GuidIndex) { + + case MSDSM_TARGETS_DEFAULT_LOAD_BALANCE_POLICYGUID_Index: { + + PMSDSM_TARGETS_DEFAULT_LOAD_BALANCE_POLICY targetsPolicyInfo = (PMSDSM_TARGETS_DEFAULT_LOAD_BALANCE_POLICY)Buffer; + PMSDSM_TARGET_DEFAULT_POLICY_INFO targetPolicyInfo; + PWSTR vidpidIndex; + DSM_LOAD_BALANCE_TYPE loadBalancePolicy; + ULONGLONG preferredPath; + DWORD index; + NTSTATUS errorStatus = STATUS_SUCCESS; + + // + // Determine the correct buffer size. + // + dataLength = AlignOn8Bytes(FIELD_OFFSET(MSDSM_TARGETS_DEFAULT_LOAD_BALANCE_POLICY, TargetDefaultPolicyInfo)); + + if (BufferAvail < dataLength) { + + status = STATUS_BUFFER_TOO_SMALL; + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmGlobalSetData (DsmContext %p): GuidIndex %u. Incorrect buffer size. Status %x\n", + DsmContext, + GuidIndex, + status)); + break; + } + + dataLength += targetsPolicyInfo->NumberDevices * sizeof(MSDSM_TARGET_DEFAULT_POLICY_INFO); + + if (BufferAvail < dataLength) { + + status = STATUS_BUFFER_TOO_SMALL; + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmGlobalSetData (DsmContext %p): GuidIndex %u. Incorrect buffer size for %u targets. Status %x\n", + DsmContext, + GuidIndex, + targetsPolicyInfo->NumberDevices, + status)); + break; + } + + targetPolicyInfo = targetsPolicyInfo->TargetDefaultPolicyInfo; + + for (index = 0; index < targetsPolicyInfo->NumberDevices; index++, targetPolicyInfo++) { + + size_t stringLength = 0; + + // + // First ensure that these values make sense. The VID/PID should be + // a string of 8+16 chars and the LB policy must be one that MSDSM + // supports. + // + // The WMI string is like a unicode string with the first USHORT + // containing the size. + // + vidpidIndex = targetPolicyInfo->HardwareId; + vidpidIndex++; + + if (!NT_SUCCESS(RtlStringCchLengthW(vidpidIndex, DSM_VENDPROD_ID_LEN + 1, &stringLength)) || (stringLength != DSM_VENDPROD_ID_LEN)) { + + errorStatus = STATUS_INVALID_PARAMETER; + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmGlobalSetData (DsmContext %p): GuidIndex %u. Ignoring incorrect VID/PID %ws. Status %x\n", + DsmContext, + GuidIndex, + vidpidIndex, + errorStatus)); + continue; + } + + if (targetPolicyInfo->LoadBalancePolicy >= DSM_LB_VENDOR_SPECIFIC) { + + errorStatus = STATUS_INVALID_PARAMETER; + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmGlobalSetData (DsmContext %p): GuidIndex %u. Ignoring policy %u for %ws. Status %x\n", + DsmContext, + GuidIndex, + targetPolicyInfo->LoadBalancePolicy, + vidpidIndex, + errorStatus)); + continue; + } + + loadBalancePolicy = targetPolicyInfo->LoadBalancePolicy; + preferredPath = (ULONGLONG)((ULONG_PTR)targetPolicyInfo->PreferredPath); + + // + // Now update/create the key in the registry with the LB policy info. + // If the LB policy is specified as 0, delete the key. + // + status = DsmpSetVidPidLBPolicyInRegistry(vidpidIndex, loadBalancePolicy, preferredPath); + + // + // If above was successful, find the group that corresponds to this + // targetId and update its LB policy as well as the states of the paths + // + if (NT_SUCCESS(status)) { + + DsmpSetLBForVidPidPolicyAdjustment(dsmContext, vidpidIndex, loadBalancePolicy, preferredPath); + } else { + errorStatus = status; + } + } + + // + // If any error occurred, return the last error. + // + if (!NT_SUCCESS(errorStatus)) { + status = errorStatus; + } + + break; + } + + case MSDSM_DEFAULT_LOAD_BALANCE_POLICYGUID_Index: { + + PMSDSM_DEFAULT_LOAD_BALANCE_POLICY dsmPolicyInfo = (PMSDSM_DEFAULT_LOAD_BALANCE_POLICY)Buffer; + DSM_LOAD_BALANCE_TYPE loadBalancePolicy; + ULONGLONG preferredPath; + + // + // Determine the correct buffer size. + // + dataLength = sizeof(MSDSM_DEFAULT_LOAD_BALANCE_POLICY); + + if (BufferAvail < dataLength) { + + status = STATUS_BUFFER_TOO_SMALL; + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmGlobalSetData (DsmContext %p): GuidIndex %u. Incorrect buffer size. Status %x\n", + DsmContext, + GuidIndex, + status)); + break; + } + + loadBalancePolicy = dsmPolicyInfo->LoadBalancePolicy; + preferredPath = (ULONGLONG)((ULONG_PTR)dsmPolicyInfo->PreferredPath); + + // + // First ensure that the values make sense. + // + if (loadBalancePolicy >= DSM_LB_VENDOR_SPECIFIC) { + + status = STATUS_INVALID_PARAMETER; + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmGlobalSetData (DsmContext %p): GuidIndex %u. Invalid policy %u specified. Status %x\n", + DsmContext, + GuidIndex, + loadBalancePolicy, + status)); + + } else { + + // + // Update/create the values in the registry with the LB policy info. + // If the LB policy is specified as 0, delete the values. + // + status = DsmpSetDsmLBPolicyInRegistry(loadBalancePolicy, preferredPath); + + // + // If above is successful, find the groups that haven't had their LB policy + // explicitly set or haven't had their policy set in accordance with target + // hardware id. For each of these, adjust the states of the paths as well. + // + if (NT_SUCCESS(status)) { + + DsmpSetLBForDsmPolicyAdjustment(dsmContext, loadBalancePolicy, preferredPath); + } + } + + break; + } + + default: { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmGlobalSetData (DsmContext %p): Unknown GuidIndex %d.\n", + DsmContext, + GuidIndex)); + + break; + } + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmGlobalSetData (DsmContext %p): Exiting function with status 0x%x.\n", + DsmContext, + status)); + + return status; +} + + +VOID +DsmpWmiInitialize( + _In_ IN PDSM_WMILIB_CONTEXT WmiInfo, + _In_ IN PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Description: + + This routine intializes the Device-specific WmiInfo structure that is passed + back to MPIO during DriverEntry. + +Arguments: + + WmiInfo - WMI information structure to initialize. + RegistryPath - Registry path to the service key for this driver. + +Return Value: + + None + +--*/ +{ + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_INIT, + "DsmpWmiInitialize (RegPath %ws): Entering function.\n", + RegistryPath->Buffer)); + + RtlZeroMemory(WmiInfo, sizeof(DSM_WMILIB_CONTEXT)); + + // + // Build the mof resource name. This tells wmi via the busdriver, + // where to find the mof data. This is found in the .rc. + // + RtlInitUnicodeString(&WmiInfo->MofResourceName, L"MofResourceName"); + + // + // This will jam in the entry points and guids for supported WMI + // operations. SetDataBlock, SetDataItem, and FunctionControl are + // currently not needed, so leave them set to zero. + // + WmiInfo->GuidCount = DsmGuidCount; + WmiInfo->GuidList = DsmGuidList; + + WmiInfo->QueryWmiDataBlockEx = DsmQueryData; + WmiInfo->ExecuteWmiMethodEx = DsmExecuteMethod; + + // + // Allocate a buffer for the reg. path. + // + WmiInfo->RegistryPath.Buffer = DsmpAllocatePool(NonPagedPoolNx, + RegistryPath->MaximumLength, + DSM_TAG_REG_PATH); + if (WmiInfo->RegistryPath.Buffer) { + + // + // Set maximum length of the new string and copy it. + // + WmiInfo->RegistryPath.MaximumLength = RegistryPath->MaximumLength; + + RtlCopyUnicodeString(&WmiInfo->RegistryPath, RegistryPath); + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "DsmpWmiInitialize (RegPath %ws): Failed to allocate memory for Registry path in WMIInfo.\n", + RegistryPath->Buffer)); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_INIT, + "DsmpWmiInitialize (RegPath %ws): Exiting function.\n", + RegistryPath->Buffer)); + + return; +} + + +NTSTATUS +DsmQueryData( + _In_ IN PVOID DsmContext, + _In_ IN PDSM_IDS DsmIds, + _In_ IN PIRP Irp, + _In_ IN ULONG GuidIndex, + _In_ IN ULONG InstanceIndex, + _In_ IN ULONG InstanceCount, + _Inout_ IN OUT PULONG InstanceLengthArray, + _In_ IN ULONG BufferAvail, + _When_(GuidIndex == DSM_LBOperationsGUID_Index || GuidIndex == MSDSM_WMI_METHODSGuidIndex, _Pre_notnull_ _Const_) + _When_(!(GuidIndex == DSM_LBOperationsGUID_Index || GuidIndex == MSDSM_WMI_METHODSGuidIndex), _Out_writes_to_(BufferAvail, *DataLength)) + OUT PUCHAR Buffer, + _Out_ OUT PULONG DataLength, + ... + ) +/*++ + +Routine Description: + + This is the main WMI query entry point. The index into the GUID array is found + and assuming the buffer is large enough, the data will be copied over. + +Arguments: + + DsmContext - Global DSM Context + DsmIds - Dsm Ids + Irp - The WMI Irp + GuidIndex - Index into the WMIGUIDINFO array + InstanceIndex - Index of the data instance + InstanceCount - Number of instances + InstanceLengthArray - Array of ULONGs that indicate per-instance data lengths. + BufferAvail - Size of the buffer in which data is returned. + Buffer - Buffer in which the data is returned. + DataLength - Storage for the actual data length written. + +Return Value: + + STATUS_BUFFER_TOO_SMALL - If output buffer is not big enough to + to return all the available data. + STATUS_WMI_GUID_NOT_FOUND - If GuidIndex doesn't correspond to an actual entry + in the reginfo array. + STATUS_SUCCESS - On success. + +--*/ +{ + ULONG sizeNeeded; + NTSTATUS status = STATUS_WMI_GUID_NOT_FOUND; + + UNREFERENCED_PARAMETER(DsmContext); + UNREFERENCED_PARAMETER(InstanceCount); + UNREFERENCED_PARAMETER(InstanceIndex); + UNREFERENCED_PARAMETER(Irp); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmQueryData (DsmIds %p): Entering function - GuidIndex %u.\n", + DsmIds, + GuidIndex)); + + // + // Check the GuidIndex - the index into the DsmGuildList array - to see + // whether this is a supported GUID or not. + // + switch(GuidIndex) { + + case DSM_LBOperationsGUID_Index: { + + // + // Even though this class only has methods, we need to respond + // to any queries for it since WMI expects that there is an actual + // instance of the class on which to execute the method + // + + sizeNeeded = sizeof(ULONG); + + *DataLength = sizeNeeded; + + if (BufferAvail >= sizeNeeded) { + + *InstanceLengthArray = sizeNeeded; + status = STATUS_SUCCESS; + + } else { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_WMI, + "DsmQueryData (DsmIds %p): Buffer too small in query data. Needed %d, Given %d.\n", + DsmIds, + sizeNeeded, + BufferAvail)); + + status = STATUS_BUFFER_TOO_SMALL; + } + + break; + } + + case DSM_QueryLBPolicyGUID_Index: + case DSM_QueryLBPolicyV2GUID_Index: { + + *DataLength = BufferAvail; + + status = DsmpQueryLoadBalancePolicy(DsmContext, + DsmIds, + ((GuidIndex == DSM_QueryLBPolicyGUID_Index) ? DSM_WMI_VERSION_1 : DSM_WMI_VERSION_2), + BufferAvail, + DataLength, + Buffer); + break; + } + + case DSM_QuerySupportedLBPoliciesGUID_Index: + case DSM_QuerySupportedLBPoliciesV2GUID_Index: { + + *DataLength = BufferAvail; + + status = DsmpQuerySupportedLBPolicies(DsmContext, + DsmIds, + BufferAvail, + ((GuidIndex == DSM_QuerySupportedLBPoliciesGUID_Index) ? DSM_WMI_VERSION_1 : DSM_WMI_VERSION_2), + DataLength, + Buffer); + break; + } + + case DSM_QueryDsmUniqueIdGUID_Index: { + + PDSM_QueryUniqueId dsmQueryUniqueId; + + *DataLength = sizeof(DSM_QueryUniqueId); + + if (BufferAvail >= sizeof(DSM_QueryUniqueId)) { + + dsmQueryUniqueId = (PDSM_QueryUniqueId) Buffer; + dsmQueryUniqueId->DsmUniqueId = (ULONGLONG)((ULONG_PTR)DsmContext); + status = STATUS_SUCCESS; + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmQueryData (DsmIds %p): Buffersize %d too small for Query Unique Id.\n", + DsmIds, + BufferAvail)); + + status = STATUS_BUFFER_TOO_SMALL; + } + + break; + } + + case MSDSM_DEVICE_PERFGuidIndex: { + + *DataLength = BufferAvail; + + status = DsmpQueryDevicePerf(DsmContext, + DsmIds, + BufferAvail, + DataLength, + Buffer); + + break; + } + + case MSDSM_WMI_METHODSGuidIndex: { + + // + // Even though this class only has methods, we need to respond + // to any queries for it since WMI expects that there is an actual + // instance of the class on which to execute the method + // + + sizeNeeded = sizeof(ULONG); + + *DataLength = sizeNeeded; + + if (BufferAvail >= sizeNeeded) { + + *InstanceLengthArray = sizeNeeded; + status = STATUS_SUCCESS; + + } else { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_WMI, + "DsmQueryData (DsmIds %p): Buffer too small in query data. Needed %d, Given %d.\n", + DsmIds, + sizeNeeded, + BufferAvail)); + + status = STATUS_BUFFER_TOO_SMALL; + } + + break; + } + + default: { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmQueryData (DsmIds %p): Unknown GuidIndex %d in DsmQueryData.\n", + DsmIds, + GuidIndex)); + + *DataLength = 0; + + break; + } + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmQueryData (DsmIds %p): Exiting function with status 0x%x.\n", + DsmIds, + status)); + + return status; +} + + +NTSTATUS +DsmpQueryLoadBalancePolicy( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_IDS DsmIds, + _In_ IN ULONG DsmWmiVersion, + _In_ IN ULONG InBufferSize, + _In_ IN PULONG OutBufferSize, + _Out_writes_bytes_(*OutBufferSize) OUT PVOID Buffer + ) +/*+++ + +Routine Description: + + This routine returns the current Load Balance policy settings + for the given device. + +Arguements: + + DsmContext - Global DSM context + DsmIds - DSM Ids for the given device + DsmWmiVersion - version of the MPIO_DSM_Path class to use + InBufferSize - Size of the input buffer + OutBufferSize - Size of the output buffer + Buffer - Buffer in which the current Load Balance policy settings + is returned, if the buffer is big enough + +Return Value: + + STATUS_SUCCESS on success + Appropriate error code on error. + +--*/ +{ + PDSM_GROUP_ENTRY groupEntry; + PDSM_DEVICE_INFO devInfo; + PDSM_DEVICE_INFO rtpgDeviceInfo = NULL; + ULONG inx; + ULONG sizeNeeded; + NTSTATUS status = STATUS_SUCCESS; + KIRQL irql; + PDSM_Load_Balance_Policy_V2 supportedLBPolicies; + PMPIO_DSM_Path_V2 dsmPath; + PDSM_FAILOVER_GROUP foGroup; + ULONG SpecialHandlingFlag = 0; + + UNREFERENCED_PARAMETER(InBufferSize); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpQueryLoadBalancePolicy (DsmIds %p): Entering function.\n", + DsmIds)); + + // + // At least one device should be given + // + if (DsmIds->Count == 0) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpQueryLoadBalancePolicy (DsmIds %p): No DSM Ids given in DsmpQueryLoadBalancePolicy.\n", + DsmIds)); + + *OutBufferSize = 0; + status = STATUS_INVALID_PARAMETER; + + goto __Exit_DsmpQueryLoadBalancePolicy; + } + + // + // Compute the size needed for returning LoadBalance policy information + // + if (DsmWmiVersion == DSM_WMI_VERSION_1) { + + sizeNeeded = AlignOn8Bytes(FIELD_OFFSET(DSM_Load_Balance_Policy, DSM_Paths)); + sizeNeeded += (DsmIds->Count) * sizeof(MPIO_DSM_Path); + + } else { + + sizeNeeded = AlignOn8Bytes(FIELD_OFFSET(DSM_Load_Balance_Policy_V2, DSM_Paths)); + sizeNeeded += (DsmIds->Count * sizeof(MPIO_DSM_Path_V2)); + } + + if (*OutBufferSize < sizeNeeded) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpQueryLoadBalancePolicy (DsmIds %p): Output buffer too small for QueryLBPolicy.\n", + DsmIds)); + + *OutBufferSize = sizeNeeded; + status = STATUS_BUFFER_TOO_SMALL; + + goto __Exit_DsmpQueryLoadBalancePolicy; + } + + // + // Set the size of the data returned to user + // + *OutBufferSize = sizeNeeded; + + // + // Zero out the output buffer first + // + RtlZeroMemory(Buffer, sizeNeeded); + + devInfo = DsmIds->IdList[0]; + DSM_ASSERT(devInfo && devInfo->DeviceSig == DSM_DEVICE_SIG); + groupEntry = devInfo->Group; + + // + // Send down an RTPG to get the current state info if implicit transitions + // are supported, since the states may have changed from under us. + // Storages that support both implicit and explicit transitions that haven't + // allowed us to turn OFF their implicit transitions, may have also changed + // TPG states from under us. So do this for such storages also. + // + if (!DsmpIsSymmetricAccess(devInfo) && + devInfo->ALUASupport != DSM_DEVINFO_ALUA_EXPLICIT) { + + rtpgDeviceInfo = DsmpGetActivePathToBeUsed(groupEntry, FALSE, SpecialHandlingFlag); + + if (!rtpgDeviceInfo) { + + BOOLEAN sendTPG = FALSE; + + rtpgDeviceInfo = DsmpFindStandbyPathToActivateALUA(groupEntry, &sendTPG, SpecialHandlingFlag); + } + + if (rtpgDeviceInfo) { + + status = DsmpGetDeviceALUAState(DsmContext, rtpgDeviceInfo, NULL); + } + } + + irql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); + + // + // If an RTPG was sent down, update all the devInfo states. + // + if (NT_SUCCESS(status) && rtpgDeviceInfo) { + + DsmpAdjustDeviceStatesALUA(groupEntry, NULL, SpecialHandlingFlag); + } + + supportedLBPolicies = &(((PDSM_QueryLBPolicy_V2)Buffer)->LoadBalancePolicy); + supportedLBPolicies->Version = DSM_WMI_VERSION; + supportedLBPolicies->LoadBalancePolicy = groupEntry->LoadBalanceType; + supportedLBPolicies->DSMPathCount = DsmIds->Count; + dsmPath = supportedLBPolicies->DSM_Paths; + + // + // Indicate which path is active and which path(s) are standby paths + // + inx = 0; + while (inx < DsmIds->Count) { + + devInfo = (PDSM_DEVICE_INFO)DsmIds->IdList[inx]; + + dsmPath->PathWeight = devInfo->PathWeight; + dsmPath->Reserved = DSM_STATE_ACTIVE_OPTIMIZED_SUPPORTED; + + if (devInfo->ALUASupport == DSM_DEVINFO_ALUA_NOT_SUPPORTED) { + + if (DsmWmiVersion > DSM_WMI_VERSION_1) { + + dsmPath->TargetPortGroup_State = DSM_DEV_NOT_USED_STATE; + } + + dsmPath->Reserved |= DSM_STATE_STANDBY_SUPPORTED; + + } else { + + if (DsmWmiVersion > DSM_WMI_VERSION_1) { + + dsmPath->TargetPortGroup_State = devInfo->TargetPortGroup->AsymmetricAccessState; + dsmPath->TargetPortGroup_Preferred = devInfo->TargetPortGroup->Preferred; + dsmPath->TargetPortGroup_Identifier = devInfo->TargetPortGroup->Identifier; + + if (devInfo->TargetPort) { + + dsmPath->TargetPort_Identifier = devInfo->TargetPort->Identifier; + } + + if (groupEntry->Symmetric) { + + // + // For certain policies like FOO and RRWS, we need to be able to put + // path in standby. + // + dsmPath->Reserved |= DSM_STATE_STANDBY_SUPPORTED; + } + } + + dsmPath->Reserved |= devInfo->TargetPortGroup->ActiveUnoptimizedSupported ? DSM_STATE_ACTIVE_UNOPTIMIZED_SUPPORTED : 0; + dsmPath->Reserved |= devInfo->TargetPortGroup->StandBySupported ? DSM_STATE_STANDBY_SUPPORTED : 0; + dsmPath->Reserved |= devInfo->TargetPortGroup->UnavailableSupported ? DSM_STATE_UNAVAILABLE_SUPPORTED : 0; + } + + groupEntry = devInfo->Group; + + if (DsmWmiVersion > DSM_WMI_VERSION_1) { + + dsmPath->SymmetricLUA = groupEntry->Symmetric; + dsmPath->ALUASupport = devInfo->ALUASupport; + + } + + if (DsmpIsDeviceFailedState(devInfo->State) || !DsmpIsDeviceInitialized(devInfo)) { + + dsmPath->PrimaryPath = FALSE; + dsmPath->DsmPathId = 0; + + if (DsmWmiVersion > DSM_WMI_VERSION_1) { + + dsmPath->OptimizedPath = dsmPath->PreferredPath = FALSE; + dsmPath->FailedPath = TRUE; + } + + } else { + + foGroup = devInfo->FailGroup; + dsmPath->DsmPathId = (ULONGLONG)((ULONG_PTR)foGroup->PathId); + + if (DsmpIsDeviceStateActive(devInfo->State)) { + + dsmPath->PrimaryPath = TRUE; + } + + if (DsmWmiVersion > DSM_WMI_VERSION_1) { + + if (devInfo->State == DSM_DEV_ACTIVE_OPTIMIZED || + devInfo->State == DSM_DEV_STANDBY) { + + dsmPath->OptimizedPath = TRUE; + } + + if (((ULONGLONG)((ULONG_PTR)(foGroup->PathId))) == (devInfo->Group->PreferredPath)) { + + dsmPath->PreferredPath = TRUE; + } + } + } + +#if DBG + if (!dsmPath->PrimaryPath && + !dsmPath->FailedPath) { + NT_ASSERT(groupEntry->LoadBalanceType != DSM_LB_ROUND_ROBIN && + groupEntry->LoadBalanceType != DSM_LB_WEIGHTED_PATHS && + groupEntry->LoadBalanceType != DSM_LB_DYN_LEAST_QUEUE_DEPTH && + groupEntry->LoadBalanceType != DSM_LB_LEAST_BLOCKS); + } +#endif + + dsmPath = DsmWmiVersion == DSM_WMI_VERSION_1 ? + (PVOID)((PUCHAR)dsmPath + sizeof(MPIO_DSM_Path)) : + (PVOID)((PUCHAR)dsmPath + sizeof(MPIO_DSM_Path_V2)); + + inx++; + } + + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); + +__Exit_DsmpQueryLoadBalancePolicy: + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpQueryLoadBalancePolicy (DsmIds %p): Exiting with status %x.\n", + DsmIds, + status)); + + return status; +} + + +NTSTATUS +DsmpQuerySupportedLBPolicies( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_IDS DsmIds, + _In_ IN ULONG BufferAvail, + _In_ IN ULONG DsmWmiVersion, + _Out_ OUT PULONG OutBufferSize, + _Out_writes_to_(BufferAvail, *OutBufferSize) OUT PUCHAR Buffer + ) +/*+++ + +Routine Description: + + This routine returns the load balance policies supported by this DSM for the + given LUN (specified by the DsmIds). + +Arguements: + + DsmContext - Global DSM context + DsmIds - DSM Ids for the given device + BufferAvail - Size of buffer available. + DsmWmiVersion - Indicates which version of MPIO_DSMPath to use. + OutBufferSize - Size of the output buffer. + Buffer - Buffer in which the supported Load Balance policies are + returned, if the buffer is big enough. + +Return Value: + + STATUS_SUCCESS on success + Appropriate error code on error. + +--*/ +{ + PDSM_QuerySupportedLBPolicies_V2 supportedLBPolicies; + PDSM_Load_Balance_Policy_V2 dsmLBPolicy; + ULONG sizeNeeded; + ULONG policyCount; + ULONG inx; + NTSTATUS status = STATUS_SUCCESS; + BOOLEAN skipRR = FALSE; + PDSM_DEVICE_INFO devInfo = NULL; + PUCHAR endOfBuffer; + + UNREFERENCED_PARAMETER(DsmContext); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI,"DsmpQuerySupportedLBPolicies (DsmIds %p): Entering function.\n", + DsmIds)); + + // + // At least one device should be given + // + if (DsmIds->Count == 0) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpQuerySupportedLBPolicies (DsmIds %p): No DSM Ids given in DsmpQuerySupportedLBPolicies.\n", + DsmIds)); + + *OutBufferSize = 0; + status = STATUS_INVALID_PARAMETER; + + goto __Exit_DsmpQuerySupportedLBPolicies; + } + + devInfo = DsmIds->IdList[0]; + DSM_ASSERT(devInfo && devInfo->DeviceSig == DSM_DEVICE_SIG); + + policyCount = DSM_NUMBER_OF_LB_POLICIES; + + // + // Round Robin policy is not supported for arrays that are AAA. + // + if (!DsmpIsSymmetricAccess(devInfo)) { + + skipRR = TRUE; + policyCount--; + } + + if (DsmWmiVersion == DSM_WMI_VERSION_1) { + + sizeNeeded = AlignOn8Bytes(FIELD_OFFSET(DSM_QuerySupportedLBPolicies, Supported_LB_Policies)); + sizeNeeded += policyCount * AlignOn8Bytes(FIELD_OFFSET(DSM_Load_Balance_Policy, DSM_Paths)); + + } else { + + sizeNeeded = AlignOn8Bytes(FIELD_OFFSET(DSM_QuerySupportedLBPolicies_V2, Supported_LB_Policies)); + sizeNeeded += policyCount * AlignOn8Bytes(FIELD_OFFSET(DSM_Load_Balance_Policy_V2, DSM_Paths)); + } + + // + // Set the size of the data returned to user or needed but not provided. + // + *OutBufferSize = sizeNeeded; + + if (sizeNeeded > BufferAvail) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpQuerySupportedLBPolicies (Buffer %p): Output buffer too small. Size needed = %u.\n", + Buffer, + sizeNeeded)); + + status = STATUS_BUFFER_TOO_SMALL; + + goto __Exit_DsmpQuerySupportedLBPolicies; + } + + endOfBuffer = Buffer + sizeNeeded - 1; + + // + // Zero out the output buffer first + // + supportedLBPolicies = (PDSM_QuerySupportedLBPolicies_V2)Buffer; + RtlZeroMemory(Buffer, sizeNeeded); + + supportedLBPolicies->SupportedLBPoliciesCount = policyCount; + + if (DsmWmiVersion > DSM_WMI_VERSION_1) { + + dsmLBPolicy = &(supportedLBPolicies->Supported_LB_Policies[0]); + + } else { + + dsmLBPolicy = (PVOID)&(((PDSM_QuerySupportedLBPolicies)supportedLBPolicies)->Supported_LB_Policies[0]); + } + + // + // All Load Balance policies are supported in Windows Server 2003 + // and above. + // + for (inx = 0; inx < DSM_NUMBER_OF_LB_POLICIES; inx++) { + + // + // Skip reporting Round Robin for AAA arrays. + // + if (((inx + 1) == DSM_LB_ROUND_ROBIN) && skipRR) { + + continue; + } + + if (DsmWmiVersion > DSM_WMI_VERSION_1) { + + if ((PUCHAR)dsmLBPolicy + AlignOn8Bytes(FIELD_OFFSET(DSM_Load_Balance_Policy_V2, DSM_Paths)) - 1 > endOfBuffer) { + + status = STATUS_BUFFER_TOO_SMALL; + break; + } + } else { + + if ((PUCHAR)dsmLBPolicy + AlignOn8Bytes(FIELD_OFFSET(DSM_Load_Balance_Policy, DSM_Paths)) - 1 > endOfBuffer) { + + status = STATUS_BUFFER_TOO_SMALL; + break; + } + } + + dsmLBPolicy->Version = DSM_WMI_VERSION; + + // + // The value set for LoadBalancePolicy is based on + // the #define for LB policies in LBPolicy.h + // + dsmLBPolicy->LoadBalancePolicy = inx + 1; + + // + // Point to the next DSM_Load_Balance_Policy area + // + if (DsmWmiVersion > DSM_WMI_VERSION_1) { + + dsmLBPolicy = (PDSM_Load_Balance_Policy_V2)((PUCHAR)dsmLBPolicy + AlignOn8Bytes(FIELD_OFFSET(DSM_Load_Balance_Policy_V2, DSM_Paths))); + + } else { + + dsmLBPolicy = (PVOID)((PUCHAR)dsmLBPolicy + AlignOn8Bytes(FIELD_OFFSET(DSM_Load_Balance_Policy, DSM_Paths))); + } + } + +__Exit_DsmpQuerySupportedLBPolicies: + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpQuerySupportedLBPolicies (DsmIds %p): Exiting function with status %x.\n", + DsmIds, + status)); + + return status; +} + + +NTSTATUS +DsmExecuteMethod( + _In_ IN PVOID DsmContext, + _In_ IN PDSM_IDS DsmIds, + _In_ IN PIRP Irp, + _In_ IN ULONG GuidIndex, + _In_ IN ULONG InstanceIndex, + _In_ IN ULONG MethodId, + _In_ IN ULONG InBufferSize, + _In_ IN PULONG OutBufferSize, + _Inout_ IN OUT PUCHAR Buffer, + ... + ) +/*++ + +Routine Description: + + This routine handles the invocation of WMI methods defined in the DSM mof. + +Arguments: + + DsmContext - Global DSM context + DsmIds - DSM Ids + Irp - The WMI Irp + GuidIndex - Index into the WMIGUIDINFO array + InstanceIndex - Index value indicating for which instance data should be returned. + MethodId - Specifies which method to invoke. + InBufferSize - Buffer size, in bytes, of input parameter data. + OutBufferSize - Buffer size, in bytes, of output data. + Buffer - Buffer to which the data is read/written. + +Return Value: + + Status of the method, or STATUS_WMI_ITEMID_NOT_FOUND + +--*/ +{ + NTSTATUS status = STATUS_WMI_GUID_NOT_FOUND; + UNREFERENCED_PARAMETER(DsmContext); + UNREFERENCED_PARAMETER(InstanceIndex); + UNREFERENCED_PARAMETER(Irp); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmExecuteMethod (DsmIds %p): Entering function.\n", + DsmIds)); + + // + // This should be the index for ExecMethod Index + // + if (GuidIndex == DSM_LBOperationsGUID_Index) { + + switch (MethodId) { + + case DsmSetLoadBalancePolicy: + case DsmSetLoadBalancePolicyALUA: { + + status = DsmpSetLoadBalancePolicy(DsmContext, + DsmIds, + (MethodId == DsmSetLoadBalancePolicy) ? DSM_WMI_VERSION_1 : DSM_WMI_VERSION_2, + InBufferSize, + OutBufferSize, + Buffer); + break; + } + + default: { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmExecuteMethod (DsmIds %p): Unknown MethodId %d in DsmExecuteMethod.\n", + DsmIds, + MethodId)); + + status = STATUS_WMI_ITEMID_NOT_FOUND; + + break; + } + } + } else if (GuidIndex == MSDSM_WMI_METHODSGuidIndex) { + + if (MethodId == MSDsmClearCounters) { + + status = DsmpClearPerfCounters(DsmContext, DsmIds); + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmExecuteMethod (DsmIds %p): Unknown MethodId %d for GuidIndex %d in DsmExecuteMethod.\n", + DsmIds, + MethodId, + GuidIndex)); + } + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmExecuteMethod (DsmIds %p): Unknown GuidIndex %d in DsmExecuteMethod.\n", + DsmIds, + GuidIndex)); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmExecuteMethod (DsmIds %p): Exiting function with status 0x%x.\n", + DsmIds, + status)); + + return status; +} + +NTSTATUS +DsmpClearLoadBalancePolicy( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_IDS DsmIds + ) +/*++ + +Routine Description: + + This routine is called to clear the LUN-specific load balance policy for the given device. + + First, the routine will try to clear the "explicitly set" registry key for the device. If + this fails, the whole routine is aborted. + + If the registry key is successfully cleared, the following happens: + 1. Check to see if there is a target-wide load balance policy set for this device's VID/PID. + If yes, we set the device's load balance policy accordingly and return. + 2. Check to see if there is an MSDSM-wide load balance policy set. + If yes, we set the device's load balance policy accordingly and return. + 3. If steps 1 and 2 fall through, we set the device's load balance policy to RR, or RRWS if + ALUA is enabled. + +Arguements: + + DsmContext - Global DSM context + DsmIds - DSM Ids for the given device + +Return Value: + + Appropriate status indicating the error if the input is malformed or + if the function was unable to clear the load balance policy. + STATUS_SUCCESS on success + +--*/ + +{ + NTSTATUS status = STATUS_SUCCESS; + PDSM_DEVICE_INFO deviceInfo = NULL; + PDSM_GROUP_ENTRY group = NULL; + HANDLE lbSettingsKey = NULL; + HANDLE deviceKey = NULL; + UNICODE_STRING subKeyName; + OBJECT_ATTRIBUTES objectAttributes; + DSM_LOAD_BALANCE_TYPE loadBalanceType; + ULONGLONG preferredPath = (ULONGLONG)((ULONG_PTR)MAXULONG); + ULONG devInfoIndex; + ULONG SpecialHandlingFlag = 0; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpClearLoadBalancePolicy (DsmIds %p): Entering function.\n", + DsmIds)); + + // + // There should be at least one device + // + if (DsmIds->Count == 0) { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_WMI, + "DsmpClearLoadBalancePolicy (DsmIds %p): No DSM Ids given.\n", + DsmIds)); + + status = STATUS_INVALID_PARAMETER; + goto __Exit_DsmpClearLoadBalancePolicy; + } + + deviceInfo = (PDSM_DEVICE_INFO)DsmIds->IdList[0]; + group = deviceInfo->Group; + + // + // First open LoadBalanceSettings key under the Services key + // + status = DsmpOpenLoadBalanceSettingsKey(KEY_ALL_ACCESS, &lbSettingsKey); + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpClearLoadBalancePolicy (DevName %ws): Failed to open LB Settings key. Status %x.\n", + group->RegistryKeyName, + status)); + + goto __Exit_DsmpClearLoadBalancePolicy; + } + + // + // Now open the key under which the LB settings for the given device is stored + // and clear the DsmLoadBalancePolicyExplicitlySet key. + // + RtlInitUnicodeString(&subKeyName, group->RegistryKeyName); + + InitializeObjectAttributes(&objectAttributes, + &subKeyName, + (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE), + lbSettingsKey, + (PSECURITY_DESCRIPTOR) NULL); + + status = ZwOpenKey(&deviceKey, KEY_ALL_ACCESS, &objectAttributes); + + if (NT_SUCCESS(status)) { + + UCHAR explicitlySet = FALSE; + + status = RtlWriteRegistryValue(RTL_REGISTRY_HANDLE, + deviceKey, + DSM_POLICY_EXPLICITLY_SET, + REG_BINARY, + &explicitlySet, + sizeof(UCHAR)); + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpClearLoadBalancePolicy (DevName %ws): Failed to clear DsmLoadBalancePolicyExplicitlySet key.\n", + group->RegistryKeyName)); + + goto __Exit_DsmpClearLoadBalancePolicy; + } + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpClearLoadBalancePolicy (DevName %ws): Failed to open device subkey.\n", + group->RegistryKeyName)); + + goto __Exit_DsmpClearLoadBalancePolicy; + } + + + + // + // Set the defaults. These will be used if no target-wide or MSDSM-wide + // load balance policies are set. + // + group->LBPolicySelection = DSM_DEFAULT_LB_POLICY_ALUA_CAPABILITY; + loadBalanceType = DSM_LB_ROUND_ROBIN; + preferredPath = 0; + + // + // Check to see if target-wide (VID/PID) LB policy is set for this device. + // + status = DsmpQueryTargetLBPolicyFromRegistry(deviceInfo, + &loadBalanceType, + &preferredPath); + if (NT_SUCCESS(status)) { + + group->LBPolicySelection = DSM_DEFAULT_LB_POLICY_VID_PID; + + } else if (status == STATUS_OBJECT_NAME_NOT_FOUND) { + + // + // Since the policy hasn't been set for this VID/PID, check if + // overall MSDSM-wide policy has been set. + // + status = DsmpQueryDsmLBPolicyFromRegistry(&loadBalanceType, + &preferredPath); + if (NT_SUCCESS(status)) { + + group->LBPolicySelection = DSM_DEFAULT_LB_POLICY_DSM_WIDE; + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpClearLoadBalancePolicy (DevInfo %p): Failed to query Dsm overall LB policy from registry. Status %x.\n", + deviceInfo, + status)); + + NT_ASSERT(status == STATUS_OBJECT_NAME_NOT_FOUND); + status = STATUS_SUCCESS; + } + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_PNP, + "DsmpClearLoadBalancePolicy (DevInfo %p): Failed to query VID/PID LB policy from registry. Status %x.\n", + deviceInfo, + status)); + + NT_ASSERT(status == STATUS_OBJECT_NAME_NOT_FOUND); + status = STATUS_SUCCESS; + } + + + // + // If the storage is ALUA enabled and we specified Round Robin, change + // it to Round Robin with Subset instead. + // + if (!DsmpIsSymmetricAccess(deviceInfo) && loadBalanceType == DSM_LB_ROUND_ROBIN) { + + loadBalanceType = DSM_LB_ROUND_ROBIN_WITH_SUBSET; + } + + // + // Finally set the load balance policy and the preferred path. + // + group->LoadBalanceType = loadBalanceType; + group->PreferredPath = preferredPath; + + // + // Update the path states in accordance with the new policy. + // + for (devInfoIndex = 0; devInfoIndex < DSM_MAX_PATHS; devInfoIndex++) { + + DsmpSetNewDefaultLBPolicy(DsmContext, + group->DeviceList[devInfoIndex], + group->LoadBalanceType, + SpecialHandlingFlag); + } + +__Exit_DsmpClearLoadBalancePolicy: + + if (deviceKey) { + ZwClose(deviceKey); + } + + if (lbSettingsKey) { + ZwClose(lbSettingsKey); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpClearLoadBalancePolicy (DsmIds %p): Exiting function with status 0x%x.\n", + DsmIds, + status)); + + return status; +} + + +NTSTATUS +DsmpSetLoadBalancePolicy( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_IDS DsmIds, + _In_ IN ULONG DsmWmiVersion, + _In_ IN ULONG InBufferSize, + _In_ IN PULONG OutBufferSize, + _In_ IN PVOID Buffer + ) +/*++ + +Routine Description: + + This routine is called to set the load balance policy for the given device. + + If zero is passed in as the load balance policy, the LUN-specific load balance + policy will attempt to be cleared. See DsmpClearLoadBalancePolicy for more details. + +Arguements: + + DsmContext - Global DSM context + DsmIds - DSM Ids for the given device + DsmWmiVersion - version of the MPIO_DSM_Path class to use + InBufferSize - Size of the input buffer + OutBufferSize - Size of the output buffer + Buffer - Buffer for input\output data + +Return Value: + + STATUS_BUFFER_TOO_SMALL - If the input buffer is too small + Appropriate status indicating the error if the input is malformed. + STATUS_SUCCESS on success + +--*/ + +{ + PDsmSetLoadBalancePolicyALUA_IN setLoadBalancePolicyIN = (PDsmSetLoadBalancePolicyALUA_IN) Buffer; + PDsmSetLoadBalancePolicyALUA_OUT setLoadBalancePolicyOUT = (PDsmSetLoadBalancePolicyALUA_OUT) Buffer; + PVOID supportedLBPolicies; + PMPIO_DSM_Path_V2 dsmPath; + ULONG inx = 0; + ULONG jnx; + NTSTATUS status = STATUS_SUCCESS; + BOOLEAN lengthOkay = TRUE; + PDSM_DEVICE_INFO devInfo = NULL; + PDSM_DEVICE_INFO tempDevInfo = NULL; + PDSM_GROUP_ENTRY groupEntry; + PDSM_LOAD_BALANCE_POLICY_SETTINGS savedLBSettings = NULL; + KIRQL irql; + BOOLEAN optimized = TRUE; + BOOLEAN preferred = FALSE; + ULONG activePaths = 0; + ULONG activeTPGs = 0; + ULONG numberDevInfoChanged = 0; + ULONG numberPreferredPaths = 0; + DSM_LOAD_BALANCE_TYPE loadBalancePolicy; + BOOLEAN sendSTPG = FALSE; + ULONGLONG preferredPath = (ULONGLONG)((ULONG_PTR)MAXULONG); + ULONG SpecialHandlingFlag = 0; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpSetLoadBalancePolicy (DsmIds %p): Entering function.\n", + DsmIds)); + + // + // There should be at least one device + // + if (DsmIds->Count == 0) { + + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_WMI, + "DsmpSetLoadBalancePolicy (DsmIds %p): No DSM Ids given.\n", + DsmIds)); + + status = STATUS_INVALID_PARAMETER; + goto __Exit_DsmpSetLoadBalancePolicy; + } + + groupEntry = ((PDSM_DEVICE_INFO)DsmIds->IdList[0])->Group; + + if (DsmWmiVersion == DSM_WMI_VERSION_1) { + + if (*OutBufferSize < sizeof(DsmSetLoadBalancePolicy_OUT)) { + + *OutBufferSize = sizeof(DsmSetLoadBalancePolicy_OUT); + lengthOkay = FALSE; + } + } else { + + if (*OutBufferSize < sizeof(DsmSetLoadBalancePolicyALUA_OUT)) { + + *OutBufferSize = sizeof(DsmSetLoadBalancePolicyALUA_OUT); + lengthOkay = FALSE; + } + } + + if (!lengthOkay) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpSetLoadBalancePolicy (DsmIds %p): Buffer too small for SetLBPolicy.\n", + DsmIds)); + + status = STATUS_BUFFER_TOO_SMALL; + goto __Exit_DsmpSetLoadBalancePolicy; + } + + *OutBufferSize = (DsmWmiVersion == DSM_WMI_VERSION_1) ? sizeof(DsmSetLoadBalancePolicy_OUT) : sizeof(DsmSetLoadBalancePolicyALUA_OUT); + + // + // If the user specified zero as the load balance policy, we need to clear the + // LUN-specific load balance policy. + // + if (setLoadBalancePolicyIN->LoadBalancePolicy.LoadBalancePolicy == 0) { + status = DsmpClearLoadBalancePolicy(DsmContext, DsmIds); + goto __Exit_DsmpSetLoadBalancePolicy; + } + + status = DsmpValidateSetLBPolicyInput(DsmContext, + DsmIds, + DsmWmiVersion, + Buffer, + InBufferSize); + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpSetLoadBalancePolicy (DsmIds %p): Failed to validate input. Status %x.\n", + DsmIds, + status)); + + goto __Exit_DsmpSetLoadBalancePolicy; + } + + // + // At this point the Reserved field in each MPIO_DSM_Path should + // contain the respective Device Info + // + supportedLBPolicies = &(setLoadBalancePolicyIN->LoadBalancePolicy); + loadBalancePolicy = ((PDSM_Load_Balance_Policy_V2)supportedLBPolicies)->LoadBalancePolicy; + + irql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); + + // + // Cache each DeviceInfo's current state. + // This will be used to rollback in case of errors. + // + DsmpSaveDeviceState(supportedLBPolicies, DsmWmiVersion); + + while (inx < ((PDSM_Load_Balance_Policy_V2)supportedLBPolicies)->DSMPathCount) { + + if (DsmWmiVersion == DSM_WMI_VERSION_1) { + + dsmPath = (PVOID)&(((PDSM_Load_Balance_Policy)supportedLBPolicies)->DSM_Paths[inx]); + + optimized = TRUE; + preferred = FALSE; + + } else { + + dsmPath = &(((PDSM_Load_Balance_Policy_V2)supportedLBPolicies)->DSM_Paths[inx]); + + optimized = dsmPath->OptimizedPath ? TRUE : FALSE; + preferred = dsmPath->PreferredPath ? TRUE : FALSE; + + if (preferred && loadBalancePolicy == DSM_LB_FAILOVER) { + + preferredPath = dsmPath->DsmPathId; + + if (preferredPath != 0) { + + numberPreferredPaths++; + } + + if (numberPreferredPaths > 1) { + + DsmpRestorePreviousDeviceState(supportedLBPolicies, DsmWmiVersion); + status = STATUS_INVALID_PARAMETER; + break; + } + } + } + + // + // Reserved field in MPIO_DSM_Path is set to DeviceInfo in + // DsmpValidateSetLBPolicyInput routine. + // + devInfo = (PDSM_DEVICE_INFO)dsmPath->Reserved; + + if (!devInfo) { + + inx++; + continue; + + } else { + + if (!tempDevInfo) { + + tempDevInfo = devInfo; + + if (loadBalancePolicy == DSM_LB_ROUND_ROBIN || + loadBalancePolicy == DSM_LB_ROUND_ROBIN_WITH_SUBSET) { + + InterlockedExchangePointer(&(groupEntry->PathToBeUsed), NULL); + } + } + } + + if (!DsmpIsDeviceFailedState(devInfo->State)) { + + if (devInfo->ALUAState == DSM_DEV_ACTIVE_OPTIMIZED) { + + activeTPGs++; + } + + if (dsmPath->PrimaryPath) { + + // + // Optimized flag decides between AO and AU + // + if (optimized) { + + // + // For implicit-only ALUA, state cannot be explicitly changed to A/O + // + if (!DsmpIsSymmetricAccess(devInfo) && devInfo->ALUASupport == DSM_DEVINFO_ALUA_IMPLICIT) { + + // + // While we can mask off acutal A/O to be A/U, there is no + // way to explicitly make non-A/O state A/O + // + if (devInfo->ALUAState != DSM_DEV_ACTIVE_OPTIMIZED) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpSetLoadBalancePolicy (DsmIds %p): Can't make non-AO path A/O for Implicit-only transitions.\n", + DsmIds)); + + DsmpRestorePreviousDeviceState(supportedLBPolicies, DsmWmiVersion); + status = STATUS_INVALID_PARAMETER; + break; + } + } + + numberDevInfoChanged++; + + devInfo->State = DSM_DEV_ACTIVE_OPTIMIZED; + activePaths++; + + // + // Check to see if the actual making of this path state A/O + // will require an STPG to be sent down. + // + if (devInfo->TargetPortGroup && + devInfo->ALUAState != DSM_DEV_ACTIVE_OPTIMIZED) { + + sendSTPG = TRUE; + } + + if (loadBalancePolicy == DSM_LB_FAILOVER) { + + // + // Only ONE path can be specified as AO for FailOverOnly policy. + // + if (activePaths > 1) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpSetLoadBalancePolicy (DsmIds %p): More than one AO node given for FO Only.\n", + DsmIds)); + + DsmpRestorePreviousDeviceState(supportedLBPolicies, DsmWmiVersion); + status = STATUS_INVALID_PARAMETER; + break; + } + } + + if (loadBalancePolicy == DSM_LB_ROUND_ROBIN || + loadBalancePolicy == DSM_LB_ROUND_ROBIN_WITH_SUBSET) { + + if (!groupEntry->PathToBeUsed) { + InterlockedExchangePointer(&(groupEntry->PathToBeUsed), (PVOID)devInfo->FailGroup); + } + } + } else { + + // + // This is an ActiveUnoptimized path + // + devInfo->State = DSM_DEV_ACTIVE_UNOPTIMIZED; + + // + // For LB policy RR, WP, LB and LQD, all paths must be in A/O + // state. However, this is not possible for ALUA storages. + // For these storages, A/U is allowable only if that is the + // access state that the TPG is in. + // + if (loadBalancePolicy == DSM_LB_ROUND_ROBIN || + loadBalancePolicy == DSM_LB_WEIGHTED_PATHS || + loadBalancePolicy == DSM_LB_DYN_LEAST_QUEUE_DEPTH || + loadBalancePolicy == DSM_LB_LEAST_BLOCKS) { + + if (devInfo->TargetPortGroup && devInfo->ALUAState != DSM_DEV_ACTIVE_UNOPTIMIZED) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpSetLoadBalancePolicy (DsmIds %p): Path (%u) specified in A/U state when TPG is in %u state (for LB %u).\n", + DsmIds, + inx, + devInfo->ALUAState, + loadBalancePolicy)); + + DsmpRestorePreviousDeviceState(supportedLBPolicies, DsmWmiVersion); + status = STATUS_INVALID_PARAMETER; + break; + } + } + } + } else { + + if (optimized) { + + // + // This is a standby path + // + devInfo->State = DSM_DEV_STANDBY; + + } else { + + // + // This is unavailable path + // + devInfo->State = DSM_DEV_UNAVAILABLE; + } + + // + // For RR, LQD, LB and WP, all paths must be in A/O state for non-ALUA + // storage. For ALUA storage, the only time path states can be in + // S/B or U/A is if the TPG itself is in that state. + // + if (loadBalancePolicy == DSM_LB_ROUND_ROBIN || + loadBalancePolicy == DSM_LB_WEIGHTED_PATHS || + loadBalancePolicy == DSM_LB_DYN_LEAST_QUEUE_DEPTH || + loadBalancePolicy == DSM_LB_LEAST_BLOCKS) { + + if ((!devInfo->TargetPortGroup) || + (devInfo->TargetPortGroup && devInfo->State != devInfo->ALUAState)) { + + // + // No paths can be in SB or UA unless its TPG is in that state. + // + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpSetLoadBalancePolicy (DsmIds %p): Path (%u) specified in non-active state for LB %u.\n", + DsmIds, + inx, + loadBalancePolicy)); + + DsmpRestorePreviousDeviceState(supportedLBPolicies, DsmWmiVersion); + status = STATUS_INVALID_PARAMETER; + break; + } + } else if (loadBalancePolicy == DSM_LB_ROUND_ROBIN_WITH_SUBSET) { + + // + // It is okay to set a path to be in S/B or U/A state in RRWS + // if either the storage is non-ALUA, or if the storage is + // ALUA but the TPG is in A/O (where it can be masked) or the + // TPG is in the state that the path is being set to. + // + if ((devInfo->TargetPortGroup) && + (devInfo->ALUAState != DSM_DEV_ACTIVE_OPTIMIZED && devInfo->State != devInfo->ALUAState)) { + + // + // No paths can be in SB or UA unless its TPG is in that state. + // + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpSetLoadBalancePolicy (DsmIds %p): Path (%u) (in TPG state %u) can't be specified in non-active state for LB %u.\n", + DsmIds, + inx, + devInfo->ALUAState, + loadBalancePolicy)); + + DsmpRestorePreviousDeviceState(supportedLBPolicies, DsmWmiVersion); + status = STATUS_INVALID_PARAMETER; + break; + } + } + } + } + + inx++; + } + + if (NT_SUCCESS(status)) { + + + // + // If we arrive here, that means DsmpValidateSetLBPolicyInput already returned success. + // The device info is found. + // + _Analysis_assume_(tempDevInfo != NULL); + + // + // There must be at least one AO path. Unless there are no A/O TPGs. + // eg. During a controller failover, it is possible that the TPG through + // the TPG through other controller is still in non-A/O state and the + // storage supports implicit transitions and is still in the midst of + // making the transition of the non-A/O TPG to A/O. During such windows + // the states for all paths will be non-A/O and there's nothing that can + // be done about it. This is not an error condition. + // + if (!activePaths) { + + if ((tempDevInfo->ALUASupport == DSM_DEVINFO_ALUA_NOT_SUPPORTED) || + (tempDevInfo->ALUASupport != DSM_DEVINFO_ALUA_NOT_SUPPORTED && activeTPGs)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpSetLoadBalancePolicy (DsmIds %p): No active node given for LB %u.\n", + DsmIds, + loadBalancePolicy)); + + // + // Roll back to DeviceState to the state it was before + // processing this SetLB policy request + // + DsmpRestorePreviousDeviceState(supportedLBPolicies, DsmWmiVersion); + + status = STATUS_INVALID_PARAMETER; + } + } + } + + if (NT_SUCCESS(status)) { + + // + // If we arrive here, that means DsmpValidateSetLBPolicyInput already returned success. + // The device info is found. + // + _Analysis_assume_(tempDevInfo != NULL); + + // + // If device supports explicit transitions, we need to send down an + // STPG to enforce A/O path selection if we need to make a path in a + // non-A/O TPG active/optimized. + // + if (tempDevInfo->ALUASupport >= DSM_DEVINFO_ALUA_EXPLICIT && sendSTPG) { + + PUCHAR targetPortGroupsInfo = NULL; + ULONG targetPortGroupsInfoLength = 0; + PSPC3_SET_TARGET_PORT_GROUP_DESCRIPTOR tpgDescriptor = NULL; + + // + // Build the target port groups info to set the new states. + // Send down an STPG for TPG descriptors for those devInfos' TPGs + // that need to be in AO state. If this causes side-effects in + // state transitions (these can't be considered implicit according + // to the spec), fake the devInfo states to what was selected. + // + targetPortGroupsInfoLength = SPC3_TARGET_PORT_GROUPS_HEADER_SIZE + + activePaths * sizeof(SPC3_SET_TARGET_PORT_GROUP_DESCRIPTOR); + + targetPortGroupsInfo = DsmpAllocatePool(NonPagedPoolNx, + targetPortGroupsInfoLength, + DSM_TAG_TARGET_PORT_GROUPS); + + if (targetPortGroupsInfo) { + + PDSM_DEVICE_INFO devInfoToUse = NULL; + + // + // Set the new asymmetric access states for the the devices' target port groups + // + tpgDescriptor = (PSPC3_SET_TARGET_PORT_GROUP_DESCRIPTOR)(targetPortGroupsInfo + SPC3_TARGET_PORT_GROUPS_HEADER_SIZE); + + for (inx = 0, jnx = 0; + inx < ((PDSM_Load_Balance_Policy_V2)supportedLBPolicies)->DSMPathCount; + inx++) { + + if (DsmWmiVersion == DSM_WMI_VERSION_1) { + + dsmPath = (PVOID)&(((PDSM_Load_Balance_Policy)supportedLBPolicies)->DSM_Paths[inx]); + + } else { + + dsmPath = &(((PDSM_Load_Balance_Policy_V2)supportedLBPolicies)->DSM_Paths[inx]); + } + + devInfo = (PDSM_DEVICE_INFO)dsmPath->Reserved; + + if (!devInfo) { + + continue; + } + + if (devInfo->State == DSM_DEV_ACTIVE_OPTIMIZED) { + + tpgDescriptor->AsymmetricAccessState = devInfo->State; + REVERSE_BYTES_SHORT(&tpgDescriptor->TPG_Identifier, &devInfo->TargetPortGroup->Identifier); + + tpgDescriptor = (PSPC3_SET_TARGET_PORT_GROUP_DESCRIPTOR)((PUCHAR)tpgDescriptor + sizeof(SPC3_SET_TARGET_PORT_GROUP_DESCRIPTOR)); + + jnx++; + } + + if (devInfo->TempPreviousStateForLB == DSM_DEV_ACTIVE_OPTIMIZED) { + + devInfoToUse = devInfo; + } + } + + NT_ASSERT(jnx == numberDevInfoChanged); + NT_ASSERT(devInfoToUse); + + if (devInfoToUse) { + + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); + + status = DsmpSetTargetPortGroups(devInfoToUse->TargetObject, + targetPortGroupsInfo, + targetPortGroupsInfoLength); + + if (NT_SUCCESS(status)) { + + DsmpFreePool(targetPortGroupsInfo); + targetPortGroupsInfo = NULL; + targetPortGroupsInfoLength = 0; + status = DsmpReportTargetPortGroups(devInfoToUse->TargetObject, + &targetPortGroupsInfo, + &targetPortGroupsInfoLength); + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpSetLoadBalancePolicy (DsmIds %p): STPG failed with status %x.\n", + DsmIds, + status)); + + DsmpRestorePreviousDeviceState(supportedLBPolicies, DsmWmiVersion); + } + + irql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); + } + + if (NT_SUCCESS(status)) { + + ULONG index; + PDSM_TARGET_PORT_GROUP_ENTRY targetPortGroup; + + status = DsmpParseTargetPortGroupsInformation(DsmContext, + groupEntry, + targetPortGroupsInfo, + targetPortGroupsInfoLength); + + NT_ASSERT(NT_SUCCESS(status)); + + for (index = 0; index < DSM_MAX_PATHS; index++) { + + targetPortGroup = groupEntry->TargetPortGroupList[index]; + + if (targetPortGroup) { + + DsmpUpdateTargetPortGroupDevicesStates(targetPortGroup, targetPortGroup->AsymmetricAccessState); + } + } + + // + // Update TPGs with new state + // + for (inx = 0; + inx < ((PDSM_Load_Balance_Policy_V2)supportedLBPolicies)->DSMPathCount; + inx++) { + + if (DsmWmiVersion == DSM_WMI_VERSION_1) { + + dsmPath = (PVOID)&(((PDSM_Load_Balance_Policy)supportedLBPolicies)->DSM_Paths[inx]); + + } else { + + dsmPath = &(((PDSM_Load_Balance_Policy_V2)supportedLBPolicies)->DSM_Paths[inx]); + } + + devInfo = (PDSM_DEVICE_INFO)dsmPath->Reserved; + + if (devInfo) { + + // + // An explicit state transition can cause TPGs that were not specified + // in the parameter list to also change (this is not considered to be + // an implicit transition. It is SPC3 behavior and we must take + // this into consideration and update the devInfo states. + // This is an unfortunate side-effect in that the Admin may not get + // the paths to be in the exact states that he has set. + // + if (devInfo->State == DSM_DEV_ACTIVE_OPTIMIZED) { + + if (devInfo->ALUAState == DSM_DEV_ACTIVE_UNOPTIMIZED || + devInfo->ALUAState == DSM_DEV_STANDBY || + devInfo->ALUAState == DSM_DEV_UNAVAILABLE) { + + // + // An A/O TPG's devInfos can be masked as A/U. + // However, the reverse the is not true (ie. we can't + // mark a non-A/O TPG's devInfo(s) to be in A/O state. + // + devInfo->State = devInfo->ALUAState; + } + } + + // + // The devInfo->State has already been set. Update its previous state. + // + devInfo->PreviousState = devInfo->TempPreviousStateForLB; + } + } + + NT_ASSERT(jnx == numberDevInfoChanged); + } + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpSetLoadBalancePolicy (DsmIds %p): Failed to allocate targetPortGroupsInfo.\n", + DsmIds)); + + status = STATUS_INSUFFICIENT_RESOURCES; + } + } + + if (NT_SUCCESS(status)) { + + groupEntry->LoadBalanceType = loadBalancePolicy; + + if (loadBalancePolicy == DSM_LB_FAILOVER) { + + groupEntry->PreferredPath = preferredPath; + } + + savedLBSettings = DsmpCopyLoadBalancePolicies(groupEntry, + DsmWmiVersion, + supportedLBPolicies); + + } else { + + // + // Roll back to DeviceState to the state it was before + // processing this SetLB policy request + // + DsmpRestorePreviousDeviceState(supportedLBPolicies, DsmWmiVersion); + } + } + + if (NT_SUCCESS(status)) { + + // + // LUN's LB policy has been explicitly set by Admin + // + groupEntry->LBPolicySelection = DSM_DEFAULT_LB_POLICY_LUN_EXPLICIT; + + // + // Update the states and if appropriate, the path weight + // + DsmpUpdateDesiredStateAndWeight(groupEntry, + DsmWmiVersion, + supportedLBPolicies); + + // + // Update the next path to be used for the group + // + devInfo = DsmpGetActivePathToBeUsed(groupEntry, + DsmpIsSymmetricAccess(tempDevInfo), + SpecialHandlingFlag); + if (devInfo != NULL) { + + InterlockedExchangePointer(&(groupEntry->PathToBeUsed), (PVOID)devInfo->FailGroup); + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpSetLoadBalancePolicy (DsmIds %p): After setting LB policy No FOG available for group %p\n", + DsmIds, + groupEntry)); + + InterlockedExchangePointer(&(groupEntry->PathToBeUsed), NULL); + } + } + + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); + + if (NT_SUCCESS(status) && savedLBSettings) { + + DsmpPersistLBSettings(savedLBSettings); + + DsmpFreePool(savedLBSettings); + } + +__Exit_DsmpSetLoadBalancePolicy: + + if (DsmWmiVersion == DSM_WMI_VERSION_1) { + + ((PDsmSetLoadBalancePolicy_OUT)setLoadBalancePolicyOUT)->Status = status; + + } else { + + setLoadBalancePolicyOUT->Status = status; + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpSetLoadBalancePolicy (DsmIds %p): Exiting function with status 0x%x.\n", + DsmIds, + status)); + + return status; +} + + +NTSTATUS +DsmpValidateSetLBPolicyInput( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_IDS DsmIds, + _In_ IN ULONG DsmWmiVersion, + _In_ IN PVOID SetLoadBalancePolicyIN, + _In_ IN ULONG InBufferSize + ) +/*++ + +Routine Description: + + This routine validates the input buffer given for setting + Load Balance policy + +Arguements: + + DsmContext - DSM Global Context + DsmIds - DSM Ids for the given device + DsmWmiVersion - version of the MPIO_DSM_Path class to use + SetLoadBalancePolicyIN - Describes the load balance policy to be set + InBufferSize - Number of bytes in SetLoadBalancePolicyIN + +Return Value: + + STATUS_SUCCESS - if the input buffer is well formed + Appropriate error status if the input buffer is malformed. + +--*/ +{ + PDSM_Load_Balance_Policy_V2 supportedLBPolicies; + PMPIO_DSM_Path_V2 dsmPath0; + PMPIO_DSM_Path_V2 dsmPath1; + NTSTATUS status = STATUS_SUCCESS; + ULONG inx; + ULONG jnx; + ULONG sizeNeeded; + KIRQL irql; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpValidateSetLBPolicyInput (DsmIds %p): Entering function.\n", + DsmIds)); + + // + // Validate the input buffer for setting Load Balance policy + // + if (DsmWmiVersion > DSM_WMI_VERSION_1) { + + sizeNeeded = FIELD_OFFSET(DSM_Load_Balance_Policy_V2, DSM_Paths); + + } else { + + sizeNeeded = FIELD_OFFSET(DSM_Load_Balance_Policy, DSM_Paths); + } + + if (InBufferSize < sizeNeeded) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpValidateSetLBPolicyInput (DsmIds %p): Insufficient buffer in SetLB. Expected %d, Given %d.\n", + DsmIds, + sizeNeeded, + InBufferSize)); + + status = STATUS_BUFFER_TOO_SMALL; + goto __Exit_DsmpValidateSetLBPolicyInput; + } + + if (DsmWmiVersion == DSM_WMI_VERSION_1) { + + supportedLBPolicies = (PVOID)&(((PDsmSetLoadBalancePolicy_IN)SetLoadBalancePolicyIN)->LoadBalancePolicy); + + sizeNeeded += supportedLBPolicies->DSMPathCount * sizeof(MPIO_DSM_Path); + + } else { + + supportedLBPolicies = &(((PDsmSetLoadBalancePolicyALUA_IN)SetLoadBalancePolicyIN)->LoadBalancePolicy); + + sizeNeeded += supportedLBPolicies->DSMPathCount * sizeof(MPIO_DSM_Path_V2); + } + + if (InBufferSize < sizeNeeded) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpValidateSetLBPolicyInput (DsmIds %p): Insufficient buffer in SetLB. Expected %d, Given %d.\n", + DsmIds, + sizeNeeded, + InBufferSize)); + + status = STATUS_BUFFER_TOO_SMALL; + goto __Exit_DsmpValidateSetLBPolicyInput; + } + + if (supportedLBPolicies->Version > DSM_WMI_VERSION) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpValidateSetLBPolicyInput (DsmIds %p): WMI Version mismatch. Expected %d, Given %d.\n", + DsmIds, + DSM_WMI_VERSION, + supportedLBPolicies->Version)); + + status = DSM_UNSUPPORTED_VERSION; + goto __Exit_DsmpValidateSetLBPolicyInput; + + } else if (supportedLBPolicies->Version < DSM_WMI_VERSION) { + + ULONG dsmWmiVersion = DSM_WMI_VERSION; + TracePrint((TRACE_LEVEL_WARNING, + TRACE_FLAG_WMI, + "DsmpValidateSetLBPolicyInput (DsmIds %p): Use of older management app (WMI-Version %x) with newer DSM (WMI-Version %x).\n", + DsmIds, + supportedLBPolicies->Version, + dsmWmiVersion)); + + NT_ASSERT(supportedLBPolicies->Version == DSM_WMI_VERSION); + } + + if ((supportedLBPolicies->LoadBalancePolicy < DSM_LB_FAILOVER) || + (supportedLBPolicies->LoadBalancePolicy > DSM_LB_LEAST_BLOCKS)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpValidateSetLBPolicyInput (DsmIds %p): Invalid LB Policy %d.\n", + DsmIds, + supportedLBPolicies->LoadBalancePolicy)); + + status = DSM_INVALID_LOAD_BALANCE_POLICY; + goto __Exit_DsmpValidateSetLBPolicyInput; + } + + // + // It is expected that the user provide LB policy settings + // for all the paths and not just a subset of the paths. + // + if (supportedLBPolicies->DSMPathCount != DsmIds->Count) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpValidateSetLBPolicyInput (DsmIds %p): Path Count %d not equal to DSM IDs count %d.\n", + DsmIds, + supportedLBPolicies->DSMPathCount, + DsmIds->Count)); + + status = STATUS_INVALID_PARAMETER; + goto __Exit_DsmpValidateSetLBPolicyInput; + } + + // + // Make sure user did not provide duplicate path ids + // + for (inx = 0; inx < supportedLBPolicies->DSMPathCount && NT_SUCCESS(status); inx++) { + + if (DsmWmiVersion == DSM_WMI_VERSION_1) { + + dsmPath0 = (PVOID)&(((PDSM_Load_Balance_Policy)supportedLBPolicies)->DSM_Paths[inx]); + + } else { + + dsmPath0 = &(supportedLBPolicies->DSM_Paths[inx]); + } + + dsmPath0->Reserved = 0; + + for (jnx = 0; jnx < supportedLBPolicies->DSMPathCount; jnx++) { + + if (DsmWmiVersion == DSM_WMI_VERSION_1) { + + dsmPath1 = (PVOID)&(((PDSM_Load_Balance_Policy)supportedLBPolicies)->DSM_Paths[jnx]); + + } else { + + dsmPath1 = &(supportedLBPolicies->DSM_Paths[jnx]); + } + + if ((inx != jnx) && + ((dsmPath0->DsmPathId == dsmPath1->DsmPathId) && (dsmPath1->DsmPathId != 0))) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpValidateSetLBPolicyInput (DsmIds %p): Duplicate path id %I64x at %d and %d.\n", + DsmIds, + dsmPath0->DsmPathId, + inx, + jnx)); + + status = STATUS_INVALID_PARAMETER; + + break; + } + } + } + + if (NT_SUCCESS(status)) { + + PDSM_DEVICE_INFO devInfo; + PDSM_FAILOVER_GROUP foGroup; + PVOID pathId; + BOOLEAN foundPath; + + irql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); + + // + // Make sure the user has provided path id corresponding + // to all the DSM IDs given to us. + // + for (inx = 0; inx < DsmIds->Count; inx++) { + + devInfo = DsmIds->IdList[inx]; + + if (!DsmpIsDeviceInitialized(devInfo)) { + + continue; + } + + foGroup = devInfo->FailGroup; + if (!foGroup) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpValidateSetLBPolicyInput (DsmIds %p): FO Group NULL for %p at index %d.\n", + DsmIds, + devInfo, + inx)); + + status = STATUS_INVALID_PARAMETER; + + break; + } + + foundPath = FALSE; + + for (jnx = 0; jnx < supportedLBPolicies->DSMPathCount; jnx++) { + + if (DsmWmiVersion == DSM_WMI_VERSION_1) { + + dsmPath0 = (PVOID)&(((PDSM_Load_Balance_Policy)supportedLBPolicies)->DSM_Paths[jnx]); + + } else { + + dsmPath0 = &(supportedLBPolicies->DSM_Paths[jnx]); + } + + pathId = (PVOID) dsmPath0->DsmPathId; + if (foGroup->PathId == pathId) { + + // + // Found the device info corresponding to the given path. + // Use the reserved field in MPIO_DSM_Path to store + // the pointer to the device info. Device Info is used + // later on to set the load balance policy for the device. + // + foundPath = TRUE; + + dsmPath0->Reserved = (ULONG_PTR) devInfo; + + // + // If ALUA, RoundRobin is not an allowed LB policy since not all paths can + // be in A/O state. RRWS must be used instead. + // + if (supportedLBPolicies->LoadBalancePolicy == DSM_LB_ROUND_ROBIN && !DsmpIsSymmetricAccess(devInfo)) { + + status = DSM_INVALID_LOAD_BALANCE_POLICY; + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpValidateSetLBPolicyInput (DsmIds %p): Invalid LB policy for ALUA. Status %x.\n", + DsmIds, + status)); + } + + break; + } + } + + if (!foundPath) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpValidateSetLBPolicyInput (DsmIds %p): Failed to find path %p for %p at index %d.\n", + DsmIds, + foGroup->PathId, + devInfo, + inx)); + + status = STATUS_INVALID_PARAMETER; + + break; + } + } + + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); + } + +__Exit_DsmpValidateSetLBPolicyInput: + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpValidateSetLBPolicyInput (DsmIds %p): Exiting function with status %x.\n", + DsmIds, + status)); + + return status; +} + + +VOID +DsmpSaveDeviceState( + _In_ IN PVOID SupportedLBPolicies, + _In_ IN ULONG DsmWmiVersion + ) +/*+++ + +Routine Description: + + This routine saves the current Load Balance policy settings. + If there is any error while setting the new policy given + by the user, the saved values will be used to restore + the old state. + + Note: This routine MUST be called with DsmContextLock held in Exclusive mode. + +Arguements: + + SupportedLBPolicies - New Load Balance policy values + DsmWmiVersion - version of the MPIO_DSM_Path class to use + +Return Value: + + None +--*/ +{ + PDSM_DEVICE_INFO devInfo; + PMPIO_DSM_Path_V2 dsmPath; + ULONG inx; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpSaveDeviceState (LBP %p): Entering function.\n", + SupportedLBPolicies)); + + inx = 0; + + while (inx < ((PDSM_Load_Balance_Policy_V2)SupportedLBPolicies)->DSMPathCount) { + + if (DsmWmiVersion == DSM_WMI_VERSION_1) { + + dsmPath = (PVOID)&(((PDSM_Load_Balance_Policy)SupportedLBPolicies)->DSM_Paths[inx]); + + } else { + + dsmPath = &(((PDSM_Load_Balance_Policy_V2)SupportedLBPolicies)->DSM_Paths[inx]); + } + + devInfo = (PDSM_DEVICE_INFO)dsmPath->Reserved; + + if (devInfo) { + + devInfo->TempPreviousStateForLB = devInfo->State; + } + + inx++; + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpSaveDeviceState (LBP %p): Exiting function.\n", + SupportedLBPolicies)); + + return; +} + + +VOID +DsmpRestorePreviousDeviceState( + _In_ IN PVOID SupportedLBPolicies, + _In_ IN ULONG DsmWmiVersion + ) +/*++ + +Routine Description: + + This routine restores the old Load Balance policy settings. + If there is any error while setting the new policy given + by the user, the old state is restored from the saved state. + + Note: This routine MUST be called with DsmContextLock held in Exclusive mode. + +Arguements: + + SupportedLBPolicies - New Load Balance policy values + DsmWmiVersion - version of the MPIO_DSM_Path class to use + +Return Value: + + None +--*/ +{ + PDSM_DEVICE_INFO devInfo; + PMPIO_DSM_Path_V2 dsmPath; + ULONG inx; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpRestorePreviousDeviceState (LBP %p): Entering function.\n", + SupportedLBPolicies)); + + inx = 0; + + while (inx < ((PDSM_Load_Balance_Policy_V2)SupportedLBPolicies)->DSMPathCount) { + + if (DsmWmiVersion == DSM_WMI_VERSION_1) { + + dsmPath = (PVOID)&(((PDSM_Load_Balance_Policy)SupportedLBPolicies)->DSM_Paths[inx]); + + } else { + + dsmPath = &(((PDSM_Load_Balance_Policy_V2)SupportedLBPolicies)->DSM_Paths[inx]); + } + + devInfo = (PDSM_DEVICE_INFO)dsmPath->Reserved; + + if (devInfo) { + + devInfo->State = devInfo->TempPreviousStateForLB; + } + + inx++; + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpRestorePreviousDeviceState (LBP %p): Exiting function.\n", + SupportedLBPolicies)); + + return; +} + + +VOID +DsmpUpdateDesiredStateAndWeight( + _In_ IN PDSM_GROUP_ENTRY Group, + _In_ IN ULONG DsmWmiVersion, + _In_ IN PVOID SupportedLBPolicies + ) +/*++ + +Routine Description: + + This routine updates the desired state and path weights + based on admin's LB selection. + + Note: This routine MUST be called with DsmContextLock held in Exclusive mode. + +Arguements: + + Group - The group entry correponding to the pseudo-LUN. + SupportedLBPolicies - New Load Balance policy values + DsmWmiVersion - version of the MPIO_DSM_Path class to use + +Return Value: + + None +--*/ +{ + PMPIO_DSM_Path_V2 dsmPath; + PDSM_DEVICE_INFO devInfo; + ULONG inx; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpUpdatedDesiredState (Group %p): Entering function.\n", + Group)); + + inx = 0; + while (inx < ((PDSM_Load_Balance_Policy_V2)SupportedLBPolicies)->DSMPathCount) { + + if (DsmWmiVersion == DSM_WMI_VERSION_1) { + + dsmPath = (PVOID)&(((PDSM_Load_Balance_Policy)SupportedLBPolicies)->DSM_Paths[inx]); + + } else { + + dsmPath = &(((PDSM_Load_Balance_Policy_V2)SupportedLBPolicies)->DSM_Paths[inx]); + } + + devInfo = (PDSM_DEVICE_INFO) dsmPath->Reserved; + + if (!devInfo) { + + inx++; + continue; + } + + DSM_ASSERT(devInfo->DeviceSig == DSM_DEVICE_SIG); + NT_ASSERT(devInfo->Group == Group); + + // + // We'll honor the chosen path for FOO for ALUA storage + // since we know for a fact that the Admin has chosen the path. + // We'll also honor path state in RRWS if it is different from TPG state + // as that too is an indication that it was explicitly selected. + // + if ((DsmpIsSymmetricAccess(devInfo)) || + (Group->LoadBalanceType == DSM_LB_FAILOVER) || + (!DsmpIsSymmetricAccess(devInfo) && Group->LoadBalanceType == DSM_LB_ROUND_ROBIN_WITH_SUBSET && devInfo->State != devInfo->ALUAState)) { + + // + // Check if this is the primary path or a standby path + // + if (dsmPath->PrimaryPath) { + + devInfo->DesiredState = DSM_DEV_ACTIVE_OPTIMIZED; + + if (DsmWmiVersion > DSM_WMI_VERSION_1) { + + if (!dsmPath->OptimizedPath) { + + devInfo->DesiredState = DSM_DEV_ACTIVE_UNOPTIMIZED; + } + } + + } else { + + devInfo->DesiredState = DSM_DEV_STANDBY; + + if (DsmWmiVersion > DSM_WMI_VERSION_1) { + + if (!dsmPath->OptimizedPath) { + + devInfo->DesiredState = DSM_DEV_UNAVAILABLE; + } + } + } + } else { + + devInfo->DesiredState = DSM_DEV_UNDETERMINED; + } + + if (Group->LoadBalanceType == DSM_LB_WEIGHTED_PATHS) { + + devInfo->PathWeight = dsmPath->PathWeight; + } + + inx++; + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpUpdatedDesiredState (Group %p): Exiting function.\n", + Group)); + + return; +} + + +NTSTATUS +DsmpQueryDevicePerf( + _In_ PDSM_CONTEXT DsmContext, + _In_ PDSM_IDS DsmIds, + _In_ ULONG InBufferSize, + _Inout_ PULONG OutBufferSize, + _Out_writes_to_(*OutBufferSize, *OutBufferSize) PUCHAR Buffer + ) +/*++ + +Routine Description: + + This routine returns the perf counters for each path for the + device that corresponds to the passed in DsmIds. + +Arguements: + + DsmContext - Global DSM context + DsmIds - DSM Ids for the given device + InBufferSize - Size of the input buffer + OutBufferSize - Size of the output buffer + Buffer - Buffer in which the current Load Balance policy settings + is returned, if the buffer is big enough + +Return Value: + + STATUS_SUCCESS on success + Appropriate error code on error. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + PDSM_DEVICE_INFO devInfo; + ULONG sizeNeeded; + PMSDSM_DEVICE_PERF devicePerf; + ULONG i; + PMSDSM_DEVICEPATH_PERF pathPerf; + KIRQL irql; + + UNREFERENCED_PARAMETER(InBufferSize); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpQueryDevicePerf (DsmIds %p): Entering function.\n", + DsmIds)); + + // + // At least one device should be given + // + if (DsmIds->Count == 0) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpQueryDevicePerf (DsmIds %p): No DSM Ids given.\n", + DsmIds)); + + *OutBufferSize = 0; + status = STATUS_INVALID_PARAMETER; + + goto __Exit_DsmpQueryDevicePerf; + } + + sizeNeeded = AlignOn8Bytes(FIELD_OFFSET(MSDSM_DEVICE_PERF, PerfInfo)); + sizeNeeded += (DsmIds->Count * sizeof(MSDSM_DEVICEPATH_PERF)); + + if (*OutBufferSize < sizeNeeded) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpQueryDevicePerf (DsmIds %p): Output buffer too small for QueryLBPolicy.\n", + DsmIds)); + + *OutBufferSize = sizeNeeded; + status = STATUS_BUFFER_TOO_SMALL; + + goto __Exit_DsmpQueryDevicePerf; + } + + // + // Zero out the output buffer first + // + RtlZeroMemory(Buffer, sizeNeeded); + +#if DBG + devInfo = DsmIds->IdList[0]; + DSM_ASSERT(devInfo); + DSM_ASSERT(devInfo->DeviceSig == DSM_DEVICE_SIG); +#endif + + irql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); + + devicePerf = (PMSDSM_DEVICE_PERF)Buffer; + devicePerf->NumberPaths = DsmIds->Count; + + // + // For each path, get the stats info + // + for (i = 0; i < DsmIds->Count; i++) { + + pathPerf = &devicePerf->PerfInfo[i]; + devInfo = DsmIds->IdList[i]; + + if (DsmpIsDeviceInitialized(devInfo)) { + + pathPerf->PathId = (ULONGLONG)((ULONG_PTR)((devInfo->FailGroup)->PathId)); + pathPerf->NumberReads = (devInfo->DeviceStats).NumberReads; + pathPerf->NumberWrites = (devInfo->DeviceStats).NumberWrites; + pathPerf->BytesRead = (devInfo->DeviceStats).BytesRead; + pathPerf->BytesWritten = (devInfo->DeviceStats).BytesWritten; + } + } + + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); + + *OutBufferSize = sizeNeeded; + +__Exit_DsmpQueryDevicePerf: + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpQueryDevicePerf (DsmIds %p): Exiting function with status %x.\n", + DsmIds, + status)); + + return status; +} + + +NTSTATUS +DsmpClearPerfCounters( + _In_ IN PDSM_CONTEXT DsmContext, + _In_ IN PDSM_IDS DsmIds + ) +/*++ + +Routine Description: + + This routine clears the perf counters for each path for the + device that corresponds to the passed in DsmIds. + +Arguements: + + DsmContext - Global DSM context + DsmIds - DSM Ids for the given device + +Return Value: + + STATUS_SUCCESS on success + Appropriate error code on error. + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + PDSM_DEVICE_INFO devInfo; + KIRQL irql; + ULONG i; + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpClearPerfCounters (DsmIds %p): Entering function.\n", + DsmIds)); + + // + // At least one device should be given + // + if (DsmIds->Count == 0) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpClearPerfCounters (DsmIds %p): No DSM Ids given.\n", + DsmIds)); + + status = STATUS_INVALID_PARAMETER; + + goto __Exit_DsmpClearPerfCounters; + } + + irql = ExAcquireSpinLockExclusive(&(DsmContext->DsmContextLock)); + + for (i = 0; i < DsmIds->Count; i++) { + + devInfo = DsmIds->IdList[i]; + DSM_ASSERT(devInfo); + DSM_ASSERT(devInfo->DeviceSig == DSM_DEVICE_SIG); + + if (devInfo) { + (devInfo->DeviceStats).BytesRead = 0; + (devInfo->DeviceStats).BytesWritten = 0; + (devInfo->DeviceStats).NumberReads = 0; + (devInfo->DeviceStats).NumberWrites = 0; + } + } + + ExReleaseSpinLockExclusive(&(DsmContext->DsmContextLock), irql); + +__Exit_DsmpClearPerfCounters: + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpClearPerfCounters (DsmIds %p): Exiting function with status %x.\n", + DsmIds, + status)); + + return status; +} + + +NTSTATUS +DsmpQuerySupportedDevicesList( + _In_ PDSM_CONTEXT DsmContext, + _In_ ULONG InBufferSize, + _Inout_ PULONG OutBufferSize, + _Out_writes_to_(*OutBufferSize, *OutBufferSize) PUCHAR Buffer + ) +/*++ + +Routine Description: + + This routine returns the list of devices that are supported by MSDSM. + +Arguements: + + DsmContext - Global DSM context + InBufferSize - Size of the input buffer + OutBufferSize - Size of the output buffer + Buffer - Buffer in which the current Load Balance policy settings + is returned, if the buffer is big enough + +Return Value: + + STATUS_SUCCESS on success + Appropriate error code on error. + +--*/ +{ + NTSTATUS status; + ULONG sizeNeeded; + PMSDSM_SUPPORTED_DEVICES_LIST supportedDeviceIds; + PWSTR szIndex; + PWSTR deviceIdIndex; + ULONG numberDeviceIds = 0; + ULONG index = 0; + KIRQL oldIrql; + PWSTR tempBuffer = NULL; + + UNREFERENCED_PARAMETER(InBufferSize); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpQuerySupportedDevicesList (DsmContext %p): Entering function.\n", + DsmContext)); + + // + // It is possible that manually changes to the registry weren't yet picked up, + // so query for the list in its current state. Failure to get this list is not + // fatal, so ignore errors. + // +#if DBG + status = DsmpGetDeviceList(DsmContext); + NT_ASSERT(NT_SUCCESS(status)); +#else + DsmpGetDeviceList(DsmContext); +#endif + + // + // Since it is possible that this list may change if a new device arrival + // gets processed at the same time as this query being processed, we need + // to protect it. + // + KeAcquireSpinLock(&DsmContext->SupportedDevicesListLock, &oldIrql); + + tempBuffer = DsmpAllocatePool(NonPagedPoolNx, DsmContext->SupportedDevices.MaximumLength, DSM_TAG_REG_VALUE_RELATED); + + if (tempBuffer) { + + RtlCopyMemory(tempBuffer, DsmContext->SupportedDevices.Buffer, DsmContext->SupportedDevices.Length); + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpQuerySupportedDevicesList (DsmContext %p): Failed to allocate temporary list.\n", + DsmContext)); + + status = STATUS_INSUFFICIENT_RESOURCES; + KeReleaseSpinLock(&DsmContext->SupportedDevicesListLock, oldIrql); + + goto __Exit_DsmpQuerySupportedDevicesList; + } + + KeReleaseSpinLock(&DsmContext->SupportedDevicesListLock, oldIrql); + + status = STATUS_SUCCESS; + szIndex = tempBuffer; + + sizeNeeded = AlignOn8Bytes(FIELD_OFFSET(MSDSM_SUPPORTED_DEVICES_LIST, DeviceId)); + + if (szIndex) { + + while (*szIndex) { + + szIndex += wcslen(szIndex) + 1; + numberDeviceIds++; + } + + sizeNeeded += numberDeviceIds * (MSDSM_MAX_DEVICE_ID_SIZE + sizeof(WNULL)); + } + + if (*OutBufferSize < sizeNeeded) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpQuerySupportedDevicesList (DsmContext %p): Output buffer too small for QuerySupportedDevicesList.\n", + DsmContext)); + + *OutBufferSize = sizeNeeded; + status = STATUS_BUFFER_TOO_SMALL; + + goto __Exit_DsmpQuerySupportedDevicesList; + } + + // + // Zero out the output buffer first + // + RtlZeroMemory(Buffer, sizeNeeded); + + *OutBufferSize = sizeNeeded; + + supportedDeviceIds = (PMSDSM_SUPPORTED_DEVICES_LIST)Buffer; + supportedDeviceIds->NumberDevices = numberDeviceIds; + + for (index = 0, szIndex = tempBuffer, deviceIdIndex = supportedDeviceIds->DeviceId; + index < numberDeviceIds; + index++, szIndex += wcslen(szIndex) + 1, deviceIdIndex += MSDSM_MAX_DEVICE_ID_LENGTH) { + + *((PUSHORT)deviceIdIndex) = MSDSM_MAX_DEVICE_ID_SIZE; + deviceIdIndex++; + + RtlStringCchCopyW(deviceIdIndex, + MSDSM_MAX_DEVICE_ID_LENGTH - 1, + szIndex); + } + +__Exit_DsmpQuerySupportedDevicesList: + + if (tempBuffer) { + DsmpFreePool(tempBuffer); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpQuerySupportedDevicesList (DsmContext %p): Exiting function with status %x.\n", + DsmContext, + status)); + + return status; +} + + +NTSTATUS +DsmpQueryTargetsDefaultPolicy( + _In_ PDSM_CONTEXT DsmContext, + _In_ ULONG InBufferSize, + _Inout_ PULONG OutBufferSize, + _Out_writes_to_(*OutBufferSize, *OutBufferSize) PUCHAR Buffer + ) +/*++ + +Routine Description: + + This routine is used to build the target list (for which the override default LB policy + was explicitly set), by querying the services key for the subkeys under + "msdsm\Parameters\DsmTargetsLoadBalanceSetting" + +Arguements: + + Context - The DSM Context value. It contains storage for the target hardware ids and their + default policy info. + InBufferSize - Size of the input buffer + OutBufferSize - Size of the output buffer + Buffer - Buffer in which the current targets whose default policy settings is returned, if the buffer is big enough + +Return Value: + + STATUS_SUCCESS on success + Appropriate error code on error. + +--*/ +{ + ULONG sizeNeeded; + PMSDSM_TARGETS_DEFAULT_LOAD_BALANCE_POLICY targetsPolicyInfo = (PMSDSM_TARGETS_DEFAULT_LOAD_BALANCE_POLICY)Buffer; + PMSDSM_TARGET_DEFAULT_POLICY_INFO targetPolicyInfo; + HANDLE targetsLBSettingKey = NULL; + NTSTATUS status; + PKEY_FULL_INFORMATION keyFullInfo = NULL; + ULONG length = sizeof(KEY_FULL_INFORMATION); + ULONG numSubKeys = 0; + WCHAR vidPid[25] = {0}; + PKEY_BASIC_INFORMATION keyBasicInfo = NULL; + OBJECT_ATTRIBUTES objectAttributes; + HANDLE targetKey = NULL; + ULONG index = 0; + RTL_QUERY_REGISTRY_TABLE queryTable[2]; + DSM_LOAD_BALANCE_TYPE loadBalanceType; + ULONGLONG preferredPath = (ULONGLONG)((ULONG_PTR)MAXULONG); + PWCHAR policyInfoIndex; + UNICODE_STRING keyValueName; + PKEY_VALUE_PARTIAL_INFORMATION keyValueInfo = NULL; + + UNREFERENCED_PARAMETER(InBufferSize); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpQueryTargetsDefaultPolicy (Context %p): Entering function.\n", + DsmContext)); + + status = DsmpOpenTargetsLoadBalanceSettingKey(KEY_ALL_ACCESS, &targetsLBSettingKey); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpQueryTargetsDefaultPolicy (Context %p): Failed to open Targets LB Setting key. Status %x.\n", + DsmContext, + status)); + + goto __Exit_DsmpQueryTargetsDefaultPolicy; + } + + // + // Query for number of subkeys + // + do { + if (keyFullInfo) { + + DsmpFreePool(keyFullInfo); + } + + keyFullInfo = DsmpAllocatePool(NonPagedPoolNxCacheAligned, length, DSM_TAG_REG_KEY_RELATED); + + if (!keyFullInfo) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpQueryTargetsDefaultPolicy (Context %p): Failed to allocate resources for key full info.\n", + DsmContext)); + + status = STATUS_INSUFFICIENT_RESOURCES; + goto __Exit_DsmpQueryTargetsDefaultPolicy; + } + + status = ZwQueryKey(targetsLBSettingKey, + KeyFullInformation, + keyFullInfo, + length, + &length); + + } while (status == STATUS_BUFFER_TOO_SMALL || status == STATUS_BUFFER_OVERFLOW); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpQueryTargetsDefaultPolicy (Context %p): Failed to query key. Status %x.\n", + DsmContext, + status)); + + goto __Exit_DsmpQueryTargetsDefaultPolicy; + } + + // + // Calculate total buffer size required + // + numSubKeys = keyFullInfo->SubKeys; + + sizeNeeded = AlignOn8Bytes(FIELD_OFFSET(MSDSM_TARGETS_DEFAULT_LOAD_BALANCE_POLICY, TargetDefaultPolicyInfo)); + sizeNeeded += numSubKeys * sizeof(MSDSM_TARGET_DEFAULT_POLICY_INFO); + + if (*OutBufferSize < sizeNeeded) { + + *OutBufferSize = sizeNeeded; + status = STATUS_BUFFER_TOO_SMALL; + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpQueryTargetsDefaultPolicy (Context %p): Buffer insufficient. Status %x.\n", + DsmContext, + status)); + + goto __Exit_DsmpQueryTargetsDefaultPolicy; + } + + *OutBufferSize = sizeNeeded; + RtlZeroMemory(Buffer, *OutBufferSize); + + targetsPolicyInfo->NumberDevices = numSubKeys; + targetPolicyInfo = targetsPolicyInfo->TargetDefaultPolicyInfo; + + // + // Now Enumerate all of the subkeys + // + for(index = 0; index < numSubKeys && NT_SUCCESS(status); index++) { + + UNICODE_STRING targetName; + + if (targetKey) { + ZwClose(targetKey); + targetKey = NULL; + } + + length = sizeof(KEY_BASIC_INFORMATION); + + do { + if (keyBasicInfo) { + + DsmpFreePool(keyBasicInfo); + } + + keyBasicInfo = DsmpAllocatePool(NonPagedPoolNxCacheAligned, + length, + DSM_TAG_REG_KEY_RELATED); + + if (!keyBasicInfo) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpQueryTargetsDefaultPolicy (Context %p): Failed to allocate resources for key basic info.\n", + DsmContext)); + + status = STATUS_INSUFFICIENT_RESOURCES; + goto __Exit_DsmpQueryTargetsDefaultPolicy; + } + + // + // Enumerate the index'th subkey + // + status = ZwEnumerateKey(targetsLBSettingKey, + index, + KeyBasicInformation, + keyBasicInfo, + length, + &length); + + } while (status == STATUS_BUFFER_TOO_SMALL || status == STATUS_BUFFER_OVERFLOW); + + // + // Ignore errors - this is a best case effort. + // + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpQueryTargetsDefaultPolicy (Context %p): Failed to enumerate sub key's info. Status %x.\n", + DsmContext, + status)); + + status = STATUS_SUCCESS; + continue; + } + + RtlZeroMemory(vidPid, sizeof(vidPid)); + RtlStringCbCopyNW(vidPid, sizeof(vidPid), keyBasicInfo->Name, keyBasicInfo->NameLength); + RtlInitUnicodeString(&targetName, vidPid); + + // + // Open a handle to the the target subkey. + // + InitializeObjectAttributes(&objectAttributes, + &targetName, + (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE), + targetsLBSettingKey, + (PSECURITY_DESCRIPTOR) NULL); + + status = ZwOpenKey(&targetKey, + KEY_ALL_ACCESS, + &objectAttributes); + + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpQueryTargetsDefaultPolicy (Context %p): Failed to open reg key %ws. Status %x.\n", + DsmContext, + vidPid, + status)); + + goto __Exit_DsmpQueryTargetsDefaultPolicy; + } + + RtlZeroMemory(queryTable, sizeof(queryTable)); + + queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED; + queryTable[0].Name = DSM_LOAD_BALANCE_POLICY; + queryTable[0].EntryContext = &loadBalanceType; + + status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE, + targetKey, + queryTable, + targetKey, + NULL); + if (!NT_SUCCESS(status)) { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpQueryTargetsDefaultPolicy (Context %p): Failed to query LB Policy for %ws - error %x.\n", + DsmContext, + vidPid, + status)); + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpQueryTargetsDefaultPolicy (Context %p): LB Policy for %ws is %d.\n", + DsmContext, + vidPid, + loadBalanceType)); + + RtlInitUnicodeString(&keyValueName, DSM_PREFERRED_PATH); + + length = sizeof(KEY_VALUE_PARTIAL_INFORMATION); + + do { + DsmpFreePool(keyValueInfo); + keyValueInfo = DsmpAllocatePool(NonPagedPoolNxCacheAligned, length, DSM_TAG_REG_KEY_RELATED); + if (!keyValueInfo) { + + status = STATUS_INSUFFICIENT_RESOURCES; + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpQueryTargetsDefaultPolicy (Context %p): Failed to allocate resources for keyValueInfo (PP). Status %x.\n", + DsmContext, + status)); + + goto __Exit_DsmpQueryTargetsDefaultPolicy; + } + + status = ZwQueryValueKey(targetKey, + &keyValueName, + KeyValuePartialInformation, + keyValueInfo, + length, + &length); + + } while (status == STATUS_BUFFER_TOO_SMALL || status == STATUS_BUFFER_OVERFLOW); + + if (NT_SUCCESS(status)) { + + NT_ASSERT(keyValueInfo->DataLength == sizeof(ULONGLONG)); + + preferredPath = *((ULONGLONG UNALIGNED *)keyValueInfo->Data); + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpQueryTargetsDefaultPolicy (Context %p): PreferredPath for %ws is %I64x.\n", + DsmContext, + vidPid, + preferredPath)); + + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpQueryTargetsDefaultPolicy (Context %p): Failed to query PreferredPath for %ws. Status %x.\n", + DsmContext, + vidPid, + status)); + } + + // + // Copy over this target's policy info. + // + policyInfoIndex = targetPolicyInfo->HardwareId; + *((PUSHORT)policyInfoIndex) = MSDSM_MAX_DEVICE_ID_SIZE; + policyInfoIndex++; + RtlStringCchCopyW((PWSTR)policyInfoIndex, MSDSM_MAX_DEVICE_ID_LENGTH - 1, vidPid); + targetPolicyInfo->LoadBalancePolicy = loadBalanceType; + targetPolicyInfo->PreferredPath = preferredPath; + + targetPolicyInfo++; + } + } + +__Exit_DsmpQueryTargetsDefaultPolicy: + + if (targetKey) { + ZwClose(targetKey); + } + + if (targetsLBSettingKey) { + ZwClose(targetsLBSettingKey); + } + + if (keyBasicInfo) { + DsmpFreePool(keyBasicInfo); + } + + if (keyValueInfo) { + DsmpFreePool(keyValueInfo); + } + + if (keyFullInfo) { + DsmpFreePool(keyFullInfo); + } + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpQueryTargetsDefaultPolicy (Context %p): Exiting function with status %x.\n", + DsmContext, + status)); + + return status; +} + + +NTSTATUS +DsmpQueryDsmDefaultPolicy( + _In_ PDSM_CONTEXT DsmContext, + _In_ ULONG InBufferSize, + _Inout_ PULONG OutBufferSize, + _Out_writes_to_(*OutBufferSize, *OutBufferSize) PUCHAR Buffer + ) +/*++ + +Routine Description: + + This routine is used to return the override MSDSM-wide default LB policy + if it was explicitly set, by querying the services key at "msdsm\Parameters" + +Arguements: + + Context - The DSM Context value. It contains storage for the target hardware ids and their + default policy info. + InBufferSize - Size of the input buffer + OutBufferSize - Size of the output buffer + Buffer - Buffer in which the current MSDSM-wide default policy is returned, if the buffer + is big enough + +Return Value: + + STATUS_SUCCESS on success + Appropriate error code on error. + +--*/ +{ + PMSDSM_DEFAULT_LOAD_BALANCE_POLICY dsmPolicyInfo = (PMSDSM_DEFAULT_LOAD_BALANCE_POLICY)Buffer; + NTSTATUS status; + DSM_LOAD_BALANCE_TYPE loadBalanceType; + ULONGLONG preferredPath = (ULONGLONG)((ULONG_PTR)MAXULONG); + + UNREFERENCED_PARAMETER(InBufferSize); + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpQueryDsmDefaultPolicy (Context %p): Entering function.\n", + DsmContext)); + + if (*OutBufferSize < sizeof(MSDSM_DEFAULT_LOAD_BALANCE_POLICY)) { + + *OutBufferSize = sizeof(MSDSM_DEFAULT_LOAD_BALANCE_POLICY); + status = STATUS_BUFFER_TOO_SMALL; + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpQueryDsmDefaultPolicy (Context %p): Buffer insufficient. Status %x.\n", + DsmContext, + status)); + + goto __Exit_DsmpQueryDsmDefaultPolicy; + } + + *OutBufferSize = sizeof(MSDSM_DEFAULT_LOAD_BALANCE_POLICY); + RtlZeroMemory(Buffer, *OutBufferSize); + + status = DsmpQueryDsmLBPolicyFromRegistry(&loadBalanceType, &preferredPath); + + if (NT_SUCCESS(status)) { + + dsmPolicyInfo->LoadBalancePolicy = loadBalanceType; + dsmPolicyInfo->PreferredPath = (ULONGLONG)((ULONG_PTR)preferredPath); + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpQueryDsmDefaultPolicy (Context %p): LB policy = %u, Preferred path = %I64x.\n", + DsmContext, + dsmPolicyInfo->LoadBalancePolicy, + dsmPolicyInfo->PreferredPath)); + } else { + + TracePrint((TRACE_LEVEL_ERROR, + TRACE_FLAG_WMI, + "DsmpQueryDsmDefaultPolicy (Context %p): Query for MSDSM-wide policy, status %x.\n", + DsmContext, + status)); + } + +__Exit_DsmpQueryDsmDefaultPolicy: + + TracePrint((TRACE_LEVEL_VERBOSE, + TRACE_FLAG_WMI, + "DsmpQueryDsmDefaultPolicy (Context %p): Exiting function with status %x.\n", + DsmContext, + status)); + + return status; +} + diff --git a/storage/ramdisk/ramdisk.sln b/storage/ramdisk/ramdisk.sln index b9097f75f..a94adfd11 100644 --- a/storage/ramdisk/ramdisk.sln +++ b/storage/ramdisk/ramdisk.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0 MinimumVisualStudioVersion = 12.0 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WdfRamdisk", "src\WdfRamdisk.vcxproj", "{FAEE8721-3E3F-4338-8E7B-1BE9FC028C15}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WdfRamdisk", "src\WdfRamdisk.vcxproj", "{8BD18F95-E980-494A-9058-F0DA28C0ECB9}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {FAEE8721-3E3F-4338-8E7B-1BE9FC028C15}.Debug|Win32.ActiveCfg = Debug|Win32 - {FAEE8721-3E3F-4338-8E7B-1BE9FC028C15}.Debug|Win32.Build.0 = Debug|Win32 - {FAEE8721-3E3F-4338-8E7B-1BE9FC028C15}.Release|Win32.ActiveCfg = Release|Win32 - {FAEE8721-3E3F-4338-8E7B-1BE9FC028C15}.Release|Win32.Build.0 = Release|Win32 - {FAEE8721-3E3F-4338-8E7B-1BE9FC028C15}.Debug|x64.ActiveCfg = Debug|x64 - {FAEE8721-3E3F-4338-8E7B-1BE9FC028C15}.Debug|x64.Build.0 = Debug|x64 - {FAEE8721-3E3F-4338-8E7B-1BE9FC028C15}.Release|x64.ActiveCfg = Release|x64 - {FAEE8721-3E3F-4338-8E7B-1BE9FC028C15}.Release|x64.Build.0 = Release|x64 + {8BD18F95-E980-494A-9058-F0DA28C0ECB9}.Debug|Win32.ActiveCfg = Debug|Win32 + {8BD18F95-E980-494A-9058-F0DA28C0ECB9}.Debug|Win32.Build.0 = Debug|Win32 + {8BD18F95-E980-494A-9058-F0DA28C0ECB9}.Release|Win32.ActiveCfg = Release|Win32 + {8BD18F95-E980-494A-9058-F0DA28C0ECB9}.Release|Win32.Build.0 = Release|Win32 + {8BD18F95-E980-494A-9058-F0DA28C0ECB9}.Debug|x64.ActiveCfg = Debug|x64 + {8BD18F95-E980-494A-9058-F0DA28C0ECB9}.Debug|x64.Build.0 = Debug|x64 + {8BD18F95-E980-494A-9058-F0DA28C0ECB9}.Release|x64.ActiveCfg = Release|x64 + {8BD18F95-E980-494A-9058-F0DA28C0ECB9}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/storage/ramdisk/src/WdfRamdisk.vcxproj b/storage/ramdisk/src/WdfRamdisk.vcxproj index 64f70c238..655faf0fc 100644 --- a/storage/ramdisk/src/WdfRamdisk.vcxproj +++ b/storage/ramdisk/src/WdfRamdisk.vcxproj @@ -19,12 +19,12 @@ - {FAEE8721-3E3F-4338-8E7B-1BE9FC028C15} + {8BD18F95-E980-494A-9058-F0DA28C0ECB9} $(MSBuildProjectName) 1 Debug Win32 - {987E33B7-1E8D-4015-A46C-08667083FC5F} + {E179004E-17AB-4904-B671-B3D996E2E00C} @@ -146,7 +146,6 @@ - diff --git a/storage/ramdisk/src/WdfRamdisk.vcxproj.Filters b/storage/ramdisk/src/WdfRamdisk.vcxproj.Filters index 1e94b2f43..d4dc52309 100644 --- a/storage/ramdisk/src/WdfRamdisk.vcxproj.Filters +++ b/storage/ramdisk/src/WdfRamdisk.vcxproj.Filters @@ -3,25 +3,22 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {D9F38118-6A63-448F-A1E6-428B134C40FF} + {B4C67F3E-3D06-47B0-A09C-4E0740919AF8} h;hpp;hxx;hm;inl;inc;xsd - {13BF032A-5FDD-40A6-BA9C-3C48A3EF713E} + {74F7327B-A6C4-481F-998B-5D49FAC38D3D} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {301177B6-FDBA-46E7-BA20-E5AAE5CA0223} + {1E530CA0-70B5-4B7F-A753-F3CDA9804897} inf;inv;inx;mof;mc; - {8D1F3AF6-FD7D-4065-8ADA-FFBF156120D3} + {7B63C0F9-A06F-453D-8701-3EA7C844F62D} - - Driver Files - Driver Files diff --git a/storage/ramdisk/src/ramdisk.inx b/storage/ramdisk/src/ramdisk.inx index 40ad4b1b4..852108235 100644 --- a/storage/ramdisk/src/ramdisk.inx +++ b/storage/ramdisk/src/ramdisk.inx @@ -11,7 +11,7 @@ Signature="$WINDOWS NT$" Class=Sample ClassGuid={78A1C341-4539-11d3-B88D-00C04FAD5171} -Provider=%MSFT% +Provider=%ProviderString% DriverVer=11/14/1999,5.00.2183.1 CatalogFile=KmdfSamples.cat @@ -35,7 +35,7 @@ WdfRamdisk.sys WdfRamdisk.sys=1 [Manufacturer] -%MSFT% = DiskDevice,NT$ARCH$ +%ManufacturerString% = DiskDevice,NT$ARCH$ ; For XP and later [DiskDevice.NT$ARCH$] @@ -87,11 +87,12 @@ KmdfLibraryVersion = $KMDFVERSION$ [Strings] -MSFT = "Microsoft" -ClassName = "Sample Device" -DiskDevDesc = "WDF Sample RAM disk Driver" -DiskServiceDesc = "Ramdisk Driver" -InstDisk = "Ramdisk Install Disk" +ProviderString = "TODO-Set-Provider" +ManufacturerString = "TODO-Set-Manufacturer" +ClassName = "Sample Device" +DiskDevDesc = "WDF Sample RAM disk Driver" +DiskServiceDesc = "Ramdisk Driver" +InstDisk = "Ramdisk Install Disk" ;******************************************* ;Handy macro substitutions (non-localizable) SPSVCINST_ASSOCSERVICE = 0x00000002 diff --git a/storage/sfloppy/sfloppy.sln b/storage/sfloppy/sfloppy.sln index 2735c399e..08aacade8 100644 --- a/storage/sfloppy/sfloppy.sln +++ b/storage/sfloppy/sfloppy.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0 MinimumVisualStudioVersion = 12.0 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sfloppy", "src\sfloppy.vcxproj", "{D0708482-EBB5-4AA4-A213-83C4E1997327}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sfloppy", "src\sfloppy.vcxproj", "{A18D5221-BD8F-40B5-BF73-8BCBECCCEFC7}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {D0708482-EBB5-4AA4-A213-83C4E1997327}.Debug|Win32.ActiveCfg = Debug|Win32 - {D0708482-EBB5-4AA4-A213-83C4E1997327}.Debug|Win32.Build.0 = Debug|Win32 - {D0708482-EBB5-4AA4-A213-83C4E1997327}.Release|Win32.ActiveCfg = Release|Win32 - {D0708482-EBB5-4AA4-A213-83C4E1997327}.Release|Win32.Build.0 = Release|Win32 - {D0708482-EBB5-4AA4-A213-83C4E1997327}.Debug|x64.ActiveCfg = Debug|x64 - {D0708482-EBB5-4AA4-A213-83C4E1997327}.Debug|x64.Build.0 = Debug|x64 - {D0708482-EBB5-4AA4-A213-83C4E1997327}.Release|x64.ActiveCfg = Release|x64 - {D0708482-EBB5-4AA4-A213-83C4E1997327}.Release|x64.Build.0 = Release|x64 + {A18D5221-BD8F-40B5-BF73-8BCBECCCEFC7}.Debug|Win32.ActiveCfg = Debug|Win32 + {A18D5221-BD8F-40B5-BF73-8BCBECCCEFC7}.Debug|Win32.Build.0 = Debug|Win32 + {A18D5221-BD8F-40B5-BF73-8BCBECCCEFC7}.Release|Win32.ActiveCfg = Release|Win32 + {A18D5221-BD8F-40B5-BF73-8BCBECCCEFC7}.Release|Win32.Build.0 = Release|Win32 + {A18D5221-BD8F-40B5-BF73-8BCBECCCEFC7}.Debug|x64.ActiveCfg = Debug|x64 + {A18D5221-BD8F-40B5-BF73-8BCBECCCEFC7}.Debug|x64.Build.0 = Debug|x64 + {A18D5221-BD8F-40B5-BF73-8BCBECCCEFC7}.Release|x64.ActiveCfg = Release|x64 + {A18D5221-BD8F-40B5-BF73-8BCBECCCEFC7}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/storage/sfloppy/src/sfloppy.inf b/storage/sfloppy/src/sfloppy.inf index c7926d37a..5eb2f79ae 100644 --- a/storage/sfloppy/src/sfloppy.inf +++ b/storage/sfloppy/src/sfloppy.inf @@ -8,7 +8,7 @@ Signature="$WINDOWS NT$" Class=FloppyDisk ClassGuid={4D36E980-E325-11CE-BFC1-08002BE10318} -Provider=%DDK_SAMPLE% +Provider=%ProviderString% CatalogFile=ddk_sample.cat DriverVer=05/17/2002,5.2.3635.0 @@ -30,7 +30,7 @@ sfloppy_copyfiles=12 DefaultDestDir=12 [Manufacturer] -%GenManufacturer%=floppy_device,NTx86,NTia64,NTamd64 +%ManufacturerName%=floppy_device,NTx86,NTia64,NTamd64 [floppy_device.NTx86] %sfloppy_devdesc%=sfloppy_install,GenSFloppy @@ -79,9 +79,9 @@ AddReg=sfloppyEnable.RegHW HKR,,SuperFloppy,%REG_DWORD%,0x00000001 [Strings] -DDK_SAMPLE="DDK Sample Provider" +ProviderString="TODO-Set-Provider" +ManufacturerName="TODO-Set-Manufacturer" floppyClassName="Floppy disk drives" -GenManufacturer="(Standard floppy disk drives)" sfloppy_devdesc="High-Capacity Floppy Disk Drive" REG_DWORD=0x00010001 diff --git a/storage/sfloppy/src/sfloppy.vcxproj b/storage/sfloppy/src/sfloppy.vcxproj index 51ddc8665..2a76c175f 100644 --- a/storage/sfloppy/src/sfloppy.vcxproj +++ b/storage/sfloppy/src/sfloppy.vcxproj @@ -19,12 +19,12 @@ - {D0708482-EBB5-4AA4-A213-83C4E1997327} + {A18D5221-BD8F-40B5-BF73-8BCBECCCEFC7} $(MSBuildProjectName) false Debug Win32 - {FF40AD7B-43CA-4917-9BD5-1F66F696A48B} + {C458139C-2A5A-4D66-A35E-B77FBAF95078} @@ -167,7 +167,6 @@ - diff --git a/storage/sfloppy/src/sfloppy.vcxproj.Filters b/storage/sfloppy/src/sfloppy.vcxproj.Filters index 6e61336ae..be90688a5 100644 --- a/storage/sfloppy/src/sfloppy.vcxproj.Filters +++ b/storage/sfloppy/src/sfloppy.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {72E51A2A-765E-4295-BC0C-8BA66207BCEE} + {25A4709A-0B52-42AD-8DE1-F6C233C9EF20} h;hpp;hxx;hm;inl;inc;xsd - {A0E005F4-468E-41D1-B484-84ED829BCCEE} + {D3F642A9-4C3E-4E09-AB52-7C7C45748D34} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {94587BDC-6D5B-42D6-BCE5-BEC6E77511AE} + {65EE5CDD-5FEB-4C5B-9ABE-2E0354873707} inf;inv;inx;mof;mc; - {F24E2189-1E6D-44D7-AF6D-A39A4B8B8160} + {37C9762C-6FA5-486F-87D6-E2B8083013F8} diff --git a/storage/tools/spti/spti.sln b/storage/tools/spti/spti.sln index 81ef36113..38b869ffd 100644 --- a/storage/tools/spti/spti.sln +++ b/storage/tools/spti/spti.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0 MinimumVisualStudioVersion = 12.0 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "spti", "src\spti.vcxproj", "{06D7097B-3EC8-4A0F-B1B4-3281A547FF8E}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "spti", "src\spti.vcxproj", "{D4F0486B-D89B-4BD7-9050-D75E28882A57}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {06D7097B-3EC8-4A0F-B1B4-3281A547FF8E}.Debug|Win32.ActiveCfg = Debug|Win32 - {06D7097B-3EC8-4A0F-B1B4-3281A547FF8E}.Debug|Win32.Build.0 = Debug|Win32 - {06D7097B-3EC8-4A0F-B1B4-3281A547FF8E}.Release|Win32.ActiveCfg = Release|Win32 - {06D7097B-3EC8-4A0F-B1B4-3281A547FF8E}.Release|Win32.Build.0 = Release|Win32 - {06D7097B-3EC8-4A0F-B1B4-3281A547FF8E}.Debug|x64.ActiveCfg = Debug|x64 - {06D7097B-3EC8-4A0F-B1B4-3281A547FF8E}.Debug|x64.Build.0 = Debug|x64 - {06D7097B-3EC8-4A0F-B1B4-3281A547FF8E}.Release|x64.ActiveCfg = Release|x64 - {06D7097B-3EC8-4A0F-B1B4-3281A547FF8E}.Release|x64.Build.0 = Release|x64 + {D4F0486B-D89B-4BD7-9050-D75E28882A57}.Debug|Win32.ActiveCfg = Debug|Win32 + {D4F0486B-D89B-4BD7-9050-D75E28882A57}.Debug|Win32.Build.0 = Debug|Win32 + {D4F0486B-D89B-4BD7-9050-D75E28882A57}.Release|Win32.ActiveCfg = Release|Win32 + {D4F0486B-D89B-4BD7-9050-D75E28882A57}.Release|Win32.Build.0 = Release|Win32 + {D4F0486B-D89B-4BD7-9050-D75E28882A57}.Debug|x64.ActiveCfg = Debug|x64 + {D4F0486B-D89B-4BD7-9050-D75E28882A57}.Debug|x64.Build.0 = Debug|x64 + {D4F0486B-D89B-4BD7-9050-D75E28882A57}.Release|x64.ActiveCfg = Release|x64 + {D4F0486B-D89B-4BD7-9050-D75E28882A57}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/storage/tools/spti/src/spti.vcxproj b/storage/tools/spti/src/spti.vcxproj index 83ca72019..01ae43d88 100644 --- a/storage/tools/spti/src/spti.vcxproj +++ b/storage/tools/spti/src/spti.vcxproj @@ -19,11 +19,11 @@ - {06D7097B-3EC8-4A0F-B1B4-3281A547FF8E} + {40B0D7DE-6A79-41C5-A355-A999BB42A951} $(MSBuildProjectName) Debug Win32 - {3808D033-40D7-4C7A-8DA1-D7E82C8FD640} + {7002EA4E-B7BB-4431-A747-8D770F3AD59B} @@ -153,7 +153,6 @@ - diff --git a/storage/tools/spti/src/spti.vcxproj.Filters b/storage/tools/spti/src/spti.vcxproj.Filters index c33a7ead6..43ad38336 100644 --- a/storage/tools/spti/src/spti.vcxproj.Filters +++ b/storage/tools/spti/src/spti.vcxproj.Filters @@ -3,15 +3,15 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {04D905C4-521E-4922-A82E-9166E240F4AE} + {BB423131-A93F-46FB-BA43-CF499EC9CC89} h;hpp;hxx;hm;inl;inc;xsd - {9101E840-E8E3-427D-8033-C66E6D748653} + {C6BB7E16-A4A0-4C56-ACCF-284379723BB1} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {023F3BB8-A899-4718-AD48-F2A263DC147C} + {65D2BE7B-77DA-4A13-9FF9-664CD8AAF907} diff --git a/thermal/simsensor/simsensor.inf b/thermal/simsensor/simsensor.inf index 8ba9722d6..dad037e10 100644 --- a/thermal/simsensor/simsensor.inf +++ b/thermal/simsensor/simsensor.inf @@ -16,7 +16,7 @@ Signature="$WINDOWS NT$" Class=System ClassGuid={4D36E97D-E325-11CE-BFC1-08002BE10318} -Provider=%MSFT% +Provider=%ProviderString% DriverVer=09/02/2011, 6.02.8105 CatalogFile=simsensor.cat @@ -99,7 +99,7 @@ Default = {381B4222-F694-41F0-9685-FF5BB260DF2E}, 1, 1 [Strings] SPSVCINST_ASSOCSERVICE= 0x00000002 -MSFT = "Microsoft" +ProviderString = "TODO-Set-Provider" StdMfg = "(Standard system devices)" DiskId1 = "Simulate Sensor Installation Disk #1" simsensor.DeviceDesc = "Simulated Sensor" diff --git a/thermal/simsensor/simsensor.sln b/thermal/simsensor/simsensor.sln index 734db11cc..8ff4936ef 100644 --- a/thermal/simsensor/simsensor.sln +++ b/thermal/simsensor/simsensor.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0 MinimumVisualStudioVersion = 12.0 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "simsensor", "simsensor.vcxproj", "{C59FD02E-3C7E-4DC3-94C7-7E04827E0934}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "simsensor", "simsensor.vcxproj", "{92D79466-CE11-417A-9142-08711CDC46CA}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {C59FD02E-3C7E-4DC3-94C7-7E04827E0934}.Debug|Win32.ActiveCfg = Debug|Win32 - {C59FD02E-3C7E-4DC3-94C7-7E04827E0934}.Debug|Win32.Build.0 = Debug|Win32 - {C59FD02E-3C7E-4DC3-94C7-7E04827E0934}.Release|Win32.ActiveCfg = Release|Win32 - {C59FD02E-3C7E-4DC3-94C7-7E04827E0934}.Release|Win32.Build.0 = Release|Win32 - {C59FD02E-3C7E-4DC3-94C7-7E04827E0934}.Debug|x64.ActiveCfg = Debug|x64 - {C59FD02E-3C7E-4DC3-94C7-7E04827E0934}.Debug|x64.Build.0 = Debug|x64 - {C59FD02E-3C7E-4DC3-94C7-7E04827E0934}.Release|x64.ActiveCfg = Release|x64 - {C59FD02E-3C7E-4DC3-94C7-7E04827E0934}.Release|x64.Build.0 = Release|x64 + {92D79466-CE11-417A-9142-08711CDC46CA}.Debug|Win32.ActiveCfg = Debug|Win32 + {92D79466-CE11-417A-9142-08711CDC46CA}.Debug|Win32.Build.0 = Debug|Win32 + {92D79466-CE11-417A-9142-08711CDC46CA}.Release|Win32.ActiveCfg = Release|Win32 + {92D79466-CE11-417A-9142-08711CDC46CA}.Release|Win32.Build.0 = Release|Win32 + {92D79466-CE11-417A-9142-08711CDC46CA}.Debug|x64.ActiveCfg = Debug|x64 + {92D79466-CE11-417A-9142-08711CDC46CA}.Debug|x64.Build.0 = Debug|x64 + {92D79466-CE11-417A-9142-08711CDC46CA}.Release|x64.ActiveCfg = Release|x64 + {92D79466-CE11-417A-9142-08711CDC46CA}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/thermal/simsensor/simsensor.vcxproj b/thermal/simsensor/simsensor.vcxproj index fc01a5806..b52284d54 100644 --- a/thermal/simsensor/simsensor.vcxproj +++ b/thermal/simsensor/simsensor.vcxproj @@ -19,12 +19,12 @@ - {C59FD02E-3C7E-4DC3-94C7-7E04827E0934} + {92D79466-CE11-417A-9142-08711CDC46CA} $(MSBuildProjectName) 1 Debug Win32 - {614E84B8-6A38-4F03-93D6-E41FEA710542} + {49B64D1C-D106-47F8-AEC5-499B090D53D3} @@ -167,7 +167,6 @@ - diff --git a/thermal/simsensor/simsensor.vcxproj.Filters b/thermal/simsensor/simsensor.vcxproj.Filters index 135dc0246..09f6d30b4 100644 --- a/thermal/simsensor/simsensor.vcxproj.Filters +++ b/thermal/simsensor/simsensor.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {C3D02E88-9BD3-4979-BB31-99764D1517A9} + {2B24E521-83A9-4A37-85BA-E860A36A1B51} h;hpp;hxx;hm;inl;inc;xsd - {69FA9ACE-C62E-4ABA-A9A2-9F2C43CCDD50} + {8FB448E3-6D96-4E76-AC71-EFC10A620CE1} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {17C8F8C4-E1A8-404B-BEB5-1D12BD37AC27} + {C1B69A40-A8D2-4AA1-AE53-159FC7579CC4} inf;inv;inx;mof;mc; - {9DB4A799-2FC4-47D4-A5BA-F2395218E4AE} + {BAEA26B6-5151-4D81-8F72-E066C8494F2D} diff --git a/thermal/thermalclient/simtc.inf b/thermal/thermalclient/simtc.inf index c2cfe2b3e..74148cadb 100644 --- a/thermal/thermalclient/simtc.inf +++ b/thermal/thermalclient/simtc.inf @@ -16,7 +16,7 @@ Signature="$WINDOWS NT$" Class=System ClassGuid={4D36E97D-E325-11CE-BFC1-08002BE10318} -Provider=%MSFT% +Provider=%ProviderString% DriverVer=07/22/2011, 6.02.8040 CatalogFile=thermalclient.cat @@ -80,7 +80,7 @@ ServiceBinary = %12%\simtc.sys [Strings] SPSVCINST_ASSOCSERVICE= 0x00000002 -MSFT = "Microsoft" +ProviderString = "TODO-Set-Provider" StdMfg = "(Standard system devices)" DiskId1 = "Simulate Thermal Client Installation Disk #1" SimTc.DeviceDesc = "Simulated Thermal Client" diff --git a/thermal/thermalclient/simtc.vcxproj b/thermal/thermalclient/simtc.vcxproj index 23b7b7356..562d52241 100644 --- a/thermal/thermalclient/simtc.vcxproj +++ b/thermal/thermalclient/simtc.vcxproj @@ -19,12 +19,12 @@ - {8C2AD3B6-D9D6-4ECA-8D92-A714B4046119} + {673CCCD9-54E7-413B-AEA1-F237E1A270B3} $(MSBuildProjectName) 1 Debug Win32 - {96B796D8-F417-486F-8B8A-C1BA76C20398} + {47D4AB7E-D100-49F1-A993-7B71D1F1DDFF} @@ -167,7 +167,6 @@ - diff --git a/thermal/thermalclient/simtc.vcxproj.Filters b/thermal/thermalclient/simtc.vcxproj.Filters index 47ec9236e..c34b549a9 100644 --- a/thermal/thermalclient/simtc.vcxproj.Filters +++ b/thermal/thermalclient/simtc.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {3F670431-8219-4202-9710-75D4686C3657} + {F22E3BDF-A139-4606-AC2F-903826B537D4} h;hpp;hxx;hm;inl;inc;xsd - {8DE58803-401C-49AD-A147-C3F70346A0DB} + {82721B5B-1E5E-42EF-AFDB-047A5C91A45A} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {964EADB1-B2FC-4397-A2F0-B4C72031CC67} + {AD4AD608-3ACF-409E-BDC3-880416ECB637} inf;inv;inx;mof;mc; - {0C7838AB-73FB-4DBB-B61F-2BAEF26FB05E} + {3D8249F0-7C5E-4714-A0E5-478DAE422E67} diff --git a/thermal/thermalclient/thermalclient.sln b/thermal/thermalclient/thermalclient.sln index 6415ed751..e6be8e1a9 100644 --- a/thermal/thermalclient/thermalclient.sln +++ b/thermal/thermalclient/thermalclient.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0 MinimumVisualStudioVersion = 12.0 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "simtc", "simtc.vcxproj", "{8C2AD3B6-D9D6-4ECA-8D92-A714B4046119}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "simtc", "simtc.vcxproj", "{673CCCD9-54E7-413B-AEA1-F237E1A270B3}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {8C2AD3B6-D9D6-4ECA-8D92-A714B4046119}.Debug|Win32.ActiveCfg = Debug|Win32 - {8C2AD3B6-D9D6-4ECA-8D92-A714B4046119}.Debug|Win32.Build.0 = Debug|Win32 - {8C2AD3B6-D9D6-4ECA-8D92-A714B4046119}.Release|Win32.ActiveCfg = Release|Win32 - {8C2AD3B6-D9D6-4ECA-8D92-A714B4046119}.Release|Win32.Build.0 = Release|Win32 - {8C2AD3B6-D9D6-4ECA-8D92-A714B4046119}.Debug|x64.ActiveCfg = Debug|x64 - {8C2AD3B6-D9D6-4ECA-8D92-A714B4046119}.Debug|x64.Build.0 = Debug|x64 - {8C2AD3B6-D9D6-4ECA-8D92-A714B4046119}.Release|x64.ActiveCfg = Release|x64 - {8C2AD3B6-D9D6-4ECA-8D92-A714B4046119}.Release|x64.Build.0 = Release|x64 + {673CCCD9-54E7-413B-AEA1-F237E1A270B3}.Debug|Win32.ActiveCfg = Debug|Win32 + {673CCCD9-54E7-413B-AEA1-F237E1A270B3}.Debug|Win32.Build.0 = Debug|Win32 + {673CCCD9-54E7-413B-AEA1-F237E1A270B3}.Release|Win32.ActiveCfg = Release|Win32 + {673CCCD9-54E7-413B-AEA1-F237E1A270B3}.Release|Win32.Build.0 = Release|Win32 + {673CCCD9-54E7-413B-AEA1-F237E1A270B3}.Debug|x64.ActiveCfg = Debug|x64 + {673CCCD9-54E7-413B-AEA1-F237E1A270B3}.Debug|x64.Build.0 = Debug|x64 + {673CCCD9-54E7-413B-AEA1-F237E1A270B3}.Release|x64.ActiveCfg = Release|x64 + {673CCCD9-54E7-413B-AEA1-F237E1A270B3}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/tools/sdv/samples/SDV-FailDriver-KMDF/SDV-FailDriver-KMDF.sln b/tools/sdv/samples/SDV-FailDriver-KMDF/SDV-FailDriver-KMDF.sln index f5a65853e..4f1ca485c 100644 --- a/tools/sdv/samples/SDV-FailDriver-KMDF/SDV-FailDriver-KMDF.sln +++ b/tools/sdv/samples/SDV-FailDriver-KMDF/SDV-FailDriver-KMDF.sln @@ -3,15 +3,15 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0 MinimumVisualStudioVersion = 12.0 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Library", "Library", "{162D69A5-1E20-46D9-A76A-F8C13814A72B}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Library", "Library", "{2F812672-CC8D-46A7-9F17-BE94F1A3057C}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Driver", "Driver", "{F2A70EFC-5B8A-48F0-A850-A10B7F1BF6CD}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Driver", "Driver", "{8A48F8B0-2493-4599-A47C-8B71571F57B7}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fail_library1", "library\fail_library1.vcxproj", "{94A7D7B0-5847-4D82-AEF8-DEBB9791264D}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fail_library1", "library\fail_library1.vcxproj", "{461FB63C-938B-43CE-9796-2505F37E8724}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fail_driver1", "driver\fail_driver1.vcxproj", "{C7B6DCCE-A00A-4FFE-943C-BE627739F557}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fail_driver1", "driver\fail_driver1.vcxproj", "{E9A28DB1-2600-42AD-BF1C-7A23223C3D40}" ProjectSection(ProjectDependencies) = postProject - {94A7D7B0-5847-4D82-AEF8-DEBB9791264D} = {94A7D7B0-5847-4D82-AEF8-DEBB9791264D} + {461FB63C-938B-43CE-9796-2505F37E8724} = {461FB63C-938B-43CE-9796-2505F37E8724} EndProjectSection EndProject Global @@ -22,28 +22,28 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {94A7D7B0-5847-4D82-AEF8-DEBB9791264D}.Debug|Win32.ActiveCfg = Debug|Win32 - {94A7D7B0-5847-4D82-AEF8-DEBB9791264D}.Debug|Win32.Build.0 = Debug|Win32 - {94A7D7B0-5847-4D82-AEF8-DEBB9791264D}.Release|Win32.ActiveCfg = Release|Win32 - {94A7D7B0-5847-4D82-AEF8-DEBB9791264D}.Release|Win32.Build.0 = Release|Win32 - {94A7D7B0-5847-4D82-AEF8-DEBB9791264D}.Debug|x64.ActiveCfg = Debug|x64 - {94A7D7B0-5847-4D82-AEF8-DEBB9791264D}.Debug|x64.Build.0 = Debug|x64 - {94A7D7B0-5847-4D82-AEF8-DEBB9791264D}.Release|x64.ActiveCfg = Release|x64 - {94A7D7B0-5847-4D82-AEF8-DEBB9791264D}.Release|x64.Build.0 = Release|x64 - {C7B6DCCE-A00A-4FFE-943C-BE627739F557}.Debug|Win32.ActiveCfg = Debug|Win32 - {C7B6DCCE-A00A-4FFE-943C-BE627739F557}.Debug|Win32.Build.0 = Debug|Win32 - {C7B6DCCE-A00A-4FFE-943C-BE627739F557}.Release|Win32.ActiveCfg = Release|Win32 - {C7B6DCCE-A00A-4FFE-943C-BE627739F557}.Release|Win32.Build.0 = Release|Win32 - {C7B6DCCE-A00A-4FFE-943C-BE627739F557}.Debug|x64.ActiveCfg = Debug|x64 - {C7B6DCCE-A00A-4FFE-943C-BE627739F557}.Debug|x64.Build.0 = Debug|x64 - {C7B6DCCE-A00A-4FFE-943C-BE627739F557}.Release|x64.ActiveCfg = Release|x64 - {C7B6DCCE-A00A-4FFE-943C-BE627739F557}.Release|x64.Build.0 = Release|x64 + {461FB63C-938B-43CE-9796-2505F37E8724}.Debug|Win32.ActiveCfg = Debug|Win32 + {461FB63C-938B-43CE-9796-2505F37E8724}.Debug|Win32.Build.0 = Debug|Win32 + {461FB63C-938B-43CE-9796-2505F37E8724}.Release|Win32.ActiveCfg = Release|Win32 + {461FB63C-938B-43CE-9796-2505F37E8724}.Release|Win32.Build.0 = Release|Win32 + {461FB63C-938B-43CE-9796-2505F37E8724}.Debug|x64.ActiveCfg = Debug|x64 + {461FB63C-938B-43CE-9796-2505F37E8724}.Debug|x64.Build.0 = Debug|x64 + {461FB63C-938B-43CE-9796-2505F37E8724}.Release|x64.ActiveCfg = Release|x64 + {461FB63C-938B-43CE-9796-2505F37E8724}.Release|x64.Build.0 = Release|x64 + {E9A28DB1-2600-42AD-BF1C-7A23223C3D40}.Debug|Win32.ActiveCfg = Debug|Win32 + {E9A28DB1-2600-42AD-BF1C-7A23223C3D40}.Debug|Win32.Build.0 = Debug|Win32 + {E9A28DB1-2600-42AD-BF1C-7A23223C3D40}.Release|Win32.ActiveCfg = Release|Win32 + {E9A28DB1-2600-42AD-BF1C-7A23223C3D40}.Release|Win32.Build.0 = Release|Win32 + {E9A28DB1-2600-42AD-BF1C-7A23223C3D40}.Debug|x64.ActiveCfg = Debug|x64 + {E9A28DB1-2600-42AD-BF1C-7A23223C3D40}.Debug|x64.Build.0 = Debug|x64 + {E9A28DB1-2600-42AD-BF1C-7A23223C3D40}.Release|x64.ActiveCfg = Release|x64 + {E9A28DB1-2600-42AD-BF1C-7A23223C3D40}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {94A7D7B0-5847-4D82-AEF8-DEBB9791264D} = {162D69A5-1E20-46D9-A76A-F8C13814A72B} - {C7B6DCCE-A00A-4FFE-943C-BE627739F557} = {F2A70EFC-5B8A-48F0-A850-A10B7F1BF6CD} + {461FB63C-938B-43CE-9796-2505F37E8724} = {2F812672-CC8D-46A7-9F17-BE94F1A3057C} + {E9A28DB1-2600-42AD-BF1C-7A23223C3D40} = {8A48F8B0-2493-4599-A47C-8B71571F57B7} EndGlobalSection EndGlobal diff --git a/tools/sdv/samples/SDV-FailDriver-KMDF/driver/fail_driver1.vcxproj b/tools/sdv/samples/SDV-FailDriver-KMDF/driver/fail_driver1.vcxproj index 7a50e41ad..ce32e45a8 100644 --- a/tools/sdv/samples/SDV-FailDriver-KMDF/driver/fail_driver1.vcxproj +++ b/tools/sdv/samples/SDV-FailDriver-KMDF/driver/fail_driver1.vcxproj @@ -19,13 +19,13 @@ - {C7B6DCCE-A00A-4FFE-943C-BE627739F557} + {E9A28DB1-2600-42AD-BF1C-7A23223C3D40} $(MSBuildProjectName) 1 false Debug Win32 - {6966CAEE-CE04-4D25-9875-4AEF3D8996DA} + {F91F2992-C128-4959-BE63-E59F6C8D44D8} @@ -167,7 +167,6 @@ - diff --git a/tools/sdv/samples/SDV-FailDriver-KMDF/driver/fail_driver1.vcxproj.Filters b/tools/sdv/samples/SDV-FailDriver-KMDF/driver/fail_driver1.vcxproj.Filters index ae7c7e705..00a963fc5 100644 --- a/tools/sdv/samples/SDV-FailDriver-KMDF/driver/fail_driver1.vcxproj.Filters +++ b/tools/sdv/samples/SDV-FailDriver-KMDF/driver/fail_driver1.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {A16A5FE6-FA81-4783-80D1-98C34A071F87} + {BDEC6820-D3F9-4DC1-87F5-BD0F935536B2} h;hpp;hxx;hm;inl;inc;xsd - {0FBD4010-4C7C-4257-90F4-072C980F0185} + {BDD5901A-4251-456A-816A-6C6FFADC38D5} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {A7C33620-384C-413F-B7D1-9730C933D08A} + {2218C1EA-513A-4DDF-9631-82EA8C353ADC} inf;inv;inx;mof;mc; - {0EFF4338-76DE-4363-99CA-F90B51F129C9} + {0D3B16A6-3CC7-465B-A266-F00AC3407943} diff --git a/tools/sdv/samples/SDV-FailDriver-KMDF/library/fail_library1.vcxproj b/tools/sdv/samples/SDV-FailDriver-KMDF/library/fail_library1.vcxproj index c54c77f0f..545e0d15a 100644 --- a/tools/sdv/samples/SDV-FailDriver-KMDF/library/fail_library1.vcxproj +++ b/tools/sdv/samples/SDV-FailDriver-KMDF/library/fail_library1.vcxproj @@ -19,13 +19,13 @@ - {94A7D7B0-5847-4D82-AEF8-DEBB9791264D} + {461FB63C-938B-43CE-9796-2505F37E8724} $(MSBuildProjectName) 1 false Debug Win32 - {931CA2A4-0AE4-4271-8F6F-3A1F1621CBAA} + {CBE71F3B-63FA-4DE1-9FAC-0F7C113325EC} @@ -127,7 +127,6 @@ - diff --git a/tools/sdv/samples/SDV-FailDriver-KMDF/library/fail_library1.vcxproj.Filters b/tools/sdv/samples/SDV-FailDriver-KMDF/library/fail_library1.vcxproj.Filters index 096701f39..ce47299f8 100644 --- a/tools/sdv/samples/SDV-FailDriver-KMDF/library/fail_library1.vcxproj.Filters +++ b/tools/sdv/samples/SDV-FailDriver-KMDF/library/fail_library1.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {D7579C99-00DB-4864-84F1-FC06F14F5684} + {6FE3B3C4-F73A-4549-9F90-E44F01741516} h;hpp;hxx;hm;inl;inc;xsd - {EAF05F4C-8A24-401B-B31A-8E45BB55A755} + {8C547F1D-BE78-45EA-94A8-5F705073A926} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {4A5E0201-5E51-4E94-9931-91EC0A7162CF} + {F1C2EE91-02D0-4115-99E6-CF2EC17F8366} inf;inv;inx;mof;mc; - {82A19C94-017E-4038-8319-B8E0A6A6565C} + {4CFEF03D-648E-4550-B830-4951C798107A} diff --git a/tools/sdv/samples/SDV-FailDriver-NDIS/SDV-FailDriver-NDIS.sln b/tools/sdv/samples/SDV-FailDriver-NDIS/SDV-FailDriver-NDIS.sln index c82285622..b1cee3961 100644 --- a/tools/sdv/samples/SDV-FailDriver-NDIS/SDV-FailDriver-NDIS.sln +++ b/tools/sdv/samples/SDV-FailDriver-NDIS/SDV-FailDriver-NDIS.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0 MinimumVisualStudioVersion = 12.0 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sdvmp", "driver\sdvmp.vcxproj", "{9FB0FC7C-E49D-4662-8A3C-F9E100FE597F}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sdvmp", "driver\sdvmp.vcxproj", "{E6EA3861-2C59-4623-9824-237579779DA0}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {9FB0FC7C-E49D-4662-8A3C-F9E100FE597F}.Debug|Win32.ActiveCfg = Debug|Win32 - {9FB0FC7C-E49D-4662-8A3C-F9E100FE597F}.Debug|Win32.Build.0 = Debug|Win32 - {9FB0FC7C-E49D-4662-8A3C-F9E100FE597F}.Release|Win32.ActiveCfg = Release|Win32 - {9FB0FC7C-E49D-4662-8A3C-F9E100FE597F}.Release|Win32.Build.0 = Release|Win32 - {9FB0FC7C-E49D-4662-8A3C-F9E100FE597F}.Debug|x64.ActiveCfg = Debug|x64 - {9FB0FC7C-E49D-4662-8A3C-F9E100FE597F}.Debug|x64.Build.0 = Debug|x64 - {9FB0FC7C-E49D-4662-8A3C-F9E100FE597F}.Release|x64.ActiveCfg = Release|x64 - {9FB0FC7C-E49D-4662-8A3C-F9E100FE597F}.Release|x64.Build.0 = Release|x64 + {E6EA3861-2C59-4623-9824-237579779DA0}.Debug|Win32.ActiveCfg = Debug|Win32 + {E6EA3861-2C59-4623-9824-237579779DA0}.Debug|Win32.Build.0 = Debug|Win32 + {E6EA3861-2C59-4623-9824-237579779DA0}.Release|Win32.ActiveCfg = Release|Win32 + {E6EA3861-2C59-4623-9824-237579779DA0}.Release|Win32.Build.0 = Release|Win32 + {E6EA3861-2C59-4623-9824-237579779DA0}.Debug|x64.ActiveCfg = Debug|x64 + {E6EA3861-2C59-4623-9824-237579779DA0}.Debug|x64.Build.0 = Debug|x64 + {E6EA3861-2C59-4623-9824-237579779DA0}.Release|x64.ActiveCfg = Release|x64 + {E6EA3861-2C59-4623-9824-237579779DA0}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/tools/sdv/samples/SDV-FailDriver-NDIS/driver/sdvmp.vcxproj b/tools/sdv/samples/SDV-FailDriver-NDIS/driver/sdvmp.vcxproj index 9173fb0d1..efc2caa81 100644 --- a/tools/sdv/samples/SDV-FailDriver-NDIS/driver/sdvmp.vcxproj +++ b/tools/sdv/samples/SDV-FailDriver-NDIS/driver/sdvmp.vcxproj @@ -19,12 +19,12 @@ - {9FB0FC7C-E49D-4662-8A3C-F9E100FE597F} + {E6EA3861-2C59-4623-9824-237579779DA0} $(MSBuildProjectName) false Debug Win32 - {238E1F6A-833F-4C08-A6FF-81E5DE0D9CC4} + {3FA5E028-F8C7-4610-BA5A-6B04A17DEDE5} @@ -171,7 +171,6 @@ - diff --git a/tools/sdv/samples/SDV-FailDriver-NDIS/driver/sdvmp.vcxproj.Filters b/tools/sdv/samples/SDV-FailDriver-NDIS/driver/sdvmp.vcxproj.Filters index 3a34320b6..ada8b238a 100644 --- a/tools/sdv/samples/SDV-FailDriver-NDIS/driver/sdvmp.vcxproj.Filters +++ b/tools/sdv/samples/SDV-FailDriver-NDIS/driver/sdvmp.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {2CE179FB-8222-49D1-978A-A2BA05CDA7B7} + {1E135777-CFFB-4A1C-B464-B85D375B7315} h;hpp;hxx;hm;inl;inc;xsd - {2610987D-58AB-4BE0-83E8-156BFD45ED70} + {EB501283-B000-4709-88B1-A9F7DCA45ECD} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {36AA2952-8F33-4E30-B29A-A7963DC4FA02} + {9583582B-8D53-434E-9D38-89394374499D} inf;inv;inx;mof;mc; - {507BF12B-8672-4B6E-ACD4-BC295EDF6F2E} + {C8339997-9DEA-40D9-9FD2-FB8D037EB104} diff --git a/tools/sdv/samples/SDV-FailDriver-STORPORT/SDV-FailDriver-STORPORT.sln b/tools/sdv/samples/SDV-FailDriver-STORPORT/SDV-FailDriver-STORPORT.sln index d85d275a6..66e73619e 100644 --- a/tools/sdv/samples/SDV-FailDriver-STORPORT/SDV-FailDriver-STORPORT.sln +++ b/tools/sdv/samples/SDV-FailDriver-STORPORT/SDV-FailDriver-STORPORT.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0 MinimumVisualStudioVersion = 12.0 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lsi_u3", "driver\lsi_u3.vcxproj", "{94AE0D66-FB0C-45A1-BC8D-D3EB0BB5AF5C}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lsi_u3", "driver\lsi_u3.vcxproj", "{61D9DA2C-F833-42D8-8389-09DE73184BF7}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {94AE0D66-FB0C-45A1-BC8D-D3EB0BB5AF5C}.Debug|Win32.ActiveCfg = Debug|Win32 - {94AE0D66-FB0C-45A1-BC8D-D3EB0BB5AF5C}.Debug|Win32.Build.0 = Debug|Win32 - {94AE0D66-FB0C-45A1-BC8D-D3EB0BB5AF5C}.Release|Win32.ActiveCfg = Release|Win32 - {94AE0D66-FB0C-45A1-BC8D-D3EB0BB5AF5C}.Release|Win32.Build.0 = Release|Win32 - {94AE0D66-FB0C-45A1-BC8D-D3EB0BB5AF5C}.Debug|x64.ActiveCfg = Debug|x64 - {94AE0D66-FB0C-45A1-BC8D-D3EB0BB5AF5C}.Debug|x64.Build.0 = Debug|x64 - {94AE0D66-FB0C-45A1-BC8D-D3EB0BB5AF5C}.Release|x64.ActiveCfg = Release|x64 - {94AE0D66-FB0C-45A1-BC8D-D3EB0BB5AF5C}.Release|x64.Build.0 = Release|x64 + {61D9DA2C-F833-42D8-8389-09DE73184BF7}.Debug|Win32.ActiveCfg = Debug|Win32 + {61D9DA2C-F833-42D8-8389-09DE73184BF7}.Debug|Win32.Build.0 = Debug|Win32 + {61D9DA2C-F833-42D8-8389-09DE73184BF7}.Release|Win32.ActiveCfg = Release|Win32 + {61D9DA2C-F833-42D8-8389-09DE73184BF7}.Release|Win32.Build.0 = Release|Win32 + {61D9DA2C-F833-42D8-8389-09DE73184BF7}.Debug|x64.ActiveCfg = Debug|x64 + {61D9DA2C-F833-42D8-8389-09DE73184BF7}.Debug|x64.Build.0 = Debug|x64 + {61D9DA2C-F833-42D8-8389-09DE73184BF7}.Release|x64.ActiveCfg = Release|x64 + {61D9DA2C-F833-42D8-8389-09DE73184BF7}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/tools/sdv/samples/SDV-FailDriver-STORPORT/driver/lsi_u3.vcxproj b/tools/sdv/samples/SDV-FailDriver-STORPORT/driver/lsi_u3.vcxproj index e05a57e68..e4880679e 100644 --- a/tools/sdv/samples/SDV-FailDriver-STORPORT/driver/lsi_u3.vcxproj +++ b/tools/sdv/samples/SDV-FailDriver-STORPORT/driver/lsi_u3.vcxproj @@ -19,12 +19,12 @@ - {94AE0D66-FB0C-45A1-BC8D-D3EB0BB5AF5C} + {61D9DA2C-F833-42D8-8389-09DE73184BF7} $(MSBuildProjectName) false Debug Win32 - {7B51C76A-5AAB-49D5-80F6-235C68235E20} + {C8BA9100-04CD-4D5E-BE47-7F4B5523B604} @@ -167,7 +167,6 @@ - diff --git a/tools/sdv/samples/SDV-FailDriver-STORPORT/driver/lsi_u3.vcxproj.Filters b/tools/sdv/samples/SDV-FailDriver-STORPORT/driver/lsi_u3.vcxproj.Filters index 7f8e0e779..6a1e0e236 100644 --- a/tools/sdv/samples/SDV-FailDriver-STORPORT/driver/lsi_u3.vcxproj.Filters +++ b/tools/sdv/samples/SDV-FailDriver-STORPORT/driver/lsi_u3.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {F6235CEF-D6BE-4998-91D7-878E40CBB400} + {1F5F254C-AB38-4FEC-8986-38F49451F32B} h;hpp;hxx;hm;inl;inc;xsd - {0D05F536-41BB-49DD-A1EF-0559AD4C6581} + {FD66C234-8B1A-42F4-B9F1-8E9395C682B1} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {6A3C8712-80E5-4CB3-A94F-069CCCED2AA9} + {C5FB1C38-51A7-4F32-977E-62A377CE9638} inf;inv;inx;mof;mc; - {5593B524-AFE3-42DE-B210-850E716AB626} + {42A467BB-9477-4CAD-9A6C-1819986F8E21} diff --git a/tools/sdv/samples/SDV-FailDriver-WDM/SDV-FailDriver-WDM.sln b/tools/sdv/samples/SDV-FailDriver-WDM/SDV-FailDriver-WDM.sln index 32a332750..67ea0c49c 100644 --- a/tools/sdv/samples/SDV-FailDriver-WDM/SDV-FailDriver-WDM.sln +++ b/tools/sdv/samples/SDV-FailDriver-WDM/SDV-FailDriver-WDM.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0 MinimumVisualStudioVersion = 12.0 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fail_driver1", "driver\fail_driver1.vcxproj", "{00B50311-24EF-44CE-B29E-A1A308EB2649}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fail_driver1", "driver\fail_driver1.vcxproj", "{653F3506-0E37-443B-ACF8-4DB2E50EAE27}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {00B50311-24EF-44CE-B29E-A1A308EB2649}.Debug|Win32.ActiveCfg = Debug|Win32 - {00B50311-24EF-44CE-B29E-A1A308EB2649}.Debug|Win32.Build.0 = Debug|Win32 - {00B50311-24EF-44CE-B29E-A1A308EB2649}.Release|Win32.ActiveCfg = Release|Win32 - {00B50311-24EF-44CE-B29E-A1A308EB2649}.Release|Win32.Build.0 = Release|Win32 - {00B50311-24EF-44CE-B29E-A1A308EB2649}.Debug|x64.ActiveCfg = Debug|x64 - {00B50311-24EF-44CE-B29E-A1A308EB2649}.Debug|x64.Build.0 = Debug|x64 - {00B50311-24EF-44CE-B29E-A1A308EB2649}.Release|x64.ActiveCfg = Release|x64 - {00B50311-24EF-44CE-B29E-A1A308EB2649}.Release|x64.Build.0 = Release|x64 + {653F3506-0E37-443B-ACF8-4DB2E50EAE27}.Debug|Win32.ActiveCfg = Debug|Win32 + {653F3506-0E37-443B-ACF8-4DB2E50EAE27}.Debug|Win32.Build.0 = Debug|Win32 + {653F3506-0E37-443B-ACF8-4DB2E50EAE27}.Release|Win32.ActiveCfg = Release|Win32 + {653F3506-0E37-443B-ACF8-4DB2E50EAE27}.Release|Win32.Build.0 = Release|Win32 + {653F3506-0E37-443B-ACF8-4DB2E50EAE27}.Debug|x64.ActiveCfg = Debug|x64 + {653F3506-0E37-443B-ACF8-4DB2E50EAE27}.Debug|x64.Build.0 = Debug|x64 + {653F3506-0E37-443B-ACF8-4DB2E50EAE27}.Release|x64.ActiveCfg = Release|x64 + {653F3506-0E37-443B-ACF8-4DB2E50EAE27}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/tools/sdv/samples/SDV-FailDriver-WDM/driver/fail_driver1.vcxproj b/tools/sdv/samples/SDV-FailDriver-WDM/driver/fail_driver1.vcxproj index 21695ceb2..3c347d358 100644 --- a/tools/sdv/samples/SDV-FailDriver-WDM/driver/fail_driver1.vcxproj +++ b/tools/sdv/samples/SDV-FailDriver-WDM/driver/fail_driver1.vcxproj @@ -19,12 +19,12 @@ - {00B50311-24EF-44CE-B29E-A1A308EB2649} + {653F3506-0E37-443B-ACF8-4DB2E50EAE27} $(MSBuildProjectName) false Debug Win32 - {4FA3F9BE-9E15-4FA0-8518-AF89D4CDD6A6} + {59613085-21C3-4A10-923E-5216AB8A902F} @@ -142,7 +142,6 @@ - diff --git a/tools/sdv/samples/SDV-FailDriver-WDM/driver/fail_driver1.vcxproj.Filters b/tools/sdv/samples/SDV-FailDriver-WDM/driver/fail_driver1.vcxproj.Filters index e8ec8ea60..e0977ad57 100644 --- a/tools/sdv/samples/SDV-FailDriver-WDM/driver/fail_driver1.vcxproj.Filters +++ b/tools/sdv/samples/SDV-FailDriver-WDM/driver/fail_driver1.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {1D87B41B-F73A-4066-8327-6374BF51EFF4} + {F688E2BB-177F-4A7D-81B1-029AA44F4726} h;hpp;hxx;hm;inl;inc;xsd - {D83503F2-ED45-43A1-AF56-C95A9F884C09} + {BD8BED2B-18A9-47E9-BD65-AAEEF4CBEBE2} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {6C268FC9-0E60-4E55-978E-3DF74D73636F} + {F06FCDF6-5892-465F-8164-24C88B27DFD3} inf;inv;inx;mof;mc; - {4FC4D076-182A-4141-BDCF-91295FD9D821} + {28A8F639-8487-4DD4-B532-CBBAA8B25661} diff --git a/usb/kmdf_enumswitches/kmdf_enumswitches.sln b/usb/kmdf_enumswitches/kmdf_enumswitches.sln index 241354f47..32b903cb3 100644 --- a/usb/kmdf_enumswitches/kmdf_enumswitches.sln +++ b/usb/kmdf_enumswitches/kmdf_enumswitches.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0 MinimumVisualStudioVersion = 12.0 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "kmdf_enumswitches", "sys\kmdf_enumswitches.vcxproj", "{2EE39DF5-47CC-427F-8C78-B7C1D9C3EF45}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "kmdf_enumswitches", "sys\kmdf_enumswitches.vcxproj", "{CE94DCE1-20AE-4D06-BFA9-13E9D92D339A}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {2EE39DF5-47CC-427F-8C78-B7C1D9C3EF45}.Debug|Win32.ActiveCfg = Debug|Win32 - {2EE39DF5-47CC-427F-8C78-B7C1D9C3EF45}.Debug|Win32.Build.0 = Debug|Win32 - {2EE39DF5-47CC-427F-8C78-B7C1D9C3EF45}.Release|Win32.ActiveCfg = Release|Win32 - {2EE39DF5-47CC-427F-8C78-B7C1D9C3EF45}.Release|Win32.Build.0 = Release|Win32 - {2EE39DF5-47CC-427F-8C78-B7C1D9C3EF45}.Debug|x64.ActiveCfg = Debug|x64 - {2EE39DF5-47CC-427F-8C78-B7C1D9C3EF45}.Debug|x64.Build.0 = Debug|x64 - {2EE39DF5-47CC-427F-8C78-B7C1D9C3EF45}.Release|x64.ActiveCfg = Release|x64 - {2EE39DF5-47CC-427F-8C78-B7C1D9C3EF45}.Release|x64.Build.0 = Release|x64 + {CE94DCE1-20AE-4D06-BFA9-13E9D92D339A}.Debug|Win32.ActiveCfg = Debug|Win32 + {CE94DCE1-20AE-4D06-BFA9-13E9D92D339A}.Debug|Win32.Build.0 = Debug|Win32 + {CE94DCE1-20AE-4D06-BFA9-13E9D92D339A}.Release|Win32.ActiveCfg = Release|Win32 + {CE94DCE1-20AE-4D06-BFA9-13E9D92D339A}.Release|Win32.Build.0 = Release|Win32 + {CE94DCE1-20AE-4D06-BFA9-13E9D92D339A}.Debug|x64.ActiveCfg = Debug|x64 + {CE94DCE1-20AE-4D06-BFA9-13E9D92D339A}.Debug|x64.Build.0 = Debug|x64 + {CE94DCE1-20AE-4D06-BFA9-13E9D92D339A}.Release|x64.ActiveCfg = Release|x64 + {CE94DCE1-20AE-4D06-BFA9-13E9D92D339A}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/usb/kmdf_enumswitches/sys/kmdf_enumswitches.inx b/usb/kmdf_enumswitches/sys/kmdf_enumswitches.inx index adab710ef..6ecf53ba8 100644 --- a/usb/kmdf_enumswitches/sys/kmdf_enumswitches.inx +++ b/usb/kmdf_enumswitches/sys/kmdf_enumswitches.inx @@ -20,7 +20,7 @@ Signature="$WINDOWS NT$" Class=Sample ClassGuid={78A1C341-4539-11d3-B88D-00C04FAD5171} -Provider=%MSFT% +Provider=%ProviderName% DriverVer=03/20/2003,5.00.3788 CatalogFile=KmdfSamples.cat @@ -38,9 +38,9 @@ HKR,,Icon,,-5 ; ================= Device section ===================== [Manufacturer] -%MfgName%=Microsoft,NT$ARCH$ +%MfgName%=OSR,NT$ARCH$ -[Microsoft.NT$ARCH$] +[OSR.NT$ARCH$] %USB\VID_045E&PID_930A.DeviceDesc%=kmdf_enumswitches.Dev, USB\VID_0547&PID_1002 %Switch.DeviceDesc%=Switch.Dev, {6FDE7521-1B65-48ae-B628-80BE62016026}\OsrUsbFxRawPdo @@ -102,7 +102,7 @@ KmdfLibraryVersion = $KMDFVERSION$ ;---------------------------------------------------------------; [Strings] -MSFT="Microsoft" +ProviderName="TODO-Set-Provider" MfgName="OSR" Disk_Description="OSRUSBFX2 Installation Disk" USB\VID_045E&PID_930A.DeviceDesc="WDF Sample Bus Driver for OSR USB-FX2 Learning Kit" diff --git a/usb/kmdf_enumswitches/sys/kmdf_enumswitches.vcxproj b/usb/kmdf_enumswitches/sys/kmdf_enumswitches.vcxproj index 6b2eac588..46f1b42ef 100644 --- a/usb/kmdf_enumswitches/sys/kmdf_enumswitches.vcxproj +++ b/usb/kmdf_enumswitches/sys/kmdf_enumswitches.vcxproj @@ -19,18 +19,18 @@ - {2EE39DF5-47CC-427F-8C78-B7C1D9C3EF45} + {96607BB4-3861-408E-B117-CAA8D0B6D372} $(MSBuildProjectName) 1 Debug Win32 - {629058BB-4E85-43D3-B519-FCEA0871305D} + {3017F5B6-A432-4BC6-982C-92EDBF88C56D} Windows10 False - Desktop + Universal KMDF WindowsKernelModeDriver10.0 Driver @@ -38,7 +38,7 @@ Windows10 True - Desktop + Universal KMDF WindowsKernelModeDriver10.0 Driver @@ -46,7 +46,7 @@ Windows10 False - Desktop + Universal KMDF WindowsKernelModeDriver10.0 Driver @@ -54,7 +54,7 @@ Windows10 True - Desktop + Universal KMDF WindowsKernelModeDriver10.0 Driver @@ -215,7 +215,6 @@ - diff --git a/usb/kmdf_enumswitches/sys/kmdf_enumswitches.vcxproj.Filters b/usb/kmdf_enumswitches/sys/kmdf_enumswitches.vcxproj.Filters index afc896f00..6d286cd38 100644 --- a/usb/kmdf_enumswitches/sys/kmdf_enumswitches.vcxproj.Filters +++ b/usb/kmdf_enumswitches/sys/kmdf_enumswitches.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {D1F8A3E1-FDAA-4BB1-9ED5-5C2237B070A1} + {51861499-AB32-435D-A5D8-467D00D1A71D} h;hpp;hxx;hm;inl;inc;xsd - {63723244-91AD-468B-B2D5-C9720A362381} + {407A2F5C-65A7-44BA-AF3B-6543832CDE3F} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {F64A4E05-C5D3-41CE-9B8E-4B303D852777} + {CDF3E397-8551-4B6E-BD1F-6E63053324D5} inf;inv;inx;mof;mc; - {ACD4D5C7-344D-4734-BF67-DF249474349A} + {59DF9552-63A9-4ABC-8DAF-9E8811DE79AE} @@ -33,9 +33,6 @@ - - Driver Files - Driver Files diff --git a/usb/kmdf_fx2/ReadMe.md b/usb/kmdf_fx2/ReadMe.md new file mode 100644 index 000000000..2e276ff07 --- /dev/null +++ b/usb/kmdf_fx2/ReadMe.md @@ -0,0 +1,393 @@ +Sample KMDF Function Driver for OSR USB-FX2 +=========================================== + +The kmdf\_fx2 sample is a Kernel-Mode Driver Framework (KMDF) driver for the OSR USB-FX2 device. It includes a test app and sample device metadata. + +In the Windows Driver Kit (WDK), the osrusbfx2 sample demonstrated how to perform bulk and interrupt data transfers to an USB device. The sample was written for the OSR USB-FX2 Learning Kit. + +The specification for the device is at . The driver and sample device metadata also work with the [Custom driver access](http://go.microsoft.com/fwlink/p/?LinkID=248288) sample. + +## Universal Windows Driver Compliant +This sample builds a Universal Windows Driver. It uses only APIs and DDIs that are included in OneCoreUAP. + + +Related topics +-------------- + +**** + +[umdf\_fx2](http://msdn.microsoft.com/en-us/library/windows/hardware/) + +[wdf\_osrfx2](http://msdn.microsoft.com/en-us/library/windows/hardware/) + + + +Build the sample +---------------- + +The default Solution build configuration is Windows 8.1 Debug and Win32. You can change the default configuration to build for Windows 7 or Windows Vista version of the operating system. + +**To select a configuration and build a driver** + +1. Open the driver project or solution in Visual Studio 2013 (find *filtername*.sln or *filtername*.vcxproj). +2. Right-click the solution in the **Solutions Explorer** and select **Configuration Manager**. +3. From the **Configuration Manager**, select the **Active Solution Configuration** (for example, Windows 8.1 Debug or Windows 8.1 Release) and the **Active Solution Platform** (for example, Win32) that correspond to the type of build you are interested in. +4. From the **Build** menu, click **Build Solution** (Ctrl+Shift+B). + +Overview +-------- + +Here is the overview of the device: + +- The device is based on the development board supplied with the Cypress EZ-USB FX2 Development Kit (CY3681). +- It contains 1 interface and 3 endpoints (Interrupt IN, Bulk Out, Bulk IN). +- Firmware supports vendor commands to query or set LED Bar graph display and 7-segment LED display, and to query toggle switch states. +- Interrupt endpoint: + - Sends an 8-bit value that represents the state of the switches. + - Sent on startup, resume from suspend, and whenever the switch pack setting changes. + - Firmware does not de-bounce the switch pack. + - One switch change can result in multiple bytes being sent. + - Bits are in the reverse order of the labels on the pack (for example, bit 0x80 is labeled 1 on the pack). +- Bulk endpoints are configured for loopback: + - The device moves data from IN endpoint to OUT endpoint. + - The device does not change the values of the data it receives nor does it internally create any data. + - Endpoints are always double buffered. + - Maximum packet size depends on speed (64 full speed, 512 high speed). +- Event Tracing for Windows (ETW) events: + - Included osrusbfx2.man, which describes events added. + - Three events are targeted to the event log: + - Failure during the add device routine. + - Failure to start the OSR device on a USB 1.1 controller. + - Invocation of the “re-enumerate device†IOCTL. + - Read/write start/stop events can be used to measure the time taken. + - For more information, see Unified Tracing later in this document. + +Sample Contents +--------------- + +Folder + +Description + +usb\\kmdf\_fx2\\driver + +This directory contains driver code that demonstrates the following functionality: + +- Loads the driver and responds to PnP and Power events. You can install, uninstall, disable, enable, suspend, and resume the system. +- Creates a context with the WDFDEVICE object. +- Initializes the USB device by registering a *EvtPrepareHardware* callback. +- Marks the interface restricted so that it can be accessed by a privileged Windows Store device app. +- Creates a default parallel queue to receive an IOCTL request to set bar graph display. +- Retrieves memory handle from the requests and uses it to send a vendor command to the USB device. +- Registers read and write events on the default queue. +- Retrieves memory from read and write requests, formats the requests, and sends it to a USB target. +- Creates two separate sequential queues and configures them to dispatch read and write requests directly. (*\*kmdf\_fx2 only*) +- Enables wait-wake and selective suspend support. (*\*kmdf\_fx2 only*) +- Configures a USB target continuous reader to read toggle switch states asynchronously from the interrupt endpoint. (*\*kmdf\_fx2 only*) +- Supports additional IOCTLs to get and set the 7-segment display and toggle switches, and to reset and re-enumerate the device. (*\*kmdf\_fx2 only*) +- Creates ETW provider to log two events to the event log, and read/write start stop events. (*\*kmdf\_fx2 only*) +- WPP tracing. + +usb\\kmdf\_fx2\\exe + +This directory contains a test application that can be used to drive the KMDF driver and FX2 device. + +usb\\kmdf\_fx2\\deviceMetadata + +This directory contains the device metadata package for the sample. You must copy the device metadata to the system before installing the device. For information on how to update and deploy device metadata, see the [Custom driver access sample](http://go.microsoft.com/fwlink/p/?LinkID=248288). + +Testing the driver +------------------ + +You can use the [Custom driver access](http://go.microsoft.com/fwlink/p/?LinkID=248288) sample as a testing method. + +The sample also includes a test application, osrusbfx2.exe, that you can use to test the device. This console application enumerates the interface registered by the driver and opens the device to send read, write, or IOCTL requests based on the command line options. + +Usage for Read/Write test: + +- -r [*n*], where *n* is number of bytes to read. +- -w [*n*], where *n* is number of bytes to write. +- -c [*n*], where *n* is number of iterations (default = 1). +- -v, shows verbose read data. +- -p, plays with Bar Display, Dip Switch, 7-Segment Display. +- -a, performs asynchronous I/O operation. +- -u, dumps USB configuration and pipe information. + +**Playing with the 7 segment display, toggle switches, and bar graph display** + +Use the command, **osrusbfx2.exe -p** options 1-9, to set and clear bar graph display, set and get 7 segment state, and read the toggle switch states. The following shows the function options: + +1. Light bar +2. Clear bar +3. Light entire bar graph +4. Clear entire bar graph +5. Get bar graph state +6. Get switch state +7. Get switch interrupt message +8. Get 7 segment state +9. Set 7 segment state +10. Reset the device +11. Re-enumerate the device + +0. Exit + +Selection: + +**Reset and re-enumerate the device** + +Use the command, osrusbfx2.exe -p with option 10 and 11, to either reset the device or re-enumerate the device. + +**Read and write to bulk endpoints** + +The following commands send read and write requests to the device's bulk endpoint. + +- `osrusbfx2.exe -r 64` + + The preceding command reads 64 bytes to the bulk IN endpoint. + +- `osrusbfx2.exe -w 64 ` + + The preceding command writes 64 bytes to the bulk OUT endpoint. + +- `osrusbfx2.exe -r 64 -w 64 -c 100 -v` + + The preceding command first writes 64 bytes of data to bulk OUT endpoint (Pipe 1), then reads 64 bytes from bulk IN endpoint (Pipe 2), and then compares the read buffer with write buffer to see if they match. If the buffer contents match, it repeats this operation 100 times. + +- `osrusbfx2.exe -a` + + The preceding command reads and writes to the device asynchronously in an infinite loop. + +The bulk endpoints are double buffered. Depending on the operational speed (full or high), the buffer size is either 64 bytes or 512 bytes, respectively. A request to read data does not complete if the buffers are empty. If the buffers are full, a request to write data does not complete until the buffers are emptied. When you are doing a synchronous read, make sure the endpoint buffer has data (for example, when you send a 512 bytes write request to the device operating in full speed mode). Because the endpoints are double buffered, the total buffer capacity is 256 bytes. The first 256 bytes fills the buffer, and the write request waits in the USB stack until the buffers are emptied. If you run another instance of the application to read 512 bytes of data, both write and read requests complete successfully. + +**Displaying descriptors** + +The following command displays all the descriptors and endpoint information. + +**osrusbfx2.exe -u** + +If the device is operating in high speed mode, you will get the following information: + +`===================` + +`USB_CONFIGURATION_DESCRIPTOR` + +`bLength = 0x9, decimal 9` + +`bDescriptorType = 0x2 ( USB_CONFIGURATION_DESCRIPTOR_TYPE )` + +`wTotalLength = 0x27, decimal 39` + +`bNumInterfaces = 0x1, decimal 1` + +`bConfigurationValue = 0x1, decimal 1` + +`iConfiguration = 0x4, decimal 4` + +`bmAttributes = 0xa0 ( USB_CONFIG_BUS_POWERED )` + +`MaxPower = 0x32, decimal 50` + +`-----------------------------` + +`USB_INTERFACE_DESCRIPTOR #0` + +`bLength = 0x9` + +`bDescriptorType = 0x4 ( USB_INTERFACE_DESCRIPTOR_TYPE )` + +`bInterfaceNumber = 0x0` + +`bAlternateSetting = 0x0` + +`bNumEndpoints = 0x3` + +`bInterfaceClass = 0xff` + +`bInterfaceSubClass = 0x0` + +`bInterfaceProtocol = 0x0` + +`bInterface = 0x0` + +`------------------------------` + +`USB_ENDPOINT_DESCRIPTOR for Pipe00` + +`bLength = 0x7` + +`bDescriptorType = 0x5 ( USB_ENDPOINT_DESCRIPTOR_TYPE )` + +`bEndpointAddress= 0x81 ( INPUT )` + +`bmAttributes= 0x3 ( USB_ENDPOINT_TYPE_INTERRUPT )` + +`wMaxPacketSize= 0x49, decimal 73` + +`bInterval = 0x1, decimal 1` + +`------------------------------` + +`USB_ENDPOINT_DESCRIPTOR for Pipe01` + +`bLength = 0x7` + +`bDescriptorType = 0x5 ( USB_ENDPOINT_DESCRIPTOR_TYPE )` + +`bEndpointAddress= 0x6 ( OUTPUT )` + +`bmAttributes= 0x2 ( USB_ENDPOINT_TYPE_BULK )` + +`wMaxPacketSize= 0x200, ` + +`decimal 512 bInterval = 0x0, ` + +`decimal 0` + +`------------------------------` + +`USB_ENDPOINT_DESCRIPTOR for Pipe02` + +`bLength = 0x7` + +`bDescriptorType = 0x5 ( USB_ENDPOINT_DESCRIPTOR_TYPE )` + +`bEndpointAddress= 0x88 ( INPUT )` + +`bmAttributes= 0x2 ( USB_ENDPOINT_TYPE_BULK )` + +`wMaxPacketSize= 0x200, decimal 512` + +`bInterval = 0x0, decimal 0` + +If the device is operating in low speed mode, you will get the following information: + +`===================` + +`USB_CONFIGURATION_DESCRIPTOR` + +`bLength = 0x9, decimal 9` + +`bDescriptorType = 0x2 ( USB_CONFIGURATION_DESCRIPTOR_TYPE )` + +`wTotalLength = 0x27, decimal 39` + +`bNumInterfaces = 0x1, decimal 1` + +`bConfigurationValue = 0x1, decimal 1` + +`iConfiguration = 0x3, decimal 3` + +`bmAttributes = 0xa0 ( USB_CONFIG_BUS_POWERED )` + +`MaxPower = 0x32, decimal 50 ` + +`-----------------------------` + +`USB_INTERFACE_DESCRIPTOR #0` + +`bLength = 0x9` + +`bDescriptorType = 0x4 ( USB_INTERFACE_DESCRIPTOR_TYPE )` + +`bInterfaceNumber = 0x0 bAlternateSetting = 0x0` + +`bNumEndpoints = 0x3` + +`bInterfaceClass = 0xff` + +`bInterfaceSubClass = 0x0` + +`bInterfaceProtocol = 0x0` + +`bInterface = 0x0` + +`------------------------------` + +`USB_ENDPOINT_DESCRIPTOR for Pipe00` + +`bLength = 0x7` + +`bDescriptorType = 0x5 ( USB_ENDPOINT_DESCRIPTOR_TYPE )` + +`bEndpointAddress= 0x81 ( INPUT )` + +`bmAttributes= 0x3 ( USB_ENDPOINT_TYPE_INTERRUPT )` + +`wMaxPacketSize= 0x49, decimal 73` + +`bInterval = 0x1, decimal 1` + +`------- -----------------------` + +`USB_ENDPOINT_DESCRIPTOR for Pipe01` + +`bLength = 0x7` + +`bDescriptorType = 0x5 ( USB_ENDPOINT_DESCRIPTOR_TYPE )` + +`bEndpointAddress= 0x6 ( OUTPUT )` + +`bmAttributes= 0x2 ( USB_ENDPOINT_TYPE_BULK )` + +`wMaxPacketSize= 0x40, decimal 64` + +`bInterval = 0x0, decimal 0` + +`------------------------------` + +`USB_ENDPOINT_DESCRIPTOR for Pipe02` + +`bLength = 0x7` + +`bDescriptorType = 0x5 ( USB_ENDPOINT_DESCRIPTOR_TYPE )` + +`bEndpointAddress= 0x88 ( INPUT )` + +`bmAttributes= 0x2 ( USB_ENDPOINT_TYPE_BULK )` + +`wMaxPacketSize= 0x40, decimal 64` + +`bInterval = 0x0, decimal 0 ` + +Unified tracing +--------------- + +To view events the provider manifest must be installed. As part of the installation, from an elevated prompt run the following: `wevtutil im osrusbfx2.man` + +Registering the manifest sets up the appropriate paths where the system can find information for decoding the events. The OSR event log can be found in the system event viewer under Event Viewer\\Applications and Services Logs\\OSRUSBFx2\\Operational channel eventlog. Triggering a device re-enumeration through osrusbfx2.exe sends an event to this log. + +To trace, you can use the in-box tools, logman and tracerpt, or download XPerf (Windows Performance Toolkit) from Microsoft. + +**Using in-box tools** + +**To start/stop the trace by using logman:** + +1. Start tracing by using the following command: + + `logman start sample -o osrusbfx2.etl -ets -p OSRUSBFX2` + +2. Generate activity through the osrusbfx2 test application, such as `osrusbfx2.exe –a`. +3. Stop tracing by using the following command: + + `Logman stop sample` + +4. View the trace file using tracerpt: + + `tracerpt -of csv OSRUSBFX2.etl ` + +**To start/stop the trace by using Xperf (Windows Performance Toolkit):** + +1. Start tracing by using the following command: + + `xperf -start sample -f osrusbfx2.etl -on OSRUSBFX2` + +2. Generate activity through the osrusbfx2 test application, such as `osrusbfx2.exe –a`. +3. Stop tracing by using the following command: + + `xperf -stop sample` + +4. View the trace file using Xperf: + + `xperfview OSRUSBFX2.etl` + + diff --git a/usb/kmdf_fx2/deviceMetadata/B4D697F5-1C56-4807-ACCD-B28C09D37FF0.devicemetadata-ms b/usb/kmdf_fx2/deviceMetadata/B4D697F5-1C56-4807-ACCD-B28C09D37FF0.devicemetadata-ms new file mode 100644 index 0000000000000000000000000000000000000000..5f8e82ee8c0ddf658bcc913783441aa3bc940eda GIT binary patch literal 105893 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 gp2|18G=GImGEXBH%M?7tuZntv$ +#include + +#if defined(EVENT_TRACING) +#include "device.tmh" +#endif + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, OsrFxEvtDeviceAdd) +#pragma alloc_text(PAGE, OsrFxEvtDevicePrepareHardware) +#pragma alloc_text(PAGE, OsrFxEvtDeviceD0Exit) +#pragma alloc_text(PAGE, SelectInterfaces) +#pragma alloc_text(PAGE, OsrFxSetPowerPolicy) +#pragma alloc_text(PAGE, OsrFxReadFdoRegistryKeyValue) +#pragma alloc_text(PAGE, GetDeviceEventLoggingNames) +#pragma alloc_text(PAGE, OsrFxValidateConfigurationDescriptor) +#endif + + +NTSTATUS +OsrFxEvtDeviceAdd( + WDFDRIVER Driver, + 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. All the software resources + should be allocated in this callback. + +Arguments: + + Driver - Handle to a framework driver object created in DriverEntry + + DeviceInit - Pointer to a framework-allocated WDFDEVICE_INIT structure. + +Return Value: + + NTSTATUS + +--*/ +{ + WDF_PNPPOWER_EVENT_CALLBACKS pnpPowerCallbacks; + WDF_OBJECT_ATTRIBUTES attributes; + NTSTATUS status; + WDFDEVICE device; + WDF_DEVICE_PNP_CAPABILITIES pnpCaps; + WDF_IO_QUEUE_CONFIG ioQueueConfig; + PDEVICE_CONTEXT pDevContext; + WDFQUEUE queue; + GUID activity; + UNICODE_STRING symbolicLinkName; + WDFSTRING symbolicLinkString; + DEVPROP_BOOLEAN isRestricted; + + UNREFERENCED_PARAMETER(Driver); + + PAGED_CODE(); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP,"--> OsrFxEvtDeviceAdd routine\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); + // + // For usb devices, PrepareHardware callback is the to place select the + // interface and configure the device. + // + pnpPowerCallbacks.EvtDevicePrepareHardware = OsrFxEvtDevicePrepareHardware; + + // + // These two callbacks start and stop the wdfusb pipe continuous reader + // as we go in and out of the D0-working state. + // + + pnpPowerCallbacks.EvtDeviceD0Entry = OsrFxEvtDeviceD0Entry; + pnpPowerCallbacks.EvtDeviceD0Exit = OsrFxEvtDeviceD0Exit; + pnpPowerCallbacks.EvtDeviceSelfManagedIoFlush = OsrFxEvtDeviceSelfManagedIoFlush; + + WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpPowerCallbacks); + + WdfDeviceInitSetIoType(DeviceInit, WdfDeviceIoBuffered); + + // + // Now specify the size of device extension where we track per device + // context.DeviceInit is completely initialized. So call the framework + // to create the device and attach it to the lower stack. + // + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, DEVICE_CONTEXT); + + status = WdfDeviceCreate(&DeviceInit, &attributes, &device); + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "WdfDeviceCreate failed with Status code %!STATUS!\n", status); + return status; + } + + // + // Setup the activity ID so that we can log events using it. + // + + activity = DeviceToActivityId(device); + + // + // Get the DeviceObject context by using accessor function specified in + // the WDF_DECLARE_CONTEXT_TYPE_WITH_NAME macro for DEVICE_CONTEXT. + // + pDevContext = GetDeviceContext(device); + + // + // Get the device's friendly name and location so that we can use it in + // error logging. If this fails then it will setup dummy strings. + // + + GetDeviceEventLoggingNames(device); + + // + // Tell the framework to set the SurpriseRemovalOK in the DeviceCaps so + // that you don't get the popup in usermode when you surprise remove the device. + // + WDF_DEVICE_PNP_CAPABILITIES_INIT(&pnpCaps); + pnpCaps.SurpriseRemovalOK = WdfTrue; + + WdfDeviceSetPnpCapabilities(device, &pnpCaps); + + // + // Create a parallel default queue and register an event callback to + // receive ioctl requests. We will create separate queues for + // handling read and write requests. All other requests will be + // completed with error status automatically by the framework. + // + WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&ioQueueConfig, + WdfIoQueueDispatchParallel); + + ioQueueConfig.EvtIoDeviceControl = OsrFxEvtIoDeviceControl; + + // + // 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(ioQueueConfig.EvtIoStop != 0); + status = WdfIoQueueCreate(device, + &ioQueueConfig, + WDF_NO_OBJECT_ATTRIBUTES, + &queue);// pointer to default queue + __analysis_assume(ioQueueConfig.EvtIoStop == 0); + + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "WdfIoQueueCreate failed %!STATUS!\n", status); + goto Error; + } + + // + // We will create a separate sequential queue and configure it + // to receive read requests. We also need to register a EvtIoStop + // handler so that we can acknowledge requests that are pending + // at the target driver. + // + WDF_IO_QUEUE_CONFIG_INIT(&ioQueueConfig, WdfIoQueueDispatchSequential); + + ioQueueConfig.EvtIoRead = OsrFxEvtIoRead; + ioQueueConfig.EvtIoStop = OsrFxEvtIoStop; + + status = WdfIoQueueCreate( + device, + &ioQueueConfig, + WDF_NO_OBJECT_ATTRIBUTES, + &queue // queue handle + ); + + if (!NT_SUCCESS (status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "WdfIoQueueCreate failed 0x%x\n", status); + goto Error; + } + + status = WdfDeviceConfigureRequestDispatching( + device, + queue, + WdfRequestTypeRead); + + if(!NT_SUCCESS (status)){ + NT_ASSERT(NT_SUCCESS(status)); + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "WdfDeviceConfigureRequestDispatching failed 0x%x\n", status); + goto Error; + } + + + // + // We will create another sequential queue and configure it + // to receive write requests. + // + WDF_IO_QUEUE_CONFIG_INIT(&ioQueueConfig, WdfIoQueueDispatchSequential); + + ioQueueConfig.EvtIoWrite = OsrFxEvtIoWrite; + ioQueueConfig.EvtIoStop = OsrFxEvtIoStop; + + status = WdfIoQueueCreate( + device, + &ioQueueConfig, + WDF_NO_OBJECT_ATTRIBUTES, + &queue // queue handle + ); + + if (!NT_SUCCESS (status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "WdfIoQueueCreate failed 0x%x\n", status); + goto Error; + } + + status = WdfDeviceConfigureRequestDispatching( + device, + queue, + WdfRequestTypeWrite); + + if(!NT_SUCCESS (status)){ + NT_ASSERT(NT_SUCCESS(status)); + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "WdfDeviceConfigureRequestDispatching failed 0x%x\n", status); + goto Error; + } + + // + // Register a manual I/O queue for handling Interrupt Message Read Requests. + // This queue will be used for storing Requests that need to wait for an + // interrupt to occur before they can be completed. + // + WDF_IO_QUEUE_CONFIG_INIT(&ioQueueConfig, WdfIoQueueDispatchManual); + + // + // This queue is used for requests that dont directly access the device. The + // requests in this queue are serviced only when the device is in a fully + // powered state and sends an interrupt. So we can use a non-power managed + // queue to park the requests since we dont care whether the device is idle + // or fully powered up. + // + ioQueueConfig.PowerManaged = WdfFalse; + + status = WdfIoQueueCreate(device, + &ioQueueConfig, + WDF_NO_OBJECT_ATTRIBUTES, + &pDevContext->InterruptMsgQueue + ); + + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "WdfIoQueueCreate failed 0x%x\n", status); + goto Error; + } + + // + // Register a device interface so that app can find our device and talk to it. + // + status = WdfDeviceCreateDeviceInterface(device, + (LPGUID) &GUID_DEVINTERFACE_OSRUSBFX2, + NULL); // Reference String + + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "WdfDeviceCreateDeviceInterface failed %!STATUS!\n", status); + goto Error; + } + + // + // Create the lock that we use to serialize calls to ResetDevice(). As an + // alternative to using a WDFWAITLOCK to serialize the calls, a sequential + // WDFQUEUE can be created and reset IOCTLs would be forwarded to it. + // + WDF_OBJECT_ATTRIBUTES_INIT(&attributes); + attributes.ParentObject = device; + + status = WdfWaitLockCreate(&attributes, &pDevContext->ResetDeviceWaitLock); + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "WdfWaitLockCreate failed %!STATUS!\n", status); + goto Error; + } + + // + // Get the string for the device interface and set the restricted + // property on it to allow applications bound with device metadata + // to access the interface. + // + if (g_pIoSetDeviceInterfacePropertyData != NULL) { + + status = WdfStringCreate(NULL, + WDF_NO_OBJECT_ATTRIBUTES, + &symbolicLinkString); + + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "WdfStringCreate failed %!STATUS!\n", status); + goto Error; + } + + status = WdfDeviceRetrieveDeviceInterfaceString(device, + (LPGUID) &GUID_DEVINTERFACE_OSRUSBFX2, + NULL, + symbolicLinkString); + + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "WdfDeviceRetrieveDeviceInterfaceString failed %!STATUS!\n", status); + goto Error; + } + + WdfStringGetUnicodeString(symbolicLinkString, &symbolicLinkName); + + isRestricted = DEVPROP_TRUE; + + status = g_pIoSetDeviceInterfacePropertyData(&symbolicLinkName, + &DEVPKEY_DeviceInterface_Restricted, + 0, + 0, + DEVPROP_TYPE_BOOLEAN, + sizeof(isRestricted), + &isRestricted ); + + WdfObjectDelete(symbolicLinkString); + + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "IoSetDeviceInterfacePropertyData failed %!STATUS!\n", status); + goto Error; + } + } + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "<-- OsrFxEvtDeviceAdd\n"); + + return status; + +Error: + + // + // Log fail to add device to the event log + // + EventWriteFailAddDevice(&activity, + pDevContext->DeviceName, + pDevContext->Location, + status); + + return status; +} + +NTSTATUS +OsrFxEvtDevicePrepareHardware( + WDFDEVICE Device, + WDFCMRESLIST ResourceList, + WDFCMRESLIST ResourceListTranslated + ) +/*++ + +Routine Description: + + In this callback, the driver does whatever is necessary to make the + hardware ready to use. In the case of a USB device, this involves + reading and selecting descriptors. + +Arguments: + + Device - handle to a device + + ResourceList - handle to a resource-list object that identifies the + raw hardware resources that the PnP manager assigned + to the device + + ResourceListTranslated - handle to a resource-list object that + identifies the translated hardware resources + that the PnP manager assigned to the device + +Return Value: + + NT status value + +--*/ +{ + NTSTATUS status; + PDEVICE_CONTEXT pDeviceContext; + WDF_USB_DEVICE_INFORMATION deviceInfo; + ULONG waitWakeEnable; + + UNREFERENCED_PARAMETER(ResourceList); + UNREFERENCED_PARAMETER(ResourceListTranslated); + waitWakeEnable = FALSE; + PAGED_CODE(); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "--> EvtDevicePrepareHardware\n"); + + pDeviceContext = GetDeviceContext(Device); + + // + // Create a USB device handle so that we can communicate with the + // underlying USB stack. The WDFUSBDEVICE handle is used to query, + // configure, and manage all aspects of the USB device. + // These aspects include device properties, bus properties, + // and I/O creation and synchronization. We only create device the first + // the PrepareHardware is called. If the device is restarted by pnp manager + // for resource rebalance, we will use the same device handle but then select + // the interfaces again because the USB stack could reconfigure the device on + // restart. + // + if (pDeviceContext->UsbDevice == NULL) { + WDF_USB_DEVICE_CREATE_CONFIG config; + + WDF_USB_DEVICE_CREATE_CONFIG_INIT(&config, + USBD_CLIENT_CONTRACT_VERSION_602); + + status = WdfUsbTargetDeviceCreateWithParameters(Device, + &config, + WDF_NO_OBJECT_ATTRIBUTES, + &pDeviceContext->UsbDevice); + + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "WdfUsbTargetDeviceCreateWithParameters failed with Status code %!STATUS!\n", status); + return status; + } + + // + // TODO: If you are fetching configuration descriptor from device for + // selecting a configuration or to parse other descriptors, call OsrFxValidateConfigurationDescriptor + // to do basic validation on the descriptors before you access them . + // + } + + // + // Retrieve USBD version information, port driver capabilites and device + // capabilites such as speed, power, etc. + // + WDF_USB_DEVICE_INFORMATION_INIT(&deviceInfo); + + status = WdfUsbTargetDeviceRetrieveInformation( + pDeviceContext->UsbDevice, + &deviceInfo); + if (NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "IsDeviceHighSpeed: %s\n", + (deviceInfo.Traits & WDF_USB_DEVICE_TRAIT_AT_HIGH_SPEED) ? "TRUE" : "FALSE"); + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, + "IsDeviceSelfPowered: %s\n", + (deviceInfo.Traits & WDF_USB_DEVICE_TRAIT_SELF_POWERED) ? "TRUE" : "FALSE"); + + waitWakeEnable = deviceInfo.Traits & + WDF_USB_DEVICE_TRAIT_REMOTE_WAKE_CAPABLE; + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, + "IsDeviceRemoteWakeable: %s\n", + waitWakeEnable ? "TRUE" : "FALSE"); + // + // Save these for use later. + // + pDeviceContext->UsbDeviceTraits = deviceInfo.Traits; + } + else { + pDeviceContext->UsbDeviceTraits = 0; + } + + status = SelectInterfaces(Device); + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "SelectInterfaces failed 0x%x\n", status); + return status; + } + + // + // Enable wait-wake and idle timeout if the device supports it + // + if (waitWakeEnable) { + status = OsrFxSetPowerPolicy(Device); + if (!NT_SUCCESS (status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "OsrFxSetPowerPolicy failed %!STATUS!\n", status); + return status; + } + } + + status = OsrFxConfigContReaderForInterruptEndPoint(pDeviceContext); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "<-- EvtDevicePrepareHardware\n"); + + return status; +} + + +NTSTATUS +OsrFxEvtDeviceD0Entry( + WDFDEVICE Device, + 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 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 + +--*/ +{ + PDEVICE_CONTEXT pDeviceContext; + NTSTATUS status; + BOOLEAN isTargetStarted; + + pDeviceContext = GetDeviceContext(Device); + isTargetStarted = FALSE; + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_POWER, + "-->OsrFxEvtEvtDeviceD0Entry - coming from %s\n", + DbgDevicePowerString(PreviousState)); + + // + // Since continuous reader is configured for this interrupt-pipe, we must explicitly start + // the I/O target to get the framework to post read requests. + // + status = WdfIoTargetStart(WdfUsbTargetPipeGetIoTarget(pDeviceContext->InterruptPipe)); + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_POWER, "Failed to start interrupt pipe %!STATUS!\n", status); + goto End; + } + + isTargetStarted = TRUE; + +End: + + if (!NT_SUCCESS(status)) { + // + // Failure in D0Entry will lead to device being removed. So let us stop the continuous + // reader in preparation for the ensuing remove. + // + if (isTargetStarted) { + WdfIoTargetStop(WdfUsbTargetPipeGetIoTarget(pDeviceContext->InterruptPipe), WdfIoTargetCancelSentIo); + } + } + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_POWER, "<--OsrFxEvtEvtDeviceD0Entry\n"); + + return status; +} + + +NTSTATUS +OsrFxEvtDeviceD0Exit( + WDFDEVICE Device, + 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. + + + 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. + +--*/ +{ + PDEVICE_CONTEXT pDeviceContext; + + PAGED_CODE(); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_POWER, + "-->OsrFxEvtDeviceD0Exit - moving to %s\n", + DbgDevicePowerString(TargetState)); + + pDeviceContext = GetDeviceContext(Device); + + WdfIoTargetStop(WdfUsbTargetPipeGetIoTarget(pDeviceContext->InterruptPipe), WdfIoTargetCancelSentIo); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_POWER, "<--OsrFxEvtDeviceD0Exit\n"); + + return STATUS_SUCCESS; +} + +VOID +OsrFxEvtDeviceSelfManagedIoFlush( + _In_ WDFDEVICE Device + ) +/*++ + +Routine Description: + + This routine handles flush activity for the device's + self-managed I/O operations. + +Arguments: + + Device - Handle to a framework device object. + +Return Value: + + None + +--*/ +{ + // Service the interrupt message queue to drain any outstanding + // requests + OsrUsbIoctlGetInterruptMessage(Device, STATUS_DEVICE_REMOVED); +} + +_IRQL_requires_(PASSIVE_LEVEL) +USBD_STATUS +OsrFxValidateConfigurationDescriptor( + _In_reads_bytes_(BufferLength) PUSB_CONFIGURATION_DESCRIPTOR ConfigDesc, + _In_ ULONG BufferLength, + _Inout_ PUCHAR *Offset + ) +/*++ + +Routine Description: + + Validates a USB Configuration Descriptor + +Parameters: + + ConfigDesc: Pointer to the entire USB Configuration descriptor returned by the device + + BufferLength: Known size of buffer pointed to by ConfigDesc (Not wTotalLength) + + Offset: if the USBD_STATUS returned is not USBD_STATUS_SUCCESS, offet will + be set to the address within the ConfigDesc buffer where the failure occured. + +Return Value: + + USBD_STATUS + Success implies the configuration descriptor is valid. + +--*/ +{ + + + USBD_STATUS status = USBD_STATUS_SUCCESS; + USHORT ValidationLevel = 3; + + PAGED_CODE(); + + // + // Call USBD_ValidateConfigurationDescriptor to validate the descriptors which are present in this supplied configuration descriptor. + // USBD_ValidateConfigurationDescriptor validates that all descriptors are completely contained within the configuration descriptor buffer. + // It also checks for interface numbers, number of endpoints in an interface etc. + // Please refer to msdn documentation for this function for more information. + // + + status = USBD_ValidateConfigurationDescriptor( ConfigDesc, BufferLength , ValidationLevel , Offset , POOL_TAG ); + if (!(NT_SUCCESS (status)) ){ + return status; + } + + // + // TODO: You should validate the correctness of other descriptors which are not taken care by USBD_ValidateConfigurationDescriptor + // Check that all such descriptors have size >= sizeof(the descriptor they point to) + // Check for any association between them if required + // + + return status; +} + + +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +OsrFxSetPowerPolicy( + _In_ WDFDEVICE Device + ) +{ + WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS idleSettings; + WDF_DEVICE_POWER_POLICY_WAKE_SETTINGS wakeSettings; + NTSTATUS status = STATUS_SUCCESS; + + PAGED_CODE(); + + // + // Init the idle policy structure. + // + WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_INIT(&idleSettings, IdleUsbSelectiveSuspend); + idleSettings.IdleTimeout = 10000; // 10-sec + + status = WdfDeviceAssignS0IdleSettings(Device, &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(Device, &wakeSettings); + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "WdfDeviceAssignSxWakeSettings failed %x\n", status); + return status; + } + + return status; +} + + +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +SelectInterfaces( + _In_ WDFDEVICE Device + ) +/*++ + +Routine Description: + + This helper routine selects the configuration, interface and + creates a context for every pipe (end point) in that interface. + +Arguments: + + Device - Handle to a framework device + +Return Value: + + NT status value + +--*/ +{ + WDF_USB_DEVICE_SELECT_CONFIG_PARAMS configParams; + NTSTATUS status = STATUS_SUCCESS; + PDEVICE_CONTEXT pDeviceContext; + WDFUSBPIPE pipe; + WDF_USB_PIPE_INFORMATION pipeInfo; + UCHAR index; + UCHAR numberConfiguredPipes; + + PAGED_CODE(); + + pDeviceContext = GetDeviceContext(Device); + + WDF_USB_DEVICE_SELECT_CONFIG_PARAMS_INIT_SINGLE_INTERFACE( &configParams); + + status = WdfUsbTargetDeviceSelectConfig(pDeviceContext->UsbDevice, + WDF_NO_OBJECT_ATTRIBUTES, + &configParams); + if(!NT_SUCCESS(status)) { + + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "WdfUsbTargetDeviceSelectConfig failed %!STATUS! \n", + status); + + // + // Since the Osr USB fx2 device is capable of working at high speed, the only reason + // the device would not be working at high speed is if the port doesn't + // support it. If the port doesn't support high speed it is a 1.1 port + // + if ((pDeviceContext->UsbDeviceTraits & WDF_USB_DEVICE_TRAIT_AT_HIGH_SPEED) == 0) { + GUID activity = DeviceToActivityId(Device); + + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + " On a 1.1 USB port on Windows Vista" + " this is expected as the OSR USB Fx2 board's Interrupt EndPoint descriptor" + " doesn't conform to the USB specification. Windows Vista detects this and" + " returns an error. \n" + ); + EventWriteSelectConfigFailure( + &activity, + pDeviceContext->DeviceName, + pDeviceContext->Location, + status + ); + } + + return status; + } + + pDeviceContext->UsbInterface = + configParams.Types.SingleInterface.ConfiguredUsbInterface; + + numberConfiguredPipes = configParams.Types.SingleInterface.NumberConfiguredPipes; + + // + // Get pipe handles + // + for(index=0; index < numberConfiguredPipes; index++) { + + WDF_USB_PIPE_INFORMATION_INIT(&pipeInfo); + + pipe = WdfUsbInterfaceGetConfiguredPipe( + pDeviceContext->UsbInterface, + index, //PipeIndex, + &pipeInfo + ); + // + // Tell the framework that it's okay to read less than + // MaximumPacketSize + // + WdfUsbTargetPipeSetNoMaximumPacketSizeCheck(pipe); + + if(WdfUsbPipeTypeInterrupt == pipeInfo.PipeType) { + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_IOCTL, + "Interrupt Pipe is 0x%p\n", pipe); + pDeviceContext->InterruptPipe = pipe; + } + + if(WdfUsbPipeTypeBulk == pipeInfo.PipeType && + WdfUsbTargetPipeIsInEndpoint(pipe)) { + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_IOCTL, + "BulkInput Pipe is 0x%p\n", pipe); + pDeviceContext->BulkReadPipe = pipe; + } + + if(WdfUsbPipeTypeBulk == pipeInfo.PipeType && + WdfUsbTargetPipeIsOutEndpoint(pipe)) { + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_IOCTL, + "BulkOutput Pipe is 0x%p\n", pipe); + pDeviceContext->BulkWritePipe = pipe; + } + + } + + // + // If we didn't find all the 3 pipes, fail the start. + // + if(!(pDeviceContext->BulkWritePipe + && pDeviceContext->BulkReadPipe && pDeviceContext->InterruptPipe)) { + status = STATUS_INVALID_DEVICE_STATE; + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "Device is not configured properly %!STATUS!\n", + status); + + return status; + } + + return status; +} + +_IRQL_requires_(PASSIVE_LEVEL) +VOID +GetDeviceEventLoggingNames( + _In_ WDFDEVICE Device + ) +/*++ + +Routine Description: + + Retrieve the friendly name and the location string into WDFMEMORY objects + and store them in the device context. + +Arguments: + +Return Value: + + None + +--*/ +{ + PDEVICE_CONTEXT pDevContext = GetDeviceContext(Device); + + WDF_OBJECT_ATTRIBUTES objectAttributes; + + WDFMEMORY deviceNameMemory = NULL; + WDFMEMORY locationMemory = NULL; + + NTSTATUS status; + + PAGED_CODE(); + + // + // We want both memory objects to be children of the device so they will + // be deleted automatically when the device is removed. + // + + WDF_OBJECT_ATTRIBUTES_INIT(&objectAttributes); + objectAttributes.ParentObject = Device; + + // + // First get the length of the string. If the FriendlyName + // is not there then get the lenght of device description. + // + + status = WdfDeviceAllocAndQueryProperty(Device, + DevicePropertyFriendlyName, + NonPagedPool, + &objectAttributes, + &deviceNameMemory); + + if (!NT_SUCCESS(status)) + { + status = WdfDeviceAllocAndQueryProperty(Device, + DevicePropertyDeviceDescription, + NonPagedPool, + &objectAttributes, + &deviceNameMemory); + } + + if (NT_SUCCESS(status)) + { + pDevContext->DeviceNameMemory = deviceNameMemory; + pDevContext->DeviceName = WdfMemoryGetBuffer(deviceNameMemory, NULL); + } + else + { + pDevContext->DeviceNameMemory = NULL; + pDevContext->DeviceName = L"(error retrieving name)"; + } + + // + // Retrieve the device location string. + // + + status = WdfDeviceAllocAndQueryProperty(Device, + DevicePropertyLocationInformation, + NonPagedPool, + WDF_NO_OBJECT_ATTRIBUTES, + &locationMemory); + + if (NT_SUCCESS(status)) + { + pDevContext->LocationMemory = locationMemory; + pDevContext->Location = WdfMemoryGetBuffer(locationMemory, NULL); + } + else + { + pDevContext->LocationMemory = NULL; + pDevContext->Location = L"(error retrieving location)"; + } + + return; +} + +_IRQL_requires_(PASSIVE_LEVEL) +PCHAR +DbgDevicePowerString( + _In_ WDF_POWER_DEVICE_STATE Type + ) +{ + 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/usb/kmdf_fx2/driver/bulkrwr.c b/usb/kmdf_fx2/driver/bulkrwr.c new file mode 100644 index 000000000..eabda1925 --- /dev/null +++ b/usb/kmdf_fx2/driver/bulkrwr.c @@ -0,0 +1,436 @@ +/*++ + +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: + + bulkrwr.c + +Abstract: + + This file has routines to perform reads and writes. + The read and writes are targeted bulk to endpoints. + +Environment: + + Kernel mode + +--*/ + +#include + + +#if defined(EVENT_TRACING) +#include "bulkrwr.tmh" +#endif + +#pragma warning(disable:4267) + +VOID +OsrFxEvtIoRead( + _In_ WDFQUEUE Queue, + _In_ WDFREQUEST Request, + _In_ size_t Length + ) +/*++ + +Routine Description: + + Called by the framework when it receives Read or Write requests. + +Arguments: + + Queue - Default queue handle + Request - Handle to the read/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: + + +--*/ +{ + WDFUSBPIPE pipe; + NTSTATUS status; + WDFMEMORY reqMemory; + PDEVICE_CONTEXT pDeviceContext; + GUID activity = RequestToActivityId(Request); + + UNREFERENCED_PARAMETER(Queue); + + // + // Log read start event, using IRP activity ID if available or request + // handle otherwise. + // + + EventWriteReadStart(&activity, WdfIoQueueGetDevice(Queue), (ULONG)Length); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "-->OsrFxEvtIoRead\n"); + + // + // First validate input parameters. + // + if (Length > TEST_BOARD_TRANSFER_BUFFER_SIZE) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_READ, "Transfer exceeds %d\n", + TEST_BOARD_TRANSFER_BUFFER_SIZE); + status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + pDeviceContext = GetDeviceContext(WdfIoQueueGetDevice(Queue)); + + pipe = pDeviceContext->BulkReadPipe; + + status = WdfRequestRetrieveOutputMemory(Request, &reqMemory); + if(!NT_SUCCESS(status)){ + TraceEvents(TRACE_LEVEL_ERROR, DBG_READ, + "WdfRequestRetrieveOutputMemory failed %!STATUS!\n", status); + goto Exit; + } + + // + // The format call validates to make sure that you are reading or + // writing to the right pipe type, sets the appropriate transfer flags, + // creates an URB and initializes the request. + // + status = WdfUsbTargetPipeFormatRequestForRead(pipe, + Request, + reqMemory, + NULL // Offsets + ); + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_READ, + "WdfUsbTargetPipeFormatRequestForRead failed 0x%x\n", status); + goto Exit; + } + + WdfRequestSetCompletionRoutine( + Request, + EvtRequestReadCompletionRoutine, + pipe); + // + // Send the request asynchronously. + // + if (WdfRequestSend(Request, WdfUsbTargetPipeGetIoTarget(pipe), WDF_NO_SEND_OPTIONS) == FALSE) { + // + // Framework couldn't send the request for some reason. + // + TraceEvents(TRACE_LEVEL_ERROR, DBG_READ, "WdfRequestSend failed\n"); + status = WdfRequestGetStatus(Request); + goto Exit; + } + + +Exit: + if (!NT_SUCCESS(status)) { + // + // log event read failed + // + EventWriteReadFail(&activity, WdfIoQueueGetDevice(Queue), status); + WdfRequestCompleteWithInformation(Request, status, 0); + } + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_READ, "<-- OsrFxEvtIoRead\n"); + + return; +} + +VOID +EvtRequestReadCompletionRoutine( + _In_ WDFREQUEST Request, + _In_ WDFIOTARGET Target, + _In_ PWDF_REQUEST_COMPLETION_PARAMS CompletionParams, + _In_ WDFCONTEXT Context + ) +/*++ + +Routine Description: + + This is the completion routine for reads + If the irp completes with success, we check if we + need to recirculate this irp for another stage of + transfer. + +Arguments: + + Context - Driver supplied context + Device - Device handle + Request - Request handle + Params - request completion params + +Return Value: + None + +--*/ +{ + NTSTATUS status; + size_t bytesRead = 0; + GUID activity = RequestToActivityId(Request); + PWDF_USB_REQUEST_COMPLETION_PARAMS usbCompletionParams; + + UNREFERENCED_PARAMETER(Target); + UNREFERENCED_PARAMETER(Context); + + status = CompletionParams->IoStatus.Status; + + usbCompletionParams = CompletionParams->Parameters.Usb.Completion; + + bytesRead = usbCompletionParams->Parameters.PipeRead.Length; + + if (NT_SUCCESS(status)){ + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_READ, + "Number of bytes read: %I64d\n", (INT64)bytesRead); + } else { + TraceEvents(TRACE_LEVEL_ERROR, DBG_READ, + "Read failed - request status 0x%x UsbdStatus 0x%x\n", + status, usbCompletionParams->UsbdStatus); + + } + + // + // Log read stop event, using IRP activity ID if available or request + // handle otherwise. + // + + EventWriteReadStop(&activity, + WdfIoQueueGetDevice(WdfRequestGetIoQueue(Request)), + bytesRead, + status, + usbCompletionParams->UsbdStatus); + + WdfRequestCompleteWithInformation(Request, status, bytesRead); + + return; +} + +VOID +OsrFxEvtIoWrite( + _In_ WDFQUEUE Queue, + _In_ WDFREQUEST Request, + _In_ size_t Length + ) +/*++ + +Routine Description: + + Called by the framework when it receives Read or Write requests. + +Arguments: + + Queue - Default queue handle + Request - Handle to the read/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; + WDFUSBPIPE pipe; + WDFMEMORY reqMemory; + PDEVICE_CONTEXT pDeviceContext; + GUID activity = RequestToActivityId(Request); + + UNREFERENCED_PARAMETER(Queue); + + + // + // Log write start event, using IRP activity ID if available or request + // handle otherwise. + // + EventWriteWriteStart(&activity, WdfIoQueueGetDevice(Queue), (ULONG)Length); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_WRITE, "-->OsrFxEvtIoWrite\n"); + + // + // First validate input parameters. + // + if (Length > TEST_BOARD_TRANSFER_BUFFER_SIZE) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_READ, "Transfer exceeds %d\n", + TEST_BOARD_TRANSFER_BUFFER_SIZE); + status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + pDeviceContext = GetDeviceContext(WdfIoQueueGetDevice(Queue)); + + pipe = pDeviceContext->BulkWritePipe; + + status = WdfRequestRetrieveInputMemory(Request, &reqMemory); + if(!NT_SUCCESS(status)){ + TraceEvents(TRACE_LEVEL_ERROR, DBG_WRITE, "WdfRequestRetrieveInputBuffer failed\n"); + goto Exit; + } + + status = WdfUsbTargetPipeFormatRequestForWrite(pipe, + Request, + reqMemory, + NULL); // Offset + + + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_WRITE, + "WdfUsbTargetPipeFormatRequestForWrite failed 0x%x\n", status); + goto Exit; + } + + WdfRequestSetCompletionRoutine( + Request, + EvtRequestWriteCompletionRoutine, + pipe); + + // + // Send the request asynchronously. + // + if (WdfRequestSend(Request, WdfUsbTargetPipeGetIoTarget(pipe), WDF_NO_SEND_OPTIONS) == FALSE) { + // + // Framework couldn't send the request for some reason. + // + TraceEvents(TRACE_LEVEL_ERROR, DBG_WRITE, "WdfRequestSend failed\n"); + status = WdfRequestGetStatus(Request); + goto Exit; + } + +Exit: + + if (!NT_SUCCESS(status)) { + // + // log event write failed + // + EventWriteWriteFail(&activity, WdfIoQueueGetDevice(Queue), status); + + WdfRequestCompleteWithInformation(Request, status, 0); + } + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_WRITE, "<-- OsrFxEvtIoWrite\n"); + + return; +} + +VOID +EvtRequestWriteCompletionRoutine( + _In_ WDFREQUEST Request, + _In_ WDFIOTARGET Target, + _In_ PWDF_REQUEST_COMPLETION_PARAMS CompletionParams, + _In_ WDFCONTEXT Context + ) +/*++ + +Routine Description: + + This is the completion routine for writes + If the irp completes with success, we check if we + need to recirculate this irp for another stage of + transfer. + +Arguments: + + Context - Driver supplied context + Device - Device handle + Request - Request handle + Params - request completion params + +Return Value: + None + +--*/ +{ + NTSTATUS status; + size_t bytesWritten = 0; + GUID activity = RequestToActivityId(Request); + PWDF_USB_REQUEST_COMPLETION_PARAMS usbCompletionParams; + + UNREFERENCED_PARAMETER(Target); + UNREFERENCED_PARAMETER(Context); + + status = CompletionParams->IoStatus.Status; + + // + // For usb devices, we should look at the Usb.Completion param. + // + usbCompletionParams = CompletionParams->Parameters.Usb.Completion; + + bytesWritten = usbCompletionParams->Parameters.PipeWrite.Length; + + if (NT_SUCCESS(status)){ + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_WRITE, + "Number of bytes written: %I64d\n", (INT64)bytesWritten); + } else { + TraceEvents(TRACE_LEVEL_ERROR, DBG_WRITE, + "Write failed: request Status 0x%x UsbdStatus 0x%x\n", + status, usbCompletionParams->UsbdStatus); + } + + // + // Log write stop event, using IRP activtiy ID if available or request + // handle otherwise + // + EventWriteWriteStop(&activity, + WdfIoQueueGetDevice(WdfRequestGetIoQueue(Request)), + bytesWritten, + status, + usbCompletionParams->UsbdStatus); + + + WdfRequestCompleteWithInformation(Request, status, bytesWritten); + + return; +} + + +VOID +OsrFxEvtIoStop( + _In_ WDFQUEUE Queue, + _In_ WDFREQUEST Request, + _In_ ULONG ActionFlags + ) +/*++ + +Routine Description: + + This callback is invoked on every inflight request when the device + is suspended or removed. Since our inflight read and write requests + are actually pending in the target device, we will just acknowledge + its presence. Until we acknowledge, complete, or requeue the requests + framework will wait before allowing the device suspend or remove to + proceeed. When the underlying USB stack gets the request to suspend or + remove, it will fail all the pending requests. + +Arguments: + + Queue - handle to queue object that is associated with the I/O request + + Request - handle to a request object + + ActionFlags - bitwise OR of one or more WDF_REQUEST_STOP_ACTION_FLAGS flags + +Return Value: + None + +--*/ +{ + UNREFERENCED_PARAMETER(Queue); + UNREFERENCED_PARAMETER(ActionFlags); + + if (ActionFlags & WdfRequestStopActionSuspend ) { + WdfRequestStopAcknowledge(Request, FALSE); // Don't requeue + } else if(ActionFlags & WdfRequestStopActionPurge) { + WdfRequestCancelSentRequest(Request); + } + return; +} + + diff --git a/usb/kmdf_fx2/driver/driver.c b/usb/kmdf_fx2/driver/driver.c new file mode 100644 index 000000000..d3b445734 --- /dev/null +++ b/usb/kmdf_fx2/driver/driver.c @@ -0,0 +1,299 @@ +/*++ + +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: + + Main module. + + This driver is for Open System Resources USB-FX2 Learning Kit designed + and built by OSR specifically for use in teaching software developers how to write + drivers for USB devices. + + The board supports a single configuration. The board automatically + detects the speed of the host controller, and supplies either the + high or full speed configuration based on the host controller's speed. + + The firmware supports 3 endpoints: + + Endpoint number 1 is used to indicate the state of the 8-switch + switch-pack on the OSR USB-FX2 board. A single byte representing + the switch state is sent (a) when the board is first started, + (b) when the board resumes after selective-suspend, + (c) whenever the state of the switches is changed. + + Endpoints 6 and 8 perform an internal loop-back function. + Data that is sent to the board at EP6 is returned to the host on EP8. + + For further information on the endpoints, please refer to the spec + http://www.osronline.com/hardware/OSRFX2_32.pdf. + + Vendor ID of the device is 0x4705 and Product ID is 0x210. + +Environment: + + Kernel mode only + +--*/ + +#include + +#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 trace.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 "driver.tmh" +#else +ULONG DebugLevel = TRACE_LEVEL_INFORMATION; +ULONG DebugFlag = 0xff; +#endif + +PFN_IO_GET_ACTIVITY_ID_IRP g_pIoGetActivityIdIrp; +PFN_IO_SET_DEVICE_INTERFACE_PROPERTY_DATA g_pIoSetDeviceInterfacePropertyData; + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT, DriverEntry) +#pragma alloc_text(PAGE, OsrFxEvtDriverContextCleanup) +#endif + +NTSTATUS +DriverEntry( + PDRIVER_OBJECT DriverObject, + PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Description: + DriverEntry initializes the driver and is the first routine called by the + system after the driver is loaded. + +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 or another NTSTATUS error code otherwise. + +--*/ +{ + WDF_DRIVER_CONFIG config; + NTSTATUS status; + WDF_OBJECT_ATTRIBUTES attributes; + UNICODE_STRING funcName; + + // + // Initialize WPP Tracing + // + WPP_INIT_TRACING( DriverObject, RegistryPath ); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, + "OSRUSBFX2 Driver Sample - Driver Framework Edition.\n"); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, + "Built %s %s\n", __DATE__, __TIME__); + + // + // IRP activity ID functions are available on some versions, save them into + // globals (or NULL if not available) + // + RtlInitUnicodeString(&funcName, L"IoGetActivityIdIrp"); + g_pIoGetActivityIdIrp = (PFN_IO_GET_ACTIVITY_ID_IRP) (ULONG_PTR) + MmGetSystemRoutineAddress(&funcName); + + // + // The Device interface property set is available on some version, save it + // into globals (or NULL if not available) + // + RtlInitUnicodeString(&funcName, L"IoSetDeviceInterfacePropertyData"); + g_pIoSetDeviceInterfacePropertyData = (PFN_IO_SET_DEVICE_INTERFACE_PROPERTY_DATA) (ULONG_PTR) + MmGetSystemRoutineAddress(&funcName); + + // + // Register with ETW (unified tracing) + // + EventRegisterOSRUSBFX2(); + + // + // 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, + OsrFxEvtDeviceAdd + ); + + // + // 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 = OsrFxEvtDriverContextCleanup; + + // + // Create a framework driver object to represent our driver. + // + status = WdfDriverCreate( + DriverObject, + RegistryPath, + &attributes, // Driver Object Attributes + &config, // Driver Config Info + WDF_NO_HANDLE // hDriver + ); + + if (!NT_SUCCESS(status)) { + + TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, + "WdfDriverCreate failed with status 0x%x\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); + EventUnregisterOSRUSBFX2(); + } + + return status; +} + +VOID +OsrFxEvtDriverContextCleanup( + WDFOBJECT Driver + ) +/*++ +Routine Description: + + Free resources allocated in DriverEntry that are not automatically + cleaned up by the framework. + +Arguments: + + Driver - handle to a WDF Driver object. + +Return Value: + + VOID. + +--*/ +{ + PAGED_CODE (); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, + "--> OsrFxEvtDriverContextCleanup\n"); + + WPP_CLEANUP( WdfDriverWdmGetDriverObject( (WDFDRIVER)Driver )); + + UNREFERENCED_PARAMETER(Driver); // For the case when WPP is not being used. + + EventUnregisterOSRUSBFX2(); +} + +#if !defined(EVENT_TRACING) + +VOID +TraceEvents ( + _In_ ULONG DebugPrintLevel, + _In_ ULONG DebugPrintFlag, + _Printf_format_string_ + _In_ PCSTR DebugMessage, + ... + ) + +/*++ + +Routine Description: + + Debug print for the sample driver. + +Arguments: + + DebugPrintLevel - print level between 0 and 3, with 3 the most verbose + DebugPrintFlag - message mask + DebugMessage - format string of the message to print + ... - values used by the format string + +Return Value: + + None. + + --*/ + { +#if DBG +#define TEMP_BUFFER_SIZE 1024 + 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 0x%x\n", status); + return; + } + if (DebugPrintLevel <= TRACE_LEVEL_ERROR || + (DebugPrintLevel <= DebugLevel && + ((DebugPrintFlag & DebugFlag) == DebugPrintFlag))) { + DbgPrint("%s %s", _DRIVER_NAME_, debugMessageBuffer); + } + } + va_end(list); + + return; +#else + UNREFERENCED_PARAMETER(DebugPrintLevel); + UNREFERENCED_PARAMETER(DebugPrintFlag); + UNREFERENCED_PARAMETER(DebugMessage); +#endif +} + +#endif + + + + diff --git a/usb/kmdf_fx2/driver/interrupt.c b/usb/kmdf_fx2/driver/interrupt.c new file mode 100644 index 000000000..00ea35505 --- /dev/null +++ b/usb/kmdf_fx2/driver/interrupt.c @@ -0,0 +1,184 @@ +/*++ + +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: + + Interrupt.c + +Abstract: + + This modules has routines configure a continuous reader on an + interrupt pipe to asynchronously read toggle switch states. + +Environment: + + Kernel mode + +--*/ + +#include + +#if defined(EVENT_TRACING) +#include "interrupt.tmh" +#endif + + +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +OsrFxConfigContReaderForInterruptEndPoint( + _In_ PDEVICE_CONTEXT DeviceContext + ) +/*++ + +Routine Description: + + This routine configures a continuous reader on the + interrupt endpoint. It's called from the PrepareHarware event. + +Arguments: + + +Return Value: + + NT status value + +--*/ +{ + WDF_USB_CONTINUOUS_READER_CONFIG contReaderConfig; + NTSTATUS status; + + WDF_USB_CONTINUOUS_READER_CONFIG_INIT(&contReaderConfig, + OsrFxEvtUsbInterruptPipeReadComplete, + DeviceContext, // Context + sizeof(UCHAR)); // TransferLength + + contReaderConfig.EvtUsbTargetPipeReadersFailed = OsrFxEvtUsbInterruptReadersFailed; + + // + // Reader requests are not posted to the target automatically. + // Driver must explictly call WdfIoTargetStart to kick start the + // reader. In this sample, it's done in D0Entry. + // By defaut, framework queues two requests to the target + // endpoint. Driver can configure up to 10 requests with CONFIG macro. + // + status = WdfUsbTargetPipeConfigContinuousReader(DeviceContext->InterruptPipe, + &contReaderConfig); + + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "OsrFxConfigContReaderForInterruptEndPoint failed %x\n", + status); + return status; + } + + return status; +} + +VOID +OsrFxEvtUsbInterruptPipeReadComplete( + WDFUSBPIPE Pipe, + WDFMEMORY Buffer, + size_t NumBytesTransferred, + WDFCONTEXT Context + ) +/*++ + +Routine Description: + + This the completion routine of the continour reader. This can + called concurrently on multiprocessor system if there are + more than one readers configured. So make sure to protect + access to global resources. + +Arguments: + + Buffer - This buffer is freed when this call returns. + If the driver wants to delay processing of the buffer, it + can take an additional referrence. + + Context - Provided in the WDF_USB_CONTINUOUS_READER_CONFIG_INIT macro + +Return Value: + + NT status value + +--*/ +{ + PUCHAR switchState = NULL; + WDFDEVICE device; + PDEVICE_CONTEXT pDeviceContext = Context; + + UNREFERENCED_PARAMETER(Pipe); + + device = WdfObjectContextGetObject(pDeviceContext); + + // + // Make sure that there is data in the read packet. Depending on the device + // specification, it is possible for it to return a 0 length read in + // certain conditions. + // + + if (NumBytesTransferred == 0) { + TraceEvents(TRACE_LEVEL_WARNING, DBG_INIT, + "OsrFxEvtUsbInterruptPipeReadComplete Zero length read " + "occured on the Interrupt Pipe's Continuous Reader\n" + ); + return; + } + + + NT_ASSERT(NumBytesTransferred == sizeof(UCHAR)); + + switchState = WdfMemoryGetBuffer(Buffer, NULL); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, + "OsrFxEvtUsbInterruptPipeReadComplete SwitchState %x\n", + *switchState); + + pDeviceContext->CurrentSwitchState = *switchState; + + // + // Handle any pending Interrupt Message IOCTLs. Note that the OSR USB device + // will generate an interrupt message when the the device resumes from a low + // power state. So if the Interrupt Message IOCTL was sent after the device + // has gone to a low power state, the pending Interrupt Message IOCTL will + // get completed in the function call below, before the user twiddles the + // dip switches on the OSR USB device. If this is not the desired behavior + // for your driver, then you could handle this condition by maintaining a + // state variable on D0Entry to track interrupt messages caused by power up. + // + OsrUsbIoctlGetInterruptMessage(device, STATUS_SUCCESS); + +} + +BOOLEAN +OsrFxEvtUsbInterruptReadersFailed( + _In_ WDFUSBPIPE Pipe, + _In_ NTSTATUS Status, + _In_ USBD_STATUS UsbdStatus + ) +{ + WDFDEVICE device = WdfIoTargetGetDevice(WdfUsbTargetPipeGetIoTarget(Pipe)); + PDEVICE_CONTEXT pDeviceContext = GetDeviceContext(device); + + UNREFERENCED_PARAMETER(UsbdStatus); + + // + // Clear the current switch state. + // + pDeviceContext->CurrentSwitchState = 0; + + // + // Service the pending interrupt switch change request + // + OsrUsbIoctlGetInterruptMessage(device, Status); + + return TRUE; +} + diff --git a/usb/kmdf_fx2/driver/ioctl.c b/usb/kmdf_fx2/driver/ioctl.c new file mode 100644 index 000000000..0f29dc8cb --- /dev/null +++ b/usb/kmdf_fx2/driver/ioctl.c @@ -0,0 +1,1057 @@ +/*++ + +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: + + USB device driver for OSR USB-FX2 Learning Kit + +Environment: + + Kernel mode only + +--*/ + +#include + +#if defined(EVENT_TRACING) +#include "ioctl.tmh" +#endif + +#pragma alloc_text(PAGE, OsrFxEvtIoDeviceControl) +#pragma alloc_text(PAGE, ResetPipe) +#pragma alloc_text(PAGE, ResetDevice) +#pragma alloc_text(PAGE, ReenumerateDevice) +#pragma alloc_text(PAGE, GetBarGraphState) +#pragma alloc_text(PAGE, SetBarGraphState) +#pragma alloc_text(PAGE, GetSevenSegmentState) +#pragma alloc_text(PAGE, SetSevenSegmentState) +#pragma alloc_text(PAGE, GetSwitchState) + +VOID +OsrFxEvtIoDeviceControl( + _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 + +--*/ +{ + WDFDEVICE device; + PDEVICE_CONTEXT pDevContext; + size_t bytesReturned = 0; + PBAR_GRAPH_STATE barGraphState = NULL; + PSWITCH_STATE switchState = NULL; + PUCHAR sevenSegment = NULL; + BOOLEAN requestPending = FALSE; + NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST; + + UNREFERENCED_PARAMETER(InputBufferLength); + UNREFERENCED_PARAMETER(OutputBufferLength); + + PAGED_CODE(); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_IOCTL, "--> OsrFxEvtIoDeviceControl\n"); + // + // initialize variables + // + device = WdfIoQueueGetDevice(Queue); + pDevContext = GetDeviceContext(device); + + switch(IoControlCode) { + + case IOCTL_OSRUSBFX2_GET_CONFIG_DESCRIPTOR: { + + PUSB_CONFIGURATION_DESCRIPTOR configurationDescriptor = NULL; + USHORT requiredSize = 0; + + // + // First get the size of the config descriptor + // + status = WdfUsbTargetDeviceRetrieveConfigDescriptor( + pDevContext->UsbDevice, + NULL, + &requiredSize); + + if (status != STATUS_BUFFER_TOO_SMALL) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, + "WdfUsbTargetDeviceRetrieveConfigDescriptor failed 0x%x\n", status); + break; + } + + // + // Get the buffer - make sure the buffer is big enough + // + status = WdfRequestRetrieveOutputBuffer(Request, + (size_t)requiredSize, // MinimumRequired + &configurationDescriptor, + NULL); + if(!NT_SUCCESS(status)){ + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, + "WdfRequestRetrieveOutputBuffer failed 0x%x\n", status); + break; + } + + status = WdfUsbTargetDeviceRetrieveConfigDescriptor( + pDevContext->UsbDevice, + configurationDescriptor, + &requiredSize); + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, + "WdfUsbTargetDeviceRetrieveConfigDescriptor failed 0x%x\n", status); + break; + } + + bytesReturned = requiredSize; + + } + break; + + case IOCTL_OSRUSBFX2_RESET_DEVICE: + + status = ResetDevice(device); + break; + + case IOCTL_OSRUSBFX2_REENUMERATE_DEVICE: + + // + // Otherwise, call our function to reenumerate the + // device + // + status = ReenumerateDevice(pDevContext); + + bytesReturned = 0; + break; + + case IOCTL_OSRUSBFX2_GET_BAR_GRAPH_DISPLAY: + + // + // Make sure the caller's output buffer is large enough + // to hold the state of the bar graph + // + status = WdfRequestRetrieveOutputBuffer(Request, + sizeof(BAR_GRAPH_STATE), + &barGraphState, + NULL); + + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, + "User's output buffer is too small for this IOCTL, expecting an BAR_GRAPH_STATE\n"); + break; + } + // + // Call our function to get the bar graph state + // + status = GetBarGraphState(pDevContext, barGraphState); + + // + // If we succeeded return the user their data + // + if (NT_SUCCESS(status)) { + + bytesReturned = sizeof(BAR_GRAPH_STATE); + + } else { + + bytesReturned = 0; + + } + break; + + case IOCTL_OSRUSBFX2_SET_BAR_GRAPH_DISPLAY: + + status = WdfRequestRetrieveInputBuffer(Request, + sizeof(BAR_GRAPH_STATE), + &barGraphState, + NULL); + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, + "User's input buffer is too small for this IOCTL, expecting an BAR_GRAPH_STATE\n"); + break; + } + + // + // Call our routine to set the bar graph state + // + status = SetBarGraphState(pDevContext, barGraphState); + + // + // There's no data returned for this call + // + bytesReturned = 0; + break; + + case IOCTL_OSRUSBFX2_GET_7_SEGMENT_DISPLAY: + + status = WdfRequestRetrieveOutputBuffer(Request, + sizeof(UCHAR), + &sevenSegment, + NULL); + + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, + "User's output buffer is too small for this IOCTL, expecting an UCHAR\n"); + break; + } + + // + // Call our function to get the 7 segment state + // + status = GetSevenSegmentState(pDevContext, sevenSegment); + + // + // If we succeeded return the user their data + // + if (NT_SUCCESS(status)) { + + bytesReturned = sizeof(UCHAR); + + } else { + + bytesReturned = 0; + + } + break; + + case IOCTL_OSRUSBFX2_SET_7_SEGMENT_DISPLAY: + + status = WdfRequestRetrieveInputBuffer(Request, + sizeof(UCHAR), + &sevenSegment, + NULL); + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, + "User's input buffer is too small for this IOCTL, expecting an UCHAR\n"); + bytesReturned = sizeof(UCHAR); + break; + } + + // + // Call our routine to set the 7 segment state + // + status = SetSevenSegmentState(pDevContext, sevenSegment); + + // + // There's no data returned for this call + // + bytesReturned = 0; + break; + + case IOCTL_OSRUSBFX2_READ_SWITCHES: + + status = WdfRequestRetrieveOutputBuffer(Request, + sizeof(SWITCH_STATE), + &switchState, + NULL);// BufferLength + + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, + "User's output buffer is too small for this IOCTL, expecting a SWITCH_STATE\n"); + bytesReturned = sizeof(SWITCH_STATE); + break; + + } + + // + // Call our routine to get the state of the switches + // + status = GetSwitchState(pDevContext, switchState); + + // + // If successful, return the user their data + // + if (NT_SUCCESS(status)) { + + bytesReturned = sizeof(SWITCH_STATE); + + } else { + // + // Don't return any data + // + bytesReturned = 0; + } + break; + + case IOCTL_OSRUSBFX2_GET_INTERRUPT_MESSAGE: + + // + // Forward the request to an interrupt message queue and dont complete + // the request until an interrupt from the USB device occurs. + // + status = WdfRequestForwardToIoQueue(Request, pDevContext->InterruptMsgQueue); + if (NT_SUCCESS(status)) { + requestPending = TRUE; + } + + break; + + default : + status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + + if (requestPending == FALSE) { + WdfRequestCompleteWithInformation(Request, status, bytesReturned); + } + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_IOCTL, "<-- OsrFxEvtIoDeviceControl\n"); + + return; +} + +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +ResetPipe( + _In_ WDFUSBPIPE Pipe + ) +/*++ + +Routine Description: + + This routine resets the pipe. + +Arguments: + + Pipe - framework pipe handle + +Return Value: + + NT status value + +--*/ +{ + NTSTATUS status; + + PAGED_CODE(); + + // + // This routine synchronously submits a URB_FUNCTION_RESET_PIPE + // request down the stack. + // + status = WdfUsbTargetPipeResetSynchronously(Pipe, + WDF_NO_HANDLE, // WDFREQUEST + NULL // PWDF_REQUEST_SEND_OPTIONS + ); + + if (NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_IOCTL, "ResetPipe - success\n"); + status = STATUS_SUCCESS; + } + else { + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, "ResetPipe - failed\n"); + } + + return status; +} + +VOID +StopAllPipes( + IN PDEVICE_CONTEXT DeviceContext + ) +{ + WdfIoTargetStop(WdfUsbTargetPipeGetIoTarget(DeviceContext->InterruptPipe), + WdfIoTargetCancelSentIo); + WdfIoTargetStop(WdfUsbTargetPipeGetIoTarget(DeviceContext->BulkReadPipe), + WdfIoTargetCancelSentIo); + WdfIoTargetStop(WdfUsbTargetPipeGetIoTarget(DeviceContext->BulkWritePipe), + WdfIoTargetCancelSentIo); +} + +NTSTATUS +StartAllPipes( + IN PDEVICE_CONTEXT DeviceContext + ) +{ + NTSTATUS status; + + status = WdfIoTargetStart(WdfUsbTargetPipeGetIoTarget(DeviceContext->InterruptPipe)); + if (!NT_SUCCESS(status)) { + return status; + } + + status = WdfIoTargetStart(WdfUsbTargetPipeGetIoTarget(DeviceContext->BulkReadPipe)); + if (!NT_SUCCESS(status)) { + return status; + } + + status = WdfIoTargetStart(WdfUsbTargetPipeGetIoTarget(DeviceContext->BulkWritePipe)); + if (!NT_SUCCESS(status)) { + return status; + } + + return status; +} + +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +ResetDevice( + _In_ WDFDEVICE Device + ) +/*++ + +Routine Description: + + This routine calls WdfUsbTargetDeviceResetPortSynchronously to reset the device if it's still + connected. + +Arguments: + + Device - Handle to a framework device + +Return Value: + + NT status value + +--*/ +{ + PDEVICE_CONTEXT pDeviceContext; + NTSTATUS status; + + PAGED_CODE(); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_IOCTL, "--> ResetDevice\n"); + + pDeviceContext = GetDeviceContext(Device); + + // + // A NULL timeout indicates an infinite wake + // + status = WdfWaitLockAcquire(pDeviceContext->ResetDeviceWaitLock, NULL); + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, "ResetDevice - could not acquire lock\n"); + return status; + } + + StopAllPipes(pDeviceContext); + + status = WdfUsbTargetDeviceResetPortSynchronously(pDeviceContext->UsbDevice); + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, "ResetDevice failed - 0x%x\n", status); + } + + status = StartAllPipes(pDeviceContext); + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, "Failed to start all pipes - 0x%x\n", status); + } + + WdfWaitLockRelease(pDeviceContext->ResetDeviceWaitLock); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_IOCTL, "<-- ResetDevice\n"); + return status; +} + +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +ReenumerateDevice( + _In_ PDEVICE_CONTEXT DevContext + ) +/*++ + +Routine Description + + This routine re-enumerates the USB device. + +Arguments: + + pDevContext - One of our device extensions + +Return Value: + + NT status value + +--*/ +{ + NTSTATUS status; + WDF_USB_CONTROL_SETUP_PACKET controlSetupPacket; + WDF_REQUEST_SEND_OPTIONS sendOptions; + GUID activity; + + PAGED_CODE(); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL,"--> ReenumerateDevice\n"); + + WDF_REQUEST_SEND_OPTIONS_INIT( + &sendOptions, + WDF_REQUEST_SEND_OPTION_TIMEOUT + ); + + WDF_REQUEST_SEND_OPTIONS_SET_TIMEOUT( + &sendOptions, + DEFAULT_CONTROL_TRANSFER_TIMEOUT + ); + + WDF_USB_CONTROL_SETUP_PACKET_INIT_VENDOR(&controlSetupPacket, + BmRequestHostToDevice, + BmRequestToDevice, + USBFX2LK_REENUMERATE, // Request + 0, // Value + 0); // Index + + + status = WdfUsbTargetDeviceSendControlTransferSynchronously( + DevContext->UsbDevice, + WDF_NO_HANDLE, // Optional WDFREQUEST + &sendOptions, + &controlSetupPacket, + NULL, // MemoryDescriptor + NULL); // BytesTransferred + + if(!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, + "ReenumerateDevice: Failed to Reenumerate - 0x%x \n", status); + } + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL,"<-- ReenumerateDevice\n"); + + // + // Send event to eventlog + // + + activity = DeviceToActivityId(WdfObjectContextGetObject(DevContext)); + EventWriteDeviceReenumerated(&activity, + DevContext->DeviceName, + DevContext->Location, + status); + + return status; + +} + +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +GetBarGraphState( + _In_ PDEVICE_CONTEXT DevContext, + _Out_ PBAR_GRAPH_STATE BarGraphState + ) +/*++ + +Routine Description + + This routine gets the state of the bar graph on the board + +Arguments: + + DevContext - One of our device extensions + + BarGraphState - Struct that receives the bar graph's state + +Return Value: + + NT status value + +--*/ +{ + NTSTATUS status; + WDF_USB_CONTROL_SETUP_PACKET controlSetupPacket; + WDF_REQUEST_SEND_OPTIONS sendOptions; + WDF_MEMORY_DESCRIPTOR memDesc; + ULONG bytesTransferred; + + PAGED_CODE(); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL, "--> GetBarGraphState\n"); + + WDF_REQUEST_SEND_OPTIONS_INIT( + &sendOptions, + WDF_REQUEST_SEND_OPTION_TIMEOUT + ); + + WDF_REQUEST_SEND_OPTIONS_SET_TIMEOUT( + &sendOptions, + DEFAULT_CONTROL_TRANSFER_TIMEOUT + ); + + WDF_USB_CONTROL_SETUP_PACKET_INIT_VENDOR(&controlSetupPacket, + BmRequestDeviceToHost, + BmRequestToDevice, + USBFX2LK_READ_BARGRAPH_DISPLAY, // Request + 0, // Value + 0); // Index + + // + // Set the buffer to 0, the board will OR in everything that is set + // + BarGraphState->BarsAsUChar = 0; + + + WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&memDesc, + BarGraphState, + sizeof(BAR_GRAPH_STATE)); + + status = WdfUsbTargetDeviceSendControlTransferSynchronously( + DevContext->UsbDevice, + WDF_NO_HANDLE, // Optional WDFREQUEST + &sendOptions, + &controlSetupPacket, + &memDesc, + &bytesTransferred); + + if(!NT_SUCCESS(status)) { + + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, + "GetBarGraphState: Failed to GetBarGraphState - 0x%x \n", status); + + } else { + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL, + "GetBarGraphState: LED mask is 0x%x\n", BarGraphState->BarsAsUChar); + } + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL, "<-- GetBarGraphState\n"); + + return status; + +} + +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +SetBarGraphState( + _In_ PDEVICE_CONTEXT DevContext, + _In_ PBAR_GRAPH_STATE BarGraphState + ) +/*++ + +Routine Description + + This routine sets the state of the bar graph on the board + +Arguments: + + DevContext - One of our device extensions + + BarGraphState - Struct that describes the bar graph's desired state + +Return Value: + + NT status value + +--*/ +{ + NTSTATUS status; + WDF_USB_CONTROL_SETUP_PACKET controlSetupPacket; + WDF_REQUEST_SEND_OPTIONS sendOptions; + WDF_MEMORY_DESCRIPTOR memDesc; + ULONG bytesTransferred; + + PAGED_CODE(); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL, "--> SetBarGraphState\n"); + + WDF_REQUEST_SEND_OPTIONS_INIT( + &sendOptions, + WDF_REQUEST_SEND_OPTION_TIMEOUT + ); + + WDF_REQUEST_SEND_OPTIONS_SET_TIMEOUT( + &sendOptions, + DEFAULT_CONTROL_TRANSFER_TIMEOUT + ); + + WDF_USB_CONTROL_SETUP_PACKET_INIT_VENDOR(&controlSetupPacket, + BmRequestHostToDevice, + BmRequestToDevice, + USBFX2LK_SET_BARGRAPH_DISPLAY, // Request + 0, // Value + 0); // Index + + WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&memDesc, + BarGraphState, + sizeof(BAR_GRAPH_STATE)); + + status = WdfUsbTargetDeviceSendControlTransferSynchronously( + DevContext->UsbDevice, + NULL, // Optional WDFREQUEST + &sendOptions, + &controlSetupPacket, + &memDesc, + &bytesTransferred); + + if(!NT_SUCCESS(status)) { + + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, + "SetBarGraphState: Failed - 0x%x \n", status); + + } else { + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL, + "SetBarGraphState: LED mask is 0x%x\n", BarGraphState->BarsAsUChar); + } + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL, "<-- SetBarGraphState\n"); + + return status; + +} + +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +GetSevenSegmentState( + _In_ PDEVICE_CONTEXT DevContext, + _Out_ PUCHAR SevenSegment + ) +/*++ + +Routine Description + + This routine gets the state of the 7 segment display on the board + by sending a synchronous control command. + + NOTE: It's not a good practice to send a synchronous request in the + context of the user thread because if the transfer takes long + time to complete, you end up holding the user thread. + + I'm choosing to do synchronous transfer because a) I know this one + completes immediately b) and for demonstration. + +Arguments: + + DevContext - One of our device extensions + + SevenSegment - receives the state of the 7 segment display + +Return Value: + + NT status value + +--*/ +{ + NTSTATUS status; + WDF_USB_CONTROL_SETUP_PACKET controlSetupPacket; + WDF_REQUEST_SEND_OPTIONS sendOptions; + + WDF_MEMORY_DESCRIPTOR memDesc; + ULONG bytesTransferred; + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL, "GetSetSevenSegmentState: Enter\n"); + + PAGED_CODE(); + + WDF_REQUEST_SEND_OPTIONS_INIT( + &sendOptions, + WDF_REQUEST_SEND_OPTION_TIMEOUT + ); + + WDF_REQUEST_SEND_OPTIONS_SET_TIMEOUT( + &sendOptions, + DEFAULT_CONTROL_TRANSFER_TIMEOUT + ); + + WDF_USB_CONTROL_SETUP_PACKET_INIT_VENDOR(&controlSetupPacket, + BmRequestDeviceToHost, + BmRequestToDevice, + USBFX2LK_READ_7SEGMENT_DISPLAY, // Request + 0, // Value + 0); // Index + + // + // Set the buffer to 0, the board will OR in everything that is set + // + *SevenSegment = 0; + + WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&memDesc, + SevenSegment, + sizeof(UCHAR)); + + status = WdfUsbTargetDeviceSendControlTransferSynchronously( + DevContext->UsbDevice, + NULL, // Optional WDFREQUEST + &sendOptions, + &controlSetupPacket, + &memDesc, + &bytesTransferred); + + if(!NT_SUCCESS(status)) { + + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, + "GetSevenSegmentState: Failed to get 7 Segment state - 0x%x \n", status); + } else { + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL, + "GetSevenSegmentState: 7 Segment mask is 0x%x\n", *SevenSegment); + } + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL, "GetSetSevenSegmentState: Exit\n"); + + return status; + +} + +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +SetSevenSegmentState( + _In_ PDEVICE_CONTEXT DevContext, + _In_ PUCHAR SevenSegment + ) +/*++ + +Routine Description + + This routine sets the state of the 7 segment display on the board + +Arguments: + + DevContext - One of our device extensions + + SevenSegment - desired state of the 7 segment display + +Return Value: + + NT status value + +--*/ +{ + NTSTATUS status; + WDF_USB_CONTROL_SETUP_PACKET controlSetupPacket; + WDF_REQUEST_SEND_OPTIONS sendOptions; + WDF_MEMORY_DESCRIPTOR memDesc; + ULONG bytesTransferred; + + PAGED_CODE(); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL, "--> SetSevenSegmentState\n"); + + WDF_REQUEST_SEND_OPTIONS_INIT( + &sendOptions, + WDF_REQUEST_SEND_OPTION_TIMEOUT + ); + + WDF_REQUEST_SEND_OPTIONS_SET_TIMEOUT( + &sendOptions, + DEFAULT_CONTROL_TRANSFER_TIMEOUT + ); + + WDF_USB_CONTROL_SETUP_PACKET_INIT_VENDOR(&controlSetupPacket, + BmRequestHostToDevice, + BmRequestToDevice, + USBFX2LK_SET_7SEGMENT_DISPLAY, // Request + 0, // Value + 0); // Index + + WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&memDesc, + SevenSegment, + sizeof(UCHAR)); + + status = WdfUsbTargetDeviceSendControlTransferSynchronously( + DevContext->UsbDevice, + NULL, // Optional WDFREQUEST + &sendOptions, + &controlSetupPacket, + &memDesc, + &bytesTransferred); + + if(!NT_SUCCESS(status)) { + + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, + "SetSevenSegmentState: Failed to set 7 Segment state - 0x%x \n", status); + + } else { + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL, + "SetSevenSegmentState: 7 Segment mask is 0x%x\n", *SevenSegment); + + } + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL, "<-- SetSevenSegmentState\n"); + + return status; + +} + +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +GetSwitchState( + _In_ PDEVICE_CONTEXT DevContext, + _In_ PSWITCH_STATE SwitchState + ) +/*++ + +Routine Description + + This routine gets the state of the switches on the board + +Arguments: + + DevContext - One of our device extensions + +Return Value: + + NT status value + +--*/ +{ + NTSTATUS status; + WDF_USB_CONTROL_SETUP_PACKET controlSetupPacket; + WDF_REQUEST_SEND_OPTIONS sendOptions; + WDF_MEMORY_DESCRIPTOR memDesc; + ULONG bytesTransferred; + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL, "--> GetSwitchState\n"); + + PAGED_CODE(); + + WDF_REQUEST_SEND_OPTIONS_INIT( + &sendOptions, + WDF_REQUEST_SEND_OPTION_TIMEOUT + ); + + WDF_REQUEST_SEND_OPTIONS_SET_TIMEOUT( + &sendOptions, + DEFAULT_CONTROL_TRANSFER_TIMEOUT + ); + + WDF_USB_CONTROL_SETUP_PACKET_INIT_VENDOR(&controlSetupPacket, + BmRequestDeviceToHost, + BmRequestToDevice, + USBFX2LK_READ_SWITCHES, // Request + 0, // Value + 0); // Index + + SwitchState->SwitchesAsUChar = 0; + + WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&memDesc, + SwitchState, + sizeof(SWITCH_STATE)); + + status = WdfUsbTargetDeviceSendControlTransferSynchronously( + DevContext->UsbDevice, + NULL, // Optional WDFREQUEST + &sendOptions, + &controlSetupPacket, + &memDesc, + &bytesTransferred); + + if(!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, + "GetSwitchState: Failed to Get switches - 0x%x \n", status); + + } else { + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL, + "GetSwitchState: Switch mask is 0x%x\n", SwitchState->SwitchesAsUChar); + } + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL, "<-- GetSwitchState\n"); + + return status; + +} + + +VOID +OsrUsbIoctlGetInterruptMessage( + _In_ WDFDEVICE Device, + _In_ NTSTATUS ReaderStatus + ) +/*++ + +Routine Description + + This method handles the completion of the pended request for the IOCTL + IOCTL_OSRUSBFX2_GET_INTERRUPT_MESSAGE. + +Arguments: + + Device - Handle to a framework device. + +Return Value: + + None. + +--*/ +{ + NTSTATUS status; + WDFREQUEST request; + PDEVICE_CONTEXT pDevContext; + size_t bytesReturned = 0; + PSWITCH_STATE switchState = NULL; + + pDevContext = GetDeviceContext(Device); + + do { + + // + // Check if there are any pending requests in the Interrupt Message Queue. + // If a request is found then complete the pending request. + // + status = WdfIoQueueRetrieveNextRequest(pDevContext->InterruptMsgQueue, &request); + + if (NT_SUCCESS(status)) { + status = WdfRequestRetrieveOutputBuffer(request, + sizeof(SWITCH_STATE), + &switchState, + NULL);// BufferLength + + if (!NT_SUCCESS(status)) { + + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, + "User's output buffer is too small for this IOCTL, expecting a SWITCH_STATE\n"); + bytesReturned = sizeof(SWITCH_STATE); + + } else { + + // + // Copy the state information saved by the continuous reader. + // + if (NT_SUCCESS(ReaderStatus)) { + switchState->SwitchesAsUChar = pDevContext->CurrentSwitchState; + bytesReturned = sizeof(SWITCH_STATE); + } else { + bytesReturned = 0; + } + } + + // + // Complete the request. If we failed to get the output buffer then + // complete with that status. Otherwise complete with the status from the reader. + // + WdfRequestCompleteWithInformation(request, + NT_SUCCESS(status) ? ReaderStatus : status, + bytesReturned); + status = STATUS_SUCCESS; + + } else if (status != STATUS_NO_MORE_ENTRIES) { + KdPrint(("WdfIoQueueRetrieveNextRequest status %08x\n", status)); + } + + request = NULL; + + } while (status == STATUS_SUCCESS); + + return; + +} + + diff --git a/usb/kmdf_fx2/driver/osrusbfx2.h b/usb/kmdf_fx2/driver/osrusbfx2.h new file mode 100644 index 000000000..909e347d4 --- /dev/null +++ b/usb/kmdf_fx2/driver/osrusbfx2.h @@ -0,0 +1,335 @@ +/*++ + +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: + + Contains structure definitions and function prototypes private to + the driver. + +Environment: + + Kernel mode + +--*/ + +#include +#include +#include "usbdi.h" +#include "usbdlib.h" +#include "public.h" +#include "driverspecs.h" +#include +#include +#define NTSTRSAFE_LIB +#include + +#include "trace.h" + +// +// Include auto-generated ETW event functions (created by MC.EXE from +// osrusbfx2.man) +// +#include "fx2Events.h" + +#ifndef _PRIVATE_H +#define _PRIVATE_H + +#define POOL_TAG (ULONG) 'FRSO' +#define _DRIVER_NAME_ "OSRUSBFX2" + +#define TEST_BOARD_TRANSFER_BUFFER_SIZE (64*1024) +#define DEVICE_DESC_LENGTH 256 + +extern const __declspec(selectany) LONGLONG DEFAULT_CONTROL_TRANSFER_TIMEOUT = 5 * -1 * WDF_TIMEOUT_TO_SEC; + +// +// Define the vendor commands supported by our device +// +#define USBFX2LK_READ_7SEGMENT_DISPLAY 0xD4 +#define USBFX2LK_READ_SWITCHES 0xD6 +#define USBFX2LK_READ_BARGRAPH_DISPLAY 0xD7 +#define USBFX2LK_SET_BARGRAPH_DISPLAY 0xD8 +#define USBFX2LK_IS_HIGH_SPEED 0xD9 +#define USBFX2LK_REENUMERATE 0xDA +#define USBFX2LK_SET_7SEGMENT_DISPLAY 0xDB + +// +// Define the features that we can clear +// and set on our device +// +#define USBFX2LK_FEATURE_EPSTALL 0x00 +#define USBFX2LK_FEATURE_WAKE 0x01 + +// +// Order of endpoints in the interface descriptor +// +#define INTERRUPT_IN_ENDPOINT_INDEX 0 +#define BULK_OUT_ENDPOINT_INDEX 1 +#define BULK_IN_ENDPOINT_INDEX 2 + +// +// A structure representing the instance information associated with +// this particular device. +// + +typedef struct _DEVICE_CONTEXT { + + WDFUSBDEVICE UsbDevice; + + WDFUSBINTERFACE UsbInterface; + + WDFUSBPIPE BulkReadPipe; + + WDFUSBPIPE BulkWritePipe; + + WDFUSBPIPE InterruptPipe; + + WDFWAITLOCK ResetDeviceWaitLock; + + UCHAR CurrentSwitchState; + + WDFQUEUE InterruptMsgQueue; + + ULONG UsbDeviceTraits; + + // + // The following fields are used during event logging to + // report the events relative to this specific instance + // of the device. + // + + WDFMEMORY DeviceNameMemory; + PCWSTR DeviceName; + + WDFMEMORY LocationMemory; + PCWSTR Location; + +} DEVICE_CONTEXT, *PDEVICE_CONTEXT; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(DEVICE_CONTEXT, GetDeviceContext) + +extern ULONG DebugLevel; + +typedef +NTSTATUS +(*PFN_IO_GET_ACTIVITY_ID_IRP) ( + _In_ PIRP Irp, + _Out_ LPGUID Guid + ); + +typedef +NTSTATUS +(*PFN_IO_SET_DEVICE_INTERFACE_PROPERTY_DATA) ( + _In_ PUNICODE_STRING SymbolicLinkName, + _In_ CONST DEVPROPKEY *PropertyKey, + _In_ LCID Lcid, + _In_ ULONG Flags, + _In_ DEVPROPTYPE Type, + _In_ ULONG Size, + _In_opt_ PVOID Data + ); + +// +// Global function pointer set in DriverEntry +// Check for NULL before using +// +extern PFN_IO_GET_ACTIVITY_ID_IRP g_pIoGetActivityIdIrp; + +extern PFN_IO_SET_DEVICE_INTERFACE_PROPERTY_DATA g_pIoSetDeviceInterfacePropertyData; + +DRIVER_INITIALIZE DriverEntry; + +EVT_WDF_OBJECT_CONTEXT_CLEANUP OsrFxEvtDriverContextCleanup; + +EVT_WDF_DRIVER_DEVICE_ADD OsrFxEvtDeviceAdd; + +EVT_WDF_DEVICE_PREPARE_HARDWARE OsrFxEvtDevicePrepareHardware; + +EVT_WDF_IO_QUEUE_IO_READ OsrFxEvtIoRead; + +EVT_WDF_IO_QUEUE_IO_WRITE OsrFxEvtIoWrite; + +EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL OsrFxEvtIoDeviceControl; + +EVT_WDF_REQUEST_COMPLETION_ROUTINE EvtRequestReadCompletionRoutine; + +EVT_WDF_REQUEST_COMPLETION_ROUTINE EvtRequestWriteCompletionRoutine; + +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +ResetPipe( + _In_ WDFUSBPIPE Pipe + ); + +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +ResetDevice( + _In_ WDFDEVICE Device + ); + +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +SelectInterfaces( + _In_ WDFDEVICE Device + ); + +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +ReenumerateDevice( + _In_ PDEVICE_CONTEXT DevContext + ); + +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +GetBarGraphState( + _In_ PDEVICE_CONTEXT DevContext, + _Out_ PBAR_GRAPH_STATE BarGraphState + ); + +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +SetBarGraphState( + _In_ PDEVICE_CONTEXT DevContext, + _In_ PBAR_GRAPH_STATE BarGraphState + ); + +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +GetSevenSegmentState( + _In_ PDEVICE_CONTEXT DevContext, + _Out_ PUCHAR SevenSegment + ); + +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +SetSevenSegmentState( + _In_ PDEVICE_CONTEXT DevContext, + _In_ PUCHAR SevenSegment + ); + +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +GetSwitchState( + _In_ PDEVICE_CONTEXT DevContext, + _In_ PSWITCH_STATE SwitchState + ); + +VOID +OsrUsbIoctlGetInterruptMessage( + _In_ WDFDEVICE Device, + _In_ NTSTATUS ReaderStatus + ); + +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +OsrFxSetPowerPolicy( + _In_ WDFDEVICE Device + ); + +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +OsrFxConfigContReaderForInterruptEndPoint( + _In_ PDEVICE_CONTEXT DeviceContext + ); + +EVT_WDF_USB_READER_COMPLETION_ROUTINE OsrFxEvtUsbInterruptPipeReadComplete; + +EVT_WDF_USB_READERS_FAILED OsrFxEvtUsbInterruptReadersFailed; + +EVT_WDF_IO_QUEUE_IO_STOP OsrFxEvtIoStop; + +EVT_WDF_DEVICE_D0_ENTRY OsrFxEvtDeviceD0Entry; + +EVT_WDF_DEVICE_D0_EXIT OsrFxEvtDeviceD0Exit; + +EVT_WDF_DEVICE_SELF_MANAGED_IO_FLUSH OsrFxEvtDeviceSelfManagedIoFlush; + +_IRQL_requires_(PASSIVE_LEVEL) +BOOLEAN +OsrFxReadFdoRegistryKeyValue( + _In_ PWDFDEVICE_INIT DeviceInit, + _In_ PWCHAR Name, + _Out_ PULONG Value + ); + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +OsrFxEnumerateChildren( + _In_ WDFDEVICE Device + ); + +_IRQL_requires_(PASSIVE_LEVEL) +VOID +GetDeviceEventLoggingNames( + _In_ WDFDEVICE Device + ); + +_IRQL_requires_(PASSIVE_LEVEL) +PCHAR +DbgDevicePowerString( + _In_ WDF_POWER_DEVICE_STATE Type + ); + + +_IRQL_requires_(PASSIVE_LEVEL) +USBD_STATUS +OsrFxValidateConfigurationDescriptor( + _In_reads_bytes_(BufferLength) PUSB_CONFIGURATION_DESCRIPTOR ConfigDesc, + _In_ ULONG BufferLength, + _Inout_ PUCHAR *Offset + ); + +FORCEINLINE +GUID +RequestToActivityId( + _In_ WDFREQUEST Request + ) +{ + GUID activity = {0}; + NTSTATUS status = STATUS_SUCCESS; + + if (g_pIoGetActivityIdIrp != NULL) { + + // + // Use activity ID generated by application (or IO manager) + // + status = g_pIoGetActivityIdIrp(WdfRequestWdmGetIrp(Request), &activity); + } + + if (g_pIoGetActivityIdIrp == NULL || !NT_SUCCESS(status)) { + + // + // Fall back to using the WDFREQUEST handle as the activity ID + // + RtlCopyMemory(&activity, &Request, sizeof(WDFREQUEST)); + } + + + return activity; +} + +FORCEINLINE +GUID +DeviceToActivityId( + _In_ WDFDEVICE Device + ) +{ + GUID activity = {0}; + RtlCopyMemory(&activity, &Device, sizeof(WDFDEVICE)); + return activity; +} + + +#endif + + diff --git a/usb/kmdf_fx2/driver/osrusbfx2.inx b/usb/kmdf_fx2/driver/osrusbfx2.inx new file mode 100644 index 000000000..6bb5ccc50 --- /dev/null +++ b/usb/kmdf_fx2/driver/osrusbfx2.inx @@ -0,0 +1,112 @@ +;/*++ +; +;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: +; +; OSRUSBFX2.INF +; +;Abstract: +; Installation inf for OSR USB-FX2 Learning Kit +; +;--*/ + +[Version] +Signature="$WINDOWS NT$" +Class=Sample +ClassGuid={78A1C341-4539-11d3-B88D-00C04FAD5171} +Provider=%ProviderName% +DriverVer=03/20/2003,5.00.3788 +CatalogFile=KmdfSamples.cat + + +; ================= Class section ===================== + +[ClassInstall32] +Addreg=SampleClassReg + +[SampleClassReg] +HKR,,,0,%ClassName% +HKR,,Icon,,-5 + + +; ================= Device section ===================== + +[Manufacturer] +%MfgName%=OSR,NT$ARCH$ + +[OSR.NT$ARCH$] +%USB\VID_045E&PID_930A.DeviceDesc%=osrusbfx2.Dev, USB\VID_0547&PID_1002 +%Switch.DeviceDesc%=Switch.Dev, {6FDE7521-1B65-48ae-B628-80BE62016026}\OsrUsbFxRawPdo + + +[osrusbfx2.Dev.NT] +CopyFiles=osrusbfx2.Files.Ext + +[Switch.Dev.NT] +;dummy section + +[Switch.Dev.NT.Services] +AddService = , %SPSVCINST_ASSOCSERVICE%, + +[osrusbfx2.Dev.NT.Services] +AddService = osrusbfx2, %SPSVCINST_ASSOCSERVICE%, osrusbfx2.AddService + +[osrusbfx2.AddService] +DisplayName = %osrusbfx2.SvcDesc% +ServiceType = 1 ; SERVICE_KERNEL_DRIVER +StartType = 3 ; SERVICE_DEMAND_START +ErrorControl = 1 ; SERVICE_ERROR_NORMAL +ServiceBinary = %10%\System32\Drivers\osrusbfx2.sys + +[osrusbfx2.Files.Ext] +osrusbfx2.sys + +[SourceDisksNames] +1=%Disk_Description%,,, + +[SourceDisksFiles] +osrusbfx2.sys = 1 + +[DestinationDirs] +DefaultDestDir = 12 + +;-------------- WDF Coinstaller installation + +[DestinationDirs] +CoInstaller_CopyFiles = 11 + +[osrusbfx2.Dev.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" + +[osrusbfx2.Dev.NT.Wdf] +KmdfService = osrusbfx2, osrusbfx2_wdfsect +[osrusbfx2_wdfsect] +KmdfLibraryVersion = $KMDFVERSION$ + +;---------------------------------------------------------------; + +[Strings] +ProviderName="TODO-Set-Provider" +MfgName="OSR" +Disk_Description="OSRUSBFX2 Installation Disk" +USB\VID_045E&PID_930A.DeviceDesc="WDF Sample Driver for OSR USB-FX2 Learning Kit" +osrusbfx2.SvcDesc="WDF Sample Driver for OSR USB-FX2 Learning Kit" +ClassName = "Sample Device" +Switch.DeviceDesc = "OsrUsbFX2 RawPdo For Switch" +SPSVCINST_ASSOCSERVICE= 0x00000002 diff --git a/usb/kmdf_fx2/driver/osrusbfx2.man b/usb/kmdf_fx2/driver/osrusbfx2.man new file mode 100644 index 000000000..176dfc896 --- /dev/null +++ b/usb/kmdf_fx2/driver/osrusbfx2.man @@ -0,0 +1,309 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/usb/kmdf_fx2/driver/osrusbfx2.rc b/usb/kmdf_fx2/driver/osrusbfx2.rc new file mode 100644 index 000000000..092ec1957 --- /dev/null +++ b/usb/kmdf_fx2/driver/osrusbfx2.rc @@ -0,0 +1,18 @@ +#include + +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "WDF Sample Driver for OSR USB-FX2 Learning Kit" +#define VER_INTERNALNAME_STR "osrusbfx2.sys" +#define VER_ORIGINALFILENAME_STR "osrusbfx2.sys" + +#include "common.ver" + +// +// Include auto-generated string resources (created by MC.EXE from +// osrusbfx2.man) +// + +#include "fx2Events.rc" diff --git a/usb/kmdf_fx2/driver/osrusbfx2.vcxproj b/usb/kmdf_fx2/driver/osrusbfx2.vcxproj new file mode 100644 index 000000000..29fe9c3a6 --- /dev/null +++ b/usb/kmdf_fx2/driver/osrusbfx2.vcxproj @@ -0,0 +1,269 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {E7ECAF21-2522-4E7D-8475-67C4A5E43F70} + $(MSBuildProjectName) + 1 + Debug + Win32 + {95AAF552-36F7-487B-A556-B969D823DC9F} + + + + 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 + TraceEvents(LEVEL,FLAGS,MSG,...) + {km-WdfDefault.tpl}*.tmh + + + $(InfArch) + true + .\$(IntDir)\osrusbfx2.inf + + + true + true + .\$(IntDir) + true + .\$(IntDir) + true + fx2Events + true + + + true + true + TraceEvents(LEVEL,FLAGS,MSG,...) + {km-WdfDefault.tpl}*.tmh + + + + osrusbfx2 + + + osrusbfx2 + + + osrusbfx2 + + + osrusbfx2 + + + + %(AdditionalIncludeDirectories);..\inc + %(PreprocessorDefinitions);EVENT_TRACING + + + %(AdditionalIncludeDirectories);..\inc + true + Level4 + %(PreprocessorDefinitions);EVENT_TRACING + + + %(AdditionalIncludeDirectories);..\inc + %(PreprocessorDefinitions);EVENT_TRACING + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ntstrsafe.lib;$(DDK_LIB_PATH)\usbd.lib + + + + + %(AdditionalIncludeDirectories);..\inc + %(PreprocessorDefinitions);EVENT_TRACING + + + %(AdditionalIncludeDirectories);..\inc + true + Level4 + %(PreprocessorDefinitions);EVENT_TRACING + + + %(AdditionalIncludeDirectories);..\inc + %(PreprocessorDefinitions);EVENT_TRACING + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ntstrsafe.lib;$(DDK_LIB_PATH)\usbd.lib + + + + + %(AdditionalIncludeDirectories);..\inc + %(PreprocessorDefinitions);EVENT_TRACING + + + %(AdditionalIncludeDirectories);..\inc + true + Level4 + %(PreprocessorDefinitions);EVENT_TRACING + + + %(AdditionalIncludeDirectories);..\inc + %(PreprocessorDefinitions);EVENT_TRACING + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ntstrsafe.lib;$(DDK_LIB_PATH)\usbd.lib + + + + + %(AdditionalIncludeDirectories);..\inc + %(PreprocessorDefinitions);EVENT_TRACING + + + %(AdditionalIncludeDirectories);..\inc + true + Level4 + %(PreprocessorDefinitions);EVENT_TRACING + + + %(AdditionalIncludeDirectories);..\inc + %(PreprocessorDefinitions);EVENT_TRACING + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ntstrsafe.lib;$(DDK_LIB_PATH)\usbd.lib + + + + 1 + + + 1 + + + 1 + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/usb/kmdf_fx2/driver/osrusbfx2.vcxproj.Filters b/usb/kmdf_fx2/driver/osrusbfx2.vcxproj.Filters new file mode 100644 index 000000000..296911ee2 --- /dev/null +++ b/usb/kmdf_fx2/driver/osrusbfx2.vcxproj.Filters @@ -0,0 +1,51 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {50246F0E-8E24-4871-83C5-D835604F0997} + + + h;hpp;hxx;hm;inl;inc;xsd + {3A59DDAC-0E70-44F4-BDA7-AF03A36ADAD4} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {E82DF9C5-AE82-4CFF-A0C5-AA939075A927} + + + inf;inv;inx;mof;mc; + {57D03874-86BB-4AC8-B719-A39C7A4EF75F} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Driver Files + + + + + Resource Files + + + Resource Files + + + \ No newline at end of file diff --git a/usb/kmdf_fx2/driver/trace.h b/usb/kmdf_fx2/driver/trace.h new file mode 100644 index 000000000..518ff5f17 --- /dev/null +++ b/usb/kmdf_fx2/driver/trace.h @@ -0,0 +1,115 @@ +/*++ + +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 !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_IOCTL 0x00000020 +#define DBG_WRITE 0x00000040 +#define DBG_READ 0x00000080 + + +VOID +TraceEvents ( + _In_ ULONG DebugPrintLevel, + _In_ ULONG DebugPrintFlag, + _Printf_format_string_ + _In_ PCSTR DebugMessage, + ... + ); + +#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. +// +// NOTE: If you are adopting this sample for your driver, please generate +// a new guid, using tools\other\i386\guidgen.exe present in the +// DDK. +// +// Name of the logger is OSRUSBFX2 and the guid is +// {D23A0C5A-D307-4f0e-AE8E-E2A355AD5DAB} +// (0xd23a0c5a, 0xd307, 0x4f0e, 0xae, 0x8e, 0xe2, 0xa3, 0x55, 0xad, 0x5d, 0xab); +// + +#define WPP_CHECK_FOR_NULL_STRING //to prevent exceptions due to NULL strings + +#define WPP_CONTROL_GUIDS \ + WPP_DEFINE_CONTROL_GUID(OsrUsbFxTraceGuid,(d23a0c5a,d307,4f0e,ae8e,E2A355AD5DAB), \ + 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_IOCTL) /* bit 5 = 0x00000020 */ \ + WPP_DEFINE_BIT(DBG_WRITE) /* bit 6 = 0x00000040 */ \ + WPP_DEFINE_BIT(DBG_READ) /* bit 7 = 0x00000080 */ \ + /* 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) + + +#endif + + + diff --git a/usb/kmdf_fx2/exe/dump.c b/usb/kmdf_fx2/exe/dump.c new file mode 100644 index 000000000..69074bf1e --- /dev/null +++ b/usb/kmdf_fx2/exe/dump.c @@ -0,0 +1,444 @@ +/*++ + +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: + + DUMP.C + +Abstract: + + Routines to dump the descriptors information in a human readable form. + +Environment: + + user mode only + +--*/ + +#include +#include +#include +#include "devioctl.h" + +#pragma warning(disable:4200) // +#pragma warning(disable:4201) // nameless struct/union +#pragma warning(disable:4214) // bit field types other than int + +#include +#include "usbdi.h" +#include "public.h" + +#pragma warning(default:4200) +#pragma warning(default:4201) +#pragma warning(default:4214) + +HANDLE +OpenDevice( + _In_ BOOL Synchronous + ); + + +char* +usbDescriptorTypeString(UCHAR bDescriptorType ) +/*++ +Routine Description: + + Called to get ascii string of USB descriptor + +Arguments: + + PUSB_ENDPOINT_DESCRIPTOR->bDescriptorType or + PUSB_DEVICE_DESCRIPTOR->bDescriptorType or + PUSB_INTERFACE_DESCRIPTOR->bDescriptorType or + PUSB_STRING_DESCRIPTOR->bDescriptorType or + PUSB_POWER_DESCRIPTOR->bDescriptorType or + PUSB_CONFIGURATION_DESCRIPTOR->bDescriptorType + +Return Value: + + ptr to string + +--*/ +{ + + switch(bDescriptorType) { + + case USB_DEVICE_DESCRIPTOR_TYPE: + return "USB_DEVICE_DESCRIPTOR_TYPE"; + + case USB_CONFIGURATION_DESCRIPTOR_TYPE: + return "USB_CONFIGURATION_DESCRIPTOR_TYPE"; + + + case USB_STRING_DESCRIPTOR_TYPE: + return "USB_STRING_DESCRIPTOR_TYPE"; + + + case USB_INTERFACE_DESCRIPTOR_TYPE: + return "USB_INTERFACE_DESCRIPTOR_TYPE"; + + + case USB_ENDPOINT_DESCRIPTOR_TYPE: + return "USB_ENDPOINT_DESCRIPTOR_TYPE"; + + +#ifdef USB_POWER_DESCRIPTOR_TYPE // this is the older definintion which is actually obsolete + // workaround for temporary bug in 98ddk, older USB100.h file + case USB_POWER_DESCRIPTOR_TYPE: + return "USB_POWER_DESCRIPTOR_TYPE"; +#endif + +#ifdef USB_RESERVED_DESCRIPTOR_TYPE // this is the current version of USB100.h as in NT5DDK + + case USB_RESERVED_DESCRIPTOR_TYPE: + return "USB_RESERVED_DESCRIPTOR_TYPE"; + + case USB_CONFIG_POWER_DESCRIPTOR_TYPE: + return "USB_CONFIG_POWER_DESCRIPTOR_TYPE"; + + case USB_INTERFACE_POWER_DESCRIPTOR_TYPE: + return "USB_INTERFACE_POWER_DESCRIPTOR_TYPE"; +#endif // for current nt5ddk version of USB100.h + + default: + return "??? UNKNOWN!!"; + } +} + + +char * +usbEndPointTypeString(UCHAR bmAttributes) +/*++ +Routine Description: + + Called to get ascii string of endpt descriptor type + +Arguments: + + PUSB_ENDPOINT_DESCRIPTOR->bmAttributes + +Return Value: + + ptr to string + +--*/ +{ + UINT typ = bmAttributes & USB_ENDPOINT_TYPE_MASK; + + + switch( typ) { + case USB_ENDPOINT_TYPE_INTERRUPT: + return "USB_ENDPOINT_TYPE_INTERRUPT"; + + case USB_ENDPOINT_TYPE_BULK: + return "USB_ENDPOINT_TYPE_BULK"; + + case USB_ENDPOINT_TYPE_ISOCHRONOUS: + return "USB_ENDPOINT_TYPE_ISOCHRONOUS"; + + case USB_ENDPOINT_TYPE_CONTROL: + return "USB_ENDPOINT_TYPE_CONTROL"; + + default: + return "??? UNKNOWN!!"; + } +} + + +char * +usbConfigAttributesString(UCHAR bmAttributes) +/*++ +Routine Description: + + Called to get ascii string of USB_CONFIGURATION_DESCRIPTOR attributes + +Arguments: + + PUSB_CONFIGURATION_DESCRIPTOR->bmAttributes + +Return Value: + + ptr to string + +--*/ +{ + UINT typ = bmAttributes & USB_CONFIG_POWERED_MASK; + + + switch( typ) { + + case USB_CONFIG_BUS_POWERED: + return "USB_CONFIG_BUS_POWERED"; + + case USB_CONFIG_SELF_POWERED: + return "USB_CONFIG_SELF_POWERED"; + + case USB_CONFIG_REMOTE_WAKEUP: + return "USB_CONFIG_REMOTE_WAKEUP"; + + + default: + return "??? UNKNOWN!!"; + } +} + + +void +print_USB_CONFIGURATION_DESCRIPTOR(PUSB_CONFIGURATION_DESCRIPTOR cd) +/*++ +Routine Description: + + Called to do formatted ascii dump to console of a USB config descriptor + +Arguments: + + ptr to USB configuration descriptor + +Return Value: + + none + +--*/ +{ + printf("\n===================\nUSB_CONFIGURATION_DESCRIPTOR\n"); + + printf( + "bLength = 0x%x, decimal %d\n", cd->bLength, cd->bLength + ); + + printf( + "bDescriptorType = 0x%x ( %s )\n", cd->bDescriptorType, + usbDescriptorTypeString( cd->bDescriptorType ) + ); + + printf( + "wTotalLength = 0x%x, decimal %d\n", cd->wTotalLength, cd->wTotalLength + ); + + printf( + "bNumInterfaces = 0x%x, decimal %d\n", cd->bNumInterfaces, cd->bNumInterfaces + ); + + printf( + "bConfigurationValue = 0x%x, decimal %d\n", + cd->bConfigurationValue, cd->bConfigurationValue + ); + + printf( + "iConfiguration = 0x%x, decimal %d\n", cd->iConfiguration, cd->iConfiguration + ); + + printf( + "bmAttributes = 0x%x ( %s )\n", cd->bmAttributes, + usbConfigAttributesString( cd->bmAttributes ) + ); + + printf( + "MaxPower = 0x%x, decimal %d\n", cd->MaxPower, cd->MaxPower + ); +} + + +void +print_USB_INTERFACE_DESCRIPTOR(PUSB_INTERFACE_DESCRIPTOR id, UINT ix) +/*++ +Routine Description: + + Called to do formatted ascii dump to console of a USB interface descriptor + +Arguments: + + ptr to USB interface descriptor + +Return Value: + + none + +--*/ +{ + printf("\n-----------------------------\nUSB_INTERFACE_DESCRIPTOR #%d\n", ix); + + + printf( + "bLength = 0x%x\n", id->bLength + ); + + + printf( + "bDescriptorType = 0x%x ( %s )\n", id->bDescriptorType, + usbDescriptorTypeString( id->bDescriptorType ) + ); + + + printf( + "bInterfaceNumber = 0x%x\n", id->bInterfaceNumber + ); + printf( + "bAlternateSetting = 0x%x\n", id->bAlternateSetting + ); + printf( + "bNumEndpoints = 0x%x\n", id->bNumEndpoints + ); + printf( + "bInterfaceClass = 0x%x\n", id->bInterfaceClass + ); + printf( + "bInterfaceSubClass = 0x%x\n", id->bInterfaceSubClass + ); + printf( + "bInterfaceProtocol = 0x%x\n", id->bInterfaceProtocol + ); + printf( + "bInterface = 0x%x\n", id->iInterface + ); +} + + +void +print_USB_ENDPOINT_DESCRIPTOR(PUSB_ENDPOINT_DESCRIPTOR ed, int i) +/*++ +Routine Description: + + Called to do formatted ascii dump to console of a USB endpoint descriptor + +Arguments: + + ptr to USB endpoint descriptor, + index of this endpt in interface desc + +Return Value: + + none + +--*/ +{ + printf( + "------------------------------\nUSB_ENDPOINT_DESCRIPTOR for Pipe%02d\n", i + ); + + printf( + "bLength = 0x%x\n", ed->bLength + ); + + printf( + "bDescriptorType = 0x%x ( %s )\n", ed->bDescriptorType, + usbDescriptorTypeString( ed->bDescriptorType ) + ); + + if ( USB_ENDPOINT_DIRECTION_IN( ed->bEndpointAddress ) ) { + printf( + "bEndpointAddress= 0x%x ( INPUT )\n", ed->bEndpointAddress + ); + } else { + printf( + "bEndpointAddress= 0x%x ( OUTPUT )\n", ed->bEndpointAddress + ); + } + + printf( + "bmAttributes= 0x%x ( %s )\n", ed->bmAttributes, + usbEndPointTypeString ( ed->bmAttributes ) + ); + + printf( + "wMaxPacketSize= 0x%x, decimal %d\n", ed->wMaxPacketSize, + ed->wMaxPacketSize + ); + + printf( + "bInterval = 0x%x, decimal %d\n", ed->bInterval, ed->bInterval + ); +} + + +BOOL +DumpUsbConfig() +/*++ +Routine Description: + + Called to do formatted ascii dump to console of USB + configuration, interface, and endpoint descriptors. + +Arguments: + + none + +Return Value: + + TRUE or FALSE + +--*/ +{ + HANDLE hDev; + UINT success; + int siz, nBytes; + char buf[256] = {'\0'}; + + hDev = OpenDevice(TRUE); + if(hDev == INVALID_HANDLE_VALUE) + { + return FALSE; + } + + siz = sizeof(buf); + + success = DeviceIoControl(hDev, + IOCTL_OSRUSBFX2_GET_CONFIG_DESCRIPTOR, + buf, + siz, + buf, + siz, + (PULONG) &nBytes, + NULL); + + if(success == FALSE) { + printf("Ioct - GetConfigDesc failed %d\n", GetLastError()); + } else { + + ULONG i; + UINT j, n; + char *pch; + PUSB_CONFIGURATION_DESCRIPTOR cd; + PUSB_INTERFACE_DESCRIPTOR id; + PUSB_ENDPOINT_DESCRIPTOR ed; + + pch = buf; + n = 0; + + cd = (PUSB_CONFIGURATION_DESCRIPTOR) pch; + + print_USB_CONFIGURATION_DESCRIPTOR( cd ); + + pch += cd->bLength; + + do { + id = (PUSB_INTERFACE_DESCRIPTOR) pch; + + print_USB_INTERFACE_DESCRIPTOR(id, n++); + + pch += id->bLength; + for (j=0; jbNumEndpoints; j++) { + + ed = (PUSB_ENDPOINT_DESCRIPTOR) pch; + + print_USB_ENDPOINT_DESCRIPTOR(ed,j); + + pch += ed->bLength; + } + i = (ULONG)(pch - buf); + + } while (iwTotalLength); + } + + CloseHandle(hDev); + + return success; + +} + diff --git a/usb/kmdf_fx2/exe/osrusbfx2.vcxproj b/usb/kmdf_fx2/exe/osrusbfx2.vcxproj new file mode 100644 index 000000000..b2388c677 --- /dev/null +++ b/usb/kmdf_fx2/exe/osrusbfx2.vcxproj @@ -0,0 +1,192 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {7289C9A2-233A-449D-AEEC-0D9FC929E0F0} + $(MSBuildProjectName) + Debug + Win32 + {6BC89B61-F14B-48F5-93B6-6F897425DDDA} + + + + Windows10 + False + Universal + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + True + Universal + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + False + Universal + + WindowsApplicationForDrivers10.0 + Application + + + Windows10 + True + Universal + + WindowsApplicationForDrivers10.0 + Application + + + + $(IntDir) + + + + + + + + + + + + + + + + osrusbfx2 + + + osrusbfx2 + + + osrusbfx2 + + + osrusbfx2 + + + + true + Level4 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\sys\inc + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\sys\inc + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\sys\inc + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(AdditionalDependencies);mincore.lib + + + + + true + Level4 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\sys\inc + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\sys\inc + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\sys\inc + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(AdditionalDependencies);mincore.lib + + + + + true + Level4 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\sys\inc + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\sys\inc + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\sys\inc + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(AdditionalDependencies);mincore.lib + + + + + true + Level4 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\sys\inc + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\sys\inc + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\inc;..\sys\inc + %(PreprocessorDefinitions);UNICODE;_UNICODE + + + %(AdditionalDependencies);mincore.lib + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/usb/kmdf_fx2/exe/osrusbfx2.vcxproj.Filters b/usb/kmdf_fx2/exe/osrusbfx2.vcxproj.Filters new file mode 100644 index 000000000..485e09f4c --- /dev/null +++ b/usb/kmdf_fx2/exe/osrusbfx2.vcxproj.Filters @@ -0,0 +1,30 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {2C8645BA-E3D5-49E8-97D5-033593663D52} + + + h;hpp;hxx;hm;inl;inc;xsd + {F8EADFE4-D5EF-4492-96A2-7C5534C7FEF2} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {61995FA8-8FC0-43EA-A5CA-17AEEA8827B6} + + + + + Source Files + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/usb/kmdf_fx2/exe/test.cmd b/usb/kmdf_fx2/exe/test.cmd new file mode 100644 index 000000000..30b18b856 --- /dev/null +++ b/usb/kmdf_fx2/exe/test.cmd @@ -0,0 +1,6 @@ +FOR /L %%i IN (0,1,100000) do ( + + osrusbfx2.exe -r 512 -w 512 -c 1000000 -v + +) + diff --git a/usb/kmdf_fx2/exe/testapp.c b/usb/kmdf_fx2/exe/testapp.c new file mode 100644 index 000000000..fce915b54 --- /dev/null +++ b/usb/kmdf_fx2/exe/testapp.c @@ -0,0 +1,1210 @@ +/*++ + +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: + + Console test app for osrusbfx2 driver. + +Environment: + + user mode only + +--*/ + + +#include +_Analysis_mode_(_Analysis_code_type_user_code_) + +#include +#include +#include +#include + +#include "devioctl.h" +#include "strsafe.h" + +#pragma warning(disable:4200) // +#pragma warning(disable:4201) // nameless struct/union +#pragma warning(disable:4214) // bit field types other than int + +#include +#include +#include "usbdi.h" +#include "public.h" + +#pragma warning(default:4200) +#pragma warning(default:4201) +#pragma warning(default:4214) + +#define WHILE(a) \ +while(__pragma(warning(disable:4127)) a __pragma(warning(disable:4127))) + +#define MAX_DEVPATH_LENGTH 256 +#define NUM_ASYNCH_IO 100 +#define BUFFER_SIZE 1024 +#define READER_TYPE 1 +#define WRITER_TYPE 2 + +BOOL G_fDumpUsbConfig = FALSE; // flags set in response to console command line switches +BOOL G_fDumpReadData = FALSE; +BOOL G_fRead = FALSE; +BOOL G_fWrite = FALSE; +BOOL G_fPlayWithDevice = FALSE; +BOOL G_fPerformAsyncIo = FALSE; +ULONG G_IterationCount = 1; //count of iterations of the test we are to perform +ULONG G_WriteLen = 512; // #bytes to write +ULONG G_ReadLen = 512; // #bytes to read + +BOOL +DumpUsbConfig( // defined in dump.c + ); + +typedef enum _INPUT_FUNCTION { + LIGHT_ONE_BAR = 1, + CLEAR_ONE_BAR, + LIGHT_ALL_BARS, + CLEAR_ALL_BARS, + GET_BAR_GRAPH_LIGHT_STATE, + GET_SWITCH_STATE, + GET_SWITCH_STATE_AS_INTERRUPT_MESSAGE, + GET_7_SEGEMENT_STATE, + SET_7_SEGEMENT_STATE, + RESET_DEVICE, + REENUMERATE_DEVICE, +} INPUT_FUNCTION; + +_Success_(return) +BOOL +GetDevicePath( + _In_ LPGUID InterfaceGuid, + _Out_writes_z_(BufLen) PWCHAR DevicePath, + _In_ size_t BufLen + ) +{ + CONFIGRET cr = CR_SUCCESS; + PWSTR deviceInterfaceList = NULL; + ULONG deviceInterfaceListLength = 0; + PWSTR nextInterface; + HRESULT hr = E_FAIL; + BOOL bRet = TRUE; + + cr = CM_Get_Device_Interface_List_Size( + &deviceInterfaceListLength, + InterfaceGuid, + NULL, + CM_GET_DEVICE_INTERFACE_LIST_PRESENT); + if (cr != CR_SUCCESS) { + printf("Error 0x%x retrieving device interface list size.\n", cr); + goto clean0; + } + + if (deviceInterfaceListLength <= 1) { + bRet = FALSE; + printf("Error: No active device interfaces found.\n" + " Is the sample driver loaded?"); + goto clean0; + } + + deviceInterfaceList = (PWSTR)malloc(deviceInterfaceListLength * sizeof(WCHAR)); + if (deviceInterfaceList == NULL) { + printf("Error allocating memory for device interface list.\n"); + goto clean0; + } + ZeroMemory(deviceInterfaceList, deviceInterfaceListLength * sizeof(WCHAR)); + + cr = CM_Get_Device_Interface_List( + InterfaceGuid, + NULL, + deviceInterfaceList, + deviceInterfaceListLength, + CM_GET_DEVICE_INTERFACE_LIST_PRESENT); + if (cr != CR_SUCCESS) { + printf("Error 0x%x retrieving device interface list.\n", cr); + goto clean0; + } + + nextInterface = deviceInterfaceList + wcslen(deviceInterfaceList) + 1; + if (*nextInterface != UNICODE_NULL) { + printf("Warning: More than one device interface instance found. \n" + "Selecting first matching device.\n\n"); + } + + hr = StringCchCopy(DevicePath, BufLen, deviceInterfaceList); + if (FAILED(hr)) { + bRet = FALSE; + printf("Error: StringCchCopy failed with HRESULT 0x%x", hr); + goto clean0; + } + +clean0: + if (deviceInterfaceList != NULL) { + free(deviceInterfaceList); + } + if (CR_SUCCESS != cr) { + bRet = FALSE; + } + + return bRet; +} + + +HANDLE +OpenDevice( + _In_ BOOL Synchronous + ) + +/*++ +Routine Description: + + Called by main() to open an instance of our device after obtaining its name + +Arguments: + + Synchronous - TRUE, if Device is to be opened for synchronous access. + FALSE, otherwise. + +Return Value: + + Device handle on success else INVALID_HANDLE_VALUE + +--*/ + +{ + HANDLE hDev; + WCHAR completeDeviceName[MAX_DEVPATH_LENGTH]; + + if ( !GetDevicePath( + (LPGUID) &GUID_DEVINTERFACE_OSRUSBFX2, + completeDeviceName, + sizeof(completeDeviceName)/sizeof(completeDeviceName[0])) ) + { + return INVALID_HANDLE_VALUE; + } + + printf("DeviceName = (%S)\n", completeDeviceName); + + if(Synchronous) { + hDev = CreateFile(completeDeviceName, + GENERIC_WRITE | GENERIC_READ, + FILE_SHARE_WRITE | FILE_SHARE_READ, + NULL, // default security + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + } else { + + hDev = CreateFile(completeDeviceName, + GENERIC_WRITE | GENERIC_READ, + FILE_SHARE_WRITE | FILE_SHARE_READ, + NULL, // default security + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, + NULL); + } + + if (hDev == INVALID_HANDLE_VALUE) { + printf("Failed to open the device, error - %d", GetLastError()); + } else { + printf("Opened the device successfully.\n"); + } + + return hDev; +} + + + +VOID +Usage() + +/*++ +Routine Description: + + Called by main() to dump usage info to the console when + the app is called with no parms or with an invalid parm + +Arguments: + + None + +Return Value: + + None + +--*/ + +{ + printf("Usage for osrusbfx2 testapp:\n"); + printf("-r [n] where n is number of bytes to read\n"); + printf("-w [n] where n is number of bytes to write\n"); + printf("-c [n] where n is number of iterations (default = 1)\n"); + printf("-v verbose -- dumps read data\n"); + printf("-p to control bar LEDs, seven segment, and dip switch\n"); + printf("-a to perform asynchronous I/O\n"); + printf("-u to dump USB configuration and pipe info \n"); + + return; +} + + +void +Parse( + _In_ int argc, + _In_reads_(argc) LPSTR *argv + ) + +/*++ +Routine Description: + + Called by main() to parse command line parms + +Arguments: + + argc and argv that was passed to main() + +Return Value: + + Sets global flags as per user function request + +--*/ + +{ + int i; + + if ( argc < 2 ) // give usage if invoked with no parms + Usage(); + + for (i=0; i= argc) { + Usage(); + exit(1); + } + else { +#pragma warning(suppress: 6385) + G_ReadLen = atoi(&argv[i+1][0]); + G_fRead = TRUE; + } + i++; + break; + case 'w': + case 'W': + if (i+1 >= argc) { + Usage(); + exit(1); + } + else { + G_WriteLen = atoi(&argv[i+1][0]); + G_fWrite = TRUE; + } + i++; + break; + case 'c': + case 'C': + if (i+1 >= argc) { + Usage(); + exit(1); + } + else { + G_IterationCount = atoi(&argv[i+1][0]); + } + i++; + break; + case 'u': + case 'U': + G_fDumpUsbConfig = TRUE; + break; + case 'p': + case 'P': + G_fPlayWithDevice = TRUE; + break; + case 'a': + case 'A': + G_fPerformAsyncIo = TRUE; + break; + case 'v': + case 'V': + G_fDumpReadData = TRUE; + break; + default: + Usage(); + } + } + } +} + +BOOL +Compare_Buffs( + _In_reads_bytes_(buff1length) char *buff1, + _In_ ULONG buff1length, + _In_reads_bytes_(buff2length) char *buff2, + _In_ ULONG buff2length + ) +/*++ +Routine Description: + + Called to verify read and write buffers match for loopback test + +Arguments: + + buffers to compare and length + +Return Value: + + TRUE if buffers match, else FALSE + +--*/ +{ + int ok = 1; + + if (buff1length != buff2length || memcmp(buff1, buff2, buff1length )) { + // Edi, and Esi point to the mismatching char and ecx indicates the + // remaining length. + ok = 0; + } + + return ok; +} + +#define NPERLN 8 + +VOID +Dump( + UCHAR *b, + int len +) + +/*++ +Routine Description: + + Called to do formatted ascii dump to console of the io buffer + +Arguments: + + buffer and length + +Return Value: + + none + +--*/ + +{ + ULONG i; + ULONG longLen = (ULONG)len / sizeof( ULONG ); + PULONG pBuf = (PULONG) b; + + // dump an ordinal ULONG for each sizeof(ULONG)'th byte + printf("\n****** BEGIN DUMP LEN decimal %d, 0x%x\n", len,len); + for (i=0; i 8){ + printf("Invalid bar number!\n"); + goto Error; + } + + bar--; // normalize to 0 to 7 + + barGraphState.BarsAsUChar = 1 << (UCHAR)bar; + + if (!DeviceIoControl(deviceHandle, + IOCTL_OSRUSBFX2_SET_BAR_GRAPH_DISPLAY, + &barGraphState, // Ptr to InBuffer + sizeof(BAR_GRAPH_STATE), // Length of InBuffer + NULL, // Ptr to OutBuffer + 0, // Length of OutBuffer + &index, // BytesReturned + 0)) { // Ptr to Overlapped structure + + code = GetLastError(); + + printf("DeviceIoControl failed with error 0x%x\n", code); + + goto Error; + } + + break; + + case CLEAR_ONE_BAR: + + + printf("Which Bar (input number 1 thru 8)?\n"); + if (scanf_s ("%d", &bar) <= 0) { + + printf("Error reading input!\n"); + goto Error; + + } + + if(bar == 0 || bar > 8){ + printf("Invalid bar number!\n"); + goto Error; + } + + bar--; + + // + // Read the current state + // + if (!DeviceIoControl(deviceHandle, + IOCTL_OSRUSBFX2_GET_BAR_GRAPH_DISPLAY, + NULL, // Ptr to InBuffer + 0, // Length of InBuffer + &barGraphState, // Ptr to OutBuffer + sizeof(BAR_GRAPH_STATE), // Length of OutBuffer + &index, // BytesReturned + 0)) { // Ptr to Overlapped structure + + code = GetLastError(); + + printf("DeviceIoControl failed with error 0x%x\n", code); + + goto Error; + } + + if (barGraphState.BarsAsUChar & (1 << bar)) { + + printf("Bar is set...Clearing it\n"); + barGraphState.BarsAsUChar &= ~(1 << bar); + + if (!DeviceIoControl(deviceHandle, + IOCTL_OSRUSBFX2_SET_BAR_GRAPH_DISPLAY, + &barGraphState, // Ptr to InBuffer + sizeof(BAR_GRAPH_STATE), // Length of InBuffer + NULL, // Ptr to OutBuffer + 0, // Length of OutBuffer + &index, // BytesReturned + 0)) { // Ptr to Overlapped structure + + code = GetLastError(); + + printf("DeviceIoControl failed with error 0x%x\n", code); + + goto Error; + + } + + } else { + + printf("Bar not set.\n"); + + } + + break; + + case LIGHT_ALL_BARS: + + barGraphState.BarsAsUChar = 0xFF; + + if (!DeviceIoControl(deviceHandle, + IOCTL_OSRUSBFX2_SET_BAR_GRAPH_DISPLAY, + &barGraphState, // Ptr to InBuffer + sizeof(BAR_GRAPH_STATE), // Length of InBuffer + NULL, // Ptr to OutBuffer + 0, // Length of OutBuffer + &index, // BytesReturned + 0)) { // Ptr to Overlapped structure + + code = GetLastError(); + + printf("DeviceIoControl failed with error 0x%x\n", code); + + goto Error; + } + + break; + + case CLEAR_ALL_BARS: + + barGraphState.BarsAsUChar = 0; + + if (!DeviceIoControl(deviceHandle, + IOCTL_OSRUSBFX2_SET_BAR_GRAPH_DISPLAY, + &barGraphState, // Ptr to InBuffer + sizeof(BAR_GRAPH_STATE), // Length of InBuffer + NULL, // Ptr to OutBuffer + 0, // Length of OutBuffer + &index, // BytesReturned + 0)) { // Ptr to Overlapped structure + + code = GetLastError(); + + printf("DeviceIoControl failed with error 0x%x\n", code); + + goto Error; + } + + break; + + + case GET_BAR_GRAPH_LIGHT_STATE: + + barGraphState.BarsAsUChar = 0; + + if (!DeviceIoControl(deviceHandle, + IOCTL_OSRUSBFX2_GET_BAR_GRAPH_DISPLAY, + NULL, // Ptr to InBuffer + 0, // Length of InBuffer + &barGraphState, // Ptr to OutBuffer + sizeof(BAR_GRAPH_STATE), // Length of OutBuffer + &index, // BytesReturned + 0)) { // Ptr to Overlapped structure + + code = GetLastError(); + + printf("DeviceIoControl failed with error 0x%x\n", code); + + goto Error; + } + + printf("Bar Graph: \n"); + printf(" Bar8 is %s\n", barGraphState.Bar8 ? "ON" : "OFF"); + printf(" Bar7 is %s\n", barGraphState.Bar7 ? "ON" : "OFF"); + printf(" Bar6 is %s\n", barGraphState.Bar6 ? "ON" : "OFF"); + printf(" Bar5 is %s\n", barGraphState.Bar5 ? "ON" : "OFF"); + printf(" Bar4 is %s\n", barGraphState.Bar4 ? "ON" : "OFF"); + printf(" Bar3 is %s\n", barGraphState.Bar3 ? "ON" : "OFF"); + printf(" Bar2 is %s\n", barGraphState.Bar2 ? "ON" : "OFF"); + printf(" Bar1 is %s\n", barGraphState.Bar1 ? "ON" : "OFF"); + + break; + + case GET_SWITCH_STATE: + + switchState.SwitchesAsUChar = 0; + + if (!DeviceIoControl(deviceHandle, + IOCTL_OSRUSBFX2_READ_SWITCHES, + NULL, // Ptr to InBuffer + 0, // Length of InBuffer + &switchState, // Ptr to OutBuffer + sizeof(SWITCH_STATE), // Length of OutBuffer + &index, // BytesReturned + 0)) { // Ptr to Overlapped structure + + code = GetLastError(); + + printf("DeviceIoControl failed with error 0x%x\n", code); + + goto Error; + } + + printf("Switches: \n"); + printf(" Switch8 is %s\n", switchState.Switch8 ? "ON" : "OFF"); + printf(" Switch7 is %s\n", switchState.Switch7 ? "ON" : "OFF"); + printf(" Switch6 is %s\n", switchState.Switch6 ? "ON" : "OFF"); + printf(" Switch5 is %s\n", switchState.Switch5 ? "ON" : "OFF"); + printf(" Switch4 is %s\n", switchState.Switch4 ? "ON" : "OFF"); + printf(" Switch3 is %s\n", switchState.Switch3 ? "ON" : "OFF"); + printf(" Switch2 is %s\n", switchState.Switch2 ? "ON" : "OFF"); + printf(" Switch1 is %s\n", switchState.Switch1 ? "ON" : "OFF"); + + break; + + case GET_SWITCH_STATE_AS_INTERRUPT_MESSAGE: + + switchState.SwitchesAsUChar = 0; + + if (!DeviceIoControl(deviceHandle, + IOCTL_OSRUSBFX2_GET_INTERRUPT_MESSAGE, + NULL, // Ptr to InBuffer + 0, // Length of InBuffer + &switchState, // Ptr to OutBuffer + sizeof(switchState), // Length of OutBuffer + &index, // BytesReturned + 0)) { // Ptr to Overlapped structure + + code = GetLastError(); + + printf("DeviceIoControl failed with error 0x%x\n", code); + + goto Error; + } + + printf("Switches: %d\n",index); + printf(" Switch8 is %s\n", switchState.Switch8 ? "ON" : "OFF"); + printf(" Switch7 is %s\n", switchState.Switch7 ? "ON" : "OFF"); + printf(" Switch6 is %s\n", switchState.Switch6 ? "ON" : "OFF"); + printf(" Switch5 is %s\n", switchState.Switch5 ? "ON" : "OFF"); + printf(" Switch4 is %s\n", switchState.Switch4 ? "ON" : "OFF"); + printf(" Switch3 is %s\n", switchState.Switch3 ? "ON" : "OFF"); + printf(" Switch2 is %s\n", switchState.Switch2 ? "ON" : "OFF"); + printf(" Switch1 is %s\n", switchState.Switch1 ? "ON" : "OFF"); + + break; + + case GET_7_SEGEMENT_STATE: + + sevenSegment = 0; + + if (!DeviceIoControl(deviceHandle, + IOCTL_OSRUSBFX2_GET_7_SEGMENT_DISPLAY, + NULL, // Ptr to InBuffer + 0, // Length of InBuffer + &sevenSegment, // Ptr to OutBuffer + sizeof(UCHAR), // Length of OutBuffer + &index, // BytesReturned + 0)) { // Ptr to Overlapped structure + + code = GetLastError(); + + printf("DeviceIoControl failed with error 0x%x\n", code); + + goto Error; + } + + printf("7 Segment mask: 0x%x\n", sevenSegment); + break; + + case SET_7_SEGEMENT_STATE: + + for (i = 0; i < 8; i++) { + + sevenSegment = 1 << i; + + if (!DeviceIoControl(deviceHandle, + IOCTL_OSRUSBFX2_SET_7_SEGMENT_DISPLAY, + &sevenSegment, // Ptr to InBuffer + sizeof(UCHAR), // Length of InBuffer + NULL, // Ptr to OutBuffer + 0, // Length of OutBuffer + &index, // BytesReturned + 0)) { // Ptr to Overlapped structure + + code = GetLastError(); + + printf("DeviceIoControl failed with error 0x%x\n", code); + + goto Error; + } + + printf("This is %d\n", i); + Sleep(500); + + } + + printf("7 Segment mask: 0x%x\n", sevenSegment); + break; + + case RESET_DEVICE: + + printf("Reset the device\n"); + + if (!DeviceIoControl(deviceHandle, + IOCTL_OSRUSBFX2_RESET_DEVICE, + NULL, // Ptr to InBuffer + 0, // Length of InBuffer + NULL, // Ptr to OutBuffer + 0, // Length of OutBuffer + &index, // BytesReturned + NULL)) { // Ptr to Overlapped structure + + code = GetLastError(); + + printf("DeviceIoControl failed with error 0x%x\n", code); + + goto Error; + } + + break; + + case REENUMERATE_DEVICE: + + printf("Re-enumerate the device\n"); + + if (!DeviceIoControl(deviceHandle, + IOCTL_OSRUSBFX2_REENUMERATE_DEVICE, + NULL, // Ptr to InBuffer + 0, // Length of InBuffer + NULL, // Ptr to OutBuffer + 0, // Length of OutBuffer + &index, // BytesReturned + NULL)) { // Ptr to Overlapped structure + + code = GetLastError(); + + printf("DeviceIoControl failed with error 0x%x\n", code); + + goto Error; + } + + // + // Close the handle to the device and exit out so that + // the driver can unload when the device is surprise-removed + // and reenumerated. + // + default: + + result = TRUE; + goto Error; + + } + + } // end of while loop + +Error: + + CloseHandle(deviceHandle); + return result; + +} + + +ULONG +AsyncIo( + PVOID ThreadParameter + ) +{ + HANDLE hDevice = INVALID_HANDLE_VALUE; + HANDLE hCompletionPort = NULL; + OVERLAPPED *pOvList = NULL; + PUCHAR buf = NULL; + ULONG_PTR i; + ULONG ioType = (ULONG)(ULONG_PTR)ThreadParameter; + ULONG error; + + hDevice = OpenDevice(FALSE); + + if (hDevice == INVALID_HANDLE_VALUE) { + printf("Cannot open device %d\n", GetLastError()); + goto Error; + } + + hCompletionPort = CreateIoCompletionPort(hDevice, NULL, 1, 0); + + if (hCompletionPort == NULL) { + printf("Cannot open completion port %d \n",GetLastError()); + goto Error; + } + + pOvList = (OVERLAPPED *)malloc(NUM_ASYNCH_IO * sizeof(OVERLAPPED)); + + if (pOvList == NULL) { + printf("Cannot allocate overlapped array \n"); + goto Error; + } + + buf = (PUCHAR)malloc(NUM_ASYNCH_IO * BUFFER_SIZE); + + if (buf == NULL) { + printf("Cannot allocate buffer \n"); + goto Error; + } + + ZeroMemory(pOvList, NUM_ASYNCH_IO * sizeof(OVERLAPPED)); + ZeroMemory(buf, NUM_ASYNCH_IO * BUFFER_SIZE); + + // + // Issue asynch I/O + // + + for (i = 0; i < NUM_ASYNCH_IO; 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(" %Iu th read failed %d \n",i, GetLastError()); + goto Error; + } + } + + } else { + if ( WriteFile( hDevice, + buf + (i* BUFFER_SIZE), + BUFFER_SIZE, + NULL, + &pOvList[i]) == 0) { + error = GetLastError(); + if (error != ERROR_IO_PENDING) { + printf(" %Iu th write failed %d \n",i, GetLastError()); + goto Error; + } + } + } + } + + // + // Wait for the I/Os to complete. If one completes then reissue the I/O + // + + WHILE (1) { + OVERLAPPED *completedOv; + ULONG_PTR key; + ULONG numberOfBytesTransferred; + + if ( GetQueuedCompletionStatus(hCompletionPort, &numberOfBytesTransferred, + &key, &completedOv, INFINITE) == 0) { + printf("GetQueuedCompletionStatus failed %d\n", GetLastError()); + goto Error; + } + + // + // Read successfully completed. Issue another one. + // + + if (ioType == READER_TYPE) { + + i = completedOv - pOvList; + + printf("Number of bytes read by request number %Iu is %d\n", + i, numberOfBytesTransferred); + + if ( ReadFile( hDevice, + buf + (i * BUFFER_SIZE), + BUFFER_SIZE, + NULL, + completedOv) == 0) { + error = GetLastError(); + if (error != ERROR_IO_PENDING) { + printf("%Iu th Read failed %d \n", i, GetLastError()); + goto Error; + } + } + } else { + + i = completedOv - pOvList; + + printf("Number of bytes written by request number %Iu is %d\n", + i, numberOfBytesTransferred); + + if ( WriteFile( hDevice, + buf + (i * BUFFER_SIZE), + BUFFER_SIZE, + NULL, + completedOv) == 0) { + error = GetLastError(); + if (error != ERROR_IO_PENDING) { + printf("%Iu th write failed %d \n", i, GetLastError()); + goto Error; + } + } + } + } + +Error: + if (hDevice != INVALID_HANDLE_VALUE) { + CloseHandle(hDevice); + } + + if (hCompletionPort) { + CloseHandle(hCompletionPort); + } + + if (pOvList) { + free(pOvList); + } + if (buf) { + free(buf); + } + + return 1; + +} + + +int +_cdecl +main( + _In_ int argc, + _In_reads_(argc) LPSTR *argv + ) +/*++ +Routine Description: + + Entry point to rwbulk.exe + Parses cmdline, performs user-requested tests + +Arguments: + + argc, argv standard console 'c' app arguments + +Return Value: + + Zero + +--*/ + +{ + char * pinBuf = NULL; + char * poutBuf = NULL; + ULONG nBytesRead; + ULONG nBytesWrite = 0; + int ok; + int retValue = 0; + UINT success; + HANDLE hRead = INVALID_HANDLE_VALUE; + HANDLE hWrite = INVALID_HANDLE_VALUE; + ULONG fail = 0L; + ULONG i; + + + Parse(argc, argv ); + + // + // dump USB configuation and pipe info + // + if (G_fDumpUsbConfig) { + DumpUsbConfig(); + } + + if (G_fPlayWithDevice) { + PlayWithDevice(); + goto exit; + } + + if (G_fPerformAsyncIo) { + HANDLE th1; + + // + // Create a reader thread + // + th1 = CreateThread( NULL, // Default Security Attrib. + 0, // Initial Stack Size, + 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()); + retValue = 1; + goto exit; + } + + // + // Use this thread for peforming write. + // + AsyncIo((PVOID)WRITER_TYPE); + + goto exit; + } + + // + // doing a read, write, or both test + // + if ((G_fRead) || (G_fWrite)) { + + if (G_fRead) { + if ( G_fDumpReadData ) { // round size to sizeof ULONG for readable dumping + while( G_ReadLen % sizeof( ULONG ) ) { + G_ReadLen++; + } + } + + // + // open the output file + // + hRead = OpenDevice(TRUE); + if(hRead == INVALID_HANDLE_VALUE) { + retValue = 1; + goto exit; + } + + pinBuf = malloc(G_ReadLen); + } + + if (G_fWrite) { + if ( G_fDumpReadData ) { // round size to sizeof ULONG for readable dumping + while( G_WriteLen % sizeof( ULONG ) ) { + G_WriteLen++; + } + } + + // + // open the output file + // + hWrite = OpenDevice(TRUE); + if(hWrite == INVALID_HANDLE_VALUE) { + retValue = 1; + goto exit; + } + + poutBuf = malloc(G_WriteLen); + } + + for (i = 0; i < G_IterationCount; i++) { + ULONG j; + + if (G_fWrite && poutBuf && hWrite != INVALID_HANDLE_VALUE) { + + PULONG pOut = (PULONG) poutBuf; + ULONG numLongs = G_WriteLen / sizeof( ULONG ); + + // + // put some data in the output buffer + // + for (j=0; j + +#include + +#define VER_FILETYPE VFT_DLL +#define VER_FILESUBTYPE VFT2_UNKNOWN +#define VER_FILEDESCRIPTION_STR "OSRUSBFX2 Bulk & Isoch Read and Write test App" +#define VER_INTERNALNAME_STR "osrusbfx2.exe" +#define VER_ORIGINALFILENAME_STR "osrusbfx2.exe" + +#include + diff --git a/usb/kmdf_fx2/inc/prototypes.h b/usb/kmdf_fx2/inc/prototypes.h new file mode 100644 index 000000000..cc99cdfe2 --- /dev/null +++ b/usb/kmdf_fx2/inc/prototypes.h @@ -0,0 +1,14 @@ +DRIVER_INITIALIZE DriverEntry; + +EVT_WDF_DRIVER_DEVICE_ADD EvtDeviceAdd; + +EVT_WDF_DEVICE_CONTEXT_CLEANUP EvtDriverContextCleanup; +EVT_WDF_DEVICE_PREPARE_HARDWARE EvtDevicePrepareHardware; + +EVT_WDF_IO_QUEUE_IO_READ EvtIoRead; +EVT_WDF_IO_QUEUE_IO_WRITE EvtIoWrite; +EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL EvtIoDeviceControl; + +EVT_WDF_REQUEST_COMPLETION_ROUTINE EvtRequestReadCompletionRoutine; +EVT_WDF_REQUEST_COMPLETION_ROUTINE EvtRequestWriteCompletionRoutine; + diff --git a/usb/kmdf_fx2/inc/public.h b/usb/kmdf_fx2/inc/public.h new file mode 100644 index 000000000..12ddf21f6 --- /dev/null +++ b/usb/kmdf_fx2/inc/public.h @@ -0,0 +1,173 @@ +/*++ + +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: + +Environment: + + User & Kernel mode + +--*/ + +#ifndef _PUBLIC_H +#define _PUBLIC_H + +#include + +// {573E8C73-0CB4-4471-A1BF-FAB26C31D384} +DEFINE_GUID(GUID_DEVINTERFACE_OSRUSBFX2, + 0x573e8c73, 0xcb4, 0x4471, 0xa1, 0xbf, 0xfa, 0xb2, 0x6c, 0x31, 0xd3, 0x84); + +#pragma warning(push) +#pragma warning(disable:4201) // nameless struct/union +#pragma warning(disable:4214) // bit field types other than int + +// +// Define the structures that will be used by the IOCTL +// interface to the driver +// + +// +// BAR_GRAPH_STATE +// +// BAR_GRAPH_STATE is a bit field structure with each +// bit corresponding to one of the bar graph on the +// OSRFX2 Development Board +// +#include +typedef struct _BAR_GRAPH_STATE { + + union { + + struct { + // + // Individual bars starting from the + // top of the stack of bars + // + // NOTE: There are actually 10 bars, + // but the very top two do not light + // and are not counted here + // + UCHAR Bar1 : 1; + UCHAR Bar2 : 1; + UCHAR Bar3 : 1; + UCHAR Bar4 : 1; + UCHAR Bar5 : 1; + UCHAR Bar6 : 1; + UCHAR Bar7 : 1; + UCHAR Bar8 : 1; + }; + + // + // The state of all the bar graph as a single + // UCHAR + // + UCHAR BarsAsUChar; + + }; + +}BAR_GRAPH_STATE, *PBAR_GRAPH_STATE; + +// +// SWITCH_STATE +// +// SWITCH_STATE is a bit field structure with each +// bit corresponding to one of the switches on the +// OSRFX2 Development Board +// +typedef struct _SWITCH_STATE { + + union { + struct { + // + // Individual switches starting from the + // left of the set of switches + // + UCHAR Switch1 : 1; + UCHAR Switch2 : 1; + UCHAR Switch3 : 1; + UCHAR Switch4 : 1; + UCHAR Switch5 : 1; + UCHAR Switch6 : 1; + UCHAR Switch7 : 1; + UCHAR Switch8 : 1; + }; + + // + // The state of all the switches as a single + // UCHAR + // + UCHAR SwitchesAsUChar; + + }; + + +}SWITCH_STATE, *PSWITCH_STATE; + +#include + +#pragma warning(pop) + +#define IOCTL_INDEX 0x800 +#define FILE_DEVICE_OSRUSBFX2 0x65500 + +#define IOCTL_OSRUSBFX2_GET_CONFIG_DESCRIPTOR CTL_CODE(FILE_DEVICE_OSRUSBFX2, \ + IOCTL_INDEX, \ + METHOD_BUFFERED, \ + FILE_READ_ACCESS) + +#define IOCTL_OSRUSBFX2_RESET_DEVICE CTL_CODE(FILE_DEVICE_OSRUSBFX2, \ + IOCTL_INDEX + 1, \ + METHOD_BUFFERED, \ + FILE_WRITE_ACCESS) + +#define IOCTL_OSRUSBFX2_REENUMERATE_DEVICE CTL_CODE(FILE_DEVICE_OSRUSBFX2, \ + IOCTL_INDEX + 3, \ + METHOD_BUFFERED, \ + FILE_WRITE_ACCESS) + +#define IOCTL_OSRUSBFX2_GET_BAR_GRAPH_DISPLAY CTL_CODE(FILE_DEVICE_OSRUSBFX2,\ + IOCTL_INDEX + 4, \ + METHOD_BUFFERED, \ + FILE_READ_ACCESS) + + +#define IOCTL_OSRUSBFX2_SET_BAR_GRAPH_DISPLAY CTL_CODE(FILE_DEVICE_OSRUSBFX2,\ + IOCTL_INDEX + 5, \ + METHOD_BUFFERED, \ + FILE_WRITE_ACCESS) + + +#define IOCTL_OSRUSBFX2_READ_SWITCHES CTL_CODE(FILE_DEVICE_OSRUSBFX2, \ + IOCTL_INDEX + 6, \ + METHOD_BUFFERED, \ + FILE_READ_ACCESS) + + +#define IOCTL_OSRUSBFX2_GET_7_SEGMENT_DISPLAY CTL_CODE(FILE_DEVICE_OSRUSBFX2, \ + IOCTL_INDEX + 7, \ + METHOD_BUFFERED, \ + FILE_READ_ACCESS) + + +#define IOCTL_OSRUSBFX2_SET_7_SEGMENT_DISPLAY CTL_CODE(FILE_DEVICE_OSRUSBFX2, \ + IOCTL_INDEX + 8, \ + METHOD_BUFFERED, \ + FILE_WRITE_ACCESS) + +#define IOCTL_OSRUSBFX2_GET_INTERRUPT_MESSAGE CTL_CODE(FILE_DEVICE_OSRUSBFX2,\ + IOCTL_INDEX + 9, \ + METHOD_OUT_DIRECT, \ + FILE_READ_ACCESS) + +#endif diff --git a/usb/kmdf_fx2/kmdf_fx2.sln b/usb/kmdf_fx2/kmdf_fx2.sln new file mode 100644 index 000000000..59e8e5481 --- /dev/null +++ b/usb/kmdf_fx2/kmdf_fx2.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", "{219EAF9D-A9F6-4F9D-897D-91AB46B2A71B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Exe", "Exe", "{7E5E361E-E36E-4728-9D4C-E030A0134F40}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "osrusbfx2", "driver\osrusbfx2.vcxproj", "{E7ECAF21-2522-4E7D-8475-67C4A5E43F70}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "osrusbfx2", "exe\osrusbfx2.vcxproj", "{7289C9A2-233A-449D-AEEC-0D9FC929E0F0}" +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 + {E7ECAF21-2522-4E7D-8475-67C4A5E43F70}.Debug|Win32.ActiveCfg = Debug|Win32 + {E7ECAF21-2522-4E7D-8475-67C4A5E43F70}.Debug|Win32.Build.0 = Debug|Win32 + {E7ECAF21-2522-4E7D-8475-67C4A5E43F70}.Release|Win32.ActiveCfg = Release|Win32 + {E7ECAF21-2522-4E7D-8475-67C4A5E43F70}.Release|Win32.Build.0 = Release|Win32 + {E7ECAF21-2522-4E7D-8475-67C4A5E43F70}.Debug|x64.ActiveCfg = Debug|x64 + {E7ECAF21-2522-4E7D-8475-67C4A5E43F70}.Debug|x64.Build.0 = Debug|x64 + {E7ECAF21-2522-4E7D-8475-67C4A5E43F70}.Release|x64.ActiveCfg = Release|x64 + {E7ECAF21-2522-4E7D-8475-67C4A5E43F70}.Release|x64.Build.0 = Release|x64 + {7289C9A2-233A-449D-AEEC-0D9FC929E0F0}.Debug|Win32.ActiveCfg = Debug|Win32 + {7289C9A2-233A-449D-AEEC-0D9FC929E0F0}.Debug|Win32.Build.0 = Debug|Win32 + {7289C9A2-233A-449D-AEEC-0D9FC929E0F0}.Release|Win32.ActiveCfg = Release|Win32 + {7289C9A2-233A-449D-AEEC-0D9FC929E0F0}.Release|Win32.Build.0 = Release|Win32 + {7289C9A2-233A-449D-AEEC-0D9FC929E0F0}.Debug|x64.ActiveCfg = Debug|x64 + {7289C9A2-233A-449D-AEEC-0D9FC929E0F0}.Debug|x64.Build.0 = Debug|x64 + {7289C9A2-233A-449D-AEEC-0D9FC929E0F0}.Release|x64.ActiveCfg = Release|x64 + {7289C9A2-233A-449D-AEEC-0D9FC929E0F0}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {E7ECAF21-2522-4E7D-8475-67C4A5E43F70} = {219EAF9D-A9F6-4F9D-897D-91AB46B2A71B} + {7289C9A2-233A-449D-AEEC-0D9FC929E0F0} = {7E5E361E-E36E-4728-9D4C-E030A0134F40} + EndGlobalSection +EndGlobal diff --git a/usb/ufxclientsample/UfxClientSample.inx b/usb/ufxclientsample/UfxClientSample.inx new file mode 100644 index 0000000000000000000000000000000000000000..51525cc852400f9dc84666ea0dfca1a6d35ff564 GIT binary patch literal 4136 zcmc(iYfl?j5QgV-rT&K%!cs5cVv^7c;)5~fB8-h}NE6X2a&42KB!Fxa1y%j)ZJ&3> zhu!6pP1O(8%HDICGuLMn?hOO9l_C3$n_LXhS|C@GcS9W6)JGVFX)@GL3H#W3y zc^|SiWarYR{LXw&^b=0#f56H)qYGPxI83s!YX^b1gFNBkCe|H4T8yLQ4` z^9E;n{CAkYVxbi`^8s;>K@9N$CmF|uoLF@{qI%zR+< z^8*;jpgOZpAnrY=8iT4CxDox8pI&D50*WGf8umLhGsohPcj6m*CG2FJ5?(XsAo>9u zCFKIlXUyNac139!HudqC&R#<&?t%5Jq+y12${k^QatGT>wnWCQxW>}@6r74`I_LD8 z@AR3yL4vaBnqB!x&nCYGTXg&ig##zcN1o6es-JPe4jcoY(%=NUKDlQT3j0vzC>%1Ixh~?g!HLbvCnJ7iw`s^bBVOYYqN+J0 z3duaBRt$)u@@3$B0yh&2MAg_o!>-(On;=K_UJ!)=9vtD9z>zXrHDdABqSJe{%lBxL zoGM=xt=4#owl%y}b#5O`^E_1*Jt0aj(X27AuPt;|W#2C7{qMMjRbffG1rrjKPhWzC z`{xv&h*6a`_s@nd*N(r(tKgK|dRnl$ioWd2uzK0O7xEgOn?0iQ2Aiw6^&Ppw+c}83 zAs))?IGx-6U$}%;dv*ZVCp<+AHL29XZ=hbaI>v-Guz2x66Bhwa@!ZIk1LB)%7VN zQkL-Lwmft?G~ae#>ns1wzl{2GqOjMW{9e~XCg_L_jF;ys&e z5dSI*WRH5<9^BO*M&H|Z{$&qs5s?eehnu<1;$K6;8vnmm=5k(1+GslhZOiDRDjSHX7r2KOk}l6DEo6oIx|AtnznRtb z&gOdSEt + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/usb/ufxclientsample/defaultqueue.c b/usb/ufxclientsample/defaultqueue.c new file mode 100644 index 000000000..111b48c8a --- /dev/null +++ b/usb/ufxclientsample/defaultqueue.c @@ -0,0 +1,200 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + defaultqueue.c + +Abstract: + + Implements the device's default queue callbacks. + +Environment: + + Kernel mode + +--*/ + +#include "device.h" +#include "defaultqueue.h" +#include "defaultqueue.tmh" + +EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL DefaultQueue_EvtIoDeviceControl; +EVT_WDF_IO_QUEUE_IO_INTERNAL_DEVICE_CONTROL DefaultQueue_EvtIoInternalDeviceControl; + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (PAGE, DefaultQueueCreate) +#endif + +_Must_inspect_result_ +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +DefaultQueueCreate( + _In_ WDFDEVICE Device + ) +/*++ + +Routine Description: + + Creates and configures the default IO Queue for the device. + +Arguments: + + Device - Handle to a framework device object. + +Return Value: + + Appropriate NTSTATUS message + +--*/ +{ + NTSTATUS Status; + WDF_IO_QUEUE_CONFIG QueueConfig; + + TraceEntry(); + + PAGED_CODE(); + + // + // Create the default queue + // + WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE( + &QueueConfig, + WdfIoQueueDispatchParallel + ); + + QueueConfig.PowerManaged = WdfTrue; + QueueConfig.EvtIoDeviceControl = DefaultQueue_EvtIoDeviceControl; + QueueConfig.EvtIoInternalDeviceControl = DefaultQueue_EvtIoInternalDeviceControl; + + Status = WdfIoQueueCreate( + Device, + &QueueConfig, + WDF_NO_OBJECT_ATTRIBUTES, + NULL); + + CHK_NT_MSG(Status, "WdfIoQueueCreate failed"); + +End: + + TraceExit(); + return Status; +} + +VOID +DefaultQueue_EvtIoDeviceControl( + _In_ WDFQUEUE Queue, + _In_ WDFREQUEST Request, + _In_ size_t OutputBufferLength, + _In_ size_t InputBufferLength, + _In_ ULONG IoControlCode + ) +/*++ + +Routine Description: + + EvtIoDeviceControl handler for the default Queue + +Arguments: + + Queue - Handle to the framework queue object that is associated with the + I/O request. + + Request - Handle to a framework request object. + + OutputBufferLength - Size of the output buffer in bytes + + InputBufferLength - Size of the input buffer in bytes + + IoControlCode - I/O control code. + +--*/ +{ + WDFDEVICE WdfDevice; + PCONTROLLER_CONTEXT ControllerContext; + BOOLEAN HandledbyUfx; + + TraceEntry(); + + TraceVerbose("Queue 0x%p, Request 0x%p, OutputBufferLength %d, " + "InputBufferLength %d, IoControlCode %d", + Queue, Request, (int) OutputBufferLength, + (int) InputBufferLength, IoControlCode); + + WdfDevice = WdfIoQueueGetDevice(Queue); + ControllerContext = DeviceGetControllerContext(WdfDevice); + + HandledbyUfx = UfxDeviceIoControl( + ControllerContext->UfxDevice, + Request, + OutputBufferLength, + InputBufferLength, + IoControlCode); + + if (!HandledbyUfx) { + TraceError("Recieved an unsupported IOCTL"); + WdfRequestComplete(Request, STATUS_INVALID_DEVICE_REQUEST); + } + + TraceExit(); +} + +VOID +DefaultQueue_EvtIoInternalDeviceControl( + _In_ WDFQUEUE Queue, + _In_ WDFREQUEST Request, + _In_ size_t OutputBufferLength, + _In_ size_t InputBufferLength, + _In_ ULONG IoControlCode + ) +/*++ + +Routine Description: + + EvtIoInternalDeviceControl handler for the default Queue + +Arguments: + + Queue - Handle to the framework queue object that is associated with the + I/O request. + + Request - Handle to a framework request object. + + OutputBufferLength - Size of the output buffer in bytes + + InputBufferLength - Size of the input buffer in bytes + + IoControlCode - I/O control code. + +--*/ +{ + WDFDEVICE WdfDevice; + PCONTROLLER_CONTEXT ControllerContext; + BOOLEAN HandledbyUfx; + + TraceEntry(); + + TraceVerbose("InternalQueue 0x%p, Request 0x%p, OutputBufferLength %d, " + "InputBufferLength %d, IoControlCode %d", + Queue, Request, (int) OutputBufferLength, + (int) InputBufferLength, IoControlCode); + + WdfDevice = WdfIoQueueGetDevice(Queue); + ControllerContext = DeviceGetControllerContext(WdfDevice); + + HandledbyUfx = UfxDeviceIoInternalControl( + ControllerContext->UfxDevice, + Request, + OutputBufferLength, + InputBufferLength, + IoControlCode); + + if (!HandledbyUfx) { + TraceError("Recieved an un supported IOCTL"); + WdfRequestComplete(Request, STATUS_INVALID_DEVICE_REQUEST); + } + + TraceExit(); +} + diff --git a/usb/ufxclientsample/defaultqueue.h b/usb/ufxclientsample/defaultqueue.h new file mode 100644 index 000000000..f32eb104c --- /dev/null +++ b/usb/ufxclientsample/defaultqueue.h @@ -0,0 +1,38 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + defaultqueue.h + +Abstract: + + Defines the export functions for the UFXDEVICE object. + +Environment: + + Kernel mode + +--*/ + +// +// This is the context that can be placed per queue +// and would contain per queue information. +// +typedef struct _QUEUE_CONTEXT { + + ULONG PrivateDeviceData; // just a placeholder + +} QUEUE_CONTEXT, *PQUEUE_CONTEXT; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(QUEUE_CONTEXT, QueueGetContext) + + +_Must_inspect_result_ +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +DefaultQueueCreate( + _In_ WDFDEVICE Device + ); + diff --git a/usb/ufxclientsample/device.c b/usb/ufxclientsample/device.c new file mode 100644 index 000000000..8bb12b520 --- /dev/null +++ b/usb/ufxclientsample/device.c @@ -0,0 +1,566 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + device.c - WDFDEVICE creation and callback handling for the sample + controller device. + +Abstract: + + This file contains the device entry points and callbacks. + +Environment: + + Kernel-mode Driver Framework + +--*/ + +#include "initguid.h" +#include "device.h" +#include "ufxdevice.h" +#include "registers.h" +#include "ufxendpoint.h" +#include "interrupt.h" +#include "defaultqueue.h" +#include "usbfnioctl.h" +#include "device.tmh" + +// +// Minimum amount of time to wait before exiting D0. The purpose of waiting +// is to ensure that the current state / port are stabilized. For example, +// on IDCP, we may be connected to a suspended PC or disconnected hub, and +// in these cases we will get a suspend interrupt after ~3ms. +// +#define IDLE_TIMEOUT (100) + +EVT_WDF_DEVICE_PREPARE_HARDWARE OnEvtDevicePrepareHardware; +EVT_WDF_DEVICE_RELEASE_HARDWARE OnEvtDeviceReleaseHardware; +EVT_WDF_DEVICE_D0_ENTRY OnEvtDeviceD0Entry; +EVT_WDF_DEVICE_D0_EXIT OnEvtDeviceD0Exit; + +_Must_inspect_result_ +_IRQL_requires_max_(PASSIVE_LEVEL) +static +NTSTATUS +DeviceInitialize( + _In_ WDFDEVICE Device + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (PAGE, UfxClientDeviceCreate) +#pragma alloc_text (PAGE, OnEvtDevicePrepareHardware) +#pragma alloc_text (PAGE, OnEvtDeviceReleaseHardware) +#endif + +_Must_inspect_result_ +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS +UfxClientDeviceCreate( + _In_ WDFDRIVER Driver, + _In_ PWDFDEVICE_INIT DeviceInit + ) +/*++ + +Routine Description: + + Worker routine called to create a device and its software resources. + +Arguments: + + Driver - WDF driver object + + 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: + + Appropriate NTSTATUS value + +--*/ +{ + WDF_OBJECT_ATTRIBUTES DeviceAttributes; + WDFDEVICE WdfDevice; + NTSTATUS Status; + WDF_PNPPOWER_EVENT_CALLBACKS PnpCallbacks; + WDF_DMA_ENABLER_CONFIG DmaConfig; + PCONTROLLER_CONTEXT ControllerContext; + + PAGED_CODE(); + + TraceEntry(); + + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&DeviceAttributes, CONTROLLER_CONTEXT); + + // + // Do UFX-specific initialization + // + Status = UfxFdoInit(Driver, DeviceInit, &DeviceAttributes); + CHK_NT_MSG(Status, "Failed UFX initialization"); + + WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&PnpCallbacks); + PnpCallbacks.EvtDevicePrepareHardware = OnEvtDevicePrepareHardware; + PnpCallbacks.EvtDeviceReleaseHardware = OnEvtDeviceReleaseHardware; + PnpCallbacks.EvtDeviceD0Entry = OnEvtDeviceD0Entry; + PnpCallbacks.EvtDeviceD0Exit = OnEvtDeviceD0Exit; + WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &PnpCallbacks); + + Status = WdfDeviceCreate(&DeviceInit, &DeviceAttributes, &WdfDevice); + CHK_NT_MSG(Status, "Failed to create wdf device"); + + ControllerContext = DeviceGetControllerContext(WdfDevice); + + KeInitializeEvent(&ControllerContext->DetachEvent, + NotificationEvent, + FALSE); + + WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_INIT(&ControllerContext->IdleSettings, IdleCanWakeFromS0); + ControllerContext->IdleSettings.IdleTimeoutType = SystemManagedIdleTimeoutWithHint; + ControllerContext->IdleSettings.IdleTimeout = IDLE_TIMEOUT; + ControllerContext->IdleSettings.DxState = PowerDeviceD3; + + Status = WdfDeviceAssignS0IdleSettings(WdfDevice, &ControllerContext->IdleSettings); + LOG_NT_MSG(Status, "Failed to set S0 Idle Settings"); + + // + // Create and initialize device's default queue + // + Status = DefaultQueueCreate(WdfDevice); + CHK_NT_MSG(Status, "Failed to intialize default queue"); + + // + // Set alignment required by controller + // + WdfDeviceSetAlignmentRequirement(WdfDevice, UFX_CLIENT_ALIGNMENT); + + // + // Create and Initialize DMA Enabler object for the device. + // + WDF_DMA_ENABLER_CONFIG_INIT( + &DmaConfig, + WdfDmaProfileScatterGatherDuplex, + MAX_DMA_LENGTH); + // + // Version 3 is required to perform multiple + // simultaneous transfers. + // + DmaConfig.WdmDmaVersionOverride = 3; + + Status = WdfDmaEnablerCreate( + WdfDevice, + &DmaConfig, + WDF_NO_OBJECT_ATTRIBUTES, + &ControllerContext->DmaEnabler); + CHK_NT_MSG(Status, "Failed to create DMA enabler object"); + + // + // Create UFXDEVICE object + // + Status = UfxDevice_DeviceCreate(WdfDevice); + CHK_NT_MSG(Status, "Failed to create UFX Device object"); + + // + // Create DPC Lock + // + Status = WdfSpinLockCreate(WDF_NO_OBJECT_ATTRIBUTES, &ControllerContext->DpcLock); + CHK_NT_MSG(Status, "Failed to create DPC lock"); + + Status = WdfWaitLockCreate(WDF_NO_OBJECT_ATTRIBUTES, &ControllerContext->InitializeDefaultEndpointLock); + CHK_NT_MSG(Status, "Failed to create Ep0 init lock"); + +End: + TraceExit(); + return Status; +} + + + +NTSTATUS +OnEvtDevicePrepareHardware ( + _In_ WDFDEVICE Device, + _In_ WDFCMRESLIST ResourcesRaw, + _In_ WDFCMRESLIST ResourcesTranslated + ) +/*++ + +Routine Description: + + EvtDevicePrepareHardware callback handler for the controller FDO. + It expects Register memory and two interrupts - Device and Wake. + +Arguments: + + Device - WDFDEVICE object representing the controller. + + ResourcesRaw - Raw resources relative to the parent bus driver. + + ResourcesTranslated - Translated resources available for the controller. + +Return Value: + + Appropriate NTSTATUS value + +--*/ +{ + NTSTATUS Status; + ULONG ResCount; + ULONG ResIndex; + PCONTROLLER_CONTEXT ControllerContext; + BOOLEAN MemoryResourceMapped; + + PAGED_CODE(); + + TraceEntry(); + + ControllerContext = DeviceGetControllerContext(Device); + MemoryResourceMapped = FALSE; + + ResCount = WdfCmResourceListGetCount(ResourcesRaw); + + Status = STATUS_SUCCESS; + + for (ResIndex = 0; ResIndex < ResCount; ResIndex++) { + + PCM_PARTIAL_RESOURCE_DESCRIPTOR ResourceDescriptorRaw; + PCM_PARTIAL_RESOURCE_DESCRIPTOR ResourceDescriptorTranslated; + + ResourceDescriptorRaw = WdfCmResourceListGetDescriptor( + ResourcesRaw, + ResIndex); + switch (ResourceDescriptorRaw->Type) { + + case CmResourceTypeMemory: + if (MemoryResourceMapped == FALSE) { + MemoryResourceMapped = TRUE; + Status = RegistersCreate(Device, ResourceDescriptorRaw); + CHK_NT_MSG(Status, "Failed to read the HW registers"); + } + break; + + case CmResourceTypeInterrupt: + ResourceDescriptorTranslated = WdfCmResourceListGetDescriptor( + ResourcesTranslated, + ResIndex); + Status = InterruptCreate( + Device, + ResourceDescriptorRaw, + ResourceDescriptorTranslated); + CHK_NT_MSG(Status, "Failed to create WDFINTERRUPT object"); + break; + + default: + break; + } + } + + NT_ASSERT(ControllerContext->DeviceInterrupt != NULL); + +End: + TraceExit(); + return STATUS_SUCCESS; +} + + +NTSTATUS +OnEvtDeviceReleaseHardware ( + _In_ WDFDEVICE Device, + _In_ WDFCMRESLIST ResourcesTranslated + ) +/*++ + +Routine Description: + + EvtDeviceReleaseHardware callback handler for the controller FDO. + It expects Register memory and two interrupts - Device and Wake. + +Arguments: + + Device - WDFDEVICE object representing the controller. + + ResourcesTranslated - Translated resources available for the controller. + +Return Value: + + Appropriate NTSTATUS value + +--*/ +{ + PREGISTERS_CONTEXT RegistersContext; + PCONTROLLER_CONTEXT ControllerContext; + + TraceEntry(); + + PAGED_CODE(); + + UNREFERENCED_PARAMETER(ResourcesTranslated); + + ControllerContext = DeviceGetControllerContext(Device); + ControllerContext->DeviceInterrupt = NULL; + ControllerContext->AttachDetachInterrupt = NULL; + + RegistersContext = DeviceGetRegistersContext(Device); + if ((RegistersContext == NULL) || (RegistersContext->RegisterBase == NULL)) { + goto End; + } + + MmUnmapIoSpace(RegistersContext->RegisterBase, RegistersContext->RegistersLength); + RtlZeroMemory(RegistersContext, sizeof(*RegistersContext)); + +End: + TraceExit(); + return STATUS_SUCCESS; +} + +NTSTATUS +OnEvtDeviceD0Entry ( + _In_ WDFDEVICE Device, + _In_ WDF_POWER_DEVICE_STATE PreviousState +) +/*++ + +Routine Description: + + Called by the framework after entering D0 state. + +Arguments: + + Device - WDFDEVICE framework handle to the bus FDO. + + PreviousState - The WDF_POWER_DEVICE_STATE from which the stack is + making this transition. + +Return Value: + + Returns STATUS_SUCCESS or an appropriate NTSTATUS code otherwise. + +--*/ +{ + PCONTROLLER_CONTEXT ControllerContext; + + TraceEntry(); + + ControllerContext = DeviceGetControllerContext(Device); + + if (PreviousState > WdfPowerDeviceD1) { + DevicePerformSoftReset(Device); + + WdfWaitLockAcquire(ControllerContext->InitializeDefaultEndpointLock, NULL); + ControllerContext->InitializeDefaultEndpoint = TRUE; + WdfWaitLockRelease(ControllerContext->InitializeDefaultEndpointLock); + } + + if (PreviousState == WdfPowerDeviceD3Final) { + // + // Notify UFX that HW is now ready + // + UfxDeviceNotifyHardwareReady(ControllerContext->UfxDevice); + } + + TraceExit(); + return STATUS_SUCCESS; +} + +NTSTATUS +OnEvtDeviceD0Exit ( + _In_ WDFDEVICE Device, + _In_ WDF_POWER_DEVICE_STATE TargetState + ) +/*++ + +Routine Description: + + Called by the framework when leaving our operational state (D0). + +Arguments: + + Device - WDFDEVICE framework handle to the bus FDO. + + TargetState - The WDF_POWER_DEVICE_STATE to which the stack is + making its transition. + + +Return Value: + + Returns STATUS_SUCCESS. + Any failed status code will leave the device stack in failed state and + device will not get any D0Entry callbacks after that. + +--*/ +{ + PCONTROLLER_CONTEXT ControllerContext; + + TraceEntry(); + + ControllerContext = DeviceGetControllerContext(Device); + + WdfWaitLockAcquire(ControllerContext->InitializeDefaultEndpointLock, NULL); + ControllerContext->InitializeDefaultEndpoint = FALSE; + WdfWaitLockRelease(ControllerContext->InitializeDefaultEndpointLock); + + if (TargetState != WdfPowerDeviceD3Final) { + goto End; + } + + // + // If the stack is being removed while we are attached to a host, simulate a detach. At this + // point all attach/detach notification mechanisms are disabled, so no other code will report a + // detach, nor will "WasAttached" change from underneath us. + // + if (ControllerContext->WasAttached) { + KeClearEvent(&ControllerContext->DetachEvent); + UfxDeviceNotifyDetach(ControllerContext->UfxDevice); + ControllerContext->WasAttached = FALSE; + KeWaitForSingleObject(&ControllerContext->DetachEvent, + Executive, + KernelMode, + FALSE, + NULL); + } + +End: + TraceExit(); + return STATUS_SUCCESS; +} + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +DevicePerformSoftReset ( + _In_ WDFDEVICE Device + ) +/*++ + +Routine Description: + + This functions performs soft reset of the controller and completes + any initialization that needs to be done afterwards. + +Arguments: + + Device - Wdf FDO device object representing the controller + +--*/ +{ + + TraceEntry(); + + UNREFERENCED_PARAMETER(Device); + + // + // #### TODO: Insert code that performs a controller soft reset #### + // + + TraceExit(); +} + +_IRQL_requires_max_(PASSIVE_LEVEL) +VOID +DeviceInitializeDefaultEndpoint ( + _In_ WDFDEVICE Device + ) +/*++ + +Routine Description: + + This function initializes the default control endpoint + +Arguments: + + Device - Wdf FDO device object representing the controller + +--*/ +{ + UFXENDPOINT Endpoint; + PUFXDEVICE_CONTEXT DeviceContext; + PCONTROLLER_CONTEXT ControllerContext; + NTSTATUS Status; + + TraceEntry(); + + ControllerContext = DeviceGetControllerContext(Device); + DeviceContext = UfxDeviceGetContext(ControllerContext->UfxDevice); + + if (DeviceContext->PhysicalEndpointToUfxEndpoint[0] != NULL) { + Endpoint = DeviceContext->PhysicalEndpointToUfxEndpoint[0]; + + // + // Re-initialize endpoint 0 + // + TransferDestroy(Endpoint); + + Status = TransferInitialize(Endpoint); + LOG_NT_MSG(Status, "Failed to initialize default endpoint"); + + UfxEndpointConfigureHardware(Endpoint, FALSE); + TransferStart(Endpoint); + + } else { + NT_ASSERT(FALSE); + } + + TraceExit(); +} + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +DeviceHardwareFailure ( + _In_ WDFDEVICE Device, + _In_ ULONG ExceptionCode + ) +/*++ + +Routine Description: + + The client driver may call this function if the controller has encountered a non-recoverable error, to + allow UFX to attempt to reset the controller if possible. + +Arguments: + + Device - Wdf FDO device object representing the controller + + ExceptionCode - Exception code indicating the specific failure encountered by the controller. This + value is specific to the type of controller. + +--*/ +{ + PCONTROLLER_CONTEXT ControllerContext; + PUFXDEVICE_CONTEXT DeviceContext; + PHARDWARE_FAILURE_CONTEXT HardwareFailureContext; + TraceEntry(); + + NT_ASSERT(ExceptionCode < ExceptionMaximum); + + ControllerContext = DeviceGetControllerContext(Device); + DeviceContext = UfxDeviceGetContext(ControllerContext->UfxDevice); + +#pragma prefast(suppress:6014, "Memory allocation is expected") + HardwareFailureContext = ExAllocatePoolWithTag( + NonPagedPool, + sizeof(HARDWARE_FAILURE_CONTEXT), + UFX_CLIENT_TAG); + + if (HardwareFailureContext) { + HardwareFailureContext->Size = sizeof(*HardwareFailureContext); + HardwareFailureContext->ExceptionCode = ExceptionCode; + + RtlCopyMemory( + &HardwareFailureContext->ControllerContext, + ControllerContext, + sizeof(CONTROLLER_CONTEXT)); + + RtlCopyMemory( + &HardwareFailureContext->DeviceContext, + DeviceContext, + sizeof(UFXDEVICE_CONTEXT)); + + } else { + TraceError("Failed to allocate hardware failure context!"); + } + + UfxDeviceNotifyHardwareFailure( + ControllerContext->UfxDevice, + (PUFX_HARDWARE_FAILURE_CONTEXT) HardwareFailureContext); + + TraceExit(); +} diff --git a/usb/ufxclientsample/device.h b/usb/ufxclientsample/device.h new file mode 100644 index 000000000..7df5a4a39 --- /dev/null +++ b/usb/ufxclientsample/device.h @@ -0,0 +1,141 @@ +/*++ + +Module Name: + + device.h + +Abstract: + + This file contains the device definitions. + +Environment: + + Kernel-mode Driver Framework + +--*/ +#pragma once + +#include "driver.h" +#include "transfer.h" +#include "registers.h" +#include "ufxdevice.h" +#include + +#define UFXCLIENT_TAG ('CXFU') // UFXC + +#define TEST_BIT(value, bitNumber) ((value) & (1<<(bitNumber))) ? TRUE : FALSE + +#define MAX_DMA_LENGTH 0x1000 + +#define UFX_CLIENT_ALIGNMENT 128 + +// +// Time interval to wait after being suspended and before requesting remote +// wakeup request +// +#define REMOTE_WAKEUP_TIMEOUT_INTERVAL_MS 1 + +// +// Context space for WDFDEVICE object representing the controller +// +typedef struct _CONTROLLER_CONTEXT { + + UFXDEVICE UfxDevice; + + WDFINTERRUPT DeviceInterrupt; + + WDFINTERRUPT WakeInterrupt; + + WDFINTERRUPT AttachDetachInterrupt; + + KEVENT DetachEvent; + + WDFDMAENABLER DmaEnabler; + + // + // This variable tells us if we have indicated to UFX, via UfxDeviceNotifyAttach, that we are + // attached. + // + BOOLEAN WasAttached; + + // + // This variable tracks what the hardware thinks about the current + // attachment status. It is updated right away based on incoming notifications. + // + volatile BOOLEAN Attached; + + // + // This variable tracks whether there was some change, possibly transient, in the attachment + // status. Simply inspecting "Attached", one may conclude that there was no change at all, but + // this variable will tell us if there was a rapid attach-detach or detach-attach event. + BOOLEAN GotAttachOrDetach; + + WDFSPINLOCK DpcLock; + + BOOLEAN RemoteWakeupRequested; + + BOOLEAN StopPending; + + BOOLEAN Connect; + + BOOLEAN Suspended; + + USB_DEVICE_SPEED Speed; + + WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS IdleSettings; + + BOOLEAN InitializeDefaultEndpoint; + + WDFWAITLOCK InitializeDefaultEndpointLock; + +} CONTROLLER_CONTEXT, *PCONTROLLER_CONTEXT; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(CONTROLLER_CONTEXT, DeviceGetControllerContext) + + +typedef enum _UFX_CLIENT_EXCEPTION_CODE { + ExceptionDisconnectTimeout = 0, + ExceptionMaximum +} UFX_CLIENT_EXCEPTION_CODE, *PUFX_CLIENT_EXCEPTION_CODE; + +typedef struct _HARDWARE_FAILURE_CONTEXT { + ULONG Size; + UFX_CLIENT_EXCEPTION_CODE ExceptionCode; + CONTROLLER_CONTEXT ControllerContext; + UFXDEVICE_CONTEXT DeviceContext; +} HARDWARE_FAILURE_CONTEXT, *PHARDWARE_FAILURE_CONTEXT; + +// +// Function to initialize the device and its callbacks +// +_Must_inspect_result_ +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS +UfxClientDeviceCreate ( + _In_ WDFDRIVER Driver, + _In_ PWDFDEVICE_INIT DeviceInit + ); + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +DevicePerformSoftReset ( + _In_ WDFDEVICE Device + ); + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +DeviceHardwareFailure ( + _In_ WDFDEVICE Device, + _In_ ULONG ExceptionCode + ); + +VOID +DeviceVendorReset ( + _In_ WDFDEVICE Device + ); + +_IRQL_requires_max_(PASSIVE_LEVEL) +VOID +DeviceInitializeDefaultEndpoint ( + _In_ WDFDEVICE Device + ); diff --git a/usb/ufxclientsample/driver.c b/usb/ufxclientsample/driver.c new file mode 100644 index 000000000..1a1f5e1ec --- /dev/null +++ b/usb/ufxclientsample/driver.c @@ -0,0 +1,184 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + driver.c + +Abstract: + + This file contains the driver entry points and callbacks. + +Environment: + + Kernel-mode Driver Framework + +--*/ + +#include "driver.h" +#include "device.h" +#include "driver.tmh" + + +DRIVER_INITIALIZE DriverEntry; + +// +// WDFDRIVER object Callbacks +// +EVT_WDF_DRIVER_DEVICE_ADD OnEvtDriverDeviceAdd; +EVT_WDF_DRIVER_UNLOAD OnEvtDriverUnload; + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (INIT, DriverEntry) +#pragma alloc_text (PAGE, OnEvtDriverDeviceAdd) +#pragma alloc_text (PAGE, OnEvtDriverUnload) +#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. + + RegistryPath - represents the driver specific path in the Registry. + +Return Value: + + STATUS_SUCCESS if successful, appropriate NTSTATUS message otherwise. + +--*/ +{ + NTSTATUS Status; + WDF_DRIVER_CONFIG DriverConfig; + WDF_OBJECT_ATTRIBUTES DriverAttributes; + + // + // Enable ETW tracing + // + EventRegisterUfxClientSample(); + // + // Initialize WPP Tracing + // + WPP_INIT_TRACING(DriverObject, RegistryPath); + + TraceEntry(); + + // + // Register 'EvtDriverUnload' to de-register WPP and ETW. + // + WDF_OBJECT_ATTRIBUTES_INIT(&DriverAttributes); + + WDF_DRIVER_CONFIG_INIT(&DriverConfig, OnEvtDriverDeviceAdd); + DriverConfig.DriverPoolTag = UFX_CLIENT_TAG; + DriverConfig.EvtDriverUnload = OnEvtDriverUnload; + + Status = WdfDriverCreate( + DriverObject, + RegistryPath, + &DriverAttributes, + &DriverConfig, + WDF_NO_HANDLE); + CHK_NT_MSG(Status, "Failed to create WDFDRIVER object"); + +End: + + if (NT_SUCCESS(Status) != TRUE) { + + // + // Stop WPP Tracing + // + WPP_CLEANUP(DriverObject); + + // + // Unregister ETW tracing + // + EventUnregisterUfxClientSample(); + goto Return; + } + + TraceExit(); +Return: + + return Status; +} + +NTSTATUS +OnEvtDriverDeviceAdd( + WDFDRIVER Driver, + PWDFDEVICE_INIT DeviceInit + ) +/*++ +Routine Description: + + EvtDriverDeviceAdd 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; + + PAGED_CODE(); + + TraceEntry(); + + Status = UfxClientDeviceCreate(Driver, DeviceInit); + CHK_NT_MSG(Status, "Failed to create wdf device"); + +End: + TraceExit(); + return Status; +} + +VOID +OnEvtDriverUnload( + _In_ WDFDRIVER WdfDriver + ) +/*++ +Routine Description: + + Free-up all the driver wide common resources like WPP and ETW. + +Arguments: + + WdfDriver - Driver object being unload. + +--*/ +{ + PAGED_CODE (); + + TraceEntry(); + + // + // Stop WPP Tracing + // + WPP_CLEANUP(WdfDriverWdmGetDriverObject(WdfDriver)); + // + // Unregister ETW tracing + // + EventUnregisterUfxClientSample(); +} diff --git a/usb/ufxclientsample/driver.h b/usb/ufxclientsample/driver.h new file mode 100644 index 000000000..bed33089c --- /dev/null +++ b/usb/ufxclientsample/driver.h @@ -0,0 +1,33 @@ +/*++ + +Module Name: + + driver.h + +Abstract: + + This file contains the driver definitions. + +Environment: + + Kernel-mode Driver Framework + +--*/ + +#pragma once + +#include +#include +#include "ufxclient.h" +#include "registers.h" +#include "trace.h" + +#define UFX_CLIENT_TAG ('cXFU') + + +//--------------------------------------------------------------------------- +// While macro to supress 4127 warning: Constant expression +//--------------------------------------------------------------------------- +#define WHILE(constant) \ + __pragma(warning(suppress:4127)) while(constant) + diff --git a/usb/ufxclientsample/event.c b/usb/ufxclientsample/event.c new file mode 100644 index 000000000..8f31c3813 --- /dev/null +++ b/usb/ufxclientsample/event.c @@ -0,0 +1,340 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + events.c + +Abstract: + + Event handler + +Environment: + + Kernel mode + +--*/ + +#include "device.h" +#include "ufxdevice.h" +#include "ufxendpoint.h" +#include "transfer.h" +#include +#include "event.h" +#include "event.tmh" + + +_IRQL_requires_(DISPATCH_LEVEL) +VOID +HandleUsbReset ( + WDFDEVICE WdfDevice + ) +/*++ + +Routine Description: + + Handles a reset event notification from the controller. + +Arguments: + + WdfDevice - WDFDEVICE object representing the controller. + +--*/ +{ + PCONTROLLER_CONTEXT ControllerContext; + + TraceEntry(); + + ControllerContext = DeviceGetControllerContext(WdfDevice); + + UfxDevice_Reset(ControllerContext->UfxDevice); + + ControllerContext->RemoteWakeupRequested = FALSE; + + // + // Update the controller to set device address to 0 + // + + // + // #### TODO: Add code to set the USB device address to 0 on the controller #### + // + + TraceExit(); +} + +_IRQL_requires_(DISPATCH_LEVEL) +VOID +HandleUsbConnect ( + WDFDEVICE WdfDevice + ) +/*++ + +Routine Description: + + Handles a connect event from the controller. + +Arguments: + + WDfDevice - WDFDEVICE object representing the controller. + +--*/ +{ + PCONTROLLER_CONTEXT ControllerContext; + USB_DEVICE_SPEED DeviceSpeed; + + TraceEntry(); + + ControllerContext = DeviceGetControllerContext(WdfDevice); + + // + // Read the device speed. + // + + // + // #### TODO: Add code to read device speed from the controller #### + // + + // Sample will assume SuperSpeed operation for illustration purposes + DeviceSpeed = UsbSuperSpeed; + + // + // #### TODO: Add any code needed to configure the controller after connect has occurred #### + // + + + ControllerContext->Speed = DeviceSpeed; + TraceInformation("Connected Speed is %d!", DeviceSpeed); + + // + // Notify UFX about reset, which will take care of updating + // Max Packet Size for EP0 by calling descriptor update. + // + UfxDeviceNotifyReset(ControllerContext->UfxDevice, DeviceSpeed); + + ControllerContext->Connect = TRUE; + + TraceExit(); +} + + +_IRQL_requires_(DISPATCH_LEVEL) +VOID +HandleUsbDisconnect ( + WDFDEVICE WdfDevice + ) +/*++ + +Routine Description: + + Handles a disconnect event from the controller. + +Arguments: + + WdfDevice - WDFDEVICE object representing the controller. + +--*/ +{ + TraceEntry(); + + UNREFERENCED_PARAMETER(WdfDevice); + + // + // #### TODO: Add any code needed to configure controller after disconnect event #### + // + + TraceExit(); +} + + +_IRQL_requires_(DISPATCH_LEVEL) +VOID +HandleUSBLinkStateChange ( + WDFDEVICE WdfDevice + ) +/*++ + +Routine Description: + + Handles a link change event from the controller. + +Arguments: + + WdfDevice - WDFDEVICE object representing the controller. + +--*/ +{ + PCONTROLLER_CONTEXT ControllerContext; + ULONG UsbLinkEvent; + + TraceEntry(); + + ControllerContext = DeviceGetControllerContext(WdfDevice); + + // + // #### TODO: Add code to read link state from controller #### + // + // For sample purposes use U0. + UsbLinkEvent = USB_LINK_STATE_U0; + + if (UsbLinkEvent == USB_LINK_STATE_U0) { + + if(ControllerContext->Suspended) { + ControllerContext->Suspended = FALSE; + UfxDeviceNotifyResume(ControllerContext->UfxDevice); + } + + ControllerContext->RemoteWakeupRequested = FALSE; + + } else { + TraceVerbose("Ignoring link state change event: 0X%X", UsbLinkEvent); + } + + TraceExit(); +} + + +_IRQL_requires_(DISPATCH_LEVEL) +VOID +HandleDeviceEvent ( + WDFDEVICE WdfDevice, + DEVICE_EVENT DeviceEvent + ) +/*++ + +Routine Description: + + Function to dispatch device events from the controller. + +Arguments: + + WdfDevice - Wdf device object corresponding to the FDO + + DeviceEvent - Device specific event. + +--*/ +{ + PCONTROLLER_CONTEXT ControllerContext; + + TraceEntry(); + + ControllerContext = DeviceGetControllerContext(WdfDevice); + + switch (DeviceEvent) { + + case DeviceEventDisconnect: + ControllerContext->Suspended = FALSE; + HandleUsbDisconnect(WdfDevice); + break; + + case DeviceEventUSBReset: + ControllerContext->Suspended = FALSE; + HandleUsbReset(WdfDevice); + break; + + case DeviceEventConnect: + HandleUsbConnect(WdfDevice); + break; + + case DeviceEventUSBLinkStateChange: + HandleUSBLinkStateChange(WdfDevice); + break; + + case DeviceEventWakeUp: + if (ControllerContext->Suspended) { + ControllerContext->Suspended = FALSE; + UfxDeviceNotifyResume(ControllerContext->UfxDevice); + } + break; + + case DeviceEventSuspend: + if (!ControllerContext->Suspended) { + ControllerContext->Suspended = TRUE; + UfxDeviceNotifySuspend(ControllerContext->UfxDevice); + } + break; + + default: + TraceError("Unknown device event raised by controller"); + NT_ASSERT(FALSE); + break; + } + + TraceExit(); +} + + + + +_IRQL_requires_(DISPATCH_LEVEL) +VOID +HandleEndpointEvent ( + WDFDEVICE WdfDevice, + ENDPOINT_EVENT EndpointEvent + ) +/*++ + +Routine Description: + + Function to dispatch endpoint events. + +Arguments: + + WdfDevice - Wdf device object corresponding to the FDO + + EndpointEvent - Endpoint specific event. + +--*/ +{ + UFXENDPOINT Endpoint; + PCONTROLLER_CONTEXT ControllerContext; + PUFXDEVICE_CONTEXT DeviceContext; + + TraceEntry(); + + ControllerContext = DeviceGetControllerContext(WdfDevice); + DeviceContext = UfxDeviceGetContext(ControllerContext->UfxDevice); + + // + // #### TODO: Insert code to extract endpoint event and endpoint number #### + // + + // Sample will assume a transfer complete event on endpoint 1 for illustration purposes + EndpointEvent = EndpointEventTransferComplete; + + Endpoint = DeviceContext->PhysicalEndpointToUfxEndpoint[1]; + + switch (EndpointEvent) { + + case EndpointEventTransferComplete: + TraceInformation("ENDPOINT EVENT: TransferComplete"); + TransferComplete(Endpoint); + break; + + case EndpointEventStartTransferComplete: + TraceInformation("ENDPOINT EVENT: Complete Start Transfer"); + TransferCommandStartComplete(Endpoint); + break; + + case EndpointEventEndTransferComplete: + TraceInformation("ENDPOINT EVENT: Complete End Transfer"); + TransferCommandEndComplete(Endpoint); + break; + + case EndpointEventSetStall: + TraceInformation("ENDPOINT EVENT: Command Complete Stall Set"); + TransferStallSetComplete(Endpoint); + break; + + case EndpointEventClearStall: + TraceInformation("ENDPOINT EVENT: Command Complete Stall Clear"); + TransferStallClearComplete(Endpoint); + break; + + default: + TraceError("Unexpected endpoint event!"); + NT_ASSERT(FALSE); + } + + TraceExit(); +} \ No newline at end of file diff --git a/usb/ufxclientsample/event.h b/usb/ufxclientsample/event.h new file mode 100644 index 000000000..545324158 --- /dev/null +++ b/usb/ufxclientsample/event.h @@ -0,0 +1,65 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + eventbuffer.h + +Abstract: + + Defines the sample controller's events + +Environment: + + Kernel mode + +--*/ +#pragma once + +typedef enum _CONTROLLER_EVENT_TYPE { + EventTypeDevice = 0, + EventTypeEndpoint = 1 +} CONTROLLER_EVENT_TYPE; + +typedef enum _DEVICE_EVENT { + DeviceEventDisconnect = 0, + DeviceEventUSBReset = 1, + DeviceEventConnect = 2, + DeviceEventUSBLinkStateChange = 3, + DeviceEventWakeUp = 4, + DeviceEventHibernationRequest = 5, + DeviceEventSuspend = 6, + DeviceEventCommandComplete = 7 +} DEVICE_EVENT, *PDEVICE_EVENT; + +typedef enum _ENDPOINT_EVENT { + EndpointEventTransferComplete = 0, + EndpointEventStartTransferComplete = 1, + EndpointEventEndTransferComplete = 2, + EndpointEventSetStall = 3, + EndpointEventClearStall = 4 +} ENDPOINT_EVENT, *PENDPOINT_EVENT; + +typedef struct _CONTROLLER_EVENT { + CONTROLLER_EVENT_TYPE Type; + + union { + DEVICE_EVENT DeviceEvent; + ENDPOINT_EVENT EndpointEvent; + } u; +} CONTROLLER_EVENT; + +_IRQL_requires_(DISPATCH_LEVEL) +VOID +HandleEndpointEvent ( + WDFDEVICE WdfDevice, + ENDPOINT_EVENT EndpointEvent + ); + +_IRQL_requires_(DISPATCH_LEVEL) +VOID +HandleDeviceEvent ( + WDFDEVICE WdfDevice, + DEVICE_EVENT DeviceEvent + ); diff --git a/usb/ufxclientsample/interrupt.c b/usb/ufxclientsample/interrupt.c new file mode 100644 index 000000000..2ce10c25a --- /dev/null +++ b/usb/ufxclientsample/interrupt.c @@ -0,0 +1,315 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + interrupt.c + +Abstract: + + Implements the WDF interrupt object's callbacks and functions to manage + the interrupts. + +Environment: + + Kernel mode + +--*/ + + +#include "device.h" +#include "interrupt.h" +#include "interrupt.tmh" + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (PAGE, InterruptCreate) +#endif + +EVT_WDF_INTERRUPT_ISR DeviceInterrupt_EvtInterruptIsr; +EVT_WDF_INTERRUPT_DPC DeviceInterrupt_EvtInterruptDpc; +EVT_WDF_INTERRUPT_ISR DeviceInterrupt_EvtAttachDetachInterruptIsr; + +_Must_inspect_result_ +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS +InterruptCreate ( + _In_ WDFDEVICE Device, + _In_ PCM_PARTIAL_RESOURCE_DESCRIPTOR InterruptResourceRaw, + _In_ PCM_PARTIAL_RESOURCE_DESCRIPTOR InterruptResourceTranslated + ) +/*++ + +Routine Description: + + Helper function to create device's WDFINTERRUPT object and any other + needed WDF resources that the interrupt needs for proper functioning. + + It assumes the first interrupt resource with which this is invoked is + the device interrupt, and the second is the attach/detach interrupt. + +Arguments: + + Device - Wdf device object corresponding to the FDO + + InterruptResourceRaw - Raw resource for the interrupt + + InterruptResourceTranslated - Translated resource for the interrupt + +Return Value: + + Appropriate NTSTATUS value + +--*/ +{ + NTSTATUS Status; + WDF_INTERRUPT_CONFIG InterruptConfig; + PCONTROLLER_CONTEXT ControllerContext; + WDF_OBJECT_ATTRIBUTES Attributes; + WDFINTERRUPT* InterruptToCreate; + + TraceEntry(); + + PAGED_CODE(); + + ControllerContext = DeviceGetControllerContext(Device); + + WDF_OBJECT_ATTRIBUTES_INIT(&Attributes); + + if (ControllerContext->DeviceInterrupt == NULL) { + WDF_INTERRUPT_CONFIG_INIT( + &InterruptConfig, + DeviceInterrupt_EvtInterruptIsr, + DeviceInterrupt_EvtInterruptDpc); + + WDF_OBJECT_ATTRIBUTES_SET_CONTEXT_TYPE( + &Attributes, + DEVICE_INTERRUPT_CONTEXT); + + InterruptToCreate = &ControllerContext->DeviceInterrupt; + + } else if (ControllerContext->AttachDetachInterrupt == NULL) { + WDF_INTERRUPT_CONFIG_INIT( + &InterruptConfig, + DeviceInterrupt_EvtAttachDetachInterruptIsr, + DeviceInterrupt_EvtInterruptDpc); + InterruptConfig.CanWakeDevice = TRUE; + InterruptConfig.PassiveHandling = TRUE; + InterruptToCreate = &ControllerContext->AttachDetachInterrupt; + + } else { + TraceWarning("Other interrupt resource [0X%X] is detected and is being" + " ignored", InterruptResourceRaw->u.Interrupt.Vector); + Status = STATUS_SUCCESS; + goto End; + } + + InterruptConfig.InterruptRaw = InterruptResourceRaw; + InterruptConfig.InterruptTranslated = InterruptResourceTranslated; + + Status = WdfInterruptCreate( + Device, + &InterruptConfig, + &Attributes, + InterruptToCreate); + CHK_NT_MSG(Status, "Failed to create Device interrupt"); + +End: + TraceExit(); + return Status; +} + +BOOLEAN +DeviceInterrupt_EvtInterruptIsr( + _In_ WDFINTERRUPT Interrupt, + _In_ ULONG MessageID + ) +/*++ + +Routine Description: + + 'EvtInterruptIsr' handler for the device interrupt object. + http://msdn.microsoft.com/en-us/library/windows/hardware/ff541735(v=vs.85).aspx + +Arguments: + + Interrupt - Associated interrupt object. + + MessageID - Message IDs for MSI + +Return Value: + + Appropriate NTSTATUS value + +--*/ +{ + BOOLEAN PendingEvents; + + TraceEntry(); + + UNREFERENCED_PARAMETER(MessageID); + + // + // #### TODO: Determine if controller has pending events. #### + // + + // Sample will assume there is always a pending event for illustration purposes. + PendingEvents = TRUE; + + if (PendingEvents) { + // + // Enqueue the DPC to handle the events. + // + WdfInterruptQueueDpcForIsr(Interrupt); + } + + TraceExit(); + return TRUE; +} + +VOID +DeviceInterrupt_EvtInterruptDpc ( + _In_ WDFINTERRUPT Interrupt, + _In_ WDFOBJECT AssociatedObject + ) +/*++ + +Routine Description: + + 'EvtInterruptDpc' handler for the device interrupt object. + http://msdn.microsoft.com/en-us/library/windows/hardware/ff541721(v=vs.85).aspx + +Arguments: + + Interrupt - Associated interrupt object. + + AssociatedObject - FDO Object + +--*/ +{ + WDFDEVICE WdfDevice; + PDEVICE_INTERRUPT_CONTEXT InterruptContext; + PCONTROLLER_CONTEXT ControllerContext; + BOOLEAN Attached; + BOOLEAN GotAttachOrDetach; + CONTROLLER_EVENT ControllerEvent; + + UNREFERENCED_PARAMETER(Interrupt); + + TraceEntry(); + + WdfDevice = (WDFDEVICE) AssociatedObject; + ControllerContext = DeviceGetControllerContext(WdfDevice); + + WdfSpinLockAcquire(ControllerContext->DpcLock); + + WdfInterruptAcquireLock(ControllerContext->DeviceInterrupt); + Attached = ControllerContext->Attached; + GotAttachOrDetach = ControllerContext->GotAttachOrDetach; + ControllerContext->GotAttachOrDetach = FALSE; + WdfInterruptReleaseLock(ControllerContext->DeviceInterrupt); + + // + // Handle attach/detach events + // + if (GotAttachOrDetach) { + if (Attached && ControllerContext->WasAttached) { + // + // We must have gotten at least one detach. Need to reset the state. + // + ControllerContext->RemoteWakeupRequested = FALSE; + ControllerContext->Suspended = FALSE; + UfxDeviceNotifyDetach(ControllerContext->UfxDevice); + } + + if (Attached) { + ControllerContext->RemoteWakeupRequested = FALSE; + ControllerContext->Suspended = FALSE; + UfxDeviceNotifyAttach(ControllerContext->UfxDevice); + } + } + + ControllerContext->WasAttached = Attached; + + InterruptContext = DeviceInterruptGetContext(ControllerContext->DeviceInterrupt); + + // + // #### TODO: Insert code to read and dispatch events from the controller #### + // + + // The sample will assume an endpoint event of EndpointEventTransferComplete + ControllerEvent.Type = EventTypeEndpoint; + ControllerEvent.u.EndpointEvent = EndpointEventTransferComplete; + + // + // Handle events from the controller + // + switch (ControllerEvent.Type) { + case EventTypeDevice: + HandleDeviceEvent(WdfDevice, ControllerEvent.u.DeviceEvent); + break; + + case EventTypeEndpoint: + HandleEndpointEvent(WdfDevice, ControllerEvent.u.EndpointEvent); + break; + } + + WdfSpinLockRelease(ControllerContext->DpcLock); + + TraceExit(); +} + +BOOLEAN +DeviceInterrupt_EvtAttachDetachInterruptIsr ( + _In_ WDFINTERRUPT Interrupt, + _In_ ULONG MessageID + ) +/*++ + +Routine Description: + + 'EvtInterruptIsr' handler for the attach/detach interrupt object. + http://msdn.microsoft.com/en-us/library/windows/hardware/ff541735(v=vs.85).aspx + +Arguments: + + Interrupt - Associated interrupt object. + + MessageID - Message IDs for MSI + +Return Value: + + BOOLEAN + +--*/ +{ + PCONTROLLER_CONTEXT ControllerContext; + + UNREFERENCED_PARAMETER(MessageID); + + TraceEntry(); + + ControllerContext = DeviceGetControllerContext(WdfInterruptGetDevice(Interrupt)); + + WdfInterruptAcquireLock(ControllerContext->DeviceInterrupt); + + // + // This is an ActiveBoth interrupt used for attach/detach. State is determined + // by counting interrupts. Previous state was attached, so this is a detach. + // + if (!ControllerContext->Attached) { + ControllerContext->Attached = TRUE; + ControllerContext->GotAttachOrDetach = TRUE; + + (void) WdfInterruptQueueDpcForIsr(Interrupt); + } else { + ControllerContext->Attached = FALSE; + ControllerContext->GotAttachOrDetach = TRUE; + (void) WdfInterruptQueueDpcForIsr(Interrupt); + } + + WdfInterruptReleaseLock(ControllerContext->DeviceInterrupt); + + TraceExit(); + return TRUE; +} diff --git a/usb/ufxclientsample/interrupt.h b/usb/ufxclientsample/interrupt.h new file mode 100644 index 000000000..9fe520d24 --- /dev/null +++ b/usb/ufxclientsample/interrupt.h @@ -0,0 +1,47 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + interrupt.h + +Abstract: + + Defines the structures and functions needed to manage wdf interrupt object. + +Environment: + + Kernel mode + +--*/ +#pragma once + +#include "registers.h" + +// +// Context space for device WDFINTERRUPT object +// +typedef struct _DEVICE_INTERRUPT_CONTEXT { + + WDFCOMMONBUFFER Buffer; + +} DEVICE_INTERRUPT_CONTEXT, *PDEVICE_INTERRUPT_CONTEXT; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(DEVICE_INTERRUPT_CONTEXT, DeviceInterruptGetContext) + +_Must_inspect_result_ +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS +InterruptCreate ( + _In_ WDFDEVICE Device, + _In_ PCM_PARTIAL_RESOURCE_DESCRIPTOR InterruptResourceRaw, + _In_ PCM_PARTIAL_RESOURCE_DESCRIPTOR InterruptResourceTranslated + ); + + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +InterruptProgramEventBuffers ( + _In_ WDFINTERRUPT DeviceInterrupt + ); \ No newline at end of file diff --git a/usb/ufxclientsample/readme.md b/usb/ufxclientsample/readme.md new file mode 100644 index 000000000..ec39fd35a --- /dev/null +++ b/usb/ufxclientsample/readme.md @@ -0,0 +1,28 @@ +USB Function Client Driver +========================== + +This is a skeleton sample driver that shows how to create a Windows USB function controller driver using the USB function class extension driver (UFX). + +This sample demonstrates the following: + +- Registration with the UFX class extension driver +- Handling USB transfers +- Handling function controller events +- Handling attach and detach notifications +- Handling charger/port detection +- Power management + +Operating system requirements +----------------------------- + +Windows 10 Mobile + +Customizing the sample for your device +-------------------------------------- + +This sample is not a functional driver. It is a skeleton driver intended to illustrate the general design of a UFX client driver. The sample contains a number of comments prefaced with " #### TODO ", which indicates where code will need to be added to perform the controller operation as described in the comment. + +Installation Note +----------------- + +Installation on Windows 10 Mobile requires the creation of a package. To properly interact with the USB UI on Windows 10 Mobile, the package must include a Security Element that specifies the ID_CAP_USB capability with DEVICE_READ and DEVICE_WRITE rights. diff --git a/usb/ufxclientsample/registers.c b/usb/ufxclientsample/registers.c new file mode 100644 index 000000000..3784f90e6 --- /dev/null +++ b/usb/ufxclientsample/registers.c @@ -0,0 +1,90 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + registers.c + +Abstract: + + Memory mapping the controller's registers + +Environment: + + Kernel mode + +--*/ + +#include "device.h" +#include "registers.h" +#include "registers.tmh" + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (PAGE, RegistersCreate) +#endif + + +_Must_inspect_result_ +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS +RegistersCreate( + _In_ WDFDEVICE Device, + _In_ PCM_PARTIAL_RESOURCE_DESCRIPTOR RegistersResource + ) +/*++ + +Routine Description: + + Helper function to map the memory resources to the HW registers. + +Arguments: + + Device - Wdf device object corresponding to the FDO + + RegisterResource - Raw resource for the memory + +Return Value: + + Appropriate NTSTATUS value + +--*/ +{ + NTSTATUS Status; + PREGISTERS_CONTEXT Context; + WDF_OBJECT_ATTRIBUTES Attributes; + + TraceEntry(); + + PAGED_CODE(); + + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&Attributes, REGISTERS_CONTEXT); + + Status = WdfObjectAllocateContext(Device, &Attributes, &Context); + if (Status == STATUS_OBJECT_NAME_EXISTS) { + // + // In the case of a resource rebalance, the context allocated + // previously still exists. + // + Status = STATUS_SUCCESS; + RtlZeroMemory(Context, sizeof(*Context)); + } + CHK_NT_MSG(Status, "Failed to allocate context for registers"); + + Context->RegisterBase = MmMapIoSpaceEx( + RegistersResource->u.Memory.Start, + RegistersResource->u.Memory.Length, + PAGE_NOCACHE | PAGE_READWRITE); + + if (Context->RegisterBase == NULL) { + Status = STATUS_INSUFFICIENT_RESOURCES; + CHK_NT_MSG(Status, "MmMapIoSpaceEx failed"); + } + + Context->RegistersLength = RegistersResource->u.Memory.Length; + +End: + + TraceExit(); + return Status; +} \ No newline at end of file diff --git a/usb/ufxclientsample/registers.h b/usb/ufxclientsample/registers.h new file mode 100644 index 000000000..bf88e9e85 --- /dev/null +++ b/usb/ufxclientsample/registers.h @@ -0,0 +1,58 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + registers.h + +Abstract: + +Environment: + + Kernel mode + +--*/ +#pragma once + +#include +#pragma warning(push) +// +// #### TODO: Add controller data structure definitions here #### +// +#pragma warning(pop) +#include + + +#define USB_LINK_STATE_U0 0 +#define USB_LINK_STATE_U1 1 +#define USB_LINK_STATE_U2 2 +#define USB_LINK_STATE_U3 3 +#define USB_LINK_STATE_SS_DISABLED 4 +#define USB_LINK_STATE_RX_DETECT 5 +#define USB_LINK_STATE_SS_INACTIVE 6 +#define USB_LINK_STATE_POLLING 7 +#define USB_LINK_STATE_RECOVERY 8 +#define USB_LINK_STATE_HOT_RESET 9 +#define USB_LINK_STATE_COMPLIANCE_MODE 10 +#define USB_LINK_STATE_LOOPBACK 11 +#define USB_LINK_STATE_RESUME_RESET 15 + + +typedef struct _REGISTERS_CONTEXT { + + PUCHAR RegisterBase; + + SIZE_T RegistersLength; + +} REGISTERS_CONTEXT, *PREGISTERS_CONTEXT; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(REGISTERS_CONTEXT, DeviceGetRegistersContext) + +_Must_inspect_result_ +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS +RegistersCreate( + _In_ WDFDEVICE Device, + _In_ PCM_PARTIAL_RESOURCE_DESCRIPTOR RegistersResource + ); \ No newline at end of file diff --git a/usb/ufxclientsample/trace.h b/usb/ufxclientsample/trace.h new file mode 100644 index 000000000..00ce3513e --- /dev/null +++ b/usb/ufxclientsample/trace.h @@ -0,0 +1,113 @@ +/*++ + +Module Name: + + Trace.h + +Abstract: + + Header file for the debug tracing related functions and macros. + +Environment: + + Kernel mode + +--*/ + +#pragma once + +#include "UfxClientSample.h" + +// +// Define the tracing flags. +// +// WPP Tracing GUID - 302A11C3-1DB2-4B1E-B549-F5300003894A +// + +#define WPP_CONTROL_GUIDS \ + WPP_DEFINE_CONTROL_GUID( \ + UfxClientSampleWPPGuid, (302A11C3,1DB2,4B1E,B549,F5300003894A), \ + WPP_DEFINE_BIT(FlagDriverWideLog) \ + WPP_DEFINE_BIT(FlagCallStack) \ + ) + +#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* functions +// +// begin_wpp config +// FUNC TraceMessage(LEVEL,FLAGS, MSG,...); +// FUNC TraceFatal{LEVEL=TRACE_LEVEL_FATAL,FLAGS=FlagDriverWideLog}(MSG,...); +// FUNC TraceError{LEVEL=TRACE_LEVEL_ERROR,FLAGS=FlagDriverWideLog}(MSG,...); +// FUNC TraceWarning{LEVEL=TRACE_LEVEL_WARNING,FLAGS=FlagDriverWideLog}(MSG,...); +// FUNC TraceInformation{LEVEL=TRACE_LEVEL_INFORMATION,FLAGS=FlagDriverWideLog}(MSG,...); +// FUNC TraceVerbose{LEVEL=TRACE_LEVEL_VERBOSE,FLAGS=FlagDriverWideLog}(MSG,...); +// end_wpp +// + + +// This block defines the function entry and exit functions +// +// begin_wpp config +// FUNC TraceEntry(); +// FUNC TraceExit(); +// USESUFFIX(TraceEntry, "==>%!FUNC!"); +// USESUFFIX(TraceExit, "<==%!FUNC!"); +// end_wpp +// +#define WPP__ENABLED() WPP_LEVEL_ENABLED(FlagCallStack) +#define WPP__LOGGER() WPP_LEVEL_LOGGER(FlagCallStack) + + +// +// This block defines a CHK_NT_MSG macro that: +// 1. Checks the NTSTATUS message in the first parameter +// 2. Logs status message to WPP and ETW, if the status indicates a failure +// 3. Jumps to label "End" +// +// begin_wpp config +// FUNC CHK_NT_MSG{CHK=DUMMY,LEVEL=TRACE_LEVEL_ERROR,FLAGS=FlagDriverWideLog}(STATUS, MSG, ...); +// USESUFFIX(CHK_NT_MSG, "[%!STATUS!]", STATUS); +// end_wpp +// +#define WPP_CHK_LEVEL_FLAGS_STATUS_PRE(CHK,LEVEL,FLAGS,STATUS) { \ + if (NT_SUCCESS(STATUS) != TRUE) { + +#define WPP_CHK_LEVEL_FLAGS_STATUS_POST(CHK,LEVEL,FLAGS,STATUS) ;\ + EventWriteFailedNtStatus(&UfxClientSampleGuid, __FILE__, __LINE__, STATUS); \ + goto End; \ + } } +#define WPP_CHK_LEVEL_FLAGS_STATUS_ENABLED(chk,level,flags,status) WPP_LEVEL_ENABLED(flags) +#define WPP_CHK_LEVEL_FLAGS_STATUS_LOGGER(chk,level,flags,status) WPP_LEVEL_LOGGER(flags) + +// +// This block defines a LOG_NT_MSG macro that: +// 1. Checks the NTSTATUS message in the first parameter +// 2. And just logs the status message to WPP and ETW, if it indicates a failure +// +// begin_wpp config +// FUNC LOG_NT_MSG{LOG=DUMMY,LEVEL=TRACE_LEVEL_ERROR,FLAGS=FlagDriverWideLog}(STATUS, MSG, ...); +// USESUFFIX(CHK_NT_MSG, "[%!STATUS!]", STATUS); +// end_wpp +// +#define WPP_LOG_LEVEL_FLAGS_STATUS_PRE(CHK,LEVEL,FLAGS,STATUS) { \ + if (NT_SUCCESS(STATUS) != TRUE) { + +#define WPP_LOG_LEVEL_FLAGS_STATUS_POST(CHK,LEVEL,FLAGS,STATUS) ;\ + EventWriteFailedNtStatus(&UfxClientSampleGuid, __FILE__, __LINE__, STATUS); \ + } } + +#define WPP_LOG_LEVEL_FLAGS_STATUS_ENABLED(chk,level,flags,status) WPP_LEVEL_ENABLED(flags) +#define WPP_LOG_LEVEL_FLAGS_STATUS_LOGGER(chk,level,flags,status) WPP_LEVEL_LOGGER(flags) + +// +// This should optimize WPP macros by disbaling checking for 'WPP_INIT_TRACING' +// (okay, because we enable WPP tracing in Driver entry) +// +#define WPP_CHECK_INIT diff --git a/usb/ufxclientsample/transfer.c b/usb/ufxclientsample/transfer.c new file mode 100644 index 000000000..0a34b8f46 --- /dev/null +++ b/usb/ufxclientsample/transfer.c @@ -0,0 +1,2230 @@ +/*++ + +Module Name: + + transfer.c + +Abstract: + + Transfer related functions + +Environment: + + Kernel mode + +--*/ + +#include "transfer.h" +#include "ufxendpoint.h" +#include "registers.h" +#include "device.h" +#include "ufxdevice.h" +#include +#include "trace.h" +#include "transfer.tmh" + +#define MAX_PHYSICAL_ENDPOINTS 32 +#define COMMON_BUFFER_ALIGNMENT 15 +#define ENDPOINT_COMMON_BUFFER_SIZE 0x60 +#define DATA_ALIGNMENT 63 + +FORCEINLINE +VOID +TraceTransfer ( + _In_ PCSTR Stage, + _In_ UFXENDPOINT Endpoint, + _In_opt_ WDFREQUEST Request + ) +{ + WDFDMATRANSACTION Transaction; + ULONG BytesRequested; + ULONG BytesProgrammed; + ULONG BytesTransferred; + ULONG SgProgrammed; + ULONG SgLength; + + Transaction = NULL; + BytesRequested = 0; + BytesProgrammed = 0; + BytesTransferred = 0; + SgProgrammed = 0; + SgLength = 0; + + if (Request == NULL) { + Transaction = UfxEndpointGetTransferContext(Endpoint)->Transaction; + if (Transaction != NULL) { + PDMA_CONTEXT DmaContext; + DmaContext = DmaGetContext(Transaction); + Request = DmaContext->Request; + } + } + + if (Request != NULL) { + PREQUEST_CONTEXT RequestContext; + RequestContext = RequestGetContext(Request); + + if (RequestContext->Transaction != NULL) { + PDMA_CONTEXT DmaContext; + DmaContext = DmaGetContext(RequestContext->Transaction); + + Transaction = RequestContext->Transaction; + BytesRequested = DmaContext->BytesRequested; + BytesProgrammed = DmaContext->BytesProgrammed; + BytesTransferred = BytesProgrammed - DmaContext->BytesRemaining; + if (DmaContext->SgList != NULL) { + SgProgrammed = DmaContext->SgIndex; + SgLength = DmaContext->SgList->NumberOfElements; + } + } + } + + EventWriteTransfer( + &UfxClientSampleGuid, + Stage, + (ULONG) Endpoint, + UfxEndpointGetContext(Endpoint)->PhysicalEndpoint, + (ULONG) Request, + (ULONG) Transaction, + BytesRequested, + BytesProgrammed, + BytesTransferred, + SgProgrammed, + SgLength); + + TraceInformation("TRANSFER %s: %08X (%d), RQ: %08X, DMA: %08X, BytesReq: %08X, BytesProg: %08X, BytesTrans: %08X, SG: %d/%d", + Stage, (ULONG) Endpoint, UfxEndpointGetContext(Endpoint)->PhysicalEndpoint, (ULONG) Request, + (ULONG) Transaction, BytesRequested, BytesProgrammed, BytesTransferred, SgProgrammed, SgLength); +} + +#define TRACE_TRANSFER(Stage, Endpoint, Request) \ + TraceTransfer(Stage, Endpoint, Request) + +EVT_WDF_PROGRAM_DMA OnEvtProgramDma; +EVT_WDF_REQUEST_CANCEL OnEvtRequestCancel; +EVT_WDF_WORKITEM TransferCompleteNext; + +_IRQL_requires_max_(DISPATCH_LEVEL) +WDFDMATRANSACTION +TransferNextRequest ( + _In_ UFXENDPOINT Endpoint + ); + +_IRQL_requires_max_(DISPATCH_LEVEL) +WDFDMATRANSACTION +TransferRequestTryUnmarkCancelableAndCleanup ( + _In_ UFXENDPOINT Endpoint + ); + +_IRQL_requires_max_(DISPATCH_LEVEL) +WDFDMATRANSACTION +TransferCancelCleanup ( + _In_ UFXENDPOINT Endpoint + ); + +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_raises_(DISPATCH_LEVEL) +_IRQL_saves_global_(EpTransferLock, Endpoint) +VOID +TransferLock ( + _In_ UFXENDPOINT Endpoint + ) +/*++ +Routine Description: + + Recursive Spinlock Lock + +Parameters Description: + + Endpoint - The endpoint to lock + +--*/ +{ + WdfSpinLockAcquire(UfxEndpointGetTransferContext(Endpoint)->TransferLock); +} + +_IRQL_requires_(DISPATCH_LEVEL) +_IRQL_restores_global_(EpTransferLock, Endpoint) +VOID +TransferUnlock ( + _In_ UFXENDPOINT Endpoint + ) +/*++ +Routine Description: + + Recursive Spinlock Unlock + +Parameters Description: + + Endpoint - The endpoint to unlock + +--*/ +{ + WdfSpinLockRelease(UfxEndpointGetTransferContext(Endpoint)->TransferLock); +} + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +CommandStallSet ( + _In_ UFXENDPOINT Endpoint + ) +/*++ +Routine Description: + + Sends a stall set command on the endpoint. + +Parameters Description: + + Endpoint - Endpoint to set stall. +--*/ +{ + PTRANSFER_CONTEXT TransferContext; + + TraceEntry(); + + TransferContext = UfxEndpointGetTransferContext(Endpoint); + + // + // #### TODO: Insert code to set stall on the endpoint #### + // + + TransferContext->Stalled = TRUE; + + TraceExit(); +} + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +CommandStallClear ( + _In_ UFXENDPOINT Endpoint + ) +/*++ +Routine Description: + + Sends a stall clear command on the endpoint. + +Parameters Description: + + Endpoint - Endpoint to clear stall. + +--*/ +{ + PTRANSFER_CONTEXT TransferContext; + + TraceEntry(); + + TransferContext = UfxEndpointGetTransferContext(Endpoint); + + NT_ASSERT(!CONTROL_ENDPOINT(Endpoint)); + + // + // #### TODO: Insert code to clear stall on the endpoint #### + // + + TransferContext->Stalled = FALSE; + + TraceExit(); +} + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +TransferCommandEnd ( + _In_ UFXENDPOINT Endpoint + ) +/*++ +Routine Description: + + Commands the controller to End Transfer. + +Parameters Description: + + Endpoint - The endpoint on which to transfer. + +--*/ +{ + PTRANSFER_CONTEXT TransferContext; + + TraceEntry(); + + TransferContext = UfxEndpointGetTransferContext(Endpoint); + + NT_ASSERT(TransferContext->TransferStarted); + if (!TransferContext->EndRequested) { + + TransferContext->EndRequested = TRUE; + + // + // #### TODO: Insert code to end the transfer on the endpoint #### + // + + } + + TraceExit(); +} + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +TransferCommandStart ( + _In_ UFXENDPOINT Endpoint + ) +/*++ +Routine Description: + + Commands the controller to Start Transfer. + +Parameters Description: + + Endpoint - The endpoint on which to transfer. + +--*/ +{ + PTRANSFER_CONTEXT TransferContext; + + TraceEntry(); + + TransferContext = UfxEndpointGetTransferContext(Endpoint); + + TransferContext->TransferStarted = TRUE; + TransferContext->TransferCommandStartComplete = FALSE; + + // + // #### TODO: Insert code to start the transfer on the endpoint #### + // + + TraceExit(); +} + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +TransferCommandUpdate ( + _In_ UFXENDPOINT Endpoint + ) +/*++ +Routine Description: + + Commands the controller to Update Transfer. + +Parameters Description: + + Endpoint - The endpoint on which to transfer. + +--*/ +{ + TraceEntry(); + + UNREFERENCED_PARAMETER(Endpoint); + + // + // #### TODO: Insert code to update the transfer on the endpoint #### + // + + TraceExit(); +} + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +TransferCommandStartOrUpdate ( + _In_ UFXENDPOINT Endpoint + ) +/*++ +Routine Description: + + If Start Transfer has not been called the the ProgrammingRequest, + then this will call Start Transfer, otherwise Update Transfer. + +Parameters Description: + + Endpoint - The endpoint on which to transfer. + +--*/ +{ + PTRANSFER_CONTEXT TransferContext; + + TraceEntry(); + + TransferContext = UfxEndpointGetTransferContext(Endpoint); + + if (TransferContext->TransferStarted) { + TransferCommandUpdate(Endpoint); + + } else { + TransferCommandStart(Endpoint); + } + + TraceExit(); +} + +VOID +TransferDmaExecute ( + _In_opt_ WDFDMATRANSACTION Transaction + ) +/*++ +Routine Description: + + Execute a DMA transfer for non-zero length transfer. + +Parameters Description: + + Transaction - Transaction to execute. + +--*/ +{ + TraceEntry(); + + if (Transaction != NULL) { + NTSTATUS Status; + PDMA_CONTEXT DmaContext; + UFXENDPOINT Endpoint; + + DmaContext = DmaGetContext(Transaction); + Endpoint = DmaContext->Endpoint; + + if (!DmaContext->ZeroLength) { + Status = WdfDmaTransactionExecute(Transaction, DmaContext->Endpoint); + LOG_NT_MSG(Status, "Failed to execute DMA Transaction"); + + if (!NT_SUCCESS(Status)) { + WDFDMATRANSACTION NewTransaction = NULL; + + TransferLock(Endpoint); + if (DmaContext->CleanupOnProgramDma) { + NewTransaction = TransferCancelCleanup(Endpoint); + } else { + NewTransaction = TransferRequestTryUnmarkCancelableAndCleanup(Endpoint); + } + TransferUnlock(Endpoint); + TransferDmaExecute(NewTransaction); + } + } + } + + TraceExit(); +} + +VOID +OnEvtRequestCancel ( + _In_ WDFREQUEST Request + ) +/*++ +Routine Description: + + EvtRequestCancel callback for cancellable transfer requests. + This cancels a programmed transfer request by issuing a corresponding + 'TransferEnd' command to the contoller. + +Parameters Description: + + Request - Request being cancelled. + +--*/ +{ + TraceEntry(); + TransferRequestCancel(Request, FALSE); + TraceExit(); +} + +_Use_decl_annotations_ +VOID +TransferRequestCancel ( + WDFREQUEST Request, + BOOLEAN QueueIsStopped + ) +/*++ +Routine Description: + + This cancels a programmed transfer request by issuing a corresponding + 'TransferEnd' command to the contoller. + +Parameters Description: + + Request - Request being cancelled. + + QueueIsStopped - indicates if this request is being stopped and cancellation + should avoid fetching subsequent request from queue + +--*/ +{ + PREQUEST_CONTEXT RequestContext; + PTRANSFER_CONTEXT TransferContext; + WDFDMATRANSACTION NewTransaction; + UFXENDPOINT Endpoint; + NTSTATUS Status; + BOOLEAN Cleanup; + + TraceEntry(); + + RequestContext = RequestGetContext(Request); + TransferContext = UfxEndpointGetTransferContext(RequestContext->Endpoint); + NewTransaction = NULL; + Endpoint = RequestContext->Endpoint; + + TransferLock(Endpoint); + + if (QueueIsStopped) { + TRACE_TRANSFER("STOP", Endpoint, Request); + RequestContext->QueueIsStopped = TRUE; + + Status = WdfRequestUnmarkCancelable(Request); + CHK_NT_MSG(Status, "WdfRequestUnmarkCancelable failed during queue stop"); + + } else { + TRACE_TRANSFER("CANCEL", Endpoint, Request); + } + + // + // After this function exists, KMDF will release the reference to this + // request. Acquire a reference to make sure it doesn't get deleted. + // + RequestContext->ReferencedOnCancel = TRUE; + WdfObjectReference(Request); + + Cleanup = TRUE; + if (RequestContext->Transaction != NULL) { + PDMA_CONTEXT DmaContext; + + DmaContext = DmaGetContext(RequestContext->Transaction); + + // + // Try to cancel the transaction. If we succeed, we can complete the request. + // + if (!DmaContext->State == NotExecuted) { + if (WdfDmaTransactionCancel(RequestContext->Transaction)) { + // + // Contrary to what MSDN says, completing the transaction at this point + // is considered an error by driver verifier since the DMA state is + // FxDmaTransactionStateTransferFailed. + // + + DmaContext->State = Cancelled; + + } else { + DmaContext->CleanupOnProgramDma = TRUE; + Cleanup = FALSE; + } + } + } + + // + // If a transfer is in progress, end it. Otherwise, just clean + // up the request. + // + if (TransferContext->TransferStarted) { + TransferContext->CleanupOnEndComplete = TRUE; + TransferCommandEnd(Endpoint); + + } else if (Cleanup) { + NewTransaction = TransferCancelCleanup(Endpoint); + } + +End: + TransferUnlock(Endpoint); + TransferDmaExecute(NewTransaction); + + TraceExit(); +} + + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +TransferSetupPacket ( + _In_ UFXENDPOINT Endpoint + ) +/*++ +Routine Description: + + Programs a setup packet for a control endpoint. This must be called + when the endpoint is first configured, when it is stalled, + and every time a handshake is completed. + +Parameters Description: + + Endpoint - The endpoint on which to transfer. + +--*/ +{ + PCONTROL_CONTEXT ControlContext; + + TraceEntry(); + + ControlContext = UfxEndpointGetControlContext(Endpoint); + + TRACE_TRANSFER("SETUP", Endpoint, NULL); + + // + // Start transfer + // + + TransferCommandStart(Endpoint); + ControlContext->SetupRequested = TRUE; + ControlContext->HandshakeRequested = FALSE; + ControlContext->DataStageExists = FALSE; + ControlContext->ReadyForHandshake = FALSE; + + TraceExit(); +} + +_IRQL_requires_max_(DISPATCH_LEVEL) +WDFDMATRANSACTION +TransferRequestTryUnmarkCancelableAndCleanup ( + _In_ UFXENDPOINT Endpoint + ) +/*++ +Routine Description: + + Attempt to cancel a pending transfer for an endpoint + +Parameters Description: + + Endpoint - The endpoint for the transfer to be cancelled. + +--*/ +{ + NTSTATUS Status; + PTRANSFER_CONTEXT TransferContext; + WDFDMATRANSACTION NewTransaction; + WDFREQUEST RequestToCancel; + + TraceEntry(); + + TransferContext = UfxEndpointGetTransferContext(Endpoint); + NewTransaction = NULL; + RequestToCancel = NULL; + + // + // Try to find a request to clean up. + // + if (TransferContext->Transaction != NULL) { + PDMA_CONTEXT DmaContext; + DmaContext = DmaGetContext(TransferContext->Transaction); + + RequestToCancel = DmaContext->Request; + + } else if (CONTROL_ENDPOINT(Endpoint)) { + PCONTROL_CONTEXT ControlContext; + ControlContext = UfxEndpointGetControlContext(Endpoint); + + RequestToCancel = ControlContext->HandshakeRequest; + } + + // + // Before canceling, we need to make sure we can unmark it cancelable. + // If not, then EvtRequestCancel will get called, and it will cancel + // the request. + // + if (RequestToCancel != NULL) { + Status = WdfRequestUnmarkCancelable(RequestToCancel); + CHK_NT_MSG(Status, "Failed to unmark cancelable on try cleanup"); + } + + NewTransaction = TransferCancelCleanup(Endpoint); + +End: + TraceExit(); + return NewTransaction; +} + +_IRQL_requires_max_(DISPATCH_LEVEL) +WDFDMATRANSACTION +TransferCancelCleanup ( + _In_ UFXENDPOINT Endpoint + ) +/*++ +Routine Description: + + Cleans up and cancels the current transfer for an endpoint + +Parameters Description: + + Endpoint - The endpoint for the transfer to be cancelled. + +--*/ +{ + PTRANSFER_CONTEXT TransferContext; + NTSTATUS Status; + WDFDMATRANSACTION Transaction; + BOOLEAN FetchNextRequest; + WDFREQUEST RequestToCancel; + WDFDMATRANSACTION NewTransaction; + + TraceEntry(); + + TransferContext = UfxEndpointGetTransferContext(Endpoint); + + FetchNextRequest = FALSE; + Transaction = NULL; + RequestToCancel = NULL; + NewTransaction = NULL; + + // + // Reset data transfer state and complete request + // + Transaction = TransferContext->Transaction; + if (Transaction != NULL) { + PDMA_CONTEXT DmaContext; + + DmaContext = DmaGetContext(Transaction); + + RequestToCancel = DmaContext->Request; + TRACE_TRANSFER("CLEANUP (Data)", Endpoint, RequestToCancel); + if (DmaContext->State == Executed) { + (void) WdfDmaTransactionDmaCompletedFinal(Transaction, 0, &Status); + DmaContext->State = Completed; + } + WdfObjectDelete(Transaction); + TransferContext->Transaction = NULL; + } + + // + // Reset control endpoint state. + // + if (CONTROL_ENDPOINT(Endpoint)) { + PCONTROL_CONTEXT ControlContext; + + ControlContext = UfxEndpointGetControlContext(Endpoint); + ControlContext->DataStageExists = FALSE; + ControlContext->HandshakeRequested = FALSE; + ControlContext->SetupRequested = FALSE; + ControlContext->ReadyForHandshake = FALSE; + if (ControlContext->HandshakeRequest != NULL) { + RequestToCancel = ControlContext->HandshakeRequest; + TRACE_TRANSFER("CLEANUP (Handshake)", Endpoint, RequestToCancel); + ControlContext->HandshakeRequest = NULL; + } + } + + if (RequestToCancel != NULL) { + BOOLEAN Referenced; + PREQUEST_CONTEXT RequestContext; + + RequestContext = RequestGetContext(RequestToCancel); + + FetchNextRequest = !RequestContext->QueueIsStopped; + + Referenced = RequestContext->ReferencedOnCancel; + + // + // In case the completion work-item has already been queued, prevent + // it from also trying to complete this request when it executes. + // + TransferContext->PendingCompletion = FALSE; + + WdfRequestComplete(RequestToCancel, STATUS_CANCELLED); + if (Referenced) { + WdfObjectDereference(RequestToCancel); + } + } + + // + // If endpoint is disabled, need to clear any stall. + // + if (!TransferContext->Enabled) { + if (TransferContext->Stalled) { + CommandStallClear(Endpoint); + } + + // + // Bring control endpoint back to setup stage. + // + } else if (CONTROL_ENDPOINT(Endpoint)) { + TransferSetupPacket(Endpoint); + + // + // Otherwise, we can check if there's another request. + // + } else if (FetchNextRequest) { + NewTransaction = TransferNextRequest(Endpoint); + } + + TraceExit(); + + return NewTransaction; +} + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +TransferProgram ( + UFXENDPOINT Endpoint, + WDFDMATRANSACTION Transaction + ) +/*++ +Routine Description: + + DMA callback for transfers that use DMA. This function may be called + multiple times for a single transfer request. + +Parameters Description: + + Endpoint - The endpoint for the transfer. + + Transaction - The DMA transaction object associated with the request. + +Return Value: + + TRUE if nothing went wrong, FALSE otherwise. + +--*/ +{ + PTRANSFER_CONTEXT TransferContext; + PDMA_CONTEXT DmaContext; + PUFXENDPOINT_CONTEXT EpContext; + + TraceEntry(); + + TransferContext = UfxEndpointGetTransferContext(Endpoint); + EpContext = UfxEndpointGetContext(Endpoint); + DmaContext = DmaGetContext(Transaction); + + DmaContext->BytesProgrammedCurrent = 0; + DmaContext->BytesRemaining = 0; + + TRACE_TRANSFER("PROGRAMMING", Endpoint, DmaContext->Request); + + // + // Try to program transfer structures + // + while (DmaContext->SgIndex < DmaContext->SgList->NumberOfElements) { + PSCATTER_GATHER_ELEMENT Sg; + BOOLEAN NoMoreSgs; + BOOLEAN DmaComplete; + BOOLEAN LastSgInDma; + + Sg = &DmaContext->SgList->Elements[DmaContext->SgIndex]; + + NoMoreSgs = (DmaContext->SgIndex == DmaContext->SgList->NumberOfElements - 1); + LastSgInDma = ((DmaContext->BytesProgrammed + Sg->Length) == DmaContext->BytesRequested); + + // + // #### TODO: Insert code to map scatter gather buffers to controller transfer structures #### + // + + // + // Need to remember how much we really programmed + // + DmaContext->BytesProgrammed += Sg->Length; + DmaContext->BytesProgrammedCurrent += Sg->Length; + DmaComplete = (DmaContext->BytesProgrammed == DmaContext->BytesRequested); + + // + // Advance to next SG + // + DmaContext->SgIndex += 1; + } + + // + // Append IN ZLP or extra OUT buffer if needed. + // + if (DmaContext->BytesProgrammed == DmaContext->BytesRequested && + DmaContext->NeedExtraBuffer) { + + TRACE_TRANSFER("EXTRA", Endpoint, DmaContext->Request); + + // + // #### TODO: Insert code to append an extra buffer to the transfer structures #### + // + + } + + // + // Update the BytesRemaining to reflect all the programmed bytes + // + DmaContext->BytesRemaining = DmaContext->BytesProgrammedCurrent; + + // + // Finally, send the command to start or continue this transfer. + // + TransferCommandStartOrUpdate(Endpoint); + + TRACE_TRANSFER("PROGRAMMING--", Endpoint, DmaContext->Request); + + TraceExit(); +} + +BOOLEAN +OnEvtProgramDma ( + _In_ WDFDMATRANSACTION Transaction, + _In_ WDFDEVICE Device, + _In_ WDFCONTEXT Context, + _In_ WDF_DMA_DIRECTION Direction, + _In_ PSCATTER_GATHER_LIST SgList + ) +/*++ +Routine Description: + + WDF EvtProgramDma event callback function + +Parameters Description: + + Transaction - The DMA transaction object associated with the request. + + Device - The device. + + Context - The UFXENDPOINT associated with the request. + + Direction - The direction of the transfer. + + SgList - An array of logical pointers to chunks of the data buffer. + +--*/ +{ + NTSTATUS Status; + UFXENDPOINT Endpoint; + PDMA_CONTEXT DmaContext; + WDFDMATRANSACTION NewTransaction; + + UNREFERENCED_PARAMETER(Device); + UNREFERENCED_PARAMETER(Direction); + + TraceEntry(); + + Endpoint = (UFXENDPOINT) Context; + DmaContext = DmaGetContext(Transaction); + + TransferLock(Endpoint); + + DmaContext->State = Executed; + NewTransaction = NULL; + + // + // EvtRequestCancel was called, but couldn't cancel the + // request becuase this function needed to run. + // + if (DmaContext->CleanupOnProgramDma) { + NewTransaction = TransferCancelCleanup(Endpoint); + goto End; + } + + // + // Don't program cancelled requests. + // + Status = WdfRequestUnmarkCancelable(DmaContext->Request); + CHK_NT_MSG(Status, "WdfRequestUnmarkCancelable failed during programming"); + + Status = WdfRequestMarkCancelableEx(DmaContext->Request, OnEvtRequestCancel); + if (Status == STATUS_CANCELLED) { + LOG_NT_MSG(Status, "Request cancelled during programming"); + NewTransaction = TransferCancelCleanup(Endpoint); + goto End; + + } else { + CHK_NT_MSG(Status, "Failed to mark request cancelable"); + } + + DmaContext->SgList = SgList; + DmaContext->SgIndex = 0; + TransferProgram(Endpoint, Transaction); + Transaction = NULL; + +End: + TransferUnlock(Endpoint); + TransferDmaExecute(NewTransaction); + TraceExit(); + return TRUE; +} + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +TransferCommandEndComplete ( + _In_ UFXENDPOINT Endpoint + ) +/*++ +Routine Description: + + Handles endpoint command complete event for the End Transfer commmand. + +Parameters Description: + + Endpoint - The endpoint that received the event. + +--*/ +{ + PTRANSFER_CONTEXT TransferContext; + WDFDMATRANSACTION Transaction; + + TraceEntry(); + + TransferLock(Endpoint); + + TransferContext = UfxEndpointGetTransferContext(Endpoint); + + TransferContext->TransferStarted = FALSE; + TransferContext->EndRequested = FALSE; + + Transaction = NULL; + if (TransferContext->CleanupOnEndComplete) { + TransferContext->CleanupOnEndComplete = FALSE; + Transaction = TransferCancelCleanup(Endpoint); + } + + TransferUnlock(Endpoint); + TransferDmaExecute(Transaction); + TraceExit(); +} + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +TransferCommandStartComplete ( + _In_ UFXENDPOINT Endpoint + ) +/*++ +Routine Description: + + Handles endpoint command complete event for the Start Transfer commmand. + +Parameters Description: + + Endpoint - The endpoint that received the event. + +--*/ +{ + PTRANSFER_CONTEXT TransferContext; + WDFDMATRANSACTION Transaction; + ULONG CommandStatus; + + TraceEntry(); + + TransferLock(Endpoint); + + TransferContext = UfxEndpointGetTransferContext(Endpoint); + Transaction = NULL; + + // + // #### TODO: Insert code to determine status of command #### + // + + // sample will assume no error for illustration purposes + CommandStatus = 0; + + // + // Command start has failed. Clean up the request. + // + if (CommandStatus != 0) { + TRACE_TRANSFER("START FAIL", Endpoint, NULL); + + TransferContext->TransferStarted = FALSE; + + if (CONTROL_ENDPOINT(Endpoint)) { + PCONTROL_CONTEXT ControlContext; + + ControlContext = UfxEndpointGetControlContext(Endpoint); + + if (ControlContext->SetupRequested) { + // + // Error on a setup request. Wait for host to reset us or user reconnect. + // + TraceError("ERROR: Failed a setup packet!"); + NT_ASSERT(FALSE); + + } else { + CommandStallSet(Endpoint); + } + + } else { + Transaction = TransferRequestTryUnmarkCancelableAndCleanup(Endpoint); + } + + } else { + TransferContext->TransferCommandStartComplete = TRUE; + } + + TransferUnlock(Endpoint); + TransferDmaExecute(Transaction); + TraceExit(); +} + +VOID +TransferCompleteNext ( + WDFWORKITEM WorkItem +) +/*++ +Routine Description: + + Work item function that handles completion of a transfer + +Parameters Description: + + WorkItem - Work item that was queued to execute this function. + +--*/ +{ + NTSTATUS Status; + WDFDMATRANSACTION Transaction; + PTRANSFER_CONTEXT TransferContext; + PDMA_CONTEXT DmaContext; + UFXENDPOINT Endpoint; + + TraceEntry(); + + Endpoint = WdfWorkItemGetParentObject(WorkItem); + TransferContext = UfxEndpointGetTransferContext(Endpoint); + + TransferLock(Endpoint); + + // + // See if the request was completed by the cancellation code + // before this work-item executed. + // + if (TransferContext->PendingCompletion == FALSE) { + Transaction = NULL; + goto End; + } + + Transaction = TransferContext->Transaction; + + // + // Make sure request wasn't cancelled before work item got to run. + // + if (Transaction == NULL) { + goto End; + } + + DmaContext = DmaGetContext(Transaction); + + if (DmaContext->State == Executed) { + DmaContext->State = Completed; + (void) WdfDmaTransactionDmaCompletedFinal(Transaction, 0, &Status); + } + + Status = WdfRequestUnmarkCancelable(DmaContext->Request); + if (NT_SUCCESS(Status)) { + ULONG BytesTransferred; + TRACE_TRANSFER("COMPLETE", Endpoint, DmaContext->Request); + + BytesTransferred = DmaContext->BytesProgrammed - DmaContext->BytesRemaining; + if (BytesTransferred > DmaContext->BytesRequested) { + TraceWarning("Transferred more than what is asked for: " + "Bytes Requested: %d, Bytes Transferred:%d", + DmaContext->BytesRequested, BytesTransferred); + BytesTransferred = DmaContext->BytesRequested; + } + + TransferContext->PendingCompletion = FALSE; + + WdfRequestCompleteWithInformation( + DmaContext->Request, + Status, + BytesTransferred); + WdfObjectDelete(Transaction); + TransferContext->Transaction = NULL; + } else { + Transaction = NULL; + CHK_NT_MSG(Status, "Failed to unmark status cancellable"); + } + + TransferContext->TransferStarted = FALSE; + Transaction = TransferNextRequest(Endpoint); + +End: + TransferUnlock(Endpoint); + + if (Transaction != NULL) { + TransferDmaExecute(Transaction); + } + + TraceExit(); +} + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +TransferCompleteData ( + _In_ UFXENDPOINT Endpoint + ) +/*++ +Routine Description: + + Handles completion of a data transfer for an endpoint. + +Parameters Description: + + Endpoint - The endpoint that completed the data transfer. + +--*/ +{ + PTRANSFER_CONTEXT TransferContext; + ULONG BytesRemaining; + WDFDMATRANSACTION Transaction; + PDMA_CONTEXT DmaContext; + WDFDMATRANSACTION NewTransaction; + BOOLEAN TransferComplete; + + TraceEntry(); + + TransferLock(Endpoint); + + TransferContext = UfxEndpointGetTransferContext(Endpoint); + BytesRemaining = 0; + NewTransaction = NULL; + + // + // Make sure request wasn't cancelled. + // + Transaction = TransferContext->Transaction; + if (Transaction == NULL) { + TRACE_TRANSFER("COMPLETION SKIPPED - Cancelled", Endpoint, NULL); + goto End; + } + + DmaContext = DmaGetContext(Transaction); + + // + // #### TODO: Insert code to determine if the transfer is complete, or terminated due to a short packet. #### + + // Sample will assume transfer is complete + TransferComplete = TRUE; + + // + // If the transfer is complete we need to complete the request. + // + if (TransferComplete) { + DmaContext->BytesRemaining = BytesRemaining; + TRACE_TRANSFER("COMPLETE (Last packet or short packet)", Endpoint, NULL); + TransferContext->PendingCompletion = TRUE; + WdfWorkItemEnqueue(TransferContext->CompletionWorkItem); + + // + // If transfer is still in progress, we need to program more transfers + // + } else { + NTSTATUS Status; + ULONG BytesTransferred; + + TRACE_TRANSFER("COMPLETE (In Progress)", Endpoint, NULL); + + DmaContext->State = NotExecuted; + + BytesTransferred = DmaContext->BytesProgrammedCurrent - BytesRemaining; + + TransferUnlock(Endpoint); + WdfDmaTransactionDmaCompletedWithLength(Transaction, BytesTransferred, &Status); + TransferLock(Endpoint); + + NT_ASSERT(!NT_SUCCESS(Status)); + + if (Status != STATUS_MORE_PROCESSING_REQUIRED && !NT_SUCCESS(Status)) { + NewTransaction = TransferRequestTryUnmarkCancelableAndCleanup(Endpoint); + } + } + +End: + TransferUnlock(Endpoint); + TransferDmaExecute(NewTransaction); + TraceExit(); +} + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +TransferCompleteControl ( + _In_ UFXENDPOINT Endpoint + ) +/*++ +Routine Description: + + Handles completion control transfer stage. + +Parameters Description: + + Endpoint - The control endpoint that completed the control transfer stage. + +--*/ +{ + PTRANSFER_CONTEXT TransferContext; + PCONTROL_CONTEXT ControlContext; + + TraceEntry(); + + TransferLock(Endpoint); + + TransferContext = UfxEndpointGetTransferContext(Endpoint); + ControlContext = UfxEndpointGetControlContext(Endpoint); + + NT_ASSERT(!(ControlContext->SetupRequested && ControlContext->HandshakeRequested)); + + // + // Handle setup packet completion + // + if (ControlContext->SetupRequested) { + TRACE_TRANSFER("COMPLETE (Setup)", Endpoint, NULL); + + ControlContext->SetupRequested = FALSE; + TransferContext->TransferStarted = FALSE; + + UfxEndpointNotifySetup(Endpoint, ControlContext->SetupPacket); + + // + // Handle control status handshake completion + // + } else if (ControlContext->HandshakeRequested) { + NTSTATUS Status; + + TRACE_TRANSFER("COMPLETE (Handshake)", Endpoint, ControlContext->HandshakeRequest); + + Status = WdfRequestUnmarkCancelable(ControlContext->HandshakeRequest); + if (Status != STATUS_CANCELLED) { + WdfRequestComplete(ControlContext->HandshakeRequest, Status); + } else { + CHK_NT_MSG(Status, "Failed to unmark cancelable"); + } + + ControlContext->HandshakeRequested = FALSE; + ControlContext->HandshakeRequest = NULL; + TransferContext->TransferStarted = FALSE; + + TransferSetupPacket(Endpoint); + + // + // Handle data stage + // + } else if (ControlContext->DataStageExists) { + TransferUnlock(Endpoint); + TransferCompleteData(Endpoint); + TransferLock(Endpoint); + + } else { + TraceWarning("Unexpected transfer complete on control endpoint!"); + NT_ASSERT(FALSE); + } + +End: + TransferUnlock(Endpoint); + TraceExit(); +} + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +TransferComplete ( + _In_ UFXENDPOINT Endpoint + ) +/*++ +Routine Description: + + Handles endpoint XferComplete or XferInProgress events. + +Parameters Description: + + Endpoint - The endpoint that received the event. + + Event - The event received. + +--*/ +{ + TraceEntry(); + + if (CONTROL_ENDPOINT(Endpoint)) { + TransferCompleteControl(Endpoint); + + } else { + TransferCompleteData(Endpoint); + } + + TraceExit(); +} + +_IRQL_requires_(PASSIVE_LEVEL) +VOID +TransferDestroy ( + _In_ UFXENDPOINT Endpoint + ) + +/*++ +Routine Description: + + Cleans up the endpoint's TRANSFER_CONTEXT during endpoint destroy + +Parameters Description: + + Endpoint - Endpoint being destroyed + +--*/ +{ + PTRANSFER_CONTEXT TransferContext; + PUFXENDPOINT_CONTEXT EpContext; + PUFXDEVICE_CONTEXT DeviceContext; + WDFCOMMONBUFFER ExtraBuffer; + WDFCOMMONBUFFER SetupPacketBuffer; + + TraceEntry(); + + TransferLock(Endpoint); + + TransferContext = UfxEndpointGetTransferContext(Endpoint); + EpContext = UfxEndpointGetContext(Endpoint); + DeviceContext = UfxDeviceGetContext(EpContext->UfxDevice); + + DeviceContext->PhysicalEndpointToUfxEndpoint[EpContext->PhysicalEndpoint] = NULL; + if (CONTROL_ENDPOINT(Endpoint)) { + DeviceContext->PhysicalEndpointToUfxEndpoint[EpContext->PhysicalEndpoint + 1] = NULL; + } + + // + // Need to do this first so another transfer isn't scheduled on cleanup. + // + TransferContext->Enabled = FALSE; + + TransferRequestTryUnmarkCancelableAndCleanup(Endpoint); + + ExtraBuffer = TransferContext->ExtraBuffer; + TransferContext->ExtraBuffer = NULL; + + SetupPacketBuffer = NULL; + if (CONTROL_ENDPOINT(Endpoint)) { + PCONTROL_CONTEXT ControlContext; + + ControlContext = UfxEndpointGetControlContext(Endpoint); + SetupPacketBuffer = ControlContext->SetupPacketBuffer; + ControlContext->SetupPacketBuffer = NULL; + } + + // + // Most of these are probably already reset, but let's + // reset them anyway. + // + TransferContext->TransferStarted = FALSE; + TransferContext->Stalled = FALSE; + TransferContext->EndRequested = FALSE; + TransferContext->TransferCommandStartComplete = FALSE; + TransferContext->CleanupOnEndComplete = FALSE; + + // + // Cannot delete common buffers with spinlock held. + // + TransferUnlock(Endpoint); + + if (ExtraBuffer != NULL) { + WdfObjectDelete(ExtraBuffer); + } + + if (SetupPacketBuffer != NULL) { + WdfObjectDelete(SetupPacketBuffer); + } + + TraceExit(); +} + +_Must_inspect_result_ +_IRQL_requires_max_(DISPATCH_LEVEL) +NTSTATUS +TransferInitialize ( + _In_ UFXENDPOINT Endpoint + ) +/*++ +Routine Description: + + Sets up all fields in the endpoint's TRANSFER_CONTEXT during + endpoint creation. + +Parameters Description: + + Endpoint - The UFXENDPOINT which may have pending transfers. + +Return Value: + + STATUS_SUCCESS if successful, appropriate NTSTATUS message otherwise. + +--*/ +{ + NTSTATUS Status; + PCONTROLLER_CONTEXT ControllerContext; + PUFXENDPOINT_CONTEXT EpContext; + PTRANSFER_CONTEXT TransferContext; + PUFXDEVICE_CONTEXT DeviceContext; + WDF_OBJECT_ATTRIBUTES Attributes; + ULONG PhysicalEndpoint; + WDF_COMMON_BUFFER_CONFIG BufferConfig; + PUCHAR Buffer; + WDF_WORKITEM_CONFIG WorkItemConfig; + + TraceEntry(); + + EpContext = UfxEndpointGetContext(Endpoint); + ControllerContext = DeviceGetControllerContext(EpContext->WdfDevice); + DeviceContext = UfxDeviceGetContext(EpContext->UfxDevice); + + // + // Allocate transfer-related context + // + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&Attributes, TRANSFER_CONTEXT); + WdfObjectAllocateContext(Endpoint, &Attributes, (PVOID*) &TransferContext); + TransferContext->WdfDevice = EpContext->WdfDevice; + + // + // Create transfer spin lock + // + if (TransferContext->TransferLock == NULL) { + WDF_OBJECT_ATTRIBUTES_INIT(&Attributes); + Attributes.ParentObject = Endpoint; + Status = WdfSpinLockCreate(&Attributes, &TransferContext->TransferLock); + CHK_NT_MSG(Status, "Failed to create transfer spinlock"); + } + + // + // Allocate and initialize common buffer for the endpoint + // + if (TransferContext->CommonBuffer == NULL) { + WDF_COMMON_BUFFER_CONFIG_INIT(&BufferConfig, COMMON_BUFFER_ALIGNMENT); + Status = WdfCommonBufferCreateWithConfig( + ControllerContext->DmaEnabler, + ENDPOINT_COMMON_BUFFER_SIZE, + &BufferConfig, + WDF_NO_OBJECT_ATTRIBUTES, + &TransferContext->CommonBuffer); + CHK_NT_MSG(Status, "Failed to create common buffer"); + } + + Buffer = WdfCommonBufferGetAlignedVirtualAddress(TransferContext->CommonBuffer); + TransferContext->LogicalCommonBuffer = + WdfCommonBufferGetAlignedLogicalAddress(TransferContext->CommonBuffer); + TransferContext->Buffer = Buffer; + + RtlZeroMemory(TransferContext->CommonBuffer, ENDPOINT_COMMON_BUFFER_SIZE); + + // + // #### TODO: Insert code to initialize controller data structures in the shared common buffer + // + + // + // Map physical endpoint + // + PhysicalEndpoint = (EpContext->Descriptor.bEndpointAddress & USB_ENDPOINT_ADDRESS_MASK) * 2; + if (DIRECTION_IN(Endpoint) && !CONTROL_ENDPOINT(Endpoint)) { + PhysicalEndpoint++; + } + + if (PhysicalEndpoint >= MAX_PHYSICAL_ENDPOINTS) { + NT_ASSERTMSG("Physical endpoint maxium exceeded", FALSE); + Status = STATUS_UNSUCCESSFUL; + CHK_NT_MSG(Status, "Physical endpoint maxium exceeded"); + } + + DeviceContext->PhysicalEndpointToUfxEndpoint[PhysicalEndpoint] = Endpoint; + EpContext->PhysicalEndpoint = PhysicalEndpoint; + + // + // Control endpoints have two consecutive physical endpoints and + // need a place to store setup packets + // + if (CONTROL_ENDPOINT(Endpoint)) { + PCONTROL_CONTEXT ControlContext; + + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&Attributes, CONTROL_CONTEXT); + WdfObjectAllocateContext(Endpoint, &Attributes, (PVOID*) &ControlContext); + + PhysicalEndpoint++; + + if (PhysicalEndpoint >= MAX_PHYSICAL_ENDPOINTS) { + NT_ASSERTMSG("Physical endpoint maxium exceeded", FALSE); + Status = STATUS_UNSUCCESSFUL; + CHK_NT_MSG(Status, "Physical endpoint maxium exceeded"); + } + + DeviceContext->PhysicalEndpointToUfxEndpoint[PhysicalEndpoint] = Endpoint; + + if (ControlContext->SetupPacketBuffer == NULL) { + WDF_COMMON_BUFFER_CONFIG_INIT(&BufferConfig, DATA_ALIGNMENT); + Status = WdfCommonBufferCreateWithConfig( + ControllerContext->DmaEnabler, + sizeof(USB_DEFAULT_PIPE_SETUP_PACKET), + &BufferConfig, + WDF_NO_OBJECT_ATTRIBUTES, + &ControlContext->SetupPacketBuffer); + CHK_NT_MSG(Status, "Failed to create setup packet buffer"); + } + + Buffer = WdfCommonBufferGetAlignedVirtualAddress(ControlContext->SetupPacketBuffer); + ControlContext->SetupPacket = (PUSB_DEFAULT_PIPE_SETUP_PACKET) Buffer; + } + + // + // Extra buffer for OUT transfers + // + if (TransferContext->ExtraBuffer == NULL) { + WDF_COMMON_BUFFER_CONFIG_INIT(&BufferConfig, DATA_ALIGNMENT); + Status = WdfCommonBufferCreateWithConfig( + ControllerContext->DmaEnabler, + 1024, + &BufferConfig, + WDF_NO_OBJECT_ATTRIBUTES, + &TransferContext->ExtraBuffer); + CHK_NT_MSG(Status, "Failed to create extra OUT buffer"); + } + + if (TransferContext->CompletionWorkItem == NULL) { + WDF_WORKITEM_CONFIG_INIT(&WorkItemConfig, TransferCompleteNext); + Attributes.ParentObject = Endpoint; + Status = WdfWorkItemCreate(&WorkItemConfig, &Attributes, &TransferContext->CompletionWorkItem); + CHK_NT_MSG(Status, "Failed to create completion work item"); + } + + Status = STATUS_SUCCESS; + +End: + TraceExit(); + return Status; +} + +_IRQL_requires_max_(DISPATCH_LEVEL) +WDFDMATRANSACTION +TransferBegin ( + _In_ UFXENDPOINT Endpoint, + _In_ WDFREQUEST Request, + _In_ BOOLEAN DirectionIn, + _In_ BOOLEAN AppendZlp + ) +/*++ +Routine Description: + + Starts a DMA transfer on the endpoint. + +Parameters Description: + + Endpoint - The endpoint to transfer on. + + Request - The WDFREQUEST which requested this transfer. + + DirectionIn - Transfer to host. + + AppendZlp - Append zero-length packet at the end of the transfer. + Only valid for DirectionIn. + +--*/ +{ + NTSTATUS Status; + PCONTROLLER_CONTEXT ControllerContext; + PUFXENDPOINT_CONTEXT EpContext; + PTRANSFER_CONTEXT TransferContext; + WDF_OBJECT_ATTRIBUTES Attributes; + PDMA_CONTEXT DmaContext; + PREQUEST_CONTEXT RequestContext; + WDFDMATRANSACTION Transaction; + WDF_REQUEST_PARAMETERS Params; + + TraceEntry(); + + EpContext = UfxEndpointGetContext(Endpoint); + TransferContext = UfxEndpointGetTransferContext(Endpoint); + ControllerContext = DeviceGetControllerContext(EpContext->WdfDevice); + + NT_ASSERT(DirectionIn || !AppendZlp); + + TransferContext->PendingCompletion = FALSE; + + // + // Create a DMA Transaction over the data + // + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&Attributes, DMA_CONTEXT); + Status = WdfDmaTransactionCreate(ControllerContext->DmaEnabler, + &Attributes, + &Transaction); + CHK_NT_MSG(Status, "Failed to create DMA Transaction"); + + // + // Set up DMA context + // + WDF_REQUEST_PARAMETERS_INIT(&Params); + WdfRequestGetParameters(Request, &Params); + DmaContext = DmaGetContext(Transaction); + DmaContext->NeedExtraBuffer = AppendZlp; + DmaContext->DirectionIn = DirectionIn; + DmaContext->Endpoint = Endpoint; + DmaContext->Request = Request; + DmaContext->BytesRequested = (ULONG) Params.Parameters.DeviceIoControl.OutputBufferLength; + + // + // Need to add extra buffer if OUT transfer not multiple of MaxPacketSize + // + if (!DirectionIn) { + if ((DmaContext->BytesRequested % EpContext->Descriptor.wMaxPacketSize) > 0) { + DmaContext->ExtraBytes = EpContext->Descriptor.wMaxPacketSize - + (DmaContext->BytesRequested % EpContext->Descriptor.wMaxPacketSize); + DmaContext->NeedExtraBuffer = TRUE; + } + } + + DmaContext->State = NotExecuted; + + // + // Set up request context + // + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&Attributes, REQUEST_CONTEXT); + Status = WdfObjectAllocateContext(Request, &Attributes, (PVOID*) &RequestContext); + CHK_NT_MSG(Status, "Failed to allocate request context"); + + // + // It's possible that the request was reused, in which case the context already + // exists. Therefore, we need to make sure to zero it out. + // + RtlZeroMemory(RequestContext, sizeof(REQUEST_CONTEXT)); + + RequestContext->Endpoint = Endpoint; + RequestContext->Transaction = Transaction; + + // + // Set up control data stage context + // + if (CONTROL_ENDPOINT(Endpoint)) { + PCONTROL_CONTEXT ControlContext; + ControlContext = UfxEndpointGetControlContext(Endpoint); + ControlContext->DataStageExists = TRUE; + } + + // + // Keep track of DMA + // + TransferContext->Transaction = Transaction; + + // + // We need to mark cancelable before starting DMA (which may take time) + // + Status = WdfRequestMarkCancelableEx(Request, OnEvtRequestCancel); + CHK_NT_MSG(Status, "Failed to mark request cancellable"); + + TRACE_TRANSFER("BEGIN", Endpoint, Request); + + // + // Can't use a DMA transaction for 0-length transfers. + // + if (DmaContext->BytesRequested == 0) { + + DmaContext->ZeroLength = TRUE; + + TRACE_TRANSFER("ZERO LENGTH", Endpoint, DmaContext->Request); + + DmaContext->ExtraBytes = 0; + + TransferCommandStartOrUpdate(Endpoint); + + } else { + // + // Use request to initialize DMA + // + Status = WdfDmaTransactionInitializeUsingRequest( + Transaction, + Request, + OnEvtProgramDma, + DirectionIn ? WdfDmaDirectionWriteToDevice : WdfDmaDirectionReadFromDevice); + CHK_NT_MSG(Status, "Failed to initialize DMA transaction using request"); + + // + // Caller is expected to execute the DMA. + // + } + +End: + if (!NT_SUCCESS(Status)) { + WdfRequestComplete(Request, Status); + if (Transaction != NULL) { + TransferContext->Transaction = NULL; + WdfObjectDelete(Transaction); + Transaction = NULL; + } + } + + TraceExit(); + return Transaction; +} + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +TransferHandshake ( + _In_ UFXENDPOINT Endpoint, + _In_ WDFREQUEST Request, + _In_ BOOLEAN DirectionIn + ) +/*++ +Routine Description: + + Starts a control handshake (zero-length packet) transfer on the endpoint. + The caller is expected to acquire the TransferLock. + +Parameters Description: + + Endpoint - The endpoint to transfer on. + + Request - The WDFREQUEST which requested this transfer. + + DirectionIn - Transfer to host. + +--*/ +{ + NTSTATUS Status; + PCONTROL_CONTEXT ControlContext; + PREQUEST_CONTEXT RequestContext; + WDF_OBJECT_ATTRIBUTES Attributes; + + TraceEntry(); + + UNREFERENCED_PARAMETER(DirectionIn); + + ControlContext = UfxEndpointGetControlContext(Endpoint); + + NT_ASSERT(CONTROL_ENDPOINT(Endpoint)); + NT_ASSERT(ControlContext->SetupRequested == FALSE); + NT_ASSERT(ControlContext->HandshakeRequested == FALSE); + NT_ASSERT(ControlContext->HandshakeRequest == Request || + ControlContext->HandshakeRequest == NULL); + + Status = STATUS_SUCCESS; + + if (ControlContext->HandshakeRequest != Request) { + // + // Set up request context + // + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&Attributes, REQUEST_CONTEXT); + Status = WdfObjectAllocateContext(Request, &Attributes, (PVOID*) &RequestContext); + CHK_NT_MSG(Status, "Failed to allocate request context"); + + // + // It's possible that the request was reused, in which case the context already + // exists. Therefore, we need to make sure to zero it out. + // + RtlZeroMemory(RequestContext, sizeof(REQUEST_CONTEXT)); + + RequestContext->Endpoint = Endpoint; + + // + // Mark request cancellable, in case host takes a long time. + // + Status = WdfRequestMarkCancelableEx(Request, OnEvtRequestCancel); + CHK_NT_MSG(Status, "Failed to mark quest cancelable"); + } + + ControlContext->HandshakeRequest = Request; + + // + // #### TODO: Insert code to initialize endpoint data structures for handshake #### + // + + TRACE_TRANSFER("HANDSHAKE", Endpoint, Request); + + // + // We should always start a new transfer for this, since it must be in + // the opposite direction of the DMA or Setup Packet. + // + TransferCommandStart(Endpoint); + ControlContext->HandshakeRequested = TRUE; + +End: + if (!NT_SUCCESS(Status)) { + WdfRequestComplete(Request, Status); + } + TraceExit(); +} + + +_IRQL_requires_max_(DISPATCH_LEVEL) +WDFDMATRANSACTION +TransferNextRequest ( + _In_ UFXENDPOINT Endpoint + ) +/*++ +Routine Description: + + Handles IOCTL requests on the endpoint. + +Parameters Description: + + Endpoint - Endpoint with request. + +--*/ +{ + NTSTATUS Status; + WDFREQUEST Request; + WDF_REQUEST_PARAMETERS Params; + ULONG Ioctl; + PUFXENDPOINT_CONTEXT EpContext; + WDFDMATRANSACTION Transaction; + + TraceEntry(); + + EpContext = UfxEndpointGetContext(Endpoint); + Transaction = NULL; + +Fetch: + + // + // Fetch next request + // + Status = WdfIoQueueRetrieveNextRequest(UfxEndpointGetTransferQueue(Endpoint), + &Request); + if (Status == STATUS_NO_MORE_ENTRIES || Status == STATUS_WDF_PAUSED) { + goto End; + } + + CHK_NT_MSG(Status, "Failed to retrieve next request"); + + WDF_REQUEST_PARAMETERS_INIT(&Params); + WdfRequestGetParameters(Request, &Params); + Ioctl = Params.Parameters.DeviceIoControl.IoControlCode; + + if (Ioctl == IOCTL_INTERNAL_USBFN_CONTROL_STATUS_HANDSHAKE_IN) { + TransferHandshake(Endpoint, Request, TRUE); + + } else if (Ioctl == IOCTL_INTERNAL_USBFN_CONTROL_STATUS_HANDSHAKE_OUT) { + TransferHandshake(Endpoint, Request, FALSE); + + } else if (DIRECTION_IN(Endpoint) && + Ioctl == IOCTL_INTERNAL_USBFN_TRANSFER_IN) { + + Transaction = TransferBegin(Endpoint, Request, TRUE, FALSE); + + } else if (DIRECTION_IN(Endpoint) && + Ioctl == IOCTL_INTERNAL_USBFN_TRANSFER_IN_APPEND_ZERO_PKT) { + + Transaction = TransferBegin(Endpoint, Request, TRUE, TRUE); + + } else if (DIRECTION_OUT(Endpoint) && + Ioctl == IOCTL_INTERNAL_USBFN_TRANSFER_OUT) { + + Transaction = TransferBegin(Endpoint, Request, FALSE, FALSE); + + } else { + TraceWarning("INVALID: %08X (%d), Ioctl: %08X", + (ULONG) Endpoint, EpContext->PhysicalEndpoint, Ioctl); + WdfRequestComplete(Request, STATUS_INVALID_DEVICE_REQUEST); + goto Fetch; + } + +End: + TraceExit(); + + return Transaction; +} + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +TransferReset ( + _In_ UFXENDPOINT Endpoint + ) +/*++ +Routine Description: + + Disables and ends an endpoint. On end complete, if the endpoint + is stalled, it will be cleared. After end complete, the endpoint + can be re-configured. + +Parameters Description: + + Endpoint - endpoint to reset + +--*/ +{ + PUFXENDPOINT_CONTEXT EpContext; + PTRANSFER_CONTEXT TransferContext; + + TraceEntry(); + + EpContext = UfxEndpointGetContext(Endpoint); + TransferContext = UfxEndpointGetTransferContext(Endpoint); + + TransferLock(Endpoint); + + TRACE_TRANSFER("RESET", Endpoint, NULL); + + // + // Need to get EP0 back to "setup" stage. + // + if (EpContext->PhysicalEndpoint <= 1) { + PCONTROL_CONTEXT ControlContext; + + ControlContext = UfxEndpointGetControlContext(Endpoint); + + if (!ControlContext->SetupRequested) { + if (TransferContext->TransferStarted) { + TransferCommandEnd(Endpoint); + } else { + CommandStallSet(Endpoint); + } + } + + } else { + TransferContext->Enabled = FALSE; + if (TransferContext->TransferStarted) { + TransferCommandEnd(Endpoint); + } + } + + TransferUnlock(Endpoint); + TraceExit(); +} + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +TransferStart ( + _In_ UFXENDPOINT Endpoint + ) +/*++ +Routine Description: + + Enables an endpoint to start transfers and starts fetching + requests. If this is a control endpoint, programs a setup + packet. + +Parameters Description: + + Endpoint - endpoint to start + +--*/ +{ + PTRANSFER_CONTEXT TransferContext; + WDFDMATRANSACTION Transaction; + + TraceEntry(); + + TransferContext = UfxEndpointGetTransferContext(Endpoint); + Transaction = NULL; + + TransferLock(Endpoint); + + TRACE_TRANSFER("START", Endpoint, NULL); + + TransferContext->Enabled = TRUE; + + // + // EP0 is programmed once on creation, not on each reset. + // + if (CONTROL_ENDPOINT(Endpoint)) { + TransferSetupPacket(Endpoint); + + } else { + Transaction = TransferNextRequest(Endpoint); + } + + TransferUnlock(Endpoint); + TransferDmaExecute(Transaction); + TraceExit(); +} + +VOID +TransferReadyNotify ( + WDFQUEUE Queue, + WDFCONTEXT Context + ) +/*++ +Routine Description: + + Indicates the number of events in the queue went from 0 -> 1. + This will fetch the next request and program it. + +Parameters Description: + + Queue - queue with request. + + Context - UFXENDPOINT + +--*/ +{ + UFXENDPOINT Endpoint; + PTRANSFER_CONTEXT TransferContext; + WDFDMATRANSACTION Transaction; + + UNREFERENCED_PARAMETER(Queue); + + TraceEntry(); + + Endpoint = (UFXENDPOINT) Context; + TransferContext = UfxEndpointGetTransferContext(Endpoint); + Transaction = NULL; + + // + // Make sure we didn't get hijacked from WdfRequestComplete... + // + if (TransferContext->TransferStarted) { + goto Skip; + } + + TransferLock(Endpoint); + + if (TransferContext->Enabled && TransferContext->Transaction == NULL) { + if (CONTROL_ENDPOINT(Endpoint)) { + PCONTROL_CONTEXT ControlContext; + ControlContext = UfxEndpointGetControlContext(Endpoint); + + if (!ControlContext->SetupRequested) { + Transaction = TransferNextRequest(Endpoint); + } + + } else { + Transaction = TransferNextRequest(Endpoint); + } + } + + TransferUnlock(Endpoint); + TransferDmaExecute(Transaction); +Skip: + TraceExit(); +} + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +TransferStallSetComplete ( + _In_ UFXENDPOINT Endpoint + ) +/*++ +Routine Description: + + Handles command completion event for Stall Set. If this is a + control endpoint, programs a setup packet. + +Parameters Description: + + Endpoint - Endpoint associated with event. + +--*/ +{ + PUFXENDPOINT_CONTEXT EpContext; + PTRANSFER_CONTEXT TransferContext; + WDFREQUEST Request; + WDFDMATRANSACTION NewTransaction; + + TraceEntry(); + + EpContext = UfxEndpointGetContext(Endpoint); + TransferContext = UfxEndpointGetTransferContext(Endpoint); + NewTransaction = NULL; + + TransferLock(Endpoint); + + // + // If we stall a control endpoint, we need to reset its state and start + // over from the setup packet request. + // + if (CONTROL_ENDPOINT(Endpoint)) { + TransferContext->Stalled = FALSE; + if (!TransferContext->TransferStarted) { + NewTransaction = TransferRequestTryUnmarkCancelableAndCleanup(Endpoint); + } + } + + if (EpContext->StallRequest) { + Request = EpContext->StallRequest; + EpContext->StallRequest = NULL; + WdfRequestComplete(Request, STATUS_SUCCESS); + } + + TransferUnlock(Endpoint); + TransferDmaExecute(NewTransaction); + TraceExit(); +} + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +TransferStallClearComplete ( + _In_ UFXENDPOINT Endpoint + ) +/*++ +Routine Description: + + Handles command completion event for Stall Clear. + +Parameters Description: + + Endpoint - Endpoint associated with event. + +--*/ +{ + PUFXENDPOINT_CONTEXT EpContext; + WDFREQUEST Request; + + TraceEntry(); + + EpContext = UfxEndpointGetContext(Endpoint); + + TransferLock(Endpoint); + + NT_ASSERT(!CONTROL_ENDPOINT(Endpoint)); + + if (EpContext->ClearRequest) { + Request = EpContext->ClearRequest; + EpContext->ClearRequest = NULL; + WdfRequestComplete(Request, STATUS_SUCCESS); + } + + TransferUnlock(Endpoint); + + TraceExit(); +} + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +TransferCommandStallSet ( + _In_ UFXENDPOINT Endpoint + ) +/*++ +Routine Description: + + Sets an endpoint to the STALL state. + +Parameters Description: + + Endpoint - Endpoint to set the STALL state on. + +--*/ +{ + TraceEntry(); + + TransferLock(Endpoint); + + CommandStallSet(Endpoint); + + TransferUnlock(Endpoint); + + TraceExit(); +} + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +TransferCommandStallClear ( + _In_ UFXENDPOINT Endpoint + ) +/*++ +Routine Description: + + Clears an endpoint to the STALL state. + +Parameters Description: + + Endpoint - Endpoint to clear the STALL state on. + +--*/ +{ + TraceEntry(); + + TransferLock(Endpoint); + + CommandStallClear(Endpoint); + + TransferUnlock(Endpoint); + + TraceExit(); +} + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +TransferGetStall ( + _In_ UFXENDPOINT Endpoint, + _Out_ PBOOLEAN Stalled + ) +/*++ +Routine Description: + + Returns the current STALL state for an endpoint. + +Parameters Description: + + Endpoint - Endpoint to retreive the STALL state for. + + Stalled - Pointer to a boolean value to receive the STALL state. + +--*/ +{ + PTRANSFER_CONTEXT TransferContext; + + TraceEntry(); + + TransferContext = UfxEndpointGetTransferContext(Endpoint); + + TransferLock(Endpoint); + + *Stalled = TransferContext->Stalled; + + TransferUnlock(Endpoint); + + TraceExit(); +} \ No newline at end of file diff --git a/usb/ufxclientsample/transfer.h b/usb/ufxclientsample/transfer.h new file mode 100644 index 000000000..42ef209f8 --- /dev/null +++ b/usb/ufxclientsample/transfer.h @@ -0,0 +1,180 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + transfer.h + +Abstract: + + Header file for the transfer related functions + +Environment: + + Kernel mode + +--*/ + +#pragma once + +#include "ufxclient.h" +#include "registers.h" +#include "event.h" + +#define MAX_TRANSFER_SIZE 0x20000000 + +// nonstandard extension used : bit field types other than int +#pragma warning(disable:4214) + +typedef enum _DMA_STATE { + NotExecuted = 0, + Executed, + Cancelled, + Completed +} DMA_STATE; + +typedef struct _CONTROL_CONTEXT { + WDFCOMMONBUFFER SetupPacketBuffer; + BOOLEAN SetupRequested; + BOOLEAN ReadyForHandshake; + BOOLEAN HandshakeRequested; + BOOLEAN DataStageExists; + PUSB_DEFAULT_PIPE_SETUP_PACKET SetupPacket; + WDFREQUEST HandshakeRequest; +} CONTROL_CONTEXT, *PCONTROL_CONTEXT; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(CONTROL_CONTEXT, UfxEndpointGetControlContext); + +typedef struct _TRANSFER_CONTEXT { + WDFDEVICE WdfDevice; + WDFCOMMONBUFFER CommonBuffer; + WDFCOMMONBUFFER ExtraBuffer; + WDFDMATRANSACTION Transaction; + PHYSICAL_ADDRESS LogicalCommonBuffer; + PUCHAR Buffer; + WDFSPINLOCK TransferLock; + BOOLEAN TransferStarted; + BOOLEAN EndRequested; + BOOLEAN Stalled; + BOOLEAN Enabled; + BOOLEAN TransferCommandStartComplete; + BOOLEAN CleanupOnEndComplete; + WDFWORKITEM CompletionWorkItem; + BOOLEAN PendingCompletion; +} TRANSFER_CONTEXT, *PTRANSFER_CONTEXT; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(TRANSFER_CONTEXT, UfxEndpointGetTransferContext); + +typedef struct _DMA_CONTEXT { + UFXENDPOINT Endpoint; + ULONG BytesRequested; + ULONG BytesProgrammed; + ULONG BytesProgrammedCurrent; + ULONG ExtraBytes; + BOOLEAN DirectionIn; + PSCATTER_GATHER_LIST SgList; + ULONG SgIndex; + WDFREQUEST Request; + BOOLEAN ZeroLength; + DMA_STATE State; + ULONG BytesRemaining; + BOOLEAN CleanupOnProgramDma; + BOOLEAN NeedExtraBuffer; +} DMA_CONTEXT, *PDMA_CONTEXT; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(DMA_CONTEXT, DmaGetContext); + +typedef struct _REQUEST_CONTEXT { + UFXENDPOINT Endpoint; + WDFDMATRANSACTION Transaction; + BOOLEAN QueueIsStopped; + BOOLEAN ReferencedOnCancel; +} REQUEST_CONTEXT, *PREQUEST_CONTEXT; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(REQUEST_CONTEXT, RequestGetContext); + +_Must_inspect_result_ +_IRQL_requires_max_(DISPATCH_LEVEL) +NTSTATUS +TransferInitialize ( + _In_ UFXENDPOINT Endpoint + ); + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +TransferCommandStartComplete ( + _In_ UFXENDPOINT Endpoint + ); + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +TransferCommandEndComplete ( + _In_ UFXENDPOINT Endpoint + ); + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID + +TransferComplete ( + _In_ UFXENDPOINT Endpoint + ); + + +_IRQL_requires_(PASSIVE_LEVEL) +VOID +TransferDestroy ( + _In_ UFXENDPOINT Endpoint + ); + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +TransferReset ( + _In_ UFXENDPOINT Endpoint + ); + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +TransferStart ( + _In_ UFXENDPOINT Endpoint + ); + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +TransferRequestCancel ( + _In_ WDFREQUEST Request, + _In_ BOOLEAN QueueIsStopped + ); + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +TransferCommandStallClear ( + _In_ UFXENDPOINT Endpoint + ); + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +TransferCommandStallSet ( + _In_ UFXENDPOINT Endpoint + ); + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +TransferStallClearComplete ( + _In_ UFXENDPOINT Endpoint + ); + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +TransferStallSetComplete ( + _In_ UFXENDPOINT Endpoint + ); + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +TransferGetStall ( + _In_ UFXENDPOINT Endpoint, + _Out_ PBOOLEAN Stalled + ); + +EVT_WDF_IO_QUEUE_STATE TransferReadyNotify; \ No newline at end of file diff --git a/usb/ufxclientsample/ufxclientsample.rc b/usb/ufxclientsample/ufxclientsample.rc new file mode 100644 index 000000000..1b249a209 --- /dev/null +++ b/usb/ufxclientsample/ufxclientsample.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 "UFX Client Sample Driver" +#define VER_INTERNALNAME_STR "ufxclientsample.sys" +#define VER_ORIGINALFILENAME_STR "ufxclientsample.sys" + +#include "common.ver" diff --git a/usb/ufxclientsample/ufxclientsample.sln b/usb/ufxclientsample/ufxclientsample.sln new file mode 100644 index 000000000..63b6eb8db --- /dev/null +++ b/usb/ufxclientsample/ufxclientsample.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}") = "ufxclientsample", "ufxclientsample.vcxproj", "{53F86825-E305-471C-8180-34E9987E4431}" +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 + {53F86825-E305-471C-8180-34E9987E4431}.Debug|Win32.ActiveCfg = Debug|Win32 + {53F86825-E305-471C-8180-34E9987E4431}.Debug|Win32.Build.0 = Debug|Win32 + {53F86825-E305-471C-8180-34E9987E4431}.Release|Win32.ActiveCfg = Release|Win32 + {53F86825-E305-471C-8180-34E9987E4431}.Release|Win32.Build.0 = Release|Win32 + {53F86825-E305-471C-8180-34E9987E4431}.Debug|x64.ActiveCfg = Debug|x64 + {53F86825-E305-471C-8180-34E9987E4431}.Debug|x64.Build.0 = Debug|x64 + {53F86825-E305-471C-8180-34E9987E4431}.Release|x64.ActiveCfg = Release|x64 + {53F86825-E305-471C-8180-34E9987E4431}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/usb/ufxclientsample/ufxclientsample.vcxproj b/usb/ufxclientsample/ufxclientsample.vcxproj new file mode 100644 index 000000000..df653028e --- /dev/null +++ b/usb/ufxclientsample/ufxclientsample.vcxproj @@ -0,0 +1,219 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {53F86825-E305-471C-8180-34E9987E4431} + $(MSBuildProjectName) + 1 + Debug + Win32 + {0A165ABC-5762-4BCF-AE58-360CF9D52BD8} + + + + 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 + ufxclientsample + trace.h + + + $(InfArch) + true + .\$(IntDir)\ufxclientsample.inf + + + true + true + .\$(IntDir) + true + .\$(IntDir) + true + ufxclientsample + true + + + true + true + ufxclientsample + trace.h + + + + ufxclientsample + + + ufxclientsample + + + ufxclientsample + + + ufxclientsample + + + + true + Level4 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH)\ufx\1.1 + %(PreprocessorDefinitions);KERNEL_MODE + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ufx\1.1\ufxstub.lib;$(DDK_LIB_PATH)\ntstrsafe.lib + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH)\ufx\1.1 + %(PreprocessorDefinitions);KERNEL_MODE + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH)\ufx\1.1 + %(PreprocessorDefinitions);KERNEL_MODE + + + + + true + Level4 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH)\ufx\1.1 + %(PreprocessorDefinitions);KERNEL_MODE + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ufx\1.1\ufxstub.lib;$(DDK_LIB_PATH)\ntstrsafe.lib + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH)\ufx\1.1 + %(PreprocessorDefinitions);KERNEL_MODE + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH)\ufx\1.1 + %(PreprocessorDefinitions);KERNEL_MODE + + + + + true + Level4 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH)\ufx\1.1 + %(PreprocessorDefinitions);KERNEL_MODE + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ufx\1.1\ufxstub.lib;$(DDK_LIB_PATH)\ntstrsafe.lib + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH)\ufx\1.1 + %(PreprocessorDefinitions);KERNEL_MODE + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH)\ufx\1.1 + %(PreprocessorDefinitions);KERNEL_MODE + + + + + true + Level4 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH)\ufx\1.1 + %(PreprocessorDefinitions);KERNEL_MODE + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ufx\1.1\ufxstub.lib;$(DDK_LIB_PATH)\ntstrsafe.lib + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH)\ufx\1.1 + %(PreprocessorDefinitions);KERNEL_MODE + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH)\ufx\1.1 + %(PreprocessorDefinitions);KERNEL_MODE + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/usb/ufxclientsample/ufxclientsample.vcxproj.Filters b/usb/ufxclientsample/ufxclientsample.vcxproj.Filters new file mode 100644 index 000000000..ba38b7e19 --- /dev/null +++ b/usb/ufxclientsample/ufxclientsample.vcxproj.Filters @@ -0,0 +1,63 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {913D3EC7-37FD-4BC5-B8F3-5B0316F839BD} + + + h;hpp;hxx;hm;inl;inc;xsd + {CFA48238-7E09-4139-BABE-B24CA80F2A32} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {088F289C-03FC-4BBE-B6D4-FEBA220934FD} + + + inf;inv;inx;mof;mc; + {B057205B-0933-44FF-A94F-65B1491C15CE} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Driver Files + + + + + Resource Files + + + Resource Files + + + \ No newline at end of file diff --git a/usb/ufxclientsample/ufxdevice.c b/usb/ufxclientsample/ufxdevice.c new file mode 100644 index 000000000..0894ff69d --- /dev/null +++ b/usb/ufxclientsample/ufxdevice.c @@ -0,0 +1,902 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + ufxdevice.c + +Abstract: + + Implements the UFXDEVICE functionality and callbacks. + +Environment: + + Kernel mode + +--*/ + + +#include "device.h" +#include "ufxdevice.h" +#include "ufxendpoint.h" +#include "transfer.h" +#include "interrupt.h" +#include + +#include "ufxdevice.tmh" + +#define DISCONNECT_TIMEOUT WDF_REL_TIMEOUT_IN_MS(2000) + +// +// UFXDEVICE callbacks +// +EVT_UFX_DEVICE_HOST_CONNECT UfxDevice_EvtDeviceHostConnect; +EVT_UFX_DEVICE_HOST_DISCONNECT UfxDevice_EvtDeviceHostDisconnect; +EVT_UFX_DEVICE_ADDRESSED UfxDevice_EvtDeviceAddressed; +EVT_UFX_DEVICE_ENDPOINT_ADD UfxDevice_EvtDeviceEndpointAdd; +EVT_UFX_DEVICE_DEFAULT_ENDPOINT_ADD UfxDevice_EvtDeviceDefaultEndpointAdd; +EVT_UFX_DEVICE_USB_STATE_CHANGE UfxDevice_EvtDeviceUsbStateChange; +EVT_UFX_DEVICE_PORT_CHANGE UfxDevice_EvtDevicePortChange; +EVT_UFX_DEVICE_PORT_DETECT UfxDevice_EvtDevicePortDetect; +EVT_UFX_DEVICE_REMOTE_WAKEUP_SIGNAL UfxDevice_EvtDeviceRemoteWakeupSignal; +EVT_UFX_DEVICE_TEST_MODE_SET UfxDevice_EvtDeviceTestModeSet; +EVT_UFX_DEVICE_SUPER_SPEED_POWER_FEATURE UfxDevice_EvtDeviceSuperSpeedPowerFeature; + +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +UfxDeviceStopOrResumeIdle ( + _In_ UFXDEVICE Device, + _In_ USBFN_DEVICE_STATE UsbState, + _In_ USBFN_PORT_TYPE UsbPort + ); + +// +// WDFOBJECT callbacks +// +EVT_WDF_OBJECT_CONTEXT_CLEANUP UfxDevice_EvtCleanupCallback; + +#ifdef ALLOC_PRAGMA +#pragma alloc_text (PAGE, UfxDevice_DeviceCreate) +#pragma alloc_text (PAGE, UfxDevice_EvtDeviceUsbStateChange) +#pragma alloc_text (PAGE, UfxDevice_EvtDevicePortChange) +#pragma alloc_text (PAGE, UfxDeviceStopOrResumeIdle) +#pragma alloc_text (PAGE, UfxDevice_EvtDeviceDefaultEndpointAdd) +#pragma alloc_text (PAGE, UfxDevice_EvtDeviceRemoteWakeupSignal) +#endif + +_Must_inspect_result_ +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +UfxDevice_DeviceCreate ( + _In_ WDFDEVICE WdfDevice + ) +/*++ + +Routine Description: + + Routine that registers with UFX and creates the UFX device object. + +Arguments: + + WdfDevice - Wdf object representing the device. + +Return Value: + + NTSTATUS. + +--*/ +{ + NTSTATUS Status; + PCONTROLLER_CONTEXT ControllerContext; + UFX_DEVICE_CALLBACKS UfxDeviceCallbacks; + UFX_DEVICE_CAPABILITIES UfxDeviceCapabilities; + PUFXDEVICE_CONTEXT UfxDeviceContext; + UFXDEVICE UfxDevice; + WDF_OBJECT_ATTRIBUTES Attributes; + + PAGED_CODE(); + + TraceEntry(); + + ControllerContext = DeviceGetControllerContext(WdfDevice); + + UFX_DEVICE_CAPABILITIES_INIT(&UfxDeviceCapabilities); + UfxDeviceCapabilities.MaxSpeed = UsbSuperSpeed; + UfxDeviceCapabilities.RemoteWakeSignalDelay = REMOTE_WAKEUP_TIMEOUT_INTERVAL_MS; + + // + // Set bitmasks that define the IN and OUT endpoint numbers that are available on the controller + // + + // + // #### TODO: Set the IN endpoint mask here if not all endpoint addresses are supported #### + // + // For illustration purposes sample will set default control endpoint 0, 1-4, 8 + UfxDeviceCapabilities.InEndpointBitmap = 0x011F; + + // + // #### TODO: Set the OUT endpoint mask here if not all endpoint addresses are supported #### + // + // For illustration purposes sample will set default control endpoint 0, 2-7 + // + UfxDeviceCapabilities.OutEndpointBitmap = 0x00FD; + + // + // Set the event callbacks for the ufxdevice + // + UFX_DEVICE_CALLBACKS_INIT(&UfxDeviceCallbacks); + UfxDeviceCallbacks.EvtDeviceHostConnect = UfxDevice_EvtDeviceHostConnect; + UfxDeviceCallbacks.EvtDeviceHostDisconnect = UfxDevice_EvtDeviceHostDisconnect; + UfxDeviceCallbacks.EvtDeviceAddressed = UfxDevice_EvtDeviceAddressed; + UfxDeviceCallbacks.EvtDeviceEndpointAdd = UfxDevice_EvtDeviceEndpointAdd; + UfxDeviceCallbacks.EvtDeviceDefaultEndpointAdd = UfxDevice_EvtDeviceDefaultEndpointAdd; + UfxDeviceCallbacks.EvtDeviceUsbStateChange = UfxDevice_EvtDeviceUsbStateChange; + UfxDeviceCallbacks.EvtDevicePortChange = UfxDevice_EvtDevicePortChange; + UfxDeviceCallbacks.EvtDevicePortDetect = UfxDevice_EvtDevicePortDetect; + UfxDeviceCallbacks.EvtDeviceRemoteWakeupSignal = UfxDevice_EvtDeviceRemoteWakeupSignal; + UfxDeviceCallbacks.EvtDeviceTestModeSet = UfxDevice_EvtDeviceTestModeSet; + UfxDeviceCallbacks.EvtDeviceSuperSpeedPowerFeature = UfxDevice_EvtDeviceSuperSpeedPowerFeature; + + // Context associated with UFXDEVICE object + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&Attributes, UFXDEVICE_CONTEXT); + Attributes.EvtCleanupCallback = UfxDevice_EvtCleanupCallback; + + // Create the UFXDEVICE object + Status = UfxDeviceCreate(WdfDevice, + &UfxDeviceCallbacks, + &UfxDeviceCapabilities, + &Attributes, + &UfxDevice); + CHK_NT_MSG(Status, "Failed to create UFXDEVICE object"); + + // Initialize the client context space in UFXDEVICE object + UfxDeviceContext = UfxDeviceGetContext(UfxDevice); + UfxDeviceContext->FdoWdfDevice = WdfDevice; + UfxDeviceContext->UsbState = UsbfnDeviceStateDetached; + UfxDeviceContext->UsbPort = UsbfnUnknownPort; + UfxDeviceContext->IsIdle = TRUE; + + ControllerContext->UfxDevice = UfxDevice; + + // + // Create endpoints collection + // + WDF_OBJECT_ATTRIBUTES_INIT(&Attributes); + Attributes.ParentObject = UfxDevice; + Status = WdfCollectionCreate(&Attributes, &UfxDeviceContext->Endpoints); + CHK_NT_MSG(Status, "Failed to create endpoint collection object"); + +End: + TraceExit(); + return Status; +} + + +VOID +UfxDevice_EvtCleanupCallback ( + _In_ WDFOBJECT UfxDevice + ) +/*++ + +Routine Description: + + EvtCleanupCallback handler for the UFXDEVICE object. + +Arguments: + + UfxDevice - Wdf object representing the UFXDEVICE + +--*/ +{ + PUFXDEVICE_CONTEXT DeviceContext = NULL; + PCONTROLLER_CONTEXT ControllerContext = NULL; + + TraceEntry(); + + DeviceContext = UfxDeviceGetContext(UfxDevice); + ControllerContext = DeviceGetControllerContext(DeviceContext->FdoWdfDevice); + + TraceExit(); +} + + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +UfxDeviceSetRunStop ( + _In_ UFXDEVICE UfxDevice, + _In_ BOOLEAN Set + ) +/*++ + +Routine Description: + + Function that sets or clears the run/stop state on the controller. Setting the + run state will initiate a connect to the host. + +Arguments: + + UfxDevice - Wdf object representing the UFXDEVICE + + Set - TRUE if setting the run state, FALSE if clearing the run state. + +--*/ +{ + PCONTROLLER_CONTEXT ControllerContext; + PUFXDEVICE_CONTEXT DeviceContext; + BOOLEAN EventComplete; + + + TraceEntry(); + + DeviceContext = UfxDeviceGetContext(UfxDevice); + ControllerContext = DeviceGetControllerContext(DeviceContext->FdoWdfDevice); + + EventComplete = TRUE; + + WdfSpinLockAcquire(ControllerContext->DpcLock); + + if (Set) { + // + // #### TODO: Insert code to set the run state on the controller #### + // + } else { + // + // #### TODO: Insert code to clear the run state on the controller #### + // + } + + WdfSpinLockRelease(ControllerContext->DpcLock); + + if (EventComplete) { + UfxDeviceEventComplete(UfxDevice, STATUS_SUCCESS); + } + + TraceExit(); +} + +VOID +UfxDevice_EvtDeviceHostConnect ( + _In_ UFXDEVICE UfxDevice + ) +/*++ + +Routine Description: + + EvtDeviceHostConnect callback handler for UFXDEVICE object. + +Arguments: + + UfxDevice - UFXDEVICE object representing the device. + +--*/ +{ + TraceEntry(); + + UfxDeviceSetRunStop(UfxDevice, TRUE); + + TraceExit(); +} + + +VOID +UfxDevice_EvtDeviceHostDisconnect ( + _In_ UFXDEVICE UfxDevice + ) +/*++ + +Routine Description: + + EvtDeviceHostDisconnect callback handler for UFXDEVICE object. + +Arguments: + + UfxDevice - UFXDEVICE object representing the device. + +--*/ +{ + TraceEntry(); + + UfxDevice_Reset(UfxDevice); + + UfxDeviceSetRunStop(UfxDevice, FALSE); + + TraceExit(); +} + + +VOID +UfxDevice_EvtDeviceAddressed ( + _In_ UFXDEVICE UfxDevice, + _In_ USHORT DeviceAddress + ) +/*++ + +Routine Description: + + EvtDeviceAddressed handler for the UFXDEVICE object. + Sets the Address indicated by 'DeviceAddress' on the controller. + +Arguments: + + UfxDevice - UFXDEVICE object representing the device. + + DeviceAddress - USB Device Address, as determined by the UFX. + +--*/ +{ + UNREFERENCED_PARAMETER(DeviceAddress); + + TraceEntry(); + + // + // Set the device address on the controller + // + + // + // #### Insert code to set the device address on controller #### + // + + UfxDeviceEventComplete(UfxDevice, STATUS_SUCCESS); + + TraceExit(); +} + + +NTSTATUS +UfxDevice_EvtDeviceEndpointAdd ( + _In_ UFXDEVICE UfxDevice, + _In_ const PUSB_ENDPOINT_DESCRIPTOR EndpointDescriptor, + _Inout_ PUFXENDPOINT_INIT EndpointInit + ) +/*++ + +Routine Description: + + EvtDeviceEndpointAdd handler for the UFXDEVICE object. + Creates UFXENDPOINT object corresponding to the newly reported endpoint. + +Arguments: + + UfxDevice - UFXDEVICE object representing the device. + + EndpointDescriptor - Cosntant Pointer to Endpoint descriptor for the + newly reported endpoint. + + EndpointInit - Pointer to the Opaque UFXENDPOINT_INIT object + +Return Value: + + STATUS_SUCCESS on success, or an appropirate NTSTATUS message on failure. + +--*/ +{ + NTSTATUS Status; + + TraceEntry(); + + Status = UfxEndpointAdd(UfxDevice, EndpointDescriptor, EndpointInit); + CHK_NT_MSG(Status, "Failed to create endpoint"); + +End: + + TraceExit(); + return Status; +} + + +VOID +UfxDevice_EvtDeviceDefaultEndpointAdd ( + _In_ UFXDEVICE UfxDevice, + _In_ USHORT MaxPacketSize, + _Inout_ PUFXENDPOINT_INIT EndpointInit + ) +/*++ + +Routine Description: + + EvtDeviceDefaultEndpointAdd handler for the UFXDEVICE object. + Creates UFXENDPOINT object corresponding to the default endpoint of the + device. + +Arguments: + + UfxDevice - UFXDEVICE object representing the device. + + MaxPacketSize - Max packet size of the device's default endpoint. + + EndpointInit - Pointer to the Opaque UFXENDPOINT_INIT object + +--*/ +{ + NTSTATUS Status; + USB_ENDPOINT_DESCRIPTOR Descriptor; + + PAGED_CODE(); + + TraceEntry(); + + Descriptor.bDescriptorType = USB_ENDPOINT_DESCRIPTOR_TYPE; + Descriptor.bEndpointAddress = 0; + Descriptor.bInterval = 0; + Descriptor.bLength = sizeof(USB_ENDPOINT_DESCRIPTOR); + Descriptor.bmAttributes = USB_ENDPOINT_TYPE_CONTROL; + Descriptor.wMaxPacketSize = MaxPacketSize; + + Status = UfxEndpointAdd(UfxDevice, &Descriptor, EndpointInit); + CHK_NT_MSG(Status, "Failed to create default endpoint!"); + +End: + UfxDeviceEventComplete(UfxDevice, Status); + TraceExit(); +} + + +VOID +UfxDevice_EvtDeviceUsbStateChange ( + _In_ UFXDEVICE UfxDevice, + _In_ USBFN_DEVICE_STATE NewState +) +/*++ + +Routine Description: + + EvtDeviceUsbStateChange handler for the UFXDEVICE object. + +Arguments: + + UfxDevice - UFXDEVICE object representing the device. + + NewState - The new device state. + +--*/ +{ + NTSTATUS Status; + PUFXDEVICE_CONTEXT Context; + PCONTROLLER_CONTEXT ControllerContext; + ULONG EpIndex; + USBFN_DEVICE_STATE OldState; + + PAGED_CODE(); + + TraceEntry(); + + Context = UfxDeviceGetContext(UfxDevice); + ControllerContext = DeviceGetControllerContext(Context->FdoWdfDevice); + OldState = Context->UsbState; + + TraceInformation("New STATE: %d", NewState); + + Status = UfxDeviceStopOrResumeIdle(UfxDevice, NewState, Context->UsbPort); + LOG_NT_MSG(Status, "Failed to stop or resume idle"); + + WdfWaitLockAcquire(ControllerContext->InitializeDefaultEndpointLock, NULL); + if (ControllerContext->InitializeDefaultEndpoint == TRUE) { + // + // Reset endpoint 0. This is the last part of soft reset, which was postponed + // until now, since we need to make sure EP0 is created by UFX. + // + DeviceInitializeDefaultEndpoint(Context->FdoWdfDevice); + ControllerContext->InitializeDefaultEndpoint = FALSE; + } + WdfWaitLockRelease(ControllerContext->InitializeDefaultEndpointLock); + + if (NewState == UsbfnDeviceStateConfigured && OldState != UsbfnDeviceStateSuspended) { + + for (EpIndex = 1; EpIndex < WdfCollectionGetCount(Context->Endpoints); EpIndex++) { + UfxEndpointConfigure(WdfCollectionGetItem(Context->Endpoints, EpIndex)); + } + + // + // #### TODO: Insert code to allow the controller to accept U1/U2, if supported #### + // + + } + + + if (NewState == UsbfnDeviceStateDetached) { + KeSetEvent(&ControllerContext->DetachEvent, + IO_NO_INCREMENT, + FALSE); + } + + UfxDeviceEventComplete(UfxDevice, STATUS_SUCCESS); + TraceExit(); +} + +_Function_class_(EVT_UFX_DEVICE_PORT_CHANGE) +_IRQL_requires_(PASSIVE_LEVEL) +VOID +UfxDevice_EvtDevicePortChange ( + _In_ + UFXDEVICE UfxDevice, + _In_ + USBFN_PORT_TYPE NewPort + ) +/*++ + +Routine Description: + + EvtDevicePortChange handler for the UFXDEVICE object. + Caches the new port type, and stops or resumes idle as needed. + +Arguments: + + UfxDevice - UFXDEVICE object representing the device. + + NewPort - New port type + +--*/ +{ + NTSTATUS Status; + PUFXDEVICE_CONTEXT Context; + + PAGED_CODE(); + + TraceEntry(); + + Context = UfxDeviceGetContext(UfxDevice); + + TraceInformation("New PORT: %d", NewPort); + + Status = UfxDeviceStopOrResumeIdle(UfxDevice, Context->UsbState, NewPort); + LOG_NT_MSG(Status, "Failed to stop or resume idle"); + + UfxDeviceEventComplete(UfxDevice, STATUS_SUCCESS); + TraceExit(); +} + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +UfxDevice_Reset ( + _In_ UFXDEVICE Device + ) +/*++ + +Routine Description: + + Cancels all transfers and disables non-zero endpoints in + response to USB reset. + +Arguments: + + UfxDevice - UFXDEVICE object representing the device. + +--*/ +{ + PUFXDEVICE_CONTEXT DeviceContext; + PREGISTERS_CONTEXT RegistersContext; + ULONG EpIndex; + + TraceEntry(); + + DeviceContext = UfxDeviceGetContext(Device); + RegistersContext = DeviceGetRegistersContext(DeviceContext->FdoWdfDevice); + + // + // Get EP0 back to setup stage. + // + TransferReset(WdfCollectionGetFirstItem(DeviceContext->Endpoints)); + + // + // Disable all non-default endpoints + // + + // + // #### TODO: Insert code to disable non-default endpoints #### + // + + // + // End any transfers on non-default endpoints. + // + for (EpIndex = 1; EpIndex < WdfCollectionGetCount(DeviceContext->Endpoints); EpIndex++) { + TransferReset(WdfCollectionGetItem(DeviceContext->Endpoints, EpIndex)); + } + + TraceExit(); +} + + +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +UfxDeviceStopOrResumeIdle ( + _In_ UFXDEVICE Device, + _In_ USBFN_DEVICE_STATE UsbState, + _In_ USBFN_PORT_TYPE UsbPort + ) +/*++ + +Routine Description: + + Examines the device USB state and port type and determines + if it needs to stop or resume idle. + +Arguments: + + UfxDevice - UFXDEVICE object representing the device. + +Return Value: + + STATUS_SUCCESS on success, or an appropirate NTSTATUS message on failure. + +--*/ +{ + PUFXDEVICE_CONTEXT DeviceContext; + PCONTROLLER_CONTEXT ControllerContext; + NTSTATUS Status; + BOOLEAN NeedPower; + DEVICE_POWER_STATE DxState = PowerDeviceD3; + + PAGED_CODE(); + + TraceEntry(); + + DeviceContext = UfxDeviceGetContext(Device); + ControllerContext = DeviceGetControllerContext(DeviceContext->FdoWdfDevice); + + switch (UsbState) { + + case UsbfnDeviceStateAttached: + __fallthrough; + case UsbfnDeviceStateDefault: + + switch (UsbPort) { + + case UsbfnStandardDownstreamPort: + __fallthrough; + case UsbfnChargingDownstreamPort: + __fallthrough; + case UsbfnUnknownPort: + NeedPower = TRUE; + break; + + case UsbfnDedicatedChargingPort: + case UsbfnProprietaryDedicatedChargingPort: + NeedPower = FALSE; + DxState = PowerDeviceD2; + break; + + default: + NeedPower = FALSE; + } + break; + + case UsbfnDeviceStateSuspended: + NeedPower = FALSE; + DxState = PowerDeviceD2; + break; + + case UsbfnDeviceStateAddressed: + __fallthrough; + + case UsbfnDeviceStateConfigured: + NeedPower = TRUE; + break; + + default: + NeedPower = FALSE; + } + + // + // Determine if our lowest idle state has changed, and set it if so + // + if (DxState != ControllerContext->IdleSettings.DxState) + { + ControllerContext->IdleSettings.DxState = DxState; + Status = WdfDeviceAssignS0IdleSettings( + DeviceContext->FdoWdfDevice, + &ControllerContext->IdleSettings); + CHK_NT_MSG(Status, "Failed to update device idle settings"); + } + + if (NeedPower && DeviceContext->IsIdle) { + // + // We don't want to update the USB state /port until we stop idle to + // prevent D0 -> DX path from reading the wrong state. + // + TraceInformation("Stopping idle"); + Status = WdfDeviceStopIdle(DeviceContext->FdoWdfDevice, TRUE); + CHK_NT_MSG(Status, "Failed to stop idle"); + DeviceContext->UsbState = UsbState; + DeviceContext->UsbPort = UsbPort; + DeviceContext->IsIdle = FALSE; + + } else if (!NeedPower && !DeviceContext->IsIdle) { + // + // We need to update USB state / port before resume idle to ensure + // D0 -> DX path reads the correct state. + // + DeviceContext->UsbState = UsbState; + DeviceContext->UsbPort = UsbPort; + DeviceContext->IsIdle = TRUE; + TraceInformation("Resuming idle"); + WdfDeviceResumeIdle(DeviceContext->FdoWdfDevice); + + } else { + TraceInformation("No idle action"); + DeviceContext->UsbState = UsbState; + DeviceContext->UsbPort = UsbPort; + } + + Status = STATUS_SUCCESS; + +End: + TraceExit(); + return Status; +} + +VOID +UfxDevice_EvtDevicePortDetect ( + _In_ UFXDEVICE UfxDevice + ) +/*++ +Routine Description: + + Starts the port detection state machine + +Arguments: + + UfxDevice - UFXDEVICE object representing the device. + +--*/ +{ + PUFXDEVICE_CONTEXT DeviceContext; + PCONTROLLER_CONTEXT ControllerContext; + + DeviceContext = UfxDeviceGetContext(UfxDevice); + ControllerContext = DeviceGetControllerContext(DeviceContext->FdoWdfDevice); + + // + // #### TODO: Insert code to determine port/charger type #### + // + // In this example we will return an unknown port type. This will allow UFX to connect to a host if + // one is present. UFX will timeout after 5 seconds if no host is present and transition to + // an invalid charger type, which will allow the controller to exit D0. + // + UfxDevicePortDetectComplete(ControllerContext->UfxDevice, UsbfnUnknownPort); +} + + +VOID +UfxDevice_EvtDeviceRemoteWakeupSignal ( + _In_ UFXDEVICE UfxDevice + ) +/*++ +Routine Description: + + Signals Remote Wakeup to the Host by issuing a link state change command. + It acquires and releases the power reference to ensure a valid power state + before accessing the device. + +Arguments: + + UfxDevice - UFXDEVICE object representing the device. + +--*/ +{ + NTSTATUS Status; + PUFXDEVICE_CONTEXT DeviceContext; + + PAGED_CODE(); + + TraceEntry(); + + DeviceContext = UfxDeviceGetContext(UfxDevice); + + // + // Stop Idle to ensure the device is in working state + // + Status = WdfDeviceStopIdle(DeviceContext->FdoWdfDevice, TRUE); + if (!NT_SUCCESS(Status)) { + TraceError("Failed to stop idle %!STATUS!", Status); + goto End; + } + + // + // Issue a Link State Change Request. + // + + // + // #### TODO: Insert code to issue a link state change on the controller #### + // + + WdfDeviceResumeIdle(DeviceContext->FdoWdfDevice); + +End: + UfxDeviceEventComplete(UfxDevice, Status); + TraceExit(); +} + +VOID +UfxDevice_EvtDeviceTestModeSet ( + _In_ UFXDEVICE UfxDevice, + _In_ ULONG TestMode + ) +/*++ + +Routine Description: + + EvtDeviceTestModeSet handler for the UFXDEVICE object. + + Handles a set test mode request from the host. Places the controller into + the specified test mode. + +Arguments: + + UfxDevice - UFXDEVICE object representing the device. + + TestMode - Test mode value. See Section 7.1.20 of the USB 2.0 specification for definitions of + each test mode. + +--*/ +{ + NTSTATUS Status; + + UNREFERENCED_PARAMETER(TestMode); + + TraceEntry(); + + // + // #### TODO: Insert code to put the controller into the specified test mode #### + // + + Status = STATUS_SUCCESS; + + UfxDeviceEventComplete(UfxDevice, Status); + TraceExit(); +} + +VOID +UfxDevice_EvtDeviceSuperSpeedPowerFeature ( + _In_ UFXDEVICE Device, + _In_ USHORT Feature, + _In_ BOOLEAN Set + ) +/*++ + +Routine Description: + + EvtDeviceSuperSpeedPowerFeature handler for the UFXDEVICE object. + + Handles a set or clear U1/U2 request from the host. + +Arguments: + + UfxDevice - UFXDEVICE object representing the device. + + Feature - Indicates the feature being set or cleared. Either U1 or U2 enable. + + Set - Indicates if the feature should be set or cleared + +--*/ +{ + TraceEntry(); + + if (Feature == USB_FEATURE_U1_ENABLE) { + if (Set == TRUE) { + // + // #### TODO: Insert code to initiate U1 #### + // + } else { + // + // #### TODO: Insert code to exit U1 #### + // + } + } else if (Feature == USB_FEATURE_U2_ENABLE) { + if (Set == TRUE) { + // + // #### TODO: Insert code to initiate U2 #### + // + } else { + // + // #### TODO: Insert code to exit U2 #### + // + } + } else { + NT_ASSERT(FALSE); + } + + UfxDeviceEventComplete(Device, STATUS_SUCCESS); + TraceExit(); +} + diff --git a/usb/ufxclientsample/ufxdevice.h b/usb/ufxclientsample/ufxdevice.h new file mode 100644 index 000000000..4b78caba7 --- /dev/null +++ b/usb/ufxclientsample/ufxdevice.h @@ -0,0 +1,67 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + ufxdevice.h + +Abstract: + + Defines the structures needed for UFXDEVICE object + +Environment: + + Kernel mode + +--*/ +#pragma once + +#define MAX_PHYSICAL_ENDPOINTS 32 + +// +// Context space for Sample UFXDEVICE object +// +typedef struct _UFXDEVICE_CONTEXT { + WDFDEVICE FdoWdfDevice; + + // + // This is an out-of-order collection of endpoints, since endpoints can be + // re-ordered as UFX brings interfaces up and down. However, endpoint 0 + // is always at index 0. + // + WDFCOLLECTION Endpoints; + UFXENDPOINT PhysicalEndpointToUfxEndpoint[MAX_PHYSICAL_ENDPOINTS]; + USBFN_DEVICE_STATE UsbState; + USBFN_PORT_TYPE UsbPort; + BOOLEAN IsIdle; +} UFXDEVICE_CONTEXT, *PUFXDEVICE_CONTEXT; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(UFXDEVICE_CONTEXT, UfxDeviceGetContext); + +_Must_inspect_result_ +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +UfxDevice_DeviceCreate( + _In_ WDFDEVICE WdfDevice + ); + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +UfxDevice_Reset ( + _In_ UFXDEVICE Device + ); + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +UfxDeviceSetRunStop ( + _In_ UFXDEVICE UfxDevice, + _In_ BOOLEAN Set + ); + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +UfxDevice_StartConfiguration ( + _In_ UFXDEVICE Device, + _In_ BOOLEAN SoftReset + ); diff --git a/usb/ufxclientsample/ufxendpoint.c b/usb/ufxclientsample/ufxendpoint.c new file mode 100644 index 000000000..195501ec1 --- /dev/null +++ b/usb/ufxclientsample/ufxendpoint.c @@ -0,0 +1,481 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + ufxendpoint.c + +Abstract: + + Defines the functions needed for UFXENDPOINT object + +Environment: + + Kernel mode + +--*/ + +#include "ufxendpoint.h" +#include "ufxdevice.h" +#include "transfer.h" +#include +#include "registers.h" +#include "trace.h" +#include "device.h" +#include "ufxendpoint.tmh" + +EVT_WDF_IO_QUEUE_IO_CANCELED_ON_QUEUE EvtIoCanceledOnQueue; +EVT_WDF_IO_QUEUE_IO_STOP EndpointQueue_EvtIoStop; +EVT_WDF_IO_QUEUE_IO_INTERNAL_DEVICE_CONTROL EvtEndpointCommandQueue; + +EVT_WDF_OBJECT_CONTEXT_CLEANUP UfxEndpoint_Cleanup; + +_Must_inspect_result_ +_IRQL_requires_max_(DISPATCH_LEVEL) +NTSTATUS +UfxEndpointDescriptorUpdate ( + _In_ UFXENDPOINT Endpoint, + _In_ PUSB_ENDPOINT_DESCRIPTOR Descriptor + ); + +_Must_inspect_result_ +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +UfxEndpointAdd ( + _In_ UFXDEVICE Device, + _In_ PUSB_ENDPOINT_DESCRIPTOR Descriptor, + _Inout_ PUFXENDPOINT_INIT EndpointInit + ) +/*++ +Routine Description: + + Creates a UFXENDPOINT object, and initializes its contexts. + +Parameters Description: + + Device - UFXDEVICE associated with the endpoint. + + Descriptor - Endpoint descriptor for this endpoint. + + EndpointInit - Opaque structure from UFX. + +Return Value: + + STATUS_SUCCESS if successful, appropriate NTSTATUS message otherwise. +--*/ +{ + NTSTATUS Status; + WDF_OBJECT_ATTRIBUTES Attributes; + WDF_IO_QUEUE_CONFIG TransferQueueConfig; + WDF_OBJECT_ATTRIBUTES TransferQueueAttributes; + WDF_IO_QUEUE_CONFIG CommandQueueConfig; + WDF_OBJECT_ATTRIBUTES CommandQueueAttributes; + UFXENDPOINT Endpoint; + PUFXENDPOINT_CONTEXT EpContext; + PUFXDEVICE_CONTEXT DeviceContext; + UFX_ENDPOINT_CALLBACKS Callbacks; + PENDPOINT_QUEUE_CONTEXT QueueContext; + WDFQUEUE Queue; + + TraceEntry(); + + DeviceContext = UfxDeviceGetContext(Device); + + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&Attributes, UFXENDPOINT_CONTEXT); + Attributes.ExecutionLevel = WdfExecutionLevelPassive; + Attributes.EvtCleanupCallback = UfxEndpoint_Cleanup; + + // + // Note: Execution level needs to be passive to avoid deadlocks with WdfRequestComplete. + // + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&TransferQueueAttributes, ENDPOINT_QUEUE_CONTEXT); + TransferQueueAttributes.ExecutionLevel = WdfExecutionLevelPassive; + + WDF_IO_QUEUE_CONFIG_INIT(&TransferQueueConfig, WdfIoQueueDispatchManual); + TransferQueueConfig.AllowZeroLengthRequests = TRUE; + TransferQueueConfig.EvtIoStop = EndpointQueue_EvtIoStop; + + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&CommandQueueAttributes, ENDPOINT_QUEUE_CONTEXT); + CommandQueueAttributes.ExecutionLevel = WdfExecutionLevelPassive; + + WDF_IO_QUEUE_CONFIG_INIT(&CommandQueueConfig, WdfIoQueueDispatchSequential); + CommandQueueConfig.EvtIoInternalDeviceControl = EvtEndpointCommandQueue; + + UFX_ENDPOINT_CALLBACKS_INIT(&Callbacks); + UfxEndpointInitSetEventCallbacks(EndpointInit, &Callbacks); + + Status = UfxEndpointCreate( + Device, + EndpointInit, + &Attributes, + &TransferQueueConfig, + &TransferQueueAttributes, + &CommandQueueConfig, + &CommandQueueAttributes, + &Endpoint); + CHK_NT_MSG(Status, "Failed to create ufxendpoint!"); + + Status = WdfCollectionAdd(DeviceContext->Endpoints, Endpoint); + CHK_NT_MSG(Status, "Failed to add endpoint to collection!"); + + EpContext = UfxEndpointGetContext(Endpoint); + EpContext->UfxDevice = Device; + EpContext->WdfDevice = DeviceContext->FdoWdfDevice; + RtlCopyMemory(&EpContext->Descriptor, Descriptor, sizeof(*Descriptor)); + + Queue = UfxEndpointGetTransferQueue(Endpoint); + QueueContext = EndpointQueueGetContext(Queue); + QueueContext->Endpoint = Endpoint; + + Queue = UfxEndpointGetCommandQueue(Endpoint); + QueueContext = EndpointQueueGetContext(Queue); + QueueContext->Endpoint = Endpoint; + + Status = TransferInitialize(Endpoint); + CHK_NT_MSG(Status, "Failed to initialize endpoint transfers"); + + // + // This can happen if we're handling a SetInterface command. + // + if (DeviceContext->UsbState == UsbfnDeviceStateConfigured) { + UfxEndpointConfigure(Endpoint); + } + + Status = WdfIoQueueReadyNotify( + UfxEndpointGetTransferQueue(Endpoint), + TransferReadyNotify, + Endpoint); + CHK_NT_MSG(Status, "Failed to register ready notify"); + +End: + TraceExit(); + return Status; +} + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +UfxEndpointConfigureHardware ( + _In_ UFXENDPOINT Endpoint, + _In_ BOOLEAN Modify + ) +/*++ +Routine Description: + + Configures the endpoint on the controller based on the + endpoint descriptor. If it is a control endpoint, configures + for both IN and OUT endpoints. + +Parameters Description: + + Endpoint - Endpoint to configure + + Modify - This is only TRUE for endpoint 0 on soft reset. + +--*/ +{ + PUFXENDPOINT_CONTEXT EpContext; + + TraceEntry(); + + UNREFERENCED_PARAMETER(Modify); + + EpContext = UfxEndpointGetContext(Endpoint); + + TraceInformation("CONFIGURE ENDPOINT: %08X Endpoint (%d)", (ULONG) Endpoint, EpContext->PhysicalEndpoint); + + // + // ControllerContext = DeviceGetControllerContext(EpContext->WdfDevice); + // + // The USB address is: + // EpContext->Descriptor.bEndpointAddress & USB_ENDPOINT_ADDRESS_MASK + // + // The maxPacketSize is: + // (Address == 0 && !Modify) ? 512 : EpContext->Descriptor.wMaxPacketSize; + // + // The Interval is: + // ((EpContext->Descriptor.bInterval > 0) && (ControllerContext->Speed != UsbFullSpeed)) ? + // EpContext->Descriptor.bInterval : + // 0; + // + + if (CONTROL_ENDPOINT(Endpoint)) { + // + // #### TODO: Configure both IN and OUT endpoints #### + // + } else { + if (DIRECTION_IN(Endpoint)) { + // + // #### TODO: Configure the IN endpoint #### + // + } else { + // + // #### TODO: Configure the OUT endpoint #### + // + } + } + + TraceExit(); +} + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +UfxEndpointConfigure ( + _In_ UFXENDPOINT Endpoint + ) +/*++ +Routine Description: + + Enables and configures the endpoint on the controller. + +Parameters Description: + + Endpoint - Endpoint to configure + +--*/ +{ + PUFXENDPOINT_CONTEXT EpContext; + ULONG EndpointMask; + ULONG Address; + + TraceEntry(); + + EpContext = UfxEndpointGetContext(Endpoint); + + EndpointMask = 1 << (EpContext->PhysicalEndpoint); + if (CONTROL_ENDPOINT(Endpoint)) { + EndpointMask |= 1 << (EpContext->PhysicalEndpoint + 1); + } + + Address = EpContext->Descriptor.bEndpointAddress & USB_ENDPOINT_ADDRESS_MASK; + + // + // Configure the endpoint + // + UfxEndpointConfigureHardware(Endpoint, Address == 0); + + // + // #### TODO: Insert code to enable the endpoint on the controller #### + // + + if (Address != 0) { + TransferStart(Endpoint); + } + + TraceExit(); +} + +_Must_inspect_result_ +_IRQL_requires_max_(DISPATCH_LEVEL) +NTSTATUS +UfxEndpointDescriptorUpdate ( + _In_ UFXENDPOINT Endpoint, + _In_ PUSB_ENDPOINT_DESCRIPTOR Descriptor + ) +/*++ +Routine Description: + + Updates the descriptor and re-programs the endpoint. + If endpoint 0 is passed, starts a new configuration. + +Parameters Description: + + Endpoint - Endpoint to update. + + Descriptor - Endpoint descriptor for this endpoint. + +Return Value: + + STATUS_SUCCESS if successful, appropriate NTSTATUS message otherwise. + +--*/ +{ + PUFXENDPOINT_CONTEXT EpContext; + + TraceEntry(); + + EpContext = UfxEndpointGetContext(Endpoint); + + RtlCopyMemory(&EpContext->Descriptor, Descriptor, sizeof(USB_ENDPOINT_DESCRIPTOR)); + + if ((Descriptor->bEndpointAddress & USB_ENDPOINT_ADDRESS_MASK) == 0) { + UfxEndpointConfigure(Endpoint); + } + + TraceExit(); + return STATUS_SUCCESS; +} + +VOID +UfxEndpoint_Cleanup ( + _In_ WDFOBJECT Object + ) +/*++ +Routine Description: + + Cleanup routine for the UFX endpoint object. + +Parameters Description: + + Object - UFX endpoint object + +--*/ +{ + UFXENDPOINT Endpoint; + PUFXENDPOINT_CONTEXT EpContext; + ULONG EndpointMask; + PUFXDEVICE_CONTEXT DeviceContext; + NTSTATUS Status; + + TraceEntry(); + + Endpoint = (UFXENDPOINT) Object; + EpContext = UfxEndpointGetContext(Endpoint); + DeviceContext = UfxDeviceGetContext(EpContext->UfxDevice); + + // + // Release all references + // + WdfCollectionRemove(DeviceContext->Endpoints, Endpoint); + + // + // Disable the endpoint. Note queue should already be purged by UFX. + // + Status = WdfDeviceStopIdle(DeviceContext->FdoWdfDevice, TRUE); + CHK_NT_MSG(Status, "Failed to stop idle to disable endpoint"); + + EndpointMask = 1 << (EpContext->PhysicalEndpoint); + if (CONTROL_ENDPOINT(Endpoint)) { + EndpointMask |= 1 << (EpContext->PhysicalEndpoint + 1); + } + + // + // #### TODO: Add code to disable the endpoint on the controller #### + // + + WdfDeviceResumeIdle(DeviceContext->FdoWdfDevice); + + // + // Delete allocated stuff + // + TransferDestroy(Endpoint); + +End: + TraceExit(); +} + +_Use_decl_annotations_ +VOID +EndpointQueue_EvtIoStop ( + WDFQUEUE Queue, + WDFREQUEST Request, + ULONG ActionFlags + ) +/*++ +Routine Description: + + EvtIoStop event handler + +Parameters Description: + + Queue - Handle to the queue object + + Request - The request to be completed, requeued, or suspended. + + ActionFlags - Bitmask indicating action to take and if request is cancelable. + +--*/ +{ + UNREFERENCED_PARAMETER(Queue); + UNREFERENCED_PARAMETER(ActionFlags); + + TraceEntry(); + + TransferRequestCancel(Request, TRUE); + + TraceExit(); +} + +VOID +EvtEndpointCommandQueue ( + _In_ WDFQUEUE Queue, + _In_ WDFREQUEST Request, + _In_ size_t OutputBufferLength, + _In_ size_t InputBufferLength, + _In_ ULONG IoControlCode + ) +/*++ +Routine Description: + + EvtIoInternalDeviceControl event handler + +Parameters Description: + + Queue - Handle to the queue object + + Request - Internal device control request. + + OutputBufferLength - size of the output buffer. + + InputBufferLength - size of the input buffer. + + IoControlCode - IOCTL for the request. + +--*/ + +{ + PVOID OutputBuffer; + PENDPOINT_QUEUE_CONTEXT QueueContext; + NTSTATUS Status; + PUFXENDPOINT_CONTEXT EpContext; + + UNREFERENCED_PARAMETER(OutputBufferLength); + UNREFERENCED_PARAMETER(InputBufferLength); + + TraceEntry(); + + QueueContext = EndpointQueueGetContext(Queue); + EpContext = UfxEndpointGetContext(QueueContext->Endpoint); + + Status = WdfRequestRetrieveOutputBuffer(Request, 0, &OutputBuffer, NULL); + CHK_NT_MSG(Status, "Failed to retrieve command output buffer"); + + switch(IoControlCode) { + case IOCTL_INTERNAL_USBFN_GET_PIPE_STATE: + TransferGetStall( + QueueContext->Endpoint, + (PBOOLEAN) OutputBuffer); + + WdfRequestComplete(Request, STATUS_SUCCESS); + break; + + case IOCTL_INTERNAL_USBFN_SET_PIPE_STATE: + if (*((PBOOLEAN) OutputBuffer)) { + EpContext->StallRequest = Request; + TransferCommandStallSet(QueueContext->Endpoint); + + } else { + EpContext->ClearRequest = Request; + TransferCommandStallClear(QueueContext->Endpoint); + } + break; + + case IOCTL_INTERNAL_USBFN_DESCRIPTOR_UPDATE: + Status = UfxEndpointDescriptorUpdate( + QueueContext->Endpoint, + (PUSB_ENDPOINT_DESCRIPTOR) OutputBuffer); + CHK_NT_MSG(Status, "Failed to update endpoint descriptor"); + WdfRequestComplete(Request, STATUS_SUCCESS); + break; + + default: + Status = STATUS_INVALID_DEVICE_REQUEST; + goto End; + } + +End: + if (!NT_SUCCESS(Status)) { + WdfRequestComplete(Request, Status); + } + TraceExit(); +} \ No newline at end of file diff --git a/usb/ufxclientsample/ufxendpoint.h b/usb/ufxclientsample/ufxendpoint.h new file mode 100644 index 000000000..7eb3a2830 --- /dev/null +++ b/usb/ufxclientsample/ufxendpoint.h @@ -0,0 +1,73 @@ +/*++ + +Copyright (c) 1990-2013 Microsoft Corporation. All rights reserved. + +Module Name: + + ufxendpoint.h + +Abstract: + + Defines the structures and functions needed for UFXENDPOINT object + +Environment: + + Kernel mode + +--*/ + +#pragma once + +#include +#include "transfer.h" + +#define CONTROL_ENDPOINT(Endpoint) \ + ((UfxEndpointGetContext(Endpoint)->Descriptor.bmAttributes & USB_ENDPOINT_TYPE_MASK) == \ + USB_ENDPOINT_TYPE_CONTROL) + +#define DIRECTION_IN(Endpoint) \ + (CONTROL_ENDPOINT(Endpoint) || \ + USB_ENDPOINT_DIRECTION_IN(UfxEndpointGetContext(Endpoint)->Descriptor.bEndpointAddress)) + +#define DIRECTION_OUT(Endpoint) \ + (CONTROL_ENDPOINT(Endpoint) || \ + USB_ENDPOINT_DIRECTION_OUT(UfxEndpointGetContext(Endpoint)->Descriptor.bEndpointAddress)) + +typedef struct _UFXENDPOINT_CONTEXT { + WDFDEVICE WdfDevice; + UFXDEVICE UfxDevice; + USB_ENDPOINT_DESCRIPTOR Descriptor; + ULONG PhysicalEndpoint; + WDFREQUEST StallRequest; + WDFREQUEST ClearRequest; +} UFXENDPOINT_CONTEXT, *PUFXENDPOINT_CONTEXT; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(UFXENDPOINT_CONTEXT, UfxEndpointGetContext); + +typedef struct _ENDPOINT_QUEUE_CONTEXT { + UFXENDPOINT Endpoint; +} ENDPOINT_QUEUE_CONTEXT, *PENDPOINT_QUEUE_CONTEXT; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(ENDPOINT_QUEUE_CONTEXT, EndpointQueueGetContext); + +_Must_inspect_result_ +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +UfxEndpointAdd ( + _In_ UFXDEVICE Device, + _In_ PUSB_ENDPOINT_DESCRIPTOR Descriptor, + _Inout_ PUFXENDPOINT_INIT EndpointInit + ); + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +UfxEndpointConfigure ( + _In_ UFXENDPOINT Endpoint + ); + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +UfxEndpointConfigureHardware ( + _In_ UFXENDPOINT Endpoint, + _In_ BOOLEAN Modify + ); \ No newline at end of file diff --git a/usb/umdf2_fx2/ReadMe.md b/usb/umdf2_fx2/ReadMe.md index 4f825de5f..291e91cbb 100644 --- a/usb/umdf2_fx2/ReadMe.md +++ b/usb/umdf2_fx2/ReadMe.md @@ -1,326 +1,74 @@ -Sample UMDF Function Driver for OSR USB-FX2 (UMDF Version 1) -============================================================ +Sample Function Driver for OSR USB-FX2 (UMDF Version 2) +======================================================= -The umdf\_fx2 sample is a User-Mode Driver Framework (UMDF) driver for the OSR USB-FX2 device. It includes a test app and sample device metadata, and supports impersonation and idle power down. +The umdf2\_fx2 sample is a User-Mode Driver Framework (UMDF) version 2 driver for the OSR USB-FX2 device. + +The specification for the device is at . The driver and sample device metadata also work with the [Custom driver access](http://go.microsoft.com/fwlink/p/?LinkID=248288) sample. ## Universal Windows Driver Compliant This sample builds a Universal Windows Driver. It uses only APIs and DDIs that are included in OneCoreUAP. -The sample can also be used with the CustomDeviceAccess MSDK sample. The sample demonstrates how to perform bulk and interrupt data transfers to an USB device. The specification for the device is at . The driver and sample device metadata also work with the [Custom driver access](http://go.microsoft.com/fwlink/p/?LinkID=248288) sample. - -Starting in Windows 8.1, the osrusbfx2 sample has been divided into these samples: -* [WDF Sample Driver Learning Lab for OSR USB-FX2](http://msdn.microsoft.com/en-us/library/windows/hardware/): This sample is a series of iterative drivers that demonstrate how to write a "Hello World" driver and adds additional features in each step. -* [kmdf\_fx2](gallery_samples.123a_gallery#1): This sample is the final version of kernel-mode [wdf\_osrfx2](http://msdn.microsoft.com/en-us/library/windows/hardware/) driver. The sample demonstrates KMDF methods. -* umdf\_fx2: This sample is the final version of the user-mode driver [wdf\_osrfx2](http://msdn.microsoft.com/en-us/library/windows/hardware/). The sample demonstrates UMDF methods. - -**Note** -Due to a known bug in the current Windows Driver Kit tools, when building this sample in Debug mode, the exe project may report some ApiValidator warnings. These can be safely ignored. - -Overview --------- - -Here is the overview of the device: - -- The device is based on the development board supplied with the Cypress EZ-USB FX2 Development Kit (CY3681). -- It contains 1 interface and 3 endpoints (Interrupt IN, Bulk Out, Bulk IN). -- Firmware supports vendor commands to query or set LED Bar graph display and 7-segment LED display, and to query toggle switch states. -- Interrupt Endpoint: - - Sends an 8-bit value that represents the state of the switches. - - Sent on startup, resume from suspend, and whenever the switch pack setting changes. - - Firmware does not de-bounce the switch pack. - - One switch change can result in multiple bytes being sent. - - Bits are in the reverse order of the labels on the pack (for example, bit 0x80 is labeled 1 on the pack). -- Bulk Endpoints are configured for loopback: - - The device moves data from IN endpoint to OUT endpoint. - - The device does not change the values of the data it receives nor does it internally create any data. - - Endpoints are always double buffered. - - Maximum packet size depends on speed (64 full speed, 512 high speed). - -Testing the driver ------------------- - -You can use the [Custom driver access](http://go.microsoft.com/fwlink/p/?LinkID=248288) sample to test the umdf\_fx2 sample. - -This sample also includes a test application, osrusbfx2.exe, that you can use to test the device. This console application enumerates the interface registered by the driver and opens the device to send read, write, or IOCTL requests based on the command line options. - -Usage for Read/Write test: - -- -r [*n*], where *n* is number of bytes to read. -- -w [*n*], where *n* is number of bytes to write. -- -c [*n*], where *n* is number of iterations (default = 1). -- -v, shows verbose read data. -- -p, plays with Bar Display, Dip Switch, 7-Segment Display. -- -a, performs asynchronous I/O operation. -- -u, dumps USB configuration and pipe information. -- -f \<*filename*\> [*interval-seconds*], where *interval-seconds* is a delay in milliseconds, to send a text file to the seven-segment display (UMDF only) - -**Playing with the 7 segment display, toggle switches, and bar graph display** - -Use the command **osrusbfx2.exe -p** with options 1 through 9 to set and clear bar graph display, set and get 7 segment state, and read the toggle switch states. The following shows the function options: - -1. Light Bar -2. Clear Bar -3. Light entire Bar graph -4. Clear entire Bar graph -5. Get bar graph state -6. Get Switch state -7. Get Switch Interrupt Message -8. Get 7 segment state -9. Set 7 segment state -10. Reset the device -11. Re-enumerate the device - -0. Exit - -Selection: - -**Reset and re-enumerate the device** - -Use the command **osrusbfx2.exe -p** with options 10 and 11 to either reset the device or re-enumerate the device. - -**Read and write to bulk endpoints** - -The following commands send read and write requests to the device's bulk endpoint. - -- `osrusbfx2.exe -r 64` - - The preceding command reads 64 bytes to the bulk IN endpoint. - -- `osrusbfx2.exe -w 64 ` - - The preceding command writes 64 bytes to the bulk OUT endpoint. - -- `osrusbfx2.exe -r 64 -w 64 -c 100 -v` - - The preceding command first writes 64 bytes of data to bulk OUT endpoint (Pipe 1), then reads 64 bytes from bulk IN endpoint (Pipe 2), and then compares the read buffer with write buffer to see if they match. If the buffer contents match, it repeats this operation 100 times. - -- `osrusbfx2.exe -a` - - The preceding command reads and writes to the device asynchronously in an infinite loop. - -The bulk endpoints are double buffered. Depending on the operational speed (full or high), the buffer size is either 64 bytes or 512 bytes, respectively. A request to read data does not complete if the buffers are empty. If the buffers are full, a request to write data does not complete until the buffers are emptied. When you are doing a synchronous read, make sure the endpoint buffer has data (for example, when you send 512 bytes write request to the device operating in full speed mode). Because the endpoints are double buffered, the total buffer capacity is 256 bytes. The first 256 bytes fills the buffer and the write request waits in the USB stack until the buffers are emptied. If you run another instance of the application to read 512 bytes of data, both write and read requests complete successfully. - -**Displaying descriptors** - -The following command displays all the descriptors and endpoint information. - -**osrusbfx2.exe -u** - -If the device is operating in high speed mode, you get the following information: - -`===================` - -`USB_CONFIGURATION_DESCRIPTOR` - -`bLength = 0x9, decimal 9` - -`bDescriptorType = 0x2 ( USB_CONFIGURATION_DESCRIPTOR_TYPE )` - -`wTotalLength = 0x27, decimal 39` - -`bNumInterfaces = 0x1, decimal 1` - -`bConfigurationValue = 0x1, decimal 1` - -`iConfiguration = 0x4, decimal 4` - -`bmAttributes = 0xa0 ( USB_CONFIG_BUS_POWERED )` - -`MaxPower = 0x32, decimal 50` - -`-----------------------------` - -`USB_INTERFACE_DESCRIPTOR #0` - -`bLength = 0x9` - -`bDescriptorType = 0x4 ( USB_INTERFACE_DESCRIPTOR_TYPE )` - -`bInterfaceNumber = 0x0` - -`bAlternateSetting = 0x0` - -`bNumEndpoints = 0x3` - -`bInterfaceClass = 0xff` - -`bInterfaceSubClass = 0x0` - -`bInterfaceProtocol = 0x0` - -`bInterface = 0x0` - -`------------------------------` - -`USB_ENDPOINT_DESCRIPTOR for Pipe00` - -`bLength = 0x7` - -`bDescriptorType = 0x5 ( USB_ENDPOINT_DESCRIPTOR_TYPE )` - -`bEndpointAddress= 0x81 ( INPUT )` - -`bmAttributes= 0x3 ( USB_ENDPOINT_TYPE_INTERRUPT )` - -`wMaxPacketSize= 0x49, decimal 73` - -`bInterval = 0x1, decimal 1` - -`------------------------------` - -`USB_ENDPOINT_DESCRIPTOR for Pipe01` - -`bLength = 0x7` - -`bDescriptorType = 0x5 ( USB_ENDPOINT_DESCRIPTOR_TYPE )` - -`bEndpointAddress= 0x6 ( OUTPUT )` - -`bmAttributes= 0x2 ( USB_ENDPOINT_TYPE_BULK )` - -`wMaxPacketSize= 0x200, ` - -`decimal 512 bInterval = 0x0, ` - -`decimal 0` - -`------------------------------` - -`USB_ENDPOINT_DESCRIPTOR for Pipe02` - -`bLength = 0x7` - -`bDescriptorType = 0x5 ( USB_ENDPOINT_DESCRIPTOR_TYPE )` - -`bEndpointAddress= 0x88 ( INPUT )` - -`bmAttributes= 0x2 ( USB_ENDPOINT_TYPE_BULK )` - -`wMaxPacketSize= 0x200, decimal 512` - -`bInterval = 0x0, decimal 0` - -If the device is operating in low speed mode, you will get the following information: - -`===================` - -`USB_CONFIGURATION_DESCRIPTOR` - -`bLength = 0x9, decimal 9` - -`bDescriptorType = 0x2 ( USB_CONFIGURATION_DESCRIPTOR_TYPE )` - -`wTotalLength = 0x27, decimal 39` - -`bNumInterfaces = 0x1, decimal 1` - -`bConfigurationValue = 0x1, decimal 1` - -`iConfiguration = 0x3, decimal 3` - -`bmAttributes = 0xa0 ( USB_CONFIG_BUS_POWERED )` - -`MaxPower = 0x32, decimal 50 ` - -`-----------------------------` - -`USB_INTERFACE_DESCRIPTOR #0` - -`bLength = 0x9` - -`bDescriptorType = 0x4 ( USB_INTERFACE_DESCRIPTOR_TYPE )` - -`bInterfaceNumber = 0x0 bAlternateSetting = 0x0` - -`bNumEndpoints = 0x3` - -`bInterfaceClass = 0xff` - -`bInterfaceSubClass = 0x0` - -`bInterfaceProtocol = 0x0` - -`bInterface = 0x0` - -`------------------------------` - -`USB_ENDPOINT_DESCRIPTOR for Pipe00` - -`bLength = 0x7` - -`bDescriptorType = 0x5 ( USB_ENDPOINT_DESCRIPTOR_TYPE )` - -`bEndpointAddress= 0x81 ( INPUT )` - -`bmAttributes= 0x3 ( USB_ENDPOINT_TYPE_INTERRUPT )` - -`wMaxPacketSize= 0x49, decimal 73` - -`bInterval = 0x1, decimal 1` +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. -`USB_ENDPOINT_DESCRIPTOR for Pipe01` +Build the sample using Visual Studio +------------------------------------ -`bLength = 0x7` +In Visual Studio, on the **Build** menu, choose **Build Solution**. -`bDescriptorType = 0x5 ( USB_ENDPOINT_DESCRIPTOR_TYPE )` +For more information about using Visual Studio to build a driver package, see [Building a Driver](http://msdn.microsoft.com/en-us/library/windows/hardware/ff554644). -`bEndpointAddress= 0x6 ( OUTPUT )` +Locate the built driver package +------------------------------- -`bmAttributes= 0x2 ( USB_ENDPOINT_TYPE_BULK )` +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. -`wMaxPacketSize= 0x40, decimal 64` +Run the sample +-------------- -`bInterval = 0x0, decimal 0` +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. -`USB_ENDPOINT_DESCRIPTOR for Pipe02` +### Automatic deployment (FX2 board) -`bLength = 0x7` +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/). -`bDescriptorType = 0x5 ( USB_ENDPOINT_DESCRIPTOR_TYPE )` +1. Plug in the OSR USB-FX2 board to the target computer. +2. 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**. +3. 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 **Install and Verify**. Click **OK**. +4. On the **Build** menu, choose **Deploy Package** or **Build Solution**. -`bEndpointAddress= 0x88 ( INPUT )` +### Manual deployment (FX2 board) -`bmAttributes= 0x2 ( USB_ENDPOINT_TYPE_BULK )` +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). -`wMaxPacketSize= 0x40, decimal 64` +1. Copy all of the files in your driver package to a folder on the target computer (for example, c:\\umdf2\_fx2). +2. Plug in the OSR USB-FX2 board to the target computer. Open a Command Prompt window and enter **dvmgmt** to open Device Manager. In Device Manager, locate the node for the OSR USB-FX2 board. Right click the node, and choose **Properties**. In the **Details** tab, under **Properties**, choose **Hardware Ids**. Note the hardware IDs listed for your FX2 board. One of these IDs should match one of the IDs in the osrusbfx2um.inf file. For example, Device Manager might show an ID of USB\\VID\_0547&PID\_1002, which matches one of the IDs in the [Microsoft.*xxx*] section of osrusbfx2um.inf. -`bInterval = 0x0, decimal 0 ` +3. On the target computer, open a Command Prompt window as Administrator. Navigate to your driver package folder, and enter this command: -Sample Contents ---------------- + **devcon update osrusbfx2um.inf"***HardwareID***"** -Folder + where *HardwareID* is the hardware ID of your FX2 board. Here is an example: -Description + **devcon update osrusbfx2um.inf "USB\\VID\_0547&PID\_1002"** -usb\\umdf\_fx2\\driver +View the driver for the OSR USB-FX2 board in Device Manager +----------------------------------------------------------- -This directory contains driver code that demonstrates the following functionality: +On the target computer, in your 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 Sample Driver for OSR Fx2 Learning Kit** (for example, this might be under the **Sample Device** node). -- Loads the driver and responds to PnP and Power events. You can install, uninstall, disable, enable, suspend, and resume the system. -- Registers a PnP device interface so that application can open a handle to the device. -- Implements **IPnpCallbackHardware** interface and initializes USB I/O targets in **IPnpCallbackHardware::OnPrepareHardware** method. -- Creates a sequential queue for handling IOCTL requests. -- Adds code to handle the IOCTL to set bar graph display. -- Creates a parallel queue for handling read and write requests. -- Retrieves memory from read and write requests, format the requests, and sends them to a USB target. -- Supports additional IOCTLs to get and set the 7-segment display, get bar graph display, and get config descriptor. -- Sets power policy for the device. -- Adds code to indicate that the device is ready by lighting up the period on 7-segment display. -- Calls **SetupDi** functions to determine the "BusTypeGUID" of the device, and uses impersonation to access resources that only the caller has access to. -- Shows how to implement idle and wake functionality to make the driver the power policy owner (PPO). The sample achieves this using power-managed queues and UMDF DDIs, AssignS0IdleSettings, and AssignSxWakeSettings. -- Demonstrates implementation of a continuous reader. -- Demonstrates the use of impersonation. +In Device Manager, on the **View** menu, choose **Devices by connection**. Locate **UMDF 2.0 Sample Driver for OSR Fx2 Learning Kit** as a child of a USB hub node, which may be contained with the **ACPI x64-based PC** node. -usb\\umdf\_fx2\\exe +Build the sample using MSBuild +------------------------------ -This directory contains a test application that can be used to drive the UMDF driver and FX2 device. This is a modified version of the test application for the KMDF Fx2 driver. +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: -usb\\umdf\_fx2\\deviceMetadata +**msbuild /p:configuration=â€Win8 Release†/p:platform=â€Win32†umdf2\_fx2.sln** -This directory contains the device metadata package for the sample. You must copy the device metadata to the system before installing the device. For information on how to update and deploy device metadata, see [Custom driver access sample](http://go.microsoft.com/fwlink/p/?LinkID=248288). +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/usb/umdf2_fx2/driver/osrusbfx2um.inx b/usb/umdf2_fx2/driver/osrusbfx2um.inx index ff535c155..4f445ae37 100644 --- a/usb/umdf2_fx2/driver/osrusbfx2um.inx +++ b/usb/umdf2_fx2/driver/osrusbfx2um.inx @@ -20,14 +20,14 @@ Signature="$Windows NT$" Class=Sample ClassGuid={78A1C341-4539-11d3-B88D-00C04FAD5171} -Provider=%MSFTUMDF% +Provider=%ProviderName% DriverVer=03/25/2005,0.0.0.1 CatalogFile=wudf.cat [Manufacturer] -%MSFTUMDF%=Microsoft,NT$ARCH$ +%MfgName%=OSR,NT$ARCH$ -[Microsoft.NT$ARCH$] +[OSR.NT$ARCH$] %OsrUsbDeviceName%=OsrUsb_Install, USB\Vid_045e&Pid_94aa&mi_00 %OsrUsbDeviceName%=OsrUsb_Install, USB\VID_0547&PID_1002 @@ -78,8 +78,9 @@ osrusbfx2um.dll ; =================== Generic ================================== [Strings] -MSFTUMDF="Microsoft Internal (WDF:UMDF)" -MediaDescription="Microsoft Sample Driver Installation Media" +ProviderName="TODO-Set-Provider" +MfgName="OSR" +MediaDescription="Sample Driver Installation Media" ClassName="Sample Device" WudfRdDisplayName="Windows Driver Foundation - User-mode Driver Framework Reflector" OsrUsbDeviceName="UMDF v2 Sample Driver for OSR USB Fx2 Learning Kit with NativeUSB" diff --git a/usb/umdf2_fx2/driver/osrusbfx2um.vcxproj b/usb/umdf2_fx2/driver/osrusbfx2um.vcxproj index baa0a4110..b6b1e2538 100644 --- a/usb/umdf2_fx2/driver/osrusbfx2um.vcxproj +++ b/usb/umdf2_fx2/driver/osrusbfx2um.vcxproj @@ -19,12 +19,12 @@ - {6A48DB7E-DC78-493F-9923-DCB9321C7667} + {AC913FF4-1E9D-4666-BA65-BA9C3C6BDC7A} $(MSBuildProjectName) 2 Debug Win32 - {7FB8FEE0-972F-4454-9A05-425221DC87B6} + {C259646B-9566-4D0C-96B5-74D23C92DABF} @@ -202,7 +202,6 @@ - diff --git a/usb/umdf2_fx2/driver/osrusbfx2um.vcxproj.Filters b/usb/umdf2_fx2/driver/osrusbfx2um.vcxproj.Filters index 160a48d95..cf5c627c9 100644 --- a/usb/umdf2_fx2/driver/osrusbfx2um.vcxproj.Filters +++ b/usb/umdf2_fx2/driver/osrusbfx2um.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {21B700F3-2B7F-4332-A55B-C436016AC536} + {043629B9-E054-4FF3-874E-548E7D4FFBB7} h;hpp;hxx;hm;inl;inc;xsd - {B93FCB80-AF61-4C1D-9534-6B6EB29B526A} + {C31DA020-0B3B-4F1A-919E-C366757C9957} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {4C88E4BA-E0F4-45C9-852B-F96E9C9E5883} + {0D5B6280-46CF-448E-ACA9-DF9F20586F0A} inf;inv;inx;mof;mc; - {3893D068-C2BA-4785-B0AE-9EF13A8DE5BB} + {B23BCA35-0435-4D67-8FEB-818995B8239C} @@ -36,9 +36,6 @@ - - Driver Files - Driver Files diff --git a/usb/umdf2_fx2/exe/osrusbfx2.vcxproj b/usb/umdf2_fx2/exe/osrusbfx2.vcxproj index 0a26c683a..8ebd4c2b0 100644 --- a/usb/umdf2_fx2/exe/osrusbfx2.vcxproj +++ b/usb/umdf2_fx2/exe/osrusbfx2.vcxproj @@ -19,11 +19,11 @@ - {FAF22768-F07B-437A-BB6B-4CA77B19D705} + {9D6DB868-3478-4CD3-9C16-CE9E1FD04AA6} $(MSBuildProjectName) Debug Win32 - {BD1E8570-B544-4A6C-BF30-70EBD4456E54} + {71E76691-6085-472A-AEE3-32F302D432A1} @@ -179,7 +179,6 @@ - diff --git a/usb/umdf2_fx2/exe/osrusbfx2.vcxproj.Filters b/usb/umdf2_fx2/exe/osrusbfx2.vcxproj.Filters index f9eac4c7a..189b77cab 100644 --- a/usb/umdf2_fx2/exe/osrusbfx2.vcxproj.Filters +++ b/usb/umdf2_fx2/exe/osrusbfx2.vcxproj.Filters @@ -3,15 +3,15 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {E08AB654-AE1F-42CF-AD59-772321BAD1ED} + {7FC48C4A-E699-452E-9AC1-B87A1398097A} h;hpp;hxx;hm;inl;inc;xsd - {60796E01-8594-4A2D-9395-F4D0D6B07875} + {4AB8E4FC-C777-4E53-B3B1-546910707A69} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {D37B1ED9-9240-47CA-BF91-6FD1F6C8572F} + {9A57DDF7-3E20-42F4-BE75-466CADEFA001} diff --git a/usb/umdf2_fx2/exe/testapp.c b/usb/umdf2_fx2/exe/testapp.c index a7d82915d..fce915b54 100644 --- a/usb/umdf2_fx2/exe/testapp.c +++ b/usb/umdf2_fx2/exe/testapp.c @@ -191,7 +191,7 @@ Return Value: return INVALID_HANDLE_VALUE; } - printf("DeviceName = (%s)\n", completeDeviceName); + printf("DeviceName = (%S)\n", completeDeviceName); if(Synchronous) { hDev = CreateFile(completeDeviceName, diff --git a/usb/umdf2_fx2/umdf2_fx2.sln b/usb/umdf2_fx2/umdf2_fx2.sln index 780c0e7ff..fd2f0eb1e 100644 --- a/usb/umdf2_fx2/umdf2_fx2.sln +++ b/usb/umdf2_fx2/umdf2_fx2.sln @@ -3,13 +3,13 @@ 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", "{CAFE3118-273C-43B1-9895-909D2132640C}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Driver", "Driver", "{3868A978-E3AA-4A0C-B3CA-23BA75D19CF8}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Exe", "Exe", "{E1EEF412-A834-4B7C-B3CB-D22FA4FF52CC}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Exe", "Exe", "{60363E6C-E86C-42ED-8222-ACAC1F71D486}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "osrusbfx2um", "driver\osrusbfx2um.vcxproj", "{6A48DB7E-DC78-493F-9923-DCB9321C7667}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "osrusbfx2um", "driver\osrusbfx2um.vcxproj", "{C25F378C-3D8F-4AF2-AFBB-7D438CBAB326}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "osrusbfx2", "exe\osrusbfx2.vcxproj", "{FAF22768-F07B-437A-BB6B-4CA77B19D705}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "osrusbfx2", "exe\osrusbfx2.vcxproj", "{9D6DB868-3478-4CD3-9C16-CE9E1FD04AA6}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -19,28 +19,28 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {6A48DB7E-DC78-493F-9923-DCB9321C7667}.Debug|Win32.ActiveCfg = Debug|Win32 - {6A48DB7E-DC78-493F-9923-DCB9321C7667}.Debug|Win32.Build.0 = Debug|Win32 - {6A48DB7E-DC78-493F-9923-DCB9321C7667}.Release|Win32.ActiveCfg = Release|Win32 - {6A48DB7E-DC78-493F-9923-DCB9321C7667}.Release|Win32.Build.0 = Release|Win32 - {6A48DB7E-DC78-493F-9923-DCB9321C7667}.Debug|x64.ActiveCfg = Debug|x64 - {6A48DB7E-DC78-493F-9923-DCB9321C7667}.Debug|x64.Build.0 = Debug|x64 - {6A48DB7E-DC78-493F-9923-DCB9321C7667}.Release|x64.ActiveCfg = Release|x64 - {6A48DB7E-DC78-493F-9923-DCB9321C7667}.Release|x64.Build.0 = Release|x64 - {FAF22768-F07B-437A-BB6B-4CA77B19D705}.Debug|Win32.ActiveCfg = Debug|Win32 - {FAF22768-F07B-437A-BB6B-4CA77B19D705}.Debug|Win32.Build.0 = Debug|Win32 - {FAF22768-F07B-437A-BB6B-4CA77B19D705}.Release|Win32.ActiveCfg = Release|Win32 - {FAF22768-F07B-437A-BB6B-4CA77B19D705}.Release|Win32.Build.0 = Release|Win32 - {FAF22768-F07B-437A-BB6B-4CA77B19D705}.Debug|x64.ActiveCfg = Debug|x64 - {FAF22768-F07B-437A-BB6B-4CA77B19D705}.Debug|x64.Build.0 = Debug|x64 - {FAF22768-F07B-437A-BB6B-4CA77B19D705}.Release|x64.ActiveCfg = Release|x64 - {FAF22768-F07B-437A-BB6B-4CA77B19D705}.Release|x64.Build.0 = Release|x64 + {C25F378C-3D8F-4AF2-AFBB-7D438CBAB326}.Debug|Win32.ActiveCfg = Debug|Win32 + {C25F378C-3D8F-4AF2-AFBB-7D438CBAB326}.Debug|Win32.Build.0 = Debug|Win32 + {C25F378C-3D8F-4AF2-AFBB-7D438CBAB326}.Release|Win32.ActiveCfg = Release|Win32 + {C25F378C-3D8F-4AF2-AFBB-7D438CBAB326}.Release|Win32.Build.0 = Release|Win32 + {C25F378C-3D8F-4AF2-AFBB-7D438CBAB326}.Debug|x64.ActiveCfg = Debug|x64 + {C25F378C-3D8F-4AF2-AFBB-7D438CBAB326}.Debug|x64.Build.0 = Debug|x64 + {C25F378C-3D8F-4AF2-AFBB-7D438CBAB326}.Release|x64.ActiveCfg = Release|x64 + {C25F378C-3D8F-4AF2-AFBB-7D438CBAB326}.Release|x64.Build.0 = Release|x64 + {9D6DB868-3478-4CD3-9C16-CE9E1FD04AA6}.Debug|Win32.ActiveCfg = Debug|Win32 + {9D6DB868-3478-4CD3-9C16-CE9E1FD04AA6}.Debug|Win32.Build.0 = Debug|Win32 + {9D6DB868-3478-4CD3-9C16-CE9E1FD04AA6}.Release|Win32.ActiveCfg = Release|Win32 + {9D6DB868-3478-4CD3-9C16-CE9E1FD04AA6}.Release|Win32.Build.0 = Release|Win32 + {9D6DB868-3478-4CD3-9C16-CE9E1FD04AA6}.Debug|x64.ActiveCfg = Debug|x64 + {9D6DB868-3478-4CD3-9C16-CE9E1FD04AA6}.Debug|x64.Build.0 = Debug|x64 + {9D6DB868-3478-4CD3-9C16-CE9E1FD04AA6}.Release|x64.ActiveCfg = Release|x64 + {9D6DB868-3478-4CD3-9C16-CE9E1FD04AA6}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {6A48DB7E-DC78-493F-9923-DCB9321C7667} = {CAFE3118-273C-43B1-9895-909D2132640C} - {FAF22768-F07B-437A-BB6B-4CA77B19D705} = {E1EEF412-A834-4B7C-B3CB-D22FA4FF52CC} + {C25F378C-3D8F-4AF2-AFBB-7D438CBAB326} = {3868A978-E3AA-4A0C-B3CA-23BA75D19CF8} + {9D6DB868-3478-4CD3-9C16-CE9E1FD04AA6} = {60363E6C-E86C-42ED-8222-ACAC1F71D486} EndGlobalSection EndGlobal diff --git a/usb/umdf_filter_kmdf/Package/package.VcxProj b/usb/umdf_filter_kmdf/Package/package.VcxProj new file mode 100644 index 000000000..4ca7636ef --- /dev/null +++ b/usb/umdf_filter_kmdf/Package/package.VcxProj @@ -0,0 +1,87 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + + {6ABDB82A-B2BA-4EE3-A4CC-C0B00B7B5220} + + + {BE14DB05-36A7-4ECE-88E1-FB8157CA568E} + + + + WindowsKernelModeDriver10.0 + Utility + Package + true + Debug + + + + {2BA68E64-0EBB-409C-937F-FDAC3FB7A39D} + {9515D06E-6666-40E0-BC9B-4D353B06AF3B} + $(MSBuildProjectName) + + + Windows10 + true + + + Windows10 + false + + + Windows10 + true + + + Windows10 + false + + + + + + + + + + + DbgengKernelDebugger + False + None + + + + + + %PathToInf% + False + False + True + + 133563 + + + + + + + \ No newline at end of file diff --git a/usb/umdf_filter_kmdf/Package/package.VcxProj.Filters b/usb/umdf_filter_kmdf/Package/package.VcxProj.Filters new file mode 100644 index 000000000..9aaef8318 --- /dev/null +++ b/usb/umdf_filter_kmdf/Package/package.VcxProj.Filters @@ -0,0 +1,21 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {86971831-99C4-48F4-BA75-63AFA1FF01D6} + + + h;hpp;hxx;hm;inl;inc;xsd + {49BD12DD-8C0A-47ED-9D86-487828F8E2E1} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {A74FBF55-5FC8-4073-89C5-B13BFE528197} + + + inf;inv;inx;mof;mc; + {71D5E423-057B-48A2-90E2-66C66E3CC18C} + + + \ No newline at end of file diff --git a/usb/umdf_filter_kmdf/ReadMe.md b/usb/umdf_filter_kmdf/ReadMe.md new file mode 100644 index 000000000..3906d7516 --- /dev/null +++ b/usb/umdf_filter_kmdf/ReadMe.md @@ -0,0 +1,78 @@ +Sample UMDF Filter above KMDF Function Driver for OSR USB-FX2 (UMDF Version 1) +============================================================================== + +The umdf\_filter\_kmdf sample demonstrates how to load a UMDF filter driver as an upper filter driver above the kmdf\_fx2 sample driver. + +The sample includes Event Tracing for Windows (ETW) tracing support, and is written for the OSR USB-FX2 Learning Kit. The specification for the device is at . + + + +Build the sample +---------------- + +The default Solution build configuration is Windows 8.1 Debug and Win32. You can change the default configuration to build for Windows 8 or Windows 7 version of the operating system. + +**To select a configuration and build a driver** + +1. Open the driver project or solution in Visual Studio 2013 (find *filtername*.sln or *filtername*.vcxproj). +2. Right-click the solution in the **Solutions Explorer** and select **Configuration Manager**. +3. From the **Configuration Manager**, select the **Active Solution Configuration** (for example, Windows 8.1 Debug or Windows 8.1 Release) and the **Active Solution Platform** (for example, Win32) that correspond to the type of build you are interested in. +4. From the **Build** menu, click **Build Solution** (Ctrl+Shift+B). + +Overview +-------- + +Here is the overview of the device: + +- The device is based on the development board supplied with the Cypress EZ-USB FX2 Development Kit (CY3681). +- It contains 1 interface and 3 endpoints (Interrupt IN, Bulk Out, Bulk IN). +- Firmware supports vendor commands to query or set LED Bar graph display and 7-segment LED display, and to query toggle switch states. +- Interrupt Endpoint: + - Sends an 8-bit value that represents the state of the switches. + - Sent on startup, resume from suspend, and whenever the switch pack setting changes. + - Firmware does not de-bounce the switch pack. + - One switch change can result in multiple bytes being sent. + - Bits are in the reverse order of the labels on the pack (for example, bit 0x80 is labeled 1 on the pack). +- Bulk Endpoints are configured for loopback: + - The device moves data from IN endpoint to OUT endpoint. + - The device does not change the values of the data it receives nor does it internally create any data. + - Endpoints are always double buffered. + - Maximum packet size depends on speed (64 full speed, 512 high speed). +- ETW events: + - Included osrusbfx2.man, which describes events added. + - Three events are targeted to the event log: + - Failure during the add device routine. + - Failure to start the OSR device on a USB 1.1 controller. + - Invocation of the “re-enumerate device†IOCTL. + - Read/write start/stop events can be used to measure the time taken. + +Testing the driver +------------------ + +You can test this sample either by using the [Custom driver access](http://go.microsoft.com/fwlink/p/?LinkID=248288) sample application, or by using the osrusbfx2.exe test application. For information on how to build and use the osrusbfx2.exe application, see the test instructions for the [kmdf\_fx2](http://msdn.microsoft.com/en-us/library/windows/hardware/) sample. + +Sample Contents +--------------- + + ++++ + + + + + + + + + + + +
Folder +Description
usb\umdf_filter_kmdf\kmdf_driver +This directory contains source code for the kmdf_fx2 sample driver.usb\umdf_filter_kmdf\umdf_filter +This directory contains the UMDF filter driver.
+ + diff --git a/usb/umdf_filter_kmdf/inc/WUDFOsrUsbPublic.h b/usb/umdf_filter_kmdf/inc/WUDFOsrUsbPublic.h new file mode 100644 index 000000000..6681fa146 --- /dev/null +++ b/usb/umdf_filter_kmdf/inc/WUDFOsrUsbPublic.h @@ -0,0 +1,32 @@ +/*++ + +Copyright (c) Microsoft Corporation, All Rights Reserved + +Module Name: + + WUDFOsrUsbPublic.h + +Abstract: + + This module contains the common declarations shared by driver + and user applications for the UMDF OSR device sample. + + Note that this driver does NOT use the same device interface GUID + as the KMDF OSR USB sample. + +Environment: + + user and kernel + +--*/ + +#pragma once + +// +// Define an Interface Guid so that app can find the device and talk to it. +// + +// {573E8C73-0CB4-4471-A1BF-FAB26C31D384} +DEFINE_GUID(GUID_DEVINTERFACE_OSRUSBFX2, + 0x573e8c73, 0xcb4, 0x4471, 0xa1, 0xbf, 0xfa, 0xb2, 0x6c, 0x31, 0xd3, 0x84); + diff --git a/usb/umdf_filter_kmdf/inc/list.h b/usb/umdf_filter_kmdf/inc/list.h new file mode 100644 index 000000000..38d0b1e90 --- /dev/null +++ b/usb/umdf_filter_kmdf/inc/list.h @@ -0,0 +1,77 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved + +Module Name: + + list.h + +Abstract: + + This module contains doubly linked list macros + +Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + +#pragma once + + +FORCEINLINE +VOID +InitializeListHead( + IN PLIST_ENTRY ListHead + ) +{ + ListHead->Flink = ListHead->Blink = 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 +VOID +InsertHeadList( + IN PLIST_ENTRY ListHead, + IN PLIST_ENTRY Entry + ) +{ + PLIST_ENTRY Flink; + + Flink = ListHead->Flink; + Entry->Flink = Flink; + Entry->Blink = ListHead; + Flink->Blink = Entry; + ListHead->Flink = Entry; +} + +FORCEINLINE +VOID +InsertTailList( + IN PLIST_ENTRY ListHead, + IN PLIST_ENTRY Entry + ) +{ + PLIST_ENTRY Blink; + + Blink = ListHead->Blink; + Entry->Flink = ListHead; + Entry->Blink = Blink; + Blink->Flink = Entry; + ListHead->Blink = Entry; +} diff --git a/usb/umdf_filter_kmdf/inc/public.h b/usb/umdf_filter_kmdf/inc/public.h new file mode 100644 index 000000000..22f6cb6d1 --- /dev/null +++ b/usb/umdf_filter_kmdf/inc/public.h @@ -0,0 +1,217 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + public.h + +Abstract: + + Public definitions for the OSR_FX2 device operations. + +Environment: + + User & Kernel mode + +--*/ + +#ifndef _PUBLIC_H +#define _PUBLIC_H + +#include + +#include "WudfOsrUsbPublic.h" + + +// +// Define the structures that will be used by the IOCTL +// interface to the driver +// + +// +// BAR_GRAPH_STATE +// +// BAR_GRAPH_STATE is a bit field structure with each +// bit corresponding to one of the bar graph on the +// OSRFX2 Development Board +// +#include + +#pragma warning( push ) +#pragma warning( disable : 4201 ) // nameless struct/union +#pragma warning( disable : 4214 ) // bit-field type other than int + +typedef struct _BAR_GRAPH_STATE { + + union { + + struct { + // + // Individual bars starting from the + // top of the stack of bars + // + // NOTE: There are actually 10 bars, + // but the very top two do not light + // and are not counted here + // + UCHAR Bar1 : 1; + UCHAR Bar2 : 1; + UCHAR Bar3 : 1; + UCHAR Bar4 : 1; + UCHAR Bar5 : 1; + UCHAR Bar6 : 1; + UCHAR Bar7 : 1; + UCHAR Bar8 : 1; + }; + + // + // The state of all the bar graph as a single + // UCHAR + // + UCHAR BarsAsUChar; + + }; + +}BAR_GRAPH_STATE, *PBAR_GRAPH_STATE; + +// +// SWITCH_STATE +// +// SWITCH_STATE is a bit field structure with each +// bit corresponding to one of the switches on the +// OSRFX2 Development Board +// +typedef struct _SWITCH_STATE { + + union { + struct { + // + // Individual switches starting from the + // left of the set of switches + // + UCHAR Switch1 : 1; + UCHAR Switch2 : 1; + UCHAR Switch3 : 1; + UCHAR Switch4 : 1; + UCHAR Switch5 : 1; + UCHAR Switch6 : 1; + UCHAR Switch7 : 1; + UCHAR Switch8 : 1; + }; + + // + // The state of all the switches as a single + // UCHAR + // + UCHAR SwitchesAsUChar; + + }; + + +}SWITCH_STATE, *PSWITCH_STATE; + +// +// Seven segment display bit values. +// + +// +// Undefine conflicting MFC constant +// +#undef SS_CENTER +#undef SS_LEFT +#undef SS_RIGHT + +#define SS_TOP 0x01 +#define SS_TOP_LEFT 0x40 +#define SS_TOP_RIGHT 0x02 +#define SS_CENTER 0x20 +#define SS_BOTTOM_LEFT 0x10 +#define SS_BOTTOM_RIGHT 0x04 +#define SS_BOTTOM 0x80 +#define SS_DOT 0x08 + +// +// FILE_PLAYBACK +// +// FILE_PLAYBACK structure contains the parameters for the PLAY_FILE I/O Control. +// + +typedef struct _FILE_PLAYBACK +{ + // + // The delay between changes in the display, in milliseconds. + // + + USHORT Delay; + + // + // The data file path. + // + + WCHAR Path[1]; +} FILE_PLAYBACK, *PFILE_PLAYBACK; + +#include + +#define IOCTL_INDEX 0x800 +#define FILE_DEVICE_OSRUSBFX2 0x65500 + +#define IOCTL_OSRUSBFX2_GET_CONFIG_DESCRIPTOR CTL_CODE(FILE_DEVICE_OSRUSBFX2, \ + IOCTL_INDEX, \ + METHOD_BUFFERED, \ + FILE_READ_ACCESS) + +#define IOCTL_OSRUSBFX2_RESET_DEVICE CTL_CODE(FILE_DEVICE_OSRUSBFX2, \ + IOCTL_INDEX + 1, \ + METHOD_BUFFERED, \ + FILE_WRITE_ACCESS) + +#define IOCTL_OSRUSBFX2_REENUMERATE_DEVICE CTL_CODE(FILE_DEVICE_OSRUSBFX2, \ + IOCTL_INDEX + 3, \ + METHOD_BUFFERED, \ + FILE_WRITE_ACCESS) + +#define IOCTL_OSRUSBFX2_GET_BAR_GRAPH_DISPLAY CTL_CODE(FILE_DEVICE_OSRUSBFX2,\ + IOCTL_INDEX + 4, \ + METHOD_BUFFERED, \ + FILE_READ_ACCESS) + + +#define IOCTL_OSRUSBFX2_SET_BAR_GRAPH_DISPLAY CTL_CODE(FILE_DEVICE_OSRUSBFX2,\ + IOCTL_INDEX + 5, \ + METHOD_BUFFERED, \ + FILE_WRITE_ACCESS) + + +#define IOCTL_OSRUSBFX2_READ_SWITCHES CTL_CODE(FILE_DEVICE_OSRUSBFX2, \ + IOCTL_INDEX + 6, \ + METHOD_BUFFERED, \ + FILE_READ_ACCESS) + + +#define IOCTL_OSRUSBFX2_GET_7_SEGMENT_DISPLAY CTL_CODE(FILE_DEVICE_OSRUSBFX2, \ + IOCTL_INDEX + 7, \ + METHOD_BUFFERED, \ + FILE_READ_ACCESS) + + +#define IOCTL_OSRUSBFX2_SET_7_SEGMENT_DISPLAY CTL_CODE(FILE_DEVICE_OSRUSBFX2, \ + IOCTL_INDEX + 8, \ + METHOD_BUFFERED, \ + FILE_WRITE_ACCESS) + +#define IOCTL_OSRUSBFX2_GET_INTERRUPT_MESSAGE CTL_CODE(FILE_DEVICE_OSRUSBFX2,\ + IOCTL_INDEX + 9, \ + METHOD_OUT_DIRECT, \ + FILE_READ_ACCESS) + +#define IOCTL_OSRUSBFX2_PLAY_FILE CTL_CODE(FILE_DEVICE_OSRUSBFX2, \ + IOCTL_INDEX + 10, \ + METHOD_BUFFERED, \ + FILE_WRITE_ACCESS) + +#pragma warning(pop) + +#endif + diff --git a/usb/umdf_filter_kmdf/inc/usb_hw.h b/usb/umdf_filter_kmdf/inc/usb_hw.h new file mode 100644 index 000000000..d6e983f1a --- /dev/null +++ b/usb/umdf_filter_kmdf/inc/usb_hw.h @@ -0,0 +1,233 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + Usb.h + +Abstract: + + Contains prototypes for interfacing with a USB connected device. These + are copied from the KMDF WDFUSB.H header file (but with the WDF specific + portions removed) + +Environment: + + kernel mode only + +--*/ + +#pragma once + +typedef enum _WINUSB_BMREQUEST_DIRECTION { + BmRequestHostToDevice = BMREQUEST_HOST_TO_DEVICE, + BmRequestDeviceToHost = BMREQUEST_DEVICE_TO_HOST, +} WINUSB_BMREQUEST_DIRECTION; + +typedef enum _WINUSB_BMREQUEST_TYPE { + BmRequestStandard = BMREQUEST_STANDARD, + BmRequestClass = BMREQUEST_CLASS, + BmRequestVendor = BMREQUEST_VENDOR, +} WINUSB_BMREQUEST_TYPE; + +typedef enum _WINUSB_BMREQUEST_RECIPIENT { + BmRequestToDevice = BMREQUEST_TO_DEVICE, + BmRequestToInterface = BMREQUEST_TO_INTERFACE, + BmRequestToEndpoint = BMREQUEST_TO_ENDPOINT, + BmRequestToOther = BMREQUEST_TO_OTHER, +} WINUSB_BMREQUEST_RECIPIENT; + +typedef enum _WINUSB_DEVICE_TRAITS { + WINUSB_DEVICE_TRAIT_SELF_POWERED = 0x00000001, + WINUSB_DEVICE_TRAIT_REMOTE_WAKE_CAPABLE = 0x00000002, + WINUSB_DEVICE_TRAIT_AT_HIGH_SPEED = 0x00000004, +} WINUSB_DEVICE_TRAITS; + +typedef enum _WdfUsbTargetDeviceSelectInterfaceType { + WdfUsbTargetDeviceSelectInterfaceTypeInterface = 0x10, + WdfUsbTargetDeviceSelectInterfaceTypeUrb = 0x11, +} WdfUsbTargetDeviceSelectInterfaceType; + + + +typedef union _WINUSB_CONTROL_SETUP_PACKET { + struct { + union { + #pragma warning(disable:4214) // bit field types other than int + struct { + // + // Valid values are BMREQUEST_TO_DEVICE, BMREQUEST_TO_INTERFACE, + // BMREQUEST_TO_ENDPOINT, BMREQUEST_TO_OTHER + // + BYTE Recipient:2; + + BYTE Reserved:3; + + // + // Valid values are BMREQUEST_STANDARD, BMREQUEST_CLASS, + // BMREQUEST_VENDOR + // + BYTE Type:2; + + // + // Valid values are BMREQUEST_HOST_TO_DEVICE, + // BMREQUEST_DEVICE_TO_HOST + // + BYTE Dir:1; + } Request; + #pragma warning(default:4214) // bit field types other than int + BYTE Byte; + } bm; + + BYTE bRequest; + + union { + struct { + BYTE LowByte; + BYTE HiByte; + } Bytes; + USHORT Value; + } wValue; + + union { + struct { + BYTE LowByte; + BYTE HiByte; + } Bytes; + USHORT Value; + } wIndex; + + USHORT wLength; + } Packet; + + struct { + BYTE Bytes[8]; + } Generic; + + WINUSB_SETUP_PACKET WinUsb; + +} WINUSB_CONTROL_SETUP_PACKET, *PWINUSB_CONTROL_SETUP_PACKET; + +VOID +FORCEINLINE +WINUSB_CONTROL_SETUP_PACKET_INIT( + PWINUSB_CONTROL_SETUP_PACKET Packet, + WINUSB_BMREQUEST_DIRECTION Direction, + WINUSB_BMREQUEST_RECIPIENT Recipient, + BYTE Request, + USHORT Value, + USHORT Index + ) +{ + RtlZeroMemory(Packet, sizeof(WINUSB_CONTROL_SETUP_PACKET)); + + Packet->Packet.bm.Request.Dir = (BYTE) Direction; + Packet->Packet.bm.Request.Type = (BYTE) BmRequestStandard; + Packet->Packet.bm.Request.Recipient = (BYTE) Recipient; + + Packet->Packet.bRequest = Request; + Packet->Packet.wValue.Value = Value; + Packet->Packet.wIndex.Value = Index; + + // Packet->Packet.wLength will be set by the formatting function +} + +VOID +FORCEINLINE +WINUSB_CONTROL_SETUP_PACKET_INIT_CLASS( + PWINUSB_CONTROL_SETUP_PACKET Packet, + WINUSB_BMREQUEST_DIRECTION Direction, + WINUSB_BMREQUEST_RECIPIENT Recipient, + BYTE Request, + USHORT Value, + USHORT Index + ) +{ + RtlZeroMemory(Packet, sizeof(WINUSB_CONTROL_SETUP_PACKET)); + + Packet->Packet.bm.Request.Dir = (BYTE) Direction; + Packet->Packet.bm.Request.Type = (BYTE) BmRequestClass; + Packet->Packet.bm.Request.Recipient = (BYTE) Recipient; + + Packet->Packet.bRequest = Request; + Packet->Packet.wValue.Value = Value; + Packet->Packet.wIndex.Value = Index; + + // Packet->Packet.wLength will be set by the formatting function +} + +VOID +FORCEINLINE +WINUSB_CONTROL_SETUP_PACKET_INIT_VENDOR( + PWINUSB_CONTROL_SETUP_PACKET Packet, + WINUSB_BMREQUEST_DIRECTION Direction, + WINUSB_BMREQUEST_RECIPIENT Recipient, + BYTE Request, + USHORT Value, + USHORT Index + ) +{ + RtlZeroMemory(Packet, sizeof(WINUSB_CONTROL_SETUP_PACKET)); + + Packet->Packet.bm.Request.Dir = (BYTE) Direction; + Packet->Packet.bm.Request.Type = (BYTE) BmRequestVendor; + Packet->Packet.bm.Request.Recipient = (BYTE) Recipient; + + Packet->Packet.bRequest = Request; + Packet->Packet.wValue.Value = Value; + Packet->Packet.wIndex.Value = Index; + + // Packet->Packet.wLength will be set by the formatting function +} + +VOID +FORCEINLINE +WINUSB_CONTROL_SETUP_PACKET_INIT_FEATURE( + PWINUSB_CONTROL_SETUP_PACKET Packet, + WINUSB_BMREQUEST_RECIPIENT BmRequestRecipient, + USHORT FeatureSelector, + USHORT Index, + BOOLEAN SetFeature + ) +{ + RtlZeroMemory(Packet, sizeof(WINUSB_CONTROL_SETUP_PACKET)); + + Packet->Packet.bm.Request.Dir = (BYTE) BmRequestHostToDevice; + Packet->Packet.bm.Request.Type = (BYTE) BmRequestStandard; + Packet->Packet.bm.Request.Recipient = (BYTE) BmRequestRecipient; + + if (SetFeature) { + Packet->Packet.bRequest = USB_REQUEST_SET_FEATURE; + } + else { + Packet->Packet.bRequest = USB_REQUEST_CLEAR_FEATURE; + } + + Packet->Packet.wValue.Value = FeatureSelector; + Packet->Packet.wIndex.Value = Index; + + // Packet->Packet.wLength will be set by the formatting function +} + +VOID +FORCEINLINE +WINUSB_CONTROL_SETUP_PACKET_INIT_GET_STATUS( + PWINUSB_CONTROL_SETUP_PACKET Packet, + WINUSB_BMREQUEST_RECIPIENT BmRequestRecipient, + USHORT Index + ) +{ + RtlZeroMemory(Packet, sizeof(WINUSB_CONTROL_SETUP_PACKET)); + + Packet->Packet.bm.Request.Dir = (BYTE) BmRequestDeviceToHost; + Packet->Packet.bm.Request.Type = (BYTE) BmRequestStandard; + Packet->Packet.bm.Request.Recipient = (BYTE) BmRequestRecipient; + + Packet->Packet.bRequest = USB_REQUEST_GET_STATUS; + Packet->Packet.wIndex.Value = Index; + Packet->Packet.wValue.Value = 0; + + // Packet->Packet.wLength will be set by the formatting function +} + diff --git a/usb/umdf_filter_kmdf/kmdf_driver/Device.c b/usb/umdf_filter_kmdf/kmdf_driver/Device.c new file mode 100644 index 000000000..87ead8570 --- /dev/null +++ b/usb/umdf_filter_kmdf/kmdf_driver/Device.c @@ -0,0 +1,1051 @@ +/*++ + +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: + + USB device driver for OSR USB-FX2 Learning Kit + +Environment: + + Kernel mode only + + +--*/ + +#include +#include + +#if defined(EVENT_TRACING) +#include "device.tmh" +#endif + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, OsrFxEvtDeviceAdd) +#pragma alloc_text(PAGE, OsrFxEvtDevicePrepareHardware) +#pragma alloc_text(PAGE, OsrFxEvtDeviceD0Exit) +#pragma alloc_text(PAGE, SelectInterfaces) +#pragma alloc_text(PAGE, OsrFxSetPowerPolicy) +#pragma alloc_text(PAGE, OsrFxReadFdoRegistryKeyValue) +#pragma alloc_text(PAGE, GetDeviceEventLoggingNames) +#pragma alloc_text(PAGE, OsrFxValidateConfigurationDescriptor) +#endif + + +NTSTATUS +OsrFxEvtDeviceAdd( + WDFDRIVER Driver, + 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. All the software resources + should be allocated in this callback. + +Arguments: + + Driver - Handle to a framework driver object created in DriverEntry + + DeviceInit - Pointer to a framework-allocated WDFDEVICE_INIT structure. + +Return Value: + + NTSTATUS + +--*/ +{ + WDF_PNPPOWER_EVENT_CALLBACKS pnpPowerCallbacks; + WDF_OBJECT_ATTRIBUTES attributes; + NTSTATUS status; + WDFDEVICE device; + WDF_DEVICE_PNP_CAPABILITIES pnpCaps; + WDF_IO_QUEUE_CONFIG ioQueueConfig; + PDEVICE_CONTEXT pDevContext; + WDFQUEUE queue; + GUID activity; + UNICODE_STRING symbolicLinkName; + WDFSTRING symbolicLinkString; + DEVPROP_BOOLEAN isRestricted; + + UNREFERENCED_PARAMETER(Driver); + + PAGED_CODE(); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP,"--> OsrFxEvtDeviceAdd routine\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); + // + // For usb devices, PrepareHardware callback is the to place select the + // interface and configure the device. + // + pnpPowerCallbacks.EvtDevicePrepareHardware = OsrFxEvtDevicePrepareHardware; + + // + // These two callbacks start and stop the wdfusb pipe continuous reader + // as we go in and out of the D0-working state. + // + + pnpPowerCallbacks.EvtDeviceD0Entry = OsrFxEvtDeviceD0Entry; + pnpPowerCallbacks.EvtDeviceD0Exit = OsrFxEvtDeviceD0Exit; + pnpPowerCallbacks.EvtDeviceSelfManagedIoFlush = OsrFxEvtDeviceSelfManagedIoFlush; + + WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpPowerCallbacks); + + WdfDeviceInitSetIoType(DeviceInit, WdfDeviceIoBuffered); + + // + // Now specify the size of device extension where we track per device + // context.DeviceInit is completely initialized. So call the framework + // to create the device and attach it to the lower stack. + // + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, DEVICE_CONTEXT); + + status = WdfDeviceCreate(&DeviceInit, &attributes, &device); + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "WdfDeviceCreate failed with Status code %!STATUS!\n", status); + return status; + } + + // + // Setup the activity ID so that we can log events using it. + // + + activity = DeviceToActivityId(device); + + // + // Get the DeviceObject context by using accessor function specified in + // the WDF_DECLARE_CONTEXT_TYPE_WITH_NAME macro for DEVICE_CONTEXT. + // + pDevContext = GetDeviceContext(device); + + // + // Get the device's friendly name and location so that we can use it in + // error logging. If this fails then it will setup dummy strings. + // + + GetDeviceEventLoggingNames(device); + + // + // Tell the framework to set the SurpriseRemovalOK in the DeviceCaps so + // that you don't get the popup in usermode when you surprise remove the device. + // + WDF_DEVICE_PNP_CAPABILITIES_INIT(&pnpCaps); + pnpCaps.SurpriseRemovalOK = WdfTrue; + + WdfDeviceSetPnpCapabilities(device, &pnpCaps); + + // + // Create a parallel default queue and register an event callback to + // receive ioctl requests. We will create separate queues for + // handling read and write requests. All other requests will be + // completed with error status automatically by the framework. + // + WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&ioQueueConfig, + WdfIoQueueDispatchParallel); + + ioQueueConfig.EvtIoDeviceControl = OsrFxEvtIoDeviceControl; + + // + // 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(ioQueueConfig.EvtIoStop != 0); + status = WdfIoQueueCreate(device, + &ioQueueConfig, + WDF_NO_OBJECT_ATTRIBUTES, + &queue);// pointer to default queue + __analysis_assume(ioQueueConfig.EvtIoStop == 0); + + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "WdfIoQueueCreate failed %!STATUS!\n", status); + goto Error; + } + + // + // We will create a separate sequential queue and configure it + // to receive read requests. We also need to register a EvtIoStop + // handler so that we can acknowledge requests that are pending + // at the target driver. + // + WDF_IO_QUEUE_CONFIG_INIT(&ioQueueConfig, WdfIoQueueDispatchSequential); + + ioQueueConfig.EvtIoRead = OsrFxEvtIoRead; + ioQueueConfig.EvtIoStop = OsrFxEvtIoStop; + + status = WdfIoQueueCreate( + device, + &ioQueueConfig, + WDF_NO_OBJECT_ATTRIBUTES, + &queue // queue handle + ); + + if (!NT_SUCCESS (status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "WdfIoQueueCreate failed 0x%x\n", status); + goto Error; + } + + status = WdfDeviceConfigureRequestDispatching( + device, + queue, + WdfRequestTypeRead); + + if(!NT_SUCCESS (status)){ + NT_ASSERT(NT_SUCCESS(status)); + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "WdfDeviceConfigureRequestDispatching failed 0x%x\n", status); + goto Error; + } + + + // + // We will create another sequential queue and configure it + // to receive write requests. + // + WDF_IO_QUEUE_CONFIG_INIT(&ioQueueConfig, WdfIoQueueDispatchSequential); + + ioQueueConfig.EvtIoWrite = OsrFxEvtIoWrite; + ioQueueConfig.EvtIoStop = OsrFxEvtIoStop; + + status = WdfIoQueueCreate( + device, + &ioQueueConfig, + WDF_NO_OBJECT_ATTRIBUTES, + &queue // queue handle + ); + + if (!NT_SUCCESS (status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "WdfIoQueueCreate failed 0x%x\n", status); + goto Error; + } + + status = WdfDeviceConfigureRequestDispatching( + device, + queue, + WdfRequestTypeWrite); + + if(!NT_SUCCESS (status)){ + NT_ASSERT(NT_SUCCESS(status)); + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "WdfDeviceConfigureRequestDispatching failed 0x%x\n", status); + goto Error; + } + + // + // Register a manual I/O queue for handling Interrupt Message Read Requests. + // This queue will be used for storing Requests that need to wait for an + // interrupt to occur before they can be completed. + // + WDF_IO_QUEUE_CONFIG_INIT(&ioQueueConfig, WdfIoQueueDispatchManual); + + // + // This queue is used for requests that dont directly access the device. The + // requests in this queue are serviced only when the device is in a fully + // powered state and sends an interrupt. So we can use a non-power managed + // queue to park the requests since we dont care whether the device is idle + // or fully powered up. + // + ioQueueConfig.PowerManaged = WdfFalse; + + status = WdfIoQueueCreate(device, + &ioQueueConfig, + WDF_NO_OBJECT_ATTRIBUTES, + &pDevContext->InterruptMsgQueue + ); + + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "WdfIoQueueCreate failed 0x%x\n", status); + goto Error; + } + + // + // Register a device interface so that app can find our device and talk to it. + // + status = WdfDeviceCreateDeviceInterface(device, + (LPGUID) &GUID_DEVINTERFACE_OSRUSBFX2, + NULL); // Reference String + + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "WdfDeviceCreateDeviceInterface failed %!STATUS!\n", status); + goto Error; + } + + // + // Create the lock that we use to serialize calls to ResetDevice(). As an + // alternative to using a WDFWAITLOCK to serialize the calls, a sequential + // WDFQUEUE can be created and reset IOCTLs would be forwarded to it. + // + WDF_OBJECT_ATTRIBUTES_INIT(&attributes); + attributes.ParentObject = device; + + status = WdfWaitLockCreate(&attributes, &pDevContext->ResetDeviceWaitLock); + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "WdfWaitLockCreate failed %!STATUS!\n", status); + goto Error; + } + + // + // Get the string for the device interface and set the restricted + // property on it to allow applications bound with device metadata + // to access the interface. + // + if (g_pIoSetDeviceInterfacePropertyData != NULL) { + + status = WdfStringCreate(NULL, + WDF_NO_OBJECT_ATTRIBUTES, + &symbolicLinkString); + + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "WdfStringCreate failed %!STATUS!\n", status); + goto Error; + } + + status = WdfDeviceRetrieveDeviceInterfaceString(device, + (LPGUID) &GUID_DEVINTERFACE_OSRUSBFX2, + NULL, + symbolicLinkString); + + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "WdfDeviceRetrieveDeviceInterfaceString failed %!STATUS!\n", status); + goto Error; + } + + WdfStringGetUnicodeString(symbolicLinkString, &symbolicLinkName); + + isRestricted = DEVPROP_TRUE; + + status = g_pIoSetDeviceInterfacePropertyData(&symbolicLinkName, + &DEVPKEY_DeviceInterface_Restricted, + 0, + 0, + DEVPROP_TYPE_BOOLEAN, + sizeof(isRestricted), + &isRestricted ); + + WdfObjectDelete(symbolicLinkString); + + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "IoSetDeviceInterfacePropertyData failed %!STATUS!\n", status); + goto Error; + } + } + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "<-- OsrFxEvtDeviceAdd\n"); + + return status; + +Error: + + // + // Log fail to add device to the event log + // + EventWriteFailAddDevice(&activity, + pDevContext->DeviceName, + pDevContext->Location, + status); + + return status; +} + +NTSTATUS +OsrFxEvtDevicePrepareHardware( + WDFDEVICE Device, + WDFCMRESLIST ResourceList, + WDFCMRESLIST ResourceListTranslated + ) +/*++ + +Routine Description: + + In this callback, the driver does whatever is necessary to make the + hardware ready to use. In the case of a USB device, this involves + reading and selecting descriptors. + +Arguments: + + Device - handle to a device + + ResourceList - handle to a resource-list object that identifies the + raw hardware resources that the PnP manager assigned + to the device + + ResourceListTranslated - handle to a resource-list object that + identifies the translated hardware resources + that the PnP manager assigned to the device + +Return Value: + + NT status value + +--*/ +{ + NTSTATUS status; + PDEVICE_CONTEXT pDeviceContext; + WDF_USB_DEVICE_INFORMATION deviceInfo; + ULONG waitWakeEnable; + + UNREFERENCED_PARAMETER(ResourceList); + UNREFERENCED_PARAMETER(ResourceListTranslated); + waitWakeEnable = FALSE; + PAGED_CODE(); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "--> EvtDevicePrepareHardware\n"); + + pDeviceContext = GetDeviceContext(Device); + + // + // Create a USB device handle so that we can communicate with the + // underlying USB stack. The WDFUSBDEVICE handle is used to query, + // configure, and manage all aspects of the USB device. + // These aspects include device properties, bus properties, + // and I/O creation and synchronization. We only create device the first + // the PrepareHardware is called. If the device is restarted by pnp manager + // for resource rebalance, we will use the same device handle but then select + // the interfaces again because the USB stack could reconfigure the device on + // restart. + // + if (pDeviceContext->UsbDevice == NULL) { + WDF_USB_DEVICE_CREATE_CONFIG config; + + WDF_USB_DEVICE_CREATE_CONFIG_INIT(&config, + USBD_CLIENT_CONTRACT_VERSION_602); + + status = WdfUsbTargetDeviceCreateWithParameters(Device, + &config, + WDF_NO_OBJECT_ATTRIBUTES, + &pDeviceContext->UsbDevice); + + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "WdfUsbTargetDeviceCreateWithParameters failed with Status code %!STATUS!\n", status); + return status; + } + + // + // TODO: If you are fetching configuration descriptor from device for + // selecting a configuration or to parse other descriptors, call OsrFxValidateConfigurationDescriptor + // to do basic validation on the descriptors before you access them . + // + } + + // + // Retrieve USBD version information, port driver capabilites and device + // capabilites such as speed, power, etc. + // + WDF_USB_DEVICE_INFORMATION_INIT(&deviceInfo); + + status = WdfUsbTargetDeviceRetrieveInformation( + pDeviceContext->UsbDevice, + &deviceInfo); + if (NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "IsDeviceHighSpeed: %s\n", + (deviceInfo.Traits & WDF_USB_DEVICE_TRAIT_AT_HIGH_SPEED) ? "TRUE" : "FALSE"); + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, + "IsDeviceSelfPowered: %s\n", + (deviceInfo.Traits & WDF_USB_DEVICE_TRAIT_SELF_POWERED) ? "TRUE" : "FALSE"); + + waitWakeEnable = deviceInfo.Traits & + WDF_USB_DEVICE_TRAIT_REMOTE_WAKE_CAPABLE; + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, + "IsDeviceRemoteWakeable: %s\n", + waitWakeEnable ? "TRUE" : "FALSE"); + // + // Save these for use later. + // + pDeviceContext->UsbDeviceTraits = deviceInfo.Traits; + } + else { + pDeviceContext->UsbDeviceTraits = 0; + } + + status = SelectInterfaces(Device); + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "SelectInterfaces failed 0x%x\n", status); + return status; + } + + // + // Enable wait-wake and idle timeout if the device supports it + // + if (waitWakeEnable) { + status = OsrFxSetPowerPolicy(Device); + if (!NT_SUCCESS (status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "OsrFxSetPowerPolicy failed %!STATUS!\n", status); + return status; + } + } + + status = OsrFxConfigContReaderForInterruptEndPoint(pDeviceContext); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "<-- EvtDevicePrepareHardware\n"); + + return status; +} + + +NTSTATUS +OsrFxEvtDeviceD0Entry( + WDFDEVICE Device, + 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 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 + +--*/ +{ + PDEVICE_CONTEXT pDeviceContext; + NTSTATUS status; + BOOLEAN isTargetStarted; + + pDeviceContext = GetDeviceContext(Device); + isTargetStarted = FALSE; + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_POWER, + "-->OsrFxEvtEvtDeviceD0Entry - coming from %s\n", + DbgDevicePowerString(PreviousState)); + + // + // Since continuous reader is configured for this interrupt-pipe, we must explicitly start + // the I/O target to get the framework to post read requests. + // + status = WdfIoTargetStart(WdfUsbTargetPipeGetIoTarget(pDeviceContext->InterruptPipe)); + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_POWER, "Failed to start interrupt pipe %!STATUS!\n", status); + goto End; + } + + isTargetStarted = TRUE; + +End: + + if (!NT_SUCCESS(status)) { + // + // Failure in D0Entry will lead to device being removed. So let us stop the continuous + // reader in preparation for the ensuing remove. + // + if (isTargetStarted) { + WdfIoTargetStop(WdfUsbTargetPipeGetIoTarget(pDeviceContext->InterruptPipe), WdfIoTargetCancelSentIo); + } + } + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_POWER, "<--OsrFxEvtEvtDeviceD0Entry\n"); + + return status; +} + + +NTSTATUS +OsrFxEvtDeviceD0Exit( + WDFDEVICE Device, + 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. + + + 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. + +--*/ +{ + PDEVICE_CONTEXT pDeviceContext; + + PAGED_CODE(); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_POWER, + "-->OsrFxEvtDeviceD0Exit - moving to %s\n", + DbgDevicePowerString(TargetState)); + + pDeviceContext = GetDeviceContext(Device); + + WdfIoTargetStop(WdfUsbTargetPipeGetIoTarget(pDeviceContext->InterruptPipe), WdfIoTargetCancelSentIo); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_POWER, "<--OsrFxEvtDeviceD0Exit\n"); + + return STATUS_SUCCESS; +} + +VOID +OsrFxEvtDeviceSelfManagedIoFlush( + _In_ WDFDEVICE Device + ) +/*++ + +Routine Description: + + This routine handles flush activity for the device's + self-managed I/O operations. + +Arguments: + + Device - Handle to a framework device object. + +Return Value: + + None + +--*/ +{ + // Service the interrupt message queue to drain any outstanding + // requests + OsrUsbIoctlGetInterruptMessage(Device, STATUS_DEVICE_REMOVED); +} + +_IRQL_requires_(PASSIVE_LEVEL) +USBD_STATUS +OsrFxValidateConfigurationDescriptor( + _In_reads_bytes_(BufferLength) PUSB_CONFIGURATION_DESCRIPTOR ConfigDesc, + _In_ ULONG BufferLength, + _Inout_ PUCHAR *Offset + ) +/*++ + +Routine Description: + + Validates a USB Configuration Descriptor + +Parameters: + + ConfigDesc: Pointer to the entire USB Configuration descriptor returned by the device + + BufferLength: Known size of buffer pointed to by ConfigDesc (Not wTotalLength) + + Offset: if the USBD_STATUS returned is not USBD_STATUS_SUCCESS, offet will + be set to the address within the ConfigDesc buffer where the failure occured. + +Return Value: + + USBD_STATUS + Success implies the configuration descriptor is valid. + +--*/ +{ + + + USBD_STATUS status = USBD_STATUS_SUCCESS; + USHORT ValidationLevel = 3; + + PAGED_CODE(); + + // + // Call USBD_ValidateConfigurationDescriptor to validate the descriptors which are present in this supplied configuration descriptor. + // USBD_ValidateConfigurationDescriptor validates that all descriptors are completely contained within the configuration descriptor buffer. + // It also checks for interface numbers, number of endpoints in an interface etc. + // Please refer to msdn documentation for this function for more information. + // + + status = USBD_ValidateConfigurationDescriptor( ConfigDesc, BufferLength , ValidationLevel , Offset , POOL_TAG ); + if (!(NT_SUCCESS (status)) ){ + return status; + } + + // + // TODO: You should validate the correctness of other descriptors which are not taken care by USBD_ValidateConfigurationDescriptor + // Check that all such descriptors have size >= sizeof(the descriptor they point to) + // Check for any association between them if required + // + + return status; +} + + +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +OsrFxSetPowerPolicy( + _In_ WDFDEVICE Device + ) +{ + WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS idleSettings; + WDF_DEVICE_POWER_POLICY_WAKE_SETTINGS wakeSettings; + NTSTATUS status = STATUS_SUCCESS; + + PAGED_CODE(); + + // + // Init the idle policy structure. + // + WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_INIT(&idleSettings, IdleUsbSelectiveSuspend); + idleSettings.IdleTimeout = 10000; // 10-sec + + status = WdfDeviceAssignS0IdleSettings(Device, &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(Device, &wakeSettings); + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "WdfDeviceAssignSxWakeSettings failed %x\n", status); + return status; + } + + return status; +} + + +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +SelectInterfaces( + _In_ WDFDEVICE Device + ) +/*++ + +Routine Description: + + This helper routine selects the configuration, interface and + creates a context for every pipe (end point) in that interface. + +Arguments: + + Device - Handle to a framework device + +Return Value: + + NT status value + +--*/ +{ + WDF_USB_DEVICE_SELECT_CONFIG_PARAMS configParams; + NTSTATUS status = STATUS_SUCCESS; + PDEVICE_CONTEXT pDeviceContext; + WDFUSBPIPE pipe; + WDF_USB_PIPE_INFORMATION pipeInfo; + UCHAR index; + UCHAR numberConfiguredPipes; + + PAGED_CODE(); + + pDeviceContext = GetDeviceContext(Device); + + WDF_USB_DEVICE_SELECT_CONFIG_PARAMS_INIT_SINGLE_INTERFACE( &configParams); + + status = WdfUsbTargetDeviceSelectConfig(pDeviceContext->UsbDevice, + WDF_NO_OBJECT_ATTRIBUTES, + &configParams); + if(!NT_SUCCESS(status)) { + + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "WdfUsbTargetDeviceSelectConfig failed %!STATUS! \n", + status); + + // + // Since the Osr USB fx2 device is capable of working at high speed, the only reason + // the device would not be working at high speed is if the port doesn't + // support it. If the port doesn't support high speed it is a 1.1 port + // + if ((pDeviceContext->UsbDeviceTraits & WDF_USB_DEVICE_TRAIT_AT_HIGH_SPEED) == 0) { + GUID activity = DeviceToActivityId(Device); + + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + " On a 1.1 USB port on Windows Vista" + " this is expected as the OSR USB Fx2 board's Interrupt EndPoint descriptor" + " doesn't conform to the USB specification. Windows Vista detects this and" + " returns an error. \n" + ); + EventWriteSelectConfigFailure( + &activity, + pDeviceContext->DeviceName, + pDeviceContext->Location, + status + ); + } + + return status; + } + + pDeviceContext->UsbInterface = + configParams.Types.SingleInterface.ConfiguredUsbInterface; + + numberConfiguredPipes = configParams.Types.SingleInterface.NumberConfiguredPipes; + + // + // Get pipe handles + // + for(index=0; index < numberConfiguredPipes; index++) { + + WDF_USB_PIPE_INFORMATION_INIT(&pipeInfo); + + pipe = WdfUsbInterfaceGetConfiguredPipe( + pDeviceContext->UsbInterface, + index, //PipeIndex, + &pipeInfo + ); + // + // Tell the framework that it's okay to read less than + // MaximumPacketSize + // + WdfUsbTargetPipeSetNoMaximumPacketSizeCheck(pipe); + + if(WdfUsbPipeTypeInterrupt == pipeInfo.PipeType) { + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_IOCTL, + "Interrupt Pipe is 0x%p\n", pipe); + pDeviceContext->InterruptPipe = pipe; + } + + if(WdfUsbPipeTypeBulk == pipeInfo.PipeType && + WdfUsbTargetPipeIsInEndpoint(pipe)) { + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_IOCTL, + "BulkInput Pipe is 0x%p\n", pipe); + pDeviceContext->BulkReadPipe = pipe; + } + + if(WdfUsbPipeTypeBulk == pipeInfo.PipeType && + WdfUsbTargetPipeIsOutEndpoint(pipe)) { + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_IOCTL, + "BulkOutput Pipe is 0x%p\n", pipe); + pDeviceContext->BulkWritePipe = pipe; + } + + } + + // + // If we didn't find all the 3 pipes, fail the start. + // + if(!(pDeviceContext->BulkWritePipe + && pDeviceContext->BulkReadPipe && pDeviceContext->InterruptPipe)) { + status = STATUS_INVALID_DEVICE_STATE; + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "Device is not configured properly %!STATUS!\n", + status); + + return status; + } + + return status; +} + +_IRQL_requires_(PASSIVE_LEVEL) +VOID +GetDeviceEventLoggingNames( + _In_ WDFDEVICE Device + ) +/*++ + +Routine Description: + + Retrieve the friendly name and the location string into WDFMEMORY objects + and store them in the device context. + +Arguments: + +Return Value: + + None + +--*/ +{ + PDEVICE_CONTEXT pDevContext = GetDeviceContext(Device); + + WDF_OBJECT_ATTRIBUTES objectAttributes; + + WDFMEMORY deviceNameMemory = NULL; + WDFMEMORY locationMemory = NULL; + + NTSTATUS status; + + PAGED_CODE(); + + // + // We want both memory objects to be children of the device so they will + // be deleted automatically when the device is removed. + // + + WDF_OBJECT_ATTRIBUTES_INIT(&objectAttributes); + objectAttributes.ParentObject = Device; + + // + // First get the length of the string. If the FriendlyName + // is not there then get the lenght of device description. + // + + status = WdfDeviceAllocAndQueryProperty(Device, + DevicePropertyFriendlyName, + NonPagedPool, + &objectAttributes, + &deviceNameMemory); + + if (!NT_SUCCESS(status)) + { + status = WdfDeviceAllocAndQueryProperty(Device, + DevicePropertyDeviceDescription, + NonPagedPool, + &objectAttributes, + &deviceNameMemory); + } + + if (NT_SUCCESS(status)) + { + pDevContext->DeviceNameMemory = deviceNameMemory; + pDevContext->DeviceName = WdfMemoryGetBuffer(deviceNameMemory, NULL); + } + else + { + pDevContext->DeviceNameMemory = NULL; + pDevContext->DeviceName = L"(error retrieving name)"; + } + + // + // Retrieve the device location string. + // + + status = WdfDeviceAllocAndQueryProperty(Device, + DevicePropertyLocationInformation, + NonPagedPool, + WDF_NO_OBJECT_ATTRIBUTES, + &locationMemory); + + if (NT_SUCCESS(status)) + { + pDevContext->LocationMemory = locationMemory; + pDevContext->Location = WdfMemoryGetBuffer(locationMemory, NULL); + } + else + { + pDevContext->LocationMemory = NULL; + pDevContext->Location = L"(error retrieving location)"; + } + + return; +} + +_IRQL_requires_(PASSIVE_LEVEL) +PCHAR +DbgDevicePowerString( + _In_ WDF_POWER_DEVICE_STATE Type + ) +{ + 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/usb/umdf_filter_kmdf/kmdf_driver/bulkrwr.c b/usb/umdf_filter_kmdf/kmdf_driver/bulkrwr.c new file mode 100644 index 000000000..eabda1925 --- /dev/null +++ b/usb/umdf_filter_kmdf/kmdf_driver/bulkrwr.c @@ -0,0 +1,436 @@ +/*++ + +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: + + bulkrwr.c + +Abstract: + + This file has routines to perform reads and writes. + The read and writes are targeted bulk to endpoints. + +Environment: + + Kernel mode + +--*/ + +#include + + +#if defined(EVENT_TRACING) +#include "bulkrwr.tmh" +#endif + +#pragma warning(disable:4267) + +VOID +OsrFxEvtIoRead( + _In_ WDFQUEUE Queue, + _In_ WDFREQUEST Request, + _In_ size_t Length + ) +/*++ + +Routine Description: + + Called by the framework when it receives Read or Write requests. + +Arguments: + + Queue - Default queue handle + Request - Handle to the read/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: + + +--*/ +{ + WDFUSBPIPE pipe; + NTSTATUS status; + WDFMEMORY reqMemory; + PDEVICE_CONTEXT pDeviceContext; + GUID activity = RequestToActivityId(Request); + + UNREFERENCED_PARAMETER(Queue); + + // + // Log read start event, using IRP activity ID if available or request + // handle otherwise. + // + + EventWriteReadStart(&activity, WdfIoQueueGetDevice(Queue), (ULONG)Length); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_READ, "-->OsrFxEvtIoRead\n"); + + // + // First validate input parameters. + // + if (Length > TEST_BOARD_TRANSFER_BUFFER_SIZE) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_READ, "Transfer exceeds %d\n", + TEST_BOARD_TRANSFER_BUFFER_SIZE); + status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + pDeviceContext = GetDeviceContext(WdfIoQueueGetDevice(Queue)); + + pipe = pDeviceContext->BulkReadPipe; + + status = WdfRequestRetrieveOutputMemory(Request, &reqMemory); + if(!NT_SUCCESS(status)){ + TraceEvents(TRACE_LEVEL_ERROR, DBG_READ, + "WdfRequestRetrieveOutputMemory failed %!STATUS!\n", status); + goto Exit; + } + + // + // The format call validates to make sure that you are reading or + // writing to the right pipe type, sets the appropriate transfer flags, + // creates an URB and initializes the request. + // + status = WdfUsbTargetPipeFormatRequestForRead(pipe, + Request, + reqMemory, + NULL // Offsets + ); + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_READ, + "WdfUsbTargetPipeFormatRequestForRead failed 0x%x\n", status); + goto Exit; + } + + WdfRequestSetCompletionRoutine( + Request, + EvtRequestReadCompletionRoutine, + pipe); + // + // Send the request asynchronously. + // + if (WdfRequestSend(Request, WdfUsbTargetPipeGetIoTarget(pipe), WDF_NO_SEND_OPTIONS) == FALSE) { + // + // Framework couldn't send the request for some reason. + // + TraceEvents(TRACE_LEVEL_ERROR, DBG_READ, "WdfRequestSend failed\n"); + status = WdfRequestGetStatus(Request); + goto Exit; + } + + +Exit: + if (!NT_SUCCESS(status)) { + // + // log event read failed + // + EventWriteReadFail(&activity, WdfIoQueueGetDevice(Queue), status); + WdfRequestCompleteWithInformation(Request, status, 0); + } + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_READ, "<-- OsrFxEvtIoRead\n"); + + return; +} + +VOID +EvtRequestReadCompletionRoutine( + _In_ WDFREQUEST Request, + _In_ WDFIOTARGET Target, + _In_ PWDF_REQUEST_COMPLETION_PARAMS CompletionParams, + _In_ WDFCONTEXT Context + ) +/*++ + +Routine Description: + + This is the completion routine for reads + If the irp completes with success, we check if we + need to recirculate this irp for another stage of + transfer. + +Arguments: + + Context - Driver supplied context + Device - Device handle + Request - Request handle + Params - request completion params + +Return Value: + None + +--*/ +{ + NTSTATUS status; + size_t bytesRead = 0; + GUID activity = RequestToActivityId(Request); + PWDF_USB_REQUEST_COMPLETION_PARAMS usbCompletionParams; + + UNREFERENCED_PARAMETER(Target); + UNREFERENCED_PARAMETER(Context); + + status = CompletionParams->IoStatus.Status; + + usbCompletionParams = CompletionParams->Parameters.Usb.Completion; + + bytesRead = usbCompletionParams->Parameters.PipeRead.Length; + + if (NT_SUCCESS(status)){ + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_READ, + "Number of bytes read: %I64d\n", (INT64)bytesRead); + } else { + TraceEvents(TRACE_LEVEL_ERROR, DBG_READ, + "Read failed - request status 0x%x UsbdStatus 0x%x\n", + status, usbCompletionParams->UsbdStatus); + + } + + // + // Log read stop event, using IRP activity ID if available or request + // handle otherwise. + // + + EventWriteReadStop(&activity, + WdfIoQueueGetDevice(WdfRequestGetIoQueue(Request)), + bytesRead, + status, + usbCompletionParams->UsbdStatus); + + WdfRequestCompleteWithInformation(Request, status, bytesRead); + + return; +} + +VOID +OsrFxEvtIoWrite( + _In_ WDFQUEUE Queue, + _In_ WDFREQUEST Request, + _In_ size_t Length + ) +/*++ + +Routine Description: + + Called by the framework when it receives Read or Write requests. + +Arguments: + + Queue - Default queue handle + Request - Handle to the read/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; + WDFUSBPIPE pipe; + WDFMEMORY reqMemory; + PDEVICE_CONTEXT pDeviceContext; + GUID activity = RequestToActivityId(Request); + + UNREFERENCED_PARAMETER(Queue); + + + // + // Log write start event, using IRP activity ID if available or request + // handle otherwise. + // + EventWriteWriteStart(&activity, WdfIoQueueGetDevice(Queue), (ULONG)Length); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_WRITE, "-->OsrFxEvtIoWrite\n"); + + // + // First validate input parameters. + // + if (Length > TEST_BOARD_TRANSFER_BUFFER_SIZE) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_READ, "Transfer exceeds %d\n", + TEST_BOARD_TRANSFER_BUFFER_SIZE); + status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + pDeviceContext = GetDeviceContext(WdfIoQueueGetDevice(Queue)); + + pipe = pDeviceContext->BulkWritePipe; + + status = WdfRequestRetrieveInputMemory(Request, &reqMemory); + if(!NT_SUCCESS(status)){ + TraceEvents(TRACE_LEVEL_ERROR, DBG_WRITE, "WdfRequestRetrieveInputBuffer failed\n"); + goto Exit; + } + + status = WdfUsbTargetPipeFormatRequestForWrite(pipe, + Request, + reqMemory, + NULL); // Offset + + + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_WRITE, + "WdfUsbTargetPipeFormatRequestForWrite failed 0x%x\n", status); + goto Exit; + } + + WdfRequestSetCompletionRoutine( + Request, + EvtRequestWriteCompletionRoutine, + pipe); + + // + // Send the request asynchronously. + // + if (WdfRequestSend(Request, WdfUsbTargetPipeGetIoTarget(pipe), WDF_NO_SEND_OPTIONS) == FALSE) { + // + // Framework couldn't send the request for some reason. + // + TraceEvents(TRACE_LEVEL_ERROR, DBG_WRITE, "WdfRequestSend failed\n"); + status = WdfRequestGetStatus(Request); + goto Exit; + } + +Exit: + + if (!NT_SUCCESS(status)) { + // + // log event write failed + // + EventWriteWriteFail(&activity, WdfIoQueueGetDevice(Queue), status); + + WdfRequestCompleteWithInformation(Request, status, 0); + } + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_WRITE, "<-- OsrFxEvtIoWrite\n"); + + return; +} + +VOID +EvtRequestWriteCompletionRoutine( + _In_ WDFREQUEST Request, + _In_ WDFIOTARGET Target, + _In_ PWDF_REQUEST_COMPLETION_PARAMS CompletionParams, + _In_ WDFCONTEXT Context + ) +/*++ + +Routine Description: + + This is the completion routine for writes + If the irp completes with success, we check if we + need to recirculate this irp for another stage of + transfer. + +Arguments: + + Context - Driver supplied context + Device - Device handle + Request - Request handle + Params - request completion params + +Return Value: + None + +--*/ +{ + NTSTATUS status; + size_t bytesWritten = 0; + GUID activity = RequestToActivityId(Request); + PWDF_USB_REQUEST_COMPLETION_PARAMS usbCompletionParams; + + UNREFERENCED_PARAMETER(Target); + UNREFERENCED_PARAMETER(Context); + + status = CompletionParams->IoStatus.Status; + + // + // For usb devices, we should look at the Usb.Completion param. + // + usbCompletionParams = CompletionParams->Parameters.Usb.Completion; + + bytesWritten = usbCompletionParams->Parameters.PipeWrite.Length; + + if (NT_SUCCESS(status)){ + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_WRITE, + "Number of bytes written: %I64d\n", (INT64)bytesWritten); + } else { + TraceEvents(TRACE_LEVEL_ERROR, DBG_WRITE, + "Write failed: request Status 0x%x UsbdStatus 0x%x\n", + status, usbCompletionParams->UsbdStatus); + } + + // + // Log write stop event, using IRP activtiy ID if available or request + // handle otherwise + // + EventWriteWriteStop(&activity, + WdfIoQueueGetDevice(WdfRequestGetIoQueue(Request)), + bytesWritten, + status, + usbCompletionParams->UsbdStatus); + + + WdfRequestCompleteWithInformation(Request, status, bytesWritten); + + return; +} + + +VOID +OsrFxEvtIoStop( + _In_ WDFQUEUE Queue, + _In_ WDFREQUEST Request, + _In_ ULONG ActionFlags + ) +/*++ + +Routine Description: + + This callback is invoked on every inflight request when the device + is suspended or removed. Since our inflight read and write requests + are actually pending in the target device, we will just acknowledge + its presence. Until we acknowledge, complete, or requeue the requests + framework will wait before allowing the device suspend or remove to + proceeed. When the underlying USB stack gets the request to suspend or + remove, it will fail all the pending requests. + +Arguments: + + Queue - handle to queue object that is associated with the I/O request + + Request - handle to a request object + + ActionFlags - bitwise OR of one or more WDF_REQUEST_STOP_ACTION_FLAGS flags + +Return Value: + None + +--*/ +{ + UNREFERENCED_PARAMETER(Queue); + UNREFERENCED_PARAMETER(ActionFlags); + + if (ActionFlags & WdfRequestStopActionSuspend ) { + WdfRequestStopAcknowledge(Request, FALSE); // Don't requeue + } else if(ActionFlags & WdfRequestStopActionPurge) { + WdfRequestCancelSentRequest(Request); + } + return; +} + + diff --git a/usb/umdf_filter_kmdf/kmdf_driver/driver.c b/usb/umdf_filter_kmdf/kmdf_driver/driver.c new file mode 100644 index 000000000..d3b445734 --- /dev/null +++ b/usb/umdf_filter_kmdf/kmdf_driver/driver.c @@ -0,0 +1,299 @@ +/*++ + +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: + + Main module. + + This driver is for Open System Resources USB-FX2 Learning Kit designed + and built by OSR specifically for use in teaching software developers how to write + drivers for USB devices. + + The board supports a single configuration. The board automatically + detects the speed of the host controller, and supplies either the + high or full speed configuration based on the host controller's speed. + + The firmware supports 3 endpoints: + + Endpoint number 1 is used to indicate the state of the 8-switch + switch-pack on the OSR USB-FX2 board. A single byte representing + the switch state is sent (a) when the board is first started, + (b) when the board resumes after selective-suspend, + (c) whenever the state of the switches is changed. + + Endpoints 6 and 8 perform an internal loop-back function. + Data that is sent to the board at EP6 is returned to the host on EP8. + + For further information on the endpoints, please refer to the spec + http://www.osronline.com/hardware/OSRFX2_32.pdf. + + Vendor ID of the device is 0x4705 and Product ID is 0x210. + +Environment: + + Kernel mode only + +--*/ + +#include + +#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 trace.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 "driver.tmh" +#else +ULONG DebugLevel = TRACE_LEVEL_INFORMATION; +ULONG DebugFlag = 0xff; +#endif + +PFN_IO_GET_ACTIVITY_ID_IRP g_pIoGetActivityIdIrp; +PFN_IO_SET_DEVICE_INTERFACE_PROPERTY_DATA g_pIoSetDeviceInterfacePropertyData; + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT, DriverEntry) +#pragma alloc_text(PAGE, OsrFxEvtDriverContextCleanup) +#endif + +NTSTATUS +DriverEntry( + PDRIVER_OBJECT DriverObject, + PUNICODE_STRING RegistryPath + ) +/*++ + +Routine Description: + DriverEntry initializes the driver and is the first routine called by the + system after the driver is loaded. + +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 or another NTSTATUS error code otherwise. + +--*/ +{ + WDF_DRIVER_CONFIG config; + NTSTATUS status; + WDF_OBJECT_ATTRIBUTES attributes; + UNICODE_STRING funcName; + + // + // Initialize WPP Tracing + // + WPP_INIT_TRACING( DriverObject, RegistryPath ); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, + "OSRUSBFX2 Driver Sample - Driver Framework Edition.\n"); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, + "Built %s %s\n", __DATE__, __TIME__); + + // + // IRP activity ID functions are available on some versions, save them into + // globals (or NULL if not available) + // + RtlInitUnicodeString(&funcName, L"IoGetActivityIdIrp"); + g_pIoGetActivityIdIrp = (PFN_IO_GET_ACTIVITY_ID_IRP) (ULONG_PTR) + MmGetSystemRoutineAddress(&funcName); + + // + // The Device interface property set is available on some version, save it + // into globals (or NULL if not available) + // + RtlInitUnicodeString(&funcName, L"IoSetDeviceInterfacePropertyData"); + g_pIoSetDeviceInterfacePropertyData = (PFN_IO_SET_DEVICE_INTERFACE_PROPERTY_DATA) (ULONG_PTR) + MmGetSystemRoutineAddress(&funcName); + + // + // Register with ETW (unified tracing) + // + EventRegisterOSRUSBFX2(); + + // + // 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, + OsrFxEvtDeviceAdd + ); + + // + // 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 = OsrFxEvtDriverContextCleanup; + + // + // Create a framework driver object to represent our driver. + // + status = WdfDriverCreate( + DriverObject, + RegistryPath, + &attributes, // Driver Object Attributes + &config, // Driver Config Info + WDF_NO_HANDLE // hDriver + ); + + if (!NT_SUCCESS(status)) { + + TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, + "WdfDriverCreate failed with status 0x%x\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); + EventUnregisterOSRUSBFX2(); + } + + return status; +} + +VOID +OsrFxEvtDriverContextCleanup( + WDFOBJECT Driver + ) +/*++ +Routine Description: + + Free resources allocated in DriverEntry that are not automatically + cleaned up by the framework. + +Arguments: + + Driver - handle to a WDF Driver object. + +Return Value: + + VOID. + +--*/ +{ + PAGED_CODE (); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, + "--> OsrFxEvtDriverContextCleanup\n"); + + WPP_CLEANUP( WdfDriverWdmGetDriverObject( (WDFDRIVER)Driver )); + + UNREFERENCED_PARAMETER(Driver); // For the case when WPP is not being used. + + EventUnregisterOSRUSBFX2(); +} + +#if !defined(EVENT_TRACING) + +VOID +TraceEvents ( + _In_ ULONG DebugPrintLevel, + _In_ ULONG DebugPrintFlag, + _Printf_format_string_ + _In_ PCSTR DebugMessage, + ... + ) + +/*++ + +Routine Description: + + Debug print for the sample driver. + +Arguments: + + DebugPrintLevel - print level between 0 and 3, with 3 the most verbose + DebugPrintFlag - message mask + DebugMessage - format string of the message to print + ... - values used by the format string + +Return Value: + + None. + + --*/ + { +#if DBG +#define TEMP_BUFFER_SIZE 1024 + 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 0x%x\n", status); + return; + } + if (DebugPrintLevel <= TRACE_LEVEL_ERROR || + (DebugPrintLevel <= DebugLevel && + ((DebugPrintFlag & DebugFlag) == DebugPrintFlag))) { + DbgPrint("%s %s", _DRIVER_NAME_, debugMessageBuffer); + } + } + va_end(list); + + return; +#else + UNREFERENCED_PARAMETER(DebugPrintLevel); + UNREFERENCED_PARAMETER(DebugPrintFlag); + UNREFERENCED_PARAMETER(DebugMessage); +#endif +} + +#endif + + + + diff --git a/usb/umdf_filter_kmdf/kmdf_driver/interrupt.c b/usb/umdf_filter_kmdf/kmdf_driver/interrupt.c new file mode 100644 index 000000000..00ea35505 --- /dev/null +++ b/usb/umdf_filter_kmdf/kmdf_driver/interrupt.c @@ -0,0 +1,184 @@ +/*++ + +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: + + Interrupt.c + +Abstract: + + This modules has routines configure a continuous reader on an + interrupt pipe to asynchronously read toggle switch states. + +Environment: + + Kernel mode + +--*/ + +#include + +#if defined(EVENT_TRACING) +#include "interrupt.tmh" +#endif + + +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +OsrFxConfigContReaderForInterruptEndPoint( + _In_ PDEVICE_CONTEXT DeviceContext + ) +/*++ + +Routine Description: + + This routine configures a continuous reader on the + interrupt endpoint. It's called from the PrepareHarware event. + +Arguments: + + +Return Value: + + NT status value + +--*/ +{ + WDF_USB_CONTINUOUS_READER_CONFIG contReaderConfig; + NTSTATUS status; + + WDF_USB_CONTINUOUS_READER_CONFIG_INIT(&contReaderConfig, + OsrFxEvtUsbInterruptPipeReadComplete, + DeviceContext, // Context + sizeof(UCHAR)); // TransferLength + + contReaderConfig.EvtUsbTargetPipeReadersFailed = OsrFxEvtUsbInterruptReadersFailed; + + // + // Reader requests are not posted to the target automatically. + // Driver must explictly call WdfIoTargetStart to kick start the + // reader. In this sample, it's done in D0Entry. + // By defaut, framework queues two requests to the target + // endpoint. Driver can configure up to 10 requests with CONFIG macro. + // + status = WdfUsbTargetPipeConfigContinuousReader(DeviceContext->InterruptPipe, + &contReaderConfig); + + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, + "OsrFxConfigContReaderForInterruptEndPoint failed %x\n", + status); + return status; + } + + return status; +} + +VOID +OsrFxEvtUsbInterruptPipeReadComplete( + WDFUSBPIPE Pipe, + WDFMEMORY Buffer, + size_t NumBytesTransferred, + WDFCONTEXT Context + ) +/*++ + +Routine Description: + + This the completion routine of the continour reader. This can + called concurrently on multiprocessor system if there are + more than one readers configured. So make sure to protect + access to global resources. + +Arguments: + + Buffer - This buffer is freed when this call returns. + If the driver wants to delay processing of the buffer, it + can take an additional referrence. + + Context - Provided in the WDF_USB_CONTINUOUS_READER_CONFIG_INIT macro + +Return Value: + + NT status value + +--*/ +{ + PUCHAR switchState = NULL; + WDFDEVICE device; + PDEVICE_CONTEXT pDeviceContext = Context; + + UNREFERENCED_PARAMETER(Pipe); + + device = WdfObjectContextGetObject(pDeviceContext); + + // + // Make sure that there is data in the read packet. Depending on the device + // specification, it is possible for it to return a 0 length read in + // certain conditions. + // + + if (NumBytesTransferred == 0) { + TraceEvents(TRACE_LEVEL_WARNING, DBG_INIT, + "OsrFxEvtUsbInterruptPipeReadComplete Zero length read " + "occured on the Interrupt Pipe's Continuous Reader\n" + ); + return; + } + + + NT_ASSERT(NumBytesTransferred == sizeof(UCHAR)); + + switchState = WdfMemoryGetBuffer(Buffer, NULL); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, + "OsrFxEvtUsbInterruptPipeReadComplete SwitchState %x\n", + *switchState); + + pDeviceContext->CurrentSwitchState = *switchState; + + // + // Handle any pending Interrupt Message IOCTLs. Note that the OSR USB device + // will generate an interrupt message when the the device resumes from a low + // power state. So if the Interrupt Message IOCTL was sent after the device + // has gone to a low power state, the pending Interrupt Message IOCTL will + // get completed in the function call below, before the user twiddles the + // dip switches on the OSR USB device. If this is not the desired behavior + // for your driver, then you could handle this condition by maintaining a + // state variable on D0Entry to track interrupt messages caused by power up. + // + OsrUsbIoctlGetInterruptMessage(device, STATUS_SUCCESS); + +} + +BOOLEAN +OsrFxEvtUsbInterruptReadersFailed( + _In_ WDFUSBPIPE Pipe, + _In_ NTSTATUS Status, + _In_ USBD_STATUS UsbdStatus + ) +{ + WDFDEVICE device = WdfIoTargetGetDevice(WdfUsbTargetPipeGetIoTarget(Pipe)); + PDEVICE_CONTEXT pDeviceContext = GetDeviceContext(device); + + UNREFERENCED_PARAMETER(UsbdStatus); + + // + // Clear the current switch state. + // + pDeviceContext->CurrentSwitchState = 0; + + // + // Service the pending interrupt switch change request + // + OsrUsbIoctlGetInterruptMessage(device, Status); + + return TRUE; +} + diff --git a/usb/umdf_filter_kmdf/kmdf_driver/ioctl.c b/usb/umdf_filter_kmdf/kmdf_driver/ioctl.c new file mode 100644 index 000000000..0f29dc8cb --- /dev/null +++ b/usb/umdf_filter_kmdf/kmdf_driver/ioctl.c @@ -0,0 +1,1057 @@ +/*++ + +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: + + USB device driver for OSR USB-FX2 Learning Kit + +Environment: + + Kernel mode only + +--*/ + +#include + +#if defined(EVENT_TRACING) +#include "ioctl.tmh" +#endif + +#pragma alloc_text(PAGE, OsrFxEvtIoDeviceControl) +#pragma alloc_text(PAGE, ResetPipe) +#pragma alloc_text(PAGE, ResetDevice) +#pragma alloc_text(PAGE, ReenumerateDevice) +#pragma alloc_text(PAGE, GetBarGraphState) +#pragma alloc_text(PAGE, SetBarGraphState) +#pragma alloc_text(PAGE, GetSevenSegmentState) +#pragma alloc_text(PAGE, SetSevenSegmentState) +#pragma alloc_text(PAGE, GetSwitchState) + +VOID +OsrFxEvtIoDeviceControl( + _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 + +--*/ +{ + WDFDEVICE device; + PDEVICE_CONTEXT pDevContext; + size_t bytesReturned = 0; + PBAR_GRAPH_STATE barGraphState = NULL; + PSWITCH_STATE switchState = NULL; + PUCHAR sevenSegment = NULL; + BOOLEAN requestPending = FALSE; + NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST; + + UNREFERENCED_PARAMETER(InputBufferLength); + UNREFERENCED_PARAMETER(OutputBufferLength); + + PAGED_CODE(); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_IOCTL, "--> OsrFxEvtIoDeviceControl\n"); + // + // initialize variables + // + device = WdfIoQueueGetDevice(Queue); + pDevContext = GetDeviceContext(device); + + switch(IoControlCode) { + + case IOCTL_OSRUSBFX2_GET_CONFIG_DESCRIPTOR: { + + PUSB_CONFIGURATION_DESCRIPTOR configurationDescriptor = NULL; + USHORT requiredSize = 0; + + // + // First get the size of the config descriptor + // + status = WdfUsbTargetDeviceRetrieveConfigDescriptor( + pDevContext->UsbDevice, + NULL, + &requiredSize); + + if (status != STATUS_BUFFER_TOO_SMALL) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, + "WdfUsbTargetDeviceRetrieveConfigDescriptor failed 0x%x\n", status); + break; + } + + // + // Get the buffer - make sure the buffer is big enough + // + status = WdfRequestRetrieveOutputBuffer(Request, + (size_t)requiredSize, // MinimumRequired + &configurationDescriptor, + NULL); + if(!NT_SUCCESS(status)){ + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, + "WdfRequestRetrieveOutputBuffer failed 0x%x\n", status); + break; + } + + status = WdfUsbTargetDeviceRetrieveConfigDescriptor( + pDevContext->UsbDevice, + configurationDescriptor, + &requiredSize); + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, + "WdfUsbTargetDeviceRetrieveConfigDescriptor failed 0x%x\n", status); + break; + } + + bytesReturned = requiredSize; + + } + break; + + case IOCTL_OSRUSBFX2_RESET_DEVICE: + + status = ResetDevice(device); + break; + + case IOCTL_OSRUSBFX2_REENUMERATE_DEVICE: + + // + // Otherwise, call our function to reenumerate the + // device + // + status = ReenumerateDevice(pDevContext); + + bytesReturned = 0; + break; + + case IOCTL_OSRUSBFX2_GET_BAR_GRAPH_DISPLAY: + + // + // Make sure the caller's output buffer is large enough + // to hold the state of the bar graph + // + status = WdfRequestRetrieveOutputBuffer(Request, + sizeof(BAR_GRAPH_STATE), + &barGraphState, + NULL); + + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, + "User's output buffer is too small for this IOCTL, expecting an BAR_GRAPH_STATE\n"); + break; + } + // + // Call our function to get the bar graph state + // + status = GetBarGraphState(pDevContext, barGraphState); + + // + // If we succeeded return the user their data + // + if (NT_SUCCESS(status)) { + + bytesReturned = sizeof(BAR_GRAPH_STATE); + + } else { + + bytesReturned = 0; + + } + break; + + case IOCTL_OSRUSBFX2_SET_BAR_GRAPH_DISPLAY: + + status = WdfRequestRetrieveInputBuffer(Request, + sizeof(BAR_GRAPH_STATE), + &barGraphState, + NULL); + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, + "User's input buffer is too small for this IOCTL, expecting an BAR_GRAPH_STATE\n"); + break; + } + + // + // Call our routine to set the bar graph state + // + status = SetBarGraphState(pDevContext, barGraphState); + + // + // There's no data returned for this call + // + bytesReturned = 0; + break; + + case IOCTL_OSRUSBFX2_GET_7_SEGMENT_DISPLAY: + + status = WdfRequestRetrieveOutputBuffer(Request, + sizeof(UCHAR), + &sevenSegment, + NULL); + + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, + "User's output buffer is too small for this IOCTL, expecting an UCHAR\n"); + break; + } + + // + // Call our function to get the 7 segment state + // + status = GetSevenSegmentState(pDevContext, sevenSegment); + + // + // If we succeeded return the user their data + // + if (NT_SUCCESS(status)) { + + bytesReturned = sizeof(UCHAR); + + } else { + + bytesReturned = 0; + + } + break; + + case IOCTL_OSRUSBFX2_SET_7_SEGMENT_DISPLAY: + + status = WdfRequestRetrieveInputBuffer(Request, + sizeof(UCHAR), + &sevenSegment, + NULL); + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, + "User's input buffer is too small for this IOCTL, expecting an UCHAR\n"); + bytesReturned = sizeof(UCHAR); + break; + } + + // + // Call our routine to set the 7 segment state + // + status = SetSevenSegmentState(pDevContext, sevenSegment); + + // + // There's no data returned for this call + // + bytesReturned = 0; + break; + + case IOCTL_OSRUSBFX2_READ_SWITCHES: + + status = WdfRequestRetrieveOutputBuffer(Request, + sizeof(SWITCH_STATE), + &switchState, + NULL);// BufferLength + + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, + "User's output buffer is too small for this IOCTL, expecting a SWITCH_STATE\n"); + bytesReturned = sizeof(SWITCH_STATE); + break; + + } + + // + // Call our routine to get the state of the switches + // + status = GetSwitchState(pDevContext, switchState); + + // + // If successful, return the user their data + // + if (NT_SUCCESS(status)) { + + bytesReturned = sizeof(SWITCH_STATE); + + } else { + // + // Don't return any data + // + bytesReturned = 0; + } + break; + + case IOCTL_OSRUSBFX2_GET_INTERRUPT_MESSAGE: + + // + // Forward the request to an interrupt message queue and dont complete + // the request until an interrupt from the USB device occurs. + // + status = WdfRequestForwardToIoQueue(Request, pDevContext->InterruptMsgQueue); + if (NT_SUCCESS(status)) { + requestPending = TRUE; + } + + break; + + default : + status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + + if (requestPending == FALSE) { + WdfRequestCompleteWithInformation(Request, status, bytesReturned); + } + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_IOCTL, "<-- OsrFxEvtIoDeviceControl\n"); + + return; +} + +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +ResetPipe( + _In_ WDFUSBPIPE Pipe + ) +/*++ + +Routine Description: + + This routine resets the pipe. + +Arguments: + + Pipe - framework pipe handle + +Return Value: + + NT status value + +--*/ +{ + NTSTATUS status; + + PAGED_CODE(); + + // + // This routine synchronously submits a URB_FUNCTION_RESET_PIPE + // request down the stack. + // + status = WdfUsbTargetPipeResetSynchronously(Pipe, + WDF_NO_HANDLE, // WDFREQUEST + NULL // PWDF_REQUEST_SEND_OPTIONS + ); + + if (NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_IOCTL, "ResetPipe - success\n"); + status = STATUS_SUCCESS; + } + else { + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, "ResetPipe - failed\n"); + } + + return status; +} + +VOID +StopAllPipes( + IN PDEVICE_CONTEXT DeviceContext + ) +{ + WdfIoTargetStop(WdfUsbTargetPipeGetIoTarget(DeviceContext->InterruptPipe), + WdfIoTargetCancelSentIo); + WdfIoTargetStop(WdfUsbTargetPipeGetIoTarget(DeviceContext->BulkReadPipe), + WdfIoTargetCancelSentIo); + WdfIoTargetStop(WdfUsbTargetPipeGetIoTarget(DeviceContext->BulkWritePipe), + WdfIoTargetCancelSentIo); +} + +NTSTATUS +StartAllPipes( + IN PDEVICE_CONTEXT DeviceContext + ) +{ + NTSTATUS status; + + status = WdfIoTargetStart(WdfUsbTargetPipeGetIoTarget(DeviceContext->InterruptPipe)); + if (!NT_SUCCESS(status)) { + return status; + } + + status = WdfIoTargetStart(WdfUsbTargetPipeGetIoTarget(DeviceContext->BulkReadPipe)); + if (!NT_SUCCESS(status)) { + return status; + } + + status = WdfIoTargetStart(WdfUsbTargetPipeGetIoTarget(DeviceContext->BulkWritePipe)); + if (!NT_SUCCESS(status)) { + return status; + } + + return status; +} + +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +ResetDevice( + _In_ WDFDEVICE Device + ) +/*++ + +Routine Description: + + This routine calls WdfUsbTargetDeviceResetPortSynchronously to reset the device if it's still + connected. + +Arguments: + + Device - Handle to a framework device + +Return Value: + + NT status value + +--*/ +{ + PDEVICE_CONTEXT pDeviceContext; + NTSTATUS status; + + PAGED_CODE(); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_IOCTL, "--> ResetDevice\n"); + + pDeviceContext = GetDeviceContext(Device); + + // + // A NULL timeout indicates an infinite wake + // + status = WdfWaitLockAcquire(pDeviceContext->ResetDeviceWaitLock, NULL); + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, "ResetDevice - could not acquire lock\n"); + return status; + } + + StopAllPipes(pDeviceContext); + + status = WdfUsbTargetDeviceResetPortSynchronously(pDeviceContext->UsbDevice); + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, "ResetDevice failed - 0x%x\n", status); + } + + status = StartAllPipes(pDeviceContext); + if (!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, "Failed to start all pipes - 0x%x\n", status); + } + + WdfWaitLockRelease(pDeviceContext->ResetDeviceWaitLock); + + TraceEvents(TRACE_LEVEL_INFORMATION, DBG_IOCTL, "<-- ResetDevice\n"); + return status; +} + +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +ReenumerateDevice( + _In_ PDEVICE_CONTEXT DevContext + ) +/*++ + +Routine Description + + This routine re-enumerates the USB device. + +Arguments: + + pDevContext - One of our device extensions + +Return Value: + + NT status value + +--*/ +{ + NTSTATUS status; + WDF_USB_CONTROL_SETUP_PACKET controlSetupPacket; + WDF_REQUEST_SEND_OPTIONS sendOptions; + GUID activity; + + PAGED_CODE(); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL,"--> ReenumerateDevice\n"); + + WDF_REQUEST_SEND_OPTIONS_INIT( + &sendOptions, + WDF_REQUEST_SEND_OPTION_TIMEOUT + ); + + WDF_REQUEST_SEND_OPTIONS_SET_TIMEOUT( + &sendOptions, + DEFAULT_CONTROL_TRANSFER_TIMEOUT + ); + + WDF_USB_CONTROL_SETUP_PACKET_INIT_VENDOR(&controlSetupPacket, + BmRequestHostToDevice, + BmRequestToDevice, + USBFX2LK_REENUMERATE, // Request + 0, // Value + 0); // Index + + + status = WdfUsbTargetDeviceSendControlTransferSynchronously( + DevContext->UsbDevice, + WDF_NO_HANDLE, // Optional WDFREQUEST + &sendOptions, + &controlSetupPacket, + NULL, // MemoryDescriptor + NULL); // BytesTransferred + + if(!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, + "ReenumerateDevice: Failed to Reenumerate - 0x%x \n", status); + } + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL,"<-- ReenumerateDevice\n"); + + // + // Send event to eventlog + // + + activity = DeviceToActivityId(WdfObjectContextGetObject(DevContext)); + EventWriteDeviceReenumerated(&activity, + DevContext->DeviceName, + DevContext->Location, + status); + + return status; + +} + +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +GetBarGraphState( + _In_ PDEVICE_CONTEXT DevContext, + _Out_ PBAR_GRAPH_STATE BarGraphState + ) +/*++ + +Routine Description + + This routine gets the state of the bar graph on the board + +Arguments: + + DevContext - One of our device extensions + + BarGraphState - Struct that receives the bar graph's state + +Return Value: + + NT status value + +--*/ +{ + NTSTATUS status; + WDF_USB_CONTROL_SETUP_PACKET controlSetupPacket; + WDF_REQUEST_SEND_OPTIONS sendOptions; + WDF_MEMORY_DESCRIPTOR memDesc; + ULONG bytesTransferred; + + PAGED_CODE(); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL, "--> GetBarGraphState\n"); + + WDF_REQUEST_SEND_OPTIONS_INIT( + &sendOptions, + WDF_REQUEST_SEND_OPTION_TIMEOUT + ); + + WDF_REQUEST_SEND_OPTIONS_SET_TIMEOUT( + &sendOptions, + DEFAULT_CONTROL_TRANSFER_TIMEOUT + ); + + WDF_USB_CONTROL_SETUP_PACKET_INIT_VENDOR(&controlSetupPacket, + BmRequestDeviceToHost, + BmRequestToDevice, + USBFX2LK_READ_BARGRAPH_DISPLAY, // Request + 0, // Value + 0); // Index + + // + // Set the buffer to 0, the board will OR in everything that is set + // + BarGraphState->BarsAsUChar = 0; + + + WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&memDesc, + BarGraphState, + sizeof(BAR_GRAPH_STATE)); + + status = WdfUsbTargetDeviceSendControlTransferSynchronously( + DevContext->UsbDevice, + WDF_NO_HANDLE, // Optional WDFREQUEST + &sendOptions, + &controlSetupPacket, + &memDesc, + &bytesTransferred); + + if(!NT_SUCCESS(status)) { + + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, + "GetBarGraphState: Failed to GetBarGraphState - 0x%x \n", status); + + } else { + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL, + "GetBarGraphState: LED mask is 0x%x\n", BarGraphState->BarsAsUChar); + } + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL, "<-- GetBarGraphState\n"); + + return status; + +} + +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +SetBarGraphState( + _In_ PDEVICE_CONTEXT DevContext, + _In_ PBAR_GRAPH_STATE BarGraphState + ) +/*++ + +Routine Description + + This routine sets the state of the bar graph on the board + +Arguments: + + DevContext - One of our device extensions + + BarGraphState - Struct that describes the bar graph's desired state + +Return Value: + + NT status value + +--*/ +{ + NTSTATUS status; + WDF_USB_CONTROL_SETUP_PACKET controlSetupPacket; + WDF_REQUEST_SEND_OPTIONS sendOptions; + WDF_MEMORY_DESCRIPTOR memDesc; + ULONG bytesTransferred; + + PAGED_CODE(); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL, "--> SetBarGraphState\n"); + + WDF_REQUEST_SEND_OPTIONS_INIT( + &sendOptions, + WDF_REQUEST_SEND_OPTION_TIMEOUT + ); + + WDF_REQUEST_SEND_OPTIONS_SET_TIMEOUT( + &sendOptions, + DEFAULT_CONTROL_TRANSFER_TIMEOUT + ); + + WDF_USB_CONTROL_SETUP_PACKET_INIT_VENDOR(&controlSetupPacket, + BmRequestHostToDevice, + BmRequestToDevice, + USBFX2LK_SET_BARGRAPH_DISPLAY, // Request + 0, // Value + 0); // Index + + WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&memDesc, + BarGraphState, + sizeof(BAR_GRAPH_STATE)); + + status = WdfUsbTargetDeviceSendControlTransferSynchronously( + DevContext->UsbDevice, + NULL, // Optional WDFREQUEST + &sendOptions, + &controlSetupPacket, + &memDesc, + &bytesTransferred); + + if(!NT_SUCCESS(status)) { + + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, + "SetBarGraphState: Failed - 0x%x \n", status); + + } else { + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL, + "SetBarGraphState: LED mask is 0x%x\n", BarGraphState->BarsAsUChar); + } + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL, "<-- SetBarGraphState\n"); + + return status; + +} + +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +GetSevenSegmentState( + _In_ PDEVICE_CONTEXT DevContext, + _Out_ PUCHAR SevenSegment + ) +/*++ + +Routine Description + + This routine gets the state of the 7 segment display on the board + by sending a synchronous control command. + + NOTE: It's not a good practice to send a synchronous request in the + context of the user thread because if the transfer takes long + time to complete, you end up holding the user thread. + + I'm choosing to do synchronous transfer because a) I know this one + completes immediately b) and for demonstration. + +Arguments: + + DevContext - One of our device extensions + + SevenSegment - receives the state of the 7 segment display + +Return Value: + + NT status value + +--*/ +{ + NTSTATUS status; + WDF_USB_CONTROL_SETUP_PACKET controlSetupPacket; + WDF_REQUEST_SEND_OPTIONS sendOptions; + + WDF_MEMORY_DESCRIPTOR memDesc; + ULONG bytesTransferred; + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL, "GetSetSevenSegmentState: Enter\n"); + + PAGED_CODE(); + + WDF_REQUEST_SEND_OPTIONS_INIT( + &sendOptions, + WDF_REQUEST_SEND_OPTION_TIMEOUT + ); + + WDF_REQUEST_SEND_OPTIONS_SET_TIMEOUT( + &sendOptions, + DEFAULT_CONTROL_TRANSFER_TIMEOUT + ); + + WDF_USB_CONTROL_SETUP_PACKET_INIT_VENDOR(&controlSetupPacket, + BmRequestDeviceToHost, + BmRequestToDevice, + USBFX2LK_READ_7SEGMENT_DISPLAY, // Request + 0, // Value + 0); // Index + + // + // Set the buffer to 0, the board will OR in everything that is set + // + *SevenSegment = 0; + + WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&memDesc, + SevenSegment, + sizeof(UCHAR)); + + status = WdfUsbTargetDeviceSendControlTransferSynchronously( + DevContext->UsbDevice, + NULL, // Optional WDFREQUEST + &sendOptions, + &controlSetupPacket, + &memDesc, + &bytesTransferred); + + if(!NT_SUCCESS(status)) { + + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, + "GetSevenSegmentState: Failed to get 7 Segment state - 0x%x \n", status); + } else { + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL, + "GetSevenSegmentState: 7 Segment mask is 0x%x\n", *SevenSegment); + } + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL, "GetSetSevenSegmentState: Exit\n"); + + return status; + +} + +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +SetSevenSegmentState( + _In_ PDEVICE_CONTEXT DevContext, + _In_ PUCHAR SevenSegment + ) +/*++ + +Routine Description + + This routine sets the state of the 7 segment display on the board + +Arguments: + + DevContext - One of our device extensions + + SevenSegment - desired state of the 7 segment display + +Return Value: + + NT status value + +--*/ +{ + NTSTATUS status; + WDF_USB_CONTROL_SETUP_PACKET controlSetupPacket; + WDF_REQUEST_SEND_OPTIONS sendOptions; + WDF_MEMORY_DESCRIPTOR memDesc; + ULONG bytesTransferred; + + PAGED_CODE(); + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL, "--> SetSevenSegmentState\n"); + + WDF_REQUEST_SEND_OPTIONS_INIT( + &sendOptions, + WDF_REQUEST_SEND_OPTION_TIMEOUT + ); + + WDF_REQUEST_SEND_OPTIONS_SET_TIMEOUT( + &sendOptions, + DEFAULT_CONTROL_TRANSFER_TIMEOUT + ); + + WDF_USB_CONTROL_SETUP_PACKET_INIT_VENDOR(&controlSetupPacket, + BmRequestHostToDevice, + BmRequestToDevice, + USBFX2LK_SET_7SEGMENT_DISPLAY, // Request + 0, // Value + 0); // Index + + WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&memDesc, + SevenSegment, + sizeof(UCHAR)); + + status = WdfUsbTargetDeviceSendControlTransferSynchronously( + DevContext->UsbDevice, + NULL, // Optional WDFREQUEST + &sendOptions, + &controlSetupPacket, + &memDesc, + &bytesTransferred); + + if(!NT_SUCCESS(status)) { + + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, + "SetSevenSegmentState: Failed to set 7 Segment state - 0x%x \n", status); + + } else { + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL, + "SetSevenSegmentState: 7 Segment mask is 0x%x\n", *SevenSegment); + + } + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL, "<-- SetSevenSegmentState\n"); + + return status; + +} + +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +GetSwitchState( + _In_ PDEVICE_CONTEXT DevContext, + _In_ PSWITCH_STATE SwitchState + ) +/*++ + +Routine Description + + This routine gets the state of the switches on the board + +Arguments: + + DevContext - One of our device extensions + +Return Value: + + NT status value + +--*/ +{ + NTSTATUS status; + WDF_USB_CONTROL_SETUP_PACKET controlSetupPacket; + WDF_REQUEST_SEND_OPTIONS sendOptions; + WDF_MEMORY_DESCRIPTOR memDesc; + ULONG bytesTransferred; + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL, "--> GetSwitchState\n"); + + PAGED_CODE(); + + WDF_REQUEST_SEND_OPTIONS_INIT( + &sendOptions, + WDF_REQUEST_SEND_OPTION_TIMEOUT + ); + + WDF_REQUEST_SEND_OPTIONS_SET_TIMEOUT( + &sendOptions, + DEFAULT_CONTROL_TRANSFER_TIMEOUT + ); + + WDF_USB_CONTROL_SETUP_PACKET_INIT_VENDOR(&controlSetupPacket, + BmRequestDeviceToHost, + BmRequestToDevice, + USBFX2LK_READ_SWITCHES, // Request + 0, // Value + 0); // Index + + SwitchState->SwitchesAsUChar = 0; + + WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&memDesc, + SwitchState, + sizeof(SWITCH_STATE)); + + status = WdfUsbTargetDeviceSendControlTransferSynchronously( + DevContext->UsbDevice, + NULL, // Optional WDFREQUEST + &sendOptions, + &controlSetupPacket, + &memDesc, + &bytesTransferred); + + if(!NT_SUCCESS(status)) { + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, + "GetSwitchState: Failed to Get switches - 0x%x \n", status); + + } else { + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL, + "GetSwitchState: Switch mask is 0x%x\n", SwitchState->SwitchesAsUChar); + } + + TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL, "<-- GetSwitchState\n"); + + return status; + +} + + +VOID +OsrUsbIoctlGetInterruptMessage( + _In_ WDFDEVICE Device, + _In_ NTSTATUS ReaderStatus + ) +/*++ + +Routine Description + + This method handles the completion of the pended request for the IOCTL + IOCTL_OSRUSBFX2_GET_INTERRUPT_MESSAGE. + +Arguments: + + Device - Handle to a framework device. + +Return Value: + + None. + +--*/ +{ + NTSTATUS status; + WDFREQUEST request; + PDEVICE_CONTEXT pDevContext; + size_t bytesReturned = 0; + PSWITCH_STATE switchState = NULL; + + pDevContext = GetDeviceContext(Device); + + do { + + // + // Check if there are any pending requests in the Interrupt Message Queue. + // If a request is found then complete the pending request. + // + status = WdfIoQueueRetrieveNextRequest(pDevContext->InterruptMsgQueue, &request); + + if (NT_SUCCESS(status)) { + status = WdfRequestRetrieveOutputBuffer(request, + sizeof(SWITCH_STATE), + &switchState, + NULL);// BufferLength + + if (!NT_SUCCESS(status)) { + + TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, + "User's output buffer is too small for this IOCTL, expecting a SWITCH_STATE\n"); + bytesReturned = sizeof(SWITCH_STATE); + + } else { + + // + // Copy the state information saved by the continuous reader. + // + if (NT_SUCCESS(ReaderStatus)) { + switchState->SwitchesAsUChar = pDevContext->CurrentSwitchState; + bytesReturned = sizeof(SWITCH_STATE); + } else { + bytesReturned = 0; + } + } + + // + // Complete the request. If we failed to get the output buffer then + // complete with that status. Otherwise complete with the status from the reader. + // + WdfRequestCompleteWithInformation(request, + NT_SUCCESS(status) ? ReaderStatus : status, + bytesReturned); + status = STATUS_SUCCESS; + + } else if (status != STATUS_NO_MORE_ENTRIES) { + KdPrint(("WdfIoQueueRetrieveNextRequest status %08x\n", status)); + } + + request = NULL; + + } while (status == STATUS_SUCCESS); + + return; + +} + + diff --git a/usb/umdf_filter_kmdf/kmdf_driver/osrusbfx2.h b/usb/umdf_filter_kmdf/kmdf_driver/osrusbfx2.h new file mode 100644 index 000000000..909e347d4 --- /dev/null +++ b/usb/umdf_filter_kmdf/kmdf_driver/osrusbfx2.h @@ -0,0 +1,335 @@ +/*++ + +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: + + Contains structure definitions and function prototypes private to + the driver. + +Environment: + + Kernel mode + +--*/ + +#include +#include +#include "usbdi.h" +#include "usbdlib.h" +#include "public.h" +#include "driverspecs.h" +#include +#include +#define NTSTRSAFE_LIB +#include + +#include "trace.h" + +// +// Include auto-generated ETW event functions (created by MC.EXE from +// osrusbfx2.man) +// +#include "fx2Events.h" + +#ifndef _PRIVATE_H +#define _PRIVATE_H + +#define POOL_TAG (ULONG) 'FRSO' +#define _DRIVER_NAME_ "OSRUSBFX2" + +#define TEST_BOARD_TRANSFER_BUFFER_SIZE (64*1024) +#define DEVICE_DESC_LENGTH 256 + +extern const __declspec(selectany) LONGLONG DEFAULT_CONTROL_TRANSFER_TIMEOUT = 5 * -1 * WDF_TIMEOUT_TO_SEC; + +// +// Define the vendor commands supported by our device +// +#define USBFX2LK_READ_7SEGMENT_DISPLAY 0xD4 +#define USBFX2LK_READ_SWITCHES 0xD6 +#define USBFX2LK_READ_BARGRAPH_DISPLAY 0xD7 +#define USBFX2LK_SET_BARGRAPH_DISPLAY 0xD8 +#define USBFX2LK_IS_HIGH_SPEED 0xD9 +#define USBFX2LK_REENUMERATE 0xDA +#define USBFX2LK_SET_7SEGMENT_DISPLAY 0xDB + +// +// Define the features that we can clear +// and set on our device +// +#define USBFX2LK_FEATURE_EPSTALL 0x00 +#define USBFX2LK_FEATURE_WAKE 0x01 + +// +// Order of endpoints in the interface descriptor +// +#define INTERRUPT_IN_ENDPOINT_INDEX 0 +#define BULK_OUT_ENDPOINT_INDEX 1 +#define BULK_IN_ENDPOINT_INDEX 2 + +// +// A structure representing the instance information associated with +// this particular device. +// + +typedef struct _DEVICE_CONTEXT { + + WDFUSBDEVICE UsbDevice; + + WDFUSBINTERFACE UsbInterface; + + WDFUSBPIPE BulkReadPipe; + + WDFUSBPIPE BulkWritePipe; + + WDFUSBPIPE InterruptPipe; + + WDFWAITLOCK ResetDeviceWaitLock; + + UCHAR CurrentSwitchState; + + WDFQUEUE InterruptMsgQueue; + + ULONG UsbDeviceTraits; + + // + // The following fields are used during event logging to + // report the events relative to this specific instance + // of the device. + // + + WDFMEMORY DeviceNameMemory; + PCWSTR DeviceName; + + WDFMEMORY LocationMemory; + PCWSTR Location; + +} DEVICE_CONTEXT, *PDEVICE_CONTEXT; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(DEVICE_CONTEXT, GetDeviceContext) + +extern ULONG DebugLevel; + +typedef +NTSTATUS +(*PFN_IO_GET_ACTIVITY_ID_IRP) ( + _In_ PIRP Irp, + _Out_ LPGUID Guid + ); + +typedef +NTSTATUS +(*PFN_IO_SET_DEVICE_INTERFACE_PROPERTY_DATA) ( + _In_ PUNICODE_STRING SymbolicLinkName, + _In_ CONST DEVPROPKEY *PropertyKey, + _In_ LCID Lcid, + _In_ ULONG Flags, + _In_ DEVPROPTYPE Type, + _In_ ULONG Size, + _In_opt_ PVOID Data + ); + +// +// Global function pointer set in DriverEntry +// Check for NULL before using +// +extern PFN_IO_GET_ACTIVITY_ID_IRP g_pIoGetActivityIdIrp; + +extern PFN_IO_SET_DEVICE_INTERFACE_PROPERTY_DATA g_pIoSetDeviceInterfacePropertyData; + +DRIVER_INITIALIZE DriverEntry; + +EVT_WDF_OBJECT_CONTEXT_CLEANUP OsrFxEvtDriverContextCleanup; + +EVT_WDF_DRIVER_DEVICE_ADD OsrFxEvtDeviceAdd; + +EVT_WDF_DEVICE_PREPARE_HARDWARE OsrFxEvtDevicePrepareHardware; + +EVT_WDF_IO_QUEUE_IO_READ OsrFxEvtIoRead; + +EVT_WDF_IO_QUEUE_IO_WRITE OsrFxEvtIoWrite; + +EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL OsrFxEvtIoDeviceControl; + +EVT_WDF_REQUEST_COMPLETION_ROUTINE EvtRequestReadCompletionRoutine; + +EVT_WDF_REQUEST_COMPLETION_ROUTINE EvtRequestWriteCompletionRoutine; + +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +ResetPipe( + _In_ WDFUSBPIPE Pipe + ); + +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +ResetDevice( + _In_ WDFDEVICE Device + ); + +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +SelectInterfaces( + _In_ WDFDEVICE Device + ); + +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +ReenumerateDevice( + _In_ PDEVICE_CONTEXT DevContext + ); + +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +GetBarGraphState( + _In_ PDEVICE_CONTEXT DevContext, + _Out_ PBAR_GRAPH_STATE BarGraphState + ); + +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +SetBarGraphState( + _In_ PDEVICE_CONTEXT DevContext, + _In_ PBAR_GRAPH_STATE BarGraphState + ); + +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +GetSevenSegmentState( + _In_ PDEVICE_CONTEXT DevContext, + _Out_ PUCHAR SevenSegment + ); + +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +SetSevenSegmentState( + _In_ PDEVICE_CONTEXT DevContext, + _In_ PUCHAR SevenSegment + ); + +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +GetSwitchState( + _In_ PDEVICE_CONTEXT DevContext, + _In_ PSWITCH_STATE SwitchState + ); + +VOID +OsrUsbIoctlGetInterruptMessage( + _In_ WDFDEVICE Device, + _In_ NTSTATUS ReaderStatus + ); + +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +OsrFxSetPowerPolicy( + _In_ WDFDEVICE Device + ); + +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +OsrFxConfigContReaderForInterruptEndPoint( + _In_ PDEVICE_CONTEXT DeviceContext + ); + +EVT_WDF_USB_READER_COMPLETION_ROUTINE OsrFxEvtUsbInterruptPipeReadComplete; + +EVT_WDF_USB_READERS_FAILED OsrFxEvtUsbInterruptReadersFailed; + +EVT_WDF_IO_QUEUE_IO_STOP OsrFxEvtIoStop; + +EVT_WDF_DEVICE_D0_ENTRY OsrFxEvtDeviceD0Entry; + +EVT_WDF_DEVICE_D0_EXIT OsrFxEvtDeviceD0Exit; + +EVT_WDF_DEVICE_SELF_MANAGED_IO_FLUSH OsrFxEvtDeviceSelfManagedIoFlush; + +_IRQL_requires_(PASSIVE_LEVEL) +BOOLEAN +OsrFxReadFdoRegistryKeyValue( + _In_ PWDFDEVICE_INIT DeviceInit, + _In_ PWCHAR Name, + _Out_ PULONG Value + ); + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +OsrFxEnumerateChildren( + _In_ WDFDEVICE Device + ); + +_IRQL_requires_(PASSIVE_LEVEL) +VOID +GetDeviceEventLoggingNames( + _In_ WDFDEVICE Device + ); + +_IRQL_requires_(PASSIVE_LEVEL) +PCHAR +DbgDevicePowerString( + _In_ WDF_POWER_DEVICE_STATE Type + ); + + +_IRQL_requires_(PASSIVE_LEVEL) +USBD_STATUS +OsrFxValidateConfigurationDescriptor( + _In_reads_bytes_(BufferLength) PUSB_CONFIGURATION_DESCRIPTOR ConfigDesc, + _In_ ULONG BufferLength, + _Inout_ PUCHAR *Offset + ); + +FORCEINLINE +GUID +RequestToActivityId( + _In_ WDFREQUEST Request + ) +{ + GUID activity = {0}; + NTSTATUS status = STATUS_SUCCESS; + + if (g_pIoGetActivityIdIrp != NULL) { + + // + // Use activity ID generated by application (or IO manager) + // + status = g_pIoGetActivityIdIrp(WdfRequestWdmGetIrp(Request), &activity); + } + + if (g_pIoGetActivityIdIrp == NULL || !NT_SUCCESS(status)) { + + // + // Fall back to using the WDFREQUEST handle as the activity ID + // + RtlCopyMemory(&activity, &Request, sizeof(WDFREQUEST)); + } + + + return activity; +} + +FORCEINLINE +GUID +DeviceToActivityId( + _In_ WDFDEVICE Device + ) +{ + GUID activity = {0}; + RtlCopyMemory(&activity, &Device, sizeof(WDFDEVICE)); + return activity; +} + + +#endif + + diff --git a/usb/umdf_filter_kmdf/kmdf_driver/osrusbfx2.man b/usb/umdf_filter_kmdf/kmdf_driver/osrusbfx2.man new file mode 100644 index 000000000..176dfc896 --- /dev/null +++ b/usb/umdf_filter_kmdf/kmdf_driver/osrusbfx2.man @@ -0,0 +1,309 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/usb/umdf_filter_kmdf/kmdf_driver/osrusbfx2.rc b/usb/umdf_filter_kmdf/kmdf_driver/osrusbfx2.rc new file mode 100644 index 000000000..092ec1957 --- /dev/null +++ b/usb/umdf_filter_kmdf/kmdf_driver/osrusbfx2.rc @@ -0,0 +1,18 @@ +#include + +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "WDF Sample Driver for OSR USB-FX2 Learning Kit" +#define VER_INTERNALNAME_STR "osrusbfx2.sys" +#define VER_ORIGINALFILENAME_STR "osrusbfx2.sys" + +#include "common.ver" + +// +// Include auto-generated string resources (created by MC.EXE from +// osrusbfx2.man) +// + +#include "fx2Events.rc" diff --git a/usb/umdf_filter_kmdf/kmdf_driver/osrusbfx2.vcxproj b/usb/umdf_filter_kmdf/kmdf_driver/osrusbfx2.vcxproj new file mode 100644 index 000000000..979852e9c --- /dev/null +++ b/usb/umdf_filter_kmdf/kmdf_driver/osrusbfx2.vcxproj @@ -0,0 +1,244 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {6ABDB82A-B2BA-4EE3-A4CC-C0B00B7B5220} + $(MSBuildProjectName) + 1 + false + true + Debug + Win32 + {18C4C80D-74E9-4145-BD1A-9525E8F04FCF} + + + + 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 + true + .\$(IntDir) + true + .\$(IntDir) + true + fx2Events + true + + + true + true + TraceEvents(LEVEL,FLAGS,MSG,...) + {km-WdfDefault.tpl}*.tmh + + + + osrusbfx2 + + + osrusbfx2 + + + osrusbfx2 + + + osrusbfx2 + + + + %(AdditionalIncludeDirectories);..\inc + %(PreprocessorDefinitions);EVENT_TRACING + + + %(AdditionalIncludeDirectories);..\inc + true + Level4 + %(PreprocessorDefinitions);EVENT_TRACING + + + %(AdditionalIncludeDirectories);..\inc + %(PreprocessorDefinitions);EVENT_TRACING + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ntstrsafe.lib;$(DDK_LIB_PATH)\usbd.lib + + + + + %(AdditionalIncludeDirectories);..\inc + %(PreprocessorDefinitions);EVENT_TRACING + + + %(AdditionalIncludeDirectories);..\inc + true + Level4 + %(PreprocessorDefinitions);EVENT_TRACING + + + %(AdditionalIncludeDirectories);..\inc + %(PreprocessorDefinitions);EVENT_TRACING + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ntstrsafe.lib;$(DDK_LIB_PATH)\usbd.lib + + + + + %(AdditionalIncludeDirectories);..\inc + %(PreprocessorDefinitions);EVENT_TRACING + + + %(AdditionalIncludeDirectories);..\inc + true + Level4 + %(PreprocessorDefinitions);EVENT_TRACING + + + %(AdditionalIncludeDirectories);..\inc + %(PreprocessorDefinitions);EVENT_TRACING + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ntstrsafe.lib;$(DDK_LIB_PATH)\usbd.lib + + + + + %(AdditionalIncludeDirectories);..\inc + %(PreprocessorDefinitions);EVENT_TRACING + + + %(AdditionalIncludeDirectories);..\inc + true + Level4 + %(PreprocessorDefinitions);EVENT_TRACING + + + %(AdditionalIncludeDirectories);..\inc + %(PreprocessorDefinitions);EVENT_TRACING + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\ntstrsafe.lib;$(DDK_LIB_PATH)\usbd.lib + + + + 1 + + + 1 + + + 1 + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/usb/umdf_filter_kmdf/kmdf_driver/osrusbfx2.vcxproj.Filters b/usb/umdf_filter_kmdf/kmdf_driver/osrusbfx2.vcxproj.Filters new file mode 100644 index 000000000..13f23accf --- /dev/null +++ b/usb/umdf_filter_kmdf/kmdf_driver/osrusbfx2.vcxproj.Filters @@ -0,0 +1,46 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {7BBF30E5-9EE0-4544-BC21-3F9BBA4719F6} + + + h;hpp;hxx;hm;inl;inc;xsd + {F4DA0DF1-D522-4DE8-A128-0E3856D30A60} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {86AACBD7-6DA3-4FF9-850F-FD8BA69A2316} + + + inf;inv;inx;mof;mc; + {C3F34238-DF88-433F-9C43-9B4DA776DCB5} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Resource Files + + + Resource Files + + + \ No newline at end of file diff --git a/usb/umdf_filter_kmdf/kmdf_driver/trace.h b/usb/umdf_filter_kmdf/kmdf_driver/trace.h new file mode 100644 index 000000000..518ff5f17 --- /dev/null +++ b/usb/umdf_filter_kmdf/kmdf_driver/trace.h @@ -0,0 +1,115 @@ +/*++ + +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 !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_IOCTL 0x00000020 +#define DBG_WRITE 0x00000040 +#define DBG_READ 0x00000080 + + +VOID +TraceEvents ( + _In_ ULONG DebugPrintLevel, + _In_ ULONG DebugPrintFlag, + _Printf_format_string_ + _In_ PCSTR DebugMessage, + ... + ); + +#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. +// +// NOTE: If you are adopting this sample for your driver, please generate +// a new guid, using tools\other\i386\guidgen.exe present in the +// DDK. +// +// Name of the logger is OSRUSBFX2 and the guid is +// {D23A0C5A-D307-4f0e-AE8E-E2A355AD5DAB} +// (0xd23a0c5a, 0xd307, 0x4f0e, 0xae, 0x8e, 0xe2, 0xa3, 0x55, 0xad, 0x5d, 0xab); +// + +#define WPP_CHECK_FOR_NULL_STRING //to prevent exceptions due to NULL strings + +#define WPP_CONTROL_GUIDS \ + WPP_DEFINE_CONTROL_GUID(OsrUsbFxTraceGuid,(d23a0c5a,d307,4f0e,ae8e,E2A355AD5DAB), \ + 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_IOCTL) /* bit 5 = 0x00000020 */ \ + WPP_DEFINE_BIT(DBG_WRITE) /* bit 6 = 0x00000040 */ \ + WPP_DEFINE_BIT(DBG_READ) /* bit 7 = 0x00000080 */ \ + /* 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) + + +#endif + + + diff --git a/usb/umdf_filter_kmdf/umdf_filter/OsrUsbFilter.rc b/usb/umdf_filter_kmdf/umdf_filter/OsrUsbFilter.rc new file mode 100644 index 000000000..cec032d97 --- /dev/null +++ b/usb/umdf_filter_kmdf/umdf_filter/OsrUsbFilter.rc @@ -0,0 +1,17 @@ +//--------------------------------------------------------------------------- +// OsrUsbFilter.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 OsrUsbFilter User-Mode Driver Sample" +#define VER_INTERNALNAME_STR "OsrUsbFilter" +#define VER_ORIGINALFILENAME_STR "OsrUsbFilter.dll" + +#include "common.ver" diff --git a/usb/umdf_filter_kmdf/umdf_filter/WUDFOsrUsbFilter.vcxproj b/usb/umdf_filter_kmdf/umdf_filter/WUDFOsrUsbFilter.vcxproj new file mode 100644 index 000000000..a0df5d882 --- /dev/null +++ b/usb/umdf_filter_kmdf/umdf_filter/WUDFOsrUsbFilter.vcxproj @@ -0,0 +1,286 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {BE14DB05-36A7-4ECE-88E1-FB8157CA568E} + $(MSBuildProjectName) + 1 + 1 + false + true + Debug + Win32 + {248CCFA9-D129-42B1-A548-A0C5CD4F8AFE} + + + + 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)\WUDFOsrUsbFilterOnKmDriver.Inf + + + true + true + internal.h + + + + WUDFOsrUsbFilter + + + WUDFOsrUsbFilter + + + WUDFOsrUsbFilter + + + WUDFOsrUsbFilter + + + + %(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 + + + + + %(AdditionalIncludeDirectories);..\..\..\inc + + + %(AdditionalIncludeDirectories);..\..\..\inc + true + Level4 + + + + + %(AdditionalIncludeDirectories);..\..\..\inc + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\strsafe.lib;$(SDK_LIB_PATH)\kernel32.lib;$(SDK_LIB_PATH)\advapi32.lib + exports.def + + + + + %(AdditionalIncludeDirectories);..\..\..\inc + + + %(AdditionalIncludeDirectories);..\..\..\inc + true + Level4 + + + + + %(AdditionalIncludeDirectories);..\..\..\inc + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\strsafe.lib;$(SDK_LIB_PATH)\kernel32.lib;$(SDK_LIB_PATH)\advapi32.lib + exports.def + + + + + %(AdditionalIncludeDirectories);..\..\..\inc + + + %(AdditionalIncludeDirectories);..\..\..\inc + true + Level4 + + + + + %(AdditionalIncludeDirectories);..\..\..\inc + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\strsafe.lib;$(SDK_LIB_PATH)\kernel32.lib;$(SDK_LIB_PATH)\advapi32.lib + exports.def + + + + + %(AdditionalIncludeDirectories);..\..\..\inc + + + %(AdditionalIncludeDirectories);..\..\..\inc + true + Level4 + + + + + %(AdditionalIncludeDirectories);..\..\..\inc + + + %(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/usb/umdf_filter_kmdf/umdf_filter/WUDFOsrUsbFilter.vcxproj.Filters b/usb/umdf_filter_kmdf/umdf_filter/WUDFOsrUsbFilter.vcxproj.Filters new file mode 100644 index 000000000..ebc49f02b --- /dev/null +++ b/usb/umdf_filter_kmdf/umdf_filter/WUDFOsrUsbFilter.vcxproj.Filters @@ -0,0 +1,51 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {80542188-20E5-48F5-9843-73E5C5357618} + + + h;hpp;hxx;hm;inl;inc;xsd + {06DDFA1D-9B66-4847-90DA-9B47DAA6BB4D} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {CFF95FF0-CB6A-408A-BDEB-2EB542DABA61} + + + inf;inv;inx;mof;mc; + {9E1415BD-A138-4C82-9F05-75AAEFB9ADC2} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Driver Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/usb/umdf_filter_kmdf/umdf_filter/WUDFOsrUsbFilterOnKmDriver.inx b/usb/umdf_filter_kmdf/umdf_filter/WUDFOsrUsbFilterOnKmDriver.inx new file mode 100644 index 000000000..3ee5cd163 --- /dev/null +++ b/usb/umdf_filter_kmdf/umdf_filter/WUDFOsrUsbFilterOnKmDriver.inx @@ -0,0 +1,122 @@ +; +; INF for installing OSR USB user-mode filter driver on top of OSR USB KMDF (final) sample driver +; You will need to have the user-mode filter driver and KMDF function driver in one install location +; The KMDF files needed are: +; osrusbfx2.sys (final version) +; WdfCoInstallerxxxx.dll (the KMDF coinstaller) +; + +[Version] +Signature="$WINDOWS NT$" +Class=Sample +ClassGuid={78A1C341-4539-11d3-B88D-00C04FAD5171} +Provider=%ProviderName% +DriverVer=10/01/2002,6.0.5058.0 +CatalogFile=wudf.cat + + +; ================= Class section ===================== + +[ClassInstall32] +Addreg=SampleClassReg + +[SampleClassReg] +HKR,,,0,%ClassName% +HKR,,Icon,,-5 + + +; ================= Device section ===================== + +[Manufacturer] +%ManufacturerName%=Standard,NT$ARCH$ + +; For XP and later +[Standard.NT$ARCH$] +%USB\VID_045E&PID_930A.DeviceDesc%=osrusbfx2, USB\VID_0547&PID_1002 + +[osrusbfx2.NT] +CopyFiles=osrusbfx2.Files.Ext,UMDriverCopy + +[osrusbfx2.NT.hw] +AddReg=OsrUsb_AddReg + + +[osrusbfx2.NT.CoInstallers] +AddReg=CoInstaller_AddReg +CopyFiles=CoInstaller_CopyFiles + +[osrusbfx2.NT.Services] +AddService=WUDFRd,0x000001f8,WUDFRD_ServiceInstall +AddService=osrusbfx2, 0x000001fa, osrusbfx2.AddService ;flag 0x2 sets this as the service for the device + +[osrusbfx2.NT.Wdf] +KmdfService=osrusbfx2, osrusbfx2_wdfsect +UmdfService="WUDFOsrUsbFilter", WudfOsrUsbFilter_Install +UmdfServiceOrder=WUDFOsrUsbFilter + +[OsrUsb_AddReg] +HKR,,"UpperFilters",0x00010008,"WUDFRd" ; FLG_ADDREG_TYPE_MULTI_SZ | FLG_ADDREG_APPEND + + +[WudfOsrUsbFilter_Install] +UmdfLibraryVersion=$UMDFVERSION$ +DriverCLSID = "{422d8dbc-520d-4d7e-8f53-920e5c867e6c}" +ServiceBinary = "%12%\UMDF\WUDFOsrUsbFilter.dll" + +[osrusbfx2_wdfsect] +KmdfLibraryVersion=1.11 + +[WUDFRD_ServiceInstall] +DisplayName = %WudfRdDisplayName% +ServiceType = 1 +StartType = 3 +ErrorControl = 1 +ServiceBinary = %12%\WUDFRd.sys + +[osrusbfx2.AddService] +DisplayName = %osrusbfx2.SvcDesc% +ServiceType = 1 ; SERVICE_KERNEL_DRIVER +StartType = 3 ; SERVICE_DEMAND_START +ErrorControl = 1 ; SERVICE_ERROR_NORMAL +ServiceBinary = %10%\System32\Drivers\osrusbfx2.sys + +[osrusbfx2.Files.Ext] +osrusbfx2.sys + +[UMDriverCopy] +WudfOsrUsbFilter.dll + +[SourceDisksNames] +1=%Disk_Description%,,, + +[SourceDisksFiles] +osrusbfx2.sys = 1 +WudfOsrUsbFilter.dll=1 +WudfUpdate_$UMDFCOINSTALLERVERSION$.dll=1 + +[DestinationDirs] +UMDriverCopy=12,UMDF ; copy to drivers\umdf +DefaultDestDir = 12 + +;-------------- WDF Coinstaller installation + +[DestinationDirs] +CoInstaller_CopyFiles = 11 + +[CoInstaller_CopyFiles] +WudfUpdate_$UMDFCOINSTALLERVERSION$.dll + +[CoInstaller_AddReg] +HKR,,CoInstallers32,0x00010000,"WudfUpdate_$UMDFCOINSTALLERVERSION$.dll" + +;---------------------------------------------------------------; + +[Strings] +ProviderName="TODO-Set-Provider" +ManufacturerName="TODO-Set-Manufacturer" +Disk_Description="OSRUSBFX2 Installation Disk" +USB\VID_045E&PID_930A.DeviceDesc="Microsoft UMDF OSR Usb Sample Device With Filter on kernel-mode Driver" +osrusbfx2.SvcDesc="WDF Sample Driver for OSR USB-FX2 Learning Kit" +ClassName = "Sample Device" +WudfRdDisplayName="Windows Driver Foundation - User-mode Driver Framework Reflector" + diff --git a/usb/umdf_filter_kmdf/umdf_filter/comsup.cpp b/usb/umdf_filter_kmdf/umdf_filter/comsup.cpp new file mode 100644 index 000000000..31257f318 --- /dev/null +++ b/usb/umdf_filter_kmdf/umdf_filter/comsup.cpp @@ -0,0 +1,351 @@ +/*++ + +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" + +// +// This is the number of characters in a GUID string including the trailing +// NULL. +// + +#define GUID_STRING_CCH (sizeof("{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}")) + +// +// 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/usb/umdf_filter_kmdf/umdf_filter/comsup.h b/usb/umdf_filter_kmdf/umdf_filter/comsup.h new file mode 100644 index 000000000..b96fd9828 --- /dev/null +++ b/usb/umdf_filter_kmdf/umdf_filter/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/usb/umdf_filter_kmdf/umdf_filter/device.cpp b/usb/umdf_filter_kmdf/umdf_filter/device.cpp new file mode 100644 index 000000000..126637ce0 --- /dev/null +++ b/usb/umdf_filter_kmdf/umdf_filter/device.cpp @@ -0,0 +1,243 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved. + +Module Name: + + Device.cpp + +Abstract: + + This module contains the implementation of the UMDF OSR USB Sample Filter driver's + device callback object. + +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 OSR USB Sample Filter 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. + // + + // + // We don't need device level locking since we do not keep any state + // across the requests + // + + FxDeviceInit->SetLockingConstraint(None); + + // + // Mark ourselves as a filter + // + + FxDeviceInit->SetFilter(); + + + // + // We are a filter; we don't want to be the power policy owner + // + + FxDeviceInit->SetPowerPolicyOwnership(FALSE); + + // + // 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(); + + defaultQueue->Release(); + + 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 OSR USB Sample Filter driver doesn't support any of the device events, this + method simply calls the base class's BaseQueryInterface. + + If OSR USB Sample Filter 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/usb/umdf_filter_kmdf/umdf_filter/device.h b/usb/umdf_filter_kmdf/umdf_filter/device.h new file mode 100644 index 000000000..f7a50b1d0 --- /dev/null +++ b/usb/umdf_filter_kmdf/umdf_filter/device.h @@ -0,0 +1,114 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved + +Module Name: + + Device.h + +Abstract: + + This module contains the type definitions for the UMDF OSR USB Sample Filter + 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/usb/umdf_filter_kmdf/umdf_filter/dllsup.cpp b/usb/umdf_filter_kmdf/umdf_filter/dllsup.cpp new file mode 100644 index 000000000..10ef8a782 --- /dev/null +++ b/usb/umdf_filter_kmdf/umdf_filter/dllsup.cpp @@ -0,0 +1,183 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved. + +Module Name: + + dllsup.cpp + +Abstract: + + This module contains the implementation of the OSR USB Sample Filter + 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 OSR USB Sample Filter 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; + +// +// Global variable to hold the module handle for this DLL. Initialized during +// DllMain and never cleared. This is used when registering and unregistering +// the driver's COM information. +// + +HINSTANCE g_ModuleHandle = NULL; + +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 + +--*/ +{ + + 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 OSR USB Sample + Filter 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/usb/umdf_filter_kmdf/umdf_filter/driver.cpp b/usb/umdf_filter_kmdf/umdf_filter/driver.cpp new file mode 100644 index 000000000..c5910c05e --- /dev/null +++ b/usb/umdf_filter_kmdf/umdf_filter/driver.cpp @@ -0,0 +1,207 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved. + +Module Name: + + Driver.cpp + +Abstract: + + This module contains the implementation of the UMDF OSR USB Sample Filter + driver'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; + + // + // Create a new instance of our device callback object + // + + hr = CMyDevice::CreateInstance(FxWdfDriver, FxDeviceInit, &device); + + // + // 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/usb/umdf_filter_kmdf/umdf_filter/driver.h b/usb/umdf_filter_kmdf/umdf_filter/driver.h new file mode 100644 index 000000000..08f36a712 --- /dev/null +++ b/usb/umdf_filter_kmdf/umdf_filter/driver.h @@ -0,0 +1,145 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved + +Module Name: + + Driver.h + +Abstract: + + This module contains the type definitions for the UMDF OSR USB Sample Filter + driver's callback class. + +Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + +#pragma once + +// +// This class handles driver events for the OSR USB Sample Filter driver. 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*/ + ) + { + return S_OK; + } + + virtual + HRESULT + STDMETHODCALLTYPE + OnDeviceAdd( + _In_ IWDFDriver *FxWdfDriver, + _In_ IWDFDeviceInitialize *FxDeviceInit + ); + + virtual + VOID + STDMETHODCALLTYPE + OnDeinitialize( + _In_ IWDFDriver * /*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/usb/umdf_filter_kmdf/umdf_filter/exports.def b/usb/umdf_filter_kmdf/umdf_filter/exports.def new file mode 100644 index 000000000..0fc428179 --- /dev/null +++ b/usb/umdf_filter_kmdf/umdf_filter/exports.def @@ -0,0 +1,4 @@ +; Skeleton.def : Declares the module parameters. + +EXPORTS + DllGetClassObject PRIVATE diff --git a/usb/umdf_filter_kmdf/umdf_filter/internal.h b/usb/umdf_filter_kmdf/umdf_filter/internal.h new file mode 100644 index 000000000..516f6d7fa --- /dev/null +++ b/usb/umdf_filter_kmdf/umdf_filter/internal.h @@ -0,0 +1,111 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved + +Module Name: + + Internal.h + +Abstract: + + This module contains the local type definitions for the UMDF OSR USB Sample Filter + driver. + +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 headers +// + +#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. +// + +#define WPP_CONTROL_GUIDS \ + WPP_DEFINE_CONTROL_GUID( \ + MyDriverTraceControl, (73cdcaa5,ce52,43f2,aa2d,5f5a84e22213), \ + \ + 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\\OsrUsbFilter" +#define MYDRIVER_COM_DESCRIPTION L"UMDF OSR USB Sample Filter Driver" +#define MYDRIVER_CLASS_ID {0x422d8dbc, 0x520d, 0x4d7e, {0x8f, 0x53, 0x92, 0x0e, 0x5c, 0x86, 0x7e, 0x6c}} + +// +// 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_SAMPLE_DRIVER_ASSERT(p) \ +{ \ + if ( !(p) ) \ + { \ + DebugBreak(); \ + WdfTestNoReturn(); \ + } \ +} + +#define SAFE_RELEASE(p) {if ((p)) { (p)->Release(); (p) = NULL; }} diff --git a/usb/umdf_filter_kmdf/umdf_filter/queue.cpp b/usb/umdf_filter_kmdf/umdf_filter/queue.cpp new file mode 100644 index 000000000..5642ec744 --- /dev/null +++ b/usb/umdf_filter_kmdf/umdf_filter/queue.cpp @@ -0,0 +1,538 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved. + +Module Name: + + Queue.cpp + +Abstract: + + This module contains the implementation of the OSR USB Filter Sample driver's + queue callback object. + +Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + +#include "internal.h" +#include "queue.h" + +#include "queue.tmh" + +HRESULT +CMyQueue::CreateInstance( + _In_ IWDFDevice * FxDevice, + _Out_ CMyQueue **Queue + ) +/*++ + + Routine Description: + + This method creates and initializs an instance of the OSR USB Filter Sample 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 + +--*/ +{ + CMyQueue *queue; + + HRESULT hr = S_OK; + + // + // Allocate a new instance of the device class. + // + + queue = new CMyQueue(); + + if (NULL == queue) + { + hr = E_OUTOFMEMORY; + } + + // + // Initialize the instance. + // + + if (SUCCEEDED(hr)) + { + hr = queue->Initialize(FxDevice); + } + + if (SUCCEEDED(hr)) + { + queue->AddRef(); + *Queue = queue; + } + + if (NULL != queue) + { + queue->Release(); + } + + return hr; +} + +HRESULT +CMyQueue::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. + + 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(IQueueCallbackDefaultIoHandler))) + { + hr = S_OK; + *Object = QueryIQueueCallbackDefaultIoHandler(); + } + else if (IsEqualIID(InterfaceId, __uuidof(IQueueCallbackWrite))) + { + hr = S_OK; + *Object = QueryIQueueCallbackWrite(); + } + else if (IsEqualIID(InterfaceId, __uuidof(IRequestCallbackRequestCompletion))) + { + hr = S_OK; + *Object = QueryIRequestCallbackRequestCompletion(); + + } + else + { + hr = CUnknown::QueryInterface(InterfaceId, Object); + } + + return hr; +} + +HRESULT +CMyQueue::Initialize( + _In_ IWDFDevice *FxDevice + ) +/*++ + + Routine Description: + + This method initializes the device callback object. Any operations which + need to be performed before the caller can use the callback object, but + which couldn't be done in the constructor becuase they could fail would + be placed here. + + Arguments: + + FxDevice - the device which this Queue is for. + + Return Value: + + status. + +--*/ +{ + IWDFIoQueue *fxQueue; + HRESULT hr; + + // + // Create the framework queue + // + + IUnknown *unknown = QueryIUnknown(); + hr = FxDevice->CreateIoQueue( + unknown, + TRUE, // bDefaultQueue + WdfIoQueueDispatchParallel, + FALSE, // bPowerManaged + TRUE, // bAllowZeroLengthRequests + &fxQueue + ); + if (FAILED(hr)) + { + Trace( + TRACE_LEVEL_ERROR, + "%!FUNC!: Could not create default I/O queue, %!hresult!", + hr + ); + } + + unknown->Release(); + + if (SUCCEEDED(hr)) + { + m_FxQueue = fxQueue; + + // + // m_FxQueue is kept as a Weak reference to framework Queue object to avoid + // circular reference. This object's lifetime is contained within + // framework Queue object's lifetime + // + + fxQueue->Release(); + } + + if (SUCCEEDED(hr)) + { + FxDevice->GetDefaultIoTarget(&m_FxIoTarget); + } + + return hr; +} + +void +CMyQueue::InvertBits( + _Inout_ IWDFMemory* FxMemory, + _In_ SIZE_T NumBytes + ) +/*++ + + Routine Description: + + This helper method inverts bits in the buffer of an FxMemory object + + Arguments: + + FxMemory - Framework memory object whose buffer's bits are to be inverted + + NumBytes - Number of bytes for which bits are to be inverted + + Return Value: + + None + +--*/ +{ + PBYTE Buffer = (PBYTE) + FxMemory->GetDataBuffer(NULL); + + for (SIZE_T i = 0; i < NumBytes; i++) + { + memset(Buffer + i, ~(Buffer[i]), sizeof(*Buffer)); + } +} + +void +CMyQueue::OnWrite( + _In_ IWDFIoQueue* FxQueue, + _In_ IWDFIoRequest* FxRequest, + _In_ SIZE_T NumOfBytesToWrite + ) +/*++ + + Routine Description: + + This method is called by Framework Queue object to deliver the Write request + This method inverts the bits in write buffer and forwards the request down the device stack + In case of any failure prior to ForwardRequest, it completets the request with failure + + Arguments: + + pWdfQueue - Framework Queue which is delivering the request + + pWdfRequest - Framework Request + + Return Value: + + None + +--*/ +{ + UNREFERENCED_PARAMETER(FxQueue); + + IWDFMemory * FxInputMemory = NULL; + + FxRequest->GetInputMemory(&FxInputMemory); + + // + // Invert bits of the buffer to be written to device + // + + InvertBits(FxInputMemory, NumOfBytesToWrite); + + // + // Forward request down the stack + // When the device below completes the request we will get notified in OnComplete + // and then we will complete the request + // + + ForwardRequest(FxRequest); + + FxInputMemory->Release(); +} + +// +// IQueueCallbackDefaultIoHandler method +// + +void +CMyQueue::OnDefaultIoHandler( + _In_ IWDFIoQueue* FxQueue, + _In_ IWDFIoRequest* FxRequest + ) +/*++ + + Routine Description: + + This method is called by Framework Queue object to deliver all the I/O + Requests for which we do not have a specific handler + (In our case anything other than Write) + + Arguments: + + pWdfQueue - Framework Queue which is delivering the request + + pWdfRequest - Framework Request + + Return Value: + + None + +--*/ +{ + UNREFERENCED_PARAMETER(FxQueue); + + // + // We just forward the request down the stack + // When the device below completes the request we will get notified in OnComplete + // and then we will complete the request + // + + ForwardRequest(FxRequest); +} + +void +CMyQueue::ForwardRequest( + _In_ IWDFIoRequest* FxRequest + ) +/*++ + + Routine Description: + + This helper method forwards the request down the stack + + Arguments: + + pWdfRequest - Request to be forwarded + + Return Value: + + None + + Remarks: + + The request gets forwarded to the next device in the stack which can be: + 1. Next device in user-mode stack + 2. Top device in kernel-mode stack (Redirector's Down Device) + + In this routine we: + 1. Set a completion callback + 2. Copy request parameters to next stack location + 3. Asynchronously send the request without any timeout + + When the lower request gets completed we will be notified via the + completion callback, where we will complete our request + + In case of failure this routine completes the request + +--*/ +{ + // + //First set the completion callback + // + + IRequestCallbackRequestCompletion *completionCallback = + QueryIRequestCallbackRequestCompletion(); + + FxRequest->SetCompletionCallback( + completionCallback, + NULL //pContext + ); + + completionCallback->Release(); + + // + //Copy current i/o stack locations parameters to the next stack location + // + + FxRequest->FormatUsingCurrentType( + ); + + // + //Send down the request + // + HRESULT hrSend = S_OK; + + hrSend = FxRequest->Send( + m_FxIoTarget, + 0, //No flag + 0 //No timeout + ); + + if (FAILED(hrSend)) + { + // + //If send failed we need to complete the request with failure + // + FxRequest->CompleteWithInformation(hrSend, 0); + } + + return; +} + +void +CMyQueue::HandleReadRequestCompletion( + IWDFIoRequest* FxRequest, + IWDFIoRequestCompletionParams* CompletionParams + ) +/*++ + + Routine Description: + + This helper method is called by OnCompletion method to complete Read request + We invert the bits in the read buffer + This is so that the client reads back the data it wrote since + we inverted bits during write to device + + Arguments: + + FxRequest - Request object of our layer + + CompletionParams - Parameters with which the lower Request got completed + + Return Value: + + None + + Remarks: + + This method always completes the request since no one else would get a chance to + complete the request + In case of failure it completes the request with failure + +--*/ +{ + HRESULT hrCompletion = CompletionParams->GetCompletionStatus(); + ULONG_PTR BytesRead = CompletionParams->GetInformation(); + + // + // Check + // 1. whether the lower device succeeded the Request (otherwise we will just complete + // the Request with failure + // 2. If data read is of non-zero length, for us to bother to invert its bits + // + + if (SUCCEEDED(hrCompletion) && + (0 != BytesRead) + ) + { + IWDFMemory *FxOutputMemory; + + FxRequest->GetOutputMemory(&FxOutputMemory ); + + InvertBits(FxOutputMemory, BytesRead); + + FxOutputMemory->Release(); + } + + // + // Complete the request + // + + FxRequest->CompleteWithInformation( + hrCompletion, + BytesRead + ); +} + + +void +CMyQueue::OnCompletion( + IWDFIoRequest* FxRequest, + IWDFIoTarget* FxIoTarget, + IWDFRequestCompletionParams* CompletionParams, + PVOID Context + ) +/*++ + + Routine Description: + + This method is called by Framework I/O Target object when + the lower device completets the Request + + Arguments: + + pWdfRequest - Request object of our layer + + pIoTarget - I/O Target object invoking this callback + + pParams - Parameters with which the lower Request got completed + + Return Value: + + None + +--*/ +{ + UNREFERENCED_PARAMETER(FxIoTarget); + UNREFERENCED_PARAMETER(Context); + + // + // If it is a read request, we invert the bits read since we inverted them during write + // so that application would read the same data as it wrote + // + + if (WdfRequestRead == FxRequest->GetType()) + { + IWDFIoRequestCompletionParams * IoCompletionParams = NULL; + HRESULT hrQI = CompletionParams->QueryInterface(IID_PPV_ARGS(&IoCompletionParams)); + WUDF_SAMPLE_DRIVER_ASSERT(SUCCEEDED(hrQI)); + + HandleReadRequestCompletion( + FxRequest, + IoCompletionParams + ); + + SAFE_RELEASE(IoCompletionParams); + } + else + { + + // + // Otherwise we just complete our Request object with the same parameters + // with which the lower Request got completed + // + + FxRequest->CompleteWithInformation( + CompletionParams->GetCompletionStatus(), + CompletionParams->GetInformation() + ); + } +} + diff --git a/usb/umdf_filter_kmdf/umdf_filter/queue.h b/usb/umdf_filter_kmdf/umdf_filter/queue.h new file mode 100644 index 000000000..216264e93 --- /dev/null +++ b/usb/umdf_filter_kmdf/umdf_filter/queue.h @@ -0,0 +1,253 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved + +Module Name: + + Queue.h + +Abstract: + + This module contains the type definitions for the OSR USB Filter Sample + driver's queue callback class. + +Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + +#pragma once + +// +// Class for the queue callbacks. +// It implements +// IQueueCallbackDeviceIoControl +// IRequestCallbackRequestCompletion +// Queue callbacks +// +// This class also implements IRequestCallbackRequestCompletion callback +// to get the request completion notification when request is sent down the +// stack. This callback can be implemented on a separate object as well. +// This callback is implemented here only for conenience. +// +class CMyQueue : + public CUnknown, + public IQueueCallbackWrite, + public IQueueCallbackDefaultIoHandler, + public IRequestCallbackRequestCompletion +{ + +// +// Private data members. +// +private: + + // + // Weak reference to framework Queue object which this object implements callbacks for + // This is kept as a weak reference to avoid circular reference + // This object's lifetime is contained within framework Queue object's lifetime + // + + IWDFIoQueue *m_FxQueue; + + // + // I/O Target to which we forward requests. Represents next device in the + // device stack + // + + IWDFIoTarget *m_FxIoTarget; + +// +// Private methods. +// + +private: + + CMyQueue() : + m_FxQueue(NULL), + m_FxIoTarget(NULL) + { + } + + virtual ~CMyQueue() + { + if (NULL != m_FxIoTarget) + { + m_FxIoTarget->Release(); + } + } + + // + // QueryInterface helpers + // + + IRequestCallbackRequestCompletion * + QueryIRequestCallbackRequestCompletion( + VOID + ) + { + AddRef(); + return static_cast(this); + } + + IQueueCallbackWrite * + QueryIQueueCallbackWrite( + VOID + ) + { + AddRef(); + return static_cast(this); + } + + IQueueCallbackDefaultIoHandler * + QueryIQueueCallbackDefaultIoHandler( + VOID + ) + { + AddRef(); + return static_cast(this); + } + + // + // Initialize + // + + HRESULT + Initialize( + _In_ IWDFDevice *FxDevice + ); + + // + // Helper method to forward request down the stack + // + + void + ForwardRequest( + _In_ IWDFIoRequest *pWdfRequest + ); + + // + // Helper method to inverts bits in the buffer of a framework Memory object + // + + void + InvertBits( + _Inout_ IWDFMemory* FxMemory, + _In_ SIZE_T NumBytes + ); + + // + // Helper method to handle Read request completion + // + + void + HandleReadRequestCompletion( + IWDFIoRequest* FxRequest, + IWDFIoRequestCompletionParams* CompletionParams + ); + +// +// Public methods +// +public: + + // + // The factory method used to create an instance of this class + // + + static + HRESULT + CreateInstance( + _In_ IWDFDevice *FxDevice, + _Out_ CMyQueue **Queue + ); + + + HRESULT + Configure( + VOID + ) + { + return S_OK; + } + +// +// 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 + ); + + // + // IQueueCallbackWrite method + // + + virtual + void + STDMETHODCALLTYPE + OnWrite( + _In_ IWDFIoQueue* FxQueue, + _In_ IWDFIoRequest* FxRequest, + _In_ SIZE_T NumOfBytesToWrite + ); + + + // + // IQueueCallbackDefaultIoHandler method + // + + virtual + void + STDMETHODCALLTYPE + OnDefaultIoHandler( + _In_ IWDFIoQueue* FxQueue, + _In_ IWDFIoRequest* FxRequest + ); + + // + //IRequestCallbackRequestCompletion + // + + virtual + void + STDMETHODCALLTYPE + OnCompletion( + IWDFIoRequest* FxRequest, + IWDFIoTarget* FxIoTarget, + IWDFRequestCompletionParams* CompletionParams, + PVOID Context + ); +}; + diff --git a/usb/umdf_filter_kmdf/umdf_filter_kmdf.sln b/usb/umdf_filter_kmdf/umdf_filter_kmdf.sln new file mode 100644 index 000000000..3d7cb72ae --- /dev/null +++ b/usb/umdf_filter_kmdf/umdf_filter_kmdf.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}") = "Package", "Package", "{C1316664-1EF9-40B6-BB74-C6680FB441CA}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Umdf_filter", "Umdf_filter", "{619F52B1-AE17-4645-9FDB-7B2D3D2679DD}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Kmdf_driver", "Kmdf_driver", "{865605E4-9818-4413-9177-9330DA3B724B}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "package", "Package\package.VcxProj", "{2BA68E64-0EBB-409C-937F-FDAC3FB7A39D}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WUDFOsrUsbFilter", "umdf_filter\WUDFOsrUsbFilter.vcxproj", "{BE14DB05-36A7-4ECE-88E1-FB8157CA568E}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "osrusbfx2", "kmdf_driver\osrusbfx2.vcxproj", "{6ABDB82A-B2BA-4EE3-A4CC-C0B00B7B5220}" +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 + {2BA68E64-0EBB-409C-937F-FDAC3FB7A39D}.Debug|Win32.ActiveCfg = Debug|Win32 + {2BA68E64-0EBB-409C-937F-FDAC3FB7A39D}.Debug|Win32.Build.0 = Debug|Win32 + {2BA68E64-0EBB-409C-937F-FDAC3FB7A39D}.Release|Win32.ActiveCfg = Release|Win32 + {2BA68E64-0EBB-409C-937F-FDAC3FB7A39D}.Release|Win32.Build.0 = Release|Win32 + {2BA68E64-0EBB-409C-937F-FDAC3FB7A39D}.Debug|x64.ActiveCfg = Debug|x64 + {2BA68E64-0EBB-409C-937F-FDAC3FB7A39D}.Debug|x64.Build.0 = Debug|x64 + {2BA68E64-0EBB-409C-937F-FDAC3FB7A39D}.Release|x64.ActiveCfg = Release|x64 + {2BA68E64-0EBB-409C-937F-FDAC3FB7A39D}.Release|x64.Build.0 = Release|x64 + {BE14DB05-36A7-4ECE-88E1-FB8157CA568E}.Debug|Win32.ActiveCfg = Debug|Win32 + {BE14DB05-36A7-4ECE-88E1-FB8157CA568E}.Debug|Win32.Build.0 = Debug|Win32 + {BE14DB05-36A7-4ECE-88E1-FB8157CA568E}.Release|Win32.ActiveCfg = Release|Win32 + {BE14DB05-36A7-4ECE-88E1-FB8157CA568E}.Release|Win32.Build.0 = Release|Win32 + {BE14DB05-36A7-4ECE-88E1-FB8157CA568E}.Debug|x64.ActiveCfg = Debug|x64 + {BE14DB05-36A7-4ECE-88E1-FB8157CA568E}.Debug|x64.Build.0 = Debug|x64 + {BE14DB05-36A7-4ECE-88E1-FB8157CA568E}.Release|x64.ActiveCfg = Release|x64 + {BE14DB05-36A7-4ECE-88E1-FB8157CA568E}.Release|x64.Build.0 = Release|x64 + {6ABDB82A-B2BA-4EE3-A4CC-C0B00B7B5220}.Debug|Win32.ActiveCfg = Debug|Win32 + {6ABDB82A-B2BA-4EE3-A4CC-C0B00B7B5220}.Debug|Win32.Build.0 = Debug|Win32 + {6ABDB82A-B2BA-4EE3-A4CC-C0B00B7B5220}.Release|Win32.ActiveCfg = Release|Win32 + {6ABDB82A-B2BA-4EE3-A4CC-C0B00B7B5220}.Release|Win32.Build.0 = Release|Win32 + {6ABDB82A-B2BA-4EE3-A4CC-C0B00B7B5220}.Debug|x64.ActiveCfg = Debug|x64 + {6ABDB82A-B2BA-4EE3-A4CC-C0B00B7B5220}.Debug|x64.Build.0 = Debug|x64 + {6ABDB82A-B2BA-4EE3-A4CC-C0B00B7B5220}.Release|x64.ActiveCfg = Release|x64 + {6ABDB82A-B2BA-4EE3-A4CC-C0B00B7B5220}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {2BA68E64-0EBB-409C-937F-FDAC3FB7A39D} = {C1316664-1EF9-40B6-BB74-C6680FB441CA} + {BE14DB05-36A7-4ECE-88E1-FB8157CA568E} = {619F52B1-AE17-4645-9FDB-7B2D3D2679DD} + {6ABDB82A-B2BA-4EE3-A4CC-C0B00B7B5220} = {865605E4-9818-4413-9177-9330DA3B724B} + EndGlobalSection +EndGlobal diff --git a/usb/umdf_filter_umdf/Package/package.VcxProj b/usb/umdf_filter_umdf/Package/package.VcxProj index 9ce9102a4..6bee13d5a 100644 --- a/usb/umdf_filter_umdf/Package/package.VcxProj +++ b/usb/umdf_filter_umdf/Package/package.VcxProj @@ -18,6 +18,14 @@ x64
+ + + {23F6BB7E-4FB3-4584-8972-5048C2C1CAEE} + + + {4BBB6EA2-FEE6-4951-8D63-BA71C959958F} + + WindowsKernelModeDriver10.0 Utility @@ -27,8 +35,8 @@ - {8D2391A3-43B4-4B9F-89E2-A4024B35FBAD} - {41DDE172-7368-4C23-A7CC-B858DDD69598} + {7F3A9577-907F-41F9-A180-C7DFFA7C9F18} + {5DD4B112-FC71-4CC4-BD6C-7A47B0C393EC} $(MSBuildProjectName) @@ -73,18 +81,6 @@ - - - - - - - {81B31960-3EA8-4F43-B79A-5A851EC49CE8} - - - {F7387F7F-59AF-4963-9D6E-8626E9E4D2EF} - - diff --git a/usb/umdf_filter_umdf/Package/package.VcxProj.Filters b/usb/umdf_filter_umdf/Package/package.VcxProj.Filters index 16a8f2b25..338a88156 100644 --- a/usb/umdf_filter_umdf/Package/package.VcxProj.Filters +++ b/usb/umdf_filter_umdf/Package/package.VcxProj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {B88534C0-78F8-4BDC-8C08-67199D9AA05A} + {CAA3CB0A-784F-417B-ADA4-267FD26703A4} h;hpp;hxx;hm;inl;inc;xsd - {5F6982F6-3AF9-4722-83E0-A9D98AE5B91F} + {9BD249E8-2871-4C6B-8319-A14DDF0914A2} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {14281D5C-7E16-4447-BBF8-E89022F25A64} + {6DF09A8E-2B37-41C7-AD3E-DBFB1708CECA} inf;inv;inx;mof;mc; - {EC6EE142-CF05-44A4-9FDC-01D842258F31} + {165858E4-F07F-4DBC-964A-A3E623018249} \ No newline at end of file diff --git a/usb/umdf_filter_umdf/umdf_driver/WUDFOsrUsbFx2.vcxproj b/usb/umdf_filter_umdf/umdf_driver/WUDFOsrUsbFx2.vcxproj index 278a29eef..bb103f8dd 100644 --- a/usb/umdf_filter_umdf/umdf_driver/WUDFOsrUsbFx2.vcxproj +++ b/usb/umdf_filter_umdf/umdf_driver/WUDFOsrUsbFx2.vcxproj @@ -19,7 +19,7 @@
- {81B31960-3EA8-4F43-B79A-5A851EC49CE8} + {23F6BB7E-4FB3-4584-8972-5048C2C1CAEE} $(MSBuildProjectName) 1 1 @@ -27,7 +27,7 @@ true Debug Win32 - {2B42915F-EE92-4D87-A546-B050A53BCCB6} + {E4C6299E-8765-4C69-8CEB-B5F530F83D42} @@ -256,7 +256,6 @@ - diff --git a/usb/umdf_filter_umdf/umdf_driver/WUDFOsrUsbFx2.vcxproj.Filters b/usb/umdf_filter_umdf/umdf_driver/WUDFOsrUsbFx2.vcxproj.Filters index 62f008072..0ff60758f 100644 --- a/usb/umdf_filter_umdf/umdf_driver/WUDFOsrUsbFx2.vcxproj.Filters +++ b/usb/umdf_filter_umdf/umdf_driver/WUDFOsrUsbFx2.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {48ABB0A1-2EEB-4A52-A63E-E2CAAF57DDA7} + {8A850A2A-3EE0-48E3-9B10-C5D80ED01050} h;hpp;hxx;hm;inl;inc;xsd - {80693C76-07E2-41A9-BA81-3F4FC6E25DE9} + {2DE6E105-292A-4C7B-A2A0-7B39D9339979} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {46C21426-AD2F-493A-A1FA-BDD65A5BB6AF} + {D880698B-D054-49BC-B7B3-607606EAB71A} inf;inv;inx;mof;mc; - {05DE7331-93CD-4B4C-8212-3510CFF88BC8} + {5CEFA42C-9FC9-4234-815F-4C86F8D5AE7D} diff --git a/usb/umdf_filter_umdf/umdf_filter/WUDFOsrUsbFilter.vcxproj b/usb/umdf_filter_umdf/umdf_filter/WUDFOsrUsbFilter.vcxproj index e7d128d96..99e4832d4 100644 --- a/usb/umdf_filter_umdf/umdf_filter/WUDFOsrUsbFilter.vcxproj +++ b/usb/umdf_filter_umdf/umdf_filter/WUDFOsrUsbFilter.vcxproj @@ -19,7 +19,7 @@ - {F7387F7F-59AF-4963-9D6E-8626E9E4D2EF} + {4BBB6EA2-FEE6-4951-8D63-BA71C959958F} $(MSBuildProjectName) 1 1 @@ -27,7 +27,7 @@ true Debug Win32 - {1A27CDB5-8D3E-47CE-B9F3-6A9C3E964175} + {D91D8948-9EF7-41ED-BBC4-7E870F5929F1} @@ -273,7 +273,6 @@ - diff --git a/usb/umdf_filter_umdf/umdf_filter/WUDFOsrUsbFilter.vcxproj.Filters b/usb/umdf_filter_umdf/umdf_filter/WUDFOsrUsbFilter.vcxproj.Filters index 054dcbeaa..2845ce7f7 100644 --- a/usb/umdf_filter_umdf/umdf_filter/WUDFOsrUsbFilter.vcxproj.Filters +++ b/usb/umdf_filter_umdf/umdf_filter/WUDFOsrUsbFilter.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {5D7700E9-F17B-412A-AD01-7ABC0A91FC45} + {C8E8CD40-00CA-4946-BD15-35A6AE9BC653} h;hpp;hxx;hm;inl;inc;xsd - {6163CCCD-5DFB-441F-A144-DFAF53F65869} + {DAC345DC-1425-47B4-BC7A-F459FCFBEA26} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {C5FC49C2-D442-4645-9D78-BC6B98C60BBC} + {396BA632-D58C-49AC-B3C3-E85ED7BB478F} inf;inv;inx;mof;mc; - {EE04D080-B627-4CBB-AD4E-2A9A70CEDE94} + {884CE389-18D3-452C-9FEA-9E5F512FC4E2} diff --git a/usb/umdf_filter_umdf/umdf_filter/WUDFOsrUsbFilterOnUmFx2Driver.inx b/usb/umdf_filter_umdf/umdf_filter/WUDFOsrUsbFilterOnUmFx2Driver.inx index db242508b..2d61a1860 100644 --- a/usb/umdf_filter_umdf/umdf_filter/WUDFOsrUsbFilterOnUmFx2Driver.inx +++ b/usb/umdf_filter_umdf/umdf_filter/WUDFOsrUsbFilterOnUmFx2Driver.inx @@ -7,14 +7,14 @@ Signature="$Windows NT$" Class=Sample ClassGuid={78A1C341-4539-11d3-B88D-00C04FAD5171} -Provider=%MSFTUMDF% +Provider=%ProviderName% DriverVer=03/25/2005,0.0.0.1 CatalogFile=wudf.cat [Manufacturer] -%MSFTUMDF%=Microsoft,NT$ARCH$ +%ManufacturerName%=Standard,NT$ARCH$ -[Microsoft.NT$ARCH$] +[Standard.NT$ARCH$] %OsrUsbDeviceName%=OsrUsb_Install, USB\Vid_045e&Pid_94aa&mi_00 %OsrUsbDeviceName%=OsrUsb_Install, USB\VID_0547&PID_1002 @@ -107,9 +107,10 @@ WudfOsrUsbFx2.dll ; =================== Generic ================================== [Strings] -MSFTUMDF="Microsoft Internal (WDF:UMDF)" -MediaDescription="Microsoft UMDF OSR USB Sample Device Installation Media" +ProviderName="TODO-Set-Provider" +ManufacturerName="TODO-Set-Manufacturer" +MediaDescription="UMDF OSR USB Sample Device Installation Media" ClassName="Sample Device" -OsrUsbDeviceName="Microsoft UMDF OSR Usb Sample Device With Filter on User-mode Driver" +OsrUsbDeviceName="UMDF OSR Usb Sample Device With Filter on User-mode Driver" WinUsb_SvcDesc="WinUSB Driver" WudfRdDisplayName="Windows Driver Foundation - User-mode Driver Framework Reflector" diff --git a/usb/umdf_filter_umdf/umdf_filter_umdf.sln b/usb/umdf_filter_umdf/umdf_filter_umdf.sln index 0bdd1b1d3..a26f3f527 100644 --- a/usb/umdf_filter_umdf/umdf_filter_umdf.sln +++ b/usb/umdf_filter_umdf/umdf_filter_umdf.sln @@ -3,17 +3,17 @@ 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", "{E1970392-D253-4054-9859-047E430EAF95}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Package", "Package", "{72DF351F-0D1A-4DC2-84E6-E71F93608A08}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Umdf_filter", "Umdf_filter", "{DEE88039-9FB9-401D-B13D-727CBF613BCB}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Umdf_filter", "Umdf_filter", "{7B826E21-097E-4279-9B72-C812B012A2D9}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Umdf_driver", "Umdf_driver", "{2E02C638-E773-463D-918E-3A1FBA040E1D}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Umdf_driver", "Umdf_driver", "{BF37E731-1A3F-4BA2-B71F-86BDC37D83F7}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "package", "Package\package.VcxProj", "{8D2391A3-43B4-4B9F-89E2-A4024B35FBAD}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "package", "Package\package.VcxProj", "{7F3A9577-907F-41F9-A180-C7DFFA7C9F18}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WUDFOsrUsbFilter", "umdf_filter\WUDFOsrUsbFilter.vcxproj", "{F7387F7F-59AF-4963-9D6E-8626E9E4D2EF}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WUDFOsrUsbFilter", "umdf_filter\WUDFOsrUsbFilter.vcxproj", "{4BBB6EA2-FEE6-4951-8D63-BA71C959958F}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WUDFOsrUsbFx2", "umdf_driver\WUDFOsrUsbFx2.vcxproj", "{81B31960-3EA8-4F43-B79A-5A851EC49CE8}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WUDFOsrUsbFx2", "umdf_driver\WUDFOsrUsbFx2.vcxproj", "{23F6BB7E-4FB3-4584-8972-5048C2C1CAEE}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -23,37 +23,37 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {8D2391A3-43B4-4B9F-89E2-A4024B35FBAD}.Debug|Win32.ActiveCfg = Debug|Win32 - {8D2391A3-43B4-4B9F-89E2-A4024B35FBAD}.Debug|Win32.Build.0 = Debug|Win32 - {8D2391A3-43B4-4B9F-89E2-A4024B35FBAD}.Release|Win32.ActiveCfg = Release|Win32 - {8D2391A3-43B4-4B9F-89E2-A4024B35FBAD}.Release|Win32.Build.0 = Release|Win32 - {8D2391A3-43B4-4B9F-89E2-A4024B35FBAD}.Debug|x64.ActiveCfg = Debug|x64 - {8D2391A3-43B4-4B9F-89E2-A4024B35FBAD}.Debug|x64.Build.0 = Debug|x64 - {8D2391A3-43B4-4B9F-89E2-A4024B35FBAD}.Release|x64.ActiveCfg = Release|x64 - {8D2391A3-43B4-4B9F-89E2-A4024B35FBAD}.Release|x64.Build.0 = Release|x64 - {F7387F7F-59AF-4963-9D6E-8626E9E4D2EF}.Debug|Win32.ActiveCfg = Debug|Win32 - {F7387F7F-59AF-4963-9D6E-8626E9E4D2EF}.Debug|Win32.Build.0 = Debug|Win32 - {F7387F7F-59AF-4963-9D6E-8626E9E4D2EF}.Release|Win32.ActiveCfg = Release|Win32 - {F7387F7F-59AF-4963-9D6E-8626E9E4D2EF}.Release|Win32.Build.0 = Release|Win32 - {F7387F7F-59AF-4963-9D6E-8626E9E4D2EF}.Debug|x64.ActiveCfg = Debug|x64 - {F7387F7F-59AF-4963-9D6E-8626E9E4D2EF}.Debug|x64.Build.0 = Debug|x64 - {F7387F7F-59AF-4963-9D6E-8626E9E4D2EF}.Release|x64.ActiveCfg = Release|x64 - {F7387F7F-59AF-4963-9D6E-8626E9E4D2EF}.Release|x64.Build.0 = Release|x64 - {81B31960-3EA8-4F43-B79A-5A851EC49CE8}.Debug|Win32.ActiveCfg = Debug|Win32 - {81B31960-3EA8-4F43-B79A-5A851EC49CE8}.Debug|Win32.Build.0 = Debug|Win32 - {81B31960-3EA8-4F43-B79A-5A851EC49CE8}.Release|Win32.ActiveCfg = Release|Win32 - {81B31960-3EA8-4F43-B79A-5A851EC49CE8}.Release|Win32.Build.0 = Release|Win32 - {81B31960-3EA8-4F43-B79A-5A851EC49CE8}.Debug|x64.ActiveCfg = Debug|x64 - {81B31960-3EA8-4F43-B79A-5A851EC49CE8}.Debug|x64.Build.0 = Debug|x64 - {81B31960-3EA8-4F43-B79A-5A851EC49CE8}.Release|x64.ActiveCfg = Release|x64 - {81B31960-3EA8-4F43-B79A-5A851EC49CE8}.Release|x64.Build.0 = Release|x64 + {7F3A9577-907F-41F9-A180-C7DFFA7C9F18}.Debug|Win32.ActiveCfg = Debug|Win32 + {7F3A9577-907F-41F9-A180-C7DFFA7C9F18}.Debug|Win32.Build.0 = Debug|Win32 + {7F3A9577-907F-41F9-A180-C7DFFA7C9F18}.Release|Win32.ActiveCfg = Release|Win32 + {7F3A9577-907F-41F9-A180-C7DFFA7C9F18}.Release|Win32.Build.0 = Release|Win32 + {7F3A9577-907F-41F9-A180-C7DFFA7C9F18}.Debug|x64.ActiveCfg = Debug|x64 + {7F3A9577-907F-41F9-A180-C7DFFA7C9F18}.Debug|x64.Build.0 = Debug|x64 + {7F3A9577-907F-41F9-A180-C7DFFA7C9F18}.Release|x64.ActiveCfg = Release|x64 + {7F3A9577-907F-41F9-A180-C7DFFA7C9F18}.Release|x64.Build.0 = Release|x64 + {4BBB6EA2-FEE6-4951-8D63-BA71C959958F}.Debug|Win32.ActiveCfg = Debug|Win32 + {4BBB6EA2-FEE6-4951-8D63-BA71C959958F}.Debug|Win32.Build.0 = Debug|Win32 + {4BBB6EA2-FEE6-4951-8D63-BA71C959958F}.Release|Win32.ActiveCfg = Release|Win32 + {4BBB6EA2-FEE6-4951-8D63-BA71C959958F}.Release|Win32.Build.0 = Release|Win32 + {4BBB6EA2-FEE6-4951-8D63-BA71C959958F}.Debug|x64.ActiveCfg = Debug|x64 + {4BBB6EA2-FEE6-4951-8D63-BA71C959958F}.Debug|x64.Build.0 = Debug|x64 + {4BBB6EA2-FEE6-4951-8D63-BA71C959958F}.Release|x64.ActiveCfg = Release|x64 + {4BBB6EA2-FEE6-4951-8D63-BA71C959958F}.Release|x64.Build.0 = Release|x64 + {23F6BB7E-4FB3-4584-8972-5048C2C1CAEE}.Debug|Win32.ActiveCfg = Debug|Win32 + {23F6BB7E-4FB3-4584-8972-5048C2C1CAEE}.Debug|Win32.Build.0 = Debug|Win32 + {23F6BB7E-4FB3-4584-8972-5048C2C1CAEE}.Release|Win32.ActiveCfg = Release|Win32 + {23F6BB7E-4FB3-4584-8972-5048C2C1CAEE}.Release|Win32.Build.0 = Release|Win32 + {23F6BB7E-4FB3-4584-8972-5048C2C1CAEE}.Debug|x64.ActiveCfg = Debug|x64 + {23F6BB7E-4FB3-4584-8972-5048C2C1CAEE}.Debug|x64.Build.0 = Debug|x64 + {23F6BB7E-4FB3-4584-8972-5048C2C1CAEE}.Release|x64.ActiveCfg = Release|x64 + {23F6BB7E-4FB3-4584-8972-5048C2C1CAEE}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {8D2391A3-43B4-4B9F-89E2-A4024B35FBAD} = {E1970392-D253-4054-9859-047E430EAF95} - {F7387F7F-59AF-4963-9D6E-8626E9E4D2EF} = {DEE88039-9FB9-401D-B13D-727CBF613BCB} - {81B31960-3EA8-4F43-B79A-5A851EC49CE8} = {2E02C638-E773-463D-918E-3A1FBA040E1D} + {7F3A9577-907F-41F9-A180-C7DFFA7C9F18} = {72DF351F-0D1A-4DC2-84E6-E71F93608A08} + {4BBB6EA2-FEE6-4951-8D63-BA71C959958F} = {7B826E21-097E-4279-9B72-C812B012A2D9} + {23F6BB7E-4FB3-4584-8972-5048C2C1CAEE} = {BF37E731-1A3F-4BA2-B71F-86BDC37D83F7} EndGlobalSection EndGlobal diff --git a/usb/umdf_fx2/ReadMe.md b/usb/umdf_fx2/ReadMe.md index e27415ccc..8b04f98e2 100644 --- a/usb/umdf_fx2/ReadMe.md +++ b/usb/umdf_fx2/ReadMe.md @@ -5,13 +5,6 @@ The umdf\_fx2 sample is a User-Mode Driver Framework (UMDF) driver for the OSR U The sample can also be used with the CustomDeviceAccess MSDK sample. The sample demonstrates how to perform bulk and interrupt data transfers to an USB device. The specification for the device is at . The driver and sample device metadata also work with the [Custom driver access](http://go.microsoft.com/fwlink/p/?LinkID=248288) sample. -Starting in Windows 8.1, the osrusbfx2 sample has been divided into these samples: - -- [WDF Sample Driver Learning Lab for OSR USB-FX2](http://msdn.microsoft.com/en-us/library/windows/hardware/): This sample is a series of iterative drivers that demonstrate how to write a "Hello World" driver and adds additional features in each step. - -- [kmdf\_fx2](gallery_samples.123a_gallery#1): This sample is the final version of kernel-mode [wdf\_osrfx2](http://msdn.microsoft.com/en-us/library/windows/hardware/) driver. The sample demonstrates KMDF methods. - -- umdf\_fx2: This sample is the final version of the user-mode driver [wdf\_osrfx2](http://msdn.microsoft.com/en-us/library/windows/hardware/). The sample demonstrates UMDF methods. Related topics -------------- diff --git a/usb/umdf_fx2/driver/WUDFOsrUsbFx2.inx b/usb/umdf_fx2/driver/WUDFOsrUsbFx2.inx index 1ade7da63..529515c61 100644 --- a/usb/umdf_fx2/driver/WUDFOsrUsbFx2.inx +++ b/usb/umdf_fx2/driver/WUDFOsrUsbFx2.inx @@ -6,14 +6,14 @@ Signature="$Windows NT$" Class=Sample ClassGuid={78A1C341-4539-11d3-B88D-00C04FAD5171} -Provider=%MSFTUMDF% +Provider=%ProviderName% DriverVer=03/25/2005,0.0.0.1 CatalogFile=wudf.cat [Manufacturer] -%MSFTUMDF%=Microsoft,NT$ARCH$ +%ManufacturerName%=Standard,NT$ARCH$ -[Microsoft.NT$ARCH$] +[Standard.NT$ARCH$] %OsrUsbDeviceName%=OsrUsb_Install, USB\Vid_045e&Pid_94aa&mi_00 %OsrUsbDeviceName%=OsrUsb_Install, USB\VID_0547&PID_1002 @@ -107,8 +107,9 @@ WUDFOsrUsbFx2.dll,,,0x00004000 ; COPYFLG_IN_USE_RENAME ; =================== Generic ================================== [Strings] -MSFTUMDF="Microsoft Internal (WDF:UMDF)" -MediaDescription="Microsoft Sample Driver Installation Media" +ProviderName="TODO-Set-Provider" +ManufacturerName="TODO-Set-Manufacturer" +MediaDescription="Sample Driver Installation Media" ClassName="Sample Device" WudfRdDisplayName="Windows Driver Foundation - User-mode Driver Framework Reflector" OsrUsbDeviceName="UMDF Sample Driver for OSR USB Fx2 Learning Kit" diff --git a/usb/umdf_fx2/driver/WUDFOsrUsbFx2.vcxproj b/usb/umdf_fx2/driver/WUDFOsrUsbFx2.vcxproj index 78e68710d..4960ba7cf 100644 --- a/usb/umdf_fx2/driver/WUDFOsrUsbFx2.vcxproj +++ b/usb/umdf_fx2/driver/WUDFOsrUsbFx2.vcxproj @@ -19,13 +19,13 @@ - {6C6B5C2D-F192-4C22-B8ED-A90C10FB9A1E} + {BCB0C08D-E6FC-4FE6-BFDE-723B3706FA53} $(MSBuildProjectName) 1 1 Debug Win32 - {DD1D49D1-69A2-4BF2-9EE8-34692D0104FB} + {CB79CACC-78FE-4BD1-8CEF-F304F7B06047} @@ -287,7 +287,6 @@ - diff --git a/usb/umdf_fx2/driver/WUDFOsrUsbFx2.vcxproj.Filters b/usb/umdf_fx2/driver/WUDFOsrUsbFx2.vcxproj.Filters index 1e50204cc..2d50f3513 100644 --- a/usb/umdf_fx2/driver/WUDFOsrUsbFx2.vcxproj.Filters +++ b/usb/umdf_fx2/driver/WUDFOsrUsbFx2.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {23E0CA5E-560F-46BF-8A5D-0C9892A60610} + {E1664368-3424-45A1-AF32-AAACC488825D} h;hpp;hxx;hm;inl;inc;xsd - {DE2D9516-18E4-493F-8E49-245A3A552F1A} + {137984AA-C7C2-452A-9DA3-3FCF41A038C9} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {0D7905F4-200D-4439-A264-543F6CC50803} + {B2A97895-8C0E-4645-AF01-3C7522D5A567} inf;inv;inx;mof;mc; - {622540FC-F451-4D00-B0A6-DED0A2B5BDBA} + {9B29C0A6-BE4B-4116-8FF9-8E8894F2055D} @@ -45,9 +45,6 @@ - - Driver Files - Driver Files diff --git a/usb/umdf_fx2/exe/WudfOsrUsbFx2Test.vcxproj b/usb/umdf_fx2/exe/WudfOsrUsbFx2Test.vcxproj index 0b2f09fd9..b79ca2a39 100644 --- a/usb/umdf_fx2/exe/WudfOsrUsbFx2Test.vcxproj +++ b/usb/umdf_fx2/exe/WudfOsrUsbFx2Test.vcxproj @@ -19,11 +19,11 @@ - {D567DDDD-4EC8-40B4-AE8F-58B2D41F837F} + {24745B15-B6D2-4D1A-AB27-C0F38B1C0086} $(MSBuildProjectName) Debug Win32 - {A76B918B-D92F-4EA3-8BA4-6D560CCAB575} + {9D5B23F4-B6A2-45BC-92DA-A299F0812254} @@ -198,7 +198,6 @@ - diff --git a/usb/umdf_fx2/exe/WudfOsrUsbFx2Test.vcxproj.Filters b/usb/umdf_fx2/exe/WudfOsrUsbFx2Test.vcxproj.Filters index b101faa40..c7c570ef6 100644 --- a/usb/umdf_fx2/exe/WudfOsrUsbFx2Test.vcxproj.Filters +++ b/usb/umdf_fx2/exe/WudfOsrUsbFx2Test.vcxproj.Filters @@ -3,15 +3,15 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {70899A87-F630-4585-8B67-E7450F161ADD} + {89C25BE5-A406-421D-A03E-1829A2E96921} h;hpp;hxx;hm;inl;inc;xsd - {01515B55-99D7-41ED-9B4E-F4E8971BB851} + {C1755921-6CDF-4C5E-A16F-564D80D9C88C} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {DC48D49A-F703-4F8E-95F8-ED4DE2603606} + {91D70E33-BE1D-40E5-B28D-65555B5DF102} diff --git a/usb/umdf_fx2/umdf_fx2.sln b/usb/umdf_fx2/umdf_fx2.sln index 9e3634691..bfaee1d4f 100644 --- a/usb/umdf_fx2/umdf_fx2.sln +++ b/usb/umdf_fx2/umdf_fx2.sln @@ -3,13 +3,13 @@ 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", "{B339DBBB-24B4-45D7-9F00-655FFD2AA012}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Driver", "Driver", "{8367199C-3B22-414B-BD8D-780BB74E06C6}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Exe", "Exe", "{2869A0EF-34B9-45CF-9E14-01A016B8FC6E}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Exe", "Exe", "{002D6001-0177-4A2D-8CDF-6F2F6A9CAAB6}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WUDFOsrUsbFx2", "driver\WUDFOsrUsbFx2.vcxproj", "{6C6B5C2D-F192-4C22-B8ED-A90C10FB9A1E}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WUDFOsrUsbFx2", "driver\WUDFOsrUsbFx2.vcxproj", "{BCB0C08D-E6FC-4FE6-BFDE-723B3706FA53}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WudfOsrUsbFx2Test", "exe\WudfOsrUsbFx2Test.vcxproj", "{D567DDDD-4EC8-40B4-AE8F-58B2D41F837F}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WudfOsrUsbFx2Test", "exe\WudfOsrUsbFx2Test.vcxproj", "{24745B15-B6D2-4D1A-AB27-C0F38B1C0086}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -19,28 +19,28 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {6C6B5C2D-F192-4C22-B8ED-A90C10FB9A1E}.Debug|Win32.ActiveCfg = Debug|Win32 - {6C6B5C2D-F192-4C22-B8ED-A90C10FB9A1E}.Debug|Win32.Build.0 = Debug|Win32 - {6C6B5C2D-F192-4C22-B8ED-A90C10FB9A1E}.Release|Win32.ActiveCfg = Release|Win32 - {6C6B5C2D-F192-4C22-B8ED-A90C10FB9A1E}.Release|Win32.Build.0 = Release|Win32 - {6C6B5C2D-F192-4C22-B8ED-A90C10FB9A1E}.Debug|x64.ActiveCfg = Debug|x64 - {6C6B5C2D-F192-4C22-B8ED-A90C10FB9A1E}.Debug|x64.Build.0 = Debug|x64 - {6C6B5C2D-F192-4C22-B8ED-A90C10FB9A1E}.Release|x64.ActiveCfg = Release|x64 - {6C6B5C2D-F192-4C22-B8ED-A90C10FB9A1E}.Release|x64.Build.0 = Release|x64 - {D567DDDD-4EC8-40B4-AE8F-58B2D41F837F}.Debug|Win32.ActiveCfg = Debug|Win32 - {D567DDDD-4EC8-40B4-AE8F-58B2D41F837F}.Debug|Win32.Build.0 = Debug|Win32 - {D567DDDD-4EC8-40B4-AE8F-58B2D41F837F}.Release|Win32.ActiveCfg = Release|Win32 - {D567DDDD-4EC8-40B4-AE8F-58B2D41F837F}.Release|Win32.Build.0 = Release|Win32 - {D567DDDD-4EC8-40B4-AE8F-58B2D41F837F}.Debug|x64.ActiveCfg = Debug|x64 - {D567DDDD-4EC8-40B4-AE8F-58B2D41F837F}.Debug|x64.Build.0 = Debug|x64 - {D567DDDD-4EC8-40B4-AE8F-58B2D41F837F}.Release|x64.ActiveCfg = Release|x64 - {D567DDDD-4EC8-40B4-AE8F-58B2D41F837F}.Release|x64.Build.0 = Release|x64 + {BCB0C08D-E6FC-4FE6-BFDE-723B3706FA53}.Debug|Win32.ActiveCfg = Debug|Win32 + {BCB0C08D-E6FC-4FE6-BFDE-723B3706FA53}.Debug|Win32.Build.0 = Debug|Win32 + {BCB0C08D-E6FC-4FE6-BFDE-723B3706FA53}.Release|Win32.ActiveCfg = Release|Win32 + {BCB0C08D-E6FC-4FE6-BFDE-723B3706FA53}.Release|Win32.Build.0 = Release|Win32 + {BCB0C08D-E6FC-4FE6-BFDE-723B3706FA53}.Debug|x64.ActiveCfg = Debug|x64 + {BCB0C08D-E6FC-4FE6-BFDE-723B3706FA53}.Debug|x64.Build.0 = Debug|x64 + {BCB0C08D-E6FC-4FE6-BFDE-723B3706FA53}.Release|x64.ActiveCfg = Release|x64 + {BCB0C08D-E6FC-4FE6-BFDE-723B3706FA53}.Release|x64.Build.0 = Release|x64 + {24745B15-B6D2-4D1A-AB27-C0F38B1C0086}.Debug|Win32.ActiveCfg = Debug|Win32 + {24745B15-B6D2-4D1A-AB27-C0F38B1C0086}.Debug|Win32.Build.0 = Debug|Win32 + {24745B15-B6D2-4D1A-AB27-C0F38B1C0086}.Release|Win32.ActiveCfg = Release|Win32 + {24745B15-B6D2-4D1A-AB27-C0F38B1C0086}.Release|Win32.Build.0 = Release|Win32 + {24745B15-B6D2-4D1A-AB27-C0F38B1C0086}.Debug|x64.ActiveCfg = Debug|x64 + {24745B15-B6D2-4D1A-AB27-C0F38B1C0086}.Debug|x64.Build.0 = Debug|x64 + {24745B15-B6D2-4D1A-AB27-C0F38B1C0086}.Release|x64.ActiveCfg = Release|x64 + {24745B15-B6D2-4D1A-AB27-C0F38B1C0086}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {6C6B5C2D-F192-4C22-B8ED-A90C10FB9A1E} = {B339DBBB-24B4-45D7-9F00-655FFD2AA012} - {D567DDDD-4EC8-40B4-AE8F-58B2D41F837F} = {2869A0EF-34B9-45CF-9E14-01A016B8FC6E} + {BCB0C08D-E6FC-4FE6-BFDE-723B3706FA53} = {8367199C-3B22-414B-BD8D-780BB74E06C6} + {24745B15-B6D2-4D1A-AB27-C0F38B1C0086} = {002D6001-0177-4A2D-8CDF-6F2F6A9CAAB6} EndGlobalSection EndGlobal diff --git a/usb/usbsamp/ReadMe.md b/usb/usbsamp/ReadMe.md new file mode 100644 index 000000000..ddab8159c --- /dev/null +++ b/usb/usbsamp/ReadMe.md @@ -0,0 +1,187 @@ +Usbsamp Generic USB Driver +========================== + +The USBSAMP sample demonstrates how to perform full speed, high speed, and SuperSpeed transfers to and from bulk and isochronous endpoints of a generic USB device. USBSAMP is based on the [Kernel Mode Driver Framework](http://msdn.microsoft.com/en-us/library/windows/hardware/ff557405) (KMDF). Superspeed bulk and isochronous transfers only work when the Microsoft USB 3.0 stack is loaded. + +The sample also contains a console test application that initiates bulk (including stream) and isochronous transfers and obtains data from the device's I/O endpoints. The application also demonstrates how to use GUID-based device names and pipe names generated by the operating system using the **SetupDiXXX** user-mode APIs. + +For information about USB, see [Universal Serial Bus (USB) Drivers](http://msdn.microsoft.com/en-us/library/windows/hardware/ff538930). + +## Universal Windows Driver Compliant +This sample builds a Universal Windows Driver. It uses only APIs and DDIs that are included in OneCoreUAP. + +**Hardware requirements** + +The sample driver can be loaded as the function driver for any of these devices: + +- OSR FX2 learning kit. You can get the kit from [OSR Online](http://www.osronline.com/). +- [MUTT devices](http://msdn.microsoft.com/en-us/library/windows/hardware/dn376873). To order those devices, see [How to get MUTT devices](buses.microsoft_usb_test_tool__mutt__devices#howto). +- Intel 82930 USB test board. + +If you have a different USB device, you can still use the driver by adding the device's hardware ID to the INX file. Note that the data transfer scenarios will work only with the endpoints supported by the device. + +Set the configuration and platform in Visual Studio +--------------------------------------------------- + +In Visual Studio, in Solution Explorer, right click **Solution ‘usbsamp’ (3 projects)**, 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. Here are some examples of configuration and platform settings. + + +++++ + + + + + + + + + + + +
Configuration +Platform +Description
Win8.1 Debug +x64 +The driver will run on an x64 hardware platform that is running Windows 8.1. The driver will not run on any earlier versions of Windows.Win7 Debug +x64 +The driver will run on an x64 hardware platform that is running Windows 7 or a later version of Windows.
+ +Build the sample using Visual Studio +------------------------------------ + +In Visual Studio, on the **Build** menu, choose **Build Solution**. + +For more information about using Visual Studio to build a driver package, see [Building a Driver](http://msdn.microsoft.com/en-us/library/windows/hardware/ff554644). + +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 Win7 Debug and x64, the package is in your solution folder under x64\\Win7Debug\\Package. + +The package contains these files: + + ++++ + + + + + + + + + + + +
File +Description
usbsamp.sys +The driver file.WdfCoinstaller010xx.dll +The coinstaller for version 1.xx of KMDF.
+ +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 USBSAMP sample 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/). + +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 **Install and Verify**. Click **OK**. +3. On the **Build** menu, choose **Deploy Package** or **Build Solution**. + +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). + +1. Copy all of the files in your driver package to a folder on the target computer (for example, c:\\Usbsamp). +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 usbsamp.inf USB\\VID\_045E&PID\_078F** + +View the device 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 the device. For example the device name might be **WDF Sample for FX2 MUTT device** under the**Sample Device** node. + +Build the sample using MSBuild +------------------------------ + +As an alternative to building the USBSAMP 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, Usbsamp.sln. Use the MSBuild command to build the solution. Here are some examples: + +**msbuild /p:configuration=â€Win7 Debug†/p:platform=â€x64†Usbsamp.sln** + +**msbuild /p:configuration=â€Win8 Release†/p:platform=â€Win32†Usbsamp.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). + +Testing the sample +------------------ + +The sample includes a test application, usbsamp.exe. This console application enumerates the interface registered by the driver and opens the device to send Read, Write, or DeviceIoControl requests based on the command line options. To test the sample, + +1. In Visual Studio, choose **Solution Explorer** from the **View** menu. Locate the application project named **usbsamp**, under the **Exe** folder. +2. Right-click and choose **Build**. For example, if your settings are Win8.1 Debug and x64, the application executable is in your solution folder under the exe\\x64\\Win8.1Debug\\usbsamp.exe. +3. Run the executable on the target machine. + +- To view all descriptors and endpoint information, use the following command. + + **usbsamp.exe -u** + + You can use the preceding command to view pipe numbers for read and write requests. + +- To send a Read-Write request, use the following command. + + **usbsamp.exe -r 1024 -w 1024 -c 100 -v** + + The preceding command first writes 1024 bytes of data to bulk out endpoint (pipe 1), then reads 1024 bytes from bulk in endpoint (pipe 0), and compares the read buffer with write buffer to see if they match. If the buffer contents match, it performs this operation 100 times. + +- To send Read-Write requests to bulk endpoints, use any of the following commands, simultaneously. If Read-Write requests are sent to a SuperSpeed bulk endpoint with streams, the sample driver always uses the first underlying stream associated with that endpoint. The driver is multi-thread safe so it can handle multiple requests at a time. + + **usbsamp.exe -r 65536** + + The preceding command reads 65536 bytes from pipe 0. + + **usbsamp.exe -w 65536** + + The preceding command writes 65536 bytes to pipe 1. + + **usbsamp.exe -r 65536 -i pipe02** + + The preceding command reads 65536 bytes from pipe 2. + + **usbsamp.exe -w 65536 -o pipe03** + + The preceding command writes 65536 bytes to pipe 3. + +- To send Read and Write requests to isochronous endpoints you can use one or more of these commands simultaneously. + + **usbsamp.exe -r 512 -i pipe04** + + The preceding command reads 512 bytes from pipe 4. + + **usbsamp.exe -w 512 -o pipe05** + + The preceding command writes 512 bytes to pipe 5. + + **usbsamp.exe -w 1024 -o pipe05 -r 1024 -i pipe04 -c 100 -v** + + The preceding command writes 1024 bytes to pipe 5, then reads 1024 bytes from pipe 4, and compares the buffers to see if they match. If the buffer contents match, it performs this operation 100 times. + +- To skip validation of the data to be read or written in a particular request, use the command with **-x** option as follows: + + **usbsamp.exe -r 1024 -w 1024 -c 100 -x** + + diff --git a/usb/usbsamp/exe/testapp.c b/usb/usbsamp/exe/testapp.c new file mode 100644 index 000000000..e44297261 --- /dev/null +++ b/usb/usbsamp/exe/testapp.c @@ -0,0 +1,1307 @@ +/*++ + +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: + + Console test app for usbsamp.SYS driver + +Environment: + + user mode only + +--*/ + + +#include +_Analysis_mode_(_Analysis_code_type_user_code_) + +#include + +#include +#include +#include +#include +#include + +#include "devioctl.h" +#include "public.h" +#include "strsafe.h" + +#pragma warning(disable:4200) // +#pragma warning(disable:4201) // nameless struct/union +#pragma warning(disable:4214) // bit field types other than int + +#include +#include +#include "usbdi.h" + +#pragma warning(default:4200) +#pragma warning(default:4201) +#pragma warning(default:4214) + + +#define NOISY(_x_) printf _x_ ; +#define MAX_LENGTH 256 + +char inPipe[MAX_LENGTH] = "PIPE00"; // pipe name for bulk input pipe on our test board +char outPipe[MAX_LENGTH] = "PIPE01"; // pipe name for bulk output pipe on our test board +char completeDeviceName[MAX_LENGTH] = ""; //generated from the GUID registered by the driver itself + +BOOL fDumpUsbConfig = FALSE; // flags set in response to console command line switches +BOOL fDumpReadData = FALSE; +BOOL fRead = FALSE; +BOOL fWrite = FALSE; +BOOL fCompareData = TRUE; + +int gDebugLevel = 1; // higher == more verbose, default is 1, 0 turns off all + +ULONG IterationCount = 1; //count of iterations of the test we are to perform +int WriteLen = 0; // #bytes to write +int ReadLen = 0; // #bytes to read + +// functions + + +HANDLE +OpenOneDevice ( + _In_ HDEVINFO HardwareDeviceInfo, + _In_ PSP_DEVICE_INTERFACE_DATA DeviceInfoData, + _In_ PSTR devName + ) +/*++ +Routine Description: + + Given the HardwareDeviceInfo, representing a handle to the plug and + play information, and deviceInfoData, representing a specific usb device, + open that device and fill in all the relevant information in the given + USB_DEVICE_DESCRIPTOR structure. + +Arguments: + + HardwareDeviceInfo: handle to info obtained from Pnp mgr via SetupDiGetClassDevs() + DeviceInfoData: ptr to info obtained via SetupDiEnumDeviceInterfaces() + +Return Value: + + return HANDLE if the open and initialization was successfull, + else INVLAID_HANDLE_VALUE. + +--*/ +{ + PSP_DEVICE_INTERFACE_DETAIL_DATA functionClassDeviceData = NULL; + ULONG predictedLength = 0; + ULONG requiredLength = 0; + HANDLE hOut = INVALID_HANDLE_VALUE; + + // + // allocate a function class device data structure to receive the + // goods about this particular device. + // + SetupDiGetDeviceInterfaceDetail ( + HardwareDeviceInfo, + DeviceInfoData, + NULL, // probing so no output buffer yet + 0, // probing so output buffer length of zero + &requiredLength, + NULL); // not interested in the specific dev-node + + + predictedLength = requiredLength; + // sizeof (SP_FNCLASS_DEVICE_DATA) + 512; + + functionClassDeviceData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)malloc (predictedLength); + if(NULL == functionClassDeviceData) { + return INVALID_HANDLE_VALUE; + } + functionClassDeviceData->cbSize = sizeof (SP_DEVICE_INTERFACE_DETAIL_DATA); + + // + // Retrieve the information from Plug and Play. + // + if (! SetupDiGetDeviceInterfaceDetail ( + HardwareDeviceInfo, + DeviceInfoData, + functionClassDeviceData, + predictedLength, + &requiredLength, + NULL)) { + free( functionClassDeviceData ); + return INVALID_HANDLE_VALUE; + } + + (void)StringCchCopy( devName, MAX_LENGTH, functionClassDeviceData->DevicePath) ; + printf( "Attempting to open %s\n", devName ); + + hOut = CreateFile ( + functionClassDeviceData->DevicePath, + GENERIC_READ | GENERIC_WRITE, + 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 == hOut) { + printf( "FAILED to open %s\n", devName ); + } + free( functionClassDeviceData ); + return hOut; +} + + +HANDLE +OpenUsbDevice( + _In_ LPGUID pGuid, + _In_ PSTR outNameBuf + ) +/*++ +Routine Description: + + Do the required PnP things in order to find + the next available proper device in the system at this time. + +Arguments: + + pGuid: ptr to GUID registered by the driver itself + outNameBuf: the generated name for this device + +Return Value: + + return HANDLE if the open and initialization was successful, + else INVLAID_HANDLE_VALUE. +--*/ +{ + ULONG NumberDevices; + HANDLE hOut = INVALID_HANDLE_VALUE; + HDEVINFO hardwareDeviceInfo; + SP_DEVICE_INTERFACE_DATA deviceInfoData; + ULONG i; + BOOLEAN done; + PUSB_DEVICE_DESCRIPTOR usbDeviceInst; + PUSB_DEVICE_DESCRIPTOR *UsbDevices = &usbDeviceInst; + PUSB_DEVICE_DESCRIPTOR tempDevDesc; + + *UsbDevices = NULL; + tempDevDesc = NULL; + NumberDevices = 0; + + // + // Open a handle to the plug and play dev node. + // SetupDiGetClassDevs() returns a device information set that contains + // info on all installed devices of a specified class. + // + hardwareDeviceInfo = + SetupDiGetClassDevs ( pGuid, + NULL, // Define no enumerator (global) + NULL, // Define no + (DIGCF_PRESENT | // Only Devices present + DIGCF_DEVICEINTERFACE)); // Function class devices. + + if (hardwareDeviceInfo == INVALID_HANDLE_VALUE) { + return INVALID_HANDLE_VALUE ; + } + // + // Take a wild guess at the number of devices we have; + // Be prepared to realloc and retry if there are more than we guessed + // + NumberDevices = 4; + done = FALSE; + deviceInfoData.cbSize = sizeof (SP_DEVICE_INTERFACE_DATA); + + i=0; + while (!done) { + NumberDevices *= 2; + + if (*UsbDevices) { + tempDevDesc = (PUSB_DEVICE_DESCRIPTOR) + realloc (*UsbDevices, (NumberDevices * sizeof (USB_DEVICE_DESCRIPTOR))); + if(tempDevDesc) { + *UsbDevices = tempDevDesc; + tempDevDesc = NULL; + } + else { + free(*UsbDevices); + *UsbDevices = NULL; + } + } else { + *UsbDevices = (PUSB_DEVICE_DESCRIPTOR)calloc (NumberDevices, sizeof (USB_DEVICE_DESCRIPTOR)); + } + + if (NULL == *UsbDevices) { + + // SetupDiDestroyDeviceInfoList destroys a device information set + // and frees all associated memory. + + SetupDiDestroyDeviceInfoList (hardwareDeviceInfo); + return INVALID_HANDLE_VALUE; + } + + for (; i < NumberDevices; i++) { + // SetupDiEnumDeviceInterfaces() returns information about device + // interfaces exposed by one or more devices. Each call returns + // information about one interface; the routine can be called + // repeatedly to get information about several interfaces exposed + // by one or more devices. + + if (SetupDiEnumDeviceInterfaces (hardwareDeviceInfo, + 0, // We don't care about specific PDOs + pGuid, + i, + &deviceInfoData)) { + + hOut = OpenOneDevice (hardwareDeviceInfo, &deviceInfoData, outNameBuf); + if ( hOut != INVALID_HANDLE_VALUE ) { + done = TRUE; + break; + } + } else { + if (ERROR_NO_MORE_ITEMS == GetLastError()) { + done = TRUE; + break; + } + } + } + } + + // SetupDiDestroyDeviceInfoList() destroys a device information set + // and frees all associated memory. + + SetupDiDestroyDeviceInfoList (hardwareDeviceInfo); + free ( *UsbDevices ); + return hOut; +} + + + + +BOOL +GetUsbDeviceFileName( + _In_ LPGUID pGuid, + _In_ PSTR outNameBuf + ) +/*++ +Routine Description: + + Given a ptr to a driver-registered GUID, give us a string with the device name + that can be used in a CreateFile() call. + Actually briefly opens and closes the device and sets outBuf if successfull; + returns FALSE if not + +Arguments: + + pGuid: ptr to GUID registered by the driver itself + outNameBuf: the generated zero-terminated name for this device + +Return Value: + + TRUE on success else FALSE + +--*/ +{ + HANDLE hDev = OpenUsbDevice( pGuid, outNameBuf ); + + if ( hDev != INVALID_HANDLE_VALUE ) { + CloseHandle( hDev ); + return TRUE; + } + return FALSE; +} + +HANDLE +open_dev() +/*++ +Routine Description: + + Called by dumpUsbConfig() to open an instance of our device + +Arguments: + + None + +Return Value: + + Device handle on success else NULL + +--*/ +{ + HANDLE hDEV = OpenUsbDevice( (LPGUID)&GUID_CLASS_USBSAMP_USB, + completeDeviceName); + + if (hDEV == INVALID_HANDLE_VALUE) { + printf("Failed to open (%s) = %u", completeDeviceName, GetLastError()); + } else { + printf("DeviceName = (%s)\n", completeDeviceName); + } + + return hDEV; + +} + + +HANDLE +open_file( + _In_ PSTR filename + ) +/*++ +Routine Description: + + Called by main() to open an instance of our device after obtaining its name + +Arguments: + + None + +Return Value: + + Device handle on success else NULL + +--*/ +{ + int success = 1; + HANDLE h; + + if ( !GetUsbDeviceFileName( + (LPGUID) &GUID_CLASS_USBSAMP_USB, + completeDeviceName) ) + { + NOISY(("Failed to GetUsbDeviceFileName err - %u\n", GetLastError())); + return INVALID_HANDLE_VALUE; + } + + (void) StringCchCat (completeDeviceName, MAX_LENGTH, "\\" ); + + if(FAILED(StringCchCat (completeDeviceName, MAX_LENGTH, filename))) { + NOISY(("Failed to open handle - possibly long filename\n")); + return INVALID_HANDLE_VALUE; + } + + printf("completeDeviceName = (%s)\n", completeDeviceName); + + h = CreateFile(completeDeviceName, + GENERIC_WRITE | GENERIC_READ, + FILE_SHARE_WRITE | FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + 0, + NULL); + + if (h == INVALID_HANDLE_VALUE) { + NOISY(("Failed to open (%s) = %u", completeDeviceName, GetLastError())); + success = 0; + } else { + NOISY(("Opened successfully.\n")); + } + + return h; +} + +void +usage() +/*++ +Routine Description: + + Called by main() to dump usage info to the console when + the app is called with no parms or with an invalid parm + +Arguments: + + None + +Return Value: + + None + +--*/ +{ + static int i=1; + + if (i) { + printf("Usage for Read/Write test:\n"); + printf("-r [n] where n is number of bytes to read\n"); + printf("-w [n] where n is number of bytes to write\n"); + printf("-c [n] where n is number of iterations (default = 1)\n"); + printf("-i [s] where s is the input pipe\n"); + printf("-o [s] where s is the output pipe\n"); + printf("-v verbose -- dumps read data\n"); + printf("-x to skip validation of read and write data\n"); + + printf("\nUsage for USB and Endpoint info:\n"); + printf("-u to dump USB configuration and pipe info \n"); + i = 0; + } +} + + +void +parse( + _In_ int argc, + _In_reads_(argc) char *argv[] + ) +/*++ +Routine Description: + + Called by main() to parse command line parms + +Arguments: + + argc and argv that was passed to main() + +Return Value: + + Sets global flags as per user function request + +--*/ +{ + int i; + + if ( argc < 2 ) // give usage if invoked with no parms + usage(); + + for (i=0; i= argc) { + usage(); + exit(1); + } + else { +#pragma warning(suppress: 6385) + ReadLen = atoi(&argv[i+1][0]); + if (ReadLen == 0) { + usage(); + exit(1); + } + fRead = TRUE; + } + i++; + break; + case 'w': + case 'W': + if (i+1 >= argc) { + usage(); + exit(1); + } + else { + WriteLen = atoi(&argv[i+1][0]); + if (WriteLen == 0) { + usage(); + exit(1); + } + fWrite = TRUE; + } + i++; + break; + case 'c': + case 'C': + if (i+1 >= argc) { + usage(); + exit(1); + } + else { + IterationCount = atoi(&argv[i+1][0]); + if (IterationCount == 0) { + usage(); + exit(1); + } + } + i++; + break; + case 'i': + case 'I': + if (i+1 >= argc) { + usage(); + exit(1); + } + else { + (void)StringCchCopy(inPipe, MAX_LENGTH, &argv[i+1][0]); + } + i++; + break; + case 'u': + case 'U': + fDumpUsbConfig = TRUE; + i++; + break; + case 'v': + case 'V': + fDumpReadData = TRUE; + i++; + break; + case 'x': + case 'X': + fCompareData = FALSE; + i++; + break; + case 'o': + case 'O': + if (i+1 >= argc) { + usage(); + exit(1); + } + else { + (void)StringCchCopy(outPipe, MAX_LENGTH, &argv[i+1][0]); + } + i++; + break; + default: + usage(); + } + } + } +} + +BOOL +compare_buffs( + _In_reads_bytes_(length) char *buff1, + _In_reads_bytes_(length) char *buff2, + _In_ int length + ) +/*++ +Routine Description: + + Called to verify read and write buffers match for loopback test + +Arguments: + + buffers to compare and length + +Return Value: + + TRUE if buffers match, else FALSE + +--*/ +{ + int ok = 1; + + if (memcmp(buff1, buff2, length )) { + // Edi, and Esi point to the mismatching char and ecx indicates the + // remaining length. + ok = 0; + } + + return ok; +} + +#define NPERLN 8 + +void +dump( + UCHAR *b, + int len +) +/*++ +Routine Description: + + Called to do formatted ascii dump to console of the io buffer + +Arguments: + + buffer and length + +Return Value: + + none + +--*/ +{ + ULONG i; + ULONG longLen = (ULONG)len / sizeof( ULONG ); + PULONG pBuf = (PULONG) b; + + // dump an ordinal ULONG for each sizeof(ULONG)'th byte + printf("\n****** BEGIN DUMP LEN decimal %d, 0x%x\n", len,len); + for (i=0; ibDescriptorType or + PUSB_DEVICE_DESCRIPTOR->bDescriptorType or + PUSB_INTERFACE_DESCRIPTOR->bDescriptorType or + PUSB_STRING_DESCRIPTOR->bDescriptorType or + PUSB_POWER_DESCRIPTOR->bDescriptorType or + PUSB_CONFIGURATION_DESCRIPTOR->bDescriptorType + +Return Value: + + ptr to string + +--*/{ + + switch(bDescriptorType) { + + case USB_DEVICE_DESCRIPTOR_TYPE: + return "USB_DEVICE_DESCRIPTOR_TYPE"; + + case USB_CONFIGURATION_DESCRIPTOR_TYPE: + return "USB_CONFIGURATION_DESCRIPTOR_TYPE"; + + + case USB_STRING_DESCRIPTOR_TYPE: + return "USB_STRING_DESCRIPTOR_TYPE"; + + + case USB_INTERFACE_DESCRIPTOR_TYPE: + return "USB_INTERFACE_DESCRIPTOR_TYPE"; + + + case USB_ENDPOINT_DESCRIPTOR_TYPE: + return "USB_ENDPOINT_DESCRIPTOR_TYPE"; + + case USB_SUPERSPEED_ENDPOINT_COMPANION_DESCRIPTOR_TYPE: + return "USB_SUPERSPEED_ENDPOINT_COMPANION_DESCRIPTOR_TYPE"; + + +#ifdef USB_POWER_DESCRIPTOR_TYPE // this is the older definintion which is actually obsolete + // workaround for temporary bug in 98ddk, older USB100.h file + case USB_POWER_DESCRIPTOR_TYPE: + return "USB_POWER_DESCRIPTOR_TYPE"; +#endif + +#ifdef USB_RESERVED_DESCRIPTOR_TYPE // this is the current version of USB100.h as in NT5DDK + + case USB_RESERVED_DESCRIPTOR_TYPE: + return "USB_RESERVED_DESCRIPTOR_TYPE"; + + case USB_CONFIG_POWER_DESCRIPTOR_TYPE: + return "USB_CONFIG_POWER_DESCRIPTOR_TYPE"; + + case USB_INTERFACE_POWER_DESCRIPTOR_TYPE: + return "USB_INTERFACE_POWER_DESCRIPTOR_TYPE"; +#endif // for current nt5ddk version of USB100.h + + default: + return "??? UNKNOWN!!"; + } +} + + +char +*usbEndPointTypeString(UCHAR bmAttributes) +/*++ +Routine Description: + + Called to get ascii string of endpt descriptor type + +Arguments: + + PUSB_ENDPOINT_DESCRIPTOR->bmAttributes + +Return Value: + + ptr to string + +--*/ +{ + UINT typ = bmAttributes & USB_ENDPOINT_TYPE_MASK; + + + switch( typ) { + case USB_ENDPOINT_TYPE_INTERRUPT: + return "USB_ENDPOINT_TYPE_INTERRUPT"; + + case USB_ENDPOINT_TYPE_BULK: + return "USB_ENDPOINT_TYPE_BULK"; + + case USB_ENDPOINT_TYPE_ISOCHRONOUS: + return "USB_ENDPOINT_TYPE_ISOCHRONOUS"; + + case USB_ENDPOINT_TYPE_CONTROL: + return "USB_ENDPOINT_TYPE_CONTROL"; + + default: + return "??? UNKNOWN!!"; + } +} + + +char +*usbConfigAttributesString(UCHAR bmAttributes) +/*++ +Routine Description: + + Called to get ascii string of USB_CONFIGURATION_DESCRIPTOR attributes + +Arguments: + + PUSB_CONFIGURATION_DESCRIPTOR->bmAttributes + +Return Value: + + ptr to string + +--*/ +{ + UINT typ = bmAttributes & USB_CONFIG_POWERED_MASK; + + + switch( typ) { + + case USB_CONFIG_BUS_POWERED: + return "USB_CONFIG_BUS_POWERED"; + + case USB_CONFIG_SELF_POWERED: + return "USB_CONFIG_SELF_POWERED"; + + case USB_CONFIG_REMOTE_WAKEUP: + return "USB_CONFIG_REMOTE_WAKEUP"; + + + default: + return "??? UNKNOWN!!"; + } +} + + +void +print_USB_CONFIGURATION_DESCRIPTOR(PUSB_CONFIGURATION_DESCRIPTOR cd) +/*++ +Routine Description: + + Called to do formatted ascii dump to console of a USB config descriptor + +Arguments: + + ptr to USB configuration descriptor + +Return Value: + + none + +--*/ +{ + printf("\n===================\nUSB_CONFIGURATION_DESCRIPTOR\n"); + + printf( + "bLength = 0x%x, decimal %u\n", cd->bLength, cd->bLength + ); + + printf( + "bDescriptorType = 0x%x ( %s )\n", cd->bDescriptorType, usbDescriptorTypeString( cd->bDescriptorType ) + ); + + printf( + "wTotalLength = 0x%x, decimal %u\n", cd->wTotalLength, cd->wTotalLength + ); + + printf( + "bNumInterfaces = 0x%x, decimal %u\n", cd->bNumInterfaces, cd->bNumInterfaces + ); + + printf( + "bConfigurationValue = 0x%x, decimal %u\n", cd->bConfigurationValue, cd->bConfigurationValue + ); + + printf( + "iConfiguration = 0x%x, decimal %u\n", cd->iConfiguration, cd->iConfiguration + ); + + printf( + "bmAttributes = 0x%x ( %s )\n", cd->bmAttributes, usbConfigAttributesString( cd->bmAttributes ) + ); + + printf( + "MaxPower = 0x%x, decimal %u\n", cd->MaxPower, cd->MaxPower + ); +} + + +void +print_USB_INTERFACE_DESCRIPTOR(PUSB_INTERFACE_DESCRIPTOR id, UINT ix) +/*++ +Routine Description: + + Called to do formatted ascii dump to console of a USB interface descriptor + +Arguments: + + ptr to USB interface descriptor + +Return Value: + + none + +--*/ +{ + printf("\n-----------------------------\nUSB_INTERFACE_DESCRIPTOR #%u\n", ix); + + + printf( + "bLength = 0x%x\n", id->bLength + ); + + + printf( + "bDescriptorType = 0x%x ( %s )\n", id->bDescriptorType, usbDescriptorTypeString( id->bDescriptorType ) + ); + + + printf( + "bInterfaceNumber = 0x%x\n", id->bInterfaceNumber + ); + printf( + "bAlternateSetting = 0x%x\n", id->bAlternateSetting + ); + printf( + "bNumEndpoints = 0x%x\n", id->bNumEndpoints + ); + printf( + "bInterfaceClass = 0x%x\n", id->bInterfaceClass + ); + printf( + "bInterfaceSubClass = 0x%x\n", id->bInterfaceSubClass + ); + printf( + "bInterfaceProtocol = 0x%x\n", id->bInterfaceProtocol + ); + printf( + "bInterface = 0x%x\n", id->iInterface + ); +} + + +void +print_USB_ENDPOINT_DESCRIPTOR(PUSB_ENDPOINT_DESCRIPTOR ed, int i) +/*++ +Routine Description: + + Called to do formatted ascii dump to console of a USB endpoint descriptor + +Arguments: + + ptr to USB endpoint descriptor, + index of this endpt in interface desc + +Return Value: + + none + +--*/ +{ + printf( + "------------------------------\nUSB_ENDPOINT_DESCRIPTOR for Pipe%02d\n", i + ); + + printf( + "bLength = 0x%x\n", ed->bLength + ); + + printf( + "bDescriptorType = 0x%x ( %s )\n", ed->bDescriptorType, usbDescriptorTypeString( ed->bDescriptorType ) + ); + + if ( USB_ENDPOINT_DIRECTION_IN( ed->bEndpointAddress ) ) { + printf( + "bEndpointAddress= 0x%x ( INPUT )\n", ed->bEndpointAddress + ); + } else { + printf( + "bEndpointAddress= 0x%x ( OUTPUT )\n", ed->bEndpointAddress + ); + } + + printf( + "bmAttributes= 0x%x ( %s )\n", ed->bmAttributes, usbEndPointTypeString ( ed->bmAttributes ) + ); + + printf( + "wMaxPacketSize= 0x%x, decimal %u\n", ed->wMaxPacketSize, ed->wMaxPacketSize + ); + + printf( + "bInterval = 0x%x, decimal %u\n", ed->bInterval, ed->bInterval + ); +} + + +void +print_USB_SUPERSPEED_ENDPOINT_COMPANION_DESCRIPTOR(PUSB_SUPERSPEED_ENDPOINT_COMPANION_DESCRIPTOR secd, int i) +/*++ +Routine Description: + + Called to do formatted ascii dump to console of a USB super speed endpoint companion descriptor + +Arguments: + + secd - ptr to USB endpoint descriptor, + i - index of this endpt in interface desc + +Return Value: + + none + +--*/ +{ + printf( + "------------------------------\nUSB_SUPERSPEED_ENDPOINT_COMPANION_DESCRIPTOR for Pipe%02d\n", i + ); + + printf( + "bLength = 0x%x\n", secd->bLength + ); + + printf( + "bDescriptorType = 0x%x ( %s )\n", secd->bDescriptorType, usbDescriptorTypeString( secd->bDescriptorType ) + ); + + printf( + "bMaxBurst = 0x%x, decimal %u\n", secd->bMaxBurst, secd->bMaxBurst + ); + + printf( + "bmAttributes = 0x%x \n", secd->bmAttributes.AsUchar + ); + + printf( + "wBytesPerInterval = 0x%x, decimal %u\n", secd->wBytesPerInterval, secd->wBytesPerInterval + ); + +} + + +void +rw_dev( HANDLE hDEV ) +/*++ +Routine Description: + + Called to do formatted ascii dump to console of USB + configuration, interface, and endpoint descriptors + (Cmdline "rwbulk -u" ) + +Arguments: + + handle to device + +Return Value: + + none + +--*/ +{ + UINT success; + int siz, nBytes; + UCHAR buf[256] = {0}; + PUSB_COMMON_DESCRIPTOR commonDesc = NULL; + PUSB_CONFIGURATION_DESCRIPTOR cd; + BOOL displayUnknown; + + siz = sizeof(buf); + + if (hDEV == INVALID_HANDLE_VALUE) { + NOISY(("DEV not open")); + return; + } + + success = DeviceIoControl(hDEV, + IOCTL_USBSAMP_GET_CONFIG_DESCRIPTOR, + buf, + siz, + buf, + siz, + (PULONG) &nBytes, + NULL); + + NOISY(("request complete, success = %u nBytes = %d\n", success, nBytes)); + + if (success) { + + UINT j = 0, k = 0, n; + PUCHAR pch; + PUCHAR descEnd; + + pch = buf; + n = 0; + + cd = (PUSB_CONFIGURATION_DESCRIPTOR) pch; + + descEnd = (PUCHAR)cd + cd->wTotalLength; + + commonDesc = (PUSB_COMMON_DESCRIPTOR)cd; + + while ((PUCHAR)commonDesc + sizeof(USB_COMMON_DESCRIPTOR) < descEnd && + (PUCHAR)commonDesc + commonDesc->bLength <= descEnd) + { + displayUnknown = FALSE; + + switch (commonDesc->bDescriptorType) + { + case USB_CONFIGURATION_DESCRIPTOR_TYPE: + if (commonDesc->bLength != sizeof(USB_CONFIGURATION_DESCRIPTOR)) + { + NOISY(("Configuration Descriptor's bLength filed does not match its size\n")); + displayUnknown = TRUE; + break; + } + n = 0; + print_USB_CONFIGURATION_DESCRIPTOR((PUSB_CONFIGURATION_DESCRIPTOR)commonDesc); + break; + + case USB_INTERFACE_DESCRIPTOR_TYPE: + if (commonDesc->bLength != sizeof(USB_INTERFACE_DESCRIPTOR)) + { + NOISY(("Interface Descriptor's bLength filed does not match its size\n")); + displayUnknown = TRUE; + break; + } + j = 0; + k = 0; + print_USB_INTERFACE_DESCRIPTOR((PUSB_INTERFACE_DESCRIPTOR)commonDesc, n++); + break; + + case USB_ENDPOINT_DESCRIPTOR_TYPE: + if (commonDesc->bLength != sizeof(USB_ENDPOINT_DESCRIPTOR)) + { + NOISY(("Endpoint Descriptor's bLength filed does not match its size\n")); + displayUnknown = TRUE; + break; + } + print_USB_ENDPOINT_DESCRIPTOR((PUSB_ENDPOINT_DESCRIPTOR)commonDesc, j++); + break; + + case USB_SUPERSPEED_ENDPOINT_COMPANION_DESCRIPTOR_TYPE: + if (commonDesc->bLength < sizeof(USB_SUPERSPEED_ENDPOINT_COMPANION_DESCRIPTOR)) + { + NOISY(("SuperSpeed Endpoint Companion Descriptor's bLength field does not match its size\n")); + displayUnknown = TRUE; + break; + } + print_USB_SUPERSPEED_ENDPOINT_COMPANION_DESCRIPTOR((PUSB_SUPERSPEED_ENDPOINT_COMPANION_DESCRIPTOR)commonDesc, k++); + break; + + default: + displayUnknown = TRUE; + break; + } + + if (displayUnknown) + { + NOISY(("Test application finds a unknown descriptor.\n")); + } + + commonDesc = (PUSB_COMMON_DESCRIPTOR)((PUCHAR)commonDesc + commonDesc->bLength); + + } + + } + + return; +} + + +int dumpUsbConfig() +/*++ +Routine Description: + + Called to do formatted ascii dump to console of USB + configuration, interface, and endpoint descriptors + (Cmdline "rwbulk -u" ) + +Arguments: + + none + +Return Value: + + none + +--*/ +{ + HANDLE hDEV = open_dev(); + + if (hDEV != INVALID_HANDLE_VALUE) + { + rw_dev( hDEV ); + CloseHandle(hDEV); + } + + return 0; + +} +// End, routines for USB configuration and pipe info dump (Cmdline "rwbulk -u" ) + + + +int +_cdecl +main( + _In_ int argc, + _In_reads_(argc) char *argv[] + ) +/*++ +Routine Description: + + Entry point to rwbulk.exe + Parses cmdline, performs user-requested tests + +Arguments: + + argc, argv standard console 'c' app arguments + +Return Value: + + Zero + +--*/ + +{ + char * pinBuf = NULL; + char * poutBuf = NULL; + int nBytesRead; + int nBytesWrite; + ULONG i; + ULONG j; + int ok; + UINT success; + HANDLE hRead = INVALID_HANDLE_VALUE; + HANDLE hWrite = INVALID_HANDLE_VALUE; + ULONG fail = 0L; + + parse(argc, argv ); + + // dump USB configuation and pipe info + if( fDumpUsbConfig ) { + dumpUsbConfig(); + } + + // doing a read, write, or both test + if ((fRead) || (fWrite)) { + + if (fRead) { + // + // open the output file + // + if ( fDumpReadData ) { // round size to sizeof ULONG for readable dumping + + while( ReadLen % sizeof( ULONG ) ) { + ReadLen++; + } + } + + hRead = open_file( inPipe); + pinBuf = (char*) malloc(ReadLen); + + } + + if (fWrite) { + if ( fDumpReadData ) { // round size to sizeof ULONG for readable dumping + while( WriteLen % sizeof( ULONG ) ) { + WriteLen++; + } + } + + hWrite = open_file( outPipe); + poutBuf = (char*)malloc(WriteLen); + } + + for (i=0; i W (%04.4u) : request %06.6d bytes -- %06.6d bytes written\n", + outPipe, i, WriteLen, nBytesWrite); + + //assert(nBytesWrite == WriteLen); + } + + if (fRead && pinBuf) { + + success = ReadFile(hRead, pinBuf, ReadLen, (PULONG) &nBytesRead, NULL); + + if (success) { + printf("<%s> R (%04.4u) : request %06.6d bytes -- %06.6d bytes read\n", + inPipe, i, ReadLen, nBytesRead); + + if (fWrite && fCompareData) { + + // + // validate the input buffer against what + // we sent to the device (loopback test) + // +#pragma warning(suppress: 26053) + ok = compare_buffs(pinBuf, poutBuf, nBytesRead); + + if( fDumpReadData ) { + printf("Dumping read buffer\n"); + dump( (PUCHAR) pinBuf, nBytesRead ); + printf("Dumping write buffer\n"); + dump( (PUCHAR) poutBuf, nBytesRead ); + } + assert(ok); + + if(ok != 1) { + fail++; + } + + assert(ReadLen == WriteLen); + assert(nBytesRead == ReadLen); + } + } + } + } + + if (pinBuf) { + free(pinBuf); + } + + if (poutBuf) { + free(poutBuf); + } + + // close devices if needed + if(hRead != INVALID_HANDLE_VALUE) + CloseHandle(hRead); + + if(hWrite != INVALID_HANDLE_VALUE) + CloseHandle(hWrite); + } + + return 0; +} diff --git a/usb/usbsamp/exe/testapp.rc b/usb/usbsamp/exe/testapp.rc new file mode 100644 index 000000000..3fa812118 --- /dev/null +++ b/usb/usbsamp/exe/testapp.rc @@ -0,0 +1,12 @@ +#include + +#include + +#define VER_FILETYPE VFT_DLL +#define VER_FILESUBTYPE VFT2_UNKNOWN +#define VER_FILEDESCRIPTION_STR "UsbSamp Bulk & Isoch Read and Write test App" +#define VER_INTERNALNAME_STR "usbsamp.exe" +#define VER_ORIGINALFILENAME_STR "usbsamp.exe" + +#include + diff --git a/usb/usbsamp/exe/usbsamp.vcxproj b/usb/usbsamp/exe/usbsamp.vcxproj new file mode 100644 index 000000000..5c27d51c3 --- /dev/null +++ b/usb/usbsamp/exe/usbsamp.vcxproj @@ -0,0 +1,179 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {82B15C8E-818A-41C0-AE62-1AEA7270E8B6} + $(MSBuildProjectName) + Debug + Win32 + {D84698F9-5D22-4B3F-ADC9-A65171DA8464} + + + + 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) + + + + + + + + + + + + + + + + usbsamp + + + usbsamp + + + usbsamp + + + usbsamp + + + + true + Level4 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\sys + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\sys + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\sys + + + %(AdditionalDependencies);setupapi.lib + + + + + true + Level4 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\sys + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\sys + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\sys + + + %(AdditionalDependencies);setupapi.lib + + + + + true + Level4 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\sys + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\sys + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\sys + + + %(AdditionalDependencies);setupapi.lib + + + + + true + Level4 + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\sys + + + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\sys + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);..\sys + + + %(AdditionalDependencies);setupapi.lib + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/usb/usbsamp/exe/usbsamp.vcxproj.Filters b/usb/usbsamp/exe/usbsamp.vcxproj.Filters new file mode 100644 index 000000000..d838bfe3a --- /dev/null +++ b/usb/usbsamp/exe/usbsamp.vcxproj.Filters @@ -0,0 +1,27 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {EE38233A-C328-44AC-A845-0B8F28FBE8F3} + + + h;hpp;hxx;hm;inl;inc;xsd + {F6CE757F-951D-4785-A12E-D52DE74A7231} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {9044173D-AE9F-4798-9138-C397123B741F} + + + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/usb/usbsamp/sys/bulkrwr.c b/usb/usbsamp/sys/bulkrwr.c new file mode 100644 index 000000000..475c89ed8 --- /dev/null +++ b/usb/usbsamp/sys/bulkrwr.c @@ -0,0 +1,907 @@ +/*++ + +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: + + bulkrwr.c + +Abstract: + + This file has routines to perform reads and writes. + The read and writes are for bulk transfers. + +Environment: + + Kernel mode + +--*/ + +#include "private.h" + +#if !defined(BUFFERED_READ_WRITE) // if doing DIRECT_IO + +VOID +ReadWriteBulkEndPoints( + _In_ WDFQUEUE Queue, + _In_ WDFREQUEST Request, + _In_ ULONG Length, + _In_ WDF_REQUEST_TYPE RequestType + ) +/*++ + +Routine Description: + + This callback is invoked when the framework received WdfRequestTypeRead or + WdfRequestTypeWrite request. This read/write is performed in stages of + maximum transfer size. Once a stage of transfer is complete, then the + request is circulated again, until the requested length of transfer is + performed. + +Arguments: + + Queue - Handle to the framework queue object that is associated + with the I/O request. + + Request - Handle to a framework request object. This one represents + the WdfRequestTypeRead/WdfRequestTypeWrite IRP received by the framework. + + Length - Length of the input/output buffer. + +Return Value: + + VOID + +--*/ +{ + PMDL newMdl=NULL, requestMdl = NULL; + PURB urb = NULL; + WDFMEMORY urbMemory; + ULONG totalLength = Length; + ULONG stageLength = 0; + ULONG urbFlags = 0; + NTSTATUS status; + ULONG_PTR virtualAddress = 0; + PREQUEST_CONTEXT rwContext = NULL; + PFILE_CONTEXT fileContext = NULL; + WDFUSBPIPE pipe; + WDF_USB_PIPE_INFORMATION pipeInfo; + WDF_OBJECT_ATTRIBUTES objectAttribs; + USBD_PIPE_HANDLE usbdPipeHandle; + PDEVICE_CONTEXT deviceContext; + ULONG maxTransferSize; + PPIPE_CONTEXT pipeContext; + + UsbSamp_DbgPrint(3, ("UsbSamp_DispatchReadWrite - begins\n")); + + // + // First validate input parameters. + // + deviceContext = GetDeviceContext(WdfIoQueueGetDevice(Queue)); + + if (totalLength > deviceContext->MaximumTransferSize) { + UsbSamp_DbgPrint(1, ("Transfer length > circular buffer\n")); + status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + if ((RequestType != WdfRequestTypeRead) && + (RequestType != WdfRequestTypeWrite)) { + UsbSamp_DbgPrint(1, ("RequestType has to be either Read or Write\n")); + status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + // + // Get the pipe associate with this request. + // + fileContext = GetFileContext(WdfRequestGetFileObject(Request)); + pipe = fileContext->Pipe; + pipeContext = GetPipeContext(pipe); + WDF_USB_PIPE_INFORMATION_INIT(&pipeInfo); + WdfUsbTargetPipeGetInformation(pipe, &pipeInfo); + + if ((WdfUsbPipeTypeBulk != pipeInfo.PipeType) && + (WdfUsbPipeTypeInterrupt != pipeInfo.PipeType)) { + UsbSamp_DbgPrint(1, ("Usbd pipe type is not bulk or interrupt\n")); + status = STATUS_INVALID_DEVICE_REQUEST; + goto Exit; + + } + + rwContext = GetRequestContext(Request); + + if (RequestType == WdfRequestTypeRead) { + + status = WdfRequestRetrieveOutputWdmMdl(Request, &requestMdl); + if (!NT_SUCCESS(status)){ + UsbSamp_DbgPrint(1, ("WdfRequestRetrieveOutputWdmMdl failed %x\n", status)); + goto Exit; + } + + urbFlags |= USBD_TRANSFER_DIRECTION_IN; + rwContext->Read = TRUE; + UsbSamp_DbgPrint(3, ("Read operation\n")); + + } + else { + status = WdfRequestRetrieveInputWdmMdl(Request, &requestMdl); + if (!NT_SUCCESS(status)){ + UsbSamp_DbgPrint(1, ("WdfRequestRetrieveInputWdmMdl failed %x\n", status)); + goto Exit; + } + + urbFlags |= USBD_TRANSFER_DIRECTION_OUT; + rwContext->Read = FALSE; + UsbSamp_DbgPrint(3, ("Write operation\n")); + } + + urbFlags |= USBD_SHORT_TRANSFER_OK; + virtualAddress = (ULONG_PTR) MmGetMdlVirtualAddress(requestMdl); + + // + // The transfer request is for totalLength. + // We can perform a max of maxTransfersize in each stage. + // + maxTransferSize = GetMaxTransferSize(pipe, deviceContext); + + if (totalLength > maxTransferSize) { + stageLength = maxTransferSize; + } + else { + stageLength = totalLength; + } + + newMdl = IoAllocateMdl((PVOID) virtualAddress, + totalLength, + FALSE, + FALSE, + NULL); + + if (newMdl == NULL) { + UsbSamp_DbgPrint(1, ("Failed to alloc mem for mdl\n")); + status = STATUS_INSUFFICIENT_RESOURCES; + goto Exit; + } + + // + // map the portion of user-buffer described by an mdl to another mdl + // + IoBuildPartialMdl(requestMdl, + newMdl, + (PVOID) virtualAddress, + stageLength); + + WDF_OBJECT_ATTRIBUTES_INIT(&objectAttribs); + objectAttribs.ParentObject = Request; + + status = WdfUsbTargetDeviceCreateUrb(deviceContext->WdfUsbTargetDevice, + &objectAttribs, + &urbMemory, + &urb); + + if (!NT_SUCCESS(status)) { + UsbSamp_DbgPrint(1, ("WdfUsbTargetDeviceCreateUrb failed %x\n", status)); + goto Exit; + } + +#if (NTDDI_VERSION >= NTDDI_WIN8) + if(WdfUsbPipeTypeBulk == pipeInfo.PipeType && + pipeContext->StreamConfigured == TRUE) { + // + // For super speed bulk pipe with streams, we specify one of its associated + // usbd pipe handles to format an URB for sending or receiving data. + // The usbd pipe handle is returned by the HCD via sucessful open-streams request + // + usbdPipeHandle = GetStreamPipeHandleFromBulkPipe(pipe); + } + else { + usbdPipeHandle = WdfUsbTargetPipeWdmGetPipeHandle(pipe); + } +#else + usbdPipeHandle = WdfUsbTargetPipeWdmGetPipeHandle(pipe); +#endif + + UsbBuildInterruptOrBulkTransferRequest(urb, + sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER), + usbdPipeHandle, + NULL, + newMdl, + stageLength, + urbFlags, + NULL); + + status = WdfUsbTargetPipeFormatRequestForUrb(pipe, Request, urbMemory, NULL ); + if (!NT_SUCCESS(status)) { + UsbSamp_DbgPrint(1, ("Failed to format requset for urb\n")); + status = STATUS_INSUFFICIENT_RESOURCES; + goto Exit; + } + + WdfRequestSetCompletionRoutine(Request, UsbSamp_EvtReadWriteCompletion, deviceContext); + + // + // set REQUEST_CONTEXT parameters. + // + rwContext->UrbMemory = urbMemory; + rwContext->Mdl = newMdl; + rwContext->Length = totalLength - stageLength; + rwContext->Numxfer = 0; + rwContext->VirtualAddress = virtualAddress + stageLength; + + if (!WdfRequestSend(Request, WdfUsbTargetPipeGetIoTarget(pipe), WDF_NO_SEND_OPTIONS)) { + status = WdfRequestGetStatus(Request); + NT_ASSERT(!NT_SUCCESS(status)); + } + +Exit: + if (!NT_SUCCESS(status)) { + WdfRequestCompleteWithInformation(Request, status, 0); + + if (newMdl != NULL) { + IoFreeMdl(newMdl); + } + } + + UsbSamp_DbgPrint(3, ("UsbSamp_DispatchReadWrite - ends\n")); + + return; +} + + +VOID +UsbSamp_EvtReadWriteCompletion( + _In_ WDFREQUEST Request, + _In_ WDFIOTARGET Target, + PWDF_REQUEST_COMPLETION_PARAMS CompletionParams, + _In_ WDFCONTEXT Context + ) +/*++ + +Routine Description: + + This is the completion routine for reads/writes + If the irp completes with success, we check if we + need to recirculate this irp for another stage of + transfer. + +Arguments: + + Context - Driver supplied context + Device - Device handle + Request - Request handle + Params - request completion params + +Return Value: + None + +--*/ +{ + PMDL requestMdl; + WDFUSBPIPE pipe; + ULONG stageLength; + NTSTATUS status; + PREQUEST_CONTEXT rwContext; + PURB urb; + PCHAR operation; + ULONG bytesReadWritten; + ULONG maxTransferSize; + PDEVICE_CONTEXT deviceContext; + + rwContext = GetRequestContext(Request); + deviceContext = Context; + + if (rwContext->Read) { + operation = "Read"; + } + else { + operation = "Write"; + } + + pipe = (WDFUSBPIPE) Target ; + status = CompletionParams->IoStatus.Status; + + if (!NT_SUCCESS(status)){ + // + // Queue a workitem to reset the pipe because the completion could be + // running at DISPATCH_LEVEL. + // + QueuePassiveLevelCallback(WdfIoTargetGetDevice(Target), pipe); + goto End; + } + + urb = (PURB) WdfMemoryGetBuffer(rwContext->UrbMemory, NULL); + bytesReadWritten = urb->UrbBulkOrInterruptTransfer.TransferBufferLength; + rwContext->Numxfer += bytesReadWritten; + + // + // If there is anything left to transfer. + // + if (rwContext->Length == 0) { + // + // this is the last transfer + // + WdfRequestSetInformation(Request, rwContext->Numxfer); + goto End; + } + + // + // Start another transfer + // + UsbSamp_DbgPrint(3, ("Stage next %s transfer...\n", operation)); + + // + // The transfer request is for totalLength. + // We can perform a max of maxTransfersize in each stage. + // + maxTransferSize = GetMaxTransferSize(pipe, deviceContext); + + if (rwContext->Length > maxTransferSize) { + stageLength = maxTransferSize; + } + else { + stageLength = rwContext->Length; + } + + // + // Following call is required to free any mapping made on the partial MDL + // and reset internal MDL state. + // + MmPrepareMdlForReuse(rwContext->Mdl); + + if (rwContext->Read) { + status = WdfRequestRetrieveOutputWdmMdl(Request, &requestMdl); + if (!NT_SUCCESS(status)){ + UsbSamp_DbgPrint(1, ("WdfRequestRetrieveOutputWdmMdl for Read failed %x\n", status)); + goto End; + } + } + else { + status = WdfRequestRetrieveInputWdmMdl(Request, &requestMdl); + if (!NT_SUCCESS(status)){ + UsbSamp_DbgPrint(1, ("WdfRequestRetrieveInputWdmMdl for Write failed %x\n", status)); + goto End; + } + } + + IoBuildPartialMdl(requestMdl, + rwContext->Mdl, + (PVOID) rwContext->VirtualAddress, + stageLength); + + // + // reinitialize the urb + // + urb->UrbBulkOrInterruptTransfer.TransferBufferLength = stageLength; + + rwContext->VirtualAddress += stageLength; + rwContext->Length -= stageLength; + + // + // Format the request to send a URB to a USB pipe. + // + status = WdfUsbTargetPipeFormatRequestForUrb(pipe, + Request, + rwContext->UrbMemory, + NULL); + if (!NT_SUCCESS(status)) { + UsbSamp_DbgPrint(1, ("Failed to format requset for urb\n")); + status = STATUS_INSUFFICIENT_RESOURCES; + goto End; + } + + WdfRequestSetCompletionRoutine(Request, UsbSamp_EvtReadWriteCompletion, deviceContext); + + // + // Send the request asynchronously. + // + if (!WdfRequestSend(Request, WdfUsbTargetPipeGetIoTarget(pipe), WDF_NO_SEND_OPTIONS)) { + UsbSamp_DbgPrint(1, ("WdfRequestSend for %s failed\n", operation)); + status = WdfRequestGetStatus(Request); + goto End; + } + + // + // Else when the request completes, this completion routine will be + // called again. + // + return; + +End: + // + // We are here because the request failed or some other call failed. + // Dump the request context, complete the request and return. + // + DbgPrintRWContext(rwContext); + + IoFreeMdl(rwContext->Mdl); + + UsbSamp_DbgPrint(3, ("%s request completed with status 0x%x\n", + operation, status)); + + WdfRequestComplete(Request, status); + + return; +} + +#else + +VOID +ReadWriteBulkEndPoints( + _In_ WDFQUEUE Queue, + _In_ WDFREQUEST Request, + _In_ ULONG Length, + _In_ WDF_REQUEST_TYPE RequestType + ) +/*++ + +Routine Description: + + This callback is invoked when the framework received WdfRequestTypeRead or + RP_MJ_WRITE request. This read/write is performed in stages of + maximum transfer size. Once a stage of transfer is complete, then the + request is circulated again, until the requested length of transfer is + performed. + +Arguments: + + Queue - Handle to the framework queue object that is associated + with the I/O request. + + Request - Handle to a framework request object. This one represents + the WdfRequestTypeRead/WdfRequestTypeWrite IRP received by the framework. + + Length - Length of the input/output buffer. + +Return Value: + + VOID + +--*/ +{ + size_t totalLength = Length; + size_t stageLength = 0; + NTSTATUS status; + PVOID virtualAddress = 0; + PREQUEST_CONTEXT rwContext = NULL; + PFILE_CONTEXT fileContext = NULL; + WDFUSBPIPE pipe; + WDF_USB_PIPE_INFORMATION pipeInfo; + WDFMEMORY reqMemory; + WDFMEMORY_OFFSET offset; + WDF_OBJECT_ATTRIBUTES objectAttribs; + PDEVICE_CONTEXT deviceContext; + PPIPE_CONTEXT pipeContext; + + ULONG maxTransferSize; + + UsbSamp_DbgPrint(3, ("UsbSamp_DispatchReadWrite - begins\n")); + + // + // First validate input parameters. + // + deviceContext = GetDeviceContext(WdfIoQueueGetDevice(Queue)); + + if (totalLength > deviceContext->MaximumTransferSize) { + UsbSamp_DbgPrint(1, ("Transfer length > circular buffer\n")); + status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + if ((RequestType != WdfRequestTypeRead) && + (RequestType != WdfRequestTypeWrite)) { + UsbSamp_DbgPrint(1, ("RequestType has to be either Read or Write\n")); + status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + // + // Get the pipe associate with this request. + // + fileContext = GetFileContext(WdfRequestGetFileObject(Request)); + pipe = fileContext->Pipe; + pipeContext = GetPipeContext(pipe); + WDF_USB_PIPE_INFORMATION_INIT(&pipeInfo); + WdfUsbTargetPipeGetInformation(pipe, &pipeInfo); + + rwContext = GetRequestContext(Request); + + if (RequestType == WdfRequestTypeRead) { + status = WdfRequestRetrieveOutputBuffer(Request, Length, &virtualAddress, &totalLength); + rwContext->Read = TRUE; + + } + else { //Write + + status = WdfRequestRetrieveInputBuffer(Request, Length, &virtualAddress, &totalLength); + rwContext->Read = FALSE; + } + + if (!NT_SUCCESS(status)){ + UsbSamp_DbgPrint(1, ("WdfRequestRetrieveInputBuffer failed\n")); + goto Exit; + } + + // + // The transfer request is for totalLength. + // We can perform a max of maxTransfersize in each stage. + // + maxTransferSize = GetMaxTransferSize(pipe, deviceContext); + + if (totalLength > maxTransferSize) { + stageLength = maxTransferSize; + } + else { + stageLength = totalLength; + } + + WDF_OBJECT_ATTRIBUTES_INIT(&objectAttribs); + objectAttribs.ParentObject = Request; + status = WdfMemoryCreatePreallocated(&objectAttribs, + virtualAddress, + totalLength, + &reqMemory); + if (!NT_SUCCESS(status)){ + UsbSamp_DbgPrint(1, ("WdfMemoryCreatePreallocated failed\n")); + goto Exit; + } + + offset.BufferOffset = 0; + offset.BufferLength = stageLength; + + // + // The framework format call validates to make sure that you are reading or + // writing to the right pipe type, sets the appropriate transfer flags, + // creates an URB and initializes the request. + // + if (RequestType == WdfRequestTypeRead) { + + UsbSamp_DbgPrint(3, ("Read operation\n")); + status = WdfUsbTargetPipeFormatRequestForRead(pipe, + Request, + reqMemory, + &offset); + } + else { + + UsbSamp_DbgPrint(3, ("Write operation\n")); + status = WdfUsbTargetPipeFormatRequestForWrite(pipe, + Request, + reqMemory, + &offset); + } + + if (!NT_SUCCESS(status)) { + UsbSamp_DbgPrint(1, ("WdfUsbTargetPipeFormatRequest failed 0x%x\n", status)); + goto Exit; + } + +#if (NTDDI_VERSION >= NTDDI_WIN8) + // + // If the request is for a super speed bulk pipe with streams, + // configure its urb's PipeHandle with its associated stream's PipeHandle + // + if(WdfUsbPipeTypeBulk == pipeInfo.PipeType && + pipeContext->StreamConfigured == TRUE) { + ConfigureStreamPipeHandleForRequest(Request, pipe); + } +#endif + + WdfRequestSetCompletionRoutine( + Request, + UsbSamp_EvtReadWriteCompletion, + deviceContext); + // + // set REQUEST_CONTEXT parameters. + // + rwContext->Length = (ULONG)totalLength - (ULONG)stageLength; + rwContext->Numxfer = 0; + + // + // Send the request asynchronously. + // + if (WdfRequestSend(Request, WdfUsbTargetPipeGetIoTarget(pipe), WDF_NO_SEND_OPTIONS) == FALSE) { + UsbSamp_DbgPrint(1, ("WdfRequestSend failed\n")); + status = WdfRequestGetStatus(Request); + goto Exit; + } + + +Exit: + if (!NT_SUCCESS(status)) { + WdfRequestCompleteWithInformation(Request, status, 0); + } + + UsbSamp_DbgPrint(3, ("UsbSamp_DispatchReadWrite - ends\n")); + + return; +} + +// TODO: Use mouser sample trick to avoid stack recursion + +VOID +UsbSamp_EvtReadWriteCompletion( + _In_ WDFREQUEST Request, + _In_ WDFIOTARGET Target, + PWDF_REQUEST_COMPLETION_PARAMS CompletionParams, + _In_ WDFCONTEXT Context + ) +/*++ + +Routine Description: + + This is the completion routine for reads/writes + If the irp completes with success, we check if we + need to recirculate this irp for another stage of + transfer. + +Arguments: + + Context - Driver supplied context + Device - Device handle + Request - Request handle + Params - request completion params + +Return Value: + None + +--*/ +{ + WDFUSBPIPE pipe; + ULONG stageLength = 0; + NTSTATUS status; + PREQUEST_CONTEXT rwContext; + ULONG bytesReadWritten; + WDFMEMORY_OFFSET offset; + PCHAR operation; + PWDF_USB_REQUEST_COMPLETION_PARAMS usbCompletionParams; + PPIPE_CONTEXT pipeContext; + WDF_USB_PIPE_INFORMATION pipeInfo; + ULONG maxTransferSize; + PDEVICE_CONTEXT deviceContext; + + usbCompletionParams = CompletionParams->Parameters.Usb.Completion; + rwContext = GetRequestContext(Request); + deviceContext = Context; + + if (rwContext->Read) { + operation = "Read"; + bytesReadWritten = (ULONG)usbCompletionParams->Parameters.PipeRead.Length; + } + else { + operation = "Write"; + bytesReadWritten = (ULONG)usbCompletionParams->Parameters.PipeWrite.Length; + } + + pipe = (WDFUSBPIPE) Target; + pipeContext = GetPipeContext(pipe); + WDF_USB_PIPE_INFORMATION_INIT(&pipeInfo); + WdfUsbTargetPipeGetInformation(pipe,&pipeInfo); + status = CompletionParams->IoStatus.Status; + + if (!NT_SUCCESS(status)){ + // + // Queue a workitem to reset the pipe because the completion could be + // running at DISPATCH_LEVEL. + // TODO: preallocate per pipe workitem to avoid allocation failure. + QueuePassiveLevelCallback(WdfIoTargetGetDevice(Target), pipe); + goto End; + } + + rwContext->Numxfer += bytesReadWritten; + + // + // If there is anything left to transfer. + // + if (rwContext->Length == 0) { + // + // this is the last transfer + // + WdfRequestSetInformation(Request, rwContext->Numxfer); + goto End; + } + + // + // Start another transfer + // + UsbSamp_DbgPrint(3, ("Stage next %s transfer...\n", operation)); + + // + // The transfer request is for totalLength. + // We can perform a max of maxTransfersize in each stage. + // + maxTransferSize = GetMaxTransferSize(pipe, deviceContext); + + if (rwContext->Length > maxTransferSize) { + stageLength = maxTransferSize; + } + else + { + stageLength = rwContext->Length; + } + + offset.BufferOffset = rwContext->Numxfer; + offset.BufferLength = stageLength; + + rwContext->Length -= stageLength; + + if (rwContext->Read) { + + status = WdfUsbTargetPipeFormatRequestForRead( + pipe, + Request, + usbCompletionParams->Parameters.PipeRead.Buffer, + &offset); + + } + else { + + status = WdfUsbTargetPipeFormatRequestForWrite( + pipe, + Request, + usbCompletionParams->Parameters.PipeWrite.Buffer, + &offset); + + } + + if (!NT_SUCCESS(status)) { + UsbSamp_DbgPrint(1, ("WdfUsbTargetPipeFormat%sRequest failed 0x%x\n", + operation, status)); + goto End; + } + +#if (NTDDI_VERSION >= NTDDI_WIN8) + // + // If the request is for a super speed bulk pipe with streams, + // configure its urb's PipeHandle with its associated stream's PipeHandle + // + if(WdfUsbPipeTypeBulk == pipeInfo.PipeType && + pipeContext->StreamConfigured == TRUE) { + ConfigureStreamPipeHandleForRequest(Request, pipe); + } +#endif + + WdfRequestSetCompletionRoutine( + Request, + UsbSamp_EvtReadWriteCompletion, + deviceContext); + + // + // Send the request asynchronously. + // + if (!WdfRequestSend(Request, WdfUsbTargetPipeGetIoTarget(pipe), WDF_NO_SEND_OPTIONS)) { + UsbSamp_DbgPrint(1, ("WdfRequestSend for %s failed\n", operation)); + status = WdfRequestGetStatus(Request); + goto End; + } + + // + // Else when the request completes, this completion routine will be + // called again. + // + return; + +End: + // + // We are here because the request failed or some other call failed. + // Dump the request context, complete the request and return. + // + DbgPrintRWContext(rwContext); + + UsbSamp_DbgPrint(3, ("%s request completed with status 0x%x\n", + operation, status)); + + WdfRequestComplete(Request, status); + + return; +} + +#endif + +VOID +UsbSamp_EvtReadWriteWorkItem( + _In_ WDFWORKITEM WorkItem + ) +{ + PWORKITEM_CONTEXT pItemContext; + NTSTATUS status; + + UsbSamp_DbgPrint(3, ("ReadWriteWorkItem called\n")); + + pItemContext = GetWorkItemContext(WorkItem); + + status = ResetPipe(pItemContext->Pipe); + if (!NT_SUCCESS(status)) { + + UsbSamp_DbgPrint(1, ("ResetPipe failed 0x%x\n", status)); + + status = ResetDevice(pItemContext->Device); + if (!NT_SUCCESS(status)){ + + UsbSamp_DbgPrint(1, ("ResetDevice failed 0x%x\n", status)); + } + } + + WdfObjectDelete(WorkItem); + + return; +} + +NTSTATUS +QueuePassiveLevelCallback( + _In_ WDFDEVICE Device, + _In_ WDFUSBPIPE Pipe + ) +/*++ + +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: + + +Return Value: + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + PWORKITEM_CONTEXT context; + WDF_OBJECT_ATTRIBUTES attributes; + WDF_WORKITEM_CONFIG workitemConfig; + WDFWORKITEM hWorkItem; + + WDF_OBJECT_ATTRIBUTES_INIT(&attributes); + WDF_OBJECT_ATTRIBUTES_SET_CONTEXT_TYPE(&attributes, WORKITEM_CONTEXT); + attributes.ParentObject = Device; + + WDF_WORKITEM_CONFIG_INIT(&workitemConfig, UsbSamp_EvtReadWriteWorkItem); + + status = WdfWorkItemCreate( &workitemConfig, + &attributes, + &hWorkItem); + + if (!NT_SUCCESS(status)) { + return status; + } + + context = GetWorkItemContext(hWorkItem); + + context->Device = Device; + context->Pipe = Pipe; + + // + // Execute this work item. + // + WdfWorkItemEnqueue(hWorkItem); + + return STATUS_SUCCESS; +} + +VOID +DbgPrintRWContext( + PREQUEST_CONTEXT rwContext + ) +{ + UNREFERENCED_PARAMETER(rwContext); + + UsbSamp_DbgPrint(3, ("rwContext->UrbMemory = %p\n", + rwContext->UrbMemory)); + UsbSamp_DbgPrint(3, ("rwContext->Mdl = %p\n", + rwContext->Mdl)); + UsbSamp_DbgPrint(3, ("rwContext->Length = %d\n", + rwContext->Length)); + UsbSamp_DbgPrint(3, ("rwContext->Numxfer = %d\n", + rwContext->Numxfer)); + UsbSamp_DbgPrint(3, ("rwContext->VirtualAddress = %p\n", + (PVOID)rwContext->VirtualAddress)); + return; +} + diff --git a/usb/usbsamp/sys/device.c b/usb/usbsamp/sys/device.c new file mode 100644 index 000000000..a710a476a --- /dev/null +++ b/usb/usbsamp/sys/device.c @@ -0,0 +1,1674 @@ +/*++ + + 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: + + Bulk USB device driver for Intel 82930 USB test board + Plug and Play module. This file contains routines to handle pnp requests. + +Environment: + + Kernel mode + +--*/ + +#include "private.h" + +#ifdef ALLOC_PRAGMA + +#pragma alloc_text(PAGE, UsbSamp_EvtDeviceAdd) +#pragma alloc_text(PAGE, UsbSamp_EvtDevicePrepareHardware) +#pragma alloc_text(PAGE, UsbSamp_EvtDeviceContextCleanup) +#pragma alloc_text(PAGE, ReadAndSelectDescriptors) +#pragma alloc_text(PAGE, ConfigureDevice) +#pragma alloc_text(PAGE, SelectInterfaces) +#pragma alloc_text(PAGE, SetPowerPolicy) +#pragma alloc_text(PAGE, AbortPipes) +#pragma alloc_text(PAGE, ReadFdoRegistryKeyValue) +#pragma alloc_text(PAGE, RetrieveDeviceInformation) +#pragma alloc_text(PAGE, UsbSamp_ValidateConfigurationDescriptor) +#if (NTDDI_VERSION >= NTDDI_WIN8) +#pragma alloc_text(PAGE, UsbSamp_EvtPipeContextCleanup) +#pragma alloc_text(PAGE, InitializePipeContextForSuperSpeedDevice) +#pragma alloc_text(PAGE, GetEndpointDescriptorForEndpointAddress) +#pragma alloc_text(PAGE, InitializePipeContextForSuperSpeedIsochPipe) +#endif +#pragma alloc_text(PAGE, InitializePipeContextForHighSpeedDevice) +#pragma alloc_text(PAGE, InitializePipeContextForFullSpeedDevice) +#endif + +NTSTATUS +UsbSamp_EvtDeviceAdd( + WDFDRIVER Driver, + 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. All the software resources + should be allocated in this callback. + +Arguments: + + Driver - Handle to a framework driver object created in DriverEntry + + DeviceInit - Pointer to a framework-allocated WDFDEVICE_INIT structure. + +Return Value: + + NTSTATUS + +--*/ +{ + WDF_FILEOBJECT_CONFIG fileConfig; + WDF_PNPPOWER_EVENT_CALLBACKS pnpPowerCallbacks; + WDF_OBJECT_ATTRIBUTES attributes; + NTSTATUS status; + WDFDEVICE device; + WDF_DEVICE_PNP_CAPABILITIES pnpCaps; + WDF_IO_QUEUE_CONFIG ioQueueConfig; + PDEVICE_CONTEXT pDevContext; + WDFQUEUE queue; + ULONG maximumTransferSize; + + UNREFERENCED_PARAMETER(Driver); + + UsbSamp_DbgPrint (3, ("UsbSamp_EvtDeviceAdd routine\n")); + + PAGED_CODE(); + + // + // 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); + + pnpPowerCallbacks.EvtDevicePrepareHardware = UsbSamp_EvtDevicePrepareHardware; + + WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpPowerCallbacks); + + // + // Initialize the request attributes to specify the context size and type + // for every request created by framework for this device. + // + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, REQUEST_CONTEXT); + + WdfDeviceInitSetRequestAttributes(DeviceInit, &attributes); + + // + // Initialize WDF_FILEOBJECT_CONFIG_INIT struct to tell the + // framework whether you are interested in handle 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, + UsbSamp_EvtDeviceFileCreate, + WDF_NO_EVENT_CALLBACK, + WDF_NO_EVENT_CALLBACK + ); + + // + // Specify a context for FileObject. If you register FILE_EVENT callbacks, + // the framework by default creates a framework FILEOBJECT corresponding + // to the WDM fileobject. If you want to track any per handle context, + // use the context for FileObject. Driver that typically use FsContext + // field should instead use Framework FileObject context. + // + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, FILE_CONTEXT); + + WdfDeviceInitSetFileObjectConfig(DeviceInit, + &fileConfig, + &attributes); + +#if !defined(BUFFERED_READ_WRITE) + // + // I/O type is Buffered by default. We want to do direct I/O for Reads + // and Writes so set it explicitly. Please note that this sample + // can do isoch transfer only if the io type is directio. + // + WdfDeviceInitSetIoType(DeviceInit, WdfDeviceIoDirect); + +#endif + + // + // Now specify the size of device extension where we track per device + // context.DeviceInit is completely initialized. So call the framework + // to create the device and attach it to the lower stack. + // + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, DEVICE_CONTEXT); + attributes.EvtCleanupCallback = UsbSamp_EvtDeviceContextCleanup; + + status = WdfDeviceCreate(&DeviceInit, &attributes, &device); + if (!NT_SUCCESS(status)) { + UsbSamp_DbgPrint(1, ("WdfDeviceCreate failed with Status code 0x%x\n", status)); + return status; + } + + // + // Get the DeviceObject context by using accessor function specified in + // the WDF_DECLARE_CONTEXT_TYPE_WITH_NAME macro for DEVICE_CONTEXT. + // + + pDevContext = GetDeviceContext(device); + + // + //Get MaximumTransferSize from registry + // + maximumTransferSize = 0; + + ReadFdoRegistryKeyValue(Driver, + L"MaximumTransferSize", + &maximumTransferSize); + + if (maximumTransferSize){ + pDevContext->MaximumTransferSize = maximumTransferSize; + } + else { + pDevContext->MaximumTransferSize = DEFAULT_REGISTRY_TRANSFER_SIZE; + } + + // + // Tell the framework to set the SurpriseRemovalOK in the DeviceCaps so + // that you don't get the popup in usermode (on Win2K) when you surprise + // remove the device. + // + WDF_DEVICE_PNP_CAPABILITIES_INIT(&pnpCaps); + pnpCaps.SurpriseRemovalOK = WdfTrue; + + WdfDeviceSetPnpCapabilities(device, &pnpCaps); + + // + // Register I/O callbacks to tell the framework that you are interested + // in handling WdfRequestTypeRead, WdfRequestTypeWrite, and + // IRP_MJ_DEVICE_CONTROL requests. + // 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. + // This queue will be, by default, automanaged by the framework with + // respect to PNP and Power events. That is, framework will take care + // of queuing, failing, dispatching incoming requests based on the current + // pnp/power state of the device. + // + + WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&ioQueueConfig, + WdfIoQueueDispatchParallel); + + ioQueueConfig.EvtIoRead = UsbSamp_EvtIoRead; + ioQueueConfig.EvtIoWrite = UsbSamp_EvtIoWrite; + ioQueueConfig.EvtIoDeviceControl = UsbSamp_EvtIoDeviceControl; + ioQueueConfig.EvtIoStop = UsbSamp_EvtIoStop; + + status = WdfIoQueueCreate(device, + &ioQueueConfig, + WDF_NO_OBJECT_ATTRIBUTES, + &queue);// pointer to default queue + if (!NT_SUCCESS(status)) { + UsbSamp_DbgPrint(1, ("WdfIoQueueCreate failed for Default Queue 0x%x\n", status)); + return status; + } + + // + // Create a synchronized manual queue so we can retrieve one read request at a + // time and dispatch it to the lower driver with the right StartFrame number. + // + WDF_IO_QUEUE_CONFIG_INIT(&ioQueueConfig, + WdfIoQueueDispatchManual); + + WDF_OBJECT_ATTRIBUTES_INIT(&attributes); + attributes.SynchronizationScope=WdfSynchronizationScopeQueue; + + ioQueueConfig.EvtIoStop = UsbSamp_EvtIoStop; + + status = WdfIoQueueCreate(device, + &ioQueueConfig, + &attributes, + &pDevContext->IsochReadQueue); + if (!NT_SUCCESS(status)) { + UsbSamp_DbgPrint(1, ("WdfIoQueueCreate failed for isoch 0x%x\n", status)); + return status; + } + + // + // Register a ready notification routine so we get notified whenever the queue transitions + // from empty to non-empty state. + // + status = WdfIoQueueReadyNotify(pDevContext->IsochReadQueue, + UsbSamp_EvtIoQueueReadyNotification, + (WDFCONTEXT)pDevContext); + + if (!NT_SUCCESS(status)) { + UsbSamp_DbgPrint(1, ("WdfIoQueueReadyNotify failed for isoch 0x%x\n", status)); + return status; + } + + // + // Create a synchronized manual queue so we can retrieve one write request at a + // time and dispatch it to the lower driver with the right StartFrame number. + // + WDF_IO_QUEUE_CONFIG_INIT(&ioQueueConfig, + WdfIoQueueDispatchManual); + + WDF_OBJECT_ATTRIBUTES_INIT(&attributes); + attributes.SynchronizationScope=WdfSynchronizationScopeQueue; + + ioQueueConfig.EvtIoStop = UsbSamp_EvtIoStop; + + status = WdfIoQueueCreate(device, + &ioQueueConfig, + &attributes, + &pDevContext->IsochWriteQueue); + if (!NT_SUCCESS(status)) { + UsbSamp_DbgPrint(1, ("WdfIoQueueCreate failed for isoch 0x%x\n", status)); + return status; + } + + // + // Register a ready notification routine so we get notified whenever the queue transitions + // from empty to non-empty state. + // + status = WdfIoQueueReadyNotify(pDevContext->IsochWriteQueue, + UsbSamp_EvtIoQueueReadyNotification, + (WDFCONTEXT)pDevContext); + + if (!NT_SUCCESS(status)) { + UsbSamp_DbgPrint(1, ("WdfIoQueueReadyNotify failed for isoch 0x%x\n", status)); + return status; + } + + // + // Register a device interface so that app can find our device and talk to it. + // + status = WdfDeviceCreateDeviceInterface(device, + (LPGUID) &GUID_CLASS_USBSAMP_USB, + NULL);// Reference String + if (!NT_SUCCESS(status)) { + UsbSamp_DbgPrint(1, ("WdfDeviceCreateDeviceInterface failed 0x%x\n", status)); + return status; + } + + status = USBD_CreateHandle(WdfDeviceWdmGetDeviceObject(device), + WdfDeviceWdmGetAttachedDevice(device), + USBD_CLIENT_CONTRACT_VERSION_602, + POOL_TAG, + &pDevContext->UsbdHandle); + if(!NT_SUCCESS(status)){ + UsbSamp_DbgPrint(1, ("USBD_CreateHandle failed 0x%x", status)); + return status; + } + + UsbSamp_DbgPrint(3, ("EvtDriverDeviceAdd - ends\n")); + + return status; +} + +NTSTATUS +UsbSamp_EvtDevicePrepareHardware( + _In_ WDFDEVICE Device, + _In_ WDFCMRESLIST ResourceList, + _In_ WDFCMRESLIST ResourceListTranslated + ) +/*++ + +Routine Description: + + In this callback, the driver does whatever is necessary to make the + hardware ready to use. In the case of a USB device, this involves + reading and selecting descriptors. + + //TODO: + +Arguments: + + Device - handle to a device + +Return Value: + + NT status value + +--*/ +{ + NTSTATUS status; + PDEVICE_CONTEXT pDeviceContext; + + UNREFERENCED_PARAMETER(ResourceList); + UNREFERENCED_PARAMETER(ResourceListTranslated); + + UsbSamp_DbgPrint(3, ("EvtDevicePrepareHardware - begins\n")); + + PAGED_CODE(); + + pDeviceContext = GetDeviceContext(Device); + + // + // Read the device descriptor, configuration descriptor + // and select the interface descriptors + // + status = ReadAndSelectDescriptors(Device); + + if (!NT_SUCCESS(status)) { + UsbSamp_DbgPrint(1, ("ReadandSelectDescriptors failed\n")); + return status; + } + + // + // Enable wait-wake and idle timeout if the device supports it + // + if (pDeviceContext->WaitWakeEnable){ + status = SetPowerPolicy(Device); + if (!NT_SUCCESS (status)) { + UsbSamp_DbgPrint(3, ("UsbSampSetPowerPolicy failed\n")); + return status; + } + } + + UsbSamp_DbgPrint(3, ("EvtDevicePrepareHardware - ends\n")); + + return status; +} + +NTSTATUS +SetPowerPolicy( + _In_ WDFDEVICE Device + ) +{ + WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS idleSettings; + WDF_DEVICE_POWER_POLICY_WAKE_SETTINGS wakeSettings; + NTSTATUS status = STATUS_SUCCESS; + + PAGED_CODE(); + + // + // Init the idle policy structure. + // + WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_INIT(&idleSettings, IDLE_CAPS_TYPE); + idleSettings.IdleTimeout = 10000; // 10-sec + + status = WdfDeviceAssignS0IdleSettings(Device, &idleSettings); + if ( !NT_SUCCESS(status)) { + UsbSamp_DbgPrint(3, ("WdfDeviceSetPowerPolicyS0IdlePolicy failed 0x%x\n", status)); + return status; + } + + // + // Init wait-wake policy structure. + // + WDF_DEVICE_POWER_POLICY_WAKE_SETTINGS_INIT(&wakeSettings); + + status = WdfDeviceAssignSxWakeSettings(Device, &wakeSettings); + if (!NT_SUCCESS(status)) { + UsbSamp_DbgPrint(3, ("WdfDeviceAssignSxWakeSettings failed 0x%x\n", status)); + return status; + } + + return status; +} + + +NTSTATUS +ReadAndSelectDescriptors( + _In_ WDFDEVICE Device + ) +/*++ + +Routine Description: + + This routine configures the USB device. + In this routines we get the device descriptor, + the configuration descriptor and select the + configuration. + +Arguments: + + Device - Handle to a framework device + +Return Value: + + NTSTATUS - NT status value. + +--*/ +{ + NTSTATUS status; + PDEVICE_CONTEXT pDeviceContext; + + PAGED_CODE(); + + // + // initialize variables + // + pDeviceContext = GetDeviceContext(Device); + + // + // Create a USB device handle so that we can communicate with the + // underlying USB stack. The WDFUSBDEVICE handle is used to query, + // configure, and manage all aspects of the USB device. + // These aspects include device properties, bus properties, + // and I/O creation and synchronization. We only create device the first + // the PrepareHardware is called. If the device is restarted by pnp manager + // for resource rebalance, we will use the same device handle but then select + // the interfaces again because the USB stack could reconfigure the device on + // restart. + // + if (pDeviceContext->WdfUsbTargetDevice == NULL) { + WDF_USB_DEVICE_CREATE_CONFIG config; + + WDF_USB_DEVICE_CREATE_CONFIG_INIT(&config, + USBD_CLIENT_CONTRACT_VERSION_602); + + status = WdfUsbTargetDeviceCreateWithParameters(Device, + &config, + WDF_NO_OBJECT_ATTRIBUTES, + &pDeviceContext->WdfUsbTargetDevice); + if (!NT_SUCCESS(status)) { + UsbSamp_DbgPrint(1, ("WdfUsbTargetDeviceCreateWithParameters failed with Status code %x\n", status)); + return status; + } + } + + WdfUsbTargetDeviceGetDeviceDescriptor(pDeviceContext->WdfUsbTargetDevice, + &pDeviceContext->UsbDeviceDescriptor); + + NT_ASSERT(pDeviceContext->UsbDeviceDescriptor.bNumConfigurations); + + status = ConfigureDevice(Device); + + return status; +} + +NTSTATUS +ConfigureDevice( + _In_ WDFDEVICE Device + ) +/*++ + +Routine Description: + + This helper routine reads the configuration descriptor + for the device in couple of steps. + +Arguments: + + Device - Handle to a framework device + +Return Value: + + NTSTATUS - NT status value + +--*/ +{ + USHORT size = 0; + NTSTATUS status; + PDEVICE_CONTEXT pDeviceContext; + PUSB_CONFIGURATION_DESCRIPTOR configurationDescriptor; + WDF_OBJECT_ATTRIBUTES attributes; + WDFMEMORY memory; + PUCHAR Offset = NULL; + + PAGED_CODE(); + + // + // initialize the variables + // + configurationDescriptor = NULL; + pDeviceContext = GetDeviceContext(Device); + + // + // Read the first configuration descriptor + // This requires two steps: + // 1. Ask the WDFUSBDEVICE how big it is + // 2. Allocate it and get it from the WDFUSBDEVICE + // + status = WdfUsbTargetDeviceRetrieveConfigDescriptor(pDeviceContext->WdfUsbTargetDevice, + NULL, + &size); + + if (status != STATUS_BUFFER_TOO_SMALL || size == 0) { + return status; + } + + // + // Create a memory object and specify usbdevice as the parent so that + // it will be freed automatically. + // + WDF_OBJECT_ATTRIBUTES_INIT(&attributes); + + attributes.ParentObject = pDeviceContext->WdfUsbTargetDevice; + + status = WdfMemoryCreate(&attributes, + NonPagedPool, + POOL_TAG, + size, + &memory, + &configurationDescriptor); + if (!NT_SUCCESS(status)) { + return status; + } + + status = WdfUsbTargetDeviceRetrieveConfigDescriptor(pDeviceContext->WdfUsbTargetDevice, + configurationDescriptor, + &size); + if (!NT_SUCCESS(status)) { + return status; + } + + // + // Check if the descriptors are valid + // + status = UsbSamp_ValidateConfigurationDescriptor(configurationDescriptor, size , &Offset); + + if (!NT_SUCCESS(status)) { + UsbSamp_DbgPrint(1, ("Descriptor validation failed with Status code %x and at the offset %p\n", status , Offset )); + return status; + } + + pDeviceContext->UsbConfigurationDescriptor = configurationDescriptor; + + status = SelectInterfaces(Device); + + return status; +} + +NTSTATUS +SelectInterfaces( + _In_ WDFDEVICE Device + ) +/*++ + +Routine Description: + + This helper routine selects the configuration, interface and + creates a context for every pipe (end point) in that interface. + +Arguments: + + Device - Handle to a framework device + +Return Value: + + NT status value + +--*/ +{ + WDF_USB_DEVICE_SELECT_CONFIG_PARAMS configParams; + NTSTATUS status; + PDEVICE_CONTEXT pDeviceContext; + UCHAR i; + WDF_OBJECT_ATTRIBUTES pipeAttributes; + WDF_USB_INTERFACE_SELECT_SETTING_PARAMS selectSettingParams; + UCHAR numberAlternateSettings = 0; + UCHAR numberConfiguredPipes; + + PAGED_CODE(); + + pDeviceContext = GetDeviceContext(Device); + + // + // The device has only one interface and the interface may have multiple + // alternate settings. It will try to use alternate setting zero if it has + // non-zero endpoints, otherwise it will try to search an alternate + // setting with non-zero endpoints. + // + + WDF_USB_DEVICE_SELECT_CONFIG_PARAMS_INIT_SINGLE_INTERFACE( &configParams); + + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&pipeAttributes, PIPE_CONTEXT); + +#if (NTDDI_VERSION >= NTDDI_WIN8) + pipeAttributes.EvtCleanupCallback = UsbSamp_EvtPipeContextCleanup; +#endif + + status = WdfUsbTargetDeviceSelectConfig(pDeviceContext->WdfUsbTargetDevice, + &pipeAttributes, + &configParams); + + + if (NT_SUCCESS(status) && + WdfUsbTargetDeviceGetNumInterfaces(pDeviceContext->WdfUsbTargetDevice) > 0) { + + status = RetrieveDeviceInformation(Device); + if (!NT_SUCCESS(status)) { + UsbSamp_DbgPrint(1, ("RetrieveDeviceInformation failed %x\n", status)); + return status; + } + + pDeviceContext->UsbInterface = + configParams.Types.SingleInterface.ConfiguredUsbInterface; + + // + // This is written to work with Intel 82930 board, OSRUSBFX2, FX2 MUTT and FX3 MUTT + // devices. The alternate setting zero of MUTT devices don't have any endpoints. So + // in the code below, we will walk through the list of alternate settings until we + // find one that has non-zero endpoints. + // + + numberAlternateSettings = WdfUsbInterfaceGetNumSettings(pDeviceContext->UsbInterface); + + NT_ASSERT(numberAlternateSettings > 0); + + numberConfiguredPipes = 0; + + for (i = 0; i < numberAlternateSettings && numberConfiguredPipes == 0; i++) { + + WDF_USB_INTERFACE_SELECT_SETTING_PARAMS_INIT_SETTING(&selectSettingParams, i); + + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&pipeAttributes, PIPE_CONTEXT); + +#if (NTDDI_VERSION >= NTDDI_WIN8) + pipeAttributes.EvtCleanupCallback = UsbSamp_EvtPipeContextCleanup; +#endif + + status = WdfUsbInterfaceSelectSetting(pDeviceContext->UsbInterface, + &pipeAttributes, + &selectSettingParams + ); + + if (NT_SUCCESS(status)) { + + numberConfiguredPipes = WdfUsbInterfaceGetNumConfiguredPipes(pDeviceContext->UsbInterface); + + if (numberConfiguredPipes > 0){ + + pDeviceContext->SelectedAlternateSetting = i; + + } + + } + + } + + pDeviceContext->NumberConfiguredPipes = numberConfiguredPipes; + + for (i = 0; i < pDeviceContext->NumberConfiguredPipes; i++) { + WDFUSBPIPE pipe; + + pipe = WdfUsbInterfaceGetConfiguredPipe(pDeviceContext->UsbInterface, + i, //PipeIndex, + NULL + ); +#if (NTDDI_VERSION >= NTDDI_WIN8) + if (pDeviceContext->IsDeviceSuperSpeed) { + status = InitializePipeContextForSuperSpeedDevice(pDeviceContext, + pipe); + } + else if (pDeviceContext->IsDeviceHighSpeed) { + status = InitializePipeContextForHighSpeedDevice(pipe); + } + else { + status = InitializePipeContextForFullSpeedDevice(pipe); + } +#else + if (pDeviceContext->IsDeviceHighSpeed) { + status = InitializePipeContextForHighSpeedDevice(pipe); + } + else { + status = InitializePipeContextForFullSpeedDevice(pipe); + } +#endif + if (!NT_SUCCESS(status)) { + UsbSamp_DbgPrint(1, ("InitializePipeContext failed %x\n", status)); + break; + } + } + + } + + return status; +} + + +NTSTATUS +AbortPipes( + _In_ WDFDEVICE Device + ) +/*++ + +Routine Description + + sends an abort pipe request on all open pipes. + +Arguments: + + Device - Handle to a framework device + +Return Value: + + NT status value + +--*/ +{ + UCHAR i; + ULONG count; + NTSTATUS status; + PDEVICE_CONTEXT pDevContext; + + PAGED_CODE(); + + // + // initialize variables + // + pDevContext = GetDeviceContext(Device); + + UsbSamp_DbgPrint(3, ("AbortPipes - begins\n")); + + count = pDevContext->NumberConfiguredPipes; + + for (i = 0; i < count; i++) { + WDFUSBPIPE pipe; + pipe = WdfUsbInterfaceGetConfiguredPipe(pDevContext->UsbInterface, + i, //PipeIndex, + NULL + ); + + UsbSamp_DbgPrint(3, ("Aborting open pipe %d\n", i)); + + status = WdfUsbTargetPipeAbortSynchronously(pipe, + WDF_NO_HANDLE, // WDFREQUEST + NULL);//PWDF_REQUEST_SEND_OPTIONS + + if (!NT_SUCCESS(status)) { + UsbSamp_DbgPrint(1, ("WdfUsbTargetPipeAbortSynchronously failed %x\n", status)); + break; + } + } + + UsbSamp_DbgPrint(3, ("AbortPipes - ends\n")); + + return STATUS_SUCCESS; +} + + +#if (NTDDI_VERSION >= NTDDI_WIN8) +NTSTATUS +InitializePipeContextForSuperSpeedDevice( + _In_ PDEVICE_CONTEXT DeviceContext, + _In_ WDFUSBPIPE Pipe + ) +/*++ + +Routine Description + + This function initialize pipe context for super speed isoch and + bulk endpoints. + +Return Value: + + NT status value + +--*/ +{ + WDF_USB_PIPE_INFORMATION pipeInfo; + NTSTATUS status = STATUS_SUCCESS; + + PAGED_CODE(); + + WDF_USB_PIPE_INFORMATION_INIT(&pipeInfo); + WdfUsbTargetPipeGetInformation(Pipe, &pipeInfo); + + // + // We only use pipe context for super speed isoch and bulk speed bulk endpoints. + // + if ((WdfUsbPipeTypeIsochronous == pipeInfo.PipeType)) { + + status = InitializePipeContextForSuperSpeedIsochPipe(DeviceContext, + WdfUsbInterfaceGetInterfaceNumber(DeviceContext->UsbInterface), + Pipe); + + } else if (WdfUsbPipeTypeBulk == pipeInfo.PipeType) { + + status = InitializePipeContextForSuperSpeedBulkPipe(DeviceContext, + WdfUsbInterfaceGetInterfaceNumber(DeviceContext->UsbInterface), + Pipe); + + } + + return status; + +} + + +PUSB_ENDPOINT_DESCRIPTOR +GetEndpointDescriptorForEndpointAddress( + _In_ PDEVICE_CONTEXT DeviceContext, + _In_ UCHAR InterfaceNumber, + _In_ UCHAR EndpointAddress, + _Out_ PUSB_SUPERSPEED_ENDPOINT_COMPANION_DESCRIPTOR *ppEndpointCompanionDescriptor + ) +/*++ + +Routine Description: + + The helper routine gets the Endpoint Descriptor matched with EndpointAddress and return + its Endpoint Companion Descriptor if it has. + + UsbSamp_ValidateConfigurationDescriptor already validates that descriptors lie within + allocated buffer. + +Arguments: + + DeviceContext - pointer to the device context which includes configuration descriptor + + InterfaceNumber - InterfaceNumber of selected interface + + EndpointAddress - EndpointAddress of the Pipe + + ppEndpointCompanionDescriptor - pointer to the Endpoint Companioin Descroptor pointer + +Return Value: + + Pointer to Endpoint Descriptor + +--*/ +{ + + PUSB_COMMON_DESCRIPTOR pCommonDescriptorHeader = NULL; + PUSB_CONFIGURATION_DESCRIPTOR pConfigurationDescriptor = NULL; + PUSB_INTERFACE_DESCRIPTOR pInterfaceDescriptor = NULL; + PUSB_ENDPOINT_DESCRIPTOR pEndpointDescriptor = NULL; + PUCHAR startingPosition; + ULONG index; + BOOLEAN found = FALSE; + + PAGED_CODE(); + + pConfigurationDescriptor = DeviceContext->UsbConfigurationDescriptor; + + *ppEndpointCompanionDescriptor = NULL; + + // + // Parse the ConfigurationDescriptor (including all Interface and + // Endpoint Descriptors) and locate a Interface Descriptor which + // matches the InterfaceNumber, AlternateSetting, InterfaceClass, + // InterfaceSubClass, and InterfaceProtocol parameters. + // + pInterfaceDescriptor = USBD_ParseConfigurationDescriptorEx( + pConfigurationDescriptor, + pConfigurationDescriptor, + InterfaceNumber, + DeviceContext->SelectedAlternateSetting, + -1, // InterfaceClass, don't care + -1, // InterfaceSubClass, don't care + -1 // InterfaceProtocol, don't care + ); + + if (pInterfaceDescriptor == NULL ) { + + UsbSamp_DbgPrint(1, ("USBD_ParseConfigurationDescriptorEx failed to retrieve Interface Descriptor.\n")); + goto End; + + } + + startingPosition = (PUCHAR) pInterfaceDescriptor; + + for(index = 0; index < pInterfaceDescriptor->bNumEndpoints; index++) { + + pCommonDescriptorHeader = USBD_ParseDescriptors(pConfigurationDescriptor, + pConfigurationDescriptor->wTotalLength, + startingPosition, + USB_ENDPOINT_DESCRIPTOR_TYPE); + + if (pCommonDescriptorHeader == NULL) { + + UsbSamp_DbgPrint(1, ("USBD_ParseDescriptors failed to retrieve SuperSpeed Endpoint Descriptor unexpectedly\n")); + goto End; + + } + + // + // UsbSamp_ValidateConfigurationDescriptor validates all descriptors. + // This means that the descriptor pointed to by pCommonDescriptorHeader( received above ) is completely + // contained within the buffer representing ConfigurationDescriptor and + // it also verifies that pCommonDescriptorHeader->bLength is equal to sizeof(USB_ENDPOINT_DESCRIPTOR). + // + + pEndpointDescriptor = (PUSB_ENDPOINT_DESCRIPTOR) pCommonDescriptorHeader; + + // + // Search an Endpoint Descriptor that matches the EndpointAddress + // + if (pEndpointDescriptor->bEndpointAddress == EndpointAddress){ + + found = TRUE; + + break; + + } + + // + // Skip the current Endpoint Descriptor and search for the next. + // + startingPosition = (PUCHAR)pCommonDescriptorHeader + pCommonDescriptorHeader->bLength; + + } + + if (found) { + // + // Locate the SuperSpeed Endpoint Companion Descriptor associated with the endpoint descriptor + // + pCommonDescriptorHeader = USBD_ParseDescriptors(pConfigurationDescriptor, + pConfigurationDescriptor->wTotalLength, + pEndpointDescriptor, + USB_SUPERSPEED_ENDPOINT_COMPANION_DESCRIPTOR_TYPE); + + if (pCommonDescriptorHeader != NULL) { + + // + // UsbSamp_ValidateConfigurationDescriptor validates all descriptors. + // This means that the descriptor pointed to by pCommonDescriptorHeader( received above ) is completely + // contained within the buffer representing ConfigurationDescriptor and + // it also verifies that pCommonDescriptorHeader->bLength is >= sizeof(USB_SUPERSPEED_ENDPOINT_COMPANION_DESCRIPTOR) + // + + *ppEndpointCompanionDescriptor = + (PUSB_SUPERSPEED_ENDPOINT_COMPANION_DESCRIPTOR) pCommonDescriptorHeader; + + } else { + + UsbSamp_DbgPrint(3, ("USBD_ParseDescriptors failed to retrieve SuperSpeed Endpoint Companion Descriptor unexpectedly\n")); + + } + + } + + +End: + return pEndpointDescriptor; +} + + +NTSTATUS +InitializePipeContextForSuperSpeedIsochPipe( + _In_ PDEVICE_CONTEXT DeviceContext, + _In_ UCHAR InterfaceNumber, + _In_ WDFUSBPIPE Pipe + ) +/*++ + +Routine Description + + This function validates all the isoch related fields in the endpoint descriptor + to make sure it's in comformance with the spec and Microsoft core stack + implementation and intializes the pipe context. + + The TransferSizePerMicroframe and TransferSizePerFrame values will be + used in the I/O path to do read and write transfers. + +Return Value: + + NT status value + +-*/ +{ + WDF_USB_PIPE_INFORMATION pipeInfo; + PPIPE_CONTEXT pipeContext; + UCHAR endpointAddress; + PUSB_ENDPOINT_DESCRIPTOR pEndpointDescriptor; + PUSB_SUPERSPEED_ENDPOINT_COMPANION_DESCRIPTOR pEndpointCompanionDescriptor; + USHORT wMaxPacketSize; + UCHAR bMaxBurst; + UCHAR bMult; + USHORT wBytesPerInterval; + + + PAGED_CODE(); + + WDF_USB_PIPE_INFORMATION_INIT(&pipeInfo); + WdfUsbTargetPipeGetInformation(Pipe, &pipeInfo); + + // + // We use the pipe context only for isoch endpoints. + // + if ((WdfUsbPipeTypeIsochronous != pipeInfo.PipeType)) { + + return STATUS_SUCCESS; + + } + + pipeContext = GetPipeContext(Pipe); + + endpointAddress = pipeInfo.EndpointAddress; + + pEndpointDescriptor = GetEndpointDescriptorForEndpointAddress( + DeviceContext, + InterfaceNumber, + endpointAddress, + &pEndpointCompanionDescriptor); + + if (pEndpointDescriptor == NULL || pEndpointCompanionDescriptor == NULL ){ + + UsbSamp_DbgPrint(1, ("pEndpointDescriptor or pEndpointCompanionDescriptor is invalid (NULL)\n")); + return STATUS_INVALID_PARAMETER; + + } + + // + // For SuperSpeed isoch endpoint, it uses wBytesPerInterval from its + // endpoint companion descriptor. If bMaxBurst field in its endpoint + // companion descriptor is greater than zero, wMaxPacketSize must be + // 1024. If the value in the bMaxBurst field is set to zero then + // wMaxPacketSize can have any value from 0 to 1024. + // + wBytesPerInterval = pEndpointCompanionDescriptor->wBytesPerInterval; + wMaxPacketSize = pEndpointDescriptor->wMaxPacketSize; + bMaxBurst = pEndpointCompanionDescriptor->bMaxBurst; + bMult = pEndpointCompanionDescriptor->bmAttributes.Isochronous.Mult; + + if (wBytesPerInterval > (wMaxPacketSize * (bMaxBurst + 1) * (bMult + 1))){ + + UsbSamp_DbgPrint(1, ("SuperSpeed isochronouse endpoints's wBytesPerInterval value (%d) is greater than wMaxPacketSize * (bMaxBurst+1) * (Mult +1) (%d) \n", + wBytesPerInterval, (wMaxPacketSize * (bMaxBurst + 1) * (bMult + 1)))); + return STATUS_INVALID_PARAMETER; + + } + + if (bMaxBurst > 0){ + + if (wMaxPacketSize != USB_ENDPOINT_SUPERSPEED_ISO_MAX_PACKET_SIZE){ + + UsbSamp_DbgPrint(1, ("SuperSpeed isochronouse endpoints must have wMaxPacketSize value of %d bytes when bMaxpBurst is %d \n", + USB_ENDPOINT_SUPERSPEED_ISO_MAX_PACKET_SIZE, bMaxBurst)); + return STATUS_INVALID_PARAMETER; + + } + + } else { + + if (wMaxPacketSize > USB_ENDPOINT_SUPERSPEED_ISO_MAX_PACKET_SIZE){ + + UsbSamp_DbgPrint(1, ("SuperSpeed isochronouse endpoints must have wMaxPacketSize value no more than %d bytes when bMaxpBurst is %d \n", + USB_ENDPOINT_SUPERSPEED_ISO_MAX_PACKET_SIZE, bMaxBurst)); + return STATUS_INVALID_PARAMETER; + + } + + } + + + // + // This sample demos how to use wBytesPerInterval from its Endpoint + // Companion Descriptor. Actaully, for Superspeed isochronous endpoints, + // MaximumPacketSize in WDF_USB_PIPE_INFORMATION and USBD_PIPE_INFORMATION + // is returned with the value of wBytesPerInterval in the endpoint + // companion descriptor. This is different than the true MaxPacketSize of + // the endpoint descriptor. + // + NT_ASSERT(pipeInfo.MaximumPacketSize == wBytesPerInterval); + pipeContext->TransferSizePerMicroframe = wBytesPerInterval; + + // + // Microsoft USB 3.0 stack only supports bInterval value of 1, 2, 3 and 4 + // (or polling period of 1, 2, 4 and 8). + // For super-speed isochronous endpoints, the bInterval value is used as + // the exponent for a 2^(bInterval-1) value expressed in microframes; + // e.g., a bInterval of 4 means a period of 8 (2^(4-1)) microframes. + // + if (pipeInfo.Interval == 0 || pipeInfo.Interval > 4) { + + UsbSamp_DbgPrint(1, ("bInterval value in pipeInfo is invalid (0 or > 4)\n")); + return STATUS_INVALID_PARAMETER; + + } + + switch (pipeInfo.Interval) { + case 1: + // + // Transfer period is every microframe (8 times a frame). + // + pipeContext->TransferSizePerFrame = pipeContext->TransferSizePerMicroframe * 8; + break; + + case 2: + // + // Transfer period is every 2 microframes (4 times a frame). + // + pipeContext->TransferSizePerFrame = pipeContext->TransferSizePerMicroframe * 4; + break; + + case 3: + // + // Transfer period is every 4 microframes (2 times a frame). + // + pipeContext->TransferSizePerFrame = pipeContext->TransferSizePerMicroframe * 2; + break; + + case 4: + // + // Transfer period is every 8 microframes (1 times a frame). + // + pipeContext->TransferSizePerFrame = pipeContext->TransferSizePerMicroframe; + break; + } + + UsbSamp_DbgPrint(1, ("MaxPacketSize = %d, bInterval = %d\n", + pipeInfo.MaximumPacketSize, + pipeInfo.Interval)); + + UsbSamp_DbgPrint(1, ("TransferSizePerFrame = %d, TransferSizePerMicroframe = %d\n", + pipeContext->TransferSizePerFrame, + pipeContext->TransferSizePerMicroframe)); + + return STATUS_SUCCESS; +} + +#endif + +NTSTATUS +InitializePipeContextForHighSpeedDevice( + _In_ WDFUSBPIPE Pipe + ) +/*++ + +Routine Description + + This function validates all the isoch related fields in the endpoint descriptor + to make sure it's in comformance with the spec and Microsoft core stack + implementation and intializes the pipe context. + + The TransferSizePerMicroframe and TransferSizePerFrame values will be + used in the I/O path to do read and write transfers. + +Return Value: + + NT status value + +--*/ +{ + WDF_USB_PIPE_INFORMATION pipeInfo; + PPIPE_CONTEXT pipeContext; + + PAGED_CODE(); + + WDF_USB_PIPE_INFORMATION_INIT(&pipeInfo); + WdfUsbTargetPipeGetInformation(Pipe, &pipeInfo); + + // + // We use the pipe context only for isoch endpoints. + // + if ((WdfUsbPipeTypeIsochronous != pipeInfo.PipeType)) { + return STATUS_SUCCESS; + } + + pipeContext = GetPipeContext(Pipe); + + if (pipeInfo.MaximumPacketSize == 0) { + UsbSamp_DbgPrint(1, ("MaximumPacketSize in the pipeInfo is invalid (zero)\n")); + return STATUS_INVALID_PARAMETER; + } + + // + // Universal Serial Bus Specification Revision 2.0 5.6.3 Isochronous Transfer + // Packet Size Constraints: High-speed endpoints are allowed up to 1024-byte data + // payloads per microframe and allowed up to a maximum of 3 transactions per microframe. + // + // For highspeed isoch endpoints, bits 12-11 of wMaxPacketSize in the endpoint descriptor + // specify the number of additional transactions oppurtunities per microframe. + // 00 - None (1 transaction per microframe) + // 01 - 1 additional (2 per microframe) + // 10 - 2 additional (3 per microframe) + // 11 - Reserved. + // + // Note: MaximumPacketSize of WDF_USB_PIPE_INFORMATION is already adjusted to include + // additional transactions if it is a high bandwidth pipe. + // + + if (pipeInfo.MaximumPacketSize > 1024 * 3) { + UsbSamp_DbgPrint(1, ("MaximumPacketSize in the endpoint descriptor is invalid (>1024*3)\n")); + return STATUS_INVALID_PARAMETER; + } + + // + // Microsoft USB stack only supports bInterval value of 1, 2, 3 and 4 (or polling period of 1, 2, 4 and 8). + // + if (pipeInfo.Interval == 0 || pipeInfo.Interval > 4) { + UsbSamp_DbgPrint(1, ("bInterval value in pipeInfo is invalid (0 or > 4)\n")); + return STATUS_INVALID_PARAMETER; + } + + pipeContext->TransferSizePerMicroframe = pipeInfo.MaximumPacketSize; + + // + // For high-speed isochronous endpoints, the bInterval value is used + // as the exponent for a 2^(bInterval-1) value expressed in + // microframes; e.g., a bInterval of 4 means a period of 8 (2^(4-1)) + // microframes. The bInterval value must be from 1 to 16. NOTE: The + // USBPORT.SYS driver only supports high-speed isochronous bInterval + // values of {1, 2, 3, 4}. + // + switch (pipeInfo.Interval) { + case 1: + // + // Transfer period is every microframe (8 times a frame). + // + pipeContext->TransferSizePerFrame = pipeContext->TransferSizePerMicroframe * 8; + break; + + case 2: + // + // Transfer period is every 2 microframes (4 times a frame). + // + pipeContext->TransferSizePerFrame = pipeContext->TransferSizePerMicroframe * 4; + break; + + case 3: + // + // Transfer period is every 4 microframes (2 times a frame). + // + pipeContext->TransferSizePerFrame = pipeContext->TransferSizePerMicroframe * 2; + break; + + case 4: + // + // Transfer period is every 8 microframes (1 times a frame). + // + pipeContext->TransferSizePerFrame = pipeContext->TransferSizePerMicroframe; + break; + } + + UsbSamp_DbgPrint(1, ("MaxPacketSize = %d, bInterval = %d\n", + pipeInfo.MaximumPacketSize, + pipeInfo.Interval)); + + UsbSamp_DbgPrint(1, ("TransferSizePerFrame = %d, TransferSizePerMicroframe = %d\n", + pipeContext->TransferSizePerFrame, + pipeContext->TransferSizePerMicroframe)); + + return STATUS_SUCCESS; +} + +NTSTATUS +InitializePipeContextForFullSpeedDevice( + _In_ WDFUSBPIPE Pipe + ) +/*++ + +Routine Description + + This function validates all the isoch related fields in the endpoint descriptor + to make sure it's in comformance with the spec and Microsoft core stack + implementation and intializes the pipe context. + + The TransferSizePerMicroframe and TransferSizePerFrame values will be + used in the I/O path to do read and write transfers. + +Return Value: + + NT status value + +--*/ +{ + WDF_USB_PIPE_INFORMATION pipeInfo; + PPIPE_CONTEXT pipeContext; + + PAGED_CODE(); + + WDF_USB_PIPE_INFORMATION_INIT(&pipeInfo); + WdfUsbTargetPipeGetInformation(Pipe, &pipeInfo); + + // + // We use the pipe context only for isoch endpoints. + // + if ((WdfUsbPipeTypeIsochronous != pipeInfo.PipeType)) { + return STATUS_SUCCESS; + } + + pipeContext = GetPipeContext(Pipe); + + if (pipeInfo.MaximumPacketSize == 0) { + UsbSamp_DbgPrint(1, ("MaximumPacketSize in the endpoint descriptor is invalid\n")); + return STATUS_INVALID_PARAMETER; + } + + // + // Universal Serial Bus Specification Revision 2.0 + // 5.6.3 Isochronous Transfer Packet Size Constraints + // + // The USB limits the maximum data payload size to 1,023 bytes + // for each full-speed isochronous endpoint. + // + if (pipeInfo.MaximumPacketSize > 1023) { + UsbSamp_DbgPrint(1, ("MaximumPacketSize in the endpoint descriptor is invalid\n")); + return STATUS_INVALID_PARAMETER; + } + + // + // Microsoft USB stack only supports bInterval value of 1 for + // full-speed isochronous endpoints. + // + if (pipeInfo.Interval != 1) { + UsbSamp_DbgPrint(1, ("bInterval value in endpoint descriptor is invalid\n")); + return STATUS_INVALID_PARAMETER; + } + + pipeContext->TransferSizePerFrame = pipeInfo.MaximumPacketSize; + pipeContext->TransferSizePerMicroframe = 0; + + UsbSamp_DbgPrint(1, ("TransferSizePerFrame = %d\n", pipeContext->TransferSizePerFrame)); + + return STATUS_SUCCESS; +} + +NTSTATUS +RetrieveDeviceInformation( + _In_ WDFDEVICE Device + ) +{ + PDEVICE_CONTEXT pDeviceContext; + WDF_USB_DEVICE_INFORMATION info; + NTSTATUS status; + USHORT numberOfStreams = 0; + PAGED_CODE(); + + pDeviceContext = GetDeviceContext(Device); + + WDF_USB_DEVICE_INFORMATION_INIT(&info); + + // + // Retrieve USBD version information, port driver capabilites and device + // capabilites such as speed, power, etc. + // + status = WdfUsbTargetDeviceRetrieveInformation(pDeviceContext->WdfUsbTargetDevice, + &info); + if (!NT_SUCCESS(status)) { + return status; + } + + pDeviceContext->IsDeviceHighSpeed = + (info.Traits & WDF_USB_DEVICE_TRAIT_AT_HIGH_SPEED) ? TRUE : FALSE; + + UsbSamp_DbgPrint(3, ("DeviceIsHighSpeed: %s\n", + pDeviceContext->IsDeviceHighSpeed ? "TRUE" : "FALSE")); + + UsbSamp_DbgPrint(3, ("IsDeviceSelfPowered: %s\n", + (info.Traits & WDF_USB_DEVICE_TRAIT_SELF_POWERED) ? "TRUE" : "FALSE")); + + pDeviceContext->WaitWakeEnable = + info.Traits & WDF_USB_DEVICE_TRAIT_REMOTE_WAKE_CAPABLE; + + UsbSamp_DbgPrint(3, ("IsDeviceRemoteWakeable: %s\n", + (info.Traits & WDF_USB_DEVICE_TRAIT_REMOTE_WAKE_CAPABLE) ? "TRUE" : "FALSE")); + + status = GetStackCapability(Device, + &GUID_USB_CAPABILITY_DEVICE_CONNECTION_SUPER_SPEED_COMPATIBLE, + 0, + NULL); + if (NT_SUCCESS(status)) { + pDeviceContext->IsDeviceSuperSpeed = TRUE; + } + + UsbSamp_DbgPrint(3, ("DeviceIsSuperSpeed: %s\n", + pDeviceContext->IsDeviceSuperSpeed ? "TRUE" : "FALSE")); + + if (pDeviceContext->IsDeviceSuperSpeed == TRUE) { + status = GetStackCapability(Device, + &GUID_USB_CAPABILITY_STATIC_STREAMS, + sizeof(numberOfStreams), + (PUCHAR)&numberOfStreams); + if (NT_SUCCESS(status)) { + pDeviceContext->IsStaticStreamsSupported = TRUE; + pDeviceContext->NumberOfStreamsSupportedByController = numberOfStreams; + } + + if (pDeviceContext->IsStaticStreamsSupported) { + UsbSamp_DbgPrint(3, ("Number of Streams supported by the controller: %d\n", numberOfStreams)); + } + } + + return STATUS_SUCCESS; +} + + +NTSTATUS +ReadFdoRegistryKeyValue( + _In_ WDFDRIVER Driver, + _In_ LPWSTR Name, + _Out_ PULONG Value + ) +/*++ + +Routine Description: + + Can be used to read any REG_DWORD registry value stored + under Device Parameter. + +Arguments: + + Driver - pointer to the device object + Name - Name of the registry value + Value - + + +Return Value: + + NTSTATUS + +--*/ +{ + WDFKEY hKey = NULL; + NTSTATUS status; + UNICODE_STRING valueName; + + UNREFERENCED_PARAMETER(Driver); + + PAGED_CODE(); + + *Value = 0; + + status = WdfDriverOpenParametersRegistryKey(WdfGetDriver(), + KEY_READ, + WDF_NO_OBJECT_ATTRIBUTES, + &hKey); + + if (NT_SUCCESS (status)) { + + RtlInitUnicodeString(&valueName,Name); + + status = WdfRegistryQueryULong (hKey, + &valueName, + Value); + + WdfRegistryClose(hKey); + } + + return status; +} + +VOID +UsbSamp_EvtDeviceContextCleanup( + IN WDFOBJECT WdfDevice + ) +/*++ + +Routine Description: + + In this callback, it cleans up device context. + +Arguments: + + WdfDevice - WDF device object + +Return Value: + + NULL + +--*/ +{ + WDFDEVICE device; + PDEVICE_CONTEXT pDevContext; + + PAGED_CODE(); + + device = (WDFDEVICE)WdfDevice; + + pDevContext = GetDeviceContext(device); + + if (pDevContext->UsbdHandle != NULL) { + USBD_CloseHandle(pDevContext->UsbdHandle); + } + +} + +USBD_STATUS +UsbSamp_ValidateConfigurationDescriptor( + _In_reads_bytes_(BufferLength) PUSB_CONFIGURATION_DESCRIPTOR ConfigDesc, + _In_ ULONG BufferLength, + _Inout_ PUCHAR *Offset + ) +/*++ + +Routine Description: + + Validates a USB Configuration Descriptor + +Parameters: + + ConfigDesc: Pointer to the entire USB Configuration descriptor returned by + the device + + BufferLength: Known size of buffer pointed to by ConfigDesc (Not wTotalLength) + + Offset: if the USBD_STATUS returned is not USBD_STATUS_SUCCESS, offet will + be set to the address within the ConfigDesc buffer where the failure + occured. + +Return Value: + + USBD_STATUS + Success implies the configuration descriptor is valid. + +--*/ +{ + USBD_STATUS status = USBD_STATUS_SUCCESS; + PUCHAR offset, end; + PUCHAR index = NULL; + USHORT ValidationLevel = 3; + + PAGED_CODE(); + + // + // Call USBD_ValidateConfigurationDescriptor to validate the descriptors which are present in this supplied configuration descriptor. + // USBD_ValidateConfigurationDescriptor validates that all descriptors are completely contained within the configuration descriptor buffer. + // It also checks for interface numbers, number of endpoints in an interface etc. + // Please refer to msdn documentation for this function for more information. + // + + status = USBD_ValidateConfigurationDescriptor( ConfigDesc, BufferLength , ValidationLevel , Offset , POOL_TAG ); + if (!(NT_SUCCESS (status)) ){ + return status; + } + + // + // TODO: You should validate the correctness of other descriptors which are not taken care by USBD_ValidateConfigurationDescriptor + // The below code loops through the configuration descriptor and verifies that all instances of + // USB_SUPERSPEED_ENDPOINT_COMPANION_DESCRIPTOR have a size of >= USB_SUPERSPEED_ENDPOINT_COMPANION_DESCRIPTOR + // You should put in more validations as per requirement + // + + + offset = ((PUCHAR)ConfigDesc) + ConfigDesc->bLength; + end = ((PUCHAR)ConfigDesc) + ConfigDesc->wTotalLength; + + while(offset < end){ + PUSB_COMMON_DESCRIPTOR commonDesc; + // + // Ensure the descriptor header is in bounds. We always + // need to ensure we have enough data to look at the descriptor + // header fields. Sometimes descriptors are zeroed at the end, + // this is OK. + // + commonDesc = (PUSB_COMMON_DESCRIPTOR)offset; + + + switch(commonDesc->bDescriptorType){ + + case USB_SUPERSPEED_ENDPOINT_COMPANION_DESCRIPTOR_TYPE: + + // + // basic validation. + // + if(commonDesc->bLength < sizeof(USB_SUPERSPEED_ENDPOINT_COMPANION_DESCRIPTOR)) { + status = USBD_STATUS_BAD_DESCRIPTOR; + index = (PUCHAR)commonDesc; + goto ValidateConfigurationDescriptor_Done; + } + break; + + // + // TODO: Add validation for any other descriptor your device will return. + // + default: + break; + } + offset += commonDesc->bLength; + + } + +ValidateConfigurationDescriptor_Done: + + if(!USBD_SUCCESS(status)){ + NT_ASSERT(index); + *Offset = index; + } + else{ + *Offset = NULL; + } + return status; +} + +#if (NTDDI_VERSION >= NTDDI_WIN8) +VOID +UsbSamp_EvtPipeContextCleanup( + IN WDFOBJECT WdfObject + ) +/*++ + +Routine Description: + + In this callback, it cleans up pipe context. + +Arguments: + + WdfObject - WDFUSBPIPE object + +Return Value: + + NULL + +--*/ +{ + WDFUSBPIPE pipe; + PPIPE_CONTEXT pPipeContext = NULL; + PUSBSAMP_STREAM_INFO pStreamInfo = NULL; + + PAGED_CODE(); + + pipe = (WDFUSBPIPE)WdfObject; + pPipeContext = GetPipeContext(pipe); + + pPipeContext->StreamConfigured = FALSE; + pStreamInfo = &pPipeContext->StreamInfo; + pStreamInfo->NumberOfStreams = 0; + if(pStreamInfo->StreamList != NULL){ + ExFreePool(pStreamInfo->StreamList); + pStreamInfo->StreamList = NULL; + } + +} +#endif diff --git a/usb/usbsamp/sys/driver.c b/usb/usbsamp/sys/driver.c new file mode 100644 index 000000000..26d614772 --- /dev/null +++ b/usb/usbsamp/sys/driver.c @@ -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: + + Driver.c + +Abstract: + + Bulk USB device driver for Intel 82930 USB test board. + Main module. + +Environment: + + Kernel mode only + +--*/ + +#include "private.h" + +// +// Globals +// + +ULONG DebugLevel = 1; + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT, DriverEntry) +#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. + +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; + + UsbSamp_DbgPrint(3, ("UsbSamp Driver Sample - Driver Framework Edition.\n")); + UsbSamp_DbgPrint(3, ("Built %s %s\n", __DATE__, __TIME__)); + + // + // 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, + UsbSamp_EvtDeviceAdd + ); + + // + // 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 // hDriver + ); + + if (!NT_SUCCESS(status)) { + UsbSamp_DbgPrint(1, ("WdfDriverCreate failed with status 0x%x\n", status)); + } + + return status; +} + + + diff --git a/usb/usbsamp/sys/driver/usbsamp.inx b/usb/usbsamp/sys/driver/usbsamp.inx new file mode 100644 index 000000000..88db6c198 --- /dev/null +++ b/usb/usbsamp/sys/driver/usbsamp.inx @@ -0,0 +1,115 @@ +;/*++ +; +;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: +; +; USBSamp.INF +; +;Abstract: +; Installation inf for the Intel 82930 USB Test Board, OSR USB-FX device, +; FX2 MUTT and FX3 MUTT device +; +;--*/ + +[Version] +Signature="$WINDOWS NT$" +Class=Sample +ClassGuid={78A1C341-4539-11d3-B88D-00C04FAD5171} +Provider=%ProviderName% +DriverVer=03/20/2003,5.00.3788 +CatalogFile=KmdfSamples.cat + + +; ================= Class section ===================== + +[ClassInstall32] +Addreg=SampleClassReg + +[SampleClassReg] +HKR,,,0,%ClassName% +HKR,,Icon,,-5 + +[DestinationDirs] +DefaultDestDir = 12 + +; ================= Device section ===================== + +[Manufacturer] +%MfgName%=IntelOSR,NT$ARCH$ + +[IntelOSR.NT$ARCH$] +%USB\VID_0547&PID_1002.DeviceDesc%=usbsamp.Dev, USB\VID_0547&PID_1002 ; OSR USB-FX +%USB\VID_045E&PID_930A.DeviceDesc%=usbsamp.Dev, USB\VID_045E&PID_930A ; Intel 82930 +%USB\VID_045E&PID_078E.DeviceDesc%=usbsamp.Dev, USB\VID_045E&PID_078E ; FX2 MUTT +%USB\VID_045E&PID_078F.DeviceDesc%=usbsamp.Dev, USB\VID_045E&PID_078F ; FX3 MUTT + + +[usbsamp.Dev.NT] +CopyFiles=usbsamp.Files.Ext + +[usbsamp.Dev.NT.Services] +Addservice = usbsamp, 0x00000002, usbsamp.AddService + +[usbsamp.AddService] +DisplayName = %usbsamp.SvcDesc% +ServiceType = 1 ; SERVICE_KERNEL_DRIVER +StartType = 3 ; SERVICE_DEMAND_START +ErrorControl = 1 ; SERVICE_ERROR_NORMAL +ServiceBinary = %10%\System32\Drivers\usbsamp.sys +AddReg = usbsamp.AddReg + +[usbsamp.AddReg] +HKR,"Parameters","MaximumTransferSize",0x10001,65536 +HKR,"Parameters","DebugLevel",0x10001,2 + +[usbsamp.Files.Ext] +usbsamp.sys + +[SourceDisksNames] +1=%Disk_Description%,,, + +[SourceDisksFiles] +usbsamp.sys = 1 + +;-------------- WDF Coinstaller installation +[DestinationDirs] +CoInstaller_CopyFiles = 11 + +[usbsamp.Dev.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" + +[usbsamp.Dev.NT.Wdf] +KmdfService = usbsamp, usbsamp_wdfsect + +[usbsamp_wdfsect] +KmdfLibraryVersion = $KMDFVERSION$ + +;---------------------------------------------------------------; + +[Strings] +ProviderName = "TODO-Set-Provider" +MfgName = "Intel/OSR" +Disk_Description= "usbsamp Installation Disk" +usbsamp.SvcDesc = "WDF Sample Driver for Intel 82930 USB Test Board" +ClassName = "Sample Device" +USB\VID_0547&PID_1002.DeviceDesc="WDF Sample for OSR USB-FX2 Learning Kit" +USB\VID_045E&PID_930A.DeviceDesc="WDF Sample for Intel 82930 USB Test Board" +USB\VID_045E&PID_078E.DeviceDesc="WDF Sample for FX2 MUTT device" +USB\VID_045E&PID_078F.DeviceDesc="WDF Sample for FX3 MUTT device" + diff --git a/usb/usbsamp/sys/driver/usbsamp.vcxproj b/usb/usbsamp/sys/driver/usbsamp.vcxproj new file mode 100644 index 000000000..0d96425ad --- /dev/null +++ b/usb/usbsamp/sys/driver/usbsamp.vcxproj @@ -0,0 +1,203 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {6395CFC7-A0E1-40D8-A744-D1DC7B6513BA} + $(MSBuildProjectName) + 1 + Debug + Win32 + {D2F4B3C2-A1FA-433C-A698-DEF22D5B8A75} + + + + 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)\usbsamp.inf + + + + usbsamp + + + usbsamp + + + usbsamp + + + usbsamp + + + + true + Level4 + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories);.. + + + + + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories);.. + + + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories);.. + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\usbdex.lib;$(DDK_LIB_PATH)\usbd.lib;$(DDK_LIB_PATH)\ntstrsafe.lib + + + + + true + Level4 + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories);.. + + + + + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories);.. + + + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories);.. + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\usbdex.lib;$(DDK_LIB_PATH)\usbd.lib;$(DDK_LIB_PATH)\ntstrsafe.lib + + + + + true + Level4 + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories);.. + + + + + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories);.. + + + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories);.. + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\usbdex.lib;$(DDK_LIB_PATH)\usbd.lib;$(DDK_LIB_PATH)\ntstrsafe.lib + + + + + true + Level4 + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories);.. + + + + + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories);.. + + + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories);.. + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\usbdex.lib;$(DDK_LIB_PATH)\usbd.lib;$(DDK_LIB_PATH)\ntstrsafe.lib + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/usb/usbsamp/sys/driver/usbsamp.vcxproj.Filters b/usb/usbsamp/sys/driver/usbsamp.vcxproj.Filters new file mode 100644 index 000000000..18f603d01 --- /dev/null +++ b/usb/usbsamp/sys/driver/usbsamp.vcxproj.Filters @@ -0,0 +1,51 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {D6BF3D48-CBBA-42B5-B745-30ECADE8EC41} + + + h;hpp;hxx;hm;inl;inc;xsd + {DB8B8A2E-77FA-4567-8AA8-B36DE48FDC76} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {122D039B-46C3-41E1-AC33-2B0AFE33A066} + + + inf;inv;inx;mof;mc; + {EA2F6E70-5FFD-4177-A858-FCEA368FFBB1} + + + + + 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/usb/usbsamp/sys/isorwr.c b/usb/usbsamp/sys/isorwr.c new file mode 100644 index 000000000..ad780c38f --- /dev/null +++ b/usb/usbsamp/sys/isorwr.c @@ -0,0 +1,565 @@ +/*++ + +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: + + isorwr.c + +Abstract: + + This file has dispatch routines for read and write. + +Environment: + + Kernel mode + +Notes: + +--*/ + +#include "private.h" + +VOID +ReadWriteIsochEndPoints( + _In_ WDFQUEUE Queue, + _In_ WDFREQUEST Request, + _In_ ULONG Length, + _In_ WDF_REQUEST_TYPE RequestType + ) +/*++ + +Routine Description: + + This routine does some validation and invokes appropriate function to perform Isoch transfer + +--*/ +{ + NTSTATUS status; + WDF_USB_PIPE_INFORMATION pipeInfo; + PFILE_CONTEXT fileContext; + WDFUSBPIPE pipe; + PDEVICE_CONTEXT deviceContext; + PREQUEST_CONTEXT rwContext; + + UNREFERENCED_PARAMETER(Length); + + UsbSamp_DbgPrint(3, ("ReadWriteIsochEndPoints - begins\n")); + + // + // Get the pipe associate with this request. + // + fileContext = GetFileContext(WdfRequestGetFileObject(Request)); + pipe = fileContext->Pipe; + + WDF_USB_PIPE_INFORMATION_INIT(&pipeInfo); + WdfUsbTargetPipeGetInformation(pipe, &pipeInfo); + + if ((WdfUsbPipeTypeIsochronous != pipeInfo.PipeType)) { + UsbSamp_DbgPrint(1, ("Pipe type is not Isochronous\n")); + status = STATUS_INVALID_DEVICE_REQUEST; + goto Exit; + + } + + if (RequestType == WdfRequestTypeRead && WdfUsbTargetPipeIsInEndpoint(pipe) == FALSE) { + UsbSamp_DbgPrint(1, ("Invalid pipe - not an input pipe\n")); + status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + if (RequestType == WdfRequestTypeWrite && WdfUsbTargetPipeIsOutEndpoint(pipe) == FALSE) { + UsbSamp_DbgPrint(1, ("Invalid pipe - not an output pipe\n")); + status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + deviceContext = GetDeviceContext(WdfIoQueueGetDevice(Queue)); + rwContext = GetRequestContext(Request); + + if (RequestType == WdfRequestTypeRead) { + rwContext->Read = TRUE; + status = WdfRequestForwardToIoQueue(Request, deviceContext->IsochReadQueue); + } + else { + rwContext->Read = FALSE; + status = WdfRequestForwardToIoQueue(Request, deviceContext->IsochWriteQueue); + } + + if (!NT_SUCCESS(status)){ + UsbSamp_DbgPrint(1, ("WdfRequestForwardToIoQueue failed with status 0x%x\n", status)); + goto Exit; + } + + return; + +Exit: + WdfRequestCompleteWithInformation(Request, status, 0); + return; +} + +VOID +UsbSamp_EvtIoQueueReadyNotification( + WDFQUEUE Queue, + WDFCONTEXT Context + ) +/*++ + +Routine Description: + + This function is called when the WDF queue transitions from 0 to 1 requests in the + queue. Because we are using queue level synchronization, the framework will not + call this routine concurrently if another request is received while this routine is + handling the previously retrieved request. That way we can compute the StartFrame + and dispatch requests without being concerned about two requests racing through + this routine and using StartFrame numbers that overlap each other. + + This is common routine for both read and write requests. + +--*/ +{ + NTSTATUS status; + WDF_REQUEST_PARAMETERS requestParams; + WDFREQUEST request; + PREQUEST_CONTEXT rwContext; + + do { + + status = WdfIoQueueRetrieveNextRequest(Queue, &request); + + if (!NT_SUCCESS(status)) { + return; + } + + rwContext = GetRequestContext(request); + + WDF_REQUEST_PARAMETERS_INIT(&requestParams); + WdfRequestGetParameters(request, &requestParams); + + if (rwContext->Read) { + PerformIsochTransfer((PDEVICE_CONTEXT)Context, + request, + (ULONG)requestParams.Parameters.Read.Length); + } + else { + PerformIsochTransfer((PDEVICE_CONTEXT)Context, + request, + (ULONG)requestParams.Parameters.Write.Length); + + } + + } while (status == STATUS_SUCCESS); + + return; +} + + +VOID +PerformIsochTransfer( + _In_ PDEVICE_CONTEXT DeviceContext, + _In_ WDFREQUEST Request, + _In_ ULONG TotalLength + ) +/*++ + +Routine Description: + + Common routine to perform isoch transfer to fullspeed and highspeed device. + +Arguments: + + Device - Device handle + Queue - Queue the request is delivered from + Request - Read/Write Request received from the user app. + TotalLength - Length of the user buffer. + Request - Read or Write request + +Return Value: + + VOID +--*/ +{ + ULONG numberOfPackets; + NTSTATUS status; + PREQUEST_CONTEXT rwContext; + WDFUSBPIPE pipe; + PFILE_CONTEXT fileContext; + WDF_OBJECT_ATTRIBUTES attributes; + ULONG j; + USBD_PIPE_HANDLE usbdPipeHandle; + PMDL requestMdl; + WDFMEMORY urbMemory; + PURB urb; + size_t urbSize; + ULONG offset; + ULONG frameNumber, numberOfFrames; + PPIPE_CONTEXT pipeContext; + + rwContext = GetRequestContext(Request); + + UsbSamp_DbgPrint(3, ("PerformIsochTransfer %s for Length %d - begins\n", + rwContext->Read ? "Read":"Write", TotalLength)); + // + // Get the pipe associate with this request. + // + fileContext = GetFileContext(WdfRequestGetFileObject(Request)); + pipe = fileContext->Pipe; + pipeContext = GetPipeContext(pipe); + + if ((TotalLength % pipeContext->TransferSizePerFrame) != 0) { + UsbSamp_DbgPrint(1, ("The transfer must evenly start and end on whole frame boundaries.\n")); + UsbSamp_DbgPrint(1, ("Transfer length should be multiples of %d\n", pipeContext->TransferSizePerFrame)); + status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + if (DeviceContext->IsDeviceSuperSpeed) { + + numberOfFrames = TotalLength / pipeContext->TransferSizePerFrame; + numberOfPackets = TotalLength / pipeContext->TransferSizePerMicroframe; + + // + // Then make sure the buffer doesn't exceed maximum allowed packets per transfer + // + + if (numberOfPackets > MAX_SUPPORTED_PACKETS_FOR_SUPER_SPEED) { + UsbSamp_DbgPrint(1, ("NumberOfPackets %d required to transfer exceeds the limit %d\n", + numberOfPackets, MAX_SUPPORTED_PACKETS_FOR_SUPER_SPEED)); + status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + UsbSamp_DbgPrint(3, ("Will send %d packets of %d bytes in %d frames\n", + numberOfPackets, pipeContext->TransferSizePerMicroframe, numberOfFrames)); + + } else if (DeviceContext->IsDeviceHighSpeed) { + + + numberOfFrames = TotalLength / pipeContext->TransferSizePerFrame; + numberOfPackets = TotalLength / pipeContext->TransferSizePerMicroframe; + + // + // Then make sure the buffer doesn't exceed maximum allowed packets per transfer + // + + if (numberOfPackets > MAX_SUPPORTED_PACKETS_FOR_HIGH_SPEED) { + UsbSamp_DbgPrint(1, ("NumberOfPackets %d required to transfer exceeds the limit %d\n", + numberOfPackets, MAX_SUPPORTED_PACKETS_FOR_HIGH_SPEED)); + status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + UsbSamp_DbgPrint(3, ("Will send %d packets of %d bytes in %d frames\n", + numberOfPackets, pipeContext->TransferSizePerMicroframe, numberOfFrames)); + + } + else { + + numberOfPackets = TotalLength / pipeContext->TransferSizePerFrame; + numberOfFrames = numberOfPackets; + + // + // Then make sure the buffer doesn't exceed maximum allowed packets per transfer + // + + if (numberOfPackets > MAX_SUPPORTED_PACKETS_FOR_FULL_SPEED) { + UsbSamp_DbgPrint(1, ("NumberOfPackets %d required to transfer exceeds the limit %d\n", + numberOfPackets, MAX_SUPPORTED_PACKETS_FOR_FULL_SPEED)); + status = STATUS_INVALID_PARAMETER; + goto Exit; + } + + UsbSamp_DbgPrint(3, ("Will send %d packets of %d bytes in %d frames\n", + numberOfPackets, pipeContext->TransferSizePerFrame, numberOfFrames)); + } + + if (rwContext->Read == TRUE) { + status = WdfRequestRetrieveOutputWdmMdl(Request, &requestMdl); + if (!NT_SUCCESS(status)){ + UsbSamp_DbgPrint(1, ("WdfRequestRetrieveOutputWdmMdl failed %x\n", status)); + goto Exit; + } + } + else { + status = WdfRequestRetrieveInputWdmMdl(Request, &requestMdl); + if (!NT_SUCCESS(status)){ + UsbSamp_DbgPrint(1, ("WdfRequestRetrieveInputWdmMdl failed %x\n", status)); + goto Exit; + } + } + + urbSize = GET_ISO_URB_SIZE(numberOfPackets); + + // + // Allocate memory for URB. + // + WDF_OBJECT_ATTRIBUTES_INIT(&attributes); + attributes.ParentObject = Request; + + status = WdfUsbTargetDeviceCreateIsochUrb(DeviceContext->WdfUsbTargetDevice, + &attributes, + numberOfPackets, + &urbMemory, + NULL); + + if (!NT_SUCCESS(status)) { + UsbSamp_DbgPrint(1, ("WdfUsbTargetDeviceCreateIsochUrb failed 0x%x\n", status)); + goto Exit; + } + + urb = WdfMemoryGetBuffer(urbMemory, NULL); + + usbdPipeHandle = WdfUsbTargetPipeWdmGetPipeHandle(pipe); + urb->UrbIsochronousTransfer.Hdr.Length = (USHORT) urbSize; + urb->UrbIsochronousTransfer.Hdr.Function = URB_FUNCTION_ISOCH_TRANSFER; + urb->UrbIsochronousTransfer.PipeHandle = usbdPipeHandle; + + if (rwContext->Read) { + urb->UrbIsochronousTransfer.TransferFlags = USBD_TRANSFER_DIRECTION_IN; + } + else { + urb->UrbIsochronousTransfer.TransferFlags = USBD_TRANSFER_DIRECTION_OUT; + } + + urb->UrbIsochronousTransfer.TransferBufferLength = TotalLength; + urb->UrbIsochronousTransfer.TransferBufferMDL = requestMdl; + urb->UrbIsochronousTransfer.NumberOfPackets = numberOfPackets; + urb->UrbIsochronousTransfer.UrbLink = NULL; + + // + // Set the offsets for every packet for reads/writes + // + offset = 0; + + for (j = 0; j < numberOfPackets; j++) { + + if (DeviceContext->IsDeviceHighSpeed || + DeviceContext->IsDeviceSuperSpeed) { + urb->UrbIsochronousTransfer.IsoPacket[j].Offset = j * pipeContext->TransferSizePerMicroframe; + } + else { + urb->UrbIsochronousTransfer.IsoPacket[j].Offset = j * pipeContext->TransferSizePerFrame; + } + + // + // Length is a return value on Isoch IN. It is ignored on Isoch OUT. + // + urb->UrbIsochronousTransfer.IsoPacket[j].Length = 0; + urb->UrbIsochronousTransfer.IsoPacket[j].Status = 0; + + UsbSamp_DbgPrint(3, ("IsoPacket[%d].Offset = %X IsoPacket[%d].Length = %X\n", + j, urb->UrbIsochronousTransfer.IsoPacket[j].Offset, + j, urb->UrbIsochronousTransfer.IsoPacket[j].Length)); + } + + // + // Calculate the StartFrame number: + // When the client driver sets the ASAP flag, it basically guarantees that + // it will make data available to the host controller (HC) and that the + // HC should transfer it in the next transfer frame for the endpoint. + // (The HC maintains a next transfer frame state variable for each endpoint). + // If the data does not get to the HC fast enough, the USBD_ISO_PACKET_DESCRIPTOR - + // Status is USBD_STATUS_BAD_START_FRAME on uhci. On ohci it is 0xC000000E. + // + + //urb->UrbIsochronousTransfer.TransferFlags |= USBD_START_ISO_TRANSFER_ASAP; + + // + // Instead of using ASAP, we will explicitly set start frame since we cannot control the + // response time of application that's sending request to us. + // + status = WdfUsbTargetDeviceRetrieveCurrentFrameNumber(DeviceContext->WdfUsbTargetDevice, + &frameNumber); + if (!NT_SUCCESS(status)) { + UsbSamp_DbgPrint(1, ("Failed to get frame number urb\n")); + goto Exit; + } + + if (frameNumber < pipeContext->NextFrameNumber) { + // + // Controller hasn't finished sending perivously scheduled request. So we will use + // the NextFrameNumber that we calculated for this one. + // + urb->UrbIsochronousTransfer.StartFrame = pipeContext->NextFrameNumber; + } + else { + // + // Controller frame number has advanced beyond the NextFrameNumber we calculated. + // + urb->UrbIsochronousTransfer.StartFrame = frameNumber; + } + + // + // Let us add a small latency to account for the time delay in reaching the controller + // from here. + // + urb->UrbIsochronousTransfer.StartFrame += DISPATCH_LATENCY_IN_MS; + + // + // Calculate the NextFrameNumber. + // + pipeContext->NextFrameNumber = urb->UrbIsochronousTransfer.StartFrame + numberOfFrames; + + // + // Associate the URB with the request. + // + status = WdfUsbTargetPipeFormatRequestForUrb(pipe, + Request, + urbMemory, + NULL ); + if (!NT_SUCCESS(status)) { + UsbSamp_DbgPrint(1, ("Failed to format requset for urb\n")); + goto Exit; + } + + WdfRequestSetCompletionRoutine(Request, + UsbSamp_EvtIsoRequestCompletionRoutine, + rwContext); + + rwContext->UrbMemory = urbMemory; + rwContext->Mdl = requestMdl; + rwContext->Length = TotalLength; + rwContext->Numxfer = 0; + rwContext->VirtualAddress = (ULONG_PTR)MmGetMdlVirtualAddress(requestMdl); + + if (WdfRequestSend(Request, WdfUsbTargetPipeGetIoTarget(pipe), WDF_NO_SEND_OPTIONS) == FALSE) { + status = WdfRequestGetStatus(Request); + UsbSamp_DbgPrint(1, ("WdfRequestSend failed with status code 0x%x\n", status)); + goto Exit; + } + +Exit: + + if (!NT_SUCCESS(status)) { + WdfRequestCompleteWithInformation(Request, status, 0); + } + + UsbSamp_DbgPrint(3, ("PerformHighSpeedIsochTransfer -- ends status 0x%x\n", status)); + return; +} + + +VOID +UsbSamp_EvtIsoRequestCompletionRoutine( + _In_ WDFREQUEST Request, + _In_ WDFIOTARGET Target, + PWDF_REQUEST_COMPLETION_PARAMS CompletionParams, + _In_ WDFCONTEXT Context + ) +/*++ + +Routine Description: + + Completion Routine + +Arguments: + + Context - Driver supplied context + Target - Target handle + Request - Request handle + Params - request completion params + + +Return Value: + + VOID + +--*/ +{ + PURB urb; + NTSTATUS status; + ULONG length, i, totalPacketLenght; + PREQUEST_CONTEXT rwContext; + + UNREFERENCED_PARAMETER(Target); + + UsbSamp_DbgPrint(3, ("UsbSampEvtIsoRequestCompletionRoutine - begins\n")); + + rwContext = (PREQUEST_CONTEXT)Context; + + urb = (PURB) WdfMemoryGetBuffer(rwContext->UrbMemory, NULL); + + length = urb->UrbIsochronousTransfer.TransferBufferLength; + + status = CompletionParams->IoStatus.Status; + + totalPacketLenght = 0; + + for (i = 0; i < urb->UrbIsochronousTransfer.NumberOfPackets; i++) { + + UsbSamp_DbgPrint(3, ("IsoPacket[%d].Length = %X IsoPacket[%d].Status = %X\n", + i, + urb->UrbIsochronousTransfer.IsoPacket[i].Length, + i, + urb->UrbIsochronousTransfer.IsoPacket[i].Status)); + + totalPacketLenght += urb->UrbIsochronousTransfer.IsoPacket[i].Length; + } + + if (NT_SUCCESS(status) && USBD_SUCCESS(urb->UrbHeader.Status)) { + // + // For iosch out, IsoPacket[].Length field is not updated by the USB stack. + // + if (rwContext->Read == TRUE) { + NT_ASSERT(totalPacketLenght == length); + } + + WdfRequestCompleteWithInformation(Request, STATUS_SUCCESS, length); + + UsbSamp_DbgPrint(3, ("Request completed with success, TransferBufferLength = %d, TotalPacketLength = %d\n", + length, totalPacketLenght)); + } + else { + + UsbSamp_DbgPrint(1, ("Read or write irp failed with NTSTATUS 0x%x, USBD_STATUS 0x%x\n", + status, urb->UrbHeader.Status)); + + WdfRequestCompleteWithInformation(Request, status, 0); + } + + UsbSamp_DbgPrint(3, ("UsbSampEvtIsoRequestCompletionRoutine - ends\n")); + + return; +} + +VOID +UsbSamp_EvtIoStop( + _In_ WDFQUEUE Queue, + _In_ WDFREQUEST Request, + _In_ ULONG ActionFlags + ) +/*++ + +Routine Description: + + This callback is invoked on every inflight request when the device + is suspended or removed. Since our inflight read and write requests + are actually pending in the target device, we will just acknowledge + its presence. Until we acknowledge, complete, or requeue the requests + framework will wait before allowing the device suspend or remove to + proceeed. When the underlying USB stack gets the request to suspend or + remove, it will fail all the pending requests. + +Arguments: + +Return Value: + None + +--*/ +{ + UNREFERENCED_PARAMETER(Queue); + + if (ActionFlags & WdfRequestStopActionSuspend ) { + WdfRequestStopAcknowledge(Request, FALSE); // Don't requeue + } + else if (ActionFlags & WdfRequestStopActionPurge) { + WdfRequestCancelSentRequest(Request); + } + + return; +} diff --git a/usb/usbsamp/sys/private.h b/usb/usbsamp/sys/private.h new file mode 100644 index 000000000..ef1649082 --- /dev/null +++ b/usb/usbsamp/sys/private.h @@ -0,0 +1,391 @@ +/*++ + +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: + + Contains structure definitions and function prototypes private to + the driver. + +Environment: + + Kernel mode + +--*/ + +#pragma warning(disable:4200) // +#pragma warning(disable:4201) // nameless struct/union +#pragma warning(disable:4214) // bit field types other than int + +#include +#include +#include +#include "usbdi.h" +#include "usbdlib.h" + +#pragma warning(default:4200) +#pragma warning(default:4201) +#pragma warning(default:4214) + +#include +#include +#include "public.h" + + +#ifndef _H +#define _H + +#define POOL_TAG (ULONG) 'SBSU' + +#define MAX_SUPPORTED_PACKETS_FOR_SUPER_SPEED 1024 +#define MAX_SUPPORTED_PACKETS_FOR_HIGH_SPEED 1024 +#define MAX_SUPPORTED_PACKETS_FOR_FULL_SPEED 255 + +#define DISPATCH_LATENCY_IN_MS 10 + + +#undef ExAllocatePool +#define ExAllocatePool(type, size) \ + ExAllocatePoolWithTag(type, size, POOL_TAG); + +#if DBG + +#define UsbSamp_DbgPrint(level, _x_) \ + if((level) <= DebugLevel) { \ + DbgPrint("UsbSamp: "); DbgPrint _x_; \ + } + +#else + +#define UsbSamp_DbgPrint(level, _x_) + +#endif + +#define MAX_FULL_SPEED_TRANSFER_SIZE 64 +#define MAX_HIGH_SPEED_TRANSFER_SIZE 512 +#define MAX_SUPER_SPEED_TRANSFER_SIZE 1024 +#define MAX_STREAM_VALID_PACKET_SIZE 1024 +#define REMOTE_WAKEUP_MASK 0x20 + +#define DEFAULT_REGISTRY_TRANSFER_SIZE 65536 + +#define IDLE_CAPS_TYPE IdleUsbSelectiveSuspend + + +// +// A structure representing the instance information associated with +// this particular device. +// + +typedef struct _DEVICE_CONTEXT { + + USB_DEVICE_DESCRIPTOR UsbDeviceDescriptor; + + PUSB_CONFIGURATION_DESCRIPTOR UsbConfigurationDescriptor; + + WDFUSBDEVICE WdfUsbTargetDevice; + + ULONG WaitWakeEnable; + + BOOLEAN IsDeviceHighSpeed; + + BOOLEAN IsDeviceSuperSpeed; + + WDFUSBINTERFACE UsbInterface; + + UCHAR SelectedAlternateSetting; + + UCHAR NumberConfiguredPipes; + + ULONG MaximumTransferSize; + + WDFQUEUE IsochReadQueue; + + WDFQUEUE IsochWriteQueue; + + BOOLEAN IsStaticStreamsSupported; + + USHORT NumberOfStreamsSupportedByController; + + USBD_HANDLE UsbdHandle; + + +} DEVICE_CONTEXT, *PDEVICE_CONTEXT; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(DEVICE_CONTEXT, GetDeviceContext) +// +// This context is associated with every open handle. +// +typedef struct _FILE_CONTEXT { + + WDFUSBPIPE Pipe; + +} FILE_CONTEXT, *PFILE_CONTEXT; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(FILE_CONTEXT, GetFileContext) + +#if (NTDDI_VERSION >= NTDDI_WIN8) + +typedef struct _USBSAMP_STREAM_INFO { + + // Number of enabled streams on this pipe + ULONG NumberOfStreams; + + // Array of stream information structures representing streams on this pipe + PUSBD_STREAM_INFORMATION StreamList; + +} USBSAMP_STREAM_INFO, *PUSBSAMP_STREAM_INFO; + +#endif + +// +// This context is associated with every pipe handle. In this sample, +// it used for isoch transfers. +// +typedef struct _PIPE_CONTEXT { + + ULONG NextFrameNumber; + + ULONG TransferSizePerMicroframe; + + ULONG TransferSizePerFrame; + + BOOLEAN StreamConfigured; + +#if (NTDDI_VERSION >= NTDDI_WIN8) + USBSAMP_STREAM_INFO StreamInfo; +#endif + +} PIPE_CONTEXT, *PPIPE_CONTEXT; + + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(PIPE_CONTEXT, GetPipeContext) + + +// +// This context is associated with every request recevied by the driver +// from the app. +// +typedef struct _REQUEST_CONTEXT { + + WDFMEMORY UrbMemory; + PMDL Mdl; + ULONG Length; // remaining to xfer + ULONG Numxfer; + ULONG_PTR VirtualAddress; // va for next segment of xfer. + BOOLEAN Read; // TRUE if Read +} REQUEST_CONTEXT, * PREQUEST_CONTEXT; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(REQUEST_CONTEXT, GetRequestContext) + +typedef struct _WORKITEM_CONTEXT { + WDFDEVICE Device; + WDFUSBPIPE Pipe; +} WORKITEM_CONTEXT, *PWORKITEM_CONTEXT; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(WORKITEM_CONTEXT, GetWorkItemContext) +extern ULONG DebugLevel; + +DRIVER_INITIALIZE DriverEntry; + +EVT_WDF_DRIVER_DEVICE_ADD UsbSamp_EvtDeviceAdd; +EVT_WDF_DEVICE_PREPARE_HARDWARE UsbSamp_EvtDevicePrepareHardware; + +EVT_WDF_OBJECT_CONTEXT_CLEANUP UsbSamp_EvtDeviceContextCleanup; + +#if (NTDDI_VERSION >= NTDDI_WIN8) +EVT_WDF_OBJECT_CONTEXT_CLEANUP UsbSamp_EvtPipeContextCleanup; +#endif + +EVT_WDF_DEVICE_FILE_CREATE UsbSamp_EvtDeviceFileCreate; + +EVT_WDF_IO_QUEUE_IO_READ UsbSamp_EvtIoRead; +EVT_WDF_IO_QUEUE_IO_WRITE UsbSamp_EvtIoWrite; +EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL UsbSamp_EvtIoDeviceControl; + +EVT_WDF_REQUEST_COMPLETION_ROUTINE UsbSamp_EvtReadWriteCompletion; +EVT_WDF_REQUEST_COMPLETION_ROUTINE UsbSamp_EvtIsoRequestCompletionRoutine; + +EVT_WDF_IO_QUEUE_IO_STOP UsbSamp_EvtIoStop; + +EVT_WDF_IO_QUEUE_STATE UsbSamp_EvtIoQueueReadyNotification; + +EVT_WDF_WORKITEM UsbSamp_EvtReadWriteWorkItem; + +WDFUSBPIPE +GetPipeFromName( + _In_ PDEVICE_CONTEXT DeviceContext, + _In_ PUNICODE_STRING FileName + ); + +VOID +ReadWriteIsochEndPoints( + _In_ WDFQUEUE Queue, + _In_ WDFREQUEST Request, + _In_ ULONG Length, + _In_ WDF_REQUEST_TYPE RequestType + ); + +VOID +ReadWriteBulkEndPoints( + _In_ WDFQUEUE Queue, + _In_ WDFREQUEST Request, + _In_ ULONG Length, + _In_ WDF_REQUEST_TYPE RequestType + ); + +NTSTATUS +ResetPipe( + _In_ WDFUSBPIPE Pipe + ); + +NTSTATUS +ResetDevice( + _In_ WDFDEVICE Device + ); + +NTSTATUS +ReadAndSelectDescriptors( + _In_ WDFDEVICE Device + ); + +NTSTATUS +ConfigureDevice( + _In_ WDFDEVICE Device + ); + +NTSTATUS +SelectInterfaces( + _In_ WDFDEVICE Device + ); + +NTSTATUS +SetPowerPolicy( + _In_ WDFDEVICE Device + ); + +NTSTATUS +AbortPipes( + _In_ WDFDEVICE Device + ); + + +NTSTATUS +QueuePassiveLevelCallback( + _In_ WDFDEVICE Device, + _In_ WDFUSBPIPE Pipe + ); + +VOID +PerformIsochTransfer( + _In_ PDEVICE_CONTEXT DeviceContext, + _In_ WDFREQUEST Request, + _In_ ULONG TotalLength + ); + +VOID +DbgPrintRWContext( + PREQUEST_CONTEXT rwContext + ); + +NTSTATUS +ReadFdoRegistryKeyValue( + _In_ WDFDRIVER Driver, + _In_ LPWSTR Name, + _Out_ PULONG Value + ); + +NTSTATUS +InitializePipeContextForHighSpeedDevice( + _In_ WDFUSBPIPE Pipe + ); + +NTSTATUS +InitializePipeContextForFullSpeedDevice( + _In_ WDFUSBPIPE Pipe + ); + +NTSTATUS +RetrieveDeviceInformation( + _In_ WDFDEVICE Device + ); + + +USBD_STATUS +UsbSamp_ValidateConfigurationDescriptor( + _In_reads_bytes_(BufferLength) PUSB_CONFIGURATION_DESCRIPTOR ConfigDesc, + _In_ ULONG BufferLength, + _Inout_ PUCHAR *Offset + ); + + +NTSTATUS +GetStackCapability( + _In_ WDFDEVICE Device, + _In_ const GUID* CapabilityType, + _In_ ULONG OutputBufferLength, + _When_(OutputBufferLength == 0, _Pre_null_) + _When_(OutputBufferLength != 0, _Out_writes_bytes_(OutputBufferLength)) + PUCHAR OutputBuffer + ); + +#if (NTDDI_VERSION >= NTDDI_WIN8) +NTSTATUS +InitializePipeContextForSuperSpeedDevice( + _In_ PDEVICE_CONTEXT DeviceContext, + _In_ WDFUSBPIPE Pipe + ); + +NTSTATUS +InitializePipeContextForSuperSpeedIsochPipe( + _In_ PDEVICE_CONTEXT DeviceContext, + _In_ UCHAR InterfaceNumber, + _In_ WDFUSBPIPE Pipe + ); + +PUSB_ENDPOINT_DESCRIPTOR +GetEndpointDescriptorForEndpointAddress( + _In_ PDEVICE_CONTEXT DeviceContext, + _In_ UCHAR InterfaceNumber, + _In_ UCHAR EndpointAddress, + _Out_ PUSB_SUPERSPEED_ENDPOINT_COMPANION_DESCRIPTOR *ppEndpointCompanionDescriptor + ); + +NTSTATUS +InitializePipeContextForSuperSpeedBulkPipe( + _In_ PDEVICE_CONTEXT DeviceContext, + _In_ UCHAR InterfaceNumber, + _In_ WDFUSBPIPE Pipe + ); + +USBD_PIPE_HANDLE +GetStreamPipeHandleFromBulkPipe( + _In_ WDFUSBPIPE Pipe + ); + +VOID +ConfigureStreamPipeHandleForRequest( + _In_ WDFREQUEST Request, + _In_ WDFUSBPIPE Pipe + ); + +#endif + +ULONG +GetMaxTransferSize( + _In_ WDFUSBPIPE Pipe, + _In_ PDEVICE_CONTEXT DeviceContext +); + + +#endif + diff --git a/usb/usbsamp/sys/public.h b/usb/usbsamp/sys/public.h new file mode 100644 index 000000000..765a57323 --- /dev/null +++ b/usb/usbsamp/sys/public.h @@ -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: + + BulkUsr.h + +Abstract: + +Environment: + + User & Kernel mode + +--*/ + +#ifndef _USER_H +#define _USER_H + +#include + +// {6068EB61-98E7-4c98-9E20-1F068295909A} +DEFINE_GUID(GUID_CLASS_USBSAMP_USB, +0x873fdf, 0x61a8, 0x11d1, 0xaa, 0x5e, 0x0, 0xc0, 0x4f, 0xb1, 0x72, 0x8b); + +#define IOCTL_INDEX 0x0000 + + +#define IOCTL_USBSAMP_GET_CONFIG_DESCRIPTOR CTL_CODE(FILE_DEVICE_UNKNOWN, \ + IOCTL_INDEX, \ + METHOD_BUFFERED, \ + FILE_ANY_ACCESS) + +#define IOCTL_USBSAMP_RESET_DEVICE CTL_CODE(FILE_DEVICE_UNKNOWN, \ + IOCTL_INDEX + 1, \ + METHOD_BUFFERED, \ + FILE_ANY_ACCESS) + +#define IOCTL_USBSAMP_RESET_PIPE CTL_CODE(FILE_DEVICE_UNKNOWN, \ + IOCTL_INDEX + 2, \ + METHOD_BUFFERED, \ + FILE_ANY_ACCESS) + +#endif diff --git a/usb/usbsamp/sys/queue.c b/usb/usbsamp/sys/queue.c new file mode 100644 index 000000000..5047d24c5 --- /dev/null +++ b/usb/usbsamp/sys/queue.c @@ -0,0 +1,595 @@ +/*++ + +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: + + Queue.c + +Abstract: + + This file contains dispatch routines for create, + close, device-control, read & write. + +Environment: + + Kernel mode + +--*/ + +#include "private.h" + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, UsbSamp_EvtDeviceFileCreate) +#pragma alloc_text(PAGE, UsbSamp_EvtIoDeviceControl) +#pragma alloc_text(PAGE, UsbSamp_EvtIoRead) +#pragma alloc_text(PAGE, UsbSamp_EvtIoWrite) +#pragma alloc_text(PAGE, GetPipeFromName) +#pragma alloc_text(PAGE, ResetPipe) +#pragma alloc_text(PAGE, ResetDevice) +#endif + + +VOID +UsbSamp_EvtDeviceFileCreate( + _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 a file. + This callback is called synchronously, 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 - copy of the create IO_STACK_LOCATION + +Return Value: + + NT status code + +--*/ +{ + NTSTATUS status = STATUS_UNSUCCESSFUL; + PUNICODE_STRING fileName; + PFILE_CONTEXT pFileContext; + PDEVICE_CONTEXT pDevContext; + WDFUSBPIPE pipe; + + UsbSamp_DbgPrint(3, ("EvtDeviceFileCreate - begins\n")); + + PAGED_CODE(); + + // + // initialize variables + // + pDevContext = GetDeviceContext(Device); + pFileContext = GetFileContext(FileObject); + + + fileName = WdfFileObjectGetFileName(FileObject); + + if (0 == fileName->Length) { + // + // opening a device as opposed to pipe. + // + status = STATUS_SUCCESS; + } + else { + pipe = GetPipeFromName(pDevContext, fileName); + + if (pipe != NULL) { + // + // found a match + // + pFileContext->Pipe = pipe; + + WdfUsbTargetPipeSetNoMaximumPacketSizeCheck(pipe); + + status = STATUS_SUCCESS; + } + else { + status = STATUS_INVALID_DEVICE_REQUEST; + } + } + + WdfRequestComplete(Request, status); + + UsbSamp_DbgPrint(3, ("EvtDeviceFileCreate - ends\n")); + + return; +} + +VOID +UsbSamp_EvtIoDeviceControl( + _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 + +--*/ +{ + WDFDEVICE device; + PVOID ioBuffer; + size_t bufLength; + NTSTATUS status; + PDEVICE_CONTEXT pDevContext; + PFILE_CONTEXT pFileContext; + ULONG length = 0; + + UNREFERENCED_PARAMETER(OutputBufferLength); + UNREFERENCED_PARAMETER(InputBufferLength); + + UsbSamp_DbgPrint(3, ("Entered UsbSamp_DispatchDevCtrl\n")); + + PAGED_CODE(); + + // + // initialize variables + // + device = WdfIoQueueGetDevice(Queue); + pDevContext = GetDeviceContext(device); + + switch(IoControlCode) { + + case IOCTL_USBSAMP_RESET_PIPE: + + pFileContext = GetFileContext(WdfRequestGetFileObject(Request)); + + if (pFileContext->Pipe == NULL) { + status = STATUS_INVALID_PARAMETER; + } + else { + status = ResetPipe(pFileContext->Pipe); + } + + break; + + case IOCTL_USBSAMP_GET_CONFIG_DESCRIPTOR: + + + if (pDevContext->UsbConfigurationDescriptor) { + + length = pDevContext->UsbConfigurationDescriptor->wTotalLength; + + status = WdfRequestRetrieveOutputBuffer(Request, length, &ioBuffer, &bufLength); + if (!NT_SUCCESS(status)){ + UsbSamp_DbgPrint(1, ("WdfRequestRetrieveInputBuffer failed\n")); + break; + } + + RtlCopyMemory(ioBuffer, + pDevContext->UsbConfigurationDescriptor, + length); + + status = STATUS_SUCCESS; + } + else { + status = STATUS_INVALID_DEVICE_STATE; + } + + break; + + case IOCTL_USBSAMP_RESET_DEVICE: + + status = ResetDevice(device); + break; + + default : + status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + + WdfRequestCompleteWithInformation(Request, status, length); + + UsbSamp_DbgPrint(3, ("Exit UsbSamp_DispatchDevCtrl\n")); + + return; +} + +VOID +UsbSamp_EvtIoRead( + _In_ WDFQUEUE Queue, + _In_ WDFREQUEST Request, + _In_ size_t Length + ) +/*++ + +Routine Description: + + Called by the framework when it receives Read requests. + +Arguments: + + Queue - Default queue handle + Request - Handle to the read/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: + + +--*/ +{ + PFILE_CONTEXT fileContext = NULL; + WDFUSBPIPE pipe; + WDF_USB_PIPE_INFORMATION pipeInfo; + + PAGED_CODE(); + + // + // Get the pipe associate with this request. + // + fileContext = GetFileContext(WdfRequestGetFileObject(Request)); + pipe = fileContext->Pipe; + if (pipe == NULL) { + UsbSamp_DbgPrint(1, ("pipe handle is NULL\n")); + WdfRequestCompleteWithInformation(Request, STATUS_INVALID_PARAMETER, 0); + return; + } + WDF_USB_PIPE_INFORMATION_INIT(&pipeInfo); + WdfUsbTargetPipeGetInformation(pipe, &pipeInfo); + + if ((WdfUsbPipeTypeBulk == pipeInfo.PipeType) || + (WdfUsbPipeTypeInterrupt == pipeInfo.PipeType)) { + + ReadWriteBulkEndPoints(Queue, Request, (ULONG) Length, WdfRequestTypeRead); + return; + + } + else if (WdfUsbPipeTypeIsochronous == pipeInfo.PipeType){ + +#if !defined(BUFFERED_READ_WRITE) // if doing DIRECT_IO + ReadWriteIsochEndPoints(Queue, Request, (ULONG) Length, WdfRequestTypeRead); + return; +#endif + + } + + UsbSamp_DbgPrint(1, ("ISO transfer is not supported for buffered I/O transfer\n")); + WdfRequestCompleteWithInformation(Request, STATUS_INVALID_DEVICE_REQUEST, 0); + + return; +} + +VOID +UsbSamp_EvtIoWrite( + _In_ WDFQUEUE Queue, + _In_ WDFREQUEST Request, + _In_ size_t Length + ) +/*++ + +Routine Description: + + Called by the framework when it receives Write requests. + +Arguments: + + Queue - Default queue handle + Request - Handle to the read/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: + + +--*/ +{ + PFILE_CONTEXT fileContext = NULL; + WDFUSBPIPE pipe; + WDF_USB_PIPE_INFORMATION pipeInfo; + + PAGED_CODE(); + + // + // Get the pipe associate with this request. + // + fileContext = GetFileContext(WdfRequestGetFileObject(Request)); + pipe = fileContext->Pipe; + if (pipe == NULL) { + UsbSamp_DbgPrint(1, ("pipe handle is NULL\n")); + WdfRequestCompleteWithInformation(Request, STATUS_INVALID_PARAMETER, 0); + return; + } + WDF_USB_PIPE_INFORMATION_INIT(&pipeInfo); + WdfUsbTargetPipeGetInformation(pipe, &pipeInfo); + + if ((WdfUsbPipeTypeBulk == pipeInfo.PipeType) || + (WdfUsbPipeTypeInterrupt == pipeInfo.PipeType)) { + + ReadWriteBulkEndPoints(Queue, Request, (ULONG) Length, WdfRequestTypeWrite); + return; + + } + else if (WdfUsbPipeTypeIsochronous == pipeInfo.PipeType){ + +#if !defined(BUFFERED_READ_WRITE) // if doing DIRECT_IO + ReadWriteIsochEndPoints(Queue, Request, (ULONG) Length, WdfRequestTypeWrite); + return; +#endif + + } + + UsbSamp_DbgPrint(1, ("ISO transfer is not supported for buffered I/O transfer\n")); + WdfRequestCompleteWithInformation(Request, STATUS_INVALID_DEVICE_REQUEST, 0); + + return; +} + +WDFUSBPIPE +GetPipeFromName( + _In_ PDEVICE_CONTEXT DeviceContext, + _In_ PUNICODE_STRING FileName + ) +/*++ + +Routine Description: + + This routine will pass the string pipe name and + fetch the pipe handle. + +Arguments: + + DeviceContext - pointer to Device Context + + FileName - string pipe name + +Return Value: + + The device extension maintains a pipe context for + the pipes on 82930 board. + +--*/ +{ + LONG ix; + ULONG uval; + ULONG nameLength; + ULONG umultiplier; + WDFUSBPIPE pipe = NULL; + + PAGED_CODE(); + + // + // typedef WCHAR *PWSTR; + // + nameLength = (FileName->Length / sizeof(WCHAR)); + + UsbSamp_DbgPrint(3, ("UsbSamp_PipeWithName - begins\n")); + + if (nameLength != 0) { + + UsbSamp_DbgPrint(3, ("Filename = %wZ nameLength = %d\n", FileName, nameLength)); + + // + // Parse the pipe# + // + ix = nameLength - 1; + + // if last char isn't digit, decrement it. + while((ix > -1) && + ((FileName->Buffer[ix] < (WCHAR) '0') || + (FileName->Buffer[ix] > (WCHAR) '9'))) { + + ix--; + } + + if (ix > -1) { + + uval = 0; + umultiplier = 1; + + // traversing least to most significant digits. + + while((ix > -1) && + (FileName->Buffer[ix] >= (WCHAR) '0') && + (FileName->Buffer[ix] <= (WCHAR) '9')) { + + uval += (umultiplier * + (ULONG) (FileName->Buffer[ix] - (WCHAR) '0')); + + ix--; + umultiplier *= 10; + } + pipe = WdfUsbInterfaceGetConfiguredPipe( + DeviceContext->UsbInterface, + (UCHAR)uval, //PipeIndex, + NULL + ); + + } + } + + UsbSamp_DbgPrint(3, ("UsbSamp_PipeWithName - ends\n")); + + return pipe; +} + +NTSTATUS +ResetPipe( + _In_ WDFUSBPIPE Pipe + ) +/*++ + +Routine Description: + + This routine resets the pipe. + +Arguments: + + Pipe - framework pipe handle + +Return Value: + + NT status value + +--*/ +{ + NTSTATUS status; + + PAGED_CODE(); + + // + // This routine synchronously submits a URB_FUNCTION_RESET_PIPE + // request down the stack. + // + status = WdfUsbTargetPipeResetSynchronously(Pipe, + WDF_NO_HANDLE, // WDFREQUEST + NULL // PWDF_REQUEST_SEND_OPTIONS + ); + + if (NT_SUCCESS(status)) { + UsbSamp_DbgPrint(3, ("ResetPipe - success\n")); + status = STATUS_SUCCESS; + } + else { + UsbSamp_DbgPrint(1, ("ResetPipe - failed\n")); + } + + return status; +} + +VOID +StopAllPipes( + _In_ PDEVICE_CONTEXT DeviceContext + ) +{ + UCHAR count,i; + + count = DeviceContext->NumberConfiguredPipes; + for (i = 0; i < count; i++) { + WDFUSBPIPE pipe; + pipe = WdfUsbInterfaceGetConfiguredPipe(DeviceContext->UsbInterface, + i, //PipeIndex, + NULL + ); + WdfIoTargetStop(WdfUsbTargetPipeGetIoTarget(pipe), + WdfIoTargetCancelSentIo); + } +} + + +VOID +StartAllPipes( + _In_ PDEVICE_CONTEXT DeviceContext + ) +{ + NTSTATUS status; + UCHAR count,i; + + count = DeviceContext->NumberConfiguredPipes; + for (i = 0; i < count; i++) { + WDFUSBPIPE pipe; + pipe = WdfUsbInterfaceGetConfiguredPipe(DeviceContext->UsbInterface, + i, //PipeIndex, + NULL + ); + status = WdfIoTargetStart(WdfUsbTargetPipeGetIoTarget(pipe)); + if (!NT_SUCCESS(status)) { + UsbSamp_DbgPrint(1, ("StartAllPipes - failed pipe #%d\n", i)); + } + } +} + +NTSTATUS +ResetDevice( + _In_ WDFDEVICE Device + ) +/*++ + +Routine Description: + + This routine calls WdfUsbTargetDeviceResetPortSynchronously to reset the device if it's still + connected. + +Arguments: + + Device - Handle to a framework device + +Return Value: + + NT status value + +--*/ +{ + PDEVICE_CONTEXT pDeviceContext; + NTSTATUS status; + + UsbSamp_DbgPrint(3, ("ResetDevice - begins\n")); + + PAGED_CODE(); + + pDeviceContext = GetDeviceContext(Device); + + // + // A reset-device + // request will be stuck in the USB until the pending transactions + // have been canceled. Similarly, if there are pending tranasfers on the BULK + // _In_/OUT pipe cancel them. + // To work around this issue, the driver should stop the continuous reader + // (by calling WdfIoTargetStop) before resetting the device, and restart the + // continuous reader (by calling WdfIoTargetStart) after the request completes. + // + StopAllPipes(pDeviceContext); + + // + // It may not be necessary to check whether device is connected before + // resetting the port. + // + status = WdfUsbTargetDeviceIsConnectedSynchronous(pDeviceContext->WdfUsbTargetDevice); + + if (NT_SUCCESS(status)) { + status = WdfUsbTargetDeviceResetPortSynchronously(pDeviceContext->WdfUsbTargetDevice); + } + + StartAllPipes(pDeviceContext); + + UsbSamp_DbgPrint(3, ("ResetDevice - ends\n")); + + return status; +} diff --git a/usb/usbsamp/sys/stream.c b/usb/usbsamp/sys/stream.c new file mode 100644 index 000000000..37067f0ac --- /dev/null +++ b/usb/usbsamp/sys/stream.c @@ -0,0 +1,433 @@ + /*++ + +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: + + stream.c + +Abstract: + + This files contain routines for Streams. + +Environment: + + Kernel mode only + +Notes: + + +--*/ + +#include "private.h" + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, GetStackCapability) +#if (NTDDI_VERSION >= NTDDI_WIN8) +#pragma alloc_text(PAGE, InitializePipeContextForSuperSpeedBulkPipe) +#endif +#endif + +NTSTATUS +GetStackCapability( + _In_ WDFDEVICE Device, + _In_ const GUID* CapabilityType, + _In_ ULONG OutputBufferLength, + _When_(OutputBufferLength == 0, _Pre_null_) + _When_(OutputBufferLength != 0, _Out_writes_bytes_(OutputBufferLength)) + PUCHAR OutputBuffer + ) +/*++ + +Routine Description: + + The helper routine gets stack's capability. + +Arguments: + + Device - WDF Device Object + + CapabilityType - Pointer to capability type GUID + + OutputBufferLength - Length of output buffer + + OutPutBuffer - Output buffer + +Return Value: + + NTSTATUS + +--*/ + +{ + PDEVICE_CONTEXT pDevContext; + NTSTATUS status; + + PAGED_CODE(); + + pDevContext = GetDeviceContext(Device); + + // + // Note: All super speed bulk stream I/O transfers use USBD Handle obtained in + // UsbSamp_EvtDeviceAdd. If you call WdfUsbTargetDeviceQueryUsbCapability + // method instead of USBD_QueryUsbCapability here, it will not set stream + // capabilites for USBD Handle used by stream transfer in which case + // the open streams request will fail in this example. + // + status = USBD_QueryUsbCapability(pDevContext->UsbdHandle, + CapabilityType, + OutputBufferLength, + OutputBuffer, + NULL); + if (NT_SUCCESS(status)) { + UsbSamp_DbgPrint(1, ("USBD_QueryUsbCapability %x\n", status)); + } + + return status; +} + +#if (NTDDI_VERSION >= NTDDI_WIN8) + +NTSTATUS +InitializePipeContextForSuperSpeedBulkPipe( + _In_ PDEVICE_CONTEXT DeviceContext, + _In_ UCHAR InterfaceNumber, + _In_ WDFUSBPIPE Pipe + ) +/*++ + +Routine Description: + + This routine initialize pipe's streams context. + +Arguments: + + DeviceContext - pointer to device context + + InterfaceNumber - InterfaceNumber of selected interface + + Pipe - Bullk Pipe + +Return Value: + + NTSTATUS + +--*/ + +{ + WDF_USB_PIPE_INFORMATION pipeInfo; + PPIPE_CONTEXT pipeContext; + PUSB_ENDPOINT_DESCRIPTOR pEndpointDescriptor; + PUSB_SUPERSPEED_ENDPOINT_COMPANION_DESCRIPTOR pEndpointCompanionDescriptor; + + UCHAR endpointAddress; + ULONG maxStreams; + ULONG supportedStreams; + PUSBSAMP_STREAM_INFO pStreamInfo; + NTSTATUS status; + PURB pUrb = NULL; + ULONG i; + + PAGED_CODE(); + + WDF_USB_PIPE_INFORMATION_INIT(&pipeInfo); + WdfUsbTargetPipeGetInformation(Pipe, &pipeInfo); + pipeContext = GetPipeContext(Pipe); + pStreamInfo = &pipeContext->StreamInfo; + + pStreamInfo->NumberOfStreams = 0; + pStreamInfo->StreamList = NULL; + + pipeContext->StreamConfigured = FALSE; + + // + // Validate that the endpoint/pipe is of type BULK. + // Streams are only allowed on a SS BULK endpoint. + // Also ensure that the bus actually supports streams + // before proceeding + // + if (pipeInfo.PipeType != WdfUsbPipeTypeBulk || + DeviceContext->NumberOfStreamsSupportedByController == 0) { + status = STATUS_SUCCESS; + goto End; + } + + endpointAddress = pipeInfo.EndpointAddress; + + pEndpointDescriptor = GetEndpointDescriptorForEndpointAddress( + DeviceContext, + InterfaceNumber, + endpointAddress, + &pEndpointCompanionDescriptor); + + if (pEndpointDescriptor != NULL && + pEndpointCompanionDescriptor != NULL) { + + maxStreams = pEndpointCompanionDescriptor->bmAttributes.Bulk.MaxStreams; + + if (maxStreams == 0) { + + supportedStreams = 0; + + } else { + + supportedStreams = 1 << maxStreams; + } + + } else { + + UsbSamp_DbgPrint(1, ("Endpoint Descriptor or Endpoint Companion Descriptor is NULL.\n")); + status = STATUS_INVALID_PARAMETER; + goto End; + + } + + if (supportedStreams == 0) { + status = STATUS_SUCCESS; + goto End; + } + + if (supportedStreams > DeviceContext->NumberOfStreamsSupportedByController) { + supportedStreams = DeviceContext->NumberOfStreamsSupportedByController; + } + + pStreamInfo->NumberOfStreams = supportedStreams; + + pStreamInfo->StreamList = ExAllocatePoolWithTag( + NonPagedPool, + supportedStreams * sizeof(USBD_STREAM_INFORMATION), + POOL_TAG); + + if (pStreamInfo->StreamList == NULL) { + status = STATUS_INSUFFICIENT_RESOURCES; + goto End; + } + + for(i = 0; i < supportedStreams; i++) + { + pStreamInfo->StreamList[i].StreamID = i + 1; + } + + status = USBD_UrbAllocate(DeviceContext->UsbdHandle, &pUrb); + + if (!NT_SUCCESS(status)){ + pUrb = NULL; + status = STATUS_INSUFFICIENT_RESOURCES; + goto End; + } + + pUrb->UrbOpenStaticStreams.Hdr.Length = sizeof(struct _URB_OPEN_STATIC_STREAMS); + pUrb->UrbOpenStaticStreams.Hdr.Function = URB_FUNCTION_OPEN_STATIC_STREAMS; + pUrb->UrbOpenStaticStreams.PipeHandle = WdfUsbTargetPipeWdmGetPipeHandle(Pipe); + pUrb->UrbOpenStaticStreams.NumberOfStreams = pStreamInfo->NumberOfStreams; + pUrb->UrbOpenStaticStreams.StreamInfoSize = sizeof(USBD_STREAM_INFORMATION); + pUrb->UrbOpenStaticStreams.Streams = pStreamInfo->StreamList; + + // Send the URB down the stack + status = WdfUsbTargetPipeSendUrbSynchronously( + Pipe, + NULL, + NULL, + pUrb + ); + + if (NT_SUCCESS(status)) { + + pipeContext->StreamConfigured = TRUE; + + } +End: + if (!NT_SUCCESS(status)) { + + pipeContext->StreamConfigured = FALSE; + pStreamInfo->NumberOfStreams = 0; + + if (pStreamInfo->StreamList != NULL) { + ExFreePool(pStreamInfo->StreamList); + pStreamInfo->StreamList = NULL; + } + + } + + if(pUrb != NULL) { + USBD_UrbFree(DeviceContext->UsbdHandle, pUrb); + } + + return status; +} + + +USBD_PIPE_HANDLE +GetStreamPipeHandleFromBulkPipe( + _In_ WDFUSBPIPE Pipe + ) +/*++ + +Routine Description: + + This routine gets a stream USBD_PIPE_HANDLE from a super speed bulk pipe + +Arguments: + + Pipe - Bullk Pipe + +Return Value: + + A stream's USBD_PIPE_HANDLE + +--*/ +{ + PPIPE_CONTEXT pipeContext; + + PUSBSAMP_STREAM_INFO pStreamInfo; + USBD_PIPE_HANDLE streamPipeHandle; + ULONG index; + + pipeContext = GetPipeContext(Pipe); + + if (pipeContext->StreamConfigured == FALSE) + { + streamPipeHandle = NULL; + goto End; + } + + pStreamInfo = &pipeContext->StreamInfo; + + if (pStreamInfo->NumberOfStreams == 0 || + pStreamInfo->StreamList == NULL) + { + streamPipeHandle = NULL; + goto End; + } + + // + // Specify one associate stream's PipeHandle as the super speed bulk endpoint's PipeHandle + // + index = 1; + + streamPipeHandle = pStreamInfo->StreamList[index].PipeHandle; + +End: + return streamPipeHandle; + +} + +VOID +ConfigureStreamPipeHandleForRequest( + _In_ WDFREQUEST Request, + _In_ WDFUSBPIPE Pipe + ) +/*++ + +Routine Description: + + The framework has formated request for super speed bulk pipe. + For stream transfer, use the associated stream's PipeHandle for transfer. + +Arguments: + + Request - Read/Write Request. + + Pipe - Bullk Pipe + +Return Value: + + NULL + +--*/ +{ + PIRP irp; + PIO_STACK_LOCATION irpSp; + PURB urb; + + // + // Get the IRP that is associated with the framework request object. + // + irp = WdfRequestWdmGetIrp(Request); + + // + // Obtain the next-lower driver's I/O stack location in the IRP + // + irpSp = IoGetNextIrpStackLocation(irp); + + // + // The framework uses pipe's Pipehandle for data transfer by default. + // For stream transfer, we should use the associated stream's PipeHandle of + // the super speed bulk pipe for transfer. Replace the PipeHandle with + // its associated stream's PipeHandle . + // + urb = irpSp->Parameters.Others.Argument1; + urb->UrbBulkOrInterruptTransfer.PipeHandle = GetStreamPipeHandleFromBulkPipe(Pipe); + +} + + +#endif + +ULONG +GetMaxTransferSize( + _In_ WDFUSBPIPE Pipe, + _In_ PDEVICE_CONTEXT DeviceContext +) +/*++ + +Routine Description: + + This routine returns maximum packet size of a bulk pipe + +Arguments: + + Pipe - Bullk Pipe + +Return Value: + + Maximum Packet Size + +--*/ +{ + ULONG maxTransferSize; + PPIPE_CONTEXT pipeContext; + + pipeContext = GetPipeContext(Pipe); + + if (pipeContext->StreamConfigured == TRUE) { + + // + // For super speed bulk stream pipe, the max transfer size is + // MAX_STREAM_VALID_PACKET_SIZE which depends on the implementation + // of super speed bulk stream endpoint + // + maxTransferSize = MAX_STREAM_VALID_PACKET_SIZE; + + } + else{ + if (DeviceContext->IsDeviceSuperSpeed == TRUE) + { + maxTransferSize = MAX_SUPER_SPEED_TRANSFER_SIZE; + } + else if (DeviceContext->IsDeviceHighSpeed == TRUE) + { + maxTransferSize = MAX_HIGH_SPEED_TRANSFER_SIZE; + } + else + { + maxTransferSize = MAX_FULL_SPEED_TRANSFER_SIZE; + } + + } + + return maxTransferSize; +} + + + + + + + diff --git a/usb/usbsamp/sys/usbsamp.rc b/usb/usbsamp/sys/usbsamp.rc new file mode 100644 index 000000000..c0861673f --- /dev/null +++ b/usb/usbsamp/sys/usbsamp.rc @@ -0,0 +1,12 @@ +#include + +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "Framework Sample for Intel 82930 USB Test Board" +#define VER_INTERNALNAME_STR "usbsamp.sys" +#define VER_ORIGINALFILENAME_STR "usbsamp.sys" + +#include "common.ver" + diff --git a/usb/usbsamp/usbsamp.htm b/usb/usbsamp/usbsamp.htm new file mode 100644 index 000000000..015e7f288 --- /dev/null +++ b/usb/usbsamp/usbsamp.htm @@ -0,0 +1,92 @@ + + + + +Bulk Usb + + + +

USBSAMP

+
+ +

+

Summary

+ + +

The USBSAMP sample demonstrates how to perform full and high speed bulk and isochronous, and super speed bulk stream data transfers to a generic USB device using Windows Kernel Mode Driver Framework (KMDF). Super speed bulk transfer only works on Microsoft USB 3.0 stack. The sample also contains a console test application to initiate bulk, bulk stream & isochronouse transfers and +obtain information on the device's I/O endpoints. The application also +demonstrates how to use GUID-based device names and pipe names generated by the +operating system using the SetupDiXXX user-mode APIs.

+ + +

Building the sample

+To build the sample driver and exe, you must first set up the DDK build environment on your host machine. The “Installation and Release Notes” in the Windows DDK has a complete description on how to do this. + +

    + + +
  1. + Run the build –ceZ command in the usbsamp directory to build usbsamp.sys and usbsamp.exe. + + + +
  2. + Copy KMDF coinstaller (wdfcoinstallerMMmmm.dll), usbsamp.sys, usbsamp.exe, and usbsamp.inf to a floppy disk or a temporary directory on the target system. +
+

+ + +

Installing the Driver

+ + Make sure your device has been programmed with the device VID/PID in the + USBSAMP.inf file. If not, edit the device VID, PID, and description text in the INF to + match your test board/device.

+ After you plug the device: +

On Windows 7 and later:

+
    +
  • Bring up the device manager, find the device, right click on the device to "Update Driver Software.." +
  • Choose the "Browse my computer for driver software" option +
  • Click on "Let me pick from a list of device drivers on my computer" +
  • In the "Select your device's type from the list below", pick the "Show All Devices" +
  • Click on "Have Disk" and specify the target media or the directory where the INF and SYS files are copied and then hit the "Next" button. +
  • The system will scan the directory and pick up the matching INF and start the installation. You will get a Hardware Installation Warning dialog stating that your driver has not passed Windows Logo Testing. Hit "Continue Anyway" button. +
  • The system will copy the KMDF coinstaller (wdfcoinstallerMMmmm.dll), driver, INF file, load the driver and start the device. If every thing goes fine, you will get "Completing the Found New Hardware Wizard". Hit the "Finish" button and the installation is complete. +
  • You should be able to see the device in the Device manager under "Samples" device type +
+ + +

Testing

+ +You can use usbsamp.exe to test the device. This console application enumerates the interface registered by the driver and opens the device to send read, write or ioctl requests based on the command line options. +

+usbsamp.exe -u <-- Will dump all the descriptors and endpoint information. Always use it to dump the endpoint information to get correct pipe number for read and write +

+

+usbsamp.exe -r 1024 -w 1024 -c 100 -v <-- This command first writes 1024 bytes of data to bulk out endpoint (pipe1), and then reads 1024 bytes from bulk in endpoint (pipe0), and compares the read buffer with write buffer to see if they match. If the buffer contents match, it performs this operation 100 times. +

+

+To read and write to bulk endpoints you can run one or more of these commands simultaneously. When it reads or writes to super speed bulk endpoints with streams, the sample driver always uses the first underlying stream in that super speed bulk endpoints. The driver is multi-thread safe so it can handle multiple request at a time. +

+usbsamp.exe -r 65536 <--- by default, the device reads pipe0 +

+usbsamp.exe -w 65536 <--- by default, the device write to pipe1 +

+usbsamp.exe -r 65536 -i pipe02 <--- read 65536 bytes from pipe02 +

+usbsamp.exe -w 65536 -o pipe03 <--- write 65536 bytes to pipe03 + +

+To read and write to Isochronous endpoints you can run one or more of these commands simultaneously. +

+usbsamp.exe -r 510 -i pipe04 +

+usbsamp.exe -w 510 -o pipe05 +

+usbsamp.exe -w 1024 -o pipe05 -r 1024 -i pipe04 -c 100 -v + +

+When run with "-x" option for read and write, it skips validation of read and write data. + + \ No newline at end of file diff --git a/usb/usbsamp/usbsamp.sln b/usb/usbsamp/usbsamp.sln new file mode 100644 index 000000000..8be6bd1af --- /dev/null +++ b/usb/usbsamp/usbsamp.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", "{49A076C1-4A83-4A1D-9D0B-71543A6621D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Driver", "Driver", "{B3E94C51-B2D9-4787-95EF-4E9DFDEE5A6E}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sys", "Sys", "{4305B583-BDD8-4C8B-BA67-EAD701F97A92}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "usbsamp", "exe\usbsamp.vcxproj", "{82B15C8E-818A-41C0-AE62-1AEA7270E8B6}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "usbsamp", "sys\driver\usbsamp.vcxproj", "{6395CFC7-A0E1-40D8-A744-D1DC7B6513BA}" +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 + {82B15C8E-818A-41C0-AE62-1AEA7270E8B6}.Debug|Win32.ActiveCfg = Debug|Win32 + {82B15C8E-818A-41C0-AE62-1AEA7270E8B6}.Debug|Win32.Build.0 = Debug|Win32 + {82B15C8E-818A-41C0-AE62-1AEA7270E8B6}.Release|Win32.ActiveCfg = Release|Win32 + {82B15C8E-818A-41C0-AE62-1AEA7270E8B6}.Release|Win32.Build.0 = Release|Win32 + {82B15C8E-818A-41C0-AE62-1AEA7270E8B6}.Debug|x64.ActiveCfg = Debug|x64 + {82B15C8E-818A-41C0-AE62-1AEA7270E8B6}.Debug|x64.Build.0 = Debug|x64 + {82B15C8E-818A-41C0-AE62-1AEA7270E8B6}.Release|x64.ActiveCfg = Release|x64 + {82B15C8E-818A-41C0-AE62-1AEA7270E8B6}.Release|x64.Build.0 = Release|x64 + {6395CFC7-A0E1-40D8-A744-D1DC7B6513BA}.Debug|Win32.ActiveCfg = Debug|Win32 + {6395CFC7-A0E1-40D8-A744-D1DC7B6513BA}.Debug|Win32.Build.0 = Debug|Win32 + {6395CFC7-A0E1-40D8-A744-D1DC7B6513BA}.Release|Win32.ActiveCfg = Release|Win32 + {6395CFC7-A0E1-40D8-A744-D1DC7B6513BA}.Release|Win32.Build.0 = Release|Win32 + {6395CFC7-A0E1-40D8-A744-D1DC7B6513BA}.Debug|x64.ActiveCfg = Debug|x64 + {6395CFC7-A0E1-40D8-A744-D1DC7B6513BA}.Debug|x64.Build.0 = Debug|x64 + {6395CFC7-A0E1-40D8-A744-D1DC7B6513BA}.Release|x64.ActiveCfg = Release|x64 + {6395CFC7-A0E1-40D8-A744-D1DC7B6513BA}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {82B15C8E-818A-41C0-AE62-1AEA7270E8B6} = {49A076C1-4A83-4A1D-9D0B-71543A6621D0} + {6395CFC7-A0E1-40D8-A744-D1DC7B6513BA} = {B3E94C51-B2D9-4787-95EF-4E9DFDEE5A6E} + {B3E94C51-B2D9-4787-95EF-4E9DFDEE5A6E} = {4305B583-BDD8-4C8B-BA67-EAD701F97A92} + EndGlobalSection +EndGlobal diff --git a/usb/usbview/display.c b/usb/usbview/display.c index 67c215e42..9ee63a14b 100644 --- a/usb/usbview/display.c +++ b/usb/usbview/display.c @@ -213,9 +213,18 @@ DisplayConfigDesc ( VOID DisplayBosDescriptor ( - PUSB_BOS_DESCRIPTOR BosDesc + PUSBDEVICEINFO info, + PUSB_BOS_DESCRIPTOR BosDesc, + PSTRING_DESCRIPTOR_NODE StringDescs ); +VOID +DisplayBillboardCapabilityDescriptor ( + PUSBDEVICEINFO info, + PUSB_DEVICE_CAPABILITY_BILLBOARD_DESCRIPTOR billboardCapDesc, + PSTRING_DESCRIPTOR_NODE StringDescs +); + VOID DisplayDeviceQualifierDescriptor ( PUSB_DEVICE_QUALIFIER_DESCRIPTOR DevQualDesc @@ -926,7 +935,9 @@ UpdateTreeItemDeviceInfo( if (BosDesc) { - DisplayBosDescriptor((PUSB_BOS_DESCRIPTOR)(BosDesc + 1)); + DisplayBosDescriptor((PUSBDEVICEINFO) info, + (PUSB_BOS_DESCRIPTOR) (BosDesc + 1), + StringDescs); } } @@ -1535,6 +1546,15 @@ DisplayConnectionInfo ( else {AppendTextBuffer("\r\n");} break; + case USB_DEVICE_CLASS_BILLBOARD: + tog = 0; + if (gDoAnnotation) + { + AppendTextBuffer(" -> This is a billboard class device\r\n"); + } + else { AppendTextBuffer("\r\n"); } + break; + case USB_MISCELLANEOUS_DEVICE: tog = 0; //@@TestCase A1.3 @@ -2381,7 +2401,12 @@ DisplayDeviceQualifierDescriptor ( AppendTextBuffer(" -> This is a Vendor Specific Device\r\n"); } break; - + case USB_DEVICE_CLASS_BILLBOARD: + if (gDoAnnotation) + { + AppendTextBuffer(" -> This is a billboard class device\r\n"); + } + break; default: //@@TestCase A3.1 //@@ERROR @@ -2675,6 +2700,131 @@ DisplayContainerIdCapabilityExtensionDescriptor ( pGuid->Data4[7]); } +VOID +DisplayBillboardCapabilityDescriptor ( + PUSBDEVICEINFO info, + PUSB_DEVICE_CAPABILITY_BILLBOARD_DESCRIPTOR billboardCapDesc, + PSTRING_DESCRIPTOR_NODE StringDescs + ) +{ + UCHAR i = 0; + UCHAR bNumAlternateModes = 0; + UCHAR alternateModeConfiguration = 0; + UCHAR adjustedBLength = 0; + + AppendTextBuffer("\r\n ===>Billboard Capability Descriptor<===\r\n"); + + AppendTextBuffer("bLength: 0x%02X", + billboardCapDesc->bLength); + + adjustedBLength = sizeof(USB_DEVICE_CAPABILITY_BILLBOARD_DESCRIPTOR) + + sizeof(billboardCapDesc->AlternateMode[0]) * (billboardCapDesc->bNumberOfAlternateModes - 1); + AppendTextBuffer(" -> Actual Length: 0x%02X\r\n", adjustedBLength); + + AppendTextBuffer("bDescriptorType: 0x%02X\r\n", + billboardCapDesc->bDescriptorType); + AppendTextBuffer("bDevCapabilityType: 0x%02X -> Billboard capability\r\n", + billboardCapDesc->bDevCapabilityType); + AppendTextBuffer("iAdditionalInfoURL: 0x%02X ->", + billboardCapDesc->iAddtionalInfoURL); + if (billboardCapDesc->iAddtionalInfoURL && gDoAnnotation) { + DisplayStringDescriptor(billboardCapDesc->iAddtionalInfoURL, + StringDescs, + info->DeviceInfoNode != NULL ? info->DeviceInfoNode->LatestDevicePowerState : PowerDeviceUnspecified); + } + AppendTextBuffer("bNumberOfAlternateModes: 0x%02X\r\n", + billboardCapDesc->bNumberOfAlternateModes); + + if (billboardCapDesc->bNumberOfAlternateModes > BILLBOARD_MAX_NUM_ALT_MODE) + { + AppendTextBuffer("*!*ERROR: Invalid bNumberofAlternateModes\r\n"); + } + AppendTextBuffer("bPreferredAlternateMode: 0x%02X\r\n", + billboardCapDesc->bPreferredAlternateMode); + + AppendTextBuffer("VCONN Power: 0x%04X", + billboardCapDesc->VconnPower); + + if (billboardCapDesc->VconnPower.NoVconnPowerRequired) + { + AppendTextBuffer(" -> The adapter does not require Vconn Power. Bits 2..0 ignored\r\n"); + } + else + { + switch (billboardCapDesc->VconnPower.VConnPowerNeededForFullFunctionality) + { + case 0: + AppendTextBuffer(" -> 1W needed by adapter for full functionality\r\n"); + break; + case 1: + AppendTextBuffer(" -> 1.5W needed by adapter for full functionality\r\n"); + break; + case 7: + AppendTextBuffer(" -> *!*ERROR: VConnPowerNeededForFullFunctionality - Reserved value being used\r\n"); + break; + default: + AppendTextBuffer(" -> %2XW needed by adapter for full functionality\r\n", billboardCapDesc->VconnPower.VConnPowerNeededForFullFunctionality); + } + } + + if (billboardCapDesc->VconnPower.Reserved) + { + AppendTextBuffer("*!*ERROR: Reserved bits in VCONN Power being used\r\n"); + } + if (billboardCapDesc->bReserved) + { + AppendTextBuffer("*!*ERROR: bReserved being used\r\n"); + } + + + bNumAlternateModes = billboardCapDesc->bNumberOfAlternateModes; + if (bNumAlternateModes > BILLBOARD_MAX_NUM_ALT_MODE) + { + bNumAlternateModes = BILLBOARD_MAX_NUM_ALT_MODE; + } + if (bNumAlternateModes > 0) + { + AppendTextBuffer("\r\nAlternate Modes Identified:\r\n"); + } + for (i = 0; i < bNumAlternateModes; i++) + { + alternateModeConfiguration = ((billboardCapDesc->bmConfigured[i / 4]) >> ((i % 4) * 2)) & 0x3; + AppendTextBuffer("wSVID - 0x%04X bAlternateMode - 0x%02X ->", + billboardCapDesc->AlternateMode[i].wSVID, + billboardCapDesc->AlternateMode[i].bAlternateMode, + billboardCapDesc->AlternateMode[i].iAlternateModeSetting); + + switch (alternateModeConfiguration) + { + case 0: + AppendTextBuffer("Unspecified Error\r\n"); + break; + case 1: + AppendTextBuffer("Alternate Mode configuration not attempted\r\n"); + break; + case 2: + AppendTextBuffer("Alternate Mode configuration attempted but unsuccessful\r\n"); + break; + case 3: + AppendTextBuffer("Alternate Mode configuration successful\r\n"); + break; + } + AppendTextBuffer("iAlternateModeString - 0x%02X ", billboardCapDesc->AlternateMode[i].iAlternateModeSetting); + if (billboardCapDesc->AlternateMode[i].iAlternateModeSetting && gDoAnnotation) + { + DisplayStringDescriptor(billboardCapDesc->AlternateMode[i].iAlternateModeSetting, + StringDescs, + info->DeviceInfoNode != NULL ? info->DeviceInfoNode->LatestDevicePowerState : PowerDeviceUnspecified); + } + else + { + AppendTextBuffer("\r\n"); + } + AppendTextBuffer("\r\n"); + } +} + + /***************************************************************************** DisplayBosDescriptor() @@ -2685,7 +2835,9 @@ BosDesc - The Binary Object Store (BOS) Descriptor, and associated Descriptors VOID DisplayBosDescriptor ( - PUSB_BOS_DESCRIPTOR BosDesc + PUSBDEVICEINFO info, + PUSB_BOS_DESCRIPTOR BosDesc, + PSTRING_DESCRIPTOR_NODE StringDescs ) { PUSB_COMMON_DESCRIPTOR commonDesc = NULL; @@ -2726,6 +2878,9 @@ DisplayBosDescriptor ( case USB_DEVICE_CAPABILITY_CONTAINER_ID: DisplayContainerIdCapabilityExtensionDescriptor((PUSB_DEVICE_CAPABILITY_CONTAINER_ID_DESCRIPTOR)capDesc); break; + case USB_DEVICE_CAPABILITY_BILLBOARD: + DisplayBillboardCapabilityDescriptor((PUSBDEVICEINFO) info, (PUSB_DEVICE_CAPABILITY_BILLBOARD_DESCRIPTOR) capDesc, StringDescs); + break; default: AppendTextBuffer("\r\n ===>Unknown Capability Descriptor<===\r\n"); @@ -3183,6 +3338,19 @@ DisplayInterfaceDescriptor ( AppendTextBuffer("bInterfaceSubClass: 0x%02X\r\n", InterfaceDesc->bInterfaceSubClass); break; + case USB_DEVICE_CLASS_BILLBOARD: + AppendTextBuffer(" -> Billboard Class\r\n"); + AppendTextBuffer("bInterfaceSubClass: 0x%02X", InterfaceDesc->bInterfaceSubClass); + switch (InterfaceDesc->bInterfaceSubClass) + { + case 0: + AppendTextBuffer(" -> Billboard Subclass\r\n"); + break; + default: + AppendTextBuffer("\r\n*!*CAUTION: This appears to be an invalid bInterfaceSubClass\r\n"); + break; + } + break; default: if(gDoAnnotation) diff --git a/usb/usbview/usbdesc.h b/usb/usbview/usbdesc.h index 5b2622a29..23420cfe2 100644 --- a/usb/usbview/usbdesc.h +++ b/usb/usbview/usbdesc.h @@ -34,6 +34,7 @@ Revision History: #define USB_INTERFACE_CLASS_DEVICE 0x00 #define USB_COMMUNICATION_DEVICE 0x02 #define USB_HUB_DEVICE 0x09 +#define USB_DEVICE_CLASS_BILLBOARD 0x11 #define USB_DIAGNOSTIC_DEVICE 0xDC #define USB_WIRELESS_CONTROLLER_DEVICE 0xE0 #define USB_MISCELLANEOUS_DEVICE 0xEF @@ -69,6 +70,11 @@ Revision History: // #define USB_IAD_PROTOCOL 0x01 +// +//Device class specific values +// +#define BILLBOARD_MAX_NUM_ALT_MODE 0x34 + // //USB 2.0 Specification Changes - New Descriptors // diff --git a/usb/usbview/usbview.sln b/usb/usbview/usbview.sln index d06846d8e..5993209e5 100644 --- a/usb/usbview/usbview.sln +++ b/usb/usbview/usbview.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0 MinimumVisualStudioVersion = 12.0 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "usbview", "usbview.vcxproj", "{53F7289B-2210-4DCD-9A41-D83DE76A7372}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "usbview", "usbview.vcxproj", "{3C291787-8A37-4E8C-86D5-0B739A1CEA74}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {53F7289B-2210-4DCD-9A41-D83DE76A7372}.Debug|Win32.ActiveCfg = Debug|Win32 - {53F7289B-2210-4DCD-9A41-D83DE76A7372}.Debug|Win32.Build.0 = Debug|Win32 - {53F7289B-2210-4DCD-9A41-D83DE76A7372}.Release|Win32.ActiveCfg = Release|Win32 - {53F7289B-2210-4DCD-9A41-D83DE76A7372}.Release|Win32.Build.0 = Release|Win32 - {53F7289B-2210-4DCD-9A41-D83DE76A7372}.Debug|x64.ActiveCfg = Debug|x64 - {53F7289B-2210-4DCD-9A41-D83DE76A7372}.Debug|x64.Build.0 = Debug|x64 - {53F7289B-2210-4DCD-9A41-D83DE76A7372}.Release|x64.ActiveCfg = Release|x64 - {53F7289B-2210-4DCD-9A41-D83DE76A7372}.Release|x64.Build.0 = Release|x64 + {3C291787-8A37-4E8C-86D5-0B739A1CEA74}.Debug|Win32.ActiveCfg = Debug|Win32 + {3C291787-8A37-4E8C-86D5-0B739A1CEA74}.Debug|Win32.Build.0 = Debug|Win32 + {3C291787-8A37-4E8C-86D5-0B739A1CEA74}.Release|Win32.ActiveCfg = Release|Win32 + {3C291787-8A37-4E8C-86D5-0B739A1CEA74}.Release|Win32.Build.0 = Release|Win32 + {3C291787-8A37-4E8C-86D5-0B739A1CEA74}.Debug|x64.ActiveCfg = Debug|x64 + {3C291787-8A37-4E8C-86D5-0B739A1CEA74}.Debug|x64.Build.0 = Debug|x64 + {3C291787-8A37-4E8C-86D5-0B739A1CEA74}.Release|x64.ActiveCfg = Release|x64 + {3C291787-8A37-4E8C-86D5-0B739A1CEA74}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/usb/usbview/usbview.vcxproj b/usb/usbview/usbview.vcxproj index 2b1fe15d4..bccb6d76d 100644 --- a/usb/usbview/usbview.vcxproj +++ b/usb/usbview/usbview.vcxproj @@ -19,11 +19,11 @@ - {53F7289B-2210-4DCD-9A41-D83DE76A7372} + {3C291787-8A37-4E8C-86D5-0B739A1CEA74} $(MSBuildProjectName) Debug Win32 - {5310F3E1-F715-4C85-8EEE-BD30F52FD176} + {0B5874D8-CA5A-4459-9471-A4D9CBD82977} @@ -168,7 +168,6 @@ - diff --git a/usb/usbview/usbview.vcxproj.Filters b/usb/usbview/usbview.vcxproj.Filters index 0688fd3e8..ee2d7801a 100644 --- a/usb/usbview/usbview.vcxproj.Filters +++ b/usb/usbview/usbview.vcxproj.Filters @@ -3,15 +3,15 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {902F6120-4AA1-4898-A1FD-6A44ADCFA148} + {24B15DD5-41FB-4742-A3C7-A074316B3FFD} h;hpp;hxx;hm;inl;inc;xsd - {B764DF51-5FA0-4F05-B3AE-E7ABC15ABE89} + {52B35399-50CE-420D-B0C6-D6D79A82EF43} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {1B5253D4-F3D5-4321-9E40-0E3E74955606} + {5DBEED0A-531F-4DD9-8C19-89D7EF22449E} diff --git a/usb/usbview/xmlhelper.cpp b/usb/usbview/xmlhelper.cpp index 4221aeb08..cf39a87de 100644 --- a/usb/usbview/xmlhelper.cpp +++ b/usb/usbview/xmlhelper.cpp @@ -200,7 +200,10 @@ array < UsbDeviceConfigurationType ^> ^ XmlGetConfigDescriptors( PUSBDEVICEINFO deviceInfo, PUSB_CONFIGURATION_DESCRIPTOR configDescs, PSTRING_DESCRIPTOR_NODE stringDesc); -UsbBosDescriptorType ^ XmlGetBosDescriptor(PUSB_BOS_DESCRIPTOR bosDesc); +UsbBosDescriptorType ^ XmlGetBosDescriptor( + PUSB_BOS_DESCRIPTOR bosDesc, + PSTRING_DESCRIPTOR_NODE stringDesc + ); UsbDeviceClassType ^ XmlGetDeviceClass(UCHAR deviceClass, UCHAR deviceSubClass, UCHAR deviceProtocol); UsbDeviceUnknownDescriptorType ^ XmlGetUnknownDescriptor( PUSB_COMMON_DESCRIPTOR unknownDesc @@ -214,6 +217,10 @@ UsbSuperSpeedExtensionDescriptorType ^ XmlGetSuperSpeedCapabilityExtensionDescri UsbDispContIdCapExtDescriptorType ^ XmlGetContainerIdCapabilityExtensionDescriptor( PUSB_DEVICE_CAPABILITY_CONTAINER_ID_DESCRIPTOR capDesc ); +UsbBillboardCapabilityDescriptorType ^ XmlGetBillboardCapabilityDescriptor( + PUSB_DEVICE_CAPABILITY_BILLBOARD_DESCRIPTOR capDesc, + PSTRING_DESCRIPTOR_NODE stringDesc + ); /***************************************************************************** D E F I N I T I O N S @@ -1525,7 +1532,18 @@ UsbDeviceClassType ^ XmlGetDeviceClass(UCHAR bInterfaceClass, UCHAR bInterfaceSu deviceSubclass += bInterfaceSubclass; } break; - + case USB_DEVICE_CLASS_BILLBOARD: + deviceClass = gcnew String("Billboard Class"); + switch (bInterfaceSubclass) + { + case 0: + deviceSubclass = gcnew String("Billboard Subclass"); + break; + default: + deviceSubclass = gcnew String("CAUTION: This appears to be an invalid bInterfaceSubClass"); + break; + } + break; default: deviceClass = gcnew String("Interface Class unknown : "); @@ -2118,6 +2136,10 @@ String ^ XmlGetDeviceClassString(UCHAR deviceClass) deviceClassStr = gcnew String("Vendor specific device"); break; + case USB_DEVICE_CLASS_BILLBOARD: + deviceClassStr = gcnew String("Billboard class device"); + break; + default: deviceClassStr= gcnew String("ERROR: unknown bDeviceClass" + deviceClass); break; @@ -2186,6 +2208,13 @@ bool XmlAddDeviceClassDetails( { deviceDetails->DeviceType = XmlGetDeviceClassString(connectionInfo->DeviceDescriptor.bDeviceClass); + if (connectionInfo->DeviceDescriptor.bDeviceClass == USB_DEVICE_CLASS_BILLBOARD && + (connectionInfo->DeviceDescriptor.bDeviceSubClass != 0x0 || + connectionInfo->DeviceDescriptor.bDeviceProtocol != 0x0)) + { + deviceDetails->DeviceTypeError = gcnew String("ERROR: Billboard device has invalid bDeviceSubclass/bDeviceProtocol"); + } + if (connectionInfo->DeviceDescriptor.bDeviceClass == USB_MISCELLANEOUS_DEVICE) { deviceDetails->DeviceTypeError = gcnew String("ERROR: Multi-interface Function code " + @@ -2443,7 +2472,7 @@ HRESULT XmlAddExternalHub(PSTR ehName, PUSBEXTERNALHUBINFO ehInfo) // Add BOS descriptor if (NULL != ehInfo->BosDesc) { - exHub->BosDescriptor = XmlGetBosDescriptor((PUSB_BOS_DESCRIPTOR) (ehInfo->BosDesc + 1)); + exHub->BosDescriptor = XmlGetBosDescriptor((PUSB_BOS_DESCRIPTOR) (ehInfo->BosDesc + 1), ehInfo->StringDescs); } gXmlStack->Push(exHub); @@ -2461,7 +2490,10 @@ HRESULT XmlAddExternalHub(PSTR ehName, PUSBEXTERNALHUBINFO ehInfo) Gets the Bos descriptor object for given BOS descriptor *****************************************************************************/ -UsbBosDescriptorType ^ XmlGetBosDescriptor(PUSB_BOS_DESCRIPTOR bosDesc) +UsbBosDescriptorType ^ XmlGetBosDescriptor( + PUSB_BOS_DESCRIPTOR bosDesc, + PSTRING_DESCRIPTOR_NODE stringDesc + ) { PUSB_COMMON_DESCRIPTOR commonDesc = NULL; PUSB_DEVICE_CAPABILITY_DESCRIPTOR capDesc = NULL; @@ -2470,6 +2502,7 @@ UsbBosDescriptorType ^ XmlGetBosDescriptor(PUSB_BOS_DESCRIPTOR bosDesc) ArrayList ^usbSuperSpeedExtDescList = gcnew ArrayList(); ArrayList ^usbContIdCapExtDescList = gcnew ArrayList(); ArrayList ^usbUnknownDescList = gcnew ArrayList(); + ArrayList ^usbBillboardDescList = gcnew ArrayList(); if(NULL == bosDesc) { @@ -2517,6 +2550,14 @@ UsbBosDescriptorType ^ XmlGetBosDescriptor(PUSB_BOS_DESCRIPTOR bosDesc) ) ); break; + case USB_DEVICE_CAPABILITY_BILLBOARD: + usbBillboardDescList->Add( + XmlGetBillboardCapabilityDescriptor( + (PUSB_DEVICE_CAPABILITY_BILLBOARD_DESCRIPTOR) capDesc, + stringDesc + ) + ); + break; default: usbUnknownDescList->Add( XmlGetUnknownDescriptor( @@ -2545,6 +2586,9 @@ UsbBosDescriptorType ^ XmlGetBosDescriptor(PUSB_BOS_DESCRIPTOR bosDesc) bosXmlDesc->UsbDispContIdCapExtDescriptor = reinterpret_cast^> ( usbContIdCapExtDescList->ToArray(UsbDispContIdCapExtDescriptorType::typeid) ); + bosXmlDesc->UsbBillboardCapabilityDescriptor = reinterpret_cast^> ( + usbBillboardDescList->ToArray(UsbBillboardCapabilityDescriptorType::typeid) + ); return bosXmlDesc; } @@ -2733,6 +2777,125 @@ UsbDispContIdCapExtDescriptorType ^ XmlGetContainerIdCapabilityExtensionDescript return capXmlDesc; } + +/***************************************************************************** + +XmlGetBillboardCapabilityDescriptor() + +Gets a billboard capability descriptor from a given descriptor +*****************************************************************************/ +UsbBillboardCapabilityDescriptorType ^ XmlGetBillboardCapabilityDescriptor( + PUSB_DEVICE_CAPABILITY_BILLBOARD_DESCRIPTOR capDesc, + PSTRING_DESCRIPTOR_NODE stringDesc + ) +{ + UCHAR i = 0; + UCHAR bNumAlternateModes = 0; + UCHAR alternateModeConfiguration = 0; + UsbBillboardCapabilityDescriptorType ^ capXmlDesc = nullptr; + UsbBillboardSVIDType ^ svidXmlDesc = nullptr; + ArrayList ^ usbSVIDDescList = gcnew ArrayList(); + + + if (NULL == capDesc) + { + return nullptr; + } + + capXmlDesc = gcnew UsbBillboardCapabilityDescriptorType(); + + + capXmlDesc->BLength = capDesc->bLength; + capXmlDesc->BDescriptorType = capDesc->bDescriptorType; + capXmlDesc->BDevCapabilityType = capDesc->bDevCapabilityType; + capXmlDesc->IAddtionalInfoURL = capDesc->iAddtionalInfoURL; + capXmlDesc->BNumberOfAlternateModes = capDesc->bNumberOfAlternateModes; + capXmlDesc->BPreferredAlternateMode = capDesc->bPreferredAlternateMode; + capXmlDesc->CalculatedBLength = sizeof(USB_DEVICE_CAPABILITY_BILLBOARD_DESCRIPTOR) + + sizeof(capDesc->AlternateMode[0]) * (capDesc->bNumberOfAlternateModes - 1); + capXmlDesc->BillboardDescriptorErrors = gcnew String(""); + capXmlDesc->AddtionalInfoURL = XmlGetStringDescriptor( + capDesc->iAddtionalInfoURL, + stringDesc, + false + ); + + if (capDesc->VconnPower.NoVconnPowerRequired) + { + capXmlDesc->VConnPower = gcnew String("The adapter does not require Vconn Power. Bits 2..0 ignored"); + } + else + { + switch (capDesc->VconnPower.VConnPowerNeededForFullFunctionality) + { + case 0: + capXmlDesc->VConnPower = gcnew String("1W needed by adapter for full functionality"); + break; + case 1: + capXmlDesc->VConnPower = gcnew String("1.5W needed by adapter for full functionality"); + break; + case 7: + capXmlDesc->BillboardDescriptorErrors += "ERROR: VConnPowerNeededForFullFunctionality - Reserved value being used"; + break; + default: + capXmlDesc->VConnPower = gcnew String(String::Format("{0} W needed by adapter for full functionality", capDesc->VconnPower.VConnPowerNeededForFullFunctionality)); + } + } + + if (capDesc->bNumberOfAlternateModes > BILLBOARD_MAX_NUM_ALT_MODE) + { + capXmlDesc->BillboardDescriptorErrors += "ERROR: Invalid bNumberofAlternateModes; "; + } + if (capDesc->VconnPower.Reserved) + { + capXmlDesc->BillboardDescriptorErrors += "ERROR: Reserved bits in VCONN Power being used; "; + } + if (capDesc->bReserved) + { + capXmlDesc->BillboardDescriptorErrors += "ERROR: bReserved being used; "; + } + + bNumAlternateModes = capDesc->bNumberOfAlternateModes; + if (bNumAlternateModes > BILLBOARD_MAX_NUM_ALT_MODE) + { + bNumAlternateModes = BILLBOARD_MAX_NUM_ALT_MODE; + } + for (i = 0; i < bNumAlternateModes; i++) + { + svidXmlDesc = gcnew UsbBillboardSVIDType(); + alternateModeConfiguration = ((capDesc->bmConfigured[i / 4]) >> ((i % 4) * 2)) & 0x3; + svidXmlDesc->WSVID = capDesc->AlternateMode[i].wSVID; + svidXmlDesc->BAlternateMode = capDesc->AlternateMode[i].bAlternateMode; + svidXmlDesc->IAlternateModeString = capDesc->AlternateMode[i].iAlternateModeSetting; + svidXmlDesc->AlternateModeString = XmlGetStringDescriptor( + capDesc->AlternateMode[i].iAlternateModeSetting, + stringDesc, + false + ); + + switch (alternateModeConfiguration) + { + case 0: + svidXmlDesc->Description = gcnew String("Unspecified Error"); + break; + case 1: + svidXmlDesc->Description = gcnew String("Alternate Mode configuration not attempted"); + break; + case 2: + svidXmlDesc->Description = gcnew String("Alternate Mode configuration attempted but unsuccessful"); + break; + case 3: + svidXmlDesc->Description = gcnew String("Alternate Mode configuration successful"); + break; + } + usbSVIDDescList->Add(svidXmlDesc); + } + capXmlDesc->UsbBillboardSVID = reinterpret_cast^> ( + usbSVIDDescList->ToArray(UsbBillboardSVIDType::typeid)); + + return capXmlDesc; +} + /***************************************************************************** XmlAddUsbDevice() @@ -2852,7 +3015,10 @@ HRESULT XmlAddUsbDevice(PSTR devName, PUSBDEVICEINFO deviceInfo) // Add BOS descriptor if (NULL != deviceInfo->BosDesc) { - usbDevice->BosDescriptor = XmlGetBosDescriptor((PUSB_BOS_DESCRIPTOR) (deviceInfo->BosDesc + 1)); + usbDevice->BosDescriptor = XmlGetBosDescriptor( + (PUSB_BOS_DESCRIPTOR) (deviceInfo->BosDesc + 1), + deviceInfo->StringDescs + ); } } else diff --git a/video/KMDOD/KMDOD.sln b/video/KMDOD/KMDOD.sln new file mode 100644 index 000000000..bede075ff --- /dev/null +++ b/video/KMDOD/KMDOD.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}") = "SampleDisplay", "Sample\SampleDisplay.vcxproj", "{667E9655-203F-4300-A246-8D5E88D7B571}" +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 + {667E9655-203F-4300-A246-8D5E88D7B571}.Debug|Win32.ActiveCfg = Debug|Win32 + {667E9655-203F-4300-A246-8D5E88D7B571}.Debug|Win32.Build.0 = Debug|Win32 + {667E9655-203F-4300-A246-8D5E88D7B571}.Release|Win32.ActiveCfg = Release|Win32 + {667E9655-203F-4300-A246-8D5E88D7B571}.Release|Win32.Build.0 = Release|Win32 + {667E9655-203F-4300-A246-8D5E88D7B571}.Debug|x64.ActiveCfg = Debug|x64 + {667E9655-203F-4300-A246-8D5E88D7B571}.Debug|x64.Build.0 = Debug|x64 + {667E9655-203F-4300-A246-8D5E88D7B571}.Release|x64.ActiveCfg = Release|x64 + {667E9655-203F-4300-A246-8D5E88D7B571}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/video/KMDOD/ReadMe.md b/video/KMDOD/ReadMe.md new file mode 100644 index 000000000..232a8f652 --- /dev/null +++ b/video/KMDOD/ReadMe.md @@ -0,0 +1,69 @@ +Kernel mode display-only miniport driver (KMDOD) sample +======================================================= + +The kernel mode display-only miniport driver (KMDOD) sample implements most of the device driver interfaces (DDIs) that a display-only miniport driver should provide to the Windows Display Driver Model (WDDM). The code is useful to understand how to write a miniport driver for a display-only device, or how to develop a full WDDM driver. + +For more info on how a KMDOD works, see [Kernel Mode Display-Only Driver (KMDOD) Interface](http://msdn.microsoft.com/en-us/library/windows/hardware/jj673962). For more info on WDDM drivers, see [Windows Vista Display Driver Model (WDDM) Design Guide](http://msdn.microsoft.com/en-us/library/windows/hardware/ff570593). + +This code can also help you to understand the use and implementation of display-related DDIs. The INF file shows how to make a display miniport driver visible to other WDDM components. + +The sample can be installed on top of a VESA-capable graphics adapter, or on top of a graphics device that supports access to frame buffer memory through the Unified Extensible Firmware Interface (UEFI). + +The sample driver does not support the *sleep* power state. If it is placed in the sleep state, the driver will cause a system bugcheck to occur. There is no workaround available, by design. + +If the current display driver is not a WDDM 1.2 compliant driver, the sample driver might fail to install, with error code 43 displayed. The KMDOD driver is actually installed, but it cannot be started. The workaround for this issue is to switch to the Microsoft Basic Display Adapter Driver before installing the KMDOD sample driver, or simply to reboot your system after installing the KMDOD sample. + + +Installation +------------ + +In Microsoft Visual Studio, press **F5** to build the sample and then deploy it to a target machine. For more info, see [Deploying a Driver to a Test Computer](http://msdn.microsoft.com/en-us/library/windows/hardware/hh454834). + +In some cases you might need to install the driver manually, as follows. + +1. Add the following files to the directory given by …\\[x64]\\C++\\Package: + - SampleDriver.cat + - SampleDriver.inf + - SampleDriver.sys + - SampleDriver.cer + +2. Unless you’ve provided a production certificate, you should manually install the SampleDriver.cer digital certificate with the following command: + + `Certutil.exe -addstore root SampleDriver.cer` + +3. Then enable test signing by running the following BCDEdit command: + + `Bcdedit.exe -set TESTSIGNING ON` + + **Note**  After you change the TESTSIGNING boot configuration option, restart the computer for the change to take effect. + + For more info, see [The TESTSIGNING Boot Configuration Option](http://msdn.microsoft.com/en-us/library/windows/hardware/ff553484). + +4. Manually install the driver using Device Manager, which is available from Control Panel. + +### ACPI-based GPUs + +To install the KMDOD sample driver on a GPU that is an Advanced Configuration and Power Interface (ACPI) device, add these lines to the `[MS]`, `[MS.NTamd64]`, and `[MS.NTarm]` sections of the Sampledisplay.inf file: + + +++ + + + + + + + + + + +
Text
" Kernel mode display only sample driver " = KDODSamp_Inst, ACPI\CLS_0003&SUBCLS_0000
+" Kernel mode display only sample driver " = KDODSamp_Inst, ACPI\CLS_0003&SUBCLS_0001
+" Kernel mode display only sample driver " = KDODSamp_Inst, ACPI\CLS_0003&SUBCLS_0003
+ +This new code provides generic identifiers for ACPI hardware. + +You can optionally delete the original lines of code within these sections of the INF file. + diff --git a/video/KMDOD/Sample/SampleDisplay.vcxproj b/video/KMDOD/Sample/SampleDisplay.vcxproj new file mode 100644 index 000000000..8bf657355 --- /dev/null +++ b/video/KMDOD/Sample/SampleDisplay.vcxproj @@ -0,0 +1,197 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {667E9655-203F-4300-A246-8D5E88D7B571} + $(MSBuildProjectName) + Debug + Win32 + {AEF8C0EE-3801-44B5-B981-DCE875B88F8D} + + + + 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) + + + + + + + + + + + + + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\displib.lib;$(DDK_LIB_PATH)\ntoskrnl.lib + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(SDK_INC_PATH) + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(SDK_INC_PATH) + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(SDK_INC_PATH) + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\displib.lib;$(DDK_LIB_PATH)\ntoskrnl.lib + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(SDK_INC_PATH) + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(SDK_INC_PATH) + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(SDK_INC_PATH) + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\displib.lib;$(DDK_LIB_PATH)\ntoskrnl.lib + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(SDK_INC_PATH) + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(SDK_INC_PATH) + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(SDK_INC_PATH) + + + + + %(AdditionalDependencies);$(DDK_LIB_PATH)\displib.lib;$(DDK_LIB_PATH)\ntoskrnl.lib + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(SDK_INC_PATH) + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(SDK_INC_PATH) + + + %(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(SDK_INC_PATH) + + + + SampleDisplay + + + SampleDisplay + + + SampleDisplay + + + SampleDisplay + + + + Level4 + + + + + + + Level4 + + + + + + + Level4 + + + + + + + Level4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/video/KMDOD/Sample/SampleDisplay.vcxproj.Filters b/video/KMDOD/Sample/SampleDisplay.vcxproj.Filters new file mode 100644 index 000000000..a3de907d7 --- /dev/null +++ b/video/KMDOD/Sample/SampleDisplay.vcxproj.Filters @@ -0,0 +1,49 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {BA7DDDBD-5A49-4EA0-B805-82253C106295} + + + h;hpp;hxx;hm;inl;inc;xsd + {039EC40D-DBFF-4DF8-9F74-98DFE0EE3548} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {46096B50-FE2E-494E-B601-0BD0DDA73387} + + + inf;inv;inx;mof;mc; + {463B3377-BB9A-4811-965B-E4D368197E71} + + + + + 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/video/KMDOD/Sample/sampledisplay.inf b/video/KMDOD/Sample/sampledisplay.inf new file mode 100644 index 0000000000000000000000000000000000000000..8f666e4d3bb80fa1b7e754544875aef2df7c3a63 GIT binary patch literal 5936 zcmeI0X-^|Z5Qgh>r2K~w3Q-Q@aP8(meDGpJEHEI<=2$jDU>g>37}=0z6XmZbdEPF! z=kOS`DyHh?a-dXk|%sE@HNkzd7ef3di2aQdx5?`0)k!8_R;wauCm^$&04Xq9eg4!P8k)l zdd5<24{0mTU%~qs|1r`kp}!i&s$opu9)+%>Y<7el`fy(dhazkB-q`5`JBwPs1JLwB z+z4SBx3cGee)rHOvv&B!n5>Rv$tOa;0Opiu8$9x)tm&RPhgXdE$aOy0EAkZbIM?mu z_{w=jT_dkaF6n9+60M4n`3%Aqx?X|tgtu0U$K7D0?<_04d_3wf8>}qbD>{$RwQH3S zop(gX^U=4~N0-*}xILi9=ZWSrj77VpyO8eyHM}X$zQw*b>_xsB(^aP4K4Xf^B+NVt zaoDA$)s?W?Hmt_mQ6~1&sNJ)>uu~18)a>K1o_AR@&Ct5VUMW?L{#{0d(=i?p33=VC z8Qnc*X2;Rup6!KoqKHUs=&@6bu0v#9f#3#9B}f*k*Zi;9VOR-T0mAZ6N?3SQ3DJVm zVhg)S4?RovDXo|GqR{`i`n|7o*FCPjGC!wr{p9OwyqS&H_qF!F#n<=yTYJ@$um3;3 z)|vI>@Bfy+r=PR#>2=QLGzV{!7pay}KDkb|to%=V|7&h?XhSYMKQ1RDN`y?Cl!eT`Ofp4cjC4YvQj4@-{EYL4gGp34nD_zfZMYmFXP@sq{+&+)pQ#=iggBALYRS8*5GHX6!5($R zUa-jqT=m@;qsn^o1+k#|WDqh{=ck%umdxTU3!I$#>2GP)RQwb$PPO9os zTy^dv6r*+eD)#l*PO_KRM5@j_He7#=tl*EtzQ{e=3si|;F#k8+$9!o8Yb~Zc@-*Ug z-|fOG7TOPW`VKc2+9Pgw7b#r#hw9sMUel3iN1f#5J_5>k8?xih^KjH8}F!o{!lN8Z*doXfoFE`Pj=sQ&HU6UqiNgE@Y!cQ zw5HH`tyYv(qo!0Ol*{s)nj+1#y*m!d#r{(ebDAG(nky2^p znoT=1QaZ1viW0MQe$MC|+sdGQSe|(Lx!K6jm0L3_Z;c(Ji9d86=ViJ@<@vbtweZ|l nm@D7CLXIB4AB?$)ZXx~2QcmufMLOLoPST}&T5*1qChildUid < MAX_CHILDREN); + + switch (pChildStatus->Type) + { + case StatusConnection: + { + // HpdAwarenessInterruptible was reported since HpdAwarenessNone is deprecated. + // However, BDD has no knowledge of HotPlug events, so just always return connected. + pChildStatus->HotPlug.Connected = IsDriverActive(); + return STATUS_SUCCESS; + } + + case StatusRotation: + { + // D3DKMDT_MOA_NONE was reported, so this should never be called + BDD_LOG_ERROR0("Child status being queried for StatusRotation even though D3DKMDT_MOA_NONE was reported"); + return STATUS_INVALID_PARAMETER; + } + + default: + { + BDD_LOG_WARNING1("Unknown pChildStatus->Type (0x%I64x) requested.", pChildStatus->Type); + return STATUS_NOT_SUPPORTED; + } + } +} + +// EDID retrieval +NTSTATUS BASIC_DISPLAY_DRIVER::QueryDeviceDescriptor(_In_ ULONG ChildUid, + _Inout_ DXGK_DEVICE_DESCRIPTOR* pDeviceDescriptor) +{ + PAGED_CODE(); + + BDD_ASSERT(pDeviceDescriptor != NULL); + BDD_ASSERT(ChildUid < MAX_CHILDREN); + + // If we haven't successfully retrieved an EDID yet (invalid ones are ok, so long as it was retrieved) + if (!m_Flags.EDID_Attempted) + { + GetEdid(ChildUid); + + } + + if (!m_Flags.EDID_Retrieved || !m_Flags.EDID_ValidHeader || !m_Flags.EDID_ValidChecksum) + { + // Report no EDID if a valid one wasn't retrieved + return STATUS_GRAPHICS_CHILD_DESCRIPTOR_NOT_SUPPORTED; + } + else if (pDeviceDescriptor->DescriptorOffset == 0) + { + // Only the base block is supported + RtlCopyMemory(pDeviceDescriptor->DescriptorBuffer, + m_EDIDs[ChildUid], + min(pDeviceDescriptor->DescriptorLength, EDID_V1_BLOCK_SIZE)); + + return STATUS_SUCCESS; + } + else + { + return STATUS_MONITOR_NO_MORE_DESCRIPTOR_DATA; + } +} + +NTSTATUS BASIC_DISPLAY_DRIVER::QueryAdapterInfo(_In_ CONST DXGKARG_QUERYADAPTERINFO* pQueryAdapterInfo) +{ + PAGED_CODE(); + + BDD_ASSERT(pQueryAdapterInfo != NULL); + + switch (pQueryAdapterInfo->Type) + { + case DXGKQAITYPE_DRIVERCAPS: + { + if (pQueryAdapterInfo->OutputDataSize < sizeof(DXGK_DRIVERCAPS)) + { + BDD_LOG_ERROR2("pQueryAdapterInfo->OutputDataSize (0x%I64x) is smaller than sizeof(DXGK_DRIVERCAPS) (0x%I64x)", pQueryAdapterInfo->OutputDataSize, sizeof(DXGK_DRIVERCAPS)); + return STATUS_BUFFER_TOO_SMALL; + } + + DXGK_DRIVERCAPS* pDriverCaps = (DXGK_DRIVERCAPS*)pQueryAdapterInfo->pOutputData; + + // Nearly all fields must be initialized to zero, so zero out to start and then change those that are non-zero. + // Fields are zero since BDD is Display-Only and therefore does not support any of the render related fields. + // It also doesn't support hardware interrupts, gamma ramps, etc. + RtlZeroMemory(pDriverCaps, sizeof(DXGK_DRIVERCAPS)); + + pDriverCaps->WDDMVersion = DXGKDDI_WDDMv1_2; + pDriverCaps->HighestAcceptableAddress.QuadPart = -1; + + pDriverCaps->SupportNonVGA = TRUE; + pDriverCaps->SupportSmoothRotation = TRUE; + + return STATUS_SUCCESS; + } + + case DXGKQAITYPE_DISPLAY_DRIVERCAPS_EXTENSION: + { + DXGK_DISPLAY_DRIVERCAPS_EXTENSION* pDriverDisplayCaps; + + if (pQueryAdapterInfo->OutputDataSize < sizeof(*pDriverDisplayCaps)) + { + BDD_LOG_ERROR2("pQueryAdapterInfo->OutputDataSize (0x%I64x) is smaller than sizeof(DXGK_DISPLAY_DRIVERCAPS_EXTENSION) (0x%I64x)", + pQueryAdapterInfo->OutputDataSize, + sizeof(DXGK_DISPLAY_DRIVERCAPS_EXTENSION)); + + return STATUS_INVALID_PARAMETER; + } + + pDriverDisplayCaps = (DXGK_DISPLAY_DRIVERCAPS_EXTENSION*)pQueryAdapterInfo->pOutputData; + + // Reset all caps values + RtlZeroMemory(pDriverDisplayCaps, pQueryAdapterInfo->OutputDataSize); + + // We claim to support virtual display mode. + pDriverDisplayCaps->VirtualModeSupport = 1; + + return STATUS_SUCCESS; + } + + default: + { + // BDD does not need to support any other adapter information types + BDD_LOG_WARNING1("Unknown QueryAdapterInfo Type (0x%I64x) requested", pQueryAdapterInfo->Type); + return STATUS_NOT_SUPPORTED; + } + } +} + + +NTSTATUS BASIC_DISPLAY_DRIVER::CheckHardware() +{ + PAGED_CODE(); + + NTSTATUS Status; + ULONG VendorID; + ULONG DeviceID; + +// TODO: If developing a driver for PCI based hardware, then use the second method to retrieve Vendor/Device IDs. +// If developing for non-PCI based hardware (i.e. ACPI based hardware), use the first method to retrieve the IDs. +#if 1 // ACPI-based device + + // Get the Vendor & Device IDs on non-PCI system + ACPI_EVAL_INPUT_BUFFER_COMPLEX AcpiInputBuffer = {0}; + AcpiInputBuffer.Signature = ACPI_EVAL_INPUT_BUFFER_COMPLEX_SIGNATURE; + AcpiInputBuffer.MethodNameAsUlong = ACPI_METHOD_HARDWARE_ID; + AcpiInputBuffer.Size = 0; + AcpiInputBuffer.ArgumentCount = 0; + + BYTE OutputBuffer[sizeof(ACPI_EVAL_OUTPUT_BUFFER) + 0x10]; + RtlZeroMemory(OutputBuffer, sizeof(OutputBuffer)); + ACPI_EVAL_OUTPUT_BUFFER* pAcpiOutputBuffer = reinterpret_cast(&OutputBuffer); + + Status = m_DxgkInterface.DxgkCbEvalAcpiMethod(m_DxgkInterface.DeviceHandle, + DISPLAY_ADAPTER_HW_ID, + &AcpiInputBuffer, + sizeof(AcpiInputBuffer), + pAcpiOutputBuffer, + sizeof(OutputBuffer)); + if (!NT_SUCCESS(Status)) + { + BDD_LOG_ERROR1("DxgkCbReadDeviceSpace failed to get hardware IDs with status 0x%I64x", Status); + return Status; + } + + VendorID = ((ULONG*)(pAcpiOutputBuffer->Argument[0].Data))[0]; + DeviceID = ((ULONG*)(pAcpiOutputBuffer->Argument[0].Data))[1]; + +#else // PCI-based device + + // Get the Vendor & Device IDs on PCI system + PCI_COMMON_HEADER Header = {0}; + ULONG BytesRead; + + Status = m_DxgkInterface.DxgkCbReadDeviceSpace(m_DxgkInterface.DeviceHandle, + DXGK_WHICHSPACE_CONFIG, + &Header, + 0, + sizeof(Header), + &BytesRead); + + if (!NT_SUCCESS(Status)) + { + BDD_LOG_ERROR1("DxgkCbReadDeviceSpace failed with status 0x%I64x", Status); + return Status; + } + + VendorID = Header.VendorID; + DeviceID = Header.DeviceID; + +#endif + + // TODO: Replace 0x1414 with your Vendor ID + if (VendorID == 0x1414) + { + switch (DeviceID) + { + // TODO: Replace the case statements below with the Device IDs supported by this driver + case 0x0000: + case 0xFFFF: return STATUS_SUCCESS; + } + } + + return STATUS_GRAPHICS_DRIVER_MISMATCH; +} + +// Even though Sample Basic Display Driver does not support hardware cursors, and reports such +// in QueryAdapterInfo. This function can still be called to set the pointer to not visible +NTSTATUS BASIC_DISPLAY_DRIVER::SetPointerPosition(_In_ CONST DXGKARG_SETPOINTERPOSITION* pSetPointerPosition) +{ + PAGED_CODE(); + + BDD_ASSERT(pSetPointerPosition != NULL); + BDD_ASSERT(pSetPointerPosition->VidPnSourceId < MAX_VIEWS); + + if (!(pSetPointerPosition->Flags.Visible)) + { + return STATUS_SUCCESS; + } + else + { + BDD_LOG_ASSERTION0("SetPointerPosition should never be called to set the pointer to visible since BDD doesn't support hardware cursors."); + return STATUS_UNSUCCESSFUL; + } +} + +// Basic Sample Display Driver does not support hardware cursors, and reports such +// in QueryAdapterInfo. Therefore this function should never be called. +NTSTATUS BASIC_DISPLAY_DRIVER::SetPointerShape(_In_ CONST DXGKARG_SETPOINTERSHAPE* pSetPointerShape) +{ + PAGED_CODE(); + + BDD_ASSERT(pSetPointerShape != NULL); + BDD_LOG_ASSERTION0("SetPointerShape should never be called since BDD doesn't support hardware cursors."); + + return STATUS_NOT_IMPLEMENTED; +} + + +NTSTATUS BASIC_DISPLAY_DRIVER::PresentDisplayOnly(_In_ CONST DXGKARG_PRESENT_DISPLAYONLY* pPresentDisplayOnly) +{ + PAGED_CODE(); + + BDD_ASSERT(pPresentDisplayOnly != NULL); + BDD_ASSERT(pPresentDisplayOnly->VidPnSourceId < MAX_VIEWS); + + if (pPresentDisplayOnly->BytesPerPixel < MIN_BYTES_PER_PIXEL_REPORTED) + { + // Only >=32bpp modes are reported, therefore this Present should never pass anything less than 4 bytes per pixel + BDD_LOG_ERROR1("pPresentDisplayOnly->BytesPerPixel is 0x%I64x, which is lower than the allowed.", pPresentDisplayOnly->BytesPerPixel); + return STATUS_INVALID_PARAMETER; + } + + // If it is in monitor off state or source is not supposed to be visible, don't present anything to the screen + if ((m_MonitorPowerState > PowerDeviceD0) || + (m_CurrentModes[pPresentDisplayOnly->VidPnSourceId].Flags.SourceNotVisible)) + { + return STATUS_SUCCESS; + } + + // Present is only valid if the target is actively connected to this source + if (m_CurrentModes[pPresentDisplayOnly->VidPnSourceId].Flags.FrameBufferIsActive) + { + + // If actual pixels are coming through, will need to completely zero out physical address next time in BlackOutScreen + m_CurrentModes[pPresentDisplayOnly->VidPnSourceId].ZeroedOutStart.QuadPart = 0; + m_CurrentModes[pPresentDisplayOnly->VidPnSourceId].ZeroedOutEnd.QuadPart = 0; + + + D3DKMDT_VIDPN_PRESENT_PATH_ROTATION RotationNeededByFb = pPresentDisplayOnly->Flags.Rotate ? + m_CurrentModes[pPresentDisplayOnly->VidPnSourceId].Rotation : + D3DKMDT_VPPR_IDENTITY; + BYTE* pDst = (BYTE*)m_CurrentModes[pPresentDisplayOnly->VidPnSourceId].FrameBuffer.Ptr; + UINT DstBitPerPixel = BPPFromPixelFormat(m_CurrentModes[pPresentDisplayOnly->VidPnSourceId].DispInfo.ColorFormat); + if (m_CurrentModes[pPresentDisplayOnly->VidPnSourceId].Scaling == D3DKMDT_VPPS_CENTERED) + { + UINT CenterShift = (m_CurrentModes[pPresentDisplayOnly->VidPnSourceId].DispInfo.Height - + m_CurrentModes[pPresentDisplayOnly->VidPnSourceId].SrcModeHeight)*m_CurrentModes[pPresentDisplayOnly->VidPnSourceId].DispInfo.Pitch; + CenterShift += (m_CurrentModes[pPresentDisplayOnly->VidPnSourceId].DispInfo.Width - + m_CurrentModes[pPresentDisplayOnly->VidPnSourceId].SrcModeWidth)*DstBitPerPixel/8; + pDst += (int)CenterShift/2; + } + return m_HardwareBlt[pPresentDisplayOnly->VidPnSourceId].ExecutePresentDisplayOnly(pDst, + DstBitPerPixel, + (BYTE*)pPresentDisplayOnly->pSource, + pPresentDisplayOnly->BytesPerPixel, + pPresentDisplayOnly->Pitch, + pPresentDisplayOnly->NumMoves, + pPresentDisplayOnly->pMoves, + pPresentDisplayOnly->NumDirtyRects, + pPresentDisplayOnly->pDirtyRect, + RotationNeededByFb); + } + + return STATUS_SUCCESS; +} + +NTSTATUS BASIC_DISPLAY_DRIVER::StopDeviceAndReleasePostDisplayOwnership(_In_ D3DDDI_VIDEO_PRESENT_TARGET_ID TargetId, + _Out_ DXGK_DISPLAY_INFORMATION* pDisplayInfo) +{ + PAGED_CODE(); + + BDD_ASSERT(TargetId < MAX_CHILDREN); + + + D3DDDI_VIDEO_PRESENT_SOURCE_ID SourceId = FindSourceForTarget(TargetId, TRUE); + + // In case BDD is the next driver to run, the monitor should not be off, since + // this could cause the BIOS to hang when the EDID is retrieved on Start. + if (m_MonitorPowerState > PowerDeviceD0) + { + SetPowerState(TargetId, PowerDeviceD0, PowerActionNone); + } + + // The driver has to black out the display and ensure it is visible when releasing ownership + BlackOutScreen(SourceId); + + *pDisplayInfo = m_CurrentModes[SourceId].DispInfo; + + return StopDevice(); +} + +NTSTATUS BASIC_DISPLAY_DRIVER::QueryVidPnHWCapability(_Inout_ DXGKARG_QUERYVIDPNHWCAPABILITY* pVidPnHWCaps) +{ + PAGED_CODE(); + + BDD_ASSERT(pVidPnHWCaps != NULL); + BDD_ASSERT(pVidPnHWCaps->SourceId < MAX_VIEWS); + BDD_ASSERT(pVidPnHWCaps->TargetId < MAX_CHILDREN); + + pVidPnHWCaps->VidPnHWCaps.DriverRotation = 1; // BDD does rotation in software + pVidPnHWCaps->VidPnHWCaps.DriverScaling = 0; // BDD does not support scaling + pVidPnHWCaps->VidPnHWCaps.DriverCloning = 0; // BDD does not support clone + pVidPnHWCaps->VidPnHWCaps.DriverColorConvert = 1; // BDD does color conversions in software + pVidPnHWCaps->VidPnHWCaps.DriverLinkedAdapaterOutput = 0; // BDD does not support linked adapters + pVidPnHWCaps->VidPnHWCaps.DriverRemoteDisplay = 0; // BDD does not support remote displays + + return STATUS_SUCCESS; +} + +NTSTATUS BASIC_DISPLAY_DRIVER::GetEdid(D3DDDI_VIDEO_PRESENT_TARGET_ID TargetId) +{ + PAGED_CODE(); + + BDD_ASSERT_CHK(!m_Flags.EDID_Attempted); + + NTSTATUS Status = STATUS_SUCCESS; + RtlZeroMemory(m_EDIDs[TargetId], sizeof(m_EDIDs[TargetId])); + + + m_Flags.EDID_Attempted = TRUE; + + return Status; +} + + +VOID BASIC_DISPLAY_DRIVER::BlackOutScreen(D3DDDI_VIDEO_PRESENT_SOURCE_ID SourceId) +{ + PAGED_CODE(); + + + UINT ScreenHeight = m_CurrentModes[SourceId].DispInfo.Height; + UINT ScreenPitch = m_CurrentModes[SourceId].DispInfo.Pitch; + + PHYSICAL_ADDRESS NewPhysAddrStart = m_CurrentModes[SourceId].DispInfo.PhysicAddress; + PHYSICAL_ADDRESS NewPhysAddrEnd; + NewPhysAddrEnd.QuadPart = NewPhysAddrStart.QuadPart + (ScreenHeight * ScreenPitch); + + if (m_CurrentModes[SourceId].Flags.FrameBufferIsActive) + { + BYTE* MappedAddr = reinterpret_cast(m_CurrentModes[SourceId].FrameBuffer.Ptr); + + // Zero any memory at the start that hasn't been zeroed recently + if (NewPhysAddrStart.QuadPart < m_CurrentModes[SourceId].ZeroedOutStart.QuadPart) + { + if (NewPhysAddrEnd.QuadPart < m_CurrentModes[SourceId].ZeroedOutStart.QuadPart) + { + // No overlap + RtlZeroMemory(MappedAddr, ScreenHeight * ScreenPitch); + } + else + { + RtlZeroMemory(MappedAddr, (UINT)(m_CurrentModes[SourceId].ZeroedOutStart.QuadPart - NewPhysAddrStart.QuadPart)); + } + } + + // Zero any memory at the end that hasn't been zeroed recently + if (NewPhysAddrEnd.QuadPart > m_CurrentModes[SourceId].ZeroedOutEnd.QuadPart) + { + if (NewPhysAddrStart.QuadPart > m_CurrentModes[SourceId].ZeroedOutEnd.QuadPart) + { + // No overlap + // NOTE: When actual pixels were the most recent thing drawn, ZeroedOutStart & ZeroedOutEnd will both be 0 + // and this is the path that will be used to black out the current screen. + RtlZeroMemory(MappedAddr, ScreenHeight * ScreenPitch); + } + else + { + RtlZeroMemory(MappedAddr, (UINT)(NewPhysAddrEnd.QuadPart - m_CurrentModes[SourceId].ZeroedOutEnd.QuadPart)); + } + } + } + + m_CurrentModes[SourceId].ZeroedOutStart.QuadPart = NewPhysAddrStart.QuadPart; + m_CurrentModes[SourceId].ZeroedOutEnd.QuadPart = NewPhysAddrEnd.QuadPart; + +} + +NTSTATUS BASIC_DISPLAY_DRIVER::WriteHWInfoStr(_In_ HANDLE DevInstRegKeyHandle, _In_ PCWSTR pszwValueName, _In_ PCSTR pszValue) +{ + PAGED_CODE(); + + NTSTATUS Status; + ANSI_STRING AnsiStrValue; + UNICODE_STRING UnicodeStrValue; + UNICODE_STRING UnicodeStrValueName; + + // ZwSetValueKey wants the ValueName as a UNICODE_STRING + RtlInitUnicodeString(&UnicodeStrValueName, pszwValueName); + + // REG_SZ is for WCHARs, there is no equivalent for CHARs + // Use the ansi/unicode conversion functions to get from PSTR to PWSTR + RtlInitAnsiString(&AnsiStrValue, pszValue); + Status = RtlAnsiStringToUnicodeString(&UnicodeStrValue, &AnsiStrValue, TRUE); + if (!NT_SUCCESS(Status)) + { + BDD_LOG_ERROR1("RtlAnsiStringToUnicodeString failed with Status: 0x%I64x", Status); + return Status; + } + + // Write the value to the registry + Status = ZwSetValueKey(DevInstRegKeyHandle, + &UnicodeStrValueName, + 0, + REG_SZ, + UnicodeStrValue.Buffer, + UnicodeStrValue.MaximumLength); + + // Free the earlier allocated unicode string + RtlFreeUnicodeString(&UnicodeStrValue); + + if (!NT_SUCCESS(Status)) + { + BDD_LOG_ERROR1("ZwSetValueKey failed with Status: 0x%I64x", Status); + } + + return Status; +} + +NTSTATUS BASIC_DISPLAY_DRIVER::RegisterHWInfo() +{ + PAGED_CODE(); + + NTSTATUS Status; + + // TODO: Replace these strings with proper information + PCSTR StrHWInfoChipType = "Replace with the chip name"; + PCSTR StrHWInfoDacType = "Replace with the DAC name or identifier (ID)"; + PCSTR StrHWInfoAdapterString = "Replace with the name of the adapter"; + PCSTR StrHWInfoBiosString = "Replace with information about the BIOS"; + + HANDLE DevInstRegKeyHandle; + Status = IoOpenDeviceRegistryKey(m_pPhysicalDevice, PLUGPLAY_REGKEY_DRIVER, KEY_SET_VALUE, &DevInstRegKeyHandle); + if (!NT_SUCCESS(Status)) + { + BDD_LOG_ERROR2("IoOpenDeviceRegistryKey failed for PDO: 0x%I64x, Status: 0x%I64x", m_pPhysicalDevice, Status); + return Status; + } + + Status = WriteHWInfoStr(DevInstRegKeyHandle, L"HardwareInformation.ChipType", StrHWInfoChipType); + if (!NT_SUCCESS(Status)) + { + return Status; + } + + Status = WriteHWInfoStr(DevInstRegKeyHandle, L"HardwareInformation.DacType", StrHWInfoDacType); + if (!NT_SUCCESS(Status)) + { + return Status; + } + + Status = WriteHWInfoStr(DevInstRegKeyHandle, L"HardwareInformation.AdapterString", StrHWInfoAdapterString); + if (!NT_SUCCESS(Status)) + { + return Status; + } + + Status = WriteHWInfoStr(DevInstRegKeyHandle, L"HardwareInformation.BiosString", StrHWInfoBiosString); + if (!NT_SUCCESS(Status)) + { + return Status; + } + + // MemorySize is a ULONG, unlike the others which are all strings + UNICODE_STRING ValueNameMemorySize; + RtlInitUnicodeString(&ValueNameMemorySize, L"HardwareInformation.MemorySize"); + DWORD MemorySize = 0; // BDD has no access to video memory + Status = ZwSetValueKey(DevInstRegKeyHandle, + &ValueNameMemorySize, + 0, + REG_DWORD, + &MemorySize, + sizeof(MemorySize)); + if (!NT_SUCCESS(Status)) + { + BDD_LOG_ERROR1("ZwSetValueKey for MemorySize failed with Status: 0x%I64x", Status); + return Status; + } + + return Status; +} + +// +// Non-Paged Code +// +#pragma code_seg(push) +#pragma code_seg() +D3DDDI_VIDEO_PRESENT_SOURCE_ID BASIC_DISPLAY_DRIVER::FindSourceForTarget(D3DDDI_VIDEO_PRESENT_TARGET_ID TargetId, BOOLEAN DefaultToZero) +{ + UNREFERENCED_PARAMETER(TargetId); + BDD_ASSERT_CHK(TargetId < MAX_CHILDREN); + + for (UINT SourceId = 0; SourceId < MAX_VIEWS; ++SourceId) + { + if (m_CurrentModes[SourceId].FrameBuffer.Ptr != NULL) + { + return SourceId; + } + } + + return DefaultToZero ? 0 : D3DDDI_ID_UNINITIALIZED; +} + +VOID BASIC_DISPLAY_DRIVER::DpcRoutine(VOID) +{ + m_DxgkInterface.DxgkCbNotifyDpc((HANDLE)m_DxgkInterface.DeviceHandle); +} + +BOOLEAN BASIC_DISPLAY_DRIVER::InterruptRoutine(_In_ ULONG MessageNumber) +{ + UNREFERENCED_PARAMETER(MessageNumber); + + // BDD cannot handle interrupts + return FALSE; +} + +VOID BASIC_DISPLAY_DRIVER::ResetDevice(VOID) +{ +} + +// Must be Non-Paged, as it sets up the display for a bugcheck +NTSTATUS BASIC_DISPLAY_DRIVER::SystemDisplayEnable(_In_ D3DDDI_VIDEO_PRESENT_TARGET_ID TargetId, + _In_ PDXGKARG_SYSTEM_DISPLAY_ENABLE_FLAGS Flags, + _Out_ UINT* pWidth, + _Out_ UINT* pHeight, + _Out_ D3DDDIFORMAT* pColorFormat) +{ + UNREFERENCED_PARAMETER(Flags); + + m_SystemDisplaySourceId = D3DDDI_ID_UNINITIALIZED; + + BDD_ASSERT((TargetId < MAX_CHILDREN) || (TargetId == D3DDDI_ID_UNINITIALIZED)); + + // Find the frame buffer for displaying the bugcheck, if it was successfully mapped + if (TargetId == D3DDDI_ID_UNINITIALIZED) + { + for (UINT SourceIdx = 0; SourceIdx < MAX_VIEWS; ++SourceIdx) + { + if (m_CurrentModes[SourceIdx].FrameBuffer.Ptr != NULL) + { + m_SystemDisplaySourceId = SourceIdx; + break; + } + } + } + else + { + m_SystemDisplaySourceId = FindSourceForTarget(TargetId, FALSE); + } + + if (m_SystemDisplaySourceId == D3DDDI_ID_UNINITIALIZED) + { + { + return STATUS_UNSUCCESSFUL; + } + } + + if ((m_CurrentModes[m_SystemDisplaySourceId].Rotation == D3DKMDT_VPPR_ROTATE90) || + (m_CurrentModes[m_SystemDisplaySourceId].Rotation == D3DKMDT_VPPR_ROTATE270)) + { + *pHeight = m_CurrentModes[m_SystemDisplaySourceId].DispInfo.Width; + *pWidth = m_CurrentModes[m_SystemDisplaySourceId].DispInfo.Height; + } + else + { + *pWidth = m_CurrentModes[m_SystemDisplaySourceId].DispInfo.Width; + *pHeight = m_CurrentModes[m_SystemDisplaySourceId].DispInfo.Height; + } + + *pColorFormat = m_CurrentModes[m_SystemDisplaySourceId].DispInfo.ColorFormat; + + + return STATUS_SUCCESS; +} + +// Must be Non-Paged, as it is called to display the bugcheck screen +VOID BASIC_DISPLAY_DRIVER::SystemDisplayWrite(_In_reads_bytes_(SourceHeight * SourceStride) VOID* pSource, + _In_ UINT SourceWidth, + _In_ UINT SourceHeight, + _In_ UINT SourceStride, + _In_ INT PositionX, + _In_ INT PositionY) +{ + + // Rect will be Offset by PositionX/Y in the src to reset it back to 0 + RECT Rect; + Rect.left = PositionX; + Rect.top = PositionY; + Rect.right = Rect.left + SourceWidth; + Rect.bottom = Rect.top + SourceHeight; + + // Set up destination blt info + BLT_INFO DstBltInfo; + DstBltInfo.pBits = m_CurrentModes[m_SystemDisplaySourceId].FrameBuffer.Ptr; + DstBltInfo.Pitch = m_CurrentModes[m_SystemDisplaySourceId].DispInfo.Pitch; + DstBltInfo.BitsPerPel = BPPFromPixelFormat(m_CurrentModes[m_SystemDisplaySourceId].DispInfo.ColorFormat); + DstBltInfo.Offset.x = 0; + DstBltInfo.Offset.y = 0; + DstBltInfo.Rotation = m_CurrentModes[m_SystemDisplaySourceId].Rotation; + DstBltInfo.Width = m_CurrentModes[m_SystemDisplaySourceId].DispInfo.Width; + DstBltInfo.Height = m_CurrentModes[m_SystemDisplaySourceId].DispInfo.Height; + + // Set up source blt info + BLT_INFO SrcBltInfo; + SrcBltInfo.pBits = pSource; + SrcBltInfo.Pitch = SourceStride; + SrcBltInfo.BitsPerPel = 32; + + SrcBltInfo.Offset.x = -PositionX; + SrcBltInfo.Offset.y = -PositionY; + SrcBltInfo.Rotation = D3DKMDT_VPPR_IDENTITY; + SrcBltInfo.Width = SourceWidth; + SrcBltInfo.Height = SourceHeight; + + BltBits(&DstBltInfo, + &SrcBltInfo, + 1, // NumRects + &Rect); +} + +#pragma code_seg(pop) // End Non-Paged Code + diff --git a/video/KMDOD/bdd.hxx b/video/KMDOD/bdd.hxx new file mode 100644 index 000000000..61c40e034 --- /dev/null +++ b/video/KMDOD/bdd.hxx @@ -0,0 +1,650 @@ +/******************************Module*Header*******************************\ +* Module Name: BDD.hxx +* +* Basic Display Driver header file +* +* +* Copyright (c) 2010 Microsoft Corporation +\**************************************************************************/ + +#ifndef _BDD_HXX_ +#define _BDD_HXX_ + +extern "C" +{ + #define __CPLUSPLUS + + // Standard C-runtime headers + #include + #include + #include + #include + #include + + #include + + // NTOS headers + #include + + #ifndef FAR + #define FAR + #endif + + // Windows headers + #include + #include + + // Windows GDI headers + #include + + // Windows DDI headers + #include + #include + + #include + #include + + #include + #include + + #include +}; + +#define EDID_V1_BLOCK_SIZE 128 + +#include "BDD_ErrorLog.hxx" + +#define MIN_WIDTH 640 +#define MIN_HEIGHT 480 +#define MIN_BITS_PER_PIXEL_ALLOWED 8 +#define MIN_BYTES_PER_PIXEL_REPORTED 4 + +#define DEFAULT_WIDTH 1024 +#define DEFAULT_HEIGHT 768 + +#define MAX_INVALID_INHERITED_WIDTH 1024 +#define MAX_INVALID_INHERITED_HEIGHT 768 + +#define BITS_PER_BYTE 8 + +typedef struct _BLT_INFO +{ + PVOID pBits; + UINT Pitch; + UINT BitsPerPel; + POINT Offset; // To unrotated top-left of dirty rects + D3DKMDT_VIDPN_PRESENT_PATH_ROTATION Rotation; + UINT Width; // For the unrotated image + UINT Height; // For the unrotated image +} BLT_INFO; + +#define MAX_CHILDREN 1 +#define MAX_VIEWS 1 + +typedef struct _BDD_FLAGS +{ + UINT DriverStarted : 1; // ( 1) 1 after StartDevice and 0 after StopDevice + UINT EDID_Retrieved : 1; // ( 2) EDID was successfully retrieved + UINT EDID_ValidChecksum : 1; // ( 3) Retrieved EDID has a valid checksum + UINT EDID_ValidHeader : 1; // ( 4) Retrieved EDID has a valid header + UINT EDID_Attempted : 1; // ( 5) 1 if an attempt was made to retrieve the EDID, successful or not + + // IMPORTANT: All new flags must be added to just before _LastFlag (i.e. right above this comment), this allows different versions of diagnostics to still be useful. + UINT _LastFlag : 1; // ( 6) Always set to 1, is used to ensure that diagnostic version matches binary version + UINT Unused : 26; +} BDD_FLAGS; + +// Represents the current mode, may not always be set (i.e. frame buffer mapped) if representing the mode passed in on single mode setups. +typedef struct _CURRENT_BDD_MODE +{ + // The source mode currently set for HW Framebuffer + // For sample driver this info filled in StartDevice by the OS and never changed. + DXGK_DISPLAY_INFORMATION DispInfo; + + // The rotation of the current mode. Rotation is performed in software during Present call + D3DKMDT_VIDPN_PRESENT_PATH_ROTATION Rotation; + + D3DKMDT_VIDPN_PRESENT_PATH_SCALING Scaling; + // This mode might be different from one which are supported for HW frame buffer + // Scaling/displasment might be needed (if supported) + UINT SrcModeWidth; + UINT SrcModeHeight; + + // Various boolean flags the struct uses + struct _CURRENT_BDD_MODE_FLAGS + { + UINT SourceNotVisible : 1; // 0 if source is visible + UINT FullscreenPresent : 1; // 0 if should use dirty rects for present + UINT FrameBufferIsActive : 1; // 0 if not currently active (i.e. target not connected to source) + UINT DoNotMapOrUnmap : 1; // 1 if the FrameBuffer should not be (un)mapped during normal execution + UINT IsInternal : 1; // 1 if it was determined (i.e. through ACPI) that an internal panel is being driven + UINT Unused : 27; + } Flags; + + // The start and end of physical memory known to be all zeroes. Used to optimize the BlackOutScreen function to not write + // zeroes to memory already known to be zero. (Physical address is located in DispInfo) + PHYSICAL_ADDRESS ZeroedOutStart; + PHYSICAL_ADDRESS ZeroedOutEnd; + + // Linear frame buffer pointer + // A union with a ULONG64 is used here to ensure this struct looks the same on 32bit and 64bit builds + // since the size of a VOID* changes depending on the build. + union + { + VOID* Ptr; + ULONG64 Force8Bytes; + } FrameBuffer; +} CURRENT_BDD_MODE; + +class BASIC_DISPLAY_DRIVER; + +class BDD_HWBLT +{ +public: + D3DDDI_VIDEO_PRESENT_SOURCE_ID m_SourceId; + BASIC_DISPLAY_DRIVER* m_DevExt; + BOOLEAN m_SynchExecution; + HANDLE m_hPresentWorkerThread; + PVOID m_pPresentWorkerThread; + + // Events to contol thread execution + KEVENT m_hThreadStartupEvent; + KEVENT m_hThreadSuspendEvent; + + BDD_HWBLT(); + + ~BDD_HWBLT(); + + void Initialize(_In_ BASIC_DISPLAY_DRIVER* DevExt, _In_ UINT IdSrc) { m_DevExt = DevExt; m_SourceId = IdSrc; } + void SetPresentWorkerThreadInfo(HANDLE hWorkerThread); + NTSTATUS ExecutePresentDisplayOnly(_In_ BYTE* DstAddr, + _In_ UINT DstBitPerPixel, + _In_ BYTE* SrcAddr, + _In_ UINT SrcBytesPerPixel, + _In_ LONG SrcPitch, + _In_ ULONG NumMoves, + _In_ D3DKMT_MOVE_RECT* pMoves, + _In_ ULONG NumDirtyRects, + _In_ RECT* pDirtyRect, + _In_ D3DKMDT_VIDPN_PRESENT_PATH_ROTATION Rotation); + +}; + +class BASIC_DISPLAY_DRIVER +{ +private: + DEVICE_OBJECT* m_pPhysicalDevice; + DXGKRNL_INTERFACE m_DxgkInterface; + + // Information passed in by StartDevice DDI + DXGK_START_INFO m_StartInfo; + + + // Array of EDIDs, currently only supporting base block, hence EDID_V1_BLOCK_SIZE for size of each EDID + BYTE m_EDIDs[MAX_CHILDREN][EDID_V1_BLOCK_SIZE]; + + CURRENT_BDD_MODE m_CurrentModes[MAX_VIEWS]; + + + BDD_HWBLT m_HardwareBlt[MAX_VIEWS]; + + // Current monitorpower state (this needs to be changed if MAX_CHILDREN != 1) + DEVICE_POWER_STATE m_MonitorPowerState; + + // Current adapter power state + DEVICE_POWER_STATE m_AdapterPowerState; + + // Source ID to be used by SystemDisplay functions + D3DDDI_VIDEO_PRESENT_SOURCE_ID m_SystemDisplaySourceId; + + // Various boolean flags the class uses + BDD_FLAGS m_Flags; + + // Device information + DXGK_DEVICE_INFO m_DeviceInfo; + +public: + BASIC_DISPLAY_DRIVER(_In_ DEVICE_OBJECT* pPhysicalDeviceObject); + ~BASIC_DISPLAY_DRIVER(); + +#pragma code_seg(push) +#pragma code_seg() + BOOLEAN IsDriverActive() const + { + return m_Flags.DriverStarted; + } +#pragma code_seg(pop) + + NTSTATUS StartDevice(_In_ DXGK_START_INFO* pDxgkStartInfo, + _In_ DXGKRNL_INTERFACE* pDxgkInterface, + _Out_ ULONG* pNumberOfViews, + _Out_ ULONG* pNumberOfChildren); + + NTSTATUS StopDevice(VOID); + + // Must be Non-Paged + VOID ResetDevice(VOID); + + + const CURRENT_BDD_MODE* GetCurrentMode(UINT SourceId) const + { + return (SourceId < MAX_VIEWS)?&m_CurrentModes[SourceId]:NULL; + } + const DXGKRNL_INTERFACE* GetDxgkInterface() const { return &m_DxgkInterface;} + + // Not implemented since no IOCTLs currently handled. + NTSTATUS DispatchIoRequest(_In_ ULONG VidPnSourceId, + _In_ VIDEO_REQUEST_PACKET* pVideoRequestPacket); + + // Used to either turn off/on monitor (if possible), or mark that system is going into hibernate + NTSTATUS SetPowerState(_In_ ULONG HardwareUid, + _In_ DEVICE_POWER_STATE DevicePowerState, + _In_ POWER_ACTION ActionType); + + // Report back child capabilities + NTSTATUS QueryChildRelations(_Out_writes_bytes_(ChildRelationsSize) DXGK_CHILD_DESCRIPTOR* pChildRelations, + _In_ ULONG ChildRelationsSize); + + NTSTATUS QueryChildStatus(_Inout_ DXGK_CHILD_STATUS* pChildStatus, + _In_ BOOLEAN NonDestructiveOnly); + + // Return EDID if previously retrieved + NTSTATUS QueryDeviceDescriptor(_In_ ULONG ChildUid, + _Inout_ DXGK_DEVICE_DESCRIPTOR* pDeviceDescriptor); + + // Must be Non-Paged + // BDD doesn't have interrupts, so just returns false + BOOLEAN InterruptRoutine(_In_ ULONG MessageNumber); + + VOID DpcRoutine(VOID); + + // Return DriverCaps, doesn't support other queries though + NTSTATUS QueryAdapterInfo(_In_ CONST DXGKARG_QUERYADAPTERINFO* pQueryAdapterInfo); + + NTSTATUS SetPointerPosition(_In_ CONST DXGKARG_SETPOINTERPOSITION* pSetPointerPosition); + + NTSTATUS SetPointerShape(_In_ CONST DXGKARG_SETPOINTERSHAPE* pSetPointerShape); + + NTSTATUS PresentDisplayOnly(_In_ CONST DXGKARG_PRESENT_DISPLAYONLY* pPresentDisplayOnly); + + NTSTATUS IsSupportedVidPn(_Inout_ DXGKARG_ISSUPPORTEDVIDPN* pIsSupportedVidPn); + + NTSTATUS RecommendFunctionalVidPn(_In_ CONST DXGKARG_RECOMMENDFUNCTIONALVIDPN* CONST pRecommendFunctionalVidPn); + + NTSTATUS RecommendVidPnTopology(_In_ CONST DXGKARG_RECOMMENDVIDPNTOPOLOGY* CONST pRecommendVidPnTopology); + + NTSTATUS RecommendMonitorModes(_In_ CONST DXGKARG_RECOMMENDMONITORMODES* CONST pRecommendMonitorModes); + + NTSTATUS EnumVidPnCofuncModality(_In_ CONST DXGKARG_ENUMVIDPNCOFUNCMODALITY* CONST pEnumCofuncModality); + + NTSTATUS SetVidPnSourceVisibility(_In_ CONST DXGKARG_SETVIDPNSOURCEVISIBILITY* pSetVidPnSourceVisibility); + + NTSTATUS CommitVidPn(_In_ CONST DXGKARG_COMMITVIDPN* CONST pCommitVidPn); + + NTSTATUS UpdateActiveVidPnPresentPath(_In_ CONST DXGKARG_UPDATEACTIVEVIDPNPRESENTPATH* CONST pUpdateActiveVidPnPresentPath); + + NTSTATUS QueryVidPnHWCapability(_Inout_ DXGKARG_QUERYVIDPNHWCAPABILITY* pVidPnHWCaps); + + // Part of PnPStop (PnP instance only), returns current mode information (which will be passed to fallback instance by dxgkrnl) + NTSTATUS StopDeviceAndReleasePostDisplayOwnership(_In_ D3DDDI_VIDEO_PRESENT_TARGET_ID TargetId, + _Out_ DXGK_DISPLAY_INFORMATION* pDisplayInfo); + + // Must be Non-Paged + // Call to initialize as part of bugcheck + NTSTATUS SystemDisplayEnable(_In_ D3DDDI_VIDEO_PRESENT_TARGET_ID TargetId, + _In_ PDXGKARG_SYSTEM_DISPLAY_ENABLE_FLAGS Flags, + _Out_ UINT* pWidth, + _Out_ UINT* pHeight, + _Out_ D3DDDIFORMAT* pColorFormat); + + // Must be Non-Paged + // Write out pixels as part of bugcheck + VOID SystemDisplayWrite(_In_reads_bytes_(SourceHeight * SourceStride) VOID* pSource, + _In_ UINT SourceWidth, + _In_ UINT SourceHeight, + _In_ UINT SourceStride, + _In_ INT PositionX, + _In_ INT PositionY); + + +private: + VOID CleanUp(); + + NTSTATUS CommonStart(); + + NTSTATUS GetEdid(D3DDDI_VIDEO_PRESENT_TARGET_ID TargetId); + + + // Given pixel format, give back the bits per pixel. Only supports pixel formats expected by BDD + // (i.e. the ones found below in PixelFormatFromBPP or that may come in from FallbackStart) + // This is because these two functions combine to allow BDD to store the bpp of a VBE mode in the + // ColorFormat field of a DispInfo + UINT BPPFromPixelFormat(D3DDDIFORMAT Format) const + { + switch (Format) + { + case D3DDDIFMT_UNKNOWN: return 0; + case D3DDDIFMT_P8: return 8; + case D3DDDIFMT_R5G6B5: return 16; + case D3DDDIFMT_R8G8B8: return 24; + case D3DDDIFMT_X8R8G8B8: // fall through + case D3DDDIFMT_A8R8G8B8: return 32; + default: BDD_LOG_ASSERTION1("Unknown D3DDDIFORMAT 0x%I64x", Format); return 0; + } + } + + // Given bits per pixel, return the pixel format at the same bpp + D3DDDIFORMAT PixelFormatFromBPP(UINT BPP) const + { + switch (BPP) + { + case 8: return D3DDDIFMT_P8; + case 16: return D3DDDIFMT_R5G6B5; + case 24: return D3DDDIFMT_R8G8B8; + case 32: return D3DDDIFMT_X8R8G8B8; + default: BDD_LOG_ASSERTION1("A bit per pixel of 0x%I64x is not supported.", BPP); return D3DDDIFMT_UNKNOWN; + } + } + + // These two functions make checks on the values of some of the fields of their respective structures to ensure + // that the specified fields are supported by BDD, i.e. gamma ramp must be D3DDDI_GAMMARAMP_DEFAULT + NTSTATUS IsVidPnPathFieldsValid(CONST D3DKMDT_VIDPN_PRESENT_PATH* pPath) const; + NTSTATUS IsVidPnSourceModeFieldsValid(CONST D3DKMDT_VIDPN_SOURCE_MODE* pSourceMode) const; + + VOID BlackOutScreen(D3DDDI_VIDEO_PRESENT_SOURCE_ID SourceId); + + // Returns the index into gBddBiosData.BddModes of the VBE mode that matches the given VidPnSourceMode. + // If such a mode cannot be found, returns a number outside of [0, gBddBiosData.CountBddModes) + UINT FindMatchingVBEMode(CONST D3DKMDT_VIDPN_SOURCE_MODE* pSourceMode) const; + + // Must be Non-Paged + // Returns the SourceId that has TargetId as a valid frame buffer or D3DDDI_ID_UNINITIALIZED if no such SourceId exists + D3DDDI_VIDEO_PRESENT_SOURCE_ID FindSourceForTarget(D3DDDI_VIDEO_PRESENT_TARGET_ID TargetId, BOOLEAN DefaultToZero); + + // Set the given source mode on the given path + NTSTATUS SetSourceModeAndPath(CONST D3DKMDT_VIDPN_SOURCE_MODE* pSourceMode, + CONST D3DKMDT_VIDPN_PRESENT_PATH* pPath); + + + // Add the current mode to the given monitor source mode set + NTSTATUS AddSingleMonitorMode(_In_ CONST DXGKARG_RECOMMENDMONITORMODES* CONST pRecommendMonitorModes); + + + // Add the current mode to the given VidPn source mode set + NTSTATUS AddSingleSourceMode(_In_ CONST DXGK_VIDPNSOURCEMODESET_INTERFACE* pVidPnSourceModeSetInterface, + D3DKMDT_HVIDPNSOURCEMODESET hVidPnSourceModeSet, + D3DDDI_VIDEO_PRESENT_SOURCE_ID SourceId); + + // Add the current mode (or the matching to pinned source mode) to the give VidPn target mode set + NTSTATUS AddSingleTargetMode(_In_ CONST DXGK_VIDPNTARGETMODESET_INTERFACE* pVidPnTargetModeSetInterface, + D3DKMDT_HVIDPNTARGETMODESET hVidPnTargetModeSet, + _In_opt_ CONST D3DKMDT_VIDPN_SOURCE_MODE* pVidPnPinnedSourceModeInfo, + D3DDDI_VIDEO_PRESENT_SOURCE_ID SourceId); + + // Check that the hardware the driver is running on is hardware it is capable of driving. + NTSTATUS CheckHardware(); + + // Helper function for RegisterHWInfo + NTSTATUS WriteHWInfoStr(_In_ HANDLE DevInstRegKeyHandle, _In_ PCWSTR pszwValueName, _In_ PCSTR pszValue); + + // Set the information in the registry as described here: http://msdn.microsoft.com/en-us/library/windows/hardware/ff569240(v=vs.85).aspx + NTSTATUS RegisterHWInfo(); +}; + +// +// Blt functions +// + +// Must be Non-Paged +VOID BltBits( + BLT_INFO* pDst, + CONST BLT_INFO* pSrc, + UINT NumRects, + _In_reads_(NumRects) CONST RECT *pRects); + +// +// Driver Entry point +// + +extern "C" +DRIVER_INITIALIZE DriverEntry; + +// +// PnP DDIs +// + +VOID +BddDdiUnload(VOID); + +// If uncommenting ENABLE_DXGK_SAL in the sources file, all the below function prototypes should be updated to use +// the function typedef's from the header files. Additionally, annotations on the function definitions can be removed +// as they are inherited from the prototype definition here. As an example the entire 4-line prototype for BddDdiAddDevice +// is replaced by the single commented line below: +// DXGKDDI_ADD_DEVICE BddDdiAddDevice; +NTSTATUS +BddDdiAddDevice( + _In_ DEVICE_OBJECT* pPhysicalDeviceObject, + _Outptr_ PVOID* ppDeviceContext); + +NTSTATUS +BddDdiRemoveDevice( + _In_ VOID* pDeviceContext); + +NTSTATUS +BddDdiStartDevice( + _In_ VOID* pDeviceContext, + _In_ DXGK_START_INFO* pDxgkStartInfo, + _In_ DXGKRNL_INTERFACE* pDxgkInterface, + _Out_ ULONG* pNumberOfViews, + _Out_ ULONG* pNumberOfChildren); + +NTSTATUS +BddDdiStopDevice( + _In_ VOID* pDeviceContext); + +VOID +BddDdiResetDevice( + _In_ VOID* pDeviceContext); + + +NTSTATUS +BddDdiDispatchIoRequest( + _In_ VOID* pDeviceContext, + _In_ ULONG VidPnSourceId, + _In_ VIDEO_REQUEST_PACKET* pVideoRequestPacket); + +NTSTATUS +BddDdiSetPowerState( + _In_ VOID* pDeviceContext, + _In_ ULONG HardwareUid, + _In_ DEVICE_POWER_STATE DevicePowerState, + _In_ POWER_ACTION ActionType); + +NTSTATUS +BddDdiQueryChildRelations( + _In_ VOID* pDeviceContext, + _Out_writes_bytes_(ChildRelationsSize) DXGK_CHILD_DESCRIPTOR* pChildRelations, + _In_ ULONG ChildRelationsSize); + +NTSTATUS +BddDdiQueryChildStatus( + _In_ VOID* pDeviceContext, + _Inout_ DXGK_CHILD_STATUS* pChildStatus, + _In_ BOOLEAN NonDestructiveOnly); + +NTSTATUS +BddDdiQueryDeviceDescriptor( + _In_ VOID* pDeviceContext, + _In_ ULONG ChildUid, + _Inout_ DXGK_DEVICE_DESCRIPTOR* pDeviceDescriptor); + +// Must be Non-Paged +BOOLEAN +BddDdiInterruptRoutine( + _In_ VOID* pDeviceContext, + _In_ ULONG MessageNumber); + +VOID +BddDdiDpcRoutine( + _In_ VOID* pDeviceContext); + +// +// WDDM Display Only Driver DDIs +// + +NTSTATUS +APIENTRY +BddDdiQueryAdapterInfo( + _In_ CONST HANDLE hAdapter, + _In_ CONST DXGKARG_QUERYADAPTERINFO* pQueryAdapterInfo); + +NTSTATUS +APIENTRY +BddDdiSetPointerPosition( + _In_ CONST HANDLE hAdapter, + _In_ CONST DXGKARG_SETPOINTERPOSITION* pSetPointerPosition); + +NTSTATUS +APIENTRY +BddDdiSetPointerShape( + _In_ CONST HANDLE hAdapter, + _In_ CONST DXGKARG_SETPOINTERSHAPE* pSetPointerShape); + +NTSTATUS +APIENTRY +BddDdiPresentDisplayOnly( + _In_ CONST HANDLE hAdapter, + _In_ CONST DXGKARG_PRESENT_DISPLAYONLY* pPresentDisplayOnly); + +NTSTATUS +APIENTRY +BddDdiIsSupportedVidPn( + _In_ CONST HANDLE hAdapter, + _Inout_ DXGKARG_ISSUPPORTEDVIDPN* pIsSupportedVidPn); + +NTSTATUS +APIENTRY +BddDdiRecommendFunctionalVidPn( + _In_ CONST HANDLE hAdapter, + _In_ CONST DXGKARG_RECOMMENDFUNCTIONALVIDPN* CONST pRecommendFunctionalVidPn); + +NTSTATUS +APIENTRY +BddDdiRecommendVidPnTopology( + _In_ CONST HANDLE hAdapter, + _In_ CONST DXGKARG_RECOMMENDVIDPNTOPOLOGY* CONST pRecommendVidPnTopology); + +NTSTATUS +APIENTRY +BddDdiRecommendMonitorModes( + _In_ CONST HANDLE hAdapter, + _In_ CONST DXGKARG_RECOMMENDMONITORMODES* CONST pRecommendMonitorModes); + +NTSTATUS +APIENTRY +BddDdiEnumVidPnCofuncModality( + _In_ CONST HANDLE hAdapter, + _In_ CONST DXGKARG_ENUMVIDPNCOFUNCMODALITY* CONST pEnumCofuncModality); + +NTSTATUS +APIENTRY +BddDdiSetVidPnSourceVisibility( + _In_ CONST HANDLE hAdapter, + _In_ CONST DXGKARG_SETVIDPNSOURCEVISIBILITY* pSetVidPnSourceVisibility); + +NTSTATUS +APIENTRY +BddDdiCommitVidPn( + _In_ CONST HANDLE hAdapter, + _In_ CONST DXGKARG_COMMITVIDPN* CONST pCommitVidPn); + +NTSTATUS +APIENTRY +BddDdiUpdateActiveVidPnPresentPath( + _In_ CONST HANDLE hAdapter, + _In_ CONST DXGKARG_UPDATEACTIVEVIDPNPRESENTPATH* CONST pUpdateActiveVidPnPresentPath); + +NTSTATUS +APIENTRY +BddDdiQueryVidPnHWCapability( + _In_ CONST HANDLE hAdapter, + _Inout_ DXGKARG_QUERYVIDPNHWCAPABILITY* pVidPnHWCaps); + +NTSTATUS +APIENTRY +BddDdiStopDeviceAndReleasePostDisplayOwnership( + _In_ VOID* pDeviceContext, + _In_ D3DDDI_VIDEO_PRESENT_TARGET_ID TargetId, + _Out_ DXGK_DISPLAY_INFORMATION* DisplayInfo); + +// Must be Non-Paged +NTSTATUS +APIENTRY +BddDdiSystemDisplayEnable( + _In_ VOID* pDeviceContext, + _In_ D3DDDI_VIDEO_PRESENT_TARGET_ID TargetId, + _In_ PDXGKARG_SYSTEM_DISPLAY_ENABLE_FLAGS Flags, + _Out_ UINT* Width, + _Out_ UINT* Height, + _Out_ D3DDDIFORMAT* ColorFormat); + +// Must be Non-Paged +VOID +APIENTRY +BddDdiSystemDisplayWrite( + _In_ VOID* pDeviceContext, + _In_ VOID* Source, + _In_ UINT SourceWidth, + _In_ UINT SourceHeight, + _In_ UINT SourceStride, + _In_ UINT PositionX, + _In_ UINT PositionY); + +// +// Frame buffer map/unmap +// + +NTSTATUS +MapFrameBuffer( + _In_ PHYSICAL_ADDRESS PhysicalAddress, + _In_ ULONG Length, + _Outptr_result_bytebuffer_(Length) VOID** VirtualAddress); + +NTSTATUS +UnmapFrameBuffer( + _In_reads_bytes_(Length) VOID* VirtualAddress, + _In_ ULONG Length); + + +BOOLEAN +IsEdidHeaderValid(_In_reads_bytes_(EDID_V1_BLOCK_SIZE) const BYTE* pEdid); + +BOOLEAN +IsEdidChecksumValid(_In_reads_bytes_(EDID_V1_BLOCK_SIZE) const BYTE* pEdid); + +// +// Memory handling +// + +// Defaulting the value of PoolType means that any call to new Foo() +// will raise a compiler error for being ambiguous. This is to help keep +// any calls to allocate memory from accidentally NOT going through +// these functions. +_When_((PoolType & NonPagedPoolMustSucceed) != 0, + __drv_reportError("Must succeed pool allocations are forbidden. " + "Allocation failures cause a system crash")) +void* __cdecl operator new(size_t Size, POOL_TYPE PoolType = PagedPool); +_When_((PoolType & NonPagedPoolMustSucceed) != 0, + __drv_reportError("Must succeed pool allocations are forbidden. " + "Allocation failures cause a system crash")) +void* __cdecl operator new[](size_t Size, POOL_TYPE PoolType = PagedPool); +void __cdecl operator delete(void* pObject); +void __cdecl operator delete(void* pObject, size_t s); +void __cdecl operator delete[](void* pObject); + +// Pool allocation tag for the Sample Display Driver. All allocations use this tag. +#define BDDTAG 'DDBS' + +#endif // _BDD_HXX_ + + diff --git a/video/KMDOD/bdd_ddi.cxx b/video/KMDOD/bdd_ddi.cxx new file mode 100644 index 000000000..5a53a3be6 --- /dev/null +++ b/video/KMDOD/bdd_ddi.cxx @@ -0,0 +1,579 @@ +/******************************Module*Header*******************************\ +* Module Name: BDD_DDI.cxx +* +* Basic Display Driver DDI entry points redirects +* +* +* Copyright (c) 2010 Microsoft Corporation +\**************************************************************************/ + +#include "BDD.hxx" + + +#pragma code_seg(push) +#pragma code_seg("INIT") +// BEGIN: Init Code + +// +// Driver Entry point +// + +extern "C" +NTSTATUS +DriverEntry( + _In_ DRIVER_OBJECT* pDriverObject, + _In_ UNICODE_STRING* pRegistryPath) +{ + PAGED_CODE(); + + + // Initialize DDI function pointers and dxgkrnl + KMDDOD_INITIALIZATION_DATA InitialData = {0}; + + InitialData.Version = DXGKDDI_INTERFACE_VERSION; + + InitialData.DxgkDdiAddDevice = BddDdiAddDevice; + InitialData.DxgkDdiStartDevice = BddDdiStartDevice; + InitialData.DxgkDdiStopDevice = BddDdiStopDevice; + InitialData.DxgkDdiResetDevice = BddDdiResetDevice; + InitialData.DxgkDdiRemoveDevice = BddDdiRemoveDevice; + InitialData.DxgkDdiDispatchIoRequest = BddDdiDispatchIoRequest; + InitialData.DxgkDdiInterruptRoutine = BddDdiInterruptRoutine; + InitialData.DxgkDdiDpcRoutine = BddDdiDpcRoutine; + InitialData.DxgkDdiQueryChildRelations = BddDdiQueryChildRelations; + InitialData.DxgkDdiQueryChildStatus = BddDdiQueryChildStatus; + InitialData.DxgkDdiQueryDeviceDescriptor = BddDdiQueryDeviceDescriptor; + InitialData.DxgkDdiSetPowerState = BddDdiSetPowerState; + InitialData.DxgkDdiUnload = BddDdiUnload; + InitialData.DxgkDdiQueryAdapterInfo = BddDdiQueryAdapterInfo; + InitialData.DxgkDdiSetPointerPosition = BddDdiSetPointerPosition; + InitialData.DxgkDdiSetPointerShape = BddDdiSetPointerShape; + InitialData.DxgkDdiIsSupportedVidPn = BddDdiIsSupportedVidPn; + InitialData.DxgkDdiRecommendFunctionalVidPn = BddDdiRecommendFunctionalVidPn; + InitialData.DxgkDdiEnumVidPnCofuncModality = BddDdiEnumVidPnCofuncModality; + InitialData.DxgkDdiSetVidPnSourceVisibility = BddDdiSetVidPnSourceVisibility; + InitialData.DxgkDdiCommitVidPn = BddDdiCommitVidPn; + InitialData.DxgkDdiUpdateActiveVidPnPresentPath = BddDdiUpdateActiveVidPnPresentPath; + InitialData.DxgkDdiRecommendMonitorModes = BddDdiRecommendMonitorModes; + InitialData.DxgkDdiQueryVidPnHWCapability = BddDdiQueryVidPnHWCapability; + InitialData.DxgkDdiPresentDisplayOnly = BddDdiPresentDisplayOnly; + InitialData.DxgkDdiStopDeviceAndReleasePostDisplayOwnership = BddDdiStopDeviceAndReleasePostDisplayOwnership; + InitialData.DxgkDdiSystemDisplayEnable = BddDdiSystemDisplayEnable; + InitialData.DxgkDdiSystemDisplayWrite = BddDdiSystemDisplayWrite; + + NTSTATUS Status = DxgkInitializeDisplayOnlyDriver(pDriverObject, pRegistryPath, &InitialData); + if (!NT_SUCCESS(Status)) + { + BDD_LOG_ERROR1("DxgkInitializeDisplayOnlyDriver failed with Status: 0x%I64x", Status); + return Status; + } + + + return Status; +} +// END: Init Code +#pragma code_seg(pop) + +#pragma code_seg(push) +#pragma code_seg("PAGE") + +// +// PnP DDIs +// + +VOID +BddDdiUnload(VOID) +{ + PAGED_CODE(); +} + +NTSTATUS +BddDdiAddDevice( + _In_ DEVICE_OBJECT* pPhysicalDeviceObject, + _Outptr_ PVOID* ppDeviceContext) +{ + PAGED_CODE(); + + if ((pPhysicalDeviceObject == NULL) || + (ppDeviceContext == NULL)) + { + BDD_LOG_ERROR2("One of pPhysicalDeviceObject (0x%I64x), ppDeviceContext (0x%I64x) is NULL", + pPhysicalDeviceObject, ppDeviceContext); + return STATUS_INVALID_PARAMETER; + } + *ppDeviceContext = NULL; + + BASIC_DISPLAY_DRIVER* pBDD = new(NonPagedPoolNx) BASIC_DISPLAY_DRIVER(pPhysicalDeviceObject); + if (pBDD == NULL) + { + BDD_LOG_LOW_RESOURCE0("pBDD failed to be allocated"); + return STATUS_NO_MEMORY; + } + + *ppDeviceContext = pBDD; + + return STATUS_SUCCESS; +} + +NTSTATUS +BddDdiRemoveDevice( + _In_ VOID* pDeviceContext) +{ + PAGED_CODE(); + + BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast(pDeviceContext); + + if (pBDD) + { + delete pBDD; + pBDD = NULL; + } + + return STATUS_SUCCESS; +} + +NTSTATUS +BddDdiStartDevice( + _In_ VOID* pDeviceContext, + _In_ DXGK_START_INFO* pDxgkStartInfo, + _In_ DXGKRNL_INTERFACE* pDxgkInterface, + _Out_ ULONG* pNumberOfViews, + _Out_ ULONG* pNumberOfChildren) +{ + PAGED_CODE(); + BDD_ASSERT_CHK(pDeviceContext != NULL); + + BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast(pDeviceContext); + return pBDD->StartDevice(pDxgkStartInfo, pDxgkInterface, pNumberOfViews, pNumberOfChildren); +} + +NTSTATUS +BddDdiStopDevice( + _In_ VOID* pDeviceContext) +{ + PAGED_CODE(); + BDD_ASSERT_CHK(pDeviceContext != NULL); + + BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast(pDeviceContext); + return pBDD->StopDevice(); +} + + +NTSTATUS +BddDdiDispatchIoRequest( + _In_ VOID* pDeviceContext, + _In_ ULONG VidPnSourceId, + _In_ VIDEO_REQUEST_PACKET* pVideoRequestPacket) +{ + PAGED_CODE(); + BDD_ASSERT_CHK(pDeviceContext != NULL); + + BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast(pDeviceContext); + if (!pBDD->IsDriverActive()) + { + BDD_LOG_ASSERTION1("BDD (0x%I64x) is being called when not active!", pBDD); + return STATUS_UNSUCCESSFUL; + } + return pBDD->DispatchIoRequest(VidPnSourceId, pVideoRequestPacket); +} + +NTSTATUS +BddDdiSetPowerState( + _In_ VOID* pDeviceContext, + _In_ ULONG HardwareUid, + _In_ DEVICE_POWER_STATE DevicePowerState, + _In_ POWER_ACTION ActionType) +{ + PAGED_CODE(); + BDD_ASSERT_CHK(pDeviceContext != NULL); + + BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast(pDeviceContext); + if (!pBDD->IsDriverActive()) + { + // If the driver isn't active, SetPowerState can still be called, however in BDD's case + // this shouldn't do anything, as it could for instance be called on BDD Fallback after + // Fallback has been stopped and BDD PnP is being started. Fallback doesn't have control + // of the hardware in this case. + return STATUS_SUCCESS; + } + return pBDD->SetPowerState(HardwareUid, DevicePowerState, ActionType); +} + +NTSTATUS +BddDdiQueryChildRelations( + _In_ VOID* pDeviceContext, + _Out_writes_bytes_(ChildRelationsSize) DXGK_CHILD_DESCRIPTOR* pChildRelations, + _In_ ULONG ChildRelationsSize) +{ + PAGED_CODE(); + BDD_ASSERT_CHK(pDeviceContext != NULL); + + BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast(pDeviceContext); + return pBDD->QueryChildRelations(pChildRelations, ChildRelationsSize); +} + +NTSTATUS +BddDdiQueryChildStatus( + _In_ VOID* pDeviceContext, + _Inout_ DXGK_CHILD_STATUS* pChildStatus, + _In_ BOOLEAN NonDestructiveOnly) +{ + PAGED_CODE(); + BDD_ASSERT_CHK(pDeviceContext != NULL); + + BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast(pDeviceContext); + return pBDD->QueryChildStatus(pChildStatus, NonDestructiveOnly); +} + +NTSTATUS +BddDdiQueryDeviceDescriptor( + _In_ VOID* pDeviceContext, + _In_ ULONG ChildUid, + _Inout_ DXGK_DEVICE_DESCRIPTOR* pDeviceDescriptor) +{ + PAGED_CODE(); + BDD_ASSERT_CHK(pDeviceContext != NULL); + + BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast(pDeviceContext); + if (!pBDD->IsDriverActive()) + { + // During stress testing of PnPStop, it is possible for BDD Fallback to get called to start then stop in quick succession. + // The first call queues a worker thread item indicating that it now has a child device, the second queues a worker thread + // item that it no longer has any child device. This function gets called based on the first worker thread item, but after + // the driver has been stopped. Therefore instead of asserting like other functions, we only warn. + BDD_LOG_WARNING1("BDD (0x%I64x) is being called when not active!", pBDD); + return STATUS_UNSUCCESSFUL; + } + return pBDD->QueryDeviceDescriptor(ChildUid, pDeviceDescriptor); +} + + +// +// WDDM Display Only Driver DDIs +// + +NTSTATUS +APIENTRY +BddDdiQueryAdapterInfo( + _In_ CONST HANDLE hAdapter, + _In_ CONST DXGKARG_QUERYADAPTERINFO* pQueryAdapterInfo) +{ + PAGED_CODE(); + BDD_ASSERT_CHK(hAdapter != NULL); + + BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast(hAdapter); + return pBDD->QueryAdapterInfo(pQueryAdapterInfo); +} + +NTSTATUS +APIENTRY +BddDdiSetPointerPosition( + _In_ CONST HANDLE hAdapter, + _In_ CONST DXGKARG_SETPOINTERPOSITION* pSetPointerPosition) +{ + PAGED_CODE(); + BDD_ASSERT_CHK(hAdapter != NULL); + + BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast(hAdapter); + if (!pBDD->IsDriverActive()) + { + BDD_LOG_ASSERTION1("BDD (0x%I64x) is being called when not active!", pBDD); + return STATUS_UNSUCCESSFUL; + } + return pBDD->SetPointerPosition(pSetPointerPosition); +} + +NTSTATUS +APIENTRY +BddDdiSetPointerShape( + _In_ CONST HANDLE hAdapter, + _In_ CONST DXGKARG_SETPOINTERSHAPE* pSetPointerShape) +{ + PAGED_CODE(); + BDD_ASSERT_CHK(hAdapter != NULL); + + BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast(hAdapter); + if (!pBDD->IsDriverActive()) + { + BDD_LOG_ASSERTION1("BDD (0x%I64x) is being called when not active!", pBDD); + return STATUS_UNSUCCESSFUL; + } + return pBDD->SetPointerShape(pSetPointerShape); +} + + +NTSTATUS +APIENTRY +BddDdiPresentDisplayOnly( + _In_ CONST HANDLE hAdapter, + _In_ CONST DXGKARG_PRESENT_DISPLAYONLY* pPresentDisplayOnly) +{ + PAGED_CODE(); + BDD_ASSERT_CHK(hAdapter != NULL); + + BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast(hAdapter); + if (!pBDD->IsDriverActive()) + { + BDD_LOG_ASSERTION1("BDD (0x%I64x) is being called when not active!", pBDD); + return STATUS_UNSUCCESSFUL; + } + return pBDD->PresentDisplayOnly(pPresentDisplayOnly); +} + +NTSTATUS +APIENTRY +BddDdiStopDeviceAndReleasePostDisplayOwnership( + _In_ VOID* pDeviceContext, + _In_ D3DDDI_VIDEO_PRESENT_TARGET_ID TargetId, + _Out_ DXGK_DISPLAY_INFORMATION* DisplayInfo) +{ + PAGED_CODE(); + BDD_ASSERT_CHK(pDeviceContext != NULL); + + BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast(pDeviceContext); + return pBDD->StopDeviceAndReleasePostDisplayOwnership(TargetId, DisplayInfo); +} + +NTSTATUS +APIENTRY +BddDdiIsSupportedVidPn( + _In_ CONST HANDLE hAdapter, + _Inout_ DXGKARG_ISSUPPORTEDVIDPN* pIsSupportedVidPn) +{ + PAGED_CODE(); + BDD_ASSERT_CHK(hAdapter != NULL); + + BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast(hAdapter); + if (!pBDD->IsDriverActive()) + { + // This path might hit because win32k/dxgport doesn't check that an adapter is active when taking the adapter lock. + // The adapter lock is the main thing BDD Fallback relies on to not be called while it's inactive. It is still a rare + // timing issue around PnpStart/Stop and isn't expected to have any effect on the stability of the system. + BDD_LOG_WARNING1("BDD (0x%I64x) is being called when not active!", pBDD); + return STATUS_UNSUCCESSFUL; + } + return pBDD->IsSupportedVidPn(pIsSupportedVidPn); +} + +NTSTATUS +APIENTRY +BddDdiRecommendFunctionalVidPn( + _In_ CONST HANDLE hAdapter, + _In_ CONST DXGKARG_RECOMMENDFUNCTIONALVIDPN* CONST pRecommendFunctionalVidPn) +{ + PAGED_CODE(); + BDD_ASSERT_CHK(hAdapter != NULL); + + BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast(hAdapter); + if (!pBDD->IsDriverActive()) + { + BDD_LOG_ASSERTION1("BDD (0x%I64x) is being called when not active!", pBDD); + return STATUS_UNSUCCESSFUL; + } + return pBDD->RecommendFunctionalVidPn(pRecommendFunctionalVidPn); +} + +NTSTATUS +APIENTRY +BddDdiRecommendVidPnTopology( + _In_ CONST HANDLE hAdapter, + _In_ CONST DXGKARG_RECOMMENDVIDPNTOPOLOGY* CONST pRecommendVidPnTopology) +{ + PAGED_CODE(); + BDD_ASSERT_CHK(hAdapter != NULL); + + BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast(hAdapter); + if (!pBDD->IsDriverActive()) + { + BDD_LOG_ASSERTION1("BDD (0x%I64x) is being called when not active!", pBDD); + return STATUS_UNSUCCESSFUL; + } + return pBDD->RecommendVidPnTopology(pRecommendVidPnTopology); +} + +NTSTATUS +APIENTRY +BddDdiRecommendMonitorModes( + _In_ CONST HANDLE hAdapter, + _In_ CONST DXGKARG_RECOMMENDMONITORMODES* CONST pRecommendMonitorModes) +{ + PAGED_CODE(); + BDD_ASSERT_CHK(hAdapter != NULL); + + BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast(hAdapter); + if (!pBDD->IsDriverActive()) + { + BDD_LOG_ASSERTION1("BDD (0x%I64x) is being called when not active!", pBDD); + return STATUS_UNSUCCESSFUL; + } + return pBDD->RecommendMonitorModes(pRecommendMonitorModes); +} + +NTSTATUS +APIENTRY +BddDdiEnumVidPnCofuncModality( + _In_ CONST HANDLE hAdapter, + _In_ CONST DXGKARG_ENUMVIDPNCOFUNCMODALITY* CONST pEnumCofuncModality) +{ + PAGED_CODE(); + BDD_ASSERT_CHK(hAdapter != NULL); + + BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast(hAdapter); + if (!pBDD->IsDriverActive()) + { + BDD_LOG_ASSERTION1("BDD (0x%I64x) is being called when not active!", pBDD); + return STATUS_UNSUCCESSFUL; + } + return pBDD->EnumVidPnCofuncModality(pEnumCofuncModality); +} + +NTSTATUS +APIENTRY +BddDdiSetVidPnSourceVisibility( + _In_ CONST HANDLE hAdapter, + _In_ CONST DXGKARG_SETVIDPNSOURCEVISIBILITY* pSetVidPnSourceVisibility) +{ + PAGED_CODE(); + BDD_ASSERT_CHK(hAdapter != NULL); + + BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast(hAdapter); + if (!pBDD->IsDriverActive()) + { + BDD_LOG_ASSERTION1("BDD (0x%I64x) is being called when not active!", pBDD); + return STATUS_UNSUCCESSFUL; + } + return pBDD->SetVidPnSourceVisibility(pSetVidPnSourceVisibility); +} + +NTSTATUS +APIENTRY +BddDdiCommitVidPn( + _In_ CONST HANDLE hAdapter, + _In_ CONST DXGKARG_COMMITVIDPN* CONST pCommitVidPn) +{ + PAGED_CODE(); + BDD_ASSERT_CHK(hAdapter != NULL); + + BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast(hAdapter); + if (!pBDD->IsDriverActive()) + { + BDD_LOG_ASSERTION1("BDD (0x%I64x) is being called when not active!", pBDD); + return STATUS_UNSUCCESSFUL; + } + return pBDD->CommitVidPn(pCommitVidPn); +} + +NTSTATUS +APIENTRY +BddDdiUpdateActiveVidPnPresentPath( + _In_ CONST HANDLE hAdapter, + _In_ CONST DXGKARG_UPDATEACTIVEVIDPNPRESENTPATH* CONST pUpdateActiveVidPnPresentPath) +{ + PAGED_CODE(); + BDD_ASSERT_CHK(hAdapter != NULL); + + BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast(hAdapter); + if (!pBDD->IsDriverActive()) + { + BDD_LOG_ASSERTION1("BDD (0x%I64x) is being called when not active!", pBDD); + return STATUS_UNSUCCESSFUL; + } + return pBDD->UpdateActiveVidPnPresentPath(pUpdateActiveVidPnPresentPath); +} + +NTSTATUS +APIENTRY +BddDdiQueryVidPnHWCapability( + _In_ CONST HANDLE hAdapter, + _Inout_ DXGKARG_QUERYVIDPNHWCAPABILITY* pVidPnHWCaps) +{ + PAGED_CODE(); + BDD_ASSERT_CHK(hAdapter != NULL); + + BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast(hAdapter); + if (!pBDD->IsDriverActive()) + { + BDD_LOG_ASSERTION1("BDD (0x%I64x) is being called when not active!", pBDD); + return STATUS_UNSUCCESSFUL; + } + return pBDD->QueryVidPnHWCapability(pVidPnHWCaps); +} +//END: Paged Code +#pragma code_seg(pop) + +#pragma code_seg(push) +#pragma code_seg() +// BEGIN: Non-Paged Code + +VOID +BddDdiDpcRoutine( + _In_ VOID* pDeviceContext) +{ + BDD_ASSERT_CHK(pDeviceContext != NULL); + + BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast(pDeviceContext); + if (!pBDD->IsDriverActive()) + { + BDD_LOG_ASSERTION1("BDD (0x%I64x) is being called when not active!", pBDD); + return; + } + pBDD->DpcRoutine(); +} + +BOOLEAN +BddDdiInterruptRoutine( + _In_ VOID* pDeviceContext, + _In_ ULONG MessageNumber) +{ + BDD_ASSERT_CHK(pDeviceContext != NULL); + + BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast(pDeviceContext); + return pBDD->InterruptRoutine(MessageNumber); +} + +VOID +BddDdiResetDevice( + _In_ VOID* pDeviceContext) +{ + BDD_ASSERT_CHK(pDeviceContext != NULL); + + BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast(pDeviceContext); + pBDD->ResetDevice(); +} + +NTSTATUS +APIENTRY +BddDdiSystemDisplayEnable( + _In_ VOID* pDeviceContext, + _In_ D3DDDI_VIDEO_PRESENT_TARGET_ID TargetId, + _In_ PDXGKARG_SYSTEM_DISPLAY_ENABLE_FLAGS Flags, + _Out_ UINT* Width, + _Out_ UINT* Height, + _Out_ D3DDDIFORMAT* ColorFormat) +{ + BDD_ASSERT_CHK(pDeviceContext != NULL); + + BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast(pDeviceContext); + return pBDD->SystemDisplayEnable(TargetId, Flags, Width, Height, ColorFormat); +} + +VOID +APIENTRY +BddDdiSystemDisplayWrite( + _In_ VOID* pDeviceContext, + _In_ VOID* Source, + _In_ UINT SourceWidth, + _In_ UINT SourceHeight, + _In_ UINT SourceStride, + _In_ UINT PositionX, + _In_ UINT PositionY) +{ + BDD_ASSERT_CHK(pDeviceContext != NULL); + + BASIC_DISPLAY_DRIVER* pBDD = reinterpret_cast(pDeviceContext); + pBDD->SystemDisplayWrite(Source, SourceWidth, SourceHeight, SourceStride, PositionX, PositionY); +} + +// END: Non-Paged Code +#pragma code_seg(pop) + diff --git a/video/KMDOD/bdd_dmm.cxx b/video/KMDOD/bdd_dmm.cxx new file mode 100644 index 000000000..95c59538f --- /dev/null +++ b/video/KMDOD/bdd_dmm.cxx @@ -0,0 +1,1083 @@ +/******************************Module*Header*******************************\ +* Module Name: bdd_dmm.hxx +* +* Basic Display Driver display-mode management (DMM) function implementations +* +* +* Copyright (c) 2010 Microsoft Corporation +\**************************************************************************/ + +#include "BDD.hxx" + +#pragma code_seg("PAGE") + +// Display-Only Devices can only return display modes of D3DDDIFMT_A8R8G8B8. +// Color conversion takes place if the app's fullscreen backbuffer has different format. +// Full display drivers can add more if the hardware supports them. +D3DDDIFORMAT gBddPixelFormats[] = { + D3DDDIFMT_A8R8G8B8 +}; + +// TODO: Need to also check pinned modes and the path parameters, not just topology +NTSTATUS BASIC_DISPLAY_DRIVER::IsSupportedVidPn(_Inout_ DXGKARG_ISSUPPORTEDVIDPN* pIsSupportedVidPn) +{ + PAGED_CODE(); + + BDD_ASSERT(pIsSupportedVidPn != NULL); + + if (pIsSupportedVidPn->hDesiredVidPn == 0) + { + // A null desired VidPn is supported + pIsSupportedVidPn->IsVidPnSupported = TRUE; + return STATUS_SUCCESS; + } + + // Default to not supported, until shown it is supported + pIsSupportedVidPn->IsVidPnSupported = FALSE; + + CONST DXGK_VIDPN_INTERFACE* pVidPnInterface; + NTSTATUS Status = m_DxgkInterface.DxgkCbQueryVidPnInterface(pIsSupportedVidPn->hDesiredVidPn, DXGK_VIDPN_INTERFACE_VERSION_V1, &pVidPnInterface); + if (!NT_SUCCESS(Status)) + { + BDD_LOG_ERROR2("DxgkCbQueryVidPnInterface failed with Status = 0x%I64x, hDesiredVidPn = 0x%I64x", Status, pIsSupportedVidPn->hDesiredVidPn); + return Status; + } + + D3DKMDT_HVIDPNTOPOLOGY hVidPnTopology; + CONST DXGK_VIDPNTOPOLOGY_INTERFACE* pVidPnTopologyInterface; + Status = pVidPnInterface->pfnGetTopology(pIsSupportedVidPn->hDesiredVidPn, &hVidPnTopology, &pVidPnTopologyInterface); + if (!NT_SUCCESS(Status)) + { + BDD_LOG_ERROR2("pfnGetTopology failed with Status = 0x%I64x, hDesiredVidPn = 0x%I64x", Status, pIsSupportedVidPn->hDesiredVidPn); + return Status; + } + + // For every source in this topology, make sure they don't have more paths than there are targets + for (D3DDDI_VIDEO_PRESENT_SOURCE_ID SourceId = 0; SourceId < MAX_VIEWS; ++SourceId) + { + SIZE_T NumPathsFromSource = 0; + Status = pVidPnTopologyInterface->pfnGetNumPathsFromSource(hVidPnTopology, SourceId, &NumPathsFromSource); + if (Status == STATUS_GRAPHICS_SOURCE_NOT_IN_TOPOLOGY) + { + continue; + } + else if (!NT_SUCCESS(Status)) + { + BDD_LOG_ERROR3("pfnGetNumPathsFromSource failed with Status = 0x%I64x. hVidPnTopology = 0x%I64x, SourceId = 0x%I64x", + Status, hVidPnTopology, SourceId); + return Status; + } + else if (NumPathsFromSource > MAX_CHILDREN) + { + // This VidPn is not supported, which has already been set as the default + return STATUS_SUCCESS; + } + } + + // All sources succeeded so this VidPn is supported + pIsSupportedVidPn->IsVidPnSupported = TRUE; + return STATUS_SUCCESS; +} + +NTSTATUS BASIC_DISPLAY_DRIVER::RecommendFunctionalVidPn(_In_ CONST DXGKARG_RECOMMENDFUNCTIONALVIDPN* CONST pRecommendFunctionalVidPn) +{ + PAGED_CODE(); + + BDD_ASSERT(pRecommendFunctionalVidPn == NULL); + + return STATUS_GRAPHICS_NO_RECOMMENDED_FUNCTIONAL_VIDPN; +} + +NTSTATUS BASIC_DISPLAY_DRIVER::RecommendVidPnTopology(_In_ CONST DXGKARG_RECOMMENDVIDPNTOPOLOGY* CONST pRecommendVidPnTopology) +{ + PAGED_CODE(); + + BDD_ASSERT(pRecommendVidPnTopology == NULL); + + return STATUS_GRAPHICS_NO_RECOMMENDED_FUNCTIONAL_VIDPN; +} + +NTSTATUS BASIC_DISPLAY_DRIVER::RecommendMonitorModes(_In_ CONST DXGKARG_RECOMMENDMONITORMODES* CONST pRecommendMonitorModes) +{ + PAGED_CODE(); + + // This is always called to recommend modes for the monitor. The sample driver doesn't provide EDID for a monitor, so + // the OS prefills the list with default monitor modes. Since the required mode might not be in the list, it should + // be provided as a recommended mode. + return AddSingleMonitorMode(pRecommendMonitorModes); +} + +// Tell DMM about all the modes, etc. that are supported +NTSTATUS BASIC_DISPLAY_DRIVER::EnumVidPnCofuncModality(_In_ CONST DXGKARG_ENUMVIDPNCOFUNCMODALITY* CONST pEnumCofuncModality) +{ + PAGED_CODE(); + + BDD_ASSERT(pEnumCofuncModality != NULL); + + D3DKMDT_HVIDPNTOPOLOGY hVidPnTopology = 0; + D3DKMDT_HVIDPNSOURCEMODESET hVidPnSourceModeSet = 0; + D3DKMDT_HVIDPNTARGETMODESET hVidPnTargetModeSet = 0; + CONST DXGK_VIDPN_INTERFACE* pVidPnInterface = NULL; + CONST DXGK_VIDPNTOPOLOGY_INTERFACE* pVidPnTopologyInterface = NULL; + CONST DXGK_VIDPNSOURCEMODESET_INTERFACE* pVidPnSourceModeSetInterface = NULL; + CONST DXGK_VIDPNTARGETMODESET_INTERFACE* pVidPnTargetModeSetInterface = NULL; + CONST D3DKMDT_VIDPN_PRESENT_PATH* pVidPnPresentPath = NULL; + CONST D3DKMDT_VIDPN_PRESENT_PATH* pVidPnPresentPathTemp = NULL; // Used for AcquireNextPathInfo + CONST D3DKMDT_VIDPN_SOURCE_MODE* pVidPnPinnedSourceModeInfo = NULL; + CONST D3DKMDT_VIDPN_TARGET_MODE* pVidPnPinnedTargetModeInfo = NULL; + + // Get the VidPn Interface so we can get the 'Source Mode Set', 'Target Mode Set' and 'VidPn Topology' interfaces + NTSTATUS Status = m_DxgkInterface.DxgkCbQueryVidPnInterface(pEnumCofuncModality->hConstrainingVidPn, DXGK_VIDPN_INTERFACE_VERSION_V1, &pVidPnInterface); + if (!NT_SUCCESS(Status)) + { + BDD_LOG_ERROR2("DxgkCbQueryVidPnInterface failed with Status = 0x%I64x, hFunctionalVidPn = 0x%I64x", Status, pEnumCofuncModality->hConstrainingVidPn); + return Status; + } + + // Get the VidPn Topology interface so we can enumerate all paths + Status = pVidPnInterface->pfnGetTopology(pEnumCofuncModality->hConstrainingVidPn, &hVidPnTopology, &pVidPnTopologyInterface); + if (!NT_SUCCESS(Status)) + { + BDD_LOG_ERROR2("pfnGetTopology failed with Status = 0x%I64x, hFunctionalVidPn = 0x%I64x", Status, pEnumCofuncModality->hConstrainingVidPn); + return Status; + } + + // Get the first path before we start looping through them + Status = pVidPnTopologyInterface->pfnAcquireFirstPathInfo(hVidPnTopology, &pVidPnPresentPath); + if (!NT_SUCCESS(Status)) + { + BDD_LOG_ERROR2("pfnAcquireFirstPathInfo failed with Status = 0x%I64x, hVidPnTopology = 0x%I64x", Status, hVidPnTopology); + return Status; + } + + // Loop through all available paths. + while (Status != STATUS_GRAPHICS_NO_MORE_ELEMENTS_IN_DATASET) + { + // Get the Source Mode Set interface so the pinned mode can be retrieved + Status = pVidPnInterface->pfnAcquireSourceModeSet(pEnumCofuncModality->hConstrainingVidPn, + pVidPnPresentPath->VidPnSourceId, + &hVidPnSourceModeSet, + &pVidPnSourceModeSetInterface); + if (!NT_SUCCESS(Status)) + { + BDD_LOG_ERROR3("pfnAcquireSourceModeSet failed with Status = 0x%I64x, hConstrainingVidPn = 0x%I64x, SourceId = 0x%I64x", + Status, pEnumCofuncModality->hConstrainingVidPn, pVidPnPresentPath->VidPnSourceId); + break; + } + + // Get the pinned mode, needed when VidPnSource isn't pivot, and when VidPnTarget isn't pivot + Status = pVidPnSourceModeSetInterface->pfnAcquirePinnedModeInfo(hVidPnSourceModeSet, &pVidPnPinnedSourceModeInfo); + if (!NT_SUCCESS(Status)) + { + BDD_LOG_ERROR2("pfnAcquirePinnedModeInfo failed with Status = 0x%I64x, hVidPnSourceModeSet = 0x%I64x", Status, hVidPnSourceModeSet); + break; + } + + // SOURCE MODES: If this source mode isn't the pivot point, do work on the source mode set + if (!((pEnumCofuncModality->EnumPivotType == D3DKMDT_EPT_VIDPNSOURCE) && + (pEnumCofuncModality->EnumPivot.VidPnSourceId == pVidPnPresentPath->VidPnSourceId))) + { + // If there's no pinned source add possible modes (otherwise they've already been added) + if (pVidPnPinnedSourceModeInfo == NULL) + { + // Release the acquired source mode set, since going to create a new one to put all modes in + Status = pVidPnInterface->pfnReleaseSourceModeSet(pEnumCofuncModality->hConstrainingVidPn, hVidPnSourceModeSet); + if (!NT_SUCCESS(Status)) + { + BDD_LOG_ERROR3("pfnReleaseSourceModeSet failed with Status = 0x%I64x, hConstrainingVidPn = 0x%I64x, hVidPnSourceModeSet = 0x%I64x", + Status, pEnumCofuncModality->hConstrainingVidPn, hVidPnSourceModeSet); + break; + } + hVidPnSourceModeSet = 0; // Successfully released it + + // Create a new source mode set which will be added to the constraining VidPn with all the possible modes + Status = pVidPnInterface->pfnCreateNewSourceModeSet(pEnumCofuncModality->hConstrainingVidPn, + pVidPnPresentPath->VidPnSourceId, + &hVidPnSourceModeSet, + &pVidPnSourceModeSetInterface); + if (!NT_SUCCESS(Status)) + { + BDD_LOG_ERROR3("pfnCreateNewSourceModeSet failed with Status = 0x%I64x, hConstrainingVidPn = 0x%I64x, SourceId = 0x%I64x", + Status, pEnumCofuncModality->hConstrainingVidPn, pVidPnPresentPath->VidPnSourceId); + break; + } + + // Add the appropriate modes to the source mode set + { + Status = AddSingleSourceMode(pVidPnSourceModeSetInterface, hVidPnSourceModeSet, pVidPnPresentPath->VidPnSourceId); + } + + if (!NT_SUCCESS(Status)) + { + break; + } + + // Give DMM back the source modes just populated + Status = pVidPnInterface->pfnAssignSourceModeSet(pEnumCofuncModality->hConstrainingVidPn, pVidPnPresentPath->VidPnSourceId, hVidPnSourceModeSet); + if (!NT_SUCCESS(Status)) + { + BDD_LOG_ERROR4("pfnAssignSourceModeSet failed with Status = 0x%I64x, hConstrainingVidPn = 0x%I64x, SourceId = 0x%I64x, hVidPnSourceModeSet = 0x%I64x", + Status, pEnumCofuncModality->hConstrainingVidPn, pVidPnPresentPath->VidPnSourceId, hVidPnSourceModeSet); + break; + } + hVidPnSourceModeSet = 0; // Successfully assigned it (equivalent to releasing it) + } + }// End: SOURCE MODES + + // TARGET MODES: If this target mode isn't the pivot point, do work on the target mode set + if (!((pEnumCofuncModality->EnumPivotType == D3DKMDT_EPT_VIDPNTARGET) && + (pEnumCofuncModality->EnumPivot.VidPnTargetId == pVidPnPresentPath->VidPnTargetId))) + { + // Get the Target Mode Set interface so modes can be added if necessary + Status = pVidPnInterface->pfnAcquireTargetModeSet(pEnumCofuncModality->hConstrainingVidPn, + pVidPnPresentPath->VidPnTargetId, + &hVidPnTargetModeSet, + &pVidPnTargetModeSetInterface); + if (!NT_SUCCESS(Status)) + { + BDD_LOG_ERROR3("pfnAcquireTargetModeSet failed with Status = 0x%I64x, hConstrainingVidPn = 0x%I64x, TargetId = 0x%I64x", + Status, pEnumCofuncModality->hConstrainingVidPn, pVidPnPresentPath->VidPnTargetId); + break; + } + + Status = pVidPnTargetModeSetInterface->pfnAcquirePinnedModeInfo(hVidPnTargetModeSet, &pVidPnPinnedTargetModeInfo); + if (!NT_SUCCESS(Status)) + { + BDD_LOG_ERROR2("pfnAcquirePinnedModeInfo failed with Status = 0x%I64x, hVidPnTargetModeSet = 0x%I64x", Status, hVidPnTargetModeSet); + break; + } + + // If there's no pinned target add possible modes (otherwise they've already been added) + if (pVidPnPinnedTargetModeInfo == NULL) + { + // Release the acquired target mode set, since going to create a new one to put all modes in + Status = pVidPnInterface->pfnReleaseTargetModeSet(pEnumCofuncModality->hConstrainingVidPn, hVidPnTargetModeSet); + if (!NT_SUCCESS(Status)) + { + BDD_LOG_ASSERTION3("pfnReleaseTargetModeSet failed with Status = 0x%I64x, hConstrainingVidPn = 0x%I64x, hVidPnTargetModeSet = 0x%I64x", + Status, pEnumCofuncModality->hConstrainingVidPn, hVidPnTargetModeSet); + break; + } + hVidPnTargetModeSet = 0; // Successfully released it + + // Create a new target mode set which will be added to the constraining VidPn with all the possible modes + Status = pVidPnInterface->pfnCreateNewTargetModeSet(pEnumCofuncModality->hConstrainingVidPn, + pVidPnPresentPath->VidPnTargetId, + &hVidPnTargetModeSet, + &pVidPnTargetModeSetInterface); + if (!NT_SUCCESS(Status)) + { + BDD_LOG_ERROR3("pfnCreateNewTargetModeSet failed with Status = 0x%I64x, hConstrainingVidPn = 0x%I64x, TargetId = 0x%I64x", + Status, pEnumCofuncModality->hConstrainingVidPn, pVidPnPresentPath->VidPnTargetId); + break; + } + + Status = AddSingleTargetMode(pVidPnTargetModeSetInterface, hVidPnTargetModeSet, pVidPnPinnedSourceModeInfo, pVidPnPresentPath->VidPnSourceId); + + if (!NT_SUCCESS(Status)) + { + break; + } + + // Give DMM back the source modes just populated + Status = pVidPnInterface->pfnAssignTargetModeSet(pEnumCofuncModality->hConstrainingVidPn, pVidPnPresentPath->VidPnTargetId, hVidPnTargetModeSet); + if (!NT_SUCCESS(Status)) + { + BDD_LOG_ERROR4("pfnAssignTargetModeSet failed with Status = 0x%I64x, hConstrainingVidPn = 0x%I64x, TargetId = 0x%I64x, hVidPnTargetModeSet = 0x%I64x", + Status, pEnumCofuncModality->hConstrainingVidPn, pVidPnPresentPath->VidPnTargetId, hVidPnTargetModeSet); + break; + } + hVidPnTargetModeSet = 0; // Successfully assigned it (equivalent to releasing it) + } + else + { + // Release the pinned target as there's no other work to do + Status = pVidPnTargetModeSetInterface->pfnReleaseModeInfo(hVidPnTargetModeSet, pVidPnPinnedTargetModeInfo); + if (!NT_SUCCESS(Status)) + { + BDD_LOG_ASSERTION3("pfnReleaseModeInfo failed with Status = 0x%I64x, hVidPnTargetModeSet = 0x%I64x, pVidPnPinnedTargetModeInfo = 0x%I64x", + Status, hVidPnTargetModeSet, pVidPnPinnedTargetModeInfo); + break; + } + pVidPnPinnedTargetModeInfo = NULL; // Successfully released it + + // Release the acquired target mode set, since it is no longer needed + Status = pVidPnInterface->pfnReleaseTargetModeSet(pEnumCofuncModality->hConstrainingVidPn, hVidPnTargetModeSet); + if (!NT_SUCCESS(Status)) + { + BDD_LOG_ASSERTION3("pfnReleaseTargetModeSet failed with Status = 0x%I64x, hConstrainingVidPn = 0x%I64x, hVidPnTargetModeSet = 0x%I64x", + Status, pEnumCofuncModality->hConstrainingVidPn, hVidPnTargetModeSet); + break; + } + hVidPnTargetModeSet = 0; // Successfully released it + } + }// End: TARGET MODES + + // Nothing else needs the pinned source mode so release it + if (pVidPnPinnedSourceModeInfo != NULL) + { + Status = pVidPnSourceModeSetInterface->pfnReleaseModeInfo(hVidPnSourceModeSet, pVidPnPinnedSourceModeInfo); + if (!NT_SUCCESS(Status)) + { + BDD_LOG_ASSERTION3("pfnReleaseModeInfo failed with Status = 0x%I64x, hVidPnSourceModeSet = 0x%I64x, pVidPnPinnedSourceModeInfo = 0x%I64x", + Status, hVidPnSourceModeSet, pVidPnPinnedSourceModeInfo); + break; + } + pVidPnPinnedSourceModeInfo = NULL; // Successfully released it + } + + // With the pinned source mode now released, if the source mode set hasn't been released, release that as well + if (hVidPnSourceModeSet != 0) + { + Status = pVidPnInterface->pfnReleaseSourceModeSet(pEnumCofuncModality->hConstrainingVidPn, hVidPnSourceModeSet); + if (!NT_SUCCESS(Status)) + { + BDD_LOG_ERROR3("pfnReleaseSourceModeSet failed with Status = 0x%I64x, hConstrainingVidPn = 0x%I64x, hVidPnSourceModeSet = 0x%I64x", + Status, pEnumCofuncModality->hConstrainingVidPn, hVidPnSourceModeSet); + break; + } + hVidPnSourceModeSet = 0; // Successfully released it + } + + // If modifying support fields, need to modify a local version of a path structure since the retrieved one is const + D3DKMDT_VIDPN_PRESENT_PATH LocalVidPnPresentPath = *pVidPnPresentPath; + BOOLEAN SupportFieldsModified = FALSE; + + // SCALING: If this path's scaling isn't the pivot point, do work on the scaling support + if (!((pEnumCofuncModality->EnumPivotType == D3DKMDT_EPT_SCALING) && + (pEnumCofuncModality->EnumPivot.VidPnSourceId == pVidPnPresentPath->VidPnSourceId) && + (pEnumCofuncModality->EnumPivot.VidPnTargetId == pVidPnPresentPath->VidPnTargetId))) + { + // If the scaling is unpinned, then modify the scaling support field + if (pVidPnPresentPath->ContentTransformation.Scaling == D3DKMDT_VPPS_UNPINNED) + { + // Identity and centered scaling are supported, but not any stretch modes + RtlZeroMemory(&(LocalVidPnPresentPath.ContentTransformation.ScalingSupport), sizeof(D3DKMDT_VIDPN_PRESENT_PATH_SCALING_SUPPORT)); + LocalVidPnPresentPath.ContentTransformation.ScalingSupport.Identity = 1; + LocalVidPnPresentPath.ContentTransformation.ScalingSupport.Centered = 1; + SupportFieldsModified = TRUE; + } + } // End: SCALING + + // ROTATION: If this path's rotation isn't the pivot point, do work on the rotation support + if (!((pEnumCofuncModality->EnumPivotType != D3DKMDT_EPT_ROTATION) && + (pEnumCofuncModality->EnumPivot.VidPnSourceId == pVidPnPresentPath->VidPnSourceId) && + (pEnumCofuncModality->EnumPivot.VidPnTargetId == pVidPnPresentPath->VidPnTargetId))) + { + // If the rotation is unpinned, then modify the rotation support field + if (pVidPnPresentPath->ContentTransformation.Rotation == D3DKMDT_VPPR_UNPINNED) + { + LocalVidPnPresentPath.ContentTransformation.RotationSupport.Identity = 1; + // Sample supports only Rotate90 + LocalVidPnPresentPath.ContentTransformation.RotationSupport.Rotate90 = 1; + LocalVidPnPresentPath.ContentTransformation.RotationSupport.Rotate180 = 0; + LocalVidPnPresentPath.ContentTransformation.RotationSupport.Rotate270 = 0; + + // Since clone is not supported, should not support path-independent rotations + LocalVidPnPresentPath.ContentTransformation.RotationSupport.Offset0 = 1; + + SupportFieldsModified = TRUE; + } + } // End: ROTATION + + if (SupportFieldsModified) + { + // The correct path will be found by this function and the appropriate fields updated + Status = pVidPnTopologyInterface->pfnUpdatePathSupportInfo(hVidPnTopology, &LocalVidPnPresentPath); + if (!NT_SUCCESS(Status)) + { + BDD_LOG_ERROR2("pfnUpdatePathSupportInfo failed with Status = 0x%I64x, hVidPnTopology = 0x%I64x", Status, hVidPnTopology); + break; + } + } + + // Get the next path... + // (NOTE: This is the value of Status that will return STATUS_GRAPHICS_NO_MORE_ELEMENTS_IN_DATASET when it's time to quit the loop) + pVidPnPresentPathTemp = pVidPnPresentPath; + Status = pVidPnTopologyInterface->pfnAcquireNextPathInfo(hVidPnTopology, pVidPnPresentPathTemp, &pVidPnPresentPath); + if (!NT_SUCCESS(Status)) + { + BDD_LOG_ERROR3("pfnAcquireNextPathInfo failed with Status = 0x%I64x, hVidPnTopology = 0x%I64x, pVidPnPresentPathTemp = 0x%I64x", Status, hVidPnTopology, pVidPnPresentPathTemp); + break; + } + + // ...and release the last path + NTSTATUS TempStatus = pVidPnTopologyInterface->pfnReleasePathInfo(hVidPnTopology, pVidPnPresentPathTemp); + if (!NT_SUCCESS(TempStatus)) + { + BDD_LOG_ERROR3("pfnReleasePathInfo failed with Status = 0x%I64x, hVidPnTopology = 0x%I64x, pVidPnPresentPathTemp = 0x%I64x", TempStatus, hVidPnTopology, pVidPnPresentPathTemp); + Status = TempStatus; + break; + } + pVidPnPresentPathTemp = NULL; // Successfully released it + }// End: while loop for paths in topology + + // If quit the while loop normally, set the return value to success + if (Status == STATUS_GRAPHICS_NO_MORE_ELEMENTS_IN_DATASET) + { + Status = STATUS_SUCCESS; + } + + // Release any resources hanging around because the loop was quit early. + // Since in normal execution everything should be released by this point, TempStatus is initialized to a bogus error to be used as an + // assertion that if anything had to be released now (TempStatus changing) Status isn't successful. + NTSTATUS TempStatus = STATUS_NOT_FOUND; + + if ((pVidPnSourceModeSetInterface != NULL) && + (pVidPnPinnedSourceModeInfo != NULL)) + { + TempStatus = pVidPnSourceModeSetInterface->pfnReleaseModeInfo(hVidPnSourceModeSet, pVidPnPinnedSourceModeInfo); + BDD_ASSERT_CHK(NT_SUCCESS(TempStatus)); + } + + if ((pVidPnTargetModeSetInterface != NULL) && + (pVidPnPinnedTargetModeInfo != NULL)) + { + TempStatus = pVidPnTargetModeSetInterface->pfnReleaseModeInfo(hVidPnTargetModeSet, pVidPnPinnedTargetModeInfo); + BDD_ASSERT_CHK(NT_SUCCESS(TempStatus)); + } + + if (pVidPnPresentPath != NULL) + { + TempStatus = pVidPnTopologyInterface->pfnReleasePathInfo(hVidPnTopology, pVidPnPresentPath); + BDD_ASSERT_CHK(NT_SUCCESS(TempStatus)); + } + + if (pVidPnPresentPathTemp != NULL) + { + TempStatus = pVidPnTopologyInterface->pfnReleasePathInfo(hVidPnTopology, pVidPnPresentPathTemp); + BDD_ASSERT_CHK(NT_SUCCESS(TempStatus)); + } + + if (hVidPnSourceModeSet != 0) + { + TempStatus = pVidPnInterface->pfnReleaseSourceModeSet(pEnumCofuncModality->hConstrainingVidPn, hVidPnSourceModeSet); + BDD_ASSERT_CHK(NT_SUCCESS(TempStatus)); + } + + if (hVidPnTargetModeSet != 0) + { + TempStatus = pVidPnInterface->pfnReleaseTargetModeSet(pEnumCofuncModality->hConstrainingVidPn, hVidPnTargetModeSet); + BDD_ASSERT_CHK(NT_SUCCESS(TempStatus)); + } + + BDD_ASSERT_CHK(TempStatus == STATUS_NOT_FOUND || Status != STATUS_SUCCESS); + + return Status; +} + +NTSTATUS BASIC_DISPLAY_DRIVER::SetVidPnSourceVisibility(_In_ CONST DXGKARG_SETVIDPNSOURCEVISIBILITY* pSetVidPnSourceVisibility) +{ + PAGED_CODE(); + + BDD_ASSERT(pSetVidPnSourceVisibility != NULL); + BDD_ASSERT((pSetVidPnSourceVisibility->VidPnSourceId < MAX_VIEWS) || + (pSetVidPnSourceVisibility->VidPnSourceId == D3DDDI_ID_ALL)); + + UINT StartVidPnSourceId = (pSetVidPnSourceVisibility->VidPnSourceId == D3DDDI_ID_ALL) ? 0 : pSetVidPnSourceVisibility->VidPnSourceId; + UINT MaxVidPnSourceId = (pSetVidPnSourceVisibility->VidPnSourceId == D3DDDI_ID_ALL) ? MAX_VIEWS : pSetVidPnSourceVisibility->VidPnSourceId + 1; + + for (UINT SourceId = StartVidPnSourceId; SourceId < MaxVidPnSourceId; ++SourceId) + { + if (pSetVidPnSourceVisibility->Visible) + { + m_CurrentModes[SourceId].Flags.FullscreenPresent = TRUE; + } + else + { + BlackOutScreen(SourceId); + } + + // Store current visibility so it can be dealt with during Present call + m_CurrentModes[SourceId].Flags.SourceNotVisible = !(pSetVidPnSourceVisibility->Visible); + } + + return STATUS_SUCCESS; +} + +// NOTE: The value of pCommitVidPn->MonitorConnectivityChecks is ignored, since BDD is unable to recognize whether a monitor is connected or not +// The value of pCommitVidPn->hPrimaryAllocation is also ignored, since BDD is a display only driver and does not deal with allocations +NTSTATUS BASIC_DISPLAY_DRIVER::CommitVidPn(_In_ CONST DXGKARG_COMMITVIDPN* CONST pCommitVidPn) +{ + PAGED_CODE(); + + BDD_ASSERT(pCommitVidPn != NULL); + BDD_ASSERT(pCommitVidPn->AffectedVidPnSourceId < MAX_VIEWS); + + NTSTATUS Status; + SIZE_T NumPaths = 0; + D3DKMDT_HVIDPNTOPOLOGY hVidPnTopology = 0; + D3DKMDT_HVIDPNSOURCEMODESET hVidPnSourceModeSet = 0; + CONST DXGK_VIDPN_INTERFACE* pVidPnInterface = NULL; + CONST DXGK_VIDPNTOPOLOGY_INTERFACE* pVidPnTopologyInterface = NULL; + CONST DXGK_VIDPNSOURCEMODESET_INTERFACE* pVidPnSourceModeSetInterface = NULL; + CONST D3DKMDT_VIDPN_PRESENT_PATH* pVidPnPresentPath = NULL; + CONST D3DKMDT_VIDPN_SOURCE_MODE* pPinnedVidPnSourceModeInfo = NULL; + + // Check this CommitVidPn is for the mode change notification when monitor is in power off state. + if (pCommitVidPn->Flags.PathPoweredOff) + { + // Ignore the commitVidPn call for the mode change notification when monitor is in power off state. + Status = STATUS_SUCCESS; + goto CommitVidPnExit; + } + + // Get the VidPn Interface so we can get the 'Source Mode Set' and 'VidPn Topology' interfaces + Status = m_DxgkInterface.DxgkCbQueryVidPnInterface(pCommitVidPn->hFunctionalVidPn, DXGK_VIDPN_INTERFACE_VERSION_V1, &pVidPnInterface); + if (!NT_SUCCESS(Status)) + { + BDD_LOG_ERROR2("DxgkCbQueryVidPnInterface failed with Status = 0x%I64x, hFunctionalVidPn = 0x%I64x", Status, pCommitVidPn->hFunctionalVidPn); + goto CommitVidPnExit; + } + + // Get the VidPn Topology interface so can enumerate paths from source + Status = pVidPnInterface->pfnGetTopology(pCommitVidPn->hFunctionalVidPn, &hVidPnTopology, &pVidPnTopologyInterface); + if (!NT_SUCCESS(Status)) + { + BDD_LOG_ERROR2("pfnGetTopology failed with Status = 0x%I64x, hFunctionalVidPn = 0x%I64x", Status, pCommitVidPn->hFunctionalVidPn); + goto CommitVidPnExit; + } + + // Find out the number of paths now, if it's 0 don't bother with source mode set and pinned mode, just clear current and then quit + Status = pVidPnTopologyInterface->pfnGetNumPaths(hVidPnTopology, &NumPaths); + if (!NT_SUCCESS(Status)) + { + BDD_LOG_ERROR2("pfnGetNumPaths failed with Status = 0x%I64x, hVidPnTopology = 0x%I64x", Status, hVidPnTopology); + goto CommitVidPnExit; + } + + if (NumPaths != 0) + { + // Get the Source Mode Set interface so we can get the pinned mode + Status = pVidPnInterface->pfnAcquireSourceModeSet(pCommitVidPn->hFunctionalVidPn, + pCommitVidPn->AffectedVidPnSourceId, + &hVidPnSourceModeSet, + &pVidPnSourceModeSetInterface); + if (!NT_SUCCESS(Status)) + { + BDD_LOG_ERROR3("pfnAcquireSourceModeSet failed with Status = 0x%I64x, hFunctionalVidPn = 0x%I64x, SourceId = 0x%I64x", Status, pCommitVidPn->hFunctionalVidPn, pCommitVidPn->AffectedVidPnSourceId); + goto CommitVidPnExit; + } + + // Get the mode that is being pinned + Status = pVidPnSourceModeSetInterface->pfnAcquirePinnedModeInfo(hVidPnSourceModeSet, &pPinnedVidPnSourceModeInfo); + if (!NT_SUCCESS(Status)) + { + BDD_LOG_ERROR2("pfnAcquirePinnedModeInfo failed with Status = 0x%I64x, hFunctionalVidPn = 0x%I64x", Status, pCommitVidPn->hFunctionalVidPn); + goto CommitVidPnExit; + } + } + else + { + // This will cause the successful quit below + pPinnedVidPnSourceModeInfo = NULL; + } + + if (m_CurrentModes[pCommitVidPn->AffectedVidPnSourceId].FrameBuffer.Ptr && + !m_CurrentModes[pCommitVidPn->AffectedVidPnSourceId].Flags.DoNotMapOrUnmap) + { + Status = UnmapFrameBuffer(m_CurrentModes[pCommitVidPn->AffectedVidPnSourceId].FrameBuffer.Ptr, + m_CurrentModes[pCommitVidPn->AffectedVidPnSourceId].DispInfo.Pitch * m_CurrentModes[pCommitVidPn->AffectedVidPnSourceId].DispInfo.Height); + m_CurrentModes[pCommitVidPn->AffectedVidPnSourceId].FrameBuffer.Ptr = NULL; + m_CurrentModes[pCommitVidPn->AffectedVidPnSourceId].Flags.FrameBufferIsActive = FALSE; + + if (!NT_SUCCESS(Status)) + { + goto CommitVidPnExit; + } + } + + if (pPinnedVidPnSourceModeInfo == NULL) + { + // There is no mode to pin on this source, any old paths here have already been cleared + Status = STATUS_SUCCESS; + goto CommitVidPnExit; + } + + Status = IsVidPnSourceModeFieldsValid(pPinnedVidPnSourceModeInfo); + if (!NT_SUCCESS(Status)) + { + goto CommitVidPnExit; + } + + // Get the number of paths from this source so we can loop through all paths + SIZE_T NumPathsFromSource = 0; + Status = pVidPnTopologyInterface->pfnGetNumPathsFromSource(hVidPnTopology, pCommitVidPn->AffectedVidPnSourceId, &NumPathsFromSource); + if (!NT_SUCCESS(Status)) + { + BDD_LOG_ERROR2("pfnGetNumPathsFromSource failed with Status = 0x%I64x, hVidPnTopology = 0x%I64x", Status, hVidPnTopology); + goto CommitVidPnExit; + } + + // Loop through all paths to set this mode + for (SIZE_T PathIndex = 0; PathIndex < NumPathsFromSource; ++PathIndex) + { + // Get the target id for this path + D3DDDI_VIDEO_PRESENT_TARGET_ID TargetId = D3DDDI_ID_UNINITIALIZED; + Status = pVidPnTopologyInterface->pfnEnumPathTargetsFromSource(hVidPnTopology, pCommitVidPn->AffectedVidPnSourceId, PathIndex, &TargetId); + if (!NT_SUCCESS(Status)) + { + BDD_LOG_ERROR4("pfnEnumPathTargetsFromSource failed with Status = 0x%I64x, hVidPnTopology = 0x%I64x, SourceId = 0x%I64x, PathIndex = 0x%I64x", + Status, hVidPnTopology, pCommitVidPn->AffectedVidPnSourceId, PathIndex); + goto CommitVidPnExit; + } + + // Get the actual path info + Status = pVidPnTopologyInterface->pfnAcquirePathInfo(hVidPnTopology, pCommitVidPn->AffectedVidPnSourceId, TargetId, &pVidPnPresentPath); + if (!NT_SUCCESS(Status)) + { + BDD_LOG_ERROR4("pfnAcquirePathInfo failed with Status = 0x%I64x, hVidPnTopology = 0x%I64x, SourceId = 0x%I64x, TargetId = 0x%I64x", + Status, hVidPnTopology, pCommitVidPn->AffectedVidPnSourceId, TargetId); + goto CommitVidPnExit; + } + + Status = IsVidPnPathFieldsValid(pVidPnPresentPath); + if (!NT_SUCCESS(Status)) + { + goto CommitVidPnExit; + } + + Status = SetSourceModeAndPath(pPinnedVidPnSourceModeInfo, pVidPnPresentPath); + if (!NT_SUCCESS(Status)) + { + goto CommitVidPnExit; + } + + Status = pVidPnTopologyInterface->pfnReleasePathInfo(hVidPnTopology, pVidPnPresentPath); + if (!NT_SUCCESS(Status)) + { + BDD_LOG_ERROR3("pfnReleasePathInfo failed with Status = 0x%I64x, hVidPnTopoogy = 0x%I64x, pVidPnPresentPath = 0x%I64x", + Status, hVidPnTopology, pVidPnPresentPath); + goto CommitVidPnExit; + } + pVidPnPresentPath = NULL; // Successfully released it + } + +CommitVidPnExit: + + NTSTATUS TempStatus; + + if ((pVidPnSourceModeSetInterface != NULL) && + (hVidPnSourceModeSet != 0) && + (pPinnedVidPnSourceModeInfo != NULL)) + { + TempStatus = pVidPnSourceModeSetInterface->pfnReleaseModeInfo(hVidPnSourceModeSet, pPinnedVidPnSourceModeInfo); + NT_ASSERT(NT_SUCCESS(TempStatus)); + } + + if ((pVidPnInterface != NULL) && + (pCommitVidPn->hFunctionalVidPn != 0) && + (hVidPnSourceModeSet != 0)) + { + TempStatus = pVidPnInterface->pfnReleaseSourceModeSet(pCommitVidPn->hFunctionalVidPn, hVidPnSourceModeSet); + NT_ASSERT(NT_SUCCESS(TempStatus)); + } + + if ((pVidPnTopologyInterface != NULL) && + (hVidPnTopology != 0) && + (pVidPnPresentPath != NULL)) + { + TempStatus = pVidPnTopologyInterface->pfnReleasePathInfo(hVidPnTopology, pVidPnPresentPath); + NT_ASSERT(NT_SUCCESS(TempStatus)); + } + + return Status; +} + +NTSTATUS BASIC_DISPLAY_DRIVER::UpdateActiveVidPnPresentPath(_In_ CONST DXGKARG_UPDATEACTIVEVIDPNPRESENTPATH* CONST pUpdateActiveVidPnPresentPath) +{ + PAGED_CODE(); + + BDD_ASSERT(pUpdateActiveVidPnPresentPath != NULL); + + NTSTATUS Status = IsVidPnPathFieldsValid(&(pUpdateActiveVidPnPresentPath->VidPnPresentPathInfo)); + if (!NT_SUCCESS(Status)) + { + return Status; + } + + // Mark the next present as fullscreen to make sure the full rotation comes through + m_CurrentModes[pUpdateActiveVidPnPresentPath->VidPnPresentPathInfo.VidPnSourceId].Flags.FullscreenPresent = TRUE; + + m_CurrentModes[pUpdateActiveVidPnPresentPath->VidPnPresentPathInfo.VidPnSourceId].Rotation = pUpdateActiveVidPnPresentPath->VidPnPresentPathInfo.ContentTransformation.Rotation; + + return STATUS_SUCCESS; +} + +// +// Private BDD DMM functions +// + +NTSTATUS BASIC_DISPLAY_DRIVER::SetSourceModeAndPath(CONST D3DKMDT_VIDPN_SOURCE_MODE* pSourceMode, + CONST D3DKMDT_VIDPN_PRESENT_PATH* pPath) +{ + PAGED_CODE(); + + CURRENT_BDD_MODE* pCurrentBddMode = &m_CurrentModes[pPath->VidPnSourceId]; + + NTSTATUS Status = STATUS_SUCCESS; + pCurrentBddMode->Scaling = pPath->ContentTransformation.Scaling; + pCurrentBddMode->SrcModeWidth = pSourceMode->Format.Graphics.PrimSurfSize.cx; + pCurrentBddMode->SrcModeHeight = pSourceMode->Format.Graphics.PrimSurfSize.cy; + pCurrentBddMode->Rotation = pPath->ContentTransformation.Rotation; + + + if (!pCurrentBddMode->Flags.DoNotMapOrUnmap) + { + // Map the new frame buffer + BDD_ASSERT(pCurrentBddMode->FrameBuffer.Ptr == NULL); + Status = MapFrameBuffer(pCurrentBddMode->DispInfo.PhysicAddress, + pCurrentBddMode->DispInfo.Pitch * pCurrentBddMode->DispInfo.Height, + &(pCurrentBddMode->FrameBuffer.Ptr)); + } + + if (NT_SUCCESS(Status)) + { + + pCurrentBddMode->Flags.FrameBufferIsActive = TRUE; + BlackOutScreen(pPath->VidPnSourceId); + + // Mark that the next present should be fullscreen so the screen doesn't go from black to actual pixels one dirty rect at a time. + pCurrentBddMode->Flags.FullscreenPresent = TRUE; + } + + return Status; +} + + +NTSTATUS BASIC_DISPLAY_DRIVER::IsVidPnPathFieldsValid(CONST D3DKMDT_VIDPN_PRESENT_PATH* pPath) const +{ + PAGED_CODE(); + + if (pPath->VidPnSourceId >= MAX_VIEWS) + { + BDD_LOG_ERROR2("VidPnSourceId is 0x%I64x is too high (MAX_VIEWS is 0x%I64x)", + pPath->VidPnSourceId, MAX_VIEWS); + return STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE; + } + else if (pPath->VidPnTargetId >= MAX_CHILDREN) + { + BDD_LOG_ERROR2("VidPnTargetId is 0x%I64x is too high (MAX_CHILDREN is 0x%I64x)", + pPath->VidPnTargetId, MAX_CHILDREN); + return STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET; + } + else if (pPath->GammaRamp.Type != D3DDDI_GAMMARAMP_DEFAULT) + { + BDD_LOG_ERROR1("pPath contains a gamma ramp (0x%I64x)", pPath->GammaRamp.Type); + return STATUS_GRAPHICS_GAMMA_RAMP_NOT_SUPPORTED; + } + else if ((pPath->ContentTransformation.Scaling != D3DKMDT_VPPS_IDENTITY) && + (pPath->ContentTransformation.Scaling != D3DKMDT_VPPS_CENTERED) && + (pPath->ContentTransformation.Scaling != D3DKMDT_VPPS_NOTSPECIFIED) && + (pPath->ContentTransformation.Scaling != D3DKMDT_VPPS_UNINITIALIZED)) + { + BDD_LOG_ERROR1("pPath contains a non-identity scaling (0x%I64x)", pPath->ContentTransformation.Scaling); + return STATUS_GRAPHICS_VIDPN_MODALITY_NOT_SUPPORTED; + } + else if ((pPath->ContentTransformation.Rotation != D3DKMDT_VPPR_IDENTITY) && + (pPath->ContentTransformation.Rotation != D3DKMDT_VPPR_ROTATE90) && + (pPath->ContentTransformation.Rotation != D3DKMDT_VPPR_NOTSPECIFIED) && + (pPath->ContentTransformation.Rotation != D3DKMDT_VPPR_UNINITIALIZED)) + { + BDD_LOG_ERROR1("pPath contains a not-supported rotation (0x%I64x)", pPath->ContentTransformation.Rotation); + return STATUS_GRAPHICS_VIDPN_MODALITY_NOT_SUPPORTED; + } + else if ((pPath->VidPnTargetColorBasis != D3DKMDT_CB_SCRGB) && + (pPath->VidPnTargetColorBasis != D3DKMDT_CB_UNINITIALIZED)) + { + BDD_LOG_ERROR1("pPath has a non-linear RGB color basis (0x%I64x)", pPath->VidPnTargetColorBasis); + return STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE_MODE; + } + else + { + return STATUS_SUCCESS; + } +} + +NTSTATUS BASIC_DISPLAY_DRIVER::IsVidPnSourceModeFieldsValid(CONST D3DKMDT_VIDPN_SOURCE_MODE* pSourceMode) const +{ + PAGED_CODE(); + + if (pSourceMode->Type != D3DKMDT_RMT_GRAPHICS) + { + BDD_LOG_ERROR1("pSourceMode is a non-graphics mode (0x%I64x)", pSourceMode->Type); + return STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE_MODE; + } + else if ((pSourceMode->Format.Graphics.ColorBasis != D3DKMDT_CB_SCRGB) && + (pSourceMode->Format.Graphics.ColorBasis != D3DKMDT_CB_UNINITIALIZED)) + { + BDD_LOG_ERROR1("pSourceMode has a non-linear RGB color basis (0x%I64x)", pSourceMode->Format.Graphics.ColorBasis); + return STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE_MODE; + } + else if (pSourceMode->Format.Graphics.PixelValueAccessMode != D3DKMDT_PVAM_DIRECT) + { + BDD_LOG_ERROR1("pSourceMode has a palettized access mode (0x%I64x)", pSourceMode->Format.Graphics.PixelValueAccessMode); + return STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE_MODE; + } + else + { + for (UINT PelFmtIdx = 0; PelFmtIdx < ARRAYSIZE(gBddPixelFormats); ++PelFmtIdx) + { + if (pSourceMode->Format.Graphics.PixelFormat == gBddPixelFormats[PelFmtIdx]) + { + return STATUS_SUCCESS; + } + } + + BDD_LOG_ERROR1("pSourceMode has an unknown pixel format (0x%I64x)", pSourceMode->Format.Graphics.PixelFormat); + return STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE_MODE; + } +} + + + +// Add more mode from the table. +struct SampleSourceMode +{ + UINT ModeWidth; + UINT ModeHeight; +}; + +// The driver will advertise all modes that fit within the actual required mode (see AddSingleSourceMode below) +const static SampleSourceMode C_SampleSourceMode[] = {{800,600},{1024,768},{1152,864},{1280,800},{1280,1024},{1400,1050},{1600,1200},{1680,1050},{1920,1200}}; +const static UINT C_SampleSourceModeMax = sizeof(C_SampleSourceMode)/sizeof(C_SampleSourceMode[0]); + +NTSTATUS BASIC_DISPLAY_DRIVER::AddSingleSourceMode(_In_ CONST DXGK_VIDPNSOURCEMODESET_INTERFACE* pVidPnSourceModeSetInterface, + D3DKMDT_HVIDPNSOURCEMODESET hVidPnSourceModeSet, + D3DDDI_VIDEO_PRESENT_SOURCE_ID SourceId) +{ + PAGED_CODE(); + + // There is only one source format supported by display-only drivers, but more can be added in a + // full WDDM driver if the hardware supports them + for (UINT PelFmtIdx = 0; PelFmtIdx < ARRAYSIZE(gBddPixelFormats); ++PelFmtIdx) + { + // Create new mode info that will be populated + D3DKMDT_VIDPN_SOURCE_MODE* pVidPnSourceModeInfo = NULL; + NTSTATUS Status = pVidPnSourceModeSetInterface->pfnCreateNewModeInfo(hVidPnSourceModeSet, &pVidPnSourceModeInfo); + if (!NT_SUCCESS(Status)) + { + // If failed to create a new mode info, mode doesn't need to be released since it was never created + BDD_LOG_ERROR2("pfnCreateNewModeInfo failed with Status = 0x%I64x, hVidPnSourceModeSet = 0x%I64x", Status, hVidPnSourceModeSet); + return Status; + } + + // Populate mode info with values from current mode and hard-coded values + // Always report 32 bpp format, this will be color converted during the present if the mode is < 32bpp + pVidPnSourceModeInfo->Type = D3DKMDT_RMT_GRAPHICS; + pVidPnSourceModeInfo->Format.Graphics.PrimSurfSize.cx = m_CurrentModes[SourceId].DispInfo.Width; + pVidPnSourceModeInfo->Format.Graphics.PrimSurfSize.cy = m_CurrentModes[SourceId].DispInfo.Height; + pVidPnSourceModeInfo->Format.Graphics.VisibleRegionSize = pVidPnSourceModeInfo->Format.Graphics.PrimSurfSize; + pVidPnSourceModeInfo->Format.Graphics.Stride = m_CurrentModes[SourceId].DispInfo.Pitch; + pVidPnSourceModeInfo->Format.Graphics.PixelFormat = gBddPixelFormats[PelFmtIdx]; + pVidPnSourceModeInfo->Format.Graphics.ColorBasis = D3DKMDT_CB_SCRGB; + pVidPnSourceModeInfo->Format.Graphics.PixelValueAccessMode = D3DKMDT_PVAM_DIRECT; + + // Add the mode to the source mode set + Status = pVidPnSourceModeSetInterface->pfnAddMode(hVidPnSourceModeSet, pVidPnSourceModeInfo); + if (!NT_SUCCESS(Status)) + { + // If adding the mode failed, release the mode, if this doesn't work there is nothing that can be done, some memory will get leaked + NTSTATUS TempStatus = pVidPnSourceModeSetInterface->pfnReleaseModeInfo(hVidPnSourceModeSet, pVidPnSourceModeInfo); + UNREFERENCED_PARAMETER(TempStatus); + NT_ASSERT(NT_SUCCESS(TempStatus)); + + if (Status != STATUS_GRAPHICS_MODE_ALREADY_IN_MODESET) + { + BDD_LOG_ERROR3("pfnAddMode failed with Status = 0x%I64x, hVidPnSourceModeSet = 0x%I64x, pVidPnSourceModeInfo = 0x%I64x", Status, hVidPnSourceModeSet, pVidPnSourceModeInfo); + return Status; + } + } + } + + UINT WidthMax = m_CurrentModes[SourceId].DispInfo.Width; + UINT HeightMax = m_CurrentModes[SourceId].DispInfo.Height; + + // Add all predefined modes that fit within the bounds of the required (POST) mode + for (UINT ModeIndex = 0; ModeIndex < C_SampleSourceModeMax; ++ModeIndex) + { + if (C_SampleSourceMode[ModeIndex].ModeWidth > WidthMax) + { + break; + } + else if (C_SampleSourceMode[ModeIndex].ModeWidth == WidthMax) + { + if(C_SampleSourceMode[ModeIndex].ModeHeight >= HeightMax) + { + break; + } + } + else + { + if(C_SampleSourceMode[ModeIndex].ModeHeight > HeightMax) + { + continue; + } + } + + // There is only one source format supported by display-only drivers, but more can be added in a + // full WDDM driver if the hardware supports them + for (UINT PelFmtIdx = 0; PelFmtIdx < ARRAYSIZE(gBddPixelFormats); ++PelFmtIdx) + { + // Create new mode info that will be populated + D3DKMDT_VIDPN_SOURCE_MODE* pVidPnSourceModeInfo = NULL; + NTSTATUS Status = pVidPnSourceModeSetInterface->pfnCreateNewModeInfo(hVidPnSourceModeSet, &pVidPnSourceModeInfo); + if (!NT_SUCCESS(Status)) + { + // If failed to create a new mode info, continuing to the next mode and trying again isn't going to be at all helpful, so return + // Also, mode doesn't need to be released since it was never created + BDD_LOG_ERROR2("pfnCreateNewModeInfo failed with Status = 0x%I64x, hVidPnSourceModeSet = 0x%I64x", Status, hVidPnSourceModeSet); + return Status; + } + + // Populate mode info with values from mode at ModeIndex and hard-coded values + // Always report 32 bpp format, this will be color converted during the present if the mode at ModeIndex was < 32bpp + pVidPnSourceModeInfo->Type = D3DKMDT_RMT_GRAPHICS; + pVidPnSourceModeInfo->Format.Graphics.PrimSurfSize.cx = C_SampleSourceMode[ModeIndex].ModeWidth; + pVidPnSourceModeInfo->Format.Graphics.PrimSurfSize.cy = C_SampleSourceMode[ModeIndex].ModeHeight; + pVidPnSourceModeInfo->Format.Graphics.VisibleRegionSize = pVidPnSourceModeInfo->Format.Graphics.PrimSurfSize; + pVidPnSourceModeInfo->Format.Graphics.Stride = 4*C_SampleSourceMode[ModeIndex].ModeWidth; + pVidPnSourceModeInfo->Format.Graphics.PixelFormat = gBddPixelFormats[PelFmtIdx]; + pVidPnSourceModeInfo->Format.Graphics.ColorBasis = D3DKMDT_CB_SCRGB; + pVidPnSourceModeInfo->Format.Graphics.PixelValueAccessMode = D3DKMDT_PVAM_DIRECT; + + // Add the mode to the source mode set + Status = pVidPnSourceModeSetInterface->pfnAddMode(hVidPnSourceModeSet, pVidPnSourceModeInfo); + if (!NT_SUCCESS(Status)) + { + if (Status != STATUS_GRAPHICS_MODE_ALREADY_IN_MODESET) + { + BDD_LOG_ERROR3("pfnAddMode failed with Status = 0x%I64x, hVidPnSourceModeSet = 0x%I64x, pVidPnSourceModeInfo = 0x%I64x", Status, hVidPnSourceModeSet, pVidPnSourceModeInfo); + } + + // If adding the mode failed, release the mode, if this doesn't work there is nothing that can be done, some memory will get leaked, continue to next mode anyway + Status = pVidPnSourceModeSetInterface->pfnReleaseModeInfo(hVidPnSourceModeSet, pVidPnSourceModeInfo); + BDD_ASSERT_CHK(NT_SUCCESS(Status)); + } + } + } + + + return STATUS_SUCCESS; +} + + +// Add the current mode information (acquired from the POST frame buffer) as the target mode. +NTSTATUS BASIC_DISPLAY_DRIVER::AddSingleTargetMode(_In_ CONST DXGK_VIDPNTARGETMODESET_INTERFACE* pVidPnTargetModeSetInterface, + D3DKMDT_HVIDPNTARGETMODESET hVidPnTargetModeSet, + _In_opt_ CONST D3DKMDT_VIDPN_SOURCE_MODE* pVidPnPinnedSourceModeInfo, + D3DDDI_VIDEO_PRESENT_SOURCE_ID SourceId) +{ + PAGED_CODE(); + + D3DKMDT_VIDPN_TARGET_MODE* pVidPnTargetModeInfo = NULL; + NTSTATUS Status = pVidPnTargetModeSetInterface->pfnCreateNewModeInfo(hVidPnTargetModeSet, &pVidPnTargetModeInfo); + if (!NT_SUCCESS(Status)) + { + // If failed to create a new mode info, mode doesn't need to be released since it was never created + BDD_LOG_ERROR2("pfnCreateNewModeInfo failed with Status = 0x%I64x, hVidPnTargetModeSet = 0x%I64x", Status, hVidPnTargetModeSet); + return Status; + } + + pVidPnTargetModeInfo->VideoSignalInfo.VideoStandard = D3DKMDT_VSS_OTHER; + UNREFERENCED_PARAMETER(pVidPnPinnedSourceModeInfo); + pVidPnTargetModeInfo->VideoSignalInfo.TotalSize.cx = m_CurrentModes[SourceId].DispInfo.Width; + pVidPnTargetModeInfo->VideoSignalInfo.TotalSize.cy = m_CurrentModes[SourceId].DispInfo.Height; + pVidPnTargetModeInfo->VideoSignalInfo.ActiveSize = pVidPnTargetModeInfo->VideoSignalInfo.TotalSize; + pVidPnTargetModeInfo->VideoSignalInfo.VSyncFreq.Numerator = D3DKMDT_FREQUENCY_NOTSPECIFIED; + pVidPnTargetModeInfo->VideoSignalInfo.VSyncFreq.Denominator = D3DKMDT_FREQUENCY_NOTSPECIFIED; + pVidPnTargetModeInfo->VideoSignalInfo.HSyncFreq.Numerator = D3DKMDT_FREQUENCY_NOTSPECIFIED; + pVidPnTargetModeInfo->VideoSignalInfo.HSyncFreq.Denominator = D3DKMDT_FREQUENCY_NOTSPECIFIED; + pVidPnTargetModeInfo->VideoSignalInfo.PixelRate = D3DKMDT_FREQUENCY_NOTSPECIFIED; + pVidPnTargetModeInfo->VideoSignalInfo.ScanLineOrdering = D3DDDI_VSSLO_PROGRESSIVE; + // We add this as PREFERRED since it is the only supported target + pVidPnTargetModeInfo->Preference = D3DKMDT_MP_PREFERRED; + + Status = pVidPnTargetModeSetInterface->pfnAddMode(hVidPnTargetModeSet, pVidPnTargetModeInfo); + if (!NT_SUCCESS(Status)) + { + if (Status != STATUS_GRAPHICS_MODE_ALREADY_IN_MODESET) + { + BDD_LOG_ERROR3("pfnAddMode failed with Status = 0x%I64x, hVidPnTargetModeSet = 0x%I64x, pVidPnTargetModeInfo = 0x%I64x", Status, hVidPnTargetModeSet, pVidPnTargetModeInfo); + } + else + { + Status = STATUS_SUCCESS; + } + + // If adding the mode failed, release the mode, if this doesn't work there is nothing that can be done, some memory will get leaked + NTSTATUS TempStatus = pVidPnTargetModeSetInterface->pfnReleaseModeInfo(hVidPnTargetModeSet, pVidPnTargetModeInfo); + UNREFERENCED_PARAMETER(TempStatus); + NT_ASSERT(NT_SUCCESS(TempStatus)); + return Status; + } + else + { + // If AddMode succeeded with something other than STATUS_SUCCESS treat it as such anyway when propagating up + return STATUS_SUCCESS; + } +} + + +NTSTATUS BASIC_DISPLAY_DRIVER::AddSingleMonitorMode(_In_ CONST DXGKARG_RECOMMENDMONITORMODES* CONST pRecommendMonitorModes) +{ + PAGED_CODE(); + + D3DKMDT_MONITOR_SOURCE_MODE* pMonitorSourceMode = NULL; + NTSTATUS Status = pRecommendMonitorModes->pMonitorSourceModeSetInterface->pfnCreateNewModeInfo(pRecommendMonitorModes->hMonitorSourceModeSet, &pMonitorSourceMode); + if (!NT_SUCCESS(Status)) + { + // If failed to create a new mode info, mode doesn't need to be released since it was never created + BDD_LOG_ERROR2("pfnCreateNewModeInfo failed with Status = 0x%I64x, hMonitorSourceModeSet = 0x%I64x", Status, pRecommendMonitorModes->hMonitorSourceModeSet); + return Status; + } + + D3DDDI_VIDEO_PRESENT_SOURCE_ID CorrespondingSourceId = FindSourceForTarget(pRecommendMonitorModes->VideoPresentTargetId, TRUE); + + // Since we don't know the real monitor timing information, just use the current display mode (from the POST device) with unknown frequencies + pMonitorSourceMode->VideoSignalInfo.VideoStandard = D3DKMDT_VSS_OTHER; + pMonitorSourceMode->VideoSignalInfo.TotalSize.cx = m_CurrentModes[CorrespondingSourceId].DispInfo.Width; + pMonitorSourceMode->VideoSignalInfo.TotalSize.cy = m_CurrentModes[CorrespondingSourceId].DispInfo.Height; + pMonitorSourceMode->VideoSignalInfo.ActiveSize = pMonitorSourceMode->VideoSignalInfo.TotalSize; + pMonitorSourceMode->VideoSignalInfo.VSyncFreq.Numerator = D3DKMDT_FREQUENCY_NOTSPECIFIED; + pMonitorSourceMode->VideoSignalInfo.VSyncFreq.Denominator = D3DKMDT_FREQUENCY_NOTSPECIFIED; + pMonitorSourceMode->VideoSignalInfo.HSyncFreq.Numerator = D3DKMDT_FREQUENCY_NOTSPECIFIED; + pMonitorSourceMode->VideoSignalInfo.HSyncFreq.Denominator = D3DKMDT_FREQUENCY_NOTSPECIFIED; + pMonitorSourceMode->VideoSignalInfo.PixelRate = D3DKMDT_FREQUENCY_NOTSPECIFIED; + pMonitorSourceMode->VideoSignalInfo.ScanLineOrdering = D3DDDI_VSSLO_PROGRESSIVE; + + // We set the preference to PREFERRED since this is the only supported mode + pMonitorSourceMode->Origin = D3DKMDT_MCO_DRIVER; + pMonitorSourceMode->Preference = D3DKMDT_MP_PREFERRED; + pMonitorSourceMode->ColorBasis = D3DKMDT_CB_SRGB; + pMonitorSourceMode->ColorCoeffDynamicRanges.FirstChannel = 8; + pMonitorSourceMode->ColorCoeffDynamicRanges.SecondChannel = 8; + pMonitorSourceMode->ColorCoeffDynamicRanges.ThirdChannel = 8; + pMonitorSourceMode->ColorCoeffDynamicRanges.FourthChannel = 8; + + Status = pRecommendMonitorModes->pMonitorSourceModeSetInterface->pfnAddMode(pRecommendMonitorModes->hMonitorSourceModeSet, pMonitorSourceMode); + if (!NT_SUCCESS(Status)) + { + if (Status != STATUS_GRAPHICS_MODE_ALREADY_IN_MODESET) + { + BDD_LOG_ERROR3("pfnAddMode failed with Status = 0x%I64x, hMonitorSourceModeSet = 0x%I64x, pMonitorSourceMode = 0x%I64x", + Status, pRecommendMonitorModes->hMonitorSourceModeSet, pMonitorSourceMode); + } + else + { + Status = STATUS_SUCCESS; + } + + // If adding the mode failed, release the mode, if this doesn't work there is nothing that can be done, some memory will get leaked + NTSTATUS TempStatus = pRecommendMonitorModes->pMonitorSourceModeSetInterface->pfnReleaseModeInfo(pRecommendMonitorModes->hMonitorSourceModeSet, pMonitorSourceMode); + UNREFERENCED_PARAMETER(TempStatus); + NT_ASSERT(NT_SUCCESS(TempStatus)); + return Status; + } + else + { + // If AddMode succeeded with something other than STATUS_SUCCESS treat it as such anyway when propagating up + return STATUS_SUCCESS; + } +} + diff --git a/video/KMDOD/bdd_errorlog.hxx b/video/KMDOD/bdd_errorlog.hxx new file mode 100644 index 000000000..f9c394f83 --- /dev/null +++ b/video/KMDOD/bdd_errorlog.hxx @@ -0,0 +1,84 @@ +/******************************Module*Header*******************************\ +* Module Name: BDD_ErrorLog.hxx +* +* Basic Display Driver Logging Macros +* +* +* Copyright (c) 2010 Microsoft Corporation +* +\**************************************************************************/ +#ifndef _BDD_ERRORLOG_HXX_ +#define _BDD_ERRORLOG_HXX_ + +#define BDD_LOG_ERROR0(Msg) +#define BDD_LOG_ERROR1(Msg,Param1) +#define BDD_LOG_ERROR2(Msg,Param1,Param2) +#define BDD_LOG_ERROR3(Msg,Param1,Param2,Param3) +#define BDD_LOG_ERROR4(Msg,Param1,Param2,Param3,Param4) +#define BDD_LOG_ERROR5(Msg,Param1,Param2,Param3,Param4,Param5) + +// +// Warnings +// + +#define BDD_LOG_WARNING0(Msg) +#define BDD_LOG_WARNING1(Msg,Param1) +#define BDD_LOG_WARNING2(Msg,Param1,Param2) +#define BDD_LOG_WARNING3(Msg,Param1,Param2,Param3) +#define BDD_LOG_WARNING4(Msg,Param1,Param2,Param3,Param4) +#define BDD_LOG_WARNING5(Msg,Param1,Param2,Param3,Param4,Param5) + +// +// Events (i.e. low-frequency tracing) +// + +#define BDD_LOG_EVENT0(Msg) +#define BDD_LOG_EVENT1(Msg,Param1) +#define BDD_LOG_EVENT2(Msg,Param1,Param2) +#define BDD_LOG_EVENT3(Msg,Param1,Param2,Param3) +#define BDD_LOG_EVENT4(Msg,Param1,Param2,Param3,Param4) +#define BDD_LOG_EVENT5(Msg,Param1,Param2,Param3,Param4,Param5) + +// +// Information (i.e. high-frequency tracing) +// + +#define BDD_LOG_INFORMATION0(Msg) +#define BDD_LOG_INFORMATION1(Msg,Param1) +#define BDD_LOG_INFORMATION2(Msg,Param1,Param2) +#define BDD_LOG_INFORMATION3(Msg,Param1,Param2,Param3) +#define BDD_LOG_INFORMATION4(Msg,Param1,Param2,Param3,Param4) +#define BDD_LOG_INFORMATION5(Msg,Param1,Param2,Param3,Param4,Param5) + +// +// Low resource logging macros. +// + +#define BDD_LOG_LOW_RESOURCE0(Msg) +#define BDD_LOG_LOW_RESOURCE1(Msg,Param1) +#define BDD_LOG_LOW_RESOURCE2(Msg,Param1,Param2) +#define BDD_LOG_LOW_RESOURCE3(Msg,Param1,Param2,Param3) +#define BDD_LOG_LOW_RESOURCE4(Msg,Param1,Param2,Param3,Param4) +#define BDD_LOG_LOW_RESOURCE5(Msg,Param1,Param2,Param3,Param4,Param5) + +// +// Assertion logging macros. +// + +#define BDD_LOG_ASSERTION0(Msg) NT_ASSERT(FALSE) +#define BDD_LOG_ASSERTION1(Msg,Param1) NT_ASSERT(FALSE) +#define BDD_LOG_ASSERTION2(Msg,Param1,Param2) NT_ASSERT(FALSE) +#define BDD_LOG_ASSERTION3(Msg,Param1,Param2,Param3) NT_ASSERT(FALSE) +#define BDD_LOG_ASSERTION4(Msg,Param1,Param2,Param3,Param4) NT_ASSERT(FALSE) +#define BDD_LOG_ASSERTION5(Msg,Param1,Param2,Param3,Param4,Param5) NT_ASSERT(FALSE) +#define BDD_ASSERT(exp) {if (!(exp)) {BDD_LOG_ASSERTION0(#exp);}} + +#if DBG +#define BDD_ASSERT_CHK(exp) BDD_ASSERT(exp) +#else +#define BDD_ASSERT_CHK(exp) {} +#endif + + +#endif //_BDD_ERRORLOG_HXX_ + diff --git a/video/KMDOD/bdd_util.cxx b/video/KMDOD/bdd_util.cxx new file mode 100644 index 000000000..78f665317 --- /dev/null +++ b/video/KMDOD/bdd_util.cxx @@ -0,0 +1,115 @@ +/******************************Module*Header*******************************\ +* Module Name: bdd_util.cxx +* +* Basic Display Driver utility functions +* +* Created: 29-Mar-2011 +* Author: Amos Eshel [amosesh] +* +* Copyright (c) 2011 Microsoft Corporation +\**************************************************************************/ + +#include "BDD.hxx" + + +#pragma code_seg("PAGE") + +// +// EDID validation +// + +BOOLEAN IsEdidHeaderValid(_In_reads_bytes_(EDID_V1_BLOCK_SIZE) const BYTE* pEdid) +{ + PAGED_CODE(); + + static const UCHAR EDID1Header[8] = {0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0}; + return memcmp(pEdid, EDID1Header, sizeof(EDID1Header)) == 0; +} + +BOOLEAN IsEdidChecksumValid(_In_reads_bytes_(EDID_V1_BLOCK_SIZE) const BYTE* pEdid) +{ + PAGED_CODE(); + + BYTE CheckSum = 0; + for (const BYTE* pEdidStart = pEdid; pEdidStart < (pEdid + EDID_V1_BLOCK_SIZE); ++pEdidStart) + { + CheckSum += *pEdidStart; + } + + return CheckSum == 0; +} + +// +// Frame buffer map/unmap +// + +NTSTATUS +MapFrameBuffer( + _In_ PHYSICAL_ADDRESS PhysicalAddress, + _In_ ULONG Length, + _Outptr_result_bytebuffer_(Length) VOID** VirtualAddress) +{ + PAGED_CODE(); + + // + // Check for parameters + // + if ((PhysicalAddress.QuadPart == (ULONGLONG)0) || + (Length == 0) || + (VirtualAddress == NULL)) + { + BDD_LOG_ERROR3("One of PhysicalAddress.QuadPart (0x%I64x), Length (0x%I64x), VirtualAddress (0x%I64x) is NULL or 0", + PhysicalAddress.QuadPart, Length, VirtualAddress); + return STATUS_INVALID_PARAMETER; + } + + *VirtualAddress = MmMapIoSpaceEx(PhysicalAddress, + Length, + PAGE_READWRITE | PAGE_WRITECOMBINE); + if (*VirtualAddress == NULL) + { + // The underlying call to MmMapIoSpace failed. This may be because, MmWriteCombined + // isn't supported, so try again with MmNonCached + + *VirtualAddress = MmMapIoSpaceEx(PhysicalAddress, + Length, + PAGE_READWRITE | PAGE_NOCACHE); + if (*VirtualAddress == NULL) + { + BDD_LOG_LOW_RESOURCE1("MmMapIoSpace returned a NULL buffer when trying to allocate 0x%I64x bytes", Length); + return STATUS_NO_MEMORY; + } + } + + return STATUS_SUCCESS; +} + +NTSTATUS +UnmapFrameBuffer( + _In_reads_bytes_(Length) VOID* VirtualAddress, + _In_ ULONG Length) +{ + PAGED_CODE(); + + + // + // Check for parameters + // + if ((VirtualAddress == NULL) && (Length == 0)) + { + // Allow this function to be called when there's no work to do, and treat as successful + return STATUS_SUCCESS; + } + else if ((VirtualAddress == NULL) || (Length == 0)) + { + BDD_LOG_ERROR2("Only one of Length (0x%I64x), VirtualAddress (0x%I64x) is NULL or 0", + Length, VirtualAddress); + return STATUS_INVALID_PARAMETER; + } + + MmUnmapIoSpace(VirtualAddress, + Length); + + return STATUS_SUCCESS; +} + diff --git a/video/KMDOD/bltfuncs.cxx b/video/KMDOD/bltfuncs.cxx new file mode 100644 index 000000000..9df7ee6b8 --- /dev/null +++ b/video/KMDOD/bltfuncs.cxx @@ -0,0 +1,346 @@ +/******************************Module*Header*******************************\ + * Module Name: BltFuncs.cxx + * + * Basic Display Driver copying functionality + * + * + * Copyright (c) 2010 Microsoft Corporation +\**************************************************************************/ + +#include "BDD.hxx" + +// For the following macros, c must be a UCHAR. +#define UPPER_6_BITS(c) (((c) & rMaskTable[6 - 1]) >> 2) +#define UPPER_5_BITS(c) (((c) & rMaskTable[5 - 1]) >> 3) +#define LOWER_6_BITS(c) (((BYTE)(c)) & lMaskTable[BITS_PER_BYTE - 6]) +#define LOWER_5_BITS(c) (((BYTE)(c)) & lMaskTable[BITS_PER_BYTE - 5]) + + +#define SHIFT_FOR_UPPER_5_IN_565 (6 + 5) +#define SHIFT_FOR_MIDDLE_6_IN_565 (5) +#define SHIFT_UPPER_5_IN_565_BACK ((BITS_PER_BYTE * 2) + (BITS_PER_BYTE - 5)) +#define SHIFT_MIDDLE_6_IN_565_BACK ((BITS_PER_BYTE * 1) + (BITS_PER_BYTE - 6)) +#define SHIFT_LOWER_5_IN_565_BACK ((BITS_PER_BYTE * 0) + (BITS_PER_BYTE - 5)) + +// For the following macros, pPixel must be a BYTE* pointing to the start of a 32 bit pixel +#define CONVERT_32BPP_TO_16BPP(pPixel) ((UPPER_5_BITS(pPixel[2]) << SHIFT_FOR_UPPER_5_IN_565) | \ + (UPPER_6_BITS(pPixel[1]) << SHIFT_FOR_MIDDLE_6_IN_565) | \ + (UPPER_5_BITS(pPixel[0]))) + +// 8bpp is done with 6 levels per color channel since this gives true grays, even if it leaves 40 empty palette entries +// The 6 levels per color is the reason for dividing below by 43 (43 * 6 == 258, closest multiple of 6 to 256) +// It is also the reason for multiplying the red channel by 36 (== 6*6) and the green channel by 6, as this is the +// equivalent to bit shifting in a 3:3:2 model. Changes to this must be reflected in vesasup.cxx with the Blues/Greens/Reds arrays +#define CONVERT_32BPP_TO_8BPP(pPixel) (((pPixel[2] / 43) * 36) + \ + ((pPixel[1] / 43) * 6) + \ + ((pPixel[0] / 43))) + +// 4bpp is done with strict grayscale since this has been found to be usable +// 30% of the red value, 59% of the green value, and 11% of the blue value is the standard way to convert true color to grayscale +#define CONVERT_32BPP_TO_4BPP(pPixel) ((BYTE)(((pPixel[2] * 30) + \ + (pPixel[1] * 59) + \ + (pPixel[0] * 11)) / (100 * 16))) + + +// For the following macro, Pixel must be a WORD representing a 16 bit pixel +#define CONVERT_16BPP_TO_32BPP(Pixel) (((ULONG)LOWER_5_BITS((Pixel) >> SHIFT_FOR_UPPER_5_IN_565) << SHIFT_UPPER_5_IN_565_BACK) | \ + ((ULONG)LOWER_6_BITS((Pixel) >> SHIFT_FOR_MIDDLE_6_IN_565) << SHIFT_MIDDLE_6_IN_565_BACK) | \ + ((ULONG)LOWER_5_BITS((Pixel)) << SHIFT_LOWER_5_IN_565_BACK)) + +#pragma code_seg(push) +#pragma code_seg() +// BEGIN: Non-Paged Code + +// Bit is 1 from Idx to end of byte, with bit count starting at high order +BYTE lMaskTable[BITS_PER_BYTE] = {0xff, 0x7f, 0x3f, 0x1f, 0x0f, 0x07, 0x03, 0x01}; + +// Bit is 1 from Idx to start of byte, with bit count starting at high order +BYTE rMaskTable[BITS_PER_BYTE] = {0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff}; + +// Bit of Idx is 1, with bit count starting at high order +BYTE PixelMask[BITS_PER_BYTE] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01}; + +/****************************Internal*Routine******************************\ + * CopyBits32_32 + * + * + * Copies rectangles from one surface to another. Both surfaces must have + * the same resolution. + * + * OffsetX, OffsetY - to add to the rectangle coordinates. + * + * Copied from %SDXROOT%\windows\Core\dxkernel\cdd\enable.cxx (CopySurfBits) + * +\**************************************************************************/ + +VOID CopyBits32_32( + BLT_INFO* pDst, + CONST BLT_INFO* pSrc, + UINT NumRects, + _In_reads_(NumRects) CONST RECT *pRects) +{ + NT_ASSERT((pDst->BitsPerPel == 32) && + (pSrc->BitsPerPel == 32)); + NT_ASSERT((pDst->Rotation == D3DKMDT_VPPR_IDENTITY) && + (pSrc->Rotation == D3DKMDT_VPPR_IDENTITY)); + + for (UINT iRect = 0; iRect < NumRects; iRect++) + { + CONST RECT* pRect = &pRects[iRect]; + + NT_ASSERT(pRect->right >= pRect->left); + NT_ASSERT(pRect->bottom >= pRect->top); + + UINT NumPixels = pRect->right - pRect->left; + UINT NumRows = pRect->bottom - pRect->top; + UINT BytesToCopy = NumPixels * 4; + BYTE* pStartDst = ((BYTE*)pDst->pBits + + (pRect->top + pDst->Offset.y) * pDst->Pitch + + (pRect->left + pDst->Offset.x) * 4); + CONST BYTE* pStartSrc = ((BYTE*)pSrc->pBits + + (pRect->top + pSrc->Offset.y) * pSrc->Pitch + + (pRect->left + pSrc->Offset.x) * 4); + + for (UINT i = 0; i < NumRows; ++i) + { + RtlCopyMemory(pStartDst, pStartSrc, BytesToCopy); + pStartDst += pDst->Pitch; + pStartSrc += pSrc->Pitch; + } + } +} + + +VOID GetPitches(_In_ CONST BLT_INFO* pBltInfo, _Out_ LONG* pPixelPitch, _Out_ LONG* pRowPitch) +{ + switch (pBltInfo->Rotation) + { + case D3DKMDT_VPPR_IDENTITY: + { + *pPixelPitch = (pBltInfo->BitsPerPel / BITS_PER_BYTE); + *pRowPitch = pBltInfo->Pitch; + return; + } + case D3DKMDT_VPPR_ROTATE90: + { + *pPixelPitch = -((LONG)pBltInfo->Pitch); + *pRowPitch = (pBltInfo->BitsPerPel / BITS_PER_BYTE); + return; + } + case D3DKMDT_VPPR_ROTATE180: + { + *pPixelPitch = -((LONG)pBltInfo->BitsPerPel / BITS_PER_BYTE); + *pRowPitch = -((LONG)pBltInfo->Pitch); + return; + } + case D3DKMDT_VPPR_ROTATE270: + { + *pPixelPitch = pBltInfo->Pitch; + *pRowPitch = -((LONG)pBltInfo->BitsPerPel / BITS_PER_BYTE); + return; + } + default: + { + BDD_LOG_ASSERTION1("Invalid rotation (0x%I64x) specified", pBltInfo->Rotation); + *pPixelPitch = 0; + *pRowPitch = 0; + return; + } + } +} + +BYTE* GetRowStart(_In_ CONST BLT_INFO* pBltInfo, CONST RECT* pRect) +{ + BYTE* pRet = NULL; + LONG OffLeft = pRect->left + pBltInfo->Offset.x; + LONG OffTop = pRect->top + pBltInfo->Offset.y; + LONG BytesPerPixel = (pBltInfo->BitsPerPel / BITS_PER_BYTE); + switch (pBltInfo->Rotation) + { + case D3DKMDT_VPPR_IDENTITY: + { + pRet = ((BYTE*)pBltInfo->pBits + + OffTop * pBltInfo->Pitch + + OffLeft * BytesPerPixel); + break; + } + case D3DKMDT_VPPR_ROTATE90: + { + pRet = ((BYTE*)pBltInfo->pBits + + (pBltInfo->Height - 1 - OffLeft) * pBltInfo->Pitch + + OffTop * BytesPerPixel); + break; + } + case D3DKMDT_VPPR_ROTATE180: + { + pRet = ((BYTE*)pBltInfo->pBits + + (pBltInfo->Height - 1 - OffTop) * pBltInfo->Pitch + + (pBltInfo->Width - 1 - OffLeft) * BytesPerPixel); + break; + } + case D3DKMDT_VPPR_ROTATE270: + { + pRet = ((BYTE*)pBltInfo->pBits + + OffLeft * pBltInfo->Pitch + + (pBltInfo->Width - 1 - OffTop) * BytesPerPixel); + break; + } + default: + { + BDD_LOG_ASSERTION1("Invalid rotation (0x%I64x) specified", pBltInfo->Rotation); + break; + } + } + + return pRet; +} + +/****************************Internal*Routine******************************\ + * CopyBitsGeneric + * + * + * Blt function which can handle a rotated dst/src, offset rects in dst/src + * and bpp combinations of: + * dst | src + * 32 | 32 // For identity rotation this is much faster in CopyBits32_32 + * 32 | 24 + * 32 | 16 + * 24 | 32 + * 16 | 32 + * 8 | 32 + * 24 | 24 // untested + * +\**************************************************************************/ + +VOID CopyBitsGeneric( + BLT_INFO* pDst, + CONST BLT_INFO* pSrc, + UINT NumRects, + _In_reads_(NumRects) CONST RECT *pRects) +{ + LONG DstPixelPitch = 0; + LONG DstRowPitch = 0; + LONG SrcPixelPitch = 0; + LONG SrcRowPitch = 0; + + GetPitches(pDst, &DstPixelPitch, &DstRowPitch); + GetPitches(pSrc, &SrcPixelPitch, &SrcRowPitch); + + for (UINT iRect = 0; iRect < NumRects; iRect++) + { + CONST RECT* pRect = &pRects[iRect]; + + NT_ASSERT(pRect->right >= pRect->left); + NT_ASSERT(pRect->bottom >= pRect->top); + + UINT NumPixels = pRect->right - pRect->left; + UINT NumRows = pRect->bottom - pRect->top; + + BYTE* pDstRow = GetRowStart(pDst, pRect); + CONST BYTE* pSrcRow = GetRowStart(pSrc, pRect); + + for (UINT y=0; y < NumRows; y++) + { + BYTE* pDstPixel = pDstRow; + CONST BYTE* pSrcPixel = pSrcRow; + + for (UINT x=0; x < NumPixels; x++) + { + if ((pDst->BitsPerPel == 24) || + (pSrc->BitsPerPel == 24)) + { + pDstPixel[0] = pSrcPixel[0]; + pDstPixel[1] = pSrcPixel[1]; + pDstPixel[2] = pSrcPixel[2]; + // pPixel[3] is the alpha channel and is ignored for whichever of Src/Dst is 32bpp + } + else if (pDst->BitsPerPel == 32) + { + if (pSrc->BitsPerPel == 32) + { + UINT32* pDstPixelAs32 = (UINT32*)pDstPixel; + UINT32* pSrcPixelAs32 = (UINT32*)pSrcPixel; + *pDstPixelAs32 = *pSrcPixelAs32; + } + else if (pSrc->BitsPerPel == 16) + { + UINT32* pDstPixelAs32 = (UINT32*)pDstPixel; + UINT16* pSrcPixelAs16 = (UINT16*)pSrcPixel; + + *pDstPixelAs32 = CONVERT_16BPP_TO_32BPP(*pSrcPixelAs16); + } + else + { + // Invalid pSrc->BitsPerPel on a pDst->BitsPerPel of 32 + NT_ASSERT(FALSE); + } + } + else if (pDst->BitsPerPel == 16) + { + NT_ASSERT(pSrc->BitsPerPel == 32); + + UINT16* pDstPixelAs16 = (UINT16*)pDstPixel; + *pDstPixelAs16 = CONVERT_32BPP_TO_16BPP(pSrcPixel); + } + else if (pDst->BitsPerPel == 8) + { + NT_ASSERT(pSrc->BitsPerPel == 32); + + *pDstPixel = CONVERT_32BPP_TO_8BPP(pSrcPixel); + } + else + { + // Invalid pDst->BitsPerPel + NT_ASSERT(FALSE); + } + pDstPixel += DstPixelPitch; + pSrcPixel += SrcPixelPitch; + } + + pDstRow += DstRowPitch; + pSrcRow += SrcRowPitch; + } + } +} + +/****************************Internal*Routine******************************\ + * BltBits + * + * + * Logic to decide which of the above functions to call based on Rotation/BPP + * +\**************************************************************************/ +VOID BltBits( + BLT_INFO* pDst, + CONST BLT_INFO* pSrc, + UINT NumRects, + _In_reads_(NumRects) CONST RECT *pRects) +{ + // pSrc->pBits might be coming from user-mode. User-mode addresses when accessed by kernel need to be protected by a __try/__except. + // This usage is redundant in the sample driver since it is already being used for MmProbeAndLockPages. However, it is very important + // to have this in place and to make sure developers don't miss it, it is in these two locations. + __try + { + if (pDst->BitsPerPel == 32 && + pSrc->BitsPerPel == 32 && + pDst->Rotation == D3DKMDT_VPPR_IDENTITY && + pSrc->Rotation == D3DKMDT_VPPR_IDENTITY) + { + // This is by far the most common copy function being called + CopyBits32_32(pDst, pSrc, NumRects, pRects); + } + else + { + CopyBitsGeneric(pDst, pSrc, NumRects, pRects); + } + } + #pragma prefast(suppress: __WARNING_EXCEPTIONEXECUTEHANDLER, "try/except is only able to protect against user-mode errors and these are the only errors we try to catch here"); + __except(EXCEPTION_EXECUTE_HANDLER) + { + BDD_LOG_ERROR2("Either dst (0x%I64x) or src (0x%I64x) bits encountered exception during access.", pDst->pBits, pSrc->pBits); + } +} + +// END: Non-Paged Code +#pragma code_seg(pop) + diff --git a/video/KMDOD/blthw.cxx b/video/KMDOD/blthw.cxx new file mode 100644 index 000000000..2e6d2e5d8 --- /dev/null +++ b/video/KMDOD/blthw.cxx @@ -0,0 +1,556 @@ +/******************************Module*Header*******************************\ +* Module Name: blthw.cxx +* +* Sample display driver functions for a HW blt simulation. This file is +* only provided to simulate how a real hardware-accelerated display-only +* driver functions, and should not be used in a real driver. +* +* Copyright (c) 2011 Microsoft Corporation +\**************************************************************************/ + +#include "BDD.hxx" + +typedef struct +{ + CONST DXGKRNL_INTERFACE* DxgkInterface; + DXGKARGCB_NOTIFY_INTERRUPT_DATA NotifyInterrupt; +} SYNC_NOTIFY_INTERRUPT; + +KSYNCHRONIZE_ROUTINE SynchronizeVidSchNotifyInterrupt; + +BOOLEAN SynchronizeVidSchNotifyInterrupt(_In_opt_ PVOID params) +{ + // This routine is non-paged code called at the device interrupt level (DIRQL) + // to notify VidSch and schedule a DPC. It is meant as a demonstration of handling + // a real hardware interrupt, even though it is actually called from asynchronous + // present worker threads in this sample. + SYNC_NOTIFY_INTERRUPT* pParam = reinterpret_cast(params); + + // The context is known to be non-NULL + __analysis_assume(pParam != NULL); + + // Update driver information related to fences + switch(pParam->NotifyInterrupt.InterruptType) + { + case DXGK_INTERRUPT_DISPLAYONLY_VSYNC: + case DXGK_INTERRUPT_DISPLAYONLY_PRESENT_PROGRESS: + break; + default: + NT_ASSERT(FALSE); + return FALSE; + } + + // Callback OS to report about the interrupt + pParam->DxgkInterface->DxgkCbNotifyInterrupt(pParam->DxgkInterface->DeviceHandle,&pParam->NotifyInterrupt); + + // Now queue a DPC for this interrupt (to callback schedule at DCP level and let it do more work there) + // DxgkCbQueueDpc can return FALSE if there is already a DPC queued + // this is an acceptable condition + pParam->DxgkInterface->DxgkCbQueueDpc(pParam->DxgkInterface->DeviceHandle); + + return TRUE; +} + +#pragma code_seg("PAGE") + +KSTART_ROUTINE HwContextWorkerThread; + +struct DoPresentMemory +{ + PVOID DstAddr; + UINT DstStride; + ULONG DstBitPerPixel; + UINT SrcWidth; + UINT SrcHeight; + BYTE* SrcAddr; + LONG SrcPitch; + ULONG NumMoves; // in: Number of screen to screen moves + D3DKMT_MOVE_RECT* Moves; // in: Point to the list of moves + ULONG NumDirtyRects; // in: Number of direct rects + RECT* DirtyRect; // in: Point to the list of dirty rects + D3DKMDT_VIDPN_PRESENT_PATH_ROTATION Rotation; + BOOLEAN SynchExecution; + D3DDDI_VIDEO_PRESENT_SOURCE_ID SourceID; + HANDLE hAdapter; + PMDL Mdl; + BDD_HWBLT* DisplaySource; +}; + +void +HwExecutePresentDisplayOnly( + HANDLE Context); + +NTSTATUS +StartHwBltPresentWorkerThread( + _In_ PKSTART_ROUTINE StartRoutine, + _In_ _When_(return==0, __drv_aliasesMem) PVOID StartContext) +/*++ + + Routine Description: + + This routine creates the worker thread to execute a single present + command. Creating a new thread on every asynchronous present is not + efficient, but this file is only meant as a simulation, not an example + of implementation. + + Arguments: + + StartRoutine - start routine + StartContext - start context + + Return Value: + + Status + +--*/ +{ + PAGED_CODE(); + + OBJECT_ATTRIBUTES ObjectAttributes; + InitializeObjectAttributes(&ObjectAttributes, NULL, OBJ_KERNEL_HANDLE, NULL, NULL); + HANDLE hWorkerThread = NULL; + + // copy data from the context which is need here, as it will be deleted in separate thread + DoPresentMemory* ctx = reinterpret_cast(StartContext); + BDD_HWBLT* displaySource = ctx->DisplaySource; + + NTSTATUS Status = PsCreateSystemThread( + &hWorkerThread, + THREAD_ALL_ACCESS, + &ObjectAttributes, + NULL, + NULL, + StartRoutine, + StartContext); + + if (!NT_SUCCESS(Status)) + { + return Status; + } + + + // wait for thread to start - infinite wait - + // need to make sure the tread is running before OS stars submitting the work items to it + KeWaitForSingleObject(&displaySource->m_hThreadStartupEvent, Executive, KernelMode, FALSE, NULL); + + // Handle is passed to the parent object which must close it + displaySource->SetPresentWorkerThreadInfo(hWorkerThread); + + // Resume context thread, this is done by setting the event the thread is waiting on + KeSetEvent(&displaySource->m_hThreadSuspendEvent, 0, FALSE); + + return STATUS_PENDING; +} + +BDD_HWBLT::BDD_HWBLT():m_DevExt (NULL), + m_SynchExecution(TRUE), + m_hPresentWorkerThread(NULL), + m_pPresentWorkerThread(NULL) +{ + PAGED_CODE(); + + KeInitializeEvent(&m_hThreadStartupEvent, NotificationEvent, FALSE); + KeInitializeEvent(&m_hThreadSuspendEvent, SynchronizationEvent, FALSE); + +} + + +BDD_HWBLT::~BDD_HWBLT() +/*++ + + Routine Description: + + This routine waits on present worker thread to exit before + destroying the object + + Arguments: + + None + + Return Value: + + None + +--*/ +{ + PAGED_CODE(); + + // make sure the worker thread has exited + SetPresentWorkerThreadInfo(NULL); +} + +#pragma warning(push) +#pragma warning(disable:26135) // The function doesn't lock anything + +void +BDD_HWBLT::SetPresentWorkerThreadInfo( + HANDLE hWorkerThread) +/*++ + + Routine Description: + + The method is updating present worker information + It is called in following cases: + - In ExecutePresent to update worker thread information + - In Dtor to wait on worker thread to exit + + Arguments: + + hWorkerThread - handle of the present worker thread + + Return Value: + + None + +--*/ +{ + + PAGED_CODE(); + + if (m_pPresentWorkerThread) + { + // Wait for thread to exit + KeWaitForSingleObject(m_pPresentWorkerThread, Executive, KernelMode, + FALSE, NULL); + // Dereference thread object + ObDereferenceObject(m_pPresentWorkerThread); + m_pPresentWorkerThread = NULL; + + NT_ASSERT(m_hPresentWorkerThread); + ZwClose(m_hPresentWorkerThread); + m_hPresentWorkerThread = NULL; + } + + if (hWorkerThread) + { + // Make sure that thread's handle would be valid even if the thread exited + ObReferenceObjectByHandle(hWorkerThread, THREAD_ALL_ACCESS, NULL, + KernelMode, &m_pPresentWorkerThread, NULL); + NT_ASSERT(m_pPresentWorkerThread); + m_hPresentWorkerThread = hWorkerThread; + } + +} +#pragma warning(pop) + +NTSTATUS +BDD_HWBLT::ExecutePresentDisplayOnly( + _In_ BYTE* DstAddr, + _In_ UINT DstBitPerPixel, + _In_ BYTE* SrcAddr, + _In_ UINT SrcBytesPerPixel, + _In_ LONG SrcPitch, + _In_ ULONG NumMoves, + _In_ D3DKMT_MOVE_RECT* Moves, + _In_ ULONG NumDirtyRects, + _In_ RECT* DirtyRect, + _In_ D3DKMDT_VIDPN_PRESENT_PATH_ROTATION Rotation) +/*++ + + Routine Description: + + The method creates present worker thread and provides context + for it filled with present commands + + Arguments: + + DstAddr - address of destination surface + DstBitPerPixel - color depth of destination surface + SrcAddr - address of source surface + SrcBytesPerPixel - bytes per pixel of source surface + SrcPitch - source surface pitch (bytes in a row) + NumMoves - number of moves to be copied + Moves - moves' data + NumDirtyRects - number of rectangles to be copied + DirtyRect - rectangles' data + Rotation - roatation to be performed when executing copy + CallBack - callback for present worker thread to report execution status + + Return Value: + + Status + +--*/ +{ + + PAGED_CODE(); + + NTSTATUS Status = STATUS_SUCCESS; + + SIZE_T sizeMoves = NumMoves*sizeof(D3DKMT_MOVE_RECT); + SIZE_T sizeRects = NumDirtyRects*sizeof(RECT); + SIZE_T size = sizeof(DoPresentMemory) + sizeMoves + sizeRects; + + DoPresentMemory* ctx = reinterpret_cast + (new (PagedPool) BYTE[size]); + + if (!ctx) + { + return STATUS_NO_MEMORY; + } + + RtlZeroMemory(ctx,size); + + const CURRENT_BDD_MODE* pModeCur = m_DevExt->GetCurrentMode(m_SourceId); + + ctx->DstAddr = DstAddr; + ctx->DstBitPerPixel = DstBitPerPixel; + ctx->DstStride = pModeCur->DispInfo.Pitch; + ctx->SrcWidth = pModeCur->SrcModeWidth; + ctx->SrcHeight = pModeCur->SrcModeHeight; + ctx->SrcAddr = NULL; + ctx->SrcPitch = SrcPitch; + ctx->Rotation = Rotation; + ctx->NumMoves = NumMoves; + ctx->Moves = Moves; + ctx->NumDirtyRects = NumDirtyRects; + ctx->DirtyRect = DirtyRect; + ctx->SourceID = m_SourceId; + ctx->hAdapter = m_DevExt; + ctx->Mdl = NULL; + ctx->DisplaySource = this; + + // Alternate between synch and asynch execution, for demonstrating + // that a real hardware implementation can do either + m_SynchExecution = !m_SynchExecution; + + ctx->SynchExecution = m_SynchExecution; + + { + // Map Source into kernel space, as Blt will be executed by system worker thread + UINT sizeToMap = SrcBytesPerPixel*pModeCur->SrcModeWidth*pModeCur->SrcModeHeight; + + PMDL mdl = IoAllocateMdl((PVOID)SrcAddr, sizeToMap, FALSE, FALSE, NULL); + if(!mdl) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + KPROCESSOR_MODE AccessMode = static_cast(( SrcAddr <= + (BYTE* const) MM_USER_PROBE_ADDRESS)?UserMode:KernelMode); + __try + { + // Probe and lock the pages of this buffer in physical memory. + // We need only IoReadAccess. + MmProbeAndLockPages(mdl, AccessMode, IoReadAccess); + } + #pragma prefast(suppress: __WARNING_EXCEPTIONEXECUTEHANDLER, "try/except is only able to protect against user-mode errors and these are the only errors we try to catch here"); + __except(EXCEPTION_EXECUTE_HANDLER) + { + Status = GetExceptionCode(); + IoFreeMdl(mdl); + return Status; + } + + // 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. + ctx->SrcAddr = reinterpret_cast + (MmGetSystemAddressForMdlSafe(mdl, NormalPagePriority )); + + if(!ctx->SrcAddr) { + Status = STATUS_INSUFFICIENT_RESOURCES; + MmUnlockPages(mdl); + IoFreeMdl(mdl); + return Status; + } + + // Save Mdl to unmap and unlock the pages in worker thread + ctx->Mdl = mdl; + } + + BYTE* rects = reinterpret_cast(ctx+1); + + // copy moves and update pointer + if (Moves) + { + memcpy(rects,Moves,sizeMoves); + ctx->Moves = reinterpret_cast(rects); + rects += sizeMoves; + } + + // copy dirty rects and update pointer + if (DirtyRect) + { + memcpy(rects,DirtyRect,sizeRects); + ctx->DirtyRect = reinterpret_cast(rects); + } + + + if (m_SynchExecution) + { + HwExecutePresentDisplayOnly((PVOID)ctx); + return STATUS_SUCCESS; + } + else + { + // Create a worker thread to perform the present asynchronously + // Ctx will be deleted in worker thread (on exit) + return StartHwBltPresentWorkerThread(HwContextWorkerThread,(PVOID)ctx); + } +} + + +void +ReportPresentProgress( + _In_ HANDLE Adapter, + _In_ D3DDDI_VIDEO_PRESENT_SOURCE_ID VidPnSourceId, + _In_ BOOLEAN CompletedOrFailed) +/*++ + + Routine Description: + + This routine runs a fake interrupt routine in order to tell the OS about the present progress. + + Arguments: + + Adapter - Handle to the adapter (Device Extension) + VidPnSourceId - Video present source id for the callback + CompletedOrFailed - Present progress status for the source + + Return Value: + + None + +--*/ +{ + PAGED_CODE(); + + BASIC_DISPLAY_DRIVER* pDevExt = + reinterpret_cast(Adapter); + + SYNC_NOTIFY_INTERRUPT SyncNotifyInterrupt = {}; + SyncNotifyInterrupt.DxgkInterface = pDevExt->GetDxgkInterface(); + SyncNotifyInterrupt.NotifyInterrupt.InterruptType = DXGK_INTERRUPT_DISPLAYONLY_PRESENT_PROGRESS; + SyncNotifyInterrupt.NotifyInterrupt.DisplayOnlyPresentProgress.VidPnSourceId = VidPnSourceId; + + SyncNotifyInterrupt.NotifyInterrupt.DisplayOnlyPresentProgress.ProgressId = + (CompletedOrFailed)?DXGK_PRESENT_DISPLAYONLY_PROGRESS_ID_COMPLETE: + DXGK_PRESENT_DISPLAYONLY_PROGRESS_ID_FAILED; + + // Execute the SynchronizeVidSchNotifyInterrupt function at the interrupt + // IRQL in order to fake a real present progress interrupt + BOOLEAN bRet = FALSE; + NT_VERIFY(NT_SUCCESS(pDevExt->GetDxgkInterface()->DxgkCbSynchronizeExecution( + pDevExt->GetDxgkInterface()->DeviceHandle, + (PKSYNCHRONIZE_ROUTINE)SynchronizeVidSchNotifyInterrupt, + (PVOID)&SyncNotifyInterrupt,0,&bRet))); + NT_ASSERT(bRet); +} + + +void +HwContextWorkerThread( + HANDLE Context) +{ + PAGED_CODE(); + + DoPresentMemory* ctx = reinterpret_cast(Context); + BDD_HWBLT* displaySource = ctx->DisplaySource; + + // Signal event to indicate that the tread has started + KeSetEvent(&displaySource->m_hThreadStartupEvent, 0, FALSE); + + // Suspend context thread, do this by waiting on the suspend event + KeWaitForSingleObject(&displaySource->m_hThreadSuspendEvent, Executive, KernelMode, FALSE, NULL); + + HwExecutePresentDisplayOnly(Context); +} + + +void +HwExecutePresentDisplayOnly( + HANDLE Context) +/*++ + + Routine Description: + + The routine executes present's commands and report progress to the OS + + Arguments: + + Context - Context with present's command + + Return Value: + + None + +--*/ +{ + PAGED_CODE(); + + DoPresentMemory* ctx = reinterpret_cast(Context); + + // Set up destination blt info + BLT_INFO DstBltInfo; + DstBltInfo.pBits = ctx->DstAddr; + DstBltInfo.Pitch = ctx->DstStride; + DstBltInfo.BitsPerPel = ctx->DstBitPerPixel; + DstBltInfo.Offset.x = 0; + DstBltInfo.Offset.y = 0; + DstBltInfo.Rotation = ctx->Rotation; + DstBltInfo.Width = ctx->SrcWidth; + DstBltInfo.Height = ctx->SrcHeight; + + // Set up source blt info + BLT_INFO SrcBltInfo; + SrcBltInfo.pBits = ctx->SrcAddr; + SrcBltInfo.Pitch = ctx->SrcPitch; + SrcBltInfo.BitsPerPel = 32; + SrcBltInfo.Offset.x = 0; + SrcBltInfo.Offset.y = 0; + SrcBltInfo.Rotation = D3DKMDT_VPPR_IDENTITY; + if (ctx->Rotation == D3DKMDT_VPPR_ROTATE90 || + ctx->Rotation == D3DKMDT_VPPR_ROTATE270) + { + SrcBltInfo.Width = DstBltInfo.Height; + SrcBltInfo.Height = DstBltInfo.Width; + } + else + { + SrcBltInfo.Width = DstBltInfo.Width; + SrcBltInfo.Height = DstBltInfo.Height; + } + + + // Copy all the scroll rects from source image to video frame buffer. + for (UINT i = 0; i < ctx->NumMoves; i++) + { + BltBits(&DstBltInfo, + &SrcBltInfo, + 1, // NumRects + &ctx->Moves[i].DestRect); + } + + // Copy all the dirty rects from source image to video frame buffer. + for (UINT i = 0; i < ctx->NumDirtyRects; i++) + { + + BltBits(&DstBltInfo, + &SrcBltInfo, + 1, // NumRects + &ctx->DirtyRect[i]); + } + + // Unmap unmap and unlock the pages. + if (ctx->Mdl) + { + MmUnlockPages(ctx->Mdl); + IoFreeMdl(ctx->Mdl); + } + + if(ctx->SynchExecution) + { + // This code simulates Blt executed synchronously + // nothing should be done here, just exit + ; + } + else + { + // TRUE == completed + // This code is emulates interrupt which HW should generate + ReportPresentProgress(ctx->hAdapter,ctx->SourceID,TRUE); + } + + delete [] reinterpret_cast(ctx); +} diff --git a/video/KMDOD/memory.cxx b/video/KMDOD/memory.cxx new file mode 100644 index 000000000..aae795dfb --- /dev/null +++ b/video/KMDOD/memory.cxx @@ -0,0 +1,90 @@ +/******************************Module*Header*******************************\ +* Module Name: bdd.h +* +* Basic Display Driver memory allocation, deletion, and tracking +* +* +* Copyright (c) 2010 Microsoft Corporation +\**************************************************************************/ + +#include "BDD.hxx" + +#pragma code_seg("PAGE") + +// +// New and delete operators +// +_When_((PoolType & NonPagedPoolMustSucceed) != 0, + __drv_reportError("Must succeed pool allocations are forbidden. " + "Allocation failures cause a system crash")) +void* __cdecl operator new(size_t Size, POOL_TYPE PoolType) +{ + PAGED_CODE(); + + Size = (Size != 0) ? Size : 1; + + void* pObject = ExAllocatePoolWithTag(PoolType, Size, BDDTAG); + +#if DBG + if (pObject != NULL) + { + RtlFillMemory(pObject, Size, 0xCD); + } +#endif // DBG + + return pObject; +} + +_When_((PoolType & NonPagedPoolMustSucceed) != 0, + __drv_reportError("Must succeed pool allocations are forbidden. " + "Allocation failures cause a system crash")) +void* __cdecl operator new[](size_t Size, POOL_TYPE PoolType) +{ + PAGED_CODE(); + + Size = (Size != 0) ? Size : 1; + + void* pObject = ExAllocatePoolWithTag(PoolType, Size, BDDTAG); + +#if DBG + if (pObject != NULL) + { + RtlFillMemory(pObject, Size, 0xCD); + } +#endif // DBG + + return pObject; +} + +void __cdecl operator delete(void* pObject) +{ + PAGED_CODE(); + + if (pObject != NULL) + { + ExFreePool(pObject); + } +} + +// +// size_t version is needed for VS2015(C++ 14). +// +void __cdecl operator delete(void* pObject, size_t s) +{ + PAGED_CODE(); + + UNREFERENCED_PARAMETER( s ); + + ::operator delete( pObject ); +} + +void __cdecl operator delete[](void* pObject) +{ + PAGED_CODE(); + + if (pObject != NULL) + { + ExFreePool(pObject); + } +} + diff --git a/video/KMDOD/readme.htm b/video/KMDOD/readme.htm new file mode 100644 index 000000000..36a7acd0a --- /dev/null +++ b/video/KMDOD/readme.htm @@ -0,0 +1,46 @@ + + + + + + Kernel-Mode Display-Only Miniport Driver + + + +

+ This is a sample kernel-mode display-only miniport driver. It implements most of DDIs which display-only drivers should provide to the + Windows Display Driver Model (WDDM). The sample can be installed on top of a VESA-capable graphics adapter or on top of a graphics device + that supports access to the frame buffer through UEFI. +

+

+ The code is provided mainly as a reference for people writing miniport drivers for display-only devices, although it can also be useful + for people working on full WDDM drivers to help in display-related DDI implementations. +

+

+ The BltHw.* files are provided only to simulate how the blt operations would work on a real hardware device and should not be used as a + basis for actual implementation. The INF file is provided mainly to illustrate how to make the miniport driver visible to other WDDM + components. +

+

+ Here are some useful links to MSDN documentation: +

+ + + + diff --git a/video/KMDOD/sampledisplay.rc b/video/KMDOD/sampledisplay.rc new file mode 100644 index 000000000..f495476d1 --- /dev/null +++ b/video/KMDOD/sampledisplay.rc @@ -0,0 +1,30 @@ +/*++ + +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + SampleDisplay.rc + +Abstract: + + This modules contains the version information for the Sample Display Driver + +Environment: + + Kernel mode only + +--*/ + +#include + +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_DISPLAY +#define VER_FILEDESCRIPTION_STR "Microsoft Sample Display Driver" +#define VER_INTERNALNAME_STR "SampleDisplay.sys" +#define VER_ORIGINALFILENAME_STR "SampleDisplay.sys" + +#define VER_LANGNEUTRAL +#include "common.ver" diff --git a/video/pixlib/PixLib.vcxproj b/video/pixlib/PixLib.vcxproj index 78e2b2360..7e5d9c179 100644 --- a/video/pixlib/PixLib.vcxproj +++ b/video/pixlib/PixLib.vcxproj @@ -19,11 +19,11 @@
- {F7850F91-2B8B-4646-9214-9FEC52E95365} + {2CC8C302-2377-42A4-A6D2-8E22B5EC85B4} $(MSBuildProjectName) Debug Win32 - {14F26315-FCC7-4B70-B304-B9BB589DE042} + {F8CD62F9-73D0-4C0D-87F6-45C58D7F0890} @@ -137,7 +137,6 @@ - diff --git a/video/pixlib/PixLib.vcxproj.Filters b/video/pixlib/PixLib.vcxproj.Filters index 10c134f77..f747d3350 100644 --- a/video/pixlib/PixLib.vcxproj.Filters +++ b/video/pixlib/PixLib.vcxproj.Filters @@ -3,15 +3,15 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {AE3F0C15-3824-4BF5-9012-CB71D541730A} + {2069F7E0-1359-4306-A263-83740F596C82} h;hpp;hxx;hm;inl;inc;xsd - {83784D0E-55CF-4946-AD66-C1DEB6BDC0B4} + {130959D7-B8A7-4917-BB82-9A4F892B0D06} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {C99ABA84-B090-45ED-BD98-DA59A71CD4B3} + {231EE37D-E030-4960-B275-77FC4DDE57FD} diff --git a/video/pixlib/pixlib.sln b/video/pixlib/pixlib.sln index 9f698ee8c..86cd1af31 100644 --- a/video/pixlib/pixlib.sln +++ b/video/pixlib/pixlib.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0 MinimumVisualStudioVersion = 12.0 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PixLib", "PixLib.vcxproj", "{F7850F91-2B8B-4646-9214-9FEC52E95365}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PixLib", "PixLib.vcxproj", "{2CC8C302-2377-42A4-A6D2-8E22B5EC85B4}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {F7850F91-2B8B-4646-9214-9FEC52E95365}.Debug|Win32.ActiveCfg = Debug|Win32 - {F7850F91-2B8B-4646-9214-9FEC52E95365}.Debug|Win32.Build.0 = Debug|Win32 - {F7850F91-2B8B-4646-9214-9FEC52E95365}.Release|Win32.ActiveCfg = Release|Win32 - {F7850F91-2B8B-4646-9214-9FEC52E95365}.Release|Win32.Build.0 = Release|Win32 - {F7850F91-2B8B-4646-9214-9FEC52E95365}.Debug|x64.ActiveCfg = Debug|x64 - {F7850F91-2B8B-4646-9214-9FEC52E95365}.Debug|x64.Build.0 = Debug|x64 - {F7850F91-2B8B-4646-9214-9FEC52E95365}.Release|x64.ActiveCfg = Release|x64 - {F7850F91-2B8B-4646-9214-9FEC52E95365}.Release|x64.Build.0 = Release|x64 + {2CC8C302-2377-42A4-A6D2-8E22B5EC85B4}.Debug|Win32.ActiveCfg = Debug|Win32 + {2CC8C302-2377-42A4-A6D2-8E22B5EC85B4}.Debug|Win32.Build.0 = Debug|Win32 + {2CC8C302-2377-42A4-A6D2-8E22B5EC85B4}.Release|Win32.ActiveCfg = Release|Win32 + {2CC8C302-2377-42A4-A6D2-8E22B5EC85B4}.Release|Win32.Build.0 = Release|Win32 + {2CC8C302-2377-42A4-A6D2-8E22B5EC85B4}.Debug|x64.ActiveCfg = Debug|x64 + {2CC8C302-2377-42A4-A6D2-8E22B5EC85B4}.Debug|x64.Build.0 = Debug|x64 + {2CC8C302-2377-42A4-A6D2-8E22B5EC85B4}.Release|x64.ActiveCfg = Release|x64 + {2CC8C302-2377-42A4-A6D2-8E22B5EC85B4}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/wmi/wmiacpi/wmiacpi/ReadMe.md b/wmi/wmiacpi/ReadMe.md similarity index 100% rename from wmi/wmiacpi/wmiacpi/ReadMe.md rename to wmi/wmiacpi/ReadMe.md diff --git a/wmi/wmiacpi/wmiacpi/acpimof.def b/wmi/wmiacpi/acpimof.def similarity index 100% rename from wmi/wmiacpi/wmiacpi/acpimof.def rename to wmi/wmiacpi/acpimof.def diff --git a/wmi/wmiacpi/wmiacpi/acpimof.mof b/wmi/wmiacpi/acpimof.mof similarity index 100% rename from wmi/wmiacpi/wmiacpi/acpimof.mof rename to wmi/wmiacpi/acpimof.mof diff --git a/wmi/wmiacpi/wmiacpi/acpimof.rc b/wmi/wmiacpi/acpimof.rc similarity index 100% rename from wmi/wmiacpi/wmiacpi/acpimof.rc rename to wmi/wmiacpi/acpimof.rc diff --git a/wmi/wmiacpi/wmiacpi/acpimof.vcxproj b/wmi/wmiacpi/acpimof.vcxproj similarity index 97% rename from wmi/wmiacpi/wmiacpi/acpimof.vcxproj rename to wmi/wmiacpi/acpimof.vcxproj index 3c6bb3d5f..93433b830 100644 --- a/wmi/wmiacpi/wmiacpi/acpimof.vcxproj +++ b/wmi/wmiacpi/acpimof.vcxproj @@ -19,11 +19,11 @@ - {698C6C2B-7B91-4243-8004-3CA38732948B} + {60A09FC3-05DB-4754-9305-678A4CFBD6C9} $(MSBuildProjectName) Debug Win32 - {21CC1425-4EAD-40AF-AEAE-684618F326F2} + {7718FA76-CE99-4A25-AE4C-5A12C000CEDB} @@ -175,7 +175,6 @@ - diff --git a/wmi/wmiacpi/wmiacpi/acpimof.vcxproj.Filters b/wmi/wmiacpi/acpimof.vcxproj.Filters similarity index 82% rename from wmi/wmiacpi/wmiacpi/acpimof.vcxproj.Filters rename to wmi/wmiacpi/acpimof.vcxproj.Filters index 3d36b2355..c4ae47123 100644 --- a/wmi/wmiacpi/wmiacpi/acpimof.vcxproj.Filters +++ b/wmi/wmiacpi/acpimof.vcxproj.Filters @@ -3,15 +3,15 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {0B8A6C1E-2E0C-4D5F-A4C8-222D806C3C3D} + {6C19878B-4845-4749-B7E3-B811AE466FA3} h;hpp;hxx;hm;inl;inc;xsd - {34F549CF-EF79-4160-AD59-87BF005E5EA4} + {FA95778D-B590-458E-B8FB-8487CA153A45} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {A674355E-0640-4EF1-A30A-55D72CFEF4CF} + {E4C2CE5D-D18B-4989-9A3E-8C4E90E496B3} diff --git a/wmi/wmiacpi/wmiacpi/device.asl b/wmi/wmiacpi/device.asl similarity index 100% rename from wmi/wmiacpi/wmiacpi/device.asl rename to wmi/wmiacpi/device.asl diff --git a/wmi/wmiacpi/wmiacpi/readme.htm b/wmi/wmiacpi/readme.htm similarity index 100% rename from wmi/wmiacpi/wmiacpi/readme.htm rename to wmi/wmiacpi/readme.htm diff --git a/wmi/wmiacpi/wmiacpi/wmi-acpi.htm b/wmi/wmiacpi/wmi-acpi.htm similarity index 100% rename from wmi/wmiacpi/wmiacpi/wmi-acpi.htm rename to wmi/wmiacpi/wmi-acpi.htm diff --git a/wmi/wmiacpi/wmiacpi.sln b/wmi/wmiacpi/wmiacpi.sln index 6c24484ac..3bcc16b62 100644 --- a/wmi/wmiacpi/wmiacpi.sln +++ b/wmi/wmiacpi/wmiacpi.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0 MinimumVisualStudioVersion = 12.0 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "acpimof", "wmiacpi\acpimof.vcxproj", "{698C6C2B-7B91-4243-8004-3CA38732948B}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "acpimof", "acpimof.vcxproj", "{60A09FC3-05DB-4754-9305-678A4CFBD6C9}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {698C6C2B-7B91-4243-8004-3CA38732948B}.Debug|Win32.ActiveCfg = Debug|Win32 - {698C6C2B-7B91-4243-8004-3CA38732948B}.Debug|Win32.Build.0 = Debug|Win32 - {698C6C2B-7B91-4243-8004-3CA38732948B}.Release|Win32.ActiveCfg = Release|Win32 - {698C6C2B-7B91-4243-8004-3CA38732948B}.Release|Win32.Build.0 = Release|Win32 - {698C6C2B-7B91-4243-8004-3CA38732948B}.Debug|x64.ActiveCfg = Debug|x64 - {698C6C2B-7B91-4243-8004-3CA38732948B}.Debug|x64.Build.0 = Debug|x64 - {698C6C2B-7B91-4243-8004-3CA38732948B}.Release|x64.ActiveCfg = Release|x64 - {698C6C2B-7B91-4243-8004-3CA38732948B}.Release|x64.Build.0 = Release|x64 + {60A09FC3-05DB-4754-9305-678A4CFBD6C9}.Debug|Win32.ActiveCfg = Debug|Win32 + {60A09FC3-05DB-4754-9305-678A4CFBD6C9}.Debug|Win32.Build.0 = Debug|Win32 + {60A09FC3-05DB-4754-9305-678A4CFBD6C9}.Release|Win32.ActiveCfg = Release|Win32 + {60A09FC3-05DB-4754-9305-678A4CFBD6C9}.Release|Win32.Build.0 = Release|Win32 + {60A09FC3-05DB-4754-9305-678A4CFBD6C9}.Debug|x64.ActiveCfg = Debug|x64 + {60A09FC3-05DB-4754-9305-678A4CFBD6C9}.Debug|x64.Build.0 = Debug|x64 + {60A09FC3-05DB-4754-9305-678A4CFBD6C9}.Release|x64.ActiveCfg = Release|x64 + {60A09FC3-05DB-4754-9305-678A4CFBD6C9}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/wmi/wmisamp/WmiSamp.vcxproj b/wmi/wmisamp/WmiSamp.vcxproj index 0d36a3546..97c15d36d 100644 --- a/wmi/wmisamp/WmiSamp.vcxproj +++ b/wmi/wmisamp/WmiSamp.vcxproj @@ -19,12 +19,12 @@ - {09048545-F6CE-4908-9BB5-9186BD12776B} + {AB1C3428-9D78-4226-9A5D-DC92A47C279E} $(MSBuildProjectName) 1 Debug Win32 - {F006BD87-731B-4D2A-B741-D0B1F762BABC} + {BB241330-87DE-4B07-A649-644EA6B5DD53} @@ -138,7 +138,6 @@ - diff --git a/wmi/wmisamp/WmiSamp.vcxproj.Filters b/wmi/wmisamp/WmiSamp.vcxproj.Filters index 33866ca0b..5762f68d3 100644 --- a/wmi/wmisamp/WmiSamp.vcxproj.Filters +++ b/wmi/wmisamp/WmiSamp.vcxproj.Filters @@ -3,25 +3,22 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {5EF5CA7C-7CAE-4919-A630-40021ADBAD7A} + {84DF3964-E443-46B3-B677-E075E77ABFEF} h;hpp;hxx;hm;inl;inc;xsd - {36AFBC05-BF36-497F-9479-232E53032646} + {6F32A550-2374-44C0-A563-5FA52662B7D8} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {4754802C-E1A1-40AA-A07E-FDD309D1FE65} + {AF19E735-3412-4D30-9744-AB595DBAB93B} inf;inv;inx;mof;mc; - {2873486C-A803-4AC3-99EC-2F2589EFBAD4} + {E5626644-0466-47E1-8470-73B4A040D5F2} - - Driver Files - Driver Files diff --git a/wmi/wmisamp/wmisamp.inx b/wmi/wmisamp/wmisamp.inx index 9a210d11c..30d83ae8e 100644 --- a/wmi/wmisamp/wmisamp.inx +++ b/wmi/wmisamp/wmisamp.inx @@ -18,7 +18,7 @@ Signature = "$WINDOWS NT$" Class = Sample ClassGUID = {78A1C341-4539-11D3-B88D-00C04FAD5171} -Provider = %MSFT% +Provider = %ProviderString% DriverVer = 10/29/2005,1.0.0.1 CatalogFile = KmdfSamples.cat @@ -111,7 +111,7 @@ KmdfLibraryVersion = $KMDFVERSION$ [Strings] SPSVCINST_ASSOCSERVICE = 0x00000002 -MSFT = "Microsoft" +ProviderString = "TODO-Set-Provider" StdMfg = "(Standard system devices)" ClassName = "Sample Device" DiskId1 = "WMI Sample Installation Disk #1" diff --git a/wmi/wmisamp/wmisamp.sln b/wmi/wmisamp/wmisamp.sln index d29358900..54c943510 100644 --- a/wmi/wmisamp/wmisamp.sln +++ b/wmi/wmisamp/wmisamp.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0 MinimumVisualStudioVersion = 12.0 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WmiSamp", "WmiSamp.vcxproj", "{09048545-F6CE-4908-9BB5-9186BD12776B}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WmiSamp", "WmiSamp.vcxproj", "{AB1C3428-9D78-4226-9A5D-DC92A47C279E}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {09048545-F6CE-4908-9BB5-9186BD12776B}.Debug|Win32.ActiveCfg = Debug|Win32 - {09048545-F6CE-4908-9BB5-9186BD12776B}.Debug|Win32.Build.0 = Debug|Win32 - {09048545-F6CE-4908-9BB5-9186BD12776B}.Release|Win32.ActiveCfg = Release|Win32 - {09048545-F6CE-4908-9BB5-9186BD12776B}.Release|Win32.Build.0 = Release|Win32 - {09048545-F6CE-4908-9BB5-9186BD12776B}.Debug|x64.ActiveCfg = Debug|x64 - {09048545-F6CE-4908-9BB5-9186BD12776B}.Debug|x64.Build.0 = Debug|x64 - {09048545-F6CE-4908-9BB5-9186BD12776B}.Release|x64.ActiveCfg = Release|x64 - {09048545-F6CE-4908-9BB5-9186BD12776B}.Release|x64.Build.0 = Release|x64 + {AB1C3428-9D78-4226-9A5D-DC92A47C279E}.Debug|Win32.ActiveCfg = Debug|Win32 + {AB1C3428-9D78-4226-9A5D-DC92A47C279E}.Debug|Win32.Build.0 = Debug|Win32 + {AB1C3428-9D78-4226-9A5D-DC92A47C279E}.Release|Win32.ActiveCfg = Release|Win32 + {AB1C3428-9D78-4226-9A5D-DC92A47C279E}.Release|Win32.Build.0 = Release|Win32 + {AB1C3428-9D78-4226-9A5D-DC92A47C279E}.Debug|x64.ActiveCfg = Debug|x64 + {AB1C3428-9D78-4226-9A5D-DC92A47C279E}.Debug|x64.Build.0 = Debug|x64 + {AB1C3428-9D78-4226-9A5D-DC92A47C279E}.Release|x64.ActiveCfg = Release|x64 + {AB1C3428-9D78-4226-9A5D-DC92A47C279E}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/wpd/WpdBasicHardwareDriver/Device.cpp b/wpd/WpdBasicHardwareDriver/Device.cpp new file mode 100644 index 000000000..22a7ade84 --- /dev/null +++ b/wpd/WpdBasicHardwareDriver/Device.cpp @@ -0,0 +1,415 @@ +#include "stdafx.h" +#include "Device.tmh" + +#include "WpdBasicHardwareDriver_i.c" + +STDMETHODIMP_(HRESULT) +CDevice::OnD0Entry(_In_ IWDFDevice* pDevice, + WDF_POWER_DEVICE_STATE previousState) +{ + TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_FLAG_DEVICE, "%!FUNC! Entry"); + + UNREFERENCED_PARAMETER(pDevice); + UNREFERENCED_PARAMETER(previousState); + + HRESULT hr = S_OK; + RS232Target* pTarget = NULL; + + if (m_pWpdBaseDriver != NULL) + { + pTarget = m_pWpdBaseDriver->GetRS232Target(); + if (pTarget != NULL) + { + hr = pTarget->Start(); + } + } + + return hr; +} + +STDMETHODIMP_(HRESULT) +CDevice::OnD0Exit(_In_ IWDFDevice* pDevice, + WDF_POWER_DEVICE_STATE newState) +{ + TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_FLAG_DEVICE, "%!FUNC! Entry"); + + UNREFERENCED_PARAMETER(pDevice); + UNREFERENCED_PARAMETER(newState); + + HRESULT hr = S_OK; + RS232Target* pTarget = NULL; + + if (m_pWpdBaseDriver != NULL) + { + pTarget = m_pWpdBaseDriver->GetRS232Target(); + if (pTarget != NULL) + { + hr = pTarget->Stop(); + } + } + return hr; +} + +STDMETHODIMP_(VOID) +CDevice::OnSurpriseRemoval(_In_ IWDFDevice* pDevice) +{ + UNREFERENCED_PARAMETER(pDevice); + return; +} + +STDMETHODIMP_(HRESULT) +CDevice::OnQueryRemove(_In_ IWDFDevice* /*pDevice*/) +{ + return S_OK; +} + +STDMETHODIMP_(HRESULT) +CDevice::OnQueryStop(_In_ IWDFDevice* /*pDevice*/) +{ + return S_OK; +} + +STDMETHODIMP_(VOID) +CDevice::OnSelfManagedIoCleanup(_In_ IWDFDevice* pDevice) +{ + UNREFERENCED_PARAMETER(pDevice); + return; +} + +STDMETHODIMP_(VOID) +CDevice::OnSelfManagedIoFlush(_In_ IWDFDevice* pDevice) +{ + UNREFERENCED_PARAMETER(pDevice); + return; +} + +STDMETHODIMP_(HRESULT) +CDevice::OnSelfManagedIoInit(_In_ IWDFDevice* pDevice) +{ + UNREFERENCED_PARAMETER(pDevice); + return S_OK; +} + +STDMETHODIMP_(HRESULT) +CDevice::OnSelfManagedIoSuspend(_In_ IWDFDevice* pDevice) +{ + UNREFERENCED_PARAMETER(pDevice); + return S_OK; +} + +STDMETHODIMP_(HRESULT) +CDevice::OnSelfManagedIoRestart(_In_ IWDFDevice* pDevice) +{ + UNREFERENCED_PARAMETER(pDevice); + return S_OK; +} + +STDMETHODIMP_(HRESULT) +CDevice::OnSelfManagedIoStop(_In_ IWDFDevice* pDevice) +{ + UNREFERENCED_PARAMETER(pDevice); + return S_OK; +} + +STDMETHODIMP_(HRESULT) +CDevice::OnPrepareHardware(_In_ IWDFDevice* pDevice) +{ + TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_FLAG_DEVICE, "%!FUNC! Entry"); + + HRESULT hr = S_OK; + + if (m_pWpdBaseDriver != NULL) + { + hr = m_pWpdBaseDriver->Initialize(pDevice); + CHECK_HR(hr, "Failed to Initialize the driver class"); + } + + // Initialize the WPD Class Extension. This will enable the appropriate WPD interface GUID, + // as well as do any additional initialization (e.g. enabling Legacy Compatibility layers for those drivers + // which requested support in their INF). + if (hr == S_OK && m_pPortableDeviceClassExtension == NULL) + { + hr = CoCreateInstance(CLSID_PortableDeviceClassExtension, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceClassExtension, + (VOID**)&m_pPortableDeviceClassExtension); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceClassExtension"); + + if (hr == S_OK) + { + CComPtr pOptions; + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**)&pOptions); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues"); + + if (hr == S_OK) + { + CComPtr pContentTypes; + hr = CoCreateInstance(CLSID_PortableDevicePropVariantCollection, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDevicePropVariantCollection, + (VOID**)&pContentTypes); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDevicePropVariantCollection"); + + // Driver has no supported content types, add an empty list of supported content types to the options + if (hr == S_OK) + { + hr = pOptions->SetIPortableDevicePropVariantCollectionValue(WPD_CLASS_EXTENSION_OPTIONS_SUPPORTED_CONTENT_TYPES, pContentTypes); + CHECK_HR(hr, "Failed to set WPD_CLASS_EXTENSION_OPTIONS_SUPPORTED_CONTENT_TYPES"); + } + + if (hr == S_OK) + { + // Initialize the PortableDeviceClassExtension + hr = m_pPortableDeviceClassExtension->Initialize(pDevice, pOptions); + CHECK_HR(hr, "Failed to Initialize portable device class extension object"); + } + } + } + + if (hr == S_OK) + { + // Since users commonly have the abiltity to customize their device even when it is not + // connected to the PC, we need to make sure the PC is current when the driver loads. + // + // Send the latest device friendly name to the PortableDeviceClassExtension component + // so the system is always updated with the current device name. + // + // This call should also be made after a successful property set operation of + // WPD_DEVICE_FRIENDLY_NAME. + LPWSTR wszDeviceFriendlyName = NULL; + + if (hr == S_OK) + { + hr = GetDeviceFriendlyName(&wszDeviceFriendlyName); + CHECK_HR(hr, "Failed to get device's friendly name"); + } + + if (hr == S_OK && wszDeviceFriendlyName != NULL) + { + hr = UpdateDeviceFriendlyName(m_pPortableDeviceClassExtension, wszDeviceFriendlyName); + CHECK_HR(hr, "Failed to update device's friendly name"); + } + + // Free the memory. + CoTaskMemFree(wszDeviceFriendlyName); + wszDeviceFriendlyName = NULL; + } + } + return hr; +} + +STDMETHODIMP_(HRESULT) +CDevice::OnReleaseHardware(_In_ IWDFDevice* pDevice) +{ + TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_FLAG_DEVICE, "%!FUNC! Entry"); + + UNREFERENCED_PARAMETER(pDevice); + if (m_pWpdBaseDriver != NULL) + { + m_pWpdBaseDriver->Uninitialize(); + } + + if (m_pPortableDeviceClassExtension != NULL) + { + m_pPortableDeviceClassExtension = NULL; + } + + return S_OK; +} + +HRESULT CDevice::GetDeviceFriendlyName( + _Outptr_result_maybenull_ LPWSTR* pwszDeviceFriendlyName) +{ + HRESULT hr = S_OK; + + CComPtr pParams; + CComPtr pResults; + CComPtr pKeys; + CComPtr pValues; + + if (pwszDeviceFriendlyName == NULL) + { + hr = E_INVALIDARG; + return hr; + } + + *pwszDeviceFriendlyName = NULL; + + // CoCreate a collection to store the WPD_COMMAND_OBJECT_PROPERTIES_GET command parameters. + if (hr == S_OK) + { + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**)&pParams); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues"); + } + + // CoCreate a collection to store the WPD_COMMAND_OBJECT_PROPERTIES_GET command results. + if (hr == S_OK) + { + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**)&pResults); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues"); + } + + // CoCreate a collection to store the requested property keys. In our case, we are requesting just the device friendly name + // (WPD_DEVICE_FRIENDLY_NAME) + if (hr == S_OK) + { + hr = CoCreateInstance(CLSID_PortableDeviceKeyCollection, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceKeyCollection, + (VOID**)&pKeys); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceKeyCollection for results"); + } + + // Set the params + if (hr == S_OK) + { + hr = pParams->SetGuidValue(WPD_PROPERTY_COMMON_COMMAND_CATEGORY, WPD_COMMAND_OBJECT_PROPERTIES_GET.fmtid); + CHECK_HR(hr, ("Failed to set WPD_PROPERTY_COMMON_COMMAND_CATEGORY")); + } + + if (hr == S_OK) + { + hr = pParams->SetUnsignedIntegerValue(WPD_PROPERTY_COMMON_COMMAND_ID, WPD_COMMAND_OBJECT_PROPERTIES_GET.pid); + CHECK_HR(hr, ("Failed to set WPD_PROPERTY_COMMON_COMMAND_ID")); + } + + if (hr == S_OK) + { + hr = pParams->SetStringValue(WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID, WPD_DEVICE_OBJECT_ID); + CHECK_HR(hr, ("Failed to set WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID")); + } + + if (hr == S_OK) + { + hr = pKeys->Add(WPD_DEVICE_FRIENDLY_NAME); + CHECK_HR(hr, ("Failed to add WPD_DEVICE_FRIENDLY_NAME to key collection")); + } + + if (hr == S_OK) + { + hr = pParams->SetIPortableDeviceKeyCollectionValue(WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_KEYS, pKeys); + CHECK_HR(hr, ("Failed to set WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_KEYS")); + } + + // Make the call + if (hr == S_OK) + { + hr = m_pWpdBaseDriver->DispatchWpdMessage(pParams, pResults); + CHECK_HR(hr, ("Failed to dispatch message to get supported content types")); + } + + // Get the results + if (hr == S_OK) + { + hr = pResults->GetIPortableDeviceValuesValue(WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_VALUES, &pValues); + CHECK_HR(hr, ("Failed to get WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_VALUES")); + } + + if (hr == S_OK) + { + hr = pValues->GetStringValue(WPD_DEVICE_FRIENDLY_NAME, pwszDeviceFriendlyName); + CHECK_HR(hr, ("Failed to get WPD_DEVICE_FRIENDLY_NAME")); + } + + return hr; +} + +HRESULT UpdateDeviceFriendlyName( + _In_ IPortableDeviceClassExtension* pPortableDeviceClassExtension, + _In_ LPCWSTR wszDeviceFriendlyName) +{ + HRESULT hr = S_OK; + + // If we were passed NULL parameters we have nothing to do, return S_OK. + if ((pPortableDeviceClassExtension == NULL) || + (wszDeviceFriendlyName == NULL)) + { + return S_OK; + } + + CComPtr pParams; + CComPtr pResults; + CComPtr pValues; + + // Prepare to make a call to set the device information + if (hr == S_OK) + { + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**)&pParams); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues"); + } + + if (hr == S_OK) + { + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**)&pResults); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues for results"); + } + + if (hr == S_OK) + { + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**)&pValues); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues for results"); + } + + // Get the information values to update and set them in WPD_PROPERTY_CLASS_EXTENSION_DEVICE_INFORMATION_VALUES + if (hr == S_OK) + { + hr = pValues->SetStringValue(WPD_DEVICE_FRIENDLY_NAME, wszDeviceFriendlyName); + CHECK_HR(hr, ("Failed to set WPD_DEVICE_FRIENDLY_NAME")); + } + + // Set the params + if (hr == S_OK) + { + hr = pParams->SetGuidValue(WPD_PROPERTY_COMMON_COMMAND_CATEGORY, WPD_COMMAND_CLASS_EXTENSION_WRITE_DEVICE_INFORMATION.fmtid); + CHECK_HR(hr, ("Failed to set WPD_PROPERTY_COMMON_COMMAND_CATEGORY")); + } + if (hr == S_OK) + { + hr = pParams->SetUnsignedIntegerValue(WPD_PROPERTY_COMMON_COMMAND_ID, WPD_COMMAND_CLASS_EXTENSION_WRITE_DEVICE_INFORMATION.pid); + CHECK_HR(hr, ("Failed to set WPD_PROPERTY_COMMON_COMMAND_ID")); + } + if (hr == S_OK) + { + hr = pParams->SetIPortableDeviceValuesValue(WPD_PROPERTY_CLASS_EXTENSION_DEVICE_INFORMATION_VALUES, pValues); + CHECK_HR(hr, ("Failed to set WPD_PROPERTY_CLASS_EXTENSION_DEVICE_INFORMATION_VALUES")); + } + + // Make the call + if (hr == S_OK) + { + hr = pPortableDeviceClassExtension->ProcessLibraryMessage(pParams, pResults); + CHECK_HR(hr, ("Failed to process update device information message")); + } + + // A Failed ProcessLibraryMessage operation for updating this value is not considered + // fatal and should return S_OK. + + return S_OK; +} diff --git a/wpd/WpdBasicHardwareDriver/Device.h b/wpd/WpdBasicHardwareDriver/Device.h new file mode 100644 index 000000000..6b7ed2d04 --- /dev/null +++ b/wpd/WpdBasicHardwareDriver/Device.h @@ -0,0 +1,89 @@ +#pragma once + +#include "resource.h" +#include "WpdBasicHardwareDriver.h" + +class ATL_NO_VTABLE CDevice : + public CComObjectRootEx, + public IPnpCallback, + public IPnpCallbackSelfManagedIo, + public IPnpCallbackHardware +{ +public: + CDevice() : + m_pWpdBaseDriver(NULL) + { + } + + DECLARE_NOT_AGGREGATABLE(CDevice) + + BEGIN_COM_MAP(CDevice) + COM_INTERFACE_ENTRY(IPnpCallback) + COM_INTERFACE_ENTRY(IPnpCallbackSelfManagedIo) + COM_INTERFACE_ENTRY(IPnpCallbackHardware) + END_COM_MAP() + +public: + static HRESULT + CreateInstance( + _In_ IWDFDeviceInitialize* pDeviceInit, + _In_ WpdBaseDriver* pWpdBaseDriver, + _COM_Outptr_ IUnknown** ppUnkwn) + { + *ppUnkwn = NULL; + + // + // Set device properties. + // + pDeviceInit->SetLockingConstraint(None); + + CComObject< CDevice> *pMyDevice = NULL; + HRESULT hr = CComObject::CreateInstance( &pMyDevice ); + if( SUCCEEDED (hr) ) + { + pMyDevice->AddRef(); + hr = pMyDevice->QueryInterface( __uuidof(IUnknown),(void **) ppUnkwn); + if (hr == S_OK) + { + pMyDevice->m_pWpdBaseDriver = pWpdBaseDriver; + } + pMyDevice->Release(); + pMyDevice = NULL; + } + + return hr; + } + + // IPnpCallback + // + STDMETHOD_(HRESULT, OnD0Entry) (_In_ IWDFDevice* pDevice, WDF_POWER_DEVICE_STATE previousState); + STDMETHOD_(HRESULT, OnD0Exit) (_In_ IWDFDevice* pDevice, WDF_POWER_DEVICE_STATE newState); + STDMETHOD_(VOID, OnSurpriseRemoval)(_In_ IWDFDevice* pDevice); + STDMETHOD_(HRESULT, OnQueryRemove) (_In_ IWDFDevice* pDevice); + STDMETHOD_(HRESULT, OnQueryStop) (_In_ IWDFDevice* pDevice); + + // IPnpCallbackSelfManagedIo + // + STDMETHOD_(VOID, OnSelfManagedIoCleanup)(_In_ IWDFDevice* pDevice); + STDMETHOD_(VOID, OnSelfManagedIoFlush) (_In_ IWDFDevice* pDevice); + STDMETHOD_(HRESULT, OnSelfManagedIoInit) (_In_ IWDFDevice* pDevice); + STDMETHOD_(HRESULT, OnSelfManagedIoSuspend)(_In_ IWDFDevice* pDevice); + STDMETHOD_(HRESULT, OnSelfManagedIoRestart)(_In_ IWDFDevice* pDevice); + STDMETHOD_(HRESULT, OnSelfManagedIoStop) (_In_ IWDFDevice* pDevice); + + // IPnpCallbackHardware + // + STDMETHOD_(HRESULT, OnPrepareHardware)(_In_ IWDFDevice* pDevice); + STDMETHOD_(HRESULT, OnReleaseHardware)(_In_ IWDFDevice* pDevice); + +private: + + HRESULT GetDeviceFriendlyName( + _Outptr_result_maybenull_ LPWSTR* pwszDeviceFriendlyName); + +private: + + WpdBaseDriver* m_pWpdBaseDriver; + CComPtr m_pPortableDeviceClassExtension; +}; + diff --git a/wpd/WpdBasicHardwareDriver/Driver.cpp b/wpd/WpdBasicHardwareDriver/Driver.cpp new file mode 100644 index 000000000..ca0697a16 --- /dev/null +++ b/wpd/WpdBasicHardwareDriver/Driver.cpp @@ -0,0 +1,197 @@ +#include "stdafx.h" + +#include "Driver.tmh" + +CDriver::CDriver() +{ + + +} + +HRESULT +CDriver::OnDeviceAdd( + _In_ IWDFDriver* pDriver, + _In_ IWDFDeviceInitialize* pDeviceInit + ) +/*++ + +Routine Description: + + The framework calls this function when a device is being added to + the driver stack. + +Arguments: + + IWDFDriver - Framework interface. The driver uses this + interface to create device objects. + IWDFDeviceInitialize - Framework interface. The driver uses this + interface to set device parameters before + creating the device obeject. + +Return Value: + + HRESULT S_OK - Device added successfully + +--*/ +{ + TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_FLAG_DRIVER, "%!FUNC! Entry"); + + HRESULT hr = S_OK; + CComPtr pDeviceCallback; + + WpdBaseDriver *pWpdBaseDriver = NULL; + + // + // Create the WPD driver object that handles all WPD messages for this device + // + pWpdBaseDriver = new WpdBaseDriver(); + if(pWpdBaseDriver == NULL) + { + hr = E_OUTOFMEMORY; + } + + if(SUCCEEDED(hr)) + { + // + // Create device callback object + // + hr = CDevice::CreateInstance(pDeviceInit, pWpdBaseDriver, &pDeviceCallback); + } + + // + // This driver has no special power management requirements and so + // we set power policy ownership to UMDF to indicate that UMDF should + // handle powermanagement for us. + // + pDeviceInit->SetPowerPolicyOwnership(FALSE); + + // + // Create WDFDevice. + // + CComPtr pIWDFDevice; + if(SUCCEEDED(hr)) + { + hr = pDriver->CreateDevice( + pDeviceInit, + pDeviceCallback, + &pIWDFDevice); + } + + // + // Assign pWpdBaseDriver to the device object. Each UMDF device requires its own instance of + // a WpdBaseDriver to handle WPD messages. + // + if(SUCCEEDED(hr)) + { + hr = pIWDFDevice->AssignContext(this, (void*)pWpdBaseDriver); + if(SUCCEEDED(hr)) + { + // AddRef the WpdBaseDriver object since it is not stored with the + // device context. + pWpdBaseDriver->AddRef(); + } + } + + // + // Create queue callback object + // + CComPtr pIUnknown; + if(S_OK == hr) + { + hr = CQueue::CreateInstance(&pIUnknown); + } + + // + // Configure the default queue. + // + if(S_OK == hr) + { + CComPtr pDefaultQueue; + hr = pIWDFDevice->CreateIoQueue( + pIUnknown, + TRUE, // bDefaultQueue + WdfIoQueueDispatchSequential, + TRUE, // bPowerManaged + FALSE, // bAllowZeroLengthRequests + &pDefaultQueue); + } + + pDeviceCallback = NULL; + pIWDFDevice = NULL; + + // + // It is fine to release the interface on the callback object. + // The framework has its own refcount on this object and will + // provide an interface when calling into the driver. + // + pIUnknown = NULL; + + // Release the WpdBaseDriver object. If it was successfully added to the device context, + // it was already addref'd above. Releasing it here ensures it will be destroyed if + // an error occured and it could not be added to the device context. + SAFE_RELEASE(pWpdBaseDriver); + + return hr; +} + +void +CDriver::OnDeinitialize( + _In_ IWDFDriver* pDriver + ) +/*++ + +Routine Description: + + The framework calls this function just before de-initializing itself. All + WDF framework resources should be released by driver before returning from this call. + +Arguments: + +Return Value: + +--*/ +{ + UNREFERENCED_PARAMETER(pDriver); + return; +} + +HRESULT +CDriver::OnInitialize( + _In_ IWDFDriver* pDriver + ) +/*++ + +Routine Description: + + The framework calls this function just after loading the driver. The driver can + perform any global, device independent intialization in this routine. + +Arguments: + +Return Value: + +--*/ +{ + UNREFERENCED_PARAMETER(pDriver); + return S_OK; +} + +STDMETHODIMP_ (void) +CDriver::OnCleanup( + _In_ IWDFObject* pWdfObject + ) +{ + TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_FLAG_DRIVER, "%!FUNC! Entry"); + + // Release the base driver object + HRESULT hr = S_OK; + WpdBaseDriver* pWpdBaseDriver = NULL; + + hr = pWdfObject->RetrieveContext((void**)&pWpdBaseDriver); + if((hr == S_OK) && (pWpdBaseDriver != NULL)) + { + pWpdBaseDriver->Release(); + pWpdBaseDriver = NULL; + } +} + diff --git a/wpd/WpdBasicHardwareDriver/Driver.h b/wpd/WpdBasicHardwareDriver/Driver.h new file mode 100644 index 000000000..9b8cadec3 --- /dev/null +++ b/wpd/WpdBasicHardwareDriver/Driver.h @@ -0,0 +1,44 @@ +#pragma once + +class ATL_NO_VTABLE CDriver : + public CComObjectRootEx, + public CComCoClass, + public IDriverEntry, + public IObjectCleanup +{ +public: + CDriver(); + + DECLARE_REGISTRY_RESOURCEID(IDR_WpdBasicHardwareDriver) + + DECLARE_NOT_AGGREGATABLE(CDriver) + + BEGIN_COM_MAP(CDriver) + COM_INTERFACE_ENTRY(IDriverEntry) + END_COM_MAP() + +public: + // + // IDriverEntry + // + STDMETHOD (OnInitialize)( + _In_ IWDFDriver* pDriver + ); + STDMETHOD (OnDeviceAdd)( + _In_ IWDFDriver* pDriver, + _In_ IWDFDeviceInitialize* pDeviceInit + ); + STDMETHOD_ (void, OnDeinitialize)( + _In_ IWDFDriver* pDriver + ); + + // + // IObjectCleanup + // + STDMETHOD_ (void, OnCleanup)( + _In_ IWDFObject* pWdfObject + ); +}; + +OBJECT_ENTRY_AUTO(__uuidof(WpdBasicHardwareDriver), CDriver) + diff --git a/wpd/WpdBasicHardwareDriver/Queue.cpp b/wpd/WpdBasicHardwareDriver/Queue.cpp new file mode 100644 index 000000000..e4e81c097 --- /dev/null +++ b/wpd/WpdBasicHardwareDriver/Queue.cpp @@ -0,0 +1,340 @@ +// Queue.cpp : Implementation of CQueue + + +#include "stdafx.h" +#include + +#include "Queue.tmh" + +// Add table used to lookup the Access required for Wpd Commands +BEGIN_WPD_COMMAND_ACCESS_MAP(g_WpdCommandAccessMap) + DECLARE_WPD_STANDARD_COMMAND_ACCESS_ENTRIES + // Add any custom commands here e.g. + // WPD_COMMAND_ACCESS_ENTRY(MyCustomCommand, WPD_COMMAND_ACCESS_READWRITE) +END_WPD_COMMAND_ACCESS_MAP + +// This enables use to use VERIFY_WPD_COMMAND_ACCESS to check command access function for us. +DECLARE_VERIFY_WPD_COMMAND_ACCESS; + +/****************************************************************************** + * This function calls the WpdBaseDriver to handle the WPD message. In order + * to do this it does the following: + * + * - Deserializes pBuffer into an IPortableDeviceValues which holds the command + * input parameters from the WPD application. + * - Creates an IPortableDeviceValues for the results. + * - Calls the WpdBaseDriver to handle the message. (The results of this + * operation are put into the previously created results IPortableDeviceValues.) + * - The results IPortableDeviceValues is then serialized back into pBuffer, making + * sure that it does not overrun ulOutputBufferLength. + * + *****************************************************************************/ +HRESULT CQueue::ProcessWpdMessage( + ULONG ControlCode, + _In_ ContextMap* pClientContextMap, + _In_ IWDFDevice* pDevice, + _In_reads_bytes_(ulInputBufferLength) PVOID pInBuffer, + ULONG ulInputBufferLength, + _Out_writes_bytes_to_(ulOutputBufferLength, *pdwBytesWritten) PVOID pOutBuffer, + ULONG ulOutputBufferLength, + _Out_ DWORD* pdwBytesWritten) +{ + CComCritSecLock Lock(m_CriticalSection); + + HRESULT hr = S_OK; + CComPtr pParams; + CComPtr pResults; + CComPtr pWpdBaseDriver; + + *pdwBytesWritten = 0; + + if (hr == S_OK) + { + hr = m_pWpdSerializer->GetIPortableDeviceValuesFromBuffer((BYTE*)pInBuffer, + ulInputBufferLength, + &pParams); + CHECK_HR(hr, "Failed to deserialize command parameters from input buffer"); + } + + // Verify that that command was sent with the appropriate access + if (hr == S_OK) + { + hr = VERIFY_WPD_COMMAND_ACCESS(ControlCode, pParams, g_WpdCommandAccessMap); + CHECK_HR(hr, "Wpd Command was sent with incorrect access flags"); + } + + // Create the WPD results collection + if (hr == S_OK) + { + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**)&pResults); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues"); + } + + // Insert the client context map as one of this driver's private properties. This is + // just a convenient place holder which allows other methods down the chain to + // access the context map. + if (hr == S_OK) + { + hr = pParams->SetIUnknownValue(PRIVATE_SAMPLE_DRIVER_CLIENT_CONTEXT_MAP, pClientContextMap); + CHECK_HR(hr, "Failed to set PRIVATE_SAMPLE_DRIVER_CLIENT_CONTEXT_MAP"); + } + + // Insert the IWDFDevice interface as one of this driver's private properties. This is + // just a convenient place holder which allows other methods down the chain to + // access the WUDF Device object. + if (hr == S_OK) + { + hr = pParams->SetIUnknownValue(PRIVATE_SAMPLE_DRIVER_WUDF_DEVICE_OBJECT, pDevice); + CHECK_HR(hr, "Failed to set PRIVATE_SAMPLE_DRIVER_WUDF_DEVICE_OBJECT"); + } + + // Insert the IWpdSerializer interface as one of this driver's private properties. This is + // just a convenient place holder which allows other methods down the chain to + // access the WPD Serializer object. + if (hr == S_OK) + { + hr = pParams->SetIUnknownValue(PRIVATE_SAMPLE_DRIVER_WPD_SERIALIZER_OBJECT, m_pWpdSerializer); + CHECK_HR(hr, "Failed to set PRIVATE_SAMPLE_DRIVER_WPD_SERIALIZER_OBJECT"); + } + + // Get the WpdBaseDriver so we can dispatch the message + if (hr == S_OK) + { + hr = GetWpdBaseDriver(pDevice, &pWpdBaseDriver); + CHECK_HR(hr, "Failed to get WpdBaseDriver"); + } + + if (hr == S_OK) + { + hr = pWpdBaseDriver->DispatchWpdMessage(pParams, pResults); + CHECK_HR(hr, "Failed to handle WPD command"); + } + + if (hr == S_OK) + { + hr = m_pWpdSerializer->WriteIPortableDeviceValuesToBuffer(ulOutputBufferLength, + pResults, + (BYTE*)pOutBuffer, + pdwBytesWritten); + CHECK_HR(hr, "Failed to serialize results to output buffer"); + } + + return hr; +} + +/****************************************************************************** + * This method gets the WpdBaseDriver associated with the UMDF device object. + * The caller should Release *ppWpdBaseDriver when it is done. + * + * When this device was created, we assigned the WpdBaseDriver as the context. + * So, in order to retrieve the correct WpdBaseDriver for this device, we simply + * get the device context. + *****************************************************************************/ +HRESULT CQueue::GetWpdBaseDriver( + _In_ IWDFDevice* pDevice, + _Outptr_result_nullonfailure_ WpdBaseDriver** ppWpdBaseDriver) +{ + HRESULT hr = S_OK; + WpdBaseDriver* pContext = NULL; + + if((pDevice == NULL) || (ppWpdBaseDriver == NULL)) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter for pDevice or ppWpdBaseDriver"); + } + + *ppWpdBaseDriver = NULL; + + if(SUCCEEDED(hr)) + { + hr = pDevice->RetrieveContext((void**)&pContext); + if(SUCCEEDED(hr)) + { + if(pContext != NULL) + { + pContext->AddRef(); + *ppWpdBaseDriver = pContext; + } + else + { + hr = E_UNEXPECTED; + CHECK_HR(hr, "Device context is NULL"); + } + } + } + + return hr; +} + +// CQueue + +STDMETHODIMP_ (void) +CQueue::OnCreateFile( + _In_ IWDFIoQueue* pQueue, + _In_ IWDFIoRequest* pRequest, + _In_ IWDFFile* pFileObject + ) +{ + UNREFERENCED_PARAMETER(pQueue); + TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_FLAG_QUEUE, "%!FUNC! Entry"); + + // This critical section protects the section of code where we + // Create the serializer and results interfaces used in handling I/O messages. + // We only need to create them once, then we hang on to them for the lifetime of this + // queue object. + CComCritSecLock Lock(m_CriticalSection); + HRESULT hr = S_OK; + + // Create the WPD serializer + if ((hr == S_OK) && + (m_pWpdSerializer == NULL)) + { + hr = CoCreateInstance(CLSID_WpdSerializer, + NULL, + CLSCTX_INPROC_SERVER, + IID_IWpdSerializer, + (VOID**)&m_pWpdSerializer); + + CHECK_HR(hr, "Failed to CoCreate CLSID_WpdSerializer"); + } + + // Create the client context map and associate it with the File Object + // so we can obtain it on a per-client basis. + if (hr == S_OK) + { + ContextMap* pClientContextMap = new ContextMap(); + + if(pClientContextMap != NULL) + { + hr = pFileObject->AssignContext(this, (void*)pClientContextMap); + CHECK_HR(hr, "Failed to set client context map"); + + // Release the client context map if we cannot set it + // properly + if(FAILED(hr)) + { + pClientContextMap->Release(); + pClientContextMap = NULL; + } + } + else + { + hr = E_OUTOFMEMORY; + CHECK_HR(hr, "Failed to create client context map"); + } + } + + pRequest->Complete(hr); + return; +} + +STDMETHODIMP_ (void) +CQueue::OnDeviceIoControl( + _In_ IWDFIoQueue* pQueue, + _In_ IWDFIoRequest* pRequest, + ULONG ControlCode, + SIZE_T InputBufferSizeInBytes, + SIZE_T OutputBufferSizeInBytes + ) +{ + UNREFERENCED_PARAMETER(InputBufferSizeInBytes); + UNREFERENCED_PARAMETER(OutputBufferSizeInBytes); + + HRESULT hr = S_OK; + DWORD dwBytesWritten = 0; + + if(IS_WPD_IOCTL(ControlCode)) + { + BYTE* pInputBuffer = NULL; + SIZE_T cbInputBuffer = 0; + BYTE* pOutputBuffer = NULL; + SIZE_T cbOutputBuffer = 0; + ContextMap* pClientContextMap = NULL; + CComPtr pMemoryIn; + CComPtr pMemoryOut; + CComPtr pDevice; + CComPtr pFileObject; + + // + // Get input memory buffer, the memory object is always returned even if the + // underlying buffer is NULL + // + pRequest->GetInputMemory(&pMemoryIn); + pInputBuffer = (BYTE*) pMemoryIn->GetDataBuffer(&cbInputBuffer); + + // + // Get output memory buffer, the memory object is always returned even if the + // underlying buffer is NULL + // + pRequest->GetOutputMemory(&pMemoryOut); + pOutputBuffer = (BYTE*) pMemoryOut->GetDataBuffer(&cbOutputBuffer); + + // Get the Context map for this client + pRequest->GetFileObject(&pFileObject); + if (pFileObject != NULL) + { + hr = pFileObject->RetrieveContext((void**)&pClientContextMap); + CHECK_HR(hr, "Failed to get Contextmap from WDF File Object"); + + if (hr == S_OK) + { + // Get the device object + pQueue->GetDevice(&pDevice ); + hr = ProcessWpdMessage(ControlCode, + pClientContextMap, + pDevice, + pInputBuffer, + (DWORD)cbInputBuffer, + pOutputBuffer, + (DWORD)cbOutputBuffer, + &dwBytesWritten); + } + } + else + { + hr = E_UNEXPECTED; + CHECK_HR(hr, "WDF File Object is NULL"); + } + } + else + { + hr = E_UNEXPECTED; + CHECK_HR(hr, "Received invalid/unsupported IOCTL code '0x%lx'",ControlCode); + } + + // Complete the request + if (hr == S_OK) + { + pRequest->CompleteWithInformation(hr, dwBytesWritten); + } + else + { + pRequest->Complete(hr); + } + + return; +} + +STDMETHODIMP_ (void) +CQueue::OnCleanup( + _In_ IWDFObject* pWdfObject + ) +{ + TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_FLAG_QUEUE, "%!FUNC! Entry"); + + // Destroy the client context map + HRESULT hr = S_OK; + ContextMap* pClientContextMap = NULL; + + hr = pWdfObject->RetrieveContext((void**)&pClientContextMap); + if((hr == S_OK) && (pClientContextMap != NULL)) + { + pClientContextMap->Release(); + pClientContextMap = NULL; + } +} + diff --git a/wpd/WpdBasicHardwareDriver/Queue.h b/wpd/WpdBasicHardwareDriver/Queue.h new file mode 100644 index 000000000..e5cea69d8 --- /dev/null +++ b/wpd/WpdBasicHardwareDriver/Queue.h @@ -0,0 +1,93 @@ +// Queue.h : Declaration of the CQueue + +#pragma once +#include "resource.h" // main symbols +#include "WpdBasicHardwareDriver.h" + +class ATL_NO_VTABLE CQueue : + public CComObjectRootEx, + public IQueueCallbackDeviceIoControl, + public IQueueCallbackCreate, + public IObjectCleanup +{ +public: + CQueue() + { + + } + + DECLARE_NOT_AGGREGATABLE(CQueue) + + BEGIN_COM_MAP(CQueue) + COM_INTERFACE_ENTRY(IQueueCallbackDeviceIoControl) + COM_INTERFACE_ENTRY(IQueueCallbackCreate) + END_COM_MAP() + +public: + static + HRESULT CreateInstance( + _COM_Outptr_ IUnknown **ppUkwn) + { + *ppUkwn = NULL; + CComObject< CQueue> *pMyQueue = NULL; + HRESULT hr = CComObject::CreateInstance( &pMyQueue ); + if( SUCCEEDED (hr) ) + { + pMyQueue->AddRef(); + hr = pMyQueue->QueryInterface( __uuidof(IUnknown), (void **) ppUkwn ); + pMyQueue->Release(); + pMyQueue = NULL; + } + + return hr; + } + + // + // Wdf Callbacks + // + + // IQueueCallbackCreateClose + // + STDMETHOD_ (void, OnCreateFile)( + _In_ IWDFIoQueue *pQueue, + _In_ IWDFIoRequest *pRequest, + _In_ IWDFFile *pFileObject + ); + + // + // IQueueCallbackDeviceIoControl + // + STDMETHOD_ (void, OnDeviceIoControl)( + _In_ IWDFIoQueue* pQueue, + _In_ IWDFIoRequest* pRequest, + ULONG ControlCode, + SIZE_T InputBufferSizeInBytes, + SIZE_T OutputBufferSizeInBytes + ); + + // + // IObjectCleanup + // + STDMETHOD_ (void, OnCleanup)( + _In_ IWDFObject* pWdfObject + ); + +private: + HRESULT ProcessWpdMessage( + ULONG ControlCode, + _In_ ContextMap* pClientContextMap, + _In_ IWDFDevice* pDevice, + _In_reads_bytes_(ulInputBufferLength) PVOID pInBuffer, + ULONG ulInputBufferLength, + _Out_writes_bytes_to_(ulOutputBufferLength, *pdwBytesWritten) PVOID pOutBuffer, + ULONG ulOutputBufferLength, + _Out_ DWORD* pdwBytesWritten); + + HRESULT GetWpdBaseDriver( + _In_ IWDFDevice* pDevice, + _Outptr_result_nullonfailure_ WpdBaseDriver** ppWpdBaseDriver); + + CComPtr m_pWpdSerializer; + CComAutoCriticalSection m_CriticalSection; +}; + diff --git a/wpd/WpdBasicHardwareDriver/RS232Connection.cpp b/wpd/WpdBasicHardwareDriver/RS232Connection.cpp new file mode 100644 index 000000000..eab5479b7 --- /dev/null +++ b/wpd/WpdBasicHardwareDriver/RS232Connection.cpp @@ -0,0 +1,316 @@ +#include "stdafx.h" + +#include "RS232Connection.tmh" + +/*----------------------------------------------------------------------------- + +FUNCTION: RS232Connection() + +PURPOSE: Constructor. Initializes TTY structure + +COMMENTS: This structure is a collection of TTY attributes + used by all parts of this program + +HISTORY: Date: Author: Comment: + 10/27/95 AllenD Wrote it + 2/14/96 AllenD Removed npTTYInfo + 12/06/06 DonnMo Changed return value type + 12/06/06 DonnMo Replaced macro calls with member-variable settings + 12/06/06 DonnMo Removed initialization of font-related variables + +-----------------------------------------------------------------------------*/ +RS232Connection::RS232Connection() +{ + // + // initialize generial TTY info + // + m_hCommPort = NULL; + m_fConnected = FALSE; + m_fLocalEcho = FALSE; + m_bPort = 1; + m_dwBaudRate = CBR_9600; + m_bByteSize = 8; + m_bParity = NOPARITY; + m_bStopBits = ONESTOPBIT; + m_fAutowrap = TRUE; + m_fNewLine = FALSE; + m_fDisplayErrors = TRUE; + + // + // timeouts + // + + // + // TimeoutsDefault + // We need ReadIntervalTimeout here to cause the read operations + // that we do to actually timeout and become overlapped. + // Specifying 1 here causes ReadFile to return very quickly + // so that our reader thread will continue execution. + // + + m_TimeoutsDefault = { 25, 0, 0, 0, 0 }; + m_timeoutsnew = m_TimeoutsDefault; + + // + // read state and status events + // + m_dwReceiveState = RECEIVE_TTY; + m_dwEventFlags = EVENTFLAGS_DEFAULT; + m_chFlag = FLAGCHAR_DEFAULT; + + // + // Flow Control Settings + // + m_fDtrControl = DTR_CONTROL_ENABLE; + m_fRtsControl = RTS_CONTROL_ENABLE; + m_chXON = ASCII_XON; + m_chXOFF = ASCII_XOFF; + m_wXONLimit = 0; + m_wXOFFLimit = 0; + m_fCTSOutFlow = FALSE; + m_fDSROutFlow = FALSE; + m_fDSRInFlow = FALSE; + m_fXonXoffOutFlow = FALSE; + m_fXonXoffInFlow = FALSE; + m_fTXafterXoffSent = FALSE; + m_fNoReading = FALSE; + m_fNoWriting = FALSE; + m_fNoEvents = FALSE; + m_fNoStatus = FALSE; + m_fDisplayTimeouts = FALSE; + +} + + +/*----------------------------------------------------------------------------- + +FUNCTION: SetPortState( void ) + +PURPOSE: Sets port state based on settings from the user + +COMMENTS: Sets up DCB structure and calls SetCommState. + Sets up new timeouts by calling SetCommTimeouts. + +HISTORY: Date: Author: Comment: + 1/9/96 AllenD Wrote it + 12/06/06 DonnMo Replaced calls to ErrorReporter() with CHECK_HR() + +-----------------------------------------------------------------------------*/ +HRESULT RS232Connection::SetPortState() +{ + HRESULT hr = S_OK; + DCB dcb = {0}; + DWORD dwLastError = 0; + + dcb.DCBlength = sizeof(dcb); + + // + // get current DCB settings + // + if (!GetCommState(m_hCommPort, &dcb)) + { + dwLastError = GetLastError(); + hr = HRESULT_FROM_WIN32(dwLastError); + CHECK_HR(hr, "GetCommState() failed within SetPortState()."); + return hr; + } + + // + // update DCB rate, byte size, parity, and stop bits size + // + dcb.BaudRate = m_dwBaudRate; + dcb.ByteSize = m_bByteSize; + dcb.Parity = m_bParity; + dcb.StopBits = m_bStopBits; + + // + // update event flags + // + if (m_dwEventFlags & EV_RXFLAG) + { + dcb.EvtChar = m_chFlag; + } + else + { + dcb.EvtChar = '\0'; + } + + dcb.EofChar = '\n'; + + // + // update flow control settings + // + dcb.fDtrControl = m_fDtrControl; + dcb.fRtsControl = m_fRtsControl; + + dcb.fOutxCtsFlow = m_fCTSOutFlow; + dcb.fOutxDsrFlow = m_fDSROutFlow; + dcb.fDsrSensitivity = m_fDSRInFlow; + dcb.fOutX = m_fXonXoffOutFlow; + dcb.fInX = m_fXonXoffInFlow; + dcb.fTXContinueOnXoff = m_fTXafterXoffSent; + dcb.XonChar = m_chXON; + dcb.XoffChar = m_chXOFF; + dcb.XonLim = m_wXONLimit; + dcb.XoffLim = m_wXOFFLimit; + + // + // DCB settings not in the user's control + // + dcb.fParity = TRUE; + + // + // set new state + // + if (!SetCommState(m_hCommPort, &dcb)) + { + dwLastError = GetLastError(); + hr = HRESULT_FROM_WIN32(dwLastError); + CHECK_HR(hr, "SetCommState() failed within SetPortState() when setting the new state."); + return hr; + } + + + // + // set new timeouts + // + if (!SetCommTimeouts(m_hCommPort, &m_timeoutsnew)) + { + dwLastError = GetLastError(); + hr = HRESULT_FROM_WIN32(dwLastError); + CHECK_HR(hr, "SetCommTimeouts() failed within SetPortState() when setting the new timeouts."); + return hr; + } + + return hr; +} + + +/*----------------------------------------------------------------------------- + +FUNCTION: Connect( void ) + +PURPOSE: Setup Communication Port with our settings + +RETURN: + S_OK and handle of com port if successful + +-----------------------------------------------------------------------------*/ +HRESULT RS232Connection::Connect(_In_ LPCWSTR wszPortName, _Out_ HANDLE *phCommPort) +{ + HRESULT hr = S_OK; + DWORD dwLastError = 0; + + if (phCommPort == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "A NULL phCommPort parameter was received"); + return hr; + } + + *phCommPort = NULL; + + // + // retrieve a handle for the com port + // and configure the port for asynchronous + // communications. + // + m_hCommPort = CreateFileW(wszPortName, + GENERIC_READ | GENERIC_WRITE, + 0, + 0, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, + 0); + + if (m_hCommPort == NULL) + { + dwLastError = GetLastError(); + hr = HRESULT_FROM_WIN32(dwLastError); + CHECK_HR(hr, "CreateFileW() failed in RS232Connection::Connect"); + return hr; + } + + // + // Save original comm timeouts and set new ones + // + if (!GetCommTimeouts( m_hCommPort, &(m_timeoutsorig))) + { + dwLastError = GetLastError(); + hr = HRESULT_FROM_WIN32(dwLastError); + CHECK_HR(hr, "GetCommTimeouts() failed within RS232Connection::Connect"); + return hr; + } + + // + // Set port state + // + hr = SetPortState(); + if (FAILED(hr)) + { + CHECK_HR(hr, "SetPortState() failed within RS232Connection::Connect"); + return hr; + } + + // + // set comm buffer sizes + // + if (!SetupComm(m_hCommPort, MAX_READ_BUFFER, MAX_WRITE_BUFFER)) + { + dwLastError = GetLastError(); + hr = HRESULT_FROM_WIN32(dwLastError); + CHECK_HR(hr, "SetupComm(SETDTR) failed within RS232Connection::Connect"); + return hr; + } + + // + // raise DTR + // + if (!EscapeCommFunction(m_hCommPort, SETDTR)) + { + dwLastError = GetLastError(); + hr = HRESULT_FROM_WIN32(dwLastError); + CHECK_HR(hr, "EscapeCommFunction() failed within RS232Connection::Connect"); + return hr; + } + + // + // set overall connect flag + // + m_fConnected = TRUE; + + // + // set the return value + // + *phCommPort = m_hCommPort; + + + if (SUCCEEDED(hr)) + { + TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_FLAG_DEVICE, "%!FUNC! handle: %p", m_hCommPort); + } + + return hr; +} + + +/*----------------------------------------------------------------------------- + +FUNCTION: Disconnect( void ) + +PURPOSE: Tears down the Communication Port + +RETURN: nothing + +-----------------------------------------------------------------------------*/ +void RS232Connection::Disconnect() +{ + if (m_hCommPort != NULL) + { + TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_FLAG_DEVICE, "%!FUNC! handle: %p", m_hCommPort); + + CloseHandle(m_hCommPort); + m_hCommPort = NULL; + + } +} \ No newline at end of file diff --git a/wpd/WpdBasicHardwareDriver/RS232Connection.h b/wpd/WpdBasicHardwareDriver/RS232Connection.h new file mode 100644 index 000000000..cd7f10b76 --- /dev/null +++ b/wpd/WpdBasicHardwareDriver/RS232Connection.h @@ -0,0 +1,153 @@ +#pragma once + +// +// Sensor device structures and definitions +// + +// +// Format of the 9-byte receive packet (device to PC): +// +// ORIGINAL TEMP_SENSOR PACKET FORMAT: [ VALUE (4 bytes) | INTERVAL (5 bytes) ] +// +// Format of the multi-byte receive packet (device to PC): +// +// NEW PACKET FORMAT: [ SENSOR_ID (1 byte) | ELEMENT_COUNT (1 byte) | ELEMENT_SIZE (1 byte) | ELEMENTS (size bytes) | INTERVAL (5 bytes) ] +// COMPASS PACKET FORMAT: [ SENSOR_ID = 1 | ELEMENT_COUNT = 1 | ELEMENT_SIZE = 3 | HEADING 3-bytes | INTERVAL (5 bytes) ] +// SENSIRON PACKET FORMAT: [ SENSOR_ID = 2 | ELEMENT_COUNT = 1 | ELEMENT_SIZE = 7 | TEMP 4-bytes | HUMIDITY 3-bytes | INTERVAL (5 bytes) ] +// FLEX PACKET FORMAT: [ SENSOR_ID = 3 | ELEMENT_COUNT = 1 | ELEMENT_SIZE = 5 | PRESSURE 5-bytes | INTERVAL (5 bytes) ] +// PING PACKET FORMAT: [ SENSOR_ID = 4 | ELEMENT_COUNT = 1 | ELEMENT_SIZE = 5 | PRESSURE 5-bytes | INTERVAL (5 bytes) ] +// PIR PACKET FORMAT: [ SENSOR_ID = 5 | ELEMENT_COUNT = 1 | ELEMENT_SIZE = 1 | STATE 1-byte | INTERVAL (5 bytes) ] +// MEMSIC PACKET FORMAT: [ SENSOR_ID = 6 | ELEMENT_COUNT = 1 | ELEMENT_SIZE = 6 | X-Axis Gs 3-bytes | Y-Axis Gs 3-bytes | INTERVAL (5 bytes) ] +// QTI PACKET FORMAT: [ SENSOR_ID = 7 | ELEMENT_COUNT = 1 | ELEMENT_SIZE = 4 | PRESSURE 4-bytes | INTERVAL (5 bytes) ] +// PIEZO PACKET FORMAT: [ SENSOR_ID = 8 | ELEMENT_COUNT = 1 | ELEMENT_SIZE = 1 | STATE 1-byte | INTERVAL (5 bytes) +// HITACHI PACKET FORMAT: [ SENSOR_ID = 9 | ELEMENT_COUNT = 3 | ELEMENT_SIZE = 4 | X-Axis Gs 4-bytes | Y-Axis Gs 4-bytes | Z-Axis Gs 4-bytes | INTERVAL (5 bytes) ] +// +// +// Format of the 6-byte send packet (PC to device): +// The final NULL byte signals the SERIN DEC formatter to stop reading +// +// [ INTERVAL (5 bytes) | 0 (1 byte) ] +// +#define INTERVAL_DATA_LENGTH 6 // count of bytes for interval +#define MIN_DATA_LENGTH 4 // minimum count of bytes for any sensor +#define MAX_DATA_LENGTH 21 // maximum count of bytes for any sensor +#define MAX_AMOUNT_TO_READ (MAX_DATA_LENGTH+INTERVAL_DATA_LENGTH) // maximum total bytes to read + +#define MEMSIC_DATA_LENGTH 9 // count of data bytes for Memsic dual-axis accelerometer +#define HITACHI_DATA_LENGTH 15 // count of data bytes for Hitachi tri-axis accelerometer +#define COMPASS_DATA_LENGTH 6 // count of data bytes for Compass +#define SENSIRON_DATA_LENGTH 10 // count of data bytes for Sensiron temp/humidity sensor +#define PING_DATA_LENGTH 8 // count of data bytes for Ping distance sensor +#define FLEX_DATA_LENGTH 8 // count of data bytes for Flexiforce pressure sensor +#define PIR_DATA_LENGTH 4 // count of data bytes for PIR +#define QTI_DATA_LENGTH 7 // count of data bytes for QTI +#define PIEZO_DATA_LENGTH 4 // count of data bytes for Piezo + +#define MEMSIC_AMOUNT_TO_READ (MEMSIC_DATA_LENGTH+INTERVAL_DATA_LENGTH) // total Memsic byte count +#define HITACHI_AMOUNT_TO_READ (HITACHI_DATA_LENGTH+INTERVAL_DATA_LENGTH) // total Hitachi byte count +#define COMPASS_AMOUNT_TO_READ (COMPASS_DATA_LENGTH+INTERVAL_DATA_LENGTH) // total Compass byte count +#define SENSIRON_AMOUNT_TO_READ (SENSIRON_DATA_LENGTH+INTERVAL_DATA_LENGTH) // total Sensiron byte count +#define PING_AMOUNT_TO_READ (PING_DATA_LENGTH+INTERVAL_DATA_LENGTH) // total Ping byte count +#define FLEX_AMOUNT_TO_READ (FLEX_DATA_LENGTH+INTERVAL_DATA_LENGTH) // total Flexiforce byte count +#define PIR_AMOUNT_TO_READ (PIR_DATA_LENGTH+INTERVAL_DATA_LENGTH) // total PIR byte count +#define QTI_AMOUNT_TO_READ (QTI_DATA_LENGTH+INTERVAL_DATA_LENGTH) // total QTI byte count +#define PIEZO_AMOUNT_TO_READ (PIEZO_DATA_LENGTH+INTERVAL_DATA_LENGTH) // total Piezo byte count + +#define DEVICE_ID 0 // byte 0 contains the Device ID +#define ELEMENT_SIZE 1 // byte 1 contains the Element Size +#define ELEMENT_COUNT 2 // byte 2 contains the Element Count + +// Update interval range in milliseconds +#define SENSOR_UPDATE_INTERVAL_MIN 10 // milliseconds +#define SENSOR_UPDATE_INTERVAL_MAX 60000 // milliseconds +#define SENSOR_UPDATE_INTERVAL_STEP 1 // milliseconds + +// TODO: Change these range values to match your sensor hardware +#define SENSOR_READING_MIN 1 // ?Units +#define SENSOR_READING_MAX 378 // ?Units +#define SENSOR_READING_STEP 1 // ?Units + +// TODO: Change this to match the default COM port to use +#define COM_PORT_NAME L"COM1" +#define NUM_READSTAT_HANDLES 4 +#define MAX_STATUS_LENGTH 100 + +// Ascii definitions +#define ASCII_BEL 0x07 +#define ASCII_BS 0x08 +#define ASCII_LF 0x0A +#define ASCII_CR 0x0D +#define ASCII_XON 0x11 +#define ASCII_XOFF 0x13 + +// Miscellaneous definitions +#define MAX_READ_BUFFER 2048 +#define MAX_WRITE_BUFFER 1024 +#define EVENTFLAGS_DEFAULT EV_BREAK | EV_CTS | EV_DSR | EV_ERR | EV_RING | EV_RLSD +#define FLAGCHAR_DEFAULT '\n' + +// Read states +#define RECEIVE_TTY 0x01 +#define RECEIVE_CAPTURED 0x02 + +class RS232Connection +{ + +public: + RS232Connection(); + + ~RS232Connection() + { + Disconnect(); + } + + HRESULT Connect(_In_ LPCWSTR wszPortName, _Out_ HANDLE *phCommPort); + void Disconnect(); + +private: + HRESULT SetPortState(); + +private: + + // + // Required for the RS232 initialization and communication. + // + + // TTY member variables and defines + HANDLE m_hCommPort; + DWORD m_dwEventFlags; + CHAR m_chFlag; + CHAR m_chXON; + CHAR m_chXOFF; + WORD m_wXONLimit; + WORD m_wXOFFLimit; + DWORD m_fRtsControl; + DWORD m_fDtrControl; + BOOL m_fConnected; + BOOL m_fTransferring; + BOOL m_fRepeating; + BOOL m_fLocalEcho; + BOOL m_fNewLine; + BOOL m_fDisplayErrors; + BOOL m_fAutowrap; + BOOL m_fCTSOutFlow; + BOOL m_fDSROutFlow; + BOOL m_fDSRInFlow; + BOOL m_fXonXoffOutFlow; + BOOL m_fXonXoffInFlow; + BOOL m_fTXafterXoffSent; + BOOL m_fNoReading; + BOOL m_fNoWriting; + BOOL m_fNoEvents; + BOOL m_fNoStatus; + BOOL m_fDisplayTimeouts; + BYTE m_bPort; + BYTE m_bByteSize; + BYTE m_bParity; + BYTE m_bStopBits; + DWORD m_dwBaudRate; + COMMTIMEOUTS m_timeoutsorig; + COMMTIMEOUTS m_timeoutsnew; + COMMTIMEOUTS m_TimeoutsDefault; + DWORD m_dwReceiveState; +}; \ No newline at end of file diff --git a/wpd/WpdBasicHardwareDriver/RS232Target.cpp b/wpd/WpdBasicHardwareDriver/RS232Target.cpp new file mode 100644 index 000000000..bedfecd28 --- /dev/null +++ b/wpd/WpdBasicHardwareDriver/RS232Target.cpp @@ -0,0 +1,433 @@ +#include "stdafx.h" + +#include "RS232Target.tmh" + +RS232Target::RS232Target() : + m_cRef(1), m_pBaseDriver(NULL) +{ +} + +RS232Target::~RS232Target() +{ + Delete(); +} + +ULONG __stdcall RS232Target::AddRef() +{ + InterlockedIncrement((long*) &m_cRef); + return m_cRef; +} + +ULONG __stdcall RS232Target::Release() +{ + ULONG ulRefCount = m_cRef - 1; + + if (InterlockedDecrement((long*) &m_cRef) == 0) + { + delete this; + return 0; + } + return ulRefCount; +} + +HRESULT __stdcall RS232Target::QueryInterface( + REFIID riid, + void** ppv) +{ + HRESULT hr = S_OK; + + if(riid == IID_IUnknown) + { + *ppv = static_cast(this); + AddRef(); + } + if(riid == __uuidof(IRequestCallbackRequestCompletion)) + { + *ppv = static_cast(this); + AddRef(); + } + else + { + *ppv = NULL; + hr = E_NOINTERFACE; + } + return hr; +} + + + +/** + * This method is called to initialize the RS232 I/O Target + */ +HRESULT RS232Target::Create(_In_ WpdBaseDriver* pBaseDriver, + _In_ IWDFDevice* pDevice, + HANDLE hRS232Port) +{ + CComPtr pFileHandleTargetFactory; + + HRESULT hr = S_OK; + + if (pDevice == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "NULL parameter received for IWDFDevice"); + return hr; + } + + m_pWDFDevice = pDevice; + m_pBaseDriver = pBaseDriver; + + hr = m_pWDFDevice->QueryInterface(IID_PPV_ARGS(&pFileHandleTargetFactory)); + CHECK_HR(hr, "QI of IID_IWDFFileHandleTargetFactory failed"); + + if (hr == S_OK) + { + hr = pFileHandleTargetFactory->CreateFileHandleTarget(hRS232Port, &m_pFileTarget); + CHECK_HR(hr, "Failed to create an I/O Target for the port file handle"); + } + + if (hr == S_OK) + { + TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_FLAG_DEVICE, + "%!FUNC! Created win32 I/O target %p for handle %p", m_pFileTarget, hRS232Port); + } + + return hr; +} + +/** + * This method is called to remove the RS232 I/O Target object + * and do any cleanup + */ +void RS232Target::Delete() +{ + if (m_pFileTarget) + { + TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_FLAG_DEVICE, + "%!FUNC! Deleted win32 I/O target %p", m_pFileTarget); + m_pFileTarget = NULL; + } + + if (m_pWDFDevice) + { + m_pWDFDevice = NULL; + } + + if (m_pBaseDriver) + { + m_pBaseDriver = NULL; + } +} + +/** + * This method is called to start the RS232 I/O Target after it + * it has been created + */ +HRESULT RS232Target::Start() +{ + CComPtr pStateMgmt; + + HRESULT hr = S_OK; + + if (m_pFileTarget) + { + hr = m_pFileTarget->QueryInterface(IID_PPV_ARGS(&pStateMgmt)); + CHECK_HR(hr, "Failed to QI IWDFIoTargetStateManagement from the I/O target"); + + if (hr == S_OK) + { + hr = pStateMgmt->Start(); + CHECK_HR(hr, "Failed to start the I/O target"); + } + + if (hr == S_OK && IsReady()) + { + TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_FLAG_DEVICE, "%!FUNC! I/O target ready. Sending read request"); + hr = SendReadRequest(); + CHECK_HR(hr, "SendReadRequest failed"); + } + } + return hr; +} + +/** + * This method is called to stop the RS232 I/O Target + * if it is currently running + */ +HRESULT RS232Target::Stop() +{ + CComPtr pStateMgmt; + + HRESULT hr = S_OK; + + // Stop the target only if it is started. + if (m_pFileTarget && (WdfIoTargetStarted == GetState())) + { + hr = m_pFileTarget->QueryInterface(IID_PPV_ARGS(&pStateMgmt)); + CHECK_HR(hr, "Failed to QI IWDFIoTargetStateManagement from the I/O target"); + + if (hr == S_OK) + { + // Stop the target, and cancel sent I/O + hr = pStateMgmt->Stop(WdfIoTargetCancelSentIo); + CHECK_HR(hr, "Failed to stop the I/O target"); + } + + if (hr == S_OK) + { + TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_FLAG_DEVICE, "%!FUNC! S_OK"); + } + } + return hr; +} + +/** + * This method is called to return the current state of the RS232 I/O Target + */ +WDF_IO_TARGET_STATE RS232Target::GetState() +{ + CComPtr pStateMgmt; + + WDF_IO_TARGET_STATE State = WdfIoTargetStateUndefined; + HRESULT hr = S_OK; + + if (m_pFileTarget) + { + hr = m_pFileTarget->QueryInterface(IID_PPV_ARGS(&pStateMgmt)); + CHECK_HR(hr, "Failed to QI IWDFIoTargetStateManagement from the I/O target"); + + if (hr == S_OK) + { + State = pStateMgmt->GetState(); + } + } + return State; +} + + +/** + * This method returns TRUE is the target is ready to receive requests + */ +BOOL RS232Target::IsReady() +{ + if (WdfIoTargetStarted == GetState()) + { + return TRUE; + } + return FALSE; +} + + +/** + * This method is called to dispatch an asynchronous read request to the RS232 I/O Target + * with a completion callback + */ +HRESULT RS232Target::SendReadRequest() +{ + CComPtr pWdfDriver; + CComPtr pWdfFile; + CComPtr pWdfBuffer; + CComPtr pWdfReadRequest; + CComPtr pCompletionCallback; + + HRESULT hr = S_OK; + + if (m_pWDFDevice == NULL || m_pFileTarget == NULL) + { + hr = HRESULT_FROM_WIN32(ERROR_NOT_READY); + CHECK_HR(hr, "Device is not ready to receive read requests"); + return hr; + } + + m_pWDFDevice->GetDriver(&pWdfDriver); + + ZeroMemory((void*)m_pReadBuffer, sizeof(m_pReadBuffer)); + + // Create the WDF memory buffer + hr = pWdfDriver->CreatePreallocatedWdfMemory(m_pReadBuffer, + sizeof(m_pReadBuffer), + NULL, // no object event callback + NULL, // driver object as parent + &pWdfBuffer); + CHECK_HR(hr, "Failed to create the pre-allocaed WDF memory"); + + if (hr == S_OK) + { + hr = m_pWDFDevice->CreateRequest(NULL, // no object event callback + m_pWDFDevice, // device object as parent + &pWdfReadRequest); + + CHECK_HR(hr, "Failed to create a WDF request"); + } + + if (hr == S_OK) + { + // Format the read request + m_pFileTarget->GetTargetFile(&pWdfFile); + + hr = m_pFileTarget->FormatRequestForRead(pWdfReadRequest, + pWdfFile, + pWdfBuffer, + NULL, // no memory offset + NULL); // no device offset + + CHECK_HR(hr, "Failed to format the WDF request for read"); + } + + if (hr == S_OK) + { + this->QueryInterface(IID_PPV_ARGS(&pCompletionCallback)); + + // Set the completion callback + pWdfReadRequest->SetCompletionCallback(pCompletionCallback, NULL); + + hr = pWdfReadRequest->Send(m_pFileTarget, 0, 0); + CHECK_HR(hr, "Failed to send the read request"); + } + + if (FAILED(hr)) + { + // Cleanup on failure + if (pWdfReadRequest) + { + pWdfReadRequest->DeleteWdfObject(); + } + } + + return hr; +} + + +/** + * This method is called to dispatch an asynchronous write request to the RS232 I/O Target + * with a completion callback + */ +HRESULT RS232Target::SendWriteRequest( + _In_reads_(cbBufferSize) BYTE* pBuffer, + size_t cbBufferSize) +{ + CComPtr pWdfDriver; + CComPtr pWdfFile; + CComPtr pWdfBuffer; + CComPtr pWdfWriteRequest; + CComPtr pCompletionCallback; + + HRESULT hr = S_OK; + + if (pBuffer == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "A NULL buffer parameter was received"); + return hr; + } + + if (m_pWDFDevice == NULL || m_pFileTarget == NULL) + { + hr = HRESULT_FROM_WIN32(ERROR_NOT_READY); + CHECK_HR(hr, "Device is not ready to receive write requests"); + } + + m_pWDFDevice->GetDriver(&pWdfDriver); + m_pFileTarget->GetTargetFile(&pWdfFile); + + // Create the WDF memory buffer + hr = pWdfDriver->CreatePreallocatedWdfMemory(pBuffer, + cbBufferSize, + NULL, // no object event callback + NULL, // driver object as parent + &pWdfBuffer); + CHECK_HR(hr, "Failed to create a pre-allocaed Wdf memory buffer from the input buffer of size %d", static_cast(cbBufferSize)); + + if (hr == S_OK) + { + hr = m_pWDFDevice->CreateRequest(NULL, // no object event callback + m_pWDFDevice, // device object as parent + &pWdfWriteRequest); + + CHECK_HR(hr, "Failed to create a WDF request"); + } + + if (hr == S_OK) + { + hr = m_pFileTarget->FormatRequestForWrite(pWdfWriteRequest, + pWdfFile, + pWdfBuffer, + NULL, // no memory offset + NULL); // no device offset + + CHECK_HR(hr, "Failed to format the WDF request for write"); + } + + if (hr == S_OK) + { + this->QueryInterface(IID_PPV_ARGS(&pCompletionCallback)); + + // Set the completion callback + pWdfWriteRequest->SetCompletionCallback(pCompletionCallback, NULL); + + hr = pWdfWriteRequest->Send(m_pFileTarget, 0, 0); + CHECK_HR(hr, "Failed to send the write request"); + } + + if (FAILED(hr)) + { + // Cleanup on failure + if (pWdfWriteRequest) + { + pWdfWriteRequest->DeleteWdfObject(); + } + } + + return hr; +} + + +/** + * This callback method is called by UMDF on the completion of the asynchronous reads and writes + */ +void RS232Target::OnCompletion( + _In_ IWDFIoRequest* pWdfRequest, + _In_ IWDFIoTarget* pIoTarget, + _In_ IWDFRequestCompletionParams* pParams, + _In_ PVOID pContext) +{ + UNREFERENCED_PARAMETER(pIoTarget); + UNREFERENCED_PARAMETER(pContext); + UNREFERENCED_PARAMETER(pParams); + + CComPtr pCompletionParams; + HRESULT hr = S_OK; + + // Get the request completion status + pWdfRequest->GetCompletionParams(&pCompletionParams); + + HRESULT hrStatus = pCompletionParams->GetCompletionStatus(); + WDF_REQUEST_TYPE RequestType = pCompletionParams->GetCompletedRequestType(); + + if (RequestType == WdfRequestRead) + { + TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_FLAG_DEVICE, "%!FUNC! Read Status %!HRESULT!", hrStatus); + + // Retrieve the data from the completed read request if successful + if (SUCCEEDED(hrStatus) && m_pBaseDriver) + { + m_pBaseDriver->ProcessReadData(m_pReadBuffer, sizeof(m_pReadBuffer)); + } + + // Send another read request if the target is not stopped or removed + if (IsReady()) + { + hr = SendReadRequest(); + CHECK_HR(hr, "SendReadRequest failed"); + } + + + } + else if (RequestType == WdfRequestWrite) + { + TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_FLAG_DEVICE, "%!FUNC! Write Status %!HRESULT!", hrStatus); + } + + // Clean up the existing request + pWdfRequest->DeleteWdfObject(); +} diff --git a/wpd/WpdBasicHardwareDriver/RS232Target.h b/wpd/WpdBasicHardwareDriver/RS232Target.h new file mode 100644 index 000000000..b4754639b --- /dev/null +++ b/wpd/WpdBasicHardwareDriver/RS232Target.h @@ -0,0 +1,53 @@ +#pragma once + +class RS232Target : + public IRequestCallbackRequestCompletion +{ +public: + RS232Target(); + + ~RS232Target(); + + HRESULT Create(_In_ WpdBaseDriver* pBaseDriver, + _In_ IWDFDevice* pDevice, + HANDLE hRS232Port); + + void Delete(); + + HRESULT Start(); + + HRESULT Stop(); + + BOOL IsReady(); + + WDF_IO_TARGET_STATE GetState(); + + HRESULT SendReadRequest(); + + HRESULT SendWriteRequest(_In_reads_(cbBufferSize) BYTE* pBuffer, + size_t cbBufferSize); + +public: // IUnknown + ULONG __stdcall AddRef(); + ULONG __stdcall Release(); + HRESULT __stdcall QueryInterface(REFIID riid, void** ppv); + + // + // IRequestCallbackRequestCompletion + // + STDMETHOD_ (void, OnCompletion)(_In_ IWDFIoRequest* pWdfRequest, + _In_ IWDFIoTarget* pIoTarget, + _In_ IWDFRequestCompletionParams* pParams, + _In_ PVOID pContext); + +private: + ULONG m_cRef; + + WpdBaseDriver* m_pBaseDriver; + + // Ensure ample buffer size + BYTE m_pReadBuffer[MAX_AMOUNT_TO_READ*2]; + + CComPtr m_pWDFDevice; + CComPtr m_pFileTarget; +}; \ No newline at end of file diff --git a/wpd/WpdBasicHardwareDriver/ReadMe.md b/wpd/WpdBasicHardwareDriver/ReadMe.md new file mode 100644 index 000000000..856efb065 --- /dev/null +++ b/wpd/WpdBasicHardwareDriver/ReadMe.md @@ -0,0 +1,45 @@ +WPD Basic Hardware Sample Driver (UMDF Version 1) +================================================= + +The WpdBasicHardwareDriver is a WPD driver that supports nine devices. These devices were selected because of their simplicity. This simplicity allowed the sample to focus on the tasks that are common to portable devices without getting bogged down in hardware complexities. + +This sample driver is based on the WpdHelloWorldDriver that is also included in the Windows Driver Kit (WDK). The "Supporting the WPD Infrastructure†sections for this driver show the changes that were made to the WpdHelloWorldDriver source so that it can communicate with basic hardware devices. Before you work through the topics in this section of the documentation, be familiar with the WpdHelloWorldDriver. + +The sensor devices that are supported by the WpdBasicHardwareDriver, such as the Memsic 2125 Accelerometer, are sold by the Parallax Corporation in Rocklin, California. + +To use these sensors with the WpdBasicHardwareDriver, you must purchase the sensors, a programmable microcontroller (Parallax BS2), a test board (like the Parallax BASIC Stamp Homework Board), an RS232 cable, and miscellaneous parts. All of this hardware is available from Parallax and can be ordered through their Web site. + +The circuit designs are based on the sample circuits provided by Parallax in their sensor data sheets. These circuits are designed to integrate each sensor with the Parallax BS2 programmable microcontroller . + +The microcontroller firmware for each of the nine circuits is included in the src\\wpd\\WpdBasicHardwareDriver\\firmware subdirectory in the Windows Driver Kit (WDK). + +For a complete description of this sample and its underlying code and functionality, refer to the [WPD Basic Hardware Driver](http://msdn.microsoft.com/en-us/library/windows/hardware/ff597697) description in the Windows Driver Kit documentation. + + +Related topics +-------------- + +[WPD Design Guide](http://msdn.microsoft.com/en-us/library/windows/hardware/ff597864) + +[WPD Driver Development Tools](http://msdn.microsoft.com/en-us/library/windows/hardware/ff597568) + +[WPD Programming Guide](http://msdn.microsoft.com/en-us/library/windows/hardware/) + + +Installation +------------ + +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 WpdBasicHardwareDriver sample, do the following: + +1. Copy the driver binary and the wpdbasichardwaredriver.inf file to a directory on your test computer (for example, C:\\wpdbasichardwaredriver.) + +2. Copy the UMDF coinstaller, WUDFUpdate\_*MMmmmm*.dll, from the \\redist\\wdf\\\ directory to the same directory (for example, C:\\wpdbasichardwaredriver). + + **Note**  Starting in Windows 8.1, the WDK no longer contains the co-installers by default. You can obtain the co-installers by downloading and installing the “Windows Driver Framework (WDF)†package from [WDK 8 Redistributable Components](http://go.microsoft.com/fwlink/p/?LinkID=226396). + +3. Navigate to the directory that contains the INF file and binaries (for example, cd /d c:\\wpdbasichardwaredriver), and run DevCon.exe as follows: + **devcon.exe install wpdbasichardwaredriver.inf WUDF\\WpdBasicHardware** + You can find DevCon.exe in the \\tools directory of the WDK (for example, \\tools\\devcon\\i386\\devcon.exe). + diff --git a/wpd/WpdBasicHardwareDriver/Stdafxsrc.cpp b/wpd/WpdBasicHardwareDriver/Stdafxsrc.cpp new file mode 100644 index 000000000..5105a28de --- /dev/null +++ b/wpd/WpdBasicHardwareDriver/Stdafxsrc.cpp @@ -0,0 +1 @@ +#include "Stdafx.h" \ No newline at end of file diff --git a/wpd/WpdBasicHardwareDriver/WpdBaseDriver.cpp b/wpd/WpdBasicHardwareDriver/WpdBaseDriver.cpp new file mode 100644 index 000000000..cf1665c29 --- /dev/null +++ b/wpd/WpdBasicHardwareDriver/WpdBaseDriver.cpp @@ -0,0 +1,506 @@ +#include "stdafx.h" + +#include "WpdBaseDriver.tmh" + + +WpdBaseDriver::WpdBaseDriver() : + m_cRef(1) +{ +} + +WpdBaseDriver::~WpdBaseDriver() +{ +} + +ULONG __stdcall WpdBaseDriver::AddRef() +{ + InterlockedIncrement((long*) &m_cRef); + return m_cRef; +} + +_At_(this, __drv_freesMem(Mem)) +ULONG __stdcall WpdBaseDriver::Release() +{ + ULONG ulRefCount = m_cRef - 1; + + if (InterlockedDecrement((long*) &m_cRef) == 0) + { + delete this; + return 0; + } + return ulRefCount; +} + +HRESULT __stdcall WpdBaseDriver::QueryInterface( + REFIID riid, + void** ppv) +{ + HRESULT hr = S_OK; + + if(riid == IID_IUnknown) + { + *ppv = static_cast(this); + AddRef(); + } + else + { + *ppv = NULL; + hr = E_NOINTERFACE; + } + return hr; +} + +RS232Target* WpdBaseDriver::GetRS232Target() +{ + return &m_Target; +} + +HRESULT WpdBaseDriver::DispatchWpdMessage(_In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + GUID guidCommandCategory = {0}; + DWORD dwCommandID = 0; + PROPERTYKEY CommandKey = WPD_PROPERTY_NULL; + + if (hr == S_OK) + { + hr = pParams->GetGuidValue(WPD_PROPERTY_COMMON_COMMAND_CATEGORY, &guidCommandCategory); + CHECK_HR(hr, "Failed to get WPD_PROPERTY_COMMON_COMMAND_CATEGORY from input parameters"); + } + + if (hr == S_OK) + { + hr = pParams->GetUnsignedIntegerValue(WPD_PROPERTY_COMMON_COMMAND_ID, &dwCommandID); + CHECK_HR(hr, "Failed to get WPD_PROPERTY_COMMON_COMMAND_ID from input parameters"); + } + + // If WPD_PROPERTY_COMMON_COMMAND_CATEGORY or WPD_PROPERTY_COMMON_COMMAND_ID could not be extracted + // properly then we should return E_INVALIDARG to the client. + if (FAILED(hr)) + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Failed to get WPD_PROPERTY_COMMON_COMMAND_CATEGORY or WPD_PROPERTY_COMMON_COMMAND_ID from input parameters"); + } + + if (hr == S_OK) + { + CommandKey.fmtid = guidCommandCategory; + CommandKey.pid = dwCommandID; + + if (CommandKey.fmtid == WPD_CATEGORY_OBJECT_ENUMERATION) + { + hr = m_ObjectEnum.DispatchWpdMessage(CommandKey, pParams, pResults); + } + else if (CommandKey.fmtid == WPD_CATEGORY_OBJECT_PROPERTIES) + { + hr = m_ObjectProperties.DispatchWpdMessage(CommandKey, pParams, pResults); + } + else if (CommandKey.fmtid == WPD_CATEGORY_CAPABILITIES) + { + hr = m_Capabilities.DispatchWpdMessage(CommandKey, pParams, pResults); + } + else if (IsEqualPropertyKey(CommandKey, WPD_COMMAND_COMMON_GET_OBJECT_IDS_FROM_PERSISTENT_UNIQUE_IDS)) + { + hr = OnGetObjectIDsFromPersistentUniqueIDs(pParams, pResults); + } + else + { + hr = E_NOTIMPL; + CHECK_HR(hr, "Unknown command %ws.%d received",CComBSTR(CommandKey.fmtid), CommandKey.pid); + } + } + + HRESULT hrTemp = pResults->SetErrorValue(WPD_PROPERTY_COMMON_HRESULT, hr); + CHECK_HR(hrTemp, ("Failed to set WPD_PROPERTY_COMMON_HRESULT")); + + // Set to a success code, to indicate that the message was received. + // the return code for the actual command's results is stored in the + // WPD_PROPERTY_COMMON_HRESULT property. + hr = S_OK; + + return hr; +} + +/** + * This method is called to initialize the driver object. + * This is where the driver establishes a connection with the + * COM port, creates the IoTarget to forward read/write requests to, and + * starts the thread which monitors these events. + */ +HRESULT WpdBaseDriver::Initialize(_In_ IWDFDevice* pDevice) +{ + HRESULT hr = S_OK; + HANDLE hPort = NULL; + + TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_FLAG_DRIVER, "%!FUNC! Entry"); + + if (pDevice == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "A NULL IWDFDevice parameter was received."); + return hr; + } + + // Save the WDF device instance + m_pWDFDevice = pDevice; + m_ObjectProperties.Initialize(this); + m_ObjectEnum.Initialize(this); + + // Initialize the Wpd Serializer for posting events + hr = CoCreateInstance(CLSID_WpdSerializer, + NULL, + CLSCTX_INPROC_SERVER, + IID_IWpdSerializer, + (VOID**)&m_pWpdSerializer); + + CHECK_HR(hr, "Failed to CoCreate the Wpd Serializer."); + + + // Initialize a handle to the RS232 connection + hr = m_Connection.Connect(COM_PORT_NAME, &hPort); + CHECK_HR(hr, "Failed to connect to port: %ws", COM_PORT_NAME); + + // Initialize the IoTarget to wrap the opened handle + if (hr == S_OK) + { + hr = m_Target.Create(this, pDevice, hPort); + CHECK_HR(hr, "Failed to create the port I/O target."); + } + + return hr; +} + +/** + * This method is called to uninitialize the driver object. + * This is where the driver disables the connection with the + * COM port and performs the necessary cleanup. + */ +void WpdBaseDriver::Uninitialize() +{ + TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_FLAG_DRIVER, "%!FUNC! Entry"); + + m_Target.Delete(); + m_Connection.Disconnect(); + + m_pWDFDevice = NULL; +} + +/** + * This method is called to extract the sensor data and interval prop from the raw serial buffer + * and to post a new reading PnP event if valid data is received + */ +HRESULT WpdBaseDriver::ProcessReadData(_In_reads_(cbData) BYTE* pData, size_t cbData) +{ + HRESULT hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + LONGLONG llSensorReading = 0; // was originally DWORD but the accelerometers required addt'l bytes + DWORD dwUpdateInterval = 0; + + // Parse the serial data + CHAR szInterval[INTERVAL_DATA_LENGTH + 1] = {0}; // last byte is always null + CHAR* szReading = NULL; // buffer containing the reading + + const SENSOR_INFO c_SensorInfoTable [] = { + {'1', COMPASS_DATA_LENGTH}, // Compass + {'2', SENSIRON_DATA_LENGTH}, // Temp/humidity sensor + {'3', FLEX_DATA_LENGTH}, // Flexiforce sensor + {'4', PING_DATA_LENGTH}, // Ultrasonic ping + {'5', PIR_DATA_LENGTH}, // Passive infrared + {'6', MEMSIC_DATA_LENGTH}, // 2-axis accelerometer + {'7', QTI_DATA_LENGTH}, // Light sensor + {'8', PIEZO_DATA_LENGTH}, // Vibration Sensor + {'9', HITACHI_DATA_LENGTH}, // 3-axis accelerometer + }; + + // Allocate the necessary bytes for collecting the sensor data. + // We'll examine the DEVICE_ID field of the batched serial data and allocate + // the necessary bytes accordingly... + // This is also where we set the sensor identifier and related properties). + + if (cbData > DEVICE_ID) + { + switch (pData[DEVICE_ID]){ + case '1': + m_SensorType = COMPASS; + szReading = (CHAR *)malloc(COMPASS_DATA_LENGTH + 1); // last byte is always null + hr = S_OK; + break; + case '2': + m_SensorType = SENSIRON; + szReading = (CHAR *)malloc(SENSIRON_DATA_LENGTH + 1); // last byte is always null + hr = S_OK; + break; + case '3': + m_SensorType = FLEX; + szReading = (CHAR *)malloc(FLEX_DATA_LENGTH + 1); // last byte is always null + hr = S_OK; + break; + case '4': + m_SensorType = PING; + szReading = (CHAR *)malloc(PING_DATA_LENGTH + 1); // last byte is always null + hr = S_OK; + break; + case '5': + m_SensorType = PIR; + szReading = (CHAR *)malloc(PIR_DATA_LENGTH + 1); // last byte is always null + hr = S_OK; + break; + case '6': + m_SensorType = MEMSIC; + szReading = (CHAR *)malloc(MEMSIC_AMOUNT_TO_READ + 1); // last byte is always null + hr = S_OK; + break; + case '7': + m_SensorType = QTI; + szReading = (CHAR *)malloc(QTI_DATA_LENGTH + 1); // last byte is always null + hr = S_OK; + break; + case '8': + m_SensorType = PIEZO; + szReading = (CHAR *)malloc(PIEZO_DATA_LENGTH + 1); // last byte is always null + hr = S_OK; + break; + case '9': + m_SensorType = HITACHI; + szReading = (CHAR *)malloc(HITACHI_DATA_LENGTH + 1); // last byte is always null + hr = S_OK; + break; + default: + break; + } + } + + if ((hr == S_OK) && (szReading == NULL)) + { + hr = E_OUTOFMEMORY; + } + + // Ensure we have sufficient input buffer size, and, if we do + // process the data for the given sensor. + if (hr == S_OK && (cbData >= (INTERVAL_DATA_LENGTH + MIN_DATA_LENGTH))) + { + for (int i=0; i pEventParams; + + TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_FLAG_DRIVER, "%!FUNC! Reading: %I64d, Interval: %d", llSensorData, dwUpdateInterval); + + // Create the event parameters collection if it doesn't exist + if (m_pEventParams == NULL) + { + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**)&m_pEventParams); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues"); + } + + if (hr == S_OK) + { + // Initialize the event parameters + m_pEventParams->Clear(); + + // Populate the event parameters + hr = m_pEventParams->SetGuidValue(WPD_EVENT_PARAMETER_EVENT_ID, EVENT_SENSOR_READING_UPDATED); + CHECK_HR(hr, "Failed to set the WPD_EVENT_PARAMETER_EVENT_ID"); + } + + if (hr == S_OK) + { + //hr = m_pEventParams->SetUnsignedIntegerValue(SENSOR_READING, dwTemperatureData); + hr = m_pEventParams->SetUnsignedLargeIntegerValue(SENSOR_READING, llSensorData); + CHECK_HR(hr, "Failed to set the sensor reading"); + } + + if (hr == S_OK) + { + hr = m_pEventParams->SetUnsignedIntegerValue(SENSOR_UPDATE_INTERVAL, dwUpdateInterval); + CHECK_HR(hr, "Failed to set the sensor update interval"); + } + + if (hr == S_OK) + { + // Create a buffer with the serialized parameters + hr = m_pWpdSerializer->GetBufferFromIPortableDeviceValues(m_pEventParams, &pBuffer, &cbBuffer); + CHECK_HR(hr, "Failed to get buffer from IPortableDeviceValues"); + } + + // Send the event + if (hr == S_OK && pBuffer != NULL) + { + hr = m_pWDFDevice->PostEvent(WPD_EVENT_NOTIFICATION, WdfEventBroadcast, pBuffer, cbBuffer); + CHECK_HR(hr, "Failed to post the WPD broadcast event"); + } + + // Free the memory + CoTaskMemFree(pBuffer); + pBuffer = NULL; + + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_COMMON_GET_OBJECT_IDS_FROM_PERSISTENT_UNIQUE_IDS + * command. + * + * The parameters sent to us are: + * - WPD_PROPERTY_COMMON_PERSISTENT_UNIQUE_IDS: Contains an IPortableDevicePropVariantCollection of VT_LPWSTR, + * indicating the PersistentUniqueIDs. + * + * The driver should: + * - Iterate through the PersistentUniqueIDs, and convert to a currently valid object id. + * This object ID list should be returned as an IPortableDevicePropVariantCollection of VT_LPWSTR + * in WPD_PROPERTY_COMMON_OBJECT_IDS. + * Order is implicit, i.e. the first element in the Persistent Unique ID list corresponds to the + * to the first element of the ObjectID list and so on. + * + * For those elements where an existing ObjectID could not be found (e.g. the + * object is no longer present on the device), the element will contain the + * empty string (L""). + */ +HRESULT WpdBaseDriver::OnGetObjectIDsFromPersistentUniqueIDs( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + + HRESULT hr = S_OK; + DWORD dwCount = 0; + CComPtr pPersistentIDs; + CComPtr pObjectIDs; + + if((pParams == NULL) || + (pResults == NULL)) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + // Get the list of Persistent IDs + if (hr == S_OK) + { + hr = pParams->GetIPortableDevicePropVariantCollectionValue(WPD_PROPERTY_COMMON_PERSISTENT_UNIQUE_IDS, &pPersistentIDs); + CHECK_HR(hr, "Failed to get WPD_PROPERTY_COMMON_PERSISTENT_UNIQUE_IDS"); + } + + // Create the collection to hold the ObjectIDs + if (hr == S_OK) + { + hr = CoCreateInstance(CLSID_PortableDevicePropVariantCollection, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDevicePropVariantCollection, + (VOID**) &pObjectIDs); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDevicePropVariantCollection"); + } + + // Iterate through the persistent ID list and add the equivalent object ID for each element. + if (hr == S_OK) + { + hr = pPersistentIDs->GetCount(&dwCount); + CHECK_HR(hr, "Failed to get count from persistent ID collection"); + + if (hr == S_OK) + { + DWORD dwIndex = 0; + PROPVARIANT pvPersistentID = {0}; + PROPVARIANT pvObjectID = {0}; + + PropVariantInit(&pvPersistentID); + PropVariantInit(&pvObjectID); + + for(dwIndex = 0; dwIndex < dwCount; dwIndex++) + { + pvObjectID.vt = VT_LPWSTR; + hr = pPersistentIDs->GetAt(dwIndex, &pvPersistentID); + CHECK_HR(hr, "Failed to get persistent ID at index %d", dwIndex); + + // Since our persistent unique identifier are identical to our object + // identifiers, we just return it back to the caller. + if (hr == S_OK) + { + pvObjectID.pwszVal = AtlAllocTaskWideString(pvPersistentID.pwszVal); + } + + if (hr == S_OK) + { + hr = pObjectIDs->Add(&pvObjectID); + CHECK_HR(hr, "Failed to add next Object ID"); + } + + PropVariantClear(&pvPersistentID); + PropVariantClear(&pvObjectID); + + if(FAILED(hr)) + { + break; + } + } + } + } + + if (hr == S_OK) + { + hr = pResults->SetIPortableDevicePropVariantCollectionValue(WPD_PROPERTY_COMMON_OBJECT_IDS, pObjectIDs); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_COMMON_OBJECT_IDS"); + } + + return hr; +} diff --git a/wpd/WpdBasicHardwareDriver/WpdBaseDriver.h b/wpd/WpdBasicHardwareDriver/WpdBaseDriver.h new file mode 100644 index 000000000..13dd46941 --- /dev/null +++ b/wpd/WpdBasicHardwareDriver/WpdBaseDriver.h @@ -0,0 +1,69 @@ +#pragma once + +typedef struct tagSENSOR_INFO{ + unsigned char deviceID; + unsigned short dataLength; +} SENSOR_INFO; + +class WpdBaseDriver : + public IUnknown +{ +public: + WpdBaseDriver(); + virtual ~WpdBaseDriver(); + + HRESULT Initialize(_In_ IWDFDevice *pDevice); + void Uninitialize(); + + HRESULT DispatchWpdMessage(_In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT ProcessReadData(_In_reads_(cbData) BYTE* pData, size_t cbData); + + RS232Target* GetRS232Target(); + +public: // IUnknown + ULONG __stdcall AddRef(); + + _At_(this, __drv_freesMem(Mem)) + ULONG __stdcall Release(); + + HRESULT __stdcall QueryInterface(REFIID riid, void** ppv); + +private: + HRESULT OnGetObjectIDsFromPersistentUniqueIDs(_In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT PostSensorReadingEvent(LONGLONG llSensorData, DWORD dwUpdateInterval); + +public: + + enum SensorType{ + UNKNOWN, // Unknown + COMPASS, // Compass + SENSIRON, // Temp/humidity sensor + FLEX, // Flexiforce sensor + PING, // Ultrasonic ping + PIR, // Passive infrared + MEMSIC, // 2-axis accelerometer + QTI, // Light sensor + PIEZO, // Vibration Sensor + HITACHI, // 3-axis accelerometer + }; + + SensorType m_SensorType; // enum value specifying sensor type + +private: + WpdObjectEnumerator m_ObjectEnum; + WpdObjectProperties m_ObjectProperties; + WpdCapabilities m_Capabilities; + + ULONG m_cRef; + RS232Connection m_Connection; + RS232Target m_Target; + + CComPtr m_pWDFDevice; + CComPtr m_pWpdSerializer; + CComPtr m_pEventParams; +}; + diff --git a/wpd/WpdBasicHardwareDriver/WpdBasicHardwareDriver.cpp b/wpd/WpdBasicHardwareDriver/WpdBasicHardwareDriver.cpp new file mode 100644 index 000000000..2620e9094 --- /dev/null +++ b/wpd/WpdBasicHardwareDriver/WpdBasicHardwareDriver.cpp @@ -0,0 +1,60 @@ +#include "stdafx.h" + +#include "WpdBasicHardwareDriver.tmh" + +HINSTANCE g_hInstance = NULL; + +class CWpdBasicHardwareDriverModule : public CAtlDllModuleT< CWpdBasicHardwareDriverModule > +{ +public : + DECLARE_REGISTRY_APPID_RESOURCEID(IDR_WpdBasicHardwareDriver, "{021AD204-6411-4698-8CFB-C1A72B581733}") + DECLARE_LIBID(LIBID_WpdBasicHardwareDriverLib) +}; + +CWpdBasicHardwareDriverModule _AtlModule; + +// DLL Entry Point +extern "C" BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) +{ + if(dwReason == DLL_PROCESS_ATTACH) + { + g_hInstance = hInstance; + + // Initialize tracing. + WPP_INIT_TRACING(MYDRIVER_TRACING_ID); + } + else if (dwReason == DLL_PROCESS_DETACH) + { + // Cleanup tracing. + WPP_CLEANUP(); + } + + return _AtlModule.DllMain(dwReason, lpReserved); +} + +// Used to determine whether the DLL can be unloaded by OLE +STDAPI DllCanUnloadNow(void) +{ + 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) +{ + 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(); + return hr; +} + +// DllUnregisterServer - Removes entries from the system registry +STDAPI DllUnregisterServer(void) +{ + HRESULT hr = _AtlModule.DllUnregisterServer(); + return hr; +} diff --git a/wpd/WpdBasicHardwareDriver/WpdBasicHardwareDriver.def b/wpd/WpdBasicHardwareDriver/WpdBasicHardwareDriver.def new file mode 100644 index 000000000..4af498d9b --- /dev/null +++ b/wpd/WpdBasicHardwareDriver/WpdBasicHardwareDriver.def @@ -0,0 +1,9 @@ +; WpdBasicHardwareDriver.def : Declares the module parameters. + +LIBRARY "WpdBasicHardwareDriver.DLL" + +EXPORTS + DllCanUnloadNow PRIVATE + DllGetClassObject PRIVATE + DllRegisterServer PRIVATE + DllUnregisterServer PRIVATE diff --git a/wpd/WpdBasicHardwareDriver/WpdBasicHardwareDriver.idl b/wpd/WpdBasicHardwareDriver/WpdBasicHardwareDriver.idl new file mode 100644 index 000000000..73ff16aff --- /dev/null +++ b/wpd/WpdBasicHardwareDriver/WpdBasicHardwareDriver.idl @@ -0,0 +1,24 @@ + +import "oaidl.idl"; +import "ocidl.idl"; + +import "wudfddi.idl"; + +[ + uuid(69A9B934-73F0-45AF-B3C5-1D5BC7BC982B), + version(1.0), + helpstring("Windows Portable Device Basic Hardware Driver Type Library") +] +library WpdBasicHardwareDriverLib +{ + importlib("stdole2.tlb"); + [ + uuid(EC7445EE-BC00-4CED-AFE7-A52849F10239), + helpstring("WpdBasicHardwareDriver Class") + ] + coclass WpdBasicHardwareDriver + { + [default] interface IDriverEntry; + }; +}; + diff --git a/wpd/WpdBasicHardwareDriver/WpdBasicHardwareDriver.inx b/wpd/WpdBasicHardwareDriver/WpdBasicHardwareDriver.inx new file mode 100644 index 000000000..493a1964f --- /dev/null +++ b/wpd/WpdBasicHardwareDriver/WpdBasicHardwareDriver.inx @@ -0,0 +1,81 @@ +; +; WpdBasicHardwareDriver.inf +; + +[Version] +Signature="$Windows NT$" +Class=WPD +ClassGuid={EEC5AD98-8080-425f-922A-DABF3DE3F69A} +Provider=%Provider% +CatalogFile=WpdBasicHardwareDriver.cat +DriverVer=01/24/2007,1.1.1.1 + +[Manufacturer] +%Mfg%=Standard,NT$ARCH$ + +[Standard.NT$ARCH$] +%BasicDeviceName%=Basic_Install,WUDF\WpdBasicHardware + +[SourceDisksFiles] +WudfUpdate_$UMDFCOINSTALLERVERSION$.dll=1 +WpdBasicHardwareDriver.dll=1 + +[SourceDisksNames] +1 = %MediaDescription% + +; =================== WPD Sample Device ================================== + +[Basic_Install] +CopyFiles=System32Copy + +[Basic_Install.hw] +AddReg=Device_AddReg + +[Basic_Install.Services] +AddService=WUDFRd,0x000001fa,WUDFRD_ServiceInstall + +[Basic_Install.CoInstallers] +AddReg=Basic_Install.CoInstallers_AddReg +CopyFiles=CoInstallers_CopyFiles + +[CoInstallers_CopyFiles] +WudfUpdate_$UMDFCOINSTALLERVERSION$.dll + +[Basic_Install.CoInstallers_AddReg] +HKR,,CoInstallers32,0x00010000,"WudfUpdate_$UMDFCOINSTALLERVERSION$.dll" + +[Basic_Install.Wdf] +UmdfService=WpdBasicHardwareDriver, WpdBasicHardwareDriver_Install +UmdfServiceOrder=WpdBasicHardwareDriver +; Enable file-handle-based I/O targets +UmdfDispatcher = FileHandle + +[WpdBasicHardwareDriver_Install] +UmdfLibraryVersion=$UMDFVERSION$ +DriverCLSID="{EC7445EE-BC00-4CED-AFE7-A52849F10239}" +ServiceBinary=%12%\UMDF\WpdBasicHardwareDriver.dll + +[Device_AddReg] +; HKR,,"EnableLegacySupport",0x10001,0 + +[WUDFRD_ServiceInstall] +ServiceType=1 +StartType=3 +ErrorControl=1 +ServiceBinary=%12%\WUDFRd.sys + +[DestinationDirs] +System32Copy=12,UMDF ; copy to system32\drivers\umdf +CoInstallers_CopyFiles=11 + +[System32Copy] +WpdBasicHardwareDriver.dll + + +; =================== Generic ================================== + +[Strings] +Mfg="Windows Portable Devices" +Provider="TODO-Set-Provider" +MediaDescription="WPD Basic Hardware Driver Installation Media" +BasicDeviceName="WPD Basic Hardware Driver" diff --git a/wpd/WpdBasicHardwareDriver/WpdBasicHardwareDriver.rc b/wpd/WpdBasicHardwareDriver/WpdBasicHardwareDriver.rc new file mode 100644 index 000000000..f743120a7 --- /dev/null +++ b/wpd/WpdBasicHardwareDriver/WpdBasicHardwareDriver.rc @@ -0,0 +1,15 @@ +#include "resource.h" +#include +#include + +#define VER_FILETYPE VFT_DLL +#define VER_FILESUBTYPE VFT2_UNKNOWN +#define VER_FILEDESCRIPTION_STR "Windows Portable Device Basic Hardware Driver" +#define VER_INTERNALNAME_STR "WpdBasicHardwareDriver.dll" + +#include + +1 TYPELIB "WpdBasicHardwareDriver.tlb" + +IDR_WpdTempSensorDriver REGISTRY "WpdBasicHardwareDriver.rgs" + diff --git a/wpd/WpdBasicHardwareDriver/WpdBasicHardwareDriver.rgs b/wpd/WpdBasicHardwareDriver/WpdBasicHardwareDriver.rgs new file mode 100644 index 000000000..6b4ae538a --- /dev/null +++ b/wpd/WpdBasicHardwareDriver/WpdBasicHardwareDriver.rgs @@ -0,0 +1,26 @@ +HKCR +{ + WpdBasicHardwareDriver.WpdBasicHardwareDriver.1 = s 'WpdBasicHardwareDriver Class' + { + CLSID = s '{FB2CC0AD-6723-45D2-9EC6-7B603F9BCD17}' + } + WpdBasicHardwareDriver.WpdBasicHardwareDriver = s 'WpdBasicHardwareDriver Class' + { + CLSID = s '{FB2CC0AD-6723-45D2-9EC6-7B603F9BCD17}' + CurVer = s 'WpdBasicHardwareDriver.WpdBasicHardwareDriver.1' + } + NoRemove CLSID + { + ForceRemove {FB2CC0AD-6723-45D2-9EC6-7B603F9BCD17} = s 'WpdBasicHardwareDriver Class' + { + ProgID = s 'WpdBasicHardwareDriver.WpdBasicHardwareDriver.1' + VersionIndependentProgID = s 'WpdBasicHardwareDriver.WpdBasicHardwareDriver.1' + InprocServer32 = s '%MODULE%' + { + val ThreadingModel = s 'Free' + } + 'TypeLib' = s '{20E7797C-2981-49EE-8A78-85A11440785C}' + } + } +} + diff --git a/wpd/WpdBasicHardwareDriver/WpdBasicHardwareDriver.sln b/wpd/WpdBasicHardwareDriver/WpdBasicHardwareDriver.sln new file mode 100644 index 000000000..33457e8fb --- /dev/null +++ b/wpd/WpdBasicHardwareDriver/WpdBasicHardwareDriver.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}") = "WpdBasicHardwareDriver", "WpdBasicHardwareDriver.vcxproj", "{6FEE397D-A6E1-4CDE-B820-9FE310350545}" +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 + {6FEE397D-A6E1-4CDE-B820-9FE310350545}.Debug|Win32.ActiveCfg = Debug|Win32 + {6FEE397D-A6E1-4CDE-B820-9FE310350545}.Debug|Win32.Build.0 = Debug|Win32 + {6FEE397D-A6E1-4CDE-B820-9FE310350545}.Release|Win32.ActiveCfg = Release|Win32 + {6FEE397D-A6E1-4CDE-B820-9FE310350545}.Release|Win32.Build.0 = Release|Win32 + {6FEE397D-A6E1-4CDE-B820-9FE310350545}.Debug|x64.ActiveCfg = Debug|x64 + {6FEE397D-A6E1-4CDE-B820-9FE310350545}.Debug|x64.Build.0 = Debug|x64 + {6FEE397D-A6E1-4CDE-B820-9FE310350545}.Release|x64.ActiveCfg = Release|x64 + {6FEE397D-A6E1-4CDE-B820-9FE310350545}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/wpd/WpdBasicHardwareDriver/WpdBasicHardwareDriver.vcxproj b/wpd/WpdBasicHardwareDriver/WpdBasicHardwareDriver.vcxproj new file mode 100644 index 000000000..de053bd55 --- /dev/null +++ b/wpd/WpdBasicHardwareDriver/WpdBasicHardwareDriver.vcxproj @@ -0,0 +1,340 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {6FEE397D-A6E1-4CDE-B820-9FE310350545} + $(MSBuildProjectName) + 1 + 1 + Debug + Win32 + {50BBD547-66A9-4101-BE38-8ABEE625CC4C} + + + + 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 + stdafx.h + ;%(AdditionalIncludeDirectories) + Stdafx.h + Use + $(IntDir)\Stdafx.h.pch + + + true + true + stdafx.h + ;%(AdditionalIncludeDirectories) + Stdafx.h + Use + $(IntDir)\Stdafx.h.pch + + + true + true + stdafx.h + ;%(AdditionalIncludeDirectories) + Stdafx.h + Use + $(IntDir)\Stdafx.h.pch + + + true + true + stdafx.h + ;%(AdditionalIncludeDirectories) + Stdafx.h + Use + $(IntDir)\Stdafx.h.pch + + + true + true + stdafx.h + ;%(AdditionalIncludeDirectories) + Stdafx.h + Use + $(IntDir)\Stdafx.h.pch + + + true + true + stdafx.h + ;%(AdditionalIncludeDirectories) + Stdafx.h + Use + $(IntDir)\Stdafx.h.pch + + + true + true + stdafx.h + ;%(AdditionalIncludeDirectories) + Stdafx.h + Use + $(IntDir)\Stdafx.h.pch + + + true + true + stdafx.h + ;%(AdditionalIncludeDirectories) + Stdafx.h + Use + $(IntDir)\Stdafx.h.pch + + + true + true + stdafx.h + ;%(AdditionalIncludeDirectories) + Stdafx.h + Use + $(IntDir)\Stdafx.h.pch + + + true + true + stdafx.h + ;%(AdditionalIncludeDirectories) + Stdafx.h + Use + $(IntDir)\Stdafx.h.pch + + + $(InfArch) + true + .\$(IntDir)\WpdBasicHardwareDriver.inf + + + true + true + stdafx.h + + + + WpdBasicHardwareDriver + + + WpdBasicHardwareDriver + + + WpdBasicHardwareDriver + + + WpdBasicHardwareDriver + + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + + _DllMainCRTStartup@12 + _DllMainCRTStartup + + + + Dynamic + + + Dynamic + + + Dynamic + + + Dynamic + + + + Sync + true + Level4 + %(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(DDK_INC_PATH) + + + %(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(DDK_INC_PATH) + + + %(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(DDK_INC_PATH) + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\strsafe.lib;$(SDK_LIB_PATH)\kernel32.lib;$(SDK_LIB_PATH)\ole32.lib;$(SDK_LIB_PATH)\oleaut32.lib;$(SDK_LIB_PATH)\uuid.lib;$(SDK_LIB_PATH)\user32.lib;$(SDK_LIB_PATH)\advapi32.lib;$(SDK_LIB_PATH)\shlwapi.lib;$(SDK_LIB_PATH)\PortableDeviceGuids.lib + + + + + Sync + true + Level4 + %(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(DDK_INC_PATH) + + + %(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(DDK_INC_PATH) + + + %(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(DDK_INC_PATH) + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\strsafe.lib;$(SDK_LIB_PATH)\kernel32.lib;$(SDK_LIB_PATH)\ole32.lib;$(SDK_LIB_PATH)\oleaut32.lib;$(SDK_LIB_PATH)\uuid.lib;$(SDK_LIB_PATH)\user32.lib;$(SDK_LIB_PATH)\advapi32.lib;$(SDK_LIB_PATH)\shlwapi.lib;$(SDK_LIB_PATH)\PortableDeviceGuids.lib + + + + + Sync + true + Level4 + %(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(DDK_INC_PATH) + + + %(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(DDK_INC_PATH) + + + %(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(DDK_INC_PATH) + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\strsafe.lib;$(SDK_LIB_PATH)\kernel32.lib;$(SDK_LIB_PATH)\ole32.lib;$(SDK_LIB_PATH)\oleaut32.lib;$(SDK_LIB_PATH)\uuid.lib;$(SDK_LIB_PATH)\user32.lib;$(SDK_LIB_PATH)\advapi32.lib;$(SDK_LIB_PATH)\shlwapi.lib;$(SDK_LIB_PATH)\PortableDeviceGuids.lib + + + + + Sync + true + Level4 + %(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(DDK_INC_PATH) + + + %(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(DDK_INC_PATH) + + + %(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(DDK_INC_PATH) + + + %(AdditionalDependencies);$(SDK_LIB_PATH)\strsafe.lib;$(SDK_LIB_PATH)\kernel32.lib;$(SDK_LIB_PATH)\ole32.lib;$(SDK_LIB_PATH)\oleaut32.lib;$(SDK_LIB_PATH)\uuid.lib;$(SDK_LIB_PATH)\user32.lib;$(SDK_LIB_PATH)\advapi32.lib;$(SDK_LIB_PATH)\shlwapi.lib;$(SDK_LIB_PATH)\PortableDeviceGuids.lib + + + + + WpdBasicHardwareDriver.def + + + + + WpdBasicHardwareDriver.def + + + + + WpdBasicHardwareDriver.def + + + + + WpdBasicHardwareDriver.def + + + + + ;%(AdditionalIncludeDirectories) + Stdafx.h + Create + $(IntDir)\Stdafx.h.pch + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/wpd/WpdBasicHardwareDriver/WpdBasicHardwareDriver.vcxproj.Filters b/wpd/WpdBasicHardwareDriver/WpdBasicHardwareDriver.vcxproj.Filters new file mode 100644 index 000000000..9d7efe834 --- /dev/null +++ b/wpd/WpdBasicHardwareDriver/WpdBasicHardwareDriver.vcxproj.Filters @@ -0,0 +1,72 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {B48EFD92-02C9-4F9D-8EDB-254B648FC6E3} + + + h;hpp;hxx;hm;inl;inc;xsd + {FFE2E9BE-A066-48D3-BD6B-0B45313789BE} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {171FE0CF-5996-447D-90DD-5C8A817C2CBA} + + + inf;inv;inx;mof;mc; + {8933E80A-2DCE-479E-B892-FFE35F130EC6} + + + + + 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 + + + + + Driver Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/wpd/WpdBasicHardwareDriver/WpdCapabilities.cpp b/wpd/WpdBasicHardwareDriver/WpdCapabilities.cpp new file mode 100644 index 000000000..e14c53c2b --- /dev/null +++ b/wpd/WpdBasicHardwareDriver/WpdCapabilities.cpp @@ -0,0 +1,505 @@ +#include "stdafx.h" + +#include "WpdCapabilities.tmh" + +const PROPERTYKEY g_SupportedCommands[] = +{ + // WPD_CATEGORY_OBJECT_ENUMERATION + WPD_COMMAND_OBJECT_ENUMERATION_START_FIND, + WPD_COMMAND_OBJECT_ENUMERATION_FIND_NEXT, + WPD_COMMAND_OBJECT_ENUMERATION_END_FIND, + + // WPD_CATEGORY_OBJECT_PROPERTIES + WPD_COMMAND_OBJECT_PROPERTIES_GET_SUPPORTED, + WPD_COMMAND_OBJECT_PROPERTIES_GET, + WPD_COMMAND_OBJECT_PROPERTIES_GET_ALL, + WPD_COMMAND_OBJECT_PROPERTIES_SET, + WPD_COMMAND_OBJECT_PROPERTIES_GET_ATTRIBUTES, + WPD_COMMAND_OBJECT_PROPERTIES_DELETE, + + // WPD_CATEGORY_CAPABILITIES + WPD_COMMAND_CAPABILITIES_GET_SUPPORTED_COMMANDS, + WPD_COMMAND_CAPABILITIES_GET_COMMAND_OPTIONS, + WPD_COMMAND_CAPABILITIES_GET_SUPPORTED_FUNCTIONAL_CATEGORIES, + WPD_COMMAND_CAPABILITIES_GET_FUNCTIONAL_OBJECTS, + WPD_COMMAND_CAPABILITIES_GET_SUPPORTED_EVENTS, + WPD_COMMAND_CAPABILITIES_GET_EVENT_OPTIONS, +}; + +const GUID g_SupportedFunctionalCategories[] = +{ + WPD_FUNCTIONAL_CATEGORY_DEVICE, + FUNCTIONAL_CATEGORY_SENSOR_SAMPLE, // Our device's functional category +}; + +const GUID g_SupportedEvents[] = +{ + EVENT_SENSOR_READING_UPDATED, +}; + +WpdCapabilities::WpdCapabilities() +{ + +} + +WpdCapabilities::~WpdCapabilities() +{ + +} + +HRESULT WpdCapabilities::DispatchWpdMessage(_In_ REFPROPERTYKEY Command, + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + + if (Command.fmtid != WPD_CATEGORY_CAPABILITIES) + { + hr = E_INVALIDARG; + CHECK_HR(hr, "This object does not support this command category %ws",CComBSTR(Command.fmtid)); + } + + if (hr == S_OK) + { + if (IsEqualPropertyKey(Command, WPD_COMMAND_CAPABILITIES_GET_SUPPORTED_COMMANDS)) + { + hr = OnGetSupportedCommands(pParams, pResults); + CHECK_HR(hr, "Failed to get supported commands"); + } + else if (IsEqualPropertyKey(Command, WPD_COMMAND_CAPABILITIES_GET_COMMAND_OPTIONS)) + { + hr = OnGetCommandOptions(pParams, pResults); + CHECK_HR(hr, "Failed to get command options"); + } + else if (IsEqualPropertyKey(Command, WPD_COMMAND_CAPABILITIES_GET_SUPPORTED_FUNCTIONAL_CATEGORIES)) + { + hr = OnGetFunctionalCategories(pParams, pResults); + CHECK_HR(hr, "Failed to get functional categories"); + } + else if (IsEqualPropertyKey(Command, WPD_COMMAND_CAPABILITIES_GET_FUNCTIONAL_OBJECTS)) + { + hr = OnGetFunctionalObjects(pParams, pResults); + CHECK_HR(hr, "Failed to get functional objects"); + } + else if (IsEqualPropertyKey(Command, WPD_COMMAND_CAPABILITIES_GET_SUPPORTED_EVENTS)) + { + hr = OnGetSupportedEvents(pParams, pResults); + CHECK_HR(hr, "Failed to get supported events"); + } + else if (IsEqualPropertyKey(Command, WPD_COMMAND_CAPABILITIES_GET_EVENT_OPTIONS)) + { + hr = OnGetEventOptions(pParams, pResults); + CHECK_HR(hr, "Failed to get event options"); + } + else + { + hr = E_NOTIMPL; + CHECK_HR(hr, "This object does not support this command id %d", Command.pid); + } + } + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_CAPABILITIES_GET_SUPPORTED_COMMANDS + * command. + * + * The parameters sent to us are: + * - none. + * + * The driver should: + * - Return all commands supported by this driver as an + * IPortableDeviceKeyCollection in WPD_PROPERTY_CAPABILITIES_SUPPORTED_COMMANDS. + * This includes custom commands, if any. + * + * Note that certain commands require a "command target" to function correctly. + * (e.g. delete object command) It is understood that not all objects are necessarily + * valid targets (e.g. you cannot delete the device object). + */ +HRESULT WpdCapabilities::OnGetSupportedCommands( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + CComPtr pCommands; + UNREFERENCED_PARAMETER(pParams); + + // CoCreate a collection to store the supported commands. + if (hr == S_OK) + { + hr = CoCreateInstance(CLSID_PortableDeviceKeyCollection, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceKeyCollection, + (VOID**) &pCommands); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceKeyCollection"); + } + + // Add the supported commands to the collection. + if (hr == S_OK) + { + for (DWORD dwIndex = 0; dwIndex < ARRAYSIZE(g_SupportedCommands); dwIndex++) + { + hr = pCommands->Add(g_SupportedCommands[dwIndex]); + CHECK_HR(hr, "Failed to add supported command at index %d", dwIndex); + if (FAILED(hr)) + { + break; + } + } + } + + // Set the WPD_PROPERTY_CAPABILITIES_SUPPORTED_COMMANDS value in the results. + if (hr == S_OK) + { + hr = pResults->SetIUnknownValue(WPD_PROPERTY_CAPABILITIES_SUPPORTED_COMMANDS, pCommands); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_CAPABILITIES_SUPPORTED_COMMANDS"); + } + + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_CAPABILITIES_GET_COMMAND_OPTIONS + * command. + * + * The parameters sent to us are: + * - WPD_PROPERTY_CAPABILITIES_COMMAND: a collection of property keys containing a single value, + * which identifies the specific command options are requested to return. + * + * The driver should: + * - Return an IPortableDeviceValues in WPD_PROPERTY_CAPABILITIES_COMMAND_OPTIONS, containing + * the relevant options. If no options are available for this command, the driver should + * return an IPortableDeviceValues with no elements in it. + */ +HRESULT WpdCapabilities::OnGetCommandOptions( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + PROPERTYKEY Command = WPD_PROPERTY_NULL; + CComPtr pOptions; + + // First get ALL parameters for this command. If we cannot get ALL parameters + // then E_INVALIDARG should be returned and no further processing should occur. + + // Get the command whose options have been requested + if (hr == S_OK) + { + hr = pParams->GetKeyValue(WPD_PROPERTY_CAPABILITIES_COMMAND, &Command); + CHECK_HR(hr, "Missing value for WPD_PROPERTY_CAPABILITIES_COMMAND"); + } + + // CoCreate a collection to store the command options. + if (hr == S_OK) + { + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**) &pOptions); + CHECK_HR(hr, "Failed to CoCreateInstance CLSID_PortableDeviceValues"); + } + + // Add command options to the collection + if (hr == S_OK) + { + // If your driver supports command options, then they should be added here + // to the command options collection 'pOptions'. + } + + // Set the WPD_PROPERTY_CAPABILITIES_COMMAND_OPTIONS value in the results. + if (hr == S_OK) + { + hr = pResults->SetIUnknownValue(WPD_PROPERTY_CAPABILITIES_COMMAND_OPTIONS, pOptions); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_CAPABILITIES_COMMAND_OPTIONS"); + } + + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_CAPABILITIES_GET_SUPPORTED_FUNCTIONAL_CATEGORIES + * command. + * + * The parameters sent to us are: + * - none. + * + * The driver should: + * - Return an IPortableDevicePropVariantCollection (of type VT_CLSID) in + * WPD_PROPERTY_CAPABILITIES_FUNCTIONAL_CATEGORIES, containing + * the supported functional categories for this device. + */ +HRESULT WpdCapabilities::OnGetFunctionalCategories( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + CComPtr pFunctionalCategories; + + UNREFERENCED_PARAMETER(pParams); + + // CoCreate a collection to store the supported functional categories. + if (hr == S_OK) + { + hr = CoCreateInstance(CLSID_PortableDevicePropVariantCollection, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDevicePropVariantCollection, + (VOID**) &pFunctionalCategories); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDevicePropVariantCollection"); + } + + // Add the supported functional categories to the collection. + if (hr == S_OK) + { + for (DWORD dwIndex = 0; dwIndex < ARRAYSIZE(g_SupportedFunctionalCategories); dwIndex++) + { + PROPVARIANT pv = {0}; + PropVariantInit(&pv); + // Don't call PropVariantClear, since we did not allocate the memory for these GUIDs + + pv.vt = VT_CLSID; + pv.puuid = (GUID*) &g_SupportedFunctionalCategories[dwIndex]; + + hr = pFunctionalCategories->Add(&pv); + CHECK_HR(hr, "Failed to add supported functional category at index %d", dwIndex); + if (FAILED(hr)) + { + break; + } + } + } + + // Set the WPD_PROPERTY_CAPABILITIES_FUNCTIONAL_CATEGORIES value in the results. + if (hr == S_OK) + { + hr = pResults->SetIUnknownValue(WPD_PROPERTY_CAPABILITIES_FUNCTIONAL_CATEGORIES, pFunctionalCategories); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_CAPABILITIES_FUNCTIONAL_CATEGORIES"); + } + + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_CAPABILITIES_GET_FUNCTIONAL_OBJECTS + * command. It is sent when the caller is interesting in finding the object IDs for all + * functional objects belonging to the specified functional category. + * Note: the number of functional objects is expected to be very small (less than 8 for the + * whole device). + * + * The parameters sent to us are: + * - WPD_PROPERTY_CAPABILITIES_FUNCTIONAL_CATEGORY - a GUID value containing the category + * the caller is looking for. If the value is WPD_FUNCTIONAL_CATEGORY_ALL, then the driver + * must return all functional objects, no matter which category they belong to. + * + * The driver should: + * - Return an IPortableDevicePropVariantCollection (of type VT_LPWSTR) in + * WPD_PROPERTY_CAPABILITIES_FUNCTIONAL_OBJECTS, containing + * the ids of the functional objects who belong to the specified functional category. + * If there are no objects in the specified category, the driver should return an + * empty collection. + */ +HRESULT WpdCapabilities::OnGetFunctionalObjects( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + GUID guidFunctionalCategory = GUID_NULL; + CComPtr pFunctionalObjects; + + // First get ALL parameters for this command. If we cannot get ALL parameters + // then E_INVALIDARG should be returned and no further processing should occur. + + // Get the functional category whose functional object identifiers have been requested + if (hr == S_OK) + { + hr = pParams->GetGuidValue(WPD_PROPERTY_CAPABILITIES_FUNCTIONAL_CATEGORY, &guidFunctionalCategory); + CHECK_HR(hr, "Missing value for WPD_PROPERTY_CAPABILITIES_FUNCTIONAL_CATEGORY"); + } + + // CoCreate a collection to store the supported functional object identifiers. + if (hr == S_OK) + { + hr = CoCreateInstance(CLSID_PortableDevicePropVariantCollection, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDevicePropVariantCollection, + (VOID**) &pFunctionalObjects); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDevicePropVariantCollection"); + } + + // Add the supported functional object identifiers for the specified functional + // category to the collection. + if (hr == S_OK) + { + PROPVARIANT pv = {0}; + PropVariantInit(&pv); + // Don't call PropVariantClear, since we did not allocate the memory for these object identifiers + + // Add WPD_DEVICE_OBJECT_ID to the functional object identifiers collection + if (hr == S_OK) + { + + if ((guidFunctionalCategory == WPD_FUNCTIONAL_CATEGORY_DEVICE) || + (guidFunctionalCategory == WPD_FUNCTIONAL_CATEGORY_ALL)) + { + pv.vt = VT_LPWSTR; + pv.pwszVal = WPD_DEVICE_OBJECT_ID; + hr = pFunctionalObjects->Add(&pv); + CHECK_HR(hr, "Failed to add device object ID"); + } + } + + // Add FUNCTIONAL_CATEGORY_SENSOR_SAMPLE to the functional object + // identifiers collection + if (hr == S_OK) + { + if ((guidFunctionalCategory == FUNCTIONAL_CATEGORY_SENSOR_SAMPLE) || + (guidFunctionalCategory == WPD_FUNCTIONAL_CATEGORY_ALL)) + { + pv.vt = VT_LPWSTR; + pv.pwszVal = SENSOR_OBJECT_ID; + hr = pFunctionalObjects->Add(&pv); + CHECK_HR(hr, "Failed to add sensor object ID"); + } + } + } + + // Set the WPD_PROPERTY_CAPABILITIES_FUNCTIONAL_OBJECTS value in the results. + if (hr == S_OK) + { + hr = pResults->SetIUnknownValue(WPD_PROPERTY_CAPABILITIES_FUNCTIONAL_OBJECTS, pFunctionalObjects); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_CAPABILITIES_FUNCTIONAL_OBJECTS"); + } + + return hr; +} + + +/** + * This method is called when we receive a WPD_COMMAND_CAPABILITIES_GET_SUPPORTED_EVENTS + * command. + * + * The parameters sent to us are: + * - none. + * + * The driver should: + * - Return all events supported by this driver should be returned as an + * IPortableDeviceKeyCollection in WPD_PROPERTY_CAPABILITIES_SUPPORTED_EVENTS. + * That includes custom commands, if any. + */ +HRESULT WpdCapabilities::OnGetSupportedEvents( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + CComPtr pEvents; + UNREFERENCED_PARAMETER(pParams); + + // CoCreate a collection to store the supported events. + if (hr == S_OK) + { + hr = CoCreateInstance(CLSID_PortableDevicePropVariantCollection, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDevicePropVariantCollection, + (VOID**) &pEvents); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDevicePropVariantCollection"); + } + + // Add the supported events to the collection. + if (hr == S_OK) + { + // populate the supported events collection + for (DWORD dwIndex = 0; dwIndex < ARRAYSIZE(g_SupportedEvents); dwIndex++) + { + PROPVARIANT pv = {0}; + PropVariantInit(&pv); + // Don't call PropVariantClear, since we did not allocate the memory for these GUIDs + + pv.vt = VT_CLSID; + pv.puuid = (GUID*) &g_SupportedEvents[dwIndex]; + + hr = pEvents->Add(&pv); + CHECK_HR(hr, "Failed to add supported event at index %d", dwIndex); + if (FAILED(hr)) + { + break; + } + } + } + + // Set the WPD_PROPERTY_CAPABILITIES_SUPPORTED_EVENTS value in the results. + if (hr == S_OK) + { + hr = pResults->SetIUnknownValue(WPD_PROPERTY_CAPABILITIES_SUPPORTED_EVENTS, pEvents); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_CAPABILITIES_SUPPORTED_EVENTS"); + } + + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_CAPABILITIES_GET_EVENT_OPTIONS + * command. + * + * The parameters sent to us are: + * - WPD_PROPERTY_CAPABILITIES_EVENT: a GUID value indicating the Event whose options should be returned. + * + * The driver should: + * - Return an IPortableDeviceValues in WPD_PROPERTY_CAPABILITIES_EVENT_OPTIONS, containing + * the relevant options. + */ +HRESULT WpdCapabilities::OnGetEventOptions( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + GUID Event = GUID_NULL; + CComPtr pOptions; + + // First get ALL parameters for this command. If we cannot get ALL parameters + // then E_INVALIDARG should be returned and no further processing should occur. + + // Get the event whose options have been requested + if (hr == S_OK) + { + hr = pParams->GetGuidValue(WPD_PROPERTY_CAPABILITIES_EVENT, &Event); + CHECK_HR(hr, "Missing value for WPD_PROPERTY_CAPABILITIES_EVENT"); + } + + // CoCreate a collection to store the event options. + if (hr == S_OK) + { + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**) &pOptions); + CHECK_HR(hr, "Failed to CoCreateInstance CLSID_PortableDeviceValues"); + } + + // Add event options to the collection + if (hr == S_OK) + { + // Check for the events we support + if (Event == EVENT_SENSOR_READING_UPDATED) + { + // These events are broadcast events + hr = pOptions->SetBoolValue(WPD_EVENT_OPTION_IS_BROADCAST_EVENT, TRUE); + CHECK_HR(hr, "Failed to set WPD_EVENT_OPTION_IS_BROADCAST_EVENT"); + } + } + + // Set the WPD_PROPERTY_CAPABILITIES_EVENT_OPTIONS value in the results. + if (hr == S_OK) + { + hr = pResults->SetIUnknownValue(WPD_PROPERTY_CAPABILITIES_EVENT_OPTIONS, pOptions); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_CAPABILITIES_EVENT_OPTIONS"); + } + + return hr; +} + + diff --git a/wpd/WpdBasicHardwareDriver/WpdCapabilities.h b/wpd/WpdBasicHardwareDriver/WpdCapabilities.h new file mode 100644 index 000000000..b74358304 --- /dev/null +++ b/wpd/WpdBasicHardwareDriver/WpdCapabilities.h @@ -0,0 +1,41 @@ +#pragma once + +class WpdCapabilities +{ +public: + WpdCapabilities(); + virtual ~WpdCapabilities(); + + HRESULT Initialize(); + + HRESULT DispatchWpdMessage( + _In_ REFPROPERTYKEY Command, + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnGetSupportedCommands( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnGetCommandOptions( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnGetFunctionalCategories( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnGetFunctionalObjects( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnGetSupportedEvents( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnGetEventOptions( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + +}; + diff --git a/wpd/WpdBasicHardwareDriver/WpdObjectEnum.cpp b/wpd/WpdBasicHardwareDriver/WpdObjectEnum.cpp new file mode 100644 index 000000000..d804802ee --- /dev/null +++ b/wpd/WpdBasicHardwareDriver/WpdObjectEnum.cpp @@ -0,0 +1,431 @@ +#include "stdafx.h" + +#include "WpdObjectEnum.tmh" + +WpdObjectEnumerator::WpdObjectEnumerator() +{ + +} + +WpdObjectEnumerator::~WpdObjectEnumerator() +{ + +} + +HRESULT WpdObjectEnumerator::Initialize(_In_ WpdBaseDriver* pBaseDriver) +{ + m_pBaseDriver = pBaseDriver; + return S_OK; +} + +HRESULT WpdObjectEnumerator::DispatchWpdMessage(_In_ REFPROPERTYKEY Command, + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + + if (Command.fmtid != WPD_CATEGORY_OBJECT_ENUMERATION) + { + hr = E_INVALIDARG; + CHECK_HR(hr, "This object does not support this command category %ws",CComBSTR(Command.fmtid)); + } + + if (hr == S_OK) + { + if (Command.pid == WPD_COMMAND_OBJECT_ENUMERATION_START_FIND.pid) + { + hr = OnStartFind(pParams, pResults); + CHECK_HR(hr, "Failed to begin enumeration"); + } + else if (Command.pid == WPD_COMMAND_OBJECT_ENUMERATION_FIND_NEXT.pid) + { + hr = OnFindNext(pParams, pResults); + if(FAILED(hr)) + { + CHECK_HR(hr, "Failed to find next object"); + } + } + else if (Command.pid == WPD_COMMAND_OBJECT_ENUMERATION_END_FIND.pid) + { + hr = OnEndFind(pParams, pResults); + CHECK_HR(hr, "Failed to end enumeration"); + } + else + { + hr = E_NOTIMPL; + CHECK_HR(hr, "This object does not support this command id %d", Command.pid); + } + } + + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_OBJECT_ENUMERATION_START_FIND + * command. + * + * The parameters sent to us are: + * - WPD_PROPERTY_OBJECT_ENUMERATION_PARENT_ID: the parent where we should start + * the enumeration. + * - WPD_PROPERTY_OBJECT_ENUMERATION_FILTER: the filter to use when doing + * enumeration. Since this parameter is optional, it may not exist. + * This driver currently ignores the filter parameter. + * + * The driver should: + * - Create a new context for this enumeration. + * - Set the string identifier in WPD_PROPERTY_OBJECT_ENUMERATION_CONTEXT for the newly created enumeration context. + * This value will be passed back during OnFindNext and OnEndFind. + */ +HRESULT WpdObjectEnumerator::OnStartFind(_In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + LPWSTR wszParentID = NULL; + ContextMap* pContextMap = NULL; + CAtlStringW strEnumContext; + + // First get ALL parameters for this command. If we cannot get ALL parameters + // then E_INVALIDARG should be returned and no further processing should occur. + + // Get the object identifier of the parent where the enumeration is starting from. + hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_ENUMERATION_PARENT_ID, &wszParentID); + if (hr != S_OK) + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_ENUMERATION_PARENT_ID"); + } + + // Get the client context map so we can store an enumeration context for this enumeration + // operation. + if (hr == S_OK) + { + hr = pParams->GetIUnknownValue(PRIVATE_SAMPLE_DRIVER_CLIENT_CONTEXT_MAP, (IUnknown**)&pContextMap); + CHECK_HR(hr, "Failed to get PRIVATE_SAMPLE_DRIVER_CLIENT_CONTEXT_MAP"); + } + + // Create and initialize a new enumeration context. + // Add the new enumertion context to the client context map. This context is used to + // keep track of this particular enumeration operation. + if (hr == S_OK) + { + WpdObjectEnumeratorContext* pEnumeratorContext = new WpdObjectEnumeratorContext(); + if (pEnumeratorContext != NULL) + { + // Initialize the enumeration context + InitializeEnumerationContext(pEnumeratorContext, wszParentID); + + // Add the enumeration context to the client context map. + pContextMap->Add(pEnumeratorContext, strEnumContext); + + // Release the context because Add AddRef's it + SAFE_RELEASE(pEnumeratorContext); + } + else + { + hr = E_OUTOFMEMORY; + CHECK_HR(hr, "Failed to allocate enumeration context"); + } + } + + // Set the WPD_PROPERTY_OBJECT_ENUMERATION_CONTEXT value in the results. + // This context identifier will be passed back during OnFindNext and OnEndFind to allow the driver to access it. + if (hr == S_OK) + { + hr = pResults->SetStringValue(WPD_PROPERTY_OBJECT_ENUMERATION_CONTEXT, strEnumContext); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_OBJECT_ENUMERATION_CONTEXT"); + } + + // Free the memory. + CoTaskMemFree(wszParentID); + wszParentID = NULL; + + SAFE_RELEASE(pContextMap); + + return hr; +} + +HRESULT WpdObjectEnumerator::OnFindNext(_In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + LPWSTR wszEnumContext = NULL; + DWORD dwNumObjectsRequested = 0; + ContextMap* pContextMap = NULL; + WpdObjectEnumeratorContext* pEnumeratorContext = NULL; + DWORD NumObjectsEnumerated = 0; + + CComPtr pObjectIDCollection; + + // First get ALL parameters for this command. If we cannot get ALL parameters + // then E_INVALIDARG should be returned and no further processing should occur. + + // Get the enumeration context identifier for this enumeration operation. + // The enumeration context identifier is needed to lookup the specific + // enumeration context in the client context map for this enumeration operation. + // NOTE that more than one enumeration may be in progress. + hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_ENUMERATION_CONTEXT, &wszEnumContext); + if (hr != S_OK) + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_ENUMERATION_CONTEXT"); + } + + // Get the number of objects requested for this enumeration call. + // The driver should always attempt to meet this requested value. + // If there are fewer children than requested, the driver should return the remaining + // children and a return code of S_FALSE. + hr = pParams->GetUnsignedIntegerValue(WPD_PROPERTY_OBJECT_ENUMERATION_NUM_OBJECTS_REQUESTED, &dwNumObjectsRequested); + if (hr != S_OK) + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_ENUMERATION_NUM_OBJECTS_REQUESTED"); + } + + // Get the client context map so we can retrieve the enumeration context for this enumeration + // operation. + if (hr == S_OK) + { + hr = pParams->GetIUnknownValue(PRIVATE_SAMPLE_DRIVER_CLIENT_CONTEXT_MAP, (IUnknown**)&pContextMap); + CHECK_HR(hr, "Failed to get PRIVATE_SAMPLE_DRIVER_CLIENT_CONTEXT_MAP"); + } + + if (hr == S_OK) + { + pEnumeratorContext = (WpdObjectEnumeratorContext*)pContextMap->GetContext(wszEnumContext); + if (pEnumeratorContext == NULL) + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Missing enumeration context"); + } + } + + // CoCreate a collection to store the object identifiers being returned for this enumeration call. + if (hr == S_OK) + { + hr = CoCreateInstance(CLSID_PortableDevicePropVariantCollection, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDevicePropVariantCollection, + (VOID**) &pObjectIDCollection); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDevicePropVariantCollection"); + } + + // If the enumeration context reports that their are more objects to return, then continue, if not, + // return an empty results set. + if ((hr == S_OK) && (pEnumeratorContext != NULL) && (pEnumeratorContext->HasMoreChildrenToEnumerate() == TRUE)) + { + if (pEnumeratorContext->m_strParentObjectID.CompareNoCase(L"") == 0) + { + // We are being asked for the WPD_DEVICE_OBJECT_ID + hr = AddStringValueToPropVariantCollection(pObjectIDCollection, WPD_DEVICE_OBJECT_ID); + CHECK_HR(hr, "Failed to add 'DEVICE' object ID to enumeration collection"); + + // Update the the number of children we are returning for this enumeration call + NumObjectsEnumerated++; + } + else if (pEnumeratorContext->m_strParentObjectID.CompareNoCase(WPD_DEVICE_OBJECT_ID) == 0) + { + + // We are being asked for direct children of the WPD_DEVICE_OBJECT_ID + switch (m_pBaseDriver->m_SensorType) + { + case WpdBaseDriver::UNKNOWN: + hr = AddStringValueToPropVariantCollection(pObjectIDCollection, SENSOR_OBJECT_ID); + break; + case WpdBaseDriver::COMPASS: + hr = AddStringValueToPropVariantCollection(pObjectIDCollection, COMPASS_SENSOR_OBJECT_ID); + break; + case WpdBaseDriver::SENSIRON: + hr = AddStringValueToPropVariantCollection(pObjectIDCollection, TEMP_SENSOR_OBJECT_ID); + break; + case WpdBaseDriver::FLEX: + hr = AddStringValueToPropVariantCollection(pObjectIDCollection, FLEX_SENSOR_OBJECT_ID); + break; + case WpdBaseDriver::PING: + hr = AddStringValueToPropVariantCollection(pObjectIDCollection, PING_SENSOR_OBJECT_ID); + break; + case WpdBaseDriver::PIR: + hr = AddStringValueToPropVariantCollection(pObjectIDCollection, PIR_SENSOR_OBJECT_ID); + break; + case WpdBaseDriver::MEMSIC: + hr = AddStringValueToPropVariantCollection(pObjectIDCollection, MEMSIC_SENSOR_OBJECT_ID); + break; + case WpdBaseDriver::QTI: + hr = AddStringValueToPropVariantCollection(pObjectIDCollection, QTI_SENSOR_OBJECT_ID); + break; + case WpdBaseDriver::PIEZO: + hr = AddStringValueToPropVariantCollection(pObjectIDCollection, PIEZO_SENSOR_OBJECT_ID); + break; + case WpdBaseDriver::HITACHI: + hr = AddStringValueToPropVariantCollection(pObjectIDCollection, HITACHI_SENSOR_OBJECT_ID); + break; + default: + break; + } + CHECK_HR(hr, "Failed to add storage object ID to enumeration collection"); + + // Update the the number of children we are returning for this enumeration call + NumObjectsEnumerated++; + + } + } + + // Set the collection of object identifiers enumerated in the results + if (hr == S_OK) + { + hr = pResults->SetIPortableDevicePropVariantCollectionValue(WPD_PROPERTY_OBJECT_ENUMERATION_OBJECT_IDS, pObjectIDCollection); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_OBJECT_ENUMERATION_OBJECT_IDS"); + } + + // If the enumeration context reports that their are no more objects to return then return S_FALSE indicating to the + // caller that we are finished. + if (hr == S_OK) + { + if (pEnumeratorContext != NULL) + { + // Update the number of children we have enumerated and returned to the caller + pEnumeratorContext->m_ChildrenEnumerated += NumObjectsEnumerated; + + // Check the number requested against the number enumerated and set the HRESULT + // accordingly. + if (NumObjectsEnumerated < dwNumObjectsRequested) + { + // We returned less than the number of objects requested to the caller + hr = S_FALSE; + } + else + { + // We returned exactly the number of objects requested to the caller + hr = S_OK; + } + } + } + + // Free the memory. + CoTaskMemFree(wszEnumContext); + wszEnumContext = NULL; + + SAFE_RELEASE(pContextMap); + SAFE_RELEASE(pEnumeratorContext); + + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_OBJECT_ENUMERATION_END_FIND + * command. + * + * The parameters sent to us are: + * - WPD_PROPERTY_OBJECT_ENUMERATION_CONTEXT: the context the driver returned to + * the client in OnStartFind. + * + * The driver should: + * - Destroy any data associated with this context. + */ +HRESULT WpdObjectEnumerator::OnEndFind(_In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + LPWSTR wszEnumContext = NULL; + ContextMap* pContextMap = NULL; + + UNREFERENCED_PARAMETER(pResults); + + // First get ALL parameters for this command. If we cannot get ALL parameters + // then E_INVALIDARG should be returned and no further processing should occur. + + // Get the enumeration context identifier for this enumeration operation. We will + // need this to lookup the specific enumeration context in the client context map. + hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_ENUMERATION_CONTEXT, &wszEnumContext); + if (hr != S_OK) + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_ENUMERATION_CONTEXT"); + } + + // Get the client context map so we can retrieve the enumeration context for this enumeration + // operation using the WPD_PROPERTY_OBJECT_ENUMERATION_CONTEXT property value obtained above. + if (hr == S_OK) + { + hr = pParams->GetIUnknownValue(PRIVATE_SAMPLE_DRIVER_CLIENT_CONTEXT_MAP, (IUnknown**)&pContextMap); + CHECK_HR(hr, "Failed to get PRIVATE_SAMPLE_DRIVER_CLIENT_CONTEXT_MAP"); + } + + // Destroy any data allocated/associated with the enumeration context and then remove it from the context map. + // We no longer need to keep this context around because the enumeration has been ended. + if (hr == S_OK) + { + pContextMap->Remove(wszEnumContext); + } + + // Free the memory. + CoTaskMemFree(wszEnumContext); + wszEnumContext = NULL; + + SAFE_RELEASE(pContextMap); + + return hr; +} + +// Initialize the enumeration context +VOID WpdObjectEnumerator::InitializeEnumerationContext( + _In_ WpdObjectEnumeratorContext* pEnumeratorContext, + _In_ LPCWSTR wszParentObjectID) +{ + if (pEnumeratorContext == NULL) + { + return; + } + + // Initialize the enumeration context with the parent object identifier + pEnumeratorContext->m_strParentObjectID = wszParentObjectID; + + // Our sample driver has a very simple object structure where we know + // how many children are under each parent. + // The eumeration context is initialized below with this information. + if (pEnumeratorContext->m_strParentObjectID.CompareNoCase(L"") == 0) + { + // Clients passing an 'empty' string for the parent are asking for the + // 'DEVICE' object. We should return 1 child in this case. + pEnumeratorContext->m_TotalChildren = 1; + } + else if (pEnumeratorContext->m_strParentObjectID.CompareNoCase(WPD_DEVICE_OBJECT_ID) == 0) + { + // The device object contains 1 child (the storage object). + pEnumeratorContext->m_TotalChildren = 1; + } + // If the sensor objects have children, add them here... + else + { + // The sensor object contains 0 children. + pEnumeratorContext->m_TotalChildren = 0; + } +} + +HRESULT WpdObjectEnumerator::AddStringValueToPropVariantCollection( + _In_ IPortableDevicePropVariantCollection* pCollection, + _In_ LPCWSTR wszValue) +{ + HRESULT hr = S_OK; + + if ((pCollection == NULL) || + (wszValue == NULL)) + { + hr = E_INVALIDARG; + return hr; + } + + PROPVARIANT pv = {0}; + PropVariantInit(&pv); + + pv.vt = VT_LPWSTR; + pv.pwszVal = (LPWSTR)wszValue; + + // The wszValue will be copied into the collection, keeping the ownership + // of the string belonging to the caller. + // Don't call PropVariantClear, since we did not allocate the memory for these string values + + hr = pCollection->Add(&pv); + + return hr; +} diff --git a/wpd/WpdBasicHardwareDriver/WpdObjectEnum.h b/wpd/WpdBasicHardwareDriver/WpdObjectEnum.h new file mode 100644 index 000000000..e6dc05422 --- /dev/null +++ b/wpd/WpdBasicHardwareDriver/WpdObjectEnum.h @@ -0,0 +1,107 @@ +#pragma once + +// This class is used to store the context for a specific enumeration. +class WpdObjectEnumeratorContext : public IUnknown +{ +public: + WpdObjectEnumeratorContext() : + m_cRef(1), + m_TotalChildren(0), + m_ChildrenEnumerated(0) + { + + } + + ~WpdObjectEnumeratorContext() + { + + } + +public: // IUnknown + ULONG __stdcall AddRef() + { + InterlockedIncrement((long*) &m_cRef); + return m_cRef; + } + + _At_(this, __drv_freesMem(Mem)) + ULONG __stdcall Release() + { + ULONG ulRefCount = m_cRef - 1; + + if (InterlockedDecrement((long*) &m_cRef) == 0) + { + delete this; + return 0; + } + return ulRefCount; + } + + HRESULT __stdcall QueryInterface( + REFIID riid, + void** ppv) + { + HRESULT hr = S_OK; + + if(riid == IID_IUnknown) + { + *ppv = static_cast(this); + AddRef(); + } + else + { + *ppv = NULL; + hr = E_NOINTERFACE; + } + + return hr; + } + +private: + DWORD m_cRef; + +public: + BOOL HasMoreChildrenToEnumerate() + { + return (m_TotalChildren > m_ChildrenEnumerated)?TRUE:FALSE; + } + +// WpdObjectEnumeratorContext specific data +public: + CAtlStringW m_strParentObjectID; // object identifier of the object whose children are being enumerated + DWORD m_TotalChildren; // number of bytes transferred from the resource to the caller + DWORD m_ChildrenEnumerated; // number of children returned during the enumeration operation +}; + +class WpdObjectEnumerator +{ +public: + WpdObjectEnumerator(); + virtual ~WpdObjectEnumerator(); + + HRESULT Initialize(_In_ WpdBaseDriver* pBaseDriver); + + HRESULT DispatchWpdMessage(_In_ REFPROPERTYKEY Command, + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnStartFind(_In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnFindNext(_In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnEndFind(_In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + +private: + WpdBaseDriver* m_pBaseDriver; + + VOID InitializeEnumerationContext( + _In_ WpdObjectEnumeratorContext* pEnumeratorContext, + _In_ LPCWSTR wszParentObjectID); + + HRESULT AddStringValueToPropVariantCollection( + _In_ IPortableDevicePropVariantCollection* pCollection, + _In_ LPCWSTR wszValue); +}; diff --git a/wpd/WpdBasicHardwareDriver/WpdObjectProperties.cpp b/wpd/WpdBasicHardwareDriver/WpdObjectProperties.cpp new file mode 100644 index 000000000..927b8a19f --- /dev/null +++ b/wpd/WpdBasicHardwareDriver/WpdObjectProperties.cpp @@ -0,0 +1,1314 @@ +#include "stdafx.h" + +#include "WpdObjectProperties.tmh" + +const PROPERTYKEY g_SupportedCommonProperties[] = +{ + WPD_OBJECT_ID, + WPD_OBJECT_PERSISTENT_UNIQUE_ID, + WPD_OBJECT_PARENT_ID, + WPD_OBJECT_NAME, + WPD_OBJECT_FORMAT, + WPD_OBJECT_CONTENT_TYPE, + WPD_OBJECT_CAN_DELETE, +}; + +const PROPERTYKEY g_SupportedDeviceProperties[] = +{ + WPD_DEVICE_FIRMWARE_VERSION, + WPD_DEVICE_POWER_LEVEL, + WPD_DEVICE_POWER_SOURCE, + WPD_DEVICE_PROTOCOL, + WPD_DEVICE_MODEL, + WPD_DEVICE_SERIAL_NUMBER, + WPD_DEVICE_SUPPORTS_NON_CONSUMABLE, + WPD_DEVICE_MANUFACTURER, + WPD_DEVICE_FRIENDLY_NAME, + WPD_DEVICE_TYPE, + WPD_FUNCTIONAL_OBJECT_CATEGORY, +}; + +const PROPERTYKEY g_SupportedSensorProperties[] = +{ + SENSOR_READING, + SENSOR_UPDATE_INTERVAL, + WPD_FUNCTIONAL_OBJECT_CATEGORY, +}; + + +WpdObjectProperties::WpdObjectProperties() : + m_dwUpdateInterval(0), + m_llSensorReading(0) +{ + +} + +WpdObjectProperties::~WpdObjectProperties() +{ + m_pBaseDriver = NULL; +} + +HRESULT WpdObjectProperties::Initialize(_In_ WpdBaseDriver* pBaseDriver) +{ + m_pBaseDriver = pBaseDriver; + return S_OK; +} + +HRESULT WpdObjectProperties::DispatchWpdMessage( + _In_ REFPROPERTYKEY Command, + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + + if (Command.fmtid != WPD_CATEGORY_OBJECT_PROPERTIES) + { + hr = E_INVALIDARG; + CHECK_HR(hr, "This object does not support this command category %ws",CComBSTR(Command.fmtid)); + } + + if (hr == S_OK) + { + if (IsEqualPropertyKey(Command, WPD_COMMAND_OBJECT_PROPERTIES_GET_SUPPORTED)) + { + hr = OnGetSupportedProperties(pParams, pResults); + CHECK_HR(hr, "Failed to get supported properties"); + } + else if(IsEqualPropertyKey(Command, WPD_COMMAND_OBJECT_PROPERTIES_GET)) + { + hr = OnGetPropertyValues(pParams, pResults); + if(FAILED(hr)) + { + CHECK_HR(hr, "Failed to get properties"); + } + } + else if(IsEqualPropertyKey(Command, WPD_COMMAND_OBJECT_PROPERTIES_GET_ALL)) + { + hr = OnGetAllPropertyValues(pParams, pResults); + if(FAILED(hr)) + { + CHECK_HR(hr, "Failed to get all properties"); + } + } + else if(IsEqualPropertyKey(Command, WPD_COMMAND_OBJECT_PROPERTIES_SET)) + { + hr = OnSetPropertyValues(pParams, pResults); + if(FAILED(hr)) + { + CHECK_HR(hr, "Failed to set properties"); + } + } + else if(IsEqualPropertyKey(Command, WPD_COMMAND_OBJECT_PROPERTIES_GET_ATTRIBUTES)) + { + hr = OnGetPropertyAttributes(pParams, pResults); + if(FAILED(hr)) + { + CHECK_HR(hr, "Failed to get property attributes"); + } + } + else if(IsEqualPropertyKey(Command, WPD_COMMAND_OBJECT_PROPERTIES_DELETE)) + { + hr = OnDeleteProperties(pParams, pResults); + if(FAILED(hr)) + { + CHECK_HR(hr, "Failed to delete properties"); + } + } + else + { + hr = E_NOTIMPL; + CHECK_HR(hr, "This object does not support this command id %d", Command.pid); + } + } + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_OBJECT_PROPERTIES_GET_SUPPORTED + * command. + * + * The parameters sent to us are: + * - WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID: identifies the object whose supported properties have + * been requested. + * + * - WPD_PROPERTY_OBJECT_PROPERTIES_FILTER: the filter to use when returning supported properties. + * Since this parameter is optional, it may not exist. + * ! This driver currently ignores the filter parameter. ! + * + * The driver should: + * - Return supported property keys for the specified object in WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_KEYS + */ +HRESULT WpdObjectProperties::OnGetSupportedProperties( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + LPWSTR wszObjectID = NULL; + CComPtr pKeys; + + // First get ALL parameters for this command. If we cannot get ALL parameters + // then E_INVALIDARG should be returned and no further processing should occur. + + // Get the object identifier whose supported properties have been requested + hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID, &wszObjectID); + if (hr != S_OK) + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Missing string value for WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID"); + } + + // CoCreate a collection to store the supported property keys. + if (hr == S_OK) + { + hr = CoCreateInstance(CLSID_PortableDeviceKeyCollection, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceKeyCollection, + (VOID**) &pKeys); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceKeyCollection"); + } + + // Add supported property keys for the specified object to the collection + if (hr == S_OK) + { + hr = AddSupportedPropertyKeys(wszObjectID, pKeys); + CHECK_HR(hr, "Failed to add supported property keys for object '%ws'", wszObjectID); + } + + // Set the WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_KEYS value in the results. + if (hr == S_OK) + { + hr = pResults->SetIPortableDeviceKeyCollectionValue(WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_KEYS, pKeys); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_KEYS"); + } + + // Free the memory. + CoTaskMemFree(wszObjectID); + wszObjectID = NULL; + + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_OBJECT_PROPERTIES_GET + * command. + * + * The parameters sent to us are: + * - WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID: identifies the object whose property values have been requested. + * - WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_KEYS: a collection of property keys, identifying which + * specific property values we are requested to return. + * + * The driver should: + * - Return all requested property values in WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_VALUES. If any property read failed, the corresponding value should be + * set to type VT_ERROR with the 'scode' member holding the HRESULT reason for the failure. + * - S_OK should be returned if all properties were read successfully. + * - S_FALSE should be returned if any property read failed. + * - Any error return indicates that the driver did not fill in any results, and the caller will + * not attempt to unpack any property values. + */ +HRESULT WpdObjectProperties::OnGetPropertyValues( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + LPWSTR wszObjectID = NULL; + CComPtr pValues; + CComPtr pKeys; + + // First get ALL parameters for this command. If we cannot get ALL parameters + // then E_INVALIDARG should be returned and no further processing should occur. + + // Get the object identifier whose property values have been requested + if (hr == S_OK) + { + hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID, &wszObjectID); + CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID"); + } + + // Get the list of property keys for the property values the caller wants to retrieve from the specified object + if (hr == S_OK) + { + hr = pParams->GetIPortableDeviceKeyCollectionValue(WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_KEYS, &pKeys); + CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_KEYS"); + } + + // CoCreate a collection to store the property values. + if (hr == S_OK) + { + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**) &pValues); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues"); + } + + // Read the specified properties on the specified object and add the property values to the collection. + if (hr == S_OK) + { + hr = GetPropertyValuesForObject(wszObjectID, pKeys, pValues); + CHECK_HR(hr, "Failed to get property values for object '%ws'", wszObjectID); + } + + // S_OK or S_FALSE can be returned from GetPropertyValuesForObject( ). + // S_FALSE means that 1 or more property values could not be retrieved successfully. + // The value for the specified property should be set to an error HRESULT of + // the reason why the property could not be read. + // (e.g. If the property being requested is not supported on the object then an error of + // HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED) should be set as the value. + if (SUCCEEDED(hr)) + { + // Set the WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_VALUES value in the results. + HRESULT hrTemp = S_OK; + hrTemp = pResults->SetIPortableDeviceValuesValue(WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_VALUES, pValues); + CHECK_HR(hrTemp, ("Failed to set WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_VALUES")); + + if(FAILED(hrTemp)) + { + hr = hrTemp; + } + } + + // Free the memory. + CoTaskMemFree(wszObjectID); + wszObjectID = NULL; + + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_OBJECT_PROPERTIES_GET_ALL + * command. + * + * The parameters sent to us are: + * - WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID: identifies the object whose property values have been requested. + * + * The driver should: + * - Return all property values in WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_VALUES. If any property read failed, the corresponding value should be + * set to type VT_ERROR with the 'scode' member holding the HRESULT reason for the failure. + * - S_OK should be returned if all properties were read successfully. + * - S_FALSE should be returned if any property read failed. + * - Any error return indicates that the driver did not fill in any results, and the caller will + * not attempt to unpack any property values. + */ +HRESULT WpdObjectProperties::OnGetAllPropertyValues( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + LPWSTR wszObjectID = NULL; + CComPtr pValues; + CComPtr pKeys; + + // First get ALL parameters for this command. If we cannot get ALL parameters + // then E_INVALIDARG should be returned and no further processing should occur. + + // Get the object identifier whose property values have been requested + if (hr == S_OK) + { + hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID, &wszObjectID); + CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID"); + } + + // CoCreate a collection to store the property values. + if (hr == S_OK) + { + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**) &pValues); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues"); + } + + // CoCreate a collection to store the property keys we are going to use + // to request the property values of. + if (hr == S_OK) + { + hr = CoCreateInstance(CLSID_PortableDeviceKeyCollection, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceKeyCollection, + (VOID**) &pKeys); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceKeyCollection"); + } + + // First we make a request for ALL supported property keys for the specified object. + // Next, we delegate to our helper function GetPropertyValuesForObject( ) passing + // the entire property key collection. This will reuse existing implementation + // in our driver to perform the GetAllPropertyValues operation. + if (hr == S_OK) + { + hr = AddSupportedPropertyKeys(wszObjectID, pKeys); + CHECK_HR(hr, "Failed to get ALL supported properties for object '%ws'", wszObjectID); + if (hr == S_OK) + { + hr = GetPropertyValuesForObject(wszObjectID, pKeys, pValues); + CHECK_HR(hr, "Failed to get property values for object '%ws'", wszObjectID); + } + } + + // S_OK or S_FALSE can be returned from GetPropertyValuesForObject( ). + // S_FALSE means that 1 or more property values could not be retrieved successfully. + // The value for the specified property key should be set to the error HRESULT of + // the reason why the property could not be read. + // (i.e. an error of HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED) if a property value was + // requested and is not supported by the specified object.) + if (SUCCEEDED(hr)) + { + // Set the WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_VALUES value in the results + HRESULT hrTemp = S_OK; + hrTemp = pResults->SetIPortableDeviceValuesValue(WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_VALUES, pValues); + CHECK_HR(hrTemp, ("Failed to set WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_VALUES")); + + if(FAILED(hrTemp)) + { + hr = hrTemp; + } + } + + // Free the memory. + CoTaskMemFree(wszObjectID); + wszObjectID = NULL; + + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_OBJECT_PROPERTIES_SET + * command. + * + * The parameters sent to us are: + * - WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID: identifies the object whose property values we want to return. + * - WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_VALUES: an IPortableDeviceValues of values, identifying which + * specific property values we are requested to write. + * + * The driver should: + * - Write all requested property values. For each property, a write result should be returned in the + * write result property store. + * - If any property write failed, the corresponding write result value should be + * set to type VT_ERROR with the 'scode' member holding the HRESULT reason for the failure. + * - S_OK should be returned if all properties were written successfully. + * - S_FALSE should be returned if any property write failed. + * - Any error return indicates that the driver did not write any results, and the caller will + * not attempt to unpack any property write results. + */ +HRESULT WpdObjectProperties::OnSetPropertyValues( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + HRESULT hrResult = S_OK; + LPWSTR wszObjectID = NULL; + DWORD cValues = 0; + CAtlStringW strObjectID; + + CComPtr pValues; + CComPtr pWriteResults; + CComPtr pEventParams; + + + // First get ALL parameters for this command. If we cannot get ALL parameters + // then E_INVALIDARG should be returned and no further processing should occur. + + // Get the object identifier whose property values are being set + if (hr == S_OK) + { + hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID, &wszObjectID); + CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID"); + } + + // Get the caller-supplied property values requested to be set on the object + if (hr == S_OK) + { + strObjectID = wszObjectID; + + hr = pParams->GetIPortableDeviceValuesValue(WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_VALUES, &pValues); + CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_VALUES"); + } + + // CoCreate a collection to store the property set operation results. + if (hr == S_OK) + { + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**) &pWriteResults); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues"); + } + + // Set the property values on the specified object + if (hr == S_OK) + { + // Since this driver does not support setting any properties, all property set operation + // results will be set to E_ACCESSDENIED. + if (hr == S_OK) + { + hr = pValues->GetCount(&cValues); + CHECK_HR(hr, "Failed to get total number of values"); + } + + if (hr == S_OK) + { + for (DWORD dwIndex = 0; dwIndex < cValues; dwIndex++) + { + PROPERTYKEY Key = WPD_PROPERTY_NULL; + PROPVARIANT Value = {0}; + + PropVariantInit(&Value); + + hr = pValues->GetAt(dwIndex, &Key, &Value); + CHECK_HR(hr, "Failed to get PROPERTYKEY at index %d", dwIndex); + + if (hr == S_OK) + { + // TODO: Add other ...OBJECT_ID strings where applicable + if ( + (strObjectID.CompareNoCase(SENSOR_OBJECT_ID) == 0) || + (strObjectID.CompareNoCase(TEMP_SENSOR_OBJECT_ID) == 0) || + (strObjectID.CompareNoCase(FLEX_SENSOR_OBJECT_ID) == 0) || + (strObjectID.CompareNoCase(PIR_SENSOR_OBJECT_ID) == 0) || + (strObjectID.CompareNoCase(PING_SENSOR_OBJECT_ID) == 0) || + (strObjectID.CompareNoCase(QTI_SENSOR_OBJECT_ID) == 0) || + (strObjectID.CompareNoCase(MEMSIC_SENSOR_OBJECT_ID) == 0) || + (strObjectID.CompareNoCase(HITACHI_SENSOR_OBJECT_ID) == 0) || + (strObjectID.CompareNoCase(PIEZO_SENSOR_OBJECT_ID) == 0) || + (strObjectID.CompareNoCase(COMPASS_SENSOR_OBJECT_ID) == 0) + ) + { + if (IsEqualPropertyKey(Key, SENSOR_UPDATE_INTERVAL)) + { + if (Value.vt == VT_UI4) + { + hr = SendUpdateIntervalToDevice(Value.ulVal); + CHECK_HR(hr, "Failed to send the new SENSOR_UPDATE_INTERVAL %d on the device", Value.ulVal); + } + else + { + // property failed to be set as it is an invalid vartype + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + CHECK_HR(hr, "Failed to update the SENSOR_UPDATE_INTERVAL because the vartype is invalid. Expected VT_UI4, got %d", Value.vt); + } + + // An error has occurred, set the overall result to S_FALSE + if (hr != S_OK) + { + hrResult = S_FALSE; + } + + hr = pWriteResults->SetErrorValue(Key, hr); + CHECK_HR(hr, "Failed to set error result value of SENSOR_UPDATE_INTERVAL for '%ws'", wszObjectID); + } + else + { + // Other properties for the sensor object are read only + hr = pWriteResults->SetErrorValue(Key, E_ACCESSDENIED); + CHECK_HR(hr, "Failed to set error result value at index %d for '%ws'", dwIndex, wszObjectID); + hrResult = S_FALSE; + } + } + else + { + // Properties for all other objects are read only + hr = pWriteResults->SetErrorValue(Key, E_ACCESSDENIED); + CHECK_HR(hr, "Failed to set error result value at index %d for '%ws'", dwIndex, wszObjectID); + hrResult = S_FALSE; + } + } + + PropVariantClear(&Value); + + } // end for + } // end if + } + + // At least one property failed to be set + if (hrResult != S_OK) + { + hr = hrResult; + } + + if (SUCCEEDED(hr)) + { + // Set the WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_WRITE_RESULTS value in the results + HRESULT hrTemp = pResults->SetIPortableDeviceValuesValue(WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_WRITE_RESULTS, pWriteResults); + CHECK_HR(hrTemp, ("Failed to set WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_WRITE_RESULTS")); + + // Don't override the S_FALSE hresult that indicates at least one property had failed + if (hr == S_OK) + { + hr = hrTemp; + } + } + + // Free the memory. + CoTaskMemFree(wszObjectID); + wszObjectID = NULL; + + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_OBJECT_PROPERTIES_GET_ATTRIBUTES + * command. + * + * The parameters sent to us are: + * - WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID: identifies the object whose property attributes we want to return. + * - WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_KEYS: a collection of property keys containing a single value, + * which is the key identifying the specific property attributes we are requested to return. + * + * The driver should: + * - Return the requested property attributes. If any property attributes failed to be retrieved, + * the corresponding value should be set to type VT_ERROR with the 'scode' member holding the + * HRESULT reason for the failure. + * - S_OK should be returned if all property attributes were read successfully. + * - S_FALSE should be returned if any property attribute failed. + * - Any error return indicates that the driver did not fill in any results, and the caller will + * not attempt to unpack any property values. + */ +HRESULT WpdObjectProperties::OnGetPropertyAttributes( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + LPWSTR wszObjectID = NULL; + PROPERTYKEY Key = WPD_PROPERTY_NULL; + CComPtr pAttributes; + + // First get ALL parameters for this command. If we cannot get ALL parameters + // then E_INVALIDARG should be returned and no further processing should occur. + + // Get the object identifier whose property attributes have been requested + if (hr == S_OK) + { + hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID, &wszObjectID); + CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID"); + } + + // Get the list of property keys whose attributes are being requested + if (hr == S_OK) + { + hr = pParams->GetKeyValue(WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_KEYS, &Key); + CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_KEYS"); + } + + // CoCreate a collection to store the property attributes. + if (hr == S_OK) + { + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**) &pAttributes); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues"); + } + + // Get the attributes for the specified properties on the specified object and add them + // to the collection. + if (hr == S_OK) + { + hr = GetPropertyAttributesForObject(wszObjectID, Key, pAttributes); + CHECK_HR(hr, "Failed to get property attributes"); + } + + if (SUCCEEDED(hr)) + { + // Set the WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_ATTRIBUTES value in the results + HRESULT hrTemp = S_OK; + hrTemp = pResults->SetIPortableDeviceValuesValue(WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_ATTRIBUTES, pAttributes); + CHECK_HR(hrTemp, ("Failed to set WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_ATTRIBUTES")); + + if(FAILED(hrTemp)) + { + hr = hrTemp; + } + } + + // Free the memory. + CoTaskMemFree(wszObjectID); + wszObjectID = NULL; + + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_OBJECT_PROPERTIES_DELETE + * command. + * + * The parameters sent to us are: + * - WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID: identifies the object whose properties should be deleted. + * - WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_KEYS: a collection of property keys indicating which + * properties to delete. + * + * The driver should: + * - Delete the specified properties from the object. + * - S_OK should be returned if all specified properties were successfully deleted. + * - E_ACCESSDENIED should be returned if the client attempts to delete a property which is not deletable (i.e. + * WPD_PROPERTY_ATTRIBUTE_CAN_DELETE is FALSE for that property.) + */ +HRESULT WpdObjectProperties::OnDeleteProperties( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = E_ACCESSDENIED; + + UNREFERENCED_PARAMETER(pParams); + UNREFERENCED_PARAMETER(pResults); + + // This driver has no properties which can be deleted. + + return hr; +} + +/** + * This method is called to populate supported PROPERTYKEYs found on objects. + * + * The parameters sent to us are: + * wszObjectID - the object whose supported property keys are being requested + * pKeys - An IPortableDeviceKeyCollection to be populated with supported PROPERTYKEYs + * + * The driver should: + * Add PROPERTYKEYs pertaining to the specified object. + */ +HRESULT AddSupportedPropertyKeys( + _In_ LPCWSTR wszObjectID, + _In_ IPortableDeviceKeyCollection* pKeys) +{ + HRESULT hr = S_OK; + CAtlStringW strObjectID = wszObjectID; + + // Add Common PROPERTYKEYs for ALL WPD objects + AddCommonPropertyKeys(pKeys); + + if (strObjectID.CompareNoCase(WPD_DEVICE_OBJECT_ID) == 0) + { + // Add the PROPERTYKEYs for the 'DEVICE' object + AddDevicePropertyKeys(pKeys); + } + + // Add other PROPERTYKEYs for other supported objects... + // TODO: Add comparison to other ..._OBJECT_IDs where applicable + if ( + (strObjectID.CompareNoCase(SENSOR_OBJECT_ID) == 0) || + (strObjectID.CompareNoCase(TEMP_SENSOR_OBJECT_ID) == 0) || + (strObjectID.CompareNoCase(FLEX_SENSOR_OBJECT_ID) == 0) || + (strObjectID.CompareNoCase(PIR_SENSOR_OBJECT_ID) == 0) || + (strObjectID.CompareNoCase(PING_SENSOR_OBJECT_ID) == 0) || + (strObjectID.CompareNoCase(QTI_SENSOR_OBJECT_ID) == 0) || + (strObjectID.CompareNoCase(MEMSIC_SENSOR_OBJECT_ID) == 0) || + (strObjectID.CompareNoCase(HITACHI_SENSOR_OBJECT_ID) == 0) || + (strObjectID.CompareNoCase(PIEZO_SENSOR_OBJECT_ID) == 0) || + (strObjectID.CompareNoCase(COMPASS_SENSOR_OBJECT_ID) == 0) + ) + { + // Add the PROPERTYKEYs for the Sensor object + AddSensorPropertyKeys(pKeys); + } + + + + return hr; +} + +/** + * This method is called to populate common PROPERTYKEYs found on ALL objects. + * + * The parameters sent to us are: + * pKeys - An IPortableDeviceKeyCollection to be populated with PROPERTYKEYs + * + * The driver should: + * Add PROPERTYKEYs pertaining to the ALL objects. + */ +VOID AddCommonPropertyKeys( + _In_ IPortableDeviceKeyCollection* pKeys) +{ + if (pKeys != NULL) + { + for (DWORD dwIndex = 0; dwIndex < ARRAYSIZE(g_SupportedCommonProperties); dwIndex++) + { + HRESULT hr = S_OK; + hr = pKeys->Add(g_SupportedCommonProperties[dwIndex] ); + CHECK_HR(hr, "Failed to add common property"); + } + } +} + +/** + * This method is called to populate PROPERTYKEYs found on the SENSOR object. + * + * The parameters sent to us are: + * pKeys - An IPortableDeviceKeyCollection to be populated with PROPERTYKEYs + * + * The driver should: + * Add PROPERTYKEYs pertaining to the DEVICE object. + */ +VOID AddSensorPropertyKeys( + _In_ IPortableDeviceKeyCollection* pKeys) +{ + if (pKeys != NULL) + { + for (DWORD dwIndex = 0; dwIndex < ARRAYSIZE(g_SupportedSensorProperties); dwIndex++) + { + HRESULT hr = S_OK; + hr = pKeys->Add(g_SupportedSensorProperties[dwIndex] ); + CHECK_HR(hr, "Failed to add sensor property"); + } + } +} + +/** + * This method is called to populate common PROPERTYKEYs found on the DEVICE object. + * + * The parameters sent to us are: + * pKeys - An IPortableDeviceKeyCollection to be populated with PROPERTYKEYs + * + * The driver should: + * Add PROPERTYKEYs pertaining to the DEVICE object. + */ +VOID AddDevicePropertyKeys( + _In_ IPortableDeviceKeyCollection* pKeys) +{ + if (pKeys != NULL) + { + for (DWORD dwIndex = 0; dwIndex < ARRAYSIZE(g_SupportedDeviceProperties); dwIndex++) + { + HRESULT hr = S_OK; + hr = pKeys->Add(g_SupportedDeviceProperties[dwIndex] ); + CHECK_HR(hr, "Failed to add device property"); + } + } +} + + +/** + * This method is called to populate property values for the object specified. + * + * The parameters sent to us are: + * wszObjectID - the object whose properties are being requested. + * pKeys - the list of property keys of the properties to request from the object + * pValues - an IPortableDeviceValues which will contain the property values retreived from the object + * + * The driver should: + * Read the specified properties for the specified object and populate pValues with the + * results. + */ +HRESULT WpdObjectProperties::GetPropertyValuesForObject( + _In_ LPCWSTR wszObjectID, + _In_ IPortableDeviceKeyCollection* pKeys, + _In_ IPortableDeviceValues* pValues) +{ + HRESULT hr = S_OK; + CAtlStringW strObjectID = wszObjectID; + DWORD cKeys = 0; + + if ((wszObjectID == NULL) || + (pKeys == NULL) || + (pValues == NULL)) + { + hr = E_INVALIDARG; + return hr; + } + + hr = pKeys->GetCount(&cKeys); + CHECK_HR(hr, "Failed to number of PROPERTYKEYs in collection"); + + if (hr == S_OK) + { + // Get values for the DEVICE object + if (strObjectID.CompareNoCase(WPD_DEVICE_OBJECT_ID) == 0) + { + for (DWORD dwIndex = 0; dwIndex < cKeys; dwIndex++) + { + PROPERTYKEY Key = WPD_PROPERTY_NULL; + hr = pKeys->GetAt(dwIndex, &Key); + CHECK_HR(hr, "Failed to get PROPERTYKEY at index %d in collection", dwIndex); + + if (hr == S_OK) + { + // Preset the property value to 'error not supported'. The actual value + // will replace this value, if read from the device. + pValues->SetErrorValue(Key, HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED)); + + // Set DEVICE object properties + if (IsEqualPropertyKey(Key, WPD_DEVICE_FIRMWARE_VERSION)) + { + hr = pValues->SetStringValue(WPD_DEVICE_FIRMWARE_VERSION, DEVICE_FIRMWARE_VERSION_VALUE); + CHECK_HR(hr, "Failed to set WPD_DEVICE_FIRMWARE_VERSION"); + } + + else if (IsEqualPropertyKey(Key, WPD_DEVICE_POWER_LEVEL)) + { + hr = pValues->SetUnsignedIntegerValue(WPD_DEVICE_POWER_LEVEL, DEVICE_POWER_LEVEL_VALUE); + CHECK_HR(hr, "Failed to set WPD_DEVICE_POWER_LEVEL"); + } + + else if (IsEqualPropertyKey(Key, WPD_DEVICE_POWER_SOURCE)) + { + hr = pValues->SetUnsignedIntegerValue(WPD_DEVICE_POWER_SOURCE, WPD_POWER_SOURCE_EXTERNAL); + CHECK_HR(hr, "Failed to set WPD_DEVICE_POWER_SOURCE"); + } + + else if (IsEqualPropertyKey(Key, WPD_DEVICE_PROTOCOL)) + { + hr = pValues->SetStringValue(WPD_DEVICE_PROTOCOL, DEVICE_PROTOCOL_VALUE); + CHECK_HR(hr, "Failed to set WPD_DEVICE_PROTOCOL"); + } + + else if (IsEqualPropertyKey(Key, WPD_DEVICE_MODEL)) + { + hr = pValues->SetStringValue(WPD_DEVICE_MODEL, DEVICE_MODEL_VALUE); + CHECK_HR(hr, "Failed to set WPD_DEVICE_MODEL"); + } + + else if (IsEqualPropertyKey(Key, WPD_DEVICE_SERIAL_NUMBER)) + { + hr = pValues->SetStringValue(WPD_DEVICE_SERIAL_NUMBER, DEVICE_SERIAL_NUMBER_VALUE); + CHECK_HR(hr, "Failed to set WPD_DEVICE_SERIAL_NUMBER"); + } + + else if (IsEqualPropertyKey(Key, WPD_DEVICE_SUPPORTS_NON_CONSUMABLE)) + { + hr = pValues->SetBoolValue(WPD_DEVICE_SUPPORTS_NON_CONSUMABLE, DEVICE_SUPPORTS_NONCONSUMABLE_VALUE); + CHECK_HR(hr, "Failed to set WPD_DEVICE_SUPPORTS_NON_CONSUMABLE"); + } + + else if (IsEqualPropertyKey(Key, WPD_DEVICE_MANUFACTURER)) + { + hr = pValues->SetStringValue(WPD_DEVICE_MANUFACTURER, DEVICE_MANUFACTURER_VALUE); + CHECK_HR(hr, "Failed to set WPD_DEVICE_MANUFACTURER"); + } + + else if (IsEqualPropertyKey(Key, WPD_DEVICE_FRIENDLY_NAME)) + { + hr = pValues->SetStringValue(WPD_DEVICE_FRIENDLY_NAME, DEVICE_FRIENDLY_NAME_VALUE); + CHECK_HR(hr, "Failed to set WPD_DEVICE_FRIENDLY_NAME"); + } + + else if (IsEqualPropertyKey(Key, WPD_DEVICE_TYPE)) + { + hr = pValues->SetUnsignedIntegerValue(WPD_DEVICE_TYPE, WPD_DEVICE_TYPE_GENERIC); + CHECK_HR(hr, "Failed to set WPD_DEVICE_TYPE"); + } + + // Set general properties for DEVICE + else if (IsEqualPropertyKey(Key, WPD_OBJECT_ID)) + { + hr = pValues->SetStringValue(WPD_OBJECT_ID, WPD_DEVICE_OBJECT_ID); + CHECK_HR(hr, "Failed to set WPD_OBJECT_ID"); + } + + else if (IsEqualPropertyKey(Key, WPD_OBJECT_NAME)) + { + // Retrieves the "DEVICE" string that identifies the root device + hr = pValues->SetStringValue(WPD_OBJECT_NAME, WPD_DEVICE_OBJECT_ID); + CHECK_HR(hr, "Failed to set WPD_OBJECT_NAME"); + } + + else if (IsEqualPropertyKey(Key, WPD_OBJECT_PERSISTENT_UNIQUE_ID)) + { + hr = pValues->SetStringValue(WPD_OBJECT_PERSISTENT_UNIQUE_ID, WPD_DEVICE_OBJECT_ID); + CHECK_HR(hr, "Failed to set WPD_OBJECT_PERSISTENT_UNIQUE_ID"); + } + + else if (IsEqualPropertyKey(Key, WPD_OBJECT_PARENT_ID)) + { + hr = pValues->SetStringValue(WPD_OBJECT_PARENT_ID, L""); + CHECK_HR(hr, "Failed to set WPD_OBJECT_PARENT_ID"); + } + + else if (IsEqualPropertyKey(Key, WPD_OBJECT_FORMAT)) + { + hr = pValues->SetGuidValue(WPD_OBJECT_FORMAT, WPD_OBJECT_FORMAT_UNSPECIFIED); + CHECK_HR(hr, "Failed to set WPD_OBJECT_FORMAT"); + } + + else if (IsEqualPropertyKey(Key, WPD_OBJECT_CONTENT_TYPE)) + { + hr = pValues->SetGuidValue(WPD_OBJECT_CONTENT_TYPE, WPD_CONTENT_TYPE_FUNCTIONAL_OBJECT); + CHECK_HR(hr, "Failed to set WPD_OBJECT_CONTENT_TYPE"); + } + + else if (IsEqualPropertyKey(Key, WPD_OBJECT_CAN_DELETE)) + { + hr = pValues->SetBoolValue(WPD_OBJECT_CAN_DELETE, FALSE); + CHECK_HR(hr, "Failed to set WPD_OBJECT_CAN_DELETE"); + } + + else if (IsEqualPropertyKey(Key, WPD_FUNCTIONAL_OBJECT_CATEGORY)) + { + hr = pValues->SetGuidValue(WPD_FUNCTIONAL_OBJECT_CATEGORY, WPD_FUNCTIONAL_CATEGORY_DEVICE); + CHECK_HR(hr, "Failed to set WPD_FUNCTIONAL_OBJECT_CATEGORY"); + } + } + } + } + // Retrieve the temperature sensor properties + else if ( + (strObjectID.CompareNoCase(SENSOR_OBJECT_ID) == 0) || + (strObjectID.CompareNoCase(TEMP_SENSOR_OBJECT_ID) == 0) || + (strObjectID.CompareNoCase(FLEX_SENSOR_OBJECT_ID) == 0) || + (strObjectID.CompareNoCase(PIR_SENSOR_OBJECT_ID) == 0) || + (strObjectID.CompareNoCase(PING_SENSOR_OBJECT_ID) == 0) || + (strObjectID.CompareNoCase(QTI_SENSOR_OBJECT_ID) == 0) || + (strObjectID.CompareNoCase(MEMSIC_SENSOR_OBJECT_ID) == 0) || + (strObjectID.CompareNoCase(HITACHI_SENSOR_OBJECT_ID) == 0) || + (strObjectID.CompareNoCase(PIEZO_SENSOR_OBJECT_ID) == 0) || + (strObjectID.CompareNoCase(COMPASS_SENSOR_OBJECT_ID) == 0) + ) + { + for (DWORD dwIndex = 0; dwIndex < cKeys; dwIndex++) + { + PROPERTYKEY Key = WPD_PROPERTY_NULL; + hr = pKeys->GetAt(dwIndex, &Key); + CHECK_HR(hr, "Failed to get PROPERTYKEY at index %d in collection", dwIndex); + + if (hr == S_OK) + { + // Preset the property value to 'error not supported'. The actual value + // will replace this value, if read from the device. + pValues->SetErrorValue(Key, HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED)); + if (IsEqualPropertyKey(Key, WPD_OBJECT_ID)) + { + hr = pValues->SetStringValue(WPD_OBJECT_ID, strObjectID); + CHECK_HR(hr, "Failed to set WPD_OBJECT_ID"); + } + + else if (IsEqualPropertyKey(Key, WPD_OBJECT_PERSISTENT_UNIQUE_ID)) + { + + // Retrieve the ID of the sensor using the m_SensorType member of the + // basedriver that is set during the data-read operation. + hr = pValues->SetStringValue(WPD_OBJECT_PERSISTENT_UNIQUE_ID, strObjectID); + CHECK_HR(hr, "Failed to set WPD_OBJECT_PERSISTENT_UNIQUE_ID"); + } + + else if (IsEqualPropertyKey(Key, WPD_OBJECT_PARENT_ID)) + { + hr = pValues->SetStringValue(WPD_OBJECT_PARENT_ID, WPD_DEVICE_OBJECT_ID); + CHECK_HR(hr, "Failed to set WPD_OBJECT_PARENT_ID"); + } + + else if (IsEqualPropertyKey(Key, WPD_OBJECT_NAME)) + { + // Retrieve the name of the sensor using the m_SensorType member of the + // basedriver that is set during the data-read operation. + if (m_pBaseDriver->m_SensorType == 0) + hr = pValues->SetStringValue(WPD_OBJECT_NAME, SENSOR_OBJECT_NAME_VALUE); + else if (m_pBaseDriver->m_SensorType == 2) + hr = pValues->SetStringValue(WPD_OBJECT_NAME, TEMP_SENSOR_OBJECT_NAME_VALUE); + else if (m_pBaseDriver->m_SensorType == 3) + hr = pValues->SetStringValue(WPD_OBJECT_NAME, FLEX_SENSOR_OBJECT_NAME_VALUE); + else if (m_pBaseDriver->m_SensorType == 4) + hr = pValues->SetStringValue(WPD_OBJECT_NAME, PING_SENSOR_OBJECT_NAME_VALUE); + else if (m_pBaseDriver->m_SensorType == 5) + hr = pValues->SetStringValue(WPD_OBJECT_NAME, PIR_SENSOR_OBJECT_NAME_VALUE); + else if (m_pBaseDriver->m_SensorType == 6) + hr = pValues->SetStringValue(WPD_OBJECT_NAME, MEMSIC_SENSOR_OBJECT_NAME_VALUE); + else if (m_pBaseDriver->m_SensorType == 7) + hr = pValues->SetStringValue(WPD_OBJECT_NAME, QTI_SENSOR_OBJECT_NAME_VALUE); + else if (m_pBaseDriver->m_SensorType == 8) + hr = pValues->SetStringValue(WPD_OBJECT_NAME, PIEZO_SENSOR_OBJECT_NAME_VALUE); + else if (m_pBaseDriver->m_SensorType == 9) + hr = pValues->SetStringValue(WPD_OBJECT_NAME, HITACHI_SENSOR_OBJECT_NAME_VALUE); + CHECK_HR(hr, "Failed to set WPD_OBJECT_NAME"); + } + + else if (IsEqualPropertyKey(Key, WPD_OBJECT_FORMAT)) + { + hr = pValues->SetGuidValue(WPD_OBJECT_FORMAT, WPD_OBJECT_FORMAT_UNSPECIFIED); + CHECK_HR(hr, "Failed to set WPD_OBJECT_FORMAT"); + } + + else if (IsEqualPropertyKey(Key, WPD_OBJECT_CONTENT_TYPE)) + { + hr = pValues->SetGuidValue(WPD_OBJECT_CONTENT_TYPE, WPD_CONTENT_TYPE_FUNCTIONAL_OBJECT); + CHECK_HR(hr, "Failed to set WPD_OBJECT_CONTENT_TYPE"); + } + + else if (IsEqualPropertyKey(Key, WPD_OBJECT_CAN_DELETE)) + { + hr = pValues->SetBoolValue(WPD_OBJECT_CAN_DELETE, FALSE); + CHECK_HR(hr, "Failed to set WPD_OBJECT_CAN_DELETE"); + } + + else if (IsEqualPropertyKey(Key, SENSOR_READING)) + { + hr = pValues->SetUnsignedLargeIntegerValue(SENSOR_READING, GetSensorReading()); + CHECK_HR(hr, "Failed to set SENSOR_READING"); + } + + else if (IsEqualPropertyKey(Key, SENSOR_UPDATE_INTERVAL)) + { + hr = pValues->SetUnsignedLargeIntegerValue(SENSOR_UPDATE_INTERVAL, GetUpdateInterval()); + CHECK_HR(hr, "Failed to set SENSOR_UPDATE_INTERVAL"); + } + else if (IsEqualPropertyKey(Key, WPD_FUNCTIONAL_OBJECT_CATEGORY)) + { + hr = pValues->SetGuidValue(WPD_FUNCTIONAL_OBJECT_CATEGORY, FUNCTIONAL_CATEGORY_SENSOR_SAMPLE); + CHECK_HR(hr, "Failed to set WPD_FUNCTIONAL_OBJECT_CATEGORY"); + } + } + } // end for + } // end else if + } + + return hr; +} + +/** + * This method is called to populate property attributes for the object and property specified. + * + * The parameters sent to us are: + * wszObjectID - the object whose property attributes are being requested. + * Key - the property whose attributes are being requested + * pAttributes - an IPortableDeviceValues which will contain the resulting property attributes + * + * The driver should: + * Read the property attributes for the specified property on the specified object and + * populate pAttributes with the results. + */ +HRESULT WpdObjectProperties::GetPropertyAttributesForObject( + _In_ LPCWSTR wszObjectID, + _In_ REFPROPERTYKEY Key, + _In_ IPortableDeviceValues* pAttributes) +{ + HRESULT hr = S_OK; + + if ((wszObjectID == NULL) || + (pAttributes == NULL)) + { + hr = E_INVALIDARG; + return hr; + } + + // + // Since ALL of our properties have the same attributes, we are ignoring the + // passed in wszObjectID parameter. This parameter allows you to + // customize attributes for properties on specific objects. (i.e. WPD_OBJECT_ORIGINAL_FILE_NAME + // may be READ/WRITE on some objects and READONLY on others. ) + // + + if (hr == S_OK) + { + hr = pAttributes->SetBoolValue(WPD_PROPERTY_ATTRIBUTE_CAN_DELETE, FALSE); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_ATTRIBUTE_CAN_DELETE"); + } + + if (hr == S_OK) + { + hr = pAttributes->SetBoolValue(WPD_PROPERTY_ATTRIBUTE_CAN_READ, TRUE); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_ATTRIBUTE_CAN_READ"); + } + + if (hr == S_OK) + { + // Allow writes for the update interval property + if (IsEqualPropertyKey(Key, SENSOR_UPDATE_INTERVAL)) + { + hr = pAttributes->SetBoolValue(WPD_PROPERTY_ATTRIBUTE_CAN_WRITE, TRUE); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_ATTRIBUTE_CAN_WRITE for SENSOR_UPDATE_INTERVAL"); + } + else + { + hr = pAttributes->SetBoolValue(WPD_PROPERTY_ATTRIBUTE_CAN_WRITE, FALSE); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_ATTRIBUTE_CAN_WRITE"); + } + } + + if (hr == S_OK) + { + hr = pAttributes->SetBoolValue(WPD_PROPERTY_ATTRIBUTE_FAST_PROPERTY, TRUE); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_ATTRIBUTE_FAST_PROPERTY"); + } + + if (hr == S_OK) + { + if (IsEqualPropertyKey(Key, SENSOR_UPDATE_INTERVAL)) + { + // Form range attributes for the update interval property + hr = pAttributes->SetUnsignedIntegerValue(WPD_PROPERTY_ATTRIBUTE_FORM, WPD_PROPERTY_ATTRIBUTE_FORM_RANGE); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_ATTRIBUTE_RANGE_MIN for SENSOR_UPDATE_INTERVAL"); + + hr = pAttributes->SetUnsignedIntegerValue(WPD_PROPERTY_ATTRIBUTE_RANGE_MIN, SENSOR_UPDATE_INTERVAL_MIN); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_ATTRIBUTE_RANGE_MIN for SENSOR_UPDATE_INTERVAL"); + + hr = pAttributes->SetUnsignedIntegerValue(WPD_PROPERTY_ATTRIBUTE_RANGE_MAX, SENSOR_UPDATE_INTERVAL_MAX); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_ATTRIBUTE_RANGE_MAX for SENSOR_UPDATE_INTERVAL"); + + hr = pAttributes->SetUnsignedIntegerValue(WPD_PROPERTY_ATTRIBUTE_RANGE_STEP, SENSOR_UPDATE_INTERVAL_STEP); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_ATTRIBUTE_RANGE_STEP for SENSOR_UPDATE_INTERVAL"); + } + else if (IsEqualPropertyKey(Key, SENSOR_READING)) + { + // Form range attributes for the reading property + hr = pAttributes->SetUnsignedIntegerValue(WPD_PROPERTY_ATTRIBUTE_FORM, WPD_PROPERTY_ATTRIBUTE_FORM_RANGE); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_ATTRIBUTE_RANGE_MIN for SENSOR_READING"); + + hr = pAttributes->SetUnsignedIntegerValue(WPD_PROPERTY_ATTRIBUTE_RANGE_MIN, SENSOR_READING_MIN); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_ATTRIBUTE_RANGE_MIN for SENSOR_READING"); + + hr = pAttributes->SetUnsignedIntegerValue(WPD_PROPERTY_ATTRIBUTE_RANGE_MAX, SENSOR_READING_MAX); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_ATTRIBUTE_RANGE_MAX for SENSOR_READING"); + + hr = pAttributes->SetUnsignedIntegerValue(WPD_PROPERTY_ATTRIBUTE_RANGE_STEP, SENSOR_READING_STEP); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_ATTRIBUTE_RANGE_STEP for SENSOR_READING"); + } + else + { + hr = pAttributes->SetUnsignedIntegerValue(WPD_PROPERTY_ATTRIBUTE_FORM, WPD_PROPERTY_ATTRIBUTE_FORM_UNSPECIFIED); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_ATTRIBUTE_FORM"); + } + } + + return hr; +} + + +/** + * This method is called to update the sensor reading + * + * The parameters sent to us are: + * dwNewReading - the temperature reading to set + * + * The driver should: + * Update the sensor reading. + */ +VOID WpdObjectProperties::SetSensorReading(LONGLONG llNewReading) +{ + // Ensure that this value isn't currently being accessed by another thread + CComCritSecLock Lock(m_SensorReadingCriticalSection); + + m_llSensorReading = llNewReading; +} + + +/** + * This method is called to retrieve the sensor reading + * + * The parameters sent to us are: + * + * The driver should: + * Return the saved sensor reading. + */ +LONGLONG WpdObjectProperties::GetSensorReading() +{ + // Ensure that this value isn't currently being accessed by another thread + CComCritSecLock Lock(m_SensorReadingCriticalSection); + + return m_llSensorReading; +} + + +/** + * This method is called to set the cached sensor interval + * + * The parameters sent to us are: + * dwNewInterval - the sensor update interval to set + * + * The driver should: + * Update the cached sensor interval property. + */ +VOID WpdObjectProperties::SetUpdateInterval(DWORD dwNewInterval) +{ + m_dwUpdateInterval = dwNewInterval; +} + + +/** + * This method is called to retrieve the sensor update interval + * + * The parameters sent to us are: + * + * The driver should: + * Return the interval property. + */ +DWORD WpdObjectProperties::GetUpdateInterval() +{ + return m_dwUpdateInterval; +} + + +/** + * This method is called to update the sensor interval on the device + * + * The parameters sent to us are: + * dwNewInterval - the sensor update interval to set + * + * The driver should: + * Update the cached sensor property and send a write request to the device + */ +HRESULT WpdObjectProperties::SendUpdateIntervalToDevice(DWORD dwNewInterval) +{ + HRESULT hr = S_OK; + RS232Target* pDeviceTarget = NULL; + + CHAR szInterval[INTERVAL_DATA_LENGTH+1] = {0}; + + // Check the input value + if (IsValidUpdateInterval(dwNewInterval) == FALSE) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + CHECK_HR(hr, "Invalid update interval: %d", dwNewInterval); + } + + // Format a write request with the input value + if (hr == S_OK) + { + hr = StringCchPrintfA(szInterval, ARRAYSIZE(szInterval), "%u", dwNewInterval); + CHECK_HR(hr, "Failed to convert the new interval to a CHAR string"); + } + + // Send the write request to the device + if (hr == S_OK) + { + pDeviceTarget = m_pBaseDriver->GetRS232Target(); + + if (pDeviceTarget->IsReady()) + { + hr = pDeviceTarget->SendWriteRequest((BYTE *)szInterval, sizeof(szInterval)); + CHECK_HR(hr, "Failed to send the write request to set the new sensor update interval"); + + if (hr == S_OK) + { + TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_FLAG_DRIVER, "%!FUNC! Sent new interval: %s", szInterval); + } + } + else + { + hr = HRESULT_FROM_WIN32(ERROR_NOT_READY); + CHECK_HR(hr, "Device is not ready to receive write requests"); + } + } + + if (hr == S_OK) + { + // Update the cached value on the driver + SetUpdateInterval(dwNewInterval); + } + + return hr; +} + + +/** + * This method is called to check that interval value falls within the accepted range + * of 2000 to 60000 milliseconds + * + * The parameters sent to us are: + * wszInterval - the sensor update interval to check + * + * The driver should: + * Return TRUE if the interval is within range + */ +BOOL WpdObjectProperties::IsValidUpdateInterval(DWORD dwInterval) +{ + if ((dwInterval >= SENSOR_UPDATE_INTERVAL_MIN) && + (dwInterval <= SENSOR_UPDATE_INTERVAL_MAX)) + { + return TRUE; + } + + return FALSE; +} + diff --git a/wpd/WpdBasicHardwareDriver/WpdObjectProperties.h b/wpd/WpdBasicHardwareDriver/WpdObjectProperties.h new file mode 100644 index 000000000..1860e0ad5 --- /dev/null +++ b/wpd/WpdBasicHardwareDriver/WpdObjectProperties.h @@ -0,0 +1,111 @@ +#pragma once + +#define DEVICE_PROTOCOL_VALUE L"Sensor Protocol ver 1.00" +#define DEVICE_FIRMWARE_VERSION_VALUE L"1.0.0.0" +#define DEVICE_POWER_LEVEL_VALUE 100 +#define DEVICE_MODEL_VALUE L"RS232 Sensor" +#define DEVICE_FRIENDLY_NAME_VALUE L"Parallax BS2 Sensor" +#define DEVICE_MANUFACTURER_VALUE L"Windows Portable Devices Group" +#define DEVICE_SERIAL_NUMBER_VALUE L"01234567890123-45676890123456" +#define DEVICE_SUPPORTS_NONCONSUMABLE_VALUE FALSE + +#define SENSOR_OBJECT_ID L"Sensor" +#define SENSOR_OBJECT_NAME_VALUE L"Parallax Sensor" +#define COMPASS_SENSOR_OBJECT_ID L"Compass" +#define COMPASS_SENSOR_OBJECT_NAME_VALUE L"HM55B Compass Sensor" +#define PIR_SENSOR_OBJECT_ID L"PIR" +#define PIR_SENSOR_OBJECT_NAME_VALUE L"Passive Infra-Red Sensor" +#define QTI_SENSOR_OBJECT_ID L"QTI" +#define QTI_SENSOR_OBJECT_NAME_VALUE L"QTI Light Sensor" +#define FLEX_SENSOR_OBJECT_ID L"Flex" +#define FLEX_SENSOR_OBJECT_NAME_VALUE L"Flex Force Sensor" +#define PING_SENSOR_OBJECT_ID L"Ping" +#define PING_SENSOR_OBJECT_NAME_VALUE L"Ultrasonic Distance Sensor" +#define PIEZO_SENSOR_OBJECT_ID L"Piezo" +#define PIEZO_SENSOR_OBJECT_NAME_VALUE L"Piezo Vibration Sensor" +#define TEMP_SENSOR_OBJECT_ID L"TempHumidity" +#define TEMP_SENSOR_OBJECT_NAME_VALUE L"Sensiron Temperature and Humidity Sensor" +#define MEMSIC_SENSOR_OBJECT_ID L"Memsic" +#define MEMSIC_SENSOR_OBJECT_NAME_VALUE L"Memsic Dual-Axis G-Force Sensor" +#define HITACHI_SENSOR_OBJECT_ID L"Hitachi" +#define HITACHI_SENSOR_OBJECT_NAME_VALUE L"Hitachi Tri-Axis G-Force Sensor" +// INSERT ID and NAME definitions for other sensors here!! + + +GUID GetObjectFormat(CAtlStringW strObjectID); +GUID GetObjectContentType(CAtlStringW strObjectID); +HRESULT AddSupportedPropertyKeys(_In_ LPCWSTR wszObjectID, + _In_ IPortableDeviceKeyCollection* pKeys); + +VOID AddCommonPropertyKeys(_In_ IPortableDeviceKeyCollection* pKeys); +VOID AddDevicePropertyKeys(_In_ IPortableDeviceKeyCollection* pKeys); + +VOID AddSensorPropertyKeys(_In_ IPortableDeviceKeyCollection* pKeys); //Required for sensor props + +class WpdObjectProperties +{ +public: + + WpdObjectProperties(); + virtual ~WpdObjectProperties(); + + HRESULT Initialize(_In_ WpdBaseDriver* pBaseDriver); + + HRESULT DispatchWpdMessage(_In_ REFPROPERTYKEY Command, + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnGetSupportedProperties(_In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnGetPropertyValues(_In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnGetAllPropertyValues(_In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnSetPropertyValues(_In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnGetPropertyAttributes(_In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnDeleteProperties(_In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + VOID SetSensorReading(LONGLONG llNewReading); + + LONGLONG GetSensorReading(); + + VOID SetUpdateInterval(DWORD dwNewInterval); + + DWORD GetUpdateInterval(); + + HRESULT SendUpdateIntervalToDevice(DWORD dwNewInterval); + + BOOL IsValidUpdateInterval(DWORD dwInterval); + +private: + + HRESULT GetPropertyValuesForObject(_In_ LPCWSTR wszObjectID, + _In_ IPortableDeviceKeyCollection* pKeys, + _In_ IPortableDeviceValues* pValues); + + HRESULT GetPropertyAttributesForObject(_In_ LPCWSTR wszObjectID, + _In_ REFPROPERTYKEY Key, + _In_ IPortableDeviceValues* pAttributes); + + +private: + WpdBaseDriver* m_pBaseDriver; + + // This critical section protects the sensor reading from concurrent access + // by the application (which reads it) and the read request callback (which updates it) + CComAutoCriticalSection m_SensorReadingCriticalSection; + LONGLONG m_llSensorReading; + + // A critical section is not needed for the update interval because it is accessed + // by the client application, and set/get requests from the application arrive serially + // through the sequential WDF queue. + DWORD m_dwUpdateInterval; +}; diff --git a/wpd/WpdBasicHardwareDriver/firmware/compass_wpd_enabled.bs2 b/wpd/WpdBasicHardwareDriver/firmware/compass_wpd_enabled.bs2 new file mode 100644 index 000000000..335d2c7e3 --- /dev/null +++ b/wpd/WpdBasicHardwareDriver/firmware/compass_wpd_enabled.bs2 @@ -0,0 +1,94 @@ +' Compass_wpd_enabled.bs2 +' +' Displays x (N/S) and y (W/E) axis measurements along with the direction the +' Compass Module is pointing, measured in degrees clockwise from north. +' +' 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 +' +' {$STAMP BS2} +' {$PBASIC 2.5} +' ============================================================================ + +' -----[ Pins/Constants/Variables ]------------------------------------------- +DinDout PIN 6 ' P6 transceives to/from Din/Dout +Clk PIN 5 ' P5 sends pulses to HM55B's Clk +En PIN 4 ' P4 controls HM55B's /EN(ABLE) + +Reset CON %0000 ' Reset command for HM55B +Measure CON %1000 ' Start measurement command +Report CON %1100 ' Get status/axis values command +Ready CON %1100 ' 11 -> Done, 00 -> no errors +NegMask CON %1111100000000000 ' For 11-bit negative to 16-bits + +x VAR Word ' x-axis data +y VAR Word ' y-axis data +status VAR Nib ' Status flags +angle VAR Word ' Store angle measurement + +SensorID VAR Byte 'Sensor identifier = 5 for PIR +ElementSize VAR Byte 'Size (in bytes) of each element +ElementCount VAR Byte 'Count of elements in packet +Padding VAR Byte 'Padding for the 8-byte element + +SensorID = 1 +ElementSize = 1 +ElementCount = 3 '3-bytes for compass data; + +NewInterval VAR Word 'New interval requested by user +Interval VAR Word 'Interval value utlized by firmware + +LFD CON $10 'Linefeed character + +Interval = 200 '.20 of a second interval +NewInterval = 200 + +' -----[ Main Routine ]------------------------------------------------------- + +Main: + GOSUB PollSensor 'Was motion detected? + GOSUB RetrieveInterval 'Retrieve units data + +' -----[ Subroutines ]-------------------------------------------------------- + +Timeout: + SEROUT 16, 16468, [DEC1 SensorID, DEC1 ElementSize, DEC1 ElementCount, DEC3 angle, DEC5 Interval,LFD] + GOTO Main + +PollSensor: ' Compass module subroutine + + HIGH En: LOW En ' Send reset command to HM55B + SHIFTOUT DinDout,clk,MSBFIRST,[Reset\4] + + HIGH En: LOW En ' HM55B start measurement command + SHIFTOUT DinDout,clk,MSBFIRST,[Measure\4] + status = 0 ' Clear previous status flags + + DO ' Status flag checking loop + HIGH En: LOW En ' Measurement status command + SHIFTOUT DinDout,clk,MSBFIRST,[Report\4] + SHIFTIN DinDout,clk,MSBPOST,[Status\4] ' Get Status + LOOP UNTIL status = Ready ' Exit loop when status is ready + + SHIFTIN DinDout,clk,MSBPOST,[x\11,y\11] ' Get x & y axis values + HIGH En ' Disable module + + IF (y.BIT10 = 1) THEN y = y | NegMask ' Store 11-bits as signed word + IF (x.BIT10 = 1) THEN x = x | NegMask ' Repeat for other axis + + angle = x ATN -y ' Convert x and y to brads + angle = angle */ 360 ' Convert brads to degrees + + RETURN + + +RetrieveInterval: + SERIN 16, 16468, Interval, Timeout, [DEC NewInterval] 'Retrieve interval + IF NewInterval >= 10 AND NewInterval <= 60000 THEN + Interval = NewInterval + ENDIF + RETURN \ No newline at end of file diff --git a/wpd/WpdBasicHardwareDriver/firmware/flex_force_wpd_enabled.bs2 b/wpd/WpdBasicHardwareDriver/firmware/flex_force_wpd_enabled.bs2 new file mode 100644 index 000000000..9b9e5b2cd --- /dev/null +++ b/wpd/WpdBasicHardwareDriver/firmware/flex_force_wpd_enabled.bs2 @@ -0,0 +1,60 @@ +' Flex_force_wpd_enabled.bs2 +' +' Displays R/C Discharge Time in BASIC Stamp DEBUG Window +' +' 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 +' +' {$STAMP BS2} +' {$PBASIC 2.5} +' ========================================================================= + +' -----[ Declarations ]---------------------------------------------------- + +rawForce VAR Word ' Stores raw output +sensorPin CON 15 ' Flexiforce sensor circuit + +' -----[ Main Routine ]---------------------------------------------------- + +SensorID VAR Byte 'Sensor identifier = 5 for PIR +ElementSize VAR Byte 'Size (in bytes) of each element +ElementCount VAR Byte 'Count of elements in packet +Padding VAR Byte 'Padding for the 8-byte element + +SensorID = 3 +ElementSize = 1 +ElementCount = 5 '4-bytes for pressure data; 5 for interval + +NewInterval VAR Word 'New interval requested by user +Interval VAR Word 'Interval value utlized by firmware + +Interval = 200 +NewInterval = 200 + +LFD CON $10 'Linefeed character + +Main: + + GOSUB PollSensor 'Was motion detected? + GOSUB RetrieveInterval 'Retrieve units data + +Timeout: + SEROUT 16, 16468, [DEC1 SensorID, DEC1 ElementSize, DEC1 ElementCount, DEC5 rawForce, DEC5 Interval, LFD] + GOTO Main + +PollSensor: + HIGH sensorPin ' Discharge the capacitor + PAUSE 2 + RCTIME sensorPin,1,rawForce ' Measure RC charge time + RETURN + +RetrieveInterval: + SERIN 16, 16468, Interval, Timeout, [DEC NewInterval] 'Retrieve interval + IF NewInterval >= 10 AND NewInterval <= 60000 THEN + Interval = NewInterval + ENDIF + RETURN diff --git a/wpd/WpdBasicHardwareDriver/firmware/h48c_3-axis_wpd_enabled.bs2 b/wpd/WpdBasicHardwareDriver/firmware/h48c_3-axis_wpd_enabled.bs2 new file mode 100644 index 000000000..daba0fc97 --- /dev/null +++ b/wpd/WpdBasicHardwareDriver/firmware/h48c_3-axis_wpd_enabled.bs2 @@ -0,0 +1,175 @@ +' h48c_3-axis_wpd_enabled.bs2 +' +' 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 +' +' {$STAMP BS2} +' {$PBASIC 2.5} +' +' ========================================================================= + +' -----[ Program Description ]--------------------------------------------- +' +' Test program for the H48C 3-Axis Accelerometer module. +' +' Connections: +' +' +------------+ +' | * | +' CLK | o o | Vdd (+5v) +' | +--+ | +' DIO [ o | | o | CS\ +' | +--+ | +' Vss | o o | 0G (free-fall indication) +' | | +' +------------+ +' +' How it Works: +' +' An onboard MCP3204 12-bit ADC is used to read the VRef, X-, Y-, and +' Z-axis outputs from the Hitachi H48C accelerometer. The reference +' voltage output from H48C is 1.65 volts (3.3 / 2). +' +' After reading the reference voltage and an output channel the g-force +' for the channel is calculated with this formula: +' +' axis - vref 3.3 +' G = ----------- x ------ +' 4095 0.3663 +' +' For use in the program the forumla can be simplified to: +' +' G = (axis - vref) x 0.0022 +' +' To allow the display of fractional g-force in the integer system of the +' BASIC Stamp we multiply 0.0022 by 100 -- this will allow us to display +' g-force in 0.01g units. + + + +' -----[ I/O Definitions ]------------------------------------------------- + +Dio PIN 15 ' data to/from module +Clk PIN 14 ' clock output +CS PIN 13 ' active-low chip select + + +' -----[ Constants ]------------------------------------------------------- + +XAxis CON 0 ' adc channels +YAxis CON 1 +ZAxis CON 2 +VRef CON 3 + +Cnt2Mv CON $CE4C ' counts to millivolts + ' 0.80586 with ** +GfCnv CON $3852 ' g-force conversion + ' 0.22 with ** + +' -----[ Variables ]------------------------------------------------------- + +axis VAR Nib ' axis selection +rvCount VAR Word ' ref voltage adc counts +axCount VAR Word ' axis voltage adc counts +mVolts VAR Word ' millivolts +Gforce VAR Word ' axis g-force + +xGforce VAR Word ' x-axis force +yGforce VAR Word ' y-axis force +zGforce VAR Word ' z-axis force + +dValue VAR Word ' display value +dPad VAR Nib ' display pad + +' Below lines are WPD additions + +SensorID VAR Byte 'Sensor identifier = 9 for Hitachi +ElementCount VAR Byte 'Count of elements in packet +ElementSize VAR Byte 'Size (in bytes) of each element + + +NewInterval VAR Word 'New interval requested by user +Interval VAR Word 'Interval value utlized by firmware + +SensorID = 9 +ElementSize = 4 'Each element contains a sign byte, followed by a G-force integer value, followed by a G-force fraction (in hundredths). +ElementCount = 3 'Each element corresponds to one of the three axis (X, Y, and Z) + +Interval = 2000 +NewInterval = 2000 + + +' -----[ EEPROM Data ]----------------------------------------------------- + + +' -----[ Initialization ]-------------------------------------------------- + +Reset: + HIGH CS ' deselect module + +' -----[ Program Code ]---------------------------------------------------- + +Main: + GOSUB GetGforces 'Retrieves G-forces along 3 axis + GOSUB RetrieveInterval 'Retrieves event-interval data + + Timeout: + SEROUT 16, 16780, [DEC1 SensorID, DEC1 ElementSize, DEC1 ElementCount, DEC1(xGforce.BIT15), DEC1(ABS xGforce/100),DEC2(ABS xGforce),DEC1(yGforce.BIT15),DEC1(ABS yGforce/100),DEC2(ABS yGforce),DEC1(zGforce.BIT15),DEC1(ABS zGforce/100),DEC2(ABS zGforce),DEC5 Interval ] + GOTO Main + + GOTO Main + + +' -----[ Subroutines ]----------------------------------------------------- + +' Retrieves the event-interval property from the WPD driver +RetrieveInterval: + SERIN 16, 16780, Interval, Timeout, [DEC NewInterval] 'Retrieve interval + IF NewInterval >= 10 AND NewInterval <= 60000 THEN + Interval = NewInterval + ENDIF + +' Retrieves G forces along all three axis +GetGforces: + FOR axis = XAxis TO ZAxis ' loop through each axis + GOSUB Get_H48C ' read vRef & axis counts + ' calculate g-force + ' -- "Gforce" is signed word + IF (axCount >= rvCount) THEN + Gforce = (axCount - rvCount) ** GfCnv ' positive g-force + ELSE + Gforce = -((rvCount - axCount) ** GfCnv) ' negative g-force + ENDIF + IF (axis = XAxis) THEN + xGforce = Gforce + ENDIF + IF (axis = YAxis) THEN + yGforce = Gforce + ENDIF + IF (axis = ZAxis) THEN + zGforce = Gforce + ENDIF + NEXT + RETURN + +' Reads VRef and selected H48C axis through an MCP3204 ADC +' -- pass axis (0 - 2) in "axis" +' -- returns reference voltage counts in "rvCount" +' -- returns axis voltage counts in "axCounts" + +Get_H48C: + LOW CS + SHIFTOUT Dio, Clk, MSBFIRST, [%11\2, VRef\3] ' select vref register + SHIFTIN Dio, Clk, MSBPOST, [rvCount\13] ' read ref voltage counts + HIGH CS + PAUSE 1 + LOW CS + SHIFTOUT Dio, Clk, MSBFIRST, [%11\2, axis\3] ' select axis + SHIFTIN Dio, Clk, MSBPOST, [axCount\13] ' read axis voltage counts + HIGH CS + RETURN + diff --git a/wpd/WpdBasicHardwareDriver/firmware/memsic2125_wpd_enabled.bs2 b/wpd/WpdBasicHardwareDriver/firmware/memsic2125_wpd_enabled.bs2 new file mode 100644 index 000000000..804493570 --- /dev/null +++ b/wpd/WpdBasicHardwareDriver/firmware/memsic2125_wpd_enabled.bs2 @@ -0,0 +1,107 @@ +' memsic2125_wpd_enabled.bs2 +' +' 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 +' +' {$STAMP BS2} +' {$PBASIC 2.5} +' +' ========================================================================= + +' -----[ Program Description ]--------------------------------------------- +' +' Read the pulse outputs from a Memsic 2125 accelerometer and converts to +' G-force +' +' g = ((t1 / 10 ms) - 0.5) / 12.5% +' +' www.memsic.com + + +' -----[ Revision History ]------------------------------------------------ + + +' -----[ I/O Definitions ]------------------------------------------------- + +Xin PIN 8 ' X input from Memsic 2125 +Yin PIN 9 ' Y input from Memsic 2125 + + +' -----[ Constants ]------------------------------------------------------- + +' Set scale factor for PULSIN + +#SELECT $STAMP + #CASE BS2, BS2E + Scale CON $200 ' 2.0 us per unit + #CASE BS2SX + Scale CON $0CC ' 0.8 us per unit + #CASE BS2P + Scale CON $0C0 ' 0.75 us per unit + #CASE BS2PE + Scale CON $1E1 ' 1.88 us per unit +#ENDSELECT + +HiPulse CON 1 ' measure high-going pulse +LoPulse CON 0 + +' -----[ Variables ]------------------------------------------------------- + +xRaw VAR Word ' pulse from Memsic 2125 +xmG VAR Word ' g force (1000ths) + +yRaw VAR Word +ymG VAR Word + +'disp VAR Byte ' displacement (0.0 - 0.99) + + +' Below lines are WPD additions + +SensorID VAR Byte 'Sensor identifier = 1 for memsic +ElementSize VAR Byte 'Size (in bytes) of each element +ElementCount VAR Byte 'Count of elements in packet + +NewInterval VAR Word 'New interval requested by user +Interval VAR Word 'Interval value utlized by firmware + +SensorID = 6 +ElementSize = 1 +ElementCount = 6 '6-bytes for g-force + +LFD CON $10 'Linefeed character + +Interval = 100 '.10 of a second interval +NewInterval = 100 + +' -----[ Program Code ]---------------------------------------------------- + +Main: + GOSUB Read_G_Force 'reads G-force + GOSUB RetrieveInterval 'Retrieve units data + + Timeout: + SEROUT 16, 16468, [DEC1 SensorID, DEC1 ElementSize, DEC1 ElementCount, DEC1(xmG.BIT15), DEC1(ABS xmG/1000),DEC1(ABS xmG/10),DEC1(ymG.BIT15),DEC1(ABS ymG/1000),DEC1(ABS ymG/10),DEC5 Interval,LFD] + GOTO Main + + + RetrieveInterval: + SERIN 16, 16468, Interval, Timeout, [DEC NewInterval] 'Retrieve interval + IF NewInterval >= 10 AND NewInterval <= 60000 THEN + Interval = NewInterval + ENDIF + +' -----[ Subroutines ]----------------------------------------------------- + +Read_G_Force: + PULSIN Xin, HiPulse, xRaw ' read pulse output + xRaw = xRaw */ Scale ' convert to uSecs + xmG = ((xRaw / 10) - 500) * 8 ' calc 1/1000 g + PULSIN Yin, HiPulse, yRaw + yRaw = yRaw */ Scale + ymG = ((yRaw / 10) - 500) * 8 + RETURN \ No newline at end of file diff --git a/wpd/WpdBasicHardwareDriver/firmware/piezo_wpd_enabled.bs2 b/wpd/WpdBasicHardwareDriver/firmware/piezo_wpd_enabled.bs2 new file mode 100644 index 000000000..ff5a7b551 --- /dev/null +++ b/wpd/WpdBasicHardwareDriver/firmware/piezo_wpd_enabled.bs2 @@ -0,0 +1,55 @@ +' Piezo_wpd_enabled.bs2 +' +' This program demonstrates using the LDT0 as a switch/trigger +' +' 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 +' +' {$STAMP BS2} +' {$PBASIC 2.5} +' ========================================================================= + +SensorID VAR Byte 'Sensor identifier = 8 for piezo +ElementSize VAR Byte 'Size (in bytes) of each element +ElementCount VAR Byte 'Count of elements in packet +bVibration VAR Byte 'Vibration-detection variable (single element for PIR) +Padding VAR Byte 'Padding for the 8-byte element + +SensorID = 8 +ElementSize = 1 +ElementCount = 1 '1-byte for motion data; 5 for interval + +NewInterval VAR Word 'New interval requested by user +Interval VAR Word 'Interval value utlized by firmware + +Interval = 500 +NewInterval = 500 + +LFD CON $10 'Linefeed character + + +' -----[ Program Code ]---------------------------------------------------- + +Main: + + GOSUB PollSensor 'Was motion detected? + GOSUB RetrieveInterval 'Retrieve units data + +Timeout: + SEROUT 16, 16468, [DEC1 SensorID, DEC1 ElementSize, DEC1 ElementCount, DEC1 bVibration, DEC5 Interval, LFD] + GOTO Main + +PollSensor: + bVibration = IN0 ' Assign status of P0 to bMotion + RETURN + +RetrieveInterval: + SERIN 16, 16468, Interval, Timeout, [DEC NewInterval] 'Retrieve interval + IF NewInterval >= 10 AND NewInterval <= 60000 THEN + Interval = NewInterval + ENDIF + RETURN diff --git a/wpd/WpdBasicHardwareDriver/firmware/ping_wpd_enabled.bs2 b/wpd/WpdBasicHardwareDriver/firmware/ping_wpd_enabled.bs2 new file mode 100644 index 000000000..e2af29a77 --- /dev/null +++ b/wpd/WpdBasicHardwareDriver/firmware/ping_wpd_enabled.bs2 @@ -0,0 +1,57 @@ +' Ping_wpd_enabled.bs2 +' +' Measure distance with Ping))) sensor and display in both in & cm +' +' 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 +' +' {$STAMP BS2} +' {$PBASIC 2.5} +' ========================================================================= + +' Conversion constants for room temperature measurements. +CmConstant CON 2260 +cmDistance VAR Word +time VAR Word +SensorID VAR Byte 'Sensor identifier = 5 for PIR +ElementSize VAR Byte 'Size (in bytes) of each element +ElementCount VAR Byte 'Count of elements in packet +Padding VAR Byte 'Padding for the 8-byte element + +SensorID = 4 +ElementSize = 1 +ElementCount = 5 '4-bytes for pressure data; 5 for interval + +NewInterval VAR Word 'New interval requested by user +Interval VAR Word 'Interval value utlized by firmware + + +LFD CON $10 'Linefeed character + +Interval = 200 '.20 of a second interval +NewInterval = 200 + +Main: + GOSUB PollSensor 'Was motion detected? + GOSUB RetrieveInterval 'Retrieve units data + +Timeout: + SEROUT 16, 16468, [DEC1 SensorID, DEC1 ElementSize, DEC1 ElementCount, DEC5 cmDistance, DEC5 Interval,LFD] +GOTO Main + +PollSensor: + PULSOUT 0, 5 + PULSIN 0, 1, time + cmDistance = cmConstant ** time +RETURN + +RetrieveInterval: + SERIN 16, 16468, Interval, Timeout, [DEC NewInterval] 'Retrieve interval + IF NewInterval >= 10 AND NewInterval <= 60000 THEN + Interval = NewInterval + ENDIF +RETURN \ No newline at end of file diff --git a/wpd/WpdBasicHardwareDriver/firmware/pir_wpd_enabled.bs2 b/wpd/WpdBasicHardwareDriver/firmware/pir_wpd_enabled.bs2 new file mode 100644 index 000000000..259c5025b --- /dev/null +++ b/wpd/WpdBasicHardwareDriver/firmware/pir_wpd_enabled.bs2 @@ -0,0 +1,50 @@ +' Pir_wpd_enabled.bs2 +' +' 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 +' +' {$STAMP BS2} +' {$PBASIC 2.5} +' ========================================================================= + +SensorID VAR Byte 'Sensor identifier = 5 for PIR +ElementSize VAR Byte 'Size (in bytes) of each element +ElementCount VAR Byte 'Count of elements in packet +bMotion VAR Byte 'Motion-detection variable (single element for PIR) +Padding VAR Byte 'Padding for the 8-byte element + +SensorID = 5 +ElementSize = 1 +ElementCount = 1 '1-byte for motion data; 5 for interval + +NewInterval VAR Word 'New interval requested by user +Interval VAR Word 'Interval value utlized by firmware + +LFD CON $10 'Linefeed character + +Interval = 500 '.500 of a second interval +NewInterval = 500 + +Main: + + GOSUB PollSensor 'Was motion detected? + GOSUB RetrieveInterval 'Retrieve units data + +Timeout: + SEROUT 16, 16468, [DEC1 SensorID, DEC1 ElementSize, DEC1 ElementCount, DEC1 bMotion, DEC5 Interval,LFD] + GOTO Main + +PollSensor: + bMotion = IN0 ' Assign status of P0 to bMotion + RETURN + +RetrieveInterval: + SERIN 16, 16468, Interval, Timeout, [DEC NewInterval] 'Retrieve interval + IF NewInterval >= 10 AND NewInterval <= 60000 THEN + Interval = NewInterval + ENDIF + RETURN \ No newline at end of file diff --git a/wpd/WpdBasicHardwareDriver/firmware/qti_wpd_enabled.bs2 b/wpd/WpdBasicHardwareDriver/firmware/qti_wpd_enabled.bs2 new file mode 100644 index 000000000..fdfdff7c9 --- /dev/null +++ b/wpd/WpdBasicHardwareDriver/firmware/qti_wpd_enabled.bs2 @@ -0,0 +1,63 @@ +' Qti_wpd_enabled.bs2 +' +' 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 +' +' {$STAMP BS2} +' {$PBASIC 2.5} +' ========================================================================= + +' -----[ I/O Definitions ]------------------------------------------------------ +LineSnsrPwr CON 10 ' line sensor power +LineSnsrIn CON 9 ' line sensor input + +' -----[ Constants ]------------------------------------------------------------ +'CLREOL CON 11 ' clear to end of line (DEBUG) + +' -----[ Variables ]------------------------------------------------------------ +Sense VAR Word ' sensor raw reading + +SensorID VAR Byte 'Sensor identifier = 5 for PIR +ElementSize VAR Byte 'Size (in bytes) of each element +ElementCount VAR Byte 'Count of elements in packet +Padding VAR Byte 'Padding for the 8-byte element +NewInterval VAR Word 'New interval requested by user +Interval VAR Word 'Interval value utlized by firmware + +SensorID = 7 +ElementSize = 1 +ElementCount = 4 '4-bytes for sensor data +Interval = 200 +NewInterval = 200 + +LFD CON $10 'Linefeed character + +' -----[ Main Code ]------------------------------------------------------------ +Main: + + GOSUB PollSensor 'Retrieve sensor value + GOSUB RetrieveInterval 'Retrieve units data + +Timeout: + SEROUT 16, 16468, [DEC1 SensorID, DEC1 ElementSize, DEC1 ElementCount, DEC4 Sense, DEC5 Interval, LFD] + GOTO Main + +PollSensor: + HIGH LineSnsrPwr ' activate sensor + HIGH LineSnsrIn ' discharge QTI cap + PAUSE 1 + RCTIME LineSnsrIn, 1, Sense ' read sensor value + LOW LineSnsrPwr ' deactivate sensor +RETURN + +RetrieveInterval: + SERIN 16, 16468, Interval, Timeout, [DEC NewInterval] 'Retrieve interval + IF NewInterval >= 10 AND NewInterval <= 60000 THEN + Interval = NewInterval + ENDIF + RETURN + diff --git a/wpd/WpdBasicHardwareDriver/firmware/temp_humidity_wpd_enabled.bs2 b/wpd/WpdBasicHardwareDriver/firmware/temp_humidity_wpd_enabled.bs2 new file mode 100644 index 000000000..bc2982f7f --- /dev/null +++ b/wpd/WpdBasicHardwareDriver/firmware/temp_humidity_wpd_enabled.bs2 @@ -0,0 +1,225 @@ +' Temp_humidity_wpd_enabled.BS2 +' This program demonstrates the interface and conversion of SHT11/15 data +' to usable program values. This program uses advanced math features of +' PBASIC, specifically the ** operator. +' +' 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 +' +' {$STAMP BS2} +' {$PBASIC 2.5} + +' +' ========================================================================= +' ------------------------------------------------------------------------- +' I/O Definitions +' ------------------------------------------------------------------------- +ShtData PIN 1 ' bi-directional data +Clock PIN 0 +' ------------------------------------------------------------------------- +' Constants +' ------------------------------------------------------------------------- +ShtTemp CON %00011 ' read temperature +ShtHumi CON %00101 ' read humidity +ShtStatW CON %00110 ' status register write +ShtStatR CON %00111 ' status register read +ShtReset CON %11110 ' soft reset +Ack CON 0 +NoAck CON 1 +No CON 0 +Yes CON 1 +DegSym CON 186 ' degrees symbol for DEBUG +' ------------------------------------------------------------------------- +' Variables +' ------------------------------------------------------------------------- +ioByte VAR Byte ' data from/to SHT11 +ackBit VAR Bit ' ack/nak from/to SHT11 +toDelay VAR Byte ' timeout delay timer +timeOut VAR Bit ' timeout status +soT VAR Word ' temp counts from SHT11 +tC VAR Word ' temp - Celcius +tF VAR Word ' temp - Fahrenheit +soRH VAR Word ' humidity counts +rhLin VAR Word ' humidity; linearized +rhTrue VAR Word ' humidity; compensated +status VAR Byte ' status byte + +'------------------------------------------------------------------------- +' WPD Variables +'------------------------------------------------------------------------- +SensorID VAR Byte 'Sensor identifier = 2 for Sensiron Temp/Humidity +ElementSize VAR Byte 'Size (in bytes) of each element +ElementCount VAR Byte 'Count of elements in packet +Padding VAR Byte 'Padding for the 8-byte element + +SensorID = 2 +ElementSize = 1 +ElementCount = 7 '4-bytes for temp, 3-bytes for relative humidity + +NewInterval VAR Word 'New interval requested by user +Interval VAR Word 'Interval value utlized by firmware + +LFD CON $10 'Linefeed character + +Interval = 2000 +NewInterval = 2000 + +' ------------------------------------------------------------------------- +' EEPROM Data +' ------------------------------------------------------------------------- +' ------------------------------------------------------------------------- +' Initialization +' ------------------------------------------------------------------------- +Initialize: +GOSUB SHT_Connection_Reset ' reset device connection +' ------------------------------------------------------------------------- +' Program Code +' ------------------------------------------------------------------------- +Main: + GOSUB SHT_Measure_Temp 'Retrieve temperature + GOSUB SHT_Measure_Humidity 'Retrieve humidity + GOSUB RetrieveInterval 'Retrieve interval data + +SendData: + SEROUT 16, 16468, [DEC1 SensorID, DEC1 ElementSize, DEC1 ElementCount, DEC4 (tF), DEC3 (rhTrue), DEC5 Interval, LFD] + GOTO Main + +RetrieveInterval: + SERIN 16, 16468, Interval, SendData, [DEC NewInterval] 'Retrieve interval + IF NewInterval >= 10 AND NewInterval <= 60000 THEN + Interval = NewInterval + ENDIF +RETURN + +' ------------------------------------------------------------------------- +' Subroutines +' ------------------------------------------------------------------------- +' connection reset: 9 clock cyles with ShtData high, then start sequence +' +SHT_Connection_Reset: +SHIFTOUT ShtData, Clock, LSBFIRST, [$FFF\9] +' generates SHT11 "start" sequence +' _____ _____ +' ShtData |_______| +' ___ ___ +' Clock ___| |___| |___ +' +SHT_Start: +INPUT ShtData ' let pull-up take high +LOW Clock +HIGH Clock +LOW ShtData +LOW Clock +HIGH Clock +INPUT ShtData +LOW Clock +RETURN +' measure temperature +' -- celcius = raw * 0.01 - 40 +' -- fahrenheit = raw * 0.018 - 40 +' +SHT_Measure_Temp: +GOSUB SHT_Start ' alert device +ioByte = ShtTemp ' temperature command +GOSUB SHT_Write_Byte ' send command +GOSUB SHT_Wait ' wait for measurement +ackBit = Ack ' another read follows +GOSUB SHT_Read_Byte ' get MSB +soT.HIGHBYTE = ioByte +ackBit = NoAck ' last read +GOSUB SHT_Read_Byte ' get LSB +soT.LOWBYTE = ioByte +' Note: Conversion factors are multiplied by 10 to return the +' temperature values in tenths of degrees +tC = soT ** $1999 - 400 ' convert to tenths C +tF = soT ** $2E14 - 400 ' convert to tenths F +RETURN +' measure humidity +' +SHT_Measure_Humidity: +GOSUB SHT_Start ' alert device +ioByte = ShtHumi ' humidity command +GOSUB SHT_Write_Byte ' send command +GOSUB SHT_Wait ' wait for measurement +ackBit = Ack ' another read follows +GOSUB SHT_Read_Byte ' get MSB +soRH.HIGHBYTE = ioByte +ackBit = NoAck ' last read +GOSUB SHT_Read_Byte ' get LSB +soRH.LOWBYTE = ioByte +' linearize humidity +' rhLin = (soRH * 0.0405) - (soRH^2 * 0.0000028) - 4 +' +' for the BASIC Stamp: +' rhLin = (soRH * 0.0405) - (soRH * 0.002 * soRH * 0.0014) - 4 +' +' Conversion factors are multiplied by 10 to return tenths +' +rhLin = (soRH ** $67AE) - (soRH ** $83 * soRH ** $5B) - 40 +' temperature compensated humidity +' rhTrue = (tc - 25) * (soRH * 0.00008 + 0.01) + rhLin +' +' Conversion factors are multiplied by 10 to return tenths +' -- simplified +' +rhTrue = (tC - 250) * (soRH ** $34) + rhLin +RETURN +' sends "status" +' +SHT_Write_Status: +GOSUB SHT_Start ' alert device +ioByte = ShtStatW ' write to status reg cmd +GOSUB SHT_Write_Byte ' send command +ioByte = status +GOSUB SHT_Write_Byte +RETURN +' returns "status" +' +SHT_Read_Status: +GOSUB SHT_Start ' alert device +ioByte = ShtStatW ' write to status reg cmd +GOSUB SHT_Read_Byte ' send command +ackBit = NoAck ' only one byte to read +GOSUB SHT_Read_Byte +RETURN +' sends "ioByte" +' returns "ackBit" +' +SHT_Write_Byte: +SHIFTOUT ShtData, Clock, MSBFIRST, [ioByte] ' send byte +SHIFTIN ShtData, Clock, LSBPRE, [ackBit\1] ' get ack bit +RETURN +' returns "ioByte" +' sends "ackBit" +' +SHT_Read_Byte: +SHIFTIN ShtData, Clock, MSBPRE, [ioByte] ' get byte +SHIFTOUT ShtData, Clock, LSBFIRST, [ackBit\1] ' send ack bit +INPUT ShtData ' release data line +RETURN +' wait for device to finish measurement (pulls data line low) +' -- timeout after ~1/4 second +' +SHT_Wait: +INPUT ShtData ' data line is input +timeOut = No ' assume no timeout +FOR toDelay = 1 TO 250 ' wait ~1/4 second +IF (ShtData = 0) THEN EXIT +PAUSE 1 +NEXT +IF (toDelay = 250) THEN timeOut = Yes ' loop completed = timeout +RETURN + +' reset SHT11/15 with soft reset +' +SHT_Soft_Reset: +GOSUB SHT_Connection_Reset ' reset the connection +ioByte = ShtReset ' reset command +ackBit = NoAck ' only one byte to send +GOSUB SHT_Write_Byte ' send it +PAUSE 11 ' wait at least 11 ms +RETURN \ No newline at end of file diff --git a/wpd/WpdBasicHardwareDriver/resource.h b/wpd/WpdBasicHardwareDriver/resource.h new file mode 100644 index 000000000..219795561 --- /dev/null +++ b/wpd/WpdBasicHardwareDriver/resource.h @@ -0,0 +1,3 @@ +#pragma once +#define IDR_WpdBasicHardwareDriver 101 + diff --git a/wpd/WpdBasicHardwareDriver/stdafx.h b/wpd/WpdBasicHardwareDriver/stdafx.h new file mode 100644 index 000000000..56150a64c --- /dev/null +++ b/wpd/WpdBasicHardwareDriver/stdafx.h @@ -0,0 +1,320 @@ +#pragma once + +#include "resource.h" +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif + +#define STRSAFE_NO_DEPRECATE + +#include +#include + +#include +#include +#include +#include +#include + +// This driver is entirely user-mode +_Analysis_mode_(_Analysis_code_type_user_code_); + +// +// Driver specific tracing #defines + +// +// TODO: Change these values to be appropriate for your driver. +// +#define MYDRIVER_TRACING_ID L"Microsoft\\WPD\\BasicHardwareDriver" + +// +// TODO: Choose a different trace control GUID +// + +#define WPP_CONTROL_GUIDS \ + WPP_DEFINE_CONTROL_GUID(BasicHardwareDriverCtlGuid,(80068de5,197d,4db6,b77c,29ee3cca4537), \ + WPP_DEFINE_BIT(TRACE_FLAG_ALL) \ + WPP_DEFINE_BIT(TRACE_FLAG_DEVICE) \ + WPP_DEFINE_BIT(TRACE_FLAG_DRIVER) \ + WPP_DEFINE_BIT(TRACE_FLAG_QUEUE) \ + ) + +#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 +// TraceEvents function. +// +// begin_wpp config +// FUNC Trace{FLAG=TRACE_FLAG_ALL}(LEVEL, MSG, ...); +// FUNC TraceEvents(LEVEL, FLAGS, MSG, ...); +// end_wpp + +// +// This comment block is scanned by the trace preprocessor to define our +// CHECK_HR function. +// +// +// begin_wpp config +// USEPREFIX (CHECK_HR,"%!STDPREFIX!"); +// FUNC CHECK_HR{FLAG=TRACE_FLAG_ALL}(hrCheck, MSG, ...); +// USESUFFIX (CHECK_HR, " hr= %!HRESULT!", hrCheck); +// end_wpp + +// +// PRE macro: The name of the macro includes the condition arguments FLAGS and EXP +// define in FUNC above +// +#define WPP_FLAG_hrCheck_PRE(FLAGS, hrCheck) {if(hrCheck != S_OK) { + +// +// POST macro +// The name of the macro includes the condition arguments FLAGS and EXP +// define in FUNC above +#define WPP_FLAG_hrCheck_POST(FLAGS, hrCheck) ; } } + +// +// 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_hrCheck_ENABLED(FLAGS, hrCheck) WPP_FLAG_ENABLED(FLAGS) +#define WPP_FLAG_hrCheck_LOGGER(FLAGS, hrCheck) WPP_FLAG_LOGGER(FLAGS) + + +#include +#include +#include + +#include // Required to access the DEFINE_GUID macro +#include // Required to access the DEFINE_PROPERTYKEY macro + +// {CDD18979-A7B0-4D5E-9EB2-0A826805CBBD} +DEFINE_PROPERTYKEY(PRIVATE_SAMPLE_DRIVER_WUDF_DEVICE_OBJECT, 0xCDD18979, 0xA7B0, 0x4D5E, 0x9E, 0xB2, 0x0A, 0x82, 0x68, 0x05, 0xCB, 0xBD, 2); +// {9BD949E5-59CF-41AE-90A9-BE1D044F578F} +DEFINE_PROPERTYKEY(PRIVATE_SAMPLE_DRIVER_WPD_SERIALIZER_OBJECT, 0x9BD949E5, 0x59CF, 0x41AE, 0x90, 0xA9, 0xBE, 0x1D, 0x04, 0x4F, 0x57, 0x8F, 2); +// {4DF6C8C7-2CE5-457C-9F53-EFCECAA95C04} +DEFINE_PROPERTYKEY(PRIVATE_SAMPLE_DRIVER_CLIENT_CONTEXT_MAP, 0x4DF6C8C7, 0x2CE5, 0x457C, 0x9F, 0x53, 0xEF, 0xCE, 0xCA, 0xA9, 0x5C, 0x04, 2); + +/**************************************************************************** +* This section defines the Functional Category associated with the sensor object +****************************************************************************/ + +// +// FUNCTIONAL_CATEGORY_SENSOR_SAMPLE +// Indicates this object encapsulates sensor functionality on the device +DEFINE_GUID (FUNCTIONAL_CATEGORY_SENSOR_SAMPLE, 0x09b93609, 0xd5c1,0x497b,0xb2, 0x52, 0xb4, 0x92, 0xcb, 0x5e, 0xfe, 0x52); + + +/**************************************************************************** +* This section defines all Commands, Parameters and Options associated with: +* SENSOR_PROPERTIES_V1 +* +* This category is for properties common to all sensor objects. +****************************************************************************/ +DEFINE_GUID (SENSOR_PROPERTIES_V1, 0xa7ef4367, 0x6550, 0x4055, 0xb6, 0x6f, 0xbe, 0x6f, 0xda, 0xcf, 0x4e, 0x9f); + +// +// SENSOR_READING +// [ VT_UI4 ] Indicates the sensor reading in degrees Kelvin. +DEFINE_PROPERTYKEY(SENSOR_READING, 0xa7ef4367, 0x6550, 0x4055, 0xb6, 0x6f, 0xbe, 0x6f, 0xda, 0xcf, 0x4e, 0x9f, 2); +// +// SENSOR_UPDATE_INTERVAL +// [ VT_UI4 ] Indicates the sensor update interval in milliseconds. +DEFINE_PROPERTYKEY(SENSOR_UPDATE_INTERVAL, 0xa7ef4367, 0x6550, 0x4055, 0xb6, 0x6f, 0xbe, 0x6f, 0xda, 0xcf, 0x4e, 0x9f, 3); + +/**************************************************************************** +* This section defines all Events associated with the sensor object +****************************************************************************/ +// +// EVENT_SENSOR_READING_UPDATED +// This event is sent after a new sensor reading is available on the device. +DEFINE_GUID (EVENT_SENSOR_READING_UPDATED, 0xada23b0b, 0xce13, 0x4e11, 0x9d, 0x2f, 0x15, 0xfe, 0x10, 0xd6, 0x63, 0x37); + + +// +// Macro Definitions +// + +#ifndef SAFE_RELEASE + #define SAFE_RELEASE(p) if( NULL != p ) { ( p )->Release(); p = NULL; } +#endif + +class ContextMap : public IUnknown +{ +public: + ContextMap() : + m_cRef(1) + { + + } + + ~ContextMap() + { + CComCritSecLock Lock(m_CriticalSection); + + IUnknown* pUnk = NULL; + POSITION elementPosition = NULL; + + elementPosition = m_Map.GetStartPosition(); + while(elementPosition != NULL) + { + pUnk = m_Map.GetNextValue(elementPosition); + if(pUnk != NULL) + { + pUnk->Release(); + } + } + } + +public: // IUnknown + ULONG __stdcall AddRef() + { + InterlockedIncrement((long*) &m_cRef); + return m_cRef; + } + + _At_(this, __drv_freesMem(Mem)) + ULONG __stdcall Release() + { + ULONG ulRefCount = m_cRef - 1; + + if (InterlockedDecrement((long*) &m_cRef) == 0) + { + delete this; + return 0; + } + return ulRefCount; + } + + HRESULT __stdcall QueryInterface( + REFIID riid, + void** ppv) + { + HRESULT hr = S_OK; + + if(riid == IID_IUnknown) + { + *ppv = static_cast(this); + AddRef(); + } + else + { + *ppv = NULL; + hr = E_NOINTERFACE; + } + return hr; + } + + +public: // Context accessor methods + + // If successful, this method AddRef's the context and returns + // a context key + HRESULT Add( + _In_ IUnknown* pContext, + _Out_ CAtlStringW& key) + { + CComCritSecLock Lock(m_CriticalSection); + HRESULT hr = S_OK; + GUID guidContext = GUID_NULL; + CComBSTR bstrContext; + key = L""; + + // Create a unique context key + hr = CoCreateGuid(&guidContext); + if (hr == S_OK) + { + bstrContext = guidContext; + if(bstrContext.Length() > 0) + { + key = bstrContext; + } + else + { + hr = E_OUTOFMEMORY; + } + } + + if (hr == S_OK) + { + // Insert this into the map + POSITION elementPosition = m_Map.SetAt(key, pContext); + if(elementPosition != NULL) + { + // AddRef since we are holding onto it + pContext->AddRef(); + } + else + { + hr = E_OUTOFMEMORY; + } + } + return hr; + } + + void Remove( + _In_ const CAtlStringW& key) + { + CComCritSecLock Lock(m_CriticalSection); + // Get the element + IUnknown* pContext = NULL; + + if (m_Map.Lookup(key, pContext) == true) + { + // Remove the entry for it + m_Map.RemoveKey(key); + + // Release it + pContext->Release(); + } + } + + // Returns the context pointer. If not found, return value is NULL. + // If non-NULL, caller is responsible for Releasing when it is done, + // since this method will AddRef the context. + IUnknown* GetContext( + _In_ const CAtlStringW& key) + { + CComCritSecLock Lock(m_CriticalSection); + // Get the element + IUnknown* pContext = NULL; + + if (m_Map.Lookup(key, pContext) == true) + { + // AddRef + pContext->AddRef(); + } + return pContext; + } + +private: + CComAutoCriticalSection m_CriticalSection; + CAtlMap m_Map; + DWORD m_cRef; +}; + +HRESULT UpdateDeviceFriendlyName( + _In_ IPortableDeviceClassExtension* pPortableDeviceClassExtension, + _In_ LPCWSTR wszDeviceFriendlyName); + + +// Forward declaration of WpdBaseDriver as it is accessed by other classes +class WpdBaseDriver; + +#include "WpdBasicHardwareDriver.h" +#include "RS232Connection.h" +#include "RS232Target.h" +#include "WpdObjectEnum.h" +#include "WpdObjectProperties.h" +#include "WpdCapabilities.h" +#include "WpdBaseDriver.h" +#include "Device.h" +#include "Driver.h" +#include "Queue.h" + +extern HINSTANCE g_hInstance; + diff --git a/wpd/WpdHelloWorldDriver/WpdHelloWorldDriver.inx b/wpd/WpdHelloWorldDriver/WpdHelloWorldDriver.inx index ec16ed5c4..40d31f8cc 100644 --- a/wpd/WpdHelloWorldDriver/WpdHelloWorldDriver.inx +++ b/wpd/WpdHelloWorldDriver/WpdHelloWorldDriver.inx @@ -11,9 +11,9 @@ CatalogFile=WpdHelloWorldDriver.cat DriverVer=01/24/2005,1.1.1.1 [Manufacturer] -%MSFTWUDF%=Microsoft,NT$ARCH$ +%Mfg%=Standard,NT$ARCH$ -[Microsoft.NT$ARCH$] +[Standard.NT$ARCH$] %BasicDeviceName%=Basic_Install,WUDF\WpdHelloWorld [SourceDisksFiles] @@ -76,7 +76,7 @@ WpdHelloWorldDriver.dll ; =================== Generic ================================== [Strings] -MSFTWUDF="Microsoft Windows Portable Devices" -Provider="Microsoft WPD" +Provider="TODO-Set-Provider" +Mfg="Windows Portable Devices" MediaDescription="Windows Portable Device Hello World Sample Driver Installation Media" BasicDeviceName="Windows Portable Device Hello World Sample Driver" diff --git a/wpd/WpdHelloWorldDriver/WpdHelloWorldDriver.sln b/wpd/WpdHelloWorldDriver/WpdHelloWorldDriver.sln index c9b35721f..b547cddaa 100644 --- a/wpd/WpdHelloWorldDriver/WpdHelloWorldDriver.sln +++ b/wpd/WpdHelloWorldDriver/WpdHelloWorldDriver.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0 MinimumVisualStudioVersion = 12.0 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WpdHelloWorldDriver", "WpdHelloWorldDriver.vcxproj", "{B0C86FEC-87BE-4E10-A509-C182F1C2F6F5}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WpdHelloWorldDriver", "WpdHelloWorldDriver.vcxproj", "{8F4134E7-1EB9-4791-9EED-D4966C4A2ECA}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {B0C86FEC-87BE-4E10-A509-C182F1C2F6F5}.Debug|Win32.ActiveCfg = Debug|Win32 - {B0C86FEC-87BE-4E10-A509-C182F1C2F6F5}.Debug|Win32.Build.0 = Debug|Win32 - {B0C86FEC-87BE-4E10-A509-C182F1C2F6F5}.Release|Win32.ActiveCfg = Release|Win32 - {B0C86FEC-87BE-4E10-A509-C182F1C2F6F5}.Release|Win32.Build.0 = Release|Win32 - {B0C86FEC-87BE-4E10-A509-C182F1C2F6F5}.Debug|x64.ActiveCfg = Debug|x64 - {B0C86FEC-87BE-4E10-A509-C182F1C2F6F5}.Debug|x64.Build.0 = Debug|x64 - {B0C86FEC-87BE-4E10-A509-C182F1C2F6F5}.Release|x64.ActiveCfg = Release|x64 - {B0C86FEC-87BE-4E10-A509-C182F1C2F6F5}.Release|x64.Build.0 = Release|x64 + {8F4134E7-1EB9-4791-9EED-D4966C4A2ECA}.Debug|Win32.ActiveCfg = Debug|Win32 + {8F4134E7-1EB9-4791-9EED-D4966C4A2ECA}.Debug|Win32.Build.0 = Debug|Win32 + {8F4134E7-1EB9-4791-9EED-D4966C4A2ECA}.Release|Win32.ActiveCfg = Release|Win32 + {8F4134E7-1EB9-4791-9EED-D4966C4A2ECA}.Release|Win32.Build.0 = Release|Win32 + {8F4134E7-1EB9-4791-9EED-D4966C4A2ECA}.Debug|x64.ActiveCfg = Debug|x64 + {8F4134E7-1EB9-4791-9EED-D4966C4A2ECA}.Debug|x64.Build.0 = Debug|x64 + {8F4134E7-1EB9-4791-9EED-D4966C4A2ECA}.Release|x64.ActiveCfg = Release|x64 + {8F4134E7-1EB9-4791-9EED-D4966C4A2ECA}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/wpd/WpdHelloWorldDriver/WpdHelloWorldDriver.vcxproj b/wpd/WpdHelloWorldDriver/WpdHelloWorldDriver.vcxproj index 2f3c2f81c..931613b40 100644 --- a/wpd/WpdHelloWorldDriver/WpdHelloWorldDriver.vcxproj +++ b/wpd/WpdHelloWorldDriver/WpdHelloWorldDriver.vcxproj @@ -19,13 +19,13 @@ - {B0C86FEC-87BE-4E10-A509-C182F1C2F6F5} + {8F4134E7-1EB9-4791-9EED-D4966C4A2ECA} $(MSBuildProjectName) 1 1 Debug Win32 - {76BA4347-D2B6-4F3B-BBB1-5EC4A2FEEB14} + {E6AF6E34-FBD7-4920-AC6F-CEF63547D014} @@ -330,7 +330,6 @@ - diff --git a/wpd/WpdHelloWorldDriver/WpdHelloWorldDriver.vcxproj.Filters b/wpd/WpdHelloWorldDriver/WpdHelloWorldDriver.vcxproj.Filters index 07e1689c9..200c87f57 100644 --- a/wpd/WpdHelloWorldDriver/WpdHelloWorldDriver.vcxproj.Filters +++ b/wpd/WpdHelloWorldDriver/WpdHelloWorldDriver.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {D130D47E-6427-4218-8931-B9A76438E407} + {104D8976-CCCB-4B0C-82E9-0BA38F5B1372} h;hpp;hxx;hm;inl;inc;xsd - {37543692-66C2-483A-B887-DCCCF1E54940} + {41B5421D-A51D-475D-8470-88324065958A} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {7BA26A24-C6A3-4FFC-AD61-E52574E57C45} + {C07CAF8B-2324-407E-A1DA-94157E08A433} inf;inv;inx;mof;mc; - {9D6CD385-5C3A-4727-AB21-03D755F75101} + {5735523F-568C-441B-84CD-042EAEB8F9AA} @@ -57,9 +57,6 @@ - - Driver Files - Driver Files diff --git a/wpd/WpdMultiTransportDriver/WpdMultiTransportDriver.inx b/wpd/WpdMultiTransportDriver/WpdMultiTransportDriver.inx index af3932d91..d293c9f76 100644 --- a/wpd/WpdMultiTransportDriver/WpdMultiTransportDriver.inx +++ b/wpd/WpdMultiTransportDriver/WpdMultiTransportDriver.inx @@ -11,9 +11,9 @@ CatalogFile=WpdMultiTransportDriver.cat DriverVer=01/24/2005,1.1.1.1 [Manufacturer] -%MSFTWUDF%=Microsoft,NT$ARCH$ +%Mfg%=Standard,NT$ARCH$ -[Microsoft.NT$ARCH$] +[Standard.NT$ARCH$] %BasicDeviceName%=Basic_Install,WUDF\MultiTransport [SourceDisksFiles] @@ -77,7 +77,7 @@ WpdMultiTransportDriver.dll ; =================== Generic ================================== [Strings] -MSFTWUDF="Microsoft Windows Portable Devices" -Provider="Microsoft WPD" +Provider="TODO-Set-Provider" +Mfg="Windows Portable Devices" MediaDescription="Windows Portable Device Multi-Transport Sample Driver Installation Media" BasicDeviceName="Windows Portable Device Multi-Transport Sample Driver" diff --git a/wpd/WpdMultiTransportDriver/WpdMultiTransportDriver.sln b/wpd/WpdMultiTransportDriver/WpdMultiTransportDriver.sln index 229bda813..0aee2ed74 100644 --- a/wpd/WpdMultiTransportDriver/WpdMultiTransportDriver.sln +++ b/wpd/WpdMultiTransportDriver/WpdMultiTransportDriver.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0 MinimumVisualStudioVersion = 12.0 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WpdMultiTransportDriver", "WpdMultiTransportDriver.vcxproj", "{0431FFA9-4A12-4072-914A-F46F86F9EC99}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WpdMultiTransportDriver", "WpdMultiTransportDriver.vcxproj", "{C0EE76FA-9D31-4B86-80D6-25C9FAEAE0A6}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {0431FFA9-4A12-4072-914A-F46F86F9EC99}.Debug|Win32.ActiveCfg = Debug|Win32 - {0431FFA9-4A12-4072-914A-F46F86F9EC99}.Debug|Win32.Build.0 = Debug|Win32 - {0431FFA9-4A12-4072-914A-F46F86F9EC99}.Release|Win32.ActiveCfg = Release|Win32 - {0431FFA9-4A12-4072-914A-F46F86F9EC99}.Release|Win32.Build.0 = Release|Win32 - {0431FFA9-4A12-4072-914A-F46F86F9EC99}.Debug|x64.ActiveCfg = Debug|x64 - {0431FFA9-4A12-4072-914A-F46F86F9EC99}.Debug|x64.Build.0 = Debug|x64 - {0431FFA9-4A12-4072-914A-F46F86F9EC99}.Release|x64.ActiveCfg = Release|x64 - {0431FFA9-4A12-4072-914A-F46F86F9EC99}.Release|x64.Build.0 = Release|x64 + {C0EE76FA-9D31-4B86-80D6-25C9FAEAE0A6}.Debug|Win32.ActiveCfg = Debug|Win32 + {C0EE76FA-9D31-4B86-80D6-25C9FAEAE0A6}.Debug|Win32.Build.0 = Debug|Win32 + {C0EE76FA-9D31-4B86-80D6-25C9FAEAE0A6}.Release|Win32.ActiveCfg = Release|Win32 + {C0EE76FA-9D31-4B86-80D6-25C9FAEAE0A6}.Release|Win32.Build.0 = Release|Win32 + {C0EE76FA-9D31-4B86-80D6-25C9FAEAE0A6}.Debug|x64.ActiveCfg = Debug|x64 + {C0EE76FA-9D31-4B86-80D6-25C9FAEAE0A6}.Debug|x64.Build.0 = Debug|x64 + {C0EE76FA-9D31-4B86-80D6-25C9FAEAE0A6}.Release|x64.ActiveCfg = Release|x64 + {C0EE76FA-9D31-4B86-80D6-25C9FAEAE0A6}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/wpd/WpdMultiTransportDriver/WpdMultiTransportDriver.vcxproj b/wpd/WpdMultiTransportDriver/WpdMultiTransportDriver.vcxproj index b89e9d1a1..438ff576b 100644 --- a/wpd/WpdMultiTransportDriver/WpdMultiTransportDriver.vcxproj +++ b/wpd/WpdMultiTransportDriver/WpdMultiTransportDriver.vcxproj @@ -19,13 +19,13 @@ - {0431FFA9-4A12-4072-914A-F46F86F9EC99} + {C0EE76FA-9D31-4B86-80D6-25C9FAEAE0A6} $(MSBuildProjectName) 1 1 Debug Win32 - {56F5B6B8-E50F-45EE-A52E-D99FA98256AA} + {CF804174-6BF8-46F1-96A8-776A06FD29D8} @@ -330,7 +330,6 @@ - diff --git a/wpd/WpdMultiTransportDriver/WpdMultiTransportDriver.vcxproj.Filters b/wpd/WpdMultiTransportDriver/WpdMultiTransportDriver.vcxproj.Filters index e051fd40c..efc6b6a40 100644 --- a/wpd/WpdMultiTransportDriver/WpdMultiTransportDriver.vcxproj.Filters +++ b/wpd/WpdMultiTransportDriver/WpdMultiTransportDriver.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {66A9D3F7-7136-483B-A3B0-0B71F39E1A4D} + {893E5462-4618-4E48-B66B-E32DE2DEF6D8} h;hpp;hxx;hm;inl;inc;xsd - {E6663B10-722E-421B-BE1F-D2450D721956} + {D5C9A765-A4D5-41BB-ADEE-2ACECEC5AE5D} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {F2C470A6-B9EF-4AD0-8AB3-3A38A05C42CD} + {FEADB211-2464-41DF-AC2F-6FD5C130DEB7} inf;inv;inx;mof;mc; - {CF4D8378-7A1D-49EC-AE7C-220F975A42A2} + {0F4DD998-05BB-4588-B66C-8897C880EAE9} @@ -57,9 +57,6 @@ - - Driver Files - Driver Files diff --git a/wpd/WpdServiceSampleDriver/WpdServiceSampleDriver.inx b/wpd/WpdServiceSampleDriver/WpdServiceSampleDriver.inx index 6b9990c8f..935a61cd9 100644 --- a/wpd/WpdServiceSampleDriver/WpdServiceSampleDriver.inx +++ b/wpd/WpdServiceSampleDriver/WpdServiceSampleDriver.inx @@ -11,9 +11,9 @@ CatalogFile=WpdServiceSampleDriver.cat DriverVer=01/24/2007,1.1.1.1 [Manufacturer] -%MSFTWUDF%=Microsoft,NT$ARCH$ +%Mfg%=Standard,NT$ARCH$ -[Microsoft.NT$ARCH$] +[Standard.NT$ARCH$] %BasicDeviceName%=Basic_Install,WUDF\WpdService [SourceDisksFiles] @@ -73,7 +73,7 @@ WpdServiceSampleDriver.dll ; =================== Generic ================================== [Strings] -MSFTWUDF="Microsoft Windows Portable Devices" -Provider="Microsoft WPD" +Provider="TODO-Set-Provider" +Mfg="Windows Portable Devices" MediaDescription="Windows Portable Device Services Sample Driver Installation Media" BasicDeviceName="Windows Portable Device Services Sample Driver" diff --git a/wpd/WpdServiceSampleDriver/WpdServiceSampleDriver.sln b/wpd/WpdServiceSampleDriver/WpdServiceSampleDriver.sln index 4e97814fe..287e80fea 100644 --- a/wpd/WpdServiceSampleDriver/WpdServiceSampleDriver.sln +++ b/wpd/WpdServiceSampleDriver/WpdServiceSampleDriver.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0 MinimumVisualStudioVersion = 12.0 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WpdServiceSampleDriver", "WpdServiceSampleDriver.vcxproj", "{060767D3-7EB1-4EC2-B2E2-6239DAEB712A}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WpdServiceSampleDriver", "WpdServiceSampleDriver.vcxproj", "{C5678647-23D8-428F-B6D8-F76D0DD66FED}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {060767D3-7EB1-4EC2-B2E2-6239DAEB712A}.Debug|Win32.ActiveCfg = Debug|Win32 - {060767D3-7EB1-4EC2-B2E2-6239DAEB712A}.Debug|Win32.Build.0 = Debug|Win32 - {060767D3-7EB1-4EC2-B2E2-6239DAEB712A}.Release|Win32.ActiveCfg = Release|Win32 - {060767D3-7EB1-4EC2-B2E2-6239DAEB712A}.Release|Win32.Build.0 = Release|Win32 - {060767D3-7EB1-4EC2-B2E2-6239DAEB712A}.Debug|x64.ActiveCfg = Debug|x64 - {060767D3-7EB1-4EC2-B2E2-6239DAEB712A}.Debug|x64.Build.0 = Debug|x64 - {060767D3-7EB1-4EC2-B2E2-6239DAEB712A}.Release|x64.ActiveCfg = Release|x64 - {060767D3-7EB1-4EC2-B2E2-6239DAEB712A}.Release|x64.Build.0 = Release|x64 + {C5678647-23D8-428F-B6D8-F76D0DD66FED}.Debug|Win32.ActiveCfg = Debug|Win32 + {C5678647-23D8-428F-B6D8-F76D0DD66FED}.Debug|Win32.Build.0 = Debug|Win32 + {C5678647-23D8-428F-B6D8-F76D0DD66FED}.Release|Win32.ActiveCfg = Release|Win32 + {C5678647-23D8-428F-B6D8-F76D0DD66FED}.Release|Win32.Build.0 = Release|Win32 + {C5678647-23D8-428F-B6D8-F76D0DD66FED}.Debug|x64.ActiveCfg = Debug|x64 + {C5678647-23D8-428F-B6D8-F76D0DD66FED}.Debug|x64.Build.0 = Debug|x64 + {C5678647-23D8-428F-B6D8-F76D0DD66FED}.Release|x64.ActiveCfg = Release|x64 + {C5678647-23D8-428F-B6D8-F76D0DD66FED}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/wpd/WpdServiceSampleDriver/WpdServiceSampleDriver.vcxproj b/wpd/WpdServiceSampleDriver/WpdServiceSampleDriver.vcxproj index e3538b91a..6431626e6 100644 --- a/wpd/WpdServiceSampleDriver/WpdServiceSampleDriver.vcxproj +++ b/wpd/WpdServiceSampleDriver/WpdServiceSampleDriver.vcxproj @@ -19,13 +19,13 @@ - {060767D3-7EB1-4EC2-B2E2-6239DAEB712A} + {C5678647-23D8-428F-B6D8-F76D0DD66FED} $(MSBuildProjectName) 1 1 Debug Win32 - {632F78B7-F73E-4B6D-959D-587356021B6B} + {BD2E3BAC-111F-4306-AAFE-529806475FC0} @@ -435,7 +435,6 @@ - diff --git a/wpd/WpdServiceSampleDriver/WpdServiceSampleDriver.vcxproj.Filters b/wpd/WpdServiceSampleDriver/WpdServiceSampleDriver.vcxproj.Filters index 0b484774b..7e058142c 100644 --- a/wpd/WpdServiceSampleDriver/WpdServiceSampleDriver.vcxproj.Filters +++ b/wpd/WpdServiceSampleDriver/WpdServiceSampleDriver.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {F5AD514F-9337-4FD2-8AF1-76095351A7A3} + {930F9018-015A-46BD-A347-171E1881999D} h;hpp;hxx;hm;inl;inc;xsd - {E3137EB0-EA19-4B3F-AF8F-D7A0B1CCE331} + {7E56FF5F-D26D-4887-8A49-57F2FC991795} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {CB0E2DC6-C230-433C-BC67-FFE4DFA85526} + {66D2DFC5-B6CE-4AA6-A4C6-8E35D08B9A9A} inf;inv;inx;mof;mc; - {10D2CAE8-0888-4817-81AC-55927D716283} + {B436CE26-D450-4F04-9C6A-13067DED6AFB} @@ -96,9 +96,6 @@ - - Driver Files - Driver Files diff --git a/wpd/WpdWudfSampleDriver/WpdWudfSampleDriver.inx b/wpd/WpdWudfSampleDriver/WpdWudfSampleDriver.inx index d0b343f01..a8515481f 100644 --- a/wpd/WpdWudfSampleDriver/WpdWudfSampleDriver.inx +++ b/wpd/WpdWudfSampleDriver/WpdWudfSampleDriver.inx @@ -11,9 +11,9 @@ CatalogFile=WpdWudfSampleDriver.cat DriverVer=01/24/2005,1.1.1.1 [Manufacturer] -%MSFTWUDF%=Microsoft,NT$ARCH$ +%Mfg%=Standard,NT$ARCH$ -[Microsoft.NT$ARCH$] +[Standard.NT$ARCH$] %BasicDeviceName%=Basic_Install,WUDF\Basic [SourceDisksFiles] @@ -76,7 +76,7 @@ WpdWudfSampleDriver.dll ; =================== Generic ================================== [Strings] -MSFTWUDF="Microsoft Windows Portable Devices" -Provider="Microsoft WPD" +Provider="TODO-Set-Provider" +Mfg="Windows Portable Devices" MediaDescription="Windows Portable Device Sample Driver Installation Media" BasicDeviceName="Windows Portable Device Comprehensive Sample Driver" diff --git a/wpd/WpdWudfSampleDriver/WpdWudfSampleDriver.sln b/wpd/WpdWudfSampleDriver/WpdWudfSampleDriver.sln index 84cd07d70..d01aadcb6 100644 --- a/wpd/WpdWudfSampleDriver/WpdWudfSampleDriver.sln +++ b/wpd/WpdWudfSampleDriver/WpdWudfSampleDriver.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0 MinimumVisualStudioVersion = 12.0 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WpdWudfSampleDriver", "WpdWudfSampleDriver.vcxproj", "{5FE7B5AC-67C9-42B7-B687-D775D7721761}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WpdWudfSampleDriver", "WpdWudfSampleDriver.vcxproj", "{8D70B287-260F-4322-B120-97419D64A487}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {5FE7B5AC-67C9-42B7-B687-D775D7721761}.Debug|Win32.ActiveCfg = Debug|Win32 - {5FE7B5AC-67C9-42B7-B687-D775D7721761}.Debug|Win32.Build.0 = Debug|Win32 - {5FE7B5AC-67C9-42B7-B687-D775D7721761}.Release|Win32.ActiveCfg = Release|Win32 - {5FE7B5AC-67C9-42B7-B687-D775D7721761}.Release|Win32.Build.0 = Release|Win32 - {5FE7B5AC-67C9-42B7-B687-D775D7721761}.Debug|x64.ActiveCfg = Debug|x64 - {5FE7B5AC-67C9-42B7-B687-D775D7721761}.Debug|x64.Build.0 = Debug|x64 - {5FE7B5AC-67C9-42B7-B687-D775D7721761}.Release|x64.ActiveCfg = Release|x64 - {5FE7B5AC-67C9-42B7-B687-D775D7721761}.Release|x64.Build.0 = Release|x64 + {8D70B287-260F-4322-B120-97419D64A487}.Debug|Win32.ActiveCfg = Debug|Win32 + {8D70B287-260F-4322-B120-97419D64A487}.Debug|Win32.Build.0 = Debug|Win32 + {8D70B287-260F-4322-B120-97419D64A487}.Release|Win32.ActiveCfg = Release|Win32 + {8D70B287-260F-4322-B120-97419D64A487}.Release|Win32.Build.0 = Release|Win32 + {8D70B287-260F-4322-B120-97419D64A487}.Debug|x64.ActiveCfg = Debug|x64 + {8D70B287-260F-4322-B120-97419D64A487}.Debug|x64.Build.0 = Debug|x64 + {8D70B287-260F-4322-B120-97419D64A487}.Release|x64.ActiveCfg = Release|x64 + {8D70B287-260F-4322-B120-97419D64A487}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/wpd/WpdWudfSampleDriver/WpdWudfSampleDriver.vcxproj b/wpd/WpdWudfSampleDriver/WpdWudfSampleDriver.vcxproj index 34a93630b..d2b6451a0 100644 --- a/wpd/WpdWudfSampleDriver/WpdWudfSampleDriver.vcxproj +++ b/wpd/WpdWudfSampleDriver/WpdWudfSampleDriver.vcxproj @@ -19,13 +19,13 @@ - {5FE7B5AC-67C9-42B7-B687-D775D7721761} + {8D70B287-260F-4322-B120-97419D64A487} $(MSBuildProjectName) 1 1 Debug Win32 - {E027B7B1-FCC5-49F8-BC8F-EEEFDECC6064} + {265A44FD-69E9-4877-8649-532DD8CE8ABD} @@ -393,7 +393,6 @@ - diff --git a/wpd/WpdWudfSampleDriver/WpdWudfSampleDriver.vcxproj.Filters b/wpd/WpdWudfSampleDriver/WpdWudfSampleDriver.vcxproj.Filters index 35ee711f4..bcd37f78d 100644 --- a/wpd/WpdWudfSampleDriver/WpdWudfSampleDriver.vcxproj.Filters +++ b/wpd/WpdWudfSampleDriver/WpdWudfSampleDriver.vcxproj.Filters @@ -3,19 +3,19 @@ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* - {A0952C7E-AD1D-4BDC-A5D4-23AB2CCA70DB} + {CBAFD46C-9514-44DF-976C-E0F20BD23F38} h;hpp;hxx;hm;inl;inc;xsd - {6C6CAD3E-05C8-49A0-A643-041976D083AC} + {99B0F668-B7D9-4D13-ADE9-FF166DD545CE} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml - {09BED7DF-74CB-49CC-B3E3-BD05003E23F5} + {3D93FBBB-1D8A-4A6A-B95F-6293FD4775E5} inf;inv;inx;mof;mc; - {882CF447-FA9C-4E3F-AB69-826D25BD8F1D} + {E7DCDE15-599C-484C-B279-AFEB59803700} @@ -72,9 +72,6 @@ - - Driver Files - Driver Files